📄 wl_linux.c
字号:
/* * Linux-specific portion of * Broadcom 802.11abg Networking Device Driver * * Copyright 2005-2006, Broadcom Corporation * All Rights Reserved. * * THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY * KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM * SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS * FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE. * * $Id$ */#define __UNDEF_NO_VERSION__#include <wlc_cfg.h>#include <typedefs.h>#include <linux/module.h>#include <linuxver.h>#include <osl.h>#include <linux/types.h>#include <linux/errno.h>#include <linux/pci.h>#include <linux/init.h>#include <linux/kernel.h>#include <linux/proc_fs.h>#include <linux/netdevice.h>#include <linux/etherdevice.h>#include <linux/skbuff.h>#include <linux/delay.h>#include <linux/string.h>#include <linux/ethtool.h>#include <linux/completion.h>#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 14)#include <net/ieee80211.h>#endif#include <asm/system.h>#include <asm/io.h>#include <asm/irq.h>#include <asm/pgtable.h>#include <asm/uaccess.h>#include <asm/unaligned.h>#include <epivers.h>#include <bcmendian.h>#include <proto/ethernet.h>#include <bcmdevs.h>#include <bcmdefs.h>#include <bcmutils.h>#include <pcicfg.h>#include <wlioctl.h>#include <wl_linux.h>#include <wlc_key.h>typedef void wlc_info_t;typedef const struct sb_pub sb_t;#include <wlc_pub.h>#include <wl_dbg.h>#include <wlc_ethereal.h>/* Linux wireless extension support */#ifdef CONFIG_NET_RADIO#include <wl_iw.h>#endif /* CONFIG_NET_RADIO */#include <wl_export.h>typedef struct wl_timer { struct timer_list timer; struct wl_info *wl; void (*fn)(void *); uint ms; bool periodic; bool set; struct wl_timer *next;} wl_timer_t;/* contortion to call functions at safe time */typedef struct wl_task { struct work_struct work; void *context;} wl_task_t;#define WL_IFTYPE_BSS 1 /* iftype subunit for BSS */typedef struct wl_if { struct wl_if *next; struct wl_info *wl; /* back pointer to main wl_info_t */ struct net_device *dev; /* virtual netdevice */ int type; /* interface type: BSS */ struct wlc_if *wlcif; /* wlc interface handle */ struct ether_addr remote; /* remote partner */ uint subunit; /* BSS unit */} wl_if_t;struct wl_info {#ifdef CONFIG_NET_RADIO wl_iw_t iw; /* wireless extensions state (must be first) */#endif /* CONFIG_NET_RADIO */ wlc_pub_t *pub; /* pointer to public wlc state */ void *wlc; /* pointer to private common os-independent data */ osl_t *osh; /* pointer to os handler */ struct net_device *dev; /* backpoint to device */ spinlock_t lock; /* per-device perimeter lock */ uint bustype; /* bus type */ bool piomode; /* set from insmod argument */ void *regsva; /* opaque chip registers virtual address */ struct net_device_stats stats; /* stat counter reporting structure */ wl_if_t *if_list; /* list of all interfaces */ struct wl_info *next; /* pointer to next wl_info_t in chain */ volatile uint callbacks; /* # outstanding callback functions */ struct wl_timer *timers; /* timer cleanup queue */ struct tasklet_struct tasklet; /* dpc tasklet */ ulong flags; /* current irq flags */ struct net_device *monitor; /* monitor pseudo device */ bool resched; /* dpc needs to be and is rescheduled */#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 14) struct ieee80211_crypto_ops *tkipmodops; /* external tkip module ops */ struct ieee80211_tkip_data *tkip_ucast_data; struct ieee80211_tkip_data *tkip_bcast_data;#endif};static int wl_found = 0;/* defines */#define DEV_WLPTR(dev) dev->priv /* priv points to wl */#define DEV_WLIFPTR(dev) dev->dn_ptr #define WL_INFO(dev) (wl_info_t*)(DEV_WLPTR(dev))#define WL_DEV_IF(dev) ((wl_if_t*)(DEV_WLIFPTR(dev)))#define WL_LOCK(wl) do { ulong flags; spin_lock_irqsave(&(wl)->lock, flags); \ (wl)->flags = flags; } while (0)#define WL_UNLOCK(wl) do { ulong flags; flags = (wl)->flags; \ spin_unlock_irqrestore(&(wl)->lock, flags); } while (0)#define WL_LOCKED(wl) spin_is_locked(&(wl)->lock)#define WL_TASKLET (LINUX_VERSION_CODE > KERNEL_VERSION(2, 4, 5))/* local prototypes */static int wl_open(struct net_device *dev);static int wl_close(struct net_device *dev);static int wl_start(struct sk_buff *skb, struct net_device *dev);static struct net_device_stats *wl_get_stats(struct net_device *dev);#ifdef CONFIG_NET_RADIOstatic struct iw_statistics *wl_get_wireless_stats(struct net_device *dev);#endif /* CONFIG_NET_RADIO */static int wl_set_mac_address(struct net_device *dev, void *addr);static void wl_set_multicast_list(struct net_device *dev);#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 4, 2)static int wl_ethtool(wl_info_t *wl, void *uaddr);#endif /* LINUX_VERSION_CODE > KERNEL_VERSION(2, 4, 2) */#if WL_TASKLETstatic void wl_dpc(ulong data);#endif /* WL_TASKLET */static void wl_link_up(wl_info_t *wl);static void wl_link_down(wl_info_t *wl);static void wl_tkipmic_error(wl_info_t *wl, struct ether_addr *ea, bool group, bool flush_txq);static int wl_schedule_task(wl_info_t *wl, void (*fn)(struct wl_task *), void *context);#if defined(CONFIG_PROC_FS) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 0))static int wl_read_proc(char *buffer, char **start, off_t offset, int length, int *eof, void *data);#endif /* defined(CONFIG_PROC_FS) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 0)) */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);#ifdef CONFIG_PCI/* recognized PCI IDs */static struct pci_device_id wl_id_table[] __devinitdata = { { vendor: PCI_ANY_ID, device: PCI_ANY_ID, subvendor: PCI_ANY_ID, subdevice: PCI_ANY_ID, class: PCI_CLASS_NETWORK_OTHER << 8, class_mask: 0xffff00, driver_data: 0, }, { 0, }};MODULE_DEVICE_TABLE(pci, wl_id_table);#endif /* CONFIG_PCI */static int oneonly = 0;module_param(oneonly, int, 0);static int piomode = 0;module_param(piomode, int, 0);static char name[IFNAMSIZ] = "eth%d";module_param_string(name, name, IFNAMSIZ, 0);/* BCMSLTGT: slow target */#ifndef SRCBASE#define SRCBASE "."#endif /* SRCBASE *//** * attach to the WL device. * * Attach to the WL device identified by vendor and device parameters. * regs is a host accessible memory address pointing to WL device registers. * * wl_attach is not defined as static because in the case where no bus * is defined, wl_attach will never be called, and thus, gcc will issue * a warning that this function is defined but not used if we declare * it as static. */wl_info_t *wl_attach(uint16 vendor, uint16 device, ulong regs, uint bustype, void *btparam, uint irq){ struct net_device *dev; wl_if_t *wlif; wl_info_t *wl;#if defined(CONFIG_PROC_FS) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 0)) char tmp[128];#endif osl_t *osh; int unit; uint err; unit = wl_found; if (oneonly && (unit != 0)) { WL_ERROR(("wl%d: wl_attach: oneonly is set, exiting\n", unit)); return NULL; } /* Requires pkttag feature */ osh = osl_attach(btparam, TRUE); ASSERT(osh); /* allocate private info */ 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; wlif = wl_alloc_if(wl, WL_IFTYPE_BSS, unit, NULL); if (!wlif) { WL_ERROR(("wl%d: wl_alloc_if failed\n", unit)); MFREE(osh, wl, sizeof(wl_info_t)); osl_detach(osh); return NULL; } dev = wlif->dev; strncpy(dev->name, name, IFNAMSIZ); wl->dev = dev; /* map chip registers (47xx: and sprom) */ dev->base_addr = regs; if (bustype == PCI_BUS) { /* piomode can be overwritten by command argument */ wl->piomode = piomode; WL_TRACE(("PCI/%s\n", wl->piomode ? "PIO" : "DMA")); } else { WL_ERROR(("wl%d: Only PCI BUS devices are supported \n", unit)); goto fail; } wl->bustype = bustype; if ((wl->regsva = ioremap_nocache(dev->base_addr, PCI_BAR0_WINSZ)) == NULL) { WL_ERROR(("wl%d: ioremap() failed\n", unit)); goto fail; } spin_lock_init(&wl->lock); /* common load-time initialization */ if (!(wl->wlc = wlc_attach((void *) wl, vendor, device, unit, wl->piomode, osh, wl->regsva, wl->bustype, btparam, &err))) { printf("%s: %s driver failed with code %d\n", dev->name, EPI_VERSION_STR, err); goto fail; } wl->pub = (wlc_pub_t *)wl->wlc;#if defined(CONFIG_PROC_FS) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 0)) /* create /proc/net/wl<unit> */ sprintf(tmp, "net/wl%d", wl->pub->unit); create_proc_read_entry(tmp, 0, 0, wl_read_proc, (void*)wl);#endif /* defined(CONFIG_PROC_FS) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 0)) */ bcopy(&wl->pub->cur_etheraddr, dev->dev_addr, ETHER_ADDR_LEN);#if WL_TASKLET /* setup the bottom half handler */ tasklet_init(&wl->tasklet, wl_dpc, (ulong)wl);#endif /* WL_TASKLET */ /* register our interrupt handler */ { if (request_irq(irq, wl_isr, SA_SHIRQ, dev->name, wl)) { WL_ERROR(("wl%d: request_irq() failed\n", unit)); goto fail; } dev->irq = irq; } /* lastly, enable our entry points */ 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;#ifdef CONFIG_NET_RADIO dev->get_wireless_stats = wl_get_wireless_stats;#if WIRELESS_EXT > 12 dev->wireless_handlers = (struct iw_handler_def *) &wl_iw_handler_def;#endif /* WIRELESS_EXT > 12 */#endif /* CONFIG_NET_RADIO */ if (register_netdev(dev)) { WL_ERROR(("wl%d: register_netdev() failed\n", unit)); goto fail; }#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 14) /* load the tkip module */ wl->tkipmodops = ieee80211_get_crypto_ops("TKIP"); if (wl->tkipmodops == NULL) { request_module("ieee80211_crypt_tkip"); wl->tkipmodops = ieee80211_get_crypto_ops("TKIP"); } WL_ERROR(("TkipModule OPs at 0x%x\n", (uint)wl->tkipmodops));#endif wl->iw.wlinfo = (void *)wl; /* print hello string */ printf("%s: Broadcom BCM%04x 802.11 Wireless Controller " EPI_VERSION_STR, dev->name, device); printf("\n"); wl_found++; return wl;fail: wl_free(wl); return NULL;}#if defined(CONFIG_PROC_FS) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 0))static intwl_read_proc(char *buffer, char **start, off_t offset, int length, int *eof, void *data){ wl_info_t *wl; int len; off_t pos; off_t begin; len = pos = begin = 0; wl = (wl_info_t*) data; len = wl_dump(wl, buffer, WLC_IOCTL_MAXLEN); pos = begin + len; if (pos < offset) { len = 0; begin = pos; } *eof = 1; *start = buffer + (offset - begin); len -= (offset - begin); if (len > length) len = length; return (len);}#endif /* defined(CONFIG_PROC_FS) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 0)) */#ifdef CONFIG_PCI/** * determines if a device is a WL device, and if so, attaches it. * * This function determines if a device pointed to by pdev is a WL device, * and if so, performs a wl_attach() on it. * */int __devinitwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent){ wl_info_t *wl; WL_TRACE(("wl_pci_probe: bus %d slot %d func %d irq %d\n", pdev->bus->number, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn), pdev->irq)); if (!wlc_chipmatch(pdev->vendor, pdev->device)) return (-ENODEV); pci_set_master(pdev); pci_enable_device(pdev); wl = wl_attach(pdev->vendor, pdev->device, pci_resource_start(pdev, 0), PCI_BUS, pdev, pdev->irq); if (!wl) return -ENODEV; pci_set_drvdata(pdev, wl); return 0;}static intwl_suspend(struct pci_dev *pdev, u32 state){ wl_info_t *wl; WL_TRACE(("wl: wl_suspend\n")); if ((wl = (wl_info_t *) pci_get_drvdata(pdev))) { netif_device_detach(wl->dev); WL_LOCK(wl); wl_down(wl); WL_UNLOCK(wl); } return 0;}static intwl_resume(struct pci_dev *pdev){ wl_info_t *wl; int error; error = 0; if ((wl = (wl_info_t *) pci_get_drvdata(pdev))) { WL_LOCK(wl); error = wl_up(wl); WL_UNLOCK(wl); netif_device_attach(wl->dev); } return (error);}/* Compatibility routines */#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 6)static void_wl_suspend(struct pci_dev *pdev){ wl_suspend(pdev, 0);}static void_wl_resume(struct pci_dev *pdev){ wl_resume(pdev);}#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 6) */static void __devexitwl_remove(struct pci_dev *pdev){ wl_info_t *wl; if (!wlc_chipmatch(pdev->vendor, pdev->device)) { return; } if ((wl = (wl_info_t *) pci_get_drvdata(pdev))) { wl_suspend(pdev, 0); wl_free(wl); pci_set_drvdata(pdev, NULL); }}static struct pci_driver wl_pci_driver = { name: "wl", probe: wl_pci_probe,#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 6) suspend: _wl_suspend, resume: _wl_resume,#else suspend: wl_suspend, resume: wl_resume,#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 6) */ remove: __devexit_p(wl_remove), id_table: wl_id_table, };#endif /* CONFIG_PCI *//** * This is the main entry point for the WL driver. * * This function determines if a device pointed to by pdev is a WL device, * and if so, performs a wl_attach() on it. * */static int __initwl_module_init(void){ int error = -ENODEV;#ifdef CONFIG_PCI if (!(error = pci_module_init(&wl_pci_driver))) return (0);#endif /* CONFIG_PCI */ return (error);}/** * This function unloads the WL driver from the system. * * This function unconditionally unloads the WL driver module from the * system. * */static void __exitwl_module_exit(void){#ifdef CONFIG_PCI pci_unregister_driver(&wl_pci_driver);#endif /* CONFIG_PCI */}module_init(wl_module_init);module_exit(wl_module_exit);/** * This function frees the WL per-device resources. * * This function frees resources owned by the WL device pointed to * by the wl parameter. * */voidwl_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 (wl->dev) { wl_free_if(wl, WL_DEV_IF(wl->dev)); wl->dev = NULL; }#if WL_TASKLET /* kill dpc */ tasklet_kill(&wl->tasklet);#endif /* WL_TASKLET */ /* free common resources */ if (wl->wlc) {#if defined(CONFIG_PROC_FS) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 0)) char tmp[128]; /* remove /proc/net/wl<unit> */ sprintf(tmp, "net/wl%d", wl->pub->unit); remove_proc_entry(tmp, 0);#endif /* defined(CONFIG_PROC_FS) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 0)) */ wlc_detach(wl->wlc); wl->wlc = NULL; wl->pub = NULL; } /* virtual interface deletion is deferred so we cannot spinwait */ /* wait for all pending callbacks to complete */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -