diff options
Diffstat (limited to 'drivers/net/wireless')
66 files changed, 22993 insertions, 255 deletions
diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig index 4426841b2be..c4b3dc291a5 100644 --- a/drivers/net/wireless/Kconfig +++ b/drivers/net/wireless/Kconfig @@ -265,6 +265,19 @@ config IPW2200_DEBUG If you are not sure, say N here. +config LIBERTAS_USB + tristate "Marvell Libertas 8388 802.11a/b/g cards" + depends on NET_RADIO && USB + select FW_LOADER + ---help--- + A driver for Marvell Libertas 8388 USB devices. + +config LIBERTAS_USB_DEBUG + bool "Enable full debugging output in the Libertas USB module." + depends on LIBERTAS_USB + ---help--- + Debugging support. + config AIRO tristate "Cisco/Aironet 34X/35X/4500/4800 ISA and PCI cards" depends on ISA_DMA_API && WLAN_80211 && (PCI || BROKEN) diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile index c613af17a15..d2124602263 100644 --- a/drivers/net/wireless/Makefile +++ b/drivers/net/wireless/Makefile @@ -43,3 +43,4 @@ obj-$(CONFIG_PCMCIA_RAYCS) += ray_cs.o obj-$(CONFIG_PCMCIA_WL3501) += wl3501_cs.o obj-$(CONFIG_USB_ZD1201) += zd1201.o +obj-$(CONFIG_LIBERTAS_USB) += libertas/ diff --git a/drivers/net/wireless/README b/drivers/net/wireless/README deleted file mode 100644 index 0c274bf6d45..00000000000 --- a/drivers/net/wireless/README +++ /dev/null @@ -1,25 +0,0 @@ - README - ------ - - This directory is mostly for Wireless LAN drivers, in their -various incarnations (ISA, PCI, Pcmcia...). - This separate directory is needed because a lot of driver work -on different bus (typically PCI + Pcmcia) and share 95% of the -code. This allow the code and the config options to be in one single -place instead of scattered all over the driver tree, which is never -100% satisfactory. - - Note : if you want more info on the topic of Wireless LANs, -you are kindly invited to have a look at the Wireless Howto : - http://www.hpl.hp.com/personal/Jean_Tourrilhes/Linux/ - Some Wireless LAN drivers, like orinoco_cs, require the use of -Wireless Tools to be configured : - http://www.hpl.hp.com/personal/Jean_Tourrilhes/Linux/Tools.html - - Special notes for distribution maintainers : - 1) wvlan_cs will be discontinued soon in favor of orinoco_cs - 2) Please add Wireless Tools support in your scripts - - Have fun... - - Jean diff --git a/drivers/net/wireless/airo.c b/drivers/net/wireless/airo.c index 7fe0a61091a..f21bbafcb72 100644 --- a/drivers/net/wireless/airo.c +++ b/drivers/net/wireless/airo.c @@ -1145,6 +1145,7 @@ static void airo_networks_free(struct airo_info *ai); struct airo_info { struct net_device_stats stats; struct net_device *dev; + struct list_head dev_list; /* Note, we can have MAX_FIDS outstanding. FIDs are 16-bits, so we use the high bit to mark whether it is in use. */ #define MAX_FIDS 6 @@ -2360,6 +2361,21 @@ static int airo_change_mtu(struct net_device *dev, int new_mtu) return 0; } +static LIST_HEAD(airo_devices); + +static void add_airo_dev(struct airo_info *ai) +{ + /* Upper layers already keep track of PCI devices, + * so we only need to remember our non-PCI cards. */ + if (!ai->pci) + list_add_tail(&ai->dev_list, &airo_devices); +} + +static void del_airo_dev(struct airo_info *ai) +{ + if (!ai->pci) + list_del(&ai->dev_list); +} static int airo_close(struct net_device *dev) { struct airo_info *ai = dev->priv; @@ -2381,8 +2397,6 @@ static int airo_close(struct net_device *dev) { return 0; } -static void del_airo_dev( struct net_device *dev ); - void stop_airo_card( struct net_device *dev, int freeres ) { struct airo_info *ai = dev->priv; @@ -2434,14 +2448,12 @@ void stop_airo_card( struct net_device *dev, int freeres ) } } crypto_free_cipher(ai->tfm); - del_airo_dev( dev ); + del_airo_dev(ai); free_netdev( dev ); } EXPORT_SYMBOL(stop_airo_card); -static int add_airo_dev( struct net_device *dev ); - static int wll_header_parse(struct sk_buff *skb, unsigned char *haddr) { memcpy(haddr, skb_mac_header(skb) + 10, ETH_ALEN); @@ -2740,8 +2752,6 @@ static int airo_networks_allocate(struct airo_info *ai) static void airo_networks_free(struct airo_info *ai) { - if (!ai->networks) - return; kfree(ai->networks); ai->networks = NULL; } @@ -2816,12 +2826,10 @@ static struct net_device *_init_airo_card( unsigned short irq, int port, if (IS_ERR(ai->airo_thread_task)) goto err_out_free; ai->tfm = NULL; - rc = add_airo_dev( dev ); - if (rc) - goto err_out_thr; + add_airo_dev(ai); if (airo_networks_allocate (ai)) - goto err_out_unlink; + goto err_out_thr; airo_networks_initialize (ai); /* The Airo-specific entries in the device structure. */ @@ -2937,9 +2945,8 @@ err_out_irq: free_irq(dev->irq, dev); err_out_nets: airo_networks_free(ai); -err_out_unlink: - del_airo_dev(dev); err_out_thr: + del_airo_dev(ai); set_bit(JOB_DIE, &ai->jobs); kthread_stop(ai->airo_thread_task); err_out_free: @@ -5535,11 +5542,6 @@ static int proc_close( struct inode *inode, struct file *file ) return 0; } -static struct net_device_list { - struct net_device *dev; - struct net_device_list *next; -} *airo_devices; - /* Since the card doesn't automatically switch to the right WEP mode, we will make it do it. If the card isn't associated, every secs we will switch WEP modes to see if that will help. If the card is @@ -5582,26 +5584,6 @@ static void timer_func( struct net_device *dev ) { apriv->expires = RUN_AT(HZ*3); } -static int add_airo_dev( struct net_device *dev ) { - struct net_device_list *node = kmalloc( sizeof( *node ), GFP_KERNEL ); - if ( !node ) - return -ENOMEM; - - node->dev = dev; - node->next = airo_devices; - airo_devices = node; - - return 0; -} - -static void del_airo_dev( struct net_device *dev ) { - struct net_device_list **p = &airo_devices; - while( *p && ( (*p)->dev != dev ) ) - p = &(*p)->next; - if ( *p && (*p)->dev == dev ) - *p = (*p)->next; -} - #ifdef CONFIG_PCI static int __devinit airo_pci_probe(struct pci_dev *pdev, const struct pci_device_id *pent) @@ -5625,6 +5607,10 @@ static int __devinit airo_pci_probe(struct pci_dev *pdev, static void __devexit airo_pci_remove(struct pci_dev *pdev) { + struct net_device *dev = pci_get_drvdata(pdev); + + airo_print_info(dev->name, "Unregistering..."); + stop_airo_card(dev, 1); } static int airo_pci_suspend(struct pci_dev *pdev, pm_message_t state) @@ -5750,9 +5736,11 @@ static int __init airo_init_module( void ) static void __exit airo_cleanup_module( void ) { - while( airo_devices ) { - airo_print_info(airo_devices->dev->name, "Unregistering...\n"); - stop_airo_card( airo_devices->dev, 1 ); + struct airo_info *ai; + while(!list_empty(&airo_devices)) { + ai = list_entry(airo_devices.next, struct airo_info, dev_list); + airo_print_info(ai->dev->name, "Unregistering..."); + stop_airo_card(ai->dev, 1); } #ifdef CONFIG_PCI pci_unregister_driver(&airo_driver); diff --git a/drivers/net/wireless/bcm43xx/bcm43xx.h b/drivers/net/wireless/bcm43xx/bcm43xx.h index 95ff175d8f3..f8483c179e4 100644 --- a/drivers/net/wireless/bcm43xx/bcm43xx.h +++ b/drivers/net/wireless/bcm43xx/bcm43xx.h @@ -277,11 +277,14 @@ #define BCM43xx_SBTMSTATELOW_REJECT 0x02 #define BCM43xx_SBTMSTATELOW_CLOCK 0x10000 #define BCM43xx_SBTMSTATELOW_FORCE_GATE_CLOCK 0x20000 +#define BCM43xx_SBTMSTATELOW_G_MODE_ENABLE 0x20000000 /* sbtmstatehigh state flags */ #define BCM43xx_SBTMSTATEHIGH_SERROR 0x00000001 #define BCM43xx_SBTMSTATEHIGH_BUSY 0x00000004 #define BCM43xx_SBTMSTATEHIGH_TIMEOUT 0x00000020 +#define BCM43xx_SBTMSTATEHIGH_G_PHY_AVAIL 0x00010000 +#define BCM43xx_SBTMSTATEHIGH_A_PHY_AVAIL 0x00020000 #define BCM43xx_SBTMSTATEHIGH_COREFLAGS 0x1FFF0000 #define BCM43xx_SBTMSTATEHIGH_DMA64BIT 0x10000000 #define BCM43xx_SBTMSTATEHIGH_GATEDCLK 0x20000000 diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_ethtool.c b/drivers/net/wireless/bcm43xx/bcm43xx_ethtool.c index c947025d655..d2df6a0100a 100644 --- a/drivers/net/wireless/bcm43xx/bcm43xx_ethtool.c +++ b/drivers/net/wireless/bcm43xx/bcm43xx_ethtool.c @@ -32,7 +32,7 @@ #include <linux/netdevice.h> #include <linux/pci.h> #include <linux/string.h> -#include <linux/utsrelease.h> +#include <linux/utsname.h> static void bcm43xx_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) @@ -40,7 +40,7 @@ static void bcm43xx_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo * 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->version, utsname()->release, sizeof(info->version)); strncpy(info->bus_info, pci_name(bcm->pci_dev), ETHTOOL_BUSINFO_LEN); } diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_main.c b/drivers/net/wireless/bcm43xx/bcm43xx_main.c index a38e7eec0e6..5e96bca6730 100644 --- a/drivers/net/wireless/bcm43xx/bcm43xx_main.c +++ b/drivers/net/wireless/bcm43xx/bcm43xx_main.c @@ -1407,7 +1407,7 @@ void bcm43xx_wireless_core_reset(struct bcm43xx_private *bcm, int connect_phy) & ~(BCM43xx_SBF_MAC_ENABLED | 0x00000002)); } else { if (connect_phy) - flags |= 0x20000000; + flags |= BCM43xx_SBTMSTATELOW_G_MODE_ENABLE; bcm43xx_phy_connect(bcm, connect_phy); bcm43xx_core_enable(bcm, flags); bcm43xx_write16(bcm, 0x03E6, 0x0000); @@ -3604,7 +3604,7 @@ int bcm43xx_select_wireless_core(struct bcm43xx_private *bcm, u32 sbtmstatelow; sbtmstatelow = bcm43xx_read32(bcm, BCM43xx_CIR_SBTMSTATELOW); - sbtmstatelow |= 0x20000000; + sbtmstatelow |= BCM43xx_SBTMSTATELOW_G_MODE_ENABLE; bcm43xx_write32(bcm, BCM43xx_CIR_SBTMSTATELOW, sbtmstatelow); } err = wireless_core_up(bcm, 1); diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_phy.c b/drivers/net/wireless/bcm43xx/bcm43xx_phy.c index 72529a440f1..b37f1e34870 100644 --- a/drivers/net/wireless/bcm43xx/bcm43xx_phy.c +++ b/drivers/net/wireless/bcm43xx/bcm43xx_phy.c @@ -168,16 +168,16 @@ int bcm43xx_phy_connect(struct bcm43xx_private *bcm, int connect) flags = bcm43xx_read32(bcm, BCM43xx_CIR_SBTMSTATEHIGH); if (connect) { - if (!(flags & 0x00010000)) + if (!(flags & BCM43xx_SBTMSTATEHIGH_G_PHY_AVAIL)) return -ENODEV; flags = bcm43xx_read32(bcm, BCM43xx_CIR_SBTMSTATELOW); - flags |= (0x800 << 18); + flags |= BCM43xx_SBTMSTATELOW_G_MODE_ENABLE; bcm43xx_write32(bcm, BCM43xx_CIR_SBTMSTATELOW, flags); } else { - if (!(flags & 0x00020000)) + if (!(flags & BCM43xx_SBTMSTATEHIGH_A_PHY_AVAIL)) return -ENODEV; flags = bcm43xx_read32(bcm, BCM43xx_CIR_SBTMSTATELOW); - flags &= ~(0x800 << 18); + flags &= ~BCM43xx_SBTMSTATELOW_G_MODE_ENABLE; bcm43xx_write32(bcm, BCM43xx_CIR_SBTMSTATELOW, flags); } out: @@ -300,16 +300,20 @@ static void bcm43xx_phy_agcsetup(struct bcm43xx_private *bcm) 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, 0x0420, (bcm43xx_phy_read(bcm, 0x0420) + & 0x0FFF) | 0x3000); } - bcm43xx_phy_write(bcm, 0x04A8, (bcm43xx_phy_read(bcm, 0x04A8) & 0x8080) | 0x7874); + 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, 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, 0x048C, (bcm43xx_phy_read(bcm, 0x048C) + & 0xFF00) | 0x001E); bcm43xx_phy_write(bcm, 0x048D, 0x0002); } @@ -335,7 +339,8 @@ static void bcm43xx_phy_setupg(struct bcm43xx_private *bcm) 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_read(bcm, BCM43xx_PHY_G_CRS) + & 0xFC3F) | 0x0340); bcm43xx_phy_write(bcm, 0x042C, 0x005A); bcm43xx_phy_write(bcm, 0x0427, 0x001A); diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_phy.h b/drivers/net/wireless/bcm43xx/bcm43xx_phy.h index 1f321ef42be..73118364b55 100644 --- a/drivers/net/wireless/bcm43xx/bcm43xx_phy.h +++ b/drivers/net/wireless/bcm43xx/bcm43xx_phy.h @@ -48,6 +48,10 @@ void bcm43xx_raw_phy_unlock(struct bcm43xx_private *bcm); local_irq_restore(flags); \ } while (0) +/* Card uses the loopback gain stuff */ +#define has_loopback_gain(phy) \ + (((phy)->rev > 1) || ((phy)->connected)) + u16 bcm43xx_phy_read(struct bcm43xx_private *bcm, u16 offset); void bcm43xx_phy_write(struct bcm43xx_private *bcm, u16 offset, u16 val); diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_radio.c b/drivers/net/wireless/bcm43xx/bcm43xx_radio.c index 4025dd0089d..6a109f4a1b7 100644 --- a/drivers/net/wireless/bcm43xx/bcm43xx_radio.c +++ b/drivers/net/wireless/bcm43xx/bcm43xx_radio.c @@ -1343,11 +1343,110 @@ u16 bcm43xx_radio_calibrationvalue(struct bcm43xx_private *bcm) return ret; } +#define LPD(L, P, D) (((L) << 2) | ((P) << 1) | ((D) << 0)) +static u16 bcm43xx_get_812_value(struct bcm43xx_private *bcm, u8 lpd) +{ + struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); + struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm); + u16 loop_or = 0; + u16 adj_loopback_gain = phy->loopback_gain[0]; + u8 loop; + u16 extern_lna_control; + + if (!phy->connected) + return 0; + if (!has_loopback_gain(phy)) { + if (phy->rev < 7 || !(bcm->sprom.boardflags + & BCM43xx_BFL_EXTLNA)) { + switch (lpd) { + case LPD(0, 1, 1): + return 0x0FB2; + case LPD(0, 0, 1): + return 0x00B2; + case LPD(1, 0, 1): + return 0x30B2; + case LPD(1, 0, 0): + return 0x30B3; + default: + assert(0); + } + } else { + switch (lpd) { + case LPD(0, 1, 1): + return 0x8FB2; + case LPD(0, 0, 1): + return 0x80B2; + case LPD(1, 0, 1): + return 0x20B2; + case LPD(1, 0, 0): + return 0x20B3; + default: + assert(0); + } + } + } else { + if (radio->revision == 8) + adj_loopback_gain += 0x003E; + else + adj_loopback_gain += 0x0026; + if (adj_loopback_gain >= 0x46) { + adj_loopback_gain -= 0x46; + extern_lna_control = 0x3000; + } else if (adj_loopback_gain >= 0x3A) { + adj_loopback_gain -= 0x3A; + extern_lna_control = 0x2000; + } else if (adj_loopback_gain >= 0x2E) { + adj_loopback_gain -= 0x2E; + extern_lna_control = 0x1000; + } else { + adj_loopback_gain -= 0x10; + extern_lna_control = 0x0000; + } + for (loop = 0; loop < 16; loop++) { + u16 tmp = adj_loopback_gain - 6 * loop; + if (tmp < 6) + break; + } + + loop_or = (loop << 8) | extern_lna_control; + if (phy->rev >= 7 && bcm->sprom.boardflags + & BCM43xx_BFL_EXTLNA) { + if (extern_lna_control) + loop_or |= 0x8000; + switch (lpd) { + case LPD(0, 1, 1): + return 0x8F92; + case LPD(0, 0, 1): + return (0x8092 | loop_or); + case LPD(1, 0, 1): + return (0x2092 | loop_or); + case LPD(1, 0, 0): + return (0x2093 | loop_or); + default: + assert(0); + } + } else { + switch (lpd) { + case LPD(0, 1, 1): + return 0x0F92; + case LPD(0, 0, 1): + case LPD(1, 0, 1): + return (0x0092 | loop_or); + case LPD(1, 0, 0): + return (0x0093 | loop_or); + default: + assert(0); + } + } + } + return 0; +} + 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 backup[21] = { 0 }; u16 ret; u16 i, j; u32 tmp1 = 0, tmp2 = 0; @@ -1373,19 +1472,36 @@ u16 bcm43xx_radio_init2050(struct bcm43xx_private *bcm) 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_read(bcm, 0x0814) + | 0x0003)); bcm43xx_phy_write(bcm, 0x0815, - (bcm43xx_phy_read(bcm, 0x0815) & 0xFFFC)); + (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_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); + if (phy->rev > 1) { /* loopback gain enabled */ + backup[19] = bcm43xx_phy_read(bcm, 0x080F); + backup[20] = bcm43xx_phy_read(bcm, 0x0810); + 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, 0x0812, + bcm43xx_get_812_value(bcm, LPD(0, 1, 1))); + if (phy->rev < 7 || !(bcm->sprom.boardflags + & BCM43xx_BFL_EXTLNA)) + bcm43xx_phy_write(bcm, 0x0811, 0x01B3); + else + bcm43xx_phy_write(bcm, 0x0811, 0x09B3); } - bcm43xx_write16(bcm, BCM43xx_MMIO_PHY_RADIO, - (bcm43xx_read16(bcm, BCM43xx_MMIO_PHY_RADIO) | 0x8000)); } + 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)); @@ -1397,10 +1513,12 @@ u16 bcm43xx_radio_init2050(struct bcm43xx_private *bcm) bcm43xx_write16(bcm, 0x03E6, 0x0122); } else { if (phy->analog >= 2) - bcm43xx_phy_write(bcm, 0x0003, (bcm43xx_phy_read(bcm, 0x0003) - & 0xFFBF) | 0x0040); + bcm43xx_phy_write(bcm, 0x0003, + (bcm43xx_phy_read(bcm, 0x0003) + & 0xFFBF) | 0x0040); bcm43xx_write16(bcm, BCM43xx_MMIO_CHANNEL_EXT, - (bcm43xx_read16(bcm, BCM43xx_MMIO_CHANNEL_EXT) | 0x2000)); + (bcm43xx_read16(bcm, BCM43xx_MMIO_CHANNEL_EXT) + | 0x2000)); } ret = bcm43xx_radio_calibrationvalue(bcm); @@ -1408,16 +1526,25 @@ u16 bcm43xx_radio_init2050(struct bcm43xx_private *bcm) if (phy->type == BCM43xx_PHYTYPE_B) bcm43xx_radio_write16(bcm, 0x0078, 0x0026); + if (phy->connected) + bcm43xx_phy_write(bcm, 0x0812, + bcm43xx_get_812_value(bcm, LPD(0, 1, 1))); 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, 0x0812, + bcm43xx_get_812_value(bcm, LPD(0, 0, 1))); 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) & 0xFFF0) | 0x0009); + if (radio->revision == 8) + bcm43xx_radio_write16(bcm, 0x0043, 0x001F); + else { + bcm43xx_radio_write16(bcm, 0x0052, 0x0000); + bcm43xx_radio_write16(bcm, 0x0043, + (bcm43xx_radio_read16(bcm, 0x0043) & 0xFFF0) + | 0x0009); + } bcm43xx_phy_write(bcm, 0x0058, 0x0000); for (i = 0; i < 16; i++) { @@ -1425,21 +1552,25 @@ u16 bcm43xx_radio_init2050(struct bcm43xx_private *bcm) 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, 0x0812, + bcm43xx_get_812_value(bcm, LPD(1, 0, 1))); bcm43xx_phy_write(bcm, 0x0015, 0xAFB0); udelay(10); if (phy->connected) - bcm43xx_phy_write(bcm, 0x0812, 0x30B2); + bcm43xx_phy_write(bcm, 0x0812, + bcm43xx_get_812_value(bcm, LPD(1, 0, 1))); bcm43xx_phy_write(bcm, 0x0015, 0xEFB0); udelay(10); if (phy->connected) - bcm43xx_phy_write(bcm, 0x0812, 0x30B2); + bcm43xx_phy_write(bcm, 0x0812, + bcm43xx_get_812_value(bcm, LPD(1, 0, 0))); bcm43xx_phy_write(bcm, 0x0015, 0xFFF0); - udelay(10); + udelay(20); 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, 0x0812, + bcm43xx_get_812_value(bcm, LPD(1, 0, 1))); bcm43xx_phy_write(bcm, 0x0015, 0xAFB0); } @@ -1457,21 +1588,29 @@ u16 bcm43xx_radio_init2050(struct bcm43xx_private *bcm) 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, 0x0812, + bcm43xx_get_812_value(bcm, + LPD(1, 0, 1))); bcm43xx_phy_write(bcm, 0x0015, 0xAFB0); udelay(10); if (phy->connected) - bcm43xx_phy_write(bcm, 0x0812, 0x30B2); + bcm43xx_phy_write(bcm, 0x0812, + bcm43xx_get_812_value(bcm, + LPD(1, 0, 1))); 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, 0x0812, + bcm43xx_get_812_value(bcm, + LPD(1, 0, 0))); 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, 0x0812, + bcm43xx_get_812_value(bcm, + LPD(1, 0, 1))); bcm43xx_phy_write(bcm, 0x0015, 0xAFB0); } tmp2++; @@ -1497,15 +1636,20 @@ u16 bcm43xx_radio_init2050(struct bcm43xx_private *bcm) 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_write16(bcm, BCM43xx_MMIO_PHY_RADIO, + (bcm43xx_read16(bcm, + BCM43xx_MMIO_PHY_RADIO) & 0x7FFF)); 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 (phy->rev > 1) { + bcm43xx_phy_write(bcm, 0x080F, backup[19]); + bcm43xx_phy_write(bcm, 0x0810, backup[20]); + } } } if (i >= 15) diff --git a/drivers/net/wireless/hostap/hostap_ap.c b/drivers/net/wireless/hostap/hostap_ap.c index 4ca8a27b8c5..5b3abd54d0e 100644 --- a/drivers/net/wireless/hostap/hostap_ap.c +++ b/drivers/net/wireless/hostap/hostap_ap.c @@ -1,8 +1,8 @@ /* * Intersil Prism2 driver with Host AP (software access point) support * Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen - * <jkmaline@cc.hut.fi> - * Copyright (c) 2002-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * <j@w1.fi> + * Copyright (c) 2002-2005, Jouni Malinen <j@w1.fi> * * This file is to be included into hostap.c when S/W AP functionality is * compiled. diff --git a/drivers/net/wireless/hostap/hostap_common.h b/drivers/net/wireless/hostap/hostap_common.h index 01624005d80..b31e6a05f23 100644 --- a/drivers/net/wireless/hostap/hostap_common.h +++ b/drivers/net/wireless/hostap/hostap_common.h @@ -368,9 +368,9 @@ enum { #define PRISM2_HOSTAPD_MAX_BUF_SIZE 1024 #define PRISM2_HOSTAPD_RID_HDR_LEN \ -((int) (&((struct prism2_hostapd_param *) 0)->u.rid.data)) +offsetof(struct prism2_hostapd_param, u.rid.data) #define PRISM2_HOSTAPD_GENERIC_ELEMENT_HDR_LEN \ -((int) (&((struct prism2_hostapd_param *) 0)->u.generic_elem.data)) +offsetof(struct prism2_hostapd_param, u.generic_elem.data) /* Maximum length for algorithm names (-1 for nul termination) used in ioctl() */ diff --git a/drivers/net/wireless/hostap/hostap_cs.c b/drivers/net/wireless/hostap/hostap_cs.c index 8d8f4b9b8b0..ee1532b62e4 100644 --- a/drivers/net/wireless/hostap/hostap_cs.c +++ b/drivers/net/wireless/hostap/hostap_cs.c @@ -22,7 +22,7 @@ #include "hostap_wlan.h" -static char *version = PRISM2_VERSION " (Jouni Malinen <jkmaline@cc.hut.fi>)"; +static char *version = PRISM2_VERSION " (Jouni Malinen <j@w1.fi>)"; static dev_info_t dev_info = "hostap_cs"; MODULE_AUTHOR("Jouni Malinen"); @@ -838,6 +838,8 @@ static struct pcmcia_device_id hostap_cs_ids[] = { PCMCIA_DEVICE_MANF_CARD(0xd601, 0x0005), PCMCIA_DEVICE_MANF_CARD(0xd601, 0x0010), PCMCIA_DEVICE_MANF_CARD(0x0126, 0x0002), + PCMCIA_DEVICE_MANF_CARD_PROD_ID1(0xd601, 0x0005, "ADLINK 345 CF", + 0x2d858104), PCMCIA_DEVICE_MANF_CARD_PROD_ID1(0x0156, 0x0002, "INTERSIL", 0x74c5e40d), PCMCIA_DEVICE_MANF_CARD_PROD_ID1(0x0156, 0x0002, "Intersil", @@ -848,6 +850,11 @@ static struct pcmcia_device_id hostap_cs_ids[] = { "Intersil", "PRISM 2_5 PCMCIA ADAPTER", "ISL37300P", "Eval-RevA", 0x4b801a17, 0x6345a0bf, 0xc9049a39, 0xc23adc0e), + /* D-Link DWL-650 Rev. P1; manfid 0x000b, 0x7110 */ + PCMCIA_DEVICE_PROD_ID1234( + "D-Link", "DWL-650 Wireless PC Card RevP", "ISL37101P-10", + "A3", + 0x1a424a1c, 0x6ea57632, 0xdd97a26b, 0x56b21f52), PCMCIA_DEVICE_PROD_ID123( "Addtron", "AWP-100 Wireless PCMCIA", "Version 01.02", 0xe6ec52ce, 0x08649af2, 0x4b74baa0), diff --git a/drivers/net/wireless/hostap/hostap_hw.c b/drivers/net/wireless/hostap/hostap_hw.c index fb01fb95a9f..959887b70ca 100644 --- a/drivers/net/wireless/hostap/hostap_hw.c +++ b/drivers/net/wireless/hostap/hostap_hw.c @@ -3,8 +3,8 @@ * Intersil Prism2/2.5/3. * * Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen - * <jkmaline@cc.hut.fi> - * Copyright (c) 2002-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * <j@w1.fi> + * Copyright (c) 2002-2005, Jouni Malinen <j@w1.fi> * * 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 diff --git a/drivers/net/wireless/hostap/hostap_main.c b/drivers/net/wireless/hostap/hostap_main.c index 1f9edd91565..4743426cf6a 100644 --- a/drivers/net/wireless/hostap/hostap_main.c +++ b/drivers/net/wireless/hostap/hostap_main.c @@ -3,8 +3,8 @@ * Intersil Prism2/2.5/3 - hostap.o module, common routines * * Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen - * <jkmaline@cc.hut.fi> - * Copyright (c) 2002-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * <j@w1.fi> + * Copyright (c) 2002-2005, Jouni Malinen <j@w1.fi> * * 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 diff --git a/drivers/net/wireless/hostap/hostap_pci.c b/drivers/net/wireless/hostap/hostap_pci.c index c4f6020baa9..db4899ed4bb 100644 --- a/drivers/net/wireless/hostap/hostap_pci.c +++ b/drivers/net/wireless/hostap/hostap_pci.c @@ -20,7 +20,7 @@ #include "hostap_wlan.h" -static char *version = PRISM2_VERSION " (Jouni Malinen <jkmaline@cc.hut.fi>)"; +static char *version = PRISM2_VERSION " (Jouni Malinen <j@w1.fi>)"; static char *dev_info = "hostap_pci"; diff --git a/drivers/net/wireless/hostap/hostap_plx.c b/drivers/net/wireless/hostap/hostap_plx.c index e235e064789..f0fd5ecdb24 100644 --- a/drivers/net/wireless/hostap/hostap_plx.c +++ b/drivers/net/wireless/hostap/hostap_plx.c @@ -23,7 +23,7 @@ #include "hostap_wlan.h" -static char *version = PRISM2_VERSION " (Jouni Malinen <jkmaline@cc.hut.fi>)"; +static char *version = PRISM2_VERSION " (Jouni Malinen <j@w1.fi>)"; static char *dev_info = "hostap_plx"; diff --git a/drivers/net/wireless/ipw2100.c b/drivers/net/wireless/ipw2100.c index 9137a4dd02e..d51daf87450 100644 --- a/drivers/net/wireless/ipw2100.c +++ b/drivers/net/wireless/ipw2100.c @@ -28,8 +28,8 @@ Portions of this file are based on the Host AP project, Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen - <jkmaline@cc.hut.fi> - Copyright (c) 2002-2003, Jouni Malinen <jkmaline@cc.hut.fi> + <j@w1.fi> + Copyright (c) 2002-2003, Jouni Malinen <j@w1.fi> Portions of ipw2100_mod_firmware_load, ipw2100_do_mod_firmware_load, and ipw2100_fw_load are loosely based on drivers/sound/sound_firmware.c diff --git a/drivers/net/wireless/ipw2200.c b/drivers/net/wireless/ipw2200.c index 4839a45098c..7cb2052a55a 100644 --- a/drivers/net/wireless/ipw2200.c +++ b/drivers/net/wireless/ipw2200.c @@ -1847,6 +1847,52 @@ static ssize_t store_net_stats(struct device *d, struct device_attribute *attr, static DEVICE_ATTR(net_stats, S_IWUSR | S_IRUGO, show_net_stats, store_net_stats); +static ssize_t show_channels(struct device *d, + struct device_attribute *attr, + char *buf) +{ + struct ipw_priv *priv = dev_get_drvdata(d); + const struct ieee80211_geo *geo = ieee80211_get_geo(priv->ieee); + int len = 0, i; + + len = sprintf(&buf[len], + "Displaying %d channels in 2.4Ghz band " + "(802.11bg):\n", geo->bg_channels); + + for (i = 0; i < geo->bg_channels; i++) { + len += sprintf(&buf[len], "%d: BSS%s%s, %s, Band %s.\n", + geo->bg[i].channel, + geo->bg[i].flags & IEEE80211_CH_RADAR_DETECT ? + " (radar spectrum)" : "", + ((geo->bg[i].flags & IEEE80211_CH_NO_IBSS) || + (geo->bg[i].flags & IEEE80211_CH_RADAR_DETECT)) + ? "" : ", IBSS", + geo->bg[i].flags & IEEE80211_CH_PASSIVE_ONLY ? + "passive only" : "active/passive", + geo->bg[i].flags & IEEE80211_CH_B_ONLY ? + "B" : "B/G"); + } + + len += sprintf(&buf[len], + "Displaying %d channels in 5.2Ghz band " + "(802.11a):\n", geo->a_channels); + for (i = 0; i < geo->a_channels; i++) { + len += sprintf(&buf[len], "%d: BSS%s%s, %s.\n", + geo->a[i].channel, + geo->a[i].flags & IEEE80211_CH_RADAR_DETECT ? + " (radar spectrum)" : "", + ((geo->a[i].flags & IEEE80211_CH_NO_IBSS) || + (geo->a[i].flags & IEEE80211_CH_RADAR_DETECT)) + ? "" : ", IBSS", + geo->a[i].flags & IEEE80211_CH_PASSIVE_ONLY ? + "passive only" : "active/passive"); + } + + return len; +} + +static DEVICE_ATTR(channels, S_IRUSR, show_channels, NULL); + static void notify_wx_assoc_event(struct ipw_priv *priv) { union iwreq_data wrqu; @@ -11383,6 +11429,7 @@ static struct attribute *ipw_sysfs_entries[] = { &dev_attr_led.attr, &dev_attr_speed_scan.attr, &dev_attr_net_stats.attr, + &dev_attr_channels.attr, #ifdef CONFIG_IPW2200_PROMISCUOUS &dev_attr_rtap_iface.attr, &dev_attr_rtap_filter.attr, diff --git a/drivers/net/wireless/libertas/11d.c b/drivers/net/wireless/libertas/11d.c new file mode 100644 index 00000000000..e0ecc4d483b --- /dev/null +++ b/drivers/net/wireless/libertas/11d.c @@ -0,0 +1,754 @@ +/** + * This file contains functions for 802.11D. + */ +#include <linux/ctype.h> +#include <linux/kernel.h> +#include <linux/wireless.h> + +#include "host.h" +#include "decl.h" +#include "11d.h" +#include "dev.h" +#include "wext.h" + +#define TX_PWR_DEFAULT 10 + +static struct region_code_mapping region_code_mapping[] = { + {"US ", 0x10}, /* US FCC */ + {"CA ", 0x10}, /* IC Canada */ + {"SG ", 0x10}, /* Singapore */ + {"EU ", 0x30}, /* ETSI */ + {"AU ", 0x30}, /* Australia */ + {"KR ", 0x30}, /* Republic Of Korea */ + {"ES ", 0x31}, /* Spain */ + {"FR ", 0x32}, /* France */ + {"JP ", 0x40}, /* Japan */ +}; + +/* Following 2 structure defines the supported channels */ +static struct chan_freq_power channel_freq_power_UN_BG[] = { + {1, 2412, TX_PWR_DEFAULT}, + {2, 2417, TX_PWR_DEFAULT}, + {3, 2422, TX_PWR_DEFAULT}, + {4, 2427, TX_PWR_DEFAULT}, + {5, 2432, TX_PWR_DEFAULT}, + {6, 2437, TX_PWR_DEFAULT}, + {7, 2442, TX_PWR_DEFAULT}, + {8, 2447, TX_PWR_DEFAULT}, + {9, 2452, TX_PWR_DEFAULT}, + {10, 2457, TX_PWR_DEFAULT}, + {11, 2462, TX_PWR_DEFAULT}, + {12, 2467, TX_PWR_DEFAULT}, + {13, 2472, TX_PWR_DEFAULT}, + {14, 2484, TX_PWR_DEFAULT} +}; + +static u8 wlan_region_2_code(u8 * region) +{ + u8 i; + u8 size = sizeof(region_code_mapping)/ + sizeof(struct region_code_mapping); + + for (i = 0; region[i] && i < COUNTRY_CODE_LEN; i++) + region[i] = toupper(region[i]); + + for (i = 0; i < size; i++) { + if (!memcmp(region, region_code_mapping[i].region, + COUNTRY_CODE_LEN)) + return (region_code_mapping[i].code); + } + + /* default is US */ + return (region_code_mapping[0].code); +} + +static u8 *wlan_code_2_region(u8 code) +{ + u8 i; + u8 size = sizeof(region_code_mapping) + / sizeof(struct region_code_mapping); + for (i = 0; i < size; i++) { + if (region_code_mapping[i].code == code) + return (region_code_mapping[i].region); + } + /* default is US */ + return (region_code_mapping[0].region); +} + +/** + * @brief This function finds the nrchan-th chan after the firstchan + * @param band band + * @param firstchan first channel number + * @param nrchan number of channels + * @return the nrchan-th chan number +*/ +static u8 wlan_get_chan_11d(u8 band, u8 firstchan, u8 nrchan, u8 * chan) +/*find the nrchan-th chan after the firstchan*/ +{ + u8 i; + struct chan_freq_power *cfp; + u8 cfp_no; + + cfp = channel_freq_power_UN_BG; + cfp_no = sizeof(channel_freq_power_UN_BG) / + sizeof(struct chan_freq_power); + + for (i = 0; i < cfp_no; i++) { + if ((cfp + i)->channel == firstchan) { + lbs_pr_debug(1, "firstchan found\n"); + break; + } + } + + if (i < cfp_no) { + /*if beyond the boundary */ + if (i + nrchan < cfp_no) { + *chan = (cfp + i + nrchan)->channel; + return 1; + } + } + + return 0; +} + +/** + * @brief This function Checks if chan txpwr is learned from AP/IBSS + * @param chan chan number + * @param parsed_region_chan pointer to parsed_region_chan_11d + * @return TRUE; FALSE +*/ +static u8 wlan_channel_known_11d(u8 chan, + struct parsed_region_chan_11d * parsed_region_chan) +{ + struct chan_power_11d *chanpwr = parsed_region_chan->chanpwr; + u8 nr_chan = parsed_region_chan->nr_chan; + u8 i = 0; + + lbs_dbg_hex("11D:parsed_region_chan:", (char *)chanpwr, + sizeof(struct chan_power_11d) * nr_chan); + + for (i = 0; i < nr_chan; i++) { + if (chan == chanpwr[i].chan) { + lbs_pr_debug(1, "11D: Found Chan:%d\n", chan); + return 1; + } + } + + lbs_pr_debug(1, "11D: Not Find Chan:%d\n", chan); + return 0; +} + +u32 libertas_chan_2_freq(u8 chan, u8 band) +{ + struct chan_freq_power *cf; + u16 cnt; + u16 i; + u32 freq = 0; + + cf = channel_freq_power_UN_BG; + cnt = + sizeof(channel_freq_power_UN_BG) / + sizeof(struct chan_freq_power); + + for (i = 0; i < cnt; i++) { + if (chan == cf[i].channel) + freq = cf[i].freq; + } + + return freq; +} + +static int generate_domain_info_11d(struct parsed_region_chan_11d + *parsed_region_chan, + struct wlan_802_11d_domain_reg * domaininfo) +{ + u8 nr_subband = 0; + + u8 nr_chan = parsed_region_chan->nr_chan; + u8 nr_parsedchan = 0; + + u8 firstchan = 0, nextchan = 0, maxpwr = 0; + + u8 i, flag = 0; + + memcpy(domaininfo->countrycode, parsed_region_chan->countrycode, + COUNTRY_CODE_LEN); + + lbs_pr_debug(1, "11D:nrchan=%d\n", nr_chan); + lbs_dbg_hex("11D:parsed_region_chan:", (char *)parsed_region_chan, + sizeof(struct parsed_region_chan_11d)); + + for (i = 0; i < nr_chan; i++) { + if (!flag) { + flag = 1; + nextchan = firstchan = + parsed_region_chan->chanpwr[i].chan; + maxpwr = parsed_region_chan->chanpwr[i].pwr; + nr_parsedchan = 1; + continue; + } + + if (parsed_region_chan->chanpwr[i].chan == nextchan + 1 && + parsed_region_chan->chanpwr[i].pwr == maxpwr) { + nextchan++; + nr_parsedchan++; + } else { + domaininfo->subband[nr_subband].firstchan = firstchan; + domaininfo->subband[nr_subband].nrchan = + nr_parsedchan; + domaininfo->subband[nr_subband].maxtxpwr = maxpwr; + nr_subband++; + nextchan = firstchan = + parsed_region_chan->chanpwr[i].chan; + maxpwr = parsed_region_chan->chanpwr[i].pwr; + } + } + + if (flag) { + domaininfo->subband[nr_subband].firstchan = firstchan; + domaininfo->subband[nr_subband].nrchan = nr_parsedchan; + domaininfo->subband[nr_subband].maxtxpwr = maxpwr; + nr_subband++; + } + domaininfo->nr_subband = nr_subband; + + lbs_pr_debug(1, "nr_subband=%x\n", domaininfo->nr_subband); + lbs_dbg_hex("11D:domaininfo:", (char *)domaininfo, + COUNTRY_CODE_LEN + 1 + + sizeof(struct ieeetypes_subbandset) * nr_subband); + return 0; +} + +/** + * @brief This function generates parsed_region_chan from Domain Info learned from AP/IBSS + * @param region_chan pointer to struct region_channel + * @param *parsed_region_chan pointer to parsed_region_chan_11d + * @return N/A +*/ +static void wlan_generate_parsed_region_chan_11d(struct region_channel * region_chan, + struct parsed_region_chan_11d * + parsed_region_chan) +{ + u8 i; + struct chan_freq_power *cfp; + + if (region_chan == NULL) { + lbs_pr_debug(1, "11D: region_chan is NULL\n"); + return; + } + + cfp = region_chan->CFP; + if (cfp == NULL) { + lbs_pr_debug(1, "11D: cfp equal NULL \n"); + return; + } + + parsed_region_chan->band = region_chan->band; + parsed_region_chan->region = region_chan->region; + memcpy(parsed_region_chan->countrycode, + wlan_code_2_region(region_chan->region), COUNTRY_CODE_LEN); + + lbs_pr_debug(1, "11D: region[0x%x] band[%d]\n", parsed_region_chan->region, + parsed_region_chan->band); + + for (i = 0; i < region_chan->nrcfp; i++, cfp++) { + parsed_region_chan->chanpwr[i].chan = cfp->channel; + parsed_region_chan->chanpwr[i].pwr = cfp->maxtxpower; + lbs_pr_debug(1, "11D: Chan[%d] Pwr[%d]\n", + parsed_region_chan->chanpwr[i].chan, + parsed_region_chan->chanpwr[i].pwr); + } + parsed_region_chan->nr_chan = region_chan->nrcfp; + + lbs_pr_debug(1, "11D: nrchan[%d]\n", parsed_region_chan->nr_chan); + + return; +} + +/** + * @brief generate parsed_region_chan from Domain Info learned from AP/IBSS + * @param region region ID + * @param band band + * @param chan chan + * @return TRUE;FALSE +*/ +static u8 wlan_region_chan_supported_11d(u8 region, u8 band, u8 chan) +{ + struct chan_freq_power *cfp; + int cfp_no; + u8 idx; + + ENTER(); + + cfp = libertas_get_region_cfp_table(region, band, &cfp_no); + if (cfp == NULL) + return 0; + + for (idx = 0; idx < cfp_no; idx++) { + if (chan == (cfp + idx)->channel) { + /* If Mrvl Chip Supported? */ + if ((cfp + idx)->unsupported) { + return 0; + } else { + return 1; + } + } + } + + /*chan is not in the region table */ + LEAVE(); + return 0; +} + +/** + * @brief This function checks if chan txpwr is learned from AP/IBSS + * @param chan chan number + * @param parsed_region_chan pointer to parsed_region_chan_11d + * @return 0 +*/ +static int parse_domain_info_11d(struct ieeetypes_countryinfofullset* + countryinfo, + u8 band, + struct parsed_region_chan_11d * + parsed_region_chan) +{ + u8 nr_subband, nrchan; + u8 lastchan, firstchan; + u8 region; + u8 curchan = 0; + + u8 idx = 0; /*chan index in parsed_region_chan */ + + u8 j, i; + + ENTER(); + + /*validation Rules: + 1. valid region Code + 2. First Chan increment + 3. channel range no overlap + 4. channel is valid? + 5. channel is supported by region? + 6. Others + */ + + lbs_dbg_hex("CountryInfo:", (u8 *) countryinfo, 30); + + if ((*(countryinfo->countrycode)) == 0 + || (countryinfo->len <= COUNTRY_CODE_LEN)) { + /* No region Info or Wrong region info: treat as No 11D info */ + LEAVE(); + return 0; + } + + /*Step1: check region_code */ + parsed_region_chan->region = region = + wlan_region_2_code(countryinfo->countrycode); + + lbs_pr_debug(1, "regioncode=%x\n", (u8) parsed_region_chan->region); + lbs_dbg_hex("CountryCode:", (char *)countryinfo->countrycode, + COUNTRY_CODE_LEN); + + parsed_region_chan->band = band; + + memcpy(parsed_region_chan->countrycode, countryinfo->countrycode, + COUNTRY_CODE_LEN); + + nr_subband = (countryinfo->len - COUNTRY_CODE_LEN) / + sizeof(struct ieeetypes_subbandset); + + for (j = 0, lastchan = 0; j < nr_subband; j++) { + + if (countryinfo->subband[j].firstchan <= lastchan) { + /*Step2&3. Check First Chan Num increment and no overlap */ + lbs_pr_debug(1, "11D: Chan[%d>%d] Overlap\n", + countryinfo->subband[j].firstchan, lastchan); + continue; + } + + firstchan = countryinfo->subband[j].firstchan; + nrchan = countryinfo->subband[j].nrchan; + + for (i = 0; idx < MAX_NO_OF_CHAN && i < nrchan; i++) { + /*step4: channel is supported? */ + + if (!wlan_get_chan_11d(band, firstchan, i, &curchan)) { + /* Chan is not found in UN table */ + lbs_pr_debug(1, "chan is not supported: %d \n", i); + break; + } + + lastchan = curchan; + + if (wlan_region_chan_supported_11d + (region, band, curchan)) { + /*step5: Check if curchan is supported by mrvl in region */ + parsed_region_chan->chanpwr[idx].chan = curchan; + parsed_region_chan->chanpwr[idx].pwr = + countryinfo->subband[j].maxtxpwr; + idx++; + } else { + /*not supported and ignore the chan */ + lbs_pr_debug(1, + "11D:i[%d] chan[%d] unsupported in region[%x] band[%d]\n", + i, curchan, region, band); + } + } + + /*Step6: Add other checking if any */ + + } + + parsed_region_chan->nr_chan = idx; + + lbs_pr_debug(1, "nrchan=%x\n", parsed_region_chan->nr_chan); + lbs_dbg_hex("11D:parsed_region_chan:", (u8 *) parsed_region_chan, + 2 + COUNTRY_CODE_LEN + sizeof(struct parsed_region_chan_11d) * idx); + + LEAVE(); + return 0; +} + +/** + * @brief This function calculates the scan type for channels + * @param chan chan number + * @param parsed_region_chan pointer to parsed_region_chan_11d + * @return PASSIVE if chan is unknown; ACTIVE if chan is known +*/ +u8 libertas_get_scan_type_11d(u8 chan, + struct parsed_region_chan_11d * parsed_region_chan) +{ + u8 scan_type = cmd_scan_type_passive; + + ENTER(); + + if (wlan_channel_known_11d(chan, parsed_region_chan)) { + lbs_pr_debug(1, "11D: Found and do Active Scan\n"); + scan_type = cmd_scan_type_active; + } else { + lbs_pr_debug(1, "11D: Not Find and do Passive Scan\n"); + } + + LEAVE(); + return scan_type; + +} + +void libertas_init_11d(wlan_private * priv) +{ + priv->adapter->enable11d = 0; + memset(&(priv->adapter->parsed_region_chan), 0, + sizeof(struct parsed_region_chan_11d)); + return; +} + +static int wlan_enable_11d(wlan_private * priv, u8 flag) +{ + int ret; + + priv->adapter->enable11d = flag; + + /* send cmd to FW to enable/disable 11D function in FW */ + ret = libertas_prepare_and_send_command(priv, + cmd_802_11_snmp_mib, + cmd_act_set, + cmd_option_waitforrsp, + OID_802_11D_ENABLE, + &priv->adapter->enable11d); + if (ret) + lbs_pr_debug(1, "11D: Fail to enable 11D \n"); + + return 0; +} + +/** + * @brief This function sets DOMAIN INFO to FW + * @param priv pointer to wlan_private + * @return 0; -1 +*/ +static int set_domain_info_11d(wlan_private * priv) +{ + int ret; + + if (!priv->adapter->enable11d) { + lbs_pr_debug(1, "11D: dnld domain Info with 11d disabled\n"); + return 0; + } + + ret = libertas_prepare_and_send_command(priv, cmd_802_11d_domain_info, + cmd_act_set, + cmd_option_waitforrsp, 0, NULL); + if (ret) + lbs_pr_debug(1, "11D: Fail to dnld domain Info\n"); + + return ret; +} + +/** + * @brief This function setups scan channels + * @param priv pointer to wlan_private + * @param band band + * @return 0 +*/ +int libertas_set_universaltable(wlan_private * priv, u8 band) +{ + wlan_adapter *adapter = priv->adapter; + u16 size = sizeof(struct chan_freq_power); + u16 i = 0; + + memset(adapter->universal_channel, 0, + sizeof(adapter->universal_channel)); + + adapter->universal_channel[i].nrcfp = + sizeof(channel_freq_power_UN_BG) / size; + lbs_pr_debug(1, "11D: BG-band nrcfp=%d\n", + adapter->universal_channel[i].nrcfp); + + adapter->universal_channel[i].CFP = channel_freq_power_UN_BG; + adapter->universal_channel[i].valid = 1; + adapter->universal_channel[i].region = UNIVERSAL_REGION_CODE; + adapter->universal_channel[i].band = band; + i++; + + return 0; +} + +/** + * @brief This function implements command CMD_802_11D_DOMAIN_INFO + * @param priv pointer to wlan_private + * @param cmd pointer to cmd buffer + * @param cmdno cmd ID + * @param cmdOption cmd action + * @return 0 +*/ +int libertas_cmd_802_11d_domain_info(wlan_private * priv, + struct cmd_ds_command *cmd, u16 cmdno, + u16 cmdoption) +{ + struct cmd_ds_802_11d_domain_info *pdomaininfo = + &cmd->params.domaininfo; + struct mrvlietypes_domainparamset *domain = &pdomaininfo->domain; + wlan_adapter *adapter = priv->adapter; + u8 nr_subband = adapter->domainreg.nr_subband; + + ENTER(); + + lbs_pr_debug(1, "nr_subband=%x\n", nr_subband); + + cmd->command = cpu_to_le16(cmdno); + pdomaininfo->action = cpu_to_le16(cmdoption); + if (cmdoption == cmd_act_get) { + cmd->size = + cpu_to_le16(sizeof(pdomaininfo->action) + S_DS_GEN); + lbs_dbg_hex("11D: 802_11D_DOMAIN_INFO:", (u8 *) cmd, + (int)(cmd->size)); + LEAVE(); + return 0; + } + + domain->header.type = cpu_to_le16(TLV_TYPE_DOMAIN); + memcpy(domain->countrycode, adapter->domainreg.countrycode, + sizeof(domain->countrycode)); + + domain->header.len = + cpu_to_le16(nr_subband * sizeof(struct ieeetypes_subbandset) + + sizeof(domain->countrycode)); + + if (nr_subband) { + memcpy(domain->subband, adapter->domainreg.subband, + nr_subband * sizeof(struct ieeetypes_subbandset)); + + cmd->size = cpu_to_le16(sizeof(pdomaininfo->action) + + domain->header.len + + sizeof(struct mrvlietypesheader) + + S_DS_GEN); + } else { + cmd->size = + cpu_to_le16(sizeof(pdomaininfo->action) + S_DS_GEN); + } + + lbs_dbg_hex("11D:802_11D_DOMAIN_INFO:", (u8 *) cmd, (int)(cmd->size)); + + LEAVE(); + + return 0; +} + +/** + * @brief This function implements private cmd: enable/disable 11D + * @param priv pointer to wlan_private + * @param wrq pointer to user data + * @return 0 or -1 + */ +int libertas_cmd_enable_11d(wlan_private * priv, struct iwreq *wrq) +{ + int data = 0; + int *val; + + ENTER(); + data = SUBCMD_DATA(wrq); + + lbs_pr_debug(1, "enable 11D: %s\n", + (data == 1) ? "enable" : "Disable"); + + wlan_enable_11d(priv, data); + val = (int *)wrq->u.name; + *val = priv->adapter->enable11d; + + LEAVE(); + return 0; +} + +/** + * @brief This function parses countryinfo from AP and download country info to FW + * @param priv pointer to wlan_private + * @param resp pointer to command response buffer + * @return 0; -1 + */ +int libertas_ret_802_11d_domain_info(wlan_private * priv, + struct cmd_ds_command *resp) +{ + struct cmd_ds_802_11d_domain_info + *domaininfo = &resp->params.domaininforesp; + struct mrvlietypes_domainparamset *domain = &domaininfo->domain; + u16 action = le16_to_cpu(domaininfo->action); + s16 ret = 0; + u8 nr_subband = 0; + + ENTER(); + + lbs_dbg_hex("11D DOMAIN Info Rsp Data:", (u8 *) resp, + (int)le16_to_cpu(resp->size)); + + nr_subband = (domain->header.len - 3) / sizeof(struct ieeetypes_subbandset); + /* countrycode 3 bytes */ + + lbs_pr_debug(1, "11D Domain Info Resp: nr_subband=%d\n", nr_subband); + + if (nr_subband > MRVDRV_MAX_SUBBAND_802_11D) { + lbs_pr_debug(1, "Invalid Numrer of Subband returned!!\n"); + return -1; + } + + switch (action) { + case cmd_act_set: /*Proc Set action */ + break; + + case cmd_act_get: + break; + default: + lbs_pr_debug(1, "Invalid action:%d\n", domaininfo->action); + ret = -1; + break; + } + + LEAVE(); + return ret; +} + +/** + * @brief This function parses countryinfo from AP and download country info to FW + * @param priv pointer to wlan_private + * @return 0; -1 + */ +int libertas_parse_dnld_countryinfo_11d(wlan_private * priv) +{ + int ret; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + if (priv->adapter->enable11d) { + memset(&adapter->parsed_region_chan, 0, + sizeof(struct parsed_region_chan_11d)); + ret = parse_domain_info_11d(&adapter->pattemptedbssdesc-> + countryinfo, 0, + &adapter->parsed_region_chan); + + if (ret == -1) { + lbs_pr_debug(1, "11D: Err Parse domain_info from AP..\n"); + LEAVE(); + return ret; + } + + memset(&adapter->domainreg, 0, + sizeof(struct wlan_802_11d_domain_reg)); + generate_domain_info_11d(&adapter->parsed_region_chan, + &adapter->domainreg); + + ret = set_domain_info_11d(priv); + + if (ret) { + lbs_pr_debug(1, "11D: Err set domainInfo to FW\n"); + LEAVE(); + return ret; + } + } + LEAVE(); + return 0; +} + +/** + * @brief This function generates 11D info from user specified regioncode and download to FW + * @param priv pointer to wlan_private + * @return 0; -1 + */ +int libertas_create_dnld_countryinfo_11d(wlan_private * priv) +{ + int ret; + wlan_adapter *adapter = priv->adapter; + struct region_channel *region_chan; + u8 j; + + ENTER(); + lbs_pr_debug(1, "11D:curbssparams.band[%d]\n", adapter->curbssparams.band); + + if (priv->adapter->enable11d) { + /* update parsed_region_chan_11; dnld domaininf to FW */ + + for (j = 0; j < sizeof(adapter->region_channel) / + sizeof(adapter->region_channel[0]); j++) { + region_chan = &adapter->region_channel[j]; + + lbs_pr_debug(1, "11D:[%d] region_chan->band[%d]\n", j, + region_chan->band); + + if (!region_chan || !region_chan->valid + || !region_chan->CFP) + continue; + if (region_chan->band != adapter->curbssparams.band) + continue; + break; + } + + if (j >= sizeof(adapter->region_channel) / + sizeof(adapter->region_channel[0])) { + lbs_pr_debug(1, "11D:region_chan not found. band[%d]\n", + adapter->curbssparams.band); + LEAVE(); + return -1; + } + + memset(&adapter->parsed_region_chan, 0, + sizeof(struct parsed_region_chan_11d)); + wlan_generate_parsed_region_chan_11d(region_chan, + &adapter-> + parsed_region_chan); + + memset(&adapter->domainreg, 0, + sizeof(struct wlan_802_11d_domain_reg)); + generate_domain_info_11d(&adapter->parsed_region_chan, + &adapter->domainreg); + + ret = set_domain_info_11d(priv); + + if (ret) { + lbs_pr_debug(1, "11D: Err set domainInfo to FW\n"); + LEAVE(); + return ret; + } + + } + + LEAVE(); + return 0; +} diff --git a/drivers/net/wireless/libertas/11d.h b/drivers/net/wireless/libertas/11d.h new file mode 100644 index 00000000000..db2ebea9f23 --- /dev/null +++ b/drivers/net/wireless/libertas/11d.h @@ -0,0 +1,105 @@ +/** + * This header file contains data structures and + * function declarations of 802.11d + */ +#ifndef _WLAN_11D_ +#define _WLAN_11D_ + +#include "types.h" +#include "defs.h" + +#define UNIVERSAL_REGION_CODE 0xff + +/** (Beaconsize(256)-5(IEId,len,contrystr(3))/3(FirstChan,NoOfChan,MaxPwr) + */ +#define MRVDRV_MAX_SUBBAND_802_11D 83 + +#define COUNTRY_CODE_LEN 3 +#define MAX_NO_OF_CHAN 40 + +struct cmd_ds_command; + +/** Data structure for Country IE*/ +struct ieeetypes_subbandset { + u8 firstchan; + u8 nrchan; + u8 maxtxpwr; +} __attribute__ ((packed)); + +struct ieeetypes_countryinfoset { + u8 element_id; + u8 len; + u8 countrycode[COUNTRY_CODE_LEN]; + struct ieeetypes_subbandset subband[1]; +}; + +struct ieeetypes_countryinfofullset { + u8 element_id; + u8 len; + u8 countrycode[COUNTRY_CODE_LEN]; + struct ieeetypes_subbandset subband[MRVDRV_MAX_SUBBAND_802_11D]; +} __attribute__ ((packed)); + +struct mrvlietypes_domainparamset { + struct mrvlietypesheader header; + u8 countrycode[COUNTRY_CODE_LEN]; + struct ieeetypes_subbandset subband[1]; +} __attribute__ ((packed)); + +struct cmd_ds_802_11d_domain_info { + u16 action; + struct mrvlietypes_domainparamset domain; +} __attribute__ ((packed)); + +/** domain regulatory information */ +struct wlan_802_11d_domain_reg { + /** country Code*/ + u8 countrycode[COUNTRY_CODE_LEN]; + /** No. of subband*/ + u8 nr_subband; + struct ieeetypes_subbandset subband[MRVDRV_MAX_SUBBAND_802_11D]; +}; + +struct chan_power_11d { + u8 chan; + u8 pwr; +} __attribute__ ((packed)); + +struct parsed_region_chan_11d { + u8 band; + u8 region; + s8 countrycode[COUNTRY_CODE_LEN]; + struct chan_power_11d chanpwr[MAX_NO_OF_CHAN]; + u8 nr_chan; +} __attribute__ ((packed)); + +struct region_code_mapping { + u8 region[COUNTRY_CODE_LEN]; + u8 code; +}; + +u8 libertas_get_scan_type_11d(u8 chan, + struct parsed_region_chan_11d *parsed_region_chan); + +u32 libertas_chan_2_freq(u8 chan, u8 band); + +enum state_11d libertas_get_state_11d(wlan_private * priv); + +void libertas_init_11d(wlan_private * priv); + +int libertas_set_universaltable(wlan_private * priv, u8 band); + +int libertas_cmd_802_11d_domain_info(wlan_private * priv, + struct cmd_ds_command *cmd, u16 cmdno, + u16 cmdOption); + +int libertas_cmd_enable_11d(wlan_private * priv, struct iwreq *wrq); + +int libertas_ret_802_11d_domain_info(wlan_private * priv, + struct cmd_ds_command *resp); + +int libertas_parse_dnld_countryinfo_11d(wlan_private * priv); + +int libertas_create_dnld_countryinfo_11d(wlan_private * priv); + +#endif /* _WLAN_11D_ */ diff --git a/drivers/net/wireless/libertas/LICENSE b/drivers/net/wireless/libertas/LICENSE new file mode 100644 index 00000000000..8862742213b --- /dev/null +++ b/drivers/net/wireless/libertas/LICENSE @@ -0,0 +1,16 @@ + Copyright (c) 2003-2006, Marvell International Ltd. + All Rights Reserved + + This program is free software; you can redistribute it and/or modify it + under the terms of version 2 of the GNU General Public License as + published by the Free Software Foundation. + + 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; if not, write to the Free Software Foundation, Inc., 59 + Temple Place - Suite 330, Boston, MA 02111-1307, USA. + diff --git a/drivers/net/wireless/libertas/Makefile b/drivers/net/wireless/libertas/Makefile new file mode 100644 index 00000000000..19c935071d8 --- /dev/null +++ b/drivers/net/wireless/libertas/Makefile @@ -0,0 +1,21 @@ +# EXTRA_CFLAGS += -Wpacked + +usb8xxx-objs := main.o fw.o wext.o \ + rx.o tx.o cmd.o \ + cmdresp.o scan.o \ + join.o 11d.o \ + ioctl.o debugfs.o \ + ethtool.o assoc.o + +ifeq ($(CONFIG_LIBERTAS_USB_DEBUG), y) +EXTRA_CFLAGS += -DDEBUG -DPROC_DEBUG +endif + + +# This is needed to support the newer boot2 bootloader (v >= 3104) +EXTRA_CFLAGS += -DSUPPORT_BOOT_COMMAND +usb8xxx-objs += if_bootcmd.o +usb8xxx-objs += if_usb.o + +obj-$(CONFIG_LIBERTAS_USB) += usb8xxx.o + diff --git a/drivers/net/wireless/libertas/README b/drivers/net/wireless/libertas/README new file mode 100644 index 00000000000..688da4c784b --- /dev/null +++ b/drivers/net/wireless/libertas/README @@ -0,0 +1,1044 @@ +================================================================================ + README for USB8388 + + (c) Copyright © 2003-2006, Marvell International Ltd. + All Rights Reserved + + This software file (the "File") is distributed by Marvell International + Ltd. under the terms of the GNU General Public License Version 2, June 1991 + (the "License"). You may use, redistribute and/or modify this File in + accordance with the terms and conditions of the License, a copy of which + is available along with the File in the license.txt file or by writing to + the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 or on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + + THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + ARE EXPRESSLY DISCLAIMED. The License provides additional details about + this warranty disclaimer. +================================================================================ + +===================== +DRIVER LOADING +===================== + + o. Copy the firmware image (e.g. usb8388.bin) to /lib/firmware/ + + o. Load driver by using the following command: + + insmod usb8388.ko [fw_name=usb8388.bin] + +===================== +IWPRIV COMMAND +===================== + +NAME + This manual describes the usage of private commands used in Marvell WLAN + Linux Driver. All the commands available in Wlanconfig will not be available + in the iwpriv. + +SYNOPSIS + iwpriv <ethX> <command> [sub-command] ... + + iwpriv ethX version + iwpriv ethX scantype [sub-command] + iwpriv ethX getSNR <n> + iwpriv ethX getNF <n> + iwpriv ethX getRSSI <n> + iwpriv ethX setrxant <n> + iwpriv ethX getrxant + iwpriv ethX settxant <n> + iwpriv ethX gettxant + iwpriv ethX authalgs <n> + iwpriv ethX pre-TBTT <n> + iwpriv ethX 8021xauthalgs <n> + iwpriv ethX encryptionmode <n> + iwpriv ethX setregioncode <n> + iwpriv ethX getregioncode + iwpriv ethX setbcnavg <n> + iwpriv ethX getbcnavg + iwpriv ethX setdataavg <n> + iwpriv ethX setlisteninter <n> + iwpriv ethX getlisteninter + iwpriv ethX setmultipledtim <n> + iwpriv ethX getmultipledtim + iwpriv ethX atimwindow <n> + iwpriv ethX deauth + iwpriv ethX adhocstop + iwpriv ethX radioon + iwpriv ethX radiooff + iwpriv ethX reasso-on + iwpriv ethX reasso-off + iwpriv ethX scanmode [sub-command] + iwpriv ethX setwpaie <n> + iwpriv ethX wlanidle-off + iwpriv ethX wlanidle-on + iwpriv ethX getcis + iwpriv ethX getlog + iwpriv ethX getadhocstatus + iwpriv ethX adhocgrate <n> + +Version 4 Command: + iwpriv ethX inactvityto <n> + iwpriv ethX sleeppd <n> + iwpriv ethX enable11d <n> + iwpriv ethX tpccfg <n> + iwpriv ethX powercfg <n> + iwpriv ethX setafc <n> + iwpriv ethX getafc + +Version 5 Command: + iwpriv ethX ledgpio <n> + iwpriv ethX scanprobes <n> + iwpriv ethX lolisteninter <n> + iwpriv ethX rateadapt <n> <m> + iwpriv ethX txcontrol <n> + iwpriv ethX psnullinterval <n> + iwpriv ethX prescan <n> + iwpriv ethX getrxinfo + iwpriv ethX gettxrate + iwpriv ethX beaconinterval + +BT Commands: + The blinding table (BT) contains a list of mac addresses that should be + ignored by the firmware. It is primarily used for debugging and + testing networks. It can be edited and inspected with the following + commands: + + iwpriv ethX bt_reset + iwpriv ethX bt_add <mac_address> + iwpriv ethX bt_del <mac_address> + iwpriv ethX bt_list <id> + +FWT Commands: + The forwarding table (FWT) is a feature used to manage mesh network + routing in the firmware. The FWT is essentially a routing table that + associates a destination mac address (da) with a next hop receiver + address (ra). The FWT can be inspected and edited with the following + iwpriv commands, which are described in greater detail below. + Eventually, the table will be automatically maintained by a custom + routing protocol. + + NOTE: FWT commands replace the previous DFT commands. What were the DFT + commands?, you might ask. They were an earlier API to the firmware that + implemented a simple MAC-layer forwarding mechanism. In the unlikely + event that you were using these commands, you must migrate to the new + FWT commands which can be used to achieve the same functionality. + + iwpriv ethX fwt_add [parameters] + iwpriv ethX fwt_del [parameters] + iwpriv ethX fwt_lookup [parameters] + iwpriv ethX fwt_list [parameters] + iwpriv ethX fwt_list_route [parameters] + iwpriv ethX fwt_list_neigh [parameters] + iwpriv ethX fwt_reset [parameters] + iwpriv ethX fwt_cleanup + iwpriv ethX fwt_time + +MESH Commands: + + The MESH commands are used to configure various features of the mesh + routing protocol. The following commands are supported: + + iwpriv ethX mesh_get_ttl + iwpriv ethX mesh_set_ttl ttl + +DESCRIPTION + Those commands are used to send additional commands to the Marvell WLAN + card via the Linux device driver. + + The ethX parameter specifies the network device that is to be used to + perform this command on. it could be eth0, eth1 etc. + +version + This is used to get the current version of the driver and the firmware. + +scantype + This command is used to set the scan type to be used by the driver in + the scan command. This setting will not be used while performing a scan + for a specific SSID, as it is always done with scan type being active. + + where the sub-commands are: - + active -- to set the scan type to active + passive -- to set the scan type to passive + get -- to get the scan type set in the driver + +getSNR + This command gets the average and non average value of Signal to Noise + Ratio of Beacon and Data. + + where value is:- + 0 -- Beacon non-average. + 1 -- Beacon average. + 2 -- Data non-average. + 3 -- Data average. + + If no value is given, all four values are returned in the order mentioned + above. + + Note: This command is available only when STA is connected. + +getRSSI + This command gets the average and non average value os Receive Signal + Strength of Beacon and Data. + + where value is:- + 0 -- Beacon non-average. + 1 -- Beacon average. + 2 -- Data non-average. + 3 -- Data average. + + Note: This command is available only when STA is connected. + +getNF + This command gets the average and non average value of Noise Floor of + Beacon and Data. + + where value is:- + 0 -- Beacon non-average. + 1 -- Beacon average. + 2 -- Data non-average. + 3 -- Data average. + + Note: This command is available only when STA is connected. + +setrxant + This command is used to set the mode for Rx antenna. + + The options that can be sent are:- + 1 -- Antenna 1. + 2 -- Antenna 2. + 0xFFFF -- Diversity. + + Usage: + iwpriv ethX setrxant 0x01: select Antenna 1. + +getrxant + This command is used to get the mode for Rx antenna. + + +settxant + This command is used to set the mode for Tx antenna. + The options that can be sent are:- + 1 -- Antenna 1. + 2 -- Antenna 2. + 0xFFFF -- Diversity. + Usage: + iwpriv ethX settxant 0x01: select Antenna 1. + +gettxant + This command is used to get the mode for Tx antenna. + +authalgs + This command is used by the WPA supplicant to set the authentication + algorithms in the station. + +8021xauthalgs + This command is used by the WPA supplicant to set the 8021.x authentication algorithm type + station. + + where values can be:- + 1 -- None + 2 -- LEAP + 4 -- TLS + 8 -- TTLs + 16 -- MD5 + + +encryptionmode + This command is used by the WPA supplicant to set the encryption algorithm. + + where values can be:- + 0 -- NONE + 1 -- WEP40 + 2 -- TKIP + 3 -- CCMP + 4 -- WEP104 + +pre-TBTT + This command is used to set pre-TBTT time period where value is in microseconds. + +setregioncode + This command is used to set the region code in the station. + where value is 'region code' for various regions like + USA FCC, Canada IC, Spain, France, Europe ETSI, Japan ... + + Usage: + iwpriv ethX setregioncode 0x10: set region code to USA (0x10). + +getregioncode + This command is used to get the region code information set in the + station. + +setbcnavg + Set the weighting factor for calculating RSSI. + +getbcnavg + Get weighting factor for calculating RSSI. + +setdataavg + Set the weighting factor for calculating SNR. + +setlisteninter + This command is used to set the listen interval in the + station. + + where the value ranges between 1 - 255 + +getlisteninter + This command is used to get the listen interval value set in the + station. + +setmultipledtim + This command is used to set the multiple dtim value in the + station. + where the value is 1,2,3,4,5,0xfffe + 0xfffe means the firmware will use listen interval in association + command for waking up + +getmultipledtim + This command is used to get the multiple dtim value set in the station. + +atimwindow + This command is used to set the atim value in the + station. + + where the value ranges between 0 - 50 + +deauth + This command is used to send the de-authentication to the AP with which + the station is associated. This command is valid only when + station is in Infrastructure mode. + + Note: This command is available only when STA is connected. + +adhocstop + This command is used to stop beacon transmission from the station and + go into idle state in ad-hoc mode. + + Note: This command is available only when STA is connected. + +radioon + This command is used to turn on the RF antenna. + +radiooff + This command is sued to turn off the RF antenna. + +scanmode + This command is used to set the station to scan for either IBSS + networks or BSS networks or both BSS and IBSS networks. This + command can be used with sub commands, + + where the value for + bss -- Scan All the BSS networks. + ibss -- Scan All the IBSS networks. + any -- Scan both BSS and IBSS networks. + + + +setwpaie + This command is used by WPA supplicant to send the WPA-IE to the driver. + +wlanidle-off + This command is used to get into idle state. + + Note: This command is available only when STA is connected. + +wlanidle-on + This command is used to get off the idle state. + + Note: This command is available only when STA is connected. + + +getlog + This command is used to get the 802.11 statistics available in the + station. + + Note: This command is available only when STA is connected. + +getadhocstatus + This command is used to get the ad-hoc Network Status. + + The various status codes are: + AdhocStarted + AdhocJoined + AdhocIdle + InfraMode + AutoUnknownMode + + Note: This command is available only when STA is connected. + +adhocgrate + This command is used to enable(1) g_rate, Disable(0) g_rate + and request(2) the status which g_rate is disabled/enabled, + for Ad-hoc creator. + + where value is:- + 0 -- Disabled + 1 -- Enabled + 2 -- Get + +ledgpio + This command is used to set/get LEDs. + + iwpriv ethX ledgpio <LEDs> + will set the corresponding LED for the GPIO Line. + + iwpriv ethX ledgpio + will give u which LEDs are Enabled. + + Usage: + iwpriv eth1 ledgpio 1 0 2 1 3 4 + will enable + LED 1 -> GPIO 0 + LED 2 -> GPIO 1 + LED 3 -> GPIO 4 + + iwpriv eth1 ledgpio + shows LED information in the format as mentioned above. + + Note: LED0 is invalid + Note: Maximum Number of LEDs are 16. + +inactivityto + This command is used by the host to set/get the inactivity timeout value, + which specifies when WLAN device is put to sleep. + + Usage: + iwpriv ethX inactivityto [<timeout>] + + where the parameter are: + timeout: timeout value in milliseconds. + + Example: + iwpriv eth1 inactivityto + "get the timeout value" + + iwpriv eth1 inactivityto X + "set timeout value to X ms" + + +sleeppd + This command is used to configure the sleep period of the WLAN device. + + Usage: + iwpriv ethX sleeppd [<sleep period>] + + where the parameter are: + Period: sleep period in milliseconds. Range 10~60. + + Example: + iwpriv eth1 sleeppd 10 + "set period as 10 ms" + iwpriv eth1 sleeppd + "get the sleep period configuration" + +enable11d + This command is used to control 11d + where value is:- + 1 -- Enabled + 0 -- Disabled + 2 -- Get + + + + +tpccfg + Enables or disables automatic transmit power control. + + The first parameter turns this feature on (1) or off (0). When turning + on, the user must also supply four more parameters in the following + order: + -UseSNR (Use SNR (in addition to PER) for TPC algorithm), + -P0 (P0 power level for TPC), + -P1 (P1 power level for TPC), + -P2 (P2 power level for TPC). + + Usage: + iwpriv ethX tpccfg: Get current configuration + iwpriv ethX tpccfg 0: disable auto TPC + iwpriv ethX tpccfg 0x01 0x00 0x05 0x0a 0x0d: enable auto TPC; do not use SNR; + P0=0x05; P1=0x0a; P2=0x0d; + iwpriv ethX tpccfg 0x01 0x01 0x05 0x0a 0x0d: enable auto TPC; use SNR; + P0=0x05; P1=0x0a; P2=0x0d. + +powercfg + Enables or disables power adaptation. + + The first parameter turns this feature on (1) or off (0). When turning + on, the user must also supply three more parameters in the following + order: + -P0 (P0 power level for Power Adaptation), + -P1 (P1 power level for Power Adaptation), + -P2 (P2 power level for Power Adaptation). + + Usage: + iwpriv ethX powercfg: Get current configuration + iwpriv ethX powercfg 0: disable power adaptation + iwpriv ethX powercfg 1 0x0d 0x0f 0x12: enable power adaptation; + P0=0x0d; P1=0x0f; P2=0x12. + +getafc + This command returns automatic frequency control parameters. It returns + three integers: + -P0: automatic is on (1), or off (0), + -P1: current timing offset in PPM (part per million), and + -P2: current frequency offset in PPM. + +setafc + Set automatic frequency control options. + + The first parameter turns automatic on (1) or off (0). + The user must supply two more parameters in either case, in the following + order: + + When auto is on: + + -P0 (automatic adjustment frequency threshold in PPM), + -P1 (automatic adjustment period in beacon period), + + When auto is off: + + -P0 (manual adjustment timing offset in PPM), and + -P1 (manual adjustment frequency offset in PPM). + + Usage: + iwpriv ethX setafc 0 10 10: manual adjustment, both timing and frequcncy + offset are 10 PPM. + + iwpriv ethX setafc 1 10 10 enable afc, automatic adjustment, + frequency threshold 10 PPM, for every 10 beacon periods. + + + +scanprobes + This command sets number of probe requests per channel. + + Usage: + iwpriv ethX scanprobes 3 (set scan probes to 3) + iwpriv ethX scanprobes (get scan probes) + +lolisteninter + This command sets the value of listen interval. + + Usage: + iwpriv ethX lolisteninter 234 (set the lolisteninter to 234) + iwpriv ethX lolisteninter (get the lolisteninter value) + +rateadapt + This command sets the data rates bitmap. + Where <n> + 0: Disable auto rate adapt + 1: Enable auto rate adapt + + <m> + data rate bitmap + Bit Data rate + 0 1 Mbps + 1 2 Mbps + 2 5.5 Mbps + 3 11 Mbps + 4 Reserved + 5 6 Mbps + 6 9 Mbps + 7 12 Mbps + 8 18 Mbps + 9 24 Mbps + 10 36 Mbps + 11 48 Mbps + 12 54 Mbps + 12-15 Reserved + + Usage: + iwpriv ethX rateadapt + read the currect data rate setting + iwpriv ethX rateadapt 1 0x07 + enable auto data rate adapt and + data rates are 1Mbps, 2Mbsp and 5.5Mbps + + +txcontrol + This command is used to set the Tx rate, ack policy, and retry limit on a per packet basis. + + Where value <n> is: + if bit[4] == 1: + bit[3:0] -- 0 1 2 3 4 5 6 7 8 9 10 11 12 13-16 + Data Rate(Mbps) -- 1 2 5.5 11 Rsv 6 9 12 18 24 36 48 54 Rsv + + bit[12:8] + if bit[12] == 1, bit[11:8] specifies the Tx retry limit. + + bit[14:13] specifies per packet ack policy: + bit[14:13] + 1 0 use immediate ack policy for this packet + 1 1 use no ack policy for this packet + 0 x use the per-packet ack policy setting + + Usage: + iwpriv ethX txcontrol 0x7513 + Use no-ack policy, 5 retires for Tx, 11Mbps rate + + + +psnullinterval + This command is used to set/request NULL package interval for Power Save + under infrastructure mode. + + where value is:- + -1 -- Disabled + n>0 -- Set interval as n (seconds) + +prescan + This command is used to enable (1)/disable(0) auto prescan before assoicate to the ap + + where value is:- + 0 -- Disabled + 1 -- Enabled + 2 -- Get + +getrxinfo + This command gets non average value of Signal to Noise Ratio of Data and rate index. + + The following table shows RateIndex and Rate + + RateIndex Data rate + 0 1 Mbps + 1 2 Mbps + 2 5.5 Mbps + 3 11 Mbps + 4 Reserved + 5 6 Mbps + 6 9 Mbps + 7 12 Mbps + 8 18 Mbps + 9 24 Mbps + 10 36 Mbps + 11 48 Mbps + 12 54 Mbps + 13-15 Reserved + +gettxrate + This command gets current Tx rate index of the first packet associated with Rate Adaptation. + + The following table shows RateIndex and Rate + + RateIndex Data rate + 0 1 Mbps + 1 2 Mbps + 2 5.5 Mbps + 3 11 Mbps + 4 Reserved + 5 6 Mbps + 6 9 Mbps + 7 12 Mbps + 8 18 Mbps + 9 24 Mbps + 10 36 Mbps + 11 48 Mbps + 12 54 Mbps + 13-15 Reserved + +bcninterval + This command is used to sets beacon interval in adhoc mode when an argument is given, and gets current adhoc + beacon interval when no argument is given. The valid beacon interval is between 20 - 1000, + default beacon interval is 100. + + Usage: + iwpriv ethX bcninterval 100 (set adhoc beacon interval to 100) + iwpriv ethX bcninterval (get adhoc beacon interval) + +fwt_add + This command is used to insert an entry into the FWT table. The list of + parameters must follow the following structure: + + iwpriv ethX fwt_add da ra [metric dir ssn dsn hopcount ttl expiration sleepmode snr] + + The parameters between brackets are optional, but they must appear in + the order specified. For example, if you want to specify the metric, + you must also specify the dir, ssn, and dsn but you need not specify the + hopcount, expiration, sleepmode, or snr. Any unspecified parameters + will be assigned the defaults specified below. + + The different parameters are:- + da -- DA MAC address in the form 00:11:22:33:44:55 + ra -- RA MAC address in the form 00:11:22:33:44:55 + metric -- route metric (cost: smaller-metric routes are + preferred, default is 0) + dir -- direction (1 for direct, 0 for reverse, + default is 1) + ssn -- Source Sequence Number (time at the RA for + reverse routes. Default is 0) + dsn -- Destination Sequence Number (time at the DA + for direct routes. Default is 0) + hopcount -- hop count (currently unused, default is 0) + ttl -- TTL (Only used in reverse entries) + expiration -- entry expiration (in ticks, where a tick is + 1024us, or ~ 1ms. Use 0 for an indefinite + entry, default is 0) + sleepmode -- RA's sleep mode (currently unused, default is + 0) + snr -- SNR in the link to RA (currently unused, + default is 0) + + The command does not return anything. + +fwt_del + This command is used to remove an entry to the FWT table. The list of + parameters must follow the following structure: + + iwpriv ethX fwt_del da ra [dir] + + where the different parameters are:- + da -- DA MAC address (in the form "00:11:22:33:44:55") + ra -- RA MAC address (in the form "00:11:22:33:44:55") + dir -- direction (1 for direct, 0 for reverse, + default is 1) + + The command does not return anything. + +fwt_lookup + This command is used to get the best route in the FWT table to a given + host. The only parameter is the MAC address of the host that is being + looked for. + + iwpriv ethX fwt_lookup da + + where:- + da -- DA MAC address (in the form "00:11:22:33:44:55") + + The command returns an output string identical to the one returned by + fwt_list described below. + + +fwt_list + This command is used to list a route from the FWT table. The only + parameter is the index into the table. If you want to list all the + routes in a table, start with index=0, and keep listing until you get a + "(null)" string. Note that the indicies may change as the fwt is + updated. It is expected that most users will not use fwt_list directly, + but that a utility similar to the traditional route command will be used + to invoke fwt_list over and over. + + iwpriv ethX fwt_list index + + The output is a string of the following form: + + da ra metric dir ssn dsn hopcount ttl expiration sleepmode snr + + where the different fields are:- + da -- DA MAC address (in the form "00:11:22:33:44:55") + ra -- RA MAC address (in the form "00:11:22:33:44:55") + metric -- route metric (cost: smaller-metric routes are preferred) + dir -- direction (1 for direct, 0 for reverse) + ssn -- Source Sequence Number (time at the RA for reverse routes) + dsn -- Destination Sequence Number (time at the DA for direct routes) + hopcount -- hop count (currently unused) + ttl -- TTL (only used in reverse entries) + expiration -- entry expiration (in ticks, where a tick is 1024us, or ~ 1ms. Use 0 for an indefinite entry) + sleepmode -- RA's sleep mode (currently unused) + snr -- SNR in the link to RA (currently unused) + +fwt_list_route + This command is used to list a route from the FWT table. The only + parameter is the route ID. If you want to list all the routes in a + table, start with rid=0, and keep incrementing rid until you get a + "(null)" string. This function is similar to fwt_list. The only + difference is the output format. Also note that this command is meant + for debugging. It is expected that users will use fwt_lookup and + fwt_list. One important reason for this is that the route id may change + as the route table is altered. + + iwpriv ethX fwt_list_route rid + + The output is a string of the following form: + + da metric dir nid ssn dsn hopcount ttl expiration + + where the different fields are:- + da -- DA MAC address (in the form "00:11:22:33:44:55") + metric -- route metric (cost: smaller-metric routes are preferred) + dir -- direction (1 for direct, 0 for reverse) + nid -- Next-hop (neighbor) host ID (nid) + ssn -- Source Sequence Number (time at the RA for reverse routes) + dsn -- Destination Sequence Number (time at the DA for direct routes) + hopcount -- hop count (currently unused) + ttl -- TTL count (only used in reverse entries) + expiration -- entry expiration (in ticks, where a tick is 1024us, or ~ 1ms. Use 0 for an indefinite entry) + +fwt_list_neigh + This command is used to list a neighbor from the FWT table. The only + parameter is the neighbor ID. If you want to list all the neighbors in a + table, start with nid=0, and keep incrementing nid until you get a + "(null)" string. Note that the nid from a fwt_list_route command can be + used as an input to this command. Also note that this command is meant + mostly for debugging. It is expected that users will use fwt_lookup. + One important reason for this is that the neighbor id may change as the + neighbor table is altered. + + iwpriv ethX fwt_list_neigh nid + + The output is a string of the following form: + + ra sleepmode snr references + + where the different fields are:- + ra -- RA MAC address (in the form "00:11:22:33:44:55") + sleepmode -- RA's sleep mode (currently unused) + snr -- SNR in the link to RA (currently unused) + references -- RA's reference counter + +fwt_reset + This command is used to reset the FWT table, getting rid of all the + entries. There are no input parameters. + + iwpriv ethX fwt_reset + + The command does not return anything. + +fwt_cleanup + This command is used to perform user-based garbage recollection. The + FWT table is checked, and all the entries that are expired or invalid + are cleaned. Note that this is exported to the driver for debugging + purposes, as garbage collection is also fired by the firmware when in + space problems. There are no input parameters. + + iwpriv ethX fwt_cleanup + + The command does returns the number of invalid/expired routes deleted. + +fwt_time + This command returns a card's internal time representation. It is this + time that is used to represent the expiration times of FWT entries. The + number is not consistent from card to card; it is simply a timer count. + The fwt_time command is used to inspect the timer so that expiration + times reported by fwt_list can be properly interpreted. + + iwpriv ethX fwt_time + +mesh_get_ttl + + The mesh ttl is the number of hops a mesh packet can traverse before it + is dropped. This parameter is used to prevent infinite loops in the + mesh network. The value returned by this function is the ttl assigned + to all mesh packets. Currently there is no way to control the ttl on a + per packet or per socket basis. + + iwpriv ethX mesh_get_ttl + +mesh_set_ttl ttl + + Set the ttl. The argument must be between 0 and 255. + + iwpriv ethX mesh_set_ttl <ttl> + +========================= +ETHTOOL +========================= + + +Use the -i option to retrieve version information from the driver. + +# ethtool -i eth0 +driver: libertas +version: COMM-USB8388-318.p4 +firmware-version: 5.110.7 +bus-info: + +Use the -e option to read the EEPROM contents of the card. + + Usage: + ethtool -e ethX [raw on|off] [offset N] [length N] + + -e retrieves and prints an EEPROM dump for the specified ethernet + device. When raw is enabled, then it dumps the raw EEPROM data + to stdout. The length and offset parameters allow dumping cer- + tain portions of the EEPROM. Default is to dump the entire EEP- + ROM. + +# ethtool -e eth0 offset 0 length 16 +Offset Values +------ ------ +0x0000 38 33 30 58 00 00 34 f4 00 00 10 00 00 c4 17 00 + +======================== +DEBUGFS COMMANDS +======================== + +those commands are used via debugfs interface + +=========== +rdmac +rdbbp +rdrf + These commands are used to read the MAC, BBP and RF registers from the + card. These commands take one parameter that specifies the offset + location that is to be read. This parameter must be specified in + hexadecimal (its possible to preceed preceding the number with a "0x"). + + Path: /debugfs/libertas_wireless/ethX/registers/ + + Usage: + echo "0xa123" > rdmac ; cat rdmac + echo "0xa123" > rdbbp ; cat rdbbp + echo "0xa123" > rdrf ; cat rdrf +wrmac +wrbbp +wrrf + These commands are used to write the MAC, BBP and RF registers in the + card. These commands take two parameters that specify the offset + location and the value that is to be written. This parameters must + be specified in hexadecimal (its possible to preceed the number + with a "0x"). + + Usage: + echo "0xa123 0xaa" > wrmac + echo "0xa123 0xaa" > wrbbp + echo "0xa123 0xaa" > wrrf + +sleepparams + This command is used to set the sleepclock configurations + + Path: /debugfs/libertas_wireless/ethX/ + + Usage: + cat sleepparams: reads the current sleepclock configuration + + echo "p1 p2 p3 p4 p5 p6" > sleepparams: writes the sleepclock configuration. + + where: + p1 is Sleep clock error in ppm (0-65535) + p2 is Wakeup offset in usec (0-65535) + p3 is Clock stabilization time in usec (0-65535) + p4 is Control periodic calibration (0-2) + p5 is Control the use of external sleep clock (0-2) + p6 is reserved for debug (0-65535) + +subscribed_events + + The subscribed_events directory contains the interface for the + subscribed events API. + + Path: /debugfs/libertas_wireless/ethX/subscribed_events/ + + Each event is represented by a filename. Each filename consists of the + following three fields: + Value Frequency Subscribed + + To read the current values for a given event, do: + cat event + To set the current values, do: + echo "60 2 1" > event + + Frequency field specifies the reporting frequency for this event. + If it is set to 0, then the event is reported only once, and then + automatically unsubscribed. If it is set to 1, then the event is + reported every time it occurs. If it is set to N, then the event is + reported every Nth time it occurs. + + beacon_missed + Value field specifies the number of consecutive missing beacons which + triggers the LINK_LOSS event. This event is generated only once after + which the firmware resets its state. At initialization, the LINK_LOSS + event is subscribed by default. The default value of MissedBeacons is + 60. + + failure_count + Value field specifies the consecutive failure count threshold which + triggers the generation of the MAX_FAIL event. Once this event is + generated, the consecutive failure count is reset to 0. + At initialization, the MAX_FAIL event is NOT subscribed by + default. + + high_rssi + This event is generated when the average received RSSI in beacons goes + above a threshold, specified by Value. + + low_rssi + This event is generated when the average received RSSI in beacons goes + below a threshold, specified by Value. + + high_snr + This event is generated when the average received SNR in beacons goes + above a threshold, specified by Value. + + low_snr + This event is generated when the average received SNR in beacons goes + below a threshold, specified by Value. + +extscan + This command is used to do a specific scan. + + Path: /debugfs/libertas_wireless/ethX/ + + Usage: echo "SSID" > extscan + + Example: + echo "LINKSYS-AP" > extscan + + To see the results of use getscantable command. + +getscantable + + Display the current contents of the driver scan table (ie. get the + scan results). + + Path: /debugfs/libertas_wireless/ethX/ + + Usage: + cat getscantable + +setuserscan + Initiate a customized scan and retrieve the results + + + Path: /debugfs/libertas_wireless/ethX/ + + Usage: + echo "[ARGS]" > setuserscan + + where [ARGS]: + + chan=[chan#][band][mode] where band is [a,b,g] and mode is + blank for active or 'p' for passive + bssid=xx:xx:xx:xx:xx:xx specify a BSSID filter for the scan + ssid="[SSID]" specify a SSID filter for the scan + keep=[0 or 1] keep the previous scan results (1), discard (0) + dur=[scan time] time to scan for each channel in milliseconds + probes=[#] number of probe requests to send on each chan + type=[1,2,3] BSS type: 1 (Infra), 2(Adhoc), 3(Any) + + Any combination of the above arguments can be supplied on the command line. + If the chan token is absent, a full channel scan will be completed by + the driver. If the dur or probes tokens are absent, the driver default + setting will be used. The bssid and ssid fields, if blank, + will produce an unfiltered scan. The type field will default to 3 (Any) + and the keep field will default to 0 (Discard). + + Examples: + 1) Perform an active scan on channels 1, 6, and 11 in the 'g' band: + echo "chan=1g,6g,11g" > setuserscan + + 2) Perform a passive scan on channel 11 for 20 ms: + echo "chan=11gp dur=20" > setuserscan + + 3) Perform an active scan on channels 1, 6, and 11; and a passive scan on + channel 36 in the 'a' band: + + echo "chan=1g,6g,11g,36ap" > setuserscan + + 4) Perform an active scan on channel 6 and 36 for a specific SSID: + echo "chan=6g,36a ssid="TestAP"" > setuserscan + + 5) Scan all available channels (B/G, A bands) for a specific BSSID, keep + the current scan table intact, update existing or append new scan data: + echo "bssid=00:50:43:20:12:82 keep=1" > setuserscan + + 6) Scan channel 6, for all infrastructure networks, sending two probe + requests. Keep the previous scan table intact. Update any duplicate + BSSID/SSID matches with the new scan data: + echo "chan=6g type=1 probes=2 keep=1" > setuserscan + + All entries in the scan table (not just the new scan data when keep=1) + will be displayed upon completion by use of the getscantable ioctl. + +============================================================================== diff --git a/drivers/net/wireless/libertas/assoc.c b/drivers/net/wireless/libertas/assoc.c new file mode 100644 index 00000000000..b55c7f57aca --- /dev/null +++ b/drivers/net/wireless/libertas/assoc.c @@ -0,0 +1,588 @@ +/* Copyright (C) 2006, Red Hat, Inc. */ + +#include <linux/bitops.h> +#include <net/ieee80211.h> + +#include "assoc.h" +#include "join.h" +#include "decl.h" +#include "hostcmd.h" +#include "host.h" + + +static const u8 bssid_any[ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; +static const u8 bssid_off[ETH_ALEN] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + +static int assoc_helper_essid(wlan_private *priv, + struct assoc_request * assoc_req) +{ + wlan_adapter *adapter = priv->adapter; + int ret = 0; + int i; + + ENTER(); + + lbs_pr_debug(1, "New SSID requested: %s\n", assoc_req->ssid.ssid); + if (assoc_req->mode == wlan802_11infrastructure) { + if (adapter->prescan) { + libertas_send_specific_SSID_scan(priv, &assoc_req->ssid, 1); + } + + i = libertas_find_SSID_in_list(adapter, &assoc_req->ssid, + NULL, wlan802_11infrastructure); + if (i >= 0) { + lbs_pr_debug(1, + "SSID found in scan list ... associating...\n"); + + ret = wlan_associate(priv, &adapter->scantable[i]); + if (ret == 0) { + memcpy(&assoc_req->bssid, + &adapter->scantable[i].macaddress, + ETH_ALEN); + } + } else { + lbs_pr_debug(1, "SSID '%s' not found; cannot associate\n", + assoc_req->ssid.ssid); + } + } else if (assoc_req->mode == wlan802_11ibss) { + /* Scan for the network, do not save previous results. Stale + * scan data will cause us to join a non-existant adhoc network + */ + libertas_send_specific_SSID_scan(priv, &assoc_req->ssid, 0); + + /* Search for the requested SSID in the scan table */ + i = libertas_find_SSID_in_list(adapter, &assoc_req->ssid, NULL, + wlan802_11ibss); + if (i >= 0) { + lbs_pr_debug(1, "SSID found at %d in List, so join\n", ret); + libertas_join_adhoc_network(priv, &adapter->scantable[i]); + } else { + /* else send START command */ + lbs_pr_debug(1, "SSID not found in list, so creating adhoc" + " with SSID '%s'\n", assoc_req->ssid.ssid); + libertas_start_adhoc_network(priv, &assoc_req->ssid); + } + memcpy(&assoc_req->bssid, &adapter->current_addr, ETH_ALEN); + } + + LEAVE(); + return ret; +} + + +static int assoc_helper_bssid(wlan_private *priv, + struct assoc_request * assoc_req) +{ + wlan_adapter *adapter = priv->adapter; + int i, ret = 0; + + ENTER(); + + lbs_pr_debug(1, "ASSOC: WAP: BSSID = " MAC_FMT "\n", + MAC_ARG(assoc_req->bssid)); + + /* Search for index position in list for requested MAC */ + i = libertas_find_BSSID_in_list(adapter, assoc_req->bssid, + assoc_req->mode); + if (i < 0) { + lbs_pr_debug(1, "ASSOC: WAP: BSSID " MAC_FMT " not found, " + "cannot associate.\n", MAC_ARG(assoc_req->bssid)); + goto out; + } + + if (assoc_req->mode == wlan802_11infrastructure) { + ret = wlan_associate(priv, &adapter->scantable[i]); + lbs_pr_debug(1, "ASSOC: return from wlan_associate(bssd) was %d\n", ret); + } else if (assoc_req->mode == wlan802_11ibss) { + libertas_join_adhoc_network(priv, &adapter->scantable[i]); + } + memcpy(&assoc_req->ssid, &adapter->scantable[i].ssid, + sizeof(struct WLAN_802_11_SSID)); + +out: + LEAVE(); + return ret; +} + + +static int assoc_helper_associate(wlan_private *priv, + struct assoc_request * assoc_req) +{ + int ret = 0, done = 0; + + /* If we're given and 'any' BSSID, try associating based on SSID */ + + if (test_bit(ASSOC_FLAG_BSSID, &assoc_req->flags)) { + if (memcmp(bssid_any, assoc_req->bssid, ETH_ALEN) + && memcmp(bssid_off, assoc_req->bssid, ETH_ALEN)) { + ret = assoc_helper_bssid(priv, assoc_req); + done = 1; + if (ret) { + lbs_pr_debug(1, "ASSOC: bssid: ret = %d\n", ret); + } + } + } + + if (!done && test_bit(ASSOC_FLAG_SSID, &assoc_req->flags)) { + ret = assoc_helper_essid(priv, assoc_req); + if (ret) { + lbs_pr_debug(1, "ASSOC: bssid: ret = %d\n", ret); + } + } + + return ret; +} + + +static int assoc_helper_mode(wlan_private *priv, + struct assoc_request * assoc_req) +{ + wlan_adapter *adapter = priv->adapter; + int ret = 0; + + ENTER(); + + if (assoc_req->mode == adapter->inframode) { + LEAVE(); + return 0; + } + + if (assoc_req->mode == wlan802_11infrastructure) { + if (adapter->psstate != PS_STATE_FULL_POWER) + libertas_ps_wakeup(priv, cmd_option_waitforrsp); + adapter->psmode = wlan802_11powermodecam; + } + + adapter->inframode = assoc_req->mode; + ret = libertas_prepare_and_send_command(priv, + cmd_802_11_snmp_mib, + 0, cmd_option_waitforrsp, + OID_802_11_INFRASTRUCTURE_MODE, + (void *) assoc_req->mode); + + LEAVE(); + return ret; +} + + +static int assoc_helper_wep_keys(wlan_private *priv, + struct assoc_request * assoc_req) +{ + wlan_adapter *adapter = priv->adapter; + int i; + int ret = 0; + + ENTER(); + + /* Set or remove WEP keys */ + if ( assoc_req->wep_keys[0].len + || assoc_req->wep_keys[1].len + || assoc_req->wep_keys[2].len + || assoc_req->wep_keys[3].len) { + ret = libertas_prepare_and_send_command(priv, + cmd_802_11_set_wep, + cmd_act_add, + cmd_option_waitforrsp, + 0, assoc_req); + } else { + ret = libertas_prepare_and_send_command(priv, + cmd_802_11_set_wep, + cmd_act_remove, + cmd_option_waitforrsp, + 0, NULL); + } + + if (ret) + goto out; + + /* enable/disable the MAC's WEP packet filter */ + if (assoc_req->secinfo.WEPstatus == wlan802_11WEPenabled) + adapter->currentpacketfilter |= cmd_act_mac_wep_enable; + else + adapter->currentpacketfilter &= ~cmd_act_mac_wep_enable; + ret = libertas_set_mac_packet_filter(priv); + if (ret) + goto out; + + mutex_lock(&adapter->lock); + + /* Copy WEP keys into adapter wep key fields */ + for (i = 0; i < 4; i++) { + memcpy(&adapter->wep_keys[i], &assoc_req->wep_keys[i], + sizeof(struct WLAN_802_11_KEY)); + } + adapter->wep_tx_keyidx = assoc_req->wep_tx_keyidx; + + mutex_unlock(&adapter->lock); + +out: + LEAVE(); + return ret; +} + +static int assoc_helper_secinfo(wlan_private *priv, + struct assoc_request * assoc_req) +{ + wlan_adapter *adapter = priv->adapter; + int ret = 0; + + ENTER(); + + memcpy(&adapter->secinfo, &assoc_req->secinfo, + sizeof(struct wlan_802_11_security)); + + ret = libertas_set_mac_packet_filter(priv); + + LEAVE(); + return ret; +} + + +static int assoc_helper_wpa_keys(wlan_private *priv, + struct assoc_request * assoc_req) +{ + int ret = 0; + + ENTER(); + + /* enable/Disable RSN */ + ret = libertas_prepare_and_send_command(priv, + cmd_802_11_enable_rsn, + cmd_act_set, + cmd_option_waitforrsp, + 0, assoc_req); + if (ret) + goto out; + + ret = libertas_prepare_and_send_command(priv, + cmd_802_11_key_material, + cmd_act_set, + cmd_option_waitforrsp, + 0, assoc_req); + +out: + LEAVE(); + return ret; +} + + +static int assoc_helper_wpa_ie(wlan_private *priv, + struct assoc_request * assoc_req) +{ + wlan_adapter *adapter = priv->adapter; + int ret = 0; + + ENTER(); + + if (assoc_req->secinfo.WPAenabled || assoc_req->secinfo.WPA2enabled) { + memcpy(&adapter->wpa_ie, &assoc_req->wpa_ie, assoc_req->wpa_ie_len); + adapter->wpa_ie_len = assoc_req->wpa_ie_len; + } else { + memset(&adapter->wpa_ie, 0, MAX_WPA_IE_LEN); + adapter->wpa_ie_len = 0; + } + + LEAVE(); + return ret; +} + + +static int should_deauth_infrastructure(wlan_adapter *adapter, + struct assoc_request * assoc_req) +{ + if (adapter->connect_status != libertas_connected) + return 0; + + if (test_bit(ASSOC_FLAG_SSID, &assoc_req->flags)) { + lbs_pr_debug(1, "Deauthenticating due to new SSID in " + " configuration request.\n"); + return 1; + } + + if (test_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags)) { + if (adapter->secinfo.authmode != + assoc_req->secinfo.authmode) { + lbs_pr_debug(1, "Deauthenticating due to updated security " + "info in configuration request.\n"); + return 1; + } + } + + if (test_bit(ASSOC_FLAG_BSSID, &assoc_req->flags)) { + lbs_pr_debug(1, "Deauthenticating due to new BSSID in " + " configuration request.\n"); + return 1; + } + + /* FIXME: deal with 'auto' mode somehow */ + if (test_bit(ASSOC_FLAG_MODE, &assoc_req->flags)) { + if (assoc_req->mode != wlan802_11infrastructure) + return 1; + } + + return 0; +} + + +static int should_stop_adhoc(wlan_adapter *adapter, + struct assoc_request * assoc_req) +{ + if (adapter->connect_status != libertas_connected) + return 0; + + if (adapter->curbssparams.ssid.ssidlength != assoc_req->ssid.ssidlength) + return 1; + if (memcmp(adapter->curbssparams.ssid.ssid, assoc_req->ssid.ssid, + sizeof(struct WLAN_802_11_SSID))) + return 1; + + /* FIXME: deal with 'auto' mode somehow */ + if (test_bit(ASSOC_FLAG_MODE, &assoc_req->flags)) { + if (assoc_req->mode != wlan802_11ibss) + return 1; + } + + return 0; +} + + +void wlan_association_worker(struct work_struct *work) +{ + wlan_private *priv = container_of(work, wlan_private, assoc_work.work); + wlan_adapter *adapter = priv->adapter; + struct assoc_request * assoc_req = NULL; + int ret = 0; + int find_any_ssid = 0; + + ENTER(); + + mutex_lock(&adapter->lock); + assoc_req = adapter->assoc_req; + adapter->assoc_req = NULL; + mutex_unlock(&adapter->lock); + + if (!assoc_req) { + LEAVE(); + return; + } + + lbs_pr_debug(1, "ASSOC: starting new association request: flags = 0x%lX\n", + assoc_req->flags); + + /* If 'any' SSID was specified, find an SSID to associate with */ + if (test_bit(ASSOC_FLAG_SSID, &assoc_req->flags) + && !assoc_req->ssid.ssidlength) + find_any_ssid = 1; + + /* But don't use 'any' SSID if there's a valid locked BSSID to use */ + if (test_bit(ASSOC_FLAG_BSSID, &assoc_req->flags)) { + if (memcmp(&assoc_req->bssid, bssid_any, ETH_ALEN) + && memcmp(&assoc_req->bssid, bssid_off, ETH_ALEN)) + find_any_ssid = 0; + } + + if (find_any_ssid) { + enum WLAN_802_11_NETWORK_INFRASTRUCTURE new_mode; + + ret = libertas_find_best_network_SSID(priv, &assoc_req->ssid, + assoc_req->mode, &new_mode); + if (ret) { + lbs_pr_debug(1, "Could not find best network\n"); + ret = -ENETUNREACH; + goto out; + } + + /* Ensure we switch to the mode of the AP */ + if (assoc_req->mode == wlan802_11autounknown) { + set_bit(ASSOC_FLAG_MODE, &assoc_req->flags); + assoc_req->mode = new_mode; + } + } + + /* + * Check if the attributes being changing require deauthentication + * from the currently associated infrastructure access point. + */ + if (adapter->inframode == wlan802_11infrastructure) { + if (should_deauth_infrastructure(adapter, assoc_req)) { + ret = libertas_send_deauthentication(priv); + if (ret) { + lbs_pr_debug(1, "Deauthentication due to new " + "configuration request failed: %d\n", + ret); + } + } + } else if (adapter->inframode == wlan802_11ibss) { + if (should_stop_adhoc(adapter, assoc_req)) { + ret = libertas_stop_adhoc_network(priv); + if (ret) { + lbs_pr_debug(1, "Teardown of AdHoc network due to " + "new configuration request failed: %d\n", + ret); + } + + } + } + + /* Send the various configuration bits to the firmware */ + if (test_bit(ASSOC_FLAG_MODE, &assoc_req->flags)) { + ret = assoc_helper_mode(priv, assoc_req); + if (ret) { +lbs_pr_debug(1, "ASSOC(:%d) mode: ret = %d\n", __LINE__, ret); + goto out; + } + } + + if ( test_bit(ASSOC_FLAG_WEP_KEYS, &assoc_req->flags) + || test_bit(ASSOC_FLAG_WEP_TX_KEYIDX, &assoc_req->flags)) { + ret = assoc_helper_wep_keys(priv, assoc_req); + if (ret) { +lbs_pr_debug(1, "ASSOC(:%d) wep_keys: ret = %d\n", __LINE__, ret); + goto out; + } + } + + if (test_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags)) { + ret = assoc_helper_secinfo(priv, assoc_req); + if (ret) { +lbs_pr_debug(1, "ASSOC(:%d) secinfo: ret = %d\n", __LINE__, ret); + goto out; + } + } + + if (test_bit(ASSOC_FLAG_WPA_IE, &assoc_req->flags)) { + ret = assoc_helper_wpa_ie(priv, assoc_req); + if (ret) { +lbs_pr_debug(1, "ASSOC(:%d) wpa_ie: ret = %d\n", __LINE__, ret); + goto out; + } + } + + if (test_bit(ASSOC_FLAG_WPA_MCAST_KEY, &assoc_req->flags) + || test_bit(ASSOC_FLAG_WPA_UCAST_KEY, &assoc_req->flags)) { + ret = assoc_helper_wpa_keys(priv, assoc_req); + if (ret) { +lbs_pr_debug(1, "ASSOC(:%d) wpa_keys: ret = %d\n", __LINE__, ret); + goto out; + } + } + + /* SSID/BSSID should be the _last_ config option set, because they + * trigger the association attempt. + */ + if (test_bit(ASSOC_FLAG_BSSID, &assoc_req->flags) + || test_bit(ASSOC_FLAG_SSID, &assoc_req->flags)) { + int success = 1; + + ret = assoc_helper_associate(priv, assoc_req); + if (ret) { + lbs_pr_debug(1, "ASSOC: association attempt unsuccessful: %d\n", + ret); + success = 0; + } + + if (adapter->connect_status != libertas_connected) { + lbs_pr_debug(1, "ASSOC: assoication attempt unsuccessful, " + "not connected.\n"); + success = 0; + } + + if (success) { + lbs_pr_debug(1, "ASSOC: association attempt successful. " + "Associated to '%s' (" MAC_FMT ")\n", + assoc_req->ssid.ssid, MAC_ARG(assoc_req->bssid)); + libertas_prepare_and_send_command(priv, + cmd_802_11_rssi, + 0, cmd_option_waitforrsp, 0, NULL); + + libertas_prepare_and_send_command(priv, + cmd_802_11_get_log, + 0, cmd_option_waitforrsp, 0, NULL); + } else { + + ret = -1; + } + } + +out: + if (ret) { + lbs_pr_debug(1, "ASSOC: reconfiguration attempt unsuccessful: %d\n", + ret); + } + kfree(assoc_req); + LEAVE(); +} + + +/* + * Caller MUST hold any necessary locks + */ +struct assoc_request * wlan_get_association_request(wlan_adapter *adapter) +{ + struct assoc_request * assoc_req; + + if (!adapter->assoc_req) { + adapter->assoc_req = kzalloc(sizeof(struct assoc_request), GFP_KERNEL); + if (!adapter->assoc_req) { + lbs_pr_info("Not enough memory to allocate association" + " request!\n"); + return NULL; + } + } + + /* Copy current configuration attributes to the association request, + * but don't overwrite any that are already set. + */ + assoc_req = adapter->assoc_req; + if (!test_bit(ASSOC_FLAG_SSID, &assoc_req->flags)) { + memcpy(&assoc_req->ssid, adapter->curbssparams.ssid.ssid, + adapter->curbssparams.ssid.ssidlength); + } + + if (!test_bit(ASSOC_FLAG_CHANNEL, &assoc_req->flags)) + assoc_req->channel = adapter->curbssparams.channel; + + if (!test_bit(ASSOC_FLAG_MODE, &assoc_req->flags)) + assoc_req->mode = adapter->inframode; + + if (!test_bit(ASSOC_FLAG_BSSID, &assoc_req->flags)) { + memcpy(&assoc_req->bssid, adapter->curbssparams.bssid, + ETH_ALEN); + } + + if (!test_bit(ASSOC_FLAG_WEP_KEYS, &assoc_req->flags)) { + int i; + for (i = 0; i < 4; i++) { + memcpy(&assoc_req->wep_keys[i], &adapter->wep_keys[i], + sizeof(struct WLAN_802_11_KEY)); + } + } + + if (!test_bit(ASSOC_FLAG_WEP_TX_KEYIDX, &assoc_req->flags)) + assoc_req->wep_tx_keyidx = adapter->wep_tx_keyidx; + + if (!test_bit(ASSOC_FLAG_WPA_MCAST_KEY, &assoc_req->flags)) { + memcpy(&assoc_req->wpa_mcast_key, &adapter->wpa_mcast_key, + sizeof(struct WLAN_802_11_KEY)); + } + + if (!test_bit(ASSOC_FLAG_WPA_UCAST_KEY, &assoc_req->flags)) { + memcpy(&assoc_req->wpa_unicast_key, &adapter->wpa_unicast_key, + sizeof(struct WLAN_802_11_KEY)); + } + + if (!test_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags)) { + memcpy(&assoc_req->secinfo, &adapter->secinfo, + sizeof(struct wlan_802_11_security)); + } + + if (!test_bit(ASSOC_FLAG_WPA_IE, &assoc_req->flags)) { + memcpy(&assoc_req->wpa_ie, &adapter->wpa_ie, + MAX_WPA_IE_LEN); + assoc_req->wpa_ie_len = adapter->wpa_ie_len; + } + + return assoc_req; +} + + diff --git a/drivers/net/wireless/libertas/assoc.h b/drivers/net/wireless/libertas/assoc.h new file mode 100644 index 00000000000..2ffd82d99b3 --- /dev/null +++ b/drivers/net/wireless/libertas/assoc.h @@ -0,0 +1,30 @@ +/* Copyright (C) 2006, Red Hat, Inc. */ + +#ifndef _WLAN_ASSOC_H_ +#define _WLAN_ASSOC_H_ + +#include "dev.h" + +void wlan_association_worker(struct work_struct *work); + +struct assoc_request * wlan_get_association_request(wlan_adapter *adapter); + +#define ASSOC_DELAY (HZ / 2) +static inline void wlan_postpone_association_work(wlan_private *priv) +{ + if (priv->adapter->surpriseremoved) + return; + cancel_delayed_work(&priv->assoc_work); + queue_delayed_work(priv->assoc_thread, &priv->assoc_work, ASSOC_DELAY); +} + +static inline void wlan_cancel_association_work(wlan_private *priv) +{ + cancel_delayed_work(&priv->assoc_work); + if (priv->adapter->assoc_req) { + kfree(priv->adapter->assoc_req); + priv->adapter->assoc_req = NULL; + } +} + +#endif /* _WLAN_ASSOC_H */ diff --git a/drivers/net/wireless/libertas/cmd.c b/drivers/net/wireless/libertas/cmd.c new file mode 100644 index 00000000000..bfdac58b5c0 --- /dev/null +++ b/drivers/net/wireless/libertas/cmd.c @@ -0,0 +1,1958 @@ +/** + * This file contains the handling of command. + * It prepares command and sends it to firmware when it is ready. + */ + +#include <net/iw_handler.h> +#include "host.h" +#include "hostcmd.h" +#include "sbi.h" +#include "decl.h" +#include "defs.h" +#include "dev.h" +#include "join.h" +#include "wext.h" + +static void cleanup_cmdnode(struct cmd_ctrl_node *ptempnode); + +static u16 commands_allowed_in_ps[] = { + cmd_802_11_rssi, +}; + +/** + * @brief This function checks if the commans is allowed + * in PS mode not. + * + * @param command the command ID + * @return TRUE or FALSE + */ +static u8 is_command_allowed_in_ps(u16 command) +{ + int count = sizeof(commands_allowed_in_ps) + / sizeof(commands_allowed_in_ps[0]); + int i; + + for (i = 0; i < count; i++) { + if (command == cpu_to_le16(commands_allowed_in_ps[i])) + return 1; + } + + return 0; +} + +static int wlan_cmd_hw_spec(wlan_private * priv, struct cmd_ds_command *cmd) +{ + struct cmd_ds_get_hw_spec *hwspec = &cmd->params.hwspec; + + ENTER(); + + cmd->command = cpu_to_le16(cmd_get_hw_spec); + cmd->size = + cpu_to_le16(sizeof(struct cmd_ds_get_hw_spec) + S_DS_GEN); + memcpy(hwspec->permanentaddr, priv->adapter->current_addr, ETH_ALEN); + + LEAVE(); + return 0; +} + +static int wlan_cmd_802_11_ps_mode(wlan_private * priv, + struct cmd_ds_command *cmd, + u16 cmd_action) +{ + struct cmd_ds_802_11_ps_mode *psm = &cmd->params.psmode; + u16 action = cmd_action; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + cmd->command = cpu_to_le16(cmd_802_11_ps_mode); + cmd->size = + cpu_to_le16(sizeof(struct cmd_ds_802_11_ps_mode) + + S_DS_GEN); + psm->action = cpu_to_le16(cmd_action); + psm->multipledtim = 0; + switch (action) { + case cmd_subcmd_enter_ps: + lbs_pr_debug(1, "PS command:" "SubCode- Enter PS\n"); + lbs_pr_debug(1, "locallisteninterval = %d\n", + adapter->locallisteninterval); + + psm->locallisteninterval = + cpu_to_le16(adapter->locallisteninterval); + psm->nullpktinterval = + cpu_to_le16(adapter->nullpktinterval); + psm->multipledtim = + cpu_to_le16(priv->adapter->multipledtim); + break; + + case cmd_subcmd_exit_ps: + lbs_pr_debug(1, "PS command:" "SubCode- Exit PS\n"); + break; + + case cmd_subcmd_sleep_confirmed: + lbs_pr_debug(1, "PS command: SubCode- sleep confirm\n"); + break; + + default: + break; + } + + LEAVE(); + return 0; +} + +static int wlan_cmd_802_11_inactivity_timeout(wlan_private * priv, + struct cmd_ds_command *cmd, + u16 cmd_action, void *pdata_buf) +{ + u16 *timeout = pdata_buf; + + cmd->command = cpu_to_le16(cmd_802_11_inactivity_timeout); + cmd->size = + cpu_to_le16(sizeof(struct cmd_ds_802_11_inactivity_timeout) + + S_DS_GEN); + + cmd->params.inactivity_timeout.action = cpu_to_le16(cmd_action); + + if (cmd_action) + cmd->params.inactivity_timeout.timeout = + cpu_to_le16(*timeout); + else + cmd->params.inactivity_timeout.timeout = 0; + + return 0; +} + +static int wlan_cmd_802_11_sleep_params(wlan_private * priv, + struct cmd_ds_command *cmd, + u16 cmd_action) +{ + wlan_adapter *adapter = priv->adapter; + struct cmd_ds_802_11_sleep_params *sp = &cmd->params.sleep_params; + + ENTER(); + + cmd->size = + cpu_to_le16((sizeof(struct cmd_ds_802_11_sleep_params)) + + S_DS_GEN); + cmd->command = cpu_to_le16(cmd_802_11_sleep_params); + + if (cmd_action == cmd_act_get) { + memset(&adapter->sp, 0, sizeof(struct sleep_params)); + memset(sp, 0, sizeof(struct cmd_ds_802_11_sleep_params)); + sp->action = cpu_to_le16(cmd_action); + } else if (cmd_action == cmd_act_set) { + sp->action = cpu_to_le16(cmd_action); + sp->error = cpu_to_le16(adapter->sp.sp_error); + sp->offset = cpu_to_le16(adapter->sp.sp_offset); + sp->stabletime = cpu_to_le16(adapter->sp.sp_stabletime); + sp->calcontrol = (u8) adapter->sp.sp_calcontrol; + sp->externalsleepclk = (u8) adapter->sp.sp_extsleepclk; + sp->reserved = cpu_to_le16(adapter->sp.sp_reserved); + } + + LEAVE(); + return 0; +} + +static int wlan_cmd_802_11_set_wep(wlan_private * priv, + struct cmd_ds_command *cmd, + u32 cmd_act, + void * pdata_buf) +{ + struct cmd_ds_802_11_set_wep *wep = &cmd->params.wep; + wlan_adapter *adapter = priv->adapter; + int ret = 0; + struct assoc_request * assoc_req = pdata_buf; + + ENTER(); + + cmd->command = cpu_to_le16(cmd_802_11_set_wep); + cmd->size = cpu_to_le16((sizeof(struct cmd_ds_802_11_set_wep)) + + S_DS_GEN); + + if (cmd_act == cmd_act_add) { + int i; + + if (!assoc_req) { + lbs_pr_debug(1, "Invalid association request!"); + ret = -1; + goto done; + } + + wep->action = cpu_to_le16(cmd_act_add); + + /* default tx key index */ + wep->keyindex = cpu_to_le16((u16) + (assoc_req->wep_tx_keyidx & + (u32)cmd_WEP_KEY_INDEX_MASK)); + + lbs_pr_debug(1, "Tx key Index: %u\n", wep->keyindex); + + /* Copy key types and material to host command structure */ + for (i = 0; i < 4; i++) { + struct WLAN_802_11_KEY * pkey = &assoc_req->wep_keys[i]; + + switch (pkey->len) { + case KEY_LEN_WEP_40: + wep->keytype[i] = cmd_type_wep_40_bit; + memmove(&wep->keymaterial[i], pkey->key, + pkey->len); + break; + case KEY_LEN_WEP_104: + wep->keytype[i] = cmd_type_wep_104_bit; + memmove(&wep->keymaterial[i], pkey->key, + pkey->len); + break; + case 0: + break; + default: + lbs_pr_debug(1, "Invalid WEP key %d length of %d\n", + i, pkey->len); + ret = -1; + goto done; + break; + } + } + } else if (cmd_act == cmd_act_remove) { + /* ACT_REMOVE clears _all_ WEP keys */ + wep->action = cpu_to_le16(cmd_act_remove); + + /* default tx key index */ + wep->keyindex = cpu_to_le16((u16) + (adapter->wep_tx_keyidx & + (u32)cmd_WEP_KEY_INDEX_MASK)); + } + + ret = 0; + +done: + LEAVE(); + return ret; +} + +static int wlan_cmd_802_11_enable_rsn(wlan_private * priv, + struct cmd_ds_command *cmd, + u16 cmd_action) +{ + struct cmd_ds_802_11_enable_rsn *penableRSN = &cmd->params.enbrsn; + wlan_adapter *adapter = priv->adapter; + + cmd->command = cpu_to_le16(cmd_802_11_enable_rsn); + cmd->size = + cpu_to_le16(sizeof(struct cmd_ds_802_11_enable_rsn) + + S_DS_GEN); + penableRSN->action = cpu_to_le16(cmd_action); + if (adapter->secinfo.WPAenabled || adapter->secinfo.WPA2enabled) { + penableRSN->enable = cpu_to_le16(cmd_enable_rsn); + } else { + penableRSN->enable = cpu_to_le16(cmd_disable_rsn); + } + + return 0; +} + + +static void set_one_wpa_key(struct MrvlIEtype_keyParamSet * pkeyparamset, + struct WLAN_802_11_KEY * pkey) +{ + pkeyparamset->keytypeid = cpu_to_le16(pkey->type); + + if (pkey->flags & KEY_INFO_WPA_ENABLED) { + pkeyparamset->keyinfo = cpu_to_le16(KEY_INFO_WPA_ENABLED); + } else { + pkeyparamset->keyinfo = cpu_to_le16(!KEY_INFO_WPA_ENABLED); + } + + if (pkey->flags & KEY_INFO_WPA_UNICAST) { + pkeyparamset->keyinfo |= cpu_to_le16(KEY_INFO_WPA_UNICAST); + } else if (pkey->flags & KEY_INFO_WPA_MCAST) { + pkeyparamset->keyinfo |= cpu_to_le16(KEY_INFO_WPA_MCAST); + } + + pkeyparamset->type = cpu_to_le16(TLV_TYPE_KEY_MATERIAL); + pkeyparamset->keylen = cpu_to_le16(pkey->len); + memcpy(pkeyparamset->key, pkey->key, pkey->len); + pkeyparamset->length = cpu_to_le16( sizeof(pkeyparamset->keytypeid) + + sizeof(pkeyparamset->keyinfo) + + sizeof(pkeyparamset->keylen) + + sizeof(pkeyparamset->key)); +} + +static int wlan_cmd_802_11_key_material(wlan_private * priv, + struct cmd_ds_command *cmd, + u16 cmd_action, + u32 cmd_oid, void *pdata_buf) +{ + wlan_adapter *adapter = priv->adapter; + struct cmd_ds_802_11_key_material *pkeymaterial = + &cmd->params.keymaterial; + int ret = 0; + int index = 0; + + ENTER(); + + cmd->command = cpu_to_le16(cmd_802_11_key_material); + pkeymaterial->action = cpu_to_le16(cmd_action); + + if (cmd_action == cmd_act_get) { + cmd->size = cpu_to_le16( S_DS_GEN + + sizeof (pkeymaterial->action)); + ret = 0; + goto done; + } + + memset(&pkeymaterial->keyParamSet, 0, sizeof(pkeymaterial->keyParamSet)); + + if (adapter->wpa_unicast_key.len) { + set_one_wpa_key(&pkeymaterial->keyParamSet[index], + &adapter->wpa_unicast_key); + index++; + } + + if (adapter->wpa_mcast_key.len) { + set_one_wpa_key(&pkeymaterial->keyParamSet[index], + &adapter->wpa_mcast_key); + index++; + } + + cmd->size = cpu_to_le16( S_DS_GEN + + sizeof (pkeymaterial->action) + + index * sizeof(struct MrvlIEtype_keyParamSet)); + + ret = 0; + +done: + LEAVE(); + return ret; +} + +static int wlan_cmd_802_11_reset(wlan_private * priv, + struct cmd_ds_command *cmd, int cmd_action) +{ + struct cmd_ds_802_11_reset *reset = &cmd->params.reset; + + cmd->command = cpu_to_le16(cmd_802_11_reset); + cmd->size = cpu_to_le16(sizeof(struct cmd_ds_802_11_reset) + S_DS_GEN); + reset->action = cpu_to_le16(cmd_action); + + return 0; +} + +static int wlan_cmd_802_11_get_log(wlan_private * priv, + struct cmd_ds_command *cmd) +{ + cmd->command = cpu_to_le16(cmd_802_11_get_log); + cmd->size = + cpu_to_le16(sizeof(struct cmd_ds_802_11_get_log) + S_DS_GEN); + + return 0; +} + +static int wlan_cmd_802_11_get_stat(wlan_private * priv, + struct cmd_ds_command *cmd) +{ + cmd->command = cpu_to_le16(cmd_802_11_get_stat); + cmd->size = + cpu_to_le16(sizeof(struct cmd_ds_802_11_get_stat) + + S_DS_GEN); + + return 0; +} + +static int wlan_cmd_802_11_snmp_mib(wlan_private * priv, + struct cmd_ds_command *cmd, + int cmd_action, + int cmd_oid, void *pdata_buf) +{ + struct cmd_ds_802_11_snmp_mib *pSNMPMIB = &cmd->params.smib; + wlan_adapter *adapter = priv->adapter; + u8 ucTemp; + + ENTER(); + + lbs_pr_debug(1, "SNMP_CMD: cmd_oid = 0x%x\n", cmd_oid); + + cmd->command = cpu_to_le16(cmd_802_11_snmp_mib); + cmd->size = + cpu_to_le16(sizeof(struct cmd_ds_802_11_snmp_mib) + + S_DS_GEN); + + switch (cmd_oid) { + case OID_802_11_INFRASTRUCTURE_MODE: + { + enum WLAN_802_11_NETWORK_INFRASTRUCTURE mode = + (enum WLAN_802_11_NETWORK_INFRASTRUCTURE) pdata_buf; + pSNMPMIB->querytype = cpu_to_le16(cmd_act_set); + pSNMPMIB->oid = cpu_to_le16((u16) desired_bsstype_i); + pSNMPMIB->bufsize = sizeof(u8); + if (mode == wlan802_11infrastructure) + ucTemp = SNMP_MIB_VALUE_INFRA; + else + ucTemp = SNMP_MIB_VALUE_ADHOC; + + memmove(pSNMPMIB->value, &ucTemp, sizeof(u8)); + + break; + } + + case OID_802_11D_ENABLE: + { + u32 ulTemp; + + pSNMPMIB->oid = cpu_to_le16((u16) dot11d_i); + + if (cmd_action == cmd_act_set) { + pSNMPMIB->querytype = cmd_act_set; + pSNMPMIB->bufsize = sizeof(u16); + ulTemp = *(u32 *)pdata_buf; + *((unsigned short *)(pSNMPMIB->value)) = + cpu_to_le16((u16) ulTemp); + } + break; + } + + case OID_802_11_FRAGMENTATION_THRESHOLD: + { + u32 ulTemp; + + pSNMPMIB->oid = cpu_to_le16((u16) fragthresh_i); + + if (cmd_action == cmd_act_get) { + pSNMPMIB->querytype = + cpu_to_le16(cmd_act_get); + } else if (cmd_action == cmd_act_set) { + pSNMPMIB->querytype = + cpu_to_le16(cmd_act_set); + pSNMPMIB->bufsize = + cpu_to_le16(sizeof(u16)); + ulTemp = *((u32 *) pdata_buf); + *((unsigned short *)(pSNMPMIB->value)) = + cpu_to_le16((u16) ulTemp); + + } + + break; + } + + case OID_802_11_RTS_THRESHOLD: + { + + u32 ulTemp; + pSNMPMIB->oid = le16_to_cpu((u16) rtsthresh_i); + + if (cmd_action == cmd_act_get) { + pSNMPMIB->querytype = + cpu_to_le16(cmd_act_get); + } else if (cmd_action == cmd_act_set) { + pSNMPMIB->querytype = + cpu_to_le16(cmd_act_set); + pSNMPMIB->bufsize = + cpu_to_le16(sizeof(u16)); + ulTemp = *((u32 *) + pdata_buf); + *(unsigned short *)(pSNMPMIB->value) = + cpu_to_le16((u16) ulTemp); + + } + break; + } + case OID_802_11_TX_RETRYCOUNT: + pSNMPMIB->oid = cpu_to_le16((u16) short_retrylim_i); + + if (cmd_action == cmd_act_get) { + pSNMPMIB->querytype = + cpu_to_le16(cmd_act_get); + } else if (cmd_action == cmd_act_set) { + pSNMPMIB->querytype = + cpu_to_le16(cmd_act_set); + pSNMPMIB->bufsize = cpu_to_le16(sizeof(u16)); + *((unsigned short *)(pSNMPMIB->value)) = + cpu_to_le16((u16) adapter->txretrycount); + } + + break; + default: + break; + } + + lbs_pr_debug(1, + "SNMP_CMD: command=0x%x, size=0x%x, seqnum=0x%x, result=0x%x\n", + cmd->command, cmd->size, cmd->seqnum, cmd->result); + + lbs_pr_debug(1, + "SNMP_CMD: action=0x%x, oid=0x%x, oidsize=0x%x, value=0x%x\n", + pSNMPMIB->querytype, pSNMPMIB->oid, pSNMPMIB->bufsize, + *(u16 *) pSNMPMIB->value); + + LEAVE(); + return 0; +} + +static int wlan_cmd_802_11_radio_control(wlan_private * priv, + struct cmd_ds_command *cmd, + int cmd_action) +{ + wlan_adapter *adapter = priv->adapter; + struct cmd_ds_802_11_radio_control *pradiocontrol = + &cmd->params.radio; + + ENTER(); + + cmd->size = + cpu_to_le16((sizeof(struct cmd_ds_802_11_radio_control)) + + S_DS_GEN); + cmd->command = cpu_to_le16(cmd_802_11_radio_control); + + pradiocontrol->action = cpu_to_le16(cmd_action); + + switch (adapter->preamble) { + case cmd_type_short_preamble: + pradiocontrol->control = cpu_to_le16(SET_SHORT_PREAMBLE); + break; + + case cmd_type_long_preamble: + pradiocontrol->control = cpu_to_le16(SET_LONG_PREAMBLE); + break; + + case cmd_type_auto_preamble: + default: + pradiocontrol->control = cpu_to_le16(SET_AUTO_PREAMBLE); + break; + } + + if (adapter->radioon) + pradiocontrol->control |= cpu_to_le16(TURN_ON_RF); + else + pradiocontrol->control &= cpu_to_le16(~TURN_ON_RF); + + LEAVE(); + return 0; +} + +static int wlan_cmd_802_11_rf_tx_power(wlan_private * priv, + struct cmd_ds_command *cmd, + u16 cmd_action, void *pdata_buf) +{ + + struct cmd_ds_802_11_rf_tx_power *prtp = &cmd->params.txp; + + ENTER(); + + cmd->size = + cpu_to_le16((sizeof(struct cmd_ds_802_11_rf_tx_power)) + + S_DS_GEN); + cmd->command = cpu_to_le16(cmd_802_11_rf_tx_power); + prtp->action = cmd_action; + + lbs_pr_debug(1, "RF_TX_POWER_CMD: size:%d cmd:0x%x Act:%d\n", cmd->size, + cmd->command, prtp->action); + + switch (cmd_action) { + case cmd_act_tx_power_opt_get: + prtp->action = cpu_to_le16(cmd_act_get); + prtp->currentlevel = 0; + break; + + case cmd_act_tx_power_opt_set_high: + prtp->action = cpu_to_le16(cmd_act_set); + prtp->currentlevel = + cpu_to_le16(cmd_act_tx_power_index_high); + break; + + case cmd_act_tx_power_opt_set_mid: + prtp->action = cpu_to_le16(cmd_act_set); + prtp->currentlevel = + cpu_to_le16(cmd_act_tx_power_index_mid); + break; + + case cmd_act_tx_power_opt_set_low: + prtp->action = cpu_to_le16(cmd_act_set); + prtp->currentlevel = cpu_to_le16(*((u16 *) pdata_buf)); + break; + } + LEAVE(); + return 0; +} + +static int wlan_cmd_802_11_rf_antenna(wlan_private * priv, + struct cmd_ds_command *cmd, + u16 cmd_action, void *pdata_buf) +{ + struct cmd_ds_802_11_rf_antenna *rant = &cmd->params.rant; + + cmd->command = cpu_to_le16(cmd_802_11_rf_antenna); + cmd->size = + cpu_to_le16(sizeof(struct cmd_ds_802_11_rf_antenna) + + S_DS_GEN); + + rant->action = cpu_to_le16(cmd_action); + if ((cmd_action == cmd_act_set_rx) || + (cmd_action == cmd_act_set_tx)) { + rant->antennamode = + cpu_to_le16((u16) (*(u32 *) pdata_buf)); + } + + return 0; +} + +static int wlan_cmd_802_11_rate_adapt_rateset(wlan_private * priv, + struct cmd_ds_command *cmd, + u16 cmd_action) +{ + struct cmd_ds_802_11_rate_adapt_rateset + *rateadapt = &cmd->params.rateset; + wlan_adapter *adapter = priv->adapter; + + cmd->size = + cpu_to_le16(sizeof(struct cmd_ds_802_11_rate_adapt_rateset) + + S_DS_GEN); + cmd->command = cpu_to_le16(cmd_802_11_rate_adapt_rateset); + + ENTER(); + + rateadapt->action = cmd_action; + rateadapt->enablehwauto = adapter->enablehwauto; + rateadapt->bitmap = adapter->ratebitmap; + + LEAVE(); + return 0; +} + +static int wlan_cmd_802_11_data_rate(wlan_private * priv, + struct cmd_ds_command *cmd, + u16 cmd_action) +{ + struct cmd_ds_802_11_data_rate *pdatarate = &cmd->params.drate; + wlan_adapter *adapter = priv->adapter; + u16 action = cmd_action; + + ENTER(); + + cmd->size = + cpu_to_le16(sizeof(struct cmd_ds_802_11_data_rate) + + S_DS_GEN); + + cmd->command = cpu_to_le16(cmd_802_11_data_rate); + + memset(pdatarate, 0, sizeof(struct cmd_ds_802_11_data_rate)); + + pdatarate->action = cpu_to_le16(cmd_action); + + if (action == cmd_act_set_tx_fix_rate) { + pdatarate->datarate[0] = libertas_data_rate_to_index(adapter->datarate); + lbs_pr_debug(1, "Setting FW for fixed rate 0x%02X\n", + adapter->datarate); + } else if (action == cmd_act_set_tx_auto) { + lbs_pr_debug(1, "Setting FW for AUTO rate\n"); + } + + LEAVE(); + return 0; +} + +static int wlan_cmd_mac_multicast_adr(wlan_private * priv, + struct cmd_ds_command *cmd, + u16 cmd_action) +{ + struct cmd_ds_mac_multicast_adr *pMCastAdr = &cmd->params.madr; + wlan_adapter *adapter = priv->adapter; + + cmd->size = + cpu_to_le16(sizeof(struct cmd_ds_mac_multicast_adr) + + S_DS_GEN); + cmd->command = cpu_to_le16(cmd_mac_multicast_adr); + + pMCastAdr->action = cpu_to_le16(cmd_action); + pMCastAdr->nr_of_adrs = + cpu_to_le16((u16) adapter->nr_of_multicastmacaddr); + memcpy(pMCastAdr->maclist, adapter->multicastlist, + adapter->nr_of_multicastmacaddr * ETH_ALEN); + + return 0; +} + +static int wlan_cmd_802_11_rf_channel(wlan_private * priv, + struct cmd_ds_command *cmd, + int option, void *pdata_buf) +{ + struct cmd_ds_802_11_rf_channel *rfchan = &cmd->params.rfchannel; + + cmd->command = cpu_to_le16(cmd_802_11_rf_channel); + cmd->size = cpu_to_le16(sizeof(struct cmd_ds_802_11_rf_channel) + + S_DS_GEN); + + if (option == cmd_opt_802_11_rf_channel_set) { + rfchan->currentchannel = cpu_to_le16(*((u16 *) pdata_buf)); + } + + rfchan->action = cpu_to_le16(option); + + return 0; +} + +static int wlan_cmd_802_11_rssi(wlan_private * priv, + struct cmd_ds_command *cmd) +{ + wlan_adapter *adapter = priv->adapter; + + cmd->command = cpu_to_le16(cmd_802_11_rssi); + cmd->size = + cpu_to_le16(sizeof(struct cmd_ds_802_11_rssi) + S_DS_GEN); + cmd->params.rssi.N = priv->adapter->bcn_avg_factor; + + /* reset Beacon SNR/NF/RSSI values */ + adapter->SNR[TYPE_BEACON][TYPE_NOAVG] = 0; + adapter->SNR[TYPE_BEACON][TYPE_AVG] = 0; + adapter->NF[TYPE_BEACON][TYPE_NOAVG] = 0; + adapter->NF[TYPE_BEACON][TYPE_AVG] = 0; + adapter->RSSI[TYPE_BEACON][TYPE_NOAVG] = 0; + adapter->RSSI[TYPE_BEACON][TYPE_AVG] = 0; + + return 0; +} + +static int wlan_cmd_reg_access(wlan_private * priv, + struct cmd_ds_command *cmdptr, + u8 cmd_action, void *pdata_buf) +{ + struct wlan_offset_value *offval; + + ENTER(); + + offval = (struct wlan_offset_value *)pdata_buf; + + switch (cmdptr->command) { + case cmd_mac_reg_access: + { + struct cmd_ds_mac_reg_access *macreg; + + cmdptr->size = + cpu_to_le16(sizeof + (struct cmd_ds_mac_reg_access) + + S_DS_GEN); + macreg = + (struct cmd_ds_mac_reg_access *)&cmdptr->params. + macreg; + + macreg->action = cpu_to_le16(cmd_action); + macreg->offset = cpu_to_le16((u16) offval->offset); + macreg->value = cpu_to_le32(offval->value); + + break; + } + + case cmd_bbp_reg_access: + { + struct cmd_ds_bbp_reg_access *bbpreg; + + cmdptr->size = + cpu_to_le16(sizeof + (struct cmd_ds_bbp_reg_access) + + S_DS_GEN); + bbpreg = + (struct cmd_ds_bbp_reg_access *)&cmdptr->params. + bbpreg; + + bbpreg->action = cpu_to_le16(cmd_action); + bbpreg->offset = cpu_to_le16((u16) offval->offset); + bbpreg->value = (u8) offval->value; + + break; + } + + case cmd_rf_reg_access: + { + struct cmd_ds_rf_reg_access *rfreg; + + cmdptr->size = + cpu_to_le16(sizeof + (struct cmd_ds_rf_reg_access) + + S_DS_GEN); + rfreg = + (struct cmd_ds_rf_reg_access *)&cmdptr->params. + rfreg; + + rfreg->action = cpu_to_le16(cmd_action); + rfreg->offset = cpu_to_le16((u16) offval->offset); + rfreg->value = (u8) offval->value; + + break; + } + + default: + break; + } + + LEAVE(); + return 0; +} + +static int wlan_cmd_802_11_mac_address(wlan_private * priv, + struct cmd_ds_command *cmd, + u16 cmd_action) +{ + wlan_adapter *adapter = priv->adapter; + + cmd->command = cpu_to_le16(cmd_802_11_mac_address); + cmd->size = + cpu_to_le16(sizeof(struct cmd_ds_802_11_mac_address) + + S_DS_GEN); + cmd->result = 0; + + cmd->params.macadd.action = cpu_to_le16(cmd_action); + + if (cmd_action == cmd_act_set) { + memcpy(cmd->params.macadd.macadd, + adapter->current_addr, ETH_ALEN); + lbs_dbg_hex("SET_CMD: MAC ADDRESS-", adapter->current_addr, 6); + } + + return 0; +} + +static int wlan_cmd_802_11_eeprom_access(wlan_private * priv, + struct cmd_ds_command *cmd, + int cmd_action, void *pdata_buf) +{ + struct wlan_ioctl_regrdwr *ea = pdata_buf; + + ENTER(); + + cmd->command = cpu_to_le16(cmd_802_11_eeprom_access); + cmd->size = + cpu_to_le16(sizeof(struct cmd_ds_802_11_eeprom_access) + + S_DS_GEN); + cmd->result = 0; + + cmd->params.rdeeprom.action = cpu_to_le16(ea->action); + cmd->params.rdeeprom.offset = cpu_to_le16(ea->offset); + cmd->params.rdeeprom.bytecount = cpu_to_le16(ea->NOB); + cmd->params.rdeeprom.value = 0; + + return 0; +} + +static int wlan_cmd_bt_access(wlan_private * priv, + struct cmd_ds_command *cmd, + u16 cmd_action, void *pdata_buf) +{ + struct cmd_ds_bt_access *bt_access = &cmd->params.bt; + lbs_pr_debug(1, "BT CMD(%d)\n", cmd_action); + + cmd->command = cpu_to_le16(cmd_bt_access); + cmd->size = cpu_to_le16(sizeof(struct cmd_ds_bt_access) + + S_DS_GEN); + cmd->result = 0; + bt_access->action = cpu_to_le16(cmd_action); + + switch (cmd_action) { + case cmd_act_bt_access_add: + memcpy(bt_access->addr1, pdata_buf, 2 * ETH_ALEN); + lbs_dbg_hex("BT_ADD: blinded mac address-", bt_access->addr1, 6); + break; + case cmd_act_bt_access_del: + memcpy(bt_access->addr1, pdata_buf, 1 * ETH_ALEN); + lbs_dbg_hex("BT_DEL: blinded mac address-", bt_access->addr1, 6); + break; + case cmd_act_bt_access_list: + bt_access->id = cpu_to_le32(*(u32 *) pdata_buf); + break; + case cmd_act_bt_access_reset: + break; + default: + break; + } + return 0; +} + +static int wlan_cmd_fwt_access(wlan_private * priv, + struct cmd_ds_command *cmd, + u16 cmd_action, void *pdata_buf) +{ + struct cmd_ds_fwt_access *fwt_access = &cmd->params.fwt; + lbs_pr_debug(1, "FWT CMD(%d)\n", cmd_action); + + cmd->command = cpu_to_le16(cmd_fwt_access); + cmd->size = cpu_to_le16(sizeof(struct cmd_ds_fwt_access) + + S_DS_GEN); + cmd->result = 0; + + if (pdata_buf) + memcpy(fwt_access, pdata_buf, sizeof(*fwt_access)); + else + memset(fwt_access, 0, sizeof(*fwt_access)); + + fwt_access->action = cpu_to_le16(cmd_action); + + return 0; +} + +static int wlan_cmd_mesh_access(wlan_private * priv, + struct cmd_ds_command *cmd, + u16 cmd_action, void *pdata_buf) +{ + struct cmd_ds_mesh_access *mesh_access = &cmd->params.mesh; + lbs_pr_debug(1, "FWT CMD(%d)\n", cmd_action); + + cmd->command = cpu_to_le16(cmd_mesh_access); + cmd->size = cpu_to_le16(sizeof(struct cmd_ds_mesh_access) + + S_DS_GEN); + cmd->result = 0; + + if (pdata_buf) + memcpy(mesh_access, pdata_buf, sizeof(*mesh_access)); + else + memset(mesh_access, 0, sizeof(*mesh_access)); + + mesh_access->action = cpu_to_le16(cmd_action); + + return 0; +} + +void libertas_queue_cmd(wlan_adapter * adapter, struct cmd_ctrl_node *cmdnode, u8 addtail) +{ + unsigned long flags; + struct cmd_ds_command *cmdptr; + + ENTER(); + + if (!cmdnode) { + lbs_pr_debug(1, "QUEUE_CMD: cmdnode is NULL\n"); + goto done; + } + + cmdptr = (struct cmd_ds_command *)cmdnode->bufvirtualaddr; + if (!cmdptr) { + lbs_pr_debug(1, "QUEUE_CMD: cmdptr is NULL\n"); + goto done; + } + + /* Exit_PS command needs to be queued in the header always. */ + if (cmdptr->command == cmd_802_11_ps_mode) { + struct cmd_ds_802_11_ps_mode *psm = &cmdptr->params.psmode; + if (psm->action == cmd_subcmd_exit_ps) { + if (adapter->psstate != PS_STATE_FULL_POWER) + addtail = 0; + } + } + + spin_lock_irqsave(&adapter->driver_lock, flags); + + if (addtail) + list_add_tail((struct list_head *)cmdnode, + &adapter->cmdpendingq); + else + list_add((struct list_head *)cmdnode, &adapter->cmdpendingq); + + spin_unlock_irqrestore(&adapter->driver_lock, flags); + + lbs_pr_debug(1, "QUEUE_CMD: Inserted node=0x%x, cmd=0x%x in cmdpendingq\n", + (u32) cmdnode, + ((struct cmd_ds_gen*)cmdnode->bufvirtualaddr)->command); + +done: + LEAVE(); + return; +} + +/* + * TODO: Fix the issue when DownloadcommandToStation is being called the + * second time when the command timesout. All the cmdptr->xxx are in little + * endian and therefore all the comparissions will fail. + * For now - we are not performing the endian conversion the second time - but + * for PS and DEEP_SLEEP we need to worry + */ +static int DownloadcommandToStation(wlan_private * priv, + struct cmd_ctrl_node *cmdnode) +{ + unsigned long flags; + struct cmd_ds_command *cmdptr; + wlan_adapter *adapter = priv->adapter; + int ret = 0; + u16 cmdsize; + u16 command; + + ENTER(); + + if (!adapter || !cmdnode) { + lbs_pr_debug(1, "DNLD_CMD: adapter = %#x, cmdnode = %#x\n", + (int)adapter, (int)cmdnode); + if (cmdnode) { + spin_lock_irqsave(&adapter->driver_lock, flags); + __libertas_cleanup_and_insert_cmd(priv, cmdnode); + spin_unlock_irqrestore(&adapter->driver_lock, flags); + } + ret = -1; + goto done; + } + + cmdptr = (struct cmd_ds_command *)cmdnode->bufvirtualaddr; + + + spin_lock_irqsave(&adapter->driver_lock, flags); + if (!cmdptr || !cmdptr->size) { + lbs_pr_debug(1, "DNLD_CMD: cmdptr is Null or cmd size is Zero, " + "Not sending\n"); + __libertas_cleanup_and_insert_cmd(priv, cmdnode); + spin_unlock_irqrestore(&adapter->driver_lock, flags); + ret = -1; + goto done; + } + + adapter->cur_cmd = cmdnode; + adapter->cur_cmd_retcode = 0; + spin_unlock_irqrestore(&adapter->driver_lock, flags); + lbs_pr_debug(1, "DNLD_CMD:: Before download, size of cmd = %d\n", + cmdptr->size); + + cmdsize = cmdptr->size; + + command = cpu_to_le16(cmdptr->command); + + cmdnode->cmdwaitqwoken = 0; + cmdsize = cpu_to_le16(cmdsize); + + ret = libertas_sbi_host_to_card(priv, MVMS_CMD, (u8 *) cmdptr, cmdsize); + + if (ret != 0) { + lbs_pr_debug(1, "DNLD_CMD: Host to Card failed\n"); + spin_lock_irqsave(&adapter->driver_lock, flags); + __libertas_cleanup_and_insert_cmd(priv, adapter->cur_cmd); + adapter->cur_cmd = NULL; + spin_unlock_irqrestore(&adapter->driver_lock, flags); + ret = -1; + goto done; + } + + lbs_pr_debug(1, "DNLD_CMD: Sent command 0x%x @ %lu\n", command, jiffies); + lbs_dbg_hex("DNLD_CMD: command", cmdnode->bufvirtualaddr, cmdsize); + + /* Setup the timer after transmit command */ + if (command == cmd_802_11_scan + || command == cmd_802_11_authenticate + || command == cmd_802_11_associate) + mod_timer(&adapter->command_timer, jiffies + (10*HZ)); + else + mod_timer(&adapter->command_timer, jiffies + (5*HZ)); + + ret = 0; + + done: + LEAVE(); + return ret; +} + +static int wlan_cmd_mac_control(wlan_private * priv, + struct cmd_ds_command *cmd) +{ + struct cmd_ds_mac_control *mac = &cmd->params.macctrl; + + ENTER(); + + cmd->command = cpu_to_le16(cmd_mac_control); + cmd->size = + cpu_to_le16(sizeof(struct cmd_ds_mac_control) + S_DS_GEN); + mac->action = cpu_to_le16(priv->adapter->currentpacketfilter); + + lbs_pr_debug(1, "wlan_cmd_mac_control(): action=0x%X size=%d\n", + mac->action, cmd->size); + + LEAVE(); + return 0; +} + +/** + * This function inserts command node to cmdfreeq + * after cleans it. Requires adapter->driver_lock held. + */ +void __libertas_cleanup_and_insert_cmd(wlan_private * priv, struct cmd_ctrl_node *ptempcmd) +{ + wlan_adapter *adapter = priv->adapter; + + if (!ptempcmd) + goto done; + + cleanup_cmdnode(ptempcmd); + list_add_tail((struct list_head *)ptempcmd, &adapter->cmdfreeq); +done: + return; +} + +void libertas_cleanup_and_insert_cmd(wlan_private * priv, struct cmd_ctrl_node *ptempcmd) +{ + unsigned long flags; + + spin_lock_irqsave(&priv->adapter->driver_lock, flags); + __libertas_cleanup_and_insert_cmd(priv, ptempcmd); + spin_unlock_irqrestore(&priv->adapter->driver_lock, flags); +} + +int libertas_set_radio_control(wlan_private * priv) +{ + int ret = 0; + + ENTER(); + + ret = libertas_prepare_and_send_command(priv, + cmd_802_11_radio_control, + cmd_act_set, + cmd_option_waitforrsp, 0, NULL); + + lbs_pr_debug(1, "RADIO_SET: on or off: 0x%X, preamble = 0x%X\n", + priv->adapter->radioon, priv->adapter->preamble); + + LEAVE(); + return ret; +} + +int libertas_set_mac_packet_filter(wlan_private * priv) +{ + int ret = 0; + + ENTER(); + + lbs_pr_debug(1, "libertas_set_mac_packet_filter value = %x\n", + priv->adapter->currentpacketfilter); + + /* Send MAC control command to station */ + ret = libertas_prepare_and_send_command(priv, + cmd_mac_control, 0, 0, 0, NULL); + + LEAVE(); + return ret; +} + +/** + * @brief This function prepare the command before send to firmware. + * + * @param priv A pointer to wlan_private structure + * @param cmd_no command number + * @param cmd_action command action: GET or SET + * @param wait_option wait option: wait response or not + * @param cmd_oid cmd oid: treated as sub command + * @param pdata_buf A pointer to informaion buffer + * @return 0 or -1 + */ +int libertas_prepare_and_send_command(wlan_private * priv, + u16 cmd_no, + u16 cmd_action, + u16 wait_option, u32 cmd_oid, void *pdata_buf) +{ + int ret = 0; + wlan_adapter *adapter = priv->adapter; + struct cmd_ctrl_node *cmdnode; + struct cmd_ds_command *cmdptr; + unsigned long flags; + + ENTER(); + + if (!adapter) { + lbs_pr_debug(1, "PREP_CMD: adapter is Null\n"); + ret = -1; + goto done; + } + + if (adapter->surpriseremoved) { + lbs_pr_debug(1, "PREP_CMD: Card is Removed\n"); + ret = -1; + goto done; + } + + cmdnode = libertas_get_free_cmd_ctrl_node(priv); + + if (cmdnode == NULL) { + lbs_pr_debug(1, "PREP_CMD: No free cmdnode\n"); + + /* Wake up main thread to execute next command */ + wake_up_interruptible(&priv->mainthread.waitq); + ret = -1; + goto done; + } + + libertas_set_cmd_ctrl_node(priv, cmdnode, cmd_oid, wait_option, pdata_buf); + + cmdptr = (struct cmd_ds_command *)cmdnode->bufvirtualaddr; + + lbs_pr_debug(1, "PREP_CMD: Val of cmd ptr =0x%x, command=0x%X\n", + (u32) cmdptr, cmd_no); + + if (!cmdptr) { + lbs_pr_debug(1, "PREP_CMD: bufvirtualaddr of cmdnode is NULL\n"); + libertas_cleanup_and_insert_cmd(priv, cmdnode); + ret = -1; + goto done; + } + + /* Set sequence number, command and INT option */ + adapter->seqnum++; + cmdptr->seqnum = cpu_to_le16(adapter->seqnum); + + cmdptr->command = cmd_no; + cmdptr->result = 0; + + switch (cmd_no) { + case cmd_get_hw_spec: + ret = wlan_cmd_hw_spec(priv, cmdptr); + break; + case cmd_802_11_ps_mode: + ret = wlan_cmd_802_11_ps_mode(priv, cmdptr, cmd_action); + break; + + case cmd_802_11_scan: + ret = libertas_cmd_80211_scan(priv, cmdptr, pdata_buf); + break; + + case cmd_mac_control: + ret = wlan_cmd_mac_control(priv, cmdptr); + break; + + case cmd_802_11_associate: + case cmd_802_11_reassociate: + ret = libertas_cmd_80211_associate(priv, cmdptr, pdata_buf); + break; + + case cmd_802_11_deauthenticate: + ret = libertas_cmd_80211_deauthenticate(priv, cmdptr); + break; + + case cmd_802_11_set_wep: + ret = wlan_cmd_802_11_set_wep(priv, cmdptr, cmd_action, pdata_buf); + break; + + case cmd_802_11_ad_hoc_start: + ret = libertas_cmd_80211_ad_hoc_start(priv, cmdptr, pdata_buf); + break; + case cmd_code_dnld: + break; + + case cmd_802_11_reset: + ret = wlan_cmd_802_11_reset(priv, cmdptr, cmd_action); + break; + + case cmd_802_11_get_log: + ret = wlan_cmd_802_11_get_log(priv, cmdptr); + break; + + case cmd_802_11_authenticate: + ret = libertas_cmd_80211_authenticate(priv, cmdptr, pdata_buf); + break; + + case cmd_802_11_get_stat: + ret = wlan_cmd_802_11_get_stat(priv, cmdptr); + break; + + case cmd_802_11_snmp_mib: + ret = wlan_cmd_802_11_snmp_mib(priv, cmdptr, + cmd_action, cmd_oid, pdata_buf); + break; + + case cmd_mac_reg_access: + case cmd_bbp_reg_access: + case cmd_rf_reg_access: + ret = wlan_cmd_reg_access(priv, cmdptr, cmd_action, pdata_buf); + break; + + case cmd_802_11_rf_channel: + ret = wlan_cmd_802_11_rf_channel(priv, cmdptr, + cmd_action, pdata_buf); + break; + + case cmd_802_11_rf_tx_power: + ret = wlan_cmd_802_11_rf_tx_power(priv, cmdptr, + cmd_action, pdata_buf); + break; + + case cmd_802_11_radio_control: + ret = wlan_cmd_802_11_radio_control(priv, cmdptr, cmd_action); + break; + + case cmd_802_11_rf_antenna: + ret = wlan_cmd_802_11_rf_antenna(priv, cmdptr, + cmd_action, pdata_buf); + break; + + case cmd_802_11_data_rate: + ret = wlan_cmd_802_11_data_rate(priv, cmdptr, cmd_action); + break; + case cmd_802_11_rate_adapt_rateset: + ret = wlan_cmd_802_11_rate_adapt_rateset(priv, + cmdptr, cmd_action); + break; + + case cmd_mac_multicast_adr: + ret = wlan_cmd_mac_multicast_adr(priv, cmdptr, cmd_action); + break; + + case cmd_802_11_ad_hoc_join: + ret = libertas_cmd_80211_ad_hoc_join(priv, cmdptr, pdata_buf); + break; + + case cmd_802_11_rssi: + ret = wlan_cmd_802_11_rssi(priv, cmdptr); + break; + + case cmd_802_11_ad_hoc_stop: + ret = libertas_cmd_80211_ad_hoc_stop(priv, cmdptr); + break; + + case cmd_802_11_enable_rsn: + ret = wlan_cmd_802_11_enable_rsn(priv, cmdptr, cmd_action); + break; + + case cmd_802_11_key_material: + ret = wlan_cmd_802_11_key_material(priv, cmdptr, + cmd_action, cmd_oid, + pdata_buf); + break; + + case cmd_802_11_pairwise_tsc: + break; + case cmd_802_11_group_tsc: + break; + + case cmd_802_11_mac_address: + ret = wlan_cmd_802_11_mac_address(priv, cmdptr, cmd_action); + break; + + case cmd_802_11_eeprom_access: + ret = wlan_cmd_802_11_eeprom_access(priv, cmdptr, + cmd_action, pdata_buf); + break; + + case cmd_802_11_set_afc: + case cmd_802_11_get_afc: + + cmdptr->command = cpu_to_le16(cmd_no); + cmdptr->size = + cpu_to_le16(sizeof(struct cmd_ds_802_11_afc) + + S_DS_GEN); + + memmove(&cmdptr->params.afc, + pdata_buf, sizeof(struct cmd_ds_802_11_afc)); + + ret = 0; + goto done; + + case cmd_802_11d_domain_info: + ret = libertas_cmd_802_11d_domain_info(priv, cmdptr, + cmd_no, cmd_action); + break; + + case cmd_802_11_sleep_params: + ret = wlan_cmd_802_11_sleep_params(priv, cmdptr, cmd_action); + break; + case cmd_802_11_inactivity_timeout: + ret = wlan_cmd_802_11_inactivity_timeout(priv, cmdptr, + cmd_action, pdata_buf); + libertas_set_cmd_ctrl_node(priv, cmdnode, 0, 0, pdata_buf); + break; + + case cmd_802_11_tpc_cfg: + cmdptr->command = cpu_to_le16(cmd_802_11_tpc_cfg); + cmdptr->size = + cpu_to_le16(sizeof(struct cmd_ds_802_11_tpc_cfg) + + S_DS_GEN); + + memmove(&cmdptr->params.tpccfg, + pdata_buf, sizeof(struct cmd_ds_802_11_tpc_cfg)); + + ret = 0; + break; + case cmd_802_11_led_gpio_ctrl: + { + struct mrvlietypes_ledgpio *gpio = + (struct mrvlietypes_ledgpio*) + cmdptr->params.ledgpio.data; + + memmove(&cmdptr->params.ledgpio, + pdata_buf, + sizeof(struct cmd_ds_802_11_led_ctrl)); + + cmdptr->command = + cpu_to_le16(cmd_802_11_led_gpio_ctrl); + +#define ACTION_NUMLED_TLVTYPE_LEN_FIELDS_LEN 8 + cmdptr->size = + cpu_to_le16(gpio->header.len + S_DS_GEN + + ACTION_NUMLED_TLVTYPE_LEN_FIELDS_LEN); + gpio->header.len = cpu_to_le16(gpio->header.len); + + ret = 0; + break; + } + case cmd_802_11_pwr_cfg: + cmdptr->command = cpu_to_le16(cmd_802_11_pwr_cfg); + cmdptr->size = + cpu_to_le16(sizeof(struct cmd_ds_802_11_pwr_cfg) + + S_DS_GEN); + memmove(&cmdptr->params.pwrcfg, pdata_buf, + sizeof(struct cmd_ds_802_11_pwr_cfg)); + + ret = 0; + break; + case cmd_bt_access: + ret = wlan_cmd_bt_access(priv, cmdptr, cmd_action, pdata_buf); + break; + + case cmd_fwt_access: + ret = wlan_cmd_fwt_access(priv, cmdptr, cmd_action, pdata_buf); + break; + + case cmd_mesh_access: + ret = wlan_cmd_mesh_access(priv, cmdptr, cmd_action, pdata_buf); + break; + + case cmd_get_tsf: + cmdptr->command = cpu_to_le16(cmd_get_tsf); + cmdptr->size = + cpu_to_le16(sizeof(struct cmd_ds_get_tsf) + + S_DS_GEN); + ret = 0; + break; + case cmd_802_11_tx_rate_query: + cmdptr->command = + cpu_to_le16(cmd_802_11_tx_rate_query); + cmdptr->size = + cpu_to_le16(sizeof(struct cmd_tx_rate_query) + + S_DS_GEN); + adapter->txrate = 0; + ret = 0; + break; + default: + lbs_pr_debug(1, "PREP_CMD: unknown command- %#x\n", cmd_no); + ret = -1; + break; + } + + /* return error, since the command preparation failed */ + if (ret != 0) { + lbs_pr_debug(1, "PREP_CMD: command preparation failed\n"); + libertas_cleanup_and_insert_cmd(priv, cmdnode); + ret = -1; + goto done; + } + + cmdnode->cmdwaitqwoken = 0; + + libertas_queue_cmd(adapter, cmdnode, 1); + adapter->nr_cmd_pending++; + wake_up_interruptible(&priv->mainthread.waitq); + + if (wait_option & cmd_option_waitforrsp) { + lbs_pr_debug(1, "PREP_CMD: Wait for CMD response\n"); + might_sleep(); + wait_event_interruptible(cmdnode->cmdwait_q, + cmdnode->cmdwaitqwoken); + } + + spin_lock_irqsave(&adapter->driver_lock, flags); + if (adapter->cur_cmd_retcode) { + lbs_pr_debug(1, "PREP_CMD: command failed with return code=%d\n", + adapter->cur_cmd_retcode); + adapter->cur_cmd_retcode = 0; + ret = -1; + } + spin_unlock_irqrestore(&adapter->driver_lock, flags); + +done: + LEAVE(); + return ret; +} + +/** + * @brief This function allocates the command buffer and link + * it to command free queue. + * + * @param priv A pointer to wlan_private structure + * @return 0 or -1 + */ +int libertas_allocate_cmd_buffer(wlan_private * priv) +{ + int ret = 0; + u32 ulbufsize; + u32 i; + struct cmd_ctrl_node *tempcmd_array; + u8 *ptempvirtualaddr; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + /* Allocate and initialize cmdCtrlNode */ + ulbufsize = sizeof(struct cmd_ctrl_node) * MRVDRV_NUM_OF_CMD_BUFFER; + + if (!(tempcmd_array = kmalloc(ulbufsize, GFP_KERNEL))) { + lbs_pr_debug(1, + "ALLOC_CMD_BUF: failed to allocate tempcmd_array\n"); + ret = -1; + goto done; + } + + adapter->cmd_array = tempcmd_array; + memset(adapter->cmd_array, 0, ulbufsize); + + /* Allocate and initialize command buffers */ + ulbufsize = MRVDRV_SIZE_OF_CMD_BUFFER; + for (i = 0; i < MRVDRV_NUM_OF_CMD_BUFFER; i++) { + if (!(ptempvirtualaddr = kmalloc(ulbufsize, GFP_KERNEL))) { + lbs_pr_debug(1, + "ALLOC_CMD_BUF: ptempvirtualaddr: out of memory\n"); + ret = -1; + goto done; + } + + memset(ptempvirtualaddr, 0, ulbufsize); + + /* Update command buffer virtual */ + tempcmd_array[i].bufvirtualaddr = ptempvirtualaddr; + } + + for (i = 0; i < MRVDRV_NUM_OF_CMD_BUFFER; i++) { + init_waitqueue_head(&tempcmd_array[i].cmdwait_q); + libertas_cleanup_and_insert_cmd(priv, &tempcmd_array[i]); + } + + ret = 0; + done: + LEAVE(); + return ret; +} + +/** + * @brief This function frees the command buffer. + * + * @param priv A pointer to wlan_private structure + * @return 0 or -1 + */ +int libertas_free_cmd_buffer(wlan_private * priv) +{ + u32 ulbufsize; + unsigned int i; + struct cmd_ctrl_node *tempcmd_array; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + /* need to check if cmd array is allocated or not */ + if (adapter->cmd_array == NULL) { + lbs_pr_debug(1, "FREE_CMD_BUF: cmd_array is Null\n"); + goto done; + } + + tempcmd_array = adapter->cmd_array; + + /* Release shared memory buffers */ + ulbufsize = MRVDRV_SIZE_OF_CMD_BUFFER; + for (i = 0; i < MRVDRV_NUM_OF_CMD_BUFFER; i++) { + if (tempcmd_array[i].bufvirtualaddr) { + lbs_pr_debug(1, "Free all the array\n"); + kfree(tempcmd_array[i].bufvirtualaddr); + tempcmd_array[i].bufvirtualaddr = NULL; + } + } + + /* Release cmd_ctrl_node */ + if (adapter->cmd_array) { + lbs_pr_debug(1, "Free cmd_array\n"); + kfree(adapter->cmd_array); + adapter->cmd_array = NULL; + } + +done: + LEAVE(); + return 0; +} + +/** + * @brief This function gets a free command node if available in + * command free queue. + * + * @param priv A pointer to wlan_private structure + * @return cmd_ctrl_node A pointer to cmd_ctrl_node structure or NULL + */ +struct cmd_ctrl_node *libertas_get_free_cmd_ctrl_node(wlan_private * priv) +{ + struct cmd_ctrl_node *tempnode; + wlan_adapter *adapter = priv->adapter; + unsigned long flags; + + if (!adapter) + return NULL; + + spin_lock_irqsave(&adapter->driver_lock, flags); + + if (!list_empty(&adapter->cmdfreeq)) { + tempnode = (struct cmd_ctrl_node *)adapter->cmdfreeq.next; + list_del((struct list_head *)tempnode); + } else { + lbs_pr_debug(1, "GET_CMD_NODE: cmd_ctrl_node is not available\n"); + tempnode = NULL; + } + + spin_unlock_irqrestore(&adapter->driver_lock, flags); + + if (tempnode) { + lbs_pr_debug(3, "GET_CMD_NODE: cmdCtrlNode available\n"); + lbs_pr_debug(3, "GET_CMD_NODE: cmdCtrlNode Address = %p\n", + tempnode); + cleanup_cmdnode(tempnode); + } + + return tempnode; +} + +/** + * @brief This function cleans command node. + * + * @param ptempnode A pointer to cmdCtrlNode structure + * @return n/a + */ +static void cleanup_cmdnode(struct cmd_ctrl_node *ptempnode) +{ + if (!ptempnode) + return; + ptempnode->cmdwaitqwoken = 1; + wake_up_interruptible(&ptempnode->cmdwait_q); + ptempnode->status = 0; + ptempnode->cmd_oid = (u32) 0; + ptempnode->wait_option = 0; + ptempnode->pdata_buf = NULL; + + if (ptempnode->bufvirtualaddr != NULL) + memset(ptempnode->bufvirtualaddr, 0, MRVDRV_SIZE_OF_CMD_BUFFER); + return; +} + +/** + * @brief This function initializes the command node. + * + * @param priv A pointer to wlan_private structure + * @param ptempnode A pointer to cmd_ctrl_node structure + * @param cmd_oid cmd oid: treated as sub command + * @param wait_option wait option: wait response or not + * @param pdata_buf A pointer to informaion buffer + * @return 0 or -1 + */ +void libertas_set_cmd_ctrl_node(wlan_private * priv, + struct cmd_ctrl_node *ptempnode, + u32 cmd_oid, u16 wait_option, void *pdata_buf) +{ + ENTER(); + + if (!ptempnode) + return; + + ptempnode->cmd_oid = cmd_oid; + ptempnode->wait_option = wait_option; + ptempnode->pdata_buf = pdata_buf; + + LEAVE(); +} + +/** + * @brief This function executes next command in command + * pending queue. It will put fimware back to PS mode + * if applicable. + * + * @param priv A pointer to wlan_private structure + * @return 0 or -1 + */ +int libertas_execute_next_command(wlan_private * priv) +{ + wlan_adapter *adapter = priv->adapter; + struct cmd_ctrl_node *cmdnode = NULL; + struct cmd_ds_command *cmdptr; + unsigned long flags; + int ret = 0; + + lbs_pr_debug(1, "libertas_execute_next_command\n"); + + spin_lock_irqsave(&adapter->driver_lock, flags); + + if (adapter->cur_cmd) { + lbs_pr_alert( "EXEC_NEXT_CMD: there is command in processing!\n"); + spin_unlock_irqrestore(&adapter->driver_lock, flags); + ret = -1; + goto done; + } + + if (!list_empty(&adapter->cmdpendingq)) { + cmdnode = (struct cmd_ctrl_node *) + adapter->cmdpendingq.next; + } + + spin_unlock_irqrestore(&adapter->driver_lock, flags); + + if (cmdnode) { + lbs_pr_debug(1, + "EXEC_NEXT_CMD: Got next command from cmdpendingq\n"); + cmdptr = (struct cmd_ds_command *)cmdnode->bufvirtualaddr; + + if (is_command_allowed_in_ps(cmdptr->command)) { + if ((adapter->psstate == PS_STATE_SLEEP) + || (adapter->psstate == PS_STATE_PRE_SLEEP) + ) { + lbs_pr_debug(1, + "EXEC_NEXT_CMD: Cannot send cmd 0x%x in psstate %d\n", + cmdptr->command, adapter->psstate); + ret = -1; + goto done; + } + lbs_pr_debug(1, "EXEC_NEXT_CMD: OK to send command " + "0x%x in psstate %d\n", + cmdptr->command, adapter->psstate); + } else if (adapter->psstate != PS_STATE_FULL_POWER) { + /* + * 1. Non-PS command: + * Queue it. set needtowakeup to TRUE if current state + * is SLEEP, otherwise call libertas_ps_wakeup to send Exit_PS. + * 2. PS command but not Exit_PS: + * Ignore it. + * 3. PS command Exit_PS: + * Set needtowakeup to TRUE if current state is SLEEP, + * otherwise send this command down to firmware + * immediately. + */ + if (cmdptr->command != + cpu_to_le16(cmd_802_11_ps_mode)) { + /* Prepare to send Exit PS, + * this non PS command will be sent later */ + if ((adapter->psstate == PS_STATE_SLEEP) + || (adapter->psstate == PS_STATE_PRE_SLEEP) + ) { + /* w/ new scheme, it will not reach here. + since it is blocked in main_thread. */ + adapter->needtowakeup = 1; + } else + libertas_ps_wakeup(priv, 0); + + ret = 0; + goto done; + } else { + /* + * PS command. Ignore it if it is not Exit_PS. + * otherwise send it down immediately. + */ + struct cmd_ds_802_11_ps_mode *psm = + &cmdptr->params.psmode; + + lbs_pr_debug(1, + "EXEC_NEXT_CMD: PS cmd- action=0x%x\n", + psm->action); + if (psm->action != + cpu_to_le16(cmd_subcmd_exit_ps)) { + lbs_pr_debug(1, + "EXEC_NEXT_CMD: Ignore Enter PS cmd\n"); + list_del((struct list_head *)cmdnode); + libertas_cleanup_and_insert_cmd(priv, cmdnode); + + ret = 0; + goto done; + } + + if ((adapter->psstate == PS_STATE_SLEEP) + || (adapter->psstate == PS_STATE_PRE_SLEEP) + ) { + lbs_pr_debug(1, + "EXEC_NEXT_CMD: Ignore ExitPS cmd in sleep\n"); + list_del((struct list_head *)cmdnode); + libertas_cleanup_and_insert_cmd(priv, cmdnode); + adapter->needtowakeup = 1; + + ret = 0; + goto done; + } + + lbs_pr_debug(1, + "EXEC_NEXT_CMD: Sending Exit_PS down...\n"); + } + } + list_del((struct list_head *)cmdnode); + lbs_pr_debug(1, "EXEC_NEXT_CMD: Sending 0x%04X command\n", + cmdptr->command); + DownloadcommandToStation(priv, cmdnode); + } else { + /* + * check if in power save mode, if yes, put the device back + * to PS mode + */ + if ((adapter->psmode != wlan802_11powermodecam) && + (adapter->psstate == PS_STATE_FULL_POWER) && + (adapter->connect_status == libertas_connected)) { + if (adapter->secinfo.WPAenabled + || adapter->secinfo.WPA2enabled) { + /* check for valid WPA group keys */ + if (adapter->wpa_mcast_key.len + || adapter->wpa_unicast_key.len) { + lbs_pr_debug(1, + "EXEC_NEXT_CMD: WPA enabled and GTK_SET" + " go back to PS_SLEEP"); + libertas_ps_sleep(priv, 0); + } + } else { + lbs_pr_debug(1, + "EXEC_NEXT_CMD: command PendQ is empty," + " go back to PS_SLEEP"); + libertas_ps_sleep(priv, 0); + } + } + } + + ret = 0; +done: + return ret; +} + +void libertas_send_iwevcustom_event(wlan_private * priv, s8 * str) +{ + union iwreq_data iwrq; + u8 buf[50]; + + ENTER(); + + memset(&iwrq, 0, sizeof(union iwreq_data)); + memset(buf, 0, sizeof(buf)); + + snprintf(buf, sizeof(buf) - 1, "%s", str); + + iwrq.data.length = strlen(buf) + 1 + IW_EV_LCP_LEN; + + /* Send Event to upper layer */ + lbs_pr_debug(1, "Event Indication string = %s\n", + (char *)buf); + lbs_pr_debug(1, "Event Indication String length = %d\n", iwrq.data.length); + + lbs_pr_debug(1, "Sending wireless event IWEVCUSTOM for %s\n", str); + wireless_send_event(priv->wlan_dev.netdev, IWEVCUSTOM, &iwrq, buf); + + LEAVE(); + return; +} + +static int sendconfirmsleep(wlan_private * priv, u8 * cmdptr, u16 size) +{ + unsigned long flags; + wlan_adapter *adapter = priv->adapter; + int ret = 0; + + ENTER(); + + lbs_pr_debug(1, "SEND_SLEEPC_CMD: Before download, size of cmd = %d\n", + size); + + lbs_dbg_hex("SEND_SLEEPC_CMD: Sleep confirm command", cmdptr, size); + + ret = libertas_sbi_host_to_card(priv, MVMS_CMD, cmdptr, size); + priv->wlan_dev.dnld_sent = DNLD_RES_RECEIVED; + + spin_lock_irqsave(&adapter->driver_lock, flags); + if (adapter->intcounter || adapter->currenttxskb) + lbs_pr_debug(1, "SEND_SLEEPC_CMD: intcounter=%d currenttxskb=%p\n", + adapter->intcounter, adapter->currenttxskb); + spin_unlock_irqrestore(&adapter->driver_lock, flags); + + if (ret) { + lbs_pr_alert( + "SEND_SLEEPC_CMD: Host to Card failed for Confirm Sleep\n"); + } else { + spin_lock_irqsave(&adapter->driver_lock, flags); + if (!adapter->intcounter) { + adapter->psstate = PS_STATE_SLEEP; + } else { + lbs_pr_debug(1, "SEND_SLEEPC_CMD: After sent,IntC=%d\n", + adapter->intcounter); + } + spin_unlock_irqrestore(&adapter->driver_lock, flags); + + lbs_pr_debug(1, "SEND_SLEEPC_CMD: Sent Confirm Sleep command\n"); + lbs_pr_debug(1, "+"); + } + + LEAVE(); + return ret; +} + +void libertas_ps_sleep(wlan_private * priv, int wait_option) +{ + + ENTER(); + + /* + * PS is currently supported only in Infrastructure mode + * Remove this check if it is to be supported in IBSS mode also + */ + + libertas_prepare_and_send_command(priv, cmd_802_11_ps_mode, + cmd_subcmd_enter_ps, wait_option, 0, NULL); + + LEAVE(); + return; +} + +/** + * @brief This function sends Eixt_PS command to firmware. + * + * @param priv A pointer to wlan_private structure + * @param wait_option wait response or not + * @return n/a + */ +void libertas_ps_wakeup(wlan_private * priv, int wait_option) +{ + enum WLAN_802_11_POWER_MODE Localpsmode; + + ENTER(); + + Localpsmode = wlan802_11powermodecam; + + lbs_pr_debug(1, "Exit_PS: Localpsmode = %d\n", Localpsmode); + + libertas_prepare_and_send_command(priv, cmd_802_11_ps_mode, + cmd_subcmd_exit_ps, + wait_option, 0, &Localpsmode); + + LEAVE(); + return; +} + +/** + * @brief This function checks condition and prepares to + * send sleep confirm command to firmware if ok. + * + * @param priv A pointer to wlan_private structure + * @param psmode Power Saving mode + * @return n/a + */ +void libertas_ps_confirm_sleep(wlan_private * priv, u16 psmode) +{ + unsigned long flags =0; + wlan_adapter *adapter = priv->adapter; + u8 allowed = 1; + + ENTER(); + + if (priv->wlan_dev.dnld_sent) { + allowed = 0; + lbs_pr_debug(1, "D"); + } + + spin_lock_irqsave(&adapter->driver_lock, flags); + if (adapter->cur_cmd) { + allowed = 0; + lbs_pr_debug(1, "C"); + } + if (adapter->intcounter > 0) { + allowed = 0; + lbs_pr_debug(1, "I%d", adapter->intcounter); + } + spin_unlock_irqrestore(&adapter->driver_lock, flags); + + if (allowed) { + lbs_pr_debug(1, "Sending libertas_ps_confirm_sleep\n"); + sendconfirmsleep(priv, (u8 *) & adapter->libertas_ps_confirm_sleep, + sizeof(struct PS_CMD_ConfirmSleep)); + } else { + lbs_pr_debug(1, "Sleep Confirm has been delayed\n"); + } + + LEAVE(); +} diff --git a/drivers/net/wireless/libertas/cmdresp.c b/drivers/net/wireless/libertas/cmdresp.c new file mode 100644 index 00000000000..cdb012c7e9c --- /dev/null +++ b/drivers/net/wireless/libertas/cmdresp.c @@ -0,0 +1,1031 @@ +/** + * This file contains the handling of command + * responses as well as events generated by firmware. + */ +#include <linux/delay.h> +#include <linux/if_arp.h> +#include <linux/netdevice.h> + +#include <net/iw_handler.h> + +#include "host.h" +#include "sbi.h" +#include "decl.h" +#include "defs.h" +#include "dev.h" +#include "join.h" +#include "wext.h" + +/** + * @brief This function handles disconnect event. it + * reports disconnect to upper layer, clean tx/rx packets, + * reset link state etc. + * + * @param priv A pointer to wlan_private structure + * @return n/a + */ +void libertas_mac_event_disconnected(wlan_private * priv) +{ + wlan_adapter *adapter = priv->adapter; + union iwreq_data wrqu; + + if (adapter->connect_status != libertas_connected) + return; + + lbs_pr_debug(1, "Handles disconnect event.\n"); + + memset(wrqu.ap_addr.sa_data, 0x00, ETH_ALEN); + wrqu.ap_addr.sa_family = ARPHRD_ETHER; + + /* + * Cisco AP sends EAP failure and de-auth in less than 0.5 ms. + * It causes problem in the Supplicant + */ + + msleep_interruptible(1000); + wireless_send_event(priv->wlan_dev.netdev, SIOCGIWAP, &wrqu, NULL); + + /* Free Tx and Rx packets */ + kfree_skb(priv->adapter->currenttxskb); + priv->adapter->currenttxskb = NULL; + + /* report disconnect to upper layer */ + netif_stop_queue(priv->wlan_dev.netdev); + netif_carrier_off(priv->wlan_dev.netdev); + + /* reset SNR/NF/RSSI values */ + memset(adapter->SNR, 0x00, sizeof(adapter->SNR)); + memset(adapter->NF, 0x00, sizeof(adapter->NF)); + memset(adapter->RSSI, 0x00, sizeof(adapter->RSSI)); + memset(adapter->rawSNR, 0x00, sizeof(adapter->rawSNR)); + memset(adapter->rawNF, 0x00, sizeof(adapter->rawNF)); + adapter->nextSNRNF = 0; + adapter->numSNRNF = 0; + adapter->rxpd_rate = 0; + lbs_pr_debug(1, "Current SSID=%s, ssid length=%u\n", + adapter->curbssparams.ssid.ssid, + adapter->curbssparams.ssid.ssidlength); + lbs_pr_debug(1, "Previous SSID=%s, ssid length=%u\n", + adapter->previousssid.ssid, adapter->previousssid.ssidlength); + + /* reset internal flags */ + adapter->secinfo.WPAenabled = 0; + adapter->secinfo.WPA2enabled = 0; + adapter->wpa_ie_len = 0; + adapter->secinfo.auth1xalg = WLAN_1X_AUTH_ALG_NONE; + adapter->secinfo.Encryptionmode = CIPHER_NONE; + + adapter->connect_status = libertas_disconnected; + + /* + * memorize the previous SSID and BSSID + * it could be used for re-assoc + */ + memcpy(&adapter->previousssid, + &adapter->curbssparams.ssid, sizeof(struct WLAN_802_11_SSID)); + memcpy(adapter->previousbssid, + adapter->curbssparams.bssid, ETH_ALEN); + + /* need to erase the current SSID and BSSID info */ + adapter->pattemptedbssdesc = NULL; + memset(&adapter->curbssparams, 0, sizeof(adapter->curbssparams)); + + if (adapter->psstate != PS_STATE_FULL_POWER) { + /* make firmware to exit PS mode */ + lbs_pr_debug(1, "Disconnected, so exit PS mode.\n"); + libertas_ps_wakeup(priv, 0); + } +} + +/** + * @brief This function handles MIC failure event. + * + * @param priv A pointer to wlan_private structure + * @para event the event id + * @return n/a + */ +static void handle_mic_failureevent(wlan_private * priv, u32 event) +{ + char buf[50]; + + memset(buf, 0, sizeof(buf)); + + sprintf(buf, "%s", "MLME-MICHAELMICFAILURE.indication "); + + if (event == MACREG_INT_CODE_MIC_ERR_UNICAST) { + strcat(buf, "unicast "); + } else { + strcat(buf, "multicast "); + } + + libertas_send_iwevcustom_event(priv, buf); +} + +static int wlan_ret_reg_access(wlan_private * priv, + u16 type, struct cmd_ds_command *resp) +{ + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + switch (type) { + case cmd_ret_mac_reg_access: + { + struct cmd_ds_mac_reg_access *reg; + + reg = + (struct cmd_ds_mac_reg_access *)&resp->params. + macreg; + + adapter->offsetvalue.offset = reg->offset; + adapter->offsetvalue.value = reg->value; + break; + } + + case cmd_ret_bbp_reg_access: + { + struct cmd_ds_bbp_reg_access *reg; + reg = + (struct cmd_ds_bbp_reg_access *)&resp->params. + bbpreg; + + adapter->offsetvalue.offset = reg->offset; + adapter->offsetvalue.value = reg->value; + break; + } + + case cmd_ret_rf_reg_access: + { + struct cmd_ds_rf_reg_access *reg; + reg = + (struct cmd_ds_rf_reg_access *)&resp->params. + rfreg; + + adapter->offsetvalue.offset = reg->offset; + adapter->offsetvalue.value = reg->value; + break; + } + + default: + LEAVE(); + return -1; + } + + LEAVE(); + return 0; +} + +static int wlan_ret_get_hw_spec(wlan_private * priv, + struct cmd_ds_command *resp) +{ + u32 i; + struct cmd_ds_get_hw_spec *hwspec = &resp->params.hwspec; + wlan_adapter *adapter = priv->adapter; + int ret = 0; + + ENTER(); + + adapter->fwcapinfo = le32_to_cpu(hwspec->fwcapinfo); + + adapter->fwreleasenumber = hwspec->fwreleasenumber; + + lbs_pr_debug(1, "GET_HW_SPEC: FWReleaseVersion- 0x%X\n", + adapter->fwreleasenumber); + lbs_pr_debug(1, "GET_HW_SPEC: Permanent addr- %2x:%2x:%2x:%2x:%2x:%2x\n", + hwspec->permanentaddr[0], hwspec->permanentaddr[1], + hwspec->permanentaddr[2], hwspec->permanentaddr[3], + hwspec->permanentaddr[4], hwspec->permanentaddr[5]); + lbs_pr_debug(1, "GET_HW_SPEC: hwifversion=0x%X version=0x%X\n", + hwspec->hwifversion, hwspec->version); + + adapter->regioncode = le16_to_cpu(hwspec->regioncode); + + for (i = 0; i < MRVDRV_MAX_REGION_CODE; i++) { + /* use the region code to search for the index */ + if (adapter->regioncode == libertas_region_code_to_index[i]) { + adapter->regiontableindex = (u16) i; + break; + } + } + + /* if it's unidentified region code, use the default (USA) */ + if (i >= MRVDRV_MAX_REGION_CODE) { + adapter->regioncode = 0x10; + adapter->regiontableindex = 0; + lbs_pr_info( + "unidentified region code, use the default (USA)\n"); + } + + if (adapter->current_addr[0] == 0xff) { + memmove(adapter->current_addr, hwspec->permanentaddr, + ETH_ALEN); + } + + memcpy(priv->wlan_dev.netdev->dev_addr, adapter->current_addr, ETH_ALEN); + memcpy(priv->mesh_dev->dev_addr, adapter->current_addr, ETH_ALEN); + + if (libertas_set_regiontable(priv, adapter->regioncode, 0)) { + ret = -1; + goto done; + } + + if (libertas_set_universaltable(priv, 0)) { + ret = -1; + goto done; + } + + done: + LEAVE(); + return ret; +} + +static int wlan_ret_802_11_sleep_params(wlan_private * priv, + struct cmd_ds_command *resp) +{ + struct cmd_ds_802_11_sleep_params *sp = &resp->params.sleep_params; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + lbs_pr_debug(1, "error=%x offset=%x stabletime=%x calcontrol=%x\n" + " extsleepclk=%x\n", sp->error, sp->offset, + sp->stabletime, sp->calcontrol, sp->externalsleepclk); + adapter->sp.sp_error = le16_to_cpu(sp->error); + adapter->sp.sp_offset = le16_to_cpu(sp->offset); + adapter->sp.sp_stabletime = le16_to_cpu(sp->stabletime); + adapter->sp.sp_calcontrol = le16_to_cpu(sp->calcontrol); + adapter->sp.sp_extsleepclk = le16_to_cpu(sp->externalsleepclk); + adapter->sp.sp_reserved = le16_to_cpu(sp->reserved); + + LEAVE(); + return 0; +} + +static int wlan_ret_802_11_stat(wlan_private * priv, + struct cmd_ds_command *resp) +{ +/* currently adapter->wlan802_11Stat is unused + + struct cmd_ds_802_11_get_stat *p11Stat = &resp->params.gstat; + wlan_adapter *adapter = priv->adapter; + + // TODO Convert it to Big endian befor copy + memcpy(&adapter->wlan802_11Stat, + p11Stat, sizeof(struct cmd_ds_802_11_get_stat)); +*/ + return 0; +} + +static int wlan_ret_802_11_snmp_mib(wlan_private * priv, + struct cmd_ds_command *resp) +{ + struct cmd_ds_802_11_snmp_mib *smib = &resp->params.smib; + u16 oid = le16_to_cpu(smib->oid); + u16 querytype = le16_to_cpu(smib->querytype); + + ENTER(); + + lbs_pr_debug(1, "SNMP_RESP: value of the oid = %x, querytype=%x\n", oid, + querytype); + lbs_pr_debug(1, "SNMP_RESP: Buf size = %x\n", + le16_to_cpu(smib->bufsize)); + + if (querytype == cmd_act_get) { + switch (oid) { + case fragthresh_i: + priv->adapter->fragthsd = + le16_to_cpu(* + ((unsigned short *)(smib->value))); + lbs_pr_debug(1, "SNMP_RESP: fragthsd =%u\n", + priv->adapter->fragthsd); + break; + case rtsthresh_i: + priv->adapter->rtsthsd = + le16_to_cpu(* + ((unsigned short *)(smib->value))); + lbs_pr_debug(1, "SNMP_RESP: rtsthsd =%u\n", + priv->adapter->rtsthsd); + break; + case short_retrylim_i: + priv->adapter->txretrycount = + le16_to_cpu(* + ((unsigned short *)(smib->value))); + lbs_pr_debug(1, "SNMP_RESP: txretrycount =%u\n", + priv->adapter->rtsthsd); + break; + default: + break; + } + } + + LEAVE(); + return 0; +} + +static int wlan_ret_802_11_key_material(wlan_private * priv, + struct cmd_ds_command *resp) +{ + struct cmd_ds_802_11_key_material *pkeymaterial = + &resp->params.keymaterial; + wlan_adapter *adapter = priv->adapter; + u16 action = le16_to_cpu(pkeymaterial->action); + + ENTER(); + + /* Copy the returned key to driver private data */ + if (action == cmd_act_get) { + u8 * buf_ptr = (u8 *) &pkeymaterial->keyParamSet; + u8 * resp_end = (u8 *) (resp + le16_to_cpu(resp->size)); + + while (buf_ptr < resp_end) { + struct MrvlIEtype_keyParamSet * pkeyparamset = + (struct MrvlIEtype_keyParamSet *) buf_ptr; + struct WLAN_802_11_KEY * pkey; + u16 key_info = le16_to_cpu(pkeyparamset->keyinfo); + u16 param_set_len = le16_to_cpu(pkeyparamset->length); + u8 * end; + u16 key_len = le16_to_cpu(pkeyparamset->keylen); + + end = (u8 *) pkeyparamset + sizeof (pkeyparamset->type) + + sizeof (pkeyparamset->length) + + param_set_len; + /* Make sure we don't access past the end of the IEs */ + if (end > resp_end) + break; + + if (key_info & KEY_INFO_WPA_UNICAST) + pkey = &adapter->wpa_unicast_key; + else if (key_info & KEY_INFO_WPA_MCAST) + pkey = &adapter->wpa_mcast_key; + else + break; + + /* Copy returned key into driver */ + memset(pkey, 0, sizeof(struct WLAN_802_11_KEY)); + if (key_len > sizeof(pkey->key)) + break; + pkey->type = le16_to_cpu(pkeyparamset->keytypeid); + pkey->flags = le16_to_cpu(pkeyparamset->keyinfo); + pkey->len = le16_to_cpu(pkeyparamset->keylen); + memcpy(pkey->key, pkeyparamset->key, pkey->len); + + buf_ptr = end + 1; + } + } + + LEAVE(); + return 0; +} + +static int wlan_ret_802_11_mac_address(wlan_private * priv, + struct cmd_ds_command *resp) +{ + struct cmd_ds_802_11_mac_address *macadd = &resp->params.macadd; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + memcpy(adapter->current_addr, macadd->macadd, ETH_ALEN); + + LEAVE(); + return 0; +} + +static int wlan_ret_802_11_rf_tx_power(wlan_private * priv, + struct cmd_ds_command *resp) +{ + struct cmd_ds_802_11_rf_tx_power *rtp = &resp->params.txp; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + adapter->txpowerlevel = le16_to_cpu(rtp->currentlevel); + + lbs_pr_debug(1, "Current TxPower Level = %d\n", adapter->txpowerlevel); + + LEAVE(); + return 0; +} + +static int wlan_ret_802_11_rf_antenna(wlan_private * priv, + struct cmd_ds_command *resp) +{ + struct cmd_ds_802_11_rf_antenna *pAntenna = &resp->params.rant; + wlan_adapter *adapter = priv->adapter; + u16 action = le16_to_cpu(pAntenna->action); + + if (action == cmd_act_get_rx) + adapter->rxantennamode = + le16_to_cpu(pAntenna->antennamode); + + if (action == cmd_act_get_tx) + adapter->txantennamode = + le16_to_cpu(pAntenna->antennamode); + + lbs_pr_debug(1, "RF_ANT_RESP: action = 0x%x, mode = 0x%04x\n", + action, le16_to_cpu(pAntenna->antennamode)); + + return 0; +} + +static int wlan_ret_802_11_rate_adapt_rateset(wlan_private * priv, + struct cmd_ds_command *resp) +{ + struct cmd_ds_802_11_rate_adapt_rateset *rates = + &resp->params.rateset; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + if (rates->action == cmd_act_get) { + adapter->enablehwauto = rates->enablehwauto; + adapter->ratebitmap = rates->bitmap; + } + + LEAVE(); + + return 0; +} + +static int wlan_ret_802_11_data_rate(wlan_private * priv, + struct cmd_ds_command *resp) +{ + struct cmd_ds_802_11_data_rate *pdatarate = &resp->params.drate; + wlan_adapter *adapter = priv->adapter; + u8 dot11datarate; + + ENTER(); + + lbs_dbg_hex("DATA_RATE_RESP: data_rate- ", + (u8 *) pdatarate, sizeof(struct cmd_ds_802_11_data_rate)); + + dot11datarate = pdatarate->datarate[0]; + if (pdatarate->action == cmd_act_get_tx_rate) { + memcpy(adapter->libertas_supported_rates, pdatarate->datarate, + sizeof(adapter->libertas_supported_rates)); + } + adapter->datarate = libertas_index_to_data_rate(dot11datarate); + + LEAVE(); + return 0; +} + +static int wlan_ret_802_11_rf_channel(wlan_private * priv, + struct cmd_ds_command *resp) +{ + struct cmd_ds_802_11_rf_channel *rfchannel = + &resp->params.rfchannel; + wlan_adapter *adapter = priv->adapter; + u16 action = le16_to_cpu(rfchannel->action); + u16 newchannel = le16_to_cpu(rfchannel->currentchannel); + + ENTER(); + + if (action == cmd_opt_802_11_rf_channel_get + && adapter->curbssparams.channel != newchannel) { + lbs_pr_debug(1, "channel Switch: %d to %d\n", + adapter->curbssparams.channel, newchannel); + + /* Update the channel again */ + adapter->curbssparams.channel = newchannel; + } + + LEAVE(); + return 0; +} + +static int wlan_ret_802_11_rssi(wlan_private * priv, + struct cmd_ds_command *resp) +{ + struct cmd_ds_802_11_rssi_rsp *rssirsp = &resp->params.rssirsp; + wlan_adapter *adapter = priv->adapter; + + /* store the non average value */ + adapter->SNR[TYPE_BEACON][TYPE_NOAVG] = le16_to_cpu(rssirsp->SNR); + adapter->NF[TYPE_BEACON][TYPE_NOAVG] = + le16_to_cpu(rssirsp->noisefloor); + + adapter->SNR[TYPE_BEACON][TYPE_AVG] = le16_to_cpu(rssirsp->avgSNR); + adapter->NF[TYPE_BEACON][TYPE_AVG] = + le16_to_cpu(rssirsp->avgnoisefloor); + + adapter->RSSI[TYPE_BEACON][TYPE_NOAVG] = + CAL_RSSI(adapter->SNR[TYPE_BEACON][TYPE_NOAVG], + adapter->NF[TYPE_BEACON][TYPE_NOAVG]); + + adapter->RSSI[TYPE_BEACON][TYPE_AVG] = + CAL_RSSI(adapter->SNR[TYPE_BEACON][TYPE_AVG] / AVG_SCALE, + adapter->NF[TYPE_BEACON][TYPE_AVG] / AVG_SCALE); + + lbs_pr_debug(1, "Beacon RSSI value = 0x%x\n", + adapter->RSSI[TYPE_BEACON][TYPE_AVG]); + + return 0; +} + +static int wlan_ret_802_11_eeprom_access(wlan_private * priv, + struct cmd_ds_command *resp) +{ + wlan_adapter *adapter = priv->adapter; + struct wlan_ioctl_regrdwr *pbuf; + pbuf = (struct wlan_ioctl_regrdwr *) adapter->prdeeprom; + + lbs_pr_debug(1, "eeprom read len=%x\n", + le16_to_cpu(resp->params.rdeeprom.bytecount)); + if (pbuf->NOB < le16_to_cpu(resp->params.rdeeprom.bytecount)) { + pbuf->NOB = 0; + lbs_pr_debug(1, "eeprom read return length is too big\n"); + return -1; + } + pbuf->NOB = le16_to_cpu(resp->params.rdeeprom.bytecount); + if (pbuf->NOB > 0) { + + memcpy(&pbuf->value, (u8 *) & resp->params.rdeeprom.value, + le16_to_cpu(resp->params.rdeeprom.bytecount)); + lbs_dbg_hex("adapter", (char *)&pbuf->value, + le16_to_cpu(resp->params.rdeeprom.bytecount)); + } + return 0; +} + +static int wlan_ret_get_log(wlan_private * priv, + struct cmd_ds_command *resp) +{ + struct cmd_ds_802_11_get_log *logmessage = + (struct cmd_ds_802_11_get_log *)&resp->params.glog; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + /* TODO Convert it to Big Endian before copy */ + memcpy(&adapter->logmsg, logmessage, + sizeof(struct cmd_ds_802_11_get_log)); + + LEAVE(); + return 0; +} + +static inline int handle_cmd_response(u16 respcmd, + struct cmd_ds_command *resp, + wlan_private *priv) +{ + int ret = 0; + unsigned long flags; + wlan_adapter *adapter = priv->adapter; + + switch (respcmd) { + case cmd_ret_mac_reg_access: + case cmd_ret_bbp_reg_access: + case cmd_ret_rf_reg_access: + ret = wlan_ret_reg_access(priv, respcmd, resp); + break; + + case cmd_ret_hw_spec_info: + ret = wlan_ret_get_hw_spec(priv, resp); + break; + + case cmd_ret_802_11_scan: + ret = libertas_ret_80211_scan(priv, resp); + break; + + case cmd_ret_802_11_get_log: + ret = wlan_ret_get_log(priv, resp); + break; + + case cmd_ret_802_11_associate: + case cmd_ret_802_11_reassociate: + ret = libertas_ret_80211_associate(priv, resp); + break; + + case cmd_ret_802_11_disassociate: + case cmd_ret_802_11_deauthenticate: + ret = libertas_ret_80211_disassociate(priv, resp); + break; + + case cmd_ret_802_11_ad_hoc_start: + case cmd_ret_802_11_ad_hoc_join: + ret = libertas_ret_80211_ad_hoc_start(priv, resp); + break; + + case cmd_ret_802_11_stat: + ret = wlan_ret_802_11_stat(priv, resp); + break; + + case cmd_ret_802_11_snmp_mib: + ret = wlan_ret_802_11_snmp_mib(priv, resp); + break; + + case cmd_ret_802_11_rf_tx_power: + ret = wlan_ret_802_11_rf_tx_power(priv, resp); + break; + + case cmd_ret_802_11_set_afc: + case cmd_ret_802_11_get_afc: + spin_lock_irqsave(&adapter->driver_lock, flags); + memmove(adapter->cur_cmd->pdata_buf, + &resp->params.afc, + sizeof(struct cmd_ds_802_11_afc)); + spin_unlock_irqrestore(&adapter->driver_lock, flags); + + break; + case cmd_ret_802_11_rf_antenna: + ret = wlan_ret_802_11_rf_antenna(priv, resp); + break; + + case cmd_ret_mac_multicast_adr: + case cmd_ret_mac_control: + case cmd_ret_802_11_set_wep: + case cmd_ret_802_11_reset: + case cmd_ret_802_11_authenticate: + case cmd_ret_802_11_radio_control: + case cmd_ret_802_11_beacon_stop: + case cmd_ret_802_11_enable_rsn: + break; + + case cmd_ret_802_11_data_rate: + ret = wlan_ret_802_11_data_rate(priv, resp); + break; + case cmd_ret_802_11_rate_adapt_rateset: + ret = wlan_ret_802_11_rate_adapt_rateset(priv, resp); + break; + case cmd_ret_802_11_rf_channel: + ret = wlan_ret_802_11_rf_channel(priv, resp); + break; + + case cmd_ret_802_11_rssi: + ret = wlan_ret_802_11_rssi(priv, resp); + break; + + case cmd_ret_802_11_mac_address: + ret = wlan_ret_802_11_mac_address(priv, resp); + break; + + case cmd_ret_802_11_ad_hoc_stop: + ret = libertas_ret_80211_ad_hoc_stop(priv, resp); + break; + + case cmd_ret_802_11_key_material: + lbs_pr_debug(1, "CMD_RESP: KEY_MATERIAL command response\n"); + ret = wlan_ret_802_11_key_material(priv, resp); + break; + + case cmd_ret_802_11_eeprom_access: + ret = wlan_ret_802_11_eeprom_access(priv, resp); + break; + + case cmd_ret_802_11d_domain_info: + ret = libertas_ret_802_11d_domain_info(priv, resp); + break; + + case cmd_ret_802_11_sleep_params: + ret = wlan_ret_802_11_sleep_params(priv, resp); + break; + case cmd_ret_802_11_inactivity_timeout: + spin_lock_irqsave(&adapter->driver_lock, flags); + *((u16 *) adapter->cur_cmd->pdata_buf) = + le16_to_cpu(resp->params.inactivity_timeout.timeout); + spin_unlock_irqrestore(&adapter->driver_lock, flags); + break; + + case cmd_ret_802_11_tpc_cfg: + spin_lock_irqsave(&adapter->driver_lock, flags); + memmove(adapter->cur_cmd->pdata_buf, + &resp->params.tpccfg, + sizeof(struct cmd_ds_802_11_tpc_cfg)); + spin_unlock_irqrestore(&adapter->driver_lock, flags); + break; + case cmd_ret_802_11_led_gpio_ctrl: + spin_lock_irqsave(&adapter->driver_lock, flags); + memmove(adapter->cur_cmd->pdata_buf, + &resp->params.ledgpio, + sizeof(struct cmd_ds_802_11_led_ctrl)); + spin_unlock_irqrestore(&adapter->driver_lock, flags); + break; + case cmd_ret_802_11_pwr_cfg: + spin_lock_irqsave(&adapter->driver_lock, flags); + memmove(adapter->cur_cmd->pdata_buf, + &resp->params.pwrcfg, + sizeof(struct cmd_ds_802_11_pwr_cfg)); + spin_unlock_irqrestore(&adapter->driver_lock, flags); + + break; + + case cmd_ret_get_tsf: + spin_lock_irqsave(&adapter->driver_lock, flags); + memcpy(priv->adapter->cur_cmd->pdata_buf, + &resp->params.gettsf.tsfvalue, sizeof(u64)); + spin_unlock_irqrestore(&adapter->driver_lock, flags); + break; + case cmd_ret_bt_access: + spin_lock_irqsave(&adapter->driver_lock, flags); + if (adapter->cur_cmd->pdata_buf) + memcpy(adapter->cur_cmd->pdata_buf, + &resp->params.bt.addr1, 2 * ETH_ALEN); + spin_unlock_irqrestore(&adapter->driver_lock, flags); + break; + case cmd_ret_fwt_access: + spin_lock_irqsave(&adapter->driver_lock, flags); + if (adapter->cur_cmd->pdata_buf) + memcpy(adapter->cur_cmd->pdata_buf, + &resp->params.fwt, + sizeof(resp->params.fwt)); + spin_unlock_irqrestore(&adapter->driver_lock, flags); + break; + case cmd_ret_mesh_access: + if (adapter->cur_cmd->pdata_buf) + memcpy(adapter->cur_cmd->pdata_buf, + &resp->params.mesh, + sizeof(resp->params.mesh)); + break; + case cmd_rte_802_11_tx_rate_query: + priv->adapter->txrate = resp->params.txrate.txrate; + break; + default: + lbs_pr_debug(1, "CMD_RESP: Unknown command response %#x\n", + resp->command); + break; + } + return ret; +} + +int libertas_process_rx_command(wlan_private * priv) +{ + u16 respcmd; + struct cmd_ds_command *resp; + wlan_adapter *adapter = priv->adapter; + int ret = 0; + ulong flags; + u16 result; + + ENTER(); + + lbs_pr_debug(1, "CMD_RESP: @ %lu\n", jiffies); + + /* Now we got response from FW, cancel the command timer */ + del_timer(&adapter->command_timer); + + mutex_lock(&adapter->lock); + spin_lock_irqsave(&adapter->driver_lock, flags); + + if (!adapter->cur_cmd) { + lbs_pr_debug(1, "CMD_RESP: NULL cur_cmd=%p\n", adapter->cur_cmd); + ret = -1; + spin_unlock_irqrestore(&adapter->driver_lock, flags); + goto done; + } + resp = (struct cmd_ds_command *)(adapter->cur_cmd->bufvirtualaddr); + + lbs_dbg_hex("CMD_RESP:", adapter->cur_cmd->bufvirtualaddr, + priv->wlan_dev.upld_len); + + respcmd = le16_to_cpu(resp->command); + + result = le16_to_cpu(resp->result); + + lbs_pr_debug(1, "CMD_RESP: %x result: %d length: %d\n", respcmd, + result, priv->wlan_dev.upld_len); + + if (!(respcmd & 0x8000)) { + lbs_pr_debug(1, "Invalid response to command!"); + adapter->cur_cmd_retcode = -1; + __libertas_cleanup_and_insert_cmd(priv, adapter->cur_cmd); + adapter->nr_cmd_pending--; + adapter->cur_cmd = NULL; + spin_unlock_irqrestore(&adapter->driver_lock, flags); + ret = -1; + goto done; + } + + /* Store the response code to cur_cmd_retcode. */ + adapter->cur_cmd_retcode = le16_to_cpu(resp->result); + + if (respcmd == cmd_ret_802_11_ps_mode) { + struct cmd_ds_802_11_ps_mode *psmode; + + psmode = &resp->params.psmode; + lbs_pr_debug(1, + "CMD_RESP: PS_MODE cmd reply result=%#x action=0x%X\n", + resp->result, psmode->action); + psmode->action = cpu_to_le16(psmode->action); + + if (result) { + lbs_pr_debug(1, "CMD_RESP: PS command failed- %#x \n", + resp->result); + if (adapter->inframode == wlan802_11ibss) { + /* + * We should not re-try enter-ps command in + * ad-hoc mode. It takes place in + * libertas_execute_next_command(). + */ + if (psmode->action == cmd_subcmd_enter_ps) + adapter->psmode = + wlan802_11powermodecam; + } + } else if (psmode->action == cmd_subcmd_enter_ps) { + adapter->needtowakeup = 0; + adapter->psstate = PS_STATE_AWAKE; + + lbs_pr_debug(1, "CMD_RESP: Enter_PS command response\n"); + if (adapter->connect_status != libertas_connected) { + /* + * When Deauth Event received before Enter_PS command + * response, We need to wake up the firmware. + */ + lbs_pr_debug(1, + "Disconnected, Going to invoke libertas_ps_wakeup\n"); + + mutex_unlock(&adapter->lock); + spin_unlock_irqrestore(&adapter->driver_lock, flags); + libertas_ps_wakeup(priv, 0); + mutex_lock(&adapter->lock); + spin_lock_irqsave(&adapter->driver_lock, flags); + } + } else if (psmode->action == cmd_subcmd_exit_ps) { + adapter->needtowakeup = 0; + adapter->psstate = PS_STATE_FULL_POWER; + lbs_pr_debug(1, "CMD_RESP: Exit_PS command response\n"); + } else { + lbs_pr_debug(1, "CMD_RESP: PS- action=0x%X\n", + psmode->action); + } + + __libertas_cleanup_and_insert_cmd(priv, adapter->cur_cmd); + adapter->nr_cmd_pending--; + adapter->cur_cmd = NULL; + spin_unlock_irqrestore(&adapter->driver_lock, flags); + + ret = 0; + goto done; + } + + if (adapter->cur_cmd->cmdflags & CMD_F_HOSTCMD) { + /* Copy the response back to response buffer */ + memcpy(adapter->cur_cmd->pdata_buf, resp, resp->size); + + adapter->cur_cmd->cmdflags &= ~CMD_F_HOSTCMD; + } + + /* If the command is not successful, cleanup and return failure */ + if ((result != 0 || !(respcmd & 0x8000))) { + lbs_pr_debug(1, "CMD_RESP: command reply %#x result=%#x\n", + resp->command, resp->result); + /* + * Handling errors here + */ + switch (respcmd) { + case cmd_ret_hw_spec_info: + case cmd_ret_802_11_reset: + lbs_pr_debug(1, "CMD_RESP: Reset command failed\n"); + break; + + } + + __libertas_cleanup_and_insert_cmd(priv, adapter->cur_cmd); + adapter->nr_cmd_pending--; + adapter->cur_cmd = NULL; + spin_unlock_irqrestore(&adapter->driver_lock, flags); + + ret = -1; + goto done; + } + + spin_unlock_irqrestore(&adapter->driver_lock, flags); + + ret = handle_cmd_response(respcmd, resp, priv); + + spin_lock_irqsave(&adapter->driver_lock, flags); + if (adapter->cur_cmd) { + /* Clean up and Put current command back to cmdfreeq */ + __libertas_cleanup_and_insert_cmd(priv, adapter->cur_cmd); + adapter->nr_cmd_pending--; + WARN_ON(adapter->nr_cmd_pending > 128); + adapter->cur_cmd = NULL; + } + spin_unlock_irqrestore(&adapter->driver_lock, flags); + +done: + mutex_unlock(&adapter->lock); + LEAVE(); + return ret; +} + +int libertas_process_event(wlan_private * priv) +{ + int ret = 0; + wlan_adapter *adapter = priv->adapter; + u32 eventcause; + + spin_lock_irq(&adapter->driver_lock); + eventcause = adapter->eventcause; + spin_unlock_irq(&adapter->driver_lock); + + ENTER(); + + lbs_pr_debug(1, "EVENT Cause %x\n", eventcause); + + switch (eventcause >> SBI_EVENT_CAUSE_SHIFT) { + case MACREG_INT_CODE_LINK_SENSED: + lbs_pr_debug(1, "EVENT: MACREG_INT_CODE_LINK_SENSED\n"); + break; + + case MACREG_INT_CODE_DEAUTHENTICATED: + lbs_pr_debug(1, "EVENT: Deauthenticated\n"); + libertas_mac_event_disconnected(priv); + break; + + case MACREG_INT_CODE_DISASSOCIATED: + lbs_pr_debug(1, "EVENT: Disassociated\n"); + libertas_mac_event_disconnected(priv); + break; + + case MACREG_INT_CODE_LINK_LOSE_NO_SCAN: + lbs_pr_debug(1, "EVENT: Link lost\n"); + libertas_mac_event_disconnected(priv); + break; + + case MACREG_INT_CODE_PS_SLEEP: + lbs_pr_debug(1, "EVENT: SLEEP\n"); + lbs_pr_debug(1, "_"); + + /* handle unexpected PS SLEEP event */ + if (adapter->psstate == PS_STATE_FULL_POWER) { + lbs_pr_debug(1, + "EVENT: In FULL POWER mode - ignore PS SLEEP\n"); + break; + } + adapter->psstate = PS_STATE_PRE_SLEEP; + + libertas_ps_confirm_sleep(priv, (u16) adapter->psmode); + + break; + + case MACREG_INT_CODE_PS_AWAKE: + lbs_pr_debug(1, "EVENT: AWAKE \n"); + lbs_pr_debug(1, "|"); + + /* handle unexpected PS AWAKE event */ + if (adapter->psstate == PS_STATE_FULL_POWER) { + lbs_pr_debug(1, + "EVENT: In FULL POWER mode - ignore PS AWAKE\n"); + break; + } + + adapter->psstate = PS_STATE_AWAKE; + + if (adapter->needtowakeup) { + /* + * wait for the command processing to finish + * before resuming sending + * adapter->needtowakeup will be set to FALSE + * in libertas_ps_wakeup() + */ + lbs_pr_debug(1, "Waking up...\n"); + libertas_ps_wakeup(priv, 0); + } + break; + + case MACREG_INT_CODE_MIC_ERR_UNICAST: + lbs_pr_debug(1, "EVENT: UNICAST MIC ERROR\n"); + handle_mic_failureevent(priv, MACREG_INT_CODE_MIC_ERR_UNICAST); + break; + + case MACREG_INT_CODE_MIC_ERR_MULTICAST: + lbs_pr_debug(1, "EVENT: MULTICAST MIC ERROR\n"); + handle_mic_failureevent(priv, MACREG_INT_CODE_MIC_ERR_MULTICAST); + break; + case MACREG_INT_CODE_MIB_CHANGED: + case MACREG_INT_CODE_INIT_DONE: + break; + + case MACREG_INT_CODE_ADHOC_BCN_LOST: + lbs_pr_debug(1, "EVENT: HWAC - ADHOC BCN LOST\n"); + break; + + case MACREG_INT_CODE_RSSI_LOW: + lbs_pr_alert( "EVENT: RSSI_LOW\n"); + break; + case MACREG_INT_CODE_SNR_LOW: + lbs_pr_alert( "EVENT: SNR_LOW\n"); + break; + case MACREG_INT_CODE_MAX_FAIL: + lbs_pr_alert( "EVENT: MAX_FAIL\n"); + break; + case MACREG_INT_CODE_RSSI_HIGH: + lbs_pr_alert( "EVENT: RSSI_HIGH\n"); + break; + case MACREG_INT_CODE_SNR_HIGH: + lbs_pr_alert( "EVENT: SNR_HIGH\n"); + break; + + default: + lbs_pr_alert( "EVENT: unknown event id: %#x\n", + eventcause >> SBI_EVENT_CAUSE_SHIFT); + break; + } + + spin_lock_irq(&adapter->driver_lock); + adapter->eventcause = 0; + spin_unlock_irq(&adapter->driver_lock); + LEAVE(); + return ret; +} diff --git a/drivers/net/wireless/libertas/debugfs.c b/drivers/net/wireless/libertas/debugfs.c new file mode 100644 index 00000000000..51dfd202f55 --- /dev/null +++ b/drivers/net/wireless/libertas/debugfs.c @@ -0,0 +1,1935 @@ +#include <linux/module.h> +#include <linux/dcache.h> +#include <linux/debugfs.h> +#include <linux/delay.h> +#include <linux/mm.h> +#include <net/iw_handler.h> +#include "dev.h" +#include "decl.h" +#include "host.h" + +static struct dentry *libertas_dir = NULL; +static char *szStates[] = { + "Connected", + "Disconnected" +}; + +void libertas_debug_init(wlan_private * priv, struct net_device *dev); + +static int open_file_generic(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static ssize_t write_file_dummy(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + return -EINVAL; +} + +static const size_t len = PAGE_SIZE; + +static ssize_t libertas_dev_info(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + wlan_private *priv = file->private_data; + size_t pos = 0; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + ssize_t res; + + pos += snprintf(buf+pos, len-pos, "state = %s\n", + szStates[priv->adapter->connect_status]); + pos += snprintf(buf+pos, len-pos, "region_code = %02x\n", + (u32) priv->adapter->regioncode); + + res = simple_read_from_buffer(userbuf, count, ppos, buf, pos); + + free_page(addr); + return res; +} + + +static ssize_t libertas_getscantable(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + wlan_private *priv = file->private_data; + size_t pos = 0; + int numscansdone = 0, res; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + + pos += snprintf(buf+pos, len-pos, + "---------------------------------------"); + pos += snprintf(buf+pos, len-pos, + "---------------------------------------\n"); + pos += snprintf(buf+pos, len-pos, + "# | ch | ss | bssid | cap | TSF | Qual | SSID \n"); + pos += snprintf(buf+pos, len-pos, + "---------------------------------------"); + pos += snprintf(buf+pos, len-pos, + "---------------------------------------\n"); + + while (numscansdone < priv->adapter->numinscantable) { + struct bss_descriptor *pbssinfo; + u16 cap; + + pbssinfo = &priv->adapter->scantable[numscansdone]; + memcpy(&cap, &pbssinfo->cap, sizeof(cap)); + pos += snprintf(buf+pos, len-pos, + "%02u| %03d | %03ld | %02x:%02x:%02x:%02x:%02x:%02x |", + numscansdone, pbssinfo->channel, pbssinfo->rssi, + pbssinfo->macaddress[0], pbssinfo->macaddress[1], + pbssinfo->macaddress[2], pbssinfo->macaddress[3], + pbssinfo->macaddress[4], pbssinfo->macaddress[5]); + pos += snprintf(buf+pos, len-pos, " %04x-", cap); + pos += snprintf(buf+pos, len-pos, "%c%c%c |", + pbssinfo->cap.ibss ? 'A' : 'I', + pbssinfo->cap.privacy ? 'P' : ' ', + pbssinfo->cap.spectrummgmt ? 'S' : ' '); + pos += snprintf(buf+pos, len-pos, " %08llx |", pbssinfo->networktsf); + pos += snprintf(buf+pos, len-pos, " %d |", + SCAN_RSSI(priv->adapter->scantable[numscansdone].rssi)); + + pos += snprintf(buf+pos, len-pos, " %s\n", pbssinfo->ssid.ssid); + + numscansdone++; + } + + res = simple_read_from_buffer(userbuf, count, ppos, buf, pos); + + free_page(addr); + return res; +} + +static ssize_t libertas_sleepparams_write(struct file *file, + const char __user *user_buf, size_t count, + loff_t *ppos) +{ + wlan_private *priv = file->private_data; + ssize_t buf_size, res; + int p1, p2, p3, p4, p5, p6; + struct sleep_params sp; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + + buf_size = min(count, len - 1); + if (copy_from_user(buf, user_buf, buf_size)) { + res = -EFAULT; + goto out_unlock; + } + res = sscanf(buf, "%d %d %d %d %d %d", &p1, &p2, &p3, &p4, &p5, &p6); + if (res != 6) { + res = -EFAULT; + goto out_unlock; + } + sp.sp_error = p1; + sp.sp_offset = p2; + sp.sp_stabletime = p3; + sp.sp_calcontrol = p4; + sp.sp_extsleepclk = p5; + sp.sp_reserved = p6; + + memcpy(&priv->adapter->sp, &sp, sizeof(struct sleep_params)); + + res = libertas_prepare_and_send_command(priv, + cmd_802_11_sleep_params, + cmd_act_set, + cmd_option_waitforrsp, 0, NULL); + + if (!res) + res = count; + else + res = -EINVAL; + +out_unlock: + free_page(addr); + return res; +} + +static ssize_t libertas_sleepparams_read(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + wlan_private *priv = file->private_data; + wlan_adapter *adapter = priv->adapter; + ssize_t res; + size_t pos = 0; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + + res = libertas_prepare_and_send_command(priv, + cmd_802_11_sleep_params, + cmd_act_get, + cmd_option_waitforrsp, 0, NULL); + if (res) { + res = -EFAULT; + goto out_unlock; + } + + pos += snprintf(buf, len, "%d %d %d %d %d %d\n", adapter->sp.sp_error, + adapter->sp.sp_offset, adapter->sp.sp_stabletime, + adapter->sp.sp_calcontrol, adapter->sp.sp_extsleepclk, + adapter->sp.sp_reserved); + + res = simple_read_from_buffer(userbuf, count, ppos, buf, pos); + +out_unlock: + free_page(addr); + return res; +} + +static ssize_t libertas_extscan(struct file *file, const char __user *userbuf, + size_t count, loff_t *ppos) +{ + wlan_private *priv = file->private_data; + ssize_t res, buf_size; + struct WLAN_802_11_SSID extscan_ssid; + union iwreq_data wrqu; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + + buf_size = min(count, len - 1); + if (copy_from_user(buf, userbuf, buf_size)) { + res = -EFAULT; + goto out_unlock; + } + + memcpy(&extscan_ssid.ssid, buf, strlen(buf)-1); + extscan_ssid.ssidlength = strlen(buf)-1; + + libertas_send_specific_SSID_scan(priv, &extscan_ssid, 1); + + memset(&wrqu, 0, sizeof(union iwreq_data)); + wireless_send_event(priv->wlan_dev.netdev, SIOCGIWSCAN, &wrqu, NULL); + +out_unlock: + free_page(addr); + return count; +} + +static int libertas_parse_chan(char *buf, size_t count, + struct wlan_ioctl_user_scan_cfg *scan_cfg, int dur) +{ + char *start, *end, *hold, *str; + int i = 0; + + start = strstr(buf, "chan="); + if (!start) + return -EINVAL; + start += 5; + end = strstr(start, " "); + if (!end) + end = buf + count; + hold = kzalloc((end - start)+1, GFP_KERNEL); + if (!hold) + return -ENOMEM; + strncpy(hold, start, end - start); + hold[(end-start)+1] = '\0'; + while(hold && (str = strsep(&hold, ","))) { + int chan; + char band, passive = 0; + sscanf(str, "%d%c%c", &chan, &band, &passive); + scan_cfg->chanlist[i].channumber = chan; + scan_cfg->chanlist[i].scantype = passive ? 1 : 0; + if (band == 'b' || band == 'g') + scan_cfg->chanlist[i].radiotype = 0; + else if (band == 'a') + scan_cfg->chanlist[i].radiotype = 1; + + scan_cfg->chanlist[i].scantime = dur; + i++; + } + + kfree(hold); + return i; +} + +static void libertas_parse_bssid(char *buf, size_t count, + struct wlan_ioctl_user_scan_cfg *scan_cfg) +{ + char *hold; + unsigned int mac[ETH_ALEN]; + int i; + + hold = strstr(buf, "bssid="); + if (!hold) + return; + hold += 6; + sscanf(hold, "%2x:%2x:%2x:%2x:%2x:%2x", mac, mac+1, mac+2, mac+3, + mac+4, mac+5); + for(i=0;i<ETH_ALEN;i++) + scan_cfg->specificBSSID[i] = mac[i]; +} + +static void libertas_parse_ssid(char *buf, size_t count, + struct wlan_ioctl_user_scan_cfg *scan_cfg) +{ + char *hold, *end; + ssize_t size; + + hold = strstr(buf, "ssid="); + if (!hold) + return; + hold += 5; + end = strstr(hold, " "); + if (!end) + end = buf + count - 1; + + size = min(IW_ESSID_MAX_SIZE, end - hold); + strncpy(scan_cfg->specificSSID, hold, size); + + return; +} + +static void libertas_parse_keep(char *buf, size_t count, + struct wlan_ioctl_user_scan_cfg *scan_cfg) +{ + char *hold; + int val; + + hold = strstr(buf, "keep="); + if (!hold) + return; + hold += 5; + sscanf(hold, "%d", &val); + + if (val != 0) + val = 1; + + scan_cfg->keeppreviousscan = val; + return; +} + +static int libertas_parse_dur(char *buf, size_t count, + struct wlan_ioctl_user_scan_cfg *scan_cfg) +{ + char *hold; + int val; + + hold = strstr(buf, "dur="); + if (!hold) + return 0; + hold += 4; + sscanf(hold, "%d", &val); + + return val; +} + +static void libertas_parse_probes(char *buf, size_t count, + struct wlan_ioctl_user_scan_cfg *scan_cfg) +{ + char *hold; + int val; + + hold = strstr(buf, "probes="); + if (!hold) + return; + hold += 7; + sscanf(hold, "%d", &val); + + scan_cfg->numprobes = val; + + return; +} + +static void libertas_parse_type(char *buf, size_t count, + struct wlan_ioctl_user_scan_cfg *scan_cfg) +{ + char *hold; + int val; + + hold = strstr(buf, "type="); + if (!hold) + return; + hold += 5; + sscanf(hold, "%d", &val); + + /* type=1,2 or 3 */ + if (val < 1 || val > 3) + return; + + scan_cfg->bsstype = val; + + return; +} + +static ssize_t libertas_setuserscan(struct file *file, + const char __user *userbuf, + size_t count, loff_t *ppos) +{ + wlan_private *priv = file->private_data; + ssize_t res, buf_size; + struct wlan_ioctl_user_scan_cfg *scan_cfg; + union iwreq_data wrqu; + int dur; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + + scan_cfg = kzalloc(sizeof(struct wlan_ioctl_user_scan_cfg), GFP_KERNEL); + if (!scan_cfg) + return -ENOMEM; + + buf_size = min(count, len - 1); + if (copy_from_user(buf, userbuf, buf_size)) { + res = -EFAULT; + goto out_unlock; + } + + scan_cfg->bsstype = WLAN_SCAN_BSS_TYPE_ANY; + + dur = libertas_parse_dur(buf, count, scan_cfg); + libertas_parse_chan(buf, count, scan_cfg, dur); + libertas_parse_bssid(buf, count, scan_cfg); + libertas_parse_ssid(buf, count, scan_cfg); + libertas_parse_keep(buf, count, scan_cfg); + libertas_parse_probes(buf, count, scan_cfg); + libertas_parse_type(buf, count, scan_cfg); + + wlan_scan_networks(priv, scan_cfg); + wait_event_interruptible(priv->adapter->cmd_pending, + !priv->adapter->nr_cmd_pending); + + memset(&wrqu, 0x00, sizeof(union iwreq_data)); + wireless_send_event(priv->wlan_dev.netdev, SIOCGIWSCAN, &wrqu, NULL); + +out_unlock: + free_page(addr); + kfree(scan_cfg); + return count; +} + +static int libertas_event_initcmd(wlan_private *priv, void **response_buf, + struct cmd_ctrl_node **cmdnode, + struct cmd_ds_command **cmd) +{ + u16 wait_option = cmd_option_waitforrsp; + + if (!(*cmdnode = libertas_get_free_cmd_ctrl_node(priv))) { + lbs_pr_debug(1, "failed libertas_get_free_cmd_ctrl_node\n"); + return -ENOMEM; + } + if (!(*response_buf = kmalloc(3000, GFP_KERNEL))) { + lbs_pr_debug(1, "failed to allocate response buffer!\n"); + return -ENOMEM; + } + libertas_set_cmd_ctrl_node(priv, *cmdnode, 0, wait_option, NULL); + init_waitqueue_head(&(*cmdnode)->cmdwait_q); + (*cmdnode)->pdata_buf = *response_buf; + (*cmdnode)->cmdflags |= CMD_F_HOSTCMD; + (*cmdnode)->cmdwaitqwoken = 0; + *cmd = (struct cmd_ds_command *)(*cmdnode)->bufvirtualaddr; + (*cmd)->command = cmd_802_11_subscribe_event; + (*cmd)->seqnum = ++priv->adapter->seqnum; + (*cmd)->result = 0; + return 0; +} + +static ssize_t libertas_lowrssi_read(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + wlan_private *priv = file->private_data; + wlan_adapter *adapter = priv->adapter; + struct cmd_ctrl_node *pcmdnode; + struct cmd_ds_command *pcmdptr; + struct cmd_ds_802_11_subscribe_event *event; + void *response_buf; + int res, cmd_len; + ssize_t pos = 0; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + + res = libertas_event_initcmd(priv, &response_buf, &pcmdnode, &pcmdptr); + if (res < 0) { + free_page(addr); + return res; + } + + event = &pcmdptr->params.subscribe_event; + event->action = cmd_act_get; + pcmdptr->size = + cpu_to_le16(sizeof(struct cmd_ds_802_11_subscribe_event) + S_DS_GEN); + libertas_queue_cmd(adapter, pcmdnode, 1); + wake_up_interruptible(&priv->mainthread.waitq); + + /* Sleep until response is generated by FW */ + wait_event_interruptible(pcmdnode->cmdwait_q, + pcmdnode->cmdwaitqwoken); + + pcmdptr = response_buf; + if (pcmdptr->result) { + lbs_pr_err("%s: fail, result=%d\n", __FUNCTION__, + pcmdptr->result); + kfree(response_buf); + free_page(addr); + return 0; + } + + if (pcmdptr->command != cmd_ret_802_11_subscribe_event) { + lbs_pr_err("command response incorrect!\n"); + kfree(response_buf); + free_page(addr); + return 0; + } + + cmd_len = S_DS_GEN + sizeof(struct cmd_ds_802_11_subscribe_event); + event = (struct cmd_ds_802_11_subscribe_event *)(response_buf + S_DS_GEN); + while (cmd_len < pcmdptr->size) { + struct mrvlietypesheader *header = (struct mrvlietypesheader *)(response_buf + cmd_len); + switch(header->type) { + struct mrvlietypes_rssithreshold *Lowrssi; + case TLV_TYPE_RSSI_LOW: + Lowrssi = (struct mrvlietypes_rssithreshold *)(response_buf + cmd_len); + pos += snprintf(buf+pos, len-pos, "%d %d %d\n", + Lowrssi->rssivalue, + Lowrssi->rssifreq, + (event->events & 0x0001)?1:0); + default: + cmd_len += sizeof(struct mrvlietypes_snrthreshold); + break; + } + } + + kfree(response_buf); + res = simple_read_from_buffer(userbuf, count, ppos, buf, pos); + free_page(addr); + return res; +} + +static u16 libertas_get_events_bitmap(wlan_private *priv) +{ + wlan_adapter *adapter = priv->adapter; + struct cmd_ctrl_node *pcmdnode; + struct cmd_ds_command *pcmdptr; + struct cmd_ds_802_11_subscribe_event *event; + void *response_buf; + int res; + u16 event_bitmap; + + res = libertas_event_initcmd(priv, &response_buf, &pcmdnode, &pcmdptr); + if (res < 0) + return res; + + event = &pcmdptr->params.subscribe_event; + event->action = cmd_act_get; + pcmdptr->size = + cpu_to_le16(sizeof(struct cmd_ds_802_11_subscribe_event) + S_DS_GEN); + libertas_queue_cmd(adapter, pcmdnode, 1); + wake_up_interruptible(&priv->mainthread.waitq); + + /* Sleep until response is generated by FW */ + wait_event_interruptible(pcmdnode->cmdwait_q, + pcmdnode->cmdwaitqwoken); + + pcmdptr = response_buf; + + if (pcmdptr->result) { + lbs_pr_err("%s: fail, result=%d\n", __FUNCTION__, + pcmdptr->result); + kfree(response_buf); + return 0; + } + + if (pcmdptr->command != cmd_ret_802_11_subscribe_event) { + lbs_pr_err("command response incorrect!\n"); + kfree(response_buf); + return 0; + } + + event = (struct cmd_ds_802_11_subscribe_event *)(response_buf + S_DS_GEN); + event_bitmap = event->events; + kfree(response_buf); + return event_bitmap; +} + +static ssize_t libertas_lowrssi_write(struct file *file, + const char __user *userbuf, + size_t count, loff_t *ppos) +{ + wlan_private *priv = file->private_data; + wlan_adapter *adapter = priv->adapter; + ssize_t res, buf_size; + int value, freq, subscribed, cmd_len; + struct cmd_ctrl_node *pcmdnode; + struct cmd_ds_command *pcmdptr; + struct cmd_ds_802_11_subscribe_event *event; + struct mrvlietypes_rssithreshold *rssi_threshold; + void *response_buf; + u16 event_bitmap; + u8 *ptr; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + + buf_size = min(count, len - 1); + if (copy_from_user(buf, userbuf, buf_size)) { + res = -EFAULT; + goto out_unlock; + } + res = sscanf(buf, "%d %d %d", &value, &freq, &subscribed); + if (res != 3) { + res = -EFAULT; + goto out_unlock; + } + + event_bitmap = libertas_get_events_bitmap(priv); + + res = libertas_event_initcmd(priv, &response_buf, &pcmdnode, &pcmdptr); + if (res < 0) + goto out_unlock; + + event = &pcmdptr->params.subscribe_event; + event->action = cmd_act_set; + pcmdptr->size = cpu_to_le16(S_DS_GEN + + sizeof(struct cmd_ds_802_11_subscribe_event) + + sizeof(struct mrvlietypes_rssithreshold)); + + cmd_len = S_DS_GEN + sizeof(struct cmd_ds_802_11_subscribe_event); + ptr = (u8*) pcmdptr+cmd_len; + rssi_threshold = (struct mrvlietypes_rssithreshold *)(ptr); + rssi_threshold->header.type = cpu_to_le16(0x0104); + rssi_threshold->header.len = 2; + rssi_threshold->rssivalue = cpu_to_le16(value); + rssi_threshold->rssifreq = cpu_to_le16(freq); + event_bitmap |= subscribed ? 0x0001 : 0x0; + event->events = event_bitmap; + + libertas_queue_cmd(adapter, pcmdnode, 1); + wake_up_interruptible(&priv->mainthread.waitq); + + /* Sleep until response is generated by FW */ + wait_event_interruptible(pcmdnode->cmdwait_q, + pcmdnode->cmdwaitqwoken); + + pcmdptr = response_buf; + + if (pcmdptr->result) { + lbs_pr_err("%s: fail, result=%d\n", __FUNCTION__, + pcmdptr->result); + kfree(response_buf); + free_page(addr); + return 0; + } + + if (pcmdptr->command != cmd_ret_802_11_subscribe_event) { + lbs_pr_err("command response incorrect!\n"); + kfree(response_buf); + free_page(addr); + return 0; + } + + res = count; +out_unlock: + free_page(addr); + return res; +} + +static ssize_t libertas_lowsnr_read(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + wlan_private *priv = file->private_data; + wlan_adapter *adapter = priv->adapter; + struct cmd_ctrl_node *pcmdnode; + struct cmd_ds_command *pcmdptr; + struct cmd_ds_802_11_subscribe_event *event; + void *response_buf; + int res, cmd_len; + ssize_t pos = 0; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + + res = libertas_event_initcmd(priv, &response_buf, &pcmdnode, &pcmdptr); + if (res < 0) { + free_page(addr); + return res; + } + + event = &pcmdptr->params.subscribe_event; + event->action = cmd_act_get; + pcmdptr->size = + cpu_to_le16(sizeof(struct cmd_ds_802_11_subscribe_event) + S_DS_GEN); + libertas_queue_cmd(adapter, pcmdnode, 1); + wake_up_interruptible(&priv->mainthread.waitq); + + /* Sleep until response is generated by FW */ + wait_event_interruptible(pcmdnode->cmdwait_q, + pcmdnode->cmdwaitqwoken); + + pcmdptr = response_buf; + + if (pcmdptr->result) { + lbs_pr_err("%s: fail, result=%d\n", __FUNCTION__, + pcmdptr->result); + kfree(response_buf); + free_page(addr); + return 0; + } + + if (pcmdptr->command != cmd_ret_802_11_subscribe_event) { + lbs_pr_err("command response incorrect!\n"); + kfree(response_buf); + free_page(addr); + return 0; + } + + cmd_len = S_DS_GEN + sizeof(struct cmd_ds_802_11_subscribe_event); + event = (struct cmd_ds_802_11_subscribe_event *)(response_buf + S_DS_GEN); + while (cmd_len < pcmdptr->size) { + struct mrvlietypesheader *header = (struct mrvlietypesheader *)(response_buf + cmd_len); + switch(header->type) { + struct mrvlietypes_snrthreshold *LowSnr; + case TLV_TYPE_SNR_LOW: + LowSnr = (struct mrvlietypes_snrthreshold *)(response_buf + cmd_len); + pos += snprintf(buf+pos, len-pos, "%d %d %d\n", + LowSnr->snrvalue, + LowSnr->snrfreq, + (event->events & 0x0002)?1:0); + default: + cmd_len += sizeof(struct mrvlietypes_snrthreshold); + break; + } + } + + kfree(response_buf); + + res = simple_read_from_buffer(userbuf, count, ppos, buf, pos); + free_page(addr); + return res; +} + +static ssize_t libertas_lowsnr_write(struct file *file, + const char __user *userbuf, + size_t count, loff_t *ppos) +{ + wlan_private *priv = file->private_data; + wlan_adapter *adapter = priv->adapter; + ssize_t res, buf_size; + int value, freq, subscribed, cmd_len; + struct cmd_ctrl_node *pcmdnode; + struct cmd_ds_command *pcmdptr; + struct cmd_ds_802_11_subscribe_event *event; + struct mrvlietypes_snrthreshold *snr_threshold; + void *response_buf; + u16 event_bitmap; + u8 *ptr; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + + buf_size = min(count, len - 1); + if (copy_from_user(buf, userbuf, buf_size)) { + res = -EFAULT; + goto out_unlock; + } + res = sscanf(buf, "%d %d %d", &value, &freq, &subscribed); + if (res != 3) { + res = -EFAULT; + goto out_unlock; + } + + event_bitmap = libertas_get_events_bitmap(priv); + + res = libertas_event_initcmd(priv, &response_buf, &pcmdnode, &pcmdptr); + if (res < 0) + goto out_unlock; + + event = &pcmdptr->params.subscribe_event; + event->action = cmd_act_set; + pcmdptr->size = cpu_to_le16(S_DS_GEN + + sizeof(struct cmd_ds_802_11_subscribe_event) + + sizeof(struct mrvlietypes_snrthreshold)); + cmd_len = S_DS_GEN + sizeof(struct cmd_ds_802_11_subscribe_event); + ptr = (u8*) pcmdptr+cmd_len; + snr_threshold = (struct mrvlietypes_snrthreshold *)(ptr); + snr_threshold->header.type = cpu_to_le16(TLV_TYPE_SNR_LOW); + snr_threshold->header.len = 2; + snr_threshold->snrvalue = cpu_to_le16(value); + snr_threshold->snrfreq = cpu_to_le16(freq); + event_bitmap |= subscribed ? 0x0002 : 0x0; + event->events = event_bitmap; + + libertas_queue_cmd(adapter, pcmdnode, 1); + wake_up_interruptible(&priv->mainthread.waitq); + + /* Sleep until response is generated by FW */ + wait_event_interruptible(pcmdnode->cmdwait_q, + pcmdnode->cmdwaitqwoken); + + pcmdptr = response_buf; + + if (pcmdptr->result) { + lbs_pr_err("%s: fail, result=%d\n", __FUNCTION__, + pcmdptr->result); + kfree(response_buf); + free_page(addr); + return 0; + } + + if (pcmdptr->command != cmd_ret_802_11_subscribe_event) { + lbs_pr_err("command response incorrect!\n"); + kfree(response_buf); + free_page(addr); + return 0; + } + + res = count; + +out_unlock: + free_page(addr); + return res; +} + +static ssize_t libertas_failcount_read(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + wlan_private *priv = file->private_data; + wlan_adapter *adapter = priv->adapter; + struct cmd_ctrl_node *pcmdnode; + struct cmd_ds_command *pcmdptr; + struct cmd_ds_802_11_subscribe_event *event; + void *response_buf; + int res, cmd_len; + ssize_t pos = 0; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + + res = libertas_event_initcmd(priv, &response_buf, &pcmdnode, &pcmdptr); + if (res < 0) { + free_page(addr); + return res; + } + + event = &pcmdptr->params.subscribe_event; + event->action = cmd_act_get; + pcmdptr->size = + cpu_to_le16(sizeof(struct cmd_ds_802_11_subscribe_event) + S_DS_GEN); + libertas_queue_cmd(adapter, pcmdnode, 1); + wake_up_interruptible(&priv->mainthread.waitq); + + /* Sleep until response is generated by FW */ + wait_event_interruptible(pcmdnode->cmdwait_q, + pcmdnode->cmdwaitqwoken); + + pcmdptr = response_buf; + + if (pcmdptr->result) { + lbs_pr_err("%s: fail, result=%d\n", __FUNCTION__, + pcmdptr->result); + kfree(response_buf); + free_page(addr); + return 0; + } + + if (pcmdptr->command != cmd_ret_802_11_subscribe_event) { + lbs_pr_err("command response incorrect!\n"); + kfree(response_buf); + free_page(addr); + return 0; + } + + cmd_len = S_DS_GEN + sizeof(struct cmd_ds_802_11_subscribe_event); + event = (struct cmd_ds_802_11_subscribe_event *)(response_buf + S_DS_GEN); + while (cmd_len < pcmdptr->size) { + struct mrvlietypesheader *header = (struct mrvlietypesheader *)(response_buf + cmd_len); + switch(header->type) { + struct mrvlietypes_failurecount *failcount; + case TLV_TYPE_FAILCOUNT: + failcount = (struct mrvlietypes_failurecount *)(response_buf + cmd_len); + pos += snprintf(buf+pos, len-pos, "%d %d %d\n", + failcount->failvalue, + failcount->Failfreq, + (event->events & 0x0004)?1:0); + default: + cmd_len += sizeof(struct mrvlietypes_failurecount); + break; + } + } + + kfree(response_buf); + res = simple_read_from_buffer(userbuf, count, ppos, buf, pos); + free_page(addr); + return res; +} + +static ssize_t libertas_failcount_write(struct file *file, + const char __user *userbuf, + size_t count, loff_t *ppos) +{ + wlan_private *priv = file->private_data; + wlan_adapter *adapter = priv->adapter; + ssize_t res, buf_size; + int value, freq, subscribed, cmd_len; + struct cmd_ctrl_node *pcmdnode; + struct cmd_ds_command *pcmdptr; + struct cmd_ds_802_11_subscribe_event *event; + struct mrvlietypes_failurecount *failcount; + void *response_buf; + u16 event_bitmap; + u8 *ptr; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + + buf_size = min(count, len - 1); + if (copy_from_user(buf, userbuf, buf_size)) { + res = -EFAULT; + goto out_unlock; + } + res = sscanf(buf, "%d %d %d", &value, &freq, &subscribed); + if (res != 3) { + res = -EFAULT; + goto out_unlock; + } + + event_bitmap = libertas_get_events_bitmap(priv); + + res = libertas_event_initcmd(priv, &response_buf, &pcmdnode, &pcmdptr); + if (res < 0) + goto out_unlock; + + event = &pcmdptr->params.subscribe_event; + event->action = cmd_act_set; + pcmdptr->size = cpu_to_le16(S_DS_GEN + + sizeof(struct cmd_ds_802_11_subscribe_event) + + sizeof(struct mrvlietypes_failurecount)); + cmd_len = S_DS_GEN + sizeof(struct cmd_ds_802_11_subscribe_event); + ptr = (u8*) pcmdptr+cmd_len; + failcount = (struct mrvlietypes_failurecount *)(ptr); + failcount->header.type = cpu_to_le16(TLV_TYPE_FAILCOUNT); + failcount->header.len = 2; + failcount->failvalue = cpu_to_le16(value); + failcount->Failfreq = cpu_to_le16(freq); + event_bitmap |= subscribed ? 0x0004 : 0x0; + event->events = event_bitmap; + + libertas_queue_cmd(adapter, pcmdnode, 1); + wake_up_interruptible(&priv->mainthread.waitq); + + /* Sleep until response is generated by FW */ + wait_event_interruptible(pcmdnode->cmdwait_q, + pcmdnode->cmdwaitqwoken); + + pcmdptr = (struct cmd_ds_command *)response_buf; + + if (pcmdptr->result) { + lbs_pr_err("%s: fail, result=%d\n", __FUNCTION__, + pcmdptr->result); + kfree(response_buf); + free_page(addr); + return 0; + } + + if (pcmdptr->command != cmd_ret_802_11_subscribe_event) { + lbs_pr_err("command response incorrect!\n"); + kfree(response_buf); + free_page(addr); + return 0; + } + + res = count; +out_unlock: + free_page(addr); + return res; +} + +static ssize_t libertas_bcnmiss_read(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + wlan_private *priv = file->private_data; + wlan_adapter *adapter = priv->adapter; + struct cmd_ctrl_node *pcmdnode; + struct cmd_ds_command *pcmdptr; + struct cmd_ds_802_11_subscribe_event *event; + void *response_buf; + int res, cmd_len; + ssize_t pos = 0; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + + res = libertas_event_initcmd(priv, &response_buf, &pcmdnode, &pcmdptr); + if (res < 0) { + free_page(addr); + return res; + } + + event = &pcmdptr->params.subscribe_event; + event->action = cmd_act_get; + pcmdptr->size = + cpu_to_le16(sizeof(struct cmd_ds_802_11_subscribe_event) + S_DS_GEN); + libertas_queue_cmd(adapter, pcmdnode, 1); + wake_up_interruptible(&priv->mainthread.waitq); + + /* Sleep until response is generated by FW */ + wait_event_interruptible(pcmdnode->cmdwait_q, + pcmdnode->cmdwaitqwoken); + + pcmdptr = response_buf; + + if (pcmdptr->result) { + lbs_pr_err("%s: fail, result=%d\n", __FUNCTION__, + pcmdptr->result); + free_page(addr); + kfree(response_buf); + return 0; + } + + if (pcmdptr->command != cmd_ret_802_11_subscribe_event) { + lbs_pr_err("command response incorrect!\n"); + free_page(addr); + kfree(response_buf); + return 0; + } + + cmd_len = S_DS_GEN + sizeof(struct cmd_ds_802_11_subscribe_event); + event = (struct cmd_ds_802_11_subscribe_event *)(response_buf + S_DS_GEN); + while (cmd_len < pcmdptr->size) { + struct mrvlietypesheader *header = (struct mrvlietypesheader *)(response_buf + cmd_len); + switch(header->type) { + struct mrvlietypes_beaconsmissed *bcnmiss; + case TLV_TYPE_BCNMISS: + bcnmiss = (struct mrvlietypes_beaconsmissed *)(response_buf + cmd_len); + pos += snprintf(buf+pos, len-pos, "%d N/A %d\n", + bcnmiss->beaconmissed, + (event->events & 0x0008)?1:0); + default: + cmd_len += sizeof(struct mrvlietypes_beaconsmissed); + break; + } + } + + kfree(response_buf); + + res = simple_read_from_buffer(userbuf, count, ppos, buf, pos); + free_page(addr); + return res; +} + +static ssize_t libertas_bcnmiss_write(struct file *file, + const char __user *userbuf, + size_t count, loff_t *ppos) +{ + wlan_private *priv = file->private_data; + wlan_adapter *adapter = priv->adapter; + ssize_t res, buf_size; + int value, freq, subscribed, cmd_len; + struct cmd_ctrl_node *pcmdnode; + struct cmd_ds_command *pcmdptr; + struct cmd_ds_802_11_subscribe_event *event; + struct mrvlietypes_beaconsmissed *bcnmiss; + void *response_buf; + u16 event_bitmap; + u8 *ptr; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + + buf_size = min(count, len - 1); + if (copy_from_user(buf, userbuf, buf_size)) { + res = -EFAULT; + goto out_unlock; + } + res = sscanf(buf, "%d %d %d", &value, &freq, &subscribed); + if (res != 3) { + res = -EFAULT; + goto out_unlock; + } + + event_bitmap = libertas_get_events_bitmap(priv); + + res = libertas_event_initcmd(priv, &response_buf, &pcmdnode, &pcmdptr); + if (res < 0) + goto out_unlock; + + event = &pcmdptr->params.subscribe_event; + event->action = cmd_act_set; + pcmdptr->size = cpu_to_le16(S_DS_GEN + + sizeof(struct cmd_ds_802_11_subscribe_event) + + sizeof(struct mrvlietypes_beaconsmissed)); + cmd_len = S_DS_GEN + sizeof(struct cmd_ds_802_11_subscribe_event); + ptr = (u8*) pcmdptr+cmd_len; + bcnmiss = (struct mrvlietypes_beaconsmissed *)(ptr); + bcnmiss->header.type = cpu_to_le16(TLV_TYPE_BCNMISS); + bcnmiss->header.len = 2; + bcnmiss->beaconmissed = cpu_to_le16(value); + event_bitmap |= subscribed ? 0x0008 : 0x0; + event->events = event_bitmap; + + libertas_queue_cmd(adapter, pcmdnode, 1); + wake_up_interruptible(&priv->mainthread.waitq); + + /* Sleep until response is generated by FW */ + wait_event_interruptible(pcmdnode->cmdwait_q, + pcmdnode->cmdwaitqwoken); + + pcmdptr = response_buf; + + if (pcmdptr->result) { + lbs_pr_err("%s: fail, result=%d\n", __FUNCTION__, + pcmdptr->result); + kfree(response_buf); + free_page(addr); + return 0; + } + + if (pcmdptr->command != cmd_ret_802_11_subscribe_event) { + lbs_pr_err("command response incorrect!\n"); + free_page(addr); + kfree(response_buf); + return 0; + } + + res = count; +out_unlock: + free_page(addr); + return res; +} + +static ssize_t libertas_highrssi_read(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + wlan_private *priv = file->private_data; + wlan_adapter *adapter = priv->adapter; + struct cmd_ctrl_node *pcmdnode; + struct cmd_ds_command *pcmdptr; + struct cmd_ds_802_11_subscribe_event *event; + void *response_buf; + int res, cmd_len; + ssize_t pos = 0; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + + res = libertas_event_initcmd(priv, &response_buf, &pcmdnode, &pcmdptr); + if (res < 0) { + free_page(addr); + return res; + } + + event = &pcmdptr->params.subscribe_event; + event->action = cmd_act_get; + pcmdptr->size = + cpu_to_le16(sizeof(struct cmd_ds_802_11_subscribe_event) + S_DS_GEN); + libertas_queue_cmd(adapter, pcmdnode, 1); + wake_up_interruptible(&priv->mainthread.waitq); + + /* Sleep until response is generated by FW */ + wait_event_interruptible(pcmdnode->cmdwait_q, + pcmdnode->cmdwaitqwoken); + + pcmdptr = response_buf; + + if (pcmdptr->result) { + lbs_pr_err("%s: fail, result=%d\n", __FUNCTION__, + pcmdptr->result); + kfree(response_buf); + free_page(addr); + return 0; + } + + if (pcmdptr->command != cmd_ret_802_11_subscribe_event) { + lbs_pr_err("command response incorrect!\n"); + kfree(response_buf); + free_page(addr); + return 0; + } + + cmd_len = S_DS_GEN + sizeof(struct cmd_ds_802_11_subscribe_event); + event = (struct cmd_ds_802_11_subscribe_event *)(response_buf + S_DS_GEN); + while (cmd_len < pcmdptr->size) { + struct mrvlietypesheader *header = (struct mrvlietypesheader *)(response_buf + cmd_len); + switch(header->type) { + struct mrvlietypes_rssithreshold *Highrssi; + case TLV_TYPE_RSSI_HIGH: + Highrssi = (struct mrvlietypes_rssithreshold *)(response_buf + cmd_len); + pos += snprintf(buf+pos, len-pos, "%d %d %d\n", + Highrssi->rssivalue, + Highrssi->rssifreq, + (event->events & 0x0010)?1:0); + default: + cmd_len += sizeof(struct mrvlietypes_snrthreshold); + break; + } + } + + kfree(response_buf); + + res = simple_read_from_buffer(userbuf, count, ppos, buf, pos); + free_page(addr); + return res; +} + +static ssize_t libertas_highrssi_write(struct file *file, + const char __user *userbuf, + size_t count, loff_t *ppos) +{ + wlan_private *priv = file->private_data; + wlan_adapter *adapter = priv->adapter; + ssize_t res, buf_size; + int value, freq, subscribed, cmd_len; + struct cmd_ctrl_node *pcmdnode; + struct cmd_ds_command *pcmdptr; + struct cmd_ds_802_11_subscribe_event *event; + struct mrvlietypes_rssithreshold *rssi_threshold; + void *response_buf; + u16 event_bitmap; + u8 *ptr; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + + buf_size = min(count, len - 1); + if (copy_from_user(buf, userbuf, buf_size)) { + res = -EFAULT; + goto out_unlock; + } + res = sscanf(buf, "%d %d %d", &value, &freq, &subscribed); + if (res != 3) { + res = -EFAULT; + goto out_unlock; + } + + event_bitmap = libertas_get_events_bitmap(priv); + + res = libertas_event_initcmd(priv, &response_buf, &pcmdnode, &pcmdptr); + if (res < 0) + goto out_unlock; + + event = &pcmdptr->params.subscribe_event; + event->action = cmd_act_set; + pcmdptr->size = cpu_to_le16(S_DS_GEN + + sizeof(struct cmd_ds_802_11_subscribe_event) + + sizeof(struct mrvlietypes_rssithreshold)); + cmd_len = S_DS_GEN + sizeof(struct cmd_ds_802_11_subscribe_event); + ptr = (u8*) pcmdptr+cmd_len; + rssi_threshold = (struct mrvlietypes_rssithreshold *)(ptr); + rssi_threshold->header.type = cpu_to_le16(TLV_TYPE_RSSI_HIGH); + rssi_threshold->header.len = 2; + rssi_threshold->rssivalue = cpu_to_le16(value); + rssi_threshold->rssifreq = cpu_to_le16(freq); + event_bitmap |= subscribed ? 0x0010 : 0x0; + event->events = event_bitmap; + + libertas_queue_cmd(adapter, pcmdnode, 1); + wake_up_interruptible(&priv->mainthread.waitq); + + /* Sleep until response is generated by FW */ + wait_event_interruptible(pcmdnode->cmdwait_q, + pcmdnode->cmdwaitqwoken); + + pcmdptr = response_buf; + + if (pcmdptr->result) { + lbs_pr_err("%s: fail, result=%d\n", __FUNCTION__, + pcmdptr->result); + kfree(response_buf); + return 0; + } + + if (pcmdptr->command != cmd_ret_802_11_subscribe_event) { + lbs_pr_err("command response incorrect!\n"); + kfree(response_buf); + return 0; + } + + res = count; +out_unlock: + free_page(addr); + return res; +} + +static ssize_t libertas_highsnr_read(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + wlan_private *priv = file->private_data; + wlan_adapter *adapter = priv->adapter; + struct cmd_ctrl_node *pcmdnode; + struct cmd_ds_command *pcmdptr; + struct cmd_ds_802_11_subscribe_event *event; + void *response_buf; + int res, cmd_len; + ssize_t pos = 0; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + + res = libertas_event_initcmd(priv, &response_buf, &pcmdnode, &pcmdptr); + if (res < 0) { + free_page(addr); + return res; + } + + event = &pcmdptr->params.subscribe_event; + event->action = cmd_act_get; + pcmdptr->size = + cpu_to_le16(sizeof(struct cmd_ds_802_11_subscribe_event) + S_DS_GEN); + libertas_queue_cmd(adapter, pcmdnode, 1); + wake_up_interruptible(&priv->mainthread.waitq); + + /* Sleep until response is generated by FW */ + wait_event_interruptible(pcmdnode->cmdwait_q, + pcmdnode->cmdwaitqwoken); + + pcmdptr = response_buf; + + if (pcmdptr->result) { + lbs_pr_err("%s: fail, result=%d\n", __FUNCTION__, + pcmdptr->result); + kfree(response_buf); + free_page(addr); + return 0; + } + + if (pcmdptr->command != cmd_ret_802_11_subscribe_event) { + lbs_pr_err("command response incorrect!\n"); + kfree(response_buf); + free_page(addr); + return 0; + } + + cmd_len = S_DS_GEN + sizeof(struct cmd_ds_802_11_subscribe_event); + event = (struct cmd_ds_802_11_subscribe_event *)(response_buf + S_DS_GEN); + while (cmd_len < pcmdptr->size) { + struct mrvlietypesheader *header = (struct mrvlietypesheader *)(response_buf + cmd_len); + switch(header->type) { + struct mrvlietypes_snrthreshold *HighSnr; + case TLV_TYPE_SNR_HIGH: + HighSnr = (struct mrvlietypes_snrthreshold *)(response_buf + cmd_len); + pos += snprintf(buf+pos, len-pos, "%d %d %d\n", + HighSnr->snrvalue, + HighSnr->snrfreq, + (event->events & 0x0020)?1:0); + default: + cmd_len += sizeof(struct mrvlietypes_snrthreshold); + break; + } + } + + kfree(response_buf); + + res = simple_read_from_buffer(userbuf, count, ppos, buf, pos); + free_page(addr); + return res; +} + +static ssize_t libertas_highsnr_write(struct file *file, + const char __user *userbuf, + size_t count, loff_t *ppos) +{ + wlan_private *priv = file->private_data; + wlan_adapter *adapter = priv->adapter; + ssize_t res, buf_size; + int value, freq, subscribed, cmd_len; + struct cmd_ctrl_node *pcmdnode; + struct cmd_ds_command *pcmdptr; + struct cmd_ds_802_11_subscribe_event *event; + struct mrvlietypes_snrthreshold *snr_threshold; + void *response_buf; + u16 event_bitmap; + u8 *ptr; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + + buf_size = min(count, len - 1); + if (copy_from_user(buf, userbuf, buf_size)) { + res = -EFAULT; + goto out_unlock; + } + res = sscanf(buf, "%d %d %d", &value, &freq, &subscribed); + if (res != 3) { + res = -EFAULT; + goto out_unlock; + } + + event_bitmap = libertas_get_events_bitmap(priv); + + res = libertas_event_initcmd(priv, &response_buf, &pcmdnode, &pcmdptr); + if (res < 0) + goto out_unlock; + + event = &pcmdptr->params.subscribe_event; + event->action = cmd_act_set; + pcmdptr->size = cpu_to_le16(S_DS_GEN + + sizeof(struct cmd_ds_802_11_subscribe_event) + + sizeof(struct mrvlietypes_snrthreshold)); + cmd_len = S_DS_GEN + sizeof(struct cmd_ds_802_11_subscribe_event); + ptr = (u8*) pcmdptr+cmd_len; + snr_threshold = (struct mrvlietypes_snrthreshold *)(ptr); + snr_threshold->header.type = cpu_to_le16(TLV_TYPE_SNR_HIGH); + snr_threshold->header.len = 2; + snr_threshold->snrvalue = cpu_to_le16(value); + snr_threshold->snrfreq = cpu_to_le16(freq); + event_bitmap |= subscribed ? 0x0020 : 0x0; + event->events = event_bitmap; + + libertas_queue_cmd(adapter, pcmdnode, 1); + wake_up_interruptible(&priv->mainthread.waitq); + + /* Sleep until response is generated by FW */ + wait_event_interruptible(pcmdnode->cmdwait_q, + pcmdnode->cmdwaitqwoken); + + pcmdptr = response_buf; + + if (pcmdptr->result) { + lbs_pr_err("%s: fail, result=%d\n", __FUNCTION__, + pcmdptr->result); + kfree(response_buf); + free_page(addr); + return 0; + } + + if (pcmdptr->command != cmd_ret_802_11_subscribe_event) { + lbs_pr_err("command response incorrect!\n"); + kfree(response_buf); + free_page(addr); + return 0; + } + + res = count; +out_unlock: + free_page(addr); + return res; +} + +static ssize_t libertas_rdmac_read(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + wlan_private *priv = file->private_data; + wlan_adapter *adapter = priv->adapter; + struct wlan_offset_value offval; + ssize_t pos = 0; + int ret; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + + offval.offset = priv->mac_offset; + offval.value = 0; + + ret = libertas_prepare_and_send_command(priv, + cmd_mac_reg_access, 0, + cmd_option_waitforrsp, 0, &offval); + mdelay(10); + pos += snprintf(buf+pos, len-pos, "MAC[0x%x] = 0x%08x\n", + priv->mac_offset, adapter->offsetvalue.value); + + ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos); + free_page(addr); + return ret; +} + +static ssize_t libertas_rdmac_write(struct file *file, + const char __user *userbuf, + size_t count, loff_t *ppos) +{ + wlan_private *priv = file->private_data; + ssize_t res, buf_size; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + + buf_size = min(count, len - 1); + if (copy_from_user(buf, userbuf, buf_size)) { + res = -EFAULT; + goto out_unlock; + } + priv->mac_offset = simple_strtoul((char *)buf, NULL, 16); + res = count; +out_unlock: + free_page(addr); + return res; +} + +static ssize_t libertas_wrmac_write(struct file *file, + const char __user *userbuf, + size_t count, loff_t *ppos) +{ + + wlan_private *priv = file->private_data; + ssize_t res, buf_size; + u32 offset, value; + struct wlan_offset_value offval; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + + buf_size = min(count, len - 1); + if (copy_from_user(buf, userbuf, buf_size)) { + res = -EFAULT; + goto out_unlock; + } + res = sscanf(buf, "%x %x", &offset, &value); + if (res != 2) { + res = -EFAULT; + goto out_unlock; + } + + offval.offset = offset; + offval.value = value; + res = libertas_prepare_and_send_command(priv, + cmd_mac_reg_access, 1, + cmd_option_waitforrsp, 0, &offval); + mdelay(10); + + res = count; +out_unlock: + free_page(addr); + return res; +} + +static ssize_t libertas_rdbbp_read(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + wlan_private *priv = file->private_data; + wlan_adapter *adapter = priv->adapter; + struct wlan_offset_value offval; + ssize_t pos = 0; + int ret; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + + offval.offset = priv->bbp_offset; + offval.value = 0; + + ret = libertas_prepare_and_send_command(priv, + cmd_bbp_reg_access, 0, + cmd_option_waitforrsp, 0, &offval); + mdelay(10); + pos += snprintf(buf+pos, len-pos, "BBP[0x%x] = 0x%08x\n", + priv->bbp_offset, adapter->offsetvalue.value); + + ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos); + free_page(addr); + + return ret; +} + +static ssize_t libertas_rdbbp_write(struct file *file, + const char __user *userbuf, + size_t count, loff_t *ppos) +{ + wlan_private *priv = file->private_data; + ssize_t res, buf_size; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + + buf_size = min(count, len - 1); + if (copy_from_user(buf, userbuf, buf_size)) { + res = -EFAULT; + goto out_unlock; + } + priv->bbp_offset = simple_strtoul((char *)buf, NULL, 16); + res = count; +out_unlock: + free_page(addr); + return res; +} + +static ssize_t libertas_wrbbp_write(struct file *file, + const char __user *userbuf, + size_t count, loff_t *ppos) +{ + + wlan_private *priv = file->private_data; + ssize_t res, buf_size; + u32 offset, value; + struct wlan_offset_value offval; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + + buf_size = min(count, len - 1); + if (copy_from_user(buf, userbuf, buf_size)) { + res = -EFAULT; + goto out_unlock; + } + res = sscanf(buf, "%x %x", &offset, &value); + if (res != 2) { + res = -EFAULT; + goto out_unlock; + } + + offval.offset = offset; + offval.value = value; + res = libertas_prepare_and_send_command(priv, + cmd_bbp_reg_access, 1, + cmd_option_waitforrsp, 0, &offval); + mdelay(10); + + res = count; +out_unlock: + free_page(addr); + return res; +} + +static ssize_t libertas_rdrf_read(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + wlan_private *priv = file->private_data; + wlan_adapter *adapter = priv->adapter; + struct wlan_offset_value offval; + ssize_t pos = 0; + int ret; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + + offval.offset = priv->rf_offset; + offval.value = 0; + + ret = libertas_prepare_and_send_command(priv, + cmd_rf_reg_access, 0, + cmd_option_waitforrsp, 0, &offval); + mdelay(10); + pos += snprintf(buf+pos, len-pos, "RF[0x%x] = 0x%08x\n", + priv->rf_offset, adapter->offsetvalue.value); + + ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos); + free_page(addr); + + return ret; +} + +static ssize_t libertas_rdrf_write(struct file *file, + const char __user *userbuf, + size_t count, loff_t *ppos) +{ + wlan_private *priv = file->private_data; + ssize_t res, buf_size; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + + buf_size = min(count, len - 1); + if (copy_from_user(buf, userbuf, buf_size)) { + res = -EFAULT; + goto out_unlock; + } + priv->rf_offset = simple_strtoul((char *)buf, NULL, 16); + res = count; +out_unlock: + free_page(addr); + return res; +} + +static ssize_t libertas_wrrf_write(struct file *file, + const char __user *userbuf, + size_t count, loff_t *ppos) +{ + + wlan_private *priv = file->private_data; + ssize_t res, buf_size; + u32 offset, value; + struct wlan_offset_value offval; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + + buf_size = min(count, len - 1); + if (copy_from_user(buf, userbuf, buf_size)) { + res = -EFAULT; + goto out_unlock; + } + res = sscanf(buf, "%x %x", &offset, &value); + if (res != 2) { + res = -EFAULT; + goto out_unlock; + } + + offval.offset = offset; + offval.value = value; + res = libertas_prepare_and_send_command(priv, + cmd_rf_reg_access, 1, + cmd_option_waitforrsp, 0, &offval); + mdelay(10); + + res = count; +out_unlock: + free_page(addr); + return res; +} + +#define FOPS(fread, fwrite) { \ + .owner = THIS_MODULE, \ + .open = open_file_generic, \ + .read = (fread), \ + .write = (fwrite), \ +} + +struct libertas_debugfs_files { + char *name; + int perm; + struct file_operations fops; +}; + +struct libertas_debugfs_files debugfs_files[] = { + { "info", 0444, FOPS(libertas_dev_info, write_file_dummy), }, + { "getscantable", 0444, FOPS(libertas_getscantable, + write_file_dummy), }, + { "sleepparams", 0644, FOPS(libertas_sleepparams_read, + libertas_sleepparams_write), }, + { "extscan", 0600, FOPS(NULL, libertas_extscan), }, + { "setuserscan", 0600, FOPS(NULL, libertas_setuserscan), }, +}; + +struct libertas_debugfs_files debugfs_events_files[] = { + {"low_rssi", 0644, FOPS(libertas_lowrssi_read, + libertas_lowrssi_write), }, + {"low_snr", 0644, FOPS(libertas_lowsnr_read, + libertas_lowsnr_write), }, + {"failure_count", 0644, FOPS(libertas_failcount_read, + libertas_failcount_write), }, + {"beacon_missed", 0644, FOPS(libertas_bcnmiss_read, + libertas_bcnmiss_write), }, + {"high_rssi", 0644, FOPS(libertas_highrssi_read, + libertas_highrssi_write), }, + {"high_snr", 0644, FOPS(libertas_highsnr_read, + libertas_highsnr_write), }, +}; + +struct libertas_debugfs_files debugfs_regs_files[] = { + {"rdmac", 0644, FOPS(libertas_rdmac_read, libertas_rdmac_write), }, + {"wrmac", 0600, FOPS(NULL, libertas_wrmac_write), }, + {"rdbbp", 0644, FOPS(libertas_rdbbp_read, libertas_rdbbp_write), }, + {"wrbbp", 0600, FOPS(NULL, libertas_wrbbp_write), }, + {"rdrf", 0644, FOPS(libertas_rdrf_read, libertas_rdrf_write), }, + {"wrrf", 0600, FOPS(NULL, libertas_wrrf_write), }, +}; + +void libertas_debugfs_init(void) +{ + if (!libertas_dir) + libertas_dir = debugfs_create_dir("libertas_wireless", NULL); + + return; +} + +void libertas_debugfs_remove(void) +{ + if (libertas_dir) + debugfs_remove(libertas_dir); + return; +} + +void libertas_debugfs_init_one(wlan_private *priv, struct net_device *dev) +{ + int i; + struct libertas_debugfs_files *files; + if (!libertas_dir) + goto exit; + + priv->debugfs_dir = debugfs_create_dir(dev->name, libertas_dir); + if (!priv->debugfs_dir) + goto exit; + + for (i=0; i<ARRAY_SIZE(debugfs_files); i++) { + files = &debugfs_files[i]; + priv->debugfs_files[i] = debugfs_create_file(files->name, + files->perm, + priv->debugfs_dir, + priv, + &files->fops); + } + + priv->events_dir = debugfs_create_dir("subscribed_events", priv->debugfs_dir); + if (!priv->events_dir) + goto exit; + + for (i=0; i<ARRAY_SIZE(debugfs_events_files); i++) { + files = &debugfs_events_files[i]; + priv->debugfs_events_files[i] = debugfs_create_file(files->name, + files->perm, + priv->events_dir, + priv, + &files->fops); + } + + priv->regs_dir = debugfs_create_dir("registers", priv->debugfs_dir); + if (!priv->regs_dir) + goto exit; + + for (i=0; i<ARRAY_SIZE(debugfs_regs_files); i++) { + files = &debugfs_regs_files[i]; + priv->debugfs_regs_files[i] = debugfs_create_file(files->name, + files->perm, + priv->regs_dir, + priv, + &files->fops); + } + +#ifdef PROC_DEBUG + libertas_debug_init(priv, dev); +#endif +exit: + return; +} + +void libertas_debugfs_remove_one(wlan_private *priv) +{ + int i; + + for(i=0; i<ARRAY_SIZE(debugfs_regs_files); i++) + debugfs_remove(priv->debugfs_regs_files[i]); + + debugfs_remove(priv->regs_dir); + + for(i=0; i<ARRAY_SIZE(debugfs_files); i++) + debugfs_remove(priv->debugfs_events_files[i]); + + debugfs_remove(priv->events_dir); +#ifdef PROC_DEBUG + debugfs_remove(priv->debugfs_debug); +#endif + for(i=0; i<ARRAY_SIZE(debugfs_files); i++) + debugfs_remove(priv->debugfs_files[i]); +} + +/* debug entry */ + +#define item_size(n) (FIELD_SIZEOF(wlan_adapter, n)) +#define item_addr(n) (offsetof(wlan_adapter, n)) + +struct debug_data { + char name[32]; + u32 size; + u32 addr; +}; + +/* To debug any member of wlan_adapter, simply add one line here. + */ +static struct debug_data items[] = { + {"intcounter", item_size(intcounter), item_addr(intcounter)}, + {"psmode", item_size(psmode), item_addr(psmode)}, + {"psstate", item_size(psstate), item_addr(psstate)}, +}; + +static int num_of_items = ARRAY_SIZE(items); + +/** + * @brief proc read function + * + * @param page pointer to buffer + * @param s read data starting position + * @param off offset + * @param cnt counter + * @param eof end of file flag + * @param data data to output + * @return number of output data + */ +static ssize_t wlan_debugfs_read(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + int val = 0; + size_t pos = 0; + ssize_t res; + char *p; + int i; + struct debug_data *d; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + + p = buf; + + d = (struct debug_data *)file->private_data; + + for (i = 0; i < num_of_items; i++) { + if (d[i].size == 1) + val = *((u8 *) d[i].addr); + else if (d[i].size == 2) + val = *((u16 *) d[i].addr); + else if (d[i].size == 4) + val = *((u32 *) d[i].addr); + + pos += sprintf(p + pos, "%s=%d\n", d[i].name, val); + } + + res = simple_read_from_buffer(userbuf, count, ppos, p, pos); + + free_page(addr); + return res; +} + +/** + * @brief proc write function + * + * @param f file pointer + * @param buf pointer to data buffer + * @param cnt data number to write + * @param data data to write + * @return number of data + */ +static int wlan_debugfs_write(struct file *f, const char __user *buf, + size_t cnt, loff_t *ppos) +{ + int r, i; + char *pdata; + char *p; + char *p0; + char *p1; + char *p2; + struct debug_data *d = (struct debug_data *)f->private_data; + + pdata = (char *)kmalloc(cnt, GFP_KERNEL); + if (pdata == NULL) + return 0; + + if (copy_from_user(pdata, buf, cnt)) { + lbs_pr_debug(1, "Copy from user failed\n"); + kfree(pdata); + return 0; + } + + p0 = pdata; + for (i = 0; i < num_of_items; i++) { + do { + p = strstr(p0, d[i].name); + if (p == NULL) + break; + p1 = strchr(p, '\n'); + if (p1 == NULL) + break; + p0 = p1++; + p2 = strchr(p, '='); + if (!p2) + break; + p2++; + r = simple_strtoul(p2, NULL, 0); + if (d[i].size == 1) + *((u8 *) d[i].addr) = (u8) r; + else if (d[i].size == 2) + *((u16 *) d[i].addr) = (u16) r; + else if (d[i].size == 4) + *((u32 *) d[i].addr) = (u32) r; + break; + } while (1); + } + kfree(pdata); + + return cnt; +} + +static struct file_operations libertas_debug_fops = { + .owner = THIS_MODULE, + .open = open_file_generic, + .write = wlan_debugfs_write, + .read = wlan_debugfs_read, +}; + +/** + * @brief create debug proc file + * + * @param priv pointer wlan_private + * @param dev pointer net_device + * @return N/A + */ +void libertas_debug_init(wlan_private * priv, struct net_device *dev) +{ + int i; + + if (!priv->debugfs_dir) + return; + + for (i = 0; i < num_of_items; i++) + items[i].addr += (u32) priv->adapter; + + priv->debugfs_debug = debugfs_create_file("debug", 0644, + priv->debugfs_dir, &items[0], + &libertas_debug_fops); +} + +/** + * @brief remove proc file + * + * @param priv pointer wlan_private + * @return N/A + */ +void libertas_debug_remove(wlan_private * priv) +{ + debugfs_remove(priv->debugfs_debug); +} diff --git a/drivers/net/wireless/libertas/debugfs.h b/drivers/net/wireless/libertas/debugfs.h new file mode 100644 index 00000000000..880a11b95d2 --- /dev/null +++ b/drivers/net/wireless/libertas/debugfs.h @@ -0,0 +1,6 @@ +void libertas_debugfs_init(void); +void libertas_debugfs_remove(void); + +void libertas_debugfs_init_one(wlan_private *priv, struct net_device *dev); +void libertas_debugfs_remove_one(wlan_private *priv); + diff --git a/drivers/net/wireless/libertas/decl.h b/drivers/net/wireless/libertas/decl.h new file mode 100644 index 00000000000..606bdd002be --- /dev/null +++ b/drivers/net/wireless/libertas/decl.h @@ -0,0 +1,83 @@ +/** + * This file contains declaration referring to + * functions defined in other source files + */ + +#ifndef _WLAN_DECL_H_ +#define _WLAN_DECL_H_ + +#include "defs.h" + +/** Function Prototype Declaration */ +struct wlan_private; +struct sk_buff; +struct net_device; + +extern char *libertas_fw_name; + +void libertas_free_adapter(wlan_private * priv); +int libertas_set_mac_packet_filter(wlan_private * priv); + +int libertas_send_null_packet(wlan_private * priv, u8 pwr_mgmt); +void libertas_send_tx_feedback(wlan_private * priv); +u8 libertas_check_last_packet_indication(wlan_private * priv); + +int libertas_free_cmd_buffer(wlan_private * priv); +struct cmd_ctrl_node; +struct cmd_ctrl_node *libertas_get_free_cmd_ctrl_node(wlan_private * priv); + +void libertas_set_cmd_ctrl_node(wlan_private * priv, + struct cmd_ctrl_node *ptempnode, + u32 cmd_oid, u16 wait_option, void *pdata_buf); + +int libertas_prepare_and_send_command(wlan_private * priv, + u16 cmd_no, + u16 cmd_action, + u16 wait_option, u32 cmd_oid, void *pdata_buf); + +void libertas_queue_cmd(wlan_adapter * adapter, struct cmd_ctrl_node *cmdnode, u8 addtail); + +int libertas_allocate_cmd_buffer(wlan_private * priv); +int libertas_execute_next_command(wlan_private * priv); +int libertas_process_event(wlan_private * priv); +void libertas_interrupt(struct net_device *); +int libertas_set_radio_control(wlan_private * priv); +u32 libertas_index_to_data_rate(u8 index); +u8 libertas_data_rate_to_index(u32 rate); +void libertas_get_fwversion(wlan_adapter * adapter, char *fwversion, int maxlen); + +int libertas_upload_rx_packet(wlan_private * priv, struct sk_buff *skb); + +/** The proc fs interface */ +int libertas_process_rx_command(wlan_private * priv); +int libertas_process_tx(wlan_private * priv, struct sk_buff *skb); +void libertas_cleanup_and_insert_cmd(wlan_private * priv, + struct cmd_ctrl_node *ptempcmd); +void __libertas_cleanup_and_insert_cmd(wlan_private * priv, + struct cmd_ctrl_node *ptempcmd); + +int libertas_set_regiontable(wlan_private * priv, u8 region, u8 band); + +int libertas_process_rxed_packet(wlan_private * priv, struct sk_buff *); + +void libertas_ps_sleep(wlan_private * priv, int wait_option); +void libertas_ps_confirm_sleep(wlan_private * priv, u16 psmode); +void libertas_ps_wakeup(wlan_private * priv, int wait_option); + +void libertas_tx_runqueue(wlan_private *priv); + +extern struct chan_freq_power *libertas_find_cfp_by_band_and_channel( + wlan_adapter * adapter, u8 band, u16 channel); + +extern void libertas_mac_event_disconnected(wlan_private * priv); + +void libertas_send_iwevcustom_event(wlan_private * priv, s8 * str); + +int reset_device(wlan_private *priv); +/* main.c */ +extern struct chan_freq_power *libertas_get_region_cfp_table(u8 region, u8 band, + int *cfp_no); +wlan_private *wlan_add_card(void *card); +int wlan_remove_card(void *card); + +#endif /* _WLAN_DECL_H_ */ diff --git a/drivers/net/wireless/libertas/defs.h b/drivers/net/wireless/libertas/defs.h new file mode 100644 index 00000000000..fb1478c1b87 --- /dev/null +++ b/drivers/net/wireless/libertas/defs.h @@ -0,0 +1,369 @@ +/** + * This header file contains global constant/enum definitions, + * global variable declaration. + */ +#ifndef _WLAN_DEFS_H_ +#define _WLAN_DEFS_H_ + +#include <linux/spinlock.h> + +extern unsigned int libertas_debug; + +#define DRV_NAME "usb8xxx" + +#define lbs_pr_info(format, args...) \ + printk(KERN_INFO DRV_NAME": " format, ## args) +#define lbs_pr_err(format, args...) \ + printk(KERN_ERR DRV_NAME": " format, ## args) +#define lbs_pr_alert(format, args...) \ + printk(KERN_ALERT DRV_NAME": " format, ## args) + +#ifdef DEBUG +#define lbs_pr_debug(level, format, args...) \ + do { if (libertas_debug >= level) \ + printk(KERN_INFO DRV_NAME": " format, ##args); } while (0) +#define lbs_dev_dbg(level, device, format, args...) \ + lbs_pr_debug(level, "%s: " format, \ + (device)->bus_id , ## args) + +static inline void lbs_dbg_hex(char *prompt, u8 * buf, int len) +{ + int i = 0; + + if (!libertas_debug) + return; + + printk(KERN_DEBUG "%s: ", prompt); + for (i = 1; i <= len; i++) { + printk(KERN_DEBUG "%02x ", (u8) * buf); + buf++; + } + printk("\n"); +} +#else +#define lbs_pr_debug(level, format, args...) do {} while (0) +#define lbs_dev_dbg(level, device, format, args...) do {} while (0) +#define lbs_dbg_hex(x,y,z) do {} while (0) +#endif + +#define ENTER() lbs_pr_debug(1, "Enter: %s, %s:%i\n", \ + __FUNCTION__, __FILE__, __LINE__) +#define LEAVE() lbs_pr_debug(1, "Leave: %s, %s:%i\n", \ + __FUNCTION__, __FILE__, __LINE__) + +/** Buffer Constants */ + +/* The size of SQ memory PPA, DPA are 8 DWORDs, that keep the physical +* addresses of TxPD buffers. Station has only 8 TxPD available, Whereas +* driver has more local TxPDs. Each TxPD on the host memory is associated +* with a Tx control node. The driver maintains 8 RxPD descriptors for +* station firmware to store Rx packet information. +* +* Current version of MAC has a 32x6 multicast address buffer. +* +* 802.11b can have up to 14 channels, the driver keeps the +* BSSID(MAC address) of each APs or Ad hoc stations it has sensed. +*/ + +#define MRVDRV_MAX_MULTICAST_LIST_SIZE 32 +#define MRVDRV_NUM_OF_CMD_BUFFER 10 +#define MRVDRV_SIZE_OF_CMD_BUFFER (2 * 1024) +#define MRVDRV_MAX_CHANNEL_SIZE 14 +#define MRVDRV_MAX_BSSID_LIST 64 +#define MRVDRV_ASSOCIATION_TIME_OUT 255 +#define MRVDRV_SNAP_HEADER_LEN 8 + +#define WLAN_UPLD_SIZE 2312 +#define DEV_NAME_LEN 32 + +/** Misc constants */ +/* This section defines 802.11 specific contants */ + +#define MRVDRV_MAX_BSS_DESCRIPTS 16 +#define MRVDRV_MAX_REGION_CODE 6 + +#define MRVDRV_IGNORE_MULTIPLE_DTIM 0xfffe +#define MRVDRV_MIN_MULTIPLE_DTIM 1 +#define MRVDRV_MAX_MULTIPLE_DTIM 5 +#define MRVDRV_DEFAULT_MULTIPLE_DTIM 1 + +#define MRVDRV_DEFAULT_LISTEN_INTERVAL 10 + +#define MRVDRV_CHANNELS_PER_SCAN 4 +#define MRVDRV_MAX_CHANNELS_PER_SCAN 14 + +#define MRVDRV_DEBUG_RX_PATH 0x00000001 +#define MRVDRV_DEBUG_TX_PATH 0x00000002 + +#define MRVDRV_MIN_BEACON_INTERVAL 20 +#define MRVDRV_MAX_BEACON_INTERVAL 1000 +#define MRVDRV_BEACON_INTERVAL 100 + +/** TxPD status */ + +/* Station firmware use TxPD status field to report final Tx transmit +* result, Bit masks are used to present combined situations. +*/ + +#define MRVDRV_TxPD_POWER_MGMT_NULL_PACKET 0x01 +#define MRVDRV_TxPD_POWER_MGMT_LAST_PACKET 0x08 + +/** Tx mesh flag */ +/* Currently we are using normal WDS flag as mesh flag. + * TODO: change to proper mesh flag when MAC understands it. + */ +#define TxPD_CONTROL_WDS_FRAME (1<<17) +#define TxPD_MESH_FRAME TxPD_CONTROL_WDS_FRAME + +/** RxPD status */ + +#define MRVDRV_RXPD_STATUS_OK 0x0001 + +/** RxPD status - Received packet types */ +/** Rx mesh flag */ +/* Currently we are using normal WDS flag as mesh flag. + * TODO: change to proper mesh flag when MAC understands it. + */ +#define RxPD_CONTROL_WDS_FRAME (0x40) +#define RxPD_MESH_FRAME RxPD_CONTROL_WDS_FRAME + +/** RSSI-related defines */ +/* RSSI constants are used to implement 802.11 RSSI threshold +* indication. if the Rx packet signal got too weak for 5 consecutive +* times, miniport driver (driver) will report this event to wrapper +*/ + +#define MRVDRV_NF_DEFAULT_SCAN_VALUE (-96) + +/** RTS/FRAG related defines */ +#define MRVDRV_RTS_MIN_VALUE 0 +#define MRVDRV_RTS_MAX_VALUE 2347 +#define MRVDRV_FRAG_MIN_VALUE 256 +#define MRVDRV_FRAG_MAX_VALUE 2346 + +/* This is for firmware specific length */ +#define EXTRA_LEN 36 + +#define MRVDRV_ETH_TX_PACKET_BUFFER_SIZE \ + (ETH_FRAME_LEN + sizeof(struct txpd) + EXTRA_LEN) + +#define MRVDRV_ETH_RX_PACKET_BUFFER_SIZE \ + (ETH_FRAME_LEN + sizeof(struct rxpd) \ + + MRVDRV_SNAP_HEADER_LEN + EXTRA_LEN) + +#define CMD_F_HOSTCMD (1 << 0) +#define FW_CAPINFO_WPA (1 << 0) + +/** WPA key LENGTH*/ +#define MRVL_MAX_KEY_WPA_KEY_LENGTH 32 + +#define KEY_LEN_WPA_AES 16 +#define KEY_LEN_WPA_TKIP 32 +#define KEY_LEN_WEP_104 13 +#define KEY_LEN_WEP_40 5 + +#define RF_ANTENNA_1 0x1 +#define RF_ANTENNA_2 0x2 +#define RF_ANTENNA_AUTO 0xFFFF + +#define BAND_B (0x01) +#define BAND_G (0x02) +#define ALL_802_11_BANDS (BAND_B | BAND_G) + +/** MACRO DEFINITIONS */ +#define CAL_NF(NF) ((s32)(-(s32)(NF))) +#define CAL_RSSI(SNR, NF) ((s32)((s32)(SNR) + CAL_NF(NF))) +#define SCAN_RSSI(RSSI) (0x100 - ((u8)(RSSI))) + +#define DEFAULT_BCN_AVG_FACTOR 8 +#define DEFAULT_DATA_AVG_FACTOR 8 +#define AVG_SCALE 100 +#define CAL_AVG_SNR_NF(AVG, SNRNF, N) \ + (((AVG) == 0) ? ((u16)(SNRNF) * AVG_SCALE) : \ + ((((int)(AVG) * (N -1)) + ((u16)(SNRNF) * \ + AVG_SCALE)) / N)) + +#define B_SUPPORTED_RATES 8 +#define G_SUPPORTED_RATES 14 + +#define WLAN_SUPPORTED_RATES 14 + +#define MAX_LEDS 8 + +#define IS_MESH_FRAME(x) (x->cb[6]) +#define SET_MESH_FRAME(x) (x->cb[6]=1) +#define UNSET_MESH_FRAME(x) (x->cb[6]=0) + +/** Global Variable Declaration */ +typedef struct _wlan_private wlan_private; +typedef struct _wlan_adapter wlan_adapter; +extern const char libertas_driver_version[]; +extern u16 libertas_region_code_to_index[MRVDRV_MAX_REGION_CODE]; + +extern u8 libertas_wlan_data_rates[WLAN_SUPPORTED_RATES]; + +extern u8 libertas_supported_rates[G_SUPPORTED_RATES]; + +extern u8 libertas_adhoc_rates_g[G_SUPPORTED_RATES]; + +extern u8 libertas_adhoc_rates_b[4]; + +/** ENUM definition*/ +/** SNRNF_TYPE */ +enum SNRNF_TYPE { + TYPE_BEACON = 0, + TYPE_RXPD, + MAX_TYPE_B +}; + +/** SNRNF_DATA*/ +enum SNRNF_DATA { + TYPE_NOAVG = 0, + TYPE_AVG, + MAX_TYPE_AVG +}; + +/** WLAN_802_11_AUTH_ALG*/ +enum WLAN_802_11_AUTH_ALG { + AUTH_ALG_OPEN_SYSTEM = 1, + AUTH_ALG_SHARED_KEY = 2, + AUTH_ALG_NETWORK_EAP = 8, +}; + +/** WLAN_802_1X_AUTH_ALG */ +enum WLAN_802_1X_AUTH_ALG { + WLAN_1X_AUTH_ALG_NONE = 1, + WLAN_1X_AUTH_ALG_LEAP = 2, + WLAN_1X_AUTH_ALG_TLS = 4, + WLAN_1X_AUTH_ALG_TTLS = 8, + WLAN_1X_AUTH_ALG_MD5 = 16, +}; + +/** WLAN_802_11_ENCRYPTION_MODE */ +enum WLAN_802_11_ENCRYPTION_MODE { + CIPHER_NONE, + CIPHER_WEP40, + CIPHER_TKIP, + CIPHER_CCMP, + CIPHER_WEP104, +}; + +/** WLAN_802_11_POWER_MODE */ +enum WLAN_802_11_POWER_MODE { + wlan802_11powermodecam, + wlan802_11powermodemax_psp, + wlan802_11Powermodefast_psp, + /*not a real mode, defined as an upper bound */ + wlan802_11powemodemax +}; + +/** PS_STATE */ +enum PS_STATE { + PS_STATE_FULL_POWER, + PS_STATE_AWAKE, + PS_STATE_PRE_SLEEP, + PS_STATE_SLEEP +}; + +/** DNLD_STATE */ +enum DNLD_STATE { + DNLD_RES_RECEIVED, + DNLD_DATA_SENT, + DNLD_CMD_SENT +}; + +/** WLAN_MEDIA_STATE */ +enum WLAN_MEDIA_STATE { + libertas_connected, + libertas_disconnected +}; + +/** WLAN_802_11_PRIVACY_FILTER */ +enum WLAN_802_11_PRIVACY_FILTER { + wlan802_11privfilteracceptall, + wlan802_11privfilter8021xWEP +}; + +/** mv_ms_type */ +enum mv_ms_type { + MVMS_DAT = 0, + MVMS_CMD = 1, + MVMS_TXDONE = 2, + MVMS_EVENT +}; + +/** WLAN_802_11_NETWORK_INFRASTRUCTURE */ +enum WLAN_802_11_NETWORK_INFRASTRUCTURE { + wlan802_11ibss, + wlan802_11infrastructure, + wlan802_11autounknown, + /*defined as upper bound */ + wlan802_11infrastructuremax +}; + +/** WLAN_802_11_AUTHENTICATION_MODE */ +enum WLAN_802_11_AUTHENTICATION_MODE { + wlan802_11authmodeopen = 0x00, + wlan802_11authmodeshared = 0x01, + wlan802_11authmodenetworkEAP = 0x80, +}; + +/** WLAN_802_11_WEP_STATUS */ +enum WLAN_802_11_WEP_STATUS { + wlan802_11WEPenabled, + wlan802_11WEPdisabled, +}; + +/** SNMP_MIB_INDEX_e */ +enum SNMP_MIB_INDEX_e { + desired_bsstype_i = 0, + op_rateset_i, + bcnperiod_i, + dtimperiod_i, + assocrsp_timeout_i, + rtsthresh_i, + short_retrylim_i, + long_retrylim_i, + fragthresh_i, + dot11d_i, + dot11h_i, + manufid_i, + prodID_i, + manuf_oui_i, + manuf_name_i, + manuf_prodname_i, + manuf_prodver_i, +}; + +/** KEY_TYPE_ID */ +enum KEY_TYPE_ID { + KEY_TYPE_ID_WEP = 0, + KEY_TYPE_ID_TKIP, + KEY_TYPE_ID_AES +}; + +/** KEY_INFO_WPA (applies to both TKIP and AES/CCMP) */ +enum KEY_INFO_WPA { + KEY_INFO_WPA_MCAST = 0x01, + KEY_INFO_WPA_UNICAST = 0x02, + KEY_INFO_WPA_ENABLED = 0x04 +}; + +/** SNMP_MIB_VALUE_e */ +enum SNMP_MIB_VALUE_e { + SNMP_MIB_VALUE_INFRA = 1, + SNMP_MIB_VALUE_ADHOC +}; + +/* Default values for fwt commands. */ +#define FWT_DEFAULT_METRIC 0 +#define FWT_DEFAULT_DIR 1 +#define FWT_DEFAULT_SSN 0xffffffff +#define FWT_DEFAULT_DSN 0 +#define FWT_DEFAULT_HOPCOUNT 0 +#define FWT_DEFAULT_TTL 0 +#define FWT_DEFAULT_EXPIRATION 0 +#define FWT_DEFAULT_SLEEPMODE 0 +#define FWT_DEFAULT_SNR 0 + +#endif /* _WLAN_DEFS_H_ */ diff --git a/drivers/net/wireless/libertas/dev.h b/drivers/net/wireless/libertas/dev.h new file mode 100644 index 00000000000..b1f876f9693 --- /dev/null +++ b/drivers/net/wireless/libertas/dev.h @@ -0,0 +1,403 @@ +/** + * This file contains definitions and data structures specific + * to Marvell 802.11 NIC. It contains the Device Information + * structure wlan_adapter. + */ +#ifndef _WLAN_DEV_H_ +#define _WLAN_DEV_H_ + +#include <linux/netdevice.h> +#include <linux/wireless.h> +#include <linux/ethtool.h> +#include <linux/debugfs.h> + +#include "defs.h" +#include "scan.h" +#include "thread.h" + +extern struct ethtool_ops libertas_ethtool_ops; + +#define MAX_BSSID_PER_CHANNEL 16 + +#define NR_TX_QUEUE 3 + +/* For the extended Scan */ +#define MAX_EXTENDED_SCAN_BSSID_LIST MAX_BSSID_PER_CHANNEL * \ + MRVDRV_MAX_CHANNEL_SIZE + 1 + +#define MAX_REGION_CHANNEL_NUM 2 + +/** Chan-freq-TxPower mapping table*/ +struct chan_freq_power { + /** channel Number */ + u16 channel; + /** frequency of this channel */ + u32 freq; + /** Max allowed Tx power level */ + u16 maxtxpower; + /** TRUE:channel unsupported; FLASE:supported*/ + u8 unsupported; +}; + +/** region-band mapping table*/ +struct region_channel { + /** TRUE if this entry is valid */ + u8 valid; + /** region code for US, Japan ... */ + u8 region; + /** band B/G/A, used for BAND_CONFIG cmd */ + u8 band; + /** Actual No. of elements in the array below */ + u8 nrcfp; + /** chan-freq-txpower mapping table*/ + struct chan_freq_power *CFP; +}; + +struct wlan_802_11_security { + u8 WPAenabled; + u8 WPA2enabled; + enum WLAN_802_11_WEP_STATUS WEPstatus; + enum WLAN_802_11_AUTHENTICATION_MODE authmode; + enum WLAN_802_1X_AUTH_ALG auth1xalg; + enum WLAN_802_11_ENCRYPTION_MODE Encryptionmode; +}; + +/** Current Basic Service Set State Structure */ +struct current_bss_params { + struct bss_descriptor bssdescriptor; + /** bssid */ + u8 bssid[ETH_ALEN]; + /** ssid */ + struct WLAN_802_11_SSID ssid; + + /** band */ + u8 band; + /** channel */ + u8 channel; + /** number of rates supported */ + int numofrates; + /** supported rates*/ + u8 datarates[WLAN_SUPPORTED_RATES]; +}; + +/** sleep_params */ +struct sleep_params { + u16 sp_error; + u16 sp_offset; + u16 sp_stabletime; + u8 sp_calcontrol; + u8 sp_extsleepclk; + u16 sp_reserved; +}; + +/** Data structure for the Marvell WLAN device */ +typedef struct _wlan_dev { + /** device name */ + char name[DEV_NAME_LEN]; + /** card pointer */ + void *card; + /** IO port */ + u32 ioport; + /** Upload received */ + u32 upld_rcv; + /** Upload type */ + u32 upld_typ; + /** Upload length */ + u32 upld_len; + /** netdev pointer */ + struct net_device *netdev; + /* Upload buffer */ + u8 upld_buf[WLAN_UPLD_SIZE]; + /* Download sent: + bit0 1/0=data_sent/data_tx_done, + bit1 1/0=cmd_sent/cmd_tx_done, + all other bits reserved 0 */ + u8 dnld_sent; +} wlan_dev_t, *pwlan_dev_t; + +/* Mesh statistics */ +struct wlan_mesh_stats { + u32 fwd_bcast_cnt; /* Fwd: Broadcast counter */ + u32 fwd_unicast_cnt; /* Fwd: Unicast counter */ + u32 fwd_drop_ttl; /* Fwd: TTL zero */ + u32 fwd_drop_rbt; /* Fwd: Recently Broadcasted */ + u32 fwd_drop_noroute; /* Fwd: No route to Destination */ + u32 fwd_drop_nobuf; /* Fwd: Run out of internal buffers */ + u32 drop_blind; /* Rx: Dropped by blinding table */ +}; + +/** Private structure for the MV device */ +struct _wlan_private { + int open; + int mesh_open; + int infra_open; + + wlan_adapter *adapter; + wlan_dev_t wlan_dev; + + struct net_device_stats stats; + struct net_device *mesh_dev ; /* Virtual device */ + + struct iw_statistics wstats; + struct wlan_mesh_stats mstats; + struct dentry *debugfs_dir; + struct dentry *debugfs_debug; + struct dentry *debugfs_files[6]; + + struct dentry *events_dir; + struct dentry *debugfs_events_files[6]; + + struct dentry *regs_dir; + struct dentry *debugfs_regs_files[6]; + + u32 mac_offset; + u32 bbp_offset; + u32 rf_offset; + + const struct firmware *firmware; + struct device *hotplug_device; + + /** thread to service interrupts */ + struct wlan_thread mainthread; + + struct delayed_work assoc_work; + struct workqueue_struct *assoc_thread; +}; + +/** Association request + * + * Encapsulates all the options that describe a specific assocation request + * or configuration of the wireless card's radio, mode, and security settings. + */ +struct assoc_request { +#define ASSOC_FLAG_SSID 1 +#define ASSOC_FLAG_CHANNEL 2 +#define ASSOC_FLAG_MODE 3 +#define ASSOC_FLAG_BSSID 4 +#define ASSOC_FLAG_WEP_KEYS 5 +#define ASSOC_FLAG_WEP_TX_KEYIDX 6 +#define ASSOC_FLAG_WPA_MCAST_KEY 7 +#define ASSOC_FLAG_WPA_UCAST_KEY 8 +#define ASSOC_FLAG_SECINFO 9 +#define ASSOC_FLAG_WPA_IE 10 + unsigned long flags; + + struct WLAN_802_11_SSID ssid; + u8 channel; + enum WLAN_802_11_NETWORK_INFRASTRUCTURE mode; + u8 bssid[ETH_ALEN]; + + /** WEP keys */ + struct WLAN_802_11_KEY wep_keys[4]; + u16 wep_tx_keyidx; + + /** WPA keys */ + struct WLAN_802_11_KEY wpa_mcast_key; + struct WLAN_802_11_KEY wpa_unicast_key; + + struct wlan_802_11_security secinfo; + + /** WPA Information Elements*/ +#define MAX_WPA_IE_LEN 64 + u8 wpa_ie[MAX_WPA_IE_LEN]; + u8 wpa_ie_len; +}; + +/** Wlan adapter data structure*/ +struct _wlan_adapter { + /** STATUS variables */ + u32 fwreleasenumber; + u32 fwcapinfo; + /* protected with big lock */ + + struct mutex lock; + + u8 tmptxbuf[WLAN_UPLD_SIZE]; + /* protected by hard_start_xmit serialization */ + + /** command-related variables */ + u16 seqnum; + /* protected by big lock */ + + struct cmd_ctrl_node *cmd_array; + /** Current command */ + struct cmd_ctrl_node *cur_cmd; + int cur_cmd_retcode; + /** command Queues */ + /** Free command buffers */ + struct list_head cmdfreeq; + /** Pending command buffers */ + struct list_head cmdpendingq; + + wait_queue_head_t cmd_pending; + u8 nr_cmd_pending; + /* command related variables protected by adapter->driver_lock */ + + /** Async and Sync Event variables */ + u32 intcounter; + u32 eventcause; + u8 nodename[16]; /* nickname */ + + /** spin locks */ + spinlock_t driver_lock; + + /** Timers */ + struct timer_list command_timer; + + /* TX queue used in PS mode */ + spinlock_t txqueue_lock; + struct sk_buff *tx_queue_ps[NR_TX_QUEUE]; + unsigned int tx_queue_idx; + + u8 hisregcpy; + + /** current ssid/bssid related parameters*/ + struct current_bss_params curbssparams; + + enum WLAN_802_11_NETWORK_INFRASTRUCTURE inframode; + + struct bss_descriptor *pattemptedbssdesc; + + struct WLAN_802_11_SSID previousssid; + u8 previousbssid[ETH_ALEN]; + + struct bss_descriptor *scantable; + u32 numinscantable; + + u8 scantype; + u32 scanmode; + + u16 beaconperiod; + u8 adhoccreate; + + /** capability Info used in Association, start, join */ + struct ieeetypes_capinfo capinfo; + + /** MAC address information */ + u8 current_addr[ETH_ALEN]; + u8 multicastlist[MRVDRV_MAX_MULTICAST_LIST_SIZE][ETH_ALEN]; + u32 nr_of_multicastmacaddr; + + /** 802.11 statistics */ +// struct cmd_DS_802_11_GET_STAT wlan802_11Stat; + + u16 enablehwauto; + u16 ratebitmap; + /** control G rates */ + u8 adhoc_grate_enabled; + + u32 txantenna; + u32 rxantenna; + + u8 adhocchannel; + u32 fragthsd; + u32 rtsthsd; + + u32 datarate; + u8 is_datarate_auto; + + u16 listeninterval; + u16 prescan; + u8 txretrycount; + + /** Tx-related variables (for single packet tx) */ + struct sk_buff *currenttxskb; + u16 TxLockFlag; + + /** NIC Operation characteristics */ + u16 currentpacketfilter; + u32 connect_status; + u16 regioncode; + u16 regiontableindex; + u16 txpowerlevel; + + /** POWER MANAGEMENT AND PnP SUPPORT */ + u8 surpriseremoved; + u16 atimwindow; + + u16 psmode; /* Wlan802_11PowermodeCAM=disable + Wlan802_11PowermodeMAX_PSP=enable */ + u16 multipledtim; + u32 psstate; + u8 needtowakeup; + + struct PS_CMD_ConfirmSleep libertas_ps_confirm_sleep; + u16 locallisteninterval; + u16 nullpktinterval; + + struct assoc_request * assoc_req; + + /** Encryption parameter */ + struct wlan_802_11_security secinfo; + + /** WEP keys */ + struct WLAN_802_11_KEY wep_keys[4]; + u16 wep_tx_keyidx; + + /** WPA keys */ + struct WLAN_802_11_KEY wpa_mcast_key; + struct WLAN_802_11_KEY wpa_unicast_key; + + /** WPA Information Elements*/ +#define MAX_WPA_IE_LEN 64 + u8 wpa_ie[MAX_WPA_IE_LEN]; + u8 wpa_ie_len; + + u16 rxantennamode; + u16 txantennamode; + + /** Requested Signal Strength*/ + u16 bcn_avg_factor; + u16 data_avg_factor; + u16 SNR[MAX_TYPE_B][MAX_TYPE_AVG]; + u16 NF[MAX_TYPE_B][MAX_TYPE_AVG]; + u8 RSSI[MAX_TYPE_B][MAX_TYPE_AVG]; + u8 rawSNR[DEFAULT_DATA_AVG_FACTOR]; + u8 rawNF[DEFAULT_DATA_AVG_FACTOR]; + u16 nextSNRNF; + u16 numSNRNF; + u16 rxpd_rate; + + u8 radioon; + u32 preamble; + + /** Multi bands Parameter*/ + u8 libertas_supported_rates[G_SUPPORTED_RATES]; + + /** Blue Tooth Co-existence Arbitration */ + + /** sleep_params */ + struct sleep_params sp; + + /** RF calibration data */ + +#define MAX_REGION_CHANNEL_NUM 2 + /** region channel data */ + struct region_channel region_channel[MAX_REGION_CHANNEL_NUM]; + + struct region_channel universal_channel[MAX_REGION_CHANNEL_NUM]; + + /** 11D and Domain Regulatory Data */ + struct wlan_802_11d_domain_reg domainreg; + struct parsed_region_chan_11d parsed_region_chan; + + /** FSM variable for 11d support */ + u32 enable11d; + + /** MISCELLANEOUS */ + u8 *prdeeprom; + struct wlan_offset_value offsetvalue; + + struct cmd_ds_802_11_get_log logmsg; + u16 scanprobes; + + u32 pkttxctrl; + + u16 txrate; + u32 linkmode; + u32 radiomode; + u32 debugmode; + u8 fw_ready; +}; + +#endif /* _WLAN_DEV_H_ */ diff --git a/drivers/net/wireless/libertas/ethtool.c b/drivers/net/wireless/libertas/ethtool.c new file mode 100644 index 00000000000..0064de54296 --- /dev/null +++ b/drivers/net/wireless/libertas/ethtool.c @@ -0,0 +1,184 @@ + +#include <linux/netdevice.h> +#include <linux/ethtool.h> +#include <linux/delay.h> + +#include "host.h" +#include "sbi.h" +#include "decl.h" +#include "defs.h" +#include "dev.h" +#include "join.h" +#include "wext.h" +static const char * mesh_stat_strings[]= { + "drop_duplicate_bcast", + "drop_ttl_zero", + "drop_no_fwd_route", + "drop_no_buffers", + "fwded_unicast_cnt", + "fwded_bcast_cnt", + "drop_blind_table" +}; + +static void libertas_ethtool_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *info) +{ + wlan_private *priv = (wlan_private *) dev->priv; + char fwver[32]; + + libertas_get_fwversion(priv->adapter, fwver, sizeof(fwver) - 1); + + strcpy(info->driver, "libertas"); + strcpy(info->version, libertas_driver_version); + strcpy(info->fw_version, fwver); +} + +/* All 8388 parts have 16KiB EEPROM size at the time of writing. + * In case that changes this needs fixing. + */ +#define LIBERTAS_EEPROM_LEN 16384 + +static int libertas_ethtool_get_eeprom_len(struct net_device *dev) +{ + return LIBERTAS_EEPROM_LEN; +} + +static int libertas_ethtool_get_eeprom(struct net_device *dev, + struct ethtool_eeprom *eeprom, u8 * bytes) +{ + wlan_private *priv = (wlan_private *) dev->priv; + wlan_adapter *adapter = priv->adapter; + struct wlan_ioctl_regrdwr regctrl; + char *ptr; + int ret; + + regctrl.action = 0; + regctrl.offset = eeprom->offset; + regctrl.NOB = eeprom->len; + + if (eeprom->offset + eeprom->len > LIBERTAS_EEPROM_LEN) + return -EINVAL; + +// mutex_lock(&priv->mutex); + + adapter->prdeeprom = + (char *)kmalloc(eeprom->len+sizeof(regctrl), GFP_KERNEL); + if (!adapter->prdeeprom) + return -ENOMEM; + memcpy(adapter->prdeeprom, ®ctrl, sizeof(regctrl)); + + /* +14 is for action, offset, and NOB in + * response */ + lbs_pr_debug(1, "action:%d offset: %x NOB: %02x\n", + regctrl.action, regctrl.offset, regctrl.NOB); + + ret = libertas_prepare_and_send_command(priv, + cmd_802_11_eeprom_access, + regctrl.action, + cmd_option_waitforrsp, 0, + ®ctrl); + + if (ret) { + if (adapter->prdeeprom) + kfree(adapter->prdeeprom); + LEAVE(); + return ret; + } + + mdelay(10); + + ptr = (char *)adapter->prdeeprom; + + /* skip the command header, but include the "value" u32 variable */ + ptr = ptr + sizeof(struct wlan_ioctl_regrdwr) - 4; + + /* + * Return the result back to the user + */ + memcpy(bytes, ptr, eeprom->len); + + if (adapter->prdeeprom) + kfree(adapter->prdeeprom); +// mutex_unlock(&priv->mutex); + + return 0; +} + +static void libertas_ethtool_get_stats(struct net_device * dev, + struct ethtool_stats * stats, u64 * data) +{ + wlan_private *priv = dev->priv; + + ENTER(); + + stats->cmd = ETHTOOL_GSTATS; + BUG_ON(stats->n_stats != MESH_STATS_NUM); + + data[0] = priv->mstats.fwd_drop_rbt; + data[1] = priv->mstats.fwd_drop_ttl; + data[2] = priv->mstats.fwd_drop_noroute; + data[3] = priv->mstats.fwd_drop_nobuf; + data[4] = priv->mstats.fwd_unicast_cnt; + data[5] = priv->mstats.fwd_bcast_cnt; + data[6] = priv->mstats.drop_blind; + + LEAVE(); +} + +static int libertas_ethtool_get_stats_count(struct net_device * dev) +{ + int ret; + wlan_private *priv = dev->priv; + struct cmd_ds_mesh_access mesh_access; + + ENTER(); + /* Get Mesh Statistics */ + ret = libertas_prepare_and_send_command(priv, + cmd_mesh_access, cmd_act_mesh_get_stats, + cmd_option_waitforrsp, 0, &mesh_access); + + if (ret) { + LEAVE(); + return 0; + } + + priv->mstats.fwd_drop_rbt = mesh_access.data[0]; + priv->mstats.fwd_drop_ttl = mesh_access.data[1]; + priv->mstats.fwd_drop_noroute = mesh_access.data[2]; + priv->mstats.fwd_drop_nobuf = mesh_access.data[3]; + priv->mstats.fwd_unicast_cnt = mesh_access.data[4]; + priv->mstats.fwd_bcast_cnt = mesh_access.data[5]; + priv->mstats.drop_blind = mesh_access.data[6]; + + LEAVE(); + return MESH_STATS_NUM; +} + +static void libertas_ethtool_get_strings (struct net_device * dev, + u32 stringset, + u8 * s) +{ + int i; + + ENTER(); + switch (stringset) { + case ETH_SS_STATS: + for (i=0; i < MESH_STATS_NUM; i++) { + memcpy(s + i * ETH_GSTRING_LEN, + mesh_stat_strings[i], + ETH_GSTRING_LEN); + } + break; + } + LEAVE(); +} + +struct ethtool_ops libertas_ethtool_ops = { + .get_drvinfo = libertas_ethtool_get_drvinfo, + .get_eeprom = libertas_ethtool_get_eeprom, + .get_eeprom_len = libertas_ethtool_get_eeprom_len, + .get_stats_count = libertas_ethtool_get_stats_count, + .get_ethtool_stats = libertas_ethtool_get_stats, + .get_strings = libertas_ethtool_get_strings, +}; + diff --git a/drivers/net/wireless/libertas/fw.c b/drivers/net/wireless/libertas/fw.c new file mode 100644 index 00000000000..b194a457079 --- /dev/null +++ b/drivers/net/wireless/libertas/fw.c @@ -0,0 +1,361 @@ +/** + * This file contains the initialization for FW and HW + */ +#include <linux/module.h> +#include <linux/moduleparam.h> + +#include <linux/vmalloc.h> +#include <linux/firmware.h> +#include <linux/version.h> + +#include "host.h" +#include "sbi.h" +#include "defs.h" +#include "decl.h" +#include "dev.h" +#include "fw.h" +#include "wext.h" +#include "if_usb.h" + +char *libertas_fw_name = NULL; +module_param_named(fw_name, libertas_fw_name, charp, 0644); + +unsigned int libertas_debug = 0; +module_param(libertas_debug, int, 0); + +/** + * @brief This function checks the validity of Boot2/FW image. + * + * @param data pointer to image + * len image length + * @return 0 or -1 + */ +static int check_fwfile_format(u8 *data, u32 totlen) +{ + u8 bincmd, exit; + u32 blksize, offset, len; + int ret; + + ret = 1; + exit = len = 0; + + do { + bincmd = *data; + blksize = *(u32*)(data + offsetof(struct fwheader, datalength)); + switch (bincmd) { + case FW_HAS_DATA_TO_RECV: + offset = sizeof(struct fwheader) + blksize; + data += offset; + len += offset; + if (len >= totlen) + exit = 1; + break; + case FW_HAS_LAST_BLOCK: + exit = 1; + ret = 0; + break; + default: + exit = 1; + break; + } + } while (!exit); + + if (ret) + lbs_pr_err("bin file format check FAIL...\n"); + else + lbs_pr_debug(1, "bin file format check PASS...\n"); + + return ret; +} + +/** + * @brief This function downloads firmware image, gets + * HW spec from firmware and set basic parameters to + * firmware. + * + * @param priv A pointer to wlan_private structure + * @return 0 or -1 + */ +static int wlan_setup_station_hw(wlan_private * priv) +{ + int ret = -1; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + if ((ret = request_firmware(&priv->firmware, libertas_fw_name, + priv->hotplug_device)) < 0) { + lbs_pr_err("request_firmware() failed, error code = %#x\n", + ret); + lbs_pr_err("%s not found in /lib/firmware\n", libertas_fw_name); + goto done; + } + + if(check_fwfile_format(priv->firmware->data, priv->firmware->size)) { + release_firmware(priv->firmware); + goto done; + } + + ret = libertas_sbi_prog_firmware(priv); + + release_firmware(priv->firmware); + + if (ret) { + lbs_pr_debug(1, "Bootloader in invalid state!\n"); + ret = -1; + goto done; + } + + /* + * Read MAC address from HW + */ + memset(adapter->current_addr, 0xff, ETH_ALEN); + + ret = libertas_prepare_and_send_command(priv, cmd_get_hw_spec, + 0, cmd_option_waitforrsp, 0, NULL); + + if (ret) { + ret = -1; + goto done; + } + + libertas_set_mac_packet_filter(priv); + + /* Get the supported Data rates */ + ret = libertas_prepare_and_send_command(priv, cmd_802_11_data_rate, + cmd_act_get_tx_rate, + cmd_option_waitforrsp, 0, NULL); + + if (ret) { + ret = -1; + goto done; + } + + ret = 0; +done: + LEAVE(); + + return (ret); +} + +static int wlan_allocate_adapter(wlan_private * priv) +{ + u32 ulbufsize; + wlan_adapter *adapter = priv->adapter; + + struct bss_descriptor *ptempscantable; + + /* Allocate buffer to store the BSSID list */ + ulbufsize = sizeof(struct bss_descriptor) * MRVDRV_MAX_BSSID_LIST; + if (!(ptempscantable = kmalloc(ulbufsize, GFP_KERNEL))) { + libertas_free_adapter(priv); + return -1; + } + + adapter->scantable = ptempscantable; + memset(adapter->scantable, 0, ulbufsize); + + /* Allocate the command buffers */ + libertas_allocate_cmd_buffer(priv); + + memset(&adapter->libertas_ps_confirm_sleep, 0, sizeof(struct PS_CMD_ConfirmSleep)); + adapter->libertas_ps_confirm_sleep.seqnum = cpu_to_le16(++adapter->seqnum); + adapter->libertas_ps_confirm_sleep.command = + cpu_to_le16(cmd_802_11_ps_mode); + adapter->libertas_ps_confirm_sleep.size = + cpu_to_le16(sizeof(struct PS_CMD_ConfirmSleep)); + adapter->libertas_ps_confirm_sleep.result = 0; + adapter->libertas_ps_confirm_sleep.action = + cpu_to_le16(cmd_subcmd_sleep_confirmed); + + return 0; +} + +static void wlan_init_adapter(wlan_private * priv) +{ + wlan_adapter *adapter = priv->adapter; + int i; + + adapter->scanprobes = 0; + + adapter->bcn_avg_factor = DEFAULT_BCN_AVG_FACTOR; + adapter->data_avg_factor = DEFAULT_DATA_AVG_FACTOR; + + /* ATIM params */ + adapter->atimwindow = 0; + + adapter->connect_status = libertas_disconnected; + memset(adapter->current_addr, 0xff, ETH_ALEN); + + /* scan type */ + adapter->scantype = cmd_scan_type_active; + + /* scan mode */ + adapter->scanmode = cmd_bss_type_any; + + /* 802.11 specific */ + adapter->secinfo.WEPstatus = wlan802_11WEPdisabled; + for (i = 0; i < sizeof(adapter->wep_keys) / sizeof(adapter->wep_keys[0]); + i++) + memset(&adapter->wep_keys[i], 0, sizeof(struct WLAN_802_11_KEY)); + adapter->wep_tx_keyidx = 0; + adapter->secinfo.WEPstatus = wlan802_11WEPdisabled; + adapter->secinfo.authmode = wlan802_11authmodeopen; + adapter->secinfo.auth1xalg = WLAN_1X_AUTH_ALG_NONE; + adapter->secinfo.Encryptionmode = CIPHER_NONE; + adapter->inframode = wlan802_11infrastructure; + + adapter->assoc_req = NULL; + + adapter->numinscantable = 0; + adapter->pattemptedbssdesc = NULL; + mutex_init(&adapter->lock); + + adapter->prescan = 1; + + memset(&adapter->curbssparams, 0, sizeof(adapter->curbssparams)); + + /* PnP and power profile */ + adapter->surpriseremoved = 0; + + adapter->currentpacketfilter = + cmd_act_mac_rx_on | cmd_act_mac_tx_on; + + adapter->radioon = RADIO_ON; + adapter->txantenna = RF_ANTENNA_2; + adapter->rxantenna = RF_ANTENNA_AUTO; + + adapter->is_datarate_auto = 1; + adapter->beaconperiod = MRVDRV_BEACON_INTERVAL; + + // set default value of capinfo. +#define SHORT_PREAMBLE_ALLOWED 1 + memset(&adapter->capinfo, 0, sizeof(adapter->capinfo)); + adapter->capinfo.shortpreamble = SHORT_PREAMBLE_ALLOWED; + + adapter->adhocchannel = DEFAULT_AD_HOC_CHANNEL; + + adapter->psmode = wlan802_11powermodecam; + adapter->multipledtim = MRVDRV_DEFAULT_MULTIPLE_DTIM; + + adapter->listeninterval = MRVDRV_DEFAULT_LISTEN_INTERVAL; + + adapter->psstate = PS_STATE_FULL_POWER; + adapter->needtowakeup = 0; + adapter->locallisteninterval = 0; /* default value in firmware will be used */ + + adapter->datarate = 0; // Initially indicate the rate as auto + + adapter->adhoc_grate_enabled = 0; + + adapter->intcounter = 0; + + adapter->currenttxskb = NULL; + adapter->pkttxctrl = 0; + + memset(&adapter->tx_queue_ps, 0, NR_TX_QUEUE*sizeof(struct sk_buff*)); + adapter->tx_queue_idx = 0; + spin_lock_init(&adapter->txqueue_lock); + + return; +} + +static void command_timer_fn(unsigned long data); + +int libertas_init_fw(wlan_private * priv) +{ + int ret = -1; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + /* Allocate adapter structure */ + if ((ret = wlan_allocate_adapter(priv)) != 0) + goto done; + + /* init adapter structure */ + wlan_init_adapter(priv); + + /* init timer etc. */ + setup_timer(&adapter->command_timer, command_timer_fn, + (unsigned long)priv); + + /* download fimrware etc. */ + if ((ret = wlan_setup_station_hw(priv)) != 0) { + del_timer_sync(&adapter->command_timer); + goto done; + } + + /* init 802.11d */ + libertas_init_11d(priv); + + ret = 0; +done: + LEAVE(); + return ret; +} + +void libertas_free_adapter(wlan_private * priv) +{ + wlan_adapter *adapter = priv->adapter; + + if (!adapter) { + lbs_pr_debug(1, "Why double free adapter?:)\n"); + return; + } + + lbs_pr_debug(1, "Free command buffer\n"); + libertas_free_cmd_buffer(priv); + + lbs_pr_debug(1, "Free commandTimer\n"); + del_timer(&adapter->command_timer); + + lbs_pr_debug(1, "Free scantable\n"); + if (adapter->scantable) { + kfree(adapter->scantable); + adapter->scantable = NULL; + } + + lbs_pr_debug(1, "Free adapter\n"); + + /* Free the adapter object itself */ + kfree(adapter); + priv->adapter = NULL; +} + +/** + * This function handles the timeout of command sending. + * It will re-send the same command again. + */ +static void command_timer_fn(unsigned long data) +{ + wlan_private *priv = (wlan_private *)data; + wlan_adapter *adapter = priv->adapter; + struct cmd_ctrl_node *ptempnode; + struct cmd_ds_command *cmd; + unsigned long flags; + + ptempnode = adapter->cur_cmd; + cmd = (struct cmd_ds_command *)ptempnode->bufvirtualaddr; + + lbs_pr_info("command_timer_fn fired (%x)\n", cmd->command); + + if (!adapter->fw_ready) + return; + + if (ptempnode == NULL) { + lbs_pr_debug(1, "PTempnode Empty\n"); + return; + } + + spin_lock_irqsave(&adapter->driver_lock, flags); + adapter->cur_cmd = NULL; + spin_unlock_irqrestore(&adapter->driver_lock, flags); + + lbs_pr_debug(1, "Re-sending same command as it timeout...!\n"); + libertas_queue_cmd(adapter, ptempnode, 0); + + wake_up_interruptible(&priv->mainthread.waitq); + + return; +} diff --git a/drivers/net/wireless/libertas/fw.h b/drivers/net/wireless/libertas/fw.h new file mode 100644 index 00000000000..1f9ae267a9e --- /dev/null +++ b/drivers/net/wireless/libertas/fw.h @@ -0,0 +1,13 @@ +/** + * This header file contains FW interface related definitions. + */ +#ifndef _WLAN_FW_H_ +#define _WLAN_FW_H_ + +#ifndef DEV_NAME_LEN +#define DEV_NAME_LEN 32 +#endif + +int libertas_init_fw(wlan_private * priv); + +#endif /* _WLAN_FW_H_ */ diff --git a/drivers/net/wireless/libertas/host.h b/drivers/net/wireless/libertas/host.h new file mode 100644 index 00000000000..c0faaecaf5b --- /dev/null +++ b/drivers/net/wireless/libertas/host.h @@ -0,0 +1,338 @@ +/** + * This file contains definitions of WLAN commands. + */ + +#ifndef _HOST_H_ +#define _HOST_H_ + +/** PUBLIC DEFINITIONS */ +#define DEFAULT_AD_HOC_CHANNEL 6 +#define DEFAULT_AD_HOC_CHANNEL_A 36 + +/** IEEE 802.11 oids */ +#define OID_802_11_SSID 0x00008002 +#define OID_802_11_INFRASTRUCTURE_MODE 0x00008008 +#define OID_802_11_FRAGMENTATION_THRESHOLD 0x00008009 +#define OID_802_11_RTS_THRESHOLD 0x0000800A +#define OID_802_11_TX_ANTENNA_SELECTED 0x0000800D +#define OID_802_11_SUPPORTED_RATES 0x0000800E +#define OID_802_11_STATISTICS 0x00008012 +#define OID_802_11_TX_RETRYCOUNT 0x0000801D +#define OID_802_11D_ENABLE 0x00008020 + +#define cmd_option_waitforrsp 0x0002 + +/** Host command ID */ +#define cmd_code_dnld 0x0002 +#define cmd_get_hw_spec 0x0003 +#define cmd_eeprom_update 0x0004 +#define cmd_802_11_reset 0x0005 +#define cmd_802_11_scan 0x0006 +#define cmd_802_11_get_log 0x000b +#define cmd_mac_multicast_adr 0x0010 +#define cmd_802_11_authenticate 0x0011 +#define cmd_802_11_eeprom_access 0x0059 +#define cmd_802_11_associate 0x0050 +#define cmd_802_11_set_wep 0x0013 +#define cmd_802_11_get_stat 0x0014 +#define cmd_802_3_get_stat 0x0015 +#define cmd_802_11_snmp_mib 0x0016 +#define cmd_mac_reg_map 0x0017 +#define cmd_bbp_reg_map 0x0018 +#define cmd_mac_reg_access 0x0019 +#define cmd_bbp_reg_access 0x001a +#define cmd_rf_reg_access 0x001b +#define cmd_802_11_radio_control 0x001c +#define cmd_802_11_rf_channel 0x001d +#define cmd_802_11_rf_tx_power 0x001e +#define cmd_802_11_rssi 0x001f +#define cmd_802_11_rf_antenna 0x0020 + +#define cmd_802_11_ps_mode 0x0021 + +#define cmd_802_11_data_rate 0x0022 +#define cmd_rf_reg_map 0x0023 +#define cmd_802_11_deauthenticate 0x0024 +#define cmd_802_11_reassociate 0x0025 +#define cmd_802_11_disassociate 0x0026 +#define cmd_mac_control 0x0028 +#define cmd_802_11_ad_hoc_start 0x002b +#define cmd_802_11_ad_hoc_join 0x002c + +#define cmd_802_11_query_tkip_reply_cntrs 0x002e +#define cmd_802_11_enable_rsn 0x002f +#define cmd_802_11_pairwise_tsc 0x0036 +#define cmd_802_11_group_tsc 0x0037 +#define cmd_802_11_key_material 0x005e + +#define cmd_802_11_set_afc 0x003c +#define cmd_802_11_get_afc 0x003d + +#define cmd_802_11_ad_hoc_stop 0x0040 + +#define cmd_802_11_beacon_stop 0x0049 + +#define cmd_802_11_mac_address 0x004D +#define cmd_802_11_eeprom_access 0x0059 + +#define cmd_802_11_band_config 0x0058 + +#define cmd_802_11d_domain_info 0x005b + +#define cmd_802_11_sleep_params 0x0066 + +#define cmd_802_11_inactivity_timeout 0x0067 + +#define cmd_802_11_tpc_cfg 0x0072 +#define cmd_802_11_pwr_cfg 0x0073 + +#define cmd_802_11_led_gpio_ctrl 0x004e + +#define cmd_802_11_subscribe_event 0x0075 + +#define cmd_802_11_rate_adapt_rateset 0x0076 + +#define cmd_802_11_tx_rate_query 0x007f + +#define cmd_get_tsf 0x0080 + +#define cmd_bt_access 0x0087 +#define cmd_ret_bt_access 0x8087 + +#define cmd_fwt_access 0x0088 +#define cmd_ret_fwt_access 0x8088 + +#define cmd_mesh_access 0x0090 +#define cmd_ret_mesh_access 0x8090 + +/* For the IEEE Power Save */ +#define cmd_subcmd_enter_ps 0x0030 +#define cmd_subcmd_exit_ps 0x0031 +#define cmd_subcmd_sleep_confirmed 0x0034 +#define cmd_subcmd_full_powerdown 0x0035 +#define cmd_subcmd_full_powerup 0x0036 + +/* command RET code, MSB is set to 1 */ +#define cmd_ret_hw_spec_info 0x8003 +#define cmd_ret_eeprom_update 0x8004 +#define cmd_ret_802_11_reset 0x8005 +#define cmd_ret_802_11_scan 0x8006 +#define cmd_ret_802_11_get_log 0x800b +#define cmd_ret_mac_control 0x8028 +#define cmd_ret_mac_multicast_adr 0x8010 +#define cmd_ret_802_11_authenticate 0x8011 +#define cmd_ret_802_11_deauthenticate 0x8024 +#define cmd_ret_802_11_associate 0x8012 +#define cmd_ret_802_11_reassociate 0x8025 +#define cmd_ret_802_11_disassociate 0x8026 +#define cmd_ret_802_11_set_wep 0x8013 +#define cmd_ret_802_11_stat 0x8014 +#define cmd_ret_802_3_stat 0x8015 +#define cmd_ret_802_11_snmp_mib 0x8016 +#define cmd_ret_mac_reg_map 0x8017 +#define cmd_ret_bbp_reg_map 0x8018 +#define cmd_ret_rf_reg_map 0x8023 +#define cmd_ret_mac_reg_access 0x8019 +#define cmd_ret_bbp_reg_access 0x801a +#define cmd_ret_rf_reg_access 0x801b +#define cmd_ret_802_11_radio_control 0x801c +#define cmd_ret_802_11_rf_channel 0x801d +#define cmd_ret_802_11_rssi 0x801f +#define cmd_ret_802_11_rf_tx_power 0x801e +#define cmd_ret_802_11_rf_antenna 0x8020 +#define cmd_ret_802_11_ps_mode 0x8021 +#define cmd_ret_802_11_data_rate 0x8022 + +#define cmd_ret_802_11_ad_hoc_start 0x802B +#define cmd_ret_802_11_ad_hoc_join 0x802C + +#define cmd_ret_802_11_query_tkip_reply_cntrs 0x802e +#define cmd_ret_802_11_enable_rsn 0x802f +#define cmd_ret_802_11_pairwise_tsc 0x8036 +#define cmd_ret_802_11_group_tsc 0x8037 +#define cmd_ret_802_11_key_material 0x805e + +#define cmd_enable_rsn 0x0001 +#define cmd_disable_rsn 0x0000 + +#define cmd_act_set 0x0001 +#define cmd_act_get 0x0000 + +#define cmd_act_get_AES (cmd_act_get + 2) +#define cmd_act_set_AES (cmd_act_set + 2) +#define cmd_act_remove_aes (cmd_act_set + 3) + +#define cmd_ret_802_11_set_afc 0x803c +#define cmd_ret_802_11_get_afc 0x803d + +#define cmd_ret_802_11_ad_hoc_stop 0x8040 + +#define cmd_ret_802_11_beacon_stop 0x8049 + +#define cmd_ret_802_11_mac_address 0x804D +#define cmd_ret_802_11_eeprom_access 0x8059 + +#define cmd_ret_802_11_band_config 0x8058 + +#define cmd_ret_802_11_sleep_params 0x8066 + +#define cmd_ret_802_11_inactivity_timeout 0x8067 + +#define cmd_ret_802_11d_domain_info (0x8000 | \ + cmd_802_11d_domain_info) + +#define cmd_ret_802_11_tpc_cfg (cmd_802_11_tpc_cfg | 0x8000) +#define cmd_ret_802_11_pwr_cfg (cmd_802_11_pwr_cfg | 0x8000) + +#define cmd_ret_802_11_led_gpio_ctrl 0x804e + +#define cmd_ret_802_11_subscribe_event (cmd_802_11_subscribe_event | 0x8000) + +#define cmd_ret_802_11_rate_adapt_rateset (cmd_802_11_rate_adapt_rateset | 0x8000) + +#define cmd_rte_802_11_tx_rate_query (cmd_802_11_tx_rate_query | 0x8000) + +#define cmd_ret_get_tsf 0x8080 + +/* Define action or option for cmd_802_11_set_wep */ +#define cmd_act_add 0x0002 +#define cmd_act_remove 0x0004 +#define cmd_act_use_default 0x0008 + +#define cmd_type_wep_40_bit 0x0001 +#define cmd_type_wep_104_bit 0x0002 + +#define cmd_NUM_OF_WEP_KEYS 4 + +#define cmd_WEP_KEY_INDEX_MASK 0x3fff + +/* Define action or option for cmd_802_11_reset */ +#define cmd_act_halt 0x0003 + +/* Define action or option for cmd_802_11_scan */ +#define cmd_bss_type_bss 0x0001 +#define cmd_bss_type_ibss 0x0002 +#define cmd_bss_type_any 0x0003 + +/* Define action or option for cmd_802_11_scan */ +#define cmd_scan_type_active 0x0000 +#define cmd_scan_type_passive 0x0001 + +#define cmd_scan_radio_type_bg 0 + +#define cmd_scan_probe_delay_time 0 + +/* Define action or option for cmd_mac_control */ +#define cmd_act_mac_rx_on 0x0001 +#define cmd_act_mac_tx_on 0x0002 +#define cmd_act_mac_loopback_on 0x0004 +#define cmd_act_mac_wep_enable 0x0008 +#define cmd_act_mac_int_enable 0x0010 +#define cmd_act_mac_multicast_enable 0x0020 +#define cmd_act_mac_broadcast_enable 0x0040 +#define cmd_act_mac_promiscuous_enable 0x0080 +#define cmd_act_mac_all_multicast_enable 0x0100 +#define cmd_act_mac_strict_protection_enable 0x0400 + +/* Define action or option for cmd_802_11_radio_control */ +#define cmd_type_auto_preamble 0x0001 +#define cmd_type_short_preamble 0x0002 +#define cmd_type_long_preamble 0x0003 + +#define TURN_ON_RF 0x01 +#define RADIO_ON 0x01 +#define RADIO_OFF 0x00 + +#define SET_AUTO_PREAMBLE 0x05 +#define SET_SHORT_PREAMBLE 0x03 +#define SET_LONG_PREAMBLE 0x01 + +/* Define action or option for CMD_802_11_RF_CHANNEL */ +#define cmd_opt_802_11_rf_channel_get 0x00 +#define cmd_opt_802_11_rf_channel_set 0x01 + +/* Define action or option for cmd_802_11_rf_tx_power */ +#define cmd_act_tx_power_opt_get 0x0000 +#define cmd_act_tx_power_opt_set_high 0x8007 +#define cmd_act_tx_power_opt_set_mid 0x8004 +#define cmd_act_tx_power_opt_set_low 0x8000 + +#define cmd_act_tx_power_index_high 0x0007 +#define cmd_act_tx_power_index_mid 0x0004 +#define cmd_act_tx_power_index_low 0x0000 + +/* Define action or option for cmd_802_11_data_rate */ +#define cmd_act_set_tx_auto 0x0000 +#define cmd_act_set_tx_fix_rate 0x0001 +#define cmd_act_get_tx_rate 0x0002 + +#define cmd_act_set_rx 0x0001 +#define cmd_act_set_tx 0x0002 +#define cmd_act_set_both 0x0003 +#define cmd_act_get_rx 0x0004 +#define cmd_act_get_tx 0x0008 +#define cmd_act_get_both 0x000c + +/* Define action or option for cmd_802_11_ps_mode */ +#define cmd_type_cam 0x0000 +#define cmd_type_max_psp 0x0001 +#define cmd_type_fast_psp 0x0002 + +/* Define action or option for cmd_bt_access */ +enum cmd_bt_access_opts { + /* The bt commands start at 5 instead of 1 because the old dft commands + * are mapped to 1-4. These old commands are no longer maintained and + * should not be called. + */ + cmd_act_bt_access_add = 5, + cmd_act_bt_access_del, + cmd_act_bt_access_list, + cmd_act_bt_access_reset +}; + +/* Define action or option for cmd_fwt_access */ +enum cmd_fwt_access_opts { + cmd_act_fwt_access_add = 1, + cmd_act_fwt_access_del, + cmd_act_fwt_access_lookup, + cmd_act_fwt_access_list, + cmd_act_fwt_access_list_route, + cmd_act_fwt_access_list_neighbor, + cmd_act_fwt_access_reset, + cmd_act_fwt_access_cleanup, + cmd_act_fwt_access_time, +}; + +/* Define action or option for cmd_mesh_access */ +enum cmd_mesh_access_opts { + cmd_act_mesh_get_ttl = 1, + cmd_act_mesh_set_ttl, + cmd_act_mesh_get_stats, + cmd_act_mesh_get_mpp, + cmd_act_mesh_set_mpp, +}; + +/** Card Event definition */ +#define MACREG_INT_CODE_TX_PPA_FREE 0x00000000 +#define MACREG_INT_CODE_TX_DMA_DONE 0x00000001 +#define MACREG_INT_CODE_LINK_LOSE_W_SCAN 0x00000002 +#define MACREG_INT_CODE_LINK_LOSE_NO_SCAN 0x00000003 +#define MACREG_INT_CODE_LINK_SENSED 0x00000004 +#define MACREG_INT_CODE_CMD_FINISHED 0x00000005 +#define MACREG_INT_CODE_MIB_CHANGED 0x00000006 +#define MACREG_INT_CODE_INIT_DONE 0x00000007 +#define MACREG_INT_CODE_DEAUTHENTICATED 0x00000008 +#define MACREG_INT_CODE_DISASSOCIATED 0x00000009 +#define MACREG_INT_CODE_PS_AWAKE 0x0000000a +#define MACREG_INT_CODE_PS_SLEEP 0x0000000b +#define MACREG_INT_CODE_MIC_ERR_MULTICAST 0x0000000d +#define MACREG_INT_CODE_MIC_ERR_UNICAST 0x0000000e +#define MACREG_INT_CODE_WM_AWAKE 0x0000000f +#define MACREG_INT_CODE_ADHOC_BCN_LOST 0x00000011 +#define MACREG_INT_CODE_RSSI_LOW 0x00000019 +#define MACREG_INT_CODE_SNR_LOW 0x0000001a +#define MACREG_INT_CODE_MAX_FAIL 0x0000001b +#define MACREG_INT_CODE_RSSI_HIGH 0x0000001c +#define MACREG_INT_CODE_SNR_HIGH 0x0000001d + +#endif /* _HOST_H_ */ diff --git a/drivers/net/wireless/libertas/hostcmd.h b/drivers/net/wireless/libertas/hostcmd.h new file mode 100644 index 00000000000..f239e5d2435 --- /dev/null +++ b/drivers/net/wireless/libertas/hostcmd.h @@ -0,0 +1,693 @@ +/* + * This file contains the function prototypes, data structure + * and defines for all the host/station commands + */ +#ifndef __HOSTCMD__H +#define __HOSTCMD__H + +#include <linux/wireless.h> +#include "11d.h" +#include "types.h" + +/* 802.11-related definitions */ + +/* TxPD descriptor */ +struct txpd { + /* Current Tx packet status */ + u32 tx_status; + /* Tx control */ + u32 tx_control; + u32 tx_packet_location; + /* Tx packet length */ + u16 tx_packet_length; + /* First 2 byte of destination MAC address */ + u8 tx_dest_addr_high[2]; + /* Last 4 byte of destination MAC address */ + u8 tx_dest_addr_low[4]; + /* Pkt Priority */ + u8 priority; + /* Pkt Trasnit Power control */ + u8 powermgmt; + /* Amount of time the packet has been queued in the driver (units = 2ms) */ + u8 pktdelay_2ms; + /* reserved */ + u8 reserved1; +}; + +/* RxPD Descriptor */ +struct rxpd { + /* Current Rx packet status */ + u16 status; + + /* SNR */ + u8 snr; + + /* Tx control */ + u8 rx_control; + + /* Pkt length */ + u16 pkt_len; + + /* Noise Floor */ + u8 nf; + + /* Rx Packet Rate */ + u8 rx_rate; + + /* Pkt addr */ + u32 pkt_ptr; + + /* Next Rx RxPD addr */ + u32 next_rxpd_ptr; + + /* Pkt Priority */ + u8 priority; + u8 reserved[3]; +}; + +struct cmd_ctrl_node { + /* CMD link list */ + struct list_head list; + u32 status; + /* CMD ID */ + u32 cmd_oid; + /*CMD wait option: wait for finish or no wait */ + u16 wait_option; + /* command parameter */ + void *pdata_buf; + /*command data */ + u8 *bufvirtualaddr; + u16 cmdflags; + /* wait queue */ + u16 cmdwaitqwoken; + wait_queue_head_t cmdwait_q; +}; + +/* WLAN_802_11_KEY + * + * Generic structure to hold all key types. key type (WEP40, WEP104, TKIP, AES) + * is determined from the keylength field. + */ +struct WLAN_802_11_KEY { + u32 len; + u32 flags; /* KEY_INFO_* from wlan_defs.h */ + u8 key[MRVL_MAX_KEY_WPA_KEY_LENGTH]; + u16 type; /* KEY_TYPE_* from wlan_defs.h */ +}; + +struct IE_WPA { + u8 elementid; + u8 len; + u8 oui[4]; + u16 version; +}; + +struct WLAN_802_11_SSID { + /* SSID length */ + u32 ssidlength; + + /* SSID information field */ + u8 ssid[IW_ESSID_MAX_SIZE]; +}; + +struct WPA_SUPPLICANT { + u8 wpa_ie[256]; + u8 wpa_ie_len; +}; + +/* wlan_offset_value */ +struct wlan_offset_value { + u32 offset; + u32 value; +}; + +struct WLAN_802_11_FIXED_IEs { + u8 timestamp[8]; + u16 beaconinterval; + u16 capabilities; +}; + +struct WLAN_802_11_VARIABLE_IEs { + u8 elementid; + u8 length; + u8 data[1]; +}; + +/* Define general data structure */ +/* cmd_DS_GEN */ +struct cmd_ds_gen { + u16 command; + u16 size; + u16 seqnum; + u16 result; +}; + +#define S_DS_GEN sizeof(struct cmd_ds_gen) +/* + * Define data structure for cmd_get_hw_spec + * This structure defines the response for the GET_HW_SPEC command + */ +struct cmd_ds_get_hw_spec { + /* HW Interface version number */ + u16 hwifversion; + /* HW version number */ + u16 version; + /* Max number of TxPD FW can handle */ + u16 nr_txpd; + /* Max no of Multicast address */ + u16 nr_mcast_adr; + /* MAC address */ + u8 permanentaddr[6]; + + /* region Code */ + u16 regioncode; + + /* Number of antenna used */ + u16 nr_antenna; + + /* FW release number, example 0x1234=1.2.3.4 */ + u32 fwreleasenumber; + + /* Base Address of TxPD queue */ + u32 wcb_base; + /* Read Pointer of RxPd queue */ + u32 rxpd_rdptr; + + /* Write Pointer of RxPd queue */ + u32 rxpd_wrptr; + + /*FW/HW capability */ + u32 fwcapinfo; +} __attribute__ ((packed)); + +struct cmd_ds_802_11_reset { + u16 action; +}; + +struct cmd_ds_802_11_subscribe_event { + u16 action; + u16 events; +}; + +/* + * This scan handle Country Information IE(802.11d compliant) + * Define data structure for cmd_802_11_scan + */ +struct cmd_ds_802_11_scan { + u8 bsstype; + u8 BSSID[ETH_ALEN]; + u8 tlvbuffer[1]; +#if 0 + mrvlietypes_ssidparamset_t ssidParamSet; + mrvlietypes_chanlistparamset_t ChanListParamSet; + mrvlietypes_ratesparamset_t OpRateSet; +#endif +}; + +struct cmd_ds_802_11_scan_rsp { + u16 bssdescriptsize; + u8 nr_sets; + u8 bssdesc_and_tlvbuffer[1]; +}; + +struct cmd_ds_802_11_get_log { + u32 mcasttxframe; + u32 failed; + u32 retry; + u32 multiretry; + u32 framedup; + u32 rtssuccess; + u32 rtsfailure; + u32 ackfailure; + u32 rxfrag; + u32 mcastrxframe; + u32 fcserror; + u32 txframe; + u32 wepundecryptable; +}; + +struct cmd_ds_mac_control { + u16 action; + u16 reserved; +}; + +struct cmd_ds_mac_multicast_adr { + u16 action; + u16 nr_of_adrs; + u8 maclist[ETH_ALEN * MRVDRV_MAX_MULTICAST_LIST_SIZE]; +}; + +struct cmd_ds_802_11_authenticate { + u8 macaddr[ETH_ALEN]; + u8 authtype; + u8 reserved[10]; +}; + +struct cmd_ds_802_11_deauthenticate { + u8 macaddr[6]; + u16 reasoncode; +}; + +struct cmd_ds_802_11_associate { + u8 peerstaaddr[6]; + struct ieeetypes_capinfo capinfo; + u16 listeninterval; + u16 bcnperiod; + u8 dtimperiod; + +#if 0 + mrvlietypes_ssidparamset_t ssidParamSet; + mrvlietypes_phyparamset_t phyparamset; + mrvlietypes_ssparamset_t ssparamset; + mrvlietypes_ratesparamset_t ratesParamSet; +#endif +} __attribute__ ((packed)); + +struct cmd_ds_802_11_disassociate { + u8 destmacaddr[6]; + u16 reasoncode; +}; + +struct cmd_ds_802_11_associate_rsp { + struct ieeetypes_assocrsp assocRsp; +}; + +struct cmd_ds_802_11_ad_hoc_result { + u8 PAD[3]; + u8 BSSID[ETH_ALEN]; +}; + +struct cmd_ds_802_11_set_wep { + /* ACT_ADD, ACT_REMOVE or ACT_ENABLE */ + u16 action; + + /* key Index selected for Tx */ + u16 keyindex; + + /* 40, 128bit or TXWEP */ + u8 keytype[4]; + u8 keymaterial[4][16]; +}; + +struct cmd_ds_802_3_get_stat { + u32 xmitok; + u32 rcvok; + u32 xmiterror; + u32 rcverror; + u32 rcvnobuffer; + u32 rcvcrcerror; +}; + +struct cmd_ds_802_11_get_stat { + u32 txfragmentcnt; + u32 mcasttxframecnt; + u32 failedcnt; + u32 retrycnt; + u32 Multipleretrycnt; + u32 rtssuccesscnt; + u32 rtsfailurecnt; + u32 ackfailurecnt; + u32 frameduplicatecnt; + u32 rxfragmentcnt; + u32 mcastrxframecnt; + u32 fcserrorcnt; + u32 bcasttxframecnt; + u32 bcastrxframecnt; + u32 txbeacon; + u32 rxbeacon; + u32 wepundecryptable; +}; + +struct cmd_ds_802_11_snmp_mib { + u16 querytype; + u16 oid; + u16 bufsize; + u8 value[128]; +}; + +struct cmd_ds_mac_reg_map { + u16 buffersize; + u8 regmap[128]; + u16 reserved; +}; + +struct cmd_ds_bbp_reg_map { + u16 buffersize; + u8 regmap[128]; + u16 reserved; +}; + +struct cmd_ds_rf_reg_map { + u16 buffersize; + u8 regmap[64]; + u16 reserved; +}; + +struct cmd_ds_mac_reg_access { + u16 action; + u16 offset; + u32 value; +}; + +struct cmd_ds_bbp_reg_access { + u16 action; + u16 offset; + u8 value; + u8 reserved[3]; +}; + +struct cmd_ds_rf_reg_access { + u16 action; + u16 offset; + u8 value; + u8 reserved[3]; +}; + +struct cmd_ds_802_11_radio_control { + u16 action; + u16 control; +}; + +struct cmd_ds_802_11_sleep_params { + /* ACT_GET/ACT_SET */ + u16 action; + + /* Sleep clock error in ppm */ + u16 error; + + /* Wakeup offset in usec */ + u16 offset; + + /* Clock stabilization time in usec */ + u16 stabletime; + + /* control periodic calibration */ + u8 calcontrol; + + /* control the use of external sleep clock */ + u8 externalsleepclk; + + /* reserved field, should be set to zero */ + u16 reserved; +}; + +struct cmd_ds_802_11_inactivity_timeout { + /* ACT_GET/ACT_SET */ + u16 action; + + /* Inactivity timeout in msec */ + u16 timeout; +}; + +struct cmd_ds_802_11_rf_channel { + u16 action; + u16 currentchannel; + u16 rftype; + u16 reserved; + u8 channellist[32]; +}; + +struct cmd_ds_802_11_rssi { + /* weighting factor */ + u16 N; + + u16 reserved_0; + u16 reserved_1; + u16 reserved_2; +}; + +struct cmd_ds_802_11_rssi_rsp { + u16 SNR; + u16 noisefloor; + u16 avgSNR; + u16 avgnoisefloor; +}; + +struct cmd_ds_802_11_mac_address { + u16 action; + u8 macadd[ETH_ALEN]; +}; + +struct cmd_ds_802_11_rf_tx_power { + u16 action; + u16 currentlevel; +}; + +struct cmd_ds_802_11_rf_antenna { + u16 action; + + /* Number of antennas or 0xffff(diversity) */ + u16 antennamode; + +}; + +struct cmd_ds_802_11_ps_mode { + u16 action; + u16 nullpktinterval; + u16 multipledtim; + u16 reserved; + u16 locallisteninterval; +}; + +struct PS_CMD_ConfirmSleep { + u16 command; + u16 size; + u16 seqnum; + u16 result; + + u16 action; + u16 reserved1; + u16 multipledtim; + u16 reserved; + u16 locallisteninterval; +}; + +struct cmd_ds_802_11_data_rate { + u16 action; + u16 reserverd; + u8 datarate[G_SUPPORTED_RATES]; +}; + +struct cmd_ds_802_11_rate_adapt_rateset { + u16 action; + u16 enablehwauto; + u16 bitmap; +}; + +struct cmd_ds_802_11_ad_hoc_start { + u8 SSID[IW_ESSID_MAX_SIZE]; + u8 bsstype; + u16 beaconperiod; + u8 dtimperiod; + union IEEEtypes_ssparamset ssparamset; + union ieeetypes_phyparamset phyparamset; + u16 probedelay; + struct ieeetypes_capinfo cap; + u8 datarate[G_SUPPORTED_RATES]; + u8 tlv_memory_size_pad[100]; +} __attribute__ ((packed)); + +struct adhoc_bssdesc { + u8 BSSID[6]; + u8 SSID[32]; + u8 bsstype; + u16 beaconperiod; + u8 dtimperiod; + u8 timestamp[8]; + u8 localtime[8]; + union ieeetypes_phyparamset phyparamset; + union IEEEtypes_ssparamset ssparamset; + struct ieeetypes_capinfo cap; + u8 datarates[G_SUPPORTED_RATES]; + + /* DO NOT ADD ANY FIELDS TO THIS STRUCTURE. It is used below in the + * Adhoc join command and will cause a binary layout mismatch with + * the firmware + */ +} __attribute__ ((packed)); + +struct cmd_ds_802_11_ad_hoc_join { + struct adhoc_bssdesc bssdescriptor; + u16 failtimeout; + u16 probedelay; + +} __attribute__ ((packed)); + +struct cmd_ds_802_11_enable_rsn { + u16 action; + u16 enable; +}; + +struct MrvlIEtype_keyParamSet { + /* type ID */ + u16 type; + + /* length of Payload */ + u16 length; + + /* type of key: WEP=0, TKIP=1, AES=2 */ + u16 keytypeid; + + /* key control Info specific to a keytypeid */ + u16 keyinfo; + + /* length of key */ + u16 keylen; + + /* key material of size keylen */ + u8 key[32]; +}; + +struct cmd_ds_802_11_key_material { + u16 action; + struct MrvlIEtype_keyParamSet keyParamSet[2]; +} __attribute__ ((packed)); + +struct cmd_ds_802_11_eeprom_access { + u16 action; + + /* multiple 4 */ + u16 offset; + u16 bytecount; + u8 value; +} __attribute__ ((packed)); + +struct cmd_ds_802_11_tpc_cfg { + u16 action; + u8 enable; + s8 P0; + s8 P1; + s8 P2; + u8 usesnr; +} __attribute__ ((packed)); + +struct cmd_ds_802_11_led_ctrl { + u16 action; + u16 numled; + u8 data[256]; +} __attribute__ ((packed)); + +struct cmd_ds_802_11_pwr_cfg { + u16 action; + u8 enable; + s8 PA_P0; + s8 PA_P1; + s8 PA_P2; +} __attribute__ ((packed)); + +struct cmd_ds_802_11_afc { + u16 afc_auto; + union { + struct { + u16 threshold; + u16 period; + }; + struct { + s16 timing_offset; + s16 carrier_offset; + }; + }; +} __attribute__ ((packed)); + +struct cmd_tx_rate_query { + u16 txrate; +} __attribute__ ((packed)); + +struct cmd_ds_get_tsf { + __le64 tsfvalue; +} __attribute__ ((packed)); + +struct cmd_ds_bt_access { + u16 action; + u32 id; + u8 addr1[ETH_ALEN]; + u8 addr2[ETH_ALEN]; +} __attribute__ ((packed)); + +struct cmd_ds_fwt_access { + u16 action; + u32 id; + u8 da[ETH_ALEN]; + u8 dir; + u8 ra[ETH_ALEN]; + u32 ssn; + u32 dsn; + u32 metric; + u8 hopcount; + u8 ttl; + u32 expiration; + u8 sleepmode; + u32 snr; + u32 references; +} __attribute__ ((packed)); + +#define MESH_STATS_NUM 7 +struct cmd_ds_mesh_access { + u16 action; + u32 data[MESH_STATS_NUM + 1]; /* last position reserved */ +} __attribute__ ((packed)); + +struct cmd_ds_command { + /* command header */ + u16 command; + u16 size; + u16 seqnum; + u16 result; + + /* command Body */ + union { + struct cmd_ds_get_hw_spec hwspec; + struct cmd_ds_802_11_ps_mode psmode; + struct cmd_ds_802_11_scan scan; + struct cmd_ds_802_11_scan_rsp scanresp; + struct cmd_ds_mac_control macctrl; + struct cmd_ds_802_11_associate associate; + struct cmd_ds_802_11_deauthenticate deauth; + struct cmd_ds_802_11_set_wep wep; + struct cmd_ds_802_11_ad_hoc_start ads; + struct cmd_ds_802_11_reset reset; + struct cmd_ds_802_11_ad_hoc_result result; + struct cmd_ds_802_11_get_log glog; + struct cmd_ds_802_11_authenticate auth; + struct cmd_ds_802_11_get_stat gstat; + struct cmd_ds_802_3_get_stat gstat_8023; + struct cmd_ds_802_11_snmp_mib smib; + struct cmd_ds_802_11_rf_tx_power txp; + struct cmd_ds_802_11_rf_antenna rant; + struct cmd_ds_802_11_data_rate drate; + struct cmd_ds_802_11_rate_adapt_rateset rateset; + struct cmd_ds_mac_multicast_adr madr; + struct cmd_ds_802_11_ad_hoc_join adj; + struct cmd_ds_802_11_radio_control radio; + struct cmd_ds_802_11_rf_channel rfchannel; + struct cmd_ds_802_11_rssi rssi; + struct cmd_ds_802_11_rssi_rsp rssirsp; + struct cmd_ds_802_11_disassociate dassociate; + struct cmd_ds_802_11_mac_address macadd; + struct cmd_ds_802_11_enable_rsn enbrsn; + struct cmd_ds_802_11_key_material keymaterial; + struct cmd_ds_mac_reg_access macreg; + struct cmd_ds_bbp_reg_access bbpreg; + struct cmd_ds_rf_reg_access rfreg; + struct cmd_ds_802_11_eeprom_access rdeeprom; + + struct cmd_ds_802_11d_domain_info domaininfo; + struct cmd_ds_802_11d_domain_info domaininforesp; + + struct cmd_ds_802_11_sleep_params sleep_params; + struct cmd_ds_802_11_inactivity_timeout inactivity_timeout; + struct cmd_ds_802_11_tpc_cfg tpccfg; + struct cmd_ds_802_11_pwr_cfg pwrcfg; + struct cmd_ds_802_11_afc afc; + struct cmd_ds_802_11_led_ctrl ledgpio; + + struct cmd_tx_rate_query txrate; + struct cmd_ds_bt_access bt; + struct cmd_ds_fwt_access fwt; + struct cmd_ds_mesh_access mesh; + struct cmd_ds_get_tsf gettsf; + struct cmd_ds_802_11_subscribe_event subscribe_event; + } params; +} __attribute__ ((packed)); + +#endif diff --git a/drivers/net/wireless/libertas/if_bootcmd.c b/drivers/net/wireless/libertas/if_bootcmd.c new file mode 100644 index 00000000000..567000c3e87 --- /dev/null +++ b/drivers/net/wireless/libertas/if_bootcmd.c @@ -0,0 +1,38 @@ +/** + * This file contains functions used in USB Boot command + * and Boot2/FW update + */ + +#include <linux/delay.h> +#include <linux/firmware.h> +#include <linux/netdevice.h> +#include <linux/usb.h> + +#include "defs.h" +#include "dev.h" +#include "if_usb.h" + +/** + * @brief This function issues Boot command to the Boot2 code + * @param ivalue 1:Boot from FW by USB-Download + * 2:Boot from FW in EEPROM + * @return 0 + */ +int if_usb_issue_boot_command(wlan_private *priv, int ivalue) +{ + struct usb_card_rec *cardp = priv->wlan_dev.card; + struct bootcmdstr sbootcmd; + int i; + + /* Prepare command */ + sbootcmd.u32magicnumber = BOOT_CMD_MAGIC_NUMBER; + sbootcmd.u8cmd_tag = ivalue; + for (i=0; i<11; i++) + sbootcmd.au8dumy[i]=0x00; + memcpy(cardp->bulk_out_buffer, &sbootcmd, sizeof(struct bootcmdstr)); + + /* Issue command */ + usb_tx_block(priv, cardp->bulk_out_buffer, sizeof(struct bootcmdstr)); + + return 0; +} diff --git a/drivers/net/wireless/libertas/if_usb.c b/drivers/net/wireless/libertas/if_usb.c new file mode 100644 index 00000000000..695fb6a66ff --- /dev/null +++ b/drivers/net/wireless/libertas/if_usb.c @@ -0,0 +1,952 @@ +/** + * This file contains functions used in USB interface module. + */ +#include <linux/delay.h> +#include <linux/firmware.h> +#include <linux/netdevice.h> +#include <linux/usb.h> + +#include "host.h" +#include "sbi.h" +#include "decl.h" +#include "defs.h" +#include "dev.h" +#include "if_usb.h" + +#define MESSAGE_HEADER_LEN 4 + +static const char usbdriver_name[] = "usb8xxx"; + +static struct usb_device_id if_usb_table[] = { + /* Enter the device signature inside */ + { + USB_DEVICE(USB8388_VID_1, USB8388_PID_1), + }, + { + USB_DEVICE(USB8388_VID_2, USB8388_PID_2), + }, + {} /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(usb, if_usb_table); + +static void if_usb_receive(struct urb *urb); +static void if_usb_receive_fwload(struct urb *urb); + +/** + * @brief call back function to handle the status of the URB + * @param urb pointer to urb structure + * @return N/A + */ +static void if_usb_write_bulk_callback(struct urb *urb) +{ + wlan_private *priv = (wlan_private *) (urb->context); + wlan_adapter *adapter = priv->adapter; + struct net_device *dev = priv->wlan_dev.netdev; + + /* handle the transmission complete validations */ + + if (urb->status != 0) { + /* print the failure status number for debug */ + lbs_pr_info("URB in failure status\n"); + } else { + lbs_dev_dbg(2, &urb->dev->dev, "URB status is successfull\n"); + lbs_dev_dbg(2, &urb->dev->dev, "Actual length transmitted %d\n", + urb->actual_length); + priv->wlan_dev.dnld_sent = DNLD_RES_RECEIVED; + /* Wake main thread if commands are pending */ + if (!adapter->cur_cmd) + wake_up_interruptible(&priv->mainthread.waitq); + if ((adapter->connect_status == libertas_connected)) + netif_wake_queue(dev); + } + + return; +} + +/** + * @brief free tx/rx urb, skb and rx buffer + * @param cardp pointer usb_card_rec + * @return N/A + */ +void if_usb_free(struct usb_card_rec *cardp) +{ + ENTER(); + + /* Unlink tx & rx urb */ + usb_kill_urb(cardp->tx_urb); + usb_kill_urb(cardp->rx_urb); + + usb_free_urb(cardp->tx_urb); + cardp->tx_urb = NULL; + + usb_free_urb(cardp->rx_urb); + cardp->rx_urb = NULL; + + kfree(cardp->bulk_out_buffer); + cardp->bulk_out_buffer = NULL; + + LEAVE(); + return; +} + +/** + * @brief sets the configuration values + * @param ifnum interface number + * @param id pointer to usb_device_id + * @return 0 on success, error code on failure + */ +static int if_usb_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_device *udev; + struct usb_host_interface *iface_desc; + struct usb_endpoint_descriptor *endpoint; + wlan_private *pwlanpriv; + struct usb_card_rec *usb_cardp; + int i; + + udev = interface_to_usbdev(intf); + + usb_cardp = kzalloc(sizeof(struct usb_card_rec), GFP_KERNEL); + if (!usb_cardp) { + lbs_pr_err("Out of memory allocating private data.\n"); + goto error; + } + + usb_cardp->udev = udev; + iface_desc = intf->cur_altsetting; + + lbs_dev_dbg(1, &udev->dev, "bcdUSB = 0x%X bDeviceClass = 0x%X" + " bDeviceSubClass = 0x%X, bDeviceProtocol = 0x%X\n", + udev->descriptor.bcdUSB, + udev->descriptor.bDeviceClass, + udev->descriptor.bDeviceSubClass, + udev->descriptor.bDeviceProtocol); + + for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { + endpoint = &iface_desc->endpoint[i].desc; + if ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) + && ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == + USB_ENDPOINT_XFER_BULK)) { + /* we found a bulk in endpoint */ + lbs_dev_dbg(1, &udev->dev, "Bulk in size is %d\n", + endpoint->wMaxPacketSize); + if (! + (usb_cardp->rx_urb = + usb_alloc_urb(0, GFP_KERNEL))) { + lbs_dev_dbg(1, &udev->dev, + "Rx URB allocation failed\n"); + goto dealloc; + } + usb_cardp->rx_urb_recall = 0; + + usb_cardp->bulk_in_size = + endpoint->wMaxPacketSize; + usb_cardp->bulk_in_endpointAddr = + (endpoint-> + bEndpointAddress & USB_ENDPOINT_NUMBER_MASK); + lbs_dev_dbg(1, &udev->dev, "in_endpoint = %d\n", + endpoint->bEndpointAddress); + } + + if (((endpoint-> + bEndpointAddress & USB_ENDPOINT_DIR_MASK) == + USB_DIR_OUT) + && ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == + USB_ENDPOINT_XFER_BULK)) { + /* We found bulk out endpoint */ + if (! + (usb_cardp->tx_urb = + usb_alloc_urb(0, GFP_KERNEL))) { + lbs_dev_dbg(1,&udev->dev, + "Tx URB allocation failed\n"); + goto dealloc; + } + + usb_cardp->bulk_out_size = + endpoint->wMaxPacketSize; + lbs_dev_dbg(1, &udev->dev, + "Bulk out size is %d\n", + endpoint->wMaxPacketSize); + usb_cardp->bulk_out_endpointAddr = + endpoint->bEndpointAddress; + lbs_dev_dbg(1, &udev->dev, "out_endpoint = %d\n", + endpoint->bEndpointAddress); + usb_cardp->bulk_out_buffer = + kmalloc(MRVDRV_ETH_TX_PACKET_BUFFER_SIZE, + GFP_KERNEL); + + if (!usb_cardp->bulk_out_buffer) { + lbs_dev_dbg(1, &udev->dev, + "Could not allocate buffer\n"); + goto dealloc; + } + } + } + + + /* At this point wlan_add_card() will be called. Don't worry + * about keeping pwlanpriv around since it will be set on our + * usb device data in -> add() -> libertas_sbi_register_dev(). + */ + if (!(pwlanpriv = wlan_add_card(usb_cardp))) + goto dealloc; + + usb_get_dev(udev); + usb_set_intfdata(intf, usb_cardp); + + /* + * return card structure, which can be got back in the + * diconnect function as the ptr + * argument. + */ + return 0; + +dealloc: + if_usb_free(usb_cardp); + +error: + return -ENOMEM; +} + +/** + * @brief free resource and cleanup + * @param udev pointer to usb_device + * @param ptr pointer to usb_cardp + * @return N/A + */ +static void if_usb_disconnect(struct usb_interface *intf) +{ + struct usb_card_rec *cardp = usb_get_intfdata(intf); + wlan_private *priv = (wlan_private *) cardp->priv; + wlan_adapter *adapter = NULL; + + adapter = priv->adapter; + + /* + * Update Surprise removed to TRUE + */ + adapter->surpriseremoved = 1; + + /* card is removed and we can call wlan_remove_card */ + lbs_dev_dbg(1, &cardp->udev->dev, "call remove card\n"); + wlan_remove_card(cardp); + + /* Unlink and free urb */ + if_usb_free(cardp); + + usb_set_intfdata(intf, NULL); + usb_put_dev(interface_to_usbdev(intf)); + + return; +} + +/** + * @brief This function download FW + * @param priv pointer to wlan_private + * @return 0 + */ +static int if_prog_firmware(wlan_private * priv) +{ + struct usb_card_rec *cardp = priv->wlan_dev.card; + struct FWData *fwdata; + struct fwheader *fwheader; + u8 *firmware = priv->firmware->data; + + fwdata = kmalloc(sizeof(struct FWData), GFP_ATOMIC); + + if (!fwdata) + return -1; + + fwheader = &fwdata->fwheader; + + if (!cardp->CRC_OK) { + cardp->totalbytes = cardp->fwlastblksent; + cardp->fwseqnum = cardp->lastseqnum - 1; + } + + lbs_dev_dbg(2, &cardp->udev->dev, "totalbytes = %d\n", + cardp->totalbytes); + + memcpy(fwheader, &firmware[cardp->totalbytes], + sizeof(struct fwheader)); + + cardp->fwlastblksent = cardp->totalbytes; + cardp->totalbytes += sizeof(struct fwheader); + + lbs_dev_dbg(2, &cardp->udev->dev,"Copy Data\n"); + memcpy(fwdata->data, &firmware[cardp->totalbytes], + fwdata->fwheader.datalength); + + lbs_dev_dbg(2, &cardp->udev->dev, + "Data length = %d\n", fwdata->fwheader.datalength); + + cardp->fwseqnum = cardp->fwseqnum + 1; + + fwdata->seqnum = cardp->fwseqnum; + cardp->lastseqnum = fwdata->seqnum; + cardp->totalbytes += fwdata->fwheader.datalength; + + if (fwheader->dnldcmd == FW_HAS_DATA_TO_RECV) { + lbs_dev_dbg(2, &cardp->udev->dev, "There is data to follow\n"); + lbs_dev_dbg(2, &cardp->udev->dev, + "seqnum = %d totalbytes = %d\n", cardp->fwseqnum, + cardp->totalbytes); + memcpy(cardp->bulk_out_buffer, fwheader, FW_DATA_XMIT_SIZE); + usb_tx_block(priv, cardp->bulk_out_buffer, FW_DATA_XMIT_SIZE); + + } else if (fwdata->fwheader.dnldcmd == FW_HAS_LAST_BLOCK) { + lbs_dev_dbg(2, &cardp->udev->dev, + "Host has finished FW downloading\n"); + lbs_dev_dbg(2, &cardp->udev->dev, + "Donwloading FW JUMP BLOCK\n"); + memcpy(cardp->bulk_out_buffer, fwheader, FW_DATA_XMIT_SIZE); + usb_tx_block(priv, cardp->bulk_out_buffer, FW_DATA_XMIT_SIZE); + cardp->fwfinalblk = 1; + } + + lbs_dev_dbg(2, &cardp->udev->dev, + "The firmware download is done size is %d\n", + cardp->totalbytes); + + kfree(fwdata); + + return 0; +} + +static int libertas_do_reset(wlan_private *priv) +{ + int ret; + struct usb_card_rec *cardp = priv->wlan_dev.card; + + ret = usb_reset_device(cardp->udev); + if (!ret) { + msleep(10); + reset_device(priv); + msleep(10); + } + return ret; +} + +/** + * @brief This function transfer the data to the device. + * @param priv pointer to wlan_private + * @param payload pointer to payload data + * @param nb data length + * @return 0 or -1 + */ +int usb_tx_block(wlan_private * priv, u8 * payload, u16 nb) +{ + /* pointer to card structure */ + struct usb_card_rec *cardp = priv->wlan_dev.card; + int ret = -1; + + /* check if device is removed */ + if (priv->adapter->surpriseremoved) { + lbs_dev_dbg(1, &cardp->udev->dev, "Device removed\n"); + goto tx_ret; + } + + usb_fill_bulk_urb(cardp->tx_urb, cardp->udev, + usb_sndbulkpipe(cardp->udev, + cardp->bulk_out_endpointAddr), + payload, nb, if_usb_write_bulk_callback, priv); + + cardp->tx_urb->transfer_flags |= URB_ZERO_PACKET; + + if ((ret = usb_submit_urb(cardp->tx_urb, GFP_ATOMIC))) { + /* transfer failed */ + lbs_dev_dbg(1, &cardp->udev->dev, "usb_submit_urb failed\n"); + ret = -1; + } else { + lbs_dev_dbg(2, &cardp->udev->dev, "usb_submit_urb success\n"); + ret = 0; + } + +tx_ret: + return ret; +} + +static int __if_usb_submit_rx_urb(wlan_private * priv, + void (*callbackfn) + (struct urb *urb)) +{ + struct usb_card_rec *cardp = priv->wlan_dev.card; + struct sk_buff *skb; + struct read_cb_info *rinfo = &cardp->rinfo; + int ret = -1; + + if (!(skb = dev_alloc_skb(MRVDRV_ETH_RX_PACKET_BUFFER_SIZE))) { + lbs_pr_err("No free skb\n"); + goto rx_ret; + } + + rinfo->skb = skb; + + /* Fill the receive configuration URB and initialise the Rx call back */ + usb_fill_bulk_urb(cardp->rx_urb, cardp->udev, + usb_rcvbulkpipe(cardp->udev, + cardp->bulk_in_endpointAddr), + skb->tail + IPFIELD_ALIGN_OFFSET, + MRVDRV_ETH_RX_PACKET_BUFFER_SIZE, callbackfn, + rinfo); + + cardp->rx_urb->transfer_flags |= URB_ZERO_PACKET; + + lbs_dev_dbg(2, &cardp->udev->dev, "Pointer for rx_urb %p\n", cardp->rx_urb); + if ((ret = usb_submit_urb(cardp->rx_urb, GFP_ATOMIC))) { + /* handle failure conditions */ + lbs_dev_dbg(1, &cardp->udev->dev, "Submit Rx URB failed\n"); + ret = -1; + } else { + lbs_dev_dbg(2, &cardp->udev->dev, "Submit Rx URB success\n"); + ret = 0; + } + +rx_ret: + return ret; +} + +static inline int if_usb_submit_rx_urb_fwload(wlan_private * priv) +{ + return __if_usb_submit_rx_urb(priv, &if_usb_receive_fwload); +} + +static inline int if_usb_submit_rx_urb(wlan_private * priv) +{ + return __if_usb_submit_rx_urb(priv, &if_usb_receive); +} + +static void if_usb_receive_fwload(struct urb *urb) +{ + struct read_cb_info *rinfo = (struct read_cb_info *)urb->context; + wlan_private *priv = rinfo->priv; + struct sk_buff *skb = rinfo->skb; + struct usb_card_rec *cardp = (struct usb_card_rec *)priv->wlan_dev.card; + struct fwsyncheader *syncfwheader; + struct bootcmdrespStr bootcmdresp; + + if (urb->status) { + lbs_dev_dbg(1, &cardp->udev->dev, + "URB status is failed during fw load\n"); + kfree_skb(skb); + return; + } + + if (cardp->bootcmdresp == 0) { + memcpy (&bootcmdresp, skb->data + IPFIELD_ALIGN_OFFSET, + sizeof(bootcmdresp)); + if (cardp->udev->descriptor.bcdDevice < 0x3106) { + kfree_skb(skb); + if_usb_submit_rx_urb_fwload(priv); + cardp->bootcmdresp = 1; + lbs_dev_dbg(1, &cardp->udev->dev, + "Received valid boot command response\n"); + return; + } + if (bootcmdresp.u32magicnumber != BOOT_CMD_MAGIC_NUMBER) { + lbs_pr_info( + "boot cmd response wrong magic number (0x%x)\n", + bootcmdresp.u32magicnumber); + } else if (bootcmdresp.u8cmd_tag != BOOT_CMD_FW_BY_USB) { + lbs_pr_info( + "boot cmd response cmd_tag error (%d)\n", + bootcmdresp.u8cmd_tag); + } else if (bootcmdresp.u8result != BOOT_CMD_RESP_OK) { + lbs_pr_info( + "boot cmd response result error (%d)\n", + bootcmdresp.u8result); + } else { + cardp->bootcmdresp = 1; + lbs_dev_dbg(1, &cardp->udev->dev, + "Received valid boot command response\n"); + } + kfree_skb(skb); + if_usb_submit_rx_urb_fwload(priv); + return; + } + + syncfwheader = kmalloc(sizeof(struct fwsyncheader), GFP_ATOMIC); + if (!syncfwheader) { + lbs_dev_dbg(1, &cardp->udev->dev, "Failure to allocate syncfwheader\n"); + kfree_skb(skb); + return; + } + + memcpy(syncfwheader, skb->data + IPFIELD_ALIGN_OFFSET, + sizeof(struct fwsyncheader)); + + if (!syncfwheader->cmd) { + lbs_dev_dbg(2, &cardp->udev->dev, + "FW received Blk with correct CRC\n"); + lbs_dev_dbg(2, &cardp->udev->dev, + "FW received Blk seqnum = %d\n", + syncfwheader->seqnum); + cardp->CRC_OK = 1; + } else { + lbs_dev_dbg(1, &cardp->udev->dev, + "FW received Blk with CRC error\n"); + cardp->CRC_OK = 0; + } + + kfree_skb(skb); + + if (cardp->fwfinalblk) { + cardp->fwdnldover = 1; + goto exit; + } + + if_prog_firmware(priv); + + if_usb_submit_rx_urb_fwload(priv); +exit: + kfree(syncfwheader); + + return; + +} + +#define MRVDRV_MIN_PKT_LEN 30 + +static inline void process_cmdtypedata(int recvlength, struct sk_buff *skb, + struct usb_card_rec *cardp, + wlan_private *priv) +{ + if (recvlength > MRVDRV_ETH_RX_PACKET_BUFFER_SIZE + + MESSAGE_HEADER_LEN || recvlength < MRVDRV_MIN_PKT_LEN) { + lbs_dev_dbg(1, &cardp->udev->dev, + "Packet length is Invalid\n"); + kfree_skb(skb); + return; + } + + skb_reserve(skb, IPFIELD_ALIGN_OFFSET); + skb_put(skb, recvlength); + skb_pull(skb, MESSAGE_HEADER_LEN); + libertas_process_rxed_packet(priv, skb); + priv->wlan_dev.upld_len = (recvlength - MESSAGE_HEADER_LEN); +} + +static inline void process_cmdrequest(int recvlength, u8 *recvbuff, + struct sk_buff *skb, + struct usb_card_rec *cardp, + wlan_private *priv) +{ + u8 *cmdbuf; + if (recvlength > MRVDRV_SIZE_OF_CMD_BUFFER) { + lbs_dev_dbg(1, &cardp->udev->dev, + "The receive buffer is too large\n"); + kfree_skb(skb); + return; + } + + if (!in_interrupt()) + BUG(); + + spin_lock(&priv->adapter->driver_lock); + /* take care of cur_cmd = NULL case by reading the + * data to clear the interrupt */ + if (!priv->adapter->cur_cmd) { + cmdbuf = priv->wlan_dev.upld_buf; + priv->adapter->hisregcpy &= ~his_cmdupldrdy; + } else + cmdbuf = priv->adapter->cur_cmd->bufvirtualaddr; + + cardp->usb_int_cause |= his_cmdupldrdy; + priv->wlan_dev.upld_len = (recvlength - MESSAGE_HEADER_LEN); + memcpy(cmdbuf, recvbuff + MESSAGE_HEADER_LEN, + priv->wlan_dev.upld_len); + + kfree_skb(skb); + libertas_interrupt(priv->wlan_dev.netdev); + spin_unlock(&priv->adapter->driver_lock); + + lbs_dev_dbg(1, &cardp->udev->dev, + "Wake up main thread to handle cmd response\n"); + + return; +} + +/** + * @brief This function reads of the packet into the upload buff, + * wake up the main thread and initialise the Rx callack. + * + * @param urb pointer to struct urb + * @return N/A + */ +static void if_usb_receive(struct urb *urb) +{ + struct read_cb_info *rinfo = (struct read_cb_info *)urb->context; + wlan_private *priv = rinfo->priv; + struct sk_buff *skb = rinfo->skb; + struct usb_card_rec *cardp = (struct usb_card_rec *)priv->wlan_dev.card; + + int recvlength = urb->actual_length; + u8 *recvbuff = NULL; + u32 recvtype; + + ENTER(); + + if (recvlength) { + if (urb->status) { + lbs_dev_dbg(1, &cardp->udev->dev, + "URB status is failed\n"); + kfree_skb(skb); + goto setup_for_next; + } + + recvbuff = skb->data + IPFIELD_ALIGN_OFFSET; + memcpy(&recvtype, recvbuff, sizeof(u32)); + lbs_dev_dbg(1, &cardp->udev->dev, + "Recv length = 0x%x\n", recvlength); + lbs_dev_dbg(1, &cardp->udev->dev, + "Receive type = 0x%X\n", recvtype); + recvtype = le32_to_cpu(recvtype); + lbs_dev_dbg(1, &cardp->udev->dev, + "Receive type after = 0x%X\n", recvtype); + } else if (urb->status) + goto rx_exit; + + + switch (recvtype) { + case CMD_TYPE_DATA: + process_cmdtypedata(recvlength, skb, cardp, priv); + break; + + case CMD_TYPE_REQUEST: + process_cmdrequest(recvlength, recvbuff, skb, cardp, priv); + break; + + case CMD_TYPE_INDICATION: + /* Event cause handling */ + spin_lock(&priv->adapter->driver_lock); + cardp->usb_event_cause = *(u32 *) (recvbuff + MESSAGE_HEADER_LEN); + lbs_dev_dbg(1, &cardp->udev->dev,"**EVENT** 0x%X\n", + cardp->usb_event_cause); + if (cardp->usb_event_cause & 0xffff0000) { + libertas_send_tx_feedback(priv); + break; + } + cardp->usb_event_cause = le32_to_cpu(cardp->usb_event_cause) << 3; + cardp->usb_int_cause |= his_cardevent; + kfree_skb(skb); + libertas_interrupt(priv->wlan_dev.netdev); + spin_unlock(&priv->adapter->driver_lock); + goto rx_exit; + default: + kfree_skb(skb); + break; + } + +setup_for_next: + if_usb_submit_rx_urb(priv); +rx_exit: + LEAVE(); + return; +} + +/** + * @brief This function downloads data to FW + * @param priv pointer to wlan_private structure + * @param type type of data + * @param buf pointer to data buffer + * @param len number of bytes + * @return 0 or -1 + */ +int libertas_sbi_host_to_card(wlan_private * priv, u8 type, u8 * payload, u16 nb) +{ + int ret = -1; + u32 tmp; + struct usb_card_rec *cardp = (struct usb_card_rec *)priv->wlan_dev.card; + + lbs_dev_dbg(1, &cardp->udev->dev,"*** type = %u\n", type); + lbs_dev_dbg(1, &cardp->udev->dev,"size after = %d\n", nb); + + if (type == MVMS_CMD) { + tmp = cpu_to_le32(CMD_TYPE_REQUEST); + priv->wlan_dev.dnld_sent = DNLD_CMD_SENT; + memcpy(cardp->bulk_out_buffer, (u8 *) & tmp, + MESSAGE_HEADER_LEN); + + } else { + tmp = cpu_to_le32(CMD_TYPE_DATA); + priv->wlan_dev.dnld_sent = DNLD_DATA_SENT; + memcpy(cardp->bulk_out_buffer, (u8 *) & tmp, + MESSAGE_HEADER_LEN); + } + + memcpy((cardp->bulk_out_buffer + MESSAGE_HEADER_LEN), payload, nb); + + ret = + usb_tx_block(priv, cardp->bulk_out_buffer, nb + MESSAGE_HEADER_LEN); + + return ret; +} + +/* called with adapter->driver_lock held */ +int libertas_sbi_get_int_status(wlan_private * priv, u8 * ireg) +{ + struct usb_card_rec *cardp = priv->wlan_dev.card; + + *ireg = cardp->usb_int_cause; + cardp->usb_int_cause = 0; + + lbs_dev_dbg(1, &cardp->udev->dev,"Int cause is 0x%X\n", *ireg); + + return 0; +} + +int libertas_sbi_read_event_cause(wlan_private * priv) +{ + struct usb_card_rec *cardp = priv->wlan_dev.card; + priv->adapter->eventcause = cardp->usb_event_cause; + /* Re-submit rx urb here to avoid event lost issue */ + if_usb_submit_rx_urb(priv); + return 0; +} + +int reset_device(wlan_private *priv) +{ + int ret; + + ret = libertas_prepare_and_send_command(priv, cmd_802_11_reset, + cmd_act_halt, 0, 0, NULL); + msleep_interruptible(10); + + return ret; +} + +int libertas_sbi_unregister_dev(wlan_private * priv) +{ + int ret = 0; + + /* Need to send a Reset command to device before USB resources freed + * and wlan_remove_card() called, then device can handle FW download + * again. + */ + if (priv) + reset_device(priv); + + return ret; +} + + +/** + * @brief This function register usb device and initialize parameter + * @param priv pointer to wlan_private + * @return 0 or -1 + */ +int libertas_sbi_register_dev(wlan_private * priv) +{ + + struct usb_card_rec *cardp = (struct usb_card_rec *)priv->wlan_dev.card; + ENTER(); + + cardp->priv = priv; + cardp->eth_dev = priv->wlan_dev.netdev; + priv->hotplug_device = &(cardp->udev->dev); + + SET_NETDEV_DEV(cardp->eth_dev, &(cardp->udev->dev)); + + lbs_dev_dbg(1, &cardp->udev->dev, "udev pointer is at %p\n", + cardp->udev); + + LEAVE(); + return 0; +} + + + +int libertas_sbi_prog_firmware(wlan_private * priv) +{ + struct usb_card_rec *cardp = priv->wlan_dev.card; + int i = 0; + static int reset_count = 10; + + ENTER(); + + cardp->rinfo.priv = priv; + +restart: + if (if_usb_submit_rx_urb_fwload(priv) < 0) { + lbs_dev_dbg(1, &cardp->udev->dev, "URB submission is failed\n"); + LEAVE(); + return -1; + } + +#ifdef SUPPORT_BOOT_COMMAND + cardp->bootcmdresp = 0; + do { + int j = 0; + i++; + /* Issue Boot command = 1, Boot from Download-FW */ + if_usb_issue_boot_command(priv, BOOT_CMD_FW_BY_USB); + /* wait for command response */ + do { + j++; + msleep_interruptible(100); + } while (cardp->bootcmdresp == 0 && j < 10); + } while (cardp->bootcmdresp == 0 && i < 5); + + if (cardp->bootcmdresp == 0) { + if (--reset_count >= 0) { + libertas_do_reset(priv); + goto restart; + } + return -1; + } +#endif + + i = 0; + priv->adapter->fw_ready = 0; + + cardp->totalbytes = 0; + cardp->fwlastblksent = 0; + cardp->CRC_OK = 1; + cardp->fwdnldover = 0; + cardp->fwseqnum = -1; + cardp->totalbytes = 0; + cardp->fwfinalblk = 0; + + if_prog_firmware(priv); + + do { + lbs_dev_dbg(1, &cardp->udev->dev,"Wlan sched timeout\n"); + i++; + msleep_interruptible(100); + if (priv->adapter->surpriseremoved || i >= 20) + break; + } while (!cardp->fwdnldover); + + if (!cardp->fwdnldover) { + lbs_pr_info("failed to load fw, resetting device!\n"); + if (--reset_count >= 0) { + libertas_do_reset(priv); + goto restart; + } + + lbs_pr_info("FW download failure, time = %d ms\n", i * 100); + LEAVE(); + return -1; + } + + if_usb_submit_rx_urb(priv); + + /* Delay 200 ms to waiting for the FW ready */ + msleep_interruptible(200); + + priv->adapter->fw_ready = 1; + + LEAVE(); + return 0; +} + +/** + * @brief Given a usb_card_rec return its wlan_private + * @param card pointer to a usb_card_rec + * @return pointer to wlan_private + */ +wlan_private *libertas_sbi_get_priv(void *card) +{ + struct usb_card_rec *cardp = card; + return cardp->priv; +} + +#ifdef ENABLE_PM +int libertas_sbi_suspend(wlan_private * priv) +{ + return 0; +} + +int libertas_sbi_resume(wlan_private * priv) +{ + return 0; +} +#endif + +#ifdef CONFIG_PM +static int if_usb_suspend(struct usb_interface *intf, pm_message_t message) +{ + struct usb_card_rec *cardp = usb_get_intfdata(intf); + wlan_private *priv = cardp->priv; + + ENTER(); + + if (priv->adapter->psstate != PS_STATE_FULL_POWER) + return -1; + + netif_device_detach(cardp->eth_dev); + + /* Unlink tx & rx urb */ + usb_kill_urb(cardp->tx_urb); + usb_kill_urb(cardp->rx_urb); + + cardp->rx_urb_recall = 1; + + LEAVE(); + return 0; +} + +static int if_usb_resume(struct usb_interface *intf) +{ + struct usb_card_rec *cardp = usb_get_intfdata(intf); + + ENTER(); + + cardp->rx_urb_recall = 0; + + if_usb_submit_rx_urb(cardp->priv); + + netif_device_attach(cardp->eth_dev); + + LEAVE(); + return 0; +} +#else +#define if_usb_suspend NULL +#define if_usb_resume NULL +#endif + +static struct usb_driver if_usb_driver = { + /* driver name */ + .name = usbdriver_name, + /* probe function name */ + .probe = if_usb_probe, + /* disconnect function name */ + .disconnect = if_usb_disconnect, + /* device signature table */ + .id_table = if_usb_table, + .suspend = if_usb_suspend, + .resume = if_usb_resume, +}; + +/** + * @brief This function registers driver. + * @param add pointer to add_card callback function + * @param remove pointer to remove card callback function + * @param arg pointer to call back function parameter + * @return dummy success variable + */ +int libertas_sbi_register(void) +{ + /* + * API registers the Marvell USB driver + * to the USB system + */ + usb_register(&if_usb_driver); + + /* Return success to wlan layer */ + return 0; +} + +/** + * @brief This function removes usb driver. + * @return N/A + */ +void libertas_sbi_unregister(void) +{ + /* API unregisters the driver from USB subsystem */ + usb_deregister(&if_usb_driver); + return; +} diff --git a/drivers/net/wireless/libertas/if_usb.h b/drivers/net/wireless/libertas/if_usb.h new file mode 100644 index 00000000000..785116720bc --- /dev/null +++ b/drivers/net/wireless/libertas/if_usb.h @@ -0,0 +1,109 @@ +/** + * This file contains definition for USB interface. + */ +#define CMD_TYPE_REQUEST 0xF00DFACE +#define CMD_TYPE_DATA 0xBEADC0DE +#define CMD_TYPE_INDICATION 0xBEEFFACE + +#define IPFIELD_ALIGN_OFFSET 2 + +#define USB8388_VID_1 0x1286 +#define USB8388_PID_1 0x2001 +#define USB8388_VID_2 0x05a3 +#define USB8388_PID_2 0x8388 + +#ifdef SUPPORT_BOOT_COMMAND +#define BOOT_CMD_FW_BY_USB 0x01 +#define BOOT_CMD_FW_IN_EEPROM 0x02 +#define BOOT_CMD_UPDATE_BOOT2 0x03 +#define BOOT_CMD_UPDATE_FW 0x04 +#define BOOT_CMD_MAGIC_NUMBER 0x4C56524D /* M=>0x4D,R=>0x52,V=>0x56,L=>0x4C */ + +struct bootcmdstr +{ + u32 u32magicnumber; + u8 u8cmd_tag; + u8 au8dumy[11]; +}; + +#define BOOT_CMD_RESP_OK 0x0001 +#define BOOT_CMD_RESP_FAIL 0x0000 + +struct bootcmdrespStr +{ + u32 u32magicnumber; + u8 u8cmd_tag; + u8 u8result; + u8 au8dumy[2]; +}; +#endif /* SUPPORT_BOOT_COMMAND */ + +/* read callback private data */ +struct read_cb_info { + wlan_private *priv; + struct sk_buff *skb; +}; + +/** USB card description structure*/ +struct usb_card_rec { + struct net_device *eth_dev; + struct usb_device *udev; + struct urb *rx_urb, *tx_urb; + void *priv; + struct read_cb_info rinfo; + + int bulk_in_size; + u8 bulk_in_endpointAddr; + + u8 *bulk_out_buffer; + int bulk_out_size; + u8 bulk_out_endpointAddr; + + u8 CRC_OK; + u32 fwseqnum; + u32 lastseqnum; + u32 totalbytes; + u32 fwlastblksent; + u8 fwdnldover; + u8 fwfinalblk; + + u32 usb_event_cause; + u8 usb_int_cause; + + u8 rx_urb_recall; + + u8 bootcmdresp; +}; + +/** fwheader */ +struct fwheader { + u32 dnldcmd; + u32 baseaddr; + u32 datalength; + u32 CRC; +}; + +#define FW_MAX_DATA_BLK_SIZE 600 +/** FWData */ +struct FWData { + struct fwheader fwheader; + u32 seqnum; + u8 data[FW_MAX_DATA_BLK_SIZE]; +}; + +/** fwsyncheader */ +struct fwsyncheader { + u32 cmd; + u32 seqnum; +}; + +#define FW_HAS_DATA_TO_RECV 0x00000001 +#define FW_HAS_LAST_BLOCK 0x00000004 + +#define FW_DATA_XMIT_SIZE \ + sizeof(struct fwheader) + fwdata->fwheader.datalength + sizeof(u32) + +int usb_tx_block(wlan_private *priv, u8 *payload, u16 nb); +void if_usb_free(struct usb_card_rec *cardp); +int if_usb_issue_boot_command(wlan_private *priv, int ivalue); + diff --git a/drivers/net/wireless/libertas/ioctl.c b/drivers/net/wireless/libertas/ioctl.c new file mode 100644 index 00000000000..82b39642423 --- /dev/null +++ b/drivers/net/wireless/libertas/ioctl.c @@ -0,0 +1,2500 @@ +/** + * This file contains ioctl functions + */ + +#include <linux/ctype.h> +#include <linux/delay.h> +#include <linux/if.h> +#include <linux/if_arp.h> +#include <linux/wireless.h> + +#include <net/iw_handler.h> +#include <net/ieee80211.h> + +#include "host.h" +#include "radiotap.h" +#include "decl.h" +#include "defs.h" +#include "dev.h" +#include "join.h" +#include "wext.h" + +#define MAX_SCAN_CELL_SIZE (IW_EV_ADDR_LEN + \ + IW_ESSID_MAX_SIZE + \ + IW_EV_UINT_LEN + IW_EV_FREQ_LEN + \ + IW_EV_QUAL_LEN + IW_ESSID_MAX_SIZE + \ + IW_EV_PARAM_LEN + 40) /* 40 for WPAIE */ + +#define WAIT_FOR_SCAN_RRESULT_MAX_TIME (10 * HZ) + +static int setrxantenna(wlan_private * priv, int mode) +{ + int ret = 0; + wlan_adapter *adapter = priv->adapter; + + if (mode != RF_ANTENNA_1 && mode != RF_ANTENNA_2 + && mode != RF_ANTENNA_AUTO) { + return -EINVAL; + } + + adapter->rxantennamode = mode; + + lbs_pr_debug(1, "SET RX Antenna mode to 0x%04x\n", adapter->rxantennamode); + + ret = libertas_prepare_and_send_command(priv, cmd_802_11_rf_antenna, + cmd_act_set_rx, + cmd_option_waitforrsp, 0, + &adapter->rxantennamode); + return ret; +} + +static int settxantenna(wlan_private * priv, int mode) +{ + int ret = 0; + wlan_adapter *adapter = priv->adapter; + + if ((mode != RF_ANTENNA_1) && (mode != RF_ANTENNA_2) + && (mode != RF_ANTENNA_AUTO)) { + return -EINVAL; + } + + adapter->txantennamode = mode; + + lbs_pr_debug(1, "SET TX Antenna mode to 0x%04x\n", adapter->txantennamode); + + ret = libertas_prepare_and_send_command(priv, cmd_802_11_rf_antenna, + cmd_act_set_tx, + cmd_option_waitforrsp, 0, + &adapter->txantennamode); + + return ret; +} + +static int getrxantenna(wlan_private * priv, char *buf) +{ + int ret = 0; + wlan_adapter *adapter = priv->adapter; + + // clear it, so we will know if the value + // returned below is correct or not. + adapter->rxantennamode = 0; + + ret = libertas_prepare_and_send_command(priv, cmd_802_11_rf_antenna, + cmd_act_get_rx, + cmd_option_waitforrsp, 0, NULL); + + if (ret) { + LEAVE(); + return ret; + } + + lbs_pr_debug(1, "Get Rx Antenna mode:0x%04x\n", adapter->rxantennamode); + + return sprintf(buf, "0x%04x", adapter->rxantennamode) + 1; +} + +static int gettxantenna(wlan_private * priv, char *buf) +{ + int ret = 0; + wlan_adapter *adapter = priv->adapter; + + // clear it, so we will know if the value + // returned below is correct or not. + adapter->txantennamode = 0; + + ret = libertas_prepare_and_send_command(priv, cmd_802_11_rf_antenna, + cmd_act_get_tx, + cmd_option_waitforrsp, 0, NULL); + + if (ret) { + LEAVE(); + return ret; + } + + lbs_pr_debug(1, "Get Tx Antenna mode:0x%04x\n", adapter->txantennamode); + + return sprintf(buf, "0x%04x", adapter->txantennamode) + 1; +} + +static int wlan_set_region(wlan_private * priv, u16 region_code) +{ + int i; + + for (i = 0; i < MRVDRV_MAX_REGION_CODE; i++) { + // use the region code to search for the index + if (region_code == libertas_region_code_to_index[i]) { + priv->adapter->regiontableindex = (u16) i; + priv->adapter->regioncode = region_code; + break; + } + } + + // if it's unidentified region code + if (i >= MRVDRV_MAX_REGION_CODE) { + lbs_pr_debug(1, "region Code not identified\n"); + LEAVE(); + return -1; + } + + if (libertas_set_regiontable(priv, priv->adapter->regioncode, 0)) { + LEAVE(); + return -EINVAL; + } + + return 0; +} + +/** + * @brief Get/Set Firmware wakeup method + * + * @param priv A pointer to wlan_private structure + * @param wrq A pointer to user data + * @return 0--success, otherwise fail + */ +static int wlan_txcontrol(wlan_private * priv, struct iwreq *wrq) +{ + wlan_adapter *adapter = priv->adapter; + int data; + ENTER(); + + if ((int)wrq->u.data.length == 0) { + if (copy_to_user + (wrq->u.data.pointer, &adapter->pkttxctrl, sizeof(u32))) { + lbs_pr_alert("copy_to_user failed!\n"); + return -EFAULT; + } + } else { + if ((int)wrq->u.data.length > 1) { + lbs_pr_alert("ioctl too many args!\n"); + return -EFAULT; + } + if (copy_from_user(&data, wrq->u.data.pointer, sizeof(int))) { + lbs_pr_alert("Copy from user failed\n"); + return -EFAULT; + } + + adapter->pkttxctrl = (u32) data; + } + + wrq->u.data.length = 1; + + LEAVE(); + return 0; +} + +/** + * @brief Get/Set NULL Package generation interval + * + * @param priv A pointer to wlan_private structure + * @param wrq A pointer to user data + * @return 0--success, otherwise fail + */ +static int wlan_null_pkt_interval(wlan_private * priv, struct iwreq *wrq) +{ + wlan_adapter *adapter = priv->adapter; + int data; + ENTER(); + + if ((int)wrq->u.data.length == 0) { + data = adapter->nullpktinterval; + + if (copy_to_user(wrq->u.data.pointer, &data, sizeof(int))) { + lbs_pr_alert( "copy_to_user failed!\n"); + return -EFAULT; + } + } else { + if ((int)wrq->u.data.length > 1) { + lbs_pr_alert( "ioctl too many args!\n"); + return -EFAULT; + } + if (copy_from_user(&data, wrq->u.data.pointer, sizeof(int))) { + lbs_pr_debug(1, "Copy from user failed\n"); + return -EFAULT; + } + + adapter->nullpktinterval = data; + } + + wrq->u.data.length = 1; + + LEAVE(); + return 0; +} + +static int wlan_get_rxinfo(wlan_private * priv, struct iwreq *wrq) +{ + wlan_adapter *adapter = priv->adapter; + int data[2]; + ENTER(); + data[0] = adapter->SNR[TYPE_RXPD][TYPE_NOAVG]; + data[1] = adapter->rxpd_rate; + if (copy_to_user(wrq->u.data.pointer, data, sizeof(int) * 2)) { + lbs_pr_debug(1, "Copy to user failed\n"); + return -EFAULT; + } + wrq->u.data.length = 2; + LEAVE(); + return 0; +} + +static int wlan_get_snr(wlan_private * priv, struct iwreq *wrq) +{ + int ret = 0; + wlan_adapter *adapter = priv->adapter; + int data[4]; + + ENTER(); + memset(data, 0, sizeof(data)); + if (wrq->u.data.length) { + if (copy_from_user(data, wrq->u.data.pointer, + min_t(size_t, wrq->u.data.length, 4) * sizeof(int))) + return -EFAULT; + } + if ((wrq->u.data.length == 0) || (data[0] == 0) || (data[0] == 1)) { + if (adapter->connect_status == libertas_connected) { + ret = libertas_prepare_and_send_command(priv, + cmd_802_11_rssi, + 0, + cmd_option_waitforrsp, + 0, NULL); + + if (ret) { + LEAVE(); + return ret; + } + } + } + + if (wrq->u.data.length == 0) { + data[0] = adapter->SNR[TYPE_BEACON][TYPE_NOAVG]; + data[1] = adapter->SNR[TYPE_BEACON][TYPE_AVG]; + data[2] = adapter->SNR[TYPE_RXPD][TYPE_NOAVG]; + data[3] = adapter->SNR[TYPE_RXPD][TYPE_AVG] / AVG_SCALE; + if (copy_to_user(wrq->u.data.pointer, data, sizeof(int) * 4)) + return -EFAULT; + wrq->u.data.length = 4; + } else if (data[0] == 0) { + data[0] = adapter->SNR[TYPE_BEACON][TYPE_NOAVG]; + if (copy_to_user(wrq->u.data.pointer, data, sizeof(int))) + return -EFAULT; + wrq->u.data.length = 1; + } else if (data[0] == 1) { + data[0] = adapter->SNR[TYPE_BEACON][TYPE_AVG]; + if (copy_to_user(wrq->u.data.pointer, data, sizeof(int))) + return -EFAULT; + wrq->u.data.length = 1; + } else if (data[0] == 2) { + data[0] = adapter->SNR[TYPE_RXPD][TYPE_NOAVG]; + if (copy_to_user(wrq->u.data.pointer, data, sizeof(int))) + return -EFAULT; + wrq->u.data.length = 1; + } else if (data[0] == 3) { + data[0] = adapter->SNR[TYPE_RXPD][TYPE_AVG] / AVG_SCALE; + if (copy_to_user(wrq->u.data.pointer, data, sizeof(int))) + return -EFAULT; + wrq->u.data.length = 1; + } else + return -ENOTSUPP; + + LEAVE(); + return 0; +} + +static int wlan_beacon_interval(wlan_private * priv, struct iwreq *wrq) +{ + int data; + wlan_adapter *adapter = priv->adapter; + + if (wrq->u.data.length > 0) { + if (copy_from_user(&data, wrq->u.data.pointer, sizeof(int))) + return -EFAULT; + + lbs_pr_debug(1, "WLAN SET BEACON INTERVAL: %d\n", data); + if ((data > MRVDRV_MAX_BEACON_INTERVAL) + || (data < MRVDRV_MIN_BEACON_INTERVAL)) + return -ENOTSUPP; + adapter->beaconperiod = data; + } + data = adapter->beaconperiod; + if (copy_to_user(wrq->u.data.pointer, &data, sizeof(int))) + return -EFAULT; + + wrq->u.data.length = 1; + + return 0; +} + +static int wlan_get_rssi(wlan_private * priv, struct iwreq *wrq) +{ + int ret = 0; + wlan_adapter *adapter = priv->adapter; + int temp; + int data = 0; + int *val; + + ENTER(); + data = SUBCMD_DATA(wrq); + if ((data == 0) || (data == 1)) { + ret = libertas_prepare_and_send_command(priv, + cmd_802_11_rssi, + 0, cmd_option_waitforrsp, + 0, NULL); + if (ret) { + LEAVE(); + return ret; + } + } + + switch (data) { + case 0: + + temp = CAL_RSSI(adapter->SNR[TYPE_BEACON][TYPE_NOAVG], + adapter->NF[TYPE_BEACON][TYPE_NOAVG]); + break; + case 1: + temp = CAL_RSSI(adapter->SNR[TYPE_BEACON][TYPE_AVG], + adapter->NF[TYPE_BEACON][TYPE_AVG]); + break; + case 2: + temp = CAL_RSSI(adapter->SNR[TYPE_RXPD][TYPE_NOAVG], + adapter->NF[TYPE_RXPD][TYPE_NOAVG]); + break; + case 3: + temp = CAL_RSSI(adapter->SNR[TYPE_RXPD][TYPE_AVG] / AVG_SCALE, + adapter->NF[TYPE_RXPD][TYPE_AVG] / AVG_SCALE); + break; + default: + return -ENOTSUPP; + } + val = (int *)wrq->u.name; + *val = temp; + + LEAVE(); + return 0; +} + +static int wlan_get_nf(wlan_private * priv, struct iwreq *wrq) +{ + int ret = 0; + wlan_adapter *adapter = priv->adapter; + int temp; + int data = 0; + int *val; + + data = SUBCMD_DATA(wrq); + if ((data == 0) || (data == 1)) { + ret = libertas_prepare_and_send_command(priv, + cmd_802_11_rssi, + 0, cmd_option_waitforrsp, + 0, NULL); + + if (ret) { + LEAVE(); + return ret; + } + } + + switch (data) { + case 0: + temp = adapter->NF[TYPE_BEACON][TYPE_NOAVG]; + break; + case 1: + temp = adapter->NF[TYPE_BEACON][TYPE_AVG]; + break; + case 2: + temp = adapter->NF[TYPE_RXPD][TYPE_NOAVG]; + break; + case 3: + temp = adapter->NF[TYPE_RXPD][TYPE_AVG] / AVG_SCALE; + break; + default: + return -ENOTSUPP; + } + + temp = CAL_NF(temp); + + lbs_pr_debug(1, "%s: temp = %d\n", __FUNCTION__, temp); + val = (int *)wrq->u.name; + *val = temp; + return 0; +} + +static int wlan_get_txrate_ioctl(wlan_private * priv, struct ifreq *req) +{ + wlan_adapter *adapter = priv->adapter; + int *pdata; + struct iwreq *wrq = (struct iwreq *)req; + int ret = 0; + adapter->txrate = 0; + lbs_pr_debug(1, "wlan_get_txrate_ioctl\n"); + ret = libertas_prepare_and_send_command(priv, cmd_802_11_tx_rate_query, + cmd_act_get, cmd_option_waitforrsp, + 0, NULL); + if (ret) + return ret; + + pdata = (int *)wrq->u.name; + *pdata = (int)adapter->txrate; + return 0; +} + +static int wlan_get_adhoc_status_ioctl(wlan_private * priv, struct iwreq *wrq) +{ + char status[64]; + wlan_adapter *adapter = priv->adapter; + + memset(status, 0, sizeof(status)); + + switch (adapter->inframode) { + case wlan802_11ibss: + if (adapter->connect_status == libertas_connected) { + if (adapter->adhoccreate) + memcpy(&status, "AdhocStarted", sizeof(status)); + else + memcpy(&status, "AdhocJoined", sizeof(status)); + } else { + memcpy(&status, "AdhocIdle", sizeof(status)); + } + break; + case wlan802_11infrastructure: + memcpy(&status, "Inframode", sizeof(status)); + break; + default: + memcpy(&status, "AutoUnknownmode", sizeof(status)); + break; + } + + lbs_pr_debug(1, "status = %s\n", status); + wrq->u.data.length = strlen(status) + 1; + + if (wrq->u.data.pointer) { + if (copy_to_user(wrq->u.data.pointer, + &status, wrq->u.data.length)) + return -EFAULT; + } + + LEAVE(); + return 0; +} + +/** + * @brief Set/Get WPA IE + * @param priv A pointer to wlan_private structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int wlan_setwpaie_ioctl(wlan_private * priv, struct ifreq *req) +{ + struct iwreq *wrq = (struct iwreq *)req; + wlan_adapter *adapter = priv->adapter; + int ret = 0; + + ENTER(); + + if (wrq->u.data.length) { + if (wrq->u.data.length > sizeof(adapter->wpa_ie)) { + lbs_pr_debug(1, "failed to copy WPA IE, too big \n"); + return -EFAULT; + } + if (copy_from_user(adapter->wpa_ie, wrq->u.data.pointer, + wrq->u.data.length)) { + lbs_pr_debug(1, "failed to copy WPA IE \n"); + return -EFAULT; + } + adapter->wpa_ie_len = wrq->u.data.length; + lbs_pr_debug(1, "Set wpa_ie_len=%d IE=%#x\n", adapter->wpa_ie_len, + adapter->wpa_ie[0]); + lbs_dbg_hex("wpa_ie", adapter->wpa_ie, adapter->wpa_ie_len); + if (adapter->wpa_ie[0] == WPA_IE) + adapter->secinfo.WPAenabled = 1; + else if (adapter->wpa_ie[0] == WPA2_IE) + adapter->secinfo.WPA2enabled = 1; + else { + adapter->secinfo.WPAenabled = 0; + adapter->secinfo.WPA2enabled = 0; + } + } else { + memset(adapter->wpa_ie, 0, sizeof(adapter->wpa_ie)); + adapter->wpa_ie_len = wrq->u.data.length; + lbs_pr_debug(1, "Reset wpa_ie_len=%d IE=%#x\n", + adapter->wpa_ie_len, adapter->wpa_ie[0]); + adapter->secinfo.WPAenabled = 0; + adapter->secinfo.WPA2enabled = 0; + } + + // enable/disable RSN in firmware if WPA is enabled/disabled + // depending on variable adapter->secinfo.WPAenabled is set or not + ret = libertas_prepare_and_send_command(priv, cmd_802_11_enable_rsn, + cmd_act_set, cmd_option_waitforrsp, + 0, NULL); + + LEAVE(); + return ret; +} + +/** + * @brief Set Auto prescan + * @param priv A pointer to wlan_private structure + * @param wrq A pointer to iwreq structure + * @return 0 --success, otherwise fail + */ +static int wlan_subcmd_setprescan_ioctl(wlan_private * priv, struct iwreq *wrq) +{ + int data; + wlan_adapter *adapter = priv->adapter; + int *val; + + data = SUBCMD_DATA(wrq); + lbs_pr_debug(1, "WLAN_SUBCMD_SET_PRESCAN %d\n", data); + adapter->prescan = data; + + val = (int *)wrq->u.name; + *val = data; + return 0; +} + +static int wlan_set_multiple_dtim_ioctl(wlan_private * priv, struct ifreq *req) +{ + struct iwreq *wrq = (struct iwreq *)req; + u32 mdtim; + int idata; + int ret = -EINVAL; + + ENTER(); + + idata = SUBCMD_DATA(wrq); + mdtim = (u32) idata; + if (((mdtim >= MRVDRV_MIN_MULTIPLE_DTIM) + && (mdtim <= MRVDRV_MAX_MULTIPLE_DTIM)) + || (mdtim == MRVDRV_IGNORE_MULTIPLE_DTIM)) { + priv->adapter->multipledtim = mdtim; + ret = 0; + } + if (ret) + lbs_pr_debug(1, "Invalid parameter, multipledtim not changed.\n"); + + LEAVE(); + return ret; +} + +/** + * @brief Set authentication mode + * @param priv A pointer to wlan_private structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int wlan_setauthalg_ioctl(wlan_private * priv, struct ifreq *req) +{ + int alg; + struct iwreq *wrq = (struct iwreq *)req; + wlan_adapter *adapter = priv->adapter; + + if (wrq->u.data.flags == 0) { + //from iwpriv subcmd + alg = SUBCMD_DATA(wrq); + } else { + //from wpa_supplicant subcmd + if (copy_from_user(&alg, wrq->u.data.pointer, sizeof(alg))) { + lbs_pr_debug(1, "Copy from user failed\n"); + return -EFAULT; + } + } + + lbs_pr_debug(1, "auth alg is %#x\n", alg); + + switch (alg) { + case AUTH_ALG_SHARED_KEY: + adapter->secinfo.authmode = wlan802_11authmodeshared; + break; + case AUTH_ALG_NETWORK_EAP: + adapter->secinfo.authmode = + wlan802_11authmodenetworkEAP; + break; + case AUTH_ALG_OPEN_SYSTEM: + default: + adapter->secinfo.authmode = wlan802_11authmodeopen; + break; + } + return 0; +} + +/** + * @brief Set 802.1x authentication mode + * @param priv A pointer to wlan_private structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int wlan_set8021xauthalg_ioctl(wlan_private * priv, struct ifreq *req) +{ + int alg; + struct iwreq *wrq = (struct iwreq *)req; + + if (wrq->u.data.flags == 0) { + //from iwpriv subcmd + alg = SUBCMD_DATA(wrq); + } else { + //from wpa_supplicant subcmd + if (copy_from_user(&alg, wrq->u.data.pointer, sizeof(int))) { + lbs_pr_debug(1, "Copy from user failed\n"); + return -EFAULT; + } + } + lbs_pr_debug(1, "802.1x auth alg is %#x\n", alg); + priv->adapter->secinfo.auth1xalg = alg; + return 0; +} + +static int wlan_setencryptionmode_ioctl(wlan_private * priv, struct ifreq *req) +{ + int mode; + struct iwreq *wrq = (struct iwreq *)req; + + ENTER(); + + if (wrq->u.data.flags == 0) { + //from iwpriv subcmd + mode = SUBCMD_DATA(wrq); + } else { + //from wpa_supplicant subcmd + if (copy_from_user(&mode, wrq->u.data.pointer, sizeof(int))) { + lbs_pr_debug(1, "Copy from user failed\n"); + return -EFAULT; + } + } + lbs_pr_debug(1, "encryption mode is %#x\n", mode); + priv->adapter->secinfo.Encryptionmode = mode; + + LEAVE(); + return 0; +} + +static void adjust_mtu(wlan_private * priv) +{ + int mtu_increment = 0; + + if (priv->adapter->linkmode == WLAN_LINKMODE_802_11) + mtu_increment += sizeof(struct ieee80211_hdr_4addr); + + if (priv->adapter->radiomode == WLAN_RADIOMODE_RADIOTAP) + mtu_increment += max(sizeof(struct tx_radiotap_hdr), + sizeof(struct rx_radiotap_hdr)); + priv->wlan_dev.netdev->mtu = ETH_FRAME_LEN + - sizeof(struct ethhdr) + + mtu_increment; +} + +/** + * @brief Set Link-Layer Layer mode + * @param priv A pointer to wlan_private structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int wlan_set_linkmode_ioctl(wlan_private * priv, struct ifreq *req) +{ + int mode; + + mode = (int)((struct ifreq *)((u8 *) req + 4))->ifr_data; + + switch (mode) { + case WLAN_LINKMODE_802_3: + priv->adapter->linkmode = mode; + break; + case WLAN_LINKMODE_802_11: + priv->adapter->linkmode = mode; + break; + default: + lbs_pr_info("usb8388-5: invalid link-layer mode (%#x)\n", + mode); + return -EINVAL; + break; + } + lbs_pr_debug(1, "usb8388-5: link-layer mode is %#x\n", mode); + + adjust_mtu(priv); + + return 0; +} + +/** + * @brief Set Radio header mode + * @param priv A pointer to wlan_private structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int wlan_set_radiomode_ioctl(wlan_private * priv, struct ifreq *req) +{ + int mode; + + mode = (int)((struct ifreq *)((u8 *) req + 4))->ifr_data; + + switch (mode) { + case WLAN_RADIOMODE_NONE: + priv->adapter->radiomode = mode; + break; + case WLAN_RADIOMODE_RADIOTAP: + priv->adapter->radiomode = mode; + break; + default: + lbs_pr_debug(1, "usb8388-5: invalid radio header mode (%#x)\n", + mode); + return -EINVAL; + } + lbs_pr_debug(1, "usb8388-5: radio-header mode is %#x\n", mode); + + adjust_mtu(priv); + return 0; +} + +/** + * @brief Set Debug header mode + * @param priv A pointer to wlan_private structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int wlan_set_debugmode_ioctl(wlan_private * priv, struct ifreq *req) +{ + priv->adapter->debugmode = (int)((struct ifreq *) + ((u8 *) req + 4))->ifr_data; + return 0; +} + +static int wlan_subcmd_getrxantenna_ioctl(wlan_private * priv, + struct ifreq *req) +{ + int len; + char buf[8]; + struct iwreq *wrq = (struct iwreq *)req; + + lbs_pr_debug(1, "WLAN_SUBCMD_GETRXANTENNA\n"); + len = getrxantenna(priv, buf); + + wrq->u.data.length = len; + if (wrq->u.data.pointer) { + if (copy_to_user(wrq->u.data.pointer, &buf, len)) { + lbs_pr_debug(1, "CopyToUser failed\n"); + return -EFAULT; + } + } + + return 0; +} + +static int wlan_subcmd_gettxantenna_ioctl(wlan_private * priv, + struct ifreq *req) +{ + int len; + char buf[8]; + struct iwreq *wrq = (struct iwreq *)req; + + lbs_pr_debug(1, "WLAN_SUBCMD_GETTXANTENNA\n"); + len = gettxantenna(priv, buf); + + wrq->u.data.length = len; + if (wrq->u.data.pointer) { + if (copy_to_user(wrq->u.data.pointer, &buf, len)) { + lbs_pr_debug(1, "CopyToUser failed\n"); + return -EFAULT; + } + } + return 0; +} + +/** + * @brief Get the MAC TSF value from the firmware + * + * @param priv A pointer to wlan_private structure + * @param wrq A pointer to iwreq structure containing buffer + * space to store a TSF value retrieved from the firmware + * + * @return 0 if successful; IOCTL error code otherwise + */ +static int wlan_get_tsf_ioctl(wlan_private * priv, struct iwreq *wrq) +{ + u64 tsfval; + int ret; + + ret = libertas_prepare_and_send_command(priv, + cmd_get_tsf, + 0, cmd_option_waitforrsp, 0, &tsfval); + + lbs_pr_debug(1, "IOCTL: Get TSF = 0x%016llx\n", tsfval); + + if (ret != 0) { + lbs_pr_debug(1, "IOCTL: Get TSF; command exec failed\n"); + ret = -EFAULT; + } else { + if (copy_to_user(wrq->u.data.pointer, + &tsfval, + min_t(size_t, wrq->u.data.length, + sizeof(tsfval))) != 0) { + + lbs_pr_debug(1, "IOCTL: Get TSF; Copy to user failed\n"); + ret = -EFAULT; + } else { + ret = 0; + } + } + return ret; +} + +/** + * @brief Get/Set adapt rate + * @param priv A pointer to wlan_private structure + * @param wrq A pointer to iwreq structure + * @return 0 --success, otherwise fail + */ +static int wlan_adapt_rateset(wlan_private * priv, struct iwreq *wrq) +{ + int ret; + wlan_adapter *adapter = priv->adapter; + int data[2]; + + memset(data, 0, sizeof(data)); + if (!wrq->u.data.length) { + lbs_pr_debug(1, "Get ADAPT RATE SET\n"); + ret = libertas_prepare_and_send_command(priv, + cmd_802_11_rate_adapt_rateset, + cmd_act_get, + cmd_option_waitforrsp, 0, NULL); + data[0] = adapter->enablehwauto; + data[1] = adapter->ratebitmap; + if (copy_to_user(wrq->u.data.pointer, data, sizeof(int) * 2)) { + lbs_pr_debug(1, "Copy to user failed\n"); + return -EFAULT; + } +#define GET_TWO_INT 2 + wrq->u.data.length = GET_TWO_INT; + } else { + lbs_pr_debug(1, "Set ADAPT RATE SET\n"); + if (wrq->u.data.length > 2) + return -EINVAL; + if (copy_from_user + (data, wrq->u.data.pointer, + sizeof(int) * wrq->u.data.length)) { + lbs_pr_debug(1, "Copy from user failed\n"); + return -EFAULT; + } + + adapter->enablehwauto = data[0]; + adapter->ratebitmap = data[1]; + ret = libertas_prepare_and_send_command(priv, + cmd_802_11_rate_adapt_rateset, + cmd_act_set, + cmd_option_waitforrsp, 0, NULL); + } + return ret; +} + +/** + * @brief Get/Set inactivity timeout + * @param priv A pointer to wlan_private structure + * @param wrq A pointer to iwreq structure + * @return 0 --success, otherwise fail + */ +static int wlan_inactivity_timeout(wlan_private * priv, struct iwreq *wrq) +{ + int ret; + int data = 0; + u16 timeout = 0; + + ENTER(); + if (wrq->u.data.length > 1) + return -ENOTSUPP; + + if (wrq->u.data.length == 0) { + /* Get */ + ret = libertas_prepare_and_send_command(priv, + cmd_802_11_inactivity_timeout, + cmd_act_get, + cmd_option_waitforrsp, 0, + &timeout); + data = timeout; + if (copy_to_user(wrq->u.data.pointer, &data, sizeof(int))) { + lbs_pr_debug(1, "Copy to user failed\n"); + return -EFAULT; + } + } else { + /* Set */ + if (copy_from_user(&data, wrq->u.data.pointer, sizeof(int))) { + lbs_pr_debug(1, "Copy from user failed\n"); + return -EFAULT; + } + + timeout = data; + ret = libertas_prepare_and_send_command(priv, + cmd_802_11_inactivity_timeout, + cmd_act_set, + cmd_option_waitforrsp, 0, + &timeout); + } + + wrq->u.data.length = 1; + + LEAVE(); + return ret; +} + +static int wlan_do_getlog_ioctl(wlan_private * priv, struct iwreq *wrq) +{ + int ret; + char buf[GETLOG_BUFSIZE - 1]; + wlan_adapter *adapter = priv->adapter; + + lbs_pr_debug(1, " GET STATS\n"); + + ret = libertas_prepare_and_send_command(priv, cmd_802_11_get_log, + 0, cmd_option_waitforrsp, 0, NULL); + + if (ret) { + return ret; + } + + if (wrq->u.data.pointer) { + sprintf(buf, "\n mcasttxframe %u failed %u retry %u " + "multiretry %u framedup %u " + "rtssuccess %u rtsfailure %u ackfailure %u\n" + "rxfrag %u mcastrxframe %u fcserror %u " + "txframe %u wepundecryptable %u ", + adapter->logmsg.mcasttxframe, + adapter->logmsg.failed, + adapter->logmsg.retry, + adapter->logmsg.multiretry, + adapter->logmsg.framedup, + adapter->logmsg.rtssuccess, + adapter->logmsg.rtsfailure, + adapter->logmsg.ackfailure, + adapter->logmsg.rxfrag, + adapter->logmsg.mcastrxframe, + adapter->logmsg.fcserror, + adapter->logmsg.txframe, + adapter->logmsg.wepundecryptable); + wrq->u.data.length = strlen(buf) + 1; + if (copy_to_user(wrq->u.data.pointer, buf, wrq->u.data.length)) { + lbs_pr_debug(1, "Copy to user failed\n"); + return -EFAULT; + } + } + + return 0; +} + +static int wlan_scan_type_ioctl(wlan_private * priv, struct iwreq *wrq) +{ + u8 buf[12]; + u8 *option[] = { "active", "passive", "get", }; + int i, max_options = (sizeof(option) / sizeof(option[0])); + int ret = 0; + wlan_adapter *adapter = priv->adapter; + + if (priv->adapter->enable11d) { + lbs_pr_debug(1, "11D: Cannot set scantype when 11D enabled\n"); + return -EFAULT; + } + + memset(buf, 0, sizeof(buf)); + + if (copy_from_user(buf, wrq->u.data.pointer, min_t(size_t, sizeof(buf), + wrq->u.data.length))) + return -EFAULT; + + lbs_pr_debug(1, "Scan type Option = %s\n", buf); + + buf[sizeof(buf) - 1] = '\0'; + + for (i = 0; i < max_options; i++) { + if (!strcmp(buf, option[i])) + break; + } + + switch (i) { + case 0: + adapter->scantype = cmd_scan_type_active; + break; + case 1: + adapter->scantype = cmd_scan_type_passive; + break; + case 2: + wrq->u.data.length = strlen(option[adapter->scantype]) + 1; + + if (copy_to_user(wrq->u.data.pointer, + option[adapter->scantype], + wrq->u.data.length)) { + lbs_pr_debug(1, "Copy to user failed\n"); + ret = -EFAULT; + } + + break; + default: + lbs_pr_debug(1, "Invalid Scan type Ioctl Option\n"); + ret = -EINVAL; + break; + } + + return ret; +} + +static int wlan_scan_mode_ioctl(wlan_private * priv, struct iwreq *wrq) +{ + wlan_adapter *adapter = priv->adapter; + u8 buf[12]; + u8 *option[] = { "bss", "ibss", "any", "get" }; + int i, max_options = (sizeof(option) / sizeof(option[0])); + int ret = 0; + + ENTER(); + + memset(buf, 0, sizeof(buf)); + + if (copy_from_user(buf, wrq->u.data.pointer, min_t(size_t, sizeof(buf), + wrq->u.data.length))) { + lbs_pr_debug(1, "Copy from user failed\n"); + return -EFAULT; + } + + lbs_pr_debug(1, "Scan mode Option = %s\n", buf); + + buf[sizeof(buf) - 1] = '\0'; + + for (i = 0; i < max_options; i++) { + if (!strcmp(buf, option[i])) + break; + } + + switch (i) { + + case 0: + adapter->scanmode = cmd_bss_type_bss; + break; + case 1: + adapter->scanmode = cmd_bss_type_ibss; + break; + case 2: + adapter->scanmode = cmd_bss_type_any; + break; + case 3: + + wrq->u.data.length = strlen(option[adapter->scanmode - 1]) + 1; + + lbs_pr_debug(1, "Get Scan mode Option = %s\n", + option[adapter->scanmode - 1]); + + lbs_pr_debug(1, "Scan mode length %d\n", wrq->u.data.length); + + if (copy_to_user(wrq->u.data.pointer, + option[adapter->scanmode - 1], + wrq->u.data.length)) { + lbs_pr_debug(1, "Copy to user failed\n"); + ret = -EFAULT; + } + lbs_pr_debug(1, "GET Scan type Option after copy = %s\n", + (char *)wrq->u.data.pointer); + + break; + + default: + lbs_pr_debug(1, "Invalid Scan mode Ioctl Option\n"); + ret = -EINVAL; + break; + } + + LEAVE(); + return ret; +} + +/** + * @brief Get/Set Adhoc G Rate + * + * @param priv A pointer to wlan_private structure + * @param wrq A pointer to user data + * @return 0--success, otherwise fail + */ +static int wlan_do_set_grate_ioctl(wlan_private * priv, struct iwreq *wrq) +{ + wlan_adapter *adapter = priv->adapter; + int data, data1; + int *val; + + ENTER(); + + data1 = SUBCMD_DATA(wrq); + switch (data1) { + case 0: + adapter->adhoc_grate_enabled = 0; + break; + case 1: + adapter->adhoc_grate_enabled = 1; + break; + case 2: + break; + default: + return -EINVAL; + } + data = adapter->adhoc_grate_enabled; + val = (int *)wrq->u.name; + *val = data; + LEAVE(); + return 0; +} + +static inline int hex2int(char c) +{ + if (c >= '0' && c <= '9') + return (c - '0'); + if (c >= 'a' && c <= 'f') + return (c - 'a' + 10); + if (c >= 'A' && c <= 'F') + return (c - 'A' + 10); + return -1; +} + +/* Convert a string representation of a MAC address ("xx:xx:xx:xx:xx:xx") + into binary format (6 bytes). + + This function expects that each byte is represented with 2 characters + (e.g., 11:2:11:11:11:11 is invalid) + + */ +static char *eth_str2addr(char *ethstr, u8 * addr) +{ + int i, val, val2; + char *pos = ethstr; + + /* get rid of initial blanks */ + while (*pos == ' ' || *pos == '\t') + ++pos; + + for (i = 0; i < 6; i++) { + val = hex2int(*pos++); + if (val < 0) + return NULL; + val2 = hex2int(*pos++); + if (val2 < 0) + return NULL; + addr[i] = (val * 16 + val2) & 0xff; + + if (i < 5 && *pos++ != ':') + return NULL; + } + return pos; +} + +/* this writes xx:xx:xx:xx:xx:xx into ethstr + (ethstr must have space for 18 chars) */ +static int eth_addr2str(u8 * addr, char *ethstr) +{ + int i; + char *pos = ethstr; + + for (i = 0; i < 6; i++) { + sprintf(pos, "%02x", addr[i] & 0xff); + pos += 2; + if (i < 5) + *pos++ = ':'; + } + return 17; +} + +/** + * @brief Add an entry to the BT table + * @param priv A pointer to wlan_private structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int wlan_bt_add_ioctl(wlan_private * priv, struct ifreq *req) +{ + struct iwreq *wrq = (struct iwreq *)req; + char ethaddrs_str[18]; + char *pos; + u8 ethaddr[ETH_ALEN]; + + ENTER(); + if (copy_from_user(ethaddrs_str, wrq->u.data.pointer, + sizeof(ethaddrs_str))) + return -EFAULT; + + if ((pos = eth_str2addr(ethaddrs_str, ethaddr)) == NULL) { + lbs_pr_info("BT_ADD: Invalid MAC address\n"); + return -EINVAL; + } + + lbs_pr_debug(1, "BT: adding %s\n", ethaddrs_str); + LEAVE(); + return (libertas_prepare_and_send_command(priv, cmd_bt_access, + cmd_act_bt_access_add, + cmd_option_waitforrsp, 0, ethaddr)); +} + +/** + * @brief Delete an entry from the BT table + * @param priv A pointer to wlan_private structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int wlan_bt_del_ioctl(wlan_private * priv, struct ifreq *req) +{ + struct iwreq *wrq = (struct iwreq *)req; + char ethaddrs_str[18]; + u8 ethaddr[ETH_ALEN]; + char *pos; + + ENTER(); + if (copy_from_user(ethaddrs_str, wrq->u.data.pointer, + sizeof(ethaddrs_str))) + return -EFAULT; + + if ((pos = eth_str2addr(ethaddrs_str, ethaddr)) == NULL) { + lbs_pr_info("Invalid MAC address\n"); + return -EINVAL; + } + + lbs_pr_debug(1, "BT: deleting %s\n", ethaddrs_str); + + return (libertas_prepare_and_send_command(priv, + cmd_bt_access, + cmd_act_bt_access_del, + cmd_option_waitforrsp, 0, ethaddr)); + LEAVE(); + return 0; +} + +/** + * @brief Reset all entries from the BT table + * @param priv A pointer to wlan_private structure + * @return 0 --success, otherwise fail + */ +static int wlan_bt_reset_ioctl(wlan_private * priv) +{ + ENTER(); + + lbs_pr_alert( "BT: resetting\n"); + + return (libertas_prepare_and_send_command(priv, + cmd_bt_access, + cmd_act_bt_access_reset, + cmd_option_waitforrsp, 0, NULL)); + + LEAVE(); + return 0; +} + +/** + * @brief List an entry from the BT table + * @param priv A pointer to wlan_private structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int wlan_bt_list_ioctl(wlan_private * priv, struct ifreq *req) +{ + int pos; + char *addr1; + struct iwreq *wrq = (struct iwreq *)req; + /* used to pass id and store the bt entry returned by the FW */ + union { + int id; + char addr1addr2[2 * ETH_ALEN]; + } param; + static char outstr[64]; + char *pbuf = outstr; + int ret; + + ENTER(); + + if (copy_from_user(outstr, wrq->u.data.pointer, sizeof(outstr))) { + lbs_pr_debug(1, "Copy from user failed\n"); + return -1; + } + param.id = simple_strtoul(outstr, NULL, 10); + pos = sprintf(pbuf, "%d: ", param.id); + pbuf += pos; + + ret = libertas_prepare_and_send_command(priv, cmd_bt_access, + cmd_act_bt_access_list, + cmd_option_waitforrsp, 0, + (char *)¶m); + + if (ret == 0) { + addr1 = param.addr1addr2; + + pos = sprintf(pbuf, "ignoring traffic from "); + pbuf += pos; + pos = eth_addr2str(addr1, pbuf); + pbuf += pos; + } else { + sprintf(pbuf, "(null)"); + pbuf += pos; + } + + wrq->u.data.length = strlen(outstr); + if (copy_to_user(wrq->u.data.pointer, (char *)outstr, + wrq->u.data.length)) { + lbs_pr_debug(1, "BT_LIST: Copy to user failed!\n"); + return -EFAULT; + } + + LEAVE(); + return 0; +} + +/** + * @brief Find the next parameter in an input string + * @param ptr A pointer to the input parameter string + * @return A pointer to the next parameter, or 0 if no parameters left. + */ +static char * next_param(char * ptr) +{ + if (!ptr) return NULL; + while (*ptr == ' ' || *ptr == '\t') ++ptr; + return (*ptr == '\0') ? NULL : ptr; +} + +/** + * @brief Add an entry to the FWT table + * @param priv A pointer to wlan_private structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int wlan_fwt_add_ioctl(wlan_private * priv, struct ifreq *req) +{ + struct iwreq *wrq = (struct iwreq *)req; + char in_str[128]; + static struct cmd_ds_fwt_access fwt_access; + char *ptr; + + ENTER(); + if (copy_from_user(in_str, wrq->u.data.pointer, sizeof(in_str))) + return -EFAULT; + + if ((ptr = eth_str2addr(in_str, fwt_access.da)) == NULL) { + lbs_pr_alert( "FWT_ADD: Invalid MAC address 1\n"); + return -EINVAL; + } + + if ((ptr = eth_str2addr(ptr, fwt_access.ra)) == NULL) { + lbs_pr_alert( "FWT_ADD: Invalid MAC address 2\n"); + return -EINVAL; + } + + if ((ptr = next_param(ptr))) + fwt_access.metric = + cpu_to_le32(simple_strtoul(ptr, &ptr, 10)); + else + fwt_access.metric = FWT_DEFAULT_METRIC; + + if ((ptr = next_param(ptr))) + fwt_access.dir = (u8)simple_strtoul(ptr, &ptr, 10); + else + fwt_access.dir = FWT_DEFAULT_DIR; + + if ((ptr = next_param(ptr))) + fwt_access.ssn = + cpu_to_le32(simple_strtoul(ptr, &ptr, 10)); + else + fwt_access.ssn = FWT_DEFAULT_SSN; + + if ((ptr = next_param(ptr))) + fwt_access.dsn = + cpu_to_le32(simple_strtoul(ptr, &ptr, 10)); + else + fwt_access.dsn = FWT_DEFAULT_DSN; + + if ((ptr = next_param(ptr))) + fwt_access.hopcount = simple_strtoul(ptr, &ptr, 10); + else + fwt_access.hopcount = FWT_DEFAULT_HOPCOUNT; + + if ((ptr = next_param(ptr))) + fwt_access.ttl = simple_strtoul(ptr, &ptr, 10); + else + fwt_access.ttl = FWT_DEFAULT_TTL; + + if ((ptr = next_param(ptr))) + fwt_access.expiration = + cpu_to_le32(simple_strtoul(ptr, &ptr, 10)); + else + fwt_access.expiration = FWT_DEFAULT_EXPIRATION; + + if ((ptr = next_param(ptr))) + fwt_access.sleepmode = (u8)simple_strtoul(ptr, &ptr, 10); + else + fwt_access.sleepmode = FWT_DEFAULT_SLEEPMODE; + + if ((ptr = next_param(ptr))) + fwt_access.snr = + cpu_to_le32(simple_strtoul(ptr, &ptr, 10)); + else + fwt_access.snr = FWT_DEFAULT_SNR; + +#ifdef DEBUG + { + char ethaddr1_str[18], ethaddr2_str[18]; + eth_addr2str(fwt_access.da, ethaddr1_str); + eth_addr2str(fwt_access.ra, ethaddr2_str); + lbs_pr_debug(1, "FWT_ADD: adding (da:%s,%i,ra:%s)\n", ethaddr1_str, + fwt_access.dir, ethaddr2_str); + lbs_pr_debug(1, "FWT_ADD: ssn:%u dsn:%u met:%u hop:%u ttl:%u exp:%u slp:%u snr:%u\n", + fwt_access.ssn, fwt_access.dsn, fwt_access.metric, + fwt_access.hopcount, fwt_access.ttl, fwt_access.expiration, + fwt_access.sleepmode, fwt_access.snr); + } +#endif + + LEAVE(); + return (libertas_prepare_and_send_command(priv, cmd_fwt_access, + cmd_act_fwt_access_add, + cmd_option_waitforrsp, 0, + (void *)&fwt_access)); +} + +/** + * @brief Delete an entry from the FWT table + * @param priv A pointer to wlan_private structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int wlan_fwt_del_ioctl(wlan_private * priv, struct ifreq *req) +{ + struct iwreq *wrq = (struct iwreq *)req; + char in_str[64]; + static struct cmd_ds_fwt_access fwt_access; + char *ptr; + + ENTER(); + if (copy_from_user(in_str, wrq->u.data.pointer, sizeof(in_str))) + return -EFAULT; + + if ((ptr = eth_str2addr(in_str, fwt_access.da)) == NULL) { + lbs_pr_alert( "FWT_DEL: Invalid MAC address 1\n"); + return -EINVAL; + } + + if ((ptr = eth_str2addr(ptr, fwt_access.ra)) == NULL) { + lbs_pr_alert( "FWT_DEL: Invalid MAC address 2\n"); + return -EINVAL; + } + + if ((ptr = next_param(ptr))) + fwt_access.dir = (u8)simple_strtoul(ptr, &ptr, 10); + else + fwt_access.dir = FWT_DEFAULT_DIR; + +#ifdef DEBUG + { + char ethaddr1_str[18], ethaddr2_str[18]; + lbs_pr_debug(1, "FWT_DEL: line is %s\n", in_str); + eth_addr2str(fwt_access.da, ethaddr1_str); + eth_addr2str(fwt_access.ra, ethaddr2_str); + lbs_pr_debug(1, "FWT_DEL: removing (da:%s,ra:%s,dir:%d)\n", ethaddr1_str, + ethaddr2_str, fwt_access.dir); + } +#endif + + LEAVE(); + return (libertas_prepare_and_send_command(priv, + cmd_fwt_access, + cmd_act_fwt_access_del, + cmd_option_waitforrsp, 0, + (void *)&fwt_access)); +} + + +/** + * @brief Print route parameters + * @param fwt_access struct cmd_ds_fwt_access with route info + * @param buf destination buffer for route info + */ +static void print_route(struct cmd_ds_fwt_access fwt_access, char *buf) +{ + buf += sprintf(buf, " "); + buf += eth_addr2str(fwt_access.da, buf); + buf += sprintf(buf, " "); + buf += eth_addr2str(fwt_access.ra, buf); + buf += sprintf(buf, " %u", le32_to_cpu(fwt_access.metric)); + buf += sprintf(buf, " %u", fwt_access.dir); + buf += sprintf(buf, " %u", le32_to_cpu(fwt_access.ssn)); + buf += sprintf(buf, " %u", le32_to_cpu(fwt_access.dsn)); + buf += sprintf(buf, " %u", fwt_access.hopcount); + buf += sprintf(buf, " %u", fwt_access.ttl); + buf += sprintf(buf, " %u", le32_to_cpu(fwt_access.expiration)); + buf += sprintf(buf, " %u", fwt_access.sleepmode); + buf += sprintf(buf, " %u", le32_to_cpu(fwt_access.snr)); +} + +/** + * @brief Lookup an entry in the FWT table + * @param priv A pointer to wlan_private structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int wlan_fwt_lookup_ioctl(wlan_private * priv, struct ifreq *req) +{ + struct iwreq *wrq = (struct iwreq *)req; + char in_str[64]; + char *ptr; + static struct cmd_ds_fwt_access fwt_access; + static char out_str[128]; + int ret; + + ENTER(); + if (copy_from_user(in_str, wrq->u.data.pointer, sizeof(in_str))) + return -EFAULT; + + if ((ptr = eth_str2addr(in_str, fwt_access.da)) == NULL) { + lbs_pr_alert( "FWT_LOOKUP: Invalid MAC address\n"); + return -EINVAL; + } + +#ifdef DEBUG + { + char ethaddr1_str[18]; + lbs_pr_debug(1, "FWT_LOOKUP: line is %s\n", in_str); + eth_addr2str(fwt_access.da, ethaddr1_str); + lbs_pr_debug(1, "FWT_LOOKUP: looking for (da:%s)\n", ethaddr1_str); + } +#endif + + ret = libertas_prepare_and_send_command(priv, + cmd_fwt_access, + cmd_act_fwt_access_lookup, + cmd_option_waitforrsp, 0, + (void *)&fwt_access); + + if (ret == 0) + print_route(fwt_access, out_str); + else + sprintf(out_str, "(null)"); + + wrq->u.data.length = strlen(out_str); + if (copy_to_user(wrq->u.data.pointer, (char *)out_str, + wrq->u.data.length)) { + lbs_pr_debug(1, "FWT_LOOKUP: Copy to user failed!\n"); + return -EFAULT; + } + + LEAVE(); + return 0; +} + +/** + * @brief Reset all entries from the FWT table + * @param priv A pointer to wlan_private structure + * @return 0 --success, otherwise fail + */ +static int wlan_fwt_reset_ioctl(wlan_private * priv) +{ + lbs_pr_debug(1, "FWT: resetting\n"); + + return (libertas_prepare_and_send_command(priv, + cmd_fwt_access, + cmd_act_fwt_access_reset, + cmd_option_waitforrsp, 0, NULL)); +} + +/** + * @brief List an entry from the FWT table + * @param priv A pointer to wlan_private structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int wlan_fwt_list_ioctl(wlan_private * priv, struct ifreq *req) +{ + struct iwreq *wrq = (struct iwreq *)req; + char in_str[8]; + static struct cmd_ds_fwt_access fwt_access; + char *ptr = in_str; + static char out_str[128]; + char *pbuf = out_str; + int ret; + + ENTER(); + if (copy_from_user(in_str, wrq->u.data.pointer, sizeof(in_str))) + return -EFAULT; + + fwt_access.id = cpu_to_le32(simple_strtoul(ptr, &ptr, 10)); + +#ifdef DEBUG + { + lbs_pr_debug(1, "FWT_LIST: line is %s\n", in_str); + lbs_pr_debug(1, "FWT_LIST: listing id:%i\n", le32_to_cpu(fwt_access.id)); + } +#endif + + ret = libertas_prepare_and_send_command(priv, cmd_fwt_access, + cmd_act_fwt_access_list, + cmd_option_waitforrsp, 0, (void *)&fwt_access); + + if (ret == 0) + print_route(fwt_access, pbuf); + else + pbuf += sprintf(pbuf, " (null)"); + + wrq->u.data.length = strlen(out_str); + if (copy_to_user(wrq->u.data.pointer, (char *)out_str, + wrq->u.data.length)) { + lbs_pr_debug(1, "FWT_LIST: Copy to user failed!\n"); + return -EFAULT; + } + + LEAVE(); + return 0; +} + +/** + * @brief List an entry from the FRT table + * @param priv A pointer to wlan_private structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int wlan_fwt_list_route_ioctl(wlan_private * priv, struct ifreq *req) +{ + struct iwreq *wrq = (struct iwreq *)req; + char in_str[64]; + static struct cmd_ds_fwt_access fwt_access; + char *ptr = in_str; + static char out_str[128]; + char *pbuf = out_str; + int ret; + + ENTER(); + if (copy_from_user(in_str, wrq->u.data.pointer, sizeof(in_str))) + return -EFAULT; + + fwt_access.id = cpu_to_le32(simple_strtoul(ptr, &ptr, 10)); + +#ifdef DEBUG + { + lbs_pr_debug(1, "FWT_LIST_ROUTE: line is %s\n", in_str); + lbs_pr_debug(1, "FWT_LIST_ROUTE: listing id:%i\n", le32_to_cpu(fwt_access.id)); + } +#endif + + ret = libertas_prepare_and_send_command(priv, cmd_fwt_access, + cmd_act_fwt_access_list_route, + cmd_option_waitforrsp, 0, (void *)&fwt_access); + + if (ret == 0) { + pbuf += sprintf(pbuf, " "); + pbuf += eth_addr2str(fwt_access.da, pbuf); + pbuf += sprintf(pbuf, " %u", le32_to_cpu(fwt_access.metric)); + pbuf += sprintf(pbuf, " %u", fwt_access.dir); + /* note that the firmware returns the nid in the id field */ + pbuf += sprintf(pbuf, " %u", le32_to_cpu(fwt_access.id)); + pbuf += sprintf(pbuf, " %u", le32_to_cpu(fwt_access.ssn)); + pbuf += sprintf(pbuf, " %u", le32_to_cpu(fwt_access.dsn)); + pbuf += sprintf(pbuf, " hop %u", fwt_access.hopcount); + pbuf += sprintf(pbuf, " ttl %u", fwt_access.ttl); + pbuf += sprintf(pbuf, " %u", le32_to_cpu(fwt_access.expiration)); + } else + pbuf += sprintf(pbuf, " (null)"); + + wrq->u.data.length = strlen(out_str); + if (copy_to_user(wrq->u.data.pointer, (char *)out_str, + wrq->u.data.length)) { + lbs_pr_debug(1, "FWT_LIST_ROUTE: Copy to user failed!\n"); + return -EFAULT; + } + + LEAVE(); + return 0; +} + +/** + * @brief List an entry from the FNT table + * @param priv A pointer to wlan_private structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int wlan_fwt_list_neighbor_ioctl(wlan_private * priv, struct ifreq *req) +{ + struct iwreq *wrq = (struct iwreq *)req; + char in_str[8]; + static struct cmd_ds_fwt_access fwt_access; + char *ptr = in_str; + static char out_str[128]; + char *pbuf = out_str; + int ret; + + ENTER(); + if (copy_from_user(in_str, wrq->u.data.pointer, sizeof(in_str))) + return -EFAULT; + + memset(&fwt_access, 0, sizeof(fwt_access)); + fwt_access.id = cpu_to_le32(simple_strtoul(ptr, &ptr, 10)); + +#ifdef DEBUG + { + lbs_pr_debug(1, "FWT_LIST_NEIGHBOR: line is %s\n", in_str); + lbs_pr_debug(1, "FWT_LIST_NEIGHBOR: listing id:%i\n", le32_to_cpu(fwt_access.id)); + } +#endif + + ret = libertas_prepare_and_send_command(priv, cmd_fwt_access, + cmd_act_fwt_access_list_neighbor, + cmd_option_waitforrsp, 0, + (void *)&fwt_access); + + if (ret == 0) { + pbuf += sprintf(pbuf, " ra "); + pbuf += eth_addr2str(fwt_access.ra, pbuf); + pbuf += sprintf(pbuf, " slp %u", fwt_access.sleepmode); + pbuf += sprintf(pbuf, " snr %u", le32_to_cpu(fwt_access.snr)); + pbuf += sprintf(pbuf, " ref %u", le32_to_cpu(fwt_access.references)); + } else + pbuf += sprintf(pbuf, " (null)"); + + wrq->u.data.length = strlen(out_str); + if (copy_to_user(wrq->u.data.pointer, (char *)out_str, + wrq->u.data.length)) { + lbs_pr_debug(1, "FWT_LIST_NEIGHBOR: Copy to user failed!\n"); + return -EFAULT; + } + + LEAVE(); + return 0; +} + +/** + * @brief Cleans up the route (FRT) and neighbor (FNT) tables + * (Garbage Collection) + * @param priv A pointer to wlan_private structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int wlan_fwt_cleanup_ioctl(wlan_private * priv, struct ifreq *req) +{ + static struct cmd_ds_fwt_access fwt_access; + int ret; + + ENTER(); + + lbs_pr_debug(1, "FWT: cleaning up\n"); + + memset(&fwt_access, 0, sizeof(fwt_access)); + + ret = libertas_prepare_and_send_command(priv, cmd_fwt_access, + cmd_act_fwt_access_cleanup, + cmd_option_waitforrsp, 0, + (void *)&fwt_access); + + if (ret == 0) + req->ifr_data = (char *)(le32_to_cpu(fwt_access.references)); + else + return -EFAULT; + + LEAVE(); + return 0; +} + +/** + * @brief Gets firmware internal time (debug purposes) + * @param priv A pointer to wlan_private structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int wlan_fwt_time_ioctl(wlan_private * priv, struct ifreq *req) +{ + static struct cmd_ds_fwt_access fwt_access; + int ret; + + ENTER(); + + lbs_pr_debug(1, "FWT: getting time\n"); + + memset(&fwt_access, 0, sizeof(fwt_access)); + + ret = libertas_prepare_and_send_command(priv, cmd_fwt_access, + cmd_act_fwt_access_time, + cmd_option_waitforrsp, 0, + (void *)&fwt_access); + + if (ret == 0) + req->ifr_data = (char *)(le32_to_cpu(fwt_access.references)); + else + return -EFAULT; + + LEAVE(); + return 0; +} + +/** + * @brief Gets mesh ttl from firmware + * @param priv A pointer to wlan_private structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int wlan_mesh_get_ttl_ioctl(wlan_private * priv, struct ifreq *req) +{ + struct cmd_ds_mesh_access mesh_access; + int ret; + + ENTER(); + + memset(&mesh_access, 0, sizeof(mesh_access)); + + ret = libertas_prepare_and_send_command(priv, cmd_mesh_access, + cmd_act_mesh_get_ttl, + cmd_option_waitforrsp, 0, + (void *)&mesh_access); + + if (ret == 0) { + req->ifr_data = (char *)(le32_to_cpu(mesh_access.data[0])); + } + else + return -EFAULT; + + LEAVE(); + return 0; +} + +/** + * @brief Gets mesh ttl from firmware + * @param priv A pointer to wlan_private structure + * @param ttl New ttl value + * @return 0 --success, otherwise fail + */ +static int wlan_mesh_set_ttl_ioctl(wlan_private * priv, int ttl) +{ + struct cmd_ds_mesh_access mesh_access; + int ret; + + ENTER(); + + if( (ttl > 0xff) || (ttl < 0) ) + return -EINVAL; + + memset(&mesh_access, 0, sizeof(mesh_access)); + mesh_access.data[0] = ttl; + + ret = libertas_prepare_and_send_command(priv, cmd_mesh_access, + cmd_act_mesh_set_ttl, + cmd_option_waitforrsp, 0, + (void *)&mesh_access); + + if (ret != 0) + ret = -EFAULT; + + LEAVE(); + return ret; +} + +/** + * @brief ioctl function - entry point + * + * @param dev A pointer to net_device structure + * @param req A pointer to ifreq structure + * @param cmd command + * @return 0--success, otherwise fail + */ +int libertas_do_ioctl(struct net_device *dev, struct ifreq *req, int cmd) +{ + int subcmd = 0; + int idata = 0; + int *pdata; + int ret = 0; + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + struct iwreq *wrq = (struct iwreq *)req; + + ENTER(); + + lbs_pr_debug(1, "libertas_do_ioctl: ioctl cmd = 0x%x\n", cmd); + switch (cmd) { + case WLANSCAN_TYPE: + lbs_pr_debug(1, "Scan type Ioctl\n"); + ret = wlan_scan_type_ioctl(priv, wrq); + break; + + case WLAN_SETNONE_GETNONE: /* set WPA mode on/off ioctl #20 */ + switch (wrq->u.data.flags) { + case WLANDEAUTH: + lbs_pr_debug(1, "Deauth\n"); + libertas_send_deauth(priv); + break; + + case WLANADHOCSTOP: + lbs_pr_debug(1, "Adhoc stop\n"); + ret = libertas_do_adhocstop_ioctl(priv); + break; + + case WLANRADIOON: + wlan_radio_ioctl(priv, 1); + break; + + case WLANRADIOOFF: + wlan_radio_ioctl(priv, 0); + break; + case WLANWLANIDLEON: + libertas_idle_on(priv); + break; + case WLANWLANIDLEOFF: + libertas_idle_off(priv); + break; + case WLAN_SUBCMD_BT_RESET: /* bt_reset */ + wlan_bt_reset_ioctl(priv); + break; + case WLAN_SUBCMD_FWT_RESET: /* fwt_reset */ + wlan_fwt_reset_ioctl(priv); + break; + } /* End of switch */ + break; + + case WLANSETWPAIE: + ret = wlan_setwpaie_ioctl(priv, req); + break; + case WLAN_SETINT_GETINT: + /* The first 4 bytes of req->ifr_data is sub-ioctl number + * after 4 bytes sits the payload. + */ + subcmd = (int)req->ifr_data; //from iwpriv subcmd + switch (subcmd) { + case WLANNF: + ret = wlan_get_nf(priv, wrq); + break; + case WLANRSSI: + ret = wlan_get_rssi(priv, wrq); + break; + case WLANENABLE11D: + ret = libertas_cmd_enable_11d(priv, wrq); + break; + case WLANADHOCGRATE: + ret = wlan_do_set_grate_ioctl(priv, wrq); + break; + case WLAN_SUBCMD_SET_PRESCAN: + ret = wlan_subcmd_setprescan_ioctl(priv, wrq); + break; + } + break; + + case WLAN_SETONEINT_GETONEINT: + switch (wrq->u.data.flags) { + case WLAN_BEACON_INTERVAL: + ret = wlan_beacon_interval(priv, wrq); + break; + + case WLAN_LISTENINTRVL: + if (!wrq->u.data.length) { + int data; + lbs_pr_debug(1, "Get locallisteninterval value\n"); +#define GET_ONE_INT 1 + data = adapter->locallisteninterval; + if (copy_to_user(wrq->u.data.pointer, + &data, sizeof(int))) { + lbs_pr_debug(1, "Copy to user failed\n"); + return -EFAULT; + } + + wrq->u.data.length = GET_ONE_INT; + } else { + int data; + if (copy_from_user + (&data, wrq->u.data.pointer, sizeof(int))) { + lbs_pr_debug(1, "Copy from user failed\n"); + return -EFAULT; + } + + lbs_pr_debug(1, "Set locallisteninterval = %d\n", + data); +#define MAX_U16_VAL 65535 + if (data > MAX_U16_VAL) { + lbs_pr_debug(1, "Exceeds U16 value\n"); + return -EINVAL; + } + adapter->locallisteninterval = data; + } + break; + case WLAN_TXCONTROL: + ret = wlan_txcontrol(priv, wrq); //adds for txcontrol ioctl + break; + + case WLAN_NULLPKTINTERVAL: + ret = wlan_null_pkt_interval(priv, wrq); + break; + + default: + ret = -EOPNOTSUPP; + break; + } + break; + + case WLAN_SETONEINT_GETNONE: + /* The first 4 bytes of req->ifr_data is sub-ioctl number + * after 4 bytes sits the payload. + */ + subcmd = wrq->u.data.flags; //from wpa_supplicant subcmd + + if (!subcmd) + subcmd = (int)req->ifr_data; //from iwpriv subcmd + + switch (subcmd) { + case WLAN_SUBCMD_SETRXANTENNA: /* SETRXANTENNA */ + idata = SUBCMD_DATA(wrq); + ret = setrxantenna(priv, idata); + break; + case WLAN_SUBCMD_SETTXANTENNA: /* SETTXANTENNA */ + idata = SUBCMD_DATA(wrq); + ret = settxantenna(priv, idata); + break; + case WLAN_SET_ATIM_WINDOW: + adapter->atimwindow = SUBCMD_DATA(wrq); + adapter->atimwindow = min_t(__u16, adapter->atimwindow, 50); + break; + case WLANSETBCNAVG: + adapter->bcn_avg_factor = SUBCMD_DATA(wrq); + if (adapter->bcn_avg_factor == 0) + adapter->bcn_avg_factor = + DEFAULT_BCN_AVG_FACTOR; + if (adapter->bcn_avg_factor > DEFAULT_BCN_AVG_FACTOR) + adapter->bcn_avg_factor = + DEFAULT_BCN_AVG_FACTOR; + break; + case WLANSETDATAAVG: + adapter->data_avg_factor = SUBCMD_DATA(wrq); + if (adapter->data_avg_factor == 0) + adapter->data_avg_factor = + DEFAULT_DATA_AVG_FACTOR; + if (adapter->data_avg_factor > DEFAULT_DATA_AVG_FACTOR) + adapter->data_avg_factor = + DEFAULT_DATA_AVG_FACTOR; + break; + case WLANSETREGION: + idata = SUBCMD_DATA(wrq); + ret = wlan_set_region(priv, (u16) idata); + break; + + case WLAN_SET_LISTEN_INTERVAL: + idata = SUBCMD_DATA(wrq); + adapter->listeninterval = (u16) idata; + break; + + case WLAN_SET_MULTIPLE_DTIM: + ret = wlan_set_multiple_dtim_ioctl(priv, req); + break; + + case WLANSETAUTHALG: + ret = wlan_setauthalg_ioctl(priv, req); + break; + + case WLANSET8021XAUTHALG: + ret = wlan_set8021xauthalg_ioctl(priv, req); + break; + + case WLANSETENCRYPTIONMODE: + ret = wlan_setencryptionmode_ioctl(priv, req); + break; + + case WLAN_SET_LINKMODE: + ret = wlan_set_linkmode_ioctl(priv, req); + break; + + case WLAN_SET_RADIOMODE: + ret = wlan_set_radiomode_ioctl(priv, req); + break; + + case WLAN_SET_DEBUGMODE: + ret = wlan_set_debugmode_ioctl(priv, req); + break; + + case WLAN_SUBCMD_MESH_SET_TTL: + idata = SUBCMD_DATA(wrq); + ret = wlan_mesh_set_ttl_ioctl(priv, idata); + break; + + default: + ret = -EOPNOTSUPP; + break; + } + + break; + + case WLAN_SETNONE_GETTWELVE_CHAR: /* Get Antenna settings */ + /* + * We've not used IW_PRIV_TYPE_FIXED so sub-ioctl number is + * in flags of iwreq structure, otherwise it will be in + * mode member of iwreq structure. + */ + switch ((int)wrq->u.data.flags) { + case WLAN_SUBCMD_GETRXANTENNA: /* Get Rx Antenna */ + ret = wlan_subcmd_getrxantenna_ioctl(priv, req); + break; + + case WLAN_SUBCMD_GETTXANTENNA: /* Get Tx Antenna */ + ret = wlan_subcmd_gettxantenna_ioctl(priv, req); + break; + + case WLAN_GET_TSF: + ret = wlan_get_tsf_ioctl(priv, wrq); + break; + } + break; + + case WLAN_SET128CHAR_GET128CHAR: + switch ((int)wrq->u.data.flags) { + + case WLANSCAN_MODE: + lbs_pr_debug(1, "Scan mode Ioctl\n"); + ret = wlan_scan_mode_ioctl(priv, wrq); + break; + + case WLAN_GET_ADHOC_STATUS: + ret = wlan_get_adhoc_status_ioctl(priv, wrq); + break; + case WLAN_SUBCMD_BT_ADD: + ret = wlan_bt_add_ioctl(priv, req); + break; + case WLAN_SUBCMD_BT_DEL: + ret = wlan_bt_del_ioctl(priv, req); + break; + case WLAN_SUBCMD_BT_LIST: + ret = wlan_bt_list_ioctl(priv, req); + break; + case WLAN_SUBCMD_FWT_ADD: + ret = wlan_fwt_add_ioctl(priv, req); + break; + case WLAN_SUBCMD_FWT_DEL: + ret = wlan_fwt_del_ioctl(priv, req); + break; + case WLAN_SUBCMD_FWT_LOOKUP: + ret = wlan_fwt_lookup_ioctl(priv, req); + break; + case WLAN_SUBCMD_FWT_LIST_NEIGHBOR: + ret = wlan_fwt_list_neighbor_ioctl(priv, req); + break; + case WLAN_SUBCMD_FWT_LIST: + ret = wlan_fwt_list_ioctl(priv, req); + break; + case WLAN_SUBCMD_FWT_LIST_ROUTE: + ret = wlan_fwt_list_route_ioctl(priv, req); + break; + } + break; + + case WLAN_SETNONE_GETONEINT: + switch ((int)req->ifr_data) { + case WLANGETBCNAVG: + pdata = (int *)wrq->u.name; + *pdata = (int)adapter->bcn_avg_factor; + break; + + case WLANGETREGION: + pdata = (int *)wrq->u.name; + *pdata = (int)adapter->regioncode; + break; + + case WLAN_GET_LISTEN_INTERVAL: + pdata = (int *)wrq->u.name; + *pdata = (int)adapter->listeninterval; + break; + + case WLAN_GET_LINKMODE: + req->ifr_data = (char *)((u32) adapter->linkmode); + break; + + case WLAN_GET_RADIOMODE: + req->ifr_data = (char *)((u32) adapter->radiomode); + break; + + case WLAN_GET_DEBUGMODE: + req->ifr_data = (char *)((u32) adapter->debugmode); + break; + + case WLAN_GET_MULTIPLE_DTIM: + pdata = (int *)wrq->u.name; + *pdata = (int)adapter->multipledtim; + break; + case WLAN_GET_TX_RATE: + ret = wlan_get_txrate_ioctl(priv, req); + break; + case WLAN_SUBCMD_FWT_CLEANUP: /* fwt_cleanup */ + ret = wlan_fwt_cleanup_ioctl(priv, req); + break; + + case WLAN_SUBCMD_FWT_TIME: /* fwt_time */ + ret = wlan_fwt_time_ioctl(priv, req); + break; + + case WLAN_SUBCMD_MESH_GET_TTL: + ret = wlan_mesh_get_ttl_ioctl(priv, req); + break; + + default: + ret = -EOPNOTSUPP; + + } + + break; + + case WLANGETLOG: + ret = wlan_do_getlog_ioctl(priv, wrq); + break; + + case WLAN_SET_GET_SIXTEEN_INT: + switch ((int)wrq->u.data.flags) { + case WLAN_TPCCFG: + { + int data[5]; + struct cmd_ds_802_11_tpc_cfg cfg; + memset(&cfg, 0, sizeof(cfg)); + if ((wrq->u.data.length > 1) + && (wrq->u.data.length != 5)) + return -1; + + if (wrq->u.data.length == 0) { + cfg.action = + cpu_to_le16 + (cmd_act_get); + } else { + if (copy_from_user + (data, wrq->u.data.pointer, + sizeof(int) * 5)) { + lbs_pr_debug(1, + "Copy from user failed\n"); + return -EFAULT; + } + + cfg.action = + cpu_to_le16 + (cmd_act_set); + cfg.enable = data[0]; + cfg.usesnr = data[1]; + cfg.P0 = data[2]; + cfg.P1 = data[3]; + cfg.P2 = data[4]; + } + + ret = + libertas_prepare_and_send_command(priv, + cmd_802_11_tpc_cfg, + 0, + cmd_option_waitforrsp, + 0, (void *)&cfg); + + data[0] = cfg.enable; + data[1] = cfg.usesnr; + data[2] = cfg.P0; + data[3] = cfg.P1; + data[4] = cfg.P2; + if (copy_to_user + (wrq->u.data.pointer, data, + sizeof(int) * 5)) { + lbs_pr_debug(1, "Copy to user failed\n"); + return -EFAULT; + } + + wrq->u.data.length = 5; + } + break; + + case WLAN_POWERCFG: + { + int data[4]; + struct cmd_ds_802_11_pwr_cfg cfg; + memset(&cfg, 0, sizeof(cfg)); + if ((wrq->u.data.length > 1) + && (wrq->u.data.length != 4)) + return -1; + if (wrq->u.data.length == 0) { + cfg.action = + cpu_to_le16 + (cmd_act_get); + } else { + if (copy_from_user + (data, wrq->u.data.pointer, + sizeof(int) * 4)) { + lbs_pr_debug(1, + "Copy from user failed\n"); + return -EFAULT; + } + + cfg.action = + cpu_to_le16 + (cmd_act_set); + cfg.enable = data[0]; + cfg.PA_P0 = data[1]; + cfg.PA_P1 = data[2]; + cfg.PA_P2 = data[3]; + } + ret = + libertas_prepare_and_send_command(priv, + cmd_802_11_pwr_cfg, + 0, + cmd_option_waitforrsp, + 0, (void *)&cfg); + data[0] = cfg.enable; + data[1] = cfg.PA_P0; + data[2] = cfg.PA_P1; + data[3] = cfg.PA_P2; + if (copy_to_user + (wrq->u.data.pointer, data, + sizeof(int) * 4)) { + lbs_pr_debug(1, "Copy to user failed\n"); + return -EFAULT; + } + + wrq->u.data.length = 4; + } + break; + case WLAN_AUTO_FREQ_SET: + { + int data[3]; + struct cmd_ds_802_11_afc afc; + memset(&afc, 0, sizeof(afc)); + if (wrq->u.data.length != 3) + return -1; + if (copy_from_user + (data, wrq->u.data.pointer, + sizeof(int) * 3)) { + lbs_pr_debug(1, "Copy from user failed\n"); + return -EFAULT; + } + afc.afc_auto = data[0]; + + if (afc.afc_auto != 0) { + afc.threshold = data[1]; + afc.period = data[2]; + } else { + afc.timing_offset = data[1]; + afc.carrier_offset = data[2]; + } + ret = + libertas_prepare_and_send_command(priv, + cmd_802_11_set_afc, + 0, + cmd_option_waitforrsp, + 0, (void *)&afc); + } + break; + case WLAN_AUTO_FREQ_GET: + { + int data[3]; + struct cmd_ds_802_11_afc afc; + memset(&afc, 0, sizeof(afc)); + ret = + libertas_prepare_and_send_command(priv, + cmd_802_11_get_afc, + 0, + cmd_option_waitforrsp, + 0, (void *)&afc); + data[0] = afc.afc_auto; + data[1] = afc.timing_offset; + data[2] = afc.carrier_offset; + if (copy_to_user + (wrq->u.data.pointer, data, + sizeof(int) * 3)) { + lbs_pr_debug(1, "Copy to user failed\n"); + return -EFAULT; + } + + wrq->u.data.length = 3; + } + break; + case WLAN_SCANPROBES: + { + int data; + if (wrq->u.data.length > 0) { + if (copy_from_user + (&data, wrq->u.data.pointer, + sizeof(int))) { + lbs_pr_debug(1, + "Copy from user failed\n"); + return -EFAULT; + } + + adapter->scanprobes = data; + } else { + data = adapter->scanprobes; + if (copy_to_user + (wrq->u.data.pointer, &data, + sizeof(int))) { + lbs_pr_debug(1, + "Copy to user failed\n"); + return -EFAULT; + } + } + wrq->u.data.length = 1; + } + break; + case WLAN_LED_GPIO_CTRL: + { + int i; + int data[16]; + + struct cmd_ds_802_11_led_ctrl ctrl; + struct mrvlietypes_ledgpio *gpio = + (struct mrvlietypes_ledgpio *) ctrl.data; + + memset(&ctrl, 0, sizeof(ctrl)); + if (wrq->u.data.length > MAX_LEDS * 2) + return -ENOTSUPP; + if ((wrq->u.data.length % 2) != 0) + return -ENOTSUPP; + if (wrq->u.data.length == 0) { + ctrl.action = + cpu_to_le16 + (cmd_act_get); + } else { + if (copy_from_user + (data, wrq->u.data.pointer, + sizeof(int) * + wrq->u.data.length)) { + lbs_pr_debug(1, + "Copy from user failed\n"); + return -EFAULT; + } + + ctrl.action = + cpu_to_le16 + (cmd_act_set); + ctrl.numled = cpu_to_le16(0); + gpio->header.type = + cpu_to_le16(TLV_TYPE_LED_GPIO); + gpio->header.len = wrq->u.data.length; + for (i = 0; i < wrq->u.data.length; + i += 2) { + gpio->ledpin[i / 2].led = + data[i]; + gpio->ledpin[i / 2].pin = + data[i + 1]; + } + } + ret = + libertas_prepare_and_send_command(priv, + cmd_802_11_led_gpio_ctrl, + 0, + cmd_option_waitforrsp, + 0, (void *)&ctrl); + for (i = 0; i < gpio->header.len; i += 2) { + data[i] = gpio->ledpin[i / 2].led; + data[i + 1] = gpio->ledpin[i / 2].pin; + } + if (copy_to_user(wrq->u.data.pointer, data, + sizeof(int) * + gpio->header.len)) { + lbs_pr_debug(1, "Copy to user failed\n"); + return -EFAULT; + } + + wrq->u.data.length = gpio->header.len; + } + break; + case WLAN_ADAPT_RATESET: + ret = wlan_adapt_rateset(priv, wrq); + break; + case WLAN_INACTIVITY_TIMEOUT: + ret = wlan_inactivity_timeout(priv, wrq); + break; + case WLANSNR: + ret = wlan_get_snr(priv, wrq); + break; + case WLAN_GET_RXINFO: + ret = wlan_get_rxinfo(priv, wrq); + } + break; + + default: + ret = -EINVAL; + break; + } + LEAVE(); + return ret; +} + + diff --git a/drivers/net/wireless/libertas/join.c b/drivers/net/wireless/libertas/join.c new file mode 100644 index 00000000000..11682cbe752 --- /dev/null +++ b/drivers/net/wireless/libertas/join.c @@ -0,0 +1,1055 @@ +/** + * Functions implementing wlan infrastructure and adhoc join routines, + * IOCTL handlers as well as command preperation and response routines + * for sending adhoc start, adhoc join, and association commands + * to the firmware. + */ +#include <linux/netdevice.h> +#include <linux/if_arp.h> +#include <linux/wireless.h> + +#include <net/iw_handler.h> + +#include "host.h" +#include "decl.h" +#include "join.h" +#include "dev.h" + +/** + * @brief This function finds out the common rates between rate1 and rate2. + * + * It will fill common rates in rate1 as output if found. + * + * NOTE: Setting the MSB of the basic rates need to be taken + * care, either before or after calling this function + * + * @param adapter A pointer to wlan_adapter structure + * @param rate1 the buffer which keeps input and output + * @param rate1_size the size of rate1 buffer + * @param rate2 the buffer which keeps rate2 + * @param rate2_size the size of rate2 buffer. + * + * @return 0 or -1 + */ +static int get_common_rates(wlan_adapter * adapter, u8 * rate1, + int rate1_size, u8 * rate2, int rate2_size) +{ + u8 *ptr = rate1; + int ret = 0; + u8 tmp[30]; + int i; + + memset(&tmp, 0, sizeof(tmp)); + memcpy(&tmp, rate1, min_t(size_t, rate1_size, sizeof(tmp))); + memset(rate1, 0, rate1_size); + + /* Mask the top bit of the original values */ + for (i = 0; tmp[i] && i < sizeof(tmp); i++) + tmp[i] &= 0x7F; + + for (i = 0; rate2[i] && i < rate2_size; i++) { + /* Check for Card Rate in tmp, excluding the top bit */ + if (strchr(tmp, rate2[i] & 0x7F)) { + /* values match, so copy the Card Rate to rate1 */ + *rate1++ = rate2[i]; + } + } + + lbs_dbg_hex("rate1 (AP) rates:", tmp, sizeof(tmp)); + lbs_dbg_hex("rate2 (Card) rates:", rate2, rate2_size); + lbs_dbg_hex("Common rates:", ptr, rate1_size); + lbs_pr_debug(1, "Tx datarate is set to 0x%X\n", adapter->datarate); + + if (!adapter->is_datarate_auto) { + while (*ptr) { + if ((*ptr & 0x7f) == adapter->datarate) { + ret = 0; + goto done; + } + ptr++; + } + lbs_pr_alert( "Previously set fixed data rate %#x isn't " + "compatible with the network.\n", adapter->datarate); + + ret = -1; + goto done; + } + + ret = 0; +done: + return ret; +} + +int libertas_send_deauth(wlan_private * priv) +{ + wlan_adapter *adapter = priv->adapter; + int ret = 0; + + if (adapter->inframode == wlan802_11infrastructure && + adapter->connect_status == libertas_connected) + ret = libertas_send_deauthentication(priv); + else + ret = -ENOTSUPP; + + return ret; +} + +int libertas_do_adhocstop_ioctl(wlan_private * priv) +{ + wlan_adapter *adapter = priv->adapter; + int ret = 0; + + if (adapter->inframode == wlan802_11ibss && + adapter->connect_status == libertas_connected) + ret = libertas_stop_adhoc_network(priv); + else + ret = -ENOTSUPP; + + return ret; +} + +/** + * @brief Associate to a specific BSS discovered in a scan + * + * @param priv A pointer to wlan_private structure + * @param pbssdesc Pointer to the BSS descriptor to associate with. + * + * @return 0-success, otherwise fail + */ +int wlan_associate(wlan_private * priv, struct bss_descriptor * pbssdesc) +{ + wlan_adapter *adapter = priv->adapter; + int ret; + + ENTER(); + + ret = libertas_prepare_and_send_command(priv, cmd_802_11_authenticate, + 0, cmd_option_waitforrsp, + 0, pbssdesc->macaddress); + + if (ret) { + LEAVE(); + return ret; + } + + /* set preamble to firmware */ + if (adapter->capinfo.shortpreamble && pbssdesc->cap.shortpreamble) + adapter->preamble = cmd_type_short_preamble; + else + adapter->preamble = cmd_type_long_preamble; + + libertas_set_radio_control(priv); + + ret = libertas_prepare_and_send_command(priv, cmd_802_11_associate, + 0, cmd_option_waitforrsp, 0, pbssdesc); + + LEAVE(); + return ret; +} + +/** + * @brief Start an Adhoc Network + * + * @param priv A pointer to wlan_private structure + * @param adhocssid The ssid of the Adhoc Network + * @return 0--success, -1--fail + */ +int libertas_start_adhoc_network(wlan_private * priv, struct WLAN_802_11_SSID *adhocssid) +{ + wlan_adapter *adapter = priv->adapter; + int ret = 0; + + adapter->adhoccreate = 1; + + if (!adapter->capinfo.shortpreamble) { + lbs_pr_debug(1, "AdhocStart: Long preamble\n"); + adapter->preamble = cmd_type_long_preamble; + } else { + lbs_pr_debug(1, "AdhocStart: Short preamble\n"); + adapter->preamble = cmd_type_short_preamble; + } + + libertas_set_radio_control(priv); + + lbs_pr_debug(1, "Adhoc channel = %d\n", adapter->adhocchannel); + lbs_pr_debug(1, "curbssparams.channel = %d\n", + adapter->curbssparams.channel); + lbs_pr_debug(1, "curbssparams.band = %d\n", adapter->curbssparams.band); + + ret = libertas_prepare_and_send_command(priv, cmd_802_11_ad_hoc_start, + 0, cmd_option_waitforrsp, 0, adhocssid); + + return ret; +} + +/** + * @brief Join an adhoc network found in a previous scan + * + * @param priv A pointer to wlan_private structure + * @param pbssdesc Pointer to a BSS descriptor found in a previous scan + * to attempt to join + * + * @return 0--success, -1--fail + */ +int libertas_join_adhoc_network(wlan_private * priv, struct bss_descriptor * pbssdesc) +{ + wlan_adapter *adapter = priv->adapter; + int ret = 0; + + lbs_pr_debug(1, "libertas_join_adhoc_network: CurBss.ssid =%s\n", + adapter->curbssparams.ssid.ssid); + lbs_pr_debug(1, "libertas_join_adhoc_network: CurBss.ssid_len =%u\n", + adapter->curbssparams.ssid.ssidlength); + lbs_pr_debug(1, "libertas_join_adhoc_network: ssid =%s\n", pbssdesc->ssid.ssid); + lbs_pr_debug(1, "libertas_join_adhoc_network: ssid len =%u\n", + pbssdesc->ssid.ssidlength); + + /* check if the requested SSID is already joined */ + if (adapter->curbssparams.ssid.ssidlength + && !libertas_SSID_cmp(&pbssdesc->ssid, &adapter->curbssparams.ssid) + && (adapter->curbssparams.bssdescriptor.inframode == + wlan802_11ibss)) { + + lbs_pr_debug(1, + "ADHOC_J_CMD: New ad-hoc SSID is the same as current, " + "not attempting to re-join"); + + return -1; + } + + /*Use shortpreamble only when both creator and card supports + short preamble */ + if (!pbssdesc->cap.shortpreamble || !adapter->capinfo.shortpreamble) { + lbs_pr_debug(1, "AdhocJoin: Long preamble\n"); + adapter->preamble = cmd_type_long_preamble; + } else { + lbs_pr_debug(1, "AdhocJoin: Short preamble\n"); + adapter->preamble = cmd_type_short_preamble; + } + + libertas_set_radio_control(priv); + + lbs_pr_debug(1, "curbssparams.channel = %d\n", + adapter->curbssparams.channel); + lbs_pr_debug(1, "curbssparams.band = %c\n", adapter->curbssparams.band); + + adapter->adhoccreate = 0; + + ret = libertas_prepare_and_send_command(priv, cmd_802_11_ad_hoc_join, + 0, cmd_option_waitforrsp, + OID_802_11_SSID, pbssdesc); + + return ret; +} + +int libertas_stop_adhoc_network(wlan_private * priv) +{ + return libertas_prepare_and_send_command(priv, cmd_802_11_ad_hoc_stop, + 0, cmd_option_waitforrsp, 0, NULL); +} + +/** + * @brief Send Deauthentication Request + * + * @param priv A pointer to wlan_private structure + * @return 0--success, -1--fail + */ +int libertas_send_deauthentication(wlan_private * priv) +{ + return libertas_prepare_and_send_command(priv, cmd_802_11_deauthenticate, + 0, cmd_option_waitforrsp, 0, NULL); +} + +/** + * @brief Set Idle Off + * + * @param priv A pointer to wlan_private structure + * @return 0 --success, otherwise fail + */ +int libertas_idle_off(wlan_private * priv) +{ + wlan_adapter *adapter = priv->adapter; + int ret = 0; + const u8 zeromac[] = { 0, 0, 0, 0, 0, 0 }; + int i; + + ENTER(); + + if (adapter->connect_status == libertas_disconnected) { + if (adapter->inframode == wlan802_11infrastructure) { + if (memcmp(adapter->previousbssid, zeromac, + sizeof(zeromac)) != 0) { + + lbs_pr_debug(1, "Previous SSID = %s\n", + adapter->previousssid.ssid); + lbs_pr_debug(1, "Previous BSSID = " + "%02x:%02x:%02x:%02x:%02x:%02x:\n", + adapter->previousbssid[0], + adapter->previousbssid[1], + adapter->previousbssid[2], + adapter->previousbssid[3], + adapter->previousbssid[4], + adapter->previousbssid[5]); + + i = libertas_find_SSID_in_list(adapter, + &adapter->previousssid, + adapter->previousbssid, + adapter->inframode); + + if (i < 0) { + libertas_send_specific_BSSID_scan(priv, + adapter-> + previousbssid, + 1); + i = libertas_find_SSID_in_list(adapter, + &adapter-> + previousssid, + adapter-> + previousbssid, + adapter-> + inframode); + } + + if (i < 0) { + /* If the BSSID could not be found, try just the SSID */ + i = libertas_find_SSID_in_list(adapter, + &adapter-> + previousssid, NULL, + adapter-> + inframode); + } + + if (i < 0) { + libertas_send_specific_SSID_scan(priv, + &adapter-> + previousssid, + 1); + i = libertas_find_SSID_in_list(adapter, + &adapter-> + previousssid, NULL, + adapter-> + inframode); + } + + if (i >= 0) { + ret = + wlan_associate(priv, + &adapter-> + scantable[i]); + } + } + } else if (adapter->inframode == wlan802_11ibss) { + ret = libertas_prepare_and_send_command(priv, + cmd_802_11_ad_hoc_start, + 0, + cmd_option_waitforrsp, + 0, &adapter->previousssid); + } + } + /* else it is connected */ + + lbs_pr_debug(1, "\nwlanidle is off"); + LEAVE(); + return ret; +} + +/** + * @brief Set Idle On + * + * @param priv A pointer to wlan_private structure + * @return 0 --success, otherwise fail + */ +int libertas_idle_on(wlan_private * priv) +{ + wlan_adapter *adapter = priv->adapter; + int ret = 0; + + if (adapter->connect_status == libertas_connected) { + if (adapter->inframode == wlan802_11infrastructure) { + lbs_pr_debug(1, "Previous SSID = %s\n", + adapter->previousssid.ssid); + memmove(&adapter->previousssid, + &adapter->curbssparams.ssid, + sizeof(struct WLAN_802_11_SSID)); + libertas_send_deauth(priv); + + } else if (adapter->inframode == wlan802_11ibss) { + ret = libertas_stop_adhoc_network(priv); + } + + } + + lbs_pr_debug(1, "\nwlanidle is on"); + + return ret; +} + +/** + * @brief This function prepares command of authenticate. + * + * @param priv A pointer to wlan_private structure + * @param cmd A pointer to cmd_ds_command structure + * @param pdata_buf Void cast of pointer to a BSSID to authenticate with + * + * @return 0 or -1 + */ +int libertas_cmd_80211_authenticate(wlan_private * priv, + struct cmd_ds_command *cmd, + void *pdata_buf) +{ + wlan_adapter *adapter = priv->adapter; + struct cmd_ds_802_11_authenticate *pauthenticate = + &cmd->params.auth; + u8 *bssid = pdata_buf; + + cmd->command = cpu_to_le16(cmd_802_11_authenticate); + cmd->size = + cpu_to_le16(sizeof(struct cmd_ds_802_11_authenticate) + + S_DS_GEN); + + pauthenticate->authtype = adapter->secinfo.authmode; + memcpy(pauthenticate->macaddr, bssid, ETH_ALEN); + + lbs_pr_debug(1, "AUTH_CMD: Bssid is : %x:%x:%x:%x:%x:%x\n", + bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5]); + + return 0; +} + +int libertas_cmd_80211_deauthenticate(wlan_private * priv, + struct cmd_ds_command *cmd) +{ + wlan_adapter *adapter = priv->adapter; + struct cmd_ds_802_11_deauthenticate *dauth = &cmd->params.deauth; + + ENTER(); + + cmd->command = cpu_to_le16(cmd_802_11_deauthenticate); + cmd->size = + cpu_to_le16(sizeof(struct cmd_ds_802_11_deauthenticate) + + S_DS_GEN); + + /* set AP MAC address */ + memmove(dauth->macaddr, adapter->curbssparams.bssid, + ETH_ALEN); + + /* Reason code 3 = Station is leaving */ +#define REASON_CODE_STA_LEAVING 3 + dauth->reasoncode = cpu_to_le16(REASON_CODE_STA_LEAVING); + + LEAVE(); + return 0; +} + +int libertas_cmd_80211_associate(wlan_private * priv, + struct cmd_ds_command *cmd, void *pdata_buf) +{ + wlan_adapter *adapter = priv->adapter; + struct cmd_ds_802_11_associate *passo = &cmd->params.associate; + int ret = 0; + struct bss_descriptor *pbssdesc; + u8 *card_rates; + u8 *pos; + int card_rates_size; + u16 tmpcap; + struct mrvlietypes_ssidparamset *ssid; + struct mrvlietypes_phyparamset *phy; + struct mrvlietypes_ssparamset *ss; + struct mrvlietypes_ratesparamset *rates; + struct mrvlietypes_rsnparamset *rsn; + + ENTER(); + + pbssdesc = pdata_buf; + pos = (u8 *) passo; + + if (!adapter) { + ret = -1; + goto done; + } + + cmd->command = cpu_to_le16(cmd_802_11_associate); + + /* Save so we know which BSS Desc to use in the response handler */ + adapter->pattemptedbssdesc = pbssdesc; + + memcpy(passo->peerstaaddr, + pbssdesc->macaddress, sizeof(passo->peerstaaddr)); + pos += sizeof(passo->peerstaaddr); + + /* set the listen interval */ + passo->listeninterval = adapter->listeninterval; + + pos += sizeof(passo->capinfo); + pos += sizeof(passo->listeninterval); + pos += sizeof(passo->bcnperiod); + pos += sizeof(passo->dtimperiod); + + ssid = (struct mrvlietypes_ssidparamset *) pos; + ssid->header.type = cpu_to_le16(TLV_TYPE_SSID); + ssid->header.len = pbssdesc->ssid.ssidlength; + memcpy(ssid->ssid, pbssdesc->ssid.ssid, ssid->header.len); + pos += sizeof(ssid->header) + ssid->header.len; + ssid->header.len = cpu_to_le16(ssid->header.len); + + phy = (struct mrvlietypes_phyparamset *) pos; + phy->header.type = cpu_to_le16(TLV_TYPE_PHY_DS); + phy->header.len = sizeof(phy->fh_ds.dsparamset); + memcpy(&phy->fh_ds.dsparamset, + &pbssdesc->phyparamset.dsparamset.currentchan, + sizeof(phy->fh_ds.dsparamset)); + pos += sizeof(phy->header) + phy->header.len; + phy->header.len = cpu_to_le16(phy->header.len); + + ss = (struct mrvlietypes_ssparamset *) pos; + ss->header.type = cpu_to_le16(TLV_TYPE_CF); + ss->header.len = sizeof(ss->cf_ibss.cfparamset); + pos += sizeof(ss->header) + ss->header.len; + ss->header.len = cpu_to_le16(ss->header.len); + + rates = (struct mrvlietypes_ratesparamset *) pos; + rates->header.type = cpu_to_le16(TLV_TYPE_RATES); + + memcpy(&rates->rates, &pbssdesc->libertas_supported_rates, WLAN_SUPPORTED_RATES); + + card_rates = libertas_supported_rates; + card_rates_size = sizeof(libertas_supported_rates); + + if (get_common_rates(adapter, rates->rates, WLAN_SUPPORTED_RATES, + card_rates, card_rates_size)) { + ret = -1; + goto done; + } + + rates->header.len = min_t(size_t, strlen(rates->rates), WLAN_SUPPORTED_RATES); + adapter->curbssparams.numofrates = rates->header.len; + + pos += sizeof(rates->header) + rates->header.len; + rates->header.len = cpu_to_le16(rates->header.len); + + if (adapter->secinfo.WPAenabled || adapter->secinfo.WPA2enabled) { + rsn = (struct mrvlietypes_rsnparamset *) pos; + rsn->header.type = (u16) adapter->wpa_ie[0]; /* WPA_IE or WPA2_IE */ + rsn->header.type = cpu_to_le16(rsn->header.type); + rsn->header.len = (u16) adapter->wpa_ie[1]; + memcpy(rsn->rsnie, &adapter->wpa_ie[2], rsn->header.len); + lbs_dbg_hex("ASSOC_CMD: RSN IE", (u8 *) rsn, + sizeof(rsn->header) + rsn->header.len); + pos += sizeof(rsn->header) + rsn->header.len; + rsn->header.len = cpu_to_le16(rsn->header.len); + } + + /* update curbssparams */ + adapter->curbssparams.channel = + (pbssdesc->phyparamset.dsparamset.currentchan); + + /* Copy the infra. association rates into Current BSS state structure */ + memcpy(&adapter->curbssparams.datarates, &rates->rates, + min_t(size_t, sizeof(adapter->curbssparams.datarates), rates->header.len)); + + lbs_pr_debug(1, "ASSOC_CMD: rates->header.len = %d\n", rates->header.len); + + /* set IBSS field */ + if (pbssdesc->inframode == wlan802_11infrastructure) { +#define CAPINFO_ESS_MODE 1 + passo->capinfo.ess = CAPINFO_ESS_MODE; + } + + if (libertas_parse_dnld_countryinfo_11d(priv)) { + ret = -1; + goto done; + } + + cmd->size = cpu_to_le16((u16) (pos - (u8 *) passo) + S_DS_GEN); + + /* set the capability info at last */ + memcpy(&tmpcap, &pbssdesc->cap, sizeof(passo->capinfo)); + tmpcap &= CAPINFO_MASK; + lbs_pr_debug(1, "ASSOC_CMD: tmpcap=%4X CAPINFO_MASK=%4X\n", + tmpcap, CAPINFO_MASK); + tmpcap = cpu_to_le16(tmpcap); + memcpy(&passo->capinfo, &tmpcap, sizeof(passo->capinfo)); + + done: + LEAVE(); + return ret; +} + +int libertas_cmd_80211_ad_hoc_start(wlan_private * priv, + struct cmd_ds_command *cmd, void *pssid) +{ + wlan_adapter *adapter = priv->adapter; + struct cmd_ds_802_11_ad_hoc_start *adhs = &cmd->params.ads; + int ret = 0; + int cmdappendsize = 0; + int i; + u16 tmpcap; + struct bss_descriptor *pbssdesc; + struct WLAN_802_11_SSID *ssid = pssid; + + ENTER(); + + if (!adapter) { + ret = -1; + goto done; + } + + cmd->command = cpu_to_le16(cmd_802_11_ad_hoc_start); + + pbssdesc = &adapter->curbssparams.bssdescriptor; + adapter->pattemptedbssdesc = pbssdesc; + + /* + * Fill in the parameters for 2 data structures: + * 1. cmd_ds_802_11_ad_hoc_start command + * 2. adapter->scantable[i] + * + * Driver will fill up SSID, bsstype,IBSS param, Physical Param, + * probe delay, and cap info. + * + * Firmware will fill up beacon period, DTIM, Basic rates + * and operational rates. + */ + + memset(adhs->SSID, 0, IW_ESSID_MAX_SIZE); + + memcpy(adhs->SSID, ssid->ssid, ssid->ssidlength); + + lbs_pr_debug(1, "ADHOC_S_CMD: SSID = %s\n", adhs->SSID); + + memset(pbssdesc->ssid.ssid, 0, IW_ESSID_MAX_SIZE); + memcpy(pbssdesc->ssid.ssid, ssid->ssid, ssid->ssidlength); + + pbssdesc->ssid.ssidlength = ssid->ssidlength; + + /* set the BSS type */ + adhs->bsstype = cmd_bss_type_ibss; + pbssdesc->inframode = wlan802_11ibss; + adhs->beaconperiod = adapter->beaconperiod; + + /* set Physical param set */ +#define DS_PARA_IE_ID 3 +#define DS_PARA_IE_LEN 1 + + adhs->phyparamset.dsparamset.elementid = DS_PARA_IE_ID; + adhs->phyparamset.dsparamset.len = DS_PARA_IE_LEN; + + WARN_ON(!adapter->adhocchannel); + + lbs_pr_debug(1, "ADHOC_S_CMD: Creating ADHOC on channel %d\n", + adapter->adhocchannel); + + adapter->curbssparams.channel = adapter->adhocchannel; + + pbssdesc->channel = adapter->adhocchannel; + adhs->phyparamset.dsparamset.currentchan = adapter->adhocchannel; + + memcpy(&pbssdesc->phyparamset, + &adhs->phyparamset, sizeof(union ieeetypes_phyparamset)); + + /* set IBSS param set */ +#define IBSS_PARA_IE_ID 6 +#define IBSS_PARA_IE_LEN 2 + + adhs->ssparamset.ibssparamset.elementid = IBSS_PARA_IE_ID; + adhs->ssparamset.ibssparamset.len = IBSS_PARA_IE_LEN; + adhs->ssparamset.ibssparamset.atimwindow = adapter->atimwindow; + memcpy(&pbssdesc->ssparamset, + &adhs->ssparamset, sizeof(union IEEEtypes_ssparamset)); + + /* set capability info */ + adhs->cap.ess = 0; + adhs->cap.ibss = 1; + pbssdesc->cap.ibss = 1; + + /* probedelay */ + adhs->probedelay = cpu_to_le16(cmd_scan_probe_delay_time); + + /* set up privacy in adapter->scantable[i] */ + if (adapter->secinfo.WEPstatus == wlan802_11WEPenabled) { + +#define AD_HOC_CAP_PRIVACY_ON 1 + lbs_pr_debug(1, "ADHOC_S_CMD: WEPstatus set, privacy to WEP\n"); + pbssdesc->privacy = wlan802_11privfilter8021xWEP; + adhs->cap.privacy = AD_HOC_CAP_PRIVACY_ON; + } else { + lbs_pr_debug(1, "ADHOC_S_CMD: WEPstatus NOT set, Setting " + "privacy to ACCEPT ALL\n"); + pbssdesc->privacy = wlan802_11privfilteracceptall; + } + + memset(adhs->datarate, 0, sizeof(adhs->datarate)); + + if (adapter->adhoc_grate_enabled) { + memcpy(adhs->datarate, libertas_adhoc_rates_g, + min(sizeof(adhs->datarate), sizeof(libertas_adhoc_rates_g))); + } else { + memcpy(adhs->datarate, libertas_adhoc_rates_b, + min(sizeof(adhs->datarate), sizeof(libertas_adhoc_rates_b))); + } + + /* Find the last non zero */ + for (i = 0; i < sizeof(adhs->datarate) && adhs->datarate[i]; i++) ; + + adapter->curbssparams.numofrates = i; + + /* Copy the ad-hoc creating rates into Current BSS state structure */ + memcpy(&adapter->curbssparams.datarates, + &adhs->datarate, adapter->curbssparams.numofrates); + + lbs_pr_debug(1, "ADHOC_S_CMD: rates=%02x %02x %02x %02x \n", + adhs->datarate[0], adhs->datarate[1], + adhs->datarate[2], adhs->datarate[3]); + + lbs_pr_debug(1, "ADHOC_S_CMD: AD HOC Start command is ready\n"); + + if (libertas_create_dnld_countryinfo_11d(priv)) { + lbs_pr_debug(1, "ADHOC_S_CMD: dnld_countryinfo_11d failed\n"); + ret = -1; + goto done; + } + + cmd->size = + cpu_to_le16(sizeof(struct cmd_ds_802_11_ad_hoc_start) + + S_DS_GEN + cmdappendsize); + + memcpy(&tmpcap, &adhs->cap, sizeof(u16)); + tmpcap = cpu_to_le16(tmpcap); + memcpy(&adhs->cap, &tmpcap, sizeof(u16)); + + ret = 0; +done: + LEAVE(); + return ret; +} + +int libertas_cmd_80211_ad_hoc_stop(wlan_private * priv, + struct cmd_ds_command *cmd) +{ + cmd->command = cpu_to_le16(cmd_802_11_ad_hoc_stop); + cmd->size = cpu_to_le16(S_DS_GEN); + + return 0; +} + +int libertas_cmd_80211_ad_hoc_join(wlan_private * priv, + struct cmd_ds_command *cmd, void *pdata_buf) +{ + wlan_adapter *adapter = priv->adapter; + struct cmd_ds_802_11_ad_hoc_join *padhocjoin = &cmd->params.adj; + struct bss_descriptor *pbssdesc = pdata_buf; + int cmdappendsize = 0; + int ret = 0; + u8 *card_rates; + int card_rates_size; + u16 tmpcap; + int i; + + ENTER(); + + adapter->pattemptedbssdesc = pbssdesc; + + cmd->command = cpu_to_le16(cmd_802_11_ad_hoc_join); + + padhocjoin->bssdescriptor.bsstype = cmd_bss_type_ibss; + + padhocjoin->bssdescriptor.beaconperiod = pbssdesc->beaconperiod; + + memcpy(&padhocjoin->bssdescriptor.BSSID, + &pbssdesc->macaddress, ETH_ALEN); + + memcpy(&padhocjoin->bssdescriptor.SSID, + &pbssdesc->ssid.ssid, pbssdesc->ssid.ssidlength); + + memcpy(&padhocjoin->bssdescriptor.phyparamset, + &pbssdesc->phyparamset, sizeof(union ieeetypes_phyparamset)); + + memcpy(&padhocjoin->bssdescriptor.ssparamset, + &pbssdesc->ssparamset, sizeof(union IEEEtypes_ssparamset)); + + memcpy(&tmpcap, &pbssdesc->cap, sizeof(struct ieeetypes_capinfo)); + tmpcap &= CAPINFO_MASK; + + lbs_pr_debug(1, "ADHOC_J_CMD: tmpcap=%4X CAPINFO_MASK=%4X\n", + tmpcap, CAPINFO_MASK); + memcpy(&padhocjoin->bssdescriptor.cap, &tmpcap, + sizeof(struct ieeetypes_capinfo)); + + /* information on BSSID descriptor passed to FW */ + lbs_pr_debug(1, + "ADHOC_J_CMD: BSSID = %2x-%2x-%2x-%2x-%2x-%2x, SSID = %s\n", + padhocjoin->bssdescriptor.BSSID[0], + padhocjoin->bssdescriptor.BSSID[1], + padhocjoin->bssdescriptor.BSSID[2], + padhocjoin->bssdescriptor.BSSID[3], + padhocjoin->bssdescriptor.BSSID[4], + padhocjoin->bssdescriptor.BSSID[5], + padhocjoin->bssdescriptor.SSID); + + lbs_pr_debug(1, "ADHOC_J_CMD: Data Rate = %x\n", + (u32) padhocjoin->bssdescriptor.datarates); + + /* failtimeout */ + padhocjoin->failtimeout = cpu_to_le16(MRVDRV_ASSOCIATION_TIME_OUT); + + /* probedelay */ + padhocjoin->probedelay = + cpu_to_le16(cmd_scan_probe_delay_time); + + /* Copy Data rates from the rates recorded in scan response */ + memset(padhocjoin->bssdescriptor.datarates, 0, + sizeof(padhocjoin->bssdescriptor.datarates)); + memcpy(padhocjoin->bssdescriptor.datarates, pbssdesc->datarates, + min(sizeof(padhocjoin->bssdescriptor.datarates), + sizeof(pbssdesc->datarates))); + + card_rates = libertas_supported_rates; + card_rates_size = sizeof(libertas_supported_rates); + + adapter->curbssparams.channel = pbssdesc->channel; + + if (get_common_rates(adapter, padhocjoin->bssdescriptor.datarates, + sizeof(padhocjoin->bssdescriptor.datarates), + card_rates, card_rates_size)) { + lbs_pr_debug(1, "ADHOC_J_CMD: get_common_rates returns error.\n"); + ret = -1; + goto done; + } + + /* Find the last non zero */ + for (i = 0; i < sizeof(padhocjoin->bssdescriptor.datarates) + && padhocjoin->bssdescriptor.datarates[i]; i++) ; + + adapter->curbssparams.numofrates = i; + + /* + * Copy the adhoc joining rates to Current BSS State structure + */ + memcpy(adapter->curbssparams.datarates, + padhocjoin->bssdescriptor.datarates, + adapter->curbssparams.numofrates); + + padhocjoin->bssdescriptor.ssparamset.ibssparamset.atimwindow = + cpu_to_le16(pbssdesc->atimwindow); + + if (adapter->secinfo.WEPstatus == wlan802_11WEPenabled) { + padhocjoin->bssdescriptor.cap.privacy = AD_HOC_CAP_PRIVACY_ON; + } + + if (adapter->psmode == wlan802_11powermodemax_psp) { + /* wake up first */ + enum WLAN_802_11_POWER_MODE Localpsmode; + + Localpsmode = wlan802_11powermodecam; + ret = libertas_prepare_and_send_command(priv, + cmd_802_11_ps_mode, + cmd_act_set, + 0, 0, &Localpsmode); + + if (ret) { + ret = -1; + goto done; + } + } + + if (libertas_parse_dnld_countryinfo_11d(priv)) { + ret = -1; + goto done; + } + + cmd->size = + cpu_to_le16(sizeof(struct cmd_ds_802_11_ad_hoc_join) + + S_DS_GEN + cmdappendsize); + + memcpy(&tmpcap, &padhocjoin->bssdescriptor.cap, + sizeof(struct ieeetypes_capinfo)); + tmpcap = cpu_to_le16(tmpcap); + + memcpy(&padhocjoin->bssdescriptor.cap, + &tmpcap, sizeof(struct ieeetypes_capinfo)); + + done: + LEAVE(); + return ret; +} + +int libertas_ret_80211_associate(wlan_private * priv, + struct cmd_ds_command *resp) +{ + wlan_adapter *adapter = priv->adapter; + int ret = 0; + union iwreq_data wrqu; + struct ieeetypes_assocrsp *passocrsp; + struct bss_descriptor *pbssdesc; + + ENTER(); + + passocrsp = (struct ieeetypes_assocrsp *) & resp->params; + + if (passocrsp->statuscode) { + + libertas_mac_event_disconnected(priv); + + lbs_pr_debug(1, + "ASSOC_RESP: Association failed, status code = %d\n", + passocrsp->statuscode); + + ret = -1; + goto done; + } + + lbs_dbg_hex("ASSOC_RESP:", (void *)&resp->params, + le16_to_cpu(resp->size) - S_DS_GEN); + + /* Send a Media Connected event, according to the Spec */ + adapter->connect_status = libertas_connected; + + /* Set the attempted BSSID Index to current */ + pbssdesc = adapter->pattemptedbssdesc; + + lbs_pr_debug(1, "ASSOC_RESP: %s\n", pbssdesc->ssid.ssid); + + /* Set the new SSID to current SSID */ + memcpy(&adapter->curbssparams.ssid, + &pbssdesc->ssid, sizeof(struct WLAN_802_11_SSID)); + + /* Set the new BSSID (AP's MAC address) to current BSSID */ + memcpy(adapter->curbssparams.bssid, + pbssdesc->macaddress, ETH_ALEN); + + /* Make a copy of current BSSID descriptor */ + memcpy(&adapter->curbssparams.bssdescriptor, + pbssdesc, sizeof(struct bss_descriptor)); + + lbs_pr_debug(1, "ASSOC_RESP: currentpacketfilter is %x\n", + adapter->currentpacketfilter); + + adapter->SNR[TYPE_RXPD][TYPE_AVG] = 0; + adapter->NF[TYPE_RXPD][TYPE_AVG] = 0; + + memset(adapter->rawSNR, 0x00, sizeof(adapter->rawSNR)); + memset(adapter->rawNF, 0x00, sizeof(adapter->rawNF)); + adapter->nextSNRNF = 0; + adapter->numSNRNF = 0; + + netif_carrier_on(priv->wlan_dev.netdev); + netif_wake_queue(priv->wlan_dev.netdev); + + lbs_pr_debug(1, "ASSOC_RESP: Associated \n"); + + memcpy(wrqu.ap_addr.sa_data, adapter->curbssparams.bssid, ETH_ALEN); + wrqu.ap_addr.sa_family = ARPHRD_ETHER; + wireless_send_event(priv->wlan_dev.netdev, SIOCGIWAP, &wrqu, NULL); + + done: + LEAVE(); + return ret; +} + +int libertas_ret_80211_disassociate(wlan_private * priv, + struct cmd_ds_command *resp) +{ + ENTER(); + + libertas_mac_event_disconnected(priv); + + LEAVE(); + return 0; +} + +int libertas_ret_80211_ad_hoc_start(wlan_private * priv, + struct cmd_ds_command *resp) +{ + wlan_adapter *adapter = priv->adapter; + int ret = 0; + u16 command = le16_to_cpu(resp->command); + u16 result = le16_to_cpu(resp->result); + struct cmd_ds_802_11_ad_hoc_result *padhocresult; + union iwreq_data wrqu; + struct bss_descriptor *pbssdesc; + + ENTER(); + + padhocresult = &resp->params.result; + + lbs_pr_debug(1, "ADHOC_S_RESP: size = %d\n", le16_to_cpu(resp->size)); + lbs_pr_debug(1, "ADHOC_S_RESP: command = %x\n", command); + lbs_pr_debug(1, "ADHOC_S_RESP: result = %x\n", result); + + pbssdesc = adapter->pattemptedbssdesc; + + /* + * Join result code 0 --> SUCCESS + */ + if (result) { + lbs_pr_debug(1, "ADHOC_RESP failed\n"); + if (adapter->connect_status == libertas_connected) { + libertas_mac_event_disconnected(priv); + } + + memset(&adapter->curbssparams.bssdescriptor, + 0x00, sizeof(adapter->curbssparams.bssdescriptor)); + + LEAVE(); + return -1; + } + + /* + * Now the join cmd should be successful + * If BSSID has changed use SSID to compare instead of BSSID + */ + lbs_pr_debug(1, "ADHOC_J_RESP %s\n", pbssdesc->ssid.ssid); + + /* Send a Media Connected event, according to the Spec */ + adapter->connect_status = libertas_connected; + + if (command == cmd_ret_802_11_ad_hoc_start) { + /* Update the created network descriptor with the new BSSID */ + memcpy(pbssdesc->macaddress, + padhocresult->BSSID, ETH_ALEN); + } else { + + /* Make a copy of current BSSID descriptor, only needed for join since + * the current descriptor is already being used for adhoc start + */ + memmove(&adapter->curbssparams.bssdescriptor, + pbssdesc, sizeof(struct bss_descriptor)); + } + + /* Set the BSSID from the joined/started descriptor */ + memcpy(&adapter->curbssparams.bssid, + pbssdesc->macaddress, ETH_ALEN); + + /* Set the new SSID to current SSID */ + memcpy(&adapter->curbssparams.ssid, + &pbssdesc->ssid, sizeof(struct WLAN_802_11_SSID)); + + netif_carrier_on(priv->wlan_dev.netdev); + netif_wake_queue(priv->wlan_dev.netdev); + + memset(&wrqu, 0, sizeof(wrqu)); + memcpy(wrqu.ap_addr.sa_data, adapter->curbssparams.bssid, ETH_ALEN); + wrqu.ap_addr.sa_family = ARPHRD_ETHER; + wireless_send_event(priv->wlan_dev.netdev, SIOCGIWAP, &wrqu, NULL); + + lbs_pr_debug(1, "ADHOC_RESP: - Joined/Started Ad Hoc\n"); + lbs_pr_debug(1, "ADHOC_RESP: channel = %d\n", adapter->adhocchannel); + lbs_pr_debug(1, "ADHOC_RESP: BSSID = %02x:%02x:%02x:%02x:%02x:%02x\n", + padhocresult->BSSID[0], padhocresult->BSSID[1], + padhocresult->BSSID[2], padhocresult->BSSID[3], + padhocresult->BSSID[4], padhocresult->BSSID[5]); + + LEAVE(); + return ret; +} + +int libertas_ret_80211_ad_hoc_stop(wlan_private * priv, + struct cmd_ds_command *resp) +{ + ENTER(); + + libertas_mac_event_disconnected(priv); + + LEAVE(); + return 0; +} diff --git a/drivers/net/wireless/libertas/join.h b/drivers/net/wireless/libertas/join.h new file mode 100644 index 00000000000..8efa2455af9 --- /dev/null +++ b/drivers/net/wireless/libertas/join.h @@ -0,0 +1,64 @@ +/* -*- mode: C; tab-width: 4; indent-tabs-mode: nil -*- */ +/* vi: set expandtab shiftwidth=4 tabstop=4 textwidth=78: */ + +/** + * Interface for the wlan infrastructure and adhoc join routines + * + * Driver interface functions and type declarations for the join module + * implemented in wlan_join.c. Process all start/join requests for + * both adhoc and infrastructure networks + */ +#ifndef _WLAN_JOIN_H +#define _WLAN_JOIN_H + +#include "defs.h" + +struct cmd_ds_command; +extern int libertas_cmd_80211_authenticate(wlan_private * priv, + struct cmd_ds_command *cmd, + void *pdata_buf); +extern int libertas_cmd_80211_ad_hoc_join(wlan_private * priv, + struct cmd_ds_command *cmd, + void *pdata_buf); +extern int libertas_cmd_80211_ad_hoc_stop(wlan_private * priv, + struct cmd_ds_command *cmd); +extern int libertas_cmd_80211_ad_hoc_start(wlan_private * priv, + struct cmd_ds_command *cmd, + void *pssid); +extern int libertas_cmd_80211_deauthenticate(wlan_private * priv, + struct cmd_ds_command *cmd); +extern int libertas_cmd_80211_associate(wlan_private * priv, + struct cmd_ds_command *cmd, + void *pdata_buf); + +extern int libertas_ret_80211_ad_hoc_start(wlan_private * priv, + struct cmd_ds_command *resp); +extern int libertas_ret_80211_ad_hoc_stop(wlan_private * priv, + struct cmd_ds_command *resp); +extern int libertas_ret_80211_disassociate(wlan_private * priv, + struct cmd_ds_command *resp); +extern int libertas_ret_80211_associate(wlan_private * priv, + struct cmd_ds_command *resp); + +extern int libertas_idle_on(wlan_private * priv); +extern int libertas_idle_off(wlan_private * priv); + +extern int libertas_do_adhocstop_ioctl(wlan_private * priv); +extern int libertas_reassociation_thread(void *data); + +struct WLAN_802_11_SSID; +struct bss_descriptor; + +extern int libertas_start_adhoc_network(wlan_private * priv, + struct WLAN_802_11_SSID *adhocssid); +extern int libertas_join_adhoc_network(wlan_private * priv, struct bss_descriptor *pbssdesc); +extern int libertas_stop_adhoc_network(wlan_private * priv); + +extern int libertas_send_deauthentication(wlan_private * priv); +extern int libertas_send_deauth(wlan_private * priv); + +extern int libertas_do_adhocstop_ioctl(wlan_private * priv); + +int wlan_associate(wlan_private * priv, struct bss_descriptor * pbssdesc); + +#endif diff --git a/drivers/net/wireless/libertas/main.c b/drivers/net/wireless/libertas/main.c new file mode 100644 index 00000000000..dcbf102a057 --- /dev/null +++ b/drivers/net/wireless/libertas/main.c @@ -0,0 +1,1258 @@ +/** + * This file contains the major functions in WLAN + * driver. It includes init, exit, open, close and main + * thread etc.. + */ + +#include <linux/delay.h> +#include <linux/freezer.h> +#include <linux/etherdevice.h> +#include <linux/netdevice.h> +#include <linux/if_arp.h> + +#include <net/iw_handler.h> + +#include "host.h" +#include "sbi.h" +#include "decl.h" +#include "dev.h" +#include "fw.h" +#include "wext.h" +#include "debugfs.h" +#include "assoc.h" + +#ifdef ENABLE_PM +static struct pm_dev *wlan_pm_dev = NULL; +#endif + +#define WLAN_TX_PWR_DEFAULT 20 /*100mW */ +#define WLAN_TX_PWR_US_DEFAULT 20 /*100mW */ +#define WLAN_TX_PWR_JP_DEFAULT 16 /*50mW */ +#define WLAN_TX_PWR_FR_DEFAULT 20 /*100mW */ +#define WLAN_TX_PWR_EMEA_DEFAULT 20 /*100mW */ + +/* Format { channel, frequency (MHz), maxtxpower } */ +/* band: 'B/G', region: USA FCC/Canada IC */ +static struct chan_freq_power channel_freq_power_US_BG[] = { + {1, 2412, WLAN_TX_PWR_US_DEFAULT}, + {2, 2417, WLAN_TX_PWR_US_DEFAULT}, + {3, 2422, WLAN_TX_PWR_US_DEFAULT}, + {4, 2427, WLAN_TX_PWR_US_DEFAULT}, + {5, 2432, WLAN_TX_PWR_US_DEFAULT}, + {6, 2437, WLAN_TX_PWR_US_DEFAULT}, + {7, 2442, WLAN_TX_PWR_US_DEFAULT}, + {8, 2447, WLAN_TX_PWR_US_DEFAULT}, + {9, 2452, WLAN_TX_PWR_US_DEFAULT}, + {10, 2457, WLAN_TX_PWR_US_DEFAULT}, + {11, 2462, WLAN_TX_PWR_US_DEFAULT} +}; + +/* band: 'B/G', region: Europe ETSI */ +static struct chan_freq_power channel_freq_power_EU_BG[] = { + {1, 2412, WLAN_TX_PWR_EMEA_DEFAULT}, + {2, 2417, WLAN_TX_PWR_EMEA_DEFAULT}, + {3, 2422, WLAN_TX_PWR_EMEA_DEFAULT}, + {4, 2427, WLAN_TX_PWR_EMEA_DEFAULT}, + {5, 2432, WLAN_TX_PWR_EMEA_DEFAULT}, + {6, 2437, WLAN_TX_PWR_EMEA_DEFAULT}, + {7, 2442, WLAN_TX_PWR_EMEA_DEFAULT}, + {8, 2447, WLAN_TX_PWR_EMEA_DEFAULT}, + {9, 2452, WLAN_TX_PWR_EMEA_DEFAULT}, + {10, 2457, WLAN_TX_PWR_EMEA_DEFAULT}, + {11, 2462, WLAN_TX_PWR_EMEA_DEFAULT}, + {12, 2467, WLAN_TX_PWR_EMEA_DEFAULT}, + {13, 2472, WLAN_TX_PWR_EMEA_DEFAULT} +}; + +/* band: 'B/G', region: Spain */ +static struct chan_freq_power channel_freq_power_SPN_BG[] = { + {10, 2457, WLAN_TX_PWR_DEFAULT}, + {11, 2462, WLAN_TX_PWR_DEFAULT} +}; + +/* band: 'B/G', region: France */ +static struct chan_freq_power channel_freq_power_FR_BG[] = { + {10, 2457, WLAN_TX_PWR_FR_DEFAULT}, + {11, 2462, WLAN_TX_PWR_FR_DEFAULT}, + {12, 2467, WLAN_TX_PWR_FR_DEFAULT}, + {13, 2472, WLAN_TX_PWR_FR_DEFAULT} +}; + +/* band: 'B/G', region: Japan */ +static struct chan_freq_power channel_freq_power_JPN_BG[] = { + {1, 2412, WLAN_TX_PWR_JP_DEFAULT}, + {2, 2417, WLAN_TX_PWR_JP_DEFAULT}, + {3, 2422, WLAN_TX_PWR_JP_DEFAULT}, + {4, 2427, WLAN_TX_PWR_JP_DEFAULT}, + {5, 2432, WLAN_TX_PWR_JP_DEFAULT}, + {6, 2437, WLAN_TX_PWR_JP_DEFAULT}, + {7, 2442, WLAN_TX_PWR_JP_DEFAULT}, + {8, 2447, WLAN_TX_PWR_JP_DEFAULT}, + {9, 2452, WLAN_TX_PWR_JP_DEFAULT}, + {10, 2457, WLAN_TX_PWR_JP_DEFAULT}, + {11, 2462, WLAN_TX_PWR_JP_DEFAULT}, + {12, 2467, WLAN_TX_PWR_JP_DEFAULT}, + {13, 2472, WLAN_TX_PWR_JP_DEFAULT}, + {14, 2484, WLAN_TX_PWR_JP_DEFAULT} +}; + +/** + * the structure for channel, frequency and power + */ +struct region_cfp_table { + u8 region; + struct chan_freq_power *cfp_BG; + int cfp_no_BG; +}; + +/** + * the structure for the mapping between region and CFP + */ +static struct region_cfp_table region_cfp_table[] = { + {0x10, /*US FCC */ + channel_freq_power_US_BG, + sizeof(channel_freq_power_US_BG) / sizeof(struct chan_freq_power), + } + , + {0x20, /*CANADA IC */ + channel_freq_power_US_BG, + sizeof(channel_freq_power_US_BG) / sizeof(struct chan_freq_power), + } + , + {0x30, /*EU*/ channel_freq_power_EU_BG, + sizeof(channel_freq_power_EU_BG) / sizeof(struct chan_freq_power), + } + , + {0x31, /*SPAIN*/ channel_freq_power_SPN_BG, + sizeof(channel_freq_power_SPN_BG) / sizeof(struct chan_freq_power), + } + , + {0x32, /*FRANCE*/ channel_freq_power_FR_BG, + sizeof(channel_freq_power_FR_BG) / sizeof(struct chan_freq_power), + } + , + {0x40, /*JAPAN*/ channel_freq_power_JPN_BG, + sizeof(channel_freq_power_JPN_BG) / sizeof(struct chan_freq_power), + } + , +/*Add new region here */ +}; + +/** + * the rates supported by the card + */ +u8 libertas_wlan_data_rates[WLAN_SUPPORTED_RATES] = + { 0x02, 0x04, 0x0B, 0x16, 0x00, 0x0C, 0x12, + 0x18, 0x24, 0x30, 0x48, 0x60, 0x6C, 0x00 +}; + +/** + * the rates supported + */ +u8 libertas_supported_rates[G_SUPPORTED_RATES] = + { 0x82, 0x84, 0x8b, 0x96, 0x0c, 0x12, 0x18, 0x24, 0x30, 0x48, 0x60, 0x6c, +0 }; + +/** + * the rates supported for ad-hoc G mode + */ +u8 libertas_adhoc_rates_g[G_SUPPORTED_RATES] = + { 0x82, 0x84, 0x8b, 0x96, 0x0c, 0x12, 0x18, 0x24, 0x30, 0x48, 0x60, 0x6c, +0 }; + +/** + * the rates supported for ad-hoc B mode + */ +u8 libertas_adhoc_rates_b[4] = { 0x82, 0x84, 0x8b, 0x96 }; + +/** + * the global variable of a pointer to wlan_private + * structure variable + */ +static wlan_private *wlanpriv = NULL; + +#define MAX_DEVS 5 +static struct net_device *libertas_devs[MAX_DEVS]; +static int libertas_found = 0; + +/** + * the table to keep region code + */ +u16 libertas_region_code_to_index[MRVDRV_MAX_REGION_CODE] = + { 0x10, 0x20, 0x30, 0x31, 0x32, 0x40 }; + +static u8 *default_fw_name = "usb8388.bin"; + +/** + * Attributes exported through sysfs + */ + +/** + * @brief Get function for sysfs attribute libertas_mpp + */ +static ssize_t libertas_mpp_get(struct device * dev, + struct device_attribute *attr, char * buf) { + struct cmd_ds_mesh_access mesh_access; + + memset(&mesh_access, 0, sizeof(mesh_access)); + libertas_prepare_and_send_command(to_net_dev(dev)->priv, + cmd_mesh_access, + cmd_act_mesh_get_mpp, + cmd_option_waitforrsp, 0, (void *)&mesh_access); + + return snprintf(buf, 3, "%d\n", mesh_access.data[0]); +} + +/** + * @brief Set function for sysfs attribute libertas_mpp + */ +static ssize_t libertas_mpp_set(struct device * dev, + struct device_attribute *attr, const char * buf, size_t count) { + struct cmd_ds_mesh_access mesh_access; + + + memset(&mesh_access, 0, sizeof(mesh_access)); + sscanf(buf, "%d", &(mesh_access.data[0])); + libertas_prepare_and_send_command((to_net_dev(dev))->priv, + cmd_mesh_access, + cmd_act_mesh_set_mpp, + cmd_option_waitforrsp, 0, (void *)&mesh_access); + return strlen(buf); +} + +/** + * libertas_mpp attribute to be exported per mshX interface + * through sysfs (/sys/class/net/mshX/libertas-mpp) + */ +static DEVICE_ATTR(libertas_mpp, 0644, libertas_mpp_get, + libertas_mpp_set ); + +/** + * @brief Check if the device can be open and wait if necessary. + * + * @param dev A pointer to net_device structure + * @return 0 + * + * For USB adapter, on some systems the device open handler will be + * called before FW ready. Use the following flag check and wait + * function to work around the issue. + * + */ +static int pre_open_check(struct net_device *dev) { + wlan_private *priv = (wlan_private *) dev->priv; + wlan_adapter *adapter = priv->adapter; + int i = 0; + + while (!adapter->fw_ready && i < 20) { + i++; + msleep_interruptible(100); + } + if (!adapter->fw_ready) { + lbs_pr_info("FW not ready, pre_open_check() return failure\n"); + LEAVE(); + return -1; + } + + return 0; +} + +/** + * @brief This function opens the device + * + * @param dev A pointer to net_device structure + * @return 0 + */ +static int wlan_dev_open(struct net_device *dev) +{ + wlan_private *priv = (wlan_private *) dev->priv; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + + priv->open = 1; + + if (adapter->connect_status == libertas_connected) { + netif_carrier_on(priv->wlan_dev.netdev); + } else + netif_carrier_off(priv->wlan_dev.netdev); + + LEAVE(); + return 0; +} +/** + * @brief This function opens the mshX interface + * + * @param dev A pointer to net_device structure + * @return 0 + */ +static int mesh_open(struct net_device *dev) +{ + wlan_private *priv = (wlan_private *) dev->priv ; + + if(pre_open_check(dev) == -1) + return -1; + priv->mesh_open = 1 ; + netif_start_queue(priv->mesh_dev); + if (priv->infra_open == 0) + return wlan_dev_open(priv->wlan_dev.netdev) ; + return 0; +} + +/** + * @brief This function opens the ethX interface + * + * @param dev A pointer to net_device structure + * @return 0 + */ +static int wlan_open(struct net_device *dev) +{ + wlan_private *priv = (wlan_private *) dev->priv ; + + if(pre_open_check(dev) == -1) + return -1; + priv->infra_open = 1 ; + netif_wake_queue(priv->wlan_dev.netdev); + if (priv->open == 0) + return wlan_dev_open(priv->wlan_dev.netdev) ; + return 0; +} + +static int wlan_dev_close(struct net_device *dev) +{ + wlan_private *priv = dev->priv; + + ENTER(); + + netif_carrier_off(priv->wlan_dev.netdev); + priv->open = 0; + + LEAVE(); + return 0; +} + +/** + * @brief This function closes the mshX interface + * + * @param dev A pointer to net_device structure + * @return 0 + */ +static int mesh_close(struct net_device *dev) +{ + wlan_private *priv = (wlan_private *) (dev->priv); + + priv->mesh_open = 0; + netif_stop_queue(priv->mesh_dev); + if (priv->infra_open == 0) + return wlan_dev_close( ((wlan_private *) dev->priv)->wlan_dev.netdev) ; + else + return 0; +} + +/** + * @brief This function closes the ethX interface + * + * @param dev A pointer to net_device structure + * @return 0 + */ +static int wlan_close(struct net_device *dev) { + wlan_private *priv = (wlan_private *) dev->priv; + + netif_stop_queue(priv->wlan_dev.netdev); + priv->infra_open = 0; + if (priv->mesh_open == 0) + return wlan_dev_close( ((wlan_private *) dev->priv)->wlan_dev.netdev) ; + else + return 0; +} + + +#ifdef ENABLE_PM + +/** + * @brief This function is a callback function. it is called by + * kernel to enter or exit power saving mode. + * + * @param pmdev A pointer to pm_dev + * @param pmreq pm_request_t + * @param pmdata A pointer to pmdata + * @return 0 or -1 + */ +static int wlan_pm_callback(struct pm_dev *pmdev, pm_request_t pmreq, + void *pmdata) +{ + wlan_private *priv = wlanpriv; + wlan_adapter *adapter = priv->adapter; + struct net_device *dev = priv->wlan_dev.netdev; + + lbs_pr_debug(1, "WPRM_PM_CALLBACK: pmreq = %d.\n", pmreq); + + switch (pmreq) { + case PM_SUSPEND: + lbs_pr_debug(1, "WPRM_PM_CALLBACK: enter PM_SUSPEND.\n"); + + /* in associated mode */ + if (adapter->connect_status == libertas_connected) { + if ((adapter->psstate != PS_STATE_SLEEP) + ) { + lbs_pr_debug(1, + "wlan_pm_callback: can't enter sleep mode\n"); + return -1; + } else { + + /* + * Detach the network interface + * if the network is running + */ + if (netif_running(dev)) { + netif_device_detach(dev); + lbs_pr_debug(1, + "netif_device_detach().\n"); + } + libertas_sbi_suspend(priv); + } + break; + } + + /* in non associated mode */ + + /* + * Detach the network interface + * if the network is running + */ + if (netif_running(dev)) + netif_device_detach(dev); + + /* + * Storing and restoring of the regs be taken care + * at the driver rest will be done at wlan driver + * this makes driver independent of the card + */ + + libertas_sbi_suspend(priv); + + break; + + case PM_RESUME: + /* in associated mode */ + if (adapter->connect_status == libertas_connected) { + { + /* + * Bring the inteface up first + * This case should not happen still ... + */ + libertas_sbi_resume(priv); + + /* + * Attach the network interface + * if the network is running + */ + if (netif_running(dev)) { + netif_device_attach(dev); + lbs_pr_debug(1, + "after netif_device_attach().\n"); + } + lbs_pr_debug(1, + "After netif attach, in associated mode.\n"); + } + break; + } + + /* in non associated mode */ + + /* + * Bring the inteface up first + * This case should not happen still ... + */ + + libertas_sbi_resume(priv); + + if (netif_running(dev)) + netif_device_attach(dev); + + lbs_pr_debug(1, "after netif attach, in NON associated mode.\n"); + break; + } + + return 0; +} +#endif /* ENABLE_PM */ + +static int wlan_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + int ret = 0; + wlan_private *priv = dev->priv; + + ENTER(); + + if (priv->wlan_dev.dnld_sent || priv->adapter->TxLockFlag) { + priv->stats.tx_dropped++; + goto done; + } + + netif_stop_queue(priv->wlan_dev.netdev); + + if (libertas_process_tx(priv, skb) == 0) + dev->trans_start = jiffies; +done: + LEAVE(); + return ret; +} + +/** + * @brief Mark mesh packets and handover them to wlan_hard_start_xmit + * + */ +static int mesh_pre_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + wlan_private *priv = dev->priv; + ENTER(); + SET_MESH_FRAME(skb); + LEAVE(); + + return wlan_hard_start_xmit(skb, priv->wlan_dev.netdev); +} + +/** + * @brief Mark non-mesh packets and handover them to wlan_hard_start_xmit + * + */ +static int wlan_pre_start_xmit(struct sk_buff *skb, struct net_device *dev) { + ENTER(); + UNSET_MESH_FRAME(skb); + LEAVE(); + return wlan_hard_start_xmit(skb, dev); +} + +static void wlan_tx_timeout(struct net_device *dev) +{ + wlan_private *priv = (wlan_private *) dev->priv; + + ENTER(); + + lbs_pr_err("tx watch dog timeout!\n"); + + priv->wlan_dev.dnld_sent = DNLD_RES_RECEIVED; + dev->trans_start = jiffies; + + if (priv->adapter->currenttxskb) { + if (priv->adapter->radiomode == WLAN_RADIOMODE_RADIOTAP) { + /* If we are here, we have not received feedback from + the previous packet. Assume TX_FAIL and move on. */ + priv->adapter->eventcause = 0x01000000; + libertas_send_tx_feedback(priv); + } else + wake_up_interruptible(&priv->mainthread.waitq); + } else if (priv->adapter->connect_status == libertas_connected) + netif_wake_queue(priv->wlan_dev.netdev); + + LEAVE(); +} + +/** + * @brief This function returns the network statistics + * + * @param dev A pointer to wlan_private structure + * @return A pointer to net_device_stats structure + */ +static struct net_device_stats *wlan_get_stats(struct net_device *dev) +{ + wlan_private *priv = (wlan_private *) dev->priv; + + return &priv->stats; +} + +static int wlan_set_mac_address(struct net_device *dev, void *addr) +{ + int ret = 0; + wlan_private *priv = (wlan_private *) dev->priv; + wlan_adapter *adapter = priv->adapter; + struct sockaddr *phwaddr = addr; + + ENTER(); + + memset(adapter->current_addr, 0, ETH_ALEN); + + /* dev->dev_addr is 8 bytes */ + lbs_dbg_hex("dev->dev_addr:", dev->dev_addr, ETH_ALEN); + + lbs_dbg_hex("addr:", phwaddr->sa_data, ETH_ALEN); + memcpy(adapter->current_addr, phwaddr->sa_data, ETH_ALEN); + + ret = libertas_prepare_and_send_command(priv, cmd_802_11_mac_address, + cmd_act_set, + cmd_option_waitforrsp, 0, NULL); + + if (ret) { + lbs_pr_debug(1, "set mac address failed.\n"); + ret = -1; + goto done; + } + + lbs_dbg_hex("adapter->macaddr:", adapter->current_addr, ETH_ALEN); + memcpy(dev->dev_addr, adapter->current_addr, ETH_ALEN); + memcpy(((wlan_private *) dev->priv)->mesh_dev->dev_addr, adapter->current_addr, ETH_ALEN); + +done: + LEAVE(); + return ret; +} + +static int wlan_copy_multicast_address(wlan_adapter * adapter, + struct net_device *dev) +{ + int i = 0; + struct dev_mc_list *mcptr = dev->mc_list; + + for (i = 0; i < dev->mc_count; i++) { + memcpy(&adapter->multicastlist[i], mcptr->dmi_addr, ETH_ALEN); + mcptr = mcptr->next; + } + + return i; + +} + +static void wlan_set_multicast_list(struct net_device *dev) +{ + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + int oldpacketfilter; + + ENTER(); + + oldpacketfilter = adapter->currentpacketfilter; + + if (dev->flags & IFF_PROMISC) { + lbs_pr_debug(1, "enable Promiscuous mode\n"); + adapter->currentpacketfilter |= + cmd_act_mac_promiscuous_enable; + adapter->currentpacketfilter &= + ~(cmd_act_mac_all_multicast_enable | + cmd_act_mac_multicast_enable); + } else { + /* Multicast */ + adapter->currentpacketfilter &= + ~cmd_act_mac_promiscuous_enable; + + if (dev->flags & IFF_ALLMULTI || dev->mc_count > + MRVDRV_MAX_MULTICAST_LIST_SIZE) { + lbs_pr_debug(1, "Enabling All Multicast!\n"); + adapter->currentpacketfilter |= + cmd_act_mac_all_multicast_enable; + adapter->currentpacketfilter &= + ~cmd_act_mac_multicast_enable; + } else { + adapter->currentpacketfilter &= + ~cmd_act_mac_all_multicast_enable; + + if (!dev->mc_count) { + lbs_pr_debug(1, "No multicast addresses - " + "disabling multicast!\n"); + adapter->currentpacketfilter &= + ~cmd_act_mac_multicast_enable; + } else { + int i; + + adapter->currentpacketfilter |= + cmd_act_mac_multicast_enable; + + adapter->nr_of_multicastmacaddr = + wlan_copy_multicast_address(adapter, dev); + + lbs_pr_debug(1, "Multicast addresses: %d\n", + dev->mc_count); + + for (i = 0; i < dev->mc_count; i++) { + lbs_pr_debug(1, "Multicast address %d:" + "%x %x %x %x %x %x\n", i, + adapter->multicastlist[i][0], + adapter->multicastlist[i][1], + adapter->multicastlist[i][2], + adapter->multicastlist[i][3], + adapter->multicastlist[i][4], + adapter->multicastlist[i][5]); + } + /* set multicast addresses to firmware */ + libertas_prepare_and_send_command(priv, + cmd_mac_multicast_adr, + cmd_act_set, 0, 0, + NULL); + } + } + } + + if (adapter->currentpacketfilter != oldpacketfilter) { + libertas_set_mac_packet_filter(priv); + } + + LEAVE(); +} + +/** + * @brief This function hanldes the major job in WLAN driver. + * it handles the event generated by firmware, rx data received + * from firmware and tx data sent from kernel. + * + * @param data A pointer to wlan_thread structure + * @return 0 + */ +static int wlan_service_main_thread(void *data) +{ + struct wlan_thread *thread = data; + wlan_private *priv = thread->priv; + wlan_adapter *adapter = priv->adapter; + wait_queue_t wait; + u8 ireg = 0; + + ENTER(); + + wlan_activate_thread(thread); + + init_waitqueue_entry(&wait, current); + + for (;;) { + lbs_pr_debug(1, "main-thread 111: intcounter=%d " + "currenttxskb=%p dnld_sent=%d\n", + adapter->intcounter, + adapter->currenttxskb, priv->wlan_dev.dnld_sent); + + add_wait_queue(&thread->waitq, &wait); + set_current_state(TASK_INTERRUPTIBLE); + spin_lock_irq(&adapter->driver_lock); + if ((adapter->psstate == PS_STATE_SLEEP) || + (!adapter->intcounter + && (priv->wlan_dev.dnld_sent || adapter->cur_cmd || + list_empty(&adapter->cmdpendingq)))) { + lbs_pr_debug(1, + "main-thread sleeping... Conn=%d IntC=%d PS_mode=%d PS_State=%d\n", + adapter->connect_status, adapter->intcounter, + adapter->psmode, adapter->psstate); + spin_unlock_irq(&adapter->driver_lock); + schedule(); + } else + spin_unlock_irq(&adapter->driver_lock); + + + lbs_pr_debug(1, + "main-thread 222 (waking up): intcounter=%d currenttxskb=%p " + "dnld_sent=%d\n", adapter->intcounter, + adapter->currenttxskb, priv->wlan_dev.dnld_sent); + + set_current_state(TASK_RUNNING); + remove_wait_queue(&thread->waitq, &wait); + try_to_freeze(); + + lbs_pr_debug(1, "main-thread 333: intcounter=%d currenttxskb=%p " + "dnld_sent=%d\n", + adapter->intcounter, + adapter->currenttxskb, priv->wlan_dev.dnld_sent); + + if (kthread_should_stop() + || adapter->surpriseremoved) { + lbs_pr_debug(1, + "main-thread: break from main thread: surpriseremoved=0x%x\n", + adapter->surpriseremoved); + break; + } + + + spin_lock_irq(&adapter->driver_lock); + if (adapter->intcounter) { + u8 int_status; + adapter->intcounter = 0; + int_status = libertas_sbi_get_int_status(priv, &ireg); + + if (int_status) { + lbs_pr_debug(1, + "main-thread: reading HOST_INT_STATUS_REG failed\n"); + spin_unlock_irq(&adapter->driver_lock); + continue; + } + adapter->hisregcpy |= ireg; + } + + lbs_pr_debug(1, "main-thread 444: intcounter=%d currenttxskb=%p " + "dnld_sent=%d\n", + adapter->intcounter, + adapter->currenttxskb, priv->wlan_dev.dnld_sent); + + /* command response? */ + if (adapter->hisregcpy & his_cmdupldrdy) { + lbs_pr_debug(1, "main-thread: cmd response ready.\n"); + + adapter->hisregcpy &= ~his_cmdupldrdy; + spin_unlock_irq(&adapter->driver_lock); + libertas_process_rx_command(priv); + spin_lock_irq(&adapter->driver_lock); + } + + /* Any Card Event */ + if (adapter->hisregcpy & his_cardevent) { + lbs_pr_debug(1, "main-thread: Card Event Activity.\n"); + + adapter->hisregcpy &= ~his_cardevent; + + if (libertas_sbi_read_event_cause(priv)) { + lbs_pr_alert( + "main-thread: libertas_sbi_read_event_cause failed.\n"); + spin_unlock_irq(&adapter->driver_lock); + continue; + } + spin_unlock_irq(&adapter->driver_lock); + libertas_process_event(priv); + } else + spin_unlock_irq(&adapter->driver_lock); + + /* Check if we need to confirm Sleep Request received previously */ + if (adapter->psstate == PS_STATE_PRE_SLEEP) { + if (!priv->wlan_dev.dnld_sent && !adapter->cur_cmd) { + if (adapter->connect_status == + libertas_connected) { + lbs_pr_debug(1, + "main_thread: PRE_SLEEP--intcounter=%d currenttxskb=%p " + "dnld_sent=%d cur_cmd=%p, confirm now\n", + adapter->intcounter, + adapter->currenttxskb, + priv->wlan_dev.dnld_sent, + adapter->cur_cmd); + + libertas_ps_confirm_sleep(priv, + (u16) adapter->psmode); + } else { + /* workaround for firmware sending + * deauth/linkloss event immediately + * after sleep request, remove this + * after firmware fixes it + */ + adapter->psstate = PS_STATE_AWAKE; + lbs_pr_alert( + "main-thread: ignore PS_SleepConfirm in non-connected state\n"); + } + } + } + + /* The PS state is changed during processing of Sleep Request + * event above + */ + if ((priv->adapter->psstate == PS_STATE_SLEEP) || + (priv->adapter->psstate == PS_STATE_PRE_SLEEP)) + continue; + + /* Execute the next command */ + if (!priv->wlan_dev.dnld_sent && !priv->adapter->cur_cmd) + libertas_execute_next_command(priv); + + /* Wake-up command waiters which can't sleep in + * libertas_prepare_and_send_command + */ + if (!adapter->nr_cmd_pending) + wake_up_all(&adapter->cmd_pending); + + libertas_tx_runqueue(priv); + } + + del_timer(&adapter->command_timer); + adapter->nr_cmd_pending = 0; + wake_up_all(&adapter->cmd_pending); + wlan_deactivate_thread(thread); + + LEAVE(); + return 0; +} + +/** + * @brief This function adds the card. it will probe the + * card, allocate the wlan_priv and initialize the device. + * + * @param card A pointer to card + * @return A pointer to wlan_private structure + */ +wlan_private *wlan_add_card(void *card) +{ + struct net_device *dev = NULL; + struct net_device *mesh_dev = NULL; + wlan_private *priv = NULL; + + ENTER(); + + /* Allocate an Ethernet device and register it */ + if (!(dev = alloc_etherdev(sizeof(wlan_private)))) { + lbs_pr_alert( "Init ethernet device failed!\n"); + return NULL; + } + + priv = dev->priv; + + /* allocate buffer for wlan_adapter */ + if (!(priv->adapter = kmalloc(sizeof(wlan_adapter), GFP_KERNEL))) { + lbs_pr_alert( "Allocate buffer for wlan_adapter failed!\n"); + goto err_kmalloc; + } + + /* Allocate a virtual mesh device */ + if (!(mesh_dev = alloc_netdev(0, "msh%d", ether_setup))) { + lbs_pr_debug(1, "Init ethernet device failed!\n"); + return NULL; + } + + /* Both intervaces share the priv structure */ + mesh_dev->priv = priv; + + /* init wlan_adapter */ + memset(priv->adapter, 0, sizeof(wlan_adapter)); + + priv->wlan_dev.netdev = dev; + priv->wlan_dev.card = card; + priv->mesh_open = 0; + priv->infra_open = 0; + priv->mesh_dev = mesh_dev; + wlanpriv = priv; + + SET_MODULE_OWNER(dev); + SET_MODULE_OWNER(mesh_dev); + + /* Setup the OS Interface to our functions */ + dev->open = wlan_open; + dev->hard_start_xmit = wlan_pre_start_xmit; + dev->stop = wlan_close; + dev->do_ioctl = libertas_do_ioctl; + dev->set_mac_address = wlan_set_mac_address; + mesh_dev->open = mesh_open; + mesh_dev->hard_start_xmit = mesh_pre_start_xmit; + mesh_dev->stop = mesh_close; + mesh_dev->do_ioctl = libertas_do_ioctl; + memcpy(mesh_dev->dev_addr, wlanpriv->wlan_dev.netdev->dev_addr, + sizeof(wlanpriv->wlan_dev.netdev->dev_addr)); + +#define WLAN_WATCHDOG_TIMEOUT (5 * HZ) + + dev->tx_timeout = wlan_tx_timeout; + dev->get_stats = wlan_get_stats; + dev->watchdog_timeo = WLAN_WATCHDOG_TIMEOUT; + dev->ethtool_ops = &libertas_ethtool_ops; + mesh_dev->get_stats = wlan_get_stats; + mesh_dev->ethtool_ops = &libertas_ethtool_ops; + +#ifdef WIRELESS_EXT + dev->wireless_handlers = (struct iw_handler_def *)&libertas_handler_def; + mesh_dev->wireless_handlers = (struct iw_handler_def *)&libertas_handler_def; +#endif +#define NETIF_F_DYNALLOC 16 + dev->features |= NETIF_F_DYNALLOC; + dev->flags |= IFF_BROADCAST | IFF_MULTICAST; + dev->set_multicast_list = wlan_set_multicast_list; + + INIT_LIST_HEAD(&priv->adapter->cmdfreeq); + INIT_LIST_HEAD(&priv->adapter->cmdpendingq); + + spin_lock_init(&priv->adapter->driver_lock); + init_waitqueue_head(&priv->adapter->cmd_pending); + priv->adapter->nr_cmd_pending = 0; + + lbs_pr_debug(1, "Starting kthread...\n"); + priv->mainthread.priv = priv; + wlan_create_thread(wlan_service_main_thread, + &priv->mainthread, "wlan_main_service"); + + priv->assoc_thread = + create_singlethread_workqueue("libertas_assoc"); + INIT_DELAYED_WORK(&priv->assoc_work, wlan_association_worker); + + /* + * Register the device. Fillup the private data structure with + * relevant information from the card and request for the required + * IRQ. + */ + if (libertas_sbi_register_dev(priv) < 0) { + lbs_pr_info("failed to register wlan device!\n"); + goto err_registerdev; + } + + /* init FW and HW */ + if (libertas_init_fw(priv)) { + lbs_pr_debug(1, "Firmware Init failed\n"); + goto err_registerdev; + } + + if (register_netdev(dev)) { + lbs_pr_err("Cannot register network device!\n"); + goto err_init_fw; + } + + /* Register virtual mesh interface */ + if (register_netdev(mesh_dev)) { + lbs_pr_info("Cannot register mesh virtual interface!\n"); + goto err_init_fw; + } + + lbs_pr_info("%s: Marvell Wlan 802.11 adapter ", dev->name); + + libertas_debugfs_init_one(priv, dev); + + if (libertas_found == MAX_DEVS) + goto err_init_fw; + libertas_devs[libertas_found] = dev; + libertas_found++; +#ifdef ENABLE_PM + if (!(wlan_pm_dev = pm_register(PM_UNKNOWN_DEV, 0, wlan_pm_callback))) + lbs_pr_alert( "failed to register PM callback\n"); +#endif + if (device_create_file(&(mesh_dev->dev), &dev_attr_libertas_mpp)) + goto err_create_file; + + LEAVE(); + return priv; + +err_create_file: + device_remove_file(&(mesh_dev->dev), &dev_attr_libertas_mpp); +err_init_fw: + libertas_sbi_unregister_dev(priv); +err_registerdev: + destroy_workqueue(priv->assoc_thread); + /* Stop the thread servicing the interrupts */ + wake_up_interruptible(&priv->mainthread.waitq); + wlan_terminate_thread(&priv->mainthread); + kfree(priv->adapter); +err_kmalloc: + free_netdev(dev); + free_netdev(mesh_dev); + wlanpriv = NULL; + + LEAVE(); + return NULL; +} + +static void wake_pending_cmdnodes(wlan_private *priv) +{ + struct cmd_ctrl_node *cmdnode; + unsigned long flags; + + spin_lock_irqsave(&priv->adapter->driver_lock, flags); + list_for_each_entry(cmdnode, &priv->adapter->cmdpendingq, list) { + cmdnode->cmdwaitqwoken = 1; + wake_up_interruptible(&cmdnode->cmdwait_q); + } + spin_unlock_irqrestore(&priv->adapter->driver_lock, flags); +} + + +int wlan_remove_card(void *card) +{ + wlan_private *priv = libertas_sbi_get_priv(card); + wlan_adapter *adapter; + struct net_device *dev; + struct net_device *mesh_dev; + union iwreq_data wrqu; + int i; + + ENTER(); + + if (!priv) { + LEAVE(); + return 0; + } + + adapter = priv->adapter; + + if (!adapter) { + LEAVE(); + return 0; + } + + dev = priv->wlan_dev.netdev; + mesh_dev = priv->mesh_dev; + + netif_stop_queue(mesh_dev); + netif_stop_queue(priv->wlan_dev.netdev); + netif_carrier_off(priv->wlan_dev.netdev); + + wake_pending_cmdnodes(priv); + + device_remove_file(&(mesh_dev->dev), &dev_attr_libertas_mpp); + unregister_netdev(mesh_dev); + unregister_netdev(dev); + + cancel_delayed_work(&priv->assoc_work); + destroy_workqueue(priv->assoc_thread); + + if (adapter->psmode == wlan802_11powermodemax_psp) { + adapter->psmode = wlan802_11powermodecam; + libertas_ps_wakeup(priv, cmd_option_waitforrsp); + } + + memset(wrqu.ap_addr.sa_data, 0xaa, ETH_ALEN); + wrqu.ap_addr.sa_family = ARPHRD_ETHER; + wireless_send_event(priv->wlan_dev.netdev, SIOCGIWAP, &wrqu, NULL); + +#ifdef ENABLE_PM + pm_unregister(wlan_pm_dev); +#endif + + adapter->surpriseremoved = 1; + + /* Stop the thread servicing the interrupts */ + wlan_terminate_thread(&priv->mainthread); + + libertas_debugfs_remove_one(priv); + + lbs_pr_debug(1, "Free adapter\n"); + libertas_free_adapter(priv); + + for (i = 0; i<libertas_found; i++) { + if (libertas_devs[i]==priv->wlan_dev.netdev) { + libertas_devs[i] = libertas_devs[--libertas_found]; + libertas_devs[libertas_found] = NULL ; + break ; + } + } + + lbs_pr_debug(1, "Unregister finish\n"); + + priv->wlan_dev.netdev = NULL; + priv->mesh_dev = NULL ; + free_netdev(mesh_dev); + free_netdev(dev); + wlanpriv = NULL; + + LEAVE(); + return 0; +} + +/** + * @brief This function finds the CFP in + * region_cfp_table based on region and band parameter. + * + * @param region The region code + * @param band The band + * @param cfp_no A pointer to CFP number + * @return A pointer to CFP + */ +struct chan_freq_power *libertas_get_region_cfp_table(u8 region, u8 band, int *cfp_no) +{ + int i, end; + + ENTER(); + + end = sizeof(region_cfp_table)/sizeof(struct region_cfp_table); + + for (i = 0; i < end ; i++) { + lbs_pr_debug(1, "region_cfp_table[i].region=%d\n", + region_cfp_table[i].region); + if (region_cfp_table[i].region == region) { + *cfp_no = region_cfp_table[i].cfp_no_BG; + LEAVE(); + return region_cfp_table[i].cfp_BG; + } + } + + LEAVE(); + return NULL; +} + +int libertas_set_regiontable(wlan_private * priv, u8 region, u8 band) +{ + wlan_adapter *adapter = priv->adapter; + int i = 0; + + struct chan_freq_power *cfp; + int cfp_no; + + ENTER(); + + memset(adapter->region_channel, 0, sizeof(adapter->region_channel)); + + { + cfp = libertas_get_region_cfp_table(region, band, &cfp_no); + if (cfp != NULL) { + adapter->region_channel[i].nrcfp = cfp_no; + adapter->region_channel[i].CFP = cfp; + } else { + lbs_pr_debug(1, "wrong region code %#x in band B-G\n", + region); + return -1; + } + adapter->region_channel[i].valid = 1; + adapter->region_channel[i].region = region; + adapter->region_channel[i].band = band; + i++; + } + LEAVE(); + return 0; +} + +/** + * @brief This function handles the interrupt. it will change PS + * state if applicable. it will wake up main_thread to handle + * the interrupt event as well. + * + * @param dev A pointer to net_device structure + * @return n/a + */ +void libertas_interrupt(struct net_device *dev) +{ + wlan_private *priv = dev->priv; + + ENTER(); + + lbs_pr_debug(1, "libertas_interrupt: intcounter=%d\n", + priv->adapter->intcounter); + + priv->adapter->intcounter++; + + if (priv->adapter->psstate == PS_STATE_SLEEP) { + priv->adapter->psstate = PS_STATE_AWAKE; + netif_wake_queue(dev); + } + + wake_up_interruptible(&priv->mainthread.waitq); + + LEAVE(); +} + +static int wlan_init_module(void) +{ + int ret = 0; + + ENTER(); + + if (libertas_fw_name == NULL) { + libertas_fw_name = default_fw_name; + } + + libertas_debugfs_init(); + + if (libertas_sbi_register()) { + ret = -1; + libertas_debugfs_remove(); + goto done; + } + +done: + LEAVE(); + return ret; +} + +static void wlan_cleanup_module(void) +{ + int i; + + ENTER(); + + for (i = 0; i<libertas_found; i++) { + wlan_private *priv = libertas_devs[i]->priv; + reset_device(priv); + } + + libertas_sbi_unregister(); + libertas_debugfs_remove(); + + LEAVE(); +} + +module_init(wlan_init_module); +module_exit(wlan_cleanup_module); + +MODULE_DESCRIPTION("M-WLAN Driver"); +MODULE_AUTHOR("Marvell International Ltd."); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/wireless/libertas/radiotap.h b/drivers/net/wireless/libertas/radiotap.h new file mode 100644 index 00000000000..5d118f40cfb --- /dev/null +++ b/drivers/net/wireless/libertas/radiotap.h @@ -0,0 +1,57 @@ +#include <net/ieee80211_radiotap.h> + +struct tx_radiotap_hdr { + struct ieee80211_radiotap_header hdr; + u8 rate; + u8 txpower; + u8 rts_retries; + u8 data_retries; +#if 0 + u8 pad[IEEE80211_RADIOTAP_HDRLEN - 12]; +#endif +} __attribute__ ((packed)); + +#define TX_RADIOTAP_PRESENT ( \ + (1 << IEEE80211_RADIOTAP_RATE) | \ + (1 << IEEE80211_RADIOTAP_DBM_TX_POWER) | \ + (1 << IEEE80211_RADIOTAP_RTS_RETRIES) | \ + (1 << IEEE80211_RADIOTAP_DATA_RETRIES) | \ + 0) + +#define IEEE80211_FC_VERSION_MASK 0x0003 +#define IEEE80211_FC_TYPE_MASK 0x000c +#define IEEE80211_FC_TYPE_MGT 0x0000 +#define IEEE80211_FC_TYPE_CTL 0x0004 +#define IEEE80211_FC_TYPE_DATA 0x0008 +#define IEEE80211_FC_SUBTYPE_MASK 0x00f0 +#define IEEE80211_FC_TOFROMDS_MASK 0x0300 +#define IEEE80211_FC_TODS_MASK 0x0100 +#define IEEE80211_FC_FROMDS_MASK 0x0200 +#define IEEE80211_FC_NODS 0x0000 +#define IEEE80211_FC_TODS 0x0100 +#define IEEE80211_FC_FROMDS 0x0200 +#define IEEE80211_FC_DSTODS 0x0300 + +struct rx_radiotap_hdr { + struct ieee80211_radiotap_header hdr; + u8 flags; + u8 rate; + u16 chan_freq; + u16 chan_flags; + u8 antenna; + u8 antsignal; + u16 rx_flags; +#if 0 + u8 pad[IEEE80211_RADIOTAP_HDRLEN - 18]; +#endif +} __attribute__ ((packed)); + +#define RX_RADIOTAP_PRESENT ( \ + (1 << IEEE80211_RADIOTAP_FLAGS) | \ + (1 << IEEE80211_RADIOTAP_RATE) | \ + (1 << IEEE80211_RADIOTAP_CHANNEL) | \ + (1 << IEEE80211_RADIOTAP_ANTENNA) | \ + (1 << IEEE80211_RADIOTAP_DB_ANTSIGNAL) |\ + (1 << IEEE80211_RADIOTAP_RX_FLAGS) | \ + 0) + diff --git a/drivers/net/wireless/libertas/rx.c b/drivers/net/wireless/libertas/rx.c new file mode 100644 index 00000000000..7e3f78f092d --- /dev/null +++ b/drivers/net/wireless/libertas/rx.c @@ -0,0 +1,459 @@ +/** + * This file contains the handling of RX in wlan driver. + */ +#include <linux/etherdevice.h> +#include <linux/types.h> + +#include "hostcmd.h" +#include "radiotap.h" +#include "decl.h" +#include "dev.h" +#include "wext.h" + +struct eth803hdr { + u8 dest_addr[6]; + u8 src_addr[6]; + u16 h803_len; +} __attribute__ ((packed)); + +struct rfc1042hdr { + u8 llc_dsap; + u8 llc_ssap; + u8 llc_ctrl; + u8 snap_oui[3]; + u16 snap_type; +} __attribute__ ((packed)); + +struct rxpackethdr { + struct rxpd rx_pd; + struct eth803hdr eth803_hdr; + struct rfc1042hdr rfc1042_hdr; +} __attribute__ ((packed)); + +struct rx80211packethdr { + struct rxpd rx_pd; + void *eth80211_hdr; +} __attribute__ ((packed)); + +static int process_rxed_802_11_packet(wlan_private * priv, struct sk_buff *skb); + +/** + * @brief This function computes the avgSNR . + * + * @param priv A pointer to wlan_private structure + * @return avgSNR + */ +static u8 wlan_getavgsnr(wlan_private * priv) +{ + u8 i; + u16 temp = 0; + wlan_adapter *adapter = priv->adapter; + if (adapter->numSNRNF == 0) + return 0; + for (i = 0; i < adapter->numSNRNF; i++) + temp += adapter->rawSNR[i]; + return (u8) (temp / adapter->numSNRNF); + +} + +/** + * @brief This function computes the AvgNF + * + * @param priv A pointer to wlan_private structure + * @return AvgNF + */ +static u8 wlan_getavgnf(wlan_private * priv) +{ + u8 i; + u16 temp = 0; + wlan_adapter *adapter = priv->adapter; + if (adapter->numSNRNF == 0) + return 0; + for (i = 0; i < adapter->numSNRNF; i++) + temp += adapter->rawNF[i]; + return (u8) (temp / adapter->numSNRNF); + +} + +/** + * @brief This function save the raw SNR/NF to our internel buffer + * + * @param priv A pointer to wlan_private structure + * @param prxpd A pointer to rxpd structure of received packet + * @return n/a + */ +static void wlan_save_rawSNRNF(wlan_private * priv, struct rxpd *p_rx_pd) +{ + wlan_adapter *adapter = priv->adapter; + if (adapter->numSNRNF < adapter->data_avg_factor) + adapter->numSNRNF++; + adapter->rawSNR[adapter->nextSNRNF] = p_rx_pd->snr; + adapter->rawNF[adapter->nextSNRNF] = p_rx_pd->nf; + adapter->nextSNRNF++; + if (adapter->nextSNRNF >= adapter->data_avg_factor) + adapter->nextSNRNF = 0; + return; +} + +/** + * @brief This function computes the RSSI in received packet. + * + * @param priv A pointer to wlan_private structure + * @param prxpd A pointer to rxpd structure of received packet + * @return n/a + */ +static void wlan_compute_rssi(wlan_private * priv, struct rxpd *p_rx_pd) +{ + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + lbs_pr_debug(1, "rxpd: SNR = %d, NF = %d\n", p_rx_pd->snr, p_rx_pd->nf); + lbs_pr_debug(1, "Before computing SNR: SNR- avg = %d, NF-avg = %d\n", + adapter->SNR[TYPE_RXPD][TYPE_AVG] / AVG_SCALE, + adapter->NF[TYPE_RXPD][TYPE_AVG] / AVG_SCALE); + + adapter->SNR[TYPE_RXPD][TYPE_NOAVG] = p_rx_pd->snr; + adapter->NF[TYPE_RXPD][TYPE_NOAVG] = p_rx_pd->nf; + wlan_save_rawSNRNF(priv, p_rx_pd); + + adapter->rxpd_rate = p_rx_pd->rx_rate; + + adapter->SNR[TYPE_RXPD][TYPE_AVG] = wlan_getavgsnr(priv) * AVG_SCALE; + adapter->NF[TYPE_RXPD][TYPE_AVG] = wlan_getavgnf(priv) * AVG_SCALE; + lbs_pr_debug(1, "After computing SNR: SNR-avg = %d, NF-avg = %d\n", + adapter->SNR[TYPE_RXPD][TYPE_AVG] / AVG_SCALE, + adapter->NF[TYPE_RXPD][TYPE_AVG] / AVG_SCALE); + + adapter->RSSI[TYPE_RXPD][TYPE_NOAVG] = + CAL_RSSI(adapter->SNR[TYPE_RXPD][TYPE_NOAVG], + adapter->NF[TYPE_RXPD][TYPE_NOAVG]); + + adapter->RSSI[TYPE_RXPD][TYPE_AVG] = + CAL_RSSI(adapter->SNR[TYPE_RXPD][TYPE_AVG] / AVG_SCALE, + adapter->NF[TYPE_RXPD][TYPE_AVG] / AVG_SCALE); + + LEAVE(); +} + +int libertas_upload_rx_packet(wlan_private * priv, struct sk_buff *skb) +{ + lbs_pr_debug(1, "skb->data=%p\n", skb->data); + + if(IS_MESH_FRAME(skb)) + skb->dev = priv->mesh_dev; + else + skb->dev = priv->wlan_dev.netdev; + skb->protocol = eth_type_trans(skb, priv->wlan_dev.netdev); + skb->ip_summed = CHECKSUM_UNNECESSARY; + + netif_rx(skb); + + return 0; +} + +/** + * @brief This function processes received packet and forwards it + * to kernel/upper layer + * + * @param priv A pointer to wlan_private + * @param skb A pointer to skb which includes the received packet + * @return 0 or -1 + */ +int libertas_process_rxed_packet(wlan_private * priv, struct sk_buff *skb) +{ + wlan_adapter *adapter = priv->adapter; + int ret = 0; + + struct rxpackethdr *p_rx_pkt; + struct rxpd *p_rx_pd; + + int hdrchop; + struct ethhdr *p_ethhdr; + + const u8 rfc1042_eth_hdr[] = { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 }; + + ENTER(); + + if (priv->adapter->debugmode & MRVDRV_DEBUG_RX_PATH) + lbs_dbg_hex("RX packet: ", skb->data, + min_t(unsigned int, skb->len, 100)); + + if (priv->adapter->linkmode == WLAN_LINKMODE_802_11) + return process_rxed_802_11_packet(priv, skb); + + p_rx_pkt = (struct rxpackethdr *) skb->data; + p_rx_pd = &p_rx_pkt->rx_pd; + if (p_rx_pd->rx_control & RxPD_MESH_FRAME) + SET_MESH_FRAME(skb); + else + UNSET_MESH_FRAME(skb); + + lbs_dbg_hex("RX Data: Before chop rxpd", skb->data, + min_t(unsigned int, skb->len, 100)); + + if (skb->len < (ETH_HLEN + 8 + sizeof(struct rxpd))) { + lbs_pr_debug(1, "RX error: FRAME RECEIVED WITH BAD LENGTH\n"); + priv->stats.rx_length_errors++; + ret = 0; + goto done; + } + + /* + * Check rxpd status and update 802.3 stat, + */ + if (!(p_rx_pd->status & MRVDRV_RXPD_STATUS_OK)) { + lbs_pr_debug(1, "RX error: frame received with bad status\n"); + lbs_pr_alert("rxpd Not OK\n"); + priv->stats.rx_errors++; + ret = 0; + goto done; + } + + lbs_pr_debug(1, "RX Data: skb->len - sizeof(RxPd) = %d - %d = %d\n", + skb->len, sizeof(struct rxpd), skb->len - sizeof(struct rxpd)); + + lbs_dbg_hex("RX Data: Dest", p_rx_pkt->eth803_hdr.dest_addr, + sizeof(p_rx_pkt->eth803_hdr.dest_addr)); + lbs_dbg_hex("RX Data: Src", p_rx_pkt->eth803_hdr.src_addr, + sizeof(p_rx_pkt->eth803_hdr.src_addr)); + + if (memcmp(&p_rx_pkt->rfc1042_hdr, + rfc1042_eth_hdr, sizeof(rfc1042_eth_hdr)) == 0) { + /* + * Replace the 803 header and rfc1042 header (llc/snap) with an + * EthernetII header, keep the src/dst and snap_type (ethertype) + * + * The firmware only passes up SNAP frames converting + * all RX Data from 802.11 to 802.2/LLC/SNAP frames. + * + * To create the Ethernet II, just move the src, dst address right + * before the snap_type. + */ + p_ethhdr = (struct ethhdr *) + ((u8 *) & p_rx_pkt->eth803_hdr + + sizeof(p_rx_pkt->eth803_hdr) + sizeof(p_rx_pkt->rfc1042_hdr) + - sizeof(p_rx_pkt->eth803_hdr.dest_addr) + - sizeof(p_rx_pkt->eth803_hdr.src_addr) + - sizeof(p_rx_pkt->rfc1042_hdr.snap_type)); + + memcpy(p_ethhdr->h_source, p_rx_pkt->eth803_hdr.src_addr, + sizeof(p_ethhdr->h_source)); + memcpy(p_ethhdr->h_dest, p_rx_pkt->eth803_hdr.dest_addr, + sizeof(p_ethhdr->h_dest)); + + /* Chop off the rxpd + the excess memory from the 802.2/llc/snap header + * that was removed + */ + hdrchop = (u8 *) p_ethhdr - (u8 *) p_rx_pkt; + } else { + lbs_dbg_hex("RX Data: LLC/SNAP", + (u8 *) & p_rx_pkt->rfc1042_hdr, + sizeof(p_rx_pkt->rfc1042_hdr)); + + /* Chop off the rxpd */ + hdrchop = (u8 *) & p_rx_pkt->eth803_hdr - (u8 *) p_rx_pkt; + } + + /* Chop off the leading header bytes so the skb points to the start of + * either the reconstructed EthII frame or the 802.2/llc/snap frame + */ + skb_pull(skb, hdrchop); + + /* Take the data rate from the rxpd structure + * only if the rate is auto + */ + if (adapter->is_datarate_auto) + adapter->datarate = libertas_index_to_data_rate(p_rx_pd->rx_rate); + + wlan_compute_rssi(priv, p_rx_pd); + + lbs_pr_debug(1, "RX Data: size of actual packet = %d\n", skb->len); + if (libertas_upload_rx_packet(priv, skb)) { + lbs_pr_debug(1, "RX error: libertas_upload_rx_packet" + " returns failure\n"); + ret = -1; + goto done; + } + priv->stats.rx_bytes += skb->len; + priv->stats.rx_packets++; + + ret = 0; +done: + LEAVE(); + + return ret; +} + +/** + * @brief This function converts Tx/Rx rates from the Marvell WLAN format + * (see Table 2 in Section 3.1) to IEEE80211_RADIOTAP_RATE units (500 Kb/s) + * + * @param rate Input rate + * @return Output Rate (0 if invalid) + */ +static u8 convert_mv_rate_to_radiotap(u8 rate) +{ + switch (rate) { + case 0: /* 1 Mbps */ + return 2; + case 1: /* 2 Mbps */ + return 4; + case 2: /* 5.5 Mbps */ + return 11; + case 3: /* 11 Mbps */ + return 22; + case 4: /* 6 Mbps */ + return 12; + case 5: /* 9 Mbps */ + return 18; + case 6: /* 12 Mbps */ + return 24; + case 7: /* 18 Mbps */ + return 36; + case 8: /* 24 Mbps */ + return 48; + case 9: /* 36 Mbps */ + return 72; + case 10: /* 48 Mbps */ + return 96; + case 11: /* 54 Mbps */ + return 108; + } + lbs_pr_alert( "Invalid Marvell WLAN rate (%i)\n", rate); + return 0; +} + +/** + * @brief This function processes a received 802.11 packet and forwards it + * to kernel/upper layer + * + * @param priv A pointer to wlan_private + * @param skb A pointer to skb which includes the received packet + * @return 0 or -1 + */ +static int process_rxed_802_11_packet(wlan_private * priv, struct sk_buff *skb) +{ + wlan_adapter *adapter = priv->adapter; + int ret = 0; + + struct rx80211packethdr *p_rx_pkt; + struct rxpd *prxpd; + struct rx_radiotap_hdr radiotap_hdr; + struct rx_radiotap_hdr *pradiotap_hdr; + + ENTER(); + + p_rx_pkt = (struct rx80211packethdr *) skb->data; + prxpd = &p_rx_pkt->rx_pd; + + // lbs_dbg_hex("RX Data: Before chop rxpd", skb->data, min(skb->len, 100)); + + if (skb->len < (ETH_HLEN + 8 + sizeof(struct rxpd))) { + lbs_pr_debug(1, "RX error: FRAME RECEIVED WITH BAD LENGTH\n"); + priv->stats.rx_length_errors++; + ret = 0; + goto done; + } + + /* + * Check rxpd status and update 802.3 stat, + */ + if (!(prxpd->status & MRVDRV_RXPD_STATUS_OK)) { + //lbs_pr_debug(1, "RX error: frame received with bad status\n"); + priv->stats.rx_errors++; + } + + lbs_pr_debug(1, "RX Data: skb->len - sizeof(RxPd) = %d - %d = %d\n", + skb->len, sizeof(struct rxpd), skb->len - sizeof(struct rxpd)); + + /* create the exported radio header */ + switch (priv->adapter->radiomode) { + case WLAN_RADIOMODE_NONE: + /* no radio header */ + /* chop the rxpd */ + skb_pull(skb, sizeof(struct rxpd)); + break; + + case WLAN_RADIOMODE_RADIOTAP: + /* radiotap header */ + radiotap_hdr.hdr.it_version = 0; + /* XXX must check this value for pad */ + radiotap_hdr.hdr.it_pad = 0; + radiotap_hdr.hdr.it_len = sizeof(struct rx_radiotap_hdr); + radiotap_hdr.hdr.it_present = RX_RADIOTAP_PRESENT; + /* unknown values */ + radiotap_hdr.flags = 0; + radiotap_hdr.chan_freq = 0; + radiotap_hdr.chan_flags = 0; + radiotap_hdr.antenna = 0; + /* known values */ + radiotap_hdr.rate = convert_mv_rate_to_radiotap(prxpd->rx_rate); + /* XXX must check no carryout */ + radiotap_hdr.antsignal = prxpd->snr + prxpd->nf; + radiotap_hdr.rx_flags = 0; + if (!(prxpd->status & MRVDRV_RXPD_STATUS_OK)) + radiotap_hdr.rx_flags |= IEEE80211_RADIOTAP_F_RX_BADFCS; + //memset(radiotap_hdr.pad, 0x11, IEEE80211_RADIOTAP_HDRLEN - 18); + + // lbs_dbg_hex1("RX radiomode packet BEF: ", skb->data, min(skb->len, 100)); + + /* chop the rxpd */ + skb_pull(skb, sizeof(struct rxpd)); + + /* add space for the new radio header */ + if ((skb_headroom(skb) < sizeof(struct rx_radiotap_hdr)) && + pskb_expand_head(skb, sizeof(struct rx_radiotap_hdr), 0, + GFP_ATOMIC)) { + lbs_pr_alert( "%s: couldn't pskb_expand_head\n", + __func__); + } + + pradiotap_hdr = + (struct rx_radiotap_hdr *)skb_push(skb, + sizeof(struct + rx_radiotap_hdr)); + memcpy(pradiotap_hdr, &radiotap_hdr, + sizeof(struct rx_radiotap_hdr)); + //lbs_dbg_hex1("RX radiomode packet AFT: ", skb->data, min(skb->len, 100)); + break; + + default: + /* unknown header */ + lbs_pr_alert( "Unknown radiomode (%i)\n", + priv->adapter->radiomode); + /* don't export any header */ + /* chop the rxpd */ + skb_pull(skb, sizeof(struct rxpd)); + break; + } + + /* Take the data rate from the rxpd structure + * only if the rate is auto + */ + if (adapter->is_datarate_auto) { + adapter->datarate = libertas_index_to_data_rate(prxpd->rx_rate); + } + + wlan_compute_rssi(priv, prxpd); + + lbs_pr_debug(1, "RX Data: size of actual packet = %d\n", skb->len); + + if (libertas_upload_rx_packet(priv, skb)) { + lbs_pr_debug(1, "RX error: libertas_upload_rx_packet " + "returns failure\n"); + ret = -1; + goto done; + } + + priv->stats.rx_bytes += skb->len; + priv->stats.rx_packets++; + + ret = 0; +done: + LEAVE(); + + skb->protocol = __constant_htons(0x0019); /* ETH_P_80211_RAW */ + + return (ret); +} diff --git a/drivers/net/wireless/libertas/sbi.h b/drivers/net/wireless/libertas/sbi.h new file mode 100644 index 00000000000..59d3a59ccef --- /dev/null +++ b/drivers/net/wireless/libertas/sbi.h @@ -0,0 +1,40 @@ +/** + * This file contains IF layer definitions. + */ + +#ifndef _SBI_H_ +#define _SBI_H_ + +#include <linux/interrupt.h> + +#include "defs.h" + +/** INT status Bit Definition*/ +#define his_cmddnldrdy 0x01 +#define his_cardevent 0x02 +#define his_cmdupldrdy 0x04 + +#ifndef DEV_NAME_LEN +#define DEV_NAME_LEN 32 +#endif + +#define SBI_EVENT_CAUSE_SHIFT 3 + +/* Probe and Check if the card is present*/ +int libertas_sbi_register_dev(wlan_private * priv); +int libertas_sbi_unregister_dev(wlan_private *); +int libertas_sbi_get_int_status(wlan_private * priv, u8 *); +int libertas_sbi_register(void); +void libertas_sbi_unregister(void); +int libertas_sbi_prog_firmware(wlan_private *); + +int libertas_sbi_read_event_cause(wlan_private *); +int libertas_sbi_host_to_card(wlan_private * priv, u8 type, u8 * payload, u16 nb); +wlan_private *libertas_sbi_get_priv(void *card); + +#ifdef ENABLE_PM +int libertas_sbi_suspend(wlan_private *); +int libertas_sbi_resume(wlan_private *); +#endif + +#endif /* _SBI_H */ diff --git a/drivers/net/wireless/libertas/scan.c b/drivers/net/wireless/libertas/scan.c new file mode 100644 index 00000000000..e1870623895 --- /dev/null +++ b/drivers/net/wireless/libertas/scan.c @@ -0,0 +1,2044 @@ +/* -*- mode: C; tab-width: 4; indent-tabs-mode: nil -*- */ +/* vi: set expandtab shiftwidth=4 tabstop=4 textwidth=78: */ + +/** + * Functions implementing wlan scan IOCTL and firmware command APIs + * + * IOCTL handlers as well as command preperation and response routines + * for sending scan commands to the firmware. + */ +#include <linux/ctype.h> +#include <linux/if.h> +#include <linux/netdevice.h> +#include <linux/wireless.h> + +#include <net/ieee80211.h> +#include <net/iw_handler.h> + +#include "host.h" +#include "decl.h" +#include "dev.h" +#include "scan.h" + +//! Approximate amount of data needed to pass a scan result back to iwlist +#define MAX_SCAN_CELL_SIZE (IW_EV_ADDR_LEN \ + + IW_ESSID_MAX_SIZE \ + + IW_EV_UINT_LEN \ + + IW_EV_FREQ_LEN \ + + IW_EV_QUAL_LEN \ + + IW_ESSID_MAX_SIZE \ + + IW_EV_PARAM_LEN \ + + 40) /* 40 for WPAIE */ + +//! Memory needed to store a max sized channel List TLV for a firmware scan +#define CHAN_TLV_MAX_SIZE (sizeof(struct mrvlietypesheader) \ + + (MRVDRV_MAX_CHANNELS_PER_SCAN \ + * sizeof(struct chanscanparamset))) + +//! Memory needed to store a max number/size SSID TLV for a firmware scan +#define SSID_TLV_MAX_SIZE (1 * sizeof(struct mrvlietypes_ssidparamset)) + +//! Maximum memory needed for a wlan_scan_cmd_config with all TLVs at max +#define MAX_SCAN_CFG_ALLOC (sizeof(struct wlan_scan_cmd_config) \ + + sizeof(struct mrvlietypes_numprobes) \ + + CHAN_TLV_MAX_SIZE \ + + SSID_TLV_MAX_SIZE) + +//! The maximum number of channels the firmware can scan per command +#define MRVDRV_MAX_CHANNELS_PER_SCAN 14 + +/** + * @brief Number of channels to scan per firmware scan command issuance. + * + * Number restricted to prevent hitting the limit on the amount of scan data + * returned in a single firmware scan command. + */ +#define MRVDRV_CHANNELS_PER_SCAN_CMD 4 + +//! Scan time specified in the channel TLV for each channel for passive scans +#define MRVDRV_PASSIVE_SCAN_CHAN_TIME 100 + +//! Scan time specified in the channel TLV for each channel for active scans +#define MRVDRV_ACTIVE_SCAN_CHAN_TIME 100 + +//! Macro to enable/disable SSID checking before storing a scan table +#ifdef DISCARD_BAD_SSID +#define CHECK_SSID_IS_VALID(x) ssid_valid(&bssidEntry.ssid) +#else +#define CHECK_SSID_IS_VALID(x) 1 +#endif + +/** + * @brief Check if a scanned network compatible with the driver settings + * + * WEP WPA WPA2 ad-hoc encrypt Network + * enabled enabled enabled AES mode privacy WPA WPA2 Compatible + * 0 0 0 0 NONE 0 0 0 yes No security + * 1 0 0 0 NONE 1 0 0 yes Static WEP + * 0 1 0 0 x 1x 1 x yes WPA + * 0 0 1 0 x 1x x 1 yes WPA2 + * 0 0 0 1 NONE 1 0 0 yes Ad-hoc AES + * 0 0 0 0 !=NONE 1 0 0 yes Dynamic WEP + * + * + * @param adapter A pointer to wlan_adapter + * @param index Index in scantable to check against current driver settings + * @param mode Network mode: Infrastructure or IBSS + * + * @return Index in scantable, or error code if negative + */ +static int is_network_compatible(wlan_adapter * adapter, int index, int mode) +{ + ENTER(); + + if (adapter->scantable[index].inframode == mode) { + if (adapter->secinfo.WEPstatus == wlan802_11WEPdisabled + && !adapter->secinfo.WPAenabled + && !adapter->secinfo.WPA2enabled + && adapter->scantable[index].wpa_supplicant.wpa_ie[0] != + WPA_IE + && adapter->scantable[index].wpa2_supplicant.wpa_ie[0] != + WPA2_IE && adapter->secinfo.Encryptionmode == CIPHER_NONE + && !adapter->scantable[index].privacy) { + /* no security */ + LEAVE(); + return index; + } else if (adapter->secinfo.WEPstatus == wlan802_11WEPenabled + && !adapter->secinfo.WPAenabled + && !adapter->secinfo.WPA2enabled + && adapter->scantable[index].privacy) { + /* static WEP enabled */ + LEAVE(); + return index; + } else if (adapter->secinfo.WEPstatus == wlan802_11WEPdisabled + && adapter->secinfo.WPAenabled + && !adapter->secinfo.WPA2enabled + && (adapter->scantable[index].wpa_supplicant. + wpa_ie[0] + == WPA_IE) + /* privacy bit may NOT be set in some APs like LinkSys WRT54G + && adapter->scantable[index].privacy */ + ) { + /* WPA enabled */ + lbs_pr_debug(1, + "is_network_compatible() WPA: index=%d wpa_ie=%#x " + "wpa2_ie=%#x WEP=%s WPA=%s WPA2=%s Encmode=%#x " + "privacy=%#x\n", index, + adapter->scantable[index].wpa_supplicant. + wpa_ie[0], + adapter->scantable[index].wpa2_supplicant. + wpa_ie[0], + (adapter->secinfo.WEPstatus == + wlan802_11WEPenabled) ? "e" : "d", + (adapter->secinfo.WPAenabled) ? "e" : "d", + (adapter->secinfo.WPA2enabled) ? "e" : "d", + adapter->secinfo.Encryptionmode, + adapter->scantable[index].privacy); + LEAVE(); + return index; + } else if (adapter->secinfo.WEPstatus == wlan802_11WEPdisabled + && !adapter->secinfo.WPAenabled + && adapter->secinfo.WPA2enabled + && (adapter->scantable[index].wpa2_supplicant. + wpa_ie[0] + == WPA2_IE) + /* privacy bit may NOT be set in some APs like LinkSys WRT54G + && adapter->scantable[index].privacy */ + ) { + /* WPA2 enabled */ + lbs_pr_debug(1, + "is_network_compatible() WPA2: index=%d wpa_ie=%#x " + "wpa2_ie=%#x WEP=%s WPA=%s WPA2=%s Encmode=%#x " + "privacy=%#x\n", index, + adapter->scantable[index].wpa_supplicant. + wpa_ie[0], + adapter->scantable[index].wpa2_supplicant. + wpa_ie[0], + (adapter->secinfo.WEPstatus == + wlan802_11WEPenabled) ? "e" : "d", + (adapter->secinfo.WPAenabled) ? "e" : "d", + (adapter->secinfo.WPA2enabled) ? "e" : "d", + adapter->secinfo.Encryptionmode, + adapter->scantable[index].privacy); + LEAVE(); + return index; + } else if (adapter->secinfo.WEPstatus == wlan802_11WEPdisabled + && !adapter->secinfo.WPAenabled + && !adapter->secinfo.WPA2enabled + && (adapter->scantable[index].wpa_supplicant. + wpa_ie[0] + != WPA_IE) + && (adapter->scantable[index].wpa2_supplicant. + wpa_ie[0] + != WPA2_IE) + && adapter->secinfo.Encryptionmode != CIPHER_NONE + && adapter->scantable[index].privacy) { + /* dynamic WEP enabled */ + lbs_pr_debug(1, + "is_network_compatible() dynamic WEP: index=%d " + "wpa_ie=%#x wpa2_ie=%#x Encmode=%#x privacy=%#x\n", + index, + adapter->scantable[index].wpa_supplicant. + wpa_ie[0], + adapter->scantable[index].wpa2_supplicant. + wpa_ie[0], adapter->secinfo.Encryptionmode, + adapter->scantable[index].privacy); + LEAVE(); + return index; + } + + /* security doesn't match */ + lbs_pr_debug(1, + "is_network_compatible() FAILED: index=%d wpa_ie=%#x " + "wpa2_ie=%#x WEP=%s WPA=%s WPA2=%s Encmode=%#x privacy=%#x\n", + index, + adapter->scantable[index].wpa_supplicant.wpa_ie[0], + adapter->scantable[index].wpa2_supplicant.wpa_ie[0], + (adapter->secinfo.WEPstatus == + wlan802_11WEPenabled) ? "e" : "d", + (adapter->secinfo.WPAenabled) ? "e" : "d", + (adapter->secinfo.WPA2enabled) ? "e" : "d", + adapter->secinfo.Encryptionmode, + adapter->scantable[index].privacy); + LEAVE(); + return -ECONNREFUSED; + } + + /* mode doesn't match */ + LEAVE(); + return -ENETUNREACH; +} + +/** + * @brief This function validates a SSID as being able to be printed + * + * @param pssid SSID structure to validate + * + * @return TRUE or FALSE + */ +static u8 ssid_valid(struct WLAN_802_11_SSID *pssid) +{ + int ssididx; + + for (ssididx = 0; ssididx < pssid->ssidlength; ssididx++) { + if (!isprint(pssid->ssid[ssididx])) { + return 0; + } + } + + return 1; +} + +/** + * @brief Post process the scan table after a new scan command has completed + * + * Inspect each entry of the scan table and try to find an entry that + * matches our current associated/joined network from the scan. If + * one is found, update the stored copy of the bssdescriptor for our + * current network. + * + * Debug dump the current scan table contents if compiled accordingly. + * + * @param priv A pointer to wlan_private structure + * + * @return void + */ +static void wlan_scan_process_results(wlan_private * priv) +{ + wlan_adapter *adapter = priv->adapter; + int foundcurrent; + int i; + + foundcurrent = 0; + + if (adapter->connect_status == libertas_connected) { + /* try to find the current BSSID in the new scan list */ + for (i = 0; i < adapter->numinscantable; i++) { + if (!libertas_SSID_cmp(&adapter->scantable[i].ssid, + &adapter->curbssparams.ssid) && + !memcmp(adapter->curbssparams.bssid, + adapter->scantable[i].macaddress, + ETH_ALEN)) { + foundcurrent = 1; + } + } + + if (foundcurrent) { + /* Make a copy of current BSSID descriptor */ + memcpy(&adapter->curbssparams.bssdescriptor, + &adapter->scantable[i], + sizeof(adapter->curbssparams.bssdescriptor)); + } + } + + for (i = 0; i < adapter->numinscantable; i++) { + lbs_pr_debug(1, "Scan:(%02d) %02x:%02x:%02x:%02x:%02x:%02x, " + "RSSI[%03d], SSID[%s]\n", + i, + adapter->scantable[i].macaddress[0], + adapter->scantable[i].macaddress[1], + adapter->scantable[i].macaddress[2], + adapter->scantable[i].macaddress[3], + adapter->scantable[i].macaddress[4], + adapter->scantable[i].macaddress[5], + (s32) adapter->scantable[i].rssi, + adapter->scantable[i].ssid.ssid); + } +} + +/** + * @brief Create a channel list for the driver to scan based on region info + * + * Use the driver region/band information to construct a comprehensive list + * of channels to scan. This routine is used for any scan that is not + * provided a specific channel list to scan. + * + * @param priv A pointer to wlan_private structure + * @param scanchanlist Output parameter: resulting channel list to scan + * @param filteredscan Flag indicating whether or not a BSSID or SSID filter + * is being sent in the command to firmware. Used to + * increase the number of channels sent in a scan + * command and to disable the firmware channel scan + * filter. + * + * @return void + */ +static void wlan_scan_create_channel_list(wlan_private * priv, + struct chanscanparamset * scanchanlist, + u8 filteredscan) +{ + + wlan_adapter *adapter = priv->adapter; + struct region_channel *scanregion; + struct chan_freq_power *cfp; + int rgnidx; + int chanidx; + int nextchan; + u8 scantype; + + chanidx = 0; + + /* Set the default scan type to the user specified type, will later + * be changed to passive on a per channel basis if restricted by + * regulatory requirements (11d or 11h) + */ + scantype = adapter->scantype; + + for (rgnidx = 0; rgnidx < ARRAY_SIZE(adapter->region_channel); rgnidx++) { + if (priv->adapter->enable11d && + adapter->connect_status != libertas_connected) { + /* Scan all the supported chan for the first scan */ + if (!adapter->universal_channel[rgnidx].valid) + continue; + scanregion = &adapter->universal_channel[rgnidx]; + + /* clear the parsed_region_chan for the first scan */ + memset(&adapter->parsed_region_chan, 0x00, + sizeof(adapter->parsed_region_chan)); + } else { + if (!adapter->region_channel[rgnidx].valid) + continue; + scanregion = &adapter->region_channel[rgnidx]; + } + + for (nextchan = 0; + nextchan < scanregion->nrcfp; nextchan++, chanidx++) { + + cfp = scanregion->CFP + nextchan; + + if (priv->adapter->enable11d) { + scantype = + libertas_get_scan_type_11d(cfp->channel, + &adapter-> + parsed_region_chan); + } + + switch (scanregion->band) { + case BAND_B: + case BAND_G: + default: + scanchanlist[chanidx].radiotype = + cmd_scan_radio_type_bg; + break; + } + + if (scantype == cmd_scan_type_passive) { + scanchanlist[chanidx].maxscantime = + cpu_to_le16 + (MRVDRV_PASSIVE_SCAN_CHAN_TIME); + scanchanlist[chanidx].chanscanmode.passivescan = + 1; + } else { + scanchanlist[chanidx].maxscantime = + cpu_to_le16 + (MRVDRV_ACTIVE_SCAN_CHAN_TIME); + scanchanlist[chanidx].chanscanmode.passivescan = + 0; + } + + scanchanlist[chanidx].channumber = cfp->channel; + + if (filteredscan) { + scanchanlist[chanidx].chanscanmode. + disablechanfilt = 1; + } + } + } +} + +/** + * @brief Construct a wlan_scan_cmd_config structure to use in issue scan cmds + * + * Application layer or other functions can invoke wlan_scan_networks + * with a scan configuration supplied in a wlan_ioctl_user_scan_cfg struct. + * This structure is used as the basis of one or many wlan_scan_cmd_config + * commands that are sent to the command processing module and sent to + * firmware. + * + * Create a wlan_scan_cmd_config based on the following user supplied + * parameters (if present): + * - SSID filter + * - BSSID filter + * - Number of Probes to be sent + * - channel list + * + * If the SSID or BSSID filter is not present, disable/clear the filter. + * If the number of probes is not set, use the adapter default setting + * Qualify the channel + * + * @param priv A pointer to wlan_private structure + * @param puserscanin NULL or pointer to scan configuration parameters + * @param ppchantlvout Output parameter: Pointer to the start of the + * channel TLV portion of the output scan config + * @param pscanchanlist Output parameter: Pointer to the resulting channel + * list to scan + * @param pmaxchanperscan Output parameter: Number of channels to scan for + * each issuance of the firmware scan command + * @param pfilteredscan Output parameter: Flag indicating whether or not + * a BSSID or SSID filter is being sent in the + * command to firmware. Used to increase the number + * of channels sent in a scan command and to + * disable the firmware channel scan filter. + * @param pscancurrentonly Output parameter: Flag indicating whether or not + * we are only scanning our current active channel + * + * @return resulting scan configuration + */ +static struct wlan_scan_cmd_config * +wlan_scan_setup_scan_config(wlan_private * priv, + const struct wlan_ioctl_user_scan_cfg * puserscanin, + struct mrvlietypes_chanlistparamset ** ppchantlvout, + struct chanscanparamset * pscanchanlist, + int *pmaxchanperscan, + u8 * pfilteredscan, + u8 * pscancurrentonly) +{ + wlan_adapter *adapter = priv->adapter; + const u8 zeromac[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 }; + struct mrvlietypes_numprobes *pnumprobestlv; + struct mrvlietypes_ssidparamset *pssidtlv; + struct wlan_scan_cmd_config * pscancfgout = NULL; + u8 *ptlvpos; + u16 numprobes; + u16 ssidlen; + int chanidx; + int scantype; + int scandur; + int channel; + int radiotype; + + pscancfgout = kzalloc(MAX_SCAN_CFG_ALLOC, GFP_KERNEL); + if (pscancfgout == NULL) + goto out; + + /* The tlvbufferlen is calculated for each scan command. The TLVs added + * in this routine will be preserved since the routine that sends + * the command will append channelTLVs at *ppchantlvout. The difference + * between the *ppchantlvout and the tlvbuffer start will be used + * to calculate the size of anything we add in this routine. + */ + pscancfgout->tlvbufferlen = 0; + + /* Running tlv pointer. Assigned to ppchantlvout at end of function + * so later routines know where channels can be added to the command buf + */ + ptlvpos = pscancfgout->tlvbuffer; + + /* + * Set the initial scan paramters for progressive scanning. If a specific + * BSSID or SSID is used, the number of channels in the scan command + * will be increased to the absolute maximum + */ + *pmaxchanperscan = MRVDRV_CHANNELS_PER_SCAN_CMD; + + /* Initialize the scan as un-filtered by firmware, set to TRUE below if + * a SSID or BSSID filter is sent in the command + */ + *pfilteredscan = 0; + + /* Initialize the scan as not being only on the current channel. If + * the channel list is customized, only contains one channel, and + * is the active channel, this is set true and data flow is not halted. + */ + *pscancurrentonly = 0; + + if (puserscanin) { + + /* Set the bss type scan filter, use adapter setting if unset */ + pscancfgout->bsstype = + (puserscanin->bsstype ? puserscanin->bsstype : adapter-> + scanmode); + + /* Set the number of probes to send, use adapter setting if unset */ + numprobes = (puserscanin->numprobes ? puserscanin->numprobes : + adapter->scanprobes); + + /* + * Set the BSSID filter to the incoming configuration, + * if non-zero. If not set, it will remain disabled (all zeros). + */ + memcpy(pscancfgout->specificBSSID, + puserscanin->specificBSSID, + sizeof(pscancfgout->specificBSSID)); + + ssidlen = strlen(puserscanin->specificSSID); + + if (ssidlen) { + pssidtlv = + (struct mrvlietypes_ssidparamset *) pscancfgout-> + tlvbuffer; + pssidtlv->header.type = cpu_to_le16(TLV_TYPE_SSID); + pssidtlv->header.len = cpu_to_le16(ssidlen); + memcpy(pssidtlv->ssid, puserscanin->specificSSID, + ssidlen); + ptlvpos += sizeof(pssidtlv->header) + ssidlen; + } + + /* + * The default number of channels sent in the command is low to + * ensure the response buffer from the firmware does not truncate + * scan results. That is not an issue with an SSID or BSSID + * filter applied to the scan results in the firmware. + */ + if (ssidlen || (memcmp(pscancfgout->specificBSSID, + &zeromac, sizeof(zeromac)) != 0)) { + *pmaxchanperscan = MRVDRV_MAX_CHANNELS_PER_SCAN; + *pfilteredscan = 1; + } + } else { + pscancfgout->bsstype = adapter->scanmode; + numprobes = adapter->scanprobes; + } + + /* If the input config or adapter has the number of Probes set, add tlv */ + if (numprobes) { + pnumprobestlv = (struct mrvlietypes_numprobes *) ptlvpos; + pnumprobestlv->header.type = + cpu_to_le16(TLV_TYPE_NUMPROBES); + pnumprobestlv->header.len = sizeof(pnumprobestlv->numprobes); + pnumprobestlv->numprobes = cpu_to_le16(numprobes); + + ptlvpos += + sizeof(pnumprobestlv->header) + pnumprobestlv->header.len; + + pnumprobestlv->header.len = + cpu_to_le16(pnumprobestlv->header.len); + } + + /* + * Set the output for the channel TLV to the address in the tlv buffer + * past any TLVs that were added in this fuction (SSID, numprobes). + * channel TLVs will be added past this for each scan command, preserving + * the TLVs that were previously added. + */ + *ppchantlvout = (struct mrvlietypes_chanlistparamset *) ptlvpos; + + if (puserscanin && puserscanin->chanlist[0].channumber) { + + lbs_pr_debug(1, "Scan: Using supplied channel list\n"); + + for (chanidx = 0; + chanidx < WLAN_IOCTL_USER_SCAN_CHAN_MAX + && puserscanin->chanlist[chanidx].channumber; chanidx++) { + + channel = puserscanin->chanlist[chanidx].channumber; + (pscanchanlist + chanidx)->channumber = channel; + + radiotype = puserscanin->chanlist[chanidx].radiotype; + (pscanchanlist + chanidx)->radiotype = radiotype; + + scantype = puserscanin->chanlist[chanidx].scantype; + + if (scantype == cmd_scan_type_passive) { + (pscanchanlist + + chanidx)->chanscanmode.passivescan = 1; + } else { + (pscanchanlist + + chanidx)->chanscanmode.passivescan = 0; + } + + if (puserscanin->chanlist[chanidx].scantime) { + scandur = + puserscanin->chanlist[chanidx].scantime; + } else { + if (scantype == cmd_scan_type_passive) { + scandur = MRVDRV_PASSIVE_SCAN_CHAN_TIME; + } else { + scandur = MRVDRV_ACTIVE_SCAN_CHAN_TIME; + } + } + + (pscanchanlist + chanidx)->minscantime = + cpu_to_le16(scandur); + (pscanchanlist + chanidx)->maxscantime = + cpu_to_le16(scandur); + } + + /* Check if we are only scanning the current channel */ + if ((chanidx == 1) && (puserscanin->chanlist[0].channumber + == + priv->adapter->curbssparams.channel)) { + *pscancurrentonly = 1; + lbs_pr_debug(1, "Scan: Scanning current channel only"); + } + + } else { + lbs_pr_debug(1, "Scan: Creating full region channel list\n"); + wlan_scan_create_channel_list(priv, pscanchanlist, + *pfilteredscan); + } + +out: + return pscancfgout; +} + +/** + * @brief Construct and send multiple scan config commands to the firmware + * + * Previous routines have created a wlan_scan_cmd_config with any requested + * TLVs. This function splits the channel TLV into maxchanperscan lists + * and sends the portion of the channel TLV along with the other TLVs + * to the wlan_cmd routines for execution in the firmware. + * + * @param priv A pointer to wlan_private structure + * @param maxchanperscan Maximum number channels to be included in each + * scan command sent to firmware + * @param filteredscan Flag indicating whether or not a BSSID or SSID + * filter is being used for the firmware command + * scan command sent to firmware + * @param pscancfgout Scan configuration used for this scan. + * @param pchantlvout Pointer in the pscancfgout where the channel TLV + * should start. This is past any other TLVs that + * must be sent down in each firmware command. + * @param pscanchanlist List of channels to scan in maxchanperscan segments + * + * @return 0 or error return otherwise + */ +static int wlan_scan_channel_list(wlan_private * priv, + int maxchanperscan, + u8 filteredscan, + struct wlan_scan_cmd_config * pscancfgout, + struct mrvlietypes_chanlistparamset * pchantlvout, + struct chanscanparamset * pscanchanlist) +{ + struct chanscanparamset *ptmpchan; + struct chanscanparamset *pstartchan; + u8 scanband; + int doneearly; + int tlvidx; + int ret = 0; + + ENTER(); + + if (pscancfgout == 0 || pchantlvout == 0 || pscanchanlist == 0) { + lbs_pr_debug(1, "Scan: Null detect: %p, %p, %p\n", + pscancfgout, pchantlvout, pscanchanlist); + return -1; + } + + pchantlvout->header.type = cpu_to_le16(TLV_TYPE_CHANLIST); + + /* Set the temp channel struct pointer to the start of the desired list */ + ptmpchan = pscanchanlist; + + /* Loop through the desired channel list, sending a new firmware scan + * commands for each maxchanperscan channels (or for 1,6,11 individually + * if configured accordingly) + */ + while (ptmpchan->channumber) { + + tlvidx = 0; + pchantlvout->header.len = 0; + scanband = ptmpchan->radiotype; + pstartchan = ptmpchan; + doneearly = 0; + + /* Construct the channel TLV for the scan command. Continue to + * insert channel TLVs until: + * - the tlvidx hits the maximum configured per scan command + * - the next channel to insert is 0 (end of desired channel list) + * - doneearly is set (controlling individual scanning of 1,6,11) + */ + while (tlvidx < maxchanperscan && ptmpchan->channumber + && !doneearly) { + + lbs_pr_debug(1, + "Scan: Chan(%3d), Radio(%d), mode(%d,%d), Dur(%d)\n", + ptmpchan->channumber, ptmpchan->radiotype, + ptmpchan->chanscanmode.passivescan, + ptmpchan->chanscanmode.disablechanfilt, + ptmpchan->maxscantime); + + /* Copy the current channel TLV to the command being prepared */ + memcpy(pchantlvout->chanscanparam + tlvidx, + ptmpchan, sizeof(pchantlvout->chanscanparam)); + + /* Increment the TLV header length by the size appended */ + pchantlvout->header.len += + sizeof(pchantlvout->chanscanparam); + + /* + * The tlv buffer length is set to the number of bytes of the + * between the channel tlv pointer and the start of the + * tlv buffer. This compensates for any TLVs that were appended + * before the channel list. + */ + pscancfgout->tlvbufferlen = ((u8 *) pchantlvout + - pscancfgout->tlvbuffer); + + /* Add the size of the channel tlv header and the data length */ + pscancfgout->tlvbufferlen += + (sizeof(pchantlvout->header) + + pchantlvout->header.len); + + /* Increment the index to the channel tlv we are constructing */ + tlvidx++; + + doneearly = 0; + + /* Stop the loop if the *current* channel is in the 1,6,11 set + * and we are not filtering on a BSSID or SSID. + */ + if (!filteredscan && (ptmpchan->channumber == 1 + || ptmpchan->channumber == 6 + || ptmpchan->channumber == 11)) { + doneearly = 1; + } + + /* Increment the tmp pointer to the next channel to be scanned */ + ptmpchan++; + + /* Stop the loop if the *next* channel is in the 1,6,11 set. + * This will cause it to be the only channel scanned on the next + * interation + */ + if (!filteredscan && (ptmpchan->channumber == 1 + || ptmpchan->channumber == 6 + || ptmpchan->channumber == 11)) { + doneearly = 1; + } + } + + /* Send the scan command to the firmware with the specified cfg */ + ret = libertas_prepare_and_send_command(priv, cmd_802_11_scan, 0, + 0, 0, pscancfgout); + } + + LEAVE(); + return ret; +} + +/** + * @brief Internal function used to start a scan based on an input config + * + * Use the input user scan configuration information when provided in + * order to send the appropriate scan commands to firmware to populate or + * update the internal driver scan table + * + * @param priv A pointer to wlan_private structure + * @param puserscanin Pointer to the input configuration for the requested + * scan. + * + * @return 0 or < 0 if error + */ +int wlan_scan_networks(wlan_private * priv, + const struct wlan_ioctl_user_scan_cfg * puserscanin) +{ + wlan_adapter *adapter = priv->adapter; + struct mrvlietypes_chanlistparamset *pchantlvout; + struct chanscanparamset * scan_chan_list = NULL; + struct wlan_scan_cmd_config * scan_cfg = NULL; + u8 keeppreviousscan; + u8 filteredscan; + u8 scancurrentchanonly; + int maxchanperscan; + int ret; + + ENTER(); + + scan_chan_list = kzalloc(sizeof(struct chanscanparamset) * + WLAN_IOCTL_USER_SCAN_CHAN_MAX, GFP_KERNEL); + if (scan_chan_list == NULL) { + ret = -ENOMEM; + goto out; + } + + scan_cfg = wlan_scan_setup_scan_config(priv, + puserscanin, + &pchantlvout, + scan_chan_list, + &maxchanperscan, + &filteredscan, + &scancurrentchanonly); + if (scan_cfg == NULL) { + ret = -ENOMEM; + goto out; + } + + keeppreviousscan = 0; + + if (puserscanin) { + keeppreviousscan = puserscanin->keeppreviousscan; + } + + if (!keeppreviousscan) { + memset(adapter->scantable, 0x00, + sizeof(struct bss_descriptor) * MRVDRV_MAX_BSSID_LIST); + adapter->numinscantable = 0; + } + + /* Keep the data path active if we are only scanning our current channel */ + if (!scancurrentchanonly) { + netif_stop_queue(priv->wlan_dev.netdev); + netif_carrier_off(priv->wlan_dev.netdev); + } + + ret = wlan_scan_channel_list(priv, + maxchanperscan, + filteredscan, + scan_cfg, + pchantlvout, + scan_chan_list); + + /* Process the resulting scan table: + * - Remove any bad ssids + * - Update our current BSS information from scan data + */ + wlan_scan_process_results(priv); + + if (priv->adapter->connect_status == libertas_connected) { + netif_carrier_on(priv->wlan_dev.netdev); + netif_wake_queue(priv->wlan_dev.netdev); + } + +out: + if (scan_cfg) + kfree(scan_cfg); + + if (scan_chan_list) + kfree(scan_chan_list); + + LEAVE(); + return ret; +} + +/** + * @brief Inspect the scan response buffer for pointers to expected TLVs + * + * TLVs can be included at the end of the scan response BSS information. + * Parse the data in the buffer for pointers to TLVs that can potentially + * be passed back in the response + * + * @param ptlv Pointer to the start of the TLV buffer to parse + * @param tlvbufsize size of the TLV buffer + * @param ptsftlv Output parameter: Pointer to the TSF TLV if found + * + * @return void + */ +static +void wlan_ret_802_11_scan_get_tlv_ptrs(struct mrvlietypes_data * ptlv, + int tlvbufsize, + struct mrvlietypes_tsftimestamp ** ptsftlv) +{ + struct mrvlietypes_data *pcurrenttlv; + int tlvbufleft; + u16 tlvtype; + u16 tlvlen; + + pcurrenttlv = ptlv; + tlvbufleft = tlvbufsize; + *ptsftlv = NULL; + + lbs_pr_debug(1, "SCAN_RESP: tlvbufsize = %d\n", tlvbufsize); + lbs_dbg_hex("SCAN_RESP: TLV Buf", (u8 *) ptlv, tlvbufsize); + + while (tlvbufleft >= sizeof(struct mrvlietypesheader)) { + tlvtype = le16_to_cpu(pcurrenttlv->header.type); + tlvlen = le16_to_cpu(pcurrenttlv->header.len); + + switch (tlvtype) { + case TLV_TYPE_TSFTIMESTAMP: + *ptsftlv = (struct mrvlietypes_tsftimestamp *) pcurrenttlv; + break; + + default: + lbs_pr_debug(1, "SCAN_RESP: Unhandled TLV = %d\n", + tlvtype); + /* Give up, this seems corrupted */ + return; + } /* switch */ + + tlvbufleft -= (sizeof(ptlv->header) + tlvlen); + pcurrenttlv = + (struct mrvlietypes_data *) (pcurrenttlv->Data + tlvlen); + } /* while */ +} + +/** + * @brief Interpret a BSS scan response returned from the firmware + * + * Parse the various fixed fields and IEs passed back for a a BSS probe + * response or beacon from the scan command. Record information as needed + * in the scan table struct bss_descriptor for that entry. + * + * @param pBSSIDEntry Output parameter: Pointer to the BSS Entry + * + * @return 0 or -1 + */ +static int InterpretBSSDescriptionWithIE(struct bss_descriptor * pBSSEntry, + u8 ** pbeaconinfo, int *bytesleft) +{ + enum ieeetypes_elementid elemID; + struct ieeetypes_fhparamset *pFH; + struct ieeetypes_dsparamset *pDS; + struct ieeetypes_cfparamset *pCF; + struct ieeetypes_ibssparamset *pibss; + struct ieeetypes_capinfo *pcap; + struct WLAN_802_11_FIXED_IEs fixedie; + u8 *pcurrentptr; + u8 *pRate; + u8 elemlen; + u8 bytestocopy; + u8 ratesize; + u16 beaconsize; + u8 founddatarateie; + int bytesleftforcurrentbeacon; + + struct WPA_SUPPLICANT *pwpa_supplicant; + struct WPA_SUPPLICANT *pwpa2_supplicant; + struct IE_WPA *pIe; + const u8 oui01[4] = { 0x00, 0x50, 0xf2, 0x01 }; + + struct ieeetypes_countryinfoset *pcountryinfo; + + ENTER(); + + founddatarateie = 0; + ratesize = 0; + beaconsize = 0; + + if (*bytesleft >= sizeof(beaconsize)) { + /* Extract & convert beacon size from the command buffer */ + memcpy(&beaconsize, *pbeaconinfo, sizeof(beaconsize)); + beaconsize = le16_to_cpu(beaconsize); + *bytesleft -= sizeof(beaconsize); + *pbeaconinfo += sizeof(beaconsize); + } + + if (beaconsize == 0 || beaconsize > *bytesleft) { + + *pbeaconinfo += *bytesleft; + *bytesleft = 0; + + return -1; + } + + /* Initialize the current working beacon pointer for this BSS iteration */ + pcurrentptr = *pbeaconinfo; + + /* Advance the return beacon pointer past the current beacon */ + *pbeaconinfo += beaconsize; + *bytesleft -= beaconsize; + + bytesleftforcurrentbeacon = beaconsize; + + pwpa_supplicant = &pBSSEntry->wpa_supplicant; + pwpa2_supplicant = &pBSSEntry->wpa2_supplicant; + + memcpy(pBSSEntry->macaddress, pcurrentptr, ETH_ALEN); + lbs_pr_debug(1, "InterpretIE: AP MAC Addr-%x:%x:%x:%x:%x:%x\n", + pBSSEntry->macaddress[0], pBSSEntry->macaddress[1], + pBSSEntry->macaddress[2], pBSSEntry->macaddress[3], + pBSSEntry->macaddress[4], pBSSEntry->macaddress[5]); + + pcurrentptr += ETH_ALEN; + bytesleftforcurrentbeacon -= ETH_ALEN; + + if (bytesleftforcurrentbeacon < 12) { + lbs_pr_debug(1, "InterpretIE: Not enough bytes left\n"); + return -1; + } + + /* + * next 4 fields are RSSI, time stamp, beacon interval, + * and capability information + */ + + /* RSSI is 1 byte long */ + pBSSEntry->rssi = le32_to_cpu((long)(*pcurrentptr)); + lbs_pr_debug(1, "InterpretIE: RSSI=%02X\n", *pcurrentptr); + pcurrentptr += 1; + bytesleftforcurrentbeacon -= 1; + + /* time stamp is 8 bytes long */ + memcpy(fixedie.timestamp, pcurrentptr, 8); + memcpy(pBSSEntry->timestamp, pcurrentptr, 8); + pcurrentptr += 8; + bytesleftforcurrentbeacon -= 8; + + /* beacon interval is 2 bytes long */ + memcpy(&fixedie.beaconinterval, pcurrentptr, 2); + pBSSEntry->beaconperiod = le16_to_cpu(fixedie.beaconinterval); + pcurrentptr += 2; + bytesleftforcurrentbeacon -= 2; + + /* capability information is 2 bytes long */ + memcpy(&fixedie.capabilities, pcurrentptr, 2); + lbs_pr_debug(1, "InterpretIE: fixedie.capabilities=0x%X\n", + fixedie.capabilities); + fixedie.capabilities = le16_to_cpu(fixedie.capabilities); + pcap = (struct ieeetypes_capinfo *) & fixedie.capabilities; + memcpy(&pBSSEntry->cap, pcap, sizeof(struct ieeetypes_capinfo)); + pcurrentptr += 2; + bytesleftforcurrentbeacon -= 2; + + /* rest of the current buffer are IE's */ + lbs_pr_debug(1, "InterpretIE: IElength for this AP = %d\n", + bytesleftforcurrentbeacon); + + lbs_dbg_hex("InterpretIE: IE info", (u8 *) pcurrentptr, + bytesleftforcurrentbeacon); + + if (pcap->privacy) { + lbs_pr_debug(1, "InterpretIE: AP WEP enabled\n"); + pBSSEntry->privacy = wlan802_11privfilter8021xWEP; + } else { + pBSSEntry->privacy = wlan802_11privfilteracceptall; + } + + if (pcap->ibss == 1) { + pBSSEntry->inframode = wlan802_11ibss; + } else { + pBSSEntry->inframode = wlan802_11infrastructure; + } + + /* process variable IE */ + while (bytesleftforcurrentbeacon >= 2) { + elemID = (enum ieeetypes_elementid) (*((u8 *) pcurrentptr)); + elemlen = *((u8 *) pcurrentptr + 1); + + if (bytesleftforcurrentbeacon < elemlen) { + lbs_pr_debug(1, "InterpretIE: error in processing IE, " + "bytes left < IE length\n"); + bytesleftforcurrentbeacon = 0; + continue; + } + + switch (elemID) { + + case SSID: + pBSSEntry->ssid.ssidlength = elemlen; + memcpy(pBSSEntry->ssid.ssid, (pcurrentptr + 2), + elemlen); + lbs_pr_debug(1, "ssid: %32s", pBSSEntry->ssid.ssid); + break; + + case SUPPORTED_RATES: + memcpy(pBSSEntry->datarates, (pcurrentptr + 2), + elemlen); + memmove(pBSSEntry->libertas_supported_rates, (pcurrentptr + 2), + elemlen); + ratesize = elemlen; + founddatarateie = 1; + break; + + case EXTRA_IE: + lbs_pr_debug(1, "InterpretIE: EXTRA_IE Found!\n"); + pBSSEntry->extra_ie = 1; + break; + + case FH_PARAM_SET: + pFH = (struct ieeetypes_fhparamset *) pcurrentptr; + memmove(&pBSSEntry->phyparamset.fhparamset, pFH, + sizeof(struct ieeetypes_fhparamset)); + pBSSEntry->phyparamset.fhparamset.dwelltime + = + le16_to_cpu(pBSSEntry->phyparamset.fhparamset. + dwelltime); + break; + + case DS_PARAM_SET: + pDS = (struct ieeetypes_dsparamset *) pcurrentptr; + + pBSSEntry->channel = pDS->currentchan; + + memcpy(&pBSSEntry->phyparamset.dsparamset, pDS, + sizeof(struct ieeetypes_dsparamset)); + break; + + case CF_PARAM_SET: + pCF = (struct ieeetypes_cfparamset *) pcurrentptr; + + memcpy(&pBSSEntry->ssparamset.cfparamset, pCF, + sizeof(struct ieeetypes_cfparamset)); + break; + + case IBSS_PARAM_SET: + pibss = (struct ieeetypes_ibssparamset *) pcurrentptr; + pBSSEntry->atimwindow = + le32_to_cpu(pibss->atimwindow); + + memmove(&pBSSEntry->ssparamset.ibssparamset, pibss, + sizeof(struct ieeetypes_ibssparamset)); + + pBSSEntry->ssparamset.ibssparamset.atimwindow + = + le16_to_cpu(pBSSEntry->ssparamset.ibssparamset. + atimwindow); + break; + + /* Handle Country Info IE */ + case COUNTRY_INFO: + pcountryinfo = + (struct ieeetypes_countryinfoset *) pcurrentptr; + + if (pcountryinfo->len < + sizeof(pcountryinfo->countrycode) + || pcountryinfo->len > 254) { + lbs_pr_debug(1, "InterpretIE: 11D- Err " + "CountryInfo len =%d min=%d max=254\n", + pcountryinfo->len, + sizeof(pcountryinfo->countrycode)); + LEAVE(); + return -1; + } + + memcpy(&pBSSEntry->countryinfo, + pcountryinfo, pcountryinfo->len + 2); + lbs_dbg_hex("InterpretIE: 11D- CountryInfo:", + (u8 *) pcountryinfo, + (u32) (pcountryinfo->len + 2)); + break; + + case EXTENDED_SUPPORTED_RATES: + /* + * only process extended supported rate + * if data rate is already found. + * data rate IE should come before + * extended supported rate IE + */ + if (founddatarateie) { + if ((elemlen + ratesize) > WLAN_SUPPORTED_RATES) { + bytestocopy = + (WLAN_SUPPORTED_RATES - ratesize); + } else { + bytestocopy = elemlen; + } + + pRate = (u8 *) pBSSEntry->datarates; + pRate += ratesize; + memmove(pRate, (pcurrentptr + 2), bytestocopy); + + pRate = (u8 *) pBSSEntry->libertas_supported_rates; + + pRate += ratesize; + memmove(pRate, (pcurrentptr + 2), bytestocopy); + } + break; + + case VENDOR_SPECIFIC_221: +#define IE_ID_LEN_FIELDS_BYTES 2 + pIe = (struct IE_WPA *)pcurrentptr; + + if (!memcmp(pIe->oui, oui01, sizeof(oui01))) { + pwpa_supplicant->wpa_ie_len + = min_t(size_t, elemlen + IE_ID_LEN_FIELDS_BYTES, + sizeof(pwpa_supplicant->wpa_ie)); + memcpy(pwpa_supplicant->wpa_ie, + pcurrentptr, + pwpa_supplicant->wpa_ie_len); + lbs_dbg_hex("InterpretIE: Resp WPA_IE", + pwpa_supplicant->wpa_ie, elemlen); + } + break; + case WPA2_IE: + pIe = (struct IE_WPA *)pcurrentptr; + pwpa2_supplicant->wpa_ie_len + = min_t(size_t, elemlen + IE_ID_LEN_FIELDS_BYTES, + sizeof(pwpa2_supplicant->wpa_ie)); + memcpy(pwpa2_supplicant->wpa_ie, + pcurrentptr, pwpa2_supplicant->wpa_ie_len); + + lbs_dbg_hex("InterpretIE: Resp WPA2_IE", + pwpa2_supplicant->wpa_ie, elemlen); + break; + case TIM: + break; + + case CHALLENGE_TEXT: + break; + } + + pcurrentptr += elemlen + 2; + + /* need to account for IE ID and IE len */ + bytesleftforcurrentbeacon -= (elemlen + 2); + + } /* while (bytesleftforcurrentbeacon > 2) */ + + return 0; +} + +/** + * @brief Compare two SSIDs + * + * @param ssid1 A pointer to ssid to compare + * @param ssid2 A pointer to ssid to compare + * + * @return 0--ssid is same, otherwise is different + */ +int libertas_SSID_cmp(struct WLAN_802_11_SSID *ssid1, struct WLAN_802_11_SSID *ssid2) +{ + if (!ssid1 || !ssid2) + return -1; + + if (ssid1->ssidlength != ssid2->ssidlength) + return -1; + + return memcmp(ssid1->ssid, ssid2->ssid, ssid1->ssidlength); +} + +/** + * @brief This function finds a specific compatible BSSID in the scan list + * + * @param adapter A pointer to wlan_adapter + * @param bssid BSSID to find in the scan list + * @param mode Network mode: Infrastructure or IBSS + * + * @return index in BSSID list, or error return code (< 0) + */ +int libertas_find_BSSID_in_list(wlan_adapter * adapter, u8 * bssid, int mode) +{ + int ret = -ENETUNREACH; + int i; + + if (!bssid) + return -EFAULT; + + lbs_pr_debug(1, "FindBSSID: Num of BSSIDs = %d\n", + adapter->numinscantable); + + /* Look through the scan table for a compatible match. The ret return + * variable will be equal to the index in the scan table (greater + * than zero) if the network is compatible. The loop will continue + * past a matched bssid that is not compatible in case there is an + * AP with multiple SSIDs assigned to the same BSSID + */ + for (i = 0; ret < 0 && i < adapter->numinscantable; i++) { + if (!memcmp(adapter->scantable[i].macaddress, bssid, ETH_ALEN)) { + switch (mode) { + case wlan802_11infrastructure: + case wlan802_11ibss: + ret = is_network_compatible(adapter, i, mode); + break; + default: + ret = i; + break; + } + } + } + + return ret; +} + +/** + * @brief This function finds ssid in ssid list. + * + * @param adapter A pointer to wlan_adapter + * @param ssid SSID to find in the list + * @param bssid BSSID to qualify the SSID selection (if provided) + * @param mode Network mode: Infrastructure or IBSS + * + * @return index in BSSID list + */ +int libertas_find_SSID_in_list(wlan_adapter * adapter, + struct WLAN_802_11_SSID *ssid, u8 * bssid, int mode) +{ + int net = -ENETUNREACH; + u8 bestrssi = 0; + int i; + int j; + + lbs_pr_debug(1, "Num of Entries in Table = %d\n", adapter->numinscantable); + + for (i = 0; i < adapter->numinscantable; i++) { + if (!libertas_SSID_cmp(&adapter->scantable[i].ssid, ssid) && + (!bssid || + !memcmp(adapter->scantable[i]. + macaddress, bssid, ETH_ALEN))) { + switch (mode) { + case wlan802_11infrastructure: + case wlan802_11ibss: + j = is_network_compatible(adapter, i, mode); + + if (j >= 0) { + if (bssid) { + return i; + } + + if (SCAN_RSSI + (adapter->scantable[i].rssi) + > bestrssi) { + bestrssi = + SCAN_RSSI(adapter-> + scantable[i]. + rssi); + net = i; + } + } else { + if (net == -ENETUNREACH) { + net = j; + } + } + break; + case wlan802_11autounknown: + default: + if (SCAN_RSSI(adapter->scantable[i].rssi) + > bestrssi) { + bestrssi = + SCAN_RSSI(adapter->scantable[i]. + rssi); + net = i; + } + break; + } + } + } + + return net; +} + +/** + * @brief This function finds the best SSID in the Scan List + * + * Search the scan table for the best SSID that also matches the current + * adapter network preference (infrastructure or adhoc) + * + * @param adapter A pointer to wlan_adapter + * + * @return index in BSSID list + */ +int libertas_find_best_SSID_in_list(wlan_adapter * adapter, + enum WLAN_802_11_NETWORK_INFRASTRUCTURE mode) +{ + int bestnet = -ENETUNREACH; + u8 bestrssi = 0; + int i; + + ENTER(); + + lbs_pr_debug(1, "Num of BSSIDs = %d\n", adapter->numinscantable); + + for (i = 0; i < adapter->numinscantable; i++) { + switch (mode) { + case wlan802_11infrastructure: + case wlan802_11ibss: + if (is_network_compatible(adapter, i, mode) >= 0) { + if (SCAN_RSSI(adapter->scantable[i].rssi) > + bestrssi) { + bestrssi = + SCAN_RSSI(adapter->scantable[i]. + rssi); + bestnet = i; + } + } + break; + case wlan802_11autounknown: + default: + if (SCAN_RSSI(adapter->scantable[i].rssi) > bestrssi) { + bestrssi = + SCAN_RSSI(adapter->scantable[i].rssi); + bestnet = i; + } + break; + } + } + + LEAVE(); + return bestnet; +} + +/** + * @brief Find the AP with specific ssid in the scan list + * + * @param priv A pointer to wlan_private structure + * @param pSSID A pointer to AP's ssid + * + * @return 0--success, otherwise--fail + */ +int libertas_find_best_network_SSID(wlan_private * priv, + struct WLAN_802_11_SSID *pSSID, + enum WLAN_802_11_NETWORK_INFRASTRUCTURE preferred_mode, + enum WLAN_802_11_NETWORK_INFRASTRUCTURE *out_mode) +{ + wlan_adapter *adapter = priv->adapter; + int ret = 0; + struct bss_descriptor *preqbssid; + int i; + + ENTER(); + + memset(pSSID, 0, sizeof(struct WLAN_802_11_SSID)); + + wlan_scan_networks(priv, NULL); + if (adapter->surpriseremoved) + return -1; + wait_event_interruptible(adapter->cmd_pending, !adapter->nr_cmd_pending); + + i = libertas_find_best_SSID_in_list(adapter, preferred_mode); + if (i < 0) { + ret = -1; + goto out; + } + + preqbssid = &adapter->scantable[i]; + memcpy(pSSID, &preqbssid->ssid, + sizeof(struct WLAN_802_11_SSID)); + *out_mode = preqbssid->inframode; + + if (!pSSID->ssidlength) { + ret = -1; + } + +out: + LEAVE(); + return ret; +} + +/** + * @brief Scan Network + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param vwrq A pointer to iw_param structure + * @param extra A pointer to extra data buf + * + * @return 0 --success, otherwise fail + */ +int libertas_set_scan(struct net_device *dev, struct iw_request_info *info, + struct iw_param *vwrq, char *extra) +{ + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + union iwreq_data wrqu; + + ENTER(); + + if (!wlan_scan_networks(priv, NULL)) { + memset(&wrqu, 0, sizeof(union iwreq_data)); + wireless_send_event(priv->wlan_dev.netdev, SIOCGIWSCAN, &wrqu, + NULL); + } + + if (adapter->surpriseremoved) + return -1; + + LEAVE(); + return 0; +} + +/** + * @brief Send a scan command for all available channels filtered on a spec + * + * @param priv A pointer to wlan_private structure + * @param prequestedssid A pointer to AP's ssid + * @param keeppreviousscan Flag used to save/clear scan table before scan + * + * @return 0-success, otherwise fail + */ +int libertas_send_specific_SSID_scan(wlan_private * priv, + struct WLAN_802_11_SSID *prequestedssid, + u8 keeppreviousscan) +{ + wlan_adapter *adapter = priv->adapter; + struct wlan_ioctl_user_scan_cfg scancfg; + + ENTER(); + + if (prequestedssid == NULL) { + return -1; + } + + memset(&scancfg, 0x00, sizeof(scancfg)); + + memcpy(scancfg.specificSSID, prequestedssid->ssid, + prequestedssid->ssidlength); + scancfg.keeppreviousscan = keeppreviousscan; + + wlan_scan_networks(priv, &scancfg); + if (adapter->surpriseremoved) + return -1; + wait_event_interruptible(adapter->cmd_pending, !adapter->nr_cmd_pending); + + LEAVE(); + return 0; +} + +/** + * @brief scan an AP with specific BSSID + * + * @param priv A pointer to wlan_private structure + * @param bssid A pointer to AP's bssid + * @param keeppreviousscan Flag used to save/clear scan table before scan + * + * @return 0-success, otherwise fail + */ +int libertas_send_specific_BSSID_scan(wlan_private * priv, u8 * bssid, u8 keeppreviousscan) +{ + struct wlan_ioctl_user_scan_cfg scancfg; + + ENTER(); + + if (bssid == NULL) { + return -1; + } + + memset(&scancfg, 0x00, sizeof(scancfg)); + memcpy(scancfg.specificBSSID, bssid, sizeof(scancfg.specificBSSID)); + scancfg.keeppreviousscan = keeppreviousscan; + + wlan_scan_networks(priv, &scancfg); + if (priv->adapter->surpriseremoved) + return -1; + wait_event_interruptible(priv->adapter->cmd_pending, + !priv->adapter->nr_cmd_pending); + + LEAVE(); + return 0; +} + +/** + * @brief Retrieve the scan table entries via wireless tools IOCTL call + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param dwrq A pointer to iw_point structure + * @param extra A pointer to extra data buf + * + * @return 0 --success, otherwise fail + */ +int libertas_get_scan(struct net_device *dev, struct iw_request_info *info, + struct iw_point *dwrq, char *extra) +{ + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + int ret = 0; + char *current_ev = extra; + char *end_buf = extra + IW_SCAN_MAX_DATA; + struct chan_freq_power *cfp; + struct bss_descriptor *pscantable; + char *current_val; /* For rates */ + struct iw_event iwe; /* Temporary buffer */ + int i; + int j; + int rate; +#define PERFECT_RSSI ((u8)50) +#define WORST_RSSI ((u8)0) +#define RSSI_DIFF ((u8)(PERFECT_RSSI - WORST_RSSI)) + u8 rssi; + + u8 buf[16 + 256 * 2]; + u8 *ptr; + + ENTER(); + + /* + * if there's either commands in the queue or one being + * processed return -EAGAIN for iwlist to retry later. + */ + if (adapter->nr_cmd_pending) + return -EAGAIN; + + if (adapter->connect_status == libertas_connected) + lbs_pr_debug(1, "Current ssid: %32s\n", + adapter->curbssparams.ssid.ssid); + + lbs_pr_debug(1, "Scan: Get: numinscantable = %d\n", + adapter->numinscantable); + + /* The old API using SIOCGIWAPLIST had a hard limit of IW_MAX_AP. + * The new API using SIOCGIWSCAN is only limited by buffer size + * WE-14 -> WE-16 the buffer is limited to IW_SCAN_MAX_DATA bytes + * which is 4096. + */ + for (i = 0; i < adapter->numinscantable; i++) { + if ((current_ev + MAX_SCAN_CELL_SIZE) >= end_buf) { + lbs_pr_debug(1, "i=%d break out: current_ev=%p end_buf=%p " + "MAX_SCAN_CELL_SIZE=%d\n", + i, current_ev, end_buf, MAX_SCAN_CELL_SIZE); + break; + } + + pscantable = &adapter->scantable[i]; + + lbs_pr_debug(1, "i=%d ssid: %32s\n", i, pscantable->ssid.ssid); + + cfp = + libertas_find_cfp_by_band_and_channel(adapter, 0, + pscantable->channel); + if (!cfp) { + lbs_pr_debug(1, "Invalid channel number %d\n", + pscantable->channel); + continue; + } + + if (!ssid_valid(&adapter->scantable[i].ssid)) { + continue; + } + + /* First entry *MUST* be the AP MAC address */ + iwe.cmd = SIOCGIWAP; + iwe.u.ap_addr.sa_family = ARPHRD_ETHER; + memcpy(iwe.u.ap_addr.sa_data, + &adapter->scantable[i].macaddress, ETH_ALEN); + + iwe.len = IW_EV_ADDR_LEN; + current_ev = + iwe_stream_add_event(current_ev, end_buf, &iwe, iwe.len); + + //Add the ESSID + iwe.u.data.length = adapter->scantable[i].ssid.ssidlength; + + if (iwe.u.data.length > 32) { + iwe.u.data.length = 32; + } + + iwe.cmd = SIOCGIWESSID; + iwe.u.data.flags = 1; + iwe.len = IW_EV_POINT_LEN + iwe.u.data.length; + current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, + adapter->scantable[i].ssid. + ssid); + + //Add mode + iwe.cmd = SIOCGIWMODE; + iwe.u.mode = adapter->scantable[i].inframode + 1; + iwe.len = IW_EV_UINT_LEN; + current_ev = + iwe_stream_add_event(current_ev, end_buf, &iwe, iwe.len); + + //frequency + iwe.cmd = SIOCGIWFREQ; + iwe.u.freq.m = (long)cfp->freq * 100000; + iwe.u.freq.e = 1; + iwe.len = IW_EV_FREQ_LEN; + current_ev = + iwe_stream_add_event(current_ev, end_buf, &iwe, iwe.len); + + /* Add quality statistics */ + iwe.cmd = IWEVQUAL; + iwe.u.qual.updated = IW_QUAL_ALL_UPDATED; + iwe.u.qual.level = SCAN_RSSI(adapter->scantable[i].rssi); + + rssi = iwe.u.qual.level - MRVDRV_NF_DEFAULT_SCAN_VALUE; + iwe.u.qual.qual = + (100 * RSSI_DIFF * RSSI_DIFF - (PERFECT_RSSI - rssi) * + (15 * (RSSI_DIFF) + 62 * (PERFECT_RSSI - rssi))) / + (RSSI_DIFF * RSSI_DIFF); + if (iwe.u.qual.qual > 100) + iwe.u.qual.qual = 100; + else if (iwe.u.qual.qual < 1) + iwe.u.qual.qual = 0; + + if (adapter->NF[TYPE_BEACON][TYPE_NOAVG] == 0) { + iwe.u.qual.noise = MRVDRV_NF_DEFAULT_SCAN_VALUE; + } else { + iwe.u.qual.noise = + CAL_NF(adapter->NF[TYPE_BEACON][TYPE_NOAVG]); + } + if ((adapter->inframode == wlan802_11ibss) && + !libertas_SSID_cmp(&adapter->curbssparams.ssid, + &adapter->scantable[i].ssid) + && adapter->adhoccreate) { + ret = libertas_prepare_and_send_command(priv, + cmd_802_11_rssi, + 0, + cmd_option_waitforrsp, + 0, NULL); + + if (!ret) { + iwe.u.qual.level = + CAL_RSSI(adapter->SNR[TYPE_RXPD][TYPE_AVG] / + AVG_SCALE, + adapter->NF[TYPE_RXPD][TYPE_AVG] / + AVG_SCALE); + } + } + iwe.len = IW_EV_QUAL_LEN; + current_ev = + iwe_stream_add_event(current_ev, end_buf, &iwe, iwe.len); + + /* Add encryption capability */ + iwe.cmd = SIOCGIWENCODE; + if (adapter->scantable[i].privacy) { + iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY; + } else { + iwe.u.data.flags = IW_ENCODE_DISABLED; + } + iwe.u.data.length = 0; + iwe.len = IW_EV_POINT_LEN + iwe.u.data.length; + current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, + adapter->scantable->ssid. + ssid); + + current_val = current_ev + IW_EV_LCP_LEN; + + iwe.cmd = SIOCGIWRATE; + + iwe.u.bitrate.fixed = 0; + iwe.u.bitrate.disabled = 0; + iwe.u.bitrate.value = 0; + + /* Bit rate given in 500 kb/s units (+ 0x80) */ + for (j = 0; j < sizeof(adapter->scantable[i].libertas_supported_rates); + j++) { + if (adapter->scantable[i].libertas_supported_rates[j] == 0) { + break; + } + rate = + (adapter->scantable[i].libertas_supported_rates[j] & 0x7F) * + 500000; + if (rate > iwe.u.bitrate.value) { + iwe.u.bitrate.value = rate; + } + + iwe.u.bitrate.value = + (adapter->scantable[i].libertas_supported_rates[j] + & 0x7f) * 500000; + iwe.len = IW_EV_PARAM_LEN; + current_ev = + iwe_stream_add_value(current_ev, current_val, + end_buf, &iwe, iwe.len); + + } + if ((adapter->scantable[i].inframode == wlan802_11ibss) + && !libertas_SSID_cmp(&adapter->curbssparams.ssid, + &adapter->scantable[i].ssid) + && adapter->adhoccreate) { + iwe.u.bitrate.value = 22 * 500000; + } + iwe.len = IW_EV_PARAM_LEN; + current_ev = + iwe_stream_add_value(current_ev, current_val, end_buf, &iwe, + iwe.len); + + /* Add new value to event */ + current_val = current_ev + IW_EV_LCP_LEN; + + if (adapter->scantable[i].wpa2_supplicant.wpa_ie[0] == WPA2_IE) { + memset(&iwe, 0, sizeof(iwe)); + memset(buf, 0, sizeof(buf)); + memcpy(buf, adapter->scantable[i]. + wpa2_supplicant.wpa_ie, + adapter->scantable[i].wpa2_supplicant. + wpa_ie_len); + iwe.cmd = IWEVGENIE; + iwe.u.data.length = adapter->scantable[i]. + wpa2_supplicant.wpa_ie_len; + iwe.len = IW_EV_POINT_LEN + iwe.u.data.length; + current_ev = iwe_stream_add_point(current_ev, end_buf, + &iwe, buf); + } + if (adapter->scantable[i].wpa_supplicant.wpa_ie[0] == WPA_IE) { + memset(&iwe, 0, sizeof(iwe)); + memset(buf, 0, sizeof(buf)); + memcpy(buf, adapter->scantable[i]. + wpa_supplicant.wpa_ie, + adapter->scantable[i].wpa_supplicant. + wpa_ie_len); + iwe.cmd = IWEVGENIE; + iwe.u.data.length = adapter->scantable[i]. + wpa_supplicant.wpa_ie_len; + iwe.len = IW_EV_POINT_LEN + iwe.u.data.length; + current_ev = iwe_stream_add_point(current_ev, end_buf, + &iwe, buf); + } + + + if (adapter->scantable[i].extra_ie != 0) { + memset(&iwe, 0, sizeof(iwe)); + memset(buf, 0, sizeof(buf)); + ptr = buf; + ptr += sprintf(ptr, "extra_ie"); + iwe.u.data.length = strlen(buf); + + lbs_pr_debug(1, "iwe.u.data.length %d\n", + iwe.u.data.length); + lbs_pr_debug(1, "BUF: %s \n", buf); + + iwe.cmd = IWEVCUSTOM; + iwe.len = IW_EV_POINT_LEN + iwe.u.data.length; + current_ev = + iwe_stream_add_point(current_ev, end_buf, &iwe, + buf); + } + + current_val = current_ev + IW_EV_LCP_LEN; + + /* + * Check if we added any event + */ + if ((current_val - current_ev) > IW_EV_LCP_LEN) + current_ev = current_val; + } + + dwrq->length = (current_ev - extra); + dwrq->flags = 0; + + LEAVE(); + return 0; +} + +/** + * @brief Prepare a scan command to be sent to the firmware + * + * Use the wlan_scan_cmd_config sent to the command processing module in + * the libertas_prepare_and_send_command to configure a cmd_ds_802_11_scan command + * struct to send to firmware. + * + * The fixed fields specifying the BSS type and BSSID filters as well as a + * variable number/length of TLVs are sent in the command to firmware. + * + * @param priv A pointer to wlan_private structure + * @param cmd A pointer to cmd_ds_command structure to be sent to + * firmware with the cmd_DS_801_11_SCAN structure + * @param pdata_buf Void pointer cast of a wlan_scan_cmd_config struct used + * to set the fields/TLVs for the command sent to firmware + * + * @return 0 or -1 + * + * @sa wlan_scan_create_channel_list + */ +int libertas_cmd_80211_scan(wlan_private * priv, + struct cmd_ds_command *cmd, void *pdata_buf) +{ + struct cmd_ds_802_11_scan *pscan = &cmd->params.scan; + struct wlan_scan_cmd_config *pscancfg; + + ENTER(); + + pscancfg = pdata_buf; + + /* Set fixed field variables in scan command */ + pscan->bsstype = pscancfg->bsstype; + memcpy(pscan->BSSID, pscancfg->specificBSSID, sizeof(pscan->BSSID)); + memcpy(pscan->tlvbuffer, pscancfg->tlvbuffer, pscancfg->tlvbufferlen); + + cmd->command = cpu_to_le16(cmd_802_11_scan); + + /* size is equal to the sizeof(fixed portions) + the TLV len + header */ + cmd->size = cpu_to_le16(sizeof(pscan->bsstype) + + sizeof(pscan->BSSID) + + pscancfg->tlvbufferlen + S_DS_GEN); + + lbs_pr_debug(1, "SCAN_CMD: command=%x, size=%x, seqnum=%x\n", + cmd->command, cmd->size, cmd->seqnum); + LEAVE(); + return 0; +} + +/** + * @brief This function handles the command response of scan + * + * The response buffer for the scan command has the following + * memory layout: + * + * .-----------------------------------------------------------. + * | header (4 * sizeof(u16)): Standard command response hdr | + * .-----------------------------------------------------------. + * | bufsize (u16) : sizeof the BSS Description data | + * .-----------------------------------------------------------. + * | NumOfSet (u8) : Number of BSS Descs returned | + * .-----------------------------------------------------------. + * | BSSDescription data (variable, size given in bufsize) | + * .-----------------------------------------------------------. + * | TLV data (variable, size calculated using header->size, | + * | bufsize and sizeof the fixed fields above) | + * .-----------------------------------------------------------. + * + * @param priv A pointer to wlan_private structure + * @param resp A pointer to cmd_ds_command + * + * @return 0 or -1 + */ +int libertas_ret_80211_scan(wlan_private * priv, struct cmd_ds_command *resp) +{ + wlan_adapter *adapter = priv->adapter; + struct cmd_ds_802_11_scan_rsp *pscan; + struct bss_descriptor newbssentry; + struct mrvlietypes_data *ptlv; + struct mrvlietypes_tsftimestamp *ptsftlv; + u8 *pbssinfo; + u16 scanrespsize; + int bytesleft; + int numintable; + int bssIdx; + int idx; + int tlvbufsize; + u64 tsfval; + + ENTER(); + + pscan = &resp->params.scanresp; + + if (pscan->nr_sets > MRVDRV_MAX_BSSID_LIST) { + lbs_pr_debug(1, + "SCAN_RESP: Invalid number of AP returned (%d)!!\n", + pscan->nr_sets); + LEAVE(); + return -1; + } + + bytesleft = le16_to_cpu(pscan->bssdescriptsize); + lbs_pr_debug(1, "SCAN_RESP: bssdescriptsize %d\n", bytesleft); + + scanrespsize = le16_to_cpu(resp->size); + lbs_pr_debug(1, "SCAN_RESP: returned %d AP before parsing\n", + pscan->nr_sets); + + numintable = adapter->numinscantable; + pbssinfo = pscan->bssdesc_and_tlvbuffer; + + /* The size of the TLV buffer is equal to the entire command response + * size (scanrespsize) minus the fixed fields (sizeof()'s), the + * BSS Descriptions (bssdescriptsize as bytesLef) and the command + * response header (S_DS_GEN) + */ + tlvbufsize = scanrespsize - (bytesleft + sizeof(pscan->bssdescriptsize) + + sizeof(pscan->nr_sets) + + S_DS_GEN); + + ptlv = (struct mrvlietypes_data *) (pscan->bssdesc_and_tlvbuffer + bytesleft); + + /* Search the TLV buffer space in the scan response for any valid TLVs */ + wlan_ret_802_11_scan_get_tlv_ptrs(ptlv, tlvbufsize, &ptsftlv); + + /* + * Process each scan response returned (pscan->nr_sets). Save + * the information in the newbssentry and then insert into the + * driver scan table either as an update to an existing entry + * or as an addition at the end of the table + */ + for (idx = 0; idx < pscan->nr_sets && bytesleft; idx++) { + /* Zero out the newbssentry we are about to store info in */ + memset(&newbssentry, 0x00, sizeof(newbssentry)); + + /* Process the data fields and IEs returned for this BSS */ + if ((InterpretBSSDescriptionWithIE(&newbssentry, + &pbssinfo, + &bytesleft) == + 0) + && CHECK_SSID_IS_VALID(&newbssentry.ssid)) { + + lbs_pr_debug(1, + "SCAN_RESP: BSSID = %02x:%02x:%02x:%02x:%02x:%02x\n", + newbssentry.macaddress[0], + newbssentry.macaddress[1], + newbssentry.macaddress[2], + newbssentry.macaddress[3], + newbssentry.macaddress[4], + newbssentry.macaddress[5]); + + /* + * Search the scan table for the same bssid + */ + for (bssIdx = 0; bssIdx < numintable; bssIdx++) { + if (memcmp(newbssentry.macaddress, + adapter->scantable[bssIdx]. + macaddress, + sizeof(newbssentry.macaddress)) == + 0) { + /* + * If the SSID matches as well, it is a duplicate of + * this entry. Keep the bssIdx set to this + * entry so we replace the old contents in the table + */ + if ((newbssentry.ssid.ssidlength == + adapter->scantable[bssIdx].ssid. + ssidlength) + && + (memcmp + (newbssentry.ssid.ssid, + adapter->scantable[bssIdx].ssid. + ssid, + newbssentry.ssid.ssidlength) == + 0)) { + lbs_pr_debug(1, + "SCAN_RESP: Duplicate of index: %d\n", + bssIdx); + break; + } + } + } + /* + * If the bssIdx is equal to the number of entries in the table, + * the new entry was not a duplicate; append it to the scan + * table + */ + if (bssIdx == numintable) { + /* Range check the bssIdx, keep it limited to the last entry */ + if (bssIdx == MRVDRV_MAX_BSSID_LIST) { + bssIdx--; + } else { + numintable++; + } + } + + /* + * If the TSF TLV was appended to the scan results, save the + * this entries TSF value in the networktsf field. The + * networktsf is the firmware's TSF value at the time the + * beacon or probe response was received. + */ + if (ptsftlv) { + memcpy(&tsfval, &ptsftlv->tsftable[idx], + sizeof(tsfval)); + tsfval = le64_to_cpu(tsfval); + + memcpy(&newbssentry.networktsf, + &tsfval, sizeof(newbssentry.networktsf)); + } + + /* Copy the locally created newbssentry to the scan table */ + memcpy(&adapter->scantable[bssIdx], + &newbssentry, + sizeof(adapter->scantable[bssIdx])); + + } else { + + /* error parsing/interpreting the scan response, skipped */ + lbs_pr_debug(1, "SCAN_RESP: " + "InterpretBSSDescriptionWithIE returned ERROR\n"); + } + } + + lbs_pr_debug(1, "SCAN_RESP: Scanned %2d APs, %d valid, %d total\n", + pscan->nr_sets, numintable - adapter->numinscantable, + numintable); + + /* Update the total number of BSSIDs in the scan table */ + adapter->numinscantable = numintable; + + LEAVE(); + return 0; +} diff --git a/drivers/net/wireless/libertas/scan.h b/drivers/net/wireless/libertas/scan.h new file mode 100644 index 00000000000..d93aa7fa44f --- /dev/null +++ b/drivers/net/wireless/libertas/scan.h @@ -0,0 +1,216 @@ +/* -*- mode: C; tab-width: 4; indent-tabs-mode: nil -*- */ +/* vi: set expandtab shiftwidth=4 tabstop=4 textwidth=78: */ + +/** + * Interface for the wlan network scan routines + * + * Driver interface functions and type declarations for the scan module + * implemented in wlan_scan.c. + */ +#ifndef _WLAN_SCAN_H +#define _WLAN_SCAN_H + +#include "hostcmd.h" + +/** + * @brief Maximum number of channels that can be sent in a setuserscan ioctl + * + * @sa wlan_ioctl_user_scan_cfg + */ +#define WLAN_IOCTL_USER_SCAN_CHAN_MAX 50 + +//! Infrastructure BSS scan type in wlan_scan_cmd_config +#define WLAN_SCAN_BSS_TYPE_BSS 1 + +//! Adhoc BSS scan type in wlan_scan_cmd_config +#define WLAN_SCAN_BSS_TYPE_IBSS 2 + +//! Adhoc or Infrastructure BSS scan type in wlan_scan_cmd_config, no filter +#define WLAN_SCAN_BSS_TYPE_ANY 3 + +/** + * @brief Structure used internally in the wlan driver to configure a scan. + * + * Sent to the command processing module to configure the firmware + * scan command prepared by libertas_cmd_80211_scan. + * + * @sa wlan_scan_networks + * + */ +struct wlan_scan_cmd_config { + /** + * @brief BSS type to be sent in the firmware command + * + * Field can be used to restrict the types of networks returned in the + * scan. valid settings are: + * + * - WLAN_SCAN_BSS_TYPE_BSS (infrastructure) + * - WLAN_SCAN_BSS_TYPE_IBSS (adhoc) + * - WLAN_SCAN_BSS_TYPE_ANY (unrestricted, adhoc and infrastructure) + */ + u8 bsstype; + + /** + * @brief Specific BSSID used to filter scan results in the firmware + */ + u8 specificBSSID[ETH_ALEN]; + + /** + * @brief length of TLVs sent in command starting at tlvBuffer + */ + int tlvbufferlen; + + /** + * @brief SSID TLV(s) and ChanList TLVs to be sent in the firmware command + * + * @sa TLV_TYPE_CHANLIST, mrvlietypes_chanlistparamset_t + * @sa TLV_TYPE_SSID, mrvlietypes_ssidparamset_t + */ + u8 tlvbuffer[1]; //!< SSID TLV(s) and ChanList TLVs are stored here +}; + +/** + * @brief IOCTL channel sub-structure sent in wlan_ioctl_user_scan_cfg + * + * Multiple instances of this structure are included in the IOCTL command + * to configure a instance of a scan on the specific channel. + */ +struct wlan_ioctl_user_scan_chan { + u8 channumber; //!< channel Number to scan + u8 radiotype; //!< Radio type: 'B/G' band = 0, 'A' band = 1 + u8 scantype; //!< Scan type: Active = 0, Passive = 1 + u16 scantime; //!< Scan duration in milliseconds; if 0 default used +}; + +/** + * @brief IOCTL input structure to configure an immediate scan cmd to firmware + * + * Used in the setuserscan (WLAN_SET_USER_SCAN) private ioctl. Specifies + * a number of parameters to be used in general for the scan as well + * as a channel list (wlan_ioctl_user_scan_chan) for each scan period + * desired. + * + * @sa libertas_set_user_scan_ioctl + */ +struct wlan_ioctl_user_scan_cfg { + + /** + * @brief Flag set to keep the previous scan table intact + * + * If set, the scan results will accumulate, replacing any previous + * matched entries for a BSS with the new scan data + */ + u8 keeppreviousscan; //!< Do not erase the existing scan results + + /** + * @brief BSS type to be sent in the firmware command + * + * Field can be used to restrict the types of networks returned in the + * scan. valid settings are: + * + * - WLAN_SCAN_BSS_TYPE_BSS (infrastructure) + * - WLAN_SCAN_BSS_TYPE_IBSS (adhoc) + * - WLAN_SCAN_BSS_TYPE_ANY (unrestricted, adhoc and infrastructure) + */ + u8 bsstype; + + /** + * @brief Configure the number of probe requests for active chan scans + */ + u8 numprobes; + + /** + * @brief BSSID filter sent in the firmware command to limit the results + */ + u8 specificBSSID[ETH_ALEN]; + + /** + * @brief SSID filter sent in the firmware command to limit the results + */ + char specificSSID[IW_ESSID_MAX_SIZE + 1]; + + /** + * @brief Variable number (fixed maximum) of channels to scan up + */ + struct wlan_ioctl_user_scan_chan chanlist[WLAN_IOCTL_USER_SCAN_CHAN_MAX]; +}; + +/** + * @brief Structure used to store information for each beacon/probe response + */ +struct bss_descriptor { + u8 macaddress[ETH_ALEN]; + + struct WLAN_802_11_SSID ssid; + + /* WEP encryption requirement */ + u32 privacy; + + /* receive signal strength in dBm */ + long rssi; + + u32 channel; + + u16 beaconperiod; + + u32 atimwindow; + + enum WLAN_802_11_NETWORK_INFRASTRUCTURE inframode; + u8 libertas_supported_rates[WLAN_SUPPORTED_RATES]; + + int extra_ie; + + u8 timestamp[8]; //!< TSF value included in the beacon/probe response + union ieeetypes_phyparamset phyparamset; + union IEEEtypes_ssparamset ssparamset; + struct ieeetypes_capinfo cap; + u8 datarates[WLAN_SUPPORTED_RATES]; + + __le64 networktsf; //!< TSF timestamp from the current firmware TSF + + struct ieeetypes_countryinfofullset countryinfo; + + struct WPA_SUPPLICANT wpa_supplicant; + struct WPA_SUPPLICANT wpa2_supplicant; + +}; + +extern int libertas_SSID_cmp(struct WLAN_802_11_SSID *ssid1, + struct WLAN_802_11_SSID *ssid2); +extern int libertas_find_SSID_in_list(wlan_adapter * adapter, struct WLAN_802_11_SSID *ssid, + u8 * bssid, int mode); +int libertas_find_best_SSID_in_list(wlan_adapter * adapter, enum WLAN_802_11_NETWORK_INFRASTRUCTURE mode); +extern int libertas_find_BSSID_in_list(wlan_adapter * adapter, u8 * bssid, int mode); + +int libertas_find_best_network_SSID(wlan_private * priv, + struct WLAN_802_11_SSID *pSSID, + enum WLAN_802_11_NETWORK_INFRASTRUCTURE preferred_mode, + enum WLAN_802_11_NETWORK_INFRASTRUCTURE *out_mode); + +extern int libertas_send_specific_SSID_scan(wlan_private * priv, + struct WLAN_802_11_SSID *prequestedssid, + u8 keeppreviousscan); +extern int libertas_send_specific_BSSID_scan(wlan_private * priv, + u8 * bssid, u8 keeppreviousscan); + +extern int libertas_cmd_80211_scan(wlan_private * priv, + struct cmd_ds_command *cmd, + void *pdata_buf); + +extern int libertas_ret_80211_scan(wlan_private * priv, + struct cmd_ds_command *resp); + +int wlan_scan_networks(wlan_private * priv, + const struct wlan_ioctl_user_scan_cfg * puserscanin); + +struct ifreq; + +struct iw_point; +struct iw_param; +struct iw_request_info; +extern int libertas_get_scan(struct net_device *dev, struct iw_request_info *info, + struct iw_point *dwrq, char *extra); +extern int libertas_set_scan(struct net_device *dev, struct iw_request_info *info, + struct iw_param *vwrq, char *extra); + +#endif /* _WLAN_SCAN_H */ diff --git a/drivers/net/wireless/libertas/thread.h b/drivers/net/wireless/libertas/thread.h new file mode 100644 index 00000000000..207b8a6cc33 --- /dev/null +++ b/drivers/net/wireless/libertas/thread.h @@ -0,0 +1,52 @@ +#ifndef __WLAN_THREAD_H_ +#define __WLAN_THREAD_H_ + +#include <linux/kthread.h> + +struct wlan_thread { + struct task_struct *task; + wait_queue_head_t waitq; + pid_t pid; + void *priv; +}; + +static inline void wlan_activate_thread(struct wlan_thread * thr) +{ + /** Record the thread pid */ + thr->pid = current->pid; + + /** Initialize the wait queue */ + init_waitqueue_head(&thr->waitq); +} + +static inline void wlan_deactivate_thread(struct wlan_thread * thr) +{ + ENTER(); + + thr->pid = 0; + + LEAVE(); +} + +static inline void wlan_create_thread(int (*wlanfunc) (void *), + struct wlan_thread * thr, char *name) +{ + thr->task = kthread_run(wlanfunc, thr, "%s", name); +} + +static inline int wlan_terminate_thread(struct wlan_thread * thr) +{ + ENTER(); + + /* Check if the thread is active or not */ + if (!thr->pid) { + printk(KERN_ERR "Thread does not exist\n"); + return -1; + } + kthread_stop(thr->task); + + LEAVE(); + return 0; +} + +#endif diff --git a/drivers/net/wireless/libertas/tx.c b/drivers/net/wireless/libertas/tx.c new file mode 100644 index 00000000000..82d06223043 --- /dev/null +++ b/drivers/net/wireless/libertas/tx.c @@ -0,0 +1,285 @@ +/** + * This file contains the handling of TX in wlan driver. + */ +#include <linux/netdevice.h> + +#include "hostcmd.h" +#include "radiotap.h" +#include "sbi.h" +#include "decl.h" +#include "defs.h" +#include "dev.h" +#include "wext.h" + +/** + * @brief This function converts Tx/Rx rates from IEEE80211_RADIOTAP_RATE + * units (500 Kb/s) into Marvell WLAN format (see Table 8 in Section 3.2.1) + * + * @param rate Input rate + * @return Output Rate (0 if invalid) + */ +static u32 convert_radiotap_rate_to_mv(u8 rate) +{ + switch (rate) { + case 2: /* 1 Mbps */ + return 0 | (1 << 4); + case 4: /* 2 Mbps */ + return 1 | (1 << 4); + case 11: /* 5.5 Mbps */ + return 2 | (1 << 4); + case 22: /* 11 Mbps */ + return 3 | (1 << 4); + case 12: /* 6 Mbps */ + return 4 | (1 << 4); + case 18: /* 9 Mbps */ + return 5 | (1 << 4); + case 24: /* 12 Mbps */ + return 6 | (1 << 4); + case 36: /* 18 Mbps */ + return 7 | (1 << 4); + case 48: /* 24 Mbps */ + return 8 | (1 << 4); + case 72: /* 36 Mbps */ + return 9 | (1 << 4); + case 96: /* 48 Mbps */ + return 10 | (1 << 4); + case 108: /* 54 Mbps */ + return 11 | (1 << 4); + } + return 0; +} + +/** + * @brief This function processes a single packet and sends + * to IF layer + * + * @param priv A pointer to wlan_private structure + * @param skb A pointer to skb which includes TX packet + * @return 0 or -1 + */ +static int SendSinglePacket(wlan_private * priv, struct sk_buff *skb) +{ + wlan_adapter *adapter = priv->adapter; + int ret = 0; + struct txpd localtxpd; + struct txpd *plocaltxpd = &localtxpd; + u8 *p802x_hdr; + struct tx_radiotap_hdr *pradiotap_hdr; + u32 new_rate; + u8 *ptr = priv->adapter->tmptxbuf; + + ENTER(); + + if (priv->adapter->surpriseremoved) + return -1; + + if ((priv->adapter->debugmode & MRVDRV_DEBUG_TX_PATH) != 0) + lbs_dbg_hex("TX packet: ", skb->data, + min_t(unsigned int, skb->len, 100)); + + if (!skb->len || (skb->len > MRVDRV_ETH_TX_PACKET_BUFFER_SIZE)) { + lbs_pr_debug(1, "Tx error: Bad skb length %d : %d\n", + skb->len, MRVDRV_ETH_TX_PACKET_BUFFER_SIZE); + ret = -1; + goto done; + } + + memset(plocaltxpd, 0, sizeof(struct txpd)); + + plocaltxpd->tx_packet_length = skb->len; + + /* offset of actual data */ + plocaltxpd->tx_packet_location = sizeof(struct txpd); + + /* TxCtrl set by user or default */ + plocaltxpd->tx_control = adapter->pkttxctrl; + + p802x_hdr = skb->data; + if (priv->adapter->radiomode == WLAN_RADIOMODE_RADIOTAP) { + + /* locate radiotap header */ + pradiotap_hdr = (struct tx_radiotap_hdr *)skb->data; + + /* set txpd fields from the radiotap header */ + new_rate = convert_radiotap_rate_to_mv(pradiotap_hdr->rate); + if (new_rate != 0) { + /* erase tx_control[4:0] */ + plocaltxpd->tx_control &= ~0x1f; + /* write new tx_control[4:0] */ + plocaltxpd->tx_control |= new_rate; + } + + /* skip the radiotap header */ + p802x_hdr += sizeof(struct tx_radiotap_hdr); + plocaltxpd->tx_packet_length -= sizeof(struct tx_radiotap_hdr); + + } + /* copy destination address from 802.3 or 802.11 header */ + if (priv->adapter->linkmode == WLAN_LINKMODE_802_11) + memcpy(plocaltxpd->tx_dest_addr_high, p802x_hdr + 4, ETH_ALEN); + else + memcpy(plocaltxpd->tx_dest_addr_high, p802x_hdr, ETH_ALEN); + + lbs_dbg_hex("txpd", (u8 *) plocaltxpd, sizeof(struct txpd)); + + if (IS_MESH_FRAME(skb)) { + plocaltxpd->tx_control |= TxPD_MESH_FRAME; + } + + memcpy(ptr, plocaltxpd, sizeof(struct txpd)); + + ptr += sizeof(struct txpd); + + lbs_dbg_hex("Tx Data", (u8 *) p802x_hdr, plocaltxpd->tx_packet_length); + memcpy(ptr, p802x_hdr, plocaltxpd->tx_packet_length); + ret = libertas_sbi_host_to_card(priv, MVMS_DAT, + priv->adapter->tmptxbuf, + plocaltxpd->tx_packet_length + + sizeof(struct txpd)); + + if (ret) { + lbs_pr_debug(1, "Tx error: libertas_sbi_host_to_card failed: 0x%X\n", ret); + goto done; + } + + lbs_pr_debug(1, "SendSinglePacket succeeds\n"); + + done: + if (!ret) { + priv->stats.tx_packets++; + priv->stats.tx_bytes += skb->len; + } else { + priv->stats.tx_dropped++; + priv->stats.tx_errors++; + } + + if (!ret && priv->adapter->radiomode == WLAN_RADIOMODE_RADIOTAP) { + /* Keep the skb to echo it back once Tx feedback is + received from FW */ + skb_orphan(skb); + /* stop processing outgoing pkts */ + netif_stop_queue(priv->wlan_dev.netdev); + /* freeze any packets already in our queues */ + priv->adapter->TxLockFlag = 1; + } else { + dev_kfree_skb_any(skb); + priv->adapter->currenttxskb = NULL; + } + + LEAVE(); + return ret; +} + + +void libertas_tx_runqueue(wlan_private *priv) +{ + wlan_adapter *adapter = priv->adapter; + int i; + + spin_lock(&adapter->txqueue_lock); + for (i = 0; i < adapter->tx_queue_idx; i++) { + struct sk_buff *skb = adapter->tx_queue_ps[i]; + spin_unlock(&adapter->txqueue_lock); + SendSinglePacket(priv, skb); + spin_lock(&adapter->txqueue_lock); + } + adapter->tx_queue_idx = 0; + spin_unlock(&adapter->txqueue_lock); +} + +static void wlan_tx_queue(wlan_private *priv, struct sk_buff *skb) +{ + wlan_adapter *adapter = priv->adapter; + + spin_lock(&adapter->txqueue_lock); + + WARN_ON(priv->adapter->tx_queue_idx >= NR_TX_QUEUE); + adapter->tx_queue_ps[adapter->tx_queue_idx++] = skb; + if (adapter->tx_queue_idx == NR_TX_QUEUE) + netif_stop_queue(priv->wlan_dev.netdev); + else + netif_start_queue(priv->wlan_dev.netdev); + + spin_unlock(&adapter->txqueue_lock); +} + +/** + * @brief This function checks the conditions and sends packet to IF + * layer if everything is ok. + * + * @param priv A pointer to wlan_private structure + * @return n/a + */ +int libertas_process_tx(wlan_private * priv, struct sk_buff *skb) +{ + int ret = -1; + + ENTER(); + + lbs_dbg_hex("TX Data", skb->data, min_t(unsigned int, skb->len, 100)); + + if (priv->wlan_dev.dnld_sent) { + lbs_pr_alert( "TX error: dnld_sent = %d, not sending\n", + priv->wlan_dev.dnld_sent); + goto done; + } + + if ((priv->adapter->psstate == PS_STATE_SLEEP) || + (priv->adapter->psstate == PS_STATE_PRE_SLEEP)) { + wlan_tx_queue(priv, skb); + return ret; + } + + priv->adapter->currenttxskb = skb; + + ret = SendSinglePacket(priv, skb); +done: + LEAVE(); + return ret; +} + +/** + * @brief This function sends to the host the last transmitted packet, + * filling the radiotap headers with transmission information. + * + * @param priv A pointer to wlan_private structure + * @param status A 32 bit value containing transmission status. + * + * @returns void + */ +void libertas_send_tx_feedback(wlan_private * priv) +{ + wlan_adapter *adapter = priv->adapter; + struct tx_radiotap_hdr *radiotap_hdr; + u32 status = adapter->eventcause; + int txfail; + int try_count; + + if (adapter->radiomode != WLAN_RADIOMODE_RADIOTAP || + adapter->currenttxskb == NULL) + return; + + radiotap_hdr = (struct tx_radiotap_hdr *)adapter->currenttxskb->data; + + if ((adapter->debugmode & MRVDRV_DEBUG_TX_PATH) != 0) + lbs_dbg_hex("TX feedback: ", (u8 *) radiotap_hdr, + min_t(unsigned int, adapter->currenttxskb->len, 100)); + + txfail = (status >> 24); + +#if 0 + /* The version of roofnet that we've tested does not use this yet + * But it may be used in the future. + */ + if (txfail) + radiotap_hdr->flags &= IEEE80211_RADIOTAP_F_TX_FAIL; +#endif + try_count = (status >> 16) & 0xff; + radiotap_hdr->data_retries = (try_count) ? + (1 + adapter->txretrycount - try_count) : 0; + libertas_upload_rx_packet(priv, adapter->currenttxskb); + adapter->currenttxskb = NULL; + priv->adapter->TxLockFlag = 0; + if (priv->adapter->connect_status == libertas_connected) + netif_wake_queue(priv->wlan_dev.netdev); +} diff --git a/drivers/net/wireless/libertas/types.h b/drivers/net/wireless/libertas/types.h new file mode 100644 index 00000000000..09d62f8b1a1 --- /dev/null +++ b/drivers/net/wireless/libertas/types.h @@ -0,0 +1,289 @@ +/** + * This header file contains definition for global types + */ +#ifndef _WLAN_TYPES_ +#define _WLAN_TYPES_ + +#include <linux/if_ether.h> + +/** IEEE type definitions */ +enum ieeetypes_elementid { + SSID = 0, + SUPPORTED_RATES, + FH_PARAM_SET, + DS_PARAM_SET, + CF_PARAM_SET, + TIM, + IBSS_PARAM_SET, + COUNTRY_INFO = 7, + + CHALLENGE_TEXT = 16, + + EXTENDED_SUPPORTED_RATES = 50, + + VENDOR_SPECIFIC_221 = 221, + + WPA_IE = 221, + WPA2_IE = 48, + + EXTRA_IE = 133, +} __attribute__ ((packed)); + +#define CAPINFO_MASK (~(0xda00)) + +struct ieeetypes_capinfo { + u8 ess:1; + u8 ibss:1; + u8 cfpollable:1; + u8 cfpollrqst:1; + u8 privacy:1; + u8 shortpreamble:1; + u8 pbcc:1; + u8 chanagility:1; + u8 spectrummgmt:1; + u8 rsrvd3:1; + u8 shortslottime:1; + u8 apsd:1; + u8 rsvrd2:1; + u8 dsssofdm:1; + u8 rsrvd1:2; +} __attribute__ ((packed)); + +struct ieeetypes_cfparamset { + u8 elementid; + u8 len; + u8 cfpcnt; + u8 cfpperiod; + u16 cfpmaxduration; + u16 cfpdurationremaining; +} __attribute__ ((packed)); + + +struct ieeetypes_ibssparamset { + u8 elementid; + u8 len; + u16 atimwindow; +} __attribute__ ((packed)); + +union IEEEtypes_ssparamset { + struct ieeetypes_cfparamset cfparamset; + struct ieeetypes_ibssparamset ibssparamset; +} __attribute__ ((packed)); + +struct ieeetypes_fhparamset { + u8 elementid; + u8 len; + u16 dwelltime; + u8 hopset; + u8 hoppattern; + u8 hopindex; +} __attribute__ ((packed)); + +struct ieeetypes_dsparamset { + u8 elementid; + u8 len; + u8 currentchan; +} __attribute__ ((packed)); + +union ieeetypes_phyparamset { + struct ieeetypes_fhparamset fhparamset; + struct ieeetypes_dsparamset dsparamset; +} __attribute__ ((packed)); + +struct ieeetypes_assocrsp { + struct ieeetypes_capinfo capability; + u16 statuscode; + u16 aid; + u8 iebuffer[1]; +} __attribute__ ((packed)); + +/** TLV type ID definition */ +#define PROPRIETARY_TLV_BASE_ID 0x0100 + +/* Terminating TLV type */ +#define MRVL_TERMINATE_TLV_ID 0xffff + +#define TLV_TYPE_SSID 0x0000 +#define TLV_TYPE_RATES 0x0001 +#define TLV_TYPE_PHY_FH 0x0002 +#define TLV_TYPE_PHY_DS 0x0003 +#define TLV_TYPE_CF 0x0004 +#define TLV_TYPE_IBSS 0x0006 + +#define TLV_TYPE_DOMAIN 0x0007 + +#define TLV_TYPE_POWER_CAPABILITY 0x0021 + +#define TLV_TYPE_KEY_MATERIAL (PROPRIETARY_TLV_BASE_ID + 0) +#define TLV_TYPE_CHANLIST (PROPRIETARY_TLV_BASE_ID + 1) +#define TLV_TYPE_NUMPROBES (PROPRIETARY_TLV_BASE_ID + 2) +#define TLV_TYPE_RSSI_LOW (PROPRIETARY_TLV_BASE_ID + 4) +#define TLV_TYPE_SNR_LOW (PROPRIETARY_TLV_BASE_ID + 5) +#define TLV_TYPE_FAILCOUNT (PROPRIETARY_TLV_BASE_ID + 6) +#define TLV_TYPE_BCNMISS (PROPRIETARY_TLV_BASE_ID + 7) +#define TLV_TYPE_LED_GPIO (PROPRIETARY_TLV_BASE_ID + 8) +#define TLV_TYPE_LEDBEHAVIOR (PROPRIETARY_TLV_BASE_ID + 9) +#define TLV_TYPE_PASSTHROUGH (PROPRIETARY_TLV_BASE_ID + 10) +#define TLV_TYPE_REASSOCAP (PROPRIETARY_TLV_BASE_ID + 11) +#define TLV_TYPE_POWER_TBL_2_4GHZ (PROPRIETARY_TLV_BASE_ID + 12) +#define TLV_TYPE_POWER_TBL_5GHZ (PROPRIETARY_TLV_BASE_ID + 13) +#define TLV_TYPE_BCASTPROBE (PROPRIETARY_TLV_BASE_ID + 14) +#define TLV_TYPE_NUMSSID_PROBE (PROPRIETARY_TLV_BASE_ID + 15) +#define TLV_TYPE_WMMQSTATUS (PROPRIETARY_TLV_BASE_ID + 16) +#define TLV_TYPE_CRYPTO_DATA (PROPRIETARY_TLV_BASE_ID + 17) +#define TLV_TYPE_WILDCARDSSID (PROPRIETARY_TLV_BASE_ID + 18) +#define TLV_TYPE_TSFTIMESTAMP (PROPRIETARY_TLV_BASE_ID + 19) +#define TLV_TYPE_RSSI_HIGH (PROPRIETARY_TLV_BASE_ID + 22) +#define TLV_TYPE_SNR_HIGH (PROPRIETARY_TLV_BASE_ID + 23) + +/** TLV related data structures*/ +struct mrvlietypesheader { + u16 type; + u16 len; +} __attribute__ ((packed)); + +struct mrvlietypes_data { + struct mrvlietypesheader header; + u8 Data[1]; +} __attribute__ ((packed)); + +struct mrvlietypes_ratesparamset { + struct mrvlietypesheader header; + u8 rates[1]; +} __attribute__ ((packed)); + +struct mrvlietypes_ssidparamset { + struct mrvlietypesheader header; + u8 ssid[1]; +} __attribute__ ((packed)); + +struct mrvlietypes_wildcardssidparamset { + struct mrvlietypesheader header; + u8 MaxSsidlength; + u8 ssid[1]; +} __attribute__ ((packed)); + +struct chanscanmode { + u8 passivescan:1; + u8 disablechanfilt:1; + u8 reserved_2_7:6; +} __attribute__ ((packed)); + +struct chanscanparamset { + u8 radiotype; + u8 channumber; + struct chanscanmode chanscanmode; + u16 minscantime; + u16 maxscantime; +} __attribute__ ((packed)); + +struct mrvlietypes_chanlistparamset { + struct mrvlietypesheader header; + struct chanscanparamset chanscanparam[1]; +} __attribute__ ((packed)); + +struct cfparamset { + u8 cfpcnt; + u8 cfpperiod; + u16 cfpmaxduration; + u16 cfpdurationremaining; +} __attribute__ ((packed)); + +struct ibssparamset { + u16 atimwindow; +} __attribute__ ((packed)); + +struct mrvlietypes_ssparamset { + struct mrvlietypesheader header; + union { + struct cfparamset cfparamset[1]; + struct ibssparamset ibssparamset[1]; + } cf_ibss; +} __attribute__ ((packed)); + +struct fhparamset { + u16 dwelltime; + u8 hopset; + u8 hoppattern; + u8 hopindex; +} __attribute__ ((packed)); + +struct dsparamset { + u8 currentchan; +} __attribute__ ((packed)); + +struct mrvlietypes_phyparamset { + struct mrvlietypesheader header; + union { + struct fhparamset fhparamset[1]; + struct dsparamset dsparamset[1]; + } fh_ds; +} __attribute__ ((packed)); + +struct mrvlietypes_rsnparamset { + struct mrvlietypesheader header; + u8 rsnie[1]; +} __attribute__ ((packed)); + +struct mrvlietypes_tsftimestamp { + struct mrvlietypesheader header; + __le64 tsftable[1]; +} __attribute__ ((packed)); + +/** Local Power capability */ +struct mrvlietypes_powercapability { + struct mrvlietypesheader header; + s8 minpower; + s8 maxpower; +} __attribute__ ((packed)); + +struct mrvlietypes_rssithreshold { + struct mrvlietypesheader header; + u8 rssivalue; + u8 rssifreq; +} __attribute__ ((packed)); + +struct mrvlietypes_snrthreshold { + struct mrvlietypesheader header; + u8 snrvalue; + u8 snrfreq; +} __attribute__ ((packed)); + +struct mrvlietypes_failurecount { + struct mrvlietypesheader header; + u8 failvalue; + u8 Failfreq; +} __attribute__ ((packed)); + +struct mrvlietypes_beaconsmissed { + struct mrvlietypesheader header; + u8 beaconmissed; + u8 reserved; +} __attribute__ ((packed)); + +struct mrvlietypes_numprobes { + struct mrvlietypesheader header; + u16 numprobes; +} __attribute__ ((packed)); + +struct mrvlietypes_bcastprobe { + struct mrvlietypesheader header; + u16 bcastprobe; +} __attribute__ ((packed)); + +struct mrvlietypes_numssidprobe { + struct mrvlietypesheader header; + u16 numssidprobe; +} __attribute__ ((packed)); + +struct led_pin { + u8 led; + u8 pin; +} __attribute__ ((packed)); + +struct mrvlietypes_ledgpio { + struct mrvlietypesheader header; + struct led_pin ledpin[1]; +} __attribute__ ((packed)); + +#endif /* _WLAN_TYPES_ */ diff --git a/drivers/net/wireless/libertas/version.h b/drivers/net/wireless/libertas/version.h new file mode 100644 index 00000000000..e86f65ae79b --- /dev/null +++ b/drivers/net/wireless/libertas/version.h @@ -0,0 +1,8 @@ +#define DRIVER_RELEASE_VERSION "320.p0" +const char libertas_driver_version[] = "COMM-USB8388-" DRIVER_RELEASE_VERSION +#ifdef DEBUG + "-dbg" +#endif + ""; + + diff --git a/drivers/net/wireless/libertas/wext.c b/drivers/net/wireless/libertas/wext.c new file mode 100644 index 00000000000..4a52336bc0f --- /dev/null +++ b/drivers/net/wireless/libertas/wext.c @@ -0,0 +1,2769 @@ +/** + * This file contains ioctl functions + */ +#include <linux/ctype.h> +#include <linux/delay.h> +#include <linux/if.h> +#include <linux/if_arp.h> +#include <linux/wireless.h> +#include <linux/bitops.h> + +#include <net/ieee80211.h> +#include <net/iw_handler.h> + +#include "host.h" +#include "radiotap.h" +#include "decl.h" +#include "defs.h" +#include "dev.h" +#include "join.h" +#include "version.h" +#include "wext.h" +#include "assoc.h" + + +/** + * @brief Convert mw value to dbm value + * + * @param mw the value of mw + * @return the value of dbm + */ +static int mw_to_dbm(int mw) +{ + if (mw < 2) + return 0; + else if (mw < 3) + return 3; + else if (mw < 4) + return 5; + else if (mw < 6) + return 7; + else if (mw < 7) + return 8; + else if (mw < 8) + return 9; + else if (mw < 10) + return 10; + else if (mw < 13) + return 11; + else if (mw < 16) + return 12; + else if (mw < 20) + return 13; + else if (mw < 25) + return 14; + else if (mw < 32) + return 15; + else if (mw < 40) + return 16; + else if (mw < 50) + return 17; + else if (mw < 63) + return 18; + else if (mw < 79) + return 19; + else if (mw < 100) + return 20; + else + return 21; +} + +/** + * @brief Find the channel frequency power info with specific channel + * + * @param adapter A pointer to wlan_adapter structure + * @param band it can be BAND_A, BAND_G or BAND_B + * @param channel the channel for looking + * @return A pointer to struct chan_freq_power structure or NULL if not find. + */ +struct chan_freq_power *libertas_find_cfp_by_band_and_channel(wlan_adapter * adapter, + u8 band, u16 channel) +{ + struct chan_freq_power *cfp = NULL; + struct region_channel *rc; + int count = sizeof(adapter->region_channel) / + sizeof(adapter->region_channel[0]); + int i, j; + + for (j = 0; !cfp && (j < count); j++) { + rc = &adapter->region_channel[j]; + + if (adapter->enable11d) + rc = &adapter->universal_channel[j]; + if (!rc->valid || !rc->CFP) + continue; + if (rc->band != band) + continue; + for (i = 0; i < rc->nrcfp; i++) { + if (rc->CFP[i].channel == channel) { + cfp = &rc->CFP[i]; + break; + } + } + } + + if (!cfp && channel) + lbs_pr_debug(1, "libertas_find_cfp_by_band_and_channel(): cannot find " + "cfp by band %d & channel %d\n", band, channel); + + return cfp; +} + +/** + * @brief Find the channel frequency power info with specific frequency + * + * @param adapter A pointer to wlan_adapter structure + * @param band it can be BAND_A, BAND_G or BAND_B + * @param freq the frequency for looking + * @return A pointer to struct chan_freq_power structure or NULL if not find. + */ +static struct chan_freq_power *find_cfp_by_band_and_freq(wlan_adapter * adapter, + u8 band, u32 freq) +{ + struct chan_freq_power *cfp = NULL; + struct region_channel *rc; + int count = sizeof(adapter->region_channel) / + sizeof(adapter->region_channel[0]); + int i, j; + + for (j = 0; !cfp && (j < count); j++) { + rc = &adapter->region_channel[j]; + + if (adapter->enable11d) + rc = &adapter->universal_channel[j]; + if (!rc->valid || !rc->CFP) + continue; + if (rc->band != band) + continue; + for (i = 0; i < rc->nrcfp; i++) { + if (rc->CFP[i].freq == freq) { + cfp = &rc->CFP[i]; + break; + } + } + } + + if (!cfp && freq) + lbs_pr_debug(1, "find_cfp_by_band_and_freql(): cannot find cfp by " + "band %d & freq %d\n", band, freq); + + return cfp; +} + +static int updatecurrentchannel(wlan_private * priv) +{ + int ret; + + /* + ** the channel in f/w could be out of sync, get the current channel + */ + ret = libertas_prepare_and_send_command(priv, cmd_802_11_rf_channel, + cmd_opt_802_11_rf_channel_get, + cmd_option_waitforrsp, 0, NULL); + + lbs_pr_debug(1, "Current channel = %d\n", + priv->adapter->curbssparams.channel); + + return ret; +} + +static int setcurrentchannel(wlan_private * priv, int channel) +{ + lbs_pr_debug(1, "Set channel = %d\n", channel); + + /* + ** Current channel is not set to adhocchannel requested, set channel + */ + return (libertas_prepare_and_send_command(priv, cmd_802_11_rf_channel, + cmd_opt_802_11_rf_channel_set, + cmd_option_waitforrsp, 0, &channel)); +} + +static int changeadhocchannel(wlan_private * priv, int channel) +{ + int ret = 0; + wlan_adapter *adapter = priv->adapter; + + adapter->adhocchannel = channel; + + updatecurrentchannel(priv); + + if (adapter->curbssparams.channel == adapter->adhocchannel) { + /* adhocchannel is set to the current channel already */ + LEAVE(); + return 0; + } + + lbs_pr_debug(1, "Updating channel from %d to %d\n", + adapter->curbssparams.channel, adapter->adhocchannel); + + setcurrentchannel(priv, adapter->adhocchannel); + + updatecurrentchannel(priv); + + if (adapter->curbssparams.channel != adapter->adhocchannel) { + lbs_pr_debug(1, "failed to updated channel to %d, channel = %d\n", + adapter->adhocchannel, adapter->curbssparams.channel); + LEAVE(); + return -1; + } + + if (adapter->connect_status == libertas_connected) { + int i; + struct WLAN_802_11_SSID curadhocssid; + + lbs_pr_debug(1, "channel Changed while in an IBSS\n"); + + /* Copy the current ssid */ + memcpy(&curadhocssid, &adapter->curbssparams.ssid, + sizeof(struct WLAN_802_11_SSID)); + + /* Exit Adhoc mode */ + lbs_pr_debug(1, "In changeadhocchannel(): Sending Adhoc Stop\n"); + ret = libertas_stop_adhoc_network(priv); + + if (ret) { + LEAVE(); + return ret; + } + /* Scan for the network, do not save previous results. Stale + * scan data will cause us to join a non-existant adhoc network + */ + libertas_send_specific_SSID_scan(priv, &curadhocssid, 0); + + // find out the BSSID that matches the current SSID + i = libertas_find_SSID_in_list(adapter, &curadhocssid, NULL, + wlan802_11ibss); + + if (i >= 0) { + lbs_pr_debug(1, "SSID found at %d in List," + "so join\n", i); + libertas_join_adhoc_network(priv, &adapter->scantable[i]); + } else { + // else send START command + lbs_pr_debug(1, "SSID not found in list, " + "so creating adhoc with ssid = %s\n", + curadhocssid.ssid); + libertas_start_adhoc_network(priv, &curadhocssid); + } // end of else (START command) + } + + LEAVE(); + return 0; +} + +/** + * @brief Set Radio On/OFF + * + * @param priv A pointer to wlan_private structure + * @option Radio Option + * @return 0 --success, otherwise fail + */ +int wlan_radio_ioctl(wlan_private * priv, u8 option) +{ + int ret = 0; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + if (adapter->radioon != option) { + lbs_pr_debug(1, "Switching %s the Radio\n", option ? "On" : "Off"); + adapter->radioon = option; + + ret = libertas_prepare_and_send_command(priv, + cmd_802_11_radio_control, + cmd_act_set, + cmd_option_waitforrsp, 0, NULL); + } + + LEAVE(); + return ret; +} + +/** + * @brief Copy rates + * + * @param dest A pointer to Dest Buf + * @param src A pointer to Src Buf + * @param len The len of Src Buf + * @return Number of rates copyed + */ +static inline int copyrates(u8 * dest, int pos, u8 * src, int len) +{ + int i; + + for (i = 0; i < len && src[i]; i++, pos++) { + if (pos >= sizeof(u8) * WLAN_SUPPORTED_RATES) + break; + dest[pos] = src[i]; + } + + return pos; +} + +/** + * @brief Get active data rates + * + * @param adapter A pointer to wlan_adapter structure + * @param rate The buf to return the active rates + * @return The number of rates + */ +static int get_active_data_rates(wlan_adapter * adapter, + u8* rates) +{ + int k = 0; + + ENTER(); + + if (adapter->connect_status != libertas_connected) { + if (adapter->inframode == wlan802_11infrastructure) { + //Infra. mode + lbs_pr_debug(1, "Infra\n"); + k = copyrates(rates, k, libertas_supported_rates, + sizeof(libertas_supported_rates)); + } else { + //ad-hoc mode + lbs_pr_debug(1, "Adhoc G\n"); + k = copyrates(rates, k, libertas_adhoc_rates_g, + sizeof(libertas_adhoc_rates_g)); + } + } else { + k = copyrates(rates, 0, adapter->curbssparams.datarates, + adapter->curbssparams.numofrates); + } + + LEAVE(); + + return k; +} + +static int wlan_get_name(struct net_device *dev, struct iw_request_info *info, + char *cwrq, char *extra) +{ + const char *cp; + char comm[6] = { "COMM-" }; + char mrvl[6] = { "MRVL-" }; + int cnt; + + ENTER(); + + strcpy(cwrq, mrvl); + + cp = strstr(libertas_driver_version, comm); + if (cp == libertas_driver_version) //skip leading "COMM-" + cp = libertas_driver_version + strlen(comm); + else + cp = libertas_driver_version; + + cnt = strlen(mrvl); + cwrq += cnt; + while (cnt < 16 && (*cp != '-')) { + *cwrq++ = toupper(*cp++); + cnt++; + } + *cwrq = '\0'; + + LEAVE(); + + return 0; +} + +static int wlan_get_freq(struct net_device *dev, struct iw_request_info *info, + struct iw_freq *fwrq, char *extra) +{ + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + struct chan_freq_power *cfp; + + ENTER(); + + cfp = libertas_find_cfp_by_band_and_channel(adapter, 0, + adapter->curbssparams.channel); + + if (!cfp) { + if (adapter->curbssparams.channel) + lbs_pr_debug(1, "Invalid channel=%d\n", + adapter->curbssparams.channel); + return -EINVAL; + } + + fwrq->m = (long)cfp->freq * 100000; + fwrq->e = 1; + + lbs_pr_debug(1, "freq=%u\n", fwrq->m); + + LEAVE(); + return 0; +} + +static int wlan_get_wap(struct net_device *dev, struct iw_request_info *info, + struct sockaddr *awrq, char *extra) +{ + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + if (adapter->connect_status == libertas_connected) { + memcpy(awrq->sa_data, adapter->curbssparams.bssid, ETH_ALEN); + } else { + memset(awrq->sa_data, 0, ETH_ALEN); + } + awrq->sa_family = ARPHRD_ETHER; + + LEAVE(); + return 0; +} + +static int wlan_set_nick(struct net_device *dev, struct iw_request_info *info, + struct iw_point *dwrq, char *extra) +{ + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + /* + * Check the size of the string + */ + + if (dwrq->length > 16) { + return -E2BIG; + } + + mutex_lock(&adapter->lock); + memset(adapter->nodename, 0, sizeof(adapter->nodename)); + memcpy(adapter->nodename, extra, dwrq->length); + mutex_unlock(&adapter->lock); + + LEAVE(); + return 0; +} + +static int wlan_get_nick(struct net_device *dev, struct iw_request_info *info, + struct iw_point *dwrq, char *extra) +{ + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + /* + * Get the Nick Name saved + */ + + mutex_lock(&adapter->lock); + strncpy(extra, adapter->nodename, 16); + mutex_unlock(&adapter->lock); + + extra[16] = '\0'; + + /* + * If none, we may want to get the one that was set + */ + + /* + * Push it out ! + */ + dwrq->length = strlen(extra) + 1; + + LEAVE(); + return 0; +} + +static int wlan_set_rts(struct net_device *dev, struct iw_request_info *info, + struct iw_param *vwrq, char *extra) +{ + int ret = 0; + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + int rthr = vwrq->value; + + ENTER(); + + if (vwrq->disabled) { + adapter->rtsthsd = rthr = MRVDRV_RTS_MAX_VALUE; + } else { + if (rthr < MRVDRV_RTS_MIN_VALUE || rthr > MRVDRV_RTS_MAX_VALUE) + return -EINVAL; + adapter->rtsthsd = rthr; + } + + ret = libertas_prepare_and_send_command(priv, cmd_802_11_snmp_mib, + cmd_act_set, cmd_option_waitforrsp, + OID_802_11_RTS_THRESHOLD, &rthr); + + LEAVE(); + return ret; +} + +static int wlan_get_rts(struct net_device *dev, struct iw_request_info *info, + struct iw_param *vwrq, char *extra) +{ + int ret = 0; + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + adapter->rtsthsd = 0; + ret = libertas_prepare_and_send_command(priv, cmd_802_11_snmp_mib, + cmd_act_get, cmd_option_waitforrsp, + OID_802_11_RTS_THRESHOLD, NULL); + if (ret) { + LEAVE(); + return ret; + } + + vwrq->value = adapter->rtsthsd; + vwrq->disabled = ((vwrq->value < MRVDRV_RTS_MIN_VALUE) + || (vwrq->value > MRVDRV_RTS_MAX_VALUE)); + vwrq->fixed = 1; + + LEAVE(); + return 0; +} + +static int wlan_set_frag(struct net_device *dev, struct iw_request_info *info, + struct iw_param *vwrq, char *extra) +{ + int ret = 0; + int fthr = vwrq->value; + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + if (vwrq->disabled) { + adapter->fragthsd = fthr = MRVDRV_FRAG_MAX_VALUE; + } else { + if (fthr < MRVDRV_FRAG_MIN_VALUE + || fthr > MRVDRV_FRAG_MAX_VALUE) + return -EINVAL; + adapter->fragthsd = fthr; + } + + ret = libertas_prepare_and_send_command(priv, cmd_802_11_snmp_mib, + cmd_act_set, cmd_option_waitforrsp, + OID_802_11_FRAGMENTATION_THRESHOLD, &fthr); + LEAVE(); + return ret; +} + +static int wlan_get_frag(struct net_device *dev, struct iw_request_info *info, + struct iw_param *vwrq, char *extra) +{ + int ret = 0; + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + adapter->fragthsd = 0; + ret = libertas_prepare_and_send_command(priv, + cmd_802_11_snmp_mib, + cmd_act_get, cmd_option_waitforrsp, + OID_802_11_FRAGMENTATION_THRESHOLD, NULL); + if (ret) { + LEAVE(); + return ret; + } + + vwrq->value = adapter->fragthsd; + vwrq->disabled = ((vwrq->value < MRVDRV_FRAG_MIN_VALUE) + || (vwrq->value > MRVDRV_FRAG_MAX_VALUE)); + vwrq->fixed = 1; + + LEAVE(); + return ret; +} + +static int wlan_get_mode(struct net_device *dev, + struct iw_request_info *info, u32 * uwrq, char *extra) +{ + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + switch (adapter->inframode) { + case wlan802_11ibss: + *uwrq = IW_MODE_ADHOC; + break; + + case wlan802_11infrastructure: + *uwrq = IW_MODE_INFRA; + break; + + default: + case wlan802_11autounknown: + *uwrq = IW_MODE_AUTO; + break; + } + + LEAVE(); + return 0; +} + +static int wlan_get_txpow(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, char *extra) +{ + int ret = 0; + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + ret = libertas_prepare_and_send_command(priv, + cmd_802_11_rf_tx_power, + cmd_act_tx_power_opt_get, + cmd_option_waitforrsp, 0, NULL); + + if (ret) { + LEAVE(); + return ret; + } + + lbs_pr_debug(1, "TXPOWER GET %d dbm.\n", adapter->txpowerlevel); + vwrq->value = adapter->txpowerlevel; + vwrq->fixed = 1; + if (adapter->radioon) { + vwrq->disabled = 0; + vwrq->flags = IW_TXPOW_DBM; + } else { + vwrq->disabled = 1; + } + + LEAVE(); + return 0; +} + +static int wlan_set_retry(struct net_device *dev, struct iw_request_info *info, + struct iw_param *vwrq, char *extra) +{ + int ret = 0; + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + if (vwrq->flags == IW_RETRY_LIMIT) { + /* The MAC has a 4-bit Total_Tx_Count register + Total_Tx_Count = 1 + Tx_Retry_Count */ +#define TX_RETRY_MIN 0 +#define TX_RETRY_MAX 14 + if (vwrq->value < TX_RETRY_MIN || vwrq->value > TX_RETRY_MAX) + return -EINVAL; + + /* Adding 1 to convert retry count to try count */ + adapter->txretrycount = vwrq->value + 1; + + ret = libertas_prepare_and_send_command(priv, cmd_802_11_snmp_mib, + cmd_act_set, + cmd_option_waitforrsp, + OID_802_11_TX_RETRYCOUNT, NULL); + + if (ret) { + LEAVE(); + return ret; + } + } else { + return -EOPNOTSUPP; + } + + LEAVE(); + return 0; +} + +static int wlan_get_retry(struct net_device *dev, struct iw_request_info *info, + struct iw_param *vwrq, char *extra) +{ + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + int ret = 0; + + ENTER(); + adapter->txretrycount = 0; + ret = libertas_prepare_and_send_command(priv, + cmd_802_11_snmp_mib, + cmd_act_get, cmd_option_waitforrsp, + OID_802_11_TX_RETRYCOUNT, NULL); + if (ret) { + LEAVE(); + return ret; + } + vwrq->disabled = 0; + if (!vwrq->flags) { + vwrq->flags = IW_RETRY_LIMIT; + /* Subtract 1 to convert try count to retry count */ + vwrq->value = adapter->txretrycount - 1; + } + + LEAVE(); + return 0; +} + +static inline void sort_channels(struct iw_freq *freq, int num) +{ + int i, j; + struct iw_freq temp; + + for (i = 0; i < num; i++) + for (j = i + 1; j < num; j++) + if (freq[i].i > freq[j].i) { + temp.i = freq[i].i; + temp.m = freq[i].m; + + freq[i].i = freq[j].i; + freq[i].m = freq[j].m; + + freq[j].i = temp.i; + freq[j].m = temp.m; + } +} + +/* data rate listing + MULTI_BANDS: + abg a b b/g + Infra G(12) A(8) B(4) G(12) + Adhoc A+B(12) A(8) B(4) B(4) + + non-MULTI_BANDS: + b b/g + Infra B(4) G(12) + Adhoc B(4) B(4) + */ +/** + * @brief Get Range Info + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param vwrq A pointer to iw_param structure + * @param extra A pointer to extra data buf + * @return 0 --success, otherwise fail + */ +static int wlan_get_range(struct net_device *dev, struct iw_request_info *info, + struct iw_point *dwrq, char *extra) +{ + int i, j; + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + struct iw_range *range = (struct iw_range *)extra; + struct chan_freq_power *cfp; + u8 rates[WLAN_SUPPORTED_RATES]; + + u8 flag = 0; + + ENTER(); + + dwrq->length = sizeof(struct iw_range); + memset(range, 0, sizeof(struct iw_range)); + + range->min_nwid = 0; + range->max_nwid = 0; + + memset(rates, 0, sizeof(rates)); + range->num_bitrates = get_active_data_rates(adapter, rates); + + for (i = 0; i < min_t(__u8, range->num_bitrates, IW_MAX_BITRATES) && rates[i]; + i++) { + range->bitrate[i] = (rates[i] & 0x7f) * 500000; + } + range->num_bitrates = i; + lbs_pr_debug(1, "IW_MAX_BITRATES=%d num_bitrates=%d\n", IW_MAX_BITRATES, + range->num_bitrates); + + range->num_frequency = 0; + if (priv->adapter->enable11d && + adapter->connect_status == libertas_connected) { + u8 chan_no; + u8 band; + + struct parsed_region_chan_11d *parsed_region_chan = + &adapter->parsed_region_chan; + + if (parsed_region_chan == NULL) { + lbs_pr_debug(1, "11D:parsed_region_chan is NULL\n"); + LEAVE(); + return 0; + } + band = parsed_region_chan->band; + lbs_pr_debug(1, "band=%d NoOfChan=%d\n", band, + parsed_region_chan->nr_chan); + + for (i = 0; (range->num_frequency < IW_MAX_FREQUENCIES) + && (i < parsed_region_chan->nr_chan); i++) { + chan_no = parsed_region_chan->chanpwr[i].chan; + lbs_pr_debug(1, "chan_no=%d\n", chan_no); + range->freq[range->num_frequency].i = (long)chan_no; + range->freq[range->num_frequency].m = + (long)libertas_chan_2_freq(chan_no, band) * 100000; + range->freq[range->num_frequency].e = 1; + range->num_frequency++; + } + flag = 1; + } + if (!flag) { + for (j = 0; (range->num_frequency < IW_MAX_FREQUENCIES) + && (j < sizeof(adapter->region_channel) + / sizeof(adapter->region_channel[0])); j++) { + cfp = adapter->region_channel[j].CFP; + for (i = 0; (range->num_frequency < IW_MAX_FREQUENCIES) + && adapter->region_channel[j].valid + && cfp + && (i < adapter->region_channel[j].nrcfp); i++) { + range->freq[range->num_frequency].i = + (long)cfp->channel; + range->freq[range->num_frequency].m = + (long)cfp->freq * 100000; + range->freq[range->num_frequency].e = 1; + cfp++; + range->num_frequency++; + } + } + } + + lbs_pr_debug(1, "IW_MAX_FREQUENCIES=%d num_frequency=%d\n", + IW_MAX_FREQUENCIES, range->num_frequency); + + range->num_channels = range->num_frequency; + + sort_channels(&range->freq[0], range->num_frequency); + + /* + * Set an indication of the max TCP throughput in bit/s that we can + * expect using this interface + */ + if (i > 2) + range->throughput = 5000 * 1000; + else + range->throughput = 1500 * 1000; + + range->min_rts = MRVDRV_RTS_MIN_VALUE; + range->max_rts = MRVDRV_RTS_MAX_VALUE; + range->min_frag = MRVDRV_FRAG_MIN_VALUE; + range->max_frag = MRVDRV_FRAG_MAX_VALUE; + + range->encoding_size[0] = 5; + range->encoding_size[1] = 13; + range->num_encoding_sizes = 2; + range->max_encoding_tokens = 4; + + range->min_pmp = 1000000; + range->max_pmp = 120000000; + range->min_pmt = 1000; + range->max_pmt = 1000000; + range->pmp_flags = IW_POWER_PERIOD; + range->pmt_flags = IW_POWER_TIMEOUT; + range->pm_capa = IW_POWER_PERIOD | IW_POWER_TIMEOUT | IW_POWER_ALL_R; + + /* + * Minimum version we recommend + */ + range->we_version_source = 15; + + /* + * Version we are compiled with + */ + range->we_version_compiled = WIRELESS_EXT; + + range->retry_capa = IW_RETRY_LIMIT; + range->retry_flags = IW_RETRY_LIMIT | IW_RETRY_MAX; + + range->min_retry = TX_RETRY_MIN; + range->max_retry = TX_RETRY_MAX; + + /* + * Set the qual, level and noise range values + */ + range->max_qual.qual = 100; + range->max_qual.level = 0; + range->max_qual.noise = 0; + range->max_qual.updated = IW_QUAL_ALL_UPDATED | IW_QUAL_DBM; + + range->avg_qual.qual = 70; + /* TODO: Find real 'good' to 'bad' threshold value for RSSI */ + range->avg_qual.level = 0; + range->avg_qual.noise = 0; + range->avg_qual.updated = IW_QUAL_ALL_UPDATED | IW_QUAL_DBM; + + range->sensitivity = 0; + + /* + * Setup the supported power level ranges + */ + memset(range->txpower, 0, sizeof(range->txpower)); + range->txpower[0] = 5; + range->txpower[1] = 7; + range->txpower[2] = 9; + range->txpower[3] = 11; + range->txpower[4] = 13; + range->txpower[5] = 15; + range->txpower[6] = 17; + range->txpower[7] = 19; + + range->num_txpower = 8; + range->txpower_capa = IW_TXPOW_DBM; + range->txpower_capa |= IW_TXPOW_RANGE; + + range->event_capa[0] = (IW_EVENT_CAPA_K_0 | + IW_EVENT_CAPA_MASK(SIOCGIWAP) | + IW_EVENT_CAPA_MASK(SIOCGIWSCAN)); + range->event_capa[1] = IW_EVENT_CAPA_K_1; + + if (adapter->fwcapinfo & FW_CAPINFO_WPA) { + range->enc_capa = IW_ENC_CAPA_WPA + | IW_ENC_CAPA_WPA2 + | IW_ENC_CAPA_CIPHER_TKIP + | IW_ENC_CAPA_CIPHER_CCMP; + } + + LEAVE(); + return 0; +} + +static int wlan_set_power(struct net_device *dev, struct iw_request_info *info, + struct iw_param *vwrq, char *extra) +{ + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + /* PS is currently supported only in Infrastructure mode + * Remove this check if it is to be supported in IBSS mode also + */ + + if (vwrq->disabled) { + adapter->psmode = wlan802_11powermodecam; + if (adapter->psstate != PS_STATE_FULL_POWER) { + libertas_ps_wakeup(priv, cmd_option_waitforrsp); + } + + return 0; + } + + if ((vwrq->flags & IW_POWER_TYPE) == IW_POWER_TIMEOUT) { + lbs_pr_debug(1, + "Setting power timeout command is not supported\n"); + return -EINVAL; + } else if ((vwrq->flags & IW_POWER_TYPE) == IW_POWER_PERIOD) { + lbs_pr_debug(1, "Setting power period command is not supported\n"); + return -EINVAL; + } + + if (adapter->psmode != wlan802_11powermodecam) { + return 0; + } + + adapter->psmode = wlan802_11powermodemax_psp; + + if (adapter->connect_status == libertas_connected) { + libertas_ps_sleep(priv, cmd_option_waitforrsp); + } + + LEAVE(); + return 0; +} + +static int wlan_get_power(struct net_device *dev, struct iw_request_info *info, + struct iw_param *vwrq, char *extra) +{ + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + int mode; + + ENTER(); + + mode = adapter->psmode; + + if ((vwrq->disabled = (mode == wlan802_11powermodecam)) + || adapter->connect_status == libertas_disconnected) { + LEAVE(); + return 0; + } + + vwrq->value = 0; + + LEAVE(); + return 0; +} + +/* + * iwpriv settable callbacks + */ + +static const iw_handler wlan_private_handler[] = { + NULL, /* SIOCIWFIRSTPRIV */ +}; + +static const struct iw_priv_args wlan_private_args[] = { + /* + * { cmd, set_args, get_args, name } + */ + { + WLANSCAN_TYPE, + IW_PRIV_TYPE_CHAR | 8, + IW_PRIV_TYPE_CHAR | 8, + "scantype"}, + + { + WLAN_SETINT_GETINT, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + ""}, + { + WLANNF, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "getNF"}, + { + WLANRSSI, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "getRSSI"}, + { + WLANENABLE11D, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "enable11d"}, + { + WLANADHOCGRATE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "adhocgrate"}, + + { + WLAN_SUBCMD_SET_PRESCAN, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "prescan"}, + { + WLAN_SETONEINT_GETONEINT, + IW_PRIV_TYPE_INT | 1, + IW_PRIV_TYPE_INT | 1, + ""}, + { + WLAN_BEACON_INTERVAL, + IW_PRIV_TYPE_INT | 1, + IW_PRIV_TYPE_INT | 1, + "bcninterval"}, + { + WLAN_LISTENINTRVL, + IW_PRIV_TYPE_INT | 1, + IW_PRIV_TYPE_INT | 1, + "lolisteninter"}, + { + WLAN_TXCONTROL, + IW_PRIV_TYPE_INT | 1, + IW_PRIV_TYPE_INT | 1, + "txcontrol"}, + { + WLAN_NULLPKTINTERVAL, + IW_PRIV_TYPE_INT | 1, + IW_PRIV_TYPE_INT | 1, + "psnullinterval"}, + /* Using iwpriv sub-command feature */ + { + WLAN_SETONEINT_GETNONE, /* IOCTL: 24 */ + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + IW_PRIV_TYPE_NONE, + ""}, + + { + WLAN_SUBCMD_SETRXANTENNA, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + IW_PRIV_TYPE_NONE, + "setrxant"}, + { + WLAN_SUBCMD_SETTXANTENNA, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + IW_PRIV_TYPE_NONE, + "settxant"}, + { + WLANSETAUTHALG, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + IW_PRIV_TYPE_NONE, + "authalgs", + }, + { + WLANSET8021XAUTHALG, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + IW_PRIV_TYPE_NONE, + "8021xauthalgs", + }, + { + WLANSETENCRYPTIONMODE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + IW_PRIV_TYPE_NONE, + "encryptionmode", + }, + { + WLANSETREGION, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + IW_PRIV_TYPE_NONE, + "setregioncode"}, + { + WLAN_SET_LISTEN_INTERVAL, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + IW_PRIV_TYPE_NONE, + "setlisteninter"}, + { + WLAN_SET_MULTIPLE_DTIM, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + IW_PRIV_TYPE_NONE, + "setmultipledtim"}, + { + WLAN_SET_ATIM_WINDOW, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + IW_PRIV_TYPE_NONE, + "atimwindow"}, + { + WLANSETBCNAVG, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + IW_PRIV_TYPE_NONE, + "setbcnavg"}, + { + WLANSETDATAAVG, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + IW_PRIV_TYPE_NONE, + "setdataavg"}, + { + WLAN_SET_LINKMODE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + IW_PRIV_TYPE_NONE, + "linkmode"}, + { + WLAN_SET_RADIOMODE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + IW_PRIV_TYPE_NONE, + "radiomode"}, + { + WLAN_SET_DEBUGMODE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + IW_PRIV_TYPE_NONE, + "debugmode"}, + { + WLAN_SUBCMD_MESH_SET_TTL, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + IW_PRIV_TYPE_NONE, + "mesh_set_ttl"}, + { + WLAN_SETNONE_GETONEINT, + IW_PRIV_TYPE_NONE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + ""}, + { + WLANGETREGION, + IW_PRIV_TYPE_NONE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "getregioncode"}, + { + WLAN_GET_LISTEN_INTERVAL, + IW_PRIV_TYPE_NONE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "getlisteninter"}, + { + WLAN_GET_MULTIPLE_DTIM, + IW_PRIV_TYPE_NONE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "getmultipledtim"}, + { + WLAN_GET_TX_RATE, + IW_PRIV_TYPE_NONE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "gettxrate"}, + { + WLANGETBCNAVG, + IW_PRIV_TYPE_NONE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "getbcnavg"}, + { + WLAN_GET_LINKMODE, + IW_PRIV_TYPE_NONE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "get_linkmode"}, + { + WLAN_GET_RADIOMODE, + IW_PRIV_TYPE_NONE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "get_radiomode"}, + { + WLAN_GET_DEBUGMODE, + IW_PRIV_TYPE_NONE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "get_debugmode"}, + { + WLAN_SUBCMD_FWT_CLEANUP, + IW_PRIV_TYPE_NONE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "fwt_cleanup"}, + { + WLAN_SUBCMD_FWT_TIME, + IW_PRIV_TYPE_NONE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "fwt_time"}, + { + WLAN_SUBCMD_MESH_GET_TTL, + IW_PRIV_TYPE_NONE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "mesh_get_ttl"}, + { + WLAN_SETNONE_GETTWELVE_CHAR, + IW_PRIV_TYPE_NONE, + IW_PRIV_TYPE_CHAR | 12, + ""}, + { + WLAN_SUBCMD_GETRXANTENNA, + IW_PRIV_TYPE_NONE, + IW_PRIV_TYPE_CHAR | 12, + "getrxant"}, + { + WLAN_SUBCMD_GETTXANTENNA, + IW_PRIV_TYPE_NONE, + IW_PRIV_TYPE_CHAR | 12, + "gettxant"}, + { + WLAN_GET_TSF, + IW_PRIV_TYPE_NONE, + IW_PRIV_TYPE_CHAR | 12, + "gettsf"}, + { + WLAN_SETNONE_GETNONE, + IW_PRIV_TYPE_NONE, + IW_PRIV_TYPE_NONE, + ""}, + { + WLANDEAUTH, + IW_PRIV_TYPE_NONE, + IW_PRIV_TYPE_NONE, + "deauth"}, + { + WLANADHOCSTOP, + IW_PRIV_TYPE_NONE, + IW_PRIV_TYPE_NONE, + "adhocstop"}, + { + WLANRADIOON, + IW_PRIV_TYPE_NONE, + IW_PRIV_TYPE_NONE, + "radioon"}, + { + WLANRADIOOFF, + IW_PRIV_TYPE_NONE, + IW_PRIV_TYPE_NONE, + "radiooff"}, + { + WLANWLANIDLEON, + IW_PRIV_TYPE_NONE, + IW_PRIV_TYPE_NONE, + "wlanidle-on"}, + { + WLANWLANIDLEOFF, + IW_PRIV_TYPE_NONE, + IW_PRIV_TYPE_NONE, + "wlanidle-off"}, + { + WLAN_SUBCMD_FWT_RESET, + IW_PRIV_TYPE_NONE, + IW_PRIV_TYPE_NONE, + "fwt_reset"}, + { + WLAN_SUBCMD_BT_RESET, + IW_PRIV_TYPE_NONE, + IW_PRIV_TYPE_NONE, + "bt_reset"}, + { + WLAN_SET128CHAR_GET128CHAR, + IW_PRIV_TYPE_CHAR | 128, + IW_PRIV_TYPE_CHAR | 128, + ""}, + /* BT Management */ + { + WLAN_SUBCMD_BT_ADD, + IW_PRIV_TYPE_CHAR | 128, + IW_PRIV_TYPE_CHAR | 128, + "bt_add"}, + { + WLAN_SUBCMD_BT_DEL, + IW_PRIV_TYPE_CHAR | 128, + IW_PRIV_TYPE_CHAR | 128, + "bt_del"}, + { + WLAN_SUBCMD_BT_LIST, + IW_PRIV_TYPE_CHAR | 128, + IW_PRIV_TYPE_CHAR | 128, + "bt_list"}, + /* FWT Management */ + { + WLAN_SUBCMD_FWT_ADD, + IW_PRIV_TYPE_CHAR | 128, + IW_PRIV_TYPE_CHAR | 128, + "fwt_add"}, + { + WLAN_SUBCMD_FWT_DEL, + IW_PRIV_TYPE_CHAR | 128, + IW_PRIV_TYPE_CHAR | 128, + "fwt_del"}, + { + WLAN_SUBCMD_FWT_LOOKUP, + IW_PRIV_TYPE_CHAR | 128, + IW_PRIV_TYPE_CHAR | 128, + "fwt_lookup"}, + { + WLAN_SUBCMD_FWT_LIST_NEIGHBOR, + IW_PRIV_TYPE_CHAR | 128, + IW_PRIV_TYPE_CHAR | 128, + "fwt_list_neigh"}, + { + WLAN_SUBCMD_FWT_LIST, + IW_PRIV_TYPE_CHAR | 128, + IW_PRIV_TYPE_CHAR | 128, + "fwt_list"}, + { + WLAN_SUBCMD_FWT_LIST_ROUTE, + IW_PRIV_TYPE_CHAR | 128, + IW_PRIV_TYPE_CHAR | 128, + "fwt_list_route"}, + { + WLANSCAN_MODE, + IW_PRIV_TYPE_CHAR | 128, + IW_PRIV_TYPE_CHAR | 128, + "scanmode"}, + { + WLAN_GET_ADHOC_STATUS, + IW_PRIV_TYPE_CHAR | 128, + IW_PRIV_TYPE_CHAR | 128, + "getadhocstatus"}, + { + WLAN_SETNONE_GETWORDCHAR, + IW_PRIV_TYPE_NONE, + IW_PRIV_TYPE_CHAR | 128, + ""}, + { + WLANSETWPAIE, + IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | 24, + IW_PRIV_TYPE_NONE, + "setwpaie"}, + { + WLANGETLOG, + IW_PRIV_TYPE_NONE, + IW_PRIV_TYPE_CHAR | GETLOG_BUFSIZE, + "getlog"}, + { + WLAN_SET_GET_SIXTEEN_INT, + IW_PRIV_TYPE_INT | 16, + IW_PRIV_TYPE_INT | 16, + ""}, + { + WLAN_TPCCFG, + IW_PRIV_TYPE_INT | 16, + IW_PRIV_TYPE_INT | 16, + "tpccfg"}, + { + WLAN_POWERCFG, + IW_PRIV_TYPE_INT | 16, + IW_PRIV_TYPE_INT | 16, + "powercfg"}, + { + WLAN_AUTO_FREQ_SET, + IW_PRIV_TYPE_INT | 16, + IW_PRIV_TYPE_INT | 16, + "setafc"}, + { + WLAN_AUTO_FREQ_GET, + IW_PRIV_TYPE_INT | 16, + IW_PRIV_TYPE_INT | 16, + "getafc"}, + { + WLAN_SCANPROBES, + IW_PRIV_TYPE_INT | 16, + IW_PRIV_TYPE_INT | 16, + "scanprobes"}, + { + WLAN_LED_GPIO_CTRL, + IW_PRIV_TYPE_INT | 16, + IW_PRIV_TYPE_INT | 16, + "ledgpio"}, + { + WLAN_ADAPT_RATESET, + IW_PRIV_TYPE_INT | 16, + IW_PRIV_TYPE_INT | 16, + "rateadapt"}, + { + WLAN_INACTIVITY_TIMEOUT, + IW_PRIV_TYPE_INT | 16, + IW_PRIV_TYPE_INT | 16, + "inactivityto"}, + { + WLANSNR, + IW_PRIV_TYPE_INT | 16, + IW_PRIV_TYPE_INT | 16, + "getSNR"}, + { + WLAN_GET_RATE, + IW_PRIV_TYPE_INT | 16, + IW_PRIV_TYPE_INT | 16, + "getrate"}, + { + WLAN_GET_RXINFO, + IW_PRIV_TYPE_INT | 16, + IW_PRIV_TYPE_INT | 16, + "getrxinfo"}, +}; + +static struct iw_statistics *wlan_get_wireless_stats(struct net_device *dev) +{ + enum { + POOR = 30, + FAIR = 60, + GOOD = 80, + VERY_GOOD = 90, + EXCELLENT = 95, + PERFECT = 100 + }; + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + u32 rssi_qual; + u32 tx_qual; + u32 quality = 0; + int stats_valid = 0; + u8 rssi; + u32 tx_retries; + + ENTER(); + + priv->wstats.status = adapter->inframode; + + /* If we're not associated, all quality values are meaningless */ + if (adapter->connect_status != libertas_connected) + goto out; + + /* Quality by RSSI */ + priv->wstats.qual.level = + CAL_RSSI(adapter->SNR[TYPE_BEACON][TYPE_NOAVG], + adapter->NF[TYPE_BEACON][TYPE_NOAVG]); + + if (adapter->NF[TYPE_BEACON][TYPE_NOAVG] == 0) { + priv->wstats.qual.noise = MRVDRV_NF_DEFAULT_SCAN_VALUE; + } else { + priv->wstats.qual.noise = + CAL_NF(adapter->NF[TYPE_BEACON][TYPE_NOAVG]); + } + + lbs_pr_debug(1, "Signal Level = %#x\n", priv->wstats.qual.level); + lbs_pr_debug(1, "Noise = %#x\n", priv->wstats.qual.noise); + + rssi = priv->wstats.qual.level - priv->wstats.qual.noise; + if (rssi < 15) + rssi_qual = rssi * POOR / 10; + else if (rssi < 20) + rssi_qual = (rssi - 15) * (FAIR - POOR) / 5 + POOR; + else if (rssi < 30) + rssi_qual = (rssi - 20) * (GOOD - FAIR) / 5 + FAIR; + else if (rssi < 40) + rssi_qual = (rssi - 30) * (VERY_GOOD - GOOD) / + 10 + GOOD; + else + rssi_qual = (rssi - 40) * (PERFECT - VERY_GOOD) / + 10 + VERY_GOOD; + quality = rssi_qual; + + /* Quality by TX errors */ + priv->wstats.discard.retries = priv->stats.tx_errors; + + tx_retries = adapter->logmsg.retry; + + if (tx_retries > 75) + tx_qual = (90 - tx_retries) * POOR / 15; + else if (tx_retries > 70) + tx_qual = (75 - tx_retries) * (FAIR - POOR) / 5 + POOR; + else if (tx_retries > 65) + tx_qual = (70 - tx_retries) * (GOOD - FAIR) / 5 + FAIR; + else if (tx_retries > 50) + tx_qual = (65 - tx_retries) * (VERY_GOOD - GOOD) / + 15 + GOOD; + else + tx_qual = (50 - tx_retries) * + (PERFECT - VERY_GOOD) / 50 + VERY_GOOD; + quality = min(quality, tx_qual); + + priv->wstats.discard.code = adapter->logmsg.wepundecryptable; + priv->wstats.discard.fragment = adapter->logmsg.fcserror; + priv->wstats.discard.retries = tx_retries; + priv->wstats.discard.misc = adapter->logmsg.ackfailure; + + /* Calculate quality */ + priv->wstats.qual.qual = max(quality, (u32)100); + priv->wstats.qual.updated = IW_QUAL_ALL_UPDATED | IW_QUAL_DBM; + stats_valid = 1; + + /* update stats asynchronously for future calls */ + libertas_prepare_and_send_command(priv, cmd_802_11_rssi, 0, + 0, 0, NULL); + libertas_prepare_and_send_command(priv, cmd_802_11_get_log, 0, + 0, 0, NULL); +out: + if (!stats_valid) { + priv->wstats.miss.beacon = 0; + priv->wstats.discard.retries = 0; + priv->wstats.qual.qual = 0; + priv->wstats.qual.level = 0; + priv->wstats.qual.noise = 0; + priv->wstats.qual.updated = IW_QUAL_ALL_UPDATED; + priv->wstats.qual.updated |= IW_QUAL_NOISE_INVALID | + IW_QUAL_QUAL_INVALID | IW_QUAL_LEVEL_INVALID; + } + + LEAVE (); + return &priv->wstats; + + +} + +static int wlan_set_freq(struct net_device *dev, struct iw_request_info *info, + struct iw_freq *fwrq, char *extra) +{ + int ret = 0; + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + int rc = -EINPROGRESS; /* Call commit handler */ + struct chan_freq_power *cfp; + + ENTER(); + + /* + * If setting by frequency, convert to a channel + */ + if (fwrq->e == 1) { + + long f = fwrq->m / 100000; + int c = 0; + + cfp = find_cfp_by_band_and_freq(adapter, 0, f); + if (!cfp) { + lbs_pr_debug(1, "Invalid freq=%ld\n", f); + return -EINVAL; + } + + c = (int)cfp->channel; + + if (c < 0) + return -EINVAL; + + fwrq->e = 0; + fwrq->m = c; + } + + /* + * Setting by channel number + */ + if (fwrq->m > 1000 || fwrq->e > 0) { + rc = -EOPNOTSUPP; + } else { + int channel = fwrq->m; + + cfp = libertas_find_cfp_by_band_and_channel(adapter, 0, channel); + if (!cfp) { + rc = -EINVAL; + } else { + if (adapter->inframode == wlan802_11ibss) { + rc = changeadhocchannel(priv, channel); + /* If station is WEP enabled, send the + * command to set WEP in firmware + */ + if (adapter->secinfo.WEPstatus == + wlan802_11WEPenabled) { + lbs_pr_debug(1, "set_freq: WEP enabled\n"); + ret = libertas_prepare_and_send_command(priv, + cmd_802_11_set_wep, + cmd_act_add, + cmd_option_waitforrsp, + 0, + NULL); + + if (ret) { + LEAVE(); + return ret; + } + + adapter->currentpacketfilter |= + cmd_act_mac_wep_enable; + + libertas_set_mac_packet_filter(priv); + } + } else { + rc = -EOPNOTSUPP; + } + } + } + + LEAVE(); + return rc; +} + +/** + * @brief use index to get the data rate + * + * @param index The index of data rate + * @return data rate or 0 + */ +u32 libertas_index_to_data_rate(u8 index) +{ + if (index >= sizeof(libertas_wlan_data_rates)) + index = 0; + + return libertas_wlan_data_rates[index]; +} + +/** + * @brief use rate to get the index + * + * @param rate data rate + * @return index or 0 + */ +u8 libertas_data_rate_to_index(u32 rate) +{ + u8 *ptr; + + if (rate) + if ((ptr = memchr(libertas_wlan_data_rates, (u8) rate, + sizeof(libertas_wlan_data_rates)))) + return (ptr - libertas_wlan_data_rates); + + return 0; +} + +static int wlan_set_rate(struct net_device *dev, struct iw_request_info *info, + struct iw_param *vwrq, char *extra) +{ + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + u32 data_rate; + u16 action; + int ret = 0; + u8 rates[WLAN_SUPPORTED_RATES]; + u8 *rate; + + ENTER(); + + lbs_pr_debug(1, "Vwrq->value = %d\n", vwrq->value); + + if (vwrq->value == -1) { + action = cmd_act_set_tx_auto; // Auto + adapter->is_datarate_auto = 1; + adapter->datarate = 0; + } else { + if (vwrq->value % 100000) { + return -EINVAL; + } + + data_rate = vwrq->value / 500000; + + memset(rates, 0, sizeof(rates)); + get_active_data_rates(adapter, rates); + rate = rates; + while (*rate) { + lbs_pr_debug(1, "Rate=0x%X Wanted=0x%X\n", *rate, + data_rate); + if ((*rate & 0x7f) == (data_rate & 0x7f)) + break; + rate++; + } + if (!*rate) { + lbs_pr_alert( "The fixed data rate 0x%X is out " + "of range.\n", data_rate); + return -EINVAL; + } + + adapter->datarate = data_rate; + action = cmd_act_set_tx_fix_rate; + adapter->is_datarate_auto = 0; + } + + ret = libertas_prepare_and_send_command(priv, cmd_802_11_data_rate, + action, cmd_option_waitforrsp, 0, NULL); + + LEAVE(); + return ret; +} + +static int wlan_get_rate(struct net_device *dev, struct iw_request_info *info, + struct iw_param *vwrq, char *extra) +{ + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + if (adapter->is_datarate_auto) { + vwrq->fixed = 0; + } else { + vwrq->fixed = 1; + } + + vwrq->value = adapter->datarate * 500000; + + LEAVE(); + return 0; +} + +static int wlan_set_mode(struct net_device *dev, + struct iw_request_info *info, u32 * uwrq, char *extra) +{ + int ret = 0; + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + struct assoc_request * assoc_req; + enum WLAN_802_11_NETWORK_INFRASTRUCTURE new_mode; + + ENTER(); + + switch (*uwrq) { + case IW_MODE_ADHOC: + lbs_pr_debug(1, "Wanted mode is ad-hoc: current datarate=%#x\n", + adapter->datarate); + new_mode = wlan802_11ibss; + adapter->adhocchannel = DEFAULT_AD_HOC_CHANNEL; + break; + + case IW_MODE_INFRA: + lbs_pr_debug(1, "Wanted mode is Infrastructure\n"); + new_mode = wlan802_11infrastructure; + break; + + case IW_MODE_AUTO: + lbs_pr_debug(1, "Wanted mode is Auto\n"); + new_mode = wlan802_11autounknown; + break; + + default: + lbs_pr_debug(1, "Wanted mode is Unknown: 0x%x\n", *uwrq); + return -EINVAL; + } + + mutex_lock(&adapter->lock); + assoc_req = wlan_get_association_request(adapter); + if (!assoc_req) { + ret = -ENOMEM; + } else { + assoc_req->mode = new_mode; + } + + if (ret == 0) { + set_bit(ASSOC_FLAG_MODE, &assoc_req->flags); + wlan_postpone_association_work(priv); + } else { + wlan_cancel_association_work(priv); + } + mutex_unlock(&adapter->lock); + + LEAVE(); + return ret; +} + + +/** + * @brief Get Encryption key + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param vwrq A pointer to iw_param structure + * @param extra A pointer to extra data buf + * @return 0 --success, otherwise fail + */ +static int wlan_get_encode(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *dwrq, u8 * extra) +{ + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + int index = (dwrq->flags & IW_ENCODE_INDEX) - 1; + + ENTER(); + + lbs_pr_debug(1, "flags=0x%x index=%d length=%d wep_tx_keyidx=%d\n", + dwrq->flags, index, dwrq->length, adapter->wep_tx_keyidx); + + dwrq->flags = 0; + + /* Authentication method */ + switch (adapter->secinfo.authmode) { + case wlan802_11authmodeopen: + dwrq->flags = IW_ENCODE_OPEN; + break; + + case wlan802_11authmodeshared: + case wlan802_11authmodenetworkEAP: + dwrq->flags = IW_ENCODE_RESTRICTED; + break; + default: + dwrq->flags = IW_ENCODE_DISABLED | IW_ENCODE_OPEN; + break; + } + + if ((adapter->secinfo.WEPstatus == wlan802_11WEPenabled) + || adapter->secinfo.WPAenabled || adapter->secinfo.WPA2enabled) { + dwrq->flags &= ~IW_ENCODE_DISABLED; + } else { + dwrq->flags |= IW_ENCODE_DISABLED; + } + + memset(extra, 0, 16); + + mutex_lock(&adapter->lock); + + /* Default to returning current transmit key */ + if (index < 0) + index = adapter->wep_tx_keyidx; + + if ((adapter->wep_keys[index].len) && + (adapter->secinfo.WEPstatus == wlan802_11WEPenabled)) { + memcpy(extra, adapter->wep_keys[index].key, + adapter->wep_keys[index].len); + dwrq->length = adapter->wep_keys[index].len; + + dwrq->flags |= (index + 1); + /* Return WEP enabled */ + dwrq->flags &= ~IW_ENCODE_DISABLED; + } else if ((adapter->secinfo.WPAenabled) + || (adapter->secinfo.WPA2enabled)) { + /* return WPA enabled */ + dwrq->flags &= ~IW_ENCODE_DISABLED; + } else { + dwrq->flags |= IW_ENCODE_DISABLED; + } + + mutex_unlock(&adapter->lock); + + dwrq->flags |= IW_ENCODE_NOKEY; + + lbs_pr_debug(1, "key:%02x:%02x:%02x:%02x:%02x:%02x keylen=%d\n", + extra[0], extra[1], extra[2], + extra[3], extra[4], extra[5], dwrq->length); + + lbs_pr_debug(1, "Return flags=0x%x\n", dwrq->flags); + + LEAVE(); + return 0; +} + +/** + * @brief Set Encryption key (internal) + * + * @param priv A pointer to private card structure + * @param key_material A pointer to key material + * @param key_length length of key material + * @param index key index to set + * @param set_tx_key Force set TX key (1 = yes, 0 = no) + * @return 0 --success, otherwise fail + */ +static int wlan_set_wep_key(struct assoc_request *assoc_req, + const char *key_material, + u16 key_length, + u16 index, + int set_tx_key) +{ + struct WLAN_802_11_KEY *pkey; + + ENTER(); + + /* Paranoid validation of key index */ + if (index > 3) { + LEAVE(); + return -EINVAL; + } + + /* validate max key length */ + if (key_length > KEY_LEN_WEP_104) { + LEAVE(); + return -EINVAL; + } + + pkey = &assoc_req->wep_keys[index]; + + if (key_length > 0) { + memset(pkey, 0, sizeof(struct WLAN_802_11_KEY)); + pkey->type = KEY_TYPE_ID_WEP; + + /* Standardize the key length */ + pkey->len = (key_length > KEY_LEN_WEP_40) ? + KEY_LEN_WEP_104 : KEY_LEN_WEP_40; + memcpy(pkey->key, key_material, key_length); + } + + if (set_tx_key) { + /* Ensure the chosen key is valid */ + if (!pkey->len) { + lbs_pr_debug(1, "key not set, so cannot enable it\n"); + LEAVE(); + return -EINVAL; + } + assoc_req->wep_tx_keyidx = index; + } + + assoc_req->secinfo.WEPstatus = wlan802_11WEPenabled; + + LEAVE(); + return 0; +} + +static int validate_key_index(u16 def_index, u16 raw_index, + u16 *out_index, u16 *is_default) +{ + if (!out_index || !is_default) + return -EINVAL; + + /* Verify index if present, otherwise use default TX key index */ + if (raw_index > 0) { + if (raw_index > 4) + return -EINVAL; + *out_index = raw_index - 1; + } else { + *out_index = def_index; + *is_default = 1; + } + return 0; +} + +static void disable_wep(struct assoc_request *assoc_req) +{ + int i; + + /* Set Open System auth mode */ + assoc_req->secinfo.authmode = wlan802_11authmodeopen; + + /* Clear WEP keys and mark WEP as disabled */ + assoc_req->secinfo.WEPstatus = wlan802_11WEPdisabled; + for (i = 0; i < 4; i++) + assoc_req->wep_keys[i].len = 0; + + set_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags); + set_bit(ASSOC_FLAG_WEP_KEYS, &assoc_req->flags); +} + +/** + * @brief Set Encryption key + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param vwrq A pointer to iw_param structure + * @param extra A pointer to extra data buf + * @return 0 --success, otherwise fail + */ +static int wlan_set_encode(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *dwrq, char *extra) +{ + int ret = 0; + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + struct assoc_request * assoc_req; + u16 is_default = 0, index = 0, set_tx_key = 0; + + ENTER(); + + mutex_lock(&adapter->lock); + assoc_req = wlan_get_association_request(adapter); + if (!assoc_req) { + ret = -ENOMEM; + goto out; + } + + if (dwrq->flags & IW_ENCODE_DISABLED) { + disable_wep (assoc_req); + goto out; + } + + ret = validate_key_index(assoc_req->wep_tx_keyidx, + (dwrq->flags & IW_ENCODE_INDEX), + &index, &is_default); + if (ret) { + ret = -EINVAL; + goto out; + } + + /* If WEP isn't enabled, or if there is no key data but a valid + * index, set the TX key. + */ + if ((assoc_req->secinfo.WEPstatus != wlan802_11WEPenabled) + || (dwrq->length == 0 && !is_default)) + set_tx_key = 1; + + ret = wlan_set_wep_key(assoc_req, extra, dwrq->length, index, set_tx_key); + if (ret) + goto out; + + if (dwrq->length) + set_bit(ASSOC_FLAG_WEP_KEYS, &assoc_req->flags); + if (set_tx_key) + set_bit(ASSOC_FLAG_WEP_TX_KEYIDX, &assoc_req->flags); + + if (dwrq->flags & IW_ENCODE_RESTRICTED) { + assoc_req->secinfo.authmode = wlan802_11authmodeshared; + } else if (dwrq->flags & IW_ENCODE_OPEN) { + assoc_req->secinfo.authmode = wlan802_11authmodeopen; + } + +out: + if (ret == 0) { + set_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags); + wlan_postpone_association_work(priv); + } else { + wlan_cancel_association_work(priv); + } + mutex_unlock(&adapter->lock); + + LEAVE(); + return ret; +} + +/** + * @brief Get Extended Encryption key (WPA/802.1x and WEP) + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param vwrq A pointer to iw_param structure + * @param extra A pointer to extra data buf + * @return 0 on success, otherwise failure + */ +static int wlan_get_encodeext(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *dwrq, + char *extra) +{ + int ret = -EINVAL; + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + struct iw_encode_ext *ext = (struct iw_encode_ext *)extra; + int index, max_key_len; + + ENTER(); + + max_key_len = dwrq->length - sizeof(*ext); + if (max_key_len < 0) + goto out; + + index = dwrq->flags & IW_ENCODE_INDEX; + if (index) { + if (index < 1 || index > 4) + goto out; + index--; + } else { + index = adapter->wep_tx_keyidx; + } + + if (!ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY && + ext->alg != IW_ENCODE_ALG_WEP) { + if (index != 0 || adapter->inframode != wlan802_11infrastructure) + goto out; + } + + dwrq->flags = index + 1; + memset(ext, 0, sizeof(*ext)); + + if ((adapter->secinfo.WEPstatus == wlan802_11WEPdisabled) + && !adapter->secinfo.WPAenabled && !adapter->secinfo.WPA2enabled) { + ext->alg = IW_ENCODE_ALG_NONE; + ext->key_len = 0; + dwrq->flags |= IW_ENCODE_DISABLED; + } else { + u8 *key = NULL; + + if ((adapter->secinfo.WEPstatus == wlan802_11WEPenabled) + && !adapter->secinfo.WPAenabled + && !adapter->secinfo.WPA2enabled) { + ext->alg = IW_ENCODE_ALG_WEP; + ext->key_len = adapter->wep_keys[index].len; + key = &adapter->wep_keys[index].key[0]; + } else if ((adapter->secinfo.WEPstatus == wlan802_11WEPdisabled) && + (adapter->secinfo.WPAenabled || + adapter->secinfo.WPA2enabled)) { + /* WPA */ + ext->alg = IW_ENCODE_ALG_TKIP; + ext->key_len = 0; + } else { + goto out; + } + + if (ext->key_len > max_key_len) { + ret = -E2BIG; + goto out; + } + + if (ext->key_len) + memcpy(ext->key, key, ext->key_len); + else + dwrq->flags |= IW_ENCODE_NOKEY; + dwrq->flags |= IW_ENCODE_ENABLED; + } + ret = 0; + +out: + LEAVE(); + return ret; +} + +/** + * @brief Set Encryption key Extended (WPA/802.1x and WEP) + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param vwrq A pointer to iw_param structure + * @param extra A pointer to extra data buf + * @return 0 --success, otherwise fail + */ +static int wlan_set_encodeext(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *dwrq, + char *extra) +{ + int ret = 0; + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + struct iw_encode_ext *ext = (struct iw_encode_ext *)extra; + int alg = ext->alg; + struct assoc_request * assoc_req; + + ENTER(); + + mutex_lock(&adapter->lock); + assoc_req = wlan_get_association_request(adapter); + if (!assoc_req) { + ret = -ENOMEM; + goto out; + } + + if ((alg == IW_ENCODE_ALG_NONE) || (dwrq->flags & IW_ENCODE_DISABLED)) { + disable_wep (assoc_req); + } else if (alg == IW_ENCODE_ALG_WEP) { + u16 is_default = 0, index, set_tx_key = 0; + + ret = validate_key_index(assoc_req->wep_tx_keyidx, + (dwrq->flags & IW_ENCODE_INDEX), + &index, &is_default); + if (ret) + goto out; + + /* If WEP isn't enabled, or if there is no key data but a valid + * index, or if the set-TX-key flag was passed, set the TX key. + */ + if ((assoc_req->secinfo.WEPstatus != wlan802_11WEPenabled) + || (dwrq->length == 0 && !is_default) + || (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY)) + set_tx_key = 1; + + /* Copy key to driver */ + ret = wlan_set_wep_key (assoc_req, ext->key, ext->key_len, index, + set_tx_key); + if (ret) + goto out; + + if (dwrq->flags & IW_ENCODE_RESTRICTED) { + assoc_req->secinfo.authmode = + wlan802_11authmodeshared; + } else if (dwrq->flags & IW_ENCODE_OPEN) { + assoc_req->secinfo.authmode = + wlan802_11authmodeopen; + } + + /* Mark the various WEP bits as modified */ + set_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags); + if (dwrq->length) + set_bit(ASSOC_FLAG_WEP_KEYS, &assoc_req->flags); + if (set_tx_key) + set_bit(ASSOC_FLAG_WEP_TX_KEYIDX, &assoc_req->flags); + + } else if ((alg == IW_ENCODE_ALG_TKIP) || (alg == IW_ENCODE_ALG_CCMP)) { + struct WLAN_802_11_KEY * pkey; + + /* validate key length */ + if (((alg == IW_ENCODE_ALG_TKIP) + && (ext->key_len != KEY_LEN_WPA_TKIP)) + || ((alg == IW_ENCODE_ALG_CCMP) + && (ext->key_len != KEY_LEN_WPA_AES))) { + lbs_pr_debug(1, "Invalid size %d for key of alg" + "type %d.\n", + ext->key_len, + alg); + ret = -EINVAL; + goto out; + } + + if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) + pkey = &assoc_req->wpa_mcast_key; + else + pkey = &assoc_req->wpa_unicast_key; + + memset(pkey, 0, sizeof (struct WLAN_802_11_KEY)); + memcpy(pkey->key, ext->key, ext->key_len); + pkey->len = ext->key_len; + pkey->flags = KEY_INFO_WPA_ENABLED; + + if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) { + pkey->flags |= KEY_INFO_WPA_MCAST; + set_bit(ASSOC_FLAG_WPA_MCAST_KEY, &assoc_req->flags); + } else { + pkey->flags |= KEY_INFO_WPA_UNICAST; + set_bit(ASSOC_FLAG_WPA_UCAST_KEY, &assoc_req->flags); + } + + if (alg == IW_ENCODE_ALG_TKIP) + pkey->type = KEY_TYPE_ID_TKIP; + else if (alg == IW_ENCODE_ALG_CCMP) + pkey->type = KEY_TYPE_ID_AES; + + /* If WPA isn't enabled yet, do that now */ + if ( assoc_req->secinfo.WPAenabled == 0 + && assoc_req->secinfo.WPA2enabled == 0) { + assoc_req->secinfo.WPAenabled = 1; + assoc_req->secinfo.WPA2enabled = 1; + set_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags); + } + + disable_wep (assoc_req); + } + +out: + if (ret == 0) { + wlan_postpone_association_work(priv); + } else { + wlan_cancel_association_work(priv); + } + mutex_unlock(&adapter->lock); + + LEAVE(); + return ret; +} + + +static int wlan_set_genie(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *dwrq, + char *extra) +{ + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + int ret = 0; + struct assoc_request * assoc_req; + + ENTER(); + + mutex_lock(&adapter->lock); + assoc_req = wlan_get_association_request(adapter); + if (!assoc_req) { + ret = -ENOMEM; + goto out; + } + + if (dwrq->length > MAX_WPA_IE_LEN || + (dwrq->length && extra == NULL)) { + ret = -EINVAL; + goto out; + } + + if (dwrq->length) { + memcpy(&assoc_req->wpa_ie[0], extra, dwrq->length); + assoc_req->wpa_ie_len = dwrq->length; + } else { + memset(&assoc_req->wpa_ie[0], 0, sizeof(adapter->wpa_ie)); + assoc_req->wpa_ie_len = 0; + } + +out: + if (ret == 0) { + set_bit(ASSOC_FLAG_WPA_IE, &assoc_req->flags); + wlan_postpone_association_work(priv); + } else { + wlan_cancel_association_work(priv); + } + mutex_unlock(&adapter->lock); + + LEAVE(); + return ret; +} + +static int wlan_get_genie(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *dwrq, + char *extra) +{ + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + if (adapter->wpa_ie_len == 0) { + dwrq->length = 0; + LEAVE(); + return 0; + } + + if (dwrq->length < adapter->wpa_ie_len) { + LEAVE(); + return -E2BIG; + } + + dwrq->length = adapter->wpa_ie_len; + memcpy(extra, &adapter->wpa_ie[0], adapter->wpa_ie_len); + + LEAVE(); + return 0; +} + + +static int wlan_set_auth(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *dwrq, + char *extra) +{ + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + struct assoc_request * assoc_req; + int ret = 0; + int updated = 0; + + ENTER(); + + mutex_lock(&adapter->lock); + assoc_req = wlan_get_association_request(adapter); + if (!assoc_req) { + ret = -ENOMEM; + goto out; + } + + switch (dwrq->flags & IW_AUTH_INDEX) { + case IW_AUTH_TKIP_COUNTERMEASURES: + case IW_AUTH_CIPHER_PAIRWISE: + case IW_AUTH_CIPHER_GROUP: + case IW_AUTH_KEY_MGMT: + /* + * libertas does not use these parameters + */ + break; + + case IW_AUTH_WPA_VERSION: + if (dwrq->value & IW_AUTH_WPA_VERSION_DISABLED) { + assoc_req->secinfo.WPAenabled = 0; + assoc_req->secinfo.WPA2enabled = 0; + } + if (dwrq->value & IW_AUTH_WPA_VERSION_WPA) { + assoc_req->secinfo.WPAenabled = 1; + assoc_req->secinfo.WEPstatus = wlan802_11WEPdisabled; + assoc_req->secinfo.authmode = + wlan802_11authmodeopen; + } + if (dwrq->value & IW_AUTH_WPA_VERSION_WPA2) { + assoc_req->secinfo.WPA2enabled = 1; + assoc_req->secinfo.WEPstatus = wlan802_11WEPdisabled; + assoc_req->secinfo.authmode = + wlan802_11authmodeopen; + } + updated = 1; + break; + + case IW_AUTH_DROP_UNENCRYPTED: + if (dwrq->value) { + adapter->currentpacketfilter |= + cmd_act_mac_strict_protection_enable; + } else { + adapter->currentpacketfilter &= + ~cmd_act_mac_strict_protection_enable; + } + updated = 1; + break; + + case IW_AUTH_80211_AUTH_ALG: + if (dwrq->value & IW_AUTH_ALG_SHARED_KEY) { + assoc_req->secinfo.authmode = + wlan802_11authmodeshared; + } else if (dwrq->value & IW_AUTH_ALG_OPEN_SYSTEM) { + assoc_req->secinfo.authmode = + wlan802_11authmodeopen; + } else if (dwrq->value & IW_AUTH_ALG_LEAP) { + assoc_req->secinfo.authmode = + wlan802_11authmodenetworkEAP; + } else { + ret = -EINVAL; + } + updated = 1; + break; + + case IW_AUTH_WPA_ENABLED: + if (dwrq->value) { + if (!assoc_req->secinfo.WPAenabled && + !assoc_req->secinfo.WPA2enabled) { + assoc_req->secinfo.WPAenabled = 1; + assoc_req->secinfo.WPA2enabled = 1; + assoc_req->secinfo.WEPstatus = wlan802_11WEPdisabled; + assoc_req->secinfo.authmode = + wlan802_11authmodeopen; + } + } else { + assoc_req->secinfo.WPAenabled = 0; + assoc_req->secinfo.WPA2enabled = 0; + } + updated = 1; + break; + + default: + ret = -EOPNOTSUPP; + break; + } + +out: + if (ret == 0) { + if (updated) + set_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags); + wlan_postpone_association_work(priv); + } else if (ret != -EOPNOTSUPP) { + wlan_cancel_association_work(priv); + } + mutex_unlock(&adapter->lock); + + LEAVE(); + return ret; +} + +static int wlan_get_auth(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *dwrq, + char *extra) +{ + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + switch (dwrq->flags & IW_AUTH_INDEX) { + case IW_AUTH_WPA_VERSION: + dwrq->value = 0; + if (adapter->secinfo.WPAenabled) + dwrq->value |= IW_AUTH_WPA_VERSION_WPA; + if (adapter->secinfo.WPA2enabled) + dwrq->value |= IW_AUTH_WPA_VERSION_WPA2; + if (!dwrq->value) + dwrq->value |= IW_AUTH_WPA_VERSION_DISABLED; + break; + + case IW_AUTH_DROP_UNENCRYPTED: + dwrq->value = 0; + if (adapter->currentpacketfilter & + cmd_act_mac_strict_protection_enable) + dwrq->value = 1; + break; + + case IW_AUTH_80211_AUTH_ALG: + switch (adapter->secinfo.authmode) { + case wlan802_11authmodeshared: + dwrq->value = IW_AUTH_ALG_SHARED_KEY; + break; + case wlan802_11authmodeopen: + dwrq->value = IW_AUTH_ALG_OPEN_SYSTEM; + break; + case wlan802_11authmodenetworkEAP: + dwrq->value = IW_AUTH_ALG_LEAP; + break; + default: + break; + } + break; + + case IW_AUTH_WPA_ENABLED: + if (adapter->secinfo.WPAenabled && adapter->secinfo.WPA2enabled) + dwrq->value = 1; + break; + + default: + LEAVE(); + return -EOPNOTSUPP; + } + + LEAVE(); + return 0; +} + + +static int wlan_set_txpow(struct net_device *dev, struct iw_request_info *info, + struct iw_param *vwrq, char *extra) +{ + int ret = 0; + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + + u16 dbm; + + ENTER(); + + if (vwrq->disabled) { + wlan_radio_ioctl(priv, RADIO_OFF); + return 0; + } + + adapter->preamble = cmd_type_auto_preamble; + + wlan_radio_ioctl(priv, RADIO_ON); + + if ((vwrq->flags & IW_TXPOW_TYPE) == IW_TXPOW_MWATT) { + dbm = (u16) mw_to_dbm(vwrq->value); + } else + dbm = (u16) vwrq->value; + + /* auto tx power control */ + + if (vwrq->fixed == 0) + dbm = 0xffff; + + lbs_pr_debug(1, "<1>TXPOWER SET %d dbm.\n", dbm); + + ret = libertas_prepare_and_send_command(priv, + cmd_802_11_rf_tx_power, + cmd_act_tx_power_opt_set_low, + cmd_option_waitforrsp, 0, (void *)&dbm); + + LEAVE(); + return ret; +} + +static int wlan_get_essid(struct net_device *dev, struct iw_request_info *info, + struct iw_point *dwrq, char *extra) +{ + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + /* + * Note : if dwrq->flags != 0, we should get the relevant SSID from + * the SSID list... + */ + + /* + * Get the current SSID + */ + if (adapter->connect_status == libertas_connected) { + memcpy(extra, adapter->curbssparams.ssid.ssid, + adapter->curbssparams.ssid.ssidlength); + extra[adapter->curbssparams.ssid.ssidlength] = '\0'; + } else { + memset(extra, 0, 32); + extra[adapter->curbssparams.ssid.ssidlength] = '\0'; + } + /* + * If none, we may want to get the one that was set + */ + + /* To make the driver backward compatible with WPA supplicant v0.2.4 */ + if (dwrq->length == 32) /* check with WPA supplicant buffer size */ + dwrq->length = min_t(size_t, adapter->curbssparams.ssid.ssidlength, + IW_ESSID_MAX_SIZE); + else + dwrq->length = adapter->curbssparams.ssid.ssidlength + 1; + + dwrq->flags = 1; /* active */ + + LEAVE(); + return 0; +} + +static int wlan_set_essid(struct net_device *dev, struct iw_request_info *info, + struct iw_point *dwrq, char *extra) +{ + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + int ret = 0; + struct WLAN_802_11_SSID ssid; + struct assoc_request * assoc_req; + int ssid_len = dwrq->length; + + ENTER(); + + /* + * WE-20 and earlier NULL pad the end of the SSID and increment + * SSID length so it can be used like a string. WE-21 and later don't, + * but some userspace tools aren't able to cope with the change. + */ + if ((ssid_len > 0) && (extra[ssid_len - 1] == '\0')) + ssid_len--; + + /* Check the size of the string */ + if (ssid_len > IW_ESSID_MAX_SIZE) { + ret = -E2BIG; + goto out; + } + + memset(&ssid, 0, sizeof(struct WLAN_802_11_SSID)); + + if (!dwrq->flags || !ssid_len) { + /* "any" SSID requested; leave SSID blank */ + } else { + /* Specific SSID requested */ + memcpy(&ssid.ssid, extra, ssid_len); + ssid.ssidlength = ssid_len; + } + + lbs_pr_debug(1, "Requested new SSID = %s\n", + (ssid.ssidlength > 0) ? (char *)ssid.ssid : "any"); + +out: + mutex_lock(&adapter->lock); + if (ret == 0) { + /* Get or create the current association request */ + assoc_req = wlan_get_association_request(adapter); + if (!assoc_req) { + ret = -ENOMEM; + } else { + /* Copy the SSID to the association request */ + memcpy(&assoc_req->ssid, &ssid, sizeof(struct WLAN_802_11_SSID)); + set_bit(ASSOC_FLAG_SSID, &assoc_req->flags); + wlan_postpone_association_work(priv); + } + } + + /* Cancel the association request if there was an error */ + if (ret != 0) { + wlan_cancel_association_work(priv); + } + + mutex_unlock(&adapter->lock); + + LEAVE(); + return ret; +} + +/** + * @brief Connect to the AP or Ad-hoc Network with specific bssid + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param awrq A pointer to iw_param structure + * @param extra A pointer to extra data buf + * @return 0 --success, otherwise fail + */ +static int wlan_set_wap(struct net_device *dev, struct iw_request_info *info, + struct sockaddr *awrq, char *extra) +{ + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + struct assoc_request * assoc_req; + int ret = 0; + + ENTER(); + + if (awrq->sa_family != ARPHRD_ETHER) + return -EINVAL; + + lbs_pr_debug(1, "ASSOC: WAP: sa_data: " MAC_FMT "\n", MAC_ARG(awrq->sa_data)); + + mutex_lock(&adapter->lock); + + /* Get or create the current association request */ + assoc_req = wlan_get_association_request(adapter); + if (!assoc_req) { + wlan_cancel_association_work(priv); + ret = -ENOMEM; + } else { + /* Copy the BSSID to the association request */ + memcpy(&assoc_req->bssid, awrq->sa_data, ETH_ALEN); + set_bit(ASSOC_FLAG_BSSID, &assoc_req->flags); + wlan_postpone_association_work(priv); + } + + mutex_unlock(&adapter->lock); + + return ret; +} + +void libertas_get_fwversion(wlan_adapter * adapter, char *fwversion, int maxlen) +{ + union { + u32 l; + u8 c[4]; + } ver; + char fwver[32]; + + mutex_lock(&adapter->lock); + ver.l = adapter->fwreleasenumber; + mutex_unlock(&adapter->lock); + + if (ver.c[3] == 0) + sprintf(fwver, "%u.%u.%u", ver.c[2], ver.c[1], ver.c[0]); + else + sprintf(fwver, "%u.%u.%u.p%u", + ver.c[2], ver.c[1], ver.c[0], ver.c[3]); + + snprintf(fwversion, maxlen, fwver); +} + + +/* + * iwconfig settable callbacks + */ +static const iw_handler wlan_handler[] = { + (iw_handler) NULL, /* SIOCSIWCOMMIT */ + (iw_handler) wlan_get_name, /* SIOCGIWNAME */ + (iw_handler) NULL, /* SIOCSIWNWID */ + (iw_handler) NULL, /* SIOCGIWNWID */ + (iw_handler) wlan_set_freq, /* SIOCSIWFREQ */ + (iw_handler) wlan_get_freq, /* SIOCGIWFREQ */ + (iw_handler) wlan_set_mode, /* SIOCSIWMODE */ + (iw_handler) wlan_get_mode, /* SIOCGIWMODE */ + (iw_handler) NULL, /* SIOCSIWSENS */ + (iw_handler) NULL, /* SIOCGIWSENS */ + (iw_handler) NULL, /* SIOCSIWRANGE */ + (iw_handler) wlan_get_range, /* SIOCGIWRANGE */ + (iw_handler) NULL, /* SIOCSIWPRIV */ + (iw_handler) NULL, /* SIOCGIWPRIV */ + (iw_handler) NULL, /* SIOCSIWSTATS */ + (iw_handler) NULL, /* SIOCGIWSTATS */ + iw_handler_set_spy, /* SIOCSIWSPY */ + iw_handler_get_spy, /* SIOCGIWSPY */ + iw_handler_set_thrspy, /* SIOCSIWTHRSPY */ + iw_handler_get_thrspy, /* SIOCGIWTHRSPY */ + (iw_handler) wlan_set_wap, /* SIOCSIWAP */ + (iw_handler) wlan_get_wap, /* SIOCGIWAP */ + (iw_handler) NULL, /* SIOCSIWMLME */ + (iw_handler) NULL, /* SIOCGIWAPLIST - deprecated */ + (iw_handler) libertas_set_scan, /* SIOCSIWSCAN */ + (iw_handler) libertas_get_scan, /* SIOCGIWSCAN */ + (iw_handler) wlan_set_essid, /* SIOCSIWESSID */ + (iw_handler) wlan_get_essid, /* SIOCGIWESSID */ + (iw_handler) wlan_set_nick, /* SIOCSIWNICKN */ + (iw_handler) wlan_get_nick, /* SIOCGIWNICKN */ + (iw_handler) NULL, /* -- hole -- */ + (iw_handler) NULL, /* -- hole -- */ + (iw_handler) wlan_set_rate, /* SIOCSIWRATE */ + (iw_handler) wlan_get_rate, /* SIOCGIWRATE */ + (iw_handler) wlan_set_rts, /* SIOCSIWRTS */ + (iw_handler) wlan_get_rts, /* SIOCGIWRTS */ + (iw_handler) wlan_set_frag, /* SIOCSIWFRAG */ + (iw_handler) wlan_get_frag, /* SIOCGIWFRAG */ + (iw_handler) wlan_set_txpow, /* SIOCSIWTXPOW */ + (iw_handler) wlan_get_txpow, /* SIOCGIWTXPOW */ + (iw_handler) wlan_set_retry, /* SIOCSIWRETRY */ + (iw_handler) wlan_get_retry, /* SIOCGIWRETRY */ + (iw_handler) wlan_set_encode, /* SIOCSIWENCODE */ + (iw_handler) wlan_get_encode, /* SIOCGIWENCODE */ + (iw_handler) wlan_set_power, /* SIOCSIWPOWER */ + (iw_handler) wlan_get_power, /* SIOCGIWPOWER */ + (iw_handler) NULL, /* -- hole -- */ + (iw_handler) NULL, /* -- hole -- */ + (iw_handler) wlan_set_genie, /* SIOCSIWGENIE */ + (iw_handler) wlan_get_genie, /* SIOCGIWGENIE */ + (iw_handler) wlan_set_auth, /* SIOCSIWAUTH */ + (iw_handler) wlan_get_auth, /* SIOCGIWAUTH */ + (iw_handler) wlan_set_encodeext,/* SIOCSIWENCODEEXT */ + (iw_handler) wlan_get_encodeext,/* SIOCGIWENCODEEXT */ + (iw_handler) NULL, /* SIOCSIWPMKSA */ +}; + +struct iw_handler_def libertas_handler_def = { + .num_standard = sizeof(wlan_handler) / sizeof(iw_handler), + .num_private = sizeof(wlan_private_handler) / sizeof(iw_handler), + .num_private_args = sizeof(wlan_private_args) / + sizeof(struct iw_priv_args), + .standard = (iw_handler *) wlan_handler, + .private = (iw_handler *) wlan_private_handler, + .private_args = (struct iw_priv_args *)wlan_private_args, + .get_wireless_stats = wlan_get_wireless_stats, +}; diff --git a/drivers/net/wireless/libertas/wext.h b/drivers/net/wireless/libertas/wext.h new file mode 100644 index 00000000000..39f367c38d9 --- /dev/null +++ b/drivers/net/wireless/libertas/wext.h @@ -0,0 +1,147 @@ +/** + * This file contains definition for IOCTL call. + */ +#ifndef _WLAN_WEXT_H_ +#define _WLAN_WEXT_H_ + +#define SUBCMD_OFFSET 4 +#define SUBCMD_DATA(x) *((int *)(x->u.name + SUBCMD_OFFSET)) + +/** PRIVATE CMD ID */ +#define WLANIOCTL SIOCIWFIRSTPRIV + +#define WLANSETWPAIE (WLANIOCTL + 0) + +#define WLAN_SETINT_GETINT (WLANIOCTL + 7) +#define WLANNF 1 +#define WLANRSSI 2 +#define WLANENABLE11D 5 +#define WLANADHOCGRATE 6 +#define WLAN_SUBCMD_SET_PRESCAN 11 + +#define WLAN_SETNONE_GETNONE (WLANIOCTL + 8) +#define WLANDEAUTH 1 +#define WLANRADIOON 2 +#define WLANRADIOOFF 3 +#define WLANREMOVEADHOCAES 4 +#define WLANADHOCSTOP 5 +#define WLANCIPHERTEST 6 +#define WLANCRYPTOTEST 7 + +#define WLANWLANIDLEON 10 +#define WLANWLANIDLEOFF 11 +#define WLAN_SUBCMD_BT_RESET 13 +#define WLAN_SUBCMD_FWT_RESET 14 + +#define WLANGETLOG (WLANIOCTL + 9) +#define GETLOG_BUFSIZE 300 + +#define WLANSCAN_TYPE (WLANIOCTL + 11) + +#define WLAN_SETNONE_GETONEINT (WLANIOCTL + 15) +#define WLANGETREGION 1 +#define WLAN_GET_LISTEN_INTERVAL 2 +#define WLAN_GET_MULTIPLE_DTIM 3 +#define WLAN_GET_TX_RATE 4 +#define WLANGETBCNAVG 5 + +#define WLAN_GET_LINKMODE 6 +#define WLAN_GET_RADIOMODE 7 +#define WLAN_GET_DEBUGMODE 8 +#define WLAN_SUBCMD_FWT_CLEANUP 15 +#define WLAN_SUBCMD_FWT_TIME 16 +#define WLAN_SUBCMD_MESH_GET_TTL 17 + +#define WLANREGCFRDWR (WLANIOCTL + 18) + +#define WLAN_SETNONE_GETTWELVE_CHAR (WLANIOCTL + 19) +#define WLAN_SUBCMD_GETRXANTENNA 1 +#define WLAN_SUBCMD_GETTXANTENNA 2 +#define WLAN_GET_TSF 3 + +#define WLAN_SETNONE_GETWORDCHAR (WLANIOCTL + 21) +#define WLANGETADHOCAES 1 + +#define WLAN_SETONEINT_GETONEINT (WLANIOCTL + 23) +#define WLAN_BEACON_INTERVAL 1 +#define WLAN_LISTENINTRVL 4 + +#define WLAN_TXCONTROL 6 +#define WLAN_NULLPKTINTERVAL 7 + +#define WLAN_SETONEINT_GETNONE (WLANIOCTL + 24) +#define WLAN_SUBCMD_SETRXANTENNA 1 +#define WLAN_SUBCMD_SETTXANTENNA 2 +#define WLANSETAUTHALG 5 +#define WLANSET8021XAUTHALG 6 +#define WLANSETENCRYPTIONMODE 7 +#define WLANSETREGION 8 +#define WLAN_SET_LISTEN_INTERVAL 9 + +#define WLAN_SET_MULTIPLE_DTIM 10 +#define WLAN_SET_ATIM_WINDOW 11 +#define WLANSETBCNAVG 13 +#define WLANSETDATAAVG 14 +#define WLAN_SET_LINKMODE 15 +#define WLAN_SET_RADIOMODE 16 +#define WLAN_SET_DEBUGMODE 17 +#define WLAN_SUBCMD_MESH_SET_TTL 18 + +#define WLAN_SET128CHAR_GET128CHAR (WLANIOCTL + 25) +#define WLANSCAN_MODE 6 + +#define WLAN_GET_ADHOC_STATUS 9 + +#define WLAN_SUBCMD_BT_ADD 18 +#define WLAN_SUBCMD_BT_DEL 19 +#define WLAN_SUBCMD_BT_LIST 20 +#define WLAN_SUBCMD_FWT_ADD 21 +#define WLAN_SUBCMD_FWT_DEL 22 +#define WLAN_SUBCMD_FWT_LOOKUP 23 +#define WLAN_SUBCMD_FWT_LIST_NEIGHBOR 24 +#define WLAN_SUBCMD_FWT_LIST 25 +#define WLAN_SUBCMD_FWT_LIST_ROUTE 26 + +#define WLAN_SET_GET_SIXTEEN_INT (WLANIOCTL + 29) +#define WLAN_TPCCFG 1 +#define WLAN_POWERCFG 2 + +#define WLAN_AUTO_FREQ_SET 3 +#define WLAN_AUTO_FREQ_GET 4 +#define WLAN_LED_GPIO_CTRL 5 +#define WLAN_SCANPROBES 6 +#define WLAN_ADAPT_RATESET 8 +#define WLAN_INACTIVITY_TIMEOUT 9 +#define WLANSNR 10 +#define WLAN_GET_RATE 11 +#define WLAN_GET_RXINFO 12 + +#define WLANCMD52RDWR (WLANIOCTL + 30) +#define WLANCMD53RDWR (WLANIOCTL + 31) +#define CMD53BUFLEN 32 + +#define REG_MAC 0x19 +#define REG_BBP 0x1a +#define REG_RF 0x1b +#define REG_EEPROM 0x59 +#define WLAN_LINKMODE_802_3 0 +#define WLAN_LINKMODE_802_11 2 +#define WLAN_RADIOMODE_NONE 0 +#define WLAN_RADIOMODE_RADIOTAP 2 + +/** wlan_ioctl_regrdwr */ +struct wlan_ioctl_regrdwr { + /** Which register to access */ + u16 whichreg; + /** Read or Write */ + u16 action; + u32 offset; + u16 NOB; + u32 value; +}; + +extern struct iw_handler_def libertas_handler_def; +int libertas_do_ioctl(struct net_device *dev, struct ifreq *req, int i); +int wlan_radio_ioctl(wlan_private * priv, u8 option); + +#endif /* _WLAN_WEXT_H_ */ diff --git a/drivers/net/wireless/todo.txt b/drivers/net/wireless/todo.txt deleted file mode 100644 index 32234018de7..00000000000 --- a/drivers/net/wireless/todo.txt +++ /dev/null @@ -1,15 +0,0 @@ - Wireless Todo - ------------- - -1) Bring other kernel Wireless LAN drivers here - Completed - -2) Bring new Wireless LAN driver not yet in the kernel there - See my web page for details - In particular : HostAP - -3) Misc - o Mark wavelan, wavelan_cs, netwave_cs drivers as obsolete - o Maybe arlan.c, ray_cs.c and strip.c also deserve to be obsolete - - Jean II diff --git a/drivers/net/wireless/zd1211rw/zd_chip.c b/drivers/net/wireless/zd1211rw/zd_chip.c index 87ee3ee020f..95b4a2a2670 100644 --- a/drivers/net/wireless/zd1211rw/zd_chip.c +++ b/drivers/net/wireless/zd1211rw/zd_chip.c @@ -67,11 +67,12 @@ static int scnprint_id(struct zd_chip *chip, char *buffer, size_t size) i += scnprint_mac_oui(chip->e2p_mac, buffer+i, size-i); i += scnprintf(buffer+i, size-i, " "); i += zd_rf_scnprint_id(&chip->rf, buffer+i, size-i); - i += scnprintf(buffer+i, size-i, " pa%1x %c%c%c%c", chip->pa_type, + i += scnprintf(buffer+i, size-i, " pa%1x %c%c%c%c%c", chip->pa_type, chip->patch_cck_gain ? 'g' : '-', chip->patch_cr157 ? '7' : '-', chip->patch_6m_band_edge ? '6' : '-', - chip->new_phy_layout ? 'N' : '-'); + chip->new_phy_layout ? 'N' : '-', + chip->al2230s_bit ? 'S' : '-'); return i; } @@ -114,7 +115,7 @@ int zd_ioread32v_locked(struct zd_chip *chip, u32 *values, const zd_addr_t *addr /* Allocate a single memory block for values and addresses. */ count16 = 2*count; a16 = (zd_addr_t *) kmalloc(count16 * (sizeof(zd_addr_t) + sizeof(u16)), - GFP_NOFS); + GFP_KERNEL); if (!a16) { dev_dbg_f(zd_chip_dev(chip), "error ENOMEM in allocation of a16\n"); @@ -163,7 +164,7 @@ int _zd_iowrite32v_locked(struct zd_chip *chip, const struct zd_ioreq32 *ioreqs, /* Allocate a single memory block for values and addresses. */ count16 = 2*count; - ioreqs16 = kmalloc(count16 * sizeof(struct zd_ioreq16), GFP_NOFS); + ioreqs16 = kmalloc(count16 * sizeof(struct zd_ioreq16), GFP_KERNEL); if (!ioreqs16) { r = -ENOMEM; dev_dbg_f(zd_chip_dev(chip), @@ -614,16 +615,24 @@ static int patch_cr157(struct zd_chip *chip) * Vendor driver says: for FCC regulation, enabled per HWFeature 6M band edge * bit (for AL2230, AL2230S) */ -static int patch_6m_band_edge(struct zd_chip *chip, int channel) +static int patch_6m_band_edge(struct zd_chip *chip, u8 channel) +{ + ZD_ASSERT(mutex_is_locked(&chip->mutex)); + if (!chip->patch_6m_band_edge) + return 0; + + return zd_rf_patch_6m_band_edge(&chip->rf, channel); +} + +/* Generic implementation of 6M band edge patching, used by most RFs via + * zd_rf_generic_patch_6m() */ +int zd_chip_generic_patch_6m_band(struct zd_chip *chip, int channel) { struct zd_ioreq16 ioreqs[] = { { CR128, 0x14 }, { CR129, 0x12 }, { CR130, 0x10 }, { CR47, 0x1e }, }; - if (!chip->patch_6m_band_edge || !chip->rf.patch_6m_band_edge) - return 0; - /* FIXME: Channel 11 is not the edge for all regulatory domains. */ if (channel == 1 || channel == 11) ioreqs[0].value = 0x12; @@ -683,17 +692,17 @@ static int zd1211_hw_reset_phy(struct zd_chip *chip) { CR111, 0x27 }, { CR112, 0x27 }, { CR113, 0x27 }, { CR114, 0x27 }, { CR115, 0x26 }, { CR116, 0x24 }, { CR117, 0xfc }, { CR118, 0xfa }, { CR120, 0x4f }, - { CR123, 0x27 }, { CR125, 0xaa }, { CR127, 0x03 }, - { CR128, 0x14 }, { CR129, 0x12 }, { CR130, 0x10 }, - { CR131, 0x0C }, { CR136, 0xdf }, { CR137, 0x40 }, - { CR138, 0xa0 }, { CR139, 0xb0 }, { CR140, 0x99 }, - { CR141, 0x82 }, { CR142, 0x54 }, { CR143, 0x1c }, - { CR144, 0x6c }, { CR147, 0x07 }, { CR148, 0x4c }, - { CR149, 0x50 }, { CR150, 0x0e }, { CR151, 0x18 }, - { CR160, 0xfe }, { CR161, 0xee }, { CR162, 0xaa }, - { CR163, 0xfa }, { CR164, 0xfa }, { CR165, 0xea }, - { CR166, 0xbe }, { CR167, 0xbe }, { CR168, 0x6a }, - { CR169, 0xba }, { CR170, 0xba }, { CR171, 0xba }, + { CR125, 0xaa }, { CR127, 0x03 }, { CR128, 0x14 }, + { CR129, 0x12 }, { CR130, 0x10 }, { CR131, 0x0C }, + { CR136, 0xdf }, { CR137, 0x40 }, { CR138, 0xa0 }, + { CR139, 0xb0 }, { CR140, 0x99 }, { CR141, 0x82 }, + { CR142, 0x54 }, { CR143, 0x1c }, { CR144, 0x6c }, + { CR147, 0x07 }, { CR148, 0x4c }, { CR149, 0x50 }, + { CR150, 0x0e }, { CR151, 0x18 }, { CR160, 0xfe }, + { CR161, 0xee }, { CR162, 0xaa }, { CR163, 0xfa }, + { CR164, 0xfa }, { CR165, 0xea }, { CR166, 0xbe }, + { CR167, 0xbe }, { CR168, 0x6a }, { CR169, 0xba }, + { CR170, 0xba }, { CR171, 0xba }, /* Note: CR204 must lead the CR203 */ { CR204, 0x7d }, { }, diff --git a/drivers/net/wireless/zd1211rw/zd_chip.h b/drivers/net/wireless/zd1211rw/zd_chip.h index e57ed75d942..ce0a5f6da0d 100644 --- a/drivers/net/wireless/zd1211rw/zd_chip.h +++ b/drivers/net/wireless/zd1211rw/zd_chip.h @@ -833,6 +833,7 @@ int zd_chip_enable_rx(struct zd_chip *chip); void zd_chip_disable_rx(struct zd_chip *chip); int zd_chip_enable_hwint(struct zd_chip *chip); int zd_chip_disable_hwint(struct zd_chip *chip); +int zd_chip_generic_patch_6m_band(struct zd_chip *chip, int channel); int zd_chip_set_rts_cts_rate_locked(struct zd_chip *chip, u8 rts_rate, int preamble); diff --git a/drivers/net/wireless/zd1211rw/zd_mac.c b/drivers/net/wireless/zd1211rw/zd_mac.c index 4c5f78eac34..6753d240c16 100644 --- a/drivers/net/wireless/zd1211rw/zd_mac.c +++ b/drivers/net/wireless/zd1211rw/zd_mac.c @@ -156,17 +156,8 @@ void zd_mac_clear(struct zd_mac *mac) static int reset_mode(struct zd_mac *mac) { struct ieee80211_device *ieee = zd_mac_to_ieee80211(mac); - struct zd_ioreq32 ioreqs[] = { - { CR_RX_FILTER, STA_RX_FILTER }, - { CR_SNIFFER_ON, 0U }, - }; - - if (ieee->iw_mode == IW_MODE_MONITOR) { - ioreqs[0].value = 0xffffffff; - ioreqs[1].value = 0x1; - } - - return zd_iowrite32a(&mac->chip, ioreqs, ARRAY_SIZE(ioreqs)); + u32 filter = (ieee->iw_mode == IW_MODE_MONITOR) ? ~0 : STA_RX_FILTER; + return zd_iowrite32(&mac->chip, CR_RX_FILTER, filter); } int zd_mac_open(struct net_device *netdev) @@ -974,14 +965,14 @@ static int is_data_packet_for_us(struct ieee80211_device *ieee, switch (ieee->iw_mode) { case IW_MODE_ADHOC: if ((fc & (IEEE80211_FCTL_TODS|IEEE80211_FCTL_FROMDS)) != 0 || - memcmp(hdr->addr3, ieee->bssid, ETH_ALEN) != 0) + compare_ether_addr(hdr->addr3, ieee->bssid) != 0) return 0; break; case IW_MODE_AUTO: case IW_MODE_INFRA: if ((fc & (IEEE80211_FCTL_TODS|IEEE80211_FCTL_FROMDS)) != IEEE80211_FCTL_FROMDS || - memcmp(hdr->addr2, ieee->bssid, ETH_ALEN) != 0) + compare_ether_addr(hdr->addr2, ieee->bssid) != 0) return 0; break; default: @@ -989,9 +980,9 @@ static int is_data_packet_for_us(struct ieee80211_device *ieee, return 0; } - return memcmp(hdr->addr1, netdev->dev_addr, ETH_ALEN) == 0 || + return compare_ether_addr(hdr->addr1, netdev->dev_addr) == 0 || (is_multicast_ether_addr(hdr->addr1) && - memcmp(hdr->addr3, netdev->dev_addr, ETH_ALEN) != 0) || + compare_ether_addr(hdr->addr3, netdev->dev_addr) != 0) || (netdev->flags & IFF_PROMISC); } @@ -1047,7 +1038,7 @@ static void update_qual_rssi(struct zd_mac *mac, hdr = (struct ieee80211_hdr_3addr *)buffer; if (length < offsetof(struct ieee80211_hdr_3addr, addr3)) return; - if (memcmp(hdr->addr2, zd_mac_to_ieee80211(mac)->bssid, ETH_ALEN) != 0) + if (compare_ether_addr(hdr->addr2, zd_mac_to_ieee80211(mac)->bssid) != 0) return; spin_lock_irqsave(&mac->lock, flags); diff --git a/drivers/net/wireless/zd1211rw/zd_rf.c b/drivers/net/wireless/zd1211rw/zd_rf.c index f50cff3db91..549c23bcd6c 100644 --- a/drivers/net/wireless/zd1211rw/zd_rf.c +++ b/drivers/net/wireless/zd1211rw/zd_rf.c @@ -23,7 +23,7 @@ #include "zd_ieee80211.h" #include "zd_chip.h" -static const char *rfs[] = { +static const char * const rfs[] = { [0] = "unknown RF0", [1] = "unknown RF1", [UW2451_RF] = "UW2451_RF", @@ -34,7 +34,7 @@ static const char *rfs[] = { [AL2210_RF] = "AL2210_RF", [MAXIM_NEW_RF] = "MAXIM_NEW_RF", [UW2453_RF] = "UW2453_RF", - [AL2230S_RF] = "AL2230S_RF", + [UNKNOWN_A_RF] = "UNKNOWN_A_RF", [RALINK_RF] = "RALINK_RF", [INTERSIL_RF] = "INTERSIL_RF", [RF2959_RF] = "RF2959_RF", @@ -154,3 +154,17 @@ int zd_switch_radio_off(struct zd_rf *rf) r = t; return r; } + +int zd_rf_patch_6m_band_edge(struct zd_rf *rf, u8 channel) +{ + if (!rf->patch_6m_band_edge) + return 0; + + return rf->patch_6m_band_edge(rf, channel); +} + +int zd_rf_generic_patch_6m(struct zd_rf *rf, u8 channel) +{ + return zd_chip_generic_patch_6m_band(zd_rf_to_chip(rf), channel); +} + diff --git a/drivers/net/wireless/zd1211rw/zd_rf.h b/drivers/net/wireless/zd1211rw/zd_rf.h index a57732eb69e..aa9cc105ce6 100644 --- a/drivers/net/wireless/zd1211rw/zd_rf.h +++ b/drivers/net/wireless/zd1211rw/zd_rf.h @@ -26,7 +26,7 @@ #define AL2210_RF 0x7 #define MAXIM_NEW_RF 0x8 #define UW2453_RF 0x9 -#define AL2230S_RF 0xa +#define UNKNOWN_A_RF 0xa #define RALINK_RF 0xb #define INTERSIL_RF 0xc #define RF2959_RF 0xd @@ -47,17 +47,13 @@ struct zd_rf { u8 type; u8 channel; - /* - * Whether this RF should patch the 6M band edge - * (assuming E2P_POD agrees) - */ - u8 patch_6m_band_edge:1; /* RF-specific functions */ int (*init_hw)(struct zd_rf *rf); int (*set_channel)(struct zd_rf *rf, u8 channel); int (*switch_radio_on)(struct zd_rf *rf); int (*switch_radio_off)(struct zd_rf *rf); + int (*patch_6m_band_edge)(struct zd_rf *rf, u8 channel); }; const char *zd_rf_name(u8 type); @@ -72,6 +68,9 @@ int zd_rf_set_channel(struct zd_rf *rf, u8 channel); int zd_switch_radio_on(struct zd_rf *rf); int zd_switch_radio_off(struct zd_rf *rf); +int zd_rf_patch_6m_band_edge(struct zd_rf *rf, u8 channel); +int zd_rf_generic_patch_6m(struct zd_rf *rf, u8 channel); + /* Functions for individual RF chips */ int zd_rf_init_rf2959(struct zd_rf *rf); diff --git a/drivers/net/wireless/zd1211rw/zd_rf_al2230.c b/drivers/net/wireless/zd1211rw/zd_rf_al2230.c index 5235a7827ac..511392acfed 100644 --- a/drivers/net/wireless/zd1211rw/zd_rf_al2230.c +++ b/drivers/net/wireless/zd1211rw/zd_rf_al2230.c @@ -59,6 +59,18 @@ static const struct zd_ioreq16 zd1211b_ioreqs_shared_1[] = { { CR240, 0x57 }, { CR9, 0xe0 }, }; +static const struct zd_ioreq16 ioreqs_init_al2230s[] = { + { CR47, 0x1e }, /* MARK_002 */ + { CR106, 0x22 }, + { CR107, 0x2a }, /* MARK_002 */ + { CR109, 0x13 }, /* MARK_002 */ + { CR118, 0xf8 }, /* MARK_002 */ + { CR119, 0x12 }, { CR122, 0xe0 }, + { CR128, 0x10 }, /* MARK_001 from 0xe->0x10 */ + { CR129, 0x0e }, /* MARK_001 from 0xd->0x0e */ + { CR130, 0x10 }, /* MARK_001 from 0xb->0x0d */ +}; + static int zd1211b_al2230_finalize_rf(struct zd_chip *chip) { int r; @@ -90,7 +102,7 @@ static int zd1211_al2230_init_hw(struct zd_rf *rf) int r; struct zd_chip *chip = zd_rf_to_chip(rf); - static const struct zd_ioreq16 ioreqs[] = { + static const struct zd_ioreq16 ioreqs_init[] = { { CR15, 0x20 }, { CR23, 0x40 }, { CR24, 0x20 }, { CR26, 0x11 }, { CR28, 0x3e }, { CR29, 0x00 }, { CR44, 0x33 }, { CR106, 0x2a }, { CR107, 0x1a }, @@ -117,10 +129,9 @@ static int zd1211_al2230_init_hw(struct zd_rf *rf) { CR119, 0x10 }, { CR120, 0x4f }, { CR121, 0x77 }, { CR122, 0xe0 }, { CR137, 0x88 }, { CR252, 0xff }, { CR253, 0xff }, + }; - /* These following happen separately in the vendor driver */ - { }, - + static const struct zd_ioreq16 ioreqs_pll[] = { /* shdnb(PLL_ON)=0 */ { CR251, 0x2f }, /* shdnb(PLL_ON)=1 */ @@ -128,7 +139,7 @@ static int zd1211_al2230_init_hw(struct zd_rf *rf) { CR138, 0x28 }, { CR203, 0x06 }, }; - static const u32 rv[] = { + static const u32 rv1[] = { /* Channel 1 */ 0x03f790, 0x033331, @@ -137,6 +148,9 @@ static int zd1211_al2230_init_hw(struct zd_rf *rf) 0x0b3331, 0x03b812, 0x00fff3, + }; + + static const u32 rv2[] = { 0x000da4, 0x0f4dc5, /* fix freq shift, 0x04edc5 */ 0x0805b6, @@ -148,8 +162,9 @@ static int zd1211_al2230_init_hw(struct zd_rf *rf) 0x0bdffc, 0x00000d, 0x00500f, + }; - /* These writes happen separately in the vendor driver */ + static const u32 rv3[] = { 0x00d00f, 0x004c0f, 0x00540f, @@ -157,11 +172,38 @@ static int zd1211_al2230_init_hw(struct zd_rf *rf) 0x00500f, }; - r = zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); + r = zd_iowrite16a_locked(chip, ioreqs_init, ARRAY_SIZE(ioreqs_init)); if (r) return r; - r = zd_rfwritev_locked(chip, rv, ARRAY_SIZE(rv), RF_RV_BITS); + if (chip->al2230s_bit) { + r = zd_iowrite16a_locked(chip, ioreqs_init_al2230s, + ARRAY_SIZE(ioreqs_init_al2230s)); + if (r) + return r; + } + + r = zd_rfwritev_locked(chip, rv1, ARRAY_SIZE(rv1), RF_RV_BITS); + if (r) + return r; + + /* improve band edge for AL2230S */ + if (chip->al2230s_bit) + r = zd_rfwrite_locked(chip, 0x000824, RF_RV_BITS); + else + r = zd_rfwrite_locked(chip, 0x0005a4, RF_RV_BITS); + if (r) + return r; + + r = zd_rfwritev_locked(chip, rv2, ARRAY_SIZE(rv2), RF_RV_BITS); + if (r) + return r; + + r = zd_iowrite16a_locked(chip, ioreqs_pll, ARRAY_SIZE(ioreqs_pll)); + if (r) + return r; + + r = zd_rfwritev_locked(chip, rv3, ARRAY_SIZE(rv3), RF_RV_BITS); if (r) return r; @@ -227,7 +269,9 @@ static int zd1211b_al2230_init_hw(struct zd_rf *rf) 0x481dc0, 0xcfff00, 0x25a000, + }; + static const u32 rv2[] = { /* To improve AL2230 yield, improve phase noise, 4713 */ 0x25a000, 0xa3b2f0, @@ -250,7 +294,7 @@ static int zd1211b_al2230_init_hw(struct zd_rf *rf) { CR251, 0x7f }, /* shdnb(PLL_ON)=1 */ }; - static const u32 rv2[] = { + static const u32 rv3[] = { /* To improve AL2230 yield, 4713 */ 0xf01b00, 0xf01e00, @@ -269,18 +313,37 @@ static int zd1211b_al2230_init_hw(struct zd_rf *rf) r = zd_iowrite16a_locked(chip, ioreqs1, ARRAY_SIZE(ioreqs1)); if (r) return r; + + if (chip->al2230s_bit) { + r = zd_iowrite16a_locked(chip, ioreqs_init_al2230s, + ARRAY_SIZE(ioreqs_init_al2230s)); + if (r) + return r; + } + r = zd_rfwritev_cr_locked(chip, zd1211b_al2230_table[0], 3); if (r) return r; r = zd_rfwritev_cr_locked(chip, rv1, ARRAY_SIZE(rv1)); if (r) return r; - r = zd_iowrite16a_locked(chip, ioreqs2, ARRAY_SIZE(ioreqs2)); + + if (chip->al2230s_bit) + r = zd_rfwrite_locked(chip, 0x241000, RF_RV_BITS); + else + r = zd_rfwrite_locked(chip, 0x25a000, RF_RV_BITS); if (r) return r; + r = zd_rfwritev_cr_locked(chip, rv2, ARRAY_SIZE(rv2)); if (r) return r; + r = zd_iowrite16a_locked(chip, ioreqs2, ARRAY_SIZE(ioreqs2)); + if (r) + return r; + r = zd_rfwritev_cr_locked(chip, rv3, ARRAY_SIZE(rv3)); + if (r) + return r; r = zd_iowrite16a_locked(chip, ioreqs3, ARRAY_SIZE(ioreqs3)); if (r) return r; @@ -358,12 +421,6 @@ int zd_rf_init_al2230(struct zd_rf *rf) { struct zd_chip *chip = zd_rf_to_chip(rf); - if (chip->al2230s_bit) { - dev_err(zd_chip_dev(chip), "AL2230S devices are not yet " - "supported by this driver.\n"); - return -ENODEV; - } - rf->switch_radio_off = al2230_switch_radio_off; if (chip->is_zd1211b) { rf->init_hw = zd1211b_al2230_init_hw; @@ -374,6 +431,6 @@ int zd_rf_init_al2230(struct zd_rf *rf) rf->set_channel = zd1211_al2230_set_channel; rf->switch_radio_on = zd1211_al2230_switch_radio_on; } - rf->patch_6m_band_edge = 1; + rf->patch_6m_band_edge = zd_rf_generic_patch_6m; return 0; } diff --git a/drivers/net/wireless/zd1211rw/zd_rf_al7230b.c b/drivers/net/wireless/zd1211rw/zd_rf_al7230b.c index a289f95187e..5e5e9ddc6a7 100644 --- a/drivers/net/wireless/zd1211rw/zd_rf_al7230b.c +++ b/drivers/net/wireless/zd1211rw/zd_rf_al7230b.c @@ -51,9 +51,52 @@ static const u32 std_rv[] = { 0xd8c010, }; -static int al7230b_init_hw(struct zd_rf *rf) +static const u32 rv_init1[] = { + 0x3c9000, + 0xbfffff, + 0x700000, + 0xf15d58, +}; + +static const u32 rv_init2[] = { + 0xf15d59, + 0xf15d5c, + 0xf15d58, +}; + +static const struct zd_ioreq16 ioreqs_sw[] = { + { CR128, 0x14 }, { CR129, 0x12 }, { CR130, 0x10 }, + { CR38, 0x38 }, { CR136, 0xdf }, +}; + +static int zd1211b_al7230b_finalize(struct zd_chip *chip) { - int i, r; + int r; + static const struct zd_ioreq16 ioreqs[] = { + { CR80, 0x30 }, { CR81, 0x30 }, { CR79, 0x58 }, + { CR12, 0xf0 }, { CR77, 0x1b }, { CR78, 0x58 }, + { CR203, 0x04 }, + { }, + { CR240, 0x80 }, + }; + + r = zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); + if (r) + return r; + + if (chip->new_phy_layout) { + /* antenna selection? */ + r = zd_iowrite16_locked(chip, 0xe5, CR9); + if (r) + return r; + } + + return zd_iowrite16_locked(chip, 0x04, CR203); +} + +static int zd1211_al7230b_init_hw(struct zd_rf *rf) +{ + int r; struct zd_chip *chip = zd_rf_to_chip(rf); /* All of these writes are identical to AL2230 unless otherwise @@ -117,39 +160,136 @@ static int al7230b_init_hw(struct zd_rf *rf) }; static const struct zd_ioreq16 ioreqs_2[] = { - /* PLL_ON */ - { CR251, 0x3f }, + { CR251, 0x3f }, /* PLL_ON */ { CR128, 0x14 }, { CR129, 0x12 }, { CR130, 0x10 }, - { CR38, 0x38 }, { CR136, 0xdf }, + { CR38, 0x38 }, { CR136, 0xdf }, }; r = zd_iowrite16a_locked(chip, ioreqs_1, ARRAY_SIZE(ioreqs_1)); if (r) return r; - r = zd_rfwrite_cr_locked(chip, 0x09ec04); + r = zd_rfwritev_cr_locked(chip, chan_rv[0], ARRAY_SIZE(chan_rv[0])); if (r) return r; - r = zd_rfwrite_cr_locked(chip, 0x8cccc8); + + r = zd_rfwritev_cr_locked(chip, std_rv, ARRAY_SIZE(std_rv)); if (r) return r; - for (i = 0; i < ARRAY_SIZE(std_rv); i++) { - r = zd_rfwrite_cr_locked(chip, std_rv[i]); - if (r) - return r; - } + r = zd_rfwritev_cr_locked(chip, rv_init1, ARRAY_SIZE(rv_init1)); + if (r) + return r; - r = zd_rfwrite_cr_locked(chip, 0x3c9000); + r = zd_iowrite16a_locked(chip, ioreqs_2, ARRAY_SIZE(ioreqs_2)); if (r) return r; - r = zd_rfwrite_cr_locked(chip, 0xbfffff); + + r = zd_rfwritev_cr_locked(chip, rv_init2, ARRAY_SIZE(rv_init2)); if (r) return r; - r = zd_rfwrite_cr_locked(chip, 0x700000); + + r = zd_iowrite16_locked(chip, 0x06, CR203); if (r) return r; - r = zd_rfwrite_cr_locked(chip, 0xf15d58); + r = zd_iowrite16_locked(chip, 0x80, CR240); + if (r) + return r; + + return 0; +} + +static int zd1211b_al7230b_init_hw(struct zd_rf *rf) +{ + int r; + struct zd_chip *chip = zd_rf_to_chip(rf); + + static const struct zd_ioreq16 ioreqs_1[] = { + { CR240, 0x57 }, { CR9, 0x9 }, + { }, + { CR10, 0x8b }, { CR15, 0x20 }, + { CR17, 0x2B }, /* for newest (3rd cut) AL2230 */ + { CR20, 0x10 }, /* 4N25->Stone Request */ + { CR23, 0x40 }, { CR24, 0x20 }, { CR26, 0x93 }, + { CR28, 0x3e }, { CR29, 0x00 }, + { CR33, 0x28 }, /* 5613 */ + { CR34, 0x30 }, + { CR35, 0x3e }, /* for newest (3rd cut) AL2230 */ + { CR41, 0x24 }, { CR44, 0x32 }, + { CR46, 0x99 }, /* for newest (3rd cut) AL2230 */ + { CR47, 0x1e }, + + /* ZD1215 5610 */ + { CR48, 0x00 }, { CR49, 0x00 }, { CR51, 0x01 }, + { CR52, 0x80 }, { CR53, 0x7e }, { CR65, 0x00 }, + { CR66, 0x00 }, { CR67, 0x00 }, { CR68, 0x00 }, + { CR69, 0x28 }, + + { CR79, 0x58 }, { CR80, 0x30 }, { CR81, 0x30 }, + { CR87, 0x0A }, { CR89, 0x04 }, + { CR90, 0x58 }, /* 5112 */ + { CR91, 0x00 }, /* 5613 */ + { CR92, 0x0a }, + { CR98, 0x8d }, /* 4804, for 1212 new algorithm */ + { CR99, 0x00 }, { CR100, 0x02 }, { CR101, 0x13 }, + { CR102, 0x27 }, + { CR106, 0x20 }, /* change to 0x24 for AL7230B */ + { CR109, 0x13 }, /* 4804, for 1212 new algorithm */ + { CR112, 0x1f }, + }; + + static const struct zd_ioreq16 ioreqs_new_phy[] = { + { CR107, 0x28 }, + { CR110, 0x1f }, /* 5127, 0x13->0x1f */ + { CR111, 0x1f }, /* 0x13 to 0x1f for AL7230B */ + { CR116, 0x2a }, { CR118, 0xfa }, { CR119, 0x12 }, + { CR121, 0x6c }, /* 5613 */ + }; + + static const struct zd_ioreq16 ioreqs_old_phy[] = { + { CR107, 0x24 }, + { CR110, 0x13 }, /* 5127, 0x13->0x1f */ + { CR111, 0x13 }, /* 0x13 to 0x1f for AL7230B */ + { CR116, 0x24 }, { CR118, 0xfc }, { CR119, 0x11 }, + { CR121, 0x6a }, /* 5613 */ + }; + + static const struct zd_ioreq16 ioreqs_2[] = { + { CR113, 0x27 }, { CR114, 0x27 }, { CR115, 0x24 }, + { CR117, 0xfa }, { CR120, 0x4f }, + { CR122, 0xfc }, /* E0->FCh at 4901 */ + { CR123, 0x57 }, /* 5613 */ + { CR125, 0xad }, /* 4804, for 1212 new algorithm */ + { CR126, 0x6c }, /* 5613 */ + { CR127, 0x03 }, /* 4804, for 1212 new algorithm */ + { CR130, 0x10 }, + { CR131, 0x00 }, /* 5112 */ + { CR137, 0x50 }, /* 5613 */ + { CR138, 0xa8 }, /* 5112 */ + { CR144, 0xac }, /* 5613 */ + { CR148, 0x40 }, /* 5112 */ + { CR149, 0x40 }, /* 4O07, 50->40 */ + { CR150, 0x1a }, /* 5112, 0C->1A */ + { CR252, 0x34 }, { CR253, 0x34 }, + { CR251, 0x2f }, /* PLL_OFF */ + }; + + static const struct zd_ioreq16 ioreqs_3[] = { + { CR251, 0x7f }, /* PLL_ON */ + { CR128, 0x14 }, { CR129, 0x12 }, { CR130, 0x10 }, + { CR38, 0x38 }, { CR136, 0xdf }, + }; + + r = zd_iowrite16a_locked(chip, ioreqs_1, ARRAY_SIZE(ioreqs_1)); + if (r) + return r; + + if (chip->new_phy_layout) + r = zd_iowrite16a_locked(chip, ioreqs_new_phy, + ARRAY_SIZE(ioreqs_new_phy)); + else + r = zd_iowrite16a_locked(chip, ioreqs_old_phy, + ARRAY_SIZE(ioreqs_old_phy)); if (r) return r; @@ -157,38 +297,36 @@ static int al7230b_init_hw(struct zd_rf *rf) if (r) return r; - r = zd_rfwrite_cr_locked(chip, 0xf15d59); + r = zd_rfwritev_cr_locked(chip, chan_rv[0], ARRAY_SIZE(chan_rv[0])); if (r) return r; - r = zd_rfwrite_cr_locked(chip, 0xf15d5c); + + r = zd_rfwritev_cr_locked(chip, std_rv, ARRAY_SIZE(std_rv)); if (r) return r; - r = zd_rfwrite_cr_locked(chip, 0xf15d58); + + r = zd_rfwritev_cr_locked(chip, rv_init1, ARRAY_SIZE(rv_init1)); if (r) return r; - r = zd_iowrite16_locked(chip, 0x06, CR203); + r = zd_iowrite16a_locked(chip, ioreqs_3, ARRAY_SIZE(ioreqs_3)); if (r) return r; - r = zd_iowrite16_locked(chip, 0x80, CR240); + + r = zd_rfwritev_cr_locked(chip, rv_init2, ARRAY_SIZE(rv_init2)); if (r) return r; - return 0; + return zd1211b_al7230b_finalize(chip); } -static int al7230b_set_channel(struct zd_rf *rf, u8 channel) +static int zd1211_al7230b_set_channel(struct zd_rf *rf, u8 channel) { - int i, r; + int r; const u32 *rv = chan_rv[channel-1]; struct zd_chip *chip = zd_rf_to_chip(rf); - struct zd_ioreq16 ioreqs_1[] = { - { CR128, 0x14 }, { CR129, 0x12 }, { CR130, 0x10 }, - { CR38, 0x38 }, { CR136, 0xdf }, - }; - - struct zd_ioreq16 ioreqs_2[] = { + static const struct zd_ioreq16 ioreqs[] = { /* PLL_ON */ { CR251, 0x3f }, { CR203, 0x06 }, { CR240, 0x08 }, @@ -203,11 +341,9 @@ static int al7230b_set_channel(struct zd_rf *rf, u8 channel) if (r) return r; - for (i = 0; i < ARRAY_SIZE(std_rv); i++) { - r = zd_rfwrite_cr_locked(chip, std_rv[i]); - if (r) - return r; - } + r = zd_rfwritev_cr_locked(chip, std_rv, ARRAY_SIZE(std_rv)); + if (r) + return r; r = zd_rfwrite_cr_locked(chip, 0x3c9000); if (r) @@ -216,24 +352,69 @@ static int al7230b_set_channel(struct zd_rf *rf, u8 channel) if (r) return r; - r = zd_iowrite16a_locked(chip, ioreqs_1, ARRAY_SIZE(ioreqs_1)); + r = zd_iowrite16a_locked(chip, ioreqs_sw, ARRAY_SIZE(ioreqs_sw)); if (r) return r; - for (i = 0; i < 2; i++) { - r = zd_rfwrite_cr_locked(chip, rv[i]); - if (r) - return r; - } + r = zd_rfwritev_cr_locked(chip, rv, 2); + if (r) + return r; r = zd_rfwrite_cr_locked(chip, 0x3c9000); if (r) return r; - return zd_iowrite16a_locked(chip, ioreqs_2, ARRAY_SIZE(ioreqs_2)); + return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); } -static int al7230b_switch_radio_on(struct zd_rf *rf) +static int zd1211b_al7230b_set_channel(struct zd_rf *rf, u8 channel) +{ + int r; + const u32 *rv = chan_rv[channel-1]; + struct zd_chip *chip = zd_rf_to_chip(rf); + + r = zd_iowrite16_locked(chip, 0x57, CR240); + if (r) + return r; + r = zd_iowrite16_locked(chip, 0xe4, CR9); + if (r) + return r; + + /* PLL_OFF */ + r = zd_iowrite16_locked(chip, 0x2f, CR251); + if (r) + return r; + r = zd_rfwritev_cr_locked(chip, std_rv, ARRAY_SIZE(std_rv)); + if (r) + return r; + + r = zd_rfwrite_cr_locked(chip, 0x3c9000); + if (r) + return r; + r = zd_rfwrite_cr_locked(chip, 0xf15d58); + if (r) + return r; + + r = zd_iowrite16a_locked(chip, ioreqs_sw, ARRAY_SIZE(ioreqs_sw)); + if (r) + return r; + + r = zd_rfwritev_cr_locked(chip, rv, 2); + if (r) + return r; + + r = zd_rfwrite_cr_locked(chip, 0x3c9000); + if (r) + return r; + + r = zd_iowrite16_locked(chip, 0x7f, CR251); + if (r) + return r; + + return zd1211b_al7230b_finalize(chip); +} + +static int zd1211_al7230b_switch_radio_on(struct zd_rf *rf) { struct zd_chip *chip = zd_rf_to_chip(rf); static const struct zd_ioreq16 ioreqs[] = { @@ -244,6 +425,17 @@ static int al7230b_switch_radio_on(struct zd_rf *rf) return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); } +static int zd1211b_al7230b_switch_radio_on(struct zd_rf *rf) +{ + struct zd_chip *chip = zd_rf_to_chip(rf); + static const struct zd_ioreq16 ioreqs[] = { + { CR11, 0x00 }, + { CR251, 0x7f }, + }; + + return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); +} + static int al7230b_switch_radio_off(struct zd_rf *rf) { struct zd_chip *chip = zd_rf_to_chip(rf); @@ -255,20 +447,45 @@ static int al7230b_switch_radio_off(struct zd_rf *rf) return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); } +/* ZD1211B+AL7230B 6m band edge patching differs slightly from other + * configurations */ +static int zd1211b_al7230b_patch_6m(struct zd_rf *rf, u8 channel) +{ + struct zd_chip *chip = zd_rf_to_chip(rf); + struct zd_ioreq16 ioreqs[] = { + { CR128, 0x14 }, { CR129, 0x12 }, + }; + + /* FIXME: Channel 11 is not the edge for all regulatory domains. */ + if (channel == 1) { + ioreqs[0].value = 0x0e; + ioreqs[1].value = 0x10; + } else if (channel == 11) { + ioreqs[0].value = 0x10; + ioreqs[1].value = 0x10; + } + + dev_dbg_f(zd_chip_dev(chip), "patching for channel %d\n", channel); + return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); +} + int zd_rf_init_al7230b(struct zd_rf *rf) { struct zd_chip *chip = zd_rf_to_chip(rf); if (chip->is_zd1211b) { - dev_err(zd_chip_dev(chip), "AL7230B is currently not " - "supported for ZD1211B devices\n"); - return -ENODEV; + rf->init_hw = zd1211b_al7230b_init_hw; + rf->switch_radio_on = zd1211b_al7230b_switch_radio_on; + rf->set_channel = zd1211b_al7230b_set_channel; + rf->patch_6m_band_edge = zd1211b_al7230b_patch_6m; + } else { + rf->init_hw = zd1211_al7230b_init_hw; + rf->switch_radio_on = zd1211_al7230b_switch_radio_on; + rf->set_channel = zd1211_al7230b_set_channel; + rf->patch_6m_band_edge = zd_rf_generic_patch_6m; } - rf->init_hw = al7230b_init_hw; - rf->set_channel = al7230b_set_channel; - rf->switch_radio_on = al7230b_switch_radio_on; rf->switch_radio_off = al7230b_switch_radio_off; - rf->patch_6m_band_edge = 1; + return 0; } diff --git a/drivers/net/wireless/zd1211rw/zd_rf_rf2959.c b/drivers/net/wireless/zd1211rw/zd_rf_rf2959.c index 58247271cc2..2d736bdf707 100644 --- a/drivers/net/wireless/zd1211rw/zd_rf_rf2959.c +++ b/drivers/net/wireless/zd1211rw/zd_rf_rf2959.c @@ -21,7 +21,7 @@ #include "zd_usb.h" #include "zd_chip.h" -static u32 rf2959_table[][2] = { +static const u32 rf2959_table[][2] = { RF_CHANNEL( 1) = { 0x181979, 0x1e6666 }, RF_CHANNEL( 2) = { 0x181989, 0x1e6666 }, RF_CHANNEL( 3) = { 0x181999, 0x1e6666 }, @@ -228,7 +228,7 @@ static int rf2959_init_hw(struct zd_rf *rf) static int rf2959_set_channel(struct zd_rf *rf, u8 channel) { int i, r; - u32 *rv = rf2959_table[channel-1]; + const u32 *rv = rf2959_table[channel-1]; struct zd_chip *chip = zd_rf_to_chip(rf); for (i = 0; i < 2; i++) { diff --git a/drivers/net/wireless/zd1211rw/zd_usb.c b/drivers/net/wireless/zd1211rw/zd_usb.c index edaaad2f648..e04cffc8adf 100644 --- a/drivers/net/wireless/zd1211rw/zd_usb.c +++ b/drivers/net/wireless/zd1211rw/zd_usb.c @@ -52,6 +52,7 @@ static struct usb_device_id usb_ids[] = { { USB_DEVICE(0x0b3b, 0x1630), .driver_info = DEVICE_ZD1211 }, { USB_DEVICE(0x0586, 0x3401), .driver_info = DEVICE_ZD1211 }, { USB_DEVICE(0x14ea, 0xab13), .driver_info = DEVICE_ZD1211 }, + { USB_DEVICE(0x13b1, 0x001e), .driver_info = DEVICE_ZD1211 }, /* ZD1211B */ { USB_DEVICE(0x0ace, 0x1215), .driver_info = DEVICE_ZD1211B }, { USB_DEVICE(0x157e, 0x300d), .driver_info = DEVICE_ZD1211B }, @@ -62,7 +63,10 @@ static struct usb_device_id usb_ids[] = { { USB_DEVICE(0x0471, 0x1236), .driver_info = DEVICE_ZD1211B }, { USB_DEVICE(0x13b1, 0x0024), .driver_info = DEVICE_ZD1211B }, { USB_DEVICE(0x0586, 0x340f), .driver_info = DEVICE_ZD1211B }, + { USB_DEVICE(0x0b05, 0x171b), .driver_info = DEVICE_ZD1211B }, + { USB_DEVICE(0x0586, 0x3410), .driver_info = DEVICE_ZD1211B }, { USB_DEVICE(0x0baf, 0x0121), .driver_info = DEVICE_ZD1211B }, + { USB_DEVICE(0x0586, 0x3412), .driver_info = DEVICE_ZD1211B }, /* "Driverless" devices that need ejecting */ { USB_DEVICE(0x0ace, 0x2011), .driver_info = DEVICE_INSTALLER }, {} @@ -413,7 +417,7 @@ int zd_usb_enable_int(struct zd_usb *usb) dev_dbg_f(zd_usb_dev(usb), "\n"); - urb = usb_alloc_urb(0, GFP_NOFS); + urb = usb_alloc_urb(0, GFP_KERNEL); if (!urb) { r = -ENOMEM; goto out; @@ -431,7 +435,7 @@ int zd_usb_enable_int(struct zd_usb *usb) /* TODO: make it a DMA buffer */ r = -ENOMEM; - transfer_buffer = kmalloc(USB_MAX_EP_INT_BUFFER, GFP_NOFS); + transfer_buffer = kmalloc(USB_MAX_EP_INT_BUFFER, GFP_KERNEL); if (!transfer_buffer) { dev_dbg_f(zd_usb_dev(usb), "couldn't allocate transfer_buffer\n"); @@ -445,7 +449,7 @@ int zd_usb_enable_int(struct zd_usb *usb) intr->interval); dev_dbg_f(zd_usb_dev(usb), "submit urb %p\n", intr->urb); - r = usb_submit_urb(urb, GFP_NOFS); + r = usb_submit_urb(urb, GFP_KERNEL); if (r) { dev_dbg_f(zd_usb_dev(usb), "Couldn't submit urb. Error number %d\n", r); @@ -594,10 +598,10 @@ static struct urb *alloc_urb(struct zd_usb *usb) struct urb *urb; void *buffer; - urb = usb_alloc_urb(0, GFP_NOFS); + urb = usb_alloc_urb(0, GFP_KERNEL); if (!urb) return NULL; - buffer = usb_buffer_alloc(udev, USB_MAX_RX_SIZE, GFP_NOFS, + buffer = usb_buffer_alloc(udev, USB_MAX_RX_SIZE, GFP_KERNEL, &urb->transfer_dma); if (!buffer) { usb_free_urb(urb); @@ -630,7 +634,7 @@ int zd_usb_enable_rx(struct zd_usb *usb) dev_dbg_f(zd_usb_dev(usb), "\n"); r = -ENOMEM; - urbs = kcalloc(URBS_COUNT, sizeof(struct urb *), GFP_NOFS); + urbs = kcalloc(URBS_COUNT, sizeof(struct urb *), GFP_KERNEL); if (!urbs) goto error; for (i = 0; i < URBS_COUNT; i++) { @@ -651,7 +655,7 @@ int zd_usb_enable_rx(struct zd_usb *usb) spin_unlock_irq(&rx->lock); for (i = 0; i < URBS_COUNT; i++) { - r = usb_submit_urb(urbs[i], GFP_NOFS); + r = usb_submit_urb(urbs[i], GFP_KERNEL); if (r) goto error_submit; } @@ -1157,7 +1161,7 @@ int zd_usb_ioread16v(struct zd_usb *usb, u16 *values, } req_len = sizeof(struct usb_req_read_regs) + count * sizeof(__le16); - req = kmalloc(req_len, GFP_NOFS); + req = kmalloc(req_len, GFP_KERNEL); if (!req) return -ENOMEM; req->id = cpu_to_le16(USB_REQ_READ_REGS); @@ -1220,7 +1224,7 @@ int zd_usb_iowrite16v(struct zd_usb *usb, const struct zd_ioreq16 *ioreqs, req_len = sizeof(struct usb_req_write_regs) + count * sizeof(struct reg_data); - req = kmalloc(req_len, GFP_NOFS); + req = kmalloc(req_len, GFP_KERNEL); if (!req) return -ENOMEM; @@ -1300,7 +1304,7 @@ int zd_usb_rfwrite(struct zd_usb *usb, u32 value, u8 bits) bit_value_template &= ~(RF_IF_LE|RF_CLK|RF_DATA); req_len = sizeof(struct usb_req_rfwrite) + bits * sizeof(__le16); - req = kmalloc(req_len, GFP_NOFS); + req = kmalloc(req_len, GFP_KERNEL); if (!req) return -ENOMEM; |