/* * Linux-specific portion of * Broadcom 802.11abg Networking Device Driver * * Copyright (C) 2015, Broadcom Corporation. All Rights Reserved. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * $Id: wl_linux.c 580354 2015-08-18 23:42:37Z $ */ #define LINUX_PORT #define __UNDEF_NO_VERSION__ #include #include #include #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 14) #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define WLC_MAXBSSCFG 1 #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) #include #else #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 4, 5) #error "No support for Kernel Rev <= 2.4.5, As the older kernel revs doesn't support Tasklets" #endif #include #include #include #include #include #ifdef USE_IW struct iw_statistics *wl_get_wireless_stats(struct net_device *dev); #endif #include #include #if defined(USE_CFG80211) #include #endif #include static void wl_timer(ulong data); static void _wl_timer(wl_timer_t *t); static struct net_device *wl_alloc_linux_if(wl_if_t *wlif); static int wl_monitor_start(struct sk_buff *skb, struct net_device *dev); static void wl_start_txqwork(wl_task_t *task); static void wl_txq_free(wl_info_t *wl); #define TXQ_LOCK(_wl) spin_lock_bh(&(_wl)->txq_lock) #define TXQ_UNLOCK(_wl) spin_unlock_bh(&(_wl)->txq_lock) static void wl_set_multicast_list_workitem(struct work_struct *work); static void wl_timer_task(wl_task_t *task); static void wl_dpc_rxwork(struct wl_task *task); static int wl_reg_proc_entry(wl_info_t *wl); static int wl_linux_watchdog(void *ctx); static int wl_found = 0; typedef struct priv_link { wl_if_t *wlif; } priv_link_t; #define WL_DEV_IF(dev) ((wl_if_t*)((priv_link_t*)DEV_PRIV(dev))->wlif) #ifdef WL_INFO #undef WL_INFO #endif #define WL_INFO(dev) ((wl_info_t*)(WL_DEV_IF(dev)->wl)) static int wl_open(struct net_device *dev); static int wl_close(struct net_device *dev); static int BCMFASTPATH wl_start(struct sk_buff *skb, struct net_device *dev); static int wl_start_int(wl_info_t *wl, wl_if_t *wlif, struct sk_buff *skb); static struct net_device_stats *wl_get_stats(struct net_device *dev); static int wl_set_mac_address(struct net_device *dev, void *addr); static void wl_set_multicast_list(struct net_device *dev); static void _wl_set_multicast_list(struct net_device *dev); static int wl_ethtool(wl_info_t *wl, void *uaddr, wl_if_t *wlif); static void wl_dpc(ulong data); static void wl_tx_tasklet(ulong data); static void wl_link_up(wl_info_t *wl, char * ifname); static void wl_link_down(wl_info_t *wl, char *ifname); static int wl_schedule_task(wl_info_t *wl, void (*fn)(struct wl_task *), void *context); #if defined(BCMDBG) static int wl_dump(wl_info_t *wl, struct bcmstrbuf *b); #endif static struct wl_if *wl_alloc_if(wl_info_t *wl, int iftype, uint unit, struct wlc_if* wlc_if); static void wl_free_if(wl_info_t *wl, wl_if_t *wlif); static void wl_get_driver_info(struct net_device *dev, struct ethtool_drvinfo *info); #if defined(WL_CONFIG_RFKILL) #include static int wl_init_rfkill(wl_info_t *wl); static void wl_uninit_rfkill(wl_info_t *wl); static int wl_set_radio_block(void *data, bool blocked); static void wl_report_radio_state(wl_info_t *wl); #endif MODULE_LICENSE("MIXED/Proprietary"); static struct pci_device_id wl_id_table[] = { { PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_NETWORK_OTHER << 8, 0xffff00, 0 }, { 0 } }; MODULE_DEVICE_TABLE(pci, wl_id_table); static unsigned int online_cpus = 1; #ifdef BCMDBG static int msglevel = 0xdeadbeef; module_param(msglevel, int, 0); static int msglevel2 = 0xdeadbeef; module_param(msglevel2, int, 0); static int phymsglevel = 0xdeadbeef; module_param(phymsglevel, int, 0); #endif #ifdef BCMDBG_ASSERT static int assert_type = 0xdeadbeef; module_param(assert_type, int, 0); #endif static int passivemode = 0; module_param(passivemode, int, 0); #define WL_TXQ_THRESH 0 static int wl_txq_thresh = WL_TXQ_THRESH; module_param(wl_txq_thresh, int, 0); static int oneonly = 0; module_param(oneonly, int, 0); static int piomode = 0; module_param(piomode, int, 0); static int instance_base = 0; module_param(instance_base, int, 0); #if defined(BCMDBG) static struct ether_addr local_ea; static char *macaddr = NULL; module_param(macaddr, charp, S_IRUGO); #endif static int nompc = 0; module_param(nompc, int, 0); #ifdef quote_str #undef quote_str #endif #ifdef to_str #undef to_str #endif #define to_str(s) #s #define quote_str(s) to_str(s) #define BRCM_WLAN_IFNAME eth%d static char intf_name[IFNAMSIZ] = quote_str(BRCM_WLAN_IFNAME); module_param_string(intf_name, intf_name, IFNAMSIZ, 0); static const u_int8_t brcm_oui[] = {0x00, 0x10, 0x18}; #define WL_RADIOTAP_BRCM2_HT_SNS 0x01 #define WL_RADIOTAP_BRCM2_HT_MCS 0x00000001 #define WL_RADIOTAP_LEGACY_SNS 0x02 #define WL_RADIOTAP_LEGACY_VHT 0x00000001 #define IEEE80211_RADIOTAP_HTMOD_40 0x01 #define IEEE80211_RADIOTAP_HTMOD_SGI 0x02 #define IEEE80211_RADIOTAP_HTMOD_GF 0x04 #define IEEE80211_RADIOTAP_HTMOD_LDPC 0x08 #define IEEE80211_RADIOTAP_HTMOD_STBC_MASK 0x30 #define IEEE80211_RADIOTAP_HTMOD_STBC_SHIFT 4 #define WL_RADIOTAP_F_NONHT_VHT_DYN_BW 0x01 #define WL_RADIOTAP_F_NONHT_VHT_BW 0x02 struct wl_radiotap_nonht_vht { u_int8_t len; u_int8_t flags; u_int8_t bw; } __attribute__ ((packed)); typedef struct wl_radiotap_nonht_vht wl_radiotap_nonht_vht_t; struct wl_radiotap_legacy { struct ieee80211_radiotap_header ieee_radiotap; u_int32_t it_present_ext; u_int32_t pad1; uint32 tsft_l; uint32 tsft_h; uint8 flags; uint8 rate; uint16 channel_freq; uint16 channel_flags; uint8 signal; uint8 noise; int8 antenna; uint8 pad2; u_int8_t vend_oui[3]; u_int8_t vend_sns; u_int16_t vend_skip_len; wl_radiotap_nonht_vht_t nonht_vht; } __attribute__ ((__packed__)); typedef struct wl_radiotap_legacy wl_radiotap_legacy_t; #define WL_RADIOTAP_LEGACY_SKIP_LEN htol16(sizeof(struct wl_radiotap_legacy) - \ offsetof(struct wl_radiotap_legacy, nonht_vht)) #define WL_RADIOTAP_NONHT_VHT_LEN (sizeof(wl_radiotap_nonht_vht_t) - 1) struct wl_radiotap_ht_brcm_2 { struct ieee80211_radiotap_header ieee_radiotap; u_int32_t it_present_ext; u_int32_t pad1; uint32 tsft_l; uint32 tsft_h; u_int8_t flags; u_int8_t pad2; u_int16_t channel_freq; u_int16_t channel_flags; u_int8_t signal; u_int8_t noise; u_int8_t antenna; u_int8_t pad3; u_int8_t vend_oui[3]; u_int8_t vend_sns; u_int16_t vend_skip_len; u_int8_t mcs; u_int8_t htflags; } __attribute__ ((packed)); typedef struct wl_radiotap_ht_brcm_2 wl_radiotap_ht_brcm_2_t; #define WL_RADIOTAP_HT_BRCM2_SKIP_LEN htol16(sizeof(struct wl_radiotap_ht_brcm_2) - \ offsetof(struct wl_radiotap_ht_brcm_2, mcs)) struct wl_radiotap_ht_brcm_3 { struct ieee80211_radiotap_header ieee_radiotap; u_int32_t it_present_ext; u_int32_t pad1; uint32 tsft_l; uint32 tsft_h; u_int8_t flags; u_int8_t pad2; u_int16_t channel_freq; u_int16_t channel_flags; u_int8_t signal; u_int8_t noise; u_int8_t antenna; u_int8_t mcs_known; u_int8_t mcs_flags; u_int8_t mcs_index; u_int8_t vend_oui[3]; u_int8_t vend_sns; u_int16_t vend_skip_len; wl_radiotap_nonht_vht_t nonht_vht; } __attribute__ ((packed)); typedef struct wl_radiotap_ht_brcm_3 wl_radiotap_ht_brcm_3_t; struct wl_radiotap_ht { struct ieee80211_radiotap_header ieee_radiotap; uint32 tsft_l; uint32 tsft_h; u_int8_t flags; u_int8_t pad1; u_int16_t channel_freq; u_int16_t channel_flags; u_int8_t signal; u_int8_t noise; u_int8_t antenna; u_int8_t mcs_known; u_int8_t mcs_flags; u_int8_t mcs_index; } __attribute__ ((packed)); typedef struct wl_radiotap_ht wl_radiotap_ht_t; struct wl_radiotap_vht { struct ieee80211_radiotap_header ieee_radiotap; uint32 tsft_l; uint32 tsft_h; u_int8_t flags; u_int8_t pad1; u_int16_t channel_freq; u_int16_t channel_flags; u_int8_t signal; u_int8_t noise; u_int8_t antenna; u_int8_t pad2; u_int16_t pad3; uint32 ampdu_ref_num; u_int16_t ampdu_flags; u_int8_t ampdu_delim_crc; u_int8_t ampdu_reserved; u_int16_t vht_known; u_int8_t vht_flags; u_int8_t vht_bw; u_int8_t vht_mcs_nss[4]; u_int8_t vht_coding; u_int8_t vht_group_id; u_int16_t vht_partial_aid; } __attribute__ ((packed)); typedef struct wl_radiotap_vht wl_radiotap_vht_t; #define WL_RADIOTAP_PRESENT_LEGACY \ ((1 << IEEE80211_RADIOTAP_TSFT) | \ (1 << IEEE80211_RADIOTAP_RATE) | \ (1 << IEEE80211_RADIOTAP_CHANNEL) | \ (1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL) | \ (1 << IEEE80211_RADIOTAP_DBM_ANTNOISE) | \ (1 << IEEE80211_RADIOTAP_FLAGS) | \ (1 << IEEE80211_RADIOTAP_ANTENNA) | \ (1 << IEEE80211_RADIOTAP_VENDOR_NAMESPACE) | \ (1 << IEEE80211_RADIOTAP_EXT)) #define WL_RADIOTAP_PRESENT_HT_BRCM2 \ ((1 << IEEE80211_RADIOTAP_TSFT) | \ (1 << IEEE80211_RADIOTAP_FLAGS) | \ (1 << IEEE80211_RADIOTAP_CHANNEL) | \ (1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL) | \ (1 << IEEE80211_RADIOTAP_DBM_ANTNOISE) | \ (1 << IEEE80211_RADIOTAP_ANTENNA) | \ (1 << IEEE80211_RADIOTAP_VENDOR_NAMESPACE) | \ (1 << IEEE80211_RADIOTAP_EXT)) #define WL_RADIOTAP_PRESENT_HT \ ((1 << IEEE80211_RADIOTAP_TSFT) | \ (1 << IEEE80211_RADIOTAP_FLAGS) | \ (1 << IEEE80211_RADIOTAP_CHANNEL) | \ (1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL) | \ (1 << IEEE80211_RADIOTAP_DBM_ANTNOISE) | \ (1 << IEEE80211_RADIOTAP_ANTENNA) | \ (1 << IEEE80211_RADIOTAP_MCS)) #define WL_RADIOTAP_PRESENT_VHT \ ((1 << IEEE80211_RADIOTAP_TSFT) | \ (1 << IEEE80211_RADIOTAP_FLAGS) | \ (1 << IEEE80211_RADIOTAP_CHANNEL) | \ (1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL) | \ (1 << IEEE80211_RADIOTAP_DBM_ANTNOISE) | \ (1 << IEEE80211_RADIOTAP_ANTENNA) | \ (1 << IEEE80211_RADIOTAP_AMPDU) | \ (1 << IEEE80211_RADIOTAP_VHT)) #ifndef ARPHRD_IEEE80211_RADIOTAP #define ARPHRD_IEEE80211_RADIOTAP 803 #endif #ifndef SRCBASE #define SRCBASE "." #endif #if WIRELESS_EXT >= 19 || LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29) #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) static struct ethtool_ops wl_ethtool_ops = #else static const struct ethtool_ops wl_ethtool_ops = #endif { .get_drvinfo = wl_get_driver_info, }; #endif #if defined(WL_USE_NETDEV_OPS) static const struct net_device_ops wl_netdev_ops = { .ndo_open = wl_open, .ndo_stop = wl_close, .ndo_start_xmit = wl_start, .ndo_get_stats = wl_get_stats, .ndo_set_mac_address = wl_set_mac_address, #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) .ndo_set_rx_mode = wl_set_multicast_list, #else .ndo_set_multicast_list = wl_set_multicast_list, #endif .ndo_do_ioctl = wl_ioctl }; static const struct net_device_ops wl_netdev_monitor_ops = { .ndo_start_xmit = wl_monitor_start, .ndo_get_stats = wl_get_stats, .ndo_do_ioctl = wl_ioctl }; #endif static void wl_if_setup(struct net_device *dev) { #if defined(WL_USE_NETDEV_OPS) dev->netdev_ops = &wl_netdev_ops; #else dev->open = wl_open; dev->stop = wl_close; dev->hard_start_xmit = wl_start; dev->get_stats = wl_get_stats; dev->set_mac_address = wl_set_mac_address; dev->set_multicast_list = wl_set_multicast_list; dev->do_ioctl = wl_ioctl; #endif #ifdef USE_IW #if WIRELESS_EXT < 19 dev->get_wireless_stats = wl_get_wireless_stats; #endif #if WIRELESS_EXT > 12 dev->wireless_handlers = (struct iw_handler_def *) &wl_iw_handler_def; #endif #endif #if WIRELESS_EXT >= 19 || LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29) dev->ethtool_ops = &wl_ethtool_ops; #endif } static wl_info_t * wl_attach(uint16 vendor, uint16 device, ulong regs, uint bustype, void *btparam, uint irq, uchar* bar1_addr, uint32 bar1_size) { struct net_device *dev; wl_if_t *wlif; wl_info_t *wl; osl_t *osh; int unit, err; #if defined(USE_CFG80211) struct device *parentdev; #endif unit = wl_found + instance_base; err = 0; if (unit < 0) { WL_ERROR(("wl%d: unit number overflow, exiting\n", unit)); return NULL; } if (oneonly && (unit != instance_base)) { WL_ERROR(("wl%d: wl_attach: oneonly is set, exiting\n", unit)); return NULL; } osh = osl_attach(btparam, bustype, TRUE); ASSERT(osh); if ((wl = (wl_info_t*) MALLOC(osh, sizeof(wl_info_t))) == NULL) { WL_ERROR(("wl%d: malloc wl_info_t, out of memory, malloced %d bytes\n", unit, MALLOCED(osh))); osl_detach(osh); return NULL; } bzero(wl, sizeof(wl_info_t)); wl->osh = osh; wl->unit = unit; atomic_set(&wl->callbacks, 0); wl->all_dispatch_mode = (passivemode == 0) ? TRUE : FALSE; if (WL_ALL_PASSIVE_ENAB(wl)) { MY_INIT_WORK(&wl->txq_task.work, (work_func_t)wl_start_txqwork); wl->txq_task.context = wl; MY_INIT_WORK(&wl->multicast_task.work, (work_func_t)wl_set_multicast_list_workitem); MY_INIT_WORK(&wl->wl_dpc_task.work, (work_func_t)wl_dpc_rxwork); wl->wl_dpc_task.context = wl; } wl->txq_dispatched = FALSE; wl->txq_head = wl->txq_tail = NULL; wl->txq_cnt = 0; wlif = wl_alloc_if(wl, WL_IFTYPE_BSS, unit, NULL); if (!wlif) { WL_ERROR(("wl%d: %s: wl_alloc_if failed\n", unit, __FUNCTION__)); MFREE(osh, wl, sizeof(wl_info_t)); osl_detach(osh); return NULL; } if (wl_alloc_linux_if(wlif) == NULL) { WL_ERROR(("wl%d: %s: wl_alloc_linux_if failed\n", unit, __FUNCTION__)); MFREE(osh, wl, sizeof(wl_info_t)); osl_detach(osh); return NULL; } dev = wlif->dev; wl->dev = dev; wl_if_setup(dev); dev->base_addr = regs; WL_TRACE(("wl%d: Bus: ", unit)); if (bustype == PCMCIA_BUS) { wl->piomode = TRUE; WL_TRACE(("PCMCIA\n")); } else if (bustype == PCI_BUS) { wl->piomode = piomode; WL_TRACE(("PCI/%s\n", wl->piomode ? "PIO" : "DMA")); } else if (bustype == RPC_BUS) { } else { bustype = PCI_BUS; WL_TRACE(("force to PCI\n")); } wl->bcm_bustype = bustype; if ((wl->regsva = ioremap_nocache(dev->base_addr, PCI_BAR0_WINSZ)) == NULL) { WL_ERROR(("wl%d: ioremap() failed\n", unit)); goto fail; } wl->bar1_addr = bar1_addr; wl->bar1_size = bar1_size; spin_lock_init(&wl->lock); spin_lock_init(&wl->isr_lock); if (WL_ALL_PASSIVE_ENAB(wl)) sema_init(&wl->sem, 1); spin_lock_init(&wl->txq_lock); if (!(wl->wlc = wlc_attach((void *) wl, vendor, device, unit, wl->piomode, osh, wl->regsva, wl->bcm_bustype, btparam, &err))) { printf("wl driver %s failed with code %d\n", EPI_VERSION_STR, err); goto fail; } wl->pub = wlc_pub(wl->wlc); wlif->wlcif = wlc_wlcif_get_by_index(wl->wlc, 0); if (nompc) { if (wlc_iovar_setint(wl->wlc, "mpc", 0)) { WL_ERROR(("wl%d: Error setting MPC variable to 0\n", unit)); } } wlc_iovar_setint(wl->wlc, "scan_passive_time", 170); wlc_iovar_setint(wl->wlc, "qtxpower", 23 * 4); #ifdef BCMDBG if (macaddr != NULL) { int dbg_err; WL_ERROR(("wl%d: setting MAC ADDRESS %s\n", unit, macaddr)); bcm_ether_atoe(macaddr, &local_ea); dbg_err = wlc_iovar_op(wl->wlc, "cur_etheraddr", NULL, 0, &local_ea, ETHER_ADDR_LEN, IOV_SET, NULL); if (dbg_err) WL_ERROR(("wl%d: Error setting MAC ADDRESS\n", unit)); } #endif bcopy(&wl->pub->cur_etheraddr, dev->dev_addr, ETHER_ADDR_LEN); online_cpus = 1; WL_ERROR(("wl%d: online cpus %d\n", unit, online_cpus)); tasklet_init(&wl->tasklet, wl_dpc, (ulong)wl); tasklet_init(&wl->tx_tasklet, wl_tx_tasklet, (ulong)wl); { if (request_irq(irq, wl_isr, IRQF_SHARED, dev->name, wl)) { WL_ERROR(("wl%d: request_irq() failed\n", unit)); goto fail; } dev->irq = irq; } #if defined(USE_IW) WL_ERROR(("Using Wireless Extension\n")); #endif #if defined(USE_CFG80211) parentdev = NULL; if (wl->bcm_bustype == PCI_BUS) { parentdev = &((struct pci_dev *)btparam)->dev; } if (parentdev) { if (wl_cfg80211_attach(dev, parentdev, WL_ALL_PASSIVE_ENAB(wl))) { goto fail; } } else { WL_ERROR(("unsupported bus type\n")); goto fail; } #else if (wl->bcm_bustype == PCI_BUS) { struct pci_dev *pci_dev = (struct pci_dev *)btparam; if (pci_dev != NULL) SET_NETDEV_DEV(dev, &pci_dev->dev); } #endif if (register_netdev(dev)) { WL_ERROR(("wl%d: register_netdev() failed\n", unit)); goto fail; } wlif->dev_registed = TRUE; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 14) #endif #ifdef USE_IW wlif->iw.wlinfo = (void *)wl; #endif #if defined(WL_CONFIG_RFKILL) if (wl_init_rfkill(wl) < 0) WL_ERROR(("%s: init_rfkill_failure\n", __FUNCTION__)); #endif if (wlc_iovar_setint(wl->wlc, "leddc", 0xa0000)) { WL_ERROR(("wl%d: Error setting led duty-cycle\n", unit)); } if (wlc_set(wl->wlc, WLC_SET_PM, PM_FAST)) { WL_ERROR(("wl%d: Error setting PM variable to FAST PS\n", unit)); } if (wlc_iovar_setint(wl->wlc, "vlan_mode", OFF)) { WL_ERROR(("wl%d: Error setting vlan mode OFF\n", unit)); } if (wlc_set(wl->wlc, WLC_SET_INFRA, 1)) { WL_ERROR(("wl%d: Error setting infra_mode to infrastructure\n", unit)); } if (wlc_module_register(wl->pub, NULL, "linux", wl, NULL, wl_linux_watchdog, NULL, NULL)) { WL_ERROR(("wl%d: %s wlc_module_register() failed\n", wl->pub->unit, __FUNCTION__)); goto fail; } #ifdef BCMDBG wlc_dump_register(wl->pub, "wl", (dump_fn_t)wl_dump, (void *)wl); #endif wl_reg_proc_entry(wl); printf("%s: Broadcom BCM%04x 802.11 Hybrid Wireless Controller%s %s", dev->name, device, WL_ALL_PASSIVE_ENAB(wl) ? ", Passive Mode" : "", EPI_VERSION_STR); #ifdef BCMDBG printf(" (Compiled in " SRCBASE " at " __TIME__ " on " __DATE__ ")"); #endif printf("\n"); wl_found++; return wl; fail: wl_free(wl); return NULL; } static void __devexit wl_remove(struct pci_dev *pdev); int __devinit wl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { int rc; wl_info_t *wl; uint32 val; uint32 bar1_size = 0; void* bar1_addr = NULL; WL_TRACE(("%s: bus %d slot %d func %d irq %d\n", __FUNCTION__, pdev->bus->number, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn), pdev->irq)); if ((pdev->vendor != PCI_VENDOR_ID_BROADCOM) || (((pdev->device & 0xff00) != 0x4300) && (pdev->device != 0x576) && ((pdev->device & 0xff00) != 0x4700) && ((pdev->device < 43000) || (pdev->device > 43999)))) { WL_TRACE(("%s: unsupported vendor %x device %x\n", __FUNCTION__, pdev->vendor, pdev->device)); return (-ENODEV); } rc = pci_enable_device(pdev); if (rc) { WL_ERROR(("%s: Cannot enable device %d-%d_%d\n", __FUNCTION__, pdev->bus->number, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn))); return (-ENODEV); } pci_set_master(pdev); pci_read_config_dword(pdev, 0x40, &val); if ((val & 0x0000ff00) != 0) pci_write_config_dword(pdev, 0x40, val & 0xffff00ff); bar1_size = pci_resource_len(pdev, 2); bar1_addr = (uchar *)ioremap_nocache(pci_resource_start(pdev, 2), bar1_size); wl = wl_attach(pdev->vendor, pdev->device, pci_resource_start(pdev, 0), PCI_BUS, pdev, pdev->irq, bar1_addr, bar1_size); if (!wl) return -ENODEV; pci_set_drvdata(pdev, wl); return 0; } static int #if !defined(SIMPLE_DEV_PM_OPS) wl_suspend(struct pci_dev *pdev, DRV_SUSPEND_STATE_TYPE state) { #else wl_suspend(struct device *dev) { struct pci_dev *pdev = to_pci_dev(dev); #endif wl_info_t *wl = (wl_info_t *) pci_get_drvdata(pdev); if (!wl) { WL_ERROR(("wl: wl_suspend: pci_get_drvdata failed\n")); return -ENODEV; } WL_ERROR(("%s: PCI Suspend handler\n", __FUNCTION__)); WL_LOCK(wl); if (WLOFFLD_ENAB(wl->pub) && wlc_iovar_setint(wl->wlc, "wowl_activate", 1) == 0) { WL_TRACE(("%s: Enabled WOWL OFFLOAD\n", __FUNCTION__)); } else { WL_ERROR(("%s: Not WOWL capable\n", __FUNCTION__)); wl_down(wl); wl->pub->hw_up = FALSE; } WL_UNLOCK(wl); if (BUSTYPE(wl->pub->sih->bustype) == PCI_BUS) si_pci_sleep(wl->pub->sih); return 0; } static int #if !defined(SIMPLE_DEV_PM_OPS) wl_resume(struct pci_dev *pdev) { #else wl_resume(struct device *dev) { struct pci_dev *pdev = to_pci_dev(dev); #endif int err = 0; wl_info_t *wl = (wl_info_t *) pci_get_drvdata(pdev); if (!wl) { WL_ERROR(("wl: wl_resume: pci_get_drvdata failed\n")); return -ENODEV; } WL_ERROR(("%s: PCI Resume handler\n", __FUNCTION__)); if (WLOFFLD_ENAB(wl->pub)) { wlc_iovar_setint(wl->wlc, "wowl_activate", 0); wlc_wowl_wake_reason_process(wl->wlc); if (WOWL_ACTIVE(wl->pub)) { if (BUSTYPE(wl->pub->sih->bustype) == PCI_BUS) { si_pci_pmeclr(wl->pub->sih); } } } WL_LOCK(wl); err = wl_up(wl); WL_UNLOCK(wl); return (err); } static void __devexit wl_remove(struct pci_dev *pdev) { wl_info_t *wl = (wl_info_t *) pci_get_drvdata(pdev); if (!wl) { WL_ERROR(("wl: wl_remove: pci_get_drvdata failed\n")); return; } if (!wlc_chipmatch(pdev->vendor, pdev->device)) { WL_ERROR(("wl: wl_remove: wlc_chipmatch failed\n")); return; } WL_LOCK(wl); WL_APSTA_UPDN(("wl%d (%s): wl_remove() -> wl_down()\n", wl->pub->unit, wl->dev->name)); wl_down(wl); WL_UNLOCK(wl); wl_free(wl); pci_disable_device(pdev); pci_set_drvdata(pdev, NULL); } #if defined(SIMPLE_DEV_PM_OPS) static SIMPLE_DEV_PM_OPS(wl_pm_ops, wl_suspend, wl_resume); #endif static struct pci_driver wl_pci_driver __refdata = { .name = "wl", .probe = wl_pci_probe, .remove = __devexit_p(wl_remove), .id_table = wl_id_table, #ifdef SIMPLE_DEV_PM_OPS .driver.pm = &wl_pm_ops, #else .suspend = wl_suspend, .resume = wl_resume, #endif }; static int __init wl_module_init(void) { int error = -ENODEV; #ifdef BCMDBG if (msglevel != 0xdeadbeef) wl_msg_level = msglevel; else { const char *var = getvar(NULL, "wl_msglevel"); if (var) wl_msg_level = bcm_strtoul(var, NULL, 0); } printf("%s: msglevel set to 0x%x\n", __FUNCTION__, wl_msg_level); if (msglevel2 != 0xdeadbeef) wl_msg_level2 = msglevel2; else { const char *var = getvar(NULL, "wl_msglevel2"); if (var) wl_msg_level2 = bcm_strtoul(var, NULL, 0); } printf("%s: msglevel2 set to 0x%x\n", __FUNCTION__, wl_msg_level2); { extern uint32 phyhal_msg_level; if (phymsglevel != 0xdeadbeef) phyhal_msg_level = phymsglevel; else { const char *var = getvar(NULL, "phy_msglevel"); if (var) phyhal_msg_level = bcm_strtoul(var, NULL, 0); } printf("%s: phymsglevel set to 0x%x\n", __FUNCTION__, phyhal_msg_level); } #endif { const char *var = getvar(NULL, "wl_dispatch_mode"); if (var) passivemode = bcm_strtoul(var, NULL, 0); if (passivemode) printf("%s: passivemode enabled\n", __FUNCTION__); } #ifdef BCMDBG_ASSERT if (assert_type != 0xdeadbeef) g_assert_type = assert_type; #endif { char *var = getvar(NULL, "wl_txq_thresh"); if (var) wl_txq_thresh = bcm_strtoul(var, NULL, 0); #ifdef BCMDBG WL_INFORM(("%s: wl_txq_thresh set to 0x%x\n", __FUNCTION__, wl_txq_thresh)); #endif } if (!(error = pci_module_init(&wl_pci_driver))) return (0); return (error); } static void __exit wl_module_exit(void) { pci_unregister_driver(&wl_pci_driver); } module_init(wl_module_init); module_exit(wl_module_exit); void wl_free(wl_info_t *wl) { wl_timer_t *t, *next; osl_t *osh; WL_TRACE(("wl: wl_free\n")); { if (wl->dev && wl->dev->irq) free_irq(wl->dev->irq, wl); } #if defined(WL_CONFIG_RFKILL) wl_uninit_rfkill(wl); #endif if (wl->dev) { wl_free_if(wl, WL_DEV_IF(wl->dev)); wl->dev = NULL; } tasklet_kill(&wl->tasklet); tasklet_kill(&wl->tx_tasklet); if (wl->pub) { wlc_module_unregister(wl->pub, "linux", wl); } if (wl->wlc) { { char tmp1[128]; sprintf(tmp1, "%s%d", HYBRID_PROC, wl->pub->unit); remove_proc_entry(tmp1, 0); } wlc_detach(wl->wlc); wl->wlc = NULL; wl->pub = NULL; } while (atomic_read(&wl->callbacks) > 0) schedule(); for (t = wl->timers; t; t = next) { next = t->next; #ifdef BCMDBG if (t->name) MFREE(wl->osh, t->name, strlen(t->name) + 1); #endif MFREE(wl->osh, t, sizeof(wl_timer_t)); } osh = wl->osh; if (wl->regsva && BUSTYPE(wl->bcm_bustype) != SDIO_BUS && BUSTYPE(wl->bcm_bustype) != JTAG_BUS) { iounmap((void*)wl->regsva); } wl->regsva = NULL; if (wl->bar1_addr) { iounmap(wl->bar1_addr); wl->bar1_addr = NULL; } #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 14) #endif wl_txq_free(wl); MFREE(osh, wl, sizeof(wl_info_t)); if (MALLOCED(osh)) { printf("Memory leak of bytes %d\n", MALLOCED(osh)); #ifndef BCMDBG_MEM ASSERT(0); #endif } #ifdef BCMDBG_MEM MALLOC_DUMP(osh, NULL); #endif osl_detach(osh); } static int wl_open(struct net_device *dev) { wl_info_t *wl; int error = 0; if (!dev) return -ENETDOWN; wl = WL_INFO(dev); WL_TRACE(("wl%d: wl_open\n", wl->pub->unit)); WL_LOCK(wl); WL_APSTA_UPDN(("wl%d: (%s): wl_open() -> wl_up()\n", wl->pub->unit, wl->dev->name)); error = wl_up(wl); if (!error) { error = wlc_set(wl->wlc, WLC_SET_PROMISC, (dev->flags & IFF_PROMISC)); } WL_UNLOCK(wl); if (!error) OLD_MOD_INC_USE_COUNT; #if defined(USE_CFG80211) if (wl_cfg80211_up(dev)) { WL_ERROR(("%s: failed to bring up cfg80211\n", __func__)); return -1; } #endif return (error? -ENODEV : 0); } static int wl_close(struct net_device *dev) { wl_info_t *wl; if (!dev) return -ENETDOWN; #if defined(USE_CFG80211) wl_cfg80211_down(dev); #endif wl = WL_INFO(dev); WL_TRACE(("wl%d: wl_close\n", wl->pub->unit)); WL_LOCK(wl); WL_APSTA_UPDN(("wl%d (%s): wl_close() -> wl_down()\n", wl->pub->unit, wl->dev->name)); if (wl->if_list == NULL) { wl_down(wl); } WL_UNLOCK(wl); OLD_MOD_DEC_USE_COUNT; return (0); } void * BCMFASTPATH wl_get_ifctx(struct wl_info *wl, int ctx_id, wl_if_t *wlif) { void *ifctx = NULL; switch (ctx_id) { case IFCTX_NETDEV: ifctx = (void *)((wlif == NULL) ? wl->dev : wlif->dev); break; default: break; } return ifctx; } static int BCMFASTPATH wl_start_int(wl_info_t *wl, wl_if_t *wlif, struct sk_buff *skb) { void *pkt; WL_TRACE(("wl%d: wl_start: len %d data_len %d summed %d csum: 0x%x\n", wl->pub->unit, skb->len, skb->data_len, skb->ip_summed, (uint32)skb->csum)); WL_LOCK(wl); pkt = PKTFRMNATIVE(wl->osh, skb); ASSERT(pkt != NULL); if (WME_ENAB(wl->pub) && (PKTPRIO(pkt) == 0)) pktsetprio(pkt, FALSE); wlc_sendpkt(wl->wlc, pkt, wlif->wlcif); WL_UNLOCK(wl); return (0); } void wl_txflowcontrol(wl_info_t *wl, struct wl_if *wlif, bool state, int prio) { struct net_device *dev; ASSERT(prio == ALLPRIO); if (wlif == NULL) dev = wl->dev; else if (!wlif->dev_registed) return; else dev = wlif->dev; if (state == ON) netif_stop_queue(dev); else netif_wake_queue(dev); } static int wl_schedule_task(wl_info_t *wl, void (*fn)(struct wl_task *task), void *context) { wl_task_t *task; WL_TRACE(("wl%d: wl_schedule_task\n", wl->pub->unit)); if (!(task = MALLOC(wl->osh, sizeof(wl_task_t)))) { WL_ERROR(("wl%d: wl_schedule_task: out of memory, malloced %d bytes\n", wl->pub->unit, MALLOCED(wl->osh))); return -ENOMEM; } MY_INIT_WORK(&task->work, (work_func_t)fn); task->context = context; if (!schedule_work(&task->work)) { WL_ERROR(("wl%d: schedule_work() failed\n", wl->pub->unit)); MFREE(wl->osh, task, sizeof(wl_task_t)); return -ENOMEM; } atomic_inc(&wl->callbacks); return 0; } static struct wl_if * wl_alloc_if(wl_info_t *wl, int iftype, uint subunit, struct wlc_if *wlcif) { wl_if_t *wlif; wl_if_t *p; if (!(wlif = MALLOC(wl->osh, sizeof(wl_if_t)))) { WL_ERROR(("wl%d: wl_alloc_if: out of memory, malloced %d bytes\n", (wl->pub)?wl->pub->unit:subunit, MALLOCED(wl->osh))); return NULL; } bzero(wlif, sizeof(wl_if_t)); wlif->wl = wl; wlif->wlcif = wlcif; wlif->subunit = subunit; wlif->if_type = iftype; if (wl->if_list == NULL) wl->if_list = wlif; else { p = wl->if_list; while (p->next != NULL) p = p->next; p->next = wlif; } return wlif; } static void wl_free_if(wl_info_t *wl, wl_if_t *wlif) { wl_if_t *p; ASSERT(wlif); ASSERT(wl); WL_TRACE(("%s\n", __FUNCTION__)); if (wlif->dev_registed) { ASSERT(wlif->dev); unregister_netdev(wlif->dev); wlif->dev_registed = FALSE; } #if defined(USE_CFG80211) wl_cfg80211_detach(wlif->dev); #endif p = wl->if_list; if (p == wlif) wl->if_list = p->next; else { while (p != NULL && p->next != wlif) p = p->next; if (p != NULL) p->next = p->next->next; } if (wlif->dev) { #if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 24)) MFREE(wl->osh, wlif->dev->priv, sizeof(priv_link_t)); MFREE(wl->osh, wlif->dev, sizeof(struct net_device)); #else free_netdev(wlif->dev); wlif->dev = NULL; #endif } MFREE(wl->osh, wlif, sizeof(wl_if_t)); } static struct net_device * wl_alloc_linux_if(wl_if_t *wlif) { wl_info_t *wl = wlif->wl; struct net_device *dev; priv_link_t *priv_link; WL_TRACE(("%s\n", __FUNCTION__)); #if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 24)) dev = MALLOC(wl->osh, sizeof(struct net_device)); if (!dev) { WL_ERROR(("wl%d: %s: malloc of net_device failed\n", (wl->pub)?wl->pub->unit:wlif->subunit, __FUNCTION__)); return NULL; } bzero(dev, sizeof(struct net_device)); ether_setup(dev); strncpy(dev->name, intf_name, IFNAMSIZ-1); dev->name[IFNAMSIZ-1] = '\0'; priv_link = MALLOC(wl->osh, sizeof(priv_link_t)); if (!priv_link) { WL_ERROR(("wl%d: %s: malloc of priv_link failed\n", (wl->pub)?wl->pub->unit:wlif->subunit, __FUNCTION__)); MFREE(wl->osh, dev, sizeof(struct net_device)); return NULL; } dev->priv = priv_link; #else #if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 17, 0)) dev = alloc_netdev(sizeof(priv_link_t), intf_name, ether_setup); #else dev = alloc_netdev(sizeof(priv_link_t), intf_name, NET_NAME_UNKNOWN, ether_setup); #endif if (!dev) { WL_ERROR(("wl%d: %s: alloc_netdev failed\n", (wl->pub)?wl->pub->unit:wlif->subunit, __FUNCTION__)); return NULL; } priv_link = netdev_priv(dev); if (!priv_link) { WL_ERROR(("wl%d: %s: cannot get netdev_priv\n", (wl->pub)?wl->pub->unit:wlif->subunit, __FUNCTION__)); return NULL; } #endif priv_link->wlif = wlif; wlif->dev = dev; if (wlif->if_type != WL_IFTYPE_MON && wl->dev && netif_queue_stopped(wl->dev)) netif_stop_queue(dev); return dev; } char * wl_ifname(wl_info_t *wl, wl_if_t *wlif) { if (wlif) { return wlif->name; } else { return wl->dev->name; } } void wl_init(wl_info_t *wl) { WL_TRACE(("wl%d: wl_init\n", wl->pub->unit)); wl_reset(wl); wlc_init(wl->wlc); } uint wl_reset(wl_info_t *wl) { uint32 macintmask; WL_TRACE(("wl%d: wl_reset\n", wl->pub->unit)); macintmask = wl_intrsoff(wl); wlc_reset(wl->wlc); wl_intrsrestore(wl, macintmask); wl->resched = 0; return (0); } void BCMFASTPATH wl_intrson(wl_info_t *wl) { unsigned long flags = 0; INT_LOCK(wl, flags); wlc_intrson(wl->wlc); INT_UNLOCK(wl, flags); } bool wl_alloc_dma_resources(wl_info_t *wl, uint addrwidth) { return TRUE; } uint32 BCMFASTPATH wl_intrsoff(wl_info_t *wl) { unsigned long flags = 0; uint32 status; INT_LOCK(wl, flags); status = wlc_intrsoff(wl->wlc); INT_UNLOCK(wl, flags); return status; } void wl_intrsrestore(wl_info_t *wl, uint32 macintmask) { unsigned long flags = 0; INT_LOCK(wl, flags); wlc_intrsrestore(wl->wlc, macintmask); INT_UNLOCK(wl, flags); } int wl_up(wl_info_t *wl) { int error = 0; wl_if_t *wlif; WL_TRACE(("wl%d: wl_up\n", wl->pub->unit)); if (wl->pub->up) return (0); error = wlc_up(wl->wlc); if (!error) { for (wlif = wl->if_list; wlif != NULL; wlif = wlif->next) { wl_txflowcontrol(wl, wlif, OFF, ALLPRIO); } } return (error); } void wl_down(wl_info_t *wl) { wl_if_t *wlif; int monitor = 0; uint callbacks, ret_val = 0; WL_TRACE(("wl%d: wl_down\n", wl->pub->unit)); for (wlif = wl->if_list; wlif != NULL; wlif = wlif->next) { if (wlif->dev) { netif_down(wlif->dev); netif_stop_queue(wlif->dev); } } if (wl->monitor_dev) { ret_val = wlc_ioctl(wl->wlc, WLC_SET_MONITOR, &monitor, sizeof(int), NULL); if (ret_val != BCME_OK) { WL_ERROR(("%s: Disabling MONITOR failed %d\n", __FUNCTION__, ret_val)); } } if (wl->wlc) ret_val = wlc_down(wl->wlc); callbacks = atomic_read(&wl->callbacks) - ret_val; BCM_REFERENCE(callbacks); WL_UNLOCK(wl); if (WL_ALL_PASSIVE_ENAB(wl)) { int i = 0; for (i = 0; (atomic_read(&wl->callbacks) > callbacks) && i < 10000; i++) { schedule(); flush_scheduled_work(); } } else { SPINWAIT((atomic_read(&wl->callbacks) > callbacks), 100 * 1000); } WL_LOCK(wl); } static int wl_toe_get(wl_info_t *wl, uint32 *toe_ol) { if (wlc_iovar_getint(wl->wlc, "toe_ol", toe_ol) != 0) return -EOPNOTSUPP; return 0; } static int wl_toe_set(wl_info_t *wl, uint32 toe_ol) { if (wlc_iovar_setint(wl->wlc, "toe_ol", toe_ol) != 0) return -EOPNOTSUPP; if (wlc_iovar_setint(wl->wlc, "toe", (toe_ol != 0)) != 0) return -EOPNOTSUPP; return 0; } static void wl_get_driver_info(struct net_device *dev, struct ethtool_drvinfo *info) { wl_info_t *wl = WL_INFO(dev); #if WIRELESS_EXT >= 19 || LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29) if (!wl || !wl->pub || !wl->wlc || !wl->dev) return; #endif bzero(info, sizeof(struct ethtool_drvinfo)); snprintf(info->driver, sizeof(info->driver), "wl%d", wl->pub->unit); strncpy(info->version, EPI_VERSION_STR, sizeof(info->version)); info->version[(sizeof(info->version))-1] = '\0'; } static int wl_ethtool(wl_info_t *wl, void *uaddr, wl_if_t *wlif) { struct ethtool_drvinfo info; struct ethtool_value edata; uint32 cmd; uint32 toe_cmpnt = 0, csum_dir; int ret; if (!wl || !wl->pub || !wl->wlc) return -ENODEV; WL_TRACE(("wl%d: %s\n", wl->pub->unit, __FUNCTION__)); if (copy_from_user(&cmd, uaddr, sizeof(uint32))) return (-EFAULT); switch (cmd) { case ETHTOOL_GDRVINFO: if (!wl->dev) return -ENETDOWN; wl_get_driver_info(wl->dev, &info); info.cmd = cmd; if (copy_to_user(uaddr, &info, sizeof(info))) return (-EFAULT); break; case ETHTOOL_GRXCSUM: case ETHTOOL_GTXCSUM: if ((ret = wl_toe_get(wl, &toe_cmpnt)) < 0) return ret; csum_dir = (cmd == ETHTOOL_GTXCSUM) ? TOE_TX_CSUM_OL : TOE_RX_CSUM_OL; edata.cmd = cmd; edata.data = (toe_cmpnt & csum_dir) ? 1 : 0; if (copy_to_user(uaddr, &edata, sizeof(edata))) return (-EFAULT); break; case ETHTOOL_SRXCSUM: case ETHTOOL_STXCSUM: if (copy_from_user(&edata, uaddr, sizeof(edata))) return (-EFAULT); if ((ret = wl_toe_get(wl, &toe_cmpnt)) < 0) return ret; csum_dir = (cmd == ETHTOOL_STXCSUM) ? TOE_TX_CSUM_OL : TOE_RX_CSUM_OL; if (edata.data != 0) toe_cmpnt |= csum_dir; else toe_cmpnt &= ~csum_dir; if ((ret = wl_toe_set(wl, toe_cmpnt)) < 0) return ret; if (cmd == ETHTOOL_STXCSUM) { if (!wl->dev) return -ENETDOWN; if (edata.data) wl->dev->features |= NETIF_F_IP_CSUM; else wl->dev->features &= ~NETIF_F_IP_CSUM; } break; default: return (-EOPNOTSUPP); } return (0); } int wl_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) { wl_info_t *wl; wl_if_t *wlif; void *buf = NULL; wl_ioctl_t ioc; int bcmerror; if (!dev) return -ENETDOWN; wl = WL_INFO(dev); wlif = WL_DEV_IF(dev); if (wlif == NULL || wl == NULL || wl->dev == NULL) return -ENETDOWN; bcmerror = 0; WL_TRACE(("wl%d: wl_ioctl: cmd 0x%x\n", wl->pub->unit, cmd)); #ifdef USE_IW if ((cmd >= SIOCIWFIRST) && (cmd <= SIOCIWLAST)) { return wl_iw_ioctl(dev, ifr, cmd); } #endif if (cmd == SIOCETHTOOL) return (wl_ethtool(wl, (void*)ifr->ifr_data, wlif)); switch (cmd) { case SIOCDEVPRIVATE : break; default: bcmerror = BCME_UNSUPPORTED; goto done2; } if (copy_from_user(&ioc, ifr->ifr_data, sizeof(wl_ioctl_t))) { bcmerror = BCME_BADADDR; goto done2; } if (segment_eq(get_fs(), KERNEL_DS)) buf = ioc.buf; else if (ioc.buf) { if (!(buf = (void *) MALLOC(wl->osh, MAX(ioc.len, WLC_IOCTL_MAXLEN)))) { bcmerror = BCME_NORESOURCE; goto done2; } if (copy_from_user(buf, ioc.buf, ioc.len)) { bcmerror = BCME_BADADDR; goto done1; } } WL_LOCK(wl); if (!capable(CAP_NET_ADMIN)) { bcmerror = BCME_EPERM; } else { bcmerror = wlc_ioctl(wl->wlc, ioc.cmd, buf, ioc.len, wlif->wlcif); } WL_UNLOCK(wl); done1: if (ioc.buf && (ioc.buf != buf)) { if (copy_to_user(ioc.buf, buf, ioc.len)) bcmerror = BCME_BADADDR; MFREE(wl->osh, buf, MAX(ioc.len, WLC_IOCTL_MAXLEN)); } done2: ASSERT(VALID_BCMERROR(bcmerror)); if (bcmerror != 0) wl->pub->bcmerror = bcmerror; return (OSL_ERROR(bcmerror)); } static struct net_device_stats* wl_get_stats(struct net_device *dev) { struct net_device_stats *stats_watchdog = NULL; struct net_device_stats *stats = NULL; wl_info_t *wl; wl_if_t *wlif; if (!dev) return NULL; if ((wl = WL_INFO(dev)) == NULL) return NULL; if ((wlif = WL_DEV_IF(dev)) == NULL) return NULL; if ((stats = &wlif->stats) == NULL) return NULL; WL_TRACE(("wl%d: wl_get_stats\n", wl->pub->unit)); ASSERT(wlif->stats_id < 2); stats_watchdog = &wlif->stats_watchdog[wlif->stats_id]; memcpy(stats, stats_watchdog, sizeof(struct net_device_stats)); return (stats); } #ifdef USE_IW struct iw_statistics * wl_get_wireless_stats(struct net_device *dev) { int res = 0; wl_info_t *wl; wl_if_t *wlif; struct iw_statistics *wstats = NULL; struct iw_statistics *wstats_watchdog = NULL; int phy_noise, rssi; if (!dev) return NULL; if ((wl = WL_INFO(dev)) == NULL) return NULL; if ((wlif = WL_DEV_IF(dev)) == NULL) return NULL; if ((wstats = &wlif->wstats) == NULL) return NULL; WL_TRACE(("wl%d: wl_get_wireless_stats\n", wl->pub->unit)); ASSERT(wlif->stats_id < 2); wstats_watchdog = &wlif->wstats_watchdog[wlif->stats_id]; phy_noise = wlif->phy_noise; #if WIRELESS_EXT > 11 wstats->discard.nwid = 0; wstats->discard.code = wstats_watchdog->discard.code; wstats->discard.fragment = wstats_watchdog->discard.fragment; wstats->discard.retries = wstats_watchdog->discard.retries; wstats->discard.misc = wstats_watchdog->discard.misc; wstats->miss.beacon = 0; #endif if (AP_ENAB(wl->pub)) rssi = 0; else { scb_val_t scb; res = wlc_ioctl(wl->wlc, WLC_GET_RSSI, &scb, sizeof(int), wlif->wlcif); if (res) { WL_ERROR(("wl%d: %s: WLC_GET_RSSI failed (%d)\n", wl->pub->unit, __FUNCTION__, res)); return NULL; } rssi = scb.val; } if (rssi <= WLC_RSSI_NO_SIGNAL) wstats->qual.qual = 0; else if (rssi <= WLC_RSSI_VERY_LOW) wstats->qual.qual = 1; else if (rssi <= WLC_RSSI_LOW) wstats->qual.qual = 2; else if (rssi <= WLC_RSSI_GOOD) wstats->qual.qual = 3; else if (rssi <= WLC_RSSI_VERY_GOOD) wstats->qual.qual = 4; else wstats->qual.qual = 5; wstats->qual.level = 0x100 + rssi; wstats->qual.noise = 0x100 + phy_noise; #if WIRELESS_EXT > 18 wstats->qual.updated |= (IW_QUAL_ALL_UPDATED | IW_QUAL_DBM); #else wstats->qual.updated |= 7; #endif return wstats; } #endif static int wl_set_mac_address(struct net_device *dev, void *addr) { int err = 0; wl_info_t *wl; struct sockaddr *sa = (struct sockaddr *) addr; if (!dev) return -ENETDOWN; wl = WL_INFO(dev); WL_TRACE(("wl%d: wl_set_mac_address\n", wl->pub->unit)); WL_LOCK(wl); bcopy(sa->sa_data, dev->dev_addr, ETHER_ADDR_LEN); err = wlc_iovar_op(wl->wlc, "cur_etheraddr", NULL, 0, sa->sa_data, ETHER_ADDR_LEN, IOV_SET, (WL_DEV_IF(dev))->wlcif); WL_UNLOCK(wl); if (err) WL_ERROR(("wl%d: wl_set_mac_address: error setting MAC addr override\n", wl->pub->unit)); return err; } static void wl_set_multicast_list(struct net_device *dev) { if (!WL_ALL_PASSIVE_ENAB((wl_info_t *)WL_INFO(dev))) _wl_set_multicast_list(dev); else { wl_info_t *wl = WL_INFO(dev); wl->multicast_task.context = dev; if (schedule_work(&wl->multicast_task.work)) { atomic_inc(&wl->callbacks); } } } static void _wl_set_multicast_list(struct net_device *dev) { #if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 34) struct dev_mc_list *mclist; #else struct netdev_hw_addr *ha; #endif wl_info_t *wl; int i, buflen; struct maclist *maclist; int allmulti; if (!dev) return; wl = WL_INFO(dev); WL_TRACE(("wl%d: wl_set_multicast_list\n", wl->pub->unit)); if (wl->pub->up) { allmulti = (dev->flags & IFF_ALLMULTI)? TRUE: FALSE; buflen = sizeof(struct maclist) + (MAXMULTILIST * ETHER_ADDR_LEN); if ((maclist = MALLOC(wl->pub->osh, buflen)) == NULL) { return; } i = 0; #if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 34) for (mclist = dev->mc_list; mclist && (i < dev->mc_count); mclist = mclist->next) { if (i >= MAXMULTILIST) { allmulti = TRUE; i = 0; break; } bcopy(mclist->dmi_addr, &maclist->ea[i++], ETHER_ADDR_LEN); } #else netdev_for_each_mc_addr(ha, dev) { if (i >= MAXMULTILIST) { allmulti = TRUE; i = 0; break; } bcopy(ha->addr, &maclist->ea[i++], ETHER_ADDR_LEN); } #endif maclist->count = i; WL_LOCK(wl); wlc_iovar_op(wl->wlc, "allmulti", NULL, 0, &allmulti, sizeof(allmulti), IOV_SET, (WL_DEV_IF(dev))->wlcif); wlc_set(wl->wlc, WLC_SET_PROMISC, (dev->flags & IFF_PROMISC)); wlc_iovar_op(wl->wlc, "mcast_list", NULL, 0, maclist, buflen, IOV_SET, (WL_DEV_IF(dev))->wlcif); WL_UNLOCK(wl); MFREE(wl->pub->osh, maclist, buflen); } } #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20) irqreturn_t BCMFASTPATH wl_isr(int irq, void *dev_id) #else irqreturn_t BCMFASTPATH wl_isr(int irq, void *dev_id, struct pt_regs *ptregs) #endif { wl_info_t *wl; bool ours, wantdpc; unsigned long flags; wl = (wl_info_t*) dev_id; WL_ISRLOCK(wl, flags); if ((ours = wlc_isr(wl->wlc, &wantdpc))) { if (wantdpc) { ASSERT(wl->resched == FALSE); if (WL_ALL_PASSIVE_ENAB(wl)) { if (schedule_work(&wl->wl_dpc_task.work)) atomic_inc(&wl->callbacks); else ASSERT(0); } else tasklet_schedule(&wl->tasklet); } } WL_ISRUNLOCK(wl, flags); return IRQ_RETVAL(ours); } static void BCMFASTPATH wl_dpc(ulong data) { wl_info_t *wl; wl = (wl_info_t *)data; WL_LOCK(wl); if (wl->pub->up) { wlc_dpc_info_t dpci = {0}; if (wl->resched) { unsigned long flags = 0; INT_LOCK(wl, flags); wlc_intrsupd(wl->wlc); INT_UNLOCK(wl, flags); } wl->resched = wlc_dpc(wl->wlc, TRUE, &dpci); wl->processed = dpci.processed; } if (!wl->pub->up) { if ((WL_ALL_PASSIVE_ENAB(wl))) { atomic_dec(&wl->callbacks); } goto done; } if (wl->resched) { if (!(WL_ALL_PASSIVE_ENAB(wl))) tasklet_schedule(&wl->tasklet); else if (!schedule_work(&wl->wl_dpc_task.work)) { ASSERT(0); } } else { if (WL_ALL_PASSIVE_ENAB(wl)) atomic_dec(&wl->callbacks); wl_intrson(wl); } done: WL_UNLOCK(wl); return; } static void BCMFASTPATH wl_dpc_rxwork(struct wl_task *task) { wl_info_t *wl = (wl_info_t *)task->context; WL_TRACE(("wl%d: %s\n", wl->pub->unit, __FUNCTION__)); wl_dpc((unsigned long)wl); return; } void BCMFASTPATH wl_sendup(wl_info_t *wl, wl_if_t *wlif, void *p, int numpkt) { struct sk_buff *skb; bool brcm_specialpkt; WL_TRACE(("wl%d: wl_sendup: %d bytes\n", wl->pub->unit, PKTLEN(wl->osh, p))); brcm_specialpkt = (ntoh16_ua(PKTDATA(wl->pub->osh, p) + ETHER_TYPE_OFFSET) == ETHER_TYPE_BRCM); if (!brcm_specialpkt) { } if (wlif) { if (!wlif->dev || !netif_device_present(wlif->dev)) { WL_ERROR(("wl%d: wl_sendup: interface not ready\n", wl->pub->unit)); PKTFREE(wl->osh, p, FALSE); return; } skb = PKTTONATIVE(wl->osh, p); skb->dev = wlif->dev; } else { skb = PKTTONATIVE(wl->osh, p); skb->dev = wl->dev; } skb->protocol = eth_type_trans(skb, skb->dev); if (!brcm_specialpkt && !ISALIGNED(skb->data, 4)) { WL_ERROR(("Unaligned assert. skb %p. skb->data %p.\n", skb, skb->data)); if (wlif) { WL_ERROR(("wl_sendup: dev name is %s (wlif) \n", wlif->dev->name)); WL_ERROR(("wl_sendup: hard header len %d (wlif) \n", wlif->dev->hard_header_len)); } WL_ERROR(("wl_sendup: dev name is %s (wl) \n", wl->dev->name)); WL_ERROR(("wl_sendup: hard header len %d (wl) \n", wl->dev->hard_header_len)); ASSERT(ISALIGNED(skb->data, 4)); } WL_APSTA_RX(("wl%d: wl_sendup(): pkt %p summed %d on interface %p (%s)\n", wl->pub->unit, p, skb->ip_summed, wlif, skb->dev->name)); netif_rx(skb); } int wl_osl_pcie_rc(struct wl_info *wl, uint op, int param) { return 0; } void wl_dump_ver(wl_info_t *wl, struct bcmstrbuf *b) { bcm_bprintf(b, "wl%d: %s %s version %s\n", wl->pub->unit, __DATE__, __TIME__, EPI_VERSION_STR); } #if defined(BCMDBG) static int wl_dump(wl_info_t *wl, struct bcmstrbuf *b) { wl_if_t *p; int i; wl_dump_ver(wl, b); bcm_bprintf(b, "name %s dev %p tbusy %d callbacks %d malloced %d\n", wl->dev->name, wl->dev, (uint)netif_queue_stopped(wl->dev), atomic_read(&wl->callbacks), MALLOCED(wl->osh)); p = wl->if_list; if (p) p = p->next; for (i = 0; p != NULL; p = p->next, i++) { if ((i % 4) == 0) { if (i != 0) bcm_bprintf(b, "\n"); bcm_bprintf(b, "Interfaces:"); } bcm_bprintf(b, " name %s dev %p", p->dev->name, p->dev); } if (i) bcm_bprintf(b, "\n"); return 0; } #endif static void wl_link_up(wl_info_t *wl, char *ifname) { WL_ERROR(("wl%d: link up (%s)\n", wl->pub->unit, ifname)); } static void wl_link_down(wl_info_t *wl, char *ifname) { WL_ERROR(("wl%d: link down (%s)\n", wl->pub->unit, ifname)); } void wl_event(wl_info_t *wl, char *ifname, wlc_event_t *e) { #ifdef USE_IW wl_iw_event(wl->dev, &(e->event), e->data); #endif #if defined(USE_CFG80211) wl_cfg80211_event(wl->dev, &(e->event), e->data); #endif switch (e->event.event_type) { case WLC_E_LINK: case WLC_E_NDIS_LINK: if (e->event.flags&WLC_EVENT_MSG_LINK) wl_link_up(wl, ifname); else wl_link_down(wl, ifname); break; #if defined(WL_CONFIG_RFKILL) case WLC_E_RADIO: { mbool i; if (wlc_get(wl->wlc, WLC_GET_RADIO, &i) < 0) WL_ERROR(("%s: WLC_GET_RADIO failed\n", __FUNCTION__)); if (wl->last_phyind == (mbool)(i & WL_RADIO_HW_DISABLE)) break; wl->last_phyind = (mbool)(i & WL_RADIO_HW_DISABLE); WL_ERROR(("wl%d: Radio hardware state changed to %d\n", wl->pub->unit, i)); wl_report_radio_state(wl); break; } #else case WLC_E_RADIO: break; #endif } } void wl_event_sync(wl_info_t *wl, char *ifname, wlc_event_t *e) { } static void BCMFASTPATH wl_sched_tx_tasklet(void *t) { wl_info_t *wl = (wl_info_t *)t; tasklet_schedule(&wl->tx_tasklet); } #define WL_CONFIG_SMP() FALSE static int BCMFASTPATH wl_start(struct sk_buff *skb, struct net_device *dev) { wl_if_t *wlif; wl_info_t *wl; if (!dev) return -ENETDOWN; wlif = WL_DEV_IF(dev); wl = WL_INFO(dev); if (WL_ALL_PASSIVE_ENAB(wl) || (WL_RTR() && WL_CONFIG_SMP())) { skb->prev = NULL; TXQ_LOCK(wl); if ((wl_txq_thresh > 0) && (wl->txq_cnt >= wl_txq_thresh)) { PKTFRMNATIVE(wl->osh, skb); PKTCFREE(wl->osh, skb, TRUE); TXQ_UNLOCK(wl); return 0; } if (wl->txq_head == NULL) wl->txq_head = skb; else wl->txq_tail->prev = skb; wl->txq_tail = skb; wl->txq_cnt++; if (!wl->txq_dispatched) { int32 err = 0; if (!WL_ALL_PASSIVE_ENAB(wl)) wl_sched_tx_tasklet(wl); else err = (int32)(schedule_work(&wl->txq_task.work) == 0); if (!err) { atomic_inc(&wl->callbacks); wl->txq_dispatched = TRUE; } else WL_ERROR(("wl%d: wl_start/schedule_work failed\n", wl->pub->unit)); } TXQ_UNLOCK(wl); } else return wl_start_int(wl, wlif, skb); return (0); } static void BCMFASTPATH wl_start_txqwork(wl_task_t *task) { wl_info_t *wl = (wl_info_t *)task->context; struct sk_buff *skb; WL_TRACE(("wl%d: %s txq_cnt %d\n", wl->pub->unit, __FUNCTION__, wl->txq_cnt)); #ifdef BCMDBG if (wl->txq_cnt >= 500) WL_ERROR(("wl%d: WARNING dispatching over 500 packets in txqwork(%d)\n", wl->pub->unit, wl->txq_cnt)); #endif TXQ_LOCK(wl); while (wl->txq_head) { skb = wl->txq_head; wl->txq_head = skb->prev; skb->prev = NULL; if (wl->txq_head == NULL) wl->txq_tail = NULL; wl->txq_cnt--; TXQ_UNLOCK(wl); wl_start_int(wl, WL_DEV_IF(skb->dev), skb); TXQ_LOCK(wl); } wl->txq_dispatched = FALSE; atomic_dec(&wl->callbacks); TXQ_UNLOCK(wl); return; } static void BCMFASTPATH wl_tx_tasklet(ulong data) { wl_task_t task; task.context = (void *)data; wl_start_txqwork(&task); } static void wl_txq_free(wl_info_t *wl) { struct sk_buff *skb; if (wl->txq_head == NULL) { ASSERT(wl->txq_tail == NULL); return; } while (wl->txq_head) { skb = wl->txq_head; wl->txq_head = skb->prev; wl->txq_cnt--; PKTFRMNATIVE(wl->osh, skb); PKTCFREE(wl->osh, skb, TRUE); } wl->txq_tail = NULL; } static void wl_set_multicast_list_workitem(struct work_struct *work) { wl_task_t *task = (wl_task_t *)work; struct net_device *dev = (struct net_device*)task->context; wl_info_t *wl; wl = WL_INFO(dev); atomic_dec(&wl->callbacks); _wl_set_multicast_list(dev); } static void wl_timer_task(wl_task_t *task) { wl_timer_t *t = (wl_timer_t *)task->context; _wl_timer(t); MFREE(t->wl->osh, task, sizeof(wl_task_t)); atomic_dec(&t->wl->callbacks); } static void wl_timer(ulong data) { wl_timer_t *t = (wl_timer_t *)data; if (!WL_ALL_PASSIVE_ENAB(t->wl)) _wl_timer(t); else wl_schedule_task(t->wl, wl_timer_task, t); } static void _wl_timer(wl_timer_t *t) { wl_info_t *wl = t->wl; WL_LOCK(wl); if (t->set && (!timer_pending(&t->timer))) { if (t->periodic) { t->timer.expires = jiffies + t->ms*HZ/1000; atomic_inc(&wl->callbacks); add_timer(&t->timer); t->set = TRUE; } else t->set = FALSE; t->fn(t->arg); #ifdef BCMDBG wlc_update_perf_stats(wl->wlc, WLC_PERF_STATS_TMR_DPC); t->ticks++; #endif } atomic_dec(&wl->callbacks); WL_UNLOCK(wl); } wl_timer_t * wl_init_timer(wl_info_t *wl, void (*fn)(void *arg), void *arg, const char *tname) { wl_timer_t *t; t = (wl_timer_t*)MALLOC(wl->osh, sizeof(wl_timer_t)); if (t == NULL) { WL_ERROR(("wl%d: wl_init_timer: out of memory, malloced %d bytes\n", wl->unit, MALLOCED(wl->osh))); return 0; } bzero(t, sizeof(wl_timer_t)); init_timer(&t->timer); t->timer.data = (ulong) t; t->timer.function = wl_timer; t->wl = wl; t->fn = fn; t->arg = arg; t->next = wl->timers; wl->timers = t; #ifdef BCMDBG if ((t->name = MALLOC(wl->osh, strlen(tname) + 1))) strcpy(t->name, tname); #endif return t; } void wl_add_timer(wl_info_t *wl, wl_timer_t *t, uint ms, int periodic) { #ifdef BCMDBG if (t->set) { WL_ERROR(("%s: Already set. Name: %s, per %d\n", __FUNCTION__, t->name, periodic)); } #endif t->ms = ms; t->periodic = (bool) periodic; if (t->set) return; t->set = TRUE; t->timer.expires = jiffies + ms*HZ/1000; atomic_inc(&wl->callbacks); add_timer(&t->timer); } bool wl_del_timer(wl_info_t *wl, wl_timer_t *t) { ASSERT(t); if (t->set) { t->set = FALSE; if (!del_timer(&t->timer)) { #ifdef BCMDBG WL_INFORM(("wl%d: Failed to delete timer %s\n", wl->unit, t->name)); #endif return TRUE; } atomic_dec(&wl->callbacks); } return TRUE; } void wl_free_timer(wl_info_t *wl, wl_timer_t *t) { wl_timer_t *tmp; wl_del_timer(wl, t); if (wl->timers == t) { wl->timers = wl->timers->next; #ifdef BCMDBG if (t->name) MFREE(wl->osh, t->name, strlen(t->name) + 1); #endif MFREE(wl->osh, t, sizeof(wl_timer_t)); return; } tmp = wl->timers; while (tmp) { if (tmp->next == t) { tmp->next = t->next; #ifdef BCMDBG if (t->name) MFREE(wl->osh, t->name, strlen(t->name) + 1); #endif MFREE(wl->osh, t, sizeof(wl_timer_t)); return; } tmp = tmp->next; } } void wl_monitor(wl_info_t *wl, wl_rxsts_t *rxsts, void *p) { struct sk_buff *oskb = (struct sk_buff *)p; struct sk_buff *skb; uchar *pdata; uint len; len = 0; skb = NULL; WL_TRACE(("wl%d: wl_monitor\n", wl->pub->unit)); if (!wl->monitor_dev) return; if (wl->monitor_type == 1) { p80211msg_t *phdr; len = sizeof(p80211msg_t) + oskb->len - D11_PHY_HDR_LEN; if ((skb = dev_alloc_skb(len)) == NULL) { WL_ERROR(("%s: dev_alloc_skb() failure, mon type 1", __FUNCTION__)); return; } skb_put(skb, len); phdr = (p80211msg_t*)skb->data; phdr->msgcode = WL_MON_FRAME; phdr->msglen = sizeof(p80211msg_t); strcpy(phdr->devname, wl->dev->name); phdr->hosttime.did = WL_MON_FRAME_HOSTTIME; phdr->hosttime.status = P80211ITEM_OK; phdr->hosttime.len = 4; phdr->hosttime.data = jiffies; phdr->channel.did = WL_MON_FRAME_CHANNEL; phdr->channel.status = P80211ITEM_NO_VALUE; phdr->channel.len = 4; phdr->channel.data = 0; phdr->signal.did = WL_MON_FRAME_SIGNAL; phdr->signal.status = P80211ITEM_OK; phdr->signal.len = 4; phdr->signal.data = rxsts->preamble; phdr->noise.did = WL_MON_FRAME_NOISE; phdr->noise.status = P80211ITEM_NO_VALUE; phdr->noise.len = 4; phdr->noise.data = 0; phdr->rate.did = WL_MON_FRAME_RATE; phdr->rate.status = P80211ITEM_OK; phdr->rate.len = 4; phdr->rate.data = rxsts->datarate; phdr->istx.did = WL_MON_FRAME_ISTX; phdr->istx.status = P80211ITEM_NO_VALUE; phdr->istx.len = 4; phdr->istx.data = 0; phdr->mactime.did = WL_MON_FRAME_MACTIME; phdr->mactime.status = P80211ITEM_OK; phdr->mactime.len = 4; phdr->mactime.data = rxsts->mactime; phdr->rssi.did = WL_MON_FRAME_RSSI; phdr->rssi.status = P80211ITEM_OK; phdr->rssi.len = 4; phdr->rssi.data = rxsts->signal; phdr->sq.did = WL_MON_FRAME_SQ; phdr->sq.status = P80211ITEM_OK; phdr->sq.len = 4; phdr->sq.data = rxsts->sq; phdr->frmlen.did = WL_MON_FRAME_FRMLEN; phdr->frmlen.status = P80211ITEM_OK; phdr->frmlen.status = P80211ITEM_OK; phdr->frmlen.len = 4; phdr->frmlen.data = rxsts->pktlength; pdata = skb->data + sizeof(p80211msg_t); bcopy(oskb->data + D11_PHY_HDR_LEN, pdata, oskb->len - D11_PHY_HDR_LEN); } else if (wl->monitor_type == 2) { int channel_frequency; uint16 channel_flags; uint8 flags; uint16 rtap_len; struct dot11_header *mac_header; uint16 fc; if (rxsts->phytype != WL_RXS_PHY_N) rtap_len = sizeof(wl_radiotap_legacy_t); else rtap_len = sizeof(wl_radiotap_ht_brcm_2_t); len = rtap_len + (oskb->len - D11_PHY_HDR_LEN); if ((skb = dev_alloc_skb(len)) == NULL) { WL_ERROR(("%s: dev_alloc_skb() failure, mon type 2", __FUNCTION__)); return; } skb_put(skb, len); if (CHSPEC_IS2G(rxsts->chanspec)) { channel_flags = IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_DYN; channel_frequency = wf_channel2mhz(wf_chspec_ctlchan(rxsts->chanspec), WF_CHAN_FACTOR_2_4_G); } else { channel_flags = IEEE80211_CHAN_5GHZ | IEEE80211_CHAN_OFDM; channel_frequency = wf_channel2mhz(wf_chspec_ctlchan(rxsts->chanspec), WF_CHAN_FACTOR_5_G); } mac_header = (struct dot11_header *)(oskb->data + D11_PHY_HDR_LEN); fc = ltoh16(mac_header->fc); flags = IEEE80211_RADIOTAP_F_FCS; if (rxsts->preamble == WL_RXS_PREAMBLE_SHORT) flags |= IEEE80211_RADIOTAP_F_SHORTPRE; if (fc & FC_WEP) flags |= IEEE80211_RADIOTAP_F_WEP; if (fc & FC_MOREFRAG) flags |= IEEE80211_RADIOTAP_F_FRAG; if (rxsts->pkterror & WL_RXS_CRC_ERROR) flags |= IEEE80211_RADIOTAP_F_BADFCS; if (rxsts->phytype != WL_RXS_PHY_N) { wl_radiotap_legacy_t *rtl = (wl_radiotap_legacy_t *)skb->data; rtl->ieee_radiotap.it_version = 0; rtl->ieee_radiotap.it_pad = 0; rtl->ieee_radiotap.it_len = HTOL16(rtap_len); rtl->ieee_radiotap.it_present = HTOL32(WL_RADIOTAP_PRESENT_LEGACY); rtl->tsft_l = htol32(rxsts->mactime); rtl->tsft_h = 0; rtl->flags = flags; rtl->rate = rxsts->datarate; rtl->channel_freq = HTOL16(channel_frequency); rtl->channel_flags = HTOL16(channel_flags); rtl->signal = (int8)rxsts->signal; rtl->noise = (int8)rxsts->noise; rtl->antenna = rxsts->antenna; memcpy(rtl->vend_oui, brcm_oui, sizeof(brcm_oui)); rtl->vend_skip_len = WL_RADIOTAP_LEGACY_SKIP_LEN; rtl->vend_sns = 0; memset(&rtl->nonht_vht, 0, sizeof(rtl->nonht_vht)); rtl->nonht_vht.len = WL_RADIOTAP_NONHT_VHT_LEN; } else { wl_radiotap_ht_brcm_2_t *rtht = (wl_radiotap_ht_brcm_2_t *)skb->data; rtht->ieee_radiotap.it_version = 0; rtht->ieee_radiotap.it_pad = 0; rtht->ieee_radiotap.it_len = HTOL16(rtap_len); rtht->ieee_radiotap.it_present = HTOL32(WL_RADIOTAP_PRESENT_HT_BRCM2); rtht->it_present_ext = HTOL32(WL_RADIOTAP_BRCM2_HT_MCS); rtht->pad1 = 0; rtht->tsft_l = htol32(rxsts->mactime); rtht->tsft_h = 0; rtht->flags = flags; rtht->pad2 = 0; rtht->channel_freq = HTOL16(channel_frequency); rtht->channel_flags = HTOL16(channel_flags); rtht->signal = (int8)rxsts->signal; rtht->noise = (int8)rxsts->noise; rtht->antenna = rxsts->antenna; rtht->pad3 = 0; memcpy(rtht->vend_oui, brcm_oui, sizeof(brcm_oui)); rtht->vend_sns = WL_RADIOTAP_BRCM2_HT_SNS; rtht->vend_skip_len = WL_RADIOTAP_HT_BRCM2_SKIP_LEN; rtht->mcs = rxsts->mcs; rtht->htflags = 0; if (rxsts->htflags & WL_RXS_HTF_40) rtht->htflags |= IEEE80211_RADIOTAP_HTMOD_40; if (rxsts->htflags & WL_RXS_HTF_SGI) rtht->htflags |= IEEE80211_RADIOTAP_HTMOD_SGI; if (rxsts->preamble & WL_RXS_PREAMBLE_HT_GF) rtht->htflags |= IEEE80211_RADIOTAP_HTMOD_GF; if (rxsts->htflags & WL_RXS_HTF_LDPC) rtht->htflags |= IEEE80211_RADIOTAP_HTMOD_LDPC; rtht->htflags |= (rxsts->htflags & WL_RXS_HTF_STBC_MASK) << IEEE80211_RADIOTAP_HTMOD_STBC_SHIFT; } pdata = skb->data + rtap_len; bcopy(oskb->data + D11_PHY_HDR_LEN, pdata, oskb->len - D11_PHY_HDR_LEN); } else if (wl->monitor_type == 3) { int channel_frequency; uint16 channel_flags; uint8 flags; uint16 rtap_len; struct dot11_header * mac_header; uint16 fc; if (rxsts->phytype == WL_RXS_PHY_N) { if (rxsts->encoding == WL_RXS_ENCODING_HT) rtap_len = sizeof(wl_radiotap_ht_t); else if (rxsts->encoding == WL_RXS_ENCODING_VHT) rtap_len = sizeof(wl_radiotap_vht_t); else rtap_len = sizeof(wl_radiotap_legacy_t); } else { rtap_len = sizeof(wl_radiotap_legacy_t); } len = rtap_len + (oskb->len - D11_PHY_HDR_LEN); if (oskb->next) { struct sk_buff *amsdu_p = oskb->next; uint amsdu_len = 0; while (amsdu_p) { amsdu_len += amsdu_p->len; amsdu_p = amsdu_p->next; } len += amsdu_len; } if ((skb = dev_alloc_skb(len)) == NULL) { WL_ERROR(("%s: dev_alloc_skb() failure, mon type 3", __FUNCTION__)); return; } skb_put(skb, len); if (CHSPEC_IS2G(rxsts->chanspec)) { channel_flags = IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_DYN; channel_frequency = wf_channel2mhz(wf_chspec_ctlchan(rxsts->chanspec), WF_CHAN_FACTOR_2_4_G); } else { channel_flags = IEEE80211_CHAN_5GHZ | IEEE80211_CHAN_OFDM; channel_frequency = wf_channel2mhz(wf_chspec_ctlchan(rxsts->chanspec), WF_CHAN_FACTOR_5_G); } mac_header = (struct dot11_header *)(oskb->data + D11_PHY_HDR_LEN); fc = ltoh16(mac_header->fc); flags = IEEE80211_RADIOTAP_F_FCS; if (rxsts->preamble == WL_RXS_PREAMBLE_SHORT) flags |= IEEE80211_RADIOTAP_F_SHORTPRE; if (fc & FC_WEP) flags |= IEEE80211_RADIOTAP_F_WEP; if (fc & FC_MOREFRAG) flags |= IEEE80211_RADIOTAP_F_FRAG; if (rxsts->pkterror & WL_RXS_CRC_ERROR) flags |= IEEE80211_RADIOTAP_F_BADFCS; if ((rxsts->phytype != WL_RXS_PHY_N) || ((rxsts->encoding != WL_RXS_ENCODING_HT) && (rxsts->encoding != WL_RXS_ENCODING_VHT))) { wl_radiotap_legacy_t *rtl = (wl_radiotap_legacy_t *)skb->data; rtl->ieee_radiotap.it_version = 0; rtl->ieee_radiotap.it_pad = 0; rtl->ieee_radiotap.it_len = HTOL16(rtap_len); rtl->ieee_radiotap.it_present = HTOL32(WL_RADIOTAP_PRESENT_LEGACY); rtl->it_present_ext = HTOL32(WL_RADIOTAP_LEGACY_VHT); rtl->tsft_l = htol32(rxsts->mactime); rtl->tsft_h = 0; rtl->flags = flags; rtl->rate = rxsts->datarate; rtl->channel_freq = HTOL16(channel_frequency); rtl->channel_flags = HTOL16(channel_flags); rtl->signal = (int8)rxsts->signal; rtl->noise = (int8)rxsts->noise; rtl->antenna = rxsts->antenna; memcpy(rtl->vend_oui, brcm_oui, sizeof(brcm_oui)); rtl->vend_skip_len = WL_RADIOTAP_LEGACY_SKIP_LEN; rtl->vend_sns = 0; memset(&rtl->nonht_vht, 0, sizeof(rtl->nonht_vht)); rtl->nonht_vht.len = WL_RADIOTAP_NONHT_VHT_LEN; if (((fc & FC_KIND_MASK) == FC_RTS) || ((fc & FC_KIND_MASK) == FC_CTS)) { rtl->nonht_vht.flags |= WL_RADIOTAP_F_NONHT_VHT_BW; rtl->nonht_vht.bw = rxsts->bw_nonht; rtl->vend_sns = WL_RADIOTAP_LEGACY_SNS; } if ((fc & FC_KIND_MASK) == FC_RTS) { if (rxsts->vhtflags & WL_RXS_VHTF_DYN_BW_NONHT) rtl->nonht_vht.flags |= WL_RADIOTAP_F_NONHT_VHT_DYN_BW; } } else if (rxsts->encoding == WL_RXS_ENCODING_VHT) { wl_radiotap_vht_t *rtvht = (wl_radiotap_vht_t *)skb->data; rtvht->ieee_radiotap.it_version = 0; rtvht->ieee_radiotap.it_pad = 0; rtvht->ieee_radiotap.it_len = HTOL16(rtap_len); rtvht->ieee_radiotap.it_present = HTOL32(WL_RADIOTAP_PRESENT_VHT); rtvht->tsft_l = htol32(rxsts->mactime); rtvht->tsft_h = 0; rtvht->flags = flags; rtvht->pad1 = 0; rtvht->channel_freq = HTOL16(channel_frequency); rtvht->channel_flags = HTOL16(channel_flags); rtvht->signal = (int8)rxsts->signal; rtvht->noise = (int8)rxsts->noise; rtvht->antenna = rxsts->antenna; rtvht->vht_known = (IEEE80211_RADIOTAP_VHT_HAVE_STBC | IEEE80211_RADIOTAP_VHT_HAVE_TXOP_PS | IEEE80211_RADIOTAP_VHT_HAVE_GI | IEEE80211_RADIOTAP_VHT_HAVE_SGI_NSYM_DA | IEEE80211_RADIOTAP_VHT_HAVE_LDPC_EXTRA | IEEE80211_RADIOTAP_VHT_HAVE_BF | IEEE80211_RADIOTAP_VHT_HAVE_BW | IEEE80211_RADIOTAP_VHT_HAVE_GID | IEEE80211_RADIOTAP_VHT_HAVE_PAID); STATIC_ASSERT(WL_RXS_VHTF_STBC == IEEE80211_RADIOTAP_VHT_STBC); STATIC_ASSERT(WL_RXS_VHTF_TXOP_PS == IEEE80211_RADIOTAP_VHT_TXOP_PS); STATIC_ASSERT(WL_RXS_VHTF_SGI == IEEE80211_RADIOTAP_VHT_SGI); STATIC_ASSERT(WL_RXS_VHTF_SGI_NSYM_DA == IEEE80211_RADIOTAP_VHT_SGI_NSYM_DA); STATIC_ASSERT(WL_RXS_VHTF_LDPC_EXTRA == IEEE80211_RADIOTAP_VHT_LDPC_EXTRA); STATIC_ASSERT(WL_RXS_VHTF_BF == IEEE80211_RADIOTAP_VHT_BF); rtvht->vht_flags = HTOL16(rxsts->vhtflags); STATIC_ASSERT(WL_RXS_VHT_BW_20 == IEEE80211_RADIOTAP_VHT_BW_20); STATIC_ASSERT(WL_RXS_VHT_BW_40 == IEEE80211_RADIOTAP_VHT_BW_40); STATIC_ASSERT(WL_RXS_VHT_BW_20L == IEEE80211_RADIOTAP_VHT_BW_20L); STATIC_ASSERT(WL_RXS_VHT_BW_20U == IEEE80211_RADIOTAP_VHT_BW_20U); STATIC_ASSERT(WL_RXS_VHT_BW_80 == IEEE80211_RADIOTAP_VHT_BW_80); STATIC_ASSERT(WL_RXS_VHT_BW_40L == IEEE80211_RADIOTAP_VHT_BW_40L); STATIC_ASSERT(WL_RXS_VHT_BW_40U == IEEE80211_RADIOTAP_VHT_BW_40U); STATIC_ASSERT(WL_RXS_VHT_BW_20LL == IEEE80211_RADIOTAP_VHT_BW_20LL); STATIC_ASSERT(WL_RXS_VHT_BW_20LU == IEEE80211_RADIOTAP_VHT_BW_20LU); STATIC_ASSERT(WL_RXS_VHT_BW_20UL == IEEE80211_RADIOTAP_VHT_BW_20UL); STATIC_ASSERT(WL_RXS_VHT_BW_20UU == IEEE80211_RADIOTAP_VHT_BW_20UU); rtvht->vht_bw = rxsts->bw; rtvht->vht_mcs_nss[0] = (rxsts->mcs << 4) | (rxsts->nss & IEEE80211_RADIOTAP_VHT_NSS); rtvht->vht_mcs_nss[1] = 0; rtvht->vht_mcs_nss[2] = 0; rtvht->vht_mcs_nss[3] = 0; STATIC_ASSERT(WL_RXS_VHTF_CODING_LDCP == IEEE80211_RADIOTAP_VHT_CODING_LDPC); rtvht->vht_coding = rxsts->coding; rtvht->vht_group_id = rxsts->gid; rtvht->vht_partial_aid = HTOL16(rxsts->aid); rtvht->ampdu_flags = 0; rtvht->ampdu_delim_crc = 0; rtvht->ampdu_ref_num = rxsts->ampdu_counter; if (!(rxsts->nfrmtype & WL_RXS_NFRM_AMPDU_FIRST) && !(rxsts->nfrmtype & WL_RXS_NFRM_AMPDU_SUB)) rtvht->ampdu_flags |= IEEE80211_RADIOTAP_AMPDU_IS_LAST; if (rxsts->nfrmtype & WL_RXS_NFRM_AMPDU_NONE) rtvht->ampdu_flags |= IEEE80211_RADIOTAP_AMPDU_MPDU_ONLY; } else if (rxsts->encoding == WL_RXS_ENCODING_HT) { wl_radiotap_ht_t *rtht = (wl_radiotap_ht_t *)skb->data; rtht->ieee_radiotap.it_version = 0; rtht->ieee_radiotap.it_pad = 0; rtht->ieee_radiotap.it_len = HTOL16(rtap_len); rtht->ieee_radiotap.it_present = HTOL32(WL_RADIOTAP_PRESENT_HT); rtht->pad1 = 0; rtht->tsft_l = htol32(rxsts->mactime); rtht->tsft_h = 0; rtht->flags = flags; rtht->channel_freq = HTOL16(channel_frequency); rtht->channel_flags = HTOL16(channel_flags); rtht->signal = (int8)rxsts->signal; rtht->noise = (int8)rxsts->noise; rtht->antenna = rxsts->antenna; rtht->mcs_known = (IEEE80211_RADIOTAP_MCS_HAVE_BW | IEEE80211_RADIOTAP_MCS_HAVE_MCS | IEEE80211_RADIOTAP_MCS_HAVE_GI | IEEE80211_RADIOTAP_MCS_HAVE_FEC | IEEE80211_RADIOTAP_MCS_HAVE_FMT); rtht->mcs_flags = 0; switch (rxsts->htflags & WL_RXS_HTF_BW_MASK) { case WL_RXS_HTF_20L: rtht->mcs_flags |= IEEE80211_RADIOTAP_MCS_BW_20L; break; case WL_RXS_HTF_20U: rtht->mcs_flags |= IEEE80211_RADIOTAP_MCS_BW_20U; break; case WL_RXS_HTF_40: rtht->mcs_flags |= IEEE80211_RADIOTAP_MCS_BW_40; break; default: rtht->mcs_flags |= IEEE80211_RADIOTAP_MCS_BW_20; } if (rxsts->htflags & WL_RXS_HTF_SGI) { rtht->mcs_flags |= IEEE80211_RADIOTAP_MCS_SGI; } if (rxsts->preamble & WL_RXS_PREAMBLE_HT_GF) { rtht->mcs_flags |= IEEE80211_RADIOTAP_MCS_FMT_GF; } if (rxsts->htflags & WL_RXS_HTF_LDPC) { rtht->mcs_flags |= IEEE80211_RADIOTAP_MCS_FEC_LDPC; } rtht->mcs_index = rxsts->mcs; } pdata = skb->data + rtap_len; bcopy(oskb->data + D11_PHY_HDR_LEN, pdata, oskb->len - D11_PHY_HDR_LEN); if (oskb->next) { struct sk_buff *amsdu_p = oskb->next; amsdu_p = oskb->next; pdata += (oskb->len - D11_PHY_HDR_LEN); while (amsdu_p) { bcopy(amsdu_p->data, pdata, amsdu_p->len); pdata += amsdu_p->len; amsdu_p = amsdu_p->next; } } } if (skb == NULL) return; skb->dev = wl->monitor_dev; skb->dev->last_rx = jiffies; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22) skb_reset_mac_header(skb); #else skb->mac.raw = skb->data; #endif skb->ip_summed = CHECKSUM_NONE; skb->pkt_type = PACKET_OTHERHOST; skb->protocol = htons(ETH_P_80211_RAW); netif_rx(skb); } static int wl_monitor_start(struct sk_buff *skb, struct net_device *dev) { wl_info_t *wl; wl = WL_DEV_IF(dev)->wl; PKTFREE(wl->osh, skb, FALSE); return 0; } static void _wl_add_monitor_if(wl_task_t *task) { struct net_device *dev; wl_if_t *wlif = (wl_if_t *) task->context; wl_info_t *wl = wlif->wl; WL_TRACE(("wl%d: %s\n", wl->pub->unit, __FUNCTION__)); ASSERT(wl); ASSERT(!wl->monitor_dev); if ((dev = wl_alloc_linux_if(wlif)) == NULL) { WL_ERROR(("wl%d: %s: wl_alloc_linux_if failed\n", wl->pub->unit, __FUNCTION__)); goto done; } ASSERT(strlen(wlif->name) > 0); strncpy(wlif->dev->name, wlif->name, strlen(wlif->name)); wl->monitor_dev = dev; if (wl->monitor_type == 1) dev->type = ARPHRD_IEEE80211_PRISM; else dev->type = ARPHRD_IEEE80211_RADIOTAP; bcopy(wl->dev->dev_addr, dev->dev_addr, ETHER_ADDR_LEN); #if defined(WL_USE_NETDEV_OPS) dev->netdev_ops = &wl_netdev_monitor_ops; #else dev->hard_start_xmit = wl_monitor_start; dev->do_ioctl = wl_ioctl; dev->get_stats = wl_get_stats; #endif if (register_netdev(dev)) { WL_ERROR(("wl%d: %s, register_netdev failed for %s\n", wl->pub->unit, __FUNCTION__, wl->monitor_dev->name)); wl->monitor_dev = NULL; goto done; } wlif->dev_registed = TRUE; done: MFREE(wl->osh, task, sizeof(wl_task_t)); atomic_dec(&wl->callbacks); } static void _wl_del_monitor(wl_task_t *task) { wl_info_t *wl = (wl_info_t *) task->context; ASSERT(wl); ASSERT(wl->monitor_dev); WL_TRACE(("wl%d: _wl_del_monitor\n", wl->pub->unit)); wl_free_if(wl, WL_DEV_IF(wl->monitor_dev)); wl->monitor_dev = NULL; MFREE(wl->osh, task, sizeof(wl_task_t)); atomic_dec(&wl->callbacks); } void wl_set_monitor(wl_info_t *wl, int val) { const char *devname; wl_if_t *wlif; WL_TRACE(("wl%d: wl_set_monitor: val %d\n", wl->pub->unit, val)); if ((val && wl->monitor_dev) || (!val && !wl->monitor_dev)) { WL_ERROR(("%s: Mismatched params, return\n", __FUNCTION__)); return; } if (!val) { (void) wl_schedule_task(wl, _wl_del_monitor, wl); return; } if (val >= 1 && val <= 3) { wl->monitor_type = val; } else { WL_ERROR(("monitor type %d not supported\n", val)); ASSERT(0); } wlif = wl_alloc_if(wl, WL_IFTYPE_MON, wl->pub->unit, NULL); if (!wlif) { WL_ERROR(("wl%d: %s: alloc wlif failed\n", wl->pub->unit, __FUNCTION__)); return; } if (wl->monitor_type == 1) devname = "prism"; else devname = "radiotap"; sprintf(wlif->name, "%s%d", devname, wl->pub->unit); if (wl_schedule_task(wl, _wl_add_monitor_if, wlif)) { MFREE(wl->osh, wlif, sizeof(wl_if_t)); return; } } #if LINUX_VERSION_CODE == KERNEL_VERSION(2, 6, 15) const char * print_tainted() { return ""; } #endif struct net_device * wl_netdev_get(wl_info_t *wl) { return wl->dev; } int wl_set_pktlen(osl_t *osh, void *p, int len) { PKTSETLEN(osh, p, len); return len; } void * wl_get_pktbuffer(osl_t *osh, int len) { return (PKTGET(osh, len, FALSE)); } uint wl_buf_to_pktcopy(osl_t *osh, void *p, uchar *buf, int len, uint offset) { if (PKTLEN(osh, p) < len + offset) return 0; bcopy(buf, (char *)PKTDATA(osh, p) + offset, len); return len; } #if defined(WL_CONFIG_RFKILL) static int wl_set_radio_block(void *data, bool blocked) { wl_info_t *wl = data; uint32 radioval; WL_TRACE(("%s: kernel set blocked = %d\n", __FUNCTION__, blocked)); radioval = WL_RADIO_SW_DISABLE << 16 | blocked; WL_LOCK(wl); if (wlc_set(wl->wlc, WLC_SET_RADIO, radioval) < 0) { WL_ERROR(("%s: SET_RADIO failed\n", __FUNCTION__)); return 1; } WL_UNLOCK(wl); return 0; } static const struct rfkill_ops bcmwl_rfkill_ops = { .set_block = wl_set_radio_block }; static int wl_init_rfkill(wl_info_t *wl) { int status; snprintf(wl->wl_rfkill.rfkill_name, sizeof(wl->wl_rfkill.rfkill_name), "brcmwl-%d", wl->pub->unit); wl->wl_rfkill.rfkill = rfkill_alloc(wl->wl_rfkill.rfkill_name, &wl->dev->dev, RFKILL_TYPE_WLAN, &bcmwl_rfkill_ops, wl); if (!wl->wl_rfkill.rfkill) { WL_ERROR(("%s: RFKILL: Failed to allocate rfkill\n", __FUNCTION__)); return -ENOMEM; } if (wlc_get(wl->wlc, WLC_GET_RADIO, &status) < 0) { WL_ERROR(("%s: WLC_GET_RADIO failed\n", __FUNCTION__)); return 1; } rfkill_init_sw_state(wl->wl_rfkill.rfkill, status); if (rfkill_register(wl->wl_rfkill.rfkill)) { WL_ERROR(("%s: rfkill_register failed! \n", __FUNCTION__)); rfkill_destroy(wl->wl_rfkill.rfkill); return 2; } WL_ERROR(("%s: rfkill registered\n", __FUNCTION__)); wl->wl_rfkill.registered = TRUE; return 0; } static void wl_uninit_rfkill(wl_info_t *wl) { if (wl->wl_rfkill.registered) { rfkill_unregister(wl->wl_rfkill.rfkill); rfkill_destroy(wl->wl_rfkill.rfkill); wl->wl_rfkill.registered = FALSE; wl->wl_rfkill.rfkill = NULL; } } static void wl_report_radio_state(wl_info_t *wl) { WL_TRACE(("%s: report radio state %d\n", __FUNCTION__, wl->last_phyind)); rfkill_set_hw_state(wl->wl_rfkill.rfkill, wl->last_phyind != 0); } #endif static int wl_linux_watchdog(void *ctx) { wl_info_t *wl = (wl_info_t *) ctx; struct net_device_stats *stats = NULL; uint id; wl_if_t *wlif; wlc_if_stats_t wlcif_stats; #ifdef USE_IW struct iw_statistics *wstats = NULL; int phy_noise; #endif if (wl == NULL) return -1; if (wl->if_list) { for (wlif = wl->if_list; wlif != NULL; wlif = wlif->next) { memset(&wlcif_stats, 0, sizeof(wlc_if_stats_t)); wlc_wlcif_stats_get(wl->wlc, wlif->wlcif, &wlcif_stats); if (wl->pub->up) { ASSERT(wlif->stats_id < 2); id = 1 - wlif->stats_id; stats = &wlif->stats_watchdog[id]; if (stats) { stats->rx_packets = WLCNTVAL(wlcif_stats.rxframe); stats->tx_packets = WLCNTVAL(wlcif_stats.txframe); stats->rx_bytes = WLCNTVAL(wlcif_stats.rxbyte); stats->tx_bytes = WLCNTVAL(wlcif_stats.txbyte); stats->rx_errors = WLCNTVAL(wlcif_stats.rxerror); stats->tx_errors = WLCNTVAL(wlcif_stats.txerror); stats->collisions = 0; stats->rx_length_errors = 0; stats->rx_over_errors = WLCNTVAL(wl->pub->_cnt->rxoflo); stats->rx_crc_errors = WLCNTVAL(wl->pub->_cnt->rxcrc); stats->rx_frame_errors = 0; stats->rx_fifo_errors = WLCNTVAL(wl->pub->_cnt->rxoflo); stats->rx_missed_errors = 0; stats->tx_fifo_errors = 0; } #ifdef USE_IW wstats = &wlif->wstats_watchdog[id]; if (wstats) { #if WIRELESS_EXT > 11 wstats->discard.nwid = 0; wstats->discard.code = WLCNTVAL(wl->pub->_cnt->rxundec); wstats->discard.fragment = WLCNTVAL(wlcif_stats.rxfragerr); wstats->discard.retries = WLCNTVAL(wlcif_stats.txfail); wstats->discard.misc = WLCNTVAL(wl->pub->_cnt->rxrunt) + WLCNTVAL(wl->pub->_cnt->rxgiant); wstats->miss.beacon = 0; #endif } #endif wlif->stats_id = id; } #ifdef USE_IW if (!wlc_get(wl->wlc, WLC_GET_PHY_NOISE, &phy_noise)) wlif->phy_noise = phy_noise; #endif } } return 0; } #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 10, 0) static int wl_proc_read(char *buffer, char **start, off_t offset, int length, int *eof, void *data) { wl_info_t * wl = (wl_info_t *)data; #else static ssize_t wl_proc_read(struct file *filp, char __user *buffer, size_t length, loff_t *offp) { wl_info_t * wl = PDE_DATA(file_inode(filp)); #endif int bcmerror, len; int to_user = 0; char tmp[8]; #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 10, 0) if (offset > 0) { *eof = 1; return 0; } #else if (*offp > 0) { return 0; } #endif WL_LOCK(wl); bcmerror = wlc_ioctl(wl->wlc, WLC_GET_MONITOR, &to_user, sizeof(int), NULL); WL_UNLOCK(wl); if (bcmerror != BCME_OK) { WL_ERROR(("%s: GET_MONITOR failed with %d\n", __FUNCTION__, bcmerror)); return -EIO; } len = snprintf(tmp, ARRAY_SIZE(tmp), "%d\n", to_user); tmp[ARRAY_SIZE(tmp) - 1] = '\0'; if ((len < 0) || (len >= ARRAY_SIZE(tmp))) { WL_ERROR(("%s: tmp array not big enough %d > %zu", __FUNCTION__, len, ARRAY_SIZE(tmp))); return -ERANGE; } if (length < len) { WL_ERROR(( "%s: user buffer is too small (%d < %d)", __FUNCTION__, (int)length, len)); return -EMSGSIZE; } if (copy_to_user(buffer, tmp, len) != 0) { WL_ERROR(( "%s: unable to copy data!", __FUNCTION__)); return -EFAULT; } #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0) *offp += len; #endif return len; } #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 10, 0) static int wl_proc_write(struct file *filp, const char *buff, unsigned long length, void *data) { wl_info_t * wl = (wl_info_t *)data; #else static ssize_t wl_proc_write(struct file *filp, const char __user *buff, size_t length, loff_t *offp) { wl_info_t * wl = PDE_DATA(file_inode(filp)); #endif int from_user = 0; int bcmerror; if (length == 0 || length > 2) { WL_ERROR(("%s: Invalid data length\n", __FUNCTION__)); return -EIO; } if (copy_from_user(&from_user, buff, 1)) { WL_ERROR(("%s: copy from user failed\n", __FUNCTION__)); #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 10, 0) return -EIO; #else return -EFAULT; #endif } if (from_user >= 0x30) from_user -= 0x30; WL_LOCK(wl); bcmerror = wlc_ioctl(wl->wlc, WLC_SET_MONITOR, &from_user, sizeof(int), NULL); WL_UNLOCK(wl); if (bcmerror != BCME_OK) { WL_ERROR(("%s: SET_MONITOR failed with %d\n", __FUNCTION__, bcmerror)); return -EIO; } return length; } #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0) static const struct file_operations wl_fops = { .owner = THIS_MODULE, .read = wl_proc_read, .write = wl_proc_write, }; #endif static int wl_reg_proc_entry(wl_info_t *wl) { char tmp[32]; sprintf(tmp, "%s%d", HYBRID_PROC, wl->pub->unit); #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 10, 0) if ((wl->proc_entry = create_proc_entry(tmp, 0644, NULL)) == NULL) { WL_ERROR(("%s: create_proc_entry %s failed\n", __FUNCTION__, tmp)); #else if ((wl->proc_entry = proc_create_data(tmp, 0644, NULL, &wl_fops, wl)) == NULL) { WL_ERROR(("%s: proc_create_data %s failed\n", __FUNCTION__, tmp)); #endif ASSERT(0); return -1; } #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 10, 0) wl->proc_entry->read_proc = wl_proc_read; wl->proc_entry->write_proc = wl_proc_write; wl->proc_entry->data = wl; #endif return 0; } uint32 wl_pcie_bar1(struct wl_info *wl, uchar** addr) { *addr = wl->bar1_addr; return (wl->bar1_size); }