Index: include/linux/packet_anno.h --- include/linux/packet_anno.h.orig 2003-08-12 14:10:54.000000000 -0400 +++ include/linux/packet_anno.h 2003-07-21 02:00:58.000000000 -0400 @@ -0,0 +1,73 @@ +#ifndef PACKET_ANNO_H +#define PACKET_ANNO_H +/* + * Click packet annotations. + */ + +#include + +enum { ADDR_ANNO_SIZE = 16 }; + +enum { USER_ANNO_SIZE = 24, + USER_ANNO_US_SIZE = 12, + USER_ANNO_S_SIZE = 12, + USER_ANNO_U_SIZE = 6, + USER_ANNO_I_SIZE = 6 }; + +// Anno must fit in sk_buff's char cb[48]. +struct Anno { + union { + uint8_t c[ADDR_ANNO_SIZE]; + uint32_t ip4; + } addr; + + union { + uint8_t c[USER_ANNO_SIZE]; + uint16_t us[USER_ANNO_US_SIZE]; + int16_t s[USER_ANNO_S_SIZE]; + uint32_t u[USER_ANNO_U_SIZE]; + int32_t i[USER_ANNO_I_SIZE]; + } user; + uint64_t perfctr; +}; + + +struct Anno *anno(struct sk_buff *skb) {return (struct Anno *) skb->cb; } + + +uint8_t user_anno_c(struct sk_buff *skb, int i) { return anno(skb)->user.c[i]; } +void set_user_anno_c(struct sk_buff *skb, int i, uint8_t v) { anno(skb)->user.c[i] = v; } +uint16_t user_anno_us(struct sk_buff *skb, int i) { return anno(skb)->user.us[i]; } +void set_user_anno_us(struct sk_buff *skb, int i, uint16_t v) { anno(skb)->user.us[i] = v; } +int16_t user_anno_s(struct sk_buff *skb, int i) { return anno(skb)->user.us[i]; } +void set_user_anno_s(struct sk_buff *skb, int i, int16_t v) { anno(skb)->user.s[i] = v; } +uint32_t user_anno_u(struct sk_buff *skb, int i) { return anno(skb)->user.u[i]; } +void set_user_anno_u(struct sk_buff *skb, int i, uint32_t v) { anno(skb)->user.u[i] = v; } +int32_t user_anno_i(struct sk_buff *skb, int i) { return anno(skb)->user.i[i]; } +void set_user_anno_i(struct sk_buff *skb, int i, int32_t v) { anno(skb)->user.i[i] = v; } + + + +#define WIFI_FROM_CLICK(p) (user_anno_c(p, 9) == 0xaa) +#define SET_WIFI_FROM_CLICK(p) (set_user_anno_c(p, 9, (0xaa))) + +#define WIFI_TX_SUCCESS_ANNO(p) (user_anno_c(p, 10)) +#define SET_WIFI_TX_SUCCESS_ANNO(p, v) (set_user_anno_c(p, 10, (v))) + +#define WIFI_TX_POWER_ANNO(p) (user_anno_c(p, 11)) +#define SET_WIFI_TX_POWER_ANNO(p, v) (set_user_anno_c(p, 11, (v))) + +#define WIFI_RATE_ANNO(p) (user_anno_c(p, 12)) +#define SET_WIFI_RATE_ANNO(p, v) (set_user_anno_c(p, 12, (v))) + +#define WIFI_RETRIES_ANNO(p) (user_anno_c(p, 13)) +#define SET_WIFI_RETRIES_ANNO(p, v) (set_user_anno_c(p, 13, (v))) + +#define WIFI_SIGNAL_ANNO(p) (user_anno_c(p, 14)) +#define SET_WIFI_SIGNAL_ANNO(p, v) (set_user_anno_c(p, 14, (v))) + +#define WIFI_NOISE_ANNO(p) (user_anno_c(p, 15)) +#define SET_WIFI_NOISE_ANNO(p, v) (set_user_anno_c(p, 15, (v))) + + +#endif /* PACKET_ANNO_H */ Index: include/linux/click_wifi.h --- include/linux/click_wifi.h.orig 2003-08-12 14:10:32.000000000 -0400 +++ include/linux/click_wifi.h 2003-08-08 22:39:41.000000000 -0400 @@ -0,0 +1,27 @@ +#ifndef CLICK_WIFI_H +#define CLICK_WIFI_H + + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + +/* begin click callbacks */ +typedef int (*tx_ev_cb)(struct sk_buff *, void *arg); +typedef int (*rx_fcserr_cb)(struct sk_buff *, void *arg); + +extern int register_click_wifi_tx_cb (tx_ev_cb cb, void *arg); +extern void click_wifi_tx_ev(struct sk_buff *skb); + +extern int register_click_wifi_rx_fcserr_cb(rx_fcserr_cb cb, void *arg); +extern void click_wifi_rx_fcserr(struct sk_buff *skb); +/* end click callbacks */ + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* CLICK_WIFI_H */ Index: drivers/net/wireless/click_wifi.c --- drivers/net/wireless/click_wifi.c.orig 2003-08-12 14:11:22.000000000 -0400 +++ drivers/net/wireless/click_wifi.c 2003-08-12 15:33:21.000000000 -0400 @@ -0,0 +1,99 @@ +#include +#include +#include /* Not needed ??? */ +#include /* Not needed ??? */ +#include /* off_t */ + +/* support for callbacks for transmission feedback --------------------------------------------- */ + +tx_ev_cb click_cb; +void *click_arg; + +int +register_click_wifi_tx_cb (tx_ev_cb cb, void *arg) +{ + if (cb) { + printk (KERN_WARNING "click_wifi: registering tx callback %ld\n", (long)(cb)); + } else { + printk (KERN_WARNING "click_wifi: unregistering tx callback\n"); + } + + click_cb = cb; + click_arg = arg; + return 0; +} + +void +click_wifi_tx_ev(struct sk_buff *skb) +{ + if (click_cb != NULL) { + click_cb (skb, click_arg); + } else { + dev_kfree_skb(skb); + } +} + + +/* support for callbacks for receiving broken frames --------------------------------------------- */ + +rx_fcserr_cb click_rx_fcserr_cb; +void * click_rx_fcserr_arg; + +int +register_click_wifi_rx_fcserr_cb(rx_fcserr_cb cb, void *arg) +{ + if (cb) { + printk (KERN_WARNING "click_wifi: registering callback for receiving broken frames %ld\n", (long)(cb)); + } else { + printk (KERN_WARNING "click_wifi: unregistering broken frame rx callback\n"); + } + + click_rx_fcserr_cb = cb; + click_rx_fcserr_arg = arg; + return 0; +} + +void click_wifi_rx_fcserr(struct sk_buff *skb) +{ + if (click_rx_fcserr_cb != NULL) { + click_rx_fcserr_cb(skb, click_rx_fcserr_arg); + } else { + dev_kfree_skb(skb); + } +} + +/* init stuff */ + +static int +__init click_wifi_init(void) +{ + click_cb = NULL; + click_arg = NULL; + + click_rx_fcserr_cb = NULL; + click_rx_fcserr_arg = NULL; + + return 0; +} + +static void +__exit click_wifi_exit(void) +{ + click_cb = NULL; + click_arg = NULL; + + click_rx_fcserr_cb = NULL; + click_rx_fcserr_arg = NULL; +} +EXPORT_SYMBOL(click_wifi_tx_ev); +EXPORT_SYMBOL(register_click_wifi_tx_cb); + +EXPORT_SYMBOL(click_wifi_rx_fcserr); +EXPORT_SYMBOL(register_click_wifi_rx_fcserr_cb); + +module_init(click_wifi_init); +module_exit(click_wifi_exit); + +#ifdef MODULE_LICENSE +MODULE_LICENSE("Dual BSD/GPL"); +#endif Index: drivers/net/wireless/airo.h --- drivers/net/wireless/airo.h.orig 2003-08-12 15:07:01.000000000 -0400 +++ drivers/net/wireless/airo.h 2003-06-09 13:30:25.000000000 -0400 @@ -0,0 +1,270 @@ +/* This is a really ugly header file! The objective is to keep airo.c as + * close as possible to what's in the 2.4 kernel. Basically we want to + * support the use of airo.c with the latest pcmcia-cs and the 2.0 and 2.2 + * stable kernels. Basically we end up with ugly hacks to insert code into + * airo.c without having to put ifdefs into airo.c. + */ +#if LINUX_VERSION_CODE < 0x20400 +#define __devinitdata +#define __devinit + +#ifdef CONFIG_PCI +#define PCI_ANY_ID 0 +struct pci_device_id { + unsigned short vendor; + unsigned short id; + unsigned short fill1; + unsigned short fill2; +}; + +struct pci_driver { + char *name; + struct pci_device_id *id_table; + int(*probe)(struct pci_dev *, const struct pci_device_id *); + void(*remove)(struct pci_dev *); +}; +#endif + +#define driver_data sysdata + +#define MODULE_DEVICE_TABLE(x,y) + +#define del_timer_sync(x) del_timer(x) + +#define alloc_etherdev(x) init_etherdev(0, x) +/* init_etherdev does it... */ +#define register_netdev(x) 0 + +/* Before 2.4 this was a void function, so we gotta fake it if we are + compiling with an older kernel. */ +static inline int bogo_request_region(unsigned long x, unsigned long y, + const char *z) { + request_region(x, y, z); + return 1; +} + +#define request_region(x,y,z) bogo_request_region(x,y,z) + +#define SET_MODULE_OWNER(x) + +#define proc_root_driver &proc_root + +static int airo_pci_probe(struct pci_dev *a, const struct pci_device_id *b) +{return 0;} +static void airo_pci_remove(struct pci_dev *c) {} + +#define set_current_state(x) current->state = x + +#if LINUX_VERSION_CODE < 0x20200 +#include +#define get_user_bogus(x,y) ((x) = get_fs_byte(y))&0 +#undef get_user +#define get_user(x,y) get_user_bogus(x,y) +#define pci_module_init(k) (int)k&0; \ +{\ + int j;\ + unsigned char bus, fun;\ +\ + for( j = 0; j < 4; j++ ) { \ + unsigned char irq;\ + unsigned int io;\ + if (pcibios_find_device(card_ids[i].vendor, card_ids[i].id, j, \ + &bus, &fun ) != PCIBIOS_SUCCESSFUL ) \ + break;\ + pcibios_read_config_byte(bus, fun, PCI_INTERRUPT_LINE, &irq);\ + pcibios_read_config_dword(bus, fun, PCI_BASE_ADDRESS_2, &io);\ + io &= PCI_BASE_ADDRESS_IO_MASK;\ + init_airo_card( irq, io, 0 );\ + }\ +} +#define MODULE_PARM_DESC(x,y) + +/* We don't support SMP on 2.0, so we just stub out the spinlocks */ +#define spinlock_t int +#define spin_lock_irqsave(x,y) y=1 +#define spin_unlock_irqrestore(x,y) y=1 +#define SPIN_LOCK_UNLOCKED 1 + +static void schedule_timeout(long to) { + current->timeout = to + jiffies; + schedule(); + current->timeout = 0; +} + +static inline void dev_kfree_skb_bogus(struct sk_buff *skb) { + dev_kfree_skb(skb, FREE_WRITE); +} + +#define dev_kfree_skb(x) dev_kfree_skb_bogus(x) +static int in_interrupt(void) { return 0; } +static inline int timer_pending(struct timer_list *ti) {return (int)ti->next;} + +#define capable(x) suser() + +/* Ah it's magical! There was a big change of the signatures of these + callbacks between 2.0 and 2.2. */ +typedef struct file_operations file_operations_t; +#define file_operations bogus_file_operations +struct file_operations { + file_operations_t real_ops; + struct inode_operations i_ops; + int (*open)( struct inode *inode, struct file *file ); + ssize_t (*read)(struct file *file, char *buffer, + size_t len, loff_t *offset); + ssize_t (*write)(struct file *file, const char *buffer, + size_t len, loff_t *offset ); + int (*release)(struct inode *inode, struct file *file ); +}; + +static int open_redirect( struct inode *inode, struct file *file ) { + return ((struct file_operations*)file->f_op)->open(inode, file); +} +static int read_redirect( struct inode *inode, struct file *file, + char *buffer, int len ) { + return ((struct file_operations*)file->f_op)-> + read(file, buffer, len, &file->f_pos); +} +static int write_redirect( struct inode *inode, struct file *file, + const char *buffer, int len ) { + return ((struct file_operations*)file->f_op)-> + write(file, buffer, len, &file->f_pos); +} +static void release_redirect( struct inode *inode, struct file *file ) { + ((struct file_operations*)file->f_op)->release(inode, file); +} + +static inline int check_match(int len, const char *name, + struct proc_dir_entry *pde) +{ + if (len != pde->namelen) return 0; + return !memcmp(pde->name, name, len); +} + +static struct proc_dir_entry *my_proc_lookup(struct proc_dir_entry *parent, + const char *name) +{ + struct proc_dir_entry *pde = parent->subdir; + int len = strlen(name); + while(pde) { + if (pde->low_ino && check_match(len, name, pde)) return pde; + pde = pde->next; + } + return 0; +} +static void remove_proc_entry(const char *name, struct proc_dir_entry *parent) +{ + struct proc_dir_entry *pde = my_proc_lookup(parent, name); + if (!pde) return; + proc_unregister(parent, pde->low_ino); + kfree(pde); +} + +static struct proc_dir_entry *create_proc_entry(const char *name, mode_t mode, + struct proc_dir_entry *parent) +{ + struct proc_dir_entry *pde = my_proc_lookup(parent, name); + int flen = strlen(name); + if (!pde) { + pde = (struct proc_dir_entry *)kmalloc(sizeof(*pde)+flen+1, + GFP_KERNEL); + if (!pde) return 0; + memset(pde, 0, sizeof(*pde)); + pde->name = (const char*)&pde[1]; + strcpy((char*)pde->name, name); + pde->namelen = flen; + proc_register_dynamic(parent, pde); + } + pde->mode = mode; + pde->nlink = S_ISDIR(mode) ? 2 : 1; + if (S_ISDIR(mode)) { + pde->ops = proc_net.ops; + } + return pde; +} + +static inline int put_user_bogus(char c, char *ptr) { + put_user(c, ptr); + return 0; +} +#include +static inline void mdelay(long x) { udelay(x*1000); } +#undef put_user +#define put_user(x,y) put_user_bogus(x,y) +static void my_setproc_ops(struct proc_dir_entry *pde, + struct file_operations *fops) +{ + memset(&fops->i_ops, 0, sizeof(fops->i_ops)); + pde->ops = &fops->i_ops; + fops->i_ops.default_file_ops = (file_operations_t*)fops; + fops->real_ops.open = open_redirect; + fops->real_ops.read = read_redirect; + fops->real_ops.write = write_redirect; + fops->real_ops.release = release_redirect; +} + +#define SETPROC_OPS(entry, fops) my_setproc_ops(entry, &fops) + +#else +/* Here are the 2.2 specific hacks */ +#define pci_module_init(k) \ + 0; \ + for( i = 0; card_ids[i].vendor; i++ ) { \ + struct pci_dev *dev = 0; \ + while((dev=pci_find_device(card_ids[i].vendor, card_ids[i].id,\ + dev))) { \ + init_airo_card( dev->irq, \ + dev->base_address[2] & \ + PCI_BASE_ADDRESS_IO_MASK, 0 \ + ); \ + } \ + } +#define SETPROC_OPS(entry, fops) (entry)->ops = &(inode_##fops) + +static struct file_operations proc_statsdelta_ops; +static struct inode_operations inode_proc_statsdelta_ops = { + &proc_statsdelta_ops}; + +static struct file_operations proc_stats_ops; +static struct inode_operations inode_proc_stats_ops = { + &proc_stats_ops}; + +static struct file_operations proc_status_ops; +static struct inode_operations inode_proc_status_ops = { + &proc_status_ops}; + +static struct file_operations proc_SSID_ops; +static struct inode_operations inode_proc_SSID_ops = { + &proc_SSID_ops}; + +static struct file_operations proc_APList_ops; +static struct inode_operations inode_proc_APList_ops = { + &proc_APList_ops}; + +static struct file_operations proc_BSSList_ops; +static struct inode_operations inode_proc_BSSList_ops = { + &proc_BSSList_ops}; + +static struct file_operations proc_config_ops; +static struct inode_operations inode_proc_config_ops = { + &proc_config_ops}; + +static struct file_operations proc_wepkey_ops; +static struct inode_operations inode_proc_wepkey_ops = { + &proc_wepkey_ops}; +#endif +#endif + +#ifndef ARPHRD_IEEE80211 +#define ARPHRD_IEEE80211 801 +#endif + +#if LINUX_VERSION_CODE <= 0x20409 +#ifdef min +#undef min +#endif +#define min(x,y) (((x)<(y))?x:y) +#endif + +#ifndef MODULE_LICENSE +#define MODULE_LICENSE(x) +#endif Index: drivers/net/wireless/airo.c --- drivers/net/wireless/airo.c.orig 2003-08-12 14:28:03.000000000 -0400 +++ drivers/net/wireless/airo.c 2003-08-12 15:40:51.000000000 -0400 @@ -44,7 +44,12 @@ #include #include #include +#include +#include #include +#include "airo.h" + +#define WEXT_USECHANNELS /* grid */ #ifdef CONFIG_PCI static struct pci_device_id card_ids[] __devinitdata = { @@ -71,6 +76,11 @@ /* Include Wireless Extension definition and check version - Jean II */ #include #define WIRELESS_SPY // enable iwspy support +#if WIRELESS_EXT < 9 +#warning "Wireless extension v9 or newer required - please upgrade your kernel" +#undef WIRELESS_EXT +#undef WIRELESS_SPY +#endif #if WIRELESS_EXT > 12 #include // New driver API #endif /* WIRELESS_EXT > 12 */ @@ -86,7 +96,7 @@ aes.h, aestab.h and mic.h files from the CVS at http://sf.net/projects/airo-linux/ Put the files in the same directory as airo.c and compile normally */ -#undef MICSUPPORT +/* #define MICSUPPORT */ /* Hack to do some power saving */ #define POWER_ON_DOWN @@ -635,7 +645,7 @@ u8 mac[ETH_ALEN]; u16 mode; u16 errorCode; - u16 sigQuality; + u16 curSignalStrength; u16 SSIDlen; char SSID[32]; char apName[16]; @@ -654,9 +664,38 @@ u16 currentXmitRate; u16 apDevExtensions; u16 normalizedSignalStrength; - u16 _reserved1; + u16 shortPreamble; u8 apIP[4]; - u16 _reserved[7]; + u8 noisePercent; /* Noise percent in last second */ + u8 noisedBm; /* Noise dBm in last second */ + u8 noiseAvePercent; /* Noise percent in last minute */ + u8 noiseAvedBm; /* Noise dBm in last minute */ + u8 noiseMaxPercent; /* Highest noise percent in last minute */ + u8 noiseMaxdBm; /* Highest noise dbm in last minute */ + u16 load; + u8 carrier[4]; + u16 assocStatus; +#define STAT_NOPACKETS 0 +#define STAT_NOCARRIERSET 10 +#define STAT_GOTCARRIERSET 11 +#define STAT_WRONGSSID 20 +#define STAT_BADCHANNEL 25 +#define STAT_BADBITRATES 30 +#define STAT_BADPRIVACY 35 +#define STAT_APFOUND 40 +#define STAT_APREJECTED 50 +#define STAT_AUTHENTICATING 60 +#define STAT_DEAUTHENTICATED 61 +#define STAT_AUTHTIMEOUT 62 +#define STAT_ASSOCIATING 70 +#define STAT_DEASSOCIATED 71 +#define STAT_ASSOCTIMEOUT 72 +#define STAT_NOTAIROAP 73 +#define STAT_ASSOCIATED 80 +#define STAT_LEAPING 90 +#define STAT_LEAPFAILED 91 +#define STAT_LEAPTIMEDOUT 92 +#define STAT_LEAPCOMPLETE 93 } StatusRid; typedef struct { @@ -921,7 +960,7 @@ #endif /* WIRELESS_EXT > 12 */ #endif /* WIRELESS_EXT */ -static const char version[] = "airo.c 0.6 (Ben Reed & Javier Achirica)"; +static const char version[] = "$Revision: 1.1 $"; struct airo_info; @@ -929,8 +968,8 @@ static void OUT4500( struct airo_info *, u16 register, u16 value ); static unsigned short IN4500( struct airo_info *, u16 register ); static u16 setup_card(struct airo_info*, u8 *mac); -static int enable_MAC( struct airo_info *ai, Resp *rsp ); -static void disable_MAC(struct airo_info *ai); +static int enable_MAC( struct airo_info *ai, Resp *rsp, int lock ); +static void disable_MAC(struct airo_info *ai, int lock); static void enable_interrupts(struct airo_info*); static void disable_interrupts(struct airo_info*); static u16 issuecommand(struct airo_info*, Cmd *pCmd, Resp *pRsp); @@ -944,11 +983,11 @@ static int bap_write(struct airo_info*, const u16 *pu16Src, int bytelen, int whichbap); static int PC4500_accessrid(struct airo_info*, u16 rid, u16 accmd); -static int PC4500_readrid(struct airo_info*, u16 rid, void *pBuf, int len); +static int PC4500_readrid(struct airo_info*, u16 rid, void *pBuf, int len, int lock); static int PC4500_writerid(struct airo_info*, u16 rid, const void - *pBuf, int len); + *pBuf, int len, int lock); static int do_writerid( struct airo_info*, u16 rid, const void *rid_data, - int len ); + int len, int dummy ); static u16 transmit_allocate(struct airo_info*, int lenPayload, int raw); static int transmit_802_3_packet(struct airo_info*, int len, char *pPacket); static int transmit_802_11_packet(struct airo_info*, int len, char *pPacket); @@ -979,6 +1018,7 @@ use the high bit to mark wether it is in use. */ #define MAX_FIDS 6 int fids[MAX_FIDS]; + struct sk_buff *txskb[MAX_FIDS]; int registered; ConfigRid config; int need_commit; // Need to set config @@ -992,7 +1032,6 @@ #define FLAG_PROMISC IFF_PROMISC /* 0x100 - include/linux/if.h */ #define FLAG_RADIO_OFF 0x02 /* User disabling of MAC */ #define FLAG_RADIO_DOWN 0x08 /* ifup/ifdown disabling of MAC */ -#define FLAG_LOCKED 2 /* 0x04 - use as a bit offset */ #define FLAG_FLASHING 0x10 #define FLAG_ADHOC 0x01 /* Needed by MIC */ #define FLAG_MIC_CAPABLE 0x20 @@ -1005,6 +1044,7 @@ tdsRssiEntry *rssi; struct semaphore sem; struct task_struct *task; + struct tq_struct stats_task; struct tq_struct promisc_task; struct { struct sk_buff *skb; @@ -1042,6 +1082,8 @@ #include "mic.h" #endif + + static int readBSSListRid(struct airo_info *ai, int first, BSSListRid *list) { int rc; @@ -1062,7 +1104,7 @@ ai->task = NULL; } rc = PC4500_readrid(ai, first ? RID_BSSLISTFIRST : RID_BSSLISTNEXT, - list, sizeof(*list)); + list, sizeof(*list), 1); list->len = le16_to_cpu(list->len); list->index = le16_to_cpu(list->index); @@ -1077,7 +1119,7 @@ static int readWepKeyRid(struct airo_info*ai, WepKeyRid *wkr, int temp) { int rc = PC4500_readrid(ai, temp ? RID_WEP_TEMP : RID_WEP_PERM, - wkr, sizeof(*wkr)); + wkr, sizeof(*wkr), 1); wkr->len = le16_to_cpu(wkr->len); wkr->kindex = le16_to_cpu(wkr->kindex); @@ -1086,17 +1128,17 @@ } /* In the writeXXXRid routines we copy the rids so that we don't screwup * the originals when we endian them... */ -static int writeWepKeyRid(struct airo_info*ai, WepKeyRid *pwkr, int perm) { +static int writeWepKeyRid(struct airo_info*ai, WepKeyRid *pwkr, int perm, int lock) { int rc; WepKeyRid wkr = *pwkr; wkr.len = cpu_to_le16(wkr.len); wkr.kindex = cpu_to_le16(wkr.kindex); wkr.klen = cpu_to_le16(wkr.klen); - rc = PC4500_writerid(ai, RID_WEP_TEMP, &wkr, sizeof(wkr)); + rc = PC4500_writerid(ai, RID_WEP_TEMP, &wkr, sizeof(wkr), lock); if (rc!=SUCCESS) printk(KERN_ERR "airo: WEP_TEMP set %x\n", rc); if (perm) { - rc = PC4500_writerid(ai, RID_WEP_PERM, &wkr, sizeof(wkr)); + rc = PC4500_writerid(ai, RID_WEP_PERM, &wkr, sizeof(wkr), lock); if (rc!=SUCCESS) { printk(KERN_ERR "airo: WEP_PERM set %x\n", rc); } @@ -1106,7 +1148,7 @@ static int readSsidRid(struct airo_info*ai, SsidRid *ssidr) { int i; - int rc = PC4500_readrid(ai, RID_SSID, ssidr, sizeof(*ssidr)); + int rc = PC4500_readrid(ai, RID_SSID, ssidr, sizeof(*ssidr), 1); ssidr->len = le16_to_cpu(ssidr->len); for(i = 0; i < 3; i++) { @@ -1123,10 +1165,10 @@ for(i = 0; i < 3; i++) { ssidr.ssids[i].len = cpu_to_le16(ssidr.ssids[i].len); } - rc = PC4500_writerid(ai, RID_SSID, &ssidr, sizeof(ssidr)); + rc = PC4500_writerid(ai, RID_SSID, &ssidr, sizeof(ssidr), 1); return rc; } -static int readConfigRid(struct airo_info*ai) { +static int readConfigRid(struct airo_info*ai, int lock) { int rc; u16 *s; ConfigRid cfg; @@ -1134,7 +1176,7 @@ if (ai->config.len) return SUCCESS; - rc = PC4500_readrid(ai, RID_ACTUALCONFIG, &cfg, sizeof(cfg)); + rc = PC4500_readrid(ai, RID_ACTUALCONFIG, &cfg, sizeof(cfg), lock); if (rc != SUCCESS) return rc; @@ -1163,7 +1205,7 @@ } } } -static int writeConfigRid(struct airo_info*ai) { +static int writeConfigRid(struct airo_info*ai, int lock) { u16 *s; ConfigRid cfgr; @@ -1190,33 +1232,34 @@ for(s = &cfgr.arlThreshold; s <= &cfgr.autoWake; s++) *s = cpu_to_le16(*s); - return PC4500_writerid( ai, RID_CONFIG, &cfgr, sizeof(cfgr)); + return PC4500_writerid( ai, RID_CONFIG, &cfgr, sizeof(cfgr), lock); } static int readStatusRid(struct airo_info*ai, StatusRid *statr) { - int rc = PC4500_readrid(ai, RID_STATUS, statr, sizeof(*statr)); + int rc = PC4500_readrid(ai, RID_STATUS, statr, sizeof(*statr), 1); u16 *s; statr->len = le16_to_cpu(statr->len); for(s = &statr->mode; s <= &statr->SSIDlen; s++) *s = le16_to_cpu(*s); - for(s = &statr->beaconPeriod; s <= &statr->_reserved[9]; s++) + for(s = &statr->beaconPeriod; s <= &statr->shortPreamble; s++) *s = le16_to_cpu(*s); - + statr->load = le16_to_cpu(statr->load); + statr->assocStatus = le16_to_cpu(statr->assocStatus); return rc; } static int readAPListRid(struct airo_info*ai, APListRid *aplr) { - int rc = PC4500_readrid(ai, RID_APLIST, aplr, sizeof(*aplr)); + int rc = PC4500_readrid(ai, RID_APLIST, aplr, sizeof(*aplr), 1); aplr->len = le16_to_cpu(aplr->len); return rc; } static int writeAPListRid(struct airo_info*ai, APListRid *aplr) { int rc; aplr->len = cpu_to_le16(aplr->len); - rc = PC4500_writerid(ai, RID_APLIST, aplr, sizeof(*aplr)); + rc = PC4500_writerid(ai, RID_APLIST, aplr, sizeof(*aplr), 1); return rc; } static int readCapabilityRid(struct airo_info*ai, CapabilityRid *capr) { - int rc = PC4500_readrid(ai, RID_CAPABILITIES, capr, sizeof(*capr)); + int rc = PC4500_readrid(ai, RID_CAPABILITIES, capr, sizeof(*capr), 1); u16 *s; capr->len = le16_to_cpu(capr->len); @@ -1227,8 +1270,8 @@ *s = le16_to_cpu(*s); return rc; } -static int readStatsRid(struct airo_info*ai, StatsRid *sr, int rid) { - int rc = PC4500_readrid(ai, rid, sr, sizeof(*sr)); +static int readStatsRid(struct airo_info*ai, StatsRid *sr, int rid, int lock) { + int rc = PC4500_readrid(ai, rid, sr, sizeof(*sr), lock); u32 *i; sr->len = le16_to_cpu(sr->len); @@ -1248,8 +1291,8 @@ * is open (to pipeline changes and speed-up card setup). If * those changes are not yet commited, do it now - Jean II */ if(info->need_commit) { - disable_MAC(info); - writeConfigRid(info); + disable_MAC(info, 1); + writeConfigRid(info, 1); } if (info->wifidev != dev) { @@ -1257,14 +1300,19 @@ info->flags &= ~FLAG_RADIO_DOWN; enable_interrupts(info); } - enable_MAC(info, &rsp); + enable_MAC(info, &rsp, 1); netif_start_queue(dev); +#if LINUX_VERSION_CODE < 0x020400 + MOD_INC_USE_COUNT; + netif_mark_up(dev); +#endif return 0; } static void get_tx_error(struct airo_info *ai, u32 fid) { +#if LINUX_VERSION_CODE > 0x20200 u16 status; if (bap_setup(ai, ai->fids[fid] & 0xffff, 4, BAP0) == SUCCESS) { @@ -1310,6 +1358,7 @@ } #endif /* WIRELESS_EXT > 13 */ } +#endif } static void airo_do_xmit(struct net_device *dev) { @@ -1332,17 +1381,24 @@ i = 0; if ( status == SUCCESS ) { - dev->trans_start = jiffies; - for (; i < MAX_FIDS / 2 && (priv->fids[i] & 0xffff0000); i++); + struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC); + priv->txskb[fid] = skb2; + + dev->trans_start = jiffies; + for (; i < MAX_FIDS / 2 && (priv->fids[i] & 0xffff0000); i++); } else { priv->fids[fid] &= 0xffff; +#if LINUX_VERSION_CODE > 0x20200 priv->stats.tx_window_errors++; +#endif } + + dev_kfree_skb(skb); + if (i < MAX_FIDS / 2) netif_wake_queue(dev); else netif_stop_queue(dev); - dev_kfree_skb(skb); } static int airo_start_xmit(struct sk_buff *skb, struct net_device *dev) { @@ -1360,7 +1416,9 @@ for( i = 0; i < MAX_FIDS / 2 && (fids[i] & 0xffff0000); i++ ); if ( i == MAX_FIDS / 2 ) { +#if LINUX_VERSION_CODE > 0x20200 priv->stats.tx_fifo_errors++; +#endif dev_kfree_skb(skb); } else { /* check min length*/ @@ -1394,17 +1452,22 @@ i = MAX_FIDS / 2; if ( status == SUCCESS ) { - dev->trans_start = jiffies; - for (; i < MAX_FIDS && (priv->fids[i] & 0xffff0000); i++); + struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC); + priv->txskb[fid] = skb2; + + dev->trans_start = jiffies; + for (; i < MAX_FIDS && (priv->fids[i] & 0xffff0000); i++); } else { priv->fids[fid] &= 0xffff; priv->stats.tx_window_errors++; } + + dev_kfree_skb(skb); + if (i < MAX_FIDS) netif_wake_queue(dev); else netif_stop_queue(dev); - dev_kfree_skb(skb); } static int airo_start_xmit11(struct sk_buff *skb, struct net_device *dev) { @@ -1436,29 +1499,47 @@ return 0; } -struct net_device_stats *airo_get_stats(struct net_device *dev) -{ - struct airo_info *local = dev->priv; +static void airo_read_stats(struct airo_info *ai) { StatsRid stats_rid; u32 *vals = stats_rid.vals; - /* Get stats out of the card */ - readStatsRid(local, &stats_rid, RID_STATS); + if (down_trylock(&ai->sem) == 0) { + readStatsRid(ai, &stats_rid, RID_STATS, 0); + up(&ai->sem); + + ai->stats.rx_packets = vals[43] + vals[44] + vals[45]; + ai->stats.tx_packets = vals[39] + vals[40] + vals[41]; + ai->stats.rx_bytes = vals[92]; + ai->stats.tx_bytes = vals[91]; + ai->stats.rx_errors = vals[0] + vals[2] + vals[3] + vals[4]; + ai->stats.tx_errors = vals[42] +#if LINUX_VERSION_CODE > 0x20200 + + ai->stats.tx_fifo_errors +#endif + ; + ai->stats.multicast = vals[43]; + ai->stats.collisions = vals[89]; + +#if LINUX_VERSION_CODE > 0x20200 + /* detailed rx_errors: */ + ai->stats.rx_length_errors = vals[3]; + ai->stats.rx_crc_errors = vals[4]; + ai->stats.rx_frame_errors = vals[2]; + ai->stats.rx_fifo_errors = vals[0]; +#endif + } else { + ai->stats_task.routine = (void (*)(void *))airo_read_stats; + ai->stats_task.data = (void *)ai; + schedule_task(&ai->stats_task); + } +} + +struct net_device_stats *airo_get_stats(struct net_device *dev) +{ + struct airo_info *local = dev->priv; - local->stats.rx_packets = vals[43] + vals[44] + vals[45]; - local->stats.tx_packets = vals[39] + vals[40] + vals[41]; - local->stats.rx_bytes = vals[92]; - local->stats.tx_bytes = vals[91]; - local->stats.rx_errors = vals[0] + vals[2] + vals[3] + vals[4]; - local->stats.tx_errors = vals[42] + local->stats.tx_fifo_errors; - local->stats.multicast = vals[43]; - local->stats.collisions = vals[89]; - - /* detailed rx_errors: */ - local->stats.rx_length_errors = vals[3]; - local->stats.rx_crc_errors = vals[4]; - local->stats.rx_frame_errors = vals[2]; - local->stats.rx_fifo_errors = vals[0]; + /* Get stats out of the card if available */ + airo_read_stats(local); return &local->stats; } @@ -1513,9 +1594,9 @@ memcpy (ai->config.macAddr, addr->sa_data, dev->addr_len); ai->need_commit = 1; - disable_MAC(ai); - writeConfigRid (ai); - enable_MAC(ai, &rsp); + disable_MAC(ai, 1); + writeConfigRid (ai, 1); + enable_MAC(ai, &rsp, 1); memcpy (ai->dev->dev_addr, addr->sa_data, dev->addr_len); if (ai->wifidev) memcpy (ai->wifidev->dev_addr, addr->sa_data, dev->addr_len); @@ -1535,6 +1616,10 @@ struct airo_info *ai = dev->priv; netif_stop_queue(dev); +#if LINUX_VERSION_CODE < 0x020400 + MOD_DEC_USE_COUNT; + netif_mark_down(dev); +#endif if (ai->wifidev != dev) { #ifdef POWER_ON_DOWN @@ -1544,7 +1629,7 @@ * stack (i.e. the network stack won't try to broadcast * anything on the interface and routes are gone. Jean II */ ai->flags |= FLAG_RADIO_DOWN; - disable_MAC(ai); + disable_MAC(ai, 1); #endif disable_interrupts( ai ); } @@ -1725,7 +1810,8 @@ ai->wifidev = init_wifidev(ai, dev); ai->registered = 1; - printk( KERN_INFO "airo: MAC enabled %s %x:%x:%x:%x:%x:%x\n", + printk( KERN_INFO "airo: (Grid hacks: MAX_SPY=%d) MAC enabled %s %x:%x:%x:%x:%x:%x\n", + IW_MAX_SPY, dev->name, dev->dev_addr[0], dev->dev_addr[1], dev->dev_addr[2], dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5] ); @@ -1768,6 +1854,9 @@ int i; struct airo_info *ai = dev->priv; + + if (down_interruptible(&ai->sem)) + return -1; waitbusy (ai); OUT4500(ai,COMMAND,CMD_SOFTRESET); set_current_state (TASK_UNINTERRUPTIBLE); @@ -1777,9 +1866,10 @@ schedule_timeout (HZ/5); if ( setup_card(ai, dev->dev_addr ) != SUCCESS ) { printk( KERN_ERR "airo: MAC could not be enabled\n" ); + up(&ai->sem); return -1; } else { - printk( KERN_INFO "airo: MAC enabled %s %x:%x:%x:%x:%x:%x\n", + printk( KERN_INFO "airo: MAC enabled (Grid hacks) %s %x:%x:%x:%x:%x:%x\n", dev->name, dev->dev_addr[0], dev->dev_addr[1], @@ -1794,6 +1884,7 @@ } enable_interrupts( ai ); netif_wake_queue(dev); + up(&ai->sem); return 0; } @@ -1806,9 +1897,7 @@ StatusRid status_rid; if (down_trylock(&ai->sem) == 0) { - __set_bit(FLAG_LOCKED, &ai->flags); - PC4500_readrid(ai, RID_STATUS, &status_rid, sizeof(status_rid)); - clear_bit(FLAG_LOCKED, &ai->flags); + PC4500_readrid(ai, RID_STATUS, &status_rid, sizeof(status_rid), 0); up(&ai->sem); wrqu.data.length = 0; wrqu.data.flags = 0; @@ -1829,9 +1918,7 @@ MICRid mic_rid; if (down_trylock(&ai->sem) == 0) { - __set_bit(FLAG_LOCKED, &ai->flags); - PC4500_readrid(ai, RID_MIC, &mic_rid, sizeof(mic_rid)); - clear_bit(FLAG_LOCKED, &ai->flags); + PC4500_readrid(ai, RID_MIC, &mic_rid, sizeof(mic_rid), 0); up(&ai->sem); #ifdef MICSUPPORT micinit (ai, &mic_rid); @@ -1869,7 +1956,8 @@ if ( status & EV_MIC ) { OUT4500( apriv, EVACK, EV_MIC ); - airo_read_mic( apriv ); + if (apriv->flags & FLAG_MIC_CAPABLE) + airo_read_mic( apriv ); } if ( status & EV_LINK ) { #if WIRELESS_EXT > 13 @@ -1966,7 +2054,7 @@ #pragma pack() u16 gap; u16 tmpbuf[4]; - u16 *buffer; + u16 *buffer = NULL; fid = IN4500( apriv, RXFID ); @@ -2078,7 +2166,7 @@ if (apriv->rssi) apriv->spy_stat[i].level = 0x100 - apriv->rssi[hdr.rssi[1]].rssidBm; else - apriv->spy_stat[i].level = (hdr.rssi[1] + 321) / 2; + apriv->spy_stat[i].level = (hdr.rssi[1]) + 0x100 - 100; apriv->spy_stat[i].noise = 0; apriv->spy_stat[i].updated = 3; break; @@ -2116,17 +2204,41 @@ if ( ( apriv->fids[i] & 0xffff ) == fid ) { len = apriv->fids[i] >> 16; index = i; - /* Set up to be used again */ - apriv->fids[i] &= 0xffff; } } if (index != -1) { - netif_wake_queue(dev); + + { + /* click callbacks */ + char res[16]; + if (bap_setup(apriv, apriv->fids[index] & 0xffff, 0x0, BAP0) == SUCCESS) { + bap_read(apriv, (u16 *)res, 16, BAP0); // get result header including retry counters, etc. + // printk (KERN_WARNING "airo: tx retry counters: %d %d\n", + // le16_to_cpu(ctrs) >> 8, le16_to_cpu(ctrs) & 0xff); + int success = !((unsigned char) res[0x04] & (0x02 || 0x04)); + int rate = ((unsigned char) res[0x08]); + int short_retries = ((unsigned char) res[0x0C]); + //int long_retries = ((unsigned char) res[0x0D]); + struct sk_buff *skbfoo = apriv->txskb[index]; + apriv->txskb[index] = NULL; + + SET_WIFI_TX_SUCCESS_ANNO(skbfoo, success); + SET_WIFI_RATE_ANNO(skbfoo, rate); + SET_WIFI_RETRIES_ANNO(skbfoo, short_retries); + click_wifi_tx_ev(skbfoo); + } + } + +#if LINUX_VERSION_CODE > 0x20200 if (status & EV_TXEXC) get_tx_error(apriv, index); - } - OUT4500( apriv, EVACK, status & (EV_TX | EV_TXEXC)); - if (index==-1) { +#endif + OUT4500( apriv, EVACK, status & (EV_TX | EV_TXEXC)); + /* Set up to be used again */ + apriv->fids[index] &= 0xffff; + netif_wake_queue(dev); + } else { + OUT4500( apriv, EVACK, status & (EV_TX | EV_TXEXC)); printk( KERN_ERR "airo: Unallocated FID was used to xmit\n" ); } } @@ -2172,7 +2284,7 @@ return rc; } -static int enable_MAC( struct airo_info *ai, Resp *rsp ) { +static int enable_MAC( struct airo_info *ai, Resp *rsp, int lock ) { int rc; Cmd cmd; @@ -2185,7 +2297,7 @@ if (ai->flags & (FLAG_RADIO_OFF|FLAG_RADIO_DOWN)) return SUCCESS; memset(&cmd, 0, sizeof(cmd)); cmd.cmd = MAC_ENABLE; - if (test_bit(FLAG_LOCKED, &ai->flags) != 0) + if (!lock) return issuecommand(ai, &cmd, rsp); if (down_interruptible(&ai->sem)) @@ -2195,13 +2307,13 @@ return rc; } -static void disable_MAC( struct airo_info *ai ) { +static void disable_MAC( struct airo_info *ai, int lock ) { Cmd cmd; Resp rsp; memset(&cmd, 0, sizeof(cmd)); cmd.cmd = MAC_DISABLE; // disable in case already enabled - if (test_bit(FLAG_LOCKED, &ai->flags) != 0) { + if (!lock) { issuecommand(ai, &cmd, &rsp); return; } @@ -2279,18 +2391,19 @@ CapabilityRid cap_rid; // general configuration (read/modify/write) - status = readConfigRid(ai); + status = readConfigRid(ai, 1); if ( status != SUCCESS ) return ERROR; status = readCapabilityRid(ai, &cap_rid); if ( status != SUCCESS ) return ERROR; - status = PC4500_readrid(ai,RID_RSSI,&rssi_rid,sizeof(rssi_rid)); + status = PC4500_readrid(ai,RID_RSSI,&rssi_rid,sizeof(rssi_rid),1); if ( status == SUCCESS ) { if (ai->rssi || (ai->rssi = kmalloc(512, GFP_KERNEL)) != NULL) memcpy(ai->rssi, (u8*)&rssi_rid + 2, 512); } else { + printk( KERN_WARNING "airo: unable to read RSSI map\n"); if (ai->rssi) { kfree(ai->rssi); ai->rssi = NULL; @@ -2349,14 +2462,14 @@ } } - status = writeConfigRid(ai); + status = writeConfigRid(ai, 1); if ( status != SUCCESS ) return ERROR; /* Set up the SSID list */ status = writeSsidRid(ai, &mySsid); if ( status != SUCCESS ) return ERROR; - status = enable_MAC(ai, &rsp); + status = enable_MAC(ai, &rsp, 1); if ( status != SUCCESS || (rsp.status & 0xFF00) != 0) { printk( KERN_ERR "airo: Bad MAC enable reason = %x, rid = %x, offset = %d\n", rsp.rsp0, rsp.rsp1, rsp.rsp2 ); return ERROR; @@ -2571,13 +2684,12 @@ /* Note, that we are using BAP1 which is also used by transmit, so * we must get a lock. */ -static int PC4500_readrid(struct airo_info *ai, u16 rid, void *pBuf, int len) +static int PC4500_readrid(struct airo_info *ai, u16 rid, void *pBuf, int len, int lock) { - u16 status, dolock = 0; + u16 status; int rc = SUCCESS; - if (test_bit(FLAG_LOCKED, &ai->flags) == 0) { - dolock = 1; + if (lock) { if (down_interruptible(&ai->sem)) return ERROR; } @@ -2605,7 +2717,7 @@ // read remainder of the rid rc = bap_read(ai, ((u16*)pBuf)+1, len, BAP1); done: - if (dolock) + if (lock) up(&ai->sem); return rc; } @@ -2613,13 +2725,14 @@ /* Note, that we are using BAP1 which is also used by transmit, so * make sure this isnt called when a transmit is happening */ static int PC4500_writerid(struct airo_info *ai, u16 rid, - const void *pBuf, int len) + const void *pBuf, int len, int lock) { - u16 status, dolock = 0; + u16 status; int rc = SUCCESS; - if (test_bit(FLAG_LOCKED, &ai->flags) == 0) { - dolock = 1; + *(u16*)pBuf = cpu_to_le16((u16)len); + + if (lock) { if (down_interruptible(&ai->sem)) return ERROR; } @@ -2637,7 +2750,7 @@ // ---now commit the rid data rc = PC4500_accessrid(ai, rid, 0x100|CMD_ACCESS); done: - if (dolock) + if (lock) up(&ai->sem); return rc; } @@ -2656,11 +2769,11 @@ if (down_interruptible(&ai->sem)) return ERROR; if (issuecommand(ai, &cmd, &rsp) != SUCCESS) { - txFid = 0; + txFid = ERROR; goto done; } if ( (rsp.status & 0xFF00) != 0) { - txFid = 0; + txFid = ERROR; goto done; } /* wait for the allocate event/indication @@ -2707,7 +2820,7 @@ len >>= 16; - if (len < ETH_ALEN * 2) { + if (len <= ETH_ALEN * 2) { printk( KERN_WARNING "Short packet %d\n", len ); return ERROR; } @@ -3094,7 +3207,7 @@ "Signal Quality: %d\n" "SSID: %-.*s\n" "AP: %-.16s\n" - "Freq: %d\n" + "Freq: %d (%d)\n" "BitRate: %dmbs\n" "Driver Version: %s\n" "Device: %s\nManufacturer: %s\nFirmware Version: %s\n" @@ -3108,6 +3221,7 @@ status_rid.SSID, status_rid.apName, (int)status_rid.channel, + (int)status_rid.channelSet, (int)status_rid.currentXmitRate/2, version, cap_rid.prodName, @@ -3158,7 +3272,7 @@ return -ENOMEM; } - readStatsRid(apriv, &stats, rid); + readStatsRid(apriv, &stats, rid, 1); j = 0; for(i=0; (int)statsLabels[i]!=-1 && @@ -3204,7 +3318,7 @@ if ( !data->writelen ) return; - readConfigRid(ai); + readConfigRid(ai, 1); line = data->wbuffer; while( line[0] ) { @@ -3390,7 +3504,7 @@ while( line[0] && line[0] != '\n' ) line++; if ( line[0] ) line++; } - disable_MAC(ai); + disable_MAC(ai, 1); if (need_reset) { APListRid APList_rid; SsidRid SSID_rid; @@ -3398,12 +3512,12 @@ readAPListRid(ai, &APList_rid); readSsidRid(ai, &SSID_rid); reset_airo_card(dev); - disable_MAC(ai); + disable_MAC(ai, 1); writeSsidRid(ai, &SSID_rid); writeAPListRid(ai, &APList_rid); } - writeConfigRid(ai); - enable_MAC(ai, &rsp); + writeConfigRid(ai, 1); + enable_MAC(ai, &rsp, 1); if (need_reset) airo_set_promisc(ai); } @@ -3443,7 +3557,7 @@ data->maxwritelen = 2048; data->on_close = proc_config_on_close; - readConfigRid(ai); + readConfigRid(ai, 1); i = sprintf( data->rbuffer, "Mode: %s\n" @@ -3535,9 +3649,9 @@ offset < data->writelen ) offset++; offset++; } - disable_MAC(ai); + disable_MAC(ai, 1); writeSsidRid(ai, &SSID_rid); - enable_MAC(ai, &rsp); + enable_MAC(ai, &rsp, 1); } inline static u8 hexVal(char c) { @@ -3576,20 +3690,20 @@ } } } - disable_MAC(ai); + disable_MAC(ai, 1); writeAPListRid(ai, &APList_rid); - enable_MAC(ai, &rsp); + enable_MAC(ai, &rsp, 1); } /* This function wraps PC4500_writerid with a MAC disable */ static int do_writerid( struct airo_info *ai, u16 rid, const void *rid_data, - int len ) { + int len, int dummy ) { int rc; Resp rsp; - disable_MAC(ai); - rc = PC4500_writerid(ai, rid, rid_data, len); - enable_MAC(ai, &rsp); + disable_MAC(ai, 1); + rc = PC4500_writerid(ai, rid, rid_data, len, 1); + enable_MAC(ai, &rsp, 1); return rc; } @@ -3617,7 +3731,7 @@ } static int set_wep_key(struct airo_info *ai, u16 index, - const char *key, u16 keylen, int perm ) { + const char *key, u16 keylen, int perm, int lock ) { static const unsigned char macaddr[ETH_ALEN] = { 0x01, 0, 0, 0, 0, 0 }; WepKeyRid wkr; @@ -3639,7 +3753,7 @@ printk(KERN_INFO "Setting key %d\n", index); } - writeWepKeyRid(ai, &wkr, perm); + writeWepKeyRid(ai, &wkr, perm, lock); return 0; } @@ -3662,7 +3776,7 @@ (data->wbuffer[1] == ' ' || data->wbuffer[1] == '\n')) { index = data->wbuffer[0] - '0'; if (data->wbuffer[1] == '\n') { - set_wep_key(ai, index, 0, 0, 1); + set_wep_key(ai, index, 0, 0, 1, 1); return; } j = 2; @@ -3681,7 +3795,7 @@ break; } } - set_wep_key(ai, index, key, i/3, 1); + set_wep_key(ai, index, key, i/3, 1, 1); } static int proc_wepkey_open( struct inode *inode, struct file *file ) { @@ -3937,10 +4051,9 @@ add_timer(&apriv->timer); return; } - __set_bit(FLAG_LOCKED, &apriv->flags); - readConfigRid(apriv); - disable_MAC(apriv); + readConfigRid(apriv, 0); + disable_MAC(apriv, 0); switch(apriv->config.authType) { case AUTH_ENCRYPT: /* So drop to OPEN */ @@ -3948,13 +4061,13 @@ break; case AUTH_SHAREDKEY: if (apriv->keyindex < auto_wep) { - set_wep_key(apriv, apriv->keyindex, 0, 0, 0); + set_wep_key(apriv, apriv->keyindex, 0, 0, 0, 0); apriv->config.authType = AUTH_SHAREDKEY; apriv->keyindex++; } else { /* Drop to ENCRYPT */ apriv->keyindex = 0; - set_wep_key(apriv, apriv->defindex, 0, 0, 0); + set_wep_key(apriv, apriv->defindex, 0, 0, 0, 0); apriv->config.authType = AUTH_ENCRYPT; } break; @@ -3962,9 +4075,8 @@ apriv->config.authType = AUTH_SHAREDKEY; } apriv->need_commit = 1; - writeConfigRid(apriv); - enable_MAC(apriv, &rsp); - clear_bit(FLAG_LOCKED, &apriv->flags); + writeConfigRid(apriv, 0); + enable_MAC(apriv, &rsp, 0); up(&apriv->sem); /* Schedule check to see if the change worked */ @@ -4003,11 +4115,16 @@ } #ifdef CONFIG_PCI +#if (LINUX_VERSION_CODE >= 0x20400) static int __devinit airo_pci_probe(struct pci_dev *pdev, const struct pci_device_id *pent) { struct net_device *dev; + if (pci_enable_device(pdev)) + return -ENODEV; + pci_set_master(pdev); + dev = init_airo_card(pdev->irq, pdev->resource[2].start, 0); if (!dev) return -ENODEV; @@ -4021,6 +4138,7 @@ stop_airo_card(pci_get_drvdata(pdev), 1); } #endif +#endif static int __init airo_init_module( void ) { @@ -4140,15 +4258,17 @@ struct airo_info *local = dev->priv; StatusRid status_rid; /* Card status info */ - readStatusRid(local, &status_rid); + if ((local->config.opmode & 0xFF) == MODE_STA_ESS) + status_rid.channel = local->config.channelSet; + else + readStatusRid(local, &status_rid); - /* Will return zero in infrastructure mode */ #ifdef WEXT_USECHANNELS - fwrq->m = ((int)status_rid.channel) + 1; + fwrq->m = ((int)status_rid.channelSet) + 1; fwrq->e = 0; #else { - int f = (int)status_rid.channel; + int f = (int)status_rid.channelSet; fwrq->m = frequency_list[f] * 100000; fwrq->e = 1; } @@ -4196,9 +4316,9 @@ SSID_rid.ssids[index].len = dwrq->length - 1; } /* Write it to the card */ - disable_MAC(local); + disable_MAC(local, 1); writeSsidRid(local, &SSID_rid); - enable_MAC(local, &rsp); + enable_MAC(local, &rsp, 1); return 0; } @@ -4260,9 +4380,9 @@ memset(&APList_rid, 0, sizeof(APList_rid)); APList_rid.len = sizeof(APList_rid); memcpy(APList_rid.ap[0], awrq->sa_data, ETH_ALEN); - disable_MAC(local); + disable_MAC(local, 1); writeAPListRid(local, &APList_rid); - enable_MAC(local, &rsp); + enable_MAC(local, &rsp, 1); } return 0; } @@ -4618,7 +4738,7 @@ /* Copy the key in the driver */ memcpy(key.key, extra, dwrq->length); /* Send the key to the card */ - set_wep_key(local, index, key.key, key.len, 1); + set_wep_key(local, index, key.key, key.len, 1, 1); } /* WE specify that if a valid key is set, encryption * should be enabled (user may turn it off later) @@ -4632,7 +4752,7 @@ /* Do we want to just set the transmit key index ? */ int index = (dwrq->flags & IW_ENCODE_INDEX) - 1; if ((index>=0) && (index<(cap_rid.softCap&0x80)?4:1)) { - set_wep_key(local, index, 0, 0, 1); + set_wep_key(local, index, 0, 0, 1, 1); } else /* Don't complain if only change the mode */ if(!dwrq->flags & IW_ENCODE_MODE) { @@ -4699,6 +4819,7 @@ return 0; } +#if WIRELESS_EXT > 9 /*------------------------------------------------------------------*/ /* * Wireless Handler : set Tx-Power @@ -4752,7 +4873,9 @@ return 0; } +#endif /* WIRELESS_EXT > 9 */ +#if WIRELESS_EXT > 10 /*------------------------------------------------------------------*/ /* * Wireless Handler : set Retry limits @@ -4818,6 +4941,7 @@ return 0; } +#endif /* WIRELESS_EXT > 10 */ /*------------------------------------------------------------------*/ /* @@ -4852,8 +4976,8 @@ range->num_frequency = k; /* Hum... Should put the right values there */ - range->max_qual.qual = 10; - range->max_qual.level = 0x100 - 120; /* -120 dBm */ + range->max_qual.qual = 0xFF; + range->max_qual.level = 0; range->max_qual.noise = 0; range->sensitivity = 65535; @@ -4891,6 +5015,7 @@ range->num_encoding_sizes = 0; range->max_encoding_tokens = 0; } +#if WIRELESS_EXT > 9 range->min_pmp = 0; range->max_pmp = 5000000; /* 5 secs */ range->min_pmt = 0; @@ -4907,6 +5032,8 @@ } range->num_txpower = i; range->txpower_capa = IW_TXPOW_MWATT; +#endif /* WIRELESS_EXT > 9 */ +#if WIRELESS_EXT > 10 range->we_version_source = 12; range->we_version_compiled = WIRELESS_EXT; range->retry_capa = IW_RETRY_LIMIT | IW_RETRY_LIFETIME; @@ -4916,6 +5043,8 @@ range->max_retry = 65535; range->min_r_time = 1024; range->max_r_time = 65535 * 1024; +#endif /* WIRELESS_EXT > 10 */ +#if WIRELESS_EXT > 11 /* Experimental measurements - boundary 11/5.5 Mb/s */ /* Note : with or without the (local->rssi), results * are somewhat different. - Jean II */ @@ -4925,6 +5054,7 @@ else range->avg_qual.level = 176; /* -80 dBm */ range->avg_qual.noise = 0; +#endif /* WIRELESS_EXT > 11 */ return 0; } @@ -5277,7 +5407,7 @@ * consequences are begnign. So I don't bother fixing it - Javier */ /* Try to read the first entry of the scan result */ - rc = PC4500_readrid(ai, RID_BSSLISTFIRST, &BSSList, sizeof(BSSList)); + rc = PC4500_readrid(ai, RID_BSSLISTFIRST, &BSSList, sizeof(BSSList), 1); if((rc) || (BSSList.index == 0xffff)) { /* Client error, no scan results... * The caller need to restart the scan. */ @@ -5293,7 +5423,7 @@ /* Read next entry */ rc = PC4500_readrid(ai, RID_BSSLISTNEXT, - &BSSList, sizeof(BSSList)); + &BSSList, sizeof(BSSList), 1); } /* Length of data */ dwrq->length = (current_ev - extra); @@ -5315,22 +5445,35 @@ { struct airo_info *local = dev->priv; struct sockaddr *address = (struct sockaddr *) extra; + int idx = 0; /* Disable spy while we copy the addresses. * As we don't disable interrupts, we need to do this to avoid races */ local->spy_number = 0; if (dwrq->length > 0) { - int i; + int i, j, dup; /* Copy addresses */ - for (i = 0; i < dwrq->length; i++) - memcpy(local->spy_address[i], address[i].sa_data, ETH_ALEN); + for (i = 0; i < dwrq->length; i++) { + dup = 0; + for (j = 0; j < i; j++) { + if (memcmp(local->spy_address[j], address[i].sa_data, ETH_ALEN) == 0) { + dup = 1; + /* printk ("tried to add duplicate MAC to spy list\n"); */ + break; + } + } + if (! dup) { + memcpy(local->spy_address[idx], address[i].sa_data, ETH_ALEN); + idx++; + } + } /* Reset stats */ memset(local->spy_stat, 0, sizeof(struct iw_quality) * IW_MAX_SPY); } /* Enable addresses */ - local->spy_number = dwrq->length; + local->spy_number = idx; return 0; } @@ -5383,9 +5526,9 @@ /* Some of the "SET" function may have modified some of the * parameters. It's now time to commit them in the card */ - disable_MAC(local); - writeConfigRid(local); - enable_MAC(local, &rsp); + disable_MAC(local, 1); + writeConfigRid(local, 1); + enable_MAC(local, &rsp, 1); return 0; } @@ -5668,19 +5811,23 @@ } break; +#if WIRELESS_EXT > 9 case SIOCGIWTXPOW: // Get the current Tx-Power rc=airo_get_txpow(dev, NULL, &(wrq->u.txpower), NULL); break; case SIOCSIWTXPOW: rc=airo_set_txpow(dev, NULL, &(wrq->u.txpower), NULL); break; +#endif /* WIRELESS_EXT > 9 */ +#if WIRELESS_EXT > 10 case SIOCSIWRETRY: rc=airo_set_retry(dev, NULL, &(wrq->u.retry), NULL); break; case SIOCGIWRETRY: rc=airo_get_retry(dev, NULL, &(wrq->u.retry), NULL); break; +#endif /* WIRELESS_EXT > 10 */ case SIOCGIWRANGE: // Get range of parameters { @@ -5869,33 +6016,39 @@ /* Get stats out of the card */ readStatusRid(local, &status_rid); - readStatsRid(local, &stats_rid, RID_STATS); + readStatsRid(local, &stats_rid, RID_STATS, 1); /* The status */ - local->wstats.status = status_rid.mode; + local->wstats.status = le16_to_cpu(status_rid.mode); /* Signal quality and co. But where is the noise level ??? */ - local->wstats.qual.qual = status_rid.signalQuality; + local->wstats.qual.qual = le16_to_cpu(status_rid.signalQuality); if (local->rssi) - local->wstats.qual.level = 0x100 - local->rssi[status_rid.sigQuality].rssidBm; - else - local->wstats.qual.level = (status_rid.normalizedSignalStrength + 321) / 2; - local->wstats.qual.noise = 0; + local->wstats.qual.level = 0x100 - local->rssi[le16_to_cpu(status_rid.curSignalStrength)].rssidBm; + else { + local->wstats.qual.level = le16_to_cpu(status_rid.curSignalStrength) + 0x100 - 100; + } + local->wstats.qual.noise = 0x100 - status_rid.noiseAvedBm; local->wstats.qual.updated = 3; /* Packets discarded in the wireless adapter due to wireless * specific problems */ local->wstats.discard.nwid = vals[56] + vals[57] + vals[58];/* SSID Mismatch */ local->wstats.discard.code = vals[6];/* RxWepErr */ +#if WIRELESS_EXT > 11 local->wstats.discard.fragment = vals[30]; local->wstats.discard.retries = vals[10]; local->wstats.discard.misc = vals[1] + vals[32]; local->wstats.miss.beacon = vals[34]; +#else /* WIRELESS_EXT > 11 */ + local->wstats.discard.misc = vals[1] + vals[30] + vals[32]; +#endif /* WIRELESS_EXT > 11 */ return &local->wstats; } #endif /* WIRELESS_EXT */ #ifdef CISCO_EXT +#define RIDS_SIZE 2048 /* * This just translates from driver IOCTL codes to the command codes to * feed to the radio's host interface. Things can be added/deleted @@ -5904,7 +6057,7 @@ */ static int readrids(struct net_device *dev, aironet_ioctl *comp) { unsigned short ridcode; - unsigned char iobuf[2048]; + unsigned char *iobuf; struct airo_info *ai = dev->priv; if (ai->flags & FLAG_FLASHING) @@ -5913,7 +6066,7 @@ switch(comp->command) { case AIROGCAP: ridcode = RID_CAPABILITIES; break; - case AIROGCFG: writeConfigRid (ai); + case AIROGCFG: writeConfigRid (ai, 1); ridcode = RID_CONFIG; break; case AIROGSLIST: ridcode = RID_SSID; break; case AIROGVLIST: ridcode = RID_APLIST; break; @@ -5942,15 +6095,21 @@ break; } - PC4500_readrid(ai,ridcode,iobuf,sizeof(iobuf)); + if ((iobuf = kmalloc(RIDS_SIZE, GFP_KERNEL)) == NULL) + return -ENOMEM; + + PC4500_readrid(ai,ridcode,iobuf,RIDS_SIZE, 1); /* get the count of bytes in the rid docs say 1st 2 bytes is it. * then return it to the user * 9/22/2000 Honor user given length */ if (copy_to_user(comp->data, iobuf, - min((int)comp->len, (int)sizeof(iobuf)))) + min((int)comp->len, (int)RIDS_SIZE))) { + kfree (iobuf); return -EFAULT; + } + kfree (iobuf); return 0; } @@ -5962,8 +6121,8 @@ struct airo_info *ai = dev->priv; int ridcode, enabled; Resp rsp; - static int (* writer)(struct airo_info *, u16 rid, const void *, int); - unsigned char iobuf[2048]; + static int (* writer)(struct airo_info *, u16 rid, const void *, int, int); + unsigned char *iobuf; /* Only super-user can write RIDs */ if (!capable(CAP_NET_ADMIN)) @@ -5992,7 +6151,7 @@ * same with MAC off */ case AIROPMACON: - if (enable_MAC(ai, &rsp) != 0) + if (enable_MAC(ai, &rsp, 1) != 0) return -EIO; return 0; @@ -6001,7 +6160,7 @@ * as disable_MAC. it's probably so short the compiler does not gen one. */ case AIROPMACOFF: - disable_MAC(ai); + disable_MAC(ai, 1); return 0; /* This command merely clears the counts does not actually store any data @@ -6009,25 +6168,36 @@ * writerid routines. */ case AIROPSTCLR: - PC4500_readrid(ai,RID_STATSDELTACLEAR,iobuf,sizeof(iobuf)); + if ((iobuf = kmalloc(RIDS_SIZE, GFP_KERNEL)) == NULL) + return -ENOMEM; + + PC4500_readrid(ai,RID_STATSDELTACLEAR,iobuf,RIDS_SIZE, 1); enabled = ai->micstats.enabled; memset(&ai->micstats,0,sizeof(ai->micstats)); ai->micstats.enabled = enabled; if (copy_to_user(comp->data, iobuf, - min((int)comp->len, (int)sizeof(iobuf)))) + min((int)comp->len, (int)RIDS_SIZE))) { + kfree (iobuf); return -EFAULT; + } + kfree (iobuf); return 0; default: return -EOPNOTSUPP; /* Blarg! */ } - if(comp->len > sizeof(iobuf)) + if(comp->len > RIDS_SIZE) return -EINVAL; - if (copy_from_user(iobuf,comp->data,comp->len)) + if ((iobuf = kmalloc(RIDS_SIZE, GFP_KERNEL)) == NULL) + return -ENOMEM; + + if (copy_from_user(iobuf,comp->data,comp->len)) { + kfree (iobuf); return -EFAULT; + } if (comp->command == AIROPCFG) { ConfigRid *cfg = (ConfigRid *)iobuf; @@ -6041,8 +6211,11 @@ ai->flags &= ~FLAG_ADHOC; } - if((*writer)(ai, ridcode, iobuf,comp->len)) + if((*writer)(ai, ridcode, iobuf,comp->len,1)) { + kfree (iobuf); return -EIO; + } + kfree (iobuf); return 0; } @@ -6121,7 +6294,7 @@ */ int cmdreset(struct airo_info *ai) { - disable_MAC(ai); + disable_MAC(ai, 1); if(!waitbusy (ai)){ printk(KERN_INFO "Waitbusy hang before RESET\n"); Index: drivers/net/wireless/Makefile --- drivers/net/wireless/Makefile.orig 2003-08-12 14:09:58.000000000 -0400 +++ drivers/net/wireless/Makefile 2003-08-12 14:26:55.000000000 -0400 @@ -12,7 +12,10 @@ obj- := # Things that need to export symbols -export-objs := airo.o orinoco.o hermes.o +export-objs := airo.o orinoco.o hermes.o hostap.o hostap_crypt.o +export-objs += click_wifi.o + +obj-m += click_wifi.o obj-$(CONFIG_HERMES) += orinoco.o hermes.o obj-$(CONFIG_PCMCIA_HERMES) += orinoco_cs.o @@ -23,4 +26,9 @@ obj-$(CONFIG_AIRO) += airo.o obj-$(CONFIG_AIRO_CS) += airo_cs.o airo.o +obj-$(CONFIG_HOSTAP) += hostap.o hostap_crypt.o hostap_crypt_wep.o +obj-$(CONFIG_HOSTAP_CS) += hostap_cs.o +obj-$(CONFIG_HOSTAP_PLX) += hostap_plx.o +obj-$(CONFIG_HOSTAP_PCI) += hostap_pci.o + include $(TOPDIR)/Rules.make Index: drivers/net/wireless/Config.in --- drivers/net/wireless/Config.in.orig 2003-08-12 14:43:39.000000000 -0400 +++ drivers/net/wireless/Config.in 2003-06-09 13:30:25.000000000 -0400 @@ -7,6 +7,7 @@ fi tristate ' Hermes chipset 802.11b support (Orinoco/Prism2/Symbol)' CONFIG_HERMES +tristate ' Host AP support for Prism2/2.5/3 IEEE 802.11b' CONFIG_HOSTAP if [ "$CONFIG_ALL_PPC" = "y" ]; then dep_tristate ' Apple Airport support (built-in)' CONFIG_APPLE_AIRPORT $CONFIG_HERMES @@ -15,6 +16,9 @@ if [ "$CONFIG_PCI" = "y" ]; then dep_tristate ' Hermes in PLX9052 based PCI adaptor support (Netgear MA301 etc.) (EXPERIMENTAL)' CONFIG_PLX_HERMES $CONFIG_HERMES $CONFIG_EXPERIMENTAL dep_tristate ' Prism 2.5 PCI 802.11b adaptor support (EXPERIMENTAL)' CONFIG_PCI_HERMES $CONFIG_HERMES $CONFIG_EXPERIMENTAL + dep_tristate ' Host AP driver for Prism2/2.5/3 in PLX9052 based PCI adaptors' CONFIG_HOSTAP_PLX $CONFIG_HOSTAP + dep_tristate ' Host AP driver for Prism2.5 PCI adaptors' CONFIG_HOSTAP_PCI $CONFIG_HOSTAP + fi # If Pcmcia is compiled in, offer Pcmcia cards... @@ -22,6 +26,7 @@ comment 'Wireless Pcmcia cards support' dep_tristate ' Hermes PCMCIA card support' CONFIG_PCMCIA_HERMES $CONFIG_HERMES + dep_tristate ' Host AP driver for Prism2/2.5/3 PC Cards' CONFIG_HOSTAP_CS $CONFIG_HOSTAP $CONFIG_PCMCIA tristate ' Cisco/Aironet 34X/35X/4500/4800 PCMCIA cards' CONFIG_AIRO_CS fi Index: drivers/net/wireless/hostap_ap.c --- drivers/net/wireless/hostap_ap.c.orig 2003-08-12 14:39:16.000000000 -0400 +++ drivers/net/wireless/hostap_ap.c 2003-06-23 18:52:56.000000000 -0400 @@ -0,0 +1,3309 @@ +/* + * Intersil Prism2 driver with Host AP (software access point) support + * Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen + * + * Copyright (c) 2002-2003, Jouni Malinen + * + * 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_PARM(other_ap_policy, PARM_MIN_MAX "i"); +MODULE_PARM_DESC(other_ap_policy, "Other AP beacon monitoring policy (0-3)"); + +static int ap_max_inactivity[MAX_PARM_DEVICES] = { AP_MAX_INACTIVITY / HZ, + DEF_INTS }; +MODULE_PARM(ap_max_inactivity, PARM_MIN_MAX "i"); +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_PARM(ap_bridge_packets, PARM_MIN_MAX "i"); +MODULE_PARM_DESC(ap_bridge_packets, "Bridge packets directly between " + "stations"); + +static int autom_ap_wds[MAX_PARM_DEVICES] = { 0, DEF_INTS }; +MODULE_PARM(autom_ap_wds, PARM_MIN_MAX "i"); +MODULE_PARM_DESC(autom_ap_wds, "Add WDS connections to other APs " + "automatically"); + + +static void prism2_ap_update_sq(struct sta_info *sta, + struct hfa384x_rx_frame *rxdesc); +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_add_wds_queue(void *data); +static void prism2_send_mgmt(struct net_device *dev, + int type, int subtype, char *body, + int body_len, int txevent, 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->auth_algs); + + 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, 6) == 0) { + ap->sta_hash[STA_HASH(sta->addr)] = s->hnext; + return; + } + + while (s->hnext != NULL && memcmp(s->hnext->addr, sta->addr, 6) != 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) +{ + struct sk_buff *skb; + + 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; + } + + while ((skb = skb_dequeue(&sta->tx_buf)) != NULL) + dev_kfree_skb(skb); + + 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); +} + + +struct set_tim_data { + struct list_head list; + int aid; + int set; +}; + +static void hostap_set_tim(local_info_t *local, int aid, int set) +{ + struct list_head *ptr; + struct set_tim_data *new_entry; + + new_entry = (struct set_tim_data *) + kmalloc(sizeof(*new_entry), GFP_ATOMIC); + if (new_entry == NULL) { + printk(KERN_DEBUG "%s: hostap_set_tim: kmalloc failed\n", + local->dev->name); + return; + } + memset(new_entry, 0, sizeof(*new_entry)); + new_entry->aid = aid; + new_entry->set = set; + + spin_lock_bh(&local->ap->set_tim_lock); + for (ptr = local->ap->set_tim_list.next; + ptr != &local->ap->set_tim_list; + ptr = ptr->next) { + struct set_tim_data *entry = (struct set_tim_data *) ptr; + if (entry->aid == aid) { + PDEBUG(DEBUG_PS2, "%s: hostap_set_tim: aid=%d " + "set=%d ==> %d\n", + local->dev->name, aid, entry->set, set); + entry->set = set; + kfree(new_entry); + new_entry = NULL; + break; + } + } + if (new_entry) + list_add_tail(&new_entry->list, &local->ap->set_tim_list); + spin_unlock_bh(&local->ap->set_tim_lock); + + PRISM2_SCHEDULE_TASK(&local->ap->set_tim_queue); +} + + +static void handle_set_tim_queue(void *data) +{ + local_info_t *local = (local_info_t *) data; + struct set_tim_data *entry; + u16 val; + + for (;;) { + entry = NULL; + spin_lock_bh(&local->ap->set_tim_lock); + if (!list_empty(&local->ap->set_tim_list)) { + entry = list_entry(local->ap->set_tim_list.next, + struct set_tim_data, list); + list_del(&entry->list); + } + spin_unlock_bh(&local->ap->set_tim_lock); + if (!entry) + break; + + PDEBUG(DEBUG_PS2, "%s: hostap_set_tim_queue: aid=%d set=%d\n", + local->dev->name, entry->aid, entry->set); + + val = entry->aid; + if (entry->set) + val |= 0x8000; + if (hostap_set_word(local->dev, HFA384X_RID_CNFTIMCTRL, val)) { + printk(KERN_DEBUG "%s: set_tim failed (aid=%d " + "set=%d)\n", + local->dev->name, entry->aid, entry->set); + } + + kfree(entry); + } + +#ifndef NEW_MODULE_CODE + MOD_DEC_USE_COUNT; +#endif +} + + +static void hostap_event_new_sta(struct net_device *dev, struct sta_info *sta) +{ +#if WIRELESS_EXT >= 15 + 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); +#endif /* WIRELESS_EXT >= 15 */ +} + + +static void hostap_event_expired_sta(struct net_device *dev, + struct sta_info *sta) +{ +#if WIRELESS_EXT >= 15 + 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); +#endif /* WIRELESS_EXT >= 15 */ +} + + +#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 (sta->last_rx + ap->max_inactivity > jiffies) { + /* 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->txexc == 0) { + /* data nullfunc frame poll did not produce TX errors; assume + * station ACKed it */ + 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)); + prism2_wds_del(local, sta->addr, 0, 1); + } + } else if (sta->timeout_next == STA_NULLFUNC) { + /* send data frame to poll STA and check whether this frame + * is ACKed */ + sta->txexc = 0; + /* FIX: WLAN_FC_STYPE_NULLFUNC would be more appropriate, but + * it is apparently not retried so TX Exc events are not + * received for it */ + prism2_send_mgmt(local->dev, WLAN_FC_TYPE_DATA, + WLAN_FC_STYPE_DATA, NULL, 0, 1, + sta->addr, 0); + } 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, WLAN_FC_TYPE_MGMT, + (deauth ? WLAN_FC_STYPE_DEAUTH : + WLAN_FC_STYPE_DISASSOC), + (char *) &resp, 2, 1, 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, WLAN_FC_TYPE_MGMT, WLAN_FC_STYPE_DEAUTH, + (char *) &resp, 2, 1, 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, 6); + + 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, 6) == 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, 6) == 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, WLAN_FC_TYPE_MGMT, WLAN_FC_STYPE_DEAUTH, + (char *) &resp, 2, 1, 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 major, int minor, + int variant) +{ + if (!ap) + return; + + if (major == 0 && minor == 8 && variant == 0) { + PDEBUG(DEBUG_AP, "Using data::nullfunc ACK workaround - " + "firmware upgrade recommended\n"); + ap->nullfunc_ack = 1; + } else + ap->nullfunc_ack = 0; + + if (major == 1 && minor == 4 && variant == 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 hostap_ieee80211_hdr *hdr; + + if (!ap->local->hostapd || !ap->local->apdev) { + dev_kfree_skb(skb); + return; + } + + hdr = (struct hostap_ieee80211_hdr *) skb->data; + fc = le16_to_cpu(hdr->frame_control); + + /* 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_control = 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 hostap_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 hostap_ieee80211_hdr *) skb->data; + fc = le16_to_cpu(hdr->frame_control); + if (WLAN_FC_GET_TYPE(fc) != WLAN_FC_TYPE_MGMT || + WLAN_FC_GET_STYPE(fc) != WLAN_FC_STYPE_AUTH || + skb->len < 24 + 6) { + printk(KERN_DEBUG "%s: hostap_ap_tx_cb_auth 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 + 24); + auth_alg = le16_to_cpu(*pos++); + auth_transaction = le16_to_cpu(*pos++); + status = le16_to_cpu(*pos++); + 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 - %s\n", + dev->name, MAC2STR(hdr->addr1), 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 hostap_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 hostap_ieee80211_hdr *) skb->data; + fc = le16_to_cpu(hdr->frame_control); + if (WLAN_FC_GET_TYPE(fc) != WLAN_FC_TYPE_MGMT || + (WLAN_FC_GET_STYPE(fc) != WLAN_FC_STYPE_ASSOC_RESP && + WLAN_FC_GET_STYPE(fc) != WLAN_FC_STYPE_REASSOC_RESP) || + skb->len < 24 + 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 + 24); + 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); +} +#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->proc = local->proc; + 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->auth_algs = PRISM2_AUTH_OPEN | PRISM2_AUTH_SHARED_KEY; + 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); + +#ifndef PRISM2_NO_PROCFS_DEBUG + if (ap->proc != NULL) { + create_proc_read_entry("ap_debug", 0, ap->proc, + ap_debug_proc_read, ap); + } +#endif /* PRISM2_NO_PROCFS_DEBUG */ + + /* Initialize task queue structure for AP management */ + HOSTAP_QUEUE_INIT(&local->ap->set_tim_queue, handle_set_tim_queue, + local); + INIT_LIST_HEAD(&ap->set_tim_list); + spin_lock_init(&ap->set_tim_lock); + + HOSTAP_QUEUE_INIT(&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 + HOSTAP_QUEUE_INIT(&local->ap->add_wds_queue, + handle_add_wds_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); + if (ap->tx_callback_auth == 0 || ap->tx_callback_assoc == 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); + if (ap->proc != NULL) { + 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 */ + + ap->initialized = 1; +} + +void hostap_free_data(struct ap_data *ap) +{ + struct list_head *ptr, *n; + + 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 */ + + ptr = ap->sta_list.next; + while (ptr != NULL && ptr != &ap->sta_list) { + struct sta_info *sta = (struct sta_info *) ptr; + ptr = ptr->next; + 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); + } + + for (ptr = ap->set_tim_list.next, n = ptr->next; + ptr != &ap->set_tim_list; ptr = n, n = ptr->next) { + struct set_tim_data *entry; + entry = list_entry(ptr, struct set_tim_data, list); + list_del(&entry->list); + kfree(entry); + } + +#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, 6) != 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, + int type, int subtype, char *body, + int body_len, int txevent, u8 *addr, + u16 tx_cb_idx) +{ + local_info_t *local = (local_info_t *) dev->priv; + struct hfa384x_tx_frame *txdesc; + u16 fc, tx_control; + struct sk_buff *skb; + + 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(*txdesc) + body_len); + if (skb == NULL) { + PDEBUG(DEBUG_AP, "%s: prism2_send_mgmt failed to allocate " + "skb\n", dev->name); + return; + } + + txdesc = (struct hfa384x_tx_frame *) skb_put(skb, sizeof(*txdesc)); + if (body) + memcpy(skb_put(skb, body_len), body, body_len); + + memset(txdesc, 0, sizeof(*txdesc)); + /* FIX: set tx_rate if f/w does not know how to do it */ + tx_control = txevent ? local->tx_control : HFA384X_TX_CTRL_802_11; + if (tx_cb_idx) + tx_control |= HFA384X_TX_CTRL_TX_OK; + txdesc->sw_support = cpu_to_le16(tx_cb_idx); + txdesc->tx_control = cpu_to_le16(tx_control); + txdesc->data_len = cpu_to_le16(body_len); + + fc = (type << 2) | (subtype << 4); + + memcpy(txdesc->addr1, addr, ETH_ALEN); /* DA / RA */ + if (type == WLAN_FC_TYPE_DATA) { + fc |= WLAN_FC_FROMDS; + memcpy(txdesc->addr2, dev->dev_addr, ETH_ALEN); /* BSSID */ + memcpy(txdesc->addr3, dev->dev_addr, ETH_ALEN); /* SA */ + } else if (type == WLAN_FC_TYPE_CTRL) { + /* control:ACK does not have addr2 or addr3 */ + memset(txdesc->addr2, 0, ETH_ALEN); + memset(txdesc->addr3, 0, ETH_ALEN); + } else { + memcpy(txdesc->addr2, dev->dev_addr, ETH_ALEN); /* SA */ + memcpy(txdesc->addr3, dev->dev_addr, ETH_ALEN); /* BSSID */ + } + + txdesc->frame_control = cpu_to_le16(fc); + + /* FIX: is it OK to call dev_queue_xmit() here? This can be called in + * interrupt context, but not in hard interrupt (like prism2_rx() that + * required bridge_list. If needed, bridge_list could be used also here + * when prism2_send_mgmt is called in interrupt context. */ + + skb->protocol = __constant_htons(ETH_P_HOSTAP); + 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\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->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 signal=%d rate=%d flow=%d\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" + "txexc=%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, + sta->last_rx_flow, + 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], + sta->txexc); + if (sta->crypt && sta->crypt->ops) + p += sprintf(p, "crypt=%s\n", sta->crypt->ops->name); +#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); + } + +#ifndef NEW_MODULE_CODE + MOD_DEC_USE_COUNT; +#endif +} + + +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; + PRISM2_SCHEDULE_TASK(&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 = hostap_get_crypto_ops("WEP"); + + if (ap->crypt) { + if (ap->crypt->init) { + ap->crypt_priv = ap->crypt->init(); + 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(0, key, WEP_KEY_LEN, + ap->crypt_priv); + } + } + } + + if (ap->crypt == NULL) { + printk(KERN_WARNING "AP could not initialize WEP: load module " + "hostap_crypt_wep.o\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; + int olen; + + if (ap->crypt == NULL) { + ap_crypt_init(ap); + if (ap->crypt == NULL) + return NULL; + } + + tmpbuf = (char *) kmalloc(WLAN_AUTH_CHALLENGE_LEN + + ap->crypt->extra_prefix_len + + ap->crypt->extra_postfix_len, + GFP_ATOMIC); + if (tmpbuf == NULL) { + PDEBUG(DEBUG_AP, "AP: kmalloc failed for challenge\n"); + return NULL; + } + memset(tmpbuf, 0, WLAN_AUTH_CHALLENGE_LEN + + ap->crypt->extra_prefix_len + ap->crypt->extra_postfix_len); + olen = ap->crypt->encrypt(tmpbuf, WLAN_AUTH_CHALLENGE_LEN, + ap->crypt_priv); + if (olen < 0) { + kfree(tmpbuf); + return NULL; + } + memmove(tmpbuf, tmpbuf + 4, WLAN_AUTH_CHALLENGE_LEN); + return tmpbuf; +} + + +/* Called only as a scheduled task for pending AP frames. */ +static void handle_authen(local_info_t *local, struct hfa384x_rx_frame *rxdesc) +{ + struct net_device *dev = local->dev; + 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 prism2_crypt_data *crypt; + char *txt = ""; + + len = __le16_to_cpu(rxdesc->data_len); + + fc = le16_to_cpu(rxdesc->frame_control); + + if (len < 6) { + PDEBUG(DEBUG_AP, "%s: handle_authen - too short payload " + "(len=%d) from " MACSTR "\n", dev->name, len, + MAC2STR(rxdesc->addr2)); + return; + } + + spin_lock_bh(&local->ap->sta_table_lock); + sta = ap_get_sta(local->ap, rxdesc->addr2); + if (sta) + atomic_inc(&sta->users); + spin_unlock_bh(&local->ap->sta_table_lock); + + if (sta && sta->crypt) + crypt = sta->crypt; + else + crypt = local->crypt; + + if (crypt && local->host_decrypt && (fc & WLAN_FC_ISWEP)) { + atomic_inc(&crypt->refcnt); + olen = crypt->ops->decrypt((u8 *) (rxdesc + 1), len, + crypt->priv); + atomic_dec(&crypt->refcnt); + if (olen < 0) { + if (sta) + atomic_dec(&sta->users); + PDEBUG(DEBUG_AP, "%s: handle_authen: auth frame from " + "STA " MACSTR " could not be decrypted\n", + dev->name, MAC2STR(rxdesc->addr2)); + return; + } + if (olen < 6) { + PDEBUG(DEBUG_AP, "%s: handle_authen - too short " + "payload (len=%d, decrypted len=%d) from " + MACSTR "\n", + dev->name, len, olen, MAC2STR(rxdesc->addr2)); + return; + } + len = olen; + } + + pos = (u16 *) (rxdesc + 1); + auth_alg = __le16_to_cpu(*pos); + pos++; + auth_transaction = __le16_to_cpu(*pos); + pos++; + status_code = __le16_to_cpu(*pos); + pos++; + + if (ap_control_mac_deny(&ap->mac_restrictions, rxdesc->addr2)) { + txt = "authentication denied"; + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + + if (((ap->auth_algs & PRISM2_AUTH_OPEN) && + auth_alg == WLAN_AUTH_OPEN) || + ((ap->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 (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, rxdesc->addr2); + if (sta == NULL) { + txt = "ap_add_sta failed"; + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + } + + prism2_ap_update_sq(sta, rxdesc); + + switch (auth_alg) { + case WLAN_AUTH_OPEN: + txt = "authOK"; + /* STA will be authenticated, if it ACKs authentication frame + */ + 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"; + /* STA will be authenticated, if it ACKs authentication + * frame */ + 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, WLAN_FC_TYPE_MGMT, WLAN_FC_STYPE_AUTH, + body, olen, 1, rxdesc->addr2, ap->tx_callback_auth); + + if (sta) { + sta->last_rx = jiffies; + atomic_dec(&sta->users); + } + +#if 0 + PDEBUG(DEBUG_AP, "%s: " MACSTR " auth (alg=%d trans#=%d stat=%d len=%d" + " fc=%04x) ==> %d (%s)\n", dev->name, MAC2STR(rxdesc->addr2), + auth_alg, auth_transaction, status_code, len, fc, resp, txt); +#endif +} + + +/* Called only as a scheduled task for pending AP frames. */ +static void handle_assoc(local_info_t *local, struct hfa384x_rx_frame *rxdesc, + int reassoc) +{ + struct net_device *dev = local->dev; + 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 = __le16_to_cpu(rxdesc->data_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(rxdesc->addr2)); + return; + } + + spin_lock_bh(&local->ap->sta_table_lock); + sta = ap_get_sta(local->ap, rxdesc->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; + goto fail; + } + atomic_inc(&sta->users); + spin_unlock_bh(&local->ap->sta_table_lock); + + prism2_ap_update_sq(sta, rxdesc); + + pos = (u16 *) (rxdesc + 1); + 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(rxdesc->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, WLAN_FC_TYPE_MGMT, + (send_deauth ? WLAN_FC_STYPE_DEAUTH : + (reassoc ? WLAN_FC_STYPE_REASSOC_RESP : + WLAN_FC_STYPE_ASSOC_RESP)), + body, (u8 *) pos - (u8 *) body, 1, + rxdesc->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(rxdesc->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 hfa384x_rx_frame *rxdesc) +{ + struct net_device *dev = local->dev; + char *body = (char *) (rxdesc + 1); + int len; + u16 reason_code, *pos; + struct sta_info *sta = NULL; + + len = __le16_to_cpu(rxdesc->data_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(rxdesc->addr2), len, + reason_code); + + spin_lock_bh(&local->ap->sta_table_lock); + sta = ap_get_sta(local->ap, rxdesc->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); + prism2_ap_update_sq(sta, rxdesc); + } + 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(rxdesc->addr2), reason_code); + } +} + + +/* Called only as a scheduled task for pending AP frames. */ +static void handle_disassoc(local_info_t *local, + struct hfa384x_rx_frame *rxdesc) +{ + struct net_device *dev = local->dev; + char *body = (char *) (rxdesc + 1); + int len; + u16 reason_code, *pos; + struct sta_info *sta = NULL; + + len = __le16_to_cpu(rxdesc->data_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(rxdesc->addr2), len, + reason_code); + + spin_lock_bh(&local->ap->sta_table_lock); + sta = ap_get_sta(local->ap, rxdesc->addr2); + if (sta != NULL) { + if ((sta->flags & WLAN_STA_ASSOC) && !sta->ap) + hostap_event_expired_sta(local->dev, sta); + sta->flags &= ~WLAN_STA_ASSOC; + prism2_ap_update_sq(sta, rxdesc); + } + 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(rxdesc->addr2), reason_code); + } +} + + +/* Called only as a scheduled task for pending AP frames. */ +static void ap_handle_data_nullfunc(local_info_t *local, + struct hfa384x_rx_frame *rxdesc) +{ + 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, WLAN_FC_TYPE_CTRL, WLAN_FC_STYPE_ACK, + NULL, 0, 0, rxdesc->addr2, 0); +} + + +/* Called only as a scheduled task for pending AP frames. */ +static void ap_handle_dropped_data(local_info_t *local, + struct hfa384x_rx_frame *rxdesc) +{ + 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, rxdesc->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, WLAN_FC_TYPE_MGMT, + ((sta == NULL || !(sta->flags & WLAN_STA_ASSOC)) ? + WLAN_FC_STYPE_DEAUTH : WLAN_FC_STYPE_DISASSOC), + (char *) &reason, sizeof(reason), 1, + rxdesc->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) +{ + 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 */ + memcpy(skb->cb, AP_SKB_CB_MAGIC, AP_SKB_CB_MAGIC_LEN); + skb->cb[AP_SKB_CB_MAGIC_LEN] = AP_SKB_CB_BUFFERED_FRAME; + if (!skb_queue_empty(&sta->tx_buf)) { + /* indicate to STA that more frames follow */ + skb->cb[AP_SKB_CB_MAGIC_LEN] |= AP_SKB_CB_ADD_MOREDATA; + } + if (skb->dev->hard_start_xmit(skb, skb->dev)) { + PDEBUG(DEBUG_AP, "%s: TX failed for buffered frame (PS Poll)" + "\n", skb->dev->name); + dev_kfree_skb(skb); + } +} + + +/* Called only as a scheduled task for pending AP frames. */ +static void handle_pspoll(local_info_t *local, + struct hfa384x_rx_frame *rxdesc) +{ + 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(rxdesc->addr1), MAC2STR(rxdesc->addr2), + !!(le16_to_cpu(rxdesc->frame_control) & WLAN_FC_PWRMGT)); + + if (memcmp(rxdesc->addr1, dev->dev_addr, 6)) { + PDEBUG(DEBUG_AP, "handle_pspoll - addr1(BSSID)=" MACSTR + " not own MAC\n", MAC2STR(rxdesc->addr1)); + return; + } + + aid = __le16_to_cpu(rxdesc->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, rxdesc->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; + } + prism2_ap_update_sq(sta, rxdesc); + 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); +} + + +static void prism2_ap_update_sq(struct sta_info *sta, + struct hfa384x_rx_frame *rxdesc) +{ + sta->last_rx_silence = rxdesc->silence; + sta->last_rx_signal = rxdesc->signal; + sta->last_rx_rate = rxdesc->rate; + sta->last_rx_flow = rxdesc->rxflow; + sta->last_rx_updated = 7; + if (rxdesc->rate == 10) + sta->rx_count[0]++; + else if (rxdesc->rate == 20) + sta->rx_count[1]++; + else if (rxdesc->rate == 55) + sta->rx_count[2]++; + else if (rxdesc->rate == 110) + sta->rx_count[3]++; +} + + +#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT + +static void handle_add_wds_queue(void *data) +{ + local_info_t *local = data; + struct add_wds_data *entry, *prev; + + spin_lock_bh(&local->lock); + entry = local->ap->add_wds_entries; + local->ap->add_wds_entries = NULL; + spin_unlock_bh(&local->lock); + + while (entry) { + PDEBUG(DEBUG_AP, "%s: adding automatic WDS connection " + "to AP " MACSTR "\n", + local->dev->name, MAC2STR(entry->addr)); + prism2_wds_add(local, entry->addr, 0); + + prev = entry; + entry = entry->next; + kfree(prev); + } + +#ifndef NEW_MODULE_CODE + MOD_DEC_USE_COUNT; +#endif +} + + +/* Called only as a scheduled task for pending AP frames. */ +static void handle_beacon(local_info_t *local, struct hfa384x_rx_frame *rxdesc) +{ + char *body = (char *) (rxdesc + 1); + 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 = __le16_to_cpu(rxdesc->data_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, rxdesc->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, rxdesc->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) { + /* schedule a non-interrupt context process to add the + * WDS device since register_netdevice() can sleep */ + struct add_wds_data *entry; + entry = kmalloc(sizeof(*entry), GFP_ATOMIC); + if (entry) { + memcpy(entry->addr, sta->addr, ETH_ALEN); + spin_lock_bh(&local->lock); + entry->next = local->ap->add_wds_entries; + local->ap->add_wds_entries = entry; + spin_unlock_bh(&local->lock); + PRISM2_SCHEDULE_TASK(&local->ap-> + add_wds_queue); + } + } + } + + 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; + prism2_ap_update_sq(sta, rxdesc); + + 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) +{ +#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT + struct net_device *dev = local->dev; +#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ + u16 fc, type, stype; + struct hfa384x_rx_frame *rxdesc; + + /* FIX: should give skb->len to handler functions and check that the + * buffer is long enough */ + rxdesc = (struct hfa384x_rx_frame *) skb->data; + fc = __le16_to_cpu(rxdesc->frame_control); + type = WLAN_FC_GET_TYPE(fc); + stype = WLAN_FC_GET_STYPE(fc); + +#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT + if (!local->hostapd && type == WLAN_FC_TYPE_DATA) { + PDEBUG(DEBUG_AP, "handle_ap_item - data frame\n"); + + if (!(fc & WLAN_FC_TODS) || (fc & WLAN_FC_FROMDS)) { + if (stype == WLAN_FC_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, rxdesc); + goto done; + } + PDEBUG(DEBUG_AP, " not ToDS frame (fc=0x%04x)\n", + fc); + goto done; + } + + if (memcmp(rxdesc->addr1, dev->dev_addr, 6)) { + PDEBUG(DEBUG_AP, "handle_ap_item - addr1(BSSID)=" + MACSTR " not own MAC\n", + MAC2STR(rxdesc->addr1)); + goto done; + } + + if (local->ap->nullfunc_ack && stype == WLAN_FC_STYPE_NULLFUNC) + ap_handle_data_nullfunc(local, rxdesc); + else + ap_handle_dropped_data(local, rxdesc); + goto done; + } +#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ + + if (type == WLAN_FC_TYPE_CTRL && + stype == WLAN_FC_STYPE_PSPOLL) { + handle_pspoll(local, rxdesc); + 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 != WLAN_FC_TYPE_MGMT) { + PDEBUG(DEBUG_AP, "handle_ap_item - not a management frame?\n"); + goto done; + } + + if (stype == WLAN_FC_STYPE_BEACON) { + handle_beacon(local, rxdesc); + goto done; + } + + if (memcmp(rxdesc->addr1, dev->dev_addr, 6)) { + PDEBUG(DEBUG_AP, "handle_ap_item - addr1(DA)=" MACSTR + " not own MAC\n", MAC2STR(rxdesc->addr1)); + goto done; + } + + if (memcmp(rxdesc->addr3, dev->dev_addr, 6)) { + PDEBUG(DEBUG_AP, "handle_ap_item - addr3(BSSID)=" MACSTR + " not own MAC\n", MAC2STR(rxdesc->addr3)); + goto done; + } + + switch (stype) { + case WLAN_FC_STYPE_ASSOC_REQ: + handle_assoc(local, rxdesc, 0); + break; + case WLAN_FC_STYPE_ASSOC_RESP: + PDEBUG(DEBUG_AP, "==> ASSOC RESP (ignored)\n"); + break; + case WLAN_FC_STYPE_REASSOC_REQ: + handle_assoc(local, rxdesc, 1); + break; + case WLAN_FC_STYPE_REASSOC_RESP: + PDEBUG(DEBUG_AP, "==> REASSOC RESP (ignored)\n"); + break; + case WLAN_FC_STYPE_ATIM: + PDEBUG(DEBUG_AP, "==> ATIM (ignored)\n"); + break; + case WLAN_FC_STYPE_DISASSOC: + handle_disassoc(local, rxdesc); + break; + case WLAN_FC_STYPE_AUTH: + handle_authen(local, rxdesc); + break; + case WLAN_FC_STYPE_DEAUTH: + handle_deauth(local, rxdesc); + break; + default: + PDEBUG(DEBUG_AP, "Unknown mgmt frame subtype 0x%02x\n", stype); + 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) +{ + local_info_t *local = (local_info_t *) dev->priv; + u16 fc; + struct hfa384x_rx_frame *rxdesc; + + if (skb->len < sizeof(*rxdesc)) + goto drop; + + local->stats.rx_packets++; + + rxdesc = (struct hfa384x_rx_frame *) skb->data; + fc = le16_to_cpu(rxdesc->frame_control); + + if (local->ap->ap_policy == AP_OTHER_AP_SKIP_ALL && + WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT && + WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_BEACON) + goto drop; + + handle_ap_item(local, skb); + 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 hfa384x_rx_frame *rxdesc; + + if (skb_queue_empty(&sta->tx_buf)) + return; + + skb = dev_alloc_skb(sizeof(*rxdesc)); + if (skb == NULL) { + printk(KERN_DEBUG "%s: schedule_packet_send: skb alloc " + "failed\n", local->dev->name); + return; + } + + rxdesc = (struct hfa384x_rx_frame *) skb_put(skb, sizeof(*rxdesc)); + + /* Generate a fake pspoll frame to start packet delivery */ + memset(rxdesc, 0, sizeof(*rxdesc)); + rxdesc->frame_control = __constant_cpu_to_le16( + (WLAN_FC_TYPE_CTRL << 2) | (WLAN_FC_STYPE_PSPOLL << 4)); + memcpy(rxdesc->addr1, local->dev->dev_addr, 6); + memcpy(rxdesc->addr2, sta->addr, 6); + rxdesc->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->protocol = __constant_htons(ETH_P_HOSTAP); + skb->dev = local->dev; + + hostap_rx(local->dev, skb); +} + + +#ifdef WIRELESS_EXT +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; +} + + +#if WIRELESS_EXT > 13 +/* 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) +{ + local_info_t *local = (local_info_t *) dev->priv; + struct ap_data *ap = local->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) && (WIRELESS_EXT > 14) + char buf[64]; +#endif + + 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); + } + +#if WIRELESS_EXT > 14 + 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 /* WIRELESS_EXT > 14 */ + } +#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; +} +#endif /* WIRELESS_EXT > 13 */ +#endif /* WIRELESS_EXT */ + + +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; + param->u.get_info_sta.txexc = sta->txexc; + + atomic_dec(&sta->users); + + return 1; +} + + +static int prism2_hostapd_reset_txexc_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->txexc = 0; + spin_unlock_bh(&ap->sta_table_lock); + + return sta ? 0 : -ENOENT; +} + + +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(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_RESET_TXEXC_STA: + return prism2_hostapd_reset_txexc_sta(ap, param); + case PRISM2_HOSTAPD_SET_FLAGS_STA: + return prism2_hostapd_set_flags_sta(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; + local_info_t *local = dev->priv; + + sta->tx_count[sta->tx_rate_idx]++; + sta->tx_since_last_failure++; + 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 sk_buff *skb, + struct hfa384x_tx_frame *txdesc, int wds, + int host_encrypt, + struct prism2_crypt_data **crypt, + void **sta_ptr) +{ + struct sta_info *sta = NULL; + int set_tim, ret; + + ret = AP_TX_CONTINUE; + if (local->ap == NULL) + goto out; + + if (txdesc->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, txdesc->addr1); + if (sta) + atomic_inc(&sta->users); + spin_unlock(&local->ap->sta_table_lock); + + if (local->iw_mode == IW_MODE_MASTER && sta == NULL && !wds) { + /* remove FromDS flag from (pseudo) ad-hoc style + * communication between APs */ + txdesc->frame_control &= + ~(__constant_cpu_to_le16(WLAN_FC_FROMDS)); + + printk(KERN_DEBUG "AP: packet to non-associated STA " + MACSTR "\n", MAC2STR(txdesc->addr1)); + } + + 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 = txdesc->tx_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 (memcmp(skb->cb, AP_SKB_CB_MAGIC, AP_SKB_CB_MAGIC_LEN) == 0) { + if (skb->cb[AP_SKB_CB_MAGIC_LEN] & AP_SKB_CB_ADD_MOREDATA) { + /* indicate to STA that more frames follow */ + txdesc->frame_control |= + __constant_cpu_to_le16(WLAN_FC_MOREDATA); + } + + if (skb->cb[AP_SKB_CB_MAGIC_LEN] & AP_SKB_CB_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). */ + 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 += le16_to_cpu(txdesc->data_len) + 36; + sta->last_tx = jiffies; + } + + if ((ret == AP_TX_CONTINUE || + ret == AP_TX_CONTINUE_NOT_AUTHORIZED) && + sta->crypt && host_encrypt) { + *crypt = sta->crypt; + *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 hfa384x_tx_frame *txdesc) +{ + struct sta_info *sta; + + spin_lock(&local->ap->sta_table_lock); + /* FIX: is addr1 correct for all frame types? */ + sta = ap_get_sta(local->ap, txdesc->addr1); + if (!sta) { + spin_unlock(&local->ap->sta_table_lock); + PDEBUG(DEBUG_AP, "%s: Could not find STA for this TX error\n", + local->dev->name); + return; + } + + sta->txexc++; + sta->tx_since_last_failure = 0; + if (sta->tx_rate_idx > 0 && txdesc->tx_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); + } + } + 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, stype); + } 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, stype); + if (type != WLAN_FC_TYPE_CTRL || stype != WLAN_FC_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_control field. */ +int hostap_update_sta_ps(local_info_t *local, struct hostap_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_control); + 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, int wds) +{ + int ret; + struct sta_info *sta; + u16 fc, type, stype; + struct hfa384x_rx_frame *rxdesc; + + if (local->ap == NULL) + return AP_RX_CONTINUE; + + rxdesc = (struct hfa384x_rx_frame *) skb->data; + + fc = le16_to_cpu(rxdesc->frame_control); + 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, rxdesc->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) { + local->func->rx_80211(local->apdev, skb, + PRISM2_RX_NON_ASSOC, + NULL, 0); +#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(rxdesc->addr2), type, + stype); + skb->protocol = __constant_htons(ETH_P_HOSTAP); + hostap_rx(dev, skb); +#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(rxdesc->addr1, dev->dev_addr, 6) == 0) { + printk(KERN_DEBUG "Odd.. FromDS packet " + "received with own BSSID\n"); + hostap_dump_rx_header(dev->name, rxdesc); + } + ret = AP_RX_DROP; + goto out; + } + } else if (stype == WLAN_FC_STYPE_NULLFUNC && sta == NULL && + memcmp(rxdesc->addr1, dev->dev_addr, 6) == 0) { + + if (local->hostapd) { + local->func->rx_80211(local->apdev, skb, + PRISM2_RX_NON_ASSOC, NULL, 0); +#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(rxdesc->addr2)); + skb->protocol = __constant_htons(ETH_P_HOSTAP); + hostap_rx(dev, skb); +#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ + } + ret = AP_RX_EXIT; + goto out; + } else if (stype == WLAN_FC_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 { + printk(KERN_DEBUG "%s: dropped received packet from " + MACSTR " with no ToDS flag (type=0x%02x, " + "subtype=0x%02x)\n", dev->name, + MAC2STR(rxdesc->addr2), type, stype); + hostap_dump_rx_header(dev->name, rxdesc); + ret = AP_RX_DROP; + goto out; + } + + if (sta) { + prism2_ap_update_sq(sta, rxdesc); + + hostap_update_sta_ps2(local, sta, fc & WLAN_FC_PWRMGT, + type, stype); + + sta->rx_packets++; + sta->rx_bytes += le16_to_cpu(rxdesc->data_len); + sta->last_rx = jiffies; + } + + if (local->ap->nullfunc_ack && stype == WLAN_FC_STYPE_NULLFUNC && + fc & WLAN_FC_TODS) { + if (local->hostapd) { + local->func->rx_80211(local->apdev, skb, + PRISM2_RX_NULLFUNC_ACK, NULL, 0); +#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 */ + skb->protocol = __constant_htons(ETH_P_HOSTAP); + hostap_rx(dev, skb); +#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 hfa384x_rx_frame *rxdesc, + struct prism2_crypt_data **crypt, void **sta_ptr) +{ + struct sta_info *sta; + + spin_lock(&local->ap->sta_table_lock); + sta = ap_get_sta(local->ap, rxdesc->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_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 hfa384x_rx_frame *rxdesc) +{ + struct sta_info *sta; + + if (!ap || !rxdesc) + return -1; + + spin_lock(&ap->sta_table_lock); + sta = ap_get_sta(ap, rxdesc->addr2); + if (sta) + prism2_ap_update_sq(sta, rxdesc); + 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 prism2_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; + struct add_wds_data *entry; + + 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; + entry = kmalloc(sizeof(*entry), GFP_ATOMIC); + if (!entry) + break; + memcpy(entry->addr, sta->addr, ETH_ALEN); + spin_lock_bh(&local->lock); + entry->next = local->ap->add_wds_entries; + local->ap->add_wds_entries = entry; + spin_unlock_bh(&local->lock); + } + spin_unlock_bh(&ap->sta_table_lock); + + PRISM2_SCHEDULE_TASK(&local->ap->add_wds_queue); +} + + +void hostap_add_wds_link(local_info_t *local, u8 *addr) +{ + struct add_wds_data *entry; + + entry = kmalloc(sizeof(*entry), GFP_ATOMIC); + if (!entry) + return; + memcpy(entry->addr, addr, ETH_ALEN); + spin_lock_bh(&local->lock); + entry->next = local->ap->add_wds_entries; + local->ap->add_wds_entries = entry; + spin_unlock_bh(&local->lock); + + PRISM2_SCHEDULE_TASK(&local->ap->add_wds_queue); +} + + +EXPORT_SYMBOL(hostap_rx); +EXPORT_SYMBOL(hostap_init_data); +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_handle_sta_crypto); +EXPORT_SYMBOL(hostap_is_sta_assoc); +EXPORT_SYMBOL(hostap_add_sta); +EXPORT_SYMBOL(hostap_update_rx_stats); +EXPORT_SYMBOL(hostap_update_rates); +EXPORT_SYMBOL(hostap_add_wds_links); +EXPORT_SYMBOL(hostap_add_wds_link); +#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT +EXPORT_SYMBOL(hostap_deauth_all_stas); +#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ Index: drivers/net/wireless/hostap_ap.h --- drivers/net/wireless/hostap_ap.h.orig 2003-08-12 14:39:16.000000000 -0400 +++ drivers/net/wireless/hostap_ap.h 2003-06-23 18:52:56.000000000 -0400 @@ -0,0 +1,263 @@ +#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 + +/* Flags used in skb->cb[6] to control how the packet is handled in TX path. + * skb->cb[0..5] must contain magic value 'hostap' to indicate that cb[6] is + * used. */ +#define AP_SKB_CB_MAGIC "hostap" +#define AP_SKB_CB_MAGIC_LEN 6 +#define AP_SKB_CB_BUFFERED_FRAME BIT(0) +#define AP_SKB_CB_ADD_MOREDATA BIT(1) + + +/* 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_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 packets */ +#define WLAN_RATE_UPDATE_COUNT 50 + +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 */ + + u8 last_rx_silence; + u8 last_rx_signal; + u8 last_rx_rate; + u8 last_rx_flow; + 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 txexc; + + struct prism2_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 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 (5 * 60 * HZ) +#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; +}; + + +struct add_wds_data { + u8 addr[ETH_ALEN]; + struct add_wds_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 */ + 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; + int auth_algs; /* PRISM2_AUTH_ flags */ + + struct mac_restrictions mac_restrictions; /* MAC-based auth */ + int last_tx_rate; + + HOSTAP_QUEUE set_tim_queue; + struct list_head set_tim_list; + spinlock_t set_tim_lock; + + HOSTAP_QUEUE add_sta_proc_queue; + struct add_sta_proc_data *add_sta_proc_entries; + + HOSTAP_QUEUE add_wds_queue; + struct add_wds_data *add_wds_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; + + /* WEP operations for generating challenges to be used with shared key + * authentication */ + struct hostap_crypto_ops *crypt; + void *crypt_priv; +#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ +}; + + +void hostap_rx(struct net_device *dev, struct sk_buff *skb); +void hostap_init_data(local_info_t *local); +void hostap_free_data(struct ap_data *ap); +void hostap_check_sta_fw_version(struct ap_data *ap, int variant, int major, + int minor); + +typedef enum { + AP_TX_CONTINUE, AP_TX_DROP, AP_TX_RETRY, AP_TX_BUFFERED, + AP_TX_CONTINUE_NOT_AUTHORIZED +} ap_tx_ret; +ap_tx_ret hostap_handle_sta_tx(local_info_t *local, struct sk_buff *skb, + struct hfa384x_tx_frame *txdesc, int wds, + int host_encrypt, + struct prism2_crypt_data **crypt, + void **sta_ptr); +void hostap_handle_sta_release(void *ptr); +void hostap_handle_sta_tx_exc(local_info_t *local, + struct hfa384x_tx_frame *txdesc); +int hostap_update_sta_ps(local_info_t *local, + struct hostap_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, int wds); +int hostap_handle_sta_crypto(local_info_t *local, + struct hfa384x_rx_frame *rxdesc, + struct prism2_crypt_data **crypt, void **sta_ptr); +int hostap_is_sta_assoc(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 hfa384x_rx_frame *rxdesc); +void hostap_update_rates(local_info_t *local); +void hostap_add_wds_links(local_info_t *local); +void hostap_add_wds_link(local_info_t *local, u8 *addr); + +#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 */ Index: drivers/net/wireless/hostap.c --- drivers/net/wireless/hostap.c.orig 2003-08-12 14:39:16.000000000 -0400 +++ drivers/net/wireless/hostap.c 2003-07-19 22:28:19.000000000 -0400 @@ -0,0 +1,1034 @@ +/* + * 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 + * + * Copyright (c) 2002-2003, Jouni Malinen + * + * 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. + */ + +#ifndef EXPORT_SYMTAB +#define EXPORT_SYMTAB +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,44)) +#include +#else +#include +#endif +#include +#include +#if WIRELESS_EXT > 12 +#include +#endif /* WIRELESS_EXT > 12 */ + +#include + + +MODULE_AUTHOR("Jouni Malinen"); +MODULE_DESCRIPTION("Host AP common routines"); +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + +#include "hostap_wlan.h" +#include "hostap_ap.h" +#include "hostap.h" +#include "hostap_crypt.h" + +#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 */ +#ifdef WIRELESS_EXT +static int prism2_ap_get_sta_qual(local_info_t *local, struct sockaddr addr[], + struct iw_quality qual[], int buf_size, + int aplist); +#if WIRELESS_EXT > 13 +static int prism2_ap_translate_scan(struct net_device *dev, char *buffer); +#endif /* WIRELESS_EXT > 13 */ +#endif /* WIRELESS_EXT */ +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 prism2_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 */ + + +#ifdef WIRELESS_EXT +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])) +#endif /* WIRELESS_EXT */ + + +/* 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" + + +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) +{ + prism2_wds_info_t *wds, *wds2 = NULL; + unsigned long flags; + int i, ret; + + spin_lock_irqsave(&local->wdslock, flags); + wds = local->wds; + while (wds != NULL && + memcmp(wds->remote_addr, remote_addr, ETH_ALEN) != 0) { + if (!wds2 && prism2_wds_special_addr(wds->remote_addr)) + wds2 = wds; + wds = wds->next; + } + if (!wds && wds2) { + /* take pre-allocated entry into use */ + memcpy(wds2->remote_addr, remote_addr, ETH_ALEN); + } + spin_unlock_irqrestore(&local->wdslock, flags); + + if (!prism2_wds_special_addr(remote_addr)) { + if (wds) + return -EEXIST; + hostap_add_sta(local->ap, remote_addr); + } + + if (!wds && wds2) { + printk(KERN_DEBUG "%s: using pre-allocated WDS netdevice %s\n", + local->dev->name, wds2->dev.name); + return 0; + } + + 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; + } + + wds = (prism2_wds_info_t *) kmalloc(sizeof(*wds) + PRISM2_NETDEV_EXTRA, + GFP_ATOMIC); + if (wds == NULL) + return -ENOMEM; + + memset(wds, 0, sizeof(*wds) + PRISM2_NETDEV_EXTRA); + prism2_set_dev_name(&wds->dev, wds + 1); + + memcpy(wds->remote_addr, remote_addr, ETH_ALEN); + + hostap_setup_dev(&wds->dev, local, 0); + + wds->dev.priv = local; + memcpy(wds->dev.dev_addr, local->dev->dev_addr, ETH_ALEN); + wds->dev.base_addr = local->dev->base_addr; + wds->dev.irq = local->dev->irq; + wds->dev.mem_start = local->dev->mem_start; + wds->dev.mem_end = local->dev->mem_end; + + i = 0; + do { + sprintf(wds->dev.name, "%swds%d", local->dev->name, i++); + } while (i < 10000 && dev_get(wds->dev.name)); + + if (rtnl_locked) + ret = register_netdevice(&wds->dev); + else + ret = register_netdev(&wds->dev); + + if (ret) { + printk(KERN_WARNING "%s: registering WDS device '%s' failed\n", + local->dev->name, wds->dev.name); + kfree(wds); + return -EINVAL; + } + + spin_lock_irqsave(&local->wdslock, flags); + local->wds_connections++; + wds->next = local->wds; + local->wds = wds; + spin_unlock_irqrestore(&local->wdslock, flags); + + printk(KERN_DEBUG "%s: registered WDS netdevice %s\n", + local->dev->name, wds->dev.name); + + return 0; +} + + +static int prism2_wds_del(local_info_t *local, u8 *remote_addr, + int rtnl_locked, int do_not_remove) +{ + prism2_wds_info_t *wds, *prev = NULL; + unsigned long flags; + + spin_lock_irqsave(&local->wdslock, flags); + wds = local->wds; + while (wds != NULL && + memcmp(wds->remote_addr, remote_addr, ETH_ALEN) != 0) { + prev = wds; + wds = wds->next; + } + if (wds && !do_not_remove) { + if (prev != NULL) + prev->next = wds->next; + else + local->wds = wds->next; + } + spin_unlock_irqrestore(&local->wdslock, flags); + + if (!wds) + return -ENODEV; + + if (do_not_remove) { + memset(wds->remote_addr, 0, ETH_ALEN); + return 0; + } + + if (rtnl_locked) + unregister_netdevice(&wds->dev); + else + unregister_netdev(&wds->dev); + local->wds_connections--; + + printk(KERN_DEBUG "%s: unregistered WDS netdevice %s\n", + local->dev->name, wds->dev.name); + + kfree(wds); + + return 0; +} + + +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) +{ + local_info_t *local = (local_info_t *) dev->priv; + u16 tmp = cpu_to_le16(val); + return local->func->set_rid(dev, rid, &tmp, 2); +} + + +int hostap_set_string(struct net_device *dev, int rid, const char *val) +{ + local_info_t *local = (local_info_t *) dev->priv; + char buf[MAX_SSID_LEN + 2]; + int len; + + 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 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; + return HFA384X_PORTTYPE_HOSTAP; +} + + +int hostap_set_encryption(local_info_t *local) +{ + u16 val; + int i, keylen, len, idx; + char keybuf[WEP_KEY_LEN + 1]; + enum { NONE, WEP, OTHER } encrypt_type; + + if (local->crypt == NULL || local->crypt->ops == NULL) + encrypt_type = NONE; + else if (strcmp(local->crypt->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); + + if (encrypt_type != NONE) + val |= HFA384X_WEPFLAGS_PRIVACYINVOKED; + else + val &= ~HFA384X_WEPFLAGS_PRIVACYINVOKED; + + if (local->open_wep || encrypt_type == NONE || + (local->ieee_802_1x && local->host_decrypt)) + val &= ~HFA384X_WEPFLAGS_EXCLUDEUNENCRYPTED; + else + val |= HFA384X_WEPFLAGS_EXCLUDEUNENCRYPTED; + + if (encrypt_type != NONE && + (encrypt_type == OTHER || local->host_encrypt)) + val |= HFA384X_WEPFLAGS_HOSTENCRYPT; + else + val &= ~HFA384X_WEPFLAGS_HOSTENCRYPT; + if (encrypt_type != NONE && + (encrypt_type == OTHER || local->host_decrypt)) + val |= HFA384X_WEPFLAGS_HOSTDECRYPT; + else + val &= ~HFA384X_WEPFLAGS_HOSTDECRYPT; + + if (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 */ + idx = local->crypt->ops->get_key_idx(local->crypt->priv); + len = local->crypt->ops->get_key(idx, keybuf, sizeof(keybuf), + local->crypt->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)); + (void) local->crypt->ops->get_key(i, keybuf, sizeof(keybuf), + local->crypt->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; +} + + +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), WLAN_FC_GET_STYPE(fc), + __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), WLAN_FC_GET_STYPE(fc), + __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)); +} + + +/* wake all netif queues in use */ +void hostap_netif_wake_queues(struct net_device *dev) +{ + local_info_t *local = (local_info_t *) dev->priv; + prism2_wds_info_t *wds; + unsigned long flags; + + if (local->dev) + netif_wake_queue(local->dev); + if (local->apdev) + netif_wake_queue(local->apdev); + if (local->stadev) + netif_wake_queue(local->stadev); + + spin_lock_irqsave(&local->wdslock, flags); + wds = local->wds; + while (wds != NULL) { + netif_wake_queue(&wds->dev); + wds = wds->next; + } + spin_unlock_irqrestore(&local->wdslock, flags); +} + + +/* stop all netif queues in use */ +void hostap_netif_stop_queues(struct net_device *dev) +{ + local_info_t *local = (local_info_t *) dev->priv; + prism2_wds_info_t *wds; + unsigned long flags; + + if (local->dev) + netif_stop_queue(local->dev); + if (local->apdev) + netif_stop_queue(local->apdev); + if (local->stadev) + netif_stop_queue(local->stadev); + + spin_lock_irqsave(&local->wdslock, flags); + wds = local->wds; + while (wds != NULL) { + netif_stop_queue(&wds->dev); + wds = wds->next; + } + spin_unlock_irqrestore(&local->wdslock, flags); +} + + +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 WLAN_FC_TYPE_DATA: + if ((fc & WLAN_FC_FROMDS) && (fc & WLAN_FC_TODS)) + hdrlen = 30; /* Addr4 */ + break; + case WLAN_FC_TYPE_CTRL: + switch (WLAN_FC_GET_STYPE(fc)) { + case WLAN_FC_STYPE_CTS: + case WLAN_FC_STYPE_ACK: + hdrlen = 10; + break; + default: + hdrlen = 16; + break; + } + break; + } + + return hdrlen; +} + + +struct net_device_stats *hostap_get_stats(struct net_device *dev) +{ + local_info_t *local = (local_info_t *) dev->priv; + if (local->apdev == dev) + return &local->apdevstats; + if (local->stadev == dev) + return &local->stadevstats; + if (local->dev != dev) { + prism2_wds_info_t *wds = (prism2_wds_info_t *) dev; + return &wds->stats; + } + return &local->stats; +} + + +static int prism2_close(struct net_device *dev) +{ + local_info_t *local = (local_info_t *) dev->priv; + + PDEBUG(DEBUG_FLOW, "%s: prism2_close\n", dev->name); + +#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 (local->disable_on_close) { + local->func->hw_shutdown(dev, HOSTAP_HW_ENABLE_CMDCOMPL); + } + + if (netif_running(dev)) { + netif_stop_queue(dev); + netif_device_detach(dev); + } + + PRISM2_FLUSH_SCHEDULED_TASKS(); + +#ifdef NEW_MODULE_CODE + module_put(local->hw_module); +#elif MODULE + __MOD_DEC_USE_COUNT(local->hw_module); +#endif + + return 0; +} + + +static int prism2_open(struct net_device *dev) +{ + local_info_t *local = (local_info_t *) dev->priv; + + PDEBUG(DEBUG_FLOW, "%s: prism2_open\n", dev->name); + + if (local->func->dev_open && local->func->dev_open(local)) + return 1; + +#ifdef NEW_MODULE_CODE + if (!try_module_get(local->hw_module)) + return -ENODEV; +#elif MODULE + __MOD_INC_USE_COUNT(local->hw_module); +#endif + + 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; + + netif_device_attach(dev); + netif_start_queue(dev); + + return 0; +} + + +#ifdef HAVE_SET_MAC_ADDR +static int prism2_set_mac_address(struct net_device *dev, void *p) +{ + struct sockaddr *addr = p; + local_info_t *local = (local_info_t *) dev->priv; + prism2_wds_info_t *wds; + unsigned long flags; + + if (local->func->set_rid(dev, HFA384X_RID_CNFOWNMACADDR, addr->sa_data, + ETH_ALEN) < 0 || local->func->reset_port(dev)) + return -EINVAL; + + dev = local->dev; + memcpy(dev->dev_addr, addr->sa_data, dev->addr_len); + if (local->apdev) + memcpy(local->apdev->dev_addr, dev->dev_addr, ETH_ALEN); + if (local->stadev) + memcpy(local->stadev->dev_addr, dev->dev_addr, ETH_ALEN); + spin_lock_irqsave(&local->wdslock, flags); + wds = local->wds; + while (wds != NULL) { + memcpy(wds->dev.dev_addr, dev->dev_addr, ETH_ALEN); + wds = wds->next; + } + spin_unlock_irqrestore(&local->wdslock, flags); + + return 0; +} +#endif /* HAVE_SET_MAC_ADDR */ + + +/* 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; + local_info_t *local = (local_info_t *) dev->priv; + + 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"); + } + +#ifndef NEW_MODULE_CODE + MOD_DEC_USE_COUNT; +#endif +} + + +static void hostap_set_multicast_list(struct net_device *dev) +{ +#if 1 + /* 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. */ + local_info_t *local = (local_info_t *) dev->priv; + + if ((dev->flags & IFF_ALLMULTI) || (dev->flags & IFF_PROMISC)) { + local->is_promisc = 1; + } else { + local->is_promisc = 0; + } + + PRISM2_SCHEDULE_TASK(&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; +} + + +#ifdef HAVE_TX_TIMEOUT +static void prism2_tx_timeout(struct net_device *dev) +{ + local_info_t *local = (local_info_t *) dev->priv; + struct hfa384x_regs regs; + + printk(KERN_WARNING "%s Tx timed out! Resetting card\n", dev->name); + hostap_netif_stop_queues(dev); + + local->func->read_regs(dev, ®s); + 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); +} +#endif /* HAVE_TX_TIMEOUT */ + + +void hostap_setup_dev(struct net_device *dev, local_info_t *local, + int main_dev) +{ + ether_setup(dev); + + /* kernel callbacks */ + dev->get_stats = hostap_get_stats; +#ifdef WIRELESS_EXT + dev->get_wireless_stats = main_dev ? hostap_get_wireless_stats : NULL; +#if WIRELESS_EXT > 12 + dev->wireless_handlers = + (struct iw_handler_def *) &hostap_iw_handler_def; +#endif /* WIRELESS_EXT > 12 */ +#endif /* WIRELESS_EXT */ + dev->open = prism2_open; + dev->stop = prism2_close; + if (local->func) + dev->hard_start_xmit = local->func->tx; + else + printk(KERN_WARNING "hostap_setup_dev: local->func == NULL\n"); +#ifdef HAVE_SET_MAC_ADDR + dev->set_mac_address = prism2_set_mac_address; +#endif /* HAVE_SET_MAC_ADDR */ +#ifdef HAVE_MULTICAST + dev->set_multicast_list = hostap_set_multicast_list; +#endif +#ifdef HAVE_PRIVATE_IOCTL + dev->do_ioctl = main_dev ? hostap_ioctl : NULL; +#endif +#ifdef HAVE_CHANGE_MTU + dev->change_mtu = prism2_change_mtu; +#endif +#ifdef HAVE_TX_TIMEOUT + dev->tx_timeout = prism2_tx_timeout; + dev->watchdog_timeo = TX_TIMEOUT; +#endif + + dev->mtu = local->mtu; + + netif_stop_queue(dev); +} + + +static int hostap_enable_hostapd(local_info_t *local, int rtnl_locked) +{ + struct net_device *dev = local->dev; + int ret; + + printk(KERN_DEBUG "%s: enabling hostapd mode\n", dev->name); + + local->apdev = kmalloc(sizeof(struct net_device) + PRISM2_NETDEV_EXTRA, + GFP_KERNEL); + if (local->apdev == NULL) + return -ENOMEM; + memset(local->apdev, 0, sizeof(struct net_device) + + PRISM2_NETDEV_EXTRA); + prism2_set_dev_name(local->apdev, local->apdev + 1); + + local->apdev->priv = local; + memcpy(local->apdev->dev_addr, dev->dev_addr, ETH_ALEN); + hostap_setup_dev(local->apdev, local, 0); + local->apdev->hard_start_xmit = local->func->tx_80211; + local->apdev->type = ARPHRD_IEEE80211; + local->apdev->hard_header_parse = hostap_80211_header_parse; + + local->apdev->base_addr = dev->base_addr; + local->apdev->irq = dev->irq; + local->apdev->mem_start = dev->mem_start; + local->apdev->mem_end = dev->mem_end; + sprintf(local->apdev->name, "%sap", dev->name); + if (rtnl_locked) + ret = register_netdevice(local->apdev); + else + ret = register_netdev(local->apdev); + if (ret) { + printk(KERN_WARNING "%s: register_netdevice(AP) failed!\n", + dev->name); + return -1; + } + printk(KERN_DEBUG "%s: Registered netdevice %s for AP management\n", + dev->name, local->apdev->name); + + + local->stadev = kmalloc(sizeof(struct net_device) + + PRISM2_NETDEV_EXTRA, + GFP_KERNEL); + if (local->stadev == NULL) + return -ENOMEM; + memset(local->stadev, 0, sizeof(struct net_device) + + PRISM2_NETDEV_EXTRA); + prism2_set_dev_name(local->stadev, local->stadev + 1); + + local->stadev->priv = local; + memcpy(local->stadev->dev_addr, dev->dev_addr, ETH_ALEN); + hostap_setup_dev(local->stadev, local, 0); + + local->stadev->base_addr = dev->base_addr; + local->stadev->irq = dev->irq; + local->stadev->mem_start = dev->mem_start; + local->stadev->mem_end = dev->mem_end; + sprintf(local->stadev->name, "%ssta", dev->name); + if (rtnl_locked) + ret = register_netdevice(local->stadev); + else + ret = register_netdev(local->stadev); + if (ret) { + printk(KERN_WARNING "%s: register_netdevice(STA) failed!\n", + dev->name); + return -1; + } + printk(KERN_DEBUG "%s: Registered netdevice %s for STA use\n", + dev->name, local->stadev->name); + + 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); + + if (local->apdev && local->apdev->name && local->apdev->name[0]) { + if (rtnl_locked) + unregister_netdevice(local->apdev); + else + unregister_netdev(local->apdev); + printk(KERN_DEBUG "%s: Netdevice %s unregistered\n", + dev->name, local->apdev->name); + } + kfree(local->apdev); + local->apdev = NULL; + + if (local->stadev && local->stadev->name && local->stadev->name[0]) { + if (rtnl_locked) + unregister_netdevice(local->stadev); + else + unregister_netdev(local->stadev); + printk(KERN_DEBUG "%s: Netdevice %s unregistered\n", + dev->name, local->stadev->name); + } + kfree(local->stadev); + local->stadev = NULL; + + return 0; +} + + +int hostap_set_hostapd(local_info_t *local, int val, int rtnl_locked) +{ + if (val < 0 || val > 1) + return -EINVAL; + + if (local->hostapd == val) + return 0; + + local->hostapd = val; + + if (val) + return hostap_enable_hostapd(local, rtnl_locked); + else + return hostap_disable_hostapd(local, rtnl_locked); +} + + +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_dump_rx_header); +EXPORT_SYMBOL(hostap_dump_tx_header); +EXPORT_SYMBOL(hostap_netif_wake_queues); +EXPORT_SYMBOL(hostap_netif_stop_queues); +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); +module_init(hostap_init); +module_exit(hostap_exit); Index: drivers/net/wireless/hostap_compat.h --- drivers/net/wireless/hostap_compat.h.orig 2003-08-12 14:39:16.000000000 -0400 +++ drivers/net/wireless/hostap_compat.h 2003-06-23 18:52:56.000000000 -0400 @@ -0,0 +1,153 @@ +#ifndef HOSTAP_COMPAT_H +#define HOSTAP_COMPAT_H + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,47)) +#define NEW_MODULE_CODE +#endif + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)) +/* 2.2 compatibility */ + +#include +#include +#include + +#ifndef spin_lock_bh +#define spin_lock_bh(lock) spin_lock_irq(lock) +#define spin_unlock_bh(lock) spin_unlock_irq(lock) +#endif +#ifndef __constant_cpu_to_le16 +#define __constant_cpu_to_le16 __cpu_to_le16 +#endif + +#define PRISM2_NETDEV_EXTRA IFNAMSIZ +#define prism2_set_dev_name(dev, pos) (dev)->name = (char *) (pos) + +#define HOSTAP_QUEUE struct tq_struct + +/* tq_scheduler was removed in 2.4.0-test12 */ +#define PRISM2_SCHEDULE_TASK(q) \ +MOD_INC_USE_COUNT; \ +queue_task((q), &tq_scheduler); + +#define PRISM2_FLUSH_SCHEDULED_TASKS() do { schedule(); schedule(); } while (0) + +static inline void HOSTAP_QUEUE_INIT(struct tq_struct *tq, + void (*routine)(void *), void *data) +{ + tq->next = NULL; + tq->sync = 0; + tq->routine = routine; + tq->data = data; +} + +#define HOSTAP_TASKLET struct tq_struct + +#define HOSTAP_TASKLET_SCHEDULE(q) \ +do { queue_task((q), &tq_immediate); mark_bh(IMMEDIATE_BH); } while (0) + +typedef void (*tasklet_func)(void *); + +#define HOSTAP_TASKLET_INIT(q, f, d) \ +do { memset((q), 0, sizeof(*(q))); \ +(q)->routine = (tasklet_func) (f); \ +(q)->data = (void *) (d); } \ +while (0) + + +static inline void dev_kfree_skb_any(struct sk_buff *skb) +{ + if (in_interrupt()) + dev_kfree_skb_irq(skb); + else + dev_kfree_skb(skb); +} + +static __inline__ void list_del_init(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); + INIT_LIST_HEAD(entry); +} + +/* end 2.2 compatibility */ + +#else /* kernel < 2.4.0 */ + +/* no extra space needed for 2.4.x net_device */ +#define PRISM2_NETDEV_EXTRA 0 +#define prism2_set_dev_name(dev, pos) do { } while (0) + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,44)) + +#define HOSTAP_QUEUE struct tq_struct + +#define PRISM2_SCHEDULE_TASK(q) \ +MOD_INC_USE_COUNT; \ +if (schedule_task((q)) == 0) \ + MOD_DEC_USE_COUNT; + +#define PRISM2_FLUSH_SCHEDULED_TASKS() flush_scheduled_tasks() + +static inline void HOSTAP_QUEUE_INIT(struct tq_struct *tq, + void (*routine)(void *), void *data) +{ + INIT_LIST_HEAD(&tq->list); + tq->sync = 0; + tq->routine = routine; + tq->data = data; +} + +#else /* kernel < 2.5.44 */ + +#define HOSTAP_QUEUE struct work_struct + +#ifdef NEW_MODULE_CODE +#define PRISM2_SCHEDULE_TASK(q) schedule_work(q); +#else /* NEW_MODULE_CODE */ +#define PRISM2_SCHEDULE_TASK(q) \ +MOD_INC_USE_COUNT; \ +if (schedule_work((q)) == 0) \ + MOD_DEC_USE_COUNT; +#endif /* NEW_MODULE_CODE */ + +#define PRISM2_FLUSH_SCHEDULED_TASKS() flush_scheduled_work() + +static inline void HOSTAP_QUEUE_INIT(struct work_struct *wq, + void (*routine)(void *), void *data) +{ + INIT_WORK(wq, routine, data); +} + +#endif /* kernel < 2.5.44 */ + +#define HOSTAP_TASKLET struct tasklet_struct + +#define HOSTAP_TASKLET_SCHEDULE(q) tasklet_schedule((q)) + +#define HOSTAP_TASKLET_INIT(q, f, d) \ +do { memset((q), 0, sizeof(*(q))); (q)->func = (f); (q)->data = (d); } \ +while (0) + +#endif /* kernel < 2.4.0 */ + + +/* Interrupt handler backwards compatibility for Linux < 2.5.69 */ +#ifndef IRQ_NONE +#define IRQ_NONE +#define IRQ_HANDLED +#define IRQ_RETVAL(x) +typedef void irqreturn_t; +#endif + + +#if WIRELESS_EXT > 12 +#if IW_HANDLER_VERSION < 3 +extern void wireless_send_event(struct net_device *dev, + unsigned int cmd, + union iwreq_data *wrqu, + char *extra); +#endif /* IW_HANDLER_VERSION < 3 */ +#endif /* WIRELESS_EXT > 12 */ + + +#endif /* HOSTAP_COMPAT_H */ Index: drivers/net/wireless/hostap_config.h --- drivers/net/wireless/hostap_config.h.orig 2003-08-12 14:39:16.000000000 -0400 +++ drivers/net/wireless/hostap_config.h 2003-08-08 22:39:41.000000000 -0400 @@ -0,0 +1,92 @@ +#ifndef HOSTAP_CONFIG_H +#define HOSTAP_CONFIG_H + +#define PRISM2_VERSION "0.0.3 - 2003-05-18" + +/* 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 + +/* Use PCI bus master to copy data to/from BAP (only available for + * hostap_pci.o). + * + * Note! This is extremely experimental. PCI bus master is not supported by + * Intersil and it seems to have some problems at least on TX path (see below). + * The driver code for implementing bus master support is based on guessing + * and experimenting suitable control bits and these might not be correct. + * This code is included because using bus master makes a huge difference in + * host CPU load (something like 40% host CPU usage to 5-10% when sending or + * receiving at maximum throughput). + * + * Note2! Station firmware version 1.3.5 and primary firmware version 1.0.7 + * have some fixes for PCI corruption and these (or newer) versions are + * recommended especially when using bus mastering. */ +/* #define PRISM2_BUS_MASTER */ + +#ifdef PRISM2_BUS_MASTER + +/* PCI bus master implementation seems to be broken in current + * hardware/firmware versions. Enable this to use enable command to fix + * something before starting bus master operation on TX path. This will add + * some latency and an extra interrupt to each TX packet. */ +#define PRISM2_ENABLE_BEFORE_TX_BUS_MASTER + +#endif /* PRISM2_BUS_MASTER */ + +/* Include code for downloading firmware images. */ +/* #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 */ + +/* Include wireless extensions sub-ioctl support even if wireless extensions + * version is less than 15 (actually, only if it is 12 .. 14). If ver >= 15, + * these will be included. Please note, that this requires iwpriv version 25 + * or higher (older versions will segfault due to long ioctl list). */ +#define PRISM2_USE_WE_SUB_IOCTLS + +/* Use IW_PRIV_TYPE_ADDR with private WE ioctls taking MAC address argument + * (instead of old 18*char). This requires iwpriv ver >= 25. This will be + * automatically included for WIRELESS_EXT >= 15. */ +/* #define PRISM2_USE_WE_TYPE_ADDR */ + +/* 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 */ Index: drivers/net/wireless/hostap_crypt.c --- drivers/net/wireless/hostap_crypt.c.orig 2003-08-12 14:39:16.000000000 -0400 +++ drivers/net/wireless/hostap_crypt.c 2003-06-23 18:52:56.000000000 -0400 @@ -0,0 +1,190 @@ +/* + * Host AP crypto routines + * + * Copyright (c) 2002, Jouni Malinen + * + * 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. + */ + +#ifndef EXPORT_SYMTAB +#define EXPORT_SYMTAB +#endif + +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Jouni Malinen"); +MODULE_DESCRIPTION("Host AP crypto"); +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + +#include "hostap_crypt.h" + + +struct hostap_crypto_alg { + struct list_head list; + struct hostap_crypto_ops *ops; +}; + + +struct hostap_crypto { + struct list_head algs; + spinlock_t lock; +}; + +static struct hostap_crypto *hcrypt; + + +int hostap_register_crypto_ops(struct hostap_crypto_ops *ops) +{ + unsigned long flags; + struct hostap_crypto_alg *alg; + + if (hcrypt == NULL) + return -1; + + alg = (struct hostap_crypto_alg *) kmalloc(sizeof(*alg), GFP_KERNEL); + if (alg == NULL) + return -ENOMEM; + + memset(alg, 0, sizeof(*alg)); + alg->ops = ops; + + spin_lock_irqsave(&hcrypt->lock, flags); + list_add(&alg->list, &hcrypt->algs); + spin_unlock_irqrestore(&hcrypt->lock, flags); + + printk(KERN_DEBUG "hostap_crypt: registered algorithm '%s'\n", + ops->name); + + return 0; +} + + +int hostap_unregister_crypto_ops(struct hostap_crypto_ops *ops) +{ + unsigned long flags; + struct list_head *ptr; + struct hostap_crypto_alg *del_alg = NULL; + + if (hcrypt == NULL) + return -1; + + spin_lock_irqsave(&hcrypt->lock, flags); + for (ptr = hcrypt->algs.next; ptr != &hcrypt->algs; ptr = ptr->next) { + struct hostap_crypto_alg *alg = + (struct hostap_crypto_alg *) ptr; + if (alg->ops == ops) { + list_del(&alg->list); + del_alg = alg; + break; + } + } + spin_unlock_irqrestore(&hcrypt->lock, flags); + + if (del_alg) { + printk(KERN_DEBUG "hostap_crypt: unregistered algorithm " + "'%s'\n", ops->name); + kfree(del_alg); + } + + return del_alg ? 0 : -1; +} + + +struct hostap_crypto_ops * hostap_get_crypto_ops(const char *name) +{ + unsigned long flags; + struct list_head *ptr; + struct hostap_crypto_alg *found_alg = NULL; + + if (hcrypt == NULL) + return NULL; + + spin_lock_irqsave(&hcrypt->lock, flags); + for (ptr = hcrypt->algs.next; ptr != &hcrypt->algs; ptr = ptr->next) { + struct hostap_crypto_alg *alg = + (struct hostap_crypto_alg *) ptr; + if (strcmp(alg->ops->name, name) == 0) { + found_alg = alg; + break; + } + } + spin_unlock_irqrestore(&hcrypt->lock, flags); + + if (found_alg) + return found_alg->ops; + else + return NULL; +} + + +static void * hostap_crypt_null_init(void) { return (void *) 1; } +static void hostap_crypt_null_deinit(void *priv) {} + +static struct hostap_crypto_ops hostap_crypt_null = { + .name = "NULL", + .init = hostap_crypt_null_init, + .deinit = hostap_crypt_null_deinit, + .encrypt = NULL, + .decrypt = NULL, + .set_key = NULL, + .get_key = NULL, + .set_key_idx = NULL, + .get_key_idx = NULL, + .extra_prefix_len = 0, + .extra_postfix_len = 0 +}; + + +static int __init hostap_crypto_init(void) +{ + hcrypt = (struct hostap_crypto *) kmalloc(sizeof(*hcrypt), GFP_KERNEL); + if (hcrypt == NULL) + return -ENOMEM; + + memset(hcrypt, 0, sizeof(*hcrypt)); + INIT_LIST_HEAD(&hcrypt->algs); + spin_lock_init(&hcrypt->lock); + + (void) hostap_register_crypto_ops(&hostap_crypt_null); + + return 0; +} + + +static void __exit hostap_crypto_deinit(void) +{ + struct list_head *ptr, *n; + + if (hcrypt == NULL) + return; + + for (ptr = hcrypt->algs.next, n = ptr->next; ptr != &hcrypt->algs; + ptr = n, n = ptr->next) { + struct hostap_crypto_alg *alg = + (struct hostap_crypto_alg *) ptr; + list_del(ptr); + printk(KERN_DEBUG "hostap_crypt: unregistered algorithm " + "'%s' (deinit)\n", alg->ops->name); + kfree(alg); + } + + kfree(hcrypt); +} + + +EXPORT_SYMBOL(hostap_register_crypto_ops); +EXPORT_SYMBOL(hostap_unregister_crypto_ops); +EXPORT_SYMBOL(hostap_get_crypto_ops); + +module_init(hostap_crypto_init); +module_exit(hostap_crypto_deinit); Index: drivers/net/wireless/hostap_crypt.h --- drivers/net/wireless/hostap_crypt.h.orig 2003-08-12 14:39:16.000000000 -0400 +++ drivers/net/wireless/hostap_crypt.h 2003-06-09 13:30:26.000000000 -0400 @@ -0,0 +1,44 @@ +#ifndef PRISM2_CRYPT_H +#define PRISM2_CRYPT_H + +/* Maximum length for algorithm names (-1 for nul termination) used in ioctl() + */ +#define HOSTAP_CRYPT_ALG_NAME_LEN 16 + + +struct hostap_crypto_ops { + char *name; + + /* init new crypto context (e.g., allocate private data space, + * select IV, etc.); returns NULL on failure or pointer to allocated + * private data on success */ + void * (*init)(void); + + /* deinitialize crypto context and free allocated private data */ + void (*deinit)(void *priv); + + /* encrypt/decrypt return < 0 on error or number of bytes written + * to out_buf; len is number of bytes in in_buf */ + int (*encrypt)(u8 *buf, int len, void *priv); + int (*decrypt)(u8 *buf, int len, void *priv); + + int (*set_key)(int idx, void *key, int len, void *priv); + int (*get_key)(int idx, void *key, int len, void *priv); + + int (*set_key_idx)(int idx, void *priv); + int (*get_key_idx)(void *priv); + + /* maximum number of bytes added by encryption; encrypt buf is + * allocated with extra_prefix_len bytes, copy of in_buf, and + * extra_postfix_len; encrypt need not use all this space, but + * the result must start at the beginning of the buffer and correct + * length must be returned */ + int extra_prefix_len, extra_postfix_len; +}; + + +int hostap_register_crypto_ops(struct hostap_crypto_ops *ops); +int hostap_unregister_crypto_ops(struct hostap_crypto_ops *ops); +struct hostap_crypto_ops * hostap_get_crypto_ops(const char *name); + +#endif /* PRISM2_CRYPT_H */ Index: drivers/net/wireless/hostap_crypt_wep.c --- drivers/net/wireless/hostap_crypt_wep.c.orig 2003-08-12 14:39:16.000000000 -0400 +++ drivers/net/wireless/hostap_crypt_wep.c 2003-06-23 18:52:56.000000000 -0400 @@ -0,0 +1,366 @@ +/* + * Host AP crypt: host-based WEP encryption implementation for Host AP driver + * + * Copyright (c) 2002, Jouni Malinen + * + * 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 +#include +#include +#include +#include +#include +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,44)) +#include +#else +#include +#endif +#include + +MODULE_AUTHOR("Jouni Malinen"); +MODULE_DESCRIPTION("Host AP crypt: WEP"); +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + +#include "hostap_crypt.h" +#include "hostap_compat.h" + + +struct prism2_wep_data { + u32 iv; +#define WEP_KEYS 4 +#define WEP_KEY_LEN 13 + u8 keys[WEP_KEYS][WEP_KEY_LEN + 1]; + u8 key_lens[WEP_KEYS]; + int tx_key; +}; + +static const __u32 crc32_table[256] = { + 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, + 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L, + 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, + 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, + 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, + 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, + 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, + 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, + 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, + 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL, + 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, + 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, + 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L, + 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, + 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, + 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L, + 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, + 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L, + 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, + 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, + 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL, + 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, + 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L, + 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, + 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, + 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L, + 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L, + 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, + 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L, + 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL, + 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, + 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, + 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, + 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL, + 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, + 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, + 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL, + 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, + 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, + 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L, + 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL, + 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L, + 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, + 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, + 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L, + 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, + 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, + 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, + 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, + 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L, + 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, + 0x2d02ef8dL +}; + + +static void * prism2_wep_init(void) +{ + struct prism2_wep_data *priv; + +#ifndef NEW_MODULE_CODE + MOD_INC_USE_COUNT; +#endif + + priv = (struct prism2_wep_data *) kmalloc(sizeof(*priv), GFP_ATOMIC); + if (priv == NULL) { +#ifndef NEW_MODULE_CODE + MOD_DEC_USE_COUNT; +#endif + return NULL; + } + memset(priv, 0, sizeof(*priv)); + + /* start WEP IV from a random value */ + get_random_bytes(&priv->iv, 4); + + return priv; +} + + +static void prism2_wep_deinit(void *priv) +{ + kfree(priv); +#ifndef NEW_MODULE_CODE + MOD_DEC_USE_COUNT; +#endif +} + + +/* Perform WEP encryption on given buffer. Buffer needs to has 4 bytes of + * extra space (IV) in the beginning, then len bytes of data, and finally + * 4 bytes of extra space (ICV). Both IV and ICV will be transmitted, so the + * payload length increases with 8 bytes. + * + * WEP frame payload: IV + TX key idx, RC4(data), ICV = RC4(CRC32(data)) + */ +static int prism2_wep_encrypt(u8 *buf, int len, void *priv) +{ + struct prism2_wep_data *wep = priv; + u32 i, j, k, crc, klen; + u8 S[256], key[WEP_KEY_LEN + 3]; + u8 kpos, *pos; +#define S_SWAP(a,b) do { u8 t = S[a]; S[a] = S[b]; S[b] = t; } while(0) + + klen = 3 + wep->key_lens[wep->tx_key]; + + wep->iv++; + + /* Fluhrer, Mantin, and Shamir have reported weaknesses in the key + * scheduling algorithm of RC4. At least IVs (KeyByte + 3, 0xff, N) + * can be used to speedup attacks, so avoid using them. */ + if ((wep->iv & 0xff00) == 0xff00) { + u8 B = (wep->iv >> 16) & 0xff; + if (B >= 3 && B < klen) + wep->iv += 0x0100; + } + + /* Prepend 24-bit IV to RC4 key and TX frame */ + pos = buf; + *pos++ = key[0] = (wep->iv >> 16) & 0xff; + *pos++ = key[1] = (wep->iv >> 8) & 0xff; + *pos++ = key[2] = wep->iv & 0xff; + *pos++ = wep->tx_key << 6; + + /* Copy rest of the WEP key (the secret part) */ + memcpy(key + 3, wep->keys[wep->tx_key], + wep->key_lens[wep->tx_key]); + + /* Setup RC4 state */ + for (i = 0; i < 256; i++) + S[i] = i; + j = 0; + kpos = 0; + for (i = 0; i < 256; i++) { + j = (j + S[i] + key[kpos]) & 0xff; + kpos++; + if (kpos >= klen) + kpos = 0; + S_SWAP(i, j); + } + + /* Compute CRC32 over unencrypted data and apply RC4 to data */ + crc = ~0; + i = j = 0; + for (k = 0; k < len; k++) { + crc = crc32_table[(crc ^ *pos) & 0xff] ^ (crc >> 8); + i = (i + 1) & 0xff; + j = (j + S[i]) & 0xff; + S_SWAP(i, j); + *pos++ ^= S[(S[i] + S[j]) & 0xff]; + } + crc = ~crc; + + /* Append little-endian CRC32 and encrypt it to produce ICV */ + pos[0] = crc; + pos[1] = crc >> 8; + pos[2] = crc >> 16; + pos[3] = crc >> 24; + for (k = 0; k < 4; k++) { + i = (i + 1) & 0xff; + j = (j + S[i]) & 0xff; + S_SWAP(i, j); + *pos++ ^= S[(S[i] + S[j]) & 0xff]; + } + + return len + 8; +} + + +/* Perform WEP decryption on given buffer. Buffer includes whole WEP part of + * the frame: IV (4 bytes), encrypted payload (including SNAP header), + * ICV (4 bytes). len includes both IV and ICV. + * + * Returns 0 if frame was decrypted successfully and ICV was correct and -1 on + * failure. If frame is OK, IV and ICV will be removed, i.e., decrypted payload + * is moved to beginning of buf and last 8 bytes of buf should be ignored. + */ +static int prism2_wep_decrypt(u8 *buf, int len, void *priv) +{ + struct prism2_wep_data *wep = priv; + u32 i, j, k, crc, klen; + u8 S[256], key[WEP_KEY_LEN + 3]; + u8 keyidx, kpos, *dpos, *cpos; + + if (len < 8) + return -1; + + key[0] = buf[0]; + key[1] = buf[1]; + key[2] = buf[2]; + keyidx = buf[3] >> 6; + + klen = 3 + wep->key_lens[keyidx]; + + /* Copy rest of the WEP key (the secret part) */ + memcpy(key + 3, wep->keys[keyidx], wep->key_lens[keyidx]); + + /* Setup RC4 state */ + for (i = 0; i < 256; i++) + S[i] = i; + j = 0; + kpos = 0; + for (i = 0; i < 256; i++) { + j = (j + S[i] + key[kpos]) & 0xff; + kpos++; + if (kpos >= klen) + kpos = 0; + S_SWAP(i, j); + } + + /* Apply RC4 to data and compute CRC32 over decrypted data */ + dpos = buf; + cpos = buf + 4; + crc = ~0; + i = j = 0; + for (k = 0; k < len - 8; k++) { + i = (i + 1) & 0xff; + j = (j + S[i]) & 0xff; + S_SWAP(i, j); + *dpos = *cpos++ ^ S[(S[i] + S[j]) & 0xff]; + crc = crc32_table[(crc ^ *dpos++) & 0xff] ^ (crc >> 8); + } + crc = ~crc; + + /* Encrypt little-endian CRC32 and verify that it matches with the + * received ICV */ + dpos[0] = crc; + dpos[1] = crc >> 8; + dpos[2] = crc >> 16; + dpos[3] = crc >> 24; + for (k = 0; k < 4; k++) { + i = (i + 1) & 0xff; + j = (j + S[i]) & 0xff; + S_SWAP(i, j); + if ((*dpos++ ^ S[(S[i] + S[j]) & 0xff]) != *cpos++) { + /* ICV mismatch - drop frame */ + return -1; + } + } + + return len - 8; +} + + +static int prism2_wep_set_key(int idx, void *key, int len, void *priv) +{ + struct prism2_wep_data *wep = priv; + + if (idx < 0 || idx >= WEP_KEYS || len < 0 || len > WEP_KEY_LEN) + return -1; + + memcpy(wep->keys[idx], key, len); + wep->key_lens[idx] = len; + + return 0; +} + + +static int prism2_wep_get_key(int idx, void *key, int len, void *priv) +{ + struct prism2_wep_data *wep = priv; + + if (idx < 0 || idx >= WEP_KEYS || len < wep->key_lens[idx]) + return -1; + + memcpy(key, wep->keys[idx], wep->key_lens[idx]); + + return wep->key_lens[idx]; +} + + +static int prism2_wep_set_key_idx(int idx, void *priv) +{ + struct prism2_wep_data *wep = priv; + + if (idx < 0 || idx >= WEP_KEYS || wep->key_lens[idx] == 0) + return -1; + + wep->tx_key = idx; + + return 0; +} + + +static int prism2_wep_get_key_idx(void *priv) +{ + struct prism2_wep_data *wep = priv; + return wep->tx_key; +} + + +static struct hostap_crypto_ops hostap_crypt_wep = { + .name = "WEP", + .init = prism2_wep_init, + .deinit = prism2_wep_deinit, + .encrypt = prism2_wep_encrypt, + .decrypt = prism2_wep_decrypt, + .set_key = prism2_wep_set_key, + .get_key = prism2_wep_get_key, + .set_key_idx = prism2_wep_set_key_idx, + .get_key_idx = prism2_wep_get_key_idx, + .extra_prefix_len = 4 /* IV */, + .extra_postfix_len = 4 /* ICV */ +}; + + +static int __init hostap_crypto_wep_init(void) +{ + if (hostap_register_crypto_ops(&hostap_crypt_wep) < 0) + return -1; + + return 0; +} + + +static void __exit hostap_crypto_wep_exit(void) +{ + hostap_unregister_crypto_ops(&hostap_crypt_wep); +} + + +module_init(hostap_crypto_wep_init); +module_exit(hostap_crypto_wep_exit); Index: drivers/net/wireless/hostap_cs.c --- drivers/net/wireless/hostap_cs.c.orig 2003-08-12 14:39:16.000000000 -0400 +++ drivers/net/wireless/hostap_cs.c 2003-06-23 18:52:56.000000000 -0400 @@ -0,0 +1,805 @@ +#define PRISM2_PCCARD + +#include +#ifdef __IN_PCMCIA_PACKAGE__ +#include +#endif /* __IN_PCMCIA_PACKAGE__ */ +#include +#include +#include +#include +#include +#include +#include +#include +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,44)) +#include +#else +#include +#endif +#include +#if WIRELESS_EXT > 12 +#include +#endif /* WIRELESS_EXT > 12 */ + +#include +#include +#include +#include +#include +#include + +#include + +#include "hostap_wlan.h" + + +#ifdef __IN_PCMCIA_PACKAGE__ +#include +#endif /* __IN_PCMCIA_PACKAGE__ */ + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)) +/* This behavior changed in some Linux 2.5.x version. I don't remember when and + * don't really care if this does not work with some early 2.5.x versions.. + */ +#define HOSTAP_USE_RELEASE_TIMER +#endif + + +static char *version = PRISM2_VERSION " (Jouni Malinen )"; +static dev_info_t dev_info = "hostap_cs"; +static dev_link_t *dev_list = NULL; + +MODULE_AUTHOR("SSH Communications Security Corp, 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)"); +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + + +static unsigned int irq_mask = 0xdeb8; +MODULE_PARM(irq_mask, "i"); + +static int irq_list[4] = { -1 }; +MODULE_PARM(irq_list, "1-4i"); + +static int ignore_cis_vcc = 0; +MODULE_PARM(ignore_cis_vcc, "i"); +MODULE_PARM_DESC(ignore_cis_vcc, "Ignore broken CIS VCC entry"); + + +#ifdef PRISM2_IO_DEBUG + +static inline void hfa384x_outb_debug(struct net_device *dev, int a, u8 v) +{ + local_info_t *local = dev->priv; + unsigned long flags; + + 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) +{ + local_info_t *local = dev->priv; + unsigned long flags; + u8 v; + + 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) +{ + local_info_t *local = dev->priv; + unsigned long flags; + + 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) +{ + local_info_t *local = dev->priv; + unsigned long flags; + u16 v; + + 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) +{ + local_info_t *local = dev->priv; + unsigned long flags; + + 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) +{ + local_info_t *local = dev->priv; + unsigned long flags; + + 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) +{ + if (local->link != NULL && + ((local->link->state & (DEV_PRESENT | DEV_CONFIG)) == + (DEV_PRESENT | DEV_CONFIG))) + return 1; + return 0; +} + +static void prism2_pccard_cor_sreset(local_info_t *local) +{ + int res; + conf_reg_t reg; + + reg.Function = 0; + reg.Action = CS_READ; + reg.Offset = CISREG_COR; + reg.Value = 0; + res = CardServices(AccessConfigurationRegister, local->link->handle, + ®); + 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 = CardServices(AccessConfigurationRegister, local->link->handle, + ®); + if (res != CS_SUCCESS) { + printk(KERN_DEBUG "prism2_pccard_cor_sreset failed 2 (%d)\n", + res); + return; + } + + mdelay(1); + + reg.Value &= ~COR_SOFT_RESET; + res = CardServices(AccessConfigurationRegister, local->link->handle, + ®); + if (res != CS_SUCCESS) { + printk(KERN_DEBUG "prism2_pccard_cor_sreset failed 3 (%d)\n", + res); + return; + } + + mdelay(1); +} + + +static int prism2_pccard_dev_open(local_info_t *local) +{ + local->link->open++; + return 0; +} + + +static int prism2_pccard_dev_close(local_info_t *local) +{ + if (local == NULL || local->link == NULL) + return 1; + + if (!local->link->open) { + printk(KERN_WARNING "%s: prism2_pccard_dev_close(): " + "link not open?!\n", local->dev->name); + return 1; + } + + local->link->open--; + + if (local->link->state & DEV_STALE_CONFIG) { +#ifdef HOSTAP_USE_RELEASE_TIMER + mod_timer(&local->link->release, jiffies + HZ / 20); +#else /* HOSTAP_USE_RELEASE_TIMER */ + prism2_release((u_long) local->link); +#endif /* HOSTAP_USE_RELEASE_TIMER */ + } + + 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 +}; + + +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,5,68) +static void cs_error(client_handle_t handle, int func, int ret) +{ + error_info_t err = { func, ret }; + CardServices(ReportError, handle, &err); +} +#endif + + +/* 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; + local_info_t *local; + client_reg_t client_reg; + int ret; + + for (link = dev_list; link; link = link->next) { + if (link->state & DEV_STALE_LINK) { + printk("%s: flushing stale link\n", dev_info); + prism2_detach(link); + } + } + + link = kmalloc(sizeof(dev_link_t), GFP_KERNEL); + if (link == NULL) + return NULL; + + memset(link, 0, sizeof(dev_link_t)); + + local = prism2_init_local_data(&prism2_pccard_funcs, 0); + if (local == NULL) { + kfree(link); + return NULL; + } + + link->priv = local->dev; + local->link = link; + +#ifdef HOSTAP_USE_RELEASE_TIMER + init_timer(&link->release); + link->release.function = &prism2_release; + link->release.data = (u_long)link; +#endif /* HOSTAP_USE_RELEASE_TIMER */ + + 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.Attributes = INFO_IO_CLIENT; + client_reg.EventMask = CS_EVENT_CARD_INSERTION | + CS_EVENT_CARD_REMOVAL | + CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET | + CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME; + client_reg.event_handler = &prism2_event; + client_reg.Version = 0x0210; + client_reg.event_callback_args.client_data = link; + ret = CardServices(RegisterClient, &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; + } + +#ifdef HOSTAP_USE_RELEASE_TIMER + del_timer(&link->release); +#endif /* HOSTAP_USE_RELEASE_TIMER */ + if (link->state & DEV_CONFIG) { + printk("%s: detach postponed, '%s' still locked\n", + dev_info, link->dev->dev_name); + prism2_release((u_long)link); + if (link->state & DEV_STALE_CONFIG) { + link->state |= DEV_STALE_LINK; + return; + } + } + + if (link->handle) { + int res = CardServices(DeregisterClient, link->handle); + if (res) { + printk("CardService(DeregisterClient) => %d\n", res); + cs_error(link->handle, DeregisterClient, res); + } + } + + *linkp = link->next; + /* release local_info_t struct */ + if (link->priv) { + struct net_device *dev = (struct net_device *) link->priv; + local_info_t *local = (local_info_t *) dev->priv; + + prism2_free_local_data(local); + + } + kfree(link); +} + + +#define CS_CHECK(fn, args...) \ +while ((last_ret = CardServices(last_fn = (fn), args)) != 0) goto cs_failed + +#define CFG_CHECK2(fn, args...) \ +do { int ret = CardServices(fn, args); \ +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_INSERTATION 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 net_device *) link->priv; + local_info_t *local = (local_info_t *) dev->priv; + int ret; + 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 }; + + PDEBUG(DEBUG_FLOW, "prism2_config()\n"); + + tuple.DesiredTuple = CISTPL_CONFIG; + tuple.Attributes = 0; + tuple.TupleData = buf; + tuple.TupleDataMax = sizeof(buf); + tuple.TupleOffset = 0; + CS_CHECK(GetFirstTuple, link->handle, &tuple); + CS_CHECK(GetTupleData, link->handle, &tuple); + CS_CHECK(ParseTuple, link->handle, &tuple, &parse); + link->conf.ConfigBase = parse.config.base; + link->conf.Present = parse.config.rmask[0]; + + CS_CHECK(GetConfigurationInfo, 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, link->handle, &tuple); + for (;;) { + cistpl_cftable_entry_t *cfg = &(parse.cftable_entry); + CFG_CHECK2(GetTupleData, link->handle, &tuple); + CFG_CHECK2(ParseTuple, 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; + else if (dflt.vpp1.present & (1 << CISTPL_POWER_VNOM)) + link->conf.Vpp1 = link->conf.Vpp2 = + dflt.vpp1.param[CISTPL_POWER_VNOM] / 10000; + + /* Do we need to allocate an interrupt? */ + if (cfg->irq.IRQInfo1 || dflt.irq.IRQInfo1) + link->conf.Attributes |= CONF_ENABLE_IRQ; + else if (!(link->conf.Attributes & CONF_ENABLE_IRQ)) { + /* At least Compaq WL200 does not have IRQInfo1 set, + * but it does not work without interrupts.. */ + printk("Config has no IRQ info, but trying to enable " + "IRQ anyway..\n"); + link->conf.Attributes |= CONF_ENABLE_IRQ; + } + + /* IO window settings */ + PDEBUG(DEBUG_EXTRA, "IO window settings: cfg->io.nwin=%d " + "dflt.io.nwin=%d\n", + cfg->io.nwin, dflt.io.nwin); + link->io.NumPorts1 = link->io.NumPorts2 = 0; + if ((cfg->io.nwin > 0) || (dflt.io.nwin > 0)) { + cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt.io; + link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO; + PDEBUG(DEBUG_EXTRA, "io->flags = 0x%04X, " + "io.base=0x%04x, len=%d\n", io->flags, + io->win[0].base, io->win[0].len); + if (!(io->flags & CISTPL_IO_8BIT)) + link->io.Attributes1 = IO_DATA_PATH_WIDTH_16; + if (!(io->flags & CISTPL_IO_16BIT)) + link->io.Attributes1 = IO_DATA_PATH_WIDTH_8; + link->io.IOAddrLines = io->flags & + CISTPL_IO_LINES_MASK; + link->io.BasePort1 = io->win[0].base; + link->io.NumPorts1 = io->win[0].len; + if (io->nwin > 1) { + link->io.Attributes2 = link->io.Attributes1; + link->io.BasePort2 = io->win[1].base; + link->io.NumPorts2 = io->win[1].len; + } + } + + /* This reserves IO space but doesn't actually enable it */ + CFG_CHECK2(RequestIO, link->handle, &link->io); + + /* This configuration table entry is OK */ + break; + + next_entry: + CS_CHECK(GetNextTuple, link->handle, &tuple); + } + + /* + * Allocate an interrupt line. Note that this does not assign a + * handler to the interrupt, unless the 'Handler' member of the + * irq structure is initialized. + */ + if (link->conf.Attributes & CONF_ENABLE_IRQ) { + int i; + link->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT; + link->irq.IRQInfo1 = IRQ_INFO2_VALID | IRQ_LEVEL_ID; + if (irq_list[0] == -1) + link->irq.IRQInfo2 = irq_mask; + else + for (i = 0; i < 4; i++) + link->irq.IRQInfo2 |= 1 << irq_list[i]; + link->irq.Handler = (void *) prism2_interrupt; + link->irq.Instance = dev; + CS_CHECK(RequestIRQ, link->handle, &link->irq); + } + + /* + * This actually configures the PCMCIA socket -- setting up + * the I/O windows and the interrupt mapping, and putting the + * card and host interface into "Memory and IO" mode. + */ + CS_CHECK(RequestConfiguration, link->handle, &link->conf); + + dev->irq = link->irq.AssignedIRQ; + dev->base_addr = link->io.BasePort1; + + /* Finally, report what we've done */ + printk(KERN_INFO "%s: index 0x%02x: Vcc %d.%d", + dev_info, link->conf.ConfigIndex, + link->conf.Vcc / 10, link->conf.Vcc % 10); + if (link->conf.Vpp1) + printk(", Vpp %d.%d", link->conf.Vpp1 / 10, + link->conf.Vpp1 % 10); + if (link->conf.Attributes & CONF_ENABLE_IRQ) + printk(", irq %d", link->irq.AssignedIRQ); + if (link->io.NumPorts1) + printk(", io 0x%04x-0x%04x", link->io.BasePort1, + link->io.BasePort1+link->io.NumPorts1-1); + if (link->io.NumPorts2) + printk(" & 0x%04x-0x%04x", link->io.BasePort2, + link->io.BasePort2+link->io.NumPorts2-1); + printk("\n"); + + link->state |= DEV_CONFIG; + link->state &= ~DEV_CONFIG_PENDING; + + if (prism2_init_dev(local)) { + prism2_release((u_long) link); + return 1; + } + + strcpy(local->node.dev_name, dev->name); + link->dev = &local->node; + + local->shutdown = 0; + + ret = prism2_hw_config(dev, 1); + return ret; + + cs_failed: + cs_error(link->handle, last_fn, last_ret); + prism2_release((u_long)link); + return 1; +} + + +static void prism2_release(u_long arg) +{ + dev_link_t *link = (dev_link_t *)arg; + struct net_device *dev = (struct net_device *) link->priv; + local_info_t *local = (local_info_t *) dev->priv; + + PDEBUG(DEBUG_FLOW, "prism2_release\n"); + + if (link->open) { + printk("%s: release postponed, '%s' still open\n", + dev_info, link->dev->dev_name); + link->state |= DEV_STALE_CONFIG; + return; + } + + if (dev != NULL) + prism2_hw_shutdown(dev, 0); + + local->shutdown = 1; + + if (link->win) + CardServices(ReleaseWindow, link->win); + CardServices(ReleaseConfiguration, link->handle); + if (link->io.NumPorts1) + CardServices(ReleaseIO, link->handle, &link->io); + if (link->irq.AssignedIRQ) + CardServices(ReleaseIRQ, link->handle, &link->irq); + + link->state &= ~DEV_CONFIG; + + PDEBUG(DEBUG_FLOW, "release - done\n"); +} + + +static int prism2_event(event_t event, int priority, + event_callback_args_t *args) +{ + dev_link_t *link = args->client_data; + struct net_device *dev = (struct net_device *) link->priv; + + switch (event) { + case CS_EVENT_CARD_INSERTION: + PDEBUG(DEBUG_EXTRA, "%s: CS_EVENT_CARD_INSERTION\n", dev_info); + link->state |= DEV_PRESENT | DEV_CONFIG_PENDING; + if (prism2_config(link)) + dev->irq = 0; + break; + + case CS_EVENT_CARD_REMOVAL: + PDEBUG(DEBUG_EXTRA, "%s: CS_EVENT_CARD_REMOVAL\n", dev_info); + link->state &= ~DEV_PRESENT; + if (link->state & DEV_CONFIG) { + hostap_netif_stop_queues(dev); + netif_device_detach(dev); +#ifdef HOSTAP_USE_RELEASE_TIMER + mod_timer(&link->release, jiffies + HZ / 20); +#else /* HOSTAP_USE_RELEASE_TIMER */ + prism2_release((u_long) link); +#endif /* HOSTAP_USE_RELEASE_TIMER */ + } + break; + + case CS_EVENT_PM_SUSPEND: + PDEBUG(DEBUG_EXTRA, "%s: CS_EVENT_PM_SUSPEND\n", dev_info); + link->state |= DEV_SUSPEND; + /* fall through */ + + case CS_EVENT_RESET_PHYSICAL: + PDEBUG(DEBUG_EXTRA, "%s: CS_EVENT_RESET_PHYSICAL\n", dev_info); + if (link->state & DEV_CONFIG) { + if (link->open) { + hostap_netif_stop_queues(dev); + netif_device_detach(dev); + } + CardServices(ReleaseConfiguration, link->handle); + } + break; + + case CS_EVENT_PM_RESUME: + PDEBUG(DEBUG_EXTRA, "%s: CS_EVENT_PM_RESUME\n", dev_info); + link->state &= ~DEV_SUSPEND; + /* fall through */ + + case CS_EVENT_CARD_RESET: + PDEBUG(DEBUG_EXTRA, "%s: CS_EVENT_CARD_RESET\n", dev_info); + if (link->state & DEV_CONFIG) { + CardServices(RequestConfiguration, link->handle, + &link->conf); + if (link->open) { + prism2_hw_shutdown(dev, 1); + prism2_hw_config(dev, 0); + netif_device_attach(dev); + netif_start_queue(dev); + } + } + break; + + default: + PDEBUG(DEBUG_EXTRA, "%s: prism2_event() - unknown event %d\n", + dev_info, event); + break; + } + return 0; +} + + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,67) +static struct pcmcia_driver hostap_driver = { + .drv = { + .name = "hostap_cs", + }, + .attach = prism2_attach, + .detach = prism2_detach, + .owner = THIS_MODULE, +}; + +static int __init init_prism2_pccard(void) +{ + printk(KERN_INFO "%s: %s\n", dev_info, version); + return pcmcia_register_driver(&hostap_driver); +} + +static void __exit exit_prism2_pccard(void) +{ + pcmcia_unregister_driver(&hostap_driver); + printk(KERN_INFO "%s: Driver unloaded\n", dev_info); +} + +#else + +static int __init init_prism2_pccard(void) +{ + servinfo_t serv; + + printk(KERN_INFO "%s: %s\n", dev_info, version); + CardServices(GetCardServicesInfo, &serv); + if (serv.Revision != CS_RELEASE_CODE) { + printk(KERN_NOTICE + "%s: CardServices release does not match!\n", dev_info); + return -1; + } + register_pccard_driver(&dev_info, &prism2_attach, &prism2_detach); + + return 0; +} + + +static void __exit exit_prism2_pccard(void) +{ + unregister_pccard_driver(&dev_info); + while (dev_list) { + PDEBUG(DEBUG_FLOW, "exit_prism2 - detaching device\n"); +#ifdef HOSTAP_USE_RELEASE_TIMER + del_timer(&dev_list->release); +#endif /* HOSTAP_USE_RELEASE_TIMER */ + if (dev_list->state & DEV_CONFIG) + prism2_release((u_long)dev_list); + prism2_detach(dev_list); + } + + printk(KERN_INFO "%s: Driver unloaded\n", dev_info); +} +#endif /* LINUX_VERSION_CODE > KERNEL_VERSION(2,5,67) */ + + +module_init(init_prism2_pccard); +module_exit(exit_prism2_pccard); Index: drivers/net/wireless/hostap_download.c --- drivers/net/wireless/hostap_download.c.orig 2003-08-12 14:39:16.000000000 -0400 +++ drivers/net/wireless/hostap_download.c 2003-06-27 23:28:21.000000000 -0400 @@ -0,0 +1,547 @@ +static int prism2_enable_aux_port(struct net_device *dev, int enable) +{ + u16 val, reg; + int i, tries; + unsigned long flags; + local_info_t *local = (local_info_t *) dev->priv; + + spin_lock_irqsave(&local->cmdlock, flags); + + /* wait until busy bit is clear */ + tries = HFA384X_CMD_BUSY_TIMEOUT; + while (HFA384X_INW(HFA384X_CMD_OFF) & HFA384X_CMD_BUSY && tries > 0) { + tries--; + udelay(1); + } + if (tries == 0) { + reg = HFA384X_INW(HFA384X_CMD_OFF); + spin_unlock_irqrestore(&local->cmdlock, flags); + printk("%s: prism2_enable_aux_port - timeout - reg=0x%04x\n", + dev->name, reg); + return -ETIMEDOUT; + } + + val = HFA384X_INW(HFA384X_CONTROL_OFF); + + if (enable) { + HFA384X_OUTW(HFA384X_AUX_MAGIC0, HFA384X_PARAM0_OFF); + HFA384X_OUTW(HFA384X_AUX_MAGIC1, HFA384X_PARAM1_OFF); + HFA384X_OUTW(HFA384X_AUX_MAGIC2, HFA384X_PARAM2_OFF); + + if ((val & HFA384X_AUX_PORT_MASK) != HFA384X_AUX_PORT_DISABLED) + printk("prism2_enable_aux_port: was not disabled!?\n"); + val &= ~HFA384X_AUX_PORT_MASK; + val |= HFA384X_AUX_PORT_ENABLE; + } else { + HFA384X_OUTW(0, HFA384X_PARAM0_OFF); + HFA384X_OUTW(0, HFA384X_PARAM1_OFF); + HFA384X_OUTW(0, HFA384X_PARAM2_OFF); + + if ((val & HFA384X_AUX_PORT_MASK) != HFA384X_AUX_PORT_ENABLED) + printk("prism2_enable_aux_port: was not enabled!?\n"); + val &= ~HFA384X_AUX_PORT_MASK; + val |= HFA384X_AUX_PORT_DISABLE; + } + HFA384X_OUTW(val, HFA384X_CONTROL_OFF); + + udelay(5); + + i = 10000; + while (i > 0) { + val = HFA384X_INW(HFA384X_CONTROL_OFF); + val &= HFA384X_AUX_PORT_MASK; + + if ((enable && val == HFA384X_AUX_PORT_ENABLED) || + (!enable && val == HFA384X_AUX_PORT_DISABLED)) + break; + + udelay(10); + i--; + } + + spin_unlock_irqrestore(&local->cmdlock, flags); + + if (i == 0) { + printk("prism2_enable_aux_port(%d) timed out\n", + enable); + return -ETIMEDOUT; + } + + return 0; +} + + +static int hfa384x_from_aux(struct net_device *dev, unsigned int addr, int len, + void *buf) +{ + u16 page, offset; + if (addr & 1 || len & 1) + return -1; + + page = addr >> 7; + offset = addr & 0x7f; + + HFA384X_OUTW(page, HFA384X_AUXPAGE_OFF); + HFA384X_OUTW(offset, HFA384X_AUXOFFSET_OFF); + + udelay(5); + +#ifdef PRISM2_PCI + { + u16 *pos = (u16 *) buf; + while (len > 0) { + *pos++ = HFA384X_INW_DATA(HFA384X_AUXDATA_OFF); + len -= 2; + } + } +#else /* PRISM2_PCI */ + HFA384X_INSW(HFA384X_AUXDATA_OFF, buf, len / 2); +#endif /* PRISM2_PCI */ + + return 0; +} + + +static int hfa384x_to_aux(struct net_device *dev, unsigned int addr, int len, + void *buf) +{ + u16 page, offset; + if (addr & 1 || len & 1) + return -1; + + page = addr >> 7; + offset = addr & 0x7f; + + HFA384X_OUTW(page, HFA384X_AUXPAGE_OFF); + HFA384X_OUTW(offset, HFA384X_AUXOFFSET_OFF); + + udelay(5); + +#ifdef PRISM2_PCI + { + u16 *pos = (u16 *) buf; + while (len > 0) { + HFA384X_OUTW_DATA(*pos++, HFA384X_AUXDATA_OFF); + len -= 2; + } + } +#else /* PRISM2_PCI */ + HFA384X_OUTSW(HFA384X_AUXDATA_OFF, buf, len / 2); +#endif /* PRISM2_PCI */ + + return 0; +} + + +static int prism2_pda_ok(u8 *buf) +{ + u16 *pda = (u16 *) buf; + int pos; + u16 len, pdr; + + if (buf[0] == 0xff && buf[1] == 0x00 && buf[2] == 0xff && + buf[3] == 0x00) + return 0; + + pos = 0; + while (pos + 1 < PRISM2_PDA_SIZE / 2) { + len = le16_to_cpu(pda[pos]); + pdr = le16_to_cpu(pda[pos + 1]); + if (len == 0 || pos + len > PRISM2_PDA_SIZE / 2) + return 0; + + if (pdr == 0x0000 && len == 2) { + /* PDA end found */ + return 1; + } + + pos += len + 1; + } + + return 0; +} + + +static u8 * prism2_read_pda(struct net_device *dev) +{ + u8 *buf; + int res, i, found = 0; +#define NUM_PDA_ADDRS 3 + unsigned int pda_addr[NUM_PDA_ADDRS] = { + 0x7f0000 /* others than HFA3841 */, + 0x3f0000 /* HFA3841 */, + 0x390000 /* apparently used in older cards */ + }; + + buf = (u8 *) kmalloc(PRISM2_PDA_SIZE, GFP_KERNEL); + if (buf == NULL) + return NULL; + + /* Note: wlan card should be in initial state (just after init cmd) + * and no other operations should be performed concurrently. */ + + prism2_enable_aux_port(dev, 1); + + for (i = 0; i < NUM_PDA_ADDRS; i++) { + printk(KERN_DEBUG "%s: trying to read PDA from 0x%08x", + dev->name, pda_addr[i]); + res = hfa384x_from_aux(dev, pda_addr[i], PRISM2_PDA_SIZE, buf); + if (res) + continue; + if (res == 0 && prism2_pda_ok(buf)) { + printk(": OK\n"); + found = 1; + break; + } else { + printk(": failed\n"); + } + } + + prism2_enable_aux_port(dev, 0); + + if (!found) { + kfree(buf); + buf = NULL; + } + + return buf; +} + + +static int prism2_download_volatile(local_info_t *local, + struct prism2_download_param *param, + u8 **copied_data) +{ + struct net_device *dev = local->dev; + int ret = 0, i; + u16 param0, param1; + + if (local->hw_downloading) { + printk(KERN_WARNING "%s: Already downloading - aborting new " + "request\n", dev->name); + return -1; + } + + local->hw_downloading = 1; + prism2_hw_shutdown(dev, 0); + + if (prism2_hw_init(dev, 0)) { + printk(KERN_WARNING "%s: Could not initialize card for " + "download\n", dev->name); + ret = -1; + goto out; + } + + if (prism2_enable_aux_port(dev, 1)) { + printk(KERN_WARNING "%s: Could not enable AUX port\n", + dev->name); + ret = -1; + goto out; + } + + param0 = param->start_addr & 0xffff; + param1 = param->start_addr >> 16; + + HFA384X_OUTW(0, HFA384X_PARAM2_OFF); + HFA384X_OUTW(param1, HFA384X_PARAM1_OFF); + if (hfa384x_cmd_wait(dev, HFA384X_CMDCODE_DOWNLOAD | + (HFA384X_PROGMODE_ENABLE_VOLATILE << 8), + param0, NULL), 0) { + printk(KERN_WARNING "%s: Download command execution failed\n", + dev->name); + ret = -1; + goto out; + } + + for (i = 0; i < param->num_areas; i++) { + printk(KERN_DEBUG "%s: Writing %d bytes at 0x%08x\n", + dev->name, param->data[i].len, param->data[i].addr); + if (hfa384x_to_aux(dev, param->data[i].addr, + param->data[i].len, copied_data[i])) { + printk(KERN_WARNING "%s: RAM download at 0x%08x " + "(len=%d) failed\n", dev->name, + param->data[i].addr, param->data[i].len); + ret = -1; + goto out; + } + } + + HFA384X_OUTW(param1, HFA384X_PARAM1_OFF); + HFA384X_OUTW(0, HFA384X_PARAM2_OFF); + if (hfa384x_cmd_no_wait(dev, HFA384X_CMDCODE_DOWNLOAD | + (HFA384X_PROGMODE_DISABLE << 8), param0)) { + printk(KERN_WARNING "%s: Download command execution failed\n", + dev->name); + ret = -1; + goto out; + } + /* ProgMode disable causes the hardware to restart itself from the + * given starting address. Give hw some time and ACK command just in + * case restart did not happen. */ + mdelay(5); + HFA384X_OUTW(HFA384X_EV_CMD, HFA384X_EVACK_OFF); + + if (prism2_enable_aux_port(dev, 0)) { + printk(KERN_DEBUG "%s: Disabling AUX port failed\n", + dev->name); + /* continue anyway.. restart should have taken care of this */ + } + + mdelay(5); + local->hw_downloading = 0; + if (prism2_hw_config(dev, 2)) { + printk(KERN_WARNING "%s: Card configuration after RAM " + "download failed\n", dev->name); + ret = -1; + goto out2; + } + + goto out2; + out: + local->hw_downloading = 0; + out2: + return ret; +} + + +#ifdef PRISM2_NON_VOLATILE_DOWNLOAD +/* Note! Non-volatile downloading functionality has not yet been tested + * thoroughly and it may corrupt flash image and effectively kill the card that + * is being updated. You have been warned. */ + +static inline int prism2_download_block(struct net_device *dev, + struct prism2_download_param *param, + u8 **copied_data, u32 bufaddr, + int idx, int rest_len, int data_off) +{ + u16 param0, param1; + int block_len; + + block_len = rest_len < 4096 ? rest_len : 4096; + + param0 = (param->data[idx].addr + data_off) & 0xffff; + param1 = (param->data[idx].addr + data_off) >> 16; + + HFA384X_OUTW(block_len, HFA384X_PARAM2_OFF); + HFA384X_OUTW(param1, HFA384X_PARAM1_OFF); + + if (hfa384x_cmd_wait(dev, HFA384X_CMDCODE_DOWNLOAD | + (HFA384X_PROGMODE_ENABLE_NON_VOLATILE << 8), + param0, NULL)) { + printk(KERN_WARNING "%s: Flash download command execution " + "failed\n", dev->name); + return -1; + } + + if (hfa384x_to_aux(dev, bufaddr, + block_len, copied_data[idx] + data_off)) { + printk(KERN_WARNING "%s: flash download at 0x%08x " + "(len=%d) failed\n", dev->name, + param->data[idx].addr, param->data[idx].len); + return -1; + } + + HFA384X_OUTW(0, HFA384X_PARAM2_OFF); + HFA384X_OUTW(0, HFA384X_PARAM1_OFF); + if (hfa384x_cmd_wait(dev, HFA384X_CMDCODE_DOWNLOAD | + (HFA384X_PROGMODE_PROGRAM_NON_VOLATILE << 8), + 0, NULL)) { + printk(KERN_WARNING "%s: Flash write command execution " + "failed\n", dev->name); + return -1; + } + + return block_len; +} + + +static int prism2_download_nonvolatile(local_info_t *local, + struct prism2_download_param *param, + u8 **copied_data) +{ + struct net_device *dev = local->dev; + int ret = 0, i; + struct { + u16 page; + u16 offset; + u16 len; + } dlbuffer; + u32 bufaddr; + + if (local->hw_downloading) { + printk(KERN_WARNING "%s: Already downloading - aborting new " + "request\n", dev->name); + return -1; + } + + local->hw_downloading = 1; + + ret = local->func->get_rid(dev, HFA384X_RID_DOWNLOADBUFFER, + &dlbuffer, 6, 0); + + if (ret < 0) { + printk(KERN_WARNING "%s: Could not read download buffer " + "parameters\n", dev->name); + goto out; + } + + dlbuffer.page = le16_to_cpu(dlbuffer.page); + dlbuffer.offset = le16_to_cpu(dlbuffer.offset); + dlbuffer.len = le16_to_cpu(dlbuffer.len); + + printk(KERN_DEBUG "Download buffer: %d bytes at 0x%04x:0x%04x\n", + dlbuffer.len, dlbuffer.page, dlbuffer.offset); + + bufaddr = (dlbuffer.page << 7) + dlbuffer.offset; + + prism2_hw_shutdown(dev, 0); + + if (prism2_hw_init(dev, 0)) { + printk(KERN_WARNING "%s: Could not initialize card for " + "download\n", dev->name); + ret = -1; + goto out; + } + + hfa384x_disable_interrupts(dev); + + if (prism2_enable_aux_port(dev, 1)) { + printk(KERN_WARNING "%s: Could not enable AUX port\n", + dev->name); + ret = -1; + goto out; + } + + printk(KERN_DEBUG "%s: starting flash download\n", dev->name); + for (i = 0; i < param->num_areas; i++) { + int rest_len = param->data[i].len; + int data_off = 0; + + while (rest_len > 0) { + int block_len; + + block_len = prism2_download_block( + dev, param, copied_data, bufaddr, + i, rest_len, data_off); + + if (block_len < 0) { + ret = -1; + goto out; + } + + rest_len -= block_len; + data_off += block_len; + } + } + + HFA384X_OUTW(0, HFA384X_PARAM1_OFF); + HFA384X_OUTW(0, HFA384X_PARAM2_OFF); + if (hfa384x_cmd_wait(dev, HFA384X_CMDCODE_DOWNLOAD | + (HFA384X_PROGMODE_DISABLE << 8), 0, NULL)) { + printk(KERN_WARNING "%s: Download command execution failed\n", + dev->name); + ret = -1; + goto out; + } + + if (prism2_enable_aux_port(dev, 0)) { + printk(KERN_DEBUG "%s: Disabling AUX port failed\n", + dev->name); + /* continue anyway.. restart should have taken care of this */ + } + + mdelay(5); + + local->func->hw_reset(dev); + local->hw_downloading = 0; + if (prism2_hw_config(dev, 2)) { + printk(KERN_WARNING "%s: Card configuration after flash " + "download failed\n", dev->name); + ret = -1; + } else { + printk(KERN_INFO "%s: Card initialized successfully after " + "flash download\n", dev->name); + } + + out: + local->hw_downloading = 0; + return ret; +} +#endif /* PRISM2_NON_VOLATILE_DOWNLOAD */ + + + +static int prism2_download(local_info_t *local, + struct prism2_download_param *param) +{ + int ret = 0; + int i; + u32 total_len = 0; + u8 **copied_data; + + printk(KERN_DEBUG "prism2_download: dl_cmd=%d start_addr=0x%08x " + "num_areas=%d\n", + param->dl_cmd, param->start_addr, param->num_areas); + + copied_data = (u8 **) kmalloc(param->num_areas * sizeof(u8 *), + GFP_KERNEL); + if (copied_data == NULL) { + ret = -ENOMEM; + goto out; + } + memset(copied_data, 0, param->num_areas * sizeof(u8 *)); + + for (i = 0; i < param->num_areas; i++) { + printk(KERN_DEBUG " area %d: addr=0x%08x len=%d ptr=0x%p\n", + i, param->data[i].addr, param->data[i].len, + param->data[i].ptr); + total_len += param->data[i].len; + if (param->data[i].len > PRISM2_MAX_DOWNLOAD_AREA_LEN || + total_len > PRISM2_MAX_DOWNLOAD_LEN) { + ret = -E2BIG; + goto out; + } + + copied_data[i] = (u8 *) + kmalloc(param->data[i].len, GFP_KERNEL); + if (copied_data[i] == NULL) { + ret = -ENOMEM; + goto out; + } + + if (copy_from_user(copied_data[i], param->data[i].ptr, + param->data[i].len)) { + ret = -EFAULT; + goto out; + } + } + + switch (param->dl_cmd) { + case PRISM2_DOWNLOAD_VOLATILE: + ret = prism2_download_volatile(local, param, copied_data); + break; + case PRISM2_DOWNLOAD_NON_VOLATILE: +#ifdef PRISM2_NON_VOLATILE_DOWNLOAD + ret = prism2_download_nonvolatile(local, param, copied_data); +#else /* PRISM2_NON_VOLATILE_DOWNLOAD */ + printk(KERN_INFO "%s: non-volatile downloading not enabled\n", + local->dev->name); + ret = -EOPNOTSUPP; +#endif /* PRISM2_NON_VOLATILE_DOWNLOAD */ + break; + default: + printk(KERN_DEBUG "%s: unsupported download command %d\n", + local->dev->name, param->dl_cmd); + ret = -EINVAL; + break; + }; + + out: + if (copied_data) { + for (i = 0; i < param->num_areas; i++) + if (copied_data[i] != NULL) + kfree(copied_data[i]); + kfree(copied_data); + } + return ret; +} Index: drivers/net/wireless/hostap.h --- drivers/net/wireless/hostap.h.orig 2003-08-12 14:39:16.000000000 -0400 +++ drivers/net/wireless/hostap.h 2003-07-08 14:51:41.000000000 -0400 @@ -0,0 +1,45 @@ +#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); +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); +void hostap_netif_wake_queues(struct net_device *dev); +void hostap_netif_stop_queues(struct net_device *dev); +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); + + +/* 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 */ Index: drivers/net/wireless/hostap_hw.c --- drivers/net/wireless/hostap_hw.c.orig 2003-08-12 14:39:16.000000000 -0400 +++ drivers/net/wireless/hostap_hw.c 2003-08-08 22:39:41.000000000 -0400 @@ -0,0 +1,4551 @@ +/* + * Host AP (software wireless LAN access point) driver for + * Intersil Prism2/2.5/3. + * + * Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen + * + * Copyright (c) 2002-2003, Jouni Malinen + * + * 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. + * + * FIX: + * - there is currently no way of associating TX packets to correct wds device + * when TX Exc/OK event occurs, so all tx_packets and some + * tx_errors/tx_dropped are added to the main netdevice; using sw_support + * field in txdesc might be used to fix this (using Alloc event to increment + * tx_packets would need some further info in txfid table) + * + * Buffer Access Path (BAP) usage: + * Prism2 cards have two separate BAPs for accessing the card memory. These + * should allow concurrent access to two different frames and the driver + * previously used BAP0 for sending data and BAP1 for receiving data. + * However, there seems to be number of issues with concurrent access and at + * least one know hardware bug in using BAP0 and BAP1 concurrently with PCI + * Prism2.5. Therefore, the driver now only uses BAP0 for moving data between + * host and card memories. BAP0 accesses are protected with local->baplock + * (spin_lock_bh) to prevent concurrent use. + */ + + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if WIRELESS_EXT > 12 +#include +#endif /* WIRELESS_EXT > 12 */ +#include + + +#include "hostap.h" +#include "hostap_ap.h" + + +/* #define final_version */ + +static int mtu = 1500; +MODULE_PARM(mtu, "i"); +MODULE_PARM_DESC(mtu, "Maximum transfer unit"); + +static int channel[MAX_PARM_DEVICES] = { 3, DEF_INTS }; +MODULE_PARM(channel, PARM_MIN_MAX "i"); +MODULE_PARM_DESC(channel, "Initial channel"); + +static char *essid[MAX_PARM_DEVICES] = { "test" }; +MODULE_PARM(essid, PARM_MIN_MAX "s"); +MODULE_PARM_DESC(essid, "Host AP's ESSID"); + +static int iw_mode[MAX_PARM_DEVICES] = { IW_MODE_MASTER, DEF_INTS }; +MODULE_PARM(iw_mode, PARM_MIN_MAX "i"); +MODULE_PARM_DESC(iw_mode, "Initial operation mode"); + +static int beacon_int[MAX_PARM_DEVICES] = { 100, DEF_INTS }; +MODULE_PARM(beacon_int, PARM_MIN_MAX "i"); +MODULE_PARM_DESC(beacon_int, "Beacon interval (1 = 1024 usec)"); + +static int dtim_period[MAX_PARM_DEVICES] = { 1, DEF_INTS }; +MODULE_PARM(dtim_period, PARM_MIN_MAX "i"); +MODULE_PARM_DESC(dtim_period, "DTIM period"); + +static int delayed_enable /* = 0 */; +MODULE_PARM(delayed_enable, "i"); +MODULE_PARM_DESC(delayed_enable, "Delay MAC port enable until netdevice open"); + +static int disable_on_close /* = 0 */; +MODULE_PARM(disable_on_close, "i"); +MODULE_PARM_DESC(disable_on_close, "Disable MAC port on netdevice close"); + +#if defined(PRISM2_PCI) && defined(PRISM2_BUS_MASTER) +static int bus_master_threshold_rx[MAX_PARM_DEVICES] = { 100, DEF_INTS }; +MODULE_PARM(bus_master_threshold_rx, "i"); +MODULE_PARM_DESC(bus_master_threshold_rx, "Packet length threshold for using " + "PCI bus master on RX"); + +static int bus_master_threshold_tx[MAX_PARM_DEVICES] = { 100, DEF_INTS }; +MODULE_PARM(bus_master_threshold_tx, "i"); +MODULE_PARM_DESC(bus_master_threshold_tx, "Packet length threshold for using " + "PCI bus master on TX"); +#endif /* PRISM2_PCI and PRISM2_BUS_MASTER */ + + +/* 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) */ + + +#ifdef final_version +#define EXTRA_EVENTS_WTERR 0 +#else +/* check WTERR events (Wait Time-out) in development versions */ +#define EXTRA_EVENTS_WTERR HFA384X_EV_WTERR +#endif + +#if defined(PRISM2_PCI) && defined(PRISM2_BUS_MASTER) +#define EXTRA_EVENTS_BUS_MASTER (HFA384X_EV_PCI_M0 | HFA384X_EV_PCI_M1) +#else +#define EXTRA_EVENTS_BUS_MASTER 0 +#endif + +/* Events that will be using BAP0 */ +#define HFA384X_BAP0_EVENTS \ + (HFA384X_EV_TXEXC | HFA384X_EV_RX | HFA384X_EV_INFO | HFA384X_EV_TX) + +/* event mask, i.e., events that will result in an interrupt */ +#define HFA384X_EVENT_MASK \ + (HFA384X_BAP0_EVENTS | HFA384X_EV_ALLOC | HFA384X_EV_INFDROP | \ + HFA384X_EV_CMD | HFA384X_EV_TICK | \ + EXTRA_EVENTS_WTERR | EXTRA_EVENTS_BUS_MASTER) + +/* Default TX control flags: use 802.11 headers and request interrupt for + * failed transmits. Frames that request ACK callback, will add + * _TX_OK flag and _ALT_RTRY flag may be used to select different retry policy. + */ + +/* + * click - we always want callbacks, so also at TX_OK to that + * --jbicket + */ + +#define HFA384X_TX_CTRL_FLAGS \ + (HFA384X_TX_CTRL_802_11 | HFA384X_TX_CTRL_TX_EX | HFA384X_TX_CTRL_TX_OK) + + +/* ca. 1 usec */ +#define HFA384X_CMD_BUSY_TIMEOUT 5000 + +/* ca. 10 usec */ +#define HFA384X_BAP_BUSY_TIMEOUT 5000 +#define HFA384X_INIT_TIMEOUT 50000 +#define HFA384X_CMD_COMPL_TIMEOUT 20000 +#define HFA384X_DL_COMPL_TIMEOUT 1000000 +#define HFA384X_ALLOC_COMPL_TIMEOUT 5000 + + + +static void prism2_hw_reset(struct net_device *dev); +static void prism2_check_sta_fw_version(local_info_t *local); + +#ifdef PRISM2_DOWNLOAD_SUPPORT +/* hostap_download.c */ +static u8 * prism2_read_pda(struct net_device *dev); +static int prism2_download(local_info_t *local, + struct prism2_download_param *param); +#endif /* PRISM2_DOWNLOAD_SUPPORT */ + + + + +#ifndef final_version +/* magic value written to SWSUPPORT0 reg. for detecting whether card is still + * present */ +#define HFA384X_MAGIC 0x8A32 +#endif + + +static u16 hfa384x_read_reg(struct net_device *dev, u16 reg) +{ + return HFA384X_INW(reg); +} + + +static void hfa384x_read_regs(struct net_device *dev, + struct hfa384x_regs *regs) +{ + regs->cmd = HFA384X_INW(HFA384X_CMD_OFF); + regs->evstat = HFA384X_INW(HFA384X_EVSTAT_OFF); + regs->offset0 = HFA384X_INW(HFA384X_OFFSET0_OFF); + regs->offset1 = HFA384X_INW(HFA384X_OFFSET1_OFF); + regs->swsupport0 = HFA384X_INW(HFA384X_SWSUPPORT0_OFF); +} + + +/* local->cmdlock must be locked when calling this helper function */ +static inline void __hostap_cmd_queue_free(local_info_t *local, + struct hostap_cmd_queue *entry, + int del_req) +{ + if (del_req) { + entry->del_req = 1; + if (!list_empty(&entry->list)) { + list_del_init(&entry->list); + local->cmd_queue_len--; + } + } + + if (atomic_dec_and_test(&entry->usecnt) && entry->del_req) + kfree(entry); +} + +static inline void hostap_cmd_queue_free(local_info_t *local, + struct hostap_cmd_queue *entry, + int del_req) +{ + unsigned long flags; + + spin_lock_irqsave(&local->cmdlock, flags); + __hostap_cmd_queue_free(local, entry, del_req); + spin_unlock_irqrestore(&local->cmdlock, flags); +} + + +static inline int hfa384x_cmd_issue(struct net_device *dev, + struct hostap_cmd_queue *entry) +{ + local_info_t *local = (local_info_t *) dev->priv; + int tries; + u16 reg; + unsigned long flags; + + if (entry->issued) { + printk(KERN_DEBUG "%s: driver bug - re-issuing command @%p\n", + dev->name, entry); + } + + /* wait until busy bit is clear; this should always be clear since the + * commands are serialized */ + tries = HFA384X_CMD_BUSY_TIMEOUT; + while (HFA384X_INW(HFA384X_CMD_OFF) & HFA384X_CMD_BUSY && tries > 0) { + tries--; + udelay(1); + } +#ifndef final_version + if (tries != HFA384X_CMD_BUSY_TIMEOUT) { + prism2_io_debug_error(dev, 1); + printk(KERN_DEBUG "%s: hfa384x_cmd_issue: cmd reg was busy " + "for %d usec\n", dev->name, + HFA384X_CMD_BUSY_TIMEOUT - tries); + } +#endif + if (tries == 0) { + reg = HFA384X_INW(HFA384X_CMD_OFF); + prism2_io_debug_error(dev, 2); + printk(KERN_DEBUG "%s: hfa384x_cmd_issue - timeout - " + "reg=0x%04x\n", dev->name, reg); + return -ETIMEDOUT; + } + + /* write command */ + spin_lock_irqsave(&local->cmdlock, flags); + HFA384X_OUTW(entry->param0, HFA384X_PARAM0_OFF); + HFA384X_OUTW(entry->param1, HFA384X_PARAM1_OFF); + HFA384X_OUTW(entry->cmd, HFA384X_CMD_OFF); + entry->issued = 1; + spin_unlock_irqrestore(&local->cmdlock, flags); + + return 0; +} + + +/* Issue given command (possibly after waiting in command queue) and sleep + * until the command is completed (or timed out). This can be called only + * from user context. */ +static int hfa384x_cmd(struct net_device *dev, u16 cmd, u16 param0, + u16 *param1, u16 *resp0) +{ + local_info_t *local = (local_info_t *) dev->priv; + int err, res, issue, issued = 0; + unsigned long flags; + struct hostap_cmd_queue *entry; + DECLARE_WAITQUEUE(wait, current); + + if (in_interrupt()) { + printk(KERN_DEBUG "%s: hfa384x_cmd called from interrupt " + "context\n", dev->name); + return -1; + } + + if (local->cmd_queue_len >= HOSTAP_CMD_QUEUE_MAX_LEN) { + printk(KERN_DEBUG "%s: hfa384x_cmd: cmd_queue full\n", + dev->name); + return -1; + } + + if (signal_pending(current)) + return -EINTR; + + entry = (struct hostap_cmd_queue *) + kmalloc(sizeof(*entry), GFP_ATOMIC); + if (entry == NULL) { + printk(KERN_DEBUG "%s: hfa384x_cmd - kmalloc failed\n", + dev->name); + return -ENOMEM; + } + memset(entry, 0, sizeof(*entry)); + atomic_set(&entry->usecnt, 1); + entry->type = CMD_SLEEP; + entry->cmd = cmd; + entry->param0 = param0; + if (param1) + entry->param1 = *param1; + init_waitqueue_head(&entry->compl); + + /* prepare to wait for command completion event, but do not sleep yet + */ + add_wait_queue(&entry->compl, &wait); + set_current_state(TASK_INTERRUPTIBLE); + + spin_lock_irqsave(&local->cmdlock, flags); + issue = list_empty(&local->cmd_queue); + if (issue) + entry->issuing = 1; + list_add_tail(&entry->list, &local->cmd_queue); + local->cmd_queue_len++; + spin_unlock_irqrestore(&local->cmdlock, flags); + + err = 0; + if (!issue) + goto wait_completion; + + if (signal_pending(current)) + err = -EINTR; + + if (!err) { + if (hfa384x_cmd_issue(dev, entry)) + err = -ETIMEDOUT; + else + issued = 1; + } + + wait_completion: + if (!err && entry->type != CMD_COMPLETED) { + /* sleep until command is completed or timed out */ + res = schedule_timeout(2 * HZ); + } else + res = -1; + + if (!err && signal_pending(current)) + err = -EINTR; + + if (err && issued) { + /* the command was issued, so a CmdCompl event should occur + * soon; however, there's a pending signal and + * schedule_timeout() would be interrupted; wait a short period + * of time to avoid removing entry from the list before + * CmdCompl event */ + udelay(300); + } + + set_current_state(TASK_RUNNING); + remove_wait_queue(&entry->compl, &wait); + + /* If entry->list is still in the list, it must be removed + * first and in this case prism2_cmd_ev() does not yet have + * local reference to it, and the data can be kfree()'d + * here. If the command completion event is still generated, + * it will be assigned to next (possibly) pending command, but + * the driver will reset the card anyway due to timeout + * + * If the entry is not in the list prism2_cmd_ev() has a local + * reference to it, but keeps cmdlock as long as the data is + * needed, so the data can be kfree()'d here. */ + + /* FIX: if the entry->list is in the list, it has not been completed + * yet, so removing it here is somewhat wrong.. this could cause + * references to freed memory and next list_del() causing NULL pointer + * dereference.. it would probably be better to leave the entry in the + * list and the list should be emptied during hw reset */ + + spin_lock_irqsave(&local->cmdlock, flags); + if (!list_empty(&entry->list)) { + printk(KERN_DEBUG "%s: hfa384x_cmd: entry still in list? " + "(entry=%p, type=%d, res=%d)\n", dev->name, entry, + entry->type, res); + list_del_init(&entry->list); + local->cmd_queue_len--; + } + spin_unlock_irqrestore(&local->cmdlock, flags); + + if (err) { + printk(KERN_DEBUG "%s: hfa384x_cmd: interrupted; err=%d\n", + dev->name, err); + res = err; + goto done; + } + + if (entry->type != CMD_COMPLETED) { + u16 reg = HFA384X_INW(HFA384X_EVSTAT_OFF); + printk(KERN_DEBUG "%s: hfa384x_cmd: command was not " + "completed (res=%d, entry=%p, type=%d, cmd=0x%04x, " + "param0=0x%04x, EVSTAT=%04x)\n", dev->name, res, + entry, entry->type, entry->cmd, entry->param0, reg); + if (reg & HFA384X_EV_CMD) { + /* Command completion event is pending, but the + * interrupt was not delivered - probably an issue + * with pcmcia-cs configuration. */ + printk(KERN_WARNING "%s: interrupt delivery does not " + "seem to work\n", dev->name); + } + prism2_io_debug_error(dev, 3); + res = -ETIMEDOUT; + goto done; + } + + if (resp0 != NULL) + *resp0 = entry->resp0; +#ifndef final_version + if (entry->res) { + printk(KERN_DEBUG "%s: CMD=0x%04x => res=0x%02x, " + "resp0=0x%04x\n", + dev->name, cmd, entry->res, entry->resp0); + } +#endif /* final_version */ + + res = entry->res; + done: + hostap_cmd_queue_free(local, entry, 1); + return res; +} + + +/* Issue given command (possibly after waiting in command queue) and use + * callback function to indicate command completion. This can be called both + * from user and interrupt context. */ +static int hfa384x_cmd_callback(struct net_device *dev, u16 cmd, u16 param0, + u16 *param1, + void (*callback)(struct net_device *dev, + void *context, u16 resp0, + u16 status), + void *context) +{ + local_info_t *local = (local_info_t *) dev->priv; + int issue, ret; + unsigned long flags; + struct hostap_cmd_queue *entry; + + if (local->cmd_queue_len >= HOSTAP_CMD_QUEUE_MAX_LEN + 2) { + printk(KERN_DEBUG "%s: hfa384x_cmd: cmd_queue full\n", + dev->name); + return -1; + } + + entry = (struct hostap_cmd_queue *) + kmalloc(sizeof(*entry), GFP_ATOMIC); + if (entry == NULL) { + printk(KERN_DEBUG "%s: hfa384x_cmd_callback - kmalloc " + "failed\n", dev->name); + return -ENOMEM; + } + memset(entry, 0, sizeof(*entry)); + atomic_set(&entry->usecnt, 1); + entry->type = CMD_CALLBACK; + entry->cmd = cmd; + entry->param0 = param0; + if (param1) { + entry->param1 = *param1; + } + entry->callback = callback; + entry->context = context; + + spin_lock_irqsave(&local->cmdlock, flags); + issue = list_empty(&local->cmd_queue); + if (issue) + entry->issuing = 1; + list_add_tail(&entry->list, &local->cmd_queue); + local->cmd_queue_len++; + spin_unlock_irqrestore(&local->cmdlock, flags); + + if (issue && hfa384x_cmd_issue(dev, entry)) + ret = -ETIMEDOUT; + else + ret = 0; + + hostap_cmd_queue_free(local, entry, ret); + + return ret; +} + + +static int hfa384x_cmd_wait(struct net_device *dev, u16 cmd, u16 param0, u16 *param1) +{ + int res, tries; + u16 reg; + + /* wait until busy bit is clear; this should always be clear since the + * commands are serialized */ + tries = HFA384X_CMD_BUSY_TIMEOUT; + while (HFA384X_INW(HFA384X_CMD_OFF) & HFA384X_CMD_BUSY && tries > 0) { + tries--; + udelay(1); + } + if (tries == 0) { + prism2_io_debug_error(dev, 4); + printk(KERN_DEBUG "%s: hfa384x_cmd_wait - timeout - " + "reg=0x%04x\n", dev->name, + HFA384X_INW(HFA384X_CMD_OFF)); + return -ETIMEDOUT; + } + + /* write command */ + HFA384X_OUTW(param0, HFA384X_PARAM0_OFF); + if (param1) { + HFA384X_OUTW(*param1, HFA384X_PARAM1_OFF); + } + HFA384X_OUTW(cmd, HFA384X_CMD_OFF); + + /* wait for command completion */ + if ((cmd & HFA384X_CMDCODE_MASK) == HFA384X_CMDCODE_DOWNLOAD) + tries = HFA384X_DL_COMPL_TIMEOUT; + else + tries = HFA384X_CMD_COMPL_TIMEOUT; + + while (!(HFA384X_INW(HFA384X_EVSTAT_OFF) & HFA384X_EV_CMD) && + tries > 0) { + tries--; + udelay(10); + } + if (tries == 0) { + reg = HFA384X_INW(HFA384X_EVSTAT_OFF); + prism2_io_debug_error(dev, 5); + printk(KERN_DEBUG "%s: hfa384x_cmd_wait - timeout2 - " + "reg=0x%04x\n", dev->name, reg); + return -ETIMEDOUT; + } + + res = (HFA384X_INW(HFA384X_STATUS_OFF) & + (BIT(14) | BIT(13) | BIT(12) | BIT(11) | BIT(10) | BIT(9) | + BIT(8))) >> 8; +#ifndef final_version + if (res) { + printk(KERN_DEBUG "%s: CMD=0x%04x => res=0x%02x\n", + dev->name, cmd, res); + } +#endif + + HFA384X_OUTW(HFA384X_EV_CMD, HFA384X_EVACK_OFF); + + return res; +} + + +static int hfa384x_cmd_no_wait(struct net_device *dev, u16 cmd, u16 param0) +{ + int tries; + u16 reg; + + /* wait until busy bit is clear */ + tries = HFA384X_CMD_BUSY_TIMEOUT; + while (HFA384X_INW(HFA384X_CMD_OFF) & HFA384X_CMD_BUSY && tries > 0) { + tries--; + udelay(1); + } + if (tries == 0) { + reg = HFA384X_INW(HFA384X_CMD_OFF); + prism2_io_debug_error(dev, 6); + printk("%s: hfa384x_cmd - timeout - reg=0x%04x\n", dev->name, + reg); + return -ETIMEDOUT; + } + + /* write command */ + HFA384X_OUTW(param0, HFA384X_PARAM0_OFF); + HFA384X_OUTW(cmd, HFA384X_CMD_OFF); + + return 0; +} + + +static inline int hfa384x_wait_offset(struct net_device *dev, u16 o_off) +{ + int tries = HFA384X_BAP_BUSY_TIMEOUT; + + while ((HFA384X_INW(o_off) & HFA384X_OFFSET_BUSY) && tries > 0) { + tries--; + udelay(10); + } + return (HFA384X_INW(o_off) & HFA384X_OFFSET_BUSY); +} + + +/* Offset must be even */ +static int hfa384x_setup_bap(struct net_device *dev, u16 bap, u16 id, + int offset) +{ + u16 o_off, s_off; + int ret = 0; + + if (offset % 2 || bap > 1) + return -EINVAL; + + if (bap == BAP1) { + o_off = HFA384X_OFFSET1_OFF; + s_off = HFA384X_SELECT1_OFF; + } else { + o_off = HFA384X_OFFSET0_OFF; + s_off = HFA384X_SELECT0_OFF; + } + + if (hfa384x_wait_offset(dev, o_off)) { + prism2_io_debug_error(dev, 7); + printk(KERN_DEBUG "%s: hfa384x_setup_bap - timeout before\n", + dev->name); + ret = -ETIMEDOUT; + goto out; + } + + HFA384X_OUTW(id, s_off); + HFA384X_OUTW(offset, o_off); + + if (hfa384x_wait_offset(dev, o_off)) { + prism2_io_debug_error(dev, 8); + printk(KERN_DEBUG "%s: hfa384x_setup_bap - timeout after\n", + dev->name); + ret = -ETIMEDOUT; + goto out; + } +#ifndef final_version + if (HFA384X_INW(o_off) & HFA384X_OFFSET_ERR) { + prism2_io_debug_error(dev, 9); + printk(KERN_DEBUG "%s: hfa384x_setup_bap - offset error " + "(%d,%d,%d)\n", + dev->name, bap, id, offset); + ret = -EINVAL; + } +#endif + + out: + return ret; +} + + +static int hfa384x_get_rid(struct net_device *dev, u16 rid, void *buf, int len, + int exact_len) +{ + local_info_t *local = (local_info_t *) dev->priv; + int res, rlen = 0; + struct hfa384x_rid_hdr rec; + + res = down_interruptible(&local->rid_bap_sem); + if (res) + return res; + + res = hfa384x_cmd(dev, HFA384X_CMDCODE_ACCESS, rid, NULL, NULL); + if (res) { + printk(KERN_DEBUG "%s: hfa384x_get_rid: CMDCODE_ACCESS failed " + "(res=%d, rid=%04x, len=%d)\n", + dev->name, res, rid, len); + up(&local->rid_bap_sem); + return res; + } + + spin_lock_bh(&local->baplock); + + res = hfa384x_setup_bap(dev, BAP0, rid, 0); + if (!res) + res = hfa384x_from_bap(dev, BAP0, &rec, sizeof(rec)); + + if (le16_to_cpu(rec.len) == 0) { + /* RID not available */ + res = -ENODATA; + } + + rlen = (le16_to_cpu(rec.len) - 1) * 2; + if (!res && exact_len && rlen != len) { + printk(KERN_DEBUG "%s: hfa384x_get_rid - RID len mismatch: " + "rid=0x%04x, len=%d (expected %d)\n", + dev->name, rid, rlen, len); + res = -ENODATA; + } + + if (!res) + res = hfa384x_from_bap(dev, BAP0, buf, len); + + spin_unlock_bh(&local->baplock); + up(&local->rid_bap_sem); + + if (res) { + if (res != -ENODATA) + printk(KERN_DEBUG "%s: hfa384x_get_rid (rid=%04x, " + "len=%d) - failed - res=%d\n", dev->name, rid, + len, res); + if (res == -ETIMEDOUT) + prism2_hw_reset(dev); + return res; + } + + return rlen; +} + + +static int hfa384x_set_rid(struct net_device *dev, u16 rid, void *buf, int len) +{ + local_info_t *local = (local_info_t *) dev->priv; + struct hfa384x_rid_hdr rec; + int res; + + rec.rid = cpu_to_le16(rid); + /* RID len in words and +1 for rec.rid */ + rec.len = cpu_to_le16(len / 2 + len % 2 + 1); + + res = down_interruptible(&local->rid_bap_sem); + if (res) + return res; + + spin_lock_bh(&local->baplock); + res = hfa384x_setup_bap(dev, BAP0, rid, 0); + if (!res) + res = hfa384x_to_bap(dev, BAP0, &rec, sizeof(rec)); + if (!res) + res = hfa384x_to_bap(dev, BAP0, buf, len); + spin_unlock_bh(&local->baplock); + + if (res) { + printk(KERN_DEBUG "%s: hfa384x_set_rid (rid=%04x, len=%d) - " + "failed - res=%d\n", dev->name, rid, len, res); + up(&local->rid_bap_sem); + return res; + } + + res = hfa384x_cmd(dev, HFA384X_CMDCODE_ACCESS_WRITE, rid, NULL, NULL); + up(&local->rid_bap_sem); + if (res) { + printk(KERN_DEBUG "%s: hfa384x_set_rid: CMDCODE_ACCESS_WRITE " + "failed (res=%d, rid=%04x, len=%d)\n", + dev->name, res, rid, len); + return res; + } + + if (res == -ETIMEDOUT) + prism2_hw_reset(dev); + + return res; +} + + +static void hfa384x_disable_interrupts(struct net_device *dev) +{ + /* disable interrupts and clear event status */ + HFA384X_OUTW(0, HFA384X_INTEN_OFF); + HFA384X_OUTW(0xffff, HFA384X_EVACK_OFF); +} + + +static void hfa384x_enable_interrupts(struct net_device *dev) +{ + /* ack pending events and enable interrupts from selected events */ + HFA384X_OUTW(0xffff, HFA384X_EVACK_OFF); + HFA384X_OUTW(HFA384X_EVENT_MASK, HFA384X_INTEN_OFF); +} + + +static void hfa384x_events_no_bap0(struct net_device *dev) +{ + HFA384X_OUTW(HFA384X_EVENT_MASK & ~HFA384X_BAP0_EVENTS, + HFA384X_INTEN_OFF); +} + + +static void hfa384x_events_all(struct net_device *dev) +{ + HFA384X_OUTW(HFA384X_EVENT_MASK, HFA384X_INTEN_OFF); +} + + +static void hfa384x_events_only_cmd(struct net_device *dev) +{ + HFA384X_OUTW(HFA384X_EV_CMD, HFA384X_INTEN_OFF); +} + +static u16 hfa384x_allocate_fid(struct net_device *dev, int len) +{ + int tries; + u16 fid; + + /* FIX: this could be replace with hfa384x_cmd() if the Alloc event + * below would be handled like CmdCompl event (sleep here, wake up from + * interrupt handler */ + if (hfa384x_cmd_wait(dev, HFA384X_CMDCODE_ALLOC, len, NULL)) { + printk(KERN_DEBUG "%s: cannot allocate fid, len=%d\n", + dev->name, len); + return 0xffff; + } + + tries = HFA384X_ALLOC_COMPL_TIMEOUT; + while (!(HFA384X_INW(HFA384X_EVSTAT_OFF) & HFA384X_EV_ALLOC) && + tries > 0) { + tries--; + udelay(10); + } + if (tries == 0) { + printk("%s: fid allocate, len=%d - timeout\n", dev->name, len); + return 0xffff; + } + + fid = HFA384X_INW(HFA384X_ALLOCFID_OFF); + HFA384X_OUTW(HFA384X_EV_ALLOC, HFA384X_EVACK_OFF); + + return fid; +} + + +static int prism2_reset_port(struct net_device *dev) +{ + local_info_t *local = (local_info_t *) dev->priv; + int res; + + if (!local->dev_enabled) + return 0; + + res = hfa384x_cmd(dev, HFA384X_CMDCODE_DISABLE, 0, + NULL, NULL); + if (res) + printk(KERN_DEBUG "%s: reset port failed to disable port\n", + dev->name); + else { + res = hfa384x_cmd(dev, HFA384X_CMDCODE_ENABLE, 0, + NULL, NULL); + if (res) + printk(KERN_DEBUG "%s: reset port failed to enable " + "port\n", dev->name); + } + + return res; +} + + +static int prism2_get_version_info(struct net_device *dev, u16 rid, + const char *txt) +{ + struct hfa384x_comp_ident comp; + + if (hfa384x_get_rid(dev, rid, &comp, sizeof(comp), 1) < 0) { + printk(KERN_DEBUG "Could not get RID for component %s\n", txt); + return -1; + } + + printk(KERN_INFO "%s: %s: id=0x%02x v%d.%d.%d\n", dev->name, txt, + __le16_to_cpu(comp.id), __le16_to_cpu(comp.major), + __le16_to_cpu(comp.minor), __le16_to_cpu(comp.variant)); + return 0; +} + + +static int prism2_setup_rids(struct net_device *dev) +{ + local_info_t *local = (local_info_t *) dev->priv; + u16 tmp; + int ret = 0; + + hostap_set_word(dev, HFA384X_RID_TICKTIME, 2000); + + if (!local->fw_ap) { + tmp = hostap_get_porttype(local); + ret = hostap_set_word(dev, HFA384X_RID_CNFPORTTYPE, tmp); + if (ret) { + printk("%s: Port type setting to %d failed\n", + dev->name, tmp); + goto fail; + } + } + + /* Setting SSID to empty string seems to kill the card in Host AP mode + */ + if (local->iw_mode != IW_MODE_MASTER || local->essid[0] != '\0') { + ret = hostap_set_string(dev, HFA384X_RID_CNFOWNSSID, + local->essid); + if (ret) { + printk("%s: AP own SSID setting failed\n", dev->name); + goto fail; + } + } + + ret = hostap_set_word(dev, HFA384X_RID_CNFMAXDATALEN, + PRISM2_DATA_MAXLEN); + if (ret) { + printk("%s: MAC data length setting to %d failed\n", + dev->name, PRISM2_DATA_MAXLEN); + goto fail; + } + + if (hfa384x_get_rid(dev, HFA384X_RID_CHANNELLIST, &tmp, 2, 1) < 0) { + printk("%s: Channel list read failed\n", dev->name); + ret = -EINVAL; + goto fail; + } + local->channel_mask = __le16_to_cpu(tmp); + + if (local->channel < 1 || local->channel > 14 || + !(local->channel_mask & (1 << (local->channel - 1)))) { + printk(KERN_WARNING "%s: Channel setting out of range " + "(%d)!\n", dev->name, local->channel); + ret = -EBUSY; + goto fail; + } + + ret = hostap_set_word(dev, HFA384X_RID_CNFOWNCHANNEL, local->channel); + if (ret) { + printk("%s: Channel setting to %d failed\n", + dev->name, local->channel); + goto fail; + } + + ret = hostap_set_word(dev, HFA384X_RID_CNFBEACONINT, + local->beacon_int); + if (ret) { + printk("%s: Beacon interval setting to %d failed\n", + dev->name, local->beacon_int); + /* this may fail with Symbol/Lucent firmware */ + if (ret == -ETIMEDOUT) + goto fail; + } + + ret = hostap_set_word(dev, HFA384X_RID_CNFOWNDTIMPERIOD, + local->dtim_period); + if (ret) { + printk("%s: DTIM period setting to %d failed\n", + dev->name, local->dtim_period); + /* this may fail with Symbol/Lucent firmware */ + if (ret == -ETIMEDOUT) + goto fail; + } + + /* click promisc */ + //local->is_promisc = 1; + /* click promisc */ + + ret = hostap_set_word(dev, HFA384X_RID_PROMISCUOUSMODE, + local->is_promisc); + if (ret) + printk(KERN_INFO "%s: Setting promiscuous mode (%d) failed\n", + dev->name, local->is_promisc); + + if (!local->fw_ap) { + ret = hostap_set_string(dev, HFA384X_RID_CNFDESIREDSSID, + local->essid); + if (ret) { + printk("%s: Desired SSID setting failed\n", dev->name); + goto fail; + } + } + + /* Setup TXRateControl, defaults to allow use of 1, 2, 5.5, and + * 11 Mbps in automatic TX rate fallback and 1 and 2 Mbps as basic + * rates */ + if (local->tx_rate_control == 0) { + local->tx_rate_control = + HFA384X_RATES_1MBPS | + HFA384X_RATES_2MBPS | + HFA384X_RATES_5MBPS | + HFA384X_RATES_11MBPS; + } + if (local->basic_rates == 0) + local->basic_rates = HFA384X_RATES_1MBPS | HFA384X_RATES_2MBPS; + + if (!local->fw_ap) { + ret = hostap_set_word(dev, HFA384X_RID_TXRATECONTROL, + local->tx_rate_control); + if (ret) { + printk("%s: TXRateControl setting to %d failed\n", + dev->name, local->tx_rate_control); + goto fail; + } + + ret = hostap_set_word(dev, HFA384X_RID_CNFSUPPORTEDRATES, + local->tx_rate_control); + if (ret) { + printk("%s: cnfSupportedRates setting to %d failed\n", + dev->name, local->tx_rate_control); + } + + ret = hostap_set_word(dev, HFA384X_RID_CNFBASICRATES, + local->basic_rates); + if (ret) { + printk("%s: cnfBasicRates setting to %d failed\n", + dev->name, local->basic_rates); + } + + ret = hostap_set_word(dev, HFA384X_RID_CREATEIBSS, 1); + if (ret) { + printk("%s: Create IBSS setting to 1 failed\n", + dev->name); + } + } + + if (local->name_set) + (void) hostap_set_string(dev, HFA384X_RID_CNFOWNNAME, + local->name); + + if (hostap_set_encryption(local)) { + printk(KERN_INFO "%s: could not configure encryption\n", + dev->name); + } + + (void) hostap_set_antsel(local); + + if (local->host_roaming && + hostap_set_word(dev, HFA384X_RID_CNFROAMINGMODE, + HFA384X_ROAMING_HOST)) { + printk(KERN_INFO "%s: could not set host roaming\n", + dev->name); + } + + if (local->sta_fw_ver >= PRISM2_FW_VER(1,6,3) && + hostap_set_word(dev, HFA384X_RID_CNFENHSECURITY, local->enh_sec)) + printk(KERN_INFO "%s: cnfEnhSecurity setting to 0x%x failed\n", + dev->name, local->enh_sec); + + fail: + return ret; +} + + +static void prism2_clear_cmd_queue(local_info_t *local) +{ + struct list_head *ptr, *n; + unsigned long flags; + struct hostap_cmd_queue *entry; + + spin_lock_irqsave(&local->cmdlock, flags); + for (ptr = local->cmd_queue.next, n = ptr->next; + ptr != &local->cmd_queue; ptr = n, n = ptr->next) { + entry = list_entry(ptr, struct hostap_cmd_queue, list); + atomic_inc(&entry->usecnt); + printk(KERN_DEBUG "%s: removed pending cmd_queue entry " + "(type=%d, cmd=0x%04x, param0=0x%04x)\n", + local->dev->name, entry->type, entry->cmd, + entry->param0); + __hostap_cmd_queue_free(local, entry, 1); + } + if (local->cmd_queue_len) { + printk(KERN_DEBUG "%s: cmd_queue_len (%d) not zero after " + "flush\n", local->dev->name, local->cmd_queue_len); + local->cmd_queue_len = 0; + } + spin_unlock_irqrestore(&local->cmdlock, flags); +} + + +static int prism2_hw_init(struct net_device *dev, int initial) +{ + local_info_t *local = (local_info_t *) dev->priv; + int ret, i, first = 1; + + PDEBUG(DEBUG_FLOW, "prism2_hw_init()\n"); + + clear_bit(HOSTAP_BITS_TRANSMIT, &local->bits); + + init: + /* initialize HFA 384x */ + ret = hfa384x_cmd_no_wait(dev, HFA384X_CMDCODE_INIT, 0); + if (ret) { + printk("%s: first command failed - is the card compatible?\n", + dev_info); + goto failed; + } + i = HFA384X_INIT_TIMEOUT; + while (!(HFA384X_INW(HFA384X_EVSTAT_OFF) & HFA384X_EV_CMD) && i > 0) { + i--; + udelay(10); + } + if (first && i == HFA384X_INIT_TIMEOUT) { + /* EvStat has Cmd bit set in some cases, so retry once if no + * wait was needed */ + HFA384X_OUTW(HFA384X_EV_CMD, HFA384X_EVACK_OFF); + printk(KERN_DEBUG "%s: init command completed too quickly - " + "retrying\n", dev->name); + first = 0; + goto init; + } + if (i == 0) { + printk("%s: card initialization timed out\n", dev_info); + goto failed; + } + printk(KERN_DEBUG "prism2_hw_init: initialized in %d iterations\n", + HFA384X_INIT_TIMEOUT - i); + HFA384X_OUTW(HFA384X_EV_CMD, HFA384X_EVACK_OFF); + + if (local->hw_downloading) + return 0; + +#ifdef PRISM2_DOWNLOAD_SUPPORT + local->pda = prism2_read_pda(dev); +#endif /* PRISM2_DOWNLOAD_SUPPORT */ + + hfa384x_disable_interrupts(dev); + +#ifndef final_version + HFA384X_OUTW(HFA384X_MAGIC, HFA384X_SWSUPPORT0_OFF); + if (HFA384X_INW(HFA384X_SWSUPPORT0_OFF) != HFA384X_MAGIC) { + printk("SWSUPPORT0 write/read failed: %04X != %04X\n", + HFA384X_INW(HFA384X_SWSUPPORT0_OFF), HFA384X_MAGIC); + goto failed; + } +#endif + + /* FIX: could convert allocate_fid to use sleeping CmdCompl wait and + * enable interrupts before this. This would also require some sort of + * sleeping AllocEv waiting */ + + /* allocate TX FIDs */ + local->txfid_len = PRISM2_TXFID_LEN; + for (i = 0; i < PRISM2_TXFID_COUNT; i++) { + local->txfid[i] = hfa384x_allocate_fid(dev, local->txfid_len); + if (local->txfid[i] == 0xffff && local->txfid_len > 1600) { + local->txfid[i] = hfa384x_allocate_fid(dev, 1600); + if (local->txfid[i] != 0xffff) { + printk(KERN_DEBUG "%s: Using shorter TX FID " + "(1600 bytes)\n", dev->name); + local->txfid_len = 1600; + } + } + if (local->txfid[i] == 0xffff) + goto failed; + local->intransmitfid[i] = PRISM2_TXFID_EMPTY; + } + + hfa384x_events_only_cmd(dev); + + if (initial) { + /* get card version information */ + prism2_get_version_info(dev, HFA384X_RID_NICID, "NIC"); + prism2_get_version_info(dev, HFA384X_RID_PRIID, "PRI"); + prism2_get_version_info(dev, HFA384X_RID_STAID, "STA"); + + prism2_check_sta_fw_version(local); + + if (hfa384x_get_rid(dev, HFA384X_RID_CNFOWNMACADDR, + &dev->dev_addr, 6, 1) < 0) { + printk("%s: could not get own MAC address\n", + dev->name); + } + if (local->apdev) + memcpy(local->apdev->dev_addr, dev->dev_addr, + ETH_ALEN); + if (local->stadev) + memcpy(local->stadev->dev_addr, dev->dev_addr, + ETH_ALEN); + } else if (local->fw_ap) + prism2_check_sta_fw_version(local); + + prism2_setup_rids(dev); + + /* MAC is now configured, but port 0 is not yet enabled */ + return 0; + + failed: + printk(KERN_WARNING "%s: Initialization failed\n", dev_info); + return 1; +} + + +static int prism2_hw_enable(struct net_device *dev, int initial) +{ + local_info_t *local = (local_info_t *) dev->priv; + int was_resetting = local->hw_resetting; + + if (hfa384x_cmd(dev, HFA384X_CMDCODE_ENABLE, 0, NULL, NULL)) { + printk("%s: MAC port 0 enabling failed\n", dev->name); + return 1; + } + + local->hw_ready = 1; + local->hw_reset_tries = 0; + local->hw_resetting = 0; + hfa384x_enable_interrupts(dev); + + /* at least D-Link DWL-650 seems to require additional port reset + * before it starts acting as an AP, so reset port automatically + * here just in case */ + if (initial && prism2_reset_port(dev)) { + printk("%s: MAC port 0 reseting failed\n", dev->name); + return 1; + } + + if (was_resetting && netif_queue_stopped(dev)) { + /* If hw_reset() was called during pending transmit, netif + * queue was stopped. Wake it up now since the wlan card has + * been resetted. */ + hostap_netif_wake_queues(dev); + } + + return 0; +} + + +static int prism2_hw_config(struct net_device *dev, int initial) +{ + local_info_t *local = (local_info_t *) dev->priv; + if (local->hw_downloading) + return 1; + + if (prism2_hw_init(dev, initial)) + return 1; + + if (!initial || !delayed_enable) { + if (!local->dev_enabled) + prism2_callback(local, PRISM2_CALLBACK_ENABLE); + local->dev_enabled = 1; + return prism2_hw_enable(dev, initial); + } + + return 0; +} + + +static void prism2_hw_shutdown(struct net_device *dev, int no_disable) +{ + local_info_t *local = (local_info_t *) dev->priv; + + /* Allow only command completion events during disable */ + hfa384x_events_only_cmd(dev); + + local->hw_ready = 0; + if (local->dev_enabled) + prism2_callback(local, PRISM2_CALLBACK_DISABLE); + local->dev_enabled = 0; + + if (local->func->card_present && !local->func->card_present(local)) { + printk(KERN_DEBUG "%s: card already removed or not configured " + "during shutdown\n", dev->name); + return; + } + + if ((no_disable & HOSTAP_HW_NO_DISABLE) == 0 && + hfa384x_cmd(dev, HFA384X_CMDCODE_DISABLE, 0, NULL, NULL)) + printk(KERN_WARNING "%s: Shutdown failed\n", dev_info); + + hfa384x_disable_interrupts(dev); + + if (no_disable & HOSTAP_HW_ENABLE_CMDCOMPL) + hfa384x_events_only_cmd(dev); + else + prism2_clear_cmd_queue(local); +} + + +static void prism2_hw_reset(struct net_device *dev) +{ + local_info_t *local = (local_info_t *) dev->priv; + +#if 0 + static long last_reset = 0; + + /* do not reset card more than once per second to avoid ending up in a + * busy loop reseting the card */ + if (last_reset + HZ >= jiffies) + return; + last_reset = jiffies; +#endif + + if (in_interrupt()) { + printk(KERN_DEBUG "%s: driver bug - prism2_hw_reset() called " + "in interrupt context\n", dev->name); + return; + } + + if (local->hw_downloading) + return; + + if (local->hw_resetting) { + printk(KERN_WARNING "%s: %s: already resetting card - " + "ignoring reset request\n", dev_info, dev->name); + return; + } + + local->hw_reset_tries++; + if (local->hw_reset_tries > 10) { + printk(KERN_WARNING "%s: too many reset tries, skipping\n", + dev->name); + return; + } + + printk(KERN_WARNING "%s: %s: resetting card\n", dev_info, dev->name); + hfa384x_disable_interrupts(dev); + local->hw_resetting = 1; + if (local->func->cor_sreset) { + /* Host system seems to hang in some cases with high traffic + * load or shared interrupts during COR sreset. Disable shared + * interrupts during reset to avoid these crashes. COS sreset + * takes quite a long time, so it is unfortunate that this + * seems to be needed. Anyway, I do not know of any better way + * of avoiding the crash. */ + disable_irq(dev->irq); + local->func->cor_sreset(local); + enable_irq(dev->irq); + } + prism2_hw_shutdown(dev, 1); + prism2_hw_config(dev, 0); + local->hw_resetting = 0; +} + + +static void prism2_schedule_reset(local_info_t *local) +{ + PRISM2_SCHEDULE_TASK(&local->reset_queue); +} + + +/* Called only as scheduled task after noticing card timeout in interrupt + * context */ +static void handle_reset_queue(void *data) +{ + local_info_t *local = (local_info_t *) data; + + printk(KERN_DEBUG "%s: scheduled card reset\n", local->dev->name); + prism2_hw_reset(local->dev); + + if (netif_queue_stopped(local->dev)) { + int i; + + for (i = 0; i < PRISM2_TXFID_COUNT; i++) + if (local->intransmitfid[i] == PRISM2_TXFID_EMPTY) { + PDEBUG(DEBUG_EXTRA, "prism2_tx_timeout: " + "wake up queue\n"); + hostap_netif_wake_queues(local->dev); + break; + } + } + +#ifndef NEW_MODULE_CODE + MOD_DEC_USE_COUNT; +#endif +} + + +/* update trans_start for all used devices */ +static void prism2_netif_update_trans_start(struct net_device *dev) +{ + local_info_t *local = (local_info_t *) dev->priv; + prism2_wds_info_t *wds; + + if (local->dev) + local->dev->trans_start = jiffies; + if (local->apdev) + local->apdev->trans_start = jiffies; + if (local->stadev) + local->stadev->trans_start = jiffies; + + wds = local->wds; + while (wds != NULL) { + wds->dev.trans_start = jiffies; + wds = wds->next; + } +} + + +static int prism2_get_txfid_idx(local_info_t *local) +{ + int idx, end; + + spin_lock_bh(&local->txfidlock); + end = idx = local->next_txfid; + do { + if (local->intransmitfid[idx] == PRISM2_TXFID_EMPTY) { + local->intransmitfid[idx] = PRISM2_TXFID_RESERVED; + spin_unlock_bh(&local->txfidlock); + return idx; + } + idx++; + if (idx >= PRISM2_TXFID_COUNT) + idx = 0; + } while (idx != end); + spin_unlock_bh(&local->txfidlock); + + PDEBUG(DEBUG_EXTRA2, "prism2_get_txfid_idx: no room in txfid buf: " + "packet dropped\n"); + local->stats.tx_dropped++; + + return -1; +} + + +/* Called only from hardware IRQ */ +static void prism2_transmit_cb(struct net_device *dev, void *context, + u16 resp0, u16 res) +{ + local_info_t *local = (local_info_t *) dev->priv; + int idx = (int) context; + + if (res) { + printk(KERN_DEBUG "%s: prism2_transmit_cb - res=0x%02x\n", + dev->name, res); + return; + } + + if (idx < 0 || idx >= PRISM2_TXFID_COUNT) { + printk(KERN_DEBUG "%s: prism2_transmit_cb called with invalid " + "idx=%d\n", dev->name, idx); + return; + } + + if (!test_and_clear_bit(HOSTAP_BITS_TRANSMIT, &local->bits)) { + printk(KERN_DEBUG "%s: driver bug: prism2_transmit_cb called " + "with no pending transmit\n", dev->name); + } + + if (netif_queue_stopped(dev)) { + /* ready for next TX, so wake up queue that was stopped in + * prism2_transmit() */ + hostap_netif_wake_queues(dev); + } + + /* FIX: is some locking needed for txfid data? */ + + /* With reclaim, Resp0 contains new txfid for transmit; the old txfid + * will be automatically allocated for the next TX frame */ + local->intransmitfid[idx] = resp0; + + PDEBUG(DEBUG_FID, "%s: prism2_cmd_ev: txfid[%d]=0x%04x, resp0=0x%04x, " + "transmit_txfid=0x%04x\n", dev->name, idx, local->txfid[idx], + resp0, local->intransmitfid[local->next_txfid]); + + idx++; + if (idx >= PRISM2_TXFID_COUNT) + idx = 0; + local->next_txfid = idx; + + /* check if all TX buffers are occupied */ + do { + if (local->intransmitfid[idx] == PRISM2_TXFID_EMPTY) { + return; + } + idx++; + if (idx >= PRISM2_TXFID_COUNT) + idx = 0; + } while (idx != local->next_txfid); + + /* no empty TX buffers, stop queue */ + hostap_netif_stop_queues(dev); +} + + +/* Called only from software IRQ if PCI bus master is not used (with bus master + * this can be called both from software and hardware IRQ) */ +static int prism2_transmit(struct net_device *dev, int idx) +{ + local_info_t *local = (local_info_t *) dev->priv; + int res; + + /* The driver tries to stop netif queue so that there would not be + * more than one attempt to transmit frames going on; check that this + * is really the case */ + + if (test_and_set_bit(HOSTAP_BITS_TRANSMIT, &local->bits)) { + printk(KERN_DEBUG "%s: driver bug - prism2_transmit() called " + "when previous TX was pending\n", dev->name); + return -1; + } + + /* stop the queue for the time that transmit is pending */ + hostap_netif_stop_queues(dev); + + /* transmit packet */ + res = hfa384x_cmd_callback( + dev, + HFA384X_CMDCODE_TRANSMIT | HFA384X_CMD_TX_RECLAIM, + local->txfid[idx], + NULL, + prism2_transmit_cb, (void *) idx); + + if (res) { + struct net_device_stats *stats; + printk(KERN_DEBUG "%s: prism2_transmit: CMDCODE_TRANSMIT " + "failed (res=%d)\n", dev->name, res); + stats = hostap_get_stats(dev); + stats->tx_dropped++; + hostap_netif_wake_queues(dev); + return -1; + } + prism2_netif_update_trans_start(dev); + + /* Since we did not wait for command completion, the card continues + * to process on the background and we will finish handling when + * command completion event is handled (prism2_cmd_ev() function) */ + + return 0; +} + + +/* Called only from hardware IRQ */ +static void prism2_cmd_ev(struct net_device *dev) +{ + local_info_t *local = (local_info_t *) dev->priv; + struct hostap_cmd_queue *entry = NULL; + spin_lock(&local->cmdlock); + if (!list_empty(&local->cmd_queue)) { + entry = list_entry(local->cmd_queue.next, + struct hostap_cmd_queue, list); + atomic_inc(&entry->usecnt); + list_del_init(&entry->list); + local->cmd_queue_len--; + + if (!entry->issued) { + printk(KERN_DEBUG "%s: Command completion event, but " + "cmd not issued\n", dev->name); + __hostap_cmd_queue_free(local, entry, 1); + entry = NULL; + } + } + spin_unlock(&local->cmdlock); + + if (!entry) { + HFA384X_OUTW(HFA384X_EV_CMD, HFA384X_EVACK_OFF); + printk(KERN_DEBUG "%s: Command completion event, but no " + "pending commands\n", dev->name); + return; + } + + entry->resp0 = HFA384X_INW(HFA384X_RESP0_OFF); + entry->res = (HFA384X_INW(HFA384X_STATUS_OFF) & + (BIT(14) | BIT(13) | BIT(12) | BIT(11) | BIT(10) | + BIT(9) | BIT(8))) >> 8; + HFA384X_OUTW(HFA384X_EV_CMD, HFA384X_EVACK_OFF); + + /* TODO: rest of the CmdEv handling could be moved to tasklet */ + if (entry->type == CMD_SLEEP) { + entry->type = CMD_COMPLETED; + wake_up_interruptible(&entry->compl); + } else if (entry->type == CMD_CALLBACK) { + if (entry->callback) + entry->callback(dev, entry->context, entry->resp0, + entry->res); + } else { + printk(KERN_DEBUG "%s: Invalid command completion type %d\n", + dev->name, entry->type); + } + hostap_cmd_queue_free(local, entry, 1); + + /* issue next command, if pending */ + entry = NULL; + spin_lock(&local->cmdlock); + if (!list_empty(&local->cmd_queue)) { + entry = list_entry(local->cmd_queue.next, + struct hostap_cmd_queue, list); + if (entry->issuing) { + /* hfa384x_cmd() has already started issuing this + * command, so do not start here */ + entry = NULL; + } + if (entry) + atomic_inc(&entry->usecnt); + } + spin_unlock(&local->cmdlock); + + if (entry) { + /* issue next command; if command issuing fails, remove the + * entry from cmd_queue */ + int res = hfa384x_cmd_issue(dev, entry); + spin_lock(&local->cmdlock); + __hostap_cmd_queue_free(local, entry, res); + spin_unlock(&local->cmdlock); + } +} + + +#if defined(PRISM2_PCI) && defined(PRISM2_BUS_MASTER) +static void prism2_tx_cb(struct net_device *dev, void *context, + u16 resp0, u16 res) +{ + local_info_t *local = (local_info_t *) dev->priv; + unsigned long addr; + int buf_len = (int) context; + + if (res) { + printk(KERN_DEBUG "%s: prism2_tx_cb - res=0x%02x\n", + dev->name, res); + return; + } + + addr = virt_to_phys(local->bus_m0_buf); + HFA384X_OUTW((addr & 0xffff0000) >> 16, HFA384X_PCI_M0_ADDRH_OFF); + HFA384X_OUTW(addr & 0x0000ffff, HFA384X_PCI_M0_ADDRL_OFF); + HFA384X_OUTW(buf_len / 2, HFA384X_PCI_M0_LEN_OFF); + HFA384X_OUTW(HFA384X_PCI_CTL_TO_BAP, HFA384X_PCI_M0_CTL_OFF); +} +#endif /* PRISM2_PCI and PRISM2_BUS_MASTER */ + +/* Called only from software IRQ */ +static void prism2_tx(struct net_device *dev, void *context, + u16 resp0, u16 res0) +{ + struct sk_buff *skb = (struct sk_buff *) context; + local_info_t *local = (local_info_t *) dev->priv; + int res, idx = -1, ret = 1, data_len; + struct hfa384x_tx_frame txdesc; + u16 fc, ethertype = 0; + prism2_wds_info_t *wds = NULL; + enum { WDS_NO, WDS_OWN_FRAME, WDS_COMPLIANT_FRAME } use_wds = WDS_NO; + struct net_device_stats *stats; + u8 *wepbuf = NULL; + int wepbuf_len = 0, host_encrypt = 0; + struct prism2_crypt_data *crypt = NULL; + void *sta = NULL; + u8 *encaps_data; + int encaps_len, skip_header_bytes; + int no_encrypt = 0; + int to_assoc_ap = 0; + + if (res0) { + local->txpower = 0; + printk(KERN_DEBUG "%s: prism2_tx - res=0x%02x\n", + dev->name, res0); + return; + } + + prism2_callback(local, PRISM2_CALLBACK_TX_START); + stats = hostap_get_stats(dev); + + if ((local->func->card_present && !local->func->card_present(local)) || + !local->hw_ready) { + if (net_ratelimit()) + printk(KERN_DEBUG "%s: prism2_tx: hw not ready - " + "skipping\n", dev->name); + ret = 0; + goto fail; + } + + if (skb->len < ETH_HLEN) { + printk(KERN_DEBUG "%s: prism2_tx: short skb (len=%d)\n", + dev->name, skb->len); + ret = 0; + goto fail; + } + + if (local->dev != dev) { + wds = (prism2_wds_info_t *) 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; + wds = NULL; + use_wds = WDS_NO; + } + } 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); + ret = 0; + goto fail; + } 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; + } + } + + if (local->host_encrypt) { + /* Set crypt to default algorithm and key; will be replaced in + * AP code if STA has own alg/key */ + crypt = local->crypt; + host_encrypt = 1; + } + + if (skb->protocol == __constant_htons(ETH_P_HOSTAP)) { + /* frame from prism2_send_mgmt() */ + if (skb->len < sizeof(txdesc)) { + printk(KERN_DEBUG "%s: prism2_tx: short ETH_P_HOSTAP " + "skb\n", dev->name); + ret = 0; + goto fail; + } + memcpy(&txdesc, skb->data, sizeof(txdesc)); + skb_pull(skb, sizeof(txdesc)); + encaps_data = NULL; + encaps_len = 0; + skip_header_bytes = 0; + data_len = skb->len; + + fc = le16_to_cpu(txdesc.frame_control); + + /* data frames use normal host encryption, if needed */ + if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_DATA) + goto data_txdesc_set; + + /* mgmt/ctrl frames do not need further processing, so skip to + * frame transmit */ + goto frame_processing_done; + } + + /* 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(&txdesc, 0, sizeof(txdesc)); + + txdesc.tx_control = __cpu_to_le16(local->tx_control); + + /* Length of data after txdesc */ + data_len = skb->len - ETH_HLEN; + 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); + data_len += encaps_len + 2; + skip_header_bytes -= 2; + } else if (ethertype >= 0x600) { + encaps_data = rfc1042_header; + encaps_len = sizeof(rfc1042_header); + data_len += encaps_len + 2; + skip_header_bytes -= 2; + } + + fc = (WLAN_FC_TYPE_DATA << 2) | (WLAN_FC_STYPE_DATA << 4); + 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(&txdesc.addr4, skb->data + ETH_ALEN, 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 */ + data_len += 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(&txdesc.addr1, 0xff, ETH_ALEN); + else if (wds) + memcpy(&txdesc.addr1, wds->remote_addr, ETH_ALEN); + else + memcpy(&txdesc.addr1, local->bssid, ETH_ALEN); + memcpy(&txdesc.addr2, dev->dev_addr, ETH_ALEN); + memcpy(&txdesc.addr3, skb->data, ETH_ALEN); + + memcpy(&txdesc.dst_addr, &txdesc.addr3, ETH_ALEN); + memcpy(&txdesc.src_addr, &txdesc.addr2, 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(&txdesc.addr1, skb->data, ETH_ALEN); + /* FIX - addr2 replaced by f/w, so no need to fill it now(?) */ + memcpy(&txdesc.addr2, dev->dev_addr, ETH_ALEN); + memcpy(&txdesc.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(&txdesc.addr1, to_assoc_ap ? + local->assoc_ap_addr : local->bssid, ETH_ALEN); + memcpy(&txdesc.addr2, skb->data + ETH_ALEN, ETH_ALEN); + memcpy(&txdesc.addr3, skb->data, ETH_ALEN); + } else if (local->iw_mode == IW_MODE_ADHOC) { + /* not From/To DS: Addr1 = DA, Addr2 = SA, Addr3 = BSSID */ + memcpy(&txdesc.addr1, skb->data, ETH_ALEN); + memcpy(&txdesc.addr2, skb->data + ETH_ALEN, ETH_ALEN); + memcpy(&txdesc.addr3, local->bssid, ETH_ALEN); + } + + if (WIFI_FROM_CLICK(skb)) { + /* begin click hack */ + int rate = WIFI_RATE_ANNO(skb); + switch (rate) { + /* + * see section 4.3.2.3.2 (page 4-30) in the developors manual + * for these values + * --jbicket + */ + case 0: + txdesc.tx_rate = 0x00; + break; + case 1: + txdesc.tx_rate = 0x0A; + break; + case 2: + txdesc.tx_rate = 0x14; + break; + case 5: + txdesc.tx_rate = 0x37; + break; + case 11: + txdesc.tx_rate = 0x6E; + break; + default: + printk(KERN_WARNING "%s: invalid attempt for packet rate %d\n", + local->dev->name, rate); + txdesc.tx_rate = 0; + } + } + /* end click hack */ + txdesc.frame_control = __cpu_to_le16(fc); + txdesc.data_len = __cpu_to_le16(data_len); + txdesc.len = __cpu_to_be16(data_len); + if (use_wds == WDS_NO) + memcpy(&txdesc.dst_addr, skb->data, 2 * ETH_ALEN); + + skb->dev = dev; + + data_txdesc_set: + if (to_assoc_ap) + goto skip_ap_processing; + + switch (hostap_handle_sta_tx(local, skb, &txdesc, use_wds != WDS_NO, + host_encrypt, &crypt, &sta)) { + case AP_TX_CONTINUE: + break; + case AP_TX_CONTINUE_NOT_AUTHORIZED: + if (local->ieee_802_1x && ethertype != ETH_P_PAE && + use_wds == WDS_NO) { + printk(KERN_DEBUG "%s: dropped frame to unauthorized " + "port (IEEE 802.1X): ethertype=0x%04x\n", + dev->name, ethertype); + hostap_dump_tx_header(dev->name, &txdesc); + + ret = 0; /* drop packet */ + stats->tx_dropped++; + goto fail; + } + break; + case AP_TX_DROP: + ret = 0; /* drop packet */ + 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; + } + + skip_ap_processing: + + if (local->ieee_802_1x && ethertype == ETH_P_PAE) { + if (crypt) { + no_encrypt = 1; + printk(KERN_DEBUG "%s: TX: IEEE 802.1X - passing " + "unencrypted EAPOL frame\n", dev->name); + } + crypt = NULL; /* no encryption for IEEE 802.1X frames */ + } + + if (crypt && (!crypt->ops || !crypt->ops->encrypt)) + crypt = NULL; + else if ((crypt || local->crypt) && !no_encrypt) { + /* Add ISWEP flag both for firmware and host based encryption + */ + fc |= WLAN_FC_ISWEP; + txdesc.frame_control = cpu_to_le16(fc); + } + + if (crypt) { + /* Perform host driver -based encryption */ + u8 *pos; + int olen; + + olen = data_len; + data_len += crypt->ops->extra_prefix_len + + crypt->ops->extra_postfix_len; + txdesc.data_len = cpu_to_le16(data_len); + txdesc.len = cpu_to_be16(data_len); + + wepbuf_len = data_len; + wepbuf = (u8 *) kmalloc(wepbuf_len, GFP_ATOMIC); + if (wepbuf == NULL) { + printk(KERN_DEBUG "%s: could not allocate TX wepbuf\n", + dev->name); + goto fail; + } + + pos = wepbuf + crypt->ops->extra_prefix_len; + if (encaps_len > 0) { + memcpy(pos, encaps_data, encaps_len); + pos += encaps_len; + } + memcpy(pos, skb->data + skip_header_bytes, + skb->len - skip_header_bytes); + if (use_wds == WDS_OWN_FRAME) { + memcpy(pos + skb->len - skip_header_bytes, + skb->data + ETH_ALEN, ETH_ALEN); + } + + atomic_inc(&crypt->refcnt); + olen = crypt->ops->encrypt(wepbuf, olen, crypt->priv); + atomic_dec(&crypt->refcnt); + if (olen > wepbuf_len) { + printk(KERN_WARNING "%s: encrypt overwrote wepbuf " + "(%d > %d)\n", dev->name, olen, wepbuf_len); + } + if (olen < 0) + goto fail; + + data_len = wepbuf_len = olen; + txdesc.data_len = cpu_to_le16(data_len); + txdesc.len = cpu_to_be16(data_len); + } + + + if (txdesc.sw_support != 0) { + printk(KERN_WARNING "%s: warning, sw_support is %d\n", dev->name, txdesc.sw_support); + } + int txskb_idx = 1; + while(txskb_idx < PRISM2_TXSKB_COUNT && local->txskb[txskb_idx]) { + txskb_idx++; + } + + if (txskb_idx < PRISM2_TXSKB_COUNT) { + //printk(KERN_WARNING "%s: using txskb buffer %d\n", dev->name, txskb_idx); + txdesc.sw_support = txskb_idx; + struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC); + local->txskb[txskb_idx] = skb2; + } else { + printk(KERN_WARNING "%s: out of txskb buffers!\n", dev->name); + txdesc.sw_support = 0; + } + + + frame_processing_done: + + + idx = prism2_get_txfid_idx(local); + if (idx < 0) + goto fail; + + if (local->frame_dump & PRISM2_DUMP_TX_HDR) + hostap_dump_tx_header(dev->name, &txdesc); + + spin_lock(&local->baplock); + res = hfa384x_setup_bap(dev, BAP0, local->txfid[idx], 0); + +#if defined(PRISM2_PCI) && defined(PRISM2_BUS_MASTER) + if (!res && skb->len >= local->bus_master_threshold_tx) { + u8 *pos; + int buf_len; + + local->bus_m0_tx_idx = idx; + + /* FIX: BAP0 should be locked during bus master transfer, but + * baplock with BH's disabled is not OK for this; netif queue + * stopping is not enough since BAP0 is used also for RID + * read/write */ + + /* stop the queue for the time that bus mastering on BAP0 is + * in use */ + hostap_netif_stop_queues(dev); + + spin_unlock(&local->baplock); + + /* Copy frame data to bus_m0_buf */ + pos = local->bus_m0_buf; + memcpy(pos, &txdesc, sizeof(txdesc)); + pos += sizeof(txdesc); + + if (!wepbuf) { + if (encaps_len > 0) { + memcpy(pos, encaps_data, encaps_len); + pos += encaps_len; + } + memcpy(pos, skb->data + skip_header_bytes, + skb->len - skip_header_bytes); + pos += skb->len - skip_header_bytes; + } + if (!wepbuf && use_wds == WDS_OWN_FRAME) { + /* add addr4 (SA) to bogus frame format if WDS is used + */ + memcpy(pos, skb->data + ETH_ALEN, ETH_ALEN); + pos += ETH_ALEN; + } + + if (wepbuf) { + memcpy(pos, wepbuf, wepbuf_len); + pos += wepbuf_len; + } + + buf_len = pos - local->bus_m0_buf; + if (buf_len & 1) + buf_len++; + +#ifdef PRISM2_ENABLE_BEFORE_TX_BUS_MASTER + /* Any RX packet seems to break something with TX bus + * mastering; enable command is enough to fix this.. */ + if (hfa384x_cmd_callback(dev, HFA384X_CMDCODE_ENABLE, 0, NULL, + prism2_tx_cb, (void *) buf_len)) { + printk(KERN_DEBUG "%s: TX: enable port0 failed\n", + dev->name); + } +#else /* PRISM2_ENABLE_BEFORE_TX_BUS_MASTER */ + prism2_tx_cb(dev, (void *) buf_len, 0, 0); +#endif /* PRISM2_ENABLE_BEFORE_TX_BUS_MASTER */ + + /* Bus master transfer will be started from command completion + * event handler and TX handling will be finished by calling + * prism2_transmit() from bus master event handler */ + goto tx_stats; + } +#endif /* PRISM2_PCI and PRISM2_BUS_MASTER */ + + if (!res) + res = hfa384x_to_bap(dev, BAP0, &txdesc, sizeof(txdesc)); + if (!res && !wepbuf && encaps_len > 0) + res = hfa384x_to_bap(dev, BAP0, encaps_data, encaps_len); + if (!res && !wepbuf && use_wds != WDS_OWN_FRAME) + res = hfa384x_to_bap(dev, BAP0, skb->data + skip_header_bytes, + skb->len - skip_header_bytes); + else if (!res && !wepbuf && use_wds == WDS_OWN_FRAME) { + int wlen, is_odd; + + wlen = skb->len - skip_header_bytes; + is_odd = wlen & 1; + + if (is_odd) + wlen--; /* need to avoid using odd offset */ + + res = hfa384x_to_bap(dev, BAP0, skb->data + skip_header_bytes, + wlen); + + /* add addr4 (SA) to bogus frame format if WDS is used */ + if (!res && is_odd) { + char tmpbuf[ETH_ALEN + 1]; + tmpbuf[0] = *(skb->data + skb->len - 1); + memcpy(tmpbuf + 1, skb->data + ETH_ALEN, ETH_ALEN); + res = hfa384x_to_bap(dev, BAP0, tmpbuf, ETH_ALEN + 1); + } else if (!res) { + res = hfa384x_to_bap(dev, BAP0, skb->data + ETH_ALEN, + ETH_ALEN); + } + } + + if (!res && wepbuf) + res = hfa384x_to_bap(dev, BAP0, wepbuf, wepbuf_len); + spin_unlock(&local->baplock); + + if (!res) + res = prism2_transmit(dev, idx); + if (res) { + printk(KERN_DEBUG "%s: prism2_tx - to BAP0 failed\n", + dev->name); + local->intransmitfid[idx] = PRISM2_TXFID_EMPTY; + PRISM2_SCHEDULE_TASK(&local->reset_queue); + ret = 0; /* do not retry failed frames to avoid problems */ + goto fail; + } + + +#if defined(PRISM2_PCI) && defined(PRISM2_BUS_MASTER) + tx_stats: +#endif + stats->tx_packets++; + stats->tx_bytes += data_len + 36; + + ret = 0; + fail: + if (wepbuf) + kfree(wepbuf); + + if (!ret) { + dev_kfree_skb(skb); + } + + tx_exit: + if (sta) + hostap_handle_sta_release(sta); + + prism2_callback(local, PRISM2_CALLBACK_TX_END); + return; /* ret; */ +} +static int prism2_tx_set_power(struct sk_buff *sk, struct net_device *dev) +{ + local_info_t *local = (local_info_t *) dev->priv; + u16 txp = WIFI_TX_POWER_ANNO(sk); + int ret = 1; + + if ((local->func->card_present && !local->func->card_present(local)) || + !local->hw_ready) { + printk(KERN_DEBUG "%s: prism2_tx_set_power: hw not ready - " + "skipping\n", dev->name); + ret = 0; + local->apdevstats.tx_dropped++; + goto fail; + } + + if (sk->len < 24) { + printk(KERN_DEBUG "%s: prism2_tx_set_power: short skb (len=%d)\n", + dev->name, sk->len); + ret = 0; + local->apdevstats.tx_dropped++; + goto fail; + } + + if (!WIFI_FROM_CLICK(sk) || txp == 0 || txp == local->txpower) { + /* + * we don't have to switch the power level, so go + * ahead and tx the packet + */ + prism2_tx(dev, (void *)sk, 0, 0); + ret = 0; + } else { + /* switch the power level and register a cb for the transmit fn */ + /* txpwr */ + local->txpower = txp; + if (hfa384x_cmd_callback(dev, HFA384X_CMDCODE_WRITEMIF, + HFA386X_CR_MANUAL_TX_POWER, &txp, + prism2_tx, (void *) sk)) { + printk(KERN_DEBUG "%s: prism2_tx_set_power: register cmd cb failed\n", + dev->name); + ret = 0; + local->apdevstats.tx_dropped++; + goto fail; + } else { + ret = 0; + } + } + + return ret; + + fail: + if (!ret) + dev_kfree_skb(sk); + + return ret; + +} + + + +/* Called only from software IRQ */ +static int prism2_tx_80211(struct sk_buff *skb, struct net_device *dev) +{ + local_info_t *local = (local_info_t *) dev->priv; + struct hfa384x_tx_frame txdesc; + int hdr_len, data_len, ret = 1, idx, res; + u16 fc, tx_control; + + + if ((local->func->card_present && !local->func->card_present(local)) || + !local->hw_ready) { + printk(KERN_DEBUG "%s: prism2_tx_80211: hw not ready - " + "skipping\n", dev->name); + ret = 0; + local->apdevstats.tx_dropped++; + goto fail; + } + + if (skb->len < 24) { + printk(KERN_DEBUG "%s: prism2_tx_80211: short skb (len=%d)\n", + dev->name, skb->len); + ret = 0; + local->apdevstats.tx_dropped++; + goto fail; + } + + memset(&txdesc, 0, sizeof(txdesc)); + /* txdesc.tx_rate might need to be set if f/w does not select suitable + * TX rate */ + + /* skb->data starts with txdesc->frame_control */ + hdr_len = 24; + memcpy(&txdesc.frame_control, skb->data, hdr_len); + fc = le16_to_cpu(txdesc.frame_control); + if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_DATA && + (fc & WLAN_FC_FROMDS) && (fc & WLAN_FC_TODS) && skb->len >= 30) { + /* Addr4 */ + memcpy(txdesc.addr4, skb->data + hdr_len, ETH_ALEN); + hdr_len += ETH_ALEN; + } + + tx_control = local->tx_control; + + /* 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) { + tx_control |= HFA384X_TX_CTRL_TX_OK; + txdesc.sw_support = cpu_to_le16(local->ap->tx_callback_idx); + + /* remove special version from the frame header */ + fc &= ~WLAN_FC_PVER; + txdesc.frame_control = cpu_to_le16(fc); + } + txdesc.tx_control = cpu_to_le16(tx_control); + + data_len = skb->len - hdr_len; + txdesc.data_len = __cpu_to_le16(data_len); + txdesc.len = __cpu_to_be16(data_len); + + idx = prism2_get_txfid_idx(local); + if (idx < 0) { + local->apdevstats.tx_dropped++; + goto fail; + } + + spin_lock(&local->baplock); + res = hfa384x_setup_bap(dev, BAP0, local->txfid[idx], 0); + if (!res) + res = hfa384x_to_bap(dev, BAP0, &txdesc, sizeof(txdesc)); + if (!res) + res = hfa384x_to_bap(dev, BAP0, skb->data + hdr_len, + skb->len - hdr_len); + spin_unlock(&local->baplock); + + if (!res) + res = prism2_transmit(dev, idx); + if (res) { + printk(KERN_DEBUG "%s: prism2_tx_80211 - to BAP0 failed\n", + dev->name); + local->intransmitfid[idx] = PRISM2_TXFID_EMPTY; + PRISM2_SCHEDULE_TASK(&local->reset_queue); + local->apdevstats.tx_dropped++; + ret = 0; + goto fail; + } + + ret = 0; + + local->apdevstats.tx_packets++; + local->apdevstats.tx_bytes += skb->len; + + fail: + if (!ret) + dev_kfree_skb(skb); + + return ret; +} + + +/* Send RX frame to netif with 802.11 (and possible prism) header. + * Called from hardware or software IRQ context. */ +static int prism2_rx_80211(struct net_device *dev, struct sk_buff *skb, + int type, char *extra, int extra_len) +{ + local_info_t *local = (local_info_t *) dev->priv; + int hdrlen, phdrlen, head_need, tail_need; + u16 len, fc; + int prism_header; + struct hfa384x_rx_frame rxdesc; + + dev->last_rx = jiffies; + + if (skb->len < sizeof(rxdesc)) { + printk(KERN_DEBUG "%s: prism2_rx_80211() called with too " + "short skb (len=%d)\n", dev->name, skb->len); + dev_kfree_skb_any(skb); + return 0; + } + + /* make a local copy of rxdesc, since we will be changing the head of + * the skb data */ + memcpy(&rxdesc, skb->data, sizeof(rxdesc)); + skb_pull(skb, sizeof(rxdesc)); + + 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; + } + + fc = le16_to_cpu(rxdesc.frame_control); + + 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); + + if (extra) { + /* set 802.11 protocol version to 3 to indicate extra data + * after the payload */ + fc |= WLAN_FC_PVER; + rxdesc.frame_control = cpu_to_le16(fc); + } + + len = le16_to_cpu(rxdesc.data_len); + /* Monitor mode reports different data_len values for packets. For + * example ctrl::ack frames may get values like -14 to 92 when heard + * from neighboring channel. Start of the frames seems to be OK anyway, + * so pass them through. Set signed negative values to zero so that + * hdrlen is used to get the proper length of data. */ + if (type == PRISM2_RX_MONITOR && len >= (u16) -14 && len != 0xffff) + len = 0; + if (len > PRISM2_DATA_MAXLEN || extra_len > 65535) { + printk(KERN_WARNING "%s: prism2_rx_80211: len(%d) > " + "MAX(%d)\n", dev->name, len, PRISM2_DATA_MAXLEN); + dev_kfree_skb_any(skb); + return 0; + } + + /* check if there is enough room for extra data; if not, expand skb + * buffer to be large enough for the changes */ + head_need = phdrlen + hdrlen; + tail_need = extra_len; + if (extra_len) + tail_need += 2; +#ifdef PRISM2_ADD_BOGUS_CRC + tail_need += 4; +#endif /* PRISM2_ADD_BOGUS_CRC */ + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)) + if (head_need > skb_headroom(skb) || tail_need > skb_tailroom(skb)) { + struct sk_buff *nskb; + + nskb = dev_alloc_skb(skb->len + head_need + tail_need); + if (nskb == NULL) { + dev_kfree_skb_any(skb); + return 0; + } + + skb_reserve(nskb, head_need); + memcpy(skb_put(nskb, skb->len), skb->data, skb->len); + dev_kfree_skb_any(skb); + skb = nskb; + } +#else /* Linux 2.4.0 or newer */ + 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; + } + } +#endif + + /* We now have an skb with enough head and tail room, so just insert + * the extra data */ + memcpy(skb_push(skb, hdrlen), &rxdesc.frame_control, hdrlen); + + 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, 0, le32_to_cpu(rxdesc.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, rxdesc.signal); + LWNG_SETVAL(noise, 7, 0, 4, rxdesc.silence); + LWNG_SETVAL(rate, 8, 0, 4, rxdesc.rate / 5); + LWNG_SETVAL(istx, 9, 0, 4, 0); +#ifdef PRISM2_ADD_BOGUS_CRC + LWNG_SETVAL(frmlen, 10, 0, 4, hdrlen + len + 4 /* CRC */); +#else /* PRISM2_ADD_BOGUS_CRC */ + LWNG_SETVAL(frmlen, 10, 0, 4, hdrlen + len); +#endif /* PRISM2_ADD_BOGUS_CRC */ +#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(rxdesc.time); + hdr->hosttime = __cpu_to_be64(jiffies); + hdr->phytype = htonl(4); /* dss_dot11_b */ + hdr->channel = htonl(local->channel); + hdr->datarate = htonl(rxdesc.rate); + hdr->antenna = htonl(0); /* unknown */ + hdr->priority = htonl(0); /* unknown */ + hdr->ssi_type = htonl(3); /* raw */ + hdr->ssi_signal = htonl(rxdesc.signal); + hdr->ssi_noise = htonl(rxdesc.silence); + hdr->preamble = htonl(0); /* unknown */ + hdr->encoding = htonl(1); /* cck */ + } + +#ifdef PRISM2_ADD_BOGUS_CRC + memset(skb_put(skb, 4), 0xff, 4); /* Prism2 strips CRC */ +#endif /* PRISM2_ADD_BOGUS_CRC */ + + /* TODO: could add 'type' information into the end of the data if it + * is needed in the user space daemon */ + if (extra != NULL) { + u16 *elen; + memcpy(skb_put(skb, extra_len), extra, extra_len); + elen = (u16 *) skb_put(skb, 2); + *elen = __cpu_to_le16(extra_len); + } + + 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 (hdrlen + len); +} + + +/* Called only as a tasklet (software IRQ) */ +static void monitor_rx(struct net_device *dev, struct sk_buff *skb) +{ + local_info_t *local = (local_info_t *) dev->priv; + struct net_device_stats *stats; + int len; + struct hfa384x_rx_frame *rxdesc = + (struct hfa384x_rx_frame *) skb->data; + + if (le16_to_cpu(rxdesc->status) & HFA384X_RX_STATUS_FCSERR && + !local->monitor_allow_fcserr) { + dev_kfree_skb(skb); + return; + } + + len = prism2_rx_80211(dev, skb, PRISM2_RX_MONITOR, NULL, 0); + 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 && + entry->first_frag_time + 2 * HZ < jiffies) { + 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 hfa384x_rx_frame *rxdesc) +{ + struct sk_buff *skb = NULL; + u16 sc; + unsigned int frag, seq; + struct prism2_frag_entry *entry; + + sc = le16_to_cpu(rxdesc->seq_ctrl); + frag = WLAN_GET_SEQ_FRAG(sc); + seq = WLAN_GET_SEQ_SEQ(sc); + + if (frag == 0) { + /* Reserve enough space to fit maximum frame length */ + skb = dev_alloc_skb(local->dev->mtu + + sizeof(struct hfa384x_rx_frame) + + 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, rxdesc->addr2, ETH_ALEN); + memcpy(entry->dst_addr, rxdesc->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, rxdesc->addr2, + rxdesc->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 hfa384x_rx_frame *rxdesc) +{ + u16 sc; + unsigned int seq; + struct prism2_frag_entry *entry; + + sc = le16_to_cpu(rxdesc->seq_ctrl); + seq = WLAN_GET_SEQ_SEQ(sc); + + entry = prism2_frag_cache_find(local, seq, -1, rxdesc->addr2, + rxdesc->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; +} + + +/* Called only as a tasklet (software IRQ) */ +static inline prism2_wds_info_t *prism2_rx_get_wds(local_info_t *local, + u8 *addr) +{ + prism2_wds_info_t *wds; + + spin_lock(&local->wdslock); + wds = local->wds; + while (wds != NULL && memcmp(wds->remote_addr, addr, ETH_ALEN) != 0) + wds = wds->next; + spin_unlock(&local->wdslock); + + return wds; +} + + +/* Called only as a tasklet (software IRQ) */ +static void prism2_rx(local_info_t *local) +{ + struct net_device *dev = local->dev; + int res, rx_pending = 0; + u16 len, rxfid, status, macport; + struct net_device_stats *stats; + struct hfa384x_rx_frame rxdesc; + struct sk_buff *skb = NULL; + + prism2_callback(local, PRISM2_CALLBACK_RX_START); + stats = hostap_get_stats(dev); + + rxfid = HFA384X_INW(HFA384X_RXFID_OFF); +#ifndef final_version + if (rxfid == 0) { + rxfid = HFA384X_INW(HFA384X_RXFID_OFF); + printk(KERN_DEBUG "prism2_rx: rxfid=0 (next 0x%04x)\n", + rxfid); + PRISM2_SCHEDULE_TASK(&local->reset_queue); + goto rx_dropped; + } +#endif + + spin_lock(&local->baplock); + res = hfa384x_setup_bap(dev, BAP0, rxfid, 0); + if (!res) + res = hfa384x_from_bap(dev, BAP0, &rxdesc, sizeof(rxdesc)); + + if (res) { + spin_unlock(&local->baplock); + printk(KERN_DEBUG "%s: copy from BAP0 failed %d\n", dev->name, + res); + if (res == -ETIMEDOUT) { + PRISM2_SCHEDULE_TASK(&local->reset_queue); + } + goto rx_dropped; + } + + len = le16_to_cpu(rxdesc.data_len); + status = le16_to_cpu(rxdesc.status); + macport = (status >> 8) & 0x07; + + /* Drop frames with too large reported payload length. Monitor mode + * seems to sometimes pass frames (e.g., ctrl::ack) with signed and + * negative value, so allow also values 65522 .. 65534 (-14 .. -2) for + * macport 7 */ + if ((len & 0x8000) && + (macport != 7 || ((len < (u16) -14) && len != 0xffff))) { + spin_unlock(&local->baplock); + printk(KERN_DEBUG "%s: Received frame with invalid length " + "0x%04x\n", dev->name, len); + hostap_dump_rx_header(dev->name, &rxdesc); + goto rx_dropped; + } + + skb = dev_alloc_skb(sizeof(rxdesc) + len); + if (!skb) { + spin_unlock(&local->baplock); + printk(KERN_DEBUG "%s: RX failed to allocate skb\n", + dev->name); + goto rx_dropped; + } + skb->dev = dev; + memcpy(skb_put(skb, sizeof(rxdesc)), &rxdesc, sizeof(rxdesc)); + +#if defined(PRISM2_PCI) && defined(PRISM2_BUS_MASTER) + if (len >= local->bus_master_threshold_rx) { + unsigned long addr; + + hfa384x_events_no_bap1(dev); + + local->rx_skb = skb; + /* Internal BAP0 offset points to the byte following rxdesc; + * copy rest of the data using bus master */ + addr = virt_to_phys(skb_put(skb, len)); + HFA384X_OUTW((addr & 0xffff0000) >> 16, + HFA384X_PCI_M0_ADDRH_OFF); + HFA384X_OUTW(addr & 0x0000ffff, HFA384X_PCI_M0_ADDRL_OFF); + if (len & 1) + len++; + HFA384X_OUTW(len / 2, HFA384X_PCI_M0_LEN_OFF); + HFA384X_OUTW(HFA384X_PCI_CTL_FROM_BAP, HFA384X_PCI_M0_CTL_OFF); + + /* pci_bus_m1 event will be generated when data transfer is + * complete and the frame will then be added to rx_list and + * rx_tasklet is scheduled */ + rx_pending = 1; + + /* Have to release baplock before returning, although BAP0 + * should really not be used before DMA transfer has been + * completed. */ + spin_unlock(&local->baplock); + } else +#endif /* PRISM2_PCI and PRISM2_BUS_MASTER */ + { + res = hfa384x_from_bap(dev, BAP0, skb_put(skb, len), len); + spin_unlock(&local->baplock); + if (res) { + printk(KERN_DEBUG "%s: RX failed to read " + "frame data\n", dev->name); + goto rx_dropped; + } + + skb_queue_tail(&local->rx_list, skb); + HOSTAP_TASKLET_SCHEDULE(&local->rx_tasklet); + } + + rx_exit: + prism2_callback(local, PRISM2_CALLBACK_RX_END); + if (!rx_pending) { + HFA384X_OUTW(HFA384X_EV_RX, HFA384X_EVACK_OFF); + } + + return; + + rx_dropped: + stats->rx_dropped++; + if (skb) + dev_kfree_skb(skb); + goto rx_exit; +} + + +static inline int +hostap_rx_frame_invalid(struct net_device *dev, u16 macport, u16 status, + u16 len, struct sk_buff *skb) +{ + if (macport != 0) { + printk(KERN_DEBUG "%s: RX: unknown MACPort %d\n", + dev->name, macport); + return -1; + } + + /* FCS errors should not come this far, but let's make sure that frames + * with errors will be dropped even in Host AP mode */ + if (status & HFA384X_RX_STATUS_FCSERR) { + + { + struct sk_buff *nskb = skb_clone(skb, GFP_ATOMIC); + click_wifi_rx_fcserr(nskb); + } + + printk(KERN_DEBUG "%s: RX: dropped FCSErr frame " + "(status=%02X)\n", dev->name, status); + return -1; + } + + if (len > PRISM2_DATA_MAXLEN) { + printk(KERN_DEBUG "%s: RX: len(%d) > MAX(%d)\n", + dev->name, len, PRISM2_DATA_MAXLEN); + return -1; + } + + return 0; +} + + +static inline int +hostap_rx_frame_invalid_data(struct net_device *dev, u16 msg_type, u16 type, + u16 stype) +{ + if (msg_type != HFA384X_RX_MSGTYPE_NORMAL && + msg_type != HFA384X_RX_MSGTYPE_RFC1042 && + msg_type != HFA384X_RX_MSGTYPE_BRIDGETUNNEL) { + printk(KERN_DEBUG "%s: RX: dropped frame " + "(msg_type=%d)\n", dev->name, msg_type); + return -1; + } + + if (type != WLAN_FC_TYPE_DATA) { + printk(KERN_DEBUG "%s: RX: dropped non-data frame " + "(type=0x%02x, subtype=0x%02x)\n", + dev->name, type, stype); + return -1; + } + + + return 0; +} + + +static inline int +hostap_rx_frame_mgmt(local_info_t *local, struct sk_buff *skb, u16 type, + u16 stype) +{ + if (local->iw_mode == IW_MODE_MASTER) { + struct hfa384x_rx_frame *rxdesc; + rxdesc = (struct hfa384x_rx_frame *) skb->data; + hostap_update_sta_ps(local, (struct hostap_ieee80211_hdr *) + &rxdesc->frame_control); + } + + if (local->hostapd && type == WLAN_FC_TYPE_MGMT) { + /* send management frames to the user space daemon for + * processing */ + local->apdevstats.rx_packets++; + local->apdevstats.rx_bytes += skb->len; + prism2_rx_80211(local->apdev, skb, PRISM2_RX_MGMT, NULL, 0); + return 0; + } + + if (local->iw_mode == IW_MODE_MASTER) { + if (type != WLAN_FC_TYPE_MGMT && type != WLAN_FC_TYPE_CTRL) { + printk(KERN_DEBUG "%s: unknown management frame " + "(type=0x%02x, stype=0x%02x) dropped\n", + skb->dev->name, type, stype); + return -1; + } + + skb->protocol = __constant_htons(ETH_P_HOSTAP); + hostap_rx(skb->dev, skb); + return 0; + } else { + printk(KERN_DEBUG "%s: prism2_rx: management frame " + "received in non-Host AP mode\n", skb->dev->name); + return -1; + } +} + + +static inline int +hostap_rx_frame_wds(local_info_t *local, struct hfa384x_rx_frame *rxdesc, + u16 fc, prism2_wds_info_t **wds) +{ + if ((fc & (WLAN_FC_TODS | WLAN_FC_FROMDS)) != + (WLAN_FC_TODS | WLAN_FC_FROMDS) && + (!local->wds || 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(rxdesc->addr1, local->dev->dev_addr, ETH_ALEN) != 0 && + (rxdesc->addr1[0] != 0xff || rxdesc->addr1[1] != 0xff || + rxdesc->addr1[2] != 0xff || rxdesc->addr1[3] != 0xff || + rxdesc->addr1[4] != 0xff || rxdesc->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(rxdesc->addr1)); + return -1; + } + + /* check if the frame came from a registered WDS connection */ + *wds = prism2_rx_get_wds(local, rxdesc->addr2); + if (*wds == NULL && fc & WLAN_FC_FROMDS && + (local->iw_mode != IW_MODE_INFRA || + !(local->wds_type & HOSTAP_WDS_AP_CLIENT) || + memcmp(rxdesc->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(rxdesc->addr2)); + if (local->ap && local->ap->autom_ap_wds) + hostap_add_wds_link(local, rxdesc->addr2); + return -1; + } + + if (*wds && !(fc & WLAN_FC_FROMDS) && local->ap && + hostap_is_sta_assoc(local->ap, rxdesc->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 hfa384x_rx_frame *rxdesc, u8 *buf, + int len) +{ + struct net_device *dev = local->dev; + u16 fc, ethertype; + + fc = le16_to_cpu(rxdesc->frame_control); + + /* check that the frame is unicast frame to us */ + if ((fc & (WLAN_FC_TODS | WLAN_FC_FROMDS)) == WLAN_FC_TODS && + memcmp(rxdesc->addr1, dev->dev_addr, ETH_ALEN) == 0 && + memcmp(rxdesc->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(rxdesc->addr1, dev->dev_addr, ETH_ALEN) == 0) { + /* FromDS frame with own addr as DA */ + } else + return 0; + + if (len < 8) + return 0; + + /* check for port access entity Ethernet type */ + ethertype = (buf[6] << 8) | buf[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, int iswep, struct sk_buff *skb) +{ + struct hfa384x_rx_frame *rxdesc; + struct prism2_crypt_data *crypt; + void *sta = NULL; + int ret = 0, olen, len; + char *payload; + + rxdesc = (struct hfa384x_rx_frame *) skb->data; + len = skb->len - sizeof(*rxdesc); + payload = (char *) (rxdesc + 1); + crypt = local->crypt; + 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 (!(rxdesc->addr1[0] & 0x01) || local->bcrx_sta_key) + (void) hostap_handle_sta_crypto(local, rxdesc, &crypt, &sta); + + /* allow NULL decrypt to indicate an station specific override for + * default encryption */ + if (crypt && (crypt->ops == NULL || crypt->ops->decrypt == NULL)) + crypt = NULL; + + if (!crypt && iswep) { + printk(KERN_DEBUG "%s: WEP decryption failed (not set) (SA=" + MACSTR ")\n", local->dev->name, MAC2STR(rxdesc->addr2)); + local->comm_tallies.rx_discards_wep_undecryptable++; + ret = -1; + goto done; + } + + if (!crypt) + goto done; + + if (!iswep && !local->open_wep) { + if (local->ieee_802_1x && + hostap_is_eapol_frame(local, rxdesc, payload, len)) { + /* pass unencrypted EAPOL frames even if encryption is + * configured */ + printk(KERN_DEBUG "%s: RX: IEEE 802.1X - passing " + "unencrypted EAPOL frame\n", local->dev->name); + goto done; + } + printk(KERN_DEBUG "%s: encryption configured, but RX frame " + "not encrypted (SA=" MACSTR ")\n", + local->dev->name, MAC2STR(rxdesc->addr2)); + ret = -1; + goto done; + } + + /* decrypt WEP part of the frame: IV (4 bytes), encrypted + * payload (including SNAP header), ICV (4 bytes) */ + atomic_inc(&crypt->refcnt); + olen = crypt->ops->decrypt(payload, len, crypt->priv); + atomic_dec(&crypt->refcnt); + if (olen < 0) { + printk(KERN_DEBUG "%s: WEP decryption failed (SA=" MACSTR + ")\n", local->dev->name, MAC2STR(rxdesc->addr2)); + local->comm_tallies.rx_discards_wep_undecryptable++; + ret = -1; + goto done; + } + + skb_trim(skb, skb->len - (len - olen)); + + done: + if (sta) + hostap_handle_sta_release(sta); + + return ret; +} + + +/* Called only as a tasklet (software IRQ) */ +static void hostap_rx_skb(local_info_t *local, struct sk_buff *skb) +{ + u16 status, macport, msg_type, fc, type, stype, sc; + prism2_wds_info_t *wds = NULL; + struct hfa384x_rx_frame *rxdesc; + struct net_device *dev = skb->dev; + 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; + + stats = hostap_get_stats(dev); + rxdesc = (struct hfa384x_rx_frame *) skb->data; + status = le16_to_cpu(rxdesc->status); + macport = HFA384X_RX_STATUS_GET_MACPORT(status); + msg_type = HFA384X_RX_STATUS_GET_MSGTYPE(status); + fc = le16_to_cpu(rxdesc->frame_control); + type = WLAN_FC_GET_TYPE(fc); + stype = WLAN_FC_GET_STYPE(fc); + sc = le16_to_cpu(rxdesc->seq_ctrl); + frag = WLAN_GET_SEQ_FRAG(sc); + + if (local->frame_dump & PRISM2_DUMP_RX_HDR) + hostap_dump_rx_header(dev->name, rxdesc); + +#if WIRELESS_EXT > 15 + /* 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 (local->spy_data.spy_number > 0) { + struct iw_quality wstats; + wstats.level = HFA384X_RSSI_LEVEL_TO_dBm(rxdesc->signal); + wstats.noise = HFA384X_RSSI_LEVEL_TO_dBm(rxdesc->silence); + wstats.updated = 6; /* No qual value */ + /* Update spy records */ + wireless_spy_update(dev, rxdesc->addr2, &wstats); + } +#endif /* IW_WIRELESS_SPY */ +#endif /* WIRELESS_EXT > 15 */ + + if (macport == 7) { + monitor_rx(dev, skb); + return; + } + + if (hostap_rx_frame_invalid(dev, macport, status, skb->len - + sizeof(struct hfa384x_rx_frame), + skb)) + goto rx_dropped; + + if (msg_type == HFA384X_RX_MSGTYPE_MGMT || type != WLAN_FC_TYPE_DATA) { + if (type == WLAN_FC_TYPE_MGMT && stype == WLAN_FC_STYPE_AUTH && + fc & WLAN_FC_ISWEP && local->host_decrypt && + hostap_rx_frame_decrypt(local, fc & WLAN_FC_ISWEP, skb)) { + printk(KERN_DEBUG "%s: failed to decrypt mgmt::auth " + "from " MACSTR "\n", dev->name, + MAC2STR(rxdesc->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, type, stype)) + goto rx_dropped; + else + goto rx_exit; + } + + if (hostap_rx_frame_invalid_data(dev, msg_type, type, stype)) + goto rx_dropped; + + if (hostap_rx_frame_wds(local, rxdesc, fc, &wds)) + goto rx_dropped; + if (wds) { + skb->dev = dev = &wds->dev; + 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(rxdesc->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 && !from_assoc_ap) { + switch (hostap_handle_sta_rx(local, dev, skb, 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; + } + } else if (local->iw_mode == IW_MODE_REPEAT || + local->wds_type & HOSTAP_WDS_AP_CLIENT) + hostap_update_rx_stats(local->ap, rxdesc); + + /* Nullfunc frames may have PS-bit set, so they must be passed to + * hostap_handle_sta_rx() before being dropped here. */ + if (stype != WLAN_FC_STYPE_DATA && + stype != WLAN_FC_STYPE_DATA_CFACK && + stype != WLAN_FC_STYPE_DATA_CFPOLL && + stype != WLAN_FC_STYPE_DATA_CFACKPOLL) { + if (stype != WLAN_FC_STYPE_NULLFUNC) + printk(KERN_DEBUG "%s: RX: dropped data frame " + "with no data (type=0x%02x, subtype=0x%02x)\n", + dev->name, type, stype); + goto rx_dropped; + } + + /* skb: rxdesc + (possibly fragmented, possibly encrypted) payload */ + + if (local->host_decrypt && + hostap_rx_frame_decrypt(local, fc & WLAN_FC_ISWEP, skb)) + goto rx_dropped; + + /* skb: rxdesc + (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, rxdesc); + 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), frag); + goto rx_dropped; + } + + flen = skb->len; + if (frag != 0) + flen -= sizeof(struct hfa384x_rx_frame); + + 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, rxdesc); + 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 + sizeof(struct hfa384x_rx_frame), + 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; + rxdesc = (struct hfa384x_rx_frame *) skb->data; + prism2_frag_cache_invalidate(local, rxdesc); + } + + /* skb: rxdesc + (possible reassembled) full plaintext payload */ + + payload = skb->data + sizeof(*rxdesc); + 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) { + printk(KERN_DEBUG "%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, + PRISM2_RX_MGMT, NULL, 0); + local->apdevstats.rx_packets++; + local->apdevstats.rx_bytes += skb->len; + goto rx_exit; + } + } else if (!frame_authorized && !wds) { + printk(KERN_DEBUG "%s: dropped frame from " + "unauthorized port (IEEE 802.1X): " + "ethertype=0x%04x\n", + dev->name, ethertype); + hostap_dump_rx_header(dev->name, rxdesc); + goto rx_dropped; + } + } + + /* convert rxdesc + possible LLC headers into Ethernet header */ + if (skb->len - sizeof(struct hfa384x_rx_frame) >= 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, sizeof(*rxdesc) + 6); + memmove(skb_push(skb, 2 * ETH_ALEN), rxdesc->dst_addr, + 2 * ETH_ALEN); + } else { + /* Leave Ethernet header part of rxdesc and full payload */ + skb_pull(skb, sizeof(*rxdesc) - ETH_HLEN); + } + + if (wds || ((fc & (WLAN_FC_TODS | WLAN_FC_FROMDS)) == + (WLAN_FC_TODS | WLAN_FC_FROMDS))) { + /* Get original source address (Addr4 = SA) */ + if (fc & WLAN_FC_FROMDS) { + /* IEEE 802.11 compliant WDS frame */ + memcpy(skb->data + ETH_ALEN, rxdesc->addr4, ETH_ALEN); + } else if (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); + } + } + + int rate = 0; + switch (rxdesc->rate) { + case 0x0A: + rate = 1; + break; + case 0x014: + rate = 2; + break; + case 0x37: + rate = 5; + break; + case 0x6E: + rate = 11; + break; + default: + rate = rxdesc->rate; + } + + + memset(skb->cb, 0, sizeof(skb->cb)); + SET_WIFI_RATE_ANNO(skb, rate); + SET_WIFI_SIGNAL_ANNO(skb, rxdesc->signal); + SET_WIFI_NOISE_ANNO(skb, rxdesc->silence); + + + + stats->rx_packets++; + stats->rx_bytes += skb->len; + + if (local->iw_mode == IW_MODE_MASTER && !wds && + local->ap->bridge_packets) { + if (rxdesc->addr3[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_assoc(local->ap, rxdesc->addr3)) { + /* 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; */ + dev_queue_xmit(skb2); + } + + if (skb) { + skb->protocol = eth_type_trans(skb, dev); + /* don't clear the cb - we need click packet annos */ + /* jbicket */ + /* memset(skb->cb, 0, sizeof(skb->cb)); */ + skb->dev = dev; + netif_rx(skb); + } + + rx_exit: + return; + + rx_dropped: + dev_kfree_skb(skb); + + stats->rx_dropped++; + goto rx_exit; +} + + +/* Called only as a tasklet (software IRQ) */ +static void hostap_rx_tasklet(unsigned long data) +{ + local_info_t *local = (local_info_t *) data; + struct sk_buff *skb; + + while ((skb = skb_dequeue(&local->rx_list)) != NULL) + hostap_rx_skb(local, skb); +} + + +/* Called only from hardware IRQ */ +static void prism2_alloc_ev(struct net_device *dev) +{ + local_info_t *local = (local_info_t *) dev->priv; + int idx; + u16 fid; + + fid = HFA384X_INW(HFA384X_ALLOCFID_OFF); + + PDEBUG(DEBUG_FID, "FID: interrupt: ALLOC - fid=0x%04x\n", fid); + + idx = local->next_alloc; + + do { + if (local->txfid[idx] == fid) { + PDEBUG(DEBUG_FID, "FID: found matching txfid[%d]\n", + idx); + +#ifndef final_version + if (local->intransmitfid[idx] == PRISM2_TXFID_EMPTY) + printk("Already released txfid found at idx " + "%d\n", idx); + if (local->intransmitfid[idx] == PRISM2_TXFID_RESERVED) + printk("Already reserved txfid found at idx " + "%d\n", idx); +#endif + local->intransmitfid[idx] = PRISM2_TXFID_EMPTY; + idx++; + local->next_alloc = idx >= PRISM2_TXFID_COUNT ? 0 : + idx; + + if (!test_bit(HOSTAP_BITS_TRANSMIT, &local->bits) && + netif_queue_stopped(dev)) + hostap_netif_wake_queues(dev); + + return; + } + + idx++; + if (idx >= PRISM2_TXFID_COUNT) + idx = 0; + } while (idx != local->next_alloc); + + printk(KERN_WARNING "%s: could not find matching txfid (0x%04x) for " + "alloc event\n", dev->name, HFA384X_INW(HFA384X_ALLOCFID_OFF)); +} + + +/* TX callback data stored in skb->cb; this must not grow over the maximum + * skb->cb size (48 bytes) */ +struct hostap_tx_callback_skb_cb { + struct hostap_tx_callback_info *cb; + int ok; +}; + + +/* Called only as a tasklet (software IRQ) */ +static void hostap_tx_callback_tasklet(unsigned long data) +{ + local_info_t *local = (local_info_t *) data; + struct sk_buff *skb; + struct hostap_tx_callback_skb_cb *cb_data; + + while ((skb = skb_dequeue(&local->tx_callback_list)) != NULL) { + cb_data = (struct hostap_tx_callback_skb_cb *) skb->cb; + cb_data->cb->func(skb, cb_data->ok, cb_data->cb->data); + } +} + + +/* Called only as a tasklet (software IRQ) */ +static void hostap_tx_callback(local_info_t *local, + struct hfa384x_tx_frame *txdesc, int ok, + char *payload) +{ + u16 sw_support, hdrlen, len; + struct sk_buff *skb; + struct hostap_tx_callback_info *cb; + struct hostap_tx_callback_skb_cb *cb_data; + + /* Make sure that frame was from us. */ + if (memcmp(txdesc->addr2, local->dev->dev_addr, ETH_ALEN)) { + printk(KERN_DEBUG "%s: TX callback - foreign frame\n", + local->dev->name); + return; + } + + sw_support = le16_to_cpu(txdesc->sw_support); + + spin_lock(&local->lock); + cb = local->tx_callback; + while (cb != NULL && cb->idx != sw_support) + cb = cb->next; + spin_unlock(&local->lock); + + if (cb == NULL) { + printk(KERN_DEBUG "%s: could not find TX callback (idx %d)\n", + local->dev->name, sw_support); + return; + } + + hdrlen = hostap_80211_get_hdrlen(le16_to_cpu(txdesc->frame_control)); + len = le16_to_cpu(txdesc->data_len); + skb = dev_alloc_skb(hdrlen + len); + if (skb == NULL) { + printk(KERN_DEBUG "%s: hostap_tx_callback failed to allocate " + "skb\n", local->dev->name); + return; + } + + memcpy(skb_put(skb, hdrlen), (void *) &txdesc->frame_control, hdrlen); + if (payload) + memcpy(skb_put(skb, len), payload, len); + + skb->dev = local->dev; + skb->mac.raw = skb->data; + + /* Store TX callback info pointer to private data in the skb and finish + * TX callback handling in tasklet so that hw IRQ can be ACKed now */ + cb_data = (struct hostap_tx_callback_skb_cb *) skb->cb; + cb_data->cb = cb; + cb_data->ok = ok; + skb_queue_tail(&local->tx_callback_list, skb); + HOSTAP_TASKLET_SCHEDULE(&local->tx_callback_tasklet); +} + + +/* Called only as a tasklet (software IRQ) */ +static int hostap_tx_compl_read(local_info_t *local, int error, + struct hfa384x_tx_frame *txdesc, + char **payload) +{ + u16 fid, len; + int res, ret = 0; + struct net_device *dev = local->dev; + + fid = HFA384X_INW(HFA384X_TXCOMPLFID_OFF); + + spin_lock(&local->baplock); + res = hfa384x_setup_bap(dev, BAP0, fid, 0); + if (!res) + res = hfa384x_from_bap(dev, BAP0, txdesc, sizeof(*txdesc)); + if (res) { + printk(KERN_WARNING "%s: TX (err=%d) - fid=0x%04x - could not " + "read txdesc\n", dev->name, error, fid); + if (res == -ETIMEDOUT) { + PRISM2_SCHEDULE_TASK(&local->reset_queue); + } + ret = -1; + goto fail; + } + + if (0 && txdesc->sw_support) { + len = le16_to_cpu(txdesc->data_len); + if (len < PRISM2_DATA_MAXLEN) { + *payload = (char *) kmalloc(len, GFP_ATOMIC); + if (*payload == NULL || + hfa384x_from_bap(dev, BAP0, *payload, len)) { + PDEBUG(DEBUG_EXTRA, "%s: could not read TX " + "frame payload\n", dev->name); + kfree(*payload); + *payload = NULL; + ret = -1; + goto fail; + } + } + } + + fail: + spin_unlock(&local->baplock); + + return ret; +} + +static void prism2_tx_click_cb(local_info_t *local, struct hfa384x_tx_frame *txdesc) +{ + if (txdesc->sw_support < PRISM2_TXSKB_COUNT && txdesc->sw_support != 0) { + struct sk_buff *skb = local->txskb[txdesc->sw_support]; + + local->txskb[txdesc->sw_support] = NULL; + + if (NULL != skb) { + u16 status = le16_to_cpu(txdesc->status); + int success = !status; + SET_WIFI_TX_SUCCESS_ANNO(skb, success); + /* + * don't set the rate anno, since it was already set in + * the skb. + * jbicket + */ + /* also don't set the retries anno, since hostap doesn't support it */ + click_wifi_tx_ev(skb); + + } else { + printk(KERN_WARNING "%s: prism2_txexc skb %d NULL\n", + local->dev->name, txdesc->sw_support); + } + } else { + printk(KERN_WARNING "%s: prism2_txexc sw_support = %d (NULL)\n", + local->dev->name, txdesc->sw_support); + } +} + +/* Called only as a tasklet (software IRQ) */ +static void prism2_tx_ev(local_info_t *local) +{ + struct net_device *dev = local->dev; + char *payload = NULL; + struct hfa384x_tx_frame txdesc; + + if (hostap_tx_compl_read(local, 0, &txdesc, &payload)) { + printk(KERN_WARNING "%s: hostap_tx_compl_read failed\n", + local->dev->name); + goto fail; + } + + prism2_tx_click_cb(local, &txdesc); + + if (local->frame_dump & PRISM2_DUMP_TX_HDR) { + PDEBUG(DEBUG_EXTRA, "%s: TX - status=0x%04x " + "retry_count=%d tx_rate=%d seq_ctrl=%d " + "duration_id=%d\n", + dev->name, le16_to_cpu(txdesc.status), + txdesc.retry_count, txdesc.tx_rate, + le16_to_cpu(txdesc.seq_ctrl), + le16_to_cpu(txdesc.duration_id)); + } + + if (0 && txdesc.sw_support) { + hostap_tx_callback(local, &txdesc, 1, payload); + } + kfree(payload); + + fail: + HFA384X_OUTW(HFA384X_EV_TX, HFA384X_EVACK_OFF); +} + + +/* Called only as a tasklet (software IRQ) */ +static void hostap_sta_tx_exc_tasklet(unsigned long data) +{ + local_info_t *local = (local_info_t *) data; + struct sk_buff *skb; + + while ((skb = skb_dequeue(&local->sta_tx_exc_list)) != NULL) { + struct hfa384x_tx_frame *txdesc = + (struct hfa384x_tx_frame *) skb->data; + hostap_handle_sta_tx_exc(local, txdesc); + dev_kfree_skb(skb); + } +} + + +/* Called only as a tasklet (software IRQ) */ +static void prism2_txexc(local_info_t *local) +{ + struct net_device *dev = local->dev; + u16 status, fc; + int show_dump, res; + char *payload = NULL; + struct hfa384x_tx_frame txdesc; + + show_dump = local->frame_dump & PRISM2_DUMP_TXEXC_HDR; + local->stats.tx_errors++; + + res = hostap_tx_compl_read(local, 1, &txdesc, &payload); + HFA384X_OUTW(HFA384X_EV_TXEXC, HFA384X_EVACK_OFF); + if (res) + return; + + status = le16_to_cpu(txdesc.status); + + prism2_tx_click_cb(local, &txdesc); + + +#if WIRELESS_EXT > 13 + /* We produce a TXDROP event only for retry or lifetime + * exceeded, because that's the only status that really mean + * that this particular node went away. + * Other errors means that *we* screwed up. - Jean II */ + if (status & (HFA384X_TX_STATUS_RETRYERR | HFA384X_TX_STATUS_AGEDERR)) + { + union iwreq_data wrqu; + + /* Copy 802.11 dest address. */ + memcpy(wrqu.addr.sa_data, txdesc.addr1, ETH_ALEN); + wrqu.addr.sa_family = ARPHRD_ETHER; + wireless_send_event(dev, IWEVTXDROP, &wrqu, NULL); + } else + show_dump = 1; +#endif /* WIRELESS_EXT > 13 */ + + if (local->iw_mode == IW_MODE_MASTER || + local->iw_mode == IW_MODE_REPEAT || + local->wds_type & HOSTAP_WDS_AP_CLIENT) { + struct sk_buff *skb; + skb = dev_alloc_skb(sizeof(txdesc)); + if (skb) { + memcpy(skb_put(skb, sizeof(txdesc)), &txdesc, + sizeof(txdesc)); + skb_queue_tail(&local->sta_tx_exc_list, skb); + HOSTAP_TASKLET_SCHEDULE(&local->sta_tx_exc_tasklet); + } + } + + if (0 && txdesc.sw_support) { + hostap_tx_callback(local, &txdesc, 0, payload); + } + + kfree(payload); + + if (!show_dump) + return; + + PDEBUG(DEBUG_EXTRA, "%s: TXEXC - status=0x%04x (%s%s%s%s)" + " tx_control=%04x\n", + dev->name, status, + status & HFA384X_TX_STATUS_RETRYERR ? "[RetryErr]" : "", + status & HFA384X_TX_STATUS_AGEDERR ? "[AgedErr]" : "", + status & HFA384X_TX_STATUS_DISCON ? "[Discon]" : "", + status & HFA384X_TX_STATUS_FORMERR ? "[FormErr]" : "", + le16_to_cpu(txdesc.tx_control)); + + fc = le16_to_cpu(txdesc.frame_control); + PDEBUG(DEBUG_EXTRA, " retry_count=%d tx_rate=%d fc=0x%04x " + "(%s%s%s::%d%s%s)\n", + txdesc.retry_count, txdesc.tx_rate, fc, + WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT ? "Mgmt" : "", + WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_CTRL ? "Ctrl" : "", + WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_DATA ? "Data" : "", + WLAN_FC_GET_STYPE(fc), + fc & WLAN_FC_TODS ? " ToDS" : "", + fc & WLAN_FC_FROMDS ? " FromDS" : ""); + PDEBUG(DEBUG_EXTRA, " A1=" MACSTR " A2=" MACSTR " A3=" + MACSTR " A4=" MACSTR "\n", + MAC2STR(txdesc.addr1), MAC2STR(txdesc.addr2), + MAC2STR(txdesc.addr3), MAC2STR(txdesc.addr4)); +} + + +/* Called only as a tasklet (software IRQ) */ +static void hostap_info_tasklet(unsigned long data) +{ + local_info_t *local = (local_info_t *) data; + struct sk_buff *skb; + + while ((skb = skb_dequeue(&local->info_list)) != NULL) { + hostap_info_process(local, skb); + dev_kfree_skb(skb); + } +} + + +/* Called only as a tasklet (software IRQ) */ +static void prism2_info(local_info_t *local) +{ + struct net_device *dev = local->dev; + u16 fid; + int res, left; + struct hfa384x_info_frame info; + struct sk_buff *skb; + + fid = HFA384X_INW(HFA384X_INFOFID_OFF); + + spin_lock(&local->baplock); + res = hfa384x_setup_bap(dev, BAP0, fid, 0); + if (!res) + res = hfa384x_from_bap(dev, BAP0, &info, sizeof(info)); + if (res) { + spin_unlock(&local->baplock); + printk(KERN_DEBUG "Could not get info frame (fid=0x%04x)\n", + fid); + if (res == -ETIMEDOUT) { + PRISM2_SCHEDULE_TASK(&local->reset_queue); + } + goto out; + } + + le16_to_cpus(&info.len); + le16_to_cpus(&info.type); + left = (info.len - 1) * 2; + + if (info.len & 0x8000 || info.len == 0 || left > 2060) { + /* data register seems to give 0x8000 in some error cases even + * though busy bit is not set in offset register; + * in addition, length must be at least 1 due to type field */ + spin_unlock(&local->baplock); + printk(KERN_DEBUG "%s: Received info frame with invalid " + "length 0x%04x (type 0x%04x)\n", dev->name, info.len, + info.type); + goto out; + } + + skb = dev_alloc_skb(sizeof(info) + left); + if (skb == NULL) { + spin_unlock(&local->baplock); + printk(KERN_DEBUG "%s: Could not allocate skb for info " + "frame\n", dev->name); + goto out; + } + + memcpy(skb_put(skb, sizeof(info)), &info, sizeof(info)); + if (left > 0 && hfa384x_from_bap(dev, BAP0, skb_put(skb, left), left)) + { + spin_unlock(&local->baplock); + printk(KERN_WARNING "%s: Info frame read failed (fid=0x%04x, " + "len=0x%04x, type=0x%04x\n", + dev->name, fid, info.len, info.type); + dev_kfree_skb(skb); + goto out; + } + spin_unlock(&local->baplock); + + skb_queue_tail(&local->info_list, skb); + HOSTAP_TASKLET_SCHEDULE(&local->info_tasklet); + + out: + HFA384X_OUTW(HFA384X_EV_INFO, HFA384X_EVACK_OFF); +} + + +/* Called only as a tasklet (software IRQ) */ +static void hostap_bap_tasklet(unsigned long data) +{ + local_info_t *local = (local_info_t *) data; + struct net_device *dev = local->dev; + u16 ev; + int frames = 30; + + if (local->func->card_present && !local->func->card_present(local)) + return; + + set_bit(HOSTAP_BITS_BAP_TASKLET, &local->bits); + + /* Process all pending BAP events without generating new interrupts + * for them */ + while (frames-- > 0) { + ev = HFA384X_INW(HFA384X_EVSTAT_OFF); + if (ev == 0xffff || !(ev & HFA384X_BAP0_EVENTS)) + break; + if (ev & HFA384X_EV_RX) + prism2_rx(local); + if (ev & HFA384X_EV_INFO) + prism2_info(local); + if (ev & HFA384X_EV_TX) + prism2_tx_ev(local); + if (ev & HFA384X_EV_TXEXC) + prism2_txexc(local); + } + + set_bit(HOSTAP_BITS_BAP_TASKLET2, &local->bits); + clear_bit(HOSTAP_BITS_BAP_TASKLET, &local->bits); + + /* Enable interrupts for new BAP events */ + hfa384x_events_all(dev); + clear_bit(HOSTAP_BITS_BAP_TASKLET2, &local->bits); +} + + +#if defined(PRISM2_PCI) && defined(PRISM2_BUS_MASTER) +/* Called only from hardware IRQ */ +static void prism2_bus_master_ev(struct net_device *dev, int bap) +{ + local_info_t *local = (local_info_t *) dev->priv; + if (bap == BAP1) { + /* FIX: frame payload was DMA'd to skb->data; might need to + * invalidate data cache for that memory area */ + skb_queue_tail(&local->rx_list, local->rx_skb); + HOSTAP_TASKLET_SCHEDULE(&local->rx_tasklet); + HFA384X_OUTW(HFA384X_EV_RX, HFA384X_EVACK_OFF); + } else { + if (prism2_transmit(dev, local->bus_m0_tx_idx)) { + printk(KERN_DEBUG "%s: prism2_transmit() failed " + "when called from bus master event\n", + dev->name); + local->intransmitfid[local->bus_m0_tx_idx] = + PRISM2_TXFID_EMPTY; + PRISM2_SCHEDULE_TASK(&local->reset_queue); + } + } +} +#endif /* PRISM2_PCI and PRISM2_BUS_MASTER */ + + +/* Called only from hardware IRQ */ +static void prism2_infdrop(struct net_device *dev) +{ + static long last_inquire = 0; + + PDEBUG(DEBUG_EXTRA, "%s: INFDROP event\n", dev->name); + + /* some firmware versions seem to get stuck with + * full CommTallies in high traffic load cases; every + * packet will then cause INFDROP event and CommTallies + * info frame will not be sent automatically. Try to + * get out of this state by inquiring CommTallies. */ + if (last_inquire + HZ < jiffies) { + hfa384x_cmd_callback(dev, HFA384X_CMDCODE_INQUIRE, + HFA384X_INFO_COMMTALLIES, NULL, NULL, NULL); + last_inquire = jiffies; + } +} + + +/* Called only from hardware IRQ */ +static void prism2_ev_tick(struct net_device *dev) +{ + local_info_t *local = (local_info_t *) dev->priv; + u16 evstat, inten; + + if (local->last_tick_timer + 5 * HZ < jiffies && + local->last_tick_timer) { + evstat = HFA384X_INW(HFA384X_EVSTAT_OFF); + inten = HFA384X_INW(HFA384X_INTEN_OFF); + printk(KERN_INFO "%s: SW TICK stuck? " + "bits=0x%lx EvStat=%04x IntEn=%04x\n", + dev->name, local->bits, evstat, inten); + local->sw_tick_stuck++; + if ((evstat & HFA384X_BAP0_EVENTS) && + (inten & HFA384X_BAP0_EVENTS)) { + printk(KERN_INFO "%s: trying to recover from IRQ " + "hang\n", dev->name); + hfa384x_events_no_bap0(dev); + } + } +} + + +/* Called only from hardware IRQ */ +static inline void prism2_check_magic(local_info_t *local) +{ + /* at least PCI Prism2.5 with bus mastering seems to sometimes + * return 0x0000 in SWSUPPORT0 for unknown reason, but re-reading the + * register once or twice seems to get the correct value.. PCI cards + * cannot anyway be removed during normal operation, so there is not + * really any need for this verification with them. */ + +#ifndef PRISM2_PCI +#ifndef final_version + static long int last_magic_err = 0; + struct net_device *dev = local->dev; + + if (HFA384X_INW(HFA384X_SWSUPPORT0_OFF) != HFA384X_MAGIC) { + if (!local->hw_ready) + return; + HFA384X_OUTW(0xffff, HFA384X_EVACK_OFF); + if (jiffies - last_magic_err > 10 * HZ) { + printk("%s: Interrupt, but SWSUPPORT0 does not match: " + "%04X != %04X - card removed?\n", dev->name, + HFA384X_INW(HFA384X_SWSUPPORT0_OFF), + HFA384X_MAGIC); + last_magic_err = jiffies; + } else if (net_ratelimit()) { + printk(KERN_DEBUG "%s: interrupt - SWSUPPORT0=%04x " + "MAGIC=%04x\n", dev->name, + HFA384X_INW(HFA384X_SWSUPPORT0_OFF), + HFA384X_MAGIC); + } + PRISM2_SCHEDULE_TASK(&local->reset_queue); + return; + } +#endif /* final_version */ +#endif /* !PRISM2_PCI */ +} + + +/* Called only from hardware IRQ */ +static irqreturn_t prism2_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct net_device *dev = (struct net_device *) dev_id; + local_info_t *local = (local_info_t *) dev->priv; + int events = 0; + u16 ev; + + prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INTERRUPT, 0, 0); + + if (local->func->card_present && !local->func->card_present(local)) { + printk(KERN_DEBUG "%s: Interrupt, but dev not OK\n", + dev->name); + return IRQ_HANDLED; + } + + prism2_check_magic(local); + + for (;;) { + ev = HFA384X_INW(HFA384X_EVSTAT_OFF); + if (ev == 0xffff) { + if (local->shutdown) + return IRQ_HANDLED; + HFA384X_OUTW(0xffff, HFA384X_EVACK_OFF); + printk(KERN_DEBUG "%s: prism2_interrupt: ev=0xffff\n", + dev->name); + PRISM2_SCHEDULE_TASK(&local->reset_queue); + return IRQ_HANDLED; + } + + ev &= HFA384X_INW(HFA384X_INTEN_OFF); + if (ev == 0) + break; + + if (ev & HFA384X_EV_CMD) { + prism2_cmd_ev(dev); + } + + /* Above events are needed even before hw is ready, but other + * events should be skipped during initialization. This may + * change for AllocEv if allocate_fid is implemented without + * busy waiting. */ + if (!local->hw_ready || local->hw_resetting || + !local->dev_enabled) { + ev = HFA384X_INW(HFA384X_EVSTAT_OFF); + if (ev & HFA384X_EV_CMD) + goto next_event; + if ((ev & HFA384X_EVENT_MASK) == 0) + return IRQ_HANDLED; + if (local->dev_enabled && (ev & ~HFA384X_EV_TICK) && + net_ratelimit()) { + printk(KERN_DEBUG "%s: prism2_interrupt: hw " + "not ready; skipping events 0x%04x " + "(IntEn=0x%04x)%s%s%s\n", + dev->name, ev, + HFA384X_INW(HFA384X_INTEN_OFF), + !local->hw_ready ? " (!hw_ready)" : "", + local->hw_resetting ? + " (hw_resetting)" : "", + !local->dev_enabled ? + " (!dev_enabled)" : ""); + } + HFA384X_OUTW(ev, HFA384X_EVACK_OFF); + return IRQ_HANDLED; + } + + if (ev & HFA384X_EV_TICK) { + prism2_ev_tick(dev); + HFA384X_OUTW(HFA384X_EV_TICK, HFA384X_EVACK_OFF); + } + +#if defined(PRISM2_PCI) && defined(PRISM2_BUS_MASTER) + if (ev & HFA384X_EV_PCI_M0) { + prism2_bus_master_ev(dev, BAP0); + HFA384X_OUTW(HFA384X_EV_PCI_M0, HFA384X_EVACK_OFF); + } + + if (ev & HFA384X_EV_PCI_M1) { + /* previous RX has been copied can be ACKed now */ + HFA384X_OUTW(HFA384X_EV_RX, HFA384X_EVACK_OFF); + + prism2_bus_master_ev(dev, BAP1); + HFA384X_OUTW(HFA384X_EV_PCI_M1, HFA384X_EVACK_OFF); + } +#endif /* PRISM2_PCI and PRISM2_BUS_MASTER */ + + if (ev & HFA384X_EV_ALLOC) { + prism2_alloc_ev(dev); + HFA384X_OUTW(HFA384X_EV_ALLOC, HFA384X_EVACK_OFF); + } + + /* Reading data from the card is quite time consuming, so do it + * in tasklets. TX, TXEXC, RX, and INFO events will be ACKed + * and unmasked after needed data has been read completely. */ + if (ev & HFA384X_BAP0_EVENTS) { + hfa384x_events_no_bap0(dev); + HOSTAP_TASKLET_SCHEDULE(&local->bap_tasklet); + } + +#ifndef final_version + if (ev & HFA384X_EV_WTERR) { + PDEBUG(DEBUG_EXTRA, "%s: WTERR event\n", dev->name); + HFA384X_OUTW(HFA384X_EV_WTERR, HFA384X_EVACK_OFF); + } +#endif /* final_version */ + + if (ev & HFA384X_EV_INFDROP) { + prism2_infdrop(dev); + HFA384X_OUTW(HFA384X_EV_INFDROP, HFA384X_EVACK_OFF); + } + + next_event: + events++; + if (events >= PRISM2_MAX_INTERRUPT_EVENTS) { + PDEBUG(DEBUG_EXTRA, "prism2_interrupt: >%d events " + "(EvStat=0x%04x)\n", + PRISM2_MAX_INTERRUPT_EVENTS, + HFA384X_INW(HFA384X_EVSTAT_OFF)); + break; + } + } + prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INTERRUPT, 0, 1); + return IRQ_RETVAL(events); +} + + +static void prism2_check_sta_fw_version(local_info_t *local) +{ + struct hfa384x_comp_ident comp; + int id, variant, major, minor; + + if (hfa384x_get_rid(local->dev, HFA384X_RID_STAID, + &comp, sizeof(comp), 1) < 0) + return; + + local->fw_ap = 0; + id = le16_to_cpu(comp.id); + if (id != HFA384X_COMP_ID_STA) { + if (id == HFA384X_COMP_ID_FW_AP) + local->fw_ap = 1; + return; + } + + major = __le16_to_cpu(comp.major); + minor = __le16_to_cpu(comp.minor); + variant = __le16_to_cpu(comp.variant); + local->sta_fw_ver = PRISM2_FW_VER(major, minor, variant); + + /* Station firmware versions before 1.4.x seem to have a bug in + * firmware-based WEP encryption when using Host AP mode, so use + * host_encrypt as a default for them. Firmware version 1.4.9 is the + * first one that has been seen to produce correct encryption, but the + * bug might be fixed before that (although, at least 1.4.2 is broken). + */ + local->fw_encrypt_ok = major > 1 || + (major == 1 && (minor > 4 || (minor == 4 && variant >= 9))); + + if (local->iw_mode == IW_MODE_MASTER && !local->host_encrypt && + !local->fw_encrypt_ok) { + printk(KERN_DEBUG "%s: defaulting to host-based encryption as " + "a workaround for firmware bug in Host AP mode WEP\n", + local->dev->name); + local->host_encrypt = 1; + } + + /* IEEE 802.11 standard compliant WDS frames (4 addresses) were broken + * in station firmware versions before 1.5.x. With these versions, the + * driver uses a workaround with bogus frame format (4th address after + * the payload). This is not compatible with other AP devices. Since + * the firmware bug is fixed in the latest station firmware versions, + * automatically enable standard compliant mode for cards using station + * firmware version 1.5.0 or newer. */ + if (major > 1 || (major == 1 && (minor >= 5))) + local->wds_type |= HOSTAP_WDS_STANDARD_FRAME; + else { + printk(KERN_DEBUG "%s: defaulting to bogus WDS frame as a " + "workaround for firmware bug in Host AP mode WDS\n", + local->dev->name); + } + + hostap_check_sta_fw_version(local->ap, major, minor, variant); +} + + +static void prism2_crypt_deinit_entries(local_info_t *local, int force) +{ + struct list_head *ptr, *n; + struct prism2_crypt_data *entry; + + for (ptr = local->crypt_deinit_list.next, n = ptr->next; + ptr != &local->crypt_deinit_list; ptr = n, n = ptr->next) { + entry = list_entry(ptr, struct prism2_crypt_data, list); + + if (atomic_read(&entry->refcnt) != 0 && !force) + continue; + + list_del(ptr); + + if (entry->ops) + entry->ops->deinit(entry->priv); + kfree(entry); + } +} + + +static void prism2_crypt_deinit_handler(unsigned long data) +{ + local_info_t *local = (local_info_t *) data; + unsigned long flags; + + spin_lock_irqsave(&local->lock, flags); + prism2_crypt_deinit_entries(local, 0); + if (!list_empty(&local->crypt_deinit_list)) { + printk(KERN_DEBUG "%s: entries remaining in delayed crypt " + "deletion list\n", local->dev->name); + local->crypt_deinit_timer.expires = jiffies + HZ; + add_timer(&local->crypt_deinit_timer); + } + spin_unlock_irqrestore(&local->lock, flags); + +} + + +static void hostap_passive_scan(unsigned long data) +{ + local_info_t *local = (local_info_t *) data; + struct net_device *dev = local->dev; + u16 channel; + + if (local->passive_scan_interval <= 0) + return; + + if (local->passive_scan_state == PASSIVE_SCAN_LISTEN) { + int max_tries = 16; + + /* Even though host system does not really know when the WLAN + * MAC is sending frames, try to avoid changing channels for + * passive scanning when a host-generated frame is being + * transmitted */ + if (test_bit(HOSTAP_BITS_TRANSMIT, &local->bits)) { + printk(KERN_DEBUG "%s: passive scan detected pending " + "TX - delaying\n", dev->name); + local->passive_scan_timer.expires = jiffies + HZ / 10; + add_timer(&local->passive_scan_timer); + return; + } + + do { + local->passive_scan_channel++; + if (local->passive_scan_channel > 14) + local->passive_scan_channel = 1; + max_tries--; + } while (!(local->channel_mask & + (1 << (local->passive_scan_channel - 1))) && + max_tries > 0); + + if (max_tries == 0) { + printk(KERN_INFO "%s: no allowed passive scan channels" + " found\n", dev->name); + return; + } + + printk(KERN_DEBUG "%s: passive scan channel %d\n", + dev->name, local->passive_scan_channel); + channel = local->passive_scan_channel; + local->passive_scan_state = PASSIVE_SCAN_WAIT; + local->passive_scan_timer.expires = jiffies + HZ / 10; + } else { + channel = local->channel; + local->passive_scan_state = PASSIVE_SCAN_LISTEN; + local->passive_scan_timer.expires = jiffies + + local->passive_scan_interval * HZ; + } + + if (hfa384x_cmd_callback(dev, HFA384X_CMDCODE_TEST | + (HFA384X_TEST_CHANGE_CHANNEL << 8), + channel, NULL, NULL, NULL)) + printk(KERN_ERR "%s: passive scan channel set %d " + "failed\n", dev->name, channel); + + add_timer(&local->passive_scan_timer); +} + + +/* Software watchdog - called as a timer. Hardware interrupt (Tick event) is + * used to monitor that local->last_tick_timer is being updated. If not, + * interrupt busy-loop is assumed and driver tries to recover by masking out + * some events. */ +static void hostap_tick_timer(unsigned long data) +{ + local_info_t *local = (local_info_t *) data; + local->last_tick_timer = jiffies; + local->tick_timer.expires = jiffies + 2 * HZ; + add_timer(&local->tick_timer); +} + + +#ifndef PRISM2_NO_PROCFS_DEBUG +static int prism2_registers_proc_read(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + char *p = page; + local_info_t *local = (local_info_t *) data; + + if (off != 0) { + *eof = 1; + return 0; + } + +#define SHOW_REG(n) \ +p += sprintf(p, "%s=%04x\n", #n, \ +hfa384x_read_reg(local->dev, HFA384X_##n##_OFF)) + + SHOW_REG(CMD); + SHOW_REG(PARAM0); + SHOW_REG(PARAM1); + SHOW_REG(PARAM2); + SHOW_REG(STATUS); + SHOW_REG(RESP0); + SHOW_REG(RESP1); + SHOW_REG(RESP2); + SHOW_REG(INFOFID); + SHOW_REG(CONTROL); + SHOW_REG(SELECT0); + SHOW_REG(SELECT1); + SHOW_REG(OFFSET0); + SHOW_REG(OFFSET1); + SHOW_REG(RXFID); + SHOW_REG(ALLOCFID); + SHOW_REG(TXCOMPLFID); + SHOW_REG(SWSUPPORT0); + SHOW_REG(SWSUPPORT1); + SHOW_REG(SWSUPPORT2); + SHOW_REG(EVSTAT); + SHOW_REG(INTEN); + SHOW_REG(EVACK); + /* Do not read data registers, because they change the state of the + * MAC (offset += 2) */ + /* SHOW_REG(DATA0); */ + /* SHOW_REG(DATA1); */ + SHOW_REG(AUXPAGE); + SHOW_REG(AUXOFFSET); + /* SHOW_REG(AUXDATA); */ +#ifdef PRISM2_PCI + SHOW_REG(PCICOR); + SHOW_REG(PCIHCR); + SHOW_REG(PCI_M0_ADDRH); + SHOW_REG(PCI_M0_ADDRL); + SHOW_REG(PCI_M0_LEN); + SHOW_REG(PCI_M0_CTL); + SHOW_REG(PCI_STATUS); + SHOW_REG(PCI_M1_ADDRH); + SHOW_REG(PCI_M1_ADDRL); + SHOW_REG(PCI_M1_LEN); + SHOW_REG(PCI_M1_CTL); +#endif /* PRISM2_PCI */ + + return (p - page); +} +#endif /* PRISM2_NO_PROCFS_DEBUG */ + + +static local_info_t * +prism2_init_local_data(struct prism2_helper_functions *funcs, int card_idx) +{ + local_info_t *local; + int len, i; + + if (funcs == NULL) + return NULL; + + local = kmalloc(sizeof(local_info_t), GFP_KERNEL); + if (local == NULL) + return NULL; + + memset(local, 0, sizeof(local_info_t)); + local->hw_module = THIS_MODULE; + local->ap = kmalloc(sizeof(struct ap_data), GFP_KERNEL); + if (local->ap == NULL) + goto fail; + + memset(local->ap, 0, sizeof(struct ap_data)); + +#ifdef PRISM2_IO_DEBUG + local->io_debug_enabled = 1; +#endif /* PRISM2_IO_DEBUG */ + +#if defined(PRISM2_PCI) && defined(PRISM2_BUS_MASTER) + local->bus_m0_buf = (u8 *) kmalloc(sizeof(struct hfa384x_tx_frame) + + PRISM2_DATA_MAXLEN, GFP_DMA); + if (local->bus_m0_buf == NULL) + goto fail; +#endif /* PRISM2_PCI and PRISM2_BUS_MASTER */ + + local->dev = kmalloc(sizeof(struct net_device) + PRISM2_NETDEV_EXTRA, + GFP_KERNEL); + if (local->dev == NULL) + goto fail; + memset(local->dev, 0, sizeof(struct net_device) + PRISM2_NETDEV_EXTRA); + prism2_set_dev_name(local->dev, local->dev + 1); + local->dev->priv = local; + + local->func = funcs; + local->func->cmd = hfa384x_cmd; + local->func->read_regs = hfa384x_read_regs; + local->func->get_rid = hfa384x_get_rid; + local->func->set_rid = hfa384x_set_rid; + local->func->hw_enable = prism2_hw_enable; + local->func->hw_config = prism2_hw_config; + local->func->hw_reset = prism2_hw_reset; + local->func->hw_shutdown = prism2_hw_shutdown; + local->func->reset_port = prism2_reset_port; + local->func->tx = prism2_tx_set_power; + local->func->schedule_reset = prism2_schedule_reset; +#ifdef PRISM2_DOWNLOAD_SUPPORT + local->func->download = prism2_download; +#endif /* PRISM2_DOWNLOAD_SUPPORT */ + local->func->rx_80211 = prism2_rx_80211; + local->func->tx_80211 = prism2_tx_80211; + + local->disable_on_close = disable_on_close; + local->mtu = mtu; + + spin_lock_init(&local->txfidlock); + spin_lock_init(&local->cmdlock); + spin_lock_init(&local->baplock); + spin_lock_init(&local->wdslock); + spin_lock_init(&local->lock); + init_MUTEX(&local->rid_bap_sem); + + if (card_idx < 0 || card_idx >= MAX_PARM_DEVICES) + card_idx = 0; + local->card_idx = card_idx; + + i = essid[card_idx] == NULL ? 0 : card_idx; + len = strlen(essid[i]); + memcpy(local->essid, essid[i], + len > MAX_SSID_LEN ? MAX_SSID_LEN : len); + local->essid[MAX_SSID_LEN] = '\0'; +#ifdef WIRELESS_EXT + i = GET_INT_PARM(iw_mode, card_idx); + if ((i >= IW_MODE_ADHOC && i <= IW_MODE_REPEAT) || + i == IW_MODE_MONITOR) { + local->iw_mode = i; + } else { + printk(KERN_WARNING "prism2: Unknown iw_mode %d; using " + "IW_MODE_MASTER\n", i); + local->iw_mode = IW_MODE_MASTER; + } +#endif + local->channel = GET_INT_PARM(channel, card_idx); + local->beacon_int = GET_INT_PARM(beacon_int, card_idx); + local->dtim_period = GET_INT_PARM(dtim_period, card_idx); + local->wds_max_connections = 16; + local->tx_control = HFA384X_TX_CTRL_FLAGS; + local->manual_retry_count = -1; +#if defined(PRISM2_PCI) && defined(PRISM2_BUS_MASTER) + local->bus_master_threshold_rx = GET_INT_PARM(bus_master_threshold_rx, + card_idx); + local->bus_master_threshold_tx = GET_INT_PARM(bus_master_threshold_tx, + card_idx); +#endif /* PRISM2_PCI and PRISM2_BUS_MASTER */ + + /* Initialize task queue structures */ + HOSTAP_QUEUE_INIT(&local->reset_queue, handle_reset_queue, local); + HOSTAP_QUEUE_INIT(&local->set_multicast_list_queue, + hostap_set_multicast_list_queue, local->dev); + + /* Initialize tasklets for handling hardware IRQ related operations + * outside hw IRQ handler */ + HOSTAP_TASKLET_INIT(&local->bap_tasklet, hostap_bap_tasklet, + (unsigned long) local); + + HOSTAP_TASKLET_INIT(&local->info_tasklet, hostap_info_tasklet, + (unsigned long) local); + hostap_info_init(local); + + HOSTAP_TASKLET_INIT(&local->tx_callback_tasklet, + hostap_tx_callback_tasklet, (unsigned long) local); + skb_queue_head_init(&local->tx_callback_list); + + HOSTAP_TASKLET_INIT(&local->rx_tasklet, + hostap_rx_tasklet, (unsigned long) local); + skb_queue_head_init(&local->rx_list); + + HOSTAP_TASKLET_INIT(&local->sta_tx_exc_tasklet, + hostap_sta_tx_exc_tasklet, (unsigned long) local); + skb_queue_head_init(&local->sta_tx_exc_list); + + INIT_LIST_HEAD(&local->cmd_queue); + init_waitqueue_head(&local->hostscan_wq); + INIT_LIST_HEAD(&local->crypt_deinit_list); + init_timer(&local->crypt_deinit_timer); + local->crypt_deinit_timer.data = (unsigned long) local; + local->crypt_deinit_timer.function = prism2_crypt_deinit_handler; + + init_timer(&local->passive_scan_timer); + local->passive_scan_timer.data = (unsigned long) local; + local->passive_scan_timer.function = hostap_passive_scan; + + init_timer(&local->tick_timer); + local->tick_timer.data = (unsigned long) local; + local->tick_timer.function = hostap_tick_timer; + local->tick_timer.expires = jiffies + 2 * HZ; + add_timer(&local->tick_timer); + + hostap_setup_dev(local->dev, local, 1); + + local->saved_eth_header_parse = local->dev->hard_header_parse; + + return local; + + fail: + kfree(local->ap); + kfree(local->dev); +#if defined(PRISM2_PCI) && defined(PRISM2_BUS_MASTER) + kfree(local->bus_m0_buf); +#endif /* PRISM2_PCI and PRISM2_BUS_MASTER */ + kfree(local->apdev); + kfree(local->stadev); + kfree(local); + return NULL; +} + + +static int prism2_init_dev(local_info_t *local) +{ + struct net_device *dev = local->dev; + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)) + { + int i = 0; + do { + sprintf(dev->name, "wlan%d", i++); + } while (dev_get(dev->name)); + } +#else + memcpy(dev->name, "wlan%d", 7); +#endif + if (register_netdev(dev)) { + printk(KERN_WARNING "%s: register_netdev() failed!\n", + dev_info); + return 1; + } + printk(KERN_INFO "%s: Registered netdevice %s\n", dev_info, dev->name); + + hostap_init_proc(local); +#ifndef PRISM2_NO_PROCFS_DEBUG + create_proc_read_entry("registers", 0, local->proc, + prism2_registers_proc_read, local); +#endif /* PRISM2_NO_PROCFS_DEBUG */ + hostap_init_data(local); + + return 0; +} + + +static void prism2_free_local_data(local_info_t *local) +{ + prism2_wds_info_t *wds, *prev; + struct hostap_tx_callback_info *tx_cb, *tx_cb_prev; + int i; + struct sk_buff *skb; + + if (local == NULL) + return; + + if (timer_pending(&local->crypt_deinit_timer)) + del_timer(&local->crypt_deinit_timer); + prism2_crypt_deinit_entries(local, 1); + + if (timer_pending(&local->passive_scan_timer)) + del_timer(&local->passive_scan_timer); + + if (timer_pending(&local->tick_timer)) + del_timer(&local->tick_timer); + + prism2_clear_cmd_queue(local); + + while ((skb = skb_dequeue(&local->info_list)) != NULL) + dev_kfree_skb(skb); + + while ((skb = skb_dequeue(&local->tx_callback_list)) != NULL) + dev_kfree_skb(skb); + + while ((skb = skb_dequeue(&local->rx_list)) != NULL) + dev_kfree_skb(skb); + + while ((skb = skb_dequeue(&local->sta_tx_exc_list)) != NULL) + dev_kfree_skb(skb); + + if (local->dev_enabled) + prism2_callback(local, PRISM2_CALLBACK_DISABLE); + + if (local->crypt) { + if (local->crypt->ops) + local->crypt->ops->deinit(local->crypt->priv); + kfree(local->crypt); + local->crypt = NULL; + } + + if (local->ap != NULL) + hostap_free_data(local->ap); + +#ifndef PRISM2_NO_PROCFS_DEBUG + if (local->proc != NULL) + remove_proc_entry("registers", local->proc); +#endif /* PRISM2_NO_PROCFS_DEBUG */ + hostap_remove_proc(local); + + wds = local->wds; + local->wds = NULL; + while (wds != NULL) { + unregister_netdev(&wds->dev); + prev = wds; + wds = wds->next; + kfree(prev); + } + + tx_cb = local->tx_callback; + while (tx_cb != NULL) { + tx_cb_prev = tx_cb; + tx_cb = tx_cb->next; + kfree(tx_cb_prev); + } + + hostap_set_hostapd(local, 0, 0); + + if (local->dev && local->dev->name && local->dev->name[0]) { + unregister_netdev(local->dev); + printk(KERN_INFO "%s: Netdevice %s unregistered\n", + dev_info, local->dev->name); + } + kfree(local->dev); + + for (i = 0; i < PRISM2_FRAG_CACHE_LEN; i++) { + if (local->frag_cache[i].skb != NULL) + dev_kfree_skb(local->frag_cache[i].skb); + } + + kfree(local->ap); +#if defined(PRISM2_PCI) && defined(PRISM2_BUS_MASTER) + kfree(local->bus_m0_buf); +#endif /* PRISM2_PCI and PRISM2_BUS_MASTER */ + kfree(local->pda); + kfree(local->last_scan_results); + kfree(local); +} + + +/* These might at some point be compiled separately and used as separate + * kernel modules or linked into one */ +#ifdef PRISM2_DOWNLOAD_SUPPORT +#include "hostap_download.c" +#endif /* PRISM2_DOWNLOAD_SUPPORT */ + +#ifdef PRISM2_CALLBACK +/* External hostap_callback.c file can be used to, e.g., blink activity led. + * This can use platform specific code and must define prism2_callback() + * function (if PRISM2_CALLBACK is not defined, these function calls are not + * used. */ +#include "hostap_callback.c" +#endif /* PRISM2_CALLBACK */ Index: drivers/net/wireless/hostap_info.c --- drivers/net/wireless/hostap_info.c.orig 2003-08-12 14:39:16.000000000 -0400 +++ drivers/net/wireless/hostap_info.c 2003-07-08 21:39:41.000000000 -0400 @@ -0,0 +1,450 @@ +/* Host AP driver Info Frame processing (part of hostap.o module) */ + + +/* Called only as a tasklet (software IRQ) */ +static void prism2_info_commtallies(local_info_t *local, unsigned char *buf, + int left) +{ + struct hfa384x_comm_tallies *tallies; + + if (left < sizeof(struct hfa384x_comm_tallies)) { + printk(KERN_DEBUG "%s: too short (len=%d) commtallies " + "info frame\n", local->dev->name, left); + return; + } + + tallies = (struct hfa384x_comm_tallies *) buf; +#define ADD_COMM_TALLIES(name) local->comm_tallies.name += tallies->name + ADD_COMM_TALLIES(tx_unicast_frames); + ADD_COMM_TALLIES(tx_multicast_frames); + ADD_COMM_TALLIES(tx_fragments); + ADD_COMM_TALLIES(tx_unicast_octets); + ADD_COMM_TALLIES(tx_multicast_octets); + ADD_COMM_TALLIES(tx_deferred_transmissions); + ADD_COMM_TALLIES(tx_single_retry_frames); + ADD_COMM_TALLIES(tx_multiple_retry_frames); + ADD_COMM_TALLIES(tx_retry_limit_exceeded); + ADD_COMM_TALLIES(tx_discards); + ADD_COMM_TALLIES(rx_unicast_frames); + ADD_COMM_TALLIES(rx_multicast_frames); + ADD_COMM_TALLIES(rx_fragments); + ADD_COMM_TALLIES(rx_unicast_octets); + ADD_COMM_TALLIES(rx_multicast_octets); + ADD_COMM_TALLIES(rx_fcs_errors); + ADD_COMM_TALLIES(rx_discards_no_buffer); + ADD_COMM_TALLIES(tx_discards_wrong_sa); + ADD_COMM_TALLIES(rx_discards_wep_undecryptable); + ADD_COMM_TALLIES(rx_message_in_msg_fragments); + ADD_COMM_TALLIES(rx_message_in_bad_msg_fragments); +#undef ADD_COMM_TALLIES +} + + +#ifndef PRISM2_NO_STATION_MODES +#ifndef PRISM2_NO_DEBUG +static const char* hfa384x_linkstatus_str(u16 linkstatus) +{ + switch (linkstatus) { + case HFA384X_LINKSTATUS_CONNECTED: + return "Connected"; + case HFA384X_LINKSTATUS_DISCONNECTED: + return "Disconnected"; + case HFA384X_LINKSTATUS_AP_CHANGE: + return "Access point change"; + case HFA384X_LINKSTATUS_AP_OUT_OF_RANGE: + return "Access point out of range"; + case HFA384X_LINKSTATUS_AP_IN_RANGE: + return "Access point in range"; + case HFA384X_LINKSTATUS_ASSOC_FAILED: + return "Association failed"; + default: + return "Unknown"; + } +} +#endif /* PRISM2_NO_DEBUG */ + + +/* Called only as a tasklet (software IRQ) */ +static void prism2_info_linkstatus(local_info_t *local, unsigned char *buf, + int left) +{ + u16 val; + int non_sta_mode; + + /* Alloc new JoinRequests to occur since LinkStatus for the previous + * has been received */ + local->last_join_time = 0; + + if (left != 2) { + printk(KERN_DEBUG "%s: invalid linkstatus info frame " + "length %d\n", local->dev->name, left); + return; + } + + non_sta_mode = local->iw_mode == IW_MODE_MASTER || + local->iw_mode == IW_MODE_REPEAT || + local->iw_mode == IW_MODE_MONITOR; + + val = buf[0] | (buf[1] << 8); + if (!non_sta_mode || val != HFA384X_LINKSTATUS_DISCONNECTED) { + PDEBUG(DEBUG_EXTRA, "%s: LinkStatus=%d (%s)\n", + local->dev->name, val, hfa384x_linkstatus_str(val)); + } + + if (non_sta_mode) + return; + + /* Get current BSSID later in scheduled task */ + set_bit(PRISM2_INFO_PENDING_LINKSTATUS, &local->pending_info); + local->prev_link_status = val; + PRISM2_SCHEDULE_TASK(&local->info_queue); +} + + +static void prism2_host_roaming(local_info_t *local) +{ + struct hfa384x_join_request req; + struct net_device *dev = local->dev; + struct hfa384x_scan_result *selected, *entry; + int i; + unsigned long flags; + + if (local->last_join_time && + time_before(jiffies, local->last_join_time + 10 * HZ)) { + PDEBUG(DEBUG_EXTRA, "%s: last join request has not yet been " + "completed - waiting for it before issuing new one\n", + dev->name); + return; + } + + /* ScanResults are sorted: first ESS results in decreasing signal + * quality then IBSS results in similar order. + * Trivial roaming policy: just select the first entry. + * This could probably be improved by adding hysteresis to limit + * number of handoffs, etc. + * + * Could do periodic RID_SCANREQUEST or Inquire F101 to get new + * ScanResults */ + spin_lock_irqsave(&local->lock, flags); + if (local->last_scan_results == NULL || + local->last_scan_results_count == 0) { + spin_unlock_irqrestore(&local->lock, flags); + PDEBUG(DEBUG_EXTRA, "%s: no scan results for host roaming\n", + dev->name); + return; + } + + selected = &local->last_scan_results[0]; + + if (local->preferred_ap[0] || local->preferred_ap[1] || + local->preferred_ap[2] || local->preferred_ap[3] || + local->preferred_ap[4] || local->preferred_ap[5]) { + /* Try to find preferred AP */ + PDEBUG(DEBUG_EXTRA, "%s: Preferred AP BSSID " MACSTR "\n", + dev->name, MAC2STR(local->preferred_ap)); + for (i = 0; i < local->last_scan_results_count; i++) { + entry = &local->last_scan_results[i]; + if (memcmp(local->preferred_ap, entry->bssid, 6) == 0) + { + PDEBUG(DEBUG_EXTRA, "%s: using preferred AP " + "selection\n", dev->name); + selected = entry; + break; + } + } + } + + memcpy(req.bssid, selected->bssid, 6); + req.channel = selected->chid; + spin_unlock_irqrestore(&local->lock, flags); + + PDEBUG(DEBUG_EXTRA, "%s: JoinRequest: BSSID=" MACSTR " channel=%d\n", + dev->name, MAC2STR(req.bssid), le16_to_cpu(req.channel)); + if (local->func->set_rid(dev, HFA384X_RID_JOINREQUEST, &req, + sizeof(req))) { + printk(KERN_DEBUG "%s: JoinRequest failed\n", dev->name); + } + local->last_join_time = jiffies; +} + + +/* Called only as a tasklet (software IRQ) */ +static void prism2_info_scanresults(local_info_t *local, unsigned char *buf, + int left) +{ + u16 *pos; + int new_count; + unsigned long flags; + + if (left < 4) { + printk(KERN_DEBUG "%s: invalid scanresult info frame " + "length %d\n", local->dev->name, left); + return; + } + + PDEBUG(DEBUG_EXTRA, "%s: ScanResults:", local->dev->name); + pos = (u16 *) buf; + PDEBUG2(DEBUG_EXTRA, " Reserved=0x%04x", le16_to_cpu(*pos)); + pos++; + PDEBUG2(DEBUG_EXTRA, " ScanReason=%d\n", le16_to_cpu(*pos)); + pos++; + left -= 4; + + /* Reserve large enough buffer to fit all ScanResults entries. + * If the previous buffer is large enough, do not free and re-allocate + * unnecessarily. This forgets the original buffer size, so + * re-allocation is used when more APs are found. */ + new_count = left / sizeof(struct hfa384x_scan_result); + spin_lock_irqsave(&local->lock, flags); + if (local->last_scan_results == NULL || + local->last_scan_results_count < new_count) { + if (local->last_scan_results) + kfree(local->last_scan_results); + local->last_scan_results = + kmalloc(new_count * sizeof(struct hfa384x_scan_result), + GFP_ATOMIC); + if (local->last_scan_results == NULL) { + local->last_scan_results_count = 0; + spin_unlock_irqrestore(&local->lock, flags); + printk(KERN_DEBUG "%s: could not kmalloc memory for " + "scan results\n", local->dev->name); + return; + } + } + local->last_scan_results_count = new_count; + memcpy(local->last_scan_results, pos, + new_count * sizeof(struct hfa384x_scan_result)); + spin_unlock_irqrestore(&local->lock, flags); + + /* Perform rest of ScanResults handling later in scheduled task */ + set_bit(PRISM2_INFO_PENDING_SCANRESULTS, &local->pending_info); + PRISM2_SCHEDULE_TASK(&local->info_queue); +} + + +#ifndef PRISM2_NO_DEBUG +/* Called only as a tasklet (software IRQ) */ +static void prism2_info_hostscanresults(local_info_t *local, + unsigned char *buf, int left) +{ + u16 *pos, val, result_size; + u8 *ptr; + int i; + + wake_up_interruptible(&local->hostscan_wq); + + if (left < 4) { + printk(KERN_DEBUG "%s: invalid hostscanresult info frame " + "length %d\n", local->dev->name, left); + return; + } + + pos = (u16 *) buf; + result_size = le16_to_cpu(*pos); + + PDEBUG(DEBUG_EXTRA, "%s: HostScanResults (%d):\n", local->dev->name, + (left - 4) / result_size); + PDEBUG(DEBUG_EXTRA, " ResultSize=0x%04x\n", result_size); + pos++; + PDEBUG(DEBUG_EXTRA, " Reserved=%u\n", le16_to_cpu(*pos)); + pos++; + left -= 4; + ptr = (u8 *) pos; + + val = 1; + while (left >= result_size) { + struct hfa384x_hostscan_result *scan; + u16 len; + + PDEBUG(DEBUG_EXTRA, " Host Scan Result %u\n", val); + scan = (struct hfa384x_hostscan_result *) ptr; + left -= result_size; + ptr += result_size; + val++; + + if (result_size == sizeof(*scan)) { + PDEBUG(DEBUG_EXTRA, " CHID=%d ANL=%d SL=%d BSSID=" + MACSTR " BeaconInt=%d\n", + le16_to_cpu(scan->chid), + (signed short) le16_to_cpu(scan->anl), + (signed short) le16_to_cpu(scan->sl), + MAC2STR(scan->bssid), + le16_to_cpu(scan->beacon_interval)); + PDEBUG(DEBUG_EXTRA, " Capability=0x%x", + le16_to_cpu(scan->capability)); + len = le16_to_cpu(scan->ssid_len); + if (len > 0 && len < sizeof(scan->ssid)) { + PDEBUG2(DEBUG_EXTRA, " SSID[%d]=", len); + for (i = 0; i < len; i++) { + PDEBUG2(DEBUG_EXTRA, + (scan->ssid[i] < 32 || + scan->ssid[i] >= 127) ? + "<%02x>" : "%c", + scan->ssid[i]); + } + } + PDEBUG2(DEBUG_EXTRA, "\n"); + PDEBUG(DEBUG_EXTRA, " SUP_RATES="); + for (i = 0; i < sizeof(scan->sup_rates); i++) + if (scan->sup_rates[i]) + PDEBUG2(DEBUG_EXTRA, "<%02x>", + scan->sup_rates[i]); + PDEBUG2(DEBUG_EXTRA, " RATE=%d ATIM=%d\n", + le16_to_cpu(scan->rate), + le16_to_cpu(scan->atim)); + } + } + if (left) { + PDEBUG(DEBUG_EXTRA, " Unknown extra data: "); + for (i = 0; i < left; i++) + PDEBUG2(DEBUG_EXTRA, " %02x", *ptr++); + PDEBUG2(DEBUG_EXTRA, "\n"); + } +} +#endif /* PRISM2_NO_DEBUG */ +#endif /* PRISM2_NO_STATION_MODES */ + + +/* Called only as a tasklet (software IRQ) */ +void hostap_info_process(local_info_t *local, struct sk_buff *skb) +{ + struct hfa384x_info_frame *info; + unsigned char *buf; + int left; +#ifndef PRISM2_NO_DEBUG + int i; +#endif /* PRISM2_NO_DEBUG */ + + info = (struct hfa384x_info_frame *) skb->data; + buf = skb->data + sizeof(*info); + left = skb->len - sizeof(*info); + + switch (info->type) { + case HFA384X_INFO_COMMTALLIES: + prism2_info_commtallies(local, buf, left); + break; + +#ifndef PRISM2_NO_STATION_MODES + case HFA384X_INFO_LINKSTATUS: + prism2_info_linkstatus(local, buf, left); + break; + + case HFA384X_INFO_SCANRESULTS: + prism2_info_scanresults(local, buf, left); + break; + +#ifndef PRISM2_NO_DEBUG + case HFA384X_INFO_HOSTSCANRESULTS: + prism2_info_hostscanresults(local, buf, left); + break; +#endif /* PRISM2_NO_DEBUG */ +#endif /* PRISM2_NO_STATION_MODES */ + +#ifndef PRISM2_NO_DEBUG + default: + PDEBUG(DEBUG_EXTRA, "%s: INFO - len=%d type=0x%04x\n", + local->dev->name, info->len, info->type); + PDEBUG(DEBUG_EXTRA, "Unknown info frame:"); + for (i = 0; i < (left < 100 ? left : 100); i++) + PDEBUG2(DEBUG_EXTRA, " %02x", buf[i]); + PDEBUG2(DEBUG_EXTRA, "\n"); + break; +#endif /* PRISM2_NO_DEBUG */ + } +} + + +#ifndef PRISM2_NO_STATION_MODES +static void handle_info_queue_linkstatus(local_info_t *local) +{ + int val = local->prev_link_status; + int connected; + + connected = + val == HFA384X_LINKSTATUS_CONNECTED || + val == HFA384X_LINKSTATUS_AP_CHANGE || + val == HFA384X_LINKSTATUS_AP_IN_RANGE; + + if (local->func->get_rid(local->dev, HFA384X_RID_CURRENTBSSID, + local->bssid, ETH_ALEN, 1) < 0) { + printk(KERN_DEBUG "%s: could not read CURRENTBSSID after " + "LinkStatus event\n", local->dev->name); + } else { + PDEBUG(DEBUG_EXTRA, "%s: LinkStatus: BSSID=" MACSTR "\n", + local->dev->name, + MAC2STR((unsigned char *) local->bssid)); + if (local->wds_type & HOSTAP_WDS_AP_CLIENT) + hostap_add_sta(local->ap, local->bssid); + } + +#if WIRELESS_EXT > 13 + { + union iwreq_data wrqu; + + /* Get BSSID if we have a valid AP address */ + if (connected) + memcpy(wrqu.ap_addr.sa_data, local->bssid, ETH_ALEN); + else + memset(wrqu.ap_addr.sa_data, 0, ETH_ALEN); + wrqu.ap_addr.sa_family = ARPHRD_ETHER; + wireless_send_event(local->dev, SIOCGIWAP, &wrqu, NULL); + } +#endif /* WIRELESS_EXT > 13 */ +} + + +static void handle_info_queue_scanresults(local_info_t *local) +{ + if (local->host_roaming && local->iw_mode == IW_MODE_INFRA) + prism2_host_roaming(local); + +#if WIRELESS_EXT > 13 + { + union iwreq_data wrqu; + + /* Inform user space about new scan results (just empty event, + * SIOCGIWSCAN can be used to fetch data */ + wrqu.data.length = 0; + wrqu.data.flags = 0; + wireless_send_event(local->dev, SIOCGIWSCAN, &wrqu, NULL); + + /* Allow SIOCGIWSCAN handling to occur since we have received + * scanning result */ + local->scan_timestamp = 0; + } +#endif /* WIRELESS_EXT > 13 */ +} + + +/* Called only as scheduled task after receiving info frames (used to avoid + * pending too much time in HW IRQ handler). */ +static void handle_info_queue(void *data) +{ + local_info_t *local = (local_info_t *) data; + + if (test_and_clear_bit(PRISM2_INFO_PENDING_LINKSTATUS, + &local->pending_info)) + handle_info_queue_linkstatus(local); + + if (test_and_clear_bit(PRISM2_INFO_PENDING_SCANRESULTS, + &local->pending_info)) + handle_info_queue_scanresults(local); + +#ifndef NEW_MODULE_CODE + MOD_DEC_USE_COUNT; +#endif +} +#endif /* PRISM2_NO_STATION_MODES */ + + +void hostap_info_init(local_info_t *local) +{ + skb_queue_head_init(&local->info_list); +#ifndef PRISM2_NO_STATION_MODES + HOSTAP_QUEUE_INIT(&local->info_queue, handle_info_queue, local); +#endif /* PRISM2_NO_STATION_MODES */ +} + + +EXPORT_SYMBOL(hostap_info_init); +EXPORT_SYMBOL(hostap_info_process); Index: drivers/net/wireless/hostap_ioctl.c --- drivers/net/wireless/hostap_ioctl.c.orig 2003-08-12 14:39:16.000000000 -0400 +++ drivers/net/wireless/hostap_ioctl.c 2003-07-10 22:49:55.000000000 -0400 @@ -0,0 +1,3592 @@ +/* ioctl() (mostly Linux Wireless Extensions) routines for Host AP driver */ + +#ifdef in_atomic +/* Get kernel_locked() for in_atomic() */ +#include +#endif + +#ifdef WIRELESS_EXT + +/* Conversion to new driver API by Jean II */ + +#if WIRELESS_EXT <= 12 +/* Wireless extensions backward compatibility */ + +/* Dummy prototype, as we don't really need it */ +struct iw_request_info; +#endif /* WIRELESS_EXT <= 12 */ + + +#if WIRELESS_EXT >= 15 +/* Wireless ext ver15 allows verification of iwpriv support and sub-ioctls can + * be included even if not especially configured. */ +#ifndef PRISM2_USE_WE_SUB_IOCTLS +#define PRISM2_USE_WE_SUB_IOCTLS +#endif /* PRISM2_USE_WE_SUB_IOCTLS */ + +/* Assume that hosts using new wireless ext also have new wireless tools + * (ver >= 25) */ +#ifndef PRISM2_USE_WE_TYPE_ADDR +#define PRISM2_USE_WE_TYPE_ADDR +#endif /* PRISM2_USE_WE_TYPE_ADDR */ +#endif /* WIRELESS_EXT >= 15 */ + + +#ifdef PRISM2_USE_WE_TYPE_ADDR +/* Added in WIRELESS_EXT 15, but can be used with older versions assuming + * iwpriv ver >= 25 */ +#ifndef IW_PRIV_TYPE_ADDR +#define IW_PRIV_TYPE_ADDR 0x6000 +#endif /* IW_PRIV_TYPE_ADDR */ +#endif /* PRISM2_USE_WE_TYPE_ADDR */ + + +#if WIRELESS_EXT < 9 +struct iw_point { + caddr_t pointer; + __u16 length; + __u16 flags; +}; +#endif /* WIRELESS_EXT < 9 */ + + +static struct iw_statistics *hostap_get_wireless_stats(struct net_device *dev) +{ + local_info_t *local = (local_info_t *) dev->priv; + + local->wstats.status = 0; + local->wstats.discard.code = + local->comm_tallies.rx_discards_wep_undecryptable; + local->wstats.discard.misc = + local->comm_tallies.rx_fcs_errors + + local->comm_tallies.rx_discards_no_buffer + + local->comm_tallies.tx_discards_wrong_sa; + +#if WIRELESS_EXT > 11 + local->wstats.discard.retries = + local->comm_tallies.tx_retry_limit_exceeded; + local->wstats.discard.fragment = + local->comm_tallies.rx_message_in_bad_msg_fragments; +#endif /* WIRELESS_EXT > 11 */ + + if (local->iw_mode != IW_MODE_MASTER && + local->iw_mode != IW_MODE_REPEAT) { + struct hfa384x_comms_quality sq; +#ifdef in_atomic + /* FIX: get_rid() will sleep and it must not be called + * in interrupt context or while atomic. However, this + * function seems to be called while atomic (at least in Linux + * 2.5.59). Now, we just avoid illegal call, but in this case + * the signal quality values are not shown. Statistics could be + * collected before, if this really needs to be called while + * atomic. */ + if (in_atomic()) { + printk(KERN_DEBUG "%s: hostap_get_wireless_stats() " + "called while atomic - skipping signal " + "quality query\n", dev->name); + } else +#endif /* in_atomic */ + if (local->func->get_rid(local->dev, + HFA384X_RID_COMMSQUALITY, + &sq, sizeof(sq), 1) >= 0) { + local->wstats.qual.qual = le16_to_cpu(sq.comm_qual); + local->wstats.qual.level = HFA384X_LEVEL_TO_dBm( + le16_to_cpu(sq.signal_level)); + local->wstats.qual.noise = HFA384X_LEVEL_TO_dBm( + le16_to_cpu(sq.noise_level)); + local->wstats.qual.updated = 7; + } + } + + return &local->wstats; +} + + +static int prism2_get_datarates(struct net_device *dev, u8 *rates) +{ + local_info_t *local = (local_info_t *) dev->priv; + u8 buf[12]; + int len; + u16 val; + + len = local->func->get_rid(dev, HFA384X_RID_SUPPORTEDDATARATES, buf, + sizeof(buf), 0); + if (len < 2) + return 0; + + val = le16_to_cpu(*(u16 *) buf); /* string length */ + + if (len - 2 < val || val > 10) + return 0; + + memcpy(rates, buf + 2, val); + return val; +} + + +static int prism2_get_name(struct net_device *dev, + struct iw_request_info *info, + char *name, char *extra) +{ + u8 rates[10]; + int len, i, over2 = 0; + + len = prism2_get_datarates(dev, rates); + + for (i = 0; i < len; i++) { + if (rates[i] == 0x0b || rates[i] == 0x16) { + over2 = 1; + break; + } + } + + strcpy(name, over2 ? "IEEE 802.11b" : "IEEE 802.11-DS"); + + return 0; +} + + +static void prism2_crypt_delayed_deinit(local_info_t *local, + struct prism2_crypt_data **crypt) +{ + struct prism2_crypt_data *tmp; + unsigned long flags; + + tmp = *crypt; + *crypt = NULL; + + if (tmp == NULL) + return; + + /* must not run ops->deinit() while there may be pending encrypt or + * decrypt operations. Use a list of delayed deinits to avoid needing + * locking. */ + + spin_lock_irqsave(&local->lock, flags); + list_add(&tmp->list, &local->crypt_deinit_list); + if (!timer_pending(&local->crypt_deinit_timer)) { + local->crypt_deinit_timer.expires = jiffies + HZ; + add_timer(&local->crypt_deinit_timer); + } + spin_unlock_irqrestore(&local->lock, flags); +} + + +#if WIRELESS_EXT > 8 +static int prism2_ioctl_siwencode(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *erq, char *keybuf) +{ + local_info_t *local = (local_info_t *) dev->priv; + int i; + int first = 0; + + if (erq->flags & IW_ENCODE_DISABLED) { + prism2_crypt_delayed_deinit(local, &local->crypt); + goto done; + } + + if (local->crypt != NULL && local->crypt->ops != NULL && + strcmp(local->crypt->ops->name, "WEP") != 0) { + /* changing to use WEP; deinit previously used algorithm */ + prism2_crypt_delayed_deinit(local, &local->crypt); + } + + if (local->crypt == NULL) { + struct prism2_crypt_data *new_crypt; + + /* take WEP into use */ + new_crypt = (struct prism2_crypt_data *) + kmalloc(sizeof(struct prism2_crypt_data), GFP_KERNEL); + if (new_crypt == NULL) + return -ENOMEM; + memset(new_crypt, 0, sizeof(struct prism2_crypt_data)); + new_crypt->ops = hostap_get_crypto_ops("WEP"); + if (!new_crypt->ops) { + request_module("hostap_crypt_wep"); + new_crypt->ops = hostap_get_crypto_ops("WEP"); + } + if (new_crypt->ops) + new_crypt->priv = new_crypt->ops->init(); + if (!new_crypt->ops || !new_crypt->priv) { + kfree(new_crypt); + new_crypt = NULL; + + printk(KERN_WARNING "%s: could not initialize WEP: " + "load module hostap_crypt_wep.o\n", + dev->name); + return -EOPNOTSUPP; + } + first = 1; + local->crypt = new_crypt; + } + + i = erq->flags & IW_ENCODE_INDEX; + if (i < 1 || i > 4) + i = local->crypt->ops->get_key_idx(local->crypt->priv); + else + i--; + if (i < 0 || i >= WEP_KEYS) + return -EINVAL; + + if (erq->length > 0) { + int len = erq->length <= 5 ? 5 : 13; + if (len > erq->length) + memset(keybuf + erq->length, 0, len - erq->length); + local->crypt->ops->set_key(i, keybuf, len, local->crypt->priv); + if (first) + local->crypt->ops->set_key_idx(i, local->crypt->priv); + } else { + if (local->crypt->ops->set_key_idx(i, local->crypt->priv) < 0) + return -EINVAL; /* keyidx not valid */ + } + + done: + local->open_wep = erq->flags & IW_ENCODE_OPEN; + + if (hostap_set_encryption(local)) { + printk(KERN_DEBUG "%s: set_encryption failed\n", dev->name); + return -EINVAL; + } + + /* Do not reset port0 if card is in Managed mode since resetting will + * generate new IEEE 802.11 authentication which may end up in looping + * with IEEE 802.1X. Prism2 documentation seem to require port reset + * after WEP configuration. However, keys are apparently changed at + * least in Managed mode. */ + if (local->iw_mode != IW_MODE_INFRA && local->func->reset_port(dev)) { + printk(KERN_DEBUG "%s: reset_port failed\n", dev->name); + return -EINVAL; + } + + return 0; +} + + +static int prism2_ioctl_giwencode(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *erq, char *key) +{ + local_info_t *local = (local_info_t *) dev->priv; + int i, len; + u16 val; + + if (local->crypt == NULL || local->crypt->ops == NULL) { + erq->length = 0; + erq->flags = IW_ENCODE_DISABLED; + return 0; + } + + if (strcmp(local->crypt->ops->name, "WEP") != 0) { + /* only WEP is supported with wireless extensions, so just + * report that encryption is used */ + erq->length = 0; + erq->flags = IW_ENCODE_ENABLED; + return 0; + } + + i = erq->flags & IW_ENCODE_INDEX; + if (i < 1 || i > 4) + i = local->crypt->ops->get_key_idx(local->crypt->priv); + else + i--; + if (i < 0 || i >= WEP_KEYS) + return -EINVAL; + + erq->flags = i + 1; + + /* Reads from HFA384X_RID_CNFDEFAULTKEY* return bogus values, so show + * the keys from driver buffer */ + len = local->crypt->ops->get_key(i, key, WEP_KEY_LEN, + local->crypt->priv); + erq->length = (len >= 0 ? len : 0); + + if (local->func->get_rid(dev, HFA384X_RID_CNFWEPFLAGS, &val, 2, 1) < 0) + { + printk("CNFWEPFLAGS reading failed\n"); + return -EOPNOTSUPP; + } + le16_to_cpus(&val); + if (val & HFA384X_WEPFLAGS_PRIVACYINVOKED) + erq->flags |= IW_ENCODE_ENABLED; + else + erq->flags |= IW_ENCODE_DISABLED; + if (val & HFA384X_WEPFLAGS_EXCLUDEUNENCRYPTED) + erq->flags |= IW_ENCODE_RESTRICTED; + else + erq->flags |= IW_ENCODE_OPEN; + + return 0; +} + + +#if WIRELESS_EXT <= 15 +static int prism2_ioctl_giwspy(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *srq, char *extra) +{ + local_info_t *local = (local_info_t *) dev->priv; + struct sockaddr addr[IW_MAX_SPY]; + struct iw_quality qual[IW_MAX_SPY]; + + if (local->iw_mode != IW_MODE_MASTER) { + printk("SIOCGIWSPY is currently only supported in Host AP " + "mode\n"); + srq->length = 0; + return -EOPNOTSUPP; + } + + srq->length = prism2_ap_get_sta_qual(local, addr, qual, IW_MAX_SPY, 0); + + memcpy(extra, &addr, sizeof(addr[0]) * srq->length); + memcpy(extra + sizeof(addr[0]) * srq->length, &qual, + sizeof(qual[0]) * srq->length); + + return 0; +} +#endif /* WIRELESS_EXT <= 15 */ + + +static int hostap_set_rate(struct net_device *dev) +{ + local_info_t *local = (local_info_t *) dev->priv; + int ret; + + ret = (hostap_set_word(dev, HFA384X_RID_TXRATECONTROL, + local->tx_rate_control) || + hostap_set_word(dev, HFA384X_RID_CNFSUPPORTEDRATES, + local->tx_rate_control) || + local->func->reset_port(dev)); + + if (ret) { + printk(KERN_WARNING "%s: TXRateControl/cnfSupportedRates " + "setting to 0x%x failed\n", + dev->name, local->tx_rate_control); + } + + /* Update TX rate configuration for all STAs based on new operational + * rate set. */ + hostap_update_rates(local); + + return ret; +} + + +static int prism2_ioctl_siwrate(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *rrq, char *extra) +{ + local_info_t *local = (local_info_t *) dev->priv; + + if (rrq->fixed) { + switch (rrq->value) { + case 11000000: + local->tx_rate_control = HFA384X_RATES_11MBPS; + break; + case 5500000: + local->tx_rate_control = HFA384X_RATES_5MBPS; + break; + case 2000000: + local->tx_rate_control = HFA384X_RATES_2MBPS; + break; + case 1000000: + local->tx_rate_control = HFA384X_RATES_1MBPS; + break; + default: + local->tx_rate_control = HFA384X_RATES_1MBPS | + HFA384X_RATES_2MBPS | HFA384X_RATES_5MBPS | + HFA384X_RATES_11MBPS; + break; + } + } else { + switch (rrq->value) { + case 11000000: + local->tx_rate_control = HFA384X_RATES_1MBPS | + HFA384X_RATES_2MBPS | HFA384X_RATES_5MBPS | + HFA384X_RATES_11MBPS; + break; + case 5500000: + local->tx_rate_control = HFA384X_RATES_1MBPS | + HFA384X_RATES_2MBPS | HFA384X_RATES_5MBPS; + break; + case 2000000: + local->tx_rate_control = HFA384X_RATES_1MBPS | + HFA384X_RATES_2MBPS; + break; + case 1000000: + local->tx_rate_control = HFA384X_RATES_1MBPS; + break; + default: + local->tx_rate_control = HFA384X_RATES_1MBPS | + HFA384X_RATES_2MBPS | HFA384X_RATES_5MBPS | + HFA384X_RATES_11MBPS; + break; + } + } + + return hostap_set_rate(dev); +} + + +static int prism2_ioctl_giwrate(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *rrq, char *extra) +{ + u16 val; + local_info_t *local = (local_info_t *) dev->priv; + int ret = 0; + + if (local->func->get_rid(dev, HFA384X_RID_TXRATECONTROL, &val, 2, 1) < + 0) + return -EINVAL; + + if ((val & 0x1) && (val > 1)) + rrq->fixed = 0; + else + rrq->fixed = 1; + + if (local->iw_mode == IW_MODE_MASTER && local->ap != NULL && + !local->fw_tx_rate_control) { + /* HFA384X_RID_CURRENTTXRATE seems to always be 2 Mbps in + * Host AP mode, so use the recorded TX rate of the last sent + * frame */ + rrq->value = local->ap->last_tx_rate > 0 ? + local->ap->last_tx_rate * 100000 : 11000000; + return 0; + } + + if (local->func->get_rid(dev, HFA384X_RID_CURRENTTXRATE, &val, 2, 1) < + 0) + return -EINVAL; + + switch (val) { + case HFA384X_RATES_1MBPS: + rrq->value = 1000000; + break; + case HFA384X_RATES_2MBPS: + rrq->value = 2000000; + break; + case HFA384X_RATES_5MBPS: + rrq->value = 5500000; + break; + case HFA384X_RATES_11MBPS: + rrq->value = 11000000; + break; + default: + /* should not happen */ + rrq->value = 11000000; + ret = -EINVAL; + break; + } + + return ret; +} + + +static int prism2_ioctl_siwsens(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *sens, char *extra) +{ + local_info_t *local = (local_info_t *) dev->priv; + + /* Set the desired AP density */ + if (sens->value < 1 || sens->value > 3) + return -EINVAL; + + if (hostap_set_word(dev, HFA384X_RID_CNFSYSTEMSCALE, sens->value) || + local->func->reset_port(dev)) + return -EINVAL; + + return 0; +} + +static int prism2_ioctl_giwsens(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *sens, char *extra) +{ + local_info_t *local = (local_info_t *) dev->priv; + u16 val; + + /* Get the current AP density */ + if (local->func->get_rid(dev, HFA384X_RID_CNFSYSTEMSCALE, &val, 2, 1) < + 0) + return -EINVAL; + + sens->value = __le16_to_cpu(val); + sens->fixed = 1; + + return 0; +} + + +/* Deprecated in new wireless extension API */ +static int prism2_ioctl_giwaplist(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *data, char *extra) +{ + local_info_t *local = (local_info_t *) dev->priv; + struct sockaddr addr[IW_MAX_AP]; + struct iw_quality qual[IW_MAX_AP]; + + if (local->iw_mode != IW_MODE_MASTER) { + printk(KERN_DEBUG "SIOCGIWAPLIST is currently only supported " + "in Host AP mode\n"); + data->length = 0; + return -EOPNOTSUPP; + } + + data->length = prism2_ap_get_sta_qual(local, addr, qual, IW_MAX_AP, 1); + + memcpy(extra, &addr, sizeof(addr[0]) * data->length); + data->flags = 1; /* has quality information */ + memcpy(extra + sizeof(addr[0]) * data->length, &qual, + sizeof(qual[0]) * data->length); + + return 0; +} + + +static int prism2_ioctl_siwrts(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *rts, char *extra) +{ + local_info_t *local = (local_info_t *) dev->priv; + u16 val; + + if (rts->disabled) + val = __constant_cpu_to_le16(2347); + else if (rts->value < 0 || rts->value > 2347) + return -EINVAL; + else + val = __cpu_to_le16(rts->value); + + if (local->func->set_rid(dev, HFA384X_RID_RTSTHRESHOLD, &val, 2) || + local->func->reset_port(dev)) + return -EINVAL; + + return 0; +} + +static int prism2_ioctl_giwrts(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *rts, char *extra) +{ + local_info_t *local = (local_info_t *) dev->priv; + u16 val; + + if (local->func->get_rid(dev, HFA384X_RID_RTSTHRESHOLD, &val, 2, 1) < + 0) + return -EINVAL; + + rts->value = __le16_to_cpu(val); + rts->disabled = (rts->value == 2347); + rts->fixed = 1; + + return 0; +} + + +static int prism2_ioctl_siwfrag(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *rts, char *extra) +{ + local_info_t *local = (local_info_t *) dev->priv; + u16 val; + + if (rts->disabled) + val = __constant_cpu_to_le16(2346); + else if (rts->value < 256 || rts->value > 2346) + return -EINVAL; + else + val = __cpu_to_le16(rts->value & ~0x1); /* even numbers only */ + + if (local->func->set_rid(dev, HFA384X_RID_FRAGMENTATIONTHRESHOLD, &val, + 2) + || local->func->reset_port(dev)) + return -EINVAL; + + return 0; +} + +static int prism2_ioctl_giwfrag(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *rts, char *extra) +{ + local_info_t *local = (local_info_t *) dev->priv; + u16 val; + + if (local->func->get_rid(dev, HFA384X_RID_FRAGMENTATIONTHRESHOLD, + &val, 2, 1) < 0) + return -EINVAL; + + rts->value = __le16_to_cpu(val); + rts->disabled = (rts->value == 2346); + rts->fixed = 1; + + return 0; +} +#endif /* WIRELESS_EXT > 8 */ + + +static int prism2_ioctl_siwap(struct net_device *dev, + struct iw_request_info *info, + struct sockaddr *ap_addr, char *extra) +{ +#ifdef PRISM2_NO_STATION_MODES + return -EOPNOTSUPP; +#else /* PRISM2_NO_STATION_MODES */ + local_info_t *local = (local_info_t *) dev->priv; + + memcpy(local->preferred_ap, &ap_addr->sa_data, ETH_ALEN); + + if (local->host_roaming && local->iw_mode == IW_MODE_INFRA) { + struct hfa384x_scan_request scan_req; + memset(&scan_req, 0, sizeof(scan_req)); + scan_req.channel_list = __constant_cpu_to_le16(0x3fff); + scan_req.txrate = __constant_cpu_to_le16(HFA384X_RATES_1MBPS); + if (local->func->set_rid(dev, HFA384X_RID_SCANREQUEST, + &scan_req, sizeof(scan_req))) { + printk(KERN_DEBUG "%s: ScanResults request failed - " + "preferred AP delayed to next unsolicited " + "scan\n", dev->name); + } + } else { + printk(KERN_DEBUG "%s: Preferred AP (SIOCSIWAP) is used only " + "in Managed mode when host_roaming is enabled\n", + dev->name); + } + + return 0; +#endif /* PRISM2_NO_STATION_MODES */ +} + +static int prism2_ioctl_giwap(struct net_device *dev, + struct iw_request_info *info, + struct sockaddr *ap_addr, char *extra) +{ + local_info_t *local = (local_info_t *) dev->priv; + + if (dev == local->stadev) { + memcpy(&ap_addr->sa_data, local->assoc_ap_addr, ETH_ALEN); + ap_addr->sa_family = ARPHRD_ETHER; + return 0; + } + + if (local->func->get_rid(dev, HFA384X_RID_CURRENTBSSID, + &ap_addr->sa_data, ETH_ALEN, 1) < 0) + return -EOPNOTSUPP; + + /* local->bssid is also updated in LinkStatus handler when in station + * mode */ + memcpy(local->bssid, &ap_addr->sa_data, ETH_ALEN); + ap_addr->sa_family = ARPHRD_ETHER; + + return 0; +} + + +#if WIRELESS_EXT > 8 +static int prism2_ioctl_siwnickn(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *data, char *nickname) +{ + local_info_t *local = (local_info_t *) dev->priv; + + memset(local->name, 0, sizeof(local->name)); + memcpy(local->name, nickname, data->length); + local->name_set = 1; + + if (hostap_set_string(dev, HFA384X_RID_CNFOWNNAME, local->name) || + local->func->reset_port(dev)) + return -EINVAL; + + return 0; +} + +static int prism2_ioctl_giwnickn(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *data, char *nickname) +{ + local_info_t *local = (local_info_t *) dev->priv; + int len; + char name[MAX_NAME_LEN + 3]; + u16 val; + + len = local->func->get_rid(dev, HFA384X_RID_CNFOWNNAME, + &name, MAX_NAME_LEN + 2, 0); + val = __le16_to_cpu(*(u16 *) name); + if (len > MAX_NAME_LEN + 2 || len < 0 || val > MAX_NAME_LEN) + return -EOPNOTSUPP; + + name[val + 2] = '\0'; + data->length = val + 1; + memcpy(nickname, name + 2, val + 1); + + return 0; +} +#endif /* WIRELESS_EXT > 8 */ + + +static int prism2_ioctl_siwfreq(struct net_device *dev, + struct iw_request_info *info, + struct iw_freq *freq, char *extra) +{ + local_info_t *local = (local_info_t *) dev->priv; + + /* freq => chan. */ + if (freq->e == 1 && + freq->m / 100000 >= freq_list[0] && + freq->m / 100000 <= freq_list[FREQ_COUNT - 1]) { + int ch; + int fr = freq->m / 100000; + for (ch = 0; ch < FREQ_COUNT; ch++) { + if (fr == freq_list[ch]) { + freq->e = 0; + freq->m = ch + 1; + break; + } + } + } + + if (freq->e != 0 || freq->m < 1 || freq->m > FREQ_COUNT || + !(local->channel_mask & (1 << (freq->m - 1)))) + return -EINVAL; + + local->channel = freq->m; /* channel is used in prism2_setup_rids() */ + if (hostap_set_word(dev, HFA384X_RID_CNFOWNCHANNEL, local->channel) || + local->func->reset_port(dev)) + return -EINVAL; + + return 0; +} + +static int prism2_ioctl_giwfreq(struct net_device *dev, + struct iw_request_info *info, + struct iw_freq *freq, char *extra) +{ + local_info_t *local = (local_info_t *) dev->priv; + u16 val; + + if (local->func->get_rid(dev, HFA384X_RID_CURRENTCHANNEL, &val, 2, 1) < + 0) + return -EINVAL; + + le16_to_cpus(&val); + if (val < 1 || val > FREQ_COUNT) + return -EINVAL; + + freq->m = freq_list[val - 1] * 100000; + freq->e = 1; + + return 0; +} + + +static void hostap_monitor_set_type(local_info_t *local) +{ + struct net_device *dev = local->dev; + + if (local->monitor_type == PRISM2_MONITOR_PRISM || + local->monitor_type == PRISM2_MONITOR_CAPHDR) { + dev->type = ARPHRD_IEEE80211_PRISM; + dev->hard_header_parse = + hostap_80211_prism_header_parse; + } else { + dev->type = ARPHRD_IEEE80211; + dev->hard_header_parse = hostap_80211_header_parse; + } +} + + +#if WIRELESS_EXT > 8 +static int prism2_ioctl_siwessid(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *data, char *ssid) +{ + local_info_t *local = (local_info_t *) dev->priv; + + if (data->flags == 0) + ssid[0] = '\0'; /* ANY */ + + if (local->iw_mode == IW_MODE_MASTER && ssid[0] == '\0') { + /* Setting SSID to empty string seems to kill the card in + * Host AP mode */ + printk(KERN_DEBUG "%s: Host AP mode does not support " + "'Any' essid\n", dev->name); + return -EINVAL; + } + + memcpy(local->essid, ssid, IW_ESSID_MAX_SIZE); + local->essid[MAX_SSID_LEN] = '\0'; + + if ((!local->fw_ap && + hostap_set_string(dev, HFA384X_RID_CNFDESIREDSSID, local->essid)) + || hostap_set_string(dev, HFA384X_RID_CNFOWNSSID, local->essid) || + local->func->reset_port(dev)) + return -EINVAL; + + return 0; +} + +static int prism2_ioctl_giwessid(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *data, char *essid) +{ + local_info_t *local = (local_info_t *) dev->priv; + u16 val; + + data->flags = 1; /* active */ + if (local->iw_mode == IW_MODE_MASTER) { + data->length = strlen(local->essid); + memcpy(essid, local->essid, IW_ESSID_MAX_SIZE); + } else { + int len; + char ssid[MAX_SSID_LEN + 2]; + memset(ssid, 0, sizeof(ssid)); + len = local->func->get_rid(dev, HFA384X_RID_CURRENTSSID, + &ssid, MAX_SSID_LEN + 2, 0); + val = __le16_to_cpu(*(u16 *) ssid); + if (len > MAX_SSID_LEN + 2 || len < 0 || val > MAX_SSID_LEN) { + return -EOPNOTSUPP; + } + data->length = val; + memcpy(essid, ssid + 2, IW_ESSID_MAX_SIZE); + } + + return 0; +} + + +static int prism2_ioctl_giwrange(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *data, char *extra) +{ + local_info_t *local = (local_info_t *) dev->priv; + struct iw_range *range = (struct iw_range *) extra; + u8 rates[10]; + u16 val; + int i, len, over2; + + data->length = sizeof(struct iw_range); + memset(range, 0, sizeof(struct iw_range)); + +#if WIRELESS_EXT > 9 + /* TODO: could fill num_txpower and txpower array with + * something; however, there are 128 different values.. */ + + range->txpower_capa = IW_TXPOW_DBM; + + if (local->iw_mode == IW_MODE_INFRA || local->iw_mode == IW_MODE_ADHOC) + { + range->min_pmp = 1 * 1024; + range->max_pmp = 65535 * 1024; + range->min_pmt = 1 * 1024; + range->max_pmt = 1000 * 1024; + range->pmp_flags = IW_POWER_PERIOD; + range->pmt_flags = IW_POWER_TIMEOUT; + range->pm_capa = IW_POWER_PERIOD | IW_POWER_TIMEOUT | + IW_POWER_UNICAST_R | IW_POWER_ALL_R; + } +#endif /* WIRELESS_EXT > 9 */ + +#if WIRELESS_EXT > 10 + range->we_version_compiled = WIRELESS_EXT; + range->we_version_source = 13; + + range->retry_capa = IW_RETRY_LIMIT; + range->retry_flags = IW_RETRY_LIMIT; + range->min_retry = 0; + range->max_retry = 255; +#endif /* WIRELESS_EXT > 10 */ + + range->num_channels = FREQ_COUNT; + + val = 0; + for (i = 0; i < FREQ_COUNT; i++) { + if (local->channel_mask & (1 << i)) { + range->freq[val].i = i + 1; + range->freq[val].m = freq_list[i] * 100000; + range->freq[val].e = 1; + val++; + } + if (val == IW_MAX_FREQUENCIES) + break; + } + range->num_frequency = val; + + range->max_qual.qual = 92; /* 0 .. 92 */ + range->max_qual.level = 154; /* 27 .. 154 */ + range->max_qual.noise = 154; /* 27 .. 154 */ + range->sensitivity = 3; + + range->max_encoding_tokens = WEP_KEYS; + range->num_encoding_sizes = 2; + range->encoding_size[0] = 5; + range->encoding_size[1] = 13; + + over2 = 0; + len = prism2_get_datarates(dev, rates); + range->num_bitrates = 0; + for (i = 0; i < len; i++) { + if (range->num_bitrates < IW_MAX_BITRATES) { + range->bitrate[range->num_bitrates] = + rates[i] * 500000; + range->num_bitrates++; + } + if (rates[i] == 0x0b || rates[i] == 0x16) + over2 = 1; + } + /* estimated maximum TCP throughput values (bps) */ + range->throughput = over2 ? 5500000 : 1500000; + + range->min_rts = 0; + range->max_rts = 2347; + range->min_frag = 256; + range->max_frag = 2346; + + return 0; +} + + +static int hostap_monitor_mode_enable(local_info_t *local) +{ + struct net_device *dev = local->dev; + + printk(KERN_DEBUG "Enabling monitor mode\n"); + hostap_monitor_set_type(local); + + if (hostap_set_word(dev, HFA384X_RID_CNFPORTTYPE, + HFA384X_PORTTYPE_PSEUDO_IBSS)) { + printk(KERN_DEBUG "Port type setting for monitor mode " + "failed\n"); + return -EOPNOTSUPP; + } + + /* Host decrypt is needed to get the IV and ICV fields; + * however, monitor mode seems to remove WEP flag from frame + * control field */ + if (hostap_set_word(dev, HFA384X_RID_CNFWEPFLAGS, + HFA384X_WEPFLAGS_HOSTENCRYPT | + HFA384X_WEPFLAGS_HOSTDECRYPT)) { + printk(KERN_DEBUG "WEP flags setting failed\n"); + return -EOPNOTSUPP; + } + + if (local->func->reset_port(dev) || + local->func->cmd(dev, HFA384X_CMDCODE_TEST | + (HFA384X_TEST_MONITOR << 8), + 0, NULL, NULL)) { + printk(KERN_DEBUG "Setting monitor mode failed\n"); + return -EOPNOTSUPP; + } + + return 0; +} + + +static int hostap_monitor_mode_disable(local_info_t *local) +{ + struct net_device *dev = local->dev; + + printk(KERN_DEBUG "%s: Disabling monitor mode\n", dev->name); + dev->type = ARPHRD_ETHER; + dev->hard_header_parse = local->saved_eth_header_parse; + if (local->func->cmd(dev, HFA384X_CMDCODE_TEST | + (HFA384X_TEST_STOP << 8), + 0, NULL, NULL)) + return -1; + return hostap_set_encryption(local); +} + + +static int prism2_ioctl_siwmode(struct net_device *dev, + struct iw_request_info *info, + __u32 *mode, char *extra) +{ + local_info_t *local = (local_info_t *) dev->priv; + int double_reset = 0; + + if (*mode != IW_MODE_ADHOC && *mode != IW_MODE_INFRA && + *mode != IW_MODE_MASTER && *mode != IW_MODE_REPEAT && + *mode != IW_MODE_MONITOR) + return -EOPNOTSUPP; + +#ifdef PRISM2_NO_STATION_MODES + if (*mode == IW_MODE_ADHOC || *mode == IW_MODE_INFRA) + return -EOPNOTSUPP; +#endif /* PRISM2_NO_STATION_MODES */ + + if (*mode == local->iw_mode) + return 0; + + if (*mode == IW_MODE_MASTER && local->essid[0] == '\0') { + printk(KERN_WARNING "%s: empty SSID not allowed in Master " + "mode\n", dev->name); + return -EINVAL; + } + + if (local->iw_mode == IW_MODE_MONITOR) + hostap_monitor_mode_disable(local); + + if (local->iw_mode == IW_MODE_ADHOC && *mode == IW_MODE_MASTER) { + /* There seems to be a firmware bug in at least STA f/w v1.5.6 + * that leaves beacon frames to use IBSS type when moving from + * IBSS to Host AP mode. Doing double Port0 reset seems to be + * enough to workaround this. */ + double_reset = 1; + } + + printk(KERN_DEBUG "prism2: %s: operating mode changed " + "%d -> %d\n", dev->name, local->iw_mode, *mode); + local->iw_mode = *mode; + + if (local->iw_mode == IW_MODE_MONITOR) + hostap_monitor_mode_enable(local); + else if (local->iw_mode == IW_MODE_MASTER && !local->host_encrypt && + !local->fw_encrypt_ok) { + printk(KERN_DEBUG "%s: defaulting to host-based encryption as " + "a workaround for firmware bug in Host AP mode WEP\n", + dev->name); + local->host_encrypt = 1; + } + + if (hostap_set_word(dev, HFA384X_RID_CNFPORTTYPE, + hostap_get_porttype(local))) + return -EOPNOTSUPP; + + if (local->func->reset_port(dev)) + return -EINVAL; + if (double_reset && local->func->reset_port(dev)) + return -EINVAL; + + return 0; +} + + +static int prism2_ioctl_giwmode(struct net_device *dev, + struct iw_request_info *info, + __u32 *mode, char *extra) +{ + local_info_t *local = (local_info_t *) dev->priv; + + if (dev == local->stadev) { + *mode = IW_MODE_INFRA; + return 0; + } + + *mode = local->iw_mode; + return 0; +} + + +static int prism2_ioctl_siwpower(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *wrq, char *extra) +{ +#ifdef PRISM2_NO_STATION_MODES + return -EOPNOTSUPP; +#else /* PRISM2_NO_STATION_MODES */ + int ret = 0; + + if (wrq->disabled) + return hostap_set_word(dev, HFA384X_RID_CNFPMENABLED, 0); + + switch (wrq->flags & IW_POWER_MODE) { + case IW_POWER_UNICAST_R: + ret = hostap_set_word(dev, HFA384X_RID_CNFMULTICASTRECEIVE, 0); + if (ret) + return ret; + ret = hostap_set_word(dev, HFA384X_RID_CNFPMENABLED, 1); + if (ret) + return ret; + break; + case IW_POWER_ALL_R: + ret = hostap_set_word(dev, HFA384X_RID_CNFMULTICASTRECEIVE, 1); + if (ret) + return ret; + ret = hostap_set_word(dev, HFA384X_RID_CNFPMENABLED, 1); + if (ret) + return ret; + break; + case IW_POWER_ON: + break; + default: + return -EINVAL; + } + + if (wrq->flags & IW_POWER_TIMEOUT) { + ret = hostap_set_word(dev, HFA384X_RID_CNFPMENABLED, 1); + if (ret) + return ret; + ret = hostap_set_word(dev, HFA384X_RID_CNFPMHOLDOVERDURATION, + wrq->value / 1024); + if (ret) + return ret; + } + if (wrq->flags & IW_POWER_PERIOD) { + ret = hostap_set_word(dev, HFA384X_RID_CNFPMENABLED, 1); + if (ret) + return ret; + ret = hostap_set_word(dev, HFA384X_RID_CNFMAXSLEEPDURATION, + wrq->value / 1024); + if (ret) + return ret; + } + + return ret; +#endif /* PRISM2_NO_STATION_MODES */ +} + + +static int prism2_ioctl_giwpower(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *rrq, char *extra) +{ +#ifdef PRISM2_NO_STATION_MODES + return -EOPNOTSUPP; +#else /* PRISM2_NO_STATION_MODES */ + local_info_t *local = (local_info_t *) dev->priv; + u16 enable, mcast; + + if (local->func->get_rid(dev, HFA384X_RID_CNFPMENABLED, &enable, 2, 1) + < 0) + return -EINVAL; + + if (!__le16_to_cpu(enable)) { + rrq->disabled = 1; + return 0; + } + + rrq->disabled = 0; + + if ((rrq->flags & IW_POWER_TYPE) == IW_POWER_TIMEOUT) { + u16 timeout; + if (local->func->get_rid(dev, + HFA384X_RID_CNFPMHOLDOVERDURATION, + &timeout, 2, 1) < 0) + return -EINVAL; + + rrq->flags = IW_POWER_TIMEOUT; + rrq->value = __le16_to_cpu(timeout) * 1024; + } else { + u16 period; + if (local->func->get_rid(dev, HFA384X_RID_CNFMAXSLEEPDURATION, + &period, 2, 1) < 0) + return -EINVAL; + + rrq->flags = IW_POWER_PERIOD; + rrq->value = __le16_to_cpu(period) * 1024; + } + + if (local->func->get_rid(dev, HFA384X_RID_CNFMULTICASTRECEIVE, &mcast, + 2, 1) < 0) + return -EINVAL; + + if (__le16_to_cpu(mcast)) + rrq->flags |= IW_POWER_ALL_R; + else + rrq->flags |= IW_POWER_UNICAST_R; + + return 0; +#endif /* PRISM2_NO_STATION_MODES */ +} +#endif /* WIRELESS_EXT > 8 */ + + +#if WIRELESS_EXT > 10 +static int prism2_ioctl_siwretry(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *rrq, char *extra) +{ + local_info_t *local = (local_info_t *) dev->priv; + + if (rrq->disabled) + return -EINVAL; + + /* setting retry limits is not supported with the current station + * firmware code; simulate this with alternative retry count for now */ + if (rrq->flags == IW_RETRY_LIMIT) { + if (rrq->value < 0) { + /* disable manual retry count setting and use firmware + * defaults */ + local->manual_retry_count = -1; + local->tx_control &= ~HFA384X_TX_CTRL_ALT_RTRY; + } else { + if (hostap_set_word(dev, HFA384X_RID_CNFALTRETRYCOUNT, + rrq->value)) { + printk(KERN_DEBUG "%s: Alternate retry count " + "setting to %d failed\n", + dev->name, rrq->value); + return -EOPNOTSUPP; + } + + local->manual_retry_count = rrq->value; + local->tx_control |= HFA384X_TX_CTRL_ALT_RTRY; + } + return 0; + } + + return -EOPNOTSUPP; + +#if 0 + /* what could be done, if firmware would support this.. */ + + if (rrq->flags & IW_RETRY_LIMIT) { + if (rrq->flags & IW_RETRY_MAX) + HFA384X_RID_LONGRETRYLIMIT = rrq->value; + else if (rrq->flags & IW_RETRY_MIN) + HFA384X_RID_SHORTRETRYLIMIT = rrq->value; + else { + HFA384X_RID_LONGRETRYLIMIT = rrq->value; + HFA384X_RID_SHORTRETRYLIMIT = rrq->value; + } + + } + + if (rrq->flags & IW_RETRY_LIFETIME) { + HFA384X_RID_MAXTRANSMITLIFETIME = rrq->value / 1024; + } + + return 0; +#endif /* 0 */ +} + +static int prism2_ioctl_giwretry(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *rrq, char *extra) +{ + local_info_t *local = (local_info_t *) dev->priv; + u16 shortretry, longretry, lifetime; + + if (local->func->get_rid(dev, HFA384X_RID_SHORTRETRYLIMIT, &shortretry, + 2, 1) < 0 || + local->func->get_rid(dev, HFA384X_RID_LONGRETRYLIMIT, &longretry, + 2, 1) < 0 || + local->func->get_rid(dev, HFA384X_RID_MAXTRANSMITLIFETIME, + &lifetime, 2, 1) < 0) + return -EINVAL; + + le16_to_cpus(&shortretry); + le16_to_cpus(&longretry); + le16_to_cpus(&lifetime); + + rrq->disabled = 0; + + if ((rrq->flags & IW_RETRY_TYPE) == IW_RETRY_LIFETIME) { + rrq->flags = IW_RETRY_LIFETIME; + rrq->value = lifetime * 1024; + } else { + if (local->manual_retry_count >= 0) { + rrq->flags = IW_RETRY_LIMIT; + rrq->value = local->manual_retry_count; + } else if ((rrq->flags & IW_RETRY_MAX)) { + rrq->flags = IW_RETRY_LIMIT | IW_RETRY_MAX; + rrq->value = longretry; + } else { + rrq->flags = IW_RETRY_LIMIT; + rrq->value = shortretry; + if (shortretry != longretry) + rrq->flags |= IW_RETRY_MIN; + } + } + return 0; +} +#endif /* WIRELESS_EXT > 10 */ + + +#if WIRELESS_EXT > 9 +/* Map HFA386x's CR31 to and from dBm with some sort of ad hoc mapping.. + * This version assumes following mapping: + * CR31 is 7-bit value with -64 to +63 range. + * -64 is mapped into +20dBm and +63 into -43dBm. + * This is certainly not an exact mapping for every card, but at least + * increasing dBm value should correspond to increasing TX power. + */ + +static int prism2_txpower_hfa386x_to_dBm(u16 val) +{ + signed char tmp; + + if (val > 255) + val = 255; + + tmp = val; + tmp >>= 2; + + return -12 - tmp; +} + +static u16 prism2_txpower_dBm_to_hfa386x(int val) +{ + signed char tmp; + + if (val > 20) + return 128; + else if (val < -43) + return 127; + + tmp = val; + tmp = -12 - tmp; + tmp <<= 2; + + return (unsigned char) tmp; +} + + +static int prism2_ioctl_siwtxpow(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *rrq, char *extra) +{ + local_info_t *local = (local_info_t *) dev->priv; + char *tmp; + u16 val; + int ret = 0; + + if (rrq->disabled) { + if (local->txpower_type != PRISM2_TXPOWER_OFF) { + val = 0xff; /* use all standby and sleep modes */ + ret = local->func->cmd(dev, HFA384X_CMDCODE_WRITEMIF, + HFA386X_CR_A_D_TEST_MODES2, + &val, NULL); + printk(KERN_DEBUG "%s: Turning radio off: %s\n", + dev->name, ret ? "failed" : "OK"); + local->txpower_type = PRISM2_TXPOWER_OFF; + } + return (ret ? -EOPNOTSUPP : 0); + } + + if (local->txpower_type == PRISM2_TXPOWER_OFF) { + val = 0; /* disable all standby and sleep modes */ + ret = local->func->cmd(dev, HFA384X_CMDCODE_WRITEMIF, + HFA386X_CR_A_D_TEST_MODES2, &val, NULL); + printk(KERN_DEBUG "%s: Turning radio on: %s\n", + dev->name, ret ? "failed" : "OK"); + local->txpower_type = PRISM2_TXPOWER_UNKNOWN; + } + + if (!rrq->fixed && local->txpower_type != PRISM2_TXPOWER_AUTO) { + printk(KERN_DEBUG "Setting ALC on\n"); + val = HFA384X_TEST_CFG_BIT_ALC; + local->func->cmd(dev, HFA384X_CMDCODE_TEST | + (HFA384X_TEST_CFG_BITS << 8), 1, &val, NULL); + local->txpower_type = PRISM2_TXPOWER_AUTO; + return 0; + } + + if (local->txpower_type != PRISM2_TXPOWER_FIXED) { + printk(KERN_DEBUG "Setting ALC off\n"); + val = HFA384X_TEST_CFG_BIT_ALC; + local->func->cmd(dev, HFA384X_CMDCODE_TEST | + (HFA384X_TEST_CFG_BITS << 8), 0, &val, NULL); + local->txpower_type = PRISM2_TXPOWER_FIXED; + } + + if (rrq->flags == IW_TXPOW_DBM) + tmp = "dBm"; + else if (rrq->flags == IW_TXPOW_MWATT) + tmp = "mW"; + else + tmp = "UNKNOWN"; + printk(KERN_DEBUG "Setting TX power to %d %s\n", rrq->value, tmp); + + if (rrq->flags != IW_TXPOW_DBM) { + printk("SIOCSIWTXPOW with mW is not supported; use dBm\n"); + return -EOPNOTSUPP; + } + + local->txpower = rrq->value; + val = prism2_txpower_dBm_to_hfa386x(local->txpower); + if (local->func->cmd(dev, HFA384X_CMDCODE_WRITEMIF, + HFA386X_CR_MANUAL_TX_POWER, &val, NULL)) + ret = -EOPNOTSUPP; + + return ret; +} + +static int prism2_ioctl_giwtxpow(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *rrq, char *extra) +{ + local_info_t *local = (local_info_t *) dev->priv; + u16 resp0; + + rrq->flags = IW_TXPOW_DBM; + rrq->disabled = 0; + rrq->fixed = 0; + + if (local->txpower_type == PRISM2_TXPOWER_AUTO) { + if (local->func->cmd(dev, HFA384X_CMDCODE_READMIF, + HFA386X_CR_MANUAL_TX_POWER, + NULL, &resp0) == 0) { + rrq->value = prism2_txpower_hfa386x_to_dBm(resp0); + } else { + /* Could not get real txpower; guess 15 dBm */ + rrq->value = 15; + } + } else if (local->txpower_type == PRISM2_TXPOWER_OFF) { + rrq->value = 0; + rrq->disabled = 1; + } else if (local->txpower_type == PRISM2_TXPOWER_FIXED) { + rrq->value = local->txpower; + rrq->fixed = 1; + } else { + printk("SIOCGIWTXPOW - unknown txpower_type=%d\n", + local->txpower_type); + } + return 0; +} +#endif /* WIRELESS_EXT > 9 */ + + +#if WIRELESS_EXT > 13 +static int prism2_ioctl_siwscan(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *data, char *extra) +{ + local_info_t *local = (local_info_t *) dev->priv; +#ifndef PRISM2_NO_STATION_MODES + struct hfa384x_scan_request scan_req; + int ret = 0; +#endif /* !PRISM2_NO_STATION_MODES */ + + if (local->iw_mode == IW_MODE_MASTER) { + /* In master mode, we just return the results of our local + * tables, so we don't need to start anything... + * Jean II */ + data->length = 0; + return 0; + } + +#ifdef PRISM2_NO_STATION_MODES + return -EOPNOTSUPP; +#else /* PRISM2_NO_STATION_MODES */ + + memset(&scan_req, 0, sizeof(scan_req)); + scan_req.channel_list = __constant_cpu_to_le16(0x3fff); + scan_req.txrate = __constant_cpu_to_le16(HFA384X_RATES_1MBPS); + + /* FIX: + * It seems to be enough to set roaming mode for a short moment to + * host-based and then setup scanrequest data and return the mode to + * firmware-based. + * + * Master mode would need to drop to Managed mode for a short while + * to make scanning work.. Or sweep through the different channels and + * use passive scan based on beacons. */ + + if (!local->host_roaming) + hostap_set_word(dev, HFA384X_RID_CNFROAMINGMODE, + HFA384X_ROAMING_HOST); + + if (local->func->set_rid(dev, HFA384X_RID_SCANREQUEST, &scan_req, + sizeof(scan_req))) { + printk(KERN_DEBUG "SCANREQUEST failed\n"); + ret = -EINVAL; + } + + if (!local->host_roaming) + hostap_set_word(dev, HFA384X_RID_CNFROAMINGMODE, + HFA384X_ROAMING_FIRMWARE); + + local->scan_timestamp = jiffies; + +#if 0 + /* NOTE! longer hostscan_request struct! Additional ssid field (empty + * means any). HostScan would be better than "old scan" since it keeps + * old association status. However, it requires newer station firmware + * (1.3.x?). */ + if (local->func->set_rid(dev, HFA384X_RID_HOSTSCAN, &scan_req, + sizeof(scan_req))) { + printk(KERN_DEBUG "HOSTSCAN failed\n"); + return -EINVAL; + } +#endif + + /* inquire F101, F103 or wait for SIOCGIWSCAN and read RID */ + + return ret; +#endif /* PRISM2_NO_STATION_MODES */ +} + + +#ifndef PRISM2_NO_STATION_MODES +/* Translate scan data returned from the card to a card independant + * format that the Wireless Tools will understand - Jean II */ +static inline int prism2_translate_scan(struct net_device *dev, char *buffer, + char *scan, int scan_len) +{ + struct hfa384x_scan_result *atom; + int left, i; + struct iw_event iwe; + char *current_ev = buffer; + char *end_buf = buffer + IW_SCAN_MAX_DATA; + char *current_val; + + left = scan_len; + + if (left % sizeof(*atom) != 0) { + printk(KERN_DEBUG "%s: invalid total scan result length %d " + "(entry length %d)\n", + dev->name, left, sizeof(*atom)); + return -EINVAL; + } + + atom = (struct hfa384x_scan_result *) scan; + while (left > 0) { + u16 capabilities; + + /* 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, atom->bssid, ETH_ALEN); + /* FIX: + * I do not know how this is possible, but iwe_stream_add_event + * seems to re-order memcpy execution so that len is set only + * after copying.. Pre-setting len here "fixes" this, but real + * problems should be solved (after which these iwe.len + * settings could be removed from this function). */ + iwe.len = IW_EV_ADDR_LEN; + current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, + IW_EV_ADDR_LEN); + + /* Other entries will be displayed in the order we give them */ + + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = SIOCGIWESSID; + iwe.u.data.length = le16_to_cpu(atom->ssid_len); + if (iwe.u.data.length > 32) + iwe.u.data.length = 32; + iwe.u.data.flags = 1; + iwe.len = IW_EV_POINT_LEN + iwe.u.data.length; + current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, + atom->ssid); + + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = SIOCGIWMODE; + capabilities = le16_to_cpu(atom->capability); + if (capabilities & (WLAN_CAPABILITY_ESS | + WLAN_CAPABILITY_IBSS)) { + if (capabilities & WLAN_CAPABILITY_ESS) + iwe.u.mode = IW_MODE_MASTER; + else + iwe.u.mode = IW_MODE_ADHOC; + iwe.len = IW_EV_UINT_LEN; + current_ev = iwe_stream_add_event(current_ev, end_buf, + &iwe, + IW_EV_UINT_LEN); + } + + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = SIOCGIWFREQ; + iwe.u.freq.m = freq_list[le16_to_cpu(atom->chid) - 1] * 100000; + iwe.u.freq.e = 1; + iwe.len = IW_EV_FREQ_LEN; + current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, + IW_EV_FREQ_LEN); + + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = IWEVQUAL; + iwe.u.qual.level = HFA384X_LEVEL_TO_dBm(le16_to_cpu(atom->sl)); + iwe.u.qual.noise = + HFA384X_LEVEL_TO_dBm(le16_to_cpu(atom->anl)); + iwe.len = IW_EV_QUAL_LEN; + current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, + IW_EV_QUAL_LEN); + + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = SIOCGIWENCODE; + if (capabilities & WLAN_CAPABILITY_PRIVACY) + iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY; + else + iwe.u.data.flags = IW_ENCODE_DISABLED; + iwe.u.data.length = 0; + iwe.len = IW_EV_POINT_LEN + iwe.u.data.length; + current_ev = iwe_stream_add_point( + current_ev, end_buf, &iwe, + atom->ssid /* memcpy 0 bytes */); + + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = SIOCGIWRATE; + current_val = current_ev + IW_EV_LCP_LEN; + for (i = 0; i < sizeof(atom->sup_rates); i++) { + if (atom->sup_rates[i] == 0) + break; + /* Bit rate given in 500 kb/s units (+ 0x80) */ + iwe.u.bitrate.value = + ((atom->sup_rates[i] & 0x7f) * 500000); + current_val = iwe_stream_add_value( + current_ev, current_val, end_buf, &iwe, + IW_EV_PARAM_LEN); + } + /* Check if we added any event */ + if ((current_val - current_ev) > IW_EV_LCP_LEN) + current_ev = current_val; + + /* Could add beacon_interval and rate (of the received + * ProbeResp) to scan results. */ + + atom++; + left -= sizeof(*atom); + } + +#if 0 + { + u8 *pos = buffer; + left = (current_ev - buffer); + printk(KERN_DEBUG "IW SCAN (len=%d):", left); + while (left > 0) { + printk(" %02x", *pos++); + left--; + } + printk("\n"); + } +#endif + + return current_ev - buffer; +} +#endif /* PRISM2_NO_STATION_MODES */ + + +static inline int prism2_ioctl_giwscan_sta(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *data, char *extra) +{ +#ifdef PRISM2_NO_STATION_MODES + return -EOPNOTSUPP; +#else /* PRISM2_NO_STATION_MODES */ + local_info_t *local = (local_info_t *) dev->priv; + int res, maxlen, len, free_buffer = 1, offset; + char *buffer; + + /* Wait until the scan is finished. We can probably do better + * than that - Jean II */ + if (local->scan_timestamp && + time_before(jiffies, local->scan_timestamp + 3 * HZ)) { + /* Important note : we don't want to block the caller + * until results are ready for various reasons. + * First, managing wait queues is complex and racy + * (there may be multiple simultaneous callers). + * Second, we grab some rtnetlink lock before comming + * here (in dev_ioctl()). + * Third, the caller can wait on the Wireless Event + * - Jean II */ + return -EAGAIN; + } + local->scan_timestamp = 0; + + /* Maximum number of scan results on Prism2 is 32. This reserves + * enough buffer for all results. Other option would be to modify + * local->func->get_rid() to be able to allocate just the correct + * amount of memory based on RID header. */ + maxlen = sizeof(struct hfa384x_scan_result_hdr) + + sizeof(struct hfa384x_scan_result) * HFA384X_SCAN_MAX_RESULTS; + buffer = kmalloc(maxlen, GFP_KERNEL); + if (buffer == NULL) + return -ENOMEM; + + len = local->func->get_rid(dev, HFA384X_RID_SCANRESULTSTABLE, + buffer, maxlen, 0); + offset = sizeof(struct hfa384x_scan_result_hdr); + PDEBUG(DEBUG_EXTRA, "Scan len = %d (entry=%d)\n", + len, sizeof(struct hfa384x_scan_result)); + if (len < 0 || len < sizeof(struct hfa384x_scan_result_hdr)) { + kfree(buffer); + if (local->last_scan_results) { + PDEBUG(DEBUG_EXTRA, "Using last received ScanResults " + "info frame instead of RID\n"); + offset = 0; + len = local->last_scan_results_count * + sizeof(struct hfa384x_scan_result); + free_buffer = 0; + buffer = (char *) local->last_scan_results; + } else + return -EINVAL; + } + + /* Translate to WE format */ + res = prism2_translate_scan(dev, extra, buffer + offset, + len - offset); + if (free_buffer) + kfree(buffer); + + if (res >= 0) { + printk(KERN_DEBUG "Scan result translation succeeded " + "(length=%d)\n", res); + data->length = res; + return 0; + } else { + printk(KERN_DEBUG "Scan result translation failed (res=%d)\n", + res); + data->length = 0; + return res; + } +#endif /* PRISM2_NO_STATION_MODES */ +} + + +static int prism2_ioctl_giwscan(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *data, char *extra) +{ + local_info_t *local = (local_info_t *) dev->priv; + int res; + + if (local->iw_mode == IW_MODE_MASTER) { + /* In MASTER mode, it doesn't make sense to go around + * scanning the frequencies and make the stations we serve + * wait when what the user is really interested about is the + * list of stations and access points we are talking to. + * So, just extract results from our cache... + * Jean II */ + + /* Translate to WE format */ + res = prism2_ap_translate_scan(dev, extra); + if (res >= 0) { + printk(KERN_DEBUG "Scan result translation succeeded " + "(length=%d)\n", res); + data->length = res; + return 0; + } else { + printk(KERN_DEBUG + "Scan result translation failed (res=%d)\n", + res); + data->length = 0; + return res; + } + } else { + /* Station mode */ + return prism2_ioctl_giwscan_sta(dev, info, data, extra); + } +} +#endif /* WIRELESS_EXT > 13 */ + + +#if WIRELESS_EXT > 8 +static const struct iw_priv_args prism2_priv[] = { + { PRISM2_IOCTL_MONITOR, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "monitor" }, + { PRISM2_IOCTL_READMIF, + IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, + IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, "readmif" }, + { PRISM2_IOCTL_WRITEMIF, + IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 2, 0, "writemif" }, + { PRISM2_IOCTL_RESET, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "reset" }, + { PRISM2_IOCTL_INQUIRE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "inquire" }, + { PRISM2_IOCTL_SET_RID_WORD, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2, 0, "set_rid_word" }, + { PRISM2_IOCTL_MACCMD, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "maccmd" }, +#ifdef PRISM2_USE_WE_TYPE_ADDR + { PRISM2_IOCTL_WDS_ADD, + IW_PRIV_TYPE_ADDR | IW_PRIV_SIZE_FIXED | 1, 0, "wds_add" }, + { PRISM2_IOCTL_WDS_DEL, + IW_PRIV_TYPE_ADDR | IW_PRIV_SIZE_FIXED | 1, 0, "wds_del" }, + { PRISM2_IOCTL_ADDMAC, + IW_PRIV_TYPE_ADDR | IW_PRIV_SIZE_FIXED | 1, 0, "addmac" }, + { PRISM2_IOCTL_DELMAC, + IW_PRIV_TYPE_ADDR | IW_PRIV_SIZE_FIXED | 1, 0, "delmac" }, + { PRISM2_IOCTL_KICKMAC, + IW_PRIV_TYPE_ADDR | IW_PRIV_SIZE_FIXED | 1, 0, "kickmac" }, +#else /* PRISM2_USE_WE_TYPE_ADDR */ + { PRISM2_IOCTL_WDS_ADD, + IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | 18, 0, "wds_add" }, + { PRISM2_IOCTL_WDS_DEL, + IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | 18, 0, "wds_del" }, + { PRISM2_IOCTL_ADDMAC, + IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | 18, 0, "addmac" }, + { PRISM2_IOCTL_DELMAC, + IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | 18, 0, "delmac" }, + { PRISM2_IOCTL_KICKMAC, + IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | 18, 0, "kickmac" }, +#endif /* PRISM2_USE_WE_TYPE_ADDR */ + /* --- raw access to sub-ioctls --- */ + { PRISM2_IOCTL_PRISM2_PARAM, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2, 0, "prism2_param" }, +#if WIRELESS_EXT >= 12 + { PRISM2_IOCTL_GET_PRISM2_PARAM, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getprism2_param" }, +#ifdef PRISM2_USE_WE_SUB_IOCTLS + /* --- sub-ioctls handlers --- */ + { PRISM2_IOCTL_PRISM2_PARAM, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "" }, + { PRISM2_IOCTL_GET_PRISM2_PARAM, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "" }, + /* --- sub-ioctls definitions --- */ + { PRISM2_PARAM_PTYPE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "ptype" }, + { PRISM2_PARAM_PTYPE, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getptype" }, + { PRISM2_PARAM_TXRATECTRL, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "txratectrl" }, + { PRISM2_PARAM_TXRATECTRL, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "gettxratectrl" }, + { PRISM2_PARAM_BEACON_INT, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "beacon_int" }, + { PRISM2_PARAM_BEACON_INT, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getbeacon_int" }, +#ifndef PRISM2_NO_STATION_MODES + { PRISM2_PARAM_PSEUDO_IBSS, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "pseudo_ibss" }, + { PRISM2_PARAM_PSEUDO_IBSS, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getpseudo_ibss" }, +#endif /* PRISM2_NO_STATION_MODES */ + { PRISM2_PARAM_ALC, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "alc" }, + { PRISM2_PARAM_ALC, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getalc" }, + { PRISM2_PARAM_TXPOWER, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "txpower" }, + { PRISM2_PARAM_TXPOWER, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getxpower" }, + { PRISM2_PARAM_DUMP, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "dump" }, + { PRISM2_PARAM_DUMP, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getdump" }, + { PRISM2_PARAM_OTHER_AP_POLICY, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "other_ap_policy" }, + { PRISM2_PARAM_OTHER_AP_POLICY, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getother_ap_pol" }, + { PRISM2_PARAM_AP_MAX_INACTIVITY, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "max_inactivity" }, + { PRISM2_PARAM_AP_MAX_INACTIVITY, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getmax_inactivi" }, + { PRISM2_PARAM_AP_BRIDGE_PACKETS, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "bridge_packets" }, + { PRISM2_PARAM_AP_BRIDGE_PACKETS, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getbridge_packe" }, + { PRISM2_PARAM_DTIM_PERIOD, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "dtim_period" }, + { PRISM2_PARAM_DTIM_PERIOD, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getdtim_period" }, + { PRISM2_PARAM_AP_NULLFUNC_ACK, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "nullfunc_ack" }, + { PRISM2_PARAM_AP_NULLFUNC_ACK, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getnullfunc_ack" }, + { PRISM2_PARAM_MAX_WDS, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "max_wds" }, + { PRISM2_PARAM_MAX_WDS, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getmax_wds" }, + { PRISM2_PARAM_AP_AUTOM_AP_WDS, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "autom_ap_wds" }, + { PRISM2_PARAM_AP_AUTOM_AP_WDS, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getautom_ap_wds" }, + { PRISM2_PARAM_AP_AUTH_ALGS, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "ap_auth_algs" }, + { PRISM2_PARAM_AP_AUTH_ALGS, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getap_auth_algs" }, + { PRISM2_PARAM_MONITOR_ALLOW_FCSERR, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "allow_fcserr" }, + { PRISM2_PARAM_MONITOR_ALLOW_FCSERR, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getallow_fcserr" }, + { PRISM2_PARAM_HOST_ENCRYPT, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "host_encrypt" }, + { PRISM2_PARAM_HOST_ENCRYPT, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "gethost_encrypt" }, + { PRISM2_PARAM_HOST_DECRYPT, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "host_decrypt" }, + { PRISM2_PARAM_HOST_DECRYPT, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "gethost_decrypt" }, + { PRISM2_PARAM_BUS_MASTER_THRESHOLD_RX, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "busmaster_rx" }, + { PRISM2_PARAM_BUS_MASTER_THRESHOLD_RX, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getbusmaster_rx" }, + { PRISM2_PARAM_BUS_MASTER_THRESHOLD_TX, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "busmaster_tx" }, + { PRISM2_PARAM_BUS_MASTER_THRESHOLD_TX, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getbusmaster_tx" }, +#ifndef PRISM2_NO_STATION_MODES + { PRISM2_PARAM_HOST_ROAMING, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "host_roaming" }, + { PRISM2_PARAM_HOST_ROAMING, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "gethost_roaming" }, +#endif /* PRISM2_NO_STATION_MODES */ + { PRISM2_PARAM_BCRX_STA_KEY, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "bcrx_sta_key" }, + { PRISM2_PARAM_BCRX_STA_KEY, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getbcrx_sta_key" }, + { PRISM2_PARAM_IEEE_802_1X, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "ieee_802_1x" }, + { PRISM2_PARAM_IEEE_802_1X, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getieee_802_1x" }, + { PRISM2_PARAM_ANTSEL_TX, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "antsel_tx" }, + { PRISM2_PARAM_ANTSEL_TX, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getantsel_tx" }, + { PRISM2_PARAM_ANTSEL_RX, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "antsel_rx" }, + { PRISM2_PARAM_ANTSEL_RX, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getantsel_rx" }, + { PRISM2_PARAM_MONITOR_TYPE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "monitor_type" }, + { PRISM2_PARAM_MONITOR_TYPE, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getmonitor_type" }, + { PRISM2_PARAM_WDS_TYPE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "wds_type" }, + { PRISM2_PARAM_WDS_TYPE, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getwds_type" }, + { PRISM2_PARAM_HOSTSCAN, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "hostscan" }, + { PRISM2_PARAM_HOSTSCAN, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "gethostscan" }, + { PRISM2_PARAM_AP_SCAN, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "ap_scan" }, + { PRISM2_PARAM_AP_SCAN, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getap_scan" }, + { PRISM2_PARAM_ENH_SEC, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "enh_sec" }, + { PRISM2_PARAM_ENH_SEC, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getenh_sec" }, +#ifdef PRISM2_IO_DEBUG + { PRISM2_PARAM_IO_DEBUG, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "io_debug" }, + { PRISM2_PARAM_IO_DEBUG, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getio_debug" }, +#endif /* PRISM2_IO_DEBUG */ + { PRISM2_PARAM_BASIC_RATES, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "basic_rates" }, + { PRISM2_PARAM_BASIC_RATES, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getbasic_rates" }, + { PRISM2_PARAM_OPER_RATES, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "oper_rates" }, + { PRISM2_PARAM_OPER_RATES, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getoper_rates" }, + { PRISM2_PARAM_HOSTAPD, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "hostapd" }, + { PRISM2_PARAM_HOSTAPD, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "gethostapd" }, +#endif /* PRISM2_USE_WE_SUB_IOCTLS */ +#endif /* WIRELESS_EXT >= 12 */ +}; + + +#if WIRELESS_EXT <= 12 +static int prism2_ioctl_giwpriv(struct net_device *dev, struct iw_point *data) +{ + + if (!data->pointer || + verify_area(VERIFY_WRITE, data->pointer, sizeof(prism2_priv))) + return -EINVAL; + + data->length = sizeof(prism2_priv) / sizeof(prism2_priv[0]); + if (copy_to_user(data->pointer, prism2_priv, sizeof(prism2_priv))) + return -EINVAL; + return 0; +} +#endif /* WIRELESS_EXT <= 12 */ +#endif /* WIRELESS_EXT > 8 */ +#endif /* WIRELESS_EXT */ + + +static int prism2_ioctl_priv_inquire(struct net_device *dev, int *i) +{ + local_info_t *local = (local_info_t *) dev->priv; + + if (local->func->cmd(dev, HFA384X_CMDCODE_INQUIRE, *i, NULL, NULL)) + return -EOPNOTSUPP; + + return 0; +} + + +static int prism2_ioctl_priv_prism2_param(struct net_device *dev, + struct iw_request_info *info, + void *wrqu, char *extra) +{ + local_info_t *local = (local_info_t *) dev->priv; + int *i = (int *) extra; + int param = *i; + int value = *(i + 1); + int ret = 0; + u16 val; + + switch (param) { + case PRISM2_PARAM_PTYPE: + if (hostap_set_word(dev, HFA384X_RID_CNFPORTTYPE, value)) { + ret = -EOPNOTSUPP; + break; + } + + if (local->func->reset_port(dev)) + ret = -EINVAL; + break; + + case PRISM2_PARAM_TXRATECTRL: + local->fw_tx_rate_control = value; + break; + + case PRISM2_PARAM_BEACON_INT: + if (hostap_set_word(dev, HFA384X_RID_CNFBEACONINT, value) || + local->func->reset_port(dev)) + ret = -EINVAL; + else + local->beacon_int = value; + break; + +#ifndef PRISM2_NO_STATION_MODES + case PRISM2_PARAM_PSEUDO_IBSS: + if (value == local->pseudo_adhoc) + break; + + if (value != 0 && value != 1) { + ret = -EINVAL; + break; + } + + printk(KERN_DEBUG "prism2: %s: pseudo IBSS change %d -> %d\n", + dev->name, local->pseudo_adhoc, value); + local->pseudo_adhoc = value; + if (local->iw_mode != IW_MODE_ADHOC) + break; + + if (hostap_set_word(dev, HFA384X_RID_CNFPORTTYPE, + hostap_get_porttype(local))) { + ret = -EOPNOTSUPP; + break; + } + + if (local->func->reset_port(dev)) + ret = -EINVAL; + break; +#endif /* PRISM2_NO_STATION_MODES */ + + case PRISM2_PARAM_ALC: + printk(KERN_DEBUG "%s: %s ALC\n", dev->name, + value == 0 ? "Disabling" : "Enabling"); + val = HFA384X_TEST_CFG_BIT_ALC; + local->func->cmd(dev, HFA384X_CMDCODE_TEST | + (HFA384X_TEST_CFG_BITS << 8), + value == 0 ? 0 : 1, &val, NULL); + break; + + case PRISM2_PARAM_TXPOWER: + val = value; + if (local->func->cmd(dev, HFA384X_CMDCODE_WRITEMIF, + HFA386X_CR_MANUAL_TX_POWER, &val, NULL)) + ret = -EOPNOTSUPP; + break; + + case PRISM2_PARAM_DUMP: + local->frame_dump = value; + break; + + case PRISM2_PARAM_OTHER_AP_POLICY: + if (value < 0 || value > 3) { + ret = -EINVAL; + break; + } + if (local->ap != NULL) + local->ap->ap_policy = value; + break; + + case PRISM2_PARAM_AP_MAX_INACTIVITY: + if (value < 0 || value > 7 * 24 * 60 * 60) { + ret = -EINVAL; + break; + } + if (local->ap != NULL) + local->ap->max_inactivity = value * HZ; + break; + + case PRISM2_PARAM_AP_BRIDGE_PACKETS: + if (local->ap != NULL) + local->ap->bridge_packets = value; + break; + + case PRISM2_PARAM_DTIM_PERIOD: + if (value < 0 || value > 65535) { + ret = -EINVAL; + break; + } + if (hostap_set_word(dev, HFA384X_RID_CNFOWNDTIMPERIOD, value) + || local->func->reset_port(dev)) + ret = -EINVAL; + else + local->dtim_period = value; + break; + + case PRISM2_PARAM_AP_NULLFUNC_ACK: + if (local->ap != NULL) + local->ap->nullfunc_ack = value; + break; + + case PRISM2_PARAM_MAX_WDS: + local->wds_max_connections = value; + break; + + case PRISM2_PARAM_AP_AUTOM_AP_WDS: + if (local->ap != NULL) { + if (!local->ap->autom_ap_wds && value) { + /* add WDS link to all APs in STA table */ + hostap_add_wds_links(local); + } + local->ap->autom_ap_wds = value; + } + break; + + case PRISM2_PARAM_AP_AUTH_ALGS: + if (local->ap != NULL) + local->ap->auth_algs = value; + break; + + case PRISM2_PARAM_MONITOR_ALLOW_FCSERR: + local->monitor_allow_fcserr = value; + break; + + case PRISM2_PARAM_HOST_ENCRYPT: + local->host_encrypt = value; + if (hostap_set_encryption(local) || + local->func->reset_port(dev)) + ret = -EINVAL; + break; + + case PRISM2_PARAM_HOST_DECRYPT: + local->host_decrypt = value; + if (hostap_set_encryption(local) || + local->func->reset_port(dev)) + ret = -EINVAL; + break; + + case PRISM2_PARAM_BUS_MASTER_THRESHOLD_RX: + local->bus_master_threshold_rx = value; + break; + + case PRISM2_PARAM_BUS_MASTER_THRESHOLD_TX: + local->bus_master_threshold_tx = value; + break; + +#ifndef PRISM2_NO_STATION_MODES + case PRISM2_PARAM_HOST_ROAMING: + local->host_roaming = value; + if (hostap_set_word(dev, HFA384X_RID_CNFROAMINGMODE, + value ? HFA384X_ROAMING_HOST : + HFA384X_ROAMING_FIRMWARE) || + local->func->reset_port(dev)) + ret = -EINVAL; + else + local->host_roaming = value; + break; +#endif /* PRISM2_NO_STATION_MODES */ + + case PRISM2_PARAM_BCRX_STA_KEY: + local->bcrx_sta_key = value; + break; + + case PRISM2_PARAM_IEEE_802_1X: + local->ieee_802_1x = value; + break; + + case PRISM2_PARAM_ANTSEL_TX: + if (value < 0 || value > HOSTAP_ANTSEL_HIGH) { + ret = -EINVAL; + break; + } + local->antsel_tx = value; + hostap_set_antsel(local); + break; + + case PRISM2_PARAM_ANTSEL_RX: + if (value < 0 || value > HOSTAP_ANTSEL_HIGH) { + ret = -EINVAL; + break; + } + local->antsel_rx = value; + hostap_set_antsel(local); + break; + + case PRISM2_PARAM_MONITOR_TYPE: + if (value != PRISM2_MONITOR_80211 && + value != PRISM2_MONITOR_CAPHDR && + value != PRISM2_MONITOR_PRISM) { + ret = -EINVAL; + break; + } + local->monitor_type = value; + if (local->iw_mode == IW_MODE_MONITOR) + hostap_monitor_set_type(local); + break; + + case PRISM2_PARAM_WDS_TYPE: + local->wds_type = value; + break; + + case PRISM2_PARAM_HOSTSCAN: + { + struct hfa384x_hostscan_request scan_req; + u16 rate; + + memset(&scan_req, 0, sizeof(scan_req)); + scan_req.channel_list = __constant_cpu_to_le16(0x3fff); + switch (value) { + case 1: rate = HFA384X_RATES_1MBPS; break; + case 2: rate = HFA384X_RATES_2MBPS; break; + case 3: rate = HFA384X_RATES_5MBPS; break; + case 4: rate = HFA384X_RATES_11MBPS; break; + default: rate = HFA384X_RATES_1MBPS; break; + } + scan_req.txrate = cpu_to_le16(rate); + /* leave SSID empty to accept all SSIDs */ + + if (local->iw_mode == IW_MODE_MASTER) { + if (hostap_set_word(dev, HFA384X_RID_CNFPORTTYPE, + HFA384X_PORTTYPE_BSS) || + local->func->reset_port(dev)) + printk(KERN_DEBUG "Leaving Host AP mode " + "for HostScan failed\n"); + } + + if (local->func->set_rid(dev, HFA384X_RID_HOSTSCAN, &scan_req, + sizeof(scan_req))) { + printk(KERN_DEBUG "HOSTSCAN failed\n"); + ret = -EINVAL; + } + if (local->iw_mode == IW_MODE_MASTER) { + wait_queue_t __wait; + init_waitqueue_entry(&__wait, current); + add_wait_queue(&local->hostscan_wq, &__wait); + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(HZ); + if (signal_pending(current)) + ret = -EINTR; + set_current_state(TASK_RUNNING); + remove_wait_queue(&local->hostscan_wq, &__wait); + + if (hostap_set_word(dev, HFA384X_RID_CNFPORTTYPE, + HFA384X_PORTTYPE_HOSTAP) || + local->func->reset_port(dev)) + printk(KERN_DEBUG "Returning to Host AP mode " + "after HostScan failed\n"); + } + break; + } + + case PRISM2_PARAM_AP_SCAN: + local->passive_scan_interval = value; + if (timer_pending(&local->passive_scan_timer)) + del_timer(&local->passive_scan_timer); + if (value > 0) { + local->passive_scan_timer.expires = jiffies + + local->passive_scan_interval * HZ; + add_timer(&local->passive_scan_timer); + } + break; + + case PRISM2_PARAM_ENH_SEC: + if (value < 0 || value > 3) { + ret = -EINVAL; + break; + } + local->enh_sec = value; + if (hostap_set_word(dev, HFA384X_RID_CNFENHSECURITY, + local->enh_sec)) { + printk(KERN_INFO "%s: cnfEnhSecurity requires STA f/w " + "1.6.3 or newer\n", dev->name); + ret = -EOPNOTSUPP; + } + break; + +#ifdef PRISM2_IO_DEBUG + case PRISM2_PARAM_IO_DEBUG: + local->io_debug_enabled = value; + break; +#endif /* PRISM2_IO_DEBUG */ + + case PRISM2_PARAM_BASIC_RATES: + local->basic_rates = value; + if (hostap_set_word(dev, HFA384X_RID_CNFBASICRATES, + local->basic_rates) || + local->func->reset_port(dev)) + ret = -EINVAL; + break; + + case PRISM2_PARAM_OPER_RATES: + local->tx_rate_control = value; + if (hostap_set_rate(dev)) + ret = -EINVAL; + break; + + case PRISM2_PARAM_HOSTAPD: + ret = hostap_set_hostapd(local, value, 1); + break; + + default: + printk(KERN_DEBUG "%s: prism2_param: unknown param %d\n", + dev->name, param); + ret = -EOPNOTSUPP; + break; + } + + return ret; +} + + +#if WIRELESS_EXT >= 12 +static int prism2_ioctl_priv_get_prism2_param(struct net_device *dev, + struct iw_request_info *info, + void *wrqu, char *extra) +{ + local_info_t *local = (local_info_t *) dev->priv; + int *param = (int *) extra; + int ret = 0; + u16 val; + + switch (*param) { + case PRISM2_PARAM_PTYPE: + if (local->func->get_rid(dev, HFA384X_RID_CNFPORTTYPE, + &val, 2, 1) < 0) + ret = -EINVAL; + else + *param = le16_to_cpu(val); + break; + + case PRISM2_PARAM_TXRATECTRL: + *param = local->fw_tx_rate_control; + break; + + case PRISM2_PARAM_BEACON_INT: + *param = local->beacon_int; + break; + + case PRISM2_PARAM_PSEUDO_IBSS: + *param = local->pseudo_adhoc; + break; + + case PRISM2_PARAM_ALC: + ret = -EOPNOTSUPP; /* FIX */ + break; + + case PRISM2_PARAM_TXPOWER: + if (local->func->cmd(dev, HFA384X_CMDCODE_READMIF, + HFA386X_CR_MANUAL_TX_POWER, NULL, &val)) + ret = -EOPNOTSUPP; + *param = val; + break; + + case PRISM2_PARAM_DUMP: + *param = local->frame_dump; + break; + + case PRISM2_PARAM_OTHER_AP_POLICY: + if (local->ap != NULL) + *param = local->ap->ap_policy; + else + ret = -EOPNOTSUPP; + break; + + case PRISM2_PARAM_AP_MAX_INACTIVITY: + if (local->ap != NULL) + *param = local->ap->max_inactivity / HZ; + else + ret = -EOPNOTSUPP; + break; + + case PRISM2_PARAM_AP_BRIDGE_PACKETS: + if (local->ap != NULL) + *param = local->ap->bridge_packets; + else + ret = -EOPNOTSUPP; + break; + + case PRISM2_PARAM_DTIM_PERIOD: + *param = local->dtim_period; + break; + + case PRISM2_PARAM_AP_NULLFUNC_ACK: + if (local->ap != NULL) + *param = local->ap->nullfunc_ack; + else + ret = -EOPNOTSUPP; + break; + + case PRISM2_PARAM_MAX_WDS: + *param = local->wds_max_connections; + break; + + case PRISM2_PARAM_AP_AUTOM_AP_WDS: + if (local->ap != NULL) + *param = local->ap->autom_ap_wds; + else + ret = -EOPNOTSUPP; + break; + + case PRISM2_PARAM_AP_AUTH_ALGS: + if (local->ap != NULL) + *param = local->ap->auth_algs; + else + ret = -EOPNOTSUPP; + break; + + case PRISM2_PARAM_MONITOR_ALLOW_FCSERR: + *param = local->monitor_allow_fcserr; + break; + + case PRISM2_PARAM_HOST_ENCRYPT: + *param = local->host_encrypt; + break; + + case PRISM2_PARAM_HOST_DECRYPT: + *param = local->host_decrypt; + break; + + case PRISM2_PARAM_BUS_MASTER_THRESHOLD_RX: + *param = local->bus_master_threshold_rx; + break; + + case PRISM2_PARAM_BUS_MASTER_THRESHOLD_TX: + *param = local->bus_master_threshold_tx; + break; + + case PRISM2_PARAM_HOST_ROAMING: + *param = local->host_roaming; + break; + + case PRISM2_PARAM_BCRX_STA_KEY: + *param = local->bcrx_sta_key; + break; + + case PRISM2_PARAM_IEEE_802_1X: + *param = local->ieee_802_1x; + break; + + case PRISM2_PARAM_ANTSEL_TX: + *param = local->antsel_tx; + break; + + case PRISM2_PARAM_ANTSEL_RX: + *param = local->antsel_rx; + break; + + case PRISM2_PARAM_MONITOR_TYPE: + *param = local->monitor_type; + break; + + case PRISM2_PARAM_WDS_TYPE: + *param = local->wds_type; + break; + + case PRISM2_PARAM_HOSTSCAN: + ret = -EOPNOTSUPP; + break; + + case PRISM2_PARAM_AP_SCAN: + *param = local->passive_scan_interval; + break; + + case PRISM2_PARAM_ENH_SEC: + *param = local->enh_sec; + break; + +#ifdef PRISM2_IO_DEBUG + case PRISM2_PARAM_IO_DEBUG: + *param = local->io_debug_enabled; + break; +#endif /* PRISM2_IO_DEBUG */ + + case PRISM2_PARAM_BASIC_RATES: + *param = local->basic_rates; + break; + + case PRISM2_PARAM_OPER_RATES: + *param = local->tx_rate_control; + break; + + case PRISM2_PARAM_HOSTAPD: + *param = local->hostapd; + break; + + default: + printk(KERN_DEBUG "%s: get_prism2_param: unknown param %d\n", + dev->name, *param); + ret = -EOPNOTSUPP; + break; + } + + return ret; +} +#endif /* WIRELESS_EXT >= 12 */ + + +static int prism2_ioctl_priv_readmif(struct net_device *dev, + struct iw_request_info *info, + void *wrqu, char *extra) +{ + local_info_t *local = (local_info_t *) dev->priv; + u16 resp0; + + if (local->func->cmd(dev, HFA384X_CMDCODE_READMIF, *extra, NULL, + &resp0)) + return -EOPNOTSUPP; + else + *extra = resp0; + + return 0; +} + + +static int prism2_ioctl_priv_writemif(struct net_device *dev, + struct iw_request_info *info, + void *wrqu, char *extra) +{ + local_info_t *local = (local_info_t *) dev->priv; + u16 cr, val; + + cr = *extra; + val = *(extra + 1); + if (local->func->cmd(dev, HFA384X_CMDCODE_WRITEMIF, cr, &val, NULL)) + return -EOPNOTSUPP; + + return 0; +} + + +static int prism2_ioctl_priv_monitor(struct net_device *dev, int *i) +{ +#if WIRELESS_EXT > 8 + local_info_t *local = (local_info_t *) dev->priv; + int ret = 0; + u32 mode; + + printk(KERN_DEBUG "%s: process %d (%s) used deprecated iwpriv monitor " + "- update software to use iwconfig mode monitor\n", + dev->name, current->pid, current->comm); + + /* Backward compatibility code - this can be removed at some point */ + + if (*i == 0) { + /* Disable monitor mode - old mode was not saved, so go to + * Master mode */ + mode = IW_MODE_MASTER; + ret = prism2_ioctl_siwmode(dev, NULL, &mode, NULL); + } else if (*i == 1) { + /* netlink socket mode is not supported anymore since it did + * not separate different devices from each other and was not + * best method for delivering large amount of packets to + * user space */ + ret = -EOPNOTSUPP; + } else if (*i == 2 || *i == 3) { + switch (*i) { + case 2: + local->monitor_type = PRISM2_MONITOR_80211; + break; + case 3: + local->monitor_type = PRISM2_MONITOR_PRISM; + break; + } + mode = IW_MODE_MONITOR; + ret = prism2_ioctl_siwmode(dev, NULL, &mode, NULL); + hostap_monitor_mode_enable(local); + } else + ret = -EINVAL; + + return ret; +#else /* WIRELESS_EXT > 8 */ + return -EOPNOTSUPP; +#endif /* WIRELESS_EXT > 8 */ +} + + +static int prism2_ioctl_priv_reset(struct net_device *dev, int *i) +{ + local_info_t *local = (local_info_t *) dev->priv; + + printk(KERN_DEBUG "%s: manual reset request(%d)\n", dev->name, *i); + switch (*i) { + case 0: + /* Disable and enable card */ + local->func->hw_shutdown(dev, 1); + local->func->hw_config(dev, 0); + break; + + case 1: + /* COR sreset */ + local->func->hw_reset(dev); + break; + + case 2: + /* Disable and enable port 0 */ + local->func->reset_port(dev); + break; + + case 3: + if (local->func->cmd(dev, HFA384X_CMDCODE_DISABLE, 0, NULL, + NULL)) + return -EINVAL; + break; + + case 4: + if (local->func->cmd(dev, HFA384X_CMDCODE_ENABLE, 0, NULL, + NULL)) + return -EINVAL; + break; + + default: + printk(KERN_DEBUG "Unknown reset request %d\n", *i); + return -EOPNOTSUPP; + } + + return 0; +} + + +#ifndef PRISM2_USE_WE_TYPE_ADDR +static inline int hex2int(char c) +{ + if (c >= '0' && c <= '9') + return (c - '0'); + if (c >= 'a' && c <= 'f') + return (c - 'a' + 10); + if (c >= 'A' && c <= 'F') + return (c - 'A' + 10); + return -1; +} + +static int macstr2addr(char *macstr, u8 *addr) +{ + int i, val, val2; + char *pos = macstr; + + for (i = 0; i < 6; i++) { + val = hex2int(*pos++); + if (val < 0) + return -1; + val2 = hex2int(*pos++); + if (val2 < 0) + return -1; + addr[i] = (val * 16 + val2) & 0xff; + + if (i < 5 && *pos++ != ':') + return -1; + } + + return 0; +} + + +static int prism2_ioctl_priv_wds(struct net_device *dev, int add, char *macstr) +{ + local_info_t *local = (local_info_t *) dev->priv; + u8 addr[6]; + + if (macstr2addr(macstr, addr)) { + printk(KERN_DEBUG "Invalid MAC address\n"); + return -EINVAL; + } + + if (add) + return prism2_wds_add(local, addr, 1); + else + return prism2_wds_del(local, addr, 1, 0); +} +#endif /* PRISM2_USE_WE_TYPE_ADDR */ + + +static int prism2_ioctl_priv_set_rid_word(struct net_device *dev, int *i) +{ + int rid = *i; + int value = *(i + 1); + + printk(KERN_DEBUG "%s: Set RID[0x%X] = %d\n", dev->name, rid, value); + + if (hostap_set_word(dev, rid, value)) + return -EINVAL; + + return 0; +} + + +#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT +static int ap_mac_cmd_ioctl(local_info_t *local, int *cmd) +{ + int ret = 0; + + switch (*cmd) { + case AP_MAC_CMD_POLICY_OPEN: + local->ap->mac_restrictions.policy = MAC_POLICY_OPEN; + break; + case AP_MAC_CMD_POLICY_ALLOW: + local->ap->mac_restrictions.policy = MAC_POLICY_ALLOW; + break; + case AP_MAC_CMD_POLICY_DENY: + local->ap->mac_restrictions.policy = MAC_POLICY_DENY; + break; + case AP_MAC_CMD_FLUSH: + ap_control_flush_macs(&local->ap->mac_restrictions); + break; + case AP_MAC_CMD_KICKALL: + ap_control_kickall(local->ap); + hostap_deauth_all_stas(local->dev, local->ap, 0); + break; + default: + ret = -EOPNOTSUPP; + break; + } + + return ret; +} + + +enum { AP_CTRL_MAC_ADD, AP_CTRL_MAC_DEL, AP_CTRL_MAC_KICK }; + +#ifndef PRISM2_USE_WE_TYPE_ADDR +static int ap_mac_ioctl(local_info_t *local, char *macstr, int cmd) +{ + u8 addr[6]; + + if (macstr2addr(macstr, addr)) { + printk(KERN_DEBUG "Invalid MAC address '%s'\n", macstr); + return -EINVAL; + } + + switch (cmd) { + case AP_CTRL_MAC_ADD: + return ap_control_add_mac(&local->ap->mac_restrictions, addr); + case AP_CTRL_MAC_DEL: + return ap_control_del_mac(&local->ap->mac_restrictions, addr); + case AP_CTRL_MAC_KICK: + return ap_control_kick_mac(local->ap, local->dev, addr); + default: + return -EOPNOTSUPP; + } +} +#endif /* PRISM2_USE_WE_TYPE_ADDR */ +#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ + + +#if defined(PRISM2_DOWNLOAD_SUPPORT) && WIRELESS_EXT > 8 +static int prism2_ioctl_priv_download(local_info_t *local, struct iw_point *p) +{ + struct prism2_download_param *param; + int ret = 0; + + if (p->length < sizeof(struct prism2_download_param) || + p->length > 1024 || !p->pointer) + return -EINVAL; + + param = (struct prism2_download_param *) + kmalloc(p->length, GFP_KERNEL); + if (param == NULL) + return -ENOMEM; + + if (copy_from_user(param, p->pointer, p->length)) { + ret = -EFAULT; + goto out; + } + + if (p->length < sizeof(struct prism2_download_param) + + param->num_areas * sizeof(struct prism2_download_area)) { + ret = -EINVAL; + goto out; + } + + ret = local->func->download(local, param); + + out: + if (param != NULL) + kfree(param); + + return ret; +} +#endif /* PRISM2_DOWNLOAD_SUPPORT and WIRELESS_EXT > 8 */ + + +static int prism2_ioctl_set_encryption(local_info_t *local, + struct prism2_hostapd_param *param, + int param_len) +{ + int ret = 0; + struct hostap_crypto_ops *ops; + struct prism2_crypt_data **crypt; + void *sta_ptr; + + param->u.crypt.err = 0; + param->u.crypt.alg[HOSTAP_CRYPT_ALG_NAME_LEN - 1] = '\0'; + + if (param_len != + (int) ((char *) param->u.crypt.key - (char *) param) + + param->u.crypt.key_len) + return -EINVAL; + + if (param->sta_addr[0] == 0xff && param->sta_addr[1] == 0xff && + param->sta_addr[2] == 0xff && param->sta_addr[3] == 0xff && + param->sta_addr[4] == 0xff && param->sta_addr[5] == 0xff) { + sta_ptr = NULL; + crypt = &local->crypt; + } else { + sta_ptr = ap_crypt_get_ptrs( + local->ap, param->sta_addr, + (param->u.crypt.flags & HOSTAP_CRYPT_FLAG_PERMANENT), + &crypt); + + if (sta_ptr == NULL) { + param->u.crypt.err = HOSTAP_CRYPT_ERR_UNKNOWN_ADDR; + return -EINVAL; + } + } + + if (strcmp(param->u.crypt.alg, "none") == 0) { + prism2_crypt_delayed_deinit(local, crypt); + goto done; + } + + ops = hostap_get_crypto_ops(param->u.crypt.alg); + if (ops == NULL && strcmp(param->u.crypt.alg, "WEP") == 0) { + request_module("hostap_crypt_wep"); + ops = hostap_get_crypto_ops(param->u.crypt.alg); + } + if (ops == NULL) { + printk(KERN_DEBUG "%s: unknown crypto alg '%s'\n", + local->dev->name, param->u.crypt.alg); + param->u.crypt.err = HOSTAP_CRYPT_ERR_UNKNOWN_ALG; + ret = -EINVAL; + goto done; + } + + /* station based encryption and other than WEP algorithms require + * host-based encryption, so force them on automatically */ + local->host_decrypt = local->host_encrypt = 1; + + if (*crypt == NULL || (*crypt)->ops != ops) { + struct prism2_crypt_data *new_crypt; + + prism2_crypt_delayed_deinit(local, crypt); + + new_crypt = (struct prism2_crypt_data *) + kmalloc(sizeof(struct prism2_crypt_data), GFP_KERNEL); + if (new_crypt == NULL) { + ret = -ENOMEM; + goto done; + } + memset(new_crypt, 0, sizeof(struct prism2_crypt_data)); + new_crypt->ops = ops; + new_crypt->priv = new_crypt->ops->init(); + if (new_crypt->priv == NULL) { + kfree(new_crypt); + param->u.crypt.err = + HOSTAP_CRYPT_ERR_CRYPT_INIT_FAILED; + ret = -EINVAL; + goto done; + } + + *crypt = new_crypt; + } + + if ((!(param->u.crypt.flags & HOSTAP_CRYPT_FLAG_SET_TX_KEY) || + param->u.crypt.key_len > 0) && (*crypt)->ops->set_key && + (*crypt)->ops->set_key(param->u.crypt.idx, param->u.crypt.key, + param->u.crypt.key_len, (*crypt)->priv) < 0) { + printk(KERN_DEBUG "%s: key setting failed\n", + local->dev->name); + param->u.crypt.err = HOSTAP_CRYPT_ERR_KEY_SET_FAILED; + ret = -EINVAL; + goto done; + } + + if ((param->u.crypt.flags & HOSTAP_CRYPT_FLAG_SET_TX_KEY) && + (*crypt)->ops->set_key_idx && + (*crypt)->ops->set_key_idx(param->u.crypt.idx, (*crypt)->priv) < 0) + { + printk(KERN_DEBUG "%s: TX key idx setting failed\n", + local->dev->name); + param->u.crypt.err = HOSTAP_CRYPT_ERR_TX_KEY_SET_FAILED; + ret = -EINVAL; + goto done; + } + + done: + if (sta_ptr) + hostap_handle_sta_release(sta_ptr); + + if (ret == 0 && + (hostap_set_encryption(local) || + local->func->reset_port(local->dev))) { + param->u.crypt.err = HOSTAP_CRYPT_ERR_CARD_CONF_FAILED; + return -EINVAL; + } + + return ret; +} + + +static int prism2_ioctl_get_encryption(local_info_t *local, + struct prism2_hostapd_param *param, + int param_len) +{ + struct prism2_crypt_data **crypt; + void *sta_ptr; + int max_key_len; + + param->u.crypt.err = 0; + + max_key_len = param_len - + (int) ((char *) param->u.crypt.key - (char *) param); + if (max_key_len < 0) + return -EINVAL; + + if (param->sta_addr[0] == 0xff && param->sta_addr[1] == 0xff && + param->sta_addr[2] == 0xff && param->sta_addr[3] == 0xff && + param->sta_addr[4] == 0xff && param->sta_addr[5] == 0xff) { + sta_ptr = NULL; + crypt = &local->crypt; + } else { + sta_ptr = ap_crypt_get_ptrs(local->ap, param->sta_addr, 0, + &crypt); + + if (sta_ptr == NULL) { + param->u.crypt.err = HOSTAP_CRYPT_ERR_UNKNOWN_ADDR; + return -EINVAL; + } + } + + if (*crypt == NULL || (*crypt)->ops == NULL) { + memcpy(param->u.crypt.alg, "none", 5); + param->u.crypt.key_len = 0; + param->u.crypt.idx = 0xff; + } else { + strncpy(param->u.crypt.alg, (*crypt)->ops->name, + HOSTAP_CRYPT_ALG_NAME_LEN); + param->u.crypt.key_len = 0; + if (param->u.crypt.idx >= WEP_KEYS && + (*crypt)->ops->get_key_idx) + param->u.crypt.idx = + (*crypt)->ops->get_key_idx((*crypt)->priv); + + if (param->u.crypt.idx < WEP_KEYS && (*crypt)->ops->get_key) + param->u.crypt.key_len = + (*crypt)->ops->get_key(param->u.crypt.idx, + param->u.crypt.key, + max_key_len, + (*crypt)->priv); + } + + if (sta_ptr) + hostap_handle_sta_release(sta_ptr); + + return 0; +} + + +static int prism2_ioctl_get_rid(local_info_t *local, + struct prism2_hostapd_param *param, + int param_len) +{ + int max_len, res; + + max_len = param_len - PRISM2_HOSTAPD_RID_HDR_LEN; + if (max_len < 0) + return -EINVAL; + + res = local->func->get_rid(local->dev, param->u.rid.rid, + param->u.rid.data, param->u.rid.len, 0); + if (res >= 0) { + param->u.rid.len = res; + return 0; + } + + return res; +} + + +static int prism2_ioctl_set_rid(local_info_t *local, + struct prism2_hostapd_param *param, + int param_len) +{ + int max_len; + + max_len = param_len - PRISM2_HOSTAPD_RID_HDR_LEN; + if (max_len < 0 || max_len < param->u.rid.len) + return -EINVAL; + + return local->func->set_rid(local->dev, param->u.rid.rid, + param->u.rid.data, param->u.rid.len); +} + + +static int prism2_ioctl_set_assoc_ap_addr(local_info_t *local, + struct prism2_hostapd_param *param, + int param_len) +{ + printk(KERN_DEBUG "%ssta: associated as client with AP " MACSTR "\n", + local->dev->name, MAC2STR(param->sta_addr)); + memcpy(local->assoc_ap_addr, param->sta_addr, ETH_ALEN); + return 0; +} + + +static int prism2_ioctl_priv_hostapd(local_info_t *local, struct iw_point *p) +{ + struct prism2_hostapd_param *param; + int ret = 0; + int ap_ioctl = 0; + + if (p->length < sizeof(struct prism2_hostapd_param) || + p->length > PRISM2_HOSTAPD_MAX_BUF_SIZE || !p->pointer) + return -EINVAL; + + param = (struct prism2_hostapd_param *) kmalloc(p->length, GFP_KERNEL); + if (param == NULL) + return -ENOMEM; + + if (copy_from_user(param, p->pointer, p->length)) { + ret = -EFAULT; + goto out; + } + + switch (param->cmd) { + case PRISM2_SET_ENCRYPTION: + ret = prism2_ioctl_set_encryption(local, param, p->length); + break; + case PRISM2_GET_ENCRYPTION: + ret = prism2_ioctl_get_encryption(local, param, p->length); + break; + case PRISM2_HOSTAPD_GET_RID: + ret = prism2_ioctl_get_rid(local, param, p->length); + break; + case PRISM2_HOSTAPD_SET_RID: + ret = prism2_ioctl_set_rid(local, param, p->length); + break; + case PRISM2_HOSTAPD_SET_ASSOC_AP_ADDR: + ret = prism2_ioctl_set_assoc_ap_addr(local, param, p->length); + break; + default: + ret = prism2_hostapd(local->ap, param); + ap_ioctl = 1; + break; + } + + if (ret == 1 || !ap_ioctl) { + if (copy_to_user(p->pointer, param, p->length)) { + ret = -EFAULT; + goto out; + } else if (ap_ioctl) + ret = 0; + } + + out: + if (param != NULL) + kfree(param); + + return ret; +} + + +#if WIRELESS_EXT > 12 +/* Structures to export the Wireless Handlers */ + +static const iw_handler prism2_handler[] = +{ + (iw_handler) NULL, /* SIOCSIWCOMMIT */ + (iw_handler) prism2_get_name, /* SIOCGIWNAME */ + (iw_handler) NULL, /* SIOCSIWNWID */ + (iw_handler) NULL, /* SIOCGIWNWID */ + (iw_handler) prism2_ioctl_siwfreq, /* SIOCSIWFREQ */ + (iw_handler) prism2_ioctl_giwfreq, /* SIOCGIWFREQ */ + (iw_handler) prism2_ioctl_siwmode, /* SIOCSIWMODE */ + (iw_handler) prism2_ioctl_giwmode, /* SIOCGIWMODE */ + (iw_handler) prism2_ioctl_siwsens, /* SIOCSIWSENS */ + (iw_handler) prism2_ioctl_giwsens, /* SIOCGIWSENS */ + (iw_handler) NULL /* not used */, /* SIOCSIWRANGE */ + (iw_handler) prism2_ioctl_giwrange, /* SIOCGIWRANGE */ + (iw_handler) NULL /* not used */, /* SIOCSIWPRIV */ + (iw_handler) NULL /* kernel code */, /* SIOCGIWPRIV */ + (iw_handler) NULL /* not used */, /* SIOCSIWSTATS */ + (iw_handler) NULL /* kernel code */, /* SIOCGIWSTATS */ +#if WIRELESS_EXT > 15 + iw_handler_set_spy, /* SIOCSIWSPY */ + iw_handler_get_spy, /* SIOCGIWSPY */ + iw_handler_set_thrspy, /* SIOCSIWTHRSPY */ + iw_handler_get_thrspy, /* SIOCGIWTHRSPY */ +#else /* WIRELESS_EXT > 15 */ + (iw_handler) NULL, /* SIOCSIWSPY */ + (iw_handler) prism2_ioctl_giwspy, /* SIOCGIWSPY */ + (iw_handler) NULL, /* -- hole -- */ + (iw_handler) NULL, /* -- hole -- */ +#endif /* WIRELESS_EXT > 15 */ + (iw_handler) prism2_ioctl_siwap, /* SIOCSIWAP */ + (iw_handler) prism2_ioctl_giwap, /* SIOCGIWAP */ + (iw_handler) NULL, /* -- hole -- */ + (iw_handler) prism2_ioctl_giwaplist, /* SIOCGIWAPLIST */ +#if WIRELESS_EXT > 13 + (iw_handler) prism2_ioctl_siwscan, /* SIOCSIWSCAN */ + (iw_handler) prism2_ioctl_giwscan, /* SIOCGIWSCAN */ +#else /* WIRELESS_EXT > 13 */ + (iw_handler) NULL, /* SIOCSIWSCAN */ + (iw_handler) NULL, /* SIOCGIWSCAN */ +#endif /* WIRELESS_EXT > 13 */ + (iw_handler) prism2_ioctl_siwessid, /* SIOCSIWESSID */ + (iw_handler) prism2_ioctl_giwessid, /* SIOCGIWESSID */ + (iw_handler) prism2_ioctl_siwnickn, /* SIOCSIWNICKN */ + (iw_handler) prism2_ioctl_giwnickn, /* SIOCGIWNICKN */ + (iw_handler) NULL, /* -- hole -- */ + (iw_handler) NULL, /* -- hole -- */ + (iw_handler) prism2_ioctl_siwrate, /* SIOCSIWRATE */ + (iw_handler) prism2_ioctl_giwrate, /* SIOCGIWRATE */ + (iw_handler) prism2_ioctl_siwrts, /* SIOCSIWRTS */ + (iw_handler) prism2_ioctl_giwrts, /* SIOCGIWRTS */ + (iw_handler) prism2_ioctl_siwfrag, /* SIOCSIWFRAG */ + (iw_handler) prism2_ioctl_giwfrag, /* SIOCGIWFRAG */ + (iw_handler) prism2_ioctl_siwtxpow, /* SIOCSIWTXPOW */ + (iw_handler) prism2_ioctl_giwtxpow, /* SIOCGIWTXPOW */ + (iw_handler) prism2_ioctl_siwretry, /* SIOCSIWRETRY */ + (iw_handler) prism2_ioctl_giwretry, /* SIOCGIWRETRY */ + (iw_handler) prism2_ioctl_siwencode, /* SIOCSIWENCODE */ + (iw_handler) prism2_ioctl_giwencode, /* SIOCGIWENCODE */ + (iw_handler) prism2_ioctl_siwpower, /* SIOCSIWPOWER */ + (iw_handler) prism2_ioctl_giwpower, /* SIOCGIWPOWER */ +}; + +static const iw_handler prism2_private_handler[] = +{ /* SIOCIWFIRSTPRIV + */ + (iw_handler) prism2_ioctl_priv_prism2_param, /* 0 */ + (iw_handler) prism2_ioctl_priv_get_prism2_param, /* 1 */ + (iw_handler) prism2_ioctl_priv_writemif, /* 2 */ + (iw_handler) prism2_ioctl_priv_readmif, /* 3 */ +}; + +static const struct iw_handler_def hostap_iw_handler_def = +{ + .num_standard = sizeof(prism2_handler) / sizeof(iw_handler), + .num_private = sizeof(prism2_private_handler) / sizeof(iw_handler), + .num_private_args = sizeof(prism2_priv) / sizeof(struct iw_priv_args), + .standard = (iw_handler *) prism2_handler, + .private = (iw_handler *) prism2_private_handler, + .private_args = (struct iw_priv_args *) prism2_priv, +#if WIRELESS_EXT > 15 + .spy_offset = ((void *) (&((local_info_t *) NULL)->spy_data) - + (void *) NULL), +#endif /* WIRELESS_EXT > 15 */ +}; +#endif /* WIRELESS_EXT > 12 */ + + +int hostap_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) +{ +#ifdef WIRELESS_EXT + struct iwreq *wrq = (struct iwreq *) ifr; +#endif + local_info_t *local = (local_info_t *) dev->priv; + int ret = 0; + + switch (cmd) { + +#ifdef WIRELESS_EXT +#if WIRELESS_EXT <= 12 + case SIOCGIWNAME: + ret = prism2_get_name(dev, NULL, (char *) &wrq->u, NULL); + break; + + case SIOCSIWFREQ: + ret = prism2_ioctl_siwfreq(dev, NULL, &wrq->u.freq, NULL); + break; + case SIOCGIWFREQ: + ret = prism2_ioctl_giwfreq(dev, NULL, &wrq->u.freq, NULL); + break; + + case SIOCSIWAP: + ret = prism2_ioctl_siwap(dev, NULL, &wrq->u.ap_addr, NULL); + break; + case SIOCGIWAP: + ret = prism2_ioctl_giwap(dev, NULL, &wrq->u.ap_addr, NULL); + break; + +#if WIRELESS_EXT > 8 + case SIOCSIWESSID: + if (!wrq->u.essid.pointer) + ret = -EINVAL; + else if (wrq->u.essid.length > IW_ESSID_MAX_SIZE) + ret = -E2BIG; + else { + char ssid[IW_ESSID_MAX_SIZE]; + if (copy_from_user(ssid, wrq->u.essid.pointer, + wrq->u.essid.length)) { + ret = -EFAULT; + break; + } + ret = prism2_ioctl_siwessid(dev, NULL, &wrq->u.essid, + ssid); + } + break; + case SIOCGIWESSID: + if (wrq->u.essid.length > IW_ESSID_MAX_SIZE) + ret = -E2BIG; + else if (wrq->u.essid.pointer) { + char ssid[IW_ESSID_MAX_SIZE]; + ret = prism2_ioctl_giwessid(dev, NULL, &wrq->u.essid, + ssid); + if (copy_to_user(wrq->u.essid.pointer, ssid, + wrq->u.essid.length)) + ret = -EFAULT; + } + break; + + case SIOCSIWRATE: + ret = prism2_ioctl_siwrate(dev, NULL, &wrq->u.bitrate, NULL); + break; + case SIOCGIWRATE: + ret = prism2_ioctl_giwrate(dev, NULL, &wrq->u.bitrate, NULL); + break; + + case SIOCSIWRTS: + ret = prism2_ioctl_siwrts(dev, NULL, &wrq->u.rts, NULL); + break; + case SIOCGIWRTS: + ret = prism2_ioctl_giwrts(dev, NULL, &wrq->u.rts, NULL); + break; + + case SIOCSIWFRAG: + ret = prism2_ioctl_siwfrag(dev, NULL, &wrq->u.rts, NULL); + break; + case SIOCGIWFRAG: + ret = prism2_ioctl_giwfrag(dev, NULL, &wrq->u.rts, NULL); + break; + + case SIOCSIWENCODE: + { + char keybuf[WEP_KEY_LEN]; + if (wrq->u.encoding.pointer) { + if (wrq->u.encoding.length > WEP_KEY_LEN) { + ret = -E2BIG; + break; + } + if (copy_from_user(keybuf, + wrq->u.encoding.pointer, + wrq->u.encoding.length)) { + ret = -EFAULT; + break; + } + } else if (wrq->u.encoding.length != 0) { + ret = -EINVAL; + break; + } + ret = prism2_ioctl_siwencode(dev, NULL, + &wrq->u.encoding, keybuf); + } + break; + case SIOCGIWENCODE: + if (!capable(CAP_NET_ADMIN)) + ret = -EPERM; + else if (wrq->u.encoding.pointer) { + char keybuf[WEP_KEY_LEN]; + ret = prism2_ioctl_giwencode(dev, NULL, + &wrq->u.encoding, keybuf); + if (copy_to_user(wrq->u.encoding.pointer, keybuf, + wrq->u.encoding.length)) + ret = -EFAULT; + } + break; + + case SIOCSIWNICKN: + if (wrq->u.essid.length > IW_ESSID_MAX_SIZE) + ret = -E2BIG; + else if (wrq->u.essid.pointer) { + char nickbuf[IW_ESSID_MAX_SIZE + 1]; + if (copy_from_user(nickbuf, wrq->u.essid.pointer, + wrq->u.essid.length)) { + ret = -EFAULT; + break; + } + ret = prism2_ioctl_siwnickn(dev, NULL, &wrq->u.essid, + nickbuf); + } + break; + case SIOCGIWNICKN: + if (wrq->u.essid.pointer) { + char nickbuf[IW_ESSID_MAX_SIZE + 1]; + ret = prism2_ioctl_giwnickn(dev, NULL, &wrq->u.essid, + nickbuf); + if (copy_to_user(wrq->u.essid.pointer, nickbuf, + wrq->u.essid.length)) + ret = -EFAULT; + } + break; + + case SIOCGIWSPY: + { + char buffer[IW_MAX_SPY * (sizeof(struct sockaddr) + + sizeof(struct iw_quality))]; + ret = prism2_ioctl_giwspy(dev, NULL, &wrq->u.data, + buffer); + if (ret == 0 && wrq->u.data.pointer && + copy_to_user(wrq->u.data.pointer, buffer, + wrq->u.data.length * + (sizeof(struct sockaddr) + + sizeof(struct iw_quality)))) + ret = -EFAULT; + } + break; + + case SIOCGIWRANGE: + { + struct iw_range range; + ret = prism2_ioctl_giwrange(dev, NULL, &wrq->u.data, + (char *) &range); + if (copy_to_user(wrq->u.data.pointer, &range, + sizeof(struct iw_range))) + ret = -EFAULT; + } + break; + + case SIOCSIWSENS: + ret = prism2_ioctl_siwsens(dev, NULL, &wrq->u.sens, NULL); + break; + case SIOCGIWSENS: + ret = prism2_ioctl_giwsens(dev, NULL, &wrq->u.sens, NULL); + break; + + case SIOCGIWAPLIST: + if (wrq->u.data.pointer) { + char buffer[IW_MAX_AP * (sizeof(struct sockaddr) + + sizeof(struct iw_quality))]; + ret = prism2_ioctl_giwaplist(dev, NULL, &wrq->u.data, + buffer); + if (copy_to_user(wrq->u.data.pointer, buffer, + (wrq->u.data.length * + (sizeof(struct sockaddr) + + sizeof(struct iw_quality))))) + ret = -EFAULT; + } + break; + + case SIOCSIWMODE: + ret = prism2_ioctl_siwmode(dev, NULL, &wrq->u.mode, NULL); + break; + case SIOCGIWMODE: + ret = prism2_ioctl_giwmode(dev, NULL, &wrq->u.mode, NULL); + break; + + case SIOCSIWPOWER: + ret = prism2_ioctl_siwpower(dev, NULL, &wrq->u.power, NULL); + break; + case SIOCGIWPOWER: + ret = prism2_ioctl_giwpower(dev, NULL, &wrq->u.power, NULL); + break; + + case SIOCGIWPRIV: + ret = prism2_ioctl_giwpriv(dev, &wrq->u.data); + break; +#endif /* WIRELESS_EXT > 8 */ + +#if WIRELESS_EXT > 9 + case SIOCSIWTXPOW: + ret = prism2_ioctl_siwtxpow(dev, NULL, &wrq->u.txpower, NULL); + break; + case SIOCGIWTXPOW: + ret = prism2_ioctl_giwtxpow(dev, NULL, &wrq->u.txpower, NULL); + break; +#endif /* WIRELESS_EXT > 9 */ + +#if WIRELESS_EXT > 10 + case SIOCSIWRETRY: + ret = prism2_ioctl_siwretry(dev, NULL, &wrq->u.retry, NULL); + break; + case SIOCGIWRETRY: + ret = prism2_ioctl_giwretry(dev, NULL, &wrq->u.retry, NULL); + break; +#endif /* WIRELESS_EXT > 10 */ + + /* not supported wireless extensions */ + case SIOCSIWNWID: + case SIOCGIWNWID: + ret = -EOPNOTSUPP; + break; + + /* FIX: add support for this: */ + case SIOCSIWSPY: + printk(KERN_DEBUG "%s unsupported WIRELESS_EXT ioctl(0x%04x)\n" + , dev->name, cmd); + ret = -EOPNOTSUPP; + break; + + + /* Private ioctls (iwpriv); these are in SIOCDEVPRIVATE range + * if WIRELESS_EXT < 12, so better check privileges */ + + case PRISM2_IOCTL_PRISM2_PARAM: + if (!capable(CAP_NET_ADMIN)) ret = -EPERM; + else ret = prism2_ioctl_priv_prism2_param(dev, NULL, &wrq->u, + (char *) &wrq->u); + break; +#if WIRELESS_EXT >= 12 + case PRISM2_IOCTL_GET_PRISM2_PARAM: + ret = prism2_ioctl_priv_get_prism2_param(dev, NULL, &wrq->u, + (char *) &wrq->u); + break; +#endif /* WIRELESS_EXT >= 12 */ + + case PRISM2_IOCTL_WRITEMIF: + if (!capable(CAP_NET_ADMIN)) ret = -EPERM; + else ret = prism2_ioctl_priv_writemif(dev, NULL, &wrq->u, + (char *) &wrq->u); + break; + + case PRISM2_IOCTL_READMIF: + ret = prism2_ioctl_priv_readmif(dev, NULL, &wrq->u, + (char *) &wrq->u); + break; + +#endif /* WIRELESS_EXT <= 12 */ + + + /* Private ioctls (iwpriv) that have not yet been converted + * into new wireless extensions API */ + + case PRISM2_IOCTL_INQUIRE: + if (!capable(CAP_NET_ADMIN)) ret = -EPERM; + else ret = prism2_ioctl_priv_inquire(dev, (int *) wrq->u.name); + break; + + case PRISM2_IOCTL_MONITOR: + if (!capable(CAP_NET_ADMIN)) ret = -EPERM; + else ret = prism2_ioctl_priv_monitor(dev, (int *) wrq->u.name); + break; + + case PRISM2_IOCTL_RESET: + if (!capable(CAP_NET_ADMIN)) ret = -EPERM; + else ret = prism2_ioctl_priv_reset(dev, (int *) wrq->u.name); + break; + +#ifdef PRISM2_USE_WE_TYPE_ADDR + case PRISM2_IOCTL_WDS_ADD: + if (!capable(CAP_NET_ADMIN)) ret = -EPERM; + else ret = prism2_wds_add(local, wrq->u.ap_addr.sa_data, 1); + break; + + case PRISM2_IOCTL_WDS_DEL: + if (!capable(CAP_NET_ADMIN)) ret = -EPERM; + else ret = prism2_wds_del(local, wrq->u.ap_addr.sa_data, 1, 0); + break; +#else /* PRISM2_USE_WE_TYPE_ADDR */ + case PRISM2_IOCTL_WDS_ADD: + if (!capable(CAP_NET_ADMIN)) ret = -EPERM; + else if (wrq->u.data.pointer) { + char addrbuf[18]; + if (copy_from_user(addrbuf, wrq->u.data.pointer, 18)) { + ret = -EFAULT; + break; + } + ret = prism2_ioctl_priv_wds(dev, 1, addrbuf); + } + break; + + case PRISM2_IOCTL_WDS_DEL: + if (!capable(CAP_NET_ADMIN)) ret = -EPERM; + else if (wrq->u.data.pointer) { + char addrbuf[18]; + if (copy_from_user(addrbuf, wrq->u.data.pointer, 18)) { + ret = -EFAULT; + break; + } + ret = prism2_ioctl_priv_wds(dev, 0, addrbuf); + } + break; +#endif /* PRISM2_USE_WE_TYPE_ADDR */ + + case PRISM2_IOCTL_SET_RID_WORD: + if (!capable(CAP_NET_ADMIN)) ret = -EPERM; + else ret = prism2_ioctl_priv_set_rid_word(dev, + (int *) wrq->u.name); + break; + +#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT + case PRISM2_IOCTL_MACCMD: + if (!capable(CAP_NET_ADMIN)) ret = -EPERM; + else ret = ap_mac_cmd_ioctl(local, (int *) wrq->u.name); + break; + +#ifdef PRISM2_USE_WE_TYPE_ADDR + case PRISM2_IOCTL_ADDMAC: + if (!capable(CAP_NET_ADMIN)) ret = -EPERM; + else ret = ap_control_add_mac(&local->ap->mac_restrictions, + wrq->u.ap_addr.sa_data); + break; + case PRISM2_IOCTL_DELMAC: + if (!capable(CAP_NET_ADMIN)) ret = -EPERM; + else ret = ap_control_del_mac(&local->ap->mac_restrictions, + wrq->u.ap_addr.sa_data); + break; + case PRISM2_IOCTL_KICKMAC: + if (!capable(CAP_NET_ADMIN)) ret = -EPERM; + else ret = ap_control_kick_mac(local->ap, local->dev, + wrq->u.ap_addr.sa_data); + break; +#else /* PRISM2_USE_WE_TYPE_ADDR */ + case PRISM2_IOCTL_ADDMAC: + if (!capable(CAP_NET_ADMIN)) ret = -EPERM; + else if (wrq->u.data.pointer) { + char addrbuf[18]; + if (copy_from_user(addrbuf, wrq->u.data.pointer, 18)) { + ret = -EFAULT; + break; + } + ret = ap_mac_ioctl(local, addrbuf, AP_CTRL_MAC_ADD); + } + break; + + case PRISM2_IOCTL_DELMAC: + if (!capable(CAP_NET_ADMIN)) ret = -EPERM; + else if (wrq->u.data.pointer) { + char addrbuf[18]; + if (copy_from_user(addrbuf, wrq->u.data.pointer, 18)) { + ret = -EFAULT; + break; + } + ret = ap_mac_ioctl(local, addrbuf, AP_CTRL_MAC_DEL); + } + break; + + case PRISM2_IOCTL_KICKMAC: + if (!capable(CAP_NET_ADMIN)) ret = -EPERM; + else if (wrq->u.data.pointer) { + char addrbuf[18]; + if (copy_from_user(addrbuf, wrq->u.data.pointer, 18)) { + ret = -EFAULT; + break; + } + ret = ap_mac_ioctl(local, addrbuf, AP_CTRL_MAC_KICK); + } + break; +#endif /* PRISM2_USE_WE_TYPE_ADDR */ +#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ + + + /* Private ioctls that are not used with iwpriv; + * in SIOCDEVPRIVATE range */ + +#if defined(PRISM2_DOWNLOAD_SUPPORT) && WIRELESS_EXT > 8 + case PRISM2_IOCTL_DOWNLOAD: + if (!capable(CAP_NET_ADMIN)) ret = -EPERM; + else ret = prism2_ioctl_priv_download(local, &wrq->u.data); + break; +#endif /* PRISM2_DOWNLOAD_SUPPORT and WIRELESS_EXT > 8 */ + + case PRISM2_IOCTL_HOSTAPD: + if (!capable(CAP_NET_ADMIN)) ret = -EPERM; +#if WIRELESS_EXT > 8 + else ret = prism2_ioctl_priv_hostapd(local, &wrq->u.data); +#else /* WIRELESS_EXT > 8 */ + else ret = prism2_ioctl_priv_hostapd( + local, (struct iw_point *) &wrq->u.data); +#endif /* WIRELESS_EXT > 8 */ + break; + +#endif /* WIRELESS_EXT */ + + default: +#if WIRELESS_EXT > 12 + if (cmd >= SIOCSIWCOMMIT && cmd <= SIOCGIWPOWER) { + /* unsupport wireless extensions get through here - do + * not report these to debug log */ + ret = -EOPNOTSUPP; + break; + } +#endif /* WIRELESS_EXT > 12 */ + printk(KERN_DEBUG "%s unsupported ioctl(0x%04x)\n", + dev->name, cmd); + ret = -EOPNOTSUPP; + break; + } + + return ret; +} Index: drivers/net/wireless/hostap_pci.c --- drivers/net/wireless/hostap_pci.c.orig 2003-08-12 14:39:16.000000000 -0400 +++ drivers/net/wireless/hostap_pci.c 2003-06-23 18:52:56.000000000 -0400 @@ -0,0 +1,415 @@ +#define PRISM2_PCI + +/* Host AP driver's support for Intersil Prism2.5 PCI cards is based on + * driver patches from Reyk Floeter and + * Andy Warner */ + +#include +#include +#include +#include +#include +#include +#include +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,44)) +#include +#else +#include +#endif +#include +#if WIRELESS_EXT > 12 +#include +#endif /* WIRELESS_EXT > 12 */ + +#include +#include + +#include "hostap_wlan.h" + + +static char *version = PRISM2_VERSION " (Jouni Malinen )"; +static char *dev_info = "hostap_pci"; + + +MODULE_AUTHOR("SSH Communications Security Corp, Jouni Malinen"); +MODULE_DESCRIPTION("Support for Intersil Prism2.5-based 802.11 wireless LAN " + "PCI cards."); +MODULE_SUPPORTED_DEVICE("Intersil Prism2.5-based WLAN PCI cards"); +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)) +/* PCI initialization uses Linux 2.4.x version and older kernels do not support + * this */ +#error Prism2.5 PCI version requires at least Linux kernel version 2.4.0 +#endif /* kernel < 2.4.0 */ + + +/* FIX: do we need mb/wmb/rmb with memory operations? */ + + +static struct pci_device_id prism2_pci_id_table[] __devinitdata = { + /* Intersil Prism2.5 ISL3874 11Mb/s WLAN Controller */ + { 0x1260, 0x3873, PCI_ANY_ID, PCI_ANY_ID }, + /* Samsung MagicLAN SWL-2210P */ + { 0x167d, 0xa000, PCI_ANY_ID, PCI_ANY_ID }, + { 0 } +}; + + +#ifdef PRISM2_IO_DEBUG + +static inline void hfa384x_outb_debug(struct net_device *dev, int a, u8 v) +{ + local_info_t *local = dev->priv; + unsigned long flags; + + spin_lock_irqsave(&local->lock, flags); + prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_OUTB, a, v); + writeb(v, dev->mem_start + a); + spin_unlock_irqrestore(&local->lock, flags); +} + +static inline u8 hfa384x_inb_debug(struct net_device *dev, int a) +{ + local_info_t *local = dev->priv; + unsigned long flags; + u8 v; + + spin_lock_irqsave(&local->lock, flags); + v = readb(dev->mem_start + 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) +{ + local_info_t *local = dev->priv; + unsigned long flags; + + spin_lock_irqsave(&local->lock, flags); + prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_OUTW, a, v); + writew(v, dev->mem_start + a); + spin_unlock_irqrestore(&local->lock, flags); +} + +static inline u16 hfa384x_inw_debug(struct net_device *dev, int a) +{ + local_info_t *local = dev->priv; + unsigned long flags; + u16 v; + + spin_lock_irqsave(&local->lock, flags); + v = readw(dev->mem_start + a); + prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INW, a, v); + spin_unlock_irqrestore(&local->lock, flags); + return v; +} + +#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_OUTW_DATA(v,a) hfa384x_outw_debug(dev, (a), cpu_to_le16((v))) +#define HFA384X_INW_DATA(a) (u16) le16_to_cpu(hfa384x_inw_debug(dev, (a))) + +#else /* PRISM2_IO_DEBUG */ + +#define HFA384X_OUTB(v,a) writeb((v), dev->mem_start + (a)) +#define HFA384X_INB(a) (u8) readb(dev->mem_start + (a)) +#define HFA384X_OUTW(v,a) writew((v), dev->mem_start + (a)) +#define HFA384X_INW(a) (u16) readw(dev->mem_start + (a)) +#define HFA384X_OUTW_DATA(v,a) writew(cpu_to_le16(v), dev->mem_start + (a)) +#define HFA384X_INW_DATA(a) (u16) le16_to_cpu(readw(dev->mem_start + (a))) + +#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; + + for ( ; len > 1; len -= 2) + *pos++ = HFA384X_INW_DATA(d_off); + + 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; + + for ( ; len > 1; len -= 2) + HFA384X_OUTW_DATA(*pos++, d_off); + + 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_pci_cor_sreset(local_info_t *local) +{ + struct net_device *dev = local->dev; + + /* linux-wlan-ng uses extremely long hold and settle times for + * COR sreset. A comment in the driver code mentions that the long + * delays appear to be necessary. However, at least IBM 22P6901 seems + * to work fine with shorter delays. + * + * Longer delays can be configured by uncommenting following line: */ +/* #define PRISM2_PCI_USE_LONG_DELAYS */ + +#ifdef PRISM2_PCI_USE_LONG_DELAYS + int i; + + HFA384X_OUTW(0x0080, HFA384X_PCICOR_OFF); + mdelay(250); + + HFA384X_OUTW(0x0, HFA384X_PCICOR_OFF); + mdelay(500); + + /* Wait for f/w to complete initialization (CMD:BUSY == 0) */ + i = 2000000 / 10; + while ((HFA384X_INW(HFA384X_CMD_OFF) & HFA384X_CMD_BUSY) && --i) + udelay(10); + +#else /* PRISM2_PCI_USE_LONG_DELAYS */ + + HFA384X_OUTW(0x0080, HFA384X_PCICOR_OFF); + mdelay(1); + HFA384X_OUTW(0x0, HFA384X_PCICOR_OFF); + mdelay(1); + +#endif /* PRISM2_PCI_USE_LONG_DELAYS */ + + if (HFA384X_INW(HFA384X_CMD_OFF) & HFA384X_CMD_BUSY) { + printk(KERN_DEBUG "%s: COR sreset timeout\n", dev->name); + } +} + + +static struct prism2_helper_functions prism2_pci_funcs = +{ + .card_present = NULL, + .cor_sreset = prism2_pci_cor_sreset, + .dev_open = NULL, + .dev_close = NULL +}; + + +static int prism2_pci_probe(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + unsigned long phymem; + unsigned long mem = 0; + local_info_t *local = NULL; + struct net_device *dev = NULL; + static int cards_found /* = 0 */; + int irq_registered = 0; + + if (pci_enable_device(pdev)) + return -EIO; + + phymem = pci_resource_start(pdev, 0); + + if (!request_mem_region(phymem, pci_resource_len(pdev, 0), "Prism2")) { + printk(KERN_ERR "prism2: Cannot reserve PCI memory region\n"); + goto err_out_disable; + } + + mem = (unsigned long) ioremap(phymem, pci_resource_len(pdev, 0)); + if (!mem) { + printk(KERN_ERR "prism2: Cannot remap PCI memory region\n") ; + goto fail; + } + +#ifdef PRISM2_BUS_MASTER + pci_set_master(pdev); +#endif /* PRISM2_BUS_MASTER */ + + local = prism2_init_local_data(&prism2_pci_funcs, cards_found); + if (local == NULL) + goto fail; + cards_found++; + + dev = local->dev; + + dev->irq = pdev->irq; + dev->mem_start = mem; + dev->mem_end = mem + pci_resource_len(pdev, 0); + + if (prism2_init_dev(local)) + goto fail; + + prism2_pci_cor_sreset(local); + + pci_set_drvdata(pdev, dev); + + if (request_irq(dev->irq, prism2_interrupt, SA_SHIRQ, dev->name, + dev)) { + printk(KERN_WARNING "%s: request_irq failed\n", dev->name); + goto fail; + } else + irq_registered = 1; + + if (prism2_hw_config(dev, 1)) { + printk(KERN_DEBUG "%s: hardware initialization failed\n", + dev_info); + goto fail; + } + + printk(KERN_INFO "%s: Intersil Prism2.5 PCI: " + "mem=0x%lx, irq=%d\n", dev->name, phymem, dev->irq); + + return 0; + + fail: + prism2_free_local_data(local); + + if (irq_registered && dev) + free_irq(dev->irq, dev); + + if (mem) + iounmap((void *) mem); + + release_mem_region(phymem, pci_resource_len(pdev, 0)); + + err_out_disable: +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,4)) + pci_disable_device(pdev); +#endif + + return -ENODEV; +} + + +static void prism2_pci_remove(struct pci_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata(pdev); + local_info_t *local = (local_info_t *) dev->priv; + unsigned long mem_start; + + /* Reset the hardware, and ensure interrupts are disabled. */ + prism2_pci_cor_sreset(local); + hfa384x_disable_interrupts(dev); + + if (dev->irq) + free_irq(dev->irq, dev); + + mem_start = dev->mem_start; + prism2_free_local_data(local); + + iounmap((void *) mem_start); + + release_mem_region(pci_resource_start(pdev, 0), + pci_resource_len(pdev, 0)); + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,4)) + pci_disable_device(pdev); +#endif +} + + +#ifdef CONFIG_PM +static int prism2_pci_suspend(struct pci_dev *pdev, u32 state) +{ + struct net_device *dev = pci_get_drvdata(pdev); + local_info_t *local = (local_info_t *) dev->priv; + + if (netif_running(dev)) { + hostap_netif_stop_queues(dev); + netif_device_detach(dev); + } + prism2_hw_shutdown(dev, 0); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,6)) + pci_save_state(pdev, local->pci_save_state); +#endif +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,4)) + pci_disable_device(pdev); +#endif + pci_set_power_state(pdev, 3); + + return 0; +} + +static int prism2_pci_resume(struct pci_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata(pdev); + local_info_t *local = (local_info_t *) dev->priv; + + pci_enable_device(pdev); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,6)) + pci_restore_state(pdev, local->pci_save_state); +#endif + prism2_hw_config(dev, 0); + if (netif_running(dev)) { + netif_device_attach(dev); + netif_start_queue(dev); + } + + return 0; +} +#endif /* CONFIG_PM */ + + +MODULE_DEVICE_TABLE(pci, prism2_pci_id_table); + +static struct pci_driver prism2_pci_drv_id = { + .name = "prism2_pci", + .id_table = prism2_pci_id_table, + .probe = prism2_pci_probe, + .remove = prism2_pci_remove, +#ifdef CONFIG_PM + .suspend = prism2_pci_suspend, + .resume = prism2_pci_resume, +#endif /* CONFIG_PM */ + /* Linux 2.4.6 added save_state and enable_wake that are not used here + */ +}; + + +static int __init init_prism2_pci(void) +{ + printk(KERN_INFO "%s: %s\n", dev_info, version); + + if (pci_register_driver(&prism2_pci_drv_id) <= 0) { + printk("hostap_pci: No devices found, driver not " + "installed.\n"); + pci_unregister_driver(&prism2_pci_drv_id); + return -ENODEV; + } + + return 0; +} + + +static void __exit exit_prism2_pci(void) +{ + pci_unregister_driver(&prism2_pci_drv_id); + printk(KERN_INFO "%s: Driver unloaded\n", dev_info); +} + + +module_init(init_prism2_pci); +module_exit(exit_prism2_pci); Index: drivers/net/wireless/hostap_plx.c --- drivers/net/wireless/hostap_plx.c.orig 2003-08-12 14:39:16.000000000 -0400 +++ drivers/net/wireless/hostap_plx.c 2003-06-23 18:52:56.000000000 -0400 @@ -0,0 +1,589 @@ +#define PRISM2_PLX + +/* Host AP driver's support for PC Cards on PCI adapters using PLX9052 is + * based on: + * - Host AP driver patch from james@madingley.org + * - linux-wlan-ng driver, Copyright (C) AbsoluteValue Systems, Inc. + */ + + +#include +#include +#include +#include +#include +#include +#include +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,44)) +#include +#else +#include +#endif +#include +#if WIRELESS_EXT > 12 +#include +#endif /* WIRELESS_EXT > 12 */ + +#include +#include + +#include "hostap_wlan.h" + + +static char *version = PRISM2_VERSION " (Jouni Malinen )"; +static char *dev_info = "hostap_plx"; + + +MODULE_AUTHOR("SSH Communications Security Corp, Jouni Malinen"); +MODULE_DESCRIPTION("Support for Intersil Prism2-based 802.11 wireless LAN " + "cards (PLX)."); +MODULE_SUPPORTED_DEVICE("Intersil Prism2-based WLAN cards (PLX)"); +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + + +static int ignore_cis = 0; +MODULE_PARM(ignore_cis, "i"); +MODULE_PARM_DESC(ignore_cis, "Do not verify manfid information in CIS"); + + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)) +/* PCI initialization uses Linux 2.4.x version and older kernels do not support + * this */ +#error PLX9052 version requires at least Linux kernel version 2.4.0 +#endif /* kernel < 2.4.0 */ + + +#define PLX_MIN_ATTR_LEN 512 /* at least 2 x 256 is needed for CIS */ +#define COR_SRESET 0x80 +#define COR_LEVLREQ 0x40 +#define COR_ENABLE_FUNC 0x01 +/* PCI Configuration Registers */ +#define PLX_PCIIPR 0x3d /* PCI Interrupt Pin */ +/* Local Configuration Registers */ +#define PLX_INTCSR 0x4c /* Interrupt Control/Status Register */ +#define PLX_INTCSR_PCI_INTEN BIT(6) /* PCI Interrupt Enable */ +#define PLX_CNTRL 0x50 +#define PLX_CNTRL_SERIAL_EEPROM_PRESENT BIT(28) + + +#define PLXDEV(vendor,dev,str) { vendor, dev, PCI_ANY_ID, PCI_ANY_ID } + +static struct pci_device_id prism2_plx_id_table[] __devinitdata = { + PLXDEV(0x10b7, 0x7770, "3Com AirConnect PCI 777A"), + PLXDEV(0x111a, 0x1023, "Siemens SpeedStream SS1023"), + PLXDEV(0x126c, 0x8030, "Nortel emobility"), + PLXDEV(0x1385, 0x4100, "Netgear MA301"), + PLXDEV(0x15e8, 0x0130, "National Datacomm NCP130 (PLX9052)"), + PLXDEV(0x15e8, 0x0131, "National Datacomm NCP130 (TMD7160)"), + PLXDEV(0x1638, 0x1100, "Eumitcom WL11000"), + PLXDEV(0x16ab, 0x1101, "Global Sun Tech GL24110P (?)"), + PLXDEV(0x16ab, 0x1102, "Linksys WPC11 with WDT11"), + PLXDEV(0x16ab, 0x1103, "Longshine 8031"), + PLXDEV(0x16ec, 0x3685, "US Robotics USR2415"), + PLXDEV(0xec80, 0xec00, "Belkin F5D6000"), + { 0 } +}; + + +/* Array of known Prism2/2.5 PC Card manufactured ids. If your card's manfid + * is not listed here, you will need to add it here to get the driver + * initialized. */ +static struct prism2_plx_manfid { + u16 manfid1, manfid2; +} prism2_plx_known_manfids[] = { + { 0x000b, 0x7300 } /* Philips 802.11b WLAN PCMCIA */, + { 0x0101, 0x0777 } /* 3Com AirConnect PCI 777A */, + { 0x0126, 0x8000 } /* Proxim RangeLAN */, + { 0x0138, 0x0002 } /* Compaq WL100 */, + { 0x0156, 0x0002 } /* Intersil Prism II Ref. Design (and others) */, + { 0x026f, 0x030b } /* Buffalo WLI-CF-S11G */, + { 0x0274, 0x1612 } /* Linksys WPC11 Ver 2.5 */, + { 0x0274, 0x1613 } /* Linksys WPC11 Ver 3 */, + { 0x028a, 0x0002 } /* D-Link DRC-650 */, + { 0x0250, 0x0002 } /* Samsung SWL2000-N */, + { 0xc250, 0x0002 } /* EMTAC A2424i */, + { 0xd601, 0x0002 } /* Z-Com XI300 */, + { 0xd601, 0x0005 } /* Zcomax XI-325H 200mW */, + { 0, 0} +}; + + +#ifdef PRISM2_IO_DEBUG + +static inline void hfa384x_outb_debug(struct net_device *dev, int a, u8 v) +{ + local_info_t *local = dev->priv; + unsigned long flags; + + 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) +{ + local_info_t *local = dev->priv; + unsigned long flags; + u8 v; + + 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) +{ + local_info_t *local = dev->priv; + unsigned long flags; + + 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) +{ + local_info_t *local = dev->priv; + unsigned long flags; + u16 v; + + 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) +{ + local_info_t *local = dev->priv; + unsigned long flags; + + 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) +{ + local_info_t *local = dev->priv; + unsigned long flags; + + 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_plx_cor_sreset(local_info_t *local) +{ + unsigned char corsave; + + printk(KERN_DEBUG "%s: Doing reset via direct COR access.\n", + dev_info); + + /* Set sreset bit of COR and clear it after hold time */ + + if (local->attr_mem == 0) { + /* TMD7160 - COR at card's first I/O addr */ + corsave = inb(local->cor_offset); + outb(corsave | COR_SRESET, local->cor_offset); + mdelay(1); + outb(corsave & ~COR_SRESET, local->cor_offset); + mdelay(1); + } else { + /* PLX9052 */ + corsave = readb(local->attr_mem + local->cor_offset); + writeb(corsave | COR_SRESET, + local->attr_mem + local->cor_offset); + mdelay(1); + writeb(corsave & ~COR_SRESET, + local->attr_mem + local->cor_offset); + mdelay(1); + } +} + + +static struct prism2_helper_functions prism2_plx_funcs = +{ + .card_present = NULL, + .cor_sreset = prism2_plx_cor_sreset, + .dev_open = NULL, + .dev_close = NULL +}; + + +static int prism2_plx_check_cis(unsigned long attr_mem, int attr_len, + unsigned int *cor_offset, + unsigned int *cor_index) +{ +#define CISTPL_CONFIG 0x1A +#define CISTPL_MANFID 0x20 +#define CISTPL_END 0xFF +#define CIS_MAX_LEN 256 + u8 cis[CIS_MAX_LEN]; + int i, pos; + unsigned int rmsz, rasz, manfid1, manfid2; + struct prism2_plx_manfid *manfid; + + /* read CIS; it is in even offsets in the beginning of attr_mem */ + for (i = 0; i < CIS_MAX_LEN; i++) + cis[i] = readb(attr_mem + 2 * i); + printk(KERN_DEBUG "%s: CIS: %02x %02x %02x %02x %02x %02x ...\n", + dev_info, cis[0], cis[1], cis[2], cis[3], cis[4], cis[5]); + + /* set reasonable defaults for Prism2 cards just in case CIS parsing + * fails */ + *cor_offset = 0x3e0; + *cor_index = 0x01; + manfid1 = manfid2 = 0; + + pos = 0; + while (pos < CIS_MAX_LEN - 1 && cis[pos] != CISTPL_END) { + if (pos + cis[pos + 1] >= CIS_MAX_LEN) + goto cis_error; + + switch (cis[pos]) { + case CISTPL_CONFIG: + if (cis[pos + 1] < 1) + goto cis_error; + rmsz = (cis[pos + 2] & 0x3c) >> 2; + rasz = cis[pos + 2] & 0x03; + if (4 + rasz + rmsz > cis[pos + 1]) + goto cis_error; + *cor_index = cis[pos + 3] & 0x3F; + *cor_offset = 0; + for (i = 0; i <= rasz; i++) + *cor_offset += cis[pos + 4 + i] << (8 * i); + printk(KERN_DEBUG "%s: cor_index=0x%x " + "cor_offset=0x%x\n", dev_info, + *cor_index, *cor_offset); + if (*cor_offset > attr_len) { + printk(KERN_ERR "%s: COR offset not within " + "attr_mem\n", dev_info); + return -1; + } + break; + + case CISTPL_MANFID: + if (cis[pos + 1] < 4) + goto cis_error; + manfid1 = cis[pos + 2] + (cis[pos + 3] << 8); + manfid2 = cis[pos + 4] + (cis[pos + 5] << 8); + printk(KERN_DEBUG "%s: manfid=0x%04x, 0x%04x\n", + dev_info, manfid1, manfid2); + break; + } + + pos += cis[pos + 1] + 2; + } + + if (pos >= CIS_MAX_LEN || cis[pos] != CISTPL_END) + goto cis_error; + + for (manfid = prism2_plx_known_manfids; manfid->manfid1 != 0; manfid++) + if (manfid1 == manfid->manfid1 && manfid2 == manfid->manfid2) + return 0; + + printk(KERN_INFO "%s: unknown manfid 0x%04x, 0x%04x - assuming this is" + " not supported card\n", dev_info, manfid1, manfid2); + goto fail; + + cis_error: + printk(KERN_WARNING "%s: invalid CIS data\n", dev_info); + + fail: + if (ignore_cis) { + printk(KERN_INFO "%s: ignore_cis parameter set - ignoring " + "errors during CIS verification\n", dev_info); + return 0; + } + return -1; +} + + +static int prism2_plx_probe(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + unsigned int pccard_ioaddr, plx_ioaddr; + unsigned long pccard_attr_mem; + unsigned int pccard_attr_len; + unsigned long attr_mem = 0; + unsigned int cor_offset, cor_index; + u32 reg; + local_info_t *local = NULL; + struct net_device *dev = NULL; + static int cards_found /* = 0 */; + int irq_registered = 0; + int tmd7160; + + if (pci_enable_device(pdev)) + return -EIO; + + /* National Datacomm NCP130 based on TMD7160, not PLX9052. */ + tmd7160 = (pdev->vendor == 0x15e8) && (pdev->device == 0x0131); + + plx_ioaddr = pci_resource_start(pdev, 1); + pccard_ioaddr = pci_resource_start(pdev, tmd7160 ? 2 : 3); + + if (tmd7160) { + /* TMD7160 */ + attr_mem = 0; /* no access to PC Card attribute memory */ + + printk(KERN_INFO "TMD7160 PCI/PCMCIA adapter: io=0x%x, " + "irq=%d, pccard_io=0x%x\n", + plx_ioaddr, pdev->irq, pccard_ioaddr); + + cor_offset = plx_ioaddr; + cor_index = 0x04; + + outb(cor_index | COR_LEVLREQ | COR_ENABLE_FUNC, plx_ioaddr); + mdelay(1); + reg = inb(plx_ioaddr); + if (reg != (cor_index | COR_LEVLREQ | COR_ENABLE_FUNC)) { + printk(KERN_ERR "%s: Error setting COR (expected=" + "0x%02x, was=0x%02x)\n", dev_info, + cor_index | COR_LEVLREQ | COR_ENABLE_FUNC, reg); + goto fail; + } + } else { + /* PLX9052 */ + pccard_attr_mem = pci_resource_start(pdev, 2); + pccard_attr_len = pci_resource_len(pdev, 2); + if (pccard_attr_len < PLX_MIN_ATTR_LEN) + goto fail; + + + attr_mem = (unsigned long) ioremap(pccard_attr_mem, + pccard_attr_len); + if (!attr_mem) { + printk(KERN_ERR "%s: cannot remap attr_mem\n", + dev_info); + goto fail; + } + + printk(KERN_INFO "PLX9052 PCI/PCMCIA adapter: " + "mem=0x%lx, plx_io=0x%x, irq=%d, pccard_io=0x%x\n", + pccard_attr_mem, plx_ioaddr, pdev->irq, pccard_ioaddr); + + if (prism2_plx_check_cis(attr_mem, pccard_attr_len, + &cor_offset, &cor_index)) { + printk(KERN_INFO "Unknown PC Card CIS - not a " + "Prism2/2.5 card?\n"); + goto fail; + } + + printk(KERN_DEBUG "Prism2/2.5 PC Card detected in PLX9052 " + "adapter\n"); + + /* Write COR to enable PC Card */ + writeb(cor_index | COR_LEVLREQ | COR_ENABLE_FUNC, + attr_mem + cor_offset); + + /* Enable PCI interrupts if they are not already enabled */ + reg = inl(plx_ioaddr + PLX_INTCSR); + printk(KERN_DEBUG "PLX_INTCSR=0x%x\n", reg); + if (!(reg & PLX_INTCSR_PCI_INTEN)) { + outl(reg | PLX_INTCSR_PCI_INTEN, + plx_ioaddr + PLX_INTCSR); + if (!(inl(plx_ioaddr + PLX_INTCSR) & + PLX_INTCSR_PCI_INTEN)) { + printk(KERN_WARNING "%s: Could not enable " + "Local Interrupts\n", dev_info); + goto fail; + } + } + + reg = inl(plx_ioaddr + PLX_CNTRL); + printk(KERN_DEBUG "PLX_CNTRL=0x%x (Serial EEPROM " + "present=%d)\n", + reg, (reg & PLX_CNTRL_SERIAL_EEPROM_PRESENT) != 0); + /* should set PLX_PCIIPR to 0x01 (INTA#) if Serial EEPROM is + * not present; but are there really such cards in use(?) */ + } + + local = prism2_init_local_data(&prism2_plx_funcs, cards_found); + if (local == NULL) + goto fail; + cards_found++; + + dev = local->dev; + + dev->irq = pdev->irq; + dev->base_addr = pccard_ioaddr; + local->attr_mem = attr_mem; + local->cor_offset = cor_offset; + + if (prism2_init_dev(local)) + goto fail; + + pci_set_drvdata(pdev, dev); + + if (request_irq(dev->irq, prism2_interrupt, SA_SHIRQ, dev->name, + dev)) { + printk(KERN_WARNING "%s: request_irq failed\n", dev->name); + goto fail; + } else + irq_registered = 1; + + if (prism2_hw_config(dev, 1)) { + printk(KERN_DEBUG "%s: hardware initialization failed\n", + dev_info); + goto fail; + } + + return 0; + + fail: + prism2_free_local_data(local); + + if (irq_registered && dev) + free_irq(dev->irq, dev); + + if (attr_mem) + iounmap((void *) attr_mem); + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,4)) + pci_disable_device(pdev); +#endif + + return -ENODEV; +} + + +static void prism2_plx_remove(struct pci_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata(pdev); + local_info_t *local = (local_info_t *) dev->priv; + + /* Reset the hardware, and ensure interrupts are disabled. */ + prism2_plx_cor_sreset(local); + hfa384x_disable_interrupts(dev); + + if (local->attr_mem) + iounmap((void *) local->attr_mem); + if (dev->irq) + free_irq(dev->irq, dev); + + prism2_free_local_data(local); + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,4)) + pci_disable_device(pdev); +#endif +} + + +MODULE_DEVICE_TABLE(pci, prism2_plx_id_table); + +static struct pci_driver prism2_plx_drv_id = { + .name = "prism2_plx", + .id_table = prism2_plx_id_table, + .probe = prism2_plx_probe, + .remove = prism2_plx_remove, +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,6)) + .save_state = NULL, + .suspend = NULL, + .resume = NULL, + .enable_wake = NULL +#else /* Linux < 2.4.6 */ + .suspend = NULL, + .resume = NULL +#endif /* Linux >= 2.4.6 */ +}; + + +static int __init init_prism2_plx(void) +{ + printk(KERN_INFO "%s: %s\n", dev_info, version); + + if (pci_register_driver(&prism2_plx_drv_id) <= 0) { + printk("hostap_plx: No devices found, driver not " + "installed.\n"); + pci_unregister_driver(&prism2_plx_drv_id); + return -ENODEV; + } + + return 0; +} + + +static void __exit exit_prism2_plx(void) +{ + pci_unregister_driver(&prism2_plx_drv_id); + printk(KERN_INFO "%s: Driver unloaded\n", dev_info); +} + + +module_init(init_prism2_plx); +module_exit(exit_prism2_plx); Index: drivers/net/wireless/hostap_proc.c --- drivers/net/wireless/hostap_proc.c.orig 2003-08-12 14:39:16.000000000 -0400 +++ drivers/net/wireless/hostap_proc.c 2003-06-23 18:52:56.000000000 -0400 @@ -0,0 +1,322 @@ +/* /proc routines for Host AP driver */ + +#define PROC_LIMIT (PAGE_SIZE - 80) + + +#ifndef PRISM2_NO_PROCFS_DEBUG +static int prism2_debug_proc_read(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + char *p = page; + local_info_t *local = (local_info_t *) data; + int i; + + if (off != 0) { + *eof = 1; + return 0; + } + + p += sprintf(p, "next_txfid=%d next_alloc=%d\n", + local->next_txfid, local->next_alloc); + for (i = 0; i < PRISM2_TXFID_COUNT; i++) + p += sprintf(p, "FID: tx=%04X intransmit=%04X\n", + local->txfid[i], local->intransmitfid[i]); + p += sprintf(p, "FW TX rate control: %d\n", local->fw_tx_rate_control); + p += sprintf(p, "beacon_int=%d\n", local->beacon_int); + p += sprintf(p, "dtim_period=%d\n", local->dtim_period); + p += sprintf(p, "wds_max_connections=%d\n", + local->wds_max_connections); + p += sprintf(p, "dev_enabled=%d\n", local->dev_enabled); + p += sprintf(p, "sw_tick_stuck=%d\n", local->sw_tick_stuck); + if (local->crypt && local->crypt->ops) + p += sprintf(p, "crypt=%s\n", local->crypt->ops->name); + + return (p - page); +} +#endif /* PRISM2_NO_PROCFS_DEBUG */ + + +static int prism2_stats_proc_read(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + char *p = page; + local_info_t *local = (local_info_t *) data; + struct comm_tallies_sums *sums = (struct comm_tallies_sums *) + &local->comm_tallies; + + if (off != 0) { + *eof = 1; + return 0; + } + + p += sprintf(p, "TxUnicastFrames=%u\n", sums->tx_unicast_frames); + p += sprintf(p, "TxMulticastframes=%u\n", sums->tx_multicast_frames); + p += sprintf(p, "TxFragments=%u\n", sums->tx_fragments); + p += sprintf(p, "TxUnicastOctets=%u\n", sums->tx_unicast_octets); + p += sprintf(p, "TxMulticastOctets=%u\n", sums->tx_multicast_octets); + p += sprintf(p, "TxDeferredTransmissions=%u\n", + sums->tx_deferred_transmissions); + p += sprintf(p, "TxSingleRetryFrames=%u\n", + sums->tx_single_retry_frames); + p += sprintf(p, "TxMultipleRetryFrames=%u\n", + sums->tx_multiple_retry_frames); + p += sprintf(p, "TxRetryLimitExceeded=%u\n", + sums->tx_retry_limit_exceeded); + p += sprintf(p, "TxDiscards=%u\n", sums->tx_discards); + p += sprintf(p, "RxUnicastFrames=%u\n", sums->rx_unicast_frames); + p += sprintf(p, "RxMulticastFrames=%u\n", sums->rx_multicast_frames); + p += sprintf(p, "RxFragments=%u\n", sums->rx_fragments); + p += sprintf(p, "RxUnicastOctets=%u\n", sums->rx_unicast_octets); + p += sprintf(p, "RxMulticastOctets=%u\n", sums->rx_multicast_octets); + p += sprintf(p, "RxFCSErrors=%u\n", sums->rx_fcs_errors); + p += sprintf(p, "RxDiscardsNoBuffer=%u\n", + sums->rx_discards_no_buffer); + p += sprintf(p, "TxDiscardsWrongSA=%u\n", sums->tx_discards_wrong_sa); + p += sprintf(p, "RxDiscardsWEPUndecryptable=%u\n", + sums->rx_discards_wep_undecryptable); + p += sprintf(p, "RxMessageInMsgFragments=%u\n", + sums->rx_message_in_msg_fragments); + p += sprintf(p, "RxMessageInBadMsgFragments=%u\n", + sums->rx_message_in_bad_msg_fragments); + /* FIX: this may grow too long for one page(?) */ + + return (p - page); +} + + +static int prism2_wds_proc_read(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + char *p = page; + local_info_t *local = (local_info_t *) data; + prism2_wds_info_t *wds; + unsigned long flags; + + if (off > PROC_LIMIT) { + *eof = 1; + return 0; + } + + spin_lock_irqsave(&local->wdslock, flags); + wds = local->wds; + while (wds != NULL) { + p += sprintf(p, "%s\t" MACSTR "\n", + wds->dev.name, MAC2STR(wds->remote_addr)); + if ((p - page) > PROC_LIMIT) { + printk(KERN_DEBUG "%s: wds proc did not fit\n", + local->dev->name); + break; + } + wds = wds->next; + } + spin_unlock_irqrestore(&local->wdslock, flags); + + if ((p - page) <= off) { + *eof = 1; + return 0; + } + + *start = page + off; + + return (p - page - off); +} + + +static int prism2_pda_proc_read(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + local_info_t *local = (local_info_t *) data; + + if (local->pda == NULL || off >= PRISM2_PDA_SIZE) { + *eof = 1; + return 0; + } + + if (off + count > PRISM2_PDA_SIZE) + count = PRISM2_PDA_SIZE - off; + + memcpy(page, local->pda + off, count); + return count; +} + + +#ifdef PRISM2_IO_DEBUG +static int prism2_io_debug_proc_read(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + local_info_t *local = (local_info_t *) data; + int head = local->io_debug_head; + int start_bytes, left, copy, copied; + + if (off + count > PRISM2_IO_DEBUG_SIZE * 4) { + *eof = 1; + if (off >= PRISM2_IO_DEBUG_SIZE * 4) + return 0; + count = PRISM2_IO_DEBUG_SIZE * 4 - off; + } + + copied = 0; + start_bytes = (PRISM2_IO_DEBUG_SIZE - head) * 4; + left = count; + + if (off < start_bytes) { + copy = start_bytes - off; + if (copy > count) + copy = count; + memcpy(page, ((u8 *) &local->io_debug[head]) + off, copy); + left -= copy; + if (left > 0) + memcpy(&page[copy], local->io_debug, left); + } else { + memcpy(page, ((u8 *) local->io_debug) + (off - start_bytes), + left); + } + + *start = page; + + return count; +} +#endif /* PRISM2_IO_DEBUG */ + + +#ifndef PRISM2_NO_STATION_MODES +static int prism2_scan_results_proc_read(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + char *p = page; + local_info_t *local = (local_info_t *) data; + int entry, i, len, total = 0; + unsigned long flags; + + spin_lock_irqsave(&local->lock, flags); + p += sprintf(p, "Total ScanResults: %d\n", + local->last_scan_results_count); + for (entry = 0; entry < local->last_scan_results_count; entry++) { + struct hfa384x_scan_result *scanres = + &local->last_scan_results[entry]; + + if (total + (p - page) <= off) { + total += p - page; + p = page; + } + if (total + (p - page) > off + count) + break; + if ((p - page) > (PAGE_SIZE - 200)) + break; + + p += sprintf(p, "ScanResult %d:\n" + " CHID=%d ANL=%d SL=%d BcnInt=%d Capab=0x%02x " + "Rate=%d\n" + " bssid=" MACSTR " SSID='", + entry, + le16_to_cpu(scanres->chid), + le16_to_cpu(scanres->anl), + le16_to_cpu(scanres->sl), + le16_to_cpu(scanres->beacon_interval), + le16_to_cpu(scanres->capability), + le16_to_cpu(scanres->rate), + MAC2STR(scanres->bssid)); + len = le16_to_cpu(scanres->ssid_len); + if (len > 32) + len = 32; + for (i = 0; i < len; i++) { + unsigned char c = scanres->ssid[i]; + if (c >= 32 && c < 127) + p += sprintf(p, "%c", c); + else + p += sprintf(p, "<%02x>", c); + } + p += sprintf(p, "'\n" + " SupRates="); + for (i = 0; i < sizeof(scanres->sup_rates); i++) { + if (scanres->sup_rates[i] == 0) + break; + p += sprintf(p, "%02x ", scanres->sup_rates[i]); + } + p += sprintf(p, "\n"); + } + spin_unlock_irqrestore(&local->lock, flags); + + total += (p - page); + if (total >= off + count) + *eof = 1; + + if (total < off) { + *eof = 1; + return 0; + } + + len = total - off; + if (len > (p - page)) + len = p - page; + *start = p - len; + if (len > count) + len = count; + + return len; +} +#endif /* PRISM2_NO_STATION_MODES */ + + +void hostap_init_proc(local_info_t *local) +{ + local->proc = NULL; + + if (hostap_proc == NULL) { + printk(KERN_WARNING "%s: hostap proc directory not created\n", + local->dev->name); + return; + } + + local->proc = proc_mkdir(local->dev->name, hostap_proc); + if (local->proc == NULL) { + printk(KERN_INFO "/proc/net/hostap/%s creation failed\n", + local->dev->name); + return; + } + +#ifndef PRISM2_NO_PROCFS_DEBUG + create_proc_read_entry("debug", 0, local->proc, + prism2_debug_proc_read, local); +#endif /* PRISM2_NO_PROCFS_DEBUG */ + create_proc_read_entry("stats", 0, local->proc, + prism2_stats_proc_read, local); + create_proc_read_entry("wds", 0, local->proc, + prism2_wds_proc_read, local); + create_proc_read_entry("pda", 0, local->proc, + prism2_pda_proc_read, local); +#ifdef PRISM2_IO_DEBUG + create_proc_read_entry("io_debug", 0, local->proc, + prism2_io_debug_proc_read, local); +#endif /* PRISM2_IO_DEBUG */ +#ifndef PRISM2_NO_STATION_MODES + create_proc_read_entry("scan_results", 0, local->proc, + prism2_scan_results_proc_read, local); +#endif /* PRISM2_NO_STATION_MODES */ +} + + +void hostap_remove_proc(local_info_t *local) +{ + if (local->proc != NULL) { +#ifndef PRISM2_NO_STATION_MODES + remove_proc_entry("scan_results", local->proc); +#endif /* PRISM2_NO_STATION_MODES */ +#ifdef PRISM2_IO_DEBUG + remove_proc_entry("io_debug", local->proc); +#endif /* PRISM2_IO_DEBUG */ + remove_proc_entry("pda", local->proc); + remove_proc_entry("wds", local->proc); + remove_proc_entry("stats", local->proc); +#ifndef PRISM2_NO_PROCFS_DEBUG + remove_proc_entry("debug", local->proc); +#endif /* PRISM2_NO_PROCFS_DEBUG */ + if (local->dev != NULL && local->dev->name != NULL && + hostap_proc != NULL) + remove_proc_entry(local->dev->name, hostap_proc); + } +} + + +EXPORT_SYMBOL(hostap_init_proc); +EXPORT_SYMBOL(hostap_remove_proc); Index: drivers/net/wireless/hostap_wlan.h --- drivers/net/wireless/hostap_wlan.h.orig 2003-08-12 14:39:16.000000000 -0400 +++ drivers/net/wireless/hostap_wlan.h 2003-07-21 02:00:22.000000000 -0400 @@ -0,0 +1,1487 @@ +#ifndef HOSTAP_WLAN_H +#define HOSTAP_WLAN_H + +#include "hostap_config.h" +#ifdef __KERNEL__ +#include "hostap_compat.h" +#endif +#include "hostap_crypt.h" + +#define MAX_PARM_DEVICES 8 +#define PARM_MIN_MAX "1-" __MODULE_STRING(MAX_PARM_DEVICES) +#define DEF_INTS -1, -1, -1, -1, -1, -1, -1 +#define GET_INT_PARM(var,idx) var[var[idx] < 0 ? 0 : idx] + + +#define BIT(x) (1 << (x)) + +/* Specific skb->protocol value that indicates that the packet already contains + * txdesc header. + * FIX: This might need own value that would be allocated especially for Prism2 + * txdesc; ETH_P_CONTROL is commented as "Card specific control frames". + * However, these skb's should have only minimal path in the kernel side since + * prism2_send_mgmt() sends these with dev_queue_xmit() to prism2_tx(). */ +#define ETH_P_HOSTAP ETH_P_CONTROL + +#ifndef ETH_P_PAE +#define ETH_P_PAE 0x888E /* Port Access Entity (IEEE 802.1X) */ +#endif /* ETH_P_PAE */ + +#ifndef ARPHRD_IEEE80211 +#define ARPHRD_IEEE80211 801 +#endif +#ifndef ARPHRD_IEEE80211_PRISM +#define ARPHRD_IEEE80211_PRISM 802 +#endif + +/* ARPHRD_IEEE80211_PRISM uses a bloated version of Prism2 RX frame header + * (from linux-wlan-ng) */ +struct linux_wlan_ng_val { + u32 did; + u16 status, len; + u32 data; +} __attribute__ ((packed)); + +struct linux_wlan_ng_prism_hdr { + u32 msgcode, msglen; + char devname[16]; + struct linux_wlan_ng_val hosttime, mactime, channel, rssi, sq, signal, + noise, rate, istx, frmlen; +} __attribute__ ((packed)); + +struct linux_wlan_ng_cap_hdr { + u32 version; + u32 length; + u64 mactime; + u64 hosttime; + u32 phytype; + u32 channel; + u32 datarate; + u32 antenna; + u32 priority; + u32 ssi_type; + s32 ssi_signal; + s32 ssi_noise; + u32 preamble; + u32 encoding; +} __attribute__ ((packed)); + +#define LWNG_CAP_DID_BASE (4 | (1 << 6)) /* section 4, group 1 */ +#define LWNG_CAPHDR_VERSION 0x80211001 + +struct hostap_ieee80211_hdr { + u16 frame_control; + u16 duration_id; + u8 addr1[6]; + u8 addr2[6]; + u8 addr3[6]; + u16 seq_ctrl; + u8 addr4[6]; +} __attribute__ ((packed)); + + +struct hfa384x_rx_frame { + /* HFA384X RX frame descriptor */ + u16 status; /* HFA384X_RX_STATUS_ flags */ + u32 time; /* timestamp, 1 microsecond resolution */ + u8 silence; /* 27 .. 154; seems to be 0 */ + u8 signal; /* 27 .. 154 */ + u8 rate; /* 10, 20, 55, or 110 */ + u8 rxflow; + u32 reserved; + + /* 802.11 */ + u16 frame_control; + u16 duration_id; + u8 addr1[6]; + u8 addr2[6]; + u8 addr3[6]; + u16 seq_ctrl; + u8 addr4[6]; + u16 data_len; + + /* 802.3 */ + u8 dst_addr[6]; + u8 src_addr[6]; + u16 len; + + /* followed by frame data; max 2304 bytes */ +} __attribute__ ((packed)); + + +struct hfa384x_tx_frame { + /* HFA384X TX frame descriptor */ + u16 status; /* HFA384X_TX_STATUS_ flags */ + u16 reserved1; + u16 reserved2; + u32 sw_support; + u8 retry_count; /* not yet implemented */ + u8 tx_rate; /* Host AP only; 0 = firmware, or 10, 20, 55, 110 */ + u16 tx_control; /* HFA384X_TX_CTRL_ flags */ + + /* 802.11 */ + u16 frame_control; /* parts not used */ + u16 duration_id; + u8 addr1[6]; + u8 addr2[6]; /* filled by firmware */ + u8 addr3[6]; + u16 seq_ctrl; /* filled by firmware */ + u8 addr4[6]; + u16 data_len; + + /* 802.3 */ + u8 dst_addr[6]; + u8 src_addr[6]; + u16 len; + + /* followed by frame data; max 2304 bytes */ +} __attribute__ ((packed)); + + +struct hfa384x_rid_hdr +{ + u16 len; + u16 rid; +} __attribute__ ((packed)); + + +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)); + +/* Macro for converting signal levels (range 27 .. 154) to wireless ext + * dBm value with some accuracy */ +#define HFA384X_LEVEL_TO_dBm(v) 0x100 + (v) * 100 / 255 - 100 + +/* Macro for converting signal/silence levels (RSSI) from RX descriptor to + * dBm */ +#define HFA384X_RSSI_LEVEL_TO_dBm(v) ((v) - 100) + +struct hfa384x_scan_request { + u16 channel_list; + u16 txrate; /* HFA384X_RATES_* */ +} __attribute__ ((packed)); + +struct hfa384x_hostscan_request { + u16 channel_list; + u16 txrate; + u16 target_ssid_len; + u8 target_ssid[32]; +} __attribute__ ((packed)); + +struct hfa384x_join_request { + u8 bssid[6]; + u16 channel; +} __attribute__ ((packed)); + +struct hfa384x_info_frame { + u16 len; + u16 type; +} __attribute__ ((packed)); + +struct hfa384x_comm_tallies { + u16 tx_unicast_frames; + u16 tx_multicast_frames; + u16 tx_fragments; + u16 tx_unicast_octets; + u16 tx_multicast_octets; + u16 tx_deferred_transmissions; + u16 tx_single_retry_frames; + u16 tx_multiple_retry_frames; + u16 tx_retry_limit_exceeded; + u16 tx_discards; + u16 rx_unicast_frames; + u16 rx_multicast_frames; + u16 rx_fragments; + u16 rx_unicast_octets; + u16 rx_multicast_octets; + u16 rx_fcs_errors; + u16 rx_discards_no_buffer; + u16 tx_discards_wrong_sa; + u16 rx_discards_wep_undecryptable; + u16 rx_message_in_msg_fragments; + u16 rx_message_in_bad_msg_fragments; +} __attribute__ ((packed)); + +struct hfa384x_scan_result_hdr { + u16 reserved; + u16 scan_reason; +#define HFA384X_SCAN_IN_PROGRESS 0 /* no results available yet */ +#define HFA384X_SCAN_HOST_INITIATED 1 +#define HFA384X_SCAN_FIRMWARE_INITIATED 2 +#define HFA384X_SCAN_INQUIRY_FROM_HOST 3 +} __attribute__ ((packed)); + +#define HFA384X_SCAN_MAX_RESULTS 32 + +struct hfa384x_scan_result { + u16 chid; + u16 anl; + u16 sl; + u8 bssid[6]; + u16 beacon_interval; + u16 capability; + u16 ssid_len; + u8 ssid[32]; + u8 sup_rates[10]; + u16 rate; +} __attribute__ ((packed)); + +struct hfa384x_hostscan_result { + u16 chid; + u16 anl; + u16 sl; + u8 bssid[6]; + u16 beacon_interval; + u16 capability; + u16 ssid_len; + u8 ssid[32]; + u8 sup_rates[10]; + u16 rate; + u16 atim; +} __attribute__ ((packed)); + +struct comm_tallies_sums { + unsigned int tx_unicast_frames; + unsigned int tx_multicast_frames; + unsigned int tx_fragments; + unsigned int tx_unicast_octets; + unsigned int tx_multicast_octets; + unsigned int tx_deferred_transmissions; + unsigned int tx_single_retry_frames; + unsigned int tx_multiple_retry_frames; + unsigned int tx_retry_limit_exceeded; + unsigned int tx_discards; + unsigned int rx_unicast_frames; + unsigned int rx_multicast_frames; + unsigned int rx_fragments; + unsigned int rx_unicast_octets; + unsigned int rx_multicast_octets; + unsigned int rx_fcs_errors; + unsigned int rx_discards_no_buffer; + unsigned int tx_discards_wrong_sa; + unsigned int rx_discards_wep_undecryptable; + unsigned int rx_message_in_msg_fragments; + unsigned int rx_message_in_bad_msg_fragments; +}; + + +struct hfa384x_regs { + u16 cmd; + u16 evstat; + u16 offset0; + u16 offset1; + u16 swsupport0; +}; + + +#if defined(PRISM2_PCCARD) || defined(PRISM2_PLX) +/* I/O ports for HFA384X Controller access */ +#define HFA384X_CMD_OFF 0x00 +#define HFA384X_PARAM0_OFF 0x02 +#define HFA384X_PARAM1_OFF 0x04 +#define HFA384X_PARAM2_OFF 0x06 +#define HFA384X_STATUS_OFF 0x08 +#define HFA384X_RESP0_OFF 0x0A +#define HFA384X_RESP1_OFF 0x0C +#define HFA384X_RESP2_OFF 0x0E +#define HFA384X_INFOFID_OFF 0x10 +#define HFA384X_CONTROL_OFF 0x14 +#define HFA384X_SELECT0_OFF 0x18 +#define HFA384X_SELECT1_OFF 0x1A +#define HFA384X_OFFSET0_OFF 0x1C +#define HFA384X_OFFSET1_OFF 0x1E +#define HFA384X_RXFID_OFF 0x20 +#define HFA384X_ALLOCFID_OFF 0x22 +#define HFA384X_TXCOMPLFID_OFF 0x24 +#define HFA384X_SWSUPPORT0_OFF 0x28 +#define HFA384X_SWSUPPORT1_OFF 0x2A +#define HFA384X_SWSUPPORT2_OFF 0x2C +#define HFA384X_EVSTAT_OFF 0x30 +#define HFA384X_INTEN_OFF 0x32 +#define HFA384X_EVACK_OFF 0x34 +#define HFA384X_DATA0_OFF 0x36 +#define HFA384X_DATA1_OFF 0x38 +#define HFA384X_AUXPAGE_OFF 0x3A +#define HFA384X_AUXOFFSET_OFF 0x3C +#define HFA384X_AUXDATA_OFF 0x3E +#endif /* PRISM2_PCCARD || PRISM2_PLX */ + +#ifdef PRISM2_PCI +/* Memory addresses for ISL3874 controller access */ +#define HFA384X_CMD_OFF 0x00 +#define HFA384X_PARAM0_OFF 0x04 +#define HFA384X_PARAM1_OFF 0x08 +#define HFA384X_PARAM2_OFF 0x0C +#define HFA384X_STATUS_OFF 0x10 +#define HFA384X_RESP0_OFF 0x14 +#define HFA384X_RESP1_OFF 0x18 +#define HFA384X_RESP2_OFF 0x1C +#define HFA384X_INFOFID_OFF 0x20 +#define HFA384X_CONTROL_OFF 0x28 +#define HFA384X_SELECT0_OFF 0x30 +#define HFA384X_SELECT1_OFF 0x34 +#define HFA384X_OFFSET0_OFF 0x38 +#define HFA384X_OFFSET1_OFF 0x3C +#define HFA384X_RXFID_OFF 0x40 +#define HFA384X_ALLOCFID_OFF 0x44 +#define HFA384X_TXCOMPLFID_OFF 0x48 +#define HFA384X_PCICOR_OFF 0x4C +#define HFA384X_SWSUPPORT0_OFF 0x50 +#define HFA384X_SWSUPPORT1_OFF 0x54 +#define HFA384X_SWSUPPORT2_OFF 0x58 +#define HFA384X_PCIHCR_OFF 0x5C +#define HFA384X_EVSTAT_OFF 0x60 +#define HFA384X_INTEN_OFF 0x64 +#define HFA384X_EVACK_OFF 0x68 +#define HFA384X_DATA0_OFF 0x6C +#define HFA384X_DATA1_OFF 0x70 +#define HFA384X_AUXPAGE_OFF 0x74 +#define HFA384X_AUXOFFSET_OFF 0x78 +#define HFA384X_AUXDATA_OFF 0x7C +#define HFA384X_PCI_M0_ADDRH_OFF 0x80 +#define HFA384X_PCI_M0_ADDRL_OFF 0x84 +#define HFA384X_PCI_M0_LEN_OFF 0x88 +#define HFA384X_PCI_M0_CTL_OFF 0x8C +#define HFA384X_PCI_STATUS_OFF 0x98 +#define HFA384X_PCI_M1_ADDRH_OFF 0xA0 +#define HFA384X_PCI_M1_ADDRL_OFF 0xA4 +#define HFA384X_PCI_M1_LEN_OFF 0xA8 +#define HFA384X_PCI_M1_CTL_OFF 0xAC + +/* PCI bus master control bits (these are undocumented; based on guessing and + * experimenting..) */ +#define HFA384X_PCI_CTL_FROM_BAP (BIT(5) | BIT(1) | BIT(0)) +#define HFA384X_PCI_CTL_TO_BAP (BIT(5) | BIT(0)) + +#endif /* PRISM2_PCI */ + + +/* Command codes for CMD reg. */ +#define HFA384X_CMDCODE_INIT 0x00 +#define HFA384X_CMDCODE_ENABLE 0x01 +#define HFA384X_CMDCODE_DISABLE 0x02 +#define HFA384X_CMDCODE_ALLOC 0x0A +#define HFA384X_CMDCODE_TRANSMIT 0x0B +#define HFA384X_CMDCODE_INQUIRE 0x11 +#define HFA384X_CMDCODE_ACCESS 0x21 +#define HFA384X_CMDCODE_ACCESS_WRITE (0x21 | BIT(8)) +#define HFA384X_CMDCODE_DOWNLOAD 0x22 +#define HFA384X_CMDCODE_READMIF 0x30 +#define HFA384X_CMDCODE_WRITEMIF 0x31 +#define HFA384X_CMDCODE_TEST 0x38 + +#define HFA384X_CMDCODE_MASK 0x3F + +/* Test mode operations */ +#define HFA384X_TEST_CHANGE_CHANNEL 0x08 +#define HFA384X_TEST_MONITOR 0x0B +#define HFA384X_TEST_STOP 0x0F +#define HFA384X_TEST_CFG_BITS 0x15 +#define HFA384X_TEST_CFG_BIT_ALC BIT(3) + +#define HFA384X_CMD_BUSY BIT(15) + +#define HFA384X_CMD_TX_RECLAIM BIT(8) + +#define HFA384X_OFFSET_ERR BIT(14) +#define HFA384X_OFFSET_BUSY BIT(15) + + +/* ProgMode for download command */ +#define HFA384X_PROGMODE_DISABLE 0 +#define HFA384X_PROGMODE_ENABLE_VOLATILE 1 +#define HFA384X_PROGMODE_ENABLE_NON_VOLATILE 2 +#define HFA384X_PROGMODE_PROGRAM_NON_VOLATILE 3 + +#define HFA384X_AUX_MAGIC0 0xfe01 +#define HFA384X_AUX_MAGIC1 0xdc23 +#define HFA384X_AUX_MAGIC2 0xba45 + +#define HFA384X_AUX_PORT_DISABLED 0 +#define HFA384X_AUX_PORT_DISABLE BIT(14) +#define HFA384X_AUX_PORT_ENABLE BIT(15) +#define HFA384X_AUX_PORT_ENABLED (BIT(14) | BIT(15)) +#define HFA384X_AUX_PORT_MASK (BIT(14) | BIT(15)) + +#define PRISM2_PDA_SIZE 1024 + + +/* Events; EvStat, Interrupt mask (IntEn), and acknowledge bits (EvAck) */ +#define HFA384X_EV_TICK BIT(15) +#define HFA384X_EV_WTERR BIT(14) +#define HFA384X_EV_INFDROP BIT(13) +#ifdef PRISM2_PCI +#define HFA384X_EV_PCI_M1 BIT(9) +#define HFA384X_EV_PCI_M0 BIT(8) +#endif /* PRISM2_PCI */ +#define HFA384X_EV_INFO BIT(7) +#define HFA384X_EV_DTIM BIT(5) +#define HFA384X_EV_CMD BIT(4) +#define HFA384X_EV_ALLOC BIT(3) +#define HFA384X_EV_TXEXC BIT(2) +#define HFA384X_EV_TX BIT(1) +#define HFA384X_EV_RX BIT(0) + + +/* 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_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_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_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 + +/* HFA384X Information frames */ +#define HFA384X_INFO_HANDOVERADDR 0xF000 /* AP f/w ? */ +#define HFA384X_INFO_HANDOVERDEAUTHADDR 0xF001 /* AP f/w 1.3.7 */ +#define HFA384X_INFO_COMMTALLIES 0xF100 +#define HFA384X_INFO_SCANRESULTS 0xF101 +#define HFA384X_INFO_CHANNELINFORESULTS 0xF102 /* AP f/w only */ +#define HFA384X_INFO_HOSTSCANRESULTS 0xF103 +#define HFA384X_INFO_LINKSTATUS 0xF200 +#define HFA384X_INFO_ASSOCSTATUS 0xF201 /* ? */ +#define HFA384X_INFO_AUTHREQ 0xF202 /* ? */ +#define HFA384X_INFO_PSUSERCNT 0xF203 /* ? */ +#define HFA384X_INFO_KEYIDCHANGED 0xF204 /* ? */ + +enum { HFA384X_LINKSTATUS_CONNECTED = 1, + HFA384X_LINKSTATUS_DISCONNECTED = 2, + HFA384X_LINKSTATUS_AP_CHANGE = 3, + HFA384X_LINKSTATUS_AP_OUT_OF_RANGE = 4, + HFA384X_LINKSTATUS_AP_IN_RANGE = 5, + HFA384X_LINKSTATUS_ASSOC_FAILED = 6 }; + +enum { HFA384X_PORTTYPE_BSS = 1, HFA384X_PORTTYPE_WDS = 2, + HFA384X_PORTTYPE_PSEUDO_IBSS = 3, HFA384X_PORTTYPE_IBSS = 0, + HFA384X_PORTTYPE_HOSTAP = 6 }; + +#define HFA384X_RATES_1MBPS BIT(0) +#define HFA384X_RATES_2MBPS BIT(1) +#define HFA384X_RATES_5MBPS BIT(2) +#define HFA384X_RATES_11MBPS BIT(3) + +#define HFA384X_ROAMING_FIRMWARE 1 +#define HFA384X_ROAMING_HOST 2 +#define HFA384X_ROAMING_DISABLED 3 + +#define HFA384X_WEPFLAGS_PRIVACYINVOKED BIT(0) +#define HFA384X_WEPFLAGS_EXCLUDEUNENCRYPTED BIT(1) +#define HFA384X_WEPFLAGS_HOSTENCRYPT BIT(4) +#define HFA384X_WEPFLAGS_HOSTDECRYPT BIT(7) + +#define HFA384X_RX_STATUS_MSGTYPE (BIT(15) | BIT(14) | BIT(13)) +#define HFA384X_RX_STATUS_PCF BIT(12) +#define HFA384X_RX_STATUS_MACPORT (BIT(10) | BIT(9) | BIT(8)) +#define HFA384X_RX_STATUS_UNDECR BIT(1) +#define HFA384X_RX_STATUS_FCSERR BIT(0) + +#define HFA384X_RX_STATUS_GET_MSGTYPE(s) \ +(((s) & HFA384X_RX_STATUS_MSGTYPE) >> 13) +#define HFA384X_RX_STATUS_GET_MACPORT(s) \ +(((s) & HFA384X_RX_STATUS_MACPORT) >> 8) + +enum { HFA384X_RX_MSGTYPE_NORMAL = 0, HFA384X_RX_MSGTYPE_RFC1042 = 1, + HFA384X_RX_MSGTYPE_BRIDGETUNNEL = 2, HFA384X_RX_MSGTYPE_MGMT = 4 }; + + +#define HFA384X_TX_CTRL_ALT_RTRY BIT(5) +#define HFA384X_TX_CTRL_802_11 BIT(3) +#define HFA384X_TX_CTRL_802_3 0 +#define HFA384X_TX_CTRL_TX_EX BIT(2) +#define HFA384X_TX_CTRL_TX_OK BIT(1) + +#define HFA384X_TX_STATUS_RETRYERR BIT(0) +#define HFA384X_TX_STATUS_AGEDERR BIT(1) +#define HFA384X_TX_STATUS_DISCON BIT(2) +#define HFA384X_TX_STATUS_FORMERR BIT(3) + +/* HFA3861/3863 (BBP) Control Registers */ +#define HFA386X_CR_TX_CONFIGURE 0x12 +#define HFA386X_CR_RX_CONFIGURE 0x14 +#define HFA386X_CR_A_D_TEST_MODES2 0x1A +#define HFA386X_CR_MANUAL_TX_POWER 0x3E + +/* 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_FC_GET_TYPE(fc) (((fc) & (BIT(3) | BIT(2))) >> 2) +#define WLAN_FC_GET_STYPE(fc) \ + (((fc) & (BIT(7) | BIT(6) | BIT(5) | BIT(4))) >> 4) + +#define WLAN_GET_SEQ_FRAG(seq) ((seq) & (BIT(3) | BIT(2) | BIT(1) | BIT(0))) +#define WLAN_GET_SEQ_SEQ(seq) \ + (((seq) & (~(BIT(3) | BIT(2) | BIT(1) | BIT(0)))) >> 4) + +#define WLAN_FC_TYPE_MGMT 0 +#define WLAN_FC_TYPE_CTRL 1 +#define WLAN_FC_TYPE_DATA 2 + +/* management */ +#define WLAN_FC_STYPE_ASSOC_REQ 0 +#define WLAN_FC_STYPE_ASSOC_RESP 1 +#define WLAN_FC_STYPE_REASSOC_REQ 2 +#define WLAN_FC_STYPE_REASSOC_RESP 3 +#define WLAN_FC_STYPE_PROBE_REQ 4 +#define WLAN_FC_STYPE_PROBE_RESP 5 +#define WLAN_FC_STYPE_BEACON 8 +#define WLAN_FC_STYPE_ATIM 9 +#define WLAN_FC_STYPE_DISASSOC 10 +#define WLAN_FC_STYPE_AUTH 11 +#define WLAN_FC_STYPE_DEAUTH 12 + +/* control */ +#define WLAN_FC_STYPE_PSPOLL 10 +#define WLAN_FC_STYPE_RTS 11 +#define WLAN_FC_STYPE_CTS 12 +#define WLAN_FC_STYPE_ACK 13 +#define WLAN_FC_STYPE_CFEND 14 +#define WLAN_FC_STYPE_CFENDACK 15 + +/* data */ +#define WLAN_FC_STYPE_DATA 0 +#define WLAN_FC_STYPE_DATA_CFACK 1 +#define WLAN_FC_STYPE_DATA_CFPOLL 2 +#define WLAN_FC_STYPE_DATA_CFACKPOLL 3 +#define WLAN_FC_STYPE_NULLFUNC 4 +#define WLAN_FC_STYPE_CFACK 5 +#define WLAN_FC_STYPE_CFPOLL 6 +#define WLAN_FC_STYPE_CFACKPOLL 7 + +/* Authentication algorithms */ +#define WLAN_AUTH_OPEN 0 +#define WLAN_AUTH_SHARED_KEY 1 + +#define WLAN_AUTH_CHALLENGE_LEN 128 + +#define WLAN_CAPABILITY_ESS BIT(0) +#define WLAN_CAPABILITY_IBSS BIT(1) +#define WLAN_CAPABILITY_CF_POLLABLE BIT(2) +#define WLAN_CAPABILITY_CF_POLL_REQUEST BIT(3) +#define WLAN_CAPABILITY_PRIVACY BIT(4) + +/* Status codes */ +#define WLAN_STATUS_SUCCESS 0 +#define WLAN_STATUS_UNSPECIFIED_FAILURE 1 +#define WLAN_STATUS_CAPS_UNSUPPORTED 10 +#define WLAN_STATUS_REASSOC_NO_ASSOC 11 +#define WLAN_STATUS_ASSOC_DENIED_UNSPEC 12 +#define WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG 13 +#define WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION 14 +#define WLAN_STATUS_CHALLENGE_FAIL 15 +#define WLAN_STATUS_AUTH_TIMEOUT 16 +#define WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA 17 +#define WLAN_STATUS_ASSOC_DENIED_RATES 18 +/* 802.11b */ +#define WLAN_STATUS_ASSOC_DENIED_NOSHORT 19 +#define WLAN_STATUS_ASSOC_DENIED_NOPBCC 20 +#define WLAN_STATUS_ASSOC_DENIED_NOAGILITY 21 + +/* Reason codes */ +#define WLAN_REASON_UNSPECIFIED 1 +#define WLAN_REASON_PREV_AUTH_NOT_VALID 2 +#define WLAN_REASON_DEAUTH_LEAVING 3 +#define WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY 4 +#define WLAN_REASON_DISASSOC_AP_BUSY 5 +#define WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA 6 +#define WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA 7 +#define WLAN_REASON_DISASSOC_STA_HAS_LEFT 8 +#define WLAN_REASON_STA_REQ_ASSOC_WITHOUT_AUTH 9 + + +/* 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 MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5] +#define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x" + + +/* netdevice private ioctls (used, e.g., with iwpriv from user space) */ + +#if WIRELESS_EXT >= 12 + +/* 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) + +#else /* WIRELESS_EXT >= 12 */ + +/* Old wireless extensions API - check permission in the driver code */ +#define PRISM2_IOCTL_MONITOR (SIOCDEVPRIVATE) +#define PRISM2_IOCTL_PRISM2_PARAM (SIOCDEVPRIVATE + 1) +#define PRISM2_IOCTL_READMIF (SIOCDEVPRIVATE + 2) +#define PRISM2_IOCTL_WRITEMIF (SIOCDEVPRIVATE + 3) +#define PRISM2_IOCTL_RESET (SIOCDEVPRIVATE + 4) +#define PRISM2_IOCTL_INQUIRE (SIOCDEVPRIVATE + 5) +#define PRISM2_IOCTL_WDS_ADD (SIOCDEVPRIVATE + 6) +#define PRISM2_IOCTL_WDS_DEL (SIOCDEVPRIVATE + 7) +#define PRISM2_IOCTL_SET_RID_WORD (SIOCDEVPRIVATE + 8) +#define PRISM2_IOCTL_MACCMD (SIOCDEVPRIVATE + 9) +#define PRISM2_IOCTL_ADDMAC (SIOCDEVPRIVATE + 10) +#define PRISM2_IOCTL_DELMAC (SIOCDEVPRIVATE + 11) +#define PRISM2_IOCTL_KICKMAC (SIOCDEVPRIVATE + 12) +#define PRISM2_IOCTL_DOWNLOAD (SIOCDEVPRIVATE + 13) +#define PRISM2_IOCTL_HOSTAPD (SIOCDEVPRIVATE + 14) + +#endif /* WIRELESS_EXT >= 12 */ + + +/* PRISM2_IOCTL_PRISM2_PARAM ioctl() subtypes: */ +enum { + PRISM2_PARAM_PTYPE = 1, + PRISM2_PARAM_TXRATECTRL = 2, + PRISM2_PARAM_BEACON_INT = 3, + PRISM2_PARAM_PSEUDO_IBSS = 4, + PRISM2_PARAM_ALC = 5, + PRISM2_PARAM_TXPOWER = 6, + PRISM2_PARAM_DUMP = 7, + PRISM2_PARAM_OTHER_AP_POLICY = 8, + PRISM2_PARAM_AP_MAX_INACTIVITY = 9, + PRISM2_PARAM_AP_BRIDGE_PACKETS = 10, + PRISM2_PARAM_DTIM_PERIOD = 11, + PRISM2_PARAM_AP_NULLFUNC_ACK = 12, + PRISM2_PARAM_MAX_WDS = 13, + PRISM2_PARAM_AP_AUTOM_AP_WDS = 14, + PRISM2_PARAM_AP_AUTH_ALGS = 15, + PRISM2_PARAM_MONITOR_ALLOW_FCSERR = 16, + PRISM2_PARAM_HOST_ENCRYPT = 17, + PRISM2_PARAM_HOST_DECRYPT = 18, + PRISM2_PARAM_BUS_MASTER_THRESHOLD_RX = 19, + PRISM2_PARAM_BUS_MASTER_THRESHOLD_TX = 20, + PRISM2_PARAM_HOST_ROAMING = 21, + PRISM2_PARAM_BCRX_STA_KEY = 22, + PRISM2_PARAM_IEEE_802_1X = 23, + PRISM2_PARAM_ANTSEL_TX = 24, + PRISM2_PARAM_ANTSEL_RX = 25, + PRISM2_PARAM_MONITOR_TYPE = 26, + PRISM2_PARAM_WDS_TYPE = 27, + PRISM2_PARAM_HOSTSCAN = 28, + PRISM2_PARAM_AP_SCAN = 29, + PRISM2_PARAM_ENH_SEC = 30, + PRISM2_PARAM_IO_DEBUG = 31, + PRISM2_PARAM_BASIC_RATES = 32, + PRISM2_PARAM_OPER_RATES = 33, + PRISM2_PARAM_HOSTAPD = 34, +}; + +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 */ +}; + +struct prism2_download_param { + u32 dl_cmd; + u32 start_addr; + u32 num_areas; + struct prism2_download_area { + u32 addr; /* wlan card address */ + u32 len; + caddr_t 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, + 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, +}; + +#define PRISM2_HOSTAPD_MAX_BUF_SIZE 1024 +#define PRISM2_HOSTAPD_RID_HDR_LEN \ +((int) (&((struct prism2_hostapd_param *) 0)->u.rid.data)) + +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; + u32 txexc; + } get_info_sta; + struct { + u8 alg[HOSTAP_CRYPT_ALG_NAME_LEN]; + u32 flags; + u32 err; + u8 idx; + 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; + } 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 + + +#ifdef __KERNEL__ + +#define PRISM2_TXFID_COUNT 7 +#define PRISM2_TXSKB_COUNT 100 +#define PRISM2_DATA_MAXLEN 2304 +#define PRISM2_TXFID_LEN (PRISM2_DATA_MAXLEN + sizeof(struct hfa384x_tx_frame)) +#define PRISM2_TXFID_EMPTY 0xffff +#define PRISM2_TXFID_RESERVED 0xfffe +#define PRISM2_DUMMY_FID 0xffff +#define MAX_SSID_LEN 32 +#define MAX_NAME_LEN 32 /* this is assumed to be equal to MAX_SSID_LEN */ + +#define PRISM2_DUMP_RX_HDR BIT(0) +#define PRISM2_DUMP_TX_HDR BIT(1) +#define PRISM2_DUMP_TXEXC_HDR BIT(2) + +typedef struct prism2_wds_info prism2_wds_info_t; + +struct prism2_wds_info { + /* must start with dev, since it is used also as a pointer to whole + * prism2_wds_info structure */ + struct net_device dev; + u8 remote_addr[6]; + struct net_device_stats stats; + prism2_wds_info_t *next; +}; + + +struct hostap_tx_callback_info { + u16 idx; + void (*func)(struct sk_buff *, int ok, void *); + void *data; + struct hostap_tx_callback_info *next; +}; + + +/* IEEE 802.11 requires that STA supports concurrent reception of at least + * three fragmented frames. This define can be increased to support more + * concurrent frames, but it should be noted that each entry can consume about + * 2 kB of RAM and increasing cache size will slow down frame reassembly. */ +#define PRISM2_FRAG_CACHE_LEN 4 + +struct prism2_frag_entry { + unsigned long first_frag_time; + unsigned int seq; + unsigned int last_frag; + struct sk_buff *skb; + u8 src_addr[ETH_ALEN]; + u8 dst_addr[ETH_ALEN]; +}; + + +struct prism2_crypt_data { + struct list_head list; /* delayed deletion list */ + struct hostap_crypto_ops *ops; + void *priv; + atomic_t refcnt; +}; + +struct hostap_cmd_queue { + struct list_head list; + wait_queue_head_t compl; + volatile enum { CMD_SLEEP, CMD_CALLBACK, CMD_COMPLETED } type; + void (*callback)(struct net_device *dev, void *context, u16 resp0, + u16 res); + void *context; + u16 cmd, param0, param1; + u16 resp0, res; + volatile int issued, issuing; + + atomic_t usecnt; + int del_req; +}; + +/* prism2_rx_80211 'type' argument */ +enum { + PRISM2_RX_MONITOR, PRISM2_RX_MGMT, PRISM2_RX_NON_ASSOC, + PRISM2_RX_NULLFUNC_ACK +}; + +/* options for hw_shutdown */ +#define HOSTAP_HW_NO_DISABLE BIT(0) +#define HOSTAP_HW_ENABLE_CMDCOMPL BIT(1) + +typedef struct local_info local_info_t; + +struct prism2_helper_functions { + /* these functions are defined in hardware model specific files + * (hostap_{cs,plx,pci}.c */ + int (*card_present)(local_info_t *local); + void (*cor_sreset)(local_info_t *local); + int (*dev_open)(local_info_t *local); + int (*dev_close)(local_info_t *local); + + /* the following functions are from hostap_hw.c, but they may have some + * hardware model specific code */ + + /* FIX: low-level commands like cmd might disappear at some point to + * make it easier to change them if needed (e.g., cmd would be replaced + * with write_mif/read_mif/testcmd/inquire); at least get_rid and + * set_rid might move to hostap_{cs,plx,pci}.c */ + int (*cmd)(struct net_device *dev, u16 cmd, u16 param0, u16 *param1, + u16 *resp0); + void (*read_regs)(struct net_device *dev, struct hfa384x_regs *regs); + int (*get_rid)(struct net_device *dev, u16 rid, void *buf, int len, + int exact_len); + int (*set_rid)(struct net_device *dev, u16 rid, void *buf, int len); + int (*hw_enable)(struct net_device *dev, int initial); + int (*hw_config)(struct net_device *dev, int initial); + void (*hw_reset)(struct net_device *dev); + void (*hw_shutdown)(struct net_device *dev, int no_disable); + int (*reset_port)(struct net_device *dev); + int (*tx)(struct sk_buff *skb, struct net_device *dev); + void (*schedule_reset)(local_info_t *local); +#ifdef PRISM2_DOWNLOAD_SUPPORT + int (*download)(local_info_t *local, + struct prism2_download_param *param); +#endif /* PRISM2_DOWNLOAD_SUPPORT */ + int (*rx_80211)(struct net_device *dev, struct sk_buff *skb, + int type, char *extra, int extra_len); + int (*tx_80211)(struct sk_buff *skb, struct net_device *dev); +}; + +struct local_info { + struct module *hw_module; + int card_idx; + int dev_enabled; + struct net_device *dev; + spinlock_t cmdlock, baplock, wdslock, lock; + struct semaphore rid_bap_sem; + u16 infofid; /* MAC buffer id for info frame */ + /* txfid, intransmitfid, next_txtid, and next_alloc are protected by + * txfidlock */ + spinlock_t txfidlock; + int txfid_len; /* length of allocated TX buffers */ + u16 txfid[PRISM2_TXFID_COUNT]; /* buffer IDs for TX frames */ + /* buffer IDs for intransmit frames or PRISM2_TXFID_EMPTY if + * corresponding txfid is free for next TX frame */ + u16 intransmitfid[PRISM2_TXFID_COUNT]; + int next_txfid; /* index to the next txfid to be checked for + * availability */ + int next_alloc; /* index to the next intransmitfid to be checked for + * allocation events */ + /* start click tx cb */ + struct sk_buff *txskb[PRISM2_TXSKB_COUNT]; + /* endclick tx cb */ + + /* bitfield for atomic bitops */ +#define HOSTAP_BITS_TRANSMIT 0 +#define HOSTAP_BITS_BAP_TASKLET 1 +#define HOSTAP_BITS_BAP_TASKLET2 2 + long bits; + + struct ap_data *ap; + + char essid[MAX_SSID_LEN + 1]; + char name[MAX_NAME_LEN + 1]; + int name_set; + u16 channel_mask; + struct comm_tallies_sums comm_tallies; + struct net_device_stats stats; + struct proc_dir_entry *proc; + int iw_mode; /* operating mode (IW_MODE_*) */ + int pseudo_adhoc; /* 0: IW_MODE_ADHOC is real 802.11 compliant IBSS + * 1: IW_MODE_ADHOC is "pseudo IBSS" */ + char bssid[ETH_ALEN]; + int channel; + int beacon_int; + int dtim_period; + int disable_on_close; + int mtu; + int frame_dump; /* dump RX/TX frame headers, PRISM2_DUMP_ flags */ + int fw_tx_rate_control; + u16 tx_rate_control; + u16 basic_rates; + int hw_resetting; + int hw_ready; + int hw_reset_tries; /* how many times reset has been tried */ + int hw_downloading; + int shutdown; + + enum { + PRISM2_TXPOWER_AUTO = 0, PRISM2_TXPOWER_OFF, + PRISM2_TXPOWER_FIXED, PRISM2_TXPOWER_UNKNOWN + } txpower_type; + u16 txpower; /* if txpower_type == PRISM2_TXPOWER_FIXED */ + + /* command queue for hfa384x_cmd(); protected with cmdlock */ + struct list_head cmd_queue; + /* max_len for cmd_queue; in addition, cmd_callback can use two + * additional entries to prevent sleeping commands from stopping + * transmits */ +#define HOSTAP_CMD_QUEUE_MAX_LEN 16 + int cmd_queue_len; /* number of entries in cmd_queue */ + + /* if card timeout is detected in interrupt context, reset_queue is + * used to schedule card reseting to be done in user context */ + HOSTAP_QUEUE reset_queue; + + /* For scheduling a change of the promiscuous mode RID */ + int is_promisc; + HOSTAP_QUEUE set_multicast_list_queue; + + prism2_wds_info_t *wds; /* list of established wds connections */ + int wds_max_connections; + int wds_connections; +#define HOSTAP_WDS_BROADCAST_RA BIT(0) +#define HOSTAP_WDS_AP_CLIENT BIT(1) +#define HOSTAP_WDS_STANDARD_FRAME BIT(2) + u32 wds_type; + u16 tx_control; /* flags to be used in TX description */ + int manual_retry_count; /* -1 = use f/w default; otherwise retry count + * to be used with all frames */ + +#ifdef WIRELESS_EXT + struct iw_statistics wstats; +#if WIRELESS_EXT > 13 + unsigned long scan_timestamp; /* Time started to scan */ +#endif /* WIRELESS_EXT > 13 */ +#if WIRELESS_EXT > 15 + struct iw_spy_data spy_data; /* iwspy support */ +#endif /* WIRELESS_EXT > 15 */ +#endif /* WIRELESS_EXT */ + enum { + PRISM2_MONITOR_80211 = 0, PRISM2_MONITOR_PRISM = 1, + PRISM2_MONITOR_CAPHDR = 2 + } monitor_type; + int (*saved_eth_header_parse)(struct sk_buff *skb, + unsigned char *haddr); + int monitor_allow_fcserr; + + int hostapd; /* whether user space daemon, hostapd, is used for AP + * management */ + struct net_device *apdev; + struct net_device_stats apdevstats; + + char assoc_ap_addr[ETH_ALEN]; + struct net_device *stadev; + struct net_device_stats stadevstats; + + struct prism2_crypt_data *crypt; + struct timer_list crypt_deinit_timer; + struct list_head crypt_deinit_list; + +#define WEP_KEYS 4 +#define WEP_KEY_LEN 13 + int open_wep; /* allow unencrypted frames */ + int host_encrypt; + int host_decrypt; + int fw_encrypt_ok; /* whether firmware-based WEP encrypt is working + * in Host AP mode (STA f/w 1.4.9 or newer) */ + int bcrx_sta_key; /* use individual keys to override default keys even + * with RX of broad/multicast frames */ + + struct prism2_frag_entry frag_cache[PRISM2_FRAG_CACHE_LEN]; + unsigned int frag_next_idx; + + int ieee_802_1x; /* is IEEE 802.1X used */ + + int antsel_tx, antsel_rx; + + int enh_sec; /* cnfEnhSecurity options (broadcast SSID hide/ignore) */ + + struct prism2_helper_functions *func; + + int bus_master_threshold_tx; + int bus_master_threshold_rx; + u8 *bus_m1_buf; + + u8 *pda; + int fw_ap; +#define PRISM2_FW_VER(major, minor, variant) \ +(((major) << 16) | ((minor) << 8) | variant) + u32 sta_fw_ver; + + /* Tasklets for handling hardware IRQ related operations outside hw IRQ + * handler */ + HOSTAP_TASKLET bap_tasklet; + + HOSTAP_TASKLET info_tasklet; + struct sk_buff_head info_list; /* info frames as skb's for + * info_tasklet */ + + HOSTAP_TASKLET tx_callback_tasklet; + struct sk_buff_head tx_callback_list; /* queued TX ACK/no-ACK data + * frames for tx_callback_tasklet + */ + struct hostap_tx_callback_info *tx_callback; /* registered TX callbacks + */ + + HOSTAP_TASKLET rx_tasklet; + struct sk_buff_head rx_list; + + HOSTAP_TASKLET sta_tx_exc_tasklet; + struct sk_buff_head sta_tx_exc_list; + + int host_roaming; + unsigned long last_join_time; /* time of last JoinRequest */ + struct hfa384x_scan_result *last_scan_results; + int last_scan_results_count; + HOSTAP_QUEUE info_queue; + long pending_info; /* bit field of pending info_queue items */ +#define PRISM2_INFO_PENDING_LINKSTATUS 0 +#define PRISM2_INFO_PENDING_SCANRESULTS 1 + int prev_link_status; /* previous received LinkStatus info */ + u8 preferred_ap[6]; /* use this AP if possible */ + +#ifdef PRISM2_CALLBACK + void *callback_data; /* Can be used in callbacks; e.g., allocate + * on enable event and free on disable event. + * Host AP driver code does not touch this. */ +#endif /* PRISM2_CALLBACK */ + + wait_queue_head_t hostscan_wq; + + /* Passive scan in Host AP mode */ + struct timer_list passive_scan_timer; + int passive_scan_interval; /* in seconds, 0 = disabled */ + int passive_scan_channel; + enum { PASSIVE_SCAN_WAIT, PASSIVE_SCAN_LISTEN } passive_scan_state; + + struct timer_list tick_timer; + unsigned long last_tick_timer; + unsigned int sw_tick_stuck; + +#ifdef PRISM2_IO_DEBUG +#define PRISM2_IO_DEBUG_SIZE 10000 + u32 io_debug[PRISM2_IO_DEBUG_SIZE]; + int io_debug_head; + int io_debug_enabled; +#endif /* PRISM2_IO_DEBUG */ + + /* struct local_info is used also in hostap.o that does not define + * any PRISM2_{PCCARD,PLX,PCI}. Make sure that the hardware version + * specific fields are in the end of the struct (these could also be + * moved to void *priv or something like that). */ +#ifdef PRISM2_PCCARD + dev_node_t node; + dev_link_t *link; +#endif /* PRISM2_PCCARD */ + +#ifdef PRISM2_PLX + unsigned long attr_mem; + unsigned int cor_offset; +#endif /* PRISM2_PLX */ + +#ifdef PRISM2_PCI + unsigned long attr_mem; + unsigned int cor_offset; +#ifdef PRISM2_BUS_MASTER + /* bus master for BAP0 (TX) */ + int bus_m0_tx_idx; + u8 *bus_m0_buf; + + /* bus master for BAP1 (RX) */ + struct sk_buff *rx_skb; +#endif /* PRISM2_BUS_MASTER */ +#ifdef CONFIG_PM + u32 pci_save_state[16]; +#endif /* CONFIG_PM */ +#endif /* PRISM2_PCI */ + + /* NOTE! Do not add common entries here after hardware version + * specific blocks. */ +}; + +/* if wireless ext is not supported */ +#ifndef IW_MODE_ADHOC +#define IW_MODE_ADHOC 1 +#endif +#ifndef IW_MODE_INFRA +#define IW_MODE_INFRA 2 +#endif +#ifndef IW_MODE_MASTER +#define IW_MODE_MASTER 3 +#endif +#ifndef IW_MODE_REPEAT +#define IW_MODE_REPEAT 4 +#endif +#ifndef IW_MODE_SECOND +#define IW_MODE_SECOND 5 +#endif +#ifndef IW_MODE_MONITOR +#define IW_MODE_MONITOR 6 +#endif + +#ifndef PRISM2_NO_DEBUG + +#define DEBUG_FID BIT(0) +#define DEBUG_PS BIT(1) +#define DEBUG_FLOW BIT(2) +#define DEBUG_AP BIT(3) +#define DEBUG_HW BIT(4) +#define DEBUG_EXTRA BIT(5) +#define DEBUG_EXTRA2 BIT(6) +#define DEBUG_PS2 BIT(7) +#define DEBUG_MASK (DEBUG_PS | DEBUG_FLOW | DEBUG_AP | DEBUG_HW | \ + DEBUG_EXTRA) +#define PDEBUG(n, args...) \ +do { if ((n) & DEBUG_MASK) printk(KERN_DEBUG args); } while (0) +#define PDEBUG2(n, args...) \ +do { if ((n) & DEBUG_MASK) printk(args); } while (0) + +#else /* PRISM2_NO_DEBUG */ + +#define PDEBUG(n, args...) +#define PDEBUG2(n, args...) + +#endif /* PRISM2_NO_DEBUG */ + +enum { BAP0 = 0, BAP1 = 1 }; + +#define PRISM2_IO_DEBUG_CMD_INB 0 +#define PRISM2_IO_DEBUG_CMD_INW 1 +#define PRISM2_IO_DEBUG_CMD_INSW 2 +#define PRISM2_IO_DEBUG_CMD_OUTB 3 +#define PRISM2_IO_DEBUG_CMD_OUTW 4 +#define PRISM2_IO_DEBUG_CMD_OUTSW 5 +#define PRISM2_IO_DEBUG_CMD_ERROR 6 +#define PRISM2_IO_DEBUG_CMD_INTERRUPT 7 + +#ifdef PRISM2_IO_DEBUG + +#define PRISM2_IO_DEBUG_ENTRY(cmd, reg, value) \ +(((cmd) << 24) | ((reg) << 16) | value) + +static inline void prism2_io_debug_add(struct net_device *dev, int cmd, + int reg, int value) +{ + local_info_t *local = dev->priv; + + if (!local->io_debug_enabled) + return; + + local->io_debug[local->io_debug_head] = jiffies & 0xffffffff; + if (++local->io_debug_head >= PRISM2_IO_DEBUG_SIZE) + local->io_debug_head = 0; + local->io_debug[local->io_debug_head] = + PRISM2_IO_DEBUG_ENTRY(cmd, reg, value); + if (++local->io_debug_head >= PRISM2_IO_DEBUG_SIZE) + local->io_debug_head = 0; +} + + +static inline void prism2_io_debug_error(struct net_device *dev, int err) +{ + local_info_t *local = dev->priv; + unsigned long flags; + + if (!local->io_debug_enabled) + return; + + spin_lock_irqsave(&local->lock, flags); + prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_ERROR, 0, err); + if (local->io_debug_enabled == 1) { + local->io_debug_enabled = 0; + printk(KERN_DEBUG "%s: I/O debug stopped\n", dev->name); + } + spin_unlock_irqrestore(&local->lock, flags); +} + +#else /* PRISM2_IO_DEBUG */ + +static inline void prism2_io_debug_add(struct net_device *dev, int cmd, + int reg, int value) +{ +} + +static inline void prism2_io_debug_error(struct net_device *dev, int err) +{ +} + +#endif /* PRISM2_IO_DEBUG */ + + +#ifdef PRISM2_CALLBACK +enum { + /* Called when card is enabled */ + PRISM2_CALLBACK_ENABLE, + + /* Called when card is disabled */ + PRISM2_CALLBACK_DISABLE, + + /* Called when RX/TX starts/ends */ + PRISM2_CALLBACK_RX_START, PRISM2_CALLBACK_RX_END, + PRISM2_CALLBACK_TX_START, PRISM2_CALLBACK_TX_END +}; +void prism2_callback(local_info_t *local, int event); +#else /* PRISM2_CALLBACK */ +#define prism2_callback(d, e) do { } while (0) +#endif /* PRISM2_CALLBACK */ + +#endif /* __KERNEL__ */ + +#endif /* HOSTAP_WLAN_H */ Index: Documentation/Configure.help --- Documentation/Configure.help.orig 2003-08-12 14:43:15.000000000 -0400 +++ Documentation/Configure.help 2003-06-09 13:28:05.000000000 -0400 @@ -9377,6 +9377,53 @@ say M here and read . The module will be called orinoco_cs.o. +Host AP support for Prism2/2.5/3 IEEE 802.11b +CONFIG_HOSTAP + A driver for 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 for more information about the + Host AP driver configuration and tools. + + 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 modules and they will be called + "hostap.o", "hostap_crypt.o", and "hostap_crypt_wep.o". + +Host AP driver for Prism2/2.5/3 in PLX9052 based PCI adaptors +CONFIG_HOSTAP_PLX + 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.o". + +Host AP driver for Prism2.5 PCI adaptors +CONFIG_HOSTAP_PCI + 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.o". + +Host AP driver for Prism2/2.5/3 PC Cards +CONFIG_HOSTAP_CS + 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.o". + Cisco/Aironet 34X/35X/4500/4800 ISA and PCI cards CONFIG_AIRO This is the standard Linux driver to support Cisco/Aironet ISA and Index: MAINTAINERS --- MAINTAINERS.orig 2003-08-12 14:42:54.000000000 -0400 +++ MAINTAINERS 2003-06-09 13:23:17.000000000 -0400 @@ -664,6 +664,13 @@ L: linux-hippi@sunsite.dk S: Maintained +HOST AP DRIVER +P: Jouni Malinen +M: jkmaline@cc.hut.fi +L: hostap@lists.ssh.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