diff options
author | wbx <wbx@3c298f89-4303-0410-b956-a3cf2f4a3e73> | 2006-03-28 23:37:21 +0000 |
---|---|---|
committer | wbx <wbx@3c298f89-4303-0410-b956-a3cf2f4a3e73> | 2006-03-28 23:37:21 +0000 |
commit | fb6759b974d9e91092ac29acc4836514bea73981 (patch) | |
tree | ab09657c4a2d214d680adeb265533a37db6b9e7b /target/linux | |
parent | 424d05490f66ce49912b34cc9b6375e6f718e346 (diff) |
add free bcm43xx driver as patch for brcm-2.6, needs more testing and Makefile changes, but compiles. madwifi users: please check if it has any side effects
git-svn-id: svn://svn.openwrt.org/openwrt/trunk/openwrt@3536 3c298f89-4303-0410-b956-a3cf2f4a3e73
Diffstat (limited to 'target/linux')
-rw-r--r-- | target/linux/brcm-2.6/config | 15 | ||||
-rw-r--r-- | target/linux/brcm-2.6/patches/005-bcm43xx-dscape-060328.patch | 35744 |
2 files changed, 35758 insertions, 1 deletions
diff --git a/target/linux/brcm-2.6/config b/target/linux/brcm-2.6/config index 38ddc40e24..81092f3783 100644 --- a/target/linux/brcm-2.6/config +++ b/target/linux/brcm-2.6/config @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit # Linux kernel version: 2.6.16 -# Thu Mar 23 15:31:19 2006 +# Wed Mar 29 00:13:01 2006 # CONFIG_MIPS=y @@ -586,6 +586,12 @@ CONFIG_BT_HCIUART_BCSP=y # CONFIG_BT_HCIBLUECARD is not set # CONFIG_BT_HCIBTUART is not set # CONFIG_BT_HCIVHCI is not set +CONFIG_D80211=m +CONFIG_D80211_DEBUG=y +CONFIG_D80211_VERBOSE_DEBUG=y +# CONFIG_TKIP_DEBUG is not set +# CONFIG_D80211_DEBUG_COUNTERS is not set +# CONFIG_HOSTAPD_WPA_TESTING is not set CONFIG_IEEE80211=m # CONFIG_IEEE80211_DEBUG is not set CONFIG_IEEE80211_CRYPT_WEP=m @@ -955,6 +961,13 @@ CONFIG_HOSTAP_FIRMWARE_NVRAM=y CONFIG_HOSTAP_PLX=m CONFIG_HOSTAP_PCI=m CONFIG_HOSTAP_CS=m +CONFIG_BCM43XX_D80211=m +CONFIG_BCM43XX_D80211_DEBUG=y +CONFIG_BCM43XX_D80211_DMA=y +CONFIG_BCM43XX_D80211_PIO=y +CONFIG_BCM43XX_D80211_DMA_AND_PIO_MODE=y +# CONFIG_BCM43XX_D80211_DMA_MODE is not set +# CONFIG_BCM43XX_D80211_PIO_MODE is not set CONFIG_NET_WIRELESS=y # diff --git a/target/linux/brcm-2.6/patches/005-bcm43xx-dscape-060328.patch b/target/linux/brcm-2.6/patches/005-bcm43xx-dscape-060328.patch new file mode 100644 index 0000000000..0611a0bcaa --- /dev/null +++ b/target/linux/brcm-2.6/patches/005-bcm43xx-dscape-060328.patch @@ -0,0 +1,35744 @@ +diff -Nur linux-2.6.16/drivers/net/wireless/bcm43xx-d80211/bcm43xx_debugfs.c linux-2.6.16-bcm43xx/drivers/net/wireless/bcm43xx-d80211/bcm43xx_debugfs.c +--- linux-2.6.16/drivers/net/wireless/bcm43xx-d80211/bcm43xx_debugfs.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.16-bcm43xx/drivers/net/wireless/bcm43xx-d80211/bcm43xx_debugfs.c 2006-03-28 22:16:14.000000000 +0200 +@@ -0,0 +1,499 @@ ++/* ++ ++ Broadcom BCM43xx wireless driver ++ ++ debugfs driver debugging code ++ ++ Copyright (c) 2005 Michael Buesch <mbuesch@freenet.de> ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; see the file COPYING. If not, write to ++ the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, ++ Boston, MA 02110-1301, USA. ++ ++*/ ++ ++ ++ ++#include <linux/fs.h> ++#include <linux/debugfs.h> ++#include <linux/slab.h> ++#include <linux/netdevice.h> ++#include <linux/pci.h> ++#include <asm/io.h> ++ ++#include "bcm43xx.h" ++#include "bcm43xx_main.h" ++#include "bcm43xx_debugfs.h" ++#include "bcm43xx_dma.h" ++#include "bcm43xx_pio.h" ++#include "bcm43xx_xmit.h" ++ ++#define REALLY_BIG_BUFFER_SIZE (1024*256) ++ ++static struct bcm43xx_debugfs fs; ++static char really_big_buffer[REALLY_BIG_BUFFER_SIZE]; ++static DECLARE_MUTEX(big_buffer_sem); ++ ++ ++static ssize_t write_file_dummy(struct file *file, const char __user *buf, ++ size_t count, loff_t *ppos) ++{ ++ return count; ++} ++ ++static int open_file_generic(struct inode *inode, struct file *file) ++{ ++ file->private_data = inode->u.generic_ip; ++ return 0; ++} ++ ++#define fappend(fmt, x...) pos += snprintf(buf + pos, len - pos, fmt , ##x) ++ ++static ssize_t devinfo_read_file(struct file *file, char __user *userbuf, ++ size_t count, loff_t *ppos) ++{ ++ const size_t len = REALLY_BIG_BUFFER_SIZE; ++ ++ struct bcm43xx_private *bcm = file->private_data; ++ char *buf = really_big_buffer; ++ size_t pos = 0; ++ ssize_t res; ++ struct net_device *net_dev; ++ struct pci_dev *pci_dev; ++ unsigned long flags; ++ u16 tmp16; ++ int i; ++ ++ down(&big_buffer_sem); ++ ++ bcm43xx_lock_mmio(bcm, flags); ++ if (!bcm->initialized) { ++ fappend("Board not initialized.\n"); ++ goto out; ++ } ++ net_dev = bcm->net_dev; ++ pci_dev = bcm->pci_dev; ++ ++ /* This is where the information is written to the "devinfo" file */ ++ fappend("*** %s devinfo ***\n", net_dev->name); ++ fappend("vendor: 0x%04x device: 0x%04x\n", ++ pci_dev->vendor, pci_dev->device); ++ fappend("subsystem_vendor: 0x%04x subsystem_device: 0x%04x\n", ++ pci_dev->subsystem_vendor, pci_dev->subsystem_device); ++ fappend("IRQ: %d\n", bcm->irq); ++ fappend("mmio_addr: 0x%p mmio_len: %u\n", bcm->mmio_addr, bcm->mmio_len); ++ fappend("chip_id: 0x%04x chip_rev: 0x%02x\n", bcm->chip_id, bcm->chip_rev); ++ if ((bcm->core_80211[0].rev >= 3) && (bcm43xx_read32(bcm, 0x0158) & (1 << 16))) ++ fappend("Radio disabled by hardware!\n"); ++ if ((bcm->core_80211[0].rev < 3) && !(bcm43xx_read16(bcm, 0x049A) & (1 << 4))) ++ fappend("Radio disabled by hardware!\n"); ++ fappend("board_vendor: 0x%04x board_type: 0x%04x\n", bcm->board_vendor, ++ bcm->board_type); ++ ++ fappend("\nCores:\n"); ++#define fappend_core(name, info) fappend("core \"" name "\" %s, %s, id: 0x%04x, " \ ++ "rev: 0x%02x, index: 0x%02x\n", \ ++ (info).available \ ++ ? "available" : "nonavailable", \ ++ (info).enabled \ ++ ? "enabled" : "disabled", \ ++ (info).id, (info).rev, (info).index) ++ fappend_core("CHIPCOMMON", bcm->core_chipcommon); ++ fappend_core("PCI", bcm->core_pci); ++ fappend_core("first 80211", bcm->core_80211[0]); ++ fappend_core("second 80211", bcm->core_80211[1]); ++#undef fappend_core ++ tmp16 = bcm43xx_read16(bcm, BCM43xx_MMIO_GPIO_CONTROL); ++ fappend("LEDs: "); ++ for (i = 0; i < BCM43xx_NR_LEDS; i++) ++ fappend("%d ", !!(tmp16 & (1 << i))); ++ fappend("\n"); ++ ++out: ++ bcm43xx_unlock_mmio(bcm, flags); ++ res = simple_read_from_buffer(userbuf, count, ppos, buf, pos); ++ up(&big_buffer_sem); ++ return res; ++} ++ ++static ssize_t drvinfo_read_file(struct file *file, char __user *userbuf, ++ size_t count, loff_t *ppos) ++{ ++ const size_t len = REALLY_BIG_BUFFER_SIZE; ++ ++ char *buf = really_big_buffer; ++ size_t pos = 0; ++ ssize_t res; ++ ++ down(&big_buffer_sem); ++ ++ /* This is where the information is written to the "driver" file */ ++ fappend(KBUILD_MODNAME " driver\n"); ++ fappend("Compiled at: %s %s\n", __DATE__, __TIME__); ++ ++ res = simple_read_from_buffer(userbuf, count, ppos, buf, pos); ++ up(&big_buffer_sem); ++ return res; ++} ++ ++static ssize_t spromdump_read_file(struct file *file, char __user *userbuf, ++ size_t count, loff_t *ppos) ++{ ++ const size_t len = REALLY_BIG_BUFFER_SIZE; ++ ++ struct bcm43xx_private *bcm = file->private_data; ++ char *buf = really_big_buffer; ++ size_t pos = 0; ++ ssize_t res; ++ unsigned long flags; ++ ++ down(&big_buffer_sem); ++ bcm43xx_lock_mmio(bcm, flags); ++ if (!bcm->initialized) { ++ fappend("Board not initialized.\n"); ++ goto out; ++ } ++ ++ /* This is where the information is written to the "sprom_dump" file */ ++ fappend("boardflags: 0x%04x\n", bcm->sprom.boardflags); ++ ++out: ++ bcm43xx_unlock_mmio(bcm, flags); ++ res = simple_read_from_buffer(userbuf, count, ppos, buf, pos); ++ up(&big_buffer_sem); ++ return res; ++} ++ ++static ssize_t tsf_read_file(struct file *file, char __user *userbuf, ++ size_t count, loff_t *ppos) ++{ ++ const size_t len = REALLY_BIG_BUFFER_SIZE; ++ ++ struct bcm43xx_private *bcm = file->private_data; ++ char *buf = really_big_buffer; ++ size_t pos = 0; ++ ssize_t res; ++ unsigned long flags; ++ u64 tsf; ++ ++ down(&big_buffer_sem); ++ bcm43xx_lock_mmio(bcm, flags); ++ if (!bcm->initialized) { ++ fappend("Board not initialized.\n"); ++ goto out; ++ } ++ bcm43xx_tsf_read(bcm, &tsf); ++ fappend("0x%08x%08x\n", ++ (unsigned int)((tsf & 0xFFFFFFFF00000000ULL) >> 32), ++ (unsigned int)(tsf & 0xFFFFFFFFULL)); ++ ++out: ++ bcm43xx_unlock_mmio(bcm, flags); ++ res = simple_read_from_buffer(userbuf, count, ppos, buf, pos); ++ up(&big_buffer_sem); ++ return res; ++} ++ ++static ssize_t tsf_write_file(struct file *file, const char __user *user_buf, ++ size_t count, loff_t *ppos) ++{ ++ struct bcm43xx_private *bcm = file->private_data; ++ char *buf = really_big_buffer; ++ ssize_t buf_size; ++ ssize_t res; ++ unsigned long flags; ++ u64 tsf; ++ ++ buf_size = min(count, sizeof (really_big_buffer) - 1); ++ down(&big_buffer_sem); ++ if (copy_from_user(buf, user_buf, buf_size)) { ++ res = -EFAULT; ++ goto out_up; ++ } ++ bcm43xx_lock_mmio(bcm, flags); ++ if (!bcm->initialized) { ++ printk(KERN_INFO PFX "debugfs: Board not initialized.\n"); ++ res = -EFAULT; ++ goto out_unlock; ++ } ++ if (sscanf(buf, "%lli", &tsf) != 1) { ++ printk(KERN_INFO PFX "debugfs: invalid values for \"tsf\"\n"); ++ res = -EINVAL; ++ goto out_unlock; ++ } ++ bcm43xx_tsf_write(bcm, tsf); ++ res = buf_size; ++ ++out_unlock: ++ bcm43xx_unlock_mmio(bcm, flags); ++out_up: ++ up(&big_buffer_sem); ++ return res; ++} ++ ++static ssize_t txstat_read_file(struct file *file, char __user *userbuf, ++ size_t count, loff_t *ppos) ++{ ++ const size_t len = REALLY_BIG_BUFFER_SIZE; ++ ++ struct bcm43xx_private *bcm = file->private_data; ++ char *buf = really_big_buffer; ++ size_t pos = 0; ++ ssize_t res; ++ unsigned long flags; ++ struct bcm43xx_dfsentry *e; ++ struct bcm43xx_xmitstatus *status; ++ int i, cnt, j = 0; ++ ++ down(&big_buffer_sem); ++ bcm43xx_lock(bcm, flags); ++ ++ fappend("Last %d logged xmitstatus blobs (Latest first):\n\n", ++ BCM43xx_NR_LOGGED_XMITSTATUS); ++ e = bcm->dfsentry; ++ if (e->xmitstatus_printing == 0) { ++ /* At the beginning, make a copy of all data to avoid ++ * concurrency, as this function is called multiple ++ * times for big logs. Without copying, the data might ++ * change between reads. This would result in total trash. ++ */ ++ e->xmitstatus_printing = 1; ++ e->saved_xmitstatus_ptr = e->xmitstatus_ptr; ++ e->saved_xmitstatus_cnt = e->xmitstatus_cnt; ++ memcpy(e->xmitstatus_print_buffer, e->xmitstatus_buffer, ++ BCM43xx_NR_LOGGED_XMITSTATUS * sizeof(*(e->xmitstatus_buffer))); ++ } ++ i = e->saved_xmitstatus_ptr - 1; ++ if (i < 0) ++ i = BCM43xx_NR_LOGGED_XMITSTATUS - 1; ++ cnt = e->saved_xmitstatus_cnt; ++ while (cnt) { ++ status = e->xmitstatus_print_buffer + i; ++ fappend("0x%02x: cookie: 0x%04x, flags: 0x%02x, " ++ "cnt1: 0x%02x, cnt2: 0x%02x, seq: 0x%04x, " ++ "unk: 0x%04x\n", j, ++ status->cookie, status->flags, ++ status->cnt1, status->cnt2, status->seq, ++ status->unknown); ++ j++; ++ cnt--; ++ i--; ++ if (i < 0) ++ i = BCM43xx_NR_LOGGED_XMITSTATUS - 1; ++ } ++ ++ bcm43xx_unlock(bcm, flags); ++ res = simple_read_from_buffer(userbuf, count, ppos, buf, pos); ++ bcm43xx_lock(bcm, flags); ++ if (*ppos == pos) { ++ /* Done. Drop the copied data. */ ++ e->xmitstatus_printing = 0; ++ } ++ bcm43xx_unlock(bcm, flags); ++ up(&big_buffer_sem); ++ return res; ++} ++ ++#undef fappend ++ ++ ++static struct file_operations devinfo_fops = { ++ .read = devinfo_read_file, ++ .write = write_file_dummy, ++ .open = open_file_generic, ++}; ++ ++static struct file_operations spromdump_fops = { ++ .read = spromdump_read_file, ++ .write = write_file_dummy, ++ .open = open_file_generic, ++}; ++ ++static struct file_operations drvinfo_fops = { ++ .read = drvinfo_read_file, ++ .write = write_file_dummy, ++ .open = open_file_generic, ++}; ++ ++static struct file_operations tsf_fops = { ++ .read = tsf_read_file, ++ .write = tsf_write_file, ++ .open = open_file_generic, ++}; ++ ++static struct file_operations txstat_fops = { ++ .read = txstat_read_file, ++ .write = write_file_dummy, ++ .open = open_file_generic, ++}; ++ ++ ++void bcm43xx_debugfs_add_device(struct bcm43xx_private *bcm) ++{ ++ struct bcm43xx_dfsentry *e; ++ char devdir[IFNAMSIZ]; ++ ++ assert(bcm); ++ e = kzalloc(sizeof(*e), GFP_KERNEL); ++ if (!e) { ++ printk(KERN_ERR PFX "out of memory\n"); ++ return; ++ } ++ e->bcm = bcm; ++ e->xmitstatus_buffer = kzalloc(BCM43xx_NR_LOGGED_XMITSTATUS ++ * sizeof(*(e->xmitstatus_buffer)), ++ GFP_KERNEL); ++ if (!e->xmitstatus_buffer) { ++ printk(KERN_ERR PFX "out of memory\n"); ++ kfree(e); ++ return; ++ } ++ e->xmitstatus_print_buffer = kzalloc(BCM43xx_NR_LOGGED_XMITSTATUS ++ * sizeof(*(e->xmitstatus_buffer)), ++ GFP_KERNEL); ++ if (!e->xmitstatus_print_buffer) { ++ printk(KERN_ERR PFX "out of memory\n"); ++ kfree(e); ++ return; ++ } ++ ++ ++ bcm->dfsentry = e; ++ ++ strncpy(devdir, bcm->net_dev->name, ARRAY_SIZE(devdir)); ++ e->subdir = debugfs_create_dir(devdir, fs.root); ++ e->dentry_devinfo = debugfs_create_file("devinfo", 0444, e->subdir, ++ bcm, &devinfo_fops); ++ if (!e->dentry_devinfo) ++ printk(KERN_ERR PFX "debugfs: creating \"devinfo\" for \"%s\" failed!\n", devdir); ++ e->dentry_spromdump = debugfs_create_file("sprom_dump", 0444, e->subdir, ++ bcm, &spromdump_fops); ++ if (!e->dentry_spromdump) ++ printk(KERN_ERR PFX "debugfs: creating \"sprom_dump\" for \"%s\" failed!\n", devdir); ++ e->dentry_tsf = debugfs_create_file("tsf", 0666, e->subdir, ++ bcm, &tsf_fops); ++ if (!e->dentry_tsf) ++ printk(KERN_ERR PFX "debugfs: creating \"tsf\" for \"%s\" failed!\n", devdir); ++ e->dentry_txstat = debugfs_create_file("tx_status", 0444, e->subdir, ++ bcm, &txstat_fops); ++ if (!e->dentry_txstat) ++ printk(KERN_ERR PFX "debugfs: creating \"tx_status\" for \"%s\" failed!\n", devdir); ++} ++ ++void bcm43xx_debugfs_remove_device(struct bcm43xx_private *bcm) ++{ ++ struct bcm43xx_dfsentry *e; ++ ++ if (!bcm) ++ return; ++ ++ e = bcm->dfsentry; ++ assert(e); ++ debugfs_remove(e->dentry_spromdump); ++ debugfs_remove(e->dentry_devinfo); ++ debugfs_remove(e->dentry_tsf); ++ debugfs_remove(e->dentry_txstat); ++ debugfs_remove(e->subdir); ++ kfree(e->xmitstatus_buffer); ++ kfree(e->xmitstatus_print_buffer); ++ kfree(e); ++} ++ ++void bcm43xx_debugfs_log_txstat(struct bcm43xx_private *bcm, ++ struct bcm43xx_xmitstatus *status) ++{ ++ struct bcm43xx_dfsentry *e; ++ struct bcm43xx_xmitstatus *savedstatus; ++ ++ /* This is protected by bcm->_lock */ ++ e = bcm->dfsentry; ++ assert(e); ++ savedstatus = e->xmitstatus_buffer + e->xmitstatus_ptr; ++ memcpy(savedstatus, status, sizeof(*status)); ++ e->xmitstatus_ptr++; ++ if (e->xmitstatus_ptr >= BCM43xx_NR_LOGGED_XMITSTATUS) ++ e->xmitstatus_ptr = 0; ++ if (e->xmitstatus_cnt < BCM43xx_NR_LOGGED_XMITSTATUS) ++ e->xmitstatus_cnt++; ++} ++ ++void bcm43xx_debugfs_init(void) ++{ ++ memset(&fs, 0, sizeof(fs)); ++ fs.root = debugfs_create_dir(KBUILD_MODNAME, NULL); ++ if (!fs.root) ++ printk(KERN_ERR PFX "debugfs: creating \"" KBUILD_MODNAME "\" subdir failed!\n"); ++ fs.dentry_driverinfo = debugfs_create_file("driver", 0444, fs.root, NULL, &drvinfo_fops); ++ if (!fs.dentry_driverinfo) ++ printk(KERN_ERR PFX "debugfs: creating \"" KBUILD_MODNAME "/driver\" failed!\n"); ++} ++ ++void bcm43xx_debugfs_exit(void) ++{ ++ debugfs_remove(fs.dentry_driverinfo); ++ debugfs_remove(fs.root); ++} ++ ++void bcm43xx_printk_dump(const char *data, ++ size_t size, ++ const char *description) ++{ ++ size_t i; ++ char c; ++ ++ printk(KERN_INFO PFX "Data dump (%s, %u bytes):", ++ description, size); ++ for (i = 0; i < size; i++) { ++ c = data[i]; ++ if (i % 8 == 0) ++ printk("\n" KERN_INFO PFX "0x%08x: 0x%02x, ", i, c & 0xff); ++ else ++ printk("0x%02x, ", c & 0xff); ++ } ++ printk("\n"); ++} ++ ++void bcm43xx_printk_bitdump(const unsigned char *data, ++ size_t bytes, int msb_to_lsb, ++ const char *description) ++{ ++ size_t i; ++ int j; ++ const unsigned char *d; ++ ++ printk(KERN_INFO PFX "*** Bitdump (%s, %u bytes, %s) ***", ++ description, bytes, msb_to_lsb ? "MSB to LSB" : "LSB to MSB"); ++ for (i = 0; i < bytes; i++) { ++ d = data + i; ++ if (i % 8 == 0) ++ printk("\n" KERN_INFO PFX "0x%08x: ", i); ++ if (msb_to_lsb) { ++ for (j = 7; j >= 0; j--) { ++ if (*d & (1 << j)) ++ printk("1"); ++ else ++ printk("0"); ++ } ++ } else { ++ for (j = 0; j < 8; j++) { ++ if (*d & (1 << j)) ++ printk("1"); ++ else ++ printk("0"); ++ } ++ } ++ printk(" "); ++ } ++ printk("\n"); ++} +diff -Nur linux-2.6.16/drivers/net/wireless/bcm43xx-d80211/bcm43xx_debugfs.h linux-2.6.16-bcm43xx/drivers/net/wireless/bcm43xx-d80211/bcm43xx_debugfs.h +--- linux-2.6.16/drivers/net/wireless/bcm43xx-d80211/bcm43xx_debugfs.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.16-bcm43xx/drivers/net/wireless/bcm43xx-d80211/bcm43xx_debugfs.h 2006-03-28 22:16:14.000000000 +0200 +@@ -0,0 +1,117 @@ ++#ifndef BCM43xx_DEBUGFS_H_ ++#define BCM43xx_DEBUGFS_H_ ++ ++struct bcm43xx_private; ++struct bcm43xx_xmitstatus; ++ ++#ifdef CONFIG_BCM43XX_D80211_DEBUG ++ ++#include <linux/list.h> ++#include <asm/semaphore.h> ++ ++struct dentry; ++ ++/* limited by the size of the "really_big_buffer" */ ++#define BCM43xx_NR_LOGGED_XMITSTATUS 100 ++ ++struct bcm43xx_dfsentry { ++ struct dentry *subdir; ++ struct dentry *dentry_devinfo; ++ struct dentry *dentry_spromdump; ++ struct dentry *dentry_tsf; ++ struct dentry *dentry_txstat; ++ ++ struct bcm43xx_private *bcm; ++ ++ /* saved xmitstatus. */ ++ struct bcm43xx_xmitstatus *xmitstatus_buffer; ++ int xmitstatus_ptr; ++ int xmitstatus_cnt; ++ /* We need a seperate buffer while printing to avoid ++ * concurrency issues. (New xmitstatus can arrive ++ * while we are printing). ++ */ ++ struct bcm43xx_xmitstatus *xmitstatus_print_buffer; ++ int saved_xmitstatus_ptr; ++ int saved_xmitstatus_cnt; ++ int xmitstatus_printing; ++}; ++ ++struct bcm43xx_debugfs { ++ struct dentry *root; ++ struct dentry *dentry_driverinfo; ++}; ++ ++void bcm43xx_debugfs_init(void); ++void bcm43xx_debugfs_exit(void); ++void bcm43xx_debugfs_add_device(struct bcm43xx_private *bcm); ++void bcm43xx_debugfs_remove_device(struct bcm43xx_private *bcm); ++void bcm43xx_debugfs_log_txstat(struct bcm43xx_private *bcm, ++ struct bcm43xx_xmitstatus *status); ++ ++/* Debug helper: Dump binary data through printk. */ ++void bcm43xx_printk_dump(const char *data, ++ size_t size, ++ const char *description); ++/* Debug helper: Dump bitwise binary data through printk. */ ++void bcm43xx_printk_bitdump(const unsigned char *data, ++ size_t bytes, int msb_to_lsb, ++ const char *description); ++#define bcm43xx_printk_bitdumpt(pointer, msb_to_lsb, description) \ ++ do { \ ++ bcm43xx_printk_bitdump((const unsigned char *)(pointer), \ ++ sizeof(*(pointer)), \ ++ (msb_to_lsb), \ ++ (description)); \ ++ } while (0) ++ ++#else /* CONFIG_BCM43XX_D80211_DEBUG*/ ++ ++static inline ++void bcm43xx_debugfs_init(void) { } ++static inline ++void bcm43xx_debugfs_exit(void) { } ++static inline ++void bcm43xx_debugfs_add_device(struct bcm43xx_private *bcm) { } ++static inline ++void bcm43xx_debugfs_remove_device(struct bcm43xx_private *bcm) { } ++static inline ++void bcm43xx_debugfs_log_txstat(struct bcm43xx_private *bcm, ++ struct bcm43xx_xmitstatus *status) { } ++ ++static inline ++void bcm43xx_printk_dump(const char *data, ++ size_t size, ++ const char *description) ++{ ++} ++static inline ++void bcm43xx_printk_bitdump(const unsigned char *data, ++ size_t bytes, int msb_to_lsb, ++ const char *description) ++{ ++} ++#define bcm43xx_printk_bitdumpt(pointer, msb_to_lsb, description) do { /* nothing */ } while (0) ++ ++#endif /* CONFIG_BCM43XX_D80211_DEBUG*/ ++ ++/* Ugly helper macros to make incomplete code more verbose on runtime */ ++#ifdef TODO ++# undef TODO ++#endif ++#define TODO() \ ++ do { \ ++ printk(KERN_INFO PFX "TODO: Incomplete code in %s() at %s:%d\n", \ ++ __FUNCTION__, __FILE__, __LINE__); \ ++ } while (0) ++ ++#ifdef FIXME ++# undef FIXME ++#endif ++#define FIXME() \ ++ do { \ ++ printk(KERN_INFO PFX "FIXME: Possibly broken code in %s() at %s:%d\n", \ ++ __FUNCTION__, __FILE__, __LINE__); \ ++ } while (0) ++ ++#endif /* BCM43xx_DEBUGFS_H_ */ +diff -Nur linux-2.6.16/drivers/net/wireless/bcm43xx-d80211/bcm43xx_dma.c linux-2.6.16-bcm43xx/drivers/net/wireless/bcm43xx-d80211/bcm43xx_dma.c +--- linux-2.6.16/drivers/net/wireless/bcm43xx-d80211/bcm43xx_dma.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.16-bcm43xx/drivers/net/wireless/bcm43xx-d80211/bcm43xx_dma.c 2006-03-28 22:16:14.000000000 +0200 +@@ -0,0 +1,991 @@ ++/* ++ ++ Broadcom BCM43xx wireless driver ++ ++ DMA ringbuffer and descriptor allocation/management ++ ++ Copyright (c) 2005 Michael Buesch <mbuesch@freenet.de> ++ ++ Some code in this file is derived from the b44.c driver ++ Copyright (C) 2002 David S. Miller ++ Copyright (C) Pekka Pietikainen ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; see the file COPYING. If not, write to ++ the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, ++ Boston, MA 02110-1301, USA. ++ ++*/ ++ ++#include "bcm43xx.h" ++#include "bcm43xx_dma.h" ++#include "bcm43xx_main.h" ++#include "bcm43xx_debugfs.h" ++#include "bcm43xx_power.h" ++#include "bcm43xx_xmit.h" ++ ++#include <linux/dma-mapping.h> ++#include <linux/pci.h> ++#include <linux/delay.h> ++#include <linux/skbuff.h> ++ ++ ++static inline int free_slots(struct bcm43xx_dmaring *ring) ++{ ++ return (ring->nr_slots - ring->used_slots); ++} ++ ++static inline int next_slot(struct bcm43xx_dmaring *ring, int slot) ++{ ++ assert(slot >= -1 && slot <= ring->nr_slots - 1); ++ if (slot == ring->nr_slots - 1) ++ return 0; ++ return slot + 1; ++} ++ ++static inline int prev_slot(struct bcm43xx_dmaring *ring, int slot) ++{ ++ assert(slot >= 0 && slot <= ring->nr_slots - 1); ++ if (slot == 0) ++ return ring->nr_slots - 1; ++ return slot - 1; ++} ++ ++/* Request a slot for usage. */ ++static inline ++int request_slot(struct bcm43xx_dmaring *ring) ++{ ++ int slot; ++ ++ assert(ring->tx); ++ assert(!ring->suspended); ++ assert(free_slots(ring) != 0); ++ ++ slot = next_slot(ring, ring->current_slot); ++ ring->current_slot = slot; ++ ring->used_slots++; ++ ++#ifdef CONFIG_BCM43XX_D80211_DEBUG ++ if (ring->used_slots > ring->max_used_slots) ++ ring->max_used_slots = ring->used_slots; ++#endif /* CONFIG_BCM43XX_D80211_DEBUG*/ ++ ++ return slot; ++} ++ ++/* Return a slot to the free slots. */ ++static inline ++void return_slot(struct bcm43xx_dmaring *ring, int slot) ++{ ++ assert(ring->tx); ++ ++ ring->used_slots--; ++} ++ ++static inline ++dma_addr_t map_descbuffer(struct bcm43xx_dmaring *ring, ++ unsigned char *buf, ++ size_t len, ++ int tx) ++{ ++ dma_addr_t dmaaddr; ++ ++ if (tx) { ++ dmaaddr = dma_map_single(&ring->bcm->pci_dev->dev, ++ buf, len, ++ DMA_TO_DEVICE); ++ } else { ++ dmaaddr = dma_map_single(&ring->bcm->pci_dev->dev, ++ buf, len, ++ DMA_FROM_DEVICE); ++ } ++ ++ return dmaaddr; ++} ++ ++static inline ++void unmap_descbuffer(struct bcm43xx_dmaring *ring, ++ dma_addr_t addr, ++ size_t len, ++ int tx) ++{ ++ if (tx) { ++ dma_unmap_single(&ring->bcm->pci_dev->dev, ++ addr, len, ++ DMA_TO_DEVICE); ++ } else { ++ dma_unmap_single(&ring->bcm->pci_dev->dev, ++ addr, len, ++ DMA_FROM_DEVICE); ++ } ++} ++ ++static inline ++void sync_descbuffer_for_cpu(struct bcm43xx_dmaring *ring, ++ dma_addr_t addr, ++ size_t len) ++{ ++ assert(!ring->tx); ++ ++ dma_sync_single_for_cpu(&ring->bcm->pci_dev->dev, ++ addr, len, DMA_FROM_DEVICE); ++} ++ ++static inline ++void sync_descbuffer_for_device(struct bcm43xx_dmaring *ring, ++ dma_addr_t addr, ++ size_t len) ++{ ++ assert(!ring->tx); ++ ++ dma_sync_single_for_device(&ring->bcm->pci_dev->dev, ++ addr, len, DMA_FROM_DEVICE); ++} ++ ++/* Unmap and free a descriptor buffer. */ ++static inline ++void free_descriptor_buffer(struct bcm43xx_dmaring *ring, ++ struct bcm43xx_dmadesc *desc, ++ struct bcm43xx_dmadesc_meta *meta, ++ int irq_context) ++{ ++ assert(meta->skb); ++ if (irq_context) ++ dev_kfree_skb_irq(meta->skb); ++ else ++ dev_kfree_skb(meta->skb); ++ meta->skb = NULL; ++} ++ ++static int alloc_ringmemory(struct bcm43xx_dmaring *ring) ++{ ++ struct device *dev = &(ring->bcm->pci_dev->dev); ++ ++ ring->vbase = dma_alloc_coherent(dev, BCM43xx_DMA_RINGMEMSIZE, ++ &(ring->dmabase), GFP_KERNEL); ++ if (!ring->vbase) { ++ printk(KERN_ERR PFX "DMA ringmemory allocation failed\n"); ++ return -ENOMEM; ++ } ++ if (ring->dmabase + BCM43xx_DMA_RINGMEMSIZE > BCM43xx_DMA_BUSADDRMAX) { ++ printk(KERN_ERR PFX ">>>FATAL ERROR<<< DMA RINGMEMORY >1G " ++ "(0x%08x, len: %lu)\n", ++ ring->dmabase, BCM43xx_DMA_RINGMEMSIZE); ++ dma_free_coherent(dev, BCM43xx_DMA_RINGMEMSIZE, ++ ring->vbase, ring->dmabase); ++ return -ENOMEM; ++ } ++ assert(!(ring->dmabase & 0x000003FF)); ++ memset(ring->vbase, 0, BCM43xx_DMA_RINGMEMSIZE); ++ ++ return 0; ++} ++ ++static void free_ringmemory(struct bcm43xx_dmaring *ring) ++{ ++ struct device *dev = &(ring->bcm->pci_dev->dev); ++ ++ dma_free_coherent(dev, BCM43xx_DMA_RINGMEMSIZE, ++ ring->vbase, ring->dmabase); ++} ++ ++/* Reset the RX DMA channel */ ++int bcm43xx_dmacontroller_rx_reset(struct bcm43xx_private *bcm, ++ u16 mmio_base) ++{ ++ int i; ++ u32 value; ++ ++ bcm43xx_write32(bcm, ++ mmio_base + BCM43xx_DMA_RX_CONTROL, ++ 0x00000000); ++ for (i = 0; i < 1000; i++) { ++ value = bcm43xx_read32(bcm, ++ mmio_base + BCM43xx_DMA_RX_STATUS); ++ value &= BCM43xx_DMA_RXSTAT_STAT_MASK; ++ if (value == BCM43xx_DMA_RXSTAT_STAT_DISABLED) { ++ i = -1; ++ break; ++ } ++ udelay(10); ++ } ++ if (i != -1) { ++ printk(KERN_ERR PFX "Error: Wait on DMA RX status timed out.\n"); ++ return -ENODEV; ++ } ++ ++ return 0; ++} ++ ++/* Reset the RX DMA channel */ ++int bcm43xx_dmacontroller_tx_reset(struct bcm43xx_private *bcm, ++ u16 mmio_base) ++{ ++ int i; ++ u32 value; ++ ++ for (i = 0; i < 1000; i++) { ++ value = bcm43xx_read32(bcm, ++ mmio_base + BCM43xx_DMA_TX_STATUS); ++ value &= BCM43xx_DMA_TXSTAT_STAT_MASK; ++ if (value == BCM43xx_DMA_TXSTAT_STAT_DISABLED || ++ value == BCM43xx_DMA_TXSTAT_STAT_IDLEWAIT || ++ value == BCM43xx_DMA_TXSTAT_STAT_STOPPED) ++ break; ++ udelay(10); ++ } ++ bcm43xx_write32(bcm, ++ mmio_base + BCM43xx_DMA_TX_CONTROL, ++ 0x00000000); ++ for (i = 0; i < 1000; i++) { ++ value = bcm43xx_read32(bcm, ++ mmio_base + BCM43xx_DMA_TX_STATUS); ++ value &= BCM43xx_DMA_TXSTAT_STAT_MASK; ++ if (value == BCM43xx_DMA_TXSTAT_STAT_DISABLED) { ++ i = -1; ++ break; ++ } ++ udelay(10); ++ } ++ if (i != -1) { ++ printk(KERN_ERR PFX "Error: Wait on DMA TX status timed out.\n"); ++ return -ENODEV; ++ } ++ /* ensure the reset is completed. */ ++ udelay(300); ++ ++ return 0; ++} ++ ++static int setup_rx_descbuffer(struct bcm43xx_dmaring *ring, ++ struct bcm43xx_dmadesc *desc, ++ struct bcm43xx_dmadesc_meta *meta, ++ gfp_t gfp_flags) ++{ ++ struct bcm43xx_rxhdr *rxhdr; ++ dma_addr_t dmaaddr; ++ u32 desc_addr; ++ u32 desc_ctl; ++ const int slot = (int)(desc - ring->vbase); ++ struct sk_buff *skb; ++ ++ assert(slot >= 0 && slot < ring->nr_slots); ++ assert(!ring->tx); ++ ++ skb = __dev_alloc_skb(ring->rx_buffersize, gfp_flags); ++ if (unlikely(!skb)) ++ return -ENOMEM; ++ dmaaddr = map_descbuffer(ring, skb->data, ++ ring->rx_buffersize, 0); ++ if (unlikely(dmaaddr + ring->rx_buffersize > BCM43xx_DMA_BUSADDRMAX)) { ++ unmap_descbuffer(ring, dmaaddr, ring->rx_buffersize, 0); ++ dev_kfree_skb_any(skb); ++ printk(KERN_ERR PFX ">>>FATAL ERROR<<< DMA RX SKB >1G " ++ "(0x%08x, len: %u)\n", ++ dmaaddr, ring->rx_buffersize); ++ return -ENOMEM; ++ } ++ meta->skb = skb; ++ meta->dmaaddr = dmaaddr; ++ skb->dev = ring->bcm->net_dev; ++ desc_addr = (u32)(dmaaddr + ring->memoffset); ++ desc_ctl = (BCM43xx_DMADTOR_BYTECNT_MASK & ++ (u32)(ring->rx_buffersize - ring->frameoffset)); ++ if (slot == ring->nr_slots - 1) ++ desc_ctl |= BCM43xx_DMADTOR_DTABLEEND; ++ set_desc_addr(desc, desc_addr); ++ set_desc_ctl(desc, desc_ctl); ++ ++ rxhdr = (struct bcm43xx_rxhdr *)(skb->data); ++ rxhdr->frame_length = 0; ++ rxhdr->flags1 = 0; ++ ++ return 0; ++} ++ ++/* Allocate the initial descbuffers. ++ * This is used for an RX ring only. ++ */ ++static int alloc_initial_descbuffers(struct bcm43xx_dmaring *ring) ++{ ++ int i, err = -ENOMEM; ++ struct bcm43xx_dmadesc *desc; ++ struct bcm43xx_dmadesc_meta *meta; ++ ++ for (i = 0; i < ring->nr_slots; i++) { ++ desc = ring->vbase + i; ++ meta = ring->meta + i; ++ ++ err = setup_rx_descbuffer(ring, desc, meta, GFP_KERNEL); ++ if (err) ++ goto err_unwind; ++ } ++ ring->used_slots = ring->nr_slots; ++ err = 0; ++out: ++ return err; ++ ++err_unwind: ++ for (i--; i >= 0; i--) { ++ desc = ring->vbase + i; ++ meta = ring->meta + i; ++ ++ unmap_descbuffer(ring, meta->dmaaddr, ring->rx_buffersize, 0); ++ dev_kfree_skb(meta->skb); ++ } ++ goto out; ++} ++ ++/* Do initial setup of the DMA controller. ++ * Reset the controller, write the ring busaddress ++ * and switch the "enable" bit on. ++ */ ++static int dmacontroller_setup(struct bcm43xx_dmaring *ring) ++{ ++ int err = 0; ++ u32 value; ++ ++ if (ring->tx) { ++ /* Set Transmit Control register to "transmit enable" */ ++ bcm43xx_dma_write(ring, BCM43xx_DMA_TX_CONTROL, ++ BCM43xx_DMA_TXCTRL_ENABLE); ++ /* Set Transmit Descriptor ring address. */ ++ bcm43xx_dma_write(ring, BCM43xx_DMA_TX_DESC_RING, ++ ring->dmabase + ring->memoffset); ++ } else { ++ err = alloc_initial_descbuffers(ring); ++ if (err) ++ goto out; ++ /* Set Receive Control "receive enable" and frame offset */ ++ value = (ring->frameoffset << BCM43xx_DMA_RXCTRL_FRAMEOFF_SHIFT); ++ value |= BCM43xx_DMA_RXCTRL_ENABLE; ++ bcm43xx_dma_write(ring, BCM43xx_DMA_RX_CONTROL, value); ++ /* Set Receive Descriptor ring address. */ ++ bcm43xx_dma_write(ring, BCM43xx_DMA_RX_DESC_RING, ++ ring->dmabase + ring->memoffset); ++ /* Init the descriptor pointer. */ ++ bcm43xx_dma_write(ring, BCM43xx_DMA_RX_DESC_INDEX, 200); ++ } ++ ++out: ++ return err; ++} ++ ++/* Shutdown the DMA controller. */ ++static void dmacontroller_cleanup(struct bcm43xx_dmaring *ring) ++{ ++ if (ring->tx) { ++ bcm43xx_dmacontroller_tx_reset(ring->bcm, ring->mmio_base); ++ /* Zero out Transmit Descriptor ring address. */ ++ bcm43xx_dma_write(ring, BCM43xx_DMA_TX_DESC_RING, 0); ++ } else { ++ bcm43xx_dmacontroller_rx_reset(ring->bcm, ring->mmio_base); ++ /* Zero out Receive Descriptor ring address. */ ++ bcm43xx_dma_write(ring, BCM43xx_DMA_RX_DESC_RING, 0); ++ } ++} ++ ++static void free_all_descbuffers(struct bcm43xx_dmaring *ring) ++{ ++ struct bcm43xx_dmadesc *desc; ++ struct bcm43xx_dmadesc_meta *meta; ++ int i; ++ ++ if (!ring->used_slots) ++ return; ++ for (i = 0; i < ring->nr_slots; i++) { ++ desc = ring->vbase + i; ++ meta = ring->meta + i; ++ ++ if (!meta->skb) { ++ assert(ring->tx); ++ continue; ++ } ++ if (ring->tx) { ++ unmap_descbuffer(ring, meta->dmaaddr, ++ meta->skb->len, 1); ++ } else { ++ unmap_descbuffer(ring, meta->dmaaddr, ++ ring->rx_buffersize, 0); ++ } ++ free_descriptor_buffer(ring, desc, meta, 0); ++ } ++} ++ ++/* Main initialization function. */ ++static ++struct bcm43xx_dmaring * bcm43xx_setup_dmaring(struct bcm43xx_private *bcm, ++ u16 dma_controller_base, ++ int nr_descriptor_slots, ++ int tx) ++{ ++ struct bcm43xx_dmaring *ring; ++ int err; ++ ++ ring = kzalloc(sizeof(*ring), GFP_KERNEL); ++ if (!ring) ++ goto out; ++ ++ ring->meta = kzalloc(sizeof(*ring->meta) * nr_descriptor_slots, ++ GFP_KERNEL); ++ if (!ring->meta) ++ goto err_kfree_ring; ++ ++ ring->memoffset = BCM43xx_DMA_DMABUSADDROFFSET; ++#ifdef CONFIG_BCM947XX ++ if (bcm->pci_dev->bus->number == 0) ++ ring->memoffset = 0; ++#endif ++ ++ ring->bcm = bcm; ++ ring->nr_slots = nr_descriptor_slots; ++ ring->mmio_base = dma_controller_base; ++ if (tx) { ++ ring->tx = 1; ++ ring->current_slot = -1; ++ } else { ++ switch (dma_controller_base) { ++ case BCM43xx_MMIO_DMA1_BASE: ++ ring->rx_buffersize = BCM43xx_DMA1_RXBUFFERSIZE; ++ ring->frameoffset = BCM43xx_DMA1_RX_FRAMEOFFSET; ++ break; ++ case BCM43xx_MMIO_DMA4_BASE: ++ ring->rx_buffersize = BCM43xx_DMA4_RXBUFFERSIZE; ++ ring->frameoffset = BCM43xx_DMA4_RX_FRAMEOFFSET; ++ break; ++ default: ++ assert(0); ++ } ++ } ++ ++ err = alloc_ringmemory(ring); ++ if (err) ++ goto err_kfree_meta; ++ err = dmacontroller_setup(ring); ++ if (err) ++ goto err_free_ringmemory; ++ ++out: ++ return ring; ++ ++err_free_ringmemory: ++ free_ringmemory(ring); ++err_kfree_meta: ++ kfree(ring->meta); ++err_kfree_ring: ++ kfree(ring); ++ ring = NULL; ++ goto out; ++} ++ ++/* Main cleanup function. */ ++static void bcm43xx_destroy_dmaring(struct bcm43xx_dmaring *ring) ++{ ++ if (!ring) ++ return; ++ ++ dprintk(KERN_INFO PFX "DMA 0x%04x (%s) max used slots: %d/%d\n", ++ ring->mmio_base, ++ (ring->tx) ? "TX" : "RX", ++ ring->max_used_slots, ring->nr_slots); ++ /* Device IRQs are disabled prior entering this function, ++ * so no need to take care of concurrency with rx handler stuff. ++ */ ++ dmacontroller_cleanup(ring); ++ free_all_descbuffers(ring); ++ free_ringmemory(ring); ++ ++ kfree(ring->meta); ++ kfree(ring); ++} ++ ++void bcm43xx_dma_free(struct bcm43xx_private *bcm) ++{ ++ struct bcm43xx_dma *dma; ++ ++ if (bcm43xx_using_pio(bcm)) ++ return; ++ dma = bcm43xx_current_dma(bcm); ++ ++ bcm43xx_destroy_dmaring(dma->rx_ring1); ++ dma->rx_ring1 = NULL; ++ bcm43xx_destroy_dmaring(dma->rx_ring0); ++ dma->rx_ring0 = NULL; ++ bcm43xx_destroy_dmaring(dma->tx_ring3); ++ dma->tx_ring3 = NULL; ++ bcm43xx_destroy_dmaring(dma->tx_ring2); ++ dma->tx_ring2 = NULL; ++ bcm43xx_destroy_dmaring(dma->tx_ring1); ++ dma->tx_ring1 = NULL; ++ bcm43xx_destroy_dmaring(dma->tx_ring0); ++ dma->tx_ring0 = NULL; ++} ++ ++int bcm43xx_dma_init(struct bcm43xx_private *bcm) ++{ ++ struct bcm43xx_dma *dma = bcm43xx_current_dma(bcm); ++ struct bcm43xx_dmaring *ring; ++ int err = -ENOMEM; ++ ++ /* setup TX DMA channels. */ ++ ring = bcm43xx_setup_dmaring(bcm, BCM43xx_MMIO_DMA1_BASE, ++ BCM43xx_TXRING_SLOTS, 1); ++ if (!ring) ++ goto out; ++ dma->tx_ring0 = ring; ++ ++ ring = bcm43xx_setup_dmaring(bcm, BCM43xx_MMIO_DMA2_BASE, ++ BCM43xx_TXRING_SLOTS, 1); ++ if (!ring) ++ goto err_destroy_tx0; ++ dma->tx_ring1 = ring; ++ ++ ring = bcm43xx_setup_dmaring(bcm, BCM43xx_MMIO_DMA3_BASE, ++ BCM43xx_TXRING_SLOTS, 1); ++ if (!ring) ++ goto err_destroy_tx1; ++ dma->tx_ring2 = ring; ++ ++ ring = bcm43xx_setup_dmaring(bcm, BCM43xx_MMIO_DMA4_BASE, ++ BCM43xx_TXRING_SLOTS, 1); ++ if (!ring) ++ goto err_destroy_tx2; ++ dma->tx_ring3 = ring; ++ ++ /* setup RX DMA channels. */ ++ ring = bcm43xx_setup_dmaring(bcm, BCM43xx_MMIO_DMA1_BASE, ++ BCM43xx_RXRING_SLOTS, 0); ++ if (!ring) ++ goto err_destroy_tx3; ++ dma->rx_ring0 = ring; ++ ++ if (bcm->current_core->rev < 5) { ++ ring = bcm43xx_setup_dmaring(bcm, BCM43xx_MMIO_DMA4_BASE, ++ BCM43xx_RXRING_SLOTS, 0); ++ if (!ring) ++ goto err_destroy_rx0; ++ dma->rx_ring1 = ring; ++ } ++ ++ dprintk(KERN_INFO PFX "DMA initialized\n"); ++ err = 0; ++out: ++ return err; ++ ++err_destroy_rx0: ++ bcm43xx_destroy_dmaring(dma->rx_ring0); ++ dma->rx_ring0 = NULL; ++err_destroy_tx3: ++ bcm43xx_destroy_dmaring(dma->tx_ring3); ++ dma->tx_ring3 = NULL; ++err_destroy_tx2: ++ bcm43xx_destroy_dmaring(dma->tx_ring2); ++ dma->tx_ring2 = NULL; ++err_destroy_tx1: ++ bcm43xx_destroy_dmaring(dma->tx_ring1); ++ dma->tx_ring1 = NULL; ++err_destroy_tx0: ++ bcm43xx_destroy_dmaring(dma->tx_ring0); ++ dma->tx_ring0 = NULL; ++ goto out; ++} ++ ++/* Generate a cookie for the TX header. */ ++static u16 generate_cookie(struct bcm43xx_dmaring *ring, ++ int slot) ++{ ++ u16 cookie = 0x0000; ++ ++ /* Use the upper 4 bits of the cookie as ++ * DMA controller ID and store the slot number ++ * in the lower 12 bits ++ */ ++ switch (ring->mmio_base) { ++ default: ++ assert(0); ++ case BCM43xx_MMIO_DMA1_BASE: ++ break; ++ case BCM43xx_MMIO_DMA2_BASE: ++ cookie = 0x1000; ++ break; ++ case BCM43xx_MMIO_DMA3_BASE: ++ cookie = 0x2000; ++ break; ++ case BCM43xx_MMIO_DMA4_BASE: ++ cookie = 0x3000; ++ break; ++ } ++ assert(((u16)slot & 0xF000) == 0x0000); ++ cookie |= (u16)slot; ++ ++ return cookie; ++} ++ ++/* Inspect a cookie and find out to which controller/slot it belongs. */ ++static ++struct bcm43xx_dmaring * parse_cookie(struct bcm43xx_private *bcm, ++ u16 cookie, int *slot) ++{ ++ struct bcm43xx_dma *dma = bcm43xx_current_dma(bcm); ++ struct bcm43xx_dmaring *ring = NULL; ++ ++ switch (cookie & 0xF000) { ++ case 0x0000: ++ ring = dma->tx_ring0; ++ break; ++ case 0x1000: ++ ring = dma->tx_ring1; ++ break; ++ case 0x2000: ++ ring = dma->tx_ring2; ++ break; ++ case 0x3000: ++ ring = dma->tx_ring3; ++ break; ++ default: ++ assert(0); ++ } ++ *slot = (cookie & 0x0FFF); ++ assert(*slot >= 0 && *slot < ring->nr_slots); ++ ++ return ring; ++} ++ ++static void dmacontroller_poke_tx(struct bcm43xx_dmaring *ring, ++ int slot) ++{ ++ /* Everything is ready to start. Buffers are DMA mapped and ++ * associated with slots. ++ * "slot" is the last slot of the new frame we want to transmit. ++ * Close your seat belts now, please. ++ */ ++ wmb(); ++ slot = next_slot(ring, slot); ++ bcm43xx_dma_write(ring, BCM43xx_DMA_TX_DESC_INDEX, ++ (u32)(slot * sizeof(struct bcm43xx_dmadesc))); ++} ++ ++static int dma_tx_fragment(struct bcm43xx_dmaring *ring, ++ struct sk_buff *skb, ++ struct ieee80211_tx_control *ctl) ++{ ++ struct sk_buff *hdr_skb; ++ int slot; ++ struct bcm43xx_dmadesc *desc; ++ struct bcm43xx_dmadesc_meta *meta; ++ u32 desc_ctl; ++ u32 desc_addr; ++ ++ assert(skb_shinfo(skb)->nr_frags == 0); ++ ++ hdr_skb = dev_alloc_skb(sizeof(struct bcm43xx_txhdr)); ++ if (unlikely(!hdr_skb)) ++ return -ENOMEM; ++ skb_put(hdr_skb, sizeof(struct bcm43xx_txhdr)); ++ ++ slot = request_slot(ring); ++ desc = ring->vbase + slot; ++ meta = ring->meta + slot; ++ ++ bcm43xx_generate_txhdr(ring->bcm, ++ (struct bcm43xx_txhdr *)hdr_skb->data, ++ skb->data, skb->len, ++ 1,//FIXME ++ generate_cookie(ring, slot), ++ ctl); ++ ++ meta->skb = hdr_skb; ++ meta->dmaaddr = map_descbuffer(ring, hdr_skb->data, hdr_skb->len, 1); ++ if (unlikely(meta->dmaaddr + hdr_skb->len > BCM43xx_DMA_BUSADDRMAX)) { ++ return_slot(ring, slot); ++ dev_kfree_skb_irq(hdr_skb); ++ printk(KERN_ERR PFX ">>>FATAL ERROR<<< DMA TX SKB >1G " ++ "(0x%08x, len: %u)\n", ++ meta->dmaaddr, hdr_skb->len); ++ return -ENOMEM; ++ } ++ ++ desc_addr = (u32)(meta->dmaaddr + ring->memoffset); ++ desc_ctl = BCM43xx_DMADTOR_FRAMESTART | ++ (BCM43xx_DMADTOR_BYTECNT_MASK & (u32)(hdr_skb->len)); ++ if (slot == ring->nr_slots - 1) ++ desc_ctl |= BCM43xx_DMADTOR_DTABLEEND; ++ set_desc_ctl(desc, desc_ctl); ++ set_desc_addr(desc, desc_addr); ++ ++ slot = request_slot(ring); ++ desc = ring->vbase + slot; ++ meta = ring->meta + slot; ++ ++ /* We inspect the txstatus on the FRAMESTART descriptor later ++ * on xmit-status IRQ. ++ */ ++ meta->must_xmit_txstat = 1; ++ memset(&meta->txstat, 0, sizeof(meta->txstat)); ++ memcpy(&meta->txstat.control, ctl, sizeof(*ctl)); ++ ++ meta->skb = skb; ++ meta->dmaaddr = map_descbuffer(ring, skb->data, skb->len, 1); ++ if (unlikely(meta->dmaaddr + skb->len > BCM43xx_DMA_BUSADDRMAX)) { ++ return_slot(ring, prev_slot(ring, slot)); ++ return_slot(ring, slot); ++ dev_kfree_skb_irq(hdr_skb); ++ printk(KERN_ERR PFX ">>>FATAL ERROR<<< DMA TX SKB >1G " ++ "(0x%08x, len: %u)\n", ++ meta->dmaaddr, skb->len); ++ return -ENOMEM; ++ } ++ ++ desc_addr = (u32)(meta->dmaaddr + ring->memoffset); ++ desc_ctl = (BCM43xx_DMADTOR_BYTECNT_MASK & (u32)(skb->len)); ++ if (slot == ring->nr_slots - 1) ++ desc_ctl |= BCM43xx_DMADTOR_DTABLEEND; ++ ++ desc_ctl |= BCM43xx_DMADTOR_FRAMEEND | BCM43xx_DMADTOR_COMPIRQ; ++ set_desc_ctl(desc, desc_ctl); ++ set_desc_addr(desc, desc_addr); ++ /* Now transfer the whole frame. */ ++ dmacontroller_poke_tx(ring, slot); ++ ++ return 0; ++} ++ ++int bcm43xx_dma_tx(struct bcm43xx_private *bcm, ++ struct sk_buff *skb, ++ struct ieee80211_tx_control *ctl) ++{ ++ struct bcm43xx_dmaring *ring = bcm43xx_current_dma(bcm)->tx_ring1; ++ int err; ++ ++ assert(ring->tx); ++ ++#define SLOTS_PER_PACKET 2 ++ if (unlikely(free_slots(ring) < SLOTS_PER_PACKET)) { ++ /* This should never trigger, as the ieee80211 stack ++ * recognizes if the device queue is full and does ++ * not send data anymore. ++ */ ++ printk(KERN_ERR PFX "DMA queue overflow\n"); ++ return -ENOMEM; ++ } ++ ++ err = dma_tx_fragment(ring, skb, ctl); ++ if (likely(!err)) ++ ring->nr_tx_packets++; ++ ++ return err; ++} ++ ++void bcm43xx_dma_handle_xmitstatus(struct bcm43xx_private *bcm, ++ struct bcm43xx_xmitstatus *status) ++{ ++ struct bcm43xx_dmaring *ring; ++ struct bcm43xx_dmadesc *desc; ++ struct bcm43xx_dmadesc_meta *meta; ++ int is_last_fragment; ++ int slot; ++ ++ ring = parse_cookie(bcm, status->cookie, &slot); ++ assert(ring); ++ assert(ring->tx); ++ assert(get_desc_ctl(ring->vbase + slot) & BCM43xx_DMADTOR_FRAMESTART); ++ while (1) { ++ assert(slot >= 0 && slot < ring->nr_slots); ++ desc = ring->vbase + slot; ++ meta = ring->meta + slot; ++ ++ is_last_fragment = !!(get_desc_ctl(desc) & BCM43xx_DMADTOR_FRAMEEND); ++ unmap_descbuffer(ring, meta->dmaaddr, meta->skb->len, 1); ++ ++ if (meta->must_xmit_txstat) { ++ meta->must_xmit_txstat = 0; ++ /* Call back to inform the ieee80211 subsystem about the ++ * status of the transmission. ++ * Some fields of txstat are already filled in dma_tx(). ++ */ ++ meta->txstat.ack = !!(status->flags & BCM43xx_TXSTAT_FLAG_ACK); ++ meta->txstat.retry_count = status->cnt2 - 1; ++ //FIXME: Fill in more information? ++ ieee80211_tx_status_irqsafe(bcm->net_dev, meta->skb, &(meta->txstat)); ++ meta->skb = NULL; ++ } else ++ free_descriptor_buffer(ring, desc, meta, 1); ++ /* Everything belonging to the slot is unmapped ++ * and freed, so we can return it. ++ */ ++ return_slot(ring, slot); ++ ++ if (is_last_fragment) ++ break; ++ slot = next_slot(ring, slot); ++ } ++ bcm->stats.last_tx = jiffies; ++} ++ ++void bcm43xx_dma_get_tx_stats(struct bcm43xx_private *bcm, ++ struct ieee80211_tx_queue_stats *stats) ++{ ++ struct bcm43xx_dma *dma = bcm43xx_current_dma(bcm); ++ struct bcm43xx_dmaring *ring; ++ struct ieee80211_tx_queue_stats_data *data; ++ ++ ring = dma->tx_ring1; ++ data = &(stats->data[0]); ++ data->len = ring->used_slots / SLOTS_PER_PACKET; ++ data->limit = ring->nr_slots / SLOTS_PER_PACKET; ++ data->count = ring->nr_tx_packets; ++} ++ ++static void dma_rx(struct bcm43xx_dmaring *ring, ++ int *slot) ++{ ++ struct bcm43xx_dmadesc *desc; ++ struct bcm43xx_dmadesc_meta *meta; ++ struct bcm43xx_rxhdr *rxhdr; ++ struct sk_buff *skb; ++ u16 len; ++ int err; ++ dma_addr_t dmaaddr; ++ ++ desc = ring->vbase + *slot; ++ meta = ring->meta + *slot; ++ ++ sync_descbuffer_for_cpu(ring, meta->dmaaddr, ring->rx_buffersize); ++ skb = meta->skb; ++ ++ if (ring->mmio_base == BCM43xx_MMIO_DMA4_BASE) { ++ /* We received an xmit status. */ ++ struct bcm43xx_hwxmitstatus *hw = (struct bcm43xx_hwxmitstatus *)skb->data; ++ struct bcm43xx_xmitstatus stat; ++ ++ stat.cookie = le16_to_cpu(hw->cookie); ++ stat.flags = hw->flags; ++ stat.cnt1 = hw->cnt1; ++ stat.cnt2 = hw->cnt2; ++ stat.seq = le16_to_cpu(hw->seq); ++ stat.unknown = le16_to_cpu(hw->unknown); ++ ++ bcm43xx_debugfs_log_txstat(ring->bcm, &stat); ++ bcm43xx_dma_handle_xmitstatus(ring->bcm, &stat); ++ /* recycle the descriptor buffer. */ ++ sync_descbuffer_for_device(ring, meta->dmaaddr, ring->rx_buffersize); ++ ++ return; ++ } ++ rxhdr = (struct bcm43xx_rxhdr *)skb->data; ++ len = le16_to_cpu(rxhdr->frame_length); ++ if (len == 0) { ++ int i = 0; ++ ++ do { ++ udelay(2); ++ barrier(); ++ len = le16_to_cpu(rxhdr->frame_length); ++ } while (len == 0 && i++ < 5); ++ if (unlikely(len == 0)) { ++ /* recycle the descriptor buffer. */ ++ sync_descbuffer_for_device(ring, meta->dmaaddr, ++ ring->rx_buffersize); ++ goto drop; ++ } ++ } ++ if (unlikely(len > ring->rx_buffersize)) { ++ /* The data did not fit into one descriptor buffer ++ * and is split over multiple buffers. ++ * This should never happen, as we try to allocate buffers ++ * big enough. So simply ignore this packet. ++ */ ++ int cnt = 0; ++ s32 tmp = len; ++ ++ while (1) { ++ desc = ring->vbase + *slot; ++ meta = ring->meta + *slot; ++ /* recycle the descriptor buffer. */ ++ sync_descbuffer_for_device(ring, meta->dmaaddr, ++ ring->rx_buffersize); ++ *slot = next_slot(ring, *slot); ++ cnt++; ++ tmp -= ring->rx_buffersize; ++ if (tmp <= 0) ++ break; ++ } ++ printkl(KERN_ERR PFX "DMA RX buffer too small " ++ "(len: %u, buffer: %u, nr-dropped: %d)\n", ++ len, ring->rx_buffersize, cnt); ++ goto drop; ++ } ++ ++ dmaaddr = meta->dmaaddr; ++ err = setup_rx_descbuffer(ring, desc, meta, GFP_ATOMIC); ++ if (unlikely(err)) { ++ dprintkl(KERN_ERR PFX "DMA RX: setup_rx_descbuffer() failed\n"); ++ sync_descbuffer_for_device(ring, dmaaddr, ++ ring->rx_buffersize); ++ goto drop; ++ } ++ ++ unmap_descbuffer(ring, dmaaddr, ring->rx_buffersize, 0); ++ skb_put(skb, len + ring->frameoffset); ++ skb_pull(skb, ring->frameoffset); ++ ++ bcm43xx_rx(ring->bcm, skb, rxhdr); ++drop: ++ return; ++} ++ ++void bcm43xx_dma_rx(struct bcm43xx_dmaring *ring) ++{ ++ u32 status; ++ u16 descptr; ++ int slot, current_slot; ++#ifdef CONFIG_BCM43XX_D80211_DEBUG ++ int used_slots = 0; ++#endif ++ ++ assert(!ring->tx); ++ status = bcm43xx_dma_read(ring, BCM43xx_DMA_RX_STATUS); ++ descptr = (status & BCM43xx_DMA_RXSTAT_DPTR_MASK); ++ current_slot = descptr / sizeof(struct bcm43xx_dmadesc); ++ assert(current_slot >= 0 && current_slot < ring->nr_slots); ++ ++ slot = ring->current_slot; ++ for ( ; slot != current_slot; slot = next_slot(ring, slot)) { ++ dma_rx(ring, &slot); ++#ifdef CONFIG_BCM43XX_D80211_DEBUG ++ if (++used_slots > ring->max_used_slots) ++ ring->max_used_slots = used_slots; ++#endif ++ } ++ bcm43xx_dma_write(ring, BCM43xx_DMA_RX_DESC_INDEX, ++ (u32)(slot * sizeof(struct bcm43xx_dmadesc))); ++ ring->current_slot = slot; ++} ++ ++void bcm43xx_dma_tx_suspend(struct bcm43xx_dmaring *ring) ++{ ++ assert(ring->tx); ++ bcm43xx_power_saving_ctl_bits(ring->bcm, -1, 1); ++ bcm43xx_dma_write(ring, BCM43xx_DMA_TX_CONTROL, ++ bcm43xx_dma_read(ring, BCM43xx_DMA_TX_CONTROL) ++ | BCM43xx_DMA_TXCTRL_SUSPEND); ++} ++ ++void bcm43xx_dma_tx_resume(struct bcm43xx_dmaring *ring) ++{ ++ assert(ring->tx); ++ bcm43xx_dma_write(ring, BCM43xx_DMA_TX_CONTROL, ++ bcm43xx_dma_read(ring, BCM43xx_DMA_TX_CONTROL) ++ & ~BCM43xx_DMA_TXCTRL_SUSPEND); ++ bcm43xx_power_saving_ctl_bits(ring->bcm, -1, -1); ++} +diff -Nur linux-2.6.16/drivers/net/wireless/bcm43xx-d80211/bcm43xx_dma.h linux-2.6.16-bcm43xx/drivers/net/wireless/bcm43xx-d80211/bcm43xx_dma.h +--- linux-2.6.16/drivers/net/wireless/bcm43xx-d80211/bcm43xx_dma.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.16-bcm43xx/drivers/net/wireless/bcm43xx-d80211/bcm43xx_dma.h 2006-03-28 22:16:14.000000000 +0200 +@@ -0,0 +1,228 @@ ++#ifndef BCM43xx_DMA_H_ ++#define BCM43xx_DMA_H_ ++ ++#include <linux/list.h> ++#include <linux/spinlock.h> ++#include <linux/workqueue.h> ++#include <linux/linkage.h> ++#include <asm/atomic.h> ++ ++#include "bcm43xx.h" ++ ++ ++/* DMA-Interrupt reasons. */ ++#define BCM43xx_DMAIRQ_FATALMASK ((1 << 10) | (1 << 11) | (1 << 12) \ ++ | (1 << 14) | (1 << 15)) ++#define BCM43xx_DMAIRQ_NONFATALMASK (1 << 13) ++#define BCM43xx_DMAIRQ_RX_DONE (1 << 16) ++ ++/* DMA controller register offsets. (relative to BCM43xx_DMA#_BASE) */ ++#define BCM43xx_DMA_TX_CONTROL 0x00 ++#define BCM43xx_DMA_TX_DESC_RING 0x04 ++#define BCM43xx_DMA_TX_DESC_INDEX 0x08 ++#define BCM43xx_DMA_TX_STATUS 0x0c ++#define BCM43xx_DMA_RX_CONTROL 0x10 ++#define BCM43xx_DMA_RX_DESC_RING 0x14 ++#define BCM43xx_DMA_RX_DESC_INDEX 0x18 ++#define BCM43xx_DMA_RX_STATUS 0x1c ++ ++/* DMA controller channel control word values. */ ++#define BCM43xx_DMA_TXCTRL_ENABLE (1 << 0) ++#define BCM43xx_DMA_TXCTRL_SUSPEND (1 << 1) ++#define BCM43xx_DMA_TXCTRL_LOOPBACK (1 << 2) ++#define BCM43xx_DMA_TXCTRL_FLUSH (1 << 4) ++#define BCM43xx_DMA_RXCTRL_ENABLE (1 << 0) ++#define BCM43xx_DMA_RXCTRL_FRAMEOFF_MASK 0x000000fe ++#define BCM43xx_DMA_RXCTRL_FRAMEOFF_SHIFT 1 ++#define BCM43xx_DMA_RXCTRL_PIO (1 << 8) ++/* DMA controller channel status word values. */ ++#define BCM43xx_DMA_TXSTAT_DPTR_MASK 0x00000fff ++#define BCM43xx_DMA_TXSTAT_STAT_MASK 0x0000f000 ++#define BCM43xx_DMA_TXSTAT_STAT_DISABLED 0x00000000 ++#define BCM43xx_DMA_TXSTAT_STAT_ACTIVE 0x00001000 ++#define BCM43xx_DMA_TXSTAT_STAT_IDLEWAIT 0x00002000 ++#define BCM43xx_DMA_TXSTAT_STAT_STOPPED 0x00003000 ++#define BCM43xx_DMA_TXSTAT_STAT_SUSP 0x00004000 ++#define BCM43xx_DMA_TXSTAT_ERROR_MASK 0x000f0000 ++#define BCM43xx_DMA_TXSTAT_FLUSHED (1 << 20) ++#define BCM43xx_DMA_RXSTAT_DPTR_MASK 0x00000fff ++#define BCM43xx_DMA_RXSTAT_STAT_MASK 0x0000f000 ++#define BCM43xx_DMA_RXSTAT_STAT_DISABLED 0x00000000 ++#define BCM43xx_DMA_RXSTAT_STAT_ACTIVE 0x00001000 ++#define BCM43xx_DMA_RXSTAT_STAT_IDLEWAIT 0x00002000 ++#define BCM43xx_DMA_RXSTAT_STAT_RESERVED 0x00003000 ++#define BCM43xx_DMA_RXSTAT_STAT_ERRORS 0x00004000 ++#define BCM43xx_DMA_RXSTAT_ERROR_MASK 0x000f0000 ++ ++/* DMA descriptor control field values. */ ++#define BCM43xx_DMADTOR_BYTECNT_MASK 0x00001fff ++#define BCM43xx_DMADTOR_DTABLEEND (1 << 28) /* End of descriptor table */ ++#define BCM43xx_DMADTOR_COMPIRQ (1 << 29) /* IRQ on completion request */ ++#define BCM43xx_DMADTOR_FRAMEEND (1 << 30) ++#define BCM43xx_DMADTOR_FRAMESTART (1 << 31) ++ ++/* Misc DMA constants */ ++#define BCM43xx_DMA_RINGMEMSIZE PAGE_SIZE ++#define BCM43xx_DMA_BUSADDRMAX 0x3FFFFFFF ++#define BCM43xx_DMA_DMABUSADDROFFSET (1 << 30) ++#define BCM43xx_DMA1_RX_FRAMEOFFSET 30 ++#define BCM43xx_DMA4_RX_FRAMEOFFSET 0 ++ ++/* DMA engine tuning knobs */ ++#define BCM43xx_TXRING_SLOTS 512 ++#define BCM43xx_RXRING_SLOTS 64 ++#define BCM43xx_DMA1_RXBUFFERSIZE (2304 + 100) ++#define BCM43xx_DMA4_RXBUFFERSIZE 16 ++ ++ ++ ++#ifdef CONFIG_BCM43XX_D80211_DMA ++ ++ ++struct sk_buff; ++struct bcm43xx_private; ++struct bcm43xx_xmitstatus; ++ ++ ++struct bcm43xx_dmadesc { ++ __le32 _control; ++ __le32 _address; ++} __attribute__((__packed__)); ++ ++/* Macros to access the bcm43xx_dmadesc struct */ ++#define get_desc_ctl(desc) le32_to_cpu((desc)->_control) ++#define set_desc_ctl(desc, ctl) do { (desc)->_control = cpu_to_le32(ctl); } while (0) ++#define get_desc_addr(desc) le32_to_cpu((desc)->_address) ++#define set_desc_addr(desc, addr) do { (desc)->_address = cpu_to_le32(addr); } while (0) ++ ++struct bcm43xx_dmadesc_meta { ++ /* The kernel DMA-able buffer. */ ++ struct sk_buff *skb; ++ /* DMA base bus-address of the descriptor buffer. */ ++ dma_addr_t dmaaddr; ++ /* ieee80211 TX status. Only used once per 802.11 frag. */ ++ u8 must_xmit_txstat:1; ++ struct ieee80211_tx_status txstat; ++}; ++ ++struct bcm43xx_dmaring { ++ struct bcm43xx_private *bcm; ++ /* Kernel virtual base address of the ring memory. */ ++ struct bcm43xx_dmadesc *vbase; ++ /* DMA memory offset */ ++ dma_addr_t memoffset; ++ /* (Unadjusted) DMA base bus-address of the ring memory. */ ++ dma_addr_t dmabase; ++ /* Meta data about all descriptors. */ ++ struct bcm43xx_dmadesc_meta *meta; ++ /* Number of descriptor slots in the ring. */ ++ int nr_slots; ++ /* Number of used descriptor slots. */ ++ int used_slots; ++ /* Currently used slot in the ring. */ ++ int current_slot; ++ /* Total number of packets sent. Statistics only. */ ++ unsigned int nr_tx_packets; ++ /* Frameoffset in octets. */ ++ u32 frameoffset; ++ /* Descriptor buffer size. */ ++ u16 rx_buffersize; ++ /* The MMIO base register of the DMA controller, this ++ * ring is posted to. ++ */ ++ u16 mmio_base; ++ u8 tx:1, /* TRUE, if this is a TX ring. */ ++ suspended:1; /* TRUE, if transfers are suspended on this ring. */ ++#ifdef CONFIG_BCM43XX_D80211_DEBUG ++ /* Maximum number of used slots. */ ++ int max_used_slots; ++#endif /* CONFIG_BCM43XX_D80211_DEBUG*/ ++}; ++ ++ ++static inline ++u32 bcm43xx_dma_read(struct bcm43xx_dmaring *ring, ++ u16 offset) ++{ ++ return bcm43xx_read32(ring->bcm, ring->mmio_base + offset); ++} ++ ++static inline ++void bcm43xx_dma_write(struct bcm43xx_dmaring *ring, ++ u16 offset, u32 value) ++{ ++ bcm43xx_write32(ring->bcm, ring->mmio_base + offset, value); ++} ++ ++ ++int bcm43xx_dma_init(struct bcm43xx_private *bcm); ++void bcm43xx_dma_free(struct bcm43xx_private *bcm); ++ ++int bcm43xx_dmacontroller_rx_reset(struct bcm43xx_private *bcm, ++ u16 dmacontroller_mmio_base); ++int bcm43xx_dmacontroller_tx_reset(struct bcm43xx_private *bcm, ++ u16 dmacontroller_mmio_base); ++ ++void bcm43xx_dma_tx_suspend(struct bcm43xx_dmaring *ring); ++void bcm43xx_dma_tx_resume(struct bcm43xx_dmaring *ring); ++ ++void bcm43xx_dma_get_tx_stats(struct bcm43xx_private *bcm, ++ struct ieee80211_tx_queue_stats *stats); ++ ++int bcm43xx_dma_tx(struct bcm43xx_private *bcm, ++ struct sk_buff *skb, ++ struct ieee80211_tx_control *ctl); ++void bcm43xx_dma_handle_xmitstatus(struct bcm43xx_private *bcm, ++ struct bcm43xx_xmitstatus *status); ++ ++void bcm43xx_dma_rx(struct bcm43xx_dmaring *ring); ++ ++ ++#else /* CONFIG_BCM43XX_D80211_DMA */ ++ ++ ++static inline ++int bcm43xx_dma_init(struct bcm43xx_private *bcm) ++{ ++ return 0; ++} ++static inline ++void bcm43xx_dma_free(struct bcm43xx_private *bcm) ++{ ++} ++static inline ++int bcm43xx_dmacontroller_rx_reset(struct bcm43xx_private *bcm, ++ u16 dmacontroller_mmio_base) ++{ ++ return 0; ++} ++static inline ++int bcm43xx_dmacontroller_tx_reset(struct bcm43xx_private *bcm, ++ u16 dmacontroller_mmio_base) ++{ ++ return 0; ++} ++static inline ++void bcm43xx_dma_get_tx_stats(struct bcm43xx_private *bcm, ++ struct ieee80211_tx_queue_stats *stats) ++{ ++} ++static inline ++int bcm43xx_dma_tx(struct bcm43xx_private *bcm, ++ struct sk_buff *skb, ++ struct ieee80211_tx_control *ctl) ++{ ++ return 0; ++} ++static inline ++void bcm43xx_dma_handle_xmitstatus(struct bcm43xx_private *bcm, ++ struct bcm43xx_xmitstatus *status) ++{ ++} ++static inline ++void bcm43xx_dma_rx(struct bcm43xx_dmaring *ring) ++{ ++} ++ ++#endif /* CONFIG_BCM43XX_D80211_DMA */ ++#endif /* BCM43xx_DMA_H_ */ +diff -Nur linux-2.6.16/drivers/net/wireless/bcm43xx-d80211/bcm43xx_ethtool.c linux-2.6.16-bcm43xx/drivers/net/wireless/bcm43xx-d80211/bcm43xx_ethtool.c +--- linux-2.6.16/drivers/net/wireless/bcm43xx-d80211/bcm43xx_ethtool.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.16-bcm43xx/drivers/net/wireless/bcm43xx-d80211/bcm43xx_ethtool.c 2006-03-28 22:16:14.000000000 +0200 +@@ -0,0 +1,50 @@ ++/* ++ ++ Broadcom BCM43xx wireless driver ++ ++ ethtool support ++ ++ Copyright (c) 2006 Jason Lunz <lunz@falooley.org> ++ ++ Some code in this file is derived from the 8139too.c driver ++ Copyright (C) 2002 Jeff Garzik ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; see the file COPYING. If not, write to ++ the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, ++ Boston, MA 02110-1301, USA. ++ ++*/ ++ ++#include "bcm43xx.h" ++#include "bcm43xx_ethtool.h" ++ ++#include <linux/netdevice.h> ++#include <linux/pci.h> ++#include <linux/string.h> ++#include <linux/version.h> ++ ++ ++static void bcm43xx_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) ++{ ++ struct bcm43xx_private *bcm = bcm43xx_priv(dev); ++ ++ strncpy(info->driver, KBUILD_MODNAME, sizeof(info->driver)); ++ strncpy(info->version, UTS_RELEASE, sizeof(info->version)); ++ strncpy(info->bus_info, pci_name(bcm->pci_dev), ETHTOOL_BUSINFO_LEN); ++} ++ ++struct ethtool_ops bcm43xx_ethtool_ops = { ++ .get_drvinfo = bcm43xx_get_drvinfo, ++ .get_link = ethtool_op_get_link, ++}; +diff -Nur linux-2.6.16/drivers/net/wireless/bcm43xx-d80211/bcm43xx_ethtool.h linux-2.6.16-bcm43xx/drivers/net/wireless/bcm43xx-d80211/bcm43xx_ethtool.h +--- linux-2.6.16/drivers/net/wireless/bcm43xx-d80211/bcm43xx_ethtool.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.16-bcm43xx/drivers/net/wireless/bcm43xx-d80211/bcm43xx_ethtool.h 2006-03-28 22:16:14.000000000 +0200 +@@ -0,0 +1,8 @@ ++#ifndef BCM43xx_ETHTOOL_H_ ++#define BCM43xx_ETHTOOL_H_ ++ ++#include <linux/ethtool.h> ++ ++extern struct ethtool_ops bcm43xx_ethtool_ops; ++ ++#endif /* BCM43xx_ETHTOOL_H_ */ +diff -Nur linux-2.6.16/drivers/net/wireless/bcm43xx-d80211/bcm43xx.h linux-2.6.16-bcm43xx/drivers/net/wireless/bcm43xx-d80211/bcm43xx.h +--- linux-2.6.16/drivers/net/wireless/bcm43xx-d80211/bcm43xx.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.16-bcm43xx/drivers/net/wireless/bcm43xx-d80211/bcm43xx.h 2006-03-28 22:16:14.000000000 +0200 +@@ -0,0 +1,917 @@ ++#ifndef BCM43xx_H_ ++#define BCM43xx_H_ ++ ++#include <linux/version.h> ++#include <linux/kernel.h> ++#include <linux/spinlock.h> ++#include <linux/interrupt.h> ++#include <linux/stringify.h> ++#include <linux/netdevice.h> ++#include <linux/pci.h> ++#include <asm/atomic.h> ++#include <asm/io.h> ++ ++#include <linux/wireless.h> ++#include <net/d80211.h> ++#include <net/d80211_mgmt.h> ++#include <net/d80211_common.h> ++ ++#include "bcm43xx_debugfs.h" ++#include "bcm43xx_leds.h" ++#include "bcm43xx_sysfs.h" ++ ++ ++#define PFX KBUILD_MODNAME ": " ++ ++#define BCM43xx_SWITCH_CORE_MAX_RETRIES 50 ++#define BCM43xx_IRQWAIT_MAX_RETRIES 50 ++ ++#define BCM43xx_IO_SIZE 8192 ++ ++/* Active Core PCI Configuration Register. */ ++#define BCM43xx_PCICFG_ACTIVE_CORE 0x80 ++/* SPROM control register. */ ++#define BCM43xx_PCICFG_SPROMCTL 0x88 ++/* Interrupt Control PCI Configuration Register. (Only on PCI cores with rev >= 6) */ ++#define BCM43xx_PCICFG_ICR 0x94 ++ ++/* MMIO offsets */ ++#define BCM43xx_MMIO_DMA1_REASON 0x20 ++#define BCM43xx_MMIO_DMA1_IRQ_MASK 0x24 ++#define BCM43xx_MMIO_DMA2_REASON 0x28 ++#define BCM43xx_MMIO_DMA2_IRQ_MASK 0x2C ++#define BCM43xx_MMIO_DMA3_REASON 0x30 ++#define BCM43xx_MMIO_DMA3_IRQ_MASK 0x34 ++#define BCM43xx_MMIO_DMA4_REASON 0x38 ++#define BCM43xx_MMIO_DMA4_IRQ_MASK 0x3C ++#define BCM43xx_MMIO_STATUS_BITFIELD 0x120 ++#define BCM43xx_MMIO_STATUS2_BITFIELD 0x124 ++#define BCM43xx_MMIO_GEN_IRQ_REASON 0x128 ++#define BCM43xx_MMIO_GEN_IRQ_MASK 0x12C ++#define BCM43xx_MMIO_RAM_CONTROL 0x130 ++#define BCM43xx_MMIO_RAM_DATA 0x134 ++#define BCM43xx_MMIO_PS_STATUS 0x140 ++#define BCM43xx_MMIO_RADIO_HWENABLED_HI 0x158 ++#define BCM43xx_MMIO_SHM_CONTROL 0x160 ++#define BCM43xx_MMIO_SHM_DATA 0x164 ++#define BCM43xx_MMIO_SHM_DATA_UNALIGNED 0x166 ++#define BCM43xx_MMIO_XMITSTAT_0 0x170 ++#define BCM43xx_MMIO_XMITSTAT_1 0x174 ++#define BCM43xx_MMIO_REV3PLUS_TSF_LOW 0x180 /* core rev >= 3 only */ ++#define BCM43xx_MMIO_REV3PLUS_TSF_HIGH 0x184 /* core rev >= 3 only */ ++#define BCM43xx_MMIO_DMA1_BASE 0x200 ++#define BCM43xx_MMIO_DMA2_BASE 0x220 ++#define BCM43xx_MMIO_DMA3_BASE 0x240 ++#define BCM43xx_MMIO_DMA4_BASE 0x260 ++#define BCM43xx_MMIO_PIO1_BASE 0x300 ++#define BCM43xx_MMIO_PIO2_BASE 0x310 ++#define BCM43xx_MMIO_PIO3_BASE 0x320 ++#define BCM43xx_MMIO_PIO4_BASE 0x330 ++#define BCM43xx_MMIO_PHY_VER 0x3E0 ++#define BCM43xx_MMIO_PHY_RADIO 0x3E2 ++#define BCM43xx_MMIO_ANTENNA 0x3E8 ++#define BCM43xx_MMIO_CHANNEL 0x3F0 ++#define BCM43xx_MMIO_CHANNEL_EXT 0x3F4 ++#define BCM43xx_MMIO_RADIO_CONTROL 0x3F6 ++#define BCM43xx_MMIO_RADIO_DATA_HIGH 0x3F8 ++#define BCM43xx_MMIO_RADIO_DATA_LOW 0x3FA ++#define BCM43xx_MMIO_PHY_CONTROL 0x3FC ++#define BCM43xx_MMIO_PHY_DATA 0x3FE ++#define BCM43xx_MMIO_MACFILTER_CONTROL 0x420 ++#define BCM43xx_MMIO_MACFILTER_DATA 0x422 ++#define BCM43xx_MMIO_RADIO_HWENABLED_LO 0x49A ++#define BCM43xx_MMIO_GPIO_CONTROL 0x49C ++#define BCM43xx_MMIO_GPIO_MASK 0x49E ++#define BCM43xx_MMIO_TSF_0 0x632 /* core rev < 3 only */ ++#define BCM43xx_MMIO_TSF_1 0x634 /* core rev < 3 only */ ++#define BCM43xx_MMIO_TSF_2 0x636 /* core rev < 3 only */ ++#define BCM43xx_MMIO_TSF_3 0x638 /* core rev < 3 only */ ++#define BCM43xx_MMIO_POWERUP_DELAY 0x6A8 ++ ++/* SPROM offsets. */ ++#define BCM43xx_SPROM_BASE 0x1000 ++#define BCM43xx_SPROM_BOARDFLAGS2 0x1c ++#define BCM43xx_SPROM_IL0MACADDR 0x24 ++#define BCM43xx_SPROM_ET0MACADDR 0x27 ++#define BCM43xx_SPROM_ET1MACADDR 0x2a ++#define BCM43xx_SPROM_ETHPHY 0x2d ++#define BCM43xx_SPROM_BOARDREV 0x2e ++#define BCM43xx_SPROM_PA0B0 0x2f ++#define BCM43xx_SPROM_PA0B1 0x30 ++#define BCM43xx_SPROM_PA0B2 0x31 ++#define BCM43xx_SPROM_WL0GPIO0 0x32 ++#define BCM43xx_SPROM_WL0GPIO2 0x33 ++#define BCM43xx_SPROM_MAXPWR 0x34 ++#define BCM43xx_SPROM_PA1B0 0x35 ++#define BCM43xx_SPROM_PA1B1 0x36 ++#define BCM43xx_SPROM_PA1B2 0x37 ++#define BCM43xx_SPROM_IDL_TSSI_TGT 0x38 ++#define BCM43xx_SPROM_BOARDFLAGS 0x39 ++#define BCM43xx_SPROM_ANTENNA_GAIN 0x3a ++#define BCM43xx_SPROM_VERSION 0x3f ++ ++/* BCM43xx_SPROM_BOARDFLAGS values */ ++#define BCM43xx_BFL_BTCOEXIST 0x0001 /* implements Bluetooth coexistance */ ++#define BCM43xx_BFL_PACTRL 0x0002 /* GPIO 9 controlling the PA */ ++#define BCM43xx_BFL_AIRLINEMODE 0x0004 /* implements GPIO 13 radio disable indication */ ++#define BCM43xx_BFL_RSSI 0x0008 /* software calculates nrssi slope. */ ++#define BCM43xx_BFL_ENETSPI 0x0010 /* has ephy roboswitch spi */ ++#define BCM43xx_BFL_XTAL_NOSLOW 0x0020 /* no slow clock available */ ++#define BCM43xx_BFL_CCKHIPWR 0x0040 /* can do high power CCK transmission */ ++#define BCM43xx_BFL_ENETADM 0x0080 /* has ADMtek switch */ ++#define BCM43xx_BFL_ENETVLAN 0x0100 /* can do vlan */ ++#define BCM43xx_BFL_AFTERBURNER 0x0200 /* supports Afterburner mode */ ++#define BCM43xx_BFL_NOPCI 0x0400 /* leaves PCI floating */ ++#define BCM43xx_BFL_FEM 0x0800 /* supports the Front End Module */ ++#define BCM43xx_BFL_EXTLNA 0x1000 /* has an external LNA */ ++#define BCM43xx_BFL_HGPA 0x2000 /* had high gain PA */ ++#define BCM43xx_BFL_BTCMOD 0x4000 /* BFL_BTCOEXIST is given in alternate GPIOs */ ++#define BCM43xx_BFL_ALTIQ 0x8000 /* alternate I/Q settings */ ++ ++/* GPIO register offset, in both ChipCommon and PCI core. */ ++#define BCM43xx_GPIO_CONTROL 0x6c ++ ++/* SHM Routing */ ++#define BCM43xx_SHM_SHARED 0x0001 ++#define BCM43xx_SHM_WIRELESS 0x0002 ++#define BCM43xx_SHM_PCM 0x0003 ++#define BCM43xx_SHM_HWMAC 0x0004 ++#define BCM43xx_SHM_UCODE 0x0300 ++ ++/* MacFilter offsets. */ ++#define BCM43xx_MACFILTER_SELF 0x0000 ++#define BCM43xx_MACFILTER_ASSOC 0x0003 ++ ++/* Chipcommon registers. */ ++#define BCM43xx_CHIPCOMMON_CAPABILITIES 0x04 ++#define BCM43xx_CHIPCOMMON_PLLONDELAY 0xB0 ++#define BCM43xx_CHIPCOMMON_FREFSELDELAY 0xB4 ++#define BCM43xx_CHIPCOMMON_SLOWCLKCTL 0xB8 ++#define BCM43xx_CHIPCOMMON_SYSCLKCTL 0xC0 ++ ++/* PCI core specific registers. */ ++#define BCM43xx_PCICORE_BCAST_ADDR 0x50 ++#define BCM43xx_PCICORE_BCAST_DATA 0x54 ++#define BCM43xx_PCICORE_SBTOPCI2 0x108 ++ ++/* SBTOPCI2 values. */ ++#define BCM43xx_SBTOPCI2_PREFETCH 0x4 ++#define BCM43xx_SBTOPCI2_BURST 0x8 ++ ++/* Chipcommon capabilities. */ ++#define BCM43xx_CAPABILITIES_PCTL 0x00040000 ++#define BCM43xx_CAPABILITIES_PLLMASK 0x00030000 ++#define BCM43xx_CAPABILITIES_PLLSHIFT 16 ++#define BCM43xx_CAPABILITIES_FLASHMASK 0x00000700 ++#define BCM43xx_CAPABILITIES_FLASHSHIFT 8 ++#define BCM43xx_CAPABILITIES_EXTBUSPRESENT 0x00000040 ++#define BCM43xx_CAPABILITIES_UARTGPIO 0x00000020 ++#define BCM43xx_CAPABILITIES_UARTCLOCKMASK 0x00000018 ++#define BCM43xx_CAPABILITIES_UARTCLOCKSHIFT 3 ++#define BCM43xx_CAPABILITIES_MIPSBIGENDIAN 0x00000004 ++#define BCM43xx_CAPABILITIES_NRUARTSMASK 0x00000003 ++ ++/* PowerControl */ ++#define BCM43xx_PCTL_IN 0xB0 ++#define BCM43xx_PCTL_OUT 0xB4 ++#define BCM43xx_PCTL_OUTENABLE 0xB8 ++#define BCM43xx_PCTL_XTAL_POWERUP 0x40 ++#define BCM43xx_PCTL_PLL_POWERDOWN 0x80 ++ ++/* PowerControl Clock Modes */ ++#define BCM43xx_PCTL_CLK_FAST 0x00 ++#define BCM43xx_PCTL_CLK_SLOW 0x01 ++#define BCM43xx_PCTL_CLK_DYNAMIC 0x02 ++ ++#define BCM43xx_PCTL_FORCE_SLOW 0x0800 ++#define BCM43xx_PCTL_FORCE_PLL 0x1000 ++#define BCM43xx_PCTL_DYN_XTAL 0x2000 ++ ++/* COREIDs */ ++#define BCM43xx_COREID_CHIPCOMMON 0x800 ++#define BCM43xx_COREID_ILINE20 0x801 ++#define BCM43xx_COREID_SDRAM 0x803 ++#define BCM43xx_COREID_PCI 0x804 ++#define BCM43xx_COREID_MIPS 0x805 ++#define BCM43xx_COREID_ETHERNET 0x806 ++#define BCM43xx_COREID_V90 0x807 ++#define BCM43xx_COREID_USB11_HOSTDEV 0x80a ++#define BCM43xx_COREID_IPSEC 0x80b ++#define BCM43xx_COREID_PCMCIA 0x80d ++#define BCM43xx_COREID_EXT_IF 0x80f ++#define BCM43xx_COREID_80211 0x812 ++#define BCM43xx_COREID_MIPS_3302 0x816 ++#define BCM43xx_COREID_USB11_HOST 0x817 ++#define BCM43xx_COREID_USB11_DEV 0x818 ++#define BCM43xx_COREID_USB20_HOST 0x819 ++#define BCM43xx_COREID_USB20_DEV 0x81a ++#define BCM43xx_COREID_SDIO_HOST 0x81b ++ ++/* Core Information Registers */ ++#define BCM43xx_CIR_BASE 0xf00 ++#define BCM43xx_CIR_SBTPSFLAG (BCM43xx_CIR_BASE + 0x18) ++#define BCM43xx_CIR_SBIMSTATE (BCM43xx_CIR_BASE + 0x90) ++#define BCM43xx_CIR_SBINTVEC (BCM43xx_CIR_BASE + 0x94) ++#define BCM43xx_CIR_SBTMSTATELOW (BCM43xx_CIR_BASE + 0x98) ++#define BCM43xx_CIR_SBTMSTATEHIGH (BCM43xx_CIR_BASE + 0x9c) ++#define BCM43xx_CIR_SBIMCONFIGLOW (BCM43xx_CIR_BASE + 0xa8) ++#define BCM43xx_CIR_SB_ID_HI (BCM43xx_CIR_BASE + 0xfc) ++ ++/* Mask to get the Backplane Flag Number from SBTPSFLAG. */ ++#define BCM43xx_BACKPLANE_FLAG_NR_MASK 0x3f ++ ++/* SBIMCONFIGLOW values/masks. */ ++#define BCM43xx_SBIMCONFIGLOW_SERVICE_TOUT_MASK 0x00000007 ++#define BCM43xx_SBIMCONFIGLOW_SERVICE_TOUT_SHIFT 0 ++#define BCM43xx_SBIMCONFIGLOW_REQUEST_TOUT_MASK 0x00000070 ++#define BCM43xx_SBIMCONFIGLOW_REQUEST_TOUT_SHIFT 4 ++#define BCM43xx_SBIMCONFIGLOW_CONNID_MASK 0x00ff0000 ++#define BCM43xx_SBIMCONFIGLOW_CONNID_SHIFT 16 ++ ++/* sbtmstatelow state flags */ ++#define BCM43xx_SBTMSTATELOW_RESET 0x01 ++#define BCM43xx_SBTMSTATELOW_REJECT 0x02 ++#define BCM43xx_SBTMSTATELOW_CLOCK 0x10000 ++#define BCM43xx_SBTMSTATELOW_FORCE_GATE_CLOCK 0x20000 ++ ++/* sbtmstatehigh state flags */ ++#define BCM43xx_SBTMSTATEHIGH_SERROR 0x1 ++#define BCM43xx_SBTMSTATEHIGH_BUSY 0x4 ++ ++/* sbimstate flags */ ++#define BCM43xx_SBIMSTATE_IB_ERROR 0x20000 ++#define BCM43xx_SBIMSTATE_TIMEOUT 0x40000 ++ ++/* PHYVersioning */ ++#define BCM43xx_PHYTYPE_A 0x00 ++#define BCM43xx_PHYTYPE_B 0x01 ++#define BCM43xx_PHYTYPE_G 0x02 ++ ++/* PHYRegisters */ ++#define BCM43xx_PHY_ILT_A_CTRL 0x0072 ++#define BCM43xx_PHY_ILT_A_DATA1 0x0073 ++#define BCM43xx_PHY_ILT_A_DATA2 0x0074 ++#define BCM43xx_PHY_G_LO_CONTROL 0x0810 ++#define BCM43xx_PHY_ILT_G_CTRL 0x0472 ++#define BCM43xx_PHY_ILT_G_DATA1 0x0473 ++#define BCM43xx_PHY_ILT_G_DATA2 0x0474 ++#define BCM43xx_PHY_A_PCTL 0x007B ++#define BCM43xx_PHY_G_PCTL 0x0029 ++#define BCM43xx_PHY_A_CRS 0x0029 ++#define BCM43xx_PHY_RADIO_BITFIELD 0x0401 ++#define BCM43xx_PHY_G_CRS 0x0429 ++#define BCM43xx_PHY_NRSSILT_CTRL 0x0803 ++#define BCM43xx_PHY_NRSSILT_DATA 0x0804 ++ ++/* RadioRegisters */ ++#define BCM43xx_RADIOCTL_ID 0x01 ++ ++/* StatusBitField */ ++#define BCM43xx_SBF_MAC_ENABLED 0x00000001 ++#define BCM43xx_SBF_2 0x00000002 /*FIXME: fix name*/ ++#define BCM43xx_SBF_CORE_READY 0x00000004 ++#define BCM43xx_SBF_400 0x00000400 /*FIXME: fix name*/ ++#define BCM43xx_SBF_4000 0x00004000 /*FIXME: fix name*/ ++#define BCM43xx_SBF_8000 0x00008000 /*FIXME: fix name*/ ++#define BCM43xx_SBF_XFER_REG_BYTESWAP 0x00010000 ++#define BCM43xx_SBF_MODE_NOTADHOC 0x00020000 ++#define BCM43xx_SBF_MODE_AP 0x00040000 ++#define BCM43xx_SBF_RADIOREG_LOCK 0x00080000 ++#define BCM43xx_SBF_MODE_MONITOR 0x00400000 ++#define BCM43xx_SBF_MODE_PROMISC 0x01000000 ++#define BCM43xx_SBF_PS1 0x02000000 ++#define BCM43xx_SBF_PS2 0x04000000 ++#define BCM43xx_SBF_NO_SSID_BCAST 0x08000000 ++#define BCM43xx_SBF_TIME_UPDATE 0x10000000 ++#define BCM43xx_SBF_80000000 0x80000000 /*FIXME: fix name*/ ++ ++/* MicrocodeFlagsBitfield (addr + lo-word values?)*/ ++#define BCM43xx_UCODEFLAGS_OFFSET 0x005E ++ ++#define BCM43xx_UCODEFLAG_AUTODIV 0x0001 ++#define BCM43xx_UCODEFLAG_UNKBGPHY 0x0002 ++#define BCM43xx_UCODEFLAG_UNKBPHY 0x0004 ++#define BCM43xx_UCODEFLAG_UNKGPHY 0x0020 ++#define BCM43xx_UCODEFLAG_UNKPACTRL 0x0040 ++#define BCM43xx_UCODEFLAG_JAPAN 0x0080 ++ ++/* Generic-Interrupt reasons. */ ++#define BCM43xx_IRQ_READY (1 << 0) ++#define BCM43xx_IRQ_BEACON (1 << 1) ++#define BCM43xx_IRQ_PS (1 << 2) ++#define BCM43xx_IRQ_REG124 (1 << 5) ++#define BCM43xx_IRQ_PMQ (1 << 6) ++#define BCM43xx_IRQ_PIO_WORKAROUND (1 << 8) ++#define BCM43xx_IRQ_XMIT_ERROR (1 << 11) ++#define BCM43xx_IRQ_RX (1 << 15) ++#define BCM43xx_IRQ_SCAN (1 << 16) ++#define BCM43xx_IRQ_NOISE (1 << 18) ++#define BCM43xx_IRQ_XMIT_STATUS (1 << 29) ++ ++#define BCM43xx_IRQ_ALL 0xffffffff ++#define BCM43xx_IRQ_INITIAL (BCM43xx_IRQ_PS | \ ++ BCM43xx_IRQ_REG124 | \ ++ BCM43xx_IRQ_PMQ | \ ++ BCM43xx_IRQ_XMIT_ERROR | \ ++ BCM43xx_IRQ_RX | \ ++ BCM43xx_IRQ_SCAN | \ ++ BCM43xx_IRQ_NOISE | \ ++ BCM43xx_IRQ_XMIT_STATUS) ++ ++/* Bus type PCI. */ ++#define BCM43xx_BUSTYPE_PCI 0 ++/* Bus type Silicone Backplane Bus. */ ++#define BCM43xx_BUSTYPE_SB 1 ++/* Bus type PCMCIA. */ ++#define BCM43xx_BUSTYPE_PCMCIA 2 ++ ++/* Rate values. */ ++#define BCM43xx_CCK_RATE_1MB 0x02 ++#define BCM43xx_CCK_RATE_2MB 0x04 ++#define BCM43xx_CCK_RATE_5MB 0x0B ++#define BCM43xx_CCK_RATE_11MB 0x16 ++#define BCM43xx_OFDM_RATE_6MB 0x0C ++#define BCM43xx_OFDM_RATE_9MB 0x12 ++#define BCM43xx_OFDM_RATE_12MB 0x18 ++#define BCM43xx_OFDM_RATE_18MB 0x24 ++#define BCM43xx_OFDM_RATE_24MB 0x30 ++#define BCM43xx_OFDM_RATE_36MB 0x48 ++#define BCM43xx_OFDM_RATE_48MB 0x60 ++#define BCM43xx_OFDM_RATE_54MB 0x6C ++ ++#define BCM43xx_DEFAULT_SHORT_RETRY_LIMIT 7 ++#define BCM43xx_DEFAULT_LONG_RETRY_LIMIT 4 ++ ++/* Max size of a security key */ ++#define BCM43xx_SEC_KEYSIZE 16 ++/* Security algorithms. */ ++enum { ++ BCM43xx_SEC_ALGO_NONE = 0, /* unencrypted, as of TX header. */ ++ BCM43xx_SEC_ALGO_WEP, ++ BCM43xx_SEC_ALGO_UNKNOWN, ++ BCM43xx_SEC_ALGO_AES, ++ BCM43xx_SEC_ALGO_WEP104, ++ BCM43xx_SEC_ALGO_TKIP, ++}; ++ ++ ++#ifdef assert ++# undef assert ++#endif ++#ifdef CONFIG_BCM43XX_D80211_DEBUG ++#define assert(expr) \ ++ do { \ ++ if (unlikely(!(expr))) { \ ++ printk(KERN_ERR PFX "ASSERTION FAILED (%s) at: %s:%d:%s()\n", \ ++ #expr, __FILE__, __LINE__, __FUNCTION__); \ ++ } \ ++ } while (0) ++#else ++#define assert(expr) do { /* nothing */ } while (0) ++#endif ++ ++/* rate limited printk(). */ ++#ifdef printkl ++# undef printkl ++#endif ++#define printkl(f, x...) do { if (printk_ratelimit()) printk(f ,##x); } while (0) ++/* rate limited printk() for debugging */ ++#ifdef dprintkl ++# undef dprintkl ++#endif ++#ifdef CONFIG_BCM43XX_D80211_DEBUG ++# define dprintkl printkl ++#else ++# define dprintkl(f, x...) do { /* nothing */ } while (0) ++#endif ++ ++/* debugging printk() */ ++#ifdef dprintk ++# undef dprintk ++#endif ++#ifdef CONFIG_BCM43XX_D80211_DEBUG ++# define dprintk(f, x...) do { printk(f ,##x); } while (0) ++#else ++# define dprintk(f, x...) do { /* nothing */ } while (0) ++#endif ++ ++ ++struct net_device; ++struct pci_dev; ++struct bcm43xx_dmaring; ++struct bcm43xx_pioqueue; ++ ++struct bcm43xx_initval { ++ u16 offset; ++ u16 size; ++ u32 value; ++} __attribute__((__packed__)); ++ ++/* Values for bcm430x_sprominfo.locale */ ++enum { ++ BCM43xx_LOCALE_WORLD = 0, ++ BCM43xx_LOCALE_THAILAND, ++ BCM43xx_LOCALE_ISRAEL, ++ BCM43xx_LOCALE_JORDAN, ++ BCM43xx_LOCALE_CHINA, ++ BCM43xx_LOCALE_JAPAN, ++ BCM43xx_LOCALE_USA_CANADA_ANZ, ++ BCM43xx_LOCALE_EUROPE, ++ BCM43xx_LOCALE_USA_LOW, ++ BCM43xx_LOCALE_JAPAN_HIGH, ++ BCM43xx_LOCALE_ALL, ++ BCM43xx_LOCALE_NONE, ++}; ++ ++#define BCM43xx_SPROM_SIZE 64 /* in 16-bit words. */ ++struct bcm43xx_sprominfo { ++ u16 boardflags2; ++ u8 il0macaddr[6]; ++ u8 et0macaddr[6]; ++ u8 et1macaddr[6]; ++ u8 et0phyaddr:5; ++ u8 et1phyaddr:5; ++ u8 et0mdcport:1; ++ u8 et1mdcport:1; ++ u8 boardrev; ++ u8 locale:4; ++ u8 antennas_aphy:2; ++ u8 antennas_bgphy:2; ++ u16 pa0b0; ++ u16 pa0b1; ++ u16 pa0b2; ++ u8 wl0gpio0; ++ u8 wl0gpio1; ++ u8 wl0gpio2; ++ u8 wl0gpio3; ++ u8 maxpower_aphy; ++ u8 maxpower_bgphy; ++ u16 pa1b0; ++ u16 pa1b1; ++ u16 pa1b2; ++ u8 idle_tssi_tgt_aphy; ++ u8 idle_tssi_tgt_bgphy; ++ u16 boardflags; ++ u16 antennagain_aphy; ++ u16 antennagain_bgphy; ++}; ++ ++/* Value pair to measure the LocalOscillator. */ ++struct bcm43xx_lopair { ++ s8 low; ++ s8 high; ++ u8 used:1; ++}; ++#define BCM43xx_LO_COUNT (14*4) ++ ++struct bcm43xx_phyinfo { ++ /* Hardware Data */ ++ u8 version; ++ u8 type; ++ u8 rev; ++ u16 antenna_diversity; ++ u16 savedpctlreg; ++ u16 minlowsig[2]; ++ u16 minlowsigpos[2]; ++ u8 connected:1, ++ calibrated:1, ++ is_locked:1, /* used in bcm43xx_phy_{un}lock() */ ++ dyn_tssi_tbl:1; /* used in bcm43xx_phy_init_tssi2dbm_table() */ ++ /* LO Measurement Data. ++ * Use bcm43xx_get_lopair() to get a value. ++ */ ++ struct bcm43xx_lopair *_lo_pairs; ++ ++ /* TSSI to dBm table in use */ ++ const s8 *tssi2dbm; ++ /* idle TSSI value */ ++ s8 idle_tssi; ++ ++ /* Values from bcm43xx_calc_loopback_gain() */ ++ u16 loopback_gain[2]; ++ ++ /* PHY lock for core.rev < 3 ++ * This lock is only used by bcm43xx_phy_{un}lock() ++ */ ++ spinlock_t lock; ++}; ++ ++ ++struct bcm43xx_radioinfo { ++ u16 manufact; ++ u16 version; ++ u8 revision; ++ ++ /* Desired TX power level (in dBm). ++ * This is set by the user and adjusted in bcm43xx_phy_xmitpower(). */ ++ u8 power_level; ++ /* TX Power control values. */ ++ union { ++ /* B/G PHY */ ++ struct { ++ u16 baseband_atten; ++ u16 radio_atten; ++ u16 txctl1; ++ u16 txctl2; ++ }; ++ /* A PHY */ ++ struct { ++ u16 txpwr_offset; ++ }; ++ }; ++ ++ /* Current Interference Mitigation mode */ ++ int interfmode; ++ /* Stack of saved values from the Interference Mitigation code. ++ * Each value in the stack is layed out as follows: ++ * bit 0-11: offset ++ * bit 12-15: register ID ++ * bit 16-32: value ++ * register ID is: 0x1 PHY, 0x2 Radio, 0x3 ILT ++ */ ++#define BCM43xx_INTERFSTACK_SIZE 26 ++ u32 interfstack[BCM43xx_INTERFSTACK_SIZE]; ++ ++ /* Saved values from the NRSSI Slope calculation */ ++ s16 nrssi[2]; ++ s32 nrssislope; ++ /* In memory nrssi lookup table. */ ++ s8 nrssi_lt[64]; ++ ++ /* current channel */ ++ u8 channel; ++ u8 initial_channel; ++ ++ u16 lofcal; ++ ++ u16 initval; ++ ++ u8 enabled:1; ++ /* ACI (adjacent channel interference) flags. */ ++ u8 aci_enable:1, ++ aci_wlan_automatic:1, ++ aci_hw_rssi:1; ++}; ++ ++/* Data structures for DMA transmission, per 80211 core. */ ++struct bcm43xx_dma { ++ struct bcm43xx_dmaring *tx_ring0; ++ struct bcm43xx_dmaring *tx_ring1; ++ struct bcm43xx_dmaring *tx_ring2; ++ struct bcm43xx_dmaring *tx_ring3; ++ struct bcm43xx_dmaring *rx_ring0; ++ struct bcm43xx_dmaring *rx_ring1; /* only available on core.rev < 5 */ ++}; ++ ++/* Data structures for PIO transmission, per 80211 core. */ ++struct bcm43xx_pio { ++ struct bcm43xx_pioqueue *queue0; ++ struct bcm43xx_pioqueue *queue1; ++ struct bcm43xx_pioqueue *queue2; ++ struct bcm43xx_pioqueue *queue3; ++}; ++ ++#define BCM43xx_MAX_80211_CORES 2 ++ ++#ifdef CONFIG_BCM947XX ++#define core_offset(bcm) (bcm)->current_core_offset ++#else ++#define core_offset(bcm) 0 ++#endif ++ ++/* Generic information about a core. */ ++struct bcm43xx_coreinfo { ++ u8 available:1, ++ enabled:1, ++ initialized:1; ++ /** core_id ID number */ ++ u16 id; ++ /** core_rev revision number */ ++ u8 rev; ++ /** Index number for _switch_core() */ ++ u8 index; ++}; ++ ++/* Additional information for each 80211 core. */ ++struct bcm43xx_coreinfo_80211 { ++ /* PHY device. */ ++ struct bcm43xx_phyinfo phy; ++ /* Radio device. */ ++ struct bcm43xx_radioinfo radio; ++ union { ++ /* DMA context. */ ++ struct bcm43xx_dma dma; ++ /* PIO context. */ ++ struct bcm43xx_pio pio; ++ }; ++}; ++ ++/* Context information for a noise calculation (Link Quality). */ ++struct bcm43xx_noise_calculation { ++ struct bcm43xx_coreinfo *core_at_start; ++ u8 channel_at_start; ++ u8 calculation_running:1; ++ u8 nr_samples; ++ s8 samples[8][4]; ++}; ++ ++struct bcm43xx_stats { ++ u8 link_quality; ++ /* Store the last TX/RX times here for updating the leds. */ ++ unsigned long last_tx; ++ unsigned long last_rx; ++}; ++ ++struct bcm43xx_key { ++ u8 enabled:1; ++ u8 algorithm; ++}; ++ ++struct bcm43xx_private { ++ struct bcm43xx_sysfs sysfs; ++ ++ struct ieee80211_hw *ieee; ++ struct ieee80211_low_level_stats ieee_stats; ++ int iw_mode; ++ ++ struct net_device *net_dev; ++ struct pci_dev *pci_dev; ++ unsigned int irq; ++ ++ void __iomem *mmio_addr; ++ unsigned int mmio_len; ++ ++ /* Do not use the lock directly. Use the bcm43xx_lock* helper ++ * functions, to be MMIO-safe. */ ++ spinlock_t _lock; ++ ++ /* Driver status flags. */ ++ u32 initialized:1, /* init_board() succeed */ ++ was_initialized:1, /* for PCI suspend/resume. */ ++ shutting_down:1, /* free_board() in progress */ ++ __using_pio:1, /* Internal, use bcm43xx_using_pio(). */ ++ bad_frames_preempt:1, /* Use "Bad Frames Preemption" (default off) */ ++ reg124_set_0x4:1, /* Some variable to keep track of IRQ stuff. */ ++ powersaving:1, /* TRUE if we are in PowerSaving mode. FALSE otherwise. */ ++ short_preamble:1, /* TRUE, if short preamble is enabled. */ ++ short_slot:1, /* TRUE, if short slot timing is enabled. */ ++ firmware_norelease:1; /* Do not release the firmware. Used on suspend. */ ++ ++ struct bcm43xx_stats stats; ++ ++ /* Bus type we are connected to. ++ * This is currently always BCM43xx_BUSTYPE_PCI ++ */ ++ u8 bustype; ++ ++ u16 board_vendor; ++ u16 board_type; ++ u16 board_revision; ++ ++ u16 chip_id; ++ u8 chip_rev; ++ u8 chip_package; ++ ++ struct bcm43xx_sprominfo sprom; ++#define BCM43xx_NR_LEDS 4 ++ struct bcm43xx_led leds[BCM43xx_NR_LEDS]; ++ ++ /* The currently active core. */ ++ struct bcm43xx_coreinfo *current_core; ++#ifdef CONFIG_BCM947XX ++ /** current core memory offset */ ++ u32 current_core_offset; ++#endif ++ struct bcm43xx_coreinfo *active_80211_core; ++ /* coreinfo structs for all possible cores follow. ++ * Note that a core might not exist. ++ * So check the coreinfo flags before using it. ++ */ ++ struct bcm43xx_coreinfo core_chipcommon; ++ struct bcm43xx_coreinfo core_pci; ++ struct bcm43xx_coreinfo core_80211[ BCM43xx_MAX_80211_CORES ]; ++ /* Additional information, specific to the 80211 cores. */ ++ struct bcm43xx_coreinfo_80211 core_80211_ext[ BCM43xx_MAX_80211_CORES ]; ++ /* Index of the current 80211 core. If current_core is not ++ * an 80211 core, this is -1. ++ */ ++ int current_80211_core_idx; ++ /* Number of available 80211 cores. */ ++ int nr_80211_available; ++ ++ u32 chipcommon_capabilities; ++ ++ /* Reason code of the last interrupt. */ ++ u32 irq_reason; ++ u32 dma_reason[4]; ++ /* saved irq enable/disable state bitfield. */ ++ u32 irq_savedstate; ++ /* Link Quality calculation context. */ ++ struct bcm43xx_noise_calculation noisecalc; ++ ++ /* Interrupt Service Routine tasklet (bottom-half) */ ++ struct tasklet_struct isr_tasklet; ++ ++ /* Periodic tasks */ ++ struct timer_list periodic_tasks; ++ unsigned int periodic_state; ++ ++ struct work_struct restart_work; ++ ++ /* Informational stuff. */ ++ char nick[IW_ESSID_MAX_SIZE + 1]; ++ u8 bssid[ETH_ALEN]; ++ ++ /* encryption/decryption */ ++ u16 security_offset; ++ struct bcm43xx_key key[54]; ++ u8 default_key_idx; ++ ++ /* Firmware. */ ++ const struct firmware *ucode; ++ const struct firmware *pcm; ++ const struct firmware *initvals0; ++ const struct firmware *initvals1; ++ ++ /* Cached beacon template while uploading the template. */ ++ struct sk_buff *cached_beacon; ++ ++ /* Debugging stuff follows. */ ++#ifdef CONFIG_BCM43XX_D80211_DEBUG ++ struct bcm43xx_dfsentry *dfsentry; ++#endif ++}; ++ ++/* bcm43xx_(un)lock() protect struct bcm43xx_private. ++ * Note that _NO_ MMIO writes are allowed. If you want to ++ * write to the device through MMIO in the critical section, use ++ * the *_mmio lock functions. ++ * MMIO read-access is allowed, though. ++ */ ++#define bcm43xx_lock(bcm, flags) spin_lock_irqsave(&(bcm)->_lock, flags) ++#define bcm43xx_unlock(bcm, flags) spin_unlock_irqrestore(&(bcm)->_lock, flags) ++/* bcm43xx_(un)lock_mmio() protect struct bcm43xx_private and MMIO. ++ * MMIO write-access to the device is allowed. ++ * All MMIO writes are flushed on unlock, so it is guaranteed to not ++ * interfere with other threads writing MMIO registers. ++ */ ++#define bcm43xx_lock_mmio(bcm, flags) bcm43xx_lock(bcm, flags) ++#define bcm43xx_unlock_mmio(bcm, flags) do { mmiowb(); bcm43xx_unlock(bcm, flags); } while (0) ++ ++static inline ++struct bcm43xx_private * bcm43xx_priv(struct net_device *dev) ++{ ++ return ieee80211_dev_hw_data(dev); ++} ++ ++/* Helper function, which returns a boolean. ++ * TRUE, if PIO is used; FALSE, if DMA is used. ++ */ ++#if defined(CONFIG_BCM43XX_D80211_DMA) && defined(CONFIG_BCM43XX_D80211_PIO) ++static inline ++int bcm43xx_using_pio(struct bcm43xx_private *bcm) ++{ ++ return bcm->__using_pio; ++} ++#elif defined(CONFIG_BCM43XX_D80211_DMA) ++static inline ++int bcm43xx_using_pio(struct bcm43xx_private *bcm) ++{ ++ return 0; ++} ++#elif defined(CONFIG_BCM43XX_D80211_PIO) ++static inline ++int bcm43xx_using_pio(struct bcm43xx_private *bcm) ++{ ++ return 1; ++} ++#else ++# error "Using neither DMA nor PIO? Confused..." ++#endif ++ ++/* Helper functions to access data structures private to the 80211 cores. ++ * Note that we _must_ have an 80211 core mapped when calling ++ * any of these functions. ++ */ ++static inline ++struct bcm43xx_pio * bcm43xx_current_pio(struct bcm43xx_private *bcm) ++{ ++ assert(bcm43xx_using_pio(bcm)); ++ assert(bcm->current_80211_core_idx >= 0); ++ assert(bcm->current_80211_core_idx < BCM43xx_MAX_80211_CORES); ++ return &(bcm->core_80211_ext[bcm->current_80211_core_idx].pio); ++} ++static inline ++struct bcm43xx_dma * bcm43xx_current_dma(struct bcm43xx_private *bcm) ++{ ++ assert(!bcm43xx_using_pio(bcm)); ++ assert(bcm->current_80211_core_idx >= 0); ++ assert(bcm->current_80211_core_idx < BCM43xx_MAX_80211_CORES); ++ return &(bcm->core_80211_ext[bcm->current_80211_core_idx].dma); ++} ++static inline ++struct bcm43xx_phyinfo * bcm43xx_current_phy(struct bcm43xx_private *bcm) ++{ ++ assert(bcm->current_80211_core_idx >= 0); ++ assert(bcm->current_80211_core_idx < BCM43xx_MAX_80211_CORES); ++ return &(bcm->core_80211_ext[bcm->current_80211_core_idx].phy); ++} ++static inline ++struct bcm43xx_radioinfo * bcm43xx_current_radio(struct bcm43xx_private *bcm) ++{ ++ assert(bcm->current_80211_core_idx >= 0); ++ assert(bcm->current_80211_core_idx < BCM43xx_MAX_80211_CORES); ++ return &(bcm->core_80211_ext[bcm->current_80211_core_idx].radio); ++} ++ ++/* Are we running in init_board() context? */ ++static inline ++int bcm43xx_is_initializing(struct bcm43xx_private *bcm) ++{ ++ if (bcm->initialized) ++ return 0; ++ if (bcm->shutting_down) ++ return 0; ++ return 1; ++} ++ ++static inline ++struct bcm43xx_lopair * bcm43xx_get_lopair(struct bcm43xx_phyinfo *phy, ++ u16 radio_attenuation, ++ u16 baseband_attenuation) ++{ ++ return phy->_lo_pairs + (radio_attenuation + 14 * (baseband_attenuation / 2)); ++} ++ ++ ++static inline ++u16 bcm43xx_read16(struct bcm43xx_private *bcm, u16 offset) ++{ ++ return ioread16(bcm->mmio_addr + core_offset(bcm) + offset); ++} ++ ++static inline ++void bcm43xx_write16(struct bcm43xx_private *bcm, u16 offset, u16 value) ++{ ++ iowrite16(value, bcm->mmio_addr + core_offset(bcm) + offset); ++} ++ ++static inline ++u32 bcm43xx_read32(struct bcm43xx_private *bcm, u16 offset) ++{ ++ return ioread32(bcm->mmio_addr + core_offset(bcm) + offset); ++} ++ ++static inline ++void bcm43xx_write32(struct bcm43xx_private *bcm, u16 offset, u32 value) ++{ ++ iowrite32(value, bcm->mmio_addr + core_offset(bcm) + offset); ++} ++ ++static inline ++int bcm43xx_pci_read_config16(struct bcm43xx_private *bcm, int offset, u16 *value) ++{ ++ return pci_read_config_word(bcm->pci_dev, offset, value); ++} ++ ++static inline ++int bcm43xx_pci_read_config32(struct bcm43xx_private *bcm, int offset, u32 *value) ++{ ++ return pci_read_config_dword(bcm->pci_dev, offset, value); ++} ++ ++static inline ++int bcm43xx_pci_write_config16(struct bcm43xx_private *bcm, int offset, u16 value) ++{ ++ return pci_write_config_word(bcm->pci_dev, offset, value); ++} ++ ++static inline ++int bcm43xx_pci_write_config32(struct bcm43xx_private *bcm, int offset, u32 value) ++{ ++ return pci_write_config_dword(bcm->pci_dev, offset, value); ++} ++ ++/** Limit a value between two limits */ ++#ifdef limit_value ++# undef limit_value ++#endif ++#define limit_value(value, min, max) \ ++ ({ \ ++ typeof(value) __value = (value); \ ++ typeof(value) __min = (min); \ ++ typeof(value) __max = (max); \ ++ if (__value < __min) \ ++ __value = __min; \ ++ else if (__value > __max) \ ++ __value = __max; \ ++ __value; \ ++ }) ++ ++/** Helpers to print MAC addresses. */ ++#define BCM43xx_MACFMT "%02x:%02x:%02x:%02x:%02x:%02x" ++#define BCM43xx_MACARG(x) ((u8*)(x))[0], ((u8*)(x))[1], \ ++ ((u8*)(x))[2], ((u8*)(x))[3], \ ++ ((u8*)(x))[4], ((u8*)(x))[5] ++ ++#endif /* BCM43xx_H_ */ +diff -Nur linux-2.6.16/drivers/net/wireless/bcm43xx-d80211/bcm43xx_ilt.c linux-2.6.16-bcm43xx/drivers/net/wireless/bcm43xx-d80211/bcm43xx_ilt.c +--- linux-2.6.16/drivers/net/wireless/bcm43xx-d80211/bcm43xx_ilt.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.16-bcm43xx/drivers/net/wireless/bcm43xx-d80211/bcm43xx_ilt.c 2006-03-28 22:16:14.000000000 +0200 +@@ -0,0 +1,337 @@ ++/* ++ ++ Broadcom BCM43xx wireless driver ++ ++ Copyright (c) 2005 Martin Langer <martin-langer@gmx.de>, ++ Stefano Brivio <st3@riseup.net> ++ Michael Buesch <mbuesch@freenet.de> ++ Danny van Dyk <kugelfang@gentoo.org> ++ Andreas Jaggi <andreas.jaggi@waterwave.ch> ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; see the file COPYING. If not, write to ++ the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, ++ Boston, MA 02110-1301, USA. ++ ++*/ ++ ++#include "bcm43xx.h" ++#include "bcm43xx_ilt.h" ++#include "bcm43xx_phy.h" ++ ++ ++/**** Initial Internal Lookup Tables ****/ ++ ++const u32 bcm43xx_ilt_rotor[BCM43xx_ILT_ROTOR_SIZE] = { ++ 0xFEB93FFD, 0xFEC63FFD, /* 0 */ ++ 0xFED23FFD, 0xFEDF3FFD, ++ 0xFEEC3FFE, 0xFEF83FFE, ++ 0xFF053FFE, 0xFF113FFE, ++ 0xFF1E3FFE, 0xFF2A3FFF, /* 8 */ ++ 0xFF373FFF, 0xFF443FFF, ++ 0xFF503FFF, 0xFF5D3FFF, ++ 0xFF693FFF, 0xFF763FFF, ++ 0xFF824000, 0xFF8F4000, /* 16 */ ++ 0xFF9B4000, 0xFFA84000, ++ 0xFFB54000, 0xFFC14000, ++ 0xFFCE4000, 0xFFDA4000, ++ 0xFFE74000, 0xFFF34000, /* 24 */ ++ 0x00004000, 0x000D4000, ++ 0x00194000, 0x00264000, ++ 0x00324000, 0x003F4000, ++ 0x004B4000, 0x00584000, /* 32 */ ++ 0x00654000, 0x00714000, ++ 0x007E4000, 0x008A3FFF, ++ 0x00973FFF, 0x00A33FFF, ++ 0x00B03FFF, 0x00BC3FFF, /* 40 */ ++ 0x00C93FFF, 0x00D63FFF, ++ 0x00E23FFE, 0x00EF3FFE, ++ 0x00FB3FFE, 0x01083FFE, ++ 0x01143FFE, 0x01213FFD, /* 48 */ ++ 0x012E3FFD, 0x013A3FFD, ++ 0x01473FFD, ++}; ++ ++const u32 bcm43xx_ilt_retard[BCM43xx_ILT_RETARD_SIZE] = { ++ 0xDB93CB87, 0xD666CF64, /* 0 */ ++ 0xD1FDD358, 0xCDA6D826, ++ 0xCA38DD9F, 0xC729E2B4, ++ 0xC469E88E, 0xC26AEE2B, ++ 0xC0DEF46C, 0xC073FA62, /* 8 */ ++ 0xC01D00D5, 0xC0760743, ++ 0xC1560D1E, 0xC2E51369, ++ 0xC4ED18FF, 0xC7AC1ED7, ++ 0xCB2823B2, 0xCEFA28D9, /* 16 */ ++ 0xD2F62D3F, 0xD7BB3197, ++ 0xDCE53568, 0xE1FE3875, ++ 0xE7D13B35, 0xED663D35, ++ 0xF39B3EC4, 0xF98E3FA7, /* 24 */ ++ 0x00004000, 0x06723FA7, ++ 0x0C653EC4, 0x129A3D35, ++ 0x182F3B35, 0x1E023875, ++ 0x231B3568, 0x28453197, /* 32 */ ++ 0x2D0A2D3F, 0x310628D9, ++ 0x34D823B2, 0x38541ED7, ++ 0x3B1318FF, 0x3D1B1369, ++ 0x3EAA0D1E, 0x3F8A0743, /* 40 */ ++ 0x3FE300D5, 0x3F8DFA62, ++ 0x3F22F46C, 0x3D96EE2B, ++ 0x3B97E88E, 0x38D7E2B4, ++ 0x35C8DD9F, 0x325AD826, /* 48 */ ++ 0x2E03D358, 0x299ACF64, ++ 0x246DCB87, ++}; ++ ++const u16 bcm43xx_ilt_finefreqa[BCM43xx_ILT_FINEFREQA_SIZE] = { ++ 0x0082, 0x0082, 0x0102, 0x0182, /* 0 */ ++ 0x0202, 0x0282, 0x0302, 0x0382, ++ 0x0402, 0x0482, 0x0502, 0x0582, ++ 0x05E2, 0x0662, 0x06E2, 0x0762, ++ 0x07E2, 0x0842, 0x08C2, 0x0942, /* 16 */ ++ 0x09C2, 0x0A22, 0x0AA2, 0x0B02, ++ 0x0B82, 0x0BE2, 0x0C62, 0x0CC2, ++ 0x0D42, 0x0DA2, 0x0E02, 0x0E62, ++ 0x0EE2, 0x0F42, 0x0FA2, 0x1002, /* 32 */ ++ 0x1062, 0x10C2, 0x1122, 0x1182, ++ 0x11E2, 0x1242, 0x12A2, 0x12E2, ++ 0x1342, 0x13A2, 0x1402, 0x1442, ++ 0x14A2, 0x14E2, 0x1542, 0x1582, /* 48 */ ++ 0x15E2, 0x1622, 0x1662, 0x16C1, ++ 0x1701, 0x1741, 0x1781, 0x17E1, ++ 0x1821, 0x1861, 0x18A1, 0x18E1, ++ 0x1921, 0x1961, 0x19A1, 0x19E1, /* 64 */ ++ 0x1A21, 0x1A61, 0x1AA1, 0x1AC1, ++ 0x1B01, 0x1B41, 0x1B81, 0x1BA1, ++ 0x1BE1, 0x1C21, 0x1C41, 0x1C81, ++ 0x1CA1, 0x1CE1, 0x1D01, 0x1D41, /* 80 */ ++ 0x1D61, 0x1DA1, 0x1DC1, 0x1E01, ++ 0x1E21, 0x1E61, 0x1E81, 0x1EA1, ++ 0x1EE1, 0x1F01, 0x1F21, 0x1F41, ++ 0x1F81, 0x1FA1, 0x1FC1, 0x1FE1, /* 96 */ ++ 0x2001, 0x2041, 0x2061, 0x2081, ++ 0x20A1, 0x20C1, 0x20E1, 0x2101, ++ 0x2121, 0x2141, 0x2161, 0x2181, ++ 0x21A1, 0x21C1, 0x21E1, 0x2201, /* 112 */ ++ 0x2221, 0x2241, 0x2261, 0x2281, ++ 0x22A1, 0x22C1, 0x22C1, 0x22E1, ++ 0x2301, 0x2321, 0x2341, 0x2361, ++ 0x2361, 0x2381, 0x23A1, 0x23C1, /* 128 */ ++ 0x23E1, 0x23E1, 0x2401, 0x2421, ++ 0x2441, 0x2441, 0x2461, 0x2481, ++ 0x2481, 0x24A1, 0x24C1, 0x24C1, ++ 0x24E1, 0x2501, 0x2501, 0x2521, /* 144 */ ++ 0x2541, 0x2541, 0x2561, 0x2561, ++ 0x2581, 0x25A1, 0x25A1, 0x25C1, ++ 0x25C1, 0x25E1, 0x2601, 0x2601, ++ 0x2621, 0x2621, 0x2641, 0x2641, /* 160 */ ++ 0x2661, 0x2661, 0x2681, 0x2681, ++ 0x26A1, 0x26A1, 0x26C1, 0x26C1, ++ 0x26E1, 0x26E1, 0x2701, 0x2701, ++ 0x2721, 0x2721, 0x2740, 0x2740, /* 176 */ ++ 0x2760, 0x2760, 0x2780, 0x2780, ++ 0x2780, 0x27A0, 0x27A0, 0x27C0, ++ 0x27C0, 0x27E0, 0x27E0, 0x27E0, ++ 0x2800, 0x2800, 0x2820, 0x2820, /* 192 */ ++ 0x2820, 0x2840, 0x2840, 0x2840, ++ 0x2860, 0x2860, 0x2880, 0x2880, ++ 0x2880, 0x28A0, 0x28A0, 0x28A0, ++ 0x28C0, 0x28C0, 0x28C0, 0x28E0, /* 208 */ ++ 0x28E0, 0x28E0, 0x2900, 0x2900, ++ 0x2900, 0x2920, 0x2920, 0x2920, ++ 0x2940, 0x2940, 0x2940, 0x2960, ++ 0x2960, 0x2960, 0x2960, 0x2980, /* 224 */ ++ 0x2980, 0x2980, 0x29A0, 0x29A0, ++ 0x29A0, 0x29A0, 0x29C0, 0x29C0, ++ 0x29C0, 0x29E0, 0x29E0, 0x29E0, ++ 0x29E0, 0x2A00, 0x2A00, 0x2A00, /* 240 */ ++ 0x2A00, 0x2A20, 0x2A20, 0x2A20, ++ 0x2A20, 0x2A40, 0x2A40, 0x2A40, ++ 0x2A40, 0x2A60, 0x2A60, 0x2A60, ++}; ++ ++const u16 bcm43xx_ilt_finefreqg[BCM43xx_ILT_FINEFREQG_SIZE] = { ++ 0x0089, 0x02E9, 0x0409, 0x04E9, /* 0 */ ++ 0x05A9, 0x0669, 0x0709, 0x0789, ++ 0x0829, 0x08A9, 0x0929, 0x0989, ++ 0x0A09, 0x0A69, 0x0AC9, 0x0B29, ++ 0x0BA9, 0x0BE9, 0x0C49, 0x0CA9, /* 16 */ ++ 0x0D09, 0x0D69, 0x0DA9, 0x0E09, ++ 0x0E69, 0x0EA9, 0x0F09, 0x0F49, ++ 0x0FA9, 0x0FE9, 0x1029, 0x1089, ++ 0x10C9, 0x1109, 0x1169, 0x11A9, /* 32 */ ++ 0x11E9, 0x1229, 0x1289, 0x12C9, ++ 0x1309, 0x1349, 0x1389, 0x13C9, ++ 0x1409, 0x1449, 0x14A9, 0x14E9, ++ 0x1529, 0x1569, 0x15A9, 0x15E9, /* 48 */ ++ 0x1629, 0x1669, 0x16A9, 0x16E8, ++ 0x1728, 0x1768, 0x17A8, 0x17E8, ++ 0x1828, 0x1868, 0x18A8, 0x18E8, ++ 0x1928, 0x1968, 0x19A8, 0x19E8, /* 64 */ ++ 0x1A28, 0x1A68, 0x1AA8, 0x1AE8, ++ 0x1B28, 0x1B68, 0x1BA8, 0x1BE8, ++ 0x1C28, 0x1C68, 0x1CA8, 0x1CE8, ++ 0x1D28, 0x1D68, 0x1DC8, 0x1E08, /* 80 */ ++ 0x1E48, 0x1E88, 0x1EC8, 0x1F08, ++ 0x1F48, 0x1F88, 0x1FE8, 0x2028, ++ 0x2068, 0x20A8, 0x2108, 0x2148, ++ 0x2188, 0x21C8, 0x2228, 0x2268, /* 96 */ ++ 0x22C8, 0x2308, 0x2348, 0x23A8, ++ 0x23E8, 0x2448, 0x24A8, 0x24E8, ++ 0x2548, 0x25A8, 0x2608, 0x2668, ++ 0x26C8, 0x2728, 0x2787, 0x27E7, /* 112 */ ++ 0x2847, 0x28C7, 0x2947, 0x29A7, ++ 0x2A27, 0x2AC7, 0x2B47, 0x2BE7, ++ 0x2CA7, 0x2D67, 0x2E47, 0x2F67, ++ 0x3247, 0x3526, 0x3646, 0x3726, /* 128 */ ++ 0x3806, 0x38A6, 0x3946, 0x39E6, ++ 0x3A66, 0x3AE6, 0x3B66, 0x3BC6, ++ 0x3C45, 0x3CA5, 0x3D05, 0x3D85, ++ 0x3DE5, 0x3E45, 0x3EA5, 0x3EE5, /* 144 */ ++ 0x3F45, 0x3FA5, 0x4005, 0x4045, ++ 0x40A5, 0x40E5, 0x4145, 0x4185, ++ 0x41E5, 0x4225, 0x4265, 0x42C5, ++ 0x4305, 0x4345, 0x43A5, 0x43E5, /* 160 */ ++ 0x4424, 0x4464, 0x44C4, 0x4504, ++ 0x4544, 0x4584, 0x45C4, 0x4604, ++ 0x4644, 0x46A4, 0x46E4, 0x4724, ++ 0x4764, 0x47A4, 0x47E4, 0x4824, /* 176 */ ++ 0x4864, 0x48A4, 0x48E4, 0x4924, ++ 0x4964, 0x49A4, 0x49E4, 0x4A24, ++ 0x4A64, 0x4AA4, 0x4AE4, 0x4B23, ++ 0x4B63, 0x4BA3, 0x4BE3, 0x4C23, /* 192 */ ++ 0x4C63, 0x4CA3, 0x4CE3, 0x4D23, ++ 0x4D63, 0x4DA3, 0x4DE3, 0x4E23, ++ 0x4E63, 0x4EA3, 0x4EE3, 0x4F23, ++ 0x4F63, 0x4FC3, 0x5003, 0x5043, /* 208 */ ++ 0x5083, 0x50C3, 0x5103, 0x5143, ++ 0x5183, 0x51E2, 0x5222, 0x5262, ++ 0x52A2, 0x52E2, 0x5342, 0x5382, ++ 0x53C2, 0x5402, 0x5462, 0x54A2, /* 224 */ ++ 0x5502, 0x5542, 0x55A2, 0x55E2, ++ 0x5642, 0x5682, 0x56E2, 0x5722, ++ 0x5782, 0x57E1, 0x5841, 0x58A1, ++ 0x5901, 0x5961, 0x59C1, 0x5A21, /* 240 */ ++ 0x5AA1, 0x5B01, 0x5B81, 0x5BE1, ++ 0x5C61, 0x5D01, 0x5D80, 0x5E20, ++ 0x5EE0, 0x5FA0, 0x6080, 0x61C0, ++}; ++ ++const u16 bcm43xx_ilt_noisea2[BCM43xx_ILT_NOISEA2_SIZE] = { ++ 0x0001, 0x0001, 0x0001, 0xFFFE, ++ 0xFFFE, 0x3FFF, 0x1000, 0x0393, ++}; ++ ++const u16 bcm43xx_ilt_noisea3[BCM43xx_ILT_NOISEA3_SIZE] = { ++ 0x4C4C, 0x4C4C, 0x4C4C, 0x2D36, ++ 0x4C4C, 0x4C4C, 0x4C4C, 0x2D36, ++}; ++ ++const u16 bcm43xx_ilt_noiseg1[BCM43xx_ILT_NOISEG1_SIZE] = { ++ 0x013C, 0x01F5, 0x031A, 0x0631, ++ 0x0001, 0x0001, 0x0001, 0x0001, ++}; ++ ++const u16 bcm43xx_ilt_noiseg2[BCM43xx_ILT_NOISEG2_SIZE] = { ++ 0x5484, 0x3C40, 0x0000, 0x0000, ++ 0x0000, 0x0000, 0x0000, 0x0000, ++}; ++ ++const u16 bcm43xx_ilt_noisescaleg1[BCM43xx_ILT_NOISESCALEG_SIZE] = { ++ 0x6C77, 0x5162, 0x3B40, 0x3335, /* 0 */ ++ 0x2F2D, 0x2A2A, 0x2527, 0x1F21, ++ 0x1A1D, 0x1719, 0x1616, 0x1414, ++ 0x1414, 0x1400, 0x1414, 0x1614, ++ 0x1716, 0x1A19, 0x1F1D, 0x2521, /* 16 */ ++ 0x2A27, 0x2F2A, 0x332D, 0x3B35, ++ 0x5140, 0x6C62, 0x0077, ++}; ++ ++const u16 bcm43xx_ilt_noisescaleg2[BCM43xx_ILT_NOISESCALEG_SIZE] = { ++ 0xD8DD, 0xCBD4, 0xBCC0, 0XB6B7, /* 0 */ ++ 0xB2B0, 0xADAD, 0xA7A9, 0x9FA1, ++ 0x969B, 0x9195, 0x8F8F, 0x8A8A, ++ 0x8A8A, 0x8A00, 0x8A8A, 0x8F8A, ++ 0x918F, 0x9695, 0x9F9B, 0xA7A1, /* 16 */ ++ 0xADA9, 0xB2AD, 0xB6B0, 0xBCB7, ++ 0xCBC0, 0xD8D4, 0x00DD, ++}; ++ ++const u16 bcm43xx_ilt_noisescaleg3[BCM43xx_ILT_NOISESCALEG_SIZE] = { ++ 0xA4A4, 0xA4A4, 0xA4A4, 0xA4A4, /* 0 */ ++ 0xA4A4, 0xA4A4, 0xA4A4, 0xA4A4, ++ 0xA4A4, 0xA4A4, 0xA4A4, 0xA4A4, ++ 0xA4A4, 0xA400, 0xA4A4, 0xA4A4, ++ 0xA4A4, 0xA4A4, 0xA4A4, 0xA4A4, /* 16 */ ++ 0xA4A4, 0xA4A4, 0xA4A4, 0xA4A4, ++ 0xA4A4, 0xA4A4, 0x00A4, ++}; ++ ++const u16 bcm43xx_ilt_sigmasqr1[BCM43xx_ILT_SIGMASQR_SIZE] = { ++ 0x007A, 0x0075, 0x0071, 0x006C, /* 0 */ ++ 0x0067, 0x0063, 0x005E, 0x0059, ++ 0x0054, 0x0050, 0x004B, 0x0046, ++ 0x0042, 0x003D, 0x003D, 0x003D, ++ 0x003D, 0x003D, 0x003D, 0x003D, /* 16 */ ++ 0x003D, 0x003D, 0x003D, 0x003D, ++ 0x003D, 0x003D, 0x0000, 0x003D, ++ 0x003D, 0x003D, 0x003D, 0x003D, ++ 0x003D, 0x003D, 0x003D, 0x003D, /* 32 */ ++ 0x003D, 0x003D, 0x003D, 0x003D, ++ 0x0042, 0x0046, 0x004B, 0x0050, ++ 0x0054, 0x0059, 0x005E, 0x0063, ++ 0x0067, 0x006C, 0x0071, 0x0075, /* 48 */ ++ 0x007A, ++}; ++ ++const u16 bcm43xx_ilt_sigmasqr2[BCM43xx_ILT_SIGMASQR_SIZE] = { ++ 0x00DE, 0x00DC, 0x00DA, 0x00D8, /* 0 */ ++ 0x00D6, 0x00D4, 0x00D2, 0x00CF, ++ 0x00CD, 0x00CA, 0x00C7, 0x00C4, ++ 0x00C1, 0x00BE, 0x00BE, 0x00BE, ++ 0x00BE, 0x00BE, 0x00BE, 0x00BE, /* 16 */ ++ 0x00BE, 0x00BE, 0x00BE, 0x00BE, ++ 0x00BE, 0x00BE, 0x0000, 0x00BE, ++ 0x00BE, 0x00BE, 0x00BE, 0x00BE, ++ 0x00BE, 0x00BE, 0x00BE, 0x00BE, /* 32 */ ++ 0x00BE, 0x00BE, 0x00BE, 0x00BE, ++ 0x00C1, 0x00C4, 0x00C7, 0x00CA, ++ 0x00CD, 0x00CF, 0x00D2, 0x00D4, ++ 0x00D6, 0x00D8, 0x00DA, 0x00DC, /* 48 */ ++ 0x00DE, ++}; ++ ++/**** Helper functions to access the device Internal Lookup Tables ****/ ++ ++void bcm43xx_ilt_write(struct bcm43xx_private *bcm, u16 offset, u16 val) ++{ ++ if (bcm43xx_current_phy(bcm)->type == BCM43xx_PHYTYPE_A) { ++ bcm43xx_phy_write(bcm, BCM43xx_PHY_ILT_A_CTRL, offset); ++ mmiowb(); ++ bcm43xx_phy_write(bcm, BCM43xx_PHY_ILT_A_DATA1, val); ++ } else { ++ bcm43xx_phy_write(bcm, BCM43xx_PHY_ILT_G_CTRL, offset); ++ mmiowb(); ++ bcm43xx_phy_write(bcm, BCM43xx_PHY_ILT_G_DATA1, val); ++ } ++} ++ ++u16 bcm43xx_ilt_read(struct bcm43xx_private *bcm, u16 offset) ++{ ++ if (bcm43xx_current_phy(bcm)->type == BCM43xx_PHYTYPE_A) { ++ bcm43xx_phy_write(bcm, BCM43xx_PHY_ILT_A_CTRL, offset); ++ return bcm43xx_phy_read(bcm, BCM43xx_PHY_ILT_A_DATA1); ++ } else { ++ bcm43xx_phy_write(bcm, BCM43xx_PHY_ILT_G_CTRL, offset); ++ return bcm43xx_phy_read(bcm, BCM43xx_PHY_ILT_G_DATA1); ++ } ++} +diff -Nur linux-2.6.16/drivers/net/wireless/bcm43xx-d80211/bcm43xx_ilt.h linux-2.6.16-bcm43xx/drivers/net/wireless/bcm43xx-d80211/bcm43xx_ilt.h +--- linux-2.6.16/drivers/net/wireless/bcm43xx-d80211/bcm43xx_ilt.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.16-bcm43xx/drivers/net/wireless/bcm43xx-d80211/bcm43xx_ilt.h 2006-03-28 22:16:14.000000000 +0200 +@@ -0,0 +1,32 @@ ++#ifndef BCM43xx_ILT_H_ ++#define BCM43xx_ILT_H_ ++ ++#define BCM43xx_ILT_ROTOR_SIZE 53 ++extern const u32 bcm43xx_ilt_rotor[BCM43xx_ILT_ROTOR_SIZE]; ++#define BCM43xx_ILT_RETARD_SIZE 53 ++extern const u32 bcm43xx_ilt_retard[BCM43xx_ILT_RETARD_SIZE]; ++#define BCM43xx_ILT_FINEFREQA_SIZE 256 ++extern const u16 bcm43xx_ilt_finefreqa[BCM43xx_ILT_FINEFREQA_SIZE]; ++#define BCM43xx_ILT_FINEFREQG_SIZE 256 ++extern const u16 bcm43xx_ilt_finefreqg[BCM43xx_ILT_FINEFREQG_SIZE]; ++#define BCM43xx_ILT_NOISEA2_SIZE 8 ++extern const u16 bcm43xx_ilt_noisea2[BCM43xx_ILT_NOISEA2_SIZE]; ++#define BCM43xx_ILT_NOISEA3_SIZE 8 ++extern const u16 bcm43xx_ilt_noisea3[BCM43xx_ILT_NOISEA3_SIZE]; ++#define BCM43xx_ILT_NOISEG1_SIZE 8 ++extern const u16 bcm43xx_ilt_noiseg1[BCM43xx_ILT_NOISEG1_SIZE]; ++#define BCM43xx_ILT_NOISEG2_SIZE 8 ++extern const u16 bcm43xx_ilt_noiseg2[BCM43xx_ILT_NOISEG2_SIZE]; ++#define BCM43xx_ILT_NOISESCALEG_SIZE 27 ++extern const u16 bcm43xx_ilt_noisescaleg1[BCM43xx_ILT_NOISESCALEG_SIZE]; ++extern const u16 bcm43xx_ilt_noisescaleg2[BCM43xx_ILT_NOISESCALEG_SIZE]; ++extern const u16 bcm43xx_ilt_noisescaleg3[BCM43xx_ILT_NOISESCALEG_SIZE]; ++#define BCM43xx_ILT_SIGMASQR_SIZE 53 ++extern const u16 bcm43xx_ilt_sigmasqr1[BCM43xx_ILT_SIGMASQR_SIZE]; ++extern const u16 bcm43xx_ilt_sigmasqr2[BCM43xx_ILT_SIGMASQR_SIZE]; ++ ++ ++void bcm43xx_ilt_write(struct bcm43xx_private *bcm, u16 offset, u16 val); ++u16 bcm43xx_ilt_read(struct bcm43xx_private *bcm, u16 offset); ++ ++#endif /* BCM43xx_ILT_H_ */ +diff -Nur linux-2.6.16/drivers/net/wireless/bcm43xx-d80211/bcm43xx_leds.c linux-2.6.16-bcm43xx/drivers/net/wireless/bcm43xx-d80211/bcm43xx_leds.c +--- linux-2.6.16/drivers/net/wireless/bcm43xx-d80211/bcm43xx_leds.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.16-bcm43xx/drivers/net/wireless/bcm43xx-d80211/bcm43xx_leds.c 2006-03-28 22:16:14.000000000 +0200 +@@ -0,0 +1,293 @@ ++/* ++ ++ Broadcom BCM43xx wireless driver ++ ++ Copyright (c) 2005 Martin Langer <martin-langer@gmx.de>, ++ Stefano Brivio <st3@riseup.net> ++ Michael Buesch <mbuesch@freenet.de> ++ Danny van Dyk <kugelfang@gentoo.org> ++ Andreas Jaggi <andreas.jaggi@waterwave.ch> ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; see the file COPYING. If not, write to ++ the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, ++ Boston, MA 02110-1301, USA. ++ ++*/ ++ ++#include "bcm43xx_leds.h" ++#include "bcm43xx.h" ++ ++#include <asm/bitops.h> ++ ++ ++static void bcm43xx_led_changestate(struct bcm43xx_led *led) ++{ ++ struct bcm43xx_private *bcm = led->bcm; ++ const int index = bcm43xx_led_index(led); ++ const u16 mask = (1 << index); ++ u16 ledctl; ++ ++ assert(index >= 0 && index < BCM43xx_NR_LEDS); ++ assert(led->blink_interval); ++ ledctl = bcm43xx_read16(bcm, BCM43xx_MMIO_GPIO_CONTROL); ++ ledctl = (ledctl & mask) ? (ledctl & ~mask) : (ledctl | mask); ++ bcm43xx_write16(bcm, BCM43xx_MMIO_GPIO_CONTROL, ledctl); ++} ++ ++static void bcm43xx_led_blink(unsigned long d) ++{ ++ struct bcm43xx_led *led = (struct bcm43xx_led *)d; ++ struct bcm43xx_private *bcm = led->bcm; ++ unsigned long flags; ++ ++ bcm43xx_lock_mmio(bcm, flags); ++ if (led->blink_interval) { ++ bcm43xx_led_changestate(led); ++ mod_timer(&led->blink_timer, jiffies + led->blink_interval); ++ } ++ bcm43xx_unlock_mmio(bcm, flags); ++} ++ ++static void bcm43xx_led_blink_start(struct bcm43xx_led *led, ++ unsigned long interval) ++{ ++ if (led->blink_interval) ++ return; ++ led->blink_interval = interval; ++ bcm43xx_led_changestate(led); ++ led->blink_timer.expires = jiffies + interval; ++ add_timer(&led->blink_timer); ++} ++ ++static void bcm43xx_led_blink_stop(struct bcm43xx_led *led, int sync) ++{ ++ struct bcm43xx_private *bcm = led->bcm; ++ const int index = bcm43xx_led_index(led); ++ u16 ledctl; ++ ++ if (!led->blink_interval) ++ return; ++ if (unlikely(sync)) ++ del_timer_sync(&led->blink_timer); ++ else ++ del_timer(&led->blink_timer); ++ led->blink_interval = 0; ++ ++ /* Make sure the LED is turned off. */ ++ assert(index >= 0 && index < BCM43xx_NR_LEDS); ++ ledctl = bcm43xx_read16(bcm, BCM43xx_MMIO_GPIO_CONTROL); ++ if (led->activelow) ++ ledctl |= (1 << index); ++ else ++ ledctl &= ~(1 << index); ++ bcm43xx_write16(bcm, BCM43xx_MMIO_GPIO_CONTROL, ledctl); ++} ++ ++static void bcm43xx_led_init_hardcoded(struct bcm43xx_private *bcm, ++ struct bcm43xx_led *led, ++ int led_index) ++{ ++ /* This function is called, if the behaviour (and activelow) ++ * information for a LED is missing in the SPROM. ++ * We hardcode the behaviour values for various devices here. ++ * Note that the BCM43xx_LED_TEST_XXX behaviour values can ++ * be used to figure out which led is mapped to which index. ++ */ ++ ++ switch (led_index) { ++ case 0: ++ led->behaviour = BCM43xx_LED_ACTIVITY; ++ if (bcm->board_vendor == PCI_VENDOR_ID_COMPAQ) ++ led->behaviour = BCM43xx_LED_RADIO_ALL; ++ break; ++ case 1: ++ led->behaviour = BCM43xx_LED_RADIO_B; ++ if (bcm->board_vendor == PCI_VENDOR_ID_ASUSTEK) ++ led->behaviour = BCM43xx_LED_ASSOC; ++ break; ++ case 2: ++ led->behaviour = BCM43xx_LED_RADIO_A; ++ break; ++ case 3: ++ led->behaviour = BCM43xx_LED_OFF; ++ break; ++ default: ++ assert(0); ++ } ++} ++ ++int bcm43xx_leds_init(struct bcm43xx_private *bcm) ++{ ++ struct bcm43xx_led *led; ++ u8 sprom[4]; ++ int i; ++ ++ sprom[0] = bcm->sprom.wl0gpio0; ++ sprom[1] = bcm->sprom.wl0gpio1; ++ sprom[2] = bcm->sprom.wl0gpio2; ++ sprom[3] = bcm->sprom.wl0gpio3; ++ ++ for (i = 0; i < BCM43xx_NR_LEDS; i++) { ++ led = &(bcm->leds[i]); ++ led->bcm = bcm; ++ setup_timer(&led->blink_timer, ++ bcm43xx_led_blink, ++ (unsigned long)led); ++ ++ if (sprom[i] == 0xFF) { ++ bcm43xx_led_init_hardcoded(bcm, led, i); ++ } else { ++ led->behaviour = sprom[i] & BCM43xx_LED_BEHAVIOUR; ++ led->activelow = !!(sprom[i] & BCM43xx_LED_ACTIVELOW); ++ } ++ } ++ ++ return 0; ++} ++ ++void bcm43xx_leds_exit(struct bcm43xx_private *bcm) ++{ ++ struct bcm43xx_led *led; ++ int i; ++ ++ for (i = 0; i < BCM43xx_NR_LEDS; i++) { ++ led = &(bcm->leds[i]); ++ bcm43xx_led_blink_stop(led, 1); ++ } ++ bcm43xx_leds_switch_all(bcm, 0); ++} ++ ++void bcm43xx_leds_update(struct bcm43xx_private *bcm, int activity) ++{ ++ struct bcm43xx_led *led; ++ struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm); ++ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); ++ const int transferring = (jiffies - bcm->stats.last_tx) < BCM43xx_LED_XFER_THRES; ++ int i, turn_on; ++ unsigned long interval = 0; ++ u16 ledctl; ++ ++ ledctl = bcm43xx_read16(bcm, BCM43xx_MMIO_GPIO_CONTROL); ++ for (i = 0; i < BCM43xx_NR_LEDS; i++) { ++ led = &(bcm->leds[i]); ++ ++ turn_on = 0; ++ switch (led->behaviour) { ++ case BCM43xx_LED_INACTIVE: ++ continue; ++ case BCM43xx_LED_OFF: ++ break; ++ case BCM43xx_LED_ON: ++ turn_on = 1; ++ break; ++ case BCM43xx_LED_ACTIVITY: ++ turn_on = activity; ++ break; ++ case BCM43xx_LED_RADIO_ALL: ++ turn_on = radio->enabled; ++ break; ++ case BCM43xx_LED_RADIO_A: ++ turn_on = (radio->enabled && phy->type == BCM43xx_PHYTYPE_A); ++ break; ++ case BCM43xx_LED_RADIO_B: ++ turn_on = (radio->enabled && ++ (phy->type == BCM43xx_PHYTYPE_B || ++ phy->type == BCM43xx_PHYTYPE_G)); ++ break; ++ case BCM43xx_LED_MODE_BG: ++ if (phy->type == BCM43xx_PHYTYPE_G && ++ 1/*FIXME: using G rates.*/) ++ turn_on = 1; ++ break; ++ case BCM43xx_LED_TRANSFER: ++ if (transferring) ++ bcm43xx_led_blink_start(led, BCM43xx_LEDBLINK_MEDIUM); ++ else ++ bcm43xx_led_blink_stop(led, 0); ++ continue; ++ case BCM43xx_LED_APTRANSFER: ++ if (bcm->iw_mode == IW_MODE_MASTER) { ++ if (transferring) { ++ interval = BCM43xx_LEDBLINK_FAST; ++ turn_on = 1; ++ } ++ } else { ++ turn_on = 1; ++ if (0/*TODO: not assoc*/) ++ interval = BCM43xx_LEDBLINK_SLOW; ++ else if (transferring) ++ interval = BCM43xx_LEDBLINK_FAST; ++ else ++ turn_on = 0; ++ } ++ if (turn_on) ++ bcm43xx_led_blink_start(led, interval); ++ else ++ bcm43xx_led_blink_stop(led, 0); ++ continue; ++ case BCM43xx_LED_WEIRD: ++ //TODO ++ break; ++ case BCM43xx_LED_ASSOC: ++ if (1/*bcm->softmac->associated*/) ++ turn_on = 1; ++ break; ++#ifdef CONFIG_BCM43XX_DEBUG ++ case BCM43xx_LED_TEST_BLINKSLOW: ++ bcm43xx_led_blink_start(led, BCM43xx_LEDBLINK_SLOW); ++ continue; ++ case BCM43xx_LED_TEST_BLINKMEDIUM: ++ bcm43xx_led_blink_start(led, BCM43xx_LEDBLINK_MEDIUM); ++ continue; ++ case BCM43xx_LED_TEST_BLINKFAST: ++ bcm43xx_led_blink_start(led, BCM43xx_LEDBLINK_FAST); ++ continue; ++#endif /* CONFIG_BCM43XX_DEBUG */ ++ default: ++ assert(0); ++ }; ++ ++ if (led->activelow) ++ turn_on = !turn_on; ++ if (turn_on) ++ ledctl |= (1 << i); ++ else ++ ledctl &= ~(1 << i); ++ } ++ bcm43xx_write16(bcm, BCM43xx_MMIO_GPIO_CONTROL, ledctl); ++} ++ ++void bcm43xx_leds_switch_all(struct bcm43xx_private *bcm, int on) ++{ ++ struct bcm43xx_led *led; ++ u16 ledctl; ++ int i; ++ int bit_on; ++ ++ ledctl = bcm43xx_read16(bcm, BCM43xx_MMIO_GPIO_CONTROL); ++ for (i = 0; i < BCM43xx_NR_LEDS; i++) { ++ led = &(bcm->leds[i]); ++ if (led->behaviour == BCM43xx_LED_INACTIVE) ++ continue; ++ if (on) ++ bit_on = led->activelow ? 0 : 1; ++ else ++ bit_on = led->activelow ? 1 : 0; ++ if (bit_on) ++ ledctl |= (1 << i); ++ else ++ ledctl &= ~(1 << i); ++ } ++ bcm43xx_write16(bcm, BCM43xx_MMIO_GPIO_CONTROL, ledctl); ++} +diff -Nur linux-2.6.16/drivers/net/wireless/bcm43xx-d80211/bcm43xx_leds.h linux-2.6.16-bcm43xx/drivers/net/wireless/bcm43xx-d80211/bcm43xx_leds.h +--- linux-2.6.16/drivers/net/wireless/bcm43xx-d80211/bcm43xx_leds.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.16-bcm43xx/drivers/net/wireless/bcm43xx-d80211/bcm43xx_leds.h 2006-03-28 22:16:14.000000000 +0200 +@@ -0,0 +1,56 @@ ++#ifndef BCM43xx_LEDS_H_ ++#define BCM43xx_LEDS_H_ ++ ++#include <linux/types.h> ++#include <linux/timer.h> ++ ++ ++struct bcm43xx_led { ++ u8 behaviour:7; ++ u8 activelow:1; ++ ++ struct bcm43xx_private *bcm; ++ struct timer_list blink_timer; ++ unsigned long blink_interval; ++}; ++#define bcm43xx_led_index(led) ((int)((led) - (led)->bcm->leds)) ++ ++/* Delay between state changes when blinking in jiffies */ ++#define BCM43xx_LEDBLINK_SLOW (HZ / 1) ++#define BCM43xx_LEDBLINK_MEDIUM (HZ / 4) ++#define BCM43xx_LEDBLINK_FAST (HZ / 8) ++ ++#define BCM43xx_LED_XFER_THRES (HZ / 100) ++ ++#define BCM43xx_LED_BEHAVIOUR 0x7F ++#define BCM43xx_LED_ACTIVELOW 0x80 ++enum { /* LED behaviour values */ ++ BCM43xx_LED_OFF, ++ BCM43xx_LED_ON, ++ BCM43xx_LED_ACTIVITY, ++ BCM43xx_LED_RADIO_ALL, ++ BCM43xx_LED_RADIO_A, ++ BCM43xx_LED_RADIO_B, ++ BCM43xx_LED_MODE_BG, ++ BCM43xx_LED_TRANSFER, ++ BCM43xx_LED_APTRANSFER, ++ BCM43xx_LED_WEIRD,//FIXME ++ BCM43xx_LED_ASSOC, ++ BCM43xx_LED_INACTIVE, ++ ++ /* Behaviour values for testing. ++ * With these values it is easier to figure out ++ * the real behaviour of leds, in case the SPROM ++ * is missing information. ++ */ ++ BCM43xx_LED_TEST_BLINKSLOW, ++ BCM43xx_LED_TEST_BLINKMEDIUM, ++ BCM43xx_LED_TEST_BLINKFAST, ++}; ++ ++int bcm43xx_leds_init(struct bcm43xx_private *bcm); ++void bcm43xx_leds_exit(struct bcm43xx_private *bcm); ++void bcm43xx_leds_update(struct bcm43xx_private *bcm, int activity); ++void bcm43xx_leds_switch_all(struct bcm43xx_private *bcm, int on); ++ ++#endif /* BCM43xx_LEDS_H_ */ +diff -Nur linux-2.6.16/drivers/net/wireless/bcm43xx-d80211/bcm43xx_main.c linux-2.6.16-bcm43xx/drivers/net/wireless/bcm43xx-d80211/bcm43xx_main.c +--- linux-2.6.16/drivers/net/wireless/bcm43xx-d80211/bcm43xx_main.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.16-bcm43xx/drivers/net/wireless/bcm43xx-d80211/bcm43xx_main.c 2006-03-28 22:16:14.000000000 +0200 +@@ -0,0 +1,4491 @@ ++/* ++ ++ Broadcom BCM43xx wireless driver ++ ++ Copyright (c) 2005 Martin Langer <martin-langer@gmx.de> ++ Copyright (c) 2005 Stefano Brivio <st3@riseup.net> ++ Copyright (c) 2005, 2006 Michael Buesch <mbuesch@freenet.de> ++ Copyright (c) 2005 Danny van Dyk <kugelfang@gentoo.org> ++ Copyright (c) 2005 Andreas Jaggi <andreas.jaggi@waterwave.ch> ++ ++ Some parts of the code in this file are derived from the ipw2200 ++ driver Copyright(c) 2003 - 2004 Intel Corporation. ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; see the file COPYING. If not, write to ++ the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, ++ Boston, MA 02110-1301, USA. ++ ++*/ ++ ++#include <linux/delay.h> ++#include <linux/init.h> ++#include <linux/moduleparam.h> ++#include <linux/if_arp.h> ++#include <linux/etherdevice.h> ++#include <linux/version.h> ++#include <linux/firmware.h> ++#include <linux/wireless.h> ++#include <linux/workqueue.h> ++#include <linux/skbuff.h> ++#include <linux/dma-mapping.h> ++#include <net/iw_handler.h> ++ ++#include "bcm43xx.h" ++#include "bcm43xx_main.h" ++#include "bcm43xx_debugfs.h" ++#include "bcm43xx_radio.h" ++#include "bcm43xx_phy.h" ++#include "bcm43xx_dma.h" ++#include "bcm43xx_pio.h" ++#include "bcm43xx_power.h" ++#include "bcm43xx_sysfs.h" ++#include "bcm43xx_ethtool.h" ++#include "bcm43xx_xmit.h" ++ ++ ++MODULE_DESCRIPTION("Broadcom BCM43xx wireless driver"); ++MODULE_AUTHOR("Martin Langer"); ++MODULE_AUTHOR("Stefano Brivio"); ++MODULE_AUTHOR("Michael Buesch"); ++MODULE_LICENSE("GPL"); ++ ++#ifdef CONFIG_BCM947XX ++extern char *nvram_get(char *name); ++#endif ++ ++#if defined(CONFIG_BCM43XX_D80211_DMA) && defined(CONFIG_BCM43XX_D80211_PIO) ++static int modparam_pio; ++module_param_named(pio, modparam_pio, int, 0444); ++MODULE_PARM_DESC(pio, "enable(1) / disable(0) PIO mode"); ++#elif defined(CONFIG_BCM43XX_D80211_DMA) ++# define modparam_pio 0 ++#elif defined(CONFIG_BCM43XX_D80211_PIO) ++# define modparam_pio 1 ++#endif ++ ++static int modparam_bad_frames_preempt; ++module_param_named(bad_frames_preempt, modparam_bad_frames_preempt, int, 0444); ++MODULE_PARM_DESC(bad_frames_preempt, "enable(1) / disable(0) Bad Frames Preemption"); ++ ++static int modparam_short_retry = BCM43xx_DEFAULT_SHORT_RETRY_LIMIT; ++module_param_named(short_retry, modparam_short_retry, int, 0444); ++MODULE_PARM_DESC(short_retry, "Short-Retry-Limit (0 - 15)"); ++ ++static int modparam_long_retry = BCM43xx_DEFAULT_LONG_RETRY_LIMIT; ++module_param_named(long_retry, modparam_long_retry, int, 0444); ++MODULE_PARM_DESC(long_retry, "Long-Retry-Limit (0 - 15)"); ++ ++static int modparam_noleds; ++module_param_named(noleds, modparam_noleds, int, 0444); ++MODULE_PARM_DESC(noleds, "Turn off all LED activity"); ++ ++#ifdef CONFIG_BCM43XX_D80211_DEBUG ++static char modparam_fwpostfix[64]; ++module_param_string(fwpostfix, modparam_fwpostfix, 64, 0444); ++MODULE_PARM_DESC(fwpostfix, "Postfix for .fw files. Useful for debugging."); ++#else ++# define modparam_fwpostfix "" ++#endif /* CONFIG_BCM43XX_D80211_DEBUG*/ ++ ++ ++/* If you want to debug with just a single device, enable this, ++ * where the string is the pci device ID (as given by the kernel's ++ * pci_name function) of the device to be used. ++ */ ++//#define DEBUG_SINGLE_DEVICE_ONLY "0001:11:00.0" ++ ++/* If you want to enable printing of each MMIO access, enable this. */ ++//#define DEBUG_ENABLE_MMIO_PRINT ++ ++/* If you want to enable printing of MMIO access within ++ * ucode/pcm upload, initvals write, enable this. ++ */ ++//#define DEBUG_ENABLE_UCODE_MMIO_PRINT ++ ++/* If you want to enable printing of PCI Config Space access, enable this */ ++//#define DEBUG_ENABLE_PCILOG ++ ++ ++/* Detailed list maintained at: ++ * http://openfacts.berlios.de/index-en.phtml?title=Bcm43xxDevices ++ */ ++static struct pci_device_id bcm43xx_pci_tbl[] = { ++ /* Broadcom 4303 802.11b */ ++ { PCI_VENDOR_ID_BROADCOM, 0x4301, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, ++ /* Broadcom 4307 802.11b */ ++ { PCI_VENDOR_ID_BROADCOM, 0x4307, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, ++ /* Broadcom 4318 802.11b/g */ ++ { PCI_VENDOR_ID_BROADCOM, 0x4318, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, ++ /* Broadcom 4306 802.11b/g */ ++ { PCI_VENDOR_ID_BROADCOM, 0x4320, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, ++ /* Broadcom 4306 802.11a */ ++// { PCI_VENDOR_ID_BROADCOM, 0x4321, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, ++ /* Broadcom 4309 802.11a/b/g */ ++ { PCI_VENDOR_ID_BROADCOM, 0x4324, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, ++ /* Broadcom 43XG 802.11b/g */ ++ { PCI_VENDOR_ID_BROADCOM, 0x4325, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, ++#ifdef CONFIG_BCM947XX ++ /* SB bus on BCM947xx */ ++ { PCI_VENDOR_ID_BROADCOM, 0x0800, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, ++#endif ++ { 0 }, ++}; ++MODULE_DEVICE_TABLE(pci, bcm43xx_pci_tbl); ++ ++ ++static void bcm43xx_ram_write(struct bcm43xx_private *bcm, u16 offset, u32 val) ++{ ++ u32 status; ++ ++ status = bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD); ++ if (!(status & BCM43xx_SBF_XFER_REG_BYTESWAP)) ++ val = swab32(val); ++ ++ bcm43xx_write32(bcm, BCM43xx_MMIO_RAM_CONTROL, offset); ++ mmiowb(); ++ bcm43xx_write32(bcm, BCM43xx_MMIO_RAM_DATA, val); ++} ++ ++static inline ++void bcm43xx_shm_control_word(struct bcm43xx_private *bcm, ++ u16 routing, u16 offset) ++{ ++ u32 control; ++ ++ /* "offset" is the WORD offset. */ ++ ++ control = routing; ++ control <<= 16; ++ control |= offset; ++ bcm43xx_write32(bcm, BCM43xx_MMIO_SHM_CONTROL, control); ++} ++ ++u32 bcm43xx_shm_read32(struct bcm43xx_private *bcm, ++ u16 routing, u16 offset) ++{ ++ u32 ret; ++ ++ if (routing == BCM43xx_SHM_SHARED) { ++ if (offset & 0x0003) { ++ /* Unaligned access */ ++ bcm43xx_shm_control_word(bcm, routing, offset >> 2); ++ ret = bcm43xx_read16(bcm, BCM43xx_MMIO_SHM_DATA_UNALIGNED); ++ ret <<= 16; ++ bcm43xx_shm_control_word(bcm, routing, (offset >> 2) + 1); ++ ret |= bcm43xx_read16(bcm, BCM43xx_MMIO_SHM_DATA); ++ ++ return ret; ++ } ++ offset >>= 2; ++ } ++ bcm43xx_shm_control_word(bcm, routing, offset); ++ ret = bcm43xx_read32(bcm, BCM43xx_MMIO_SHM_DATA); ++ ++ return ret; ++} ++ ++u16 bcm43xx_shm_read16(struct bcm43xx_private *bcm, ++ u16 routing, u16 offset) ++{ ++ u16 ret; ++ ++ if (routing == BCM43xx_SHM_SHARED) { ++ if (offset & 0x0003) { ++ /* Unaligned access */ ++ bcm43xx_shm_control_word(bcm, routing, offset >> 2); ++ ret = bcm43xx_read16(bcm, BCM43xx_MMIO_SHM_DATA_UNALIGNED); ++ ++ return ret; ++ } ++ offset >>= 2; ++ } ++ bcm43xx_shm_control_word(bcm, routing, offset); ++ ret = bcm43xx_read16(bcm, BCM43xx_MMIO_SHM_DATA); ++ ++ return ret; ++} ++ ++void bcm43xx_shm_write32(struct bcm43xx_private *bcm, ++ u16 routing, u16 offset, ++ u32 value) ++{ ++ if (routing == BCM43xx_SHM_SHARED) { ++ if (offset & 0x0003) { ++ /* Unaligned access */ ++ bcm43xx_shm_control_word(bcm, routing, offset >> 2); ++ mmiowb(); ++ bcm43xx_write16(bcm, BCM43xx_MMIO_SHM_DATA_UNALIGNED, ++ (value >> 16) & 0xffff); ++ mmiowb(); ++ bcm43xx_shm_control_word(bcm, routing, (offset >> 2) + 1); ++ mmiowb(); ++ bcm43xx_write16(bcm, BCM43xx_MMIO_SHM_DATA, ++ value & 0xffff); ++ return; ++ } ++ offset >>= 2; ++ } ++ bcm43xx_shm_control_word(bcm, routing, offset); ++ mmiowb(); ++ bcm43xx_write32(bcm, BCM43xx_MMIO_SHM_DATA, value); ++} ++ ++void bcm43xx_shm_write16(struct bcm43xx_private *bcm, ++ u16 routing, u16 offset, ++ u16 value) ++{ ++ if (routing == BCM43xx_SHM_SHARED) { ++ if (offset & 0x0003) { ++ /* Unaligned access */ ++ bcm43xx_shm_control_word(bcm, routing, offset >> 2); ++ mmiowb(); ++ bcm43xx_write16(bcm, BCM43xx_MMIO_SHM_DATA_UNALIGNED, ++ value); ++ return; ++ } ++ offset >>= 2; ++ } ++ bcm43xx_shm_control_word(bcm, routing, offset); ++ mmiowb(); ++ bcm43xx_write16(bcm, BCM43xx_MMIO_SHM_DATA, value); ++} ++ ++void bcm43xx_tsf_read(struct bcm43xx_private *bcm, u64 *tsf) ++{ ++ /* We need to be careful. As we read the TSF from multiple ++ * registers, we should take care of register overflows. ++ * In theory, the whole tsf read process should be atomic. ++ * We try to be atomic here, by restaring the read process, ++ * if any of the high registers changed (overflew). ++ */ ++ if (bcm->current_core->rev >= 3) { ++ u32 low, high, high2; ++ ++ do { ++ high = bcm43xx_read32(bcm, BCM43xx_MMIO_REV3PLUS_TSF_HIGH); ++ low = bcm43xx_read32(bcm, BCM43xx_MMIO_REV3PLUS_TSF_LOW); ++ high2 = bcm43xx_read32(bcm, BCM43xx_MMIO_REV3PLUS_TSF_HIGH); ++ } while (unlikely(high != high2)); ++ ++ *tsf = high; ++ *tsf <<= 32; ++ *tsf |= low; ++ } else { ++ u64 tmp; ++ u16 v0, v1, v2, v3; ++ u16 test1, test2, test3; ++ ++ do { ++ v3 = bcm43xx_read16(bcm, BCM43xx_MMIO_TSF_3); ++ v2 = bcm43xx_read16(bcm, BCM43xx_MMIO_TSF_2); ++ v1 = bcm43xx_read16(bcm, BCM43xx_MMIO_TSF_1); ++ v0 = bcm43xx_read16(bcm, BCM43xx_MMIO_TSF_0); ++ ++ test3 = bcm43xx_read16(bcm, BCM43xx_MMIO_TSF_3); ++ test2 = bcm43xx_read16(bcm, BCM43xx_MMIO_TSF_2); ++ test1 = bcm43xx_read16(bcm, BCM43xx_MMIO_TSF_1); ++ } while (v3 != test3 || v2 != test2 || v1 != test1); ++ ++ *tsf = v3; ++ *tsf <<= 48; ++ tmp = v2; ++ tmp <<= 32; ++ *tsf |= tmp; ++ tmp = v1; ++ tmp <<= 16; ++ *tsf |= tmp; ++ *tsf |= v0; ++ } ++} ++ ++void bcm43xx_tsf_write(struct bcm43xx_private *bcm, u64 tsf) ++{ ++ u32 status; ++ ++ status = bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD); ++ status |= BCM43xx_SBF_TIME_UPDATE; ++ bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS_BITFIELD, status); ++ mmiowb(); ++ ++ /* Be careful with the in-progress timer. ++ * First zero out the low register, so we have a full ++ * register-overflow duration to complete the operation. ++ */ ++ if (bcm->current_core->rev >= 3) { ++ u32 lo = (tsf & 0x00000000FFFFFFFFULL); ++ u32 hi = (tsf & 0xFFFFFFFF00000000ULL) >> 32; ++ ++ bcm43xx_write32(bcm, BCM43xx_MMIO_REV3PLUS_TSF_LOW, 0); ++ mmiowb(); ++ bcm43xx_write32(bcm, BCM43xx_MMIO_REV3PLUS_TSF_HIGH, hi); ++ mmiowb(); ++ bcm43xx_write32(bcm, BCM43xx_MMIO_REV3PLUS_TSF_LOW, lo); ++ } else { ++ u16 v0 = (tsf & 0x000000000000FFFFULL); ++ u16 v1 = (tsf & 0x00000000FFFF0000ULL) >> 16; ++ u16 v2 = (tsf & 0x0000FFFF00000000ULL) >> 32; ++ u16 v3 = (tsf & 0xFFFF000000000000ULL) >> 48; ++ ++ bcm43xx_write16(bcm, BCM43xx_MMIO_TSF_0, 0); ++ mmiowb(); ++ bcm43xx_write16(bcm, BCM43xx_MMIO_TSF_3, v3); ++ mmiowb(); ++ bcm43xx_write16(bcm, BCM43xx_MMIO_TSF_2, v2); ++ mmiowb(); ++ bcm43xx_write16(bcm, BCM43xx_MMIO_TSF_1, v1); ++ mmiowb(); ++ bcm43xx_write16(bcm, BCM43xx_MMIO_TSF_0, v0); ++ } ++ ++ status = bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD); ++ status &= ~BCM43xx_SBF_TIME_UPDATE; ++ bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS_BITFIELD, status); ++} ++ ++static ++void bcm43xx_macfilter_set(struct bcm43xx_private *bcm, ++ u16 offset, ++ const u8 *mac) ++{ ++ u16 data; ++ ++ offset |= 0x0020; ++ bcm43xx_write16(bcm, BCM43xx_MMIO_MACFILTER_CONTROL, offset); ++ ++ data = mac[0]; ++ data |= mac[1] << 8; ++ bcm43xx_write16(bcm, BCM43xx_MMIO_MACFILTER_DATA, data); ++ data = mac[2]; ++ data |= mac[3] << 8; ++ bcm43xx_write16(bcm, BCM43xx_MMIO_MACFILTER_DATA, data); ++ data = mac[4]; ++ data |= mac[5] << 8; ++ bcm43xx_write16(bcm, BCM43xx_MMIO_MACFILTER_DATA, data); ++} ++ ++static void bcm43xx_macfilter_clear(struct bcm43xx_private *bcm, ++ u16 offset) ++{ ++ const u8 zero_addr[ETH_ALEN] = { 0 }; ++ ++ bcm43xx_macfilter_set(bcm, offset, zero_addr); ++} ++ ++static void bcm43xx_write_mac_bssid_templates(struct bcm43xx_private *bcm) ++{ ++ const u8 *mac = (const u8 *)(bcm->net_dev->dev_addr); ++ const u8 *bssid = bcm->bssid; ++ u8 mac_bssid[ETH_ALEN * 2]; ++ int i; ++ ++ memcpy(mac_bssid, mac, ETH_ALEN); ++ memcpy(mac_bssid + ETH_ALEN, bssid, ETH_ALEN); ++ ++ /* Write our MAC address and BSSID to template ram */ ++ for (i = 0; i < ARRAY_SIZE(mac_bssid); i += sizeof(u32)) ++ bcm43xx_ram_write(bcm, 0x20 + i, *((u32 *)(mac_bssid + i))); ++ for (i = 0; i < ARRAY_SIZE(mac_bssid); i += sizeof(u32)) ++ bcm43xx_ram_write(bcm, 0x78 + i, *((u32 *)(mac_bssid + i))); ++ for (i = 0; i < ARRAY_SIZE(mac_bssid); i += sizeof(u32)) ++ bcm43xx_ram_write(bcm, 0x478 + i, *((u32 *)(mac_bssid + i))); ++} ++ ++static void bcm43xx_set_slot_time(struct bcm43xx_private *bcm, u16 slot_time) ++{ ++ /* slot_time is in usec. */ ++ if (bcm43xx_current_phy(bcm)->type != BCM43xx_PHYTYPE_G) ++ return; ++ bcm43xx_write16(bcm, 0x684, 510 + slot_time); ++ bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, 0x0010, slot_time); ++} ++ ++static void bcm43xx_short_slot_timing_enable(struct bcm43xx_private *bcm) ++{ ++ bcm43xx_set_slot_time(bcm, 9); ++ bcm->short_slot = 1; ++} ++ ++static void bcm43xx_short_slot_timing_disable(struct bcm43xx_private *bcm) ++{ ++ bcm43xx_set_slot_time(bcm, 20); ++ bcm->short_slot = 0; ++} ++ ++/* FIXME: To get the MAC-filter working, we need to implement the ++ * following functions (and rename them :) ++ */ ++#if 0 ++static void bcm43xx_disassociate(struct bcm43xx_private *bcm) ++{ ++ bcm43xx_mac_suspend(bcm); ++ bcm43xx_macfilter_clear(bcm, BCM43xx_MACFILTER_ASSOC); ++ ++ bcm43xx_ram_write(bcm, 0x0026, 0x0000); ++ bcm43xx_ram_write(bcm, 0x0028, 0x0000); ++ bcm43xx_ram_write(bcm, 0x007E, 0x0000); ++ bcm43xx_ram_write(bcm, 0x0080, 0x0000); ++ bcm43xx_ram_write(bcm, 0x047E, 0x0000); ++ bcm43xx_ram_write(bcm, 0x0480, 0x0000); ++ ++ if (bcm->current_core->rev < 3) { ++ bcm43xx_write16(bcm, 0x0610, 0x8000); ++ bcm43xx_write16(bcm, 0x060E, 0x0000); ++ } else ++ bcm43xx_write32(bcm, 0x0188, 0x80000000); ++ ++ bcm43xx_shm_write32(bcm, BCM43xx_SHM_WIRELESS, 0x0004, 0x000003ff); ++ ++#if 0 ++ if (bcm43xx_current_phy(bcm)->type == BCM43xx_PHYTYPE_G && ++ ieee80211_is_ofdm_rate(bcm->softmac->txrates.default_rate)) ++ bcm43xx_short_slot_timing_enable(bcm); ++#endif ++ ++ bcm43xx_mac_enable(bcm); ++} ++ ++static void bcm43xx_associate(struct bcm43xx_private *bcm, ++ const u8 *mac) ++{ ++ bcm43xx_mac_suspend(bcm); ++ bcm43xx_macfilter_set(bcm, BCM43xx_MACFILTER_ASSOC, mac); ++ bcm43xx_write_mac_bssid_templates(bcm); ++ bcm43xx_mac_enable(bcm); ++} ++#endif ++ ++/* Enable a Generic IRQ. "mask" is the mask of which IRQs to enable. ++ * Returns the _previously_ enabled IRQ mask. ++ */ ++static inline u32 bcm43xx_interrupt_enable(struct bcm43xx_private *bcm, u32 mask) ++{ ++ u32 old_mask; ++ ++ old_mask = bcm43xx_read32(bcm, BCM43xx_MMIO_GEN_IRQ_MASK); ++ bcm43xx_write32(bcm, BCM43xx_MMIO_GEN_IRQ_MASK, old_mask | mask); ++ ++ return old_mask; ++} ++ ++/* Disable a Generic IRQ. "mask" is the mask of which IRQs to disable. ++ * Returns the _previously_ enabled IRQ mask. ++ */ ++static inline u32 bcm43xx_interrupt_disable(struct bcm43xx_private *bcm, u32 mask) ++{ ++ u32 old_mask; ++ ++ old_mask = bcm43xx_read32(bcm, BCM43xx_MMIO_GEN_IRQ_MASK); ++ bcm43xx_write32(bcm, BCM43xx_MMIO_GEN_IRQ_MASK, old_mask & ~mask); ++ ++ return old_mask; ++} ++ ++/* Make sure we don't receive more data from the device. */ ++static int bcm43xx_disable_interrupts_sync(struct bcm43xx_private *bcm, u32 *oldstate) ++{ ++ u32 old; ++ unsigned long flags; ++ ++ bcm43xx_lock_mmio(bcm, flags); ++ if (bcm43xx_is_initializing(bcm) || bcm->shutting_down) { ++ bcm43xx_unlock_mmio(bcm, flags); ++ return -EBUSY; ++ } ++ old = bcm43xx_interrupt_disable(bcm, BCM43xx_IRQ_ALL); ++ tasklet_disable(&bcm->isr_tasklet); ++ bcm43xx_unlock_mmio(bcm, flags); ++ if (oldstate) ++ *oldstate = old; ++ ++ return 0; ++} ++ ++static int bcm43xx_read_radioinfo(struct bcm43xx_private *bcm) ++{ ++ struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm); ++ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); ++ u32 radio_id; ++ u16 manufact; ++ u16 version; ++ u8 revision; ++ s8 i; ++ ++ if (bcm->chip_id == 0x4317) { ++ if (bcm->chip_rev == 0x00) ++ radio_id = 0x3205017F; ++ else if (bcm->chip_rev == 0x01) ++ radio_id = 0x4205017F; ++ else ++ radio_id = 0x5205017F; ++ } else { ++ bcm43xx_write16(bcm, BCM43xx_MMIO_RADIO_CONTROL, BCM43xx_RADIOCTL_ID); ++ radio_id = bcm43xx_read16(bcm, BCM43xx_MMIO_RADIO_DATA_HIGH); ++ radio_id <<= 16; ++ bcm43xx_write16(bcm, BCM43xx_MMIO_RADIO_CONTROL, BCM43xx_RADIOCTL_ID); ++ radio_id |= bcm43xx_read16(bcm, BCM43xx_MMIO_RADIO_DATA_LOW); ++ } ++ ++ manufact = (radio_id & 0x00000FFF); ++ version = (radio_id & 0x0FFFF000) >> 12; ++ revision = (radio_id & 0xF0000000) >> 28; ++ ++ dprintk(KERN_INFO PFX "Detected Radio: ID: %x (Manuf: %x Ver: %x Rev: %x)\n", ++ radio_id, manufact, version, revision); ++ ++ switch (phy->type) { ++ case BCM43xx_PHYTYPE_A: ++ if ((version != 0x2060) || (revision != 1) || (manufact != 0x17f)) ++ goto err_unsupported_radio; ++ break; ++ case BCM43xx_PHYTYPE_B: ++ if ((version & 0xFFF0) != 0x2050) ++ goto err_unsupported_radio; ++ break; ++ case BCM43xx_PHYTYPE_G: ++ if (version != 0x2050) ++ goto err_unsupported_radio; ++ break; ++ } ++ ++ radio->manufact = manufact; ++ radio->version = version; ++ radio->revision = revision; ++ ++ /* Set default attenuation values. */ ++ radio->baseband_atten = bcm43xx_default_baseband_attenuation(bcm); ++ radio->radio_atten = bcm43xx_default_radio_attenuation(bcm); ++ radio->txctl1 = bcm43xx_default_txctl1(bcm); ++ radio->txctl2 = 0xFFFF; ++ radio->power_level = ~0; ++ ++ /* Initialize the in-memory nrssi Lookup Table. */ ++ for (i = 0; i < 64; i++) ++ radio->nrssi_lt[i] = i; ++ ++ return 0; ++ ++err_unsupported_radio: ++ printk(KERN_ERR PFX "Unsupported Radio connected to the PHY!\n"); ++ return -ENODEV; ++} ++ ++static inline u8 bcm43xx_crc8(u8 crc, u8 data) ++{ ++ static const u8 t[] = { ++ 0x00, 0xF7, 0xB9, 0x4E, 0x25, 0xD2, 0x9C, 0x6B, ++ 0x4A, 0xBD, 0xF3, 0x04, 0x6F, 0x98, 0xD6, 0x21, ++ 0x94, 0x63, 0x2D, 0xDA, 0xB1, 0x46, 0x08, 0xFF, ++ 0xDE, 0x29, 0x67, 0x90, 0xFB, 0x0C, 0x42, 0xB5, ++ 0x7F, 0x88, 0xC6, 0x31, 0x5A, 0xAD, 0xE3, 0x14, ++ 0x35, 0xC2, 0x8C, 0x7B, 0x10, 0xE7, 0xA9, 0x5E, ++ 0xEB, 0x1C, 0x52, 0xA5, 0xCE, 0x39, 0x77, 0x80, ++ 0xA1, 0x56, 0x18, 0xEF, 0x84, 0x73, 0x3D, 0xCA, ++ 0xFE, 0x09, 0x47, 0xB0, 0xDB, 0x2C, 0x62, 0x95, ++ 0xB4, 0x43, 0x0D, 0xFA, 0x91, 0x66, 0x28, 0xDF, ++ 0x6A, 0x9D, 0xD3, 0x24, 0x4F, 0xB8, 0xF6, 0x01, ++ 0x20, 0xD7, 0x99, 0x6E, 0x05, 0xF2, 0xBC, 0x4B, ++ 0x81, 0x76, 0x38, 0xCF, 0xA4, 0x53, 0x1D, 0xEA, ++ 0xCB, 0x3C, 0x72, 0x85, 0xEE, 0x19, 0x57, 0xA0, ++ 0x15, 0xE2, 0xAC, 0x5B, 0x30, 0xC7, 0x89, 0x7E, ++ 0x5F, 0xA8, 0xE6, 0x11, 0x7A, 0x8D, 0xC3, 0x34, ++ 0xAB, 0x5C, 0x12, 0xE5, 0x8E, 0x79, 0x37, 0xC0, ++ 0xE1, 0x16, 0x58, 0xAF, 0xC4, 0x33, 0x7D, 0x8A, ++ 0x3F, 0xC8, 0x86, 0x71, 0x1A, 0xED, 0xA3, 0x54, ++ 0x75, 0x82, 0xCC, 0x3B, 0x50, 0xA7, 0xE9, 0x1E, ++ 0xD4, 0x23, 0x6D, 0x9A, 0xF1, 0x06, 0x48, 0xBF, ++ 0x9E, 0x69, 0x27, 0xD0, 0xBB, 0x4C, 0x02, 0xF5, ++ 0x40, 0xB7, 0xF9, 0x0E, 0x65, 0x92, 0xDC, 0x2B, ++ 0x0A, 0xFD, 0xB3, 0x44, 0x2F, 0xD8, 0x96, 0x61, ++ 0x55, 0xA2, 0xEC, 0x1B, 0x70, 0x87, 0xC9, 0x3E, ++ 0x1F, 0xE8, 0xA6, 0x51, 0x3A, 0xCD, 0x83, 0x74, ++ 0xC1, 0x36, 0x78, 0x8F, 0xE4, 0x13, 0x5D, 0xAA, ++ 0x8B, 0x7C, 0x32, 0xC5, 0xAE, 0x59, 0x17, 0xE0, ++ 0x2A, 0xDD, 0x93, 0x64, 0x0F, 0xF8, 0xB6, 0x41, ++ 0x60, 0x97, 0xD9, 0x2E, 0x45, 0xB2, 0xFC, 0x0B, ++ 0xBE, 0x49, 0x07, 0xF0, 0x9B, 0x6C, 0x22, 0xD5, ++ 0xF4, 0x03, 0x4D, 0xBA, 0xD1, 0x26, 0x68, 0x9F, ++ }; ++ return t[crc ^ data]; ++} ++ ++static u8 bcm43xx_sprom_crc(const u16 *sprom) ++{ ++ int word; ++ u8 crc = 0xFF; ++ ++ for (word = 0; word < BCM43xx_SPROM_SIZE - 1; word++) { ++ crc = bcm43xx_crc8(crc, sprom[word] & 0x00FF); ++ crc = bcm43xx_crc8(crc, (sprom[word] & 0xFF00) >> 8); ++ } ++ crc = bcm43xx_crc8(crc, sprom[BCM43xx_SPROM_VERSION] & 0x00FF); ++ crc ^= 0xFF; ++ ++ return crc; ++} ++ ++int bcm43xx_sprom_read(struct bcm43xx_private *bcm, u16 *sprom) ++{ ++ int i; ++ u8 crc, expected_crc; ++ ++ for (i = 0; i < BCM43xx_SPROM_SIZE; i++) ++ sprom[i] = bcm43xx_read16(bcm, BCM43xx_SPROM_BASE + (i * 2)); ++ /* CRC-8 check. */ ++ crc = bcm43xx_sprom_crc(sprom); ++ expected_crc = (sprom[BCM43xx_SPROM_VERSION] & 0xFF00) >> 8; ++ if (crc != expected_crc) { ++ printk(KERN_WARNING PFX "WARNING: Invalid SPROM checksum " ++ "(0x%02X, expected: 0x%02X)\n", ++ crc, expected_crc); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++int bcm43xx_sprom_write(struct bcm43xx_private *bcm, const u16 *sprom) ++{ ++ int i, err; ++ u8 crc, expected_crc; ++ u32 spromctl; ++ ++ /* CRC-8 validation of the input data. */ ++ crc = bcm43xx_sprom_crc(sprom); ++ expected_crc = (sprom[BCM43xx_SPROM_VERSION] & 0xFF00) >> 8; ++ if (crc != expected_crc) { ++ printk(KERN_ERR PFX "SPROM input data: Invalid CRC\n"); ++ return -EINVAL; ++ } ++ ++ printk(KERN_INFO PFX "Writing SPROM. Do NOT turn off the power! Please stand by...\n"); ++ err = bcm43xx_pci_read_config32(bcm, BCM43xx_PCICFG_SPROMCTL, &spromctl); ++ if (err) ++ goto err_ctlreg; ++ spromctl |= 0x10; /* SPROM WRITE enable. */ ++ bcm43xx_pci_write_config32(bcm, BCM43xx_PCICFG_SPROMCTL, spromctl); ++ if (err) ++ goto err_ctlreg; ++ /* We must burn lots of CPU cycles here, but that does not ++ * really matter as one does not write the SPROM every other minute... ++ */ ++ printk(KERN_INFO PFX "[ 0%%"); ++ mdelay(500); ++ for (i = 0; i < BCM43xx_SPROM_SIZE; i++) { ++ if (i == 16) ++ printk("25%%"); ++ else if (i == 32) ++ printk("50%%"); ++ else if (i == 48) ++ printk("75%%"); ++ else if (i % 2) ++ printk("."); ++ bcm43xx_write16(bcm, BCM43xx_SPROM_BASE + (i * 2), sprom[i]); ++ mmiowb(); ++ mdelay(20); ++ } ++ spromctl &= ~0x10; /* SPROM WRITE enable. */ ++ bcm43xx_pci_write_config32(bcm, BCM43xx_PCICFG_SPROMCTL, spromctl); ++ if (err) ++ goto err_ctlreg; ++ mdelay(500); ++ printk("100%% ]\n"); ++ printk(KERN_INFO PFX "SPROM written.\n"); ++ bcm43xx_controller_restart(bcm, "SPROM update"); ++ ++ return 0; ++err_ctlreg: ++ printk(KERN_ERR PFX "Could not access SPROM control register.\n"); ++ return -ENODEV; ++} ++ ++static int bcm43xx_sprom_extract(struct bcm43xx_private *bcm) ++{ ++ u16 value; ++ u16 *sprom; ++#ifdef CONFIG_BCM947XX ++ char *c; ++#endif ++ ++ sprom = kzalloc(BCM43xx_SPROM_SIZE * sizeof(u16), ++ GFP_KERNEL); ++ if (!sprom) { ++ printk(KERN_ERR PFX "sprom_extract OOM\n"); ++ return -ENOMEM; ++ } ++#ifdef CONFIG_BCM947XX ++ sprom[BCM43xx_SPROM_BOARDFLAGS2] = atoi(nvram_get("boardflags2")); ++ sprom[BCM43xx_SPROM_BOARDFLAGS] = atoi(nvram_get("boardflags")); ++ ++ if ((c = nvram_get("il0macaddr")) != NULL) ++ e_aton(c, (char *) &(sprom[BCM43xx_SPROM_IL0MACADDR])); ++ ++ if ((c = nvram_get("et1macaddr")) != NULL) ++ e_aton(c, (char *) &(sprom[BCM43xx_SPROM_ET1MACADDR])); ++ ++ sprom[BCM43xx_SPROM_PA0B0] = atoi(nvram_get("pa0b0")); ++ sprom[BCM43xx_SPROM_PA0B1] = atoi(nvram_get("pa0b1")); ++ sprom[BCM43xx_SPROM_PA0B2] = atoi(nvram_get("pa0b2")); ++ ++ sprom[BCM43xx_SPROM_PA1B0] = atoi(nvram_get("pa1b0")); ++ sprom[BCM43xx_SPROM_PA1B1] = atoi(nvram_get("pa1b1")); ++ sprom[BCM43xx_SPROM_PA1B2] = atoi(nvram_get("pa1b2")); ++ ++ sprom[BCM43xx_SPROM_BOARDREV] = atoi(nvram_get("boardrev")); ++#else ++ bcm43xx_sprom_read(bcm, sprom); ++#endif ++ ++ /* boardflags2 */ ++ value = sprom[BCM43xx_SPROM_BOARDFLAGS2]; ++ bcm->sprom.boardflags2 = value; ++ ++ /* il0macaddr */ ++ value = sprom[BCM43xx_SPROM_IL0MACADDR + 0]; ++ *(((u16 *)bcm->sprom.il0macaddr) + 0) = cpu_to_be16(value); ++ value = sprom[BCM43xx_SPROM_IL0MACADDR + 1]; ++ *(((u16 *)bcm->sprom.il0macaddr) + 1) = cpu_to_be16(value); ++ value = sprom[BCM43xx_SPROM_IL0MACADDR + 2]; ++ *(((u16 *)bcm->sprom.il0macaddr) + 2) = cpu_to_be16(value); ++ ++ /* et0macaddr */ ++ value = sprom[BCM43xx_SPROM_ET0MACADDR + 0]; ++ *(((u16 *)bcm->sprom.et0macaddr) + 0) = cpu_to_be16(value); ++ value = sprom[BCM43xx_SPROM_ET0MACADDR + 1]; ++ *(((u16 *)bcm->sprom.et0macaddr) + 1) = cpu_to_be16(value); ++ value = sprom[BCM43xx_SPROM_ET0MACADDR + 2]; ++ *(((u16 *)bcm->sprom.et0macaddr) + 2) = cpu_to_be16(value); ++ ++ /* et1macaddr */ ++ value = sprom[BCM43xx_SPROM_ET1MACADDR + 0]; ++ *(((u16 *)bcm->sprom.et1macaddr) + 0) = cpu_to_be16(value); ++ value = sprom[BCM43xx_SPROM_ET1MACADDR + 1]; ++ *(((u16 *)bcm->sprom.et1macaddr) + 1) = cpu_to_be16(value); ++ value = sprom[BCM43xx_SPROM_ET1MACADDR + 2]; ++ *(((u16 *)bcm->sprom.et1macaddr) + 2) = cpu_to_be16(value); ++ ++ /* ethernet phy settings */ ++ value = sprom[BCM43xx_SPROM_ETHPHY]; ++ bcm->sprom.et0phyaddr = (value & 0x001F); ++ bcm->sprom.et1phyaddr = (value & 0x03E0) >> 5; ++ bcm->sprom.et0mdcport = (value & (1 << 14)) >> 14; ++ bcm->sprom.et1mdcport = (value & (1 << 15)) >> 15; ++ ++ /* boardrev, antennas, locale */ ++ value = sprom[BCM43xx_SPROM_BOARDREV]; ++ bcm->sprom.boardrev = (value & 0x00FF); ++ bcm->sprom.locale = (value & 0x0F00) >> 8; ++ bcm->sprom.antennas_aphy = (value & 0x3000) >> 12; ++ bcm->sprom.antennas_bgphy = (value & 0xC000) >> 14; ++ ++ /* pa0b* */ ++ value = sprom[BCM43xx_SPROM_PA0B0]; ++ bcm->sprom.pa0b0 = value; ++ value = sprom[BCM43xx_SPROM_PA0B1]; ++ bcm->sprom.pa0b1 = value; ++ value = sprom[BCM43xx_SPROM_PA0B2]; ++ bcm->sprom.pa0b2 = value; ++ ++ /* wl0gpio* */ ++ value = sprom[BCM43xx_SPROM_WL0GPIO0]; ++ if (value == 0x0000) ++ value = 0xFFFF; ++ bcm->sprom.wl0gpio0 = value & 0x00FF; ++ bcm->sprom.wl0gpio1 = (value & 0xFF00) >> 8; ++ value = sprom[BCM43xx_SPROM_WL0GPIO2]; ++ if (value == 0x0000) ++ value = 0xFFFF; ++ bcm->sprom.wl0gpio2 = value & 0x00FF; ++ bcm->sprom.wl0gpio3 = (value & 0xFF00) >> 8; ++ ++ /* maxpower */ ++ value = sprom[BCM43xx_SPROM_MAXPWR]; ++ bcm->sprom.maxpower_aphy = (value & 0xFF00) >> 8; ++ bcm->sprom.maxpower_bgphy = value & 0x00FF; ++ ++ /* pa1b* */ ++ value = sprom[BCM43xx_SPROM_PA1B0]; ++ bcm->sprom.pa1b0 = value; ++ value = sprom[BCM43xx_SPROM_PA1B1]; ++ bcm->sprom.pa1b1 = value; ++ value = sprom[BCM43xx_SPROM_PA1B2]; ++ bcm->sprom.pa1b2 = value; ++ ++ /* idle tssi target */ ++ value = sprom[BCM43xx_SPROM_IDL_TSSI_TGT]; ++ bcm->sprom.idle_tssi_tgt_aphy = value & 0x00FF; ++ bcm->sprom.idle_tssi_tgt_bgphy = (value & 0xFF00) >> 8; ++ ++ /* boardflags */ ++ value = sprom[BCM43xx_SPROM_BOARDFLAGS]; ++ if (value == 0xFFFF) ++ value = 0x0000; ++ bcm->sprom.boardflags = value; ++ /* boardflags workarounds */ ++ if (bcm->board_vendor == PCI_VENDOR_ID_DELL && ++ bcm->chip_id == 0x4301 && ++ bcm->board_revision == 0x74) ++ bcm->sprom.boardflags |= BCM43xx_BFL_BTCOEXIST; ++ if (bcm->board_vendor == PCI_VENDOR_ID_APPLE && ++ bcm->board_type == 0x4E && ++ bcm->board_revision > 0x40) ++ bcm->sprom.boardflags |= BCM43xx_BFL_PACTRL; ++ ++ /* antenna gain */ ++ value = sprom[BCM43xx_SPROM_ANTENNA_GAIN]; ++ if (value == 0x0000 || value == 0xFFFF) ++ value = 0x0202; ++ /* convert values to Q5.2 */ ++ bcm->sprom.antennagain_aphy = ((value & 0xFF00) >> 8) * 4; ++ bcm->sprom.antennagain_bgphy = (value & 0x00FF) * 4; ++ ++ kfree(sprom); ++ ++ return 0; ++} ++ ++/* DummyTransmission function, as documented on ++ * http://bcm-specs.sipsolutions.net/DummyTransmission ++ */ ++void bcm43xx_dummy_transmission(struct bcm43xx_private *bcm) ++{ ++ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); ++ struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm); ++ unsigned int i, max_loop; ++ u16 value = 0; ++ u32 buffer[5] = { ++ 0x00000000, ++ 0x0000D400, ++ 0x00000000, ++ 0x00000001, ++ 0x00000000, ++ }; ++ ++ switch (phy->type) { ++ case BCM43xx_PHYTYPE_A: ++ max_loop = 0x1E; ++ buffer[0] = 0xCC010200; ++ break; ++ case BCM43xx_PHYTYPE_B: ++ case BCM43xx_PHYTYPE_G: ++ max_loop = 0xFA; ++ buffer[0] = 0x6E840B00; ++ break; ++ default: ++ assert(0); ++ return; ++ } ++ ++ for (i = 0; i < 5; i++) ++ bcm43xx_ram_write(bcm, i * 4, buffer[i]); ++ ++ bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD); /* dummy read */ ++ ++ bcm43xx_write16(bcm, 0x0568, 0x0000); ++ bcm43xx_write16(bcm, 0x07C0, 0x0000); ++ bcm43xx_write16(bcm, 0x050C, ((phy->type == BCM43xx_PHYTYPE_A) ? 1 : 0)); ++ bcm43xx_write16(bcm, 0x0508, 0x0000); ++ bcm43xx_write16(bcm, 0x050A, 0x0000); ++ bcm43xx_write16(bcm, 0x054C, 0x0000); ++ bcm43xx_write16(bcm, 0x056A, 0x0014); ++ bcm43xx_write16(bcm, 0x0568, 0x0826); ++ bcm43xx_write16(bcm, 0x0500, 0x0000); ++ bcm43xx_write16(bcm, 0x0502, 0x0030); ++ ++ if (radio->version == 0x2050 && radio->revision <= 0x5) ++ bcm43xx_radio_write16(bcm, 0x0051, 0x0017); ++ for (i = 0x00; i < max_loop; i++) { ++ value = bcm43xx_read16(bcm, 0x050E); ++ if (value & 0x0080) ++ break; ++ udelay(10); ++ } ++ for (i = 0x00; i < 0x0A; i++) { ++ value = bcm43xx_read16(bcm, 0x050E); ++ if (value & 0x0400) ++ break; ++ udelay(10); ++ } ++ for (i = 0x00; i < 0x0A; i++) { ++ value = bcm43xx_read16(bcm, 0x0690); ++ if (!(value & 0x0100)) ++ break; ++ udelay(10); ++ } ++ if (radio->version == 0x2050 && radio->revision <= 0x5) ++ bcm43xx_radio_write16(bcm, 0x0051, 0x0037); ++} ++ ++static void key_write(struct bcm43xx_private *bcm, ++ u8 index, u8 algorithm, const u16 *key) ++{ ++ unsigned int i, basic_wep = 0; ++ u32 offset; ++ u16 value; ++ ++ /* Write associated key information */ ++ bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, 0x100 + (index * 2), ++ ((index << 4) | (algorithm & 0x0F))); ++ ++ /* The first 4 WEP keys need extra love */ ++ if (((algorithm == BCM43xx_SEC_ALGO_WEP) || ++ (algorithm == BCM43xx_SEC_ALGO_WEP104)) && (index < 4)) ++ basic_wep = 1; ++ ++ /* Write key payload, 8 little endian words */ ++ offset = bcm->security_offset + (index * BCM43xx_SEC_KEYSIZE); ++ for (i = 0; i < (BCM43xx_SEC_KEYSIZE / sizeof(u16)); i++) { ++ value = cpu_to_le16(key[i]); ++ bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, ++ offset + (i * 2), value); ++ ++ if (!basic_wep) ++ continue; ++ ++ bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, ++ offset + (i * 2) + 4 * BCM43xx_SEC_KEYSIZE, ++ value); ++ } ++} ++ ++static void keymac_write(struct bcm43xx_private *bcm, ++ u8 index, const u32 *addr) ++{ ++ /* for keys 0-3 there is no associated mac address */ ++ if (index < 4) ++ return; ++ ++ index -= 4; ++ if (bcm->current_core->rev >= 5) { ++ bcm43xx_shm_write32(bcm, ++ BCM43xx_SHM_HWMAC, ++ index * 2, ++ cpu_to_be32(*addr)); ++ bcm43xx_shm_write16(bcm, ++ BCM43xx_SHM_HWMAC, ++ (index * 2) + 1, ++ cpu_to_be16(*((u16 *)(addr + 1)))); ++ } else { ++ if (index < 8) { ++ TODO(); /* Put them in the macaddress filter */ ++ } else { ++ TODO(); ++ /* Put them BCM43xx_SHM_SHARED, stating index 0x0120. ++ Keep in mind to update the count of keymacs in 0x003E as well! */ ++ } ++ } ++} ++ ++static int bcm43xx_key_write(struct bcm43xx_private *bcm, ++ u8 index, u8 algorithm, ++ const u8 *_key, int key_len, ++ const u8 *mac_addr) ++{ ++ u8 key[BCM43xx_SEC_KEYSIZE] = { 0 }; ++ ++ if (index >= ARRAY_SIZE(bcm->key)) ++ return -EINVAL; ++ if (key_len > ARRAY_SIZE(key)) ++ return -EINVAL; ++ if (algorithm < 1 || algorithm > 5) ++ return -EINVAL; ++ ++ memcpy(key, _key, key_len); ++ key_write(bcm, index, algorithm, (const u16 *)key); ++ keymac_write(bcm, index, (const u32 *)mac_addr); ++ ++ bcm->key[index].algorithm = algorithm; ++ ++ return 0; ++} ++ ++static void bcm43xx_clear_keys(struct bcm43xx_private *bcm) ++{ ++ static const u32 zero_mac[2] = { 0 }; ++ unsigned int i,j, nr_keys = 54; ++ u16 offset; ++ ++ if (bcm->current_core->rev < 5) ++ nr_keys = 16; ++ assert(nr_keys <= ARRAY_SIZE(bcm->key)); ++ ++ for (i = 0; i < nr_keys; i++) { ++ bcm->key[i].enabled = 0; ++ /* returns for i < 4 immediately */ ++ keymac_write(bcm, i, zero_mac); ++ bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, ++ 0x100 + (i * 2), 0x0000); ++ for (j = 0; j < 8; j++) { ++ offset = bcm->security_offset + (j * 4) + (i * BCM43xx_SEC_KEYSIZE); ++ bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, ++ offset, 0x0000); ++ } ++ } ++ dprintk(KERN_INFO PFX "Keys cleared\n"); ++} ++ ++/* Lowlevel core-switch function. This is only to be used in ++ * bcm43xx_switch_core() and bcm43xx_probe_cores() ++ */ ++static int _switch_core(struct bcm43xx_private *bcm, int core) ++{ ++ int err; ++ int attempts = 0; ++ u32 current_core; ++ ++ assert(core >= 0); ++ while (1) { ++ err = bcm43xx_pci_write_config32(bcm, BCM43xx_PCICFG_ACTIVE_CORE, ++ (core * 0x1000) + 0x18000000); ++ if (unlikely(err)) ++ goto error; ++ err = bcm43xx_pci_read_config32(bcm, BCM43xx_PCICFG_ACTIVE_CORE, ++ ¤t_core); ++ if (unlikely(err)) ++ goto error; ++ current_core = (current_core - 0x18000000) / 0x1000; ++ if (current_core == core) ++ break; ++ ++ if (unlikely(attempts++ > BCM43xx_SWITCH_CORE_MAX_RETRIES)) ++ goto error; ++ udelay(10); ++ } ++#ifdef CONFIG_BCM947XX ++ if (bcm->pci_dev->bus->number == 0) ++ bcm->current_core_offset = 0x1000 * core; ++ else ++ bcm->current_core_offset = 0; ++#endif ++ ++ return 0; ++error: ++ printk(KERN_ERR PFX "Failed to switch to core %d\n", core); ++ return -ENODEV; ++} ++ ++int bcm43xx_switch_core(struct bcm43xx_private *bcm, struct bcm43xx_coreinfo *new_core) ++{ ++ int err; ++ ++ if (unlikely(!new_core)) ++ return 0; ++ if (!new_core->available) ++ return -ENODEV; ++ if (bcm->current_core == new_core) ++ return 0; ++ err = _switch_core(bcm, new_core->index); ++ if (unlikely(err)) ++ goto out; ++ ++ bcm->current_core = new_core; ++ bcm->current_80211_core_idx = -1; ++ if (new_core->id == BCM43xx_COREID_80211) ++ bcm->current_80211_core_idx = (int)(new_core - &(bcm->core_80211[0])); ++ ++out: ++ return err; ++} ++ ++static int bcm43xx_core_enabled(struct bcm43xx_private *bcm) ++{ ++ u32 value; ++ ++ value = bcm43xx_read32(bcm, BCM43xx_CIR_SBTMSTATELOW); ++ value &= BCM43xx_SBTMSTATELOW_CLOCK | BCM43xx_SBTMSTATELOW_RESET ++ | BCM43xx_SBTMSTATELOW_REJECT; ++ ++ return (value == BCM43xx_SBTMSTATELOW_CLOCK); ++} ++ ++/* disable current core */ ++static int bcm43xx_core_disable(struct bcm43xx_private *bcm, u32 core_flags) ++{ ++ u32 sbtmstatelow; ++ u32 sbtmstatehigh; ++ int i; ++ ++ /* fetch sbtmstatelow from core information registers */ ++ sbtmstatelow = bcm43xx_read32(bcm, BCM43xx_CIR_SBTMSTATELOW); ++ ++ /* core is already in reset */ ++ if (sbtmstatelow & BCM43xx_SBTMSTATELOW_RESET) ++ goto out; ++ ++ if (sbtmstatelow & BCM43xx_SBTMSTATELOW_CLOCK) { ++ sbtmstatelow = BCM43xx_SBTMSTATELOW_CLOCK | ++ BCM43xx_SBTMSTATELOW_REJECT; ++ bcm43xx_write32(bcm, BCM43xx_CIR_SBTMSTATELOW, sbtmstatelow); ++ ++ for (i = 0; i < 1000; i++) { ++ sbtmstatelow = bcm43xx_read32(bcm, BCM43xx_CIR_SBTMSTATELOW); ++ if (sbtmstatelow & BCM43xx_SBTMSTATELOW_REJECT) { ++ i = -1; ++ break; ++ } ++ udelay(10); ++ } ++ if (i != -1) { ++ printk(KERN_ERR PFX "Error: core_disable() REJECT timeout!\n"); ++ return -EBUSY; ++ } ++ ++ for (i = 0; i < 1000; i++) { ++ sbtmstatehigh = bcm43xx_read32(bcm, BCM43xx_CIR_SBTMSTATEHIGH); ++ if (!(sbtmstatehigh & BCM43xx_SBTMSTATEHIGH_BUSY)) { ++ i = -1; ++ break; ++ } ++ udelay(10); ++ } ++ if (i != -1) { ++ printk(KERN_ERR PFX "Error: core_disable() BUSY timeout!\n"); ++ return -EBUSY; ++ } ++ ++ sbtmstatelow = BCM43xx_SBTMSTATELOW_FORCE_GATE_CLOCK | ++ BCM43xx_SBTMSTATELOW_REJECT | ++ BCM43xx_SBTMSTATELOW_RESET | ++ BCM43xx_SBTMSTATELOW_CLOCK | ++ core_flags; ++ bcm43xx_write32(bcm, BCM43xx_CIR_SBTMSTATELOW, sbtmstatelow); ++ udelay(10); ++ } ++ ++ sbtmstatelow = BCM43xx_SBTMSTATELOW_RESET | ++ BCM43xx_SBTMSTATELOW_REJECT | ++ core_flags; ++ bcm43xx_write32(bcm, BCM43xx_CIR_SBTMSTATELOW, sbtmstatelow); ++ ++out: ++ bcm->current_core->enabled = 0; ++ ++ return 0; ++} ++ ++/* enable (reset) current core */ ++static int bcm43xx_core_enable(struct bcm43xx_private *bcm, u32 core_flags) ++{ ++ u32 sbtmstatelow; ++ u32 sbtmstatehigh; ++ u32 sbimstate; ++ int err; ++ ++ err = bcm43xx_core_disable(bcm, core_flags); ++ if (err) ++ goto out; ++ ++ sbtmstatelow = BCM43xx_SBTMSTATELOW_CLOCK | ++ BCM43xx_SBTMSTATELOW_RESET | ++ BCM43xx_SBTMSTATELOW_FORCE_GATE_CLOCK | ++ core_flags; ++ bcm43xx_write32(bcm, BCM43xx_CIR_SBTMSTATELOW, sbtmstatelow); ++ udelay(1); ++ ++ sbtmstatehigh = bcm43xx_read32(bcm, BCM43xx_CIR_SBTMSTATEHIGH); ++ if (sbtmstatehigh & BCM43xx_SBTMSTATEHIGH_SERROR) { ++ sbtmstatehigh = 0x00000000; ++ bcm43xx_write32(bcm, BCM43xx_CIR_SBTMSTATEHIGH, sbtmstatehigh); ++ } ++ ++ sbimstate = bcm43xx_read32(bcm, BCM43xx_CIR_SBIMSTATE); ++ if (sbimstate & (BCM43xx_SBIMSTATE_IB_ERROR | BCM43xx_SBIMSTATE_TIMEOUT)) { ++ sbimstate &= ~(BCM43xx_SBIMSTATE_IB_ERROR | BCM43xx_SBIMSTATE_TIMEOUT); ++ bcm43xx_write32(bcm, BCM43xx_CIR_SBIMSTATE, sbimstate); ++ } ++ ++ sbtmstatelow = BCM43xx_SBTMSTATELOW_CLOCK | ++ BCM43xx_SBTMSTATELOW_FORCE_GATE_CLOCK | ++ core_flags; ++ bcm43xx_write32(bcm, BCM43xx_CIR_SBTMSTATELOW, sbtmstatelow); ++ udelay(1); ++ ++ sbtmstatelow = BCM43xx_SBTMSTATELOW_CLOCK | core_flags; ++ bcm43xx_write32(bcm, BCM43xx_CIR_SBTMSTATELOW, sbtmstatelow); ++ udelay(1); ++ ++ bcm->current_core->enabled = 1; ++ assert(err == 0); ++out: ++ return err; ++} ++ ++/* http://bcm-specs.sipsolutions.net/80211CoreReset */ ++void bcm43xx_wireless_core_reset(struct bcm43xx_private *bcm, int connect_phy) ++{ ++ u32 flags = 0x00040000; ++ ++ if ((bcm43xx_core_enabled(bcm)) && ++ !bcm43xx_using_pio(bcm)) { ++//FIXME: Do we _really_ want #ifndef CONFIG_BCM947XX here? ++#ifndef CONFIG_BCM947XX ++ /* reset all used DMA controllers. */ ++ bcm43xx_dmacontroller_tx_reset(bcm, BCM43xx_MMIO_DMA1_BASE); ++ bcm43xx_dmacontroller_tx_reset(bcm, BCM43xx_MMIO_DMA2_BASE); ++ bcm43xx_dmacontroller_tx_reset(bcm, BCM43xx_MMIO_DMA3_BASE); ++ bcm43xx_dmacontroller_tx_reset(bcm, BCM43xx_MMIO_DMA4_BASE); ++ bcm43xx_dmacontroller_rx_reset(bcm, BCM43xx_MMIO_DMA1_BASE); ++ if (bcm->current_core->rev < 5) ++ bcm43xx_dmacontroller_rx_reset(bcm, BCM43xx_MMIO_DMA4_BASE); ++#endif ++ } ++ if (bcm->shutting_down) { ++ bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS_BITFIELD, ++ bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD) ++ & ~(BCM43xx_SBF_MAC_ENABLED | 0x00000002)); ++ } else { ++ if (connect_phy) ++ flags |= 0x20000000; ++ bcm43xx_phy_connect(bcm, connect_phy); ++ bcm43xx_core_enable(bcm, flags); ++ bcm43xx_write16(bcm, 0x03E6, 0x0000); ++ bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS_BITFIELD, ++ bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD) ++ | BCM43xx_SBF_400); ++ } ++} ++ ++static void bcm43xx_wireless_core_disable(struct bcm43xx_private *bcm) ++{ ++ bcm43xx_radio_turn_off(bcm); ++ bcm43xx_write16(bcm, 0x03E6, 0x00F4); ++ bcm43xx_core_disable(bcm, 0); ++} ++ ++/* Mark the current 80211 core inactive. ++ * "active_80211_core" is the other 80211 core, which is used. ++ */ ++static int bcm43xx_wireless_core_mark_inactive(struct bcm43xx_private *bcm, ++ struct bcm43xx_coreinfo *active_80211_core) ++{ ++ u32 sbtmstatelow; ++ struct bcm43xx_coreinfo *old_core; ++ int err = 0; ++ ++ bcm43xx_interrupt_disable(bcm, BCM43xx_IRQ_ALL); ++ bcm43xx_radio_turn_off(bcm); ++ sbtmstatelow = bcm43xx_read32(bcm, BCM43xx_CIR_SBTMSTATELOW); ++ sbtmstatelow &= ~0x200a0000; ++ sbtmstatelow |= 0xa0000; ++ bcm43xx_write32(bcm, BCM43xx_CIR_SBTMSTATELOW, sbtmstatelow); ++ udelay(1); ++ sbtmstatelow = bcm43xx_read32(bcm, BCM43xx_CIR_SBTMSTATELOW); ++ sbtmstatelow &= ~0xa0000; ++ sbtmstatelow |= 0x80000; ++ bcm43xx_write32(bcm, BCM43xx_CIR_SBTMSTATELOW, sbtmstatelow); ++ udelay(1); ++ ++ if (bcm43xx_current_phy(bcm)->type == BCM43xx_PHYTYPE_G) { ++ old_core = bcm->current_core; ++ err = bcm43xx_switch_core(bcm, active_80211_core); ++ if (err) ++ goto out; ++ sbtmstatelow = bcm43xx_read32(bcm, BCM43xx_CIR_SBTMSTATELOW); ++ sbtmstatelow &= ~0x20000000; ++ sbtmstatelow |= 0x20000000; ++ bcm43xx_write32(bcm, BCM43xx_CIR_SBTMSTATELOW, sbtmstatelow); ++ err = bcm43xx_switch_core(bcm, old_core); ++ } ++ ++out: ++ return err; ++} ++ ++static void handle_irq_transmit_status(struct bcm43xx_private *bcm) ++{ ++ u32 v0, v1; ++ u16 tmp; ++ struct bcm43xx_xmitstatus stat; ++ ++ while (1) { ++ v0 = bcm43xx_read32(bcm, BCM43xx_MMIO_XMITSTAT_0); ++ if (!v0) ++ break; ++ v1 = bcm43xx_read32(bcm, BCM43xx_MMIO_XMITSTAT_1); ++ ++ stat.cookie = (v0 >> 16) & 0x0000FFFF; ++ tmp = (u16)((v0 & 0xFFF0) | ((v0 & 0xF) >> 1)); ++ stat.flags = tmp & 0xFF; ++ stat.cnt1 = (tmp & 0x0F00) >> 8; ++ stat.cnt2 = (tmp & 0xF000) >> 12; ++ stat.seq = (u16)(v1 & 0xFFFF); ++ stat.unknown = (u16)((v1 >> 16) & 0xFF); ++ ++ bcm43xx_debugfs_log_txstat(bcm, &stat); ++ ++ if (stat.flags & BCM43xx_TXSTAT_FLAG_IGNORE) ++ continue; ++ if (!(stat.flags & BCM43xx_TXSTAT_FLAG_ACK)) ++ bcm->ieee_stats.dot11ACKFailureCount++; ++ //TODO: There are more (unknown) flags to test. see bcm43xx_main.h ++ ++ if (bcm43xx_using_pio(bcm)) ++ bcm43xx_pio_handle_xmitstatus(bcm, &stat); ++ else ++ bcm43xx_dma_handle_xmitstatus(bcm, &stat); ++ } ++} ++ ++static void bcm43xx_generate_noise_sample(struct bcm43xx_private *bcm) ++{ ++ bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, 0x408, 0x7F7F); ++ bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, 0x40A, 0x7F7F); ++ bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS2_BITFIELD, ++ bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS2_BITFIELD) | (1 << 4)); ++ assert(bcm->noisecalc.core_at_start == bcm->current_core); ++ assert(bcm->noisecalc.channel_at_start == bcm43xx_current_radio(bcm)->channel); ++} ++ ++static void bcm43xx_calculate_link_quality(struct bcm43xx_private *bcm) ++{ ++ /* Top half of Link Quality calculation. */ ++ ++ if (bcm->noisecalc.calculation_running) ++ return; ++ bcm->noisecalc.core_at_start = bcm->current_core; ++ bcm->noisecalc.channel_at_start = bcm43xx_current_radio(bcm)->channel; ++ bcm->noisecalc.calculation_running = 1; ++ bcm->noisecalc.nr_samples = 0; ++ ++ bcm43xx_generate_noise_sample(bcm); ++} ++ ++static void handle_irq_noise(struct bcm43xx_private *bcm) ++{ ++ struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm); ++ u16 tmp; ++ u8 noise[4]; ++ u8 i, j; ++ s32 average; ++ ++ /* Bottom half of Link Quality calculation. */ ++ ++ assert(bcm->noisecalc.calculation_running); ++ if (bcm->noisecalc.core_at_start != bcm->current_core || ++ bcm->noisecalc.channel_at_start != radio->channel) ++ goto drop_calculation; ++ tmp = bcm43xx_shm_read16(bcm, BCM43xx_SHM_SHARED, 0x408); ++ noise[0] = (tmp & 0x00FF); ++ noise[1] = (tmp & 0xFF00) >> 8; ++ tmp = bcm43xx_shm_read16(bcm, BCM43xx_SHM_SHARED, 0x40A); ++ noise[2] = (tmp & 0x00FF); ++ noise[3] = (tmp & 0xFF00) >> 8; ++ if (noise[0] == 0x7F || noise[1] == 0x7F || ++ noise[2] == 0x7F || noise[3] == 0x7F) ++ goto generate_new; ++ ++ /* Get the noise samples. */ ++ assert(bcm->noisecalc.nr_samples <= 8); ++ i = bcm->noisecalc.nr_samples; ++ noise[0] = limit_value(noise[0], 0, ARRAY_SIZE(radio->nrssi_lt) - 1); ++ noise[1] = limit_value(noise[1], 0, ARRAY_SIZE(radio->nrssi_lt) - 1); ++ noise[2] = limit_value(noise[2], 0, ARRAY_SIZE(radio->nrssi_lt) - 1); ++ noise[3] = limit_value(noise[3], 0, ARRAY_SIZE(radio->nrssi_lt) - 1); ++ bcm->noisecalc.samples[i][0] = radio->nrssi_lt[noise[0]]; ++ bcm->noisecalc.samples[i][1] = radio->nrssi_lt[noise[1]]; ++ bcm->noisecalc.samples[i][2] = radio->nrssi_lt[noise[2]]; ++ bcm->noisecalc.samples[i][3] = radio->nrssi_lt[noise[3]]; ++ bcm->noisecalc.nr_samples++; ++ if (bcm->noisecalc.nr_samples == 8) { ++ /* Calculate the Link Quality by the noise samples. */ ++ average = 0; ++ for (i = 0; i < 8; i++) { ++ for (j = 0; j < 4; j++) ++ average += bcm->noisecalc.samples[i][j]; ++ } ++ average /= (8 * 4); ++ average *= 125; ++ average += 64; ++ average /= 128; ++ tmp = bcm43xx_shm_read16(bcm, BCM43xx_SHM_SHARED, 0x40C); ++ tmp = (tmp / 128) & 0x1F; ++ if (tmp >= 8) ++ average += 2; ++ else ++ average -= 25; ++ if (tmp == 8) ++ average -= 72; ++ else ++ average -= 48; ++ ++ if (average > -65) ++ bcm->stats.link_quality = 0; ++ else if (average > -75) ++ bcm->stats.link_quality = 1; ++ else if (average > -85) ++ bcm->stats.link_quality = 2; ++ else ++ bcm->stats.link_quality = 3; ++// dprintk(KERN_INFO PFX "Link Quality: %u (avg was %d)\n", bcm->stats.link_quality, average); ++drop_calculation: ++ bcm->noisecalc.calculation_running = 0; ++ return; ++ } ++generate_new: ++ bcm43xx_generate_noise_sample(bcm); ++} ++ ++static void handle_irq_ps(struct bcm43xx_private *bcm) ++{ ++ if (bcm->iw_mode == IW_MODE_MASTER) { ++ ///TODO: PS TBTT ++ } else { ++ if (1/*FIXME: the last PSpoll frame was sent successfully */) ++ bcm43xx_power_saving_ctl_bits(bcm, -1, -1); ++ } ++ if (bcm->iw_mode == IW_MODE_ADHOC) ++ bcm->reg124_set_0x4 = 1; ++ //FIXME else set to false? ++} ++ ++static void handle_irq_reg124(struct bcm43xx_private *bcm) ++{ ++ if (!bcm->reg124_set_0x4) ++ return; ++ bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS2_BITFIELD, ++ bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS2_BITFIELD) ++ | 0x4); ++ //FIXME: reset reg124_set_0x4 to false? ++} ++ ++static void handle_irq_pmq(struct bcm43xx_private *bcm) ++{ ++ u32 tmp; ++ ++ //TODO: AP mode. ++ ++ while (1) { ++ tmp = bcm43xx_read32(bcm, BCM43xx_MMIO_PS_STATUS); ++ if (!(tmp & 0x00000008)) ++ break; ++ } ++ /* 16bit write is odd, but correct. */ ++ bcm43xx_write16(bcm, BCM43xx_MMIO_PS_STATUS, 0x0002); ++} ++ ++static void bcm43xx_write_beacon_template(struct bcm43xx_private *bcm, ++ u16 ram_offset, ++ u16 shm_size_offset) ++{ ++ u32 tmp; ++ u16 i, size; ++ const u8 *data; ++ ++ data = (const u8 *)(bcm->cached_beacon->data); ++ size = min(bcm->cached_beacon->len, (unsigned int)17); ++ ++ for (i = 0; i < size; i += sizeof(u32)) { ++ tmp = (u32)((data + i)[0]); ++ tmp |= (u32)((data + i)[1]) << 8; ++ tmp |= (u32)((data + i)[2]) << 16; ++ tmp |= (u32)((data + i)[3]) << 24; ++ bcm43xx_ram_write(bcm, ram_offset + i, tmp); ++ } ++ bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, shm_size_offset, size); ++} ++ ++static void handle_irq_beacon(struct bcm43xx_private *bcm) ++{ ++ u32 status; ++ ++ bcm->irq_savedstate &= ~BCM43xx_IRQ_BEACON; ++ status = bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS2_BITFIELD); ++ ++ if (!bcm->cached_beacon) { ++ struct ieee80211_tx_control control; ++ ++ /* No cached template available, yet. ++ * Request the 80211 subsystem to generate a new beacon ++ * frame and use it as template. ++ */ ++ bcm->cached_beacon = ieee80211_beacon_get(bcm->net_dev, 0, &control); ++ if (unlikely(!bcm->cached_beacon)) { ++ dprintkl(KERN_WARNING PFX "Could not generate beacon template.\n"); ++ goto ack; ++ } ++ } ++ ++ if ((status & 0x1) && (status & 0x2)) { ++ack: ++ /* ACK beacon IRQ. */ ++ bcm43xx_write32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON, ++ BCM43xx_IRQ_BEACON); ++ bcm->irq_savedstate |= BCM43xx_IRQ_BEACON; ++ if (likely(bcm->cached_beacon)) ++ kfree_skb(bcm->cached_beacon); ++ bcm->cached_beacon = NULL; ++ return; ++ } ++ if (!(status & 0x1)) { ++ bcm43xx_write_beacon_template(bcm, 0x68, 0x18); ++ status |= 0x1; ++ bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS2_BITFIELD, status); ++ } ++ if (!(status & 0x2)) { ++ bcm43xx_write_beacon_template(bcm, 0x468, 0x1A); ++ status |= 0x2; ++ bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS2_BITFIELD, status); ++ } ++} ++ ++/* Interrupt handler bottom-half */ ++static void bcm43xx_interrupt_tasklet(struct bcm43xx_private *bcm) ++{ ++ u32 reason; ++ u32 dma_reason[4]; ++ int activity = 0; ++ unsigned long flags; ++ ++#ifdef CONFIG_BCM43XX_D80211_DEBUG ++ u32 _handled = 0x00000000; ++# define bcmirq_handled(irq) do { _handled |= (irq); } while (0) ++#else ++# define bcmirq_handled(irq) do { /* nothing */ } while (0) ++#endif /* CONFIG_BCM43XX_D80211_DEBUG*/ ++ ++ bcm43xx_lock_mmio(bcm, flags); ++ reason = bcm->irq_reason; ++ dma_reason[0] = bcm->dma_reason[0]; ++ dma_reason[1] = bcm->dma_reason[1]; ++ dma_reason[2] = bcm->dma_reason[2]; ++ dma_reason[3] = bcm->dma_reason[3]; ++ ++ if (unlikely(reason & BCM43xx_IRQ_XMIT_ERROR)) { ++ /* TX error. We get this when Template Ram is written in wrong endianess ++ * in dummy_tx(). We also get this if something is wrong with the TX header ++ * on DMA or PIO queues. ++ * Maybe we get this in other error conditions, too. ++ */ ++ printkl(KERN_ERR PFX "FATAL ERROR: BCM43xx_IRQ_XMIT_ERROR\n"); ++ bcmirq_handled(BCM43xx_IRQ_XMIT_ERROR); ++ } ++ if (unlikely((dma_reason[0] & BCM43xx_DMAIRQ_FATALMASK) | ++ (dma_reason[1] & BCM43xx_DMAIRQ_FATALMASK) | ++ (dma_reason[2] & BCM43xx_DMAIRQ_FATALMASK) | ++ (dma_reason[3] & BCM43xx_DMAIRQ_FATALMASK))) { ++ printkl(KERN_ERR PFX "FATAL ERROR: Fatal DMA error: " ++ "0x%08X, 0x%08X, 0x%08X, 0x%08X\n", ++ dma_reason[0], dma_reason[1], ++ dma_reason[2], dma_reason[3]); ++ bcm43xx_controller_restart(bcm, "DMA error"); ++ bcm43xx_unlock_mmio(bcm, flags); ++ return; ++ } ++ if (unlikely((dma_reason[0] & BCM43xx_DMAIRQ_NONFATALMASK) | ++ (dma_reason[1] & BCM43xx_DMAIRQ_NONFATALMASK) | ++ (dma_reason[2] & BCM43xx_DMAIRQ_NONFATALMASK) | ++ (dma_reason[3] & BCM43xx_DMAIRQ_NONFATALMASK))) { ++ printkl(KERN_ERR PFX "DMA error: " ++ "0x%08X, 0x%08X, 0x%08X, 0x%08X\n", ++ dma_reason[0], dma_reason[1], ++ dma_reason[2], dma_reason[3]); ++ } ++ ++ if (reason & BCM43xx_IRQ_PS) { ++ handle_irq_ps(bcm); ++ bcmirq_handled(BCM43xx_IRQ_PS); ++ } ++ ++ if (reason & BCM43xx_IRQ_REG124) { ++ handle_irq_reg124(bcm); ++ bcmirq_handled(BCM43xx_IRQ_REG124); ++ } ++ ++ if (reason & BCM43xx_IRQ_BEACON) { ++ if (bcm->iw_mode == IW_MODE_MASTER) ++ handle_irq_beacon(bcm); ++ bcmirq_handled(BCM43xx_IRQ_BEACON); ++ } ++ ++ if (reason & BCM43xx_IRQ_PMQ) { ++ handle_irq_pmq(bcm); ++ bcmirq_handled(BCM43xx_IRQ_PMQ); ++ } ++ ++ if (reason & BCM43xx_IRQ_SCAN) { ++ /*TODO*/ ++ //bcmirq_handled(BCM43xx_IRQ_SCAN); ++ } ++ ++ if (reason & BCM43xx_IRQ_NOISE) { ++ handle_irq_noise(bcm); ++ bcmirq_handled(BCM43xx_IRQ_NOISE); ++ } ++ ++ /* Check the DMA reason registers for received data. */ ++ assert(!(dma_reason[1] & BCM43xx_DMAIRQ_RX_DONE)); ++ assert(!(dma_reason[2] & BCM43xx_DMAIRQ_RX_DONE)); ++ if (dma_reason[0] & BCM43xx_DMAIRQ_RX_DONE) { ++ if (bcm43xx_using_pio(bcm)) ++ bcm43xx_pio_rx(bcm43xx_current_pio(bcm)->queue0); ++ else ++ bcm43xx_dma_rx(bcm43xx_current_dma(bcm)->rx_ring0); ++ /* We intentionally don't set "activity" to 1, here. */ ++ } ++ if (dma_reason[3] & BCM43xx_DMAIRQ_RX_DONE) { ++ if (bcm43xx_using_pio(bcm)) ++ bcm43xx_pio_rx(bcm43xx_current_pio(bcm)->queue3); ++ else ++ bcm43xx_dma_rx(bcm43xx_current_dma(bcm)->rx_ring1); ++ activity = 1; ++ } ++ bcmirq_handled(BCM43xx_IRQ_RX); ++ ++ if (reason & BCM43xx_IRQ_XMIT_STATUS) { ++ handle_irq_transmit_status(bcm); ++ activity = 1; ++ //TODO: In AP mode, this also causes sending of powersave responses. ++ bcmirq_handled(BCM43xx_IRQ_XMIT_STATUS); ++ } ++ ++ /* IRQ_PIO_WORKAROUND is handled in the top-half. */ ++ bcmirq_handled(BCM43xx_IRQ_PIO_WORKAROUND); ++#ifdef CONFIG_BCM43XX_D80211_DEBUG ++ if (unlikely(reason & ~_handled)) { ++ printkl(KERN_WARNING PFX ++ "Unhandled IRQ! Reason: 0x%08x, Unhandled: 0x%08x, " ++ "DMA: 0x%08x, 0x%08x, 0x%08x, 0x%08x\n", ++ reason, (reason & ~_handled), ++ dma_reason[0], dma_reason[1], ++ dma_reason[2], dma_reason[3]); ++ } ++#endif ++#undef bcmirq_handled ++ ++ if (!modparam_noleds) ++ bcm43xx_leds_update(bcm, activity); ++ bcm43xx_interrupt_enable(bcm, bcm->irq_savedstate); ++ bcm43xx_unlock_mmio(bcm, flags); ++} ++ ++static void pio_irq_workaround(struct bcm43xx_private *bcm, ++ u16 base, int queueidx) ++{ ++ u16 rxctl; ++ ++ rxctl = bcm43xx_read16(bcm, base + BCM43xx_PIO_RXCTL); ++ if (rxctl & BCM43xx_PIO_RXCTL_DATAAVAILABLE) ++ bcm->dma_reason[queueidx] |= BCM43xx_DMAIRQ_RX_DONE; ++ else ++ bcm->dma_reason[queueidx] &= ~BCM43xx_DMAIRQ_RX_DONE; ++} ++ ++static void bcm43xx_interrupt_ack(struct bcm43xx_private *bcm, u32 reason) ++{ ++ if (bcm43xx_using_pio(bcm) && ++ (bcm->current_core->rev < 3) && ++ (!(reason & BCM43xx_IRQ_PIO_WORKAROUND))) { ++ /* Apply a PIO specific workaround to the dma_reasons */ ++ pio_irq_workaround(bcm, BCM43xx_MMIO_PIO1_BASE, 0); ++ pio_irq_workaround(bcm, BCM43xx_MMIO_PIO2_BASE, 1); ++ pio_irq_workaround(bcm, BCM43xx_MMIO_PIO3_BASE, 2); ++ pio_irq_workaround(bcm, BCM43xx_MMIO_PIO4_BASE, 3); ++ } ++ ++ bcm43xx_write32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON, reason); ++ ++ bcm43xx_write32(bcm, BCM43xx_MMIO_DMA1_REASON, ++ bcm->dma_reason[0]); ++ bcm43xx_write32(bcm, BCM43xx_MMIO_DMA2_REASON, ++ bcm->dma_reason[1]); ++ bcm43xx_write32(bcm, BCM43xx_MMIO_DMA3_REASON, ++ bcm->dma_reason[2]); ++ bcm43xx_write32(bcm, BCM43xx_MMIO_DMA4_REASON, ++ bcm->dma_reason[3]); ++} ++ ++/* Interrupt handler top-half */ ++static irqreturn_t bcm43xx_interrupt_handler(int irq, void *dev_id, struct pt_regs *regs) ++{ ++ irqreturn_t ret = IRQ_HANDLED; ++ struct bcm43xx_private *bcm = dev_id; ++ u32 reason; ++ ++ if (!bcm) ++ return IRQ_NONE; ++ ++ spin_lock(&bcm->_lock); ++ ++ reason = bcm43xx_read32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON); ++ if (reason == 0xffffffff) { ++ /* irq not for us (shared irq) */ ++ ret = IRQ_NONE; ++ goto out; ++ } ++ reason &= bcm43xx_read32(bcm, BCM43xx_MMIO_GEN_IRQ_MASK); ++ if (!reason) ++ goto out; ++ ++ bcm->dma_reason[0] = bcm43xx_read32(bcm, BCM43xx_MMIO_DMA1_REASON) ++ & 0x0001dc00; ++ bcm->dma_reason[1] = bcm43xx_read32(bcm, BCM43xx_MMIO_DMA2_REASON) ++ & 0x0000dc00; ++ bcm->dma_reason[2] = bcm43xx_read32(bcm, BCM43xx_MMIO_DMA3_REASON) ++ & 0x0000dc00; ++ bcm->dma_reason[3] = bcm43xx_read32(bcm, BCM43xx_MMIO_DMA4_REASON) ++ & 0x0001dc00; ++ ++ bcm43xx_interrupt_ack(bcm, reason); ++ ++ /* Only accept IRQs, if we are initialized properly. ++ * This avoids an RX race while initializing. ++ * We should probably not enable IRQs before we are initialized ++ * completely, but some careful work is needed to fix this. I think it ++ * is best to stay with this cheap workaround for now... . ++ */ ++ if (likely(bcm->initialized)) { ++ /* disable all IRQs. They are enabled again in the bottom half. */ ++ bcm->irq_savedstate = bcm43xx_interrupt_disable(bcm, BCM43xx_IRQ_ALL); ++ /* save the reason code and call our bottom half. */ ++ bcm->irq_reason = reason; ++ tasklet_schedule(&bcm->isr_tasklet); ++ } ++ ++out: ++ mmiowb(); ++ spin_unlock(&bcm->_lock); ++ ++ return ret; ++} ++ ++static void bcm43xx_release_firmware(struct bcm43xx_private *bcm, int force) ++{ ++ if (bcm->firmware_norelease && !force) ++ return; /* Suspending or controller reset. */ ++ release_firmware(bcm->ucode); ++ bcm->ucode = NULL; ++ release_firmware(bcm->pcm); ++ bcm->pcm = NULL; ++ release_firmware(bcm->initvals0); ++ bcm->initvals0 = NULL; ++ release_firmware(bcm->initvals1); ++ bcm->initvals1 = NULL; ++} ++ ++static int bcm43xx_request_firmware(struct bcm43xx_private *bcm) ++{ ++ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); ++ u8 rev = bcm->current_core->rev; ++ int err = 0; ++ int nr; ++ char buf[22 + sizeof(modparam_fwpostfix) - 1] = { 0 }; ++ ++ if (!bcm->ucode) { ++ snprintf(buf, ARRAY_SIZE(buf), "bcm43xx_microcode%d%s.fw", ++ (rev >= 5 ? 5 : rev), ++ modparam_fwpostfix); ++ err = request_firmware(&bcm->ucode, buf, &bcm->pci_dev->dev); ++ if (err) { ++ printk(KERN_ERR PFX ++ "Error: Microcode \"%s\" not available or load failed.\n", ++ buf); ++ goto error; ++ } ++ } ++ ++ if (!bcm->pcm) { ++ snprintf(buf, ARRAY_SIZE(buf), ++ "bcm43xx_pcm%d%s.fw", ++ (rev < 5 ? 4 : 5), ++ modparam_fwpostfix); ++ err = request_firmware(&bcm->pcm, buf, &bcm->pci_dev->dev); ++ if (err) { ++ printk(KERN_ERR PFX ++ "Error: PCM \"%s\" not available or load failed.\n", ++ buf); ++ goto error; ++ } ++ } ++ ++ if (!bcm->initvals0) { ++ if (rev == 2 || rev == 4) { ++ switch (phy->type) { ++ case BCM43xx_PHYTYPE_A: ++ nr = 3; ++ break; ++ case BCM43xx_PHYTYPE_B: ++ case BCM43xx_PHYTYPE_G: ++ nr = 1; ++ break; ++ default: ++ goto err_noinitval; ++ } ++ ++ } else if (rev >= 5) { ++ switch (phy->type) { ++ case BCM43xx_PHYTYPE_A: ++ nr = 7; ++ break; ++ case BCM43xx_PHYTYPE_B: ++ case BCM43xx_PHYTYPE_G: ++ nr = 5; ++ break; ++ default: ++ goto err_noinitval; ++ } ++ } else ++ goto err_noinitval; ++ snprintf(buf, ARRAY_SIZE(buf), "bcm43xx_initval%02d%s.fw", ++ nr, modparam_fwpostfix); ++ ++ err = request_firmware(&bcm->initvals0, buf, &bcm->pci_dev->dev); ++ if (err) { ++ printk(KERN_ERR PFX ++ "Error: InitVals \"%s\" not available or load failed.\n", ++ buf); ++ goto error; ++ } ++ if (bcm->initvals0->size % sizeof(struct bcm43xx_initval)) { ++ printk(KERN_ERR PFX "InitVals fileformat error.\n"); ++ goto error; ++ } ++ } ++ ++ if (!bcm->initvals1) { ++ if (rev >= 5) { ++ u32 sbtmstatehigh; ++ ++ switch (phy->type) { ++ case BCM43xx_PHYTYPE_A: ++ sbtmstatehigh = bcm43xx_read32(bcm, BCM43xx_CIR_SBTMSTATEHIGH); ++ if (sbtmstatehigh & 0x00010000) ++ nr = 9; ++ else ++ nr = 10; ++ break; ++ case BCM43xx_PHYTYPE_B: ++ case BCM43xx_PHYTYPE_G: ++ nr = 6; ++ break; ++ default: ++ goto err_noinitval; ++ } ++ snprintf(buf, ARRAY_SIZE(buf), "bcm43xx_initval%02d%s.fw", ++ nr, modparam_fwpostfix); ++ ++ err = request_firmware(&bcm->initvals1, buf, &bcm->pci_dev->dev); ++ if (err) { ++ printk(KERN_ERR PFX ++ "Error: InitVals \"%s\" not available or load failed.\n", ++ buf); ++ goto error; ++ } ++ if (bcm->initvals1->size % sizeof(struct bcm43xx_initval)) { ++ printk(KERN_ERR PFX "InitVals fileformat error.\n"); ++ goto error; ++ } ++ } ++ } ++ ++out: ++ return err; ++error: ++ bcm43xx_release_firmware(bcm, 1); ++ goto out; ++err_noinitval: ++ printk(KERN_ERR PFX "Error: No InitVals available!\n"); ++ err = -ENOENT; ++ goto error; ++} ++ ++static void bcm43xx_upload_microcode(struct bcm43xx_private *bcm) ++{ ++ const u32 *data; ++ unsigned int i, len; ++ ++ /* Upload Microcode. */ ++ data = (u32 *)(bcm->ucode->data); ++ len = bcm->ucode->size / sizeof(u32); ++ bcm43xx_shm_control_word(bcm, BCM43xx_SHM_UCODE, 0x0000); ++ for (i = 0; i < len; i++) { ++ bcm43xx_write32(bcm, BCM43xx_MMIO_SHM_DATA, ++ be32_to_cpu(data[i])); ++ udelay(10); ++ } ++ ++ /* Upload PCM data. */ ++ data = (u32 *)(bcm->pcm->data); ++ len = bcm->pcm->size / sizeof(u32); ++ bcm43xx_shm_control_word(bcm, BCM43xx_SHM_PCM, 0x01ea); ++ bcm43xx_write32(bcm, BCM43xx_MMIO_SHM_DATA, 0x00004000); ++ bcm43xx_shm_control_word(bcm, BCM43xx_SHM_PCM, 0x01eb); ++ for (i = 0; i < len; i++) { ++ bcm43xx_write32(bcm, BCM43xx_MMIO_SHM_DATA, ++ be32_to_cpu(data[i])); ++ udelay(10); ++ } ++} ++ ++static int bcm43xx_write_initvals(struct bcm43xx_private *bcm, ++ const struct bcm43xx_initval *data, ++ const unsigned int len) ++{ ++ u16 offset, size; ++ u32 value; ++ unsigned int i; ++ ++ for (i = 0; i < len; i++) { ++ offset = be16_to_cpu(data[i].offset); ++ size = be16_to_cpu(data[i].size); ++ value = be32_to_cpu(data[i].value); ++ ++ if (unlikely(offset >= 0x1000)) ++ goto err_format; ++ if (size == 2) { ++ if (unlikely(value & 0xFFFF0000)) ++ goto err_format; ++ bcm43xx_write16(bcm, offset, (u16)value); ++ } else if (size == 4) { ++ bcm43xx_write32(bcm, offset, value); ++ } else ++ goto err_format; ++ } ++ ++ return 0; ++ ++err_format: ++ printk(KERN_ERR PFX "InitVals (bcm43xx_initvalXX.fw) file-format error. " ++ "Please fix your bcm43xx firmware files.\n"); ++ return -EPROTO; ++} ++ ++static int bcm43xx_upload_initvals(struct bcm43xx_private *bcm) ++{ ++ int err; ++ ++ err = bcm43xx_write_initvals(bcm, (struct bcm43xx_initval *)bcm->initvals0->data, ++ bcm->initvals0->size / sizeof(struct bcm43xx_initval)); ++ if (err) ++ goto out; ++ if (bcm->initvals1) { ++ err = bcm43xx_write_initvals(bcm, (struct bcm43xx_initval *)bcm->initvals1->data, ++ bcm->initvals1->size / sizeof(struct bcm43xx_initval)); ++ if (err) ++ goto out; ++ } ++out: ++ return err; ++} ++ ++static int bcm43xx_initialize_irq(struct bcm43xx_private *bcm) ++{ ++ int res; ++ unsigned int i; ++ u32 data; ++ ++ bcm->irq = bcm->pci_dev->irq; ++#ifdef CONFIG_BCM947XX ++ if (bcm->pci_dev->bus->number == 0) { ++ struct pci_dev *d = NULL; ++ /* FIXME: we will probably need more device IDs here... */ ++ d = pci_find_device(PCI_VENDOR_ID_BROADCOM, 0x4324, NULL); ++ if (d != NULL) { ++ bcm->irq = d->irq; ++ } ++ } ++#endif ++ res = request_irq(bcm->irq, bcm43xx_interrupt_handler, ++ SA_SHIRQ, KBUILD_MODNAME, bcm); ++ if (res) { ++ printk(KERN_ERR PFX "Cannot register IRQ%d\n", bcm->irq); ++ return -ENODEV; ++ } ++ bcm43xx_write32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON, 0xffffffff); ++ bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS_BITFIELD, 0x00020402); ++ i = 0; ++ while (1) { ++ data = bcm43xx_read32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON); ++ if (data == BCM43xx_IRQ_READY) ++ break; ++ i++; ++ if (i >= BCM43xx_IRQWAIT_MAX_RETRIES) { ++ printk(KERN_ERR PFX "Card IRQ register not responding. " ++ "Giving up.\n"); ++ free_irq(bcm->irq, bcm); ++ return -ENODEV; ++ } ++ udelay(10); ++ } ++ // dummy read ++ bcm43xx_read32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON); ++ ++ return 0; ++} ++ ++/* Switch to the core used to write the GPIO register. ++ * This is either the ChipCommon, or the PCI core. ++ */ ++static int switch_to_gpio_core(struct bcm43xx_private *bcm) ++{ ++ int err; ++ ++ /* Where to find the GPIO register depends on the chipset. ++ * If it has a ChipCommon, its register at offset 0x6c is the GPIO ++ * control register. Otherwise the register at offset 0x6c in the ++ * PCI core is the GPIO control register. ++ */ ++ err = bcm43xx_switch_core(bcm, &bcm->core_chipcommon); ++ if (err == -ENODEV) { ++ err = bcm43xx_switch_core(bcm, &bcm->core_pci); ++ if (unlikely(err == -ENODEV)) { ++ printk(KERN_ERR PFX "gpio error: " ++ "Neither ChipCommon nor PCI core available!\n"); ++ } ++ } ++ ++ return err; ++} ++ ++/* Initialize the GPIOs ++ * http://bcm-specs.sipsolutions.net/GPIO ++ */ ++static int bcm43xx_gpio_init(struct bcm43xx_private *bcm) ++{ ++ struct bcm43xx_coreinfo *old_core; ++ int err; ++ u32 mask, set; ++ ++ bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS_BITFIELD, ++ bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD) ++ & 0xFFFF3FFF); ++ ++ bcm43xx_leds_switch_all(bcm, 0); ++ bcm43xx_write16(bcm, BCM43xx_MMIO_GPIO_MASK, ++ bcm43xx_read16(bcm, BCM43xx_MMIO_GPIO_MASK) | 0x000F); ++ ++ mask = 0x0000001F; ++ set = 0x0000000F; ++ if (bcm->chip_id == 0x4301) { ++ mask |= 0x0060; ++ set |= 0x0060; ++ } ++ if (0 /* FIXME: conditional unknown */) { ++ bcm43xx_write16(bcm, BCM43xx_MMIO_GPIO_MASK, ++ bcm43xx_read16(bcm, BCM43xx_MMIO_GPIO_MASK) ++ | 0x0100); ++ mask |= 0x0180; ++ set |= 0x0180; ++ } ++ if (bcm->sprom.boardflags & BCM43xx_BFL_PACTRL) { ++ bcm43xx_write16(bcm, BCM43xx_MMIO_GPIO_MASK, ++ bcm43xx_read16(bcm, BCM43xx_MMIO_GPIO_MASK) ++ | 0x0200); ++ mask |= 0x0200; ++ set |= 0x0200; ++ } ++ if (bcm->current_core->rev >= 2) ++ mask |= 0x0010; /* FIXME: This is redundant. */ ++ ++ old_core = bcm->current_core; ++ err = switch_to_gpio_core(bcm); ++ if (err) ++ goto out; ++ bcm43xx_write32(bcm, BCM43xx_GPIO_CONTROL, ++ (bcm43xx_read32(bcm, BCM43xx_GPIO_CONTROL) & mask) | set); ++ err = bcm43xx_switch_core(bcm, old_core); ++out: ++ return err; ++} ++ ++/* Turn off all GPIO stuff. Call this on module unload, for example. */ ++static int bcm43xx_gpio_cleanup(struct bcm43xx_private *bcm) ++{ ++ struct bcm43xx_coreinfo *old_core; ++ int err; ++ ++ old_core = bcm->current_core; ++ err = switch_to_gpio_core(bcm); ++ if (err) ++ return err; ++ bcm43xx_write32(bcm, BCM43xx_GPIO_CONTROL, 0x00000000); ++ err = bcm43xx_switch_core(bcm, old_core); ++ assert(err == 0); ++ ++ return 0; ++} ++ ++/* http://bcm-specs.sipsolutions.net/EnableMac */ ++void bcm43xx_mac_enable(struct bcm43xx_private *bcm) ++{ ++ bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS_BITFIELD, ++ bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD) ++ | BCM43xx_SBF_MAC_ENABLED); ++ bcm43xx_write32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON, BCM43xx_IRQ_READY); ++ bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD); /* dummy read */ ++ bcm43xx_read32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON); /* dummy read */ ++ bcm43xx_power_saving_ctl_bits(bcm, -1, -1); ++} ++ ++/* http://bcm-specs.sipsolutions.net/SuspendMAC */ ++void bcm43xx_mac_suspend(struct bcm43xx_private *bcm) ++{ ++ int i; ++ u32 tmp; ++ ++ bcm43xx_power_saving_ctl_bits(bcm, -1, 1); ++ bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS_BITFIELD, ++ bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD) ++ & ~BCM43xx_SBF_MAC_ENABLED); ++ bcm43xx_read32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON); /* dummy read */ ++ for (i = 100000; i; i--) { ++ tmp = bcm43xx_read32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON); ++ if (tmp & BCM43xx_IRQ_READY) ++ return; ++ udelay(10); ++ } ++ printkl(KERN_ERR PFX "MAC suspend failed\n"); ++} ++ ++void bcm43xx_set_iwmode(struct bcm43xx_private *bcm, ++ int iw_mode) ++{ ++ struct net_device *net_dev = bcm->net_dev; ++ u32 status; ++ u16 value; ++ ++ bcm->iw_mode = iw_mode; ++ if (iw_mode == IW_MODE_MONITOR) ++ net_dev->type = ARPHRD_IEEE80211; ++ else ++ net_dev->type = ARPHRD_ETHER; ++ ++ status = bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD); ++ /* Reset status to infrastructured mode */ ++ status &= ~(BCM43xx_SBF_MODE_AP | BCM43xx_SBF_MODE_MONITOR); ++ status &= ~BCM43xx_SBF_MODE_PROMISC; ++ status |= BCM43xx_SBF_MODE_NOTADHOC; ++ ++/* FIXME: Always enable promisc mode, until we get the MAC filters working correctly. */ ++status |= BCM43xx_SBF_MODE_PROMISC; ++ ++ switch (iw_mode) { ++ case IW_MODE_MONITOR: ++ status |= BCM43xx_SBF_MODE_MONITOR; ++ status |= BCM43xx_SBF_MODE_PROMISC; ++ break; ++ case IW_MODE_ADHOC: ++ status &= ~BCM43xx_SBF_MODE_NOTADHOC; ++ break; ++ case IW_MODE_MASTER: ++ status |= BCM43xx_SBF_MODE_AP; ++ break; ++ case IW_MODE_SECOND: ++ case IW_MODE_REPEAT: ++ TODO(); /* TODO */ ++ break; ++ case IW_MODE_INFRA: ++ /* nothing to be done here... */ ++ break; ++ default: ++ dprintk(KERN_ERR PFX "Unknown mode in set_iwmode: %d\n", iw_mode); ++ } ++ if (net_dev->flags & IFF_PROMISC) ++ status |= BCM43xx_SBF_MODE_PROMISC; ++ bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS_BITFIELD, status); ++ ++ value = 0x0002; ++ if (iw_mode != IW_MODE_ADHOC && iw_mode != IW_MODE_MASTER) { ++ if (bcm->chip_id == 0x4306 && bcm->chip_rev == 3) ++ value = 0x0064; ++ else ++ value = 0x0032; ++ } ++ bcm43xx_write16(bcm, 0x0612, value); ++} ++ ++/* This is the opposite of bcm43xx_chip_init() */ ++static void bcm43xx_chip_cleanup(struct bcm43xx_private *bcm) ++{ ++ bcm43xx_radio_turn_off(bcm); ++ if (!modparam_noleds) ++ bcm43xx_leds_exit(bcm); ++ bcm43xx_gpio_cleanup(bcm); ++ free_irq(bcm->irq, bcm); ++ bcm43xx_release_firmware(bcm, 0); ++} ++ ++/* Initialize the chip ++ * http://bcm-specs.sipsolutions.net/ChipInit ++ */ ++static int bcm43xx_chip_init(struct bcm43xx_private *bcm) ++{ ++ struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm); ++ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); ++ int err; ++ int tmp; ++ u32 value32; ++ u16 value16; ++ ++ bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS_BITFIELD, ++ BCM43xx_SBF_CORE_READY ++ | BCM43xx_SBF_400); ++ ++ err = bcm43xx_request_firmware(bcm); ++ if (err) ++ goto out; ++ bcm43xx_upload_microcode(bcm); ++ err = bcm43xx_initialize_irq(bcm); ++ if (err) ++ goto err_release_fw; ++ err = bcm43xx_gpio_init(bcm); ++ if (err) ++ goto err_free_irq; ++ err = bcm43xx_upload_initvals(bcm); ++ if (err) ++ goto err_gpio_cleanup; ++ bcm43xx_radio_turn_on(bcm); ++ ++ bcm43xx_write16(bcm, 0x03E6, 0x0000); ++ err = bcm43xx_phy_init(bcm); ++ if (err) ++ goto err_radio_off; ++ ++ /* Select initial Interference Mitigation. */ ++ tmp = radio->interfmode; ++ radio->interfmode = BCM43xx_RADIO_INTERFMODE_NONE; ++ bcm43xx_radio_set_interference_mitigation(bcm, tmp); ++ ++ bcm43xx_phy_set_antenna_diversity(bcm); ++ bcm43xx_radio_set_txantenna(bcm, BCM43xx_RADIO_TXANTENNA_DEFAULT); ++ if (phy->type == BCM43xx_PHYTYPE_B) { ++ value16 = bcm43xx_read16(bcm, 0x005E); ++ value16 |= 0x0004; ++ bcm43xx_write16(bcm, 0x005E, value16); ++ } ++ bcm43xx_write32(bcm, 0x0100, 0x01000000); ++ if (bcm->current_core->rev < 5) ++ bcm43xx_write32(bcm, 0x010C, 0x01000000); ++ ++ value32 = bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD); ++ value32 &= ~ BCM43xx_SBF_MODE_NOTADHOC; ++ bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS_BITFIELD, value32); ++ value32 = bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD); ++ value32 |= BCM43xx_SBF_MODE_NOTADHOC; ++ bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS_BITFIELD, value32); ++ ++ value32 = bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD); ++ value32 |= 0x100000; ++ bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS_BITFIELD, value32); ++ ++ if (bcm43xx_using_pio(bcm)) { ++ bcm43xx_write32(bcm, 0x0210, 0x00000100); ++ bcm43xx_write32(bcm, 0x0230, 0x00000100); ++ bcm43xx_write32(bcm, 0x0250, 0x00000100); ++ bcm43xx_write32(bcm, 0x0270, 0x00000100); ++ bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, 0x0034, 0x0000); ++ } ++ ++ /* Probe Response Timeout value */ ++ /* FIXME: Default to 0, has to be set by ioctl probably... :-/ */ ++ bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, 0x0074, 0x0000); ++ ++ /* Initially set the wireless operation mode. */ ++ bcm43xx_set_iwmode(bcm, bcm->iw_mode); ++ ++ if (bcm->current_core->rev < 3) { ++ bcm43xx_write16(bcm, 0x060E, 0x0000); ++ bcm43xx_write16(bcm, 0x0610, 0x8000); ++ bcm43xx_write16(bcm, 0x0604, 0x0000); ++ bcm43xx_write16(bcm, 0x0606, 0x0200); ++ } else { ++ bcm43xx_write32(bcm, 0x0188, 0x80000000); ++ bcm43xx_write32(bcm, 0x018C, 0x02000000); ++ } ++ bcm43xx_write32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON, 0x00004000); ++ bcm43xx_write32(bcm, BCM43xx_MMIO_DMA1_IRQ_MASK, 0x0001DC00); ++ bcm43xx_write32(bcm, BCM43xx_MMIO_DMA2_IRQ_MASK, 0x0000DC00); ++ bcm43xx_write32(bcm, BCM43xx_MMIO_DMA3_IRQ_MASK, 0x0000DC00); ++ bcm43xx_write32(bcm, BCM43xx_MMIO_DMA4_IRQ_MASK, 0x0001DC00); ++ ++ value32 = bcm43xx_read32(bcm, BCM43xx_CIR_SBTMSTATELOW); ++ value32 |= 0x00100000; ++ bcm43xx_write32(bcm, BCM43xx_CIR_SBTMSTATELOW, value32); ++ ++ bcm43xx_write16(bcm, BCM43xx_MMIO_POWERUP_DELAY, bcm43xx_pctl_powerup_delay(bcm)); ++ ++ assert(err == 0); ++ dprintk(KERN_INFO PFX "Chip initialized\n"); ++out: ++ return err; ++ ++err_radio_off: ++ bcm43xx_radio_turn_off(bcm); ++err_gpio_cleanup: ++ bcm43xx_gpio_cleanup(bcm); ++err_free_irq: ++ free_irq(bcm->irq, bcm); ++err_release_fw: ++ bcm43xx_release_firmware(bcm, 1); ++ goto out; ++} ++ ++/* Validate chip access ++ * http://bcm-specs.sipsolutions.net/ValidateChipAccess */ ++static int bcm43xx_validate_chip(struct bcm43xx_private *bcm) ++{ ++ u32 value; ++ u32 shm_backup; ++ ++ shm_backup = bcm43xx_shm_read32(bcm, BCM43xx_SHM_SHARED, 0x0000); ++ bcm43xx_shm_write32(bcm, BCM43xx_SHM_SHARED, 0x0000, 0xAA5555AA); ++ if (bcm43xx_shm_read32(bcm, BCM43xx_SHM_SHARED, 0x0000) != 0xAA5555AA) ++ goto error; ++ bcm43xx_shm_write32(bcm, BCM43xx_SHM_SHARED, 0x0000, 0x55AAAA55); ++ if (bcm43xx_shm_read32(bcm, BCM43xx_SHM_SHARED, 0x0000) != 0x55AAAA55) ++ goto error; ++ bcm43xx_shm_write32(bcm, BCM43xx_SHM_SHARED, 0x0000, shm_backup); ++ ++ value = bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD); ++ if ((value | 0x80000000) != 0x80000400) ++ goto error; ++ ++ value = bcm43xx_read32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON); ++ if (value != 0x00000000) ++ goto error; ++ ++ return 0; ++error: ++ printk(KERN_ERR PFX "Failed to validate the chipaccess\n"); ++ return -ENODEV; ++} ++ ++static void bcm43xx_init_struct_phyinfo(struct bcm43xx_phyinfo *phy) ++{ ++ /* Initialize a "phyinfo" structure. The structure is already ++ * zeroed out. ++ */ ++ phy->antenna_diversity = 0xFFFF; ++ phy->savedpctlreg = 0xFFFF; ++ phy->minlowsig[0] = 0xFFFF; ++ phy->minlowsig[1] = 0xFFFF; ++ spin_lock_init(&phy->lock); ++} ++ ++static void bcm43xx_init_struct_radioinfo(struct bcm43xx_radioinfo *radio) ++{ ++ /* Initialize a "radioinfo" structure. The structure is already ++ * zeroed out. ++ */ ++ radio->interfmode = BCM43xx_RADIO_INTERFMODE_NONE; ++ radio->channel = 0xFF; ++ radio->initial_channel = 0xFF; ++ radio->lofcal = 0xFFFF; ++ radio->initval = 0xFFFF; ++ radio->nrssi[0] = -1000; ++ radio->nrssi[1] = -1000; ++} ++ ++static int bcm43xx_probe_cores(struct bcm43xx_private *bcm) ++{ ++ int err, i; ++ int current_core; ++ u32 core_vendor, core_id, core_rev; ++ u32 sb_id_hi, chip_id_32 = 0; ++ u16 pci_device, chip_id_16; ++ u8 core_count; ++ ++ memset(&bcm->core_chipcommon, 0, sizeof(struct bcm43xx_coreinfo)); ++ memset(&bcm->core_pci, 0, sizeof(struct bcm43xx_coreinfo)); ++ memset(&bcm->core_80211, 0, sizeof(struct bcm43xx_coreinfo) ++ * BCM43xx_MAX_80211_CORES); ++ memset(&bcm->core_80211_ext, 0, sizeof(struct bcm43xx_coreinfo_80211) ++ * BCM43xx_MAX_80211_CORES); ++ bcm->current_80211_core_idx = -1; ++ bcm->nr_80211_available = 0; ++ bcm->current_core = NULL; ++ bcm->active_80211_core = NULL; ++ ++ /* map core 0 */ ++ err = _switch_core(bcm, 0); ++ if (err) ++ goto out; ++ ++ /* fetch sb_id_hi from core information registers */ ++ sb_id_hi = bcm43xx_read32(bcm, BCM43xx_CIR_SB_ID_HI); ++ ++ core_id = (sb_id_hi & 0xFFF0) >> 4; ++ core_rev = (sb_id_hi & 0xF); ++ core_vendor = (sb_id_hi & 0xFFFF0000) >> 16; ++ ++ /* if present, chipcommon is always core 0; read the chipid from it */ ++ if (core_id == BCM43xx_COREID_CHIPCOMMON) { ++ chip_id_32 = bcm43xx_read32(bcm, 0); ++ chip_id_16 = chip_id_32 & 0xFFFF; ++ bcm->core_chipcommon.available = 1; ++ bcm->core_chipcommon.id = core_id; ++ bcm->core_chipcommon.rev = core_rev; ++ bcm->core_chipcommon.index = 0; ++ /* While we are at it, also read the capabilities. */ ++ bcm->chipcommon_capabilities = bcm43xx_read32(bcm, BCM43xx_CHIPCOMMON_CAPABILITIES); ++ } else { ++ /* without a chipCommon, use a hard coded table. */ ++ pci_device = bcm->pci_dev->device; ++ if (pci_device == 0x4301) ++ chip_id_16 = 0x4301; ++ else if ((pci_device >= 0x4305) && (pci_device <= 0x4307)) ++ chip_id_16 = 0x4307; ++ else if ((pci_device >= 0x4402) && (pci_device <= 0x4403)) ++ chip_id_16 = 0x4402; ++ else if ((pci_device >= 0x4610) && (pci_device <= 0x4615)) ++ chip_id_16 = 0x4610; ++ else if ((pci_device >= 0x4710) && (pci_device <= 0x4715)) ++ chip_id_16 = 0x4710; ++#ifdef CONFIG_BCM947XX ++ else if ((pci_device >= 0x4320) && (pci_device <= 0x4325)) ++ chip_id_16 = 0x4309; ++#endif ++ else { ++ printk(KERN_ERR PFX "Could not determine Chip ID\n"); ++ return -ENODEV; ++ } ++ } ++ ++ /* ChipCommon with Core Rev >=4 encodes number of cores, ++ * otherwise consult hardcoded table */ ++ if ((core_id == BCM43xx_COREID_CHIPCOMMON) && (core_rev >= 4)) { ++ core_count = (chip_id_32 & 0x0F000000) >> 24; ++ } else { ++ switch (chip_id_16) { ++ case 0x4610: ++ case 0x4704: ++ case 0x4710: ++ core_count = 9; ++ break; ++ case 0x4310: ++ core_count = 8; ++ break; ++ case 0x5365: ++ core_count = 7; ++ break; ++ case 0x4306: ++ core_count = 6; ++ break; ++ case 0x4301: ++ case 0x4307: ++ core_count = 5; ++ break; ++ case 0x4402: ++ core_count = 3; ++ break; ++ default: ++ /* SOL if we get here */ ++ assert(0); ++ core_count = 1; ++ } ++ } ++ ++ bcm->chip_id = chip_id_16; ++ bcm->chip_rev = (chip_id_32 & 0x000F0000) >> 16; ++ bcm->chip_package = (chip_id_32 & 0x00F00000) >> 20; ++ ++ dprintk(KERN_INFO PFX "Chip ID 0x%x, rev 0x%x\n", ++ bcm->chip_id, bcm->chip_rev); ++ dprintk(KERN_INFO PFX "Number of cores: %d\n", core_count); ++ if (bcm->core_chipcommon.available) { ++ dprintk(KERN_INFO PFX "Core 0: ID 0x%x, rev 0x%x, vendor 0x%x, %s\n", ++ core_id, core_rev, core_vendor, ++ bcm43xx_core_enabled(bcm) ? "enabled" : "disabled"); ++ } ++ ++ if (bcm->core_chipcommon.available) ++ current_core = 1; ++ else ++ current_core = 0; ++ for ( ; current_core < core_count; current_core++) { ++ struct bcm43xx_coreinfo *core; ++ struct bcm43xx_coreinfo_80211 *ext_80211; ++ ++ err = _switch_core(bcm, current_core); ++ if (err) ++ goto out; ++ /* Gather information */ ++ /* fetch sb_id_hi from core information registers */ ++ sb_id_hi = bcm43xx_read32(bcm, BCM43xx_CIR_SB_ID_HI); ++ ++ /* extract core_id, core_rev, core_vendor */ ++ core_id = (sb_id_hi & 0xFFF0) >> 4; ++ core_rev = (sb_id_hi & 0xF); ++ core_vendor = (sb_id_hi & 0xFFFF0000) >> 16; ++ ++ dprintk(KERN_INFO PFX "Core %d: ID 0x%x, rev 0x%x, vendor 0x%x, %s\n", ++ current_core, core_id, core_rev, core_vendor, ++ bcm43xx_core_enabled(bcm) ? "enabled" : "disabled" ); ++ ++ core = NULL; ++ switch (core_id) { ++ case BCM43xx_COREID_PCI: ++ core = &bcm->core_pci; ++ if (core->available) { ++ printk(KERN_WARNING PFX "Multiple PCI cores found.\n"); ++ continue; ++ } ++ break; ++ case BCM43xx_COREID_80211: ++ for (i = 0; i < BCM43xx_MAX_80211_CORES; i++) { ++ core = &(bcm->core_80211[i]); ++ ext_80211 = &(bcm->core_80211_ext[i]); ++ if (!core->available) ++ break; ++ core = NULL; ++ } ++ if (!core) { ++ printk(KERN_WARNING PFX "More than %d cores of type 802.11 found.\n", ++ BCM43xx_MAX_80211_CORES); ++ continue; ++ } ++ if (i != 0) { ++ /* More than one 80211 core is only supported ++ * by special chips. ++ * There are chips with two 80211 cores, but with ++ * dangling pins on the second core. Be careful ++ * and ignore these cores here. ++ */ ++ if (bcm->pci_dev->device != 0x4324) { ++ dprintk(KERN_INFO PFX "Ignoring additional 802.11 core.\n"); ++ continue; ++ } ++ } ++ switch (core_rev) { ++ case 2: ++ case 4: ++ case 5: ++ case 6: ++ case 7: ++ case 9: ++ break; ++ default: ++ printk(KERN_ERR PFX "Error: Unsupported 80211 core revision %u\n", ++ core_rev); ++ err = -ENODEV; ++ goto out; ++ } ++ bcm->nr_80211_available++; ++ bcm43xx_init_struct_phyinfo(&ext_80211->phy); ++ bcm43xx_init_struct_radioinfo(&ext_80211->radio); ++ break; ++ case BCM43xx_COREID_CHIPCOMMON: ++ printk(KERN_WARNING PFX "Multiple CHIPCOMMON cores found.\n"); ++ break; ++ } ++ if (core) { ++ core->available = 1; ++ core->id = core_id; ++ core->rev = core_rev; ++ core->index = current_core; ++ } ++ } ++ ++ if (!bcm->core_80211[0].available) { ++ printk(KERN_ERR PFX "Error: No 80211 core found!\n"); ++ err = -ENODEV; ++ goto out; ++ } ++ ++ err = bcm43xx_switch_core(bcm, &bcm->core_80211[0]); ++ ++ assert(err == 0); ++out: ++ return err; ++} ++ ++static void bcm43xx_gen_bssid(struct bcm43xx_private *bcm) ++{ ++ const u8 *mac = (const u8*)(bcm->net_dev->dev_addr); ++ u8 *bssid = bcm->bssid; ++ ++ switch (bcm->iw_mode) { ++ case IW_MODE_ADHOC: ++ random_ether_addr(bssid); ++ break; ++ case IW_MODE_MASTER: ++ case IW_MODE_INFRA: ++ case IW_MODE_REPEAT: ++ case IW_MODE_SECOND: ++ case IW_MODE_MONITOR: ++ memcpy(bssid, mac, ETH_ALEN); ++ break; ++ default: ++ assert(0); ++ } ++} ++ ++static void bcm43xx_rate_memory_write(struct bcm43xx_private *bcm, ++ u16 rate, ++ int is_ofdm) ++{ ++ u16 offset; ++ ++ if (is_ofdm) { ++ offset = 0x480; ++ offset += (bcm43xx_plcp_get_ratecode_ofdm(rate) & 0x000F) * 2; ++ } else { ++ offset = 0x4C0; ++ offset += (bcm43xx_plcp_get_ratecode_cck(rate) & 0x000F) * 2; ++ } ++ bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, offset + 0x20, ++ bcm43xx_shm_read16(bcm, BCM43xx_SHM_SHARED, offset)); ++} ++ ++static void bcm43xx_rate_memory_init(struct bcm43xx_private *bcm) ++{ ++ switch (bcm43xx_current_phy(bcm)->type) { ++ case BCM43xx_PHYTYPE_A: ++ case BCM43xx_PHYTYPE_G: ++ bcm43xx_rate_memory_write(bcm, BCM43xx_OFDM_RATE_6MB, 1); ++ bcm43xx_rate_memory_write(bcm, BCM43xx_OFDM_RATE_12MB, 1); ++ bcm43xx_rate_memory_write(bcm, BCM43xx_OFDM_RATE_18MB, 1); ++ bcm43xx_rate_memory_write(bcm, BCM43xx_OFDM_RATE_24MB, 1); ++ bcm43xx_rate_memory_write(bcm, BCM43xx_OFDM_RATE_36MB, 1); ++ bcm43xx_rate_memory_write(bcm, BCM43xx_OFDM_RATE_48MB, 1); ++ bcm43xx_rate_memory_write(bcm, BCM43xx_OFDM_RATE_54MB, 1); ++ case BCM43xx_PHYTYPE_B: ++ bcm43xx_rate_memory_write(bcm, BCM43xx_CCK_RATE_1MB, 0); ++ bcm43xx_rate_memory_write(bcm, BCM43xx_CCK_RATE_2MB, 0); ++ bcm43xx_rate_memory_write(bcm, BCM43xx_CCK_RATE_5MB, 0); ++ bcm43xx_rate_memory_write(bcm, BCM43xx_CCK_RATE_11MB, 0); ++ break; ++ default: ++ assert(0); ++ } ++} ++ ++static void bcm43xx_wireless_core_cleanup(struct bcm43xx_private *bcm) ++{ ++ bcm43xx_chip_cleanup(bcm); ++ bcm43xx_pio_free(bcm); ++ bcm43xx_dma_free(bcm); ++ ++ bcm->current_core->initialized = 0; ++} ++ ++/* http://bcm-specs.sipsolutions.net/80211Init */ ++static int bcm43xx_wireless_core_init(struct bcm43xx_private *bcm) ++{ ++ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); ++ struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm); ++ u32 ucodeflags; ++ int err; ++ u32 sbimconfiglow; ++ u8 limit; ++ ++ if (bcm->chip_rev < 5) { ++ sbimconfiglow = bcm43xx_read32(bcm, BCM43xx_CIR_SBIMCONFIGLOW); ++ sbimconfiglow &= ~ BCM43xx_SBIMCONFIGLOW_REQUEST_TOUT_MASK; ++ sbimconfiglow &= ~ BCM43xx_SBIMCONFIGLOW_SERVICE_TOUT_MASK; ++ if (bcm->bustype == BCM43xx_BUSTYPE_PCI) ++ sbimconfiglow |= 0x32; ++ else if (bcm->bustype == BCM43xx_BUSTYPE_SB) ++ sbimconfiglow |= 0x53; ++ else ++ assert(0); ++ bcm43xx_write32(bcm, BCM43xx_CIR_SBIMCONFIGLOW, sbimconfiglow); ++ } ++ ++ bcm43xx_phy_calibrate(bcm); ++ err = bcm43xx_chip_init(bcm); ++ if (err) ++ goto out; ++ ++ bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, 0x0016, bcm->current_core->rev); ++ ucodeflags = bcm43xx_shm_read32(bcm, BCM43xx_SHM_SHARED, BCM43xx_UCODEFLAGS_OFFSET); ++ ++ if (0 /*FIXME: which condition has to be used here? */) ++ ucodeflags |= 0x00000010; ++ ++ /* HW decryption needs to be set now. */ ++ ucodeflags |= 0x40000000; ++ ++ if (phy->type == BCM43xx_PHYTYPE_G) { ++ ucodeflags |= BCM43xx_UCODEFLAG_UNKBGPHY; ++ if (phy->rev == 1) ++ ucodeflags |= BCM43xx_UCODEFLAG_UNKGPHY; ++ if (bcm->sprom.boardflags & BCM43xx_BFL_PACTRL) ++ ucodeflags |= BCM43xx_UCODEFLAG_UNKPACTRL; ++ } else if (phy->type == BCM43xx_PHYTYPE_B) { ++ ucodeflags |= BCM43xx_UCODEFLAG_UNKBGPHY; ++ if (phy->rev >= 2 && radio->version == 0x2050) ++ ucodeflags &= ~BCM43xx_UCODEFLAG_UNKGPHY; ++ } ++ ++ if (ucodeflags != bcm43xx_shm_read32(bcm, BCM43xx_SHM_SHARED, ++ BCM43xx_UCODEFLAGS_OFFSET)) { ++ bcm43xx_shm_write32(bcm, BCM43xx_SHM_SHARED, ++ BCM43xx_UCODEFLAGS_OFFSET, ucodeflags); ++ } ++ ++ /* Short/Long Retry Limit. ++ * The retry-limit is a 4-bit counter. Enforce this to avoid overflowing ++ * the chip-internal counter. ++ */ ++ limit = limit_value(modparam_short_retry, 0, 0xF); ++ bcm43xx_shm_write32(bcm, BCM43xx_SHM_WIRELESS, 0x0006, limit); ++ limit = limit_value(modparam_long_retry, 0, 0xF); ++ bcm43xx_shm_write32(bcm, BCM43xx_SHM_WIRELESS, 0x0007, limit); ++ ++ bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, 0x0044, 3); ++ bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, 0x0046, 2); ++ ++ bcm43xx_rate_memory_init(bcm); ++ ++ /* Minimum Contention Window */ ++ if (phy->type == BCM43xx_PHYTYPE_B) ++ bcm43xx_shm_write32(bcm, BCM43xx_SHM_WIRELESS, 0x0003, 0x0000001f); ++ else ++ bcm43xx_shm_write32(bcm, BCM43xx_SHM_WIRELESS, 0x0003, 0x0000000f); ++ /* Maximum Contention Window */ ++ bcm43xx_shm_write32(bcm, BCM43xx_SHM_WIRELESS, 0x0004, 0x000003ff); ++ ++ bcm43xx_gen_bssid(bcm); ++ bcm43xx_write_mac_bssid_templates(bcm); ++ ++ if (bcm->current_core->rev >= 5) ++ bcm43xx_write16(bcm, 0x043C, 0x000C); ++ ++ if (bcm43xx_using_pio(bcm)) ++ err = bcm43xx_pio_init(bcm); ++ else ++ err = bcm43xx_dma_init(bcm); ++ if (err) ++ goto err_chip_cleanup; ++ bcm43xx_write16(bcm, 0x0612, 0x0050); ++ bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, 0x0416, 0x0050); ++ bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, 0x0414, 0x01F4); ++ ++ bcm43xx_mac_enable(bcm); ++ bcm43xx_interrupt_enable(bcm, bcm->irq_savedstate); ++ ++ bcm->current_core->initialized = 1; ++out: ++ return err; ++ ++err_chip_cleanup: ++ bcm43xx_chip_cleanup(bcm); ++ goto out; ++} ++ ++static int bcm43xx_chipset_attach(struct bcm43xx_private *bcm) ++{ ++ int err; ++ u16 pci_status; ++ ++ err = bcm43xx_pctl_set_crystal(bcm, 1); ++ if (err) ++ goto out; ++ bcm43xx_pci_read_config16(bcm, PCI_STATUS, &pci_status); ++ bcm43xx_pci_write_config16(bcm, PCI_STATUS, pci_status & ~PCI_STATUS_SIG_TARGET_ABORT); ++ ++out: ++ return err; ++} ++ ++static void bcm43xx_chipset_detach(struct bcm43xx_private *bcm) ++{ ++ bcm43xx_pctl_set_clock(bcm, BCM43xx_PCTL_CLK_SLOW); ++ bcm43xx_pctl_set_crystal(bcm, 0); ++} ++ ++static void bcm43xx_pcicore_broadcast_value(struct bcm43xx_private *bcm, ++ u32 address, ++ u32 data) ++{ ++ bcm43xx_write32(bcm, BCM43xx_PCICORE_BCAST_ADDR, address); ++ bcm43xx_write32(bcm, BCM43xx_PCICORE_BCAST_DATA, data); ++} ++ ++static int bcm43xx_pcicore_commit_settings(struct bcm43xx_private *bcm) ++{ ++ int err; ++ struct bcm43xx_coreinfo *old_core; ++ ++ old_core = bcm->current_core; ++ err = bcm43xx_switch_core(bcm, &bcm->core_pci); ++ if (err) ++ goto out; ++ ++ bcm43xx_pcicore_broadcast_value(bcm, 0xfd8, 0x00000000); ++ ++ bcm43xx_switch_core(bcm, old_core); ++ assert(err == 0); ++out: ++ return err; ++} ++ ++/* Make an I/O Core usable. "core_mask" is the bitmask of the cores to enable. ++ * To enable core 0, pass a core_mask of 1<<0 ++ */ ++static int bcm43xx_setup_backplane_pci_connection(struct bcm43xx_private *bcm, ++ u32 core_mask) ++{ ++ u32 backplane_flag_nr; ++ u32 value; ++ struct bcm43xx_coreinfo *old_core; ++ int err = 0; ++ ++ value = bcm43xx_read32(bcm, BCM43xx_CIR_SBTPSFLAG); ++ backplane_flag_nr = value & BCM43xx_BACKPLANE_FLAG_NR_MASK; ++ ++ old_core = bcm->current_core; ++ err = bcm43xx_switch_core(bcm, &bcm->core_pci); ++ if (err) ++ goto out; ++ ++ if (bcm->core_pci.rev < 6) { ++ value = bcm43xx_read32(bcm, BCM43xx_CIR_SBINTVEC); ++ value |= (1 << backplane_flag_nr); ++ bcm43xx_write32(bcm, BCM43xx_CIR_SBINTVEC, value); ++ } else { ++ err = bcm43xx_pci_read_config32(bcm, BCM43xx_PCICFG_ICR, &value); ++ if (err) { ++ printk(KERN_ERR PFX "Error: ICR setup failure!\n"); ++ goto out_switch_back; ++ } ++ value |= core_mask << 8; ++ err = bcm43xx_pci_write_config32(bcm, BCM43xx_PCICFG_ICR, value); ++ if (err) { ++ printk(KERN_ERR PFX "Error: ICR setup failure!\n"); ++ goto out_switch_back; ++ } ++ } ++ ++ value = bcm43xx_read32(bcm, BCM43xx_PCICORE_SBTOPCI2); ++ value |= BCM43xx_SBTOPCI2_PREFETCH | BCM43xx_SBTOPCI2_BURST; ++ bcm43xx_write32(bcm, BCM43xx_PCICORE_SBTOPCI2, value); ++ ++ if (bcm->core_pci.rev < 5) { ++ value = bcm43xx_read32(bcm, BCM43xx_CIR_SBIMCONFIGLOW); ++ value |= (2 << BCM43xx_SBIMCONFIGLOW_SERVICE_TOUT_SHIFT) ++ & BCM43xx_SBIMCONFIGLOW_SERVICE_TOUT_MASK; ++ value |= (3 << BCM43xx_SBIMCONFIGLOW_REQUEST_TOUT_SHIFT) ++ & BCM43xx_SBIMCONFIGLOW_REQUEST_TOUT_MASK; ++ bcm43xx_write32(bcm, BCM43xx_CIR_SBIMCONFIGLOW, value); ++ err = bcm43xx_pcicore_commit_settings(bcm); ++ assert(err == 0); ++ } ++ ++out_switch_back: ++ err = bcm43xx_switch_core(bcm, old_core); ++out: ++ return err; ++} ++ ++static void bcm43xx_periodic_every120sec(struct bcm43xx_private *bcm) ++{ ++ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); ++ ++ if (phy->type != BCM43xx_PHYTYPE_G || phy->rev < 2) ++ return; ++ ++ bcm43xx_mac_suspend(bcm); ++ bcm43xx_phy_lo_g_measure(bcm); ++ bcm43xx_mac_enable(bcm); ++} ++ ++static void bcm43xx_periodic_every60sec(struct bcm43xx_private *bcm) ++{ ++ bcm43xx_phy_lo_mark_all_unused(bcm); ++ if (bcm->sprom.boardflags & BCM43xx_BFL_RSSI) { ++ bcm43xx_mac_suspend(bcm); ++ bcm43xx_calc_nrssi_slope(bcm); ++ bcm43xx_mac_enable(bcm); ++ } ++} ++ ++static void bcm43xx_periodic_every30sec(struct bcm43xx_private *bcm) ++{ ++ /* Update device statistics. */ ++ bcm43xx_calculate_link_quality(bcm); ++} ++ ++static void bcm43xx_periodic_every15sec(struct bcm43xx_private *bcm) ++{ ++ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); ++ struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm); ++ ++ if (phy->type == BCM43xx_PHYTYPE_G) { ++ //TODO: update_aci_moving_average ++ if (radio->aci_enable && radio->aci_wlan_automatic) { ++ bcm43xx_mac_suspend(bcm); ++ if (!radio->aci_enable && 1 /*TODO: not scanning? */) { ++ if (0 /*TODO: bunch of conditions*/) { ++ bcm43xx_radio_set_interference_mitigation(bcm, ++ BCM43xx_RADIO_INTERFMODE_MANUALWLAN); ++ } ++ } else if (1/*TODO*/) { ++ /* ++ if ((aci_average > 1000) && !(bcm43xx_radio_aci_scan(bcm))) { ++ bcm43xx_radio_set_interference_mitigation(bcm, ++ BCM43xx_RADIO_INTERFMODE_NONE); ++ } ++ */ ++ } ++ bcm43xx_mac_enable(bcm); ++ } else if (radio->interfmode == BCM43xx_RADIO_INTERFMODE_NONWLAN && ++ phy->rev == 1) { ++ //TODO: implement rev1 workaround ++ } ++ } ++ bcm43xx_phy_xmitpower(bcm); //FIXME: unless scanning? ++ //TODO for APHY (temperature?) ++} ++ ++static void bcm43xx_periodic_task_handler(unsigned long d) ++{ ++ struct bcm43xx_private *bcm = (struct bcm43xx_private *)d; ++ unsigned long flags; ++ unsigned int state; ++ ++ bcm43xx_lock_mmio(bcm, flags); ++ ++ assert(bcm->initialized); ++ state = bcm->periodic_state; ++ if (state % 8 == 0) ++ bcm43xx_periodic_every120sec(bcm); ++ if (state % 4 == 0) ++ bcm43xx_periodic_every60sec(bcm); ++ if (state % 2 == 0) ++ bcm43xx_periodic_every30sec(bcm); ++ bcm43xx_periodic_every15sec(bcm); ++ bcm->periodic_state = state + 1; ++ ++ mod_timer(&bcm->periodic_tasks, jiffies + (HZ * 15)); ++ ++ bcm43xx_unlock_mmio(bcm, flags); ++} ++ ++static void bcm43xx_periodic_tasks_delete(struct bcm43xx_private *bcm) ++{ ++ del_timer_sync(&bcm->periodic_tasks); ++} ++ ++static void bcm43xx_periodic_tasks_setup(struct bcm43xx_private *bcm) ++{ ++ struct timer_list *timer = &(bcm->periodic_tasks); ++ ++ assert(bcm->initialized); ++ setup_timer(timer, ++ bcm43xx_periodic_task_handler, ++ (unsigned long)bcm); ++ timer->expires = jiffies; ++ add_timer(timer); ++} ++ ++static void bcm43xx_free_modes(struct bcm43xx_private *bcm) ++{ ++ struct ieee80211_hw *ieee = bcm->ieee; ++ int i; ++ ++ for (i = 0; i < ieee->num_modes; i++) { ++ kfree(ieee->modes[i].channels); ++ kfree(ieee->modes[i].rates); ++ } ++ kfree(ieee->modes); ++ ieee->modes = NULL; ++ ieee->num_modes = 0; ++} ++ ++static int bcm43xx_append_mode(struct ieee80211_hw *ieee, ++ int mode_id, ++ int nr_channels, ++ const struct ieee80211_channel *channels, ++ int nr_rates, ++ const struct ieee80211_rate *rates) ++{ ++ struct ieee80211_hw_modes *mode; ++ int err = -ENOMEM; ++ ++ mode = &(ieee->modes[ieee->num_modes]); ++ ++ mode->mode = mode_id; ++ mode->num_channels = nr_channels; ++ mode->channels = kzalloc(sizeof(*channels) * nr_channels, GFP_KERNEL); ++ if (!mode->channels) ++ goto out; ++ memcpy(mode->channels, channels, sizeof(*channels) * nr_channels); ++ ++ mode->num_rates = nr_rates; ++ mode->rates = kzalloc(sizeof(*rates) * nr_rates, GFP_KERNEL); ++ if (!mode->rates) ++ goto err_free_channels; ++ memcpy(mode->rates, rates, sizeof(*rates) * nr_rates); ++ ++ ieee->num_modes++; ++ err = 0; ++out: ++ return err; ++ ++err_free_channels: ++ kfree(mode->channels); ++ goto out; ++} ++ ++static int bcm43xx_setup_modes_aphy(struct bcm43xx_private *bcm) ++{ ++ int err = 0; ++ ++ static const struct ieee80211_rate rates[] = { ++ { ++ .rate = 60, ++ .val = BCM43xx_OFDM_RATE_6MB, ++ .flags = IEEE80211_RATE_OFDM, ++ .val2 = BCM43xx_OFDM_RATE_6MB, ++ }, { ++ .rate = 90, ++ .val = BCM43xx_OFDM_RATE_9MB, ++ .flags = IEEE80211_RATE_OFDM, ++ .val2 = BCM43xx_OFDM_RATE_9MB, ++ }, { ++ .rate = 120, ++ .val = BCM43xx_OFDM_RATE_12MB, ++ .flags = IEEE80211_RATE_OFDM, ++ .val2 = BCM43xx_OFDM_RATE_12MB, ++ }, { ++ .rate = 180, ++ .val = BCM43xx_OFDM_RATE_18MB, ++ .flags = IEEE80211_RATE_OFDM, ++ .val2 = BCM43xx_OFDM_RATE_18MB, ++ }, { ++ .rate = 240, ++ .val = BCM43xx_OFDM_RATE_24MB, ++ .flags = IEEE80211_RATE_OFDM, ++ .val2 = BCM43xx_OFDM_RATE_24MB, ++ }, { ++ .rate = 360, ++ .val = BCM43xx_OFDM_RATE_36MB, ++ .flags = IEEE80211_RATE_OFDM, ++ .val2 = BCM43xx_OFDM_RATE_36MB, ++ }, { ++ .rate = 480, ++ .val = BCM43xx_OFDM_RATE_48MB, ++ .flags = IEEE80211_RATE_OFDM, ++ .val2 = BCM43xx_OFDM_RATE_48MB, ++ }, { ++ .rate = 540, ++ .val = BCM43xx_OFDM_RATE_54MB, ++ .flags = IEEE80211_RATE_OFDM, ++ .val2 = BCM43xx_OFDM_RATE_54MB, ++ }, ++ }; ++ static const struct ieee80211_channel channels[] = { ++ { ++ .chan = 36, ++ .freq = 5180, ++ .val = 36, ++ .flag = IEEE80211_CHAN_W_SCAN | ++ IEEE80211_CHAN_W_ACTIVE_SCAN | ++ IEEE80211_CHAN_W_IBSS, ++ .power_level = 0xFF, ++ .antenna_max = 0xFF, ++ }, { ++ .chan = 40, ++ .freq = 5200, ++ .val = 40, ++ .flag = IEEE80211_CHAN_W_SCAN | ++ IEEE80211_CHAN_W_ACTIVE_SCAN | ++ IEEE80211_CHAN_W_IBSS, ++ .power_level = 0xFF, ++ .antenna_max = 0xFF, ++ }, { ++ .chan = 44, ++ .freq = 5220, ++ .val = 44, ++ .flag = IEEE80211_CHAN_W_SCAN | ++ IEEE80211_CHAN_W_ACTIVE_SCAN | ++ IEEE80211_CHAN_W_IBSS, ++ .power_level = 0xFF, ++ .antenna_max = 0xFF, ++ }, { ++ .chan = 48, ++ .freq = 5240, ++ .val = 48, ++ .flag = IEEE80211_CHAN_W_SCAN | ++ IEEE80211_CHAN_W_ACTIVE_SCAN | ++ IEEE80211_CHAN_W_IBSS, ++ .power_level = 0xFF, ++ .antenna_max = 0xFF, ++ }, { ++ .chan = 52, ++ .freq = 5260, ++ .val = 52, ++ .flag = IEEE80211_CHAN_W_SCAN | ++ IEEE80211_CHAN_W_ACTIVE_SCAN | ++ IEEE80211_CHAN_W_IBSS, ++ .power_level = 0xFF, ++ .antenna_max = 0xFF, ++ }, { ++ .chan = 56, ++ .freq = 5280, ++ .val = 56, ++ .flag = IEEE80211_CHAN_W_SCAN | ++ IEEE80211_CHAN_W_ACTIVE_SCAN | ++ IEEE80211_CHAN_W_IBSS, ++ .power_level = 0xFF, ++ .antenna_max = 0xFF, ++ }, { ++ .chan = 60, ++ .freq = 5300, ++ .val = 60, ++ .flag = IEEE80211_CHAN_W_SCAN | ++ IEEE80211_CHAN_W_ACTIVE_SCAN | ++ IEEE80211_CHAN_W_IBSS, ++ .power_level = 0xFF, ++ .antenna_max = 0xFF, ++ }, { ++ .chan = 64, ++ .freq = 5320, ++ .val = 64, ++ .flag = IEEE80211_CHAN_W_SCAN | ++ IEEE80211_CHAN_W_ACTIVE_SCAN | ++ IEEE80211_CHAN_W_IBSS, ++ .power_level = 0xFF, ++ .antenna_max = 0xFF, ++ }, { ++ .chan = 149, ++ .freq = 5745, ++ .val = 149, ++ .flag = IEEE80211_CHAN_W_SCAN | ++ IEEE80211_CHAN_W_ACTIVE_SCAN | ++ IEEE80211_CHAN_W_IBSS, ++ .power_level = 0xFF, ++ .antenna_max = 0xFF, ++ }, { ++ .chan = 153, ++ .freq = 5765, ++ .val = 153, ++ .flag = IEEE80211_CHAN_W_SCAN | ++ IEEE80211_CHAN_W_ACTIVE_SCAN | ++ IEEE80211_CHAN_W_IBSS, ++ .power_level = 0xFF, ++ .antenna_max = 0xFF, ++ }, { ++ .chan = 157, ++ .freq = 5785, ++ .val = 157, ++ .flag = IEEE80211_CHAN_W_SCAN | ++ IEEE80211_CHAN_W_ACTIVE_SCAN | ++ IEEE80211_CHAN_W_IBSS, ++ .power_level = 0xFF, ++ .antenna_max = 0xFF, ++ }, { ++ .chan = 161, ++ .freq = 5805, ++ .val = 161, ++ .flag = IEEE80211_CHAN_W_SCAN | ++ IEEE80211_CHAN_W_ACTIVE_SCAN | ++ IEEE80211_CHAN_W_IBSS, ++ .power_level = 0xFF, ++ .antenna_max = 0xFF, ++ }, { ++ .chan = 165, ++ .freq = 5825, ++ .val = 165, ++ .flag = IEEE80211_CHAN_W_SCAN | ++ IEEE80211_CHAN_W_ACTIVE_SCAN | ++ IEEE80211_CHAN_W_IBSS, ++ .power_level = 0xFF, ++ .antenna_max = 0xFF, ++ }, ++ }; ++ ++ if (bcm43xx_current_phy(bcm)->type == BCM43xx_PHYTYPE_A) { ++ err = bcm43xx_append_mode(bcm->ieee, MODE_IEEE80211A, ++ ARRAY_SIZE(channels), channels, ++ ARRAY_SIZE(rates), rates); ++ } ++ ++ return err; ++} ++ ++static int bcm43xx_setup_modes_bphy(struct bcm43xx_private *bcm) ++{ ++ int err = 0; ++ ++ static const struct ieee80211_rate rates[] = { ++ { ++ .rate = 10, ++ .val = BCM43xx_CCK_RATE_1MB, ++ .flags = IEEE80211_RATE_CCK, ++ .val2 = BCM43xx_CCK_RATE_1MB, ++ }, { ++ .rate = 20, ++ .val = BCM43xx_CCK_RATE_2MB, ++ .flags = IEEE80211_RATE_CCK_2, ++ .val2 = BCM43xx_CCK_RATE_2MB, ++ }, { ++ .rate = 55, ++ .val = BCM43xx_CCK_RATE_5MB, ++ .flags = IEEE80211_RATE_CCK_2, ++ .val2 = BCM43xx_CCK_RATE_5MB, ++ }, { ++ .rate = 110, ++ .val = BCM43xx_CCK_RATE_11MB, ++ .flags = IEEE80211_RATE_CCK_2, ++ .val2 = BCM43xx_CCK_RATE_11MB, ++ }, ++ }; ++ static const struct ieee80211_channel channels[] = { ++ { ++ .chan = 1, ++ .freq = 2412, ++ .val = 1, ++ .flag = IEEE80211_CHAN_W_SCAN | ++ IEEE80211_CHAN_W_ACTIVE_SCAN | ++ IEEE80211_CHAN_W_IBSS, ++ .power_level = 0xFF, ++ .antenna_max = 0xFF, ++ }, { ++ .chan = 2, ++ .freq = 2417, ++ .val = 2, ++ .flag = IEEE80211_CHAN_W_SCAN | ++ IEEE80211_CHAN_W_ACTIVE_SCAN | ++ IEEE80211_CHAN_W_IBSS, ++ .power_level = 0xFF, ++ .antenna_max = 0xFF, ++ }, { ++ .chan = 3, ++ .freq = 2422, ++ .val = 3, ++ .flag = IEEE80211_CHAN_W_SCAN | ++ IEEE80211_CHAN_W_ACTIVE_SCAN | ++ IEEE80211_CHAN_W_IBSS, ++ .power_level = 0xFF, ++ .antenna_max = 0xFF, ++ }, { ++ .chan = 4, ++ .freq = 2427, ++ .val = 4, ++ .flag = IEEE80211_CHAN_W_SCAN | ++ IEEE80211_CHAN_W_ACTIVE_SCAN | ++ IEEE80211_CHAN_W_IBSS, ++ .power_level = 0xFF, ++ .antenna_max = 0xFF, ++ }, { ++ .chan = 5, ++ .freq = 2432, ++ .val = 5, ++ .flag = IEEE80211_CHAN_W_SCAN | ++ IEEE80211_CHAN_W_ACTIVE_SCAN | ++ IEEE80211_CHAN_W_IBSS, ++ .power_level = 0xFF, ++ .antenna_max = 0xFF, ++ }, { ++ .chan = 6, ++ .freq = 2437, ++ .val = 6, ++ .flag = IEEE80211_CHAN_W_SCAN | ++ IEEE80211_CHAN_W_ACTIVE_SCAN | ++ IEEE80211_CHAN_W_IBSS, ++ .power_level = 0xFF, ++ .antenna_max = 0xFF, ++ }, { ++ .chan = 7, ++ .freq = 2442, ++ .val = 7, ++ .flag = IEEE80211_CHAN_W_SCAN | ++ IEEE80211_CHAN_W_ACTIVE_SCAN | ++ IEEE80211_CHAN_W_IBSS, ++ .power_level = 0xFF, ++ .antenna_max = 0xFF, ++ }, { ++ .chan = 8, ++ .freq = 2447, ++ .val = 8, ++ .flag = IEEE80211_CHAN_W_SCAN | ++ IEEE80211_CHAN_W_ACTIVE_SCAN | ++ IEEE80211_CHAN_W_IBSS, ++ .power_level = 0xFF, ++ .antenna_max = 0xFF, ++ }, { ++ .chan = 9, ++ .freq = 2452, ++ .val = 9, ++ .flag = IEEE80211_CHAN_W_SCAN | ++ IEEE80211_CHAN_W_ACTIVE_SCAN | ++ IEEE80211_CHAN_W_IBSS, ++ .power_level = 0xFF, ++ .antenna_max = 0xFF, ++ }, { ++ .chan = 10, ++ .freq = 2457, ++ .val = 10, ++ .flag = IEEE80211_CHAN_W_SCAN | ++ IEEE80211_CHAN_W_ACTIVE_SCAN | ++ IEEE80211_CHAN_W_IBSS, ++ .power_level = 0xFF, ++ .antenna_max = 0xFF, ++ }, { ++ .chan = 11, ++ .freq = 2462, ++ .val = 11, ++ .flag = IEEE80211_CHAN_W_SCAN | ++ IEEE80211_CHAN_W_ACTIVE_SCAN | ++ IEEE80211_CHAN_W_IBSS, ++ .power_level = 0xFF, ++ .antenna_max = 0xFF, ++ }, { ++ .chan = 12, ++ .freq = 2467, ++ .val = 12, ++ .flag = IEEE80211_CHAN_W_SCAN | ++ IEEE80211_CHAN_W_ACTIVE_SCAN | ++ IEEE80211_CHAN_W_IBSS, ++ .power_level = 0xFF, ++ .antenna_max = 0xFF, ++ }, { ++ .chan = 13, ++ .freq = 2472, ++ .val = 13, ++ .flag = IEEE80211_CHAN_W_SCAN | ++ IEEE80211_CHAN_W_ACTIVE_SCAN | ++ IEEE80211_CHAN_W_IBSS, ++ .power_level = 0xFF, ++ .antenna_max = 0xFF, ++ }, /*{ ++ .chan = 14, ++ .freq = 2484, ++ .val = 14, ++ .flag = IEEE80211_CHAN_W_SCAN | ++ IEEE80211_CHAN_W_ACTIVE_SCAN | ++ IEEE80211_CHAN_W_IBSS, ++ .power_level = 0xFF, ++ .antenna_max = 0xFF, ++ },*/ ++ }; ++ ++ if (bcm43xx_current_phy(bcm)->type == BCM43xx_PHYTYPE_B || ++ bcm43xx_current_phy(bcm)->type == BCM43xx_PHYTYPE_G) { ++ err = bcm43xx_append_mode(bcm->ieee, MODE_IEEE80211B, ++ ARRAY_SIZE(channels), channels, ++ ARRAY_SIZE(rates), rates); ++ } ++ ++ return err; ++} ++ ++static int bcm43xx_setup_modes_gphy(struct bcm43xx_private *bcm) ++{ ++ int err = 0; ++ ++ static const struct ieee80211_rate rates[] = { ++ { ++ .rate = 10, ++ .val = BCM43xx_CCK_RATE_1MB, ++ .flags = IEEE80211_RATE_CCK, ++ .val2 = BCM43xx_CCK_RATE_1MB, ++ }, { ++ .rate = 20, ++ .val = BCM43xx_CCK_RATE_2MB, ++ .flags = IEEE80211_RATE_CCK_2, ++ .val2 = BCM43xx_CCK_RATE_2MB, ++ }, { ++ .rate = 55, ++ .val = BCM43xx_CCK_RATE_5MB, ++ .flags = IEEE80211_RATE_CCK_2, ++ .val2 = BCM43xx_CCK_RATE_5MB, ++ }, { ++ .rate = 60, ++ .val = BCM43xx_OFDM_RATE_6MB, ++ .flags = IEEE80211_RATE_OFDM, ++ .val2 = BCM43xx_OFDM_RATE_6MB, ++ }, { ++ .rate = 90, ++ .val = BCM43xx_OFDM_RATE_9MB, ++ .flags = IEEE80211_RATE_OFDM, ++ .val2 = BCM43xx_OFDM_RATE_9MB, ++ }, { ++ .rate = 110, ++ .val = BCM43xx_CCK_RATE_11MB, ++ .flags = IEEE80211_RATE_CCK_2, ++ .val2 = BCM43xx_CCK_RATE_11MB, ++ }, { ++ .rate = 120, ++ .val = BCM43xx_OFDM_RATE_12MB, ++ .flags = IEEE80211_RATE_OFDM, ++ .val2 = BCM43xx_OFDM_RATE_12MB, ++ }, { ++ .rate = 180, ++ .val = BCM43xx_OFDM_RATE_18MB, ++ .flags = IEEE80211_RATE_OFDM, ++ .val2 = BCM43xx_OFDM_RATE_18MB, ++ }, { ++ .rate = 240, ++ .val = BCM43xx_OFDM_RATE_24MB, ++ .flags = IEEE80211_RATE_OFDM, ++ .val2 = BCM43xx_OFDM_RATE_24MB, ++ }, { ++ .rate = 360, ++ .val = BCM43xx_OFDM_RATE_36MB, ++ .flags = IEEE80211_RATE_OFDM, ++ .val2 = BCM43xx_OFDM_RATE_36MB, ++ }, { ++ .rate = 480, ++ .val = BCM43xx_OFDM_RATE_48MB, ++ .flags = IEEE80211_RATE_OFDM, ++ .val2 = BCM43xx_OFDM_RATE_48MB, ++ }, { ++ .rate = 540, ++ .val = BCM43xx_OFDM_RATE_54MB, ++ .flags = IEEE80211_RATE_OFDM, ++ .val2 = BCM43xx_OFDM_RATE_54MB, ++ }, ++ }; ++ static const struct ieee80211_channel channels[] = { ++ { ++ .chan = 1, ++ .freq = 2412, ++ .val = 1, ++ .flag = IEEE80211_CHAN_W_SCAN | ++ IEEE80211_CHAN_W_ACTIVE_SCAN | ++ IEEE80211_CHAN_W_IBSS, ++ .power_level = 0xFF, ++ .antenna_max = 0xFF, ++ }, { ++ .chan = 2, ++ .freq = 2417, ++ .val = 2, ++ .flag = IEEE80211_CHAN_W_SCAN | ++ IEEE80211_CHAN_W_ACTIVE_SCAN | ++ IEEE80211_CHAN_W_IBSS, ++ .power_level = 0xFF, ++ .antenna_max = 0xFF, ++ }, { ++ .chan = 3, ++ .freq = 2422, ++ .val = 3, ++ .flag = IEEE80211_CHAN_W_SCAN | ++ IEEE80211_CHAN_W_ACTIVE_SCAN | ++ IEEE80211_CHAN_W_IBSS, ++ .power_level = 0xFF, ++ .antenna_max = 0xFF, ++ }, { ++ .chan = 4, ++ .freq = 2427, ++ .val = 4, ++ .flag = IEEE80211_CHAN_W_SCAN | ++ IEEE80211_CHAN_W_ACTIVE_SCAN | ++ IEEE80211_CHAN_W_IBSS, ++ .power_level = 0xFF, ++ .antenna_max = 0xFF, ++ }, { ++ .chan = 5, ++ .freq = 2432, ++ .val = 5, ++ .flag = IEEE80211_CHAN_W_SCAN | ++ IEEE80211_CHAN_W_ACTIVE_SCAN | ++ IEEE80211_CHAN_W_IBSS, ++ .power_level = 0xFF, ++ .antenna_max = 0xFF, ++ }, { ++ .chan = 6, ++ .freq = 2437, ++ .val = 6, ++ .flag = IEEE80211_CHAN_W_SCAN | ++ IEEE80211_CHAN_W_ACTIVE_SCAN | ++ IEEE80211_CHAN_W_IBSS, ++ .power_level = 0xFF, ++ .antenna_max = 0xFF, ++ }, { ++ .chan = 7, ++ .freq = 2442, ++ .val = 7, ++ .flag = IEEE80211_CHAN_W_SCAN | ++ IEEE80211_CHAN_W_ACTIVE_SCAN | ++ IEEE80211_CHAN_W_IBSS, ++ .power_level = 0xFF, ++ .antenna_max = 0xFF, ++ }, { ++ .chan = 8, ++ .freq = 2447, ++ .val = 8, ++ .flag = IEEE80211_CHAN_W_SCAN | ++ IEEE80211_CHAN_W_ACTIVE_SCAN | ++ IEEE80211_CHAN_W_IBSS, ++ .power_level = 0xFF, ++ .antenna_max = 0xFF, ++ }, { ++ .chan = 9, ++ .freq = 2452, ++ .val = 9, ++ .flag = IEEE80211_CHAN_W_SCAN | ++ IEEE80211_CHAN_W_ACTIVE_SCAN | ++ IEEE80211_CHAN_W_IBSS, ++ .power_level = 0xFF, ++ .antenna_max = 0xFF, ++ }, { ++ .chan = 10, ++ .freq = 2457, ++ .val = 10, ++ .flag = IEEE80211_CHAN_W_SCAN | ++ IEEE80211_CHAN_W_ACTIVE_SCAN | ++ IEEE80211_CHAN_W_IBSS, ++ .power_level = 0xFF, ++ .antenna_max = 0xFF, ++ }, { ++ .chan = 11, ++ .freq = 2462, ++ .val = 11, ++ .flag = IEEE80211_CHAN_W_SCAN | ++ IEEE80211_CHAN_W_ACTIVE_SCAN | ++ IEEE80211_CHAN_W_IBSS, ++ .power_level = 0xFF, ++ .antenna_max = 0xFF, ++ }, { ++ .chan = 12, ++ .freq = 2467, ++ .val = 12, ++ .flag = IEEE80211_CHAN_W_SCAN | ++ IEEE80211_CHAN_W_ACTIVE_SCAN | ++ IEEE80211_CHAN_W_IBSS, ++ .power_level = 0xFF, ++ .antenna_max = 0xFF, ++ }, { ++ .chan = 13, ++ .freq = 2472, ++ .val = 13, ++ .flag = IEEE80211_CHAN_W_SCAN | ++ IEEE80211_CHAN_W_ACTIVE_SCAN | ++ IEEE80211_CHAN_W_IBSS, ++ .power_level = 0xFF, ++ .antenna_max = 0xFF, ++ }, /*{ ++ .chan = 14, ++ .freq = 2484, ++ .val = 14, ++ .flag = IEEE80211_CHAN_W_SCAN | ++ IEEE80211_CHAN_W_ACTIVE_SCAN | ++ IEEE80211_CHAN_W_IBSS, ++ .power_level = 0xFF, ++ .antenna_max = 0xFF, ++ },*/ ++ }; ++ ++ if (bcm43xx_current_phy(bcm)->type == BCM43xx_PHYTYPE_G) { ++ err = bcm43xx_append_mode(bcm->ieee, MODE_IEEE80211G, ++ ARRAY_SIZE(channels), channels, ++ ARRAY_SIZE(rates), rates); ++ } ++ ++ return err; ++} ++ ++static int bcm43xx_setup_modes(struct bcm43xx_private *bcm) ++{ ++ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); ++ int err = -ENOMEM; ++ int nr; ++ struct ieee80211_hw *ieee = bcm->ieee; ++ ++ if (phy->type == BCM43xx_PHYTYPE_A) ++ nr = 1; ++ else if (phy->type == BCM43xx_PHYTYPE_B) ++ nr = 1; ++ else ++ nr = 2; ++ ieee->modes = kzalloc(sizeof(*(ieee->modes)) * nr, GFP_KERNEL); ++ if (!ieee->modes) ++ goto out; ++ ieee->num_modes = 0; ++ ++ err = bcm43xx_setup_modes_aphy(bcm); ++ if (err) ++ goto error; ++ err = bcm43xx_setup_modes_gphy(bcm); ++ if (err) ++ goto error; ++ err = bcm43xx_setup_modes_bphy(bcm); ++ if (err) ++ goto error; ++ ++ assert(ieee->num_modes == nr && nr > 0); ++out: ++ return err; ++ ++error: ++ bcm43xx_free_modes(bcm); ++ goto out; ++} ++ ++static void bcm43xx_security_init(struct bcm43xx_private *bcm) ++{ ++ bcm->security_offset = bcm43xx_shm_read16(bcm, BCM43xx_SHM_SHARED, ++ 0x0056) * 2; ++ bcm43xx_clear_keys(bcm); ++} ++ ++/* This is the opposite of bcm43xx_init_board() */ ++static void bcm43xx_free_board(struct bcm43xx_private *bcm) ++{ ++ int i, err; ++ unsigned long flags; ++ ++ bcm43xx_sysfs_unregister(bcm); ++ ++ bcm43xx_periodic_tasks_delete(bcm); ++ ++ bcm43xx_lock(bcm, flags); ++ bcm->initialized = 0; ++ bcm->shutting_down = 1; ++ bcm43xx_unlock(bcm, flags); ++ ++ for (i = 0; i < BCM43xx_MAX_80211_CORES; i++) { ++ if (!bcm->core_80211[i].available) ++ continue; ++ if (!bcm->core_80211[i].initialized) ++ continue; ++ ++ err = bcm43xx_switch_core(bcm, &bcm->core_80211[i]); ++ assert(err == 0); ++ bcm43xx_wireless_core_cleanup(bcm); ++ } ++ ++ bcm43xx_pctl_set_crystal(bcm, 0); ++ bcm43xx_free_modes(bcm); ++ ++ bcm43xx_lock(bcm, flags); ++ bcm->shutting_down = 0; ++ bcm43xx_unlock(bcm, flags); ++} ++ ++static int bcm43xx_init_board(struct bcm43xx_private *bcm) ++{ ++ int i, err; ++ int connect_phy; ++ unsigned long flags; ++ ++ might_sleep(); ++ ++ bcm43xx_lock(bcm, flags); ++ bcm->initialized = 0; ++ bcm->shutting_down = 0; ++ bcm43xx_unlock(bcm, flags); ++ ++ err = bcm43xx_pctl_set_crystal(bcm, 1); ++ if (err) ++ goto out; ++ err = bcm43xx_pctl_init(bcm); ++ if (err) ++ goto err_crystal_off; ++ err = bcm43xx_pctl_set_clock(bcm, BCM43xx_PCTL_CLK_FAST); ++ if (err) ++ goto err_crystal_off; ++ ++ tasklet_enable(&bcm->isr_tasklet); ++ for (i = 0; i < bcm->nr_80211_available; i++) { ++ err = bcm43xx_switch_core(bcm, &bcm->core_80211[i]); ++ assert(err != -ENODEV); ++ if (err) ++ goto err_80211_unwind; ++ ++ /* Enable the selected wireless core. ++ * Connect PHY only on the first core. ++ */ ++ if (!bcm43xx_core_enabled(bcm)) { ++ if (bcm->nr_80211_available == 1) { ++ connect_phy = bcm43xx_current_phy(bcm)->connected; ++ } else { ++ if (i == 0) ++ connect_phy = 1; ++ else ++ connect_phy = 0; ++ } ++ bcm43xx_wireless_core_reset(bcm, connect_phy); ++ } ++ ++ if (i != 0) ++ bcm43xx_wireless_core_mark_inactive(bcm, &bcm->core_80211[0]); ++ ++ err = bcm43xx_wireless_core_init(bcm); ++ if (err) ++ goto err_80211_unwind; ++ ++ if (i != 0) { ++ bcm43xx_mac_suspend(bcm); ++ bcm43xx_interrupt_disable(bcm, BCM43xx_IRQ_ALL); ++ bcm43xx_radio_turn_off(bcm); ++ } ++ } ++ bcm->active_80211_core = &bcm->core_80211[0]; ++ if (bcm->nr_80211_available >= 2) { ++ bcm43xx_switch_core(bcm, &bcm->core_80211[0]); ++ bcm43xx_mac_enable(bcm); ++ } ++ bcm43xx_macfilter_clear(bcm, BCM43xx_MACFILTER_ASSOC); ++ bcm43xx_macfilter_set(bcm, BCM43xx_MACFILTER_SELF, (u8 *)(bcm->net_dev->dev_addr)); ++ dprintk(KERN_INFO PFX "80211 cores initialized\n"); ++ bcm43xx_setup_modes(bcm); ++ bcm43xx_security_init(bcm); ++ ieee80211_update_hw(bcm->net_dev, bcm->ieee); ++ ieee80211_netif_oper(bcm->net_dev, NETIF_ATTACH); ++ ieee80211_netif_oper(bcm->net_dev, NETIF_START); ++ ieee80211_netif_oper(bcm->net_dev, NETIF_WAKE); ++ ++ bcm43xx_pctl_set_clock(bcm, BCM43xx_PCTL_CLK_DYNAMIC); ++ ++ if (bcm43xx_current_radio(bcm)->initial_channel != 0xFF) { ++ bcm43xx_mac_suspend(bcm); ++ bcm43xx_radio_selectchannel(bcm, bcm43xx_current_radio(bcm)->initial_channel, 0); ++ bcm43xx_mac_enable(bcm); ++ } ++ ++ /* Initialization of the board is done. Flag it as such. */ ++ bcm43xx_lock(bcm, flags); ++ bcm->initialized = 1; ++ bcm43xx_unlock(bcm, flags); ++ ++ bcm43xx_periodic_tasks_setup(bcm); ++ bcm43xx_sysfs_register(bcm); ++ ++ assert(err == 0); ++out: ++ return err; ++ ++err_80211_unwind: ++ tasklet_disable(&bcm->isr_tasklet); ++ /* unwind all 80211 initialization */ ++ for (i = 0; i < bcm->nr_80211_available; i++) { ++ if (!bcm->core_80211[i].initialized) ++ continue; ++ bcm43xx_interrupt_disable(bcm, BCM43xx_IRQ_ALL); ++ bcm43xx_wireless_core_cleanup(bcm); ++ } ++err_crystal_off: ++ bcm43xx_pctl_set_crystal(bcm, 0); ++ goto out; ++} ++ ++static void bcm43xx_detach_board(struct bcm43xx_private *bcm) ++{ ++ struct pci_dev *pci_dev = bcm->pci_dev; ++ int i; ++ ++ bcm43xx_chipset_detach(bcm); ++ /* Do _not_ access the chip, after it is detached. */ ++ iounmap(bcm->mmio_addr); ++ ++ pci_release_regions(pci_dev); ++ pci_disable_device(pci_dev); ++ ++ /* Free allocated structures/fields */ ++ for (i = 0; i < BCM43xx_MAX_80211_CORES; i++) { ++ kfree(bcm->core_80211_ext[i].phy._lo_pairs); ++ if (bcm->core_80211_ext[i].phy.dyn_tssi_tbl) ++ kfree(bcm->core_80211_ext[i].phy.tssi2dbm); ++ } ++} ++ ++static int bcm43xx_read_phyinfo(struct bcm43xx_private *bcm) ++{ ++ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); ++ u16 value; ++ u8 phy_version; ++ u8 phy_type; ++ u8 phy_rev; ++ int phy_rev_ok = 1; ++ void *p; ++ ++ value = bcm43xx_read16(bcm, BCM43xx_MMIO_PHY_VER); ++ ++ phy_version = (value & 0xF000) >> 12; ++ phy_type = (value & 0x0F00) >> 8; ++ phy_rev = (value & 0x000F); ++ ++ dprintk(KERN_INFO PFX "Detected PHY: Version: %x, Type %x, Revision %x\n", ++ phy_version, phy_type, phy_rev); ++ ++ switch (phy_type) { ++ case BCM43xx_PHYTYPE_A: ++ if (phy_rev >= 4) ++ phy_rev_ok = 0; ++ break; ++ case BCM43xx_PHYTYPE_B: ++ if (phy_rev != 2 && phy_rev != 4 && phy_rev != 6 && phy_rev != 7) ++ phy_rev_ok = 0; ++ break; ++ case BCM43xx_PHYTYPE_G: ++ if (phy_rev > 7) ++ phy_rev_ok = 0; ++ break; ++ default: ++ printk(KERN_ERR PFX "Error: Unknown PHY Type %x\n", ++ phy_type); ++ return -ENODEV; ++ }; ++ if (!phy_rev_ok) { ++ printk(KERN_WARNING PFX "Invalid PHY Revision %x\n", ++ phy_rev); ++ } ++ ++ phy->version = phy_version; ++ phy->type = phy_type; ++ phy->rev = phy_rev; ++ if ((phy_type == BCM43xx_PHYTYPE_B) || (phy_type == BCM43xx_PHYTYPE_G)) { ++ p = kzalloc(sizeof(struct bcm43xx_lopair) * BCM43xx_LO_COUNT, ++ GFP_KERNEL); ++ if (!p) ++ return -ENOMEM; ++ phy->_lo_pairs = p; ++ } ++ ++ return 0; ++} ++ ++static int bcm43xx_attach_board(struct bcm43xx_private *bcm) ++{ ++ struct pci_dev *pci_dev = bcm->pci_dev; ++ struct net_device *net_dev = bcm->net_dev; ++ int err; ++ int i; ++ unsigned long mmio_start, mmio_flags, mmio_len; ++ u32 coremask; ++ ++ err = pci_enable_device(pci_dev); ++ if (err) { ++ printk(KERN_ERR PFX "unable to wake up pci device (%i)\n", err); ++ goto out; ++ } ++ mmio_start = pci_resource_start(pci_dev, 0); ++ mmio_flags = pci_resource_flags(pci_dev, 0); ++ mmio_len = pci_resource_len(pci_dev, 0); ++ if (!(mmio_flags & IORESOURCE_MEM)) { ++ printk(KERN_ERR PFX ++ "%s, region #0 not an MMIO resource, aborting\n", ++ pci_name(pci_dev)); ++ err = -ENODEV; ++ goto err_pci_disable; ++ } ++ err = pci_request_regions(pci_dev, KBUILD_MODNAME); ++ if (err) { ++ printk(KERN_ERR PFX ++ "could not access PCI resources (%i)\n", err); ++ goto err_pci_disable; ++ } ++ /* enable PCI bus-mastering */ ++ pci_set_master(pci_dev); ++ bcm->mmio_addr = ioremap(mmio_start, mmio_len); ++ if (!bcm->mmio_addr) { ++ printk(KERN_ERR PFX "%s: cannot remap MMIO, aborting\n", ++ pci_name(pci_dev)); ++ err = -EIO; ++ goto err_pci_release; ++ } ++ bcm->mmio_len = mmio_len; ++ net_dev->base_addr = (unsigned long)bcm->mmio_addr; ++ ++ bcm43xx_pci_read_config16(bcm, PCI_SUBSYSTEM_VENDOR_ID, ++ &bcm->board_vendor); ++ bcm43xx_pci_read_config16(bcm, PCI_SUBSYSTEM_ID, ++ &bcm->board_type); ++ bcm43xx_pci_read_config16(bcm, PCI_REVISION_ID, ++ &bcm->board_revision); ++ ++ err = bcm43xx_chipset_attach(bcm); ++ if (err) ++ goto err_iounmap; ++ err = bcm43xx_pctl_init(bcm); ++ if (err) ++ goto err_chipset_detach; ++ err = bcm43xx_probe_cores(bcm); ++ if (err) ++ goto err_chipset_detach; ++ ++ /* Attach all IO cores to the backplane. */ ++ coremask = 0; ++ for (i = 0; i < bcm->nr_80211_available; i++) ++ coremask |= (1 << bcm->core_80211[i].index); ++ //FIXME: Also attach some non80211 cores? ++ err = bcm43xx_setup_backplane_pci_connection(bcm, coremask); ++ if (err) { ++ printk(KERN_ERR PFX "Backplane->PCI connection failed!\n"); ++ goto err_chipset_detach; ++ } ++ ++ err = bcm43xx_sprom_extract(bcm); ++ if (err) ++ goto err_chipset_detach; ++ err = bcm43xx_leds_init(bcm); ++ if (err) ++ goto err_chipset_detach; ++ ++ for (i = 0; i < bcm->nr_80211_available; i++) { ++ err = bcm43xx_switch_core(bcm, &bcm->core_80211[i]); ++ assert(err != -ENODEV); ++ if (err) ++ goto err_80211_unwind; ++ ++ /* Enable the selected wireless core. ++ * Connect PHY only on the first core. ++ */ ++ bcm43xx_wireless_core_reset(bcm, (i == 0)); ++ ++ err = bcm43xx_read_phyinfo(bcm); ++ if (err && (i == 0)) ++ goto err_80211_unwind; ++ ++ err = bcm43xx_read_radioinfo(bcm); ++ if (err && (i == 0)) ++ goto err_80211_unwind; ++ ++ err = bcm43xx_validate_chip(bcm); ++ if (err && (i == 0)) ++ goto err_80211_unwind; ++ ++ bcm43xx_radio_turn_off(bcm); ++ err = bcm43xx_phy_init_tssi2dbm_table(bcm); ++ if (err) ++ goto err_80211_unwind; ++ bcm43xx_wireless_core_disable(bcm); ++ } ++ bcm43xx_pctl_set_crystal(bcm, 0); ++ ++ /* Set the MAC address in the networking subsystem */ ++ if (bcm43xx_current_phy(bcm)->type == BCM43xx_PHYTYPE_A) ++ memcpy(bcm->net_dev->dev_addr, bcm->sprom.et1macaddr, 6); ++ else ++ memcpy(bcm->net_dev->dev_addr, bcm->sprom.il0macaddr, 6); ++ ++ snprintf(bcm->nick, IW_ESSID_MAX_SIZE, ++ "Broadcom %04X", bcm->chip_id); ++ ++ assert(err == 0); ++out: ++ return err; ++ ++err_80211_unwind: ++ for (i = 0; i < BCM43xx_MAX_80211_CORES; i++) { ++ kfree(bcm->core_80211_ext[i].phy._lo_pairs); ++ if (bcm->core_80211_ext[i].phy.dyn_tssi_tbl) ++ kfree(bcm->core_80211_ext[i].phy.tssi2dbm); ++ } ++err_chipset_detach: ++ bcm43xx_chipset_detach(bcm); ++err_iounmap: ++ iounmap(bcm->mmio_addr); ++err_pci_release: ++ pci_release_regions(pci_dev); ++err_pci_disable: ++ pci_disable_device(pci_dev); ++ goto out; ++} ++ ++/* hard_start_xmit() callback in struct ieee80211_device */ ++static int bcm43xx_net_hard_start_xmit(struct net_device *net_dev, ++ struct sk_buff *skb, ++ struct ieee80211_tx_control *ctl) ++{ ++ struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); ++ int err = -ENODEV; ++ unsigned long flags; ++ ++ bcm43xx_lock_mmio(bcm, flags); ++ if (likely(bcm->initialized)) { ++ if (bcm43xx_using_pio(bcm)) ++ err = bcm43xx_pio_tx(bcm, skb, ctl); ++ else ++ err = bcm43xx_dma_tx(bcm, skb, ctl); ++ } ++ bcm43xx_unlock_mmio(bcm, flags); ++ ++ return err; ++} ++ ++static int bcm43xx_net_reset(struct net_device *net_dev) ++{ ++ struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); ++ unsigned long flags; ++ ++ bcm43xx_lock_mmio(bcm, flags); ++ bcm43xx_controller_restart(bcm, "IEEE reset"); ++ bcm43xx_unlock_mmio(bcm, flags); ++ ++ return 0; ++} ++ ++static int bcm43xx_net_config(struct net_device *net_dev, ++ struct ieee80211_conf *conf) ++{ ++ struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); ++ struct bcm43xx_radioinfo *radio; ++ struct bcm43xx_phyinfo *phy; ++ unsigned long flags; ++ ++ bcm43xx_lock_mmio(bcm, flags); ++ if (!bcm->initialized) { ++ bcm43xx_unlock_mmio(bcm, flags); ++ return 0; ++ } ++ radio = bcm43xx_current_radio(bcm); ++ phy = bcm43xx_current_phy(bcm); ++ ++ if (conf->channel != radio->channel) ++ bcm43xx_radio_selectchannel(bcm, conf->channel, 0); ++ ++ if (conf->mode != bcm->iw_mode) ++ bcm43xx_set_iwmode(bcm, conf->mode); ++ ++ if (conf->short_slot_time != bcm->short_slot) { ++ assert(phy->type == BCM43xx_PHYTYPE_G); ++ if (conf->short_slot_time) ++ bcm43xx_short_slot_timing_enable(bcm); ++ else ++ bcm43xx_short_slot_timing_disable(bcm); ++ } ++ ++ if (conf->power_level != 0) { ++ radio->power_level = conf->power_level; ++ bcm43xx_phy_xmitpower(bcm); ++ } ++//FIXME: This does not seem to wake up: ++#if 0 ++ if (conf->power_level == 0) { ++ if (radio->enabled) ++ bcm43xx_radio_turn_off(bcm); ++ } else { ++ if (!radio->enabled) ++ bcm43xx_radio_turn_on(bcm); ++ } ++#endif ++ ++ //TODO: phymode ++ //TODO: antennas ++ ++ bcm43xx_unlock_mmio(bcm, flags); ++ ++ return 0; ++} ++ ++static int bcm43xx_net_set_key(struct net_device *net_dev, ++ set_key_cmd cmd, ++ u8 *addr, ++ struct ieee80211_key_conf *key, ++ int aid) ++{ ++ struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); ++ unsigned long flags; ++ u8 algorithm; ++ u8 index; ++ int err = -EINVAL; ++ ++ switch (key->alg) { ++ default: ++ case ALG_NONE: ++ case ALG_NULL: ++ algorithm = BCM43xx_SEC_ALGO_NONE; ++ break; ++ case ALG_WEP: ++ if (key->keylen == 5) ++ algorithm = BCM43xx_SEC_ALGO_WEP; ++ else ++ algorithm = BCM43xx_SEC_ALGO_WEP104; ++ break; ++ case ALG_TKIP: ++ algorithm = BCM43xx_SEC_ALGO_TKIP; ++ break; ++ case ALG_CCMP: ++ algorithm = BCM43xx_SEC_ALGO_AES; ++ break; ++ } ++ ++ index = (u8)(key->keyidx); ++ if (index >= ARRAY_SIZE(bcm->key)) ++ goto out; ++ bcm43xx_lock_mmio(bcm, flags); ++ switch (cmd) { ++ case SET_KEY: ++ err = bcm43xx_key_write(bcm, index, algorithm, ++ key->key, key->keylen, ++ addr); ++ if (err) ++ goto out_unlock; ++ key->hw_key_idx = index; ++ key->force_sw_encrypt = 0; ++ if (key->default_tx_key) ++ bcm->default_key_idx = index; ++ bcm->key[index].enabled = 1; ++ break; ++ case DISABLE_KEY: ++ bcm->key[index].enabled = 0; ++ err = 0; ++ break; ++ case REMOVE_ALL_KEYS: ++ bcm43xx_clear_keys(bcm); ++ err = 0; ++ break; ++ case ENABLE_COMPRESSION: ++ case DISABLE_COMPRESSION: ++ err = 0; ++ break; ++ } ++out_unlock: ++ bcm43xx_unlock_mmio(bcm, flags); ++out: ++ return err; ++} ++ ++static int bcm43xx_net_conf_tx(struct net_device *net_dev, ++ int queue, ++ const struct ieee80211_tx_queue_params *params) ++{ ++ return 0; ++} ++ ++static int bcm43xx_net_get_tx_stats(struct net_device *net_dev, ++ struct ieee80211_tx_queue_stats *stats) ++{ ++ struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); ++ unsigned long flags; ++ ++ bcm43xx_lock(bcm, flags); ++ if (bcm43xx_using_pio(bcm)) ++ bcm43xx_pio_get_tx_stats(bcm, stats); ++ else ++ bcm43xx_dma_get_tx_stats(bcm, stats); ++ bcm43xx_unlock(bcm, flags); ++ ++ return 0; ++} ++ ++static int bcm43xx_net_get_stats(struct net_device *net_dev, ++ struct ieee80211_low_level_stats *stats) ++{ ++ struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); ++ unsigned long flags; ++ ++ bcm43xx_lock(bcm, flags); ++ memcpy(stats, &bcm->ieee_stats, sizeof(*stats)); ++ bcm43xx_unlock(bcm, flags); ++ ++ return 0; ++} ++ ++#ifdef CONFIG_NET_POLL_CONTROLLER ++static void bcm43xx_net_poll_controller(struct net_device *net_dev) ++{ ++ struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); ++ unsigned long flags; ++ ++ local_irq_save(flags); ++ bcm43xx_interrupt_handler(bcm->irq, bcm, NULL); ++ local_irq_restore(flags); ++} ++#endif /* CONFIG_NET_POLL_CONTROLLER */ ++ ++static int bcm43xx_net_open(struct net_device *net_dev) ++{ ++ struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); ++ ++ return bcm43xx_init_board(bcm); ++} ++ ++static int bcm43xx_net_stop(struct net_device *net_dev) ++{ ++ struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); ++ ++ if (bcm->initialized) { ++ bcm43xx_disable_interrupts_sync(bcm, NULL); ++ bcm43xx_free_board(bcm); ++ } ++ ++ return 0; ++} ++ ++/* Initialization of struct net_device, just after allocation. */ ++static void bcm43xx_netdev_setup(struct net_device *net_dev) ++{ ++#ifdef CONFIG_NET_POLL_CONTROLLER ++ net_dev->poll_controller = bcm43xx_net_poll_controller; ++#endif ++ SET_ETHTOOL_OPS(net_dev, &bcm43xx_ethtool_ops); ++} ++ ++static int bcm43xx_init_private(struct bcm43xx_private *bcm, ++ struct net_device *net_dev, ++ struct pci_dev *pci_dev, ++ struct ieee80211_hw *ieee) ++{ ++ int err; ++ ++ bcm->ieee = ieee; ++ bcm->irq_savedstate = BCM43xx_IRQ_INITIAL; ++ bcm->pci_dev = pci_dev; ++ bcm->net_dev = net_dev; ++ bcm->bad_frames_preempt = modparam_bad_frames_preempt; ++ spin_lock_init(&bcm->_lock); ++ tasklet_init(&bcm->isr_tasklet, ++ (void (*)(unsigned long))bcm43xx_interrupt_tasklet, ++ (unsigned long)bcm); ++ tasklet_disable_nosync(&bcm->isr_tasklet); ++ if (modparam_pio) { ++ bcm->__using_pio = 1; ++ } else { ++ err = pci_set_dma_mask(pci_dev, DMA_30BIT_MASK); ++ err |= pci_set_consistent_dma_mask(pci_dev, DMA_30BIT_MASK); ++ if (err) { ++#ifdef CONFIG_BCM43XX_D80211_PIO ++ printk(KERN_WARNING PFX "DMA not supported. Falling back to PIO.\n"); ++ bcm->__using_pio = 1; ++#else ++ printk(KERN_ERR PFX "FATAL: DMA not supported and PIO not configured. " ++ "Recompile the driver with PIO support, please.\n"); ++ return -ENODEV; ++#endif /* CONFIG_BCM43XX_D80211_PIO */ ++ } ++ } ++ ++ return 0; ++} ++ ++static int __devinit bcm43xx_init_one(struct pci_dev *pdev, ++ const struct pci_device_id *ent) ++{ ++ struct net_device *net_dev; ++ struct bcm43xx_private *bcm; ++ struct ieee80211_hw *ieee; ++ int err = -ENOMEM; ++ ++#ifdef CONFIG_BCM947XX ++ if ((pdev->bus->number == 0) && (pdev->device != 0x0800)) ++ return -ENODEV; ++#endif ++ ++#ifdef DEBUG_SINGLE_DEVICE_ONLY ++ if (strcmp(pci_name(pdev), DEBUG_SINGLE_DEVICE_ONLY)) ++ return -ENODEV; ++#endif ++ ++ ieee = kzalloc(sizeof(*ieee), GFP_KERNEL); ++ if (!ieee) ++ goto out; ++ ieee->version = IEEE80211_VERSION; ++ ieee->name = KBUILD_MODNAME; ++ ieee->host_gen_beacon = 1; ++ ieee->rx_includes_fcs = 1; ++ ieee->tx = bcm43xx_net_hard_start_xmit; ++ ieee->open = bcm43xx_net_open; ++ ieee->stop = bcm43xx_net_stop; ++ ieee->reset = bcm43xx_net_reset; ++ ieee->config = bcm43xx_net_config; ++//TODO ieee->set_key = bcm43xx_net_set_key; ++ ieee->get_stats = bcm43xx_net_get_stats; ++ ieee->queues = 1; ++ ieee->get_tx_stats = bcm43xx_net_get_tx_stats; ++ ieee->conf_tx = bcm43xx_net_conf_tx; ++ ieee->wep_include_iv = 1; ++ ++ net_dev = ieee80211_alloc_hw(sizeof(*bcm), bcm43xx_netdev_setup); ++ if (!net_dev) { ++ printk(KERN_ERR PFX ++ "could not allocate ieee80211 device %s\n", ++ pci_name(pdev)); ++ goto err_free_ieee; ++ } ++ /* initialize the bcm43xx_private struct */ ++ bcm = bcm43xx_priv(net_dev); ++ memset(bcm, 0, sizeof(*bcm)); ++ err = bcm43xx_init_private(bcm, net_dev, pdev, ieee); ++ if (err) ++ goto err_free_netdev; ++ ++ pci_set_drvdata(pdev, net_dev); ++ ++ err = bcm43xx_attach_board(bcm); ++ if (err) ++ goto err_free_netdev; ++ err = ieee80211_register_hw(net_dev, ieee); ++ if (err) ++ goto err_detach_board; ++ ++ bcm43xx_debugfs_add_device(bcm); ++ ++ assert(err == 0); ++out: ++ return err; ++ ++err_detach_board: ++ bcm43xx_detach_board(bcm); ++err_free_netdev: ++ ieee80211_free_hw(net_dev); ++err_free_ieee: ++ kfree(ieee); ++ goto out; ++} ++ ++static void __devexit bcm43xx_remove_one(struct pci_dev *pdev) ++{ ++ struct net_device *net_dev = pci_get_drvdata(pdev); ++ struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); ++ struct ieee80211_hw *ieee = bcm->ieee; ++ ++ bcm43xx_debugfs_remove_device(bcm); ++ ++ /* Bring down the device early to stop all TX and RX operation. */ ++ ieee80211_netif_oper(net_dev, NETIF_DETACH); ++ bcm43xx_net_stop(net_dev); ++ ++ ieee80211_unregister_hw(net_dev); ++ bcm43xx_detach_board(bcm); ++ if (bcm->cached_beacon) ++ kfree_skb(bcm->cached_beacon); ++ bcm->cached_beacon = NULL; ++ assert(bcm->ucode == NULL); ++ ieee80211_free_hw(net_dev); ++ kfree(ieee); ++} ++ ++/* Hard-reset the chip. Do not call this directly. ++ * Use bcm43xx_controller_restart() ++ */ ++static void bcm43xx_chip_reset(void *_bcm) ++{ ++ struct bcm43xx_private *bcm = _bcm; ++ struct net_device *net_dev = bcm->net_dev; ++ struct pci_dev *pci_dev = bcm->pci_dev; ++ struct ieee80211_hw *ieee = bcm->ieee; ++ int err; ++ int was_initialized = bcm->initialized; ++ ++ ieee80211_netif_oper(bcm->net_dev, NETIF_DETACH); ++ tasklet_disable(&bcm->isr_tasklet); ++ ++ bcm->firmware_norelease = 1; ++ if (was_initialized) ++ bcm43xx_free_board(bcm); ++ bcm->firmware_norelease = 0; ++ bcm43xx_detach_board(bcm); ++ err = bcm43xx_init_private(bcm, net_dev, pci_dev, ieee); ++ if (err) ++ goto failure; ++ err = bcm43xx_attach_board(bcm); ++ if (err) ++ goto failure; ++ if (was_initialized) { ++ err = bcm43xx_init_board(bcm); ++ if (err) ++ goto failure; ++ } ++ ieee80211_netif_oper(bcm->net_dev, NETIF_ATTACH); ++ printk(KERN_INFO PFX "Controller restarted\n"); ++ ++ return; ++failure: ++ printk(KERN_ERR PFX "Controller restart failed\n"); ++} ++ ++/* Hard-reset the chip. ++ * This can be called from interrupt or process context. ++ * Make sure to _not_ re-enable device interrupts after this has been called. ++ */ ++void bcm43xx_controller_restart(struct bcm43xx_private *bcm, const char *reason) ++{ ++ bcm43xx_interrupt_disable(bcm, BCM43xx_IRQ_ALL); ++ bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD); /* dummy read */ ++ printk(KERN_ERR PFX "Controller RESET (%s) ...\n", reason); ++ INIT_WORK(&bcm->restart_work, bcm43xx_chip_reset, bcm); ++ schedule_work(&bcm->restart_work); ++} ++ ++#ifdef CONFIG_PM ++ ++static int bcm43xx_suspend(struct pci_dev *pdev, pm_message_t state) ++{ ++ struct net_device *net_dev = pci_get_drvdata(pdev); ++ struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); ++ unsigned long flags; ++ int try_to_shutdown = 0, err; ++ ++ dprintk(KERN_INFO PFX "Suspending...\n"); ++ ++ bcm43xx_lock(bcm, flags); ++ bcm->was_initialized = bcm->initialized; ++ if (bcm->initialized) ++ try_to_shutdown = 1; ++ bcm43xx_unlock(bcm, flags); ++ ++ ieee80211_netif_oper(bcm->net_dev, NETIF_DETACH); ++ if (try_to_shutdown) { ++ err = bcm43xx_disable_interrupts_sync(bcm, &bcm->irq_savedstate); ++ if (unlikely(err)) { ++ dprintk(KERN_ERR PFX "Suspend failed.\n"); ++ return -EAGAIN; ++ } ++ bcm->firmware_norelease = 1; ++ bcm43xx_free_board(bcm); ++ bcm->firmware_norelease = 0; ++ } ++ bcm43xx_chipset_detach(bcm); ++ ++ pci_save_state(pdev); ++ pci_disable_device(pdev); ++ pci_set_power_state(pdev, pci_choose_state(pdev, state)); ++ ++ dprintk(KERN_INFO PFX "Device suspended.\n"); ++ ++ return 0; ++} ++ ++static int bcm43xx_resume(struct pci_dev *pdev) ++{ ++ struct net_device *net_dev = pci_get_drvdata(pdev); ++ struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); ++ int err = 0; ++ ++ dprintk(KERN_INFO PFX "Resuming...\n"); ++ ++ pci_set_power_state(pdev, 0); ++ pci_enable_device(pdev); ++ pci_restore_state(pdev); ++ ++ bcm43xx_chipset_attach(bcm); ++ if (bcm->was_initialized) { ++ bcm->irq_savedstate = BCM43xx_IRQ_INITIAL; ++ err = bcm43xx_init_board(bcm); ++ } ++ if (err) { ++ printk(KERN_ERR PFX "Resume failed!\n"); ++ return err; ++ } ++ ++ ieee80211_netif_oper(bcm->net_dev, NETIF_ATTACH); ++ ++ dprintk(KERN_INFO PFX "Device resumed.\n"); ++ ++ return 0; ++} ++ ++#endif /* CONFIG_PM */ ++ ++static struct pci_driver bcm43xx_pci_driver = { ++ .name = KBUILD_MODNAME, ++ .id_table = bcm43xx_pci_tbl, ++ .probe = bcm43xx_init_one, ++ .remove = __devexit_p(bcm43xx_remove_one), ++#ifdef CONFIG_PM ++ .suspend = bcm43xx_suspend, ++ .resume = bcm43xx_resume, ++#endif /* CONFIG_PM */ ++}; ++ ++static int __init bcm43xx_init(void) ++{ ++ printk(KERN_INFO KBUILD_MODNAME " driver\n"); ++ bcm43xx_debugfs_init(); ++ return pci_register_driver(&bcm43xx_pci_driver); ++} ++ ++static void __exit bcm43xx_exit(void) ++{ ++ pci_unregister_driver(&bcm43xx_pci_driver); ++ bcm43xx_debugfs_exit(); ++} ++ ++module_init(bcm43xx_init) ++module_exit(bcm43xx_exit) +diff -Nur linux-2.6.16/drivers/net/wireless/bcm43xx-d80211/bcm43xx_main.h linux-2.6.16-bcm43xx/drivers/net/wireless/bcm43xx-d80211/bcm43xx_main.h +--- linux-2.6.16/drivers/net/wireless/bcm43xx-d80211/bcm43xx_main.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.16-bcm43xx/drivers/net/wireless/bcm43xx-d80211/bcm43xx_main.h 2006-03-28 22:16:14.000000000 +0200 +@@ -0,0 +1,177 @@ ++/* ++ ++ Broadcom BCM43xx wireless driver ++ ++ Copyright (c) 2005 Martin Langer <martin-langer@gmx.de>, ++ Stefano Brivio <st3@riseup.net> ++ Michael Buesch <mbuesch@freenet.de> ++ Danny van Dyk <kugelfang@gentoo.org> ++ Andreas Jaggi <andreas.jaggi@waterwave.ch> ++ ++ Some parts of the code in this file are derived from the ipw2200 ++ driver Copyright(c) 2003 - 2004 Intel Corporation. ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; see the file COPYING. If not, write to ++ the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, ++ Boston, MA 02110-1301, USA. ++ ++*/ ++ ++#ifndef BCM43xx_MAIN_H_ ++#define BCM43xx_MAIN_H_ ++ ++#include "bcm43xx.h" ++ ++#ifdef CONFIG_BCM947XX ++#define atoi(str) simple_strtoul(((str != NULL) ? str : ""), NULL, 0) ++ ++static inline void e_aton(char *str, char *dest) ++{ ++ int i = 0; ++ u16 *d = (u16 *) dest; ++ ++ for (;;) { ++ dest[i++] = (char) simple_strtoul(str, NULL, 16); ++ str += 2; ++ if (!*str++ || i == 6) ++ break; ++ } ++ for (i = 0; i < 3; i++) ++ d[i] = cpu_to_be16(d[i]); ++} ++#endif ++ ++#define P4D_BYT3S(magic, nr_bytes) u8 __p4dding##magic[nr_bytes] ++#define P4D_BYTES(line, nr_bytes) P4D_BYT3S(line, nr_bytes) ++/* Magic helper macro to pad structures. Ignore those above. It's magic. */ ++#define PAD_BYTES(nr_bytes) P4D_BYTES( __LINE__ , (nr_bytes)) ++ ++ ++/* Lightweight function to convert a frequency (in Mhz) to a channel number. */ ++static inline ++u8 bcm43xx_freq_to_channel_a(int freq) ++{ ++ return ((freq - 5000) / 5); ++} ++static inline ++u8 bcm43xx_freq_to_channel_bg(int freq) ++{ ++ u8 channel; ++ ++ if (freq == 2484) ++ channel = 14; ++ else ++ channel = (freq - 2407) / 5; ++ ++ return channel; ++} ++static inline ++u8 bcm43xx_freq_to_channel(struct bcm43xx_private *bcm, ++ int freq) ++{ ++ if (bcm43xx_current_phy(bcm)->type == BCM43xx_PHYTYPE_A) ++ return bcm43xx_freq_to_channel_a(freq); ++ return bcm43xx_freq_to_channel_bg(freq); ++} ++ ++/* Lightweight function to convert a channel number to a frequency (in Mhz). */ ++static inline ++int bcm43xx_channel_to_freq_a(u8 channel) ++{ ++ return (5000 + (5 * channel)); ++} ++static inline ++int bcm43xx_channel_to_freq_bg(u8 channel) ++{ ++ int freq; ++ ++ if (channel == 14) ++ freq = 2484; ++ else ++ freq = 2407 + (5 * channel); ++ ++ return freq; ++} ++static inline ++int bcm43xx_channel_to_freq(struct bcm43xx_private *bcm, ++ u8 channel) ++{ ++ if (bcm43xx_current_phy(bcm)->type == BCM43xx_PHYTYPE_A) ++ return bcm43xx_channel_to_freq_a(channel); ++ return bcm43xx_channel_to_freq_bg(channel); ++} ++ ++/* Lightweight function to check if a channel number is valid. ++ * Note that this does _NOT_ check for geographical restrictions! ++ */ ++static inline ++int bcm43xx_is_valid_channel_a(u8 channel) ++{ ++ return (channel <= 200); ++} ++static inline ++int bcm43xx_is_valid_channel_bg(u8 channel) ++{ ++ return (channel >= 1 && channel <= 14); ++} ++static inline ++int bcm43xx_is_valid_channel(struct bcm43xx_private *bcm, ++ u8 channel) ++{ ++ if (bcm43xx_current_phy(bcm)->type == BCM43xx_PHYTYPE_A) ++ return bcm43xx_is_valid_channel_a(channel); ++ return bcm43xx_is_valid_channel_bg(channel); ++} ++ ++static inline ++int bcm43xx_is_cck_rate(int rate) ++{ ++ return (rate == BCM43xx_CCK_RATE_1MB || ++ rate == BCM43xx_CCK_RATE_2MB || ++ rate == BCM43xx_CCK_RATE_5MB || ++ rate == BCM43xx_CCK_RATE_11MB); ++} ++ ++void bcm43xx_tsf_read(struct bcm43xx_private *bcm, u64 *tsf); ++void bcm43xx_tsf_write(struct bcm43xx_private *bcm, u64 tsf); ++ ++void bcm43xx_set_iwmode(struct bcm43xx_private *bcm, ++ int iw_mode); ++ ++u32 bcm43xx_shm_read32(struct bcm43xx_private *bcm, ++ u16 routing, u16 offset); ++u16 bcm43xx_shm_read16(struct bcm43xx_private *bcm, ++ u16 routing, u16 offset); ++void bcm43xx_shm_write32(struct bcm43xx_private *bcm, ++ u16 routing, u16 offset, ++ u32 value); ++void bcm43xx_shm_write16(struct bcm43xx_private *bcm, ++ u16 routing, u16 offset, ++ u16 value); ++ ++void bcm43xx_dummy_transmission(struct bcm43xx_private *bcm); ++ ++int bcm43xx_switch_core(struct bcm43xx_private *bcm, struct bcm43xx_coreinfo *new_core); ++ ++void bcm43xx_wireless_core_reset(struct bcm43xx_private *bcm, int connect_phy); ++ ++void bcm43xx_mac_suspend(struct bcm43xx_private *bcm); ++void bcm43xx_mac_enable(struct bcm43xx_private *bcm); ++ ++void bcm43xx_controller_restart(struct bcm43xx_private *bcm, const char *reason); ++ ++int bcm43xx_sprom_read(struct bcm43xx_private *bcm, u16 *sprom); ++int bcm43xx_sprom_write(struct bcm43xx_private *bcm, const u16 *sprom); ++ ++#endif /* BCM43xx_MAIN_H_ */ +diff -Nur linux-2.6.16/drivers/net/wireless/bcm43xx-d80211/bcm43xx_phy.c linux-2.6.16-bcm43xx/drivers/net/wireless/bcm43xx-d80211/bcm43xx_phy.c +--- linux-2.6.16/drivers/net/wireless/bcm43xx-d80211/bcm43xx_phy.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.16-bcm43xx/drivers/net/wireless/bcm43xx-d80211/bcm43xx_phy.c 2006-03-28 22:16:14.000000000 +0200 +@@ -0,0 +1,2347 @@ ++/* ++ ++ Broadcom BCM43xx wireless driver ++ ++ Copyright (c) 2005 Martin Langer <martin-langer@gmx.de>, ++ Stefano Brivio <st3@riseup.net> ++ Michael Buesch <mbuesch@freenet.de> ++ Danny van Dyk <kugelfang@gentoo.org> ++ Andreas Jaggi <andreas.jaggi@waterwave.ch> ++ ++ Some parts of the code in this file are derived from the ipw2200 ++ driver Copyright(c) 2003 - 2004 Intel Corporation. ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; see the file COPYING. If not, write to ++ the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, ++ Boston, MA 02110-1301, USA. ++ ++*/ ++ ++#include <linux/delay.h> ++#include <linux/pci.h> ++#include <linux/types.h> ++ ++#include "bcm43xx.h" ++#include "bcm43xx_phy.h" ++#include "bcm43xx_main.h" ++#include "bcm43xx_radio.h" ++#include "bcm43xx_ilt.h" ++#include "bcm43xx_power.h" ++ ++ ++static const s8 bcm43xx_tssi2dbm_b_table[] = { ++ 0x4D, 0x4C, 0x4B, 0x4A, ++ 0x4A, 0x49, 0x48, 0x47, ++ 0x47, 0x46, 0x45, 0x45, ++ 0x44, 0x43, 0x42, 0x42, ++ 0x41, 0x40, 0x3F, 0x3E, ++ 0x3D, 0x3C, 0x3B, 0x3A, ++ 0x39, 0x38, 0x37, 0x36, ++ 0x35, 0x34, 0x32, 0x31, ++ 0x30, 0x2F, 0x2D, 0x2C, ++ 0x2B, 0x29, 0x28, 0x26, ++ 0x25, 0x23, 0x21, 0x1F, ++ 0x1D, 0x1A, 0x17, 0x14, ++ 0x10, 0x0C, 0x06, 0x00, ++ -7, -7, -7, -7, ++ -7, -7, -7, -7, ++ -7, -7, -7, -7, ++}; ++ ++static const s8 bcm43xx_tssi2dbm_g_table[] = { ++ 77, 77, 77, 76, ++ 76, 76, 75, 75, ++ 74, 74, 73, 73, ++ 73, 72, 72, 71, ++ 71, 70, 70, 69, ++ 68, 68, 67, 67, ++ 66, 65, 65, 64, ++ 63, 63, 62, 61, ++ 60, 59, 58, 57, ++ 56, 55, 54, 53, ++ 52, 50, 49, 47, ++ 45, 43, 40, 37, ++ 33, 28, 22, 14, ++ 5, -7, -20, -20, ++ -20, -20, -20, -20, ++ -20, -20, -20, -20, ++}; ++ ++static void bcm43xx_phy_initg(struct bcm43xx_private *bcm); ++ ++ ++void bcm43xx_raw_phy_lock(struct bcm43xx_private *bcm) ++{ ++ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); ++ ++ assert(irqs_disabled()); ++ if (bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD) == 0x00000000) { ++ phy->is_locked = 0; ++ return; ++ } ++ if (bcm->current_core->rev < 3) { ++ bcm43xx_mac_suspend(bcm); ++ spin_lock(&phy->lock); ++ } else { ++ if (bcm->iw_mode != IW_MODE_MASTER) ++ bcm43xx_power_saving_ctl_bits(bcm, -1, 1); ++ } ++ phy->is_locked = 1; ++} ++ ++void bcm43xx_raw_phy_unlock(struct bcm43xx_private *bcm) ++{ ++ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); ++ ++ assert(irqs_disabled()); ++ if (bcm->current_core->rev < 3) { ++ if (phy->is_locked) { ++ spin_unlock(&phy->lock); ++ bcm43xx_mac_enable(bcm); ++ } ++ } else { ++ if (bcm->iw_mode != IW_MODE_MASTER) ++ bcm43xx_power_saving_ctl_bits(bcm, -1, -1); ++ } ++ phy->is_locked = 0; ++} ++ ++u16 bcm43xx_phy_read(struct bcm43xx_private *bcm, u16 offset) ++{ ++ bcm43xx_write16(bcm, BCM43xx_MMIO_PHY_CONTROL, offset); ++ return bcm43xx_read16(bcm, BCM43xx_MMIO_PHY_DATA); ++} ++ ++void bcm43xx_phy_write(struct bcm43xx_private *bcm, u16 offset, u16 val) ++{ ++ bcm43xx_write16(bcm, BCM43xx_MMIO_PHY_CONTROL, offset); ++ mmiowb(); ++ bcm43xx_write16(bcm, BCM43xx_MMIO_PHY_DATA, val); ++} ++ ++void bcm43xx_phy_calibrate(struct bcm43xx_private *bcm) ++{ ++ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); ++ unsigned long flags; ++ ++ bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD); /* Dummy read. */ ++ if (phy->calibrated) ++ return; ++ if (phy->type == BCM43xx_PHYTYPE_G && phy->rev == 1) { ++ /* We do not want to be preempted while calibrating ++ * the hardware. ++ */ ++ local_irq_save(flags); ++ ++ bcm43xx_wireless_core_reset(bcm, 0); ++ bcm43xx_phy_initg(bcm); ++ bcm43xx_wireless_core_reset(bcm, 1); ++ ++ local_irq_restore(flags); ++ } ++ phy->calibrated = 1; ++} ++ ++/* Connect the PHY ++ * http://bcm-specs.sipsolutions.net/SetPHY ++ */ ++int bcm43xx_phy_connect(struct bcm43xx_private *bcm, int connect) ++{ ++ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); ++ u32 flags; ++ ++ if (bcm->current_core->rev < 5) ++ goto out; ++ ++ flags = bcm43xx_read32(bcm, BCM43xx_CIR_SBTMSTATEHIGH); ++ if (connect) { ++ if (!(flags & 0x00010000)) ++ return -ENODEV; ++ flags = bcm43xx_read32(bcm, BCM43xx_CIR_SBTMSTATELOW); ++ flags |= (0x800 << 18); ++ bcm43xx_write32(bcm, BCM43xx_CIR_SBTMSTATELOW, flags); ++ } else { ++ if (!(flags & 0x00020000)) ++ return -ENODEV; ++ flags = bcm43xx_read32(bcm, BCM43xx_CIR_SBTMSTATELOW); ++ flags &= ~(0x800 << 18); ++ bcm43xx_write32(bcm, BCM43xx_CIR_SBTMSTATELOW, flags); ++ } ++out: ++ phy->connected = connect; ++ if (connect) ++ dprintk(KERN_INFO PFX "PHY connected\n"); ++ else ++ dprintk(KERN_INFO PFX "PHY disconnected\n"); ++ ++ return 0; ++} ++ ++/* intialize B PHY power control ++ * as described in http://bcm-specs.sipsolutions.net/InitPowerControl ++ */ ++static void bcm43xx_phy_init_pctl(struct bcm43xx_private *bcm) ++{ ++ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); ++ struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm); ++ u16 saved_batt = 0, saved_ratt = 0, saved_txctl1 = 0; ++ int must_reset_txpower = 0; ++ ++ assert(phy->type != BCM43xx_PHYTYPE_A); ++ if ((bcm->board_vendor == PCI_VENDOR_ID_BROADCOM) && ++ (bcm->board_type == 0x0416)) ++ return; ++ ++ bcm43xx_write16(bcm, 0x03E6, bcm43xx_read16(bcm, 0x03E6) & 0xFFDF); ++ bcm43xx_phy_write(bcm, 0x0028, 0x8018); ++ ++ if (phy->type == BCM43xx_PHYTYPE_G) { ++ if (!phy->connected) ++ return; ++ bcm43xx_phy_write(bcm, 0x047A, 0xC111); ++ } ++ if (phy->savedpctlreg != 0xFFFF) ++ return; ++ ++ if (phy->type == BCM43xx_PHYTYPE_B && ++ phy->rev >= 2 && ++ radio->version == 0x2050) { ++ bcm43xx_radio_write16(bcm, 0x0076, ++ bcm43xx_radio_read16(bcm, 0x0076) | 0x0084); ++ } else { ++ saved_batt = radio->baseband_atten; ++ saved_ratt = radio->radio_atten; ++ saved_txctl1 = radio->txctl1; ++ if ((radio->revision >= 6) && (radio->revision <= 8) ++ && /*FIXME: incomplete specs for 5 < revision < 9 */ 0) ++ bcm43xx_radio_set_txpower_bg(bcm, 0xB, 0x1F, 0); ++ else ++ bcm43xx_radio_set_txpower_bg(bcm, 0xB, 9, 0); ++ must_reset_txpower = 1; ++ } ++ bcm43xx_dummy_transmission(bcm); ++ ++ phy->savedpctlreg = bcm43xx_phy_read(bcm, BCM43xx_PHY_G_PCTL); ++ ++ if (must_reset_txpower) ++ bcm43xx_radio_set_txpower_bg(bcm, saved_batt, saved_ratt, saved_txctl1); ++ else ++ bcm43xx_radio_write16(bcm, 0x0076, bcm43xx_radio_read16(bcm, 0x0076) & 0xFF7B); ++ bcm43xx_radio_clear_tssi(bcm); ++} ++ ++static void bcm43xx_phy_agcsetup(struct bcm43xx_private *bcm) ++{ ++ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); ++ u16 offset = 0x0000; ++ ++ if (phy->rev == 1) ++ offset = 0x4C00; ++ ++ bcm43xx_ilt_write(bcm, offset, 0x00FE); ++ bcm43xx_ilt_write(bcm, offset + 1, 0x000D); ++ bcm43xx_ilt_write(bcm, offset + 2, 0x0013); ++ bcm43xx_ilt_write(bcm, offset + 3, 0x0019); ++ ++ if (phy->rev == 1) { ++ bcm43xx_ilt_write(bcm, 0x1800, 0x2710); ++ bcm43xx_ilt_write(bcm, 0x1801, 0x9B83); ++ bcm43xx_ilt_write(bcm, 0x1802, 0x9B83); ++ bcm43xx_ilt_write(bcm, 0x1803, 0x0F8D); ++ bcm43xx_phy_write(bcm, 0x0455, 0x0004); ++ } ++ ++ bcm43xx_phy_write(bcm, 0x04A5, (bcm43xx_phy_read(bcm, 0x04A5) & 0x00FF) | 0x5700); ++ bcm43xx_phy_write(bcm, 0x041A, (bcm43xx_phy_read(bcm, 0x041A) & 0xFF80) | 0x000F); ++ bcm43xx_phy_write(bcm, 0x041A, (bcm43xx_phy_read(bcm, 0x041A) & 0xC07F) | 0x2B80); ++ bcm43xx_phy_write(bcm, 0x048C, (bcm43xx_phy_read(bcm, 0x048C) & 0xF0FF) | 0x0300); ++ ++ bcm43xx_radio_write16(bcm, 0x007A, bcm43xx_radio_read16(bcm, 0x007A) | 0x0008); ++ ++ bcm43xx_phy_write(bcm, 0x04A0, (bcm43xx_phy_read(bcm, 0x04A0) & 0xFFF0) | 0x0008); ++ bcm43xx_phy_write(bcm, 0x04A1, (bcm43xx_phy_read(bcm, 0x04A1) & 0xF0FF) | 0x0600); ++ bcm43xx_phy_write(bcm, 0x04A2, (bcm43xx_phy_read(bcm, 0x04A2) & 0xF0FF) | 0x0700); ++ bcm43xx_phy_write(bcm, 0x04A0, (bcm43xx_phy_read(bcm, 0x04A0) & 0xF0FF) | 0x0100); ++ ++ if (phy->rev == 1) ++ bcm43xx_phy_write(bcm, 0x04A2, (bcm43xx_phy_read(bcm, 0x04A2) & 0xFFF0) | 0x0007); ++ ++ bcm43xx_phy_write(bcm, 0x0488, (bcm43xx_phy_read(bcm, 0x0488) & 0xFF00) | 0x001C); ++ bcm43xx_phy_write(bcm, 0x0488, (bcm43xx_phy_read(bcm, 0x0488) & 0xC0FF) | 0x0200); ++ bcm43xx_phy_write(bcm, 0x0496, (bcm43xx_phy_read(bcm, 0x0496) & 0xFF00) | 0x001C); ++ bcm43xx_phy_write(bcm, 0x0489, (bcm43xx_phy_read(bcm, 0x0489) & 0xFF00) | 0x0020); ++ bcm43xx_phy_write(bcm, 0x0489, (bcm43xx_phy_read(bcm, 0x0489) & 0xC0FF) | 0x0200); ++ bcm43xx_phy_write(bcm, 0x0482, (bcm43xx_phy_read(bcm, 0x0482) & 0xFF00) | 0x002E); ++ bcm43xx_phy_write(bcm, 0x0496, (bcm43xx_phy_read(bcm, 0x0496) & 0x00FF) | 0x1A00); ++ bcm43xx_phy_write(bcm, 0x0481, (bcm43xx_phy_read(bcm, 0x0481) & 0xFF00) | 0x0028); ++ bcm43xx_phy_write(bcm, 0x0481, (bcm43xx_phy_read(bcm, 0x0481) & 0x00FF) | 0x2C00); ++ ++ if (phy->rev == 1) { ++ bcm43xx_phy_write(bcm, 0x0430, 0x092B); ++ bcm43xx_phy_write(bcm, 0x041B, (bcm43xx_phy_read(bcm, 0x041B) & 0xFFE1) | 0x0002); ++ } else { ++ bcm43xx_phy_write(bcm, 0x041B, bcm43xx_phy_read(bcm, 0x041B) & 0xFFE1); ++ bcm43xx_phy_write(bcm, 0x041F, 0x287A); ++ bcm43xx_phy_write(bcm, 0x0420, (bcm43xx_phy_read(bcm, 0x0420) & 0xFFF0) | 0x0004); ++ } ++ ++ if (phy->rev > 2) { ++ bcm43xx_phy_write(bcm, 0x0422, 0x287A); ++ bcm43xx_phy_write(bcm, 0x0420, (bcm43xx_phy_read(bcm, 0x0420) & 0x0FFF) | 0x3000); ++ } ++ ++ bcm43xx_phy_write(bcm, 0x04A8, (bcm43xx_phy_read(bcm, 0x04A8) & 0x8080) | 0x7874); ++ bcm43xx_phy_write(bcm, 0x048E, 0x1C00); ++ ++ if (phy->rev == 1) { ++ bcm43xx_phy_write(bcm, 0x04AB, (bcm43xx_phy_read(bcm, 0x04AB) & 0xF0FF) | 0x0600); ++ bcm43xx_phy_write(bcm, 0x048B, 0x005E); ++ bcm43xx_phy_write(bcm, 0x048C, (bcm43xx_phy_read(bcm, 0x048C) & 0xFF00) | 0x001E); ++ bcm43xx_phy_write(bcm, 0x048D, 0x0002); ++ } ++ ++ bcm43xx_ilt_write(bcm, offset + 0x0800, 0); ++ bcm43xx_ilt_write(bcm, offset + 0x0801, 7); ++ bcm43xx_ilt_write(bcm, offset + 0x0802, 16); ++ bcm43xx_ilt_write(bcm, offset + 0x0803, 28); ++} ++ ++static void bcm43xx_phy_setupg(struct bcm43xx_private *bcm) ++{ ++ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); ++ u16 i; ++ ++ assert(phy->type == BCM43xx_PHYTYPE_G); ++ if (phy->rev == 1) { ++ bcm43xx_phy_write(bcm, 0x0406, 0x4F19); ++ bcm43xx_phy_write(bcm, BCM43xx_PHY_G_CRS, ++ (bcm43xx_phy_read(bcm, BCM43xx_PHY_G_CRS) & 0xFC3F) | 0x0340); ++ bcm43xx_phy_write(bcm, 0x042C, 0x005A); ++ bcm43xx_phy_write(bcm, 0x0427, 0x001A); ++ ++ for (i = 0; i < BCM43xx_ILT_FINEFREQG_SIZE; i++) ++ bcm43xx_ilt_write(bcm, 0x5800 + i, bcm43xx_ilt_finefreqg[i]); ++ for (i = 0; i < BCM43xx_ILT_NOISEG1_SIZE; i++) ++ bcm43xx_ilt_write(bcm, 0x1800 + i, bcm43xx_ilt_noiseg1[i]); ++ for (i = 0; i < BCM43xx_ILT_ROTOR_SIZE; i++) ++ bcm43xx_ilt_write(bcm, 0x2000 + i, bcm43xx_ilt_rotor[i]); ++ } else { ++ /* nrssi values are signed 6-bit values. Not sure why we write 0x7654 here... */ ++ bcm43xx_nrssi_hw_write(bcm, 0xBA98, (s16)0x7654); ++ ++ if (phy->rev == 2) { ++ bcm43xx_phy_write(bcm, 0x04C0, 0x1861); ++ bcm43xx_phy_write(bcm, 0x04C1, 0x0271); ++ } else if (phy->rev > 2) { ++ bcm43xx_phy_write(bcm, 0x04C0, 0x0098); ++ bcm43xx_phy_write(bcm, 0x04C1, 0x0070); ++ bcm43xx_phy_write(bcm, 0x04C9, 0x0080); ++ } ++ bcm43xx_phy_write(bcm, 0x042B, bcm43xx_phy_read(bcm, 0x042B) | 0x800); ++ ++ for (i = 0; i < 64; i++) ++ bcm43xx_ilt_write(bcm, 0x4000 + i, i); ++ for (i = 0; i < BCM43xx_ILT_NOISEG2_SIZE; i++) ++ bcm43xx_ilt_write(bcm, 0x1800 + i, bcm43xx_ilt_noiseg2[i]); ++ } ++ ++ if (phy->rev <= 2) ++ for (i = 0; i < BCM43xx_ILT_NOISESCALEG_SIZE; i++) ++ bcm43xx_ilt_write(bcm, 0x1400 + i, bcm43xx_ilt_noisescaleg1[i]); ++ else if ((phy->rev == 7) && (bcm43xx_phy_read(bcm, 0x0449) & 0x0200)) ++ for (i = 0; i < BCM43xx_ILT_NOISESCALEG_SIZE; i++) ++ bcm43xx_ilt_write(bcm, 0x1400 + i, bcm43xx_ilt_noisescaleg3[i]); ++ else ++ for (i = 0; i < BCM43xx_ILT_NOISESCALEG_SIZE; i++) ++ bcm43xx_ilt_write(bcm, 0x1400 + i, bcm43xx_ilt_noisescaleg2[i]); ++ ++ if (phy->rev == 2) ++ for (i = 0; i < BCM43xx_ILT_SIGMASQR_SIZE; i++) ++ bcm43xx_ilt_write(bcm, 0x5000 + i, bcm43xx_ilt_sigmasqr1[i]); ++ else if ((phy->rev > 2) && (phy->rev <= 7)) ++ for (i = 0; i < BCM43xx_ILT_SIGMASQR_SIZE; i++) ++ bcm43xx_ilt_write(bcm, 0x5000 + i, bcm43xx_ilt_sigmasqr2[i]); ++ ++ if (phy->rev == 1) { ++ for (i = 0; i < BCM43xx_ILT_RETARD_SIZE; i++) ++ bcm43xx_ilt_write(bcm, 0x2400 + i, bcm43xx_ilt_retard[i]); ++ for (i = 0; i < 4; i++) { ++ bcm43xx_ilt_write(bcm, 0x5404 + i, 0x0020); ++ bcm43xx_ilt_write(bcm, 0x5408 + i, 0x0020); ++ bcm43xx_ilt_write(bcm, 0x540C + i, 0x0020); ++ bcm43xx_ilt_write(bcm, 0x5410 + i, 0x0020); ++ } ++ bcm43xx_phy_agcsetup(bcm); ++ ++ if ((bcm->board_vendor == PCI_VENDOR_ID_BROADCOM) && ++ (bcm->board_type == 0x0416) && ++ (bcm->board_revision == 0x0017)) ++ return; ++ ++ bcm43xx_ilt_write(bcm, 0x5001, 0x0002); ++ bcm43xx_ilt_write(bcm, 0x5002, 0x0001); ++ } else { ++ for (i = 0; i <= 0x2F; i++) ++ bcm43xx_ilt_write(bcm, 0x1000 + i, 0x0820); ++ bcm43xx_phy_agcsetup(bcm); ++ bcm43xx_phy_read(bcm, 0x0400); /* dummy read */ ++ bcm43xx_phy_write(bcm, 0x0403, 0x1000); ++ bcm43xx_ilt_write(bcm, 0x3C02, 0x000F); ++ bcm43xx_ilt_write(bcm, 0x3C03, 0x0014); ++ ++ if ((bcm->board_vendor == PCI_VENDOR_ID_BROADCOM) && ++ (bcm->board_type == 0x0416) && ++ (bcm->board_revision == 0x0017)) ++ return; ++ ++ bcm43xx_ilt_write(bcm, 0x0401, 0x0002); ++ bcm43xx_ilt_write(bcm, 0x0402, 0x0001); ++ } ++} ++ ++/* Initialize the noisescaletable for APHY */ ++static void bcm43xx_phy_init_noisescaletbl(struct bcm43xx_private *bcm) ++{ ++ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); ++ int i; ++ ++ bcm43xx_phy_write(bcm, BCM43xx_PHY_ILT_A_CTRL, 0x1400); ++ for (i = 0; i < 12; i++) { ++ if (phy->rev == 2) ++ bcm43xx_phy_write(bcm, BCM43xx_PHY_ILT_A_DATA1, 0x6767); ++ else ++ bcm43xx_phy_write(bcm, BCM43xx_PHY_ILT_A_DATA1, 0x2323); ++ } ++ if (phy->rev == 2) ++ bcm43xx_phy_write(bcm, BCM43xx_PHY_ILT_A_DATA1, 0x6700); ++ else ++ bcm43xx_phy_write(bcm, BCM43xx_PHY_ILT_A_DATA1, 0x2300); ++ for (i = 0; i < 11; i++) { ++ if (phy->rev == 2) ++ bcm43xx_phy_write(bcm, BCM43xx_PHY_ILT_A_DATA1, 0x6767); ++ else ++ bcm43xx_phy_write(bcm, BCM43xx_PHY_ILT_A_DATA1, 0x2323); ++ } ++ if (phy->rev == 2) ++ bcm43xx_phy_write(bcm, BCM43xx_PHY_ILT_A_DATA1, 0x0067); ++ else ++ bcm43xx_phy_write(bcm, BCM43xx_PHY_ILT_A_DATA1, 0x0023); ++} ++ ++static void bcm43xx_phy_setupa(struct bcm43xx_private *bcm) ++{ ++ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); ++ u16 i; ++ ++ assert(phy->type == BCM43xx_PHYTYPE_A); ++ switch (phy->rev) { ++ case 2: ++ bcm43xx_phy_write(bcm, 0x008E, 0x3800); ++ bcm43xx_phy_write(bcm, 0x0035, 0x03FF); ++ bcm43xx_phy_write(bcm, 0x0036, 0x0400); ++ ++ bcm43xx_ilt_write(bcm, 0x3807, 0x0051); ++ ++ bcm43xx_phy_write(bcm, 0x001C, 0x0FF9); ++ bcm43xx_phy_write(bcm, 0x0020, bcm43xx_phy_read(bcm, 0x0020) & 0xFF0F); ++ bcm43xx_ilt_write(bcm, 0x3C0C, 0x07BF); ++ bcm43xx_radio_write16(bcm, 0x0002, 0x07BF); ++ ++ bcm43xx_phy_write(bcm, 0x0024, 0x4680); ++ bcm43xx_phy_write(bcm, 0x0020, 0x0003); ++ bcm43xx_phy_write(bcm, 0x001D, 0x0F40); ++ bcm43xx_phy_write(bcm, 0x001F, 0x1C00); ++ ++ bcm43xx_phy_write(bcm, 0x002A, (bcm43xx_phy_read(bcm, 0x002A) & 0x00FF) | 0x0400); ++ bcm43xx_phy_write(bcm, 0x002B, bcm43xx_phy_read(bcm, 0x002B) & 0xFBFF); ++ bcm43xx_phy_write(bcm, 0x008E, 0x58C1); ++ ++ bcm43xx_ilt_write(bcm, 0x0803, 0x000F); ++ bcm43xx_ilt_write(bcm, 0x0804, 0x001F); ++ bcm43xx_ilt_write(bcm, 0x0805, 0x002A); ++ bcm43xx_ilt_write(bcm, 0x0805, 0x0030); ++ bcm43xx_ilt_write(bcm, 0x0807, 0x003A); ++ ++ bcm43xx_ilt_write(bcm, 0x0000, 0x0013); ++ bcm43xx_ilt_write(bcm, 0x0001, 0x0013); ++ bcm43xx_ilt_write(bcm, 0x0002, 0x0013); ++ bcm43xx_ilt_write(bcm, 0x0003, 0x0013); ++ bcm43xx_ilt_write(bcm, 0x0004, 0x0015); ++ bcm43xx_ilt_write(bcm, 0x0005, 0x0015); ++ bcm43xx_ilt_write(bcm, 0x0006, 0x0019); ++ ++ bcm43xx_ilt_write(bcm, 0x0404, 0x0003); ++ bcm43xx_ilt_write(bcm, 0x0405, 0x0003); ++ bcm43xx_ilt_write(bcm, 0x0406, 0x0007); ++ ++ for (i = 0; i < 16; i++) ++ bcm43xx_ilt_write(bcm, 0x4000 + i, (0x8 + i) & 0x000F); ++ ++ bcm43xx_ilt_write(bcm, 0x3003, 0x1044); ++ bcm43xx_ilt_write(bcm, 0x3004, 0x7201); ++ bcm43xx_ilt_write(bcm, 0x3006, 0x0040); ++ bcm43xx_ilt_write(bcm, 0x3001, (bcm43xx_ilt_read(bcm, 0x3001) & 0x0010) | 0x0008); ++ ++ for (i = 0; i < BCM43xx_ILT_FINEFREQA_SIZE; i++) ++ bcm43xx_ilt_write(bcm, 0x5800 + i, bcm43xx_ilt_finefreqa[i]); ++ for (i = 0; i < BCM43xx_ILT_NOISEA2_SIZE; i++) ++ bcm43xx_ilt_write(bcm, 0x1800 + i, bcm43xx_ilt_noisea2[i]); ++ for (i = 0; i < BCM43xx_ILT_ROTOR_SIZE; i++) ++ bcm43xx_ilt_write(bcm, 0x2000 + i, bcm43xx_ilt_rotor[i]); ++ bcm43xx_phy_init_noisescaletbl(bcm); ++ for (i = 0; i < BCM43xx_ILT_RETARD_SIZE; i++) ++ bcm43xx_ilt_write(bcm, 0x2400 + i, bcm43xx_ilt_retard[i]); ++ break; ++ case 3: ++ for (i = 0; i < 64; i++) ++ bcm43xx_ilt_write(bcm, 0x4000 + i, i); ++ ++ bcm43xx_ilt_write(bcm, 0x3807, 0x0051); ++ ++ bcm43xx_phy_write(bcm, 0x001C, 0x0FF9); ++ bcm43xx_phy_write(bcm, 0x0020, bcm43xx_phy_read(bcm, 0x0020) & 0xFF0F); ++ bcm43xx_radio_write16(bcm, 0x0002, 0x07BF); ++ ++ bcm43xx_phy_write(bcm, 0x0024, 0x4680); ++ bcm43xx_phy_write(bcm, 0x0020, 0x0003); ++ bcm43xx_phy_write(bcm, 0x001D, 0x0F40); ++ bcm43xx_phy_write(bcm, 0x001F, 0x1C00); ++ bcm43xx_phy_write(bcm, 0x002A, (bcm43xx_phy_read(bcm, 0x002A) & 0x00FF) | 0x0400); ++ ++ bcm43xx_ilt_write(bcm, 0x3001, (bcm43xx_ilt_read(bcm, 0x3001) & 0x0010) | 0x0008); ++ for (i = 0; i < BCM43xx_ILT_NOISEA3_SIZE; i++) ++ bcm43xx_ilt_write(bcm, 0x1800 + i, bcm43xx_ilt_noisea3[i]); ++ bcm43xx_phy_init_noisescaletbl(bcm); ++ for (i = 0; i < BCM43xx_ILT_SIGMASQR_SIZE; i++) ++ bcm43xx_ilt_write(bcm, 0x5000 + i, bcm43xx_ilt_sigmasqr1[i]); ++ ++ bcm43xx_phy_write(bcm, 0x0003, 0x1808); ++ ++ bcm43xx_ilt_write(bcm, 0x0803, 0x000F); ++ bcm43xx_ilt_write(bcm, 0x0804, 0x001F); ++ bcm43xx_ilt_write(bcm, 0x0805, 0x002A); ++ bcm43xx_ilt_write(bcm, 0x0805, 0x0030); ++ bcm43xx_ilt_write(bcm, 0x0807, 0x003A); ++ ++ bcm43xx_ilt_write(bcm, 0x0000, 0x0013); ++ bcm43xx_ilt_write(bcm, 0x0001, 0x0013); ++ bcm43xx_ilt_write(bcm, 0x0002, 0x0013); ++ bcm43xx_ilt_write(bcm, 0x0003, 0x0013); ++ bcm43xx_ilt_write(bcm, 0x0004, 0x0015); ++ bcm43xx_ilt_write(bcm, 0x0005, 0x0015); ++ bcm43xx_ilt_write(bcm, 0x0006, 0x0019); ++ ++ bcm43xx_ilt_write(bcm, 0x0404, 0x0003); ++ bcm43xx_ilt_write(bcm, 0x0405, 0x0003); ++ bcm43xx_ilt_write(bcm, 0x0406, 0x0007); ++ ++ bcm43xx_ilt_write(bcm, 0x3C02, 0x000F); ++ bcm43xx_ilt_write(bcm, 0x3C03, 0x0014); ++ break; ++ default: ++ assert(0); ++ } ++} ++ ++/* Initialize APHY. This is also called for the GPHY in some cases. */ ++static void bcm43xx_phy_inita(struct bcm43xx_private *bcm) ++{ ++ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); ++ struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm); ++ u16 tval; ++ ++ if (phy->type == BCM43xx_PHYTYPE_A) { ++ bcm43xx_phy_setupa(bcm); ++ } else { ++ bcm43xx_phy_setupg(bcm); ++ if (bcm->sprom.boardflags & BCM43xx_BFL_PACTRL) ++ bcm43xx_phy_write(bcm, 0x046E, 0x03CF); ++ return; ++ } ++ ++ bcm43xx_phy_write(bcm, BCM43xx_PHY_A_CRS, ++ (bcm43xx_phy_read(bcm, BCM43xx_PHY_A_CRS) & 0xF83C) | 0x0340); ++ bcm43xx_phy_write(bcm, 0x0034, 0x0001); ++ ++ TODO();//TODO: RSSI AGC ++ bcm43xx_phy_write(bcm, BCM43xx_PHY_A_CRS, ++ bcm43xx_phy_read(bcm, BCM43xx_PHY_A_CRS) | (1 << 14)); ++ bcm43xx_radio_init2060(bcm); ++ ++ if ((bcm->board_vendor == PCI_VENDOR_ID_BROADCOM) ++ && ((bcm->board_type == 0x0416) || (bcm->board_type == 0x040A))) { ++ if (radio->lofcal == 0xFFFF) { ++ TODO();//TODO: LOF Cal ++ bcm43xx_radio_set_tx_iq(bcm); ++ } else ++ bcm43xx_radio_write16(bcm, 0x001E, radio->lofcal); ++ } ++ ++ bcm43xx_phy_write(bcm, 0x007A, 0xF111); ++ ++ if (phy->savedpctlreg == 0xFFFF) { ++ bcm43xx_radio_write16(bcm, 0x0019, 0x0000); ++ bcm43xx_radio_write16(bcm, 0x0017, 0x0020); ++ ++ tval = bcm43xx_ilt_read(bcm, 0x3001); ++ if (phy->rev == 1) { ++ bcm43xx_ilt_write(bcm, 0x3001, ++ (bcm43xx_ilt_read(bcm, 0x3001) & 0xFF87) ++ | 0x0058); ++ } else { ++ bcm43xx_ilt_write(bcm, 0x3001, ++ (bcm43xx_ilt_read(bcm, 0x3001) & 0xFFC3) ++ | 0x002C); ++ } ++ bcm43xx_dummy_transmission(bcm); ++ phy->savedpctlreg = bcm43xx_phy_read(bcm, BCM43xx_PHY_A_PCTL); ++ bcm43xx_ilt_write(bcm, 0x3001, tval); ++ ++ bcm43xx_radio_set_txpower_a(bcm, 0x0018); ++ } ++ bcm43xx_radio_clear_tssi(bcm); ++} ++ ++static void bcm43xx_phy_initb2(struct bcm43xx_private *bcm) ++{ ++ struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm); ++ u16 offset, val; ++ ++ bcm43xx_write16(bcm, 0x03EC, 0x3F22); ++ bcm43xx_phy_write(bcm, 0x0020, 0x301C); ++ bcm43xx_phy_write(bcm, 0x0026, 0x0000); ++ bcm43xx_phy_write(bcm, 0x0030, 0x00C6); ++ bcm43xx_phy_write(bcm, 0x0088, 0x3E00); ++ val = 0x3C3D; ++ for (offset = 0x0089; offset < 0x00A7; offset++) { ++ bcm43xx_phy_write(bcm, offset, val); ++ val -= 0x0202; ++ } ++ bcm43xx_phy_write(bcm, 0x03E4, 0x3000); ++ if (radio->channel == 0xFF) ++ bcm43xx_radio_selectchannel(bcm, BCM43xx_RADIO_DEFAULT_CHANNEL_BG, 0); ++ else ++ bcm43xx_radio_selectchannel(bcm, radio->channel, 0); ++ if (radio->version != 0x2050) { ++ bcm43xx_radio_write16(bcm, 0x0075, 0x0080); ++ bcm43xx_radio_write16(bcm, 0x0079, 0x0081); ++ } ++ bcm43xx_radio_write16(bcm, 0x0050, 0x0020); ++ bcm43xx_radio_write16(bcm, 0x0050, 0x0023); ++ if (radio->version == 0x2050) { ++ bcm43xx_radio_write16(bcm, 0x0050, 0x0020); ++ bcm43xx_radio_write16(bcm, 0x005A, 0x0070); ++ bcm43xx_radio_write16(bcm, 0x005B, 0x007B); ++ bcm43xx_radio_write16(bcm, 0x005C, 0x00B0); ++ bcm43xx_radio_write16(bcm, 0x007A, 0x000F); ++ bcm43xx_phy_write(bcm, 0x0038, 0x0677); ++ bcm43xx_radio_init2050(bcm); ++ } ++ bcm43xx_phy_write(bcm, 0x0014, 0x0080); ++ bcm43xx_phy_write(bcm, 0x0032, 0x00CA); ++ bcm43xx_phy_write(bcm, 0x0032, 0x00CC); ++ bcm43xx_phy_write(bcm, 0x0035, 0x07C2); ++ bcm43xx_phy_lo_b_measure(bcm); ++ bcm43xx_phy_write(bcm, 0x0026, 0xCC00); ++ if (radio->version != 0x2050) ++ bcm43xx_phy_write(bcm, 0x0026, 0xCE00); ++ bcm43xx_write16(bcm, BCM43xx_MMIO_CHANNEL_EXT, 0x1000); ++ bcm43xx_phy_write(bcm, 0x002A, 0x88A3); ++ if (radio->version != 0x2050) ++ bcm43xx_phy_write(bcm, 0x002A, 0x88C2); ++ bcm43xx_radio_set_txpower_bg(bcm, 0xFFFF, 0xFFFF, 0xFFFF); ++ bcm43xx_phy_init_pctl(bcm); ++} ++ ++static void bcm43xx_phy_initb4(struct bcm43xx_private *bcm) ++{ ++ struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm); ++ u16 offset, val; ++ ++ bcm43xx_write16(bcm, 0x03EC, 0x3F22); ++ bcm43xx_phy_write(bcm, 0x0020, 0x301C); ++ bcm43xx_phy_write(bcm, 0x0026, 0x0000); ++ bcm43xx_phy_write(bcm, 0x0030, 0x00C6); ++ bcm43xx_phy_write(bcm, 0x0088, 0x3E00); ++ val = 0x3C3D; ++ for (offset = 0x0089; offset < 0x00A7; offset++) { ++ bcm43xx_phy_write(bcm, offset, val); ++ val -= 0x0202; ++ } ++ bcm43xx_phy_write(bcm, 0x03E4, 0x3000); ++ if (radio->channel == 0xFF) ++ bcm43xx_radio_selectchannel(bcm, BCM43xx_RADIO_DEFAULT_CHANNEL_BG, 0); ++ else ++ bcm43xx_radio_selectchannel(bcm, radio->channel, 0); ++ if (radio->version != 0x2050) { ++ bcm43xx_radio_write16(bcm, 0x0075, 0x0080); ++ bcm43xx_radio_write16(bcm, 0x0079, 0x0081); ++ } ++ bcm43xx_radio_write16(bcm, 0x0050, 0x0020); ++ bcm43xx_radio_write16(bcm, 0x0050, 0x0023); ++ if (radio->version == 0x2050) { ++ bcm43xx_radio_write16(bcm, 0x0050, 0x0020); ++ bcm43xx_radio_write16(bcm, 0x005A, 0x0070); ++ bcm43xx_radio_write16(bcm, 0x005B, 0x007B); ++ bcm43xx_radio_write16(bcm, 0x005C, 0x00B0); ++ bcm43xx_radio_write16(bcm, 0x007A, 0x000F); ++ bcm43xx_phy_write(bcm, 0x0038, 0x0677); ++ bcm43xx_radio_init2050(bcm); ++ } ++ bcm43xx_phy_write(bcm, 0x0014, 0x0080); ++ bcm43xx_phy_write(bcm, 0x0032, 0x00CA); ++ if (radio->version == 0x2050) ++ bcm43xx_phy_write(bcm, 0x0032, 0x00E0); ++ bcm43xx_phy_write(bcm, 0x0035, 0x07C2); ++ ++ bcm43xx_phy_lo_b_measure(bcm); ++ ++ bcm43xx_phy_write(bcm, 0x0026, 0xCC00); ++ if (radio->version == 0x2050) ++ bcm43xx_phy_write(bcm, 0x0026, 0xCE00); ++ bcm43xx_write16(bcm, BCM43xx_MMIO_CHANNEL_EXT, 0x1100); ++ bcm43xx_phy_write(bcm, 0x002A, 0x88A3); ++ if (radio->version == 0x2050) ++ bcm43xx_phy_write(bcm, 0x002A, 0x88C2); ++ bcm43xx_radio_set_txpower_bg(bcm, 0xFFFF, 0xFFFF, 0xFFFF); ++ if (bcm->sprom.boardflags & BCM43xx_BFL_RSSI) { ++ bcm43xx_calc_nrssi_slope(bcm); ++ bcm43xx_calc_nrssi_threshold(bcm); ++ } ++ bcm43xx_phy_init_pctl(bcm); ++} ++ ++static void bcm43xx_phy_initb5(struct bcm43xx_private *bcm) ++{ ++ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); ++ struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm); ++ u16 offset; ++ ++ if (phy->version == 1 && ++ radio->version == 0x2050) { ++ bcm43xx_radio_write16(bcm, 0x007A, ++ bcm43xx_radio_read16(bcm, 0x007A) ++ | 0x0050); ++ } ++ if ((bcm->board_vendor != PCI_VENDOR_ID_BROADCOM) && ++ (bcm->board_type != 0x0416)) { ++ for (offset = 0x00A8 ; offset < 0x00C7; offset++) { ++ bcm43xx_phy_write(bcm, offset, ++ (bcm43xx_phy_read(bcm, offset) + 0x2020) ++ & 0x3F3F); ++ } ++ } ++ bcm43xx_phy_write(bcm, 0x0035, ++ (bcm43xx_phy_read(bcm, 0x0035) & 0xF0FF) ++ | 0x0700); ++ if (radio->version == 0x2050) ++ bcm43xx_phy_write(bcm, 0x0038, 0x0667); ++ ++ if (phy->connected) { ++ if (radio->version == 0x2050) { ++ bcm43xx_radio_write16(bcm, 0x007A, ++ bcm43xx_radio_read16(bcm, 0x007A) ++ | 0x0020); ++ bcm43xx_radio_write16(bcm, 0x0051, ++ bcm43xx_radio_read16(bcm, 0x0051) ++ | 0x0004); ++ } ++ bcm43xx_write16(bcm, BCM43xx_MMIO_PHY_RADIO, 0x0000); ++ ++ bcm43xx_phy_write(bcm, 0x0802, bcm43xx_phy_read(bcm, 0x0802) | 0x0100); ++ bcm43xx_phy_write(bcm, 0x042B, bcm43xx_phy_read(bcm, 0x042B) | 0x2000); ++ ++ bcm43xx_phy_write(bcm, 0x001C, 0x186A); ++ ++ bcm43xx_phy_write(bcm, 0x0013, (bcm43xx_phy_read(bcm, 0x0013) & 0x00FF) | 0x1900); ++ bcm43xx_phy_write(bcm, 0x0035, (bcm43xx_phy_read(bcm, 0x0035) & 0xFFC0) | 0x0064); ++ bcm43xx_phy_write(bcm, 0x005D, (bcm43xx_phy_read(bcm, 0x005D) & 0xFF80) | 0x000A); ++ } ++ ++ if (bcm->bad_frames_preempt) { ++ bcm43xx_phy_write(bcm, BCM43xx_PHY_RADIO_BITFIELD, ++ bcm43xx_phy_read(bcm, BCM43xx_PHY_RADIO_BITFIELD) | (1 << 11)); ++ } ++ ++ if (phy->version == 1 && radio->version == 0x2050) { ++ bcm43xx_phy_write(bcm, 0x0026, 0xCE00); ++ bcm43xx_phy_write(bcm, 0x0021, 0x3763); ++ bcm43xx_phy_write(bcm, 0x0022, 0x1BC3); ++ bcm43xx_phy_write(bcm, 0x0023, 0x06F9); ++ bcm43xx_phy_write(bcm, 0x0024, 0x037E); ++ } else ++ bcm43xx_phy_write(bcm, 0x0026, 0xCC00); ++ bcm43xx_phy_write(bcm, 0x0030, 0x00C6); ++ bcm43xx_write16(bcm, 0x03EC, 0x3F22); ++ ++ if (phy->version == 1 && radio->version == 0x2050) ++ bcm43xx_phy_write(bcm, 0x0020, 0x3E1C); ++ else ++ bcm43xx_phy_write(bcm, 0x0020, 0x301C); ++ ++ if (phy->version == 0) ++ bcm43xx_write16(bcm, 0x03E4, 0x3000); ++ ++ /* Force to channel 7, even if not supported. */ ++ bcm43xx_radio_selectchannel(bcm, 7, 0); ++ ++ if (radio->version != 0x2050) { ++ bcm43xx_radio_write16(bcm, 0x0075, 0x0080); ++ bcm43xx_radio_write16(bcm, 0x0079, 0x0081); ++ } ++ ++ bcm43xx_radio_write16(bcm, 0x0050, 0x0020); ++ bcm43xx_radio_write16(bcm, 0x0050, 0x0023); ++ ++ if (radio->version == 0x2050) { ++ bcm43xx_radio_write16(bcm, 0x0050, 0x0020); ++ bcm43xx_radio_write16(bcm, 0x005A, 0x0070); ++ } ++ ++ bcm43xx_radio_write16(bcm, 0x005B, 0x007B); ++ bcm43xx_radio_write16(bcm, 0x005C, 0x00B0); ++ ++ bcm43xx_radio_write16(bcm, 0x007A, bcm43xx_radio_read16(bcm, 0x007A) | 0x0007); ++ ++ bcm43xx_radio_selectchannel(bcm, BCM43xx_RADIO_DEFAULT_CHANNEL_BG, 0); ++ ++ bcm43xx_phy_write(bcm, 0x0014, 0x0080); ++ bcm43xx_phy_write(bcm, 0x0032, 0x00CA); ++ bcm43xx_phy_write(bcm, 0x88A3, 0x002A); ++ ++ bcm43xx_radio_set_txpower_bg(bcm, 0xFFFF, 0xFFFF, 0xFFFF); ++ ++ if (radio->version == 0x2050) ++ bcm43xx_radio_write16(bcm, 0x005D, 0x000D); ++ ++ bcm43xx_write16(bcm, 0x03E4, (bcm43xx_read16(bcm, 0x03E4) & 0xFFC0) | 0x0004); ++} ++ ++static void bcm43xx_phy_initb6(struct bcm43xx_private *bcm) ++{ ++ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); ++ struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm); ++ u16 offset, val; ++ ++ bcm43xx_phy_write(bcm, 0x003E, 0x817A); ++ bcm43xx_radio_write16(bcm, 0x007A, ++ (bcm43xx_radio_read16(bcm, 0x007A) | 0x0058)); ++ if ((radio->manufact == 0x17F) && ++ (radio->version == 0x2050) && ++ (radio->revision == 3 || ++ radio->revision == 4 || ++ radio->revision == 5)) { ++ bcm43xx_radio_write16(bcm, 0x0051, 0x001F); ++ bcm43xx_radio_write16(bcm, 0x0052, 0x0040); ++ bcm43xx_radio_write16(bcm, 0x0053, 0x005B); ++ bcm43xx_radio_write16(bcm, 0x0054, 0x0098); ++ bcm43xx_radio_write16(bcm, 0x005A, 0x0088); ++ bcm43xx_radio_write16(bcm, 0x005B, 0x0088); ++ bcm43xx_radio_write16(bcm, 0x005D, 0x0088); ++ bcm43xx_radio_write16(bcm, 0x005E, 0x0088); ++ bcm43xx_radio_write16(bcm, 0x007D, 0x0088); ++ } ++ if ((radio->manufact == 0x17F) && ++ (radio->version == 0x2050) && ++ (radio->revision == 6)) { ++ bcm43xx_radio_write16(bcm, 0x0051, 0x0000); ++ bcm43xx_radio_write16(bcm, 0x0052, 0x0040); ++ bcm43xx_radio_write16(bcm, 0x0053, 0x00B7); ++ bcm43xx_radio_write16(bcm, 0x0054, 0x0098); ++ bcm43xx_radio_write16(bcm, 0x005A, 0x0088); ++ bcm43xx_radio_write16(bcm, 0x005B, 0x008B); ++ bcm43xx_radio_write16(bcm, 0x005C, 0x00B5); ++ bcm43xx_radio_write16(bcm, 0x005D, 0x0088); ++ bcm43xx_radio_write16(bcm, 0x005E, 0x0088); ++ bcm43xx_radio_write16(bcm, 0x007D, 0x0088); ++ bcm43xx_radio_write16(bcm, 0x007C, 0x0001); ++ bcm43xx_radio_write16(bcm, 0x007E, 0x0008); ++ } ++ if ((radio->manufact == 0x17F) && ++ (radio->version == 0x2050) && ++ (radio->revision == 7)) { ++ bcm43xx_radio_write16(bcm, 0x0051, 0x0000); ++ bcm43xx_radio_write16(bcm, 0x0052, 0x0040); ++ bcm43xx_radio_write16(bcm, 0x0053, 0x00B7); ++ bcm43xx_radio_write16(bcm, 0x0054, 0x0098); ++ bcm43xx_radio_write16(bcm, 0x005A, 0x0088); ++ bcm43xx_radio_write16(bcm, 0x005B, 0x00A8); ++ bcm43xx_radio_write16(bcm, 0x005C, 0x0075); ++ bcm43xx_radio_write16(bcm, 0x005D, 0x00F5); ++ bcm43xx_radio_write16(bcm, 0x005E, 0x00B8); ++ bcm43xx_radio_write16(bcm, 0x007D, 0x00E8); ++ bcm43xx_radio_write16(bcm, 0x007C, 0x0001); ++ bcm43xx_radio_write16(bcm, 0x007E, 0x0008); ++ bcm43xx_radio_write16(bcm, 0x007B, 0x0000); ++ } ++ if ((radio->manufact == 0x17F) && ++ (radio->version == 0x2050) && ++ (radio->revision == 8)) { ++ bcm43xx_radio_write16(bcm, 0x0051, 0x0000); ++ bcm43xx_radio_write16(bcm, 0x0052, 0x0040); ++ bcm43xx_radio_write16(bcm, 0x0053, 0x00B7); ++ bcm43xx_radio_write16(bcm, 0x0054, 0x0098); ++ bcm43xx_radio_write16(bcm, 0x005A, 0x0088); ++ bcm43xx_radio_write16(bcm, 0x005B, 0x006B); ++ bcm43xx_radio_write16(bcm, 0x005C, 0x000F); ++ if (bcm->sprom.boardflags & 0x8000) { ++ bcm43xx_radio_write16(bcm, 0x005D, 0x00FA); ++ bcm43xx_radio_write16(bcm, 0x005E, 0x00D8); ++ } else { ++ bcm43xx_radio_write16(bcm, 0x005D, 0x00F5); ++ bcm43xx_radio_write16(bcm, 0x005E, 0x00B8); ++ } ++ bcm43xx_radio_write16(bcm, 0x0073, 0x0003); ++ bcm43xx_radio_write16(bcm, 0x007D, 0x00A8); ++ bcm43xx_radio_write16(bcm, 0x007C, 0x0001); ++ bcm43xx_radio_write16(bcm, 0x007E, 0x0008); ++ } ++ val = 0x1E1F; ++ for (offset = 0x0088; offset < 0x0098; offset++) { ++ bcm43xx_phy_write(bcm, offset, val); ++ val -= 0x0202; ++ } ++ val = 0x3E3F; ++ for (offset = 0x0098; offset < 0x00A8; offset++) { ++ bcm43xx_phy_write(bcm, offset, val); ++ val -= 0x0202; ++ } ++ val = 0x2120; ++ for (offset = 0x00A8; offset < 0x00C8; offset++) { ++ bcm43xx_phy_write(bcm, offset, (val & 0x3F3F)); ++ val += 0x0202; ++ } ++ if (phy->type == BCM43xx_PHYTYPE_G) { ++ bcm43xx_radio_write16(bcm, 0x007A, ++ bcm43xx_radio_read16(bcm, 0x007A) | 0x0020); ++ bcm43xx_radio_write16(bcm, 0x0051, ++ bcm43xx_radio_read16(bcm, 0x0051) | 0x0004); ++ bcm43xx_phy_write(bcm, 0x0802, ++ bcm43xx_phy_read(bcm, 0x0802) | 0x0100); ++ bcm43xx_phy_write(bcm, 0x042B, ++ bcm43xx_phy_read(bcm, 0x042B) | 0x2000); ++ } ++ ++ /* Force to channel 7, even if not supported. */ ++ bcm43xx_radio_selectchannel(bcm, 7, 0); ++ ++ bcm43xx_radio_write16(bcm, 0x0050, 0x0020); ++ bcm43xx_radio_write16(bcm, 0x0050, 0x0023); ++ udelay(40); ++ bcm43xx_radio_write16(bcm, 0x007C, (bcm43xx_radio_read16(bcm, 0x007C) | 0x0002)); ++ bcm43xx_radio_write16(bcm, 0x0050, 0x0020); ++ if (radio->manufact == 0x17F && ++ radio->version == 0x2050 && ++ radio->revision <= 2) { ++ bcm43xx_radio_write16(bcm, 0x0050, 0x0020); ++ bcm43xx_radio_write16(bcm, 0x005A, 0x0070); ++ bcm43xx_radio_write16(bcm, 0x005B, 0x007B); ++ bcm43xx_radio_write16(bcm, 0x005C, 0x00B0); ++ } ++ bcm43xx_radio_write16(bcm, 0x007A, ++ (bcm43xx_radio_read16(bcm, 0x007A) & 0x00F8) | 0x0007); ++ ++ bcm43xx_radio_selectchannel(bcm, BCM43xx_RADIO_DEFAULT_CHANNEL_BG, 0); ++ ++ bcm43xx_phy_write(bcm, 0x0014, 0x0200); ++ if (radio->version == 0x2050){ ++ if (radio->revision == 3 || ++ radio->revision == 4 || ++ radio->revision == 5) ++ bcm43xx_phy_write(bcm, 0x002A, 0x8AC0); ++ else ++ bcm43xx_phy_write(bcm, 0x002A, 0x88C2); ++ } ++ bcm43xx_phy_write(bcm, 0x0038, 0x0668); ++ bcm43xx_radio_set_txpower_bg(bcm, 0xFFFF, 0xFFFF, 0xFFFF); ++ if (radio->version == 0x2050) { ++ if (radio->revision == 3 || ++ radio->revision == 4 || ++ radio->revision == 5) ++ bcm43xx_phy_write(bcm, 0x005D, bcm43xx_phy_read(bcm, 0x005D) | 0x0003); ++ else if (radio->revision <= 2) ++ bcm43xx_radio_write16(bcm, 0x005D, 0x000D); ++ } ++ ++ if (phy->rev == 4) ++ bcm43xx_phy_write(bcm, 0x0002, (bcm43xx_phy_read(bcm, 0x0002) & 0xFFC0) | 0x0004); ++ else ++ bcm43xx_write16(bcm, 0x03E4, 0x0009); ++ if (phy->type == BCM43xx_PHYTYPE_B) { ++ bcm43xx_write16(bcm, 0x03E6, 0x8140); ++ bcm43xx_phy_write(bcm, 0x0016, 0x0410); ++ bcm43xx_phy_write(bcm, 0x0017, 0x0820); ++ bcm43xx_phy_write(bcm, 0x0062, 0x0007); ++ (void) bcm43xx_radio_calibrationvalue(bcm); ++ bcm43xx_phy_lo_b_measure(bcm); ++ if (bcm->sprom.boardflags & BCM43xx_BFL_RSSI) { ++ bcm43xx_calc_nrssi_slope(bcm); ++ bcm43xx_calc_nrssi_threshold(bcm); ++ } ++ bcm43xx_phy_init_pctl(bcm); ++ } else ++ bcm43xx_write16(bcm, 0x03E6, 0x0); ++} ++ ++static void bcm43xx_calc_loopback_gain(struct bcm43xx_private *bcm) ++{ ++ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); ++ struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm); ++ u16 backup_phy[15]; ++ u16 backup_radio[3]; ++ u16 backup_bband; ++ u16 i; ++ u16 loop1_cnt, loop1_done, loop1_omitted; ++ u16 loop2_done; ++ ++ backup_phy[0] = bcm43xx_phy_read(bcm, 0x0429); ++ backup_phy[1] = bcm43xx_phy_read(bcm, 0x0001); ++ backup_phy[2] = bcm43xx_phy_read(bcm, 0x0811); ++ backup_phy[3] = bcm43xx_phy_read(bcm, 0x0812); ++ backup_phy[4] = bcm43xx_phy_read(bcm, 0x0814); ++ backup_phy[5] = bcm43xx_phy_read(bcm, 0x0815); ++ backup_phy[6] = bcm43xx_phy_read(bcm, 0x005A); ++ backup_phy[7] = bcm43xx_phy_read(bcm, 0x0059); ++ backup_phy[8] = bcm43xx_phy_read(bcm, 0x0058); ++ backup_phy[9] = bcm43xx_phy_read(bcm, 0x000A); ++ backup_phy[10] = bcm43xx_phy_read(bcm, 0x0003); ++ backup_phy[11] = bcm43xx_phy_read(bcm, 0x080F); ++ backup_phy[12] = bcm43xx_phy_read(bcm, 0x0810); ++ backup_phy[13] = bcm43xx_phy_read(bcm, 0x002B); ++ backup_phy[14] = bcm43xx_phy_read(bcm, 0x0015); ++ bcm43xx_phy_read(bcm, 0x002D); /* dummy read */ ++ backup_bband = radio->baseband_atten; ++ backup_radio[0] = bcm43xx_radio_read16(bcm, 0x0052); ++ backup_radio[1] = bcm43xx_radio_read16(bcm, 0x0043); ++ backup_radio[2] = bcm43xx_radio_read16(bcm, 0x007A); ++ ++ bcm43xx_phy_write(bcm, 0x0429, ++ bcm43xx_phy_read(bcm, 0x0429) & 0x3FFF); ++ bcm43xx_phy_write(bcm, 0x0001, ++ bcm43xx_phy_read(bcm, 0x0001) & 0x8000); ++ bcm43xx_phy_write(bcm, 0x0811, ++ bcm43xx_phy_read(bcm, 0x0811) | 0x0002); ++ bcm43xx_phy_write(bcm, 0x0812, ++ bcm43xx_phy_read(bcm, 0x0812) & 0xFFFD); ++ bcm43xx_phy_write(bcm, 0x0811, ++ bcm43xx_phy_read(bcm, 0x0811) | 0x0001); ++ bcm43xx_phy_write(bcm, 0x0812, ++ bcm43xx_phy_read(bcm, 0x0812) & 0xFFFE); ++ bcm43xx_phy_write(bcm, 0x0814, ++ bcm43xx_phy_read(bcm, 0x0814) | 0x0001); ++ bcm43xx_phy_write(bcm, 0x0815, ++ bcm43xx_phy_read(bcm, 0x0815) & 0xFFFE); ++ bcm43xx_phy_write(bcm, 0x0814, ++ bcm43xx_phy_read(bcm, 0x0814) | 0x0002); ++ bcm43xx_phy_write(bcm, 0x0815, ++ bcm43xx_phy_read(bcm, 0x0815) & 0xFFFD); ++ bcm43xx_phy_write(bcm, 0x0811, ++ bcm43xx_phy_read(bcm, 0x0811) | 0x000C); ++ bcm43xx_phy_write(bcm, 0x0812, ++ bcm43xx_phy_read(bcm, 0x0812) | 0x000C); ++ ++ bcm43xx_phy_write(bcm, 0x0811, ++ (bcm43xx_phy_read(bcm, 0x0811) ++ & 0xFFCF) | 0x0030); ++ bcm43xx_phy_write(bcm, 0x0812, ++ (bcm43xx_phy_read(bcm, 0x0812) ++ & 0xFFCF) | 0x0010); ++ ++ bcm43xx_phy_write(bcm, 0x005A, 0x0780); ++ bcm43xx_phy_write(bcm, 0x0059, 0xC810); ++ bcm43xx_phy_write(bcm, 0x0058, 0x000D); ++ if (phy->version == 0) { ++ bcm43xx_phy_write(bcm, 0x0003, 0x0122); ++ } else { ++ bcm43xx_phy_write(bcm, 0x000A, ++ bcm43xx_phy_read(bcm, 0x000A) ++ | 0x2000); ++ } ++ bcm43xx_phy_write(bcm, 0x0814, ++ bcm43xx_phy_read(bcm, 0x0814) | 0x0004); ++ bcm43xx_phy_write(bcm, 0x0815, ++ bcm43xx_phy_read(bcm, 0x0815) & 0xFFFB); ++ bcm43xx_phy_write(bcm, 0x0003, ++ (bcm43xx_phy_read(bcm, 0x0003) ++ & 0xFF9F) | 0x0040); ++ if (radio->version == 0x2050 && radio->revision == 2) { ++ bcm43xx_radio_write16(bcm, 0x0052, 0x0000); ++ bcm43xx_radio_write16(bcm, 0x0043, ++ (bcm43xx_radio_read16(bcm, 0x0043) ++ & 0xFFF0) | 0x0009); ++ loop1_cnt = 9; ++ } else if (radio->revision == 8) { ++ bcm43xx_radio_write16(bcm, 0x0043, 0x000F); ++ loop1_cnt = 15; ++ } else ++ loop1_cnt = 0; ++ ++ bcm43xx_phy_set_baseband_attenuation(bcm, 11); ++ ++ if (phy->rev >= 3) ++ bcm43xx_phy_write(bcm, 0x080F, 0xC020); ++ else ++ bcm43xx_phy_write(bcm, 0x080F, 0x8020); ++ bcm43xx_phy_write(bcm, 0x0810, 0x0000); ++ ++ bcm43xx_phy_write(bcm, 0x002B, ++ (bcm43xx_phy_read(bcm, 0x002B) ++ & 0xFFC0) | 0x0001); ++ bcm43xx_phy_write(bcm, 0x002B, ++ (bcm43xx_phy_read(bcm, 0x002B) ++ & 0xC0FF) | 0x0800); ++ bcm43xx_phy_write(bcm, 0x0811, ++ bcm43xx_phy_read(bcm, 0x0811) | 0x0100); ++ bcm43xx_phy_write(bcm, 0x0812, ++ bcm43xx_phy_read(bcm, 0x0812) & 0xCFFF); ++ if (bcm->sprom.boardflags & BCM43xx_BFL_EXTLNA) { ++ if (phy->rev >= 7) { ++ bcm43xx_phy_write(bcm, 0x0811, ++ bcm43xx_phy_read(bcm, 0x0811) ++ | 0x0800); ++ bcm43xx_phy_write(bcm, 0x0812, ++ bcm43xx_phy_read(bcm, 0x0812) ++ | 0x8000); ++ } ++ } ++ bcm43xx_radio_write16(bcm, 0x007A, ++ bcm43xx_radio_read16(bcm, 0x007A) ++ & 0x00F7); ++ ++ for (i = 0; i < loop1_cnt; i++) { ++ bcm43xx_radio_write16(bcm, 0x0043, loop1_cnt); ++ bcm43xx_phy_write(bcm, 0x0812, ++ (bcm43xx_phy_read(bcm, 0x0812) ++ & 0xF0FF) | (i << 8)); ++ bcm43xx_phy_write(bcm, 0x0015, ++ (bcm43xx_phy_read(bcm, 0x0015) ++ & 0x0FFF) | 0xA000); ++ bcm43xx_phy_write(bcm, 0x0015, ++ (bcm43xx_phy_read(bcm, 0x0015) ++ & 0x0FFF) | 0xF000); ++ udelay(20); ++ if (bcm43xx_phy_read(bcm, 0x002D) >= 0x0DFC) ++ break; ++ } ++ loop1_done = i; ++ loop1_omitted = loop1_cnt - loop1_done; ++ ++ loop2_done = 0; ++ if (loop1_done >= 8) { ++ bcm43xx_phy_write(bcm, 0x0812, ++ bcm43xx_phy_read(bcm, 0x0812) ++ | 0x0030); ++ for (i = loop1_done - 8; i < 16; i++) { ++ bcm43xx_phy_write(bcm, 0x0812, ++ (bcm43xx_phy_read(bcm, 0x0812) ++ & 0xF0FF) | (i << 8)); ++ bcm43xx_phy_write(bcm, 0x0015, ++ (bcm43xx_phy_read(bcm, 0x0015) ++ & 0x0FFF) | 0xA000); ++ bcm43xx_phy_write(bcm, 0x0015, ++ (bcm43xx_phy_read(bcm, 0x0015) ++ & 0x0FFF) | 0xF000); ++ udelay(20); ++ if (bcm43xx_phy_read(bcm, 0x002D) >= 0x0DFC) ++ break; ++ } ++ } ++ ++ bcm43xx_phy_write(bcm, 0x0814, backup_phy[4]); ++ bcm43xx_phy_write(bcm, 0x0815, backup_phy[5]); ++ bcm43xx_phy_write(bcm, 0x005A, backup_phy[6]); ++ bcm43xx_phy_write(bcm, 0x0059, backup_phy[7]); ++ bcm43xx_phy_write(bcm, 0x0058, backup_phy[8]); ++ bcm43xx_phy_write(bcm, 0x000A, backup_phy[9]); ++ bcm43xx_phy_write(bcm, 0x0003, backup_phy[10]); ++ bcm43xx_phy_write(bcm, 0x080F, backup_phy[11]); ++ bcm43xx_phy_write(bcm, 0x0810, backup_phy[12]); ++ bcm43xx_phy_write(bcm, 0x002B, backup_phy[13]); ++ bcm43xx_phy_write(bcm, 0x0015, backup_phy[14]); ++ ++ bcm43xx_phy_set_baseband_attenuation(bcm, backup_bband); ++ ++ bcm43xx_radio_write16(bcm, 0x0052, backup_radio[0]); ++ bcm43xx_radio_write16(bcm, 0x0043, backup_radio[1]); ++ bcm43xx_radio_write16(bcm, 0x007A, backup_radio[2]); ++ ++ bcm43xx_phy_write(bcm, 0x0811, backup_phy[2] | 0x0003); ++ udelay(10); ++ bcm43xx_phy_write(bcm, 0x0811, backup_phy[2]); ++ bcm43xx_phy_write(bcm, 0x0812, backup_phy[3]); ++ bcm43xx_phy_write(bcm, 0x0429, backup_phy[0]); ++ bcm43xx_phy_write(bcm, 0x0001, backup_phy[1]); ++ ++ phy->loopback_gain[0] = ((loop1_done * 6) - (loop1_omitted * 4)) - 11; ++ phy->loopback_gain[1] = (24 - (3 * loop2_done)) * 2; ++} ++ ++static void bcm43xx_phy_initg(struct bcm43xx_private *bcm) ++{ ++ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); ++ struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm); ++ u16 tmp; ++ ++ if (phy->rev == 1) ++ bcm43xx_phy_initb5(bcm); ++ else if (phy->rev >= 2 && phy->rev <= 7) ++ bcm43xx_phy_initb6(bcm); ++ if (phy->rev >= 2 || phy->connected) ++ bcm43xx_phy_inita(bcm); ++ ++ if (phy->rev >= 2) { ++ bcm43xx_phy_write(bcm, 0x0814, 0x0000); ++ bcm43xx_phy_write(bcm, 0x0815, 0x0000); ++ if (phy->rev == 2) ++ bcm43xx_phy_write(bcm, 0x0811, 0x0000); ++ else if (phy->rev >= 3) ++ bcm43xx_phy_write(bcm, 0x0811, 0x0400); ++ bcm43xx_phy_write(bcm, 0x0015, 0x00C0); ++ if (phy->connected) { ++ tmp = bcm43xx_phy_read(bcm, 0x0400) & 0xFF; ++ if (tmp < 6) { ++ bcm43xx_phy_write(bcm, 0x04C2, 0x1816); ++ bcm43xx_phy_write(bcm, 0x04C3, 0x8006); ++ if (tmp != 3) { ++ bcm43xx_phy_write(bcm, 0x04CC, ++ (bcm43xx_phy_read(bcm, 0x04CC) ++ & 0x00FF) | 0x1F00); ++ } ++ } ++ } ++ } ++ if (phy->rev < 3 && phy->connected) ++ bcm43xx_phy_write(bcm, 0x047E, 0x0078); ++ if (phy->rev >= 6 && phy->rev <= 8) { ++ bcm43xx_phy_write(bcm, 0x0801, bcm43xx_phy_read(bcm, 0x0801) | 0x0080); ++ bcm43xx_phy_write(bcm, 0x043E, bcm43xx_phy_read(bcm, 0x043E) | 0x0004); ++ } ++ if (phy->rev >= 2 && phy->connected) ++ bcm43xx_calc_loopback_gain(bcm); ++ if (radio->revision != 8) { ++ if (radio->initval == 0xFFFF) ++ radio->initval = bcm43xx_radio_init2050(bcm); ++ else ++ bcm43xx_radio_write16(bcm, 0x0078, radio->initval); ++ } ++ if (radio->txctl2 == 0xFFFF) { ++ bcm43xx_phy_lo_g_measure(bcm); ++ } else { ++ if (radio->version == 0x2050 && radio->revision == 8) { ++ //FIXME ++ } else { ++ bcm43xx_radio_write16(bcm, 0x0052, ++ (bcm43xx_radio_read16(bcm, 0x0052) ++ & 0xFFF0) | radio->txctl1); ++ } ++ if (phy->rev >= 6) { ++ /* ++ bcm43xx_phy_write(bcm, 0x0036, ++ (bcm43xx_phy_read(bcm, 0x0036) ++ & 0xF000) | (FIXME << 12)); ++ */ ++ } ++ if (bcm->sprom.boardflags & BCM43xx_BFL_PACTRL) ++ bcm43xx_phy_write(bcm, 0x002E, 0x8075); ++ else ++ bcm43xx_phy_write(bcm, 0x003E, 0x807F); ++ if (phy->rev < 2) ++ bcm43xx_phy_write(bcm, 0x002F, 0x0101); ++ else ++ bcm43xx_phy_write(bcm, 0x002F, 0x0202); ++ } ++ if (phy->connected) { ++ bcm43xx_phy_lo_adjust(bcm, 0); ++ bcm43xx_phy_write(bcm, 0x080F, 0x8078); ++ } ++ ++ if (!(bcm->sprom.boardflags & BCM43xx_BFL_RSSI)) { ++ /* The specs state to update the NRSSI LT with ++ * the value 0x7FFFFFFF here. I think that is some weird ++ * compiler optimization in the original driver. ++ * Essentially, what we do here is resetting all NRSSI LT ++ * entries to -32 (see the limit_value() in nrssi_hw_update()) ++ */ ++ bcm43xx_nrssi_hw_update(bcm, 0xFFFF); ++ bcm43xx_calc_nrssi_threshold(bcm); ++ } else if (phy->connected) { ++ if (radio->nrssi[0] == -1000) { ++ assert(radio->nrssi[1] == -1000); ++ bcm43xx_calc_nrssi_slope(bcm); ++ } else { ++ assert(radio->nrssi[1] != -1000); ++ bcm43xx_calc_nrssi_threshold(bcm); ++ } ++ } ++ if (radio->revision == 8) ++ bcm43xx_phy_write(bcm, 0x0805, 0x3230); ++ bcm43xx_phy_init_pctl(bcm); ++ if (bcm->chip_id == 0x4306 && bcm->chip_package != 2) { ++ bcm43xx_phy_write(bcm, 0x0429, ++ bcm43xx_phy_read(bcm, 0x0429) & 0xBFFF); ++ bcm43xx_phy_write(bcm, 0x04C3, ++ bcm43xx_phy_read(bcm, 0x04C3) & 0x7FFF); ++ } ++} ++ ++static u16 bcm43xx_phy_lo_b_r15_loop(struct bcm43xx_private *bcm) ++{ ++ int i; ++ u16 ret = 0; ++ ++ for (i = 0; i < 10; i++){ ++ bcm43xx_phy_write(bcm, 0x0015, 0xAFA0); ++ udelay(1); ++ bcm43xx_phy_write(bcm, 0x0015, 0xEFA0); ++ udelay(10); ++ bcm43xx_phy_write(bcm, 0x0015, 0xFFA0); ++ udelay(40); ++ ret += bcm43xx_phy_read(bcm, 0x002C); ++ } ++ ++ return ret; ++} ++ ++void bcm43xx_phy_lo_b_measure(struct bcm43xx_private *bcm) ++{ ++ struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm); ++ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); ++ u16 regstack[12] = { 0 }; ++ u16 mls; ++ u16 fval; ++ int i, j; ++ ++ regstack[0] = bcm43xx_phy_read(bcm, 0x0015); ++ regstack[1] = bcm43xx_radio_read16(bcm, 0x0052) & 0xFFF0; ++ ++ if (radio->version == 0x2053) { ++ regstack[2] = bcm43xx_phy_read(bcm, 0x000A); ++ regstack[3] = bcm43xx_phy_read(bcm, 0x002A); ++ regstack[4] = bcm43xx_phy_read(bcm, 0x0035); ++ regstack[5] = bcm43xx_phy_read(bcm, 0x0003); ++ regstack[6] = bcm43xx_phy_read(bcm, 0x0001); ++ regstack[7] = bcm43xx_phy_read(bcm, 0x0030); ++ ++ regstack[8] = bcm43xx_radio_read16(bcm, 0x0043); ++ regstack[9] = bcm43xx_radio_read16(bcm, 0x007A); ++ regstack[10] = bcm43xx_read16(bcm, 0x03EC); ++ regstack[11] = bcm43xx_radio_read16(bcm, 0x0052) & 0x00F0; ++ ++ bcm43xx_phy_write(bcm, 0x0030, 0x00FF); ++ bcm43xx_write16(bcm, 0x03EC, 0x3F3F); ++ bcm43xx_phy_write(bcm, 0x0035, regstack[4] & 0xFF7F); ++ bcm43xx_radio_write16(bcm, 0x007A, regstack[9] & 0xFFF0); ++ } ++ bcm43xx_phy_write(bcm, 0x0015, 0xB000); ++ bcm43xx_phy_write(bcm, 0x002B, 0x0004); ++ ++ if (radio->version == 0x2053) { ++ bcm43xx_phy_write(bcm, 0x002B, 0x0203); ++ bcm43xx_phy_write(bcm, 0x002A, 0x08A3); ++ } ++ ++ phy->minlowsig[0] = 0xFFFF; ++ ++ for (i = 0; i < 4; i++) { ++ bcm43xx_radio_write16(bcm, 0x0052, regstack[1] | i); ++ bcm43xx_phy_lo_b_r15_loop(bcm); ++ } ++ for (i = 0; i < 10; i++) { ++ bcm43xx_radio_write16(bcm, 0x0052, regstack[1] | i); ++ mls = bcm43xx_phy_lo_b_r15_loop(bcm) / 10; ++ if (mls < phy->minlowsig[0]) { ++ phy->minlowsig[0] = mls; ++ phy->minlowsigpos[0] = i; ++ } ++ } ++ bcm43xx_radio_write16(bcm, 0x0052, regstack[1] | phy->minlowsigpos[0]); ++ ++ phy->minlowsig[1] = 0xFFFF; ++ ++ for (i = -4; i < 5; i += 2) { ++ for (j = -4; j < 5; j += 2) { ++ if (j < 0) ++ fval = (0x0100 * i) + j + 0x0100; ++ else ++ fval = (0x0100 * i) + j; ++ bcm43xx_phy_write(bcm, 0x002F, fval); ++ mls = bcm43xx_phy_lo_b_r15_loop(bcm) / 10; ++ if (mls < phy->minlowsig[1]) { ++ phy->minlowsig[1] = mls; ++ phy->minlowsigpos[1] = fval; ++ } ++ } ++ } ++ phy->minlowsigpos[1] += 0x0101; ++ ++ bcm43xx_phy_write(bcm, 0x002F, phy->minlowsigpos[1]); ++ if (radio->version == 0x2053) { ++ bcm43xx_phy_write(bcm, 0x000A, regstack[2]); ++ bcm43xx_phy_write(bcm, 0x002A, regstack[3]); ++ bcm43xx_phy_write(bcm, 0x0035, regstack[4]); ++ bcm43xx_phy_write(bcm, 0x0003, regstack[5]); ++ bcm43xx_phy_write(bcm, 0x0001, regstack[6]); ++ bcm43xx_phy_write(bcm, 0x0030, regstack[7]); ++ ++ bcm43xx_radio_write16(bcm, 0x0043, regstack[8]); ++ bcm43xx_radio_write16(bcm, 0x007A, regstack[9]); ++ ++ bcm43xx_radio_write16(bcm, 0x0052, ++ (bcm43xx_radio_read16(bcm, 0x0052) & 0x000F) ++ | regstack[11]); ++ ++ bcm43xx_write16(bcm, 0x03EC, regstack[10]); ++ } ++ bcm43xx_phy_write(bcm, 0x0015, regstack[0]); ++} ++ ++static inline ++u16 bcm43xx_phy_lo_g_deviation_subval(struct bcm43xx_private *bcm, u16 control) ++{ ++ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); ++ ++ if (phy->connected) { ++ bcm43xx_phy_write(bcm, 0x15, 0xE300); ++ control <<= 8; ++ bcm43xx_phy_write(bcm, 0x0812, control | 0x00B0); ++ udelay(5); ++ bcm43xx_phy_write(bcm, 0x0812, control | 0x00B2); ++ udelay(2); ++ bcm43xx_phy_write(bcm, 0x0812, control | 0x00B3); ++ udelay(4); ++ bcm43xx_phy_write(bcm, 0x0015, 0xF300); ++ udelay(8); ++ } else { ++ bcm43xx_phy_write(bcm, 0x0015, control | 0xEFA0); ++ udelay(2); ++ bcm43xx_phy_write(bcm, 0x0015, control | 0xEFE0); ++ udelay(4); ++ bcm43xx_phy_write(bcm, 0x0015, control | 0xFFE0); ++ udelay(8); ++ } ++ ++ return bcm43xx_phy_read(bcm, 0x002D); ++} ++ ++static u32 bcm43xx_phy_lo_g_singledeviation(struct bcm43xx_private *bcm, u16 control) ++{ ++ int i; ++ u32 ret = 0; ++ ++ for (i = 0; i < 8; i++) ++ ret += bcm43xx_phy_lo_g_deviation_subval(bcm, control); ++ ++ return ret; ++} ++ ++/* Write the LocalOscillator CONTROL */ ++static inline ++void bcm43xx_lo_write(struct bcm43xx_private *bcm, ++ struct bcm43xx_lopair *pair) ++{ ++ u16 value; ++ ++ value = (u8)(pair->low); ++ value |= ((u8)(pair->high)) << 8; ++ ++#ifdef CONFIG_BCM43XX_D80211_DEBUG ++ /* Sanity check. */ ++ if (pair->low < -8 || pair->low > 8 || ++ pair->high < -8 || pair->high > 8) { ++ printk(KERN_WARNING PFX ++ "WARNING: Writing invalid LOpair " ++ "(low: %d, high: %d, index: %lu)\n", ++ pair->low, pair->high, ++ (unsigned long)(pair - bcm43xx_current_phy(bcm)->_lo_pairs)); ++ dump_stack(); ++ } ++#endif ++ ++ bcm43xx_phy_write(bcm, BCM43xx_PHY_G_LO_CONTROL, value); ++} ++ ++static inline ++struct bcm43xx_lopair * bcm43xx_find_lopair(struct bcm43xx_private *bcm, ++ u16 baseband_attenuation, ++ u16 radio_attenuation, ++ u16 tx) ++{ ++ static const u8 dict[10] = { 11, 10, 11, 12, 13, 12, 13, 12, 13, 12 }; ++ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); ++ ++ if (baseband_attenuation > 6) ++ baseband_attenuation = 6; ++ assert(radio_attenuation < 10); ++ ++ if (tx == 3) { ++ return bcm43xx_get_lopair(phy, ++ radio_attenuation, ++ baseband_attenuation); ++ } ++ return bcm43xx_get_lopair(phy, dict[radio_attenuation], baseband_attenuation); ++} ++ ++static inline ++struct bcm43xx_lopair * bcm43xx_current_lopair(struct bcm43xx_private *bcm) ++{ ++ struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm); ++ ++ return bcm43xx_find_lopair(bcm, ++ radio->baseband_atten, ++ radio->radio_atten, ++ radio->txctl1); ++} ++ ++/* Adjust B/G LO */ ++void bcm43xx_phy_lo_adjust(struct bcm43xx_private *bcm, int fixed) ++{ ++ struct bcm43xx_lopair *pair; ++ ++ if (fixed) { ++ /* Use fixed values. Only for initialization. */ ++ pair = bcm43xx_find_lopair(bcm, 2, 3, 0); ++ } else ++ pair = bcm43xx_current_lopair(bcm); ++ bcm43xx_lo_write(bcm, pair); ++} ++ ++static void bcm43xx_phy_lo_g_measure_txctl2(struct bcm43xx_private *bcm) ++{ ++ struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm); ++ u16 txctl2 = 0, i; ++ u32 smallest, tmp; ++ ++ bcm43xx_radio_write16(bcm, 0x0052, 0x0000); ++ udelay(10); ++ smallest = bcm43xx_phy_lo_g_singledeviation(bcm, 0); ++ for (i = 0; i < 16; i++) { ++ bcm43xx_radio_write16(bcm, 0x0052, i); ++ udelay(10); ++ tmp = bcm43xx_phy_lo_g_singledeviation(bcm, 0); ++ if (tmp < smallest) { ++ smallest = tmp; ++ txctl2 = i; ++ } ++ } ++ radio->txctl2 = txctl2; ++} ++ ++static ++void bcm43xx_phy_lo_g_state(struct bcm43xx_private *bcm, ++ const struct bcm43xx_lopair *in_pair, ++ struct bcm43xx_lopair *out_pair, ++ u16 r27) ++{ ++ static const struct bcm43xx_lopair transitions[8] = { ++ { .high = 1, .low = 1, }, ++ { .high = 1, .low = 0, }, ++ { .high = 1, .low = -1, }, ++ { .high = 0, .low = -1, }, ++ { .high = -1, .low = -1, }, ++ { .high = -1, .low = 0, }, ++ { .high = -1, .low = 1, }, ++ { .high = 0, .low = 1, }, ++ }; ++ struct bcm43xx_lopair lowest_transition = { ++ .high = in_pair->high, ++ .low = in_pair->low, ++ }; ++ struct bcm43xx_lopair tmp_pair; ++ struct bcm43xx_lopair transition; ++ int i = 12; ++ int state = 0; ++ int found_lower; ++ int j, begin, end; ++ u32 lowest_deviation; ++ u32 tmp; ++ ++ /* Note that in_pair and out_pair can point to the same pair. Be careful. */ ++ ++ bcm43xx_lo_write(bcm, &lowest_transition); ++ lowest_deviation = bcm43xx_phy_lo_g_singledeviation(bcm, r27); ++ do { ++ found_lower = 0; ++ assert(state >= 0 && state <= 8); ++ if (state == 0) { ++ begin = 1; ++ end = 8; ++ } else if (state % 2 == 0) { ++ begin = state - 1; ++ end = state + 1; ++ } else { ++ begin = state - 2; ++ end = state + 2; ++ } ++ if (begin < 1) ++ begin += 8; ++ if (end > 8) ++ end -= 8; ++ ++ j = begin; ++ tmp_pair.high = lowest_transition.high; ++ tmp_pair.low = lowest_transition.low; ++ while (1) { ++ assert(j >= 1 && j <= 8); ++ transition.high = tmp_pair.high + transitions[j - 1].high; ++ transition.low = tmp_pair.low + transitions[j - 1].low; ++ if ((abs(transition.low) < 9) && (abs(transition.high) < 9)) { ++ bcm43xx_lo_write(bcm, &transition); ++ tmp = bcm43xx_phy_lo_g_singledeviation(bcm, r27); ++ if (tmp < lowest_deviation) { ++ lowest_deviation = tmp; ++ state = j; ++ found_lower = 1; ++ ++ lowest_transition.high = transition.high; ++ lowest_transition.low = transition.low; ++ } ++ } ++ if (j == end) ++ break; ++ if (j == 8) ++ j = 1; ++ else ++ j++; ++ } ++ } while (i-- && found_lower); ++ ++ out_pair->high = lowest_transition.high; ++ out_pair->low = lowest_transition.low; ++} ++ ++/* Set the baseband attenuation value on chip. */ ++void bcm43xx_phy_set_baseband_attenuation(struct bcm43xx_private *bcm, ++ u16 baseband_attenuation) ++{ ++ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); ++ u16 value; ++ ++ if (phy->version == 0) { ++ value = (bcm43xx_read16(bcm, 0x03E6) & 0xFFF0); ++ value |= (baseband_attenuation & 0x000F); ++ bcm43xx_write16(bcm, 0x03E6, value); ++ return; ++ } ++ ++ if (phy->version > 1) { ++ value = bcm43xx_phy_read(bcm, 0x0060) & ~0x003C; ++ value |= (baseband_attenuation << 2) & 0x003C; ++ } else { ++ value = bcm43xx_phy_read(bcm, 0x0060) & ~0x0078; ++ value |= (baseband_attenuation << 3) & 0x0078; ++ } ++ bcm43xx_phy_write(bcm, 0x0060, value); ++} ++ ++/* http://bcm-specs.sipsolutions.net/LocalOscillator/Measure */ ++void bcm43xx_phy_lo_g_measure(struct bcm43xx_private *bcm) ++{ ++ static const u8 pairorder[10] = { 3, 1, 5, 7, 9, 2, 0, 4, 6, 8 }; ++ const int is_initializing = bcm43xx_is_initializing(bcm); ++ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); ++ struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm); ++ u16 h, i, oldi = 0, j; ++ struct bcm43xx_lopair control; ++ struct bcm43xx_lopair *tmp_control; ++ u16 tmp; ++ u16 regstack[16] = { 0 }; ++ u8 oldchannel; ++ ++ //XXX: What are these? ++ u8 r27 = 0, r31; ++ ++ oldchannel = radio->channel; ++ /* Setup */ ++ if (phy->connected) { ++ regstack[0] = bcm43xx_phy_read(bcm, BCM43xx_PHY_G_CRS); ++ regstack[1] = bcm43xx_phy_read(bcm, 0x0802); ++ bcm43xx_phy_write(bcm, BCM43xx_PHY_G_CRS, regstack[0] & 0x7FFF); ++ bcm43xx_phy_write(bcm, 0x0802, regstack[1] & 0xFFFC); ++ } ++ regstack[3] = bcm43xx_read16(bcm, 0x03E2); ++ bcm43xx_write16(bcm, 0x03E2, regstack[3] | 0x8000); ++ regstack[4] = bcm43xx_read16(bcm, BCM43xx_MMIO_CHANNEL_EXT); ++ regstack[5] = bcm43xx_phy_read(bcm, 0x15); ++ regstack[6] = bcm43xx_phy_read(bcm, 0x2A); ++ regstack[7] = bcm43xx_phy_read(bcm, 0x35); ++ regstack[8] = bcm43xx_phy_read(bcm, 0x60); ++ regstack[9] = bcm43xx_radio_read16(bcm, 0x43); ++ regstack[10] = bcm43xx_radio_read16(bcm, 0x7A); ++ regstack[11] = bcm43xx_radio_read16(bcm, 0x52); ++ if (phy->connected) { ++ regstack[12] = bcm43xx_phy_read(bcm, 0x0811); ++ regstack[13] = bcm43xx_phy_read(bcm, 0x0812); ++ regstack[14] = bcm43xx_phy_read(bcm, 0x0814); ++ regstack[15] = bcm43xx_phy_read(bcm, 0x0815); ++ } ++ bcm43xx_radio_selectchannel(bcm, 6, 0); ++ if (phy->connected) { ++ bcm43xx_phy_write(bcm, BCM43xx_PHY_G_CRS, regstack[0] & 0x7FFF); ++ bcm43xx_phy_write(bcm, 0x0802, regstack[1] & 0xFFFC); ++ bcm43xx_dummy_transmission(bcm); ++ } ++ bcm43xx_radio_write16(bcm, 0x0043, 0x0006); ++ ++ bcm43xx_phy_set_baseband_attenuation(bcm, 2); ++ ++ bcm43xx_write16(bcm, BCM43xx_MMIO_CHANNEL_EXT, 0x0000); ++ bcm43xx_phy_write(bcm, 0x002E, 0x007F); ++ bcm43xx_phy_write(bcm, 0x080F, 0x0078); ++ bcm43xx_phy_write(bcm, 0x0035, regstack[7] & ~(1 << 7)); ++ bcm43xx_radio_write16(bcm, 0x007A, regstack[10] & 0xFFF0); ++ bcm43xx_phy_write(bcm, 0x002B, 0x0203); ++ bcm43xx_phy_write(bcm, 0x002A, 0x08A3); ++ if (phy->connected) { ++ bcm43xx_phy_write(bcm, 0x0814, regstack[14] | 0x0003); ++ bcm43xx_phy_write(bcm, 0x0815, regstack[15] & 0xFFFC); ++ bcm43xx_phy_write(bcm, 0x0811, 0x01B3); ++ bcm43xx_phy_write(bcm, 0x0812, 0x00B2); ++ } ++ if (is_initializing) ++ bcm43xx_phy_lo_g_measure_txctl2(bcm); ++ bcm43xx_phy_write(bcm, 0x080F, 0x8078); ++ ++ /* Measure */ ++ control.low = 0; ++ control.high = 0; ++ for (h = 0; h < 10; h++) { ++ /* Loop over each possible RadioAttenuation (0-9) */ ++ i = pairorder[h]; ++ if (is_initializing) { ++ if (i == 3) { ++ control.low = 0; ++ control.high = 0; ++ } else if (((i % 2 == 1) && (oldi % 2 == 1)) || ++ ((i % 2 == 0) && (oldi % 2 == 0))) { ++ tmp_control = bcm43xx_get_lopair(phy, oldi, 0); ++ memcpy(&control, tmp_control, sizeof(control)); ++ } else { ++ tmp_control = bcm43xx_get_lopair(phy, 3, 0); ++ memcpy(&control, tmp_control, sizeof(control)); ++ } ++ } ++ /* Loop over each possible BasebandAttenuation/2 */ ++ for (j = 0; j < 4; j++) { ++ if (is_initializing) { ++ tmp = i * 2 + j; ++ r27 = 0; ++ r31 = 0; ++ if (tmp > 14) { ++ r31 = 1; ++ if (tmp > 17) ++ r27 = 1; ++ if (tmp > 19) ++ r27 = 2; ++ } ++ } else { ++ tmp_control = bcm43xx_get_lopair(phy, i, j * 2); ++ if (!tmp_control->used) ++ continue; ++ memcpy(&control, tmp_control, sizeof(control)); ++ r27 = 3; ++ r31 = 0; ++ } ++ bcm43xx_radio_write16(bcm, 0x43, i); ++ bcm43xx_radio_write16(bcm, 0x52, radio->txctl2); ++ udelay(10); ++ ++ bcm43xx_phy_set_baseband_attenuation(bcm, j * 2); ++ ++ tmp = (regstack[10] & 0xFFF0); ++ if (r31) ++ tmp |= 0x0008; ++ bcm43xx_radio_write16(bcm, 0x007A, tmp); ++ ++ tmp_control = bcm43xx_get_lopair(phy, i, j * 2); ++ bcm43xx_phy_lo_g_state(bcm, &control, tmp_control, r27); ++ } ++ oldi = i; ++ } ++ /* Loop over each possible RadioAttenuation (10-13) */ ++ for (i = 10; i < 14; i++) { ++ /* Loop over each possible BasebandAttenuation/2 */ ++ for (j = 0; j < 4; j++) { ++ if (is_initializing) { ++ tmp_control = bcm43xx_get_lopair(phy, i - 9, j * 2); ++ memcpy(&control, tmp_control, sizeof(control)); ++ tmp = (i - 9) * 2 + j - 5;//FIXME: This is wrong, as the following if statement can never trigger. ++ r27 = 0; ++ r31 = 0; ++ if (tmp > 14) { ++ r31 = 1; ++ if (tmp > 17) ++ r27 = 1; ++ if (tmp > 19) ++ r27 = 2; ++ } ++ } else { ++ tmp_control = bcm43xx_get_lopair(phy, i - 9, j * 2); ++ if (!tmp_control->used) ++ continue; ++ memcpy(&control, tmp_control, sizeof(control)); ++ r27 = 3; ++ r31 = 0; ++ } ++ bcm43xx_radio_write16(bcm, 0x43, i - 9); ++ bcm43xx_radio_write16(bcm, 0x52, ++ radio->txctl2 ++ | (3/*txctl1*/ << 4));//FIXME: shouldn't txctl1 be zero here and 3 in the loop above? ++ udelay(10); ++ ++ bcm43xx_phy_set_baseband_attenuation(bcm, j * 2); ++ ++ tmp = (regstack[10] & 0xFFF0); ++ if (r31) ++ tmp |= 0x0008; ++ bcm43xx_radio_write16(bcm, 0x7A, tmp); ++ ++ tmp_control = bcm43xx_get_lopair(phy, i, j * 2); ++ bcm43xx_phy_lo_g_state(bcm, &control, tmp_control, r27); ++ } ++ } ++ ++ /* Restoration */ ++ if (phy->connected) { ++ bcm43xx_phy_write(bcm, 0x0015, 0xE300); ++ bcm43xx_phy_write(bcm, 0x0812, (r27 << 8) | 0xA0); ++ udelay(5); ++ bcm43xx_phy_write(bcm, 0x0812, (r27 << 8) | 0xA2); ++ udelay(2); ++ bcm43xx_phy_write(bcm, 0x0812, (r27 << 8) | 0xA3); ++ } else ++ bcm43xx_phy_write(bcm, 0x0015, r27 | 0xEFA0); ++ bcm43xx_phy_lo_adjust(bcm, is_initializing); ++ bcm43xx_phy_write(bcm, 0x002E, 0x807F); ++ if (phy->connected) ++ bcm43xx_phy_write(bcm, 0x002F, 0x0202); ++ else ++ bcm43xx_phy_write(bcm, 0x002F, 0x0101); ++ bcm43xx_write16(bcm, BCM43xx_MMIO_CHANNEL_EXT, regstack[4]); ++ bcm43xx_phy_write(bcm, 0x0015, regstack[5]); ++ bcm43xx_phy_write(bcm, 0x002A, regstack[6]); ++ bcm43xx_phy_write(bcm, 0x0035, regstack[7]); ++ bcm43xx_phy_write(bcm, 0x0060, regstack[8]); ++ bcm43xx_radio_write16(bcm, 0x0043, regstack[9]); ++ bcm43xx_radio_write16(bcm, 0x007A, regstack[10]); ++ regstack[11] &= 0x00F0; ++ regstack[11] |= (bcm43xx_radio_read16(bcm, 0x52) & 0x000F); ++ bcm43xx_radio_write16(bcm, 0x52, regstack[11]); ++ bcm43xx_write16(bcm, 0x03E2, regstack[3]); ++ if (phy->connected) { ++ bcm43xx_phy_write(bcm, 0x0811, regstack[12]); ++ bcm43xx_phy_write(bcm, 0x0812, regstack[13]); ++ bcm43xx_phy_write(bcm, 0x0814, regstack[14]); ++ bcm43xx_phy_write(bcm, 0x0815, regstack[15]); ++ bcm43xx_phy_write(bcm, BCM43xx_PHY_G_CRS, regstack[0]); ++ bcm43xx_phy_write(bcm, 0x0802, regstack[1]); ++ } ++ bcm43xx_radio_selectchannel(bcm, oldchannel, 1); ++ ++#ifdef CONFIG_BCM43XX_D80211_DEBUG ++ { ++ /* Sanity check for all lopairs. */ ++ for (i = 0; i < BCM43xx_LO_COUNT; i++) { ++ tmp_control = phy->_lo_pairs + i; ++ if (tmp_control->low < -8 || tmp_control->low > 8 || ++ tmp_control->high < -8 || tmp_control->high > 8) { ++ printk(KERN_WARNING PFX ++ "WARNING: Invalid LOpair (low: %d, high: %d, index: %d)\n", ++ tmp_control->low, tmp_control->high, i); ++ } ++ } ++ } ++#endif /* CONFIG_BCM43XX_D80211_DEBUG */ ++} ++ ++static ++void bcm43xx_phy_lo_mark_current_used(struct bcm43xx_private *bcm) ++{ ++ struct bcm43xx_lopair *pair; ++ ++ pair = bcm43xx_current_lopair(bcm); ++ pair->used = 1; ++} ++ ++void bcm43xx_phy_lo_mark_all_unused(struct bcm43xx_private *bcm) ++{ ++ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); ++ struct bcm43xx_lopair *pair; ++ int i; ++ ++ for (i = 0; i < BCM43xx_LO_COUNT; i++) { ++ pair = phy->_lo_pairs + i; ++ pair->used = 0; ++ } ++} ++ ++/* http://bcm-specs.sipsolutions.net/EstimatePowerOut ++ * This function converts a TSSI value to dBm in Q5.2 ++ */ ++static s8 bcm43xx_phy_estimate_power_out(struct bcm43xx_private *bcm, s8 tssi) ++{ ++ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); ++ s8 dbm = 0; ++ s32 tmp; ++ ++ tmp = phy->idle_tssi; ++ tmp += tssi; ++ tmp -= phy->savedpctlreg; ++ ++ switch (phy->type) { ++ case BCM43xx_PHYTYPE_A: ++ tmp += 0x80; ++ tmp = limit_value(tmp, 0x00, 0xFF); ++ dbm = phy->tssi2dbm[tmp]; ++ TODO(); //TODO: There's a FIXME on the specs ++ break; ++ case BCM43xx_PHYTYPE_B: ++ case BCM43xx_PHYTYPE_G: ++ tmp = limit_value(tmp, 0x00, 0x3F); ++ dbm = phy->tssi2dbm[tmp]; ++ break; ++ default: ++ assert(0); ++ } ++ ++ return dbm; ++} ++ ++/* http://bcm-specs.sipsolutions.net/RecalculateTransmissionPower */ ++void bcm43xx_phy_xmitpower(struct bcm43xx_private *bcm) ++{ ++ struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm); ++ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); ++ ++ if (phy->savedpctlreg == 0xFFFF) ++ return; ++ if ((bcm->board_type == 0x0416) && ++ (bcm->board_vendor == PCI_VENDOR_ID_BROADCOM)) ++ return; ++ ++ switch (phy->type) { ++ case BCM43xx_PHYTYPE_A: { ++ ++ TODO(); //TODO: Nothing for A PHYs yet :-/ ++ ++ break; ++ } ++ case BCM43xx_PHYTYPE_B: ++ case BCM43xx_PHYTYPE_G: { ++ u16 tmp; ++ u16 txpower; ++ s8 v0, v1, v2, v3; ++ s8 average; ++ u8 max_pwr; ++ s16 desired_pwr, estimated_pwr, pwr_adjust; ++ s16 radio_att_delta, baseband_att_delta; ++ s16 radio_attenuation, baseband_attenuation; ++ unsigned long phylock_flags; ++ ++ tmp = bcm43xx_shm_read16(bcm, BCM43xx_SHM_SHARED, 0x0058); ++ v0 = (s8)(tmp & 0x00FF); ++ v1 = (s8)((tmp & 0xFF00) >> 8); ++ tmp = bcm43xx_shm_read16(bcm, BCM43xx_SHM_SHARED, 0x005A); ++ v2 = (s8)(tmp & 0x00FF); ++ v3 = (s8)((tmp & 0xFF00) >> 8); ++ tmp = 0; ++ ++ if (v0 == 0x7F || v1 == 0x7F || v2 == 0x7F || v3 == 0x7F) { ++ tmp = bcm43xx_shm_read16(bcm, BCM43xx_SHM_SHARED, 0x0070); ++ v0 = (s8)(tmp & 0x00FF); ++ v1 = (s8)((tmp & 0xFF00) >> 8); ++ tmp = bcm43xx_shm_read16(bcm, BCM43xx_SHM_SHARED, 0x0072); ++ v2 = (s8)(tmp & 0x00FF); ++ v3 = (s8)((tmp & 0xFF00) >> 8); ++ if (v0 == 0x7F || v1 == 0x7F || v2 == 0x7F || v3 == 0x7F) ++ return; ++ v0 = (v0 + 0x20) & 0x3F; ++ v1 = (v1 + 0x20) & 0x3F; ++ v2 = (v2 + 0x20) & 0x3F; ++ v3 = (v3 + 0x20) & 0x3F; ++ tmp = 1; ++ } ++ bcm43xx_radio_clear_tssi(bcm); ++ ++ average = (v0 + v1 + v2 + v3 + 2) / 4; ++ ++ if (tmp && (bcm43xx_shm_read16(bcm, BCM43xx_SHM_SHARED, 0x005E) & 0x8)) ++ average -= 13; ++ ++ estimated_pwr = bcm43xx_phy_estimate_power_out(bcm, average); ++ ++ max_pwr = bcm->sprom.maxpower_bgphy; ++ ++ if ((bcm->sprom.boardflags & BCM43xx_BFL_PACTRL) && ++ (phy->type == BCM43xx_PHYTYPE_G)) ++ max_pwr -= 0x3; ++ ++ /*TODO: ++ max_pwr = min(REG - bcm->sprom.antennagain_bgphy - 0x6, max_pwr) ++ where REG is the max power as per the regulatory domain ++ */ ++ ++ desired_pwr = radio->power_level; ++ /* Convert the desired_pwr to Q5.2 and limit it. */ ++ desired_pwr = limit_value((desired_pwr << 2), 0, max_pwr); ++ ++ pwr_adjust = desired_pwr - estimated_pwr; ++ radio_att_delta = -(pwr_adjust + 7) >> 3; ++ baseband_att_delta = -(pwr_adjust >> 1) - (4 * radio_att_delta); ++ if ((radio_att_delta == 0) && (baseband_att_delta == 0)) { ++ bcm43xx_phy_lo_mark_current_used(bcm); ++ return; ++ } ++ ++ /* Calculate the new attenuation values. */ ++ baseband_attenuation = radio->baseband_atten; ++ baseband_attenuation += baseband_att_delta; ++ radio_attenuation = radio->radio_atten; ++ radio_attenuation += radio_att_delta; ++ ++ /* Get baseband and radio attenuation values into their permitted ranges. ++ * baseband 0-11, radio 0-9. ++ * Radio attenuation affects power level 4 times as much as baseband. ++ */ ++ if (radio_attenuation < 0) { ++ baseband_attenuation -= (4 * -radio_attenuation); ++ radio_attenuation = 0; ++ } else if (radio_attenuation > 9) { ++ baseband_attenuation += (4 * (radio_attenuation - 9)); ++ radio_attenuation = 9; ++ } else { ++ while (baseband_attenuation < 0 && radio_attenuation > 0) { ++ baseband_attenuation += 4; ++ radio_attenuation--; ++ } ++ while (baseband_attenuation > 11 && radio_attenuation < 9) { ++ baseband_attenuation -= 4; ++ radio_attenuation++; ++ } ++ } ++ baseband_attenuation = limit_value(baseband_attenuation, 0, 11); ++ ++ txpower = radio->txctl1; ++ if ((radio->version == 0x2050) && (radio->revision == 2)) { ++ if (radio_attenuation <= 1) { ++ if (txpower == 0) { ++ txpower = 3; ++ radio_attenuation += 2; ++ baseband_attenuation += 2; ++ } else if (bcm->sprom.boardflags & BCM43xx_BFL_PACTRL) { ++ baseband_attenuation += 4 * (radio_attenuation - 2); ++ radio_attenuation = 2; ++ } ++ } else if (radio_attenuation > 4 && txpower != 0) { ++ txpower = 0; ++ if (baseband_attenuation < 3) { ++ radio_attenuation -= 3; ++ baseband_attenuation += 2; ++ } else { ++ radio_attenuation -= 2; ++ baseband_attenuation -= 2; ++ } ++ } ++ } ++ radio->txctl1 = txpower; ++ baseband_attenuation = limit_value(baseband_attenuation, 0, 11); ++ radio_attenuation = limit_value(radio_attenuation, 0, 9); ++ ++ bcm43xx_phy_lock(bcm, phylock_flags); ++ bcm43xx_radio_lock(bcm); ++ bcm43xx_radio_set_txpower_bg(bcm, baseband_attenuation, ++ radio_attenuation, txpower); ++ bcm43xx_phy_lo_mark_current_used(bcm); ++ bcm43xx_radio_unlock(bcm); ++ bcm43xx_phy_unlock(bcm, phylock_flags); ++ break; ++ } ++ default: ++ assert(0); ++ } ++} ++ ++static inline ++s32 bcm43xx_tssi2dbm_ad(s32 num, s32 den) ++{ ++ if (num < 0) ++ return num/den; ++ else ++ return (num+den/2)/den; ++} ++ ++static inline ++s8 bcm43xx_tssi2dbm_entry(s8 entry [], u8 index, s16 pab0, s16 pab1, s16 pab2) ++{ ++ s32 m1, m2, f = 256, q, delta; ++ s8 i = 0; ++ ++ m1 = bcm43xx_tssi2dbm_ad(16 * pab0 + index * pab1, 32); ++ m2 = max(bcm43xx_tssi2dbm_ad(32768 + index * pab2, 256), 1); ++ do { ++ if (i > 15) ++ return -EINVAL; ++ q = bcm43xx_tssi2dbm_ad(f * 4096 - ++ bcm43xx_tssi2dbm_ad(m2 * f, 16) * f, 2048); ++ delta = abs(q - f); ++ f = q; ++ i++; ++ } while (delta >= 2); ++ entry[index] = limit_value(bcm43xx_tssi2dbm_ad(m1 * f, 8192), -127, 128); ++ return 0; ++} ++ ++/* http://bcm-specs.sipsolutions.net/TSSI_to_DBM_Table */ ++int bcm43xx_phy_init_tssi2dbm_table(struct bcm43xx_private *bcm) ++{ ++ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); ++ struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm); ++ s16 pab0, pab1, pab2; ++ u8 idx; ++ s8 *dyn_tssi2dbm; ++ ++ if (phy->type == BCM43xx_PHYTYPE_A) { ++ pab0 = (s16)(bcm->sprom.pa1b0); ++ pab1 = (s16)(bcm->sprom.pa1b1); ++ pab2 = (s16)(bcm->sprom.pa1b2); ++ } else { ++ pab0 = (s16)(bcm->sprom.pa0b0); ++ pab1 = (s16)(bcm->sprom.pa0b1); ++ pab2 = (s16)(bcm->sprom.pa0b2); ++ } ++ ++ if ((bcm->chip_id == 0x4301) && (radio->version != 0x2050)) { ++ phy->idle_tssi = 0x34; ++ phy->tssi2dbm = bcm43xx_tssi2dbm_b_table; ++ return 0; ++ } ++ ++ if (pab0 != 0 && pab1 != 0 && pab2 != 0 && ++ pab0 != -1 && pab1 != -1 && pab2 != -1) { ++ /* The pabX values are set in SPROM. Use them. */ ++ if (phy->type == BCM43xx_PHYTYPE_A) { ++ if ((s8)bcm->sprom.idle_tssi_tgt_aphy != 0 && ++ (s8)bcm->sprom.idle_tssi_tgt_aphy != -1) ++ phy->idle_tssi = (s8)(bcm->sprom.idle_tssi_tgt_aphy); ++ else ++ phy->idle_tssi = 62; ++ } else { ++ if ((s8)bcm->sprom.idle_tssi_tgt_bgphy != 0 && ++ (s8)bcm->sprom.idle_tssi_tgt_bgphy != -1) ++ phy->idle_tssi = (s8)(bcm->sprom.idle_tssi_tgt_bgphy); ++ else ++ phy->idle_tssi = 62; ++ } ++ dyn_tssi2dbm = kmalloc(64, GFP_KERNEL); ++ if (dyn_tssi2dbm == NULL) { ++ printk(KERN_ERR PFX "Could not allocate memory" ++ "for tssi2dbm table\n"); ++ return -ENOMEM; ++ } ++ for (idx = 0; idx < 64; idx++) ++ if (bcm43xx_tssi2dbm_entry(dyn_tssi2dbm, idx, pab0, pab1, pab2)) { ++ phy->tssi2dbm = NULL; ++ printk(KERN_ERR PFX "Could not generate " ++ "tssi2dBm table\n"); ++ return -ENODEV; ++ } ++ phy->tssi2dbm = dyn_tssi2dbm; ++ phy->dyn_tssi_tbl = 1; ++ } else { ++ /* pabX values not set in SPROM. */ ++ switch (phy->type) { ++ case BCM43xx_PHYTYPE_A: ++ /* APHY needs a generated table. */ ++ phy->tssi2dbm = NULL; ++ printk(KERN_ERR PFX "Could not generate tssi2dBm " ++ "table (wrong SPROM info)!\n"); ++ return -ENODEV; ++ case BCM43xx_PHYTYPE_B: ++ phy->idle_tssi = 0x34; ++ phy->tssi2dbm = bcm43xx_tssi2dbm_b_table; ++ break; ++ case BCM43xx_PHYTYPE_G: ++ phy->idle_tssi = 0x34; ++ phy->tssi2dbm = bcm43xx_tssi2dbm_g_table; ++ break; ++ } ++ } ++ ++ return 0; ++} ++ ++int bcm43xx_phy_init(struct bcm43xx_private *bcm) ++{ ++ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); ++ int err = -ENODEV; ++ unsigned long flags; ++ ++ /* We do not want to be preempted while calibrating ++ * the hardware. ++ */ ++ local_irq_save(flags); ++ ++ switch (phy->type) { ++ case BCM43xx_PHYTYPE_A: ++ if (phy->rev == 2 || phy->rev == 3) { ++ bcm43xx_phy_inita(bcm); ++ err = 0; ++ } ++ break; ++ case BCM43xx_PHYTYPE_B: ++ switch (phy->rev) { ++ case 2: ++ bcm43xx_phy_initb2(bcm); ++ err = 0; ++ break; ++ case 4: ++ bcm43xx_phy_initb4(bcm); ++ err = 0; ++ break; ++ case 5: ++ bcm43xx_phy_initb5(bcm); ++ err = 0; ++ break; ++ case 6: ++ bcm43xx_phy_initb6(bcm); ++ err = 0; ++ break; ++ } ++ break; ++ case BCM43xx_PHYTYPE_G: ++ bcm43xx_phy_initg(bcm); ++ err = 0; ++ break; ++ } ++ local_irq_restore(flags); ++ if (err) ++ printk(KERN_WARNING PFX "Unknown PHYTYPE found!\n"); ++ ++ return err; ++} ++ ++void bcm43xx_phy_set_antenna_diversity(struct bcm43xx_private *bcm) ++{ ++ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); ++ u16 antennadiv; ++ u16 offset; ++ u16 value; ++ u32 ucodeflags; ++ ++ antennadiv = phy->antenna_diversity; ++ ++ if (antennadiv == 0xFFFF) ++ antennadiv = 3; ++ assert(antennadiv <= 3); ++ ++ ucodeflags = bcm43xx_shm_read32(bcm, BCM43xx_SHM_SHARED, ++ BCM43xx_UCODEFLAGS_OFFSET); ++ bcm43xx_shm_write32(bcm, BCM43xx_SHM_SHARED, ++ BCM43xx_UCODEFLAGS_OFFSET, ++ ucodeflags & ~BCM43xx_UCODEFLAG_AUTODIV); ++ ++ switch (phy->type) { ++ case BCM43xx_PHYTYPE_A: ++ case BCM43xx_PHYTYPE_G: ++ if (phy->type == BCM43xx_PHYTYPE_A) ++ offset = 0x0000; ++ else ++ offset = 0x0400; ++ ++ if (antennadiv == 2) ++ value = (3/*automatic*/ << 7); ++ else ++ value = (antennadiv << 7); ++ bcm43xx_phy_write(bcm, offset + 1, ++ (bcm43xx_phy_read(bcm, offset + 1) ++ & 0x7E7F) | value); ++ ++ if (antennadiv >= 2) { ++ if (antennadiv == 2) ++ value = (antennadiv << 7); ++ else ++ value = (0/*force0*/ << 7); ++ bcm43xx_phy_write(bcm, offset + 0x2B, ++ (bcm43xx_phy_read(bcm, offset + 0x2B) ++ & 0xFEFF) | value); ++ } ++ ++ if (phy->type == BCM43xx_PHYTYPE_G) { ++ if (antennadiv >= 2) ++ bcm43xx_phy_write(bcm, 0x048C, ++ bcm43xx_phy_read(bcm, 0x048C) ++ | 0x2000); ++ else ++ bcm43xx_phy_write(bcm, 0x048C, ++ bcm43xx_phy_read(bcm, 0x048C) ++ & ~0x2000); ++ if (phy->rev >= 2) { ++ bcm43xx_phy_write(bcm, 0x0461, ++ bcm43xx_phy_read(bcm, 0x0461) ++ | 0x0010); ++ bcm43xx_phy_write(bcm, 0x04AD, ++ (bcm43xx_phy_read(bcm, 0x04AD) ++ & 0x00FF) | 0x0015); ++ if (phy->rev == 2) ++ bcm43xx_phy_write(bcm, 0x0427, 0x0008); ++ else ++ bcm43xx_phy_write(bcm, 0x0427, ++ (bcm43xx_phy_read(bcm, 0x0427) ++ & 0x00FF) | 0x0008); ++ } ++ else if (phy->rev >= 6) ++ bcm43xx_phy_write(bcm, 0x049B, 0x00DC); ++ } else { ++ if (phy->rev < 3) ++ bcm43xx_phy_write(bcm, 0x002B, ++ (bcm43xx_phy_read(bcm, 0x002B) ++ & 0x00FF) | 0x0024); ++ else { ++ bcm43xx_phy_write(bcm, 0x0061, ++ bcm43xx_phy_read(bcm, 0x0061) ++ | 0x0010); ++ if (phy->rev == 3) { ++ bcm43xx_phy_write(bcm, 0x0093, 0x001D); ++ bcm43xx_phy_write(bcm, 0x0027, 0x0008); ++ } else { ++ bcm43xx_phy_write(bcm, 0x0093, 0x003A); ++ bcm43xx_phy_write(bcm, 0x0027, ++ (bcm43xx_phy_read(bcm, 0x0027) ++ & 0x00FF) | 0x0008); ++ } ++ } ++ } ++ break; ++ case BCM43xx_PHYTYPE_B: ++ if (bcm->current_core->rev == 2) ++ value = (3/*automatic*/ << 7); ++ else ++ value = (antennadiv << 7); ++ bcm43xx_phy_write(bcm, 0x03E2, ++ (bcm43xx_phy_read(bcm, 0x03E2) ++ & 0xFE7F) | value); ++ break; ++ default: ++ assert(0); ++ } ++ ++ if (antennadiv >= 2) { ++ ucodeflags = bcm43xx_shm_read32(bcm, BCM43xx_SHM_SHARED, ++ BCM43xx_UCODEFLAGS_OFFSET); ++ bcm43xx_shm_write32(bcm, BCM43xx_SHM_SHARED, ++ BCM43xx_UCODEFLAGS_OFFSET, ++ ucodeflags | BCM43xx_UCODEFLAG_AUTODIV); ++ } ++ ++ phy->antenna_diversity = antennadiv; ++} +diff -Nur linux-2.6.16/drivers/net/wireless/bcm43xx-d80211/bcm43xx_phy.h linux-2.6.16-bcm43xx/drivers/net/wireless/bcm43xx-d80211/bcm43xx_phy.h +--- linux-2.6.16/drivers/net/wireless/bcm43xx-d80211/bcm43xx_phy.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.16-bcm43xx/drivers/net/wireless/bcm43xx-d80211/bcm43xx_phy.h 2006-03-28 22:16:14.000000000 +0200 +@@ -0,0 +1,74 @@ ++/* ++ ++ Broadcom BCM43xx wireless driver ++ ++ Copyright (c) 2005 Martin Langer <martin-langer@gmx.de>, ++ Stefano Brivio <st3@riseup.net> ++ Michael Buesch <mbuesch@freenet.de> ++ Danny van Dyk <kugelfang@gentoo.org> ++ Andreas Jaggi <andreas.jaggi@waterwave.ch> ++ ++ Some parts of the code in this file are derived from the ipw2200 ++ driver Copyright(c) 2003 - 2004 Intel Corporation. ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; see the file COPYING. If not, write to ++ the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, ++ Boston, MA 02110-1301, USA. ++ ++*/ ++ ++#ifndef BCM43xx_PHY_H_ ++#define BCM43xx_PHY_H_ ++ ++#include <linux/types.h> ++ ++struct bcm43xx_private; ++ ++void bcm43xx_raw_phy_lock(struct bcm43xx_private *bcm); ++#define bcm43xx_phy_lock(bcm, flags) \ ++ do { \ ++ local_irq_save(flags); \ ++ bcm43xx_raw_phy_lock(bcm); \ ++ } while (0) ++void bcm43xx_raw_phy_unlock(struct bcm43xx_private *bcm); ++#define bcm43xx_phy_unlock(bcm, flags) \ ++ do { \ ++ bcm43xx_raw_phy_unlock(bcm); \ ++ local_irq_restore(flags); \ ++ } while (0) ++ ++u16 bcm43xx_phy_read(struct bcm43xx_private *bcm, u16 offset); ++void bcm43xx_phy_write(struct bcm43xx_private *bcm, u16 offset, u16 val); ++ ++int bcm43xx_phy_init_tssi2dbm_table(struct bcm43xx_private *bcm); ++int bcm43xx_phy_init(struct bcm43xx_private *bcm); ++ ++void bcm43xx_phy_set_antenna_diversity(struct bcm43xx_private *bcm); ++void bcm43xx_phy_calibrate(struct bcm43xx_private *bcm); ++int bcm43xx_phy_connect(struct bcm43xx_private *bcm, int connect); ++ ++void bcm43xx_phy_lo_b_measure(struct bcm43xx_private *bcm); ++void bcm43xx_phy_lo_g_measure(struct bcm43xx_private *bcm); ++void bcm43xx_phy_xmitpower(struct bcm43xx_private *bcm); ++ ++/* Adjust the LocalOscillator to the saved values. ++ * "fixed" is only set to 1 once in initialization. Set to 0 otherwise. ++ */ ++void bcm43xx_phy_lo_adjust(struct bcm43xx_private *bcm, int fixed); ++void bcm43xx_phy_lo_mark_all_unused(struct bcm43xx_private *bcm); ++ ++void bcm43xx_phy_set_baseband_attenuation(struct bcm43xx_private *bcm, ++ u16 baseband_attenuation); ++ ++#endif /* BCM43xx_PHY_H_ */ +diff -Nur linux-2.6.16/drivers/net/wireless/bcm43xx-d80211/bcm43xx_pio.c linux-2.6.16-bcm43xx/drivers/net/wireless/bcm43xx-d80211/bcm43xx_pio.c +--- linux-2.6.16/drivers/net/wireless/bcm43xx-d80211/bcm43xx_pio.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.16-bcm43xx/drivers/net/wireless/bcm43xx-d80211/bcm43xx_pio.c 2006-03-28 22:16:14.000000000 +0200 +@@ -0,0 +1,592 @@ ++/* ++ ++ Broadcom BCM43xx wireless driver ++ ++ PIO Transmission ++ ++ Copyright (c) 2005 Michael Buesch <mbuesch@freenet.de> ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; see the file COPYING. If not, write to ++ the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, ++ Boston, MA 02110-1301, USA. ++ ++*/ ++ ++#include "bcm43xx.h" ++#include "bcm43xx_pio.h" ++#include "bcm43xx_main.h" ++#include "bcm43xx_xmit.h" ++ ++#include <linux/delay.h> ++ ++ ++static void tx_start(struct bcm43xx_pioqueue *queue) ++{ ++ bcm43xx_pio_write(queue, BCM43xx_PIO_TXCTL, ++ BCM43xx_PIO_TXCTL_INIT); ++} ++ ++static void tx_octet(struct bcm43xx_pioqueue *queue, ++ u8 octet) ++{ ++ if (queue->need_workarounds) { ++ bcm43xx_pio_write(queue, BCM43xx_PIO_TXDATA, ++ octet); ++ bcm43xx_pio_write(queue, BCM43xx_PIO_TXCTL, ++ BCM43xx_PIO_TXCTL_WRITEHI); ++ } else { ++ bcm43xx_pio_write(queue, BCM43xx_PIO_TXCTL, ++ BCM43xx_PIO_TXCTL_WRITEHI); ++ bcm43xx_pio_write(queue, BCM43xx_PIO_TXDATA, ++ octet); ++ } ++} ++ ++static u16 tx_get_next_word(struct bcm43xx_txhdr *txhdr, ++ const u8 *packet, ++ unsigned int *pos) ++{ ++ const u8 *source; ++ unsigned int i = *pos; ++ u16 ret; ++ ++ if (i < sizeof(*txhdr)) { ++ source = (const u8 *)txhdr; ++ } else { ++ source = packet; ++ i -= sizeof(*txhdr); ++ } ++ ret = le16_to_cpu( *((u16 *)(source + i)) ); ++ *pos += 2; ++ ++ return ret; ++} ++ ++static void tx_data(struct bcm43xx_pioqueue *queue, ++ struct bcm43xx_txhdr *txhdr, ++ const u8 *packet, ++ unsigned int octets) ++{ ++ u16 data; ++ unsigned int i = 0; ++ ++ if (queue->need_workarounds) { ++ data = tx_get_next_word(txhdr, packet, &i); ++ bcm43xx_pio_write(queue, BCM43xx_PIO_TXDATA, data); ++ } ++ bcm43xx_pio_write(queue, BCM43xx_PIO_TXCTL, ++ BCM43xx_PIO_TXCTL_WRITELO | ++ BCM43xx_PIO_TXCTL_WRITEHI); ++ while (i < octets - 1) { ++ data = tx_get_next_word(txhdr, packet, &i); ++ bcm43xx_pio_write(queue, BCM43xx_PIO_TXDATA, data); ++ } ++ if (octets % 2) ++ tx_octet(queue, packet[octets - sizeof(*txhdr) - 1]); ++} ++ ++static void tx_complete(struct bcm43xx_pioqueue *queue, ++ struct sk_buff *skb) ++{ ++ if (queue->need_workarounds) { ++ bcm43xx_pio_write(queue, BCM43xx_PIO_TXDATA, ++ skb->data[skb->len - 1]); ++ bcm43xx_pio_write(queue, BCM43xx_PIO_TXCTL, ++ BCM43xx_PIO_TXCTL_WRITEHI | ++ BCM43xx_PIO_TXCTL_COMPLETE); ++ } else { ++ bcm43xx_pio_write(queue, BCM43xx_PIO_TXCTL, ++ BCM43xx_PIO_TXCTL_COMPLETE); ++ } ++} ++ ++static u16 generate_cookie(struct bcm43xx_pioqueue *queue, ++ int packetindex) ++{ ++ u16 cookie = 0x0000; ++ ++ /* We use the upper 4 bits for the PIO ++ * controller ID and the lower 12 bits ++ * for the packet index (in the cache). ++ */ ++ switch (queue->mmio_base) { ++ case BCM43xx_MMIO_PIO1_BASE: ++ break; ++ case BCM43xx_MMIO_PIO2_BASE: ++ cookie = 0x1000; ++ break; ++ case BCM43xx_MMIO_PIO3_BASE: ++ cookie = 0x2000; ++ break; ++ case BCM43xx_MMIO_PIO4_BASE: ++ cookie = 0x3000; ++ break; ++ default: ++ assert(0); ++ } ++ assert(((u16)packetindex & 0xF000) == 0x0000); ++ cookie |= (u16)packetindex; ++ ++ return cookie; ++} ++ ++static ++struct bcm43xx_pioqueue * parse_cookie(struct bcm43xx_private *bcm, ++ u16 cookie, ++ struct bcm43xx_pio_txpacket **packet) ++{ ++ struct bcm43xx_pio *pio = bcm43xx_current_pio(bcm); ++ struct bcm43xx_pioqueue *queue = NULL; ++ int packetindex; ++ ++ switch (cookie & 0xF000) { ++ case 0x0000: ++ queue = pio->queue0; ++ break; ++ case 0x1000: ++ queue = pio->queue1; ++ break; ++ case 0x2000: ++ queue = pio->queue2; ++ break; ++ case 0x3000: ++ queue = pio->queue3; ++ break; ++ default: ++ assert(0); ++ } ++ packetindex = (cookie & 0x0FFF); ++ assert(packetindex >= 0 && packetindex < BCM43xx_PIO_MAXTXPACKETS); ++ *packet = &(queue->tx_packets_cache[packetindex]); ++ ++ return queue; ++} ++ ++static void pio_tx_write_fragment(struct bcm43xx_pioqueue *queue, ++ struct sk_buff *skb, ++ struct bcm43xx_pio_txpacket *packet) ++{ ++ struct bcm43xx_txhdr txhdr; ++ unsigned int octets; ++ ++ assert(skb_shinfo(skb)->nr_frags == 0); ++ bcm43xx_generate_txhdr(queue->bcm, ++ &txhdr, skb->data, skb->len, ++ 1,//FIXME ++ generate_cookie(queue, pio_txpacket_getindex(packet)), ++ packet->ctl); ++ ++ tx_start(queue); ++ octets = skb->len + sizeof(txhdr); ++ if (queue->need_workarounds) ++ octets--; ++ tx_data(queue, &txhdr, (u8 *)skb->data, octets); ++ tx_complete(queue, skb); ++} ++ ++static void free_txpacket(struct bcm43xx_pio_txpacket *packet, ++ int irq_context) ++{ ++ struct bcm43xx_pioqueue *queue = packet->queue; ++ ++ if (irq_context) ++ dev_kfree_skb_irq(packet->skb); ++ else ++ dev_kfree_skb(packet->skb); ++ list_move(&packet->list, &queue->txfree); ++ queue->nr_txfree++; ++} ++ ++static int pio_tx_packet(struct bcm43xx_pio_txpacket *packet) ++{ ++ struct bcm43xx_pioqueue *queue = packet->queue; ++ struct sk_buff *skb = packet->skb; ++ u16 octets; ++ ++ octets = (u16)skb->len + sizeof(struct bcm43xx_txhdr); ++ if (queue->tx_devq_size < octets) { ++ dprintkl(KERN_WARNING PFX "PIO queue too small. " ++ "Dropping packet.\n"); ++ /* Drop it silently (return success) */ ++ free_txpacket(packet, 1); ++ return 0; ++ } ++ assert(queue->tx_devq_packets <= BCM43xx_PIO_MAXTXDEVQPACKETS); ++ assert(queue->tx_devq_used <= queue->tx_devq_size); ++ /* Check if there is sufficient free space on the device ++ * TX queue. If not, return and let the TX tasklet ++ * retry later. ++ */ ++ if (queue->tx_devq_packets == BCM43xx_PIO_MAXTXDEVQPACKETS) ++ return -EBUSY; ++ if (queue->tx_devq_used + octets > queue->tx_devq_size) ++ return -EBUSY; ++ /* Now poke the device. */ ++ pio_tx_write_fragment(queue, skb, packet); ++ ++ /* Account for the packet size. ++ * (We must not overflow the device TX queue) ++ */ ++ queue->tx_devq_packets++; ++ queue->tx_devq_used += octets; ++ ++ /* Transmission started, everything ok, move the ++ * packet to the txrunning list. ++ */ ++ list_move_tail(&packet->list, &queue->txrunning); ++ ++ return 0; ++} ++ ++static void tx_tasklet(unsigned long d) ++{ ++ struct bcm43xx_pioqueue *queue = (struct bcm43xx_pioqueue *)d; ++ struct bcm43xx_private *bcm = queue->bcm; ++ unsigned long flags; ++ struct bcm43xx_pio_txpacket *packet, *tmp_packet; ++ int err; ++ ++ bcm43xx_lock_mmio(bcm, flags); ++ list_for_each_entry_safe(packet, tmp_packet, &queue->txqueue, list) { ++ /* Try to transmit the packet. This can fail, if ++ * the device queue is full. In case of failure, the ++ * packet is left in the txqueue. ++ * If transmission succeed, the packet is moved to txrunning. ++ * If it is impossible to transmit the packet, it ++ * is dropped. ++ */ ++ err = pio_tx_packet(packet); ++ if (err) ++ break; ++ } ++ bcm43xx_unlock_mmio(bcm, flags); ++} ++ ++static void setup_txqueues(struct bcm43xx_pioqueue *queue) ++{ ++ struct bcm43xx_pio_txpacket *packet; ++ int i; ++ ++ queue->nr_txfree = BCM43xx_PIO_MAXTXPACKETS; ++ for (i = 0; i < BCM43xx_PIO_MAXTXPACKETS; i++) { ++ packet = &(queue->tx_packets_cache[i]); ++ ++ packet->queue = queue; ++ INIT_LIST_HEAD(&packet->list); ++ ++ list_add(&packet->list, &queue->txfree); ++ } ++} ++ ++static ++struct bcm43xx_pioqueue * bcm43xx_setup_pioqueue(struct bcm43xx_private *bcm, ++ u16 pio_mmio_base) ++{ ++ struct bcm43xx_pioqueue *queue; ++ u32 value; ++ u16 qsize; ++ ++ queue = kzalloc(sizeof(*queue), GFP_KERNEL); ++ if (!queue) ++ goto out; ++ ++ queue->bcm = bcm; ++ queue->mmio_base = pio_mmio_base; ++ queue->need_workarounds = (bcm->current_core->rev < 3); ++ ++ INIT_LIST_HEAD(&queue->txfree); ++ INIT_LIST_HEAD(&queue->txqueue); ++ INIT_LIST_HEAD(&queue->txrunning); ++ tasklet_init(&queue->txtask, tx_tasklet, ++ (unsigned long)queue); ++ ++ value = bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD); ++ value |= BCM43xx_SBF_XFER_REG_BYTESWAP; ++ bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS_BITFIELD, value); ++ ++ qsize = bcm43xx_read16(bcm, queue->mmio_base + BCM43xx_PIO_TXQBUFSIZE); ++ if (qsize <= BCM43xx_PIO_TXQADJUST) { ++ printk(KERN_ERR PFX "PIO tx device-queue too small (%u)\n", qsize); ++ goto err_freequeue; ++ } ++ qsize -= BCM43xx_PIO_TXQADJUST; ++ queue->tx_devq_size = qsize; ++ ++ setup_txqueues(queue); ++ ++out: ++ return queue; ++ ++err_freequeue: ++ kfree(queue); ++ queue = NULL; ++ goto out; ++} ++ ++static void cancel_transfers(struct bcm43xx_pioqueue *queue) ++{ ++ struct bcm43xx_pio_txpacket *packet, *tmp_packet; ++ ++ ieee80211_netif_oper(queue->bcm->net_dev, NETIF_DETACH); ++ assert(queue->bcm->shutting_down); ++ tasklet_disable(&queue->txtask); ++ ++ list_for_each_entry_safe(packet, tmp_packet, &queue->txrunning, list) ++ free_txpacket(packet, 0); ++ list_for_each_entry_safe(packet, tmp_packet, &queue->txqueue, list) ++ free_txpacket(packet, 0); ++} ++ ++static void bcm43xx_destroy_pioqueue(struct bcm43xx_pioqueue *queue) ++{ ++ if (!queue) ++ return; ++ ++ cancel_transfers(queue); ++ kfree(queue); ++} ++ ++void bcm43xx_pio_free(struct bcm43xx_private *bcm) ++{ ++ struct bcm43xx_pio *pio; ++ ++ if (!bcm43xx_using_pio(bcm)) ++ return; ++ pio = bcm43xx_current_pio(bcm); ++ ++ bcm43xx_destroy_pioqueue(pio->queue3); ++ pio->queue3 = NULL; ++ bcm43xx_destroy_pioqueue(pio->queue2); ++ pio->queue2 = NULL; ++ bcm43xx_destroy_pioqueue(pio->queue1); ++ pio->queue1 = NULL; ++ bcm43xx_destroy_pioqueue(pio->queue0); ++ pio->queue0 = NULL; ++} ++ ++int bcm43xx_pio_init(struct bcm43xx_private *bcm) ++{ ++ struct bcm43xx_pio *pio = bcm43xx_current_pio(bcm); ++ struct bcm43xx_pioqueue *queue; ++ int err = -ENOMEM; ++ ++ queue = bcm43xx_setup_pioqueue(bcm, BCM43xx_MMIO_PIO1_BASE); ++ if (!queue) ++ goto out; ++ pio->queue0 = queue; ++ ++ queue = bcm43xx_setup_pioqueue(bcm, BCM43xx_MMIO_PIO2_BASE); ++ if (!queue) ++ goto err_destroy0; ++ pio->queue1 = queue; ++ ++ queue = bcm43xx_setup_pioqueue(bcm, BCM43xx_MMIO_PIO3_BASE); ++ if (!queue) ++ goto err_destroy1; ++ pio->queue2 = queue; ++ ++ queue = bcm43xx_setup_pioqueue(bcm, BCM43xx_MMIO_PIO4_BASE); ++ if (!queue) ++ goto err_destroy2; ++ pio->queue3 = queue; ++ ++ if (bcm->current_core->rev < 3) ++ bcm->irq_savedstate |= BCM43xx_IRQ_PIO_WORKAROUND; ++ ++ dprintk(KERN_INFO PFX "PIO initialized\n"); ++ err = 0; ++out: ++ return err; ++ ++err_destroy2: ++ bcm43xx_destroy_pioqueue(pio->queue2); ++ pio->queue2 = NULL; ++err_destroy1: ++ bcm43xx_destroy_pioqueue(pio->queue1); ++ pio->queue1 = NULL; ++err_destroy0: ++ bcm43xx_destroy_pioqueue(pio->queue0); ++ pio->queue0 = NULL; ++ goto out; ++} ++ ++int bcm43xx_pio_tx(struct bcm43xx_private *bcm, ++ struct sk_buff *skb, ++ struct ieee80211_tx_control *ctl) ++{ ++ struct bcm43xx_pioqueue *queue = bcm43xx_current_pio(bcm)->queue1; ++ struct bcm43xx_pio_txpacket *packet; ++ u16 tmp; ++ ++ assert(!queue->tx_suspended); ++ assert(!list_empty(&queue->txfree)); ++ ++ tmp = bcm43xx_pio_read(queue, BCM43xx_PIO_TXCTL); ++ if (tmp & BCM43xx_PIO_TXCTL_SUSPEND) ++ return -EBUSY; ++ ++ packet = list_entry(queue->txfree.next, struct bcm43xx_pio_txpacket, list); ++ packet->skb = skb; ++ packet->ctl = ctl; ++ list_move_tail(&packet->list, &queue->txqueue); ++ queue->nr_txfree--; ++ assert(queue->nr_txfree < BCM43xx_PIO_MAXTXPACKETS); ++ ++ tasklet_schedule(&queue->txtask); ++ ++ return 0; ++} ++ ++void bcm43xx_pio_handle_xmitstatus(struct bcm43xx_private *bcm, ++ struct bcm43xx_xmitstatus *status) ++{ ++ struct bcm43xx_pioqueue *queue; ++ struct bcm43xx_pio_txpacket *packet; ++ ++ queue = parse_cookie(bcm, status->cookie, &packet); ++ assert(queue); ++//TODO ++if (!queue) ++return; ++ free_txpacket(packet, 1); ++ /* If there are packets on the txqueue, poke the tasklet. */ ++ if (!list_empty(&queue->txqueue)) ++ tasklet_schedule(&queue->txtask); ++} ++ ++void bcm43xx_pio_get_tx_stats(struct bcm43xx_private *bcm, ++ struct ieee80211_tx_queue_stats *stats) ++{ ++ struct bcm43xx_pio *pio = bcm43xx_current_pio(bcm); ++ struct bcm43xx_pioqueue *queue; ++ struct ieee80211_tx_queue_stats_data *data; ++ ++ queue = pio->queue1; ++ data = &(stats->data[0]); ++ data->len = BCM43xx_PIO_MAXTXPACKETS - queue->nr_txfree; ++ data->limit = BCM43xx_PIO_MAXTXPACKETS; ++ data->count = queue->nr_tx_packets; ++} ++ ++static void pio_rx_error(struct bcm43xx_pioqueue *queue, ++ int clear_buffers, ++ const char *error) ++{ ++ int i; ++ ++ printkl("PIO RX error: %s\n", error); ++ bcm43xx_pio_write(queue, BCM43xx_PIO_RXCTL, ++ BCM43xx_PIO_RXCTL_READY); ++ if (clear_buffers) { ++ assert(queue->mmio_base == BCM43xx_MMIO_PIO1_BASE); ++ for (i = 0; i < 15; i++) { ++ /* Dummy read. */ ++ bcm43xx_pio_read(queue, BCM43xx_PIO_RXDATA); ++ } ++ } ++} ++ ++void bcm43xx_pio_rx(struct bcm43xx_pioqueue *queue) ++{ ++ u16 preamble[21] = { 0 }; ++ struct bcm43xx_rxhdr *rxhdr; ++ u16 tmp, len, rxflags2; ++ int i, preamble_readwords; ++ struct sk_buff *skb; ++ ++return; ++ tmp = bcm43xx_pio_read(queue, BCM43xx_PIO_RXCTL); ++ if (!(tmp & BCM43xx_PIO_RXCTL_DATAAVAILABLE)) { ++ dprintkl(KERN_ERR PFX "PIO RX: No data available\n");//TODO: remove this printk. ++ return; ++ } ++ bcm43xx_pio_write(queue, BCM43xx_PIO_RXCTL, ++ BCM43xx_PIO_RXCTL_DATAAVAILABLE); ++ ++ for (i = 0; i < 10; i++) { ++ tmp = bcm43xx_pio_read(queue, BCM43xx_PIO_RXCTL); ++ if (tmp & BCM43xx_PIO_RXCTL_READY) ++ goto data_ready; ++ udelay(10); ++ } ++ dprintkl(KERN_ERR PFX "PIO RX timed out\n"); ++ return; ++data_ready: ++ ++//FIXME: endianess in this function. ++ len = le16_to_cpu(bcm43xx_pio_read(queue, BCM43xx_PIO_RXDATA)); ++ if (unlikely(len > 0x700)) { ++ pio_rx_error(queue, 0, "len > 0x700"); ++ return; ++ } ++ if (unlikely(len == 0 && queue->mmio_base != BCM43xx_MMIO_PIO4_BASE)) { ++ pio_rx_error(queue, 0, "len == 0"); ++ return; ++ } ++ preamble[0] = cpu_to_le16(len); ++ if (queue->mmio_base == BCM43xx_MMIO_PIO4_BASE) ++ preamble_readwords = 14 / sizeof(u16); ++ else ++ preamble_readwords = 18 / sizeof(u16); ++ for (i = 0; i < preamble_readwords; i++) { ++ tmp = bcm43xx_pio_read(queue, BCM43xx_PIO_RXDATA); ++ preamble[i + 1] = cpu_to_be16(tmp);//FIXME? ++ } ++ rxhdr = (struct bcm43xx_rxhdr *)preamble; ++ rxflags2 = le16_to_cpu(rxhdr->flags2); ++ if (unlikely(rxflags2 & BCM43xx_RXHDR_FLAGS2_INVALIDFRAME)) { ++ pio_rx_error(queue, ++ (queue->mmio_base == BCM43xx_MMIO_PIO1_BASE), ++ "invalid frame"); ++ return; ++ } ++ if (queue->mmio_base == BCM43xx_MMIO_PIO4_BASE) { ++ /* We received an xmit status. */ ++ struct bcm43xx_hwxmitstatus *hw; ++ struct bcm43xx_xmitstatus stat; ++ ++ hw = (struct bcm43xx_hwxmitstatus *)(preamble + 1); ++ stat.cookie = le16_to_cpu(hw->cookie); ++ stat.flags = hw->flags; ++ stat.cnt1 = hw->cnt1; ++ stat.cnt2 = hw->cnt2; ++ stat.seq = le16_to_cpu(hw->seq); ++ stat.unknown = le16_to_cpu(hw->unknown); ++ ++ bcm43xx_debugfs_log_txstat(queue->bcm, &stat); ++ bcm43xx_pio_handle_xmitstatus(queue->bcm, &stat); ++ ++ return; ++ } ++ ++ skb = dev_alloc_skb(len); ++ if (unlikely(!skb)) { ++ pio_rx_error(queue, 1, "OOM"); ++ return; ++ } ++ skb_put(skb, len); ++ for (i = 0; i < len - 1; i += 2) { ++ tmp = cpu_to_be16(bcm43xx_pio_read(queue, BCM43xx_PIO_RXDATA)); ++ *((u16 *)(skb->data + i)) = tmp; ++ } ++ if (len % 2) { ++ tmp = bcm43xx_pio_read(queue, BCM43xx_PIO_RXDATA); ++ skb->data[len - 1] = (tmp & 0x00FF); ++ if (rxflags2 & BCM43xx_RXHDR_FLAGS2_TYPE2FRAME) ++ skb->data[0x20] = (tmp & 0xFF00) >> 8; ++ else ++ skb->data[0x1E] = (tmp & 0xFF00) >> 8; ++ } ++ bcm43xx_rx(queue->bcm, skb, rxhdr); ++} +diff -Nur linux-2.6.16/drivers/net/wireless/bcm43xx-d80211/bcm43xx_pio.h linux-2.6.16-bcm43xx/drivers/net/wireless/bcm43xx-d80211/bcm43xx_pio.h +--- linux-2.6.16/drivers/net/wireless/bcm43xx-d80211/bcm43xx_pio.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.16-bcm43xx/drivers/net/wireless/bcm43xx-d80211/bcm43xx_pio.h 2006-03-28 22:16:14.000000000 +0200 +@@ -0,0 +1,146 @@ ++#ifndef BCM43xx_PIO_H_ ++#define BCM43xx_PIO_H_ ++ ++#include "bcm43xx.h" ++ ++#include <linux/interrupt.h> ++#include <linux/list.h> ++#include <linux/skbuff.h> ++ ++ ++#define BCM43xx_PIO_TXCTL 0x00 ++#define BCM43xx_PIO_TXDATA 0x02 ++#define BCM43xx_PIO_TXQBUFSIZE 0x04 ++#define BCM43xx_PIO_RXCTL 0x08 ++#define BCM43xx_PIO_RXDATA 0x0A ++ ++#define BCM43xx_PIO_TXCTL_WRITEHI (1 << 0) ++#define BCM43xx_PIO_TXCTL_WRITELO (1 << 1) ++#define BCM43xx_PIO_TXCTL_COMPLETE (1 << 2) ++#define BCM43xx_PIO_TXCTL_INIT (1 << 3) ++#define BCM43xx_PIO_TXCTL_SUSPEND (1 << 7) ++ ++#define BCM43xx_PIO_RXCTL_DATAAVAILABLE (1 << 0) ++#define BCM43xx_PIO_RXCTL_READY (1 << 1) ++ ++/* PIO constants */ ++#define BCM43xx_PIO_MAXTXDEVQPACKETS 31 ++#define BCM43xx_PIO_TXQADJUST 80 ++ ++/* PIO tuning knobs */ ++#define BCM43xx_PIO_MAXTXPACKETS 256 ++ ++ ++ ++#ifdef CONFIG_BCM43XX_D80211_PIO ++ ++ ++struct bcm43xx_pioqueue; ++struct bcm43xx_xmitstatus; ++ ++struct bcm43xx_pio_txpacket { ++ struct bcm43xx_pioqueue *queue; ++ struct sk_buff *skb; ++ struct ieee80211_tx_control *ctl; ++ struct list_head list; ++}; ++ ++#define pio_txpacket_getindex(packet) ((int)((packet) - (packet)->queue->tx_packets_cache)) ++ ++struct bcm43xx_pioqueue { ++ struct bcm43xx_private *bcm; ++ u16 mmio_base; ++ ++ u8 tx_suspended:1, ++ need_workarounds:1; /* Workarounds needed for core.rev < 3 */ ++ ++ /* Adjusted size of the device internal TX buffer. */ ++ u16 tx_devq_size; ++ /* Used octets of the device internal TX buffer. */ ++ u16 tx_devq_used; ++ /* Used packet slots in the device internal TX buffer. */ ++ u8 tx_devq_packets; ++ /* Packets from the txfree list can ++ * be taken on incoming TX requests. ++ */ ++ struct list_head txfree; ++ unsigned int nr_txfree; ++ /* Packets on the txqueue are queued, ++ * but not completely written to the chip, yet. ++ */ ++ struct list_head txqueue; ++ /* Packets on the txrunning queue are completely ++ * posted to the device. We are waiting for the txstatus. ++ */ ++ struct list_head txrunning; ++ /* Total number or packets sent. ++ * (This counter can obviously wrap). ++ */ ++ unsigned int nr_tx_packets; ++ struct tasklet_struct txtask; ++ struct bcm43xx_pio_txpacket tx_packets_cache[BCM43xx_PIO_MAXTXPACKETS]; ++}; ++ ++static inline ++u16 bcm43xx_pio_read(struct bcm43xx_pioqueue *queue, ++ u16 offset) ++{ ++ return bcm43xx_read16(queue->bcm, queue->mmio_base + offset); ++} ++ ++static inline ++void bcm43xx_pio_write(struct bcm43xx_pioqueue *queue, ++ u16 offset, u16 value) ++{ ++ bcm43xx_write16(queue->bcm, queue->mmio_base + offset, value); ++} ++ ++ ++int bcm43xx_pio_init(struct bcm43xx_private *bcm); ++void bcm43xx_pio_free(struct bcm43xx_private *bcm); ++ ++int bcm43xx_pio_tx(struct bcm43xx_private *bcm, ++ struct sk_buff *skb, ++ struct ieee80211_tx_control *ctl); ++void bcm43xx_pio_handle_xmitstatus(struct bcm43xx_private *bcm, ++ struct bcm43xx_xmitstatus *status); ++void bcm43xx_pio_get_tx_stats(struct bcm43xx_private *bcm, ++ struct ieee80211_tx_queue_stats *stats); ++ ++void bcm43xx_pio_rx(struct bcm43xx_pioqueue *queue); ++ ++#else /* CONFIG_BCM43XX_D80211_PIO */ ++ ++static inline ++int bcm43xx_pio_init(struct bcm43xx_private *bcm) ++{ ++ return 0; ++} ++static inline ++void bcm43xx_pio_free(struct bcm43xx_private *bcm) ++{ ++} ++static inline ++int bcm43xx_pio_tx(struct bcm43xx_private *bcm, ++ struct sk_buff *skb, ++ struct ieee80211_tx_control *ctl) ++{ ++ return 0; ++} ++static inline ++void bcm43xx_pio_handle_xmitstatus(struct bcm43xx_private *bcm, ++ struct bcm43xx_xmitstatus *status) ++{ ++} ++static inline ++void bcm43xx_pio_get_tx_stats(struct bcm43xx_private *bcm, ++ struct ieee80211_tx_queue_stats *stats) ++{ ++} ++static inline ++void bcm43xx_pio_rx(struct bcm43xx_pioqueue *queue) ++{ ++} ++ ++#endif /* CONFIG_BCM43XX_D80211_PIO */ ++#endif /* BCM43xx_PIO_H_ */ +diff -Nur linux-2.6.16/drivers/net/wireless/bcm43xx-d80211/bcm43xx_power.c linux-2.6.16-bcm43xx/drivers/net/wireless/bcm43xx-d80211/bcm43xx_power.c +--- linux-2.6.16/drivers/net/wireless/bcm43xx-d80211/bcm43xx_power.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.16-bcm43xx/drivers/net/wireless/bcm43xx-d80211/bcm43xx_power.c 2006-03-28 22:16:14.000000000 +0200 +@@ -0,0 +1,358 @@ ++/* ++ ++ Broadcom BCM43xx wireless driver ++ ++ Copyright (c) 2005 Martin Langer <martin-langer@gmx.de>, ++ Stefano Brivio <st3@riseup.net> ++ Michael Buesch <mbuesch@freenet.de> ++ Danny van Dyk <kugelfang@gentoo.org> ++ Andreas Jaggi <andreas.jaggi@waterwave.ch> ++ ++ Some parts of the code in this file are derived from the ipw2200 ++ driver Copyright(c) 2003 - 2004 Intel Corporation. ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; see the file COPYING. If not, write to ++ the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, ++ Boston, MA 02110-1301, USA. ++ ++*/ ++ ++#include <linux/delay.h> ++ ++#include "bcm43xx.h" ++#include "bcm43xx_power.h" ++#include "bcm43xx_main.h" ++ ++ ++/* Get max/min slowclock frequency ++ * as described in http://bcm-specs.sipsolutions.net/PowerControl ++ */ ++static int bcm43xx_pctl_clockfreqlimit(struct bcm43xx_private *bcm, ++ int get_max) ++{ ++ int limit = 0; ++ int divisor; ++ int selection; ++ int err; ++ u32 tmp; ++ struct bcm43xx_coreinfo *old_core; ++ ++ if (!(bcm->chipcommon_capabilities & BCM43xx_CAPABILITIES_PCTL)) ++ goto out; ++ old_core = bcm->current_core; ++ err = bcm43xx_switch_core(bcm, &bcm->core_chipcommon); ++ if (err) ++ goto out; ++ ++ if (bcm->current_core->rev < 6) { ++ if ((bcm->bustype == BCM43xx_BUSTYPE_PCMCIA) || ++ (bcm->bustype == BCM43xx_BUSTYPE_SB)) { ++ selection = 1; ++ divisor = 32; ++ } else { ++ err = bcm43xx_pci_read_config32(bcm, BCM43xx_PCTL_OUT, &tmp); ++ if (err) { ++ printk(KERN_ERR PFX "clockfreqlimit pcicfg read failure\n"); ++ goto out_switchback; ++ } ++ if (tmp & 0x10) { ++ /* PCI */ ++ selection = 2; ++ divisor = 64; ++ } else { ++ /* XTAL */ ++ selection = 1; ++ divisor = 32; ++ } ++ } ++ } else if (bcm->current_core->rev < 10) { ++ selection = (tmp & 0x07); ++ if (selection) { ++ tmp = bcm43xx_read32(bcm, BCM43xx_CHIPCOMMON_SLOWCLKCTL); ++ divisor = 4 * (1 + ((tmp & 0xFFFF0000) >> 16)); ++ } else ++ divisor = 1; ++ } else { ++ tmp = bcm43xx_read32(bcm, BCM43xx_CHIPCOMMON_SYSCLKCTL); ++ divisor = 4 * (1 + ((tmp & 0xFFFF0000) >> 16)); ++ selection = 1; ++ } ++ ++ switch (selection) { ++ case 0: ++ /* LPO */ ++ if (get_max) ++ limit = 43000; ++ else ++ limit = 25000; ++ break; ++ case 1: ++ /* XTAL */ ++ if (get_max) ++ limit = 20200000; ++ else ++ limit = 19800000; ++ break; ++ case 2: ++ /* PCI */ ++ if (get_max) ++ limit = 34000000; ++ else ++ limit = 25000000; ++ break; ++ default: ++ assert(0); ++ } ++ limit /= divisor; ++ ++out_switchback: ++ err = bcm43xx_switch_core(bcm, old_core); ++ assert(err == 0); ++ ++out: ++ return limit; ++} ++ ++/* init power control ++ * as described in http://bcm-specs.sipsolutions.net/PowerControl ++ */ ++int bcm43xx_pctl_init(struct bcm43xx_private *bcm) ++{ ++ int err, maxfreq; ++ struct bcm43xx_coreinfo *old_core; ++ ++ if (!(bcm->chipcommon_capabilities & BCM43xx_CAPABILITIES_PCTL)) ++ return 0; ++ old_core = bcm->current_core; ++ err = bcm43xx_switch_core(bcm, &bcm->core_chipcommon); ++ if (err == -ENODEV) ++ return 0; ++ if (err) ++ goto out; ++ ++ maxfreq = bcm43xx_pctl_clockfreqlimit(bcm, 1); ++ bcm43xx_write32(bcm, BCM43xx_CHIPCOMMON_PLLONDELAY, ++ (maxfreq * 150 + 999999) / 1000000); ++ bcm43xx_write32(bcm, BCM43xx_CHIPCOMMON_FREFSELDELAY, ++ (maxfreq * 15 + 999999) / 1000000); ++ ++ err = bcm43xx_switch_core(bcm, old_core); ++ assert(err == 0); ++ ++out: ++ return err; ++} ++ ++u16 bcm43xx_pctl_powerup_delay(struct bcm43xx_private *bcm) ++{ ++ u16 delay = 0; ++ int err; ++ u32 pll_on_delay; ++ struct bcm43xx_coreinfo *old_core; ++ int minfreq; ++ ++ if (bcm->bustype != BCM43xx_BUSTYPE_PCI) ++ goto out; ++ if (!(bcm->chipcommon_capabilities & BCM43xx_CAPABILITIES_PCTL)) ++ goto out; ++ old_core = bcm->current_core; ++ err = bcm43xx_switch_core(bcm, &bcm->core_chipcommon); ++ if (err == -ENODEV) ++ goto out; ++ ++ minfreq = bcm43xx_pctl_clockfreqlimit(bcm, 0); ++ pll_on_delay = bcm43xx_read32(bcm, BCM43xx_CHIPCOMMON_PLLONDELAY); ++ delay = (((pll_on_delay + 2) * 1000000) + (minfreq - 1)) / minfreq; ++ ++ err = bcm43xx_switch_core(bcm, old_core); ++ assert(err == 0); ++ ++out: ++ return delay; ++} ++ ++/* set the powercontrol clock ++ * as described in http://bcm-specs.sipsolutions.net/PowerControl ++ */ ++int bcm43xx_pctl_set_clock(struct bcm43xx_private *bcm, u16 mode) ++{ ++ int err; ++ struct bcm43xx_coreinfo *old_core; ++ u32 tmp; ++ ++ old_core = bcm->current_core; ++ err = bcm43xx_switch_core(bcm, &bcm->core_chipcommon); ++ if (err == -ENODEV) ++ return 0; ++ if (err) ++ goto out; ++ ++ if (bcm->core_chipcommon.rev < 6) { ++ if (mode == BCM43xx_PCTL_CLK_FAST) { ++ err = bcm43xx_pctl_set_crystal(bcm, 1); ++ if (err) ++ goto out; ++ } ++ } else { ++ if ((bcm->chipcommon_capabilities & BCM43xx_CAPABILITIES_PCTL) && ++ (bcm->core_chipcommon.rev < 10)) { ++ switch (mode) { ++ case BCM43xx_PCTL_CLK_FAST: ++ tmp = bcm43xx_read32(bcm, BCM43xx_CHIPCOMMON_SLOWCLKCTL); ++ tmp = (tmp & ~BCM43xx_PCTL_FORCE_SLOW) | BCM43xx_PCTL_FORCE_PLL; ++ bcm43xx_write32(bcm, BCM43xx_CHIPCOMMON_SLOWCLKCTL, tmp); ++ break; ++ case BCM43xx_PCTL_CLK_SLOW: ++ tmp = bcm43xx_read32(bcm, BCM43xx_CHIPCOMMON_SLOWCLKCTL); ++ tmp |= BCM43xx_PCTL_FORCE_SLOW; ++ bcm43xx_write32(bcm, BCM43xx_CHIPCOMMON_SLOWCLKCTL, tmp); ++ break; ++ case BCM43xx_PCTL_CLK_DYNAMIC: ++ tmp = bcm43xx_read32(bcm, BCM43xx_CHIPCOMMON_SLOWCLKCTL); ++ tmp &= ~BCM43xx_PCTL_FORCE_SLOW; ++ tmp |= BCM43xx_PCTL_FORCE_PLL; ++ tmp &= ~BCM43xx_PCTL_DYN_XTAL; ++ bcm43xx_write32(bcm, BCM43xx_CHIPCOMMON_SLOWCLKCTL, tmp); ++ } ++ } ++ } ++ ++ err = bcm43xx_switch_core(bcm, old_core); ++ assert(err == 0); ++ ++out: ++ return err; ++} ++ ++int bcm43xx_pctl_set_crystal(struct bcm43xx_private *bcm, int on) ++{ ++ int err; ++ u32 in, out, outenable; ++ ++ err = bcm43xx_pci_read_config32(bcm, BCM43xx_PCTL_IN, &in); ++ if (err) ++ goto err_pci; ++ err = bcm43xx_pci_read_config32(bcm, BCM43xx_PCTL_OUT, &out); ++ if (err) ++ goto err_pci; ++ err = bcm43xx_pci_read_config32(bcm, BCM43xx_PCTL_OUTENABLE, &outenable); ++ if (err) ++ goto err_pci; ++ ++ outenable |= (BCM43xx_PCTL_XTAL_POWERUP | BCM43xx_PCTL_PLL_POWERDOWN); ++ ++ if (on) { ++ if (in & 0x40) ++ return 0; ++ ++ out |= (BCM43xx_PCTL_XTAL_POWERUP | BCM43xx_PCTL_PLL_POWERDOWN); ++ ++ err = bcm43xx_pci_write_config32(bcm, BCM43xx_PCTL_OUT, out); ++ if (err) ++ goto err_pci; ++ err = bcm43xx_pci_write_config32(bcm, BCM43xx_PCTL_OUTENABLE, outenable); ++ if (err) ++ goto err_pci; ++ udelay(1000); ++ ++ out &= ~BCM43xx_PCTL_PLL_POWERDOWN; ++ err = bcm43xx_pci_write_config32(bcm, BCM43xx_PCTL_OUT, out); ++ if (err) ++ goto err_pci; ++ udelay(5000); ++ } else { ++ if (bcm->current_core->rev < 5) ++ return 0; ++ if (bcm->sprom.boardflags & BCM43xx_BFL_XTAL_NOSLOW) ++ return 0; ++ ++/* XXX: Why BCM43xx_MMIO_RADIO_HWENABLED_xx can't be read at this time? ++ * err = bcm43xx_switch_core(bcm, bcm->active_80211_core); ++ * if (err) ++ * return err; ++ * if (((bcm->current_core->rev >= 3) && ++ * (bcm43xx_read32(bcm, BCM43xx_MMIO_RADIO_HWENABLED_HI) & (1 << 16))) || ++ * ((bcm->current_core->rev < 3) && ++ * !(bcm43xx_read16(bcm, BCM43xx_MMIO_RADIO_HWENABLED_LO) & (1 << 4)))) ++ * return 0; ++ * err = bcm43xx_switch_core(bcm, &bcm->core_chipcommon); ++ * if (err) ++ * return err; ++ */ ++ ++ err = bcm43xx_pctl_set_clock(bcm, BCM43xx_PCTL_CLK_SLOW); ++ if (err) ++ goto out; ++ out &= ~BCM43xx_PCTL_XTAL_POWERUP; ++ out |= BCM43xx_PCTL_PLL_POWERDOWN; ++ err = bcm43xx_pci_write_config32(bcm, BCM43xx_PCTL_OUT, out); ++ if (err) ++ goto err_pci; ++ err = bcm43xx_pci_write_config32(bcm, BCM43xx_PCTL_OUTENABLE, outenable); ++ if (err) ++ goto err_pci; ++ } ++ ++out: ++ return err; ++ ++err_pci: ++ printk(KERN_ERR PFX "Error: pctl_set_clock() could not access PCI config space!\n"); ++ err = -EBUSY; ++ goto out; ++} ++ ++/* Set the PowerSavingControlBits. ++ * Bitvalues: ++ * 0 => unset the bit ++ * 1 => set the bit ++ * -1 => calculate the bit ++ */ ++void bcm43xx_power_saving_ctl_bits(struct bcm43xx_private *bcm, ++ int bit25, int bit26) ++{ ++ int i; ++ u32 status; ++ ++//FIXME: Force 25 to off and 26 to on for now: ++bit25 = 0; ++bit26 = 1; ++ ++ if (bit25 == -1) { ++ //TODO: If powersave is not off and FIXME is not set and we are not in adhoc ++ // and thus is not an AP and we are associated, set bit 25 ++ } ++ if (bit26 == -1) { ++ //TODO: If the device is awake or this is an AP, or we are scanning, or FIXME, ++ // or we are associated, or FIXME, or the latest PS-Poll packet sent was ++ // successful, set bit26 ++ } ++ status = bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD); ++ if (bit25) ++ status |= BCM43xx_SBF_PS1; ++ else ++ status &= ~BCM43xx_SBF_PS1; ++ if (bit26) ++ status |= BCM43xx_SBF_PS2; ++ else ++ status &= ~BCM43xx_SBF_PS2; ++ bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS_BITFIELD, status); ++ if (bit26 && bcm->current_core->rev >= 5) { ++ for (i = 0; i < 100; i++) { ++ if (bcm43xx_shm_read32(bcm, BCM43xx_SHM_SHARED, 0x0040) != 4) ++ break; ++ udelay(10); ++ } ++ } ++} +diff -Nur linux-2.6.16/drivers/net/wireless/bcm43xx-d80211/bcm43xx_power.h linux-2.6.16-bcm43xx/drivers/net/wireless/bcm43xx-d80211/bcm43xx_power.h +--- linux-2.6.16/drivers/net/wireless/bcm43xx-d80211/bcm43xx_power.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.16-bcm43xx/drivers/net/wireless/bcm43xx-d80211/bcm43xx_power.h 2006-03-28 22:16:14.000000000 +0200 +@@ -0,0 +1,47 @@ ++/* ++ ++ Broadcom BCM43xx wireless driver ++ ++ Copyright (c) 2005 Martin Langer <martin-langer@gmx.de>, ++ Stefano Brivio <st3@riseup.net> ++ Michael Buesch <mbuesch@freenet.de> ++ Danny van Dyk <kugelfang@gentoo.org> ++ Andreas Jaggi <andreas.jaggi@waterwave.ch> ++ ++ Some parts of the code in this file are derived from the ipw2200 ++ driver Copyright(c) 2003 - 2004 Intel Corporation. ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; see the file COPYING. If not, write to ++ the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, ++ Boston, MA 02110-1301, USA. ++ ++*/ ++ ++#ifndef BCM43xx_POWER_H_ ++#define BCM43xx_POWER_H_ ++ ++#include <linux/types.h> ++ ++ ++struct bcm43xx_private; ++ ++int bcm43xx_pctl_init(struct bcm43xx_private *bcm); ++int bcm43xx_pctl_set_clock(struct bcm43xx_private *bcm, u16 mode); ++int bcm43xx_pctl_set_crystal(struct bcm43xx_private *bcm, int on); ++u16 bcm43xx_pctl_powerup_delay(struct bcm43xx_private *bcm); ++ ++void bcm43xx_power_saving_ctl_bits(struct bcm43xx_private *bcm, ++ int bit25, int bit26); ++ ++#endif /* BCM43xx_POWER_H_ */ +diff -Nur linux-2.6.16/drivers/net/wireless/bcm43xx-d80211/bcm43xx_radio.c linux-2.6.16-bcm43xx/drivers/net/wireless/bcm43xx-d80211/bcm43xx_radio.c +--- linux-2.6.16/drivers/net/wireless/bcm43xx-d80211/bcm43xx_radio.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.16-bcm43xx/drivers/net/wireless/bcm43xx-d80211/bcm43xx_radio.c 2006-03-28 22:16:14.000000000 +0200 +@@ -0,0 +1,2026 @@ ++/* ++ ++ Broadcom BCM43xx wireless driver ++ ++ Copyright (c) 2005 Martin Langer <martin-langer@gmx.de>, ++ Stefano Brivio <st3@riseup.net> ++ Michael Buesch <mbuesch@freenet.de> ++ Danny van Dyk <kugelfang@gentoo.org> ++ Andreas Jaggi <andreas.jaggi@waterwave.ch> ++ ++ Some parts of the code in this file are derived from the ipw2200 ++ driver Copyright(c) 2003 - 2004 Intel Corporation. ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; see the file COPYING. If not, write to ++ the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, ++ Boston, MA 02110-1301, USA. ++ ++*/ ++ ++#include <linux/delay.h> ++ ++#include "bcm43xx.h" ++#include "bcm43xx_main.h" ++#include "bcm43xx_phy.h" ++#include "bcm43xx_radio.h" ++#include "bcm43xx_ilt.h" ++ ++ ++/* Table for bcm43xx_radio_calibrationvalue() */ ++static const u16 rcc_table[16] = { ++ 0x0002, 0x0003, 0x0001, 0x000F, ++ 0x0006, 0x0007, 0x0005, 0x000F, ++ 0x000A, 0x000B, 0x0009, 0x000F, ++ 0x000E, 0x000F, 0x000D, 0x000F, ++}; ++ ++/* Reverse the bits of a 4bit value. ++ * Example: 1101 is flipped 1011 ++ */ ++static u16 flip_4bit(u16 value) ++{ ++ u16 flipped = 0x0000; ++ ++ assert((value & ~0x000F) == 0x0000); ++ ++ flipped |= (value & 0x0001) << 3; ++ flipped |= (value & 0x0002) << 1; ++ flipped |= (value & 0x0004) >> 1; ++ flipped |= (value & 0x0008) >> 3; ++ ++ return flipped; ++} ++ ++/* Get the freq, as it has to be written to the device. */ ++static inline ++u16 channel2freq_bg(u8 channel) ++{ ++ /* Frequencies are given as frequencies_bg[index] + 2.4GHz ++ * Starting with channel 1 ++ */ ++ static const u16 frequencies_bg[14] = { ++ 12, 17, 22, 27, ++ 32, 37, 42, 47, ++ 52, 57, 62, 67, ++ 72, 84, ++ }; ++ ++ assert(channel >= 1 && channel <= 14); ++ ++ return frequencies_bg[channel - 1]; ++} ++ ++/* Get the freq, as it has to be written to the device. */ ++static inline ++u16 channel2freq_a(u8 channel) ++{ ++ assert(channel <= 200); ++ ++ return (5000 + 5 * channel); ++} ++ ++void bcm43xx_radio_lock(struct bcm43xx_private *bcm) ++{ ++ u32 status; ++ ++ status = bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD); ++ status |= BCM43xx_SBF_RADIOREG_LOCK; ++ bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS_BITFIELD, status); ++ mmiowb(); ++ udelay(10); ++} ++ ++void bcm43xx_radio_unlock(struct bcm43xx_private *bcm) ++{ ++ u32 status; ++ ++ bcm43xx_read16(bcm, BCM43xx_MMIO_PHY_VER); /* dummy read */ ++ status = bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD); ++ status &= ~BCM43xx_SBF_RADIOREG_LOCK; ++ bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS_BITFIELD, status); ++ mmiowb(); ++} ++ ++u16 bcm43xx_radio_read16(struct bcm43xx_private *bcm, u16 offset) ++{ ++ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); ++ struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm); ++ ++ switch (phy->type) { ++ case BCM43xx_PHYTYPE_A: ++ offset |= 0x0040; ++ break; ++ case BCM43xx_PHYTYPE_B: ++ if (radio->version == 0x2053) { ++ if (offset < 0x70) ++ offset += 0x80; ++ else if (offset < 0x80) ++ offset += 0x70; ++ } else if (radio->version == 0x2050) { ++ offset |= 0x80; ++ } else ++ assert(0); ++ break; ++ case BCM43xx_PHYTYPE_G: ++ offset |= 0x80; ++ break; ++ } ++ ++ bcm43xx_write16(bcm, BCM43xx_MMIO_RADIO_CONTROL, offset); ++ return bcm43xx_read16(bcm, BCM43xx_MMIO_RADIO_DATA_LOW); ++} ++ ++void bcm43xx_radio_write16(struct bcm43xx_private *bcm, u16 offset, u16 val) ++{ ++ bcm43xx_write16(bcm, BCM43xx_MMIO_RADIO_CONTROL, offset); ++ mmiowb(); ++ bcm43xx_write16(bcm, BCM43xx_MMIO_RADIO_DATA_LOW, val); ++} ++ ++static void bcm43xx_set_all_gains(struct bcm43xx_private *bcm, ++ s16 first, s16 second, s16 third) ++{ ++ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); ++ u16 i; ++ u16 start = 0x08, end = 0x18; ++ u16 offset = 0x0400; ++ u16 tmp; ++ ++ if (phy->rev <= 1) { ++ offset = 0x5000; ++ start = 0x10; ++ end = 0x20; ++ } ++ ++ for (i = 0; i < 4; i++) ++ bcm43xx_ilt_write(bcm, offset + i, first); ++ ++ for (i = start; i < end; i++) ++ bcm43xx_ilt_write(bcm, offset + i, second); ++ ++ if (third != -1) { ++ tmp = ((u16)third << 14) | ((u16)third << 6); ++ bcm43xx_phy_write(bcm, 0x04A0, ++ (bcm43xx_phy_read(bcm, 0x04A0) & 0xBFBF) | tmp); ++ bcm43xx_phy_write(bcm, 0x04A1, ++ (bcm43xx_phy_read(bcm, 0x04A1) & 0xBFBF) | tmp); ++ bcm43xx_phy_write(bcm, 0x04A2, ++ (bcm43xx_phy_read(bcm, 0x04A2) & 0xBFBF) | tmp); ++ } ++ bcm43xx_dummy_transmission(bcm); ++} ++ ++static void bcm43xx_set_original_gains(struct bcm43xx_private *bcm) ++{ ++ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); ++ u16 i, tmp; ++ u16 offset = 0x0400; ++ u16 start = 0x0008, end = 0x0018; ++ ++ if (phy->rev <= 1) { ++ offset = 0x5000; ++ start = 0x0010; ++ end = 0x0020; ++ } ++ ++ for (i = 0; i < 4; i++) { ++ tmp = (i & 0xFFFC); ++ tmp |= (i & 0x0001) << 1; ++ tmp |= (i & 0x0002) >> 1; ++ ++ bcm43xx_ilt_write(bcm, offset + i, tmp); ++ } ++ ++ for (i = start; i < end; i++) ++ bcm43xx_ilt_write(bcm, offset + i, i - start); ++ ++ bcm43xx_phy_write(bcm, 0x04A0, ++ (bcm43xx_phy_read(bcm, 0x04A0) & 0xBFBF) | 0x4040); ++ bcm43xx_phy_write(bcm, 0x04A1, ++ (bcm43xx_phy_read(bcm, 0x04A1) & 0xBFBF) | 0x4040); ++ bcm43xx_phy_write(bcm, 0x04A2, ++ (bcm43xx_phy_read(bcm, 0x04A2) & 0xBFBF) | 0x4000); ++ bcm43xx_dummy_transmission(bcm); ++} ++ ++/* Synthetic PU workaround */ ++static void bcm43xx_synth_pu_workaround(struct bcm43xx_private *bcm, u8 channel) ++{ ++ struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm); ++ ++ if (radio->version != 0x2050 || radio->revision >= 6) { ++ /* We do not need the workaround. */ ++ return; ++ } ++ ++ if (channel <= 10) { ++ bcm43xx_write16(bcm, BCM43xx_MMIO_CHANNEL, ++ channel2freq_bg(channel + 4)); ++ } else { ++ bcm43xx_write16(bcm, BCM43xx_MMIO_CHANNEL, ++ channel2freq_bg(1)); ++ } ++ udelay(100); ++ bcm43xx_write16(bcm, BCM43xx_MMIO_CHANNEL, ++ channel2freq_bg(channel)); ++} ++ ++u8 bcm43xx_radio_aci_detect(struct bcm43xx_private *bcm, u8 channel) ++{ ++ struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm); ++ u8 ret = 0; ++ u16 saved, rssi, temp; ++ int i, j = 0; ++ ++ saved = bcm43xx_phy_read(bcm, 0x0403); ++ bcm43xx_radio_selectchannel(bcm, channel, 0); ++ bcm43xx_phy_write(bcm, 0x0403, (saved & 0xFFF8) | 5); ++ if (radio->aci_hw_rssi) ++ rssi = bcm43xx_phy_read(bcm, 0x048A) & 0x3F; ++ else ++ rssi = saved & 0x3F; ++ /* clamp temp to signed 5bit */ ++ if (rssi > 32) ++ rssi -= 64; ++ for (i = 0;i < 100; i++) { ++ temp = (bcm43xx_phy_read(bcm, 0x047F) >> 8) & 0x3F; ++ if (temp > 32) ++ temp -= 64; ++ if (temp < rssi) ++ j++; ++ if (j >= 20) ++ ret = 1; ++ } ++ bcm43xx_phy_write(bcm, 0x0403, saved); ++ ++ return ret; ++} ++ ++u8 bcm43xx_radio_aci_scan(struct bcm43xx_private *bcm) ++{ ++ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); ++ struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm); ++ u8 ret[13]; ++ unsigned int channel = radio->channel; ++ unsigned int i, j, start, end; ++ unsigned long phylock_flags; ++ ++ if (!((phy->type == BCM43xx_PHYTYPE_G) && (phy->rev > 0))) ++ return 0; ++ ++ bcm43xx_phy_lock(bcm, phylock_flags); ++ bcm43xx_radio_lock(bcm); ++ bcm43xx_phy_write(bcm, 0x0802, ++ bcm43xx_phy_read(bcm, 0x0802) & 0xFFFC); ++ bcm43xx_phy_write(bcm, BCM43xx_PHY_G_CRS, ++ bcm43xx_phy_read(bcm, BCM43xx_PHY_G_CRS) & 0x7FFF); ++ bcm43xx_set_all_gains(bcm, 3, 8, 1); ++ ++ start = (channel - 5 > 0) ? channel - 5 : 1; ++ end = (channel + 5 < 14) ? channel + 5 : 13; ++ ++ for (i = start; i <= end; i++) { ++ if (abs(channel - i) > 2) ++ ret[i-1] = bcm43xx_radio_aci_detect(bcm, i); ++ } ++ bcm43xx_radio_selectchannel(bcm, channel, 0); ++ bcm43xx_phy_write(bcm, 0x0802, ++ (bcm43xx_phy_read(bcm, 0x0802) & 0xFFFC) | 0x0003); ++ bcm43xx_phy_write(bcm, 0x0403, ++ bcm43xx_phy_read(bcm, 0x0403) & 0xFFF8); ++ bcm43xx_phy_write(bcm, BCM43xx_PHY_G_CRS, ++ bcm43xx_phy_read(bcm, BCM43xx_PHY_G_CRS) | 0x8000); ++ bcm43xx_set_original_gains(bcm); ++ for (i = 0; i < 13; i++) { ++ if (!ret[i]) ++ continue; ++ end = (i + 5 < 13) ? i + 5 : 13; ++ for (j = i; j < end; j++) ++ ret[j] = 1; ++ } ++ bcm43xx_radio_unlock(bcm); ++ bcm43xx_phy_unlock(bcm, phylock_flags); ++ ++ return ret[channel - 1]; ++} ++ ++/* http://bcm-specs.sipsolutions.net/NRSSILookupTable */ ++void bcm43xx_nrssi_hw_write(struct bcm43xx_private *bcm, u16 offset, s16 val) ++{ ++ bcm43xx_phy_write(bcm, BCM43xx_PHY_NRSSILT_CTRL, offset); ++ mmiowb(); ++ bcm43xx_phy_write(bcm, BCM43xx_PHY_NRSSILT_DATA, (u16)val); ++} ++ ++/* http://bcm-specs.sipsolutions.net/NRSSILookupTable */ ++s16 bcm43xx_nrssi_hw_read(struct bcm43xx_private *bcm, u16 offset) ++{ ++ u16 val; ++ ++ bcm43xx_phy_write(bcm, BCM43xx_PHY_NRSSILT_CTRL, offset); ++ val = bcm43xx_phy_read(bcm, BCM43xx_PHY_NRSSILT_DATA); ++ ++ return (s16)val; ++} ++ ++/* http://bcm-specs.sipsolutions.net/NRSSILookupTable */ ++void bcm43xx_nrssi_hw_update(struct bcm43xx_private *bcm, u16 val) ++{ ++ u16 i; ++ s16 tmp; ++ ++ for (i = 0; i < 64; i++) { ++ tmp = bcm43xx_nrssi_hw_read(bcm, i); ++ tmp -= val; ++ tmp = limit_value(tmp, -32, 31); ++ bcm43xx_nrssi_hw_write(bcm, i, tmp); ++ } ++} ++ ++/* http://bcm-specs.sipsolutions.net/NRSSILookupTable */ ++void bcm43xx_nrssi_mem_update(struct bcm43xx_private *bcm) ++{ ++ struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm); ++ s16 i, delta; ++ s32 tmp; ++ ++ delta = 0x1F - radio->nrssi[0]; ++ for (i = 0; i < 64; i++) { ++ tmp = (i - delta) * radio->nrssislope; ++ tmp /= 0x10000; ++ tmp += 0x3A; ++ tmp = limit_value(tmp, 0, 0x3F); ++ radio->nrssi_lt[i] = tmp; ++ } ++} ++ ++static void bcm43xx_calc_nrssi_offset(struct bcm43xx_private *bcm) ++{ ++ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); ++ u16 backup[20] = { 0 }; ++ s16 v47F; ++ u16 i; ++ u16 saved = 0xFFFF; ++ ++ backup[0] = bcm43xx_phy_read(bcm, 0x0001); ++ backup[1] = bcm43xx_phy_read(bcm, 0x0811); ++ backup[2] = bcm43xx_phy_read(bcm, 0x0812); ++ backup[3] = bcm43xx_phy_read(bcm, 0x0814); ++ backup[4] = bcm43xx_phy_read(bcm, 0x0815); ++ backup[5] = bcm43xx_phy_read(bcm, 0x005A); ++ backup[6] = bcm43xx_phy_read(bcm, 0x0059); ++ backup[7] = bcm43xx_phy_read(bcm, 0x0058); ++ backup[8] = bcm43xx_phy_read(bcm, 0x000A); ++ backup[9] = bcm43xx_phy_read(bcm, 0x0003); ++ backup[10] = bcm43xx_radio_read16(bcm, 0x007A); ++ backup[11] = bcm43xx_radio_read16(bcm, 0x0043); ++ ++ bcm43xx_phy_write(bcm, 0x0429, ++ bcm43xx_phy_read(bcm, 0x0429) & 0x7FFF); ++ bcm43xx_phy_write(bcm, 0x0001, ++ (bcm43xx_phy_read(bcm, 0x0001) & 0x3FFF) | 0x4000); ++ bcm43xx_phy_write(bcm, 0x0811, ++ bcm43xx_phy_read(bcm, 0x0811) | 0x000C); ++ bcm43xx_phy_write(bcm, 0x0812, ++ (bcm43xx_phy_read(bcm, 0x0812) & 0xFFF3) | 0x0004); ++ bcm43xx_phy_write(bcm, 0x0802, ++ bcm43xx_phy_read(bcm, 0x0802) & ~(0x1 | 0x2)); ++ if (phy->rev >= 6) { ++ backup[12] = bcm43xx_phy_read(bcm, 0x002E); ++ backup[13] = bcm43xx_phy_read(bcm, 0x002F); ++ backup[14] = bcm43xx_phy_read(bcm, 0x080F); ++ backup[15] = bcm43xx_phy_read(bcm, 0x0810); ++ backup[16] = bcm43xx_phy_read(bcm, 0x0801); ++ backup[17] = bcm43xx_phy_read(bcm, 0x0060); ++ backup[18] = bcm43xx_phy_read(bcm, 0x0014); ++ backup[19] = bcm43xx_phy_read(bcm, 0x0478); ++ ++ bcm43xx_phy_write(bcm, 0x002E, 0); ++ bcm43xx_phy_write(bcm, 0x002F, 0); ++ bcm43xx_phy_write(bcm, 0x080F, 0); ++ bcm43xx_phy_write(bcm, 0x0810, 0); ++ bcm43xx_phy_write(bcm, 0x0478, ++ bcm43xx_phy_read(bcm, 0x0478) | 0x0100); ++ bcm43xx_phy_write(bcm, 0x0801, ++ bcm43xx_phy_read(bcm, 0x0801) | 0x0040); ++ bcm43xx_phy_write(bcm, 0x0060, ++ bcm43xx_phy_read(bcm, 0x0060) | 0x0040); ++ bcm43xx_phy_write(bcm, 0x0014, ++ bcm43xx_phy_read(bcm, 0x0014) | 0x0200); ++ } ++ bcm43xx_radio_write16(bcm, 0x007A, ++ bcm43xx_radio_read16(bcm, 0x007A) | 0x0070); ++ bcm43xx_radio_write16(bcm, 0x007A, ++ bcm43xx_radio_read16(bcm, 0x007A) | 0x0080); ++ udelay(30); ++ ++ v47F = (s16)((bcm43xx_phy_read(bcm, 0x047F) >> 8) & 0x003F); ++ if (v47F >= 0x20) ++ v47F -= 0x40; ++ if (v47F == 31) { ++ for (i = 7; i >= 4; i--) { ++ bcm43xx_radio_write16(bcm, 0x007B, i); ++ udelay(20); ++ v47F = (s16)((bcm43xx_phy_read(bcm, 0x047F) >> 8) & 0x003F); ++ if (v47F >= 0x20) ++ v47F -= 0x40; ++ if (v47F < 31 && saved == 0xFFFF) ++ saved = i; ++ } ++ if (saved == 0xFFFF) ++ saved = 4; ++ } else { ++ bcm43xx_radio_write16(bcm, 0x007A, ++ bcm43xx_radio_read16(bcm, 0x007A) & 0x007F); ++ bcm43xx_phy_write(bcm, 0x0814, ++ bcm43xx_phy_read(bcm, 0x0814) | 0x0001); ++ bcm43xx_phy_write(bcm, 0x0815, ++ bcm43xx_phy_read(bcm, 0x0815) & 0xFFFE); ++ bcm43xx_phy_write(bcm, 0x0811, ++ bcm43xx_phy_read(bcm, 0x0811) | 0x000C); ++ bcm43xx_phy_write(bcm, 0x0812, ++ bcm43xx_phy_read(bcm, 0x0812) | 0x000C); ++ bcm43xx_phy_write(bcm, 0x0811, ++ bcm43xx_phy_read(bcm, 0x0811) | 0x0030); ++ bcm43xx_phy_write(bcm, 0x0812, ++ bcm43xx_phy_read(bcm, 0x0812) | 0x0030); ++ bcm43xx_phy_write(bcm, 0x005A, 0x0480); ++ bcm43xx_phy_write(bcm, 0x0059, 0x0810); ++ bcm43xx_phy_write(bcm, 0x0058, 0x000D); ++ if (phy->rev == 0) { ++ bcm43xx_phy_write(bcm, 0x0003, 0x0122); ++ } else { ++ bcm43xx_phy_write(bcm, 0x000A, ++ bcm43xx_phy_read(bcm, 0x000A) ++ | 0x2000); ++ } ++ bcm43xx_phy_write(bcm, 0x0814, ++ bcm43xx_phy_read(bcm, 0x0814) | 0x0004); ++ bcm43xx_phy_write(bcm, 0x0815, ++ bcm43xx_phy_read(bcm, 0x0815) & 0xFFFB); ++ bcm43xx_phy_write(bcm, 0x0003, ++ (bcm43xx_phy_read(bcm, 0x0003) & 0xFF9F) ++ | 0x0040); ++ bcm43xx_radio_write16(bcm, 0x007A, ++ bcm43xx_radio_read16(bcm, 0x007A) | 0x000F); ++ bcm43xx_set_all_gains(bcm, 3, 0, 1); ++ bcm43xx_radio_write16(bcm, 0x0043, ++ (bcm43xx_radio_read16(bcm, 0x0043) ++ & 0x00F0) | 0x000F); ++ udelay(30); ++ v47F = (s16)((bcm43xx_phy_read(bcm, 0x047F) >> 8) & 0x003F); ++ if (v47F >= 0x20) ++ v47F -= 0x40; ++ if (v47F == -32) { ++ for (i = 0; i < 4; i++) { ++ bcm43xx_radio_write16(bcm, 0x007B, i); ++ udelay(20); ++ v47F = (s16)((bcm43xx_phy_read(bcm, 0x047F) >> 8) & 0x003F); ++ if (v47F >= 0x20) ++ v47F -= 0x40; ++ if (v47F > -31 && saved == 0xFFFF) ++ saved = i; ++ } ++ if (saved == 0xFFFF) ++ saved = 3; ++ } else ++ saved = 0; ++ } ++ bcm43xx_radio_write16(bcm, 0x007B, saved); ++ ++ if (phy->rev >= 6) { ++ bcm43xx_phy_write(bcm, 0x002E, backup[12]); ++ bcm43xx_phy_write(bcm, 0x002F, backup[13]); ++ bcm43xx_phy_write(bcm, 0x080F, backup[14]); ++ bcm43xx_phy_write(bcm, 0x0810, backup[15]); ++ } ++ bcm43xx_phy_write(bcm, 0x0814, backup[3]); ++ bcm43xx_phy_write(bcm, 0x0815, backup[4]); ++ bcm43xx_phy_write(bcm, 0x005A, backup[5]); ++ bcm43xx_phy_write(bcm, 0x0059, backup[6]); ++ bcm43xx_phy_write(bcm, 0x0058, backup[7]); ++ bcm43xx_phy_write(bcm, 0x000A, backup[8]); ++ bcm43xx_phy_write(bcm, 0x0003, backup[9]); ++ bcm43xx_radio_write16(bcm, 0x0043, backup[11]); ++ bcm43xx_radio_write16(bcm, 0x007A, backup[10]); ++ bcm43xx_phy_write(bcm, 0x0802, ++ bcm43xx_phy_read(bcm, 0x0802) | 0x1 | 0x2); ++ bcm43xx_phy_write(bcm, 0x0429, ++ bcm43xx_phy_read(bcm, 0x0429) | 0x8000); ++ bcm43xx_set_original_gains(bcm); ++ if (phy->rev >= 6) { ++ bcm43xx_phy_write(bcm, 0x0801, backup[16]); ++ bcm43xx_phy_write(bcm, 0x0060, backup[17]); ++ bcm43xx_phy_write(bcm, 0x0014, backup[18]); ++ bcm43xx_phy_write(bcm, 0x0478, backup[19]); ++ } ++ bcm43xx_phy_write(bcm, 0x0001, backup[0]); ++ bcm43xx_phy_write(bcm, 0x0812, backup[2]); ++ bcm43xx_phy_write(bcm, 0x0811, backup[1]); ++} ++ ++void bcm43xx_calc_nrssi_slope(struct bcm43xx_private *bcm) ++{ ++ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); ++ struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm); ++ u16 backup[18] = { 0 }; ++ u16 tmp; ++ s16 nrssi0, nrssi1; ++ ++ switch (phy->type) { ++ case BCM43xx_PHYTYPE_B: ++ backup[0] = bcm43xx_radio_read16(bcm, 0x007A); ++ backup[1] = bcm43xx_radio_read16(bcm, 0x0052); ++ backup[2] = bcm43xx_radio_read16(bcm, 0x0043); ++ backup[3] = bcm43xx_phy_read(bcm, 0x0030); ++ backup[4] = bcm43xx_phy_read(bcm, 0x0026); ++ backup[5] = bcm43xx_phy_read(bcm, 0x0015); ++ backup[6] = bcm43xx_phy_read(bcm, 0x002A); ++ backup[7] = bcm43xx_phy_read(bcm, 0x0020); ++ backup[8] = bcm43xx_phy_read(bcm, 0x005A); ++ backup[9] = bcm43xx_phy_read(bcm, 0x0059); ++ backup[10] = bcm43xx_phy_read(bcm, 0x0058); ++ backup[11] = bcm43xx_read16(bcm, 0x03E2); ++ backup[12] = bcm43xx_read16(bcm, 0x03E6); ++ backup[13] = bcm43xx_read16(bcm, BCM43xx_MMIO_CHANNEL_EXT); ++ ++ tmp = bcm43xx_radio_read16(bcm, 0x007A); ++ tmp &= (phy->rev >= 5) ? 0x007F : 0x000F; ++ bcm43xx_radio_write16(bcm, 0x007A, tmp); ++ bcm43xx_phy_write(bcm, 0x0030, 0x00FF); ++ bcm43xx_write16(bcm, 0x03EC, 0x7F7F); ++ bcm43xx_phy_write(bcm, 0x0026, 0x0000); ++ bcm43xx_phy_write(bcm, 0x0015, ++ bcm43xx_phy_read(bcm, 0x0015) | 0x0020); ++ bcm43xx_phy_write(bcm, 0x002A, 0x08A3); ++ bcm43xx_radio_write16(bcm, 0x007A, ++ bcm43xx_radio_read16(bcm, 0x007A) | 0x0080); ++ ++ nrssi0 = (s16)bcm43xx_phy_read(bcm, 0x0027); ++ bcm43xx_radio_write16(bcm, 0x007A, ++ bcm43xx_radio_read16(bcm, 0x007A) & 0x007F); ++ if (phy->rev >= 2) { ++ bcm43xx_write16(bcm, 0x03E6, 0x0040); ++ } else if (phy->rev == 0) { ++ bcm43xx_write16(bcm, 0x03E6, 0x0122); ++ } else { ++ bcm43xx_write16(bcm, BCM43xx_MMIO_CHANNEL_EXT, ++ bcm43xx_read16(bcm, BCM43xx_MMIO_CHANNEL_EXT) & 0x2000); ++ } ++ bcm43xx_phy_write(bcm, 0x0020, 0x3F3F); ++ bcm43xx_phy_write(bcm, 0x0015, 0xF330); ++ bcm43xx_radio_write16(bcm, 0x005A, 0x0060); ++ bcm43xx_radio_write16(bcm, 0x0043, ++ bcm43xx_radio_read16(bcm, 0x0043) & 0x00F0); ++ bcm43xx_phy_write(bcm, 0x005A, 0x0480); ++ bcm43xx_phy_write(bcm, 0x0059, 0x0810); ++ bcm43xx_phy_write(bcm, 0x0058, 0x000D); ++ udelay(20); ++ ++ nrssi1 = (s16)bcm43xx_phy_read(bcm, 0x0027); ++ bcm43xx_phy_write(bcm, 0x0030, backup[3]); ++ bcm43xx_radio_write16(bcm, 0x007A, backup[0]); ++ bcm43xx_write16(bcm, 0x03E2, backup[11]); ++ bcm43xx_phy_write(bcm, 0x0026, backup[4]); ++ bcm43xx_phy_write(bcm, 0x0015, backup[5]); ++ bcm43xx_phy_write(bcm, 0x002A, backup[6]); ++ bcm43xx_synth_pu_workaround(bcm, radio->channel); ++ if (phy->rev != 0) ++ bcm43xx_write16(bcm, 0x03F4, backup[13]); ++ ++ bcm43xx_phy_write(bcm, 0x0020, backup[7]); ++ bcm43xx_phy_write(bcm, 0x005A, backup[8]); ++ bcm43xx_phy_write(bcm, 0x0059, backup[9]); ++ bcm43xx_phy_write(bcm, 0x0058, backup[10]); ++ bcm43xx_radio_write16(bcm, 0x0052, backup[1]); ++ bcm43xx_radio_write16(bcm, 0x0043, backup[2]); ++ ++ if (nrssi0 == nrssi1) ++ radio->nrssislope = 0x00010000; ++ else ++ radio->nrssislope = 0x00400000 / (nrssi0 - nrssi1); ++ ++ if (nrssi0 <= -4) { ++ radio->nrssi[0] = nrssi0; ++ radio->nrssi[1] = nrssi1; ++ } ++ break; ++ case BCM43xx_PHYTYPE_G: ++ if (radio->revision >= 9) ++ return; ++ if (radio->revision == 8) ++ bcm43xx_calc_nrssi_offset(bcm); ++ ++ bcm43xx_phy_write(bcm, BCM43xx_PHY_G_CRS, ++ bcm43xx_phy_read(bcm, BCM43xx_PHY_G_CRS) & 0x7FFF); ++ bcm43xx_phy_write(bcm, 0x0802, ++ bcm43xx_phy_read(bcm, 0x0802) & 0xFFFC); ++ backup[7] = bcm43xx_read16(bcm, 0x03E2); ++ bcm43xx_write16(bcm, 0x03E2, ++ bcm43xx_read16(bcm, 0x03E2) | 0x8000); ++ backup[0] = bcm43xx_radio_read16(bcm, 0x007A); ++ backup[1] = bcm43xx_radio_read16(bcm, 0x0052); ++ backup[2] = bcm43xx_radio_read16(bcm, 0x0043); ++ backup[3] = bcm43xx_phy_read(bcm, 0x0015); ++ backup[4] = bcm43xx_phy_read(bcm, 0x005A); ++ backup[5] = bcm43xx_phy_read(bcm, 0x0059); ++ backup[6] = bcm43xx_phy_read(bcm, 0x0058); ++ backup[8] = bcm43xx_read16(bcm, 0x03E6); ++ backup[9] = bcm43xx_read16(bcm, BCM43xx_MMIO_CHANNEL_EXT); ++ if (phy->rev >= 3) { ++ backup[10] = bcm43xx_phy_read(bcm, 0x002E); ++ backup[11] = bcm43xx_phy_read(bcm, 0x002F); ++ backup[12] = bcm43xx_phy_read(bcm, 0x080F); ++ backup[13] = bcm43xx_phy_read(bcm, BCM43xx_PHY_G_LO_CONTROL); ++ backup[14] = bcm43xx_phy_read(bcm, 0x0801); ++ backup[15] = bcm43xx_phy_read(bcm, 0x0060); ++ backup[16] = bcm43xx_phy_read(bcm, 0x0014); ++ backup[17] = bcm43xx_phy_read(bcm, 0x0478); ++ bcm43xx_phy_write(bcm, 0x002E, 0); ++ bcm43xx_phy_write(bcm, BCM43xx_PHY_G_LO_CONTROL, 0); ++ switch (phy->rev) { ++ case 4: case 6: case 7: ++ bcm43xx_phy_write(bcm, 0x0478, ++ bcm43xx_phy_read(bcm, 0x0478) ++ | 0x0100); ++ bcm43xx_phy_write(bcm, 0x0801, ++ bcm43xx_phy_read(bcm, 0x0801) ++ | 0x0040); ++ break; ++ case 3: case 5: ++ bcm43xx_phy_write(bcm, 0x0801, ++ bcm43xx_phy_read(bcm, 0x0801) ++ & 0xFFBF); ++ break; ++ } ++ bcm43xx_phy_write(bcm, 0x0060, ++ bcm43xx_phy_read(bcm, 0x0060) ++ | 0x0040); ++ bcm43xx_phy_write(bcm, 0x0014, ++ bcm43xx_phy_read(bcm, 0x0014) ++ | 0x0200); ++ } ++ bcm43xx_radio_write16(bcm, 0x007A, ++ bcm43xx_radio_read16(bcm, 0x007A) | 0x0070); ++ bcm43xx_set_all_gains(bcm, 0, 8, 0); ++ bcm43xx_radio_write16(bcm, 0x007A, ++ bcm43xx_radio_read16(bcm, 0x007A) & 0x00F7); ++ if (phy->rev >= 2) { ++ bcm43xx_phy_write(bcm, 0x0811, ++ (bcm43xx_phy_read(bcm, 0x0811) & 0xFFCF) | 0x0030); ++ bcm43xx_phy_write(bcm, 0x0812, ++ (bcm43xx_phy_read(bcm, 0x0812) & 0xFFCF) | 0x0010); ++ } ++ bcm43xx_radio_write16(bcm, 0x007A, ++ bcm43xx_radio_read16(bcm, 0x007A) | 0x0080); ++ udelay(20); ++ ++ nrssi0 = (s16)((bcm43xx_phy_read(bcm, 0x047F) >> 8) & 0x003F); ++ if (nrssi0 >= 0x0020) ++ nrssi0 -= 0x0040; ++ ++ bcm43xx_radio_write16(bcm, 0x007A, ++ bcm43xx_radio_read16(bcm, 0x007A) & 0x007F); ++ if (phy->rev >= 2) { ++ bcm43xx_phy_write(bcm, 0x0003, ++ (bcm43xx_phy_read(bcm, 0x0003) ++ & 0xFF9F) | 0x0040); ++ } ++ ++ bcm43xx_write16(bcm, BCM43xx_MMIO_CHANNEL_EXT, ++ bcm43xx_read16(bcm, BCM43xx_MMIO_CHANNEL_EXT) ++ | 0x2000); ++ bcm43xx_radio_write16(bcm, 0x007A, ++ bcm43xx_radio_read16(bcm, 0x007A) | 0x000F); ++ bcm43xx_phy_write(bcm, 0x0015, 0xF330); ++ if (phy->rev >= 2) { ++ bcm43xx_phy_write(bcm, 0x0812, ++ (bcm43xx_phy_read(bcm, 0x0812) & 0xFFCF) | 0x0020); ++ bcm43xx_phy_write(bcm, 0x0811, ++ (bcm43xx_phy_read(bcm, 0x0811) & 0xFFCF) | 0x0020); ++ } ++ ++ bcm43xx_set_all_gains(bcm, 3, 0, 1); ++ if (radio->revision == 8) { ++ bcm43xx_radio_write16(bcm, 0x0043, 0x001F); ++ } else { ++ tmp = bcm43xx_radio_read16(bcm, 0x0052) & 0xFF0F; ++ bcm43xx_radio_write16(bcm, 0x0052, tmp | 0x0060); ++ tmp = bcm43xx_radio_read16(bcm, 0x0043) & 0xFFF0; ++ bcm43xx_radio_write16(bcm, 0x0043, tmp | 0x0009); ++ } ++ bcm43xx_phy_write(bcm, 0x005A, 0x0480); ++ bcm43xx_phy_write(bcm, 0x0059, 0x0810); ++ bcm43xx_phy_write(bcm, 0x0058, 0x000D); ++ udelay(20); ++ nrssi1 = (s16)((bcm43xx_phy_read(bcm, 0x047F) >> 8) & 0x003F); ++ if (nrssi1 >= 0x0020) ++ nrssi1 -= 0x0040; ++ if (nrssi0 == nrssi1) ++ radio->nrssislope = 0x00010000; ++ else ++ radio->nrssislope = 0x00400000 / (nrssi0 - nrssi1); ++ if (nrssi0 >= -4) { ++ radio->nrssi[0] = nrssi1; ++ radio->nrssi[1] = nrssi0; ++ } ++ if (phy->rev >= 3) { ++ bcm43xx_phy_write(bcm, 0x002E, backup[10]); ++ bcm43xx_phy_write(bcm, 0x002F, backup[11]); ++ bcm43xx_phy_write(bcm, 0x080F, backup[12]); ++ bcm43xx_phy_write(bcm, BCM43xx_PHY_G_LO_CONTROL, backup[13]); ++ } ++ if (phy->rev >= 2) { ++ bcm43xx_phy_write(bcm, 0x0812, ++ bcm43xx_phy_read(bcm, 0x0812) & 0xFFCF); ++ bcm43xx_phy_write(bcm, 0x0811, ++ bcm43xx_phy_read(bcm, 0x0811) & 0xFFCF); ++ } ++ ++ bcm43xx_radio_write16(bcm, 0x007A, backup[0]); ++ bcm43xx_radio_write16(bcm, 0x0052, backup[1]); ++ bcm43xx_radio_write16(bcm, 0x0043, backup[2]); ++ bcm43xx_write16(bcm, 0x03E2, backup[7]); ++ bcm43xx_write16(bcm, 0x03E6, backup[8]); ++ bcm43xx_write16(bcm, BCM43xx_MMIO_CHANNEL_EXT, backup[9]); ++ bcm43xx_phy_write(bcm, 0x0015, backup[3]); ++ bcm43xx_phy_write(bcm, 0x005A, backup[4]); ++ bcm43xx_phy_write(bcm, 0x0059, backup[5]); ++ bcm43xx_phy_write(bcm, 0x0058, backup[6]); ++ bcm43xx_synth_pu_workaround(bcm, radio->channel); ++ bcm43xx_phy_write(bcm, 0x0802, ++ bcm43xx_phy_read(bcm, 0x0802) | (0x0001 | 0x0002)); ++ bcm43xx_set_original_gains(bcm); ++ bcm43xx_phy_write(bcm, BCM43xx_PHY_G_CRS, ++ bcm43xx_phy_read(bcm, BCM43xx_PHY_G_CRS) | 0x8000); ++ if (phy->rev >= 3) { ++ bcm43xx_phy_write(bcm, 0x0801, backup[14]); ++ bcm43xx_phy_write(bcm, 0x0060, backup[15]); ++ bcm43xx_phy_write(bcm, 0x0014, backup[16]); ++ bcm43xx_phy_write(bcm, 0x0478, backup[17]); ++ } ++ bcm43xx_nrssi_mem_update(bcm); ++ bcm43xx_calc_nrssi_threshold(bcm); ++ break; ++ default: ++ assert(0); ++ } ++} ++ ++void bcm43xx_calc_nrssi_threshold(struct bcm43xx_private *bcm) ++{ ++ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); ++ struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm); ++ s32 threshold; ++ s32 a, b; ++ s16 tmp16; ++ u16 tmp_u16; ++ ++ switch (phy->type) { ++ case BCM43xx_PHYTYPE_B: { ++ if (radio->version != 0x2050) ++ return; ++ if (!(bcm->sprom.boardflags & BCM43xx_BFL_RSSI)) ++ return; ++ ++ if (radio->revision >= 6) { ++ threshold = (radio->nrssi[1] - radio->nrssi[0]) * 32; ++ threshold += 20 * (radio->nrssi[0] + 1); ++ threshold /= 40; ++ } else ++ threshold = radio->nrssi[1] - 5; ++ ++ threshold = limit_value(threshold, 0, 0x3E); ++ bcm43xx_phy_read(bcm, 0x0020); /* dummy read */ ++ bcm43xx_phy_write(bcm, 0x0020, (((u16)threshold) << 8) | 0x001C); ++ ++ if (radio->revision >= 6) { ++ bcm43xx_phy_write(bcm, 0x0087, 0x0E0D); ++ bcm43xx_phy_write(bcm, 0x0086, 0x0C0B); ++ bcm43xx_phy_write(bcm, 0x0085, 0x0A09); ++ bcm43xx_phy_write(bcm, 0x0084, 0x0808); ++ bcm43xx_phy_write(bcm, 0x0083, 0x0808); ++ bcm43xx_phy_write(bcm, 0x0082, 0x0604); ++ bcm43xx_phy_write(bcm, 0x0081, 0x0302); ++ bcm43xx_phy_write(bcm, 0x0080, 0x0100); ++ } ++ break; ++ } ++ case BCM43xx_PHYTYPE_G: ++ if (!phy->connected || ++ !(bcm->sprom.boardflags & BCM43xx_BFL_RSSI)) { ++ tmp16 = bcm43xx_nrssi_hw_read(bcm, 0x20); ++ if (tmp16 >= 0x20) ++ tmp16 -= 0x40; ++ if (tmp16 < 3) { ++ bcm43xx_phy_write(bcm, 0x048A, ++ (bcm43xx_phy_read(bcm, 0x048A) ++ & 0xF000) | 0x09EB); ++ } else { ++ bcm43xx_phy_write(bcm, 0x048A, ++ (bcm43xx_phy_read(bcm, 0x048A) ++ & 0xF000) | 0x0AED); ++ } ++ } else { ++ if (radio->interfmode == BCM43xx_RADIO_INTERFMODE_NONWLAN) { ++ a = 0xE; ++ b = 0xA; ++ } else if (!radio->aci_wlan_automatic && radio->aci_enable) { ++ a = 0x13; ++ b = 0x12; ++ } else { ++ a = 0xE; ++ b = 0x11; ++ } ++ ++ a = a * (radio->nrssi[1] - radio->nrssi[0]); ++ a += (radio->nrssi[0] << 6); ++ if (a < 32) ++ a += 31; ++ else ++ a += 32; ++ a = a >> 6; ++ a = limit_value(a, -31, 31); ++ ++ b = b * (radio->nrssi[1] - radio->nrssi[0]); ++ b += (radio->nrssi[0] << 6); ++ if (b < 32) ++ b += 31; ++ else ++ b += 32; ++ b = b >> 6; ++ b = limit_value(b, -31, 31); ++ ++ tmp_u16 = bcm43xx_phy_read(bcm, 0x048A) & 0xF000; ++ tmp_u16 |= ((u32)b & 0x0000003F); ++ tmp_u16 |= (((u32)a & 0x0000003F) << 6); ++ bcm43xx_phy_write(bcm, 0x048A, tmp_u16); ++ } ++ break; ++ default: ++ assert(0); ++ } ++} ++ ++/* Stack implementation to save/restore values from the ++ * interference mitigation code. ++ * It is save to restore values in random order. ++ */ ++static void _stack_save(u32 *_stackptr, size_t *stackidx, ++ u8 id, u16 offset, u16 value) ++{ ++ u32 *stackptr = &(_stackptr[*stackidx]); ++ ++ assert((offset & 0xF000) == 0x0000); ++ assert((id & 0xF0) == 0x00); ++ *stackptr = offset; ++ *stackptr |= ((u32)id) << 12; ++ *stackptr |= ((u32)value) << 16; ++ (*stackidx)++; ++ assert(*stackidx < BCM43xx_INTERFSTACK_SIZE); ++} ++ ++static u16 _stack_restore(u32 *stackptr, ++ u8 id, u16 offset) ++{ ++ size_t i; ++ ++ assert((offset & 0xF000) == 0x0000); ++ assert((id & 0xF0) == 0x00); ++ for (i = 0; i < BCM43xx_INTERFSTACK_SIZE; i++, stackptr++) { ++ if ((*stackptr & 0x00000FFF) != offset) ++ continue; ++ if (((*stackptr & 0x0000F000) >> 12) != id) ++ continue; ++ return ((*stackptr & 0xFFFF0000) >> 16); ++ } ++ assert(0); ++ ++ return 0; ++} ++ ++#define phy_stacksave(offset) \ ++ do { \ ++ _stack_save(stack, &stackidx, 0x1, (offset), \ ++ bcm43xx_phy_read(bcm, (offset))); \ ++ } while (0) ++#define phy_stackrestore(offset) \ ++ do { \ ++ bcm43xx_phy_write(bcm, (offset), \ ++ _stack_restore(stack, 0x1, \ ++ (offset))); \ ++ } while (0) ++#define radio_stacksave(offset) \ ++ do { \ ++ _stack_save(stack, &stackidx, 0x2, (offset), \ ++ bcm43xx_radio_read16(bcm, (offset))); \ ++ } while (0) ++#define radio_stackrestore(offset) \ ++ do { \ ++ bcm43xx_radio_write16(bcm, (offset), \ ++ _stack_restore(stack, 0x2, \ ++ (offset))); \ ++ } while (0) ++#define ilt_stacksave(offset) \ ++ do { \ ++ _stack_save(stack, &stackidx, 0x3, (offset), \ ++ bcm43xx_ilt_read(bcm, (offset))); \ ++ } while (0) ++#define ilt_stackrestore(offset) \ ++ do { \ ++ bcm43xx_ilt_write(bcm, (offset), \ ++ _stack_restore(stack, 0x3, \ ++ (offset))); \ ++ } while (0) ++ ++static void ++bcm43xx_radio_interference_mitigation_enable(struct bcm43xx_private *bcm, ++ int mode) ++{ ++ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); ++ struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm); ++ u16 tmp, flipped; ++ u32 tmp32; ++ size_t stackidx = 0; ++ u32 *stack = radio->interfstack; ++ ++ switch (mode) { ++ case BCM43xx_RADIO_INTERFMODE_NONWLAN: ++ if (phy->rev != 1) { ++ bcm43xx_phy_write(bcm, 0x042B, ++ bcm43xx_phy_read(bcm, 0x042B) | 0x0800); ++ bcm43xx_phy_write(bcm, BCM43xx_PHY_G_CRS, ++ bcm43xx_phy_read(bcm, BCM43xx_PHY_G_CRS) & ~0x4000); ++ break; ++ } ++ radio_stacksave(0x0078); ++ tmp = (bcm43xx_radio_read16(bcm, 0x0078) & 0x001E); ++ flipped = flip_4bit(tmp); ++ if (flipped < 10 && flipped >= 8) ++ flipped = 7; ++ else if (flipped >= 10) ++ flipped -= 3; ++ flipped = flip_4bit(flipped); ++ flipped = (flipped << 1) | 0x0020; ++ bcm43xx_radio_write16(bcm, 0x0078, flipped); ++ ++ bcm43xx_calc_nrssi_threshold(bcm); ++ ++ phy_stacksave(0x0406); ++ bcm43xx_phy_write(bcm, 0x0406, 0x7E28); ++ ++ bcm43xx_phy_write(bcm, 0x042B, ++ bcm43xx_phy_read(bcm, 0x042B) | 0x0800); ++ bcm43xx_phy_write(bcm, BCM43xx_PHY_RADIO_BITFIELD, ++ bcm43xx_phy_read(bcm, BCM43xx_PHY_RADIO_BITFIELD) | 0x1000); ++ ++ phy_stacksave(0x04A0); ++ bcm43xx_phy_write(bcm, 0x04A0, ++ (bcm43xx_phy_read(bcm, 0x04A0) & 0xC0C0) | 0x0008); ++ phy_stacksave(0x04A1); ++ bcm43xx_phy_write(bcm, 0x04A1, ++ (bcm43xx_phy_read(bcm, 0x04A1) & 0xC0C0) | 0x0605); ++ phy_stacksave(0x04A2); ++ bcm43xx_phy_write(bcm, 0x04A2, ++ (bcm43xx_phy_read(bcm, 0x04A2) & 0xC0C0) | 0x0204); ++ phy_stacksave(0x04A8); ++ bcm43xx_phy_write(bcm, 0x04A8, ++ (bcm43xx_phy_read(bcm, 0x04A8) & 0xC0C0) | 0x0803); ++ phy_stacksave(0x04AB); ++ bcm43xx_phy_write(bcm, 0x04AB, ++ (bcm43xx_phy_read(bcm, 0x04AB) & 0xC0C0) | 0x0605); ++ ++ phy_stacksave(0x04A7); ++ bcm43xx_phy_write(bcm, 0x04A7, 0x0002); ++ phy_stacksave(0x04A3); ++ bcm43xx_phy_write(bcm, 0x04A3, 0x287A); ++ phy_stacksave(0x04A9); ++ bcm43xx_phy_write(bcm, 0x04A9, 0x2027); ++ phy_stacksave(0x0493); ++ bcm43xx_phy_write(bcm, 0x0493, 0x32F5); ++ phy_stacksave(0x04AA); ++ bcm43xx_phy_write(bcm, 0x04AA, 0x2027); ++ phy_stacksave(0x04AC); ++ bcm43xx_phy_write(bcm, 0x04AC, 0x32F5); ++ break; ++ case BCM43xx_RADIO_INTERFMODE_MANUALWLAN: ++ if (bcm43xx_phy_read(bcm, 0x0033) & 0x0800) ++ break; ++ ++ radio->aci_enable = 1; ++ ++ phy_stacksave(BCM43xx_PHY_RADIO_BITFIELD); ++ phy_stacksave(BCM43xx_PHY_G_CRS); ++ if (phy->rev < 2) { ++ phy_stacksave(0x0406); ++ } else { ++ phy_stacksave(0x04C0); ++ phy_stacksave(0x04C1); ++ } ++ phy_stacksave(0x0033); ++ phy_stacksave(0x04A7); ++ phy_stacksave(0x04A3); ++ phy_stacksave(0x04A9); ++ phy_stacksave(0x04AA); ++ phy_stacksave(0x04AC); ++ phy_stacksave(0x0493); ++ phy_stacksave(0x04A1); ++ phy_stacksave(0x04A0); ++ phy_stacksave(0x04A2); ++ phy_stacksave(0x048A); ++ phy_stacksave(0x04A8); ++ phy_stacksave(0x04AB); ++ if (phy->rev == 2) { ++ phy_stacksave(0x04AD); ++ phy_stacksave(0x04AE); ++ } else if (phy->rev >= 3) { ++ phy_stacksave(0x04AD); ++ phy_stacksave(0x0415); ++ phy_stacksave(0x0416); ++ phy_stacksave(0x0417); ++ ilt_stacksave(0x1A00 + 0x2); ++ ilt_stacksave(0x1A00 + 0x3); ++ } ++ phy_stacksave(0x042B); ++ phy_stacksave(0x048C); ++ ++ bcm43xx_phy_write(bcm, BCM43xx_PHY_RADIO_BITFIELD, ++ bcm43xx_phy_read(bcm, BCM43xx_PHY_RADIO_BITFIELD) ++ & ~0x1000); ++ bcm43xx_phy_write(bcm, BCM43xx_PHY_G_CRS, ++ (bcm43xx_phy_read(bcm, BCM43xx_PHY_G_CRS) ++ & 0xFFFC) | 0x0002); ++ ++ bcm43xx_phy_write(bcm, 0x0033, 0x0800); ++ bcm43xx_phy_write(bcm, 0x04A3, 0x2027); ++ bcm43xx_phy_write(bcm, 0x04A9, 0x1CA8); ++ bcm43xx_phy_write(bcm, 0x0493, 0x287A); ++ bcm43xx_phy_write(bcm, 0x04AA, 0x1CA8); ++ bcm43xx_phy_write(bcm, 0x04AC, 0x287A); ++ ++ bcm43xx_phy_write(bcm, 0x04A0, ++ (bcm43xx_phy_read(bcm, 0x04A0) ++ & 0xFFC0) | 0x001A); ++ bcm43xx_phy_write(bcm, 0x04A7, 0x000D); ++ ++ if (phy->rev < 2) { ++ bcm43xx_phy_write(bcm, 0x0406, 0xFF0D); ++ } else if (phy->rev == 2) { ++ bcm43xx_phy_write(bcm, 0x04C0, 0xFFFF); ++ bcm43xx_phy_write(bcm, 0x04C1, 0x00A9); ++ } else { ++ bcm43xx_phy_write(bcm, 0x04C0, 0x00C1); ++ bcm43xx_phy_write(bcm, 0x04C1, 0x0059); ++ } ++ ++ bcm43xx_phy_write(bcm, 0x04A1, ++ (bcm43xx_phy_read(bcm, 0x04A1) ++ & 0xC0FF) | 0x1800); ++ bcm43xx_phy_write(bcm, 0x04A1, ++ (bcm43xx_phy_read(bcm, 0x04A1) ++ & 0xFFC0) | 0x0015); ++ bcm43xx_phy_write(bcm, 0x04A8, ++ (bcm43xx_phy_read(bcm, 0x04A8) ++ & 0xCFFF) | 0x1000); ++ bcm43xx_phy_write(bcm, 0x04A8, ++ (bcm43xx_phy_read(bcm, 0x04A8) ++ & 0xF0FF) | 0x0A00); ++ bcm43xx_phy_write(bcm, 0x04AB, ++ (bcm43xx_phy_read(bcm, 0x04AB) ++ & 0xCFFF) | 0x1000); ++ bcm43xx_phy_write(bcm, 0x04AB, ++ (bcm43xx_phy_read(bcm, 0x04AB) ++ & 0xF0FF) | 0x0800); ++ bcm43xx_phy_write(bcm, 0x04AB, ++ (bcm43xx_phy_read(bcm, 0x04AB) ++ & 0xFFCF) | 0x0010); ++ bcm43xx_phy_write(bcm, 0x04AB, ++ (bcm43xx_phy_read(bcm, 0x04AB) ++ & 0xFFF0) | 0x0005); ++ bcm43xx_phy_write(bcm, 0x04A8, ++ (bcm43xx_phy_read(bcm, 0x04A8) ++ & 0xFFCF) | 0x0010); ++ bcm43xx_phy_write(bcm, 0x04A8, ++ (bcm43xx_phy_read(bcm, 0x04A8) ++ & 0xFFF0) | 0x0006); ++ bcm43xx_phy_write(bcm, 0x04A2, ++ (bcm43xx_phy_read(bcm, 0x04A2) ++ & 0xF0FF) | 0x0800); ++ bcm43xx_phy_write(bcm, 0x04A0, ++ (bcm43xx_phy_read(bcm, 0x04A0) ++ & 0xF0FF) | 0x0500); ++ bcm43xx_phy_write(bcm, 0x04A2, ++ (bcm43xx_phy_read(bcm, 0x04A2) ++ & 0xFFF0) | 0x000B); ++ ++ if (phy->rev >= 3) { ++ bcm43xx_phy_write(bcm, 0x048A, ++ bcm43xx_phy_read(bcm, 0x048A) ++ & ~0x8000); ++ bcm43xx_phy_write(bcm, 0x0415, ++ (bcm43xx_phy_read(bcm, 0x0415) ++ & 0x8000) | 0x36D8); ++ bcm43xx_phy_write(bcm, 0x0416, ++ (bcm43xx_phy_read(bcm, 0x0416) ++ & 0x8000) | 0x36D8); ++ bcm43xx_phy_write(bcm, 0x0417, ++ (bcm43xx_phy_read(bcm, 0x0417) ++ & 0xFE00) | 0x016D); ++ } else { ++ bcm43xx_phy_write(bcm, 0x048A, ++ bcm43xx_phy_read(bcm, 0x048A) ++ | 0x1000); ++ bcm43xx_phy_write(bcm, 0x048A, ++ (bcm43xx_phy_read(bcm, 0x048A) ++ & 0x9FFF) | 0x2000); ++ tmp32 = bcm43xx_shm_read32(bcm, BCM43xx_SHM_SHARED, ++ BCM43xx_UCODEFLAGS_OFFSET); ++ if (!(tmp32 & 0x800)) { ++ tmp32 |= 0x800; ++ bcm43xx_shm_write32(bcm, BCM43xx_SHM_SHARED, ++ BCM43xx_UCODEFLAGS_OFFSET, ++ tmp32); ++ } ++ } ++ if (phy->rev >= 2) { ++ bcm43xx_phy_write(bcm, 0x042B, ++ bcm43xx_phy_read(bcm, 0x042B) ++ | 0x0800); ++ } ++ bcm43xx_phy_write(bcm, 0x048C, ++ (bcm43xx_phy_read(bcm, 0x048C) ++ & 0xF0FF) | 0x0200); ++ if (phy->rev == 2) { ++ bcm43xx_phy_write(bcm, 0x04AE, ++ (bcm43xx_phy_read(bcm, 0x04AE) ++ & 0xFF00) | 0x007F); ++ bcm43xx_phy_write(bcm, 0x04AD, ++ (bcm43xx_phy_read(bcm, 0x04AD) ++ & 0x00FF) | 0x1300); ++ } else if (phy->rev >= 6) { ++ bcm43xx_ilt_write(bcm, 0x1A00 + 0x3, 0x007F); ++ bcm43xx_ilt_write(bcm, 0x1A00 + 0x2, 0x007F); ++ bcm43xx_phy_write(bcm, 0x04AD, ++ bcm43xx_phy_read(bcm, 0x04AD) ++ & 0x00FF); ++ } ++ bcm43xx_calc_nrssi_slope(bcm); ++ break; ++ default: ++ assert(0); ++ } ++} ++ ++static void ++bcm43xx_radio_interference_mitigation_disable(struct bcm43xx_private *bcm, ++ int mode) ++{ ++ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); ++ struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm); ++ u32 tmp32; ++ u32 *stack = radio->interfstack; ++ ++ switch (mode) { ++ case BCM43xx_RADIO_INTERFMODE_NONWLAN: ++ if (phy->rev != 1) { ++ bcm43xx_phy_write(bcm, 0x042B, ++ bcm43xx_phy_read(bcm, 0x042B) & ~0x0800); ++ bcm43xx_phy_write(bcm, BCM43xx_PHY_G_CRS, ++ bcm43xx_phy_read(bcm, BCM43xx_PHY_G_CRS) | 0x4000); ++ break; ++ } ++ phy_stackrestore(0x0078); ++ bcm43xx_calc_nrssi_threshold(bcm); ++ phy_stackrestore(0x0406); ++ bcm43xx_phy_write(bcm, 0x042B, ++ bcm43xx_phy_read(bcm, 0x042B) & ~0x0800); ++ if (!bcm->bad_frames_preempt) { ++ bcm43xx_phy_write(bcm, BCM43xx_PHY_RADIO_BITFIELD, ++ bcm43xx_phy_read(bcm, BCM43xx_PHY_RADIO_BITFIELD) ++ & ~(1 << 11)); ++ } ++ bcm43xx_phy_write(bcm, BCM43xx_PHY_G_CRS, ++ bcm43xx_phy_read(bcm, BCM43xx_PHY_G_CRS) | 0x4000); ++ phy_stackrestore(0x04A0); ++ phy_stackrestore(0x04A1); ++ phy_stackrestore(0x04A2); ++ phy_stackrestore(0x04A8); ++ phy_stackrestore(0x04AB); ++ phy_stackrestore(0x04A7); ++ phy_stackrestore(0x04A3); ++ phy_stackrestore(0x04A9); ++ phy_stackrestore(0x0493); ++ phy_stackrestore(0x04AA); ++ phy_stackrestore(0x04AC); ++ break; ++ case BCM43xx_RADIO_INTERFMODE_MANUALWLAN: ++ if (!(bcm43xx_phy_read(bcm, 0x0033) & 0x0800)) ++ break; ++ ++ radio->aci_enable = 0; ++ ++ phy_stackrestore(BCM43xx_PHY_RADIO_BITFIELD); ++ phy_stackrestore(BCM43xx_PHY_G_CRS); ++ phy_stackrestore(0x0033); ++ phy_stackrestore(0x04A3); ++ phy_stackrestore(0x04A9); ++ phy_stackrestore(0x0493); ++ phy_stackrestore(0x04AA); ++ phy_stackrestore(0x04AC); ++ phy_stackrestore(0x04A0); ++ phy_stackrestore(0x04A7); ++ if (phy->rev >= 2) { ++ phy_stackrestore(0x04C0); ++ phy_stackrestore(0x04C1); ++ } else ++ phy_stackrestore(0x0406); ++ phy_stackrestore(0x04A1); ++ phy_stackrestore(0x04AB); ++ phy_stackrestore(0x04A8); ++ if (phy->rev == 2) { ++ phy_stackrestore(0x04AD); ++ phy_stackrestore(0x04AE); ++ } else if (phy->rev >= 3) { ++ phy_stackrestore(0x04AD); ++ phy_stackrestore(0x0415); ++ phy_stackrestore(0x0416); ++ phy_stackrestore(0x0417); ++ ilt_stackrestore(0x1A00 + 0x2); ++ ilt_stackrestore(0x1A00 + 0x3); ++ } ++ phy_stackrestore(0x04A2); ++ phy_stackrestore(0x04A8); ++ phy_stackrestore(0x042B); ++ phy_stackrestore(0x048C); ++ tmp32 = bcm43xx_shm_read32(bcm, BCM43xx_SHM_SHARED, ++ BCM43xx_UCODEFLAGS_OFFSET); ++ if (tmp32 & 0x800) { ++ tmp32 &= ~0x800; ++ bcm43xx_shm_write32(bcm, BCM43xx_SHM_SHARED, ++ BCM43xx_UCODEFLAGS_OFFSET, ++ tmp32); ++ } ++ bcm43xx_calc_nrssi_slope(bcm); ++ break; ++ default: ++ assert(0); ++ } ++} ++ ++#undef phy_stacksave ++#undef phy_stackrestore ++#undef radio_stacksave ++#undef radio_stackrestore ++#undef ilt_stacksave ++#undef ilt_stackrestore ++ ++int bcm43xx_radio_set_interference_mitigation(struct bcm43xx_private *bcm, ++ int mode) ++{ ++ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); ++ struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm); ++ int currentmode; ++ ++ if ((phy->type != BCM43xx_PHYTYPE_G) || ++ (phy->rev == 0) || ++ (!phy->connected)) ++ return -ENODEV; ++ ++ radio->aci_wlan_automatic = 0; ++ switch (mode) { ++ case BCM43xx_RADIO_INTERFMODE_AUTOWLAN: ++ radio->aci_wlan_automatic = 1; ++ if (radio->aci_enable) ++ mode = BCM43xx_RADIO_INTERFMODE_MANUALWLAN; ++ else ++ mode = BCM43xx_RADIO_INTERFMODE_NONE; ++ break; ++ case BCM43xx_RADIO_INTERFMODE_NONE: ++ case BCM43xx_RADIO_INTERFMODE_NONWLAN: ++ case BCM43xx_RADIO_INTERFMODE_MANUALWLAN: ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ currentmode = radio->interfmode; ++ if (currentmode == mode) ++ return 0; ++ if (currentmode != BCM43xx_RADIO_INTERFMODE_NONE) ++ bcm43xx_radio_interference_mitigation_disable(bcm, currentmode); ++ ++ if (mode == BCM43xx_RADIO_INTERFMODE_NONE) { ++ radio->aci_enable = 0; ++ radio->aci_hw_rssi = 0; ++ } else ++ bcm43xx_radio_interference_mitigation_enable(bcm, mode); ++ radio->interfmode = mode; ++ ++ return 0; ++} ++ ++u16 bcm43xx_radio_calibrationvalue(struct bcm43xx_private *bcm) ++{ ++ u16 reg, index, ret; ++ ++ reg = bcm43xx_radio_read16(bcm, 0x0060); ++ index = (reg & 0x001E) >> 1; ++ ret = rcc_table[index] << 1; ++ ret |= (reg & 0x0001); ++ ret |= 0x0020; ++ ++ return ret; ++} ++ ++u16 bcm43xx_radio_init2050(struct bcm43xx_private *bcm) ++{ ++ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); ++ struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm); ++ u16 backup[19] = { 0 }; ++ u16 ret; ++ u16 i, j; ++ u32 tmp1 = 0, tmp2 = 0; ++ ++ backup[0] = bcm43xx_radio_read16(bcm, 0x0043); ++ backup[14] = bcm43xx_radio_read16(bcm, 0x0051); ++ backup[15] = bcm43xx_radio_read16(bcm, 0x0052); ++ backup[1] = bcm43xx_phy_read(bcm, 0x0015); ++ backup[16] = bcm43xx_phy_read(bcm, 0x005A); ++ backup[17] = bcm43xx_phy_read(bcm, 0x0059); ++ backup[18] = bcm43xx_phy_read(bcm, 0x0058); ++ if (phy->type == BCM43xx_PHYTYPE_B) { ++ backup[2] = bcm43xx_phy_read(bcm, 0x0030); ++ backup[3] = bcm43xx_read16(bcm, 0x03EC); ++ bcm43xx_phy_write(bcm, 0x0030, 0x00FF); ++ bcm43xx_write16(bcm, 0x03EC, 0x3F3F); ++ } else { ++ if (phy->connected) { ++ backup[4] = bcm43xx_phy_read(bcm, 0x0811); ++ backup[5] = bcm43xx_phy_read(bcm, 0x0812); ++ backup[6] = bcm43xx_phy_read(bcm, 0x0814); ++ backup[7] = bcm43xx_phy_read(bcm, 0x0815); ++ backup[8] = bcm43xx_phy_read(bcm, BCM43xx_PHY_G_CRS); ++ backup[9] = bcm43xx_phy_read(bcm, 0x0802); ++ bcm43xx_phy_write(bcm, 0x0814, ++ (bcm43xx_phy_read(bcm, 0x0814) | 0x0003)); ++ bcm43xx_phy_write(bcm, 0x0815, ++ (bcm43xx_phy_read(bcm, 0x0815) & 0xFFFC)); ++ bcm43xx_phy_write(bcm, BCM43xx_PHY_G_CRS, ++ (bcm43xx_phy_read(bcm, BCM43xx_PHY_G_CRS) & 0x7FFF)); ++ bcm43xx_phy_write(bcm, 0x0802, ++ (bcm43xx_phy_read(bcm, 0x0802) & 0xFFFC)); ++ bcm43xx_phy_write(bcm, 0x0811, 0x01B3); ++ bcm43xx_phy_write(bcm, 0x0812, 0x0FB2); ++ } ++ bcm43xx_write16(bcm, BCM43xx_MMIO_PHY_RADIO, ++ (bcm43xx_read16(bcm, BCM43xx_MMIO_PHY_RADIO) | 0x8000)); ++ } ++ backup[10] = bcm43xx_phy_read(bcm, 0x0035); ++ bcm43xx_phy_write(bcm, 0x0035, ++ (bcm43xx_phy_read(bcm, 0x0035) & 0xFF7F)); ++ backup[11] = bcm43xx_read16(bcm, 0x03E6); ++ backup[12] = bcm43xx_read16(bcm, BCM43xx_MMIO_CHANNEL_EXT); ++ ++ // Initialization ++ if (phy->version == 0) { ++ bcm43xx_write16(bcm, 0x03E6, 0x0122); ++ } else { ++ if (phy->version >= 2) ++ bcm43xx_write16(bcm, 0x03E6, 0x0040); ++ bcm43xx_write16(bcm, BCM43xx_MMIO_CHANNEL_EXT, ++ (bcm43xx_read16(bcm, BCM43xx_MMIO_CHANNEL_EXT) | 0x2000)); ++ } ++ ++ ret = bcm43xx_radio_calibrationvalue(bcm); ++ ++ if (phy->type == BCM43xx_PHYTYPE_B) ++ bcm43xx_radio_write16(bcm, 0x0078, 0x0003); ++ ++ bcm43xx_phy_write(bcm, 0x0015, 0xBFAF); ++ bcm43xx_phy_write(bcm, 0x002B, 0x1403); ++ if (phy->connected) ++ bcm43xx_phy_write(bcm, 0x0812, 0x00B2); ++ bcm43xx_phy_write(bcm, 0x0015, 0xBFA0); ++ bcm43xx_radio_write16(bcm, 0x0051, ++ (bcm43xx_radio_read16(bcm, 0x0051) | 0x0004)); ++ bcm43xx_radio_write16(bcm, 0x0052, 0x0000); ++ bcm43xx_radio_write16(bcm, 0x0043, ++ bcm43xx_radio_read16(bcm, 0x0043) | 0x0009); ++ bcm43xx_phy_write(bcm, 0x0058, 0x0000); ++ ++ for (i = 0; i < 16; i++) { ++ bcm43xx_phy_write(bcm, 0x005A, 0x0480); ++ bcm43xx_phy_write(bcm, 0x0059, 0xC810); ++ bcm43xx_phy_write(bcm, 0x0058, 0x000D); ++ if (phy->connected) ++ bcm43xx_phy_write(bcm, 0x0812, 0x30B2); ++ bcm43xx_phy_write(bcm, 0x0015, 0xAFB0); ++ udelay(10); ++ if (phy->connected) ++ bcm43xx_phy_write(bcm, 0x0812, 0x30B2); ++ bcm43xx_phy_write(bcm, 0x0015, 0xEFB0); ++ udelay(10); ++ if (phy->connected) ++ bcm43xx_phy_write(bcm, 0x0812, 0x30B2); ++ bcm43xx_phy_write(bcm, 0x0015, 0xFFF0); ++ udelay(10); ++ tmp1 += bcm43xx_phy_read(bcm, 0x002D); ++ bcm43xx_phy_write(bcm, 0x0058, 0x0000); ++ if (phy->connected) ++ bcm43xx_phy_write(bcm, 0x0812, 0x30B2); ++ bcm43xx_phy_write(bcm, 0x0015, 0xAFB0); ++ } ++ ++ tmp1++; ++ tmp1 >>= 9; ++ udelay(10); ++ bcm43xx_phy_write(bcm, 0x0058, 0x0000); ++ ++ for (i = 0; i < 16; i++) { ++ bcm43xx_radio_write16(bcm, 0x0078, (flip_4bit(i) << 1) | 0x0020); ++ backup[13] = bcm43xx_radio_read16(bcm, 0x0078); ++ udelay(10); ++ for (j = 0; j < 16; j++) { ++ bcm43xx_phy_write(bcm, 0x005A, 0x0D80); ++ bcm43xx_phy_write(bcm, 0x0059, 0xC810); ++ bcm43xx_phy_write(bcm, 0x0058, 0x000D); ++ if (phy->connected) ++ bcm43xx_phy_write(bcm, 0x0812, 0x30B2); ++ bcm43xx_phy_write(bcm, 0x0015, 0xAFB0); ++ udelay(10); ++ if (phy->connected) ++ bcm43xx_phy_write(bcm, 0x0812, 0x30B2); ++ bcm43xx_phy_write(bcm, 0x0015, 0xEFB0); ++ udelay(10); ++ if (phy->connected) ++ bcm43xx_phy_write(bcm, 0x0812, 0x30B3); /* 0x30B3 is not a typo */ ++ bcm43xx_phy_write(bcm, 0x0015, 0xFFF0); ++ udelay(10); ++ tmp2 += bcm43xx_phy_read(bcm, 0x002D); ++ bcm43xx_phy_write(bcm, 0x0058, 0x0000); ++ if (phy->connected) ++ bcm43xx_phy_write(bcm, 0x0812, 0x30B2); ++ bcm43xx_phy_write(bcm, 0x0015, 0xAFB0); ++ } ++ tmp2++; ++ tmp2 >>= 8; ++ if (tmp1 < tmp2) ++ break; ++ } ++ ++ /* Restore the registers */ ++ bcm43xx_phy_write(bcm, 0x0015, backup[1]); ++ bcm43xx_radio_write16(bcm, 0x0051, backup[14]); ++ bcm43xx_radio_write16(bcm, 0x0052, backup[15]); ++ bcm43xx_radio_write16(bcm, 0x0043, backup[0]); ++ bcm43xx_phy_write(bcm, 0x005A, backup[16]); ++ bcm43xx_phy_write(bcm, 0x0059, backup[17]); ++ bcm43xx_phy_write(bcm, 0x0058, backup[18]); ++ bcm43xx_write16(bcm, 0x03E6, backup[11]); ++ if (phy->version != 0) ++ bcm43xx_write16(bcm, BCM43xx_MMIO_CHANNEL_EXT, backup[12]); ++ bcm43xx_phy_write(bcm, 0x0035, backup[10]); ++ bcm43xx_radio_selectchannel(bcm, radio->channel, 1); ++ if (phy->type == BCM43xx_PHYTYPE_B) { ++ bcm43xx_phy_write(bcm, 0x0030, backup[2]); ++ bcm43xx_write16(bcm, 0x03EC, backup[3]); ++ } else { ++ bcm43xx_write16(bcm, BCM43xx_MMIO_PHY_RADIO, ++ (bcm43xx_read16(bcm, BCM43xx_MMIO_PHY_RADIO) & 0x7FFF)); ++ if (phy->connected) { ++ bcm43xx_phy_write(bcm, 0x0811, backup[4]); ++ bcm43xx_phy_write(bcm, 0x0812, backup[5]); ++ bcm43xx_phy_write(bcm, 0x0814, backup[6]); ++ bcm43xx_phy_write(bcm, 0x0815, backup[7]); ++ bcm43xx_phy_write(bcm, BCM43xx_PHY_G_CRS, backup[8]); ++ bcm43xx_phy_write(bcm, 0x0802, backup[9]); ++ } ++ } ++ if (i >= 15) ++ ret = backup[13]; ++ ++ return ret; ++} ++ ++void bcm43xx_radio_init2060(struct bcm43xx_private *bcm) ++{ ++ int err; ++ ++ bcm43xx_radio_write16(bcm, 0x0004, 0x00C0); ++ bcm43xx_radio_write16(bcm, 0x0005, 0x0008); ++ bcm43xx_radio_write16(bcm, 0x0009, 0x0040); ++ bcm43xx_radio_write16(bcm, 0x0005, 0x00AA); ++ bcm43xx_radio_write16(bcm, 0x0032, 0x008F); ++ bcm43xx_radio_write16(bcm, 0x0006, 0x008F); ++ bcm43xx_radio_write16(bcm, 0x0034, 0x008F); ++ bcm43xx_radio_write16(bcm, 0x002C, 0x0007); ++ bcm43xx_radio_write16(bcm, 0x0082, 0x0080); ++ bcm43xx_radio_write16(bcm, 0x0080, 0x0000); ++ bcm43xx_radio_write16(bcm, 0x003F, 0x00DA); ++ bcm43xx_radio_write16(bcm, 0x0005, bcm43xx_radio_read16(bcm, 0x0005) & ~0x0008); ++ bcm43xx_radio_write16(bcm, 0x0081, bcm43xx_radio_read16(bcm, 0x0081) & ~0x0010); ++ bcm43xx_radio_write16(bcm, 0x0081, bcm43xx_radio_read16(bcm, 0x0081) & ~0x0020); ++ bcm43xx_radio_write16(bcm, 0x0081, bcm43xx_radio_read16(bcm, 0x0081) & ~0x0020); ++ udelay(400); ++ ++ bcm43xx_radio_write16(bcm, 0x0081, (bcm43xx_radio_read16(bcm, 0x0081) & ~0x0020) | 0x0010); ++ udelay(400); ++ ++ bcm43xx_radio_write16(bcm, 0x0005, (bcm43xx_radio_read16(bcm, 0x0005) & ~0x0008) | 0x0008); ++ bcm43xx_radio_write16(bcm, 0x0085, bcm43xx_radio_read16(bcm, 0x0085) & ~0x0010); ++ bcm43xx_radio_write16(bcm, 0x0005, bcm43xx_radio_read16(bcm, 0x0005) & ~0x0008); ++ bcm43xx_radio_write16(bcm, 0x0081, bcm43xx_radio_read16(bcm, 0x0081) & ~0x0040); ++ bcm43xx_radio_write16(bcm, 0x0081, (bcm43xx_radio_read16(bcm, 0x0081) & ~0x0040) | 0x0040); ++ bcm43xx_radio_write16(bcm, 0x0005, (bcm43xx_radio_read16(bcm, 0x0081) & ~0x0008) | 0x0008); ++ bcm43xx_phy_write(bcm, 0x0063, 0xDDC6); ++ bcm43xx_phy_write(bcm, 0x0069, 0x07BE); ++ bcm43xx_phy_write(bcm, 0x006A, 0x0000); ++ ++ err = bcm43xx_radio_selectchannel(bcm, BCM43xx_RADIO_DEFAULT_CHANNEL_A, 0); ++ assert(err == 0); ++ udelay(1000); ++} ++ ++static inline ++u16 freq_r3A_value(u16 frequency) ++{ ++ u16 value; ++ ++ if (frequency < 5091) ++ value = 0x0040; ++ else if (frequency < 5321) ++ value = 0x0000; ++ else if (frequency < 5806) ++ value = 0x0080; ++ else ++ value = 0x0040; ++ ++ return value; ++} ++ ++void bcm43xx_radio_set_tx_iq(struct bcm43xx_private *bcm) ++{ ++ static const u8 data_high[5] = { 0x00, 0x40, 0x80, 0x90, 0xD0 }; ++ static const u8 data_low[5] = { 0x00, 0x01, 0x05, 0x06, 0x0A }; ++ u16 tmp = bcm43xx_radio_read16(bcm, 0x001E); ++ int i, j; ++ ++ for (i = 0; i < 5; i++) { ++ for (j = 0; j < 5; j++) { ++ if (tmp == (data_high[i] << 4 | data_low[j])) { ++ bcm43xx_phy_write(bcm, 0x0069, (i - j) << 8 | 0x00C0); ++ return; ++ } ++ } ++ } ++} ++ ++int bcm43xx_radio_selectchannel(struct bcm43xx_private *bcm, ++ u8 channel, ++ int synthetic_pu_workaround) ++{ ++ struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm); ++ u16 r8, tmp; ++ u16 freq; ++ ++ if ((radio->manufact == 0x17F) && ++ (radio->version == 0x2060) && ++ (radio->revision == 1)) { ++ if (channel > 200) ++ return -EINVAL; ++ freq = channel2freq_a(channel); ++ ++ r8 = bcm43xx_radio_read16(bcm, 0x0008); ++ bcm43xx_write16(bcm, 0x03F0, freq); ++ bcm43xx_radio_write16(bcm, 0x0008, r8); ++ ++ TODO();//TODO: write max channel TX power? to Radio 0x2D ++ tmp = bcm43xx_radio_read16(bcm, 0x002E); ++ tmp &= 0x0080; ++ TODO();//TODO: OR tmp with the Power out estimation for this channel? ++ bcm43xx_radio_write16(bcm, 0x002E, tmp); ++ ++ if (freq >= 4920 && freq <= 5500) { ++ /* ++ * r8 = (((freq * 15 * 0xE1FC780F) >> 32) / 29) & 0x0F; ++ * = (freq * 0.025862069 ++ */ ++ r8 = 3 * freq / 116; /* is equal to r8 = freq * 0.025862 */ ++ } ++ bcm43xx_radio_write16(bcm, 0x0007, (r8 << 4) | r8); ++ bcm43xx_radio_write16(bcm, 0x0020, (r8 << 4) | r8); ++ bcm43xx_radio_write16(bcm, 0x0021, (r8 << 4) | r8); ++ bcm43xx_radio_write16(bcm, 0x0022, ++ (bcm43xx_radio_read16(bcm, 0x0022) ++ & 0x000F) | (r8 << 4)); ++ bcm43xx_radio_write16(bcm, 0x002A, (r8 << 4)); ++ bcm43xx_radio_write16(bcm, 0x002B, (r8 << 4)); ++ bcm43xx_radio_write16(bcm, 0x0008, ++ (bcm43xx_radio_read16(bcm, 0x0008) ++ & 0x00F0) | (r8 << 4)); ++ bcm43xx_radio_write16(bcm, 0x0029, ++ (bcm43xx_radio_read16(bcm, 0x0029) ++ & 0xFF0F) | 0x00B0); ++ bcm43xx_radio_write16(bcm, 0x0035, 0x00AA); ++ bcm43xx_radio_write16(bcm, 0x0036, 0x0085); ++ bcm43xx_radio_write16(bcm, 0x003A, ++ (bcm43xx_radio_read16(bcm, 0x003A) ++ & 0xFF20) | freq_r3A_value(freq)); ++ bcm43xx_radio_write16(bcm, 0x003D, ++ bcm43xx_radio_read16(bcm, 0x003D) & 0x00FF); ++ bcm43xx_radio_write16(bcm, 0x0081, ++ (bcm43xx_radio_read16(bcm, 0x0081) ++ & 0xFF7F) | 0x0080); ++ bcm43xx_radio_write16(bcm, 0x0035, ++ bcm43xx_radio_read16(bcm, 0x0035) & 0xFFEF); ++ bcm43xx_radio_write16(bcm, 0x0035, ++ (bcm43xx_radio_read16(bcm, 0x0035) ++ & 0xFFEF) | 0x0010); ++ bcm43xx_radio_set_tx_iq(bcm); ++ TODO(); //TODO: TSSI2dbm workaround ++ bcm43xx_phy_xmitpower(bcm);//FIXME correct? ++ } else { ++ if ((channel < 1) || (channel > 14)) ++ return -EINVAL; ++ ++ if (synthetic_pu_workaround) ++ bcm43xx_synth_pu_workaround(bcm, channel); ++ ++ bcm43xx_write16(bcm, BCM43xx_MMIO_CHANNEL, ++ channel2freq_bg(channel)); ++ ++ if (channel == 14) { ++ if (bcm->sprom.locale == BCM43xx_LOCALE_JAPAN) { ++ bcm43xx_shm_write32(bcm, BCM43xx_SHM_SHARED, ++ BCM43xx_UCODEFLAGS_OFFSET, ++ bcm43xx_shm_read32(bcm, BCM43xx_SHM_SHARED, ++ BCM43xx_UCODEFLAGS_OFFSET) ++ & ~(1 << 7)); ++ } else { ++ bcm43xx_shm_write32(bcm, BCM43xx_SHM_SHARED, ++ BCM43xx_UCODEFLAGS_OFFSET, ++ bcm43xx_shm_read32(bcm, BCM43xx_SHM_SHARED, ++ BCM43xx_UCODEFLAGS_OFFSET) ++ | (1 << 7)); ++ } ++ bcm43xx_write16(bcm, BCM43xx_MMIO_CHANNEL_EXT, ++ bcm43xx_read16(bcm, BCM43xx_MMIO_CHANNEL_EXT) ++ | (1 << 11)); ++ } else { ++ bcm43xx_write16(bcm, BCM43xx_MMIO_CHANNEL_EXT, ++ bcm43xx_read16(bcm, BCM43xx_MMIO_CHANNEL_EXT) ++ & 0xF7BF); ++ } ++ } ++ ++ radio->channel = channel; ++ //XXX: Using the longer of 2 timeouts (8000 vs 2000 usecs). Specs states ++ // that 2000 usecs might suffice. ++ udelay(8000); ++ ++ return 0; ++} ++ ++void bcm43xx_radio_set_txantenna(struct bcm43xx_private *bcm, u32 val) ++{ ++ u16 tmp; ++ ++ val <<= 8; ++ tmp = bcm43xx_shm_read16(bcm, BCM43xx_SHM_SHARED, 0x0022) & 0xFCFF; ++ bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, 0x0022, tmp | val); ++ tmp = bcm43xx_shm_read16(bcm, BCM43xx_SHM_SHARED, 0x03A8) & 0xFCFF; ++ bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, 0x03A8, tmp | val); ++ tmp = bcm43xx_shm_read16(bcm, BCM43xx_SHM_SHARED, 0x0054) & 0xFCFF; ++ bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, 0x0054, tmp | val); ++} ++ ++/* http://bcm-specs.sipsolutions.net/TX_Gain_Base_Band */ ++static u16 bcm43xx_get_txgain_base_band(u16 txpower) ++{ ++ u16 ret; ++ ++ assert(txpower <= 63); ++ ++ if (txpower >= 54) ++ ret = 2; ++ else if (txpower >= 49) ++ ret = 4; ++ else if (txpower >= 44) ++ ret = 5; ++ else ++ ret = 6; ++ ++ return ret; ++} ++ ++/* http://bcm-specs.sipsolutions.net/TX_Gain_Radio_Frequency_Power_Amplifier */ ++static u16 bcm43xx_get_txgain_freq_power_amp(u16 txpower) ++{ ++ u16 ret; ++ ++ assert(txpower <= 63); ++ ++ if (txpower >= 32) ++ ret = 0; ++ else if (txpower >= 25) ++ ret = 1; ++ else if (txpower >= 20) ++ ret = 2; ++ else if (txpower >= 12) ++ ret = 3; ++ else ++ ret = 4; ++ ++ return ret; ++} ++ ++/* http://bcm-specs.sipsolutions.net/TX_Gain_Digital_Analog_Converter */ ++static u16 bcm43xx_get_txgain_dac(u16 txpower) ++{ ++ u16 ret; ++ ++ assert(txpower <= 63); ++ ++ if (txpower >= 54) ++ ret = txpower - 53; ++ else if (txpower >= 49) ++ ret = txpower - 42; ++ else if (txpower >= 44) ++ ret = txpower - 37; ++ else if (txpower >= 32) ++ ret = txpower - 32; ++ else if (txpower >= 25) ++ ret = txpower - 20; ++ else if (txpower >= 20) ++ ret = txpower - 13; ++ else if (txpower >= 12) ++ ret = txpower - 8; ++ else ++ ret = txpower; ++ ++ return ret; ++} ++ ++void bcm43xx_radio_set_txpower_a(struct bcm43xx_private *bcm, u16 txpower) ++{ ++ struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm); ++ u16 pamp, base, dac, ilt; ++ ++ txpower = limit_value(txpower, 0, 63); ++ ++ pamp = bcm43xx_get_txgain_freq_power_amp(txpower); ++ pamp <<= 5; ++ pamp &= 0x00E0; ++ bcm43xx_phy_write(bcm, 0x0019, pamp); ++ ++ base = bcm43xx_get_txgain_base_band(txpower); ++ base &= 0x000F; ++ bcm43xx_phy_write(bcm, 0x0017, base | 0x0020); ++ ++ ilt = bcm43xx_ilt_read(bcm, 0x3001); ++ ilt &= 0x0007; ++ ++ dac = bcm43xx_get_txgain_dac(txpower); ++ dac <<= 3; ++ dac |= ilt; ++ ++ bcm43xx_ilt_write(bcm, 0x3001, dac); ++ ++ radio->txpwr_offset = txpower; ++ ++ TODO(); ++ //TODO: FuncPlaceholder (Adjust BB loft cancel) ++} ++ ++void bcm43xx_radio_set_txpower_bg(struct bcm43xx_private *bcm, ++ u16 baseband_attenuation, u16 radio_attenuation, ++ u16 txpower) ++{ ++ struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm); ++ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); ++ ++ if (baseband_attenuation == 0xFFFF) ++ baseband_attenuation = radio->baseband_atten; ++ if (radio_attenuation == 0xFFFF) ++ radio_attenuation = radio->radio_atten; ++ if (txpower == 0xFFFF) ++ txpower = radio->txctl1; ++ radio->baseband_atten = baseband_attenuation; ++ radio->radio_atten = radio_attenuation; ++ radio->txctl1 = txpower; ++ ++ assert(/*baseband_attenuation >= 0 &&*/ baseband_attenuation <= 11); ++ if (radio->revision < 6) ++ assert(/*radio_attenuation >= 0 &&*/ radio_attenuation <= 9); ++ else ++ assert(/* radio_attenuation >= 0 &&*/ radio_attenuation <= 31); ++ assert(/*txpower >= 0 &&*/ txpower <= 7); ++ ++ bcm43xx_phy_set_baseband_attenuation(bcm, baseband_attenuation); ++ bcm43xx_radio_write16(bcm, 0x0043, radio_attenuation); ++ bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, 0x0064, radio_attenuation); ++ if (radio->version == 0x2050) { ++ bcm43xx_radio_write16(bcm, 0x0052, ++ (bcm43xx_radio_read16(bcm, 0x0052) & ~0x0070) ++ | ((txpower << 4) & 0x0070)); ++ } ++ //FIXME: The spec is very weird and unclear here. ++ if (phy->type == BCM43xx_PHYTYPE_G) ++ bcm43xx_phy_lo_adjust(bcm, 0); ++} ++ ++u16 bcm43xx_default_baseband_attenuation(struct bcm43xx_private *bcm) ++{ ++ struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm); ++ ++ if (radio->version == 0x2050 && radio->revision < 6) ++ return 0; ++ return 2; ++} ++ ++u16 bcm43xx_default_radio_attenuation(struct bcm43xx_private *bcm) ++{ ++ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); ++ struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm); ++ u16 att = 0xFFFF; ++ ++ if (phy->type == BCM43xx_PHYTYPE_A) ++ return 0x60; ++ ++ switch (radio->version) { ++ case 0x2053: ++ switch (radio->revision) { ++ case 1: ++ att = 6; ++ break; ++ } ++ break; ++ case 0x2050: ++ switch (radio->revision) { ++ case 0: ++ att = 5; ++ break; ++ case 1: ++ if (phy->type == BCM43xx_PHYTYPE_G) { ++ if (bcm->board_vendor == PCI_VENDOR_ID_BROADCOM && ++ bcm->board_type == 0x421 && ++ bcm->board_revision >= 30) ++ att = 3; ++ else if (bcm->board_vendor == PCI_VENDOR_ID_BROADCOM && ++ bcm->board_type == 0x416) ++ att = 3; ++ else ++ att = 1; ++ } else { ++ if (bcm->board_vendor == PCI_VENDOR_ID_BROADCOM && ++ bcm->board_type == 0x421 && ++ bcm->board_revision >= 30) ++ att = 7; ++ else ++ att = 6; ++ } ++ break; ++ case 2: ++ if (phy->type == BCM43xx_PHYTYPE_G) { ++ if (bcm->board_vendor == PCI_VENDOR_ID_BROADCOM && ++ bcm->board_type == 0x421 && ++ bcm->board_revision >= 30) ++ att = 3; ++ else if (bcm->board_vendor == PCI_VENDOR_ID_BROADCOM && ++ bcm->board_type == 0x416) ++ att = 5; ++ else if (bcm->chip_id == 0x4320) ++ att = 4; ++ else ++ att = 3; ++ } else ++ att = 6; ++ break; ++ case 3: ++ att = 5; ++ break; ++ case 4: ++ case 5: ++ att = 1; ++ break; ++ case 6: ++ case 7: ++ att = 5; ++ break; ++ case 8: ++ att = 0x1A; ++ break; ++ case 9: ++ default: ++ att = 5; ++ } ++ } ++ if (bcm->board_vendor == PCI_VENDOR_ID_BROADCOM && ++ bcm->board_type == 0x421) { ++ if (bcm->board_revision < 0x43) ++ att = 2; ++ else if (bcm->board_revision < 0x51) ++ att = 3; ++ } ++ if (att == 0xFFFF) ++ att = 5; ++ ++ return att; ++} ++ ++u16 bcm43xx_default_txctl1(struct bcm43xx_private *bcm) ++{ ++ struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm); ++ ++ if (radio->version != 0x2050) ++ return 0; ++ if (radio->revision == 1) ++ return 3; ++ if (radio->revision < 6) ++ return 2; ++ if (radio->revision == 8) ++ return 1; ++ return 0; ++} ++ ++void bcm43xx_radio_turn_on(struct bcm43xx_private *bcm) ++{ ++ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); ++ struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm); ++ int err; ++ ++ if (radio->enabled) ++ return; ++ ++ switch (phy->type) { ++ case BCM43xx_PHYTYPE_A: ++ bcm43xx_radio_write16(bcm, 0x0004, 0x00C0); ++ bcm43xx_radio_write16(bcm, 0x0005, 0x0008); ++ bcm43xx_phy_write(bcm, 0x0010, bcm43xx_phy_read(bcm, 0x0010) & 0xFFF7); ++ bcm43xx_phy_write(bcm, 0x0011, bcm43xx_phy_read(bcm, 0x0011) & 0xFFF7); ++ bcm43xx_radio_init2060(bcm); ++ break; ++ case BCM43xx_PHYTYPE_B: ++ case BCM43xx_PHYTYPE_G: ++ bcm43xx_phy_write(bcm, 0x0015, 0x8000); ++ bcm43xx_phy_write(bcm, 0x0015, 0xCC00); ++ bcm43xx_phy_write(bcm, 0x0015, (phy->connected ? 0x00C0 : 0x0000)); ++ err = bcm43xx_radio_selectchannel(bcm, BCM43xx_RADIO_DEFAULT_CHANNEL_BG, 1); ++ assert(err == 0); ++ break; ++ default: ++ assert(0); ++ } ++ radio->enabled = 1; ++ dprintk(KERN_INFO PFX "Radio turned on\n"); ++} ++ ++void bcm43xx_radio_turn_off(struct bcm43xx_private *bcm) ++{ ++ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); ++ struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm); ++ ++ if (phy->type == BCM43xx_PHYTYPE_A) { ++ bcm43xx_radio_write16(bcm, 0x0004, 0x00FF); ++ bcm43xx_radio_write16(bcm, 0x0005, 0x00FB); ++ bcm43xx_phy_write(bcm, 0x0010, bcm43xx_phy_read(bcm, 0x0010) | 0x0008); ++ bcm43xx_phy_write(bcm, 0x0011, bcm43xx_phy_read(bcm, 0x0011) | 0x0008); ++ } ++ if (phy->type == BCM43xx_PHYTYPE_G && bcm->current_core->rev >= 5) { ++ bcm43xx_phy_write(bcm, 0x0811, bcm43xx_phy_read(bcm, 0x0811) | 0x008C); ++ bcm43xx_phy_write(bcm, 0x0812, bcm43xx_phy_read(bcm, 0x0812) & 0xFF73); ++ } else ++ bcm43xx_phy_write(bcm, 0x0015, 0xAA00); ++ radio->enabled = 0; ++ dprintk(KERN_INFO PFX "Radio turned off\n"); ++} ++ ++void bcm43xx_radio_clear_tssi(struct bcm43xx_private *bcm) ++{ ++ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); ++ ++ switch (phy->type) { ++ case BCM43xx_PHYTYPE_A: ++ bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, 0x0068, 0x7F7F); ++ bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, 0x006a, 0x7F7F); ++ break; ++ case BCM43xx_PHYTYPE_B: ++ case BCM43xx_PHYTYPE_G: ++ bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, 0x0058, 0x7F7F); ++ bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, 0x005a, 0x7F7F); ++ bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, 0x0070, 0x7F7F); ++ bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, 0x0072, 0x7F7F); ++ break; ++ } ++} +diff -Nur linux-2.6.16/drivers/net/wireless/bcm43xx-d80211/bcm43xx_radio.h linux-2.6.16-bcm43xx/drivers/net/wireless/bcm43xx-d80211/bcm43xx_radio.h +--- linux-2.6.16/drivers/net/wireless/bcm43xx-d80211/bcm43xx_radio.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.16-bcm43xx/drivers/net/wireless/bcm43xx-d80211/bcm43xx_radio.h 2006-03-28 22:16:14.000000000 +0200 +@@ -0,0 +1,99 @@ ++/* ++ ++ Broadcom BCM43xx wireless driver ++ ++ Copyright (c) 2005 Martin Langer <martin-langer@gmx.de>, ++ Stefano Brivio <st3@riseup.net> ++ Michael Buesch <mbuesch@freenet.de> ++ Danny van Dyk <kugelfang@gentoo.org> ++ Andreas Jaggi <andreas.jaggi@waterwave.ch> ++ ++ Some parts of the code in this file are derived from the ipw2200 ++ driver Copyright(c) 2003 - 2004 Intel Corporation. ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; see the file COPYING. If not, write to ++ the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, ++ Boston, MA 02110-1301, USA. ++ ++*/ ++ ++#ifndef BCM43xx_RADIO_H_ ++#define BCM43xx_RADIO_H_ ++ ++#include "bcm43xx.h" ++ ++ ++#define BCM43xx_RADIO_DEFAULT_CHANNEL_A 36 ++#define BCM43xx_RADIO_DEFAULT_CHANNEL_BG 6 ++ ++/* Force antenna 0. */ ++#define BCM43xx_RADIO_TXANTENNA_0 0 ++/* Force antenna 1. */ ++#define BCM43xx_RADIO_TXANTENNA_1 1 ++/* Use the RX antenna, that was selected for the most recently ++ * received good PLCP header. ++ */ ++#define BCM43xx_RADIO_TXANTENNA_LASTPLCP 3 ++#define BCM43xx_RADIO_TXANTENNA_DEFAULT BCM43xx_RADIO_TXANTENNA_LASTPLCP ++ ++#define BCM43xx_RADIO_INTERFMODE_NONE 0 ++#define BCM43xx_RADIO_INTERFMODE_NONWLAN 1 ++#define BCM43xx_RADIO_INTERFMODE_MANUALWLAN 2 ++#define BCM43xx_RADIO_INTERFMODE_AUTOWLAN 3 ++ ++ ++void bcm43xx_radio_lock(struct bcm43xx_private *bcm); ++void bcm43xx_radio_unlock(struct bcm43xx_private *bcm); ++ ++u16 bcm43xx_radio_read16(struct bcm43xx_private *bcm, u16 offset); ++void bcm43xx_radio_write16(struct bcm43xx_private *bcm, u16 offset, u16 val); ++ ++u16 bcm43xx_radio_init2050(struct bcm43xx_private *bcm); ++void bcm43xx_radio_init2060(struct bcm43xx_private *bcm); ++ ++void bcm43xx_radio_turn_on(struct bcm43xx_private *bcm); ++void bcm43xx_radio_turn_off(struct bcm43xx_private *bcm); ++ ++int bcm43xx_radio_selectchannel(struct bcm43xx_private *bcm, u8 channel, ++ int synthetic_pu_workaround); ++ ++void bcm43xx_radio_set_txpower_a(struct bcm43xx_private *bcm, u16 txpower); ++void bcm43xx_radio_set_txpower_bg(struct bcm43xx_private *bcm, ++ u16 baseband_attenuation, u16 attenuation, ++ u16 txpower); ++ ++u16 bcm43xx_default_baseband_attenuation(struct bcm43xx_private *bcm); ++u16 bcm43xx_default_radio_attenuation(struct bcm43xx_private *bcm); ++u16 bcm43xx_default_txctl1(struct bcm43xx_private *bcm); ++ ++void bcm43xx_radio_set_txantenna(struct bcm43xx_private *bcm, u32 val); ++ ++void bcm43xx_radio_clear_tssi(struct bcm43xx_private *bcm); ++ ++u8 bcm43xx_radio_aci_detect(struct bcm43xx_private *bcm, u8 channel); ++u8 bcm43xx_radio_aci_scan(struct bcm43xx_private *bcm); ++ ++int bcm43xx_radio_set_interference_mitigation(struct bcm43xx_private *bcm, int mode); ++ ++void bcm43xx_calc_nrssi_slope(struct bcm43xx_private *bcm); ++void bcm43xx_calc_nrssi_threshold(struct bcm43xx_private *bcm); ++s16 bcm43xx_nrssi_hw_read(struct bcm43xx_private *bcm, u16 offset); ++void bcm43xx_nrssi_hw_write(struct bcm43xx_private *bcm, u16 offset, s16 val); ++void bcm43xx_nrssi_hw_update(struct bcm43xx_private *bcm, u16 val); ++void bcm43xx_nrssi_mem_update(struct bcm43xx_private *bcm); ++ ++void bcm43xx_radio_set_tx_iq(struct bcm43xx_private *bcm); ++u16 bcm43xx_radio_calibrationvalue(struct bcm43xx_private *bcm); ++ ++#endif /* BCM43xx_RADIO_H_ */ +diff -Nur linux-2.6.16/drivers/net/wireless/bcm43xx-d80211/bcm43xx_sysfs.c linux-2.6.16-bcm43xx/drivers/net/wireless/bcm43xx-d80211/bcm43xx_sysfs.c +--- linux-2.6.16/drivers/net/wireless/bcm43xx-d80211/bcm43xx_sysfs.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.16-bcm43xx/drivers/net/wireless/bcm43xx-d80211/bcm43xx_sysfs.c 2006-03-28 22:16:14.000000000 +0200 +@@ -0,0 +1,322 @@ ++/* ++ ++ Broadcom BCM43xx wireless driver ++ ++ SYSFS support routines ++ ++ Copyright (c) 2006 Michael Buesch <mbuesch@freenet.de> ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; see the file COPYING. If not, write to ++ the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, ++ Boston, MA 02110-1301, USA. ++ ++*/ ++ ++#include "bcm43xx_sysfs.h" ++#include "bcm43xx.h" ++#include "bcm43xx_main.h" ++#include "bcm43xx_radio.h" ++ ++#include <linux/capability.h> ++ ++ ++#define GENERIC_FILESIZE 64 ++ ++ ++static int get_integer(const char *buf, size_t count) ++{ ++ char tmp[10 + 1] = { 0 }; ++ int ret = -EINVAL; ++ ++ if (count == 0) ++ goto out; ++ count = min(count, (size_t)10); ++ memcpy(tmp, buf, count); ++ ret = simple_strtol(tmp, NULL, 10); ++out: ++ return ret; ++} ++ ++static int get_boolean(const char *buf, size_t count) ++{ ++ if (count != 0) { ++ if (buf[0] == '1') ++ return 1; ++ if (buf[0] == '0') ++ return 0; ++ if (count >= 4 && memcmp(buf, "true", 4) == 0) ++ return 1; ++ if (count >= 5 && memcmp(buf, "false", 5) == 0) ++ return 0; ++ if (count >= 3 && memcmp(buf, "yes", 3) == 0) ++ return 1; ++ if (count >= 2 && memcmp(buf, "no", 2) == 0) ++ return 0; ++ if (count >= 2 && memcmp(buf, "on", 2) == 0) ++ return 1; ++ if (count >= 3 && memcmp(buf, "off", 3) == 0) ++ return 0; ++ } ++ return -EINVAL; ++} ++ ++static ssize_t bcm43xx_attr_sprom_show(struct device *dev, ++ struct device_attribute *attr, ++ char *buf) ++{ ++ struct bcm43xx_private *bcm = devattr_to_bcm(attr, attr_sprom); ++ u16 *sprom; ++ unsigned long flags; ++ int i, err; ++ ++ if (!capable(CAP_NET_ADMIN)) ++ return -EPERM; ++ ++ assert(BCM43xx_SPROM_SIZE * sizeof(u16) <= PAGE_SIZE); ++ sprom = kmalloc(BCM43xx_SPROM_SIZE * sizeof(*sprom), ++ GFP_KERNEL); ++ if (!sprom) ++ return -ENOMEM; ++ bcm43xx_lock_mmio(bcm, flags); ++ assert(bcm->initialized); ++ err = bcm43xx_sprom_read(bcm, sprom); ++ if (!err) { ++ for (i = 0; i < BCM43xx_SPROM_SIZE; i++) { ++ buf[i * 2] = sprom[i] & 0x00FF; ++ buf[i * 2 + 1] = (sprom[i] & 0xFF00) >> 8; ++ } ++ } ++ bcm43xx_unlock_mmio(bcm, flags); ++ kfree(sprom); ++ ++ return err ? err : BCM43xx_SPROM_SIZE * sizeof(u16); ++} ++ ++static ssize_t bcm43xx_attr_sprom_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ struct bcm43xx_private *bcm = devattr_to_bcm(attr, attr_sprom); ++ u16 *sprom; ++ unsigned long flags; ++ int i, err; ++ ++ if (!capable(CAP_NET_ADMIN)) ++ return -EPERM; ++ ++ if (count != BCM43xx_SPROM_SIZE * sizeof(u16)) ++ return -EINVAL; ++ sprom = kmalloc(BCM43xx_SPROM_SIZE * sizeof(*sprom), ++ GFP_KERNEL); ++ if (!sprom) ++ return -ENOMEM; ++ for (i = 0; i < BCM43xx_SPROM_SIZE; i++) { ++ sprom[i] = buf[i * 2] & 0xFF; ++ sprom[i] |= ((u16)(buf[i * 2 + 1] & 0xFF)) << 8; ++ } ++ bcm43xx_lock_mmio(bcm, flags); ++ assert(bcm->initialized); ++ err = bcm43xx_sprom_write(bcm, sprom); ++ bcm43xx_unlock_mmio(bcm, flags); ++ kfree(sprom); ++ ++ return err ? err : count; ++ ++} ++ ++static ssize_t bcm43xx_attr_interfmode_show(struct device *dev, ++ struct device_attribute *attr, ++ char *buf) ++{ ++ struct bcm43xx_private *bcm = devattr_to_bcm(attr, attr_interfmode); ++ unsigned long flags; ++ int err; ++ ssize_t count = 0; ++ ++ if (!capable(CAP_NET_ADMIN)) ++ return -EPERM; ++ ++ bcm43xx_lock(bcm, flags); ++ assert(bcm->initialized); ++ ++ switch (bcm43xx_current_radio(bcm)->interfmode) { ++ case BCM43xx_RADIO_INTERFMODE_NONE: ++ count = snprintf(buf, PAGE_SIZE, "0 (No Interference Mitigation)\n"); ++ break; ++ case BCM43xx_RADIO_INTERFMODE_NONWLAN: ++ count = snprintf(buf, PAGE_SIZE, "1 (Non-WLAN Interference Mitigation)\n"); ++ break; ++ case BCM43xx_RADIO_INTERFMODE_MANUALWLAN: ++ count = snprintf(buf, PAGE_SIZE, "2 (WLAN Interference Mitigation)\n"); ++ break; ++ default: ++ assert(0); ++ } ++ err = 0; ++ ++ bcm43xx_unlock(bcm, flags); ++ ++ return err ? err : count; ++ ++} ++ ++static ssize_t bcm43xx_attr_interfmode_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ struct bcm43xx_private *bcm = devattr_to_bcm(attr, attr_interfmode); ++ unsigned long flags; ++ int err; ++ int mode; ++ ++ if (!capable(CAP_NET_ADMIN)) ++ return -EPERM; ++ ++ mode = get_integer(buf, count); ++ switch (mode) { ++ case 0: ++ mode = BCM43xx_RADIO_INTERFMODE_NONE; ++ break; ++ case 1: ++ mode = BCM43xx_RADIO_INTERFMODE_NONWLAN; ++ break; ++ case 2: ++ mode = BCM43xx_RADIO_INTERFMODE_MANUALWLAN; ++ break; ++ case 3: ++ mode = BCM43xx_RADIO_INTERFMODE_AUTOWLAN; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ bcm43xx_lock_mmio(bcm, flags); ++ assert(bcm->initialized); ++ ++ err = bcm43xx_radio_set_interference_mitigation(bcm, mode); ++ if (err) { ++ printk(KERN_ERR PFX "Interference Mitigation not " ++ "supported by device\n"); ++ } ++ ++ bcm43xx_unlock_mmio(bcm, flags); ++ ++ return err ? err : count; ++} ++ ++static ssize_t bcm43xx_attr_preamble_show(struct device *dev, ++ struct device_attribute *attr, ++ char *buf) ++{ ++ struct bcm43xx_private *bcm = devattr_to_bcm(attr, attr_preamble); ++ unsigned long flags; ++ int err; ++ ssize_t count; ++ ++ if (!capable(CAP_NET_ADMIN)) ++ return -EPERM; ++ ++ bcm43xx_lock(bcm, flags); ++ assert(bcm->initialized); ++ ++ if (bcm->short_preamble) ++ count = snprintf(buf, PAGE_SIZE, "1 (Short Preamble enabled)\n"); ++ else ++ count = snprintf(buf, PAGE_SIZE, "0 (Short Preamble disabled)\n"); ++ ++ err = 0; ++ bcm43xx_unlock(bcm, flags); ++ ++ return err ? err : count; ++} ++ ++static ssize_t bcm43xx_attr_preamble_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ struct bcm43xx_private *bcm = devattr_to_bcm(attr, attr_preamble); ++ unsigned long flags; ++ int err; ++ int value; ++ ++ if (!capable(CAP_NET_ADMIN)) ++ return -EPERM; ++ ++ value = get_boolean(buf, count); ++ if (value < 0) ++ return value; ++ bcm43xx_lock(bcm, flags); ++ assert(bcm->initialized); ++ ++ bcm->short_preamble = !!value; ++ ++ err = 0; ++ bcm43xx_unlock(bcm, flags); ++ ++ return err ? err : count; ++} ++ ++int bcm43xx_sysfs_register(struct bcm43xx_private *bcm) ++{ ++ struct device *dev = &bcm->pci_dev->dev; ++ struct bcm43xx_sysfs *sysfs = &bcm->sysfs; ++ int err; ++ ++ assert(bcm->initialized); ++ ++ sysfs->attr_sprom.attr.name = "sprom"; ++ sysfs->attr_sprom.attr.owner = THIS_MODULE; ++ sysfs->attr_sprom.attr.mode = 0600; ++ sysfs->attr_sprom.show = bcm43xx_attr_sprom_show; ++ sysfs->attr_sprom.store = bcm43xx_attr_sprom_store; ++ err = device_create_file(dev, &sysfs->attr_sprom); ++ if (err) ++ goto out; ++ ++ sysfs->attr_interfmode.attr.name = "interference"; ++ sysfs->attr_interfmode.attr.owner = THIS_MODULE; ++ sysfs->attr_interfmode.attr.mode = 0600; ++ sysfs->attr_interfmode.show = bcm43xx_attr_interfmode_show; ++ sysfs->attr_interfmode.store = bcm43xx_attr_interfmode_store; ++ err = device_create_file(dev, &sysfs->attr_interfmode); ++ if (err) ++ goto err_remove_sprom; ++ ++ sysfs->attr_preamble.attr.name = "shortpreamble"; ++ sysfs->attr_preamble.attr.owner = THIS_MODULE; ++ sysfs->attr_preamble.attr.mode = 0600; ++ sysfs->attr_preamble.show = bcm43xx_attr_preamble_show; ++ sysfs->attr_preamble.store = bcm43xx_attr_preamble_store; ++ err = device_create_file(dev, &sysfs->attr_preamble); ++ if (err) ++ goto err_remove_interfmode; ++ ++out: ++ return err; ++err_remove_interfmode: ++ device_remove_file(dev, &sysfs->attr_interfmode); ++err_remove_sprom: ++ device_remove_file(dev, &sysfs->attr_sprom); ++ goto out; ++} ++ ++void bcm43xx_sysfs_unregister(struct bcm43xx_private *bcm) ++{ ++ struct device *dev = &bcm->pci_dev->dev; ++ struct bcm43xx_sysfs *sysfs = &bcm->sysfs; ++ ++ device_remove_file(dev, &sysfs->attr_preamble); ++ device_remove_file(dev, &sysfs->attr_interfmode); ++ device_remove_file(dev, &sysfs->attr_sprom); ++} +diff -Nur linux-2.6.16/drivers/net/wireless/bcm43xx-d80211/bcm43xx_sysfs.h linux-2.6.16-bcm43xx/drivers/net/wireless/bcm43xx-d80211/bcm43xx_sysfs.h +--- linux-2.6.16/drivers/net/wireless/bcm43xx-d80211/bcm43xx_sysfs.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.16-bcm43xx/drivers/net/wireless/bcm43xx-d80211/bcm43xx_sysfs.h 2006-03-28 22:16:14.000000000 +0200 +@@ -0,0 +1,25 @@ ++#ifndef BCM43xx_SYSFS_H_ ++#define BCM43xx_SYSFS_H_ ++ ++#include <linux/device.h> ++ ++ ++struct bcm43xx_sysfs { ++ struct device_attribute attr_sprom; ++ struct device_attribute attr_interfmode; ++ struct device_attribute attr_preamble; ++}; ++ ++#define devattr_to_bcm(attr, attr_name) ({ \ ++ struct bcm43xx_sysfs *__s; struct bcm43xx_private *__p; \ ++ __s = container_of((attr), struct bcm43xx_sysfs, attr_name); \ ++ __p = container_of(__s, struct bcm43xx_private, sysfs); \ ++ __p; \ ++ }) ++ ++struct bcm43xx_private; ++ ++int bcm43xx_sysfs_register(struct bcm43xx_private *bcm); ++void bcm43xx_sysfs_unregister(struct bcm43xx_private *bcm); ++ ++#endif /* BCM43xx_SYSFS_H_ */ +diff -Nur linux-2.6.16/drivers/net/wireless/bcm43xx-d80211/bcm43xx_xmit.c linux-2.6.16-bcm43xx/drivers/net/wireless/bcm43xx-d80211/bcm43xx_xmit.c +--- linux-2.6.16/drivers/net/wireless/bcm43xx-d80211/bcm43xx_xmit.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.16-bcm43xx/drivers/net/wireless/bcm43xx-d80211/bcm43xx_xmit.c 2006-03-28 22:16:14.000000000 +0200 +@@ -0,0 +1,494 @@ ++/* ++ ++ Broadcom BCM43xx wireless driver ++ ++ Transmission (TX/RX) related functions. ++ ++ Copyright (c) 2005 Martin Langer <martin-langer@gmx.de>, ++ Stefano Brivio <st3@riseup.net> ++ Michael Buesch <mbuesch@freenet.de> ++ Danny van Dyk <kugelfang@gentoo.org> ++ Andreas Jaggi <andreas.jaggi@waterwave.ch> ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; see the file COPYING. If not, write to ++ the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, ++ Boston, MA 02110-1301, USA. ++ ++*/ ++ ++#include "bcm43xx_xmit.h" ++ ++ ++/* Extract the bitrate out of a CCK PLCP header. */ ++static u8 bcm43xx_plcp_get_bitrate_cck(struct bcm43xx_plcp_hdr4 *plcp) ++{ ++ switch (plcp->raw[0]) { ++ case 0x0A: ++ return BCM43xx_CCK_RATE_1MB; ++ case 0x14: ++ return BCM43xx_CCK_RATE_2MB; ++ case 0x37: ++ return BCM43xx_CCK_RATE_5MB; ++ case 0x6E: ++ return BCM43xx_CCK_RATE_11MB; ++ } ++ assert(0); ++ return 0; ++} ++ ++/* Extract the bitrate out of an OFDM PLCP header. */ ++static u8 bcm43xx_plcp_get_bitrate_ofdm(struct bcm43xx_plcp_hdr4 *plcp) ++{ ++ switch (plcp->raw[0] & 0xF) { ++ case 0xB: ++ return BCM43xx_OFDM_RATE_6MB; ++ case 0xF: ++ return BCM43xx_OFDM_RATE_9MB; ++ case 0xA: ++ return BCM43xx_OFDM_RATE_12MB; ++ case 0xE: ++ return BCM43xx_OFDM_RATE_18MB; ++ case 0x9: ++ return BCM43xx_OFDM_RATE_24MB; ++ case 0xD: ++ return BCM43xx_OFDM_RATE_36MB; ++ case 0x8: ++ return BCM43xx_OFDM_RATE_48MB; ++ case 0xC: ++ return BCM43xx_OFDM_RATE_54MB; ++ } ++ assert(0); ++ return 0; ++} ++ ++u8 bcm43xx_plcp_get_ratecode_cck(const u8 bitrate) ++{ ++ switch (bitrate) { ++ case BCM43xx_CCK_RATE_1MB: ++ return 0x0A; ++ case BCM43xx_CCK_RATE_2MB: ++ return 0x14; ++ case BCM43xx_CCK_RATE_5MB: ++ return 0x37; ++ case BCM43xx_CCK_RATE_11MB: ++ return 0x6E; ++ } ++ assert(0); ++ return 0; ++} ++ ++u8 bcm43xx_plcp_get_ratecode_ofdm(const u8 bitrate) ++{ ++ switch (bitrate) { ++ case BCM43xx_OFDM_RATE_6MB: ++ return 0xB; ++ case BCM43xx_OFDM_RATE_9MB: ++ return 0xF; ++ case BCM43xx_OFDM_RATE_12MB: ++ return 0xA; ++ case BCM43xx_OFDM_RATE_18MB: ++ return 0xE; ++ case BCM43xx_OFDM_RATE_24MB: ++ return 0x9; ++ case BCM43xx_OFDM_RATE_36MB: ++ return 0xD; ++ case BCM43xx_OFDM_RATE_48MB: ++ return 0x8; ++ case BCM43xx_OFDM_RATE_54MB: ++ return 0xC; ++ } ++ assert(0); ++ return 0; ++} ++ ++static void bcm43xx_generate_plcp_hdr(struct bcm43xx_plcp_hdr4 *plcp, ++ const u16 octets, const u8 bitrate, ++ const int ofdm_modulation) ++{ ++ __le32 *data = &(plcp->data); ++ __u8 *raw = plcp->raw; ++ ++ if (ofdm_modulation) { ++ *data = bcm43xx_plcp_get_ratecode_ofdm(bitrate); ++ assert(!(octets & 0xF000)); ++ *data |= (octets << 5); ++ *data = cpu_to_le32(*data); ++ } else { ++ u32 plen; ++ ++ plen = octets * 16 / bitrate; ++ if ((octets * 16 % bitrate) > 0) { ++ plen++; ++ if ((bitrate == BCM43xx_CCK_RATE_11MB) ++ && ((octets * 8 % 11) < 4)) { ++ raw[1] = 0x84; ++ } else ++ raw[1] = 0x04; ++ } else ++ raw[1] = 0x04; ++ *data |= cpu_to_le32(plen << 16); ++ raw[0] = bcm43xx_plcp_get_ratecode_cck(bitrate); ++ } ++} ++ ++static u8 bcm43xx_calc_fallback_rate(u8 bitrate) ++{ ++ switch (bitrate) { ++ case BCM43xx_CCK_RATE_1MB: ++ return BCM43xx_CCK_RATE_1MB; ++ case BCM43xx_CCK_RATE_2MB: ++ return BCM43xx_CCK_RATE_1MB; ++ case BCM43xx_CCK_RATE_5MB: ++ return BCM43xx_CCK_RATE_2MB; ++ case BCM43xx_CCK_RATE_11MB: ++ return BCM43xx_CCK_RATE_5MB; ++ case BCM43xx_OFDM_RATE_6MB: ++ return BCM43xx_CCK_RATE_5MB; ++ case BCM43xx_OFDM_RATE_9MB: ++ return BCM43xx_OFDM_RATE_6MB; ++ case BCM43xx_OFDM_RATE_12MB: ++ return BCM43xx_OFDM_RATE_9MB; ++ case BCM43xx_OFDM_RATE_18MB: ++ return BCM43xx_OFDM_RATE_12MB; ++ case BCM43xx_OFDM_RATE_24MB: ++ return BCM43xx_OFDM_RATE_18MB; ++ case BCM43xx_OFDM_RATE_36MB: ++ return BCM43xx_OFDM_RATE_24MB; ++ case BCM43xx_OFDM_RATE_48MB: ++ return BCM43xx_OFDM_RATE_36MB; ++ case BCM43xx_OFDM_RATE_54MB: ++ return BCM43xx_OFDM_RATE_48MB; ++ } ++ assert(0); ++ return 0; ++} ++ ++static ++__le16 bcm43xx_calc_duration_id(const struct ieee80211_hdr *wireless_header, ++ u8 bitrate) ++{ ++ const u16 frame_ctl = le16_to_cpu(wireless_header->frame_control); ++ __le16 duration_id = wireless_header->duration_id; ++ ++ switch (WLAN_FC_GET_TYPE(frame_ctl)) { ++ case WLAN_FC_TYPE_DATA: ++ case WLAN_FC_TYPE_MGMT: ++ //TODO: Steal the code from ieee80211, once it is completed there. ++ break; ++ case WLAN_FC_TYPE_CTRL: ++ /* Use the original duration/id. */ ++ break; ++ default: ++ assert(0); ++ } ++ ++ return duration_id; ++} ++ ++static inline ++u16 ceiling_div(u16 dividend, u16 divisor) ++{ ++ return ((dividend + divisor - 1) / divisor); ++} ++ ++static void bcm43xx_generate_rts(const struct bcm43xx_phyinfo *phy, ++ struct bcm43xx_txhdr *txhdr, ++ u16 *flags, ++ u8 bitrate, ++ const struct ieee80211_hdr *wlhdr) ++{ ++ u16 fctl; ++ u16 dur; ++ u8 fallback_bitrate; ++ int ofdm_modulation; ++ int fallback_ofdm_modulation; ++ u8 *sa, *da; ++ u16 flen; ++ ++ sa = ieee80211_get_SA((struct ieee80211_hdr *)wlhdr); ++ da = ieee80211_get_DA((struct ieee80211_hdr *)wlhdr); ++ fallback_bitrate = bcm43xx_calc_fallback_rate(bitrate); ++ ofdm_modulation = !(bcm43xx_is_cck_rate(bitrate)); ++ fallback_ofdm_modulation = !(bcm43xx_is_cck_rate(fallback_bitrate)); ++ ++ flen = sizeof(u16) + sizeof(u16) + ETH_ALEN + ETH_ALEN + FCS_LEN, ++ bcm43xx_generate_plcp_hdr((struct bcm43xx_plcp_hdr4 *)(&txhdr->rts_cts_plcp), ++ flen, bitrate, ++ !bcm43xx_is_cck_rate(bitrate)); ++ bcm43xx_generate_plcp_hdr((struct bcm43xx_plcp_hdr4 *)(&txhdr->rts_cts_fallback_plcp), ++ flen, fallback_bitrate, ++ !bcm43xx_is_cck_rate(fallback_bitrate)); ++ fctl = WLAN_FC_TYPE_CTRL << 2; ++ fctl |= WLAN_FC_STYPE_RTS << 4; ++ dur = le16_to_cpu(wlhdr->duration_id); ++/*FIXME: should we test for dur==0 here and let it unmodified in this case? ++ * The following assert checks for this case... ++ */ ++assert(dur); ++/*FIXME: The duration calculation is not really correct. ++ * I am not 100% sure which bitrate to use. We use the RTS rate here, ++ * but this is likely to be wrong. ++ */ ++ if (phy->type == BCM43xx_PHYTYPE_A) { ++ /* Three times SIFS */ ++ dur += 16 * 3; ++ /* Add ACK duration. */ ++ dur += ceiling_div((16 + 8 * (14 /*bytes*/) + 6) * 10, ++ bitrate * 4); ++ /* Add CTS duration. */ ++ dur += ceiling_div((16 + 8 * (14 /*bytes*/) + 6) * 10, ++ bitrate * 4); ++ } else { ++ /* Three times SIFS */ ++ dur += 10 * 3; ++ /* Add ACK duration. */ ++ dur += ceiling_div(8 * (14 /*bytes*/) * 10, ++ bitrate); ++ /* Add CTS duration. */ ++ dur += ceiling_div(8 * (14 /*bytes*/) * 10, ++ bitrate); ++ } ++ ++ txhdr->rts_cts_frame_control = cpu_to_le16(fctl); ++ txhdr->rts_cts_dur = cpu_to_le16(dur); ++//printk(BCM43xx_MACFMT " " BCM43xx_MACFMT " " BCM43xx_MACFMT "\n", BCM43xx_MACARG(wlhdr->addr1), BCM43xx_MACARG(wlhdr->addr2), BCM43xx_MACARG(wlhdr->addr3)); ++//printk(BCM43xx_MACFMT " " BCM43xx_MACFMT "\n", BCM43xx_MACARG(sa), BCM43xx_MACARG(da)); ++ memcpy(txhdr->rts_cts_mac1, wlhdr->addr1, ETH_ALEN);//FIXME! ++ memcpy(txhdr->rts_cts_mac2, sa, ETH_ALEN); ++ ++ *flags |= BCM43xx_TXHDRFLAG_RTSCTS; ++ *flags |= BCM43xx_TXHDRFLAG_RTS; ++ if (ofdm_modulation) ++ *flags |= BCM43xx_TXHDRFLAG_RTSCTS_OFDM; ++ if (fallback_ofdm_modulation) ++ *flags |= BCM43xx_TXHDRFLAG_RTSCTSFALLBACK_OFDM; ++} ++ ++void bcm43xx_generate_txhdr(struct bcm43xx_private *bcm, ++ struct bcm43xx_txhdr *txhdr, ++ const unsigned char *fragment_data, ++ const unsigned int fragment_len, ++ const int is_first_fragment, ++ const u16 cookie, ++ struct ieee80211_tx_control *txctl) ++{ ++ const struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); ++ const struct ieee80211_hdr *wireless_header = (const struct ieee80211_hdr *)fragment_data; ++ const int use_encryption = (!txctl->do_not_encrypt && txctl->key_idx >= 0); ++ u8 bitrate; ++ u8 fallback_bitrate; ++ int ofdm_modulation; ++ int fallback_ofdm_modulation; ++ u16 plcp_fragment_len = fragment_len; ++ u16 flags = 0; ++ u16 control = 0; ++ u16 wsec_rate = 0; ++ ++ /* Now construct the TX header. */ ++ memset(txhdr, 0, sizeof(*txhdr)); ++ ++ bitrate = txctl->tx_rate; ++ ofdm_modulation = !(bcm43xx_is_cck_rate(bitrate)); ++ fallback_bitrate = bcm43xx_calc_fallback_rate(bitrate); ++ fallback_ofdm_modulation = !(bcm43xx_is_cck_rate(fallback_bitrate)); ++ ++ /* Set Frame Control from 80211 header. */ ++ txhdr->frame_control = wireless_header->frame_control; ++ /* Copy address1 from 80211 header. */ ++ memcpy(txhdr->mac1, wireless_header->addr1, 6); ++ /* Set the fallback duration ID. */ ++ txhdr->fallback_dur_id = bcm43xx_calc_duration_id(wireless_header, ++ fallback_bitrate); ++ /* Set the cookie (used as driver internal ID for the frame) */ ++ txhdr->cookie = cpu_to_le16(cookie); ++ ++ /* Hardware appends FCS. */ ++ plcp_fragment_len += FCS_LEN; ++ if (use_encryption) { ++ u16 key_idx = (u16)(txctl->key_idx); ++ struct bcm43xx_key *key = &(bcm->key[key_idx]); ++ int wlhdr_len; ++ ++ if (key->enabled) { ++ /* Hardware appends ICV. */ ++ plcp_fragment_len += txctl->icv_len; ++ ++ wsec_rate |= ((key_idx & 0x000F) << 4); ++ wsec_rate |= key->algorithm; ++ wlhdr_len = ieee80211_get_hdrlen(le16_to_cpu(wireless_header->frame_control)); ++ memcpy(txhdr->wep_iv, ((u8 *)wireless_header) + wlhdr_len, 4); ++ } ++ } ++ /* Generate the PLCP header and the fallback PLCP header. */ ++ bcm43xx_generate_plcp_hdr((struct bcm43xx_plcp_hdr4 *)(&txhdr->plcp), ++ plcp_fragment_len, ++ bitrate, ofdm_modulation); ++ bcm43xx_generate_plcp_hdr(&txhdr->fallback_plcp, plcp_fragment_len, ++ fallback_bitrate, fallback_ofdm_modulation); ++ ++ /* Set the CONTROL field */ ++ if (ofdm_modulation) ++ control |= BCM43xx_TXHDRCTL_OFDM; ++ if (bcm->short_preamble) //FIXME: could be the other way around, please test ++ control |= BCM43xx_TXHDRCTL_SHORT_PREAMBLE; ++ control |= (phy->antenna_diversity << BCM43xx_TXHDRCTL_ANTENNADIV_SHIFT) ++ & BCM43xx_TXHDRCTL_ANTENNADIV_MASK; ++ ++ /* Set the FLAGS field */ ++ if (!txctl->no_ack) ++ flags |= BCM43xx_TXHDRFLAG_EXPECTACK; ++ if (1 /* FIXME: PS poll?? */) ++ flags |= 0x10; // FIXME: unknown meaning. ++ if (fallback_ofdm_modulation) ++ flags |= BCM43xx_TXHDRFLAG_FALLBACKOFDM; ++ if (is_first_fragment) ++ flags |= BCM43xx_TXHDRFLAG_FIRSTFRAGMENT; ++ ++ /* Set WSEC/RATE field */ ++ wsec_rate |= (txhdr->plcp.raw[0] << BCM43xx_TXHDR_RATE_SHIFT) ++ & BCM43xx_TXHDR_RATE_MASK; ++ ++ /* Generate the RTS/CTS packet, if required. */ ++ /* FIXME: We should first try with CTS-to-self, ++ * if we are on 80211g. If we get too many ++ * failures (hidden nodes), we should switch back to RTS/CTS. ++ */ ++ if (txctl->use_rts_cts) { ++ bcm43xx_generate_rts(phy, txhdr, &flags, ++ txctl->rts_cts_rate, ++ wireless_header); ++ } ++ ++ txhdr->flags = cpu_to_le16(flags); ++ txhdr->control = cpu_to_le16(control); ++ txhdr->wsec_rate = cpu_to_le16(wsec_rate); ++} ++ ++static s8 bcm43xx_rssi_postprocess(struct bcm43xx_private *bcm, ++ u8 in_rssi, int ofdm, ++ int adjust_2053, int adjust_2050) ++{ ++ struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm); ++ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); ++ s32 tmp; ++ ++ switch (radio->version) { ++ case 0x2050: ++ if (ofdm) { ++ tmp = in_rssi; ++ if (tmp > 127) ++ tmp -= 256; ++ tmp *= 73; ++ tmp /= 64; ++ if (adjust_2050) ++ tmp += 25; ++ else ++ tmp -= 3; ++ } else { ++ if (bcm->sprom.boardflags & BCM43xx_BFL_RSSI) { ++ if (in_rssi > 63) ++ in_rssi = 63; ++ tmp = radio->nrssi_lt[in_rssi]; ++ tmp = 31 - tmp; ++ tmp *= -131; ++ tmp /= 128; ++ tmp -= 57; ++ } else { ++ tmp = in_rssi; ++ tmp = 31 - tmp; ++ tmp *= -149; ++ tmp /= 128; ++ tmp -= 68; ++ } ++ if (phy->type == BCM43xx_PHYTYPE_G && ++ adjust_2050) ++ tmp += 25; ++ } ++ break; ++ case 0x2060: ++ if (in_rssi > 127) ++ tmp = in_rssi - 256; ++ else ++ tmp = in_rssi; ++ break; ++ default: ++ tmp = in_rssi; ++ tmp -= 11; ++ tmp *= 103; ++ tmp /= 64; ++ if (adjust_2053) ++ tmp -= 109; ++ else ++ tmp -= 83; ++ } ++ ++ return (s8)tmp; ++} ++ ++//TODO ++#if 0 ++static s8 bcm43xx_rssinoise_postprocess(struct bcm43xx_private *bcm, ++ u8 in_rssi) ++{ ++ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); ++ s8 ret; ++ ++ if (phy->type == BCM43xx_PHYTYPE_A) { ++ //TODO: Incomplete specs. ++ ret = 0; ++ } else ++ ret = bcm43xx_rssi_postprocess(bcm, in_rssi, 0, 1, 1); ++ ++ return ret; ++} ++#endif ++ ++void bcm43xx_rx(struct bcm43xx_private *bcm, ++ struct sk_buff *skb, ++ struct bcm43xx_rxhdr *rxhdr) ++{ ++ struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm); ++ struct bcm43xx_plcp_hdr4 *plcp; ++ struct ieee80211_rx_status status; ++ const u16 rxflags1 = le16_to_cpu(rxhdr->flags1); ++ const u16 rxflags2 = le16_to_cpu(rxhdr->flags2); ++ const u16 rxflags3 = le16_to_cpu(rxhdr->flags3); ++ const int is_ofdm = !!(rxflags1 & BCM43xx_RXHDR_FLAGS1_OFDM); ++ ++ if (rxflags2 & BCM43xx_RXHDR_FLAGS2_TYPE2FRAME) { ++ plcp = (struct bcm43xx_plcp_hdr4 *)(skb->data + 2); ++ /* Skip two unknown bytes and the PLCP header. */ ++ skb_pull(skb, 2 + sizeof(struct bcm43xx_plcp_hdr6)); ++ } else { ++ plcp = (struct bcm43xx_plcp_hdr4 *)(skb->data); ++ /* Skip the PLCP header. */ ++ skb_pull(skb, sizeof(struct bcm43xx_plcp_hdr6)); ++ } ++ /* The SKB contains the PAYLOAD (wireless header + data) ++ * at this point. ++ */ ++ ++ memset(&status, 0, sizeof(status)); ++ status.ssi = bcm43xx_rssi_postprocess(bcm, rxhdr->rssi, is_ofdm, ++ !!(rxflags1 & BCM43xx_RXHDR_FLAGS1_2053RSSIADJ), ++ !!(rxflags3 & BCM43xx_RXHDR_FLAGS3_2050RSSIADJ)); ++ if (is_ofdm) ++ status.rate = bcm43xx_plcp_get_bitrate_ofdm(plcp); ++ else ++ status.rate = bcm43xx_plcp_get_bitrate_cck(plcp); ++ status.channel = radio->channel; ++ ++ bcm->stats.last_rx = jiffies; ++ ieee80211_rx_irqsafe(bcm->net_dev, skb, &status); ++} +diff -Nur linux-2.6.16/drivers/net/wireless/bcm43xx-d80211/bcm43xx_xmit.h linux-2.6.16-bcm43xx/drivers/net/wireless/bcm43xx-d80211/bcm43xx_xmit.h +--- linux-2.6.16/drivers/net/wireless/bcm43xx-d80211/bcm43xx_xmit.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.16-bcm43xx/drivers/net/wireless/bcm43xx-d80211/bcm43xx_xmit.h 2006-03-28 22:16:14.000000000 +0200 +@@ -0,0 +1,157 @@ ++#ifndef BCM43xx_XMIT_H_ ++#define BCM43xx_XMIT_H_ ++ ++#include "bcm43xx_main.h" ++ ++ ++#define _bcm43xx_declare_plcp_hdr(size) \ ++ struct bcm43xx_plcp_hdr##size { \ ++ union { \ ++ __le32 data; \ ++ __u8 raw[size]; \ ++ } __attribute__((__packed__)); \ ++ } __attribute__((__packed__)) ++ ++/* struct bcm43xx_plcp_hdr4 */ ++_bcm43xx_declare_plcp_hdr(4); ++/* struct bcm43xx_plcp_hdr6 */ ++_bcm43xx_declare_plcp_hdr(6); ++ ++#undef _bcm43xx_declare_plcp_hdr ++ ++/* Device specific TX header. To be prepended to TX frames. */ ++struct bcm43xx_txhdr { ++ union { ++ struct { ++ __le16 flags; ++ __le16 wsec_rate; ++ __le16 frame_control; ++ u16 unknown_zeroed_0; ++ __le16 control; ++ u8 wep_iv[10]; ++ u8 unknown_wsec_tkip_data[3]; //FIXME ++ PAD_BYTES(3); ++ u8 mac1[6]; ++ u16 unknown_zeroed_1; ++ struct bcm43xx_plcp_hdr4 rts_cts_fallback_plcp; ++ __le16 rts_cts_dur_fallback; ++ struct bcm43xx_plcp_hdr4 fallback_plcp; ++ __le16 fallback_dur_id; ++ PAD_BYTES(2); ++ __le16 cookie; ++ __le16 unknown_scb_stuff; //FIXME ++ struct bcm43xx_plcp_hdr6 rts_cts_plcp; ++ __le16 rts_cts_frame_control; ++ __le16 rts_cts_dur; ++ u8 rts_cts_mac1[6]; ++ u8 rts_cts_mac2[6]; ++ PAD_BYTES(2); ++ struct bcm43xx_plcp_hdr6 plcp; ++ } __attribute__((__packed__)); ++ u8 raw[82]; ++ } __attribute__((__packed__)); ++} __attribute__((__packed__)); ++ ++/* Values/Masks for the device TX header */ ++#define BCM43xx_TXHDRFLAG_EXPECTACK 0x0001 ++#define BCM43xx_TXHDRFLAG_RTSCTS 0x0002 ++#define BCM43xx_TXHDRFLAG_RTS 0x0004 ++#define BCM43xx_TXHDRFLAG_FIRSTFRAGMENT 0x0008 ++#define BCM43xx_TXHDRFLAG_DESTPSMODE 0x0020 ++#define BCM43xx_TXHDRFLAG_RTSCTS_OFDM 0x0080 ++#define BCM43xx_TXHDRFLAG_FALLBACKOFDM 0x0100 ++#define BCM43xx_TXHDRFLAG_RTSCTSFALLBACK_OFDM 0x0200 ++#define BCM43xx_TXHDRFLAG_CTS 0x0400 ++#define BCM43xx_TXHDRFLAG_FRAMEBURST 0x0800 ++ ++#define BCM43xx_TXHDRCTL_OFDM 0x0001 ++#define BCM43xx_TXHDRCTL_SHORT_PREAMBLE 0x0010 ++#define BCM43xx_TXHDRCTL_ANTENNADIV_MASK 0x0030 ++#define BCM43xx_TXHDRCTL_ANTENNADIV_SHIFT 8 ++ ++#define BCM43xx_TXHDR_RATE_MASK 0x0F00 ++#define BCM43xx_TXHDR_RATE_SHIFT 8 ++#define BCM43xx_TXHDR_RTSRATE_MASK 0xF000 ++#define BCM43xx_TXHDR_RTSRATE_SHIFT 12 ++#define BCM43xx_TXHDR_WSEC_KEYINDEX_MASK 0x00F0 ++#define BCM43xx_TXHDR_WSEC_KEYINDEX_SHIFT 4 ++#define BCM43xx_TXHDR_WSEC_ALGO_MASK 0x0003 ++#define BCM43xx_TXHDR_WSEC_ALGO_SHIFT 0 ++ ++void bcm43xx_generate_txhdr(struct bcm43xx_private *bcm, ++ struct bcm43xx_txhdr *txhdr, ++ const unsigned char *fragment_data, ++ const unsigned int fragment_len, ++ const int is_first_fragment, ++ const u16 cookie, ++ struct ieee80211_tx_control *txctl); ++ ++/* RX header as received from the hardware. */ ++struct bcm43xx_rxhdr { ++ /* Frame Length. Must be generated explicitely in PIO mode. */ ++ __le16 frame_length; ++ PAD_BYTES(2); ++ /* Flags field 1 */ ++ __le16 flags1; ++ u8 rssi; ++ u8 signal_quality; ++ PAD_BYTES(2); ++ /* Flags field 3 */ ++ __le16 flags3; ++ /* Flags field 2 */ ++ __le16 flags2; ++ /* Lower 16bits of the TSF at the time the frame started. */ ++ __le16 mactime; ++ PAD_BYTES(14); ++} __attribute__((__packed__)); ++ ++#define BCM43xx_RXHDR_FLAGS1_OFDM (1 << 0) ++/*#define BCM43xx_RXHDR_FLAGS1_SIGNAL??? (1 << 3) FIXME */ ++#define BCM43xx_RXHDR_FLAGS1_SHORTPREAMBLE (1 << 7) ++#define BCM43xx_RXHDR_FLAGS1_2053RSSIADJ (1 << 14) ++ ++#define BCM43xx_RXHDR_FLAGS2_INVALIDFRAME (1 << 0) ++#define BCM43xx_RXHDR_FLAGS2_TYPE2FRAME (1 << 2) ++/*FIXME: WEP related flags */ ++ ++#define BCM43xx_RXHDR_FLAGS3_2050RSSIADJ (1 << 10) ++ ++/* Transmit Status as received from the hardware. */ ++struct bcm43xx_hwxmitstatus { ++ PAD_BYTES(4); ++ __le16 cookie; ++ u8 flags; ++ u8 cnt1:4, ++ cnt2:4; ++ PAD_BYTES(2); ++ __le16 seq; ++ __le16 unknown; //FIXME ++} __attribute__((__packed__)); ++ ++/* Transmit Status in CPU byteorder. */ ++struct bcm43xx_xmitstatus { ++ u16 cookie; ++ u8 flags; ++ u8 cnt1:4, ++ cnt2:4; ++ u16 seq; ++ u16 unknown; //FIXME ++}; ++ ++#define BCM43xx_TXSTAT_FLAG_ACK 0x01 ++//TODO #define BCM43xx_TXSTAT_FLAG_??? 0x02 ++//TODO #define BCM43xx_TXSTAT_FLAG_??? 0x04 ++//TODO #define BCM43xx_TXSTAT_FLAG_??? 0x08 ++//TODO #define BCM43xx_TXSTAT_FLAG_??? 0x10 ++#define BCM43xx_TXSTAT_FLAG_IGNORE 0x20 ++//TODO #define BCM43xx_TXSTAT_FLAG_??? 0x40 ++//TODO #define BCM43xx_TXSTAT_FLAG_??? 0x80 ++ ++u8 bcm43xx_plcp_get_ratecode_cck(const u8 bitrate); ++u8 bcm43xx_plcp_get_ratecode_ofdm(const u8 bitrate); ++ ++void bcm43xx_rx(struct bcm43xx_private *bcm, ++ struct sk_buff *skb, ++ struct bcm43xx_rxhdr *rxhdr); ++ ++#endif /* BCM43xx_XMIT_H_ */ +diff -Nur linux-2.6.16/drivers/net/wireless/bcm43xx-d80211/Kconfig linux-2.6.16-bcm43xx/drivers/net/wireless/bcm43xx-d80211/Kconfig +--- linux-2.6.16/drivers/net/wireless/bcm43xx-d80211/Kconfig 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.16-bcm43xx/drivers/net/wireless/bcm43xx-d80211/Kconfig 2006-03-28 22:16:14.000000000 +0200 +@@ -0,0 +1,62 @@ ++config BCM43XX_D80211 ++ tristate "Broadcom BCM43xx wireless support (DeviceScape stack)" ++ depends on PCI && D80211 && NET_RADIO && EXPERIMENTAL ++ select FW_LOADER ++ ---help--- ++ This is an experimental driver for the Broadcom 43xx wireless chip, ++ found in the Apple Airport Extreme and various other devices. ++ ++config BCM43XX_D80211_DEBUG ++ bool "Broadcom BCM43xx debugging (RECOMMENDED)" ++ depends on BCM43XX_D80211 ++ default y ++ ---help--- ++ Broadcom 43xx debugging messages. ++ Say Y, because the driver is still very experimental and ++ this will help you get it running. ++ ++config BCM43XX_D80211_DMA ++ bool ++config BCM43XX_D80211_PIO ++ bool ++ ++choice ++ prompt "BCM43xx data transfer mode" ++ depends on BCM43XX_D80211 ++ default BCM43XX_D80211_DMA_AND_PIO_MODE ++ ++config BCM43XX_D80211_DMA_AND_PIO_MODE ++ bool "DMA + PIO" ++ select BCM43XX_D80211_DMA ++ select BCM43XX_D80211_PIO ++ ---help--- ++ Include both, Direct Memory Access (DMA) and Programmed I/O (PIO) ++ data transfer modes. ++ The actually used mode is selectable through the module ++ parameter "pio". If the module parameter is pio=0, DMA is used. ++ Otherwise PIO is used. DMA is default. ++ ++ If unsure, choose this option. ++ ++config BCM43XX_D80211_DMA_MODE ++ bool "DMA (Direct Memory Access) only" ++ select BCM43XX_D80211_DMA ++ ---help--- ++ Only include Direct Memory Access (DMA). ++ This reduces the size of the driver module, by omitting the PIO code. ++ ++config BCM43XX_D80211_PIO_MODE ++ bool "PIO (Programmed I/O) only" ++ select BCM43XX_D80211_PIO ++ ---help--- ++ Only include Programmed I/O (PIO). ++ This reduces the size of the driver module, by omitting the DMA code. ++ Please note that PIO transfers are slow (compared to DMA). ++ ++ Also note that not all devices of the 43xx series support PIO. ++ The 4306 (Apple Airport Extreme and others) supports PIO, while ++ the 4318 is known to _not_ support PIO. ++ ++ Only use PIO, if DMA does not work for you. ++ ++endchoice +diff -Nur linux-2.6.16/drivers/net/wireless/bcm43xx-d80211/Makefile linux-2.6.16-bcm43xx/drivers/net/wireless/bcm43xx-d80211/Makefile +--- linux-2.6.16/drivers/net/wireless/bcm43xx-d80211/Makefile 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.16-bcm43xx/drivers/net/wireless/bcm43xx-d80211/Makefile 2006-03-28 22:16:14.000000000 +0200 +@@ -0,0 +1,12 @@ ++obj-$(CONFIG_BCM43XX_D80211) += bcm43xx-d80211.o ++bcm43xx-d80211-obj-$(CONFIG_BCM43XX_D80211_DEBUG) += bcm43xx_debugfs.o ++ ++bcm43xx-d80211-obj-$(CONFIG_BCM43XX_D80211_DMA) += bcm43xx_dma.o ++bcm43xx-d80211-obj-$(CONFIG_BCM43XX_D80211_PIO) += bcm43xx_pio.o ++ ++bcm43xx-d80211-objs := bcm43xx_main.o bcm43xx_ilt.o \ ++ bcm43xx_radio.o bcm43xx_phy.o \ ++ bcm43xx_power.o bcm43xx_sysfs.o \ ++ bcm43xx_leds.o bcm43xx_ethtool.o \ ++ bcm43xx_xmit.o \ ++ $(bcm43xx-d80211-obj-y) +diff -Nur linux-2.6.16/drivers/net/wireless/Kconfig linux-2.6.16-bcm43xx/drivers/net/wireless/Kconfig +--- linux-2.6.16/drivers/net/wireless/Kconfig 2006-03-20 06:53:29.000000000 +0100 ++++ linux-2.6.16-bcm43xx/drivers/net/wireless/Kconfig 2006-03-28 22:16:38.000000000 +0200 +@@ -472,6 +472,7 @@ + will be called prism54.ko. + + source "drivers/net/wireless/hostap/Kconfig" ++source "drivers/net/wireless/bcm43xx-d80211/Kconfig" + + # yes, this works even when no drivers are selected + config NET_WIRELESS +diff -Nur linux-2.6.16/drivers/net/wireless/Makefile linux-2.6.16-bcm43xx/drivers/net/wireless/Makefile +--- linux-2.6.16/drivers/net/wireless/Makefile 2006-03-20 06:53:29.000000000 +0100 ++++ linux-2.6.16-bcm43xx/drivers/net/wireless/Makefile 2006-03-28 22:16:46.000000000 +0200 +@@ -35,6 +35,7 @@ + obj-$(CONFIG_PRISM54) += prism54/ + + obj-$(CONFIG_HOSTAP) += hostap/ ++obj-$(CONFIG_BCM43XX_D80211) += bcm43xx-d80211/ + + # 16-bit wireless PCMCIA client drivers + obj-$(CONFIG_PCMCIA_RAYCS) += ray_cs.o +diff -Nur linux-2.6.16/include/net/d80211_common.h linux-2.6.16-bcm43xx/include/net/d80211_common.h +--- linux-2.6.16/include/net/d80211_common.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.16-bcm43xx/include/net/d80211_common.h 2006-03-28 22:16:14.000000000 +0200 +@@ -0,0 +1,98 @@ ++/* ++ * IEEE 802.11 driver (80211.o) -- hostapd interface ++ * Copyright 2002-2004, Instant802 Networks, Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#ifndef D80211_COMMON_H ++#define D80211_COMMON_H ++ ++#include <linux/types.h> ++ ++/* ++ * This is common header information with user space. It is used on all ++ * frames sent to wlan#ap interface. ++ */ ++ ++#define IEEE80211_FI_VERSION 0x80211001 ++ ++struct ieee80211_frame_info { ++ u32 version; ++ u32 length; ++ u64 mactime; ++ u64 hosttime; ++ u32 phytype; ++ u32 channel; ++ u32 datarate; ++ u32 antenna; ++ u32 priority; ++ u32 ssi_type; ++ u32 ssi_signal; ++ u32 ssi_noise; ++ u32 preamble; ++ u32 encoding; ++ ++ /* Note: this structure is otherwise identical to capture format used ++ * in linux-wlan-ng, but this additional field is used to provide meta ++ * data about the frame to hostapd. This was the easiest method for ++ * providing this information, but this might change in the future. */ ++ u32 msg_type; ++} __attribute__ ((packed)); ++ ++ ++enum ieee80211_msg_type { ++ ieee80211_msg_normal = 0, ++ ieee80211_msg_tx_callback_ack = 1, ++ ieee80211_msg_tx_callback_fail = 2, ++ ieee80211_msg_passive_scan = 3, ++ ieee80211_msg_wep_frame_unknown_key = 4, ++ ieee80211_msg_michael_mic_failure = 5, ++ ieee80211_msg_monitor = 6, ++ ieee80211_msg_sta_not_assoc = 7, ++ ieee80211_msg_set_aid_for_sta = 8 /* used by Intersil MVC driver */, ++ ieee80211_msg_key_threshold_notification = 9, ++ ieee80211_msg_radar = 11, ++}; ++ ++struct ieee80211_msg_set_aid_for_sta { ++ char sta_address[ETH_ALEN]; ++ u16 aid; ++}; ++ ++struct ieee80211_msg_key_notification { ++ int tx_rx_count; ++ char ifname[IFNAMSIZ]; ++ u8 addr[ETH_ALEN]; /* ff:ff:ff:ff:ff:ff for broadcast keys */ ++}; ++ ++ ++enum ieee80211_phytype { ++ ieee80211_phytype_fhss_dot11_97 = 1, ++ ieee80211_phytype_dsss_dot11_97 = 2, ++ ieee80211_phytype_irbaseband = 3, ++ ieee80211_phytype_dsss_dot11_b = 4, ++ ieee80211_phytype_pbcc_dot11_b = 5, ++ ieee80211_phytype_ofdm_dot11_g = 6, ++ ieee80211_phytype_pbcc_dot11_g = 7, ++ ieee80211_phytype_ofdm_dot11_a = 8, ++ ieee80211_phytype_dsss_dot11_turbog = 255, ++ ieee80211_phytype_dsss_dot11_turbo = 256, ++}; ++ ++enum ieee80211_ssi_type { ++ ieee80211_ssi_none = 0, ++ ieee80211_ssi_norm = 1, /* normalized, 0-1000 */ ++ ieee80211_ssi_dbm = 2, ++ ieee80211_ssi_raw = 3, /* raw SSI */ ++}; ++ ++struct ieee80211_radar_info { ++ int channel; ++ int radar; ++ int radar_type; ++}; ++ ++#endif /* D80211_COMMON_H */ +diff -Nur linux-2.6.16/include/net/d80211.h linux-2.6.16-bcm43xx/include/net/d80211.h +--- linux-2.6.16/include/net/d80211.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.16-bcm43xx/include/net/d80211.h 2006-03-28 22:16:14.000000000 +0200 +@@ -0,0 +1,870 @@ ++/* ++ * Low-level hardware driver -- IEEE 802.11 driver (80211.o) interface ++ * Copyright 2002-2005, Devicescape Software, Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#ifndef D80211_H ++#define D80211_H ++ ++#include <linux/kernel.h> ++#include <linux/if_ether.h> ++#include <linux/netdevice.h> ++#include <linux/skbuff.h> ++#include <linux/wireless.h> ++#include "d80211_shared.h" ++ ++/* Note! Only ieee80211_tx_status_irqsafe() and ieee80211_rx_irqsave() can be ++ * called in hardware interrupt context. The low-level driver must not call any ++ * other functions in hardware interrupt context. If there is a need for such ++ * call, the low-level driver should first ACK the interrupt and perform the ++ * IEEE 802.11 code call after this, e.g., from a scheduled tasklet (in ++ * software interrupt context). ++ */ ++ ++/* ++ * Frame format used when passing frame between low-level hardware drivers ++ * and IEEE 802.11 driver the same as used in the wireless media, i.e., ++ * buffers start with IEEE 802.11 header and include the same octets that ++ * are sent over air. ++ * ++ * If hardware uses IEEE 802.3 headers (and perform 802.3 <-> 802.11 ++ * conversion in firmware), upper layer 802.11 code needs to be changed to ++ * support this. ++ * ++ * If the receive frame format is not the same as the real frame sent ++ * on the wireless media (e.g., due to padding etc.), upper layer 802.11 code ++ * could be updated to provide support for such format assuming this would ++ * optimize the performance, e.g., by removing need to re-allocation and ++ * copying of the data. ++ */ ++ ++/* Interface version (used for compatibility verification) */ ++#define IEEE80211_VERSION 2 ++ ++ ++/* Channel information structure. Low-level driver is expected to fill in chan, ++ * freq, and val fields. Other fields will be filled in by 80211.o based on ++ * hostapd information and low-level driver does not need to use them. The ++ * limits for each channel will be provided in 'struct ieee80211_conf' when ++ * configuring the low-level driver with hw->config callback. */ ++struct ieee80211_channel { ++ short chan; /* channel number (IEEE 802.11) */ ++ short freq; /* frequency in MHz */ ++ int val; /* hw specific value for the channel */ ++ int flag; /* flag for hostapd use (IEEE80211_CHAN_*) */ ++ unsigned char power_level; ++ unsigned char antenna_max; ++}; ++ ++struct ieee80211_rate { ++ int rate; /* rate in 100 kbps */ ++ int val; /* hw specific value for the rate */ ++ int flags; /* IEEE80211_RATE_ flags */ ++ int val2; /* hw specific value for the rate when using short preamble ++ * (only when IEEE80211_RATE_PREAMBLE2 flag is set, i.e., for ++ * 2, 5.5, and 11 Mbps) */ ++ signed char min_rssi_ack; ++ unsigned char min_rssi_ack_delta; ++ ++ /* following fields are set by 80211.o and need not be filled by the ++ * low-level driver */ ++ int rate_inv; /* inverse of the rate (LCM(all rates) / rate) for ++ * optimizing channel utilization estimates */ ++}; ++ ++struct ieee80211_hw_modes { ++ int mode; ++ int num_channels; ++ struct ieee80211_channel *channels; ++ int num_rates; ++ struct ieee80211_rate *rates; ++ int xr_end; /* only used with Atheros XR */ ++}; ++ ++struct ieee80211_tx_queue_params { ++ int aifs; /* 0 .. 255; -1 = use default */ ++ int cw_min; /* 2^n-1: 1, 3, 7, .. , 1023; 0 = use default */ ++ int cw_max; /* 2^n-1: 1, 3, 7, .. , 1023; 0 = use default */ ++ int burst_time; /* maximum burst time in 0.1 ms (i.e., 10 = 1 ms); ++ * 0 = disabled */ ++}; ++ ++#define NUM_TX_DATA_QUEUES 6 ++ ++struct ieee80211_tx_queue_stats_data { ++ unsigned int len; /* num packets in queue */ ++ unsigned int limit; /* queue len (soft) limit */ ++ unsigned int count; /* total num frames sent */ ++}; ++ ++struct ieee80211_tx_queue_stats { ++ struct ieee80211_tx_queue_stats_data data[NUM_TX_DATA_QUEUES]; ++}; ++ ++#ifndef IEEE80211_TX_QUEUE_NUMS ++#define IEEE80211_TX_QUEUE_NUMS ++/* TODO: these need to be synchronized with hostapd_ioctl.h; make a shared ++ * header file that can be included into low-level drivers, 80211.o, and ++ * hostapd */ ++enum { ++ IEEE80211_TX_QUEUE_DATA0 = 0, ++ IEEE80211_TX_QUEUE_DATA1 = 1, ++ IEEE80211_TX_QUEUE_DATA2 = 2, ++ IEEE80211_TX_QUEUE_DATA3 = 3, ++ IEEE80211_TX_QUEUE_DATA4 = 4, ++ IEEE80211_TX_QUEUE_SVP = 5, ++ IEEE80211_TX_QUEUE_AFTER_BEACON = 6, ++ IEEE80211_TX_QUEUE_BEACON = 7 ++}; ++#endif /* IEEE80211_TX_QUEUE_NUMS */ ++ ++ ++struct ieee80211_low_level_stats { ++ unsigned int dot11ACKFailureCount; ++ unsigned int dot11RTSFailureCount; ++ unsigned int dot11FCSErrorCount; ++ unsigned int dot11RTSSuccessCount; ++}; ++ ++/* Transmit control fields. This data structure is passed to low-level driver ++ * with each TX frame. The low-level driver is responsible of configuring ++ * hardware to use given values (depending on what is supported). */ ++#define HW_KEY_IDX_INVALID -1 ++ ++struct ieee80211_tx_control { ++ enum { PKT_NORMAL = 0, PKT_PROBE_RESP } pkt_type; ++ int tx_rate; /* Transmit rate, given as the hw specific value for the ++ * rate (from struct ieee80211_rate) */ ++ int rts_cts_rate; /* Transmit rate for RTS/CTS frame, given as the hw ++ * specific value for the rate (from ++ * struct ieee80211_rate) */ ++ /* 1 = only first attempt, 2 = one retry, .. */ ++ unsigned int retry_limit:8; ++ /* duration field for RTS/CTS frame */ ++ unsigned int rts_cts_duration:16; ++ unsigned int req_tx_status:1; /* request TX status callback for this ++ * frame */ ++ unsigned int do_not_encrypt:1; /* send this frame without encryption; ++ * e.g., for EAPOL frames */ ++ unsigned int use_rts_cts:1; /* Use RTS-CTS before sending frame. */ ++ unsigned int use_cts_protect:1; /* Use CTS protection for the frame ++ * (e.g., for combined 802.11g / ++ * 802.11b networks) */ ++ unsigned int no_ack:1; /* Tell the low level not to wait for an ack */ ++ unsigned int rate_ctrl_probe:1; ++ unsigned int clear_dst_mask:1; ++ unsigned int requeue:1; ++ /* following three flags are only used with Atheros Super A/G */ ++ unsigned int compress:1; ++ unsigned int turbo_prime_notify:1; /* notify HostaAPd after frame ++ * transmission */ ++ unsigned int fast_frame:1; ++ ++ unsigned int atheros_xr:1; /* only used with Atheros XR */ ++ ++ unsigned int power_level:8; /* per-packet transmit power level, in dBm ++ */ ++ unsigned int antenna_sel:4; /* 0 = default/diversity, ++ * 1 = Ant0, 2 = Ant1 */ ++ int key_idx:8; /* -1 = do not encrypt, >= 0 keyidx from hw->set_key() ++ */ ++ int icv_len:8; /* Length of the ICV/MIC field in octets */ ++ int iv_len:8; /* Length of the IV field in octets */ ++ unsigned int queue:4; /* hardware queue to use for this frame; ++ * 0 = highest, hw->queues-1 = lowest */ ++ unsigned int sw_retry_attempt:4; /* no. of times hw has tried to ++ * transmit frame (not incl. hw retries) */ ++ ++ int rateidx; /* internal 80211.o rateidx */ ++ int alt_retry_rate; /* retry rate for the last retries, given as the ++ * hw specific value for the rate (from ++ * struct ieee80211_rate). To be used to limit ++ * packet dropping when probing higher rates, if hw ++ * supports multiple retry rates. -1 = not used */ ++ struct ieee80211_sub_if_data *sdata; /* internal */ ++}; ++ ++/* Stored in sk_buff->cb */ ++struct ieee80211_tx_packet_data { ++ struct ieee80211_sub_if_data *sdata; ++ unsigned long jiffies; ++ unsigned int req_tx_status:1; ++ unsigned int do_not_encrypt:1; ++ unsigned int pkt_probe_resp:1; ++ unsigned int requeue:1; ++ unsigned int queue:4; ++}; ++ ++#define RX_FLAG_MMIC_ERROR 0x1 ++#define RX_FLAG_DECRYPTED 0x2 ++#define RX_FLAG_XR_DOUBLE_CHIRP 0x4 ++ ++/* Receive status. The low-level driver should provide this information ++ * (the subset supported by hardware) to the 802.11 code with each received ++ * frame. */ ++struct ieee80211_rx_status { ++ u64 hosttime; ++ u64 mactime; ++ int freq; /* receive frequency in Mhz */ ++ int channel; ++ int phymode; ++ int ssi; ++ int antenna; ++ int rate; ++ int flag; ++}; ++ ++/* Transmit status. The low-level driver should provide this information ++ * (the subset supported by hardware) to the 802.11 code for each transmit ++ * frame. */ ++struct ieee80211_tx_status { ++ /* copied ieee80211_tx_control structure */ ++ struct ieee80211_tx_control control; ++ ++ unsigned int tx_filtered:1; ++ unsigned int ack:1; /* whether the TX frame was ACKed */ ++ int ack_signal; /* measured signal strength of the ACK frame */ ++ int excessive_retries; ++ int retry_count; ++ ++ /* following two fields are only used with Atheros Super A/G */ ++ int queue_length; /* information about TX queue */ ++ int queue_number; ++}; ++ ++ ++struct ieee80211_conf { ++ int channel; /* IEEE 802.11 channel number */ ++ int freq; /* MHz */ ++ int channel_val; /* hw specific value for the channel */ ++ ++ int mode; /* IW_MODE_ */ ++ ++ int phymode; /* MODE_IEEE80211A, .. */ ++ unsigned int regulatory_domain; ++ int adm_status; ++ ++ int beacon_int; ++ ++ /* Bitfields, grouped together */ ++ ++ int sw_encrypt:1; ++ int sw_decrypt:1; ++ int short_slot_time:1; /* use IEEE 802.11g Short Slot Time */ ++ int ssid_hidden:1; /* do not broadcast the ssid */ ++ ++ /* these fields are used by low level drivers for hardware ++ * that generate beacons independently */ ++ u8 *ssid; ++ size_t ssid_len; ++ u8 *generic_elem; ++ size_t generic_elem_len; ++ ++ u8 power_level; /* transmit power limit for current ++ * regulatory domain; in dBm */ ++ u8 antenna_max; /* maximum antenna gain */ ++ short tx_power_reduction; /* in 0.1 dBm */ ++ ++ int antenna_sel; /* default antenna conf: ++ * 0 = default/diversity, ++ * 1 = Ant0, ++ * 2 = Ant1 */ ++ ++ int calib_int; /* hw/radio calibration interval in ++ * seconds */ ++ int antenna_def; ++ int antenna_mode; ++ ++ u8 bssid_mask[ETH_ALEN]; /* ff:ff:ff:ff:ff:ff = 1 BSSID */ ++ int bss_count; ++ ++ int atheros_super_ag_compression; ++ int atheros_super_ag_fast_frame; ++ int atheros_super_ag_burst; ++ int atheros_super_ag_wme_ele; ++ int atheros_super_ag_turbo_g; ++ int atheros_super_ag_turbo_prime; ++ ++ int atheros_xr; ++ ++ u8 client_bssid[ETH_ALEN]; ++ ++ /* Following five fields are used for IEEE 802.11H */ ++ unsigned int radar_detect; ++ unsigned int spect_mgmt; ++ unsigned int quiet_duration; /* duration of quiet period */ ++ unsigned int quiet_offset; /* how far into the beacon is the quiet ++ * period */ ++ unsigned int quiet_period; ++ u8 radar_firpwr_threshold; ++ u8 radar_rssi_threshold; ++ u8 pulse_height_threshold; ++ u8 pulse_rssi_threshold; ++ u8 pulse_inband_threshold; ++}; ++ ++ ++typedef enum { ALG_NONE, ALG_WEP, ALG_TKIP, ALG_CCMP, ALG_NULL } ++ieee80211_key_alg; ++ ++ ++struct ieee80211_key_conf { ++ ++ int hw_key_idx; /* filled + used by low-level driver */ ++ ieee80211_key_alg alg; ++ int keylen; ++ ++ int force_sw_encrypt:1; /* to be cleared by low-level driver */ ++ int keyidx:8; /* WEP key index */ ++ int default_tx_key:1; /* This key is the new default TX key ++ * (used only for broadcast keys). */ ++ int default_wep_only:1; /* static WEP is the only configured security ++ * policy; this allows some low-level drivers ++ * to determine when hwaccel can be used */ ++ u8 key[0]; ++}; ++ ++#define IEEE80211_SCAN_START 1 ++#define IEEE80211_SCAN_END 2 ++ ++struct ieee80211_scan_conf { ++ int scan_channel; /* IEEE 802.11 channel number to do passive scan ++ * on */ ++ int scan_freq; /* new freq in MHz to switch to for passive scan ++ */ ++ int scan_channel_val; /* hw specific value for the channel */ ++ int scan_phymode; /* MODE_IEEE80211A, .. */ ++ unsigned char scan_power_level; ++ unsigned char scan_antenna_max; ++ ++ ++ int running_channel; /* IEEE 802.11 channel number we operate on ++ * normally */ ++ int running_freq; /* freq in MHz we're operating on normally */ ++ int running_channel_val; /* hw specific value for the channel */ ++ int running_phymode; ++ unsigned char running_power_level; ++ unsigned char running_antenna_max; ++ ++ int scan_time; /* time a scan will take in us */ ++ int tries; ++ ++ struct sk_buff *skb; /* skb to transmit before changing channels, maybe ++ * NULL for none */ ++ struct ieee80211_tx_control *tx_control; ++ ++}; ++ ++#ifndef IW_MODE_ADHOC ++#define IW_MODE_ADHOC 1 ++#endif ++ ++#ifndef IW_MODE_INFRA ++#define IW_MODE_INFRA 2 ++#endif ++ ++#ifndef IW_MODE_MASTER ++#define IW_MODE_MASTER 3 ++#endif ++ ++#ifndef IW_MODE_MONITOR ++#define IW_MODE_MONITOR 6 ++#endif ++ ++#define IEEE80211_SEQ_COUNTER_RX 0 ++#define IEEE80211_SEQ_COUNTER_TX 1 ++ ++typedef enum { ++ SET_KEY, DISABLE_KEY, REMOVE_ALL_KEYS, ++ ENABLE_COMPRESSION, DISABLE_COMPRESSION ++} set_key_cmd; ++ ++/* Configuration block used by the low-level driver to tell 802.11 code about ++ * supported hardware features and to pass function pointers for callback ++ * functions. */ ++struct ieee80211_hw { ++ int version; /* IEEE80211_VERSION */ ++ ++ /* Driver name */ ++ char *name; ++ ++ /* TODO: frame_type 802.11/802.3, sw_encryption requirements */ ++ ++ /* Some wireless LAN chipsets generate beacons in the hardware/firmware ++ * and others rely on host generated beacons. This option is used to ++ * configure upper layer IEEE 802.11 module to generate beacons. The ++ * low-level driver can use ieee80211_beacon_get() to fetch next ++ * beacon frame. */ ++ int host_gen_beacon:1; ++ ++ ++ /* Some devices handle decryption internally and do not ++ * indicate whether the frame was encrypted (unencrypted frames ++ * will be dropped by the hardware, unless specifically allowed ++ * through) */ ++ int device_hides_wep:1; ++ ++ /* Whether RX frames passed to ieee80211_rx() include FCS in the end ++ */ ++ int rx_includes_fcs:1; ++ ++ /* Some wireless LAN chipsets buffer broadcast/multicast frames for ++ * power saving stations in the hardware/firmware and others rely on ++ * the host system for such buffering. This option is used to ++ * configure upper layer IEEE 802.11 to buffer broadcast/multicast ++ * frames when there are power saving stations so that low-level driver ++ * can fetch them with ieee80211_get_buffered_bc(). */ ++ int host_broadcast_ps_buffering:1; ++ ++ int wep_include_iv:1; ++ int data_nullfunc_ack:1; /* will data nullfunc frames get proper ++ * TX status callback */ ++ ++ /* Force sw version of encryption for TKIP packets if WMM is enabled. ++ */ ++ int no_tkip_wmm_hwaccel:1; ++ ++ /* 1 if the payload needs to be padded at even boundaries after the ++ * header */ ++ unsigned int extra_hdr_room:1; ++ ++ /* Some devices handle Michael MIC internally and do not include MIC in ++ * the received packets given to 80211.o. device_strips_mic must be set ++ * for such devices. ISWEP bit is still expected to be set in the IEEE ++ * 802.11 header with this option unlike with device_hides_wep option. ++ */ ++ unsigned int device_strips_mic:1; ++ ++ /* 1 = low-level driver supports skb fraglist (NETIF_F_FRAGLIST), i.e., ++ * more than one skb per frame */ ++ unsigned int fraglist; ++ ++ /* This is the time in us to change channels ++ */ ++ int channel_change_time; ++ ++ int num_modes; ++ struct ieee80211_hw_modes *modes; ++ ++ /* Handler that 802.11 module calls for each transmitted frame. ++ * skb contains the buffer starting from the IEEE 802.11 header. ++ * The low-level driver should send the frame out based on ++ * configuration in the TX control data. */ ++ int (*tx)(struct net_device *dev, struct sk_buff *skb, ++ struct ieee80211_tx_control *control); ++ ++ /* Handler for performing hardware reset. */ ++ int (*reset)(struct net_device *dev); ++ ++ /* Handler that is called when any netdevice attached to the hardware ++ * device is set UP for the first time. This can be used, e.g., to ++ * enable interrupts and beacon sending. */ ++ int (*open)(struct net_device *dev); ++ ++ /* Handler that is called when the last netdevice attached to the ++ * hardware device is set DOWN. This can be used, e.g., to disable ++ * interrupts and beacon sending. */ ++ int (*stop)(struct net_device *dev); ++ ++ /* Handler for configuration requests. IEEE 802.11 code calls this ++ * function to change hardware configuration, e.g., channel. */ ++ int (*config)(struct net_device *dev, struct ieee80211_conf *conf); ++ ++ /* Set TIM bit handler. If the hardware/firmware takes care of beacon ++ * generation, IEEE 802.11 code uses this function to tell the ++ * low-level to set (or clear if set==0) TIM bit for the given aid. If ++ * host system is used to generate beacons, this handler is not used ++ * and low-level driver should set it to NULL. */ ++ int (*set_tim)(struct net_device *dev, int aid, int set); ++ ++ /* Set encryption key. IEEE 802.11 module calls this function to set ++ * encryption keys. addr is ff:ff:ff:ff:ff:ff for default keys and ++ * station hwaddr for individual keys. aid of the station is given ++ * to help low-level driver in selecting which key->hw_key_idx to use ++ * for this key. TX control data will use the hw_key_idx selected by ++ * the low-level driver. */ ++ int (*set_key)(struct net_device *dev, set_key_cmd cmd, u8 *addr, ++ struct ieee80211_key_conf *key, int aid); ++ ++ /* Set TX key index for default/broadcast keys. This is needed in cases ++ * where wlan card is doing full WEP/TKIP encapsulation (wep_include_iv ++ * is not set), in other cases, this function pointer can be set to ++ * NULL since 80211.o takes care of selecting the key index for each ++ * TX frame. */ ++ int (*set_key_idx)(struct net_device *dev, int idx); ++ ++ /* Enable/disable IEEE 802.1X. This item requests wlan card to pass ++ * unencrypted EAPOL-Key frames even when encryption is configured. ++ * If the wlan card does not require such a configuration, this ++ * function pointer can be set to NULL. 80211.o */ ++ int (*set_ieee8021x)(struct net_device *dev, int use_ieee8021x); ++ ++ /* Set port authorization state (IEEE 802.1X PAE) to be authorized ++ * (authorized=1) or unauthorized (authorized=0). This function can be ++ * used if the wlan hardware or low-level driver implements PAE. ++ * 80211.o module will anyway filter frames based on authorization ++ * state, so this function pointer can be NULL if low-level driver does ++ * not require event notification about port state changes. */ ++ int (*set_port_auth)(struct net_device *dev, u8 *addr, int authorized); ++ ++ /* Ask the hardware to do a passive scan on a new channel. The hardware ++ * will do what ever is required to nicely leave the current channel ++ * including transmit any CTS packets, etc. */ ++ int (*passive_scan)(struct net_device *dev, int state, ++ struct ieee80211_scan_conf *conf); ++ ++ /* return low-level statistics */ ++ int (*get_stats)(struct net_device *dev, ++ struct ieee80211_low_level_stats *stats); ++ ++ /* Enable/disable test modes; mode = IEEE80211_TEST_* */ ++ int (*test_mode)(struct net_device *dev, int mode); ++ ++ /* Configuration of test parameters */ ++ int (*test_param)(struct net_device *dev, int param, int value); ++ ++ /* Change MAC address. addr is pointer to struct sockaddr. */ ++ int (*set_mac_address)(struct net_device *dev, void *addr); ++ ++ /* For devices that generate their own beacons and probe response ++ * or association responses this updates the state of privacy_invoked ++ * returns 0 for success or an error number */ ++ ++ int (*set_privacy_invoked)(struct net_device *dev, ++ int privacy_invoked); ++ ++ /* For devices that have internal sequence counters, allow 802.11 ++ * code to access the current value of a counter */ ++ int (*get_sequence_counter)(struct net_device *dev, ++ u8* addr, u8 keyidx, u8 txrx, ++ u32* iv32, u16* iv16); ++ ++ /* Configuration of RTS threshold (if device needs it) */ ++ int (*set_rts_threshold)(struct net_device *dev, u32 value); ++ ++ /* Configuration of fragmentation threshold (if device needs it) */ ++ int (*set_frag_threshold)(struct net_device *dev, u32 value); ++ ++ /* Configuration of retry limits (if device needs it) */ ++ int (*set_retry_limit)(struct net_device *dev, u32 short_retry, ++ u32 long_retr); ++ ++ /* Number of STAs in STA table notification (NULL = disabled) */ ++ void (*sta_table_notification)(struct net_device *dev, int num_sta); ++ ++ /* Configure TX queue parameters (EDCF (aifs, cw_min, cw_max), ++ * bursting) for a hardware TX queue. ++ * queue = IEEE80211_TX_QUEUE_*. */ ++ int (*conf_tx)(struct net_device *dev, int queue, ++ const struct ieee80211_tx_queue_params *params); ++ ++ /* Get statistics of the current TX queue status. This is used to get ++ * number of currently queued packets (queue length), maximum queue ++ * size (limit), and total number of packets sent using each TX queue ++ * (count). This information is used for WMM to find out which TX ++ * queues have room for more packets and by hostapd to provide ++ * statistics about the current queueing state to external programs. */ ++ int (*get_tx_stats)(struct net_device *dev, ++ struct ieee80211_tx_queue_stats *stats); ++ ++ /* Number of available hardware TX queues for data packets. ++ * WMM requires at least four queues. */ ++ int queues; ++ ++ /* Get the current TSF timer value from firmware/hardware. Currently, ++ * this is only used for IBSS mode debugging and, as such, is not a ++ * required function. */ ++ u64 (*get_tsf)(struct net_device *dev); ++ ++ /* Reset the TSF timer and allow firmware/hardware to synchronize with ++ * other STAs in the IBSS. This is only used in IBSS mode. This ++ * function is optional if the firmware/hardware takes full care of ++ * TSF synchronization. */ ++ void (*reset_tsf)(struct net_device *dev); ++ ++ /* Setup beacon data for IBSS beacons. Unlike access point (Master), ++ * IBSS uses a fixed beacon frame which is configured using this ++ * function. This handler is required only for IBSS mode. */ ++ int (*beacon_update)(struct net_device *dev, struct sk_buff *skb, ++ struct ieee80211_tx_control *control); ++ ++ /* Determine whether the last IBSS beacon was sent by us. This is ++ * needed only for IBSS mode and the result of this function is used to ++ * determine whether to reply to Probe Requests. */ ++ int (*tx_last_beacon)(struct net_device *dev); ++ ++ /* Optional handler for XR-in-use notification. */ ++ int (*atheros_xr_in_use)(struct net_device *dev, int in_use); ++}; ++ ++/* Allocate a new hardware device. This must be called once for each ++ * hardware device. The returned pointer must be used to refer to this ++ * device when calling other functions. 802.11 code allocates a private data ++ * area for the low-level driver. The size of this area is given as ++ * priv_data_len. ieee80211_dev_hw_data() is used to get a pointer to the ++ * private data area. ++ * ++ * Note: in this version of the interface the returned pointer is struct ++ * net_device *. This may change in the future and low-level driver should ++ * not refer the device data directly to remain compatible with the future ++ * versions of the interface. */ ++struct net_device *ieee80211_alloc_hw(size_t priv_data_len, ++ void (*setup)(struct net_device *)); ++ ++/* Register hardware device to the IEEE 802.11 code and kernel. Low-level ++ * drivers must call this function before using any other IEEE 802.11 ++ * function. */ ++int ieee80211_register_hw(struct net_device *dev, struct ieee80211_hw *hw); ++ ++/* This function is allowed to update hardware configuration (e.g., list of ++ * supported operation modes and rates). */ ++int ieee80211_update_hw(struct net_device *dev, struct ieee80211_hw *hw); ++ ++/* Unregister a hardware device. This function instructs 802.11 code to free ++ * allocated resources and unregister netdevices from the kernel. */ ++void ieee80211_unregister_hw(struct net_device *dev); ++ ++/* Free allocated net_device including private data of a driver. */ ++void ieee80211_free_hw(struct net_device *dev); ++ ++/* Receive frame callback function. The low-level driver uses this function to ++ * send received frames to the IEEE 802.11 code. Receive buffer (skb) must ++ * start with IEEE 802.11 header. */ ++void __ieee80211_rx(struct net_device *dev, struct sk_buff *skb, ++ struct ieee80211_rx_status *status); ++void ieee80211_rx_irqsafe(struct net_device *dev, struct sk_buff *skb, ++ struct ieee80211_rx_status *status); ++ ++/* Transmit status callback function. The low-level driver must call this ++ * function to report transmit status for all the TX frames that had ++ * req_tx_status set in the transmit control fields. In addition, this should ++ * be called at least for all unicast frames to provide information for TX rate ++ * control algorithm. In order to maintain all statistics, this function is ++ * recommended to be called after each frame, including multicast/broadcast, is ++ * sent. */ ++void ieee80211_tx_status(struct net_device *dev, struct sk_buff *skb, ++ struct ieee80211_tx_status *status); ++void ieee80211_tx_status_irqsafe(struct net_device *dev, struct sk_buff *skb, ++ struct ieee80211_tx_status *status); ++ ++/* Beacon generation function. If the beacon frames are generated by the host ++ * system (i.e., not in hardware/firmware), the low-level driver uses this ++ * function to receive the next beacon frame from the 802.11 code. The ++ * low-level is responsible for calling this function before beacon data is ++ * needed (e.g., based on hardware interrupt). Returned skb is used only once ++ * and low-level driver is responsible of freeing it. */ ++struct sk_buff * ieee80211_beacon_get(struct net_device *dev, int bss_idx, ++ struct ieee80211_tx_control *control); ++ ++/* Function for accessing buffered broadcast and multicast frames. If ++ * hardware/firmware does not implement buffering of broadcast/multicast ++ * frames when power saving is used, 802.11 code buffers them in the host ++ * memory. The low-level driver uses this function to fetch next buffered ++ * frame. In most cases, this is used when generating beacon frame. This ++ * function returns a pointer to the next buffered skb or NULL if no more ++ * buffered frames are available. ++ * ++ * Note: buffered frames are returned only after DTIM beacon frame was ++ * generated with ieee80211_beacon_get() and the low-level driver must thus ++ * call ieee80211_beacon_get() first. ieee80211_get_buffered_bc() returns ++ * NULL if the previous generated beacon was not DTIM, so the low-level driver ++ * does not need to check for DTIM beacons separately and should be able to ++ * use common code for all beacons. */ ++struct sk_buff * ++ieee80211_get_buffered_bc(struct net_device *dev, int bss_idx, ++ struct ieee80211_tx_control *control); ++ ++/* Low level drivers that have their own MLME and MAC indicate ++ * the aid for an associating station with this call */ ++int ieee80211_set_aid_for_sta(struct net_device *dev, u8 *peer_address, ++ u16 aid); ++ ++ ++/* Given an sk_buff with a raw 802.11 header at the data pointer this function ++ * returns the 802.11 header length in bytes (not including encryption ++ * headers). If the data in the sk_buff is too short to contain a valid 802.11 ++ * header the function returns 0. ++ */ ++int ieee80211_get_hdrlen_from_skb(struct sk_buff *skb); ++ ++/* Like ieee80211_get_hdrlen_from_skb() but takes a FC in CPU order. */ ++int ieee80211_get_hdrlen(u16 fc); ++ ++/* Function for net interface operation. IEEE 802.11 may use multiple kernel ++ * netdevices for each hardware device. The low-level driver does not "see" ++ * these interfaces, so it should use this function to perform netif ++ * operations on all interface. */ ++typedef enum { ++ NETIF_ATTACH, NETIF_DETACH, NETIF_START, NETIF_STOP, NETIF_WAKE, ++ NETIF_IS_STOPPED, NETIF_UPDATE_TX_START ++} Netif_Oper; ++int ieee80211_netif_oper(struct net_device *dev, Netif_Oper op); ++ ++ ++/* ++ * Function to get hardware configuration information ++ * by the low level driver should it need it. ++ */ ++struct ieee80211_conf * ++ieee80211_get_hw_conf(struct net_device *dev); ++ ++ ++/* Return a pointer to the low-level private data area for the given device. */ ++void * ieee80211_dev_hw_data(struct net_device *dev); ++/* Return a pointer to network statistics data area for the given device. */ ++void * ieee80211_dev_stats(struct net_device *dev); ++ ++/* Function to indicate Radar Detection. The low level driver must call this ++ * function to indicate the presence of radar in the current channel. ++ * Additionally the radar type also could be sent */ ++int ieee80211_radar_status(struct net_device *dev, int channel, int radar, ++ int radar_type); ++ ++/* Test modes */ ++enum { ++ IEEE80211_TEST_DISABLE = 0 /* terminate testing */, ++ IEEE80211_TEST_UNMASK_CHANNELS = 1 /* allow all channels to be used */, ++ IEEE80211_TEST_CONTINUOUS_TX = 2, ++}; ++ ++/* Test parameters */ ++enum { ++ /* TX power in hardware specific raw value */ ++ IEEE80211_TEST_PARAM_TX_POWER_RAW = 0, ++ /* TX rate in hardware specific raw value */ ++ IEEE80211_TEST_PARAM_TX_RATE_RAW = 1, ++ /* Continuous TX pattern (32-bit) */ ++ IEEE80211_TEST_PARAM_TX_PATTERN = 2, ++ /* TX power in 0.1 dBm, 100 = 10 dBm */ ++ IEEE80211_TEST_PARAM_TX_POWER = 3, ++ /* TX rate in 100 kbps, 540 = 54 Mbps */ ++ IEEE80211_TEST_PARAM_TX_RATE = 4, ++ IEEE80211_TEST_PARAM_TX_ANT_SEL_RAW = 5, ++}; ++ ++/* ieee80211_tx_led called with state == 1 when the first frame is queued ++ * with state == 0 when the last frame is transmitted and tx queue is empty ++ */ ++void ieee80211_tx_led(int state, struct net_device *dev); ++/* ieee80211_rx_led is called each time frame is received, state is not used ++ * (== 2) ++ */ ++void ieee80211_rx_led(int state, struct net_device *dev); ++ ++ ++/* IEEE 802.11 defines */ ++ ++#define FCS_LEN 4 ++ ++#define WLAN_FC_PVER 0x0003 ++#define WLAN_FC_TODS 0x0100 ++#define WLAN_FC_FROMDS 0x0200 ++#define WLAN_FC_MOREFRAG 0x0400 ++#define WLAN_FC_RETRY 0x0800 ++#define WLAN_FC_PWRMGT 0x1000 ++#define WLAN_FC_MOREDATA 0x2000 ++#define WLAN_FC_ISWEP 0x4000 ++#define WLAN_FC_ORDER 0x8000 ++ ++#define WLAN_FC_GET_TYPE(fc) (((fc) & 0x000c) >> 2) ++#define WLAN_FC_GET_STYPE(fc) (((fc) & 0x00f0) >> 4) ++ ++#define WLAN_GET_SEQ_FRAG(seq) ((seq) & 0x000f) ++#define WLAN_GET_SEQ_SEQ(seq) ((seq) >> 4) ++ ++#define WLAN_FC_DATA_PRESENT(fc) (((fc) & 0x4c) == 0x08) ++ ++#define WLAN_FC_TYPE_MGMT 0 ++#define WLAN_FC_TYPE_CTRL 1 ++#define WLAN_FC_TYPE_DATA 2 ++ ++/* management */ ++#define WLAN_FC_STYPE_ASSOC_REQ 0 ++#define WLAN_FC_STYPE_ASSOC_RESP 1 ++#define WLAN_FC_STYPE_REASSOC_REQ 2 ++#define WLAN_FC_STYPE_REASSOC_RESP 3 ++#define WLAN_FC_STYPE_PROBE_REQ 4 ++#define WLAN_FC_STYPE_PROBE_RESP 5 ++#define WLAN_FC_STYPE_BEACON 8 ++#define WLAN_FC_STYPE_ATIM 9 ++#define WLAN_FC_STYPE_DISASSOC 10 ++#define WLAN_FC_STYPE_AUTH 11 ++#define WLAN_FC_STYPE_DEAUTH 12 ++#define WLAN_FC_STYPE_ACTION 13 ++ ++/* control */ ++#define WLAN_FC_STYPE_PSPOLL 10 ++#define WLAN_FC_STYPE_RTS 11 ++#define WLAN_FC_STYPE_CTS 12 ++#define WLAN_FC_STYPE_ACK 13 ++#define WLAN_FC_STYPE_CFEND 14 ++#define WLAN_FC_STYPE_CFENDACK 15 ++ ++/* data */ ++#define WLAN_FC_STYPE_DATA 0 ++#define WLAN_FC_STYPE_DATA_CFACK 1 ++#define WLAN_FC_STYPE_DATA_CFPOLL 2 ++#define WLAN_FC_STYPE_DATA_CFACKPOLL 3 ++#define WLAN_FC_STYPE_NULLFUNC 4 ++#define WLAN_FC_STYPE_CFACK 5 ++#define WLAN_FC_STYPE_CFPOLL 6 ++#define WLAN_FC_STYPE_CFACKPOLL 7 ++#define WLAN_FC_STYPE_QOS_DATA 8 ++#define WLAN_FC_STYPE_QOS_DATA_CFACK 9 ++#define WLAN_FC_STYPE_QOS_DATA_CFPOLL 10 ++#define WLAN_FC_STYPE_QOS_DATA_CFACKPOLL 11 ++#define WLAN_FC_STYPE_QOS_NULLFUNC 12 ++#define WLAN_FC_STYPE_QOS_CFACK 13 ++#define WLAN_FC_STYPE_QOS_CFPOLL 14 ++#define WLAN_FC_STYPE_QOS_CFACKPOLL 15 ++ ++ ++#define IEEE80211_MAX_FRAG_THRESHOLD 2346 ++#define IEEE80211_MAX_RTS_THRESHOLD 2347 ++ ++struct ieee80211_hdr { ++ u16 frame_control; ++ u16 duration_id; ++ u8 addr1[6]; ++ u8 addr2[6]; ++ u8 addr3[6]; ++ u16 seq_ctrl; ++ u8 addr4[6]; ++} __attribute__ ((packed)); ++ ++/* return a pointer to the source address (SA) */ ++static inline u8 *ieee80211_get_SA(struct ieee80211_hdr *hdr) ++{ ++ u8 *raw = (u8 *) hdr; ++ u8 tofrom = (*(raw+1)) & 3; /* get the TODS and FROMDS bits */ ++ ++ switch (tofrom) { ++ case 2: ++ return hdr->addr3; ++ case 3: ++ return hdr->addr4; ++ } ++ return hdr->addr2; ++} ++ ++/* return a pointer to the destination address (DA) */ ++static inline u8 *ieee80211_get_DA(struct ieee80211_hdr *hdr) ++{ ++ u8 *raw = (u8 *) hdr; ++ u8 to_ds = (*(raw+1)) & 1; /* get the TODS bit */ ++ ++ if (to_ds) ++ return hdr->addr3; ++ return hdr->addr1; ++} ++ ++static inline int ieee80211_get_morefrag(struct ieee80211_hdr *hdr) ++{ ++ return (le16_to_cpu(hdr->frame_control) & WLAN_FC_MOREFRAG) != 0; ++} ++ ++#endif /* D80211_H */ +diff -Nur linux-2.6.16/include/net/d80211_mgmt.h linux-2.6.16-bcm43xx/include/net/d80211_mgmt.h +--- linux-2.6.16/include/net/d80211_mgmt.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.16-bcm43xx/include/net/d80211_mgmt.h 2006-03-28 22:16:14.000000000 +0200 +@@ -0,0 +1,197 @@ ++/* ++ * IEEE 802.11 -- shared defines for 80211.o and hostapd ++ * Copyright 2002, Jouni Malinen <jkmaline@cc.hut.fi> ++ * Copyright 2002-2004, Instant802 Networks, Inc. ++ * Copyright 2005, Devicescape Software, Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#ifndef D802_11_MGMT_H ++#define D802_11_MGMT_H ++ ++#include <linux/types.h> ++ ++struct ieee80211_mgmt { ++ u16 frame_control; ++ u16 duration; ++ u8 da[6]; ++ u8 sa[6]; ++ u8 bssid[6]; ++ u16 seq_ctrl; ++ union { ++ struct { ++ u16 auth_alg; ++ u16 auth_transaction; ++ u16 status_code; ++ /* possibly followed by Challenge text */ ++ u8 variable[0]; ++ } __attribute__ ((packed)) auth; ++ struct { ++ u16 reason_code; ++ } __attribute__ ((packed)) deauth; ++ struct { ++ u16 capab_info; ++ u16 listen_interval; ++ /* followed by SSID and Supported rates */ ++ u8 variable[0]; ++ } __attribute__ ((packed)) assoc_req; ++ struct { ++ u16 capab_info; ++ u16 status_code; ++ u16 aid; ++ /* followed by Supported rates */ ++ u8 variable[0]; ++ } __attribute__ ((packed)) assoc_resp, reassoc_resp; ++ struct { ++ u16 capab_info; ++ u16 listen_interval; ++ u8 current_ap[6]; ++ /* followed by SSID and Supported rates */ ++ u8 variable[0]; ++ } __attribute__ ((packed)) reassoc_req; ++ struct { ++ u16 reason_code; ++ } __attribute__ ((packed)) disassoc; ++ struct { ++ u8 timestamp[8]; ++ u16 beacon_int; ++ u16 capab_info; ++ /* followed by some of SSID, Supported rates, ++ * FH Params, DS Params, CF Params, IBSS Params, TIM */ ++ u8 variable[0]; ++ } __attribute__ ((packed)) beacon; ++ struct { ++ /* only variable items: SSID, Supported rates */ ++ u8 variable[0]; ++ } __attribute__ ((packed)) probe_req; ++ struct { ++ u8 timestamp[8]; ++ u16 beacon_int; ++ u16 capab_info; ++ /* followed by some of SSID, Supported rates, ++ * FH Params, DS Params, CF Params, IBSS Params */ ++ u8 variable[0]; ++ } __attribute__ ((packed)) probe_resp; ++ struct { ++ u8 category; ++ union { ++ struct { ++ u8 action_code; ++ u8 dialog_token; ++ u8 status_code; ++ u8 variable[0]; ++ } __attribute__ ((packed)) wme_action; ++ struct{ ++ u8 action_code; ++ u8 element_id; ++ u8 length; ++ u8 switch_mode; ++ u8 new_chan; ++ u8 switch_count; ++ } __attribute__((packed)) chan_switch; ++ } u; ++ } __attribute__ ((packed)) action; ++ } u; ++} __attribute__ ((packed)); ++ ++ ++/* Authentication algorithms */ ++#define WLAN_AUTH_OPEN 0 ++#define WLAN_AUTH_SHARED_KEY 1 ++#define WLAN_AUTH_LEAP 128 ++ ++#define WLAN_AUTH_CHALLENGE_LEN 128 ++ ++#define WLAN_CAPABILITY_ESS BIT(0) ++#define WLAN_CAPABILITY_IBSS BIT(1) ++#define WLAN_CAPABILITY_CF_POLLABLE BIT(2) ++#define WLAN_CAPABILITY_CF_POLL_REQUEST BIT(3) ++#define WLAN_CAPABILITY_PRIVACY BIT(4) ++#define WLAN_CAPABILITY_SHORT_PREAMBLE BIT(5) ++#define WLAN_CAPABILITY_PBCC BIT(6) ++#define WLAN_CAPABILITY_CHANNEL_AGILITY BIT(7) ++/* 802.11h */ ++#define WLAN_CAPABILITY_SPECTRUM_MGMT BIT(8) ++#define WLAN_CAPABILITY_SHORT_SLOT_TIME BIT(10) ++#define WLAN_CAPABILITY_DSSS_OFDM BIT(13) ++ ++/* Status codes */ ++#define WLAN_STATUS_SUCCESS 0 ++#define WLAN_STATUS_UNSPECIFIED_FAILURE 1 ++#define WLAN_STATUS_CAPS_UNSUPPORTED 10 ++#define WLAN_STATUS_REASSOC_NO_ASSOC 11 ++#define WLAN_STATUS_ASSOC_DENIED_UNSPEC 12 ++#define WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG 13 ++#define WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION 14 ++#define WLAN_STATUS_CHALLENGE_FAIL 15 ++#define WLAN_STATUS_AUTH_TIMEOUT 16 ++#define WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA 17 ++#define WLAN_STATUS_ASSOC_DENIED_RATES 18 ++/* 802.11b */ ++#define WLAN_STATUS_ASSOC_DENIED_NOSHORT 19 ++#define WLAN_STATUS_ASSOC_DENIED_NOPBCC 20 ++#define WLAN_STATUS_ASSOC_DENIED_NOAGILITY 21 ++/* 802.11h */ ++#define WLAN_STATUS_SPEC_MGMT_REQUIRED 22 ++#define WLAN_STATUS_PWR_CAPABILITY_NOT_VALID 23 ++#define WLAN_STATUS_SUPPORTED_CHANNEL_NOT_VALID 24 ++/* 802.11g */ ++#define WLAN_STATUS_ASSOC_DENOED_NO_SHORT_SLOT_TIME 25 ++#define WLAN_STATUS_ASSOC_DENOED_NO_ER_PBCC 26 ++#define WLAN_STATUS_ASSOC_DENOED_NO_DSSS_OFDM 27 ++ ++ ++/* Reason codes */ ++#define WLAN_REASON_UNSPECIFIED 1 ++#define WLAN_REASON_PREV_AUTH_NOT_VALID 2 ++#define WLAN_REASON_DEAUTH_LEAVING 3 ++#define WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY 4 ++#define WLAN_REASON_DISASSOC_AP_BUSY 5 ++#define WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA 6 ++#define WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA 7 ++#define WLAN_REASON_DISASSOC_STA_HAS_LEFT 8 ++#define WLAN_REASON_STA_REQ_ASSOC_WITHOUT_AUTH 9 ++/* 802.11h */ ++#define WLAN_REASON_PWR_CAPABILITY_NOT_VALID 10 ++#define WLAN_REASON_SUPPORTED_CHANNEL_NOT_VALID 11 ++ ++#define WLAN_REASON_MIC_FAILURE 14 ++ ++ ++/* Information Element IDs */ ++#define WLAN_EID_SSID 0 ++#define WLAN_EID_SUPP_RATES 1 ++#define WLAN_EID_FH_PARAMS 2 ++#define WLAN_EID_DS_PARAMS 3 ++#define WLAN_EID_CF_PARAMS 4 ++#define WLAN_EID_TIM 5 ++#define WLAN_EID_IBSS_PARAMS 6 ++#define WLAN_EID_COUNTRY 7 ++#define WLAN_EID_CHALLENGE 16 ++/* EIDs defined as part fo 11h - starts */ ++#define WLAN_EID_PWR_CONSTRAINT 32 ++#define WLAN_EID_PWR_CAPABILITY 33 ++#define WLAN_EID_TPC_REQUEST 34 ++#define WLAN_EID_TPC_REPORT 35 ++#define WLAN_EID_SUPPORTED_CHANNELS 36 ++#define WLAN_EID_CHANNEL_SWITCH 37 ++#define WLAN_EID_MEASURE_REQUEST 38 ++#define WLAN_EID_MEASURE_REPORT 39 ++#define WLAN_EID_QUITE 40 ++#define WLAN_EID_IBSS_DFS 41 ++/* EIDs defined as part fo 11h - ends */ ++#define WLAN_EID_ERP_INFO 42 ++#define WLAN_EID_RSN 48 ++#define WLAN_EID_EXT_SUPP_RATES 50 ++#define WLAN_EID_WPA 221 ++#define WLAN_EID_GENERIC 221 ++#define WLAN_EID_VENDOR_SPECIFIC 221 ++ ++ ++ ++#define ATHEROS_INFO_USEXR BIT(3) ++ ++#endif /* D802_11_MGMT_H */ +diff -Nur linux-2.6.16/include/net/d80211_shared.h linux-2.6.16-bcm43xx/include/net/d80211_shared.h +--- linux-2.6.16/include/net/d80211_shared.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.16-bcm43xx/include/net/d80211_shared.h 2006-03-28 22:16:14.000000000 +0200 +@@ -0,0 +1,50 @@ ++/* ++ * IEEE 802.11 -- shared defines for low-level drivers, 80211.o, and hostapd ++ * Copyright 2002-2004, Instant802 Networks, Inc. ++ * Copyright 2005, Devicescape Software, Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#ifndef D80211_SHARED_H ++#define D80211_SHARED_H ++ ++/* 802.11g is backwards-compatible with 802.11b, so a wlan card can ++ * actually be both in 11b and 11g modes at the same time. */ ++enum { ++ MODE_IEEE80211A = 0 /* IEEE 802.11a */, ++ MODE_IEEE80211B = 1 /* IEEE 802.11b only */, ++ MODE_ATHEROS_TURBO = 2 /* Atheros Turbo mode (2x.11a at 5 GHz) */, ++ MODE_IEEE80211G = 3 /* IEEE 802.11g (and 802.11b compatibility) */, ++ MODE_ATHEROS_TURBOG = 4 /* Atheros Turbo mode (2x.11g at 2.4 GHz) */, ++ MODE_ATHEROS_PRIME = 5 /* Atheros Dynamic Turbo mode */, ++ MODE_ATHEROS_PRIMEG = 6 /* Atheros Dynamic Turbo mode G */, ++ MODE_ATHEROS_XR = 7 /* Atheros XR mode */, ++ NUM_IEEE80211_MODES = 8 ++}; ++ ++#define IEEE80211_CHAN_W_SCAN 0x00000001 ++#define IEEE80211_CHAN_W_ACTIVE_SCAN 0x00000002 ++#define IEEE80211_CHAN_W_IBSS 0x00000004 ++ ++/* Low-level driver should set PREAMBLE2, OFDM, CCK, and TURBO flags. ++ * BASIC, SUPPORTED, ERP, and MANDATORY flags are set in 80211.o based on the ++ * configuration. */ ++#define IEEE80211_RATE_ERP 0x00000001 ++#define IEEE80211_RATE_BASIC 0x00000002 ++#define IEEE80211_RATE_PREAMBLE2 0x00000004 ++#define IEEE80211_RATE_SUPPORTED 0x00000010 ++#define IEEE80211_RATE_OFDM 0x00000020 ++#define IEEE80211_RATE_CCK 0x00000040 ++#define IEEE80211_RATE_TURBO 0x00000080 ++#define IEEE80211_RATE_MANDATORY 0x00000100 ++#define IEEE80211_RATE_XR 0x00000200 ++ ++#define IEEE80211_RATE_CCK_2 (IEEE80211_RATE_CCK | IEEE80211_RATE_PREAMBLE2) ++#define IEEE80211_RATE_MODULATION(f) \ ++(f & (IEEE80211_RATE_CCK | IEEE80211_RATE_OFDM)) ++ ++ ++#endif /* D80211_SHARED_H */ +diff -Nur linux-2.6.16/include/net/ieee80211_crypt.h linux-2.6.16-bcm43xx/include/net/ieee80211_crypt.h +--- linux-2.6.16/include/net/ieee80211_crypt.h 2006-03-20 06:53:29.000000000 +0100 ++++ linux-2.6.16-bcm43xx/include/net/ieee80211_crypt.h 2006-03-28 22:16:14.000000000 +0200 +@@ -47,7 +47,8 @@ + /* deinitialize crypto context and free allocated private data */ + void (*deinit) (void *priv); + +- int (*build_iv) (struct sk_buff * skb, int hdr_len, void *priv); ++ int (*build_iv) (struct sk_buff * skb, int hdr_len, ++ u8 *key, int keylen, void *priv); + + /* encrypt/decrypt return < 0 on error or >= 0 on success. The return + * value from decrypt_mpdu is passed as the keyidx value for +diff -Nur linux-2.6.16/include/net/ieee80211.h linux-2.6.16-bcm43xx/include/net/ieee80211.h +--- linux-2.6.16/include/net/ieee80211.h 2006-03-20 06:53:29.000000000 +0100 ++++ linux-2.6.16-bcm43xx/include/net/ieee80211.h 2006-03-28 22:16:14.000000000 +0200 +@@ -220,6 +220,7 @@ + /* Authentication algorithms */ + #define WLAN_AUTH_OPEN 0 + #define WLAN_AUTH_SHARED_KEY 1 ++#define WLAN_AUTH_LEAP 2 + + #define WLAN_AUTH_CHALLENGE_LEN 128 + +@@ -299,6 +300,23 @@ + WLAN_REASON_CIPHER_SUITE_REJECTED = 24, + }; + ++/* Action categories - 802.11h */ ++enum ieee80211_actioncategories { ++ WLAN_ACTION_SPECTRUM_MGMT = 0, ++ /* Reserved 1-127 */ ++ /* Error 128-255 */ ++}; ++ ++/* Action details - 802.11h */ ++enum ieee80211_actiondetails { ++ WLAN_ACTION_CATEGORY_MEASURE_REQUEST = 0, ++ WLAN_ACTION_CATEGORY_MEASURE_REPORT = 1, ++ WLAN_ACTION_CATEGORY_TPC_REQUEST = 2, ++ WLAN_ACTION_CATEGORY_TPC_REPORT = 3, ++ WLAN_ACTION_CATEGORY_CHANNEL_SWITCH = 4, ++ /* 5 - 255 Reserved */ ++}; ++ + #define IEEE80211_STATMASK_SIGNAL (1<<0) + #define IEEE80211_STATMASK_RSSI (1<<1) + #define IEEE80211_STATMASK_NOISE (1<<2) +@@ -377,6 +395,8 @@ + u8 mask; + u8 freq; + u16 len; ++ u64 tsf; ++ u32 beacon_time; + }; + + /* IEEE 802.11 requires that STA supports concurrent reception of at least +@@ -608,6 +628,28 @@ + struct ieee80211_info_element info_element[0]; + } __attribute__ ((packed)); + ++struct ieee80211_channel_switch { ++ u8 id; ++ u8 len; ++ u8 mode; ++ u8 channel; ++ u8 count; ++} __attribute__ ((packed)); ++ ++struct ieee80211_action { ++ struct ieee80211_hdr_3addr header; ++ u8 category; ++ u8 action; ++ union { ++ struct ieee80211_action_exchange { ++ u8 token; ++ struct ieee80211_info_element info_element[0]; ++ } exchange; ++ struct ieee80211_channel_switch channel_switch; ++ ++ } format; ++} __attribute__ ((packed)); ++ + struct ieee80211_disassoc { + struct ieee80211_hdr_3addr header; + __le16 reason; +@@ -692,7 +734,15 @@ + /* QoS structure */ + #define NETWORK_HAS_QOS_PARAMETERS (1<<3) + #define NETWORK_HAS_QOS_INFORMATION (1<<4) +-#define NETWORK_HAS_QOS_MASK (NETWORK_HAS_QOS_PARAMETERS | NETWORK_HAS_QOS_INFORMATION) ++#define NETWORK_HAS_QOS_MASK (NETWORK_HAS_QOS_PARAMETERS | \ ++ NETWORK_HAS_QOS_INFORMATION) ++ ++/* 802.11h */ ++#define NETWORK_HAS_POWER_CONSTRAINT (1<<5) ++#define NETWORK_HAS_CSA (1<<6) ++#define NETWORK_HAS_QUIET (1<<7) ++#define NETWORK_HAS_IBSS_DFS (1<<8) ++#define NETWORK_HAS_TPC_REPORT (1<<9) + + #define QOS_QUEUE_NUM 4 + #define QOS_OUI_LEN 3 +@@ -748,6 +798,91 @@ + + /*******************************************************/ + ++enum { /* ieee80211_basic_report.map */ ++ IEEE80211_BASIC_MAP_BSS = (1 << 0), ++ IEEE80211_BASIC_MAP_OFDM = (1 << 1), ++ IEEE80211_BASIC_MAP_UNIDENTIFIED = (1 << 2), ++ IEEE80211_BASIC_MAP_RADAR = (1 << 3), ++ IEEE80211_BASIC_MAP_UNMEASURED = (1 << 4), ++ /* Bits 5-7 are reserved */ ++ ++}; ++struct ieee80211_basic_report { ++ u8 channel; ++ __le64 start_time; ++ __le16 duration; ++ u8 map; ++} __attribute__ ((packed)); ++ ++enum { /* ieee80211_measurement_request.mode */ ++ /* Bit 0 is reserved */ ++ IEEE80211_MEASUREMENT_ENABLE = (1 << 1), ++ IEEE80211_MEASUREMENT_REQUEST = (1 << 2), ++ IEEE80211_MEASUREMENT_REPORT = (1 << 3), ++ /* Bits 4-7 are reserved */ ++}; ++ ++enum { ++ IEEE80211_REPORT_BASIC = 0, /* required */ ++ IEEE80211_REPORT_CCA = 1, /* optional */ ++ IEEE80211_REPORT_RPI = 2, /* optional */ ++ /* 3-255 reserved */ ++}; ++ ++struct ieee80211_measurement_params { ++ u8 channel; ++ __le64 start_time; ++ __le16 duration; ++} __attribute__ ((packed)); ++ ++struct ieee80211_measurement_request { ++ struct ieee80211_info_element ie; ++ u8 token; ++ u8 mode; ++ u8 type; ++ struct ieee80211_measurement_params params[0]; ++} __attribute__ ((packed)); ++ ++struct ieee80211_measurement_report { ++ struct ieee80211_info_element ie; ++ u8 token; ++ u8 mode; ++ u8 type; ++ union { ++ struct ieee80211_basic_report basic[0]; ++ } u; ++} __attribute__ ((packed)); ++ ++struct ieee80211_tpc_report { ++ u8 transmit_power; ++ u8 link_margin; ++} __attribute__ ((packed)); ++ ++struct ieee80211_channel_map { ++ u8 channel; ++ u8 map; ++} __attribute__ ((packed)); ++ ++struct ieee80211_ibss_dfs { ++ struct ieee80211_info_element ie; ++ u8 owner[ETH_ALEN]; ++ u8 recovery_interval; ++ struct ieee80211_channel_map channel_map[0]; ++}; ++ ++struct ieee80211_csa { ++ u8 mode; ++ u8 channel; ++ u8 count; ++} __attribute__ ((packed)); ++ ++struct ieee80211_quiet { ++ u8 count; ++ u8 period; ++ u8 duration; ++ u8 offset; ++} __attribute__ ((packed)); ++ + struct ieee80211_network { + /* These entries are used to identify a unique network */ + u8 bssid[ETH_ALEN]; +@@ -767,7 +902,7 @@ + u8 rates_ex_len; + unsigned long last_scanned; + u8 mode; +- u8 flags; ++ u32 flags; + u32 last_associate; + u32 time_stamp[2]; + u16 beacon_interval; +@@ -779,6 +914,25 @@ + u8 rsn_ie[MAX_WPA_IE_LEN]; + size_t rsn_ie_len; + struct ieee80211_tim_parameters tim; ++ ++ /* 802.11h info */ ++ ++ /* Power Constraint - mandatory if spctrm mgmt required */ ++ u8 power_constraint; ++ ++ /* TPC Report - mandatory if spctrm mgmt required */ ++ struct ieee80211_tpc_report tpc_report; ++ ++ /* IBSS DFS - mandatory if spctrm mgmt required and IBSS ++ * NOTE: This is variable length and so must be allocated dynamically */ ++ struct ieee80211_ibss_dfs *ibss_dfs; ++ ++ /* Channel Switch Announcement - optional if spctrm mgmt required */ ++ struct ieee80211_csa csa; ++ ++ /* Quiet - optional if spctrm mgmt required */ ++ struct ieee80211_quiet quiet; ++ + struct list_head list; + }; + +@@ -924,7 +1078,10 @@ + int (*handle_auth) (struct net_device * dev, + struct ieee80211_auth * auth); + int (*handle_deauth) (struct net_device * dev, +- struct ieee80211_auth * auth); ++ struct ieee80211_deauth * auth); ++ int (*handle_action) (struct net_device * dev, ++ struct ieee80211_action * action, ++ struct ieee80211_rx_stats * stats); + int (*handle_disassoc) (struct net_device * dev, + struct ieee80211_disassoc * assoc); + int (*handle_beacon) (struct net_device * dev, +@@ -1093,6 +1250,7 @@ + extern void ieee80211_rx_mgt(struct ieee80211_device *ieee, + struct ieee80211_hdr_4addr *header, + struct ieee80211_rx_stats *stats); ++extern void ieee80211_network_reset(struct ieee80211_network *network); + + /* ieee80211_geo.c */ + extern const struct ieee80211_geo *ieee80211_get_geo(struct ieee80211_device +@@ -1105,6 +1263,11 @@ + extern int ieee80211_channel_to_index(struct ieee80211_device *ieee, + u8 channel); + extern u8 ieee80211_freq_to_channel(struct ieee80211_device *ieee, u32 freq); ++extern u8 ieee80211_get_channel_flags(struct ieee80211_device *ieee, ++ u8 channel); ++extern const struct ieee80211_channel *ieee80211_get_channel(struct ++ ieee80211_device ++ *ieee, u8 channel); + + /* ieee80211_wx.c */ + extern int ieee80211_wx_get_scan(struct ieee80211_device *ieee, +@@ -1122,6 +1285,14 @@ + extern int ieee80211_wx_get_encodeext(struct ieee80211_device *ieee, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra); ++extern int ieee80211_wx_set_auth(struct net_device *dev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, ++ char *extra); ++extern int ieee80211_wx_get_auth(struct net_device *dev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, ++ char *extra); + + static inline void ieee80211_increment_scans(struct ieee80211_device *ieee) + { +diff -Nur linux-2.6.16/include/net/sock.h linux-2.6.16-bcm43xx/include/net/sock.h +--- linux-2.6.16/include/net/sock.h 2006-03-20 06:53:29.000000000 +0100 ++++ linux-2.6.16-bcm43xx/include/net/sock.h 2006-03-28 22:16:14.000000000 +0200 +@@ -478,9 +478,9 @@ + rc = __condition; \ + if (!rc) { \ + *(__timeo) = schedule_timeout(*(__timeo)); \ ++ rc = __condition; \ + } \ + lock_sock(__sk); \ +- rc = __condition; \ + rc; \ + }) + +diff -Nur linux-2.6.16/net/core/dev.c linux-2.6.16-bcm43xx/net/core/dev.c +--- linux-2.6.16/net/core/dev.c 2006-03-20 06:53:29.000000000 +0100 ++++ linux-2.6.16-bcm43xx/net/core/dev.c 2006-03-28 22:16:14.000000000 +0200 +@@ -110,10 +110,8 @@ + #include <linux/netpoll.h> + #include <linux/rcupdate.h> + #include <linux/delay.h> +-#ifdef CONFIG_NET_RADIO +-#include <linux/wireless.h> /* Note : will define WIRELESS_EXT */ ++#include <linux/wireless.h> + #include <net/iw_handler.h> +-#endif /* CONFIG_NET_RADIO */ + #include <asm/current.h> + + /* +@@ -1448,8 +1446,29 @@ + { + struct net_device *dev = skb->dev; + +- if (dev->master) ++ if (dev->master) { ++ /* ++ * On bonding slaves other than the currently active ++ * slave, suppress duplicates except for 802.3ad ++ * ETH_P_SLOW and alb non-mcast/bcast. ++ */ ++ if (dev->priv_flags & IFF_SLAVE_INACTIVE) { ++ if (dev->master->priv_flags & IFF_MASTER_ALB) { ++ if (skb->pkt_type != PACKET_BROADCAST && ++ skb->pkt_type != PACKET_MULTICAST) ++ goto keep; ++ } ++ ++ if (dev->master->priv_flags & IFF_MASTER_8023AD && ++ skb->protocol == __constant_htons(ETH_P_SLOW)) ++ goto keep; ++ ++ kfree_skb(skb); ++ return NULL; ++ } ++keep: + skb->dev = dev->master; ++ } + + return dev; + } +@@ -1593,6 +1612,9 @@ + + orig_dev = skb_bond(skb); + ++ if (!orig_dev) ++ return NET_RX_DROP; ++ + __get_cpu_var(netdev_rx_stat).total++; + + skb->h.raw = skb->nh.raw = skb->data; +@@ -2028,7 +2050,7 @@ + .release = seq_release, + }; + +-#ifdef WIRELESS_EXT ++#ifdef CONFIG_WIRELESS_EXT + extern int wireless_proc_init(void); + #else + #define wireless_proc_init() 0 +@@ -2582,7 +2604,7 @@ + ret = -EFAULT; + return ret; + } +-#ifdef WIRELESS_EXT ++#ifdef CONFIG_WIRELESS_EXT + /* Take care of Wireless Extensions */ + if (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) { + /* If command is `set a parameter', or +@@ -2603,7 +2625,7 @@ + ret = -EFAULT; + return ret; + } +-#endif /* WIRELESS_EXT */ ++#endif /* CONFIG_WIRELESS_EXT */ + return -EINVAL; + } + } +diff -Nur linux-2.6.16/net/core/Makefile linux-2.6.16-bcm43xx/net/core/Makefile +--- linux-2.6.16/net/core/Makefile 2006-03-20 06:53:29.000000000 +0100 ++++ linux-2.6.16-bcm43xx/net/core/Makefile 2006-03-28 22:16:14.000000000 +0200 +@@ -14,5 +14,5 @@ + obj-$(CONFIG_SYSFS) += net-sysfs.o + obj-$(CONFIG_NET_DIVERT) += dv.o + obj-$(CONFIG_NET_PKTGEN) += pktgen.o +-obj-$(CONFIG_NET_RADIO) += wireless.o ++obj-$(CONFIG_WIRELESS_EXT) += wireless.o + obj-$(CONFIG_NETPOLL) += netpoll.o +diff -Nur linux-2.6.16/net/d80211/aes.c linux-2.6.16-bcm43xx/net/d80211/aes.c +--- linux-2.6.16/net/d80211/aes.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.16-bcm43xx/net/d80211/aes.c 2006-03-28 22:16:14.000000000 +0200 +@@ -0,0 +1,564 @@ ++/* Based on Rijndael implementation that has been placed in the public domain, ++ * although heavily modified. ++ * ++ * Modifications Copyright 2003, Instant802 Networks, Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ * Optimized both speed and size by removing not used key lengths (only ++ * 128-bit is used in IEEE 802.11i). ++ */ ++ ++/* Use 256-byte Te4 table instead of larger 1024-byte */ ++#define SMALL_TE4 ++ ++/* Save data size by using only one 1k table, but with a drawback of having to ++ * rotate entries at lookup. This can be useful, if the CPU supports free ++ * rotate on memory read. However, if this is not the case, this is much slower ++ * than four-table implementation. */ ++/* #define ONLY_ONE_TABLE */ ++ ++ ++/* --- start of code that is based on public domain AES implementation --- */ ++ ++/** ++ * rijndael-alg-fst.c ++ * ++ * @version 3.0 (December 2000) ++ * ++ * Optimised ANSI C code for the Rijndael cipher (now AES) ++ * ++ * @author Vincent Rijmen <vincent.rijmen@esat.kuleuven.ac.be> ++ * @author Antoon Bosselaers <antoon.bosselaers@esat.kuleuven.ac.be> ++ * @author Paulo Barreto <paulo.barreto@terra.com.br> ++ * ++ * This code is hereby placed in the public domain. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS ++ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED ++ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE ++ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR ++ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF ++ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR ++ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, ++ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE ++ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, ++ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++/* ++Te0[x] = S [x].[02, 01, 01, 03]; ++Te1[x] = S [x].[03, 02, 01, 01]; ++Te2[x] = S [x].[01, 03, 02, 01]; ++Te3[x] = S [x].[01, 01, 03, 02]; ++Te4[x] = S [x].[01, 01, 01, 01]; ++*/ ++ ++static const u32 Te0[256] = ++{ ++ 0xc66363a5U, 0xf87c7c84U, 0xee777799U, 0xf67b7b8dU, ++ 0xfff2f20dU, 0xd66b6bbdU, 0xde6f6fb1U, 0x91c5c554U, ++ 0x60303050U, 0x02010103U, 0xce6767a9U, 0x562b2b7dU, ++ 0xe7fefe19U, 0xb5d7d762U, 0x4dababe6U, 0xec76769aU, ++ 0x8fcaca45U, 0x1f82829dU, 0x89c9c940U, 0xfa7d7d87U, ++ 0xeffafa15U, 0xb25959ebU, 0x8e4747c9U, 0xfbf0f00bU, ++ 0x41adadecU, 0xb3d4d467U, 0x5fa2a2fdU, 0x45afafeaU, ++ 0x239c9cbfU, 0x53a4a4f7U, 0xe4727296U, 0x9bc0c05bU, ++ 0x75b7b7c2U, 0xe1fdfd1cU, 0x3d9393aeU, 0x4c26266aU, ++ 0x6c36365aU, 0x7e3f3f41U, 0xf5f7f702U, 0x83cccc4fU, ++ 0x6834345cU, 0x51a5a5f4U, 0xd1e5e534U, 0xf9f1f108U, ++ 0xe2717193U, 0xabd8d873U, 0x62313153U, 0x2a15153fU, ++ 0x0804040cU, 0x95c7c752U, 0x46232365U, 0x9dc3c35eU, ++ 0x30181828U, 0x379696a1U, 0x0a05050fU, 0x2f9a9ab5U, ++ 0x0e070709U, 0x24121236U, 0x1b80809bU, 0xdfe2e23dU, ++ 0xcdebeb26U, 0x4e272769U, 0x7fb2b2cdU, 0xea75759fU, ++ 0x1209091bU, 0x1d83839eU, 0x582c2c74U, 0x341a1a2eU, ++ 0x361b1b2dU, 0xdc6e6eb2U, 0xb45a5aeeU, 0x5ba0a0fbU, ++ 0xa45252f6U, 0x763b3b4dU, 0xb7d6d661U, 0x7db3b3ceU, ++ 0x5229297bU, 0xdde3e33eU, 0x5e2f2f71U, 0x13848497U, ++ 0xa65353f5U, 0xb9d1d168U, 0x00000000U, 0xc1eded2cU, ++ 0x40202060U, 0xe3fcfc1fU, 0x79b1b1c8U, 0xb65b5bedU, ++ 0xd46a6abeU, 0x8dcbcb46U, 0x67bebed9U, 0x7239394bU, ++ 0x944a4adeU, 0x984c4cd4U, 0xb05858e8U, 0x85cfcf4aU, ++ 0xbbd0d06bU, 0xc5efef2aU, 0x4faaaae5U, 0xedfbfb16U, ++ 0x864343c5U, 0x9a4d4dd7U, 0x66333355U, 0x11858594U, ++ 0x8a4545cfU, 0xe9f9f910U, 0x04020206U, 0xfe7f7f81U, ++ 0xa05050f0U, 0x783c3c44U, 0x259f9fbaU, 0x4ba8a8e3U, ++ 0xa25151f3U, 0x5da3a3feU, 0x804040c0U, 0x058f8f8aU, ++ 0x3f9292adU, 0x219d9dbcU, 0x70383848U, 0xf1f5f504U, ++ 0x63bcbcdfU, 0x77b6b6c1U, 0xafdada75U, 0x42212163U, ++ 0x20101030U, 0xe5ffff1aU, 0xfdf3f30eU, 0xbfd2d26dU, ++ 0x81cdcd4cU, 0x180c0c14U, 0x26131335U, 0xc3ecec2fU, ++ 0xbe5f5fe1U, 0x359797a2U, 0x884444ccU, 0x2e171739U, ++ 0x93c4c457U, 0x55a7a7f2U, 0xfc7e7e82U, 0x7a3d3d47U, ++ 0xc86464acU, 0xba5d5de7U, 0x3219192bU, 0xe6737395U, ++ 0xc06060a0U, 0x19818198U, 0x9e4f4fd1U, 0xa3dcdc7fU, ++ 0x44222266U, 0x542a2a7eU, 0x3b9090abU, 0x0b888883U, ++ 0x8c4646caU, 0xc7eeee29U, 0x6bb8b8d3U, 0x2814143cU, ++ 0xa7dede79U, 0xbc5e5ee2U, 0x160b0b1dU, 0xaddbdb76U, ++ 0xdbe0e03bU, 0x64323256U, 0x743a3a4eU, 0x140a0a1eU, ++ 0x924949dbU, 0x0c06060aU, 0x4824246cU, 0xb85c5ce4U, ++ 0x9fc2c25dU, 0xbdd3d36eU, 0x43acacefU, 0xc46262a6U, ++ 0x399191a8U, 0x319595a4U, 0xd3e4e437U, 0xf279798bU, ++ 0xd5e7e732U, 0x8bc8c843U, 0x6e373759U, 0xda6d6db7U, ++ 0x018d8d8cU, 0xb1d5d564U, 0x9c4e4ed2U, 0x49a9a9e0U, ++ 0xd86c6cb4U, 0xac5656faU, 0xf3f4f407U, 0xcfeaea25U, ++ 0xca6565afU, 0xf47a7a8eU, 0x47aeaee9U, 0x10080818U, ++ 0x6fbabad5U, 0xf0787888U, 0x4a25256fU, 0x5c2e2e72U, ++ 0x381c1c24U, 0x57a6a6f1U, 0x73b4b4c7U, 0x97c6c651U, ++ 0xcbe8e823U, 0xa1dddd7cU, 0xe874749cU, 0x3e1f1f21U, ++ 0x964b4bddU, 0x61bdbddcU, 0x0d8b8b86U, 0x0f8a8a85U, ++ 0xe0707090U, 0x7c3e3e42U, 0x71b5b5c4U, 0xcc6666aaU, ++ 0x904848d8U, 0x06030305U, 0xf7f6f601U, 0x1c0e0e12U, ++ 0xc26161a3U, 0x6a35355fU, 0xae5757f9U, 0x69b9b9d0U, ++ 0x17868691U, 0x99c1c158U, 0x3a1d1d27U, 0x279e9eb9U, ++ 0xd9e1e138U, 0xebf8f813U, 0x2b9898b3U, 0x22111133U, ++ 0xd26969bbU, 0xa9d9d970U, 0x078e8e89U, 0x339494a7U, ++ 0x2d9b9bb6U, 0x3c1e1e22U, 0x15878792U, 0xc9e9e920U, ++ 0x87cece49U, 0xaa5555ffU, 0x50282878U, 0xa5dfdf7aU, ++ 0x038c8c8fU, 0x59a1a1f8U, 0x09898980U, 0x1a0d0d17U, ++ 0x65bfbfdaU, 0xd7e6e631U, 0x844242c6U, 0xd06868b8U, ++ 0x824141c3U, 0x299999b0U, 0x5a2d2d77U, 0x1e0f0f11U, ++ 0x7bb0b0cbU, 0xa85454fcU, 0x6dbbbbd6U, 0x2c16163aU, ++}; ++ ++#ifndef ONLY_ONE_TABLE ++static const u32 Te1[256] = ++{ ++ 0xa5c66363U, 0x84f87c7cU, 0x99ee7777U, 0x8df67b7bU, ++ 0x0dfff2f2U, 0xbdd66b6bU, 0xb1de6f6fU, 0x5491c5c5U, ++ 0x50603030U, 0x03020101U, 0xa9ce6767U, 0x7d562b2bU, ++ 0x19e7fefeU, 0x62b5d7d7U, 0xe64dababU, 0x9aec7676U, ++ 0x458fcacaU, 0x9d1f8282U, 0x4089c9c9U, 0x87fa7d7dU, ++ 0x15effafaU, 0xebb25959U, 0xc98e4747U, 0x0bfbf0f0U, ++ 0xec41adadU, 0x67b3d4d4U, 0xfd5fa2a2U, 0xea45afafU, ++ 0xbf239c9cU, 0xf753a4a4U, 0x96e47272U, 0x5b9bc0c0U, ++ 0xc275b7b7U, 0x1ce1fdfdU, 0xae3d9393U, 0x6a4c2626U, ++ 0x5a6c3636U, 0x417e3f3fU, 0x02f5f7f7U, 0x4f83ccccU, ++ 0x5c683434U, 0xf451a5a5U, 0x34d1e5e5U, 0x08f9f1f1U, ++ 0x93e27171U, 0x73abd8d8U, 0x53623131U, 0x3f2a1515U, ++ 0x0c080404U, 0x5295c7c7U, 0x65462323U, 0x5e9dc3c3U, ++ 0x28301818U, 0xa1379696U, 0x0f0a0505U, 0xb52f9a9aU, ++ 0x090e0707U, 0x36241212U, 0x9b1b8080U, 0x3ddfe2e2U, ++ 0x26cdebebU, 0x694e2727U, 0xcd7fb2b2U, 0x9fea7575U, ++ 0x1b120909U, 0x9e1d8383U, 0x74582c2cU, 0x2e341a1aU, ++ 0x2d361b1bU, 0xb2dc6e6eU, 0xeeb45a5aU, 0xfb5ba0a0U, ++ 0xf6a45252U, 0x4d763b3bU, 0x61b7d6d6U, 0xce7db3b3U, ++ 0x7b522929U, 0x3edde3e3U, 0x715e2f2fU, 0x97138484U, ++ 0xf5a65353U, 0x68b9d1d1U, 0x00000000U, 0x2cc1ededU, ++ 0x60402020U, 0x1fe3fcfcU, 0xc879b1b1U, 0xedb65b5bU, ++ 0xbed46a6aU, 0x468dcbcbU, 0xd967bebeU, 0x4b723939U, ++ 0xde944a4aU, 0xd4984c4cU, 0xe8b05858U, 0x4a85cfcfU, ++ 0x6bbbd0d0U, 0x2ac5efefU, 0xe54faaaaU, 0x16edfbfbU, ++ 0xc5864343U, 0xd79a4d4dU, 0x55663333U, 0x94118585U, ++ 0xcf8a4545U, 0x10e9f9f9U, 0x06040202U, 0x81fe7f7fU, ++ 0xf0a05050U, 0x44783c3cU, 0xba259f9fU, 0xe34ba8a8U, ++ 0xf3a25151U, 0xfe5da3a3U, 0xc0804040U, 0x8a058f8fU, ++ 0xad3f9292U, 0xbc219d9dU, 0x48703838U, 0x04f1f5f5U, ++ 0xdf63bcbcU, 0xc177b6b6U, 0x75afdadaU, 0x63422121U, ++ 0x30201010U, 0x1ae5ffffU, 0x0efdf3f3U, 0x6dbfd2d2U, ++ 0x4c81cdcdU, 0x14180c0cU, 0x35261313U, 0x2fc3ececU, ++ 0xe1be5f5fU, 0xa2359797U, 0xcc884444U, 0x392e1717U, ++ 0x5793c4c4U, 0xf255a7a7U, 0x82fc7e7eU, 0x477a3d3dU, ++ 0xacc86464U, 0xe7ba5d5dU, 0x2b321919U, 0x95e67373U, ++ 0xa0c06060U, 0x98198181U, 0xd19e4f4fU, 0x7fa3dcdcU, ++ 0x66442222U, 0x7e542a2aU, 0xab3b9090U, 0x830b8888U, ++ 0xca8c4646U, 0x29c7eeeeU, 0xd36bb8b8U, 0x3c281414U, ++ 0x79a7dedeU, 0xe2bc5e5eU, 0x1d160b0bU, 0x76addbdbU, ++ 0x3bdbe0e0U, 0x56643232U, 0x4e743a3aU, 0x1e140a0aU, ++ 0xdb924949U, 0x0a0c0606U, 0x6c482424U, 0xe4b85c5cU, ++ 0x5d9fc2c2U, 0x6ebdd3d3U, 0xef43acacU, 0xa6c46262U, ++ 0xa8399191U, 0xa4319595U, 0x37d3e4e4U, 0x8bf27979U, ++ 0x32d5e7e7U, 0x438bc8c8U, 0x596e3737U, 0xb7da6d6dU, ++ 0x8c018d8dU, 0x64b1d5d5U, 0xd29c4e4eU, 0xe049a9a9U, ++ 0xb4d86c6cU, 0xfaac5656U, 0x07f3f4f4U, 0x25cfeaeaU, ++ 0xafca6565U, 0x8ef47a7aU, 0xe947aeaeU, 0x18100808U, ++ 0xd56fbabaU, 0x88f07878U, 0x6f4a2525U, 0x725c2e2eU, ++ 0x24381c1cU, 0xf157a6a6U, 0xc773b4b4U, 0x5197c6c6U, ++ 0x23cbe8e8U, 0x7ca1ddddU, 0x9ce87474U, 0x213e1f1fU, ++ 0xdd964b4bU, 0xdc61bdbdU, 0x860d8b8bU, 0x850f8a8aU, ++ 0x90e07070U, 0x427c3e3eU, 0xc471b5b5U, 0xaacc6666U, ++ 0xd8904848U, 0x05060303U, 0x01f7f6f6U, 0x121c0e0eU, ++ 0xa3c26161U, 0x5f6a3535U, 0xf9ae5757U, 0xd069b9b9U, ++ 0x91178686U, 0x5899c1c1U, 0x273a1d1dU, 0xb9279e9eU, ++ 0x38d9e1e1U, 0x13ebf8f8U, 0xb32b9898U, 0x33221111U, ++ 0xbbd26969U, 0x70a9d9d9U, 0x89078e8eU, 0xa7339494U, ++ 0xb62d9b9bU, 0x223c1e1eU, 0x92158787U, 0x20c9e9e9U, ++ 0x4987ceceU, 0xffaa5555U, 0x78502828U, 0x7aa5dfdfU, ++ 0x8f038c8cU, 0xf859a1a1U, 0x80098989U, 0x171a0d0dU, ++ 0xda65bfbfU, 0x31d7e6e6U, 0xc6844242U, 0xb8d06868U, ++ 0xc3824141U, 0xb0299999U, 0x775a2d2dU, 0x111e0f0fU, ++ 0xcb7bb0b0U, 0xfca85454U, 0xd66dbbbbU, 0x3a2c1616U, ++}; ++ ++static const u32 Te2[256] = ++{ ++ 0x63a5c663U, 0x7c84f87cU, 0x7799ee77U, 0x7b8df67bU, ++ 0xf20dfff2U, 0x6bbdd66bU, 0x6fb1de6fU, 0xc55491c5U, ++ 0x30506030U, 0x01030201U, 0x67a9ce67U, 0x2b7d562bU, ++ 0xfe19e7feU, 0xd762b5d7U, 0xabe64dabU, 0x769aec76U, ++ 0xca458fcaU, 0x829d1f82U, 0xc94089c9U, 0x7d87fa7dU, ++ 0xfa15effaU, 0x59ebb259U, 0x47c98e47U, 0xf00bfbf0U, ++ 0xadec41adU, 0xd467b3d4U, 0xa2fd5fa2U, 0xafea45afU, ++ 0x9cbf239cU, 0xa4f753a4U, 0x7296e472U, 0xc05b9bc0U, ++ 0xb7c275b7U, 0xfd1ce1fdU, 0x93ae3d93U, 0x266a4c26U, ++ 0x365a6c36U, 0x3f417e3fU, 0xf702f5f7U, 0xcc4f83ccU, ++ 0x345c6834U, 0xa5f451a5U, 0xe534d1e5U, 0xf108f9f1U, ++ 0x7193e271U, 0xd873abd8U, 0x31536231U, 0x153f2a15U, ++ 0x040c0804U, 0xc75295c7U, 0x23654623U, 0xc35e9dc3U, ++ 0x18283018U, 0x96a13796U, 0x050f0a05U, 0x9ab52f9aU, ++ 0x07090e07U, 0x12362412U, 0x809b1b80U, 0xe23ddfe2U, ++ 0xeb26cdebU, 0x27694e27U, 0xb2cd7fb2U, 0x759fea75U, ++ 0x091b1209U, 0x839e1d83U, 0x2c74582cU, 0x1a2e341aU, ++ 0x1b2d361bU, 0x6eb2dc6eU, 0x5aeeb45aU, 0xa0fb5ba0U, ++ 0x52f6a452U, 0x3b4d763bU, 0xd661b7d6U, 0xb3ce7db3U, ++ 0x297b5229U, 0xe33edde3U, 0x2f715e2fU, 0x84971384U, ++ 0x53f5a653U, 0xd168b9d1U, 0x00000000U, 0xed2cc1edU, ++ 0x20604020U, 0xfc1fe3fcU, 0xb1c879b1U, 0x5bedb65bU, ++ 0x6abed46aU, 0xcb468dcbU, 0xbed967beU, 0x394b7239U, ++ 0x4ade944aU, 0x4cd4984cU, 0x58e8b058U, 0xcf4a85cfU, ++ 0xd06bbbd0U, 0xef2ac5efU, 0xaae54faaU, 0xfb16edfbU, ++ 0x43c58643U, 0x4dd79a4dU, 0x33556633U, 0x85941185U, ++ 0x45cf8a45U, 0xf910e9f9U, 0x02060402U, 0x7f81fe7fU, ++ 0x50f0a050U, 0x3c44783cU, 0x9fba259fU, 0xa8e34ba8U, ++ 0x51f3a251U, 0xa3fe5da3U, 0x40c08040U, 0x8f8a058fU, ++ 0x92ad3f92U, 0x9dbc219dU, 0x38487038U, 0xf504f1f5U, ++ 0xbcdf63bcU, 0xb6c177b6U, 0xda75afdaU, 0x21634221U, ++ 0x10302010U, 0xff1ae5ffU, 0xf30efdf3U, 0xd26dbfd2U, ++ 0xcd4c81cdU, 0x0c14180cU, 0x13352613U, 0xec2fc3ecU, ++ 0x5fe1be5fU, 0x97a23597U, 0x44cc8844U, 0x17392e17U, ++ 0xc45793c4U, 0xa7f255a7U, 0x7e82fc7eU, 0x3d477a3dU, ++ 0x64acc864U, 0x5de7ba5dU, 0x192b3219U, 0x7395e673U, ++ 0x60a0c060U, 0x81981981U, 0x4fd19e4fU, 0xdc7fa3dcU, ++ 0x22664422U, 0x2a7e542aU, 0x90ab3b90U, 0x88830b88U, ++ 0x46ca8c46U, 0xee29c7eeU, 0xb8d36bb8U, 0x143c2814U, ++ 0xde79a7deU, 0x5ee2bc5eU, 0x0b1d160bU, 0xdb76addbU, ++ 0xe03bdbe0U, 0x32566432U, 0x3a4e743aU, 0x0a1e140aU, ++ 0x49db9249U, 0x060a0c06U, 0x246c4824U, 0x5ce4b85cU, ++ 0xc25d9fc2U, 0xd36ebdd3U, 0xacef43acU, 0x62a6c462U, ++ 0x91a83991U, 0x95a43195U, 0xe437d3e4U, 0x798bf279U, ++ 0xe732d5e7U, 0xc8438bc8U, 0x37596e37U, 0x6db7da6dU, ++ 0x8d8c018dU, 0xd564b1d5U, 0x4ed29c4eU, 0xa9e049a9U, ++ 0x6cb4d86cU, 0x56faac56U, 0xf407f3f4U, 0xea25cfeaU, ++ 0x65afca65U, 0x7a8ef47aU, 0xaee947aeU, 0x08181008U, ++ 0xbad56fbaU, 0x7888f078U, 0x256f4a25U, 0x2e725c2eU, ++ 0x1c24381cU, 0xa6f157a6U, 0xb4c773b4U, 0xc65197c6U, ++ 0xe823cbe8U, 0xdd7ca1ddU, 0x749ce874U, 0x1f213e1fU, ++ 0x4bdd964bU, 0xbddc61bdU, 0x8b860d8bU, 0x8a850f8aU, ++ 0x7090e070U, 0x3e427c3eU, 0xb5c471b5U, 0x66aacc66U, ++ 0x48d89048U, 0x03050603U, 0xf601f7f6U, 0x0e121c0eU, ++ 0x61a3c261U, 0x355f6a35U, 0x57f9ae57U, 0xb9d069b9U, ++ 0x86911786U, 0xc15899c1U, 0x1d273a1dU, 0x9eb9279eU, ++ 0xe138d9e1U, 0xf813ebf8U, 0x98b32b98U, 0x11332211U, ++ 0x69bbd269U, 0xd970a9d9U, 0x8e89078eU, 0x94a73394U, ++ 0x9bb62d9bU, 0x1e223c1eU, 0x87921587U, 0xe920c9e9U, ++ 0xce4987ceU, 0x55ffaa55U, 0x28785028U, 0xdf7aa5dfU, ++ 0x8c8f038cU, 0xa1f859a1U, 0x89800989U, 0x0d171a0dU, ++ 0xbfda65bfU, 0xe631d7e6U, 0x42c68442U, 0x68b8d068U, ++ 0x41c38241U, 0x99b02999U, 0x2d775a2dU, 0x0f111e0fU, ++ 0xb0cb7bb0U, 0x54fca854U, 0xbbd66dbbU, 0x163a2c16U, ++}; ++ ++static const u32 Te3[256] = ++{ ++ 0x6363a5c6U, 0x7c7c84f8U, 0x777799eeU, 0x7b7b8df6U, ++ 0xf2f20dffU, 0x6b6bbdd6U, 0x6f6fb1deU, 0xc5c55491U, ++ 0x30305060U, 0x01010302U, 0x6767a9ceU, 0x2b2b7d56U, ++ 0xfefe19e7U, 0xd7d762b5U, 0xababe64dU, 0x76769aecU, ++ 0xcaca458fU, 0x82829d1fU, 0xc9c94089U, 0x7d7d87faU, ++ 0xfafa15efU, 0x5959ebb2U, 0x4747c98eU, 0xf0f00bfbU, ++ 0xadadec41U, 0xd4d467b3U, 0xa2a2fd5fU, 0xafafea45U, ++ 0x9c9cbf23U, 0xa4a4f753U, 0x727296e4U, 0xc0c05b9bU, ++ 0xb7b7c275U, 0xfdfd1ce1U, 0x9393ae3dU, 0x26266a4cU, ++ 0x36365a6cU, 0x3f3f417eU, 0xf7f702f5U, 0xcccc4f83U, ++ 0x34345c68U, 0xa5a5f451U, 0xe5e534d1U, 0xf1f108f9U, ++ 0x717193e2U, 0xd8d873abU, 0x31315362U, 0x15153f2aU, ++ 0x04040c08U, 0xc7c75295U, 0x23236546U, 0xc3c35e9dU, ++ 0x18182830U, 0x9696a137U, 0x05050f0aU, 0x9a9ab52fU, ++ 0x0707090eU, 0x12123624U, 0x80809b1bU, 0xe2e23ddfU, ++ 0xebeb26cdU, 0x2727694eU, 0xb2b2cd7fU, 0x75759feaU, ++ 0x09091b12U, 0x83839e1dU, 0x2c2c7458U, 0x1a1a2e34U, ++ 0x1b1b2d36U, 0x6e6eb2dcU, 0x5a5aeeb4U, 0xa0a0fb5bU, ++ 0x5252f6a4U, 0x3b3b4d76U, 0xd6d661b7U, 0xb3b3ce7dU, ++ 0x29297b52U, 0xe3e33eddU, 0x2f2f715eU, 0x84849713U, ++ 0x5353f5a6U, 0xd1d168b9U, 0x00000000U, 0xeded2cc1U, ++ 0x20206040U, 0xfcfc1fe3U, 0xb1b1c879U, 0x5b5bedb6U, ++ 0x6a6abed4U, 0xcbcb468dU, 0xbebed967U, 0x39394b72U, ++ 0x4a4ade94U, 0x4c4cd498U, 0x5858e8b0U, 0xcfcf4a85U, ++ 0xd0d06bbbU, 0xefef2ac5U, 0xaaaae54fU, 0xfbfb16edU, ++ 0x4343c586U, 0x4d4dd79aU, 0x33335566U, 0x85859411U, ++ 0x4545cf8aU, 0xf9f910e9U, 0x02020604U, 0x7f7f81feU, ++ 0x5050f0a0U, 0x3c3c4478U, 0x9f9fba25U, 0xa8a8e34bU, ++ 0x5151f3a2U, 0xa3a3fe5dU, 0x4040c080U, 0x8f8f8a05U, ++ 0x9292ad3fU, 0x9d9dbc21U, 0x38384870U, 0xf5f504f1U, ++ 0xbcbcdf63U, 0xb6b6c177U, 0xdada75afU, 0x21216342U, ++ 0x10103020U, 0xffff1ae5U, 0xf3f30efdU, 0xd2d26dbfU, ++ 0xcdcd4c81U, 0x0c0c1418U, 0x13133526U, 0xecec2fc3U, ++ 0x5f5fe1beU, 0x9797a235U, 0x4444cc88U, 0x1717392eU, ++ 0xc4c45793U, 0xa7a7f255U, 0x7e7e82fcU, 0x3d3d477aU, ++ 0x6464acc8U, 0x5d5de7baU, 0x19192b32U, 0x737395e6U, ++ 0x6060a0c0U, 0x81819819U, 0x4f4fd19eU, 0xdcdc7fa3U, ++ 0x22226644U, 0x2a2a7e54U, 0x9090ab3bU, 0x8888830bU, ++ 0x4646ca8cU, 0xeeee29c7U, 0xb8b8d36bU, 0x14143c28U, ++ 0xdede79a7U, 0x5e5ee2bcU, 0x0b0b1d16U, 0xdbdb76adU, ++ 0xe0e03bdbU, 0x32325664U, 0x3a3a4e74U, 0x0a0a1e14U, ++ 0x4949db92U, 0x06060a0cU, 0x24246c48U, 0x5c5ce4b8U, ++ 0xc2c25d9fU, 0xd3d36ebdU, 0xacacef43U, 0x6262a6c4U, ++ 0x9191a839U, 0x9595a431U, 0xe4e437d3U, 0x79798bf2U, ++ 0xe7e732d5U, 0xc8c8438bU, 0x3737596eU, 0x6d6db7daU, ++ 0x8d8d8c01U, 0xd5d564b1U, 0x4e4ed29cU, 0xa9a9e049U, ++ 0x6c6cb4d8U, 0x5656faacU, 0xf4f407f3U, 0xeaea25cfU, ++ 0x6565afcaU, 0x7a7a8ef4U, 0xaeaee947U, 0x08081810U, ++ 0xbabad56fU, 0x787888f0U, 0x25256f4aU, 0x2e2e725cU, ++ 0x1c1c2438U, 0xa6a6f157U, 0xb4b4c773U, 0xc6c65197U, ++ 0xe8e823cbU, 0xdddd7ca1U, 0x74749ce8U, 0x1f1f213eU, ++ 0x4b4bdd96U, 0xbdbddc61U, 0x8b8b860dU, 0x8a8a850fU, ++ 0x707090e0U, 0x3e3e427cU, 0xb5b5c471U, 0x6666aaccU, ++ 0x4848d890U, 0x03030506U, 0xf6f601f7U, 0x0e0e121cU, ++ 0x6161a3c2U, 0x35355f6aU, 0x5757f9aeU, 0xb9b9d069U, ++ 0x86869117U, 0xc1c15899U, 0x1d1d273aU, 0x9e9eb927U, ++ 0xe1e138d9U, 0xf8f813ebU, 0x9898b32bU, 0x11113322U, ++ 0x6969bbd2U, 0xd9d970a9U, 0x8e8e8907U, 0x9494a733U, ++ 0x9b9bb62dU, 0x1e1e223cU, 0x87879215U, 0xe9e920c9U, ++ 0xcece4987U, 0x5555ffaaU, 0x28287850U, 0xdfdf7aa5U, ++ 0x8c8c8f03U, 0xa1a1f859U, 0x89898009U, 0x0d0d171aU, ++ 0xbfbfda65U, 0xe6e631d7U, 0x4242c684U, 0x6868b8d0U, ++ 0x4141c382U, 0x9999b029U, 0x2d2d775aU, 0x0f0f111eU, ++ 0xb0b0cb7bU, 0x5454fca8U, 0xbbbbd66dU, 0x16163a2cU, ++}; ++ ++#define TE0(v) (Te0[(v) >> 24]) ++#define TE1(v) (Te1[((v) >> 16) & 0xff]) ++#define TE2(v) (Te2[((v) >> 8) & 0xff]) ++#define TE3(v) (Te3[(v) & 0xff]) ++ ++#else /* ONLY_ONE_TABLE */ ++ ++ ++static inline u32 ROR8(u32 v) ++{ ++ return (v >> 8) | (v << 24); ++} ++ ++static inline u32 ROR16(u32 v) ++{ ++ return (v >> 16) | (v << 16); ++} ++ ++static inline u32 ROR24(u32 v) ++{ ++ return (v >> 24) | (v << 8); ++} ++ ++#define TE0(v) (Te0[(v) >> 24]) ++#define TE1(v) (ROR8(Te0[((v) >> 16) & 0xff])) ++#define TE2(v) (ROR16(Te0[((v) >> 8) & 0xff])) ++#define TE3(v) (ROR24(Te0[(v) & 0xff])) ++ ++#endif /* ONLY_ONE_TABLE */ ++ ++ ++ ++#ifdef SMALL_TE4 ++static const u8 Te4s[256] = { ++ 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, ++ 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, ++ 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, ++ 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, ++ 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, ++ 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, ++ 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, ++ 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, ++ 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, ++ 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, ++ 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, ++ 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, ++ 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, ++ 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, ++ 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, ++ 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, ++ 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, ++ 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, ++ 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, ++ 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, ++ 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, ++ 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, ++ 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, ++ 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, ++ 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, ++ 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, ++ 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, ++ 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, ++ 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, ++ 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, ++ 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, ++ 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16, ++}; ++ ++#define TE4_1(v) (Te4s[(v) & 0xff] << 24) ++#define TE4_2(v) (Te4s[(v) & 0xff] << 16) ++#define TE4_3(v) (Te4s[(v) & 0xff] << 8) ++#define TE4_4(v) (Te4s[(v) & 0xff]) ++ ++#else /* SMALL_TE4 */ ++ ++static const u32 Te4[256] = ++{ ++ 0x63636363U, 0x7c7c7c7cU, 0x77777777U, 0x7b7b7b7bU, ++ 0xf2f2f2f2U, 0x6b6b6b6bU, 0x6f6f6f6fU, 0xc5c5c5c5U, ++ 0x30303030U, 0x01010101U, 0x67676767U, 0x2b2b2b2bU, ++ 0xfefefefeU, 0xd7d7d7d7U, 0xababababU, 0x76767676U, ++ 0xcacacacaU, 0x82828282U, 0xc9c9c9c9U, 0x7d7d7d7dU, ++ 0xfafafafaU, 0x59595959U, 0x47474747U, 0xf0f0f0f0U, ++ 0xadadadadU, 0xd4d4d4d4U, 0xa2a2a2a2U, 0xafafafafU, ++ 0x9c9c9c9cU, 0xa4a4a4a4U, 0x72727272U, 0xc0c0c0c0U, ++ 0xb7b7b7b7U, 0xfdfdfdfdU, 0x93939393U, 0x26262626U, ++ 0x36363636U, 0x3f3f3f3fU, 0xf7f7f7f7U, 0xccccccccU, ++ 0x34343434U, 0xa5a5a5a5U, 0xe5e5e5e5U, 0xf1f1f1f1U, ++ 0x71717171U, 0xd8d8d8d8U, 0x31313131U, 0x15151515U, ++ 0x04040404U, 0xc7c7c7c7U, 0x23232323U, 0xc3c3c3c3U, ++ 0x18181818U, 0x96969696U, 0x05050505U, 0x9a9a9a9aU, ++ 0x07070707U, 0x12121212U, 0x80808080U, 0xe2e2e2e2U, ++ 0xebebebebU, 0x27272727U, 0xb2b2b2b2U, 0x75757575U, ++ 0x09090909U, 0x83838383U, 0x2c2c2c2cU, 0x1a1a1a1aU, ++ 0x1b1b1b1bU, 0x6e6e6e6eU, 0x5a5a5a5aU, 0xa0a0a0a0U, ++ 0x52525252U, 0x3b3b3b3bU, 0xd6d6d6d6U, 0xb3b3b3b3U, ++ 0x29292929U, 0xe3e3e3e3U, 0x2f2f2f2fU, 0x84848484U, ++ 0x53535353U, 0xd1d1d1d1U, 0x00000000U, 0xededededU, ++ 0x20202020U, 0xfcfcfcfcU, 0xb1b1b1b1U, 0x5b5b5b5bU, ++ 0x6a6a6a6aU, 0xcbcbcbcbU, 0xbebebebeU, 0x39393939U, ++ 0x4a4a4a4aU, 0x4c4c4c4cU, 0x58585858U, 0xcfcfcfcfU, ++ 0xd0d0d0d0U, 0xefefefefU, 0xaaaaaaaaU, 0xfbfbfbfbU, ++ 0x43434343U, 0x4d4d4d4dU, 0x33333333U, 0x85858585U, ++ 0x45454545U, 0xf9f9f9f9U, 0x02020202U, 0x7f7f7f7fU, ++ 0x50505050U, 0x3c3c3c3cU, 0x9f9f9f9fU, 0xa8a8a8a8U, ++ 0x51515151U, 0xa3a3a3a3U, 0x40404040U, 0x8f8f8f8fU, ++ 0x92929292U, 0x9d9d9d9dU, 0x38383838U, 0xf5f5f5f5U, ++ 0xbcbcbcbcU, 0xb6b6b6b6U, 0xdadadadaU, 0x21212121U, ++ 0x10101010U, 0xffffffffU, 0xf3f3f3f3U, 0xd2d2d2d2U, ++ 0xcdcdcdcdU, 0x0c0c0c0cU, 0x13131313U, 0xececececU, ++ 0x5f5f5f5fU, 0x97979797U, 0x44444444U, 0x17171717U, ++ 0xc4c4c4c4U, 0xa7a7a7a7U, 0x7e7e7e7eU, 0x3d3d3d3dU, ++ 0x64646464U, 0x5d5d5d5dU, 0x19191919U, 0x73737373U, ++ 0x60606060U, 0x81818181U, 0x4f4f4f4fU, 0xdcdcdcdcU, ++ 0x22222222U, 0x2a2a2a2aU, 0x90909090U, 0x88888888U, ++ 0x46464646U, 0xeeeeeeeeU, 0xb8b8b8b8U, 0x14141414U, ++ 0xdedededeU, 0x5e5e5e5eU, 0x0b0b0b0bU, 0xdbdbdbdbU, ++ 0xe0e0e0e0U, 0x32323232U, 0x3a3a3a3aU, 0x0a0a0a0aU, ++ 0x49494949U, 0x06060606U, 0x24242424U, 0x5c5c5c5cU, ++ 0xc2c2c2c2U, 0xd3d3d3d3U, 0xacacacacU, 0x62626262U, ++ 0x91919191U, 0x95959595U, 0xe4e4e4e4U, 0x79797979U, ++ 0xe7e7e7e7U, 0xc8c8c8c8U, 0x37373737U, 0x6d6d6d6dU, ++ 0x8d8d8d8dU, 0xd5d5d5d5U, 0x4e4e4e4eU, 0xa9a9a9a9U, ++ 0x6c6c6c6cU, 0x56565656U, 0xf4f4f4f4U, 0xeaeaeaeaU, ++ 0x65656565U, 0x7a7a7a7aU, 0xaeaeaeaeU, 0x08080808U, ++ 0xbabababaU, 0x78787878U, 0x25252525U, 0x2e2e2e2eU, ++ 0x1c1c1c1cU, 0xa6a6a6a6U, 0xb4b4b4b4U, 0xc6c6c6c6U, ++ 0xe8e8e8e8U, 0xddddddddU, 0x74747474U, 0x1f1f1f1fU, ++ 0x4b4b4b4bU, 0xbdbdbdbdU, 0x8b8b8b8bU, 0x8a8a8a8aU, ++ 0x70707070U, 0x3e3e3e3eU, 0xb5b5b5b5U, 0x66666666U, ++ 0x48484848U, 0x03030303U, 0xf6f6f6f6U, 0x0e0e0e0eU, ++ 0x61616161U, 0x35353535U, 0x57575757U, 0xb9b9b9b9U, ++ 0x86868686U, 0xc1c1c1c1U, 0x1d1d1d1dU, 0x9e9e9e9eU, ++ 0xe1e1e1e1U, 0xf8f8f8f8U, 0x98989898U, 0x11111111U, ++ 0x69696969U, 0xd9d9d9d9U, 0x8e8e8e8eU, 0x94949494U, ++ 0x9b9b9b9bU, 0x1e1e1e1eU, 0x87878787U, 0xe9e9e9e9U, ++ 0xcecececeU, 0x55555555U, 0x28282828U, 0xdfdfdfdfU, ++ 0x8c8c8c8cU, 0xa1a1a1a1U, 0x89898989U, 0x0d0d0d0dU, ++ 0xbfbfbfbfU, 0xe6e6e6e6U, 0x42424242U, 0x68686868U, ++ 0x41414141U, 0x99999999U, 0x2d2d2d2dU, 0x0f0f0f0fU, ++ 0xb0b0b0b0U, 0x54545454U, 0xbbbbbbbbU, 0x16161616U, ++}; ++ ++#define TE4_1(v) (Te4[(v) & 0xff] & 0xff000000) ++#define TE4_2(v) (Te4[(v) & 0xff] & 0x00ff0000) ++#define TE4_3(v) (Te4[(v) & 0xff] & 0x0000ff00) ++#define TE4_4(v) (Te4[(v) & 0xff] & 0x000000ff) ++ ++#endif /* SMALL_TE4 */ ++ ++ ++static const u32 rcon[] = { ++ 0x01000000, 0x02000000, 0x04000000, 0x08000000, 0x10000000, ++ 0x20000000, 0x40000000, 0x80000000, 0x1B000000, 0x36000000, ++}; ++ ++#define GETU32(pt) \ ++(((u32)(pt)[0] << 24) ^ ((u32)(pt)[1] << 16) ^ ((u32)(pt)[2] << 8) ^ \ ++((u32)(pt)[3])) ++#define PUTU32(ct, st) \ ++{ (ct)[0] = (u8)((st) >> 24); (ct)[1] = (u8)((st) >> 16); \ ++(ct)[2] = (u8)((st) >> 8); (ct)[3] = (u8)(st); } ++ ++ ++/* Expand the cipher key into the encryption key schedule. */ ++void ieee80211_aes_key_setup_encrypt(u32 rk[/*44*/], const u8 key[]) ++{ ++ int i; ++ u32 temp; ++ ++ rk[0] = GETU32(key ); ++ rk[1] = GETU32(key + 4); ++ rk[2] = GETU32(key + 8); ++ rk[3] = GETU32(key + 12); ++ ++ for (i = 0; i < 10; i++) { ++ temp = rk[3]; ++ rk[4] = rk[0] ^ TE4_1(temp >> 16) ^ TE4_2(temp >> 8) ^ ++ TE4_3(temp) ^ TE4_4(temp >> 24) ^ rcon[i]; ++ rk[5] = rk[1] ^ rk[4]; ++ rk[6] = rk[2] ^ rk[5]; ++ rk[7] = rk[3] ^ rk[6]; ++ rk += 4; ++ } ++} ++ ++ ++void ieee80211_aes_encrypt(const u32 rk[/*44*/], const u8 pt[16], u8 ct[16]) ++{ ++ const int Nr = 10; ++ u32 s0, s1, s2, s3, t0, t1, t2, t3; ++ ++ /* Map byte array block to cipher state and add initial round key */ ++ s0 = GETU32(pt ) ^ rk[0]; ++ s1 = GETU32(pt + 4) ^ rk[1]; ++ s2 = GETU32(pt + 8) ^ rk[2]; ++ s3 = GETU32(pt + 12) ^ rk[3]; ++ ++#define ROUND(r,d,s,i) \ ++d##0 = TE0(s##0) ^ TE1(s##1) ^ TE2(s##2) ^ TE3(s##3) ^ rk[i]; \ ++d##1 = TE0(s##1) ^ TE1(s##2) ^ TE2(s##3) ^ TE3(s##0) ^ rk[i + 1]; \ ++d##2 = TE0(s##2) ^ TE1(s##3) ^ TE2(s##0) ^ TE3(s##1) ^ rk[i + 2]; \ ++d##3 = TE0(s##3) ^ TE1(s##0) ^ TE2(s##1) ^ TE3(s##2) ^ rk[i + 3] ++ ROUND(1,t,s,4); ++ ROUND(2,s,t,8); ++ ROUND(3,t,s,12); ++ ROUND(4,s,t,16); ++ ROUND(5,t,s,20); ++ ROUND(6,s,t,24); ++ ROUND(7,t,s,28); ++ ROUND(8,s,t,32); ++ ROUND(9,t,s,36); ++#undef ROUND ++ ++ rk += Nr << 2; ++ ++ /* Apply the last round and map cipher state to byte array block */ ++ s0 = TE4_1(t0 >> 24) ^ TE4_2(t1 >> 16) ^ TE4_3(t2 >> 8) ^ TE4_4(t3) ^ ++ rk[0]; ++ PUTU32(ct, s0); ++ s0 = TE4_1(t1 >> 24) ^ TE4_2(t2 >> 16) ^ TE4_3(t3 >> 8) ^ TE4_4(t0) ^ ++ rk[1]; ++ PUTU32(ct + 4, s0); ++ s0 = TE4_1(t2 >> 24) ^ TE4_2(t3 >> 16) ^ TE4_3(t0 >> 8) ^ TE4_4(t1) ^ ++ rk[2]; ++ PUTU32(ct + 8, s0); ++ s0 = TE4_1(t3 >> 24) ^ TE4_2(t0 >> 16) ^ TE4_3(t1 >> 8) ^ TE4_4(t2) ^ ++ rk[3]; ++ PUTU32(ct + 12, s0); ++} ++ ++/* --- end of code that is based on public domain AES implementation --- */ +diff -Nur linux-2.6.16/net/d80211/aes_ccm.c linux-2.6.16-bcm43xx/net/d80211/aes_ccm.c +--- linux-2.6.16/net/d80211/aes_ccm.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.16-bcm43xx/net/d80211/aes_ccm.c 2006-03-28 22:16:14.000000000 +0200 +@@ -0,0 +1,119 @@ ++/* ++ * Copyright 2003-2004, Instant802 Networks, Inc. ++ * Copyright 2005, Devicescape Software, Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include <linux/types.h> ++#include <linux/netdevice.h> ++ ++#include <net/d80211.h> ++#include "ieee80211_key.h" ++#include "aes_ccm.h" ++ ++#include "aes.c" ++ ++static inline void aes_ccm_prepare(u32 *rk, u8 *b_0, u8 *aad, u8 *b, ++ u8 *s_0, u8 *a) ++{ ++ int i; ++ ++ ieee80211_aes_encrypt(rk, b_0, b); ++ ++ /* Extra Authenticate-only data (always two AES blocks) */ ++ for (i = 0; i < AES_BLOCK_LEN; i++) ++ aad[i] ^= b[i]; ++ ieee80211_aes_encrypt(rk, aad, b); ++ ++ aad += AES_BLOCK_LEN; ++ ++ for (i = 0; i < AES_BLOCK_LEN; i++) ++ aad[i] ^= b[i]; ++ ieee80211_aes_encrypt(rk, aad, a); ++ ++ /* Mask out bits from auth-only-b_0 */ ++ b_0[0] &= 0x07; ++ ++ /* S_0 is used to encrypt T (= MIC) */ ++ b_0[14] = 0; ++ b_0[15] = 0; ++ ieee80211_aes_encrypt(rk, b_0, s_0); ++} ++ ++ ++void ieee80211_aes_ccm_encrypt(u32 *rk, u8 *b_0, u8 *aad, u8 *data, ++ size_t data_len, u8 *cdata, u8 *mic) ++{ ++ int i, j, last_len, num_blocks; ++ u8 *pos, *cpos; ++ u8 b[AES_BLOCK_LEN], s_0[AES_BLOCK_LEN], e[AES_BLOCK_LEN]; ++ ++ num_blocks = (data_len + AES_BLOCK_LEN - 1) / AES_BLOCK_LEN; ++ last_len = data_len % AES_BLOCK_LEN; ++ aes_ccm_prepare(rk, b_0, aad, b, s_0, b); ++ ++ /* Process payload blocks */ ++ pos = data; ++ cpos = cdata; ++ for (j = 1; j <= num_blocks; j++) { ++ int blen = (j == num_blocks && last_len) ? ++ last_len : AES_BLOCK_LEN; ++ ++ /* Authentication followed by encryption */ ++ for (i = 0; i < blen; i++) ++ b[i] ^= pos[i]; ++ ieee80211_aes_encrypt(rk, b, b); ++ ++ b_0[14] = (j >> 8) & 0xff; ++ b_0[15] = j & 0xff; ++ ieee80211_aes_encrypt(rk, b_0, e); ++ for (i = 0; i < blen; i++) ++ *cpos++ = *pos++ ^ e[i]; ++ } ++ ++ for (i = 0; i < CCMP_MIC_LEN; i++) ++ mic[i] = b[i] ^ s_0[i]; ++} ++ ++ ++int ieee80211_aes_ccm_decrypt(u32 *rk, u8 *b_0, u8 *aad, u8 *cdata, ++ size_t data_len, u8 *mic, u8 *data) ++{ ++ int i, j, last_len, num_blocks; ++ u8 *pos, *cpos; ++ u8 b[AES_BLOCK_LEN], s_0[AES_BLOCK_LEN], a[AES_BLOCK_LEN]; ++ ++ num_blocks = (data_len + AES_BLOCK_LEN - 1) / AES_BLOCK_LEN; ++ last_len = data_len % AES_BLOCK_LEN; ++ aes_ccm_prepare(rk, b_0, aad, b, s_0, a); ++ ++ /* Process payload blocks */ ++ cpos = cdata; ++ pos = data; ++ for (j = 1; j <= num_blocks; j++) { ++ int blen = (j == num_blocks && last_len) ? ++ last_len : AES_BLOCK_LEN; ++ ++ /* Decryption followed by authentication */ ++ b_0[14] = (j >> 8) & 0xff; ++ b_0[15] = j & 0xff; ++ ieee80211_aes_encrypt(rk, b_0, b); ++ for (i = 0; i < blen; i++) { ++ *pos = *cpos++ ^ b[i]; ++ a[i] ^= *pos++; ++ } ++ ++ ieee80211_aes_encrypt(rk, a, a); ++ } ++ ++ for (i = 0; i < CCMP_MIC_LEN; i++) { ++ if ((mic[i] ^ s_0[i]) != a[i]) ++ return -1; ++ } ++ ++ return 0; ++} ++ +diff -Nur linux-2.6.16/net/d80211/aes_ccm.h linux-2.6.16-bcm43xx/net/d80211/aes_ccm.h +--- linux-2.6.16/net/d80211/aes_ccm.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.16-bcm43xx/net/d80211/aes_ccm.h 2006-03-28 22:16:14.000000000 +0200 +@@ -0,0 +1,24 @@ ++/* ++ * Copyright 2003-2004, Instant802 Networks, Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#ifndef AES_CCM_H ++#define AES_CCM_H ++ ++#include <linux/types.h> ++ ++#define AES_BLOCK_LEN 16 ++#define AES_STATE_LEN 44 ++ ++void ieee80211_aes_key_setup_encrypt(u32 rk[/*44*/], const u8 key[]); ++void ieee80211_aes_encrypt(const u32 rk[/*44*/], const u8 pt[16], u8 ct[16]); ++void ieee80211_aes_ccm_encrypt(u32 rk[/*44*/], u8 *b_0, u8 *aad, u8 *data, ++ size_t data_len, u8 *cdata, u8 *mic); ++int ieee80211_aes_ccm_decrypt(u32 rk[/*44*/], u8 *b_0, u8 *aad, u8 *cdata, ++ size_t data_len, u8 *mic, u8 *data); ++ ++#endif /* AES_CCM_H */ +diff -Nur linux-2.6.16/net/d80211/fifo_qdisc.c linux-2.6.16-bcm43xx/net/d80211/fifo_qdisc.c +--- linux-2.6.16/net/d80211/fifo_qdisc.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.16-bcm43xx/net/d80211/fifo_qdisc.c 2006-03-28 22:16:14.000000000 +0200 +@@ -0,0 +1,103 @@ ++/* ++ * Copyright 2005, Devicescape Software, Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ * If building without CONFIG_NET_SCHED we need a simple ++ * fifo qdisc to install by default as the sub-qdisc. ++ * This is a simple replacement for sch_fifo. ++ */ ++ ++#include <linux/config.h> ++#include <linux/version.h> ++#include <linux/netdevice.h> ++#include <net/d80211.h> ++#include "ieee80211_i.h" ++#include "wme.h" ++ ++static int pfifo_enqueue(struct sk_buff *skb, struct Qdisc* qd) ++{ ++ struct sk_buff_head *q = qdisc_priv(qd); ++ ++ if (skb_queue_len(q) > qd->dev->tx_queue_len) { ++ qd->qstats.drops++; ++ kfree_skb(skb); ++ return NET_XMIT_DROP; ++ } ++ ++ skb_queue_tail(q, skb); ++ qd->q.qlen++; ++ qd->bstats.bytes += skb->len; ++ qd->bstats.packets++; ++ ++ return NET_XMIT_SUCCESS; ++} ++ ++ ++static int pfifo_requeue(struct sk_buff *skb, struct Qdisc* qd) ++{ ++ struct sk_buff_head *q = qdisc_priv(qd); ++ ++ skb_queue_head(q, skb); ++ qd->q.qlen++; ++ qd->bstats.bytes += skb->len; ++ qd->bstats.packets++; ++ ++ return NET_XMIT_SUCCESS; ++} ++ ++ ++static struct sk_buff *pfifo_dequeue(struct Qdisc* qd) ++{ ++ struct sk_buff_head *q = qdisc_priv(qd); ++ ++ return skb_dequeue(q); ++} ++ ++ ++static int pfifo_init(struct Qdisc* qd, struct rtattr *opt) ++{ ++ struct sk_buff_head *q = qdisc_priv(qd); ++ ++ skb_queue_head_init(q); ++ return 0; ++} ++ ++ ++static void pfifo_reset(struct Qdisc* qd) ++{ ++ struct sk_buff_head *q = qdisc_priv(qd); ++ ++ skb_queue_purge(q); ++ qd->q.qlen = 0; ++} ++ ++ ++static int pfifo_dump(struct Qdisc *qd, struct sk_buff *skb) ++{ ++ return skb->len; ++} ++ ++ ++struct Qdisc_ops pfifo_qdisc_ops = ++{ ++ .next = NULL, ++ .cl_ops = NULL, ++ .id = "ieee80211_pfifo", ++ .priv_size = sizeof(struct sk_buff_head), ++ ++ .enqueue = pfifo_enqueue, ++ .dequeue = pfifo_dequeue, ++ .requeue = pfifo_requeue, ++ .drop = NULL, ++ ++ .init = pfifo_init, ++ .reset = pfifo_reset, ++ .destroy = NULL, ++ .change = NULL, ++ ++ .dump = pfifo_dump, ++}; ++ +diff -Nur linux-2.6.16/net/d80211/hostapd_ioctl.h linux-2.6.16-bcm43xx/net/d80211/hostapd_ioctl.h +--- linux-2.6.16/net/d80211/hostapd_ioctl.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.16-bcm43xx/net/d80211/hostapd_ioctl.h 2006-03-28 22:16:14.000000000 +0200 +@@ -0,0 +1,439 @@ ++/* ++ * Host AP (software wireless LAN access point) user space daemon for ++ * Host AP kernel driver ++ * Copyright 2002-2003, Jouni Malinen <jkmaline@cc.hut.fi> ++ * Copyright 2002-2004, Instant802 Networks, Inc. ++ * Copyright 2005, Devicescape Software, Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#ifndef HOSTAPD_IOCTL_H ++#define HOSTAPD_IOCTL_H ++ ++#include <linux/types.h> ++ ++#ifndef __KERNEL__ ++#include "ieee80211_shared.h" ++#endif /* __KERNEL__ */ ++ ++#define PRISM2_IOCTL_PRISM2_PARAM (SIOCIWFIRSTPRIV + 0) ++#define PRISM2_IOCTL_GET_PRISM2_PARAM (SIOCIWFIRSTPRIV + 1) ++#define PRISM2_IOCTL_HOSTAPD (SIOCIWFIRSTPRIV + 3) ++#define PRISM2_IOCTL_TEST_PARAM (SIOCIWFIRSTPRIV + 4) ++ ++/* PRISM2_IOCTL_PRISM2_PARAM ioctl() subtypes: */ ++enum { ++ PRISM2_PARAM_PTYPE = 1, ++ PRISM2_PARAM_TXRATECTRL = 2, ++ PRISM2_PARAM_BEACON_INT = 3, ++ PRISM2_PARAM_PSEUDO_IBSS = 4, ++ PRISM2_PARAM_ALC = 5, ++ PRISM2_PARAM_TXPOWER = 6, ++ PRISM2_PARAM_DUMP = 7, ++ PRISM2_PARAM_OTHER_AP_POLICY = 8, ++ PRISM2_PARAM_AP_MAX_INACTIVITY = 9, ++ PRISM2_PARAM_AP_BRIDGE_PACKETS = 10, ++ PRISM2_PARAM_DTIM_PERIOD = 11, ++ PRISM2_PARAM_AP_NULLFUNC_ACK = 12, ++ PRISM2_PARAM_MAX_WDS = 13, ++ PRISM2_PARAM_AP_AUTOM_AP_WDS = 14, ++ PRISM2_PARAM_AP_AUTH_ALGS = 15, ++ PRISM2_PARAM_MONITOR_ALLOW_FCSERR = 16, ++ PRISM2_PARAM_HOST_ENCRYPT = 17, ++ PRISM2_PARAM_HOST_DECRYPT = 18, ++ PRISM2_PARAM_BUS_MASTER_THRESHOLD_RX = 19, ++ PRISM2_PARAM_BUS_MASTER_THRESHOLD_TX = 20, ++ PRISM2_PARAM_HOST_ROAMING = 21, ++ PRISM2_PARAM_BCRX_STA_KEY = 22, ++ PRISM2_PARAM_IEEE_802_1X = 23, ++ PRISM2_PARAM_ANTSEL_TX = 24, ++ PRISM2_PARAM_ANTSEL_RX = 25, ++ PRISM2_PARAM_MONITOR_TYPE = 26, ++ PRISM2_PARAM_WDS_TYPE = 27, ++ PRISM2_PARAM_HOSTSCAN = 28, ++ PRISM2_PARAM_AP_SCAN = 29, ++ ++ /* Instant802 additions */ ++ PRISM2_PARAM_CTS_PROTECT_ERP_FRAMES = 1001, ++ PRISM2_PARAM_DROP_UNENCRYPTED = 1002, ++ PRISM2_PARAM_PREAMBLE = 1003, ++ PRISM2_PARAM_RATE_LIMIT = 1004, ++ PRISM2_PARAM_RATE_LIMIT_BURST = 1005, ++ PRISM2_PARAM_SHORT_SLOT_TIME = 1006, ++ PRISM2_PARAM_TEST_MODE = 1007, ++ PRISM2_PARAM_NEXT_MODE = 1008, ++ PRISM2_PARAM_CLEAR_KEYS = 1009, ++ PRISM2_PARAM_ADM_STATUS = 1010, ++ PRISM2_PARAM_ANTENNA_SEL = 1011, ++ PRISM2_PARAM_CALIB_INT = 1012, ++ PRISM2_PARAM_ANTENNA_MODE = 1013, ++ PRISM2_PARAM_PRIVACY_INVOKED = 1014, ++ PRISM2_PARAM_BROADCAST_SSID = 1015, ++ PRISM2_PARAM_STAT_TIME = 1016, ++ PRISM2_PARAM_STA_ANTENNA_SEL = 1017, ++ PRISM2_PARAM_FORCE_UNICAST_RATE = 1018, ++ PRISM2_PARAM_RATE_CTRL_NUM_UP = 1019, ++ PRISM2_PARAM_RATE_CTRL_NUM_DOWN = 1020, ++ PRISM2_PARAM_MAX_RATECTRL_RATE = 1021, ++ PRISM2_PARAM_TX_POWER_REDUCTION = 1022, ++ PRISM2_PARAM_EAPOL = 1023, ++ PRISM2_PARAM_KEY_TX_RX_THRESHOLD = 1024, ++ PRISM2_PARAM_KEY_INDEX = 1025, ++ PRISM2_PARAM_DEFAULT_WEP_ONLY = 1026, ++ PRISM2_PARAM_WIFI_WME_NOACK_TEST = 1033, ++ PRISM2_PARAM_ALLOW_BROADCAST_ALWAYS = 1034, ++ PRISM2_PARAM_SCAN_FLAGS = 1035, ++ PRISM2_PARAM_HW_MODES = 1036, ++ PRISM2_PARAM_CREATE_IBSS = 1037, ++ PRISM2_PARAM_WMM_ENABLED = 1038, ++ PRISM2_PARAM_MIXED_CELL = 1039, ++ PRISM2_PARAM_KEY_MGMT = 1040, ++ PRISM2_PARAM_RADAR_DETECT = 1043, ++ PRISM2_PARAM_SPECTRUM_MGMT = 1044, ++ /* NOTE: Please try to coordinate with other active development ++ * branches before allocating new param numbers so that each new param ++ * will be unique within all branches and the allocated number will not ++ * need to be changed when merging new features. Existing numbers in ++ * the mainline (or main devel branch) must not be changed when merging ++ * in new features. */ ++}; ++ ++/* PRISM2_IOCTL_HOSTAPD ioctl() cmd: */ ++enum { ++ PRISM2_HOSTAPD_FLUSH = 1, ++ PRISM2_HOSTAPD_ADD_STA = 2, ++ PRISM2_HOSTAPD_REMOVE_STA = 3, ++ PRISM2_HOSTAPD_GET_INFO_STA = 4, ++ /* REMOVED: PRISM2_HOSTAPD_RESET_TXEXC_STA = 5, */ ++ PRISM2_SET_ENCRYPTION = 6, ++ PRISM2_GET_ENCRYPTION = 7, ++ PRISM2_HOSTAPD_SET_FLAGS_STA = 8, ++ PRISM2_HOSTAPD_GET_RID = 9, ++ PRISM2_HOSTAPD_SET_RID = 10, ++ PRISM2_HOSTAPD_SET_ASSOC_AP_ADDR = 11, ++ PRISM2_HOSTAPD_MLME = 13, ++ ++ /* Instant802 additions */ ++ PRISM2_HOSTAPD_SET_BEACON = 1001, ++ PRISM2_HOSTAPD_GET_HW_FEATURES = 1002, ++ PRISM2_HOSTAPD_SCAN = 1003, ++ PRISM2_HOSTAPD_WPA_TRIGGER = 1004, ++ PRISM2_HOSTAPD_SET_RATE_SETS = 1005, ++ PRISM2_HOSTAPD_ADD_IF = 1006, ++ PRISM2_HOSTAPD_REMOVE_IF = 1007, ++ PRISM2_HOSTAPD_GET_DOT11COUNTERSTABLE = 1008, ++ PRISM2_HOSTAPD_GET_LOAD_STATS = 1009, ++ PRISM2_HOSTAPD_SET_STA_VLAN = 1010, ++ PRISM2_HOSTAPD_SET_GENERIC_INFO_ELEM = 1011, ++ PRISM2_HOSTAPD_SET_CHANNEL_FLAG = 1012, ++ PRISM2_HOSTAPD_SET_REGULATORY_DOMAIN = 1013, ++ PRISM2_HOSTAPD_SET_TX_QUEUE_PARAMS = 1014, ++ PRISM2_HOSTAPD_SET_BSS = 1015, ++ PRISM2_HOSTAPD_GET_TX_STATS = 1016, ++ PRISM2_HOSTAPD_UPDATE_IF = 1017, ++ PRISM2_HOSTAPD_SCAN_REQ = 1019, ++ PRISM2_STA_GET_STATE = 1020, ++ PRISM2_HOSTAPD_FLUSH_IFS = 1021, ++ PRISM2_HOSTAPD_SET_RADAR_PARAMS = 1023, ++ PRISM2_HOSTAPD_SET_QUIET_PARAMS = 1024, ++ PRISM2_HOSTAPD_GET_TX_POWER = 1025, ++ /* NOTE: Please try to coordinate with other active development ++ * branches before allocating new param numbers so that each new param ++ * will be unique within all branches and the allocated number will not ++ * need to be changed when merging new features. Existing numbers in ++ * the mainline (or main devel branch) must not be changed when merging ++ * in new features. */ ++}; ++ ++ /* these definitions mirror the ieee80211_i.h ++ * IEEE80211_DISABLED, ... IEEE80211_ASSOCIATED enumeration */ ++enum { ++ PRISM2_PARAM_STA_DISABLED, ++ PRISM2_PARAM_STA_AUTHENTICATE, ++ PRISM2_PARAM_STA_ASSOCIATE, ++ PRISM2_PARAM_STA_ASSOCIATED, ++}; ++ ++#define PRISM2_HOSTAPD_MAX_BUF_SIZE 2048 ++#define HOSTAP_CRYPT_ALG_NAME_LEN 16 ++ ++/* Use this to make sure that structure elements are correctly aligned ++ * for access as other types. Most commonly, this affects the placeholder ++ * types used for data at the end of a structure in this union. ++ */ ++#ifdef __GNUC__ ++#undef ALIGNED ++#define ALIGNED __attribute__ ((aligned)) ++#else ++/* Check if it has been defined elsewhere */ ++#ifndef ALIGNED ++#error "Must define ALIGNED to generate aligned structure elements" ++#endif ++#endif ++ ++struct prism2_hostapd_param { ++ u32 cmd; ++ u8 sta_addr[ETH_ALEN]; ++ u8 pad[2]; ++ union { ++ struct { ++ u16 aid; ++ u16 capability; ++ u8 supp_rates[32]; ++ /* atheros_super_ag and enc_flags are only used with ++ * IEEE80211_ATHEROS_SUPER_AG ++ */ ++ u8 atheros_super_ag; ++ u8 atheros_xr_mode; ++ u8 wds_flags; ++#define IEEE80211_STA_DYNAMIC_ENC BIT(0) ++ u8 enc_flags; ++ } add_sta; ++ struct { ++ u32 inactive_msec; ++ u32 rx_packets; ++ u32 tx_packets; ++ u32 rx_bytes; ++ u32 tx_bytes; ++ u32 current_tx_rate; /* in 100 kbps */ ++ u32 channel_use; ++ u32 flags; ++ u32 num_ps_buf_frames; ++ u32 tx_retry_failed; ++ u32 tx_retry_count; ++ u32 last_rssi; ++ u32 last_ack_rssi; ++ } get_info_sta; ++ struct { ++ u8 alg[HOSTAP_CRYPT_ALG_NAME_LEN]; ++ u32 flags; ++ u32 err; ++ u8 idx; ++#define HOSTAP_SEQ_COUNTER_SIZE 8 ++ u8 seq_counter[HOSTAP_SEQ_COUNTER_SIZE]; ++ u16 key_len; ++ u8 key[0] ALIGNED; ++ } crypt; ++ struct { ++ u32 flags_and; ++ u32 flags_or; ++ } set_flags_sta; ++ struct { ++ u16 rid; ++ u16 len; ++ u8 data[0] ALIGNED; ++ } rid; ++ struct { ++ u16 head_len; ++ u16 tail_len; ++ u8 data[0] ALIGNED; /* head_len + tail_len bytes */ ++ } beacon; ++ struct { ++ u16 num_modes; ++ u16 flags; ++ u8 data[0] ALIGNED; /* num_modes * feature data */ ++ } hw_features; ++ struct { ++ u8 now; ++ s8 our_mode_only; ++ s16 last_rx; ++ u16 channel; ++ s16 interval; /* seconds */ ++ s32 listen; /* microseconds */ ++ } scan; ++ struct { ++#define WPA_TRIGGER_FAIL_TX_MIC BIT(0) ++#define WPA_TRIGGER_FAIL_TX_ICV BIT(1) ++#define WPA_TRIGGER_FAIL_RX_MIC BIT(2) ++#define WPA_TRIGGER_FAIL_RX_ICV BIT(3) ++#define WPA_TRIGGER_TX_REPLAY BIT(4) ++#define WPA_TRIGGER_TX_REPLAY_FRAG BIT(5) ++#define WPA_TRIGGER_TX_SKIP_SEQ BIT(6) ++ u32 trigger; ++ } wpa_trigger; ++ struct { ++ u16 mode; /* MODE_* */ ++ u16 num_supported_rates; ++ u16 num_basic_rates; ++ u8 data[0] ALIGNED; /* num_supported_rates * u16 + ++ * num_basic_rates * u16 */ ++ } set_rate_sets; ++ struct { ++ u8 type; /* WDS, VLAN, etc */ ++ u8 name[IFNAMSIZ]; ++ u8 data[0] ALIGNED; ++ } if_info; ++ struct dot11_counters { ++ u32 dot11TransmittedFragmentCount; ++ u32 dot11MulticastTransmittedFrameCount; ++ u32 dot11FailedCount; ++ u32 dot11ReceivedFragmentCount; ++ u32 dot11MulticastReceivedFrameCount; ++ u32 dot11FCSErrorCount; ++ u32 dot11TransmittedFrameCount; ++ u32 dot11WEPUndecryptableCount; ++ u32 dot11ACKFailureCount; ++ u32 dot11RTSFailureCount; ++ u32 dot11RTSSuccessCount; ++ } dot11CountersTable; ++ struct { ++#define LOAD_STATS_CLEAR BIT(1) ++ u32 flags; ++ u32 channel_use; ++ } get_load_stats; ++ struct { ++ char vlan_name[IFNAMSIZ]; ++ int vlan_id; ++ } set_sta_vlan; ++ struct { ++ u8 len; ++ u8 data[0] ALIGNED; ++ } set_generic_info_elem; ++ struct { ++ u16 mode; /* MODE_* */ ++ u16 chan; ++ u32 flag; ++ u8 power_level; /* regulatory limit in dBm */ ++ u8 antenna_max; ++ } set_channel_flag; ++ struct { ++ u32 rd; ++ } set_regulatory_domain; ++ struct { ++ u32 queue; ++ s32 aifs; ++ u32 cw_min; ++ u32 cw_max; ++ u32 burst_time; /* maximum burst time in 0.1 ms, i.e., ++ * 10 = 1 ms */ ++ } tx_queue_params; ++ struct { ++ u32 bss_count; ++ u8 bssid_mask[ETH_ALEN]; ++ } set_bss; ++ struct ieee80211_tx_stats { ++ struct { ++ unsigned int len; /* num packets in queue */ ++ unsigned int limit; /* queue len (soft) limit ++ */ ++ unsigned int count; /* total num frames sent */ ++ } data[4]; ++ } get_tx_stats; ++ struct { ++ u8 ssid_len; ++ u8 ssid[0] ALIGNED; ++ } scan_req; ++ struct { ++ u32 state; ++ } sta_get_state; ++ struct { ++#define MLME_STA_DEAUTH 0 ++#define MLME_STA_DISASSOC 1 ++ u16 cmd; ++ u16 reason_code; ++ } mlme; ++ struct { ++ u8 radar_firpwr_threshold; ++ u8 radar_rssi_threshold; ++ u8 pulse_height_threshold; ++ u8 pulse_rssi_threshold; ++ u8 pulse_inband_threshold; ++ } radar; ++ struct { ++ unsigned int period; ++ unsigned int offset; ++ unsigned int duration; ++ } quiet; ++ struct { ++ unsigned int tx_power_min; ++ unsigned int tx_power_max; ++ } tx_power; ++ struct { ++ u8 dummy[80]; /* Make sizeof() this struct large enough ++ * with some compiler versions. */ ++ } dummy; ++ } u; ++}; ++ ++ ++#ifndef IEEE80211_TX_QUEUE_NUMS ++#define IEEE80211_TX_QUEUE_NUMS ++/* TODO: these need to be synchronized with ieee80211.h; make a shared header ++ * file that can be included into low-level drivers, 80211.o, and hostapd */ ++/* tx_queue_params - queue */ ++enum { ++ IEEE80211_TX_QUEUE_DATA0 = 0, /* used for EDCA AC_VO data */ ++ IEEE80211_TX_QUEUE_DATA1 = 1, /* used for EDCA AC_VI data */ ++ IEEE80211_TX_QUEUE_DATA2 = 2, /* used for EDCA AC_BE data */ ++ IEEE80211_TX_QUEUE_DATA3 = 3, /* used for EDCA AC_BK data */ ++ IEEE80211_TX_QUEUE_DATA4 = 4, ++ IEEE80211_TX_QUEUE_AFTER_BEACON = 6, ++ IEEE80211_TX_QUEUE_BEACON = 7 ++}; ++#endif /* IEEE80211_TX_QUEUE_NUMS */ ++ ++ ++#define HOSTAP_CRYPT_FLAG_SET_TX_KEY BIT(0) ++#define HOSTAP_CRYPT_FLAG_PERMANENT BIT(1) ++ ++#define HOSTAP_CRYPT_ERR_UNKNOWN_ALG 2 ++#define HOSTAP_CRYPT_ERR_UNKNOWN_ADDR 3 ++#define HOSTAP_CRYPT_ERR_CRYPT_INIT_FAILED 4 ++#define HOSTAP_CRYPT_ERR_KEY_SET_FAILED 5 ++#define HOSTAP_CRYPT_ERR_TX_KEY_SET_FAILED 6 ++#define HOSTAP_CRYPT_ERR_CARD_CONF_FAILED 7 ++ ++#define HOSTAP_HW_FLAG_NULLFUNC_OK BIT(0) ++ ++enum { ++ IEEE80211_KEY_MGMT_NONE = 0, ++ IEEE80211_KEY_MGMT_IEEE8021X = 1, ++ IEEE80211_KEY_MGMT_WPA_PSK = 2, ++ IEEE80211_KEY_MGMT_WPA_EAP = 3, ++}; ++ ++ ++/* Data structures used for get_hw_features ioctl */ ++struct hostapd_ioctl_hw_modes_hdr { ++ int mode; ++ int num_channels; ++ int num_rates; ++}; ++ ++struct ieee80211_channel_data { ++ short chan; /* channel number (IEEE 802.11) */ ++ short freq; /* frequency in MHz */ ++ int flag; /* flag for hostapd use (IEEE80211_CHAN_*) */ ++}; ++ ++struct ieee80211_rate_data { ++ int rate; /* rate in 100 kbps */ ++ int flags; /* IEEE80211_RATE_ flags */ ++}; ++ ++ ++/* ADD_IF, REMOVE_IF, and UPDATE_IF 'type' argument */ ++enum { ++ HOSTAP_IF_WDS = 1, HOSTAP_IF_VLAN = 2, HOSTAP_IF_BSS = 3, ++ HOSTAP_IF_STA = 4 ++}; ++ ++struct hostapd_if_wds { ++ u8 remote_addr[ETH_ALEN]; ++}; ++ ++struct hostapd_if_vlan { ++ u8 id; ++}; ++ ++struct hostapd_if_bss { ++ u8 bssid[ETH_ALEN]; ++}; ++ ++struct hostapd_if_sta { ++}; ++ ++#endif /* HOSTAPD_IOCTL_H */ +diff -Nur linux-2.6.16/net/d80211/ieee80211.c linux-2.6.16-bcm43xx/net/d80211/ieee80211.c +--- linux-2.6.16/net/d80211/ieee80211.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.16-bcm43xx/net/d80211/ieee80211.c 2006-03-28 22:16:14.000000000 +0200 +@@ -0,0 +1,4896 @@ ++/* ++ * Copyright 2002-2005, Instant802 Networks, Inc. ++ * Copyright 2005-2006, Devicescape Software, Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include <linux/config.h> ++#include <linux/version.h> ++#include <linux/module.h> ++#include <linux/init.h> ++#include <linux/netdevice.h> ++#include <linux/types.h> ++#include <linux/slab.h> ++#include <linux/skbuff.h> ++#include <linux/etherdevice.h> ++#include <linux/if_arp.h> ++#include <linux/wireless.h> ++#include <net/iw_handler.h> ++#include <linux/compiler.h> ++ ++#include <net/d80211.h> ++#include <net/d80211_common.h> ++#include <net/d80211_mgmt.h> ++#include "ieee80211_i.h" ++#include "ieee80211_proc.h" ++#include "rate_control.h" ++#include "wep.h" ++#include "wpa.h" ++#include "tkip.h" ++#include "wme.h" ++ ++ ++/* See IEEE 802.1H for LLC/SNAP encapsulation/decapsulation */ ++/* Ethernet-II snap header (RFC1042 for most EtherTypes) */ ++static unsigned char rfc1042_header[] = ++{ 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 }; ++/* Bridge-Tunnel header (for EtherTypes ETH_P_AARP and ETH_P_IPX) */ ++static unsigned char bridge_tunnel_header[] = ++{ 0xaa, 0xaa, 0x03, 0x00, 0x00, 0xf8 }; ++/* No encapsulation header if EtherType < 0x600 (=length) */ ++ ++static unsigned char eapol_header[] = ++{ 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00, 0x88, 0x8e }; ++ ++ ++struct rate_control_algs { ++ struct rate_control_algs *next; ++ struct rate_control_ops *ops; ++}; ++ ++static struct rate_control_algs *ieee80211_rate_ctrl_algs; ++ ++static int rate_control_initialize(struct ieee80211_local *local); ++ ++ ++static u8 * ieee80211_get_bssid(struct ieee80211_hdr *hdr, size_t len); ++ ++ ++struct ieee80211_key_conf * ++ieee80211_key_data2conf(struct ieee80211_local *local, ++ struct ieee80211_key *data) ++{ ++ struct ieee80211_key_conf *conf; ++ ++ conf = kmalloc(sizeof(*conf) + data->keylen, GFP_ATOMIC); ++ if (conf == NULL) ++ return NULL; ++ ++ conf->hw_key_idx = data->hw_key_idx; ++ conf->alg = data->alg; ++ conf->keylen = data->keylen; ++ conf->force_sw_encrypt = data->force_sw_encrypt; ++ conf->keyidx = data->keyidx; ++ conf->default_tx_key = data->default_tx_key; ++ conf->default_wep_only = local->default_wep_only; ++ memcpy(conf->key, data->key, data->keylen); ++ ++ return conf; ++} ++ ++ ++static int rate_list_match(int *rate_list, int rate) ++{ ++ int i; ++ ++ if (rate_list == NULL) ++ return 0; ++ ++ for (i = 0; rate_list[i] >= 0; i++) ++ if (rate_list[i] == rate) ++ return 1; ++ ++ return 0; ++} ++ ++ ++void ieee80211_prepare_rates(struct net_device *dev) ++{ ++ struct ieee80211_local *local = dev->priv; ++ int i; ++ ++ for (i = 0; i < local->num_curr_rates; i++) { ++ struct ieee80211_rate *rate = &local->curr_rates[i]; ++ ++ rate->flags &= ~(IEEE80211_RATE_SUPPORTED | ++ IEEE80211_RATE_BASIC); ++ ++ if (local->supp_rates[local->conf.phymode]) { ++ if (!rate_list_match(local->supp_rates ++ [local->conf.phymode], ++ rate->rate)) ++ continue; ++ } ++ ++ rate->flags |= IEEE80211_RATE_SUPPORTED; ++ ++ /* Use configured basic rate set if it is available. If not, ++ * use defaults that are sane for most cases. */ ++ if (local->basic_rates[local->conf.phymode]) { ++ if (rate_list_match(local->basic_rates ++ [local->conf.phymode], ++ rate->rate)) ++ rate->flags |= IEEE80211_RATE_BASIC; ++ } else switch (local->conf.phymode) { ++ case MODE_IEEE80211A: ++ if (rate->rate == 60 || rate->rate == 120 || ++ rate->rate == 240) ++ rate->flags |= IEEE80211_RATE_BASIC; ++ break; ++ case MODE_IEEE80211B: ++ if (rate->rate == 10 || rate->rate == 20) ++ rate->flags |= IEEE80211_RATE_BASIC; ++ break; ++ case MODE_ATHEROS_TURBO: ++ if (rate->rate == 120 || rate->rate == 240 || ++ rate->rate == 480) ++ rate->flags |= IEEE80211_RATE_BASIC; ++ break; ++ case MODE_IEEE80211G: ++ if (rate->rate == 10 || rate->rate == 20 || ++ rate->rate == 55 || rate->rate == 110) ++ rate->flags |= IEEE80211_RATE_BASIC; ++ break; ++ } ++ ++ /* Set ERP and MANDATORY flags based on phymode */ ++ switch (local->conf.phymode) { ++ case MODE_IEEE80211A: ++ if (rate->rate == 60 || rate->rate == 120 || ++ rate->rate == 240) ++ rate->flags |= IEEE80211_RATE_MANDATORY; ++ break; ++ case MODE_IEEE80211B: ++ if (rate->rate == 10) ++ rate->flags |= IEEE80211_RATE_MANDATORY; ++ break; ++ case MODE_ATHEROS_TURBO: ++ break; ++ case MODE_IEEE80211G: ++ if (rate->rate == 10 || rate->rate == 20 || ++ rate->rate == 55 || rate->rate == 110 || ++ rate->rate == 60 || rate->rate == 120 || ++ rate->rate == 240) ++ rate->flags |= IEEE80211_RATE_MANDATORY; ++ if (rate->rate != 10 && rate->rate != 20 && ++ rate->rate != 55 && rate->rate != 110) ++ rate->flags |= IEEE80211_RATE_ERP; ++ break; ++ } ++ } ++} ++ ++ ++static void ieee80211_key_threshold_notify(struct net_device *dev, ++ struct ieee80211_key *key, ++ struct sta_info *sta) ++{ ++ struct sk_buff *skb; ++ struct ieee80211_msg_key_notification *msg; ++ ++ skb = dev_alloc_skb(sizeof(struct ieee80211_frame_info) + ++ sizeof(struct ieee80211_msg_key_notification)); ++ if (skb == NULL) ++ return; ++ ++ skb_reserve(skb, sizeof(struct ieee80211_frame_info)); ++ msg = (struct ieee80211_msg_key_notification *) ++ skb_put(skb, sizeof(struct ieee80211_msg_key_notification)); ++ msg->tx_rx_count = key->tx_rx_count; ++ memcpy(msg->ifname, dev->name, IFNAMSIZ); ++ if (sta) ++ memcpy(msg->addr, sta->addr, ETH_ALEN); ++ else ++ memset(msg->addr, 0xff, ETH_ALEN); ++ ++ key->tx_rx_count = 0; ++ ++ ieee80211_rx_mgmt(dev, skb, 0, ++ ieee80211_msg_key_threshold_notification); ++} ++ ++ ++int ieee80211_get_hdrlen(u16 fc) ++{ ++ int hdrlen = 24; ++ ++ switch (WLAN_FC_GET_TYPE(fc)) { ++ case WLAN_FC_TYPE_DATA: ++ if ((fc & WLAN_FC_FROMDS) && (fc & WLAN_FC_TODS)) ++ hdrlen = 30; /* Addr4 */ ++ if (WLAN_FC_GET_STYPE(fc) & 0x08) ++ hdrlen += 2; /* QoS Control Field */ ++ break; ++ case WLAN_FC_TYPE_CTRL: ++ switch (WLAN_FC_GET_STYPE(fc)) { ++ case WLAN_FC_STYPE_CTS: ++ case WLAN_FC_STYPE_ACK: ++ hdrlen = 10; ++ break; ++ default: ++ hdrlen = 16; ++ break; ++ } ++ break; ++ } ++ ++ return hdrlen; ++} ++ ++ ++int ieee80211_get_hdrlen_from_skb(struct sk_buff *skb) ++{ ++ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; ++ int hdrlen; ++ ++ if (unlikely(skb->len < 10)) ++ return 0; ++ hdrlen = ieee80211_get_hdrlen(le16_to_cpu(hdr->frame_control)); ++ if (unlikely(hdrlen > skb->len)) ++ return 0; ++ return hdrlen; ++} ++ ++ ++#ifdef IEEE80211_VERBOSE_DEBUG_FRAME_DUMP ++static void ieee80211_dump_frame(const char *ifname, const char *title, ++ struct sk_buff *skb) ++{ ++ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; ++ u16 fc; ++ int hdrlen; ++ ++ printk(KERN_DEBUG "%s: %s (len=%d)", ifname, title, skb->len); ++ if (skb->len < 4) { ++ printk("\n"); ++ return; ++ } ++ ++ fc = le16_to_cpu(hdr->frame_control); ++ hdrlen = ieee80211_get_hdrlen(fc); ++ if (hdrlen > skb->len) ++ hdrlen = skb->len; ++ if (hdrlen >= 4) ++ printk(" FC=0x%04x DUR=0x%04x", ++ fc, le16_to_cpu(hdr->duration_id)); ++ if (hdrlen >= 10) ++ printk(" A1=" MACSTR, MAC2STR(hdr->addr1)); ++ if (hdrlen >= 16) ++ printk(" A2=" MACSTR, MAC2STR(hdr->addr2)); ++ if (hdrlen >= 24) ++ printk(" A3=" MACSTR, MAC2STR(hdr->addr3)); ++ if (hdrlen >= 30) ++ printk(" A4=" MACSTR, MAC2STR(hdr->addr4)); ++ printk("\n"); ++} ++#else /* IEEE80211_VERBOSE_DEBUG_FRAME_DUMP */ ++static inline void ieee80211_dump_frame(const char *ifname, const char *title, ++ struct sk_buff *skb) ++{ ++} ++#endif /* IEEE80211_VERBOSE_DEBUG_FRAME_DUMP */ ++ ++ ++static int ieee80211_is_eapol(struct sk_buff *skb) ++{ ++ struct ieee80211_hdr *hdr; ++ u16 fc; ++ int hdrlen; ++ ++ if (unlikely(skb->len < 10)) ++ return 0; ++ ++ hdr = (struct ieee80211_hdr *) skb->data; ++ fc = le16_to_cpu(hdr->frame_control); ++ ++ if (unlikely(!WLAN_FC_DATA_PRESENT(fc))) ++ return 0; ++ ++ hdrlen = ieee80211_get_hdrlen(fc); ++ ++ if (unlikely(skb->len >= hdrlen + sizeof(eapol_header) && ++ memcmp(skb->data + hdrlen, eapol_header, ++ sizeof(eapol_header)) == 0)) ++ return 1; ++ ++ return 0; ++} ++ ++ ++static ieee80211_txrx_result ++ieee80211_tx_h_rate_ctrl(struct ieee80211_txrx_data *tx) ++{ ++ struct rate_control_extra extra; ++ ++ memset(&extra, 0, sizeof(extra)); ++ extra.mgmt_data = tx->sdata && ++ tx->sdata->type == IEEE80211_SUB_IF_TYPE_MGMT; ++ extra.ethertype = tx->ethertype; ++ extra.startidx = 0; ++ extra.endidx = tx->local->num_curr_rates; ++ ++ tx->u.tx.rate = rate_control_get_rate(tx->dev, tx->skb, &extra); ++ if (unlikely(extra.probe != NULL)) { ++ tx->u.tx.control->rate_ctrl_probe = 1; ++ tx->u.tx.probe_last_frag = 1; ++ tx->u.tx.control->alt_retry_rate = tx->u.tx.rate->val; ++ tx->u.tx.rate = extra.probe; ++ } else { ++ tx->u.tx.control->alt_retry_rate = -1; ++ } ++ if (!tx->u.tx.rate) ++ return TXRX_DROP; ++ if (tx->local->conf.phymode == MODE_IEEE80211G && ++ tx->local->cts_protect_erp_frames && tx->fragmented && ++ extra.nonerp) { ++ tx->u.tx.last_frag_rate = tx->u.tx.rate; ++ tx->u.tx.last_frag_rateidx = extra.rateidx; ++ tx->u.tx.probe_last_frag = extra.probe ? 1 : 0; ++ ++ tx->u.tx.rate = extra.nonerp; ++ tx->u.tx.control->rateidx = extra.nonerp_idx; ++ tx->u.tx.control->rate_ctrl_probe = 0; ++ } else { ++ tx->u.tx.last_frag_rate = tx->u.tx.rate; ++ tx->u.tx.last_frag_rateidx = extra.rateidx; ++ tx->u.tx.control->rateidx = extra.rateidx; ++ } ++ tx->u.tx.control->tx_rate = tx->u.tx.rate->val; ++ if ((tx->u.tx.rate->flags & IEEE80211_RATE_PREAMBLE2) && ++ tx->local->short_preamble && ++ (!tx->sta || (tx->sta->flags & WLAN_STA_SHORT_PREAMBLE))) { ++ tx->u.tx.short_preamble = 1; ++ tx->u.tx.control->tx_rate = tx->u.tx.rate->val2; ++ } ++ ++ return TXRX_CONTINUE; ++} ++ ++ ++static ieee80211_txrx_result ++ieee80211_tx_h_select_key(struct ieee80211_txrx_data *tx) ++{ ++ if (tx->sta) ++ tx->u.tx.control->key_idx = tx->sta->key_idx_compression; ++ else ++ tx->u.tx.control->key_idx = HW_KEY_IDX_INVALID; ++ ++ if (unlikely(tx->u.tx.control->do_not_encrypt)) ++ tx->key = NULL; ++ else if (tx->sta && tx->sta->key) ++ tx->key = tx->sta->key; ++ else if (tx->sdata->default_key) ++ tx->key = tx->sdata->default_key; ++ else if (tx->sdata->drop_unencrypted && ++ !(tx->sdata->eapol && ieee80211_is_eapol(tx->skb))) { ++ I802_DEBUG_INC(tx->local->tx_handlers_drop_unencrypted); ++ return TXRX_DROP; ++ } else ++ tx->key = NULL; ++ ++ if (tx->key) { ++ tx->key->tx_rx_count++; ++ if (unlikely(tx->local->key_tx_rx_threshold && ++ tx->key->tx_rx_count > ++ tx->local->key_tx_rx_threshold)) { ++ ieee80211_key_threshold_notify(tx->dev, tx->key, ++ tx->sta); ++ } ++ } ++ ++ return TXRX_CONTINUE; ++} ++ ++ ++static ieee80211_txrx_result ++ieee80211_tx_h_fragment(struct ieee80211_txrx_data *tx) ++{ ++ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) tx->skb->data; ++ size_t hdrlen, per_fragm, num_fragm, payload_len, left; ++ struct sk_buff **frags, *first, *frag; ++ int i; ++ u8 *pos; ++ int frag_threshold = tx->local->fragmentation_threshold; ++ ++ if (!tx->fragmented) ++ return TXRX_CONTINUE; ++ ++ first = tx->skb; ++ ++ hdrlen = ieee80211_get_hdrlen(tx->fc); ++ payload_len = first->len - hdrlen; ++ per_fragm = frag_threshold - hdrlen - 4 /* FCS */; ++ num_fragm = (payload_len + per_fragm - 1) / per_fragm; ++ ++ frags = (struct sk_buff **) ++ kmalloc(num_fragm * sizeof(struct sk_buff *), GFP_ATOMIC); ++ if (frags == NULL) ++ goto fail; ++ memset(frags, 0, num_fragm * sizeof(struct sk_buff *)); ++ ++ hdr->frame_control |= cpu_to_le16(WLAN_FC_MOREFRAG); ++ pos = first->data + hdrlen + per_fragm; ++ left = payload_len - per_fragm; ++ for (i = 0; i < num_fragm - 1; i++) { ++ struct ieee80211_hdr *fhdr; ++ size_t copylen; ++ ++ if (left <= 0) ++ goto fail; ++ ++ /* reserve enough extra head and tail room for possible ++ * encryption */ ++#define IEEE80211_ENCRYPT_HEADROOM 8 ++#define IEEE80211_ENCRYPT_TAILROOM 12 ++ frag = frags[i] = ++ dev_alloc_skb(frag_threshold + ++ IEEE80211_ENCRYPT_HEADROOM + ++ IEEE80211_ENCRYPT_TAILROOM); ++ if (!frag) ++ goto fail; ++ /* Make sure that all fragments use the same priority so ++ * that they end up using the same TX queue */ ++ frag->priority = first->priority; ++ skb_reserve(frag, IEEE80211_ENCRYPT_HEADROOM); ++ fhdr = (struct ieee80211_hdr *) skb_put(frag, hdrlen); ++ memcpy(fhdr, first->data, hdrlen); ++ if (i == num_fragm - 2) ++ fhdr->frame_control &= cpu_to_le16(~WLAN_FC_MOREFRAG); ++ fhdr->seq_ctrl = cpu_to_le16(i + 1); ++ copylen = left > per_fragm ? per_fragm : left; ++ memcpy(skb_put(frag, copylen), pos, copylen); ++ ++ pos += copylen; ++ left -= copylen; ++ } ++ skb_trim(first, hdrlen + per_fragm); ++ ++ tx->u.tx.num_extra_frag = num_fragm - 1; ++ tx->u.tx.extra_frag = frags; ++ ++ return TXRX_CONTINUE; ++ ++ fail: ++ printk(KERN_DEBUG "%s: failed to fragment frame\n", tx->dev->name); ++ if (frags) { ++ for (i = 0; i < num_fragm - 1; i++) ++ if (frags[i]) ++ dev_kfree_skb(frags[i]); ++ kfree(frags); ++ } ++ I802_DEBUG_INC(tx->local->tx_handlers_drop_fragment); ++ return TXRX_DROP; ++} ++ ++ ++static int wep_encrypt_skb(struct ieee80211_txrx_data *tx, struct sk_buff *skb) ++{ ++ if (tx->key->force_sw_encrypt || tx->local->conf.sw_encrypt) { ++ if (ieee80211_wep_encrypt(tx->local, skb, tx->key)) ++ return -1; ++ } else { ++ tx->u.tx.control->key_idx = tx->key->hw_key_idx; ++ if (tx->local->hw->wep_include_iv) { ++ if (ieee80211_wep_add_iv(tx->local, skb, tx->key) == ++ NULL) ++ return -1; ++ } ++ } ++ return 0; ++} ++ ++ ++void ieee80211_tx_set_iswep(struct ieee80211_txrx_data *tx) ++{ ++ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) tx->skb->data; ++ ++ hdr->frame_control |= cpu_to_le16(WLAN_FC_ISWEP); ++ if (tx->u.tx.extra_frag) { ++ struct ieee80211_hdr *fhdr; ++ int i; ++ for (i = 0; i < tx->u.tx.num_extra_frag; i++) { ++ fhdr = (struct ieee80211_hdr *) ++ tx->u.tx.extra_frag[i]->data; ++ fhdr->frame_control |= cpu_to_le16(WLAN_FC_ISWEP); ++ } ++ } ++} ++ ++ ++static ieee80211_txrx_result ++ieee80211_tx_h_wep_encrypt(struct ieee80211_txrx_data *tx) ++{ ++ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) tx->skb->data; ++ u16 fc; ++ ++ fc = le16_to_cpu(hdr->frame_control); ++ ++ if (!tx->key || tx->key->alg != ALG_WEP || ++ (WLAN_FC_GET_TYPE(fc) != WLAN_FC_TYPE_DATA && ++ (WLAN_FC_GET_TYPE(fc) != WLAN_FC_TYPE_MGMT || ++ WLAN_FC_GET_STYPE(fc) != WLAN_FC_STYPE_AUTH))) ++ return TXRX_CONTINUE; ++ ++ tx->u.tx.control->iv_len = WEP_IV_LEN; ++ tx->u.tx.control->icv_len = WEP_ICV_LEN; ++ ieee80211_tx_set_iswep(tx); ++ ++ if (wep_encrypt_skb(tx, tx->skb) < 0) { ++ I802_DEBUG_INC(tx->local->tx_handlers_drop_wep); ++ return TXRX_DROP; ++ } ++ ++ if (tx->u.tx.extra_frag) { ++ int i; ++ for (i = 0; i < tx->u.tx.num_extra_frag; i++) { ++ if (wep_encrypt_skb(tx, tx->u.tx.extra_frag[i]) < 0) { ++ I802_DEBUG_INC(tx->local-> ++ tx_handlers_drop_wep); ++ return TXRX_DROP; ++ } ++ } ++ } ++ ++ return TXRX_CONTINUE; ++} ++ ++ ++static inline int ceiling_div(int dividend, int divisor) ++{ ++ return ((dividend + divisor - 1) / divisor); ++} ++ ++ ++static int ieee80211_frame_duration(struct ieee80211_local *local, size_t len, ++ int rate, int erp, int short_preamble) ++{ ++ int dur; ++ ++ /* calculate duration (in microseconds, rounded up to next higher ++ * integer if it includes a fractional microsecond) to send frame of ++ * len bytes (does not include FCS) at the given rate. Duration will ++ * also include SIFS. ++ * ++ * rate is in 100 kbps, so divident is multiplied by 10 in the ++ * ceiling_div() operations. ++ */ ++ ++ if (local->conf.phymode == MODE_IEEE80211A || erp || ++ local->conf.phymode == MODE_ATHEROS_TURBO) { ++ /* ++ * OFDM: ++ * ++ * N_DBPS = DATARATE x 4 ++ * N_SYM = Ceiling((16+8xLENGTH+6) / N_DBPS) ++ * (16 = SIGNAL time, 6 = tail bits) ++ * TXTIME = T_PREAMBLE + T_SIGNAL + T_SYM x N_SYM + Signal Ext ++ * ++ * T_SYM = 4 usec ++ * 802.11a - 17.5.2: aSIFSTime = 16 usec ++ * 802.11g - 19.8.4: aSIFSTime = 10 usec + ++ * signal ext = 6 usec ++ */ ++ /* FIX: Atheros Turbo may have different (shorter) duration? */ ++ dur = 16; /* SIFS + signal ext */ ++ dur += 16; /* 17.3.2.3: T_PREAMBLE = 16 usec */ ++ dur += 4; /* 17.3.2.3: T_SIGNAL = 4 usec */ ++ dur += 4 * ceiling_div((16 + 8 * (len + 4) + 6) * 10, ++ 4 * rate); /* T_SYM x N_SYM */ ++ } else { ++ /* ++ * 802.11b or 802.11g with 802.11b compatibility: ++ * 18.3.4: TXTIME = PreambleLength + PLCPHeaderTime + ++ * Ceiling(((LENGTH+PBCC)x8)/DATARATE). PBCC=0. ++ * ++ * 802.11 (DS): 15.3.3, 802.11b: 18.3.4 ++ * aSIFSTime = 10 usec ++ * aPreambleLength = 144 usec or 72 usec with short preamble ++ * aPLCPHeaderLength = 48 ms or 24 ms with short preamble ++ */ ++ dur = 10; /* aSIFSTime = 10 usec */ ++ dur += short_preamble ? (72 + 24) : (144 + 48); ++ ++ dur += ceiling_div(8 * (len + 4) * 10, rate); ++ } ++ ++ return dur; ++} ++ ++ ++static u16 ieee80211_duration(struct ieee80211_txrx_data *tx, int group_addr, ++ int next_frag_len) ++{ ++ int rate, mrate, erp, dur, i; ++ struct ieee80211_rate *txrate = tx->u.tx.rate; ++ struct ieee80211_local *local = tx->local; ++ ++ erp = txrate->flags & IEEE80211_RATE_ERP; ++ ++ /* ++ * data and mgmt (except PS Poll): ++ * - during CFP: 32768 ++ * - during contention period: ++ * if addr1 is group address: 0 ++ * if more fragments = 0 and addr1 is individual address: time to ++ * transmit one ACK plus SIFS ++ * if more fragments = 1 and addr1 is individual address: time to ++ * transmit next fragment plus 2 x ACK plus 3 x SIFS ++ * ++ * IEEE 802.11, 9.6: ++ * - control response frame (CTS or ACK) shall be transmitted using the ++ * same rate as the immediately previous frame in the frame exchange ++ * sequence, if this rate belongs to the PHY mandatory rates, or else ++ * at the highest possible rate belonging to the PHY rates in the ++ * BSSBasicRateSet ++ */ ++ ++ if (WLAN_FC_GET_TYPE(tx->fc) == WLAN_FC_TYPE_CTRL) { ++ /* TODO: These control frames are not currently sent by ++ * 80211.o, but should they be implemented, this function ++ * needs to be updated to support duration field calculation. ++ * ++ * RTS: time needed to transmit pending data/mgmt frame plus ++ * one CTS frame plus one ACK frame plus 3 x SIFS ++ * CTS: duration of immediately previous RTS minus time ++ * required to transmit CTS and its SIFS ++ * ACK: 0 if immediately previous directed data/mgmt had ++ * more=0, with more=1 duration in ACK frame is duration ++ * from previous frame minus time needed to transmit ACK ++ * and its SIFS ++ * PS Poll: BIT(15) | BIT(14) | aid ++ */ ++ return 0; ++ } ++ ++ /* data/mgmt */ ++ if (0 /* FIX: data/mgmt during CFP */) ++ return 32768; ++ ++ if (group_addr) /* Group address as the destination - no ACK */ ++ return 0; ++ ++ /* Individual destination address: ++ * IEEE 802.11, Ch. 9.6 (after IEEE 802.11g changes) ++ * CTS and ACK frames shall be transmitted using the highest rate in ++ * basic rate set that is less than or equal to the rate of the ++ * immediately previous frame and that is using the same modulation ++ * (CCK or OFDM). If no basic rate set matches with these requirements, ++ * the highest mandatory rate of the PHY that is less than or equal to ++ * the rate of the previous frame is used. ++ * Mandatory rates for IEEE 802.11g PHY: 1, 2, 5.5, 11, 6, 12, 24 Mbps ++ */ ++ rate = -1; ++ mrate = 10; /* use 1 Mbps if everything fails */ ++ for (i = 0; i < local->num_curr_rates; i++) { ++ struct ieee80211_rate *r = &local->curr_rates[i]; ++ if (r->rate > txrate->rate) ++ break; ++ ++ if (IEEE80211_RATE_MODULATION(txrate->flags) != ++ IEEE80211_RATE_MODULATION(r->flags)) ++ continue; ++ ++ if (r->flags & IEEE80211_RATE_BASIC) ++ rate = r->rate; ++ else if (r->flags & IEEE80211_RATE_MANDATORY) ++ mrate = r->rate; ++ } ++ if (rate == -1) { ++ /* No matching basic rate found; use highest suitable mandatory ++ * PHY rate */ ++ rate = mrate; ++ } ++ ++ /* Time needed to transmit ACK ++ * (10 bytes + 4-byte FCS = 112 bits) plus SIFS; rounded up ++ * to closest integer */ ++ ++ dur = ieee80211_frame_duration(local, 10, rate, erp, ++ local->short_preamble); ++ ++ if (next_frag_len) { ++ /* Frame is fragmented: duration increases with time needed to ++ * transmit next fragment plus ACK and 2 x SIFS. */ ++ dur *= 2; /* ACK + SIFS */ ++ /* next fragment */ ++ dur += ieee80211_frame_duration(local, next_frag_len, ++ txrate->rate, erp, ++ local->short_preamble); ++ } ++ ++ return dur; ++} ++ ++ ++static ieee80211_txrx_result ++ieee80211_tx_h_misc(struct ieee80211_txrx_data *tx) ++{ ++ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) tx->skb->data; ++ u16 dur; ++ struct ieee80211_tx_control *control = tx->u.tx.control; ++ ++ if (!MULTICAST_ADDR(hdr->addr1)) { ++ if (tx->skb->len >= tx->local->rts_threshold && ++ tx->local->rts_threshold < IEEE80211_MAX_RTS_THRESHOLD) { ++ control->use_rts_cts = 1; ++ control->retry_limit = ++ tx->local->long_retry_limit; ++ } else { ++ control->retry_limit = ++ tx->local->short_retry_limit; ++ } ++ } else { ++ control->retry_limit = 1; ++ } ++ ++ if (tx->fragmented) { ++ /* Do not use multiple retry rates when sending fragmented ++ * frames. ++ * TODO: The last fragment could still use multiple retry ++ * rates. */ ++ control->alt_retry_rate = -1; ++ } ++ ++ /* Use CTS protection for unicast frames sent using extended rates if ++ * there are associated non-ERP stations and RTS/CTS is not configured ++ * for the frame. */ ++ if (tx->local->conf.phymode == MODE_IEEE80211G && ++ (tx->u.tx.rate->flags & IEEE80211_RATE_ERP) && ++ tx->u.tx.unicast && ++ tx->local->cts_protect_erp_frames && ++ !control->use_rts_cts) ++ control->use_cts_protect = 1; ++ ++ /* Setup duration field for the first fragment of the frame. Duration ++ * for remaining fragments will be updated when they are being sent ++ * to low-level driver in ieee80211_tx(). */ ++ dur = ieee80211_duration(tx, MULTICAST_ADDR(hdr->addr1), ++ tx->fragmented ? tx->u.tx.extra_frag[0]->len : ++ 0); ++ hdr->duration_id = cpu_to_le16(dur); ++ ++ if (control->use_rts_cts || control->use_cts_protect) { ++ struct ieee80211_rate *rate; ++ int erp = tx->u.tx.rate->flags & IEEE80211_RATE_ERP; ++ ++ /* Do not use multiple retry rates when using RTS/CTS */ ++ control->alt_retry_rate = -1; ++ ++ /* Use min(data rate, max base rate) as CTS/RTS rate */ ++ rate = tx->u.tx.rate; ++ while (rate > tx->local->curr_rates && ++ !(rate->flags & IEEE80211_RATE_BASIC)) ++ rate--; ++ ++ if (control->use_rts_cts) ++ dur += ieee80211_frame_duration(tx->local, 10, ++ rate->rate, erp, ++ tx->local-> ++ short_preamble); ++ dur += ieee80211_frame_duration(tx->local, tx->skb->len, ++ tx->u.tx.rate->rate, erp, ++ tx->u.tx.short_preamble); ++ control->rts_cts_duration = dur; ++ control->rts_cts_rate = rate->val; ++ } ++ ++ if (tx->sta) { ++ tx->sta->tx_packets++; ++ tx->sta->tx_fragments++; ++ tx->sta->tx_bytes += tx->skb->len; ++ if (tx->u.tx.extra_frag) { ++ int i; ++ tx->sta->tx_fragments += tx->u.tx.num_extra_frag; ++ for (i = 0; i < tx->u.tx.num_extra_frag; i++) { ++ tx->sta->tx_bytes += ++ tx->u.tx.extra_frag[i]->len; ++ } ++ } ++ } ++ tx->local->scan.txrx_count++; ++ ++ return TXRX_CONTINUE; ++} ++ ++ ++static void ieee80211_rate_limit(unsigned long data) ++{ ++ struct ieee80211_local *local = (struct ieee80211_local *) data; ++ ++ if (local->rate_limit) { ++ local->rate_limit_bucket += local->rate_limit; ++ if (local->rate_limit_bucket > local->rate_limit_burst) ++ local->rate_limit_bucket = local->rate_limit_burst; ++ local->rate_limit_timer.expires = jiffies + HZ; ++ add_timer(&local->rate_limit_timer); ++ } ++} ++ ++ ++static ieee80211_txrx_result ++ieee80211_tx_h_rate_limit(struct ieee80211_txrx_data *tx) ++{ ++ ++ if (likely(!tx->local->rate_limit || tx->u.tx.unicast)) ++ return TXRX_CONTINUE; ++ ++ /* rate limit */ ++ if (tx->local->rate_limit_bucket) { ++ tx->local->rate_limit_bucket--; ++ return TXRX_CONTINUE; ++ } ++ ++ I802_DEBUG_INC(tx->local->tx_handlers_drop_rate_limit); ++ return TXRX_DROP; ++} ++ ++ ++static ieee80211_txrx_result ++ieee80211_tx_h_check_assoc(struct ieee80211_txrx_data *tx) ++{ ++#ifdef CONFIG_D80211_VERBOSE_DEBUG ++ struct sk_buff *skb = tx->skb; ++ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; ++#endif /* CONFIG_D80211_VERBOSE_DEBUG */ ++ u32 sta_flags; ++ ++ if (unlikely(tx->local->sta_scanning != 0) && ++ (WLAN_FC_GET_TYPE(tx->fc) != WLAN_FC_TYPE_MGMT || ++ WLAN_FC_GET_STYPE(tx->fc) != WLAN_FC_STYPE_PROBE_REQ)) ++ return TXRX_DROP; ++ ++ if (tx->u.tx.ps_buffered) ++ return TXRX_CONTINUE; ++ ++ sta_flags = tx->sta ? tx->sta->flags : 0; ++ ++ if (likely(tx->u.tx.unicast)) { ++ if (unlikely(!(sta_flags & WLAN_STA_ASSOC) && ++ tx->local->conf.mode != IW_MODE_ADHOC && ++ WLAN_FC_GET_TYPE(tx->fc) == WLAN_FC_TYPE_DATA)) { ++#ifdef CONFIG_D80211_VERBOSE_DEBUG ++ printk(KERN_DEBUG "%s: dropped data frame to not " ++ "associated station " MACSTR "\n", ++ tx->dev->name, MAC2STR(hdr->addr1)); ++#endif /* CONFIG_D80211_VERBOSE_DEBUG */ ++ I802_DEBUG_INC(tx->local->tx_handlers_drop_not_assoc); ++ return TXRX_DROP; ++ } ++ } else { ++ if (unlikely(WLAN_FC_GET_TYPE(tx->fc) == WLAN_FC_TYPE_DATA && ++ tx->local->num_sta == 0 && ++ !tx->local->allow_broadcast_always && ++ tx->local->conf.mode != IW_MODE_ADHOC)) { ++ /* ++ * No associated STAs - no need to send multicast ++ * frames. ++ */ ++ return TXRX_DROP; ++ } ++ return TXRX_CONTINUE; ++ } ++ ++ if (unlikely(!tx->u.tx.mgmt_interface && tx->sdata->ieee802_1x && ++ !(sta_flags & WLAN_STA_AUTHORIZED))) { ++#ifdef CONFIG_D80211_DEBUG ++ struct ieee80211_hdr *hdr = ++ (struct ieee80211_hdr *) tx->skb->data; ++ printk(KERN_DEBUG "%s: dropped frame to " MACSTR ++ " (unauthorized port)\n", tx->dev->name, ++ MAC2STR(hdr->addr1)); ++#endif ++ I802_DEBUG_INC(tx->local->tx_handlers_drop_unauth_port); ++ return TXRX_DROP; ++ } ++ ++ return TXRX_CONTINUE; ++} ++ ++ ++/* This function is called whenever the AP is about to exceed the maximum limit ++ * of buffered frames for power saving STAs. This situation should not really ++ * happen often during normal operation, so dropping the oldest buffered packet ++ * from each queue should be OK to make some room for new frames. */ ++static void purge_old_ps_buffers(struct ieee80211_local *local) ++{ ++ int total = 0, purged = 0; ++ struct sk_buff *skb; ++ struct list_head *ptr; ++ ++ spin_lock_bh(&local->sub_if_lock); ++ list_for_each(ptr, &local->sub_if_list) { ++ struct ieee80211_if_ap *ap; ++ struct ieee80211_sub_if_data *sdata = ++ list_entry(ptr, struct ieee80211_sub_if_data, list); ++ if (sdata->dev == local->mdev || ++ sdata->type != IEEE80211_SUB_IF_TYPE_AP) ++ continue; ++ ap = &sdata->u.ap; ++ skb = skb_dequeue(&ap->ps_bc_buf); ++ if (skb) { ++ purged++; ++ dev_kfree_skb(skb); ++ } ++ total += skb_queue_len(&ap->ps_bc_buf); ++ } ++ spin_unlock_bh(&local->sub_if_lock); ++ ++ spin_lock_bh(&local->sta_lock); ++ list_for_each(ptr, &local->sta_list) { ++ struct sta_info *sta = ++ list_entry(ptr, struct sta_info, list); ++ skb = skb_dequeue(&sta->ps_tx_buf); ++ if (skb) { ++ purged++; ++ dev_kfree_skb(skb); ++ } ++ total += skb_queue_len(&sta->ps_tx_buf); ++ } ++ spin_unlock_bh(&local->sta_lock); ++ ++ local->total_ps_buffered = total; ++ printk(KERN_DEBUG "%s: PS buffers full - purged %d frames\n", ++ local->mdev->name, purged); ++} ++ ++ ++static inline ieee80211_txrx_result ++ieee80211_tx_h_multicast_ps_buf(struct ieee80211_txrx_data *tx) ++{ ++ /* broadcast/multicast frame */ ++ /* If any of the associated stations is in power save mode, ++ * the frame is buffered to be sent after DTIM beacon frame */ ++ if (tx->local->hw->host_broadcast_ps_buffering && ++ tx->sdata->type != IEEE80211_SUB_IF_TYPE_WDS && ++ tx->sdata->bss && atomic_read(&tx->sdata->bss->num_sta_ps) && ++ !(tx->fc & WLAN_FC_ORDER)) { ++ if (tx->local->total_ps_buffered >= TOTAL_MAX_TX_BUFFER) ++ purge_old_ps_buffers(tx->local); ++ if (skb_queue_len(&tx->sdata->bss->ps_bc_buf) >= ++ AP_MAX_BC_BUFFER) { ++ if (net_ratelimit()) { ++ printk(KERN_DEBUG "%s: BC TX buffer full - " ++ "dropping the oldest frame\n", ++ tx->dev->name); ++ } ++ dev_kfree_skb(skb_dequeue(&tx->sdata->bss->ps_bc_buf)); ++ } else ++ tx->local->total_ps_buffered++; ++ skb_queue_tail(&tx->sdata->bss->ps_bc_buf, tx->skb); ++ return TXRX_QUEUED; ++ } ++ ++ return TXRX_CONTINUE; ++} ++ ++ ++static inline ieee80211_txrx_result ++ieee80211_tx_h_unicast_ps_buf(struct ieee80211_txrx_data *tx) ++{ ++ struct sta_info *sta = tx->sta; ++ ++ if (unlikely(!sta || ++ (WLAN_FC_GET_TYPE(tx->fc) == WLAN_FC_TYPE_MGMT && ++ WLAN_FC_GET_STYPE(tx->fc) == WLAN_FC_STYPE_PROBE_RESP))) ++ return TXRX_CONTINUE; ++ ++ if (unlikely((sta->flags & WLAN_STA_PS) && !sta->pspoll)) { ++ struct ieee80211_tx_packet_data *pkt_data; ++#ifdef IEEE80211_VERBOSE_DEBUG_PS ++ printk(KERN_DEBUG "STA " MACSTR " aid %d: PS buffer (entries " ++ "before %d)\n", ++ MAC2STR(sta->addr), sta->aid, ++ skb_queue_len(&sta->ps_tx_buf)); ++#endif /* IEEE80211_VERBOSE_DEBUG_PS */ ++ sta->flags |= WLAN_STA_TIM; ++ if (tx->local->total_ps_buffered >= TOTAL_MAX_TX_BUFFER) ++ purge_old_ps_buffers(tx->local); ++ if (skb_queue_len(&sta->ps_tx_buf) >= STA_MAX_TX_BUFFER) { ++ struct sk_buff *old = skb_dequeue(&sta->ps_tx_buf); ++ if (net_ratelimit()) { ++ printk(KERN_DEBUG "%s: STA " MACSTR " TX " ++ "buffer full - dropping oldest frame\n", ++ tx->dev->name, MAC2STR(sta->addr)); ++ } ++ dev_kfree_skb(old); ++ } else ++ tx->local->total_ps_buffered++; ++ /* Queue frame to be sent after STA sends an PS Poll frame */ ++ if (skb_queue_empty(&sta->ps_tx_buf) && tx->local->hw->set_tim) ++ tx->local->hw->set_tim(tx->dev, sta->aid, 1); ++ pkt_data = (struct ieee80211_tx_packet_data *)tx->skb->cb; ++ pkt_data->jiffies = jiffies; ++ skb_queue_tail(&sta->ps_tx_buf, tx->skb); ++ return TXRX_QUEUED; ++ } ++#ifdef IEEE80211_VERBOSE_DEBUG_PS ++ else if (unlikely(sta->flags & WLAN_STA_PS)) { ++ printk(KERN_DEBUG "%s: STA " MACSTR " in PS mode, but pspoll " ++ "set -> send frame\n", tx->dev->name, ++ MAC2STR(sta->addr)); ++ } ++#endif /* IEEE80211_VERBOSE_DEBUG_PS */ ++ sta->pspoll = 0; ++ ++ return TXRX_CONTINUE; ++} ++ ++ ++static ieee80211_txrx_result ++ieee80211_tx_h_ps_buf(struct ieee80211_txrx_data *tx) ++{ ++ if (unlikely(tx->u.tx.ps_buffered)) ++ return TXRX_CONTINUE; ++ ++ if (tx->u.tx.unicast) ++ return ieee80211_tx_h_unicast_ps_buf(tx); ++ else ++ return ieee80211_tx_h_multicast_ps_buf(tx); ++} ++ ++ ++static void inline ieee80211_tx_prepare(struct ieee80211_txrx_data *tx, ++ struct sk_buff *skb, ++ struct net_device *dev, ++ struct ieee80211_tx_control *control) ++{ ++ struct ieee80211_local *local = dev->priv; ++ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; ++ struct ieee80211_tx_packet_data *pkt_data; ++ int hdrlen; ++ ++ pkt_data = (struct ieee80211_tx_packet_data *)skb->cb; ++ ++ memset(tx, 0, sizeof(*tx)); ++ tx->skb = skb; ++ tx->dev = pkt_data->sdata->dev; /* use original interface */ ++ tx->local = local; ++ tx->sdata = pkt_data->sdata; ++ tx->sta = sta_info_get(local, hdr->addr1); ++ tx->fc = le16_to_cpu(hdr->frame_control); ++ control->power_level = local->conf.power_level; ++ tx->u.tx.control = control; ++ tx->u.tx.unicast = !MULTICAST_ADDR(hdr->addr1); ++ control->no_ack = MULTICAST_ADDR(hdr->addr1); ++ tx->fragmented = local->fragmentation_threshold < ++ IEEE80211_MAX_FRAG_THRESHOLD && tx->u.tx.unicast && ++ skb->len + 4 /* FCS */ > local->fragmentation_threshold && ++ (local->hw->set_frag_threshold == NULL); ++ if (tx->sta == NULL) ++ control->clear_dst_mask = 1; ++ else if (tx->sta->clear_dst_mask) { ++ control->clear_dst_mask = 1; ++ tx->sta->clear_dst_mask = 0; ++ } ++ control->antenna_sel = local->conf.antenna_sel; ++ if (local->sta_antenna_sel != STA_ANTENNA_SEL_AUTO && tx->sta) ++ control->antenna_sel = tx->sta->antenna_sel; ++ hdrlen = ieee80211_get_hdrlen(tx->fc); ++ if (skb->len > hdrlen + sizeof(rfc1042_header) + 2) { ++ u8 *pos = &skb->data[hdrlen + sizeof(rfc1042_header)]; ++ tx->ethertype = (pos[0] << 8) | pos[1]; ++ } ++ ++} ++ ++ ++static int ieee80211_tx(struct net_device *dev, struct sk_buff *skb, ++ struct ieee80211_tx_control *control, int mgmt) ++{ ++ struct ieee80211_local *local = dev->priv; ++ struct sta_info *sta; ++ ieee80211_tx_handler *handler; ++ struct ieee80211_txrx_data tx; ++ ieee80211_txrx_result res = TXRX_DROP; ++ int ret, i; ++ ++ if (unlikely(skb->len < 10)) { ++ dev_kfree_skb(skb); ++ return 0; ++ } ++ ++ ieee80211_tx_prepare(&tx, skb, dev, control); ++ sta = tx.sta; ++ tx.u.tx.mgmt_interface = mgmt; ++ ++ for (handler = local->tx_handlers; *handler != NULL; handler++) { ++ res = (*handler)(&tx); ++ if (res != TXRX_CONTINUE) ++ break; ++ } ++ ++ skb = tx.skb; /* handlers are allowed to change skb */ ++ ++ if (sta) ++ sta_info_release(local, sta); ++ ++ if (unlikely(res == TXRX_DROP)) { ++ I802_DEBUG_INC(local->tx_handlers_drop); ++ goto drop; ++ } ++ ++ if (unlikely(res == TXRX_QUEUED)) { ++ I802_DEBUG_INC(local->tx_handlers_queued); ++ return 0; ++ } ++ ++ ieee80211_dump_frame(dev->name, "TX to low-level driver", skb); ++ ret = local->hw->tx(dev, skb, control); ++#ifdef IEEE80211_LEDS ++ if (!ret && local->tx_led_counter++ == 0) { ++ ieee80211_tx_led(1, dev); ++ } ++#endif /* IEEE80211_LEDS */ ++ if (tx.u.tx.extra_frag) { ++ if (ret > 0) { ++ /* Must free all fragments and return 0 since skb data ++ * has been fragmented into multiple buffers. ++ * TODO: could free extra fragments and restore skb to ++ * the original form since the data is still there and ++ * then return nonzero so that Linux netif would ++ * retry. */ ++ goto drop; ++ } ++ ++ skb = NULL; /* skb is now owned by low-level driver */ ++ control->use_rts_cts = 0; ++ control->use_cts_protect = 0; ++ control->clear_dst_mask = 0; ++ for (i = 0; i < tx.u.tx.num_extra_frag; i++) { ++ int next_len, dur; ++ struct ieee80211_hdr *hdr = ++ (struct ieee80211_hdr *) ++ tx.u.tx.extra_frag[i]->data; ++ if (i + 1 < tx.u.tx.num_extra_frag) ++ next_len = tx.u.tx.extra_frag[i + 1]->len; ++ else { ++ next_len = 0; ++ tx.u.tx.rate = tx.u.tx.last_frag_rate; ++ tx.u.tx.control->tx_rate = tx.u.tx.rate->val; ++ tx.u.tx.control->rateidx = ++ tx.u.tx.last_frag_rateidx; ++ tx.u.tx.control->rate_ctrl_probe = ++ tx.u.tx.probe_last_frag; ++ } ++ dur = ieee80211_duration(&tx, 0, next_len); ++ hdr->duration_id = cpu_to_le16(dur); ++ ++ ieee80211_dump_frame(dev->name, ++ "TX to low-level driver", skb); ++ ret = local->hw->tx(dev, tx.u.tx.extra_frag[i], ++ control); ++ if (ret > 0) ++ goto drop; ++#ifdef IEEE80211_LEDS ++ if (local->tx_led_counter++ == 0) { ++ ieee80211_tx_led(1, dev); ++ } ++#endif /* IEEE80211_LEDS */ ++ tx.u.tx.extra_frag[i] = NULL; ++ } ++ kfree(tx.u.tx.extra_frag); ++ } ++ if (ret == -1) ++ ret = 0; ++ return ret; ++ ++ drop: ++ if (skb) ++ dev_kfree_skb(skb); ++ for (i = 0; i < tx.u.tx.num_extra_frag; i++) ++ if (tx.u.tx.extra_frag[i]) ++ dev_kfree_skb(tx.u.tx.extra_frag[i]); ++ kfree(tx.u.tx.extra_frag); ++ return 0; ++} ++ ++ ++static int ieee80211_master_start_xmit(struct sk_buff *skb, ++ struct net_device *dev) ++{ ++ struct ieee80211_tx_control control; ++ struct ieee80211_tx_packet_data *pkt_data; ++ struct ieee80211_sub_if_data *sdata; ++ int ret; ++ ++ sdata = IEEE80211_DEV_TO_SUB_IF(dev); ++ ++ /* ++ * copy control out of the skb so other people can use skb->cb ++ */ ++ pkt_data = (struct ieee80211_tx_packet_data *)skb->cb; ++ memset(&control, 0, sizeof(struct ieee80211_tx_control)); ++ control.sdata = pkt_data->sdata; ++ control.req_tx_status = pkt_data->req_tx_status; ++ control.do_not_encrypt = pkt_data->do_not_encrypt; ++ control.pkt_type = ++ pkt_data->pkt_probe_resp ? PKT_PROBE_RESP : PKT_NORMAL; ++ control.requeue = pkt_data->requeue; ++ control.queue = pkt_data->queue; ++ ++ ret = ieee80211_tx(dev, skb, &control, ++ control.sdata->type == IEEE80211_SUB_IF_TYPE_MGMT); ++ ++ return ret; ++} ++ ++ ++/** ++ * ieee80211_subif_start_xmit - netif start_xmit function for Ethernet-type ++ * subinterfaces (wlan#, WDS, and VLAN interfaces) ++ * @skb: packet to be sent ++ * @dev: incoming interface ++ * ++ * Returns: 0 on success (and frees skb in this case) or 1 on failure (skb will ++ * not be freed, and caller is responsible for either retrying later or freeing ++ * skb). ++ * ++ * This function takes in an Ethernet header and encapsulates it with suitable ++ * IEEE 802.11 header based on which interface the packet is coming in. The ++ * encapsulated packet will then be passed to master interface, wlan#.11, for ++ * transmission (through low-level driver). ++ */ ++static int ieee80211_subif_start_xmit(struct sk_buff *skb, ++ struct net_device *dev) ++{ ++ struct ieee80211_local *local = (struct ieee80211_local *) dev->priv; ++ struct ieee80211_tx_packet_data *pkt_data; ++ struct ieee80211_sub_if_data *sdata; ++ int ret = 1, head_need; ++ u16 ethertype, hdrlen, fc; ++ struct ieee80211_hdr hdr; ++ u8 *encaps_data; ++ int encaps_len, skip_header_bytes; ++ int nh_pos, h_pos, no_encrypt = 0; ++ struct sta_info *sta; ++ ++ sdata = IEEE80211_DEV_TO_SUB_IF(dev); ++ if (unlikely(skb->len < ETH_HLEN)) { ++ printk(KERN_DEBUG "%s: short skb (len=%d)\n", ++ dev->name, skb->len); ++ ret = 0; ++ goto fail; ++ } ++ ++ nh_pos = skb->nh.raw - skb->data; ++ h_pos = skb->h.raw - skb->data; ++ ++ /* convert Ethernet header to proper 802.11 header (based on ++ * operation mode) */ ++ ethertype = (skb->data[12] << 8) | skb->data[13]; ++ /* TODO: handling for 802.1x authorized/unauthorized port */ ++ fc = (WLAN_FC_TYPE_DATA << 2) | (WLAN_FC_STYPE_DATA << 4); ++ ++ if (likely(sdata->type == IEEE80211_SUB_IF_TYPE_AP || ++ sdata->type == IEEE80211_SUB_IF_TYPE_VLAN)) { ++ if (local->conf.mode == IW_MODE_MASTER) { ++ fc |= WLAN_FC_FROMDS; ++ /* DA BSSID SA */ ++ memcpy(hdr.addr1, skb->data, ETH_ALEN); ++ memcpy(hdr.addr2, dev->dev_addr, ETH_ALEN); ++ memcpy(hdr.addr3, skb->data + ETH_ALEN, ETH_ALEN); ++ } else if (local->conf.mode == IW_MODE_INFRA) { ++ fc |= WLAN_FC_TODS; ++ /* BSSID SA DA */ ++ memcpy(hdr.addr1, local->bssid, ETH_ALEN); ++ memcpy(hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN); ++ memcpy(hdr.addr3, skb->data, ETH_ALEN); ++ } else if (local->conf.mode == IW_MODE_ADHOC) { ++ /* DA SA BSSID */ ++ memcpy(hdr.addr1, skb->data, ETH_ALEN); ++ memcpy(hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN); ++ memcpy(hdr.addr3, local->bssid, ETH_ALEN); ++ } ++ hdrlen = 24; ++ } else if (sdata->type == IEEE80211_SUB_IF_TYPE_WDS) { ++ fc |= WLAN_FC_FROMDS | WLAN_FC_TODS; ++ /* RA TA DA SA */ ++ memcpy(hdr.addr1, sdata->u.wds.remote_addr, ETH_ALEN); ++ memcpy(hdr.addr2, dev->dev_addr, ETH_ALEN); ++ memcpy(hdr.addr3, skb->data, ETH_ALEN); ++ memcpy(hdr.addr4, skb->data + ETH_ALEN, ETH_ALEN); ++ hdrlen = 30; ++ } else if (sdata->type == IEEE80211_SUB_IF_TYPE_STA) { ++ if (local->conf.mode == IW_MODE_INFRA) { ++ fc |= WLAN_FC_TODS; ++ /* BSSID SA DA */ ++ memcpy(hdr.addr1, sdata->u.sta.bssid, ETH_ALEN); ++ memcpy(hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN); ++ memcpy(hdr.addr3, skb->data, ETH_ALEN); ++ } else { ++ /* DA SA BSSID */ ++ memcpy(hdr.addr1, skb->data, ETH_ALEN); ++ memcpy(hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN); ++ memcpy(hdr.addr3, sdata->u.sta.bssid, ETH_ALEN); ++ } ++ hdrlen = 24; ++ } else { ++ ret = 0; ++ goto fail; ++ } ++ ++ /* receiver is QoS enabled, use a QoS type frame */ ++ sta = sta_info_get(local, hdr.addr1); ++ if (sta) { ++ if (sta->flags & WLAN_STA_WME) { ++ fc |= WLAN_FC_STYPE_QOS_DATA << 4; ++ hdrlen += 2; ++ } ++ sta_info_release(local, sta); ++ } ++ ++ hdr.frame_control = cpu_to_le16(fc); ++ hdr.duration_id = 0; ++ hdr.seq_ctrl = 0; ++ ++ skip_header_bytes = ETH_HLEN; ++ if (ethertype == ETH_P_AARP || ethertype == ETH_P_IPX) { ++ encaps_data = bridge_tunnel_header; ++ encaps_len = sizeof(bridge_tunnel_header); ++ skip_header_bytes -= 2; ++ } else if (ethertype >= 0x600) { ++ encaps_data = rfc1042_header; ++ encaps_len = sizeof(rfc1042_header); ++ skip_header_bytes -= 2; ++ } else { ++ encaps_data = NULL; ++ encaps_len = 0; ++ } ++ ++ skb_pull(skb, skip_header_bytes); ++ nh_pos -= skip_header_bytes; ++ h_pos -= skip_header_bytes; ++ ++ /* TODO: implement support for fragments so that there is no need to ++ * reallocate and copy payload; it might be enough to support one ++ * extra fragment that would be copied in the beginning of the frame ++ * data.. anyway, it would be nice to include this into skb structure ++ * somehow ++ * ++ * There are few options for this: ++ * use skb->cb as an extra space for 802.11 header ++ * allocate new buffer if not enough headroom ++ * make sure that there is enough headroom in every skb by increasing ++ * build in headroom in __dev_alloc_skb() (linux/skbuff.h) and ++ * alloc_skb() (net/core/skbuff.c) ++ */ ++ head_need = hdrlen + encaps_len + (local->hw->extra_hdr_room ? 2 : 0); ++ head_need -= skb_headroom(skb); ++ ++ /* We are going to modify skb data, so make a copy of it if happens to ++ * be cloned. This could happen, e.g., with Linux bridge code passing ++ * us broadcast frames. */ ++ ++ if (head_need > 0 || skb_cloned(skb)) { ++#if 0 ++ printk(KERN_DEBUG "%s: need to reallocate buffer for %d bytes " ++ "of headroom\n", dev->name, head_need); ++#endif ++ ++ if (skb_cloned(skb)) ++ I802_DEBUG_INC(local->tx_expand_skb_head_cloned); ++ else ++ I802_DEBUG_INC(local->tx_expand_skb_head); ++ /* Since we have to reallocate the buffer, make sure that there ++ * is enough room for possible WEP IV/ICV and TKIP (8 bytes ++ * before payload and 12 after). */ ++ if (pskb_expand_head(skb, (head_need > 0 ? head_need + 8 : 8), ++ 12, GFP_ATOMIC)) { ++ printk(KERN_DEBUG "%s: failed to reallocate TX buffer" ++ "\n", dev->name); ++ goto fail; ++ } ++ } ++ ++ if (encaps_data) { ++ memcpy(skb_push(skb, encaps_len), encaps_data, encaps_len); ++ nh_pos += encaps_len; ++ h_pos += encaps_len; ++ } ++ memcpy(skb_push(skb, hdrlen), &hdr, hdrlen); ++ nh_pos += hdrlen; ++ h_pos += hdrlen; ++ ++ pkt_data = (struct ieee80211_tx_packet_data *)skb->cb; ++ memset(pkt_data, 0, sizeof(struct ieee80211_tx_packet_data)); ++ pkt_data->sdata = sdata; ++ pkt_data->do_not_encrypt = no_encrypt; ++ ++ skb->dev = sdata->master; ++ sdata->stats.tx_packets++; ++ sdata->stats.tx_bytes += skb->len; ++ ++ /* Update skb pointers to various headers since this modified frame ++ * is going to go through Linux networking code that may potentially ++ * need things like pointer to IP header. */ ++ skb->mac.raw = skb->data; ++ skb->nh.raw = skb->data + nh_pos; ++ skb->h.raw = skb->data + h_pos; ++ ++ dev_queue_xmit(skb); ++ ++ return 0; ++ ++ fail: ++ if (!ret) ++ dev_kfree_skb(skb); ++ ++ return ret; ++} ++ ++ ++/* ++ * This is the transmit routine for the 802.11 type interfaces ++ * called by upper layers of the linux networking ++ * stack when it has a frame to transmit ++ */ ++static int ++ieee80211_mgmt_start_xmit(struct sk_buff *skb, struct net_device *dev) ++{ ++ struct ieee80211_sub_if_data *sdata; ++ struct ieee80211_tx_packet_data *pkt_data; ++ struct ieee80211_hdr *hdr; ++ u16 fc; ++ ++ sdata = IEEE80211_DEV_TO_SUB_IF(dev); ++ ++ if (skb->len < 10) { ++ dev_kfree_skb(skb); ++ return 0; ++ } ++ ++ hdr = (struct ieee80211_hdr *) skb->data; ++ fc = le16_to_cpu(hdr->frame_control); ++ ++ pkt_data = (struct ieee80211_tx_packet_data *) skb->cb; ++ memset(pkt_data, 0, sizeof(struct ieee80211_tx_packet_data)); ++ pkt_data->sdata = sdata; ++ ++ if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT && ++ WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_PROBE_RESP) ++ pkt_data->pkt_probe_resp = 1; ++ ++ skb->priority = 20; /* use hardcoded priority for mgmt TX queue */ ++ skb->dev = sdata->master; ++ ++ /* ++ * We're using the protocol field of the the frame control header ++ * to request TX callback for hostapd. BIT(1) is checked. ++ */ ++ if ((fc & BIT(1)) == BIT(1)) { ++ pkt_data->req_tx_status = 1; ++ fc &= ~BIT(1); ++ hdr->frame_control = cpu_to_le16(fc); ++ } ++ ++ pkt_data->do_not_encrypt = !(fc & WLAN_FC_ISWEP); ++ ++ sdata->stats.tx_packets++; ++ sdata->stats.tx_bytes += skb->len; ++ ++ dev_queue_xmit(skb); ++ ++ return 0; ++} ++ ++ ++static void ieee80211_beacon_add_tim(struct ieee80211_local *local, ++ struct ieee80211_if_ap *bss, ++ struct sk_buff *skb) ++{ ++ u8 *pos, *tim; ++ int aid0 = 0; ++ int i, num_bits = 0, n1, n2; ++ u8 bitmap[251]; ++ ++ /* Generate bitmap for TIM only if there are any STAs in power save ++ * mode. */ ++ if (atomic_read(&bss->num_sta_ps) > 0 && bss->max_aid > 0) { ++ memset(bitmap, 0, sizeof(bitmap)); ++ spin_lock_bh(&local->sta_lock); ++ for (i = 0; i < bss->max_aid; i++) { ++ if (bss->sta_aid[i] && ++ (!skb_queue_empty(&bss->sta_aid[i]->ps_tx_buf) || ++ !skb_queue_empty(&bss->sta_aid[i]->tx_filtered))) ++ { ++ bitmap[(i + 1) / 8] |= 1 << (i + 1) % 8; ++ num_bits++; ++ } ++ } ++ spin_unlock_bh(&local->sta_lock); ++ } ++ ++ if (bss->dtim_count == 0) ++ bss->dtim_count = bss->dtim_period - 1; ++ else ++ bss->dtim_count--; ++ ++ tim = pos = (u8 *) skb_put(skb, 6); ++ *pos++ = WLAN_EID_TIM; ++ *pos++ = 4; ++ *pos++ = bss->dtim_count; ++ *pos++ = bss->dtim_period; ++ ++ if (bss->dtim_count == 0 && !skb_queue_empty(&bss->ps_bc_buf)) { ++ aid0 = 1; ++ } ++ ++ if (num_bits) { ++ /* Find largest even number N1 so that bits numbered 1 through ++ * (N1 x 8) - 1 in the bitmap are 0 and number N2 so that bits ++ * (N2 + 1) x 8 through 2007 are 0. */ ++ n1 = 0; ++ for (i = 0; i < sizeof(bitmap); i++) { ++ if (bitmap[i]) { ++ n1 = i & 0xfe; ++ break; ++ } ++ } ++ n2 = n1; ++ for (i = sizeof(bitmap) - 1; i >= n1; i--) { ++ if (bitmap[i]) { ++ n2 = i; ++ break; ++ } ++ } ++ ++ /* Bitmap control */ ++ *pos++ = n1 | (aid0 ? 1 : 0); ++ /* Part Virt Bitmap */ ++ memcpy(pos, bitmap + n1, n2 - n1 + 1); ++ ++ tim[1] = n2 - n1 + 4; ++ skb_put(skb, n2 - n1); ++ } else { ++ *pos++ = aid0 ? 1 : 0; /* Bitmap control */ ++ *pos++ = 0; /* Part Virt Bitmap */ ++ } ++} ++ ++ ++struct sk_buff * ieee80211_beacon_get(struct net_device *dev, int bss_idx, ++ struct ieee80211_tx_control *control) ++{ ++ struct ieee80211_local *local = dev->priv; ++ struct sk_buff *skb; ++ struct net_device *bdev; ++ struct ieee80211_sub_if_data *sdata = NULL; ++ struct ieee80211_if_ap *ap = NULL; ++ struct ieee80211_rate *rate; ++ struct rate_control_extra extra; ++ u8 *b_head, *b_tail; ++ int bh_len, bt_len; ++ ++ spin_lock_bh(&local->sub_if_lock); ++ if (bss_idx < 0 || bss_idx >= local->bss_dev_count) ++ bdev = NULL; ++ else { ++ bdev = local->bss_devs[bss_idx]; ++ sdata = IEEE80211_DEV_TO_SUB_IF(bdev); ++ ap = &sdata->u.ap; ++ } ++ spin_unlock_bh(&local->sub_if_lock); ++ ++ if (bdev == NULL || ap == NULL || ap->beacon_head == NULL) { ++#ifdef CONFIG_D80211_VERBOSE_DEBUG ++ if (net_ratelimit()) ++ printk(KERN_DEBUG "no beacon data avail for idx=%d " ++ "(%s)\n", bss_idx, bdev ? bdev->name : "N/A"); ++#endif /* CONFIG_D80211_VERBOSE_DEBUG */ ++ return NULL; ++ } ++ ++ /* Assume we are generating the normal beacon locally */ ++ b_head = ap->beacon_head; ++ b_tail = ap->beacon_tail; ++ bh_len = ap->beacon_head_len; ++ bt_len = ap->beacon_tail_len; ++ ++ skb = dev_alloc_skb(bh_len + bt_len + 256 /* maximum TIM len */); ++ if (!skb) ++ return NULL; ++ ++ memcpy(skb_put(skb, bh_len), b_head, bh_len); ++ ++ ieee80211_beacon_add_tim(local, ap, skb); ++ ++ if (b_tail) { ++ memcpy(skb_put(skb, bt_len), b_tail, bt_len); ++ } ++ ++ memset(&extra, 0, sizeof(extra)); ++ extra.endidx = local->num_curr_rates; ++ ++ rate = rate_control_get_rate(dev, skb, &extra); ++ if (rate == NULL) { ++ if (net_ratelimit()) { ++ printk(KERN_DEBUG "%s: ieee80211_beacon_get: no rate " ++ "found\n", dev->name); ++ } ++ dev_kfree_skb(skb); ++ return NULL; ++ } ++ ++ control->tx_rate = (local->short_preamble && ++ (rate->flags & IEEE80211_RATE_PREAMBLE2)) ? ++ rate->val2 : rate->val; ++ control->antenna_sel = local->conf.antenna_sel; ++ control->power_level = local->conf.power_level; ++ control->no_ack = 1; ++ control->retry_limit = 1; ++ control->rts_cts_duration = 0; ++ control->clear_dst_mask = 1; ++ ++ ap->num_beacons++; ++ return skb; ++} ++ ++ ++struct sk_buff * ++ieee80211_get_buffered_bc(struct net_device *dev, int bss_idx, ++ struct ieee80211_tx_control *control) ++{ ++ struct ieee80211_local *local = dev->priv; ++ struct sk_buff *skb; ++ struct sta_info *sta; ++ ieee80211_tx_handler *handler; ++ struct ieee80211_txrx_data tx; ++ ieee80211_txrx_result res = TXRX_DROP; ++ struct net_device *bdev; ++ struct ieee80211_sub_if_data *sdata; ++ struct ieee80211_if_ap *bss; ++ ++ spin_lock_bh(&local->sub_if_lock); ++ if (bss_idx < 0 || bss_idx >= local->bss_dev_count) { ++ bdev = NULL; ++ bss = NULL; ++ } else { ++ bdev = local->bss_devs[bss_idx]; ++ sdata = IEEE80211_DEV_TO_SUB_IF(bdev); ++ bss = &sdata->u.ap; ++ } ++ spin_unlock_bh(&local->sub_if_lock); ++ if (bdev == NULL || bss == NULL || bss->beacon_head == NULL) ++ return NULL; ++ ++ if (bss->dtim_count != 0) ++ return NULL; /* send buffered bc/mc only after DTIM beacon */ ++ skb = skb_dequeue(&bss->ps_bc_buf); ++ memset(control, 0, sizeof(*control)); ++ if (skb == NULL) ++ return NULL; ++ local->total_ps_buffered--; ++ ++ if (!skb_queue_empty(&bss->ps_bc_buf) && skb->len >= 2) { ++ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; ++ /* more buffered multicast/broadcast frames ==> set MoreData ++ * flag in IEEE 802.11 header to inform PS STAs */ ++ hdr->frame_control |= cpu_to_le16(WLAN_FC_MOREDATA); ++ } ++ ++ ieee80211_tx_prepare(&tx, skb, dev, control); ++ sta = tx.sta; ++ tx.u.tx.ps_buffered = 1; ++ ++ for (handler = local->tx_handlers; *handler != NULL; handler++) { ++ res = (*handler)(&tx); ++ if (res == TXRX_DROP || res == TXRX_QUEUED) ++ break; ++ } ++ ++ if (res == TXRX_DROP) { ++ I802_DEBUG_INC(local->tx_handlers_drop); ++ dev_kfree_skb(skb); ++ skb = NULL; ++ } else if (res == TXRX_QUEUED) { ++ I802_DEBUG_INC(local->tx_handlers_queued); ++ skb = NULL; ++ } ++ ++ if (sta) ++ sta_info_release(local, sta); ++ ++ return skb; ++} ++ ++ ++int ieee80211_hw_config(struct net_device *dev) ++{ ++ struct ieee80211_local *local = dev->priv; ++ int i, ret = 0; ++ ++#ifdef CONFIG_D80211_VERBOSE_DEBUG ++ printk(KERN_DEBUG "HW CONFIG: channel=%d freq=%d mode=%d " ++ "phymode=%d\n", local->conf.channel, local->conf.freq, ++ local->conf.mode, local->conf.phymode); ++#endif /* CONFIG_D80211_VERBOSE_DEBUG */ ++ ++ if (local->hw->config) ++ ret = local->hw->config(dev, &local->conf); ++ ++ for (i = 0; i < local->hw->num_modes; i++) { ++ struct ieee80211_hw_modes *mode = &local->hw->modes[i]; ++ if (mode->mode == local->conf.phymode) { ++ if (local->curr_rates != mode->rates) { ++ rate_control_clear(local); ++ } ++ local->curr_rates = mode->rates; ++ local->num_curr_rates = mode->num_rates; ++ ieee80211_prepare_rates(dev); ++ break; ++ } ++ } ++ ++ return ret; ++} ++ ++ ++struct ieee80211_conf *ieee80211_get_hw_conf(struct net_device *dev) ++{ ++ struct ieee80211_local *local = dev->priv; ++ return &local->conf; ++} ++ ++ ++static int ieee80211_change_mtu(struct net_device *dev, int new_mtu) ++{ ++ /* FIX: what would be proper limits for MTU? ++ * This interface uses 802.3 frames. */ ++ if (new_mtu < 256 || new_mtu > 2304 - 24 - 6) { ++ printk(KERN_WARNING "%s: invalid MTU %d\n", ++ dev->name, new_mtu); ++ return -EINVAL; ++ } ++ ++#ifdef CONFIG_D80211_VERBOSE_DEBUG ++ printk(KERN_DEBUG "%s: setting MTU %d\n", dev->name, new_mtu); ++#endif /* CONFIG_D80211_VERBOSE_DEBUG */ ++ dev->mtu = new_mtu; ++ return 0; ++} ++ ++ ++static int ieee80211_change_mtu_apdev(struct net_device *dev, int new_mtu) ++{ ++ /* FIX: what would be proper limits for MTU? ++ * This interface uses 802.11 frames. */ ++ if (new_mtu < 256 || new_mtu > 2304) { ++ printk(KERN_WARNING "%s: invalid MTU %d\n", ++ dev->name, new_mtu); ++ return -EINVAL; ++ } ++ ++#ifdef CONFIG_D80211_VERBOSE_DEBUG ++ printk(KERN_DEBUG "%s: setting MTU %d\n", dev->name, new_mtu); ++#endif /* CONFIG_D80211_VERBOSE_DEBUG */ ++ dev->mtu = new_mtu; ++ return 0; ++} ++ ++ ++static void ieee80211_tx_timeout(struct net_device *dev) ++{ ++ struct ieee80211_local *local = dev->priv; ++ ++ printk(KERN_WARNING "%s: resetting interface.\n", dev->name); ++ ++ if (local->hw->reset(dev)) ++ printk(KERN_ERR "%s: failed to reset interface.\n", dev->name); ++ else ++ netif_wake_queue(dev); ++} ++ ++ ++static int ieee80211_set_mac_address(struct net_device *dev, void *addr) ++{ ++ struct ieee80211_local *local = dev->priv; ++ struct sockaddr *a = addr; ++ struct list_head *ptr; ++ int res; ++ ++ if (!local->hw->set_mac_address) ++ return -EOPNOTSUPP; ++ ++ res = local->hw->set_mac_address(dev, addr); ++ if (res) ++ return res; ++ ++ list_for_each(ptr, &local->sub_if_list) { ++ struct ieee80211_sub_if_data *sdata = ++ list_entry(ptr, struct ieee80211_sub_if_data, list); ++ memcpy(sdata->dev->dev_addr, a->sa_data, ETH_ALEN); ++ } ++ ++ return 0; ++} ++ ++ ++static struct net_device_stats *ieee80211_get_stats(struct net_device *dev) ++{ ++ struct ieee80211_sub_if_data *sdata; ++ sdata = IEEE80211_DEV_TO_SUB_IF(dev); ++ return &(sdata->stats); ++} ++ ++ ++static int ieee80211_open(struct net_device *dev) ++{ ++ struct ieee80211_sub_if_data *sdata; ++ struct ieee80211_local *local = dev->priv; ++ int res; ++ ++ sdata = IEEE80211_DEV_TO_SUB_IF(dev); ++ ++ if (local->open_count == 0) { ++ res = local->hw->open(sdata->master); ++ if (res) ++ return res; ++ ieee80211_init_scan(sdata->master); ++ } ++ local->open_count++; ++ ++ netif_start_queue(dev); ++ return 0; ++} ++ ++ ++static int ieee80211_stop(struct net_device *dev) ++{ ++ struct ieee80211_sub_if_data *sdata; ++ struct ieee80211_local *local = dev->priv; ++ int res; ++ ++ sdata = IEEE80211_DEV_TO_SUB_IF(dev); ++ ++ netif_stop_queue(dev); ++ ++ local->open_count--; ++ if (local->open_count == 0) { ++ ieee80211_stop_scan(sdata->master); ++ res = local->hw->stop(sdata->master); ++ if (res) ++ return res; ++ } ++ ++ return 0; ++} ++ ++ ++static int header_parse_80211(struct sk_buff *skb, unsigned char *haddr) ++{ ++ memcpy(haddr, skb->mac.raw + 10, ETH_ALEN); /* addr2 */ ++ return ETH_ALEN; ++} ++ ++ ++static struct net_device * ++ieee80211_get_wds_dev(struct ieee80211_local *local, u8 *addr) ++{ ++ struct list_head *ptr; ++ ++ list_for_each(ptr, &local->sub_if_list) { ++ struct ieee80211_sub_if_data *sdata = ++ list_entry(ptr, struct ieee80211_sub_if_data, list); ++ if (sdata->type == IEEE80211_SUB_IF_TYPE_WDS && ++ memcmp(addr, sdata->u.wds.remote_addr, ETH_ALEN) == 0) ++ return sdata->dev; ++ } ++ ++ return NULL; ++} ++ ++ ++static struct net_device * ieee80211_own_bssid(struct ieee80211_local *local, ++ u8 *addr) ++{ ++ int i; ++ struct net_device *dev = NULL; ++ ++ spin_lock_bh(&local->sub_if_lock); ++ for (i = 0; i < local->bss_dev_count; i++) { ++ if ((memcmp(local->bss_devs[i]->dev_addr, addr, ETH_ALEN) == 0) ++ ) { ++ dev = local->bss_devs[i]; ++ break; ++ } ++ } ++ spin_unlock_bh(&local->sub_if_lock); ++ ++ return dev; ++} ++ ++ ++static struct net_device * ieee80211_sta_bssid(struct ieee80211_local *local, ++ u8 *addr, u8 *a1, ++ int *sta_multicast) ++{ ++ struct list_head *ptr; ++ int multicast; ++ u8 *own_addr = local->mdev->dev_addr; ++ ++ multicast = a1[0] & 0x01; ++ ++ /* Try O(1) lookup for a common case of only one AP being used. */ ++ if (own_addr[0] == a1[0] && own_addr[1] == a1[1] && ++ own_addr[2] == a1[2]) { ++ int index = (((int) a1[3] << 16) | ((int) a1[4] << 8) | a1[5]) ++ - (((int) own_addr[3] << 16) | ++ ((int) own_addr[4] << 8) | own_addr[5]); ++ if (index >= 0 && index < local->conf.bss_count && ++ local->sta_devs[index]) { ++ struct net_device *dev = local->sta_devs[index]; ++ struct ieee80211_sub_if_data *sdata; ++ sdata = IEEE80211_DEV_TO_SUB_IF(dev); ++ if (memcmp(addr, sdata->u.sta.bssid, ETH_ALEN) == 0) { ++ *sta_multicast = multicast; ++ return dev; ++ } ++ } ++ } ++ ++ if (!multicast) ++ return NULL; ++ ++ /* Could not find station interface, resort to O(n) lookup. */ ++ list_for_each(ptr, &local->sub_if_list) { ++ struct ieee80211_sub_if_data *sdata = ++ list_entry(ptr, struct ieee80211_sub_if_data, list); ++ if (sdata->type != IEEE80211_SUB_IF_TYPE_STA) ++ continue; ++ if (!multicast && ++ memcmp(a1, sdata->dev->dev_addr, ETH_ALEN) != 0) ++ continue; ++ ++ if (memcmp(addr, sdata->u.sta.bssid, ETH_ALEN) == 0 || ++ (memcmp(addr, "\xff\xff\xff\xff\xff\xff", ETH_ALEN) == 0 && ++ local->conf.mode == IW_MODE_ADHOC)) { ++ *sta_multicast = multicast; ++ return sdata->dev; ++ } ++ } ++ ++ return NULL; ++} ++ ++ ++static int ieee80211_own_addr(struct net_device *dev, u8 *addr) ++{ ++ struct ieee80211_local *local = dev->priv; ++ u8 *own = dev->dev_addr; ++ int index; ++ ++ /* Optimization: assume that BSSID mask does not change for first ++ * three octets. */ ++ if (own[0] != addr[0] || own[1] != addr[1] || own[2] != addr[2]) ++ return 0; ++ ++ index = (((int) addr[3] << 16) | ((int) addr[4] << 8) | addr[5]) - ++ (((int) own[3] << 16) | ((int) own[4] << 8) | own[5]); ++ if (index >= 0 && index < local->conf.bss_count && ++ local->sta_devs[index]) ++ return 1; ++ ++ return 0; ++} ++ ++ ++static ieee80211_txrx_result ++ieee80211_rx_h_data(struct ieee80211_txrx_data *rx) ++{ ++ struct net_device *dev = rx->dev; ++ struct ieee80211_local *local = rx->local; ++ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) rx->skb->data; ++ u16 fc, hdrlen, ethertype; ++ u8 *payload; ++ u8 dst[ETH_ALEN]; ++ u8 src[ETH_ALEN]; ++ struct sk_buff *skb = rx->skb, *skb2; ++ struct ieee80211_sub_if_data *sdata; ++ ++ fc = rx->fc; ++ if (unlikely(WLAN_FC_GET_TYPE(fc) != WLAN_FC_TYPE_DATA)) ++ return TXRX_CONTINUE; ++ ++ if (unlikely(!WLAN_FC_DATA_PRESENT(fc))) ++ return TXRX_DROP; ++ ++ hdrlen = ieee80211_get_hdrlen(fc); ++ ++ /* convert IEEE 802.11 header + possible LLC headers into Ethernet ++ * header ++ * IEEE 802.11 address fields: ++ * ToDS FromDS Addr1 Addr2 Addr3 Addr4 ++ * 0 0 DA SA BSSID n/a ++ * 0 1 DA BSSID SA n/a ++ * 1 0 BSSID SA DA n/a ++ * 1 1 RA TA DA SA ++ */ ++ ++ switch (fc & (WLAN_FC_TODS | WLAN_FC_FROMDS)) { ++ case WLAN_FC_TODS: ++ /* BSSID SA DA */ ++ memcpy(dst, hdr->addr3, ETH_ALEN); ++ memcpy(src, hdr->addr2, ETH_ALEN); ++ ++ if (unlikely(local->conf.mode != IW_MODE_MASTER || ++ !ieee80211_own_bssid(local, hdr->addr1))) { ++ printk(KERN_DEBUG "%s: dropped ToDS frame (BSSID=" ++ MACSTR " SA=" MACSTR " DA=" MACSTR ")\n", ++ dev->name, MAC2STR(hdr->addr1), ++ MAC2STR(hdr->addr2), MAC2STR(hdr->addr3)); ++ return TXRX_DROP; ++ } ++ break; ++ case (WLAN_FC_TODS | WLAN_FC_FROMDS): ++ /* RA TA DA SA */ ++ memcpy(dst, hdr->addr3, ETH_ALEN); ++ memcpy(src, hdr->addr4, ETH_ALEN); ++ ++ dev = ieee80211_get_wds_dev(local, hdr->addr2); ++ if (!dev || memcmp(hdr->addr1, dev->dev_addr, ETH_ALEN) != 0) { ++ printk(KERN_DEBUG "%s: dropped FromDS&ToDS frame (RA=" ++ MACSTR " TA=" MACSTR " DA=" MACSTR " SA=" ++ MACSTR ")\n", ++ rx->dev->name, MAC2STR(hdr->addr1), ++ MAC2STR(hdr->addr2), MAC2STR(hdr->addr3), ++ MAC2STR(hdr->addr4)); ++ return TXRX_DROP; ++ } ++ break; ++ case WLAN_FC_FROMDS: ++ /* DA BSSID SA */ ++ memcpy(dst, hdr->addr1, ETH_ALEN); ++ memcpy(src, hdr->addr3, ETH_ALEN); ++ ++ sdata = IEEE80211_DEV_TO_SUB_IF(dev); ++ if (sdata->type != IEEE80211_SUB_IF_TYPE_STA || ++ memcmp(hdr->addr3, dev->dev_addr, ETH_ALEN) == 0 || ++ memcmp(hdr->addr2, sdata->u.sta.bssid, ETH_ALEN) != 0) { ++ return TXRX_DROP; ++ } ++ break; ++ case 0: ++ /* DA SA BSSID */ ++ memcpy(dst, hdr->addr1, ETH_ALEN); ++ memcpy(src, hdr->addr2, ETH_ALEN); ++ ++ if (local->conf.mode != IW_MODE_ADHOC || ++ memcmp(hdr->addr3, local->bssid, ETH_ALEN) != 0) { ++ if (net_ratelimit()) { ++ printk(KERN_DEBUG "%s: dropped IBSS frame (DA=" ++ MACSTR " SA=" MACSTR " BSSID=" MACSTR ++ ")\n", ++ dev->name, MAC2STR(hdr->addr1), ++ MAC2STR(hdr->addr2), ++ MAC2STR(hdr->addr3)); ++ } ++ return TXRX_DROP; ++ } ++ break; ++ } ++ ++ payload = skb->data + hdrlen; ++ ++ if (unlikely(skb->len - hdrlen < 8)) { ++ if (net_ratelimit()) { ++ printk(KERN_DEBUG "%s: RX too short data frame " ++ "payload\n", dev->name); ++ } ++ return TXRX_DROP; ++ } ++ ++ ethertype = (payload[6] << 8) | payload[7]; ++ ++ if (likely((memcmp(payload, rfc1042_header, 6) == 0 && ++ ethertype != ETH_P_AARP && ethertype != ETH_P_IPX) || ++ memcmp(payload, bridge_tunnel_header, 6) == 0)) { ++ /* remove RFC1042 or Bridge-Tunnel encapsulation and ++ * replace EtherType */ ++ skb_pull(skb, hdrlen + 6); ++ memcpy(skb_push(skb, ETH_ALEN), src, ETH_ALEN); ++ memcpy(skb_push(skb, ETH_ALEN), dst, ETH_ALEN); ++ } else { ++ struct ethhdr *ehdr; ++ unsigned short len; ++ skb_pull(skb, hdrlen); ++ len = htons(skb->len); ++ ehdr = (struct ethhdr *) skb_push(skb, sizeof(struct ethhdr)); ++ memcpy(ehdr->h_dest, dst, ETH_ALEN); ++ memcpy(ehdr->h_source, src, ETH_ALEN); ++ ehdr->h_proto = len; ++ } ++ ++ if (rx->sta && !rx->sta->assoc_ap && ++ !(rx->sta && (rx->sta->flags & WLAN_STA_WDS))) ++ skb->dev = rx->sta->dev; ++ else ++ skb->dev = dev; ++ ++ skb2 = NULL; ++ sdata = IEEE80211_DEV_TO_SUB_IF(dev); ++ ++ /* ++ * don't count the master since the low level code ++ * counts it already for us. ++ */ ++ if (skb->dev != sdata->master) { ++ sdata->stats.rx_packets++; ++ sdata->stats.rx_bytes += skb->len; ++ } ++ ++ if (local->bridge_packets && sdata->type != IEEE80211_SUB_IF_TYPE_WDS ++ && sdata->type != IEEE80211_SUB_IF_TYPE_STA) { ++ if (MULTICAST_ADDR(skb->data)) { ++ /* send multicast frames both to higher layers in ++ * local net stack and back to the wireless media */ ++ skb2 = skb_copy(skb, GFP_ATOMIC); ++ if (skb2 == NULL) ++ printk(KERN_DEBUG "%s: failed to clone " ++ "multicast frame\n", dev->name); ++ } else { ++ struct sta_info *dsta; ++ dsta = sta_info_get(local, skb->data); ++ if (dsta && dsta->dev == NULL) { ++ printk(KERN_DEBUG "Station with null dev " ++ "structure!\n"); ++ } else if (dsta && dsta->dev == dev) { ++ /* Destination station is associated to this ++ * AP, so send the frame directly to it and ++ * do not pass the frame to local net stack. ++ */ ++ skb2 = skb; ++ skb = NULL; ++ } ++ if (dsta) ++ sta_info_release(local, dsta); ++ } ++ } ++ ++ if (skb) { ++ /* deliver to local stack */ ++ skb->protocol = eth_type_trans(skb, dev); ++ memset(skb->cb, 0, sizeof(skb->cb)); ++ netif_rx(skb); ++ } ++ ++ if (skb2) { ++ /* send to wireless media */ ++ skb2->protocol = __constant_htons(ETH_P_802_3); ++ skb2->mac.raw = skb2->nh.raw = skb2->data; ++ dev_queue_xmit(skb2); ++ } ++ ++ return TXRX_QUEUED; ++} ++ ++ ++static struct ieee80211_rate * ++ieee80211_get_rate(struct ieee80211_local *local, int phymode, int hw_rate) ++{ ++ int m, r; ++ ++ for (m = 0; m < local->hw->num_modes; m++) { ++ struct ieee80211_hw_modes *mode = &local->hw->modes[m]; ++ if (mode->mode != phymode) ++ continue; ++ for (r = 0; r < mode->num_rates; r++) { ++ struct ieee80211_rate *rate = &mode->rates[r]; ++ if (rate->val == hw_rate || ++ (rate->flags & IEEE80211_RATE_PREAMBLE2 && ++ rate->val2 == hw_rate)) ++ return rate; ++ } ++ } ++ ++ return NULL; ++} ++ ++ ++void ++ieee80211_rx_mgmt(struct net_device *dev, struct sk_buff *skb, ++ struct ieee80211_rx_status *status, u32 msg_type) ++{ ++ struct ieee80211_local *local = dev->priv; ++ struct ieee80211_frame_info *fi; ++ size_t hlen; ++ struct ieee80211_sub_if_data *sdata; ++ ++ dev = local->apdev; ++ skb->dev = dev; ++ ++ sdata = IEEE80211_DEV_TO_SUB_IF(dev); ++ ++ if (skb_headroom(skb) < sizeof(struct ieee80211_frame_info)) { ++ I802_DEBUG_INC(local->rx_expand_skb_head); ++ if (pskb_expand_head(skb, sizeof(struct ieee80211_frame_info), ++ 0, GFP_ATOMIC)) { ++ dev_kfree_skb(skb); ++ return; ++ } ++ } ++ ++ hlen = sizeof(struct ieee80211_frame_info); ++ if (msg_type == ieee80211_msg_monitor) ++ hlen -= sizeof(fi->msg_type); ++ ++ fi = (struct ieee80211_frame_info *) skb_push(skb, hlen); ++ memset(fi, 0, hlen); ++ if (msg_type != ieee80211_msg_monitor) ++ fi->msg_type = htonl(msg_type); ++ fi->version = htonl(IEEE80211_FI_VERSION); ++ fi->length = htonl(hlen); ++ if (status) { ++ struct timespec ts; ++ struct ieee80211_rate *rate; ++ ++ jiffies_to_timespec(status->hosttime, &ts); ++ fi->hosttime = cpu_to_be64(ts.tv_sec * 1000000 + ++ ts.tv_nsec / 1000); ++ fi->mactime = cpu_to_be64(status->mactime); ++ switch (status->phymode) { ++ case MODE_IEEE80211A: ++ fi->phytype = htonl(ieee80211_phytype_ofdm_dot11_a); ++ break; ++ case MODE_IEEE80211B: ++ fi->phytype = htonl(ieee80211_phytype_dsss_dot11_b); ++ break; ++ case MODE_IEEE80211G: ++ fi->phytype = htonl(ieee80211_phytype_pbcc_dot11_g); ++ break; ++ case MODE_ATHEROS_TURBO: ++ fi->phytype = ++ htonl(ieee80211_phytype_dsss_dot11_turbo); ++ break; ++ default: ++ fi->phytype = 0xAAAAAAAA; ++ break; ++ } ++ fi->channel = htonl(status->channel); ++ rate = ieee80211_get_rate(local, status->phymode, ++ status->rate); ++ if (rate) { ++ fi->datarate = htonl(rate->rate); ++ if (rate->flags & IEEE80211_RATE_PREAMBLE2) { ++ if (status->rate == rate->val) ++ fi->preamble = htonl(2); /* long */ ++ else if (status->rate == rate->val2) ++ fi->preamble = htonl(1); /* short */ ++ } else ++ fi->preamble = htonl(0); ++ } else { ++ fi->datarate = htonl(0); ++ fi->preamble = htonl(0); ++ } ++ ++ fi->antenna = htonl(status->antenna); ++ fi->priority = 0xffffffff; /* no clue */ ++ fi->ssi_type = htonl(ieee80211_ssi_raw); ++ fi->ssi_signal = htonl(status->ssi); ++ fi->ssi_noise = 0x00000000; ++ fi->encoding = 0; ++ } else { ++ fi->ssi_type = htonl(ieee80211_ssi_none); ++ } ++ ++ sdata->stats.rx_packets++; ++ sdata->stats.rx_bytes += skb->len; ++ ++ skb->mac.raw = skb->data; ++ skb->ip_summed = CHECKSUM_UNNECESSARY; ++ skb->pkt_type = PACKET_OTHERHOST; ++ skb->protocol = __constant_htons(ETH_P_802_2); ++ memset(skb->cb, 0, sizeof(skb->cb)); ++ netif_rx(skb); ++} ++ ++ ++int ieee80211_radar_status(struct net_device *dev, int channel, int radar, ++ int radar_type) ++{ ++ struct sk_buff *skb; ++ struct ieee80211_radar_info *msg; ++ ++ skb = dev_alloc_skb(sizeof(struct ieee80211_frame_info) + ++ sizeof(struct ieee80211_radar_info)); ++ ++ if (skb == NULL) ++ return -ENOMEM; ++ skb_reserve(skb, sizeof(struct ieee80211_frame_info)); ++ ++ msg = (struct ieee80211_radar_info *) ++ skb_put(skb, sizeof(struct ieee80211_radar_info)); ++ msg->channel = channel; ++ msg->radar = radar; ++ msg->radar_type = radar_type; ++ ++ ieee80211_rx_mgmt(dev, skb, 0, ieee80211_msg_radar); ++ return 0; ++} ++ ++ ++int ieee80211_set_aid_for_sta(struct net_device *dev, u8 *peer_address, ++ u16 aid) ++{ ++ struct sk_buff *skb; ++ struct ieee80211_msg_set_aid_for_sta *msg; ++ ++ skb = dev_alloc_skb(sizeof(struct ieee80211_frame_info) + ++ sizeof(struct ieee80211_msg_set_aid_for_sta)); ++ ++ if (skb == NULL) ++ return -ENOMEM; ++ skb_reserve(skb, sizeof(struct ieee80211_frame_info)); ++ ++ msg = (struct ieee80211_msg_set_aid_for_sta *) ++ skb_put(skb, sizeof(struct ieee80211_msg_set_aid_for_sta)); ++ memcpy(msg->sta_address, peer_address, ETH_ALEN); ++ msg->aid = aid; ++ ++ ieee80211_rx_mgmt(dev, skb, 0, ieee80211_msg_set_aid_for_sta); ++ return 0; ++} ++ ++ ++static void ap_sta_ps_start(struct net_device *dev, struct sta_info *sta) ++{ ++ struct ieee80211_sub_if_data *sdata; ++ sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev); ++ ++ if (sdata->bss) ++ atomic_inc(&sdata->bss->num_sta_ps); ++ sta->flags |= WLAN_STA_PS; ++ sta->pspoll = 0; ++#ifdef IEEE80211_VERBOSE_DEBUG_PS ++ printk(KERN_DEBUG "%s: STA " MACSTR " aid %d enters power " ++ "save mode\n", dev->name, MAC2STR(sta->addr), sta->aid); ++#endif /* IEEE80211_VERBOSE_DEBUG_PS */ ++} ++ ++ ++static int ap_sta_ps_end(struct net_device *dev, struct sta_info *sta) ++{ ++ struct ieee80211_local *local = dev->priv; ++ struct sk_buff *skb; ++ int sent = 0; ++ struct ieee80211_sub_if_data *sdata; ++ struct ieee80211_tx_packet_data *pkt_data; ++ ++ sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev); ++ if (sdata->bss) ++ atomic_dec(&sdata->bss->num_sta_ps); ++ sta->flags &= ~(WLAN_STA_PS | WLAN_STA_TIM); ++ sta->pspoll = 0; ++ if (!skb_queue_empty(&sta->ps_tx_buf) && local->hw->set_tim) ++ local->hw->set_tim(dev, sta->aid, 0); ++#ifdef IEEE80211_VERBOSE_DEBUG_PS ++ printk(KERN_DEBUG "%s: STA " MACSTR " aid %d exits power " ++ "save mode\n", dev->name, MAC2STR(sta->addr), sta->aid); ++#endif /* IEEE80211_VERBOSE_DEBUG_PS */ ++ /* Send all buffered frames to the station */ ++ while ((skb = skb_dequeue(&sta->tx_filtered)) != NULL) { ++ pkt_data = (struct ieee80211_tx_packet_data *) skb->cb; ++ sent++; ++ pkt_data->requeue = 1; ++ dev_queue_xmit(skb); ++ } ++ while ((skb = skb_dequeue(&sta->ps_tx_buf)) != NULL) { ++ pkt_data = (struct ieee80211_tx_packet_data *) skb->cb; ++ local->total_ps_buffered--; ++ sent++; ++#ifdef IEEE80211_VERBOSE_DEBUG_PS ++ printk(KERN_DEBUG "%s: STA " MACSTR " aid %d send PS frame " ++ "since STA not sleeping anymore\n", dev->name, ++ MAC2STR(sta->addr), sta->aid); ++#endif /* IEEE80211_VERBOSE_DEBUG_PS */ ++ pkt_data->requeue = 1; ++ dev_queue_xmit(skb); ++ } ++ ++ return sent; ++} ++ ++ ++static ieee80211_txrx_result ++ieee80211_rx_h_ieee80211_rx_h_ps_poll(struct ieee80211_txrx_data *rx) ++{ ++ struct sk_buff *skb; ++ int no_pending_pkts; ++ ++ if (likely(!rx->sta || WLAN_FC_GET_TYPE(rx->fc) != WLAN_FC_TYPE_CTRL || ++ WLAN_FC_GET_STYPE(rx->fc) != WLAN_FC_STYPE_PSPOLL)) ++ return TXRX_CONTINUE; ++ ++ skb = skb_dequeue(&rx->sta->tx_filtered); ++ if (skb == NULL) { ++ skb = skb_dequeue(&rx->sta->ps_tx_buf); ++ if (skb) ++ rx->local->total_ps_buffered--; ++ } ++ no_pending_pkts = skb_queue_empty(&rx->sta->tx_filtered) && ++ skb_queue_empty(&rx->sta->ps_tx_buf); ++ ++ if (skb) { ++ struct ieee80211_hdr *hdr = ++ (struct ieee80211_hdr *) skb->data; ++ ++ /* tell TX path to send one frame even though the STA may ++ * still remain is PS mode after this frame exchange */ ++ rx->sta->pspoll = 1; ++ ++#ifdef IEEE80211_VERBOSE_DEBUG_PS ++ printk(KERN_DEBUG "STA " MACSTR " aid %d: PS Poll (entries " ++ "after %d)\n", ++ MAC2STR(rx->sta->addr), rx->sta->aid, ++ skb_queue_len(&rx->sta->ps_tx_buf)); ++#endif /* IEEE80211_VERBOSE_DEBUG_PS */ ++ ++ /* Use MoreData flag to indicate whether there are more ++ * buffered frames for this STA */ ++ if (no_pending_pkts) { ++ hdr->frame_control &= cpu_to_le16(~WLAN_FC_MOREDATA); ++ rx->sta->flags &= ~WLAN_STA_TIM; ++ } else ++ hdr->frame_control |= cpu_to_le16(WLAN_FC_MOREDATA); ++ ++ dev_queue_xmit(skb); ++ ++ if (no_pending_pkts && rx->local->hw->set_tim) ++ rx->local->hw->set_tim(rx->dev, rx->sta->aid, 0); ++#ifdef IEEE80211_VERBOSE_DEBUG_PS ++ } else if (!rx->u.rx.sent_ps_buffered) { ++ printk(KERN_DEBUG "%s: STA " MACSTR " sent PS Poll even " ++ "though there is no buffered frames for it\n", ++ rx->dev->name, MAC2STR(rx->sta->addr)); ++#endif /* IEEE80211_VERBOSE_DEBUG_PS */ ++ ++ } ++ ++ /* Free PS Poll skb here instead of returning TXRX_DROP that would ++ * count as an dropped frame. */ ++ dev_kfree_skb(rx->skb); ++ ++ return TXRX_QUEUED; ++} ++ ++ ++static inline struct ieee80211_fragment_entry * ++ieee80211_reassemble_add(struct ieee80211_local *local, ++ unsigned int frag, unsigned int seq, int rx_queue, ++ struct sk_buff **skb) ++{ ++ struct ieee80211_fragment_entry *entry; ++ int idx; ++ ++ idx = local->fragment_next; ++ entry = &local->fragments[local->fragment_next++]; ++ if (local->fragment_next >= IEEE80211_FRAGMENT_MAX) ++ local->fragment_next = 0; ++ ++ if (entry->skb) { ++#ifdef CONFIG_D80211_DEBUG ++ struct ieee80211_hdr *hdr = ++ (struct ieee80211_hdr *) entry->skb->data; ++ printk(KERN_DEBUG "%s: RX reassembly removed oldest " ++ "fragment entry (idx=%d age=%lu seq=%d last_frag=%d " ++ "addr1=" MACSTR " addr2=" MACSTR "\n", ++ local->mdev->name, idx, ++ jiffies - entry->first_frag_time, entry->seq, ++ entry->last_frag, MAC2STR(hdr->addr1), ++ MAC2STR(hdr->addr2)); ++#endif /* CONFIG_D80211_DEBUG */ ++ dev_kfree_skb(entry->skb); ++ } ++ ++ entry->skb = *skb; ++ *skb = NULL; ++ entry->first_frag_time = jiffies; ++ entry->seq = seq; ++ entry->rx_queue = rx_queue; ++ entry->last_frag = frag; ++ entry->ccmp = 0; ++ ++ return entry; ++} ++ ++ ++static inline struct ieee80211_fragment_entry * ++ieee80211_reassemble_find(struct ieee80211_local *local, ++ u16 fc, unsigned int frag, unsigned int seq, ++ int rx_queue, struct ieee80211_hdr *hdr) ++{ ++ struct ieee80211_fragment_entry *entry; ++ int i, idx; ++ ++ idx = local->fragment_next; ++ for (i = 0; i < IEEE80211_FRAGMENT_MAX; i++) { ++ struct ieee80211_hdr *f_hdr; ++ u16 f_fc; ++ ++ idx--; ++ if (idx < 0) ++ idx = IEEE80211_FRAGMENT_MAX - 1; ++ ++ entry = &local->fragments[idx]; ++ if (!entry->skb || entry->seq != seq || ++ entry->rx_queue != rx_queue || ++ entry->last_frag + 1 != frag) ++ continue; ++ ++ f_hdr = (struct ieee80211_hdr *) entry->skb->data; ++ f_fc = le16_to_cpu(f_hdr->frame_control); ++ ++ if (WLAN_FC_GET_TYPE(fc) != WLAN_FC_GET_TYPE(f_fc) || ++ memcmp(hdr->addr1, f_hdr->addr1, ETH_ALEN) != 0 || ++ memcmp(hdr->addr2, f_hdr->addr2, ETH_ALEN) != 0) ++ continue; ++ ++ if (entry->first_frag_time + 2 * HZ < jiffies) { ++ dev_kfree_skb(entry->skb); ++ entry->skb = NULL; ++ continue; ++ } ++ return entry; ++ } ++ ++ return NULL; ++} ++ ++ ++static ieee80211_txrx_result ++ieee80211_rx_h_defragment(struct ieee80211_txrx_data *rx) ++{ ++ struct ieee80211_hdr *hdr; ++ u16 sc; ++ unsigned int frag, seq; ++ struct ieee80211_fragment_entry *entry; ++ ++ hdr = (struct ieee80211_hdr *) rx->skb->data; ++ sc = le16_to_cpu(hdr->seq_ctrl); ++ frag = WLAN_GET_SEQ_FRAG(sc); ++ ++ if (likely((!(rx->fc & WLAN_FC_MOREFRAG) && frag == 0) || ++ (rx->skb)->len < 24 || MULTICAST_ADDR(hdr->addr1))) { ++ /* not fragmented */ ++ goto out; ++ } ++ I802_DEBUG_INC(rx->local->rx_handlers_fragments); ++ ++ seq = WLAN_GET_SEQ_SEQ(sc); ++ ++ if (frag == 0) { ++ /* This is the first fragment of a new frame. */ ++ entry = ieee80211_reassemble_add(rx->local, frag, seq, ++ rx->u.rx.queue, &(rx->skb)); ++ if (rx->key && rx->key->alg == ALG_CCMP && ++ (rx->fc & WLAN_FC_ISWEP)) { ++ /* Store CCMP PN so that we can verify that the next ++ * fragment has a sequential PN value. */ ++ entry->ccmp = 1; ++ memcpy(entry->last_pn, ++ rx->key->u.ccmp.rx_pn[rx->u.rx.queue], ++ CCMP_PN_LEN); ++ } ++ return TXRX_QUEUED; ++ } ++ ++ /* This is a fragment for a frame that should already be pending in ++ * fragment cache. Add this fragment to the end of the pending entry. ++ */ ++ entry = ieee80211_reassemble_find(rx->local, rx->fc, frag, seq, ++ rx->u.rx.queue, hdr); ++ if (!entry) { ++ I802_DEBUG_INC(rx->local->rx_handlers_drop_defrag); ++ return TXRX_DROP; ++ } ++ ++ /* Verify that MPDUs within one MSDU have sequential PN values. ++ * (IEEE 802.11i, 8.3.3.4.5) */ ++ if (entry->ccmp) { ++ int i; ++ u8 pn[CCMP_PN_LEN], *rpn; ++ if (rx->key == NULL || rx->key->alg != ALG_CCMP) ++ return TXRX_DROP; ++ memcpy(pn, entry->last_pn, CCMP_PN_LEN); ++ for (i = CCMP_PN_LEN - 1; i >= 0; i--) { ++ pn[i]++; ++ if (pn[i]) ++ break; ++ } ++ rpn = rx->key->u.ccmp.rx_pn[rx->u.rx.queue]; ++ if (memcmp(pn, rpn, CCMP_PN_LEN) != 0) { ++ printk(KERN_DEBUG "%s: defrag: CCMP PN not sequential" ++ " A2=" MACSTR " PN=%02x%02x%02x%02x%02x%02x " ++ "(expected %02x%02x%02x%02x%02x%02x)\n", ++ rx->dev->name, MAC2STR(hdr->addr2), ++ rpn[0], rpn[1], rpn[2], rpn[3], rpn[4], rpn[5], ++ pn[0], pn[1], pn[2], pn[3], pn[4], pn[5]); ++ return TXRX_DROP; ++ } ++ memcpy(entry->last_pn, pn, CCMP_PN_LEN); ++ } ++ ++ /* TODO: could gather list of skb's and reallocate data buffer only ++ * after finding out the total length of the frame */ ++ skb_pull(rx->skb, ieee80211_get_hdrlen(rx->fc)); ++ if (skb_tailroom(entry->skb) < rx->skb->len) { ++ I802_DEBUG_INC(rx->local->rx_expand_skb_head2); ++ if (unlikely(pskb_expand_head(entry->skb, 0, rx->skb->len, ++ GFP_ATOMIC))) { ++ I802_DEBUG_INC(rx->local->rx_handlers_drop_defrag); ++ return TXRX_DROP; ++ } ++ } ++ memcpy(skb_put(entry->skb, rx->skb->len), rx->skb->data, rx->skb->len); ++ entry->last_frag = frag; ++ dev_kfree_skb(rx->skb); ++ ++ if (rx->fc & WLAN_FC_MOREFRAG) { ++ rx->skb = NULL; ++ return TXRX_QUEUED; ++ } ++ ++ /* Complete frame has been reassembled - process it now */ ++ rx->skb = entry->skb; ++ rx->fragmented = 1; ++ entry->skb = NULL; ++ ++ out: ++ if (rx->sta) ++ rx->sta->rx_packets++; ++ if (MULTICAST_ADDR(hdr->addr1)) ++ rx->local->dot11MulticastReceivedFrameCount++; ++#ifdef IEEE80211_LEDS ++ else ++ ieee80211_rx_led(2, rx->dev); ++#endif /* IEEE80211_LEDS */ ++ return TXRX_CONTINUE; ++} ++ ++ ++static ieee80211_txrx_result ++ieee80211_rx_h_monitor(struct ieee80211_txrx_data *rx) ++{ ++ if (rx->local->conf.mode == IW_MODE_MONITOR) { ++ ieee80211_rx_mgmt(rx->dev, rx->skb, rx->u.rx.status, ++ ieee80211_msg_monitor); ++ return TXRX_QUEUED; ++ } ++ ++ return TXRX_CONTINUE; ++} ++ ++ ++static ieee80211_txrx_result ++ieee80211_rx_h_check(struct ieee80211_txrx_data *rx) ++{ ++ struct ieee80211_hdr *hdr; ++ int always_sta_key; ++ hdr = (struct ieee80211_hdr *) rx->skb->data; ++ ++ /* Drop duplicate 802.11 retransmissions (IEEE 802.11 Chap. 9.2.9) */ ++ if (rx->sta && !MULTICAST_ADDR(hdr->addr1)) { ++ if (unlikely(rx->fc & WLAN_FC_RETRY && ++ rx->sta->last_seq_ctrl[rx->u.rx.queue] == ++ hdr->seq_ctrl)) { ++ rx->local->dot11FrameDuplicateCount++; ++ rx->sta->num_duplicates++; ++ return TXRX_DROP; ++ } else ++ rx->sta->last_seq_ctrl[rx->u.rx.queue] = hdr->seq_ctrl; ++ } ++ ++ if (rx->local->hw->rx_includes_fcs && rx->skb->len > FCS_LEN) ++ skb_trim(rx->skb, rx->skb->len - FCS_LEN); ++ ++ if (unlikely(rx->skb->len < 16)) { ++ I802_DEBUG_INC(rx->local->rx_handlers_drop_short); ++ return TXRX_DROP; ++ } ++ ++ /* Filter out foreign unicast packets when in promiscuous mode. ++ * FIX: Filter out multicast to foreign BSSID. */ ++ if (rx->local->conf.mode == IW_MODE_INFRA && ++ !MULTICAST_ADDR(hdr->addr1) && ++ !ieee80211_own_addr(rx->dev, hdr->addr1)) ++ return TXRX_DROP; ++ ++ /* Drop disallowed frame classes based on STA auth/assoc state; ++ * IEEE 802.11, Chap 5.5. ++ * ++ * 80211.o does filtering only based on association state, i.e., it ++ * drops Class 3 frames from not associated stations. hostapd sends ++ * deauth/disassoc frames when needed. In addition, hostapd is ++ * responsible for filtering on both auth and assoc states. ++ */ ++ if (unlikely((WLAN_FC_GET_TYPE(rx->fc) == WLAN_FC_TYPE_DATA || ++ (WLAN_FC_GET_TYPE(rx->fc) == WLAN_FC_TYPE_CTRL && ++ WLAN_FC_GET_STYPE(rx->fc) == WLAN_FC_STYPE_PSPOLL)) && ++ rx->local->conf.mode != IW_MODE_ADHOC && ++ (!rx->sta || !(rx->sta->flags & WLAN_STA_ASSOC)))) { ++ if (!(rx->fc & WLAN_FC_FROMDS) && !(rx->fc & WLAN_FC_TODS)) { ++ /* Drop IBSS frames silently. */ ++ return TXRX_DROP; ++ } ++ ++ ieee80211_rx_mgmt(rx->dev, rx->skb, rx->u.rx.status, ++ ieee80211_msg_sta_not_assoc); ++ return TXRX_QUEUED; ++ } ++ ++ if (rx->local->conf.mode == IW_MODE_INFRA) ++ always_sta_key = 0; ++ else ++ always_sta_key = 1; ++ ++ if (rx->sta && rx->sta->key && always_sta_key) { ++ rx->key = rx->sta->key; ++ } else { ++ if (!rx->sdata) { ++ printk(KERN_DEBUG "%s: sdata was null in packet!!\n", ++ rx->dev->name); ++ printk(KERN_DEBUG "%s: Addr1: " MACSTR "\n", ++ rx->dev->name, MAC2STR(hdr->addr1)); ++ printk(KERN_DEBUG "%s: Addr2: " MACSTR "\n", ++ rx->dev->name, MAC2STR(hdr->addr2)); ++ printk(KERN_DEBUG "%s: Addr3: " MACSTR "\n", ++ rx->dev->name, MAC2STR(hdr->addr3)); ++ return TXRX_DROP; ++ } ++ if (rx->sta && rx->sta->key) ++ rx->key = rx->sta->key; ++ else ++ rx->key = rx->sdata->default_key; ++ ++ if (rx->local->hw->wep_include_iv && ++ rx->fc & WLAN_FC_ISWEP) { ++ int keyidx = ieee80211_wep_get_keyidx(rx->skb); ++ ++ if (keyidx >= 0 && keyidx < NUM_DEFAULT_KEYS && ++ (rx->sta == NULL || rx->sta->key == NULL || ++ keyidx > 0)) { ++ rx->key = rx->sdata->keys[keyidx]; ++ } ++ if (!rx->key) { ++ printk(KERN_DEBUG "%s: RX WEP frame with " ++ "unknown keyidx %d (A1=" MACSTR " A2=" ++ MACSTR " A3=" MACSTR ")\n", ++ rx->dev->name, keyidx, ++ MAC2STR(hdr->addr1), ++ MAC2STR(hdr->addr2), ++ MAC2STR(hdr->addr3)); ++ ieee80211_rx_mgmt( ++ rx->dev, rx->skb, rx->u.rx.status, ++ ieee80211_msg_wep_frame_unknown_key); ++ return TXRX_QUEUED; ++ } ++ } ++ } ++ ++ if (rx->fc & WLAN_FC_ISWEP && rx->key) { ++ rx->key->tx_rx_count++; ++ if (unlikely(rx->local->key_tx_rx_threshold && ++ rx->key->tx_rx_count > ++ rx->local->key_tx_rx_threshold)) { ++ ieee80211_key_threshold_notify(rx->dev, rx->key, ++ rx->sta); ++ } ++ } ++ ++ return TXRX_CONTINUE; ++} ++ ++ ++static ieee80211_txrx_result ++ieee80211_rx_h_sta_process(struct ieee80211_txrx_data *rx) ++{ ++ struct sta_info *sta = rx->sta; ++ struct net_device *dev = rx->dev; ++ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) rx->skb->data; ++ ++ if (!sta) ++ return TXRX_CONTINUE; ++ ++ /* Update last_rx only for IBSS packets which are for the current ++ * BSSID to avoid keeping the current IBSS network alive in cases where ++ * other STAs are using different BSSID. */ ++ if (rx->local->conf.mode == IW_MODE_ADHOC) { ++ u8 *bssid = ieee80211_get_bssid(hdr, rx->skb->len); ++ if (memcmp(bssid, rx->local->bssid, ETH_ALEN) == 0) ++ sta->last_rx = jiffies; ++ } else ++ if (!MULTICAST_ADDR(hdr->addr1) || ++ rx->local->conf.mode == IW_MODE_INFRA) { ++ /* Update last_rx only for unicast frames in order to prevent ++ * the Probe Request frames (the only broadcast frames from a ++ * STA in infrastructure mode) from keeping a connection alive. ++ */ ++ sta->last_rx = jiffies; ++ } ++ sta->rx_fragments++; ++ sta->rx_bytes += rx->skb->len; ++ sta->last_rssi = rx->u.rx.status->ssi; ++ ++ if (!(rx->fc & WLAN_FC_MOREFRAG)) { ++ /* Change STA power saving mode only in the end of a frame ++ * exchange sequence */ ++ if ((sta->flags & WLAN_STA_PS) && !(rx->fc & WLAN_FC_PWRMGT)) ++ rx->u.rx.sent_ps_buffered += ap_sta_ps_end(dev, sta); ++ else if (!(sta->flags & WLAN_STA_PS) && ++ (rx->fc & WLAN_FC_PWRMGT)) ++ ap_sta_ps_start(dev, sta); ++ } ++ ++ /* Drop data::nullfunc frames silently, since they are used only to ++ * control station power saving mode. */ ++ if (WLAN_FC_GET_TYPE(rx->fc) == WLAN_FC_TYPE_DATA && ++ WLAN_FC_GET_STYPE(rx->fc) == WLAN_FC_STYPE_NULLFUNC) { ++ I802_DEBUG_INC(rx->local->rx_handlers_drop_nullfunc); ++ /* Update counter and free packet here to avoid counting this ++ * as a dropped packed. */ ++ sta->rx_packets++; ++ dev_kfree_skb(rx->skb); ++ return TXRX_QUEUED; ++ } ++ ++ return TXRX_CONTINUE; ++} ++ ++ ++static ieee80211_txrx_result ++ieee80211_rx_h_wep_weak_iv_detection(struct ieee80211_txrx_data *rx) ++{ ++ if (!rx->sta || !(rx->fc & WLAN_FC_ISWEP) || ++ WLAN_FC_GET_TYPE(rx->fc) != WLAN_FC_TYPE_DATA || !rx->key || ++ rx->key->alg != ALG_WEP) ++ return TXRX_CONTINUE; ++ ++ /* Check for weak IVs, if hwaccel did not remove IV from the frame */ ++ if (rx->local->hw->wep_include_iv || ++ rx->key->force_sw_encrypt || rx->local->conf.sw_decrypt) { ++ u8 *iv = ieee80211_wep_is_weak_iv(rx->skb, rx->key); ++ if (iv) { ++ rx->sta->wep_weak_iv_count++; ++ } ++ } ++ ++ return TXRX_CONTINUE; ++} ++ ++ ++static ieee80211_txrx_result ++ieee80211_rx_h_wep_decrypt(struct ieee80211_txrx_data *rx) ++{ ++ /* If the device handles decryption totally, skip this test */ ++ if (rx->local->hw->device_hides_wep) ++ return TXRX_CONTINUE; ++ ++ if ((rx->key && rx->key->alg != ALG_WEP) || ++ !(rx->fc & WLAN_FC_ISWEP) || ++ (WLAN_FC_GET_TYPE(rx->fc) != WLAN_FC_TYPE_DATA && ++ (WLAN_FC_GET_TYPE(rx->fc) != WLAN_FC_TYPE_MGMT || ++ WLAN_FC_GET_STYPE(rx->fc) != WLAN_FC_STYPE_AUTH))) ++ return TXRX_CONTINUE; ++ ++ if (!rx->key) { ++ printk(KERN_DEBUG "%s: RX WEP frame, but no key set\n", ++ rx->dev->name); ++ return TXRX_DROP; ++ } ++ ++ if (!(rx->u.rx.status->flag & RX_FLAG_DECRYPTED) || ++ rx->key->force_sw_encrypt || rx->local->conf.sw_decrypt) { ++ if (ieee80211_wep_decrypt(rx->local, rx->skb, rx->key)) { ++ printk(KERN_DEBUG "%s: RX WEP frame, decrypt " ++ "failed\n", rx->dev->name); ++ return TXRX_DROP; ++ } ++ } else if (rx->local->hw->wep_include_iv) { ++ ieee80211_wep_remove_iv(rx->local, rx->skb, rx->key); ++ /* remove ICV */ ++ skb_trim(rx->skb, rx->skb->len - 4); ++ } ++ ++ return TXRX_CONTINUE; ++} ++ ++ ++static ieee80211_txrx_result ++ieee80211_rx_h_802_1x_pae(struct ieee80211_txrx_data *rx) ++{ ++ if (rx->sdata->eapol && ieee80211_is_eapol(rx->skb) && ++ rx->local->conf.mode != IW_MODE_INFRA) { ++ /* Pass both encrypted and unencrypted EAPOL frames to user ++ * space for processing. */ ++ ieee80211_rx_mgmt(rx->dev, rx->skb, rx->u.rx.status, ++ ieee80211_msg_normal); ++ return TXRX_QUEUED; ++ } ++ ++ if (unlikely(rx->sdata->ieee802_1x && ++ WLAN_FC_GET_TYPE(rx->fc) == WLAN_FC_TYPE_DATA && ++ WLAN_FC_GET_STYPE(rx->fc) != WLAN_FC_STYPE_NULLFUNC && ++ (!rx->sta || !(rx->sta->flags & WLAN_STA_AUTHORIZED)) && ++ !ieee80211_is_eapol(rx->skb))) { ++#ifdef CONFIG_D80211_DEBUG ++ struct ieee80211_hdr *hdr = ++ (struct ieee80211_hdr *) rx->skb->data; ++ printk(KERN_DEBUG "%s: dropped frame from " MACSTR ++ " (unauthorized port)\n", rx->dev->name, ++ MAC2STR(hdr->addr2)); ++#endif /* CONFIG_D80211_DEBUG */ ++ return TXRX_DROP; ++ } ++ ++ return TXRX_CONTINUE; ++} ++ ++ ++static ieee80211_txrx_result ++ieee80211_rx_h_drop_unencrypted(struct ieee80211_txrx_data *rx) ++{ ++ /* If the device handles decryption totally, skip this test */ ++ if (rx->local->hw->device_hides_wep) ++ return TXRX_CONTINUE; ++ ++ /* Drop unencrypted frames if key is set. */ ++ if (unlikely(!(rx->fc & WLAN_FC_ISWEP) && ++ WLAN_FC_GET_TYPE(rx->fc) == WLAN_FC_TYPE_DATA && ++ WLAN_FC_GET_STYPE(rx->fc) != WLAN_FC_STYPE_NULLFUNC && ++ (rx->key || rx->sdata->drop_unencrypted) && ++ (rx->sdata->eapol == 0 || ++ !ieee80211_is_eapol(rx->skb)))) { ++ printk(KERN_DEBUG "%s: RX non-WEP frame, but expected " ++ "encryption\n", rx->dev->name); ++ return TXRX_DROP; ++ } ++ return TXRX_CONTINUE; ++} ++ ++ ++static ieee80211_txrx_result ++ieee80211_rx_h_mgmt(struct ieee80211_txrx_data *rx) ++{ ++ struct ieee80211_sub_if_data *sdata; ++ sdata = IEEE80211_DEV_TO_SUB_IF(rx->dev); ++ if (sdata->type == IEEE80211_SUB_IF_TYPE_STA) { ++ ieee80211_sta_rx_mgmt(rx->dev, rx->skb, rx->u.rx.status); ++ } else { ++ /* Management frames are sent to hostapd for processing */ ++ ieee80211_rx_mgmt(rx->dev, rx->skb, rx->u.rx.status, ++ ieee80211_msg_normal); ++ } ++ return TXRX_QUEUED; ++} ++ ++ ++static ieee80211_txrx_result ++ieee80211_rx_h_passive_scan(struct ieee80211_txrx_data *rx) ++{ ++ struct ieee80211_local *local = rx->local; ++ struct sk_buff *skb = rx->skb; ++ ++ if (unlikely(local->sta_scanning != 0)) { ++ ieee80211_sta_rx_scan(rx->dev, skb, rx->u.rx.status); ++ return TXRX_QUEUED; ++ } ++ ++ if (WLAN_FC_GET_TYPE(rx->fc) == WLAN_FC_TYPE_DATA) ++ local->scan.txrx_count++; ++ if (unlikely(local->scan.in_scan != 0 && ++ rx->u.rx.status->freq == local->scan.freq)) { ++ struct ieee80211_hdr *hdr; ++ u16 fc; ++ ++ local->scan.rx_packets++; ++ ++ hdr = (struct ieee80211_hdr *) skb->data; ++ fc = le16_to_cpu(hdr->frame_control); ++ ++ if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT && ++ WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_BEACON) { ++ local->scan.rx_beacon++; ++ /* Need to trim FCS here because it is normally ++ * removed only after this passive scan handler. */ ++ if (rx->local->hw->rx_includes_fcs && ++ rx->skb->len > FCS_LEN) ++ skb_trim(rx->skb, rx->skb->len - FCS_LEN); ++ ++ ieee80211_rx_mgmt(rx->dev, rx->skb, rx->u.rx.status, ++ ieee80211_msg_passive_scan); ++ return TXRX_QUEUED; ++ } else { ++ I802_DEBUG_INC(local->rx_handlers_drop_passive_scan); ++ return TXRX_DROP; ++ } ++ } ++ ++ return TXRX_CONTINUE; ++} ++ ++ ++static u8 * ieee80211_get_bssid(struct ieee80211_hdr *hdr, size_t len) ++{ ++ u16 fc; ++ ++ if (len < 24) ++ return NULL; ++ ++ fc = le16_to_cpu(hdr->frame_control); ++ ++ switch (WLAN_FC_GET_TYPE(fc)) { ++ case WLAN_FC_TYPE_DATA: ++ switch (fc & (WLAN_FC_TODS | WLAN_FC_FROMDS)) { ++ case WLAN_FC_TODS: ++ return hdr->addr1; ++ case (WLAN_FC_TODS | WLAN_FC_FROMDS): ++ return NULL; ++ case WLAN_FC_FROMDS: ++ return hdr->addr2; ++ case 0: ++ return hdr->addr3; ++ } ++ break; ++ case WLAN_FC_TYPE_MGMT: ++ return hdr->addr3; ++ case WLAN_FC_TYPE_CTRL: ++ if (WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_PSPOLL) ++ return hdr->addr1; ++ else ++ return NULL; ++ } ++ ++ return NULL; ++} ++ ++ ++static struct net_device * ieee80211_get_rx_dev(struct ieee80211_local *local, ++ struct ieee80211_hdr *hdr, ++ size_t len, int *sta_broadcast) ++{ ++ u8 *bssid; ++ struct net_device *dev; ++ u16 fc; ++ ++ bssid = ieee80211_get_bssid(hdr, len); ++ if (bssid) { ++ dev = ieee80211_own_bssid(local, bssid); ++ if (!dev && (local->conf.mode == IW_MODE_INFRA || ++ local->conf.mode == IW_MODE_ADHOC)) ++ dev = ieee80211_sta_bssid(local, bssid, hdr->addr1, ++ sta_broadcast); ++ if (dev) ++ return dev; ++ } ++ ++ if (len >= 30) { ++ fc = le16_to_cpu(hdr->frame_control); ++ if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_DATA && ++ (fc & (WLAN_FC_TODS | WLAN_FC_FROMDS)) == ++ (WLAN_FC_TODS | WLAN_FC_FROMDS)) { ++ dev = ieee80211_get_wds_dev(local, hdr->addr2); ++ if (dev) ++ return dev; ++ } ++ } ++ ++ /* Default to default device if nothing else matches */ ++ return local->wdev; ++} ++ ++ ++static void ieee80211_rx_michael_mic_report(struct net_device *dev, ++ struct ieee80211_hdr *hdr, ++ struct sta_info *sta, ++ struct ieee80211_txrx_data *rx) ++{ ++ int keyidx, hdrlen; ++ ++ hdrlen = ieee80211_get_hdrlen_from_skb(rx->skb); ++ if (rx->skb->len >= hdrlen + 4) ++ keyidx = rx->skb->data[hdrlen + 3] >> 6; ++ else ++ keyidx = -1; ++ ++ /* TODO: verify that this is not triggered by fragmented ++ * frames (hw does not verify MIC for them). */ ++ printk(KERN_DEBUG "%s: TKIP hwaccel reported Michael MIC " ++ "failure from " MACSTR " to " MACSTR " keyidx=%d\n", ++ dev->name, MAC2STR(hdr->addr2), MAC2STR(hdr->addr1), keyidx); ++ ++ if (sta == NULL) { ++ /* Some hardware versions seem to generate incorrect ++ * Michael MIC reports; ignore them to avoid triggering ++ * countermeasures. */ ++ printk(KERN_DEBUG "%s: ignored spurious Michael MIC " ++ "error for unknown address " MACSTR "\n", ++ dev->name, MAC2STR(hdr->addr2)); ++ goto ignore; ++ } ++ ++ if (!(rx->fc & WLAN_FC_ISWEP)) { ++ printk(KERN_DEBUG "%s: ignored spurious Michael MIC " ++ "error for a frame with no ISWEP flag (src " ++ MACSTR ")\n", dev->name, MAC2STR(hdr->addr2)); ++ goto ignore; ++ } ++ ++ if (rx->local->hw->wep_include_iv && ++ rx->local->conf.mode == IW_MODE_MASTER) { ++ int keyidx = ieee80211_wep_get_keyidx(rx->skb); ++ /* AP with Pairwise keys support should never receive Michael ++ * MIC errors for non-zero keyidx because these are reserved ++ * for group keys and only the AP is sending real multicast ++ * frames in BSS. */ ++ if (keyidx) { ++ printk(KERN_DEBUG "%s: ignored Michael MIC error for " ++ "a frame with non-zero keyidx (%d) (src " MACSTR ++ ")\n", dev->name, keyidx, MAC2STR(hdr->addr2)); ++ goto ignore; ++ } ++ } ++ ++ if (WLAN_FC_GET_TYPE(rx->fc) != WLAN_FC_TYPE_DATA && ++ (WLAN_FC_GET_TYPE(rx->fc) != WLAN_FC_TYPE_MGMT || ++ WLAN_FC_GET_STYPE(rx->fc) != WLAN_FC_STYPE_AUTH)) { ++ printk(KERN_DEBUG "%s: ignored spurious Michael MIC " ++ "error for a frame that cannot be encrypted " ++ "(fc=0x%04x) (src " MACSTR ")\n", ++ dev->name, rx->fc, MAC2STR(hdr->addr2)); ++ goto ignore; ++ } ++ ++ do { ++ union iwreq_data wrqu; ++ char *buf = kmalloc(128, GFP_ATOMIC); ++ if (buf == NULL) ++ break; ++ ++ /* TODO: needed parameters: count, key type, TSC */ ++ sprintf(buf, "MLME-MICHAELMICFAILURE.indication(" ++ "keyid=%d %scast addr=" MACSTR ")", ++ keyidx, hdr->addr1[0] & 0x01 ? "broad" : "uni", ++ MAC2STR(hdr->addr2)); ++ memset(&wrqu, 0, sizeof(wrqu)); ++ wrqu.data.length = strlen(buf); ++ wireless_send_event(rx->dev, IWEVCUSTOM, &wrqu, buf); ++ kfree(buf); ++ } while (0); ++ ++ /* TODO: consider verifying the MIC error report with software ++ * implementation if we get too many spurious reports from the ++ * hardware. */ ++ ieee80211_rx_mgmt(rx->dev, rx->skb, rx->u.rx.status, ++ ieee80211_msg_michael_mic_failure); ++ return; ++ ++ ignore: ++ dev_kfree_skb(rx->skb); ++ rx->skb = NULL; ++} ++ ++ ++static void ieee80211_sta_rx_broadcast(struct ieee80211_txrx_data *rx) ++{ ++ struct ieee80211_local *local = rx->dev->priv; ++ u8 *_bssid, bssid[ETH_ALEN]; ++ struct sk_buff *orig_skb = rx->skb, *skb; ++ struct ieee80211_hdr *hdr; ++ ieee80211_rx_handler *handler; ++ ieee80211_txrx_result res; ++ struct list_head *ptr; ++ ++ hdr = (struct ieee80211_hdr *) orig_skb->data; ++ _bssid = ieee80211_get_bssid(hdr, orig_skb->len); ++ if (_bssid == NULL) { ++ dev_kfree_skb(orig_skb); ++ return; ++ } ++ memcpy(bssid, _bssid, ETH_ALEN); ++ ++ list_for_each(ptr, &local->sub_if_list) { ++ struct ieee80211_sub_if_data *sdata = ++ list_entry(ptr, struct ieee80211_sub_if_data, list); ++ if (sdata->type != IEEE80211_SUB_IF_TYPE_STA || ++ (memcmp(bssid, sdata->u.sta.bssid, ETH_ALEN) != 0 && ++ !(bssid[0] & 0x01))) ++ continue; ++ ++ skb = skb_copy(orig_skb, GFP_ATOMIC); ++ if (skb == NULL) { ++ if (net_ratelimit()) { ++ printk(KERN_DEBUG "%s: failed to copy " ++ "multicast frame for %s", ++ rx->dev->name, sdata->dev->name); ++ } ++ continue; ++ } ++ ++ hdr = (struct ieee80211_hdr *) skb->data; ++ rx->skb = skb; ++ rx->dev = sdata->dev; ++ rx->sdata = IEEE80211_DEV_TO_SUB_IF(rx->dev); ++ ++ res = TXRX_DROP; ++ for (handler = local->rx_handlers; *handler != NULL; handler++) ++ { ++ res = (*handler)(rx); ++ if (res == TXRX_DROP || res == TXRX_QUEUED) ++ break; ++ } ++ ++ if (res == TXRX_DROP || *handler == NULL) ++ dev_kfree_skb(skb); ++ } ++ ++ dev_kfree_skb(orig_skb); ++} ++ ++ ++/* ++ * This is the receive path handler. It is called by a low level driver when an ++ * 802.11 MPDU is received from the hardware. ++ */ ++void __ieee80211_rx(struct net_device *dev, struct sk_buff *skb, ++ struct ieee80211_rx_status *status) ++{ ++ struct ieee80211_local *local = dev->priv; ++ struct sta_info *sta; ++ struct ieee80211_hdr *hdr; ++ ieee80211_rx_handler *handler; ++ struct ieee80211_txrx_data rx; ++ ieee80211_txrx_result res = TXRX_DROP; ++ u16 type; ++ int sta_broadcast = 0; ++ ++ hdr = (struct ieee80211_hdr *) skb->data; ++ memset(&rx, 0, sizeof(rx)); ++ rx.skb = skb; ++ rx.local = local; ++ if (skb->len >= 16) { ++ sta = rx.sta = sta_info_get(local, hdr->addr2); ++ if (unlikely(sta == NULL && ++ local->conf.mode == IW_MODE_ADHOC)) { ++ u8 *bssid = ieee80211_get_bssid(hdr, skb->len); ++ if (bssid && ++ memcmp(bssid, local->bssid, ETH_ALEN) == 0) ++ sta = rx.sta = ++ ieee80211_ibss_add_sta(dev, skb, bssid, ++ hdr->addr2); ++ } ++ } else ++ sta = rx.sta = NULL; ++ if (sta && !sta->assoc_ap && !(sta->flags & WLAN_STA_WDS)) ++ rx.dev = sta->dev; ++ else ++ rx.dev = ieee80211_get_rx_dev(local, hdr, skb->len, ++ &sta_broadcast); ++ ++ rx.sdata = IEEE80211_DEV_TO_SUB_IF(rx.dev); ++ rx.u.rx.status = status; ++ rx.fc = skb->len >= 2 ? le16_to_cpu(hdr->frame_control) : 0; ++ type = WLAN_FC_GET_TYPE(rx.fc); ++ if (type == WLAN_FC_TYPE_DATA || type == WLAN_FC_TYPE_MGMT) ++ local->dot11ReceivedFragmentCount++; ++ if (sta_broadcast) { ++ ieee80211_sta_rx_broadcast(&rx); ++ goto end; ++ } ++ ++ if ((status->flag & RX_FLAG_MMIC_ERROR)) { ++ ieee80211_rx_michael_mic_report(dev, hdr, sta, &rx); ++ goto end; ++ } ++ ++ for (handler = local->rx_handlers; *handler != NULL; handler++) { ++ res = (*handler)(&rx); ++ if (res != TXRX_CONTINUE) { ++ if (res == TXRX_DROP) { ++ I802_DEBUG_INC(local->rx_handlers_drop); ++ if (sta) ++ sta->rx_dropped++; ++ } ++ if (res == TXRX_QUEUED) ++ I802_DEBUG_INC(local->rx_handlers_queued); ++ break; ++ } ++ } ++ skb = rx.skb; /* handlers are allowed to change skb */ ++ ++ if (res == TXRX_DROP || *handler == NULL) ++ dev_kfree_skb(skb); ++ ++ end: ++ if (sta) ++ sta_info_release(local, sta); ++} ++ ++ ++static ieee80211_txrx_result ++ieee80211_tx_h_load_stats(struct ieee80211_txrx_data *tx) ++{ ++ struct ieee80211_local *local = tx->local; ++ struct sk_buff *skb = tx->skb; ++ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; ++ u32 load = 0, hdrtime; ++ ++ /* TODO: this could be part of tx_status handling, so that the number ++ * of retries would be known; TX rate should in that case be stored ++ * somewhere with the packet */ ++ ++ /* Estimate total channel use caused by this frame */ ++ ++ /* 1 bit at 1 Mbit/s takes 1 usec; in channel_use values, ++ * 1 usec = 1/8 * (1080 / 10) = 13.5 */ ++ ++ if (local->conf.phymode == MODE_IEEE80211A || ++ local->conf.phymode == MODE_ATHEROS_TURBO || ++ local->conf.phymode == MODE_ATHEROS_TURBOG || ++ (local->conf.phymode == MODE_IEEE80211G && ++ tx->u.tx.rate->flags & IEEE80211_RATE_ERP)) ++ hdrtime = CHAN_UTIL_HDR_SHORT; ++ else ++ hdrtime = CHAN_UTIL_HDR_LONG; ++ ++ load = hdrtime; ++ if (!MULTICAST_ADDR(hdr->addr1)) ++ load += hdrtime; ++ ++ if (tx->u.tx.control->use_rts_cts) ++ load += 2 * hdrtime; ++ else if (tx->u.tx.control->use_cts_protect) ++ load += hdrtime; ++ ++ load += skb->len * tx->u.tx.rate->rate_inv; ++ ++ if (tx->u.tx.extra_frag) { ++ int i; ++ for (i = 0; i < tx->u.tx.num_extra_frag; i++) { ++ load += 2 * hdrtime; ++ load += tx->u.tx.extra_frag[i]->len * ++ tx->u.tx.rate->rate; ++ } ++ } ++ ++ /* Divide channel_use by 8 to avoid wrapping around the counter */ ++ load >>= CHAN_UTIL_SHIFT; ++ local->channel_use_raw += load; ++ if (tx->sta) ++ tx->sta->channel_use_raw += load; ++ tx->sdata->channel_use_raw += load; ++ ++ return TXRX_CONTINUE; ++} ++ ++ ++static ieee80211_txrx_result ++ieee80211_rx_h_load_stats(struct ieee80211_txrx_data *rx) ++{ ++ struct ieee80211_local *local = rx->local; ++ struct sk_buff *skb = rx->skb; ++ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; ++ u32 load = 0, hdrtime; ++ struct ieee80211_rate *rate; ++ int i; ++ ++ /* Estimate total channel use caused by this frame */ ++ ++ if (unlikely(local->num_curr_rates < 0)) ++ return TXRX_CONTINUE; ++ ++ rate = &local->curr_rates[0]; ++ for (i = 0; i < local->num_curr_rates; i++) { ++ if (local->curr_rates[i].val == rx->u.rx.status->rate) { ++ rate = &local->curr_rates[i]; ++ break; ++ } ++ } ++ ++ /* 1 bit at 1 Mbit/s takes 1 usec; in channel_use values, ++ * 1 usec = 1/8 * (1080 / 10) = 13.5 */ ++ ++ if (local->conf.phymode == MODE_IEEE80211A || ++ local->conf.phymode == MODE_ATHEROS_TURBO || ++ local->conf.phymode == MODE_ATHEROS_TURBOG || ++ (local->conf.phymode == MODE_IEEE80211G && ++ rate->flags & IEEE80211_RATE_ERP)) ++ hdrtime = CHAN_UTIL_HDR_SHORT; ++ else ++ hdrtime = CHAN_UTIL_HDR_LONG; ++ ++ load = hdrtime; ++ if (!MULTICAST_ADDR(hdr->addr1)) ++ load += hdrtime; ++ ++ load += skb->len * rate->rate_inv; ++ ++ /* Divide channel_use by 8 to avoid wrapping around the counter */ ++ load >>= CHAN_UTIL_SHIFT; ++ local->channel_use_raw += load; ++ if (rx->sta) ++ rx->sta->channel_use_raw += load; ++ rx->sdata->channel_use_raw += load; ++ ++ return TXRX_CONTINUE; ++} ++ ++ ++static void ieee80211_stat_refresh(unsigned long data) ++{ ++ struct ieee80211_local *local = (struct ieee80211_local *) data; ++ struct list_head *ptr, *n; ++ ++ if (!local->stat_time) ++ return; ++ ++ /* go through all stations */ ++ spin_lock_bh(&local->sta_lock); ++ list_for_each(ptr, &local->sta_list) { ++ struct sta_info *sta = ++ list_entry(ptr, struct sta_info, list); ++ sta->channel_use = (sta->channel_use_raw / local->stat_time) / ++ CHAN_UTIL_PER_10MS; ++ sta->channel_use_raw = 0; ++ } ++ spin_unlock_bh(&local->sta_lock); ++ ++ /* go through all subinterfaces */ ++ list_for_each_safe(ptr, n, &local->sub_if_list) { ++ struct ieee80211_sub_if_data *sdata = ++ list_entry(ptr, struct ieee80211_sub_if_data, list); ++ sdata->channel_use = (sdata->channel_use_raw / ++ local->stat_time) / CHAN_UTIL_PER_10MS; ++ sdata->channel_use_raw = 0; ++ ++ } ++ ++ /* hardware interface */ ++ local->channel_use = (local->channel_use_raw / ++ local->stat_time) / CHAN_UTIL_PER_10MS; ++ local->channel_use_raw = 0; ++ ++ local->stat_timer.expires = jiffies + HZ * local->stat_time / 100; ++ add_timer(&local->stat_timer); ++} ++ ++ ++/* This is a version of the rx handler that can be called from hard irq ++ * context. Post the skb on the queue and schedule the tasklet */ ++void ieee80211_rx_irqsafe(struct net_device *dev, struct sk_buff *skb, ++ struct ieee80211_rx_status *status) ++{ ++ struct ieee80211_local *local = dev->priv; ++ struct ieee80211_rx_status *saved; ++ ++ skb->dev = dev; ++ saved = kmalloc(sizeof(struct ieee80211_rx_status), GFP_ATOMIC); ++ if (saved) ++ memcpy(saved, status, sizeof(struct ieee80211_rx_status)); ++ memcpy(skb->cb, &saved, sizeof(saved)); ++ skb->pkt_type = ieee80211_rx_msg; ++ skb_queue_tail(&local->skb_queue, skb); ++ tasklet_schedule(&local->tasklet); ++} ++ ++ ++void ieee80211_tx_status_irqsafe(struct net_device *dev, struct sk_buff *skb, ++ struct ieee80211_tx_status *status) ++{ ++ struct ieee80211_local *local = dev->priv; ++ struct ieee80211_tx_status *saved; ++ int tmp; ++ ++ skb->dev = dev; ++ saved = kmalloc(sizeof(struct ieee80211_tx_status), GFP_ATOMIC); ++ if (saved) ++ memcpy(saved, status, sizeof(struct ieee80211_tx_status)); ++ memcpy(skb->cb, &saved, sizeof(saved)); ++ skb->pkt_type = ieee80211_tx_status_msg; ++ skb_queue_tail(status->control.req_tx_status ? ++ &local->skb_queue : &local->skb_queue_unreliable, skb); ++ tmp = skb_queue_len(&local->skb_queue) + ++ skb_queue_len(&local->skb_queue_unreliable); ++ while (tmp > IEEE80211_IRQSAFE_QUEUE_LIMIT && ++ (skb = skb_dequeue(&local->skb_queue_unreliable))) { ++ memcpy(&saved, skb->cb, sizeof(saved)); ++ kfree(saved); ++ dev_kfree_skb_irq(skb); ++ tmp--; ++ I802_DEBUG_INC(local->tx_status_drop); ++ } ++ tasklet_schedule(&local->tasklet); ++} ++ ++ ++static void ieee80211_tasklet_handler(unsigned long data) ++{ ++ struct ieee80211_local *local = (struct ieee80211_local *) data; ++ struct sk_buff *skb; ++ struct ieee80211_rx_status *rx_status; ++ struct ieee80211_tx_status *tx_status; ++ ++ while ((skb = skb_dequeue(&local->skb_queue)) || ++ (skb = skb_dequeue(&local->skb_queue_unreliable))) { ++ switch (skb->pkt_type) { ++ case ieee80211_rx_msg: ++ memcpy(&rx_status, skb->cb, sizeof(rx_status)); ++ if (!rx_status) { ++ if (net_ratelimit()) ++ printk(KERN_WARNING "%s: Not enough " ++ "memory, dropping packet", ++ skb->dev->name); ++ dev_kfree_skb(skb); ++ return; ++ } ++ /* Clear skb->type in order to not confuse kernel ++ * netstack. */ ++ skb->pkt_type = 0; ++ __ieee80211_rx(skb->dev, skb, rx_status); ++ kfree(rx_status); ++ break; ++ case ieee80211_tx_status_msg: ++ memcpy(&tx_status, skb->cb, sizeof(tx_status)); ++ if (!tx_status) { ++ dev_kfree_skb(skb); ++ return; ++ } ++ skb->pkt_type = 0; ++ ieee80211_tx_status(skb->dev, skb, tx_status); ++ kfree(tx_status); ++ break; ++ default: /* should never get here! */ ++ printk(KERN_ERR "%s: Unknown message type (%d)\n", ++ local->wdev->name, skb->pkt_type); ++ dev_kfree_skb(skb); ++ break; ++ } ++ } ++} ++ ++ ++/* Remove added headers (e.g., QoS control), encryption header/MIC, etc. to ++ * make a prepared TX frame (one that has been given to hw) to look like brand ++ * new IEEE 802.11 frame that is ready to go through TX processing again. ++ * Also, tx_packet_data in cb is restored from tx_control. */ ++static void ieee80211_remove_tx_extra(struct ieee80211_local *local, ++ struct ieee80211_key *key, ++ struct sk_buff *skb, ++ struct ieee80211_tx_control *control) ++{ ++ int hdrlen, iv_len, mic_len; ++ struct ieee80211_tx_packet_data *pkt_data; ++ ++ pkt_data = (struct ieee80211_tx_packet_data *)skb->cb; ++ pkt_data->sdata = control->sdata; ++ pkt_data->req_tx_status = control->req_tx_status; ++ pkt_data->do_not_encrypt = control->do_not_encrypt; ++ pkt_data->pkt_probe_resp = (control->pkt_type == PKT_PROBE_RESP); ++ pkt_data->requeue = control->requeue; ++ pkt_data->queue = control->queue; ++ ++ if (key == NULL) ++ return; ++ ++ hdrlen = ieee80211_get_hdrlen_from_skb(skb); ++ ++ switch (key->alg) { ++ case ALG_WEP: ++ iv_len = WEP_IV_LEN; ++ mic_len = WEP_ICV_LEN; ++ break; ++ case ALG_TKIP: ++ iv_len = TKIP_IV_LEN; ++ mic_len = TKIP_ICV_LEN; ++ break; ++ case ALG_CCMP: ++ iv_len = CCMP_HDR_LEN; ++ mic_len = CCMP_MIC_LEN; ++ break; ++ default: ++ return; ++ } ++ ++ if (skb->len >= mic_len && key->force_sw_encrypt) ++ skb_trim(skb, skb->len - mic_len); ++ if (skb->len >= iv_len && skb->len > hdrlen) { ++ memmove(skb->data + iv_len, skb->data, hdrlen); ++ skb_pull(skb, iv_len); ++ } ++ ++ { ++ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; ++ u16 fc = le16_to_cpu(hdr->frame_control); ++ if ((fc & 0x8C) == 0x88) /* QoS Control Field */ { ++ fc &= ~(WLAN_FC_STYPE_QOS_DATA << 4); ++ hdr->frame_control = cpu_to_le16(fc); ++ memmove(skb->data + 2, skb->data, hdrlen - 2); ++ skb_pull(skb, 2); ++ } ++ } ++} ++ ++ ++void ieee80211_tx_status(struct net_device *dev, struct sk_buff *skb, ++ struct ieee80211_tx_status *status) ++{ ++ struct sk_buff *skb2; ++ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; ++ struct ieee80211_local *local = dev->priv; ++ u16 frag, type; ++ u32 msg_type; ++ ++ if (!status) { ++ printk(KERN_ERR ++ "%s: ieee80211_tx_status called with NULL status\n", ++ dev->name); ++ dev_kfree_skb(skb); ++ return; ++ } ++ ++ if (status->excessive_retries) { ++ struct sta_info *sta; ++ sta = sta_info_get(local, hdr->addr1); ++ if (sta) { ++ if (sta->flags & WLAN_STA_PS) { ++ /* The STA is in power save mode, so assume ++ * that this TX packet failed because of that. ++ */ ++ status->excessive_retries = 0; ++ status->tx_filtered = 1; ++ } ++ sta_info_release(local, sta); ++ } ++ } ++ ++ if (status->tx_filtered) { ++ struct sta_info *sta; ++ sta = sta_info_get(local, hdr->addr1); ++ if (sta) { ++ sta->tx_filtered_count++; ++ ++ /* Clear the TX filter mask for this STA when sending ++ * the next packet. If the STA went to power save mode, ++ * this will happen when it is waking up for the next ++ * time. */ ++ sta->clear_dst_mask = 1; ++ ++ /* TODO: Is the WLAN_STA_PS flag always set here or is ++ * the race between RX and TX status causing some ++ * packets to be filtered out before 80211.o gets an ++ * update for PS status? This seems to be the case, so ++ * no changes are likely to be needed. */ ++ if (sta->flags & WLAN_STA_PS && ++ skb_queue_len(&sta->tx_filtered) < ++ STA_MAX_TX_BUFFER) { ++ ieee80211_remove_tx_extra(local, sta->key, ++ skb, ++ &status->control); ++ skb_queue_tail(&sta->tx_filtered, skb); ++ } else if (!(sta->flags & WLAN_STA_PS) && ++ !status->control.requeue) { ++ /* Software retry the packet once */ ++ status->control.requeue = 1; ++ ieee80211_remove_tx_extra(local, sta->key, ++ skb, ++ &status->control); ++ dev_queue_xmit(skb); ++ } else { ++ if (net_ratelimit()) { ++ printk(KERN_DEBUG "%s: dropped TX " ++ "filtered frame queue_len=%d " ++ "PS=%d @%lu\n", ++ dev->name, ++ skb_queue_len( ++ &sta->tx_filtered), ++ !!(sta->flags & WLAN_STA_PS), ++ jiffies); ++ } ++ dev_kfree_skb(skb); ++ } ++ sta_info_release(local, sta); ++ return; ++ } ++ } else { ++ rate_control_tx_status(dev, skb, status); ++ } ++ ++#ifdef IEEE80211_LEDS ++ if (local->tx_led_counter && (local->tx_led_counter-- == 1)) { ++ ieee80211_tx_led(0, dev); ++ } ++#endif /* IEEE80211_LEDS */ ++ /* SNMP counters ++ * Fragments are passed to low-level drivers as separate skbs, so these ++ * are actually fragments, not frames. Update frame counters only for ++ * the first fragment of the frame. */ ++ ++ frag = WLAN_GET_SEQ_FRAG(le16_to_cpu(hdr->seq_ctrl)); ++ type = WLAN_FC_GET_TYPE(le16_to_cpu(hdr->frame_control)); ++ ++ if (status->ack) { ++ if (frag == 0) { ++ local->dot11TransmittedFrameCount++; ++ if (MULTICAST_ADDR(hdr->addr1)) ++ local->dot11MulticastTransmittedFrameCount++; ++ if (status->retry_count > 0) ++ local->dot11RetryCount++; ++ if (status->retry_count > 1) ++ local->dot11MultipleRetryCount++; ++ } ++ ++ /* This counter shall be incremented for an acknowledged MPDU ++ * with an individual address in the address 1 field or an MPDU ++ * with a multicast address in the address 1 field of type Data ++ * or Management. */ ++ if (!MULTICAST_ADDR(hdr->addr1) || type == WLAN_FC_TYPE_DATA || ++ type == WLAN_FC_TYPE_MGMT) ++ local->dot11TransmittedFragmentCount++; ++ } else { ++ if (frag == 0) ++ local->dot11FailedCount++; ++ } ++ ++ if (!status->control.req_tx_status) { ++ dev_kfree_skb(skb); ++ return; ++ } ++ ++ msg_type = status->ack ? ieee80211_msg_tx_callback_ack : ++ ieee80211_msg_tx_callback_fail; ++ ++ /* skb was the original skb used for TX. Clone it and give the clone ++ * to netif_rx(). Free original skb. */ ++ skb2 = skb_copy(skb, GFP_ATOMIC); ++ if (!skb2) { ++ dev_kfree_skb(skb); ++ return; ++ } ++ dev_kfree_skb(skb); ++ skb = skb2; ++ ++ /* Send frame to hostapd */ ++ ieee80211_rx_mgmt(dev, skb, NULL, msg_type); ++} ++ ++ ++/* TODO: implement register/unregister functions for adding TX/RX handlers ++ * into ordered list */ ++ ++static ieee80211_rx_handler ieee80211_rx_handlers[] = ++{ ++ ieee80211_rx_h_parse_qos, ++ ieee80211_rx_h_load_stats, ++ ieee80211_rx_h_monitor, ++ ieee80211_rx_h_passive_scan, ++ ieee80211_rx_h_check, ++ ieee80211_rx_h_sta_process, ++ ieee80211_rx_h_ccmp_decrypt, ++ ieee80211_rx_h_tkip_decrypt, ++ ieee80211_rx_h_wep_weak_iv_detection, ++ ieee80211_rx_h_wep_decrypt, ++ ieee80211_rx_h_defragment, ++ ieee80211_rx_h_ieee80211_rx_h_ps_poll, ++ ieee80211_rx_h_michael_mic_verify, ++ /* this must be after decryption - so header is counted in MPDU mic ++ * must be before pae and data, so QOS_DATA format frames ++ * are not passed to user space by these functions ++ */ ++ ieee80211_rx_h_remove_qos_control, ++ ieee80211_rx_h_802_1x_pae, ++ ieee80211_rx_h_drop_unencrypted, ++ ieee80211_rx_h_data, ++ ieee80211_rx_h_mgmt, ++ NULL ++}; ++ ++static ieee80211_tx_handler ieee80211_tx_handlers[] = ++{ ++ ieee80211_tx_h_rate_limit, ++ ieee80211_tx_h_check_assoc, ++ ieee80211_tx_h_ps_buf, ++ ieee80211_tx_h_select_key, ++ ieee80211_tx_h_michael_mic_add, ++ ieee80211_tx_h_fragment, ++ ieee80211_tx_h_tkip_encrypt, ++ ieee80211_tx_h_ccmp_encrypt, ++ ieee80211_tx_h_wep_encrypt, ++ ieee80211_tx_h_rate_ctrl, ++ ieee80211_tx_h_misc, ++ ieee80211_tx_h_load_stats, ++ NULL ++}; ++ ++ ++static void ieee80211_if_sdata_init(struct ieee80211_sub_if_data *sdata) ++{ ++ /* Default values for sub-interface parameters */ ++ sdata->drop_unencrypted = 0; ++ sdata->eapol = 1; ++} ++ ++ ++static struct net_device *ieee80211_if_add(struct net_device *dev, ++ const char *name, int locked) ++{ ++ struct net_device *wds_dev = NULL, *tmp_dev; ++ struct ieee80211_local *local = dev->priv; ++ struct ieee80211_sub_if_data *sdata = NULL, *sdata_parent; ++ int alloc_size; ++ int ret; ++ int i; ++ ++ /* ensure 32-bit alignment of our private data and hw private data */ ++ alloc_size = sizeof(struct net_device) + 3 + ++ sizeof(struct ieee80211_sub_if_data) + 3; ++ ++ wds_dev = (struct net_device *) kmalloc(alloc_size, GFP_KERNEL); ++ if (wds_dev == NULL) ++ return NULL; ++ ++ memset(wds_dev, 0, alloc_size); ++ wds_dev->priv = local; ++ ether_setup(wds_dev); ++ if (strlen(name) == 0) { ++ i = 0; ++ do { ++ sprintf(wds_dev->name, "%s.%d", dev->name, i++); ++ tmp_dev = dev_get_by_name(wds_dev->name); ++ if (tmp_dev == NULL) ++ break; ++ dev_put(tmp_dev); ++ } while (i < 10000); ++ } else { ++ snprintf(wds_dev->name, IFNAMSIZ, "%s", name); ++ } ++ ++ memcpy(wds_dev->dev_addr, dev->dev_addr, ETH_ALEN); ++ wds_dev->hard_start_xmit = ieee80211_subif_start_xmit; ++ wds_dev->wireless_handlers = ++ (struct iw_handler_def *) &ieee80211_iw_handler_def; ++ wds_dev->do_ioctl = ieee80211_ioctl; ++ wds_dev->change_mtu = ieee80211_change_mtu; ++ wds_dev->tx_timeout = ieee80211_tx_timeout; ++ wds_dev->get_stats = ieee80211_get_stats; ++ wds_dev->open = ieee80211_open; ++ wds_dev->stop = ieee80211_stop; ++ wds_dev->base_addr = dev->base_addr; ++ wds_dev->irq = dev->irq; ++ wds_dev->mem_start = dev->mem_start; ++ wds_dev->mem_end = dev->mem_end; ++ wds_dev->tx_queue_len = 0; ++ ++ sdata = IEEE80211_DEV_TO_SUB_IF(wds_dev); ++ sdata->type = IEEE80211_SUB_IF_TYPE_AP; ++ sdata->master = local->mdev; ++ sdata->dev = wds_dev; ++ sdata->local = local; ++ memset(&sdata->stats, 0, sizeof(struct net_device_stats)); ++ sdata_parent = IEEE80211_DEV_TO_SUB_IF(dev); ++ if (sdata_parent->type == IEEE80211_SUB_IF_TYPE_AP) ++ sdata->bss = &sdata_parent->u.ap; ++ else { ++ printk(KERN_DEBUG "%s: could not set BSS pointer for new " ++ "interface %s\n", dev->name, wds_dev->name); ++ } ++ ieee80211_if_sdata_init(sdata); ++ ++ if (locked) ++ ret = register_netdevice(wds_dev); ++ else ++ ret = register_netdev(wds_dev); ++ if (ret) { ++ kfree(wds_dev); ++ return NULL; ++ } ++ ++ list_add(&sdata->list, &local->sub_if_list); ++ ++ return wds_dev; ++} ++ ++ ++int ieee80211_if_add_wds(struct net_device *dev, const char *name, ++ struct ieee80211_if_wds *wds, int locked) ++{ ++ struct net_device *wds_dev = NULL; ++ struct ieee80211_sub_if_data *sdata = NULL; ++ ++ if (strlen(name) != 0) { ++ wds_dev = dev_get_by_name(name); ++ if (wds_dev) { ++ dev_put(wds_dev); ++ return -EEXIST; ++ } ++ } ++ ++ wds_dev = ieee80211_if_add(dev, name, locked); ++ if (wds_dev == NULL) ++ return -ENOANO; ++ ++ sdata = IEEE80211_DEV_TO_SUB_IF(wds_dev); ++ sdata->type = IEEE80211_SUB_IF_TYPE_WDS; ++ memcpy(&sdata->u.wds, wds, sizeof(struct ieee80211_if_wds)); ++ ++#ifdef CONFIG_D80211_VERBOSE_DEBUG ++ printk(KERN_DEBUG ++ "%s: Added WDS Link to " MACSTR "\n", ++ wds_dev->name, MAC2STR(sdata->u.wds.remote_addr)); ++#endif /* CONFIG_D80211_VERBOSE_DEBUG */ ++ ++ ieee80211_proc_init_virtual(wds_dev); ++ ++ return 0; ++} ++ ++ ++int ieee80211_if_update_wds(struct net_device *dev, char *name, ++ struct ieee80211_if_wds *wds, int locked) ++{ ++ struct net_device *wds_dev = NULL; ++ struct ieee80211_local *local = dev->priv; ++ struct ieee80211_sub_if_data *sdata = NULL; ++ struct sta_info *sta; ++ struct list_head *ptr; ++ ++ list_for_each(ptr, &local->sub_if_list) { ++ sdata = list_entry(ptr, struct ieee80211_sub_if_data, list); ++ if (strcmp(name, sdata->dev->name) == 0) { ++ wds_dev = sdata->dev; ++ break; ++ } ++ } ++ ++ if (wds_dev == NULL || sdata->type != IEEE80211_SUB_IF_TYPE_WDS) ++ return -ENODEV; ++ ++ /* Remove STA entry for the old peer */ ++ sta = sta_info_get(local, sdata->u.wds.remote_addr); ++ if (sta) { ++ sta_info_release(local, sta); ++ sta_info_free(local, sta, 0); ++ } else { ++ printk(KERN_DEBUG "%s: could not find STA entry for WDS link " ++ "%s peer " MACSTR "\n", ++ dev->name, wds_dev->name, ++ MAC2STR(sdata->u.wds.remote_addr)); ++ } ++ ++ /* Update WDS link data */ ++ memcpy(&sdata->u.wds, wds, sizeof(struct ieee80211_if_wds)); ++ ++ return 0; ++} ++ ++ ++static void ieee80211_if_init(struct net_device *dev) ++{ ++ struct ieee80211_local *local = dev->priv; ++ ++ spin_lock_init(&local->sub_if_lock); ++ INIT_LIST_HEAD(&local->sub_if_list); ++} ++ ++ ++int ieee80211_if_add_vlan(struct net_device *dev, const char *name, ++ struct ieee80211_if_vlan *vlan, int locked) ++{ ++ struct net_device *vlan_dev = NULL; ++ struct ieee80211_sub_if_data *sdata = NULL; ++ ++ if (strlen(name) != 0) { ++ vlan_dev = dev_get_by_name(name); ++ if (vlan_dev) { ++ dev_put(vlan_dev); ++ return -EEXIST; ++ } ++ } ++ ++ vlan_dev = ieee80211_if_add(dev, name, locked); ++ if (vlan_dev == NULL) ++ return -ENOANO; ++ ++ sdata = IEEE80211_DEV_TO_SUB_IF(vlan_dev); ++ sdata->type = IEEE80211_SUB_IF_TYPE_VLAN; ++ ieee80211_proc_init_virtual(vlan_dev); ++ return 0; ++} ++ ++ ++static void ieee80211_if_ap_init(struct ieee80211_sub_if_data *sdata) ++{ ++ sdata->type = IEEE80211_SUB_IF_TYPE_AP; ++ sdata->u.ap.dtim_period = 2; ++ sdata->u.ap.force_unicast_rateidx = -1; ++ sdata->u.ap.max_ratectrl_rateidx = -1; ++ skb_queue_head_init(&sdata->u.ap.ps_bc_buf); ++ sdata->bss = &sdata->u.ap; ++} ++ ++ ++int ieee80211_if_add_ap(struct net_device *dev, const char *name, u8 *bssid, ++ int locked) ++{ ++ struct ieee80211_local *local = dev->priv; ++ struct net_device *ap_dev = NULL; ++ struct ieee80211_sub_if_data *sdata = NULL; ++ ++ if (local->bss_dev_count >= local->conf.bss_count) ++ return -ENOBUFS; ++ ++ if (strlen(name) != 0) { ++ ap_dev = dev_get_by_name(name); ++ if (ap_dev) { ++ dev_put(ap_dev); ++ return -EEXIST; ++ } ++ } ++ ++ ap_dev = ieee80211_if_add(dev, name, locked); ++ if (ap_dev == NULL) ++ return -ENOANO; ++ ++ memcpy(ap_dev->dev_addr, bssid, ETH_ALEN); ++ sdata = IEEE80211_DEV_TO_SUB_IF(ap_dev); ++ ieee80211_if_ap_init(sdata); ++ ieee80211_proc_init_virtual(ap_dev); ++ spin_lock_bh(&local->sub_if_lock); ++ local->bss_devs[local->bss_dev_count] = ap_dev; ++ local->bss_dev_count++; ++ spin_unlock_bh(&local->sub_if_lock); ++ ++ return 0; ++} ++ ++ ++static void ieee80211_addr_inc(u8 *addr) ++{ ++ int pos = 5; ++ while (pos >= 0) { ++ addr[pos]++; ++ if (addr[pos] != 0) ++ break; ++ pos--; ++ } ++} ++ ++ ++int ieee80211_if_add_sta(struct net_device *dev, const char *name, int locked) ++{ ++ struct ieee80211_local *local = dev->priv; ++ struct net_device *sta_dev; ++ struct ieee80211_sub_if_data *sdata; ++ struct ieee80211_if_sta *ifsta; ++ int i; ++ ++ if (local->sta_dev_count >= local->conf.bss_count) ++ return -ENOBUFS; ++ ++ if (strlen(name) != 0) { ++ sta_dev = dev_get_by_name(name); ++ if (sta_dev) { ++ dev_put(sta_dev); ++ return -EEXIST; ++ } ++ } ++ ++ sta_dev = ieee80211_if_add(dev, name, locked); ++ if (sta_dev == NULL) ++ return -ENOANO; ++ ++ sdata = IEEE80211_DEV_TO_SUB_IF(sta_dev); ++ ifsta = &sdata->u.sta; ++ sdata->type = IEEE80211_SUB_IF_TYPE_STA; ++ ieee80211_proc_init_virtual(sta_dev); ++ ++ spin_lock_bh(&local->sub_if_lock); ++ for (i = 0; i < local->conf.bss_count; i++) { ++ if (local->sta_devs[i] == NULL) { ++ local->sta_devs[i] = sta_dev; ++ local->sta_dev_count++; ++ printk(KERN_DEBUG "%s: using STA entry %d\n", ++ sta_dev->name, i); ++ while (i > 0) { ++ ieee80211_addr_inc(sta_dev->dev_addr); ++ i--; ++ } ++ printk(KERN_DEBUG "%s: MAC address " MACSTR "\n", ++ sta_dev->name, MAC2STR(sta_dev->dev_addr)); ++ break; ++ } ++ } ++ spin_unlock_bh(&local->sub_if_lock); ++ ++ init_timer(&ifsta->timer); ++ ifsta->timer.data = (unsigned long) sta_dev; ++ ifsta->timer.function = ieee80211_sta_timer; ++ ++ ifsta->capab = WLAN_CAPABILITY_ESS; ++ ifsta->auth_algs = IEEE80211_AUTH_ALG_OPEN | ++ IEEE80211_AUTH_ALG_SHARED_KEY; ++ ifsta->create_ibss = 1; ++ ifsta->wmm_enabled = 1; ++ ++ return 0; ++} ++ ++ ++static void ieee80211_if_del(struct ieee80211_local *local, ++ struct ieee80211_sub_if_data *sdata, int locked) ++{ ++ struct sta_info *sta; ++ u8 addr[ETH_ALEN]; ++ int i, j; ++ struct list_head *ptr, *n; ++ ++ memset(addr, 0xff, ETH_ALEN); ++ for (i = 0; i < NUM_DEFAULT_KEYS; i++) { ++ if (!sdata->keys[i]) ++ continue; ++#if 0 ++ /* Low-level driver has probably disabled hw ++ * already, so there is not really much point ++ * in disabling the keys at this point. */ ++ if (local->hw->set_key) ++ local->hw->set_key(dev, DISABLE_KEY, addr, ++ local->keys[i], 0); ++#endif ++ kfree(sdata->keys[i]); ++ } ++ ++ switch (sdata->type) { ++ case IEEE80211_SUB_IF_TYPE_AP: ++ /* Remove all virtual interfaces that use this BSS ++ * as their sdata->bss */ ++ list_for_each_safe(ptr, n, &local->sub_if_list) { ++ struct ieee80211_sub_if_data *tsdata = ++ list_entry(ptr, struct ieee80211_sub_if_data, ++ list); ++ ++ if (tsdata != sdata && tsdata->bss == &sdata->u.ap) { ++ printk(KERN_DEBUG "%s: removing virtual " ++ "interface %s because its BSS interface" ++ " is being removed\n", ++ sdata->dev->name, tsdata->dev->name); ++ ieee80211_if_del(local, tsdata, locked); ++ } ++ } ++ ++ kfree(sdata->u.ap.beacon_head); ++ kfree(sdata->u.ap.beacon_tail); ++ spin_lock_bh(&local->sub_if_lock); ++ for (j = 0; j < local->bss_dev_count; j++) { ++ if (sdata->dev == local->bss_devs[j]) { ++ if (j + 1 < local->bss_dev_count) { ++ memcpy(&local->bss_devs[j], ++ &local->bss_devs[j + 1], ++ (local->bss_dev_count - j - 1) * ++ sizeof(local->bss_devs[0])); ++ local->bss_devs[local->bss_dev_count - ++ 1] = NULL; ++ } else ++ local->bss_devs[j] = NULL; ++ local->bss_dev_count--; ++ break; ++ } ++ } ++ spin_unlock_bh(&local->sub_if_lock); ++ ++ if (sdata->dev != local->mdev) { ++ struct sk_buff *skb; ++ while ((skb = skb_dequeue(&sdata->u.ap.ps_bc_buf))) { ++ local->total_ps_buffered--; ++ dev_kfree_skb(skb); ++ } ++ } ++ ++ break; ++ case IEEE80211_SUB_IF_TYPE_WDS: ++ sta = sta_info_get(local, sdata->u.wds.remote_addr); ++ if (sta) { ++ sta_info_release(local, sta); ++ sta_info_free(local, sta, 0); ++ } else { ++#ifdef CONFIG_D80211_VERBOSE_DEBUG ++ printk(KERN_DEBUG "%s: Someone had deleted my STA " ++ "entry for the WDS link\n", local->mdev->name); ++#endif /* CONFIG_D80211_VERBOSE_DEBUG */ ++ } ++ break; ++ case IEEE80211_SUB_IF_TYPE_STA: ++ del_timer_sync(&sdata->u.sta.timer); ++ if (local->scan_timer.data == (unsigned long) sdata->dev) ++ del_timer_sync(&local->scan_timer); ++ kfree(sdata->u.sta.extra_ie); ++ sdata->u.sta.extra_ie = NULL; ++ kfree(sdata->u.sta.assocreq_ies); ++ sdata->u.sta.assocreq_ies = NULL; ++ kfree(sdata->u.sta.assocresp_ies); ++ sdata->u.sta.assocresp_ies = NULL; ++ if (sdata->u.sta.probe_resp) { ++ dev_kfree_skb(sdata->u.sta.probe_resp); ++ sdata->u.sta.probe_resp = NULL; ++ } ++ for (i = 0; i < local->conf.bss_count; i++) { ++ if (local->sta_devs[i] == sdata->dev) { ++ local->sta_devs[i] = NULL; ++ local->sta_dev_count--; ++ break; ++ } ++ } ++ ++ break; ++ } ++ ++ /* remove all STAs that are bound to this virtual interface */ ++ sta_info_flush(local, sdata->dev); ++ ++ list_del(&sdata->list); ++ ieee80211_proc_deinit_virtual(sdata->dev); ++ if (locked) ++ unregister_netdevice(sdata->dev); ++ else ++ unregister_netdev(sdata->dev); ++ /* Default data device and management device are allocated with the ++ * master device. All other devices are separately allocated and will ++ * be freed here. */ ++ if (sdata->dev != local->mdev && sdata->dev != local->wdev && ++ sdata->dev != local->apdev) ++ kfree(sdata->dev); ++} ++ ++ ++static int ieee80211_if_remove(struct net_device *dev, const char *name, ++ int id, int locked) ++{ ++ struct ieee80211_local *local = dev->priv; ++ struct list_head *ptr, *n; ++ ++ /* Make sure not to touch sdata->master since it may ++ * have already been deleted, etc. */ ++ ++ list_for_each_safe(ptr, n, &local->sub_if_list) { ++ struct ieee80211_sub_if_data *sdata = ++ list_entry(ptr, struct ieee80211_sub_if_data, list); ++ ++ if (sdata->type == id && strcmp(name, sdata->dev->name) == 0) { ++ ieee80211_if_del(local, sdata, locked); ++ break; ++ } ++ } ++ ++ return 0; ++} ++ ++ ++int ieee80211_if_remove_wds(struct net_device *dev, const char *name, ++ int locked) ++{ ++ return ieee80211_if_remove(dev, name, IEEE80211_SUB_IF_TYPE_WDS, ++ locked); ++} ++ ++ ++int ieee80211_if_remove_vlan(struct net_device *dev, const char *name, ++ int locked) ++{ ++ return ieee80211_if_remove(dev, name, IEEE80211_SUB_IF_TYPE_VLAN, ++ locked); ++} ++ ++ ++int ieee80211_if_remove_ap(struct net_device *dev, const char *name, ++ int locked) ++{ ++ return ieee80211_if_remove(dev, name, IEEE80211_SUB_IF_TYPE_AP, ++ locked); ++} ++ ++ ++int ieee80211_if_remove_sta(struct net_device *dev, const char *name, ++ int locked) ++{ ++ return ieee80211_if_remove(dev, name, IEEE80211_SUB_IF_TYPE_STA, ++ locked); ++} ++ ++ ++int ieee80211_if_flush(struct net_device *dev, int locked) ++{ ++ struct ieee80211_local *local = dev->priv; ++ struct list_head *ptr, *n; ++ ++ list_for_each_safe(ptr, n, &local->sub_if_list) { ++ struct ieee80211_sub_if_data *sdata = ++ list_entry(ptr, struct ieee80211_sub_if_data, list); ++ ++ if (sdata->dev != local->mdev && ++ sdata->dev != local->wdev && ++ sdata->dev != local->apdev) ++ ieee80211_if_del(local, sdata, locked); ++ } ++ ++ return 0; ++} ++ ++ ++static void ieee80211_precalc_rates(struct ieee80211_hw *hw) ++{ ++ struct ieee80211_hw_modes *mode; ++ struct ieee80211_rate *rate; ++ int m, r; ++ ++ for (m = 0; m < hw->num_modes; m++) { ++ mode = &hw->modes[m]; ++ for (r = 0; r < mode->num_rates; r++) { ++ rate = &mode->rates[r]; ++ rate->rate_inv = CHAN_UTIL_RATE_LCM / rate->rate; ++ } ++ } ++} ++ ++ ++struct net_device *ieee80211_alloc_hw(size_t priv_data_len, ++ void (*setup)(struct net_device *)) ++{ ++ struct net_device *dev, *apdev, *mdev; ++ struct ieee80211_local *local; ++ struct ieee80211_sub_if_data *sdata; ++ int alloc_size; ++ ++ /* Ensure 32-bit alignment of our private data and hw private data. ++ * Each net_device is followed by a sub_if_data which which is used ++ * for wds/vlan information; it is aligned as well. ++ * ++ * Sample memory map looks something like: ++ * ++ * 0000 ***************** ++ * * net_dev * ++ * 015c ***************** ++ * * sub_if * ++ * 017c ***************** ++ * * local * ++ * 0b84 ***************** ++ * * hw_priv * ++ * 1664 ***************** ++ * * ap net_dev * ++ * 17c0 ***************** ++ * * sub_if * ++ * ***************** ++ * * master net_dev* ++ * ***************** ++ * * sub_if * ++ * ***************** ++ */ ++ alloc_size = sizeof(struct net_device) + ++ sizeof(struct ieee80211_sub_if_data) + 3 + ++ sizeof(struct ieee80211_local) + 3 + ++ priv_data_len + 3 + ++ sizeof(struct net_device) + 3 + ++ sizeof(struct ieee80211_sub_if_data) + 3 + ++ sizeof(struct net_device) + 3 + ++ sizeof(struct ieee80211_sub_if_data) + 3 + ++ 4096; ++ mdev = (struct net_device *) kzalloc(alloc_size, GFP_KERNEL); ++ if (mdev == NULL) ++ return NULL; ++ ++ mdev->priv = (struct net_device *) ++ ((char *) mdev + ++ ((sizeof(struct net_device) + 3) & ~3) + ++ ((sizeof(struct ieee80211_sub_if_data) + 3) & ~3)); ++ local = mdev->priv; ++ local->hw_priv = (void *) ++ ((char *) local + ((sizeof(struct ieee80211_local) + 3) & ~3)); ++ apdev = (struct net_device *) ++ ((char *) local->hw_priv + ((priv_data_len + 3) & ~3)); ++ dev = (struct net_device *) ++ ((char *) apdev + ++ ((sizeof(struct net_device) + 3) & ~3) + ++ ((sizeof(struct ieee80211_sub_if_data) + 3) & ~3)); ++ dev->priv = local; ++ ++ ether_setup(dev); ++ memcpy(dev->name, "wlan%d", 7); ++ ++ dev->hard_start_xmit = ieee80211_subif_start_xmit; ++ dev->wireless_handlers = ++ (struct iw_handler_def *) &ieee80211_iw_handler_def; ++ dev->do_ioctl = ieee80211_ioctl; ++ dev->change_mtu = ieee80211_change_mtu; ++ dev->tx_timeout = ieee80211_tx_timeout; ++ dev->get_stats = ieee80211_get_stats; ++ dev->open = ieee80211_open; ++ dev->stop = ieee80211_stop; ++ dev->tx_queue_len = 0; ++ dev->set_mac_address = ieee80211_set_mac_address; ++ ++ local->dev_index = -1; ++ local->wdev = dev; ++ local->mdev = mdev; ++ local->rx_handlers = ieee80211_rx_handlers; ++ local->tx_handlers = ieee80211_tx_handlers; ++ ++ local->bridge_packets = 1; ++ ++ local->rts_threshold = IEEE80211_MAX_RTS_THRESHOLD; ++ local->fragmentation_threshold = IEEE80211_MAX_FRAG_THRESHOLD; ++ local->short_retry_limit = 7; ++ local->long_retry_limit = 4; ++ local->conf.calib_int = 60; ++ local->rate_ctrl_num_up = RATE_CONTROL_NUM_UP; ++ local->rate_ctrl_num_down = RATE_CONTROL_NUM_DOWN; ++ local->conf.bss_count = 1; ++ memset(local->conf.bssid_mask, 0xff, ETH_ALEN); ++ local->bss_devs = kmalloc(sizeof(struct net_device *), GFP_KERNEL); ++ if (local->bss_devs == NULL) ++ goto fail; ++ local->bss_devs[0] = local->wdev; ++ local->bss_dev_count = 1; ++ local->sta_devs = kmalloc(sizeof(struct net_device *), GFP_KERNEL); ++ if (local->sta_devs == NULL) ++ goto fail; ++ local->sta_devs[0] = NULL; ++ ++ local->scan.in_scan = 0; ++ local->hw_modes = (unsigned int) -1; ++ ++ init_timer(&local->scan.timer); /* clear it out */ ++ ++ spin_lock_init(&local->generic_lock); ++ init_timer(&local->rate_limit_timer); ++ local->rate_limit_timer.function = ieee80211_rate_limit; ++ local->rate_limit_timer.data = (unsigned long) local; ++ init_timer(&local->stat_timer); ++ local->stat_timer.function = ieee80211_stat_refresh; ++ local->stat_timer.data = (unsigned long) local; ++ ieee80211_rx_bss_list_init(dev); ++ ++ sta_info_init(local); ++ ++ ieee80211_if_init(dev); ++ ++ sdata = IEEE80211_DEV_TO_SUB_IF(dev); ++ sdata->dev = dev; ++ sdata->master = mdev; ++ sdata->local = local; ++ ieee80211_if_sdata_init(sdata); ++ ieee80211_if_ap_init(sdata); ++ list_add_tail(&sdata->list, &local->sub_if_list); ++ ++ if (strlen(dev->name) + 2 >= sizeof(dev->name)) ++ goto fail; ++ ++ apdev = (struct net_device *) ++ ((char *) local->hw_priv + ((priv_data_len + 3) & ~3)); ++ local->apdev = apdev; ++ ether_setup(apdev); ++ apdev->priv = local; ++ apdev->hard_start_xmit = ieee80211_mgmt_start_xmit; ++ apdev->change_mtu = ieee80211_change_mtu_apdev; ++ apdev->get_stats = ieee80211_get_stats; ++ apdev->open = ieee80211_open; ++ apdev->stop = ieee80211_stop; ++ apdev->type = ARPHRD_IEEE80211_PRISM; ++ apdev->hard_header_parse = header_parse_80211; ++ apdev->tx_queue_len = 0; ++ sprintf(apdev->name, "%sap", dev->name); ++ ++ sdata = IEEE80211_DEV_TO_SUB_IF(apdev); ++ sdata->type = IEEE80211_SUB_IF_TYPE_MGMT; ++ sdata->dev = apdev; ++ sdata->master = mdev; ++ sdata->local = local; ++ list_add_tail(&sdata->list, &local->sub_if_list); ++ ++ ether_setup(mdev); ++ mdev->hard_start_xmit = ieee80211_master_start_xmit; ++ mdev->wireless_handlers = ++ (struct iw_handler_def *) &ieee80211_iw_handler_def; ++ mdev->do_ioctl = ieee80211_ioctl; ++ mdev->change_mtu = ieee80211_change_mtu; ++ mdev->tx_timeout = ieee80211_tx_timeout; ++ mdev->get_stats = ieee80211_get_stats; ++ mdev->open = ieee80211_open; ++ mdev->stop = ieee80211_stop; ++ mdev->type = ARPHRD_IEEE80211; ++ mdev->hard_header_parse = header_parse_80211; ++ sprintf(mdev->name, "%s.11", dev->name); ++ ++ sdata = IEEE80211_DEV_TO_SUB_IF(mdev); ++ sdata->type = IEEE80211_SUB_IF_TYPE_AP; ++ sdata->dev = mdev; ++ sdata->master = mdev; ++ sdata->local = local; ++ list_add_tail(&sdata->list, &local->sub_if_list); ++ ++ tasklet_init(&local->tasklet, ++ ieee80211_tasklet_handler, ++ (unsigned long) local); ++ skb_queue_head_init(&local->skb_queue); ++ skb_queue_head_init(&local->skb_queue_unreliable); ++ ++ if (setup) ++ setup(mdev); ++ ++ return mdev; ++ ++ fail: ++ ieee80211_free_hw(mdev); ++ return NULL; ++} ++ ++ ++int ieee80211_register_hw(struct net_device *dev, struct ieee80211_hw *hw) ++{ ++ struct ieee80211_local *local = dev->priv; ++ int result; ++ ++ if (!hw) ++ return -1; ++ ++ if (hw->version != IEEE80211_VERSION) { ++ printk("ieee80211_register_hw - version mismatch: 80211.o " ++ "version %d, low-level driver version %d\n", ++ IEEE80211_VERSION, hw->version); ++ return -1; ++ } ++ ++ result = ieee80211_dev_alloc_index(local); ++ if (result < 0) ++ return -1; ++ ++ local->class_dev.dev = dev->class_dev.dev; ++ result = ieee80211_register_sysfs(local); ++ if (result < 0) ++ goto fail_sysfs; ++ ++ local->conf.mode = IW_MODE_MASTER; ++ local->conf.beacon_int = 1000; ++ ++ ieee80211_update_hw(dev, hw); /* Don't care about the result. */ ++ ++ sta_info_start(local); ++ ++ result = register_netdev(local->wdev); ++ if (result < 0) ++ goto fail_1st_dev; ++ ++ result = register_netdev(local->apdev); ++ if (result < 0) ++ goto fail_2nd_dev; ++ ++ if (hw->fraglist) ++ dev->features |= NETIF_F_FRAGLIST; ++ result = register_netdev(dev); ++ if (result < 0) ++ goto fail_3rd_dev; ++ ++ if (rate_control_initialize(local) < 0) { ++ printk(KERN_DEBUG "%s: Failed to initialize rate control " ++ "algorithm\n", dev->name); ++ goto fail_rate; ++ } ++ ++ /* TODO: add rtnl locking around device creation and qdisc install */ ++ ieee80211_install_qdisc(dev); ++ ++ ieee80211_wep_init(local); ++ ieee80211_proc_init_interface(local); ++ return 0; ++ ++fail_rate: ++ unregister_netdev(dev); ++fail_3rd_dev: ++ unregister_netdev(local->apdev); ++fail_2nd_dev: ++ unregister_netdev(local->wdev); ++fail_1st_dev: ++ sta_info_stop(local); ++ ieee80211_unregister_sysfs(local); ++fail_sysfs: ++ ieee80211_dev_free_index(local); ++ return result; ++} ++ ++int ieee80211_update_hw(struct net_device *dev, struct ieee80211_hw *hw) ++{ ++ struct ieee80211_local *local = dev->priv; ++ ++ local->hw = hw; ++ ++ /* Backwards compatibility for low-level drivers that do not set number ++ * of TX queues. */ ++ if (hw->queues == 0) ++ hw->queues = 1; ++ ++ memcpy(local->apdev->dev_addr, dev->dev_addr, ETH_ALEN); ++ local->apdev->base_addr = dev->base_addr; ++ local->apdev->irq = dev->irq; ++ local->apdev->mem_start = dev->mem_start; ++ local->apdev->mem_end = dev->mem_end; ++ ++ memcpy(local->wdev->dev_addr, dev->dev_addr, ETH_ALEN); ++ local->wdev->base_addr = dev->base_addr; ++ local->wdev->irq = dev->irq; ++ local->wdev->mem_start = dev->mem_start; ++ local->wdev->mem_end = dev->mem_end; ++ ++ if (!hw->modes || !hw->modes->channels || !hw->modes->rates || ++ !hw->modes->num_channels || !hw->modes->num_rates) ++ return -1; ++ ++ ieee80211_precalc_rates(hw); ++ local->conf.phymode = hw->modes[0].mode; ++ local->curr_rates = hw->modes[0].rates; ++ local->num_curr_rates = hw->modes[0].num_rates; ++ ieee80211_prepare_rates(dev); ++ ++ local->conf.freq = local->hw->modes[0].channels[0].freq; ++ local->conf.channel = local->hw->modes[0].channels[0].chan; ++ local->conf.channel_val = local->hw->modes[0].channels[0].val; ++ /* FIXME: Invoke config to allow driver to set the channel. */ ++ ++ return 0; ++} ++ ++ ++void ieee80211_unregister_hw(struct net_device *dev) ++{ ++ struct ieee80211_local *local = dev->priv; ++ struct list_head *ptr, *n; ++ int i; ++ ++ tasklet_disable(&local->tasklet); ++ /* TODO: skb_queue should be empty here, no need to do anything? */ ++ ++ if (local->rate_limit) ++ del_timer_sync(&local->rate_limit_timer); ++ if (local->stat_time) ++ del_timer_sync(&local->stat_timer); ++ if (local->scan_timer.data) ++ del_timer_sync(&local->scan_timer); ++ ieee80211_rx_bss_list_deinit(dev); ++ ++ list_for_each_safe(ptr, n, &local->sub_if_list) { ++ struct ieee80211_sub_if_data *sdata = ++ list_entry(ptr, struct ieee80211_sub_if_data, list); ++ ieee80211_if_del(local, sdata, 0); ++ } ++ ++ sta_info_stop(local); ++ ieee80211_unregister_sysfs(local); ++ ++ for (i = 0; i < IEEE80211_FRAGMENT_MAX; i++) ++ if (local->fragments[i].skb) ++ dev_kfree_skb(local->fragments[i].skb); ++ ++ for (i = 0; i < NUM_IEEE80211_MODES; i++) { ++ kfree(local->supp_rates[i]); ++ kfree(local->basic_rates[i]); ++ } ++ ++ kfree(local->conf.ssid); ++ kfree(local->conf.generic_elem); ++ ++ ieee80211_proc_deinit_interface(local); ++ ++ if (skb_queue_len(&local->skb_queue) ++ || skb_queue_len(&local->skb_queue_unreliable)) ++ printk(KERN_WARNING "%s: skb_queue not empty", dev->name); ++ skb_queue_purge(&local->skb_queue); ++ skb_queue_purge(&local->skb_queue_unreliable); ++ ++ rate_control_free(local); ++ ieee80211_dev_free_index(local); ++} ++ ++void ieee80211_free_hw(struct net_device *dev) ++{ ++ struct ieee80211_local *local = dev->priv; ++ ++ kfree(local->sta_devs); ++ kfree(local->bss_devs); ++ kfree(dev); ++} ++ ++ ++/* Perform netif operations on all configured interfaces */ ++int ieee80211_netif_oper(struct net_device *sdev, Netif_Oper op) ++{ ++ struct ieee80211_local *local = sdev->priv; ++ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(sdev); ++ struct net_device *dev = sdata->master; ++ ++ switch (op) { ++ case NETIF_ATTACH: ++ netif_device_attach(dev); ++ break; ++ case NETIF_DETACH: ++ netif_device_detach(dev); ++ break; ++ case NETIF_START: ++ netif_start_queue(dev); ++ break; ++ case NETIF_STOP: ++ break; ++ case NETIF_WAKE: ++ if (local->scan.in_scan == 0) { ++ netif_wake_queue(dev); ++#if 1 ++ if (/* FIX: 802.11 qdisc in use */ 1) ++ __netif_schedule(dev); ++#endif ++ } ++ break; ++ case NETIF_IS_STOPPED: ++ if (netif_queue_stopped(dev)) ++ return 1; ++ break; ++ case NETIF_UPDATE_TX_START: ++ dev->trans_start = jiffies; ++ break; ++ } ++ ++ return 0; ++} ++ ++ ++void * ieee80211_dev_hw_data(struct net_device *dev) ++{ ++ struct ieee80211_local *local = dev->priv; ++ return local->hw_priv; ++} ++ ++ ++void * ieee80211_dev_stats(struct net_device *dev) ++{ ++ struct ieee80211_sub_if_data *sdata; ++ sdata = IEEE80211_DEV_TO_SUB_IF(dev); ++ return &(sdata->stats); ++} ++ ++ ++int ieee80211_rate_control_register(struct rate_control_ops *ops) ++{ ++ struct rate_control_algs *alg; ++ ++ alg = kmalloc(sizeof(*alg), GFP_KERNEL); ++ if (alg == NULL) { ++ return -1; ++ } ++ memset(alg, 0, sizeof(*alg)); ++ alg->next = ieee80211_rate_ctrl_algs; ++ alg->ops = ops; ++ ieee80211_rate_ctrl_algs = alg; ++ ++ return 0; ++} ++ ++ ++void ieee80211_rate_control_unregister(struct rate_control_ops *ops) ++{ ++ struct rate_control_algs *alg, *prev; ++ ++ prev = NULL; ++ alg = ieee80211_rate_ctrl_algs; ++ while (alg) { ++ if (alg->ops == ops) { ++ if (prev) ++ prev->next = alg->next; ++ else ++ ieee80211_rate_ctrl_algs = alg->next; ++ kfree(alg); ++ break; ++ } ++ prev = alg; ++ alg = alg->next; ++ } ++} ++ ++ ++static int rate_control_initialize(struct ieee80211_local *local) ++{ ++ struct rate_control_algs *algs; ++ ++ if (!ieee80211_rate_ctrl_algs) ++ request_module("ieee80211_rate_control"); ++ ++ for (algs = ieee80211_rate_ctrl_algs; algs; algs = algs->next) { ++ local->rate_ctrl = algs->ops; ++ local->rate_ctrl_priv = rate_control_alloc(local); ++ if (local->rate_ctrl_priv) { ++ printk(KERN_DEBUG "%s: Selected rate control " ++ "algorithm '%s'\n", local->wdev->name, ++ local->rate_ctrl->name); ++ return 0; ++ } ++ } ++ ++ printk(KERN_WARNING "%s: Failed to select rate control algorithm\n", ++ local->wdev->name); ++ return -1; ++} ++ ++ ++static int __init ieee80211_init(void) ++{ ++ struct sk_buff *skb; ++ int ret; ++ ++ if (sizeof(struct ieee80211_tx_packet_data) > (sizeof(skb->cb))) { ++ printk("80211: ieee80211_tx_packet_data is bigger " ++ "than the skb->cb (%d > %d)\n", ++ (int) sizeof(struct ieee80211_tx_packet_data), ++ (int) sizeof(skb->cb)); ++ return -EINVAL; ++ } ++ if ((ret = ieee80211_sysfs_init())) { ++ printk(KERN_WARNING "ieee80211_init: sysfs initialization " ++ "failed\n"); ++ return ret; ++ } ++ ++ ieee80211_proc_init(); ++ { ++ ret = ieee80211_wme_register(); ++ if (ret) { ++ printk(KERN_DEBUG "ieee80211_init: failed to " ++ "initialize WME (err=%d)\n", ret); ++ ieee80211_proc_deinit(); ++ return ret; ++ } ++ } ++ ++ return 0; ++} ++ ++ ++static void __exit ieee80211_exit(void) ++{ ++ ieee80211_wme_unregister(); ++ ieee80211_proc_deinit(); ++ ieee80211_sysfs_deinit(); ++} ++ ++ ++EXPORT_SYMBOL(ieee80211_alloc_hw); ++EXPORT_SYMBOL(ieee80211_register_hw); ++EXPORT_SYMBOL(ieee80211_update_hw); ++EXPORT_SYMBOL(ieee80211_unregister_hw); ++EXPORT_SYMBOL(ieee80211_free_hw); ++EXPORT_SYMBOL(__ieee80211_rx); ++EXPORT_SYMBOL(ieee80211_tx_status); ++EXPORT_SYMBOL(ieee80211_beacon_get); ++EXPORT_SYMBOL(ieee80211_get_buffered_bc); ++EXPORT_SYMBOL(ieee80211_netif_oper); ++EXPORT_SYMBOL(ieee80211_dev_hw_data); ++EXPORT_SYMBOL(ieee80211_dev_stats); ++EXPORT_SYMBOL(ieee80211_get_hw_conf); ++EXPORT_SYMBOL(ieee80211_set_aid_for_sta); ++EXPORT_SYMBOL(ieee80211_rx_irqsafe); ++EXPORT_SYMBOL(ieee80211_tx_status_irqsafe); ++EXPORT_SYMBOL(ieee80211_get_hdrlen); ++EXPORT_SYMBOL(ieee80211_get_hdrlen_from_skb); ++EXPORT_SYMBOL(ieee80211_rate_control_register); ++EXPORT_SYMBOL(ieee80211_rate_control_unregister); ++EXPORT_SYMBOL(sta_info_get); ++EXPORT_SYMBOL(sta_info_release); ++EXPORT_SYMBOL(ieee80211_radar_status); ++ ++module_init(ieee80211_init); ++module_exit(ieee80211_exit); ++ ++MODULE_DESCRIPTION("IEEE 802.11 subsystem"); ++MODULE_LICENSE("GPL"); +diff -Nur linux-2.6.16/net/d80211/ieee80211_dev.c linux-2.6.16-bcm43xx/net/d80211/ieee80211_dev.c +--- linux-2.6.16/net/d80211/ieee80211_dev.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.16-bcm43xx/net/d80211/ieee80211_dev.c 2006-03-28 22:16:14.000000000 +0200 +@@ -0,0 +1,85 @@ ++/* ++ * Copyright (c) 2006 Jiri Benc <jbenc@suse.cz> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/interrupt.h> ++#include <linux/if.h> ++#include <linux/if_ether.h> ++#include <linux/netdevice.h> ++#include <net/d80211.h> ++#include "ieee80211_i.h" ++ ++struct ieee80211_dev_list { ++ struct list_head list; ++ int dev_index; ++ struct ieee80211_local *local; ++}; ++ ++static LIST_HEAD(dev_list); ++static DEFINE_SPINLOCK(dev_list_lock); ++ ++ ++/* Caller must hold dev_list_lock */ ++static struct ieee80211_dev_list *__ieee80211_dev_find(int index) ++{ ++ struct ieee80211_dev_list *dev_item; ++ ++ list_for_each_entry(dev_item, &dev_list, list) { ++ if (dev_item->dev_index == index) ++ return dev_item; ++ } ++ return NULL; ++} ++ ++int ieee80211_dev_alloc_index(struct ieee80211_local *local) ++{ ++ struct list_head *i; ++ struct ieee80211_dev_list *dev_item, *new; ++ int index = 0; ++ ++ new = kmalloc(sizeof(struct ieee80211_dev_list), GFP_KERNEL); ++ if (!new) ++ return -ENOMEM; ++ new->local = local; ++ spin_lock(&dev_list_lock); ++ list_for_each(i, &dev_list) { ++ dev_item = list_entry(i, struct ieee80211_dev_list, list); ++ if (index < dev_item->dev_index) ++ break; ++ index++; ++ } ++ new->dev_index = index; ++ list_add_tail(&new->list, i); ++ spin_unlock(&dev_list_lock); ++ local->dev_index = index; ++ return index; ++} ++ ++void ieee80211_dev_free_index(struct ieee80211_local *local) ++{ ++ struct ieee80211_dev_list *dev_item; ++ ++ spin_lock(&dev_list_lock); ++ dev_item = __ieee80211_dev_find(local->dev_index); ++ if (dev_item) ++ list_del(&dev_item->list); ++ spin_unlock(&dev_list_lock); ++ if (dev_item) ++ kfree(dev_item); ++ local->dev_index = -1; ++} ++ ++struct ieee80211_local *ieee80211_dev_find(int index) ++{ ++ struct ieee80211_dev_list *dev_item; ++ ++ spin_lock(&dev_list_lock); ++ dev_item = __ieee80211_dev_find(index); ++ spin_unlock(&dev_list_lock); ++ return dev_item ? dev_item->local : NULL; ++} +diff -Nur linux-2.6.16/net/d80211/ieee80211_i.h linux-2.6.16-bcm43xx/net/d80211/ieee80211_i.h +--- linux-2.6.16/net/d80211/ieee80211_i.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.16-bcm43xx/net/d80211/ieee80211_i.h 2006-03-28 22:16:14.000000000 +0200 +@@ -0,0 +1,596 @@ ++/* ++ * Copyright 2002-2005, Instant802 Networks, Inc. ++ * Copyright 2005, Devicescape Software, Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#ifndef IEEE80211_I_H ++#define IEEE80211_I_H ++ ++#include <linux/kernel.h> ++#include <linux/device.h> ++#include <linux/if_ether.h> ++#include <linux/interrupt.h> ++#include <linux/list.h> ++#include <linux/netdevice.h> ++#include <linux/skbuff.h> ++#include "ieee80211_key.h" ++#include "sta_info.h" ++ ++/* ieee80211.o internal definitions, etc. These are not included into ++ * low-level drivers. */ ++ ++#ifndef ETH_P_PAE ++#define ETH_P_PAE 0x888E /* Port Access Entity (IEEE 802.1X) */ ++#endif /* ETH_P_PAE */ ++ ++#define IEEE80211_MAX_SSID_LEN 32 ++ ++struct ieee80211_local; ++ ++#define BIT(x) (1 << (x)) ++ ++#define IEEE80211_ALIGN32_PAD(a) ((4 - ((a) & 3)) & 3) ++ ++ ++/* Maximum number of broadcast/multicast frames to buffer when some of the ++ * associated stations are using power saving. */ ++#define AP_MAX_BC_BUFFER 128 ++ ++/* Maximum number of frames buffered to all STAs, including multicast frames. ++ * Note: increasing this limit increases the potential memory requirement. Each ++ * frame can be up to about 2 kB long. */ ++#define TOTAL_MAX_TX_BUFFER 512 ++ ++ ++#define MAC2STR(a) ((a)[0] & 0xff), ((a)[1] & 0xff), ((a)[2] & 0xff), \ ++ ((a)[3] & 0xff), ((a)[4] & 0xff), ((a)[5] & 0xff) ++#define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x" ++ ++#define MULTICAST_ADDR(a) ((a)[0] & 0x01) ++ ++ ++/* IEEE 802.11 (Ch. 9.5 Defragmentation) requires support for concurrent ++ * reception of at least three fragmented frames. This limit can be increased ++ * by changing this define, at the cost of slower frame reassembly and ++ * increased memory use (about 2 kB of RAM per entry). */ ++#define IEEE80211_FRAGMENT_MAX 4 ++ ++struct ieee80211_fragment_entry { ++ unsigned long first_frag_time; ++ unsigned int seq; ++ unsigned int rx_queue; ++ unsigned int last_frag; ++ struct sk_buff *skb; ++ int ccmp; /* Whether fragments were encrypted with CCMP */ ++ u8 last_pn[6]; /* PN of the last fragment if CCMP was used */ ++}; ++ ++ ++struct ieee80211_sta_bss { ++ struct list_head list; ++ struct ieee80211_sta_bss *hnext; ++ atomic_t users; ++ ++ u8 bssid[ETH_ALEN]; ++ u8 ssid[IEEE80211_MAX_SSID_LEN]; ++ size_t ssid_len; ++ u16 capability; /* host byte order */ ++ int hw_mode; ++ int channel; ++ int freq; ++ int rssi; ++ u8 *wpa_ie; ++ size_t wpa_ie_len; ++ u8 *rsn_ie; ++ size_t rsn_ie_len; ++ u8 *wmm_ie; ++ size_t wmm_ie_len; ++#define IEEE80211_MAX_SUPP_RATES 32 ++ u8 supp_rates[IEEE80211_MAX_SUPP_RATES]; ++ size_t supp_rates_len; ++ int beacon_int; ++ u64 timestamp; ++ ++ int probe_resp; ++ unsigned long last_update; ++ ++}; ++ ++ ++typedef enum { ++ TXRX_CONTINUE, TXRX_DROP, TXRX_QUEUED ++} ieee80211_txrx_result; ++ ++struct ieee80211_txrx_data { ++ struct sk_buff *skb; ++ struct net_device *dev; ++ struct ieee80211_local *local; ++ struct ieee80211_sub_if_data *sdata; ++ struct sta_info *sta; ++ u16 fc, ethertype; ++ struct ieee80211_key *key; ++ unsigned int fragmented:1; /* whether the MSDU was fragmented */ ++ union { ++ struct { ++ struct ieee80211_tx_control *control; ++ int unicast:1; ++ int ps_buffered:1; ++ int short_preamble:1; ++ int probe_last_frag:1; ++ struct ieee80211_rate *rate; ++ /* use this rate (if set) for last fragment; rate can ++ * be set to lower rate for the first fragments, e.g., ++ * when using CTS protection with IEEE 802.11g. */ ++ struct ieee80211_rate *last_frag_rate; ++ int last_frag_rateidx; ++ int mgmt_interface; ++ ++ /* Extra fragments (in addition to the first fragment ++ * in skb) */ ++ int num_extra_frag; ++ struct sk_buff **extra_frag; ++ } tx; ++ struct { ++ struct ieee80211_rx_status *status; ++ int sent_ps_buffered; ++ int queue; ++ } rx; ++ } u; ++#ifdef CONFIG_HOSTAPD_WPA_TESTING ++ int wpa_test; ++#endif /* CONFIG_HOSTAPD_WPA_TESTING */ ++}; ++ ++struct ieee80211_passive_scan { ++ unsigned int in_scan:1; /* this must be cleared before calling ++ * netif_oper(WAKEUP) */ ++ unsigned int our_mode_only:1; /* only scan our physical mode a/b/g/etc ++ */ ++ int interval; /* time in seconds between scans */ ++ int time; /* time in microseconds to scan for */ ++ int channel; /* channel to be scanned */ ++ int tries; ++ ++ int mode_idx; ++ int chan_idx; ++ ++ int freq; ++ int rx_packets; ++ int rx_beacon; ++ int txrx_count; ++ ++ struct timer_list timer; ++ ++ struct sk_buff *skb; /* skb to transmit before changing channels, ++ * maybe null for none */ ++ struct ieee80211_tx_control tx_control; ++ ++ unsigned int num_scans; ++}; ++ ++typedef ieee80211_txrx_result (*ieee80211_tx_handler) ++(struct ieee80211_txrx_data *tx); ++ ++typedef ieee80211_txrx_result (*ieee80211_rx_handler) ++(struct ieee80211_txrx_data *rx); ++ ++struct ieee80211_if_ap { ++ u8 *beacon_head, *beacon_tail; ++ int beacon_head_len, beacon_tail_len; ++ ++ /* TODO: sta_aid could be replaced by 2008-bit large bitfield of ++ * that could be used in TIM element generation. This would also ++ * make TIM element generation a bit faster. */ ++ /* AID mapping to station data. NULL, if AID is free. AID is in the ++ * range 1..2007 and sta_aid[i] corresponds to AID i+1. */ ++ struct sta_info *sta_aid[MAX_AID_TABLE_SIZE]; ++ int max_aid; /* largest aid currently in use */ ++ atomic_t num_sta_ps; /* number of stations in PS mode */ ++ struct sk_buff_head ps_bc_buf; ++ int dtim_period, dtim_count; ++ int force_unicast_rateidx; /* forced TX rateidx for unicast frames */ ++ int max_ratectrl_rateidx; /* max TX rateidx for rate control */ ++ int num_beacons; /* number of TXed beacon frames for this BSS */ ++}; ++ ++struct ieee80211_if_wds { ++ u8 remote_addr[ETH_ALEN]; ++ struct sta_info *sta; ++}; ++ ++struct ieee80211_if_vlan { ++ u8 id; ++}; ++ ++struct ieee80211_if_sta { ++ enum { ++ IEEE80211_DISABLED, IEEE80211_AUTHENTICATE, ++ IEEE80211_ASSOCIATE, IEEE80211_ASSOCIATED, ++ IEEE80211_IBSS_SEARCH, IEEE80211_IBSS_JOINED ++ } state; ++ struct timer_list timer; ++ u8 bssid[ETH_ALEN], prev_bssid[ETH_ALEN]; ++ u8 ssid[IEEE80211_MAX_SSID_LEN]; ++ size_t ssid_len; ++ u16 aid; ++ u16 ap_capab, capab; ++ u8 *extra_ie; /* to be added to the end of AssocReq */ ++ size_t extra_ie_len; ++ ++ /* The last AssocReq/Resp IEs */ ++ u8 *assocreq_ies, *assocresp_ies; ++ size_t assocreq_ies_len, assocresp_ies_len; ++ ++ int auth_tries, assoc_tries; ++ ++ int ssid_set:1; ++ int bssid_set:1; ++ int prev_bssid_set:1; ++ int authenticated:1; ++ int associated:1; ++ int probereq_poll:1; ++ int use_protection:1; ++ int create_ibss:1; ++ int mixed_cell:1; ++ int wmm_enabled:1; ++ ++ int key_mgmt; ++ unsigned long last_probe; ++ ++#define IEEE80211_AUTH_ALG_OPEN BIT(0) ++#define IEEE80211_AUTH_ALG_SHARED_KEY BIT(1) ++#define IEEE80211_AUTH_ALG_LEAP BIT(2) ++ unsigned int auth_algs; /* bitfield of allowed auth algs */ ++ int auth_alg; /* currently used IEEE 802.11 authentication algorithm */ ++ int auth_transaction; ++ ++ unsigned long ibss_join_req; ++ struct sk_buff *probe_resp; /* ProbeResp template for IBSS */ ++ u32 supp_rates_bits; ++ ++ int wmm_last_param_set; ++}; ++ ++ ++#define IEEE80211_SUB_IF_TYPE_AP 0x00000000 ++#define IEEE80211_SUB_IF_TYPE_MGMT 0x00000001 ++#define IEEE80211_SUB_IF_TYPE_STA 0x00000002 ++#define IEEE80211_SUB_IF_TYPE_WDS 0x5A580211 ++#define IEEE80211_SUB_IF_TYPE_VLAN 0x00080211 ++ ++struct ieee80211_sub_if_data { ++ struct list_head list; ++ unsigned int type; ++ ++ struct net_device *dev; ++ struct net_device *master; ++ struct ieee80211_local *local; ++ ++ struct net_device_stats stats; ++ int drop_unencrypted; ++ int eapol; /* 0 = process EAPOL frames as normal data frames, ++ * 1 = send EAPOL frames through wlan#ap to hostapd ++ * (default) */ ++ int ieee802_1x; /* IEEE 802.1X PAE - drop packet to/from unauthorized ++ * port */ ++ ++#define NUM_DEFAULT_KEYS 4 ++ struct ieee80211_key *keys[NUM_DEFAULT_KEYS]; ++ struct ieee80211_key *default_key; ++ ++ struct ieee80211_if_ap *bss; /* BSS that this device belongs to */ ++ ++ union { ++ struct ieee80211_if_ap ap; ++ struct ieee80211_if_wds wds; ++ struct ieee80211_if_vlan vlan; ++ struct ieee80211_if_sta sta; ++ } u; ++ int channel_use; ++ int channel_use_raw; ++}; ++ ++#define IEEE80211_DEV_TO_SUB_IF(dev) ((struct ieee80211_sub_if_data *) \ ++ ((char *)(dev) + ((sizeof(struct net_device) + 3) & ~3))) ++#define IEEE80211_SUB_IF_TO_DEV(sub_if) ((struct net_device *) \ ++ ((char *)(sub_if) - ((sizeof(struct net_device) + 3) & ~3))) ++ ++ ++struct ieee80211_local { ++ struct ieee80211_hw *hw; ++ void *hw_priv; ++ struct net_device *mdev; /* wlan#.11 - "master" 802.11 device */ ++ struct net_device *wdev; /* wlan# - default Ethernet (data) devide */ ++ struct net_device *apdev; /* wlan#ap - management frames (hostapd) */ ++ int open_count; ++ struct ieee80211_conf conf; ++ ++ int dev_index; ++ struct class_device class_dev; ++ ++ /* Tasklet and skb queue to process calls from IRQ mode. All frames ++ * added to skb_queue will be processed, but frames in ++ * skb_queue_unreliable may be dropped if the total length of these ++ * queues increases over the limit. */ ++#define IEEE80211_IRQSAFE_QUEUE_LIMIT 128 ++ struct tasklet_struct tasklet; ++ struct sk_buff_head skb_queue; ++ struct sk_buff_head skb_queue_unreliable; ++ enum { ++ ieee80211_rx_msg = 1, ++ ieee80211_tx_status_msg = 2 ++ } ieee80211_msg_enum; ++ ++ spinlock_t generic_lock; ++ /* Station data structures */ ++ spinlock_t sta_lock; /* mutex for STA data structures */ ++ int num_sta; /* number of stations in sta_list */ ++ struct list_head sta_list; ++ struct sta_info *sta_hash[STA_HASH_SIZE]; ++ struct timer_list sta_cleanup; ++ ++ /* Current rate table. This is a pointer to hw->modes structure. */ ++ struct ieee80211_rate *curr_rates; ++ int num_curr_rates; ++ ++ void *rate_ctrl_priv; ++ struct rate_control_ops *rate_ctrl; ++ ++ int next_mode; /* MODE_IEEE80211* ++ * The mode preference for next channel change. This is ++ * used to select .11g vs. .11b channels (or 4.9 GHz vs. ++ * .11a) when the channel number is not unique. */ ++ ++ /* Supported and basic rate filters for different modes. These are ++ * pointers to -1 terminated lists and rates in 100 kbps units. */ ++ int *supp_rates[NUM_IEEE80211_MODES]; ++ int *basic_rates[NUM_IEEE80211_MODES]; ++ ++ int rts_threshold; ++ int cts_protect_erp_frames; ++ int fragmentation_threshold; ++ int short_retry_limit; /* dot11ShortRetryLimit */ ++ int long_retry_limit; /* dot11LongRetryLimit */ ++ int short_preamble; /* use short preamble with IEEE 802.11b */ ++ ++ u32 wep_iv; ++ int key_tx_rx_threshold; /* number of times any key can be used in TX ++ * or RX before generating a rekey ++ * notification; 0 = notification disabled. */ ++ ++ /* Fragment table for host-based reassembly */ ++ struct ieee80211_fragment_entry fragments[IEEE80211_FRAGMENT_MAX]; ++ unsigned int fragment_next; ++ ++ int bridge_packets; /* bridge packets between associated stations and ++ * deliver multicast frames both back to wireless ++ * media and to the local net stack */ ++ ++ struct ieee80211_passive_scan scan; ++ ++ ++ ieee80211_rx_handler *rx_handlers; ++ ieee80211_tx_handler *tx_handlers; ++ ++ spinlock_t sub_if_lock; /* mutex for STA data structures */ ++ struct list_head sub_if_list; ++ struct net_device **bss_devs; /* pointer to IF_TYPE_AP devices for ++ * quick access to BSS data */ ++ int bss_dev_count; /* number of used entries in bss_devs; note: the ++ * total size of bss_devs array is stored in ++ * conf.bss_count */ ++ struct net_device **sta_devs; /* pointer to IF_TYPE_STA devices */ ++ int sta_dev_count; /* number of used entries in sta_devs */ ++ int sta_scanning; ++ int scan_hw_mode_idx; ++ int scan_channel_idx; ++ enum { SCAN_SET_CHANNEL, SCAN_SEND_PROBE } scan_state; ++ unsigned long last_scan_completed; ++ struct timer_list scan_timer; ++ int scan_oper_channel; ++ int scan_oper_channel_val; ++ int scan_oper_power_level; ++ int scan_oper_freq; ++ int scan_oper_phymode; ++ int scan_oper_antenna_max; ++ u8 scan_ssid[IEEE80211_MAX_SSID_LEN]; ++ size_t scan_ssid_len; ++ int scan_skip_11b; ++ struct list_head sta_bss_list; ++ struct ieee80211_sta_bss *sta_bss_hash[STA_HASH_SIZE]; ++ spinlock_t sta_bss_lock; ++#define IEEE80211_SCAN_MATCH_SSID BIT(0) ++#define IEEE80211_SCAN_WPA_ONLY BIT(1) ++#define IEEE80211_SCAN_EXTRA_INFO BIT(2) ++ int scan_flags; ++ ++#ifdef CONFIG_HOSTAPD_WPA_TESTING ++ u32 wpa_trigger; ++#endif /* CONFIG_HOSTAPD_WPA_TESTING */ ++ /* SNMP counters */ ++ /* dot11CountersTable */ ++ u32 dot11TransmittedFragmentCount; ++ u32 dot11MulticastTransmittedFrameCount; ++ u32 dot11FailedCount; ++ u32 dot11RetryCount; ++ u32 dot11MultipleRetryCount; ++ u32 dot11FrameDuplicateCount; ++ u32 dot11ReceivedFragmentCount; ++ u32 dot11MulticastReceivedFrameCount; ++ u32 dot11TransmittedFrameCount; ++ u32 dot11WEPUndecryptableCount; ++ ++ int tx_led_counter; ++ ++ u32 channel_use; ++ u32 channel_use_raw; ++ u32 stat_time; ++ struct timer_list stat_timer; ++ ++ u8 bssid[ETH_ALEN]; /* BSSID for STA modes (Adhoc/Managed) */ ++ struct timer_list rate_limit_timer; ++ u32 rate_limit; ++ u32 rate_limit_burst; ++ u32 rate_limit_bucket; ++ struct proc_dir_entry *proc, *proc_sta, *proc_iface; ++ ++ struct work_struct sta_proc_add; ++ ++ enum { ++ STA_ANTENNA_SEL_AUTO = 0, ++ STA_ANTENNA_SEL_SW_CTRL = 1, ++ STA_ANTENNA_SEL_SW_CTRL_DEBUG = 2 ++ } sta_antenna_sel; ++ ++ int rate_ctrl_num_up, rate_ctrl_num_down; ++ ++#ifdef CONFIG_D80211_DEBUG_COUNTERS ++ /* TX/RX handler statistics */ ++ unsigned int tx_handlers_drop; ++ unsigned int tx_handlers_queued; ++ unsigned int tx_handlers_drop_unencrypted; ++ unsigned int tx_handlers_drop_fragment; ++ unsigned int tx_handlers_drop_wep; ++ unsigned int tx_handlers_drop_rate_limit; ++ unsigned int tx_handlers_drop_not_assoc; ++ unsigned int tx_handlers_drop_unauth_port; ++ unsigned int rx_handlers_drop; ++ unsigned int rx_handlers_queued; ++ unsigned int rx_handlers_drop_nullfunc; ++ unsigned int rx_handlers_drop_defrag; ++ unsigned int rx_handlers_drop_short; ++ unsigned int rx_handlers_drop_passive_scan; ++ unsigned int tx_expand_skb_head; ++ unsigned int tx_expand_skb_head_cloned; ++ unsigned int rx_expand_skb_head; ++ unsigned int rx_expand_skb_head2; ++ unsigned int rx_handlers_fragments; ++ unsigned int tx_status_drop; ++ unsigned int wme_rx_queue[NUM_RX_DATA_QUEUES]; ++ unsigned int wme_tx_queue[NUM_RX_DATA_QUEUES]; ++#define I802_DEBUG_INC(c) (c)++ ++#else /* CONFIG_D80211_DEBUG_COUNTERS */ ++#define I802_DEBUG_INC(c) do { } while (0) ++#endif /* CONFIG_D80211_DEBUG_COUNTERS */ ++ ++ ++ int default_wep_only; /* only default WEP keys are used with this ++ * interface; this is used to decide when hwaccel ++ * can be used with default keys */ ++ int total_ps_buffered; /* total number of all buffered unicast and ++ * multicast packets for power saving stations ++ */ ++ int allow_broadcast_always; /* whether to allow TX of broadcast frames ++ * even when there are no associated STAs ++ */ ++ ++ int wifi_wme_noack_test; ++ unsigned int wmm_acm; /* bit field of ACM bits (BIT(802.1D tag)) */ ++ ++ unsigned int hw_modes; /* bitfield of allowed hardware modes; ++ * (1 << MODE_*) */ ++}; ++ ++ ++/* ieee80211.c */ ++int ieee80211_hw_config(struct net_device *dev); ++struct ieee80211_key_conf * ++ieee80211_key_data2conf(struct ieee80211_local *local, ++ struct ieee80211_key *data); ++void ieee80211_rx_mgmt(struct net_device *dev, struct sk_buff *skb, ++ struct ieee80211_rx_status *status, u32 msg_type); ++void ieee80211_prepare_rates(struct net_device *dev); ++void ieee80211_tx_set_iswep(struct ieee80211_txrx_data *tx); ++int ieee80211_if_add_wds(struct net_device *dev, const char *name, ++ struct ieee80211_if_wds *wds, int locked); ++int ieee80211_if_add_vlan(struct net_device *dev, const char *name, ++ struct ieee80211_if_vlan *vlan, int locked); ++int ieee80211_if_add_ap(struct net_device *dev, const char *name, u8 *bssid, ++ int locked); ++ ++int ieee80211_if_remove_wds(struct net_device *dev, const char *name, int locked); ++int ieee80211_if_remove_vlan(struct net_device *dev, const char *name, int locked); ++int ieee80211_if_remove_ap(struct net_device *dev, const char *name, int locked); ++int ieee80211_if_flush(struct net_device *dev, int locked); ++int ieee80211_if_update_wds(struct net_device *dev, char *name, ++ struct ieee80211_if_wds *wds, int locked); ++ ++/* ieee80211_ioctl.c */ ++int ieee80211_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); ++extern const struct iw_handler_def ieee80211_iw_handler_def; ++ ++/* Set hw encryption from ieee80211 */ ++int ieee80211_set_hw_encryption(struct net_device *dev, ++ struct sta_info *sta, u8 addr[ETH_ALEN], ++ struct ieee80211_key *key); ++ ++/* ieee80211_scan.c */ ++void ieee80211_init_scan(struct net_device *dev); ++void ieee80211_stop_scan(struct net_device *dev); ++ ++ ++ ++/* Least common multiple of the used rates (in 100 kbps). This is used to ++ * calculate rate_inv values for each rate so that only integers are needed. */ ++#define CHAN_UTIL_RATE_LCM 95040 ++/* 1 usec is 1/8 * (95040/10) = 1188 */ ++#define CHAN_UTIL_PER_USEC 1188 ++/* Amount of bits to shift the result right to scale the total utilization ++ * to values that will not wrap around 32-bit integers. */ ++#define CHAN_UTIL_SHIFT 9 ++/* Theoretical maximum of channel utilization counter in 10 ms (stat_time=1): ++ * (CHAN_UTIL_PER_USEC * 10000) >> CHAN_UTIL_SHIFT = 23203. So dividing the ++ * raw value with about 23 should give utilization in 10th of a percentage ++ * (1/1000). However, utilization is only estimated and not all intervals ++ * between frames etc. are calculated. 18 seems to give numbers that are closer ++ * to the real maximum. */ ++#define CHAN_UTIL_PER_10MS 18 ++#define CHAN_UTIL_HDR_LONG (202 * CHAN_UTIL_PER_USEC) ++#define CHAN_UTIL_HDR_SHORT (40 * CHAN_UTIL_PER_USEC) ++ ++ ++ ++/* ieee80211.c */ ++int ieee80211_if_add_sta(struct net_device *dev, const char *name, int locked); ++int ieee80211_if_remove_sta(struct net_device *dev, const char *name, int locked); ++/* ieee80211_ioctl.c */ ++int ieee80211_set_compression(struct ieee80211_local *local, ++ struct net_device *dev, struct sta_info *sta); ++int ieee80211_set_bss_count(struct net_device *dev, int new_count, ++ u8 *bssid_mask); ++/* ieee80211_sta.c */ ++void ieee80211_sta_timer(unsigned long ptr); ++void ieee80211_sta_rx_mgmt(struct net_device *dev, struct sk_buff *skb, ++ struct ieee80211_rx_status *rx_status); ++int ieee80211_sta_set_ssid(struct net_device *dev, char *ssid, size_t len); ++int ieee80211_sta_get_ssid(struct net_device *dev, char *ssid, size_t *len); ++int ieee80211_sta_set_bssid(struct net_device *dev, u8 *bssid); ++int ieee80211_sta_req_scan(struct net_device *dev, u8 *ssid, size_t ssid_len); ++int ieee80211_sta_scan_results(struct net_device *dev, char *buf, size_t len); ++void ieee80211_sta_rx_scan(struct net_device *dev, struct sk_buff *skb, ++ struct ieee80211_rx_status *rx_status); ++void ieee80211_rx_bss_list_init(struct net_device *dev); ++void ieee80211_rx_bss_list_deinit(struct net_device *dev); ++int ieee80211_sta_set_extra_ie(struct net_device *dev, char *ie, size_t len); ++struct sta_info * ieee80211_ibss_add_sta(struct net_device *dev, ++ struct sk_buff *skb, u8 *bssid, ++ u8 *addr); ++int ieee80211_sta_deauthenticate(struct net_device *dev, u16 reason); ++int ieee80211_sta_disassociate(struct net_device *dev, u16 reason); ++ ++/* ieee80211_dev.c */ ++int ieee80211_dev_alloc_index(struct ieee80211_local *local); ++void ieee80211_dev_free_index(struct ieee80211_local *local); ++struct ieee80211_local *ieee80211_dev_find(int index); ++ ++/* ieee80211_sysfs.c */ ++int ieee80211_register_sysfs(struct ieee80211_local *local); ++void ieee80211_unregister_sysfs(struct ieee80211_local *local); ++int ieee80211_sysfs_init(void); ++void ieee80211_sysfs_deinit(void); ++ ++#endif /* IEEE80211_I_H */ +diff -Nur linux-2.6.16/net/d80211/ieee80211_ioctl.c linux-2.6.16-bcm43xx/net/d80211/ieee80211_ioctl.c +--- linux-2.6.16/net/d80211/ieee80211_ioctl.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.16-bcm43xx/net/d80211/ieee80211_ioctl.c 2006-03-28 22:16:14.000000000 +0200 +@@ -0,0 +1,3040 @@ ++/* ++ * Copyright 2002-2005, Instant802 Networks, Inc. ++ * Copyright 2005-2006, Devicescape Software, Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include <linux/config.h> ++#include <linux/version.h> ++#include <linux/module.h> ++#include <linux/init.h> ++#include <linux/netdevice.h> ++#include <linux/types.h> ++#include <linux/slab.h> ++#include <linux/skbuff.h> ++#include <linux/if_arp.h> ++#include <linux/wireless.h> ++#include <net/iw_handler.h> ++#include <asm/uaccess.h> ++ ++#include <net/d80211.h> ++#include <net/d80211_mgmt.h> ++#include "ieee80211_i.h" ++#include "hostapd_ioctl.h" ++#include "rate_control.h" ++#include "wpa.h" ++#include "aes_ccm.h" ++ ++ ++static int ieee80211_regdom = 0x10; /* FCC */ ++MODULE_PARM(ieee80211_regdom, "i"); ++MODULE_PARM_DESC(ieee80211_regdom, "IEEE 802.11 regulatory domain; 64=MKK"); ++ ++/* ++ * If firmware is upgraded by the vendor, additional channels can be used based ++ * on the new Japanese regulatory rules. This is indicated by setting ++ * ieee80211_japan_5ghz module parameter to one when loading the 80211 kernel ++ * module. ++ */ ++static int ieee80211_japan_5ghz /* = 0 */; ++MODULE_PARM(ieee80211_japan_5ghz, "i"); ++MODULE_PARM_DESC(ieee80211_japan_5ghz, "Vendor-updated firmware for 5 GHz"); ++ ++ ++static int ieee80211_ioctl_set_beacon(struct net_device *dev, ++ struct prism2_hostapd_param *param, ++ int param_len, ++ int flag) ++{ ++ struct ieee80211_sub_if_data *sdata; ++ struct ieee80211_if_ap *ap; ++ u8 **b_head, **b_tail; ++ int *b_head_len, *b_tail_len; ++ int len; ++ ++ len = ((char *) param->u.beacon.data - (char *) param) + ++ param->u.beacon.head_len + param->u.beacon.tail_len; ++ ++ if (param_len > len) ++ param_len = len; ++ else if (param_len != len) ++ return -EINVAL; ++ ++ sdata = IEEE80211_DEV_TO_SUB_IF(dev); ++ if (sdata->type != IEEE80211_SUB_IF_TYPE_AP) ++ return -EINVAL; ++ ap = &sdata->u.ap; ++ ++ switch (flag) { ++ case 0: ++ b_head = &ap->beacon_head; ++ b_tail = &ap->beacon_tail; ++ b_head_len = &ap->beacon_head_len; ++ b_tail_len = &ap->beacon_tail_len; ++ break; ++ default: ++ printk(KERN_DEBUG "%s: unknown beacon flag %d\n", ++ dev->name, flag); ++ return -EINVAL; ++ } ++ ++ kfree(*b_head); ++ kfree(*b_tail); ++ *b_head = NULL; ++ *b_tail = NULL; ++ ++ *b_head_len = param->u.beacon.head_len; ++ *b_tail_len = param->u.beacon.tail_len; ++ ++ *b_head = kmalloc(*b_head_len, GFP_KERNEL); ++ if (*b_head) ++ memcpy(*b_head, param->u.beacon.data, *b_head_len); ++ else { ++ printk(KERN_DEBUG "%s: failed to allocate beacon_head\n", ++ dev->name); ++ return -ENOMEM; ++ } ++ ++ if (*b_tail_len > 0) { ++ *b_tail = kmalloc(*b_tail_len, GFP_KERNEL); ++ if (*b_tail) ++ memcpy(*b_tail, param->u.beacon.data + (*b_head_len), ++ (*b_tail_len)); ++ else { ++ printk(KERN_DEBUG "%s: failed to allocate " ++ "beacon_tail\n", dev->name); ++ return -ENOMEM; ++ } ++ } ++ ++ return 0; ++} ++ ++ ++static int ieee80211_ioctl_get_hw_features(struct net_device *dev, ++ struct prism2_hostapd_param *param, ++ int param_len) ++{ ++ struct ieee80211_local *local = dev->priv; ++ u8 *pos = param->u.hw_features.data; ++ int left = param_len - (pos - (u8 *) param); ++ int mode, i; ++ struct hostapd_ioctl_hw_modes_hdr *hdr; ++ struct ieee80211_rate_data *rate; ++ struct ieee80211_channel_data *chan; ++ ++ param->u.hw_features.flags = 0; ++ if (local->hw->data_nullfunc_ack) ++ param->u.hw_features.flags |= HOSTAP_HW_FLAG_NULLFUNC_OK; ++ ++ param->u.hw_features.num_modes = local->hw->num_modes; ++ for (mode = 0; mode < local->hw->num_modes; mode++) { ++ int clen, rlen; ++ struct ieee80211_hw_modes *m = &local->hw->modes[mode]; ++ clen = m->num_channels * sizeof(struct ieee80211_channel_data); ++ rlen = m->num_rates * sizeof(struct ieee80211_rate_data); ++ if (left < sizeof(*hdr) + clen + rlen) ++ return -E2BIG; ++ left -= sizeof(*hdr) + clen + rlen; ++ ++ hdr = (struct hostapd_ioctl_hw_modes_hdr *) pos; ++ hdr->mode = m->mode; ++ hdr->num_channels = m->num_channels; ++ hdr->num_rates = m->num_rates; ++ ++ pos = (u8 *) (hdr + 1); ++ chan = (struct ieee80211_channel_data *) pos; ++ for (i = 0; i < m->num_channels; i++) { ++ chan[i].chan = m->channels[i].chan; ++ chan[i].freq = m->channels[i].freq; ++ chan[i].flag = m->channels[i].flag; ++ } ++ pos += clen; ++ ++ rate = (struct ieee80211_rate_data *) pos; ++ for (i = 0; i < m->num_rates; i++) { ++ rate[i].rate = m->rates[i].rate; ++ rate[i].flags = m->rates[i].flags; ++ } ++ pos += rlen; ++ } ++ ++ return 0; ++} ++ ++ ++static int ieee80211_ioctl_scan(struct net_device *dev, ++ struct prism2_hostapd_param *param) ++{ ++ struct ieee80211_local *local = dev->priv; ++ ++ if (local->hw->passive_scan == NULL) ++ return -EOPNOTSUPP; ++ ++ if ((param->u.scan.now == 1) && (local->scan.in_scan == 1)) ++ return -EBUSY; ++ ++ if (param->u.scan.our_mode_only >= 0) ++ local->scan.our_mode_only = param->u.scan.our_mode_only; ++ if (param->u.scan.interval >= 0) ++ local->scan.interval = param->u.scan.interval; ++ if (param->u.scan.listen >= 0) ++ local->scan.time = param->u.scan.listen; ++ if (param->u.scan.channel > 0) ++ local->scan.channel = param->u.scan.channel; ++ if (param->u.scan.now == 1) { ++ local->scan.in_scan = 0; ++ mod_timer(&local->scan.timer, jiffies); ++ } ++ ++ param->u.scan.our_mode_only = local->scan.our_mode_only; ++ param->u.scan.interval = local->scan.interval; ++ param->u.scan.listen = local->scan.time; ++ if (local->scan.in_scan == 1) ++ param->u.scan.last_rx = -1; ++ else { ++ param->u.scan.last_rx = local->scan.rx_packets; ++ local->scan.rx_packets = -1; ++ } ++ param->u.scan.channel = local->hw->modes[local->scan.mode_idx]. ++ channels[local->scan.chan_idx].chan; ++ ++ return 0; ++} ++ ++ ++static int ieee80211_ioctl_flush(struct net_device *dev, ++ struct prism2_hostapd_param *param) ++{ ++ struct ieee80211_local *local = dev->priv; ++ sta_info_flush(local, NULL); ++ return 0; ++} ++ ++ ++static int ieee80211_ioctl_add_sta(struct net_device *dev, ++ struct prism2_hostapd_param *param) ++{ ++ struct ieee80211_local *local = dev->priv; ++ struct sta_info *sta; ++ u32 rates; ++ int i, j; ++ struct ieee80211_sub_if_data *sdata; ++ int add_key_entry = 1; ++ ++ sta = sta_info_get(local, param->sta_addr); ++ ++ if (sta == NULL) { ++ sta = sta_info_add(local, dev, param->sta_addr); ++ if (sta == NULL) ++ return -ENOMEM; ++ } ++ ++ if (sta->dev != dev) { ++ /* Binding STA to a new interface, so remove all references to ++ * the old BSS. */ ++ sta_info_remove_aid_ptr(sta); ++ } ++ ++ /* TODO ++ * We "steal" the device in case someone owns it ++ * This will hurt WDS links and such when we have a ++ * WDS link and a client associating from the same station ++ */ ++ sta->dev = dev; ++ sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev); ++ ++ sta->flags |= WLAN_STA_AUTH | WLAN_STA_ASSOC; ++ sta->aid = param->u.add_sta.aid; ++ if (sta->aid > MAX_AID_TABLE_SIZE) ++ sta->aid = 0; ++ if (sta->aid > 0 && sdata->bss) ++ sdata->bss->sta_aid[sta->aid - 1] = sta; ++ if (sdata->bss && sta->aid > sdata->bss->max_aid) ++ sdata->bss->max_aid = sta->aid; ++ ++ rates = 0; ++ for (i = 0; i < sizeof(param->u.add_sta.supp_rates); i++) { ++ int rate = (param->u.add_sta.supp_rates[i] & 0x7f) * 5; ++ if (local->conf.phymode == MODE_ATHEROS_TURBO || ++ local->conf.phymode == MODE_ATHEROS_TURBOG) ++ rate *= 2; ++ for (j = 0; j < local->num_curr_rates; j++) { ++ if (local->curr_rates[j].rate == rate) ++ rates |= BIT(j); ++ } ++ ++ } ++ sta->supp_rates = rates; ++ ++ rate_control_rate_init(local, sta); ++ ++ if (param->u.add_sta.wds_flags & 0x01) ++ sta->flags |= WLAN_STA_WDS; ++ else ++ sta->flags &= ~WLAN_STA_WDS; ++ ++ if (add_key_entry && sta->key == NULL && sdata->default_key == NULL && ++ local->hw->set_key) { ++ struct ieee80211_key_conf conf; ++ /* Add key cache entry with NULL key type because this may used ++ * for TX filtering. */ ++ memset(&conf, 0, sizeof(conf)); ++ conf.hw_key_idx = HW_KEY_IDX_INVALID; ++ conf.alg = ALG_NULL; ++ conf.force_sw_encrypt = 1; ++ if (local->hw->set_key(dev, SET_KEY, sta->addr, &conf, ++ sta->aid)) { ++ sta->key_idx_compression = HW_KEY_IDX_INVALID; ++ } else { ++ sta->key_idx_compression = conf.hw_key_idx; ++ } ++ } ++ ++ sta_info_release(local, sta); ++ ++ return 0; ++} ++ ++ ++static int ieee80211_ioctl_remove_sta(struct net_device *dev, ++ struct prism2_hostapd_param *param) ++{ ++ struct ieee80211_local *local = dev->priv; ++ struct sta_info *sta; ++ ++ sta = sta_info_get(local, param->sta_addr); ++ if (sta) { ++ sta_info_release(local, sta); ++ sta_info_free(local, sta, 1); ++ } ++ ++ return sta ? 0 : -ENOENT; ++} ++ ++ ++static int ieee80211_ioctl_get_dot11counterstable(struct net_device *dev, ++ struct prism2_hostapd_param *param) ++{ ++ struct ieee80211_local *local = dev->priv; ++ struct ieee80211_low_level_stats stats; ++ ++ memset(&stats, 0, sizeof(stats)); ++ if (local->hw->get_stats) ++ local->hw->get_stats(dev, &stats); ++ param->u.dot11CountersTable.dot11TransmittedFragmentCount = ++ local->dot11TransmittedFragmentCount; ++ param->u.dot11CountersTable.dot11MulticastTransmittedFrameCount = ++ local->dot11MulticastTransmittedFrameCount; ++ param->u.dot11CountersTable.dot11ReceivedFragmentCount = ++ local->dot11ReceivedFragmentCount; ++ param->u.dot11CountersTable.dot11MulticastReceivedFrameCount = ++ local->dot11MulticastReceivedFrameCount; ++ param->u.dot11CountersTable.dot11TransmittedFrameCount = ++ local->dot11TransmittedFrameCount; ++ param->u.dot11CountersTable.dot11FCSErrorCount = ++ stats.dot11FCSErrorCount; ++ param->u.dot11CountersTable.dot11ACKFailureCount = ++ stats.dot11ACKFailureCount; ++ param->u.dot11CountersTable.dot11RTSFailureCount = ++ stats.dot11RTSFailureCount; ++ param->u.dot11CountersTable.dot11RTSSuccessCount = ++ stats.dot11RTSSuccessCount; ++ ++ return 0; ++} ++ ++ ++static int ieee80211_ioctl_get_info_sta(struct net_device *dev, ++ struct prism2_hostapd_param *param) ++{ ++ struct ieee80211_local *local = dev->priv; ++ struct sta_info *sta; ++ ++ if (param->sta_addr[0] == 0xff && param->sta_addr[1] == 0xff && ++ param->sta_addr[2] == 0xff && param->sta_addr[3] == 0xff && ++ param->sta_addr[4] == 0xff && param->sta_addr[5] == 0xff) { ++ struct ieee80211_sub_if_data *sdata; ++ struct net_device_stats *stats; ++ ++ sdata = IEEE80211_DEV_TO_SUB_IF(dev); ++ stats = ieee80211_dev_stats(sdata->master); ++ param->u.get_info_sta.rx_bytes = stats->rx_bytes; ++ param->u.get_info_sta.tx_bytes = stats->tx_bytes; ++ /* go through all STAs and get STA with lowest max. rate */ ++ param->u.get_info_sta.current_tx_rate = ++ local->curr_rates[sta_info_min_txrate_get(local)].rate; ++ return 0; ++ } ++ ++ sta = sta_info_get(local, param->sta_addr); ++ ++ if (!sta) ++ return -ENOENT; ++ ++ param->u.get_info_sta.inactive_msec = ++ jiffies_to_msecs(jiffies - sta->last_rx); ++ param->u.get_info_sta.rx_packets = sta->rx_packets; ++ param->u.get_info_sta.tx_packets = sta->tx_packets; ++ param->u.get_info_sta.rx_bytes = sta->rx_bytes; ++ param->u.get_info_sta.tx_bytes = sta->tx_bytes; ++ param->u.get_info_sta.channel_use = sta->channel_use; ++ param->u.get_info_sta.flags = sta->flags; ++ if (sta->txrate >= 0 && sta->txrate < local->num_curr_rates) ++ param->u.get_info_sta.current_tx_rate = ++ local->curr_rates[sta->txrate].rate; ++ param->u.get_info_sta.num_ps_buf_frames = ++ skb_queue_len(&sta->ps_tx_buf); ++ param->u.get_info_sta.tx_retry_failed = sta->tx_retry_failed; ++ param->u.get_info_sta.tx_retry_count = sta->tx_retry_count; ++ param->u.get_info_sta.last_rssi = sta->last_rssi; ++ param->u.get_info_sta.last_ack_rssi = sta->last_ack_rssi[2]; ++ ++ sta_info_release(local, sta); ++ ++ return 0; ++} ++ ++ ++static int ieee80211_ioctl_set_flags_sta(struct net_device *dev, ++ struct prism2_hostapd_param *param) ++{ ++ struct ieee80211_local *local = dev->priv; ++ struct sta_info *sta; ++ ++ sta = sta_info_get(local, param->sta_addr); ++ if (sta) { ++ sta->flags |= param->u.set_flags_sta.flags_or; ++ sta->flags &= param->u.set_flags_sta.flags_and; ++ if (local->hw->set_port_auth && ++ (param->u.set_flags_sta.flags_or & WLAN_STA_AUTHORIZED) && ++ local->hw->set_port_auth(local->mdev, sta->addr, 1)) ++ printk(KERN_DEBUG "%s: failed to set low-level driver " ++ "PAE state (authorized) for " MACSTR "\n", ++ dev->name, MAC2STR(sta->addr)); ++ if (local->hw->set_port_auth && ++ !(param->u.set_flags_sta.flags_and & WLAN_STA_AUTHORIZED) ++ && local->hw->set_port_auth(local->mdev, sta->addr, 0)) ++ printk(KERN_DEBUG "%s: failed to set low-level driver " ++ "PAE state (unauthorized) for " MACSTR "\n", ++ dev->name, MAC2STR(sta->addr)); ++ sta_info_release(local, sta); ++ } ++ ++ return sta ? 0 : -ENOENT; ++} ++ ++ ++int ieee80211_set_hw_encryption(struct net_device *dev, ++ struct sta_info *sta, u8 addr[ETH_ALEN], ++ struct ieee80211_key *key) ++{ ++ struct ieee80211_key_conf *keyconf = NULL; ++ struct ieee80211_local *local = dev->priv; ++ int rc = 0; ++ ++ /* default to sw encryption; this will be cleared by low-level ++ * driver if the hw supports requested encryption */ ++ if (key) ++ key->force_sw_encrypt = 1; ++ ++ if (key && local->hw->set_key && ++ (!local->conf.sw_encrypt || !local->conf.sw_decrypt) && ++ (keyconf = ieee80211_key_data2conf(local, key)) != NULL) { ++ if (local->hw->set_key(dev, SET_KEY, addr, ++ keyconf, sta ? sta->aid : 0)) { ++ rc = HOSTAP_CRYPT_ERR_KEY_SET_FAILED; ++ key->force_sw_encrypt = 1; ++ key->hw_key_idx = HW_KEY_IDX_INVALID; ++ } else { ++ key->force_sw_encrypt = ++ keyconf->force_sw_encrypt; ++ key->hw_key_idx = ++ keyconf->hw_key_idx; ++ ++ } ++ } ++ kfree(keyconf); ++ ++ return rc; ++} ++ ++ ++static int ieee80211_set_encryption(struct net_device *dev, u8 *sta_addr, ++ int idx, int alg, int set_tx_key, int *err, ++ const u8 *_key, size_t key_len) ++{ ++ struct ieee80211_local *local = dev->priv; ++ int ret = 0; ++ struct sta_info *sta; ++ struct ieee80211_key **key; ++ int try_hwaccel = 1; ++ struct ieee80211_key_conf *keyconf; ++ struct ieee80211_sub_if_data *sdata; ++ ++ sdata = IEEE80211_DEV_TO_SUB_IF(dev); ++ ++ if (sta_addr[0] == 0xff && sta_addr[1] == 0xff && ++ sta_addr[2] == 0xff && sta_addr[3] == 0xff && ++ sta_addr[4] == 0xff && sta_addr[5] == 0xff) { ++ sta = NULL; ++ if (idx >= NUM_DEFAULT_KEYS) { ++ printk(KERN_DEBUG "%s: set_encrypt - invalid idx=%d\n", ++ dev->name, idx); ++ return -EINVAL; ++ } ++ key = &sdata->keys[idx]; ++ ++ /* Disable hwaccel for default keys when the interface is not ++ * the default one. ++ * TODO: consider adding hwaccel support for these; at least ++ * Atheros key cache should be able to handle this since AP is ++ * only transmitting frames with default keys. */ ++ /* FIX: hw key cache can be used when only one virtual ++ * STA is associated with each AP. If more than one STA ++ * is associated to the same AP, software encryption ++ * must be used. This should be done automatically ++ * based on configured station devices. For the time ++ * being, this can be only set at compile time. */ ++ if (sdata->type == IEEE80211_SUB_IF_TYPE_STA) { ++ if (0 /* FIX: more than one STA per AP */) ++ try_hwaccel = 0; ++ } else ++ if (sdata->type != IEEE80211_SUB_IF_TYPE_AP || ++ dev != local->wdev) ++ try_hwaccel = 0; ++ } else { ++ set_tx_key = 0; ++ if (idx != 0) { ++ printk(KERN_DEBUG "%s: set_encrypt - non-zero idx for " ++ "individual key\n", dev->name); ++ return -EINVAL; ++ } ++ ++ sta = sta_info_get(local, sta_addr); ++ if (sta == NULL) { ++ if (err) ++ *err = HOSTAP_CRYPT_ERR_UNKNOWN_ADDR; ++#ifdef CONFIG_D80211_VERBOSE_DEBUG ++ printk(KERN_DEBUG "%s: set_encrypt - unknown addr " ++ MACSTR "\n", ++ dev->name, MAC2STR(sta_addr)); ++#endif /* CONFIG_D80211_VERBOSE_DEBUG */ ++ ++ return -ENOENT; ++ } ++ ++ key = &sta->key; ++ } ++ ++ /* FIX: ++ * Cannot configure default hwaccel keys with WEP algorithm, if ++ * any of the virtual interfaces is using static WEP ++ * configuration because hwaccel would otherwise try to decrypt ++ * these frames. ++ * ++ * For now, just disable WEP hwaccel for broadcast when there is ++ * possibility of conflict with default keys. This can maybe later be ++ * optimized by using non-default keys (at least with Atheros ar521x). ++ */ ++ if (!sta && alg == ALG_WEP && !local->default_wep_only && ++ local->conf.mode != IW_MODE_ADHOC && ++ local->conf.mode != IW_MODE_INFRA) { ++ try_hwaccel = 0; ++ } ++ ++ if (local->hw->device_hides_wep) { ++ /* Software encryption cannot be used with devices that hide ++ * encryption from the host system, so always try to use ++ * hardware acceleration with such devices. */ ++ try_hwaccel = 1; ++ } ++ ++ if (local->hw->no_tkip_wmm_hwaccel && alg == ALG_TKIP) { ++ if (sta && (sta->flags & WLAN_STA_WME)) { ++ /* Hardware does not support hwaccel with TKIP when using WMM. ++ */ ++ try_hwaccel = 0; ++ } ++ else if (sdata->type == IEEE80211_SUB_IF_TYPE_STA) { ++ sta = sta_info_get(local, sdata->u.sta.bssid); ++ if (sta) { ++ if (sta->flags & WLAN_STA_WME) { ++ try_hwaccel = 0; ++ } ++ sta_info_release(local, sta); ++ sta = NULL; ++ } ++ } ++ } ++ ++ if (alg == ALG_NONE) { ++ keyconf = NULL; ++ if (try_hwaccel && *key && local->hw->set_key && ++ (keyconf = ieee80211_key_data2conf(local, *key)) != NULL && ++ local->hw->set_key(dev, DISABLE_KEY, sta_addr, ++ keyconf, sta ? sta->aid : 0)) { ++ if (err) ++ *err = HOSTAP_CRYPT_ERR_KEY_SET_FAILED; ++ printk(KERN_DEBUG "%s: set_encrypt - low-level disable" ++ " failed\n", dev->name); ++ ret = -EINVAL; ++ } ++ kfree(keyconf); ++ ++ if (sdata->default_key == *key) ++ sdata->default_key = NULL; ++ kfree(*key); ++ *key = NULL; ++ } else { ++ if (*key == NULL || (*key)->keylen < key_len) { ++ kfree(*key); ++ *key = kmalloc(sizeof(struct ieee80211_key) + ++ key_len, GFP_ATOMIC); ++ if (*key == NULL) { ++ ret = -ENOMEM; ++ goto done; ++ } ++ } ++ memset(*key, 0, sizeof(struct ieee80211_key) + key_len); ++ /* default to sw encryption; low-level driver sets these if the ++ * requested encryption is supported */ ++ (*key)->hw_key_idx = HW_KEY_IDX_INVALID; ++ (*key)->force_sw_encrypt = 1; ++ ++ (*key)->alg = alg; ++ (*key)->keyidx = idx; ++ (*key)->keylen = key_len; ++ memcpy((*key)->key, _key, key_len); ++ if (set_tx_key) ++ (*key)->default_tx_key = 1; ++ ++ if (alg == ALG_CCMP) { ++ /* Initialize AES key state here as an optimization ++ * so that it does not need to be initialized for every ++ * packet. */ ++ ieee80211_aes_key_setup_encrypt( ++ (*key)->u.ccmp.aes_state, (*key)->key); ++ } ++ ++ if (try_hwaccel && ++ (alg == ALG_WEP || alg == ALG_TKIP || alg == ALG_CCMP)) { ++ int e = ieee80211_set_hw_encryption(dev, sta, sta_addr, ++ *key); ++ if (err) ++ *err = e; ++ } ++ } ++ ++ if (set_tx_key || (sta == NULL && sdata->default_key == NULL)) { ++ sdata->default_key = *key; ++ if (local->hw->set_key_idx && ++ local->hw->set_key_idx(dev, idx)) ++ printk(KERN_DEBUG "%s: failed to set TX key idx for " ++ "low-level driver\n", dev->name); ++ } ++ ++ done: ++ if (sta) ++ sta_info_release(local, sta); ++ ++ return ret; ++} ++ ++ ++static int ieee80211_ioctl_set_encryption(struct net_device *dev, ++ struct prism2_hostapd_param *param, ++ int param_len) ++{ ++ int alg; ++ ++ param->u.crypt.err = 0; ++ param->u.crypt.alg[HOSTAP_CRYPT_ALG_NAME_LEN - 1] = '\0'; ++ ++ if (param_len < ++ (int) ((char *) param->u.crypt.key - (char *) param) + ++ param->u.crypt.key_len) { ++ printk(KERN_DEBUG "%s: set_encrypt - invalid param_lem\n", ++ dev->name); ++ return -EINVAL; ++ } ++ ++ if (strcmp(param->u.crypt.alg, "none") == 0) ++ alg = ALG_NONE; ++ else if (strcmp(param->u.crypt.alg, "WEP") == 0) ++ alg = ALG_WEP; ++ else if (strcmp(param->u.crypt.alg, "TKIP") == 0) { ++ if (param->u.crypt.key_len != ALG_TKIP_KEY_LEN) { ++ printk(KERN_DEBUG "%s: set_encrypt - invalid TKIP key " ++ "length %d\n", dev->name, ++ param->u.crypt.key_len); ++ return -EINVAL; ++ } ++ alg = ALG_TKIP; ++ } else if (strcmp(param->u.crypt.alg, "CCMP") == 0) { ++ if (param->u.crypt.key_len != ALG_CCMP_KEY_LEN) { ++ printk(KERN_DEBUG "%s: set_encrypt - invalid CCMP key " ++ "length %d\n", dev->name, ++ param->u.crypt.key_len); ++ return -EINVAL; ++ } ++ alg = ALG_CCMP; ++ } else { ++ param->u.crypt.err = HOSTAP_CRYPT_ERR_UNKNOWN_ALG; ++ printk(KERN_DEBUG "%s: set_encrypt - unknown alg\n", ++ dev->name); ++ return -EINVAL; ++ } ++ ++ return ieee80211_set_encryption( ++ dev, param->sta_addr, ++ param->u.crypt.idx, alg, ++ param->u.crypt.flags & HOSTAP_CRYPT_FLAG_SET_TX_KEY, ++ ¶m->u.crypt.err, param->u.crypt.key, ++ param->u.crypt.key_len); ++} ++ ++ ++static int ieee80211_ioctl_get_encryption(struct net_device *dev, ++ struct prism2_hostapd_param *param, ++ int param_len) ++{ ++ struct ieee80211_local *local = dev->priv; ++ int ret = 0; ++ struct sta_info *sta; ++ struct ieee80211_key **key; ++ int max_key_len; ++ struct ieee80211_sub_if_data *sdata; ++ u8 *pos; ++ ++ sdata = IEEE80211_DEV_TO_SUB_IF(dev); ++ ++ param->u.crypt.err = 0; ++ ++ max_key_len = param_len - ++ (int) ((char *) param->u.crypt.key - (char *) param); ++ if (max_key_len < 0) ++ return -EINVAL; ++ ++ if (param->sta_addr[0] == 0xff && param->sta_addr[1] == 0xff && ++ param->sta_addr[2] == 0xff && param->sta_addr[3] == 0xff && ++ param->sta_addr[4] == 0xff && param->sta_addr[5] == 0xff) { ++ sta = NULL; ++ if (param->u.crypt.idx > NUM_DEFAULT_KEYS) { ++ param->u.crypt.idx = sdata->default_key ? ++ sdata->default_key->keyidx : 0; ++ return 0; ++ } else ++ key = &sdata->keys[param->u.crypt.idx]; ++ } else { ++ sta = sta_info_get(local, param->sta_addr); ++ if (sta == NULL) { ++ param->u.crypt.err = HOSTAP_CRYPT_ERR_UNKNOWN_ADDR; ++ return -EINVAL; ++ } ++ ++ key = &sta->key; ++ } ++ ++ memset(param->u.crypt.seq_counter, 0, HOSTAP_SEQ_COUNTER_SIZE); ++ if (*key == NULL) { ++ memcpy(param->u.crypt.alg, "none", 5); ++ param->u.crypt.key_len = 0; ++ param->u.crypt.idx = 0xff; ++ } else { ++ switch ((*key)->alg) { ++ case ALG_WEP: ++ memcpy(param->u.crypt.alg, "WEP", 4); ++ break; ++ case ALG_TKIP: ++ { ++ u32 iv32; ++ u16 iv16; ++ ++ memcpy(param->u.crypt.alg, "TKIP", 5); ++ if (local->hw->get_sequence_counter) { ++ /* Get transmit counter from low level driver */ ++ if (local->hw->get_sequence_counter(dev, ++ param->sta_addr, ++ (*key)->keyidx, ++ IEEE80211_SEQ_COUNTER_TX, ++ &iv32, ++ &iv16)) { ++ /* Error getting value from device */ ++ return -EIO; ++ } ++ } else { ++ /* Get it from our own local data */ ++ iv32 = (*key)->u.tkip.iv32; ++ iv16 = (*key)->u.tkip.iv16; ++ } ++ pos = param->u.crypt.seq_counter; ++ *pos++ = iv16 & 0xff; ++ *pos++ = (iv16 >> 8) & 0xff; ++ *pos++ = iv32 & 0xff; ++ *pos++ = (iv32 >> 8) & 0xff; ++ *pos++ = (iv32 >> 16) & 0xff; ++ *pos++ = (iv32 >> 24) & 0xff; ++ break; ++ } ++ case ALG_CCMP: ++ { ++ u8 *pn; ++ memcpy(param->u.crypt.alg, "CCMP", 5); ++ pos = param->u.crypt.seq_counter; ++ pn = (*key)->u.ccmp.tx_pn; ++ *pos++ = pn[5]; ++ *pos++ = pn[4]; ++ *pos++ = pn[3]; ++ *pos++ = pn[2]; ++ *pos++ = pn[1]; ++ *pos++ = pn[0]; ++ break; ++ } ++ default: ++ memcpy(param->u.crypt.alg, "unknown", 8); ++ break; ++ } ++ ++ if (max_key_len < (*key)->keylen) ++ ret = -E2BIG; ++ else { ++ param->u.crypt.key_len = (*key)->keylen; ++ memcpy(param->u.crypt.key, (*key)->key, ++ (*key)->keylen); ++ } ++ } ++ ++ if (sta) ++ sta_info_release(local, sta); ++ ++ return ret; ++} ++ ++ ++#ifdef CONFIG_HOSTAPD_WPA_TESTING ++static int ieee80211_ioctl_wpa_trigger(struct net_device *dev, ++ struct prism2_hostapd_param *param) ++{ ++ struct ieee80211_local *local = dev->priv; ++ struct sta_info *sta; ++ ++ if (param->sta_addr[0] == 0xff && param->sta_addr[1] == 0xff && ++ param->sta_addr[2] == 0xff && param->sta_addr[3] == 0xff && ++ param->sta_addr[4] == 0xff && param->sta_addr[5] == 0xff) { ++ local->wpa_trigger = param->u.wpa_trigger.trigger; ++ return 0; ++ } ++ ++ sta = sta_info_get(local, param->sta_addr); ++ if (sta == NULL) { ++ printk(KERN_DEBUG "%s: wpa_trigger - unknown addr\n", ++ dev->name); ++ return -EINVAL; ++ } ++ ++ sta->wpa_trigger = param->u.wpa_trigger.trigger; ++ ++ sta_info_release(local, sta); ++ return 0; ++} ++#endif /* CONFIG_HOSTAPD_WPA_TESTING */ ++ ++ ++static int ieee80211_ioctl_set_rate_sets(struct net_device *dev, ++ struct prism2_hostapd_param *param, ++ int param_len) ++{ ++ struct ieee80211_local *local = dev->priv; ++ u16 *pos = (u16 *) param->u.set_rate_sets.data; ++ int left = param_len - ((u8 *) pos - (u8 *) param); ++ int i, mode, num_supp, num_basic, *supp, *basic, *prev; ++ ++ mode = param->u.set_rate_sets.mode; ++ num_supp = param->u.set_rate_sets.num_supported_rates; ++ num_basic = param->u.set_rate_sets.num_basic_rates; ++ ++ if (left < (num_supp + num_basic) * 2) { ++ printk(KERN_WARNING "%s: invalid length in hostapd set rate " ++ "sets ioctl (%d != %d)\n", dev->name, left, ++ (num_supp + num_basic) * 2); ++ return -EINVAL; ++ } ++ ++ supp = (int *) kmalloc((num_supp + 1) * sizeof(int), GFP_KERNEL); ++ basic = (int *) kmalloc((num_basic + 1) * sizeof(int), GFP_KERNEL); ++ ++ if (!supp || !basic) { ++ kfree(supp); ++ kfree(basic); ++ return -ENOMEM; ++ } ++ ++ for (i = 0; i < num_supp; i++) ++ supp[i] = *pos++; ++ supp[i] = -1; ++ ++ for (i = 0; i < num_basic; i++) ++ basic[i] = *pos++; ++ basic[i] = -1; ++ ++ if (num_supp == 0) { ++ kfree(supp); ++ supp = NULL; ++ } ++ ++ if (num_basic == 0) { ++ kfree(basic); ++ basic = NULL; ++ } ++ ++ prev = local->supp_rates[mode]; ++ local->supp_rates[mode] = supp; ++ kfree(prev); ++ ++ prev = local->basic_rates[mode]; ++ local->basic_rates[mode] = basic; ++ kfree(prev); ++ ++ if (mode == local->conf.phymode) { ++ /* TODO: should update STA TX rates and remove STAs if they ++ * do not have any remaining supported rates after the change ++ */ ++ ieee80211_prepare_rates(dev); ++ } ++ ++ return 0; ++} ++ ++ ++static int ieee80211_ioctl_add_if(struct net_device *dev, ++ struct prism2_hostapd_param *param, ++ int param_len) ++{ ++ u8 *pos = param->u.if_info.data; ++ int left = param_len - ((u8 *) pos - (u8 *) param); ++ ++ if (param->u.if_info.type == HOSTAP_IF_WDS) { ++ struct ieee80211_if_wds iwds; ++ struct hostapd_if_wds *wds = ++ (struct hostapd_if_wds *) param->u.if_info.data; ++ ++ if (left < sizeof(struct ieee80211_if_wds)) ++ return -EPROTO; ++ ++ memcpy(iwds.remote_addr, wds->remote_addr, ETH_ALEN); ++ ++ return ieee80211_if_add_wds(dev, param->u.if_info.name, ++ &iwds, 1); ++ } else if (param->u.if_info.type == HOSTAP_IF_VLAN) { ++ struct hostapd_if_vlan *vlan = (struct hostapd_if_vlan *) pos; ++ struct ieee80211_if_vlan ivlan; ++ ++ if (left < sizeof(struct hostapd_if_vlan)) ++ return -EPROTO; ++ ++ ivlan.id = vlan->id; ++ ++ return ieee80211_if_add_vlan(dev, param->u.if_info.name, ++ &ivlan, 1); ++ } else if (param->u.if_info.type == HOSTAP_IF_BSS) { ++ struct hostapd_if_bss *bss = ++ (struct hostapd_if_bss *) param->u.if_info.data; ++ ++ if (left < sizeof(struct hostapd_if_bss)) ++ return -EPROTO; ++ ++ return ieee80211_if_add_ap(dev, param->u.if_info.name, ++ bss->bssid, 1); ++ } else if (param->u.if_info.type == HOSTAP_IF_STA) { ++#if 0 ++ struct hostapd_if_sta *sta = ++ (struct hostapd_if_sta *) param->u.if_info.data; ++#endif ++ ++ if (left < sizeof(struct hostapd_if_sta)) ++ return -EPROTO; ++ ++ return ieee80211_if_add_sta(dev, param->u.if_info.name, 1); ++ } else ++ return -EINVAL; ++ ++ return 0; ++} ++ ++ ++static int ieee80211_ioctl_remove_if(struct net_device *dev, ++ struct prism2_hostapd_param *param) ++{ ++ if (param->u.if_info.type == HOSTAP_IF_WDS) { ++ return ieee80211_if_remove_wds(dev, param->u.if_info.name, 1); ++ } else if (param->u.if_info.type == HOSTAP_IF_VLAN) { ++ return ieee80211_if_remove_vlan(dev, param->u.if_info.name, 1); ++ } else if (param->u.if_info.type == HOSTAP_IF_BSS) { ++ return ieee80211_if_remove_ap(dev, param->u.if_info.name, 1); ++ } else if (param->u.if_info.type == HOSTAP_IF_STA) { ++ return ieee80211_if_remove_sta(dev, param->u.if_info.name, 1); ++ } else { ++ return -EINVAL; ++ } ++} ++ ++ ++static int ieee80211_ioctl_update_if(struct net_device *dev, ++ struct prism2_hostapd_param *param, ++ int param_len) ++{ ++ u8 *pos = param->u.if_info.data; ++ int left = param_len - ((u8 *) pos - (u8 *) param); ++ ++ if (param->u.if_info.type == HOSTAP_IF_WDS) { ++ struct ieee80211_if_wds iwds; ++ struct hostapd_if_wds *wds = ++ (struct hostapd_if_wds *) param->u.if_info.data; ++ ++ if (left < sizeof(struct ieee80211_if_wds)) ++ return -EPROTO; ++ ++ memcpy(iwds.remote_addr, wds->remote_addr, ETH_ALEN); ++ ++ return ieee80211_if_update_wds(dev, param->u.if_info.name, ++ &iwds, 1); ++ } else { ++ return -EOPNOTSUPP; ++ } ++} ++ ++ ++static int ieee80211_ioctl_flush_ifs(struct net_device *dev, ++ struct prism2_hostapd_param *param) ++{ ++ return ieee80211_if_flush(dev, 1); ++} ++ ++ ++static int ieee80211_ioctl_scan_req(struct net_device *dev, ++ struct prism2_hostapd_param *param, ++ int param_len) ++{ ++ u8 *pos = param->u.scan_req.ssid; ++ int left = param_len - ((u8 *) pos - (u8 *) param); ++ int len = param->u.scan_req.ssid_len; ++ ++ if (left < len || len > IEEE80211_MAX_SSID_LEN) ++ return -EINVAL; ++ ++ return ieee80211_sta_req_scan(dev, pos, len); ++} ++ ++ ++static int ieee80211_ioctl_sta_get_state(struct net_device *dev, ++ struct prism2_hostapd_param *param) ++{ ++ struct ieee80211_sub_if_data *sdata; ++ ++ sdata = IEEE80211_DEV_TO_SUB_IF(dev); ++ if (sdata->type != IEEE80211_SUB_IF_TYPE_STA) ++ return -EINVAL; ++ param->u.sta_get_state.state = sdata->u.sta.state; ++ return 0; ++} ++ ++ ++static int ieee80211_ioctl_mlme(struct net_device *dev, ++ struct prism2_hostapd_param *param) ++{ ++ struct ieee80211_sub_if_data *sdata; ++ ++ sdata = IEEE80211_DEV_TO_SUB_IF(dev); ++ if (sdata->type != IEEE80211_SUB_IF_TYPE_STA) ++ return -EINVAL; ++ switch (param->u.mlme.cmd) { ++ case MLME_STA_DEAUTH: ++ ieee80211_sta_deauthenticate(dev, param->u.mlme.reason_code); ++ break; ++ case MLME_STA_DISASSOC: ++ ieee80211_sta_disassociate(dev, param->u.mlme.reason_code); ++ break; ++ } ++ return 0; ++} ++ ++ ++static int ieee80211_ioctl_get_load_stats(struct net_device *dev, ++ struct prism2_hostapd_param *param) ++{ ++ struct ieee80211_local *local = dev->priv; ++ ++ param->u.get_load_stats.channel_use = local->channel_use; ++/* if (param->u.get_load_stats.flags & LOAD_STATS_CLEAR) ++ local->channel_use = 0; */ /* now it's not raw counter */ ++ ++ return 0; ++} ++ ++ ++static int ieee80211_ioctl_set_sta_vlan(struct net_device *dev, ++ struct prism2_hostapd_param *param) ++{ ++ struct ieee80211_local *local = dev->priv; ++ struct sta_info *sta; ++ ++ sta = sta_info_get(local, param->sta_addr); ++ if (sta) { ++ struct net_device *new_vlan_dev; ++ new_vlan_dev = ++ dev_get_by_name(param->u.set_sta_vlan.vlan_name); ++ if (new_vlan_dev) { ++#if 0 ++ printk("%s: Station " MACSTR " moved to vlan: %s\n", ++ dev->name, MAC2STR(param->sta_addr), ++ new_vlan_dev->name); ++#endif ++ sta->dev = new_vlan_dev; ++ sta->vlan_id = param->u.set_sta_vlan.vlan_id; ++ dev_put(new_vlan_dev); ++ } ++ sta_info_release(local, sta); ++ } ++ ++ return sta ? 0 : -ENOENT; ++} ++ ++ ++static int ieee80211_set_gen_ie(struct net_device *dev, u8 *ie, size_t len) ++{ ++ struct ieee80211_local *local = dev->priv; ++ struct ieee80211_sub_if_data *sdata; ++ ++ sdata = IEEE80211_DEV_TO_SUB_IF(dev); ++ if (sdata->type == IEEE80211_SUB_IF_TYPE_STA) ++ return ieee80211_sta_set_extra_ie(dev, ie, len); ++ ++ kfree(local->conf.generic_elem); ++ local->conf.generic_elem = kmalloc(len, GFP_KERNEL); ++ if (local->conf.generic_elem == NULL) ++ return -ENOMEM; ++ memcpy(local->conf.generic_elem, ie, len); ++ local->conf.generic_elem_len = len; ++ ++ return ieee80211_hw_config(dev); ++} ++ ++ ++static int ++ieee80211_ioctl_set_generic_info_elem(struct net_device *dev, ++ struct prism2_hostapd_param *param, ++ int param_len) ++{ ++ u8 *pos = param->u.set_generic_info_elem.data; ++ int left = param_len - ((u8 *) pos - (u8 *) param); ++ int len = param->u.set_generic_info_elem.len; ++ ++ if (left < len) ++ return -EINVAL; ++ ++ return ieee80211_set_gen_ie(dev, pos, len); ++} ++ ++ ++static int ieee80211_ioctl_set_regulatory_domain(struct net_device *dev, ++ struct prism2_hostapd_param *param) ++{ ++ struct ieee80211_conf *conf = ieee80211_get_hw_conf(dev); ++ conf->regulatory_domain = param->u.set_regulatory_domain.rd; ++ return 0; ++} ++ ++ ++static int ieee80211_ioctl_set_adm_status(struct net_device *dev, ++ int val) ++{ ++ struct ieee80211_conf *conf = ieee80211_get_hw_conf(dev); ++ conf->adm_status = val; ++ return ieee80211_hw_config(dev); ++} ++ ++static int ++ieee80211_ioctl_set_tx_queue_params(struct net_device *dev, ++ struct prism2_hostapd_param *param) ++{ ++ struct ieee80211_local *local = dev->priv; ++ struct ieee80211_tx_queue_params qparam; ++ ++ if (!local->hw->conf_tx) { ++ printk(KERN_DEBUG "%s: low-level driver does not support TX " ++ "queue configuration\n", dev->name); ++ return -EOPNOTSUPP; ++ } ++ ++ memset(&qparam, 0, sizeof(qparam)); ++ qparam.aifs = param->u.tx_queue_params.aifs; ++ qparam.cw_min = param->u.tx_queue_params.cw_min; ++ qparam.cw_max = param->u.tx_queue_params.cw_max; ++ qparam.burst_time = param->u.tx_queue_params.burst_time; ++ ++ return local->hw->conf_tx(dev, param->u.tx_queue_params.queue, ++ &qparam); ++} ++ ++ ++static int ieee80211_ioctl_get_tx_stats(struct net_device *dev, ++ struct prism2_hostapd_param *param) ++{ ++ struct ieee80211_local *local = dev->priv; ++ struct ieee80211_tx_queue_stats stats; ++ int ret, i; ++ ++ if (!local->hw->get_tx_stats) ++ return -EOPNOTSUPP; ++ ++ memset(&stats, 0, sizeof(stats)); ++ ret = local->hw->get_tx_stats(dev, &stats); ++ if (ret) ++ return ret; ++ ++ for (i = 0; i < 4; i++) { ++ param->u.get_tx_stats.data[i].len = stats.data[i].len; ++ param->u.get_tx_stats.data[i].limit = stats.data[i].limit; ++ param->u.get_tx_stats.data[i].count = stats.data[i].count; ++ } ++ ++ return 0; ++} ++ ++ ++int ieee80211_set_bss_count(struct net_device *dev, int new_count, ++ u8 *bssid_mask) ++{ ++ struct ieee80211_local *local = dev->priv; ++ struct ieee80211_conf *conf = ieee80211_get_hw_conf(dev); ++ int i, bss_count; ++ struct net_device **bss_devs, **prev; ++ struct net_device **sta_devs, **prev_sta_devs; ++ ++ bss_count = 0; ++ for (i = 0; i < conf->bss_count; i++) { ++ if (local->bss_devs[i]) ++ bss_count++; ++ } ++ ++ if (new_count < bss_count) { ++ printk(KERN_DEBUG "%s: invalid BSS count %d (in use: %d)\n", ++ dev->name, new_count, bss_count); ++ return -EINVAL; ++ } ++ ++ bss_devs = kmalloc(new_count * sizeof(struct net_device *), ++ GFP_KERNEL); ++ if (bss_devs == NULL) ++ return -ENOMEM; ++ sta_devs = kmalloc(new_count * sizeof(struct net_device *), ++ GFP_KERNEL); ++ if (sta_devs == NULL) { ++ kfree(bss_devs); ++ return -ENOMEM; ++ } ++ ++ spin_lock_bh(&local->sub_if_lock); ++ memcpy(bss_devs, local->bss_devs, ++ bss_count * sizeof(struct net_device *)); ++ memset(&bss_devs[bss_count], 0, ++ (new_count - bss_count) * sizeof(struct net_device *)); ++ ++ if (bssid_mask) ++ memcpy(conf->bssid_mask, bssid_mask, ETH_ALEN); ++ ++ prev = local->bss_devs; ++ local->bss_devs = bss_devs; ++ conf->bss_count = new_count; ++ ++ memcpy(sta_devs, local->sta_devs, ++ bss_count * sizeof(struct net_device *)); ++ memset(&sta_devs[bss_count], 0, ++ (new_count - bss_count) * sizeof(struct net_device *)); ++ prev_sta_devs = local->sta_devs; ++ local->sta_devs = sta_devs; ++ ++ spin_unlock_bh(&local->sub_if_lock); ++ kfree(prev); ++ kfree(prev_sta_devs); ++ ++ return ieee80211_hw_config(dev); ++} ++ ++static int ieee80211_ioctl_set_bss(struct net_device *dev, ++ struct prism2_hostapd_param *param) ++{ ++ return ieee80211_set_bss_count(dev, param->u.set_bss.bss_count, ++ param->u.set_bss.bssid_mask); ++} ++ ++ ++static int ieee80211_ioctl_set_channel_flag(struct net_device *dev, ++ struct prism2_hostapd_param *param) ++{ ++ struct ieee80211_local *local = dev->priv; ++ struct ieee80211_hw_modes *mode = NULL; ++ struct ieee80211_channel *chan = NULL; ++ int i; ++ ++ for (i = 0; i < local->hw->num_modes; i++) { ++ mode = &local->hw->modes[i]; ++ if (mode->mode == param->u.set_channel_flag.mode) ++ break; ++ mode = NULL; ++ } ++ ++ if (!mode) ++ return -ENOENT; ++ ++ for (i = 0; i < mode->num_channels; i++) { ++ chan = &mode->channels[i]; ++ if (chan->chan == param->u.set_channel_flag.chan) ++ break; ++ chan = NULL; ++ } ++ ++ if (!chan) ++ return -ENOENT; ++ ++ chan->flag = param->u.set_channel_flag.flag; ++ chan->power_level = param->u.set_channel_flag.power_level; ++ chan->antenna_max = param->u.set_channel_flag.antenna_max; ++ ++ return 0; ++} ++ ++ ++static int ieee80211_ioctl_set_quiet_params(struct net_device *dev, ++ struct prism2_hostapd_param *param) ++{ ++ struct ieee80211_conf *conf = ieee80211_get_hw_conf(dev); ++ conf->quiet_duration = param->u.quiet.duration; ++ conf->quiet_offset = param->u.quiet.offset; ++ conf->quiet_period = param->u.quiet.period; ++ return 0; ++} ++ ++ ++static int ieee80211_ioctl_set_radar_params(struct net_device *dev, ++ struct prism2_hostapd_param *param) ++{ ++ struct ieee80211_conf *conf = ieee80211_get_hw_conf(dev); ++ conf->radar_firpwr_threshold = param->u.radar.radar_firpwr_threshold; ++ conf->radar_rssi_threshold = param->u.radar.radar_rssi_threshold; ++ conf->pulse_height_threshold = param->u.radar.pulse_height_threshold; ++ conf->pulse_rssi_threshold = param->u.radar.pulse_rssi_threshold; ++ conf->pulse_inband_threshold = param->u.radar.pulse_inband_threshold; ++ return 0; ++} ++ ++ ++static int ieee80211_ioctl_priv_hostapd(struct net_device *dev, ++ struct iw_point *p) ++{ ++ struct prism2_hostapd_param *param; ++ int ret = 0; ++ ++ if (p->length < sizeof(struct prism2_hostapd_param) || ++ p->length > PRISM2_HOSTAPD_MAX_BUF_SIZE || !p->pointer) { ++ printk(KERN_DEBUG "%s: hostapd ioctl: ptr=%p len=%d min=%d " ++ "max=%d\n", dev->name, p->pointer, p->length, ++ (int)sizeof(struct prism2_hostapd_param), ++ PRISM2_HOSTAPD_MAX_BUF_SIZE); ++ return -EINVAL; ++ } ++ ++ param = (struct prism2_hostapd_param *) kmalloc(p->length, GFP_KERNEL); ++ if (param == NULL) ++ return -ENOMEM; ++ ++ if (copy_from_user(param, p->pointer, p->length)) { ++ ret = -EFAULT; ++ goto out; ++ } ++ ++ switch (param->cmd) { ++ case PRISM2_HOSTAPD_FLUSH: ++ ret = ieee80211_ioctl_flush(dev, param); ++ break; ++ case PRISM2_HOSTAPD_ADD_STA: ++ ret = ieee80211_ioctl_add_sta(dev, param); ++ break; ++ case PRISM2_HOSTAPD_REMOVE_STA: ++ ret = ieee80211_ioctl_remove_sta(dev, param); ++ break; ++ case PRISM2_HOSTAPD_GET_INFO_STA: ++ ret = ieee80211_ioctl_get_info_sta(dev, param); ++ break; ++ case PRISM2_SET_ENCRYPTION: ++ ret = ieee80211_ioctl_set_encryption(dev, param, p->length); ++ break; ++ case PRISM2_GET_ENCRYPTION: ++ ret = ieee80211_ioctl_get_encryption(dev, param, p->length); ++ break; ++ case PRISM2_HOSTAPD_SET_FLAGS_STA: ++ ret = ieee80211_ioctl_set_flags_sta(dev, param); ++ break; ++ case PRISM2_HOSTAPD_SET_BEACON: ++ ret = ieee80211_ioctl_set_beacon(dev, param, p->length, 0); ++ break; ++ case PRISM2_HOSTAPD_GET_HW_FEATURES: ++ ret = ieee80211_ioctl_get_hw_features(dev, param, p->length); ++ break; ++ case PRISM2_HOSTAPD_SCAN: ++ ret = ieee80211_ioctl_scan(dev, param); ++ break; ++#ifdef CONFIG_HOSTAPD_WPA_TESTING ++ case PRISM2_HOSTAPD_WPA_TRIGGER: ++ ret = ieee80211_ioctl_wpa_trigger(dev, param); ++ break; ++#endif /* CONFIG_HOSTAPD_WPA_TESTING */ ++ case PRISM2_HOSTAPD_SET_RATE_SETS: ++ ret = ieee80211_ioctl_set_rate_sets(dev, param, p->length); ++ break; ++ case PRISM2_HOSTAPD_ADD_IF: ++ ret = ieee80211_ioctl_add_if(dev, param, p->length); ++ break; ++ case PRISM2_HOSTAPD_REMOVE_IF: ++ ret = ieee80211_ioctl_remove_if(dev, param); ++ break; ++ case PRISM2_HOSTAPD_GET_DOT11COUNTERSTABLE: ++ ret = ieee80211_ioctl_get_dot11counterstable(dev, param); ++ break; ++ case PRISM2_HOSTAPD_GET_LOAD_STATS: ++ ret = ieee80211_ioctl_get_load_stats(dev, param); ++ break; ++ case PRISM2_HOSTAPD_SET_STA_VLAN: ++ ret = ieee80211_ioctl_set_sta_vlan(dev, param); ++ break; ++ case PRISM2_HOSTAPD_SET_GENERIC_INFO_ELEM: ++ ret = ieee80211_ioctl_set_generic_info_elem(dev, param, ++ p->length); ++ break; ++ case PRISM2_HOSTAPD_SET_CHANNEL_FLAG: ++ ret = ieee80211_ioctl_set_channel_flag(dev, param); ++ break; ++ case PRISM2_HOSTAPD_SET_REGULATORY_DOMAIN: ++ ret = ieee80211_ioctl_set_regulatory_domain(dev, param); ++ break; ++ case PRISM2_HOSTAPD_SET_TX_QUEUE_PARAMS: ++ ret = ieee80211_ioctl_set_tx_queue_params(dev, param); ++ break; ++ case PRISM2_HOSTAPD_SET_BSS: ++ ret = ieee80211_ioctl_set_bss(dev, param); ++ break; ++ case PRISM2_HOSTAPD_GET_TX_STATS: ++ ret = ieee80211_ioctl_get_tx_stats(dev, param); ++ break; ++ case PRISM2_HOSTAPD_UPDATE_IF: ++ ret = ieee80211_ioctl_update_if(dev, param, p->length); ++ break; ++ case PRISM2_HOSTAPD_SCAN_REQ: ++ ret = ieee80211_ioctl_scan_req(dev, param, p->length); ++ break; ++ case PRISM2_STA_GET_STATE: ++ ret = ieee80211_ioctl_sta_get_state(dev, param); ++ break; ++ case PRISM2_HOSTAPD_MLME: ++ ret = ieee80211_ioctl_mlme(dev, param); ++ break; ++ case PRISM2_HOSTAPD_FLUSH_IFS: ++ ret = ieee80211_ioctl_flush_ifs(dev, param); ++ break; ++ case PRISM2_HOSTAPD_SET_RADAR_PARAMS: ++ ret = ieee80211_ioctl_set_radar_params(dev, param); ++ break; ++ case PRISM2_HOSTAPD_SET_QUIET_PARAMS: ++ ret = ieee80211_ioctl_set_quiet_params(dev, param); ++ break; ++ default: ++ ret = -EOPNOTSUPP; ++ break; ++ } ++ ++ if (copy_to_user(p->pointer, param, p->length)) ++ ret = -EFAULT; ++ ++ out: ++ kfree(param); ++ ++ return ret; ++} ++ ++ ++static int ieee80211_ioctl_giwname(struct net_device *dev, ++ struct iw_request_info *info, ++ char *name, char *extra) ++{ ++ struct ieee80211_local *local = dev->priv; ++ ++ switch (local->conf.phymode) { ++ case MODE_IEEE80211A: ++ strcpy(name, "IEEE 802.11a"); ++ break; ++ case MODE_IEEE80211B: ++ strcpy(name, "IEEE 802.11b"); ++ break; ++ case MODE_IEEE80211G: ++ strcpy(name, "IEEE 802.11g"); ++ break; ++ case MODE_ATHEROS_TURBO: ++ strcpy(name, "5GHz Turbo"); ++ break; ++ default: ++ strcpy(name, "IEEE 802.11"); ++ break; ++ } ++ ++ return 0; ++} ++ ++ ++static int ieee80211_ioctl_giwrange(struct net_device *dev, ++ struct iw_request_info *info, ++ struct iw_point *data, char *extra) ++{ ++ struct iw_range *range = (struct iw_range *) extra; ++ ++ data->length = sizeof(struct iw_range); ++ memset(range, 0, sizeof(struct iw_range)); ++ ++ range->we_version_compiled = WIRELESS_EXT; ++ range->we_version_source = 14; ++ range->retry_capa = IW_RETRY_LIMIT; ++ range->retry_flags = IW_RETRY_LIMIT; ++ range->min_retry = 0; ++ range->max_retry = 255; ++ range->min_rts = 0; ++ range->max_rts = 2347; ++ range->min_frag = 256; ++ range->max_frag = 2346; ++ ++ return 0; ++} ++ ++ ++struct ieee80211_channel_range { ++ short start_freq; ++ short end_freq; ++ unsigned char power_level; ++ unsigned char antenna_max; ++}; ++ ++static const struct ieee80211_channel_range ieee80211_fcc_channels[] = { ++ { 2412, 2462, 27, 6 } /* IEEE 802.11b/g, channels 1..11 */, ++ { 5180, 5240, 17, 6 } /* IEEE 802.11a, channels 36..48 */, ++ { 5260, 5320, 23, 6 } /* IEEE 802.11a, channels 52..64 */, ++ { 5745, 5825, 30, 6 } /* IEEE 802.11a, channels 149..165, outdoor */, ++ { 0 } ++}; ++ ++static const struct ieee80211_channel_range ieee80211_mkk_channels[] = { ++ { 2412, 2472, 20, 6 } /* IEEE 802.11b/g, channels 1..13 */, ++ { 5170, 5240, 20, 6 } /* IEEE 802.11a, channels 34..48 */, ++ { 5260, 5320, 20, 6 } /* IEEE 802.11a, channels 52..64 */, ++ { 0 } ++}; ++ ++ ++static const struct ieee80211_channel_range *channel_range = ++ ieee80211_fcc_channels; ++ ++ ++static void ieee80211_unmask_channel(struct net_device *dev, int mode, ++ struct ieee80211_channel *chan) ++{ ++ int i; ++ ++ chan->flag = 0; ++ ++ if (ieee80211_regdom == 64 && ++ (mode == MODE_ATHEROS_TURBO || mode == MODE_ATHEROS_TURBOG)) { ++ /* Do not allow Turbo modes in Japan. */ ++ return; ++ } ++ ++ for (i = 0; channel_range[i].start_freq; i++) { ++ const struct ieee80211_channel_range *r = &channel_range[i]; ++ if (r->start_freq <= chan->freq && r->end_freq >= chan->freq) { ++ if (ieee80211_regdom == 64 && !ieee80211_japan_5ghz && ++ chan->freq >= 5260 && chan->freq <= 5320) { ++ /* ++ * Skip new channels in Japan since the ++ * firmware was not marked having been upgraded ++ * by the vendor. ++ */ ++ continue; ++ } ++ ++ if (ieee80211_regdom == 0x10 && ++ (chan->freq == 5190 || chan->freq == 5210 || ++ chan->freq == 5230)) { ++ /* Skip MKK channels when in FCC domain. */ ++ continue; ++ } ++ ++ chan->flag |= IEEE80211_CHAN_W_SCAN | ++ IEEE80211_CHAN_W_ACTIVE_SCAN | ++ IEEE80211_CHAN_W_IBSS; ++ chan->power_level = r->power_level; ++ chan->antenna_max = r->antenna_max; ++ ++ if (ieee80211_regdom == 64 && ++ (chan->freq == 5170 || chan->freq == 5190 || ++ chan->freq == 5210 || chan->freq == 5230)) { ++ /* ++ * New regulatory rules in Japan have backwards ++ * compatibility with old channels in 5.15-5.25 ++ * GHz band, but the station is not allowed to ++ * use active scan on these old channels. ++ */ ++ chan->flag &= ~IEEE80211_CHAN_W_ACTIVE_SCAN; ++ } ++ ++ if (ieee80211_regdom == 64 && ++ (chan->freq == 5260 || chan->freq == 5280 || ++ chan->freq == 5300 || chan->freq == 5320)) { ++ /* ++ * IBSS is not allowed on 5.25-5.35 GHz band ++ * due to radar detection requirements. ++ */ ++ chan->flag &= ~IEEE80211_CHAN_W_IBSS; ++ } ++ ++ break; ++ } ++ } ++} ++ ++ ++static int ieee80211_unmask_channels(struct net_device *dev) ++{ ++ struct ieee80211_local *local = dev->priv; ++ int m, c; ++ ++ for (m = 0; m < local->hw->num_modes; m++) { ++ struct ieee80211_hw_modes *mode = &local->hw->modes[m]; ++ for (c = 0; c < mode->num_channels; c++) { ++ ieee80211_unmask_channel(dev, mode->mode, ++ &mode->channels[c]); ++ } ++ } ++ return 0; ++} ++ ++ ++static int ieee80211_init_client(struct net_device *dev) ++{ ++ if (ieee80211_regdom == 0x40) ++ channel_range = ieee80211_mkk_channels; ++ ieee80211_unmask_channels(dev); ++ ieee80211_ioctl_set_adm_status(dev, 1); ++ return 0; ++} ++ ++ ++static int ieee80211_is_client_mode(int iw_mode) ++{ ++ return (iw_mode == IW_MODE_INFRA || iw_mode == IW_MODE_ADHOC); ++} ++ ++ ++static int ieee80211_ioctl_siwmode(struct net_device *dev, ++ struct iw_request_info *info, ++ __u32 *mode, char *extra) ++{ ++ struct ieee80211_local *local = dev->priv; ++ ++ if (!ieee80211_is_client_mode(local->conf.mode) && ++ ieee80211_is_client_mode(*mode)) { ++ ieee80211_init_client(dev); ++ } ++ if (local->conf.mode != *mode) { ++ struct ieee80211_sub_if_data *sdata = ++ IEEE80211_DEV_TO_SUB_IF(dev); ++ sta_info_flush(local, NULL); ++ if (local->conf.mode == IW_MODE_ADHOC && ++ sdata->type == IEEE80211_SUB_IF_TYPE_STA) { ++ /* Clear drop_unencrypted when leaving adhoc mode since ++ * only adhoc mode is using automatic setting for this ++ * in 80211.o. */ ++ sdata->drop_unencrypted = 0; ++ } ++ if (*mode == IW_MODE_MASTER) { ++ /* AP mode does not currently use ACM bits to limit ++ * TX, so clear the bitfield here. */ ++ local->wmm_acm = 0; ++ } ++ } ++ local->conf.mode = *mode; ++ return ieee80211_hw_config(dev); ++} ++ ++ ++static int ieee80211_ioctl_giwmode(struct net_device *dev, ++ struct iw_request_info *info, ++ __u32 *mode, char *extra) ++{ ++ struct ieee80211_local *local = dev->priv; ++ struct ieee80211_sub_if_data *sdata; ++ ++ sdata = IEEE80211_DEV_TO_SUB_IF(dev); ++ if (sdata->type == IEEE80211_SUB_IF_TYPE_STA) { ++ if (local->conf.mode == IW_MODE_ADHOC) ++ *mode = IW_MODE_ADHOC; ++ else ++ *mode = IW_MODE_INFRA; ++ } else ++ *mode = local->conf.mode; ++ return 0; ++} ++ ++ ++int ieee80211_ioctl_siwfreq(struct net_device *dev, ++ struct iw_request_info *info, ++ struct iw_freq *freq, char *extra) ++{ ++ struct ieee80211_local *local = dev->priv; ++ int m, c, nfreq, set = 0; ++ ++ /* freq->e == 0: freq->m = channel; otherwise freq = m * 10^e */ ++ if (freq->e == 0) ++ nfreq = -1; ++ else { ++ int i, div = 1000000; ++ for (i = 0; i < freq->e; i++) ++ div /= 10; ++ if (div > 0) ++ nfreq = freq->m / div; ++ else ++ return -EINVAL; ++ } ++ ++ for (m = 0; m < local->hw->num_modes; m++) { ++ struct ieee80211_hw_modes *mode = &local->hw->modes[m]; ++ for (c = 0; c < mode->num_channels; c++) { ++ struct ieee80211_channel *chan = &mode->channels[c]; ++ if (chan->flag & IEEE80211_CHAN_W_SCAN && ++ ((freq->e == 0 && chan->chan == freq->m) || ++ (freq->e > 0 && nfreq == chan->freq)) && ++ (local->hw_modes & (1 << mode->mode))) { ++ /* Use next_mode as the mode preference to ++ * resolve non-unique channel numbers. */ ++ if (set && mode->mode != local->next_mode) ++ continue; ++ ++ local->conf.channel = chan->chan; ++ local->conf.channel_val = chan->val; ++ local->conf.power_level = chan->power_level; ++ local->conf.freq = chan->freq; ++ local->conf.phymode = mode->mode; ++ local->conf.antenna_max = chan->antenna_max; ++ set++; ++ } ++ } ++ } ++ ++ if (set) { ++ local->sta_scanning = 0; /* Abort possible scan */ ++ return ieee80211_hw_config(dev); ++ } ++ ++ return -EINVAL; ++} ++ ++ ++static int ieee80211_ioctl_giwfreq(struct net_device *dev, ++ struct iw_request_info *info, ++ struct iw_freq *freq, char *extra) ++{ ++ struct ieee80211_local *local = dev->priv; ++ ++ /* TODO: in station mode (Managed/Ad-hoc) might need to poll low-level ++ * driver for the current channel with firmware-based management */ ++ ++ freq->m = local->conf.freq; ++ freq->e = 6; ++ ++ return 0; ++} ++ ++ ++static int ieee80211_ioctl_siwessid(struct net_device *dev, ++ struct iw_request_info *info, ++ struct iw_point *data, char *ssid) ++{ ++ struct ieee80211_local *local = dev->priv; ++ struct ieee80211_sub_if_data *sdata; ++ size_t len = data->length; ++ ++ /* iwconfig uses nul termination in SSID.. */ ++ if (len > 0 && ssid[len - 1] == '\0') ++ len--; ++ ++ sdata = IEEE80211_DEV_TO_SUB_IF(dev); ++ if (sdata->type == IEEE80211_SUB_IF_TYPE_STA) ++ return ieee80211_sta_set_ssid(dev, ssid, len); ++ ++ kfree(local->conf.ssid); ++ local->conf.ssid = kmalloc(len + 1, GFP_KERNEL); ++ if (local->conf.ssid == NULL) ++ return -ENOMEM; ++ memcpy(local->conf.ssid, ssid, len); ++ local->conf.ssid[len] = '\0'; ++ local->conf.ssid_len = len; ++ return ieee80211_hw_config(dev); ++} ++ ++ ++static int ieee80211_ioctl_giwessid(struct net_device *dev, ++ struct iw_request_info *info, ++ struct iw_point *data, char *ssid) ++{ ++ struct ieee80211_local *local = dev->priv; ++ size_t len; ++ ++ struct ieee80211_sub_if_data *sdata; ++ sdata = IEEE80211_DEV_TO_SUB_IF(dev); ++ if (sdata->type == IEEE80211_SUB_IF_TYPE_STA) { ++ int res = ieee80211_sta_get_ssid(dev, ssid, &len); ++ if (res == 0) ++ data->length = len; ++ return res; ++ } ++ ++ len = local->conf.ssid_len; ++ if (len > IW_ESSID_MAX_SIZE) ++ len = IW_ESSID_MAX_SIZE; ++ memcpy(ssid, local->conf.ssid, len); ++ data->length = len; ++ return 0; ++} ++ ++ ++static int ieee80211_ioctl_siwap(struct net_device *dev, ++ struct iw_request_info *info, ++ struct sockaddr *ap_addr, char *extra) ++{ ++ struct ieee80211_local *local = dev->priv; ++ struct ieee80211_sub_if_data *sdata; ++ ++ sdata = IEEE80211_DEV_TO_SUB_IF(dev); ++ if (sdata->type == IEEE80211_SUB_IF_TYPE_STA) { ++ int changed_bssid = 0; ++ if (memcmp(local->conf.client_bssid, (u8 *) &ap_addr->sa_data, ++ ETH_ALEN) != 0) ++ changed_bssid = 1; ++ memcpy(local->conf.client_bssid, (u8 *) &ap_addr->sa_data, ++ ETH_ALEN); ++ if (changed_bssid && ieee80211_hw_config(dev)) { ++ printk(KERN_DEBUG "%s: Failed to config new BSSID to " ++ "the low-level driver\n", dev->name); ++ } ++ return ieee80211_sta_set_bssid(dev, (u8 *) &ap_addr->sa_data); ++ } ++ ++ return -EOPNOTSUPP; ++} ++ ++ ++static int ieee80211_ioctl_giwap(struct net_device *dev, ++ struct iw_request_info *info, ++ struct sockaddr *ap_addr, char *extra) ++{ ++ struct ieee80211_sub_if_data *sdata; ++ ++ sdata = IEEE80211_DEV_TO_SUB_IF(dev); ++ if (sdata->type == IEEE80211_SUB_IF_TYPE_STA) { ++ ap_addr->sa_family = ARPHRD_ETHER; ++ memcpy(&ap_addr->sa_data, sdata->u.sta.bssid, ETH_ALEN); ++ return 0; ++ } ++ ++ return -EOPNOTSUPP; ++} ++ ++ ++static int ieee80211_ioctl_siwscan(struct net_device *dev, ++ struct iw_request_info *info, ++ struct iw_point *data, char *extra) ++{ ++ struct ieee80211_local *local = dev->priv; ++ u8 *ssid = NULL; ++ size_t ssid_len = 0; ++ ++ if (local->scan_flags & IEEE80211_SCAN_MATCH_SSID) { ++ ssid = local->conf.ssid; ++ ssid_len = local->conf.ssid_len; ++ } ++ return ieee80211_sta_req_scan(dev, ssid, ssid_len); ++} ++ ++ ++static int ieee80211_ioctl_giwscan(struct net_device *dev, ++ struct iw_request_info *info, ++ struct iw_point *data, char *extra) ++{ ++ int res; ++ struct ieee80211_local *local = dev->priv; ++ if (local->sta_scanning) ++ return -EAGAIN; ++ res = ieee80211_sta_scan_results(dev, extra, IW_SCAN_MAX_DATA); ++ if (res >= 0) { ++ data->length = res; ++ return 0; ++ } ++ data->length = 0; ++ return res; ++} ++ ++ ++static int ieee80211_ioctl_siwrts(struct net_device *dev, ++ struct iw_request_info *info, ++ struct iw_param *rts, char *extra) ++{ ++ struct ieee80211_local *local = dev->priv; ++ ++ if (rts->disabled) ++ local->rts_threshold = IEEE80211_MAX_RTS_THRESHOLD; ++ else if (rts->value < 0 || rts->value > IEEE80211_MAX_RTS_THRESHOLD) ++ return -EINVAL; ++ else ++ local->rts_threshold = rts->value; ++ ++ /* If the wlan card performs RTS/CTS in hardware/firmware, ++ * configure it here */ ++ ++ if (local->hw->set_rts_threshold) { ++ local->hw->set_rts_threshold(dev, local->rts_threshold); ++ } ++ ++ return 0; ++} ++ ++static int ieee80211_ioctl_giwrts(struct net_device *dev, ++ struct iw_request_info *info, ++ struct iw_param *rts, char *extra) ++{ ++ struct ieee80211_local *local = dev->priv; ++ ++ rts->value = local->rts_threshold; ++ rts->disabled = (rts->value >= IEEE80211_MAX_RTS_THRESHOLD); ++ rts->fixed = 1; ++ ++ return 0; ++} ++ ++ ++static int ieee80211_ioctl_siwfrag(struct net_device *dev, ++ struct iw_request_info *info, ++ struct iw_param *frag, char *extra) ++{ ++ struct ieee80211_local *local = dev->priv; ++ ++ if (frag->disabled) ++ local->fragmentation_threshold = IEEE80211_MAX_FRAG_THRESHOLD; ++ else if (frag->value < 256 || ++ frag->value > IEEE80211_MAX_FRAG_THRESHOLD) ++ return -EINVAL; ++ else { ++ /* Fragment length must be even, so strip LSB. */ ++ local->fragmentation_threshold = frag->value & ~0x1; ++ } ++ ++ /* If the wlan card performs fragmentation in hardware/firmware, ++ * configure it here */ ++ ++ if (local->hw->set_frag_threshold) { ++ local->hw->set_frag_threshold( ++ dev, local->fragmentation_threshold); ++ } ++ ++ return 0; ++} ++ ++static int ieee80211_ioctl_giwfrag(struct net_device *dev, ++ struct iw_request_info *info, ++ struct iw_param *frag, char *extra) ++{ ++ struct ieee80211_local *local = dev->priv; ++ ++ frag->value = local->fragmentation_threshold; ++ frag->disabled = (frag->value >= IEEE80211_MAX_RTS_THRESHOLD); ++ frag->fixed = 1; ++ ++ return 0; ++} ++ ++ ++static int ieee80211_ioctl_siwretry(struct net_device *dev, ++ struct iw_request_info *info, ++ struct iw_param *retry, char *extra) ++{ ++ struct ieee80211_local *local = dev->priv; ++ ++ if (retry->disabled || ++ (retry->flags & IW_RETRY_TYPE) != IW_RETRY_LIMIT) ++ return -EINVAL; ++ ++ if (retry->flags & IW_RETRY_MAX) ++ local->long_retry_limit = retry->value; ++ else if (retry->flags & IW_RETRY_MIN) ++ local->short_retry_limit = retry->value; ++ else { ++ local->long_retry_limit = retry->value; ++ local->short_retry_limit = retry->value; ++ } ++ ++ if (local->hw->set_retry_limit) { ++ return local->hw->set_retry_limit( ++ dev, local->short_retry_limit, ++ local->long_retry_limit); ++ } ++ ++ return 0; ++} ++ ++ ++static int ieee80211_ioctl_giwretry(struct net_device *dev, ++ struct iw_request_info *info, ++ struct iw_param *retry, char *extra) ++{ ++ struct ieee80211_local *local = dev->priv; ++ ++ retry->disabled = 0; ++ if ((retry->flags & IW_RETRY_TYPE) != IW_RETRY_LIMIT) ++ return -EINVAL; ++ if (retry->flags & IW_RETRY_MAX) { ++ retry->flags = IW_RETRY_LIMIT | IW_RETRY_MAX; ++ retry->value = local->long_retry_limit; ++ } else { ++ retry->flags = IW_RETRY_LIMIT; ++ retry->value = local->short_retry_limit; ++ if (local->long_retry_limit != local->short_retry_limit) ++ retry->flags |= IW_RETRY_MIN; ++ } ++ ++ return 0; ++} ++ ++ ++static void ieee80211_ioctl_unmask_channels(struct ieee80211_local *local) ++{ ++ int m, c; ++ ++ for (m = 0; m < local->hw->num_modes; m++) { ++ struct ieee80211_hw_modes *mode = &local->hw->modes[m]; ++ for (c = 0; c < mode->num_channels; c++) { ++ struct ieee80211_channel *chan = &mode->channels[c]; ++ chan->flag |= IEEE80211_CHAN_W_SCAN; ++ } ++ } ++} ++ ++ ++static int ieee80211_ioctl_test_mode(struct net_device *dev, int mode) ++{ ++ struct ieee80211_local *local = dev->priv; ++ int ret = -EOPNOTSUPP; ++ ++ if (mode == IEEE80211_TEST_UNMASK_CHANNELS) { ++ ieee80211_ioctl_unmask_channels(local); ++ ret = 0; ++ } ++ ++ if (local->hw->test_mode) ++ ret = local->hw->test_mode(dev, mode); ++ ++ return ret; ++} ++ ++ ++static int ieee80211_ioctl_clear_keys(struct net_device *dev) ++{ ++ struct ieee80211_local *local = dev->priv; ++ struct ieee80211_key_conf key; ++ struct list_head *ptr; ++ int i; ++ u8 addr[ETH_ALEN]; ++ struct ieee80211_key_conf *keyconf; ++ ++ memset(addr, 0xff, ETH_ALEN); ++ list_for_each(ptr, &local->sub_if_list) { ++ struct ieee80211_sub_if_data *sdata = ++ list_entry(ptr, struct ieee80211_sub_if_data, list); ++ for (i = 0; i < NUM_DEFAULT_KEYS; i++) { ++ keyconf = NULL; ++ if (sdata->keys[i] && ++ !sdata->keys[i]->force_sw_encrypt && ++ local->hw->set_key && ++ (keyconf = ieee80211_key_data2conf(local, ++ sdata->keys[i])) ++ != NULL) ++ local->hw->set_key(dev, DISABLE_KEY, addr, ++ keyconf, 0); ++ kfree(keyconf); ++ kfree(sdata->keys[i]); ++ sdata->keys[i] = NULL; ++ } ++ sdata->default_key = NULL; ++ } ++ ++ spin_lock_bh(&local->sta_lock); ++ list_for_each(ptr, &local->sta_list) { ++ struct sta_info *sta = ++ list_entry(ptr, struct sta_info, list); ++ keyconf = NULL; ++ if (sta->key && !sta->key->force_sw_encrypt && ++ local->hw->set_key && ++ (keyconf = ieee80211_key_data2conf(local, sta->key)) ++ != NULL) ++ local->hw->set_key(dev, DISABLE_KEY, sta->addr, ++ keyconf, sta->aid); ++ kfree(keyconf); ++ kfree(sta->key); ++ sta->key = NULL; ++ } ++ spin_unlock_bh(&local->sta_lock); ++ ++ memset(&key, 0, sizeof(key)); ++ if (local->hw->set_key && ++ local->hw->set_key(dev, REMOVE_ALL_KEYS, NULL, ++ &key, 0)) ++ printk(KERN_DEBUG "%s: failed to remove hwaccel keys\n", ++ dev->name); ++ ++ return 0; ++} ++ ++ ++static int ++ieee80211_ioctl_force_unicast_rate(struct net_device *dev, ++ struct ieee80211_sub_if_data *sdata, ++ int rate) ++{ ++ struct ieee80211_local *local = dev->priv; ++ int i; ++ ++ if (sdata->type != IEEE80211_SUB_IF_TYPE_AP) ++ return -ENOENT; ++ ++ if (rate == 0) { ++ sdata->u.ap.force_unicast_rateidx = -1; ++ return 0; ++ } ++ ++ for (i = 0; i < local->num_curr_rates; i++) { ++ if (local->curr_rates[i].rate == rate) { ++ sdata->u.ap.force_unicast_rateidx = i; ++ return 0; ++ } ++ } ++ return -EINVAL; ++} ++ ++ ++static int ++ieee80211_ioctl_max_ratectrl_rate(struct net_device *dev, ++ struct ieee80211_sub_if_data *sdata, ++ int rate) ++{ ++ struct ieee80211_local *local = dev->priv; ++ int i; ++ ++ if (sdata->type != IEEE80211_SUB_IF_TYPE_AP) ++ return -ENOENT; ++ ++ if (rate == 0) { ++ sdata->u.ap.max_ratectrl_rateidx = -1; ++ return 0; ++ } ++ ++ for (i = 0; i < local->num_curr_rates; i++) { ++ if (local->curr_rates[i].rate == rate) { ++ sdata->u.ap.max_ratectrl_rateidx = i; ++ return 0; ++ } ++ } ++ return -EINVAL; ++} ++ ++ ++static void ieee80211_key_enable_hwaccel(struct ieee80211_local *local, ++ struct ieee80211_key *key) ++{ ++ struct ieee80211_key_conf *keyconf; ++ u8 addr[ETH_ALEN]; ++ ++ if (key == NULL || key->alg != ALG_WEP || !key->force_sw_encrypt || ++ local->hw->device_hides_wep) ++ return; ++ ++ memset(addr, 0xff, ETH_ALEN); ++ keyconf = ieee80211_key_data2conf(local, key); ++ if (keyconf && local->hw->set_key && ++ local->hw->set_key(local->mdev, SET_KEY, addr, keyconf, 0) == 0) { ++ key->force_sw_encrypt = keyconf->force_sw_encrypt; ++ key->hw_key_idx = keyconf->hw_key_idx; ++ } ++ kfree(keyconf); ++} ++ ++ ++static void ieee80211_key_disable_hwaccel(struct ieee80211_local *local, ++ struct ieee80211_key *key) ++{ ++ struct ieee80211_key_conf *keyconf; ++ u8 addr[ETH_ALEN]; ++ ++ if (key == NULL || key->alg != ALG_WEP || key->force_sw_encrypt || ++ local->hw->device_hides_wep) ++ return; ++ ++ memset(addr, 0xff, ETH_ALEN); ++ keyconf = ieee80211_key_data2conf(local, key); ++ if (keyconf && local->hw->set_key) ++ local->hw->set_key(local->mdev, DISABLE_KEY, addr, keyconf, 0); ++ kfree(keyconf); ++ key->force_sw_encrypt = 1; ++} ++ ++ ++static int ieee80211_ioctl_default_wep_only(struct ieee80211_local *local, ++ int value) ++{ ++ int i; ++ struct list_head *ptr; ++ ++ local->default_wep_only = value; ++ list_for_each(ptr, &local->sub_if_list) { ++ struct ieee80211_sub_if_data *sdata = ++ list_entry(ptr, struct ieee80211_sub_if_data, list); ++ for (i = 0; i < NUM_DEFAULT_KEYS; i++) { ++ if (value) { ++ ieee80211_key_enable_hwaccel(local, ++ sdata->keys[i]); ++ } else { ++ ieee80211_key_disable_hwaccel(local, ++ sdata->keys[i]); ++ } ++ } ++ } ++ ++ return 0; ++} ++ ++ ++static int ieee80211_ioctl_prism2_param(struct net_device *dev, ++ struct iw_request_info *info, ++ void *wrqu, char *extra) ++{ ++ struct ieee80211_local *local = dev->priv; ++ struct ieee80211_sub_if_data *sdata; ++ int *i = (int *) extra; ++ int param = *i; ++ int value = *(i + 1); ++ int ret = 0; ++ ++ if (!capable(CAP_NET_ADMIN)) ++ return -EPERM; ++ ++ sdata = IEEE80211_DEV_TO_SUB_IF(dev); ++ ++ switch (param) { ++ case PRISM2_PARAM_HOST_ENCRYPT: ++ case PRISM2_PARAM_HOST_DECRYPT: ++ /* TODO: implement these; return success now to prevent ++ * hostapd from aborting */ ++ break; ++ ++ case PRISM2_PARAM_BEACON_INT: ++ local->conf.beacon_int = value; ++ if (ieee80211_hw_config(dev)) ++ ret = -EINVAL; ++ break; ++ ++ case PRISM2_PARAM_AP_BRIDGE_PACKETS: ++ local->bridge_packets = value; ++ break; ++ ++ case PRISM2_PARAM_AP_AUTH_ALGS: ++ if (sdata->type == IEEE80211_SUB_IF_TYPE_STA) { ++ sdata->u.sta.auth_algs = value; ++ } else ++ ret = -EOPNOTSUPP; ++ break; ++ ++ case PRISM2_PARAM_DTIM_PERIOD: ++ if (value < 1) ++ ret = -EINVAL; ++ else if (sdata->type != IEEE80211_SUB_IF_TYPE_AP) ++ ret = -ENOENT; ++ else ++ sdata->u.ap.dtim_period = value; ++ break; ++ ++ case PRISM2_PARAM_IEEE_802_1X: ++ sdata->ieee802_1x = value; ++ if (local->hw->set_ieee8021x && ++ local->hw->set_ieee8021x(dev, value)) ++ printk(KERN_DEBUG "%s: failed to set IEEE 802.1X (%d) " ++ "for low-level driver\n", dev->name, value); ++ break; ++ ++ case PRISM2_PARAM_CTS_PROTECT_ERP_FRAMES: ++ local->cts_protect_erp_frames = value; ++ break; ++ ++ case PRISM2_PARAM_DROP_UNENCRYPTED: ++ sdata->drop_unencrypted = value; ++ break; ++ ++ case PRISM2_PARAM_PREAMBLE: ++ local->short_preamble = value; ++ break; ++ ++ case PRISM2_PARAM_RATE_LIMIT_BURST: ++ local->rate_limit_burst = value; ++ local->rate_limit_bucket = value; ++ break; ++ ++ case PRISM2_PARAM_RATE_LIMIT: ++ /* number of packets (tokens) allowed per second */ ++ if (!local->rate_limit && value) { ++ if (!local->rate_limit_burst) local->rate_limit_burst = ++ value; ++ local->rate_limit_bucket = local->rate_limit_burst; ++ local->rate_limit_timer.expires = jiffies + HZ; ++ add_timer(&local->rate_limit_timer); ++ } else if (local->rate_limit && !value) { ++ del_timer_sync(&local->rate_limit_timer); ++ } ++ local->rate_limit = value; ++ break; ++ ++ case PRISM2_PARAM_STAT_TIME: ++ if (!local->stat_time && value) { ++ local->stat_timer.expires = jiffies + HZ * value / 100; ++ add_timer(&local->stat_timer); ++ } else if (local->stat_time && !value) { ++ del_timer_sync(&local->stat_timer); ++ } ++ local->stat_time = value; ++ break; ++ case PRISM2_PARAM_SHORT_SLOT_TIME: ++ local->conf.short_slot_time = value; ++ if (ieee80211_hw_config(dev)) ++ ret = -EINVAL; ++ break; ++ ++ case PRISM2_PARAM_PRIVACY_INVOKED: ++ if (local->hw->set_privacy_invoked) ++ ret = local->hw->set_privacy_invoked(dev, value); ++ break; ++ ++ case PRISM2_PARAM_TEST_MODE: ++ ret = ieee80211_ioctl_test_mode(dev, value); ++ break; ++ ++ case PRISM2_PARAM_NEXT_MODE: ++ local->next_mode = value; ++ break; ++ ++ case PRISM2_PARAM_CLEAR_KEYS: ++ ret = ieee80211_ioctl_clear_keys(dev); ++ break; ++ ++ case PRISM2_PARAM_ADM_STATUS: ++ ret = ieee80211_ioctl_set_adm_status(dev, value); ++ break; ++ ++ case PRISM2_PARAM_ANTENNA_SEL: ++ local->conf.antenna_sel = value; ++ if (ieee80211_hw_config(dev)) ++ ret = -EINVAL; ++ break; ++ ++ case PRISM2_PARAM_CALIB_INT: ++ local->conf.calib_int = value; ++ if (ieee80211_hw_config(dev)) ++ ret = -EINVAL; ++ break; ++ ++ case PRISM2_PARAM_ANTENNA_MODE: ++ local->conf.antenna_mode = value; ++ if (ieee80211_hw_config(dev)) ++ ret = -EINVAL; ++ break; ++ ++ case PRISM2_PARAM_BROADCAST_SSID: ++ if ((value < 0) || (value > 1)) ++ ret = -EINVAL; ++ else ++ local->conf.ssid_hidden = value; ++ break; ++ ++ case PRISM2_PARAM_STA_ANTENNA_SEL: ++ local->sta_antenna_sel = value; ++ break; ++ ++ case PRISM2_PARAM_FORCE_UNICAST_RATE: ++ ret = ieee80211_ioctl_force_unicast_rate(dev, sdata, value); ++ break; ++ ++ case PRISM2_PARAM_MAX_RATECTRL_RATE: ++ ret = ieee80211_ioctl_max_ratectrl_rate(dev, sdata, value); ++ break; ++ ++ case PRISM2_PARAM_RATE_CTRL_NUM_UP: ++ local->rate_ctrl_num_up = value; ++ break; ++ ++ case PRISM2_PARAM_RATE_CTRL_NUM_DOWN: ++ local->rate_ctrl_num_down = value; ++ break; ++ ++ case PRISM2_PARAM_TX_POWER_REDUCTION: ++ if (value < 0) ++ ret = -EINVAL; ++ else ++ local->conf.tx_power_reduction = value; ++ break; ++ ++ case PRISM2_PARAM_EAPOL: ++ sdata->eapol = value; ++ break; ++ ++ case PRISM2_PARAM_KEY_TX_RX_THRESHOLD: ++ local->key_tx_rx_threshold = value; ++ break; ++ ++ case PRISM2_PARAM_KEY_INDEX: ++ if (value < 0 || value >= NUM_DEFAULT_KEYS) ++ ret = -EINVAL; ++ else if (sdata->keys[value] == NULL) ++ ret = -ENOENT; ++ else ++ sdata->default_key = sdata->keys[value]; ++ break; ++ ++ case PRISM2_PARAM_DEFAULT_WEP_ONLY: ++ ret = ieee80211_ioctl_default_wep_only(local, value); ++ break; ++ ++ case PRISM2_PARAM_WIFI_WME_NOACK_TEST: ++ local->wifi_wme_noack_test = value; ++ break; ++ ++ case PRISM2_PARAM_ALLOW_BROADCAST_ALWAYS: ++ local->allow_broadcast_always = value; ++ break; ++ ++ case PRISM2_PARAM_SCAN_FLAGS: ++ local->scan_flags = value; ++ break; ++ ++ case PRISM2_PARAM_MIXED_CELL: ++ if (sdata->type != IEEE80211_SUB_IF_TYPE_STA) ++ ret = -EINVAL; ++ else ++ sdata->u.sta.mixed_cell = !!value; ++ break; ++ ++ case PRISM2_PARAM_KEY_MGMT: ++ if (sdata->type != IEEE80211_SUB_IF_TYPE_STA) ++ ret = -EINVAL; ++ else ++ sdata->u.sta.key_mgmt = value; ++ break; ++ ++ case PRISM2_PARAM_HW_MODES: ++ local->hw_modes = value; ++ break; ++ ++ case PRISM2_PARAM_CREATE_IBSS: ++ if (sdata->type != IEEE80211_SUB_IF_TYPE_STA) ++ ret = -EINVAL; ++ else ++ sdata->u.sta.create_ibss = !!value; ++ break; ++ case PRISM2_PARAM_WMM_ENABLED: ++ if (sdata->type != IEEE80211_SUB_IF_TYPE_STA) ++ ret = -EINVAL; ++ else ++ sdata->u.sta.wmm_enabled = !!value; ++ break; ++ case PRISM2_PARAM_RADAR_DETECT: ++ local->conf.radar_detect = value; ++ break; ++ case PRISM2_PARAM_SPECTRUM_MGMT: ++ local->conf.spect_mgmt = value; ++ break; ++ default: ++ ret = -EOPNOTSUPP; ++ break; ++ } ++ ++ return ret; ++} ++ ++ ++static int ieee80211_ioctl_get_prism2_param(struct net_device *dev, ++ struct iw_request_info *info, ++ void *wrqu, char *extra) ++{ ++ struct ieee80211_local *local = dev->priv; ++ struct ieee80211_sub_if_data *sdata; ++ int *param = (int *) extra; ++ int ret = 0; ++ ++ sdata = IEEE80211_DEV_TO_SUB_IF(dev); ++ ++ switch (*param) { ++ case PRISM2_PARAM_BEACON_INT: ++ *param = local->conf.beacon_int; ++ break; ++ ++ case PRISM2_PARAM_AP_BRIDGE_PACKETS: ++ *param = local->bridge_packets; ++ break; ++ ++ case PRISM2_PARAM_AP_AUTH_ALGS: ++ if (sdata->type == IEEE80211_SUB_IF_TYPE_STA) { ++ *param = sdata->u.sta.auth_algs; ++ } else ++ ret = -EOPNOTSUPP; ++ break; ++ ++ case PRISM2_PARAM_DTIM_PERIOD: ++ if (sdata->type != IEEE80211_SUB_IF_TYPE_AP) ++ ret = -ENOENT; ++ else ++ *param = sdata->u.ap.dtim_period; ++ break; ++ ++ case PRISM2_PARAM_IEEE_802_1X: ++ *param = sdata->ieee802_1x; ++ break; ++ ++ case PRISM2_PARAM_CTS_PROTECT_ERP_FRAMES: ++ *param = local->cts_protect_erp_frames; ++ break; ++ ++ case PRISM2_PARAM_DROP_UNENCRYPTED: ++ *param = sdata->drop_unencrypted; ++ break; ++ ++ case PRISM2_PARAM_PREAMBLE: ++ *param = local->short_preamble; ++ break; ++ ++ case PRISM2_PARAM_RATE_LIMIT_BURST: ++ *param = local->rate_limit_burst; ++ break; ++ ++ case PRISM2_PARAM_RATE_LIMIT: ++ *param = local->rate_limit; ++ break; ++ ++ case PRISM2_PARAM_STAT_TIME: ++ *param = local->stat_time; ++ break; ++ case PRISM2_PARAM_SHORT_SLOT_TIME: ++ *param = local->conf.short_slot_time; ++ break; ++ ++ case PRISM2_PARAM_NEXT_MODE: ++ *param = local->next_mode; ++ break; ++ ++ case PRISM2_PARAM_ANTENNA_SEL: ++ *param = local->conf.antenna_sel; ++ break; ++ ++ case PRISM2_PARAM_CALIB_INT: ++ *param = local->conf.calib_int; ++ break; ++ ++ case PRISM2_PARAM_ANTENNA_MODE: ++ *param = local->conf.antenna_mode; ++ break; ++ ++ case PRISM2_PARAM_BROADCAST_SSID: ++ *param = local->conf.ssid_hidden; ++ break; ++ ++ case PRISM2_PARAM_STA_ANTENNA_SEL: ++ *param = local->sta_antenna_sel; ++ break; ++ ++ case PRISM2_PARAM_RATE_CTRL_NUM_UP: ++ *param = local->rate_ctrl_num_up; ++ break; ++ ++ case PRISM2_PARAM_RATE_CTRL_NUM_DOWN: ++ *param = local->rate_ctrl_num_down; ++ break; ++ ++ case PRISM2_PARAM_TX_POWER_REDUCTION: ++ *param = local->conf.tx_power_reduction; ++ break; ++ ++ case PRISM2_PARAM_EAPOL: ++ *param = sdata->eapol; ++ break; ++ ++ case PRISM2_PARAM_KEY_TX_RX_THRESHOLD: ++ *param = local->key_tx_rx_threshold; ++ break; ++ ++ case PRISM2_PARAM_KEY_INDEX: ++ if (sdata->default_key == NULL) ++ ret = -ENOENT; ++ else if (sdata->default_key == sdata->keys[0]) ++ *param = 0; ++ else if (sdata->default_key == sdata->keys[1]) ++ *param = 1; ++ else if (sdata->default_key == sdata->keys[2]) ++ *param = 2; ++ else if (sdata->default_key == sdata->keys[3]) ++ *param = 3; ++ else ++ ret = -ENOENT; ++ break; ++ ++ case PRISM2_PARAM_DEFAULT_WEP_ONLY: ++ *param = local->default_wep_only; ++ break; ++ ++ case PRISM2_PARAM_WIFI_WME_NOACK_TEST: ++ *param = local->wifi_wme_noack_test; ++ break; ++ ++ case PRISM2_PARAM_ALLOW_BROADCAST_ALWAYS: ++ *param = local->allow_broadcast_always; ++ break; ++ ++ case PRISM2_PARAM_SCAN_FLAGS: ++ *param = local->scan_flags; ++ break; ++ ++ case PRISM2_PARAM_HW_MODES: ++ *param = local->hw_modes; ++ break; ++ ++ case PRISM2_PARAM_CREATE_IBSS: ++ if (sdata->type != IEEE80211_SUB_IF_TYPE_STA) ++ ret = -EINVAL; ++ else ++ *param = !!sdata->u.sta.create_ibss; ++ break; ++ ++ case PRISM2_PARAM_MIXED_CELL: ++ if (sdata->type != IEEE80211_SUB_IF_TYPE_STA) ++ ret = -EINVAL; ++ else ++ *param = !!sdata->u.sta.mixed_cell; ++ break; ++ ++ case PRISM2_PARAM_KEY_MGMT: ++ if (sdata->type != IEEE80211_SUB_IF_TYPE_STA) ++ ret = -EINVAL; ++ else ++ *param = sdata->u.sta.key_mgmt; ++ break; ++ case PRISM2_PARAM_WMM_ENABLED: ++ if (sdata->type != IEEE80211_SUB_IF_TYPE_STA) ++ ret = -EINVAL; ++ else ++ *param = !!sdata->u.sta.wmm_enabled; ++ break; ++ ++ default: ++ ret = -EOPNOTSUPP; ++ break; ++ } ++ ++ return ret; ++} ++ ++ ++static int ieee80211_ioctl_test_param(struct net_device *dev, ++ struct iw_request_info *info, ++ void *wrqu, char *extra) ++{ ++ struct ieee80211_local *local = dev->priv; ++ int *i = (int *) extra; ++ int param = *i; ++ int value = *(i + 1); ++ ++ if (!capable(CAP_NET_ADMIN)) ++ return -EPERM; ++ ++ if (local->hw->test_param) ++ return local->hw->test_param(local->mdev, param, value); ++ ++ return -EOPNOTSUPP; ++} ++ ++ ++static int ieee80211_ioctl_siwmlme(struct net_device *dev, ++ struct iw_request_info *info, ++ struct iw_point *data, char *extra) ++{ ++ struct ieee80211_sub_if_data *sdata; ++ struct iw_mlme *mlme = (struct iw_mlme *) extra; ++ ++ sdata = IEEE80211_DEV_TO_SUB_IF(dev); ++ if (sdata->type != IEEE80211_SUB_IF_TYPE_STA) ++ return -EINVAL; ++ ++ switch (mlme->cmd) { ++ case IW_MLME_DEAUTH: ++ /* TODO: mlme->addr.sa_data */ ++ return ieee80211_sta_deauthenticate(dev, mlme->reason_code); ++ case IW_MLME_DISASSOC: ++ /* TODO: mlme->addr.sa_data */ ++ return ieee80211_sta_disassociate(dev, mlme->reason_code); ++ default: ++ return -EOPNOTSUPP; ++ } ++} ++ ++ ++static int ieee80211_ioctl_siwencode(struct net_device *dev, ++ struct iw_request_info *info, ++ struct iw_point *erq, char *keybuf) ++{ ++ struct ieee80211_sub_if_data *sdata; ++ int idx, i, alg = ALG_WEP; ++ u8 bcaddr[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; ++ ++ sdata = IEEE80211_DEV_TO_SUB_IF(dev); ++ ++ idx = erq->flags & IW_ENCODE_INDEX; ++ if (idx < 1 || idx > 4) { ++ idx = -1; ++ if (sdata->default_key == NULL) ++ idx = 0; ++ else for (i = 0; i < NUM_DEFAULT_KEYS; i++) { ++ if (sdata->default_key == sdata->keys[i]) ++ idx = i; ++ break; ++ } ++ if (idx < 0) ++ return -EINVAL; ++ } else ++ idx--; ++ ++ if (erq->flags & IW_ENCODE_DISABLED) ++ alg = ALG_NONE; ++ else if (erq->length == 0) { ++ /* No key data - just set the default TX key index */ ++ sdata->default_key = sdata->keys[idx]; ++ } ++ ++ return ieee80211_set_encryption( ++ dev, bcaddr, ++ idx, erq->length == 0 ? ALG_NONE : ALG_WEP, ++ sdata->default_key == NULL, ++ NULL, keybuf, erq->length); ++ ++ return 0; ++} ++ ++ ++static int ieee80211_ioctl_giwencode(struct net_device *dev, ++ struct iw_request_info *info, ++ struct iw_point *erq, char *key) ++{ ++ struct ieee80211_sub_if_data *sdata; ++ int idx, i; ++ ++ sdata = IEEE80211_DEV_TO_SUB_IF(dev); ++ ++ idx = erq->flags & IW_ENCODE_INDEX; ++ if (idx < 1 || idx > 4) { ++ idx = -1; ++ if (sdata->default_key == NULL) ++ idx = 0; ++ else for (i = 0; i < NUM_DEFAULT_KEYS; i++) { ++ if (sdata->default_key == sdata->keys[i]) ++ idx = i; ++ break; ++ } ++ if (idx < 0) ++ return -EINVAL; ++ } else ++ idx--; ++ ++ erq->flags = idx + 1; ++ ++ if (sdata->keys[idx] == NULL) { ++ erq->length = 0; ++ erq->flags |= IW_ENCODE_DISABLED; ++ return 0; ++ } ++ ++ erq->length = 0; ++ erq->flags |= IW_ENCODE_ENABLED; ++ ++ return 0; ++} ++ ++ ++static int ieee80211_ioctl_siwgenie(struct net_device *dev, ++ struct iw_request_info *info, ++ struct iw_point *data, char *extra) ++{ ++ return ieee80211_set_gen_ie(dev, extra, data->length); ++} ++ ++ ++static int ieee80211_ioctl_siwauth(struct net_device *dev, ++ struct iw_request_info *info, ++ struct iw_param *data, char *extra) ++{ ++ struct ieee80211_local *local = dev->priv; ++ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); ++ int ret = 0; ++ ++ switch (data->flags & IW_AUTH_INDEX) { ++ case IW_AUTH_WPA_VERSION: ++ case IW_AUTH_CIPHER_PAIRWISE: ++ case IW_AUTH_CIPHER_GROUP: ++ case IW_AUTH_WPA_ENABLED: ++ case IW_AUTH_RX_UNENCRYPTED_EAPOL: ++ break; ++ case IW_AUTH_KEY_MGMT: ++ if (sdata->type != IEEE80211_SUB_IF_TYPE_STA) ++ ret = -EINVAL; ++ else { ++ /* ++ * TODO: sdata->u.sta.key_mgmt does not match with WE18 ++ * value completely; could consider modifying this to ++ * be closer to WE18. For now, this value is not really ++ * used for anything else than Privacy matching, so the ++ * current code here should be more or less OK. ++ */ ++ if (data->value & IW_AUTH_KEY_MGMT_802_1X) { ++ sdata->u.sta.key_mgmt = ++ IEEE80211_KEY_MGMT_WPA_EAP; ++ } else if (data->value & IW_AUTH_KEY_MGMT_PSK) { ++ sdata->u.sta.key_mgmt = ++ IEEE80211_KEY_MGMT_WPA_PSK; ++ } else { ++ sdata->u.sta.key_mgmt = ++ IEEE80211_KEY_MGMT_NONE; ++ } ++ } ++ break; ++ case IW_AUTH_80211_AUTH_ALG: ++ if (sdata->type == IEEE80211_SUB_IF_TYPE_STA) ++ sdata->u.sta.auth_algs = data->value; ++ else ++ ret = -EOPNOTSUPP; ++ break; ++ case IW_AUTH_PRIVACY_INVOKED: ++ if (local->hw->set_privacy_invoked) ++ ret = local->hw->set_privacy_invoked(dev, data->value); ++ break; ++ default: ++ ret = -EOPNOTSUPP; ++ break; ++ } ++ return ret; ++} ++ ++ ++static int ieee80211_ioctl_giwauth(struct net_device *dev, ++ struct iw_request_info *info, ++ struct iw_param *data, char *extra) ++{ ++ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); ++ int ret = 0; ++ ++ switch (data->flags & IW_AUTH_INDEX) { ++ case IW_AUTH_80211_AUTH_ALG: ++ if (sdata->type == IEEE80211_SUB_IF_TYPE_STA) ++ data->value = sdata->u.sta.auth_algs; ++ else ++ ret = -EOPNOTSUPP; ++ break; ++ default: ++ ret = -EOPNOTSUPP; ++ break; ++ } ++ return ret; ++} ++ ++ ++static int ieee80211_ioctl_siwencodeext(struct net_device *dev, ++ struct iw_request_info *info, ++ struct iw_point *erq, char *extra) ++{ ++ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); ++ struct iw_encode_ext *ext = (struct iw_encode_ext *) extra; ++ int alg, idx, i; ++ ++ switch (ext->alg) { ++ case IW_ENCODE_ALG_NONE: ++ alg = ALG_NONE; ++ break; ++ case IW_ENCODE_ALG_WEP: ++ alg = ALG_WEP; ++ break; ++ case IW_ENCODE_ALG_TKIP: ++ alg = ALG_TKIP; ++ break; ++ case IW_ENCODE_ALG_CCMP: ++ alg = ALG_CCMP; ++ break; ++ default: ++ return -EOPNOTSUPP; ++ } ++ ++ if (erq->flags & IW_ENCODE_DISABLED) ++ alg = ALG_NONE; ++ ++ idx = erq->flags & IW_ENCODE_INDEX; ++ if (idx < 1 || idx > 4) { ++ idx = -1; ++ if (sdata->default_key == NULL) ++ idx = 0; ++ else for (i = 0; i < NUM_DEFAULT_KEYS; i++) { ++ if (sdata->default_key == sdata->keys[i]) ++ idx = i; ++ break; ++ } ++ if (idx < 0) ++ return -EINVAL; ++ } else ++ idx--; ++ ++ return ieee80211_set_encryption(dev, ext->addr.sa_data, idx, alg, ++ ext->ext_flags & ++ IW_ENCODE_EXT_SET_TX_KEY, ++ NULL, ext->key, ext->key_len); ++} ++ ++ ++static const struct iw_priv_args ieee80211_ioctl_priv[] = { ++ { PRISM2_IOCTL_PRISM2_PARAM, ++ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2, 0, "param" }, ++ { PRISM2_IOCTL_GET_PRISM2_PARAM, ++ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, ++ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_param" }, ++ { PRISM2_IOCTL_TEST_PARAM, ++ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2, 0, "test_param" }, ++}; ++ ++ ++int ieee80211_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) ++{ ++ struct iwreq *wrq = (struct iwreq *) rq; ++ int ret = 0; ++ ++ switch (cmd) { ++ /* Private ioctls (iwpriv) that have not yet been converted ++ * into new wireless extensions API */ ++ case PRISM2_IOCTL_TEST_PARAM: ++ ret = ieee80211_ioctl_test_param(dev, NULL, &wrq->u, ++ (char *) &wrq->u); ++ break; ++ case PRISM2_IOCTL_HOSTAPD: ++ if (!capable(CAP_NET_ADMIN)) ret = -EPERM; ++ else ret = ieee80211_ioctl_priv_hostapd(dev, &wrq->u.data); ++ break; ++ default: ++ ret = -EOPNOTSUPP; ++ break; ++ } ++ ++ return ret; ++} ++ ++ ++/* Structures to export the Wireless Handlers */ ++ ++static const iw_handler ieee80211_handler[] = ++{ ++ (iw_handler) NULL, /* SIOCSIWCOMMIT */ ++ (iw_handler) ieee80211_ioctl_giwname, /* SIOCGIWNAME */ ++ (iw_handler) NULL, /* SIOCSIWNWID */ ++ (iw_handler) NULL, /* SIOCGIWNWID */ ++ (iw_handler) ieee80211_ioctl_siwfreq, /* SIOCSIWFREQ */ ++ (iw_handler) ieee80211_ioctl_giwfreq, /* SIOCGIWFREQ */ ++ (iw_handler) ieee80211_ioctl_siwmode, /* SIOCSIWMODE */ ++ (iw_handler) ieee80211_ioctl_giwmode, /* SIOCGIWMODE */ ++ (iw_handler) NULL, /* SIOCSIWSENS */ ++ (iw_handler) NULL, /* SIOCGIWSENS */ ++ (iw_handler) NULL /* not used */, /* SIOCSIWRANGE */ ++ (iw_handler) ieee80211_ioctl_giwrange, /* SIOCGIWRANGE */ ++ (iw_handler) NULL /* not used */, /* SIOCSIWPRIV */ ++ (iw_handler) NULL /* kernel code */, /* SIOCGIWPRIV */ ++ (iw_handler) NULL /* not used */, /* SIOCSIWSTATS */ ++ (iw_handler) NULL /* kernel code */, /* SIOCGIWSTATS */ ++ iw_handler_set_spy, /* SIOCSIWSPY */ ++ iw_handler_get_spy, /* SIOCGIWSPY */ ++ iw_handler_set_thrspy, /* SIOCSIWTHRSPY */ ++ iw_handler_get_thrspy, /* SIOCGIWTHRSPY */ ++ (iw_handler) ieee80211_ioctl_siwap, /* SIOCSIWAP */ ++ (iw_handler) ieee80211_ioctl_giwap, /* SIOCGIWAP */ ++ (iw_handler) ieee80211_ioctl_siwmlme, /* SIOCSIWMLME */ ++ (iw_handler) NULL, /* SIOCGIWAPLIST */ ++ (iw_handler) ieee80211_ioctl_siwscan, /* SIOCSIWSCAN */ ++ (iw_handler) ieee80211_ioctl_giwscan, /* SIOCGIWSCAN */ ++ (iw_handler) ieee80211_ioctl_siwessid, /* SIOCSIWESSID */ ++ (iw_handler) ieee80211_ioctl_giwessid, /* SIOCGIWESSID */ ++ (iw_handler) NULL, /* SIOCSIWNICKN */ ++ (iw_handler) NULL, /* SIOCGIWNICKN */ ++ (iw_handler) NULL, /* -- hole -- */ ++ (iw_handler) NULL, /* -- hole -- */ ++ (iw_handler) NULL, /* SIOCSIWRATE */ ++ (iw_handler) NULL, /* SIOCGIWRATE */ ++ (iw_handler) ieee80211_ioctl_siwrts, /* SIOCSIWRTS */ ++ (iw_handler) ieee80211_ioctl_giwrts, /* SIOCGIWRTS */ ++ (iw_handler) ieee80211_ioctl_siwfrag, /* SIOCSIWFRAG */ ++ (iw_handler) ieee80211_ioctl_giwfrag, /* SIOCGIWFRAG */ ++ (iw_handler) NULL, /* SIOCSIWTXPOW */ ++ (iw_handler) NULL, /* SIOCGIWTXPOW */ ++ (iw_handler) ieee80211_ioctl_siwretry, /* SIOCSIWRETRY */ ++ (iw_handler) ieee80211_ioctl_giwretry, /* SIOCGIWRETRY */ ++ (iw_handler) ieee80211_ioctl_siwencode, /* SIOCSIWENCODE */ ++ (iw_handler) ieee80211_ioctl_giwencode, /* SIOCGIWENCODE */ ++ (iw_handler) NULL, /* SIOCSIWPOWER */ ++ (iw_handler) NULL, /* SIOCGIWPOWER */ ++ (iw_handler) NULL, /* -- hole -- */ ++ (iw_handler) NULL, /* -- hole -- */ ++ (iw_handler) ieee80211_ioctl_siwgenie, /* SIOCSIWGENIE */ ++ (iw_handler) NULL, /* SIOCGIWGENIE */ ++ (iw_handler) ieee80211_ioctl_siwauth, /* SIOCSIWAUTH */ ++ (iw_handler) ieee80211_ioctl_giwauth, /* SIOCGIWAUTH */ ++ (iw_handler) ieee80211_ioctl_siwencodeext, /* SIOCSIWENCODEEXT */ ++ (iw_handler) NULL, /* SIOCGIWENCODEEXT */ ++ (iw_handler) NULL, /* SIOCSIWPMKSA */ ++ (iw_handler) NULL, /* -- hole -- */ ++}; ++ ++static const iw_handler ieee80211_private_handler[] = ++{ /* SIOCIWFIRSTPRIV + */ ++ (iw_handler) ieee80211_ioctl_prism2_param, /* 0 */ ++ (iw_handler) ieee80211_ioctl_get_prism2_param, /* 1 */ ++}; ++ ++const struct iw_handler_def ieee80211_iw_handler_def = ++{ ++ .num_standard = sizeof(ieee80211_handler) / sizeof(iw_handler), ++ .num_private = sizeof(ieee80211_private_handler) / ++ sizeof(iw_handler), ++ .num_private_args = sizeof(ieee80211_ioctl_priv) / ++ sizeof(struct iw_priv_args), ++ .standard = (iw_handler *) ieee80211_handler, ++ .private = (iw_handler *) ieee80211_private_handler, ++ .private_args = (struct iw_priv_args *) ieee80211_ioctl_priv, ++}; +diff -Nur linux-2.6.16/net/d80211/ieee80211_key.h linux-2.6.16-bcm43xx/net/d80211/ieee80211_key.h +--- linux-2.6.16/net/d80211/ieee80211_key.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.16-bcm43xx/net/d80211/ieee80211_key.h 2006-03-28 22:16:14.000000000 +0200 +@@ -0,0 +1,83 @@ ++/* ++ * Copyright 2002-2004, Instant802 Networks, Inc. ++ * Copyright 2005, Devicescape Software, Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#ifndef IEEE80211_KEY_H ++#define IEEE80211_KEY_H ++ ++#include <linux/types.h> ++#include <net/d80211.h> ++ ++/* ALG_TKIP ++ * struct ieee80211_key::key is encoded as a 256-bit (32 byte) data block: ++ * Temporal Encryption Key (128 bits) ++ * Temporal Authenticator Tx MIC Key (64 bits) ++ * Temporal Authenticator Rx MIC Key (64 bits) ++ */ ++ ++#define WEP_IV_LEN 4 ++#define WEP_ICV_LEN 4 ++ ++#define ALG_TKIP_KEY_LEN 32 ++/* Starting offsets for each key */ ++#define ALG_TKIP_TEMP_ENCR_KEY 0 ++#define ALG_TKIP_TEMP_AUTH_TX_MIC_KEY 16 ++#define ALG_TKIP_TEMP_AUTH_RX_MIC_KEY 24 ++#define TKIP_IV_LEN 8 ++#define TKIP_ICV_LEN 4 ++ ++#define ALG_CCMP_KEY_LEN 16 ++#define CCMP_HDR_LEN 8 ++#define CCMP_MIC_LEN 8 ++#define CCMP_TK_LEN 16 ++#define CCMP_PN_LEN 6 ++ ++#define NUM_RX_DATA_QUEUES 17 ++ ++struct ieee80211_key { ++ int hw_key_idx; /* filled and used by low-level driver */ ++ ieee80211_key_alg alg; ++ union { ++ struct { ++ /* last used TSC */ ++ u32 iv32; ++ u16 iv16; ++ u16 p1k[5]; ++ int tx_initialized; ++ ++ /* last received RSC */ ++ u32 iv32_rx[NUM_RX_DATA_QUEUES]; ++ u16 iv16_rx[NUM_RX_DATA_QUEUES]; ++ u16 p1k_rx[NUM_RX_DATA_QUEUES][5]; ++ int rx_initialized[NUM_RX_DATA_QUEUES]; ++ } tkip; ++ struct { ++ u8 tx_pn[6]; ++ /* TODO: for WME make this replay counter per AC */ ++ u8 rx_pn[NUM_RX_DATA_QUEUES][6]; ++#ifndef AES_STATE_LEN ++#define AES_STATE_LEN 44 ++#endif ++ u32 aes_state[AES_STATE_LEN]; ++ u32 replays; /* dot11RSNAStatsCCMPReplays */ ++ } ccmp; ++ } u; ++ int tx_rx_count; /* number of times this key has been used */ ++ int keylen; ++ ++ /* if the low level driver can provide hardware acceleration it should ++ * clear this flag */ ++ int force_sw_encrypt:1; ++ int keyidx:8; /* WEP key index */ ++ int default_tx_key:1; /* This key is the new default TX key ++ * (used only for broadcast keys). */ ++ ++ u8 key[0]; ++}; ++ ++#endif /* IEEE80211_KEY_H */ +diff -Nur linux-2.6.16/net/d80211/ieee80211_led.c linux-2.6.16-bcm43xx/net/d80211/ieee80211_led.c +--- linux-2.6.16/net/d80211/ieee80211_led.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.16-bcm43xx/net/d80211/ieee80211_led.c 2006-03-28 22:16:14.000000000 +0200 +@@ -0,0 +1,32 @@ ++/* ++ * Copyright 2002-2004, Instant802 Networks, Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include <linux/config.h> ++#include <linux/netdevice.h> ++#include <linux/types.h> ++ ++#ifdef CONFIG_OAP_LEDS_WLAN ++extern void leds_wlan_set(int unit, int tx, int state); ++#endif ++ ++void ieee80211_rx_led(int state, struct net_device *dev) { ++#ifdef CONFIG_OAP_LEDS_WLAN ++ static unsigned int count = 0; ++ ++ if (state == 2) { ++ leds_wlan_set(0, 0, (++count) & 1); ++ } ++#endif ++} ++ ++void ieee80211_tx_led(int state, struct net_device *dev) { ++#ifdef CONFIG_OAP_LEDS_WLAN ++ leds_wlan_set(0, 1, state); ++#endif ++} ++ +diff -Nur linux-2.6.16/net/d80211/ieee80211_proc.c linux-2.6.16-bcm43xx/net/d80211/ieee80211_proc.c +--- linux-2.6.16/net/d80211/ieee80211_proc.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.16-bcm43xx/net/d80211/ieee80211_proc.c 2006-03-28 22:16:14.000000000 +0200 +@@ -0,0 +1,777 @@ ++/* ++ * Copyright 2003-2005, Devicescape Software, Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include <linux/config.h> ++#include <linux/version.h> ++#include <linux/module.h> ++#include <linux/netdevice.h> ++#include <linux/proc_fs.h> ++#include <linux/delay.h> ++ ++#ifdef CONFIG_PROC_FS ++ ++#include <net/d80211.h> ++#include <net/d80211_common.h> ++#include <net/d80211_mgmt.h> ++#include "ieee80211_i.h" ++#include "sta_info.h" ++#include "ieee80211_proc.h" ++#include "rate_control.h" ++ ++ ++static struct proc_dir_entry *ieee80211_proc; ++ ++#define PROC_LIMIT (PAGE_SIZE - 80) ++ ++ ++static char * ieee80211_proc_key(char *p, struct ieee80211_key *key, ++ int idx, int def_key) ++{ ++ int i; ++ u8 *tpn, *rpn; ++ ++ if (!key) ++ return p; ++ ++ p += sprintf(p, "key[%d]%s len=%d sw_encrypt=%d idx=%d hwidx=%d " ++ "tx_rx_count=%d", ++ idx, def_key ? "*" : "", key->keylen, ++ key->force_sw_encrypt, key->keyidx, key->hw_key_idx, ++ key->tx_rx_count); ++ switch (key->alg) { ++ case ALG_WEP: ++ p += sprintf(p, " alg=WEP"); ++ break; ++ case ALG_TKIP: ++ p += sprintf(p, " alg=TKIP iv(tx)=%08x %04x", ++ key->u.tkip.iv32, key->u.tkip.iv16); ++ for (i = 0; i < NUM_RX_DATA_QUEUES; i++) { ++ if (key->u.tkip.iv32_rx[i] == 0 && ++ key->u.tkip.iv16_rx[i] == 0) ++ continue; ++ p += sprintf(p, " iv(rx %d)=%08x %04x", i, ++ key->u.tkip.iv32_rx[i], ++ key->u.tkip.iv16_rx[i]); ++ } ++ break; ++ case ALG_CCMP: ++ tpn = key->u.ccmp.tx_pn; ++ p += sprintf(p, " alg=CCMP PN(tx)=%02x%02x%02x%02x%02x%02x", ++ tpn[0], tpn[1], tpn[2], tpn[3], tpn[4], tpn[5]); ++ for (i = 0; i < NUM_RX_DATA_QUEUES; i++) { ++ rpn = key->u.ccmp.rx_pn[i]; ++ if (memcmp(rpn, "\x00\x00\x00\x00\x00\x00", 6) == 0) ++ continue; ++ p += sprintf(p, " PN(rx %d)=%02x%02x%02x%02x%02x%02x", ++ i, rpn[0], rpn[1], rpn[2], rpn[3], rpn[4], ++ rpn[5]); ++ } ++ p += sprintf(p, " replays=%u", key->u.ccmp.replays); ++ break; ++ default: ++ break; ++ } ++ ++ p += sprintf(p, " key="); ++ for (i = 0; i < key->keylen; i++) ++ p += sprintf(p, "%02x", key->key[i]); ++ p += sprintf(p, "\n"); ++ return p; ++} ++ ++ ++static char * ieee80211_proc_sub_if_ap(char *p, ++ struct ieee80211_if_ap *ap) ++{ ++ p += sprintf(p, "type=ap\n"); ++ if (ap->beacon_head) ++ p += sprintf(p, "beacon_head_len=%d\n", ap->beacon_head_len); ++ if (ap->beacon_tail) ++ p += sprintf(p, "beacon_tail_len=%d\n", ap->beacon_tail_len); ++ p += sprintf(p, ++ "max_aid=%d\n" ++ "num_sta_ps=%d\n" ++ "num_buffered_multicast=%u\n" ++ "dtim_period=%d\n" ++ "dtim_count=%d\n" ++ "num_beacons=%d\n" ++ "force_unicast_rateidx=%d\n" ++ "max_ratectrl_rateidx=%d\n", ++ ap->max_aid, atomic_read(&ap->num_sta_ps), ++ skb_queue_len(&ap->ps_bc_buf), ++ ap->dtim_period, ap->dtim_count, ap->num_beacons, ++ ap->force_unicast_rateidx, ap->max_ratectrl_rateidx); ++ return p; ++} ++ ++ ++static char * ieee80211_proc_sub_if_sta(char *p, ++ struct ieee80211_if_sta *ifsta) ++{ ++ p += sprintf(p, "type=sta\n"); ++ p += sprintf(p, ++ "state=%d\n" ++ "bssid=" MACSTR "\n" ++ "prev_bssid=" MACSTR "\n" ++ "ssid_len=%zd\n" ++ "aid=%d\n" ++ "ap_capab=0x%x\n" ++ "capab=0x%x\n" ++ "extra_ie_len=%zd\n" ++ "auth_tries=%d\n" ++ "assoc_tries=%d\n" ++ "flags=%s%s%s%s%s%s%s\n" ++ "auth_algs=0x%x\n" ++ "auth_alg=%d\n" ++ "auth_transaction=%d\n", ++ ifsta->state, ++ MAC2STR(ifsta->bssid), ++ MAC2STR(ifsta->prev_bssid), ++ ifsta->ssid_len, ++ ifsta->aid, ++ ifsta->ap_capab, ++ ifsta->capab, ++ ifsta->extra_ie_len, ++ ifsta->auth_tries, ++ ifsta->assoc_tries, ++ ifsta->ssid_set ? "[SSID]" : "", ++ ifsta->bssid_set ? "[BSSID]" : "", ++ ifsta->prev_bssid_set ? "[prev BSSID" : "", ++ ifsta->authenticated ? "[AUTH]" : "", ++ ifsta->associated ? "[ASSOC]" : "", ++ ifsta->probereq_poll ? "[PROBEREQ POLL]" : "", ++ ifsta->use_protection ? "[CTS prot]" : "", ++ ifsta->auth_algs, ++ ifsta->auth_alg, ++ ifsta->auth_transaction); ++ return p; ++} ++ ++ ++static char * ieee80211_proc_sub_if(char *p, ++ struct ieee80211_sub_if_data *sdata) ++{ ++ if (sdata == NULL) ++ return p; ++ ++ if (sdata->bss) ++ p += sprintf(p, "bss=%p\n", sdata->bss); ++ ++ switch (sdata->type) { ++ case IEEE80211_SUB_IF_TYPE_AP: ++ p = ieee80211_proc_sub_if_ap(p, &sdata->u.ap); ++ break; ++ case IEEE80211_SUB_IF_TYPE_WDS: ++ p += sprintf(p, "type=wds\n"); ++ p += sprintf(p, "wds.peer=" MACSTR "\n", ++ MAC2STR(sdata->u.wds.remote_addr)); ++ break; ++ case IEEE80211_SUB_IF_TYPE_VLAN: ++ p += sprintf(p, "type=vlan\n"); ++ p += sprintf(p, "vlan.id=%d\n", sdata->u.vlan.id); ++ break; ++ case IEEE80211_SUB_IF_TYPE_STA: ++ p = ieee80211_proc_sub_if_sta(p, &sdata->u.sta); ++ break; ++ } ++ p += sprintf(p, "channel_use=%d\n", sdata->channel_use); ++ p += sprintf(p, "drop_unencrypted=%d\n", sdata->drop_unencrypted); ++ p += sprintf(p, "eapol=%d\n", sdata->eapol); ++ p += sprintf(p, "ieee802_1x=%d\n", sdata->ieee802_1x); ++ ++ return p; ++} ++ ++ ++static int ieee80211_proc_iface_read(char *page, char **start, off_t off, ++ int count, int *eof, void *data) ++{ ++ char *p = page; ++ struct net_device *dev = (struct net_device *) data; ++ struct ieee80211_sub_if_data *sdata; ++ int i; ++ ++ if (off != 0) { ++ *eof = 1; ++ return 0; ++ } ++ ++ sdata = IEEE80211_DEV_TO_SUB_IF(dev); ++ if (!sdata) ++ return -1; ++ ++ p = ieee80211_proc_sub_if(p, sdata); ++ ++ for (i = 0; i < NUM_DEFAULT_KEYS; i++) { ++ if (sdata->keys[i] == NULL) ++ continue; ++ ++ p = ieee80211_proc_key(p, sdata->keys[i], i, ++ sdata->keys[i] == sdata->default_key); ++ } ++ ++ return (p - page); ++} ++ ++ ++static int ieee80211_proc_sta_read(char *page, char **start, off_t off, ++ int count, int *eof, void *data) ++{ ++ char *p = page; ++ struct sta_info *sta = (struct sta_info *) data; ++ struct ieee80211_local *local; ++ int inactive, i; ++ ++ if (off != 0) { ++ *eof = 1; ++ return 0; ++ } ++ ++ if (!sta || !sta->dev) ++ return -1; ++ ++ p += sprintf(p, "users=%d\n", atomic_read(&sta->users)); ++ p += sprintf(p, "aid=%d\n", sta->aid); ++ p += sprintf(p, "flags=0x%x %s%s%s%s%s%s%s%s%s%s\n", sta->flags, ++ sta->flags & WLAN_STA_AUTH ? "[AUTH]" : "", ++ sta->flags & WLAN_STA_ASSOC ? "[ASSOC]" : "", ++ sta->flags & WLAN_STA_PS ? "[PS]" : "", ++ sta->flags & WLAN_STA_TIM ? "[TIM]" : "", ++ sta->flags & WLAN_STA_PERM ? "[PERM]" : "", ++ sta->flags & WLAN_STA_AUTHORIZED ? "[AUTHORIZED]" : "", ++ sta->flags & WLAN_STA_SHORT_PREAMBLE ? ++ "[SHORT PREAMBLE]" : "", ++ sta->flags & WLAN_STA_WME ? "[WME]" : "", ++ sta->flags & WLAN_STA_WDS ? "[WDS]" : "", ++ sta->flags & WLAN_STA_XR ? "[XR]" : ""); ++ p += sprintf(p, "key_idx_compression=%d\n", ++ sta->key_idx_compression); ++ p += sprintf(p, "dev=%s\n", sta->dev->name); ++ if (sta->vlan_id > 0) ++ p += sprintf(p, "vlan_id=%d\n", sta->vlan_id); ++ p += sprintf(p, "rx_packets=%lu\ntx_packets=%lu\nrx_bytes=%lu\n" ++ "tx_bytes=%lu\nrx_duplicates=%lu\nrx_fragments=%lu\n" ++ "rx_dropped=%lu\ntx_fragments=%lu\ntx_filtered=%lu\n", ++ sta->rx_packets, sta->tx_packets, ++ sta->rx_bytes, sta->tx_bytes, ++ sta->num_duplicates, sta->rx_fragments, sta->rx_dropped, ++ sta->tx_fragments, sta->tx_filtered_count); ++ p = ieee80211_proc_key(p, sta->key, 0, 1); ++ ++ local = (struct ieee80211_local *) sta->dev->priv; ++ if (sta->txrate >= 0 && sta->txrate < local->num_curr_rates) { ++ p += sprintf(p, "txrate=%d\n", ++ local->curr_rates[sta->txrate].rate); ++ } ++ if (sta->last_txrate >= 0 && ++ sta->last_txrate < local->num_curr_rates) { ++ p += sprintf(p, "last_txrate=%d\n", ++ local->curr_rates[sta->last_txrate].rate); ++ } ++ p += sprintf(p, "num_ps_buf_frames=%u\n", ++ skb_queue_len(&sta->ps_tx_buf)); ++ p += sprintf(p, "tx_retry_failed=%lu\n", sta->tx_retry_failed); ++ p += sprintf(p, "tx_retry_count=%lu\n", sta->tx_retry_count); ++ p += sprintf(p, "last_rssi=%d\n", sta->last_rssi); ++ p += sprintf(p, "last_ack_rssi=%d %d %d\n", ++ sta->last_ack_rssi[0], sta->last_ack_rssi[1], ++ sta->last_ack_rssi[2]); ++ if (sta->last_ack) ++ p += sprintf(p, "last_ack_ms=%d\n", ++ jiffies_to_msecs(jiffies - sta->last_ack)); ++ inactive = jiffies - sta->last_rx; ++ p += sprintf(p, "inactive_msec=%d\n", jiffies_to_msecs(inactive)); ++ p += sprintf(p, "channel_use=%d\n", sta->channel_use); ++ p += sprintf(p, "wep_weak_iv_count=%d\n", sta->wep_weak_iv_count); ++#ifdef CONFIG_D80211_DEBUG_COUNTERS ++ p += sprintf(p, "wme_rx_queue="); ++ for (i = 0; i < NUM_RX_DATA_QUEUES; i++) ++ p += sprintf(p, "%u ", sta->wme_rx_queue[i]); ++ p += sprintf(p, "\n"); ++ ++ p += sprintf(p, "wme_tx_queue="); ++ for (i = 0; i < NUM_RX_DATA_QUEUES; i++) ++ p += sprintf(p, "%u ", sta->wme_tx_queue[i]); ++ p += sprintf(p, "\n"); ++#endif /* CONFIG_D80211_DEBUG_COUNTERS */ ++ p += sprintf(p, "last_seq_ctrl="); ++ for (i = 0; i < NUM_RX_DATA_QUEUES; i++) { ++ p += sprintf(p, "%x ", sta->last_seq_ctrl[i]); ++ } ++ p += sprintf(p, "\n"); ++ ++ p += rate_control_status_sta(local, sta, p); ++ ++ return (p - page); ++} ++ ++ ++static int ieee80211_proc_counters_read(char *page, char **start, off_t off, ++ int count, int *eof, void *data) ++{ ++ char *p = page; ++ struct ieee80211_local *local = (struct ieee80211_local *) data; ++ struct ieee80211_low_level_stats stats; ++ ++ if (off != 0) { ++ *eof = 1; ++ return 0; ++ } ++ ++ p += sprintf(p, ++ "TransmittedFragmentCount=%u\n" ++ "MulticastTransmittedFrameCount=%u\n" ++ "FailedCount=%u\n" ++ "RetryCount=%u\n" ++ "MultipleRetryCount=%d\n" ++ "FrameDuplicateCount=%d\n" ++ "ReceivedFragmentCount=%u\n" ++ "MulticastReceivedFrameCount=%u\n" ++ "TransmittedFrameCount=%u\n" ++ "WEPUndecryptableCount=%u\n", ++ local->dot11TransmittedFragmentCount, ++ local->dot11MulticastTransmittedFrameCount, ++ local->dot11FailedCount, ++ local->dot11RetryCount, ++ local->dot11MultipleRetryCount, ++ local->dot11FrameDuplicateCount, ++ local->dot11ReceivedFragmentCount, ++ local->dot11MulticastReceivedFrameCount, ++ local->dot11TransmittedFrameCount, ++ local->dot11WEPUndecryptableCount); ++ ++ memset(&stats, 0, sizeof(stats)); ++ if (local->hw->get_stats && ++ local->hw->get_stats(local->mdev, &stats) == 0) { ++ p += sprintf(p, ++ "ACKFailureCount=%u\n" ++ "RTSFailureCount=%u\n" ++ "FCSErrorCount=%u\n" ++ "RTSSuccessCount=%u\n", ++ stats.dot11ACKFailureCount, ++ stats.dot11RTSFailureCount, ++ stats.dot11FCSErrorCount, ++ stats.dot11RTSSuccessCount); ++ } ++ ++ return (p - page); ++} ++ ++ ++static int ieee80211_proc_debug_read(char *page, char **start, off_t off, ++ int count, int *eof, void *data) ++{ ++ char *p = page; ++ struct ieee80211_local *local = (struct ieee80211_local *) data; ++ int i; ++ ++ if (off != 0) { ++ *eof = 1; ++ return 0; ++ } ++ ++#ifdef CONFIG_D80211_DEBUG_COUNTERS ++ p += sprintf(p, ++ "tx_handlers_drop=%u\n" ++ "tx_handlers_queued=%u\n" ++ "tx_handlers_drop_unencrypted=%u\n" ++ "tx_handlers_drop_fragment=%u\n" ++ "tx_handlers_drop_wep=%u\n" ++ "tx_handlers_drop_rate_limit=%u\n" ++ "tx_handlers_drop_not_assoc=%u\n" ++ "tx_handlers_drop_unauth_port=%u\n" ++ "rx_handlers_drop=%u\n" ++ "rx_handlers_queued=%u\n" ++ "rx_handlers_drop_nullfunc=%u\n" ++ "rx_handlers_drop_defrag=%u\n" ++ "rx_handlers_drop_short=%u\n" ++ "rx_handlers_drop_passive_scan=%u\n" ++ "tx_expand_skb_head=%u\n" ++ "tx_expand_skb_head_cloned=%u\n" ++ "rx_expand_skb_head=%u\n" ++ "rx_expand_skb_head2=%u\n" ++ "rx_handlers_fragments=%u\n" ++ "tx_status_drop=%u\n", ++ local->tx_handlers_drop, ++ local->tx_handlers_queued, ++ local->tx_handlers_drop_unencrypted, ++ local->tx_handlers_drop_fragment, ++ local->tx_handlers_drop_wep, ++ local->tx_handlers_drop_rate_limit, ++ local->tx_handlers_drop_not_assoc, ++ local->tx_handlers_drop_unauth_port, ++ local->rx_handlers_drop, ++ local->rx_handlers_queued, ++ local->rx_handlers_drop_nullfunc, ++ local->rx_handlers_drop_defrag, ++ local->rx_handlers_drop_short, ++ local->rx_handlers_drop_passive_scan, ++ local->tx_expand_skb_head, ++ local->tx_expand_skb_head_cloned, ++ local->rx_expand_skb_head, ++ local->rx_expand_skb_head2, ++ local->rx_handlers_fragments, ++ local->tx_status_drop); ++ { ++ int i; ++ p += sprintf(p, "wme_rx_queue="); ++ for (i = 0; i < NUM_RX_DATA_QUEUES; i++) ++ p += sprintf(p, " %u", local->wme_rx_queue[i]); ++ p += sprintf(p, "\n"); ++ ++ p += sprintf(p, "wme_tx_queue="); ++ for (i = 0; i < NUM_RX_DATA_QUEUES; i++) ++ p += sprintf(p, " %u", local->wme_tx_queue[i]); ++ p += sprintf(p, "\n"); ++ } ++#endif /* CONFIG_D80211_DEBUG_COUNTERS */ ++ ++ p += sprintf(p, "num_scans=%u\n", local->scan.num_scans); ++ ++ p += sprintf(p, ++ "conf.bss_count=%d\n" ++ "bss_dev_count=%u\n", ++ local->conf.bss_count, local->bss_dev_count); ++ for (i = 0; i < local->conf.bss_count; i++) { ++ p += sprintf(p, "bss_dev[%d]=%p (%s)\n", ++ i, local->bss_devs[i], ++ (i < local->bss_dev_count && local->bss_devs[i]) ? ++ local->bss_devs[i]->name : "N/A"); ++ } ++ ++ return (p - page); ++} ++ ++ ++static const char * ieee80211_mode_str_short(int mode) ++{ ++ switch (mode) { ++ case MODE_IEEE80211A: ++ return "802.11a"; ++ case MODE_IEEE80211B: ++ return "802.11b"; ++ case MODE_IEEE80211G: ++ return "802.11g"; ++ case MODE_ATHEROS_TURBO: ++ return "AtherosTurbo"; ++ default: ++ return "UNKNOWN"; ++ } ++} ++ ++ ++static const char * ieee80211_mode_str(int mode) ++{ ++ switch (mode) { ++ case MODE_IEEE80211A: ++ return "IEEE 802.11a"; ++ case MODE_IEEE80211B: ++ return "IEEE 802.11b"; ++ case MODE_IEEE80211G: ++ return "IEEE 802.11g"; ++ case MODE_ATHEROS_TURBO: ++ return "Atheros Turbo (5 GHz)"; ++ default: ++ return "UNKNOWN"; ++ } ++} ++ ++ ++static int ieee80211_proc_info_read(char *page, char **start, off_t off, ++ int count, int *eof, void *data) ++{ ++ char *p = page; ++ struct ieee80211_local *local = (struct ieee80211_local *) data; ++ int m; ++ struct ieee80211_hw_modes *mode; ++ ++ if (off != 0) { ++ *eof = 1; ++ return 0; ++ } ++ ++ p += sprintf(p, "hw_name=%s\n", local->hw->name); ++ p += sprintf(p, "modes="); ++ for (m = 0; m < local->hw->num_modes; m++) { ++ mode = &local->hw->modes[m]; ++ p += sprintf(p, "[%s]", ieee80211_mode_str_short(mode->mode)); ++ } ++ p += sprintf(p, "\n"); ++ if (local->rate_ctrl && local->rate_ctrl_priv) ++ p+= sprintf(p, "rate_ctrl_alg=%s\n", local->rate_ctrl->name); ++ return (p - page); ++} ++ ++ ++static int ieee80211_proc_config_read(char *page, char **start, off_t off, ++ int count, int *eof, void *data) ++{ ++ char *p = page; ++ struct ieee80211_local *local = (struct ieee80211_local *) data; ++ ++ if (off != 0) { ++ *eof = 1; ++ return 0; ++ } ++ ++ p += sprintf(p, ++ "low_level_driver=%s\n" ++ "channel=%d\n" ++ "freq=%d\n" ++ "mode=%s\n" ++ "802.11h=%d\n" ++ "wep_iv=0x%06x\n" ++ "antenna_sel=%d\n" ++ "calib_int=%d\n" ++ "tx_power_reduction=%d.%d dBm\n" ++ "bridge_packets=%d\n" ++ "key_tx_rx_threshold=%d\n" ++ "rts_threshold=%d\n" ++ "fragmentation_threshold=%d\n" ++ "short_retry_limit=%d\n" ++ "long_retry_limit=%d\n" ++ "total_ps_buffered=%d\n", ++ local->hw->name ? local->hw->name : "N/A", ++ local->conf.channel, ++ local->conf.freq, ++ ieee80211_mode_str(local->conf.phymode), ++ local->conf.radar_detect, ++ local->wep_iv & 0xffffff, ++ local->conf.antenna_sel, ++ local->conf.calib_int, ++ local->conf.tx_power_reduction / 10, ++ local->conf.tx_power_reduction % 10, ++ local->bridge_packets, ++ local->key_tx_rx_threshold, ++ local->rts_threshold, ++ local->fragmentation_threshold, ++ local->short_retry_limit, ++ local->long_retry_limit, ++ local->total_ps_buffered); ++ ++ return (p - page); ++} ++ ++ ++static int ieee80211_proc_channels_read(char *page, char **start, off_t off, ++ int count, int *eof, void *data) ++{ ++ char *p = page; ++ struct ieee80211_local *local = (struct ieee80211_local *) data; ++ int m, c; ++ struct ieee80211_hw_modes *mode; ++ struct ieee80211_channel *chan; ++ ++ if (off != 0) { ++ *eof = 1; ++ return 0; ++ } ++ ++ p += sprintf(p, "MODE CHAN FREQ TXPOWER ANTMAX FLAGS\n"); ++ for (m = 0; m < local->hw->num_modes; m++) { ++ mode = &local->hw->modes[m]; ++ for (c = 0; c < mode->num_channels; c++) { ++ chan = &mode->channels[c]; ++ p += sprintf(p, "%d %d %d %d %d %s%s%s\n", ++ mode->mode, chan->chan, chan->freq, ++ chan->power_level, chan->antenna_max, ++ chan->flag & IEEE80211_CHAN_W_SCAN ? ++ "[W_SCAN]" : "", ++ chan->flag & IEEE80211_CHAN_W_ACTIVE_SCAN ++ ? "[W_ACTIVE_SCAN]" : "", ++ chan->flag & IEEE80211_CHAN_W_IBSS ? ++ "[W_IBSS]" : ""); ++ } ++ } ++ return (p - page); ++} ++ ++ ++static int ieee80211_proc_rates_read(char *page, char **start, off_t off, ++ int count, int *eof, void *data) ++{ ++ char *p = page; ++ struct ieee80211_local *local = (struct ieee80211_local *) data; ++ int r; ++ struct ieee80211_rate *rate; ++ ++ if (off != 0) { ++ *eof = 1; ++ return 0; ++ } ++ ++ p += sprintf(p, "RATE VAL VAL2 MIN_RSSI_ACK MIN_RSSI_ACK_DELTA " ++ "FLAGS\n"); ++ for (r = 0; r < local->num_curr_rates; r++) { ++ rate = &local->curr_rates[r]; ++ p += sprintf(p, "%d %d %d %d %d 0x%x %s%s%s%s%s%s%s%s\n", ++ rate->rate, rate->val, rate->val2, ++ rate->min_rssi_ack, rate->min_rssi_ack_delta, ++ rate->flags, ++ rate->flags & IEEE80211_RATE_ERP ? "[ERP]" : "", ++ rate->flags & IEEE80211_RATE_BASIC ? ++ "[BASIC]" : "", ++ rate->flags & IEEE80211_RATE_PREAMBLE2 ? ++ "[PREAMBLE2]" : "", ++ rate->flags & IEEE80211_RATE_SUPPORTED ? ++ "[SUPPORTED]" : "", ++ rate->flags & IEEE80211_RATE_OFDM ? "[OFDM]" : "", ++ rate->flags & IEEE80211_RATE_CCK ? "[CCK]" : "", ++ rate->flags & IEEE80211_RATE_TURBO ? ++ "[TURBO]" : "", ++ rate->flags & IEEE80211_RATE_MANDATORY ? ++ "[MANDATORY]" : ""); ++ } ++ return (p - page); ++} ++ ++ ++static int ieee80211_proc_multicast_read(char *page, char **start, off_t off, ++ int count, int *eof, void *data) ++{ ++ char *p = page; ++ struct ieee80211_local *local = (struct ieee80211_local *) data; ++ ++ if (off != 0) { ++ *eof = 1; ++ return 0; ++ } ++ ++ return rate_control_status_global(local, p); ++ ++} ++ ++ ++void ieee80211_proc_init_sta(struct ieee80211_local *local, ++ struct sta_info *sta) ++{ ++ char buf[30]; ++ struct proc_dir_entry *entry; ++ ++ sprintf(buf, MACSTR, MAC2STR(sta->addr)); ++ ++ if (!local->proc_sta) ++ return; ++ ++ entry = create_proc_read_entry(buf, 0, local->proc_sta, ++ ieee80211_proc_sta_read, sta); ++ if (entry) { ++ entry->mode &= ~(S_IRWXG | S_IRWXO); ++ sta->proc_entry_added = 1; ++ } ++} ++ ++ ++void ieee80211_proc_deinit_sta(struct ieee80211_local *local, ++ struct sta_info *sta) ++{ ++ char buf[30]; ++ sprintf(buf, MACSTR, MAC2STR(sta->addr)); ++ if (local->proc_sta) { ++ remove_proc_entry(buf, local->proc_sta); ++ sta->proc_entry_added = 0; ++ } ++} ++ ++ ++void ieee80211_proc_init_virtual(struct net_device *dev) ++{ ++ struct proc_dir_entry *entry; ++ struct ieee80211_local *local = (struct ieee80211_local *) dev->priv; ++ ++ if (!local->proc_iface) ++ return; ++ ++ entry = create_proc_read_entry(dev->name, 0, local->proc_iface, ++ ieee80211_proc_iface_read, dev); ++ if (entry) ++ entry->mode &= ~(S_IRWXG | S_IRWXO); ++} ++ ++ ++void ieee80211_proc_deinit_virtual(struct net_device *dev) ++{ ++ struct ieee80211_local *local = (struct ieee80211_local *) dev->priv; ++ ++ if (local->proc_iface) ++ remove_proc_entry(dev->name, local->proc_iface); ++} ++ ++ ++void ieee80211_proc_init_interface(struct ieee80211_local *local) ++{ ++ if (!ieee80211_proc) ++ return; ++ ++ local->proc = proc_mkdir(local->wdev->name, ieee80211_proc); ++ if (!local->proc) ++ return; ++ ++ local->proc_sta = proc_mkdir("sta", local->proc); ++ local->proc_iface = proc_mkdir("iface", local->proc); ++ create_proc_read_entry("counters", 0, local->proc, ++ ieee80211_proc_counters_read, local); ++ create_proc_read_entry("config", 0, local->proc, ++ ieee80211_proc_config_read, local); ++ create_proc_read_entry("channels", 0, local->proc, ++ ieee80211_proc_channels_read, local); ++ create_proc_read_entry("rates", 0, local->proc, ++ ieee80211_proc_rates_read, local); ++ create_proc_read_entry("multicast", 0, local->proc, ++ ieee80211_proc_multicast_read, local); ++ create_proc_read_entry("debug", 0, local->proc, ++ ieee80211_proc_debug_read, local); ++ create_proc_read_entry("info", 0, local->proc, ++ ieee80211_proc_info_read, local); ++ ieee80211_proc_init_virtual(local->wdev); ++} ++ ++ ++void ieee80211_proc_deinit_interface(struct ieee80211_local *local) ++{ ++ if (!local->proc) ++ return; ++ ++ ieee80211_proc_deinit_virtual(local->wdev); ++ remove_proc_entry("iface", local->proc); ++ remove_proc_entry("sta", local->proc); ++ remove_proc_entry("counters", local->proc); ++ remove_proc_entry("debug", local->proc); ++ remove_proc_entry("config", local->proc); ++ remove_proc_entry("channels", local->proc); ++ remove_proc_entry("rates", local->proc); ++ remove_proc_entry("multicast", local->proc); ++ remove_proc_entry("info", local->proc); ++ local->proc = NULL; ++ remove_proc_entry(local->wdev->name, ieee80211_proc); ++} ++ ++ ++void ieee80211_proc_init(void) ++{ ++ if (proc_net == NULL) { ++ ieee80211_proc = NULL; ++ return; ++ } ++ ++ ieee80211_proc = proc_mkdir("ieee80211", proc_net); ++ if (!ieee80211_proc) ++ printk(KERN_WARNING "Failed to mkdir /proc/net/ieee80211\n"); ++} ++ ++ ++void ieee80211_proc_deinit(void) ++{ ++ if (!ieee80211_proc) ++ return; ++ ++ ieee80211_proc = NULL; ++ remove_proc_entry("ieee80211", proc_net); ++} ++ ++#endif /* CONFIG_PROC_FS */ +diff -Nur linux-2.6.16/net/d80211/ieee80211_proc.h linux-2.6.16-bcm43xx/net/d80211/ieee80211_proc.h +--- linux-2.6.16/net/d80211/ieee80211_proc.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.16-bcm43xx/net/d80211/ieee80211_proc.h 2006-03-28 22:16:14.000000000 +0200 +@@ -0,0 +1,45 @@ ++/* ++ * Copyright 2003-2004, Instant802 Networks, Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#ifndef IEEE80211_PROC_H ++#define IEEE80211_PROC_H ++ ++#include <linux/netdevice.h> ++#include "ieee80211_i.h" ++#include "sta_info.h" ++ ++#ifdef CONFIG_PROC_FS ++ ++void ieee80211_proc_init_sta(struct ieee80211_local *local, ++ struct sta_info *sta); ++void ieee80211_proc_deinit_sta(struct ieee80211_local *local, ++ struct sta_info *sta); ++void ieee80211_proc_init_virtual(struct net_device *dev); ++void ieee80211_proc_deinit_virtual(struct net_device *dev); ++void ieee80211_proc_init_interface(struct ieee80211_local *local); ++void ieee80211_proc_deinit_interface(struct ieee80211_local *local); ++void ieee80211_proc_init(void); ++void ieee80211_proc_deinit(void); ++ ++#else /* CONFIG_PROC_FS */ ++ ++static inline void ieee80211_proc_init_sta(struct ieee80211_local *local, ++ struct sta_info *sta) {} ++static inline void ieee80211_proc_deinit_sta(struct ieee80211_local *local, ++ struct sta_info *sta) {} ++static inline void ieee80211_proc_init_virtual(struct net_device *dev) {} ++static inline void ieee80211_proc_deinit_virtual(struct net_device *dev) {} ++static inline void ++ieee80211_proc_init_interface(struct ieee80211_local *local) {} ++static inline void ++ieee80211_proc_deinit_interface(struct ieee80211_local *local) {} ++static inline void ieee80211_proc_init(void) {} ++static inline void ieee80211_proc_deinit(void) {} ++#endif /* CONFIG_PROC_FS */ ++ ++#endif /* IEEE80211_PROC_H */ +diff -Nur linux-2.6.16/net/d80211/ieee80211_scan.c linux-2.6.16-bcm43xx/net/d80211/ieee80211_scan.c +--- linux-2.6.16/net/d80211/ieee80211_scan.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.16-bcm43xx/net/d80211/ieee80211_scan.c 2006-03-28 22:16:14.000000000 +0200 +@@ -0,0 +1,352 @@ ++/* ++ * Copyright 2002-2004, Instant802 Networks, Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include <linux/config.h> ++#include <linux/version.h> ++#include <linux/module.h> ++#include <linux/netdevice.h> ++#include <linux/types.h> ++#include <linux/slab.h> ++#include <linux/skbuff.h> ++ ++#include <net/d80211.h> ++#include "ieee80211_i.h" ++#include "rate_control.h" ++ ++ ++/* Maximum number of seconds to wait for the traffic load to get below ++ * threshold before forcing a passive scan. */ ++#define MAX_SCAN_WAIT 60 ++/* Threshold (pkts/sec TX or RX) for delaying passive scan */ ++#define SCAN_TXRX_THRESHOLD 75 ++ ++static void get_channel_params(struct ieee80211_local *local, int channel, ++ struct ieee80211_hw_modes **mode, ++ struct ieee80211_channel **chan) ++{ ++ int m; ++ ++ for (m = 0; m < local->hw->num_modes; m++) { ++ *mode = &local->hw->modes[m]; ++ if ((*mode)->mode == local->conf.phymode) ++ break; ++ } ++ local->scan.mode_idx = m; ++ local->scan.chan_idx = 0; ++ do { ++ *chan = &(*mode)->channels[local->scan.chan_idx]; ++ if ((*chan)->chan == channel) { ++ return; ++ } ++ local->scan.chan_idx++; ++ } while (local->scan.chan_idx < (*mode)->num_channels); ++ *chan = NULL; ++} ++ ++ ++static void next_chan_same_mode(struct ieee80211_local *local, ++ struct ieee80211_hw_modes **mode, ++ struct ieee80211_channel **chan) ++{ ++ int m, prev; ++ ++ for (m = 0; m < local->hw->num_modes; m++) { ++ *mode = &local->hw->modes[m]; ++ if ((*mode)->mode == local->conf.phymode) ++ break; ++ } ++ local->scan.mode_idx = m; ++ ++ /* Select next channel - scan only channels marked with W_SCAN flag */ ++ prev = local->scan.chan_idx; ++ do { ++ local->scan.chan_idx++; ++ if (local->scan.chan_idx >= (*mode)->num_channels) ++ local->scan.chan_idx = 0; ++ *chan = &(*mode)->channels[local->scan.chan_idx]; ++ if ((*chan)->flag & IEEE80211_CHAN_W_SCAN) ++ break; ++ } while (local->scan.chan_idx != prev); ++} ++ ++ ++static void next_chan_all_modes(struct ieee80211_local *local, ++ struct ieee80211_hw_modes **mode, ++ struct ieee80211_channel **chan) ++{ ++ int prev, prev_m; ++ ++ if (local->scan.mode_idx >= local->hw->num_modes) { ++ local->scan.mode_idx = 0; ++ local->scan.chan_idx = 0; ++ } ++ ++ /* Select next channel - scan only channels marked with W_SCAN flag */ ++ prev = local->scan.chan_idx; ++ prev_m = local->scan.mode_idx; ++ do { ++ *mode = &local->hw->modes[local->scan.mode_idx]; ++ local->scan.chan_idx++; ++ if (local->scan.chan_idx >= (*mode)->num_channels) { ++ local->scan.chan_idx = 0; ++ local->scan.mode_idx++; ++ if (local->scan.mode_idx >= local->hw->num_modes) ++ local->scan.mode_idx = 0; ++ *mode = &local->hw->modes[local->scan.mode_idx]; ++ } ++ *chan = &(*mode)->channels[local->scan.chan_idx]; ++ if ((*chan)->flag & IEEE80211_CHAN_W_SCAN) ++ break; ++ } while (local->scan.chan_idx != prev || ++ local->scan.mode_idx != prev_m); ++} ++ ++ ++static void ieee80211_scan_start(struct net_device *dev, ++ struct ieee80211_scan_conf *conf) ++{ ++ struct ieee80211_local *local = dev->priv; ++ int old_mode_idx = local->scan.mode_idx; ++ int old_chan_idx = local->scan.chan_idx; ++ struct ieee80211_hw_modes *mode = NULL; ++ struct ieee80211_channel *chan = NULL; ++ int ret; ++ ++ if (local->hw->passive_scan == 0) { ++ printk(KERN_DEBUG "%s: Scan handler called, yet the hardware " ++ "does not support passive scanning. Disabled.\n", ++ dev->name); ++ return; ++ } ++ ++ if ((local->scan.tries < MAX_SCAN_WAIT && ++ local->scan.txrx_count > SCAN_TXRX_THRESHOLD)) { ++ local->scan.tries++; ++ /* Count TX/RX packets during one second interval and allow ++ * scan to start only if the number of packets is below the ++ * threshold. */ ++ local->scan.txrx_count = 0; ++ local->scan.timer.expires = jiffies + HZ; ++ add_timer(&local->scan.timer); ++ return; ++ } ++ ++ if (local->scan.skb == NULL) { ++ printk(KERN_DEBUG "%s: Scan start called even though scan.skb " ++ "is not set\n", dev->name); ++ } ++ ++ if (local->scan.our_mode_only) { ++ if (local->scan.channel > 0) { ++ get_channel_params(local, local->scan.channel, &mode, ++ &chan); ++ } else ++ next_chan_same_mode(local, &mode, &chan); ++ } ++ else ++ next_chan_all_modes(local, &mode, &chan); ++ ++ conf->scan_channel = chan->chan; ++ conf->scan_freq = chan->freq; ++ conf->scan_channel_val = chan->val; ++ conf->scan_phymode = mode->mode; ++ conf->scan_power_level = chan->power_level; ++ conf->scan_antenna_max = chan->antenna_max; ++ conf->scan_time = 2 * local->hw->channel_change_time + ++ local->scan.time; /* 10ms scan time+hardware changes */ ++ conf->skb = local->scan.skb ? ++ skb_clone(local->scan.skb, GFP_ATOMIC) : NULL; ++ conf->tx_control = &local->scan.tx_control; ++#if 0 ++ printk(KERN_DEBUG "%s: Doing scan on mode: %d freq: %d chan: %d " ++ "for %d ms\n", ++ dev->name, conf->scan_phymode, conf->scan_freq, ++ conf->scan_channel, conf->scan_time); ++#endif ++ local->scan.rx_packets = 0; ++ local->scan.rx_beacon = 0; ++ local->scan.freq = chan->freq; ++ local->scan.in_scan = 1; ++ ++ ieee80211_netif_oper(dev, NETIF_STOP); ++ ++ ret = local->hw->passive_scan(dev, IEEE80211_SCAN_START, conf); ++ ++ if (ret == 0) { ++ long usec = local->hw->channel_change_time + ++ local->scan.time; ++ usec += 1000000L / HZ - 1; ++ usec /= 1000000L / HZ; ++ local->scan.timer.expires = jiffies + usec; ++ } else { ++ local->scan.in_scan = 0; ++ if (conf->skb) ++ dev_kfree_skb(conf->skb); ++ ieee80211_netif_oper(dev, NETIF_WAKE); ++ if (ret == -EAGAIN) { ++ local->scan.timer.expires = jiffies + ++ (local->scan.interval * HZ / 100); ++ local->scan.mode_idx = old_mode_idx; ++ local->scan.chan_idx = old_chan_idx; ++ } else { ++ printk(KERN_DEBUG "%s: Got unknown error from " ++ "passive_scan %d\n", dev->name, ret); ++ local->scan.timer.expires = jiffies + ++ (local->scan.interval * HZ); ++ } ++ local->scan.in_scan = 0; ++ } ++ ++ add_timer(&local->scan.timer); ++} ++ ++ ++static void ieee80211_scan_stop(struct net_device *dev, ++ struct ieee80211_scan_conf *conf) ++{ ++ struct ieee80211_local *local = dev->priv; ++ struct ieee80211_hw_modes *mode; ++ struct ieee80211_channel *chan; ++ int wait; ++ ++ if (local->hw->passive_scan == NULL) ++ return; ++ ++ if (local->scan.mode_idx >= local->hw->num_modes) { ++ local->scan.mode_idx = 0; ++ local->scan.chan_idx = 0; ++ } ++ ++ mode = &local->hw->modes[local->scan.mode_idx]; ++ ++ if (local->scan.chan_idx >= mode->num_channels) { ++ local->scan.chan_idx = 0; ++ } ++ ++ chan = &mode->channels[local->scan.chan_idx]; ++ ++ local->hw->passive_scan(dev, IEEE80211_SCAN_END, conf); ++ ++#ifdef CONFIG_D80211_VERBOSE_DEBUG ++ printk(KERN_DEBUG "%s: Did scan on mode: %d freq: %d chan: %d " ++ "GOT: %d Beacon: %d (%d)\n", ++ dev->name, ++ mode->mode, chan->freq, chan->chan, ++ local->scan.rx_packets, local->scan.rx_beacon, ++ local->scan.tries); ++#endif /* CONFIG_D80211_VERBOSE_DEBUG */ ++ local->scan.num_scans++; ++ ++ local->scan.in_scan = 0; ++ ieee80211_netif_oper(dev, NETIF_WAKE); ++ ++ local->scan.tries = 0; ++ /* Use random interval of scan.interval .. 2 * scan.interval */ ++ wait = (local->scan.interval * HZ * ((net_random() & 127) + 128)) / ++ 128; ++ local->scan.timer.expires = jiffies + wait; ++ ++ add_timer(&local->scan.timer); ++} ++ ++ ++static void ieee80211_scan_handler(unsigned long uldev) ++{ ++ struct net_device *dev = (struct net_device *) uldev; ++ struct ieee80211_local *local = dev->priv; ++ struct ieee80211_scan_conf conf; ++ ++ if (local->scan.interval == 0 && !local->scan.in_scan) { ++ /* Passive scanning is disabled - keep the timer always ++ * running to make code cleaner. */ ++ local->scan.timer.expires = jiffies + 10 * HZ; ++ add_timer(&local->scan.timer); ++ return; ++ } ++ ++ memset(&conf, 0, sizeof(struct ieee80211_scan_conf)); ++ conf.running_freq = local->conf.freq; ++ conf.running_channel = local->conf.channel; ++ conf.running_phymode = local->conf.phymode; ++ conf.running_channel_val = local->conf.channel_val; ++ conf.running_power_level = local->conf.power_level; ++ conf.running_antenna_max = local->conf.antenna_max; ++ ++ if (local->scan.in_scan == 0) ++ ieee80211_scan_start(dev, &conf); ++ else ++ ieee80211_scan_stop(dev, &conf); ++} ++ ++ ++void ieee80211_init_scan(struct net_device *dev) ++{ ++ struct ieee80211_local *local = dev->priv; ++ struct ieee80211_hdr hdr; ++ u16 fc; ++ int len = 10; ++ struct rate_control_extra extra; ++ ++ /* Only initialize passive scanning if the hardware supports it */ ++ if (!local->hw->passive_scan) { ++ local->scan.skb = NULL; ++ memset(&local->scan.tx_control, 0, ++ sizeof(local->scan.tx_control)); ++ printk(KERN_DEBUG "%s: Does not support passive scan, " ++ "disabled\n", dev->name); ++ return; ++ } ++ ++ local->scan.interval = 0; ++ local->scan.our_mode_only = 1; ++ local->scan.time = 10000; ++ local->scan.timer.function = ieee80211_scan_handler; ++ local->scan.timer.data = (unsigned long) dev; ++ local->scan.timer.expires = jiffies + local->scan.interval * HZ; ++ add_timer(&local->scan.timer); ++ ++ /* Create a CTS from for broadcasting before ++ * the low level changes channels */ ++ local->scan.skb = alloc_skb(len, GFP_KERNEL); ++ if (local->scan.skb == NULL) { ++ printk(KERN_WARNING "%s: Failed to allocate CTS packet for " ++ "passive scan\n", dev->name); ++ return; ++ } ++ ++ fc = (WLAN_FC_TYPE_CTRL << 2) | (WLAN_FC_STYPE_CTS << 4); ++ hdr.frame_control = cpu_to_le16(fc); ++ hdr.duration_id = ++ cpu_to_le16(2 * local->hw->channel_change_time + ++ local->scan.time); ++ memcpy(hdr.addr1, dev->dev_addr, ETH_ALEN); /* DA */ ++ hdr.seq_ctrl = 0; ++ ++ memcpy(skb_put(local->scan.skb, len), &hdr, len); ++ ++ memset(&local->scan.tx_control, 0, sizeof(local->scan.tx_control)); ++ local->scan.tx_control.key_idx = HW_KEY_IDX_INVALID; ++ local->scan.tx_control.do_not_encrypt = 1; ++ memset(&extra, 0, sizeof(extra)); ++ extra.endidx = local->num_curr_rates; ++ local->scan.tx_control.tx_rate = ++ rate_control_get_rate(dev, local->scan.skb, &extra)->val; ++ local->scan.tx_control.no_ack = 1; ++} ++ ++ ++void ieee80211_stop_scan(struct net_device *dev) ++{ ++ struct ieee80211_local *local = dev->priv; ++ ++ if (local->hw->passive_scan != 0) { ++ del_timer_sync(&local->scan.timer); ++ dev_kfree_skb(local->scan.skb); ++ local->scan.skb = NULL; ++ } ++} +diff -Nur linux-2.6.16/net/d80211/ieee80211_sta.c linux-2.6.16-bcm43xx/net/d80211/ieee80211_sta.c +--- linux-2.6.16/net/d80211/ieee80211_sta.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.16-bcm43xx/net/d80211/ieee80211_sta.c 2006-03-28 22:16:14.000000000 +0200 +@@ -0,0 +1,2866 @@ ++/* ++ * BSS client mode implementation ++ * Copyright 2003, Jouni Malinen <jkmaline@cc.hut.fi> ++ * Copyright 2004, Instant802 Networks, Inc. ++ * Copyright 2005, Devicescape Software, Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++/* TODO: ++ * BSS table: use <BSSID,SSID> as the key to support multi-SSID APs ++ * order BSS list by RSSI(?) ("quality of AP") ++ * scan result table filtering (by capability (privacy, IBSS/BSS, WPA/RSN IE, ++ * SSID) ++ */ ++#include <linux/config.h> ++#include <linux/version.h> ++#include <linux/if_ether.h> ++#include <linux/skbuff.h> ++#include <linux/netdevice.h> ++#include <linux/if_arp.h> ++#include <linux/wireless.h> ++#include <linux/random.h> ++#include <net/iw_handler.h> ++#include <asm/types.h> ++#include <asm/delay.h> ++ ++#include <net/d80211.h> ++#include <net/d80211_mgmt.h> ++#include "ieee80211_i.h" ++#include "rate_control.h" ++#include "hostapd_ioctl.h" ++ ++/* #define IEEE80211_IBSS_DEBUG */ ++ ++#define IEEE80211_AUTH_TIMEOUT (HZ / 5) ++#define IEEE80211_AUTH_MAX_TRIES 3 ++#define IEEE80211_ASSOC_TIMEOUT (HZ / 5) ++#define IEEE80211_ASSOC_MAX_TRIES 3 ++#define IEEE80211_MONITORING_INTERVAL (2 * HZ) ++#define IEEE80211_PROBE_INTERVAL (60 * HZ) ++#define IEEE80211_RETRY_AUTH_INTERVAL (1 * HZ) ++#define IEEE80211_SCAN_INTERVAL (2 * HZ) ++#define IEEE80211_SCAN_INTERVAL_SLOW (15 * HZ) ++#define IEEE80211_IBSS_JOIN_TIMEOUT (20 * HZ) ++ ++#define IEEE80211_PROBE_DELAY (HZ / 33) ++#define IEEE80211_CHANNEL_TIME (HZ / 33) ++#define IEEE80211_PASSIVE_CHANNEL_TIME (HZ / 5) ++#define IEEE80211_SCAN_RESULT_EXPIRE (10 * HZ) ++#define IEEE80211_IBSS_MERGE_INTERVAL (30 * HZ) ++#define IEEE80211_IBSS_INACTIVITY_LIMIT (60 * HZ) ++ ++#define IEEE80211_IBSS_MAX_STA_ENTRIES 128 ++ ++ ++#define IEEE80211_FC(type, stype) cpu_to_le16((type << 2) | (stype << 4)) ++ ++#define ERP_INFO_USE_PROTECTION BIT(1) ++ ++static void ieee80211_send_probe_req(struct net_device *dev, u8 *dst, ++ u8 *ssid, size_t ssid_len); ++static struct ieee80211_sta_bss * ++ieee80211_rx_bss_get(struct net_device *dev, u8 *bssid); ++static void ieee80211_rx_bss_put(struct net_device *dev, ++ struct ieee80211_sta_bss *bss); ++static int ieee80211_sta_find_ibss(struct net_device *dev, ++ struct ieee80211_if_sta *ifsta); ++static int ieee80211_sta_wep_configured(struct net_device *dev); ++ ++ ++/* Parsed Information Elements */ ++struct ieee802_11_elems { ++ u8 *ssid; ++ u8 ssid_len; ++ u8 *supp_rates; ++ u8 supp_rates_len; ++ u8 *fh_params; ++ u8 fh_params_len; ++ u8 *ds_params; ++ u8 ds_params_len; ++ u8 *cf_params; ++ u8 cf_params_len; ++ u8 *tim; ++ u8 tim_len; ++ u8 *ibss_params; ++ u8 ibss_params_len; ++ u8 *challenge; ++ u8 challenge_len; ++ u8 *wpa; ++ u8 wpa_len; ++ u8 *rsn; ++ u8 rsn_len; ++ u8 *erp_info; ++ u8 erp_info_len; ++ u8 *ext_supp_rates; ++ u8 ext_supp_rates_len; ++ u8 *wmm_info; ++ u8 wmm_info_len; ++ u8 *wmm_param; ++ u8 wmm_param_len; ++}; ++ ++typedef enum { ParseOK = 0, ParseUnknown = 1, ParseFailed = -1 } ParseRes; ++ ++ ++static ParseRes ieee802_11_parse_elems(u8 *start, size_t len, ++ struct ieee802_11_elems *elems) ++{ ++ size_t left = len; ++ u8 *pos = start; ++ int unknown = 0; ++ ++ memset(elems, 0, sizeof(*elems)); ++ ++ while (left >= 2) { ++ u8 id, elen; ++ ++ id = *pos++; ++ elen = *pos++; ++ left -= 2; ++ ++ if (elen > left) { ++#if 0 ++ if (net_ratelimit()) ++ printk(KERN_DEBUG "IEEE 802.11 element parse " ++ "failed (id=%d elen=%d left=%d)\n", ++ id, elen, left); ++#endif ++ return ParseFailed; ++ } ++ ++ switch (id) { ++ case WLAN_EID_SSID: ++ elems->ssid = pos; ++ elems->ssid_len = elen; ++ break; ++ case WLAN_EID_SUPP_RATES: ++ elems->supp_rates = pos; ++ elems->supp_rates_len = elen; ++ break; ++ case WLAN_EID_FH_PARAMS: ++ elems->fh_params = pos; ++ elems->fh_params_len = elen; ++ break; ++ case WLAN_EID_DS_PARAMS: ++ elems->ds_params = pos; ++ elems->ds_params_len = elen; ++ break; ++ case WLAN_EID_CF_PARAMS: ++ elems->cf_params = pos; ++ elems->cf_params_len = elen; ++ break; ++ case WLAN_EID_TIM: ++ elems->tim = pos; ++ elems->tim_len = elen; ++ break; ++ case WLAN_EID_IBSS_PARAMS: ++ elems->ibss_params = pos; ++ elems->ibss_params_len = elen; ++ break; ++ case WLAN_EID_CHALLENGE: ++ elems->challenge = pos; ++ elems->challenge_len = elen; ++ break; ++ case WLAN_EID_WPA: ++ if (elen >= 4 && pos[0] == 0x00 && pos[1] == 0x50 && ++ pos[2] == 0xf2) { ++ /* Microsoft OUI (00:50:F2) */ ++ if (pos[3] == 1) { ++ /* OUI Type 1 - WPA IE */ ++ elems->wpa = pos; ++ elems->wpa_len = elen; ++ } else if (elen >= 5 && pos[3] == 2) { ++ if (pos[4] == 0) { ++ elems->wmm_info = pos; ++ elems->wmm_info_len = elen; ++ } else if (pos[4] == 1) { ++ elems->wmm_param = pos; ++ elems->wmm_param_len = elen; ++ } ++ } ++ } ++ break; ++ case WLAN_EID_RSN: ++ elems->rsn = pos; ++ elems->rsn_len = elen; ++ break; ++ case WLAN_EID_ERP_INFO: ++ elems->erp_info = pos; ++ elems->erp_info_len = elen; ++ break; ++ case WLAN_EID_EXT_SUPP_RATES: ++ elems->ext_supp_rates = pos; ++ elems->ext_supp_rates_len = elen; ++ break; ++ default: ++#if 0 ++ printk(KERN_DEBUG "IEEE 802.11 element parse ignored " ++ "unknown element (id=%d elen=%d)\n", ++ id, elen); ++#endif ++ unknown++; ++ break; ++ } ++ ++ left -= elen; ++ pos += elen; ++ } ++ ++ /* Do not trigger error if left == 1 as Apple Airport base stations ++ * send AssocResps that are one spurious byte too long. */ ++ ++ return unknown ? ParseUnknown : ParseOK; ++} ++ ++ ++ ++ ++static int ecw2cw(int ecw) ++{ ++ int cw = 1; ++ while (ecw > 0) { ++ cw <<= 1; ++ ecw--; ++ } ++ return cw - 1; ++} ++ ++ ++static void ieee80211_sta_wmm_params(struct net_device *dev, ++ struct ieee80211_if_sta *ifsta, ++ u8 *wmm_param, size_t wmm_param_len) ++{ ++ struct ieee80211_local *local = dev->priv; ++ struct ieee80211_tx_queue_params params; ++ size_t left; ++ int count; ++ u8 *pos; ++ ++ if (wmm_param_len < 8 || wmm_param[5] /* version */ != 1) ++ return; ++ count = wmm_param[6] & 0x0f; ++ if (count == ifsta->wmm_last_param_set) ++ return; ++ ifsta->wmm_last_param_set = count; ++ ++ pos = wmm_param + 8; ++ left = wmm_param_len - 8; ++ ++ memset(¶ms, 0, sizeof(params)); ++ ++ if (local->hw->conf_tx == NULL) ++ return; ++ ++ local->wmm_acm = 0; ++ for (; left >= 4; left -= 4, pos += 4) { ++ int aci = (pos[0] >> 5) & 0x03; ++ int acm = (pos[0] >> 4) & 0x01; ++ int queue; ++ ++ switch (aci) { ++ case 1: ++ queue = IEEE80211_TX_QUEUE_DATA3; ++ if (acm) { ++ local->wmm_acm |= BIT(1) | BIT(2); ++ } ++ break; ++ case 2: ++ queue = IEEE80211_TX_QUEUE_DATA1; ++ if (acm) { ++ local->wmm_acm |= BIT(4) | BIT(5); ++ } ++ break; ++ case 3: ++ queue = IEEE80211_TX_QUEUE_DATA0; ++ if (acm) { ++ local->wmm_acm |= BIT(6) | BIT(7); ++ } ++ break; ++ case 0: ++ default: ++ queue = IEEE80211_TX_QUEUE_DATA2; ++ if (acm) { ++ local->wmm_acm |= BIT(0) | BIT(3); ++ } ++ break; ++ } ++ ++ params.aifs = pos[0] & 0x0f; ++ params.cw_max = ecw2cw((pos[1] & 0xf0) >> 4); ++ params.cw_min = ecw2cw(pos[1] & 0x0f); ++ /* TXOP is in units of 32 usec; burst_time in 0.1 ms */ ++ params.burst_time = (pos[2] | (pos[3] << 8)) * 32 / 100; ++ printk(KERN_DEBUG "%s: WMM queue=%d aci=%d acm=%d aifs=%d " ++ "cWmin=%d cWmax=%d burst=%d\n", ++ dev->name, queue, aci, acm, params.aifs, params.cw_min, ++ params.cw_max, params.burst_time); ++ /* TODO: handle ACM (block TX, fallback to next lowest allowed ++ * AC for now) */ ++ if (local->hw->conf_tx(local->mdev, queue, ¶ms)) { ++ printk(KERN_DEBUG "%s: failed to set TX queue " ++ "parameters for queue %d\n", dev->name, queue); ++ } ++ } ++} ++ ++ ++static void ieee80211_sta_send_associnfo(struct net_device *dev, ++ struct ieee80211_if_sta *ifsta) ++{ ++ char *buf; ++ size_t len; ++ int i; ++ union iwreq_data wrqu; ++ ++ if (ifsta->assocreq_ies == NULL && ifsta->assocresp_ies == NULL) ++ return; ++ ++ buf = kmalloc(50 + 2 * (ifsta->assocreq_ies_len + ++ ifsta->assocresp_ies_len), GFP_ATOMIC); ++ if (buf == NULL) ++ return; ++ ++ len = sprintf(buf, "ASSOCINFO("); ++ if (ifsta->assocreq_ies) { ++ len += sprintf(buf + len, "ReqIEs="); ++ for (i = 0; i < ifsta->assocreq_ies_len; i++) { ++ len += sprintf(buf + len, "%02x", ++ ifsta->assocreq_ies[i]); ++ } ++ } ++ if (ifsta->assocresp_ies) { ++ if (ifsta->assocreq_ies) ++ len += sprintf(buf + len, " "); ++ len += sprintf(buf + len, "RespIEs="); ++ for (i = 0; i < ifsta->assocresp_ies_len; i++) { ++ len += sprintf(buf + len, "%02x", ++ ifsta->assocresp_ies[i]); ++ } ++ } ++ len += sprintf(buf + len, ")"); ++ ++ if (len > IW_CUSTOM_MAX) { ++ len = sprintf(buf, "ASSOCRESPIE="); ++ for (i = 0; i < ifsta->assocresp_ies_len; i++) { ++ len += sprintf(buf + len, "%02x", ++ ifsta->assocresp_ies[i]); ++ } ++ } ++ ++ memset(&wrqu, 0, sizeof(wrqu)); ++ wrqu.data.length = len; ++ wireless_send_event(dev, IWEVCUSTOM, &wrqu, buf); ++ ++ kfree(buf); ++} ++ ++ ++static void ieee80211_set_associated(struct net_device *dev, ++ struct ieee80211_if_sta *ifsta, int assoc) ++{ ++ union iwreq_data wrqu; ++ ++ if (ifsta->associated == assoc) ++ return; ++ ++ ifsta->associated = assoc; ++ ++ if (assoc) { ++ struct ieee80211_sub_if_data *sdata; ++ sdata = IEEE80211_DEV_TO_SUB_IF(dev); ++ if (sdata->type != IEEE80211_SUB_IF_TYPE_STA) ++ return; ++ ifsta->prev_bssid_set = 1; ++ memcpy(ifsta->prev_bssid, sdata->u.sta.bssid, ETH_ALEN); ++ memcpy(wrqu.ap_addr.sa_data, sdata->u.sta.bssid, ETH_ALEN); ++ ieee80211_sta_send_associnfo(dev, ifsta); ++ } else { ++ memset(wrqu.ap_addr.sa_data, 0, ETH_ALEN); ++ } ++ wrqu.ap_addr.sa_family = ARPHRD_ETHER; ++ wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); ++ ifsta->last_probe = jiffies; ++} ++ ++ ++static void ieee80211_sta_tx(struct net_device *dev, struct sk_buff *skb, ++ int encrypt, int probe_resp) ++{ ++ struct ieee80211_sub_if_data *sdata; ++ struct ieee80211_tx_packet_data *pkt_data; ++ ++ sdata = IEEE80211_DEV_TO_SUB_IF(dev); ++ skb->dev = sdata->master; ++ skb->mac.raw = skb->nh.raw = skb->h.raw = skb->data; ++ ++ pkt_data = (struct ieee80211_tx_packet_data *) skb->cb; ++ memset(pkt_data, 0, sizeof(struct ieee80211_tx_packet_data)); ++ pkt_data->sdata = sdata; ++ pkt_data->do_not_encrypt = !encrypt; ++ if (probe_resp) ++ pkt_data->pkt_probe_resp = 1; ++ ++ dev_queue_xmit(skb); ++} ++ ++ ++static void ieee80211_send_auth(struct net_device *dev, ++ struct ieee80211_if_sta *ifsta, ++ int transaction, u8 *extra, size_t extra_len, ++ int encrypt) ++{ ++ struct sk_buff *skb; ++ struct ieee80211_mgmt *mgmt; ++ ++ skb = dev_alloc_skb(sizeof(*mgmt) + 6 + extra_len); ++ if (skb == NULL) { ++ printk(KERN_DEBUG "%s: failed to allocate buffer for auth " ++ "frame\n", dev->name); ++ return; ++ } ++ ++ mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24 + 6); ++ memset(mgmt, 0, 24 + 6); ++ mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, ++ WLAN_FC_STYPE_AUTH); ++ if (encrypt) ++ mgmt->frame_control |= cpu_to_le16(WLAN_FC_ISWEP); ++ memcpy(mgmt->da, ifsta->bssid, ETH_ALEN); ++ memcpy(mgmt->sa, dev->dev_addr, ETH_ALEN); ++ memcpy(mgmt->bssid, ifsta->bssid, ETH_ALEN); ++ mgmt->u.auth.auth_alg = cpu_to_le16(ifsta->auth_alg); ++ mgmt->u.auth.auth_transaction = cpu_to_le16(transaction); ++ ifsta->auth_transaction = transaction + 1; ++ mgmt->u.auth.status_code = cpu_to_le16(0); ++ if (extra) ++ memcpy(skb_put(skb, extra_len), extra, extra_len); ++ ++ ieee80211_sta_tx(dev, skb, encrypt, 0); ++} ++ ++ ++static void ieee80211_authenticate(struct net_device *dev, ++ struct ieee80211_if_sta *ifsta) ++{ ++ ifsta->auth_tries++; ++ if (ifsta->auth_tries > IEEE80211_AUTH_MAX_TRIES) { ++ printk(KERN_DEBUG "%s: authentication with AP " MACSTR ++ " timed out\n", ++ dev->name, MAC2STR(ifsta->bssid)); ++ return; ++ } ++ ++ ifsta->state = IEEE80211_AUTHENTICATE; ++ printk(KERN_DEBUG "%s: authenticate with AP " MACSTR "\n", ++ dev->name, MAC2STR(ifsta->bssid)); ++ ++ ieee80211_send_auth(dev, ifsta, 1, NULL, 0, 0); ++ ++ mod_timer(&ifsta->timer, jiffies + IEEE80211_AUTH_TIMEOUT); ++} ++ ++ ++static void ieee80211_send_assoc(struct net_device *dev, ++ struct ieee80211_if_sta *ifsta) ++{ ++ struct ieee80211_local *local = dev->priv; ++ struct sk_buff *skb; ++ struct ieee80211_mgmt *mgmt; ++ u8 *pos, *ies; ++ int i, len; ++ u16 capab; ++ struct ieee80211_sta_bss *bss; ++ int wmm = 0; ++ ++ skb = dev_alloc_skb(sizeof(*mgmt) + 200 + ifsta->extra_ie_len + ++ ifsta->ssid_len); ++ if (skb == NULL) { ++ printk(KERN_DEBUG "%s: failed to allocate buffer for assoc " ++ "frame\n", dev->name); ++ return; ++ } ++ ++ capab = ifsta->capab; ++ if (local->conf.phymode == MODE_IEEE80211G) { ++ capab |= WLAN_CAPABILITY_SHORT_SLOT_TIME | ++ WLAN_CAPABILITY_SHORT_PREAMBLE; ++ } ++ bss = ieee80211_rx_bss_get(dev, ifsta->bssid); ++ if (bss) { ++ if (bss->capability & WLAN_CAPABILITY_PRIVACY) ++ capab |= WLAN_CAPABILITY_PRIVACY; ++ if (bss->wmm_ie) { ++ wmm = 1; ++ } ++ ieee80211_rx_bss_put(dev, bss); ++ } ++ ++ mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24); ++ memset(mgmt, 0, 24); ++ memcpy(mgmt->da, ifsta->bssid, ETH_ALEN); ++ memcpy(mgmt->sa, dev->dev_addr, ETH_ALEN); ++ memcpy(mgmt->bssid, ifsta->bssid, ETH_ALEN); ++ ++ if (ifsta->prev_bssid_set) { ++ skb_put(skb, 10); ++ mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, ++ WLAN_FC_STYPE_REASSOC_REQ); ++ mgmt->u.reassoc_req.capab_info = cpu_to_le16(capab); ++ mgmt->u.reassoc_req.listen_interval = cpu_to_le16(1); ++ memcpy(mgmt->u.reassoc_req.current_ap, ifsta->prev_bssid, ++ ETH_ALEN); ++ } else { ++ skb_put(skb, 4); ++ mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, ++ WLAN_FC_STYPE_ASSOC_REQ); ++ mgmt->u.assoc_req.capab_info = cpu_to_le16(capab); ++ mgmt->u.assoc_req.listen_interval = cpu_to_le16(1); ++ } ++ ++ /* SSID */ ++ ies = pos = skb_put(skb, 2 + ifsta->ssid_len); ++ *pos++ = WLAN_EID_SSID; ++ *pos++ = ifsta->ssid_len; ++ memcpy(pos, ifsta->ssid, ifsta->ssid_len); ++ ++ len = local->num_curr_rates; ++ if (len > 8) ++ len = 8; ++ pos = skb_put(skb, len + 2); ++ *pos++ = WLAN_EID_SUPP_RATES; ++ *pos++ = len; ++ for (i = 0; i < len; i++) { ++ int rate = local->curr_rates[i].rate; ++ if (local->conf.phymode == MODE_ATHEROS_TURBO) ++ rate /= 2; ++ *pos++ = (u8) (rate / 5); ++ } ++ ++ if (local->num_curr_rates > len) { ++ pos = skb_put(skb, local->num_curr_rates - len + 2); ++ *pos++ = WLAN_EID_EXT_SUPP_RATES; ++ *pos++ = local->num_curr_rates - len; ++ for (i = len; i < local->num_curr_rates; i++) { ++ int rate = local->curr_rates[i].rate; ++ if (local->conf.phymode == MODE_ATHEROS_TURBO) ++ rate /= 2; ++ *pos++ = (u8) (rate / 5); ++ } ++ } ++ ++ if (ifsta->extra_ie) { ++ pos = skb_put(skb, ifsta->extra_ie_len); ++ memcpy(pos, ifsta->extra_ie, ifsta->extra_ie_len); ++ } ++ ++ if (wmm && ifsta->wmm_enabled) { ++ pos = skb_put(skb, 9); ++ *pos++ = WLAN_EID_VENDOR_SPECIFIC; ++ *pos++ = 7; /* len */ ++ *pos++ = 0x00; /* Microsoft OUI 00:50:F2 */ ++ *pos++ = 0x50; ++ *pos++ = 0xf2; ++ *pos++ = 2; /* WME */ ++ *pos++ = 0; /* WME info */ ++ *pos++ = 1; /* WME ver */ ++ *pos++ = 0; ++ } ++ ++ kfree(ifsta->assocreq_ies); ++ ifsta->assocreq_ies_len = (skb->data + skb->len) - ies; ++ ifsta->assocreq_ies = kmalloc(ifsta->assocreq_ies_len, GFP_ATOMIC); ++ if (ifsta->assocreq_ies) ++ memcpy(ifsta->assocreq_ies, ies, ifsta->assocreq_ies_len); ++ ++ ieee80211_sta_tx(dev, skb, 0, 0); ++} ++ ++ ++static void ieee80211_send_deauth(struct net_device *dev, ++ struct ieee80211_if_sta *ifsta, u16 reason) ++{ ++ struct sk_buff *skb; ++ struct ieee80211_mgmt *mgmt; ++ ++ skb = dev_alloc_skb(sizeof(*mgmt)); ++ if (skb == NULL) { ++ printk(KERN_DEBUG "%s: failed to allocate buffer for deauth " ++ "frame\n", dev->name); ++ return; ++ } ++ ++ mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24); ++ memset(mgmt, 0, 24); ++ memcpy(mgmt->da, ifsta->bssid, ETH_ALEN); ++ memcpy(mgmt->sa, dev->dev_addr, ETH_ALEN); ++ memcpy(mgmt->bssid, ifsta->bssid, ETH_ALEN); ++ mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, ++ WLAN_FC_STYPE_DEAUTH); ++ skb_put(skb, 2); ++ mgmt->u.deauth.reason_code = cpu_to_le16(reason); ++ ++ ieee80211_sta_tx(dev, skb, 0, 0); ++} ++ ++ ++static void ieee80211_send_disassoc(struct net_device *dev, ++ struct ieee80211_if_sta *ifsta, u16 reason) ++{ ++ struct sk_buff *skb; ++ struct ieee80211_mgmt *mgmt; ++ ++ skb = dev_alloc_skb(sizeof(*mgmt)); ++ if (skb == NULL) { ++ printk(KERN_DEBUG "%s: failed to allocate buffer for disassoc " ++ "frame\n", dev->name); ++ return; ++ } ++ ++ mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24); ++ memset(mgmt, 0, 24); ++ memcpy(mgmt->da, ifsta->bssid, ETH_ALEN); ++ memcpy(mgmt->sa, dev->dev_addr, ETH_ALEN); ++ memcpy(mgmt->bssid, ifsta->bssid, ETH_ALEN); ++ mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, ++ WLAN_FC_STYPE_DISASSOC); ++ skb_put(skb, 2); ++ mgmt->u.disassoc.reason_code = cpu_to_le16(reason); ++ ++ ieee80211_sta_tx(dev, skb, 0, 0); ++} ++ ++ ++static int ieee80211_privacy_mismatch(struct net_device *dev, ++ struct ieee80211_if_sta *ifsta) ++{ ++ struct ieee80211_sta_bss *bss; ++ int res = 0; ++ ++ if (ifsta == NULL || ifsta->mixed_cell || ++ ifsta->key_mgmt != IEEE80211_KEY_MGMT_NONE) ++ return 0; ++ ++ bss = ieee80211_rx_bss_get(dev, ifsta->bssid); ++ if (bss == NULL) ++ return 0; ++ ++ if (ieee80211_sta_wep_configured(dev) != ++ !!(bss->capability & WLAN_CAPABILITY_PRIVACY)) ++ res = 1; ++ ++ ieee80211_rx_bss_put(dev, bss); ++ ++ return res; ++} ++ ++ ++static void ieee80211_associate(struct net_device *dev, ++ struct ieee80211_if_sta *ifsta) ++{ ++ ifsta->assoc_tries++; ++ if (ifsta->assoc_tries > IEEE80211_ASSOC_MAX_TRIES) { ++ printk(KERN_DEBUG "%s: association with AP " MACSTR ++ " timed out\n", ++ dev->name, MAC2STR(ifsta->bssid)); ++ return; ++ } ++ ++ ifsta->state = IEEE80211_ASSOCIATE; ++ printk(KERN_DEBUG "%s: associate with AP " MACSTR "\n", ++ dev->name, MAC2STR(ifsta->bssid)); ++ if (ieee80211_privacy_mismatch(dev, ifsta)) { ++ printk(KERN_DEBUG "%s: mismatch in privacy configuration and " ++ "mixed-cell disabled - abort association\n", dev->name); ++ return; ++ } ++ ++ ieee80211_send_assoc(dev, ifsta); ++ ++ mod_timer(&ifsta->timer, jiffies + IEEE80211_ASSOC_TIMEOUT); ++} ++ ++ ++static void ieee80211_associated(struct net_device *dev, ++ struct ieee80211_if_sta *ifsta) ++{ ++ struct ieee80211_local *local = dev->priv; ++ struct sta_info *sta; ++ int disassoc; ++ ++ /* TODO: start monitoring current AP signal quality and number of ++ * missed beacons. Scan other channels every now and then and search ++ * for better APs. */ ++ /* TODO: remove expired BSSes */ ++ ++ ifsta->state = IEEE80211_ASSOCIATED; ++ ++ sta = sta_info_get(local, ifsta->bssid); ++ if (sta == NULL) { ++ printk(KERN_DEBUG "%s: No STA entry for own AP " MACSTR "\n", ++ dev->name, MAC2STR(ifsta->bssid)); ++ disassoc = 1; ++ } else { ++ disassoc = 0; ++ if (time_after(jiffies, ++ sta->last_rx + IEEE80211_MONITORING_INTERVAL)) { ++ if (ifsta->probereq_poll) { ++ printk(KERN_DEBUG "%s: No ProbeResp from " ++ "current AP " MACSTR " - assume out of " ++ "range\n", ++ dev->name, MAC2STR(ifsta->bssid)); ++ disassoc = 1; ++ } else { ++ ieee80211_send_probe_req(dev, ifsta->bssid, ++ local->scan_ssid, ++ local->scan_ssid_len); ++ ifsta->probereq_poll = 1; ++ } ++ } else { ++ ifsta->probereq_poll = 0; ++ if (time_after(jiffies, ifsta->last_probe + ++ IEEE80211_PROBE_INTERVAL)) { ++ ifsta->last_probe = jiffies; ++ ieee80211_send_probe_req(dev, ifsta->bssid, ++ ifsta->ssid, ++ ifsta->ssid_len); ++ } ++ } ++ sta_info_release(local, sta); ++ } ++ if (disassoc) { ++ union iwreq_data wrqu; ++ memset(wrqu.ap_addr.sa_data, 0, ETH_ALEN); ++ wrqu.ap_addr.sa_family = ARPHRD_ETHER; ++ wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); ++ mod_timer(&ifsta->timer, ++ jiffies + IEEE80211_MONITORING_INTERVAL + 30 * HZ); ++ } else { ++ mod_timer(&ifsta->timer, ++ jiffies + IEEE80211_MONITORING_INTERVAL); ++ } ++} ++ ++ ++static void ieee80211_send_probe_req(struct net_device *dev, u8 *dst, ++ u8 *ssid, size_t ssid_len) ++{ ++ struct ieee80211_local *local = dev->priv; ++ struct sk_buff *skb; ++ struct ieee80211_mgmt *mgmt; ++ u8 *pos, *supp_rates, *esupp_rates = NULL; ++ int i; ++ ++ skb = dev_alloc_skb(sizeof(*mgmt) + 200); ++ if (skb == NULL) { ++ printk(KERN_DEBUG "%s: failed to allocate buffer for probe " ++ "request\n", dev->name); ++ return; ++ } ++ ++ mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24); ++ memset(mgmt, 0, 24); ++ mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, ++ WLAN_FC_STYPE_PROBE_REQ); ++ memcpy(mgmt->sa, dev->dev_addr, ETH_ALEN); ++ if (dst) { ++ memcpy(mgmt->da, dst, ETH_ALEN); ++ memcpy(mgmt->bssid, dst, ETH_ALEN); ++ } else { ++ memset(mgmt->da, 0xff, ETH_ALEN); ++ memset(mgmt->bssid, 0xff, ETH_ALEN); ++ } ++ pos = skb_put(skb, 2 + ssid_len); ++ *pos++ = WLAN_EID_SSID; ++ *pos++ = ssid_len; ++ memcpy(pos, ssid, ssid_len); ++ ++ supp_rates = skb_put(skb, 2); ++ supp_rates[0] = WLAN_EID_SUPP_RATES; ++ supp_rates[1] = 0; ++ for (i = 0; i < local->num_curr_rates; i++) { ++ struct ieee80211_rate *rate = &local->curr_rates[i]; ++ if (!(rate->flags & IEEE80211_RATE_SUPPORTED)) ++ continue; ++ if (esupp_rates) { ++ pos = skb_put(skb, 1); ++ esupp_rates[1]++; ++ } else if (supp_rates[1] == 8) { ++ esupp_rates = skb_put(skb, 3); ++ esupp_rates[0] = WLAN_EID_EXT_SUPP_RATES; ++ esupp_rates[1] = 1; ++ pos = &esupp_rates[2]; ++ } else { ++ pos = skb_put(skb, 1); ++ supp_rates[1]++; ++ } ++ if (local->conf.phymode == MODE_ATHEROS_TURBO) ++ *pos = rate->rate / 10; ++ else ++ *pos = rate->rate / 5; ++ } ++ ++ ieee80211_sta_tx(dev, skb, 0, 0); ++} ++ ++ ++static int ieee80211_sta_wep_configured(struct net_device *dev) ++{ ++ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); ++ if (sdata == NULL || sdata->default_key == NULL || ++ sdata->default_key->alg != ALG_WEP) ++ return 0; ++ return 1; ++} ++ ++ ++static void ieee80211_auth_completed(struct net_device *dev, ++ struct ieee80211_if_sta *ifsta) ++{ ++ printk(KERN_DEBUG "%s: authenticated\n", dev->name); ++ ifsta->authenticated = 1; ++ ieee80211_associate(dev, ifsta); ++} ++ ++ ++static void ieee80211_auth_challenge(struct net_device *dev, ++ struct ieee80211_if_sta *ifsta, ++ struct ieee80211_mgmt *mgmt, ++ size_t len, ++ struct ieee80211_rx_status *rx_status) ++{ ++ u8 *pos; ++ struct ieee802_11_elems elems; ++ ++ printk(KERN_DEBUG "%s: replying to auth challenge\n", dev->name); ++ pos = mgmt->u.auth.variable; ++ if (ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), &elems) ++ == ParseFailed) { ++ printk(KERN_DEBUG "%s: failed to parse Auth(challenge)\n", ++ dev->name); ++ return; ++ } ++ if (elems.challenge == NULL) { ++ printk(KERN_DEBUG "%s: no challenge IE in shared key auth " ++ "frame\n", dev->name); ++ return; ++ } ++ ieee80211_send_auth(dev, ifsta, 3, elems.challenge - 2, ++ elems.challenge_len + 2, 1); ++} ++ ++ ++static void ieee80211_rx_mgmt_auth(struct net_device *dev, ++ struct ieee80211_if_sta *ifsta, ++ struct ieee80211_mgmt *mgmt, ++ size_t len, ++ struct ieee80211_rx_status *rx_status) ++{ ++ struct ieee80211_local *local = dev->priv; ++ u16 auth_alg, auth_transaction, status_code; ++ ++ if (ifsta->state != IEEE80211_AUTHENTICATE && ++ local->conf.mode != IW_MODE_ADHOC) { ++ printk(KERN_DEBUG "%s: authentication frame received from " ++ MACSTR ", but not in authenticate state - ignored\n", ++ dev->name, MAC2STR(mgmt->sa)); ++ return; ++ } ++ ++ if (len < 24 + 6) { ++ printk(KERN_DEBUG "%s: too short (%zd) authentication frame " ++ "received from " MACSTR " - ignored\n", ++ dev->name, len, MAC2STR(mgmt->sa)); ++ return; ++ } ++ ++ if (local->conf.mode != IW_MODE_ADHOC && ++ memcmp(ifsta->bssid, mgmt->sa, ETH_ALEN) != 0) { ++ printk(KERN_DEBUG "%s: authentication frame received from " ++ "unknown AP (SA=" MACSTR " BSSID=" MACSTR ") - " ++ "ignored\n", dev->name, MAC2STR(mgmt->sa), ++ MAC2STR(mgmt->bssid)); ++ return; ++ } ++ ++ if (local->conf.mode == IW_MODE_ADHOC && ++ memcmp(ifsta->bssid, mgmt->bssid, ETH_ALEN) != 0) { ++ printk(KERN_DEBUG "%s: authentication frame received from " ++ "unknown BSSID (SA=" MACSTR " BSSID=" MACSTR ") - " ++ "ignored\n", dev->name, MAC2STR(mgmt->sa), ++ MAC2STR(mgmt->bssid)); ++ return; ++ } ++ ++ auth_alg = le16_to_cpu(mgmt->u.auth.auth_alg); ++ auth_transaction = le16_to_cpu(mgmt->u.auth.auth_transaction); ++ status_code = le16_to_cpu(mgmt->u.auth.status_code); ++ ++ printk(KERN_DEBUG "%s: RX authentication from " MACSTR " (alg=%d " ++ "transaction=%d status=%d)\n", ++ dev->name, MAC2STR(mgmt->sa), auth_alg, ++ auth_transaction, status_code); ++ ++ if (local->conf.mode == IW_MODE_ADHOC) { ++ /* IEEE 802.11 standard does not require authentication in IBSS ++ * networks and most implementations do not seem to use it. ++ * However, try to reply to authentication attempts if someone ++ * has actually implemented this. ++ * TODO: Could implement shared key authentication. */ ++ if (auth_alg != WLAN_AUTH_OPEN || auth_transaction != 1) { ++ printk(KERN_DEBUG "%s: unexpected IBSS authentication " ++ "frame (alg=%d transaction=%d)\n", ++ dev->name, auth_alg, auth_transaction); ++ return; ++ } ++ ieee80211_send_auth(dev, ifsta, 2, NULL, 0, 0); ++ } ++ ++ if (auth_alg != ifsta->auth_alg || ++ auth_transaction != ifsta->auth_transaction) { ++ printk(KERN_DEBUG "%s: unexpected authentication frame " ++ "(alg=%d transaction=%d)\n", ++ dev->name, auth_alg, auth_transaction); ++ return; ++ } ++ ++ if (status_code != WLAN_STATUS_SUCCESS) { ++ printk(KERN_DEBUG "%s: AP denied authentication (auth_alg=%d " ++ "code=%d)\n", dev->name, ifsta->auth_alg, status_code); ++ if (status_code == WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG) { ++ const int num_algs = 3; ++ u8 algs[num_algs]; ++ int i, pos; ++ algs[0] = algs[1] = algs[2] = 0xff; ++ if (ifsta->auth_algs & IEEE80211_AUTH_ALG_OPEN) ++ algs[0] = WLAN_AUTH_OPEN; ++ if (ifsta->auth_algs & IEEE80211_AUTH_ALG_SHARED_KEY) ++ algs[1] = WLAN_AUTH_SHARED_KEY; ++ if (ifsta->auth_algs & IEEE80211_AUTH_ALG_LEAP) ++ algs[2] = WLAN_AUTH_LEAP; ++ if (ifsta->auth_alg == WLAN_AUTH_OPEN) ++ pos = 0; ++ else if (ifsta->auth_alg == WLAN_AUTH_SHARED_KEY) ++ pos = 1; ++ else ++ pos = 2; ++ for (i = 0; i < num_algs; i++) { ++ pos++; ++ if (pos >= num_algs) ++ pos = 0; ++ if (algs[pos] == ifsta->auth_alg || ++ algs[pos] == 0xff) ++ continue; ++ if (algs[pos] == WLAN_AUTH_SHARED_KEY && ++ !ieee80211_sta_wep_configured(dev)) ++ continue; ++ ifsta->auth_alg = algs[pos]; ++ printk(KERN_DEBUG "%s: set auth_alg=%d for " ++ "next try\n", ++ dev->name, ifsta->auth_alg); ++ break; ++ } ++ } ++ return; ++ } ++ ++ switch (ifsta->auth_alg) { ++ case WLAN_AUTH_OPEN: ++ case WLAN_AUTH_LEAP: ++ ieee80211_auth_completed(dev, ifsta); ++ break; ++ case WLAN_AUTH_SHARED_KEY: ++ if (ifsta->auth_transaction == 4) ++ ieee80211_auth_completed(dev, ifsta); ++ else ++ ieee80211_auth_challenge(dev, ifsta, mgmt, len, ++ rx_status); ++ break; ++ } ++} ++ ++ ++static void ieee80211_rx_mgmt_deauth(struct net_device *dev, ++ struct ieee80211_if_sta *ifsta, ++ struct ieee80211_mgmt *mgmt, ++ size_t len, ++ struct ieee80211_rx_status *rx_status) ++{ ++ u16 reason_code; ++ ++ if (len < 24 + 2) { ++ printk(KERN_DEBUG "%s: too short (%zd) deauthentication frame " ++ "received from " MACSTR " - ignored\n", ++ dev->name, len, MAC2STR(mgmt->sa)); ++ return; ++ } ++ ++ if (memcmp(ifsta->bssid, mgmt->sa, ETH_ALEN) != 0) { ++ printk(KERN_DEBUG "%s: deauthentication frame received from " ++ "unknown AP (SA=" MACSTR " BSSID=" MACSTR ") - " ++ "ignored\n", dev->name, MAC2STR(mgmt->sa), ++ MAC2STR(mgmt->bssid)); ++ return; ++ } ++ ++ reason_code = le16_to_cpu(mgmt->u.deauth.reason_code); ++ ++ printk(KERN_DEBUG "%s: RX deauthentication from " MACSTR ++ " (reason=%d)\n", ++ dev->name, MAC2STR(mgmt->sa), reason_code); ++ ++ if (ifsta->authenticated) { ++ printk(KERN_DEBUG "%s: deauthenticated\n", dev->name); ++ } ++ ++ if (ifsta->state == IEEE80211_AUTHENTICATE || ++ ifsta->state == IEEE80211_ASSOCIATE || ++ ifsta->state == IEEE80211_ASSOCIATED) { ++ ifsta->state = IEEE80211_AUTHENTICATE; ++ mod_timer(&ifsta->timer, ++ jiffies + IEEE80211_RETRY_AUTH_INTERVAL); ++ } ++ ++ ieee80211_set_associated(dev, ifsta, 0); ++ ifsta->authenticated = 0; ++} ++ ++ ++static void ieee80211_rx_mgmt_disassoc(struct net_device *dev, ++ struct ieee80211_if_sta *ifsta, ++ struct ieee80211_mgmt *mgmt, ++ size_t len, ++ struct ieee80211_rx_status *rx_status) ++{ ++ u16 reason_code; ++ ++ if (len < 24 + 2) { ++ printk(KERN_DEBUG "%s: too short (%zd) disassociation frame " ++ "received from " MACSTR " - ignored\n", ++ dev->name, len, MAC2STR(mgmt->sa)); ++ return; ++ } ++ ++ if (memcmp(ifsta->bssid, mgmt->sa, ETH_ALEN) != 0) { ++ printk(KERN_DEBUG "%s: disassociation frame received from " ++ "unknown AP (SA=" MACSTR " BSSID=" MACSTR ") - " ++ "ignored\n", dev->name, MAC2STR(mgmt->sa), ++ MAC2STR(mgmt->bssid)); ++ return; ++ } ++ ++ reason_code = le16_to_cpu(mgmt->u.disassoc.reason_code); ++ ++ printk(KERN_DEBUG "%s: RX disassociation from " MACSTR ++ " (reason=%d)\n", ++ dev->name, MAC2STR(mgmt->sa), reason_code); ++ ++ if (ifsta->associated) ++ printk(KERN_DEBUG "%s: disassociated\n", dev->name); ++ ++ if (ifsta->state == IEEE80211_ASSOCIATED) { ++ ifsta->state = IEEE80211_ASSOCIATE; ++ mod_timer(&ifsta->timer, ++ jiffies + IEEE80211_RETRY_AUTH_INTERVAL); ++ } ++ ++ ieee80211_set_associated(dev, ifsta, 0); ++} ++ ++ ++static void ieee80211_rx_mgmt_assoc_resp(struct net_device *dev, ++ struct ieee80211_if_sta *ifsta, ++ struct ieee80211_mgmt *mgmt, ++ size_t len, ++ struct ieee80211_rx_status *rx_status, ++ int reassoc) ++{ ++ struct ieee80211_local *local = dev->priv; ++ struct sta_info *sta; ++ u32 rates; ++ u16 capab_info, status_code, aid; ++ struct ieee802_11_elems elems; ++ u8 *pos; ++ int i, j; ++ ++ /* AssocResp and ReassocResp have identical structure, so process both ++ * of them in this function. */ ++ ++ if (ifsta->state != IEEE80211_ASSOCIATE) { ++ printk(KERN_DEBUG "%s: association frame received from " ++ MACSTR ", but not in associate state - ignored\n", ++ dev->name, MAC2STR(mgmt->sa)); ++ return; ++ } ++ ++ if (len < 24 + 6) { ++ printk(KERN_DEBUG "%s: too short (%zd) association frame " ++ "received from " MACSTR " - ignored\n", ++ dev->name, len, MAC2STR(mgmt->sa)); ++ return; ++ } ++ ++ if (memcmp(ifsta->bssid, mgmt->sa, ETH_ALEN) != 0) { ++ printk(KERN_DEBUG "%s: association frame received from " ++ "unknown AP (SA=" MACSTR " BSSID=" MACSTR ") - " ++ "ignored\n", dev->name, MAC2STR(mgmt->sa), ++ MAC2STR(mgmt->bssid)); ++ return; ++ } ++ ++ capab_info = le16_to_cpu(mgmt->u.assoc_resp.capab_info); ++ status_code = le16_to_cpu(mgmt->u.assoc_resp.status_code); ++ aid = le16_to_cpu(mgmt->u.assoc_resp.aid); ++ if ((aid & (BIT(15) | BIT(14))) != (BIT(15) | BIT(14))) ++ printk(KERN_DEBUG "%s: invalid aid value %d; bits 15:14 not " ++ "set\n", dev->name, aid); ++ aid &= ~(BIT(15) | BIT(14)); ++ ++ printk(KERN_DEBUG "%s: RX %sssocResp from " MACSTR " (capab=0x%x " ++ "status=%d aid=%d)\n", ++ dev->name, reassoc ? "Rea" : "A", MAC2STR(mgmt->sa), ++ capab_info, status_code, aid); ++ ++ if (status_code != WLAN_STATUS_SUCCESS) { ++ printk(KERN_DEBUG "%s: AP denied association (code=%d)\n", ++ dev->name, status_code); ++ return; ++ } ++ ++ pos = mgmt->u.assoc_resp.variable; ++ if (ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), &elems) ++ == ParseFailed) { ++ printk(KERN_DEBUG "%s: failed to parse AssocResp\n", ++ dev->name); ++ return; ++ } ++ ++ if (elems.supp_rates == NULL) { ++ printk(KERN_DEBUG "%s: no SuppRates element in AssocResp\n", ++ dev->name); ++ return; ++ } ++ ++ printk(KERN_DEBUG "%s: associated\n", dev->name); ++ ifsta->aid = aid; ++ ifsta->ap_capab = capab_info; ++ ++ kfree(ifsta->assocresp_ies); ++ ifsta->assocresp_ies_len = len - (pos - (u8 *) mgmt); ++ ifsta->assocresp_ies = kmalloc(ifsta->assocresp_ies_len, GFP_ATOMIC); ++ if (ifsta->assocresp_ies) ++ memcpy(ifsta->assocresp_ies, pos, ifsta->assocresp_ies_len); ++ ++ ieee80211_set_associated(dev, ifsta, 1); ++ ++ /* Add STA entry for the AP */ ++ sta = sta_info_get(local, ifsta->bssid); ++ if (sta == NULL) { ++ sta = sta_info_add(local, dev, ifsta->bssid); ++ if (sta == NULL) { ++ printk(KERN_DEBUG "%s: failed to add STA entry for the" ++ " AP\n", dev->name); ++ return; ++ } ++ } ++ ++ sta->dev = dev; ++ sta->flags |= WLAN_STA_AUTH | WLAN_STA_ASSOC; ++ sta->assoc_ap = 1; ++ ++ rates = 0; ++ for (i = 0; i < elems.supp_rates_len; i++) { ++ int rate = (elems.supp_rates[i] & 0x7f) * 5; ++ if (local->conf.phymode == MODE_ATHEROS_TURBO) ++ rate *= 2; ++ for (j = 0; j < local->num_curr_rates; j++) ++ if (local->curr_rates[j].rate == rate) ++ rates |= BIT(j); ++ } ++ for (i = 0; i < elems.ext_supp_rates_len; i++) { ++ int rate = (elems.ext_supp_rates[i] & 0x7f) * 5; ++ if (local->conf.phymode == MODE_ATHEROS_TURBO) ++ rate *= 2; ++ for (j = 0; j < local->num_curr_rates; j++) ++ if (local->curr_rates[j].rate == rate) ++ rates |= BIT(j); ++ } ++ sta->supp_rates = rates; ++ ++ rate_control_rate_init(local, sta); ++ ++ if (elems.wmm_param && ifsta->wmm_enabled) { ++ sta->flags |= WLAN_STA_WME; ++ ieee80211_sta_wmm_params(dev, ifsta, elems.wmm_param, ++ elems.wmm_param_len); ++ } ++ ++ ++ sta_info_release(local, sta); ++ ++ ieee80211_associated(dev, ifsta); ++} ++ ++ ++/* Caller must hold local->sta_bss_lock */ ++static void __ieee80211_rx_bss_hash_add(struct net_device *dev, ++ struct ieee80211_sta_bss *bss) ++{ ++ struct ieee80211_local *local = dev->priv; ++ bss->hnext = local->sta_bss_hash[STA_HASH(bss->bssid)]; ++ local->sta_bss_hash[STA_HASH(bss->bssid)] = bss; ++} ++ ++ ++/* Caller must hold local->sta_bss_lock */ ++static void __ieee80211_rx_bss_hash_del(struct net_device *dev, ++ struct ieee80211_sta_bss *bss) ++{ ++ struct ieee80211_local *local = dev->priv; ++ struct ieee80211_sta_bss *b, *prev = NULL; ++ b = local->sta_bss_hash[STA_HASH(bss->bssid)]; ++ while (b) { ++ if (b == bss) { ++ if (prev == NULL) { ++ local->sta_bss_hash[STA_HASH(bss->bssid)] = ++ bss->hnext; ++ } else { ++ prev->hnext = bss->hnext; ++ } ++ break; ++ } ++ prev = b; ++ b = b->hnext; ++ } ++} ++ ++ ++static struct ieee80211_sta_bss * ++ieee80211_rx_bss_add(struct net_device *dev, u8 *bssid) ++{ ++ struct ieee80211_local *local = dev->priv; ++ struct ieee80211_sta_bss *bss; ++ ++ bss = kmalloc(sizeof(*bss), GFP_ATOMIC); ++ if (bss == NULL) ++ return NULL; ++ memset(bss, 0, sizeof(*bss)); ++ atomic_inc(&bss->users); ++ atomic_inc(&bss->users); ++ memcpy(bss->bssid, bssid, ETH_ALEN); ++ ++ spin_lock_bh(&local->sta_bss_lock); ++ /* TODO: order by RSSI? */ ++ list_add_tail(&bss->list, &local->sta_bss_list); ++ __ieee80211_rx_bss_hash_add(dev, bss); ++ spin_unlock_bh(&local->sta_bss_lock); ++ return bss; ++} ++ ++ ++static struct ieee80211_sta_bss * ++ieee80211_rx_bss_get(struct net_device *dev, u8 *bssid) ++{ ++ struct ieee80211_local *local = dev->priv; ++ struct ieee80211_sta_bss *bss; ++ ++ spin_lock_bh(&local->sta_bss_lock); ++ bss = local->sta_bss_hash[STA_HASH(bssid)]; ++ while (bss) { ++ if (memcmp(bss->bssid, bssid, ETH_ALEN) == 0) { ++ atomic_inc(&bss->users); ++ break; ++ } ++ bss = bss->hnext; ++ } ++ spin_unlock_bh(&local->sta_bss_lock); ++ return bss; ++} ++ ++ ++static void ieee80211_rx_bss_free(struct ieee80211_sta_bss *bss) ++{ ++ kfree(bss->wpa_ie); ++ kfree(bss->rsn_ie); ++ kfree(bss->wmm_ie); ++ kfree(bss); ++} ++ ++ ++static void ieee80211_rx_bss_put(struct net_device *dev, ++ struct ieee80211_sta_bss *bss) ++{ ++ struct ieee80211_local *local = dev->priv; ++ if (!atomic_dec_and_test(&bss->users)) ++ return; ++ ++ spin_lock_bh(&local->sta_bss_lock); ++ __ieee80211_rx_bss_hash_del(dev, bss); ++ list_del(&bss->list); ++ spin_unlock_bh(&local->sta_bss_lock); ++ ieee80211_rx_bss_free(bss); ++} ++ ++ ++void ieee80211_rx_bss_list_init(struct net_device *dev) ++{ ++ struct ieee80211_local *local = dev->priv; ++ spin_lock_init(&local->sta_bss_lock); ++ INIT_LIST_HEAD(&local->sta_bss_list); ++} ++ ++ ++void ieee80211_rx_bss_list_deinit(struct net_device *dev) ++{ ++ struct ieee80211_local *local = dev->priv; ++ struct ieee80211_sta_bss *bss; ++ struct list_head *ptr; ++ ++ for (;;) { ++ ptr = local->sta_bss_list.next; ++ if (!ptr || ptr == &local->sta_bss_list) ++ break; ++ bss = list_entry(ptr, struct ieee80211_sta_bss, list); ++ ieee80211_rx_bss_put(dev, bss); ++ } ++} ++ ++ ++static void ieee80211_rx_bss_info(struct net_device *dev, ++ struct ieee80211_mgmt *mgmt, ++ size_t len, ++ struct ieee80211_rx_status *rx_status, ++ int beacon) ++{ ++ struct ieee80211_local *local = dev->priv; ++ struct ieee802_11_elems elems; ++ size_t baselen; ++ int channel, invalid = 0, clen; ++ struct ieee80211_sta_bss *bss; ++ struct sta_info *sta; ++ struct ieee80211_sub_if_data *sdata; ++ u64 timestamp; ++ u8 *pos; ++ ++ if (!beacon && memcmp(mgmt->da, dev->dev_addr, ETH_ALEN)) ++ return; /* ignore ProbeResp to foreign address */ ++ ++#if 0 ++ printk(KERN_DEBUG "%s: RX %s from " MACSTR " to " MACSTR "\n", ++ dev->name, beacon ? "Beacon" : "Probe Response", ++ MAC2STR(mgmt->sa), MAC2STR(mgmt->da)); ++#endif ++ ++ baselen = (u8 *) mgmt->u.beacon.variable - (u8 *) mgmt; ++ if (baselen > len) ++ return; ++ ++ pos = mgmt->u.beacon.timestamp; ++ timestamp = ((u64) pos[7] << 56) | ((u64) pos[6] << 48) | ++ ((u64) pos[5] << 40) | ((u64) pos[4] << 32) | ++ ((u64) pos[3] << 24) | ((u64) pos[2] << 16) | ++ ((u64) pos[1] << 8) | ((u64) pos[0]); ++ ++ if (local->conf.mode == IW_MODE_ADHOC && beacon && ++ memcmp(mgmt->bssid, local->bssid, ETH_ALEN) == 0) { ++#ifdef IEEE80211_IBSS_DEBUG ++ static unsigned long last_tsf_debug = 0; ++ u64 tsf; ++ if (local->hw->get_tsf) ++ tsf = local->hw->get_tsf(local->mdev); ++ else ++ tsf = -1LLU; ++ if (time_after(jiffies, last_tsf_debug + 5 * HZ)) { ++ printk(KERN_DEBUG "RX beacon SA=" MACSTR " BSSID=" ++ MACSTR " TSF=0x%llx BCN=0x%llx diff=%lld " ++ "@%ld\n", ++ MAC2STR(mgmt->sa), MAC2STR(mgmt->bssid), ++ tsf, timestamp, tsf - timestamp, jiffies); ++ last_tsf_debug = jiffies; ++ } ++#endif /* IEEE80211_IBSS_DEBUG */ ++ } ++ ++ if (ieee802_11_parse_elems(mgmt->u.beacon.variable, len - baselen, ++ &elems) == ParseFailed) ++ invalid = 1; ++ ++ if (local->conf.mode == IW_MODE_ADHOC && elems.supp_rates && ++ memcmp(mgmt->bssid, local->bssid, ETH_ALEN) == 0 && ++ (sta = sta_info_get(local, mgmt->sa)) && ++ (sdata = IEEE80211_DEV_TO_SUB_IF(dev)) && ++ sdata->type == IEEE80211_SUB_IF_TYPE_STA) { ++ struct ieee80211_rate *rates; ++ size_t num_rates; ++ u32 supp_rates, prev_rates; ++ int i, j, oper_mode; ++ ++ rates = local->curr_rates; ++ num_rates = local->num_curr_rates; ++ oper_mode = local->sta_scanning ? local->scan_oper_phymode : ++ local->conf.phymode; ++ for (i = 0; i < local->hw->num_modes; i++) { ++ struct ieee80211_hw_modes *mode = &local->hw->modes[i]; ++ if (oper_mode == mode->mode) { ++ rates = mode->rates; ++ num_rates = mode->num_rates; ++ break; ++ } ++ } ++ ++ supp_rates = 0; ++ for (i = 0; i < elems.supp_rates_len + ++ elems.ext_supp_rates_len; i++) { ++ u8 rate = 0; ++ int own_rate; ++ if (i < elems.supp_rates_len) ++ rate = elems.supp_rates[i]; ++ else if (elems.ext_supp_rates) ++ rate = elems.ext_supp_rates ++ [i - elems.supp_rates_len]; ++ own_rate = 5 * (rate & 0x7f); ++ if (oper_mode == MODE_ATHEROS_TURBO) ++ own_rate *= 2; ++ for (j = 0; j < num_rates; j++) ++ if (rates[j].rate == own_rate) ++ supp_rates |= BIT(j); ++ } ++ ++ prev_rates = sta->supp_rates; ++ sta->supp_rates &= supp_rates; ++ if (sta->supp_rates == 0) { ++ /* No matching rates - this should not really happen. ++ * Make sure that at least one rate is marked ++ * supported to avoid issues with TX rate ctrl. */ ++ sta->supp_rates = sdata->u.sta.supp_rates_bits; ++ } ++ if (sta->supp_rates != prev_rates) { ++ printk(KERN_DEBUG "%s: updated supp_rates set for " ++ MACSTR " based on beacon info (0x%x & 0x%x -> " ++ "0x%x)\n", ++ dev->name, MAC2STR(sta->addr), prev_rates, ++ supp_rates, sta->supp_rates); ++ } ++ sta_info_release(local, sta); ++ } ++ ++ if (elems.ssid == NULL) ++ return; ++ ++ if (elems.ds_params && elems.ds_params_len == 1) ++ channel = elems.ds_params[0]; ++ else ++ channel = rx_status->channel; ++ ++ bss = ieee80211_rx_bss_get(dev, mgmt->bssid); ++ if (bss == NULL) { ++ bss = ieee80211_rx_bss_add(dev, mgmt->bssid); ++ if (bss == NULL) ++ return; ++ } else { ++#if 0 ++ /* TODO: order by RSSI? */ ++ spin_lock_bh(&local->sta_bss_lock); ++ list_move_tail(&bss->list, &local->sta_bss_list); ++ spin_unlock_bh(&local->sta_bss_lock); ++#endif ++ } ++ ++ if (bss->probe_resp && beacon) { ++ /* Do not allow beacon to override data from Probe Response. */ ++ ieee80211_rx_bss_put(dev, bss); ++ return; ++ } ++ ++ bss->beacon_int = le16_to_cpu(mgmt->u.beacon.beacon_int); ++ bss->capability = le16_to_cpu(mgmt->u.beacon.capab_info); ++ if (elems.ssid && elems.ssid_len <= IEEE80211_MAX_SSID_LEN) { ++ memcpy(bss->ssid, elems.ssid, elems.ssid_len); ++ bss->ssid_len = elems.ssid_len; ++ } ++ ++ bss->supp_rates_len = 0; ++ if (elems.supp_rates) { ++ clen = IEEE80211_MAX_SUPP_RATES - bss->supp_rates_len; ++ if (clen > elems.supp_rates_len) ++ clen = elems.supp_rates_len; ++ memcpy(&bss->supp_rates[bss->supp_rates_len], elems.supp_rates, ++ clen); ++ bss->supp_rates_len += clen; ++ } ++ if (elems.ext_supp_rates) { ++ clen = IEEE80211_MAX_SUPP_RATES - bss->supp_rates_len; ++ if (clen > elems.ext_supp_rates_len) ++ clen = elems.ext_supp_rates_len; ++ memcpy(&bss->supp_rates[bss->supp_rates_len], ++ elems.ext_supp_rates, clen); ++ bss->supp_rates_len += clen; ++ } ++ ++ if (elems.wpa && ++ (bss->wpa_ie == NULL || bss->wpa_ie_len != elems.wpa_len || ++ memcmp(bss->wpa_ie, elems.wpa, elems.wpa_len))) { ++ kfree(bss->wpa_ie); ++ bss->wpa_ie = kmalloc(elems.wpa_len + 2, GFP_ATOMIC); ++ if (bss->wpa_ie) { ++ memcpy(bss->wpa_ie, elems.wpa - 2, elems.wpa_len + 2); ++ bss->wpa_ie_len = elems.wpa_len + 2; ++ } else ++ bss->wpa_ie_len = 0; ++ } else if (!elems.wpa && bss->wpa_ie) { ++ kfree(bss->wpa_ie); ++ bss->wpa_ie = NULL; ++ bss->wpa_ie_len = 0; ++ } ++ ++ if (elems.rsn && ++ (bss->rsn_ie == NULL || bss->rsn_ie_len != elems.rsn_len || ++ memcmp(bss->rsn_ie, elems.rsn, elems.rsn_len))) { ++ kfree(bss->rsn_ie); ++ bss->rsn_ie = kmalloc(elems.rsn_len + 2, GFP_ATOMIC); ++ if (bss->rsn_ie) { ++ memcpy(bss->rsn_ie, elems.rsn - 2, elems.rsn_len + 2); ++ bss->rsn_ie_len = elems.rsn_len + 2; ++ } else ++ bss->rsn_ie_len = 0; ++ } else if (!elems.rsn && bss->rsn_ie) { ++ kfree(bss->rsn_ie); ++ bss->rsn_ie = NULL; ++ bss->rsn_ie_len = 0; ++ } ++ ++ if (elems.wmm_param && ++ (bss->wmm_ie == NULL || bss->wmm_ie_len != elems.wmm_param_len || ++ memcmp(bss->wmm_ie, elems.wmm_param, elems.wmm_param_len))) { ++ kfree(bss->wmm_ie); ++ bss->wmm_ie = kmalloc(elems.wmm_param_len + 2, GFP_ATOMIC); ++ if (bss->wmm_ie) { ++ memcpy(bss->wmm_ie, elems.wmm_param - 2, ++ elems.wmm_param_len + 2); ++ bss->wmm_ie_len = elems.wmm_param_len + 2; ++ } else ++ bss->wmm_ie_len = 0; ++ } else if (!elems.wmm_param && bss->wmm_ie) { ++ kfree(bss->wmm_ie); ++ bss->wmm_ie = NULL; ++ bss->wmm_ie_len = 0; ++ } ++ ++ ++ bss->hw_mode = local->conf.phymode; ++ bss->channel = channel; ++ bss->freq = local->conf.freq; ++ if (channel != local->conf.channel && ++ (local->conf.phymode == MODE_IEEE80211G || ++ local->conf.phymode == MODE_IEEE80211B) && ++ channel >= 1 && channel <= 14) { ++ static const int freq_list[] = { ++ 2412, 2417, 2422, 2427, 2432, 2437, 2442, ++ 2447, 2452, 2457, 2462, 2467, 2472, 2484 ++ }; ++ /* IEEE 802.11g/b mode can receive packets from neighboring ++ * channels, so map the channel into frequency. */ ++ bss->freq = freq_list[channel - 1]; ++ } ++ bss->timestamp = timestamp; ++ bss->last_update = jiffies; ++ bss->rssi = rx_status->ssi; ++ if (!beacon) ++ bss->probe_resp++; ++ ieee80211_rx_bss_put(dev, bss); ++} ++ ++ ++static void ieee80211_rx_mgmt_probe_resp(struct net_device *dev, ++ struct ieee80211_mgmt *mgmt, ++ size_t len, ++ struct ieee80211_rx_status *rx_status) ++{ ++ ieee80211_rx_bss_info(dev, mgmt, len, rx_status, 0); ++} ++ ++ ++static void ieee80211_rx_mgmt_beacon(struct net_device *dev, ++ struct ieee80211_mgmt *mgmt, ++ size_t len, ++ struct ieee80211_rx_status *rx_status) ++{ ++ struct ieee80211_local *local = dev->priv; ++ struct ieee80211_sub_if_data *sdata; ++ struct ieee80211_if_sta *ifsta; ++ int use_protection; ++ size_t baselen; ++ struct ieee802_11_elems elems; ++ ++ ieee80211_rx_bss_info(dev, mgmt, len, rx_status, 1); ++ ++ sdata = IEEE80211_DEV_TO_SUB_IF(dev); ++ if (sdata->type != IEEE80211_SUB_IF_TYPE_STA) ++ return; ++ ifsta = &sdata->u.sta; ++ ++ if (!ifsta->associated || ++ memcmp(ifsta->bssid, mgmt->bssid, ETH_ALEN) != 0) ++ return; ++ ++ /* Process beacon from the current BSS */ ++ baselen = (u8 *) mgmt->u.beacon.variable - (u8 *) mgmt; ++ if (baselen > len) ++ return; ++ ++ if (ieee802_11_parse_elems(mgmt->u.beacon.variable, len - baselen, ++ &elems) == ParseFailed) ++ return; ++ ++ use_protection = 0; ++ if (elems.erp_info && elems.erp_info_len >= 1) { ++ use_protection = ++ (elems.erp_info[0] & ERP_INFO_USE_PROTECTION) != 0; ++ } ++ ++ if (use_protection != !!ifsta->use_protection) { ++ if (net_ratelimit()) { ++ printk(KERN_DEBUG "%s: CTS protection %s (BSSID=" ++ MACSTR ")\n", ++ dev->name, ++ use_protection ? "enabled" : "disabled", ++ MAC2STR(ifsta->bssid)); ++ } ++ ifsta->use_protection = use_protection ? 1 : 0; ++ local->cts_protect_erp_frames = use_protection; ++ } ++ ++ if (elems.wmm_param && ifsta->wmm_enabled) { ++ ieee80211_sta_wmm_params(dev, ifsta, elems.wmm_param, ++ elems.wmm_param_len); ++ } ++} ++ ++ ++static void ieee80211_rx_mgmt_probe_req(struct net_device *dev, ++ struct ieee80211_if_sta *ifsta, ++ struct ieee80211_mgmt *mgmt, ++ size_t len, ++ struct ieee80211_rx_status *rx_status) ++{ ++ struct ieee80211_local *local = dev->priv; ++ int tx_last_beacon; ++ struct sk_buff *skb; ++ struct ieee80211_mgmt *resp; ++ u8 *pos, *end; ++ ++ if (local->conf.mode != IW_MODE_ADHOC || ++ ifsta->state != IEEE80211_IBSS_JOINED || ++ len < 24 + 2 || ifsta->probe_resp == NULL) ++ return; ++ ++ if (local->hw->tx_last_beacon) ++ tx_last_beacon = local->hw->tx_last_beacon(local->mdev); ++ else ++ tx_last_beacon = 1; ++ ++#ifdef IEEE80211_IBSS_DEBUG ++ printk(KERN_DEBUG "%s: RX ProbeReq SA=" MACSTR " DA=" MACSTR " BSSID=" ++ MACSTR " (tx_last_beacon=%d)\n", ++ dev->name, MAC2STR(mgmt->sa), MAC2STR(mgmt->da), ++ MAC2STR(mgmt->bssid), tx_last_beacon); ++#endif /* IEEE80211_IBSS_DEBUG */ ++ ++ if (!tx_last_beacon) ++ return; ++ ++ if (memcmp(mgmt->bssid, ifsta->bssid, ETH_ALEN) != 0 && ++ memcmp(mgmt->bssid, "\xff\xff\xff\xff\xff\xff", ETH_ALEN) != 0) ++ return; ++ ++ end = ((u8 *) mgmt) + len; ++ pos = mgmt->u.probe_req.variable; ++ if (pos[0] != WLAN_EID_SSID || ++ pos + 2 + pos[1] > end) { ++ if (net_ratelimit()) { ++ printk(KERN_DEBUG "%s: Invalid SSID IE in ProbeReq " ++ "from " MACSTR "\n", ++ dev->name, MAC2STR(mgmt->sa)); ++ } ++ return; ++ } ++ if (pos[1] != 0 && ++ (pos[1] != ifsta->ssid_len || ++ memcmp(pos + 2, ifsta->ssid, ifsta->ssid_len) != 0)) { ++ /* Ignore ProbeReq for foreign SSID */ ++ return; ++ } ++ ++ /* Reply with ProbeResp */ ++ skb = skb_copy(ifsta->probe_resp, GFP_ATOMIC); ++ if (skb == NULL) ++ return; ++ ++ resp = (struct ieee80211_mgmt *) skb->data; ++ memcpy(resp->da, mgmt->sa, ETH_ALEN); ++#ifdef IEEE80211_IBSS_DEBUG ++ printk(KERN_DEBUG "%s: Sending ProbeResp to " MACSTR "\n", ++ dev->name, MAC2STR(resp->da)); ++#endif /* IEEE80211_IBSS_DEBUG */ ++ ieee80211_sta_tx(dev, skb, 0, 1); ++} ++ ++ ++void ieee80211_sta_rx_mgmt(struct net_device *dev, struct sk_buff *skb, ++ struct ieee80211_rx_status *rx_status) ++{ ++ struct ieee80211_sub_if_data *sdata; ++ struct ieee80211_if_sta *ifsta; ++ struct ieee80211_mgmt *mgmt; ++ u16 fc; ++ ++ if (skb->len < 24) ++ goto fail; ++ ++ sdata = IEEE80211_DEV_TO_SUB_IF(dev); ++ if (sdata->type != IEEE80211_SUB_IF_TYPE_STA) { ++ printk(KERN_DEBUG "%s: ieee80211_sta_rx_mgmt: non-STA " ++ "interface (type=%d)\n", dev->name, sdata->type); ++ goto fail; ++ } ++ ifsta = &sdata->u.sta; ++ ++ mgmt = (struct ieee80211_mgmt *) skb->data; ++ fc = le16_to_cpu(mgmt->frame_control); ++ ++ switch (WLAN_FC_GET_STYPE(fc)) { ++ case WLAN_FC_STYPE_PROBE_REQ: ++ ieee80211_rx_mgmt_probe_req(dev, ifsta, mgmt, skb->len, ++ rx_status); ++ break; ++ case WLAN_FC_STYPE_PROBE_RESP: ++ ieee80211_rx_mgmt_probe_resp(dev, mgmt, skb->len, rx_status); ++ break; ++ case WLAN_FC_STYPE_BEACON: ++ ieee80211_rx_mgmt_beacon(dev, mgmt, skb->len, rx_status); ++ break; ++ case WLAN_FC_STYPE_AUTH: ++ ieee80211_rx_mgmt_auth(dev, ifsta, mgmt, skb->len, rx_status); ++ break; ++ case WLAN_FC_STYPE_ASSOC_RESP: ++ ieee80211_rx_mgmt_assoc_resp(dev, ifsta, mgmt, skb->len, ++ rx_status, 0); ++ break; ++ case WLAN_FC_STYPE_REASSOC_RESP: ++ ieee80211_rx_mgmt_assoc_resp(dev, ifsta, mgmt, skb->len, ++ rx_status, 1); ++ break; ++ case WLAN_FC_STYPE_DEAUTH: ++ ieee80211_rx_mgmt_deauth(dev, ifsta, mgmt, skb->len, ++ rx_status); ++ break; ++ case WLAN_FC_STYPE_DISASSOC: ++ ieee80211_rx_mgmt_disassoc(dev, ifsta, mgmt, skb->len, ++ rx_status); ++ break; ++ default: ++ printk(KERN_DEBUG "%s: received unknown management frame - " ++ "stype=%d\n", dev->name, WLAN_FC_GET_STYPE(fc)); ++ break; ++ } ++ ++ fail: ++ dev_kfree_skb(skb); ++} ++ ++ ++void ieee80211_sta_rx_scan(struct net_device *dev, struct sk_buff *skb, ++ struct ieee80211_rx_status *rx_status) ++{ ++ struct ieee80211_mgmt *mgmt; ++ u16 fc; ++ ++ if (skb->len < 24) { ++ dev_kfree_skb(skb); ++ return; ++ } ++ ++ mgmt = (struct ieee80211_mgmt *) skb->data; ++ fc = le16_to_cpu(mgmt->frame_control); ++ ++ if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT) { ++ if (WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_PROBE_RESP) { ++ ieee80211_rx_mgmt_probe_resp(dev, mgmt, ++ skb->len, rx_status); ++ } else if (WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_BEACON) { ++ ieee80211_rx_mgmt_beacon(dev, mgmt, skb->len, ++ rx_status); ++ } ++ } ++ ++ dev_kfree_skb(skb); ++} ++ ++ ++static int ieee80211_sta_active_ibss(struct net_device *dev) ++{ ++ struct ieee80211_local *local = dev->priv; ++ struct list_head *ptr; ++ int active = 0; ++ struct sta_info *sta; ++ ++ spin_lock_bh(&local->sta_lock); ++ list_for_each(ptr, &local->sta_list) { ++ sta = list_entry(ptr, struct sta_info, list); ++ if (sta->dev == dev && ++ time_after(sta->last_rx + IEEE80211_IBSS_MERGE_INTERVAL, ++ jiffies)) { ++ active++; ++ break; ++ } ++ } ++ spin_unlock_bh(&local->sta_lock); ++ ++ return active; ++} ++ ++ ++static void ieee80211_sta_expire(struct net_device *dev) ++{ ++ struct ieee80211_local *local = dev->priv; ++ struct list_head *ptr, *n; ++ struct sta_info *sta; ++ ++ spin_lock_bh(&local->sta_lock); ++ list_for_each_safe(ptr, n, &local->sta_list) { ++ sta = list_entry(ptr, struct sta_info, list); ++ if (time_after(jiffies, sta->last_rx + ++ IEEE80211_IBSS_INACTIVITY_LIMIT)) { ++ printk(KERN_DEBUG "%s: expiring inactive STA " MACSTR ++ "\n", dev->name, MAC2STR(sta->addr)); ++ sta_info_free(local, sta, 1); ++ } ++ } ++ spin_unlock_bh(&local->sta_lock); ++} ++ ++ ++static void ieee80211_sta_merge_ibss(struct net_device *dev, ++ struct ieee80211_if_sta *ifsta) ++{ ++ mod_timer(&ifsta->timer, jiffies + IEEE80211_IBSS_MERGE_INTERVAL); ++ ++ ieee80211_sta_expire(dev); ++ if (ieee80211_sta_active_ibss(dev)) ++ return; ++ ++ printk(KERN_DEBUG "%s: No active IBSS STAs - trying to scan for other " ++ "IBSS networks with same SSID (merge)\n", dev->name); ++ ieee80211_sta_req_scan(dev, ifsta->ssid, ifsta->ssid_len); ++} ++ ++ ++void ieee80211_sta_timer(unsigned long ptr) ++{ ++ struct net_device *dev; ++ struct ieee80211_sub_if_data *sdata; ++ struct ieee80211_if_sta *ifsta; ++ ++ dev = (struct net_device *) ptr; ++ sdata = IEEE80211_DEV_TO_SUB_IF(dev); ++ if (sdata->type != IEEE80211_SUB_IF_TYPE_STA) { ++ printk(KERN_DEBUG "%s: ieee80211_sta_timer: non-STA interface " ++ "(type=%d)\n", dev->name, sdata->type); ++ return; ++ } ++ ifsta = &sdata->u.sta; ++ ++ switch (ifsta->state) { ++ case IEEE80211_DISABLED: ++ break; ++ case IEEE80211_AUTHENTICATE: ++ ieee80211_authenticate(dev, ifsta); ++ break; ++ case IEEE80211_ASSOCIATE: ++ ieee80211_associate(dev, ifsta); ++ break; ++ case IEEE80211_ASSOCIATED: ++ ieee80211_associated(dev, ifsta); ++ break; ++ case IEEE80211_IBSS_SEARCH: ++ ieee80211_sta_find_ibss(dev, ifsta); ++ break; ++ case IEEE80211_IBSS_JOINED: ++ ieee80211_sta_merge_ibss(dev, ifsta); ++ break; ++ default: ++ printk(KERN_DEBUG "ieee80211_sta_timer: Unknown state %d\n", ++ ifsta->state); ++ break; ++ } ++ ++ if (ieee80211_privacy_mismatch(dev, ifsta)) { ++ printk(KERN_DEBUG "%s: privacy configuration mismatch and " ++ "mixed-cell disabled - disassociate\n", dev->name); ++ ++ ieee80211_send_disassoc(dev, ifsta, WLAN_REASON_UNSPECIFIED); ++ ieee80211_set_associated(dev, ifsta, 0); ++ } ++} ++ ++ ++static void ieee80211_sta_new_auth(struct net_device *dev, ++ struct ieee80211_if_sta *ifsta) ++{ ++ struct ieee80211_local *local = dev->priv; ++ ++ if (local->conf.mode != IW_MODE_INFRA) ++ return; ++ ++ if (local->hw->reset_tsf) { ++ /* Reset own TSF to allow time synchronization work. */ ++ local->hw->reset_tsf(local->mdev); ++ } ++ ++ ifsta->wmm_last_param_set = -1; /* allow any WMM update */ ++ ++ ++ if (ifsta->auth_algs & IEEE80211_AUTH_ALG_OPEN) ++ ifsta->auth_alg = WLAN_AUTH_OPEN; ++ else if (ifsta->auth_algs & IEEE80211_AUTH_ALG_SHARED_KEY) ++ ifsta->auth_alg = WLAN_AUTH_SHARED_KEY; ++ else if (ifsta->auth_algs & IEEE80211_AUTH_ALG_LEAP) ++ ifsta->auth_alg = WLAN_AUTH_LEAP; ++ else ++ ifsta->auth_alg = WLAN_AUTH_OPEN; ++ printk(KERN_DEBUG "%s: Initial auth_alg=%d\n", dev->name, ++ ifsta->auth_alg); ++ ifsta->auth_transaction = -1; ++ ifsta->auth_tries = ifsta->assoc_tries = 0; ++ ieee80211_authenticate(dev, ifsta); ++} ++ ++ ++static int ieee80211_ibss_allowed(struct ieee80211_local *local) ++{ ++ int m, c; ++ ++ for (m = 0; m < local->hw->num_modes; m++) { ++ struct ieee80211_hw_modes *mode = &local->hw->modes[m]; ++ if (mode->mode != local->conf.phymode) ++ continue; ++ for (c = 0; c < mode->num_channels; c++) { ++ struct ieee80211_channel *chan = &mode->channels[c]; ++ if (chan->flag & IEEE80211_CHAN_W_SCAN && ++ chan->chan == local->conf.channel) { ++ if (chan->flag & IEEE80211_CHAN_W_IBSS) ++ return 1; ++ break; ++ } ++ } ++ } ++ ++ return 0; ++} ++ ++ ++extern int ieee80211_ioctl_siwfreq(struct net_device *dev, ++ struct iw_request_info *info, ++ struct iw_freq *freq, char *extra); ++ ++static int ieee80211_sta_join_ibss(struct net_device *dev, ++ struct ieee80211_if_sta *ifsta, ++ struct ieee80211_sta_bss *bss) ++{ ++ struct ieee80211_local *local = dev->priv; ++ struct iw_freq rq; ++ int res, rates, i, j; ++ struct sk_buff *skb; ++ struct ieee80211_mgmt *mgmt; ++ struct ieee80211_tx_control control; ++ struct ieee80211_rate *rate; ++ struct rate_control_extra extra; ++ u8 *pos; ++ struct ieee80211_sub_if_data *sdata; ++ ++ /* Remove possible STA entries from other IBSS networks. */ ++ sta_info_flush(local, NULL); ++ ++ if (local->hw->reset_tsf) { ++ /* Reset own TSF to allow time synchronization work. */ ++ local->hw->reset_tsf(local->mdev); ++ } ++ memcpy(ifsta->bssid, bss->bssid, ETH_ALEN); ++ memcpy(local->bssid, bss->bssid, ETH_ALEN); ++ memcpy(local->conf.client_bssid, bss->bssid, ETH_ALEN); ++ ++ local->conf.beacon_int = bss->beacon_int >= 10 ? bss->beacon_int : 10; ++ ++ sdata = IEEE80211_DEV_TO_SUB_IF(dev); ++ sdata->drop_unencrypted = bss->capability & ++ cpu_to_le16(WLAN_CAPABILITY_PRIVACY) ? 1 : 0; ++ ++ memset(&rq, 0, sizeof(rq)); ++ rq.m = bss->freq * 100000; ++ rq.e = 1; ++ res = ieee80211_ioctl_siwfreq(dev, NULL, &rq, NULL); ++ ++ if (!ieee80211_ibss_allowed(local)) { ++ printk(KERN_DEBUG "%s: IBSS not allowed on channel %d " ++ "(%d MHz)\n", dev->name, local->conf.channel, ++ local->conf.freq); ++ return -1; ++ } ++ ++ /* Set beacon template based on scan results */ ++ skb = dev_alloc_skb(400); ++ do { ++ if (skb == NULL) ++ break; ++ ++ mgmt = (struct ieee80211_mgmt *) ++ skb_put(skb, 24 + sizeof(mgmt->u.beacon)); ++ memset(mgmt, 0, 24 + sizeof(mgmt->u.beacon)); ++ mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, ++ WLAN_FC_STYPE_BEACON); ++ memset(mgmt->da, 0xff, ETH_ALEN); ++ memcpy(mgmt->sa, dev->dev_addr, ETH_ALEN); ++ memcpy(mgmt->bssid, ifsta->bssid, ETH_ALEN); ++ mgmt->u.beacon.beacon_int = ++ cpu_to_le16(local->conf.beacon_int); ++ mgmt->u.beacon.capab_info = cpu_to_le16(bss->capability); ++ ++ pos = skb_put(skb, 2 + ifsta->ssid_len); ++ *pos++ = WLAN_EID_SSID; ++ *pos++ = ifsta->ssid_len; ++ memcpy(pos, ifsta->ssid, ifsta->ssid_len); ++ ++ rates = bss->supp_rates_len; ++ if (rates > 8) ++ rates = 8; ++ pos = skb_put(skb, 2 + rates); ++ *pos++ = WLAN_EID_SUPP_RATES; ++ *pos++ = rates; ++ memcpy(pos, bss->supp_rates, rates); ++ ++ pos = skb_put(skb, 2 + 1); ++ *pos++ = WLAN_EID_DS_PARAMS; ++ *pos++ = 1; ++ *pos++ = bss->channel; ++ ++ pos = skb_put(skb, 2 + 2); ++ *pos++ = WLAN_EID_IBSS_PARAMS; ++ *pos++ = 2; ++ /* FIX: set ATIM window based on scan results */ ++ *pos++ = 0; ++ *pos++ = 0; ++ ++ if (bss->supp_rates_len > 8) { ++ rates = bss->supp_rates_len - 8; ++ pos = skb_put(skb, 2 + rates); ++ *pos++ = WLAN_EID_EXT_SUPP_RATES; ++ *pos++ = rates; ++ memcpy(pos, &bss->supp_rates[8], rates); ++ } ++ ++ memset(&control, 0, sizeof(control)); ++ control.pkt_type = PKT_PROBE_RESP; ++ memset(&extra, 0, sizeof(extra)); ++ extra.endidx = local->num_curr_rates; ++ rate = rate_control_get_rate(dev, skb, &extra); ++ if (rate == NULL) { ++ printk(KERN_DEBUG "%s: Failed to determine TX rate " ++ "for IBSS beacon\n", dev->name); ++ break; ++ } ++ control.tx_rate = (local->short_preamble && ++ (rate->flags & IEEE80211_RATE_PREAMBLE2)) ? ++ rate->val2 : rate->val; ++ control.antenna_sel = local->conf.antenna_sel; ++ control.power_level = local->conf.power_level; ++ control.no_ack = 1; ++ control.retry_limit = 1; ++ control.rts_cts_duration = 0; ++ ++ ifsta->probe_resp = skb_copy(skb, GFP_ATOMIC); ++ if (ifsta->probe_resp) { ++ mgmt = (struct ieee80211_mgmt *) ++ ifsta->probe_resp->data; ++ mgmt->frame_control = ++ IEEE80211_FC(WLAN_FC_TYPE_MGMT, ++ WLAN_FC_STYPE_PROBE_RESP); ++ } else { ++ printk(KERN_DEBUG "%s: Could not allocate ProbeResp " ++ "template for IBSS\n", dev->name); ++ } ++ ++ if (local->hw->beacon_update && ++ local->hw->beacon_update(dev, skb, &control) == 0) { ++ printk(KERN_DEBUG "%s: Configured IBSS beacon " ++ "template based on scan results\n", dev->name); ++ skb = NULL; ++ } ++ ++ rates = 0; ++ for (i = 0; i < bss->supp_rates_len; i++) { ++ int rate = (bss->supp_rates[i] & 0x7f) * 5; ++ if (local->conf.phymode == MODE_ATHEROS_TURBO) ++ rate *= 2; ++ for (j = 0; j < local->num_curr_rates; j++) ++ if (local->curr_rates[j].rate == rate) ++ rates |= BIT(j); ++ } ++ ifsta->supp_rates_bits = rates; ++ } while (0); ++ ++ if (skb) { ++ printk(KERN_DEBUG "%s: Failed to configure IBSS beacon " ++ "template\n", dev->name); ++ dev_kfree_skb(skb); ++ } ++ ++ ifsta->state = IEEE80211_IBSS_JOINED; ++ mod_timer(&ifsta->timer, jiffies + IEEE80211_IBSS_MERGE_INTERVAL); ++ ++ ieee80211_rx_bss_put(dev, bss); ++ ++ return res; ++} ++ ++ ++static int ieee80211_sta_create_ibss(struct net_device *dev, ++ struct ieee80211_if_sta *ifsta) ++{ ++ struct ieee80211_local *local = dev->priv; ++ struct ieee80211_sta_bss *bss; ++ struct ieee80211_sub_if_data *sdata; ++ u8 bssid[ETH_ALEN], *pos; ++ int i; ++ ++#if 0 ++ /* Easier testing, use fixed BSSID. */ ++ memset(bssid, 0xfe, ETH_ALEN); ++#else ++ /* Generate random, not broadcast, locally administered BSSID. Mix in ++ * own MAC address to make sure that devices that do not have proper ++ * random number generator get different BSSID. */ ++ get_random_bytes(bssid, ETH_ALEN); ++ for (i = 0; i < ETH_ALEN; i++) ++ bssid[i] ^= dev->dev_addr[i]; ++ bssid[0] &= ~0x01; ++ bssid[0] |= 0x02; ++#endif ++ ++ printk(KERN_DEBUG "%s: Creating new IBSS network, BSSID " MACSTR "\n", ++ dev->name, MAC2STR(bssid)); ++ ++ bss = ieee80211_rx_bss_add(dev, bssid); ++ if (bss == NULL) ++ return -ENOMEM; ++ ++ sdata = IEEE80211_DEV_TO_SUB_IF(dev); ++ ++ if (local->conf.beacon_int == 0) ++ local->conf.beacon_int = 100; ++ bss->beacon_int = local->conf.beacon_int; ++ bss->hw_mode = local->conf.phymode; ++ bss->channel = local->conf.channel; ++ bss->freq = local->conf.freq; ++ bss->last_update = jiffies; ++ bss->capability = cpu_to_le16(WLAN_CAPABILITY_IBSS); ++ if (sdata->default_key) { ++ bss->capability |= cpu_to_le16(WLAN_CAPABILITY_PRIVACY); ++ } else ++ sdata->drop_unencrypted = 0; ++ bss->supp_rates_len = local->num_curr_rates; ++ pos = bss->supp_rates; ++ for (i = 0; i < local->num_curr_rates; i++) { ++ int rate = local->curr_rates[i].rate; ++ if (local->conf.phymode == MODE_ATHEROS_TURBO) ++ rate /= 2; ++ *pos++ = (u8) (rate / 5); ++ } ++ ++ return ieee80211_sta_join_ibss(dev, ifsta, bss); ++} ++ ++ ++static int ieee80211_sta_find_ibss(struct net_device *dev, ++ struct ieee80211_if_sta *ifsta) ++{ ++ struct ieee80211_local *local = dev->priv; ++ struct ieee80211_sta_bss *bss; ++ int found = 0; ++ u8 bssid[ETH_ALEN]; ++ struct list_head *ptr; ++ int active_ibss; ++ ++ if (ifsta->ssid_len == 0) ++ return -EINVAL; ++ ++ active_ibss = ieee80211_sta_active_ibss(dev); ++#ifdef IEEE80211_IBSS_DEBUG ++ printk(KERN_DEBUG "%s: sta_find_ibss (active_ibss=%d)\n", ++ dev->name, active_ibss); ++#endif /* IEEE80211_IBSS_DEBUG */ ++ spin_lock_bh(&local->sta_bss_lock); ++ list_for_each(ptr, &local->sta_bss_list) { ++ bss = list_entry(ptr, struct ieee80211_sta_bss, list); ++ if (ifsta->ssid_len != bss->ssid_len || ++ memcmp(ifsta->ssid, bss->ssid, bss->ssid_len) != 0 ++ || !(bss->capability & WLAN_CAPABILITY_IBSS)) ++ continue; ++#ifdef IEEE80211_IBSS_DEBUG ++ printk(KERN_DEBUG " bssid=" MACSTR " found\n", ++ MAC2STR(bss->bssid)); ++#endif /* IEEE80211_IBSS_DEBUG */ ++ memcpy(bssid, bss->bssid, ETH_ALEN); ++ found = 1; ++ if (active_ibss || memcmp(bssid, ifsta->bssid, ETH_ALEN) != 0) ++ break; ++ } ++ spin_unlock_bh(&local->sta_bss_lock); ++ ++#ifdef IEEE80211_IBSS_DEBUG ++ printk(KERN_DEBUG " sta_find_ibss: selected " MACSTR " current " ++ MACSTR "\n", MAC2STR(bssid), MAC2STR(ifsta->bssid)); ++#endif /* IEEE80211_IBSS_DEBUG */ ++ if (found && memcmp(ifsta->bssid, bssid, ETH_ALEN) != 0 && ++ (bss = ieee80211_rx_bss_get(dev, bssid))) { ++ printk(KERN_DEBUG "%s: Selected IBSS BSSID " MACSTR ++ " based on configured SSID\n", ++ dev->name, MAC2STR(bssid)); ++ return ieee80211_sta_join_ibss(dev, ifsta, bss); ++ } ++#ifdef IEEE80211_IBSS_DEBUG ++ printk(KERN_DEBUG " did not try to join ibss\n"); ++#endif /* IEEE80211_IBSS_DEBUG */ ++ ++ /* Selected IBSS not found in current scan results - try to scan */ ++ if (ifsta->state == IEEE80211_IBSS_JOINED && ++ !ieee80211_sta_active_ibss(dev)) { ++ mod_timer(&ifsta->timer, ++ jiffies + IEEE80211_IBSS_MERGE_INTERVAL); ++ } else if (time_after(jiffies, local->last_scan_completed + ++ IEEE80211_SCAN_INTERVAL)) { ++ printk(KERN_DEBUG "%s: Trigger new scan to find an IBSS to " ++ "join\n", dev->name); ++ return ieee80211_sta_req_scan(dev, ifsta->ssid, ++ ifsta->ssid_len); ++ } else if (ifsta->state != IEEE80211_IBSS_JOINED) { ++ int interval = IEEE80211_SCAN_INTERVAL; ++ ++ if (time_after(jiffies, ifsta->ibss_join_req + ++ IEEE80211_IBSS_JOIN_TIMEOUT)) { ++ if (ifsta->create_ibss && ++ ieee80211_ibss_allowed(local)) ++ return ieee80211_sta_create_ibss(dev, ifsta); ++ if (ifsta->create_ibss) { ++ printk(KERN_DEBUG "%s: IBSS not allowed on the" ++ " configured channel %d (%d MHz)\n", ++ dev->name, local->conf.channel, ++ local->conf.freq); ++ } ++ ++ /* No IBSS found - decrease scan interval and continue ++ * scanning. */ ++ interval = IEEE80211_SCAN_INTERVAL_SLOW; ++ } ++ ++ ifsta->state = IEEE80211_IBSS_SEARCH; ++ mod_timer(&ifsta->timer, jiffies + interval); ++ return 0; ++ } ++ ++ return 0; ++} ++ ++ ++int ieee80211_sta_set_ssid(struct net_device *dev, char *ssid, size_t len) ++{ ++ struct ieee80211_sub_if_data *sdata; ++ struct ieee80211_if_sta *ifsta; ++ struct ieee80211_local *local = dev->priv; ++ ++ if (len > IEEE80211_MAX_SSID_LEN) ++ return -EINVAL; ++ ++ /* TODO: This should always be done for IBSS, even if IEEE80211_QOS is ++ * not defined. */ ++ if (local->hw->conf_tx) { ++ struct ieee80211_tx_queue_params qparam; ++ int i; ++ ++ memset(&qparam, 0, sizeof(qparam)); ++ /* TODO: are these ok defaults for all hw_modes? */ ++ qparam.aifs = 2; ++ qparam.cw_min = ++ local->conf.phymode == MODE_IEEE80211B ? 31 : 15; ++ qparam.cw_max = 1023; ++ qparam.burst_time = 0; ++ for (i = IEEE80211_TX_QUEUE_DATA0; i < NUM_TX_DATA_QUEUES; i++) ++ { ++ local->hw->conf_tx(dev, i + IEEE80211_TX_QUEUE_DATA0, ++ &qparam); ++ } ++ /* IBSS uses different parameters for Beacon sending */ ++ qparam.cw_min++; ++ qparam.cw_min *= 2; ++ qparam.cw_min--; ++ local->hw->conf_tx(dev, IEEE80211_TX_QUEUE_BEACON, &qparam); ++ } ++ ++ sdata = IEEE80211_DEV_TO_SUB_IF(dev); ++ ifsta = &sdata->u.sta; ++ ++ if (ifsta->ssid_len != len || memcmp(ifsta->ssid, ssid, len) != 0) ++ ifsta->prev_bssid_set = 0; ++ memcpy(ifsta->ssid, ssid, len); ++ memset(ifsta->ssid + len, 0, IEEE80211_MAX_SSID_LEN - len); ++ ifsta->ssid_len = len; ++ ++ ifsta->ssid_set = 1; ++ if (local->conf.mode == IW_MODE_ADHOC && !ifsta->bssid_set) { ++ ifsta->ibss_join_req = jiffies; ++ ifsta->state = IEEE80211_IBSS_SEARCH; ++ return ieee80211_sta_find_ibss(dev, ifsta); ++ } ++ ++ if (ifsta->bssid_set && ifsta->state != IEEE80211_AUTHENTICATE) ++ ieee80211_sta_new_auth(dev, ifsta); ++ ++ return 0; ++} ++ ++ ++int ieee80211_sta_get_ssid(struct net_device *dev, char *ssid, size_t *len) ++{ ++ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); ++ struct ieee80211_if_sta *ifsta = &sdata->u.sta; ++ memcpy(ssid, ifsta->ssid, ifsta->ssid_len); ++ *len = ifsta->ssid_len; ++ return 0; ++} ++ ++ ++int ieee80211_sta_set_bssid(struct net_device *dev, u8 *bssid) ++{ ++ struct ieee80211_local *local = dev->priv; ++ struct ieee80211_sub_if_data *sdata; ++ struct ieee80211_if_sta *ifsta; ++ ++ sdata = IEEE80211_DEV_TO_SUB_IF(dev); ++ ifsta = &sdata->u.sta; ++ ++ memcpy(ifsta->bssid, bssid, ETH_ALEN); ++ if (local->conf.mode == IW_MODE_ADHOC) ++ memcpy(local->bssid, bssid, ETH_ALEN); ++ ++ if (memcmp(bssid, "\x00\x00\x00\x00\x00\x00", ETH_ALEN) == 0) ++ ifsta->bssid_set = 0; ++ else ++ ifsta->bssid_set = 1; ++ if (ifsta->ssid_set) ++ ieee80211_sta_new_auth(dev, ifsta); ++ ++ return 0; ++} ++ ++ ++static void ieee80211_sta_save_oper_chan(struct net_device *dev) ++{ ++ struct ieee80211_local *local = dev->priv; ++ local->scan_oper_channel = local->conf.channel; ++ local->scan_oper_channel_val = local->conf.channel_val; ++ local->scan_oper_power_level = local->conf.power_level; ++ local->scan_oper_freq = local->conf.freq; ++ local->scan_oper_phymode = local->conf.phymode; ++ local->scan_oper_antenna_max = local->conf.antenna_max; ++} ++ ++ ++static int ieee80211_sta_restore_oper_chan(struct net_device *dev) ++{ ++ struct ieee80211_local *local = dev->priv; ++ local->conf.channel = local->scan_oper_channel; ++ local->conf.channel_val = local->scan_oper_channel_val; ++ local->conf.power_level = local->scan_oper_power_level; ++ local->conf.freq = local->scan_oper_freq; ++ local->conf.phymode = local->scan_oper_phymode; ++ local->conf.antenna_max = local->scan_oper_antenna_max; ++ return ieee80211_hw_config(dev); ++} ++ ++ ++static int ieee80211_active_scan(struct ieee80211_local *local) ++{ ++ int m, c; ++ ++ for (m = 0; m < local->hw->num_modes; m++) { ++ struct ieee80211_hw_modes *mode = &local->hw->modes[m]; ++ if (mode->mode != local->conf.phymode) ++ continue; ++ for (c = 0; c < mode->num_channels; c++) { ++ struct ieee80211_channel *chan = &mode->channels[c]; ++ if (chan->flag & IEEE80211_CHAN_W_SCAN && ++ chan->chan == local->conf.channel) { ++ if (chan->flag & IEEE80211_CHAN_W_ACTIVE_SCAN) ++ return 1; ++ break; ++ } ++ } ++ } ++ ++ return 0; ++} ++ ++ ++static void ieee80211_sta_scan_timer(unsigned long ptr) ++{ ++ struct net_device *dev = (struct net_device *) ptr; ++ struct ieee80211_local *local = dev->priv; ++ struct ieee80211_hw_modes *mode; ++ struct ieee80211_channel *chan; ++ int skip; ++ union iwreq_data wrqu; ++ ++ if (!local->sta_scanning) ++ return; ++ ++ switch (local->scan_state) { ++ case SCAN_SET_CHANNEL: ++ mode = &local->hw->modes[local->scan_hw_mode_idx]; ++ if (local->scan_hw_mode_idx >= local->hw->num_modes || ++ (local->scan_hw_mode_idx + 1 == local->hw->num_modes && ++ local->scan_channel_idx >= mode->num_channels)) { ++ if (ieee80211_sta_restore_oper_chan(dev)) { ++ printk(KERN_DEBUG "%s: failed to restore " ++ "operational channel after scan\n", ++ dev->name); ++ } ++ printk(KERN_DEBUG "%s: scan completed\n", dev->name); ++ local->sta_scanning = 0; ++ local->last_scan_completed = jiffies; ++ memset(&wrqu, 0, sizeof(wrqu)); ++ wireless_send_event(dev, SIOCGIWSCAN, &wrqu, NULL); ++ if (local->conf.mode == IW_MODE_ADHOC) { ++ struct ieee80211_sub_if_data *sdata = ++ IEEE80211_DEV_TO_SUB_IF(dev); ++ struct ieee80211_if_sta *ifsta = &sdata->u.sta; ++ if (!ifsta->bssid_set || ++ (ifsta->state == IEEE80211_IBSS_JOINED && ++ !ieee80211_sta_active_ibss(dev))) ++ ieee80211_sta_find_ibss(dev, ifsta); ++ } ++ return; ++ } ++ skip = !(local->hw_modes & (1 << mode->mode)); ++ chan = &mode->channels[local->scan_channel_idx]; ++ if (!(chan->flag & IEEE80211_CHAN_W_SCAN) || ++ (local->conf.mode == IW_MODE_ADHOC && ++ !(chan->flag & IEEE80211_CHAN_W_IBSS)) || ++ (local->hw_modes & (1 << MODE_IEEE80211G) && ++ mode->mode == MODE_IEEE80211B && local->scan_skip_11b)) ++ skip = 1; ++ ++ if (!skip) { ++#if 0 ++ printk(KERN_DEBUG "%s: scan channel %d (%d MHz)\n", ++ dev->name, chan->chan, chan->freq); ++#endif ++ ++ local->conf.channel = chan->chan; ++ local->conf.channel_val = chan->val; ++ local->conf.power_level = chan->power_level; ++ local->conf.freq = chan->freq; ++ local->conf.phymode = mode->mode; ++ local->conf.antenna_max = chan->antenna_max; ++ if (ieee80211_hw_config(dev)) { ++ printk(KERN_DEBUG "%s: failed to set channel " ++ "%d (%d MHz) for scan\n", dev->name, ++ chan->chan, chan->freq); ++ skip = 1; ++ } ++ } ++ ++ local->scan_channel_idx++; ++ if (local->scan_channel_idx >= ++ local->hw->modes[local->scan_hw_mode_idx].num_channels) { ++ local->scan_hw_mode_idx++; ++ local->scan_channel_idx = 0; ++ } ++ ++ if (skip) { ++ local->scan_timer.expires = jiffies; ++ break; ++ } ++ ++ local->scan_timer.expires = jiffies + IEEE80211_PROBE_DELAY; ++ local->scan_state = SCAN_SEND_PROBE; ++ break; ++ case SCAN_SEND_PROBE: ++ if (ieee80211_active_scan(local)) { ++ ieee80211_send_probe_req(dev, NULL, local->scan_ssid, ++ local->scan_ssid_len); ++ local->scan_timer.expires = ++ jiffies + IEEE80211_CHANNEL_TIME; ++ } else { ++ local->scan_timer.expires = ++ jiffies + IEEE80211_PASSIVE_CHANNEL_TIME; ++ } ++ local->scan_state = SCAN_SET_CHANNEL; ++ break; ++ } ++ ++ add_timer(&local->scan_timer); ++} ++ ++ ++int ieee80211_sta_req_scan(struct net_device *dev, u8 *ssid, size_t ssid_len) ++{ ++ struct ieee80211_local *local = dev->priv; ++ ++ if (ssid_len > IEEE80211_MAX_SSID_LEN) ++ return -EINVAL; ++ ++ /* MLME-SCAN.request (page 118) page 144 (11.1.3.1) ++ * BSSType: INFRASTRUCTURE, INDEPENDENT, ANY_BSS ++ * BSSID: MACAddress ++ * SSID ++ * ScanType: ACTIVE, PASSIVE ++ * ProbeDelay: delay (in microseconds) to be used prior to transmitting ++ * a Probe frame during active scanning ++ * ChannelList ++ * MinChannelTime (>= ProbeDelay), in TU ++ * MaxChannelTime: (>= MinChannelTime), in TU ++ */ ++ ++ /* MLME-SCAN.confirm ++ * BSSDescriptionSet ++ * ResultCode: SUCCESS, INVALID_PARAMETERS ++ */ ++ ++ /* TODO: if assoc, move to power save mode for the duration of the ++ * scan */ ++ ++ if (local->sta_scanning) ++ return -EBUSY; ++ ++ printk(KERN_DEBUG "%s: starting scan\n", dev->name); ++ ++ ieee80211_sta_save_oper_chan(dev); ++ ++ local->sta_scanning = 1; ++ /* TODO: stop TX queue? */ ++ ++ if (ssid) { ++ local->scan_ssid_len = ssid_len; ++ memcpy(local->scan_ssid, ssid, ssid_len); ++ } else ++ local->scan_ssid_len = 0; ++ local->scan_skip_11b = 1; /* FIX: clear this is 11g is not supported */ ++ local->scan_state = SCAN_SET_CHANNEL; ++ local->scan_hw_mode_idx = 0; ++ local->scan_channel_idx = 0; ++ init_timer(&local->scan_timer); ++ local->scan_timer.data = (unsigned long) dev; ++ local->scan_timer.function = ieee80211_sta_scan_timer; ++ local->scan_timer.expires = jiffies + 1; ++ add_timer(&local->scan_timer); ++ ++ return 0; ++} ++ ++ ++static char * ++ieee80211_sta_scan_result(struct net_device *dev, ++ struct ieee80211_sta_bss *bss, ++ char *current_ev, char *end_buf) ++{ ++ struct ieee80211_local *local = dev->priv; ++ struct iw_event iwe; ++ ++ if (time_after(jiffies, ++ bss->last_update + IEEE80211_SCAN_RESULT_EXPIRE)) ++ return current_ev; ++ ++ if (!(local->hw_modes & (1 << bss->hw_mode))) ++ return current_ev; ++ ++ if (local->scan_flags & IEEE80211_SCAN_WPA_ONLY && ++ bss->wpa_ie == NULL && bss->rsn_ie == NULL) ++ return current_ev; ++ ++ if (local->scan_flags & IEEE80211_SCAN_MATCH_SSID && ++ (local->scan_ssid_len != bss->ssid_len || ++ memcmp(local->scan_ssid, bss->ssid, bss->ssid_len) != 0)) ++ return current_ev; ++ ++ memset(&iwe, 0, sizeof(iwe)); ++ iwe.cmd = SIOCGIWAP; ++ iwe.u.ap_addr.sa_family = ARPHRD_ETHER; ++ memcpy(iwe.u.ap_addr.sa_data, bss->bssid, ETH_ALEN); ++ current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, ++ IW_EV_ADDR_LEN); ++ ++ memset(&iwe, 0, sizeof(iwe)); ++ iwe.cmd = SIOCGIWESSID; ++ iwe.u.data.length = bss->ssid_len; ++ iwe.u.data.flags = 1; ++ current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, ++ bss->ssid); ++ ++ if (bss->capability & (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS)) { ++ memset(&iwe, 0, sizeof(iwe)); ++ iwe.cmd = SIOCGIWMODE; ++ if (bss->capability & WLAN_CAPABILITY_ESS) ++ iwe.u.mode = IW_MODE_MASTER; ++ else ++ iwe.u.mode = IW_MODE_ADHOC; ++ current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, ++ IW_EV_UINT_LEN); ++ } ++ ++ memset(&iwe, 0, sizeof(iwe)); ++ iwe.cmd = SIOCGIWFREQ; ++ iwe.u.freq.m = bss->freq * 100000; ++ iwe.u.freq.e = 1; ++ current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, ++ IW_EV_FREQ_LEN); ++ ++ memset(&iwe, 0, sizeof(iwe)); ++ iwe.cmd = SIOCGIWENCODE; ++ if (bss->capability & WLAN_CAPABILITY_PRIVACY) ++ iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY; ++ else ++ iwe.u.data.flags = IW_ENCODE_DISABLED; ++ iwe.u.data.length = 0; ++ current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, ""); ++ ++ if (bss && bss->wpa_ie) { ++ char *buf, *p; ++ int i; ++ buf = kmalloc(30 + bss->wpa_ie_len * 2, GFP_ATOMIC); ++ if (buf) { ++ p = buf; ++ p += sprintf(p, "wpa_ie="); ++ for (i = 0; i < bss->wpa_ie_len; i++) ++ p+= sprintf(p, "%02x", bss->wpa_ie[i]); ++ memset(&iwe, 0, sizeof(iwe)); ++ iwe.cmd = IWEVCUSTOM; ++ iwe.u.data.length = strlen(buf); ++ current_ev = iwe_stream_add_point(current_ev, end_buf, ++ &iwe, buf); ++ kfree(buf); ++ } ++ } ++ ++ if (bss && bss->rsn_ie) { ++ char *buf, *p; ++ int i; ++ buf = kmalloc(30 + bss->rsn_ie_len * 2, GFP_ATOMIC); ++ if (buf) { ++ p = buf; ++ p += sprintf(p, "rsn_ie="); ++ for (i = 0; i < bss->rsn_ie_len; i++) ++ p+= sprintf(p, "%02x", bss->rsn_ie[i]); ++ memset(&iwe, 0, sizeof(iwe)); ++ iwe.cmd = IWEVCUSTOM; ++ iwe.u.data.length = strlen(buf); ++ current_ev = iwe_stream_add_point(current_ev, end_buf, ++ &iwe, buf); ++ kfree(buf); ++ } ++ } ++ ++ if (bss) { ++ char *buf; ++ buf = kmalloc(30, GFP_ATOMIC); ++ if (buf) { ++ memset(&iwe, 0, sizeof(iwe)); ++ iwe.cmd = IWEVCUSTOM; ++ sprintf(buf, "tsf=%016llx", bss->timestamp); ++ iwe.u.data.length = strlen(buf); ++ current_ev = iwe_stream_add_point(current_ev, end_buf, ++ &iwe, buf); ++ kfree(buf); ++ } ++ } ++ ++ do { ++ char *buf, *p; ++ int i; ++ ++ if (!(local->scan_flags & IEEE80211_SCAN_EXTRA_INFO)) ++ break; ++ ++ buf = kmalloc(100, GFP_ATOMIC); ++ if (buf == NULL) ++ break; ++ ++ memset(&iwe, 0, sizeof(iwe)); ++ iwe.cmd = IWEVCUSTOM; ++ sprintf(buf, "bcn_int=%d", bss->beacon_int); ++ iwe.u.data.length = strlen(buf); ++ current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, ++ buf); ++ ++ memset(&iwe, 0, sizeof(iwe)); ++ iwe.cmd = IWEVCUSTOM; ++ sprintf(buf, "rssi=%d", bss->rssi); ++ iwe.u.data.length = strlen(buf); ++ current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, ++ buf); ++ ++ memset(&iwe, 0, sizeof(iwe)); ++ iwe.cmd = IWEVCUSTOM; ++ sprintf(buf, "capab=0x%04x", bss->capability); ++ iwe.u.data.length = strlen(buf); ++ current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, ++ buf); ++ ++ p = buf; ++ p += sprintf(p, "supp_rates="); ++ for (i = 0; i < bss->supp_rates_len; i++) ++ p+= sprintf(p, "%02x", bss->supp_rates[i]); ++ memset(&iwe, 0, sizeof(iwe)); ++ iwe.cmd = IWEVCUSTOM; ++ iwe.u.data.length = strlen(buf); ++ current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, ++ buf); ++ ++ kfree(buf); ++ break; ++ } while (0); ++ ++ return current_ev; ++} ++ ++ ++int ieee80211_sta_scan_results(struct net_device *dev, char *buf, size_t len) ++{ ++ struct ieee80211_local *local = dev->priv; ++ struct list_head *ptr; ++ char *current_ev = buf; ++ char *end_buf = buf + len; ++ struct ieee80211_sta_bss *bss; ++ ++ spin_lock_bh(&local->sta_bss_lock); ++ list_for_each(ptr, &local->sta_bss_list) { ++ bss = list_entry(ptr, struct ieee80211_sta_bss, list); ++ current_ev = ieee80211_sta_scan_result(dev, bss, current_ev, ++ end_buf); ++ } ++ spin_unlock_bh(&local->sta_bss_lock); ++ return current_ev - buf; ++} ++ ++ ++int ieee80211_sta_set_extra_ie(struct net_device *dev, char *ie, size_t len) ++{ ++ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); ++ struct ieee80211_if_sta *ifsta = &sdata->u.sta; ++ kfree(ifsta->extra_ie); ++ if (len == 0) { ++ ifsta->extra_ie = NULL; ++ ifsta->extra_ie_len = 0; ++ return 0; ++ } ++ ifsta->extra_ie = kmalloc(len, GFP_KERNEL); ++ if (ifsta->extra_ie == NULL) { ++ ifsta->extra_ie_len = 0; ++ return -ENOMEM; ++ } ++ memcpy(ifsta->extra_ie, ie, len); ++ ifsta->extra_ie_len = len; ++ if (ifsta->bssid_set && ifsta->ssid_set && ++ ifsta->state != IEEE80211_AUTHENTICATE) ++ ieee80211_sta_new_auth(dev, ifsta); ++ return 0; ++} ++ ++ ++struct sta_info * ieee80211_ibss_add_sta(struct net_device *dev, ++ struct sk_buff *skb, u8 *bssid, ++ u8 *addr) ++{ ++ struct ieee80211_local *local = dev->priv; ++ struct list_head *ptr; ++ struct sta_info *sta; ++ struct ieee80211_sub_if_data *sdata = NULL; ++ struct net_device *sta_dev = NULL; ++ ++ /* TODO: Could consider removing the least recently used entry and ++ * allow new one to be added. */ ++ if (local->num_sta >= IEEE80211_IBSS_MAX_STA_ENTRIES) { ++ if (net_ratelimit()) { ++ printk(KERN_DEBUG "%s: No room for a new IBSS STA " ++ "entry " MACSTR "\n", dev->name, MAC2STR(addr)); ++ } ++ return NULL; ++ } ++ ++ spin_lock_bh(&local->sub_if_lock); ++ list_for_each(ptr, &local->sub_if_list) { ++ sdata = list_entry(ptr, struct ieee80211_sub_if_data, list); ++ if (sdata->type == IEEE80211_SUB_IF_TYPE_STA && ++ memcmp(bssid, sdata->u.sta.bssid, ETH_ALEN) == 0) { ++ sta_dev = sdata->dev; ++ break; ++ } ++ } ++ spin_unlock_bh(&local->sub_if_lock); ++ ++ if (sta_dev == NULL) ++ return NULL; ++ ++ printk(KERN_DEBUG "%s: Adding new IBSS station " MACSTR " (dev=%s)\n", ++ dev->name, MAC2STR(addr), sta_dev->name); ++ ++ sta = sta_info_add(local, dev, addr); ++ if (sta == NULL) { ++ return NULL; ++ } ++ ++ sta->dev = sta_dev; ++ sta->supp_rates = sdata->u.sta.supp_rates_bits; ++ ++ rate_control_rate_init(local, sta); ++ ++ return sta; /* caller will call sta_info_release() */ ++} ++ ++ ++int ieee80211_sta_deauthenticate(struct net_device *dev, u16 reason) ++{ ++ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); ++ struct ieee80211_if_sta *ifsta = &sdata->u.sta; ++ ++ printk(KERN_DEBUG "%s: deauthenticate(reason=%d)\n", ++ dev->name, reason); ++ ++ if (sdata->type != IEEE80211_SUB_IF_TYPE_STA) ++ return -EINVAL; ++ ++ ieee80211_send_deauth(dev, ifsta, reason); ++ ieee80211_set_associated(dev, ifsta, 0); ++ return 0; ++} ++ ++ ++int ieee80211_sta_disassociate(struct net_device *dev, u16 reason) ++{ ++ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); ++ struct ieee80211_if_sta *ifsta = &sdata->u.sta; ++ ++ printk(KERN_DEBUG "%s: disassociate(reason=%d)\n", ++ dev->name, reason); ++ ++ if (sdata->type != IEEE80211_SUB_IF_TYPE_STA) ++ return -EINVAL; ++ ++ if (!ifsta->associated) ++ return -1; ++ ++ ieee80211_send_disassoc(dev, ifsta, reason); ++ ieee80211_set_associated(dev, ifsta, 0); ++ return 0; ++} +diff -Nur linux-2.6.16/net/d80211/ieee80211_sysfs.c linux-2.6.16-bcm43xx/net/d80211/ieee80211_sysfs.c +--- linux-2.6.16/net/d80211/ieee80211_sysfs.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.16-bcm43xx/net/d80211/ieee80211_sysfs.c 2006-03-28 22:16:14.000000000 +0200 +@@ -0,0 +1,135 @@ ++/* ++ * Copyright (c) 2006 Jiri Benc <jbenc@suse.cz> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/device.h> ++#include <linux/if.h> ++#include <linux/interrupt.h> ++#include <linux/rtnetlink.h> ++#include <net/d80211.h> ++#include "ieee80211_i.h" ++ ++#define to_ieee80211_local(class) container_of(class, struct ieee80211_local, class_dev) ++ ++ ++static ssize_t store_add_iface(struct class_device *dev, ++ const char *buf, size_t len) ++{ ++ struct ieee80211_local *local = to_ieee80211_local(dev); ++ int res; ++ ++ if (!capable(CAP_NET_ADMIN)) ++ return -EPERM; ++ if (len > IFNAMSIZ) ++ return -EINVAL; ++ /* Cannot call ieee80211_if_add_sta() with 'locked' parameter equal ++ * to zero as it would lead to call to register_netdev() and ++ * interpreting '%d' character in an interface name. */ ++ rtnl_lock(); ++ res = ieee80211_if_add_sta(local->mdev, buf, 1); ++ rtnl_unlock(); ++ return res < 0 ? res : len; ++} ++ ++static ssize_t store_remove_iface(struct class_device *dev, ++ const char *buf, size_t len) ++{ ++ struct ieee80211_local *local = to_ieee80211_local(dev); ++ int res; ++ ++ if (!capable(CAP_NET_ADMIN)) ++ return -EPERM; ++ if (len > IFNAMSIZ) ++ return -EINVAL; ++ res = ieee80211_if_remove_sta(local->mdev, buf, 0); ++ return res < 0 ? res : len; ++} ++ ++static ssize_t show_max_iface_count(struct class_device *dev, ++ char *buf) ++{ ++ struct ieee80211_local *local = to_ieee80211_local(dev); ++ ++ return sprintf(buf, "%d\n", local->conf.bss_count); ++} ++ ++static ssize_t store_max_iface_count(struct class_device *dev, ++ const char *buf, size_t len) ++{ ++ struct ieee80211_local *local = to_ieee80211_local(dev); ++ unsigned long new_count; ++ char *endp; ++ int res; ++ ++ if (!capable(CAP_NET_ADMIN)) ++ return -EPERM; ++ new_count = simple_strtoul(buf, &endp, 0); ++ if (endp == buf) ++ return -EINVAL; ++ rtnl_lock(); ++ res = ieee80211_set_bss_count(local->mdev, new_count, NULL); ++ rtnl_unlock(); ++ return res < 0 ? res : len; ++} ++ ++#ifdef CONFIG_HOTPLUG ++static int ieee80211_uevent(struct class_device *cd, char **envp, ++ int num_envp, char *buf, int size) ++{ ++ struct ieee80211_local *local = to_ieee80211_local(cd); ++ ++ if (num_envp < 2) ++ return -ENOMEM; ++ envp[0] = buf; ++ if (snprintf(buf, size, "IEEE80211_DEV=phy%d", ++ local->dev_index) + 1 >= size) ++ return -ENOMEM; ++ envp[1] = NULL; ++ return 0; ++} ++#endif ++ ++static struct class_device_attribute ieee80211_class_dev_attrs[] = { ++ __ATTR(add_iface, S_IWUSR, NULL, store_add_iface), ++ __ATTR(remove_iface, S_IWUSR, NULL, store_remove_iface), ++ __ATTR(max_iface_count, S_IRUGO | S_IWUSR, ++ show_max_iface_count, store_max_iface_count), ++ {} ++}; ++ ++static struct class ieee80211_class = { ++ .name = "ieee80211", ++ .class_dev_attrs = ieee80211_class_dev_attrs, ++#ifdef CONFIG_HOTPLUG ++ .uevent = ieee80211_uevent, ++#endif ++}; ++ ++int ieee80211_register_sysfs(struct ieee80211_local *local) ++{ ++ local->class_dev.class = &ieee80211_class; ++ local->class_dev.class_data = local; ++ snprintf(local->class_dev.class_id, BUS_ID_SIZE, ++ "phy%d", local->dev_index); ++ return class_device_register(&local->class_dev); ++} ++ ++void ieee80211_unregister_sysfs(struct ieee80211_local *local) ++{ ++ class_device_del(&local->class_dev); ++} ++ ++int ieee80211_sysfs_init(void) ++{ ++ return class_register(&ieee80211_class); ++} ++ ++void ieee80211_sysfs_deinit(void) ++{ ++ class_unregister(&ieee80211_class); ++} +diff -Nur linux-2.6.16/net/d80211/Kconfig linux-2.6.16-bcm43xx/net/d80211/Kconfig +--- linux-2.6.16/net/d80211/Kconfig 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.16-bcm43xx/net/d80211/Kconfig 2006-03-28 22:16:14.000000000 +0200 +@@ -0,0 +1,31 @@ ++config D80211 ++ tristate "Generic IEEE 802.11 Networking Stack (dscape)" ++ ---help--- ++ This option enables the hardware independent IEEE 802.11 ++ networking stack. ++ ++config D80211_DEBUG ++ bool "Enable debugging output" ++ depends on D80211 ++ ---help--- ++ This option will enable debug tracing output for the ++ ieee80211 network stack. ++ ++ If you are not trying to debug or develop the ieee80211 ++ subsystem, you most likely want to say N here. ++ ++config D80211_VERBOSE_DEBUG ++ bool "Verbose debugging output" ++ depends on D80211_DEBUG ++ ++config TKIP_DEBUG ++ bool "TKIP debugging" ++ depends on D80211_DEBUG ++ ++config D80211_DEBUG_COUNTERS ++ bool "Extra statistics for TX/RX debugging" ++ depends on D80211_DEBUG ++ ++config HOSTAPD_WPA_TESTING ++ bool "Support for TKIP countermeasures testing" ++ depends on D80211_DEBUG +diff -Nur linux-2.6.16/net/d80211/Makefile linux-2.6.16-bcm43xx/net/d80211/Makefile +--- linux-2.6.16/net/d80211/Makefile 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.16-bcm43xx/net/d80211/Makefile 2006-03-28 22:16:14.000000000 +0200 +@@ -0,0 +1,26 @@ ++obj-$(CONFIG_D80211) += 80211.o rate_control.o ++ ++80211-objs := \ ++ ieee80211.o \ ++ ieee80211_ioctl.o \ ++ sta_info.o \ ++ wep.o \ ++ wpa.o \ ++ ieee80211_proc.o \ ++ ieee80211_scan.o \ ++ ieee80211_sta.o \ ++ ieee80211_dev.o \ ++ ieee80211_sysfs.o \ ++ michael.o \ ++ tkip.o \ ++ aes_ccm.o \ ++ wme.o ++ ++ifeq ($(CONFIG_NET_SCHED),) ++ 80211-objs += fifo_qdisc.o ++endif ++ ++ifeq ($(CONFIG_D80211_LEDS),y) ++ 80211-objs += ieee80211_led.o ++endif ++ +diff -Nur linux-2.6.16/net/d80211/michael.c linux-2.6.16-bcm43xx/net/d80211/michael.c +--- linux-2.6.16/net/d80211/michael.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.16-bcm43xx/net/d80211/michael.c 2006-03-28 22:16:14.000000000 +0200 +@@ -0,0 +1,104 @@ ++/* ++ * Michael MIC implementation - optimized for TKIP MIC operations ++ * Copyright 2002-2003, Instant802 Networks, Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include <linux/types.h> ++ ++#include "michael.h" ++ ++static inline u32 rotr(u32 val, int bits) ++{ ++ return (val >> bits) | (val << (32 - bits)); ++} ++ ++ ++static inline u32 rotl(u32 val, int bits) ++{ ++ return (val << bits) | (val >> (32 - bits)); ++} ++ ++ ++static inline u32 xswap(u32 val) ++{ ++ return ((val & 0xff00ff00) >> 8) | ((val & 0x00ff00ff) << 8); ++} ++ ++ ++#define michael_block(l, r) \ ++do { \ ++ r ^= rotl(l, 17); \ ++ l += r; \ ++ r ^= xswap(l); \ ++ l += r; \ ++ r ^= rotl(l, 3); \ ++ l += r; \ ++ r ^= rotr(l, 2); \ ++ l += r; \ ++} while (0) ++ ++ ++static inline u32 michael_get32(u8 *data) ++{ ++ return data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24); ++} ++ ++ ++static inline void michael_put32(u32 val, u8 *data) ++{ ++ data[0] = val & 0xff; ++ data[1] = (val >> 8) & 0xff; ++ data[2] = (val >> 16) & 0xff; ++ data[3] = (val >> 24) & 0xff; ++} ++ ++ ++void michael_mic(u8 *key, u8 *da, u8 *sa, u8 priority, ++ u8 *data, size_t data_len, u8 *mic) ++{ ++ u32 l, r, val; ++ size_t block, blocks, left; ++ ++ l = michael_get32(key); ++ r = michael_get32(key + 4); ++ ++ /* A pseudo header (DA, SA, Priority, 0, 0, 0) is used in Michael MIC ++ * calculation, but it is _not_ transmitted */ ++ l ^= michael_get32(da); ++ michael_block(l, r); ++ l ^= da[4] | (da[5] << 8) | (sa[0] << 16) | (sa[1] << 24); ++ michael_block(l, r); ++ l ^= michael_get32(&sa[2]); ++ michael_block(l, r); ++ l ^= priority; ++ michael_block(l, r); ++ ++ /* Real data */ ++ blocks = data_len / 4; ++ left = data_len % 4; ++ ++ for (block = 0; block < blocks; block++) { ++ l ^= michael_get32(&data[block * 4]); ++ michael_block(l, r); ++ } ++ ++ /* Partial block of 0..3 bytes and padding: 0x5a + 4..7 zeros to make ++ * total length a multiple of 4. */ ++ val = 0x5a; ++ while (left > 0) { ++ val <<= 8; ++ left--; ++ val |= data[blocks * 4 + left]; ++ } ++ l ^= val; ++ michael_block(l, r); ++ /* last block is zero, so l ^ 0 = l */ ++ michael_block(l, r); ++ ++ michael_put32(l, mic); ++ michael_put32(r, mic + 4); ++} +diff -Nur linux-2.6.16/net/d80211/michael.h linux-2.6.16-bcm43xx/net/d80211/michael.h +--- linux-2.6.16/net/d80211/michael.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.16-bcm43xx/net/d80211/michael.h 2006-03-28 22:16:14.000000000 +0200 +@@ -0,0 +1,20 @@ ++/* ++ * Michael MIC implementation - optimized for TKIP MIC operations ++ * Copyright 2002-2003, Instant802 Networks, Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#ifndef MICHAEL_H ++#define MICHAEL_H ++ ++#include <linux/types.h> ++ ++#define MICHAEL_MIC_LEN 8 ++ ++void michael_mic(u8 *key, u8 *da, u8 *sa, u8 priority, ++ u8 *data, size_t data_len, u8 *mic); ++ ++#endif /* MICHAEL_H */ +diff -Nur linux-2.6.16/net/d80211/rate_control.c linux-2.6.16-bcm43xx/net/d80211/rate_control.c +--- linux-2.6.16/net/d80211/rate_control.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.16-bcm43xx/net/d80211/rate_control.c 2006-03-28 22:16:14.000000000 +0200 +@@ -0,0 +1,385 @@ ++/* ++ * Copyright 2002-2005, Instant802 Networks, Inc. ++ * Copyright 2005, Devicescape Software, Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include <linux/config.h> ++#include <linux/version.h> ++#include <linux/module.h> ++#include <linux/init.h> ++#include <linux/netdevice.h> ++#include <linux/types.h> ++#include <linux/slab.h> ++#include <linux/skbuff.h> ++#include <linux/compiler.h> ++ ++#include <net/d80211.h> ++#include "ieee80211_i.h" ++#include "rate_control.h" ++ ++ ++/* This is a minimal implementation of TX rate controlling that can be used ++ * as the default when no improved mechanisms are available. */ ++ ++ ++#define RATE_CONTROL_EMERG_DEC 2 ++#define RATE_CONTROL_INTERVAL (HZ / 20) ++#define RATE_CONTROL_MIN_TX 10 ++ ++MODULE_ALIAS("ieee80211_rate_control"); ++ ++static void rate_control_rate_inc(struct ieee80211_local *local, ++ struct sta_info *sta) ++{ ++ struct ieee80211_sub_if_data *sdata; ++ int i = sta->txrate; ++ int maxrate; ++ ++ sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev); ++ if (sdata->bss && sdata->bss->force_unicast_rateidx > -1) { ++ /* forced unicast rate - do not change STA rate */ ++ return; ++ } ++ ++ maxrate = sdata->bss ? sdata->bss->max_ratectrl_rateidx : -1; ++ ++ if (i > local->num_curr_rates) ++ i = local->num_curr_rates - 2; ++ ++ while (i + 1 < local->num_curr_rates) { ++ i++; ++ if (sta->supp_rates & BIT(i) && ++ local->curr_rates[i].flags & IEEE80211_RATE_SUPPORTED && ++ (maxrate < 0 || i <= maxrate)) { ++ sta->txrate = i; ++ break; ++ } ++ } ++} ++ ++ ++static void rate_control_rate_dec(struct ieee80211_local *local, ++ struct sta_info *sta) ++{ ++ struct ieee80211_sub_if_data *sdata; ++ int i = sta->txrate; ++ ++ sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev); ++ if (sdata->bss && sdata->bss->force_unicast_rateidx > -1) { ++ /* forced unicast rate - do not change STA rate */ ++ return; ++ } ++ ++ if (i > local->num_curr_rates) ++ i = local->num_curr_rates; ++ ++ while (i > 0) { ++ i--; ++ if (sta->supp_rates & BIT(i) && ++ local->curr_rates[i].flags & IEEE80211_RATE_SUPPORTED) { ++ sta->txrate = i; ++ break; ++ } ++ } ++} ++ ++ ++static struct ieee80211_rate * ++rate_control_lowest_rate(struct ieee80211_local *local) ++{ ++ int i; ++ ++ for (i = 0; i < local->num_curr_rates; i++) { ++ struct ieee80211_rate *rate = &local->curr_rates[i]; ++ ++ if (rate->flags & IEEE80211_RATE_SUPPORTED ++ ) ++ return rate; ++ } ++ ++ printk(KERN_DEBUG "rate_control_lowest_rate - no supported rates " ++ "found\n"); ++ return &local->curr_rates[0]; ++} ++ ++ ++struct global_rate_control { ++ int dummy; ++}; ++ ++struct sta_rate_control { ++ unsigned long last_rate_change; ++ u32 tx_num_failures; ++ u32 tx_num_xmit; ++ ++ unsigned long avg_rate_update; ++ u32 tx_avg_rate_sum; ++ u32 tx_avg_rate_num; ++}; ++ ++ ++static void rate_control_simple_tx_status(struct net_device *dev, ++ struct sk_buff *skb, ++ struct ieee80211_tx_status *status) ++{ ++ struct ieee80211_local *local = dev->priv; ++ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; ++ struct sta_info *sta; ++ struct sta_rate_control *srctrl; ++ ++ sta = sta_info_get(local, hdr->addr1); ++ ++ if (!sta) ++ return; ++ ++ srctrl = sta->rate_ctrl_priv; ++ srctrl->tx_num_xmit++; ++ if (status->excessive_retries) { ++ sta->antenna_sel = sta->antenna_sel == 1 ? 2 : 1; ++ if (local->sta_antenna_sel == STA_ANTENNA_SEL_SW_CTRL_DEBUG) { ++ printk(KERN_DEBUG "%s: " MACSTR " TX antenna --> %d " ++ "(@%lu)\n", ++ dev->name, MAC2STR(hdr->addr1), ++ sta->antenna_sel, jiffies); ++ } ++ srctrl->tx_num_failures++; ++ sta->tx_retry_failed++; ++ sta->tx_num_consecutive_failures++; ++ sta->tx_num_mpdu_fail++; ++ } else { ++ sta->last_ack_rssi[0] = sta->last_ack_rssi[1]; ++ sta->last_ack_rssi[1] = sta->last_ack_rssi[2]; ++ sta->last_ack_rssi[2] = status->ack_signal; ++ sta->tx_num_consecutive_failures = 0; ++ sta->tx_num_mpdu_ok++; ++ } ++ sta->tx_retry_count += status->retry_count; ++ sta->tx_num_mpdu_fail += status->retry_count; ++ ++ if (time_after(jiffies, ++ srctrl->last_rate_change + RATE_CONTROL_INTERVAL) && ++ srctrl->tx_num_xmit > RATE_CONTROL_MIN_TX) { ++ u32 per_failed; ++ srctrl->last_rate_change = jiffies; ++ ++ per_failed = (100 * sta->tx_num_mpdu_fail) / ++ (sta->tx_num_mpdu_fail + sta->tx_num_mpdu_ok); ++ /* TODO: calculate average per_failed to make adjusting ++ * parameters easier */ ++#if 0 ++ if (net_ratelimit()) { ++ printk(KERN_DEBUG "MPDU fail=%d ok=%d per_failed=%d\n", ++ sta->tx_num_mpdu_fail, sta->tx_num_mpdu_ok, ++ per_failed); ++ } ++#endif ++ ++ if (per_failed > local->rate_ctrl_num_down) { ++ rate_control_rate_dec(local, sta); ++ } else if (per_failed < local->rate_ctrl_num_up) { ++ rate_control_rate_inc(local, sta); ++ } ++ srctrl->tx_avg_rate_sum += local->curr_rates[sta->txrate].rate; ++ srctrl->tx_avg_rate_num++; ++ srctrl->tx_num_failures = 0; ++ srctrl->tx_num_xmit = 0; ++ } else if (sta->tx_num_consecutive_failures >= ++ RATE_CONTROL_EMERG_DEC) { ++ rate_control_rate_dec(local, sta); ++ } ++ ++ if (srctrl->avg_rate_update + 60 * HZ < jiffies) { ++ srctrl->avg_rate_update = jiffies; ++ if (srctrl->tx_avg_rate_num > 0) { ++#ifdef CONFIG_D80211_VERBOSE_DEBUG ++ printk(KERN_DEBUG "%s: STA " MACSTR " Average rate: " ++ "%d (%d/%d)\n", ++ dev->name, MAC2STR(sta->addr), ++ srctrl->tx_avg_rate_sum / ++ srctrl->tx_avg_rate_num, ++ srctrl->tx_avg_rate_sum, ++ srctrl->tx_avg_rate_num); ++#endif /* CONFIG_D80211_VERBOSE_DEBUG */ ++ srctrl->tx_avg_rate_sum = 0; ++ srctrl->tx_avg_rate_num = 0; ++ } ++ } ++ ++ sta_info_release(local, sta); ++} ++ ++ ++static struct ieee80211_rate * ++rate_control_simple_get_rate(struct net_device *dev, struct sk_buff *skb, ++ struct rate_control_extra *extra) ++{ ++ struct ieee80211_local *local = dev->priv; ++ struct ieee80211_sub_if_data *sdata; ++ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; ++ struct sta_info *sta; ++ int rateidx, nonerp_idx; ++ u16 fc; ++ ++ memset(extra, 0, sizeof(*extra)); ++ ++ fc = le16_to_cpu(hdr->frame_control); ++ if (WLAN_FC_GET_TYPE(fc) != WLAN_FC_TYPE_DATA || ++ (hdr->addr1[0] & 0x01)) { ++ /* Send management frames and broadcast/multicast data using ++ * lowest rate. */ ++ /* TODO: this could probably be improved.. */ ++ return rate_control_lowest_rate(local); ++ } ++ ++ sta = sta_info_get(local, hdr->addr1); ++ ++ if (!sta) ++ return rate_control_lowest_rate(local); ++ ++ sdata = IEEE80211_DEV_TO_SUB_IF(dev); ++ if (sdata->bss && sdata->bss->force_unicast_rateidx > -1) ++ sta->txrate = sdata->bss->force_unicast_rateidx; ++ ++ rateidx = sta->txrate; ++ ++ if (rateidx >= local->num_curr_rates) ++ rateidx = local->num_curr_rates - 1; ++ ++ sta->last_txrate = rateidx; ++ nonerp_idx = rateidx; ++ while (nonerp_idx > 0 && ++ ((local->curr_rates[nonerp_idx].flags & IEEE80211_RATE_ERP) || ++ !(local->curr_rates[nonerp_idx].flags & ++ IEEE80211_RATE_SUPPORTED) || ++ !(sta->supp_rates & BIT(nonerp_idx)))) ++ nonerp_idx--; ++ extra->nonerp_idx = nonerp_idx; ++ extra->nonerp = &local->curr_rates[extra->nonerp_idx]; ++ ++ sta_info_release(local, sta); ++ ++ return &local->curr_rates[rateidx]; ++} ++ ++ ++static void rate_control_simple_rate_init(struct ieee80211_local *local, ++ struct sta_info *sta) ++{ ++ int i; ++ sta->txrate = 0; ++ /* TODO: what is a good starting rate for STA? About middle? Maybe not ++ * the lowest or the highest rate.. Could consider using RSSI from ++ * previous packets? Need to have IEEE 802.1X auth succeed immediately ++ * after assoc.. */ ++ for (i = 0; i < local->num_curr_rates; i++) { ++ if ((sta->supp_rates & BIT(i)) && ++ (local->curr_rates[i].flags & IEEE80211_RATE_SUPPORTED)) ++ sta->txrate = i; ++ } ++} ++ ++ ++static void * rate_control_simple_alloc(struct ieee80211_local *local) ++{ ++ struct global_rate_control *rctrl; ++ ++ rctrl = kmalloc(sizeof(*rctrl), GFP_ATOMIC); ++ if (rctrl == NULL) { ++ return NULL; ++ } ++ memset(rctrl, 0, sizeof(*rctrl)); ++ return rctrl; ++} ++ ++ ++static void rate_control_simple_free(void *priv) ++{ ++ struct global_rate_control *rctrl = priv; ++ kfree(rctrl); ++} ++ ++ ++static void rate_control_simple_clear(void *priv) ++{ ++} ++ ++ ++static void * rate_control_simple_alloc_sta(void) ++{ ++ struct sta_rate_control *rctrl; ++ ++ rctrl = kmalloc(sizeof(*rctrl), GFP_ATOMIC); ++ if (rctrl == NULL) { ++ return NULL; ++ } ++ memset(rctrl, 0, sizeof(*rctrl)); ++ return rctrl; ++} ++ ++ ++static void rate_control_simple_free_sta(void *priv) ++{ ++ struct sta_rate_control *rctrl = priv; ++ kfree(rctrl); ++} ++ ++ ++static int rate_control_simple_status_sta(struct ieee80211_local *local, ++ struct sta_info *sta, char *buf) ++{ ++ char *p = buf; ++ struct sta_rate_control *srctrl = sta->rate_ctrl_priv; ++ ++ p += sprintf(p, "tx_avg_rate_sum=%d\n", srctrl->tx_avg_rate_sum); ++ p += sprintf(p, "tx_avg_rate_num=%d\n", srctrl->tx_avg_rate_num); ++ if (srctrl->tx_avg_rate_num) ++ p += sprintf(p, "tx_avg_rate_avg=%d\n", ++ srctrl->tx_avg_rate_sum / ++ srctrl->tx_avg_rate_num); ++ return p - buf; ++} ++ ++ ++static int rate_control_simple_status_global(struct ieee80211_local *local, ++ char *buf) ++{ ++ return 0; ++} ++ ++ ++static struct rate_control_ops rate_control_simple = { ++ .name = "simple", ++ .tx_status = rate_control_simple_tx_status, ++ .get_rate = rate_control_simple_get_rate, ++ .rate_init = rate_control_simple_rate_init, ++ .clear = rate_control_simple_clear, ++ .status_sta = rate_control_simple_status_sta, ++ .status_global = rate_control_simple_status_global, ++ .alloc = rate_control_simple_alloc, ++ .free = rate_control_simple_free, ++ .alloc_sta = rate_control_simple_alloc_sta, ++ .free_sta = rate_control_simple_free_sta, ++}; ++ ++ ++int __init rate_control_simple_init(void) ++{ ++ return ieee80211_rate_control_register(&rate_control_simple); ++} ++ ++ ++static void __exit rate_control_simple_exit(void) ++{ ++ ieee80211_rate_control_unregister(&rate_control_simple); ++} ++ ++ ++module_init(rate_control_simple_init); ++module_exit(rate_control_simple_exit); ++ ++MODULE_DESCRIPTION("Simple rate control algorithm for ieee80211"); ++MODULE_LICENSE("GPL"); +diff -Nur linux-2.6.16/net/d80211/rate_control.h linux-2.6.16-bcm43xx/net/d80211/rate_control.h +--- linux-2.6.16/net/d80211/rate_control.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.16-bcm43xx/net/d80211/rate_control.h 2006-03-28 22:16:14.000000000 +0200 +@@ -0,0 +1,135 @@ ++/* ++ * Copyright 2002-2005, Instant802 Networks, Inc. ++ * Copyright 2005, Devicescape Software, Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#ifndef RATE_CONTROL ++#define RATE_CONTROL ++ ++#include <linux/netdevice.h> ++#include <linux/skbuff.h> ++#include <linux/types.h> ++#include <net/d80211.h> ++#include "ieee80211_i.h" ++#include "sta_info.h" ++ ++#define RATE_CONTROL_NUM_DOWN 20 ++#define RATE_CONTROL_NUM_UP 15 ++ ++ ++struct rate_control_extra { ++ /* values from rate_control_get_rate() to the caller: */ ++ struct ieee80211_rate *probe; /* probe with this rate, or NULL for no ++ * probing */ ++ int startidx, endidx, rateidx; ++ struct ieee80211_rate *nonerp; ++ int nonerp_idx; ++ ++ /* parameters from the caller to rate_control_get_rate(): */ ++ int mgmt_data; /* this is data frame that is used for management ++ * (e.g., IEEE 802.1X EAPOL) */ ++ u16 ethertype; ++}; ++ ++ ++struct rate_control_ops { ++ const char *name; ++ void (*tx_status)(struct net_device *dev, struct sk_buff *skb, ++ struct ieee80211_tx_status *status); ++ struct ieee80211_rate * ++ (*get_rate)(struct net_device *dev, struct sk_buff *skb, ++ struct rate_control_extra *extra); ++ void (*rate_init)(struct ieee80211_local *local, struct sta_info *sta); ++ void (*clear)(void *priv); ++ int (*status_sta)(struct ieee80211_local *local, ++ struct sta_info *sta, char *buf); ++ int (*status_global)(struct ieee80211_local *local, char *buf); ++ ++ void * (*alloc)(struct ieee80211_local *local); ++ void (*free)(void *priv); ++ void * (*alloc_sta)(void); ++ void (*free_sta)(void *priv); ++}; ++ ++ ++int ieee80211_rate_control_register(struct rate_control_ops *ops); ++void ieee80211_rate_control_unregister(struct rate_control_ops *ops); ++ ++ ++static inline void rate_control_tx_status(struct net_device *dev, ++ struct sk_buff *skb, ++ struct ieee80211_tx_status *status) ++{ ++ struct ieee80211_local *local = dev->priv; ++ local->rate_ctrl->tx_status(dev, skb, status); ++} ++ ++ ++static inline struct ieee80211_rate * ++rate_control_get_rate(struct net_device *dev, struct sk_buff *skb, ++ struct rate_control_extra *extra) ++{ ++ struct ieee80211_local *local = dev->priv; ++ return local->rate_ctrl->get_rate(dev, skb, extra); ++} ++ ++ ++static inline void rate_control_rate_init(struct ieee80211_local *local, ++ struct sta_info *sta) ++{ ++ local->rate_ctrl->rate_init(local, sta); ++} ++ ++ ++static inline void rate_control_clear(struct ieee80211_local *local) ++{ ++ local->rate_ctrl->clear(local->rate_ctrl_priv); ++} ++ ++ ++static inline int rate_control_status_sta(struct ieee80211_local *local, ++ struct sta_info *sta, char *buf) ++{ ++ return local->rate_ctrl->status_sta(local, sta, buf); ++} ++ ++ ++static inline int rate_control_status_global(struct ieee80211_local *local, ++ char *buf) ++{ ++ return local->rate_ctrl->status_global(local, buf); ++} ++ ++ ++static inline void * rate_control_alloc(struct ieee80211_local *local) ++{ ++ return local->rate_ctrl->alloc(local); ++} ++ ++ ++static inline void rate_control_free(struct ieee80211_local *local) ++{ ++ if (local->rate_ctrl == NULL || local->rate_ctrl_priv == NULL) ++ return; ++ local->rate_ctrl->free(local->rate_ctrl_priv); ++ local->rate_ctrl_priv = NULL; ++} ++ ++ ++static inline void * rate_control_alloc_sta(struct ieee80211_local *local) ++{ ++ return local->rate_ctrl->alloc_sta(); ++} ++ ++ ++static inline void rate_control_free_sta(struct ieee80211_local *local, ++ void *priv) ++{ ++ local->rate_ctrl->free_sta(priv); ++} ++ ++#endif /* RATE_CONTROL */ +diff -Nur linux-2.6.16/net/d80211/sta_info.c linux-2.6.16-bcm43xx/net/d80211/sta_info.c +--- linux-2.6.16/net/d80211/sta_info.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.16-bcm43xx/net/d80211/sta_info.c 2006-03-28 22:16:14.000000000 +0200 +@@ -0,0 +1,413 @@ ++/* ++ * Copyright 2002-2005, Instant802 Networks, Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include <linux/config.h> ++#include <linux/version.h> ++#include <linux/module.h> ++#include <linux/init.h> ++#include <linux/netdevice.h> ++#include <linux/types.h> ++#include <linux/slab.h> ++#include <linux/skbuff.h> ++#include <linux/if_arp.h> ++ ++#include <net/d80211.h> ++#include "ieee80211_i.h" ++#include "ieee80211_proc.h" ++#include "rate_control.h" ++ ++ ++/* Caller must hold local->sta_lock */ ++static void sta_info_hash_add(struct ieee80211_local *local, ++ struct sta_info *sta) ++{ ++ sta->hnext = local->sta_hash[STA_HASH(sta->addr)]; ++ local->sta_hash[STA_HASH(sta->addr)] = sta; ++} ++ ++ ++/* Caller must hold local->sta_lock */ ++static void sta_info_hash_del(struct ieee80211_local *local, ++ struct sta_info *sta) ++{ ++ struct sta_info *s; ++ ++ s = local->sta_hash[STA_HASH(sta->addr)]; ++ if (s == NULL) ++ return; ++ if (memcmp(s->addr, sta->addr, ETH_ALEN) == 0) { ++ local->sta_hash[STA_HASH(sta->addr)] = s->hnext; ++ return; ++ } ++ ++ while (s->hnext != NULL && ++ memcmp(s->hnext->addr, sta->addr, ETH_ALEN) != 0) ++ s = s->hnext; ++ if (s->hnext != NULL) ++ s->hnext = s->hnext->hnext; ++ else ++ printk(KERN_ERR "%s: could not remove STA " MACSTR " from " ++ "hash table\n", local->mdev->name, MAC2STR(sta->addr)); ++} ++ ++ ++struct sta_info * sta_info_get(struct ieee80211_local *local, u8 *addr) ++{ ++ struct sta_info *sta; ++ ++ spin_lock_bh(&local->sta_lock); ++ sta = local->sta_hash[STA_HASH(addr)]; ++ while (sta) { ++ if (memcmp(sta->addr, addr, ETH_ALEN) == 0) { ++ atomic_inc(&sta->users); ++ break; ++ } ++ sta = sta->hnext; ++ } ++ spin_unlock_bh(&local->sta_lock); ++ ++ return sta; ++} ++ ++ ++int sta_info_min_txrate_get(struct ieee80211_local *local) ++{ ++ struct sta_info *sta; ++ int min_txrate = 9999999; ++ int i; ++ ++ spin_lock_bh(&local->sta_lock); ++ for (i = 0; i < STA_HASH_SIZE; i++) { ++ sta = local->sta_hash[i]; ++ while (sta) { ++ if (sta->txrate < min_txrate) ++ min_txrate = sta->txrate; ++ sta = sta->hnext; ++ } ++ } ++ spin_unlock_bh(&local->sta_lock); ++ if (min_txrate == 9999999) ++ min_txrate = 0; ++ ++ return min_txrate; ++} ++ ++ ++void sta_info_release(struct ieee80211_local *local, struct sta_info *sta) ++{ ++ struct sk_buff *skb; ++ ++ if (!atomic_dec_and_test(&sta->users)) ++ return; ++ ++ /* free sta structure; it has already been removed from ++ * hash table etc. external structures. Make sure that all ++ * buffered frames are release (one might have been added ++ * after sta_info_free() was called). */ ++ while ((skb = skb_dequeue(&sta->ps_tx_buf)) != NULL) { ++ local->total_ps_buffered--; ++ dev_kfree_skb_any(skb); ++ } ++ while ((skb = skb_dequeue(&sta->tx_filtered)) != NULL) { ++ dev_kfree_skb_any(skb); ++ } ++ rate_control_free_sta(local, sta->rate_ctrl_priv); ++ kfree(sta); ++} ++ ++ ++struct sta_info * sta_info_add(struct ieee80211_local *local, ++ struct net_device *dev, u8 *addr) ++{ ++ struct sta_info *sta; ++ ++ sta = kmalloc(sizeof(*sta), GFP_ATOMIC); ++ if (!sta) ++ return NULL; ++ ++ memset(sta, 0, sizeof(*sta)); ++ ++ sta->rate_ctrl_priv = rate_control_alloc_sta(local); ++ if (sta->rate_ctrl_priv == NULL) { ++ kfree(sta); ++ return NULL; ++ } ++ ++ memcpy(sta->addr, addr, ETH_ALEN); ++ sta->dev = dev; ++ skb_queue_head_init(&sta->ps_tx_buf); ++ skb_queue_head_init(&sta->tx_filtered); ++ atomic_inc(&sta->users); /* sta in hashlist etc, decremented by ++ * sta_info_free() */ ++ atomic_inc(&sta->users); /* sta used by caller, decremented by ++ * sta_info_release() */ ++ spin_lock_bh(&local->sta_lock); ++ list_add(&sta->list, &local->sta_list); ++ local->num_sta++; ++ sta_info_hash_add(local, sta); ++ spin_unlock_bh(&local->sta_lock); ++ if (local->hw->sta_table_notification) ++ local->hw->sta_table_notification(local->mdev, local->num_sta); ++ sta->key_idx_compression = HW_KEY_IDX_INVALID; ++ ++#ifdef CONFIG_D80211_VERBOSE_DEBUG ++ printk(KERN_DEBUG "%s: Added STA " MACSTR "\n", ++ local->mdev->name, MAC2STR(addr)); ++#endif /* CONFIG_D80211_VERBOSE_DEBUG */ ++ ++ if (!in_interrupt()) { ++ ieee80211_proc_init_sta(local, sta); ++ } else { ++ /* procfs entry adding might sleep, so schedule process context ++ * task for adding proc entry for STAs that do not yet have ++ * one. */ ++ schedule_work(&local->sta_proc_add); ++ } ++ ++ return sta; ++} ++ ++ ++void sta_info_free(struct ieee80211_local *local, struct sta_info *sta, ++ int locked) ++{ ++ struct sk_buff *skb; ++ struct ieee80211_sub_if_data *sdata; ++ ++ if (!locked) ++ spin_lock_bh(&local->sta_lock); ++ sta_info_hash_del(local, sta); ++ list_del(&sta->list); ++ sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev); ++ if (sta->flags & WLAN_STA_PS) { ++ sta->flags &= ~WLAN_STA_PS; ++ if (sdata->bss) ++ atomic_dec(&sdata->bss->num_sta_ps); ++ } ++ local->num_sta--; ++ sta_info_remove_aid_ptr(sta); ++ if (!locked) ++ spin_unlock_bh(&local->sta_lock); ++ if (local->hw->sta_table_notification) ++ local->hw->sta_table_notification(local->mdev, local->num_sta); ++ ++ while ((skb = skb_dequeue(&sta->ps_tx_buf)) != NULL) { ++ local->total_ps_buffered--; ++ dev_kfree_skb_any(skb); ++ } ++ while ((skb = skb_dequeue(&sta->tx_filtered)) != NULL) { ++ dev_kfree_skb_any(skb); ++ } ++ ++ if (sta->key) { ++ if (local->hw->set_key) { ++ struct ieee80211_key_conf *key; ++ key = ieee80211_key_data2conf(local, sta->key); ++ if (key) { ++ local->hw->set_key(local->mdev, DISABLE_KEY, ++ sta->addr, key, sta->aid); ++ kfree(key); ++ } ++ } ++ kfree(sta->key); ++ sta->key = NULL; ++ } else if (sta->key_idx_compression != HW_KEY_IDX_INVALID) { ++ struct ieee80211_key_conf conf; ++ memset(&conf, 0, sizeof(conf)); ++ conf.hw_key_idx = sta->key_idx_compression; ++ conf.alg = ALG_NULL; ++ conf.force_sw_encrypt = 1; ++ local->hw->set_key(local->mdev, DISABLE_KEY, sta->addr, &conf, ++ sta->aid); ++ sta->key_idx_compression = HW_KEY_IDX_INVALID; ++ } ++ ++#ifdef CONFIG_D80211_VERBOSE_DEBUG ++ printk(KERN_DEBUG "%s: Removed STA " MACSTR "\n", ++ local->mdev->name, MAC2STR(sta->addr)); ++#endif /* CONFIG_D80211_VERBOSE_DEBUG */ ++ ++ ieee80211_proc_deinit_sta(local, sta); ++ ++ if (atomic_read(&sta->users) != 1) { ++ /* This is OK, but printed for debugging. The station structure ++ * will be removed when the other user of the data calls ++ * sta_info_release(). */ ++ printk(KERN_DEBUG "%s: STA " MACSTR " users count %d when " ++ "removing it\n", local->mdev->name, MAC2STR(sta->addr), ++ atomic_read(&sta->users)); ++ } ++ ++ sta_info_release(local, sta); ++} ++ ++ ++static inline int sta_info_buffer_expired(struct sk_buff *skb) ++{ ++ struct ieee80211_tx_packet_data *pkt_data; ++ if (!skb) ++ return 0; ++ ++ /* TODO: this could be improved by passing STA listen interval into ++ * the kernel driver and expiring frames after 2 x listen_interval x ++ * beacon interval */ ++ ++ pkt_data = (struct ieee80211_tx_packet_data *) skb->cb; ++ return time_after(jiffies, pkt_data->jiffies + STA_TX_BUFFER_EXPIRE); ++} ++ ++ ++static void sta_info_cleanup_expire_buffered(struct ieee80211_local *local, ++ struct sta_info *sta) ++{ ++ unsigned long flags; ++ struct sk_buff *skb; ++ ++ if (skb_queue_empty(&sta->ps_tx_buf)) ++ return; ++ ++ for (;;) { ++ spin_lock_irqsave(&sta->ps_tx_buf.lock, flags); ++ skb = skb_peek(&sta->ps_tx_buf); ++ if (sta_info_buffer_expired(skb)) ++ skb = __skb_dequeue(&sta->ps_tx_buf); ++ else ++ skb = NULL; ++ spin_unlock_irqrestore(&sta->ps_tx_buf.lock, flags); ++ ++ if (skb) { ++ local->total_ps_buffered--; ++ printk(KERN_DEBUG "Buffered frame expired (STA " ++ MACSTR ")\n", MAC2STR(sta->addr)); ++ dev_kfree_skb(skb); ++ } else ++ break; ++ } ++} ++ ++ ++static void sta_info_cleanup(unsigned long data) ++{ ++ struct ieee80211_local *local = (struct ieee80211_local *) data; ++ struct list_head *ptr; ++ ++ spin_lock_bh(&local->sta_lock); ++ ptr = local->sta_list.next; ++ while (ptr && ptr != &local->sta_list) { ++ struct sta_info *sta = (struct sta_info *) ptr; ++ atomic_inc(&sta->users); ++ sta_info_cleanup_expire_buffered(local, sta); ++ sta_info_release(local, sta); ++ ptr = ptr->next; ++ } ++ spin_unlock_bh(&local->sta_lock); ++ ++ local->sta_cleanup.expires = jiffies + STA_INFO_CLEANUP_INTERVAL; ++ add_timer(&local->sta_cleanup); ++} ++ ++ ++static void sta_info_proc_add_task(void *data) ++{ ++ struct ieee80211_local *local = data; ++ struct list_head *ptr; ++ struct sta_info *sta; ++ int max_adds = 100; ++ ++ while (max_adds > 0) { ++ sta = NULL; ++ spin_lock_bh(&local->sta_lock); ++ list_for_each(ptr, &local->sta_list) { ++ sta = list_entry(ptr, struct sta_info, list); ++ if (!sta->proc_entry_added) { ++ atomic_inc(&sta->users); ++ break; ++ } ++ sta = NULL; ++ } ++ spin_unlock_bh(&local->sta_lock); ++ ++ if (!sta) ++ break; ++ ++ ieee80211_proc_init_sta(local, sta); ++ atomic_dec(&sta->users); ++ ++ max_adds--; ++ } ++} ++ ++ ++void sta_info_init(struct ieee80211_local *local) ++{ ++ spin_lock_init(&local->sta_lock); ++ INIT_LIST_HEAD(&local->sta_list); ++ ++ init_timer(&local->sta_cleanup); ++ local->sta_cleanup.expires = jiffies + STA_INFO_CLEANUP_INTERVAL; ++ local->sta_cleanup.data = (unsigned long) local; ++ local->sta_cleanup.function = sta_info_cleanup; ++ ++ INIT_WORK(&local->sta_proc_add, sta_info_proc_add_task, local); ++} ++ ++void sta_info_start(struct ieee80211_local *local) ++{ ++ add_timer(&local->sta_cleanup); ++} ++ ++void sta_info_stop(struct ieee80211_local *local) ++{ ++ struct list_head *ptr; ++ ++ del_timer(&local->sta_cleanup); ++ ++ ptr = local->sta_list.next; ++ while (ptr && ptr != &local->sta_list) { ++ struct sta_info *sta = (struct sta_info *) ptr; ++ ptr = ptr->next; ++ sta_info_free(local, sta, 0); ++ } ++} ++ ++ ++void sta_info_remove_aid_ptr(struct sta_info *sta) ++{ ++ struct ieee80211_sub_if_data *sdata; ++ ++ sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev); ++ if (sta->aid <= 0 || !sdata->bss) ++ return; ++ ++ sdata->bss->sta_aid[sta->aid - 1] = NULL; ++ if (sta->aid == sdata->bss->max_aid) { ++ while (sdata->bss->max_aid > 0 && ++ sdata->bss->sta_aid[sdata->bss->max_aid - 1] == NULL) ++ sdata->bss->max_aid--; ++ } ++} ++ ++ ++/** ++ * sta_info_flush - flush matching STA entries from the STA table ++ * @local: local interface data ++ * @dev: matching rule for the net device (sta->dev) or %NULL to match all STAs ++ */ ++void sta_info_flush(struct ieee80211_local *local, struct net_device *dev) ++{ ++ struct list_head *ptr, *n; ++ ++ spin_lock_bh(&local->sta_lock); ++ ++ list_for_each_safe(ptr, n, &local->sta_list) { ++ struct sta_info *sta = list_entry(ptr, struct sta_info, list); ++ if (dev == NULL || dev == sta->dev) ++ sta_info_free(local, sta, 1); ++ } ++ spin_unlock_bh(&local->sta_lock); ++} +diff -Nur linux-2.6.16/net/d80211/sta_info.h linux-2.6.16-bcm43xx/net/d80211/sta_info.h +--- linux-2.6.16/net/d80211/sta_info.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.16-bcm43xx/net/d80211/sta_info.h 2006-03-28 22:16:14.000000000 +0200 +@@ -0,0 +1,148 @@ ++/* ++ * Copyright 2002-2005, Devicescape Software, Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#ifndef STA_INFO_H ++#define STA_INFO_H ++ ++#include <linux/if_ether.h> ++#include <linux/types.h> ++#include "ieee80211_i.h" ++#include "ieee80211_key.h" ++ ++/* Stations flags (struct sta_info::flags) */ ++#define WLAN_STA_AUTH BIT(0) ++#define WLAN_STA_ASSOC BIT(1) ++#define WLAN_STA_PS BIT(2) ++#define WLAN_STA_TIM BIT(3) /* TIM bit is on for PS stations */ ++#define WLAN_STA_PERM BIT(4) /* permanent; do not remove entry on expiration */ ++#define WLAN_STA_AUTHORIZED BIT(5) /* If 802.1X is used, this flag is ++ * controlling whether STA is authorized to ++ * send and receive non-IEEE 802.1X frames ++ */ ++#define WLAN_STA_SHORT_PREAMBLE BIT(7) ++#define WLAN_STA_WME BIT(9) ++#define WLAN_STA_XR BIT(26) ++#define WLAN_STA_WDS BIT(27) ++ ++ ++struct sta_info { ++ struct list_head list; ++ struct sta_info *hnext; /* next entry in hash table list */ ++ atomic_t users; /* number of users (do not remove if > 0) */ ++ ++ u8 addr[ETH_ALEN]; ++ u16 aid; /* STA's unique AID (1..2007), 0 = not yet assigned */ ++ u32 flags; /* WLAN_STA_ */ ++ ++ struct sk_buff_head ps_tx_buf; /* buffer of TX frames for station in ++ * power saving state */ ++ int pspoll; /* whether STA has send a PS Poll frame */ ++ struct sk_buff_head tx_filtered; /* buffer of TX frames that were ++ * already given to low-level driver, ++ * but were filtered */ ++ int clear_dst_mask; ++ ++ unsigned long rx_packets, tx_packets; /* number of RX/TX MSDUs */ ++ unsigned long rx_bytes, tx_bytes; ++ unsigned long tx_retry_failed, tx_retry_count; ++ unsigned long tx_filtered_count; ++ ++ unsigned int wep_weak_iv_count; /* number of RX frames with weak IV */ ++ ++ unsigned long last_rx; ++ u32 supp_rates; /* bitmap of supported rates in local->curr_rates */ ++ int txrate; /* index in local->curr_rates */ ++ int last_txrate; /* last rate used to send a frame to this STA */ ++ int last_nonerp_idx; ++ ++ struct net_device *dev; /* which net device is this station associated ++ * to */ ++ ++ struct ieee80211_key *key; ++ ++ u32 tx_num_consecutive_failures; ++ u32 tx_num_mpdu_ok; ++ u32 tx_num_mpdu_fail; ++ ++ void *rate_ctrl_priv; ++ ++ /* last received seq/frag number from this STA (per RX queue) */ ++ u16 last_seq_ctrl[NUM_RX_DATA_QUEUES]; ++ unsigned long num_duplicates; /* number of duplicate frames received ++ * from this STA */ ++ unsigned long tx_fragments; /* number of transmitted MPDUs */ ++ unsigned long rx_fragments; /* number of received MPDUs */ ++ unsigned long rx_dropped; /* number of dropped MPDUs from this STA */ ++ ++ int last_rssi; /* RSSI of last received frame from this STA */ ++ int last_ack_rssi[3]; /* RSSI of last received ACKs from this STA */ ++ unsigned long last_ack; ++ int channel_use; ++ int channel_use_raw; ++ ++ int antenna_sel; ++ ++ ++ int key_idx_compression; /* key table index for compression and TX ++ * filtering; used only if sta->key is not ++ * set */ ++ ++ int proc_entry_added:1; ++ int assoc_ap:1; /* whether this is an AP that we are associated with ++ * as a client */ ++ ++#ifdef CONFIG_HOSTAPD_WPA_TESTING ++ u32 wpa_trigger; ++#endif /* CONFIG_HOSTAPD_WPA_TESTING */ ++ ++#ifdef CONFIG_D80211_DEBUG_COUNTERS ++ unsigned int wme_rx_queue[NUM_RX_DATA_QUEUES]; ++ unsigned int wme_tx_queue[NUM_RX_DATA_QUEUES]; ++#endif /* CONFIG_D80211_DEBUG_COUNTERS */ ++ ++ int vlan_id; ++}; ++ ++ ++/* Maximum number of concurrently registered stations */ ++#define MAX_STA_COUNT 2007 ++ ++/* Maximum number of AIDs to use for STAs; must be 2007 or lower ++ * (IEEE 802.11 beacon format limitation) */ ++#define MAX_AID_TABLE_SIZE 2007 ++ ++#define STA_HASH_SIZE 256 ++#define STA_HASH(sta) (sta[5]) ++ ++ ++/* Maximum number of frames to buffer per power saving station */ ++#define STA_MAX_TX_BUFFER 128 ++ ++/* Buffered frame expiry time */ ++#define STA_TX_BUFFER_EXPIRE (10 * HZ) ++ ++/* How often station data is cleaned up (e.g., expiration of buffered frames) ++ */ ++#define STA_INFO_CLEANUP_INTERVAL (10 * HZ) ++ ++struct ieee80211_local; ++ ++struct sta_info * sta_info_get(struct ieee80211_local *local, u8 *addr); ++int sta_info_min_txrate_get(struct ieee80211_local *local); ++void sta_info_release(struct ieee80211_local *local, struct sta_info *sta); ++struct sta_info * sta_info_add(struct ieee80211_local *local, ++ struct net_device *dev, u8 *addr); ++void sta_info_free(struct ieee80211_local *local, struct sta_info *sta, ++ int locked); ++void sta_info_init(struct ieee80211_local *local); ++void sta_info_start(struct ieee80211_local *local); ++void sta_info_stop(struct ieee80211_local *local); ++void sta_info_remove_aid_ptr(struct sta_info *sta); ++void sta_info_flush(struct ieee80211_local *local, struct net_device *dev); ++ ++#endif /* STA_INFO_H */ +diff -Nur linux-2.6.16/net/d80211/tkip.c linux-2.6.16-bcm43xx/net/d80211/tkip.c +--- linux-2.6.16/net/d80211/tkip.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.16-bcm43xx/net/d80211/tkip.c 2006-03-28 22:16:14.000000000 +0200 +@@ -0,0 +1,341 @@ ++/* ++ * Copyright 2002-2004, Instant802 Networks, Inc. ++ * Copyright 2005, Devicescape Software, Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#ifdef CONFIG_TKIP_DEBUG ++#include <linux/config.h> ++#include <linux/version.h> ++#include <linux/module.h> ++#include <linux/kernel.h> ++#include <linux/skbuff.h> ++#endif /* CONFIG_TKIP_DEBUG */ ++ ++#include <linux/types.h> ++#include <linux/netdevice.h> ++ ++#include <net/d80211.h> ++#include "ieee80211_key.h" ++#ifdef CONFIG_TKIP_DEBUG ++#include "ieee80211_i.h" ++#endif /* CONFIG_TKIP_DEBUG */ ++#include "tkip.h" ++ ++/* Dummy prototypes for structures used in wep.h, but not really needed for ++ * TKIP. */ ++struct ieee80211_local; ++struct sk_buff; ++#include "wep.h" ++ ++ ++/* TKIP key mixing functions */ ++ ++ ++#define PHASE1_LOOP_COUNT 8 ++ ++ ++/* 2-byte by 2-byte subset of the full AES S-box table; second part of this ++ * table is identical to first part but byte-swapped */ ++static const u16 tkip_sbox[256] = ++{ ++ 0xC6A5, 0xF884, 0xEE99, 0xF68D, 0xFF0D, 0xD6BD, 0xDEB1, 0x9154, ++ 0x6050, 0x0203, 0xCEA9, 0x567D, 0xE719, 0xB562, 0x4DE6, 0xEC9A, ++ 0x8F45, 0x1F9D, 0x8940, 0xFA87, 0xEF15, 0xB2EB, 0x8EC9, 0xFB0B, ++ 0x41EC, 0xB367, 0x5FFD, 0x45EA, 0x23BF, 0x53F7, 0xE496, 0x9B5B, ++ 0x75C2, 0xE11C, 0x3DAE, 0x4C6A, 0x6C5A, 0x7E41, 0xF502, 0x834F, ++ 0x685C, 0x51F4, 0xD134, 0xF908, 0xE293, 0xAB73, 0x6253, 0x2A3F, ++ 0x080C, 0x9552, 0x4665, 0x9D5E, 0x3028, 0x37A1, 0x0A0F, 0x2FB5, ++ 0x0E09, 0x2436, 0x1B9B, 0xDF3D, 0xCD26, 0x4E69, 0x7FCD, 0xEA9F, ++ 0x121B, 0x1D9E, 0x5874, 0x342E, 0x362D, 0xDCB2, 0xB4EE, 0x5BFB, ++ 0xA4F6, 0x764D, 0xB761, 0x7DCE, 0x527B, 0xDD3E, 0x5E71, 0x1397, ++ 0xA6F5, 0xB968, 0x0000, 0xC12C, 0x4060, 0xE31F, 0x79C8, 0xB6ED, ++ 0xD4BE, 0x8D46, 0x67D9, 0x724B, 0x94DE, 0x98D4, 0xB0E8, 0x854A, ++ 0xBB6B, 0xC52A, 0x4FE5, 0xED16, 0x86C5, 0x9AD7, 0x6655, 0x1194, ++ 0x8ACF, 0xE910, 0x0406, 0xFE81, 0xA0F0, 0x7844, 0x25BA, 0x4BE3, ++ 0xA2F3, 0x5DFE, 0x80C0, 0x058A, 0x3FAD, 0x21BC, 0x7048, 0xF104, ++ 0x63DF, 0x77C1, 0xAF75, 0x4263, 0x2030, 0xE51A, 0xFD0E, 0xBF6D, ++ 0x814C, 0x1814, 0x2635, 0xC32F, 0xBEE1, 0x35A2, 0x88CC, 0x2E39, ++ 0x9357, 0x55F2, 0xFC82, 0x7A47, 0xC8AC, 0xBAE7, 0x322B, 0xE695, ++ 0xC0A0, 0x1998, 0x9ED1, 0xA37F, 0x4466, 0x547E, 0x3BAB, 0x0B83, ++ 0x8CCA, 0xC729, 0x6BD3, 0x283C, 0xA779, 0xBCE2, 0x161D, 0xAD76, ++ 0xDB3B, 0x6456, 0x744E, 0x141E, 0x92DB, 0x0C0A, 0x486C, 0xB8E4, ++ 0x9F5D, 0xBD6E, 0x43EF, 0xC4A6, 0x39A8, 0x31A4, 0xD337, 0xF28B, ++ 0xD532, 0x8B43, 0x6E59, 0xDAB7, 0x018C, 0xB164, 0x9CD2, 0x49E0, ++ 0xD8B4, 0xACFA, 0xF307, 0xCF25, 0xCAAF, 0xF48E, 0x47E9, 0x1018, ++ 0x6FD5, 0xF088, 0x4A6F, 0x5C72, 0x3824, 0x57F1, 0x73C7, 0x9751, ++ 0xCB23, 0xA17C, 0xE89C, 0x3E21, 0x96DD, 0x61DC, 0x0D86, 0x0F85, ++ 0xE090, 0x7C42, 0x71C4, 0xCCAA, 0x90D8, 0x0605, 0xF701, 0x1C12, ++ 0xC2A3, 0x6A5F, 0xAEF9, 0x69D0, 0x1791, 0x9958, 0x3A27, 0x27B9, ++ 0xD938, 0xEB13, 0x2BB3, 0x2233, 0xD2BB, 0xA970, 0x0789, 0x33A7, ++ 0x2DB6, 0x3C22, 0x1592, 0xC920, 0x8749, 0xAAFF, 0x5078, 0xA57A, ++ 0x038F, 0x59F8, 0x0980, 0x1A17, 0x65DA, 0xD731, 0x84C6, 0xD0B8, ++ 0x82C3, 0x29B0, 0x5A77, 0x1E11, 0x7BCB, 0xA8FC, 0x6DD6, 0x2C3A, ++}; ++ ++ ++static inline u16 Mk16(u8 x, u8 y) ++{ ++ return ((u16) x << 8) | (u16) y; ++} ++ ++ ++static inline u8 Hi8(u16 v) ++{ ++ return v >> 8; ++} ++ ++ ++static inline u8 Lo8(u16 v) ++{ ++ return v & 0xff; ++} ++ ++ ++static inline u16 Hi16(u32 v) ++{ ++ return v >> 16; ++} ++ ++ ++static inline u16 Lo16(u32 v) ++{ ++ return v & 0xffff; ++} ++ ++ ++static inline u16 RotR1(u16 v) ++{ ++ return (v >> 1) | ((v & 0x0001) << 15); ++} ++ ++ ++static inline u16 tkip_S(u16 val) ++{ ++ u16 a = tkip_sbox[Hi8(val)]; ++ ++ return tkip_sbox[Lo8(val)] ^ Hi8(a) ^ (Lo8(a) << 8); ++} ++ ++ ++ ++/* P1K := Phase1(TA, TK, TSC) ++ * TA = transmitter address (48 bits) ++ * TK = dot11DefaultKeyValue or dot11KeyMappingValue (128 bits) ++ * TSC = TKIP sequence counter (48 bits, only 32 msb bits used) ++ * P1K: 80 bits ++ */ ++static void tkip_mixing_phase1(const u8 *ta, const u8 *tk, u32 tsc_IV32, ++ u16 *p1k) ++{ ++ int i, j; ++ ++ p1k[0] = Lo16(tsc_IV32); ++ p1k[1] = Hi16(tsc_IV32); ++ p1k[2] = Mk16(ta[1], ta[0]); ++ p1k[3] = Mk16(ta[3], ta[2]); ++ p1k[4] = Mk16(ta[5], ta[4]); ++ ++ for (i = 0; i < PHASE1_LOOP_COUNT; i++) { ++ j = 2 * (i & 1); ++ p1k[0] += tkip_S(p1k[4] ^ Mk16(tk[ 1 + j], tk[ 0 + j])); ++ p1k[1] += tkip_S(p1k[0] ^ Mk16(tk[ 5 + j], tk[ 4 + j])); ++ p1k[2] += tkip_S(p1k[1] ^ Mk16(tk[ 9 + j], tk[ 8 + j])); ++ p1k[3] += tkip_S(p1k[2] ^ Mk16(tk[13 + j], tk[12 + j])); ++ p1k[4] += tkip_S(p1k[3] ^ Mk16(tk[ 1 + j], tk[ 0 + j])) + i; ++ } ++} ++ ++ ++static void tkip_mixing_phase2(const u16 *p1k, const u8 *tk, u16 tsc_IV16, ++ u8 *rc4key) ++{ ++ u16 ppk[6]; ++ int i; ++ ++ ppk[0] = p1k[0]; ++ ppk[1] = p1k[1]; ++ ppk[2] = p1k[2]; ++ ppk[3] = p1k[3]; ++ ppk[4] = p1k[4]; ++ ppk[5] = p1k[4] + tsc_IV16; ++ ++ ppk[0] += tkip_S(ppk[5] ^ Mk16(tk[ 1], tk[ 0])); ++ ppk[1] += tkip_S(ppk[0] ^ Mk16(tk[ 3], tk[ 2])); ++ ppk[2] += tkip_S(ppk[1] ^ Mk16(tk[ 5], tk[ 4])); ++ ppk[3] += tkip_S(ppk[2] ^ Mk16(tk[ 7], tk[ 6])); ++ ppk[4] += tkip_S(ppk[3] ^ Mk16(tk[ 9], tk[ 8])); ++ ppk[5] += tkip_S(ppk[4] ^ Mk16(tk[11], tk[10])); ++ ppk[0] += RotR1(ppk[5] ^ Mk16(tk[13], tk[12])); ++ ppk[1] += RotR1(ppk[0] ^ Mk16(tk[15], tk[14])); ++ ppk[2] += RotR1(ppk[1]); ++ ppk[3] += RotR1(ppk[2]); ++ ppk[4] += RotR1(ppk[3]); ++ ppk[5] += RotR1(ppk[4]); ++ ++ rc4key[0] = Hi8(tsc_IV16); ++ rc4key[1] = (Hi8(tsc_IV16) | 0x20) & 0x7f; ++ rc4key[2] = Lo8(tsc_IV16); ++ rc4key[3] = Lo8((ppk[5] ^ Mk16(tk[1], tk[0])) >> 1); ++ ++ for (i = 0; i < 6; i++) { ++ rc4key[4 + 2 * i] = Lo8(ppk[i]); ++ rc4key[5 + 2 * i] = Hi8(ppk[i]); ++ } ++} ++ ++ ++/* Add TKIP IV and Ext. IV at @pos. @iv0, @iv1, and @iv2 are the first octets ++ * of the IV. Returns pointer to the octet following IVs (i.e., beginning of ++ * the packet payload). */ ++u8 * ieee80211_tkip_add_iv(u8 *pos, struct ieee80211_key *key, ++ u8 iv0, u8 iv1, u8 iv2) ++{ ++ *pos++ = iv0; ++ *pos++ = iv1; ++ *pos++ = iv2; ++ *pos++ = (key->keyidx << 6) | (1 << 5) /* Ext IV */; ++ *pos++ = key->u.tkip.iv32 & 0xff; ++ *pos++ = (key->u.tkip.iv32 >> 8) & 0xff; ++ *pos++ = (key->u.tkip.iv32 >> 16) & 0xff; ++ *pos++ = (key->u.tkip.iv32 >> 24) & 0xff; ++ return pos; ++} ++ ++ ++/* Encrypt packet payload with TKIP using @key. @pos is a pointer to the ++ * beginning of the buffer containing payload. This payload must include ++ * headroom of eight octets for IV and Ext. IV and taildroom of four octets ++ * for ICV. @payload_len is the length of payload (_not_ including extra ++ * headroom and tailroom). @ta is the transmitter addresses. */ ++void ieee80211_tkip_encrypt_data(struct ieee80211_key *key, u8 *pos, ++ size_t payload_len, u8 *ta) ++{ ++ u8 rc4key[16]; ++ ++ /* Calculate per-packet key */ ++ if (key->u.tkip.iv16 == 0 || !key->u.tkip.tx_initialized) { ++ /* IV16 wrapped around - perform TKIP phase 1 */ ++ tkip_mixing_phase1(ta, &key->key[ALG_TKIP_TEMP_ENCR_KEY], ++ key->u.tkip.iv32, key->u.tkip.p1k); ++ key->u.tkip.tx_initialized = 1; ++ } ++ ++ tkip_mixing_phase2(key->u.tkip.p1k, &key->key[ALG_TKIP_TEMP_ENCR_KEY], ++ key->u.tkip.iv16, rc4key); ++ ++ pos = ieee80211_tkip_add_iv(pos, key, rc4key[0], rc4key[1], rc4key[2]); ++ ieee80211_wep_encrypt_data(rc4key, 16, pos, payload_len); ++} ++ ++ ++/* Decrypt packet payload with TKIP using @key. @pos is a pointer to the ++ * beginning of the buffer containing IEEE 802.11 header payload, i.e., ++ * including IV, Ext. IV, real data, Michael MIC, ICV. @payload_len is the ++ * length of payload, including IV, Ext. IV, MIC, ICV. */ ++int ieee80211_tkip_decrypt_data(struct ieee80211_key *key, u8 *payload, ++ size_t payload_len, u8 *ta, int only_iv, ++ int queue) ++{ ++ u32 iv32; ++ u32 iv16; ++ u8 rc4key[16], keyid, *pos = payload; ++ int res; ++ ++ if (payload_len < 12) ++ return -1; ++ ++ iv16 = (pos[0] << 8) | pos[2]; ++ keyid = pos[3]; ++ iv32 = pos[4] | (pos[5] << 8) | (pos[6] << 16) | (pos[7] << 24); ++ pos += 8; ++#ifdef CONFIG_TKIP_DEBUG ++ { ++ int i; ++ printk(KERN_DEBUG "TKIP decrypt: data(len=%zd)", payload_len); ++ for (i = 0; i < payload_len; i++) ++ printk(" %02x", payload[i]); ++ printk("\n"); ++ printk(KERN_DEBUG "TKIP decrypt: iv16=%04x iv32=%08x\n", ++ iv16, iv32); ++ } ++#endif /* CONFIG_TKIP_DEBUG */ ++ ++ if (!(keyid & (1 << 5))) ++ return TKIP_DECRYPT_NO_EXT_IV; ++ ++ if ((keyid >> 6) != key->keyidx) ++ return TKIP_DECRYPT_INVALID_KEYIDX; ++ ++ if (key->u.tkip.rx_initialized[queue] && ++ (iv32 < key->u.tkip.iv32_rx[queue] || ++ (iv32 == key->u.tkip.iv32_rx[queue] && ++ iv16 <= key->u.tkip.iv16_rx[queue]))) { ++#ifdef CONFIG_TKIP_DEBUG ++ printk(KERN_DEBUG "TKIP replay detected for RX frame from " ++ MACSTR " (RX IV (%04x,%02x) <= prev. IV (%04x,%02x)\n", ++ MAC2STR(ta), ++ iv32, iv16, key->u.tkip.iv32_rx[queue], ++ key->u.tkip.iv16_rx[queue]); ++#endif /* CONFIG_TKIP_DEBUG */ ++ return TKIP_DECRYPT_REPLAY; ++ } ++ ++ if (only_iv) { ++ res = TKIP_DECRYPT_OK; ++ goto done; ++ } ++ ++ if (!key->u.tkip.rx_initialized[queue] || ++ key->u.tkip.iv32_rx[queue] != iv32) { ++ key->u.tkip.rx_initialized[queue] = 1; ++ /* IV16 wrapped around - perform TKIP phase 1 */ ++ tkip_mixing_phase1(ta, &key->key[ALG_TKIP_TEMP_ENCR_KEY], ++ iv32, key->u.tkip.p1k_rx[queue]); ++#ifdef CONFIG_TKIP_DEBUG ++ { ++ int i; ++ printk(KERN_DEBUG "TKIP decrypt: Phase1 TA=" MACSTR ++ " TK=", MAC2STR(ta)); ++ for (i = 0; i < 16; i++) ++ printk("%02x ", ++ key->key[ALG_TKIP_TEMP_ENCR_KEY + i]); ++ printk("\n"); ++ printk(KERN_DEBUG "TKIP decrypt: P1K="); ++ for (i = 0; i < 5; i++) ++ printk("%04x ", key->u.tkip.p1k_rx[queue][i]); ++ printk("\n"); ++ } ++#endif /* CONFIG_TKIP_DEBUG */ ++ } ++ ++ tkip_mixing_phase2(key->u.tkip.p1k_rx[queue], ++ &key->key[ALG_TKIP_TEMP_ENCR_KEY], ++ iv16, rc4key); ++#ifdef CONFIG_TKIP_DEBUG ++ { ++ int i; ++ printk(KERN_DEBUG "TKIP decrypt: Phase2 rc4key="); ++ for (i = 0; i < 16; i++) ++ printk("%02x ", rc4key[i]); ++ printk("\n"); ++ } ++#endif /* CONFIG_TKIP_DEBUG */ ++ ++ res = ieee80211_wep_decrypt_data(rc4key, 16, pos, payload_len - 12); ++ done: ++ if (res == TKIP_DECRYPT_OK) { ++ /* FIX: these should be updated only after Michael MIC has been ++ * verified */ ++ /* Record previously received IV */ ++ key->u.tkip.iv32_rx[queue] = iv32; ++ key->u.tkip.iv16_rx[queue] = iv16; ++ } ++ ++ return res; ++} ++ ++ +diff -Nur linux-2.6.16/net/d80211/tkip.h linux-2.6.16-bcm43xx/net/d80211/tkip.h +--- linux-2.6.16/net/d80211/tkip.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.16-bcm43xx/net/d80211/tkip.h 2006-03-28 22:16:14.000000000 +0200 +@@ -0,0 +1,29 @@ ++/* ++ * Copyright 2002-2004, Instant802 Networks, Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#ifndef TKIP_H ++#define TKIP_H ++ ++#include <linux/types.h> ++#include "ieee80211_key.h" ++ ++u8 * ieee80211_tkip_add_iv(u8 *pos, struct ieee80211_key *key, ++ u8 iv0, u8 iv1, u8 iv2); ++void ieee80211_tkip_encrypt_data(struct ieee80211_key *key, u8 *pos, ++ size_t payload_len, u8 *ta); ++enum { ++ TKIP_DECRYPT_OK = 0, ++ TKIP_DECRYPT_NO_EXT_IV = -1, ++ TKIP_DECRYPT_INVALID_KEYIDX = -2, ++ TKIP_DECRYPT_REPLAY = -3, ++}; ++int ieee80211_tkip_decrypt_data(struct ieee80211_key *key, u8 *payload, ++ size_t payload_len, u8 *ta, int only_iv, ++ int queue); ++ ++#endif /* TKIP_H */ +diff -Nur linux-2.6.16/net/d80211/wep.c linux-2.6.16-bcm43xx/net/d80211/wep.c +--- linux-2.6.16/net/d80211/wep.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.16-bcm43xx/net/d80211/wep.c 2006-03-28 22:16:14.000000000 +0200 +@@ -0,0 +1,423 @@ ++/* ++ * Software WEP encryption implementation ++ * Copyright 2002, Jouni Malinen <jkmaline@cc.hut.fi> ++ * Copyright 2003, Instant802 Networks, Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include <linux/config.h> ++#include <linux/version.h> ++#include <linux/netdevice.h> ++#include <linux/types.h> ++#include <linux/random.h> ++#include <linux/compiler.h> ++ ++#include <net/d80211.h> ++#include "ieee80211_i.h" ++#include "wep.h" ++ ++ ++static const __u32 crc32_table[256] = { ++ 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, ++ 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L, ++ 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, ++ 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, ++ 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, ++ 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, ++ 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, ++ 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, ++ 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, ++ 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL, ++ 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, ++ 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, ++ 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L, ++ 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, ++ 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, ++ 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L, ++ 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, ++ 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L, ++ 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, ++ 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, ++ 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL, ++ 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, ++ 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L, ++ 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, ++ 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, ++ 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L, ++ 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L, ++ 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, ++ 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L, ++ 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL, ++ 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, ++ 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, ++ 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, ++ 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL, ++ 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, ++ 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, ++ 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL, ++ 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, ++ 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, ++ 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L, ++ 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL, ++ 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L, ++ 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, ++ 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, ++ 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L, ++ 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, ++ 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, ++ 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, ++ 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, ++ 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L, ++ 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, ++ 0x2d02ef8dL ++}; ++ ++ ++void ieee80211_wep_init(struct ieee80211_local *local) ++{ ++ /* start WEP IV from a random value */ ++ get_random_bytes(&local->wep_iv, WEP_IV_LEN); ++} ++ ++ ++static inline int ieee80211_wep_weak_iv(u32 iv, int keylen) ++{ ++ /* Fluhrer, Mantin, and Shamir have reported weaknesses in the ++ * key scheduling algorithm of RC4. At least IVs (KeyByte + 3, ++ * 0xff, N) can be used to speedup attacks, so avoid using them. */ ++ if ((iv & 0xff00) == 0xff00) { ++ u8 B = (iv >> 16) & 0xff; ++ if (B >= 3 && B < 3 + keylen) ++ return 1; ++ } ++ return 0; ++} ++ ++ ++void ieee80211_wep_get_iv(struct ieee80211_local *local, ++ struct ieee80211_key *key, u8 *iv) ++{ ++ local->wep_iv++; ++ if (ieee80211_wep_weak_iv(local->wep_iv, key->keylen)) ++ local->wep_iv += 0x0100; ++ ++ if (iv == NULL) ++ return; ++ ++ *iv++ = (local->wep_iv >> 16) & 0xff; ++ *iv++ = (local->wep_iv >> 8) & 0xff; ++ *iv++ = local->wep_iv & 0xff; ++ *iv++ = key->keyidx << 6; ++} ++ ++ ++u8 * ieee80211_wep_add_iv(struct ieee80211_local *local, ++ struct sk_buff *skb, ++ struct ieee80211_key *key) ++{ ++ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; ++ u16 fc; ++ int hdrlen; ++ u8 *newhdr; ++ ++ fc = le16_to_cpu(hdr->frame_control); ++ fc |= WLAN_FC_ISWEP; ++ hdr->frame_control = cpu_to_le16(fc); ++ ++ if ((skb_headroom(skb) < WEP_IV_LEN || ++ skb_tailroom(skb) < WEP_ICV_LEN)) { ++ I802_DEBUG_INC(local->tx_expand_skb_head); ++ if (unlikely(pskb_expand_head(skb, WEP_IV_LEN, WEP_ICV_LEN, ++ GFP_ATOMIC))) ++ return NULL; ++ } ++ ++ hdrlen = ieee80211_get_hdrlen(fc); ++ newhdr = skb_push(skb, WEP_IV_LEN); ++ memmove(newhdr, newhdr + WEP_IV_LEN, hdrlen); ++ ieee80211_wep_get_iv(local, key, newhdr + hdrlen); ++ return newhdr + hdrlen; ++} ++ ++ ++void ieee80211_wep_remove_iv(struct ieee80211_local *local, ++ struct sk_buff *skb, ++ struct ieee80211_key *key) ++{ ++ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; ++ u16 fc; ++ int hdrlen; ++ ++ fc = le16_to_cpu(hdr->frame_control); ++ hdrlen = ieee80211_get_hdrlen(fc); ++ memmove(skb->data + WEP_IV_LEN, skb->data, hdrlen); ++ skb_pull(skb, WEP_IV_LEN); ++} ++ ++ ++/* Perform WEP encryption using given key. data buffer must have tailroom ++ * for 4-byte ICV. data_len must not include this ICV. Note: this function ++ * does _not_ add IV. data = RC4(data | CRC32(data)) */ ++void ieee80211_wep_encrypt_data(u8 *rc4key, size_t klen, u8 *data, ++ size_t data_len) ++{ ++ u32 i, j, k, crc; ++ u8 S[256]; ++ u8 kpos, *pos; ++#define S_SWAP(a,b) do { u8 t = S[a]; S[a] = S[b]; S[b] = t; } while(0) ++ ++ /* Setup RC4 state */ ++ for (i = 0; i < 256; i++) ++ S[i] = i; ++ j = 0; ++ kpos = 0; ++ for (i = 0; i < 256; i++) { ++ j = (j + S[i] + rc4key[kpos]) & 0xff; ++ kpos++; ++ if (kpos >= klen) ++ kpos = 0; ++ S_SWAP(i, j); ++ } ++ ++ /* Compute CRC32 over unencrypted data and apply RC4 to data */ ++ pos = data; ++ crc = ~0; ++ i = j = 0; ++ for (k = 0; k < data_len; k++) { ++ crc = crc32_table[(crc ^ *pos) & 0xff] ^ (crc >> 8); ++ i = (i + 1) & 0xff; ++ j = (j + S[i]) & 0xff; ++ S_SWAP(i, j); ++ *pos++ ^= S[(S[i] + S[j]) & 0xff]; ++ } ++ crc = ~crc; ++ ++ /* Append little-endian CRC32 and encrypt it to produce ICV */ ++ pos[0] = crc; ++ pos[1] = crc >> 8; ++ pos[2] = crc >> 16; ++ pos[3] = crc >> 24; ++ for (k = 0; k < 4; k++) { ++ i = (i + 1) & 0xff; ++ j = (j + S[i]) & 0xff; ++ S_SWAP(i, j); ++ *pos++ ^= S[(S[i] + S[j]) & 0xff]; ++ } ++} ++ ++ ++/* Perform WEP encryption on given skb. 4 bytes of extra space (IV) in the ++ * beginning of the buffer 4 bytes of extra space (ICV) in the end of the ++ * buffer will be added. Both IV and ICV will be transmitted, so the ++ * payload length increases with 8 bytes. ++ * ++ * WEP frame payload: IV + TX key idx, RC4(data), ICV = RC4(CRC32(data)) ++ */ ++int ieee80211_wep_encrypt(struct ieee80211_local *local, struct sk_buff *skb, ++ struct ieee80211_key *key) ++{ ++ u32 klen; ++ u8 *rc4key, *iv; ++ size_t len; ++ ++ if (key == NULL || key->alg != ALG_WEP) ++ return -1; ++ ++ klen = 3 + key->keylen; ++ rc4key = kmalloc(klen, GFP_ATOMIC); ++ if (rc4key == NULL) ++ return -1; ++ ++ iv = ieee80211_wep_add_iv(local, skb, key); ++ if (iv == NULL) { ++ kfree(rc4key); ++ return -1; ++ } ++ ++ len = skb->len - (iv + WEP_IV_LEN - skb->data); ++ ++ /* Prepend 24-bit IV to RC4 key */ ++ memcpy(rc4key, iv, 3); ++ ++ /* Copy rest of the WEP key (the secret part) */ ++ memcpy(rc4key + 3, key->key, key->keylen); ++ ++ /* Add room for ICV */ ++ skb_put(skb, WEP_ICV_LEN); ++ ++ ieee80211_wep_encrypt_data(rc4key, klen, iv + WEP_IV_LEN, len); ++ ++ kfree(rc4key); ++ ++ return 0; ++} ++ ++ ++/* Perform WEP decryption using given key. data buffer includes encrypted ++ * payload, including 4-byte ICV, but _not_ IV. data_len must not include ICV. ++ * Return 0 on success and -1 on ICV mismatch. */ ++int ieee80211_wep_decrypt_data(u8 *rc4key, size_t klen, u8 *data, ++ size_t data_len) ++{ ++ u32 i, j, k, crc; ++ u8 S[256]; ++ u8 kpos, *pos, crcbuf[WEP_ICV_LEN], *cpos; ++ ++ /* Setup RC4 state */ ++ for (i = 0; i < 256; i++) ++ S[i] = i; ++ j = 0; ++ kpos = 0; ++ for (i = 0; i < 256; i++) { ++ j = (j + S[i] + rc4key[kpos]) & 0xff; ++ kpos++; ++ if (kpos >= klen) ++ kpos = 0; ++ S_SWAP(i, j); ++ } ++ ++ /* Apply RC4 to data and compute CRC32 over decrypted data */ ++ pos = data; ++ crc = ~0; ++ i = j = 0; ++ for (k = 0; k < data_len; k++) { ++ i = (i + 1) & 0xff; ++ j = (j + S[i]) & 0xff; ++ S_SWAP(i, j); ++ *pos ^= S[(S[i] + S[j]) & 0xff]; ++ crc = crc32_table[(crc ^ *pos++) & 0xff] ^ (crc >> 8); ++ } ++ crc = ~crc; ++ ++ /* Decrypt little-endian CRC32 and verify that it matches with the ++ * received ICV */ ++ cpos = crcbuf; ++ crcbuf[0] = crc; ++ crcbuf[1] = crc >> 8; ++ crcbuf[2] = crc >> 16; ++ crcbuf[3] = crc >> 24; ++ for (k = 0; k < WEP_ICV_LEN; k++) { ++ i = (i + 1) & 0xff; ++ j = (j + S[i]) & 0xff; ++ S_SWAP(i, j); ++ if (*cpos++ != (*pos++ ^ S[(S[i] + S[j]) & 0xff])) { ++ /* ICV mismatch */ ++ return -1; ++ } ++ } ++ ++ return 0; ++} ++ ++ ++/* Perform WEP decryption on given skb. Buffer includes whole WEP part of ++ * the frame: IV (4 bytes), encrypted payload (including SNAP header), ++ * ICV (4 bytes). skb->len includes both IV and ICV. ++ * ++ * Returns 0 if frame was decrypted successfully and ICV was correct and -1 on ++ * failure. If frame is OK, IV and ICV will be removed, i.e., decrypted payload ++ * is moved to the beginning of the skb and skb length will be reduced. ++ */ ++int ieee80211_wep_decrypt(struct ieee80211_local *local, struct sk_buff *skb, ++ struct ieee80211_key *key) ++{ ++ u32 klen; ++ u8 *rc4key; ++ u8 keyidx; ++ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; ++ u16 fc; ++ int hdrlen; ++ size_t len; ++ int ret = 0; ++ ++ fc = le16_to_cpu(hdr->frame_control); ++ if (!(fc & WLAN_FC_ISWEP)) ++ return -1; ++ ++ hdrlen = ieee80211_get_hdrlen(fc); ++ ++ if (skb->len < 8 + hdrlen) ++ return -1; ++ ++ len = skb->len - hdrlen - 8; ++ ++ keyidx = skb->data[hdrlen + 3] >> 6; ++ ++ if (key == NULL || keyidx != key->keyidx || key->alg != ALG_WEP) ++ return -1; ++ ++ klen = 3 + key->keylen; ++ ++ rc4key = kmalloc(klen, GFP_ATOMIC); ++ if (rc4key == NULL) ++ return -1; ++ ++ /* Prepend 24-bit IV to RC4 key */ ++ memcpy(rc4key, skb->data + hdrlen, 3); ++ ++ /* Copy rest of the WEP key (the secret part) */ ++ memcpy(rc4key + 3, key->key, key->keylen); ++ ++ if (ieee80211_wep_decrypt_data(rc4key, klen, ++ skb->data + hdrlen + WEP_IV_LEN, ++ len)) { ++ printk(KERN_DEBUG "WEP decrypt failed (ICV)\n"); ++ ret = -1; ++ } ++ ++ kfree(rc4key); ++ ++ /* Trim ICV */ ++ skb_trim(skb, skb->len - WEP_ICV_LEN); ++ ++ /* Remove IV */ ++ memmove(skb->data + WEP_IV_LEN, skb->data, hdrlen); ++ skb_pull(skb, WEP_IV_LEN); ++ ++ return ret; ++} ++ ++ ++int ieee80211_wep_get_keyidx(struct sk_buff *skb) ++{ ++ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; ++ u16 fc; ++ int hdrlen; ++ ++ fc = le16_to_cpu(hdr->frame_control); ++ if (!(fc & WLAN_FC_ISWEP)) ++ return -1; ++ ++ hdrlen = ieee80211_get_hdrlen(fc); ++ ++ if (skb->len < 8 + hdrlen) ++ return -1; ++ ++ return skb->data[hdrlen + 3] >> 6; ++} ++ ++ ++u8 * ieee80211_wep_is_weak_iv(struct sk_buff *skb, struct ieee80211_key *key) ++{ ++ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; ++ u16 fc; ++ int hdrlen; ++ u8 *ivpos; ++ u32 iv; ++ ++ fc = le16_to_cpu(hdr->frame_control); ++ if (!(fc & WLAN_FC_ISWEP)) ++ return NULL; ++ ++ hdrlen = ieee80211_get_hdrlen(fc); ++ ivpos = skb->data + hdrlen; ++ iv = (ivpos[0] << 16) | (ivpos[1] << 8) | ivpos[2]; ++ ++ if (ieee80211_wep_weak_iv(iv, key->keylen)) ++ return ivpos; ++ ++ return NULL; ++} +diff -Nur linux-2.6.16/net/d80211/wep.h linux-2.6.16-bcm43xx/net/d80211/wep.h +--- linux-2.6.16/net/d80211/wep.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.16-bcm43xx/net/d80211/wep.h 2006-03-28 22:16:14.000000000 +0200 +@@ -0,0 +1,39 @@ ++/* ++ * Software WEP encryption implementation ++ * Copyright 2002, Jouni Malinen <jkmaline@cc.hut.fi> ++ * Copyright 2003, Instant802 Networks, Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#ifndef WEP_H ++#define WEP_H ++ ++#include <linux/skbuff.h> ++#include <linux/types.h> ++#include "ieee80211_i.h" ++#include "ieee80211_key.h" ++ ++void ieee80211_wep_init(struct ieee80211_local *local); ++void ieee80211_wep_get_iv(struct ieee80211_local *local, ++ struct ieee80211_key *key, u8 *iv); ++u8 * ieee80211_wep_add_iv(struct ieee80211_local *local, ++ struct sk_buff *skb, ++ struct ieee80211_key *key); ++void ieee80211_wep_remove_iv(struct ieee80211_local *local, ++ struct sk_buff *skb, ++ struct ieee80211_key *key); ++void ieee80211_wep_encrypt_data(u8 *rc4key, size_t klen, u8 *data, ++ size_t data_len); ++int ieee80211_wep_decrypt_data(u8 *rc4key, size_t klen, u8 *data, ++ size_t data_len); ++int ieee80211_wep_encrypt(struct ieee80211_local *local, struct sk_buff *skb, ++ struct ieee80211_key *key); ++int ieee80211_wep_decrypt(struct ieee80211_local *local, struct sk_buff *skb, ++ struct ieee80211_key *key); ++int ieee80211_wep_get_keyidx(struct sk_buff *skb); ++u8 * ieee80211_wep_is_weak_iv(struct sk_buff *skb, struct ieee80211_key *key); ++ ++#endif /* WEP_H */ +diff -Nur linux-2.6.16/net/d80211/wme.c linux-2.6.16-bcm43xx/net/d80211/wme.c +--- linux-2.6.16/net/d80211/wme.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.16-bcm43xx/net/d80211/wme.c 2006-03-28 22:16:14.000000000 +0200 +@@ -0,0 +1,695 @@ ++/* ++ * Copyright 2004, Instant802 Networks, Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include <linux/config.h> ++#include <linux/version.h> ++#include <linux/netdevice.h> ++#include <linux/skbuff.h> ++#include <linux/module.h> ++#include <linux/if_arp.h> ++#include <net/ip.h> ++ ++#include <net/d80211.h> ++#include "ieee80211_i.h" ++#include "wme.h" ++ ++#define CHILD_QDISC_OPS pfifo_qdisc_ops ++ ++static inline int WLAN_FC_IS_QOS_DATA(u16 fc) ++{ ++ return (fc & 0x8C) == 0x88; ++} ++ ++ ++ieee80211_txrx_result ++ieee80211_rx_h_parse_qos(struct ieee80211_txrx_data *rx) ++{ ++ u8 *data = rx->skb->data; ++ int tid; ++ ++ /* does the frame have a qos control field? */ ++ if (WLAN_FC_IS_QOS_DATA(rx->fc)) { ++ u8 *qc = data + ieee80211_get_hdrlen(rx->fc) - QOS_CONTROL_LEN; ++ /* frame has qos control */ ++ tid = qc[0] & QOS_CONTROL_TID_MASK; ++ } else { ++ if (unlikely(WLAN_FC_GET_TYPE(rx->fc) == WLAN_FC_TYPE_MGMT)) { ++ /* Separate TID for management frames */ ++ tid = NUM_RX_DATA_QUEUES - 1; ++ } else { ++ /* no qos control present */ ++ tid = 0; /* 802.1d - Best Effort */ ++ } ++ } ++#ifdef CONFIG_D80211_DEBUG_COUNTERS ++ I802_DEBUG_INC(rx->local->wme_rx_queue[tid]); ++ if (rx->sta) { ++ I802_DEBUG_INC(rx->sta->wme_rx_queue[tid]); ++ } ++#endif /* CONFIG_D80211_DEBUG_COUNTERS */ ++ ++ rx->u.rx.queue = tid; ++ /* Set skb->priority to 1d tag if highest order bit of TID is not set. ++ * For now, set skb->priority to 0 for other cases. */ ++ rx->skb->priority = (tid > 7) ? 0 : tid; ++ ++ return TXRX_CONTINUE; ++} ++ ++ ++ieee80211_txrx_result ++ieee80211_rx_h_remove_qos_control(struct ieee80211_txrx_data *rx) ++{ ++ u16 fc = rx->fc; ++ u8 *data = rx->skb->data; ++ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) data; ++ ++ if (!WLAN_FC_IS_QOS_DATA(fc)) ++ return TXRX_CONTINUE; ++ ++ /* remove the qos control field, update frame type and meta-data */ ++ memmove(data + 2, data, ieee80211_get_hdrlen(fc) - 2); ++ hdr = (struct ieee80211_hdr *) skb_pull(rx->skb, 2); ++ /* change frame type to non QOS */ ++ rx->fc = fc &= ~(WLAN_FC_STYPE_QOS_DATA << 4); ++ hdr->frame_control = cpu_to_le16(fc); ++ ++ return TXRX_CONTINUE; ++} ++ ++ ++/* maximum number of hardware queues we support. */ ++#define TC_80211_MAX_QUEUES 8 ++ ++struct ieee80211_sched_data ++{ ++ struct tcf_proto *filter_list; ++ struct Qdisc *queues[TC_80211_MAX_QUEUES]; ++ struct sk_buff_head requeued[TC_80211_MAX_QUEUES]; ++}; ++ ++ ++/* given a data frame determine the 802.1p/1d tag to use */ ++static inline unsigned classify_1d(struct sk_buff *skb, struct Qdisc *qd) ++{ ++ struct iphdr *ip; ++ int dscp; ++ int offset; ++ ++#ifdef CONFIG_NET_SCHED ++ struct ieee80211_sched_data *q = qdisc_priv(qd); ++ struct tcf_result res = { -1, 0 }; ++ ++ /* if there is a user set filter list, call out to that */ ++ if (q->filter_list) { ++ tc_classify(skb, q->filter_list, &res); ++ if (res.class != -1) ++ return res.class; ++ } ++#endif /* CONFIG_NET_SCHED */ ++ ++ /* skb->priority values from 256->263 are magic values to ++ * directly indicate a specific 802.1d priority. ++ * This is used to allow 802.1d priority to be passed directly in ++ * from VLAN tags, etc. */ ++ if (skb->priority >= 256 && skb->priority <= 263) ++ return skb->priority - 256; ++ ++ /* check there is a valid IP header present */ ++ offset = ieee80211_get_hdrlen_from_skb(skb) + 8 /* LLC + proto */; ++ if (skb->protocol != __constant_htons(ETH_P_IP) || ++ skb->len < offset + sizeof(*ip)) ++ return 0; ++ ++ ip = (struct iphdr *) (skb->data + offset); ++ ++ dscp = ip->tos & 0xfc; ++ switch (dscp) { ++ case 0x20: ++ return 2; ++ case 0x40: ++ return 1; ++ case 0x60: ++ return 3; ++ case 0x80: ++ return 4; ++ case 0xa0: ++ return 5; ++ case 0xc0: ++ return 6; ++ case 0xe0: ++ return 7; ++ default: ++ return 0; ++ } ++} ++ ++ ++static inline int wme_downgrade_ac(struct sk_buff *skb) ++{ ++ switch (skb->priority) { ++ case 6: ++ case 7: ++ skb->priority = 5; /* VO -> VI */ ++ return 0; ++ case 4: ++ case 5: ++ skb->priority = 3; /* VI -> BE */ ++ return 0; ++ case 0: ++ case 3: ++ skb->priority = 2; /* BE -> BK */ ++ return 0; ++ default: ++ return -1; ++ } ++} ++ ++ ++/* positive return value indicates which queue to use ++ * negative return value indicates to drop the frame */ ++static inline int classify80211(struct sk_buff *skb, struct Qdisc *qd) ++{ ++ struct ieee80211_local *local = qd->dev->priv; ++ struct ieee80211_tx_packet_data *pkt_data = ++ (struct ieee80211_tx_packet_data *) skb->cb; ++ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; ++ unsigned short fc = le16_to_cpu(hdr->frame_control); ++ int qos; ++ const int ieee802_1d_to_ac[8] = { 2, 3, 3, 2, 1, 1, 0, 0 }; ++ ++ /* see if frame is data or non data frame */ ++ if (unlikely(WLAN_FC_GET_TYPE(fc) != WLAN_FC_TYPE_DATA)) { ++ /* management frames go on AC_VO queue, but are sent ++ * without QoS control fields */ ++ return IEEE80211_TX_QUEUE_DATA0; ++ } ++ ++ if (unlikely(pkt_data->sdata->type == IEEE80211_SUB_IF_TYPE_MGMT)) { ++ /* Data frames from hostapd (mainly, EAPOL) use AC_VO ++ * and they will include QoS control fields if ++ * the target STA is using WME. */ ++ skb->priority = 7; ++ return ieee802_1d_to_ac[skb->priority]; ++ } ++ ++ /* is this a QoS frame? */ ++ qos = fc & (WLAN_FC_STYPE_QOS_DATA << 4); ++ ++ if (!qos) { ++ skb->priority = 0; /* required for correct WPA/11i MIC */ ++ return ieee802_1d_to_ac[skb->priority]; ++ } ++ ++ /* use the data classifier to determine what 802.1d tag the ++ * data frame has */ ++ skb->priority = classify_1d(skb, qd); ++ ++ /* incase we are a client verify acm is not set for this ac */ ++ for (; unlikely(local->wmm_acm & BIT(skb->priority)); ) ++ { ++ if (wme_downgrade_ac(skb)) { ++ /* No AC with lower priority has acm=0, ++ * drop packet. */ ++ return -1; ++ } ++ } ++ ++ /* look up which queue to use for frames with this 1d tag */ ++ return ieee802_1d_to_ac[skb->priority]; ++} ++ ++ ++static int wme_qdiscop_enqueue(struct sk_buff *skb, struct Qdisc* qd) ++{ ++ struct ieee80211_local *local = qd->dev->priv; ++ struct ieee80211_sched_data *q = qdisc_priv(qd); ++ struct ieee80211_tx_packet_data *pkt_data = ++ (struct ieee80211_tx_packet_data *) skb->cb; ++ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; ++ unsigned short fc = le16_to_cpu(hdr->frame_control); ++ struct Qdisc *qdisc; ++ int err, queue; ++ ++ if (pkt_data->requeue) { ++ skb_queue_tail(&q->requeued[pkt_data->queue], skb); ++ return 0; ++ } ++ ++ queue = classify80211(skb, qd); ++ ++ /* now we know the 1d priority, fill in the QoS header if there is one ++ */ ++ if (WLAN_FC_IS_QOS_DATA(fc)) { ++ struct qos_control *qc = (struct qos_control *) ++ (skb->data + ieee80211_get_hdrlen(fc) - 2); ++ u8 *p = (u8 *) qc; ++ *p++ = 0; /* do this due to gcc's lack of optimization on ++ * bitfield ops */ ++ *p = 0; ++ qc->tag1d = skb->priority; ++ if (local->wifi_wme_noack_test) ++ qc->ack_policy = 1; ++ } ++ ++ if (unlikely(queue >= local->hw->queues)) { ++#if 0 ++ if (net_ratelimit()) { ++ printk(KERN_DEBUG "%s - queue=%d (hw does not " ++ "support) -> %d\n", ++ __func__, queue, local->hw->queues - 1); ++ } ++#endif ++ queue = local->hw->queues - 1; ++ } ++ ++ if (unlikely(queue < 0)) { ++ kfree_skb(skb); ++ err = NET_XMIT_DROP; ++ } else { ++ pkt_data->queue = (unsigned int) queue; ++ qdisc = q->queues[queue]; ++ err = qdisc->enqueue(skb, qdisc); ++ if (err == NET_XMIT_SUCCESS) { ++ qd->q.qlen++; ++ qd->bstats.bytes += skb->len; ++ qd->bstats.packets++; ++ return NET_XMIT_SUCCESS; ++ } ++ } ++ qd->qstats.drops++; ++ return err; ++} ++ ++ ++/* TODO: clean up the cases where master_hard_start_xmit ++ * returns non 0 - it shouldn't ever do that. Once done we ++ * can remove this function */ ++static int wme_qdiscop_requeue(struct sk_buff *skb, struct Qdisc* qd) ++{ ++ struct ieee80211_sched_data *q = qdisc_priv(qd); ++ struct ieee80211_tx_packet_data *pkt_data = ++ (struct ieee80211_tx_packet_data *) skb->cb; ++ struct Qdisc *qdisc; ++ int err; ++ ++ /* we recorded which queue to use earlier! */ ++ qdisc = q->queues[pkt_data->queue]; ++ ++ if ((err = qdisc->ops->requeue(skb, qdisc)) == 0) { ++ qd->q.qlen++; ++ return 0; ++ } ++ qd->qstats.drops++; ++ return err; ++} ++ ++ ++static struct sk_buff *wme_qdiscop_dequeue(struct Qdisc* qd) ++{ ++ struct ieee80211_sched_data *q = qdisc_priv(qd); ++ struct net_device *dev = qd->dev; ++ struct ieee80211_local *local = dev->priv; ++ struct ieee80211_hw *hw = local->hw; ++ struct ieee80211_tx_queue_stats stats; ++ struct sk_buff *skb; ++ struct Qdisc *qdisc; ++ int queue; ++ ++ /* find which hardware queues have space in them */ ++ hw->get_tx_stats(dev, &stats); ++ ++ /* check all the h/w queues in numeric/priority order */ ++ for (queue = 0; queue < hw->queues; queue++) { ++ /* see if there is room in this hardware queue */ ++ if (stats.data[queue].len >= stats.data[queue].limit) ++ continue; ++ ++ /* there is space - try and get a frame */ ++ skb = skb_dequeue(&q->requeued[queue]); ++ if (skb) ++ return skb; ++ ++ qdisc = q->queues[queue]; ++ skb = qdisc->dequeue(qdisc); ++ if (skb) { ++ qd->q.qlen--; ++ return skb; ++ } ++ } ++ /* returning a NULL here when all the h/w queues are full means we ++ * never need to call netif_stop_queue in the driver */ ++ return NULL; ++} ++ ++ ++static void wme_qdiscop_reset(struct Qdisc* qd) ++{ ++ struct ieee80211_sched_data *q = qdisc_priv(qd); ++ struct ieee80211_local *local = qd->dev->priv; ++ struct ieee80211_hw *hw = local->hw; ++ int queue; ++ ++ /* QUESTION: should we have some hardware flush functionality here? */ ++ ++ for (queue = 0; queue < hw->queues; queue++) { ++ skb_queue_purge(&q->requeued[queue]); ++ qdisc_reset(q->queues[queue]); ++ } ++ qd->q.qlen = 0; ++} ++ ++ ++static void wme_qdiscop_destroy(struct Qdisc* qd) ++{ ++ struct ieee80211_sched_data *q = qdisc_priv(qd); ++ struct ieee80211_local *local = qd->dev->priv; ++ struct ieee80211_hw *hw = local->hw; ++ struct tcf_proto *tp; ++ int queue; ++ ++ while ((tp = q->filter_list) != NULL) { ++ q->filter_list = tp->next; ++ tp->ops->destroy(tp); ++ } ++ ++ for (queue=0; queue < hw->queues; queue++) { ++ skb_queue_purge(&q->requeued[queue]); ++ qdisc_destroy(q->queues[queue]); ++ q->queues[queue] = &noop_qdisc; ++ } ++} ++ ++ ++/* called whenever parameters are updated on existing qdisc */ ++static int wme_qdiscop_tune(struct Qdisc *qd, struct rtattr *opt) ++{ ++/* struct ieee80211_sched_data *q = qdisc_priv(qd); ++*/ ++ /* check our options block is the right size */ ++ /* copy any options to our local structure */ ++/* Ignore options block for now - always use static mapping ++ struct tc_ieee80211_qopt *qopt = RTA_DATA(opt); ++ ++ if (opt->rta_len < RTA_LENGTH(sizeof(*qopt))) ++ return -EINVAL; ++ memcpy(q->tag2queue, qopt->tag2queue, sizeof(qopt->tag2queue)); ++*/ ++ return 0; ++} ++ ++ ++/* called during initial creation of qdisc on device */ ++static int wme_qdiscop_init(struct Qdisc *qd, struct rtattr *opt) ++{ ++ struct ieee80211_sched_data *q = qdisc_priv(qd); ++ struct net_device *dev = qd->dev; ++ struct ieee80211_local *local = dev->priv; ++ int queues = local->hw->queues; ++ int err = 0, i; ++ ++ /* check this device is an ieee80211 master type device */ ++ if (dev->type != ARPHRD_IEEE80211) ++ return -EINVAL; ++ ++ /* check that there is no qdisc currently attached to device ++ * this ensures that we will be the root qdisc. (I can't find a better ++ * way to test this explicitly) */ ++ if (dev->qdisc_sleeping != &noop_qdisc) ++ return -EINVAL; ++ ++ if (qd->flags & TCQ_F_INGRESS) ++ return -EINVAL; ++ ++ /* if options were passed in, set them */ ++ if (opt) { ++ err = wme_qdiscop_tune(qd, opt); ++ } ++ ++ /* create child queues */ ++ for (i = 0; i < queues; i++) { ++ skb_queue_head_init(&q->requeued[i]); ++ q->queues[i] = qdisc_create_dflt(qd->dev, &CHILD_QDISC_OPS); ++ if (q->queues[i] == 0) { ++ q->queues[i] = &noop_qdisc; ++ printk(KERN_ERR "%s child qdisc %i creation failed", dev->name, i); ++ } ++ } ++ ++ return err; ++} ++ ++static int wme_qdiscop_dump(struct Qdisc *qd, struct sk_buff *skb) ++{ ++/* struct ieee80211_sched_data *q = qdisc_priv(qd); ++ unsigned char *p = skb->tail; ++ struct tc_ieee80211_qopt opt; ++ ++ memcpy(&opt.tag2queue, q->tag2queue, TC_80211_MAX_TAG + 1); ++ RTA_PUT(skb, TCA_OPTIONS, sizeof(opt), &opt); ++*/ return skb->len; ++/* ++rtattr_failure: ++ skb_trim(skb, p - skb->data);*/ ++ return -1; ++} ++ ++ ++static int wme_classop_graft(struct Qdisc *qd, unsigned long arg, ++ struct Qdisc *new, struct Qdisc **old) ++{ ++ struct ieee80211_sched_data *q = qdisc_priv(qd); ++ struct ieee80211_local *local = qd->dev->priv; ++ struct ieee80211_hw *hw = local->hw; ++ unsigned long queue = arg - 1; ++ ++ if (queue >= hw->queues) ++ return -EINVAL; ++ ++ if (new == NULL) ++ new = &noop_qdisc; ++ ++ sch_tree_lock(qd); ++ *old = q->queues[queue]; ++ q->queues[queue] = new; ++ qdisc_reset(*old); ++ sch_tree_unlock(qd); ++ ++ return 0; ++} ++ ++ ++static struct Qdisc * ++wme_classop_leaf(struct Qdisc *qd, unsigned long arg) ++{ ++ struct ieee80211_sched_data *q = qdisc_priv(qd); ++ struct ieee80211_local *local = qd->dev->priv; ++ struct ieee80211_hw *hw = local->hw; ++ unsigned long queue = arg - 1; ++ ++ if (queue >= hw->queues) ++ return NULL; ++ ++ return q->queues[queue]; ++} ++ ++ ++static unsigned long wme_classop_get(struct Qdisc *qd, u32 classid) ++{ ++ struct ieee80211_local *local = qd->dev->priv; ++ struct ieee80211_hw *hw = local->hw; ++ unsigned long queue = TC_H_MIN(classid); ++ ++ if (queue - 1 >= hw->queues) ++ return 0; ++ ++ return queue; ++} ++ ++ ++static unsigned long wme_classop_bind(struct Qdisc *qd, unsigned long parent, ++ u32 classid) ++{ ++ return wme_classop_get(qd, classid); ++} ++ ++ ++static void wme_classop_put(struct Qdisc *q, unsigned long cl) ++{ ++ /* printk(KERN_DEBUG "entering %s\n", __func__); */ ++} ++ ++ ++static int wme_classop_change(struct Qdisc *qd, u32 handle, u32 parent, ++ struct rtattr **tca, unsigned long *arg) ++{ ++ unsigned long cl = *arg; ++ struct ieee80211_local *local = qd->dev->priv; ++ struct ieee80211_hw *hw = local->hw; ++ /* printk(KERN_DEBUG "entering %s\n", __func__); */ ++ ++ if (cl - 1 > hw->queues) ++ return -ENOENT; ++ ++ /* TODO: put code to program hardware queue parameters here, ++ * to allow programming from tc command line */ ++ ++ return 0; ++} ++ ++ ++/* we don't support deleting hardware queues ++ * when we add WMM-SA support - TSPECs may be deleted here */ ++static int wme_classop_delete(struct Qdisc *qd, unsigned long cl) ++{ ++ struct ieee80211_local *local = qd->dev->priv; ++ struct ieee80211_hw *hw = local->hw; ++ /* printk(KERN_DEBUG "entering %s\n", __func__); */ ++ ++ if (cl - 1 > hw->queues) ++ return -ENOENT; ++ return 0; ++} ++ ++ ++static int wme_classop_dump_class(struct Qdisc *qd, unsigned long cl, ++ struct sk_buff *skb, struct tcmsg *tcm) ++{ ++ struct ieee80211_sched_data *q = qdisc_priv(qd); ++ struct ieee80211_local *local = qd->dev->priv; ++ struct ieee80211_hw *hw = local->hw; ++ /* printk(KERN_DEBUG "entering %s\n", __func__); */ ++ ++ if (cl - 1 > hw->queues) ++ return -ENOENT; ++ tcm->tcm_handle = TC_H_MIN(cl); ++ tcm->tcm_parent = qd->handle; ++ tcm->tcm_info = q->queues[cl-1]->handle; /* do we need this? */ ++ return 0; ++} ++ ++ ++static void wme_classop_walk(struct Qdisc *qd, struct qdisc_walker *arg) ++{ ++ struct ieee80211_local *local = qd->dev->priv; ++ struct ieee80211_hw *hw = local->hw; ++ int queue; ++ /* printk(KERN_DEBUG "entering %s\n", __func__); */ ++ ++ if (arg->stop) ++ return; ++ ++ for (queue = 0; queue < hw->queues; queue++) { ++ if (arg->count < arg->skip) { ++ arg->count++; ++ continue; ++ } ++ /* we should return classids for our internal queues here ++ * as well as the external ones */ ++ if (arg->fn(qd, queue+1, arg) < 0) { ++ arg->stop = 1; ++ break; ++ } ++ arg->count++; ++ } ++} ++ ++ ++static struct tcf_proto ** wme_classop_find_tcf(struct Qdisc *qd, ++ unsigned long cl) ++{ ++ struct ieee80211_sched_data *q = qdisc_priv(qd); ++ /* printk("entering %s\n", __func__); */ ++ ++ if (cl) ++ return NULL; ++ ++ return &q->filter_list; ++} ++ ++ ++/* this qdisc is classful (i.e. has classes, some of which may have leaf qdiscs attached) ++ * - these are the operations on the classes */ ++static struct Qdisc_class_ops class_ops = ++{ ++ .graft = wme_classop_graft, ++ .leaf = wme_classop_leaf, ++ ++ .get = wme_classop_get, ++ .put = wme_classop_put, ++ .change = wme_classop_change, ++ .delete = wme_classop_delete, ++ .walk = wme_classop_walk, ++ ++ .tcf_chain = wme_classop_find_tcf, ++ .bind_tcf = wme_classop_bind, ++ .unbind_tcf = wme_classop_put, ++ ++ .dump = wme_classop_dump_class, ++}; ++ ++ ++/* queueing discipline operations */ ++static struct Qdisc_ops wme_qdisc_ops = ++{ ++ .next = NULL, ++ .cl_ops = &class_ops, ++ .id = "ieee80211", ++ .priv_size = sizeof(struct ieee80211_sched_data), ++ ++ .enqueue = wme_qdiscop_enqueue, ++ .dequeue = wme_qdiscop_dequeue, ++ .requeue = wme_qdiscop_requeue, ++ .drop = NULL, /* drop not needed since we are always the root qdisc */ ++ ++ .init = wme_qdiscop_init, ++ .reset = wme_qdiscop_reset, ++ .destroy = wme_qdiscop_destroy, ++ .change = wme_qdiscop_tune, ++ ++ .dump = wme_qdiscop_dump, ++}; ++ ++ ++void ieee80211_install_qdisc(struct net_device *dev) ++{ ++ struct Qdisc *qdisc; ++ ++ qdisc = qdisc_create_dflt(dev, &wme_qdisc_ops); ++ if (qdisc == NULL) { ++ printk(KERN_ERR "%s: qdisc installation failed\n", dev->name); ++ return; ++ } ++ ++ /* same handle as would be allocated by qdisc_alloc_handle() */ ++ qdisc->handle = 0x80010000; ++ ++ qdisc_lock_tree(dev); ++ list_add_tail(&qdisc->list, &dev->qdisc_list); ++ dev->qdisc_sleeping = qdisc; ++ qdisc_unlock_tree(dev); ++} ++ ++ ++int ieee80211_wme_register(void) ++{ ++ int err = 0; ++ ++#ifdef CONFIG_NET_SCHED ++ err = register_qdisc(&wme_qdisc_ops); ++#endif ++ return err; ++} ++ ++ ++void ieee80211_wme_unregister(void) ++{ ++#ifdef CONFIG_NET_SCHED ++ unregister_qdisc(&wme_qdisc_ops); ++#endif ++} +diff -Nur linux-2.6.16/net/d80211/wme.h linux-2.6.16-bcm43xx/net/d80211/wme.h +--- linux-2.6.16/net/d80211/wme.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.16-bcm43xx/net/d80211/wme.h 2006-03-28 22:16:14.000000000 +0200 +@@ -0,0 +1,59 @@ ++/* ++ * IEEE 802.11 driver (80211.o) - QoS datatypes ++ * Copyright 2004, Instant802 Networks, Inc. ++ * Copyright 2005, Devicescape Software, Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#ifndef _WME_H ++#define _WME_H ++ ++#include <asm/byteorder.h> ++#include <linux/netdevice.h> ++#include <linux/types.h> ++#include <net/pkt_sched.h> ++ ++#define QOS_CONTROL_LEN 2 ++ ++#define QOS_CONTROL_ACK_POLICY_NORMAL 0 ++#define QOS_CONTROL_ACK_POLICY_NOACK 1 ++ ++#define QOS_CONTROL_TID_MASK 0x0f ++#define QOS_CONTROL_ACK_POLICY_SHIFT 5 ++ ++/* This bit field structure should not be used; it can cause compiler to ++ * generate unaligned accesses and inefficient code. */ ++struct qos_control { ++#if defined(__LITTLE_ENDIAN_BITFIELD) ++ u8 tag1d:3, /* bits 0-2 */ ++ reserved1:1, ++ eosp:1, ++ ack_policy:2, ++ reserved2:1; ++#elif defined (__BIG_ENDIAN_BITFIELD) ++ u8 reserved2:1, ++ ack_policy:2, ++ eosp:1, ++ reserved1:1, ++ tag1d:3; /* bits 0-2 */ ++#else ++#error "Please fix <asm/byteorder.h>" ++#endif ++ u8 reserved; ++} __attribute__ ((packed)); ++ ++ieee80211_txrx_result ++ieee80211_rx_h_parse_qos(struct ieee80211_txrx_data *rx); ++ ++ieee80211_txrx_result ++ieee80211_rx_h_remove_qos_control(struct ieee80211_txrx_data *rx); ++ ++void ieee80211_install_qdisc(struct net_device *dev); ++ ++int ieee80211_wme_register(void); ++void ieee80211_wme_unregister(void); ++ ++#endif +diff -Nur linux-2.6.16/net/d80211/wpa.c linux-2.6.16-bcm43xx/net/d80211/wpa.c +--- linux-2.6.16/net/d80211/wpa.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.16-bcm43xx/net/d80211/wpa.c 2006-03-28 22:16:14.000000000 +0200 +@@ -0,0 +1,825 @@ ++/* ++ * Copyright 2002-2004, Instant802 Networks, Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include <linux/config.h> ++#include <linux/version.h> ++#include <linux/module.h> ++#include <linux/netdevice.h> ++#include <linux/types.h> ++#include <linux/slab.h> ++#include <linux/skbuff.h> ++#include <linux/compiler.h> ++#include <linux/wireless.h> ++#include <net/iw_handler.h> ++ ++#include <net/d80211.h> ++#include <net/d80211_common.h> ++#include "ieee80211_i.h" ++#include "michael.h" ++#include "tkip.h" ++#include "aes_ccm.h" ++#include "wpa.h" ++#ifdef CONFIG_HOSTAPD_WPA_TESTING ++#include "hostapd_ioctl.h" ++#endif /* CONFIG_HOSTAPD_WPA_TESTING */ ++ ++ ++#define MICHAEL_MIC_HWACCEL ++ ++ ++int ieee80211_get_hdr_info(const struct sk_buff *skb, u8 **sa, u8 **da, ++ u8 *qos_tid, u8 **data, size_t *data_len) ++{ ++ struct ieee80211_hdr *hdr; ++ size_t hdrlen; ++ u16 fc; ++ int a4_included; ++ u8 *pos; ++ ++ hdr = (struct ieee80211_hdr *) skb->data; ++ fc = le16_to_cpu(hdr->frame_control); ++ ++ hdrlen = 24; ++ if ((fc & (WLAN_FC_FROMDS | WLAN_FC_TODS)) == ++ (WLAN_FC_FROMDS | WLAN_FC_TODS)) { ++ hdrlen += ETH_ALEN; ++ *sa = hdr->addr4; ++ *da = hdr->addr3; ++ } else if (fc & WLAN_FC_FROMDS) { ++ *sa = hdr->addr3; ++ *da = hdr->addr1; ++ } else if (fc & WLAN_FC_TODS) { ++ *sa = hdr->addr2; ++ *da = hdr->addr3; ++ } else { ++ *sa = hdr->addr2; ++ *da = hdr->addr1; ++ } ++ ++ if (fc & 0x80) ++ hdrlen += 2; ++ ++ *data = skb->data + hdrlen; ++ *data_len = skb->len - hdrlen; ++ ++ a4_included = (fc & (WLAN_FC_TODS | WLAN_FC_FROMDS)) == ++ (WLAN_FC_TODS | WLAN_FC_FROMDS); ++ if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_DATA && ++ WLAN_FC_GET_STYPE(fc) & 0x08) { ++ pos = (u8 *) &hdr->addr4; ++ if (a4_included) ++ pos += 6; ++ *qos_tid = pos[0] & 0x0f; ++ *qos_tid |= 0x80; /* qos_included flag */ ++ } else ++ *qos_tid = 0; ++ ++ return skb->len < hdrlen ? -1 : 0; ++} ++ ++ ++ieee80211_txrx_result ++ieee80211_tx_h_michael_mic_add(struct ieee80211_txrx_data *tx) ++{ ++ u8 *data, *sa, *da, *key, *mic, qos_tid; ++ size_t data_len; ++ u16 fc; ++ struct sk_buff *skb = tx->skb; ++ int authenticator; ++#if defined(CONFIG_HOSTAPD_WPA_TESTING) || defined(MICHAEL_MIC_HWACCEL) ++ int wpa_test = 0; ++#endif ++ ++ fc = tx->fc; ++ ++ if (!tx->key || tx->key->alg != ALG_TKIP || skb->len < 24 || ++ !WLAN_FC_DATA_PRESENT(fc)) ++ return TXRX_CONTINUE; ++ ++ if (ieee80211_get_hdr_info(skb, &sa, &da, &qos_tid, &data, &data_len)) ++ return TXRX_DROP; ++ ++#ifdef CONFIG_HOSTAPD_WPA_TESTING ++ if ((tx->sta && tx->sta->wpa_trigger & WPA_TRIGGER_FAIL_TX_MIC) || ++ (!tx->u.tx.unicast && ++ tx->local->wpa_trigger & WPA_TRIGGER_FAIL_TX_MIC)) { ++ wpa_test = 1; ++ } ++#endif /* CONFIG_HOSTAPD_WPA_TESTING */ ++ ++#ifdef MICHAEL_MIC_HWACCEL ++ if (!tx->key->force_sw_encrypt && !tx->local->conf.sw_decrypt && ++ !tx->fragmented && !wpa_test) { ++ /* hwaccel - with no need for preallocated room for Michael MIC ++ */ ++ return TXRX_CONTINUE; ++ } ++#endif /* MICHAEL_MIC_HWACCEL */ ++ ++ if (skb_tailroom(skb) < MICHAEL_MIC_LEN) { ++ I802_DEBUG_INC(tx->local->tx_expand_skb_head); ++ if (unlikely(pskb_expand_head(skb, TKIP_IV_LEN, ++ MICHAEL_MIC_LEN + TKIP_ICV_LEN, ++ GFP_ATOMIC))) { ++ printk(KERN_DEBUG "%s: failed to allocate more memory " ++ "for Michael MIC\n", tx->dev->name); ++ return TXRX_DROP; ++ } ++ } ++ ++#if 0 ++ authenticator = fc & WLAN_FC_FROMDS; /* FIX */ ++#else ++ authenticator = 1; ++#endif ++ key = &tx->key->key[authenticator ? ALG_TKIP_TEMP_AUTH_TX_MIC_KEY : ++ ALG_TKIP_TEMP_AUTH_RX_MIC_KEY]; ++ mic = skb_put(skb, MICHAEL_MIC_LEN); ++ michael_mic(key, da, sa, qos_tid & 0x0f, data, data_len, mic); ++ ++#ifdef CONFIG_HOSTAPD_WPA_TESTING ++ if (tx->sta && tx->sta->wpa_trigger & WPA_TRIGGER_FAIL_TX_MIC) { ++ printk(KERN_INFO "%s: WPA testing - corrupting TX Michael MIC " ++ "for STA " MACSTR "\n", ++ tx->dev->name, MAC2STR(tx->sta->addr)); ++ tx->u.tx.control->key_idx = HW_KEY_IDX_INVALID; ++ tx->sta->wpa_trigger &= ~WPA_TRIGGER_FAIL_TX_MIC; ++ tx->wpa_test = 1; ++ mic[0]++; ++ } else if (!tx->u.tx.unicast && ++ tx->local->wpa_trigger & WPA_TRIGGER_FAIL_TX_MIC) { ++ printk(KERN_INFO "%s: WPA testing - corrupting TX Michael MIC " ++ "for Group Key\n", tx->dev->name); ++ tx->u.tx.control->key_idx = HW_KEY_IDX_INVALID; ++ tx->local->wpa_trigger &= ~WPA_TRIGGER_FAIL_TX_MIC; ++ tx->wpa_test = 1; ++ mic[0]++; ++ } ++#endif /* CONFIG_HOSTAPD_WPA_TESTING */ ++ ++ return TXRX_CONTINUE; ++} ++ ++ ++ieee80211_txrx_result ++ieee80211_rx_h_michael_mic_verify(struct ieee80211_txrx_data *rx) ++{ ++ u8 *data, *sa, *da, *key = NULL, qos_tid; ++ size_t data_len; ++ u16 fc; ++ u8 mic[MICHAEL_MIC_LEN]; ++ struct sk_buff *skb = rx->skb; ++ int authenticator = 1, wpa_test = 0; ++ ++ fc = rx->fc; ++ ++ /* If device handles decryption totally, skip this check */ ++ if (rx->local->hw->device_hides_wep || ++ rx->local->hw->device_strips_mic) ++ return TXRX_CONTINUE; ++ ++ if (!rx->key || rx->key->alg != ALG_TKIP || ++ !(rx->fc & WLAN_FC_ISWEP) || !WLAN_FC_DATA_PRESENT(fc)) ++ return TXRX_CONTINUE; ++ ++#ifdef CONFIG_HOSTAPD_WPA_TESTING ++ if (rx->sta && rx->sta->wpa_trigger & WPA_TRIGGER_FAIL_RX_MIC) { ++ wpa_test = 1; ++ } ++#endif /* CONFIG_HOSTAPD_WPA_TESTING */ ++ ++#ifdef MICHAEL_MIC_HWACCEL ++ if ((rx->u.rx.status->flag & RX_FLAG_DECRYPTED) && ++ !rx->key->force_sw_encrypt && !rx->local->conf.sw_decrypt) { ++ if (rx->local->hw->wep_include_iv) { ++ if (skb->len < MICHAEL_MIC_LEN) ++ return TXRX_DROP; ++ } ++ /* Need to verify Michael MIC sometimes in software even when ++ * hwaccel is used. Atheros ar5212: fragmented frames and QoS ++ * frames. */ ++ if (!rx->fragmented && !wpa_test) ++ goto remove_mic; ++ } ++#endif /* MICHAEL_MIC_HWACCEL */ ++ ++ if (ieee80211_get_hdr_info(skb, &sa, &da, &qos_tid, &data, &data_len) ++ || data_len < MICHAEL_MIC_LEN) ++ return TXRX_DROP; ++ ++ data_len -= MICHAEL_MIC_LEN; ++ ++#if 0 ++ authenticator = fc & WLAN_FC_TODS; /* FIX */ ++#else ++ authenticator = 1; ++#endif ++ key = &rx->key->key[authenticator ? ALG_TKIP_TEMP_AUTH_RX_MIC_KEY : ++ ALG_TKIP_TEMP_AUTH_TX_MIC_KEY]; ++ michael_mic(key, da, sa, qos_tid & 0x0f, data, data_len, mic); ++#ifdef CONFIG_HOSTAPD_WPA_TESTING ++ if (rx->sta && rx->sta->wpa_trigger & WPA_TRIGGER_FAIL_RX_MIC) { ++ printk(KERN_INFO "%s: WPA testing - corrupting RX Michael MIC " ++ "for STA " MACSTR "\n", ++ rx->dev->name, MAC2STR(rx->sta->addr)); ++ rx->sta->wpa_trigger &= ~WPA_TRIGGER_FAIL_RX_MIC; ++ mic[0]++; ++ } ++#endif /* CONFIG_HOSTAPD_WPA_TESTING */ ++ if (memcmp(mic, data + data_len, MICHAEL_MIC_LEN) != 0 || wpa_test) { ++#ifdef CONFIG_HOSTAPD_WPA_TESTING ++ int i; ++#endif /* CONFIG_HOSTAPD_WPA_TESTING */ ++ printk(KERN_DEBUG "%s: invalid Michael MIC in data frame from " ++ MACSTR "\n", rx->dev->name, MAC2STR(sa)); ++#ifdef CONFIG_HOSTAPD_WPA_TESTING ++ printk(KERN_DEBUG " received"); ++ for (i = 0; i < MICHAEL_MIC_LEN; i++) ++ printk(" %02x", data[data_len + i]); ++ printk(" expected"); ++ for (i = 0; i < MICHAEL_MIC_LEN; i++) ++ printk(" %02x", mic[i]); ++ printk("\n"); ++ printk(KERN_DEBUG " SA=" MACSTR " DA=" MACSTR " key", ++ MAC2STR(sa), MAC2STR(da)); ++ for (i = 0; i < 8; i++) ++ printk(" %02x", key[i]); ++ printk(" (%d)\n", authenticator); ++#endif /* CONFIG_HOSTAPD_WPA_TESTING */ ++ ++ do { ++ struct ieee80211_hdr *hdr; ++ union iwreq_data wrqu; ++ char *buf = kmalloc(128, GFP_ATOMIC); ++ if (buf == NULL) ++ break; ++ ++ /* TODO: needed parameters: count, key type, TSC */ ++ hdr = (struct ieee80211_hdr *) skb->data; ++ sprintf(buf, "MLME-MICHAELMICFAILURE.indication(" ++ "keyid=%d %scast addr=" MACSTR ")", ++ rx->key->keyidx, ++ hdr->addr1[0] & 0x01 ? "broad" : "uni", ++ MAC2STR(hdr->addr2)); ++ memset(&wrqu, 0, sizeof(wrqu)); ++ wrqu.data.length = strlen(buf); ++ wireless_send_event(rx->dev, IWEVCUSTOM, &wrqu, buf); ++ kfree(buf); ++ } while (0); ++ ++ ieee80211_rx_mgmt(rx->dev, rx->skb, rx->u.rx.status, ++ ieee80211_msg_michael_mic_failure); ++ ++ return TXRX_QUEUED; ++ } ++ ++#ifdef MICHAEL_MIC_HWACCEL ++ remove_mic: ++#endif /* MICHAEL_MIC_HWACCEL */ ++ /* remove Michael MIC from payload */ ++ skb_trim(skb, skb->len - MICHAEL_MIC_LEN); ++ ++ return TXRX_CONTINUE; ++} ++ ++ ++static int tkip_encrypt_skb(struct ieee80211_txrx_data *tx, ++ struct sk_buff *skb, int test) ++{ ++ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; ++ struct ieee80211_key *key = tx->key; ++ int hdrlen, len, tailneed; ++ u16 fc; ++ u8 *pos; ++ ++ fc = le16_to_cpu(hdr->frame_control); ++ hdrlen = ieee80211_get_hdrlen(fc); ++ len = skb->len - hdrlen; ++ ++ tailneed = (!tx->key->force_sw_encrypt && !tx->local->conf.sw_decrypt) ++ ? 0 : TKIP_ICV_LEN; ++ if ((skb_headroom(skb) < TKIP_IV_LEN || ++ skb_tailroom(skb) < tailneed)) { ++ I802_DEBUG_INC(tx->local->tx_expand_skb_head); ++ if (unlikely(pskb_expand_head(skb, TKIP_IV_LEN, tailneed, ++ GFP_ATOMIC))) ++ return -1; ++ } ++ ++ pos = skb_push(skb, TKIP_IV_LEN); ++ memmove(pos, pos + TKIP_IV_LEN, hdrlen); ++ pos += hdrlen; ++ ++#ifdef CONFIG_HOSTAPD_WPA_TESTING ++ if (test & WPA_TRIGGER_TX_REPLAY) ++ goto skip_iv_inc; ++iv_inc: ++#endif /* CONFIG_HOSTAPD_WPA_TESTING */ ++ ++ /* Increase IV for the frame */ ++ key->u.tkip.iv16++; ++ if (key->u.tkip.iv16 == 0) ++ key->u.tkip.iv32++; ++ ++#ifdef CONFIG_HOSTAPD_WPA_TESTING ++ if (test & WPA_TRIGGER_TX_SKIP_SEQ) { ++ test = 0; ++ goto iv_inc; ++ } ++skip_iv_inc: ++#endif /* CONFIG_HOSTAPD_WPA_TESTING */ ++ ++ if (!tx->key->force_sw_encrypt && !tx->local->conf.sw_decrypt ++#ifdef CONFIG_HOSTAPD_WPA_TESTING ++ && !tx->wpa_test ++#endif /* CONFIG_HOSTAPD_WPA_TESTING */ ++ ) { ++ /* hwaccel - with preallocated room for IV */ ++ ++ ieee80211_tkip_add_iv(pos, key, ++ (u8) (key->u.tkip.iv16 >> 8), ++ (u8) (((key->u.tkip.iv16 >> 8) | 0x20) & ++ 0x7f), ++ (u8) key->u.tkip.iv16); ++ ++ tx->u.tx.control->key_idx = tx->key->hw_key_idx; ++ return 0; ++ } ++ ++ /* Add room for ICV */ ++ skb_put(skb, TKIP_ICV_LEN); ++ ++ hdr = (struct ieee80211_hdr *) skb->data; ++ ieee80211_tkip_encrypt_data(key, pos, len, hdr->addr2); ++ return 0; ++} ++ ++ ++ieee80211_txrx_result ++ieee80211_tx_h_tkip_encrypt(struct ieee80211_txrx_data *tx) ++{ ++ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) tx->skb->data; ++ u16 fc; ++ struct ieee80211_key *key = tx->key; ++ struct sk_buff *skb = tx->skb; ++ int wpa_test = 0, test = 0; ++ ++ fc = le16_to_cpu(hdr->frame_control); ++ ++ if (!key || key->alg != ALG_TKIP || !WLAN_FC_DATA_PRESENT(fc)) ++ return TXRX_CONTINUE; ++ ++ tx->u.tx.control->icv_len = TKIP_ICV_LEN; ++ tx->u.tx.control->iv_len = TKIP_IV_LEN; ++ ieee80211_tx_set_iswep(tx); ++ ++#ifdef CONFIG_HOSTAPD_WPA_TESTING ++ if ((tx->sta && tx->sta->wpa_trigger & WPA_TRIGGER_FAIL_TX_ICV) || ++ (!tx->u.tx.unicast && ++ tx->local->wpa_trigger & WPA_TRIGGER_FAIL_TX_ICV)) { ++ wpa_test = 1; ++ } ++ ++ if (tx->sta) { ++ test = tx->sta->wpa_trigger; ++ tx->sta->wpa_trigger &= ++ ~(WPA_TRIGGER_TX_REPLAY | WPA_TRIGGER_TX_REPLAY_FRAG | ++ WPA_TRIGGER_TX_SKIP_SEQ); ++ } else { ++ test = tx->local->wpa_trigger; ++ tx->local->wpa_trigger &= ++ ~(WPA_TRIGGER_TX_REPLAY | WPA_TRIGGER_TX_REPLAY_FRAG | ++ WPA_TRIGGER_TX_SKIP_SEQ); ++ } ++ if (test & ++ (WPA_TRIGGER_TX_REPLAY | WPA_TRIGGER_TX_REPLAY_FRAG | ++ WPA_TRIGGER_TX_SKIP_SEQ)) { ++ printk(KERN_INFO "%s: WPA testing - TKIP TX packet number " ++ "%s%s%s%s\n", tx->dev->name, ++ tx->sta ? "[UNICAST]" : "[MULTICAST]", ++ test & WPA_TRIGGER_TX_REPLAY ? "[REPLAY]" : "", ++ test & WPA_TRIGGER_TX_REPLAY_FRAG ? ++ "[REPLAY FRAG]" : "", ++ test & WPA_TRIGGER_TX_SKIP_SEQ ? "[SKIP SEQ]" : ""); ++ } ++#endif /* CONFIG_HOSTAPD_WPA_TESTING */ ++ ++ if (!tx->key->force_sw_encrypt && !tx->local->conf.sw_decrypt && ++ !tx->local->hw->wep_include_iv && !wpa_test) { ++ /* hwaccel - with no need for preallocated room for IV/ICV */ ++ tx->u.tx.control->key_idx = tx->key->hw_key_idx; ++ return TXRX_CONTINUE; ++ } ++ ++ if (tkip_encrypt_skb(tx, skb, test) < 0) ++ return TXRX_DROP; ++ ++ if (tx->u.tx.extra_frag) { ++ int i; ++#ifdef CONFIG_HOSTAPD_WPA_TESTING ++ if (test & WPA_TRIGGER_TX_REPLAY_FRAG) ++ test |= WPA_TRIGGER_TX_REPLAY; ++#endif /* CONFIG_HOSTAPD_WPA_TESTING */ ++ for (i = 0; i < tx->u.tx.num_extra_frag; i++) { ++ if (tkip_encrypt_skb(tx, tx->u.tx.extra_frag[i], test) ++ < 0) ++ return TXRX_DROP; ++ } ++ } ++ ++#ifdef CONFIG_HOSTAPD_WPA_TESTING ++ if (tx->sta && tx->sta->wpa_trigger & WPA_TRIGGER_FAIL_TX_ICV) { ++ printk(KERN_INFO "%s: WPA testing - corrupting TX TKIP ICV " ++ "for STA " MACSTR "\n", ++ tx->dev->name, MAC2STR(tx->sta->addr)); ++ tx->u.tx.control->key_idx = HW_KEY_IDX_INVALID; ++ tx->sta->wpa_trigger &= ~WPA_TRIGGER_FAIL_TX_ICV; ++ skb->data[skb->len - 1]++; ++ } else if (!tx->u.tx.unicast && ++ tx->local->wpa_trigger & WPA_TRIGGER_FAIL_TX_ICV) { ++ printk(KERN_INFO "%s: WPA testing - corrupting TX TKIP ICV " ++ "for Group Key\n", ++ tx->dev->name); ++ tx->u.tx.control->key_idx = HW_KEY_IDX_INVALID; ++ tx->local->wpa_trigger &= ~WPA_TRIGGER_FAIL_TX_ICV; ++ skb->data[skb->len - 1]++; ++ } ++#endif /* CONFIG_HOSTAPD_WPA_TESTING */ ++ ++ return TXRX_CONTINUE; ++} ++ ++ ++ieee80211_txrx_result ++ieee80211_rx_h_tkip_decrypt(struct ieee80211_txrx_data *rx) ++{ ++ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) rx->skb->data; ++ u16 fc; ++ int hdrlen, res, hwaccel = 0, wpa_test = 0; ++ struct ieee80211_key *key = rx->key; ++ struct sk_buff *skb = rx->skb; ++ ++ fc = le16_to_cpu(hdr->frame_control); ++ hdrlen = ieee80211_get_hdrlen(fc); ++ ++ if (!rx->key || rx->key->alg != ALG_TKIP || ++ !(rx->fc & WLAN_FC_ISWEP) || ++ WLAN_FC_GET_TYPE(rx->fc) != WLAN_FC_TYPE_DATA) ++ return TXRX_CONTINUE; ++ ++ if (!rx->sta || skb->len - hdrlen < 12) ++ return TXRX_DROP; ++ ++#ifdef CONFIG_HOSTAPD_WPA_TESTING ++ if (rx->sta && rx->sta->wpa_trigger & WPA_TRIGGER_FAIL_RX_ICV) { ++ printk(KERN_INFO "%s: WPA testing - corrupting RX TKIP ICV " ++ "for STA " MACSTR "\n", ++ rx->dev->name, MAC2STR(rx->sta->addr)); ++ rx->sta->wpa_trigger &= ~WPA_TRIGGER_FAIL_RX_ICV; ++ skb->data[skb->len - 1]++; ++ wpa_test = 1; ++ } ++#endif /* CONFIG_HOSTAPD_WPA_TESTING */ ++ ++ if ((rx->u.rx.status->flag & RX_FLAG_DECRYPTED) && ++ !rx->key->force_sw_encrypt && !rx->local->conf.sw_decrypt) { ++ if (!rx->local->hw->wep_include_iv) { ++ /* Hardware takes care of all processing, including ++ * replay protection, so no need to continue here. */ ++ return TXRX_CONTINUE; ++ } ++ ++ /* let TKIP code verify IV, but skip decryption */ ++ hwaccel = 1; ++ } ++ ++ res = ieee80211_tkip_decrypt_data(key, skb->data + hdrlen, ++ skb->len - hdrlen, rx->sta->addr, ++ hwaccel, rx->u.rx.queue); ++ if (res != TKIP_DECRYPT_OK || wpa_test) { ++ printk(KERN_DEBUG "%s: TKIP decrypt failed for RX frame from " ++ MACSTR " (res=%d)\n", ++ rx->dev->name, MAC2STR(rx->sta->addr), res); ++ return TXRX_DROP; ++ } ++ ++ /* Trim ICV */ ++ skb_trim(skb, skb->len - TKIP_ICV_LEN); ++ ++ /* Remove IV */ ++ memmove(skb->data + TKIP_IV_LEN, skb->data, hdrlen); ++ skb_pull(skb, TKIP_IV_LEN); ++ ++ return TXRX_CONTINUE; ++} ++ ++ ++static void ccmp_special_blocks(struct sk_buff *skb, u8 *pn, u8 *b_0, u8 *aad, ++ int encrypted) ++{ ++ u16 fc; ++ int a4_included, qos_included; ++ u8 qos_tid, *fc_pos, *data, *sa, *da; ++ int len_a; ++ size_t data_len; ++ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; ++ ++ fc_pos = (u8 *) &hdr->frame_control; ++ fc = fc_pos[0] ^ (fc_pos[1] << 8); ++ a4_included = (fc & (WLAN_FC_TODS | WLAN_FC_FROMDS)) == ++ (WLAN_FC_TODS | WLAN_FC_FROMDS); ++ ++ ieee80211_get_hdr_info(skb, &sa, &da, &qos_tid, &data, &data_len); ++ data_len -= CCMP_HDR_LEN + (encrypted ? CCMP_MIC_LEN : 0); ++ if (qos_tid & 0x80) { ++ qos_included = 1; ++ qos_tid &= 0x0f; ++ } else ++ qos_included = 0; ++ /* First block, b_0 */ ++ ++ b_0[0] = 0x59; /* flags: Adata: 1, M: 011, L: 001 */ ++ /* Nonce: QoS Priority | A2 | PN */ ++ b_0[1] = qos_tid; ++ memcpy(&b_0[2], hdr->addr2, 6); ++ memcpy(&b_0[8], pn, CCMP_PN_LEN); ++ /* l(m) */ ++ b_0[14] = (data_len >> 8) & 0xff; ++ b_0[15] = data_len & 0xff; ++ ++ ++ /* AAD (extra authenticate-only data) / masked 802.11 header ++ * FC | A1 | A2 | A3 | SC | [A4] | [QC] */ ++ ++ len_a = a4_included ? 28 : 22; ++ if (qos_included) ++ len_a += 2; ++ ++ aad[0] = 0; /* (len_a >> 8) & 0xff; */ ++ aad[1] = len_a & 0xff; ++ /* Mask FC: zero subtype b4 b5 b6 */ ++ aad[2] = fc_pos[0] & ~(BIT(4) | BIT(5) | BIT(6)); ++ /* Retry, PwrMgt, MoreData; set Protected */ ++ aad[3] = (fc_pos[1] & ~(BIT(3) | BIT(4) | BIT(5))) | BIT(6); ++ memcpy(&aad[4], &hdr->addr1, 18); ++ ++ /* Mask Seq#, leave Frag# */ ++ aad[22] = *((u8 *) &hdr->seq_ctrl) & 0x0f; ++ aad[23] = 0; ++ if (a4_included) { ++ memcpy(&aad[24], hdr->addr4, 6); ++ aad[30] = 0; ++ aad[31] = 0; ++ } else ++ memset(&aad[24], 0, 8); ++ if (qos_included) { ++ u8 *dpos = &aad[a4_included ? 30 : 24]; ++ ++ /* Mask QoS Control field */ ++ dpos[0] = qos_tid; ++ dpos[1] = 0; ++ } ++} ++ ++ ++static inline void ccmp_pn2hdr(u8 *hdr, u8 *pn, int key_id) ++{ ++ hdr[0] = pn[5]; ++ hdr[1] = pn[4]; ++ hdr[2] = 0; ++ hdr[3] = 0x20 | (key_id << 6); ++ hdr[4] = pn[3]; ++ hdr[5] = pn[2]; ++ hdr[6] = pn[1]; ++ hdr[7] = pn[0]; ++} ++ ++ ++static inline int ccmp_hdr2pn(u8 *pn, u8 *hdr) ++{ ++ pn[0] = hdr[7]; ++ pn[1] = hdr[6]; ++ pn[2] = hdr[5]; ++ pn[3] = hdr[4]; ++ pn[4] = hdr[1]; ++ pn[5] = hdr[0]; ++ return (hdr[3] >> 6) & 0x03; ++} ++ ++ ++static int ccmp_encrypt_skb(struct ieee80211_txrx_data *tx, ++ struct sk_buff *skb, int test) ++{ ++ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; ++ struct ieee80211_key *key = tx->key; ++ int hdrlen, len, tailneed; ++ u16 fc; ++ u8 *pos, *pn; ++ u8 b_0[AES_BLOCK_LEN], aad[2 * AES_BLOCK_LEN]; ++ int i; ++ ++ fc = le16_to_cpu(hdr->frame_control); ++ hdrlen = ieee80211_get_hdrlen(fc); ++ len = skb->len - hdrlen; ++ ++ tailneed = (!tx->key->force_sw_encrypt && !tx->local->conf.sw_decrypt) ++ ? 0 : CCMP_MIC_LEN; ++ ++ if ((skb_headroom(skb) < CCMP_HDR_LEN || ++ skb_tailroom(skb) < tailneed)) { ++ I802_DEBUG_INC(tx->local->tx_expand_skb_head); ++ if (unlikely(pskb_expand_head(skb, CCMP_HDR_LEN, tailneed, ++ GFP_ATOMIC))) ++ return -1; ++ } ++ ++ pos = skb_push(skb, CCMP_HDR_LEN); ++ memmove(pos, pos + CCMP_HDR_LEN, hdrlen); ++ hdr = (struct ieee80211_hdr *) pos; ++ pos += hdrlen; ++ ++ /* PN = PN + 1 */ ++ pn = key->u.ccmp.tx_pn; ++ ++#ifdef CONFIG_HOSTAPD_WPA_TESTING ++ if (test & WPA_TRIGGER_TX_REPLAY) ++ goto skip_pn_inc; ++pn_inc: ++#endif /* CONFIG_HOSTAPD_WPA_TESTING */ ++ ++ for (i = CCMP_PN_LEN - 1; i >= 0; i--) { ++ pn[i]++; ++ if (pn[i]) ++ break; ++ } ++ ++#ifdef CONFIG_HOSTAPD_WPA_TESTING ++ if (test & WPA_TRIGGER_TX_SKIP_SEQ) { ++ test = 0; ++ goto pn_inc; ++ } ++skip_pn_inc: ++#endif /* CONFIG_HOSTAPD_WPA_TESTING */ ++ ++ ccmp_pn2hdr(pos, pn, key->keyidx); ++ ++ if (!tx->key->force_sw_encrypt && !tx->local->conf.sw_decrypt) { ++ /* hwaccel - with preallocated room for CCMP header */ ++ tx->u.tx.control->key_idx = tx->key->hw_key_idx; ++ return 0; ++ } ++ ++ pos += CCMP_HDR_LEN; ++ ccmp_special_blocks(skb, pn, b_0, aad, 0); ++ ieee80211_aes_ccm_encrypt(key->u.ccmp.aes_state, b_0, aad, pos, len, ++ pos, skb_put(skb, CCMP_MIC_LEN)); ++ ++ return 0; ++} ++ ++ ++ieee80211_txrx_result ++ieee80211_tx_h_ccmp_encrypt(struct ieee80211_txrx_data *tx) ++{ ++ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) tx->skb->data; ++ struct ieee80211_key *key = tx->key; ++ u16 fc; ++ struct sk_buff *skb = tx->skb; ++ int test = 0; ++ ++ fc = le16_to_cpu(hdr->frame_control); ++ ++ if (!key || key->alg != ALG_CCMP || !WLAN_FC_DATA_PRESENT(fc)) ++ return TXRX_CONTINUE; ++ ++#ifdef CONFIG_HOSTAPD_WPA_TESTING ++ if (tx->sta) { ++ test = tx->sta->wpa_trigger; ++ tx->sta->wpa_trigger = 0; ++ } else { ++ test = tx->local->wpa_trigger; ++ tx->local->wpa_trigger = 0; ++ } ++ if (test & ++ (WPA_TRIGGER_TX_REPLAY | WPA_TRIGGER_TX_REPLAY_FRAG | ++ WPA_TRIGGER_TX_SKIP_SEQ)) { ++ printk(KERN_INFO "%s: WPA testing - CCMP TX packet number " ++ "%s%s%s%s\n", tx->dev->name, ++ tx->sta ? "[UNICAST]" : "[MULTICAST]", ++ test & WPA_TRIGGER_TX_REPLAY ? "[REPLAY]" : "", ++ test & WPA_TRIGGER_TX_REPLAY_FRAG ? ++ "[REPLAY FRAG]" : "", ++ test & WPA_TRIGGER_TX_SKIP_SEQ ? "[SKIP SEQ]" : ""); ++ } ++#endif /* CONFIG_HOSTAPD_WPA_TESTING */ ++ ++ tx->u.tx.control->icv_len = CCMP_MIC_LEN; ++ tx->u.tx.control->iv_len = CCMP_HDR_LEN; ++ ieee80211_tx_set_iswep(tx); ++ ++ if (!tx->key->force_sw_encrypt && !tx->local->conf.sw_decrypt && ++ !tx->local->hw->wep_include_iv) { ++ /* hwaccel - with no need for preallocated room for CCMP " ++ * header or MIC fields */ ++ tx->u.tx.control->key_idx = tx->key->hw_key_idx; ++ return TXRX_CONTINUE; ++ } ++ ++ if (ccmp_encrypt_skb(tx, skb, test) < 0) ++ return TXRX_DROP; ++ ++ if (tx->u.tx.extra_frag) { ++ int i; ++#ifdef CONFIG_HOSTAPD_WPA_TESTING ++ if (test & WPA_TRIGGER_TX_REPLAY_FRAG) ++ test |= WPA_TRIGGER_TX_REPLAY; ++#endif /* CONFIG_HOSTAPD_WPA_TESTING */ ++ for (i = 0; i < tx->u.tx.num_extra_frag; i++) { ++ if (ccmp_encrypt_skb(tx, tx->u.tx.extra_frag[i], test) ++ < 0) ++ return TXRX_DROP; ++ } ++ } ++ ++ return TXRX_CONTINUE; ++} ++ ++ ++ieee80211_txrx_result ++ieee80211_rx_h_ccmp_decrypt(struct ieee80211_txrx_data *rx) ++{ ++ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) rx->skb->data; ++ u16 fc; ++ int hdrlen; ++ struct ieee80211_key *key = rx->key; ++ struct sk_buff *skb = rx->skb; ++ u8 b_0[AES_BLOCK_LEN], aad[2 * AES_BLOCK_LEN]; ++ u8 pn[CCMP_PN_LEN]; ++ int data_len; ++ ++ fc = le16_to_cpu(hdr->frame_control); ++ hdrlen = ieee80211_get_hdrlen(fc); ++ ++ if (!rx->key || rx->key->alg != ALG_CCMP || ++ !(rx->fc & WLAN_FC_ISWEP) || ++ WLAN_FC_GET_TYPE(rx->fc) != WLAN_FC_TYPE_DATA) ++ return TXRX_CONTINUE; ++ ++ data_len = skb->len - hdrlen - CCMP_HDR_LEN - CCMP_MIC_LEN; ++ if (!rx->sta || data_len < 0) ++ return TXRX_DROP; ++ ++ if ((rx->u.rx.status->flag & RX_FLAG_DECRYPTED) && ++ !rx->key->force_sw_encrypt && !rx->local->conf.sw_decrypt && ++ !rx->local->hw->wep_include_iv) ++ return TXRX_CONTINUE; ++ ++ (void) ccmp_hdr2pn(pn, skb->data + hdrlen); ++ ++ if (memcmp(pn, key->u.ccmp.rx_pn[rx->u.rx.queue], CCMP_PN_LEN) <= 0) { ++#ifdef CONFIG_D80211_DEBUG ++ u8 *ppn = key->u.ccmp.rx_pn[rx->u.rx.queue]; ++ printk(KERN_DEBUG "%s: CCMP replay detected for RX frame from " ++ MACSTR " (RX PN %02x%02x%02x%02x%02x%02x <= prev. PN " ++ "%02x%02x%02x%02x%02x%02x)\n", rx->dev->name, ++ MAC2STR(rx->sta->addr), ++ pn[0], pn[1], pn[2], pn[3], pn[4], pn[5], ++ ppn[0], ppn[1], ppn[2], ppn[3], ppn[4], ppn[5]); ++#endif /* CONFIG_D80211_DEBUG */ ++ key->u.ccmp.replays++; ++ return TXRX_DROP; ++ } ++ ++ if ((rx->u.rx.status->flag & RX_FLAG_DECRYPTED) && ++ !rx->key->force_sw_encrypt && !rx->local->conf.sw_decrypt) { ++ /* hwaccel has already decrypted frame and verified MIC */ ++ } else { ++ ccmp_special_blocks(skb, pn, b_0, aad, 1); ++ ++ if (ieee80211_aes_ccm_decrypt( ++ key->u.ccmp.aes_state, b_0, aad, ++ skb->data + hdrlen + CCMP_HDR_LEN, data_len, ++ skb->data + skb->len - CCMP_MIC_LEN, ++ skb->data + hdrlen + CCMP_HDR_LEN)) { ++ printk(KERN_DEBUG "%s: CCMP decrypt failed for RX " ++ "frame from " MACSTR "\n", rx->dev->name, ++ MAC2STR(rx->sta->addr)); ++ return TXRX_DROP; ++ } ++ } ++ ++ memcpy(key->u.ccmp.rx_pn[rx->u.rx.queue], pn, CCMP_PN_LEN); ++ ++ /* Remove CCMP header and MIC */ ++ skb_trim(skb, skb->len - CCMP_MIC_LEN); ++ memmove(skb->data + CCMP_HDR_LEN, skb->data, hdrlen); ++ skb_pull(skb, CCMP_HDR_LEN); ++ ++ return TXRX_CONTINUE; ++} ++ +diff -Nur linux-2.6.16/net/d80211/wpa.h linux-2.6.16-bcm43xx/net/d80211/wpa.h +--- linux-2.6.16/net/d80211/wpa.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.16-bcm43xx/net/d80211/wpa.h 2006-03-28 22:16:14.000000000 +0200 +@@ -0,0 +1,34 @@ ++/* ++ * Copyright 2002-2004, Instant802 Networks, Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#ifndef WPA_H ++#define WPA_H ++ ++#include <linux/skbuff.h> ++#include <linux/types.h> ++#include "ieee80211_i.h" ++ ++ieee80211_txrx_result ++ieee80211_tx_h_michael_mic_add(struct ieee80211_txrx_data *tx); ++ieee80211_txrx_result ++ieee80211_rx_h_michael_mic_verify(struct ieee80211_txrx_data *rx); ++ ++ieee80211_txrx_result ++ieee80211_tx_h_tkip_encrypt(struct ieee80211_txrx_data *tx); ++ieee80211_txrx_result ++ieee80211_rx_h_tkip_decrypt(struct ieee80211_txrx_data *rx); ++ ++ieee80211_txrx_result ++ieee80211_tx_h_ccmp_encrypt(struct ieee80211_txrx_data *tx); ++ieee80211_txrx_result ++ieee80211_rx_h_ccmp_decrypt(struct ieee80211_txrx_data *rx); ++ ++int ieee80211_get_hdr_info(const struct sk_buff *skb, u8 **sa, u8 **da, ++ u8 *qos_tid, u8 **data, size_t *data_len); ++ ++#endif /* WPA_H */ +diff -Nur linux-2.6.16/net/ieee80211/ieee80211_crypt.c linux-2.6.16-bcm43xx/net/ieee80211/ieee80211_crypt.c +--- linux-2.6.16/net/ieee80211/ieee80211_crypt.c 2006-03-20 06:53:29.000000000 +0100 ++++ linux-2.6.16-bcm43xx/net/ieee80211/ieee80211_crypt.c 2006-03-28 22:16:14.000000000 +0200 +@@ -18,7 +18,6 @@ + #include <linux/string.h> + #include <net/ieee80211.h> + +- + MODULE_AUTHOR("Jouni Malinen"); + MODULE_DESCRIPTION("HostAP crypto"); + MODULE_LICENSE("GPL"); +@@ -33,11 +32,11 @@ + + void ieee80211_crypt_deinit_entries(struct ieee80211_device *ieee, int force) + { +- struct ieee80211_crypt_data *entry, *next; ++ struct ieee80211_crypt_data *entry, *next; + unsigned long flags; + + spin_lock_irqsave(&ieee->lock, flags); +- list_for_each_entry_safe(entry, next, &ieee->crypt_deinit_list, list) { ++ list_for_each_entry_safe(entry, next, &ieee->crypt_deinit_list, list) { + if (atomic_read(&entry->refcnt) != 0 && !force) + continue; + +@@ -141,9 +140,9 @@ + spin_unlock_irqrestore(&ieee80211_crypto_lock, flags); + return -EINVAL; + +- found: ++ found: + printk(KERN_DEBUG "ieee80211_crypt: unregistered algorithm " +- "'%s'\n", ops->name); ++ "'%s'\n", ops->name); + list_del(&alg->list); + spin_unlock_irqrestore(&ieee80211_crypto_lock, flags); + kfree(alg); +@@ -163,7 +162,7 @@ + spin_unlock_irqrestore(&ieee80211_crypto_lock, flags); + return NULL; + +- found: ++ found: + spin_unlock_irqrestore(&ieee80211_crypto_lock, flags); + return alg->ops; + } +diff -Nur linux-2.6.16/net/ieee80211/ieee80211_crypt_ccmp.c linux-2.6.16-bcm43xx/net/ieee80211/ieee80211_crypt_ccmp.c +--- linux-2.6.16/net/ieee80211/ieee80211_crypt_ccmp.c 2006-03-20 06:53:29.000000000 +0100 ++++ linux-2.6.16-bcm43xx/net/ieee80211/ieee80211_crypt_ccmp.c 2006-03-28 22:16:14.000000000 +0200 +@@ -190,7 +190,8 @@ + ieee80211_ccmp_aes_encrypt(tfm, b0, s0); + } + +-static int ieee80211_ccmp_hdr(struct sk_buff *skb, int hdr_len, void *priv) ++static int ieee80211_ccmp_hdr(struct sk_buff *skb, int hdr_len, ++ u8 *aeskey, int keylen, void *priv) + { + struct ieee80211_ccmp_data *key = priv; + int i; +@@ -199,6 +200,9 @@ + if (skb_headroom(skb) < CCMP_HDR_LEN || skb->len < hdr_len) + return -1; + ++ if (aeskey != NULL && keylen >= CCMP_TK_LEN) ++ memcpy(aeskey, key->key, CCMP_TK_LEN); ++ + pos = skb_push(skb, CCMP_HDR_LEN); + memmove(pos, pos + CCMP_HDR_LEN, hdr_len); + pos += hdr_len; +@@ -238,7 +242,7 @@ + return -1; + + data_len = skb->len - hdr_len; +- len = ieee80211_ccmp_hdr(skb, hdr_len, priv); ++ len = ieee80211_ccmp_hdr(skb, hdr_len, NULL, 0, priv); + if (len < 0) + return -1; + +diff -Nur linux-2.6.16/net/ieee80211/ieee80211_crypt_tkip.c linux-2.6.16-bcm43xx/net/ieee80211/ieee80211_crypt_tkip.c +--- linux-2.6.16/net/ieee80211/ieee80211_crypt_tkip.c 2006-03-20 06:53:29.000000000 +0100 ++++ linux-2.6.16-bcm43xx/net/ieee80211/ieee80211_crypt_tkip.c 2006-03-28 22:16:14.000000000 +0200 +@@ -80,10 +80,9 @@ + { + struct ieee80211_tkip_data *priv; + +- priv = kmalloc(sizeof(*priv), GFP_ATOMIC); ++ priv = kzalloc(sizeof(*priv), GFP_ATOMIC); + if (priv == NULL) + goto fail; +- memset(priv, 0, sizeof(*priv)); + + priv->key_idx = key_idx; + +@@ -271,34 +270,33 @@ + #endif + } + +-static u8 *ieee80211_tkip_hdr(struct sk_buff *skb, int hdr_len, void *priv) ++static int ieee80211_tkip_hdr(struct sk_buff *skb, int hdr_len, ++ u8 * rc4key, int keylen, void *priv) + { + struct ieee80211_tkip_data *tkey = priv; + int len; +- u8 *rc4key, *pos, *icv; ++ u8 *pos; + struct ieee80211_hdr_4addr *hdr; +- u32 crc; + + hdr = (struct ieee80211_hdr_4addr *)skb->data; + + if (skb_headroom(skb) < 8 || skb->len < hdr_len) +- return NULL; ++ return -1; ++ ++ if (rc4key == NULL || keylen < 16) ++ return -1; + + if (!tkey->tx_phase1_done) { + tkip_mixing_phase1(tkey->tx_ttak, tkey->key, hdr->addr2, + tkey->tx_iv32); + tkey->tx_phase1_done = 1; + } +- rc4key = kmalloc(16, GFP_ATOMIC); +- if (!rc4key) +- return NULL; + tkip_mixing_phase2(rc4key, tkey->key, tkey->tx_ttak, tkey->tx_iv16); + + len = skb->len - hdr_len; + pos = skb_push(skb, 8); + memmove(pos, pos + 8, hdr_len); + pos += hdr_len; +- icv = skb_put(skb, 4); + + *pos++ = *rc4key; + *pos++ = *(rc4key + 1); +@@ -309,28 +307,28 @@ + *pos++ = (tkey->tx_iv32 >> 16) & 0xff; + *pos++ = (tkey->tx_iv32 >> 24) & 0xff; + +- crc = ~crc32_le(~0, pos, len); +- icv[0] = crc; +- icv[1] = crc >> 8; +- icv[2] = crc >> 16; +- icv[3] = crc >> 24; ++ tkey->tx_iv16++; ++ if (tkey->tx_iv16 == 0) { ++ tkey->tx_phase1_done = 0; ++ tkey->tx_iv32++; ++ } + +- return rc4key; ++ return 8; + } + + static int ieee80211_tkip_encrypt(struct sk_buff *skb, int hdr_len, void *priv) + { + struct ieee80211_tkip_data *tkey = priv; + int len; +- const u8 *rc4key; +- u8 *pos; ++ u8 rc4key[16], *pos, *icv; ++ u32 crc; + struct scatterlist sg; + + if (tkey->flags & IEEE80211_CRYPTO_TKIP_COUNTERMEASURES) { + if (net_ratelimit()) { + struct ieee80211_hdr_4addr *hdr = + (struct ieee80211_hdr_4addr *)skb->data; +- printk(KERN_DEBUG "TKIP countermeasures: dropped " ++ printk(KERN_DEBUG ": TKIP countermeasures: dropped " + "TX packet to " MAC_FMT "\n", + MAC_ARG(hdr->addr1)); + } +@@ -343,22 +341,23 @@ + len = skb->len - hdr_len; + pos = skb->data + hdr_len; + +- rc4key = ieee80211_tkip_hdr(skb, hdr_len, priv); +- if (!rc4key) ++ if ((ieee80211_tkip_hdr(skb, hdr_len, rc4key, 16, priv)) < 0) + return -1; + ++ icv = skb_put(skb, 4); ++ ++ crc = ~crc32_le(~0, pos, len); ++ icv[0] = crc; ++ icv[1] = crc >> 8; ++ icv[2] = crc >> 16; ++ icv[3] = crc >> 24; ++ + crypto_cipher_setkey(tkey->tfm_arc4, rc4key, 16); + sg.page = virt_to_page(pos); + sg.offset = offset_in_page(pos); + sg.length = len + 4; + crypto_cipher_encrypt(tkey->tfm_arc4, &sg, &sg, len + 4); + +- tkey->tx_iv16++; +- if (tkey->tx_iv16 == 0) { +- tkey->tx_phase1_done = 0; +- tkey->tx_iv32++; +- } +- + return 0; + } + +@@ -379,7 +378,7 @@ + + if (tkey->flags & IEEE80211_CRYPTO_TKIP_COUNTERMEASURES) { + if (net_ratelimit()) { +- printk(KERN_DEBUG "TKIP countermeasures: dropped " ++ printk(KERN_DEBUG ": TKIP countermeasures: dropped " + "received packet from " MAC_FMT "\n", + MAC_ARG(hdr->addr2)); + } +@@ -695,6 +694,7 @@ + .name = "TKIP", + .init = ieee80211_tkip_init, + .deinit = ieee80211_tkip_deinit, ++ .build_iv = ieee80211_tkip_hdr, + .encrypt_mpdu = ieee80211_tkip_encrypt, + .decrypt_mpdu = ieee80211_tkip_decrypt, + .encrypt_msdu = ieee80211_michael_mic_add, +diff -Nur linux-2.6.16/net/ieee80211/ieee80211_crypt_wep.c linux-2.6.16-bcm43xx/net/ieee80211/ieee80211_crypt_wep.c +--- linux-2.6.16/net/ieee80211/ieee80211_crypt_wep.c 2006-03-20 06:53:29.000000000 +0100 ++++ linux-2.6.16-bcm43xx/net/ieee80211/ieee80211_crypt_wep.c 2006-03-28 22:16:14.000000000 +0200 +@@ -76,7 +76,8 @@ + } + + /* Add WEP IV/key info to a frame that has at least 4 bytes of headroom */ +-static int prism2_wep_build_iv(struct sk_buff *skb, int hdr_len, void *priv) ++static int prism2_wep_build_iv(struct sk_buff *skb, int hdr_len, ++ u8 *key, int keylen, void *priv) + { + struct prism2_wep_data *wep = priv; + u32 klen, len; +@@ -131,7 +132,7 @@ + return -1; + + /* add the IV to the frame */ +- if (prism2_wep_build_iv(skb, hdr_len, priv)) ++ if (prism2_wep_build_iv(skb, hdr_len, NULL, 0, priv)) + return -1; + + /* Copy the IV into the first 3 bytes of the key */ +diff -Nur linux-2.6.16/net/ieee80211/ieee80211_geo.c linux-2.6.16-bcm43xx/net/ieee80211/ieee80211_geo.c +--- linux-2.6.16/net/ieee80211/ieee80211_geo.c 2006-03-20 06:53:29.000000000 +0100 ++++ linux-2.6.16-bcm43xx/net/ieee80211/ieee80211_geo.c 2006-03-28 22:16:14.000000000 +0200 +@@ -50,7 +50,8 @@ + + /* Driver needs to initialize the geography map before using + * these helper functions */ +- BUG_ON(ieee->geo.bg_channels == 0 && ieee->geo.a_channels == 0); ++ if (ieee->geo.bg_channels == 0 && ieee->geo.a_channels == 0) ++ return 0; + + if (ieee->freq_band & IEEE80211_24GHZ_BAND) + for (i = 0; i < ieee->geo.bg_channels; i++) +@@ -58,13 +59,15 @@ + * this is a B only channel, we don't see it + * as valid. */ + if ((ieee->geo.bg[i].channel == channel) && ++ !(ieee->geo.bg[i].flags & IEEE80211_CH_INVALID) && + (!(ieee->mode & IEEE_G) || + !(ieee->geo.bg[i].flags & IEEE80211_CH_B_ONLY))) + return IEEE80211_24GHZ_BAND; + + if (ieee->freq_band & IEEE80211_52GHZ_BAND) + for (i = 0; i < ieee->geo.a_channels; i++) +- if (ieee->geo.a[i].channel == channel) ++ if ((ieee->geo.a[i].channel == channel) && ++ !(ieee->geo.a[i].flags & IEEE80211_CH_INVALID)) + return IEEE80211_52GHZ_BAND; + + return 0; +@@ -76,7 +79,8 @@ + + /* Driver needs to initialize the geography map before using + * these helper functions */ +- BUG_ON(ieee->geo.bg_channels == 0 && ieee->geo.a_channels == 0); ++ if (ieee->geo.bg_channels == 0 && ieee->geo.a_channels == 0) ++ return -1; + + if (ieee->freq_band & IEEE80211_24GHZ_BAND) + for (i = 0; i < ieee->geo.bg_channels; i++) +@@ -97,7 +101,8 @@ + + /* Driver needs to initialize the geography map before using + * these helper functions */ +- BUG_ON(ieee->geo.bg_channels == 0 && ieee->geo.a_channels == 0); ++ if (ieee->geo.bg_channels == 0 && ieee->geo.a_channels == 0) ++ return 0; + + freq /= 100000; + +@@ -133,6 +138,41 @@ + return &ieee->geo; + } + ++u8 ieee80211_get_channel_flags(struct ieee80211_device * ieee, u8 channel) ++{ ++ int index = ieee80211_channel_to_index(ieee, channel); ++ ++ if (index == -1) ++ return IEEE80211_CH_INVALID; ++ ++ if (channel <= IEEE80211_24GHZ_CHANNELS) ++ return ieee->geo.bg[index].flags; ++ ++ return ieee->geo.a[index].flags; ++} ++ ++static const struct ieee80211_channel bad_channel = { ++ .channel = 0, ++ .flags = IEEE80211_CH_INVALID, ++ .max_power = 0, ++}; ++ ++const struct ieee80211_channel *ieee80211_get_channel(struct ieee80211_device ++ *ieee, u8 channel) ++{ ++ int index = ieee80211_channel_to_index(ieee, channel); ++ ++ if (index == -1) ++ return &bad_channel; ++ ++ if (channel <= IEEE80211_24GHZ_CHANNELS) ++ return &ieee->geo.bg[index]; ++ ++ return &ieee->geo.a[index]; ++} ++ ++EXPORT_SYMBOL(ieee80211_get_channel); ++EXPORT_SYMBOL(ieee80211_get_channel_flags); + EXPORT_SYMBOL(ieee80211_is_valid_channel); + EXPORT_SYMBOL(ieee80211_freq_to_channel); + EXPORT_SYMBOL(ieee80211_channel_to_index); +diff -Nur linux-2.6.16/net/ieee80211/ieee80211_module.c linux-2.6.16-bcm43xx/net/ieee80211/ieee80211_module.c +--- linux-2.6.16/net/ieee80211/ieee80211_module.c 2006-03-20 06:53:29.000000000 +0100 ++++ linux-2.6.16-bcm43xx/net/ieee80211/ieee80211_module.c 2006-03-28 22:16:14.000000000 +0200 +@@ -82,10 +82,28 @@ + return 0; + } + ++void ieee80211_network_reset(struct ieee80211_network *network) ++{ ++ if (!network) ++ return; ++ ++ if (network->ibss_dfs) { ++ kfree(network->ibss_dfs); ++ network->ibss_dfs = NULL; ++ } ++} ++ + static inline void ieee80211_networks_free(struct ieee80211_device *ieee) + { ++ int i; ++ + if (!ieee->networks) + return; ++ ++ for (i = 0; i < MAX_NETWORK_COUNT; i++) ++ if (ieee->networks[i].ibss_dfs) ++ kfree(ieee->networks[i].ibss_dfs); ++ + kfree(ieee->networks); + ieee->networks = NULL; + } +@@ -195,7 +213,7 @@ + + static int debug = 0; + u32 ieee80211_debug_level = 0; +-struct proc_dir_entry *ieee80211_proc = NULL; ++static struct proc_dir_entry *ieee80211_proc = NULL; + + static int show_debug_level(char *page, char **start, off_t offset, + int count, int *eof, void *data) +diff -Nur linux-2.6.16/net/ieee80211/ieee80211_rx.c linux-2.6.16-bcm43xx/net/ieee80211/ieee80211_rx.c +--- linux-2.6.16/net/ieee80211/ieee80211_rx.c 2006-03-20 06:53:29.000000000 +0100 ++++ linux-2.6.16-bcm43xx/net/ieee80211/ieee80211_rx.c 2006-03-28 22:16:14.000000000 +0200 +@@ -369,8 +369,8 @@ + + /* Put this code here so that we avoid duplicating it in all + * Rx paths. - Jean II */ ++#ifdef CONFIG_WIRELESS_EXT + #ifdef IW_WIRELESS_SPY /* defined in iw_handler.h */ +-#ifdef CONFIG_NET_RADIO + /* If spy monitoring on */ + if (ieee->spy_data.spy_number > 0) { + struct iw_quality wstats; +@@ -397,8 +397,8 @@ + /* Update spy records */ + wireless_spy_update(ieee->dev, hdr->addr2, &wstats); + } +-#endif /* CONFIG_NET_RADIO */ + #endif /* IW_WIRELESS_SPY */ ++#endif /* CONFIG_WIRELESS_EXT */ + + #ifdef NOT_YET + hostap_update_rx_stats(local->ap, hdr, rx_stats); +@@ -574,7 +574,7 @@ + /* skb: hdr + (possibly fragmented) plaintext payload */ + // PR: FIXME: hostap has additional conditions in the "if" below: + // ieee->host_decrypt && (fc & IEEE80211_FCTL_PROTECTED) && +- if ((frag != 0 || (fc & IEEE80211_FCTL_MOREFRAGS))) { ++ if ((frag != 0) || (fc & IEEE80211_FCTL_MOREFRAGS)) { + int flen; + struct sk_buff *frag_skb = ieee80211_frag_cache_get(ieee, hdr); + IEEE80211_DEBUG_FRAG("Rx Fragment received (%u)\n", frag); +@@ -754,7 +754,14 @@ + memset(skb->cb, 0, sizeof(skb->cb)); + skb->dev = dev; + skb->ip_summed = CHECKSUM_NONE; /* 802.11 crc not sufficient */ +- netif_rx(skb); ++ if (netif_rx(skb) == NET_RX_DROP) { ++ /* netif_rx always succeeds, but it might drop ++ * the packet. If it drops the packet, we log that ++ * in our stats. */ ++ IEEE80211_DEBUG_DROP ++ ("RX: netif_rx dropped the packet\n"); ++ stats->rx_dropped++; ++ } + } + + rx_exit: +@@ -930,6 +937,45 @@ + return rc; + } + ++#ifdef CONFIG_IEEE80211_DEBUG ++#define MFIE_STRING(x) case MFIE_TYPE_ ##x: return #x ++ ++static const char *get_info_element_string(u16 id) ++{ ++ switch (id) { ++ MFIE_STRING(SSID); ++ MFIE_STRING(RATES); ++ MFIE_STRING(FH_SET); ++ MFIE_STRING(DS_SET); ++ MFIE_STRING(CF_SET); ++ MFIE_STRING(TIM); ++ MFIE_STRING(IBSS_SET); ++ MFIE_STRING(COUNTRY); ++ MFIE_STRING(HOP_PARAMS); ++ MFIE_STRING(HOP_TABLE); ++ MFIE_STRING(REQUEST); ++ MFIE_STRING(CHALLENGE); ++ MFIE_STRING(POWER_CONSTRAINT); ++ MFIE_STRING(POWER_CAPABILITY); ++ MFIE_STRING(TPC_REQUEST); ++ MFIE_STRING(TPC_REPORT); ++ MFIE_STRING(SUPP_CHANNELS); ++ MFIE_STRING(CSA); ++ MFIE_STRING(MEASURE_REQUEST); ++ MFIE_STRING(MEASURE_REPORT); ++ MFIE_STRING(QUIET); ++ MFIE_STRING(IBSS_DFS); ++ MFIE_STRING(ERP_INFO); ++ MFIE_STRING(RSN); ++ MFIE_STRING(RATES_EX); ++ MFIE_STRING(GENERIC); ++ MFIE_STRING(QOS_PARAMETER); ++ default: ++ return "UNKNOWN"; ++ } ++} ++#endif ++ + static int ieee80211_parse_info_param(struct ieee80211_info_element + *info_element, u16 length, + struct ieee80211_network *network) +@@ -1040,7 +1086,9 @@ + break; + + case MFIE_TYPE_TIM: +- IEEE80211_DEBUG_MGMT("MFIE_TYPE_TIM: ignored\n"); ++ network->tim.tim_count = info_element->data[0]; ++ network->tim.tim_period = info_element->data[1]; ++ IEEE80211_DEBUG_MGMT("MFIE_TYPE_TIM: partially ignored\n"); + break; + + case MFIE_TYPE_ERP_INFO: +@@ -1091,10 +1139,49 @@ + printk(KERN_ERR + "QoS Error need to parse QOS_PARAMETER IE\n"); + break; ++ /* 802.11h */ ++ case MFIE_TYPE_POWER_CONSTRAINT: ++ network->power_constraint = info_element->data[0]; ++ network->flags |= NETWORK_HAS_POWER_CONSTRAINT; ++ break; ++ ++ case MFIE_TYPE_CSA: ++ network->power_constraint = info_element->data[0]; ++ network->flags |= NETWORK_HAS_CSA; ++ break; ++ ++ case MFIE_TYPE_QUIET: ++ network->quiet.count = info_element->data[0]; ++ network->quiet.period = info_element->data[1]; ++ network->quiet.duration = info_element->data[2]; ++ network->quiet.offset = info_element->data[3]; ++ network->flags |= NETWORK_HAS_QUIET; ++ break; ++ ++ case MFIE_TYPE_IBSS_DFS: ++ if (network->ibss_dfs) ++ break; ++ network->ibss_dfs = ++ kmalloc(info_element->len, GFP_ATOMIC); ++ if (!network->ibss_dfs) ++ return 1; ++ memcpy(network->ibss_dfs, info_element->data, ++ info_element->len); ++ network->flags |= NETWORK_HAS_IBSS_DFS; ++ break; ++ ++ case MFIE_TYPE_TPC_REPORT: ++ network->tpc_report.transmit_power = ++ info_element->data[0]; ++ network->tpc_report.link_margin = info_element->data[1]; ++ network->flags |= NETWORK_HAS_TPC_REPORT; ++ break; + + default: +- IEEE80211_DEBUG_MGMT("unsupported IE %d\n", +- info_element->id); ++ IEEE80211_DEBUG_MGMT ++ ("Unsupported info element: %s (%d)\n", ++ get_info_element_string(info_element->id), ++ info_element->id); + break; + } + +@@ -1110,7 +1197,9 @@ + static int ieee80211_handle_assoc_resp(struct ieee80211_device *ieee, struct ieee80211_assoc_response + *frame, struct ieee80211_rx_stats *stats) + { +- struct ieee80211_network network_resp; ++ struct ieee80211_network network_resp = { ++ .ibss_dfs = NULL, ++ }; + struct ieee80211_network *network = &network_resp; + struct net_device *dev = ieee->dev; + +@@ -1253,7 +1342,22 @@ + int qos_active; + u8 old_param; + +- memcpy(&dst->stats, &src->stats, sizeof(struct ieee80211_rx_stats)); ++ ieee80211_network_reset(dst); ++ dst->ibss_dfs = src->ibss_dfs; ++ ++ /* We only update the statistics if they were created by receiving ++ * the network information on the actual channel the network is on. ++ * ++ * This keeps beacons received on neighbor channels from bringing ++ * down the signal level of an AP. */ ++ if (dst->channel == src->stats.received_channel) ++ memcpy(&dst->stats, &src->stats, ++ sizeof(struct ieee80211_rx_stats)); ++ else ++ IEEE80211_DEBUG_SCAN("Network " MAC_FMT " info received " ++ "off channel (%d vs. %d)\n", MAC_ARG(src->bssid), ++ dst->channel, src->stats.received_channel); ++ + dst->capability = src->capability; + memcpy(dst->rates, src->rates, src->rates_len); + dst->rates_len = src->rates_len; +@@ -1269,6 +1373,7 @@ + dst->listen_interval = src->listen_interval; + dst->atim_window = src->atim_window; + dst->erp_value = src->erp_value; ++ dst->tim = src->tim; + + memcpy(dst->wpa_ie, src->wpa_ie, src->wpa_ie_len); + dst->wpa_ie_len = src->wpa_ie_len; +@@ -1313,7 +1418,9 @@ + *stats) + { + struct net_device *dev = ieee->dev; +- struct ieee80211_network network; ++ struct ieee80211_network network = { ++ .ibss_dfs = NULL, ++ }; + struct ieee80211_network *target; + struct ieee80211_network *oldest = NULL; + #ifdef CONFIG_IEEE80211_DEBUG +@@ -1386,6 +1493,7 @@ + escape_essid(target->ssid, + target->ssid_len), + MAC_ARG(target->bssid)); ++ ieee80211_network_reset(target); + } else { + /* Otherwise just pull from the free list */ + target = list_entry(ieee->network_free_list.next, +@@ -1402,6 +1510,7 @@ + "BEACON" : "PROBE RESPONSE"); + #endif + memcpy(target, &network, sizeof(*target)); ++ network.ibss_dfs = NULL; + list_add_tail(&target->list, &ieee->network_list); + } else { + IEEE80211_DEBUG_SCAN("Updating '%s' (" MAC_FMT ") via %s.\n", +@@ -1411,6 +1520,7 @@ + is_beacon(beacon->header.frame_ctl) ? + "BEACON" : "PROBE RESPONSE"); + update_network(target, &network); ++ network.ibss_dfs = NULL; + } + + spin_unlock_irqrestore(&ieee->lock, flags); +@@ -1495,10 +1605,43 @@ + header); + break; + ++ case IEEE80211_STYPE_ACTION: ++ IEEE80211_DEBUG_MGMT("ACTION\n"); ++ if (ieee->handle_action) ++ ieee->handle_action(ieee->dev, ++ (struct ieee80211_action *) ++ header, stats); ++ break; ++ ++ case IEEE80211_STYPE_REASSOC_REQ: ++ IEEE80211_DEBUG_MGMT("received reassoc (%d)\n", ++ WLAN_FC_GET_STYPE(le16_to_cpu ++ (header->frame_ctl))); ++ ++ IEEE80211_WARNING("%s: IEEE80211_REASSOC_REQ received\n", ++ ieee->dev->name); ++ if (ieee->handle_reassoc_request != NULL) ++ ieee->handle_reassoc_request(ieee->dev, ++ (struct ieee80211_reassoc_request *) ++ header); ++ break; ++ ++ case IEEE80211_STYPE_ASSOC_REQ: ++ IEEE80211_DEBUG_MGMT("received assoc (%d)\n", ++ WLAN_FC_GET_STYPE(le16_to_cpu ++ (header->frame_ctl))); ++ ++ IEEE80211_WARNING("%s: IEEE80211_ASSOC_REQ received\n", ++ ieee->dev->name); ++ if (ieee->handle_assoc_request != NULL) ++ ieee->handle_assoc_request(ieee->dev); ++ break; ++ + case IEEE80211_STYPE_DEAUTH: +- printk("DEAUTH from AP\n"); ++ IEEE80211_DEBUG_MGMT("DEAUTH\n"); + if (ieee->handle_deauth != NULL) +- ieee->handle_deauth(ieee->dev, (struct ieee80211_auth *) ++ ieee->handle_deauth(ieee->dev, ++ (struct ieee80211_deauth *) + header); + break; + default: +diff -Nur linux-2.6.16/net/ieee80211/ieee80211_tx.c linux-2.6.16-bcm43xx/net/ieee80211/ieee80211_tx.c +--- linux-2.6.16/net/ieee80211/ieee80211_tx.c 2006-03-20 06:53:29.000000000 +0100 ++++ linux-2.6.16-bcm43xx/net/ieee80211/ieee80211_tx.c 2006-03-28 22:16:14.000000000 +0200 +@@ -56,7 +56,18 @@ + `--------------------------------------------------| |------' + Total: 28 non-data bytes `----.----' + | +- .- 'Frame data' expands to <---------------------------' ++ .- 'Frame data' expands, if WEP enabled, to <----------' ++ | ++ V ++ ,-----------------------. ++Bytes | 4 | 0-2296 | 4 | ++ |-----|-----------|-----| ++Desc. | IV | Encrypted | ICV | ++ | | Packet | | ++ `-----| |-----' ++ `-----.-----' ++ | ++ .- 'Encrypted Packet' expands to + | + V + ,---------------------------------------------------. +@@ -65,18 +76,7 @@ + Desc. | SNAP | SNAP | Control |Eth Tunnel| Type | IP | + | DSAP | SSAP | | | | Packet | + | 0xAA | 0xAA |0x03 (UI)|0x00-00-F8| | | +- `-----------------------------------------| | +-Total: 8 non-data bytes `----.----' +- | +- .- 'IP Packet' expands, if WEP enabled, to <--' +- | +- V +- ,-----------------------. +-Bytes | 4 | 0-2296 | 4 | +- |-----|-----------|-----| +-Desc. | IV | Encrypted | ICV | +- | | IP Packet | | +- `-----------------------' ++ `---------------------------------------------------- + Total: 8 non-data bytes + + 802.3 Ethernet Data Frame +@@ -470,7 +470,9 @@ + atomic_inc(&crypt->refcnt); + if (crypt->ops->build_iv) + crypt->ops->build_iv(skb_frag, hdr_len, +- crypt->priv); ++ ieee->sec.keys[ieee->sec.active_key], ++ ieee->sec.key_sizes[ieee->sec.active_key], ++ crypt->priv); + atomic_dec(&crypt->refcnt); + } + +diff -Nur linux-2.6.16/net/ieee80211/ieee80211_wx.c linux-2.6.16-bcm43xx/net/ieee80211/ieee80211_wx.c +--- linux-2.6.16/net/ieee80211/ieee80211_wx.c 2006-03-20 06:53:29.000000000 +0100 ++++ linux-2.6.16-bcm43xx/net/ieee80211/ieee80211_wx.c 2006-03-28 22:16:14.000000000 +0200 +@@ -149,9 +149,7 @@ + iwe.u.qual.updated |= IW_QUAL_QUAL_INVALID | + IW_QUAL_LEVEL_INVALID; + iwe.u.qual.qual = 0; +- iwe.u.qual.level = 0; + } else { +- iwe.u.qual.level = network->stats.rssi; + if (ieee->perfect_rssi == ieee->worst_rssi) + iwe.u.qual.qual = 100; + else +@@ -179,6 +177,13 @@ + iwe.u.qual.noise = network->stats.noise; + } + ++ if (!(network->stats.mask & IEEE80211_STATMASK_SIGNAL)) { ++ iwe.u.qual.updated |= IW_QUAL_LEVEL_INVALID; ++ iwe.u.qual.level = 0; ++ } else { ++ iwe.u.qual.level = network->stats.signal; ++ } ++ + start = iwe_stream_add_event(start, stop, &iwe, IW_EV_QUAL_LEN); + + iwe.cmd = IWEVCUSTOM; +@@ -188,33 +193,21 @@ + if (iwe.u.data.length) + start = iwe_stream_add_point(start, stop, &iwe, custom); + ++ memset(&iwe, 0, sizeof(iwe)); + if (network->wpa_ie_len) { +- char buf[MAX_WPA_IE_LEN * 2 + 30]; +- +- u8 *p = buf; +- p += sprintf(p, "wpa_ie="); +- for (i = 0; i < network->wpa_ie_len; i++) { +- p += sprintf(p, "%02x", network->wpa_ie[i]); +- } +- +- memset(&iwe, 0, sizeof(iwe)); +- iwe.cmd = IWEVCUSTOM; +- iwe.u.data.length = strlen(buf); ++ char buf[MAX_WPA_IE_LEN]; ++ memcpy(buf, network->wpa_ie, network->wpa_ie_len); ++ iwe.cmd = IWEVGENIE; ++ iwe.u.data.length = network->wpa_ie_len; + start = iwe_stream_add_point(start, stop, &iwe, buf); + } + ++ memset(&iwe, 0, sizeof(iwe)); + if (network->rsn_ie_len) { +- char buf[MAX_WPA_IE_LEN * 2 + 30]; +- +- u8 *p = buf; +- p += sprintf(p, "rsn_ie="); +- for (i = 0; i < network->rsn_ie_len; i++) { +- p += sprintf(p, "%02x", network->rsn_ie[i]); +- } +- +- memset(&iwe, 0, sizeof(iwe)); +- iwe.cmd = IWEVCUSTOM; +- iwe.u.data.length = strlen(buf); ++ char buf[MAX_WPA_IE_LEN]; ++ memcpy(buf, network->rsn_ie, network->rsn_ie_len); ++ iwe.cmd = IWEVGENIE; ++ iwe.u.data.length = network->rsn_ie_len; + start = iwe_stream_add_point(start, stop, &iwe, buf); + } + +@@ -229,6 +222,28 @@ + if (iwe.u.data.length) + start = iwe_stream_add_point(start, stop, &iwe, custom); + ++ /* Add spectrum management information */ ++ iwe.cmd = -1; ++ p = custom; ++ p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), " Channel flags: "); ++ ++ if (ieee80211_get_channel_flags(ieee, network->channel) & ++ IEEE80211_CH_INVALID) { ++ iwe.cmd = IWEVCUSTOM; ++ p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), "INVALID "); ++ } ++ ++ if (ieee80211_get_channel_flags(ieee, network->channel) & ++ IEEE80211_CH_RADAR_DETECT) { ++ iwe.cmd = IWEVCUSTOM; ++ p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), "DFS "); ++ } ++ ++ if (iwe.cmd == IWEVCUSTOM) { ++ iwe.u.data.length = p - custom; ++ start = iwe_stream_add_point(start, stop, &iwe, custom); ++ } ++ + return start; + } + +@@ -734,9 +749,98 @@ + return 0; + } + ++int ieee80211_wx_set_auth(struct net_device *dev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, ++ char *extra) ++{ ++ struct ieee80211_device *ieee = netdev_priv(dev); ++ unsigned long flags; ++ int err = 0; ++ ++ spin_lock_irqsave(&ieee->lock, flags); ++ ++ switch (wrqu->param.flags & IW_AUTH_INDEX) { ++ case IW_AUTH_WPA_VERSION: ++ case IW_AUTH_CIPHER_PAIRWISE: ++ case IW_AUTH_CIPHER_GROUP: ++ case IW_AUTH_KEY_MGMT: ++ /* ++ * Host AP driver does not use these parameters and allows ++ * wpa_supplicant to control them internally. ++ */ ++ break; ++ case IW_AUTH_TKIP_COUNTERMEASURES: ++ break; /* FIXME */ ++ case IW_AUTH_DROP_UNENCRYPTED: ++ ieee->drop_unencrypted = !!wrqu->param.value; ++ break; ++ case IW_AUTH_80211_AUTH_ALG: ++ break; /* FIXME */ ++ case IW_AUTH_WPA_ENABLED: ++ ieee->privacy_invoked = ieee->wpa_enabled = !!wrqu->param.value; ++ break; ++ case IW_AUTH_RX_UNENCRYPTED_EAPOL: ++ ieee->ieee802_1x = !!wrqu->param.value; ++ break; ++ case IW_AUTH_PRIVACY_INVOKED: ++ ieee->privacy_invoked = !!wrqu->param.value; ++ break; ++ default: ++ err = -EOPNOTSUPP; ++ break; ++ } ++ spin_unlock_irqrestore(&ieee->lock, flags); ++ return err; ++} ++ ++int ieee80211_wx_get_auth(struct net_device *dev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, ++ char *extra) ++{ ++ struct ieee80211_device *ieee = netdev_priv(dev); ++ unsigned long flags; ++ int err = 0; ++ ++ spin_lock_irqsave(&ieee->lock, flags); ++ ++ switch (wrqu->param.flags & IW_AUTH_INDEX) { ++ case IW_AUTH_WPA_VERSION: ++ case IW_AUTH_CIPHER_PAIRWISE: ++ case IW_AUTH_CIPHER_GROUP: ++ case IW_AUTH_KEY_MGMT: ++ case IW_AUTH_TKIP_COUNTERMEASURES: /* FIXME */ ++ case IW_AUTH_80211_AUTH_ALG: /* FIXME */ ++ /* ++ * Host AP driver does not use these parameters and allows ++ * wpa_supplicant to control them internally. ++ */ ++ err = -EOPNOTSUPP; ++ break; ++ case IW_AUTH_DROP_UNENCRYPTED: ++ wrqu->param.value = ieee->drop_unencrypted; ++ break; ++ case IW_AUTH_WPA_ENABLED: ++ wrqu->param.value = ieee->wpa_enabled; ++ break; ++ case IW_AUTH_RX_UNENCRYPTED_EAPOL: ++ wrqu->param.value = ieee->ieee802_1x; ++ break; ++ default: ++ err = -EOPNOTSUPP; ++ break; ++ } ++ spin_unlock_irqrestore(&ieee->lock, flags); ++ return err; ++} ++ + EXPORT_SYMBOL(ieee80211_wx_set_encodeext); + EXPORT_SYMBOL(ieee80211_wx_get_encodeext); + + EXPORT_SYMBOL(ieee80211_wx_get_scan); + EXPORT_SYMBOL(ieee80211_wx_set_encode); + EXPORT_SYMBOL(ieee80211_wx_get_encode); ++ ++EXPORT_SYMBOL_GPL(ieee80211_wx_set_auth); ++EXPORT_SYMBOL_GPL(ieee80211_wx_get_auth); +diff -Nur linux-2.6.16/net/Kconfig linux-2.6.16-bcm43xx/net/Kconfig +--- linux-2.6.16/net/Kconfig 2006-03-20 06:53:29.000000000 +0100 ++++ linux-2.6.16-bcm43xx/net/Kconfig 2006-03-28 22:16:14.000000000 +0200 +@@ -222,8 +222,12 @@ + source "net/ax25/Kconfig" + source "net/irda/Kconfig" + source "net/bluetooth/Kconfig" ++source "net/d80211/Kconfig" + source "net/ieee80211/Kconfig" + ++config WIRELESS_EXT ++ bool ++ + endif # if NET + endmenu # Networking + +diff -Nur linux-2.6.16/net/Makefile linux-2.6.16-bcm43xx/net/Makefile +--- linux-2.6.16/net/Makefile 2006-03-20 06:53:29.000000000 +0100 ++++ linux-2.6.16-bcm43xx/net/Makefile 2006-03-28 22:16:14.000000000 +0200 +@@ -44,6 +44,7 @@ + obj-$(CONFIG_VLAN_8021Q) += 8021q/ + obj-$(CONFIG_IP_DCCP) += dccp/ + obj-$(CONFIG_IP_SCTP) += sctp/ ++obj-$(CONFIG_D80211) += d80211/ + obj-$(CONFIG_IEEE80211) += ieee80211/ + obj-$(CONFIG_TIPC) += tipc/ + +diff -Nur linux-2.6.16/net/socket.c linux-2.6.16-bcm43xx/net/socket.c +--- linux-2.6.16/net/socket.c 2006-03-20 06:53:29.000000000 +0100 ++++ linux-2.6.16-bcm43xx/net/socket.c 2006-03-28 22:16:14.000000000 +0200 +@@ -84,10 +84,7 @@ + #include <linux/compat.h> + #include <linux/kmod.h> + #include <linux/audit.h> +- +-#ifdef CONFIG_NET_RADIO +-#include <linux/wireless.h> /* Note : will define WIRELESS_EXT */ +-#endif /* CONFIG_NET_RADIO */ ++#include <linux/wireless.h> + + #include <asm/uaccess.h> + #include <asm/unistd.h> +@@ -840,11 +837,11 @@ + if (cmd >= SIOCDEVPRIVATE && cmd <= (SIOCDEVPRIVATE + 15)) { + err = dev_ioctl(cmd, argp); + } else +-#ifdef WIRELESS_EXT ++#ifdef CONFIG_WIRELESS_EXT + if (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) { + err = dev_ioctl(cmd, argp); + } else +-#endif /* WIRELESS_EXT */ ++#endif /* CONFIG_WIRELESS_EXT */ + switch (cmd) { + case FIOSETOWN: + case SIOCSPGRP: +diff -Nur linux-2.6.16/scripts/bcm43xx-d80211-sta_up.sh linux-2.6.16-bcm43xx/scripts/bcm43xx-d80211-sta_up.sh +--- linux-2.6.16/scripts/bcm43xx-d80211-sta_up.sh 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.16-bcm43xx/scripts/bcm43xx-d80211-sta_up.sh 2006-03-28 22:16:14.000000000 +0200 +@@ -0,0 +1,80 @@ ++#!/bin/bash ++ ++if [ "$1" == "--help" ] || [ "$1" == "-h" ]; then ++ echo "$0, an ugly script to configure and bring up a STA (802.11 station)" ++ echo "device for the linux devicescape 802.11 stack." ++ echo ++ echo "Usage:" ++ echo "$0 [ wlan_device local_ip wpasupplicant_config ]" ++ echo ++ echo "Examples:" ++ echo "Run with default parameters: $0" ++ echo "Manually define parameters: $0 wlan0 192.168.1.1 ./wpasupp.conf" ++ echo ++ echo "Default parameters are: $0 wlan0 192.168.1.101 /etc/wpa_supplicant.conf" ++ exit 1 ++fi ++ ++wlan_dev="$1" ++ip_addr="$2" ++wpasupp_conf="$3" ++ ++if [ -z "$wlan_dev" ]; then ++ wlan_dev="wlan0" ++fi ++if [ -z "$sta_dev" ]; then ++ sta_dev="sta0" ++fi ++if [ -z "$ip_addr" ]; then ++ ip_addr="192.168.1.101" ++fi ++if [ -z "$wpasupp_conf" ]; then ++ wpasupp_conf="/etc/wpa_supplicant.conf" ++fi ++ ++idx=$(echo $wlan_dev | awk '{ gsub("[^0-9]", "", $0); printf($0); }') ++if [ -z "$idx" ]; then ++ echo "Invalid wlan_device parameter \"$wlan_dev\". Example: wlan0" ++ exit 1 ++fi ++sta_dev="sta$idx" ++phy_dev="phy$idx" ++ ++function run() ++{ ++ echo "$@" ++ $@ ++ res=$? ++ if [ $res -ne 0 ]; then ++ echo "FAILED ($res)" ++ exit 1 ++ fi ++} ++ ++if [ -z "$(grep -e bcm43xx /proc/modules)" ]; then ++ echo "ERROR: bcm43xx module not loaded." ++ exit 1 ++fi ++ ++killall wpa_supplicant 2>/dev/null ++echo -n "$sta_dev" > /sys/class/ieee80211/${phy_dev}/add_iface ++if [ $? -ne 0 ]; then ++ echo "ERROR: Could not add STA device." ++ exit 1 ++fi ++run iwconfig $wlan_dev.11 mode managed ++run ifconfig $wlan_dev.11 up ++ ++hwaddr="$(ifconfig | grep $wlan_dev.11 | awk '{print $NF}')" ++run ifconfig $sta_dev hw ether $hwaddr ++run ifconfig $sta_dev $ip_addr ++run ifconfig $sta_dev up ++run iwconfig $sta_dev mode managed ++ ++run wpa_supplicant -B -Dwext -i$sta_dev -c$wpasupp_conf ++ ++echo ++echo "You may want to set the default route, now:" ++echo " route add default gw GATEWAY_IP_ADDRESS" ++ ++exit 0 |