]> nv-tegra.nvidia Code Review - linux-2.6.git/commitdiff
/spare/repo/netdev-2.6 branch 'ieee80211'
authorJeff Garzik <jgarzik@pobox.com>
Mon, 15 Aug 2005 03:10:26 +0000 (23:10 -0400)
committerJeff Garzik <jgarzik@pobox.com>
Mon, 15 Aug 2005 03:10:26 +0000 (23:10 -0400)
29 files changed:
MAINTAINERS
drivers/net/wireless/Kconfig
drivers/net/wireless/Makefile
drivers/net/wireless/airo.c
drivers/net/wireless/hostap/Kconfig [new file with mode: 0644]
drivers/net/wireless/hostap/Makefile [new file with mode: 0644]
drivers/net/wireless/hostap/hostap.c [new file with mode: 0644]
drivers/net/wireless/hostap/hostap.h [new file with mode: 0644]
drivers/net/wireless/hostap/hostap_80211.h [new file with mode: 0644]
drivers/net/wireless/hostap/hostap_80211_rx.c [new file with mode: 0644]
drivers/net/wireless/hostap/hostap_80211_tx.c [new file with mode: 0644]
drivers/net/wireless/hostap/hostap_ap.c [new file with mode: 0644]
drivers/net/wireless/hostap/hostap_ap.h [new file with mode: 0644]
drivers/net/wireless/hostap/hostap_common.h [new file with mode: 0644]
drivers/net/wireless/hostap/hostap_config.h [new file with mode: 0644]
drivers/net/wireless/hostap/hostap_cs.c [new file with mode: 0644]
drivers/net/wireless/hostap/hostap_download.c [new file with mode: 0644]
drivers/net/wireless/hostap/hostap_hw.c [new file with mode: 0644]
drivers/net/wireless/hostap/hostap_info.c [new file with mode: 0644]
drivers/net/wireless/hostap/hostap_ioctl.c [new file with mode: 0644]
drivers/net/wireless/hostap/hostap_pci.c [new file with mode: 0644]
drivers/net/wireless/hostap/hostap_plx.c [new file with mode: 0644]
drivers/net/wireless/hostap/hostap_proc.c [new file with mode: 0644]
drivers/net/wireless/hostap/hostap_wlan.h [new file with mode: 0644]
drivers/net/wireless/strip.c
drivers/net/wireless/wavelan_cs.c
drivers/net/wireless/wavelan_cs.h
drivers/net/wireless/wavelan_cs.p.h
drivers/net/wireless/wl3501_cs.c

index 3b38d6ab06cfc8b846758ee6f555d64e71fd0414..473a08a9cc3ddcabc6769d3586021514747ea3e3 100644 (file)
@@ -991,6 +991,13 @@ M: mike.miller@hp.com
 L:     iss_storagedev@hp.com
 S:     Supported
  
+HOST AP DRIVER
+P:     Jouni Malinen
+M:     jkmaline@cc.hut.fi
+L:     hostap@shmoo.com
+W:     http://hostap.epitest.fi/
+S:     Maintained
+
 HP100: Driver for HP 10/100 Mbit/s Voice Grade Network Adapter Series
 P:     Jaroslav Kysela
 M:     perex@suse.cz
index d20e0da05a26eab996841f5c678f11d54a8fb460..ff5b1b1de2fa2400cc0493dc4d3aa4b9ea4312f9 100644 (file)
@@ -459,6 +459,8 @@ config PRISM54
          say M here and read <file:Documentation/modules.txt>.  The module
          will be called prism54.ko.
 
+source "drivers/net/wireless/hostap/Kconfig"
+
 # yes, this works even when no drivers are selected
 config NET_WIRELESS
        bool
index 0859787581bb0c084cf5b0aa4c2987d5be442dfd..0953cc0cdee66c81b23f8f138e1f94c85b2eafeb 100644 (file)
@@ -32,6 +32,8 @@ obj-$(CONFIG_PCMCIA_ATMEL)      += atmel_cs.o
 
 obj-$(CONFIG_PRISM54)          += prism54/
 
+obj-$(CONFIG_HOSTAP)           += hostap/
+
 # 16-bit wireless PCMCIA client drivers
 obj-$(CONFIG_PCMCIA_RAYCS)     += ray_cs.o
 obj-$(CONFIG_PCMCIA_WL3501)    += wl3501_cs.o
index df20adcd0730aa1cf5fb899d7b2017209782fb6c..6db1fb6461def034c898badd7c9a30bda7c4383d 100644 (file)
@@ -1040,7 +1040,7 @@ typedef struct {
        u16 status;
 } WifiCtlHdr;
 
-WifiCtlHdr wifictlhdr8023 = {
+static WifiCtlHdr wifictlhdr8023 = {
        .ctlhdr = {
                .ctl    = HOST_DONT_RLSE,
        }
@@ -1111,13 +1111,13 @@ static int airo_thread(void *data);
 static void timer_func( struct net_device *dev );
 static int airo_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
 #ifdef WIRELESS_EXT
-struct iw_statistics *airo_get_wireless_stats (struct net_device *dev);
+static struct iw_statistics *airo_get_wireless_stats (struct net_device *dev);
 static void airo_read_wireless_stats (struct airo_info *local);
 #endif /* WIRELESS_EXT */
 #ifdef CISCO_EXT
 static int readrids(struct net_device *dev, aironet_ioctl *comp);
 static int writerids(struct net_device *dev, aironet_ioctl *comp);
-int flashcard(struct net_device *dev, aironet_ioctl *comp);
+static int flashcard(struct net_device *dev, aironet_ioctl *comp);
 #endif /* CISCO_EXT */
 #ifdef MICSUPPORT
 static void micinit(struct airo_info *ai);
@@ -1226,6 +1226,12 @@ static int setup_proc_entry( struct net_device *dev,
 static int takedown_proc_entry( struct net_device *dev,
                                struct airo_info *apriv );
 
+static int cmdreset(struct airo_info *ai);
+static int setflashmode (struct airo_info *ai);
+static int flashgchar(struct airo_info *ai,int matchbyte,int dwelltime);
+static int flashputbuf(struct airo_info *ai);
+static int flashrestart(struct airo_info *ai,struct net_device *dev);
+
 #ifdef MICSUPPORT
 /***********************************************************************
  *                              MIC ROUTINES                           *
@@ -1234,10 +1240,11 @@ static int takedown_proc_entry( struct net_device *dev,
 
 static int RxSeqValid (struct airo_info *ai,miccntx *context,int mcast,u32 micSeq);
 static void MoveWindow(miccntx *context, u32 micSeq);
-void emmh32_setseed(emmh32_context *context, u8 *pkey, int keylen, struct crypto_tfm *);
-void emmh32_init(emmh32_context *context);
-void emmh32_update(emmh32_context *context, u8 *pOctets, int len);
-void emmh32_final(emmh32_context *context, u8 digest[4]);
+static void emmh32_setseed(emmh32_context *context, u8 *pkey, int keylen, struct crypto_tfm *);
+static void emmh32_init(emmh32_context *context);
+static void emmh32_update(emmh32_context *context, u8 *pOctets, int len);
+static void emmh32_final(emmh32_context *context, u8 digest[4]);
+static int flashpchar(struct airo_info *ai,int byte,int dwelltime);
 
 /* micinit - Initialize mic seed */
 
@@ -1315,7 +1322,7 @@ static int micsetup(struct airo_info *ai) {
        return SUCCESS;
 }
 
-char micsnap[]= {0xAA,0xAA,0x03,0x00,0x40,0x96,0x00,0x02};
+static char micsnap[] = {0xAA,0xAA,0x03,0x00,0x40,0x96,0x00,0x02};
 
 /*===========================================================================
  * Description: Mic a packet
@@ -1570,7 +1577,7 @@ static void MoveWindow(miccntx *context, u32 micSeq)
 static unsigned char aes_counter[16];
 
 /* expand the key to fill the MMH coefficient array */
-void emmh32_setseed(emmh32_context *context, u8 *pkey, int keylen, struct crypto_tfm *tfm)
+static void emmh32_setseed(emmh32_context *context, u8 *pkey, int keylen, struct crypto_tfm *tfm)
 {
   /* take the keying material, expand if necessary, truncate at 16-bytes */
   /* run through AES counter mode to generate context->coeff[] */
@@ -1602,7 +1609,7 @@ void emmh32_setseed(emmh32_context *context, u8 *pkey, int keylen, struct crypto
 }
 
 /* prepare for calculation of a new mic */
-void emmh32_init(emmh32_context *context)
+static void emmh32_init(emmh32_context *context)
 {
        /* prepare for new mic calculation */
        context->accum = 0;
@@ -1610,7 +1617,7 @@ void emmh32_init(emmh32_context *context)
 }
 
 /* add some bytes to the mic calculation */
-void emmh32_update(emmh32_context *context, u8 *pOctets, int len)
+static void emmh32_update(emmh32_context *context, u8 *pOctets, int len)
 {
        int     coeff_position, byte_position;
   
@@ -1652,7 +1659,7 @@ void emmh32_update(emmh32_context *context, u8 *pOctets, int len)
 static u32 mask32[4] = { 0x00000000L, 0xFF000000L, 0xFFFF0000L, 0xFFFFFF00L };
 
 /* calculate the mic */
-void emmh32_final(emmh32_context *context, u8 digest[4])
+static void emmh32_final(emmh32_context *context, u8 digest[4])
 {
        int     coeff_position, byte_position;
        u32     val;
@@ -2255,7 +2262,7 @@ static void airo_read_stats(struct airo_info *ai) {
        ai->stats.rx_fifo_errors = vals[0];
 }
 
-struct net_device_stats *airo_get_stats(struct net_device *dev)
+static struct net_device_stats *airo_get_stats(struct net_device *dev)
 {
        struct airo_info *local =  dev->priv;
 
@@ -2414,7 +2421,7 @@ EXPORT_SYMBOL(stop_airo_card);
 
 static int add_airo_dev( struct net_device *dev );
 
-int wll_header_parse(struct sk_buff *skb, unsigned char *haddr)
+static int wll_header_parse(struct sk_buff *skb, unsigned char *haddr)
 {
        memcpy(haddr, skb->mac.raw + 10, ETH_ALEN);
        return ETH_ALEN;
@@ -2681,7 +2688,7 @@ static struct net_device *init_wifidev(struct airo_info *ai,
        return dev;
 }
 
-int reset_card( struct net_device *dev , int lock) {
+static int reset_card( struct net_device *dev , int lock) {
        struct airo_info *ai = dev->priv;
 
        if (lock && down_interruptible(&ai->sem))
@@ -2696,9 +2703,9 @@ int reset_card( struct net_device *dev , int lock) {
        return 0;
 }
 
-struct net_device *_init_airo_card( unsigned short irq, int port,
-                                   int is_pcmcia, struct pci_dev *pci,
-                                   struct device *dmdev )
+static struct net_device *_init_airo_card( unsigned short irq, int port,
+                                          int is_pcmcia, struct pci_dev *pci,
+                                          struct device *dmdev )
 {
        struct net_device *dev;
        struct airo_info *ai;
@@ -7235,7 +7242,7 @@ static void airo_read_wireless_stats(struct airo_info *local)
        local->wstats.miss.beacon = vals[34];
 }
 
-struct iw_statistics *airo_get_wireless_stats(struct net_device *dev)
+static struct iw_statistics *airo_get_wireless_stats(struct net_device *dev)
 {
        struct airo_info *local =  dev->priv;
 
@@ -7450,14 +7457,8 @@ static int writerids(struct net_device *dev, aironet_ioctl *comp) {
  * Flash command switch table
  */
 
-int flashcard(struct net_device *dev, aironet_ioctl *comp) {
+static int flashcard(struct net_device *dev, aironet_ioctl *comp) {
        int z;
-       int cmdreset(struct airo_info *);
-       int setflashmode(struct airo_info *);
-       int flashgchar(struct airo_info *,int,int);
-       int flashpchar(struct airo_info *,int,int);
-       int flashputbuf(struct airo_info *);
-       int flashrestart(struct airo_info *,struct net_device *);
 
        /* Only super-user can modify flash */
        if (!capable(CAP_NET_ADMIN))
@@ -7515,7 +7516,7 @@ int flashcard(struct net_device *dev, aironet_ioctl *comp) {
  * card.
  */
 
-int cmdreset(struct airo_info *ai) {
+static int cmdreset(struct airo_info *ai) {
        disable_MAC(ai, 1);
 
        if(!waitbusy (ai)){
@@ -7539,7 +7540,7 @@ int cmdreset(struct airo_info *ai) {
  * mode
  */
 
-int setflashmode (struct airo_info *ai) {
+static int setflashmode (struct airo_info *ai) {
        set_bit (FLAG_FLASHING, &ai->flags);
 
        OUT4500(ai, SWS0, FLASH_COMMAND);
@@ -7566,7 +7567,7 @@ int setflashmode (struct airo_info *ai) {
  * x 50us for  echo .
  */
 
-int flashpchar(struct airo_info *ai,int byte,int dwelltime) {
+static int flashpchar(struct airo_info *ai,int byte,int dwelltime) {
        int echo;
        int waittime;
 
@@ -7606,7 +7607,7 @@ int flashpchar(struct airo_info *ai,int byte,int dwelltime) {
  * Get a character from the card matching matchbyte
  * Step 3)
  */
-int flashgchar(struct airo_info *ai,int matchbyte,int dwelltime){
+static int flashgchar(struct airo_info *ai,int matchbyte,int dwelltime){
        int           rchar;
        unsigned char rbyte=0;
 
@@ -7637,7 +7638,7 @@ int flashgchar(struct airo_info *ai,int matchbyte,int dwelltime){
  * send to the card
  */
 
-int flashputbuf(struct airo_info *ai){
+static int flashputbuf(struct airo_info *ai){
        int            nwords;
 
        /* Write stuff */
@@ -7659,7 +7660,7 @@ int flashputbuf(struct airo_info *ai){
 /*
  *
  */
-int flashrestart(struct airo_info *ai,struct net_device *dev){
+static int flashrestart(struct airo_info *ai,struct net_device *dev){
        int    i,status;
 
        ssleep(1);                      /* Added 12/7/00 */
diff --git a/drivers/net/wireless/hostap/Kconfig b/drivers/net/wireless/hostap/Kconfig
new file mode 100644 (file)
index 0000000..1445f3f
--- /dev/null
@@ -0,0 +1,71 @@
+config HOSTAP
+       tristate "IEEE 802.11 for Host AP (Prism2/2.5/3 and WEP/TKIP/CCMP)"
+       depends on NET_RADIO
+       ---help---
+       Shared driver code for IEEE 802.11b wireless cards based on
+       Intersil Prism2/2.5/3 chipset. This driver supports so called
+       Host AP mode that allows the card to act as an IEEE 802.11
+       access point.
+
+       See <http://hostap.epitest.fi/> for more information about the
+       Host AP driver configuration and tools. This site includes
+       information and tools (hostapd and wpa_supplicant) for WPA/WPA2
+       support.
+
+       This option includes the base Host AP driver code that is shared by
+       different hardware models. You will also need to enable support for
+       PLX/PCI/CS version of the driver to actually use the driver.
+
+       The driver can be compiled as a module and it will be called
+       "hostap.ko".
+
+config HOSTAP_FIRMWARE
+       bool "Support downloading firmware images with Host AP driver"
+       depends on HOSTAP
+       ---help---
+       Configure Host AP driver to include support for firmware image
+       download. Current version supports only downloading to volatile, i.e.,
+       RAM memory. Flash upgrade is not yet supported.
+
+       Firmware image downloading needs user space tool, prism2_srec. It is
+       available from http://hostap.epitest.fi/.
+
+config HOSTAP_PLX
+       tristate "Host AP driver for Prism2/2.5/3 in PLX9052 PCI adaptors"
+       depends on PCI && HOSTAP
+       ---help---
+       Host AP driver's version for Prism2/2.5/3 PC Cards in PLX9052 based
+       PCI adaptors.
+
+       "Host AP support for Prism2/2.5/3 IEEE 802.11b" is required for this
+       driver and its help text includes more information about the Host AP
+       driver.
+
+       The driver can be compiled as a module and will be named
+       "hostap_plx.ko".
+
+config HOSTAP_PCI
+       tristate "Host AP driver for Prism2.5 PCI adaptors"
+       depends on PCI && HOSTAP
+       ---help---
+       Host AP driver's version for Prism2.5 PCI adaptors.
+
+       "Host AP support for Prism2/2.5/3 IEEE 802.11b" is required for this
+       driver and its help text includes more information about the Host AP
+       driver.
+
+       The driver can be compiled as a module and will be named
+       "hostap_pci.ko".
+
+config HOSTAP_CS
+       tristate "Host AP driver for Prism2/2.5/3 PC Cards"
+       depends on PCMCIA!=n && HOSTAP
+       ---help---
+       Host AP driver's version for Prism2/2.5/3 PC Cards.
+
+       "Host AP support for Prism2/2.5/3 IEEE 802.11b" is required for this
+       driver and its help text includes more information about the Host AP
+       driver.
+
+       The driver can be compiled as a module and will be named
+       "hostap_cs.ko".
diff --git a/drivers/net/wireless/hostap/Makefile b/drivers/net/wireless/hostap/Makefile
new file mode 100644 (file)
index 0000000..fc62235
--- /dev/null
@@ -0,0 +1,5 @@
+obj-$(CONFIG_HOSTAP) += hostap.o
+
+obj-$(CONFIG_HOSTAP_CS) += hostap_cs.o
+obj-$(CONFIG_HOSTAP_PLX) += hostap_plx.o
+obj-$(CONFIG_HOSTAP_PCI) += hostap_pci.o
diff --git a/drivers/net/wireless/hostap/hostap.c b/drivers/net/wireless/hostap/hostap.c
new file mode 100644 (file)
index 0000000..9ce18b6
--- /dev/null
@@ -0,0 +1,1198 @@
+/*
+ * Host AP (software wireless LAN access point) driver for
+ * 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>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation. See README and COPYING for
+ * more details.
+ */
+
+#include <linux/config.h>
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/proc_fs.h>
+#include <linux/if_arp.h>
+#include <linux/delay.h>
+#include <linux/random.h>
+#include <linux/workqueue.h>
+#include <linux/kmod.h>
+#include <linux/rtnetlink.h>
+#include <linux/wireless.h>
+#include <net/iw_handler.h>
+#include <net/ieee80211.h>
+#include <net/ieee80211_crypt.h>
+#include <asm/uaccess.h>
+
+#include "hostap_wlan.h"
+#include "hostap_80211.h"
+#include "hostap_ap.h"
+#include "hostap.h"
+
+MODULE_AUTHOR("Jouni Malinen");
+MODULE_DESCRIPTION("Host AP common routines");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(PRISM2_VERSION);
+
+#define TX_TIMEOUT (2 * HZ)
+
+#define PRISM2_MAX_FRAME_SIZE 2304
+#define PRISM2_MIN_MTU 256
+/* FIX: */
+#define PRISM2_MAX_MTU (PRISM2_MAX_FRAME_SIZE - (6 /* LLC */ + 8 /* WEP */))
+
+
+/* hostap.c */
+static int prism2_wds_add(local_info_t *local, u8 *remote_addr,
+                         int rtnl_locked);
+static int prism2_wds_del(local_info_t *local, u8 *remote_addr,
+                         int rtnl_locked, int do_not_remove);
+
+/* hostap_ap.c */
+static int prism2_ap_get_sta_qual(local_info_t *local, struct sockaddr addr[],
+                                 struct iw_quality qual[], int buf_size,
+                                 int aplist);
+static int prism2_ap_translate_scan(struct net_device *dev, char *buffer);
+static int prism2_hostapd(struct ap_data *ap,
+                         struct prism2_hostapd_param *param);
+static void * ap_crypt_get_ptrs(struct ap_data *ap, u8 *addr, int permanent,
+                               struct ieee80211_crypt_data ***crypt);
+static void ap_control_kickall(struct ap_data *ap);
+#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
+static int ap_control_add_mac(struct mac_restrictions *mac_restrictions,
+                             u8 *mac);
+static int ap_control_del_mac(struct mac_restrictions *mac_restrictions,
+                             u8 *mac);
+static void ap_control_flush_macs(struct mac_restrictions *mac_restrictions);
+static int ap_control_kick_mac(struct ap_data *ap, struct net_device *dev,
+                              u8 *mac);
+#endif /* !PRISM2_NO_KERNEL_IEEE80211_MGMT */
+
+
+static const long freq_list[] = { 2412, 2417, 2422, 2427, 2432, 2437, 2442,
+                                 2447, 2452, 2457, 2462, 2467, 2472, 2484 };
+#define FREQ_COUNT (sizeof(freq_list) / sizeof(freq_list[0]))
+
+
+/* See IEEE 802.1H for LLC/SNAP encapsulation/decapsulation */
+/* Ethernet-II snap header (RFC1042 for most EtherTypes) */
+static unsigned char rfc1042_header[] =
+{ 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 };
+/* Bridge-Tunnel header (for EtherTypes ETH_P_AARP and ETH_P_IPX) */
+static unsigned char bridge_tunnel_header[] =
+{ 0xaa, 0xaa, 0x03, 0x00, 0x00, 0xf8 };
+/* No encapsulation header if EtherType < 0x600 (=length) */
+
+
+/* FIX: these could be compiled separately and linked together to hostap.o */
+#include "hostap_ap.c"
+#include "hostap_info.c"
+#include "hostap_ioctl.c"
+#include "hostap_proc.c"
+#include "hostap_80211_rx.c"
+#include "hostap_80211_tx.c"
+
+
+struct net_device * hostap_add_interface(struct local_info *local,
+                                        int type, int rtnl_locked,
+                                        const char *prefix,
+                                        const char *name)
+{
+       struct net_device *dev, *mdev;
+       struct hostap_interface *iface;
+       int ret;
+
+       dev = alloc_etherdev(sizeof(struct hostap_interface));
+       if (dev == NULL)
+               return NULL;
+
+       iface = netdev_priv(dev);
+       iface->dev = dev;
+       iface->local = local;
+       iface->type = type;
+       list_add(&iface->list, &local->hostap_interfaces);
+
+       mdev = local->dev;
+       memcpy(dev->dev_addr, mdev->dev_addr, ETH_ALEN);
+       dev->base_addr = mdev->base_addr;
+       dev->irq = mdev->irq;
+       dev->mem_start = mdev->mem_start;
+       dev->mem_end = mdev->mem_end;
+
+       hostap_setup_dev(dev, local, 0);
+       dev->destructor = free_netdev;
+
+       sprintf(dev->name, "%s%s", prefix, name);
+       if (!rtnl_locked)
+               rtnl_lock();
+
+       ret = 0;
+       if (strchr(dev->name, '%'))
+               ret = dev_alloc_name(dev, dev->name);
+
+       SET_NETDEV_DEV(dev, mdev->class_dev.dev);
+       if (ret >= 0)
+               ret = register_netdevice(dev);
+
+       if (!rtnl_locked)
+               rtnl_unlock();
+
+       if (ret < 0) {
+               printk(KERN_WARNING "%s: failed to add new netdevice!\n",
+                      dev->name);
+               free_netdev(dev);
+               return NULL;
+       }
+
+       printk(KERN_DEBUG "%s: registered netdevice %s\n",
+              mdev->name, dev->name);
+
+       return dev;
+}
+
+
+void hostap_remove_interface(struct net_device *dev, int rtnl_locked,
+                            int remove_from_list)
+{
+       struct hostap_interface *iface;
+
+       if (!dev)
+               return;
+
+       iface = netdev_priv(dev);
+
+       if (remove_from_list) {
+               list_del(&iface->list);
+       }
+
+       if (dev == iface->local->ddev)
+               iface->local->ddev = NULL;
+       else if (dev == iface->local->apdev)
+               iface->local->apdev = NULL;
+       else if (dev == iface->local->stadev)
+               iface->local->stadev = NULL;
+
+       if (rtnl_locked)
+               unregister_netdevice(dev);
+       else
+               unregister_netdev(dev);
+
+       /* dev->destructor = free_netdev() will free the device data, including
+        * private data, when removing the device */
+}
+
+
+static inline int prism2_wds_special_addr(u8 *addr)
+{
+       if (addr[0] || addr[1] || addr[2] || addr[3] || addr[4] || addr[5])
+               return 0;
+
+       return 1;
+}
+
+
+static int prism2_wds_add(local_info_t *local, u8 *remote_addr,
+                         int rtnl_locked)
+{
+       struct net_device *dev;
+       struct list_head *ptr;
+       struct hostap_interface *iface, *empty, *match;
+
+       empty = match = NULL;
+       read_lock_bh(&local->iface_lock);
+       list_for_each(ptr, &local->hostap_interfaces) {
+               iface = list_entry(ptr, struct hostap_interface, list);
+               if (iface->type != HOSTAP_INTERFACE_WDS)
+                       continue;
+
+               if (prism2_wds_special_addr(iface->u.wds.remote_addr))
+                       empty = iface;
+               else if (memcmp(iface->u.wds.remote_addr, remote_addr,
+                               ETH_ALEN) == 0) {
+                       match = iface;
+                       break;
+               }
+       }
+       if (!match && empty && !prism2_wds_special_addr(remote_addr)) {
+               /* take pre-allocated entry into use */
+               memcpy(empty->u.wds.remote_addr, remote_addr, ETH_ALEN);
+               read_unlock_bh(&local->iface_lock);
+               printk(KERN_DEBUG "%s: using pre-allocated WDS netdevice %s\n",
+                      local->dev->name, empty->dev->name);
+               return 0;
+       }
+       read_unlock_bh(&local->iface_lock);
+
+       if (!prism2_wds_special_addr(remote_addr)) {
+               if (match)
+                       return -EEXIST;
+               hostap_add_sta(local->ap, remote_addr);
+       }
+
+       if (local->wds_connections >= local->wds_max_connections)
+               return -ENOBUFS;
+
+       /* verify that there is room for wds# postfix in the interface name */
+       if (strlen(local->dev->name) > IFNAMSIZ - 5) {
+               printk(KERN_DEBUG "'%s' too long base device name\n",
+                      local->dev->name);
+               return -EINVAL;
+       }
+
+       dev = hostap_add_interface(local, HOSTAP_INTERFACE_WDS, rtnl_locked,
+                                  local->ddev->name, "wds%d");
+       if (dev == NULL)
+               return -ENOMEM;
+
+       iface = netdev_priv(dev);
+       memcpy(iface->u.wds.remote_addr, remote_addr, ETH_ALEN);
+
+       local->wds_connections++;
+
+       return 0;
+}
+
+
+static int prism2_wds_del(local_info_t *local, u8 *remote_addr,
+                         int rtnl_locked, int do_not_remove)
+{
+       unsigned long flags;
+       struct list_head *ptr;
+       struct hostap_interface *iface, *selected = NULL;
+
+       write_lock_irqsave(&local->iface_lock, flags);
+       list_for_each(ptr, &local->hostap_interfaces) {
+               iface = list_entry(ptr, struct hostap_interface, list);
+               if (iface->type != HOSTAP_INTERFACE_WDS)
+                       continue;
+
+               if (memcmp(iface->u.wds.remote_addr, remote_addr,
+                          ETH_ALEN) == 0) {
+                       selected = iface;
+                       break;
+               }
+       }
+       if (selected && !do_not_remove)
+               list_del(&selected->list);
+       write_unlock_irqrestore(&local->iface_lock, flags);
+
+       if (selected) {
+               if (do_not_remove)
+                       memset(selected->u.wds.remote_addr, 0, ETH_ALEN);
+               else {
+                       hostap_remove_interface(selected->dev, rtnl_locked, 0);
+                       local->wds_connections--;
+               }
+       }
+
+       return selected ? 0 : -ENODEV;
+}
+
+
+u16 hostap_tx_callback_register(local_info_t *local,
+                               void (*func)(struct sk_buff *, int ok, void *),
+                               void *data)
+{
+       unsigned long flags;
+       struct hostap_tx_callback_info *entry;
+
+       entry = (struct hostap_tx_callback_info *) kmalloc(sizeof(*entry),
+                                                          GFP_ATOMIC);
+       if (entry == NULL)
+               return 0;
+
+       entry->func = func;
+       entry->data = data;
+
+       spin_lock_irqsave(&local->lock, flags);
+       entry->idx = local->tx_callback ? local->tx_callback->idx + 1 : 1;
+       entry->next = local->tx_callback;
+       local->tx_callback = entry;
+       spin_unlock_irqrestore(&local->lock, flags);
+
+       return entry->idx;
+}
+
+
+int hostap_tx_callback_unregister(local_info_t *local, u16 idx)
+{
+       unsigned long flags;
+       struct hostap_tx_callback_info *cb, *prev = NULL;
+
+       spin_lock_irqsave(&local->lock, flags);
+       cb = local->tx_callback;
+       while (cb != NULL && cb->idx != idx) {
+               prev = cb;
+               cb = cb->next;
+       }
+       if (cb) {
+               if (prev == NULL)
+                       local->tx_callback = cb->next;
+               else
+                       prev->next = cb->next;
+               kfree(cb);
+       }
+       spin_unlock_irqrestore(&local->lock, flags);
+
+       return cb ? 0 : -1;
+}
+
+
+/* val is in host byte order */
+int hostap_set_word(struct net_device *dev, int rid, u16 val)
+{
+       struct hostap_interface *iface;
+       u16 tmp = cpu_to_le16(val);
+       iface = netdev_priv(dev);
+       return iface->local->func->set_rid(dev, rid, &tmp, 2);
+}
+
+
+int hostap_set_string(struct net_device *dev, int rid, const char *val)
+{
+       struct hostap_interface *iface;
+       char buf[MAX_SSID_LEN + 2];
+       int len;
+
+       iface = netdev_priv(dev);
+       len = strlen(val);
+       if (len > MAX_SSID_LEN)
+               return -1;
+       memset(buf, 0, sizeof(buf));
+       buf[0] = len; /* little endian 16 bit word */
+       memcpy(buf + 2, val, len);
+
+       return iface->local->func->set_rid(dev, rid, &buf, MAX_SSID_LEN + 2);
+}
+
+
+u16 hostap_get_porttype(local_info_t *local)
+{
+       if (local->iw_mode == IW_MODE_ADHOC && local->pseudo_adhoc)
+               return HFA384X_PORTTYPE_PSEUDO_IBSS;
+       if (local->iw_mode == IW_MODE_ADHOC)
+               return HFA384X_PORTTYPE_IBSS;
+       if (local->iw_mode == IW_MODE_INFRA)
+               return HFA384X_PORTTYPE_BSS;
+       if (local->iw_mode == IW_MODE_REPEAT)
+               return HFA384X_PORTTYPE_WDS;
+       if (local->iw_mode == IW_MODE_MONITOR)
+               return HFA384X_PORTTYPE_PSEUDO_IBSS;
+       return HFA384X_PORTTYPE_HOSTAP;
+}
+
+
+int hostap_set_encryption(local_info_t *local)
+{
+       u16 val, old_val;
+       int i, keylen, len, idx;
+       char keybuf[WEP_KEY_LEN + 1];
+       enum { NONE, WEP, OTHER } encrypt_type;
+
+       idx = local->tx_keyidx;
+       if (local->crypt[idx] == NULL || local->crypt[idx]->ops == NULL)
+               encrypt_type = NONE;
+       else if (strcmp(local->crypt[idx]->ops->name, "WEP") == 0)
+               encrypt_type = WEP;
+       else
+               encrypt_type = OTHER;
+
+       if (local->func->get_rid(local->dev, HFA384X_RID_CNFWEPFLAGS, &val, 2,
+                                1) < 0) {
+               printk(KERN_DEBUG "Could not read current WEP flags.\n");
+               goto fail;
+       }
+       le16_to_cpus(&val);
+       old_val = val;
+
+       if (encrypt_type != NONE || local->privacy_invoked)
+               val |= HFA384X_WEPFLAGS_PRIVACYINVOKED;
+       else
+               val &= ~HFA384X_WEPFLAGS_PRIVACYINVOKED;
+
+       if (local->open_wep || encrypt_type == NONE ||
+           ((local->ieee_802_1x || local->wpa) && local->host_decrypt))
+               val &= ~HFA384X_WEPFLAGS_EXCLUDEUNENCRYPTED;
+       else
+               val |= HFA384X_WEPFLAGS_EXCLUDEUNENCRYPTED;
+
+       if ((encrypt_type != NONE || local->privacy_invoked) &&
+           (encrypt_type == OTHER || local->host_encrypt))
+               val |= HFA384X_WEPFLAGS_HOSTENCRYPT;
+       else
+               val &= ~HFA384X_WEPFLAGS_HOSTENCRYPT;
+       if ((encrypt_type != NONE || local->privacy_invoked) &&
+           (encrypt_type == OTHER || local->host_decrypt))
+               val |= HFA384X_WEPFLAGS_HOSTDECRYPT;
+       else
+               val &= ~HFA384X_WEPFLAGS_HOSTDECRYPT;
+
+       if (val != old_val &&
+           hostap_set_word(local->dev, HFA384X_RID_CNFWEPFLAGS, val)) {
+               printk(KERN_DEBUG "Could not write new WEP flags (0x%x)\n",
+                      val);
+               goto fail;
+       }
+
+       if (encrypt_type != WEP)
+               return 0;
+
+       /* 104-bit support seems to require that all the keys are set to the
+        * same keylen */
+       keylen = 6; /* first 5 octets */
+       len = local->crypt[idx]->ops->get_key(keybuf, sizeof(keybuf),
+                                             NULL, local->crypt[idx]->priv);
+       if (idx >= 0 && idx < WEP_KEYS && len > 5)
+               keylen = WEP_KEY_LEN + 1; /* first 13 octets */
+
+       for (i = 0; i < WEP_KEYS; i++) {
+               memset(keybuf, 0, sizeof(keybuf));
+               if (local->crypt[i]) {
+                       (void) local->crypt[i]->ops->get_key(
+                               keybuf, sizeof(keybuf),
+                               NULL, local->crypt[i]->priv);
+               }
+               if (local->func->set_rid(local->dev,
+                                        HFA384X_RID_CNFDEFAULTKEY0 + i,
+                                        keybuf, keylen)) {
+                       printk(KERN_DEBUG "Could not set key %d (len=%d)\n",
+                              i, keylen);
+                       goto fail;
+               }
+       }
+       if (hostap_set_word(local->dev, HFA384X_RID_CNFWEPDEFAULTKEYID, idx)) {
+               printk(KERN_DEBUG "Could not set default keyid %d\n", idx);
+               goto fail;
+       }
+
+       return 0;
+
+ fail:
+       printk(KERN_DEBUG "%s: encryption setup failed\n", local->dev->name);
+       return -1;
+}
+
+
+int hostap_set_antsel(local_info_t *local)
+{
+       u16 val;
+       int ret = 0;
+
+       if (local->antsel_tx != HOSTAP_ANTSEL_DO_NOT_TOUCH &&
+           local->func->cmd(local->dev, HFA384X_CMDCODE_READMIF,
+                            HFA386X_CR_TX_CONFIGURE,
+                            NULL, &val) == 0) {
+               val &= ~(BIT(2) | BIT(1));
+               switch (local->antsel_tx) {
+               case HOSTAP_ANTSEL_DIVERSITY:
+                       val |= BIT(1);
+                       break;
+               case HOSTAP_ANTSEL_LOW:
+                       break;
+               case HOSTAP_ANTSEL_HIGH:
+                       val |= BIT(2);
+                       break;
+               }
+
+               if (local->func->cmd(local->dev, HFA384X_CMDCODE_WRITEMIF,
+                                    HFA386X_CR_TX_CONFIGURE, &val, NULL)) {
+                       printk(KERN_INFO "%s: setting TX AntSel failed\n",
+                              local->dev->name);
+                       ret = -1;
+               }
+       }
+
+       if (local->antsel_rx != HOSTAP_ANTSEL_DO_NOT_TOUCH &&
+           local->func->cmd(local->dev, HFA384X_CMDCODE_READMIF,
+                            HFA386X_CR_RX_CONFIGURE,
+                            NULL, &val) == 0) {
+               val &= ~(BIT(1) | BIT(0));
+               switch (local->antsel_rx) {
+               case HOSTAP_ANTSEL_DIVERSITY:
+                       break;
+               case HOSTAP_ANTSEL_LOW:
+                       val |= BIT(0);
+                       break;
+               case HOSTAP_ANTSEL_HIGH:
+                       val |= BIT(0) | BIT(1);
+                       break;
+               }
+
+               if (local->func->cmd(local->dev, HFA384X_CMDCODE_WRITEMIF,
+                                    HFA386X_CR_RX_CONFIGURE, &val, NULL)) {
+                       printk(KERN_INFO "%s: setting RX AntSel failed\n",
+                              local->dev->name);
+                       ret = -1;
+               }
+       }
+
+       return ret;
+}
+
+
+int hostap_set_roaming(local_info_t *local)
+{
+       u16 val;
+
+       switch (local->host_roaming) {
+       case 1:
+               val = HFA384X_ROAMING_HOST;
+               break;
+       case 2:
+               val = HFA384X_ROAMING_DISABLED;
+               break;
+       case 0:
+       default:
+               val = HFA384X_ROAMING_FIRMWARE;
+               break;
+       }
+
+       return hostap_set_word(local->dev, HFA384X_RID_CNFROAMINGMODE, val);
+}
+
+
+int hostap_set_auth_algs(local_info_t *local)
+{
+       int val = local->auth_algs;
+       /* At least STA f/w v0.6.2 seems to have issues with cnfAuthentication
+        * set to include both Open and Shared Key flags. It tries to use
+        * Shared Key authentication in that case even if WEP keys are not
+        * configured.. STA f/w v0.7.6 is able to handle such configuration,
+        * but it is unknown when this was fixed between 0.6.2 .. 0.7.6. */
+       if (local->sta_fw_ver < PRISM2_FW_VER(0,7,0) &&
+           val != PRISM2_AUTH_OPEN && val != PRISM2_AUTH_SHARED_KEY)
+               val = PRISM2_AUTH_OPEN;
+
+       if (hostap_set_word(local->dev, HFA384X_RID_CNFAUTHENTICATION, val)) {
+               printk(KERN_INFO "%s: cnfAuthentication setting to 0x%x "
+                      "failed\n", local->dev->name, local->auth_algs);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+
+void hostap_dump_rx_header(const char *name, const struct hfa384x_rx_frame *rx)
+{
+       u16 status, fc;
+
+       status = __le16_to_cpu(rx->status);
+
+       printk(KERN_DEBUG "%s: RX status=0x%04x (port=%d, type=%d, "
+              "fcserr=%d) silence=%d signal=%d rate=%d rxflow=%d; "
+              "jiffies=%ld\n",
+              name, status, (status >> 8) & 0x07, status >> 13, status & 1,
+              rx->silence, rx->signal, rx->rate, rx->rxflow, jiffies);
+
+       fc = __le16_to_cpu(rx->frame_control);
+       printk(KERN_DEBUG "   FC=0x%04x (type=%d:%d) dur=0x%04x seq=0x%04x "
+              "data_len=%d%s%s\n",
+              fc, WLAN_FC_GET_TYPE(fc) >> 2, WLAN_FC_GET_STYPE(fc) >> 4,
+              __le16_to_cpu(rx->duration_id), __le16_to_cpu(rx->seq_ctrl),
+              __le16_to_cpu(rx->data_len),
+              fc & WLAN_FC_TODS ? " [ToDS]" : "",
+              fc & WLAN_FC_FROMDS ? " [FromDS]" : "");
+
+       printk(KERN_DEBUG "   A1=" MACSTR " A2=" MACSTR " A3=" MACSTR " A4="
+              MACSTR "\n",
+              MAC2STR(rx->addr1), MAC2STR(rx->addr2), MAC2STR(rx->addr3),
+              MAC2STR(rx->addr4));
+
+       printk(KERN_DEBUG "   dst=" MACSTR " src=" MACSTR " len=%d\n",
+              MAC2STR(rx->dst_addr), MAC2STR(rx->src_addr),
+              __be16_to_cpu(rx->len));
+}
+
+
+void hostap_dump_tx_header(const char *name, const struct hfa384x_tx_frame *tx)
+{
+       u16 fc;
+
+       printk(KERN_DEBUG "%s: TX status=0x%04x retry_count=%d tx_rate=%d "
+              "tx_control=0x%04x; jiffies=%ld\n",
+              name, __le16_to_cpu(tx->status), tx->retry_count, tx->tx_rate,
+              __le16_to_cpu(tx->tx_control), jiffies);
+
+       fc = __le16_to_cpu(tx->frame_control);
+       printk(KERN_DEBUG "   FC=0x%04x (type=%d:%d) dur=0x%04x seq=0x%04x "
+              "data_len=%d%s%s\n",
+              fc, WLAN_FC_GET_TYPE(fc) >> 2, WLAN_FC_GET_STYPE(fc) >> 4,
+              __le16_to_cpu(tx->duration_id), __le16_to_cpu(tx->seq_ctrl),
+              __le16_to_cpu(tx->data_len),
+              fc & WLAN_FC_TODS ? " [ToDS]" : "",
+              fc & WLAN_FC_FROMDS ? " [FromDS]" : "");
+
+       printk(KERN_DEBUG "   A1=" MACSTR " A2=" MACSTR " A3=" MACSTR " A4="
+              MACSTR "\n",
+              MAC2STR(tx->addr1), MAC2STR(tx->addr2), MAC2STR(tx->addr3),
+              MAC2STR(tx->addr4));
+
+       printk(KERN_DEBUG "   dst=" MACSTR " src=" MACSTR " len=%d\n",
+              MAC2STR(tx->dst_addr), MAC2STR(tx->src_addr),
+              __be16_to_cpu(tx->len));
+}
+
+
+int hostap_80211_header_parse(struct sk_buff *skb, unsigned char *haddr)
+{
+       memcpy(haddr, skb->mac.raw + 10, ETH_ALEN); /* addr2 */
+       return ETH_ALEN;
+}
+
+
+int hostap_80211_prism_header_parse(struct sk_buff *skb, unsigned char *haddr)
+{
+       if (*(u32 *)skb->mac.raw == LWNG_CAP_DID_BASE) {
+               memcpy(haddr, skb->mac.raw +
+                      sizeof(struct linux_wlan_ng_prism_hdr) + 10,
+                      ETH_ALEN); /* addr2 */
+       } else { /* (*(u32 *)skb->mac.raw == htonl(LWNG_CAPHDR_VERSION)) */
+               memcpy(haddr, skb->mac.raw +
+                      sizeof(struct linux_wlan_ng_cap_hdr) + 10,
+                      ETH_ALEN); /* addr2 */
+       }
+       return ETH_ALEN;
+}
+
+
+int hostap_80211_get_hdrlen(u16 fc)
+{
+       int hdrlen = 24;
+
+       switch (WLAN_FC_GET_TYPE(fc)) {
+       case IEEE80211_FTYPE_DATA:
+               if ((fc & WLAN_FC_FROMDS) && (fc & WLAN_FC_TODS))
+                       hdrlen = 30; /* Addr4 */
+               break;
+       case IEEE80211_FTYPE_CTL:
+               switch (WLAN_FC_GET_STYPE(fc)) {
+               case IEEE80211_STYPE_CTS:
+               case IEEE80211_STYPE_ACK:
+                       hdrlen = 10;
+                       break;
+               default:
+                       hdrlen = 16;
+                       break;
+               }
+               break;
+       }
+
+       return hdrlen;
+}
+
+
+struct net_device_stats *hostap_get_stats(struct net_device *dev)
+{
+       struct hostap_interface *iface;
+       iface = netdev_priv(dev);
+       return &iface->stats;
+}
+
+
+static int prism2_close(struct net_device *dev)
+{
+       struct hostap_interface *iface;
+       local_info_t *local;
+
+       PDEBUG(DEBUG_FLOW, "%s: prism2_close\n", dev->name);
+
+       iface = netdev_priv(dev);
+       local = iface->local;
+
+       if (dev == local->ddev) {
+               prism2_sta_deauth(local, WLAN_REASON_DEAUTH_LEAVING);
+       }
+#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
+       if (!local->hostapd && dev == local->dev &&
+           (!local->func->card_present || local->func->card_present(local)) &&
+           local->hw_ready && local->ap && local->iw_mode == IW_MODE_MASTER)
+               hostap_deauth_all_stas(dev, local->ap, 1);
+#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
+
+       if (local->func->dev_close && local->func->dev_close(local))
+               return 0;
+
+       if (dev == local->dev) {
+               local->func->hw_shutdown(dev, HOSTAP_HW_ENABLE_CMDCOMPL);
+       }
+
+       if (netif_running(dev)) {
+               netif_stop_queue(dev);
+               netif_device_detach(dev);
+       }
+
+       flush_scheduled_work();
+
+       module_put(local->hw_module);
+
+       local->num_dev_open--;
+
+       if (dev != local->dev && local->dev->flags & IFF_UP &&
+           local->master_dev_auto_open && local->num_dev_open == 1) {
+               /* Close master radio interface automatically if it was also
+                * opened automatically and we are now closing the last
+                * remaining non-master device. */
+               dev_close(local->dev);
+       }
+
+       return 0;
+}
+
+
+static int prism2_open(struct net_device *dev)
+{
+       struct hostap_interface *iface;
+       local_info_t *local;
+
+       PDEBUG(DEBUG_FLOW, "%s: prism2_open\n", dev->name);
+
+       iface = netdev_priv(dev);
+       local = iface->local;
+
+       if (local->no_pri) {
+               printk(KERN_DEBUG "%s: could not set interface UP - no PRI "
+                      "f/w\n", dev->name);
+               return 1;
+       }
+
+       if ((local->func->card_present && !local->func->card_present(local)) ||
+           local->hw_downloading)
+               return -ENODEV;
+
+       if (local->func->dev_open && local->func->dev_open(local))
+               return 1;
+
+       if (!try_module_get(local->hw_module))
+               return -ENODEV;
+       local->num_dev_open++;
+
+       if (!local->dev_enabled && local->func->hw_enable(dev, 1)) {
+               printk(KERN_WARNING "%s: could not enable MAC port\n",
+                      dev->name);
+               prism2_close(dev);
+               return 1;
+       }
+       if (!local->dev_enabled)
+               prism2_callback(local, PRISM2_CALLBACK_ENABLE);
+       local->dev_enabled = 1;
+
+       if (dev != local->dev && !(local->dev->flags & IFF_UP)) {
+               /* Master radio interface is needed for all operation, so open
+                * it automatically when any virtual net_device is opened. */
+               local->master_dev_auto_open = 1;
+               dev_open(local->dev);
+       }
+
+       netif_device_attach(dev);
+       netif_start_queue(dev);
+
+       return 0;
+}
+
+
+static int prism2_set_mac_address(struct net_device *dev, void *p)
+{
+       struct hostap_interface *iface;
+       local_info_t *local;
+       struct list_head *ptr;
+       struct sockaddr *addr = p;
+
+       iface = netdev_priv(dev);
+       local = iface->local;
+
+       if (local->func->set_rid(dev, HFA384X_RID_CNFOWNMACADDR, addr->sa_data,
+                                ETH_ALEN) < 0 || local->func->reset_port(dev))
+               return -EINVAL;
+
+       read_lock_bh(&local->iface_lock);
+       list_for_each(ptr, &local->hostap_interfaces) {
+               iface = list_entry(ptr, struct hostap_interface, list);
+               memcpy(iface->dev->dev_addr, addr->sa_data, ETH_ALEN);
+       }
+       memcpy(local->dev->dev_addr, addr->sa_data, ETH_ALEN);
+       read_unlock_bh(&local->iface_lock);
+
+       return 0;
+}
+
+
+/* TODO: to be further implemented as soon as Prism2 fully supports
+ *       GroupAddresses and correct documentation is available */
+void hostap_set_multicast_list_queue(void *data)
+{
+       struct net_device *dev = (struct net_device *) data;
+       struct hostap_interface *iface;
+       local_info_t *local;
+
+       iface = netdev_priv(dev);
+       local = iface->local;
+       if (hostap_set_word(dev, HFA384X_RID_PROMISCUOUSMODE,
+                           local->is_promisc)) {
+               printk(KERN_INFO "%s: %sabling promiscuous mode failed\n",
+                      dev->name, local->is_promisc ? "en" : "dis");
+       }
+}
+
+
+static void hostap_set_multicast_list(struct net_device *dev)
+{
+#if 0
+       /* FIX: promiscuous mode seems to be causing a lot of problems with
+        * some station firmware versions (FCSErr frames, invalid MACPort, etc.
+        * corrupted incoming frames). This code is now commented out while the
+        * problems are investigated. */
+       struct hostap_interface *iface;
+       local_info_t *local;
+
+       iface = netdev_priv(dev);
+       local = iface->local;
+       if ((dev->flags & IFF_ALLMULTI) || (dev->flags & IFF_PROMISC)) {
+               local->is_promisc = 1;
+       } else {
+               local->is_promisc = 0;
+       }
+
+       schedule_work(&local->set_multicast_list_queue);
+#endif
+}
+
+
+static int prism2_change_mtu(struct net_device *dev, int new_mtu)
+{
+       if (new_mtu < PRISM2_MIN_MTU || new_mtu > PRISM2_MAX_MTU)
+               return -EINVAL;
+
+       dev->mtu = new_mtu;
+       return 0;
+}
+
+
+static void prism2_tx_timeout(struct net_device *dev)
+{
+       struct hostap_interface *iface;
+       local_info_t *local;
+       struct hfa384x_regs regs;
+
+       iface = netdev_priv(dev);
+       local = iface->local;
+
+       printk(KERN_WARNING "%s Tx timed out! Resetting card\n", dev->name);
+       netif_stop_queue(local->dev);
+
+       local->func->read_regs(dev, &regs);
+       printk(KERN_DEBUG "%s: CMD=%04x EVSTAT=%04x "
+              "OFFSET0=%04x OFFSET1=%04x SWSUPPORT0=%04x\n",
+              dev->name, regs.cmd, regs.evstat, regs.offset0, regs.offset1,
+              regs.swsupport0);
+
+       local->func->schedule_reset(local);
+}
+
+
+void hostap_setup_dev(struct net_device *dev, local_info_t *local,
+                     int main_dev)
+{
+       struct hostap_interface *iface;
+
+       iface = netdev_priv(dev);
+       ether_setup(dev);
+
+       /* kernel callbacks */
+       dev->get_stats = hostap_get_stats;
+       if (iface) {
+               /* Currently, we point to the proper spy_data only on
+                * the main_dev. This could be fixed. Jean II */
+               iface->wireless_data.spy_data = &iface->spy_data;
+               dev->wireless_data = &iface->wireless_data;
+       }
+       dev->wireless_handlers =
+               (struct iw_handler_def *) &hostap_iw_handler_def;
+       dev->do_ioctl = hostap_ioctl;
+       dev->open = prism2_open;
+       dev->stop = prism2_close;
+       dev->hard_start_xmit = hostap_data_start_xmit;
+       dev->set_mac_address = prism2_set_mac_address;
+       dev->set_multicast_list = hostap_set_multicast_list;
+       dev->change_mtu = prism2_change_mtu;
+       dev->tx_timeout = prism2_tx_timeout;
+       dev->watchdog_timeo = TX_TIMEOUT;
+
+       dev->mtu = local->mtu;
+       if (!main_dev) {
+               /* use main radio device queue */
+               dev->tx_queue_len = 0;
+       }
+
+       SET_ETHTOOL_OPS(dev, &prism2_ethtool_ops);
+
+       netif_stop_queue(dev);
+}
+
+
+static int hostap_enable_hostapd(local_info_t *local, int rtnl_locked)
+{
+       struct net_device *dev = local->dev;
+
+       if (local->apdev)
+               return -EEXIST;
+
+       printk(KERN_DEBUG "%s: enabling hostapd mode\n", dev->name);
+
+       local->apdev = hostap_add_interface(local, HOSTAP_INTERFACE_AP,
+                                           rtnl_locked, local->ddev->name,
+                                           "ap");
+       if (local->apdev == NULL)
+               return -ENOMEM;
+
+       local->apdev->hard_start_xmit = hostap_mgmt_start_xmit;
+       local->apdev->type = ARPHRD_IEEE80211;
+       local->apdev->hard_header_parse = hostap_80211_header_parse;
+
+       return 0;
+}
+
+
+static int hostap_disable_hostapd(local_info_t *local, int rtnl_locked)
+{
+       struct net_device *dev = local->dev;
+
+       printk(KERN_DEBUG "%s: disabling hostapd mode\n", dev->name);
+
+       hostap_remove_interface(local->apdev, rtnl_locked, 1);
+       local->apdev = NULL;
+
+       return 0;
+}
+
+
+static int hostap_enable_hostapd_sta(local_info_t *local, int rtnl_locked)
+{
+       struct net_device *dev = local->dev;
+
+       if (local->stadev)
+               return -EEXIST;
+
+       printk(KERN_DEBUG "%s: enabling hostapd STA mode\n", dev->name);
+
+       local->stadev = hostap_add_interface(local, HOSTAP_INTERFACE_STA,
+                                            rtnl_locked, local->ddev->name,
+                                            "sta");
+       if (local->stadev == NULL)
+               return -ENOMEM;
+
+       return 0;
+}
+
+
+static int hostap_disable_hostapd_sta(local_info_t *local, int rtnl_locked)
+{
+       struct net_device *dev = local->dev;
+
+       printk(KERN_DEBUG "%s: disabling hostapd mode\n", dev->name);
+
+       hostap_remove_interface(local->stadev, rtnl_locked, 1);
+       local->stadev = NULL;
+
+       return 0;
+}
+
+
+int hostap_set_hostapd(local_info_t *local, int val, int rtnl_locked)
+{
+       int ret;
+
+       if (val < 0 || val > 1)
+               return -EINVAL;
+
+       if (local->hostapd == val)
+               return 0;
+
+       if (val) {
+               ret = hostap_enable_hostapd(local, rtnl_locked);
+               if (ret == 0)
+                       local->hostapd = 1;
+       } else {
+               local->hostapd = 0;
+               ret = hostap_disable_hostapd(local, rtnl_locked);
+               if (ret != 0)
+                       local->hostapd = 1;
+       }
+
+       return ret;
+}
+
+
+int hostap_set_hostapd_sta(local_info_t *local, int val, int rtnl_locked)
+{
+       int ret;
+
+       if (val < 0 || val > 1)
+               return -EINVAL;
+
+       if (local->hostapd_sta == val)
+               return 0;
+
+       if (val) {
+               ret = hostap_enable_hostapd_sta(local, rtnl_locked);
+               if (ret == 0)
+                       local->hostapd_sta = 1;
+       } else {
+               local->hostapd_sta = 0;
+               ret = hostap_disable_hostapd_sta(local, rtnl_locked);
+               if (ret != 0)
+                       local->hostapd_sta = 1;
+       }
+
+
+       return ret;
+}
+
+
+int prism2_update_comms_qual(struct net_device *dev)
+{
+       struct hostap_interface *iface;
+       local_info_t *local;
+       int ret = 0;
+       struct hfa384x_comms_quality sq;
+
+       iface = netdev_priv(dev);
+       local = iface->local;
+       if (!local->sta_fw_ver)
+               ret = -1;
+       else if (local->sta_fw_ver >= PRISM2_FW_VER(1,3,1)) {
+               if (local->func->get_rid(local->dev,
+                                        HFA384X_RID_DBMCOMMSQUALITY,
+                                        &sq, sizeof(sq), 1) >= 0) {
+                       local->comms_qual = (s16) le16_to_cpu(sq.comm_qual);
+                       local->avg_signal = (s16) le16_to_cpu(sq.signal_level);
+                       local->avg_noise = (s16) le16_to_cpu(sq.noise_level);
+                       local->last_comms_qual_update = jiffies;
+               } else
+                       ret = -1;
+       } else {
+               if (local->func->get_rid(local->dev, HFA384X_RID_COMMSQUALITY,
+                                        &sq, sizeof(sq), 1) >= 0) {
+                       local->comms_qual = le16_to_cpu(sq.comm_qual);
+                       local->avg_signal = HFA384X_LEVEL_TO_dBm(
+                               le16_to_cpu(sq.signal_level));
+                       local->avg_noise = HFA384X_LEVEL_TO_dBm(
+                               le16_to_cpu(sq.noise_level));
+                       local->last_comms_qual_update = jiffies;
+               } else
+                       ret = -1;
+       }
+
+       return ret;
+}
+
+
+int prism2_sta_send_mgmt(local_info_t *local, u8 *dst, u16 stype,
+                        u8 *body, size_t bodylen)
+{
+       struct sk_buff *skb;
+       struct hostap_ieee80211_mgmt *mgmt;
+       struct hostap_skb_tx_data *meta;
+       struct net_device *dev = local->dev;
+
+       skb = dev_alloc_skb(IEEE80211_MGMT_HDR_LEN + bodylen);
+       if (skb == NULL)
+               return -ENOMEM;
+
+       mgmt = (struct hostap_ieee80211_mgmt *)
+               skb_put(skb, IEEE80211_MGMT_HDR_LEN);
+       memset(mgmt, 0, IEEE80211_MGMT_HDR_LEN);
+       mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | stype);
+       memcpy(mgmt->da, dst, ETH_ALEN);
+       memcpy(mgmt->sa, dev->dev_addr, ETH_ALEN);
+       memcpy(mgmt->bssid, dst, ETH_ALEN);
+       if (body)
+               memcpy(skb_put(skb, bodylen), body, bodylen);
+
+       meta = (struct hostap_skb_tx_data *) skb->cb;
+       memset(meta, 0, sizeof(*meta));
+       meta->magic = HOSTAP_SKB_TX_DATA_MAGIC;
+       meta->iface = netdev_priv(dev);
+
+       skb->dev = dev;
+       skb->mac.raw = skb->nh.raw = skb->data;
+       dev_queue_xmit(skb);
+
+       return 0;
+}
+
+
+int prism2_sta_deauth(local_info_t *local, u16 reason)
+{
+       union iwreq_data wrqu;
+       int ret;
+
+       if (local->iw_mode != IW_MODE_INFRA ||
+           memcmp(local->bssid, "\x00\x00\x00\x00\x00\x00", ETH_ALEN) == 0 ||
+           memcmp(local->bssid, "\x44\x44\x44\x44\x44\x44", ETH_ALEN) == 0)
+               return 0;
+
+       reason = cpu_to_le16(reason);
+       ret = prism2_sta_send_mgmt(local, local->bssid, IEEE80211_STYPE_DEAUTH,
+                                  (u8 *) &reason, 2);
+       memset(wrqu.ap_addr.sa_data, 0, ETH_ALEN);
+       wireless_send_event(local->dev, SIOCGIWAP, &wrqu, NULL);
+       return ret;
+}
+
+
+struct proc_dir_entry *hostap_proc;
+
+static int __init hostap_init(void)
+{
+       if (proc_net != NULL) {
+               hostap_proc = proc_mkdir("hostap", proc_net);
+               if (!hostap_proc)
+                       printk(KERN_WARNING "Failed to mkdir "
+                              "/proc/net/hostap\n");
+       } else
+               hostap_proc = NULL;
+
+       return 0;
+}
+
+
+static void __exit hostap_exit(void)
+{
+       if (hostap_proc != NULL) {
+               hostap_proc = NULL;
+               remove_proc_entry("hostap", proc_net);
+       }
+}
+
+
+EXPORT_SYMBOL(hostap_set_word);
+EXPORT_SYMBOL(hostap_set_string);
+EXPORT_SYMBOL(hostap_get_porttype);
+EXPORT_SYMBOL(hostap_set_encryption);
+EXPORT_SYMBOL(hostap_set_antsel);
+EXPORT_SYMBOL(hostap_set_roaming);
+EXPORT_SYMBOL(hostap_set_auth_algs);
+EXPORT_SYMBOL(hostap_dump_rx_header);
+EXPORT_SYMBOL(hostap_dump_tx_header);
+EXPORT_SYMBOL(hostap_80211_header_parse);
+EXPORT_SYMBOL(hostap_80211_prism_header_parse);
+EXPORT_SYMBOL(hostap_80211_get_hdrlen);
+EXPORT_SYMBOL(hostap_get_stats);
+EXPORT_SYMBOL(hostap_setup_dev);
+EXPORT_SYMBOL(hostap_proc);
+EXPORT_SYMBOL(hostap_set_multicast_list_queue);
+EXPORT_SYMBOL(hostap_set_hostapd);
+EXPORT_SYMBOL(hostap_set_hostapd_sta);
+EXPORT_SYMBOL(hostap_add_interface);
+EXPORT_SYMBOL(hostap_remove_interface);
+EXPORT_SYMBOL(prism2_update_comms_qual);
+
+module_init(hostap_init);
+module_exit(hostap_exit);
diff --git a/drivers/net/wireless/hostap/hostap.h b/drivers/net/wireless/hostap/hostap.h
new file mode 100644 (file)
index 0000000..5fac89b
--- /dev/null
@@ -0,0 +1,57 @@
+#ifndef HOSTAP_H
+#define HOSTAP_H
+
+/* hostap.c */
+
+extern struct proc_dir_entry *hostap_proc;
+
+u16 hostap_tx_callback_register(local_info_t *local,
+                               void (*func)(struct sk_buff *, int ok, void *),
+                               void *data);
+int hostap_tx_callback_unregister(local_info_t *local, u16 idx);
+int hostap_set_word(struct net_device *dev, int rid, u16 val);
+int hostap_set_string(struct net_device *dev, int rid, const char *val);
+u16 hostap_get_porttype(local_info_t *local);
+int hostap_set_encryption(local_info_t *local);
+int hostap_set_antsel(local_info_t *local);
+int hostap_set_roaming(local_info_t *local);
+int hostap_set_auth_algs(local_info_t *local);
+void hostap_dump_rx_header(const char *name,
+                          const struct hfa384x_rx_frame *rx);
+void hostap_dump_tx_header(const char *name,
+                          const struct hfa384x_tx_frame *tx);
+int hostap_80211_header_parse(struct sk_buff *skb, unsigned char *haddr);
+int hostap_80211_prism_header_parse(struct sk_buff *skb, unsigned char *haddr);
+int hostap_80211_get_hdrlen(u16 fc);
+struct net_device_stats *hostap_get_stats(struct net_device *dev);
+void hostap_setup_dev(struct net_device *dev, local_info_t *local,
+                     int main_dev);
+void hostap_set_multicast_list_queue(void *data);
+int hostap_set_hostapd(local_info_t *local, int val, int rtnl_locked);
+int hostap_set_hostapd_sta(local_info_t *local, int val, int rtnl_locked);
+void hostap_cleanup(local_info_t *local);
+void hostap_cleanup_handler(void *data);
+struct net_device * hostap_add_interface(struct local_info *local,
+                                        int type, int rtnl_locked,
+                                        const char *prefix, const char *name);
+void hostap_remove_interface(struct net_device *dev, int rtnl_locked,
+                            int remove_from_list);
+int prism2_update_comms_qual(struct net_device *dev);
+int prism2_sta_send_mgmt(local_info_t *local, u8 *dst, u16 stype,
+                        u8 *body, size_t bodylen);
+int prism2_sta_deauth(local_info_t *local, u16 reason);
+
+
+/* hostap_proc.c */
+
+void hostap_init_proc(local_info_t *local);
+void hostap_remove_proc(local_info_t *local);
+
+
+/* hostap_info.c */
+
+void hostap_info_init(local_info_t *local);
+void hostap_info_process(local_info_t *local, struct sk_buff *skb);
+
+
+#endif /* HOSTAP_H */
diff --git a/drivers/net/wireless/hostap/hostap_80211.h b/drivers/net/wireless/hostap/hostap_80211.h
new file mode 100644 (file)
index 0000000..bf506f5
--- /dev/null
@@ -0,0 +1,96 @@
+#ifndef HOSTAP_80211_H
+#define HOSTAP_80211_H
+
+struct hostap_ieee80211_mgmt {
+       u16 frame_control;
+       u16 duration;
+       u8 da[6];
+       u8 sa[6];
+       u8 bssid[6];
+       u16 seq_ctrl;
+       union {
+               struct {
+                       u16 auth_alg;
+                       u16 auth_transaction;
+                       u16 status_code;
+                       /* possibly followed by Challenge text */
+                       u8 variable[0];
+               } __attribute__ ((packed)) auth;
+               struct {
+                       u16 reason_code;
+               } __attribute__ ((packed)) deauth;
+               struct {
+                       u16 capab_info;
+                       u16 listen_interval;
+                       /* followed by SSID and Supported rates */
+                       u8 variable[0];
+               } __attribute__ ((packed)) assoc_req;
+               struct {
+                       u16 capab_info;
+                       u16 status_code;
+                       u16 aid;
+                       /* followed by Supported rates */
+                       u8 variable[0];
+               } __attribute__ ((packed)) assoc_resp, reassoc_resp;
+               struct {
+                       u16 capab_info;
+                       u16 listen_interval;
+                       u8 current_ap[6];
+                       /* followed by SSID and Supported rates */
+                       u8 variable[0];
+               } __attribute__ ((packed)) reassoc_req;
+               struct {
+                       u16 reason_code;
+               } __attribute__ ((packed)) disassoc;
+               struct {
+               } __attribute__ ((packed)) probe_req;
+               struct {
+                       u8 timestamp[8];
+                       u16 beacon_int;
+                       u16 capab_info;
+                       /* followed by some of SSID, Supported rates,
+                        * FH Params, DS Params, CF Params, IBSS Params, TIM */
+                       u8 variable[0];
+               } __attribute__ ((packed)) beacon, probe_resp;
+       } u;
+} __attribute__ ((packed));
+
+
+#define IEEE80211_MGMT_HDR_LEN 24
+#define IEEE80211_DATA_HDR3_LEN 24
+#define IEEE80211_DATA_HDR4_LEN 30
+
+
+struct hostap_80211_rx_status {
+       u32 mac_time;
+       u8 signal;
+       u8 noise;
+       u16 rate; /* in 100 kbps */
+};
+
+
+void hostap_80211_rx(struct net_device *dev, struct sk_buff *skb,
+                    struct hostap_80211_rx_status *rx_stats);
+
+
+/* prism2_rx_80211 'type' argument */
+enum {
+       PRISM2_RX_MONITOR, PRISM2_RX_MGMT, PRISM2_RX_NON_ASSOC,
+       PRISM2_RX_NULLFUNC_ACK
+};
+
+int prism2_rx_80211(struct net_device *dev, struct sk_buff *skb,
+                   struct hostap_80211_rx_status *rx_stats, int type);
+void hostap_80211_rx(struct net_device *dev, struct sk_buff *skb,
+                    struct hostap_80211_rx_status *rx_stats);
+void hostap_dump_rx_80211(const char *name, struct sk_buff *skb,
+                         struct hostap_80211_rx_status *rx_stats);
+
+void hostap_dump_tx_80211(const char *name, struct sk_buff *skb);
+int hostap_data_start_xmit(struct sk_buff *skb, struct net_device *dev);
+int hostap_mgmt_start_xmit(struct sk_buff *skb, struct net_device *dev);
+struct sk_buff * hostap_tx_encrypt(struct sk_buff *skb,
+                                  struct ieee80211_crypt_data *crypt);
+int hostap_master_start_xmit(struct sk_buff *skb, struct net_device *dev);
+
+#endif /* HOSTAP_80211_H */
diff --git a/drivers/net/wireless/hostap/hostap_80211_rx.c b/drivers/net/wireless/hostap/hostap_80211_rx.c
new file mode 100644 (file)
index 0000000..3752a67
--- /dev/null
@@ -0,0 +1,1086 @@
+#include <linux/etherdevice.h>
+
+#include "hostap_80211.h"
+#include "hostap.h"
+
+void hostap_dump_rx_80211(const char *name, struct sk_buff *skb,
+                         struct hostap_80211_rx_status *rx_stats)
+{
+       struct ieee80211_hdr *hdr;
+       u16 fc;
+
+       hdr = (struct ieee80211_hdr *) skb->data;
+
+       printk(KERN_DEBUG "%s: RX signal=%d noise=%d rate=%d len=%d "
+              "jiffies=%ld\n",
+              name, rx_stats->signal, rx_stats->noise, rx_stats->rate,
+              skb->len, jiffies);
+
+       if (skb->len < 2)
+               return;
+
+       fc = le16_to_cpu(hdr->frame_ctl);
+       printk(KERN_DEBUG "   FC=0x%04x (type=%d:%d)%s%s",
+              fc, WLAN_FC_GET_TYPE(fc) >> 2, WLAN_FC_GET_STYPE(fc) >> 4,
+              fc & WLAN_FC_TODS ? " [ToDS]" : "",
+              fc & WLAN_FC_FROMDS ? " [FromDS]" : "");
+
+       if (skb->len < IEEE80211_DATA_HDR3_LEN) {
+               printk("\n");
+               return;
+       }
+
+       printk(" dur=0x%04x seq=0x%04x\n", le16_to_cpu(hdr->duration_id),
+              le16_to_cpu(hdr->seq_ctl));
+
+       printk(KERN_DEBUG "   A1=" MACSTR " A2=" MACSTR " A3=" MACSTR,
+              MAC2STR(hdr->addr1), MAC2STR(hdr->addr2), MAC2STR(hdr->addr3));
+       if (skb->len >= 30)
+               printk(" A4=" MACSTR, MAC2STR(hdr->addr4));
+       printk("\n");
+}
+
+
+/* Send RX frame to netif with 802.11 (and possible prism) header.
+ * Called from hardware or software IRQ context. */
+int prism2_rx_80211(struct net_device *dev, struct sk_buff *skb,
+                   struct hostap_80211_rx_status *rx_stats, int type)
+{
+       struct hostap_interface *iface;
+       local_info_t *local;
+       int hdrlen, phdrlen, head_need, tail_need;
+       u16 fc;
+       int prism_header, ret;
+       struct ieee80211_hdr *hdr;
+
+       iface = netdev_priv(dev);
+       local = iface->local;
+       dev->last_rx = jiffies;
+
+       if (dev->type == ARPHRD_IEEE80211_PRISM) {
+               if (local->monitor_type == PRISM2_MONITOR_PRISM) {
+                       prism_header = 1;
+                       phdrlen = sizeof(struct linux_wlan_ng_prism_hdr);
+               } else { /* local->monitor_type == PRISM2_MONITOR_CAPHDR */
+                       prism_header = 2;
+                       phdrlen = sizeof(struct linux_wlan_ng_cap_hdr);
+               }
+       } else {
+               prism_header = 0;
+               phdrlen = 0;
+       }
+
+       hdr = (struct ieee80211_hdr *) skb->data;
+       fc = le16_to_cpu(hdr->frame_ctl);
+
+       if (type == PRISM2_RX_MGMT && (fc & WLAN_FC_PVER)) {
+               printk(KERN_DEBUG "%s: dropped management frame with header "
+                      "version %d\n", dev->name, fc & WLAN_FC_PVER);
+               dev_kfree_skb_any(skb);
+               return 0;
+       }
+
+       hdrlen = hostap_80211_get_hdrlen(fc);
+
+       /* check if there is enough room for extra data; if not, expand skb
+        * buffer to be large enough for the changes */
+       head_need = phdrlen;
+       tail_need = 0;
+#ifdef PRISM2_ADD_BOGUS_CRC
+       tail_need += 4;
+#endif /* PRISM2_ADD_BOGUS_CRC */
+
+       head_need -= skb_headroom(skb);
+       tail_need -= skb_tailroom(skb);
+
+       if (head_need > 0 || tail_need > 0) {
+               if (pskb_expand_head(skb, head_need > 0 ? head_need : 0,
+                                    tail_need > 0 ? tail_need : 0,
+                                    GFP_ATOMIC)) {
+                       printk(KERN_DEBUG "%s: prism2_rx_80211 failed to "
+                              "reallocate skb buffer\n", dev->name);
+                       dev_kfree_skb_any(skb);
+                       return 0;
+               }
+       }
+
+       /* We now have an skb with enough head and tail room, so just insert
+        * the extra data */
+
+#ifdef PRISM2_ADD_BOGUS_CRC
+       memset(skb_put(skb, 4), 0xff, 4); /* Prism2 strips CRC */
+#endif /* PRISM2_ADD_BOGUS_CRC */
+
+       if (prism_header == 1) {
+               struct linux_wlan_ng_prism_hdr *hdr;
+               hdr = (struct linux_wlan_ng_prism_hdr *)
+                       skb_push(skb, phdrlen);
+               memset(hdr, 0, phdrlen);
+               hdr->msgcode = LWNG_CAP_DID_BASE;
+               hdr->msglen = sizeof(*hdr);
+               memcpy(hdr->devname, dev->name, sizeof(hdr->devname));
+#define LWNG_SETVAL(f,i,s,l,d) \
+hdr->f.did = LWNG_CAP_DID_BASE | (i << 12); \
+hdr->f.status = s; hdr->f.len = l; hdr->f.data = d
+               LWNG_SETVAL(hosttime, 1, 0, 4, jiffies);
+               LWNG_SETVAL(mactime, 2, 0, 4, rx_stats->mac_time);
+               LWNG_SETVAL(channel, 3, 1 /* no value */, 4, 0);
+               LWNG_SETVAL(rssi, 4, 1 /* no value */, 4, 0);
+               LWNG_SETVAL(sq, 5, 1 /* no value */, 4, 0);
+               LWNG_SETVAL(signal, 6, 0, 4, rx_stats->signal);
+               LWNG_SETVAL(noise, 7, 0, 4, rx_stats->noise);
+               LWNG_SETVAL(rate, 8, 0, 4, rx_stats->rate / 5);
+               LWNG_SETVAL(istx, 9, 0, 4, 0);
+               LWNG_SETVAL(frmlen, 10, 0, 4, skb->len - phdrlen);
+#undef LWNG_SETVAL
+       } else if (prism_header == 2) {
+               struct linux_wlan_ng_cap_hdr *hdr;
+               hdr = (struct linux_wlan_ng_cap_hdr *)
+                       skb_push(skb, phdrlen);
+               memset(hdr, 0, phdrlen);
+               hdr->version    = htonl(LWNG_CAPHDR_VERSION);
+               hdr->length     = htonl(phdrlen);
+               hdr->mactime    = __cpu_to_be64(rx_stats->mac_time);
+               hdr->hosttime   = __cpu_to_be64(jiffies);
+               hdr->phytype    = htonl(4); /* dss_dot11_b */
+               hdr->channel    = htonl(local->channel);
+               hdr->datarate   = htonl(rx_stats->rate);
+               hdr->antenna    = htonl(0); /* unknown */
+               hdr->priority   = htonl(0); /* unknown */
+               hdr->ssi_type   = htonl(3); /* raw */
+               hdr->ssi_signal = htonl(rx_stats->signal);
+               hdr->ssi_noise  = htonl(rx_stats->noise);
+               hdr->preamble   = htonl(0); /* unknown */
+               hdr->encoding   = htonl(1); /* cck */
+       }
+
+       ret = skb->len - phdrlen;
+       skb->dev = dev;
+       skb->mac.raw = skb->data;
+       skb_pull(skb, hdrlen);
+       if (prism_header)
+               skb_pull(skb, phdrlen);
+       skb->pkt_type = PACKET_OTHERHOST;
+       skb->protocol = __constant_htons(ETH_P_802_2);
+       memset(skb->cb, 0, sizeof(skb->cb));
+       netif_rx(skb);
+
+       return ret;
+}
+
+
+/* Called only as a tasklet (software IRQ) */
+static void monitor_rx(struct net_device *dev, struct sk_buff *skb,
+                      struct hostap_80211_rx_status *rx_stats)
+{
+       struct net_device_stats *stats;
+       int len;
+
+       len = prism2_rx_80211(dev, skb, rx_stats, PRISM2_RX_MONITOR);
+       stats = hostap_get_stats(dev);
+       stats->rx_packets++;
+       stats->rx_bytes += len;
+}
+
+
+/* Called only as a tasklet (software IRQ) */
+static struct prism2_frag_entry *
+prism2_frag_cache_find(local_info_t *local, unsigned int seq,
+                      unsigned int frag, u8 *src, u8 *dst)
+{
+       struct prism2_frag_entry *entry;
+       int i;
+
+       for (i = 0; i < PRISM2_FRAG_CACHE_LEN; i++) {
+               entry = &local->frag_cache[i];
+               if (entry->skb != NULL &&
+                   time_after(jiffies, entry->first_frag_time + 2 * HZ)) {
+                       printk(KERN_DEBUG "%s: expiring fragment cache entry "
+                              "seq=%u last_frag=%u\n",
+                              local->dev->name, entry->seq, entry->last_frag);
+                       dev_kfree_skb(entry->skb);
+                       entry->skb = NULL;
+               }
+
+               if (entry->skb != NULL && entry->seq == seq &&
+                   (entry->last_frag + 1 == frag || frag == -1) &&
+                   memcmp(entry->src_addr, src, ETH_ALEN) == 0 &&
+                   memcmp(entry->dst_addr, dst, ETH_ALEN) == 0)
+                       return entry;
+       }
+
+       return NULL;
+}
+
+
+/* Called only as a tasklet (software IRQ) */
+static struct sk_buff *
+prism2_frag_cache_get(local_info_t *local, struct ieee80211_hdr *hdr)
+{
+       struct sk_buff *skb = NULL;
+       u16 sc;
+       unsigned int frag, seq;
+       struct prism2_frag_entry *entry;
+
+       sc = le16_to_cpu(hdr->seq_ctl);
+       frag = WLAN_GET_SEQ_FRAG(sc);
+       seq = WLAN_GET_SEQ_SEQ(sc) >> 4;
+
+       if (frag == 0) {
+               /* Reserve enough space to fit maximum frame length */
+               skb = dev_alloc_skb(local->dev->mtu +
+                                   sizeof(struct ieee80211_hdr) +
+                                   8 /* LLC */ +
+                                   2 /* alignment */ +
+                                   8 /* WEP */ + ETH_ALEN /* WDS */);
+               if (skb == NULL)
+                       return NULL;
+
+               entry = &local->frag_cache[local->frag_next_idx];
+               local->frag_next_idx++;
+               if (local->frag_next_idx >= PRISM2_FRAG_CACHE_LEN)
+                       local->frag_next_idx = 0;
+
+               if (entry->skb != NULL)
+                       dev_kfree_skb(entry->skb);
+
+               entry->first_frag_time = jiffies;
+               entry->seq = seq;
+               entry->last_frag = frag;
+               entry->skb = skb;
+               memcpy(entry->src_addr, hdr->addr2, ETH_ALEN);
+               memcpy(entry->dst_addr, hdr->addr1, ETH_ALEN);
+       } else {
+               /* received a fragment of a frame for which the head fragment
+                * should have already been received */
+               entry = prism2_frag_cache_find(local, seq, frag, hdr->addr2,
+                                              hdr->addr1);
+               if (entry != NULL) {
+                       entry->last_frag = frag;
+                       skb = entry->skb;
+               }
+       }
+
+       return skb;
+}
+
+
+/* Called only as a tasklet (software IRQ) */
+static int prism2_frag_cache_invalidate(local_info_t *local,
+                                       struct ieee80211_hdr *hdr)
+{
+       u16 sc;
+       unsigned int seq;
+       struct prism2_frag_entry *entry;
+
+       sc = le16_to_cpu(hdr->seq_ctl);
+       seq = WLAN_GET_SEQ_SEQ(sc) >> 4;
+
+       entry = prism2_frag_cache_find(local, seq, -1, hdr->addr2, hdr->addr1);
+
+       if (entry == NULL) {
+               printk(KERN_DEBUG "%s: could not invalidate fragment cache "
+                      "entry (seq=%u)\n",
+                      local->dev->name, seq);
+               return -1;
+       }
+
+       entry->skb = NULL;
+       return 0;
+}
+
+
+static struct hostap_bss_info *__hostap_get_bss(local_info_t *local, u8 *bssid,
+                                               u8 *ssid, size_t ssid_len)
+{
+       struct list_head *ptr;
+       struct hostap_bss_info *bss;
+
+       list_for_each(ptr, &local->bss_list) {
+               bss = list_entry(ptr, struct hostap_bss_info, list);
+               if (memcmp(bss->bssid, bssid, ETH_ALEN) == 0 &&
+                   (ssid == NULL ||
+                    (ssid_len == bss->ssid_len &&
+                     memcmp(ssid, bss->ssid, ssid_len) == 0))) {
+                       list_move(&bss->list, &local->bss_list);
+                       return bss;
+               }
+       }
+
+       return NULL;
+}
+
+
+static struct hostap_bss_info *__hostap_add_bss(local_info_t *local, u8 *bssid,
+                                               u8 *ssid, size_t ssid_len)
+{
+       struct hostap_bss_info *bss;
+
+       if (local->num_bss_info >= HOSTAP_MAX_BSS_COUNT) {
+               bss = list_entry(local->bss_list.prev,
+                                struct hostap_bss_info, list);
+               list_del(&bss->list);
+               local->num_bss_info--;
+       } else {
+               bss = (struct hostap_bss_info *)
+                       kmalloc(sizeof(*bss), GFP_ATOMIC);
+               if (bss == NULL)
+                       return NULL;
+       }
+
+       memset(bss, 0, sizeof(*bss));
+       memcpy(bss->bssid, bssid, ETH_ALEN);
+       memcpy(bss->ssid, ssid, ssid_len);
+       bss->ssid_len = ssid_len;
+       local->num_bss_info++;
+       list_add(&bss->list, &local->bss_list);
+       return bss;
+}
+
+
+static void __hostap_expire_bss(local_info_t *local)
+{
+       struct hostap_bss_info *bss;
+
+       while (local->num_bss_info > 0) {
+               bss = list_entry(local->bss_list.prev,
+                                struct hostap_bss_info, list);
+               if (!time_after(jiffies, bss->last_update + 60 * HZ))
+                       break;
+
+               list_del(&bss->list);
+               local->num_bss_info--;
+               kfree(bss);
+       }
+}
+
+
+/* Both IEEE 802.11 Beacon and Probe Response frames have similar structure, so
+ * the same routine can be used to parse both of them. */
+static void hostap_rx_sta_beacon(local_info_t *local, struct sk_buff *skb,
+                                int stype)
+{
+       struct hostap_ieee80211_mgmt *mgmt;
+       int left, chan = 0;
+       u8 *pos;
+       u8 *ssid = NULL, *wpa = NULL, *rsn = NULL;
+       size_t ssid_len = 0, wpa_len = 0, rsn_len = 0;
+       struct hostap_bss_info *bss;
+
+       if (skb->len < IEEE80211_MGMT_HDR_LEN + sizeof(mgmt->u.beacon))
+               return;
+
+       mgmt = (struct hostap_ieee80211_mgmt *) skb->data;
+       pos = mgmt->u.beacon.variable;
+       left = skb->len - (pos - skb->data);
+
+       while (left >= 2) {
+               if (2 + pos[1] > left)
+                       return; /* parse failed */
+               switch (*pos) {
+               case WLAN_EID_SSID:
+                       ssid = pos + 2;
+                       ssid_len = pos[1];
+                       break;
+               case WLAN_EID_GENERIC:
+                       if (pos[1] >= 4 &&
+                           pos[2] == 0x00 && pos[3] == 0x50 &&
+                           pos[4] == 0xf2 && pos[5] == 1) {
+                               wpa = pos;
+                               wpa_len = pos[1] + 2;
+                       }
+                       break;
+               case WLAN_EID_RSN:
+                       rsn = pos;
+                       rsn_len = pos[1] + 2;
+                       break;
+               case WLAN_EID_DS_PARAMS:
+                       if (pos[1] >= 1)
+                               chan = pos[2];
+                       break;
+               }
+               left -= 2 + pos[1];
+               pos += 2 + pos[1];
+       }
+
+       if (wpa_len > MAX_WPA_IE_LEN)
+               wpa_len = MAX_WPA_IE_LEN;
+       if (rsn_len > MAX_WPA_IE_LEN)
+               rsn_len = MAX_WPA_IE_LEN;
+       if (ssid_len > sizeof(bss->ssid))
+               ssid_len = sizeof(bss->ssid);
+
+       spin_lock(&local->lock);
+       bss = __hostap_get_bss(local, mgmt->bssid, ssid, ssid_len);
+       if (bss == NULL)
+               bss = __hostap_add_bss(local, mgmt->bssid, ssid, ssid_len);
+       if (bss) {
+               bss->last_update = jiffies;
+               bss->count++;
+               bss->capab_info = le16_to_cpu(mgmt->u.beacon.capab_info);
+               if (wpa) {
+                       memcpy(bss->wpa_ie, wpa, wpa_len);
+                       bss->wpa_ie_len = wpa_len;
+               } else
+                       bss->wpa_ie_len = 0;
+               if (rsn) {
+                       memcpy(bss->rsn_ie, rsn, rsn_len);
+                       bss->rsn_ie_len = rsn_len;
+               } else
+                       bss->rsn_ie_len = 0;
+               bss->chan = chan;
+       }
+       __hostap_expire_bss(local);
+       spin_unlock(&local->lock);
+}
+
+
+static inline int
+hostap_rx_frame_mgmt(local_info_t *local, struct sk_buff *skb,
+                    struct hostap_80211_rx_status *rx_stats, u16 type,
+                    u16 stype)
+{
+       if (local->iw_mode == IW_MODE_MASTER) {
+               hostap_update_sta_ps(local, (struct ieee80211_hdr *)
+                                    skb->data);
+       }
+
+       if (local->hostapd && type == IEEE80211_FTYPE_MGMT) {
+               if (stype == IEEE80211_STYPE_BEACON &&
+                   local->iw_mode == IW_MODE_MASTER) {
+                       struct sk_buff *skb2;
+                       /* Process beacon frames also in kernel driver to
+                        * update STA(AP) table statistics */
+                       skb2 = skb_clone(skb, GFP_ATOMIC);
+                       if (skb2)
+                               hostap_rx(skb2->dev, skb2, rx_stats);
+               }
+
+               /* send management frames to the user space daemon for
+                * processing */
+               local->apdevstats.rx_packets++;
+               local->apdevstats.rx_bytes += skb->len;
+               if (local->apdev == NULL)
+                       return -1;
+               prism2_rx_80211(local->apdev, skb, rx_stats, PRISM2_RX_MGMT);
+               return 0;
+       }
+
+       if (local->iw_mode == IW_MODE_MASTER) {
+               if (type != IEEE80211_FTYPE_MGMT &&
+                   type != IEEE80211_FTYPE_CTL) {
+                       printk(KERN_DEBUG "%s: unknown management frame "
+                              "(type=0x%02x, stype=0x%02x) dropped\n",
+                              skb->dev->name, type >> 2, stype >> 4);
+                       return -1;
+               }
+
+               hostap_rx(skb->dev, skb, rx_stats);
+               return 0;
+       } else if (type == IEEE80211_FTYPE_MGMT &&
+                  (stype == IEEE80211_STYPE_BEACON ||
+                   stype == IEEE80211_STYPE_PROBE_RESP)) {
+               hostap_rx_sta_beacon(local, skb, stype);
+               return -1;
+       } else if (type == IEEE80211_FTYPE_MGMT &&
+                  (stype == IEEE80211_STYPE_ASSOC_RESP ||
+                   stype == IEEE80211_STYPE_REASSOC_RESP)) {
+               /* Ignore (Re)AssocResp silently since these are not currently
+                * needed but are still received when WPA/RSN mode is enabled.
+                */
+               return -1;
+       } else {
+               printk(KERN_DEBUG "%s: hostap_rx_frame_mgmt: dropped unhandled"
+                      " management frame in non-Host AP mode (type=%d:%d)\n",
+                      skb->dev->name, type >> 2, stype >> 4);
+               return -1;
+       }
+}
+
+
+/* Called only as a tasklet (software IRQ) */
+static inline struct net_device *prism2_rx_get_wds(local_info_t *local,
+                                                  u8 *addr)
+{
+       struct hostap_interface *iface = NULL;
+       struct list_head *ptr;
+
+       read_lock_bh(&local->iface_lock);
+       list_for_each(ptr, &local->hostap_interfaces) {
+               iface = list_entry(ptr, struct hostap_interface, list);
+               if (iface->type == HOSTAP_INTERFACE_WDS &&
+                   memcmp(iface->u.wds.remote_addr, addr, ETH_ALEN) == 0)
+                       break;
+               iface = NULL;
+       }
+       read_unlock_bh(&local->iface_lock);
+
+       return iface ? iface->dev : NULL;
+}
+
+
+static inline int
+hostap_rx_frame_wds(local_info_t *local, struct ieee80211_hdr *hdr,
+                   u16 fc, struct net_device **wds)
+{
+       /* FIX: is this really supposed to accept WDS frames only in Master
+        * mode? What about Repeater or Managed with WDS frames? */
+       if ((fc & (WLAN_FC_TODS | WLAN_FC_FROMDS)) !=
+           (WLAN_FC_TODS | WLAN_FC_FROMDS) &&
+           (local->iw_mode != IW_MODE_MASTER || !(fc & WLAN_FC_TODS)))
+               return 0; /* not a WDS frame */
+
+       /* Possible WDS frame: either IEEE 802.11 compliant (if FromDS)
+        * or own non-standard frame with 4th address after payload */
+       if (memcmp(hdr->addr1, local->dev->dev_addr, ETH_ALEN) != 0 &&
+           (hdr->addr1[0] != 0xff || hdr->addr1[1] != 0xff ||
+            hdr->addr1[2] != 0xff || hdr->addr1[3] != 0xff ||
+            hdr->addr1[4] != 0xff || hdr->addr1[5] != 0xff)) {
+               /* RA (or BSSID) is not ours - drop */
+               PDEBUG(DEBUG_EXTRA, "%s: received WDS frame with "
+                      "not own or broadcast %s=" MACSTR "\n",
+                      local->dev->name, fc & WLAN_FC_FROMDS ? "RA" : "BSSID",
+                      MAC2STR(hdr->addr1));
+               return -1;
+       }
+
+       /* check if the frame came from a registered WDS connection */
+       *wds = prism2_rx_get_wds(local, hdr->addr2);
+       if (*wds == NULL && fc & WLAN_FC_FROMDS &&
+           (local->iw_mode != IW_MODE_INFRA ||
+            !(local->wds_type & HOSTAP_WDS_AP_CLIENT) ||
+            memcmp(hdr->addr2, local->bssid, ETH_ALEN) != 0)) {
+               /* require that WDS link has been registered with TA or the
+                * frame is from current AP when using 'AP client mode' */
+               PDEBUG(DEBUG_EXTRA, "%s: received WDS[4 addr] frame "
+                      "from unknown TA=" MACSTR "\n",
+                      local->dev->name, MAC2STR(hdr->addr2));
+               if (local->ap && local->ap->autom_ap_wds)
+                       hostap_wds_link_oper(local, hdr->addr2, WDS_ADD);
+               return -1;
+       }
+
+       if (*wds && !(fc & WLAN_FC_FROMDS) && local->ap &&
+           hostap_is_sta_assoc(local->ap, hdr->addr2)) {
+               /* STA is actually associated with us even though it has a
+                * registered WDS link. Assume it is in 'AP client' mode.
+                * Since this is a 3-addr frame, assume it is not (bogus) WDS
+                * frame and process it like any normal ToDS frame from
+                * associated STA. */
+               *wds = NULL;
+       }
+
+       return 0;
+}
+
+
+static int hostap_is_eapol_frame(local_info_t *local, struct sk_buff *skb)
+{
+       struct net_device *dev = local->dev;
+       u16 fc, ethertype;
+       struct ieee80211_hdr *hdr;
+       u8 *pos;
+
+       if (skb->len < 24)
+               return 0;
+
+       hdr = (struct ieee80211_hdr *) skb->data;
+       fc = le16_to_cpu(hdr->frame_ctl);
+
+       /* check that the frame is unicast frame to us */
+       if ((fc & (WLAN_FC_TODS | WLAN_FC_FROMDS)) == WLAN_FC_TODS &&
+           memcmp(hdr->addr1, dev->dev_addr, ETH_ALEN) == 0 &&
+           memcmp(hdr->addr3, dev->dev_addr, ETH_ALEN) == 0) {
+               /* ToDS frame with own addr BSSID and DA */
+       } else if ((fc & (WLAN_FC_TODS | WLAN_FC_FROMDS)) == WLAN_FC_FROMDS &&
+                  memcmp(hdr->addr1, dev->dev_addr, ETH_ALEN) == 0) {
+               /* FromDS frame with own addr as DA */
+       } else
+               return 0;
+
+       if (skb->len < 24 + 8)
+               return 0;
+
+       /* check for port access entity Ethernet type */
+       pos = skb->data + 24;
+       ethertype = (pos[6] << 8) | pos[7];
+       if (ethertype == ETH_P_PAE)
+               return 1;
+
+       return 0;
+}
+
+
+/* Called only as a tasklet (software IRQ) */
+static inline int
+hostap_rx_frame_decrypt(local_info_t *local, struct sk_buff *skb,
+                       struct ieee80211_crypt_data *crypt)
+{
+       struct ieee80211_hdr *hdr;
+       int res, hdrlen;
+
+       if (crypt == NULL || crypt->ops->decrypt_mpdu == NULL)
+               return 0;
+
+       hdr = (struct ieee80211_hdr *) skb->data;
+       hdrlen = hostap_80211_get_hdrlen(le16_to_cpu(hdr->frame_ctl));
+
+       if (local->tkip_countermeasures &&
+           strcmp(crypt->ops->name, "TKIP") == 0) {
+               if (net_ratelimit()) {
+                       printk(KERN_DEBUG "%s: TKIP countermeasures: dropped "
+                              "received packet from " MACSTR "\n",
+                              local->dev->name, MAC2STR(hdr->addr2));
+               }
+               return -1;
+       }
+
+       atomic_inc(&crypt->refcnt);
+       res = crypt->ops->decrypt_mpdu(skb, hdrlen, crypt->priv);
+       atomic_dec(&crypt->refcnt);
+       if (res < 0) {
+               printk(KERN_DEBUG "%s: decryption failed (SA=" MACSTR
+                      ") res=%d\n",
+                      local->dev->name, MAC2STR(hdr->addr2), res);
+               local->comm_tallies.rx_discards_wep_undecryptable++;
+               return -1;
+       }
+
+       return res;
+}
+
+
+/* Called only as a tasklet (software IRQ) */
+static inline int
+hostap_rx_frame_decrypt_msdu(local_info_t *local, struct sk_buff *skb,
+                            int keyidx, struct ieee80211_crypt_data *crypt)
+{
+       struct ieee80211_hdr *hdr;
+       int res, hdrlen;
+
+       if (crypt == NULL || crypt->ops->decrypt_msdu == NULL)
+               return 0;
+
+       hdr = (struct ieee80211_hdr *) skb->data;
+       hdrlen = hostap_80211_get_hdrlen(le16_to_cpu(hdr->frame_ctl));
+
+       atomic_inc(&crypt->refcnt);
+       res = crypt->ops->decrypt_msdu(skb, keyidx, hdrlen, crypt->priv);
+       atomic_dec(&crypt->refcnt);
+       if (res < 0) {
+               printk(KERN_DEBUG "%s: MSDU decryption/MIC verification failed"
+                      " (SA=" MACSTR " keyidx=%d)\n",
+                      local->dev->name, MAC2STR(hdr->addr2), keyidx);
+               return -1;
+       }
+
+       return 0;
+}
+
+
+/* All received frames are sent to this function. @skb contains the frame in
+ * IEEE 802.11 format, i.e., in the format it was sent over air.
+ * This function is called only as a tasklet (software IRQ). */
+void hostap_80211_rx(struct net_device *dev, struct sk_buff *skb,
+                    struct hostap_80211_rx_status *rx_stats)
+{
+       struct hostap_interface *iface;
+       local_info_t *local;
+       struct ieee80211_hdr *hdr;
+       size_t hdrlen;
+       u16 fc, type, stype, sc;
+       struct net_device *wds = NULL;
+       struct net_device_stats *stats;
+       unsigned int frag;
+       u8 *payload;
+       struct sk_buff *skb2 = NULL;
+       u16 ethertype;
+       int frame_authorized = 0;
+       int from_assoc_ap = 0;
+       u8 dst[ETH_ALEN];
+       u8 src[ETH_ALEN];
+       struct ieee80211_crypt_data *crypt = NULL;
+       void *sta = NULL;
+       int keyidx = 0;
+
+       iface = netdev_priv(dev);
+       local = iface->local;
+       iface->stats.rx_packets++;
+       iface->stats.rx_bytes += skb->len;
+
+       /* dev is the master radio device; change this to be the default
+        * virtual interface (this may be changed to WDS device below) */
+       dev = local->ddev;
+       iface = netdev_priv(dev);
+
+       hdr = (struct ieee80211_hdr *) skb->data;
+       stats = hostap_get_stats(dev);
+
+       if (skb->len < 10)
+               goto rx_dropped;
+
+       fc = le16_to_cpu(hdr->frame_ctl);
+       type = WLAN_FC_GET_TYPE(fc);
+       stype = WLAN_FC_GET_STYPE(fc);
+       sc = le16_to_cpu(hdr->seq_ctl);
+       frag = WLAN_GET_SEQ_FRAG(sc);
+       hdrlen = hostap_80211_get_hdrlen(fc);
+
+       /* Put this code here so that we avoid duplicating it in all
+        * Rx paths. - Jean II */
+#ifdef IW_WIRELESS_SPY         /* defined in iw_handler.h */
+       /* If spy monitoring on */
+       if (iface->spy_data.spy_number > 0) {
+               struct iw_quality wstats;
+               wstats.level = rx_stats->signal;
+               wstats.noise = rx_stats->noise;
+               wstats.updated = 6;     /* No qual value */
+               /* Update spy records */
+               wireless_spy_update(dev, hdr->addr2, &wstats);
+       }
+#endif /* IW_WIRELESS_SPY */
+       hostap_update_rx_stats(local->ap, hdr, rx_stats);
+
+       if (local->iw_mode == IW_MODE_MONITOR) {
+               monitor_rx(dev, skb, rx_stats);
+               return;
+       }
+
+       if (local->host_decrypt) {
+               int idx = 0;
+               if (skb->len >= hdrlen + 3)
+                       idx = skb->data[hdrlen + 3] >> 6;
+               crypt = local->crypt[idx];
+               sta = NULL;
+
+               /* Use station specific key to override default keys if the
+                * receiver address is a unicast address ("individual RA"). If
+                * bcrx_sta_key parameter is set, station specific key is used
+                * even with broad/multicast targets (this is against IEEE
+                * 802.11, but makes it easier to use different keys with
+                * stations that do not support WEP key mapping). */
+
+               if (!(hdr->addr1[0] & 0x01) || local->bcrx_sta_key)
+                       (void) hostap_handle_sta_crypto(local, hdr, &crypt,
+                                                       &sta);
+
+               /* allow NULL decrypt to indicate an station specific override
+                * for default encryption */
+               if (crypt && (crypt->ops == NULL ||
+                             crypt->ops->decrypt_mpdu == NULL))
+                       crypt = NULL;
+
+               if (!crypt && (fc & WLAN_FC_ISWEP)) {
+#if 0
+                       /* This seems to be triggered by some (multicast?)
+                        * frames from other than current BSS, so just drop the
+                        * frames silently instead of filling system log with
+                        * these reports. */
+                       printk(KERN_DEBUG "%s: WEP decryption failed (not set)"
+                              " (SA=" MACSTR ")\n",
+                              local->dev->name, MAC2STR(hdr->addr2));
+#endif
+                       local->comm_tallies.rx_discards_wep_undecryptable++;
+                       goto rx_dropped;
+               }
+       }
+
+       if (type != IEEE80211_FTYPE_DATA) {
+               if (type == IEEE80211_FTYPE_MGMT &&
+                   stype == IEEE80211_STYPE_AUTH &&
+                   fc & WLAN_FC_ISWEP && local->host_decrypt &&
+                   (keyidx = hostap_rx_frame_decrypt(local, skb, crypt)) < 0)
+               {
+                       printk(KERN_DEBUG "%s: failed to decrypt mgmt::auth "
+                              "from " MACSTR "\n", dev->name,
+                              MAC2STR(hdr->addr2));
+                       /* TODO: could inform hostapd about this so that it
+                        * could send auth failure report */
+                       goto rx_dropped;
+               }
+
+               if (hostap_rx_frame_mgmt(local, skb, rx_stats, type, stype))
+                       goto rx_dropped;
+               else
+                       goto rx_exit;
+       }
+
+       /* Data frame - extract src/dst addresses */
+       if (skb->len < IEEE80211_DATA_HDR3_LEN)
+               goto rx_dropped;
+
+       switch (fc & (WLAN_FC_FROMDS | WLAN_FC_TODS)) {
+       case WLAN_FC_FROMDS:
+               memcpy(dst, hdr->addr1, ETH_ALEN);
+               memcpy(src, hdr->addr3, ETH_ALEN);
+               break;
+       case WLAN_FC_TODS:
+               memcpy(dst, hdr->addr3, ETH_ALEN);
+               memcpy(src, hdr->addr2, ETH_ALEN);
+               break;
+       case WLAN_FC_FROMDS | WLAN_FC_TODS:
+               if (skb->len < IEEE80211_DATA_HDR4_LEN)
+                       goto rx_dropped;
+               memcpy(dst, hdr->addr3, ETH_ALEN);
+               memcpy(src, hdr->addr4, ETH_ALEN);
+               break;
+       case 0:
+               memcpy(dst, hdr->addr1, ETH_ALEN);
+               memcpy(src, hdr->addr2, ETH_ALEN);
+               break;
+       }
+
+       if (hostap_rx_frame_wds(local, hdr, fc, &wds))
+               goto rx_dropped;
+       if (wds) {
+               skb->dev = dev = wds;
+               stats = hostap_get_stats(dev);
+       }
+
+       if (local->iw_mode == IW_MODE_MASTER && !wds &&
+           (fc & (WLAN_FC_TODS | WLAN_FC_FROMDS)) == WLAN_FC_FROMDS &&
+           local->stadev &&
+           memcmp(hdr->addr2, local->assoc_ap_addr, ETH_ALEN) == 0) {
+               /* Frame from BSSID of the AP for which we are a client */
+               skb->dev = dev = local->stadev;
+               stats = hostap_get_stats(dev);
+               from_assoc_ap = 1;
+       }
+
+       dev->last_rx = jiffies;
+
+       if ((local->iw_mode == IW_MODE_MASTER ||
+            local->iw_mode == IW_MODE_REPEAT) &&
+           !from_assoc_ap) {
+               switch (hostap_handle_sta_rx(local, dev, skb, rx_stats,
+                                            wds != NULL)) {
+               case AP_RX_CONTINUE_NOT_AUTHORIZED:
+                       frame_authorized = 0;
+                       break;
+               case AP_RX_CONTINUE:
+                       frame_authorized = 1;
+                       break;
+               case AP_RX_DROP:
+                       goto rx_dropped;
+               case AP_RX_EXIT:
+                       goto rx_exit;
+               }
+       }
+
+       /* Nullfunc frames may have PS-bit set, so they must be passed to
+        * hostap_handle_sta_rx() before being dropped here. */
+       if (stype != IEEE80211_STYPE_DATA &&
+           stype != IEEE80211_STYPE_DATA_CFACK &&
+           stype != IEEE80211_STYPE_DATA_CFPOLL &&
+           stype != IEEE80211_STYPE_DATA_CFACKPOLL) {
+               if (stype != IEEE80211_STYPE_NULLFUNC)
+                       printk(KERN_DEBUG "%s: RX: dropped data frame "
+                              "with no data (type=0x%02x, subtype=0x%02x)\n",
+                              dev->name, type >> 2, stype >> 4);
+               goto rx_dropped;
+       }
+
+       /* skb: hdr + (possibly fragmented, possibly encrypted) payload */
+
+       if (local->host_decrypt && (fc & WLAN_FC_ISWEP) &&
+           (keyidx = hostap_rx_frame_decrypt(local, skb, crypt)) < 0)
+               goto rx_dropped;
+       hdr = (struct ieee80211_hdr *) skb->data;
+
+       /* skb: hdr + (possibly fragmented) plaintext payload */
+
+       if (local->host_decrypt && (fc & WLAN_FC_ISWEP) &&
+           (frag != 0 || (fc & WLAN_FC_MOREFRAG))) {
+               int flen;
+               struct sk_buff *frag_skb =
+                       prism2_frag_cache_get(local, hdr);
+               if (!frag_skb) {
+                       printk(KERN_DEBUG "%s: Rx cannot get skb from "
+                              "fragment cache (morefrag=%d seq=%u frag=%u)\n",
+                              dev->name, (fc & WLAN_FC_MOREFRAG) != 0,
+                              WLAN_GET_SEQ_SEQ(sc) >> 4, frag);
+                       goto rx_dropped;
+               }
+
+               flen = skb->len;
+               if (frag != 0)
+                       flen -= hdrlen;
+
+               if (frag_skb->tail + flen > frag_skb->end) {
+                       printk(KERN_WARNING "%s: host decrypted and "
+                              "reassembled frame did not fit skb\n",
+                              dev->name);
+                       prism2_frag_cache_invalidate(local, hdr);
+                       goto rx_dropped;
+               }
+
+               if (frag == 0) {
+                       /* copy first fragment (including full headers) into
+                        * beginning of the fragment cache skb */
+                       memcpy(skb_put(frag_skb, flen), skb->data, flen);
+               } else {
+                       /* append frame payload to the end of the fragment
+                        * cache skb */
+                       memcpy(skb_put(frag_skb, flen), skb->data + hdrlen,
+                              flen);
+               }
+               dev_kfree_skb(skb);
+               skb = NULL;
+
+               if (fc & WLAN_FC_MOREFRAG) {
+                       /* more fragments expected - leave the skb in fragment
+                        * cache for now; it will be delivered to upper layers
+                        * after all fragments have been received */
+                       goto rx_exit;
+               }
+
+               /* this was the last fragment and the frame will be
+                * delivered, so remove skb from fragment cache */
+               skb = frag_skb;
+               hdr = (struct ieee80211_hdr *) skb->data;
+               prism2_frag_cache_invalidate(local, hdr);
+       }
+
+       /* skb: hdr + (possible reassembled) full MSDU payload; possibly still
+        * encrypted/authenticated */
+
+       if (local->host_decrypt && (fc & WLAN_FC_ISWEP) &&
+           hostap_rx_frame_decrypt_msdu(local, skb, keyidx, crypt))
+               goto rx_dropped;
+
+       hdr = (struct ieee80211_hdr *) skb->data;
+       if (crypt && !(fc & WLAN_FC_ISWEP) && !local->open_wep) {
+               if (local->ieee_802_1x &&
+                   hostap_is_eapol_frame(local, skb)) {
+                       /* pass unencrypted EAPOL frames even if encryption is
+                        * configured */
+                       PDEBUG(DEBUG_EXTRA2, "%s: RX: IEEE 802.1X - passing "
+                              "unencrypted EAPOL frame\n", local->dev->name);
+               } else {
+                       printk(KERN_DEBUG "%s: encryption configured, but RX "
+                              "frame not encrypted (SA=" MACSTR ")\n",
+                              local->dev->name, MAC2STR(hdr->addr2));
+                       goto rx_dropped;
+               }
+       }
+
+       if (local->drop_unencrypted && !(fc & WLAN_FC_ISWEP) &&
+           !hostap_is_eapol_frame(local, skb)) {
+               if (net_ratelimit()) {
+                       printk(KERN_DEBUG "%s: dropped unencrypted RX data "
+                              "frame from " MACSTR " (drop_unencrypted=1)\n",
+                              dev->name, MAC2STR(hdr->addr2));
+               }
+               goto rx_dropped;
+       }
+
+       /* skb: hdr + (possible reassembled) full plaintext payload */
+
+       payload = skb->data + hdrlen;
+       ethertype = (payload[6] << 8) | payload[7];
+
+       /* If IEEE 802.1X is used, check whether the port is authorized to send
+        * the received frame. */
+       if (local->ieee_802_1x && local->iw_mode == IW_MODE_MASTER) {
+               if (ethertype == ETH_P_PAE) {
+                       PDEBUG(DEBUG_EXTRA2, "%s: RX: IEEE 802.1X frame\n",
+                              dev->name);
+                       if (local->hostapd && local->apdev) {
+                               /* Send IEEE 802.1X frames to the user
+                                * space daemon for processing */
+                               prism2_rx_80211(local->apdev, skb, rx_stats,
+                                               PRISM2_RX_MGMT);
+                               local->apdevstats.rx_packets++;
+                               local->apdevstats.rx_bytes += skb->len;
+                               goto rx_exit;
+                       }
+               } else if (!frame_authorized) {
+                       printk(KERN_DEBUG "%s: dropped frame from "
+                              "unauthorized port (IEEE 802.1X): "
+                              "ethertype=0x%04x\n",
+                              dev->name, ethertype);
+                       goto rx_dropped;
+               }
+       }
+
+       /* convert hdr + possible LLC headers into Ethernet header */
+       if (skb->len - hdrlen >= 8 &&
+           ((memcmp(payload, rfc1042_header, 6) == 0 &&
+             ethertype != ETH_P_AARP && ethertype != ETH_P_IPX) ||
+            memcmp(payload, bridge_tunnel_header, 6) == 0)) {
+               /* remove RFC1042 or Bridge-Tunnel encapsulation and
+                * replace EtherType */
+               skb_pull(skb, hdrlen + 6);
+               memcpy(skb_push(skb, ETH_ALEN), src, ETH_ALEN);
+               memcpy(skb_push(skb, ETH_ALEN), dst, ETH_ALEN);
+       } else {
+               u16 len;
+               /* Leave Ethernet header part of hdr and full payload */
+               skb_pull(skb, hdrlen);
+               len = htons(skb->len);
+               memcpy(skb_push(skb, 2), &len, 2);
+               memcpy(skb_push(skb, ETH_ALEN), src, ETH_ALEN);
+               memcpy(skb_push(skb, ETH_ALEN), dst, ETH_ALEN);
+       }
+
+       if (wds && ((fc & (WLAN_FC_TODS | WLAN_FC_FROMDS)) == WLAN_FC_TODS) &&
+           skb->len >= ETH_HLEN + ETH_ALEN) {
+               /* Non-standard frame: get addr4 from its bogus location after
+                * the payload */
+               memcpy(skb->data + ETH_ALEN,
+                      skb->data + skb->len - ETH_ALEN, ETH_ALEN);
+               skb_trim(skb, skb->len - ETH_ALEN);
+       }
+
+       stats->rx_packets++;
+       stats->rx_bytes += skb->len;
+
+       if (local->iw_mode == IW_MODE_MASTER && !wds &&
+           local->ap->bridge_packets) {
+               if (dst[0] & 0x01) {
+                       /* copy multicast frame both to the higher layers and
+                        * to the wireless media */
+                       local->ap->bridged_multicast++;
+                       skb2 = skb_clone(skb, GFP_ATOMIC);
+                       if (skb2 == NULL)
+                               printk(KERN_DEBUG "%s: skb_clone failed for "
+                                      "multicast frame\n", dev->name);
+               } else if (hostap_is_sta_authorized(local->ap, dst)) {
+                       /* send frame directly to the associated STA using
+                        * wireless media and not passing to higher layers */
+                       local->ap->bridged_unicast++;
+                       skb2 = skb;
+                       skb = NULL;
+               }
+       }
+
+       if (skb2 != NULL) {
+               /* send to wireless media */
+               skb2->protocol = __constant_htons(ETH_P_802_3);
+               skb2->mac.raw = skb2->nh.raw = skb2->data;
+               /* skb2->nh.raw = skb2->data + ETH_HLEN; */
+               skb2->dev = dev;
+               dev_queue_xmit(skb2);
+       }
+
+       if (skb) {
+               skb->protocol = eth_type_trans(skb, dev);
+               memset(skb->cb, 0, sizeof(skb->cb));
+               skb->dev = dev;
+               netif_rx(skb);
+       }
+
+ rx_exit:
+       if (sta)
+               hostap_handle_sta_release(sta);
+       return;
+
+ rx_dropped:
+       dev_kfree_skb(skb);
+
+       stats->rx_dropped++;
+       goto rx_exit;
+}
+
+
+EXPORT_SYMBOL(hostap_80211_rx);
diff --git a/drivers/net/wireless/hostap/hostap_80211_tx.c b/drivers/net/wireless/hostap/hostap_80211_tx.c
new file mode 100644 (file)
index 0000000..79cf553
--- /dev/null
@@ -0,0 +1,524 @@
+void hostap_dump_tx_80211(const char *name, struct sk_buff *skb)
+{
+       struct ieee80211_hdr *hdr;
+       u16 fc;
+
+       hdr = (struct ieee80211_hdr *) skb->data;
+
+       printk(KERN_DEBUG "%s: TX len=%d jiffies=%ld\n",
+              name, skb->len, jiffies);
+
+       if (skb->len < 2)
+               return;
+
+       fc = le16_to_cpu(hdr->frame_ctl);
+       printk(KERN_DEBUG "   FC=0x%04x (type=%d:%d)%s%s",
+              fc, WLAN_FC_GET_TYPE(fc) >> 2, WLAN_FC_GET_STYPE(fc) >> 4,
+              fc & WLAN_FC_TODS ? " [ToDS]" : "",
+              fc & WLAN_FC_FROMDS ? " [FromDS]" : "");
+
+       if (skb->len < IEEE80211_DATA_HDR3_LEN) {
+               printk("\n");
+               return;
+       }
+
+       printk(" dur=0x%04x seq=0x%04x\n", le16_to_cpu(hdr->duration_id),
+              le16_to_cpu(hdr->seq_ctl));
+
+       printk(KERN_DEBUG "   A1=" MACSTR " A2=" MACSTR " A3=" MACSTR,
+              MAC2STR(hdr->addr1), MAC2STR(hdr->addr2), MAC2STR(hdr->addr3));
+       if (skb->len >= 30)
+               printk(" A4=" MACSTR, MAC2STR(hdr->addr4));
+       printk("\n");
+}
+
+
+/* hard_start_xmit function for data interfaces (wlan#, wlan#wds#, wlan#sta)
+ * Convert Ethernet header into a suitable IEEE 802.11 header depending on
+ * device configuration. */
+int hostap_data_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+       struct hostap_interface *iface;
+       local_info_t *local;
+       int need_headroom, need_tailroom = 0;
+       struct ieee80211_hdr hdr;
+       u16 fc, ethertype = 0;
+       enum {
+               WDS_NO = 0, WDS_OWN_FRAME, WDS_COMPLIANT_FRAME
+       } use_wds = WDS_NO;
+       u8 *encaps_data;
+       int hdr_len, encaps_len, skip_header_bytes;
+       int to_assoc_ap = 0;
+       struct hostap_skb_tx_data *meta;
+
+       iface = netdev_priv(dev);
+       local = iface->local;
+
+       if (skb->len < ETH_HLEN) {
+               printk(KERN_DEBUG "%s: hostap_data_start_xmit: short skb "
+                      "(len=%d)\n", dev->name, skb->len);
+               kfree_skb(skb);
+               return 0;
+       }
+
+       if (local->ddev != dev) {
+               use_wds = (local->iw_mode == IW_MODE_MASTER &&
+                          !(local->wds_type & HOSTAP_WDS_STANDARD_FRAME)) ?
+                       WDS_OWN_FRAME : WDS_COMPLIANT_FRAME;
+               if (dev == local->stadev) {
+                       to_assoc_ap = 1;
+                       use_wds = WDS_NO;
+               } else if (dev == local->apdev) {
+                       printk(KERN_DEBUG "%s: prism2_tx: trying to use "
+                              "AP device with Ethernet net dev\n", dev->name);
+                       kfree_skb(skb);
+                       return 0;
+               }
+       } else {
+               if (local->iw_mode == IW_MODE_REPEAT) {
+                       printk(KERN_DEBUG "%s: prism2_tx: trying to use "
+                              "non-WDS link in Repeater mode\n", dev->name);
+                       kfree_skb(skb);
+                       return 0;
+               } else if (local->iw_mode == IW_MODE_INFRA &&
+                          (local->wds_type & HOSTAP_WDS_AP_CLIENT) &&
+                          memcmp(skb->data + ETH_ALEN, dev->dev_addr,
+                                 ETH_ALEN) != 0) {
+                       /* AP client mode: send frames with foreign src addr
+                        * using 4-addr WDS frames */
+                       use_wds = WDS_COMPLIANT_FRAME;
+               }
+       }
+
+       /* Incoming skb->data: dst_addr[6], src_addr[6], proto[2], payload
+        * ==>
+        * Prism2 TX frame with 802.11 header:
+        * txdesc (address order depending on used mode; includes dst_addr and
+        * src_addr), possible encapsulation (RFC1042/Bridge-Tunnel;
+        * proto[2], payload {, possible addr4[6]} */
+
+       ethertype = (skb->data[12] << 8) | skb->data[13];
+
+       memset(&hdr, 0, sizeof(hdr));
+
+       /* Length of data after IEEE 802.11 header */
+       encaps_data = NULL;
+       encaps_len = 0;
+       skip_header_bytes = ETH_HLEN;
+       if (ethertype == ETH_P_AARP || ethertype == ETH_P_IPX) {
+               encaps_data = bridge_tunnel_header;
+               encaps_len = sizeof(bridge_tunnel_header);
+               skip_header_bytes -= 2;
+       } else if (ethertype >= 0x600) {
+               encaps_data = rfc1042_header;
+               encaps_len = sizeof(rfc1042_header);
+               skip_header_bytes -= 2;
+       }
+
+       fc = IEEE80211_FTYPE_DATA | IEEE80211_STYPE_DATA;
+       hdr_len = IEEE80211_DATA_HDR3_LEN;
+
+       if (use_wds != WDS_NO) {
+               /* Note! Prism2 station firmware has problems with sending real
+                * 802.11 frames with four addresses; until these problems can
+                * be fixed or worked around, 4-addr frames needed for WDS are
+                * using incompatible format: FromDS flag is not set and the
+                * fourth address is added after the frame payload; it is
+                * assumed, that the receiving station knows how to handle this
+                * frame format */
+
+               if (use_wds == WDS_COMPLIANT_FRAME) {
+                       fc |= WLAN_FC_FROMDS | WLAN_FC_TODS;
+                       /* From&To DS: Addr1 = RA, Addr2 = TA, Addr3 = DA,
+                        * Addr4 = SA */
+                       memcpy(&hdr.addr4, skb->data + ETH_ALEN, ETH_ALEN);
+                       hdr_len += ETH_ALEN;
+               } else {
+                       /* bogus 4-addr format to workaround Prism2 station
+                        * f/w bug */
+                       fc |= WLAN_FC_TODS;
+                       /* From DS: Addr1 = DA (used as RA),
+                        * Addr2 = BSSID (used as TA), Addr3 = SA (used as DA),
+                        */
+
+                       /* SA from skb->data + ETH_ALEN will be added after
+                        * frame payload; use hdr.addr4 as a temporary buffer
+                        */
+                       memcpy(&hdr.addr4, skb->data + ETH_ALEN, ETH_ALEN);
+                       need_tailroom += ETH_ALEN;
+               }
+
+               /* send broadcast and multicast frames to broadcast RA, if
+                * configured; otherwise, use unicast RA of the WDS link */
+               if ((local->wds_type & HOSTAP_WDS_BROADCAST_RA) &&
+                   skb->data[0] & 0x01)
+                       memset(&hdr.addr1, 0xff, ETH_ALEN);
+               else if (iface->type == HOSTAP_INTERFACE_WDS)
+                       memcpy(&hdr.addr1, iface->u.wds.remote_addr,
+                              ETH_ALEN);
+               else
+                       memcpy(&hdr.addr1, local->bssid, ETH_ALEN);
+               memcpy(&hdr.addr2, dev->dev_addr, ETH_ALEN);
+               memcpy(&hdr.addr3, skb->data, ETH_ALEN);
+       } else if (local->iw_mode == IW_MODE_MASTER && !to_assoc_ap) {
+               fc |= WLAN_FC_FROMDS;
+               /* From DS: Addr1 = DA, Addr2 = BSSID, Addr3 = SA */
+               memcpy(&hdr.addr1, skb->data, ETH_ALEN);
+               memcpy(&hdr.addr2, dev->dev_addr, ETH_ALEN);
+               memcpy(&hdr.addr3, skb->data + ETH_ALEN, ETH_ALEN);
+       } else if (local->iw_mode == IW_MODE_INFRA || to_assoc_ap) {
+               fc |= WLAN_FC_TODS;
+               /* To DS: Addr1 = BSSID, Addr2 = SA, Addr3 = DA */
+               memcpy(&hdr.addr1, to_assoc_ap ?
+                      local->assoc_ap_addr : local->bssid, ETH_ALEN);
+               memcpy(&hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN);
+               memcpy(&hdr.addr3, skb->data, ETH_ALEN);
+       } else if (local->iw_mode == IW_MODE_ADHOC) {
+               /* not From/To DS: Addr1 = DA, Addr2 = SA, Addr3 = BSSID */
+               memcpy(&hdr.addr1, skb->data, ETH_ALEN);
+               memcpy(&hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN);
+               memcpy(&hdr.addr3, local->bssid, ETH_ALEN);
+       }
+
+       hdr.frame_ctl = cpu_to_le16(fc);
+
+       skb_pull(skb, skip_header_bytes);
+       need_headroom = local->func->need_tx_headroom + hdr_len + encaps_len;
+       if (skb_tailroom(skb) < need_tailroom) {
+               skb = skb_unshare(skb, GFP_ATOMIC);
+               if (skb == NULL) {
+                       iface->stats.tx_dropped++;
+                       return 0;
+               }
+               if (pskb_expand_head(skb, need_headroom, need_tailroom,
+                                    GFP_ATOMIC)) {
+                       kfree_skb(skb);
+                       iface->stats.tx_dropped++;
+                       return 0;
+               }
+       } else if (skb_headroom(skb) < need_headroom) {
+               struct sk_buff *tmp = skb;
+               skb = skb_realloc_headroom(skb, need_headroom);
+               kfree_skb(tmp);
+               if (skb == NULL) {
+                       iface->stats.tx_dropped++;
+                       return 0;
+               }
+       } else {
+               skb = skb_unshare(skb, GFP_ATOMIC);
+               if (skb == NULL) {
+                       iface->stats.tx_dropped++;
+                       return 0;
+               }
+       }
+
+       if (encaps_data)
+               memcpy(skb_push(skb, encaps_len), encaps_data, encaps_len);
+       memcpy(skb_push(skb, hdr_len), &hdr, hdr_len);
+       if (use_wds == WDS_OWN_FRAME) {
+               memcpy(skb_put(skb, ETH_ALEN), &hdr.addr4, ETH_ALEN);
+       }
+
+       iface->stats.tx_packets++;
+       iface->stats.tx_bytes += skb->len;
+
+       skb->mac.raw = skb->data;
+       meta = (struct hostap_skb_tx_data *) skb->cb;
+       memset(meta, 0, sizeof(*meta));
+       meta->magic = HOSTAP_SKB_TX_DATA_MAGIC;
+       if (use_wds)
+               meta->flags |= HOSTAP_TX_FLAGS_WDS;
+       meta->ethertype = ethertype;
+       meta->iface = iface;
+
+       /* Send IEEE 802.11 encapsulated frame using the master radio device */
+       skb->dev = local->dev;
+       dev_queue_xmit(skb);
+       return 0;
+}
+
+
+/* hard_start_xmit function for hostapd wlan#ap interfaces */
+int hostap_mgmt_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+       struct hostap_interface *iface;
+       local_info_t *local;
+       struct hostap_skb_tx_data *meta;
+       struct ieee80211_hdr *hdr;
+       u16 fc;
+
+       iface = netdev_priv(dev);
+       local = iface->local;
+
+       if (skb->len < 10) {
+               printk(KERN_DEBUG "%s: hostap_mgmt_start_xmit: short skb "
+                      "(len=%d)\n", dev->name, skb->len);
+               kfree_skb(skb);
+               return 0;
+       }
+
+       iface->stats.tx_packets++;
+       iface->stats.tx_bytes += skb->len;
+
+       meta = (struct hostap_skb_tx_data *) skb->cb;
+       memset(meta, 0, sizeof(*meta));
+       meta->magic = HOSTAP_SKB_TX_DATA_MAGIC;
+       meta->iface = iface;
+
+       if (skb->len >= IEEE80211_DATA_HDR3_LEN + sizeof(rfc1042_header) + 2) {
+               hdr = (struct ieee80211_hdr *) skb->data;
+               fc = le16_to_cpu(hdr->frame_ctl);
+               if (WLAN_FC_GET_TYPE(fc) == IEEE80211_FTYPE_DATA &&
+                   WLAN_FC_GET_STYPE(fc) == IEEE80211_STYPE_DATA) {
+                       u8 *pos = &skb->data[IEEE80211_DATA_HDR3_LEN +
+                                            sizeof(rfc1042_header)];
+                       meta->ethertype = (pos[0] << 8) | pos[1];
+               }
+       }
+
+       /* Send IEEE 802.11 encapsulated frame using the master radio device */
+       skb->dev = local->dev;
+       dev_queue_xmit(skb);
+       return 0;
+}
+
+
+/* Called only from software IRQ */
+struct sk_buff * hostap_tx_encrypt(struct sk_buff *skb,
+                                  struct ieee80211_crypt_data *crypt)
+{
+       struct hostap_interface *iface;
+       local_info_t *local;
+       struct ieee80211_hdr *hdr;
+       u16 fc;
+       int hdr_len, res;
+
+       iface = netdev_priv(skb->dev);
+       local = iface->local;
+
+       if (skb->len < IEEE80211_DATA_HDR3_LEN) {
+               kfree_skb(skb);
+               return NULL;
+       }
+
+       if (local->tkip_countermeasures &&
+           crypt && crypt->ops && strcmp(crypt->ops->name, "TKIP") == 0) {
+               hdr = (struct ieee80211_hdr *) skb->data;
+               if (net_ratelimit()) {
+                       printk(KERN_DEBUG "%s: TKIP countermeasures: dropped "
+                              "TX packet to " MACSTR "\n",
+                              local->dev->name, MAC2STR(hdr->addr1));
+               }
+               kfree_skb(skb);
+               return NULL;
+       }
+
+       skb = skb_unshare(skb, GFP_ATOMIC);
+       if (skb == NULL)
+               return NULL;
+
+       if ((skb_headroom(skb) < crypt->ops->extra_prefix_len ||
+            skb_tailroom(skb) < crypt->ops->extra_postfix_len) &&
+           pskb_expand_head(skb, crypt->ops->extra_prefix_len,
+                            crypt->ops->extra_postfix_len, GFP_ATOMIC)) {
+               kfree_skb(skb);
+               return NULL;
+       }
+
+       hdr = (struct ieee80211_hdr *) skb->data;
+       fc = le16_to_cpu(hdr->frame_ctl);
+       hdr_len = hostap_80211_get_hdrlen(fc);
+
+       /* Host-based IEEE 802.11 fragmentation for TX is not yet supported, so
+        * call both MSDU and MPDU encryption functions from here. */
+       atomic_inc(&crypt->refcnt);
+       res = 0;
+       if (crypt->ops->encrypt_msdu)
+               res = crypt->ops->encrypt_msdu(skb, hdr_len, crypt->priv);
+       if (res == 0 && crypt->ops->encrypt_mpdu)
+               res = crypt->ops->encrypt_mpdu(skb, hdr_len, crypt->priv);
+       atomic_dec(&crypt->refcnt);
+       if (res < 0) {
+               kfree_skb(skb);
+               return NULL;
+       }
+
+       return skb;
+}
+
+
+/* hard_start_xmit function for master radio interface wifi#.
+ * AP processing (TX rate control, power save buffering, etc.).
+ * Use hardware TX function to send the frame. */
+int hostap_master_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+       struct hostap_interface *iface;
+       local_info_t *local;
+       int ret = 1;
+       u16 fc;
+       struct hostap_tx_data tx;
+       ap_tx_ret tx_ret;
+       struct hostap_skb_tx_data *meta;
+       int no_encrypt = 0;
+       struct ieee80211_hdr *hdr;
+
+       iface = netdev_priv(dev);
+       local = iface->local;
+
+       tx.skb = skb;
+       tx.sta_ptr = NULL;
+
+       meta = (struct hostap_skb_tx_data *) skb->cb;
+       if (meta->magic != HOSTAP_SKB_TX_DATA_MAGIC) {
+               printk(KERN_DEBUG "%s: invalid skb->cb magic (0x%08x, "
+                      "expected 0x%08x)\n",
+                      dev->name, meta->magic, HOSTAP_SKB_TX_DATA_MAGIC);
+               ret = 0;
+               iface->stats.tx_dropped++;
+               goto fail;
+       }
+
+       if (local->host_encrypt) {
+               /* Set crypt to default algorithm and key; will be replaced in
+                * AP code if STA has own alg/key */
+               tx.crypt = local->crypt[local->tx_keyidx];
+               tx.host_encrypt = 1;
+       } else {
+               tx.crypt = NULL;
+               tx.host_encrypt = 0;
+       }
+
+       if (skb->len < 24) {
+               printk(KERN_DEBUG "%s: hostap_master_start_xmit: short skb "
+                      "(len=%d)\n", dev->name, skb->len);
+               ret = 0;
+               iface->stats.tx_dropped++;
+               goto fail;
+       }
+
+       /* FIX (?):
+        * Wi-Fi 802.11b test plan suggests that AP should ignore power save
+        * bit in authentication and (re)association frames and assume tha
+        * STA remains awake for the response. */
+       tx_ret = hostap_handle_sta_tx(local, &tx);
+       skb = tx.skb;
+       meta = (struct hostap_skb_tx_data *) skb->cb;
+       hdr = (struct ieee80211_hdr *) skb->data;
+       fc = le16_to_cpu(hdr->frame_ctl);
+       switch (tx_ret) {
+       case AP_TX_CONTINUE:
+               break;
+       case AP_TX_CONTINUE_NOT_AUTHORIZED:
+               if (local->ieee_802_1x &&
+                   WLAN_FC_GET_TYPE(fc) == IEEE80211_FTYPE_DATA &&
+                   meta->ethertype != ETH_P_PAE &&
+                   !(meta->flags & HOSTAP_TX_FLAGS_WDS)) {
+                       printk(KERN_DEBUG "%s: dropped frame to unauthorized "
+                              "port (IEEE 802.1X): ethertype=0x%04x\n",
+                              dev->name, meta->ethertype);
+                       hostap_dump_tx_80211(dev->name, skb);
+
+                       ret = 0; /* drop packet */
+                       iface->stats.tx_dropped++;
+                       goto fail;
+               }
+               break;
+       case AP_TX_DROP:
+               ret = 0; /* drop packet */
+               iface->stats.tx_dropped++;
+               goto fail;
+       case AP_TX_RETRY:
+               goto fail;
+       case AP_TX_BUFFERED:
+               /* do not free skb here, it will be freed when the
+                * buffered frame is sent/timed out */
+               ret = 0;
+               goto tx_exit;
+       }
+
+       /* Request TX callback if protocol version is 2 in 802.11 header;
+        * this version 2 is a special case used between hostapd and kernel
+        * driver */
+       if (((fc & WLAN_FC_PVER) == BIT(1)) &&
+           local->ap && local->ap->tx_callback_idx && meta->tx_cb_idx == 0) {
+               meta->tx_cb_idx = local->ap->tx_callback_idx;
+
+               /* remove special version from the frame header */
+               fc &= ~WLAN_FC_PVER;
+               hdr->frame_ctl = cpu_to_le16(fc);
+       }
+
+       if (WLAN_FC_GET_TYPE(fc) != IEEE80211_FTYPE_DATA) {
+               no_encrypt = 1;
+               tx.crypt = NULL;
+       }
+
+       if (local->ieee_802_1x && meta->ethertype == ETH_P_PAE && tx.crypt &&
+           !(fc & WLAN_FC_ISWEP)) {
+               no_encrypt = 1;
+               PDEBUG(DEBUG_EXTRA2, "%s: TX: IEEE 802.1X - passing "
+                      "unencrypted EAPOL frame\n", dev->name);
+               tx.crypt = NULL; /* no encryption for IEEE 802.1X frames */
+       }
+
+       if (tx.crypt && (!tx.crypt->ops || !tx.crypt->ops->encrypt_mpdu))
+               tx.crypt = NULL;
+       else if ((tx.crypt || local->crypt[local->tx_keyidx]) && !no_encrypt) {
+               /* Add ISWEP flag both for firmware and host based encryption
+                */
+               fc |= WLAN_FC_ISWEP;
+               hdr->frame_ctl = cpu_to_le16(fc);
+       } else if (local->drop_unencrypted &&
+                  WLAN_FC_GET_TYPE(fc) == IEEE80211_FTYPE_DATA &&
+                  meta->ethertype != ETH_P_PAE) {
+               if (net_ratelimit()) {
+                       printk(KERN_DEBUG "%s: dropped unencrypted TX data "
+                              "frame (drop_unencrypted=1)\n", dev->name);
+               }
+               iface->stats.tx_dropped++;
+               ret = 0;
+               goto fail;
+       }
+
+       if (tx.crypt) {
+               skb = hostap_tx_encrypt(skb, tx.crypt);
+               if (skb == NULL) {
+                       printk(KERN_DEBUG "%s: TX - encryption failed\n",
+                              dev->name);
+                       ret = 0;
+                       goto fail;
+               }
+               meta = (struct hostap_skb_tx_data *) skb->cb;
+               if (meta->magic != HOSTAP_SKB_TX_DATA_MAGIC) {
+                       printk(KERN_DEBUG "%s: invalid skb->cb magic (0x%08x, "
+                              "expected 0x%08x) after hostap_tx_encrypt\n",
+                              dev->name, meta->magic,
+                              HOSTAP_SKB_TX_DATA_MAGIC);
+                       ret = 0;
+                       iface->stats.tx_dropped++;
+                       goto fail;
+               }
+       }
+
+       if (local->func->tx == NULL || local->func->tx(skb, dev)) {
+               ret = 0;
+               iface->stats.tx_dropped++;
+       } else {
+               ret = 0;
+               iface->stats.tx_packets++;
+               iface->stats.tx_bytes += skb->len;
+       }
+
+ fail:
+       if (!ret && skb)
+               dev_kfree_skb(skb);
+ tx_exit:
+       if (tx.sta_ptr)
+               hostap_handle_sta_release(tx.sta_ptr);
+       return ret;
+}
+
+
+EXPORT_SYMBOL(hostap_dump_tx_80211);
+EXPORT_SYMBOL(hostap_tx_encrypt);
+EXPORT_SYMBOL(hostap_master_start_xmit);
diff --git a/drivers/net/wireless/hostap/hostap_ap.c b/drivers/net/wireless/hostap/hostap_ap.c
new file mode 100644 (file)
index 0000000..596c4dd
--- /dev/null
@@ -0,0 +1,3286 @@
+/*
+ * 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>
+ *
+ * This file is to be included into hostap.c when S/W AP functionality is
+ * compiled.
+ *
+ * AP:  FIX:
+ * - if unicast Class 2 (assoc,reassoc,disassoc) frame received from
+ *   unauthenticated STA, send deauth. frame (8802.11: 5.5)
+ * - if unicast Class 3 (data with to/from DS,deauth,pspoll) frame received
+ *   from authenticated, but unassoc STA, send disassoc frame (8802.11: 5.5)
+ * - if unicast Class 3 received from unauthenticated STA, send deauth. frame
+ *   (8802.11: 5.5)
+ */
+
+static int other_ap_policy[MAX_PARM_DEVICES] = { AP_OTHER_AP_SKIP_ALL,
+                                                DEF_INTS };
+module_param_array(other_ap_policy, int, NULL, 0444);
+MODULE_PARM_DESC(other_ap_policy, "Other AP beacon monitoring policy (0-3)");
+
+static int ap_max_inactivity[MAX_PARM_DEVICES] = { AP_MAX_INACTIVITY_SEC,
+                                                  DEF_INTS };
+module_param_array(ap_max_inactivity, int, NULL, 0444);
+MODULE_PARM_DESC(ap_max_inactivity, "AP timeout (in seconds) for station "
+                "inactivity");
+
+static int ap_bridge_packets[MAX_PARM_DEVICES] = { 1, DEF_INTS };
+module_param_array(ap_bridge_packets, int, NULL, 0444);
+MODULE_PARM_DESC(ap_bridge_packets, "Bridge packets directly between "
+                "stations");
+
+static int autom_ap_wds[MAX_PARM_DEVICES] = { 0, DEF_INTS };
+module_param_array(autom_ap_wds, int, NULL, 0444);
+MODULE_PARM_DESC(autom_ap_wds, "Add WDS connections to other APs "
+                "automatically");
+
+
+static struct sta_info* ap_get_sta(struct ap_data *ap, u8 *sta);
+static void hostap_event_expired_sta(struct net_device *dev,
+                                    struct sta_info *sta);
+static void handle_add_proc_queue(void *data);
+
+#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
+static void handle_wds_oper_queue(void *data);
+static void prism2_send_mgmt(struct net_device *dev,
+                            u16 type_subtype, char *body,
+                            int body_len, u8 *addr, u16 tx_cb_idx);
+#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
+
+
+#ifndef PRISM2_NO_PROCFS_DEBUG
+static int ap_debug_proc_read(char *page, char **start, off_t off,
+                             int count, int *eof, void *data)
+{
+       char *p = page;
+       struct ap_data *ap = (struct ap_data *) data;
+
+       if (off != 0) {
+               *eof = 1;
+               return 0;
+       }
+
+       p += sprintf(p, "BridgedUnicastFrames=%u\n", ap->bridged_unicast);
+       p += sprintf(p, "BridgedMulticastFrames=%u\n", ap->bridged_multicast);
+       p += sprintf(p, "max_inactivity=%u\n", ap->max_inactivity / HZ);
+       p += sprintf(p, "bridge_packets=%u\n", ap->bridge_packets);
+       p += sprintf(p, "nullfunc_ack=%u\n", ap->nullfunc_ack);
+       p += sprintf(p, "autom_ap_wds=%u\n", ap->autom_ap_wds);
+       p += sprintf(p, "auth_algs=%u\n", ap->local->auth_algs);
+       p += sprintf(p, "tx_drop_nonassoc=%u\n", ap->tx_drop_nonassoc);
+
+       return (p - page);
+}
+#endif /* PRISM2_NO_PROCFS_DEBUG */
+
+
+static void ap_sta_hash_add(struct ap_data *ap, struct sta_info *sta)
+{
+       sta->hnext = ap->sta_hash[STA_HASH(sta->addr)];
+       ap->sta_hash[STA_HASH(sta->addr)] = sta;
+}
+
+static void ap_sta_hash_del(struct ap_data *ap, struct sta_info *sta)
+{
+       struct sta_info *s;
+
+       s = ap->sta_hash[STA_HASH(sta->addr)];
+       if (s == NULL) return;
+       if (memcmp(s->addr, sta->addr, ETH_ALEN) == 0) {
+               ap->sta_hash[STA_HASH(sta->addr)] = s->hnext;
+               return;
+       }
+
+       while (s->hnext != NULL && memcmp(s->hnext->addr, sta->addr, ETH_ALEN)
+              != 0)
+               s = s->hnext;
+       if (s->hnext != NULL)
+               s->hnext = s->hnext->hnext;
+       else
+               printk("AP: could not remove STA " MACSTR " from hash table\n",
+                      MAC2STR(sta->addr));
+}
+
+static void ap_free_sta(struct ap_data *ap, struct sta_info *sta)
+{
+       if (sta->ap && sta->local)
+               hostap_event_expired_sta(sta->local->dev, sta);
+
+       if (ap->proc != NULL) {
+               char name[20];
+               sprintf(name, MACSTR, MAC2STR(sta->addr));
+               remove_proc_entry(name, ap->proc);
+       }
+
+       if (sta->crypt) {
+               sta->crypt->ops->deinit(sta->crypt->priv);
+               kfree(sta->crypt);
+               sta->crypt = NULL;
+       }
+
+       skb_queue_purge(&sta->tx_buf);
+
+       ap->num_sta--;
+#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
+       if (sta->aid > 0)
+               ap->sta_aid[sta->aid - 1] = NULL;
+
+       if (!sta->ap && sta->u.sta.challenge)
+               kfree(sta->u.sta.challenge);
+       del_timer(&sta->timer);
+#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
+
+       kfree(sta);
+}
+
+
+static void hostap_set_tim(local_info_t *local, int aid, int set)
+{
+       if (local->func->set_tim)
+               local->func->set_tim(local->dev, aid, set);
+}
+
+
+static void hostap_event_new_sta(struct net_device *dev, struct sta_info *sta)
+{
+       union iwreq_data wrqu;
+       memset(&wrqu, 0, sizeof(wrqu));
+       memcpy(wrqu.addr.sa_data, sta->addr, ETH_ALEN);
+       wrqu.addr.sa_family = ARPHRD_ETHER;
+       wireless_send_event(dev, IWEVREGISTERED, &wrqu, NULL);
+}
+
+
+static void hostap_event_expired_sta(struct net_device *dev,
+                                    struct sta_info *sta)
+{
+       union iwreq_data wrqu;
+       memset(&wrqu, 0, sizeof(wrqu));
+       memcpy(wrqu.addr.sa_data, sta->addr, ETH_ALEN);
+       wrqu.addr.sa_family = ARPHRD_ETHER;
+       wireless_send_event(dev, IWEVEXPIRED, &wrqu, NULL);
+}
+
+
+#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
+
+static void ap_handle_timer(unsigned long data)
+{
+       struct sta_info *sta = (struct sta_info *) data;
+       local_info_t *local;
+       struct ap_data *ap;
+       unsigned long next_time = 0;
+       int was_assoc;
+
+       if (sta == NULL || sta->local == NULL || sta->local->ap == NULL) {
+               PDEBUG(DEBUG_AP, "ap_handle_timer() called with NULL data\n");
+               return;
+       }
+
+       local = sta->local;
+       ap = local->ap;
+       was_assoc = sta->flags & WLAN_STA_ASSOC;
+
+       if (atomic_read(&sta->users) != 0)
+               next_time = jiffies + HZ;
+       else if ((sta->flags & WLAN_STA_PERM) && !(sta->flags & WLAN_STA_AUTH))
+               next_time = jiffies + ap->max_inactivity;
+
+       if (time_before(jiffies, sta->last_rx + ap->max_inactivity)) {
+               /* station activity detected; reset timeout state */
+               sta->timeout_next = STA_NULLFUNC;
+               next_time = sta->last_rx + ap->max_inactivity;
+       } else if (sta->timeout_next == STA_DISASSOC &&
+                  !(sta->flags & WLAN_STA_PENDING_POLL)) {
+               /* STA ACKed data nullfunc frame poll */
+               sta->timeout_next = STA_NULLFUNC;
+               next_time = jiffies + ap->max_inactivity;
+       }
+
+       if (next_time) {
+               sta->timer.expires = next_time;
+               add_timer(&sta->timer);
+               return;
+       }
+
+       if (sta->ap)
+               sta->timeout_next = STA_DEAUTH;
+
+       if (sta->timeout_next == STA_DEAUTH && !(sta->flags & WLAN_STA_PERM)) {
+               spin_lock(&ap->sta_table_lock);
+               ap_sta_hash_del(ap, sta);
+               list_del(&sta->list);
+               spin_unlock(&ap->sta_table_lock);
+               sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC);
+       } else if (sta->timeout_next == STA_DISASSOC)
+               sta->flags &= ~WLAN_STA_ASSOC;
+
+       if (was_assoc && !(sta->flags & WLAN_STA_ASSOC) && !sta->ap)
+               hostap_event_expired_sta(local->dev, sta);
+
+       if (sta->timeout_next == STA_DEAUTH && sta->aid > 0 &&
+           !skb_queue_empty(&sta->tx_buf)) {
+               hostap_set_tim(local, sta->aid, 0);
+               sta->flags &= ~WLAN_STA_TIM;
+       }
+
+       if (sta->ap) {
+               if (ap->autom_ap_wds) {
+                       PDEBUG(DEBUG_AP, "%s: removing automatic WDS "
+                              "connection to AP " MACSTR "\n",
+                              local->dev->name, MAC2STR(sta->addr));
+                       hostap_wds_link_oper(local, sta->addr, WDS_DEL);
+               }
+       } else if (sta->timeout_next == STA_NULLFUNC) {
+               /* send data frame to poll STA and check whether this frame
+                * is ACKed */
+               /* FIX: IEEE80211_STYPE_NULLFUNC would be more appropriate, but
+                * it is apparently not retried so TX Exc events are not
+                * received for it */
+               sta->flags |= WLAN_STA_PENDING_POLL;
+               prism2_send_mgmt(local->dev, IEEE80211_FTYPE_DATA |
+                                IEEE80211_STYPE_DATA, NULL, 0,
+                                sta->addr, ap->tx_callback_poll);
+       } else {
+               int deauth = sta->timeout_next == STA_DEAUTH;
+               u16 resp;
+               PDEBUG(DEBUG_AP, "%s: sending %s info to STA " MACSTR
+                      "(last=%lu, jiffies=%lu)\n",
+                      local->dev->name,
+                      deauth ? "deauthentication" : "disassociation",
+                      MAC2STR(sta->addr), sta->last_rx, jiffies);
+
+               resp = cpu_to_le16(deauth ? WLAN_REASON_PREV_AUTH_NOT_VALID :
+                                  WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY);
+               prism2_send_mgmt(local->dev, IEEE80211_FTYPE_MGMT |
+                                (deauth ? IEEE80211_STYPE_DEAUTH :
+                                 IEEE80211_STYPE_DISASSOC),
+                                (char *) &resp, 2, sta->addr, 0);
+       }
+
+       if (sta->timeout_next == STA_DEAUTH) {
+               if (sta->flags & WLAN_STA_PERM) {
+                       PDEBUG(DEBUG_AP, "%s: STA " MACSTR " would have been "
+                              "removed, but it has 'perm' flag\n",
+                              local->dev->name, MAC2STR(sta->addr));
+               } else
+                       ap_free_sta(ap, sta);
+               return;
+       }
+
+       if (sta->timeout_next == STA_NULLFUNC) {
+               sta->timeout_next = STA_DISASSOC;
+               sta->timer.expires = jiffies + AP_DISASSOC_DELAY;
+       } else {
+               sta->timeout_next = STA_DEAUTH;
+               sta->timer.expires = jiffies + AP_DEAUTH_DELAY;
+       }
+
+       add_timer(&sta->timer);
+}
+
+
+void hostap_deauth_all_stas(struct net_device *dev, struct ap_data *ap,
+                           int resend)
+{
+       u8 addr[ETH_ALEN];
+       u16 resp;
+       int i;
+
+       PDEBUG(DEBUG_AP, "%s: Deauthenticate all stations\n", dev->name);
+       memset(addr, 0xff, ETH_ALEN);
+
+       resp = __constant_cpu_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID);
+
+       /* deauth message sent; try to resend it few times; the message is
+        * broadcast, so it may be delayed until next DTIM; there is not much
+        * else we can do at this point since the driver is going to be shut
+        * down */
+       for (i = 0; i < 5; i++) {
+               prism2_send_mgmt(dev, IEEE80211_FTYPE_MGMT |
+                                IEEE80211_STYPE_DEAUTH,
+                                (char *) &resp, 2, addr, 0);
+
+               if (!resend || ap->num_sta <= 0)
+                       return;
+
+               mdelay(50);
+       }
+}
+
+
+static int ap_control_proc_read(char *page, char **start, off_t off,
+                               int count, int *eof, void *data)
+{
+       char *p = page;
+       struct ap_data *ap = (struct ap_data *) data;
+       char *policy_txt;
+       struct list_head *ptr;
+       struct mac_entry *entry;
+
+       if (off != 0) {
+               *eof = 1;
+               return 0;
+       }
+
+       switch (ap->mac_restrictions.policy) {
+       case MAC_POLICY_OPEN:
+               policy_txt = "open";
+               break;
+       case MAC_POLICY_ALLOW:
+               policy_txt = "allow";
+               break;
+       case MAC_POLICY_DENY:
+               policy_txt = "deny";
+               break;
+       default:
+               policy_txt = "unknown";
+               break;
+       };
+       p += sprintf(p, "MAC policy: %s\n", policy_txt);
+       p += sprintf(p, "MAC entries: %u\n", ap->mac_restrictions.entries);
+       p += sprintf(p, "MAC list:\n");
+       spin_lock_bh(&ap->mac_restrictions.lock);
+       for (ptr = ap->mac_restrictions.mac_list.next;
+            ptr != &ap->mac_restrictions.mac_list; ptr = ptr->next) {
+               if (p - page > PAGE_SIZE - 80) {
+                       p += sprintf(p, "All entries did not fit one page.\n");
+                       break;
+               }
+
+               entry = list_entry(ptr, struct mac_entry, list);
+               p += sprintf(p, MACSTR "\n", MAC2STR(entry->addr));
+       }
+       spin_unlock_bh(&ap->mac_restrictions.lock);
+
+       return (p - page);
+}
+
+
+static int ap_control_add_mac(struct mac_restrictions *mac_restrictions,
+                             u8 *mac)
+{
+       struct mac_entry *entry;
+
+       entry = kmalloc(sizeof(struct mac_entry), GFP_KERNEL);
+       if (entry == NULL)
+               return -1;
+
+       memcpy(entry->addr, mac, ETH_ALEN);
+
+       spin_lock_bh(&mac_restrictions->lock);
+       list_add_tail(&entry->list, &mac_restrictions->mac_list);
+       mac_restrictions->entries++;
+       spin_unlock_bh(&mac_restrictions->lock);
+
+       return 0;
+}
+
+
+static int ap_control_del_mac(struct mac_restrictions *mac_restrictions,
+                             u8 *mac)
+{
+       struct list_head *ptr;
+       struct mac_entry *entry;
+
+       spin_lock_bh(&mac_restrictions->lock);
+       for (ptr = mac_restrictions->mac_list.next;
+            ptr != &mac_restrictions->mac_list; ptr = ptr->next) {
+               entry = list_entry(ptr, struct mac_entry, list);
+
+               if (memcmp(entry->addr, mac, ETH_ALEN) == 0) {
+                       list_del(ptr);
+                       kfree(entry);
+                       mac_restrictions->entries--;
+                       spin_unlock_bh(&mac_restrictions->lock);
+                       return 0;
+               }
+       }
+       spin_unlock_bh(&mac_restrictions->lock);
+       return -1;
+}
+
+
+static int ap_control_mac_deny(struct mac_restrictions *mac_restrictions,
+                              u8 *mac)
+{
+       struct list_head *ptr;
+       struct mac_entry *entry;
+       int found = 0;
+
+       if (mac_restrictions->policy == MAC_POLICY_OPEN)
+               return 0;
+
+       spin_lock_bh(&mac_restrictions->lock);
+       for (ptr = mac_restrictions->mac_list.next;
+            ptr != &mac_restrictions->mac_list; ptr = ptr->next) {
+               entry = list_entry(ptr, struct mac_entry, list);
+
+               if (memcmp(entry->addr, mac, ETH_ALEN) == 0) {
+                       found = 1;
+                       break;
+               }
+       }
+       spin_unlock_bh(&mac_restrictions->lock);
+
+       if (mac_restrictions->policy == MAC_POLICY_ALLOW)
+               return !found;
+       else
+               return found;
+}
+
+
+static void ap_control_flush_macs(struct mac_restrictions *mac_restrictions)
+{
+       struct list_head *ptr, *n;
+       struct mac_entry *entry;
+
+       if (mac_restrictions->entries == 0)
+               return;
+
+       spin_lock_bh(&mac_restrictions->lock);
+       for (ptr = mac_restrictions->mac_list.next, n = ptr->next;
+            ptr != &mac_restrictions->mac_list;
+            ptr = n, n = ptr->next) {
+               entry = list_entry(ptr, struct mac_entry, list);
+               list_del(ptr);
+               kfree(entry);
+       }
+       mac_restrictions->entries = 0;
+       spin_unlock_bh(&mac_restrictions->lock);
+}
+
+
+static int ap_control_kick_mac(struct ap_data *ap, struct net_device *dev,
+                              u8 *mac)
+{
+       struct sta_info *sta;
+       u16 resp;
+
+       spin_lock_bh(&ap->sta_table_lock);
+       sta = ap_get_sta(ap, mac);
+       if (sta) {
+               ap_sta_hash_del(ap, sta);
+               list_del(&sta->list);
+       }
+       spin_unlock_bh(&ap->sta_table_lock);
+
+       if (!sta)
+               return -EINVAL;
+
+       resp = cpu_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID);
+       prism2_send_mgmt(dev, IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_DEAUTH,
+                        (char *) &resp, 2, sta->addr, 0);
+
+       if ((sta->flags & WLAN_STA_ASSOC) && !sta->ap)
+               hostap_event_expired_sta(dev, sta);
+
+       ap_free_sta(ap, sta);
+
+       return 0;
+}
+
+#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
+
+
+static void ap_control_kickall(struct ap_data *ap)
+{
+       struct list_head *ptr, *n;
+       struct sta_info *sta;
+
+       spin_lock_bh(&ap->sta_table_lock);
+       for (ptr = ap->sta_list.next, n = ptr->next; ptr != &ap->sta_list;
+            ptr = n, n = ptr->next) {
+               sta = list_entry(ptr, struct sta_info, list);
+               ap_sta_hash_del(ap, sta);
+               list_del(&sta->list);
+               if ((sta->flags & WLAN_STA_ASSOC) && !sta->ap && sta->local)
+                       hostap_event_expired_sta(sta->local->dev, sta);
+               ap_free_sta(ap, sta);
+       }
+       spin_unlock_bh(&ap->sta_table_lock);
+}
+
+
+#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
+
+#define PROC_LIMIT (PAGE_SIZE - 80)
+
+static int prism2_ap_proc_read(char *page, char **start, off_t off,
+                              int count, int *eof, void *data)
+{
+       char *p = page;
+       struct ap_data *ap = (struct ap_data *) data;
+       struct list_head *ptr;
+       int i;
+
+       if (off > PROC_LIMIT) {
+               *eof = 1;
+               return 0;
+       }
+
+       p += sprintf(p, "# BSSID CHAN SIGNAL NOISE RATE SSID FLAGS\n");
+       spin_lock_bh(&ap->sta_table_lock);
+       for (ptr = ap->sta_list.next; ptr != &ap->sta_list; ptr = ptr->next) {
+               struct sta_info *sta = (struct sta_info *) ptr;
+
+               if (!sta->ap)
+                       continue;
+
+               p += sprintf(p, MACSTR " %d %d %d %d '", MAC2STR(sta->addr),
+                            sta->u.ap.channel, sta->last_rx_signal,
+                            sta->last_rx_silence, sta->last_rx_rate);
+               for (i = 0; i < sta->u.ap.ssid_len; i++)
+                       p += sprintf(p, ((sta->u.ap.ssid[i] >= 32 &&
+                                         sta->u.ap.ssid[i] < 127) ?
+                                        "%c" : "<%02x>"),
+                                    sta->u.ap.ssid[i]);
+               p += sprintf(p, "'");
+               if (sta->capability & WLAN_CAPABILITY_ESS)
+                       p += sprintf(p, " [ESS]");
+               if (sta->capability & WLAN_CAPABILITY_IBSS)
+                       p += sprintf(p, " [IBSS]");
+               if (sta->capability & WLAN_CAPABILITY_PRIVACY)
+                       p += sprintf(p, " [WEP]");
+               p += sprintf(p, "\n");
+
+               if ((p - page) > PROC_LIMIT) {
+                       printk(KERN_DEBUG "hostap: ap proc did not fit\n");
+                       break;
+               }
+       }
+       spin_unlock_bh(&ap->sta_table_lock);
+
+       if ((p - page) <= off) {
+               *eof = 1;
+               return 0;
+       }
+
+       *start = page + off;
+
+       return (p - page - off);
+}
+#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
+
+
+void hostap_check_sta_fw_version(struct ap_data *ap, int sta_fw_ver)
+{
+       if (!ap)
+               return;
+
+       if (sta_fw_ver == PRISM2_FW_VER(0,8,0)) {
+               PDEBUG(DEBUG_AP, "Using data::nullfunc ACK workaround - "
+                      "firmware upgrade recommended\n");
+               ap->nullfunc_ack = 1;
+       } else
+               ap->nullfunc_ack = 0;
+
+       if (sta_fw_ver == PRISM2_FW_VER(1,4,2)) {
+               printk(KERN_WARNING "%s: Warning: secondary station firmware "
+                      "version 1.4.2 does not seem to work in Host AP mode\n",
+                      ap->local->dev->name);
+       }
+}
+
+
+/* Called only as a tasklet (software IRQ) */
+static void hostap_ap_tx_cb(struct sk_buff *skb, int ok, void *data)
+{
+       struct ap_data *ap = data;
+       u16 fc;
+       struct ieee80211_hdr *hdr;
+
+       if (!ap->local->hostapd || !ap->local->apdev) {
+               dev_kfree_skb(skb);
+               return;
+       }
+
+       hdr = (struct ieee80211_hdr *) skb->data;
+       fc = le16_to_cpu(hdr->frame_ctl);
+
+       /* Pass the TX callback frame to the hostapd; use 802.11 header version
+        * 1 to indicate failure (no ACK) and 2 success (frame ACKed) */
+
+       fc &= ~WLAN_FC_PVER;
+       fc |= ok ? BIT(1) : BIT(0);
+       hdr->frame_ctl = cpu_to_le16(fc);
+
+       skb->dev = ap->local->apdev;
+       skb_pull(skb, hostap_80211_get_hdrlen(fc));
+       skb->pkt_type = PACKET_OTHERHOST;
+       skb->protocol = __constant_htons(ETH_P_802_2);
+       memset(skb->cb, 0, sizeof(skb->cb));
+       netif_rx(skb);
+}
+
+
+#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
+/* Called only as a tasklet (software IRQ) */
+static void hostap_ap_tx_cb_auth(struct sk_buff *skb, int ok, void *data)
+{
+       struct ap_data *ap = data;
+       struct net_device *dev = ap->local->dev;
+       struct ieee80211_hdr *hdr;
+       u16 fc, *pos, auth_alg, auth_transaction, status;
+       struct sta_info *sta = NULL;
+       char *txt = NULL;
+
+       if (ap->local->hostapd) {
+               dev_kfree_skb(skb);
+               return;
+       }
+
+       hdr = (struct ieee80211_hdr *) skb->data;
+       fc = le16_to_cpu(hdr->frame_ctl);
+       if (WLAN_FC_GET_TYPE(fc) != IEEE80211_FTYPE_MGMT ||
+           WLAN_FC_GET_STYPE(fc) != IEEE80211_STYPE_AUTH ||
+           skb->len < IEEE80211_MGMT_HDR_LEN + 6) {
+               printk(KERN_DEBUG "%s: hostap_ap_tx_cb_auth received invalid "
+                      "frame\n", dev->name);
+               dev_kfree_skb(skb);
+               return;
+       }
+
+       pos = (u16 *) (skb->data + IEEE80211_MGMT_HDR_LEN);
+       auth_alg = le16_to_cpu(*pos++);
+       auth_transaction = le16_to_cpu(*pos++);
+       status = le16_to_cpu(*pos++);
+
+       if (!ok) {
+               txt = "frame was not ACKed";
+               goto done;
+       }
+
+       spin_lock(&ap->sta_table_lock);
+       sta = ap_get_sta(ap, hdr->addr1);
+       if (sta)
+               atomic_inc(&sta->users);
+       spin_unlock(&ap->sta_table_lock);
+
+       if (!sta) {
+               txt = "STA not found";
+               goto done;
+       }
+
+       if (status == WLAN_STATUS_SUCCESS &&
+           ((auth_alg == WLAN_AUTH_OPEN && auth_transaction == 2) ||
+            (auth_alg == WLAN_AUTH_SHARED_KEY && auth_transaction == 4))) {
+               txt = "STA authenticated";
+               sta->flags |= WLAN_STA_AUTH;
+               sta->last_auth = jiffies;
+       } else if (status != WLAN_STATUS_SUCCESS)
+               txt = "authentication failed";
+
+ done:
+       if (sta)
+               atomic_dec(&sta->users);
+       if (txt) {
+               PDEBUG(DEBUG_AP, "%s: " MACSTR " auth_cb - alg=%d trans#=%d "
+                      "status=%d - %s\n",
+                      dev->name, MAC2STR(hdr->addr1), auth_alg,
+                      auth_transaction, status, txt);
+       }
+       dev_kfree_skb(skb);
+}
+
+
+/* Called only as a tasklet (software IRQ) */
+static void hostap_ap_tx_cb_assoc(struct sk_buff *skb, int ok, void *data)
+{
+       struct ap_data *ap = data;
+       struct net_device *dev = ap->local->dev;
+       struct ieee80211_hdr *hdr;
+       u16 fc, *pos, status;
+       struct sta_info *sta = NULL;
+       char *txt = NULL;
+
+       if (ap->local->hostapd) {
+               dev_kfree_skb(skb);
+               return;
+       }
+
+       hdr = (struct ieee80211_hdr *) skb->data;
+       fc = le16_to_cpu(hdr->frame_ctl);
+       if (WLAN_FC_GET_TYPE(fc) != IEEE80211_FTYPE_MGMT ||
+           (WLAN_FC_GET_STYPE(fc) != IEEE80211_STYPE_ASSOC_RESP &&
+            WLAN_FC_GET_STYPE(fc) != IEEE80211_STYPE_REASSOC_RESP) ||
+           skb->len < IEEE80211_MGMT_HDR_LEN + 4) {
+               printk(KERN_DEBUG "%s: hostap_ap_tx_cb_assoc received invalid "
+                      "frame\n", dev->name);
+               dev_kfree_skb(skb);
+               return;
+       }
+
+       if (!ok) {
+               txt = "frame was not ACKed";
+               goto done;
+       }
+
+       spin_lock(&ap->sta_table_lock);
+       sta = ap_get_sta(ap, hdr->addr1);
+       if (sta)
+               atomic_inc(&sta->users);
+       spin_unlock(&ap->sta_table_lock);
+
+       if (!sta) {
+               txt = "STA not found";
+               goto done;
+       }
+
+       pos = (u16 *) (skb->data + IEEE80211_MGMT_HDR_LEN);
+       pos++;
+       status = le16_to_cpu(*pos++);
+       if (status == WLAN_STATUS_SUCCESS) {
+               if (!(sta->flags & WLAN_STA_ASSOC))
+                       hostap_event_new_sta(dev, sta);
+               txt = "STA associated";
+               sta->flags |= WLAN_STA_ASSOC;
+               sta->last_assoc = jiffies;
+       } else
+               txt = "association failed";
+
+ done:
+       if (sta)
+               atomic_dec(&sta->users);
+       if (txt) {
+               PDEBUG(DEBUG_AP, "%s: " MACSTR " assoc_cb - %s\n",
+                      dev->name, MAC2STR(hdr->addr1), txt);
+       }
+       dev_kfree_skb(skb);
+}
+
+/* Called only as a tasklet (software IRQ); TX callback for poll frames used
+ * in verifying whether the STA is still present. */
+static void hostap_ap_tx_cb_poll(struct sk_buff *skb, int ok, void *data)
+{
+       struct ap_data *ap = data;
+       struct ieee80211_hdr *hdr;
+       struct sta_info *sta;
+
+       if (skb->len < 24)
+               goto fail;
+       hdr = (struct ieee80211_hdr *) skb->data;
+       if (ok) {
+               spin_lock(&ap->sta_table_lock);
+               sta = ap_get_sta(ap, hdr->addr1);
+               if (sta)
+                       sta->flags &= ~WLAN_STA_PENDING_POLL;
+               spin_unlock(&ap->sta_table_lock);
+       } else {
+               PDEBUG(DEBUG_AP, "%s: STA " MACSTR " did not ACK activity "
+                      "poll frame\n", ap->local->dev->name,
+                      MAC2STR(hdr->addr1));
+       }
+
+ fail:
+       dev_kfree_skb(skb);
+}
+#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
+
+
+void hostap_init_data(local_info_t *local)
+{
+       struct ap_data *ap = local->ap;
+
+       if (ap == NULL) {
+               printk(KERN_WARNING "hostap_init_data: ap == NULL\n");
+               return;
+       }
+       memset(ap, 0, sizeof(struct ap_data));
+       ap->local = local;
+
+       ap->ap_policy = GET_INT_PARM(other_ap_policy, local->card_idx);
+       ap->bridge_packets = GET_INT_PARM(ap_bridge_packets, local->card_idx);
+       ap->max_inactivity =
+               GET_INT_PARM(ap_max_inactivity, local->card_idx) * HZ;
+       ap->autom_ap_wds = GET_INT_PARM(autom_ap_wds, local->card_idx);
+
+       spin_lock_init(&ap->sta_table_lock);
+       INIT_LIST_HEAD(&ap->sta_list);
+
+       /* Initialize task queue structure for AP management */
+       INIT_WORK(&local->ap->add_sta_proc_queue, handle_add_proc_queue, ap);
+
+       ap->tx_callback_idx =
+               hostap_tx_callback_register(local, hostap_ap_tx_cb, ap);
+       if (ap->tx_callback_idx == 0)
+               printk(KERN_WARNING "%s: failed to register TX callback for "
+                      "AP\n", local->dev->name);
+#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
+       INIT_WORK(&local->ap->wds_oper_queue, handle_wds_oper_queue, local);
+
+       ap->tx_callback_auth =
+               hostap_tx_callback_register(local, hostap_ap_tx_cb_auth, ap);
+       ap->tx_callback_assoc =
+               hostap_tx_callback_register(local, hostap_ap_tx_cb_assoc, ap);
+       ap->tx_callback_poll =
+               hostap_tx_callback_register(local, hostap_ap_tx_cb_poll, ap);
+       if (ap->tx_callback_auth == 0 || ap->tx_callback_assoc == 0 ||
+               ap->tx_callback_poll == 0)
+               printk(KERN_WARNING "%s: failed to register TX callback for "
+                      "AP\n", local->dev->name);
+
+       spin_lock_init(&ap->mac_restrictions.lock);
+       INIT_LIST_HEAD(&ap->mac_restrictions.mac_list);
+#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
+
+       ap->initialized = 1;
+}
+
+
+void hostap_init_ap_proc(local_info_t *local)
+{
+       struct ap_data *ap = local->ap;
+
+       ap->proc = local->proc;
+       if (ap->proc == NULL)
+               return;
+
+#ifndef PRISM2_NO_PROCFS_DEBUG
+       create_proc_read_entry("ap_debug", 0, ap->proc,
+                              ap_debug_proc_read, ap);
+#endif /* PRISM2_NO_PROCFS_DEBUG */
+
+#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
+       create_proc_read_entry("ap_control", 0, ap->proc,
+                              ap_control_proc_read, ap);
+       create_proc_read_entry("ap", 0, ap->proc,
+                              prism2_ap_proc_read, ap);
+#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
+
+}
+
+
+void hostap_free_data(struct ap_data *ap)
+{
+       struct list_head *n, *ptr;
+
+       if (ap == NULL || !ap->initialized) {
+               printk(KERN_DEBUG "hostap_free_data: ap has not yet been "
+                      "initialized - skip resource freeing\n");
+               return;
+       }
+
+#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
+       if (ap->crypt)
+               ap->crypt->deinit(ap->crypt_priv);
+       ap->crypt = ap->crypt_priv = NULL;
+#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
+
+       list_for_each_safe(ptr, n, &ap->sta_list) {
+               struct sta_info *sta = list_entry(ptr, struct sta_info, list);
+               ap_sta_hash_del(ap, sta);
+               list_del(&sta->list);
+               if ((sta->flags & WLAN_STA_ASSOC) && !sta->ap && sta->local)
+                       hostap_event_expired_sta(sta->local->dev, sta);
+               ap_free_sta(ap, sta);
+       }
+
+#ifndef PRISM2_NO_PROCFS_DEBUG
+       if (ap->proc != NULL) {
+               remove_proc_entry("ap_debug", ap->proc);
+       }
+#endif /* PRISM2_NO_PROCFS_DEBUG */
+
+#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
+       if (ap->proc != NULL) {
+         remove_proc_entry("ap", ap->proc);
+               remove_proc_entry("ap_control", ap->proc);
+       }
+       ap_control_flush_macs(&ap->mac_restrictions);
+#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
+
+       ap->initialized = 0;
+}
+
+
+/* caller should have mutex for AP STA list handling */
+static struct sta_info* ap_get_sta(struct ap_data *ap, u8 *sta)
+{
+       struct sta_info *s;
+
+       s = ap->sta_hash[STA_HASH(sta)];
+       while (s != NULL && memcmp(s->addr, sta, ETH_ALEN) != 0)
+               s = s->hnext;
+       return s;
+}
+
+
+#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
+
+/* Called from timer handler and from scheduled AP queue handlers */
+static void prism2_send_mgmt(struct net_device *dev,
+                            u16 type_subtype, char *body,
+                            int body_len, u8 *addr, u16 tx_cb_idx)
+{
+       struct hostap_interface *iface;
+       local_info_t *local;
+       struct ieee80211_hdr *hdr;
+       u16 fc;
+       struct sk_buff *skb;
+       struct hostap_skb_tx_data *meta;
+       int hdrlen;
+
+       iface = netdev_priv(dev);
+       local = iface->local;
+       dev = local->dev; /* always use master radio device */
+       iface = netdev_priv(dev);
+
+       if (!(dev->flags & IFF_UP)) {
+               PDEBUG(DEBUG_AP, "%s: prism2_send_mgmt - device is not UP - "
+                      "cannot send frame\n", dev->name);
+               return;
+       }
+
+       skb = dev_alloc_skb(sizeof(*hdr) + body_len);
+       if (skb == NULL) {
+               PDEBUG(DEBUG_AP, "%s: prism2_send_mgmt failed to allocate "
+                      "skb\n", dev->name);
+               return;
+       }
+
+       fc = type_subtype;
+       hdrlen = hostap_80211_get_hdrlen(fc);
+       hdr = (struct ieee80211_hdr *) skb_put(skb, hdrlen);
+       if (body)
+               memcpy(skb_put(skb, body_len), body, body_len);
+
+       memset(hdr, 0, hdrlen);
+
+       /* FIX: ctrl::ack sending used special HFA384X_TX_CTRL_802_11
+        * tx_control instead of using local->tx_control */
+
+
+       memcpy(hdr->addr1, addr, ETH_ALEN); /* DA / RA */
+       if (WLAN_FC_GET_TYPE(fc) == IEEE80211_FTYPE_DATA) {
+               fc |= WLAN_FC_FROMDS;
+               memcpy(hdr->addr2, dev->dev_addr, ETH_ALEN); /* BSSID */
+               memcpy(hdr->addr3, dev->dev_addr, ETH_ALEN); /* SA */
+       } else if (WLAN_FC_GET_TYPE(fc) == IEEE80211_FTYPE_CTL) {
+               /* control:ACK does not have addr2 or addr3 */
+               memset(hdr->addr2, 0, ETH_ALEN);
+               memset(hdr->addr3, 0, ETH_ALEN);
+       } else {
+               memcpy(hdr->addr2, dev->dev_addr, ETH_ALEN); /* SA */
+               memcpy(hdr->addr3, dev->dev_addr, ETH_ALEN); /* BSSID */
+       }
+
+       hdr->frame_ctl = cpu_to_le16(fc);
+
+       meta = (struct hostap_skb_tx_data *) skb->cb;
+       memset(meta, 0, sizeof(*meta));
+       meta->magic = HOSTAP_SKB_TX_DATA_MAGIC;
+       meta->iface = iface;
+       meta->tx_cb_idx = tx_cb_idx;
+
+       skb->dev = dev;
+       skb->mac.raw = skb->nh.raw = skb->data;
+       dev_queue_xmit(skb);
+}
+#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
+
+
+static int prism2_sta_proc_read(char *page, char **start, off_t off,
+                               int count, int *eof, void *data)
+{
+       char *p = page;
+       struct sta_info *sta = (struct sta_info *) data;
+       int i;
+
+       /* FIX: possible race condition.. the STA data could have just expired,
+        * but proc entry was still here so that the read could have started;
+        * some locking should be done here.. */
+
+       if (off != 0) {
+               *eof = 1;
+               return 0;
+       }
+
+       p += sprintf(p, "%s=" MACSTR "\nusers=%d\naid=%d\n"
+                    "flags=0x%04x%s%s%s%s%s%s%s\n"
+                    "capability=0x%02x\nlisten_interval=%d\nsupported_rates=",
+                    sta->ap ? "AP" : "STA",
+                    MAC2STR(sta->addr), atomic_read(&sta->users), sta->aid,
+                    sta->flags,
+                    sta->flags & WLAN_STA_AUTH ? " AUTH" : "",
+                    sta->flags & WLAN_STA_ASSOC ? " ASSOC" : "",
+                    sta->flags & WLAN_STA_PS ? " PS" : "",
+                    sta->flags & WLAN_STA_TIM ? " TIM" : "",
+                    sta->flags & WLAN_STA_PERM ? " PERM" : "",
+                    sta->flags & WLAN_STA_AUTHORIZED ? " AUTHORIZED" : "",
+                    sta->flags & WLAN_STA_PENDING_POLL ? " POLL" : "",
+                    sta->capability, sta->listen_interval);
+       /* supported_rates: 500 kbit/s units with msb ignored */
+       for (i = 0; i < sizeof(sta->supported_rates); i++)
+               if (sta->supported_rates[i] != 0)
+                       p += sprintf(p, "%d%sMbps ",
+                                    (sta->supported_rates[i] & 0x7f) / 2,
+                                    sta->supported_rates[i] & 1 ? ".5" : "");
+       p += sprintf(p, "\njiffies=%lu\nlast_auth=%lu\nlast_assoc=%lu\n"
+                    "last_rx=%lu\nlast_tx=%lu\nrx_packets=%lu\n"
+                    "tx_packets=%lu\n"
+                    "rx_bytes=%lu\ntx_bytes=%lu\nbuffer_count=%d\n"
+                    "last_rx: silence=%d dBm signal=%d dBm rate=%d%s Mbps\n"
+                    "tx_rate=%d\ntx[1M]=%d\ntx[2M]=%d\ntx[5.5M]=%d\n"
+                    "tx[11M]=%d\n"
+                    "rx[1M]=%d\nrx[2M]=%d\nrx[5.5M]=%d\nrx[11M]=%d\n",
+                    jiffies, sta->last_auth, sta->last_assoc, sta->last_rx,
+                    sta->last_tx,
+                    sta->rx_packets, sta->tx_packets, sta->rx_bytes,
+                    sta->tx_bytes, skb_queue_len(&sta->tx_buf),
+                    sta->last_rx_silence,
+                    sta->last_rx_signal, sta->last_rx_rate / 10,
+                    sta->last_rx_rate % 10 ? ".5" : "",
+                    sta->tx_rate, sta->tx_count[0], sta->tx_count[1],
+                    sta->tx_count[2], sta->tx_count[3],  sta->rx_count[0],
+                    sta->rx_count[1], sta->rx_count[2], sta->rx_count[3]);
+       if (sta->crypt && sta->crypt->ops && sta->crypt->ops->print_stats)
+               p = sta->crypt->ops->print_stats(p, sta->crypt->priv);
+#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
+       if (sta->ap) {
+               if (sta->u.ap.channel >= 0)
+                       p += sprintf(p, "channel=%d\n", sta->u.ap.channel);
+               p += sprintf(p, "ssid=");
+               for (i = 0; i < sta->u.ap.ssid_len; i++)
+                       p += sprintf(p, ((sta->u.ap.ssid[i] >= 32 &&
+                                         sta->u.ap.ssid[i] < 127) ?
+                                        "%c" : "<%02x>"),
+                                    sta->u.ap.ssid[i]);
+               p += sprintf(p, "\n");
+       }
+#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
+
+       return (p - page);
+}
+
+
+static void handle_add_proc_queue(void *data)
+{
+       struct ap_data *ap = (struct ap_data *) data;
+       struct sta_info *sta;
+       char name[20];
+       struct add_sta_proc_data *entry, *prev;
+
+       entry = ap->add_sta_proc_entries;
+       ap->add_sta_proc_entries = NULL;
+
+       while (entry) {
+               spin_lock_bh(&ap->sta_table_lock);
+               sta = ap_get_sta(ap, entry->addr);
+               if (sta)
+                       atomic_inc(&sta->users);
+               spin_unlock_bh(&ap->sta_table_lock);
+
+               if (sta) {
+                       sprintf(name, MACSTR, MAC2STR(sta->addr));
+                       sta->proc = create_proc_read_entry(
+                               name, 0, ap->proc,
+                               prism2_sta_proc_read, sta);
+
+                       atomic_dec(&sta->users);
+               }
+
+               prev = entry;
+               entry = entry->next;
+               kfree(prev);
+       }
+}
+
+
+static struct sta_info * ap_add_sta(struct ap_data *ap, u8 *addr)
+{
+       struct sta_info *sta;
+
+       sta = (struct sta_info *)
+               kmalloc(sizeof(struct sta_info), GFP_ATOMIC);
+       if (sta == NULL) {
+               PDEBUG(DEBUG_AP, "AP: kmalloc failed\n");
+               return NULL;
+       }
+
+       /* initialize STA info data */
+       memset(sta, 0, sizeof(struct sta_info));
+       sta->local = ap->local;
+       skb_queue_head_init(&sta->tx_buf);
+       memcpy(sta->addr, addr, ETH_ALEN);
+
+       atomic_inc(&sta->users);
+       spin_lock_bh(&ap->sta_table_lock);
+       list_add(&sta->list, &ap->sta_list);
+       ap->num_sta++;
+       ap_sta_hash_add(ap, sta);
+       spin_unlock_bh(&ap->sta_table_lock);
+
+       if (ap->proc) {
+               struct add_sta_proc_data *entry;
+               /* schedule a non-interrupt context process to add a procfs
+                * entry for the STA since procfs code use GFP_KERNEL */
+               entry = kmalloc(sizeof(*entry), GFP_ATOMIC);
+               if (entry) {
+                       memcpy(entry->addr, sta->addr, ETH_ALEN);
+                       entry->next = ap->add_sta_proc_entries;
+                       ap->add_sta_proc_entries = entry;
+                       schedule_work(&ap->add_sta_proc_queue);
+               } else
+                       printk(KERN_DEBUG "Failed to add STA proc data\n");
+       }
+
+#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
+       init_timer(&sta->timer);
+       sta->timer.expires = jiffies + ap->max_inactivity;
+       sta->timer.data = (unsigned long) sta;
+       sta->timer.function = ap_handle_timer;
+       if (!ap->local->hostapd)
+               add_timer(&sta->timer);
+#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
+
+       return sta;
+}
+
+
+static int ap_tx_rate_ok(int rateidx, struct sta_info *sta,
+                        local_info_t *local)
+{
+       if (rateidx > sta->tx_max_rate ||
+           !(sta->tx_supp_rates & (1 << rateidx)))
+               return 0;
+
+       if (local->tx_rate_control != 0 &&
+           !(local->tx_rate_control & (1 << rateidx)))
+               return 0;
+
+       return 1;
+}
+
+
+static void prism2_check_tx_rates(struct sta_info *sta)
+{
+       int i;
+
+       sta->tx_supp_rates = 0;
+       for (i = 0; i < sizeof(sta->supported_rates); i++) {
+               if ((sta->supported_rates[i] & 0x7f) == 2)
+                       sta->tx_supp_rates |= WLAN_RATE_1M;
+               if ((sta->supported_rates[i] & 0x7f) == 4)
+                       sta->tx_supp_rates |= WLAN_RATE_2M;
+               if ((sta->supported_rates[i] & 0x7f) == 11)
+                       sta->tx_supp_rates |= WLAN_RATE_5M5;
+               if ((sta->supported_rates[i] & 0x7f) == 22)
+                       sta->tx_supp_rates |= WLAN_RATE_11M;
+       }
+       sta->tx_max_rate = sta->tx_rate = sta->tx_rate_idx = 0;
+       if (sta->tx_supp_rates & WLAN_RATE_1M) {
+               sta->tx_max_rate = 0;
+               if (ap_tx_rate_ok(0, sta, sta->local)) {
+                       sta->tx_rate = 10;
+                       sta->tx_rate_idx = 0;
+               }
+       }
+       if (sta->tx_supp_rates & WLAN_RATE_2M) {
+               sta->tx_max_rate = 1;
+               if (ap_tx_rate_ok(1, sta, sta->local)) {
+                       sta->tx_rate = 20;
+                       sta->tx_rate_idx = 1;
+               }
+       }
+       if (sta->tx_supp_rates & WLAN_RATE_5M5) {
+               sta->tx_max_rate = 2;
+               if (ap_tx_rate_ok(2, sta, sta->local)) {
+                       sta->tx_rate = 55;
+                       sta->tx_rate_idx = 2;
+               }
+       }
+       if (sta->tx_supp_rates & WLAN_RATE_11M) {
+               sta->tx_max_rate = 3;
+               if (ap_tx_rate_ok(3, sta, sta->local)) {
+                       sta->tx_rate = 110;
+                       sta->tx_rate_idx = 3;
+               }
+       }
+}
+
+
+#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
+
+static void ap_crypt_init(struct ap_data *ap)
+{
+       ap->crypt = ieee80211_get_crypto_ops("WEP");
+
+       if (ap->crypt) {
+               if (ap->crypt->init) {
+                       ap->crypt_priv = ap->crypt->init(0);
+                       if (ap->crypt_priv == NULL)
+                               ap->crypt = NULL;
+                       else {
+                               u8 key[WEP_KEY_LEN];
+                               get_random_bytes(key, WEP_KEY_LEN);
+                               ap->crypt->set_key(key, WEP_KEY_LEN, NULL,
+                                                  ap->crypt_priv);
+                       }
+               }
+       }
+
+       if (ap->crypt == NULL) {
+               printk(KERN_WARNING "AP could not initialize WEP: load module "
+                      "ieee80211_crypt_wep.ko\n");
+       }
+}
+
+
+/* Generate challenge data for shared key authentication. IEEE 802.11 specifies
+ * that WEP algorithm is used for generating challange. This should be unique,
+ * but otherwise there is not really need for randomness etc. Initialize WEP
+ * with pseudo random key and then use increasing IV to get unique challenge
+ * streams.
+ *
+ * Called only as a scheduled task for pending AP frames.
+ */
+static char * ap_auth_make_challenge(struct ap_data *ap)
+{
+       char *tmpbuf;
+       struct sk_buff *skb;
+
+       if (ap->crypt == NULL) {
+               ap_crypt_init(ap);
+               if (ap->crypt == NULL)
+                       return NULL;
+       }
+
+       tmpbuf = (char *) kmalloc(WLAN_AUTH_CHALLENGE_LEN, GFP_ATOMIC);
+       if (tmpbuf == NULL) {
+               PDEBUG(DEBUG_AP, "AP: kmalloc failed for challenge\n");
+               return NULL;
+       }
+
+       skb = dev_alloc_skb(WLAN_AUTH_CHALLENGE_LEN +
+                           ap->crypt->extra_prefix_len +
+                           ap->crypt->extra_postfix_len);
+       if (skb == NULL) {
+               kfree(tmpbuf);
+               return NULL;
+       }
+
+       skb_reserve(skb, ap->crypt->extra_prefix_len);
+       memset(skb_put(skb, WLAN_AUTH_CHALLENGE_LEN), 0,
+              WLAN_AUTH_CHALLENGE_LEN);
+       if (ap->crypt->encrypt_mpdu(skb, 0, ap->crypt_priv)) {
+               dev_kfree_skb(skb);
+               kfree(tmpbuf);
+               return NULL;
+       }
+
+       memcpy(tmpbuf, skb->data + ap->crypt->extra_prefix_len,
+              WLAN_AUTH_CHALLENGE_LEN);
+       dev_kfree_skb(skb);
+
+       return tmpbuf;
+}
+
+
+/* Called only as a scheduled task for pending AP frames. */
+static void handle_authen(local_info_t *local, struct sk_buff *skb,
+                         struct hostap_80211_rx_status *rx_stats)
+{
+       struct net_device *dev = local->dev;
+       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
+       size_t hdrlen;
+       struct ap_data *ap = local->ap;
+       char body[8 + WLAN_AUTH_CHALLENGE_LEN], *challenge = NULL;
+       int len, olen;
+       u16 auth_alg, auth_transaction, status_code, *pos;
+       u16 resp = WLAN_STATUS_SUCCESS, fc;
+       struct sta_info *sta = NULL;
+       struct ieee80211_crypt_data *crypt;
+       char *txt = "";
+
+       len = skb->len - IEEE80211_MGMT_HDR_LEN;
+
+       fc = le16_to_cpu(hdr->frame_ctl);
+       hdrlen = hostap_80211_get_hdrlen(fc);
+
+       if (len < 6) {
+               PDEBUG(DEBUG_AP, "%s: handle_authen - too short payload "
+                      "(len=%d) from " MACSTR "\n", dev->name, len,
+                      MAC2STR(hdr->addr2));
+               return;
+       }
+
+       spin_lock_bh(&local->ap->sta_table_lock);
+       sta = ap_get_sta(local->ap, hdr->addr2);
+       if (sta)
+               atomic_inc(&sta->users);
+       spin_unlock_bh(&local->ap->sta_table_lock);
+
+       if (sta && sta->crypt)
+               crypt = sta->crypt;
+       else {
+               int idx = 0;
+               if (skb->len >= hdrlen + 3)
+                       idx = skb->data[hdrlen + 3] >> 6;
+               crypt = local->crypt[idx];
+       }
+
+       pos = (u16 *) (skb->data + IEEE80211_MGMT_HDR_LEN);
+       auth_alg = __le16_to_cpu(*pos);
+       pos++;
+       auth_transaction = __le16_to_cpu(*pos);
+       pos++;
+       status_code = __le16_to_cpu(*pos);
+       pos++;
+
+       if (memcmp(dev->dev_addr, hdr->addr2, ETH_ALEN) == 0 ||
+           ap_control_mac_deny(&ap->mac_restrictions, hdr->addr2)) {
+               txt = "authentication denied";
+               resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+               goto fail;
+       }
+
+       if (((local->auth_algs & PRISM2_AUTH_OPEN) &&
+            auth_alg == WLAN_AUTH_OPEN) ||
+           ((local->auth_algs & PRISM2_AUTH_SHARED_KEY) &&
+            crypt && auth_alg == WLAN_AUTH_SHARED_KEY)) {
+       } else {
+               txt = "unsupported algorithm";
+               resp = WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG;
+               goto fail;
+       }
+
+       if (len >= 8) {
+               u8 *u = (u8 *) pos;
+               if (*u == WLAN_EID_CHALLENGE) {
+                       if (*(u + 1) != WLAN_AUTH_CHALLENGE_LEN) {
+                               txt = "invalid challenge len";
+                               resp = WLAN_STATUS_CHALLENGE_FAIL;
+                               goto fail;
+                       }
+                       if (len - 8 < WLAN_AUTH_CHALLENGE_LEN) {
+                               txt = "challenge underflow";
+                               resp = WLAN_STATUS_CHALLENGE_FAIL;
+                               goto fail;
+                       }
+                       challenge = (char *) (u + 2);
+               }
+       }
+
+       if (sta && sta->ap) {
+               if (time_after(jiffies, sta->u.ap.last_beacon +
+                              (10 * sta->listen_interval * HZ) / 1024)) {
+                       PDEBUG(DEBUG_AP, "%s: no beacons received for a while,"
+                              " assuming AP " MACSTR " is now STA\n",
+                              dev->name, MAC2STR(sta->addr));
+                       sta->ap = 0;
+                       sta->flags = 0;
+                       sta->u.sta.challenge = NULL;
+               } else {
+                       txt = "AP trying to authenticate?";
+                       resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+                       goto fail;
+               }
+       }
+
+       if ((auth_alg == WLAN_AUTH_OPEN && auth_transaction == 1) ||
+           (auth_alg == WLAN_AUTH_SHARED_KEY &&
+            (auth_transaction == 1 ||
+             (auth_transaction == 3 && sta != NULL &&
+              sta->u.sta.challenge != NULL)))) {
+       } else {
+               txt = "unknown authentication transaction number";
+               resp = WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION;
+               goto fail;
+       }
+
+       if (sta == NULL) {
+               txt = "new STA";
+
+               if (local->ap->num_sta >= MAX_STA_COUNT) {
+                       /* FIX: might try to remove some old STAs first? */
+                       txt = "no more room for new STAs";
+                       resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+                       goto fail;
+               }
+
+               sta = ap_add_sta(local->ap, hdr->addr2);
+               if (sta == NULL) {
+                       txt = "ap_add_sta failed";
+                       resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+                       goto fail;
+               }
+       }
+
+       switch (auth_alg) {
+       case WLAN_AUTH_OPEN:
+               txt = "authOK";
+               /* IEEE 802.11 standard is not completely clear about
+                * whether STA is considered authenticated after
+                * authentication OK frame has been send or after it
+                * has been ACKed. In order to reduce interoperability
+                * issues, mark the STA authenticated before ACK. */
+               sta->flags |= WLAN_STA_AUTH;
+               break;
+
+       case WLAN_AUTH_SHARED_KEY:
+               if (auth_transaction == 1) {
+                       if (sta->u.sta.challenge == NULL) {
+                               sta->u.sta.challenge =
+                                       ap_auth_make_challenge(local->ap);
+                               if (sta->u.sta.challenge == NULL) {
+                                       resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+                                       goto fail;
+                               }
+                       }
+               } else {
+                       if (sta->u.sta.challenge == NULL ||
+                           challenge == NULL ||
+                           memcmp(sta->u.sta.challenge, challenge,
+                                  WLAN_AUTH_CHALLENGE_LEN) != 0 ||
+                           !(fc & WLAN_FC_ISWEP)) {
+                               txt = "challenge response incorrect";
+                               resp = WLAN_STATUS_CHALLENGE_FAIL;
+                               goto fail;
+                       }
+
+                       txt = "challenge OK - authOK";
+                       /* IEEE 802.11 standard is not completely clear about
+                        * whether STA is considered authenticated after
+                        * authentication OK frame has been send or after it
+                        * has been ACKed. In order to reduce interoperability
+                        * issues, mark the STA authenticated before ACK. */
+                       sta->flags |= WLAN_STA_AUTH;
+                       kfree(sta->u.sta.challenge);
+                       sta->u.sta.challenge = NULL;
+               }
+               break;
+       }
+
+ fail:
+       pos = (u16 *) body;
+       *pos = cpu_to_le16(auth_alg);
+       pos++;
+       *pos = cpu_to_le16(auth_transaction + 1);
+       pos++;
+       *pos = cpu_to_le16(resp); /* status_code */
+       pos++;
+       olen = 6;
+
+       if (resp == WLAN_STATUS_SUCCESS && sta != NULL &&
+           sta->u.sta.challenge != NULL &&
+           auth_alg == WLAN_AUTH_SHARED_KEY && auth_transaction == 1) {
+               u8 *tmp = (u8 *) pos;
+               *tmp++ = WLAN_EID_CHALLENGE;
+               *tmp++ = WLAN_AUTH_CHALLENGE_LEN;
+               pos++;
+               memcpy(pos, sta->u.sta.challenge, WLAN_AUTH_CHALLENGE_LEN);
+               olen += 2 + WLAN_AUTH_CHALLENGE_LEN;
+       }
+
+       prism2_send_mgmt(dev, IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_AUTH,
+                        body, olen, hdr->addr2, ap->tx_callback_auth);
+
+       if (sta) {
+               sta->last_rx = jiffies;
+               atomic_dec(&sta->users);
+       }
+
+       if (resp) {
+               PDEBUG(DEBUG_AP, "%s: " MACSTR " auth (alg=%d trans#=%d "
+                      "stat=%d len=%d fc=%04x) ==> %d (%s)\n",
+                      dev->name, MAC2STR(hdr->addr2), auth_alg,
+                      auth_transaction, status_code, len, fc, resp, txt);
+       }
+}
+
+
+/* Called only as a scheduled task for pending AP frames. */
+static void handle_assoc(local_info_t *local, struct sk_buff *skb,
+                        struct hostap_80211_rx_status *rx_stats, int reassoc)
+{
+       struct net_device *dev = local->dev;
+       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
+       char body[12], *p, *lpos;
+       int len, left;
+       u16 *pos;
+       u16 resp = WLAN_STATUS_SUCCESS;
+       struct sta_info *sta = NULL;
+       int send_deauth = 0;
+       char *txt = "";
+       u8 prev_ap[ETH_ALEN];
+
+       left = len = skb->len - IEEE80211_MGMT_HDR_LEN;
+
+       if (len < (reassoc ? 10 : 4)) {
+               PDEBUG(DEBUG_AP, "%s: handle_assoc - too short payload "
+                      "(len=%d, reassoc=%d) from " MACSTR "\n",
+                      dev->name, len, reassoc, MAC2STR(hdr->addr2));
+               return;
+       }
+
+       spin_lock_bh(&local->ap->sta_table_lock);
+       sta = ap_get_sta(local->ap, hdr->addr2);
+       if (sta == NULL || (sta->flags & WLAN_STA_AUTH) == 0) {
+               spin_unlock_bh(&local->ap->sta_table_lock);
+               txt = "trying to associate before authentication";
+               send_deauth = 1;
+               resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+               sta = NULL; /* do not decrement sta->users */
+               goto fail;
+       }
+       atomic_inc(&sta->users);
+       spin_unlock_bh(&local->ap->sta_table_lock);
+
+       pos = (u16 *) (skb->data + IEEE80211_MGMT_HDR_LEN);
+       sta->capability = __le16_to_cpu(*pos);
+       pos++; left -= 2;
+       sta->listen_interval = __le16_to_cpu(*pos);
+       pos++; left -= 2;
+
+       if (reassoc) {
+               memcpy(prev_ap, pos, ETH_ALEN);
+               pos++; pos++; pos++; left -= 6;
+       } else
+               memset(prev_ap, 0, ETH_ALEN);
+
+       if (left >= 2) {
+               unsigned int ileft;
+               unsigned char *u = (unsigned char *) pos;
+
+               if (*u == WLAN_EID_SSID) {
+                       u++; left--;
+                       ileft = *u;
+                       u++; left--;
+
+                       if (ileft > left || ileft > MAX_SSID_LEN) {
+                               txt = "SSID overflow";
+                               resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+                               goto fail;
+                       }
+
+                       if (ileft != strlen(local->essid) ||
+                           memcmp(local->essid, u, ileft) != 0) {
+                               txt = "not our SSID";
+                               resp = WLAN_STATUS_ASSOC_DENIED_UNSPEC;
+                               goto fail;
+                       }
+
+                       u += ileft;
+                       left -= ileft;
+               }
+
+               if (left >= 2 && *u == WLAN_EID_SUPP_RATES) {
+                       u++; left--;
+                       ileft = *u;
+                       u++; left--;
+
+                       if (ileft > left || ileft == 0 ||
+                           ileft > WLAN_SUPP_RATES_MAX) {
+                               txt = "SUPP_RATES len error";
+                               resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+                               goto fail;
+                       }
+
+                       memset(sta->supported_rates, 0,
+                              sizeof(sta->supported_rates));
+                       memcpy(sta->supported_rates, u, ileft);
+                       prism2_check_tx_rates(sta);
+
+                       u += ileft;
+                       left -= ileft;
+               }
+
+               if (left > 0) {
+                       PDEBUG(DEBUG_AP, "%s: assoc from " MACSTR " with extra"
+                              " data (%d bytes) [",
+                              dev->name, MAC2STR(hdr->addr2), left);
+                       while (left > 0) {
+                               PDEBUG2(DEBUG_AP, "<%02x>", *u);
+                               u++; left--;
+                       }
+                       PDEBUG2(DEBUG_AP, "]\n");
+               }
+       } else {
+               txt = "frame underflow";
+               resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+               goto fail;
+       }
+
+       /* get a unique AID */
+       if (sta->aid > 0)
+               txt = "OK, old AID";
+       else {
+               spin_lock_bh(&local->ap->sta_table_lock);
+               for (sta->aid = 1; sta->aid <= MAX_AID_TABLE_SIZE; sta->aid++)
+                       if (local->ap->sta_aid[sta->aid - 1] == NULL)
+                               break;
+               if (sta->aid > MAX_AID_TABLE_SIZE) {
+                       sta->aid = 0;
+                       spin_unlock_bh(&local->ap->sta_table_lock);
+                       resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
+                       txt = "no room for more AIDs";
+               } else {
+                       local->ap->sta_aid[sta->aid - 1] = sta;
+                       spin_unlock_bh(&local->ap->sta_table_lock);
+                       txt = "OK, new AID";
+               }
+       }
+
+ fail:
+       pos = (u16 *) body;
+
+       if (send_deauth) {
+               *pos = __constant_cpu_to_le16(
+                       WLAN_REASON_STA_REQ_ASSOC_WITHOUT_AUTH);
+               pos++;
+       } else {
+               /* FIX: CF-Pollable and CF-PollReq should be set to match the
+                * values in beacons/probe responses */
+               /* FIX: how about privacy and WEP? */
+               /* capability */
+               *pos = __constant_cpu_to_le16(WLAN_CAPABILITY_ESS);
+               pos++;
+
+               /* status_code */
+               *pos = __cpu_to_le16(resp);
+               pos++;
+
+               *pos = __cpu_to_le16((sta && sta->aid > 0 ? sta->aid : 0) |
+                                    BIT(14) | BIT(15)); /* AID */
+               pos++;
+
+               /* Supported rates (Information element) */
+               p = (char *) pos;
+               *p++ = WLAN_EID_SUPP_RATES;
+               lpos = p;
+               *p++ = 0; /* len */
+               if (local->tx_rate_control & WLAN_RATE_1M) {
+                       *p++ = local->basic_rates & WLAN_RATE_1M ? 0x82 : 0x02;
+                       (*lpos)++;
+               }
+               if (local->tx_rate_control & WLAN_RATE_2M) {
+                       *p++ = local->basic_rates & WLAN_RATE_2M ? 0x84 : 0x04;
+                       (*lpos)++;
+               }
+               if (local->tx_rate_control & WLAN_RATE_5M5) {
+                       *p++ = local->basic_rates & WLAN_RATE_5M5 ?
+                               0x8b : 0x0b;
+                       (*lpos)++;
+               }
+               if (local->tx_rate_control & WLAN_RATE_11M) {
+                       *p++ = local->basic_rates & WLAN_RATE_11M ?
+                               0x96 : 0x16;
+                       (*lpos)++;
+               }
+               pos = (u16 *) p;
+       }
+
+       prism2_send_mgmt(dev, IEEE80211_FTYPE_MGMT |
+                        (send_deauth ? IEEE80211_STYPE_DEAUTH :
+                         (reassoc ? IEEE80211_STYPE_REASSOC_RESP :
+                          IEEE80211_STYPE_ASSOC_RESP)),
+                        body, (u8 *) pos - (u8 *) body,
+                        hdr->addr2,
+                        send_deauth ? 0 : local->ap->tx_callback_assoc);
+
+       if (sta) {
+               if (resp == WLAN_STATUS_SUCCESS) {
+                       sta->last_rx = jiffies;
+                       /* STA will be marked associated from TX callback, if
+                        * AssocResp is ACKed */
+               }
+               atomic_dec(&sta->users);
+       }
+
+#if 0
+       PDEBUG(DEBUG_AP, "%s: " MACSTR " %sassoc (len=%d prev_ap=" MACSTR
+              ") => %d(%d) (%s)\n",
+              dev->name, MAC2STR(hdr->addr2), reassoc ? "re" : "", len,
+              MAC2STR(prev_ap), resp, send_deauth, txt);
+#endif
+}
+
+
+/* Called only as a scheduled task for pending AP frames. */
+static void handle_deauth(local_info_t *local, struct sk_buff *skb,
+                         struct hostap_80211_rx_status *rx_stats)
+{
+       struct net_device *dev = local->dev;
+       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
+       char *body = (char *) (skb->data + IEEE80211_MGMT_HDR_LEN);
+       int len;
+       u16 reason_code, *pos;
+       struct sta_info *sta = NULL;
+
+       len = skb->len - IEEE80211_MGMT_HDR_LEN;
+
+       if (len < 2) {
+               printk("handle_deauth - too short payload (len=%d)\n", len);
+               return;
+       }
+
+       pos = (u16 *) body;
+       reason_code = __le16_to_cpu(*pos);
+
+       PDEBUG(DEBUG_AP, "%s: deauthentication: " MACSTR " len=%d, "
+              "reason_code=%d\n", dev->name, MAC2STR(hdr->addr2), len,
+              reason_code);
+
+       spin_lock_bh(&local->ap->sta_table_lock);
+       sta = ap_get_sta(local->ap, hdr->addr2);
+       if (sta != NULL) {
+               if ((sta->flags & WLAN_STA_ASSOC) && !sta->ap)
+                       hostap_event_expired_sta(local->dev, sta);
+               sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC);
+       }
+       spin_unlock_bh(&local->ap->sta_table_lock);
+       if (sta == NULL) {
+               printk("%s: deauthentication from " MACSTR ", "
+              "reason_code=%d, but STA not authenticated\n", dev->name,
+                      MAC2STR(hdr->addr2), reason_code);
+       }
+}
+
+
+/* Called only as a scheduled task for pending AP frames. */
+static void handle_disassoc(local_info_t *local, struct sk_buff *skb,
+                           struct hostap_80211_rx_status *rx_stats)
+{
+       struct net_device *dev = local->dev;
+       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
+       char *body = skb->data + IEEE80211_MGMT_HDR_LEN;
+       int len;
+       u16 reason_code, *pos;
+       struct sta_info *sta = NULL;
+
+       len = skb->len - IEEE80211_MGMT_HDR_LEN;
+
+       if (len < 2) {
+               printk("handle_disassoc - too short payload (len=%d)\n", len);
+               return;
+       }
+
+       pos = (u16 *) body;
+       reason_code = __le16_to_cpu(*pos);
+
+       PDEBUG(DEBUG_AP, "%s: disassociation: " MACSTR " len=%d, "
+              "reason_code=%d\n", dev->name, MAC2STR(hdr->addr2), len,
+              reason_code);
+
+       spin_lock_bh(&local->ap->sta_table_lock);
+       sta = ap_get_sta(local->ap, hdr->addr2);
+       if (sta != NULL) {
+               if ((sta->flags & WLAN_STA_ASSOC) && !sta->ap)
+                       hostap_event_expired_sta(local->dev, sta);
+               sta->flags &= ~WLAN_STA_ASSOC;
+       }
+       spin_unlock_bh(&local->ap->sta_table_lock);
+       if (sta == NULL) {
+               printk("%s: disassociation from " MACSTR ", "
+                      "reason_code=%d, but STA not authenticated\n",
+                      dev->name, MAC2STR(hdr->addr2), reason_code);
+       }
+}
+
+
+/* Called only as a scheduled task for pending AP frames. */
+static void ap_handle_data_nullfunc(local_info_t *local,
+                                   struct ieee80211_hdr *hdr)
+{
+       struct net_device *dev = local->dev;
+
+       /* some STA f/w's seem to require control::ACK frame for
+        * data::nullfunc, but at least Prism2 station f/w version 0.8.0 does
+        * not send this..
+        * send control::ACK for the data::nullfunc */
+
+       printk(KERN_DEBUG "Sending control::ACK for data::nullfunc\n");
+       prism2_send_mgmt(dev, IEEE80211_FTYPE_CTL | IEEE80211_STYPE_ACK,
+                        NULL, 0, hdr->addr2, 0);
+}
+
+
+/* Called only as a scheduled task for pending AP frames. */
+static void ap_handle_dropped_data(local_info_t *local,
+                                  struct ieee80211_hdr *hdr)
+{
+       struct net_device *dev = local->dev;
+       struct sta_info *sta;
+       u16 reason;
+
+       spin_lock_bh(&local->ap->sta_table_lock);
+       sta = ap_get_sta(local->ap, hdr->addr2);
+       if (sta)
+               atomic_inc(&sta->users);
+       spin_unlock_bh(&local->ap->sta_table_lock);
+
+       if (sta != NULL && (sta->flags & WLAN_STA_ASSOC)) {
+               PDEBUG(DEBUG_AP, "ap_handle_dropped_data: STA is now okay?\n");
+               atomic_dec(&sta->users);
+               return;
+       }
+
+       reason = __constant_cpu_to_le16(
+               WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA);
+       prism2_send_mgmt(dev, IEEE80211_FTYPE_MGMT |
+                        ((sta == NULL || !(sta->flags & WLAN_STA_ASSOC)) ?
+                         IEEE80211_STYPE_DEAUTH : IEEE80211_STYPE_DISASSOC),
+                        (char *) &reason, sizeof(reason), hdr->addr2, 0);
+
+       if (sta)
+               atomic_dec(&sta->users);
+}
+
+#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
+
+
+/* Called only as a scheduled task for pending AP frames. */
+static void pspoll_send_buffered(local_info_t *local, struct sta_info *sta,
+                                struct sk_buff *skb)
+{
+       struct hostap_skb_tx_data *meta;
+
+       if (!(sta->flags & WLAN_STA_PS)) {
+               /* Station has moved to non-PS mode, so send all buffered
+                * frames using normal device queue. */
+               dev_queue_xmit(skb);
+               return;
+       }
+
+       /* add a flag for hostap_handle_sta_tx() to know that this skb should
+        * be passed through even though STA is using PS */
+       meta = (struct hostap_skb_tx_data *) skb->cb;
+       meta->flags |= HOSTAP_TX_FLAGS_BUFFERED_FRAME;
+       if (!skb_queue_empty(&sta->tx_buf)) {
+               /* indicate to STA that more frames follow */
+               meta->flags |= HOSTAP_TX_FLAGS_ADD_MOREDATA;
+       }
+       dev_queue_xmit(skb);
+}
+
+
+/* Called only as a scheduled task for pending AP frames. */
+static void handle_pspoll(local_info_t *local,
+                         struct ieee80211_hdr *hdr,
+                         struct hostap_80211_rx_status *rx_stats)
+{
+       struct net_device *dev = local->dev;
+       struct sta_info *sta;
+       u16 aid;
+       struct sk_buff *skb;
+
+       PDEBUG(DEBUG_PS2, "handle_pspoll: BSSID=" MACSTR ", TA=" MACSTR
+              " PWRMGT=%d\n",
+              MAC2STR(hdr->addr1), MAC2STR(hdr->addr2),
+              !!(le16_to_cpu(hdr->frame_ctl) & WLAN_FC_PWRMGT));
+
+       if (memcmp(hdr->addr1, dev->dev_addr, ETH_ALEN)) {
+               PDEBUG(DEBUG_AP, "handle_pspoll - addr1(BSSID)=" MACSTR
+                      " not own MAC\n", MAC2STR(hdr->addr1));
+               return;
+       }
+
+       aid = __le16_to_cpu(hdr->duration_id);
+       if ((aid & (BIT(15) | BIT(14))) != (BIT(15) | BIT(14))) {
+               PDEBUG(DEBUG_PS, "   PSPOLL and AID[15:14] not set\n");
+               return;
+       }
+       aid &= ~BIT(15) & ~BIT(14);
+       if (aid == 0 || aid > MAX_AID_TABLE_SIZE) {
+               PDEBUG(DEBUG_PS, "   invalid aid=%d\n", aid);
+               return;
+       }
+       PDEBUG(DEBUG_PS2, "   aid=%d\n", aid);
+
+       spin_lock_bh(&local->ap->sta_table_lock);
+       sta = ap_get_sta(local->ap, hdr->addr2);
+       if (sta)
+               atomic_inc(&sta->users);
+       spin_unlock_bh(&local->ap->sta_table_lock);
+
+       if (sta == NULL) {
+               PDEBUG(DEBUG_PS, "   STA not found\n");
+               return;
+       }
+       if (sta->aid != aid) {
+               PDEBUG(DEBUG_PS, "   received aid=%i does not match with "
+                      "assoc.aid=%d\n", aid, sta->aid);
+               return;
+       }
+
+       /* FIX: todo:
+        * - add timeout for buffering (clear aid in TIM vector if buffer timed
+        *   out (expiry time must be longer than ListenInterval for
+        *   the corresponding STA; "8802-11: 11.2.1.9 AP aging function"
+        * - what to do, if buffered, pspolled, and sent frame is not ACKed by
+        *   sta; store buffer for later use and leave TIM aid bit set? use
+        *   TX event to check whether frame was ACKed?
+        */
+
+       while ((skb = skb_dequeue(&sta->tx_buf)) != NULL) {
+               /* send buffered frame .. */
+               PDEBUG(DEBUG_PS2, "Sending buffered frame to STA after PS POLL"
+                      " (buffer_count=%d)\n", skb_queue_len(&sta->tx_buf));
+
+               pspoll_send_buffered(local, sta, skb);
+
+               if (sta->flags & WLAN_STA_PS) {
+                       /* send only one buffered packet per PS Poll */
+                       /* FIX: should ignore further PS Polls until the
+                        * buffered packet that was just sent is acknowledged
+                        * (Tx or TxExc event) */
+                       break;
+               }
+       }
+
+       if (skb_queue_empty(&sta->tx_buf)) {
+               /* try to clear aid from TIM */
+               if (!(sta->flags & WLAN_STA_TIM))
+                       PDEBUG(DEBUG_PS2,  "Re-unsetting TIM for aid %d\n",
+                              aid);
+               hostap_set_tim(local, aid, 0);
+               sta->flags &= ~WLAN_STA_TIM;
+       }
+
+       atomic_dec(&sta->users);
+}
+
+
+#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
+
+static void handle_wds_oper_queue(void *data)
+{
+       local_info_t *local = data;
+       struct wds_oper_data *entry, *prev;
+
+       spin_lock_bh(&local->lock);
+       entry = local->ap->wds_oper_entries;
+       local->ap->wds_oper_entries = NULL;
+       spin_unlock_bh(&local->lock);
+
+       while (entry) {
+               PDEBUG(DEBUG_AP, "%s: %s automatic WDS connection "
+                      "to AP " MACSTR "\n",
+                      local->dev->name,
+                      entry->type == WDS_ADD ? "adding" : "removing",
+                      MAC2STR(entry->addr));
+               if (entry->type == WDS_ADD)
+                       prism2_wds_add(local, entry->addr, 0);
+               else if (entry->type == WDS_DEL)
+                       prism2_wds_del(local, entry->addr, 0, 1);
+
+               prev = entry;
+               entry = entry->next;
+               kfree(prev);
+       }
+}
+
+
+/* Called only as a scheduled task for pending AP frames. */
+static void handle_beacon(local_info_t *local, struct sk_buff *skb,
+                         struct hostap_80211_rx_status *rx_stats)
+{
+       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
+       char *body = skb->data + IEEE80211_MGMT_HDR_LEN;
+       int len, left;
+       u16 *pos, beacon_int, capability;
+       char *ssid = NULL;
+       unsigned char *supp_rates = NULL;
+       int ssid_len = 0, supp_rates_len = 0;
+       struct sta_info *sta = NULL;
+       int new_sta = 0, channel = -1;
+
+       len = skb->len - IEEE80211_MGMT_HDR_LEN;
+
+       if (len < 8 + 2 + 2) {
+               printk(KERN_DEBUG "handle_beacon - too short payload "
+                      "(len=%d)\n", len);
+               return;
+       }
+
+       pos = (u16 *) body;
+       left = len;
+
+       /* Timestamp (8 octets) */
+       pos += 4; left -= 8;
+       /* Beacon interval (2 octets) */
+       beacon_int = __le16_to_cpu(*pos);
+       pos++; left -= 2;
+       /* Capability information (2 octets) */
+       capability = __le16_to_cpu(*pos);
+       pos++; left -= 2;
+
+       if (local->ap->ap_policy != AP_OTHER_AP_EVEN_IBSS &&
+           capability & WLAN_CAPABILITY_IBSS)
+               return;
+
+       if (left >= 2) {
+               unsigned int ileft;
+               unsigned char *u = (unsigned char *) pos;
+
+               if (*u == WLAN_EID_SSID) {
+                       u++; left--;
+                       ileft = *u;
+                       u++; left--;
+
+                       if (ileft > left || ileft > MAX_SSID_LEN) {
+                               PDEBUG(DEBUG_AP, "SSID: overflow\n");
+                               return;
+                       }
+
+                       if (local->ap->ap_policy == AP_OTHER_AP_SAME_SSID &&
+                           (ileft != strlen(local->essid) ||
+                            memcmp(local->essid, u, ileft) != 0)) {
+                               /* not our SSID */
+                               return;
+                       }
+
+                       ssid = u;
+                       ssid_len = ileft;
+
+                       u += ileft;
+                       left -= ileft;
+               }
+
+               if (*u == WLAN_EID_SUPP_RATES) {
+                       u++; left--;
+                       ileft = *u;
+                       u++; left--;
+
+                       if (ileft > left || ileft == 0 || ileft > 8) {
+                               PDEBUG(DEBUG_AP, " - SUPP_RATES len error\n");
+                               return;
+                       }
+
+                       supp_rates = u;
+                       supp_rates_len = ileft;
+
+                       u += ileft;
+                       left -= ileft;
+               }
+
+               if (*u == WLAN_EID_DS_PARAMS) {
+                       u++; left--;
+                       ileft = *u;
+                       u++; left--;
+
+                       if (ileft > left || ileft != 1) {
+                               PDEBUG(DEBUG_AP, " - DS_PARAMS len error\n");
+                               return;
+                       }
+
+                       channel = *u;
+
+                       u += ileft;
+                       left -= ileft;
+               }
+       }
+
+       spin_lock_bh(&local->ap->sta_table_lock);
+       sta = ap_get_sta(local->ap, hdr->addr2);
+       if (sta != NULL)
+               atomic_inc(&sta->users);
+       spin_unlock_bh(&local->ap->sta_table_lock);
+
+       if (sta == NULL) {
+               /* add new AP */
+               new_sta = 1;
+               sta = ap_add_sta(local->ap, hdr->addr2);
+               if (sta == NULL) {
+                       printk(KERN_INFO "prism2: kmalloc failed for AP "
+                              "data structure\n");
+                       return;
+               }
+               hostap_event_new_sta(local->dev, sta);
+
+               /* mark APs authentication and associated for pseudo ad-hoc
+                * style communication */
+               sta->flags = WLAN_STA_AUTH | WLAN_STA_ASSOC;
+
+               if (local->ap->autom_ap_wds) {
+                       hostap_wds_link_oper(local, sta->addr, WDS_ADD);
+               }
+       }
+
+       sta->ap = 1;
+       if (ssid) {
+               sta->u.ap.ssid_len = ssid_len;
+               memcpy(sta->u.ap.ssid, ssid, ssid_len);
+               sta->u.ap.ssid[ssid_len] = '\0';
+       } else {
+               sta->u.ap.ssid_len = 0;
+               sta->u.ap.ssid[0] = '\0';
+       }
+       sta->u.ap.channel = channel;
+       sta->rx_packets++;
+       sta->rx_bytes += len;
+       sta->u.ap.last_beacon = sta->last_rx = jiffies;
+       sta->capability = capability;
+       sta->listen_interval = beacon_int;
+
+       atomic_dec(&sta->users);
+
+       if (new_sta) {
+               memset(sta->supported_rates, 0, sizeof(sta->supported_rates));
+               memcpy(sta->supported_rates, supp_rates, supp_rates_len);
+               prism2_check_tx_rates(sta);
+       }
+}
+
+#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
+
+
+/* Called only as a tasklet. */
+static void handle_ap_item(local_info_t *local, struct sk_buff *skb,
+                          struct hostap_80211_rx_status *rx_stats)
+{
+#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
+       struct net_device *dev = local->dev;
+#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
+       u16 fc, type, stype;
+       struct ieee80211_hdr *hdr;
+
+       /* FIX: should give skb->len to handler functions and check that the
+        * buffer is long enough */
+       hdr = (struct ieee80211_hdr *) skb->data;
+       fc = le16_to_cpu(hdr->frame_ctl);
+       type = WLAN_FC_GET_TYPE(fc);
+       stype = WLAN_FC_GET_STYPE(fc);
+
+#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
+       if (!local->hostapd && type == IEEE80211_FTYPE_DATA) {
+               PDEBUG(DEBUG_AP, "handle_ap_item - data frame\n");
+
+               if (!(fc & WLAN_FC_TODS) || (fc & WLAN_FC_FROMDS)) {
+                       if (stype == IEEE80211_STYPE_NULLFUNC) {
+                               /* no ToDS nullfunc seems to be used to check
+                                * AP association; so send reject message to
+                                * speed up re-association */
+                               ap_handle_dropped_data(local, hdr);
+                               goto done;
+                       }
+                       PDEBUG(DEBUG_AP, "   not ToDS frame (fc=0x%04x)\n",
+                              fc);
+                       goto done;
+               }
+
+               if (memcmp(hdr->addr1, dev->dev_addr, ETH_ALEN)) {
+                       PDEBUG(DEBUG_AP, "handle_ap_item - addr1(BSSID)="
+                              MACSTR " not own MAC\n",
+                              MAC2STR(hdr->addr1));
+                       goto done;
+               }
+
+               if (local->ap->nullfunc_ack &&
+                   stype == IEEE80211_STYPE_NULLFUNC)
+                       ap_handle_data_nullfunc(local, hdr);
+               else
+                       ap_handle_dropped_data(local, hdr);
+               goto done;
+       }
+
+       if (type == IEEE80211_FTYPE_MGMT && stype == IEEE80211_STYPE_BEACON) {
+               handle_beacon(local, skb, rx_stats);
+               goto done;
+       }
+#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
+
+       if (type == IEEE80211_FTYPE_CTL && stype == IEEE80211_STYPE_PSPOLL) {
+               handle_pspoll(local, hdr, rx_stats);
+               goto done;
+       }
+
+       if (local->hostapd) {
+               PDEBUG(DEBUG_AP, "Unknown frame in AP queue: type=0x%02x "
+                      "subtype=0x%02x\n", type, stype);
+               goto done;
+       }
+
+#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
+       if (type != IEEE80211_FTYPE_MGMT) {
+               PDEBUG(DEBUG_AP, "handle_ap_item - not a management frame?\n");
+               goto done;
+       }
+
+       if (memcmp(hdr->addr1, dev->dev_addr, ETH_ALEN)) {
+               PDEBUG(DEBUG_AP, "handle_ap_item - addr1(DA)=" MACSTR
+                      " not own MAC\n", MAC2STR(hdr->addr1));
+               goto done;
+       }
+
+       if (memcmp(hdr->addr3, dev->dev_addr, ETH_ALEN)) {
+               PDEBUG(DEBUG_AP, "handle_ap_item - addr3(BSSID)=" MACSTR
+                      " not own MAC\n", MAC2STR(hdr->addr3));
+               goto done;
+       }
+
+       switch (stype) {
+       case IEEE80211_STYPE_ASSOC_REQ:
+               handle_assoc(local, skb, rx_stats, 0);
+               break;
+       case IEEE80211_STYPE_ASSOC_RESP:
+               PDEBUG(DEBUG_AP, "==> ASSOC RESP (ignored)\n");
+               break;
+       case IEEE80211_STYPE_REASSOC_REQ:
+               handle_assoc(local, skb, rx_stats, 1);
+               break;
+       case IEEE80211_STYPE_REASSOC_RESP:
+               PDEBUG(DEBUG_AP, "==> REASSOC RESP (ignored)\n");
+               break;
+       case IEEE80211_STYPE_ATIM:
+               PDEBUG(DEBUG_AP, "==> ATIM (ignored)\n");
+               break;
+       case IEEE80211_STYPE_DISASSOC:
+               handle_disassoc(local, skb, rx_stats);
+               break;
+       case IEEE80211_STYPE_AUTH:
+               handle_authen(local, skb, rx_stats);
+               break;
+       case IEEE80211_STYPE_DEAUTH:
+               handle_deauth(local, skb, rx_stats);
+               break;
+       default:
+               PDEBUG(DEBUG_AP, "Unknown mgmt frame subtype 0x%02x\n",
+                      stype >> 4);
+               break;
+       }
+#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
+
+ done:
+       dev_kfree_skb(skb);
+}
+
+
+/* Called only as a tasklet (software IRQ) */
+void hostap_rx(struct net_device *dev, struct sk_buff *skb,
+              struct hostap_80211_rx_status *rx_stats)
+{
+       struct hostap_interface *iface;
+       local_info_t *local;
+       u16 fc;
+       struct ieee80211_hdr *hdr;
+
+       iface = netdev_priv(dev);
+       local = iface->local;
+
+       if (skb->len < 16)
+               goto drop;
+
+       local->stats.rx_packets++;
+
+       hdr = (struct ieee80211_hdr *) skb->data;
+       fc = le16_to_cpu(hdr->frame_ctl);
+
+       if (local->ap->ap_policy == AP_OTHER_AP_SKIP_ALL &&
+           WLAN_FC_GET_TYPE(fc) == IEEE80211_FTYPE_MGMT &&
+           WLAN_FC_GET_STYPE(fc) == IEEE80211_STYPE_BEACON)
+               goto drop;
+
+       skb->protocol = __constant_htons(ETH_P_HOSTAP);
+       handle_ap_item(local, skb, rx_stats);
+       return;
+
+ drop:
+       dev_kfree_skb(skb);
+}
+
+
+/* Called only as a tasklet (software IRQ) */
+static void schedule_packet_send(local_info_t *local, struct sta_info *sta)
+{
+       struct sk_buff *skb;
+       struct ieee80211_hdr *hdr;
+       struct hostap_80211_rx_status rx_stats;
+
+       if (skb_queue_empty(&sta->tx_buf))
+               return;
+
+       skb = dev_alloc_skb(16);
+       if (skb == NULL) {
+               printk(KERN_DEBUG "%s: schedule_packet_send: skb alloc "
+                      "failed\n", local->dev->name);
+               return;
+       }
+
+       hdr = (struct ieee80211_hdr *) skb_put(skb, 16);
+
+       /* Generate a fake pspoll frame to start packet delivery */
+       hdr->frame_ctl = __constant_cpu_to_le16(
+               IEEE80211_FTYPE_CTL | IEEE80211_STYPE_PSPOLL);
+       memcpy(hdr->addr1, local->dev->dev_addr, ETH_ALEN);
+       memcpy(hdr->addr2, sta->addr, ETH_ALEN);
+       hdr->duration_id = cpu_to_le16(sta->aid | BIT(15) | BIT(14));
+
+       PDEBUG(DEBUG_PS2, "%s: Scheduling buffered packet delivery for "
+              "STA " MACSTR "\n", local->dev->name, MAC2STR(sta->addr));
+
+       skb->dev = local->dev;
+
+       memset(&rx_stats, 0, sizeof(rx_stats));
+       hostap_rx(local->dev, skb, &rx_stats);
+}
+
+
+static int prism2_ap_get_sta_qual(local_info_t *local, struct sockaddr addr[],
+                                 struct iw_quality qual[], int buf_size,
+                                 int aplist)
+{
+       struct ap_data *ap = local->ap;
+       struct list_head *ptr;
+       int count = 0;
+
+       spin_lock_bh(&ap->sta_table_lock);
+
+       for (ptr = ap->sta_list.next; ptr != NULL && ptr != &ap->sta_list;
+            ptr = ptr->next) {
+               struct sta_info *sta = (struct sta_info *) ptr;
+
+               if (aplist && !sta->ap)
+                       continue;
+               addr[count].sa_family = ARPHRD_ETHER;
+               memcpy(addr[count].sa_data, sta->addr, ETH_ALEN);
+               if (sta->last_rx_silence == 0)
+                       qual[count].qual = sta->last_rx_signal < 27 ?
+                               0 : (sta->last_rx_signal - 27) * 92 / 127;
+               else
+                       qual[count].qual = sta->last_rx_signal -
+                               sta->last_rx_silence - 35;
+               qual[count].level = HFA384X_LEVEL_TO_dBm(sta->last_rx_signal);
+               qual[count].noise = HFA384X_LEVEL_TO_dBm(sta->last_rx_silence);
+               qual[count].updated = sta->last_rx_updated;
+
+               sta->last_rx_updated = 0;
+
+               count++;
+               if (count >= buf_size)
+                       break;
+       }
+       spin_unlock_bh(&ap->sta_table_lock);
+
+       return count;
+}
+
+
+/* Translate our list of Access Points & Stations to a card independant
+ * format that the Wireless Tools will understand - Jean II */
+static int prism2_ap_translate_scan(struct net_device *dev, char *buffer)
+{
+       struct hostap_interface *iface;
+       local_info_t *local;
+       struct ap_data *ap;
+       struct list_head *ptr;
+       struct iw_event iwe;
+       char *current_ev = buffer;
+       char *end_buf = buffer + IW_SCAN_MAX_DATA;
+#if !defined(PRISM2_NO_KERNEL_IEEE80211_MGMT)
+       char buf[64];
+#endif
+
+       iface = netdev_priv(dev);
+       local = iface->local;
+       ap = local->ap;
+
+       spin_lock_bh(&ap->sta_table_lock);
+
+       for (ptr = ap->sta_list.next; ptr != NULL && ptr != &ap->sta_list;
+            ptr = ptr->next) {
+               struct sta_info *sta = (struct sta_info *) ptr;
+
+               /* First entry *MUST* be the AP MAC address */
+               memset(&iwe, 0, sizeof(iwe));
+               iwe.cmd = SIOCGIWAP;
+               iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
+               memcpy(iwe.u.ap_addr.sa_data, sta->addr, ETH_ALEN);
+               iwe.len = IW_EV_ADDR_LEN;
+               current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe,
+                                                 IW_EV_ADDR_LEN);
+
+               /* Use the mode to indicate if it's a station or
+                * an Access Point */
+               memset(&iwe, 0, sizeof(iwe));
+               iwe.cmd = SIOCGIWMODE;
+               if (sta->ap)
+                       iwe.u.mode = IW_MODE_MASTER;
+               else
+                       iwe.u.mode = IW_MODE_INFRA;
+               iwe.len = IW_EV_UINT_LEN;
+               current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe,
+                                                 IW_EV_UINT_LEN);
+
+               /* Some quality */
+               memset(&iwe, 0, sizeof(iwe));
+               iwe.cmd = IWEVQUAL;
+               if (sta->last_rx_silence == 0)
+                       iwe.u.qual.qual = sta->last_rx_signal < 27 ?
+                               0 : (sta->last_rx_signal - 27) * 92 / 127;
+               else
+                       iwe.u.qual.qual = sta->last_rx_signal -
+                               sta->last_rx_silence - 35;
+               iwe.u.qual.level = HFA384X_LEVEL_TO_dBm(sta->last_rx_signal);
+               iwe.u.qual.noise = HFA384X_LEVEL_TO_dBm(sta->last_rx_silence);
+               iwe.u.qual.updated = sta->last_rx_updated;
+               iwe.len = IW_EV_QUAL_LEN;
+               current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe,
+                                                 IW_EV_QUAL_LEN);
+
+#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
+               if (sta->ap) {
+                       memset(&iwe, 0, sizeof(iwe));
+                       iwe.cmd = SIOCGIWESSID;
+                       iwe.u.data.length = sta->u.ap.ssid_len;
+                       iwe.u.data.flags = 1;
+                       current_ev = iwe_stream_add_point(current_ev, end_buf,
+                                                         &iwe,
+                                                         sta->u.ap.ssid);
+
+                       memset(&iwe, 0, sizeof(iwe));
+                       iwe.cmd = SIOCGIWENCODE;
+                       if (sta->capability & WLAN_CAPABILITY_PRIVACY)
+                               iwe.u.data.flags =
+                                       IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
+                       else
+                               iwe.u.data.flags = IW_ENCODE_DISABLED;
+                       current_ev = iwe_stream_add_point(current_ev, end_buf,
+                                                         &iwe,
+                                                         sta->u.ap.ssid
+                                                         /* 0 byte memcpy */);
+
+                       if (sta->u.ap.channel > 0 &&
+                           sta->u.ap.channel <= FREQ_COUNT) {
+                               memset(&iwe, 0, sizeof(iwe));
+                               iwe.cmd = SIOCGIWFREQ;
+                               iwe.u.freq.m = freq_list[sta->u.ap.channel - 1]
+                                       * 100000;
+                               iwe.u.freq.e = 1;
+                               current_ev = iwe_stream_add_event(
+                                       current_ev, end_buf, &iwe,
+                                       IW_EV_FREQ_LEN);
+                       }
+
+                       memset(&iwe, 0, sizeof(iwe));
+                       iwe.cmd = IWEVCUSTOM;
+                       sprintf(buf, "beacon_interval=%d",
+                               sta->listen_interval);
+                       iwe.u.data.length = strlen(buf);
+                       current_ev = iwe_stream_add_point(current_ev, end_buf,
+                                                         &iwe, buf);
+               }
+#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
+
+               sta->last_rx_updated = 0;
+
+               /* To be continued, we should make good use of IWEVCUSTOM */
+       }
+
+       spin_unlock_bh(&ap->sta_table_lock);
+
+       return current_ev - buffer;
+}
+
+
+static int prism2_hostapd_add_sta(struct ap_data *ap,
+                                 struct prism2_hostapd_param *param)
+{
+       struct sta_info *sta;
+
+       spin_lock_bh(&ap->sta_table_lock);
+       sta = ap_get_sta(ap, param->sta_addr);
+       if (sta)
+               atomic_inc(&sta->users);
+       spin_unlock_bh(&ap->sta_table_lock);
+
+       if (sta == NULL) {
+               sta = ap_add_sta(ap, param->sta_addr);
+               if (sta == NULL)
+                       return -1;
+       }
+
+       if (!(sta->flags & WLAN_STA_ASSOC) && !sta->ap && sta->local)
+               hostap_event_new_sta(sta->local->dev, sta);
+
+       sta->flags |= WLAN_STA_AUTH | WLAN_STA_ASSOC;
+       sta->last_rx = jiffies;
+       sta->aid = param->u.add_sta.aid;
+       sta->capability = param->u.add_sta.capability;
+       sta->tx_supp_rates = param->u.add_sta.tx_supp_rates;
+       if (sta->tx_supp_rates & WLAN_RATE_1M)
+               sta->supported_rates[0] = 2;
+       if (sta->tx_supp_rates & WLAN_RATE_2M)
+               sta->supported_rates[1] = 4;
+       if (sta->tx_supp_rates & WLAN_RATE_5M5)
+               sta->supported_rates[2] = 11;
+       if (sta->tx_supp_rates & WLAN_RATE_11M)
+               sta->supported_rates[3] = 22;
+       prism2_check_tx_rates(sta);
+       atomic_dec(&sta->users);
+       return 0;
+}
+
+
+static int prism2_hostapd_remove_sta(struct ap_data *ap,
+                                    struct prism2_hostapd_param *param)
+{
+       struct sta_info *sta;
+
+       spin_lock_bh(&ap->sta_table_lock);
+       sta = ap_get_sta(ap, param->sta_addr);
+       if (sta) {
+               ap_sta_hash_del(ap, sta);
+               list_del(&sta->list);
+       }
+       spin_unlock_bh(&ap->sta_table_lock);
+
+       if (!sta)
+               return -ENOENT;
+
+       if ((sta->flags & WLAN_STA_ASSOC) && !sta->ap && sta->local)
+               hostap_event_expired_sta(sta->local->dev, sta);
+       ap_free_sta(ap, sta);
+
+       return 0;
+}
+
+
+static int prism2_hostapd_get_info_sta(struct ap_data *ap,
+                                      struct prism2_hostapd_param *param)
+{
+       struct sta_info *sta;
+
+       spin_lock_bh(&ap->sta_table_lock);
+       sta = ap_get_sta(ap, param->sta_addr);
+       if (sta)
+               atomic_inc(&sta->users);
+       spin_unlock_bh(&ap->sta_table_lock);
+
+       if (!sta)
+               return -ENOENT;
+
+       param->u.get_info_sta.inactive_sec = (jiffies - sta->last_rx) / HZ;
+
+       atomic_dec(&sta->users);
+
+       return 1;
+}
+
+
+static int prism2_hostapd_set_flags_sta(struct ap_data *ap,
+                                       struct prism2_hostapd_param *param)
+{
+       struct sta_info *sta;
+
+       spin_lock_bh(&ap->sta_table_lock);
+       sta = ap_get_sta(ap, param->sta_addr);
+       if (sta) {
+               sta->flags |= param->u.set_flags_sta.flags_or;
+               sta->flags &= param->u.set_flags_sta.flags_and;
+       }
+       spin_unlock_bh(&ap->sta_table_lock);
+
+       if (!sta)
+               return -ENOENT;
+
+       return 0;
+}
+
+
+static int prism2_hostapd_sta_clear_stats(struct ap_data *ap,
+                                         struct prism2_hostapd_param *param)
+{
+       struct sta_info *sta;
+       int rate;
+
+       spin_lock_bh(&ap->sta_table_lock);
+       sta = ap_get_sta(ap, param->sta_addr);
+       if (sta) {
+               sta->rx_packets = sta->tx_packets = 0;
+               sta->rx_bytes = sta->tx_bytes = 0;
+               for (rate = 0; rate < WLAN_RATE_COUNT; rate++) {
+                       sta->tx_count[rate] = 0;
+                       sta->rx_count[rate] = 0;
+               }
+       }
+       spin_unlock_bh(&ap->sta_table_lock);
+
+       if (!sta)
+               return -ENOENT;
+
+       return 0;
+}
+
+
+static int prism2_hostapd(struct ap_data *ap,
+                         struct prism2_hostapd_param *param)
+{
+       switch (param->cmd) {
+       case PRISM2_HOSTAPD_FLUSH:
+               ap_control_kickall(ap);
+               return 0;
+       case PRISM2_HOSTAPD_ADD_STA:
+               return prism2_hostapd_add_sta(ap, param);
+       case PRISM2_HOSTAPD_REMOVE_STA:
+               return prism2_hostapd_remove_sta(ap, param);
+       case PRISM2_HOSTAPD_GET_INFO_STA:
+               return prism2_hostapd_get_info_sta(ap, param);
+       case PRISM2_HOSTAPD_SET_FLAGS_STA:
+               return prism2_hostapd_set_flags_sta(ap, param);
+       case PRISM2_HOSTAPD_STA_CLEAR_STATS:
+               return prism2_hostapd_sta_clear_stats(ap, param);
+       default:
+               printk(KERN_WARNING "prism2_hostapd: unknown cmd=%d\n",
+                      param->cmd);
+               return -EOPNOTSUPP;
+       }
+}
+
+
+/* Update station info for host-based TX rate control and return current
+ * TX rate */
+static int ap_update_sta_tx_rate(struct sta_info *sta, struct net_device *dev)
+{
+       int ret = sta->tx_rate;
+       struct hostap_interface *iface;
+       local_info_t *local;
+
+       iface = netdev_priv(dev);
+       local = iface->local;
+
+       sta->tx_count[sta->tx_rate_idx]++;
+       sta->tx_since_last_failure++;
+       sta->tx_consecutive_exc = 0;
+       if (sta->tx_since_last_failure >= WLAN_RATE_UPDATE_COUNT &&
+           sta->tx_rate_idx < sta->tx_max_rate) {
+               /* use next higher rate */
+               int old_rate, new_rate;
+               old_rate = new_rate = sta->tx_rate_idx;
+               while (new_rate < sta->tx_max_rate) {
+                       new_rate++;
+                       if (ap_tx_rate_ok(new_rate, sta, local)) {
+                               sta->tx_rate_idx = new_rate;
+                               break;
+                       }
+               }
+               if (old_rate != sta->tx_rate_idx) {
+                       switch (sta->tx_rate_idx) {
+                       case 0: sta->tx_rate = 10; break;
+                       case 1: sta->tx_rate = 20; break;
+                       case 2: sta->tx_rate = 55; break;
+                       case 3: sta->tx_rate = 110; break;
+                       default: sta->tx_rate = 0; break;
+                       }
+                       PDEBUG(DEBUG_AP, "%s: STA " MACSTR " TX rate raised to"
+                              " %d\n", dev->name, MAC2STR(sta->addr),
+                              sta->tx_rate);
+               }
+               sta->tx_since_last_failure = 0;
+       }
+
+       return ret;
+}
+
+
+/* Called only from software IRQ. Called for each TX frame prior possible
+ * encryption and transmit. */
+ap_tx_ret hostap_handle_sta_tx(local_info_t *local, struct hostap_tx_data *tx)
+{
+       struct sta_info *sta = NULL;
+       struct sk_buff *skb = tx->skb;
+       int set_tim, ret;
+       struct ieee80211_hdr *hdr;
+       struct hostap_skb_tx_data *meta;
+
+       meta = (struct hostap_skb_tx_data *) skb->cb;
+       ret = AP_TX_CONTINUE;
+       if (local->ap == NULL || skb->len < 10 ||
+           meta->iface->type == HOSTAP_INTERFACE_STA)
+               goto out;
+
+       hdr = (struct ieee80211_hdr *) skb->data;
+
+       if (hdr->addr1[0] & 0x01) {
+               /* broadcast/multicast frame - no AP related processing */
+               goto out;
+       }
+
+       /* unicast packet - check whether destination STA is associated */
+       spin_lock(&local->ap->sta_table_lock);
+       sta = ap_get_sta(local->ap, hdr->addr1);
+       if (sta)
+               atomic_inc(&sta->users);
+       spin_unlock(&local->ap->sta_table_lock);
+
+       if (local->iw_mode == IW_MODE_MASTER && sta == NULL &&
+           !(meta->flags & HOSTAP_TX_FLAGS_WDS) &&
+           meta->iface->type != HOSTAP_INTERFACE_MASTER &&
+           meta->iface->type != HOSTAP_INTERFACE_AP) {
+#if 0
+               /* This can happen, e.g., when wlan0 is added to a bridge and
+                * bridging code does not know which port is the correct target
+                * for a unicast frame. In this case, the packet is send to all
+                * ports of the bridge. Since this is a valid scenario, do not
+                * print out any errors here. */
+               if (net_ratelimit()) {
+                       printk(KERN_DEBUG "AP: drop packet to non-associated "
+                              "STA " MACSTR "\n", MAC2STR(hdr->addr1));
+               }
+#endif
+               local->ap->tx_drop_nonassoc++;
+               ret = AP_TX_DROP;
+               goto out;
+       }
+
+       if (sta == NULL)
+               goto out;
+
+       if (!(sta->flags & WLAN_STA_AUTHORIZED))
+               ret = AP_TX_CONTINUE_NOT_AUTHORIZED;
+
+       /* Set tx_rate if using host-based TX rate control */
+       if (!local->fw_tx_rate_control)
+               local->ap->last_tx_rate = meta->rate =
+                       ap_update_sta_tx_rate(sta, local->dev);
+
+       if (local->iw_mode != IW_MODE_MASTER)
+               goto out;
+
+       if (!(sta->flags & WLAN_STA_PS))
+               goto out;
+
+       if (meta->flags & HOSTAP_TX_FLAGS_ADD_MOREDATA) {
+               /* indicate to STA that more frames follow */
+               hdr->frame_ctl |= __constant_cpu_to_le16(WLAN_FC_MOREDATA);
+       }
+
+       if (meta->flags & HOSTAP_TX_FLAGS_BUFFERED_FRAME) {
+               /* packet was already buffered and now send due to
+                * PS poll, so do not rebuffer it */
+               goto out;
+       }
+
+       if (skb_queue_len(&sta->tx_buf) >= STA_MAX_TX_BUFFER) {
+               PDEBUG(DEBUG_PS, "%s: No more space in STA (" MACSTR ")'s PS "
+                      "mode buffer\n", local->dev->name, MAC2STR(sta->addr));
+               /* Make sure that TIM is set for the station (it might not be
+                * after AP wlan hw reset). */
+               /* FIX: should fix hw reset to restore bits based on STA
+                * buffer state.. */
+               hostap_set_tim(local, sta->aid, 1);
+               sta->flags |= WLAN_STA_TIM;
+               ret = AP_TX_DROP;
+               goto out;
+       }
+
+       /* STA in PS mode, buffer frame for later delivery */
+       set_tim = skb_queue_empty(&sta->tx_buf);
+       skb_queue_tail(&sta->tx_buf, skb);
+       /* FIX: could save RX time to skb and expire buffered frames after
+        * some time if STA does not poll for them */
+
+       if (set_tim) {
+               if (sta->flags & WLAN_STA_TIM)
+                       PDEBUG(DEBUG_PS2, "Re-setting TIM for aid %d\n",
+                              sta->aid);
+               hostap_set_tim(local, sta->aid, 1);
+               sta->flags |= WLAN_STA_TIM;
+       }
+
+       ret = AP_TX_BUFFERED;
+
+ out:
+       if (sta != NULL) {
+               if (ret == AP_TX_CONTINUE ||
+                   ret == AP_TX_CONTINUE_NOT_AUTHORIZED) {
+                       sta->tx_packets++;
+                       sta->tx_bytes += skb->len;
+                       sta->last_tx = jiffies;
+               }
+
+               if ((ret == AP_TX_CONTINUE ||
+                    ret == AP_TX_CONTINUE_NOT_AUTHORIZED) &&
+                   sta->crypt && tx->host_encrypt) {
+                       tx->crypt = sta->crypt;
+                       tx->sta_ptr = sta; /* hostap_handle_sta_release() will
+                                           * be called to release sta info
+                                           * later */
+               } else
+                       atomic_dec(&sta->users);
+       }
+
+       return ret;
+}
+
+
+void hostap_handle_sta_release(void *ptr)
+{
+       struct sta_info *sta = ptr;
+       atomic_dec(&sta->users);
+}
+
+
+/* Called only as a tasklet (software IRQ) */
+void hostap_handle_sta_tx_exc(local_info_t *local, struct sk_buff *skb)
+{
+       struct sta_info *sta;
+       struct ieee80211_hdr *hdr;
+       struct hostap_skb_tx_data *meta;
+
+       hdr = (struct ieee80211_hdr *) skb->data;
+       meta = (struct hostap_skb_tx_data *) skb->cb;
+
+       spin_lock(&local->ap->sta_table_lock);
+       sta = ap_get_sta(local->ap, hdr->addr1);
+       if (!sta) {
+               spin_unlock(&local->ap->sta_table_lock);
+               PDEBUG(DEBUG_AP, "%s: Could not find STA " MACSTR " for this "
+                      "TX error (@%lu)\n",
+                      local->dev->name, MAC2STR(hdr->addr1), jiffies);
+               return;
+       }
+
+       sta->tx_since_last_failure = 0;
+       sta->tx_consecutive_exc++;
+
+       if (sta->tx_consecutive_exc >= WLAN_RATE_DECREASE_THRESHOLD &&
+           sta->tx_rate_idx > 0 && meta->rate <= sta->tx_rate) {
+               /* use next lower rate */
+               int old, rate;
+               old = rate = sta->tx_rate_idx;
+               while (rate > 0) {
+                       rate--;
+                       if (ap_tx_rate_ok(rate, sta, local)) {
+                               sta->tx_rate_idx = rate;
+                               break;
+                       }
+               }
+               if (old != sta->tx_rate_idx) {
+                       switch (sta->tx_rate_idx) {
+                       case 0: sta->tx_rate = 10; break;
+                       case 1: sta->tx_rate = 20; break;
+                       case 2: sta->tx_rate = 55; break;
+                       case 3: sta->tx_rate = 110; break;
+                       default: sta->tx_rate = 0; break;
+                       }
+                       PDEBUG(DEBUG_AP, "%s: STA " MACSTR " TX rate lowered "
+                              "to %d\n", local->dev->name, MAC2STR(sta->addr),
+                              sta->tx_rate);
+               }
+               sta->tx_consecutive_exc = 0;
+       }
+       spin_unlock(&local->ap->sta_table_lock);
+}
+
+
+static void hostap_update_sta_ps2(local_info_t *local, struct sta_info *sta,
+                                 int pwrmgt, int type, int stype)
+{
+       if (pwrmgt && !(sta->flags & WLAN_STA_PS)) {
+               sta->flags |= WLAN_STA_PS;
+               PDEBUG(DEBUG_PS2, "STA " MACSTR " changed to use PS "
+                      "mode (type=0x%02X, stype=0x%02X)\n",
+                      MAC2STR(sta->addr), type >> 2, stype >> 4);
+       } else if (!pwrmgt && (sta->flags & WLAN_STA_PS)) {
+               sta->flags &= ~WLAN_STA_PS;
+               PDEBUG(DEBUG_PS2, "STA " MACSTR " changed to not use "
+                      "PS mode (type=0x%02X, stype=0x%02X)\n",
+                      MAC2STR(sta->addr), type >> 2, stype >> 4);
+               if (type != IEEE80211_FTYPE_CTL ||
+                   stype != IEEE80211_STYPE_PSPOLL)
+                       schedule_packet_send(local, sta);
+       }
+}
+
+
+/* Called only as a tasklet (software IRQ). Called for each RX frame to update
+ * STA power saving state. pwrmgt is a flag from 802.11 frame_ctl field. */
+int hostap_update_sta_ps(local_info_t *local, struct ieee80211_hdr *hdr)
+{
+       struct sta_info *sta;
+       u16 fc;
+
+       spin_lock(&local->ap->sta_table_lock);
+       sta = ap_get_sta(local->ap, hdr->addr2);
+       if (sta)
+               atomic_inc(&sta->users);
+       spin_unlock(&local->ap->sta_table_lock);
+
+       if (!sta)
+               return -1;
+
+       fc = le16_to_cpu(hdr->frame_ctl);
+       hostap_update_sta_ps2(local, sta, fc & WLAN_FC_PWRMGT,
+                             WLAN_FC_GET_TYPE(fc), WLAN_FC_GET_STYPE(fc));
+
+       atomic_dec(&sta->users);
+       return 0;
+}
+
+
+/* Called only as a tasklet (software IRQ). Called for each RX frame after
+ * getting RX header and payload from hardware. */
+ap_rx_ret hostap_handle_sta_rx(local_info_t *local, struct net_device *dev,
+                              struct sk_buff *skb,
+                              struct hostap_80211_rx_status *rx_stats,
+                              int wds)
+{
+       int ret;
+       struct sta_info *sta;
+       u16 fc, type, stype;
+       struct ieee80211_hdr *hdr;
+
+       if (local->ap == NULL)
+               return AP_RX_CONTINUE;
+
+       hdr = (struct ieee80211_hdr *) skb->data;
+
+       fc = le16_to_cpu(hdr->frame_ctl);
+       type = WLAN_FC_GET_TYPE(fc);
+       stype = WLAN_FC_GET_STYPE(fc);
+
+       spin_lock(&local->ap->sta_table_lock);
+       sta = ap_get_sta(local->ap, hdr->addr2);
+       if (sta)
+               atomic_inc(&sta->users);
+       spin_unlock(&local->ap->sta_table_lock);
+
+       if (sta && !(sta->flags & WLAN_STA_AUTHORIZED))
+               ret = AP_RX_CONTINUE_NOT_AUTHORIZED;
+       else
+               ret = AP_RX_CONTINUE;
+
+
+       if (fc & WLAN_FC_TODS) {
+               if (!wds && (sta == NULL || !(sta->flags & WLAN_STA_ASSOC))) {
+                       if (local->hostapd) {
+                               prism2_rx_80211(local->apdev, skb, rx_stats,
+                                               PRISM2_RX_NON_ASSOC);
+#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
+                       } else {
+                               printk(KERN_DEBUG "%s: dropped received packet"
+                                      " from non-associated STA " MACSTR
+                                      " (type=0x%02x, subtype=0x%02x)\n",
+                                      dev->name, MAC2STR(hdr->addr2),
+                                      type >> 2, stype >> 4);
+                               hostap_rx(dev, skb, rx_stats);
+#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
+                       }
+                       ret = AP_RX_EXIT;
+                       goto out;
+               }
+       } else if (fc & WLAN_FC_FROMDS) {
+               if (!wds) {
+                       /* FromDS frame - not for us; probably
+                        * broadcast/multicast in another BSS - drop */
+                       if (memcmp(hdr->addr1, dev->dev_addr, ETH_ALEN) == 0) {
+                               printk(KERN_DEBUG "Odd.. FromDS packet "
+                                      "received with own BSSID\n");
+                               hostap_dump_rx_80211(dev->name, skb, rx_stats);
+                       }
+                       ret = AP_RX_DROP;
+                       goto out;
+               }
+       } else if (stype == IEEE80211_STYPE_NULLFUNC && sta == NULL &&
+                  memcmp(hdr->addr1, dev->dev_addr, ETH_ALEN) == 0) {
+
+               if (local->hostapd) {
+                       prism2_rx_80211(local->apdev, skb, rx_stats,
+                                       PRISM2_RX_NON_ASSOC);
+#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
+               } else {
+                       /* At least Lucent f/w seems to send data::nullfunc
+                        * frames with no ToDS flag when the current AP returns
+                        * after being unavailable for some time. Speed up
+                        * re-association by informing the station about it not
+                        * being associated. */
+                       printk(KERN_DEBUG "%s: rejected received nullfunc "
+                              "frame without ToDS from not associated STA "
+                              MACSTR "\n",
+                              dev->name, MAC2STR(hdr->addr2));
+                       hostap_rx(dev, skb, rx_stats);
+#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
+               }
+               ret = AP_RX_EXIT;
+               goto out;
+       } else if (stype == IEEE80211_STYPE_NULLFUNC) {
+               /* At least Lucent cards seem to send periodic nullfunc
+                * frames with ToDS. Let these through to update SQ
+                * stats and PS state. Nullfunc frames do not contain
+                * any data and they will be dropped below. */
+       } else {
+               /* If BSSID (Addr3) is foreign, this frame is a normal
+                * broadcast frame from an IBSS network. Drop it silently.
+                * If BSSID is own, report the dropping of this frame. */
+               if (memcmp(hdr->addr3, dev->dev_addr, ETH_ALEN) == 0) {
+                       printk(KERN_DEBUG "%s: dropped received packet from "
+                              MACSTR " with no ToDS flag (type=0x%02x, "
+                              "subtype=0x%02x)\n", dev->name,
+                              MAC2STR(hdr->addr2), type >> 2, stype >> 4);
+                       hostap_dump_rx_80211(dev->name, skb, rx_stats);
+               }
+               ret = AP_RX_DROP;
+               goto out;
+       }
+
+       if (sta) {
+               hostap_update_sta_ps2(local, sta, fc & WLAN_FC_PWRMGT,
+                                     type, stype);
+
+               sta->rx_packets++;
+               sta->rx_bytes += skb->len;
+               sta->last_rx = jiffies;
+       }
+
+       if (local->ap->nullfunc_ack && stype == IEEE80211_STYPE_NULLFUNC &&
+           fc & WLAN_FC_TODS) {
+               if (local->hostapd) {
+                       prism2_rx_80211(local->apdev, skb, rx_stats,
+                                       PRISM2_RX_NULLFUNC_ACK);
+#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
+               } else {
+                       /* some STA f/w's seem to require control::ACK frame
+                        * for data::nullfunc, but Prism2 f/w 0.8.0 (at least
+                        * from Compaq) does not send this.. Try to generate
+                        * ACK for these frames from the host driver to make
+                        * power saving work with, e.g., Lucent WaveLAN f/w */
+                       hostap_rx(dev, skb, rx_stats);
+#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
+               }
+               ret = AP_RX_EXIT;
+               goto out;
+       }
+
+ out:
+       if (sta)
+               atomic_dec(&sta->users);
+
+       return ret;
+}
+
+
+/* Called only as a tasklet (software IRQ) */
+int hostap_handle_sta_crypto(local_info_t *local,
+                            struct ieee80211_hdr *hdr,
+                            struct ieee80211_crypt_data **crypt,
+                            void **sta_ptr)
+{
+       struct sta_info *sta;
+
+       spin_lock(&local->ap->sta_table_lock);
+       sta = ap_get_sta(local->ap, hdr->addr2);
+       if (sta)
+               atomic_inc(&sta->users);
+       spin_unlock(&local->ap->sta_table_lock);
+
+       if (!sta)
+               return -1;
+
+       if (sta->crypt) {
+               *crypt = sta->crypt;
+               *sta_ptr = sta;
+               /* hostap_handle_sta_release() will be called to release STA
+                * info */
+       } else
+               atomic_dec(&sta->users);
+
+       return 0;
+}
+
+
+/* Called only as a tasklet (software IRQ) */
+int hostap_is_sta_assoc(struct ap_data *ap, u8 *sta_addr)
+{
+       struct sta_info *sta;
+       int ret = 0;
+
+       spin_lock(&ap->sta_table_lock);
+       sta = ap_get_sta(ap, sta_addr);
+       if (sta != NULL && (sta->flags & WLAN_STA_ASSOC) && !sta->ap)
+               ret = 1;
+       spin_unlock(&ap->sta_table_lock);
+
+       return ret;
+}
+
+
+/* Called only as a tasklet (software IRQ) */
+int hostap_is_sta_authorized(struct ap_data *ap, u8 *sta_addr)
+{
+       struct sta_info *sta;
+       int ret = 0;
+
+       spin_lock(&ap->sta_table_lock);
+       sta = ap_get_sta(ap, sta_addr);
+       if (sta != NULL && (sta->flags & WLAN_STA_ASSOC) && !sta->ap &&
+           ((sta->flags & WLAN_STA_AUTHORIZED) ||
+            ap->local->ieee_802_1x == 0))
+               ret = 1;
+       spin_unlock(&ap->sta_table_lock);
+
+       return ret;
+}
+
+
+/* Called only as a tasklet (software IRQ) */
+int hostap_add_sta(struct ap_data *ap, u8 *sta_addr)
+{
+       struct sta_info *sta;
+       int ret = 1;
+
+       if (!ap)
+               return -1;
+
+       spin_lock(&ap->sta_table_lock);
+       sta = ap_get_sta(ap, sta_addr);
+       if (sta)
+               ret = 0;
+       spin_unlock(&ap->sta_table_lock);
+
+       if (ret == 1) {
+               sta = ap_add_sta(ap, sta_addr);
+               if (!sta)
+                       ret = -1;
+               sta->flags = WLAN_STA_AUTH | WLAN_STA_ASSOC;
+               sta->ap = 1;
+               memset(sta->supported_rates, 0, sizeof(sta->supported_rates));
+               /* No way of knowing which rates are supported since we did not
+                * get supported rates element from beacon/assoc req. Assume
+                * that remote end supports all 802.11b rates. */
+               sta->supported_rates[0] = 0x82;
+               sta->supported_rates[1] = 0x84;
+               sta->supported_rates[2] = 0x0b;
+               sta->supported_rates[3] = 0x16;
+               sta->tx_supp_rates = WLAN_RATE_1M | WLAN_RATE_2M |
+                       WLAN_RATE_5M5 | WLAN_RATE_11M;
+               sta->tx_rate = 110;
+               sta->tx_max_rate = sta->tx_rate_idx = 3;
+       }
+
+       return ret;
+}
+
+
+/* Called only as a tasklet (software IRQ) */
+int hostap_update_rx_stats(struct ap_data *ap,
+                          struct ieee80211_hdr *hdr,
+                          struct hostap_80211_rx_status *rx_stats)
+{
+       struct sta_info *sta;
+
+       if (!ap)
+               return -1;
+
+       spin_lock(&ap->sta_table_lock);
+       sta = ap_get_sta(ap, hdr->addr2);
+       if (sta) {
+               sta->last_rx_silence = rx_stats->noise;
+               sta->last_rx_signal = rx_stats->signal;
+               sta->last_rx_rate = rx_stats->rate;
+               sta->last_rx_updated = 7;
+               if (rx_stats->rate == 10)
+                       sta->rx_count[0]++;
+               else if (rx_stats->rate == 20)
+                       sta->rx_count[1]++;
+               else if (rx_stats->rate == 55)
+                       sta->rx_count[2]++;
+               else if (rx_stats->rate == 110)
+                       sta->rx_count[3]++;
+       }
+       spin_unlock(&ap->sta_table_lock);
+
+       return sta ? 0 : -1;
+}
+
+
+void hostap_update_rates(local_info_t *local)
+{
+       struct list_head *ptr;
+       struct ap_data *ap = local->ap;
+
+       if (!ap)
+               return;
+
+       spin_lock_bh(&ap->sta_table_lock);
+       for (ptr = ap->sta_list.next; ptr != &ap->sta_list; ptr = ptr->next) {
+               struct sta_info *sta = (struct sta_info *) ptr;
+               prism2_check_tx_rates(sta);
+       }
+       spin_unlock_bh(&ap->sta_table_lock);
+}
+
+
+static void * ap_crypt_get_ptrs(struct ap_data *ap, u8 *addr, int permanent,
+                               struct ieee80211_crypt_data ***crypt)
+{
+       struct sta_info *sta;
+
+       spin_lock_bh(&ap->sta_table_lock);
+       sta = ap_get_sta(ap, addr);
+       if (sta)
+               atomic_inc(&sta->users);
+       spin_unlock_bh(&ap->sta_table_lock);
+
+       if (!sta && permanent)
+               sta = ap_add_sta(ap, addr);
+
+       if (!sta)
+               return NULL;
+
+       if (permanent)
+               sta->flags |= WLAN_STA_PERM;
+
+       *crypt = &sta->crypt;
+
+       return sta;
+}
+
+
+void hostap_add_wds_links(local_info_t *local)
+{
+       struct ap_data *ap = local->ap;
+       struct list_head *ptr;
+
+       spin_lock_bh(&ap->sta_table_lock);
+       list_for_each(ptr, &ap->sta_list) {
+               struct sta_info *sta = list_entry(ptr, struct sta_info, list);
+               if (sta->ap)
+                       hostap_wds_link_oper(local, sta->addr, WDS_ADD);
+       }
+       spin_unlock_bh(&ap->sta_table_lock);
+
+       schedule_work(&local->ap->wds_oper_queue);
+}
+
+
+void hostap_wds_link_oper(local_info_t *local, u8 *addr, wds_oper_type type)
+{
+       struct wds_oper_data *entry;
+
+       entry = kmalloc(sizeof(*entry), GFP_ATOMIC);
+       if (!entry)
+               return;
+       memcpy(entry->addr, addr, ETH_ALEN);
+       entry->type = type;
+       spin_lock_bh(&local->lock);
+       entry->next = local->ap->wds_oper_entries;
+       local->ap->wds_oper_entries = entry;
+       spin_unlock_bh(&local->lock);
+
+       schedule_work(&local->ap->wds_oper_queue);
+}
+
+
+EXPORT_SYMBOL(hostap_init_data);
+EXPORT_SYMBOL(hostap_init_ap_proc);
+EXPORT_SYMBOL(hostap_free_data);
+EXPORT_SYMBOL(hostap_check_sta_fw_version);
+EXPORT_SYMBOL(hostap_handle_sta_tx);
+EXPORT_SYMBOL(hostap_handle_sta_release);
+EXPORT_SYMBOL(hostap_handle_sta_tx_exc);
+EXPORT_SYMBOL(hostap_update_sta_ps);
+EXPORT_SYMBOL(hostap_handle_sta_rx);
+EXPORT_SYMBOL(hostap_is_sta_assoc);
+EXPORT_SYMBOL(hostap_is_sta_authorized);
+EXPORT_SYMBOL(hostap_add_sta);
+EXPORT_SYMBOL(hostap_update_rates);
+EXPORT_SYMBOL(hostap_add_wds_links);
+EXPORT_SYMBOL(hostap_wds_link_oper);
+#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
+EXPORT_SYMBOL(hostap_deauth_all_stas);
+#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
diff --git a/drivers/net/wireless/hostap/hostap_ap.h b/drivers/net/wireless/hostap/hostap_ap.h
new file mode 100644 (file)
index 0000000..816a52b
--- /dev/null
@@ -0,0 +1,261 @@
+#ifndef HOSTAP_AP_H
+#define HOSTAP_AP_H
+
+/* AP data structures for STAs */
+
+/* maximum number of frames to buffer per STA */
+#define STA_MAX_TX_BUFFER 32
+
+/* STA flags */
+#define WLAN_STA_AUTH BIT(0)
+#define WLAN_STA_ASSOC BIT(1)
+#define WLAN_STA_PS BIT(2)
+#define WLAN_STA_TIM BIT(3) /* TIM bit is on for PS stations */
+#define WLAN_STA_PERM BIT(4) /* permanent; do not remove entry on expiration */
+#define WLAN_STA_AUTHORIZED BIT(5) /* If 802.1X is used, this flag is
+                                   * controlling whether STA is authorized to
+                                   * send and receive non-IEEE 802.1X frames
+                                   */
+#define WLAN_STA_PENDING_POLL BIT(6) /* pending activity poll not ACKed */
+
+#define WLAN_RATE_1M BIT(0)
+#define WLAN_RATE_2M BIT(1)
+#define WLAN_RATE_5M5 BIT(2)
+#define WLAN_RATE_11M BIT(3)
+#define WLAN_RATE_COUNT 4
+
+/* Maximum size of Supported Rates info element. IEEE 802.11 has a limit of 8,
+ * but some pre-standard IEEE 802.11g products use longer elements. */
+#define WLAN_SUPP_RATES_MAX 32
+
+/* Try to increase TX rate after # successfully sent consecutive packets */
+#define WLAN_RATE_UPDATE_COUNT 50
+
+/* Decrease TX rate after # consecutive dropped packets */
+#define WLAN_RATE_DECREASE_THRESHOLD 2
+
+struct sta_info {
+       struct list_head list;
+       struct sta_info *hnext; /* next entry in hash table list */
+       atomic_t users; /* number of users (do not remove if > 0) */
+       struct proc_dir_entry *proc;
+
+       u8 addr[6];
+       u16 aid; /* STA's unique AID (1 .. 2007) or 0 if not yet assigned */
+       u32 flags;
+       u16 capability;
+       u16 listen_interval; /* or beacon_int for APs */
+       u8 supported_rates[WLAN_SUPP_RATES_MAX];
+
+       unsigned long last_auth;
+       unsigned long last_assoc;
+       unsigned long last_rx;
+       unsigned long last_tx;
+       unsigned long rx_packets, tx_packets;
+       unsigned long rx_bytes, tx_bytes;
+       struct sk_buff_head tx_buf;
+       /* FIX: timeout buffers with an expiry time somehow derived from
+        * listen_interval */
+
+       s8 last_rx_silence; /* Noise in dBm */
+       s8 last_rx_signal; /* Signal strength in dBm */
+       u8 last_rx_rate; /* TX rate in 0.1 Mbps */
+       u8 last_rx_updated; /* IWSPY's struct iw_quality::updated */
+
+       u8 tx_supp_rates; /* bit field of supported TX rates */
+       u8 tx_rate; /* current TX rate (in 0.1 Mbps) */
+       u8 tx_rate_idx; /* current TX rate (WLAN_RATE_*) */
+       u8 tx_max_rate; /* max TX rate (WLAN_RATE_*) */
+       u32 tx_count[WLAN_RATE_COUNT]; /* number of frames sent (per rate) */
+       u32 rx_count[WLAN_RATE_COUNT]; /* number of frames received (per rate)
+                                       */
+       u32 tx_since_last_failure;
+       u32 tx_consecutive_exc;
+
+       struct ieee80211_crypt_data *crypt;
+
+       int ap; /* whether this station is an AP */
+
+       local_info_t *local;
+
+#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
+       union {
+               struct {
+                       char *challenge; /* shared key authentication
+                                         * challenge */
+               } sta;
+               struct {
+                       int ssid_len;
+                       unsigned char ssid[MAX_SSID_LEN + 1]; /* AP's ssid */
+                       int channel;
+                       unsigned long last_beacon; /* last RX beacon time */
+               } ap;
+       } u;
+
+       struct timer_list timer;
+       enum { STA_NULLFUNC = 0, STA_DISASSOC, STA_DEAUTH } timeout_next;
+#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
+};
+
+
+#define MAX_STA_COUNT 1024
+
+/* Maximum number of AIDs to use for STAs; must be 2007 or lower
+ * (8802.11 limitation) */
+#define MAX_AID_TABLE_SIZE 128
+
+#define STA_HASH_SIZE 256
+#define STA_HASH(sta) (sta[5])
+
+
+/* Default value for maximum station inactivity. After AP_MAX_INACTIVITY_SEC
+ * has passed since last received frame from the station, a nullfunc data
+ * frame is sent to the station. If this frame is not acknowledged and no other
+ * frames have been received, the station will be disassociated after
+ * AP_DISASSOC_DELAY. Similarily, a the station will be deauthenticated after
+ * AP_DEAUTH_DELAY. AP_TIMEOUT_RESOLUTION is the resolution that is used with
+ * max inactivity timer. */
+#define AP_MAX_INACTIVITY_SEC (5 * 60)
+#define AP_DISASSOC_DELAY (HZ)
+#define AP_DEAUTH_DELAY (HZ)
+
+/* ap_policy: whether to accept frames to/from other APs/IBSS */
+typedef enum {
+       AP_OTHER_AP_SKIP_ALL = 0,
+       AP_OTHER_AP_SAME_SSID = 1,
+       AP_OTHER_AP_ALL = 2,
+       AP_OTHER_AP_EVEN_IBSS = 3
+} ap_policy_enum;
+
+#define PRISM2_AUTH_OPEN BIT(0)
+#define PRISM2_AUTH_SHARED_KEY BIT(1)
+
+
+/* MAC address-based restrictions */
+struct mac_entry {
+       struct list_head list;
+       u8 addr[6];
+};
+
+struct mac_restrictions {
+       enum { MAC_POLICY_OPEN = 0, MAC_POLICY_ALLOW, MAC_POLICY_DENY } policy;
+       unsigned int entries;
+       struct list_head mac_list;
+       spinlock_t lock;
+};
+
+
+struct add_sta_proc_data {
+       u8 addr[ETH_ALEN];
+       struct add_sta_proc_data *next;
+};
+
+
+typedef enum { WDS_ADD, WDS_DEL } wds_oper_type;
+struct wds_oper_data {
+       wds_oper_type type;
+       u8 addr[ETH_ALEN];
+       struct wds_oper_data *next;
+};
+
+
+struct ap_data {
+       int initialized; /* whether ap_data has been initialized */
+       local_info_t *local;
+       int bridge_packets; /* send packet to associated STAs directly to the
+                            * wireless media instead of higher layers in the
+                            * kernel */
+       unsigned int bridged_unicast; /* number of unicast frames bridged on
+                                      * wireless media */
+       unsigned int bridged_multicast; /* number of non-unicast frames
+                                        * bridged on wireless media */
+       unsigned int tx_drop_nonassoc; /* number of unicast TX packets dropped
+                                       * because they were to an address that
+                                       * was not associated */
+       int nullfunc_ack; /* use workaround for nullfunc frame ACKs */
+
+       spinlock_t sta_table_lock;
+       int num_sta; /* number of entries in sta_list */
+       struct list_head sta_list; /* STA info list head */
+       struct sta_info *sta_hash[STA_HASH_SIZE];
+
+       struct proc_dir_entry *proc;
+
+       ap_policy_enum ap_policy;
+       unsigned int max_inactivity;
+       int autom_ap_wds;
+
+       struct mac_restrictions mac_restrictions; /* MAC-based auth */
+       int last_tx_rate;
+
+       struct work_struct add_sta_proc_queue;
+       struct add_sta_proc_data *add_sta_proc_entries;
+
+       struct work_struct wds_oper_queue;
+       struct wds_oper_data *wds_oper_entries;
+
+       u16 tx_callback_idx;
+
+#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
+       /* pointers to STA info; based on allocated AID or NULL if AID free
+        * AID is in the range 1-2007, so sta_aid[0] corresponders to AID 1
+        * and so on
+        */
+       struct sta_info *sta_aid[MAX_AID_TABLE_SIZE];
+
+       u16 tx_callback_auth, tx_callback_assoc, tx_callback_poll;
+
+       /* WEP operations for generating challenges to be used with shared key
+        * authentication */
+       struct ieee80211_crypto_ops *crypt;
+       void *crypt_priv;
+#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
+};
+
+
+void hostap_rx(struct net_device *dev, struct sk_buff *skb,
+              struct hostap_80211_rx_status *rx_stats);
+void hostap_init_data(local_info_t *local);
+void hostap_init_ap_proc(local_info_t *local);
+void hostap_free_data(struct ap_data *ap);
+void hostap_check_sta_fw_version(struct ap_data *ap, int sta_fw_ver);
+
+typedef enum {
+       AP_TX_CONTINUE, AP_TX_DROP, AP_TX_RETRY, AP_TX_BUFFERED,
+       AP_TX_CONTINUE_NOT_AUTHORIZED
+} ap_tx_ret;
+struct hostap_tx_data {
+       struct sk_buff *skb;
+       int host_encrypt;
+       struct ieee80211_crypt_data *crypt;
+       void *sta_ptr;
+};
+ap_tx_ret hostap_handle_sta_tx(local_info_t *local, struct hostap_tx_data *tx);
+void hostap_handle_sta_release(void *ptr);
+void hostap_handle_sta_tx_exc(local_info_t *local, struct sk_buff *skb);
+int hostap_update_sta_ps(local_info_t *local, struct ieee80211_hdr *hdr);
+typedef enum {
+       AP_RX_CONTINUE, AP_RX_DROP, AP_RX_EXIT, AP_RX_CONTINUE_NOT_AUTHORIZED
+} ap_rx_ret;
+ap_rx_ret hostap_handle_sta_rx(local_info_t *local, struct net_device *dev,
+                              struct sk_buff *skb,
+                              struct hostap_80211_rx_status *rx_stats,
+                              int wds);
+int hostap_handle_sta_crypto(local_info_t *local, struct ieee80211_hdr *hdr,
+                            struct ieee80211_crypt_data **crypt,
+                            void **sta_ptr);
+int hostap_is_sta_assoc(struct ap_data *ap, u8 *sta_addr);
+int hostap_is_sta_authorized(struct ap_data *ap, u8 *sta_addr);
+int hostap_add_sta(struct ap_data *ap, u8 *sta_addr);
+int hostap_update_rx_stats(struct ap_data *ap, struct ieee80211_hdr *hdr,
+                          struct hostap_80211_rx_status *rx_stats);
+void hostap_update_rates(local_info_t *local);
+void hostap_add_wds_links(local_info_t *local);
+void hostap_wds_link_oper(local_info_t *local, u8 *addr, wds_oper_type type);
+
+#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
+void hostap_deauth_all_stas(struct net_device *dev, struct ap_data *ap,
+                           int resend);
+#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
+
+#endif /* HOSTAP_AP_H */
diff --git a/drivers/net/wireless/hostap/hostap_common.h b/drivers/net/wireless/hostap/hostap_common.h
new file mode 100644 (file)
index 0000000..199a658
--- /dev/null
@@ -0,0 +1,448 @@
+#ifndef HOSTAP_COMMON_H
+#define HOSTAP_COMMON_H
+
+#define BIT(x) (1 << (x))
+
+#define MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5]
+#define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x"
+
+
+/* IEEE 802.11 defines */
+
+#define WLAN_FC_PVER (BIT(1) | BIT(0))
+#define WLAN_FC_TODS BIT(8)
+#define WLAN_FC_FROMDS BIT(9)
+#define WLAN_FC_MOREFRAG BIT(10)
+#define WLAN_FC_RETRY BIT(11)
+#define WLAN_FC_PWRMGT BIT(12)
+#define WLAN_FC_MOREDATA BIT(13)
+#define WLAN_FC_ISWEP BIT(14)
+#define WLAN_FC_ORDER BIT(15)
+
+#define WLAN_CAPABILITY_ESS WLAN_CAPABILITY_BSS
+
+
+/* Information Element IDs */
+#define WLAN_EID_SSID 0
+#define WLAN_EID_SUPP_RATES 1
+#define WLAN_EID_FH_PARAMS 2
+#define WLAN_EID_DS_PARAMS 3
+#define WLAN_EID_CF_PARAMS 4
+#define WLAN_EID_TIM 5
+#define WLAN_EID_IBSS_PARAMS 6
+#define WLAN_EID_CHALLENGE 16
+#define WLAN_EID_RSN 48
+#define WLAN_EID_GENERIC 221
+
+
+/* HFA384X Configuration RIDs */
+#define HFA384X_RID_CNFPORTTYPE 0xFC00
+#define HFA384X_RID_CNFOWNMACADDR 0xFC01
+#define HFA384X_RID_CNFDESIREDSSID 0xFC02
+#define HFA384X_RID_CNFOWNCHANNEL 0xFC03
+#define HFA384X_RID_CNFOWNSSID 0xFC04
+#define HFA384X_RID_CNFOWNATIMWINDOW 0xFC05
+#define HFA384X_RID_CNFSYSTEMSCALE 0xFC06
+#define HFA384X_RID_CNFMAXDATALEN 0xFC07
+#define HFA384X_RID_CNFWDSADDRESS 0xFC08
+#define HFA384X_RID_CNFPMENABLED 0xFC09
+#define HFA384X_RID_CNFPMEPS 0xFC0A
+#define HFA384X_RID_CNFMULTICASTRECEIVE 0xFC0B
+#define HFA384X_RID_CNFMAXSLEEPDURATION 0xFC0C
+#define HFA384X_RID_CNFPMHOLDOVERDURATION 0xFC0D
+#define HFA384X_RID_CNFOWNNAME 0xFC0E
+#define HFA384X_RID_CNFOWNDTIMPERIOD 0xFC10
+#define HFA384X_RID_CNFWDSADDRESS1 0xFC11 /* AP f/w only */
+#define HFA384X_RID_CNFWDSADDRESS2 0xFC12 /* AP f/w only */
+#define HFA384X_RID_CNFWDSADDRESS3 0xFC13 /* AP f/w only */
+#define HFA384X_RID_CNFWDSADDRESS4 0xFC14 /* AP f/w only */
+#define HFA384X_RID_CNFWDSADDRESS5 0xFC15 /* AP f/w only */
+#define HFA384X_RID_CNFWDSADDRESS6 0xFC16 /* AP f/w only */
+#define HFA384X_RID_CNFMULTICASTPMBUFFERING 0xFC17 /* AP f/w only */
+#define HFA384X_RID_UNKNOWN1 0xFC20
+#define HFA384X_RID_UNKNOWN2 0xFC21
+#define HFA384X_RID_CNFWEPDEFAULTKEYID 0xFC23
+#define HFA384X_RID_CNFDEFAULTKEY0 0xFC24
+#define HFA384X_RID_CNFDEFAULTKEY1 0xFC25
+#define HFA384X_RID_CNFDEFAULTKEY2 0xFC26
+#define HFA384X_RID_CNFDEFAULTKEY3 0xFC27
+#define HFA384X_RID_CNFWEPFLAGS 0xFC28
+#define HFA384X_RID_CNFWEPKEYMAPPINGTABLE 0xFC29
+#define HFA384X_RID_CNFAUTHENTICATION 0xFC2A
+#define HFA384X_RID_CNFMAXASSOCSTA 0xFC2B /* AP f/w only */
+#define HFA384X_RID_CNFTXCONTROL 0xFC2C
+#define HFA384X_RID_CNFROAMINGMODE 0xFC2D
+#define HFA384X_RID_CNFHOSTAUTHENTICATION 0xFC2E /* AP f/w only */
+#define HFA384X_RID_CNFRCVCRCERROR 0xFC30
+#define HFA384X_RID_CNFMMLIFE 0xFC31
+#define HFA384X_RID_CNFALTRETRYCOUNT 0xFC32
+#define HFA384X_RID_CNFBEACONINT 0xFC33
+#define HFA384X_RID_CNFAPPCFINFO 0xFC34 /* AP f/w only */
+#define HFA384X_RID_CNFSTAPCFINFO 0xFC35
+#define HFA384X_RID_CNFPRIORITYQUSAGE 0xFC37
+#define HFA384X_RID_CNFTIMCTRL 0xFC40
+#define HFA384X_RID_UNKNOWN3 0xFC41 /* added in STA f/w 0.7.x */
+#define HFA384X_RID_CNFTHIRTY2TALLY 0xFC42 /* added in STA f/w 0.8.0 */
+#define HFA384X_RID_CNFENHSECURITY 0xFC43 /* AP f/w or STA f/w >= 1.6.3 */
+#define HFA384X_RID_CNFDBMADJUST 0xFC46 /* added in STA f/w 1.3.1 */
+#define HFA384X_RID_GENERICELEMENT 0xFC48 /* added in STA f/w 1.7.0;
+                                          * write only */
+#define HFA384X_RID_PROPAGATIONDELAY 0xFC49 /* added in STA f/w 1.7.6 */
+#define HFA384X_RID_GROUPADDRESSES 0xFC80
+#define HFA384X_RID_CREATEIBSS 0xFC81
+#define HFA384X_RID_FRAGMENTATIONTHRESHOLD 0xFC82
+#define HFA384X_RID_RTSTHRESHOLD 0xFC83
+#define HFA384X_RID_TXRATECONTROL 0xFC84
+#define HFA384X_RID_PROMISCUOUSMODE 0xFC85
+#define HFA384X_RID_FRAGMENTATIONTHRESHOLD0 0xFC90 /* AP f/w only */
+#define HFA384X_RID_FRAGMENTATIONTHRESHOLD1 0xFC91 /* AP f/w only */
+#define HFA384X_RID_FRAGMENTATIONTHRESHOLD2 0xFC92 /* AP f/w only */
+#define HFA384X_RID_FRAGMENTATIONTHRESHOLD3 0xFC93 /* AP f/w only */
+#define HFA384X_RID_FRAGMENTATIONTHRESHOLD4 0xFC94 /* AP f/w only */
+#define HFA384X_RID_FRAGMENTATIONTHRESHOLD5 0xFC95 /* AP f/w only */
+#define HFA384X_RID_FRAGMENTATIONTHRESHOLD6 0xFC96 /* AP f/w only */
+#define HFA384X_RID_RTSTHRESHOLD0 0xFC97 /* AP f/w only */
+#define HFA384X_RID_RTSTHRESHOLD1 0xFC98 /* AP f/w only */
+#define HFA384X_RID_RTSTHRESHOLD2 0xFC99 /* AP f/w only */
+#define HFA384X_RID_RTSTHRESHOLD3 0xFC9A /* AP f/w only */
+#define HFA384X_RID_RTSTHRESHOLD4 0xFC9B /* AP f/w only */
+#define HFA384X_RID_RTSTHRESHOLD5 0xFC9C /* AP f/w only */
+#define HFA384X_RID_RTSTHRESHOLD6 0xFC9D /* AP f/w only */
+#define HFA384X_RID_TXRATECONTROL0 0xFC9E /* AP f/w only */
+#define HFA384X_RID_TXRATECONTROL1 0xFC9F /* AP f/w only */
+#define HFA384X_RID_TXRATECONTROL2 0xFCA0 /* AP f/w only */
+#define HFA384X_RID_TXRATECONTROL3 0xFCA1 /* AP f/w only */
+#define HFA384X_RID_TXRATECONTROL4 0xFCA2 /* AP f/w only */
+#define HFA384X_RID_TXRATECONTROL5 0xFCA3 /* AP f/w only */
+#define HFA384X_RID_TXRATECONTROL6 0xFCA4 /* AP f/w only */
+#define HFA384X_RID_CNFSHORTPREAMBLE 0xFCB0
+#define HFA384X_RID_CNFEXCLUDELONGPREAMBLE 0xFCB1
+#define HFA384X_RID_CNFAUTHENTICATIONRSPTO 0xFCB2
+#define HFA384X_RID_CNFBASICRATES 0xFCB3
+#define HFA384X_RID_CNFSUPPORTEDRATES 0xFCB4
+#define HFA384X_RID_CNFFALLBACKCTRL 0xFCB5 /* added in STA f/w 1.3.1 */
+#define HFA384X_RID_WEPKEYDISABLE 0xFCB6 /* added in STA f/w 1.3.1 */
+#define HFA384X_RID_WEPKEYMAPINDEX 0xFCB7 /* ? */
+#define HFA384X_RID_BROADCASTKEYID 0xFCB8 /* ? */
+#define HFA384X_RID_ENTSECFLAGEYID 0xFCB9 /* ? */
+#define HFA384X_RID_CNFPASSIVESCANCTRL 0xFCBA /* added in STA f/w 1.5.0 */
+#define HFA384X_RID_SSNHANDLINGMODE 0xFCBB /* added in STA f/w 1.7.0 */
+#define HFA384X_RID_MDCCONTROL 0xFCBC /* added in STA f/w 1.7.0 */
+#define HFA384X_RID_MDCCOUNTRY 0xFCBD /* added in STA f/w 1.7.0 */
+#define HFA384X_RID_TXPOWERMAX 0xFCBE /* added in STA f/w 1.7.0 */
+#define HFA384X_RID_CNFLFOENABLED 0xFCBF /* added in STA f/w 1.6.3 */
+#define HFA384X_RID_CAPINFO 0xFCC0 /* added in STA f/w 1.7.0 */
+#define HFA384X_RID_LISTENINTERVAL 0xFCC1 /* added in STA f/w 1.7.0 */
+#define HFA384X_RID_SW_ANT_DIV 0xFCC2 /* added in STA f/w 1.7.0; Prism3 */
+#define HFA384X_RID_LED_CTRL 0xFCC4 /* added in STA f/w 1.7.6 */
+#define HFA384X_RID_HFODELAY 0xFCC5 /* added in STA f/w 1.7.6 */
+#define HFA384X_RID_DISALLOWEDBSSID 0xFCC6 /* added in STA f/w 1.8.0 */
+#define HFA384X_RID_TICKTIME 0xFCE0
+#define HFA384X_RID_SCANREQUEST 0xFCE1
+#define HFA384X_RID_JOINREQUEST 0xFCE2
+#define HFA384X_RID_AUTHENTICATESTATION 0xFCE3 /* AP f/w only */
+#define HFA384X_RID_CHANNELINFOREQUEST 0xFCE4 /* AP f/w only */
+#define HFA384X_RID_HOSTSCAN 0xFCE5 /* added in STA f/w 1.3.1 */
+
+/* HFA384X Information RIDs */
+#define HFA384X_RID_MAXLOADTIME 0xFD00
+#define HFA384X_RID_DOWNLOADBUFFER 0xFD01
+#define HFA384X_RID_PRIID 0xFD02
+#define HFA384X_RID_PRISUPRANGE 0xFD03
+#define HFA384X_RID_CFIACTRANGES 0xFD04
+#define HFA384X_RID_NICSERNUM 0xFD0A
+#define HFA384X_RID_NICID 0xFD0B
+#define HFA384X_RID_MFISUPRANGE 0xFD0C
+#define HFA384X_RID_CFISUPRANGE 0xFD0D
+#define HFA384X_RID_CHANNELLIST 0xFD10
+#define HFA384X_RID_REGULATORYDOMAINS 0xFD11
+#define HFA384X_RID_TEMPTYPE 0xFD12
+#define HFA384X_RID_CIS 0xFD13
+#define HFA384X_RID_STAID 0xFD20
+#define HFA384X_RID_STASUPRANGE 0xFD21
+#define HFA384X_RID_MFIACTRANGES 0xFD22
+#define HFA384X_RID_CFIACTRANGES2 0xFD23
+#define HFA384X_RID_PRODUCTNAME 0xFD24 /* added in STA f/w 1.3.1;
+                                       * only Prism2.5(?) */
+#define HFA384X_RID_PORTSTATUS 0xFD40
+#define HFA384X_RID_CURRENTSSID 0xFD41
+#define HFA384X_RID_CURRENTBSSID 0xFD42
+#define HFA384X_RID_COMMSQUALITY 0xFD43
+#define HFA384X_RID_CURRENTTXRATE 0xFD44
+#define HFA384X_RID_CURRENTBEACONINTERVAL 0xFD45
+#define HFA384X_RID_CURRENTSCALETHRESHOLDS 0xFD46
+#define HFA384X_RID_PROTOCOLRSPTIME 0xFD47
+#define HFA384X_RID_SHORTRETRYLIMIT 0xFD48
+#define HFA384X_RID_LONGRETRYLIMIT 0xFD49
+#define HFA384X_RID_MAXTRANSMITLIFETIME 0xFD4A
+#define HFA384X_RID_MAXRECEIVELIFETIME 0xFD4B
+#define HFA384X_RID_CFPOLLABLE 0xFD4C
+#define HFA384X_RID_AUTHENTICATIONALGORITHMS 0xFD4D
+#define HFA384X_RID_PRIVACYOPTIONIMPLEMENTED 0xFD4F
+#define HFA384X_RID_DBMCOMMSQUALITY 0xFD51 /* added in STA f/w 1.3.1 */
+#define HFA384X_RID_CURRENTTXRATE1 0xFD80 /* AP f/w only */
+#define HFA384X_RID_CURRENTTXRATE2 0xFD81 /* AP f/w only */
+#define HFA384X_RID_CURRENTTXRATE3 0xFD82 /* AP f/w only */
+#define HFA384X_RID_CURRENTTXRATE4 0xFD83 /* AP f/w only */
+#define HFA384X_RID_CURRENTTXRATE5 0xFD84 /* AP f/w only */
+#define HFA384X_RID_CURRENTTXRATE6 0xFD85 /* AP f/w only */
+#define HFA384X_RID_OWNMACADDR 0xFD86 /* AP f/w only */
+#define HFA384X_RID_SCANRESULTSTABLE 0xFD88 /* added in STA f/w 0.8.3 */
+#define HFA384X_RID_HOSTSCANRESULTS 0xFD89 /* added in STA f/w 1.3.1 */
+#define HFA384X_RID_AUTHENTICATIONUSED 0xFD8A /* added in STA f/w 1.3.4 */
+#define HFA384X_RID_CNFFAASWITCHCTRL 0xFD8B /* added in STA f/w 1.6.3 */
+#define HFA384X_RID_ASSOCIATIONFAILURE 0xFD8D /* added in STA f/w 1.8.0 */
+#define HFA384X_RID_PHYTYPE 0xFDC0
+#define HFA384X_RID_CURRENTCHANNEL 0xFDC1
+#define HFA384X_RID_CURRENTPOWERSTATE 0xFDC2
+#define HFA384X_RID_CCAMODE 0xFDC3
+#define HFA384X_RID_SUPPORTEDDATARATES 0xFDC6
+#define HFA384X_RID_LFO_VOLT_REG_TEST_RES 0xFDC7 /* added in STA f/w 1.7.1 */
+#define HFA384X_RID_BUILDSEQ 0xFFFE
+#define HFA384X_RID_FWID 0xFFFF
+
+
+struct hfa384x_comp_ident
+{
+       u16 id;
+       u16 variant;
+       u16 major;
+       u16 minor;
+} __attribute__ ((packed));
+
+#define HFA384X_COMP_ID_PRI 0x15
+#define HFA384X_COMP_ID_STA 0x1f
+#define HFA384X_COMP_ID_FW_AP 0x14b
+
+struct hfa384x_sup_range
+{
+       u16 role;
+       u16 id;
+       u16 variant;
+       u16 bottom;
+       u16 top;
+} __attribute__ ((packed));
+
+
+struct hfa384x_build_id
+{
+       u16 pri_seq;
+       u16 sec_seq;
+} __attribute__ ((packed));
+
+/* FD01 - Download Buffer */
+struct hfa384x_rid_download_buffer
+{
+       u16 page;
+       u16 offset;
+       u16 length;
+} __attribute__ ((packed));
+
+/* BSS connection quality (RID FD43 range, RID FD51 dBm-normalized) */
+struct hfa384x_comms_quality {
+       u16 comm_qual; /* 0 .. 92 */
+       u16 signal_level; /* 27 .. 154 */
+       u16 noise_level; /* 27 .. 154 */
+} __attribute__ ((packed));
+
+
+/* netdevice private ioctls (used, e.g., with iwpriv from user space) */
+
+/* New wireless extensions API - SET/GET convention (even ioctl numbers are
+ * root only)
+ */
+#define PRISM2_IOCTL_PRISM2_PARAM (SIOCIWFIRSTPRIV + 0)
+#define PRISM2_IOCTL_GET_PRISM2_PARAM (SIOCIWFIRSTPRIV + 1)
+#define PRISM2_IOCTL_WRITEMIF (SIOCIWFIRSTPRIV + 2)
+#define PRISM2_IOCTL_READMIF (SIOCIWFIRSTPRIV + 3)
+#define PRISM2_IOCTL_MONITOR (SIOCIWFIRSTPRIV + 4)
+#define PRISM2_IOCTL_RESET (SIOCIWFIRSTPRIV + 6)
+#define PRISM2_IOCTL_INQUIRE (SIOCIWFIRSTPRIV + 8)
+#define PRISM2_IOCTL_WDS_ADD (SIOCIWFIRSTPRIV + 10)
+#define PRISM2_IOCTL_WDS_DEL (SIOCIWFIRSTPRIV + 12)
+#define PRISM2_IOCTL_SET_RID_WORD (SIOCIWFIRSTPRIV + 14)
+#define PRISM2_IOCTL_MACCMD (SIOCIWFIRSTPRIV + 16)
+#define PRISM2_IOCTL_ADDMAC (SIOCIWFIRSTPRIV + 18)
+#define PRISM2_IOCTL_DELMAC (SIOCIWFIRSTPRIV + 20)
+#define PRISM2_IOCTL_KICKMAC (SIOCIWFIRSTPRIV + 22)
+
+/* following are not in SIOCGIWPRIV list; check permission in the driver code
+ */
+#define PRISM2_IOCTL_DOWNLOAD (SIOCDEVPRIVATE + 13)
+#define PRISM2_IOCTL_HOSTAPD (SIOCDEVPRIVATE + 14)
+
+
+/* PRISM2_IOCTL_PRISM2_PARAM ioctl() subtypes: */
+enum {
+       /* PRISM2_PARAM_PTYPE = 1, */ /* REMOVED 2003-10-22 */
+       PRISM2_PARAM_TXRATECTRL = 2,
+       PRISM2_PARAM_BEACON_INT = 3,
+       PRISM2_PARAM_PSEUDO_IBSS = 4,
+       PRISM2_PARAM_ALC = 5,
+       /* PRISM2_PARAM_TXPOWER = 6, */ /* REMOVED 2003-10-22 */
+       PRISM2_PARAM_DUMP = 7,
+       PRISM2_PARAM_OTHER_AP_POLICY = 8,
+       PRISM2_PARAM_AP_MAX_INACTIVITY = 9,
+       PRISM2_PARAM_AP_BRIDGE_PACKETS = 10,
+       PRISM2_PARAM_DTIM_PERIOD = 11,
+       PRISM2_PARAM_AP_NULLFUNC_ACK = 12,
+       PRISM2_PARAM_MAX_WDS = 13,
+       PRISM2_PARAM_AP_AUTOM_AP_WDS = 14,
+       PRISM2_PARAM_AP_AUTH_ALGS = 15,
+       PRISM2_PARAM_MONITOR_ALLOW_FCSERR = 16,
+       PRISM2_PARAM_HOST_ENCRYPT = 17,
+       PRISM2_PARAM_HOST_DECRYPT = 18,
+       /* PRISM2_PARAM_BUS_MASTER_THRESHOLD_RX = 19, REMOVED 2005-08-14 */
+       /* PRISM2_PARAM_BUS_MASTER_THRESHOLD_TX = 20, REMOVED 2005-08-14 */
+       PRISM2_PARAM_HOST_ROAMING = 21,
+       PRISM2_PARAM_BCRX_STA_KEY = 22,
+       PRISM2_PARAM_IEEE_802_1X = 23,
+       PRISM2_PARAM_ANTSEL_TX = 24,
+       PRISM2_PARAM_ANTSEL_RX = 25,
+       PRISM2_PARAM_MONITOR_TYPE = 26,
+       PRISM2_PARAM_WDS_TYPE = 27,
+       PRISM2_PARAM_HOSTSCAN = 28,
+       PRISM2_PARAM_AP_SCAN = 29,
+       PRISM2_PARAM_ENH_SEC = 30,
+       PRISM2_PARAM_IO_DEBUG = 31,
+       PRISM2_PARAM_BASIC_RATES = 32,
+       PRISM2_PARAM_OPER_RATES = 33,
+       PRISM2_PARAM_HOSTAPD = 34,
+       PRISM2_PARAM_HOSTAPD_STA = 35,
+       PRISM2_PARAM_WPA = 36,
+       PRISM2_PARAM_PRIVACY_INVOKED = 37,
+       PRISM2_PARAM_TKIP_COUNTERMEASURES = 38,
+       PRISM2_PARAM_DROP_UNENCRYPTED = 39,
+       PRISM2_PARAM_SCAN_CHANNEL_MASK = 40,
+};
+
+enum { HOSTAP_ANTSEL_DO_NOT_TOUCH = 0, HOSTAP_ANTSEL_DIVERSITY = 1,
+       HOSTAP_ANTSEL_LOW = 2, HOSTAP_ANTSEL_HIGH = 3 };
+
+
+/* PRISM2_IOCTL_MACCMD ioctl() subcommands: */
+enum { AP_MAC_CMD_POLICY_OPEN = 0, AP_MAC_CMD_POLICY_ALLOW = 1,
+       AP_MAC_CMD_POLICY_DENY = 2, AP_MAC_CMD_FLUSH = 3,
+       AP_MAC_CMD_KICKALL = 4 };
+
+
+/* PRISM2_IOCTL_DOWNLOAD ioctl() dl_cmd: */
+enum {
+       PRISM2_DOWNLOAD_VOLATILE = 1 /* RAM */,
+       /* Note! Old versions of prism2_srec have a fatal error in CRC-16
+        * calculation, which will corrupt all non-volatile downloads.
+        * PRISM2_DOWNLOAD_NON_VOLATILE used to be 2, but it is now 3 to
+        * prevent use of old versions of prism2_srec for non-volatile
+        * download. */
+       PRISM2_DOWNLOAD_NON_VOLATILE = 3 /* FLASH */,
+       PRISM2_DOWNLOAD_VOLATILE_GENESIS = 4 /* RAM in Genesis mode */,
+       /* Persistent versions of volatile download commands (keep firmware
+        * data in memory and automatically re-download after hw_reset */
+       PRISM2_DOWNLOAD_VOLATILE_PERSISTENT = 5,
+       PRISM2_DOWNLOAD_VOLATILE_GENESIS_PERSISTENT = 6,
+};
+
+struct prism2_download_param {
+       u32 dl_cmd;
+       u32 start_addr;
+       u32 num_areas;
+       struct prism2_download_area {
+               u32 addr; /* wlan card address */
+               u32 len;
+               void __user *ptr; /* pointer to data in user space */
+       } data[0];
+};
+
+#define PRISM2_MAX_DOWNLOAD_AREA_LEN 131072
+#define PRISM2_MAX_DOWNLOAD_LEN 262144
+
+
+/* PRISM2_IOCTL_HOSTAPD ioctl() cmd: */
+enum {
+       PRISM2_HOSTAPD_FLUSH = 1,
+       PRISM2_HOSTAPD_ADD_STA = 2,
+       PRISM2_HOSTAPD_REMOVE_STA = 3,
+       PRISM2_HOSTAPD_GET_INFO_STA = 4,
+       /* REMOVED: PRISM2_HOSTAPD_RESET_TXEXC_STA = 5, */
+       PRISM2_SET_ENCRYPTION = 6,
+       PRISM2_GET_ENCRYPTION = 7,
+       PRISM2_HOSTAPD_SET_FLAGS_STA = 8,
+       PRISM2_HOSTAPD_GET_RID = 9,
+       PRISM2_HOSTAPD_SET_RID = 10,
+       PRISM2_HOSTAPD_SET_ASSOC_AP_ADDR = 11,
+       PRISM2_HOSTAPD_SET_GENERIC_ELEMENT = 12,
+       PRISM2_HOSTAPD_MLME = 13,
+       PRISM2_HOSTAPD_SCAN_REQ = 14,
+       PRISM2_HOSTAPD_STA_CLEAR_STATS = 15,
+};
+
+#define PRISM2_HOSTAPD_MAX_BUF_SIZE 1024
+#define PRISM2_HOSTAPD_RID_HDR_LEN \
+((int) (&((struct prism2_hostapd_param *) 0)->u.rid.data))
+#define PRISM2_HOSTAPD_GENERIC_ELEMENT_HDR_LEN \
+((int) (&((struct prism2_hostapd_param *) 0)->u.generic_elem.data))
+
+/* Maximum length for algorithm names (-1 for nul termination) used in ioctl()
+ */
+#define HOSTAP_CRYPT_ALG_NAME_LEN 16
+
+
+struct prism2_hostapd_param {
+       u32 cmd;
+       u8 sta_addr[ETH_ALEN];
+       union {
+               struct {
+                       u16 aid;
+                       u16 capability;
+                       u8 tx_supp_rates;
+               } add_sta;
+               struct {
+                       u32 inactive_sec;
+               } get_info_sta;
+               struct {
+                       u8 alg[HOSTAP_CRYPT_ALG_NAME_LEN];
+                       u32 flags;
+                       u32 err;
+                       u8 idx;
+                       u8 seq[8]; /* sequence counter (set: RX, get: TX) */
+                       u16 key_len;
+                       u8 key[0];
+               } crypt;
+               struct {
+                       u32 flags_and;
+                       u32 flags_or;
+               } set_flags_sta;
+               struct {
+                       u16 rid;
+                       u16 len;
+                       u8 data[0];
+               } rid;
+               struct {
+                       u8 len;
+                       u8 data[0];
+               } generic_elem;
+               struct {
+#define MLME_STA_DEAUTH 0
+#define MLME_STA_DISASSOC 1
+                       u16 cmd;
+                       u16 reason_code;
+               } mlme;
+               struct {
+                       u8 ssid_len;
+                       u8 ssid[32];
+               } scan_req;
+       } u;
+};
+
+#define HOSTAP_CRYPT_FLAG_SET_TX_KEY BIT(0)
+#define HOSTAP_CRYPT_FLAG_PERMANENT BIT(1)
+
+#define HOSTAP_CRYPT_ERR_UNKNOWN_ALG 2
+#define HOSTAP_CRYPT_ERR_UNKNOWN_ADDR 3
+#define HOSTAP_CRYPT_ERR_CRYPT_INIT_FAILED 4
+#define HOSTAP_CRYPT_ERR_KEY_SET_FAILED 5
+#define HOSTAP_CRYPT_ERR_TX_KEY_SET_FAILED 6
+#define HOSTAP_CRYPT_ERR_CARD_CONF_FAILED 7
+
+
+#endif /* HOSTAP_COMMON_H */
diff --git a/drivers/net/wireless/hostap/hostap_config.h b/drivers/net/wireless/hostap/hostap_config.h
new file mode 100644 (file)
index 0000000..174068b
--- /dev/null
@@ -0,0 +1,55 @@
+#ifndef HOSTAP_CONFIG_H
+#define HOSTAP_CONFIG_H
+
+#define PRISM2_VERSION "0.4.1-kernel"
+
+/* In the previous versions of Host AP driver, support for user space version
+ * of IEEE 802.11 management (hostapd) used to be disabled in the default
+ * configuration. From now on, support for hostapd is always included and it is
+ * possible to disable kernel driver version of IEEE 802.11 management with a
+ * separate define, PRISM2_NO_KERNEL_IEEE80211_MGMT. */
+/* #define PRISM2_NO_KERNEL_IEEE80211_MGMT */
+
+/* Maximum number of events handler per one interrupt */
+#define PRISM2_MAX_INTERRUPT_EVENTS 20
+
+/* Include code for downloading firmware images into volatile RAM. */
+#define PRISM2_DOWNLOAD_SUPPORT
+
+/* Allow kernel configuration to enable download support. */
+#if !defined(PRISM2_DOWNLOAD_SUPPORT) && defined(CONFIG_HOSTAP_FIRMWARE)
+#define PRISM2_DOWNLOAD_SUPPORT
+#endif
+
+#ifdef PRISM2_DOWNLOAD_SUPPORT
+/* Allow writing firmware images into flash, i.e., to non-volatile storage.
+ * Before you enable this option, you should make absolutely sure that you are
+ * using prism2_srec utility that comes with THIS version of the driver!
+ * In addition, please note that it is possible to kill your card with
+ * non-volatile download if you are using incorrect image. This feature has not
+ * been fully tested, so please be careful with it. */
+/* #define PRISM2_NON_VOLATILE_DOWNLOAD */
+#endif /* PRISM2_DOWNLOAD_SUPPORT */
+
+/* Save low-level I/O for debugging. This should not be enabled in normal use.
+ */
+/* #define PRISM2_IO_DEBUG */
+
+/* Following defines can be used to remove unneeded parts of the driver, e.g.,
+ * to limit the size of the kernel module. Definitions can be added here in
+ * hostap_config.h or they can be added to make command with EXTRA_CFLAGS,
+ * e.g.,
+ * 'make pccard EXTRA_CFLAGS="-DPRISM2_NO_DEBUG -DPRISM2_NO_PROCFS_DEBUG"'
+ */
+
+/* Do not include debug messages into the driver */
+/* #define PRISM2_NO_DEBUG */
+
+/* Do not include /proc/net/prism2/wlan#/{registers,debug} */
+/* #define PRISM2_NO_PROCFS_DEBUG */
+
+/* Do not include station functionality (i.e., allow only Master (Host AP) mode
+ */
+/* #define PRISM2_NO_STATION_MODES */
+
+#endif /* HOSTAP_CONFIG_H */
diff --git a/drivers/net/wireless/hostap/hostap_cs.c b/drivers/net/wireless/hostap/hostap_cs.c
new file mode 100644 (file)
index 0000000..7024245
--- /dev/null
@@ -0,0 +1,1030 @@
+#define PRISM2_PCCARD
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/if.h>
+#include <linux/wait.h>
+#include <linux/timer.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/workqueue.h>
+#include <linux/wireless.h>
+#include <net/iw_handler.h>
+
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/cisreg.h>
+#include <pcmcia/ds.h>
+
+#include <asm/io.h>
+
+#include "hostap_wlan.h"
+
+
+static char *version = PRISM2_VERSION " (Jouni Malinen <jkmaline@cc.hut.fi>)";
+static dev_info_t dev_info = "hostap_cs";
+static dev_link_t *dev_list = NULL;
+
+MODULE_AUTHOR("Jouni Malinen");
+MODULE_DESCRIPTION("Support for Intersil Prism2-based 802.11 wireless LAN "
+                  "cards (PC Card).");
+MODULE_SUPPORTED_DEVICE("Intersil Prism2-based WLAN cards (PC Card)");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(PRISM2_VERSION);
+
+
+static int ignore_cis_vcc;
+module_param(ignore_cis_vcc, int, 0444);
+MODULE_PARM_DESC(ignore_cis_vcc, "Ignore broken CIS VCC entry");
+
+
+/* struct local_info::hw_priv */
+struct hostap_cs_priv {
+       dev_node_t node;
+       dev_link_t *link;
+       int sandisk_connectplus;
+};
+
+
+#ifdef PRISM2_IO_DEBUG
+
+static inline void hfa384x_outb_debug(struct net_device *dev, int a, u8 v)
+{
+       struct hostap_interface *iface;
+       local_info_t *local;
+       unsigned long flags;
+
+       iface = netdev_priv(dev);
+       local = iface->local;
+       spin_lock_irqsave(&local->lock, flags);
+       prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_OUTB, a, v);
+       outb(v, dev->base_addr + a);
+       spin_unlock_irqrestore(&local->lock, flags);
+}
+
+static inline u8 hfa384x_inb_debug(struct net_device *dev, int a)
+{
+       struct hostap_interface *iface;
+       local_info_t *local;
+       unsigned long flags;
+       u8 v;
+
+       iface = netdev_priv(dev);
+       local = iface->local;
+       spin_lock_irqsave(&local->lock, flags);
+       v = inb(dev->base_addr + a);
+       prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INB, a, v);
+       spin_unlock_irqrestore(&local->lock, flags);
+       return v;
+}
+
+static inline void hfa384x_outw_debug(struct net_device *dev, int a, u16 v)
+{
+       struct hostap_interface *iface;
+       local_info_t *local;
+       unsigned long flags;
+
+       iface = netdev_priv(dev);
+       local = iface->local;
+       spin_lock_irqsave(&local->lock, flags);
+       prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_OUTW, a, v);
+       outw(v, dev->base_addr + a);
+       spin_unlock_irqrestore(&local->lock, flags);
+}
+
+static inline u16 hfa384x_inw_debug(struct net_device *dev, int a)
+{
+       struct hostap_interface *iface;
+       local_info_t *local;
+       unsigned long flags;
+       u16 v;
+
+       iface = netdev_priv(dev);
+       local = iface->local;
+       spin_lock_irqsave(&local->lock, flags);
+       v = inw(dev->base_addr + a);
+       prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INW, a, v);
+       spin_unlock_irqrestore(&local->lock, flags);
+       return v;
+}
+
+static inline void hfa384x_outsw_debug(struct net_device *dev, int a,
+                                      u8 *buf, int wc)
+{
+       struct hostap_interface *iface;
+       local_info_t *local;
+       unsigned long flags;
+
+       iface = netdev_priv(dev);
+       local = iface->local;
+       spin_lock_irqsave(&local->lock, flags);
+       prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_OUTSW, a, wc);
+       outsw(dev->base_addr + a, buf, wc);
+       spin_unlock_irqrestore(&local->lock, flags);
+}
+
+static inline void hfa384x_insw_debug(struct net_device *dev, int a,
+                                     u8 *buf, int wc)
+{
+       struct hostap_interface *iface;
+       local_info_t *local;
+       unsigned long flags;
+
+       iface = netdev_priv(dev);
+       local = iface->local;
+       spin_lock_irqsave(&local->lock, flags);
+       prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INSW, a, wc);
+       insw(dev->base_addr + a, buf, wc);
+       spin_unlock_irqrestore(&local->lock, flags);
+}
+
+#define HFA384X_OUTB(v,a) hfa384x_outb_debug(dev, (a), (v))
+#define HFA384X_INB(a) hfa384x_inb_debug(dev, (a))
+#define HFA384X_OUTW(v,a) hfa384x_outw_debug(dev, (a), (v))
+#define HFA384X_INW(a) hfa384x_inw_debug(dev, (a))
+#define HFA384X_OUTSW(a, buf, wc) hfa384x_outsw_debug(dev, (a), (buf), (wc))
+#define HFA384X_INSW(a, buf, wc) hfa384x_insw_debug(dev, (a), (buf), (wc))
+
+#else /* PRISM2_IO_DEBUG */
+
+#define HFA384X_OUTB(v,a) outb((v), dev->base_addr + (a))
+#define HFA384X_INB(a) inb(dev->base_addr + (a))
+#define HFA384X_OUTW(v,a) outw((v), dev->base_addr + (a))
+#define HFA384X_INW(a) inw(dev->base_addr + (a))
+#define HFA384X_INSW(a, buf, wc) insw(dev->base_addr + (a), buf, wc)
+#define HFA384X_OUTSW(a, buf, wc) outsw(dev->base_addr + (a), buf, wc)
+
+#endif /* PRISM2_IO_DEBUG */
+
+
+static int hfa384x_from_bap(struct net_device *dev, u16 bap, void *buf,
+                           int len)
+{
+       u16 d_off;
+       u16 *pos;
+
+       d_off = (bap == 1) ? HFA384X_DATA1_OFF : HFA384X_DATA0_OFF;
+       pos = (u16 *) buf;
+
+       if (len / 2)
+               HFA384X_INSW(d_off, buf, len / 2);
+       pos += len / 2;
+
+       if (len & 1)
+               *((char *) pos) = HFA384X_INB(d_off);
+
+       return 0;
+}
+
+
+static int hfa384x_to_bap(struct net_device *dev, u16 bap, void *buf, int len)
+{
+       u16 d_off;
+       u16 *pos;
+
+       d_off = (bap == 1) ? HFA384X_DATA1_OFF : HFA384X_DATA0_OFF;
+       pos = (u16 *) buf;
+
+       if (len / 2)
+               HFA384X_OUTSW(d_off, buf, len / 2);
+       pos += len / 2;
+
+       if (len & 1)
+               HFA384X_OUTB(*((char *) pos), d_off);
+
+       return 0;
+}
+
+
+/* FIX: This might change at some point.. */
+#include "hostap_hw.c"
+
+
+
+static void prism2_detach(dev_link_t *link);
+static void prism2_release(u_long arg);
+static int prism2_event(event_t event, int priority,
+                       event_callback_args_t *args);
+
+
+static int prism2_pccard_card_present(local_info_t *local)
+{
+       struct hostap_cs_priv *hw_priv = local->hw_priv;
+       if (hw_priv->link != NULL &&
+           ((hw_priv->link->state & (DEV_PRESENT | DEV_CONFIG)) ==
+            (DEV_PRESENT | DEV_CONFIG)))
+               return 1;
+       return 0;
+}
+
+
+/*
+ * SanDisk CompactFlash WLAN Flashcard - Product Manual v1.0
+ * Document No. 20-10-00058, January 2004
+ * http://www.sandisk.com/pdf/industrial/ProdManualCFWLANv1.0.pdf
+ */
+#define SANDISK_WLAN_ACTIVATION_OFF 0x40
+#define SANDISK_HCR_OFF 0x42
+
+
+static void sandisk_set_iobase(local_info_t *local)
+{
+       int res;
+       conf_reg_t reg;
+       struct hostap_cs_priv *hw_priv = local->hw_priv;
+
+       reg.Function = 0;
+       reg.Action = CS_WRITE;
+       reg.Offset = 0x10; /* 0x3f0 IO base 1 */
+       reg.Value = hw_priv->link->io.BasePort1 & 0x00ff;
+       res = pcmcia_access_configuration_register(hw_priv->link->handle,
+                                                  &reg);
+       if (res != CS_SUCCESS) {
+               printk(KERN_DEBUG "Prism3 SanDisk - failed to set I/O base 0 -"
+                      " res=%d\n", res);
+       }
+       udelay(10);
+
+       reg.Function = 0;
+       reg.Action = CS_WRITE;
+       reg.Offset = 0x12; /* 0x3f2 IO base 2 */
+       reg.Value = (hw_priv->link->io.BasePort1 & 0xff00) >> 8;
+       res = pcmcia_access_configuration_register(hw_priv->link->handle,
+                                                  &reg);
+       if (res != CS_SUCCESS) {
+               printk(KERN_DEBUG "Prism3 SanDisk - failed to set I/O base 1 -"
+                      " res=%d\n", res);
+       }
+}
+
+
+static void sandisk_write_hcr(local_info_t *local, int hcr)
+{
+       struct net_device *dev = local->dev;
+       int i;
+
+       HFA384X_OUTB(0x80, SANDISK_WLAN_ACTIVATION_OFF);
+       udelay(50);
+       for (i = 0; i < 10; i++) {
+               HFA384X_OUTB(hcr, SANDISK_HCR_OFF);
+       }
+       udelay(55);
+       HFA384X_OUTB(0x45, SANDISK_WLAN_ACTIVATION_OFF);
+}
+
+
+static int sandisk_enable_wireless(struct net_device *dev)
+{
+       int res, ret = 0;
+       conf_reg_t reg;
+       struct hostap_interface *iface = dev->priv;
+       local_info_t *local = iface->local;
+       tuple_t tuple;
+       cisparse_t *parse = NULL;
+       u_char buf[64];
+       struct hostap_cs_priv *hw_priv = local->hw_priv;
+
+       if (hw_priv->link->io.NumPorts1 < 0x42) {
+               /* Not enough ports to be SanDisk multi-function card */
+               ret = -ENODEV;
+               goto done;
+       }
+
+       parse = kmalloc(sizeof(cisparse_t), GFP_KERNEL);
+       if (parse == NULL) {
+               ret = -ENOMEM;
+               goto done;
+       }
+
+       tuple.DesiredTuple = CISTPL_MANFID;
+       tuple.Attributes = TUPLE_RETURN_COMMON;
+       tuple.TupleData = buf;
+       tuple.TupleDataMax = sizeof(buf);
+       tuple.TupleOffset = 0;
+       if (pcmcia_get_first_tuple(hw_priv->link->handle, &tuple) ||
+           pcmcia_get_tuple_data(hw_priv->link->handle, &tuple) ||
+           pcmcia_parse_tuple(hw_priv->link->handle, &tuple, parse) ||
+           parse->manfid.manf != 0xd601 || parse->manfid.card != 0x0101) {
+               /* No SanDisk manfid found */
+               ret = -ENODEV;
+               goto done;
+       }
+
+       tuple.DesiredTuple = CISTPL_LONGLINK_MFC;
+       if (pcmcia_get_first_tuple(hw_priv->link->handle, &tuple) ||
+           pcmcia_get_tuple_data(hw_priv->link->handle, &tuple) ||
+           pcmcia_parse_tuple(hw_priv->link->handle, &tuple, parse) ||
+               parse->longlink_mfc.nfn < 2) {
+               /* No multi-function links found */
+               ret = -ENODEV;
+               goto done;
+       }
+
+       printk(KERN_DEBUG "%s: Multi-function SanDisk ConnectPlus detected"
+              " - using vendor-specific initialization\n", dev->name);
+       hw_priv->sandisk_connectplus = 1;
+
+       reg.Function = 0;
+       reg.Action = CS_WRITE;
+       reg.Offset = CISREG_COR;
+       reg.Value = COR_SOFT_RESET;
+       res = pcmcia_access_configuration_register(hw_priv->link->handle,
+                                                  &reg);
+       if (res != CS_SUCCESS) {
+               printk(KERN_DEBUG "%s: SanDisk - COR sreset failed (%d)\n",
+                      dev->name, res);
+               goto done;
+       }
+       mdelay(5);
+
+       reg.Function = 0;
+       reg.Action = CS_WRITE;
+       reg.Offset = CISREG_COR;
+       /*
+        * Do not enable interrupts here to avoid some bogus events. Interrupts
+        * will be enabled during the first cor_sreset call.
+        */
+       reg.Value = COR_LEVEL_REQ | 0x8 | COR_ADDR_DECODE | COR_FUNC_ENA;
+       res = pcmcia_access_configuration_register(hw_priv->link->handle,
+                                                  &reg);
+       if (res != CS_SUCCESS) {
+               printk(KERN_DEBUG "%s: SanDisk - COR sreset failed (%d)\n",
+                      dev->name, res);
+               goto done;
+       }
+       mdelay(5);
+
+       sandisk_set_iobase(local);
+
+       HFA384X_OUTB(0xc5, SANDISK_WLAN_ACTIVATION_OFF);
+       udelay(10);
+       HFA384X_OUTB(0x4b, SANDISK_WLAN_ACTIVATION_OFF);
+       udelay(10);
+
+done:
+       kfree(parse);
+       return ret;
+}
+
+
+static void prism2_pccard_cor_sreset(local_info_t *local)
+{
+       int res;
+       conf_reg_t reg;
+       struct hostap_cs_priv *hw_priv = local->hw_priv;
+
+       if (!prism2_pccard_card_present(local))
+              return;
+
+       reg.Function = 0;
+       reg.Action = CS_READ;
+       reg.Offset = CISREG_COR;
+       reg.Value = 0;
+       res = pcmcia_access_configuration_register(hw_priv->link->handle,
+                                                  &reg);
+       if (res != CS_SUCCESS) {
+               printk(KERN_DEBUG "prism2_pccard_cor_sreset failed 1 (%d)\n",
+                      res);
+               return;
+       }
+       printk(KERN_DEBUG "prism2_pccard_cor_sreset: original COR %02x\n",
+              reg.Value);
+
+       reg.Action = CS_WRITE;
+       reg.Value |= COR_SOFT_RESET;
+       res = pcmcia_access_configuration_register(hw_priv->link->handle,
+                                                  &reg);
+       if (res != CS_SUCCESS) {
+               printk(KERN_DEBUG "prism2_pccard_cor_sreset failed 2 (%d)\n",
+                      res);
+               return;
+       }
+
+       mdelay(hw_priv->sandisk_connectplus ? 5 : 2);
+
+       reg.Value &= ~COR_SOFT_RESET;
+       if (hw_priv->sandisk_connectplus)
+               reg.Value |= COR_IREQ_ENA;
+       res = pcmcia_access_configuration_register(hw_priv->link->handle,
+                                                  &reg);
+       if (res != CS_SUCCESS) {
+               printk(KERN_DEBUG "prism2_pccard_cor_sreset failed 3 (%d)\n",
+                      res);
+               return;
+       }
+
+       mdelay(hw_priv->sandisk_connectplus ? 5 : 2);
+
+       if (hw_priv->sandisk_connectplus)
+               sandisk_set_iobase(local);
+}
+
+
+static void prism2_pccard_genesis_reset(local_info_t *local, int hcr)
+{
+       int res;
+       conf_reg_t reg;
+       int old_cor;
+       struct hostap_cs_priv *hw_priv = local->hw_priv;
+
+       if (!prism2_pccard_card_present(local))
+              return;
+
+       if (hw_priv->sandisk_connectplus) {
+               sandisk_write_hcr(local, hcr);
+               return;
+       }
+
+       reg.Function = 0;
+       reg.Action = CS_READ;
+       reg.Offset = CISREG_COR;
+       reg.Value = 0;
+       res = pcmcia_access_configuration_register(hw_priv->link->handle,
+                                                  &reg);
+       if (res != CS_SUCCESS) {
+               printk(KERN_DEBUG "prism2_pccard_genesis_sreset failed 1 "
+                      "(%d)\n", res);
+               return;
+       }
+       printk(KERN_DEBUG "prism2_pccard_genesis_sreset: original COR %02x\n",
+              reg.Value);
+       old_cor = reg.Value;
+
+       reg.Action = CS_WRITE;
+       reg.Value |= COR_SOFT_RESET;
+       res = pcmcia_access_configuration_register(hw_priv->link->handle,
+                                                  &reg);
+       if (res != CS_SUCCESS) {
+               printk(KERN_DEBUG "prism2_pccard_genesis_sreset failed 2 "
+                      "(%d)\n", res);
+               return;
+       }
+
+       mdelay(10);
+
+       /* Setup Genesis mode */
+       reg.Action = CS_WRITE;
+       reg.Value = hcr;
+       reg.Offset = CISREG_CCSR;
+       res = pcmcia_access_configuration_register(hw_priv->link->handle,
+                                                  &reg);
+       if (res != CS_SUCCESS) {
+               printk(KERN_DEBUG "prism2_pccard_genesis_sreset failed 3 "
+                      "(%d)\n", res);
+               return;
+       }
+       mdelay(10);
+
+       reg.Action = CS_WRITE;
+       reg.Offset = CISREG_COR;
+       reg.Value = old_cor & ~COR_SOFT_RESET;
+       res = pcmcia_access_configuration_register(hw_priv->link->handle,
+                                                  &reg);
+       if (res != CS_SUCCESS) {
+               printk(KERN_DEBUG "prism2_pccard_genesis_sreset failed 4 "
+                      "(%d)\n", res);
+               return;
+       }
+
+       mdelay(10);
+}
+
+
+static int prism2_pccard_dev_open(local_info_t *local)
+{
+       struct hostap_cs_priv *hw_priv = local->hw_priv;
+       hw_priv->link->open++;
+       return 0;
+}
+
+
+static int prism2_pccard_dev_close(local_info_t *local)
+{
+       struct hostap_cs_priv *hw_priv;
+
+       if (local == NULL || local->hw_priv == NULL)
+               return 1;
+       hw_priv = local->hw_priv;
+       if (hw_priv->link == NULL)
+               return 1;
+
+       if (!hw_priv->link->open) {
+               printk(KERN_WARNING "%s: prism2_pccard_dev_close(): "
+                      "link not open?!\n", local->dev->name);
+               return 1;
+       }
+
+       hw_priv->link->open--;
+
+       return 0;
+}
+
+
+static struct prism2_helper_functions prism2_pccard_funcs =
+{
+       .card_present   = prism2_pccard_card_present,
+       .cor_sreset     = prism2_pccard_cor_sreset,
+       .dev_open       = prism2_pccard_dev_open,
+       .dev_close      = prism2_pccard_dev_close,
+       .genesis_reset  = prism2_pccard_genesis_reset,
+       .hw_type        = HOSTAP_HW_PCCARD,
+};
+
+
+/* allocate local data and register with CardServices
+ * initialize dev_link structure, but do not configure the card yet */
+static dev_link_t *prism2_attach(void)
+{
+       dev_link_t *link;
+       client_reg_t client_reg;
+       int ret;
+
+       link = kmalloc(sizeof(dev_link_t), GFP_KERNEL);
+       if (link == NULL)
+               return NULL;
+
+       memset(link, 0, sizeof(dev_link_t));
+
+       PDEBUG(DEBUG_HW, "%s: setting Vcc=33 (constant)\n", dev_info);
+       link->conf.Vcc = 33;
+       link->conf.IntType = INT_MEMORY_AND_IO;
+
+       /* register with CardServices */
+       link->next = dev_list;
+       dev_list = link;
+       client_reg.dev_info = &dev_info;
+       client_reg.Version = 0x0210;
+       client_reg.event_callback_args.client_data = link;
+       ret = pcmcia_register_client(&link->handle, &client_reg);
+       if (ret != CS_SUCCESS) {
+               cs_error(link->handle, RegisterClient, ret);
+               prism2_detach(link);
+               return NULL;
+       }
+       return link;
+}
+
+
+static void prism2_detach(dev_link_t *link)
+{
+       dev_link_t **linkp;
+
+       PDEBUG(DEBUG_FLOW, "prism2_detach\n");
+
+       for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
+               if (*linkp == link)
+                       break;
+       if (*linkp == NULL) {
+               printk(KERN_WARNING "%s: Attempt to detach non-existing "
+                      "PCMCIA client\n", dev_info);
+               return;
+       }
+
+       if (link->state & DEV_CONFIG) {
+               prism2_release((u_long)link);
+       }
+
+       if (link->handle) {
+               int res = pcmcia_deregister_client(link->handle);
+               if (res) {
+                       printk("CardService(DeregisterClient) => %d\n", res);
+                       cs_error(link->handle, DeregisterClient, res);
+               }
+       }
+
+       *linkp = link->next;
+       /* release net devices */
+       if (link->priv) {
+               struct net_device *dev;
+               struct hostap_interface *iface;
+               dev = link->priv;
+               iface = netdev_priv(dev);
+               kfree(iface->local->hw_priv);
+               iface->local->hw_priv = NULL;
+               prism2_free_local_data(dev);
+       }
+       kfree(link);
+}
+
+
+#define CS_CHECK(fn, ret) \
+do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
+
+#define CFG_CHECK2(fn, retf) \
+do { int ret = (retf); \
+if (ret != 0) { \
+       PDEBUG(DEBUG_EXTRA, "CardServices(" #fn ") returned %d\n", ret); \
+       cs_error(link->handle, fn, ret); \
+       goto next_entry; \
+} \
+} while (0)
+
+
+/* run after a CARD_INSERTION event is received to configure the PCMCIA
+ * socket and make the device available to the system */
+static int prism2_config(dev_link_t *link)
+{
+       struct net_device *dev;
+       struct hostap_interface *iface;
+       local_info_t *local;
+       int ret = 1;
+       tuple_t tuple;
+       cisparse_t *parse;
+       int last_fn, last_ret;
+       u_char buf[64];
+       config_info_t conf;
+       cistpl_cftable_entry_t dflt = { 0 };
+       struct hostap_cs_priv *hw_priv;
+
+       PDEBUG(DEBUG_FLOW, "prism2_config()\n");
+
+       parse = kmalloc(sizeof(cisparse_t), GFP_KERNEL);
+       hw_priv = kmalloc(sizeof(*hw_priv), GFP_KERNEL);
+       if (parse == NULL || hw_priv == NULL) {
+               kfree(parse);
+               kfree(hw_priv);
+               ret = -ENOMEM;
+               goto failed;
+       }
+       memset(hw_priv, 0, sizeof(*hw_priv));
+
+       tuple.DesiredTuple = CISTPL_CONFIG;
+       tuple.Attributes = 0;
+       tuple.TupleData = buf;
+       tuple.TupleDataMax = sizeof(buf);
+       tuple.TupleOffset = 0;
+       CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link->handle, &tuple));
+       CS_CHECK(GetTupleData, pcmcia_get_tuple_data(link->handle, &tuple));
+       CS_CHECK(ParseTuple, pcmcia_parse_tuple(link->handle, &tuple, parse));
+       link->conf.ConfigBase = parse->config.base;
+       link->conf.Present = parse->config.rmask[0];
+
+       CS_CHECK(GetConfigurationInfo,
+                pcmcia_get_configuration_info(link->handle, &conf));
+       PDEBUG(DEBUG_HW, "%s: %s Vcc=%d (from config)\n", dev_info,
+              ignore_cis_vcc ? "ignoring" : "setting", conf.Vcc);
+       link->conf.Vcc = conf.Vcc;
+
+       /* Look for an appropriate configuration table entry in the CIS */
+       tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
+       CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link->handle, &tuple));
+       for (;;) {
+               cistpl_cftable_entry_t *cfg = &(parse->cftable_entry);
+               CFG_CHECK2(GetTupleData,
+                          pcmcia_get_tuple_data(link->handle, &tuple));
+               CFG_CHECK2(ParseTuple,
+                          pcmcia_parse_tuple(link->handle, &tuple, parse));
+
+               if (cfg->flags & CISTPL_CFTABLE_DEFAULT)
+                       dflt = *cfg;
+               if (cfg->index == 0)
+                       goto next_entry;
+               link->conf.ConfigIndex = cfg->index;
+               PDEBUG(DEBUG_EXTRA, "Checking CFTABLE_ENTRY 0x%02X "
+                      "(default 0x%02X)\n", cfg->index, dflt.index);
+
+               /* Does this card need audio output? */
+               if (cfg->flags & CISTPL_CFTABLE_AUDIO) {
+                       link->conf.Attributes |= CONF_ENABLE_SPKR;
+                       link->conf.Status = CCSR_AUDIO_ENA;
+               }
+
+               /* Use power settings for Vcc and Vpp if present */
+               /*  Note that the CIS values need to be rescaled */
+               if (cfg->vcc.present & (1 << CISTPL_POWER_VNOM)) {
+                       if (conf.Vcc != cfg->vcc.param[CISTPL_POWER_VNOM] /
+                           10000 && !ignore_cis_vcc) {
+                               PDEBUG(DEBUG_EXTRA, "  Vcc mismatch - skipping"
+                                      " this entry\n");
+                               goto next_entry;
+                       }
+               } else if (dflt.vcc.present & (1 << CISTPL_POWER_VNOM)) {
+                       if (conf.Vcc != dflt.vcc.param[CISTPL_POWER_VNOM] /
+                           10000 && !ignore_cis_vcc) {
+                               PDEBUG(DEBUG_EXTRA, "  Vcc (default) mismatch "
+                                      "- skipping this entry\n");
+                               goto next_entry;
+                       }
+               }
+
+               if (cfg->vpp1.present & (1 << CISTPL_POWER_VNOM))
+                       link->conf.Vpp1 = link->conf.Vpp2 =
+                               cfg->vpp1.param[CISTPL_POWER_VNOM] / 10000;