/* * compatibility.cpp * iwi4965 * * Created by Sean Cross on 2/8/08. * Copyright 2008 __MyCompanyName__. All rights reserved. * osx 10.4 info.plist com.apple.iokit.IONetworkingFamily 1.1.3 com.apple.iokit.IOPCIFamily 1.2 com.apple.iokit.IO80211Family 1.0.0 com.apple.kernel.iokit 6.0 com.apple.kpi.bsd 8.0.0b2 com.apple.kpi.mach 8.0.0b2 com.apple.kpi.unsupported 8.0.0b2 com.apple.kpi.libkern 8.0.0b2 com.apple.kpi.iokit 8.0.0b2 */ #define NO_SPIN_LOCKS 0 #define NO_MUTEX_LOCKS 0 #define IM_HERE_NOW() printf("%s @ %s:%d\n", __FUNCTION__, __FILE__, __LINE__) /*#include #include #include #include #include */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "defines.h" //#include "compatibility.h" #include "firmware/ipw4965.ucode.h" // Note: This, in itself, makes this very much non-reentrant. It's used // primarily when allocating sk_buff entries. static IONetworkController *currentController; #ifdef IO80211_VERSION static IO80211Interface* my_fNetif; #else static IOEthernetInterface* my_fNetif; #endif static IOBasicOutputQueue * fTransmitQueue; static IOWorkLoop * workqueue; static IOInterruptEventSource * fInterruptSrc; static IOInterruptEventSource * DMAInterruptSource; static irqreturn_t (*realHandler)(int, void *); static pci_driver * my_drv; struct pci_dev* my_pci_dev; IOPCIDevice* my_pci_device; IOMemoryMap * my_map; ifnet_t my_fifnet; static int next_thread=0; static int thread_pos=0; static IOLock* thread_lock; static bool is_unloaded=false; #define MAX_MUTEXES 256 static struct mutex *mutexes[MAX_MUTEXES]; unsigned long current_mutex = 0; extern void (*iwl_scan)(struct iwl4965_priv *); struct ieee80211_hw* local_to_hw(struct ieee80211_local *local) { return &local->hw; } /* Getters */ u8 * getMyMacAddr(){ return my_mac_addr; } void setCurController(IONetworkController *tmp){ currentController=tmp; printf("settCurController [OK]\n"); } struct ieee80211_hw * get_my_hw(){ if(my_hw) return my_hw; return NULL; } void * get_my_priv(){ if(my_hw) return my_hw->priv; return NULL; } IOWorkLoop * getWorkLoop(){ if(workqueue) return workqueue; return NULL; } IOInterruptEventSource * getInterruptEventSource(){ if(fInterruptSrc) return fInterruptSrc; return NULL; } IOPCIDevice * getPCIDevice(){ if(my_pci_device) return my_pci_device; return NULL; } IOMemoryMap * getMap(){ if(my_map) return my_map; return NULL; } int netif_running(struct net_device *dev) { if (!my_fNetif || !dev) return 0; if((my_fNetif->getFlags() & IFF_RUNNING)==0) return 0; return 1;//running } /* Setters */ void setfTransmitQueue(IOBasicOutputQueue* fT){ fTransmitQueue=fT; } void setMyfifnet(ifnet_t fifnet){ my_fifnet = fifnet; } void setUnloaded(){ is_unloaded=true; } void setfNetif(IOEthernetInterface* Intf){ my_fNetif=Intf; } #pragma mark Various #pragma mark - #pragma mark Adapt sk_buff functions to mbuf for OS X static inline void __skb_queue_tail(struct sk_buff_head *list,struct sk_buff *newsk) { struct sk_buff *prev, *next; list->qlen++; next = (struct sk_buff *)list; prev = next->prev; newsk->next = next; newsk->prev = prev; next->prev = prev->next = newsk; } /** 1470 * skb_queue_tail - queue a buffer at the list tail 1471 * @list: list to use 1472 * @newsk: buffer to queue 1473 * 1474 * Queue a buffer at the tail of the list. This function takes the 1475 * list lock and can be used safely with other locking &sk_buff functions 1476 * safely. 1477 * 1478 * A buffer cannot be placed on two lists at the same time. 1479 */ void skb_queue_tail(struct sk_buff_head *list, struct sk_buff *newsk) { unsigned long flags; spin_lock_irqsave(&list->lock, flags); __skb_queue_tail(list, newsk); spin_unlock_irqrestore(&list->lock, flags); } static inline struct sk_buff *__skb_dequeue(struct sk_buff_head *list) { struct sk_buff *next, *prev, *result; prev = (struct sk_buff *) list; next = prev->next; result = NULL; if(next != prev) { result = next; next = next->next; list->qlen--; next->prev = prev; prev->next = next; result->next = result->prev = NULL; } return result; } struct sk_buff *skb_dequeue(struct sk_buff_head *list) { unsigned long flags; struct sk_buff *result; spin_lock_irqsave(&list->lock, flags); result = __skb_dequeue(list); spin_unlock_irqrestore(&list->lock, flags); return result; } struct sk_buff *skb_copy( struct sk_buff *skb, gfp_t gfp_mask) { struct sk_buff *skb_copy = (struct sk_buff *)IOMalloc(sizeof(struct sk_buff)); mbuf_copym(skb->mac_data, 0, mbuf_len(skb->mac_data), 1, &skb_copy->mac_data); skb_copy->intf = skb->intf; return skb_copy;//need to check for prev, next } /** * skb_queue_empty - check if a queue is empty * @list: queue head * * Returns true if the queue is empty, false otherwise. */ static inline int skb_queue_empty(const struct sk_buff_head *list) { return list->next == (struct sk_buff *)list; } /** * skb_trim - remove end from a buffer * @skb: buffer to alter * @len: new length * * Cut the length of a buffer down by removing data from the tail. If * the buffer is already under the length specified it is not modified. * The skb must be linear. */ static inline void skb_trim(struct sk_buff *skb, signed int len) { //cut from the end of mbuf if (len>0) mbuf_adj(skb->mac_data, len); else mbuf_adj(skb->mac_data, -len); } static inline void skb_queue_head_init(struct sk_buff_head *list) { spin_lock_init(&list->lock); list->prev = list->next = (struct sk_buff *)list; list->qlen = 0; } static inline struct sk_buff *skb_peek(struct sk_buff_head *list_) { struct sk_buff *list = ((struct sk_buff *)list_)->next; if (list == (struct sk_buff *)list_) list = NULL; return list; } void *skb_push(const struct sk_buff *skb, unsigned int len) { mbuf_prepend(&(((struct sk_buff*)skb)->mac_data),len,MBUF_WAITOK); return mbuf_data(skb->mac_data); } static inline void skb_set_mac_header(struct sk_buff *skb, const int offset) { //need to change skb->mac_data //skb_reset_mac_header(skb); //skb->mac_header += offset; /*u8 et[ETH_ALEN]; memset(et,0,sizeof(et)); mbuf_adj(skb->mac_data, ETH_ALEN); bcopy(et, skb_push(skb, ETH_ALEN), ETH_ALEN);*/ } static inline void skb_set_network_header(struct sk_buff *skb, const int offset) { //need to change skb->mac_data //skb->network_header = skb->data + offset; /*u8 et[ETH_ALEN]; memset(et,0,sizeof(et)); mbuf_adj(skb->mac_data, ETH_ALEN); bcopy(et, skb_push(skb, ETH_ALEN), ETH_ALEN);*/ } int skb_tailroom(const struct sk_buff *skb) { return mbuf_trailingspace(skb->mac_data); } int skb_headroom(const struct sk_buff *skb){ return mbuf_leadingspace(skb->mac_data); } struct sk_buff *skb_clone(const struct sk_buff *skb, unsigned int ignored) { struct sk_buff *skb_copy = (struct sk_buff *)IOMalloc(sizeof(struct sk_buff)); mbuf_copym(skb->mac_data, 0, mbuf_len(skb->mac_data), 1, &skb_copy->mac_data); skb_copy->intf = skb->intf; return skb_copy; } void *skb_data(const struct sk_buff *skb) { return mbuf_data(skb->mac_data); } int skb_set_data(const struct sk_buff *skb, void *data, size_t len) { mbuf_setdata(skb->mac_data,data,len); mbuf_pkthdr_setlen(skb->mac_data,len); mbuf_setlen(skb->mac_data,len); return 0; } int skb_len(const struct sk_buff *skb) { return mbuf_len(skb->mac_data); } void skb_reserve(struct sk_buff *skb, int len) { void *data = (UInt8*)mbuf_data(skb->mac_data) + len; mbuf_setdata(skb->mac_data,data, mbuf_len(skb->mac_data));// m_len is not changed. } void *skb_put(struct sk_buff *skb, unsigned int len) { /*unsigned char *tmp = skb->tail; SKB_LINEAR_ASSERT(skb); skb->tail += len; skb->len += len; return tmp;*/ void *data = (UInt8*)skb_data(skb) + mbuf_len(skb->mac_data); //mbuf_prepend(&skb,len,1); /* no prepend work */ //IWI_DUMP_MBUF(1,skb,len); if(mbuf_trailingspace(skb->mac_data) > len ){ mbuf_setlen(skb->mac_data, mbuf_len(skb->mac_data)+len); if(mbuf_flags(skb->mac_data) & MBUF_PKTHDR) mbuf_pkthdr_setlen(skb->mac_data, mbuf_pkthdr_len(skb->mac_data)+len); } else IOLog("skb_put failded\n"); //IWI_DUMP_MBUF(2,skb,len); return data; } static inline unsigned char *__skb_pull(struct sk_buff *skb, unsigned int len) { //skb->len -= len; //return skb->data += len; mbuf_adj(skb->mac_data,len); return (unsigned char*)skb_data(skb);//added } /** * skb_pull - remove data from the start of a buffer * @skb: buffer to use * @len: amount of data to remove * * This function removes data from the start of a buffer, returning * the memory to the headroom. A pointer to the next data in the buffer * is returned. Once the data has been pulled future pushes will overwrite * the old data. */ static inline unsigned char *skb_pull(struct sk_buff *skb, unsigned int len) { return unlikely(len > skb_len(skb)) ? NULL : __skb_pull(skb, len); } void dev_kfree_skb_any(struct sk_buff *skb) { //need to free prev,next dev_kfree_skb(skb); } void kfree_skb(struct sk_buff *skb){ IONetworkController *intf = (IONetworkController *)skb->intf; if (skb->mac_data) if (!(mbuf_type(skb->mac_data) == MBUF_TYPE_FREE)) intf->freePacket(skb->mac_data); } void dev_kfree_skb(struct sk_buff *skb) { IONetworkController *intf = (IONetworkController *)skb->intf; if (skb->mac_data) if (!(mbuf_type(skb->mac_data) == MBUF_TYPE_FREE)) intf->freePacket(skb->mac_data); skb->mac_data=NULL; } struct sk_buff *__alloc_skb(unsigned int size,gfp_t priority, int fclone, int node) { struct sk_buff *skb = (struct sk_buff *)IOMalloc(sizeof(struct sk_buff)); skb->mac_data = currentController->allocatePacket(size); skb->intf = (void *)currentController; mbuf_setlen(skb->mac_data, 0); mbuf_pkthdr_setlen(skb->mac_data,0); return skb; } #define NET_SKB_PAD 16 static inline struct sk_buff *__dev_alloc_skb(unsigned int length, gfp_t gfp_mask) { //check if work struct sk_buff *skb = alloc_skb(length,1);// + NET_SKB_PAD, 1); // if (likely(skb)) // skb_reserve(skb, NET_SKB_PAD); return skb; } struct sk_buff *dev_alloc_skb(unsigned int length) { return __dev_alloc_skb(length, GFP_ATOMIC); } //added int sysfs_create_group(struct kobject * kobj,const struct attribute_group * grp){ return 0; } /** name not used for the moment device too size error */ int request_firmware(const struct firmware ** firmware_p, const char * name, struct device * device){ struct firmware *firmware; *firmware_p = firmware =(struct firmware*) IOMalloc(sizeof(struct firmware)); firmware->data = (u8*)ipw4965_ucode_raw; firmware->size = sizeof(ipw4965_ucode_raw); //149652;//crappy //load the file "name" in return 0; } void release_firmware ( const struct firmware * fw){ if( fw ) IOFree((void *)fw, sizeof(struct firmware)); return; } void sysfs_remove_group(struct kobject * kobj,const struct attribute_group * grp){ return; } void print_hex_dump(const char *level, const char *prefix_str, int prefix_type, int rowsize, int groupsize, const void *buf, size_t len, bool ascii) { const u8 *ptr = (const u8*)buf; int i, linelen, remaining = len; unsigned char linebuf[200]; if (rowsize != 16 && rowsize != 32) rowsize = 16; for (i = 0; i < len; i += rowsize) { linelen = min(remaining, rowsize); remaining -= rowsize; hex_dump_to_buffer(ptr + i, linelen, rowsize, groupsize, (char*)linebuf, sizeof(linebuf), ascii); switch (prefix_type) { case DUMP_PREFIX_ADDRESS: printk("%s%s%*p: %s\n", level, prefix_str, (int)(2 * sizeof(void *)), ptr + i, linebuf); break; case DUMP_PREFIX_OFFSET: printk("%s%s%.8x: %s\n", level, prefix_str, i, linebuf); break; default: printk("%s%s%s\n", level, prefix_str, linebuf); break; } } } #define hex_asc(x) "0123456789abcdef"[x] #define isascii(c) (((unsigned char)(c))<=0x7f) #define isprint(a) ((a >=' ')&&(a <= '~')) void hex_dump_to_buffer(const void *buf, size_t len, int rowsize,int groupsize, char *linebuf, size_t linebuflen, bool ascii){ const u8 *ptr = (const u8 *)buf; u8 ch; int j, lx = 0; int ascii_column; if (rowsize != 16 && rowsize != 32) rowsize = 16; if (!len) goto nil; if (len > rowsize) // limit to one line at a time len = rowsize; if ((len % groupsize) != 0) // no mixed size output groupsize = 1; switch (groupsize) { case 8: { const u64 *ptr8 = (const u64 *)buf; int ngroups = len / groupsize; for (j = 0; j < ngroups; j++) lx += snprintf(linebuf + lx, linebuflen - lx, "%16.16llx ", (unsigned long long)*(ptr8 + j)); ascii_column = 17 * ngroups + 2; break; } case 4: { const u32 *ptr4 = (const u32 *)buf; int ngroups = len / groupsize; for (j = 0; j < ngroups; j++) lx += snprintf(linebuf + lx, linebuflen - lx, "%8.8x ", *(ptr4 + j)); ascii_column = 9 * ngroups + 2; break; } case 2: { const u16 *ptr2 = (const u16 *)buf; int ngroups = len / groupsize; for (j = 0; j < ngroups; j++) lx += snprintf(linebuf + lx, linebuflen - lx, "%4.4x ", *(ptr2 + j)); ascii_column = 5 * ngroups + 2; break; } default: for (j = 0; (j < rowsize) && (j < len) && (lx + 4) < linebuflen; j++) { ch = ptr[j]; linebuf[lx++] = hex_asc(ch >> 4); linebuf[lx++] = hex_asc(ch & 0x0f); linebuf[lx++] = ' '; } ascii_column = 3 * rowsize + 2; break; } if (!ascii) goto nil; while (lx < (linebuflen - 1) && lx < (ascii_column - 1)) linebuf[lx++] = ' '; for (j = 0; (j < rowsize) && (j < len) && (lx + 2) < linebuflen; j++) linebuf[lx++] = (isascii(ptr[j]) && isprint(ptr[j])) ? ptr[j] : '.'; nil: linebuf[lx++] = '\0'; return; } unsigned long simple_strtoul (const char * cp, char ** endp, unsigned int base){ return 1; } int is_zero_ether_addr ( const u8 * addr){ return !(addr[0] | addr[1] | addr[2] | addr[3] | addr[4] | addr[5]); } /* herre we call the real interuptsHandler from ipw4965 */ void interuptsHandler(){ if(!realHandler){ printf("No Handler defined\n"); return; } //printf("Call the IRQ Handler\n"); (*realHandler)(1,my_hw->priv); } bool DMAFilter(OSObject* obj, IOFilterInterruptEventSource * source) { // check if this interrupt belongs to me int interruptIndex = source->getIntIndex(); if (interruptIndex == 1) { IOLog("Rx DMA Interrupt Filtered\n"); return true;// go ahead and invoke completion routine } /*if (interruptIndex == kIntTxDMA) { IOLog("Tx DMA Interrupt Filtered\n"); return true;// go ahead and invoke completion routine }*/ IOLog("NOT Rx or Tx Interrupt Filtered\n"); return false; } typedef bool ( *Filter)( OSObject *, IOFilterInterruptEventSource *); /* not finish parameter of handler and workqueue */ int request_irq(unsigned int irq, irqreturn_t (*handler)(int, void *), unsigned long irqflags, const char *devname, void *dev_id) { if(fInterruptSrc) return 0; if(!workqueue){ workqueue = IOWorkLoop::workLoop(); if( workqueue ) workqueue->init(); if (!workqueue) { IOLog(" ERR: start - getWorkLoop failed\n"); return -1; } } /* set the handler for intterupts */ realHandler=handler; fInterruptSrc = IOInterruptEventSource::interruptEventSource( currentController, (IOInterruptEventAction)&interuptsHandler,currentController->getProvider() ); if(!fInterruptSrc || (workqueue->addEventSource(fInterruptSrc) != kIOReturnSuccess)) { IOLog(" fInterruptSrc error\n"); } fInterruptSrc->enable(); return 0; } //FIXME: test void enable_int(){ if(fInterruptSrc) fInterruptSrc->enable(); } void disable_int(){ if(fInterruptSrc) fInterruptSrc->disable(); } #pragma mark - #pragma mark mutex and spinlock routines // Code taken almost verbatim from "Kernel Programming Guide: Locks" void mutex_init(struct mutex *new_mutex) { #ifndef NO_MUTEX_LOCKS static int first_alloc = 1; static lck_grp_attr_t *group_attributes; static lck_grp_t *slock_group; static lck_attr_t *lock_attributes; /* allocate lock group attribute and group */ if( first_alloc ) { /* allocate lock group attribute and group */ group_attributes = lck_grp_attr_alloc_init(); lck_grp_attr_setstat(group_attributes); slock_group = lck_grp_alloc_init("80211_mutex_locks", group_attributes); /* Allocate lock attribute */ lock_attributes = lck_attr_alloc_init(); //lck_attr_setdebug(lock_attributes); // set the debug flag //lck_attr_setdefault(lock_attributes); // clear the debug flag first_alloc = 0; } /* Allocate the spin lock */ new_mutex->mlock = lck_mtx_alloc_init(slock_group, lock_attributes); #endif return; } void mutex_lock(struct mutex *new_mtx) { //#ifndef NO_MUTEX_LOCKS //mutexes[current_mutex++] = new_mtx; //if(new_mtx) // lck_mtx_lock(new_mtx->mlock); //#endif return; } void mutex_unlock(struct mutex *new_mtx) { //#ifndef NO_MUTEX_LOCKS //mutexes[current_mutex--] = NULL; //if(new_mtx) // lck_mtx_unlock(new_mtx->mlock); //#endif return; } void spin_lock_init(spinlock_t *new_lock) { #ifndef NO_SPIN_LOCKS static int first_alloc = 1; static lck_grp_attr_t *group_attributes; static lck_grp_t *slock_group; static lck_attr_t *lock_attributes; if( first_alloc ) { /* allocate lock group attribute and group */ group_attributes = lck_grp_attr_alloc_init(); lck_grp_attr_setstat(group_attributes); slock_group = lck_grp_alloc_init("80211_spin_locks", group_attributes); /* Allocate lock attribute */ lock_attributes = lck_attr_alloc_init(); //lck_attr_setdebug(lock_attributes); // set the debug flag //lck_attr_setdefault(lock_attributes); // clear the debug flag first_alloc = 0; } /* Allocate the spin lock */ new_lock->lock = lck_spin_alloc_init(slock_group, lock_attributes); #endif //NO_SPIN_LOCKS return; } void spin_lock(spinlock_t *lock) { #ifndef NO_SPIN_LOCKS //lck_spin_lock(lock->lock); #endif //NO_SPIN_LOCKS //lck_mtx_lock(lock->mlock); return; } void spin_unlock(spinlock_t *lock) { #ifndef NO_SPIN_LOCKS //lck_spin_unlock(lock->lock); #endif //NO_SPIN_LOCKS //lck_mtx_unlock(lock->mlock); return; } void spin_lock_irqsave(spinlock_t *lock, int fl) { //disable_int(); spin_lock(lock); return; } #define typecheck(type,x) \ ({ type __dummy; \ typeof(x) __dummy2; \ (void)(&__dummy == &__dummy2); \ 1; \ }) void spin_unlock_irqrestore(spinlock_t *lock, int fl) { spin_unlock(lock); //enable_int(); return; } //http://hira.main.jp/wiki/pukiwiki.php?spin_lock_bh()%2Flinux2.6 void spin_lock_bh( spinlock_t *lock ) { spin_lock(lock); return; } void spin_unlock_bh( spinlock_t *lock ) { spin_unlock(lock); return; } #pragma mark - #pragma mark timer adaptation static thread_call_t timer_func[99]; int timer_func_count=0; void IOPCCardAddTimer(struct timer_list2 * timer) { if (!timer->on) { IOLog("timer not on\n"); return; } thread_call_cancel(timer_func[timer->vv]); uint64_t deadline, timei; if (timer->expires>0) timei=jiffies_to_msecs(timer->expires); else timei=0; clock_interval_to_deadline(timei,kMillisecondScale,&deadline); //IOLog("timer->expires %d timei %d deadline %d\n",timer->expires,timei,deadline); thread_call_enter1_delayed(timer_func[timer->vv],(void*)timer->data,deadline); } void test_timer(struct timer_list2 * timer,unsigned long data){ if(timer && data) { if(timer->on) { (timer->function)((unsigned long)data); IOPCCardAddTimer(timer); } else IOLog("timer is off\n"); } else IOLog("Error while launching timer thread\n"); } int IOPCCardDeleteTimer(struct timer_list2 * timer) { if (!timer->on) return 0; thread_call_cancel(timer_func[timer->vv]); timer->on=0; return 0; } int add_timer(struct timer_list2 *timer) { IOPCCardAddTimer(timer); return 0; } int del_timer(struct timer_list2 *timer) { IOPCCardDeleteTimer(timer); return 0; } void init_timer(struct timer_list2 *timer) { //timer=(struct timer_list2*)IOMalloc(sizeof(struct timer_list2*)); timer_func_count++; timer->vv=timer_func_count; timer->on=1; timer_func[timer->vv]=thread_call_allocate((thread_call_func_t)test_timer,(void*)timer); } int mod_timer(struct timer_list2 *timer, int length) { del_timer(timer); timer->expires = length; timer->on=1; add_timer(timer); } int del_timer_sync(struct timer_list2 *timer) { del_timer(timer); } int in_interrupt() { return 0; } void *dev_get_drvdata(void *p) { return p; } #pragma mark - #pragma mark Adapt 80211 functions to OS X void netif_device_attach(struct net_device *dev) { #warning Begin network device here } void netif_device_detach(struct net_device *dev) { #warning Stop network device here } void netif_start_queue(struct net_device *dev) { #warning Start queue here } void netif_wake_queue(struct net_device *dev) { #warning Wake queue here } void __netif_schedule(struct net_device *dev) { #warning Schedule queue here } bool netif_queue_stopped(struct net_device *dev) { #warning Check for stopped queue here return 0; } /* Perform netif operations on all configured interfaces */ int ieee80211_netif_oper(struct ieee80211_hw *hw, Netif_Oper op) { IM_HERE_NOW(); struct ieee80211_local *local = hw_to_local(hw); struct net_device *dev = local->mdev; switch (op) { case NETIF_ATTACH: netif_device_attach(dev); break; case NETIF_DETACH: netif_device_detach(dev); break; case NETIF_START: netif_start_queue(dev); break; case NETIF_STOP: break; case NETIF_WAKE: if (local->scan.in_scan == 0) { netif_wake_queue(dev); #if 1 if (/* FIX: 802.11 qdisc in use */ 1) __netif_schedule(dev); #endif } break; case NETIF_IS_STOPPED: if (netif_queue_stopped(dev)) return 1; break; case NETIF_UPDATE_TX_START: dev->trans_start = jiffies; break; } return 0; } int ieee80211_rate_control_register(struct rate_control_ops *ops) { IM_HERE_NOW(); struct rate_control_alg *alg; alg = (struct rate_control_alg*)kzalloc(sizeof(*alg), GFP_KERNEL); if (alg == NULL) { return -ENOMEM; } alg->ops = ops; //mutex_lock(&rate_ctrl_mutex); list_add_tail(&alg->list, &rate_ctrl_algs); //mutex_unlock(&rate_ctrl_mutex); return 0; } void ieee80211_rate_control_unregister(struct rate_control_ops *ops) { IM_HERE_NOW(); struct rate_control_alg *alg; //mutex_lock(&rate_ctrl_mutex); list_for_each_entry(alg, &rate_ctrl_algs, list) { if (alg->ops == ops) { list_del(&alg->list); break; } } //mutex_unlock(&rate_ctrl_mutex); kfree(alg); return; } int ieee80211_get_morefrag(struct ieee80211_hdr *hdr) { IM_HERE_NOW(); return (le16_to_cpu(hdr->frame_control) & IEEE80211_FCTL_MOREFRAGS) != 0; } #pragma mark Rx static inline int __ieee80211_invoke_rx_handlers( struct ieee80211_local *local, ieee80211_rx_handler *handlers, struct ieee80211_txrx_data *rx, struct sta_info *sta){ IM_HERE_NOW(); ieee80211_rx_handler *handler; ieee80211_txrx_result res = TXRX_DROP; for (handler = handlers; *handler != NULL; handler++) { res = (*handler)(rx); if (res != TXRX_CONTINUE) { if (res == TXRX_DROP) { I802_DEBUG_INC(local->rx_handlers_drop); if (sta) sta->rx_dropped++; } if (res == TXRX_QUEUED) I802_DEBUG_INC(local->rx_handlers_queued); break; } } if (res == TXRX_DROP) { dev_kfree_skb(rx->skb); } return res; //return TXRX_CONTINUE; } static inline void ieee80211_invoke_rx_handlers(struct ieee80211_local *local, ieee80211_rx_handler *handlers, struct ieee80211_txrx_data *rx, struct sta_info *sta) { IM_HERE_NOW(); if (__ieee80211_invoke_rx_handlers(local, handlers, rx, sta) == TXRX_CONTINUE) dev_kfree_skb(rx->skb); } static inline void *netdev_priv(const struct net_device *dev) { IM_HERE_NOW(); return dev->priv; } u8 *ieee80211_get_bssid(struct ieee80211_hdr *hdr, size_t len) { IM_HERE_NOW(); u16 fc; if (len < 24) return NULL; fc = le16_to_cpu(hdr->frame_control); switch (fc & IEEE80211_FCTL_FTYPE) { case IEEE80211_FTYPE_DATA: switch (fc & (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) { case IEEE80211_FCTL_TODS: return hdr->addr1; case (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS): return NULL; case IEEE80211_FCTL_FROMDS: return hdr->addr2; case 0: return hdr->addr3; } break; case IEEE80211_FTYPE_MGMT: return hdr->addr3; case IEEE80211_FTYPE_CTL: if ((fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_PSPOLL) return hdr->addr1; else return NULL; } return NULL; } static int ieee80211_get_radiotap_len(struct sk_buff *skb) { IM_HERE_NOW(); struct ieee80211_radiotap_header *hdr = (struct ieee80211_radiotap_header *) skb_data(skb); return le16_to_cpu(hdr->it_len); } #define WLAN_STA_WDS BIT(27) int ieee80211_get_hdrlen_from_skb(const struct sk_buff *skb) { IM_HERE_NOW(); const struct ieee80211_hdr *hdr = (const struct ieee80211_hdr *) skb_data(skb); int hdrlen; if (unlikely(skb_len(skb) < 10)) return 0; hdrlen = ieee80211_get_hdrlen(le16_to_cpu(hdr->frame_control)); if (unlikely(hdrlen > skb_len(skb))) return 0; return hdrlen; } int ieee80211_wep_get_keyidx(struct sk_buff *skb) { IM_HERE_NOW(); struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb_data(skb); u16 fc; int hdrlen; fc = le16_to_cpu(hdr->frame_control); if (!(fc & IEEE80211_FCTL_PROTECTED)) return -1; hdrlen = ieee80211_get_hdrlen(fc); if (skb_len(skb) < 8 + hdrlen) return -1; return ((u8*)(skb_data(skb)))[hdrlen + 3] >> 6; } #define FCS_LEN 4 #define WLAN_STA_ASSOC BIT(1) enum ieee80211_msg_type { ieee80211_msg_normal = 0, ieee80211_msg_tx_callback_ack = 1, ieee80211_msg_tx_callback_fail = 2, /* hole at 3, was ieee80211_msg_passive_scan but unused */ ieee80211_msg_wep_frame_unknown_key = 4, ieee80211_msg_michael_mic_failure = 5, /* hole at 6, was monitor but never sent to userspace */ ieee80211_msg_sta_not_assoc = 7, /* 8 was ieee80211_msg_set_aid_for_sta */ ieee80211_msg_key_threshold_notification = 9, ieee80211_msg_radar = 11, }; static struct ieee80211_rate * ieee80211_get_rate(struct ieee80211_local *local, int phymode, int hw_rate) { IM_HERE_NOW(); struct ieee80211_hw_mode *mode; int r; list_for_each_entry(mode, &local->modes_list, list) { if (mode->mode != phymode) continue; for (r = 0; r < mode->num_rates; r++) { struct ieee80211_rate *rate = &mode->rates[r]; if (rate->val == hw_rate || (rate->flags & IEEE80211_RATE_PREAMBLE2 && rate->val2 == hw_rate)) return rate; } } return NULL; } static void ieee80211_fill_frame_info(struct ieee80211_local *local, struct ieee80211_frame_info *fi, struct ieee80211_rx_status *status) { IM_HERE_NOW(); if (status) { struct timespec ts; struct ieee80211_rate *rate; jiffies_to_timespec(jiffies, &ts); fi->hosttime = cpu_to_be64((u64) ts.tv_sec * 1000000 + ts.tv_nsec / 1000); fi->mactime = cpu_to_be64(status->mactime); switch (status->phymode) { case MODE_IEEE80211A: fi->phytype = htonl(ieee80211_phytype_ofdm_dot11_a); break; case MODE_IEEE80211B: fi->phytype = htonl(ieee80211_phytype_dsss_dot11_b); break; case MODE_IEEE80211G: fi->phytype = htonl(ieee80211_phytype_pbcc_dot11_g); break; case MODE_ATHEROS_TURBO: fi->phytype = htonl(ieee80211_phytype_dsss_dot11_turbo); break; default: fi->phytype = htonl(0xAAAAAAAA); break; } fi->channel = htonl(status->channel); rate = ieee80211_get_rate(local, status->phymode, status->rate); if (rate) { fi->datarate = htonl(rate->rate); if (rate->flags & IEEE80211_RATE_PREAMBLE2) { if (status->rate == rate->val) fi->preamble = htonl(2); /* long */ else if (status->rate == rate->val2) fi->preamble = htonl(1); /* short */ } else fi->preamble = htonl(0); } else { fi->datarate = htonl(0); fi->preamble = htonl(0); } fi->antenna = htonl(status->antenna); fi->priority = htonl(0xffffffff); /* no clue */ fi->ssi_type = htonl(ieee80211_ssi_raw); fi->ssi_signal = htonl(status->ssi); fi->ssi_noise = 0x00000000; fi->encoding = 0; } else { /* clear everything because we really don't know. * the msg_type field isn't present on monitor frames * so we don't know whether it will be present or not, * but it's ok to not clear it since it'll be assigned * anyway */ memset(fi, 0, sizeof(*fi) - sizeof(fi->msg_type)); fi->ssi_type = htonl(ieee80211_ssi_none); } fi->version = htonl(IEEE80211_FI_VERSION); fi->length = cpu_to_be32(sizeof(*fi) - sizeof(fi->msg_type)); } /* this routine is actually not just for this, but also * for pushing fake 'management' frames into userspace. * it shall be replaced by a netlink-based system. */ void ieee80211_rx_mgmt(struct ieee80211_local *local, struct sk_buff *skb, struct ieee80211_rx_status *status, u32 msg_type) { IM_HERE_NOW(); struct ieee80211_frame_info *fi; const size_t hlen = sizeof(struct ieee80211_frame_info); struct ieee80211_sub_if_data *sdata; //skb->dev = local->apdev; sdata = (ieee80211_sub_if_data *)IEEE80211_DEV_TO_SUB_IF(local->apdev); if (skb_headroom(skb) < hlen) { I802_DEBUG_INC(local->rx_expand_skb_head); if (pskb_expand_head(skb, hlen, 0)) { dev_kfree_skb(skb); return; } } fi = (struct ieee80211_frame_info *) skb_push(skb, hlen); ieee80211_fill_frame_info(local, fi, status); fi->msg_type = htonl(msg_type); sdata->stats.rx_packets++; sdata->stats.rx_bytes += skb_len(skb); //FIXME: Preparation of the mac header and send the packet skb_set_mac_header(skb, 0); //skb->ip_summed = CHECKSUM_UNNECESSARY; skb->pkt_type = PACKET_OTHERHOST; //skb->protocol = htons(ETH_P_802_2); memset(skb->cb, 0, sizeof(skb->cb)); //netif_rx(skb); my_fNetif->inputPacket(skb->mac_data,mbuf_len(skb->mac_data)); } static void ieee80211_rx_michael_mic_report(struct net_device *dev, struct ieee80211_hdr *hdr, struct sta_info *sta, struct ieee80211_txrx_data *rx) { IM_HERE_NOW(); int keyidx, hdrlen; hdrlen = ieee80211_get_hdrlen_from_skb(rx->skb); if (skb_len(rx->skb) >= hdrlen + 4){ //keyidx = rx->skb->data[hdrlen + 3] >> 6; u8 * tmp = (u8 *)skb_data(rx->skb); keyidx = tmp[hdrlen + 3] >> 6; }else keyidx = -1; /* TODO: verify that this is not triggered by fragmented * frames (hw does not verify MIC for them). */ printk(KERN_DEBUG "%s: TKIP hwaccel reported Michael MIC " "failure from " MAC_FMT " to " MAC_FMT " keyidx=%d\n", dev->name, MAC_ARG(hdr->addr2), MAC_ARG(hdr->addr1), keyidx); if (!sta) { /* Some hardware versions seem to generate incorrect * Michael MIC reports; ignore them to avoid triggering * countermeasures. */ printk(KERN_DEBUG "%s: ignored spurious Michael MIC " "error for unknown address " MAC_FMT "\n", dev->name, MAC_ARG(hdr->addr2)); goto ignore; } if (!(rx->fc & IEEE80211_FCTL_PROTECTED)) { printk(KERN_DEBUG "%s: ignored spurious Michael MIC " "error for a frame with no ISWEP flag (src " MAC_FMT ")\n", dev->name, MAC_ARG(hdr->addr2)); goto ignore; } if ((rx->local->hw.flags & IEEE80211_HW_WEP_INCLUDE_IV) && rx->sdata->type == IEEE80211_IF_TYPE_AP) { keyidx = ieee80211_wep_get_keyidx(rx->skb); /* AP with Pairwise keys support should never receive Michael * MIC errors for non-zero keyidx because these are reserved * for group keys and only the AP is sending real multicast * frames in BSS. */ if (keyidx) { printk(KERN_DEBUG "%s: ignored Michael MIC error for " "a frame with non-zero keyidx (%d) (src " MAC_FMT ")\n", dev->name, keyidx, MAC_ARG(hdr->addr2)); goto ignore; } } if ((rx->fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA && ((rx->fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_MGMT || (rx->fc & IEEE80211_FCTL_STYPE) != IEEE80211_STYPE_AUTH)) { printk(KERN_DEBUG "%s: ignored spurious Michael MIC " "error for a frame that cannot be encrypted " "(fc=0x%04x) (src " MAC_FMT ")\n", dev->name, rx->fc, MAC_ARG(hdr->addr2)); goto ignore; } do { union iwreq_data wrqu; char *buf = (char *)kmalloc(128, GFP_ATOMIC); if (!buf) break; /* TODO: needed parameters: count, key type, TSC */ sprintf(buf, "MLME-MICHAELMICFAILURE.indication(" "keyid=%d %scast addr=" MAC_FMT ")", keyidx, hdr->addr1[0] & 0x01 ? "broad" : "uni", MAC_ARG(hdr->addr2)); memset(&wrqu, 0, sizeof(wrqu)); //wrqu.data.length = strlen(buf); //FIXME: wireless send eve,t! //wireless_send_event(rx->dev, IWEVCUSTOM, &wrqu, buf); kfree(buf); } while (0); /* TODO: consider verifying the MIC error report with software * implementation if we get too many spurious reports from the * hardware. */ if (!rx->local->apdev) goto ignore; ieee80211_rx_mgmt(rx->local, rx->skb, rx->u.rx.status, ieee80211_msg_michael_mic_failure); return; ignore: dev_kfree_skb(rx->skb); rx->skb = NULL; } inline int ieee80211_bssid_match(const u8 *raddr, const u8 *addr) { IM_HERE_NOW(); return compare_ether_addr(raddr, addr) == 0 || is_broadcast_ether_addr(raddr); } static inline void rate_control_rate_init(struct sta_info *sta, struct ieee80211_local *local) { IM_HERE_NOW(); struct rate_control_ref *ref = sta->rate_ctrl; ref->ops->rate_init(ref->priv, sta->rate_ctrl_priv, local, sta); } /* Caller must hold local->sta_lock */ static void sta_info_hash_add(struct ieee80211_local *local, struct sta_info *sta) { IM_HERE_NOW(); sta->hnext = local->sta_hash[STA_HASH(sta->addr)]; local->sta_hash[STA_HASH(sta->addr)] = sta; } static void kref_init(struct kref *kref) { //WARN_ON(release == NULL); atomic_set(&kref->refcount,1); } static struct kref *kref_get(struct kref *kref) { IM_HERE_NOW(); //WARN_ON(!atomic_read(&kref->refcount)); atomic_inc(&kref->refcount); return kref; } static inline void __sta_info_get(struct sta_info *sta) { IM_HERE_NOW(); kref_get(&sta->kref); } struct sta_info * sta_info_get(struct ieee80211_local *local, u8 *addr) { IM_HERE_NOW(); struct sta_info *sta; spin_lock_bh(&local->sta_lock); sta = local->sta_hash[STA_HASH(addr)]; while (sta) { if (memcmp(sta->addr, addr, ETH_ALEN) == 0) { __sta_info_get(sta); break; } sta = sta->hnext; } spin_unlock_bh(&local->sta_lock); return sta; } /** * kref_put - decrement refcount for object. * @kref: object. * * Decrement the refcount, and if 0, call kref->release(). */ static void kref_put(struct kref *kref, void (*release)(struct kref *kref)) { IM_HERE_NOW(); if (atomic_dec_and_test(&kref->refcount)) { IOLog("kref cleaning up\n"); release(kref); } } static inline void rate_control_free_sta(struct rate_control_ref *ref, void *priv) { ref->ops->free_sta(ref->priv, priv); } void ieee80211_debugfs_key_sta_del(struct ieee80211_key *key, struct sta_info *sta) { //debugfs_remove(key->debugfs.stalink); //key->debugfs.stalink = NULL; } static void rate_control_put(struct rate_control_ref *ref) { IM_HERE_NOW(); kref_put(&ref->kref, rate_control_release); } static void sta_info_release(struct kref *kref) { IM_HERE_NOW(); struct sta_info *sta = container_of(kref, struct sta_info, kref); struct ieee80211_local *local = sta->local; struct sk_buff *skb; int i; /* free sta structure; it has already been removed from * hash table etc. external structures. Make sure that all * buffered frames are release (one might have been added * after sta_info_free() was called). */ while ((skb = skb_dequeue(&sta->ps_tx_buf)) != NULL) { local->total_ps_buffered--; dev_kfree_skb_any(skb); } while ((skb = skb_dequeue(&sta->tx_filtered)) != NULL) { dev_kfree_skb_any(skb); } /*for (i=0; i< STA_TID_NUM; i++) { del_timer_sync(&sta->ht_ba_mlme.tid_agg_info_tx[i].addba_resp_timer); del_timer_sync(&sta->ht_ba_mlme.tid_agg_info_rx[i].session_timer); }*/ rate_control_free_sta(sta->rate_ctrl, sta->rate_ctrl_priv); rate_control_put(sta->rate_ctrl); if (sta->key) ieee80211_debugfs_key_sta_del(sta->key, sta); kfree(sta); } void sta_info_put(struct sta_info *sta) { IM_HERE_NOW(); kref_put(&sta->kref,sta_info_release); } static struct rate_control_ref *rate_control_get(struct rate_control_ref *ref) { IM_HERE_NOW(); kref_get(&ref->kref); return ref; } void rate_control_release(struct kref *kref) { IM_HERE_NOW(); struct rate_control_ref *ctrl_ref; ctrl_ref = container_of(kref, struct rate_control_ref, kref); ctrl_ref->ops->free(ctrl_ref->priv); //ieee80211_rate_control_ops_put(ctrl_ref->ops); kfree(ctrl_ref); } static inline void *rate_control_alloc_sta(struct rate_control_ref *ref, gfp_t gfp) { IM_HERE_NOW(); return ref->ops->alloc_sta(ref->priv, gfp); } struct sta_info * sta_info_add(struct ieee80211_local *local, struct net_device *dev, u8 *addr, gfp_t gfp) { IM_HERE_NOW(); struct sta_info *sta; sta = (sta_info*)kzalloc(sizeof(*sta), gfp); if (!sta) return NULL; kref_init(&sta->kref); sta->rate_ctrl = rate_control_get(local->rate_ctrl); sta->rate_ctrl_priv = rate_control_alloc_sta(sta->rate_ctrl, gfp); if (!sta->rate_ctrl_priv) { rate_control_put(sta->rate_ctrl); kref_put(&sta->kref, sta_info_release); kfree(sta); return NULL; } memcpy(sta->addr, addr, ETH_ALEN); sta->local = local; sta->dev = dev; skb_queue_head_init(&sta->ps_tx_buf); skb_queue_head_init(&sta->tx_filtered); __sta_info_get(sta); /* sta used by caller, decremented by * sta_info_put() */ spin_lock_bh(&local->sta_lock); list_add(&sta->list, &local->sta_list); local->num_sta++; sta_info_hash_add(local, sta); spin_unlock_bh(&local->sta_lock); if (local->ops->sta_table_notification) local->ops->sta_table_notification(local_to_hw(local), local->num_sta); sta->key_idx_compression = HW_KEY_IDX_INVALID; #ifdef CONFIG_MAC80211_VERBOSE_DEBUG printk(KERN_DEBUG "%s: Added STA " MAC_FMT "\n", local->mdev->name, MAC_ARG(addr)); #endif /* CONFIG_MAC80211_VERBOSE_DEBUG */ /* #ifdef CONFIG_MAC80211_DEBUGFS if (!in_interrupt()) { sta->debugfs_registered = 1; ieee80211_sta_debugfs_add(sta); rate_control_add_sta_debugfs(sta); } else { queue_work(local->hw.workqueue, &local->sta_debugfs_add); } #endif */ return sta; } #define IEEE80211_IBSS_MAX_STA_ENTRIES 128 struct sta_info * ieee80211_ibss_add_sta(struct net_device *dev, struct sk_buff *skb, u8 *bssid, u8 *addr) { IM_HERE_NOW(); struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); struct sta_info *sta; struct ieee80211_sub_if_data *sdata = (ieee80211_sub_if_data *)IEEE80211_DEV_TO_SUB_IF(dev); /* TODO: Could consider removing the least recently used entry and * allow new one to be added. */ if (local->num_sta >= IEEE80211_IBSS_MAX_STA_ENTRIES) { if (net_ratelimit()) { printk(KERN_DEBUG "%s: No room for a new IBSS STA " "entry " MAC_FMT "\n", dev->name, MAC_ARG(addr)); } return NULL; } printk(KERN_DEBUG "%s: Adding new IBSS station " MAC_FMT " (dev=%s)\n", local->mdev->name, MAC_ARG(addr), dev->name); sta = sta_info_add(local, dev, addr, GFP_ATOMIC); if (!sta) return NULL; sta->supp_rates = sdata->u.sta.supp_rates_bits; rate_control_rate_init(sta, local); return sta; /* caller will call sta_info_put() */ } /* * This is the receive path handler. It is called by a low level driver when an * 802.11 MPDU is received from the hardware. */ void __ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb, struct ieee80211_rx_status *status) { IM_HERE_NOW(); struct ieee80211_local *local = hw_to_local(hw); struct ieee80211_sub_if_data *sdata; struct sta_info *sta; struct ieee80211_hdr *hdr; struct ieee80211_txrx_data rx; u16 type; int multicast=0; int radiotap_len = 0; if (status->flag & RX_FLAG_RADIOTAP) { radiotap_len = ieee80211_get_radiotap_len(skb); skb_pull(skb, radiotap_len); } hdr = (struct ieee80211_hdr *) skb_data(skb); memset(&rx, 0, sizeof(rx)); rx.skb = skb; rx.local = local; rx.u.rx.status = status; rx.fc = skb_len(skb) >= 2 ? le16_to_cpu(hdr->frame_control) : 0; type = rx.fc & IEEE80211_FCTL_FTYPE; if (type == IEEE80211_FTYPE_DATA || type == IEEE80211_FTYPE_MGMT) local->dot11ReceivedFragmentCount++; multicast = is_multicast_ether_addr(hdr->addr1); if (skb_len(skb) >= 16) sta = rx.sta = sta_info_get(local, hdr->addr2); else sta = rx.sta = NULL; if (sta) { rx.dev = sta->dev; rx.sdata = (ieee80211_sub_if_data *)IEEE80211_DEV_TO_SUB_IF(rx.dev); printk("rxbssid=" MAC_FMT " ('%s')\n", MAC_ARG(rx.sdata->u.sta.bssid), escape_essid((const char*)rx.sdata->u.sta.ssid, rx.sdata->u.sta.ssid_len)); } if ((status->flag & RX_FLAG_MMIC_ERROR)) { ieee80211_rx_michael_mic_report(local->mdev, hdr, sta, &rx); goto end; } if (unlikely(local->sta_scanning)) rx.u.rx.in_scan = 1; if (__ieee80211_invoke_rx_handlers(local, local->rx_pre_handlers, &rx, sta) != TXRX_CONTINUE) goto end; skb = rx.skb; skb_push(skb, radiotap_len); if (sta && !sta->assoc_ap && !(sta->flags & WLAN_STA_WDS) && !local->iff_promiscs && !multicast) { rx.u.rx.ra_match = 1; ieee80211_invoke_rx_handlers(local, local->rx_handlers, &rx, sta); } else { struct ieee80211_sub_if_data *prev = NULL; struct sk_buff *skb_new; u8 *bssid = ieee80211_get_bssid(hdr, skb_len(skb) - radiotap_len); //FIXME: read_lock //read_lock(&local->sub_if_lock); list_for_each_entry(sdata, &local->sub_if_list, list) { printk( "bssid=" MAC_FMT " stabssid=" MAC_FMT "\n", MAC_ARG(bssid),MAC_ARG(sdata->u.sta.bssid)); rx.u.rx.ra_match = 1; switch (sdata->type) { case IEEE80211_IF_TYPE_STA: if (!bssid) continue; if (!ieee80211_bssid_match(bssid, sdata->u.sta.bssid)) { if (!rx.u.rx.in_scan) continue; rx.u.rx.ra_match = 0; } else if (!multicast && compare_ether_addr(sdata->dev->dev_addr, hdr->addr1) != 0) { printk( "mul %d dev_addr=" MAC_FMT " addr1=" MAC_FMT "\n", multicast, MAC_ARG(sdata->dev->dev_addr), MAC_ARG(hdr->addr1)); if (!sdata->promisc) continue; rx.u.rx.ra_match = 0; } break; case IEEE80211_IF_TYPE_IBSS: if (!bssid) continue; if (!ieee80211_bssid_match(bssid, sdata->u.sta.bssid)) { if (!rx.u.rx.in_scan) continue; rx.u.rx.ra_match = 0; } else if (!multicast && compare_ether_addr(sdata->dev->dev_addr, hdr->addr1) != 0) { if (!sdata->promisc) continue; rx.u.rx.ra_match = 0; } else if (!sta) sta = rx.sta = ieee80211_ibss_add_sta(sdata->dev, skb, bssid, hdr->addr2); break; case IEEE80211_IF_TYPE_AP: if (!bssid) { if (compare_ether_addr(sdata->dev->dev_addr, hdr->addr1) != 0) continue; } else if (!ieee80211_bssid_match(bssid, sdata->dev->dev_addr)) { if (!rx.u.rx.in_scan) continue; rx.u.rx.ra_match = 0; } if (sdata->dev == local->mdev && !rx.u.rx.in_scan) /* do not receive anything via * master device when not scanning */ continue; break; case IEEE80211_IF_TYPE_WDS: if (bssid || (rx.fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA) continue; if (compare_ether_addr(sdata->u.wds.remote_addr, hdr->addr2) != 0) continue; break; } if (prev) { skb_new = skb_copy(skb, GFP_ATOMIC); if (!skb_new) { if (net_ratelimit()) printk(KERN_DEBUG "%s: failed to copy " "multicast frame for %s", local->mdev->name, prev->dev->name); continue; } rx.skb = skb_new; rx.dev = prev->dev; rx.sdata = prev; ieee80211_invoke_rx_handlers(local, local->rx_handlers, &rx, sta); } prev = sdata; } if (prev) { rx.skb = skb; rx.dev = prev->dev; rx.sdata = prev; ieee80211_invoke_rx_handlers(local, local->rx_handlers, &rx, sta); } else dev_kfree_skb(skb); //FIXME: read_unlock //read_unlock(&local->sub_if_lock); } end: if (sta) sta_info_put(sta); } #define IEEE80211_RX_MSG 1 #define IEEE80211_TX_STATUS_MSG 2 static void ieee80211_tasklet_handler(unsigned long data) { IM_HERE_NOW(); struct ieee80211_local *local = (struct ieee80211_local *) data; struct sk_buff *skb; struct ieee80211_rx_status rx_status; struct ieee80211_tx_status *tx_status; while ((skb = skb_dequeue(&local->skb_queue)) || (skb = skb_dequeue(&local->skb_queue_unreliable))) { //IOLog("Packet Found\n"); switch (skb->pkt_type) { case IEEE80211_RX_MSG: /* status is in skb->cb */ memcpy(&rx_status, skb->cb, sizeof(rx_status)); /* Clear skb->type in order to not confuse kernel * netstack. */ skb->pkt_type = 0; __ieee80211_rx(local_to_hw(local), skb, &rx_status); break; case IEEE80211_TX_STATUS_MSG: /* get pointer to saved status out of skb->cb */ memcpy(&tx_status, skb->cb, sizeof(tx_status)); skb->pkt_type = 0; ieee80211_tx_status(local_to_hw(local), skb, tx_status); kfree(tx_status); break; default: /* should never get here! */ printk(KERN_ERR "%s: Unknown message type (%d)\n", local->mdev->name, skb->pkt_type); dev_kfree_skb(skb); break; } } } /* This is a version of the rx handler that can be called from hard irq * context. Post the skb on the queue and schedule the tasklet */ void ieee80211_rx_irqsafe(struct ieee80211_hw *hw, struct sk_buff *skb, struct ieee80211_rx_status *status) { IM_HERE_NOW(); struct ieee80211_local *local = hw_to_local(hw); BUILD_BUG_ON(sizeof(struct ieee80211_rx_status) > sizeof(skb->cb)); // IOLog("ieee80211_rx_irqsafe\n"); //PrintPacketHeader(skb->mac_data); /*char *frame; frame = (char*)skb_data(skb); for (int i = 0; i < mbuf_len(skb->mac_data); i++) { IOLog("%02X", (u_int8_t)frame[i]); }*/ memcpy(skb->cb, status, sizeof(*status)); skb->pkt_type = IEEE80211_RX_MSG; skb_queue_tail(&local->skb_queue, skb); tasklet_schedule(&local->tasklet); //FIXME: tasklet only give the priv as argument must be changed //IOCreateThread((void(*)(void*))&ieee80211_tasklet_handler,local); //IOExitThread(); } void ieee80211_stop_queue(struct ieee80211_hw *hw, int queue) { IM_HERE_NOW(); struct ieee80211_local *local = hw_to_local(hw); //if (!ieee80211_qdisc_installed(local->mdev) && queue == 0) // netif_stop_queue(local->mdev); set_bit(IEEE80211_LINK_STATE_XOFF, &local->state[queue]); } static void ieee80211_remove_tx_extra(struct ieee80211_local *local, struct ieee80211_key *key, struct sk_buff *skb, struct ieee80211_tx_control *control) { IM_HERE_NOW(); int hdrlen, iv_len, mic_len; struct ieee80211_tx_packet_data *pkt_data; pkt_data = (struct ieee80211_tx_packet_data *)skb->cb; pkt_data->ifindex = control->ifindex; pkt_data->mgmt_iface = (control->type == IEEE80211_IF_TYPE_MGMT); pkt_data->req_tx_status = !!(control->flags & IEEE80211_TXCTL_REQ_TX_STATUS); pkt_data->do_not_encrypt = !!(control->flags & IEEE80211_TXCTL_DO_NOT_ENCRYPT); pkt_data->requeue = !!(control->flags & IEEE80211_TXCTL_REQUEUE); pkt_data->queue = control->queue; hdrlen = ieee80211_get_hdrlen_from_skb(skb); if (!key) goto no_key; switch (key->alg) { case ALG_WEP: iv_len = WEP_IV_LEN; mic_len = WEP_ICV_LEN; break; case ALG_TKIP: iv_len = TKIP_IV_LEN; mic_len = TKIP_ICV_LEN; break; case ALG_CCMP: iv_len = CCMP_HDR_LEN; mic_len = CCMP_MIC_LEN; break; default: goto no_key; } if (skb_len(skb) >= mic_len && key->force_sw_encrypt) skb_trim(skb, skb_len(skb) - mic_len); if (skb_len(skb) >= iv_len && skb_len(skb) > hdrlen) { memmove((u8*)skb_data(skb) + iv_len, skb_data(skb), hdrlen); skb_pull(skb, iv_len); } no_key: { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb_data(skb); u16 fc = le16_to_cpu(hdr->frame_control); if ((fc & 0x8C) == 0x88) /* QoS Control Field */ { fc &= ~IEEE80211_STYPE_QOS_DATA; hdr->frame_control = cpu_to_le16(fc); memmove((u8*)skb_data(skb) + 2, (u8*)skb_data(skb), hdrlen - 2); skb_pull(skb, 2); } } } static inline void rate_control_tx_status(struct ieee80211_local *local, struct net_device *dev, struct sk_buff *skb, struct ieee80211_tx_status *status) { IM_HERE_NOW(); struct rate_control_ref *ref = local->rate_ctrl; ref->ops->tx_status(ref->priv, dev, skb, status); } void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb, struct ieee80211_tx_status *status) { IM_HERE_NOW(); struct sk_buff *skb2; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb_data(skb); struct ieee80211_local *local = hw_to_local(hw); u16 frag, type; u32 msg_type; struct ieee80211_tx_status_rtap_hdr *rthdr; struct ieee80211_sub_if_data *sdata; int monitors; if (!status) { printk(KERN_ERR "%s: ieee80211_tx_status called with NULL status\n", local->mdev->name); dev_kfree_skb(skb); return; } if (status->excessive_retries) { struct sta_info *sta; sta = sta_info_get(local, hdr->addr1); if (sta) { if (sta->flags & WLAN_STA_PS) { /* The STA is in power save mode, so assume * that this TX packet failed because of that. */ status->excessive_retries = 0; status->flags |= IEEE80211_TX_STATUS_TX_FILTERED; } sta_info_put(sta); } } if (status->flags & IEEE80211_TX_STATUS_TX_FILTERED) { struct sta_info *sta; sta = sta_info_get(local, hdr->addr1); if (sta) { sta->tx_filtered_count++; /* Clear the TX filter mask for this STA when sending * the next packet. If the STA went to power save mode, * this will happen when it is waking up for the next * time. */ sta->clear_dst_mask = 1; /* TODO: Is the WLAN_STA_PS flag always set here or is * the race between RX and TX status causing some * packets to be filtered out before 80211.o gets an * update for PS status? This seems to be the case, so * no changes are likely to be needed. */ if (sta->flags & WLAN_STA_PS && skb_queue_len(&sta->tx_filtered) < STA_MAX_TX_BUFFER) { ieee80211_remove_tx_extra(local, sta->key, skb, &status->control); skb_queue_tail(&sta->tx_filtered, skb); } else if (!(sta->flags & WLAN_STA_PS) && !(status->control.flags & IEEE80211_TXCTL_REQUEUE)) { /* Software retry the packet once */ status->control.flags |= IEEE80211_TXCTL_REQUEUE; ieee80211_remove_tx_extra(local, sta->key, skb, &status->control); dev_queue_xmit(skb); } else { if (net_ratelimit()) { printk(KERN_DEBUG "%s: dropped TX " "filtered frame queue_len=%d " "PS=%d @%lu\n", local->mdev->name, skb_queue_len( &sta->tx_filtered), !!(sta->flags & WLAN_STA_PS), jiffies); } dev_kfree_skb(skb); } sta_info_put(sta); return; } } else { /* FIXME: STUPID to call this with both local and local->mdev */ rate_control_tx_status(local, local->mdev, skb, status); } //ieee80211_led_tx(local, 0); /* SNMP counters * Fragments are passed to low-level drivers as separate skbs, so these * are actually fragments, not frames. Update frame counters only for * the first fragment of the frame. */ frag = le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_FRAG; type = le16_to_cpu(hdr->frame_control) & IEEE80211_FCTL_FTYPE; if (status->flags & IEEE80211_TX_STATUS_ACK) { if (frag == 0) { local->dot11TransmittedFrameCount++; if (is_multicast_ether_addr(hdr->addr1)) local->dot11MulticastTransmittedFrameCount++; if (status->retry_count > 0) local->dot11RetryCount++; if (status->retry_count > 1) local->dot11MultipleRetryCount++; } /* This counter shall be incremented for an acknowledged MPDU * with an individual address in the address 1 field or an MPDU * with a multicast address in the address 1 field of type Data * or Management. */ if (!is_multicast_ether_addr(hdr->addr1) || type == IEEE80211_FTYPE_DATA || type == IEEE80211_FTYPE_MGMT) local->dot11TransmittedFragmentCount++; } else { if (frag == 0) local->dot11FailedCount++; } msg_type = (status->flags & IEEE80211_TX_STATUS_ACK) ? ieee80211_msg_tx_callback_ack : ieee80211_msg_tx_callback_fail; /* this was a transmitted frame, but now we want to reuse it */ //skb_orphan(skb); if ((status->control.flags & IEEE80211_TXCTL_REQ_TX_STATUS) && local->apdev) { if (local->monitors) { skb2 = skb_clone(skb, GFP_ATOMIC); } else { skb2 = skb; skb = NULL; } if (skb2) /* Send frame to hostapd */ ieee80211_rx_mgmt(local, skb2, NULL, msg_type); if (!skb) return; } if (!local->monitors) { dev_kfree_skb(skb); return; } /* send frame to monitor interfaces now */ if (skb_headroom(skb) < sizeof(*rthdr)) { printk(KERN_ERR "ieee80211_tx_status: headroom too small\n"); dev_kfree_skb(skb); return; } rthdr = (struct ieee80211_tx_status_rtap_hdr*) skb_push(skb, sizeof(*rthdr)); memset(rthdr, 0, sizeof(*rthdr)); rthdr->hdr.it_len = cpu_to_le16(sizeof(*rthdr)); rthdr->hdr.it_present = cpu_to_le32((1 << IEEE80211_RADIOTAP_TX_FLAGS) | (1 << IEEE80211_RADIOTAP_DATA_RETRIES)); if (!(status->flags & IEEE80211_TX_STATUS_ACK) && !is_multicast_ether_addr(hdr->addr1)) rthdr->tx_flags |= cpu_to_le16(IEEE80211_RADIOTAP_F_TX_FAIL); if ((status->control.flags & IEEE80211_TXCTL_USE_RTS_CTS) && (status->control.flags & IEEE80211_TXCTL_USE_CTS_PROTECT)) rthdr->tx_flags |= cpu_to_le16(IEEE80211_RADIOTAP_F_TX_CTS); else if (status->control.flags & IEEE80211_TXCTL_USE_RTS_CTS) rthdr->tx_flags |= cpu_to_le16(IEEE80211_RADIOTAP_F_TX_RTS); rthdr->data_retries = status->retry_count; //read_lock(&local->sub_if_lock); monitors = local->monitors; list_for_each_entry(sdata, &local->sub_if_list, list) { /* * Using the monitors counter is possibly racy, but * if the value is wrong we simply either clone the skb * once too much or forget sending it to one monitor iface * The latter case isn't nice but fixing the race is much * more complicated. */ if (!monitors || !skb) goto out; if (sdata->type == IEEE80211_IF_TYPE_MNTR) { if (!netif_running(sdata->dev)) continue; monitors--; if (monitors) skb2 = skb_clone(skb, GFP_KERNEL); else skb2 = NULL; //skb->dev = sdata->dev; /* XXX: is this sufficient for BPF? */ skb_set_mac_header(skb, 0); //skb->ip_summed = CHECKSUM_UNNECESSARY; skb->pkt_type = PACKET_OTHERHOST; //skb->protocol = htons(ETH_P_802_2); memset(skb->cb, 0, sizeof(skb->cb)); //netif_rx(skb); my_fNetif->inputPacket(skb->mac_data,mbuf_len(skb->mac_data)); skb = skb2; } } out: //read_unlock(&local->sub_if_lock); if (skb) dev_kfree_skb(skb); } void ieee80211_tx_status_irqsafe(struct ieee80211_hw *hw, struct sk_buff *skb, struct ieee80211_tx_status *status) { IM_HERE_NOW(); struct ieee80211_local *local = hw_to_local(hw); struct ieee80211_tx_status *saved; int tmp; //skb->dev = local->mdev; saved = (struct ieee80211_tx_status*)kmalloc(sizeof(struct ieee80211_tx_status), GFP_ATOMIC); if (unlikely(!saved)) { if (net_ratelimit()) printk(KERN_WARNING "%s: Not enough memory, " "dropping tx status", "en1"); /* should be dev_kfree_skb_irq, but due to this function being * named _irqsafe instead of just _irq we can't be sure that * people won't call it from non-irq contexts */ dev_kfree_skb_any(skb); return; } memcpy(saved, status, sizeof(struct ieee80211_tx_status)); /* copy pointer to saved status into skb->cb for use by tasklet */ memcpy(skb->cb, &saved, sizeof(saved)); skb->pkt_type = IEEE80211_TX_STATUS_MSG; skb_queue_tail(status->control.flags & IEEE80211_TXCTL_REQ_TX_STATUS ? &local->skb_queue : &local->skb_queue_unreliable, skb); tmp = skb_queue_len(&local->skb_queue) + skb_queue_len(&local->skb_queue_unreliable); while (tmp > IEEE80211_IRQSAFE_QUEUE_LIMIT && (skb = skb_dequeue(&local->skb_queue_unreliable))) { memcpy(&saved, skb->cb, sizeof(saved)); kfree(saved); //dev_kfree_skb_irq(skb); dev_kfree_skb(skb); tmp--; I802_DEBUG_INC(local->tx_status_drop); } tasklet_schedule(&local->tasklet); } void ieee80211_wake_queue(struct ieee80211_hw *hw, int queue) { IM_HERE_NOW(); // fTransmitQueue->service(IOBasicOutputQueue::kServiceAsync); return; } int __bitmap_empty(const unsigned long *bitmap, int bits) { IM_HERE_NOW(); int k, lim = bits/BITS_PER_LONG; for (k = 0; k < lim; ++k) if (bitmap[k]) return 0; if (bits % BITS_PER_LONG) if (bitmap[k] & BITMAP_LAST_WORD_MASK(bits)) return 0; return 1; } static inline int bitmap_empty(const unsigned long *src, int nbits) { IM_HERE_NOW(); if (nbits <= BITS_PER_LONG) return ! (*src & BITMAP_LAST_WORD_MASK(nbits)); else return __bitmap_empty(src, nbits); } static void ieee80211_beacon_add_tim(struct ieee80211_local *local, struct ieee80211_if_ap *bss, struct sk_buff *skb) { IM_HERE_NOW(); u8 *pos, *tim; int aid0 = 0; int i, have_bits = 0, n1, n2; /* Generate bitmap for TIM only if there are any STAs in power save * mode. */ //spin_lock_bh(&local->sta_lock); if (atomic_read(&bss->num_sta_ps) > 0) /* in the hope that this is faster than * checking byte-for-byte */ have_bits = !bitmap_empty((unsigned long*)bss->tim, IEEE80211_MAX_AID+1); if (bss->dtim_count == 0) bss->dtim_count = bss->dtim_period - 1; else bss->dtim_count--; tim = pos = (u8 *) skb_put(skb, 6); *pos++ = WLAN_EID_TIM; *pos++ = 4; *pos++ = bss->dtim_count; *pos++ = bss->dtim_period; if (bss->dtim_count == 0 && !skb_queue_empty(&bss->ps_bc_buf)) aid0 = 1; if (have_bits) { /* Find largest even number N1 so that bits numbered 1 through * (N1 x 8) - 1 in the bitmap are 0 and number N2 so that bits * (N2 + 1) x 8 through 2007 are 0. */ n1 = 0; for (i = 0; i < IEEE80211_MAX_TIM_LEN; i++) { if (bss->tim[i]) { n1 = i & 0xfe; break; } } n2 = n1; for (i = IEEE80211_MAX_TIM_LEN - 1; i >= n1; i--) { if (bss->tim[i]) { n2 = i; break; } } /* Bitmap control */ *pos++ = n1 | aid0; /* Part Virt Bitmap */ memcpy(pos, bss->tim + n1, n2 - n1 + 1); tim[1] = n2 - n1 + 4; skb_put(skb, n2 - n1); } else { *pos++ = aid0; /* Bitmap control */ *pos++ = 0; /* Part Virt Bitmap */ } spin_unlock_bh(&local->sta_lock); } struct sk_buff *ieee80211_beacon_get(struct ieee80211_hw *hw,int if_id,struct ieee80211_tx_control *control) { IM_HERE_NOW(); struct ieee80211_local *local = hw_to_local(hw); struct sk_buff *skb; struct net_device *bdev; struct ieee80211_sub_if_data *sdata = NULL; struct ieee80211_if_ap *ap = NULL; struct ieee80211_rate *rate; struct rate_control_extra extra; u8 *b_head, *b_tail; int bh_len, bt_len; bdev = dev_get_by_index(if_id); if (bdev) { sdata = (struct ieee80211_sub_if_data*)IEEE80211_DEV_TO_SUB_IF(bdev); ap = &sdata->u.ap; //dev_put(bdev); } if (!ap || sdata->type != IEEE80211_IF_TYPE_AP || !ap->beacon_head) { #ifdef CONFIG_MAC80211_VERBOSE_DEBUG if (net_ratelimit()) printk(KERN_DEBUG "no beacon data avail for idx=%d " "(%s)\n", if_id, bdev ? bdev->name : "N/A"); #endif /* CONFIG_MAC80211_VERBOSE_DEBUG */ return NULL; } /* Assume we are generating the normal beacon locally */ b_head = ap->beacon_head; b_tail = ap->beacon_tail; bh_len = ap->beacon_head_len; bt_len = ap->beacon_tail_len; skb = dev_alloc_skb(local->tx_headroom + bh_len + bt_len + 256 /* maximum TIM len */); if (!skb) return NULL; skb_reserve(skb, local->tx_headroom); memcpy(skb_put(skb, bh_len), b_head, bh_len); ieee80211_include_sequence(sdata, (struct ieee80211_hdr *)skb_data(skb)); ieee80211_beacon_add_tim(local, ap, skb); if (b_tail) { memcpy(skb_put(skb, bt_len), b_tail, bt_len); } if (control) { memset(&extra, 0, sizeof(extra)); extra.mode = local->oper_hw_mode; rate = rate_control_get_rate(local, local->mdev, skb, &extra); if (!rate) { if (net_ratelimit()) { printk(KERN_DEBUG "%s: ieee80211_beacon_get: no rate " "found\n", local->mdev->name); } dev_kfree_skb(skb); return NULL; } control->tx_rate = (local->short_preamble && (rate->flags & IEEE80211_RATE_PREAMBLE2)) ? rate->val2 : rate->val; control->antenna_sel_tx = local->hw.conf.antenna_sel_tx; control->power_level = local->hw.conf.power_level; control->flags |= IEEE80211_TXCTL_NO_ACK; control->retry_limit = 1; control->flags |= IEEE80211_TXCTL_CLEAR_DST_MASK; } ap->num_beacons++; return skb; } void ieee80211_stop_queues(struct ieee80211_hw *hw) { IM_HERE_NOW(); int i; for (i = 0; i < hw->queues; i++) ieee80211_stop_queue(hw, i); } int sta_info_start(struct ieee80211_local *local) { IM_HERE_NOW(); //check this add_timer(&local->sta_cleanup); return 0; } void ieee80211_if_sdata_init(struct ieee80211_sub_if_data *sdata) { IM_HERE_NOW(); /* Default values for sub-interface parameters */ sdata->drop_unencrypted = 0; sdata->eapol = 1; for (int i = 0; i < IEEE80211_FRAGMENT_MAX; i++) skb_queue_head_init(&sdata->fragments[i].skb_list); } static void ieee80211_sta_tx(struct net_device *dev, struct sk_buff *skb, int encrypt) { IM_HERE_NOW(); //FIXME: lot of skb_function struct ieee80211_sub_if_data *sdata; struct ieee80211_tx_packet_data *pkt_data; sdata = (struct ieee80211_sub_if_data*)IEEE80211_DEV_TO_SUB_IF(dev); //skb->dev = sdata->local->mdev; skb_set_mac_header(skb, 0); skb_set_network_header(skb, 0); //skb_set_transport_header(skb, 0); pkt_data = (struct ieee80211_tx_packet_data *) skb->cb; memset(pkt_data, 0, sizeof(struct ieee80211_tx_packet_data)); sdata->dev->ifindex=2;//hack pkt_data->ifindex = sdata->dev->ifindex; pkt_data->mgmt_iface = (sdata->type == IEEE80211_IF_TYPE_MGMT); pkt_data->do_not_encrypt = !encrypt; dev_queue_xmit(skb); //currentController->outputPacket(skb->mac_data,NULL); } static void ieee80211_rx_mgmt_probe_req(struct net_device *dev, struct ieee80211_if_sta *ifsta, struct ieee80211_mgmt *mgmt, size_t len, struct ieee80211_rx_status *rx_status) { IM_HERE_NOW(); struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); struct ieee80211_sub_if_data *sdata = (struct ieee80211_sub_if_data*)IEEE80211_DEV_TO_SUB_IF(dev); int tx_last_beacon; struct sk_buff *skb; struct ieee80211_mgmt *resp; u8 *pos, *end; if (sdata->type != IEEE80211_IF_TYPE_IBSS || ifsta->state != IEEE80211_IBSS_JOINED || len < 24 + 2 || !ifsta->probe_resp) return; if (local->ops->tx_last_beacon) tx_last_beacon = local->ops->tx_last_beacon(local_to_hw(local)); else tx_last_beacon = 1; #ifdef CONFIG_MAC80211_IBSS_DEBUG printk(KERN_DEBUG "%s: RX ProbeReq SA=" MAC_FMT " DA=" MAC_FMT " BSSID=" MAC_FMT " (tx_last_beacon=%d)\n", dev->name, MAC_ARG(mgmt->sa), MAC_ARG(mgmt->da), MAC_ARG(mgmt->bssid), tx_last_beacon); #endif /* CONFIG_MAC80211_IBSS_DEBUG */ if (!tx_last_beacon) return; if (memcmp(mgmt->bssid, ifsta->bssid, ETH_ALEN) != 0 && memcmp(mgmt->bssid, "\xff\xff\xff\xff\xff\xff", ETH_ALEN) != 0) return; end = ((u8 *) mgmt) + len; pos = mgmt->u.probe_req.variable; if (pos[0] != WLAN_EID_SSID || pos + 2 + pos[1] > end) { if (net_ratelimit()) { printk(KERN_DEBUG "%s: Invalid SSID IE in ProbeReq " "from " MAC_FMT "\n", dev->name, MAC_ARG(mgmt->sa)); } return; } if (pos[1] != 0 && (pos[1] != ifsta->ssid_len || memcmp(pos + 2, ifsta->ssid, ifsta->ssid_len) != 0)) { /* Ignore ProbeReq for foreign SSID */ return; } /* Reply with ProbeResp */ skb = skb_copy(ifsta->probe_resp, GFP_ATOMIC); if (!skb) return; resp = (struct ieee80211_mgmt *) skb_data(skb); memcpy(resp->da, mgmt->sa, ETH_ALEN); #ifdef CONFIG_MAC80211_IBSS_DEBUG printk(KERN_DEBUG "%s: Sending ProbeResp to " MAC_FMT "\n", dev->name, MAC_ARG(resp->da)); #endif /* CONFIG_MAC80211_IBSS_DEBUG */ ieee80211_sta_tx(dev, skb, 0); } static inline void setup_timer(struct timer_list2 * timer, void (*function)(unsigned long), unsigned long data) { IM_HERE_NOW(); init_timer(timer); timer->function = function; timer->data = data; //add_timer(timer);//hack } int ieee80211_sta_req_scan(struct net_device *dev, u8 *ssid, size_t ssid_len) { IM_HERE_NOW(); struct ieee80211_sub_if_data *sdata = (struct ieee80211_sub_if_data*)IEEE80211_DEV_TO_SUB_IF(dev); struct ieee80211_if_sta *ifsta = &sdata->u.sta; struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); if (sdata->type != IEEE80211_IF_TYPE_STA) return ieee80211_sta_start_scan(dev, ssid, ssid_len); if (local->sta_scanning) { if (local->scan_dev == dev) return 0; return -EBUSY; } set_bit(IEEE80211_STA_REQ_SCAN, &ifsta->request); queue_te(ifsta->work.number,(thread_call_func_t)ifsta->work.func,sdata,NULL,true); //queue_work(local->hw.workqueue, &ifsta->work); return 0; } void ieee80211_sta_timer(unsigned long data) { IM_HERE_NOW(); struct ieee80211_sub_if_data *sdata = (struct ieee80211_sub_if_data *) data; struct ieee80211_if_sta *ifsta = &sdata->u.sta; struct ieee80211_local *local = wdev_priv(&sdata->dev);//wdev); set_bit(IEEE80211_STA_REQ_RUN, &ifsta->request); //queue_work(local->hw.workqueue, &ifsta->work); //set_bit(IEEE80211_STA_REQ_SCAN, &ifsta->request);//hack queue_te(ifsta->work.number,(thread_call_func_t)ifsta->work.func,sdata,NULL,true); } void ieee80211_if_set_type(struct net_device *dev, int type) { IM_HERE_NOW(); struct ieee80211_sub_if_data *sdata = (struct ieee80211_sub_if_data*)IEEE80211_DEV_TO_SUB_IF(dev); struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); int oldtype = sdata->type; //dev->hard_start_xmit = ieee80211_subif_start_xmit; sdata->type = type; switch (type) { case IEEE80211_IF_TYPE_WDS: sdata->bss = NULL; break; case IEEE80211_IF_TYPE_VLAN: break; case IEEE80211_IF_TYPE_AP: sdata->u.ap.dtim_period = 2; sdata->u.ap.force_unicast_rateidx = -1; sdata->u.ap.max_ratectrl_rateidx = -1; skb_queue_head_init(&sdata->u.ap.ps_bc_buf); sdata->bss = &sdata->u.ap; break; case IEEE80211_IF_TYPE_STA: case IEEE80211_IF_TYPE_IBSS: { struct ieee80211_sub_if_data *msdata; struct ieee80211_if_sta *ifsta; ifsta = &sdata->u.sta; INIT_WORK(&ifsta->work, ieee80211_sta_work, 12); setup_timer(&ifsta->timer, ieee80211_sta_timer,(unsigned long) sdata); skb_queue_head_init(&ifsta->skb_queue); ifsta->capab = WLAN_CAPABILITY_ESS; ifsta->auth_algs = IEEE80211_AUTH_ALG_OPEN | IEEE80211_AUTH_ALG_SHARED_KEY; ifsta->create_ibss = 1; ifsta->wmm_enabled = 1; ifsta->auto_channel_sel = 1; ifsta->auto_bssid_sel = 1; msdata = (struct ieee80211_sub_if_data*)IEEE80211_DEV_TO_SUB_IF(sdata->local->mdev); sdata->bss = &msdata->u.ap; break; } case IEEE80211_IF_TYPE_MNTR: dev->type = ARPHRD_IEEE80211_RADIOTAP; //dev->hard_start_xmit = ieee80211_monitor_start_xmit; break; default: printk(KERN_WARNING "%s: %s: Unknown interface type 0x%x", dev->name, __FUNCTION__, type); } //ieee80211_debugfs_change_if_type(sdata, oldtype); //ieee80211_update_default_wep_only(local); } int ieee80211_if_add(struct net_device *dev, const char *name, struct net_device **new_dev, int type) { IM_HERE_NOW(); struct net_device *ndev; struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); struct ieee80211_sub_if_data *sdata = NULL; int ret; //ASSERT_RTNL(); ndev = alloc_netdev(sizeof(struct ieee80211_sub_if_data), name, NULL);//ieee80211_if_setup); if (!ndev) return -ENOMEM; //char ii[4]="en1"; //sprintf(ii,"%s%d" ,my_fNetif->getNamePrefix(), my_fNetif->getUnitNumber()); //bcopy(ii,ndev->name,sizeof(ii)); /*ret = dev_alloc_name(ndev, ndev->name); if (ret < 0) goto fail;*/ memcpy(ndev->dev_addr, my_mac_addr, ETH_ALEN);//local->hw.wiphy->perm_addr, ETH_ALEN); ndev->base_addr = dev->base_addr; ndev->irq = dev->irq; ndev->mem_start = dev->mem_start; ndev->mem_end = dev->mem_end; ndev->ifindex=2;//hack //SET_NETDEV_DEV(ndev, wiphy_dev(local->hw.wiphy)); sdata = (struct ieee80211_sub_if_data*)IEEE80211_DEV_TO_SUB_IF(ndev); ndev->ieee80211_ptr = hw_to_local(my_hw);//&sdata->wdev; //sdata->wdev.wiphy = local->hw.wiphy; sdata->type = IEEE80211_IF_TYPE_AP; sdata->dev = ndev; sdata->local = local; ieee80211_if_sdata_init(sdata); /*ret = register_netdevice(ndev); if (ret) goto fail;*/ //ieee80211_debugfs_add_netdev(sdata); ieee80211_if_set_type(ndev, type); ieee80211_open(ndev); //write_lock_bh(&local->sub_if_lock); //if (unlikely(local->reg_state == IEEE80211_DEV_UNREGISTERED)) { if (unlikely(local->reg_state == 0)) { //write_unlock_bh(&local->sub_if_lock); //__ieee80211_if_del(local, sdata); //return 0;//-ENODEV; } IOLog("listadd\n"); list_add(&sdata->list, &local->sub_if_list); if (new_dev) *new_dev = ndev; //write_unlock_bh(&local->sub_if_lock); //ieee80211_update_default_wep_only(local); local->scan_dev=ndev; return 0; fail: //free_netdev(ndev); return ret; } static struct rate_control_ops * ieee80211_try_rate_control_ops_get(const char *name) { struct rate_control_alg *alg; struct rate_control_ops *ops = NULL; IM_HERE_NOW(); //mutex_lock(&rate_ctrl_mutex); list_for_each_entry(alg, &rate_ctrl_algs, list) { if (!name || !strcmp(alg->ops->name, name)) /*if (try_module_get(alg->ops->module)) { ops = alg->ops; break; }*/ ops = alg->ops; } //mutex_unlock(&rate_ctrl_mutex); return ops; } static struct rate_control_ops * ieee80211_rate_control_ops_get(const char *name) { struct rate_control_ops *ops; IM_HERE_NOW(); ops = ieee80211_try_rate_control_ops_get(name); if (!ops) { //request_module("rc80211_%s", name ? name : "default"); //rate_control_simple_init(); ops = ieee80211_try_rate_control_ops_get(name); } return ops; } struct rate_control_ref *rate_control_alloc(const char *name, struct ieee80211_local *local) { IM_HERE_NOW(); struct rate_control_ref *ref; ref = (struct rate_control_ref*)kmalloc(sizeof(struct rate_control_ref), GFP_KERNEL); if (!ref) goto fail_ref; kref_init(&ref->kref); ref->ops = ieee80211_rate_control_ops_get(name); if (!ref->ops) goto fail_ops; ref->priv = ref->ops->alloc(local); if (!ref->priv) goto fail_priv; return ref; fail_priv: //ieee80211_rate_control_ops_put(ref->ops); fail_ops: kfree(ref); fail_ref: return NULL; } static void sta_info_hash_del(struct ieee80211_local *local, struct sta_info *sta) { IM_HERE_NOW(); struct sta_info *s; s = local->sta_hash[STA_HASH(sta->addr)]; if (!s) return; if (memcmp(s->addr, sta->addr, ETH_ALEN) == 0) { local->sta_hash[STA_HASH(sta->addr)] = s->hnext; return; } while (s->hnext && memcmp(s->hnext->addr, sta->addr, ETH_ALEN) != 0) s = s->hnext; if (s->hnext) s->hnext = s->hnext->hnext; else printk(KERN_ERR "%s: could not remove STA " MAC_FMT " from " "hash table\n", local->mdev->name, MAC_ARG(sta->addr)); } static inline void __bss_tim_clear(struct ieee80211_if_ap *bss, int aid) { IM_HERE_NOW(); /* * This format has ben mandated by the IEEE specifications, * so this line may not be changed to use the __clear_bit() format. */ bss->tim[(aid)/8] &= !(1<<((aid) % 8)); } void sta_info_remove_aid_ptr(struct sta_info *sta) { IM_HERE_NOW(); struct ieee80211_sub_if_data *sdata; if (sta->aid <= 0) return; sdata = (struct ieee80211_sub_if_data*)IEEE80211_DEV_TO_SUB_IF(sta->dev); if (sdata->local->ops->set_tim) sdata->local->ops->set_tim(local_to_hw(sdata->local), sta->aid, 0); if (sdata->bss) __bss_tim_clear(sdata->bss, sta->aid); } static void sta_info_remove(struct sta_info *sta) { IM_HERE_NOW(); struct ieee80211_local *local = sta->local; struct ieee80211_sub_if_data *sdata; sta_info_hash_del(local, sta); list_del(&sta->list); sdata = (struct ieee80211_sub_if_data*)IEEE80211_DEV_TO_SUB_IF(sta->dev); if (sta->flags & WLAN_STA_PS) { sta->flags &= ~WLAN_STA_PS; if (sdata->bss) atomic_dec(&sdata->bss->num_sta_ps); } local->num_sta--; sta_info_remove_aid_ptr(sta); } static void finish_sta_info_free(struct ieee80211_local *local, struct sta_info *sta) { IM_HERE_NOW(); #ifdef CONFIG_MAC80211_VERBOSE_DEBUG printk(KERN_DEBUG "%s: Removed STA " MAC_FMT "\n", local->mdev->name, MAC_ARG(sta->addr)); #endif /* CONFIG_MAC80211_VERBOSE_DEBUG */ /*if (sta->key) { ieee80211_debugfs_key_remove(sta->key); ieee80211_key_free(sta->key); sta->key = NULL; }*/ //rate_control_remove_sta_debugfs(sta); //ieee80211_sta_debugfs_remove(sta); sta_info_put(sta); } void sta_info_free(struct sta_info *sta, int locked) { IM_HERE_NOW(); struct sk_buff *skb; struct ieee80211_local *local = sta->local; if (!locked) { spin_lock_bh(&local->sta_lock); sta_info_remove(sta); spin_unlock_bh(&local->sta_lock); } else { sta_info_remove(sta); } if (local->ops->sta_table_notification) local->ops->sta_table_notification(local_to_hw(local), local->num_sta); while ((skb = skb_dequeue(&sta->ps_tx_buf)) != NULL) { local->total_ps_buffered--; dev_kfree_skb_any(skb); } while ((skb = skb_dequeue(&sta->tx_filtered)) != NULL) { dev_kfree_skb_any(skb); } /*if (sta->key) { if (local->ops->set_key) { struct ieee80211_key_conf *key; key = ieee80211_key_data2conf(local, sta->key); if (key) { local->ops->set_key(local_to_hw(local), DISABLE_KEY, sta->addr, key, sta->aid); kfree(key); } } } else if (sta->key_idx_compression != HW_KEY_IDX_INVALID) { struct ieee80211_key_conf conf; memset(&conf, 0, sizeof(conf)); conf.hw_key_idx = sta->key_idx_compression; conf.alg = ALG_NULL; conf.flags |= IEEE80211_KEY_FORCE_SW_ENCRYPT; local->ops->set_key(local_to_hw(local), DISABLE_KEY, sta->addr, &conf, sta->aid); sta->key_idx_compression = HW_KEY_IDX_INVALID; }*/ /* #ifdef CONFIG_MAC80211_DEBUGFS if (in_atomic()) { list_add(&sta->list, &local->deleted_sta_list); queue_work(local->hw.workqueue, &local->sta_debugfs_add); } else #endif */ finish_sta_info_free(local, sta); } void sta_info_flush(struct ieee80211_local *local, struct net_device *dev) { IM_HERE_NOW(); struct sta_info *sta, *tmp; spin_lock_bh(&local->sta_lock); list_for_each_entry_safe(sta, tmp, &local->sta_list, list) if (!dev || dev == sta->dev) sta_info_free(sta, 1); spin_unlock_bh(&local->sta_lock); } int ieee80211_init_rate_ctrl_alg(struct ieee80211_local *local, const char *name) { IM_HERE_NOW(); struct rate_control_ref *ref, *old; //ASSERT_RTNL(); if (local->open_count || netif_running(local->mdev) || (local->apdev && netif_running(local->apdev))) return -EBUSY; ref = rate_control_alloc(name, local); if (!ref) { printk(KERN_WARNING "%s: Failed to select rate control " "algorithm\n", local->mdev->name); return -ENOENT; } old = local->rate_ctrl; local->rate_ctrl = ref; if (old) { rate_control_put(old); sta_info_flush(local, NULL); } printk(KERN_DEBUG "%s: Selected rate control " "algorithm '%s'\n", local->mdev->name, ref->ops->name); return 0; } static int ieee80211_regdom = 0x10; /* FCC */ static int ieee80211_japan_5ghz /* = 0 */; struct ieee80211_channel_range { short start_freq; short end_freq; unsigned char power_level; unsigned char antenna_max; }; static const struct ieee80211_channel_range ieee80211_fcc_channels[] = { { 2412, 2462, 27, 6 } /* IEEE 802.11b/g, channels 1..11 */, { 5180, 5240, 17, 6 } /* IEEE 802.11a, channels 36..48 */, { 5260, 5320, 23, 6 } /* IEEE 802.11a, channels 52..64 */, { 5745, 5825, 30, 6 } /* IEEE 802.11a, channels 149..165, outdoor */, { 0 } }; static const struct ieee80211_channel_range *channel_range = ieee80211_fcc_channels; static void ieee80211_unmask_channel(int mode, struct ieee80211_channel *chan) { int i; chan->flag = 0; if (ieee80211_regdom == 64 && (mode == MODE_ATHEROS_TURBO || mode == MODE_ATHEROS_TURBOG)) { /* Do not allow Turbo modes in Japan. */ return; } for (i = 0; channel_range[i].start_freq; i++) { const struct ieee80211_channel_range *r = &channel_range[i]; if (r->start_freq <= chan->freq && r->end_freq >= chan->freq) { if (ieee80211_regdom == 64 && !ieee80211_japan_5ghz && chan->freq >= 5260 && chan->freq <= 5320) { /* * Skip new channels in Japan since the * firmware was not marked having been upgraded * by the vendor. */ continue; } if (ieee80211_regdom == 0x10 && (chan->freq == 5190 || chan->freq == 5210 || chan->freq == 5230)) { /* Skip MKK channels when in FCC domain. */ continue; } chan->flag |= IEEE80211_CHAN_W_SCAN | IEEE80211_CHAN_W_ACTIVE_SCAN | IEEE80211_CHAN_W_IBSS; chan->power_level = r->power_level; chan->antenna_max = r->antenna_max; if (ieee80211_regdom == 64 && (chan->freq == 5170 || chan->freq == 5190 || chan->freq == 5210 || chan->freq == 5230)) { /* * New regulatory rules in Japan have backwards * compatibility with old channels in 5.15-5.25 * GHz band, but the station is not allowed to * use active scan on these old channels. */ chan->flag &= ~IEEE80211_CHAN_W_ACTIVE_SCAN; } if (ieee80211_regdom == 64 && (chan->freq == 5260 || chan->freq == 5280 || chan->freq == 5300 || chan->freq == 5320)) { /* * IBSS is not allowed on 5.25-5.35 GHz band * due to radar detection requirements. */ chan->flag &= ~IEEE80211_CHAN_W_IBSS; } break; } } } void ieee80211_set_default_regdomain(struct ieee80211_hw_mode *mode) { int c; for (c = 0; c < mode->num_channels; c++) ieee80211_unmask_channel(mode->mode, &mode->channels[c]); } #define max_t(type,x,y) \ ({ type __x = (x); type __y = (y); __x > __y ? __x: __y; }) int ieee80211_register_hw ( struct ieee80211_hw * hw){ IM_HERE_NOW(); struct ieee80211_local *local = hw_to_local(hw); const char *name; int result; /*result = wiphy_register(local->hw.wiphy); if (result < 0) return result; name = wiphy_dev(local->hw.wiphy)->driver->name;*/ local->hw.workqueue = create_workqueue("singlethread_workqueue");//create_singlethread_workqueue(name); if (!local->hw.workqueue) { result = -ENOMEM; return result; //goto fail_workqueue; } /* * The hardware needs headroom for sending the frame, * and we need some headroom for passing the frame to monitor * interfaces, but never both at the same time. */ local->tx_headroom = max_t(unsigned int , local->hw.extra_tx_headroom, sizeof(struct ieee80211_tx_status_rtap_hdr)); //debugfs_hw_add(local); local->hw.conf.beacon_int = 1000; local->wstats_flags |= local->hw.max_rssi ? IW_QUAL_LEVEL_UPDATED : IW_QUAL_LEVEL_INVALID; local->wstats_flags |= local->hw.max_signal ? IW_QUAL_QUAL_UPDATED : IW_QUAL_QUAL_INVALID; local->wstats_flags |= local->hw.max_noise ? IW_QUAL_NOISE_UPDATED : IW_QUAL_NOISE_INVALID; if (local->hw.max_rssi < 0 || local->hw.max_noise < 0) local->wstats_flags |= IW_QUAL_DBM; result = sta_info_start(local); if (result < 0) return -1; // goto fail_sta_info; //char ii[4]="en1"; //sprintf(ii,"%s%d" ,my_fNetif->getNamePrefix(), my_fNetif->getUnitNumber()); //bcopy(ii,local->mdev->name,sizeof(ii)); /*rtnl_lock(); result = dev_alloc_name(local->mdev, local->mdev->name); if (result < 0) goto fail_dev;*/ memcpy(local->mdev->dev_addr, my_mac_addr, ETH_ALEN);//local->hw.wiphy->perm_addr, ETH_ALEN); //check this //SET_NETDEV_DEV(local->mdev, wiphy_dev(local->hw.wiphy)); /*result = register_netdevice(local->mdev); if (result < 0) goto fail_dev; ieee80211_debugfs_add_netdev(IEEE80211_DEV_TO_SUB_IF(local->mdev));*/ result = ieee80211_init_rate_ctrl_alg(local, NULL); if (result < 0) { printk(KERN_DEBUG "%s: Failed to initialize rate control " "algorithm\n", local->mdev->name); //goto fail_rate; } //this one maybe /* result = ieee80211_wep_init(local); if (result < 0) { printk(KERN_DEBUG "%s: Failed to initialize wep\n", local->mdev->name); goto fail_wep; }*/ //ieee80211_install_qdisc(local->mdev); /* add one default STA interface */ result = ieee80211_if_add(local->mdev, local->mdev->name, NULL, IEEE80211_IF_TYPE_STA); if (result) printk(KERN_WARNING "%s: Failed to add default virtual iface\n", local->mdev->name); (int)local->reg_state = 1;//IEEE80211_DEV_REGISTERED;//check this /*rtnl_unlock(); ieee80211_led_init(local);*/ return 0; /*fail_wep: rate_control_deinitialize(local); fail_rate: ieee80211_debugfs_remove_netdev(IEEE80211_DEV_TO_SUB_IF(local->mdev)); unregister_netdevice(local->mdev); fail_dev: rtnl_unlock(); sta_info_stop(local); fail_sta_info: debugfs_hw_del(local); destroy_workqueue(local->hw.workqueue); fail_workqueue: wiphy_unregister(local->hw.wiphy);*/ return result; } void ieee80211_unregister_hw(struct ieee80211_hw * hw){ IM_HERE_NOW(); return; } void ieee80211_start_queues(struct ieee80211_hw *hw){ IM_HERE_NOW(); struct ieee80211_local *local = hw_to_local(hw); int i; for (i = 0; i < local->hw.queues; i++) clear_bit(IEEE80211_LINK_STATE_XOFF, &local->state[i]); } typedef enum { ParseOK = 0, ParseUnknown = 1, ParseFailed = -1 } ParseRes; static ParseRes ieee802_11_parse_elems(u8 *start, size_t len, struct ieee802_11_elems *elems) { IM_HERE_NOW(); size_t left = len; u8 *pos = start; int unknown = 0; memset(elems, 0, sizeof(*elems)); while (left >= 2) { u8 id, elen; id = *pos++; elen = *pos++; left -= 2; if (elen > left) { #if 0 if (net_ratelimit()) printk(KERN_DEBUG "IEEE 802.11 element parse " "failed (id=%d elen=%d left=%d)\n", id, elen, left); #endif return ParseFailed; } switch (id) { case WLAN_EID_SSID: elems->ssid = pos; elems->ssid_len = elen; break; case WLAN_EID_SUPP_RATES: elems->supp_rates = pos; elems->supp_rates_len = elen; break; case WLAN_EID_FH_PARAMS: elems->fh_params = pos; elems->fh_params_len = elen; break; case WLAN_EID_DS_PARAMS: elems->ds_params = pos; elems->ds_params_len = elen; break; case WLAN_EID_CF_PARAMS: elems->cf_params = pos; elems->cf_params_len = elen; break; case WLAN_EID_TIM: elems->tim = pos; elems->tim_len = elen; break; case WLAN_EID_IBSS_PARAMS: elems->ibss_params = pos; elems->ibss_params_len = elen; break; case WLAN_EID_CHALLENGE: elems->challenge = pos; elems->challenge_len = elen; break; case WLAN_EID_WPA: if (elen >= 4 && pos[0] == 0x00 && pos[1] == 0x50 && pos[2] == 0xf2) { /* Microsoft OUI (00:50:F2) */ if (pos[3] == 1) { /* OUI Type 1 - WPA IE */ elems->wpa = pos; elems->wpa_len = elen; } else if (elen >= 5 && pos[3] == 2) { if (pos[4] == 0) { elems->wmm_info = pos; elems->wmm_info_len = elen; } else if (pos[4] == 1) { elems->wmm_param = pos; elems->wmm_param_len = elen; } } } break; case WLAN_EID_RSN: elems->rsn = pos; elems->rsn_len = elen; break; case WLAN_EID_ERP_INFO: elems->erp_info = pos; elems->erp_info_len = elen; break; case WLAN_EID_EXT_SUPP_RATES: elems->ext_supp_rates = pos; elems->ext_supp_rates_len = elen; break; default: #if 0 printk(KERN_DEBUG "IEEE 802.11 element parse ignored " "unknown element (id=%d elen=%d)\n", id, elen); #endif unknown++; break; } left -= elen; pos += elen; } /* Do not trigger error if left == 1 as Apple Airport base stations * send AssocResps that are one spurious byte too long. */ return unknown ? ParseUnknown : ParseOK; } static struct ieee80211_sta_bss * ieee80211_rx_bss_get(struct net_device *dev, u8 *bssid) { IM_HERE_NOW(); struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); struct ieee80211_sta_bss *bss; spin_lock_bh(&local->sta_bss_lock); bss = local->sta_bss_hash[STA_HASH(bssid)]; while (bss) { if (memcmp(bss->bssid, bssid, ETH_ALEN) == 0) { atomic_inc(&bss->users); break; } bss = bss->hnext; } spin_unlock_bh(&local->sta_bss_lock); return bss; } /* Caller must hold local->sta_bss_lock */ static void __ieee80211_rx_bss_hash_add(struct net_device *dev, struct ieee80211_sta_bss *bss) { IM_HERE_NOW(); struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); bss->hnext = local->sta_bss_hash[STA_HASH(bss->bssid)]; local->sta_bss_hash[STA_HASH(bss->bssid)] = bss; } static struct ieee80211_sta_bss * ieee80211_rx_bss_add(struct net_device *dev, u8 *bssid) { IM_HERE_NOW(); struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); struct ieee80211_sta_bss *bss; bss = (ieee80211_sta_bss*)kzalloc(sizeof(*bss), GFP_ATOMIC); if (!bss) return NULL; atomic_inc(&bss->users); //atomic_inc(&bss->users);//hack memcpy(bss->bssid, bssid, ETH_ALEN); spin_lock_bh(&local->sta_bss_lock); /* TODO: order by RSSI? */ list_add_tail(&bss->list, &local->sta_bss_list); __ieee80211_rx_bss_hash_add(dev, bss); spin_unlock_bh(&local->sta_bss_lock); printk("bss_add= " MAC_FMT " ('%s')\n", MAC_ARG(bss->bssid), escape_essid((const char*)bss->ssid, bss->ssid_len)); return bss; } /* Caller must hold local->sta_bss_lock */ static void __ieee80211_rx_bss_hash_del(struct net_device *dev, struct ieee80211_sta_bss *bss) { IM_HERE_NOW(); struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); struct ieee80211_sta_bss *b, *prev = NULL; b = local->sta_bss_hash[STA_HASH(bss->bssid)]; while (b) { if (b == bss) { if (!prev) local->sta_bss_hash[STA_HASH(bss->bssid)] = bss->hnext; else prev->hnext = bss->hnext; break; } prev = b; b = b->hnext; } } static void ieee80211_rx_bss_free(struct ieee80211_sta_bss *bss) { IM_HERE_NOW(); kfree(bss->wpa_ie); kfree(bss->rsn_ie); kfree(bss->wmm_ie); kfree(bss); } static void ieee80211_rx_bss_put(struct net_device *dev, struct ieee80211_sta_bss *bss) { IM_HERE_NOW(); struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); if (!atomic_dec_and_test(&bss->users)) return; spin_lock_bh(&local->sta_bss_lock); __ieee80211_rx_bss_hash_del(dev, bss); list_del(&bss->list); spin_unlock_bh(&local->sta_bss_lock); ieee80211_rx_bss_free(bss); } static void ieee80211_rx_bss_info(struct net_device *dev, struct ieee80211_mgmt *mgmt, size_t len, struct ieee80211_rx_status *rx_status, int beacon) { IM_HERE_NOW(); struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); struct ieee802_11_elems elems; size_t baselen; int channel, invalid = 0, clen; struct ieee80211_sta_bss *bss; struct sta_info *sta; struct ieee80211_sub_if_data *sdata = (ieee80211_sub_if_data*)IEEE80211_DEV_TO_SUB_IF(dev); u64 timestamp; if (!beacon && memcmp(mgmt->da, dev->dev_addr, ETH_ALEN)) return; /* ignore ProbeResp to foreign address */ #if 1 printk(KERN_DEBUG "%s: RX %s from " MAC_FMT " to " MAC_FMT "\n", dev->name, beacon ? "Beacon" : "Probe Response", MAC_ARG(mgmt->sa), MAC_ARG(mgmt->da)); /*if (!beacon) { IOLog("hacking add station\n"); struct ieee80211_if_sta *ifsta = &sdata->u.sta; bcopy(mgmt->sa,sdata->u.sta.bssid,ETH_ALEN); }*/ #endif baselen = (u8 *) mgmt->u.beacon.variable - (u8 *) mgmt; if (baselen > len) return; timestamp = le64_to_cpu(mgmt->u.beacon.timestamp); if (sdata->type == IEEE80211_IF_TYPE_IBSS && beacon && memcmp(mgmt->bssid, sdata->u.sta.bssid, ETH_ALEN) == 0) { #ifdef CONFIG_MAC80211_IBSS_DEBUG static unsigned long last_tsf_debug = 0; u64 tsf; if (local->ops->get_tsf) tsf = local->ops->get_tsf(local_to_hw(local)); else tsf = -1LLU; if (time_after(jiffies, last_tsf_debug + 5 * HZ)) { printk(KERN_DEBUG "RX beacon SA=" MAC_FMT " BSSID=" MAC_FMT " TSF=0x%llx BCN=0x%llx diff=%lld " "@%lu\n", MAC_ARG(mgmt->sa), MAC_ARG(mgmt->bssid), (unsigned long long)tsf, (unsigned long long)timestamp, (unsigned long long)(tsf - timestamp), jiffies); last_tsf_debug = jiffies; } #endif /* CONFIG_MAC80211_IBSS_DEBUG */ } if (ieee802_11_parse_elems(mgmt->u.beacon.variable, len - baselen, &elems) == ParseFailed) invalid = 1; if (sdata->type == IEEE80211_IF_TYPE_IBSS && elems.supp_rates && memcmp(mgmt->bssid, sdata->u.sta.bssid, ETH_ALEN) == 0 && (sta = sta_info_get(local, mgmt->sa))) { struct ieee80211_hw_mode *mode; struct ieee80211_rate *rates; size_t num_rates; u32 supp_rates, prev_rates; int i, j; mode = local->sta_scanning ? local->scan_hw_mode : local->oper_hw_mode; rates = mode->rates; num_rates = mode->num_rates; supp_rates = 0; for (i = 0; i < elems.supp_rates_len + elems.ext_supp_rates_len; i++) { u8 rate = 0; int own_rate; if (i < elems.supp_rates_len) rate = elems.supp_rates[i]; else if (elems.ext_supp_rates) rate = elems.ext_supp_rates [i - elems.supp_rates_len]; own_rate = 5 * (rate & 0x7f); if (mode->mode == MODE_ATHEROS_TURBO) own_rate *= 2; for (j = 0; j < num_rates; j++) if (rates[j].rate == own_rate) supp_rates |= BIT(j); } prev_rates = sta->supp_rates; sta->supp_rates &= supp_rates; if (sta->supp_rates == 0) { /* No matching rates - this should not really happen. * Make sure that at least one rate is marked * supported to avoid issues with TX rate ctrl. */ sta->supp_rates = sdata->u.sta.supp_rates_bits; } if (sta->supp_rates != prev_rates) { printk(KERN_DEBUG "%s: updated supp_rates set for " MAC_FMT " based on beacon info (0x%x & 0x%x -> " "0x%x)\n", dev->name, MAC_ARG(sta->addr), prev_rates, supp_rates, sta->supp_rates); } sta_info_put(sta); } if (!elems.ssid) return; if (elems.ds_params && elems.ds_params_len == 1) channel = elems.ds_params[0]; else channel = rx_status->channel; bss = ieee80211_rx_bss_get(dev, mgmt->bssid); if (!bss) { bss = ieee80211_rx_bss_add(dev, mgmt->bssid); if (!bss) return; } else { #if 0 /* TODO: order by RSSI? */ spin_lock_bh(&local->sta_bss_lock); list_move_tail(&bss->list, &local->sta_bss_list); spin_unlock_bh(&local->sta_bss_lock); #endif } if (bss->probe_resp && beacon) { /* Do not allow beacon to override data from Probe Response. */ ieee80211_rx_bss_put(dev, bss); return; } /* save the ERP value so that it is available at association time */ if (elems.erp_info && elems.erp_info_len >= 1) { bss->erp_value = elems.erp_info[0]; bss->has_erp_value = 1; } bss->beacon_int = le16_to_cpu(mgmt->u.beacon.beacon_int); bss->capability = le16_to_cpu(mgmt->u.beacon.capab_info); if (elems.ssid && elems.ssid_len <= IEEE80211_MAX_SSID_LEN) { memcpy(bss->ssid, elems.ssid, elems.ssid_len); bss->ssid_len = elems.ssid_len; } bss->supp_rates_len = 0; if (elems.supp_rates) { clen = IEEE80211_MAX_SUPP_RATES - bss->supp_rates_len; if (clen > elems.supp_rates_len) clen = elems.supp_rates_len; memcpy(&bss->supp_rates[bss->supp_rates_len], elems.supp_rates, clen); bss->supp_rates_len += clen; } if (elems.ext_supp_rates) { clen = IEEE80211_MAX_SUPP_RATES - bss->supp_rates_len; if (clen > elems.ext_supp_rates_len) clen = elems.ext_supp_rates_len; memcpy(&bss->supp_rates[bss->supp_rates_len], elems.ext_supp_rates, clen); bss->supp_rates_len += clen; } if (elems.wpa && (!bss->wpa_ie || bss->wpa_ie_len != elems.wpa_len || memcmp(bss->wpa_ie, elems.wpa, elems.wpa_len))) { kfree(bss->wpa_ie); bss->wpa_ie = (u8 *)kmalloc(elems.wpa_len + 2, GFP_ATOMIC); if (bss->wpa_ie) { memcpy(bss->wpa_ie, elems.wpa - 2, elems.wpa_len + 2); bss->wpa_ie_len = elems.wpa_len + 2; } else bss->wpa_ie_len = 0; } else if (!elems.wpa && bss->wpa_ie) { kfree(bss->wpa_ie); bss->wpa_ie = NULL; bss->wpa_ie_len = 0; } if (elems.rsn && (!bss->rsn_ie || bss->rsn_ie_len != elems.rsn_len || memcmp(bss->rsn_ie, elems.rsn, elems.rsn_len))) { kfree(bss->rsn_ie); bss->rsn_ie = (u8 *)kmalloc(elems.rsn_len + 2, GFP_ATOMIC); if (bss->rsn_ie) { memcpy(bss->rsn_ie, elems.rsn - 2, elems.rsn_len + 2); bss->rsn_ie_len = elems.rsn_len + 2; } else bss->rsn_ie_len = 0; } else if (!elems.rsn && bss->rsn_ie) { kfree(bss->rsn_ie); bss->rsn_ie = NULL; bss->rsn_ie_len = 0; } if (elems.wmm_param && (!bss->wmm_ie || bss->wmm_ie_len != elems.wmm_param_len || memcmp(bss->wmm_ie, elems.wmm_param, elems.wmm_param_len))) { kfree(bss->wmm_ie); bss->wmm_ie = (u8 *)kmalloc(elems.wmm_param_len + 2, GFP_ATOMIC); if (bss->wmm_ie) { memcpy(bss->wmm_ie, elems.wmm_param - 2, elems.wmm_param_len + 2); bss->wmm_ie_len = elems.wmm_param_len + 2; } else bss->wmm_ie_len = 0; } else if (elems.wmm_info && (!bss->wmm_ie || bss->wmm_ie_len != elems.wmm_info_len || memcmp(bss->wmm_ie, elems.wmm_info, elems.wmm_info_len))) { /* As for certain AP's Fifth bit is not set in WMM IE in * beacon frames.So while parsing the beacon frame the * wmm_info structure is used instead of wmm_param. * wmm_info structure was never used to set bss->wmm_ie. * This code fixes this problem by copying the WME * information from wmm_info to bss->wmm_ie and enabling * n-band association. */ kfree(bss->wmm_ie); bss->wmm_ie = (u8*)kmalloc(elems.wmm_info_len + 2, GFP_ATOMIC); if (bss->wmm_ie) { memcpy(bss->wmm_ie, elems.wmm_info - 2, elems.wmm_info_len + 2); bss->wmm_ie_len = elems.wmm_info_len + 2; } else bss->wmm_ie_len = 0; } else if (!elems.wmm_param && !elems.wmm_info && bss->wmm_ie) { kfree(bss->wmm_ie); bss->wmm_ie = NULL; bss->wmm_ie_len = 0; } bss->hw_mode = rx_status->phymode; bss->channel = channel; bss->freq = rx_status->freq; if (channel != rx_status->channel && (bss->hw_mode == MODE_IEEE80211G || bss->hw_mode == MODE_IEEE80211B) && channel >= 1 && channel <= 14) { static const int freq_list[] = { 2412, 2417, 2422, 2427, 2432, 2437, 2442, 2447, 2452, 2457, 2462, 2467, 2472, 2484 }; /* IEEE 802.11g/b mode can receive packets from neighboring * channels, so map the channel into frequency. */ bss->freq = freq_list[channel - 1]; } bss->timestamp = timestamp; bss->last_update = jiffies; bss->rssi = rx_status->ssi; bss->signal = rx_status->signal; bss->noise = rx_status->noise; if (!beacon) bss->probe_resp++; ieee80211_rx_bss_put(dev, bss); } static void ieee80211_rx_mgmt_probe_resp(struct net_device *dev, struct ieee80211_mgmt *mgmt, size_t len, struct ieee80211_rx_status *rx_status) { IM_HERE_NOW(); ieee80211_rx_bss_info(dev, mgmt, len, rx_status, 0); /*struct ieee80211_sub_if_data *sdata = (ieee80211_sub_if_data*)IEEE80211_DEV_TO_SUB_IF(dev); struct ieee80211_if_sta *ifsta = &sdata->u.sta; if (ifsta->state != IEEE80211_AUTHENTICATE && ifsta->state != IEEE80211_ASSOCIATE && ifsta->state != IEEE80211_ASSOCIATED) ieee80211_sta_config_auth(dev, ifsta);*/ } static void ieee80211_handle_erp_ie(struct net_device *dev, u8 erp_value) { IM_HERE_NOW(); struct ieee80211_sub_if_data *sdata = (ieee80211_sub_if_data *)IEEE80211_DEV_TO_SUB_IF(dev); struct ieee80211_if_sta *ifsta = &sdata->u.sta; int use_protection = (erp_value & WLAN_ERP_USE_PROTECTION) != 0; if (use_protection != sdata->use_protection) { if (net_ratelimit()) { printk(KERN_DEBUG "%s: CTS protection %s (BSSID=" MAC_FMT ")\n", dev->name, use_protection ? "enabled" : "disabled", MAC_ARG(ifsta->bssid)); } sdata->use_protection = use_protection; } } static int ecw2cw(int ecw) { int cw = 1; while (ecw > 0) { cw <<= 1; ecw--; } return cw - 1; } static void ieee80211_sta_wmm_params(struct net_device *dev, struct ieee80211_if_sta *ifsta, u8 *wmm_param, size_t wmm_param_len) { IM_HERE_NOW(); struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); struct ieee80211_tx_queue_params params; size_t left; int count; u8 *pos; if (wmm_param_len < 8 || wmm_param[5] /* version */ != 1) return; count = wmm_param[6] & 0x0f; if (count == ifsta->wmm_last_param_set) return; ifsta->wmm_last_param_set = count; pos = wmm_param + 8; left = wmm_param_len - 8; memset(¶ms, 0, sizeof(params)); if (!local->ops->conf_tx) return; local->wmm_acm = 0; for (; left >= 4; left -= 4, pos += 4) { int aci = (pos[0] >> 5) & 0x03; int acm = (pos[0] >> 4) & 0x01; int queue; switch (aci) { case 1: queue = IEEE80211_TX_QUEUE_DATA3; if (acm) { local->wmm_acm |= BIT(0) | BIT(3); } break; case 2: queue = IEEE80211_TX_QUEUE_DATA1; if (acm) { local->wmm_acm |= BIT(4) | BIT(5); } break; case 3: queue = IEEE80211_TX_QUEUE_DATA0; if (acm) { local->wmm_acm |= BIT(6) | BIT(7); } break; case 0: default: queue = IEEE80211_TX_QUEUE_DATA2; if (acm) { local->wmm_acm |= BIT(1) | BIT(2); } break; } params.aifs = pos[0] & 0x0f; params.cw_max = ecw2cw((pos[1] & 0xf0) >> 4); params.cw_min = ecw2cw(pos[1] & 0x0f); /* TXOP is in units of 32 usec; burst_time in 0.1 ms */ params.burst_time = (pos[2] | (pos[3] << 8)) * 32 / 100; printk(KERN_DEBUG "%s: WMM queue=%d aci=%d acm=%d aifs=%d " "cWmin=%d cWmax=%d burst=%d\n", dev->name, queue, aci, acm, params.aifs, params.cw_min, params.cw_max, params.burst_time); /* TODO: handle ACM (block TX, fallback to next lowest allowed * AC for now) */ if (local->ops->conf_tx(local_to_hw(local), queue, ¶ms)) { printk(KERN_DEBUG "%s: failed to set TX queue " "parameters for queue %d\n", dev->name, queue); } } } static void ieee80211_rx_mgmt_beacon(struct net_device *dev, struct ieee80211_mgmt *mgmt, size_t len, struct ieee80211_rx_status *rx_status) { IM_HERE_NOW(); struct ieee80211_sub_if_data *sdata; struct ieee80211_if_sta *ifsta; size_t baselen; struct ieee802_11_elems elems; ieee80211_rx_bss_info(dev, mgmt, len, rx_status, 1); sdata = (ieee80211_sub_if_data *)IEEE80211_DEV_TO_SUB_IF(dev); if (sdata->type != IEEE80211_IF_TYPE_STA) return; ifsta = &sdata->u.sta; if (!ifsta->associated || memcmp(ifsta->bssid, mgmt->bssid, ETH_ALEN) != 0) return; /* Process beacon from the current BSS */ baselen = (u8 *) mgmt->u.beacon.variable - (u8 *) mgmt; if (baselen > len) return; if (ieee802_11_parse_elems(mgmt->u.beacon.variable, len - baselen, &elems) == ParseFailed) return; if (elems.erp_info && elems.erp_info_len >= 1) ieee80211_handle_erp_ie(dev, elems.erp_info[0]); if (elems.wmm_param && ifsta->wmm_enabled) { ieee80211_sta_wmm_params(dev, ifsta, elems.wmm_param, elems.wmm_param_len); } } void ieee80211_sta_rx_scan(struct net_device *dev, struct sk_buff *skb, struct ieee80211_rx_status *rx_status) { IM_HERE_NOW(); struct ieee80211_mgmt *mgmt; u16 fc; if (skb_len(skb) < 24) { dev_kfree_skb(skb); return; } mgmt = (struct ieee80211_mgmt *) skb_data(skb); fc = le16_to_cpu(mgmt->frame_control); if ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT) { if ((fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_PROBE_RESP) { ieee80211_rx_mgmt_probe_resp(dev, mgmt, skb_len(skb), rx_status); } else if ((fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_BEACON) { ieee80211_rx_mgmt_beacon(dev, mgmt, skb_len(skb), rx_status); } } dev_kfree_skb(skb); } static void ieee80211_send_auth(struct net_device *dev, struct ieee80211_if_sta *ifsta, int transaction, u8 *extra, size_t extra_len, int encrypt) { IM_HERE_NOW(); struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); struct sk_buff *skb; struct ieee80211_mgmt *mgmt; skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*mgmt) + 6 + extra_len); if (!skb) { printk(KERN_DEBUG "%s: failed to allocate buffer for auth " "frame\n", dev->name); return; } skb_reserve(skb, local->hw.extra_tx_headroom); mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24 + 6); memset(mgmt, 0, 24 + 6); mgmt->frame_control = IEEE80211_FC(IEEE80211_FTYPE_MGMT, IEEE80211_STYPE_AUTH); if (encrypt) mgmt->frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED); memcpy(mgmt->da, ifsta->bssid, ETH_ALEN); memcpy(mgmt->sa, dev->dev_addr, ETH_ALEN); memcpy(mgmt->bssid, ifsta->bssid, ETH_ALEN); mgmt->u.auth.auth_alg = cpu_to_le16(ifsta->auth_alg); mgmt->u.auth.auth_transaction = cpu_to_le16(transaction); ifsta->auth_transaction = transaction + 1; mgmt->u.auth.status_code = cpu_to_le16(0); if (extra) memcpy(skb_put(skb, extra_len), extra, extra_len); ieee80211_sta_tx(dev, skb, encrypt); } static int ieee80211_sta_wep_configured(struct net_device *dev) { IM_HERE_NOW(); struct ieee80211_sub_if_data *sdata = (struct ieee80211_sub_if_data*)IEEE80211_DEV_TO_SUB_IF(dev); if (!sdata || !sdata->default_key || sdata->default_key->alg != ALG_WEP) return 0; return 1; } static int ieee80211_privacy_mismatch(struct net_device *dev, struct ieee80211_if_sta *ifsta) { IM_HERE_NOW(); struct ieee80211_sta_bss *bss; int res = 0; if (!ifsta || ifsta->mixed_cell || ifsta->key_mgmt != IEEE80211_KEY_MGMT_NONE) return 0; bss = ieee80211_rx_bss_get(dev, ifsta->bssid); if (!bss) return 0; if (ieee80211_sta_wep_configured(dev) != !!(bss->capability & WLAN_CAPABILITY_PRIVACY)) res = 1; ieee80211_rx_bss_put(dev, bss); return res; } static void ieee80211_send_assoc(struct net_device *dev, struct ieee80211_if_sta *ifsta) { IM_HERE_NOW(); struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); struct ieee80211_hw_mode *mode; struct sk_buff *skb; struct ieee80211_mgmt *mgmt; u8 *pos, *ies; int i, len; u16 capab; struct ieee80211_sta_bss *bss; int wmm = 0; skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*mgmt) + 200 + ifsta->extra_ie_len + ifsta->ssid_len); if (!skb) { printk(KERN_DEBUG "%s: failed to allocate buffer for assoc " "frame\n", dev->name); return; } skb_reserve(skb, local->hw.extra_tx_headroom); mode = local->oper_hw_mode; capab = ifsta->capab; if (mode->mode == MODE_IEEE80211G) { capab |= WLAN_CAPABILITY_SHORT_SLOT_TIME | WLAN_CAPABILITY_SHORT_PREAMBLE; } bss = ieee80211_rx_bss_get(dev, ifsta->bssid); if (bss) { if (bss->capability & WLAN_CAPABILITY_PRIVACY) capab |= WLAN_CAPABILITY_PRIVACY; if (bss->wmm_ie) { wmm = 1; } ieee80211_rx_bss_put(dev, bss); } mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24); memset(mgmt, 0, 24); memcpy(mgmt->da, ifsta->bssid, ETH_ALEN); memcpy(mgmt->sa, dev->dev_addr, ETH_ALEN); memcpy(mgmt->bssid, ifsta->bssid, ETH_ALEN); if (ifsta->prev_bssid_set) { skb_put(skb, 10); mgmt->frame_control = IEEE80211_FC(IEEE80211_FTYPE_MGMT, IEEE80211_STYPE_REASSOC_REQ); mgmt->u.reassoc_req.capab_info = cpu_to_le16(capab); mgmt->u.reassoc_req.listen_interval = cpu_to_le16(1); memcpy(mgmt->u.reassoc_req.current_ap, ifsta->prev_bssid, ETH_ALEN); } else { skb_put(skb, 4); mgmt->frame_control = IEEE80211_FC(IEEE80211_FTYPE_MGMT, IEEE80211_STYPE_ASSOC_REQ); mgmt->u.assoc_req.capab_info = cpu_to_le16(capab); mgmt->u.assoc_req.listen_interval = cpu_to_le16(1); } /* SSID */ ies = pos = (u8*)skb_put(skb, 2 + ifsta->ssid_len); *pos++ = WLAN_EID_SSID; *pos++ = ifsta->ssid_len; memcpy(pos, ifsta->ssid, ifsta->ssid_len); len = mode->num_rates; if (len > 8) len = 8; pos = (u8*)skb_put(skb, len + 2); *pos++ = WLAN_EID_SUPP_RATES; *pos++ = len; for (i = 0; i < len; i++) { int rate = mode->rates[i].rate; if (mode->mode == MODE_ATHEROS_TURBO) rate /= 2; *pos++ = (u8) (rate / 5); } if (mode->num_rates > len) { pos = (u8*)skb_put(skb, mode->num_rates - len + 2); *pos++ = WLAN_EID_EXT_SUPP_RATES; *pos++ = mode->num_rates - len; for (i = len; i < mode->num_rates; i++) { int rate = mode->rates[i].rate; if (mode->mode == MODE_ATHEROS_TURBO) rate /= 2; *pos++ = (u8) (rate / 5); } } if (ifsta->extra_ie) { pos = (u8*)skb_put(skb, ifsta->extra_ie_len); memcpy(pos, ifsta->extra_ie, ifsta->extra_ie_len); } if (wmm && ifsta->wmm_enabled) { pos = (u8*)skb_put(skb, 9); *pos++ = WLAN_EID_VENDOR_SPECIFIC; *pos++ = 7; /* len */ *pos++ = 0x00; /* Microsoft OUI 00:50:F2 */ *pos++ = 0x50; *pos++ = 0xf2; *pos++ = 2; /* WME */ *pos++ = 0; /* WME info */ *pos++ = 1; /* WME ver */ *pos++ = 0; } kfree(ifsta->assocreq_ies); ifsta->assocreq_ies_len = ((u8*)skb_data(skb) + mbuf_len(skb->mac_data)) - ies; ifsta->assocreq_ies = (u8*)kmalloc(ifsta->assocreq_ies_len, GFP_ATOMIC); if (ifsta->assocreq_ies) memcpy(ifsta->assocreq_ies, ies, ifsta->assocreq_ies_len); ieee80211_sta_tx(dev, skb, 0); } void ieee80211_associate(struct net_device *dev, struct ieee80211_if_sta *ifsta) { IM_HERE_NOW(); ifsta->assoc_tries++; if (ifsta->assoc_tries > IEEE80211_ASSOC_MAX_TRIES) { printk(KERN_DEBUG "%s: association with AP " MAC_FMT " timed out\n", dev->name, MAC_ARG(ifsta->bssid)); ifsta->state = IEEE80211_DISABLED; //ifsta->assoc_tries=0;//hack //del_timer(&ifsta->timer);//hack return; } ifsta->state = IEEE80211_ASSOCIATE; printk(KERN_DEBUG "%s: associate with AP " MAC_FMT "\n", dev->name, MAC_ARG(ifsta->bssid)); if (ieee80211_privacy_mismatch(dev, ifsta)) { printk(KERN_DEBUG "%s: mismatch in privacy configuration and " "mixed-cell disabled - abort association\n", dev->name); ifsta->state = IEEE80211_DISABLED; return; } ieee80211_send_assoc(dev, ifsta); mod_timer(&ifsta->timer, IEEE80211_ASSOC_TIMEOUT); } static void ieee80211_auth_completed(struct net_device *dev, struct ieee80211_if_sta *ifsta) { IM_HERE_NOW(); printk(KERN_DEBUG "%s: authenticated\n", dev->name); ifsta->authenticated = 1; ieee80211_associate(dev, ifsta); } static void ieee80211_auth_challenge(struct net_device *dev, struct ieee80211_if_sta *ifsta, struct ieee80211_mgmt *mgmt, size_t len) { IM_HERE_NOW(); u8 *pos; struct ieee802_11_elems elems; printk(KERN_DEBUG "%s: replying to auth challenge\n", dev->name); pos = mgmt->u.auth.variable; if (ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), &elems) == ParseFailed) { printk(KERN_DEBUG "%s: failed to parse Auth(challenge)\n", dev->name); return; } if (!elems.challenge) { printk(KERN_DEBUG "%s: no challenge IE in shared key auth " "frame\n", dev->name); return; } ieee80211_send_auth(dev, ifsta, 3, elems.challenge - 2, elems.challenge_len + 2, 1); } static void ieee80211_rx_mgmt_auth(struct net_device *dev, struct ieee80211_if_sta *ifsta, struct ieee80211_mgmt *mgmt, size_t len) { IM_HERE_NOW(); struct ieee80211_sub_if_data *sdata = (struct ieee80211_sub_if_data*)IEEE80211_DEV_TO_SUB_IF(dev); u16 auth_alg, auth_transaction, status_code; if (ifsta->state != IEEE80211_AUTHENTICATE && sdata->type != IEEE80211_IF_TYPE_IBSS) { printk(KERN_DEBUG "%s: authentication frame received from " MAC_FMT ", but not in authenticate state - ignored\n", dev->name, MAC_ARG(mgmt->sa)); return; } if (len < 24 + 6) { printk(KERN_DEBUG "%s: too short (%zd) authentication frame " "received from " MAC_FMT " - ignored\n", dev->name, len, MAC_ARG(mgmt->sa)); return; } if (sdata->type != IEEE80211_IF_TYPE_IBSS && memcmp(ifsta->bssid, mgmt->sa, ETH_ALEN) != 0) { printk(KERN_DEBUG "%s: authentication frame received from " "unknown AP (SA=" MAC_FMT " BSSID=" MAC_FMT ") - " "ignored\n", dev->name, MAC_ARG(mgmt->sa), MAC_ARG(mgmt->bssid)); return; } if (sdata->type != IEEE80211_IF_TYPE_IBSS && memcmp(ifsta->bssid, mgmt->bssid, ETH_ALEN) != 0) { printk(KERN_DEBUG "%s: authentication frame received from " "unknown BSSID (SA=" MAC_FMT " BSSID=" MAC_FMT ") - " "ignored\n", dev->name, MAC_ARG(mgmt->sa), MAC_ARG(mgmt->bssid)); return; } auth_alg = le16_to_cpu(mgmt->u.auth.auth_alg); auth_transaction = le16_to_cpu(mgmt->u.auth.auth_transaction); status_code = le16_to_cpu(mgmt->u.auth.status_code); printk(KERN_DEBUG "%s: RX authentication from " MAC_FMT " (alg=%d " "transaction=%d status=%d)\n", dev->name, MAC_ARG(mgmt->sa), auth_alg, auth_transaction, status_code); if (sdata->type == IEEE80211_IF_TYPE_IBSS) { /* IEEE 802.11 standard does not require authentication in IBSS * networks and most implementations do not seem to use it. * However, try to reply to authentication attempts if someone * has actually implemented this. * TODO: Could implement shared key authentication. */ if (auth_alg != WLAN_AUTH_OPEN || auth_transaction != 1) { printk(KERN_DEBUG "%s: unexpected IBSS authentication " "frame (alg=%d transaction=%d)\n", dev->name, auth_alg, auth_transaction); return; } ieee80211_send_auth(dev, ifsta, 2, NULL, 0, 0); } if (auth_alg != ifsta->auth_alg || auth_transaction != ifsta->auth_transaction) { printk(KERN_DEBUG "%s: unexpected authentication frame " "(alg=%d transaction=%d)\n", dev->name, auth_alg, auth_transaction); return; } if (status_code != WLAN_STATUS_SUCCESS) { printk(KERN_DEBUG "%s: AP denied authentication (auth_alg=%d " "code=%d)\n", dev->name, ifsta->auth_alg, status_code); if (status_code == WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG) { u8 algs[3]; const int num_algs = ARRAY_SIZE(algs); int i, pos; algs[0] = algs[1] = algs[2] = 0xff; if (ifsta->auth_algs & IEEE80211_AUTH_ALG_OPEN) algs[0] = WLAN_AUTH_OPEN; if (ifsta->auth_algs & IEEE80211_AUTH_ALG_SHARED_KEY) algs[1] = WLAN_AUTH_SHARED_KEY; if (ifsta->auth_algs & IEEE80211_AUTH_ALG_LEAP) algs[2] = WLAN_AUTH_LEAP; if (ifsta->auth_alg == WLAN_AUTH_OPEN) pos = 0; else if (ifsta->auth_alg == WLAN_AUTH_SHARED_KEY) pos = 1; else pos = 2; for (i = 0; i < num_algs; i++) { pos++; if (pos >= num_algs) pos = 0; if (algs[pos] == ifsta->auth_alg || algs[pos] == 0xff) continue; if (algs[pos] == WLAN_AUTH_SHARED_KEY && !ieee80211_sta_wep_configured(dev)) continue; ifsta->auth_alg = algs[pos]; printk(KERN_DEBUG "%s: set auth_alg=%d for " "next try\n", dev->name, ifsta->auth_alg); break; } } return; } switch (ifsta->auth_alg) { case WLAN_AUTH_OPEN: case WLAN_AUTH_LEAP: ieee80211_auth_completed(dev, ifsta); break; case WLAN_AUTH_SHARED_KEY: if (ifsta->auth_transaction == 4) ieee80211_auth_completed(dev, ifsta); else ieee80211_auth_challenge(dev, ifsta, mgmt, len); break; } } static void ieee80211_sta_send_associnfo(struct net_device *dev, struct ieee80211_if_sta *ifsta) { IM_HERE_NOW(); char *buf; size_t len; int i; //union iwreq_data wrqu; if (!ifsta->assocreq_ies && !ifsta->assocresp_ies) return; buf = (char*)kmalloc(50 + 2 * (ifsta->assocreq_ies_len + ifsta->assocresp_ies_len), GFP_ATOMIC); if (!buf) return; len = sprintf(buf, "ASSOCINFO("); if (ifsta->assocreq_ies) { len += sprintf(buf + len, "ReqIEs="); for (i = 0; i < ifsta->assocreq_ies_len; i++) { len += sprintf(buf + len, "%02x", ifsta->assocreq_ies[i]); } } if (ifsta->assocresp_ies) { if (ifsta->assocreq_ies) len += sprintf(buf + len, " "); len += sprintf(buf + len, "RespIEs="); for (i = 0; i < ifsta->assocresp_ies_len; i++) { len += sprintf(buf + len, "%02x", ifsta->assocresp_ies[i]); } } len += sprintf(buf + len, ")"); if (len > IW_CUSTOM_MAX) { len = sprintf(buf, "ASSOCRESPIE="); for (i = 0; i < ifsta->assocresp_ies_len; i++) { len += sprintf(buf + len, "%02x", ifsta->assocresp_ies[i]); } } //memset(&wrqu, 0, sizeof(wrqu)); //wrqu.data.length = len; //wireless_send_event(dev, IWEVCUSTOM, &wrqu, buf); kfree(buf); } static void ieee80211_set_associated(struct net_device *dev, struct ieee80211_if_sta *ifsta, int assoc) { IM_HERE_NOW(); //union iwreq_data wrqu; struct ieee80211_sub_if_data *sdata = (struct ieee80211_sub_if_data*)IEEE80211_DEV_TO_SUB_IF(dev); if (ifsta->associated == assoc) return; ifsta->associated = assoc; if (assoc) { struct ieee80211_sub_if_data *sdata; struct ieee80211_sta_bss *bss; sdata = (struct ieee80211_sub_if_data*)IEEE80211_DEV_TO_SUB_IF(dev); if (sdata->type != IEEE80211_IF_TYPE_STA) return; bss = ieee80211_rx_bss_get(dev, ifsta->bssid); if (bss) { if (bss->has_erp_value) ieee80211_handle_erp_ie(dev, bss->erp_value); ieee80211_rx_bss_put(dev, bss); } //netif_carrier_on(dev); ifsta->prev_bssid_set = 1; memcpy(ifsta->prev_bssid, sdata->u.sta.bssid, ETH_ALEN); //memcpy(wrqu.ap_addr.sa_data, sdata->u.sta.bssid, ETH_ALEN); ieee80211_sta_send_associnfo(dev, ifsta); } else { //netif_carrier_off(dev); sdata->use_protection = 0; //memset(wrqu.ap_addr.sa_data, 0, ETH_ALEN); } //wrqu.ap_addr.sa_family = ARPHRD_ETHER; //wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); ifsta->last_probe = jiffies; } #define IEEE80211_PROBE_DELAY (HZ / 33) #define IEEE80211_FC(type, stype) cpu_to_le16(type | stype) static void ieee80211_send_probe_req(struct net_device *dev, u8 *dst, u8 *ssid, size_t ssid_len) { IM_HERE_NOW(); struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); struct ieee80211_hw_mode *mode; struct sk_buff *skb; struct ieee80211_mgmt *mgmt; u8 *pos, *supp_rates, *esupp_rates = NULL; int i; skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*mgmt) + 200); if (!skb) { printk(KERN_DEBUG "%s: failed to allocate buffer for probe " "request\n", dev->name); return; } skb_reserve(skb, local->hw.extra_tx_headroom); mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24); memset(mgmt, 0, 24); mgmt->frame_control = IEEE80211_FC(IEEE80211_FTYPE_MGMT, IEEE80211_STYPE_PROBE_REQ); memcpy(mgmt->sa, dev->dev_addr, ETH_ALEN); if (dst) { memcpy(mgmt->da, dst, ETH_ALEN); memcpy(mgmt->bssid, dst, ETH_ALEN); } else { memset(mgmt->da, 0xff, ETH_ALEN); memset(mgmt->bssid, 0xff, ETH_ALEN); } pos = (u8*)skb_put(skb, 2 + ssid_len); *pos++ = WLAN_EID_SSID; *pos++ = ssid_len; memcpy(pos, ssid, ssid_len); supp_rates = (u8*)skb_put(skb, 2); supp_rates[0] = WLAN_EID_SUPP_RATES; supp_rates[1] = 0; mode = local->oper_hw_mode; for (i = 0; i < mode->num_rates; i++) { struct ieee80211_rate *rate = &mode->rates[i]; if (!(rate->flags & IEEE80211_RATE_SUPPORTED)) continue; if (esupp_rates) { pos = (u8*)skb_put(skb, 1); esupp_rates[1]++; } else if (supp_rates[1] == 8) { esupp_rates = (u8*)skb_put(skb, 3); esupp_rates[0] = WLAN_EID_EXT_SUPP_RATES; esupp_rates[1] = 1; pos = &esupp_rates[2]; } else { pos = (u8*)skb_put(skb, 1); supp_rates[1]++; } if (mode->mode == MODE_ATHEROS_TURBO) *pos = rate->rate / 10; else *pos = rate->rate / 5; } ieee80211_sta_tx(dev, skb, 0); } static void ieee80211_associated(struct net_device *dev, struct ieee80211_if_sta *ifsta) { IM_HERE_NOW(); struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); struct sta_info *sta; int disassoc; /* TODO: start monitoring current AP signal quality and number of * missed beacons. Scan other channels every now and then and search * for better APs. */ /* TODO: remove expired BSSes */ ifsta->state = IEEE80211_ASSOCIATED; sta = sta_info_get(local, ifsta->bssid); if (!sta) { printk(KERN_DEBUG "%s: No STA entry for own AP " MAC_FMT "\n", dev->name, MAC_ARG(ifsta->bssid)); disassoc = 1; } else { disassoc = 0; if (time_after(jiffies, sta->last_rx + IEEE80211_MONITORING_INTERVAL)) { if (ifsta->probereq_poll) { printk(KERN_DEBUG "%s: No ProbeResp from " "current AP " MAC_FMT " - assume out of " "range\n", dev->name, MAC_ARG(ifsta->bssid)); disassoc = 1; sta_info_free(sta, 0); ifsta->probereq_poll = 0; } else { ieee80211_send_probe_req(dev, ifsta->bssid, local->scan_ssid, local->scan_ssid_len); ifsta->probereq_poll = 1; } } else { ifsta->probereq_poll = 0; if (time_after(jiffies, ifsta->last_probe + IEEE80211_PROBE_INTERVAL)) { ifsta->last_probe = jiffies; ieee80211_send_probe_req(dev, ifsta->bssid, ifsta->ssid, ifsta->ssid_len); } } sta_info_put(sta); } if (disassoc) { /*union iwreq_data wrqu; memset(wrqu.ap_addr.sa_data, 0, ETH_ALEN); wrqu.ap_addr.sa_family = ARPHRD_ETHER; wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL);*/ mod_timer(&ifsta->timer, IEEE80211_MONITORING_INTERVAL + 30 * HZ); } else { mod_timer(&ifsta->timer, IEEE80211_MONITORING_INTERVAL); } } static void ieee80211_rx_mgmt_assoc_resp(struct net_device *dev, struct ieee80211_if_sta *ifsta, struct ieee80211_mgmt *mgmt, size_t len, int reassoc) { IM_HERE_NOW(); struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); struct ieee80211_hw_mode *mode; struct sta_info *sta; u32 rates; u16 capab_info, status_code, aid; struct ieee802_11_elems elems; u8 *pos; int i, j; /* AssocResp and ReassocResp have identical structure, so process both * of them in this function. */ if (ifsta->state != IEEE80211_ASSOCIATE) { printk(KERN_DEBUG "%s: association frame received from " MAC_FMT ", but not in associate state - ignored\n", dev->name, MAC_ARG(mgmt->sa)); return; } if (len < 24 + 6) { printk(KERN_DEBUG "%s: too short (%zd) association frame " "received from " MAC_FMT " - ignored\n", dev->name, len, MAC_ARG(mgmt->sa)); return; } if (memcmp(ifsta->bssid, mgmt->sa, ETH_ALEN) != 0) { printk(KERN_DEBUG "%s: association frame received from " "unknown AP (SA=" MAC_FMT " BSSID=" MAC_FMT ") - " "ignored\n", dev->name, MAC_ARG(mgmt->sa), MAC_ARG(mgmt->bssid)); return; } capab_info = le16_to_cpu(mgmt->u.assoc_resp.capab_info); status_code = le16_to_cpu(mgmt->u.assoc_resp.status_code); aid = le16_to_cpu(mgmt->u.assoc_resp.aid); if ((aid & (BIT(15) | BIT(14))) != (BIT(15) | BIT(14))) printk(KERN_DEBUG "%s: invalid aid value %d; bits 15:14 not " "set\n", dev->name, aid); aid &= ~(BIT(15) | BIT(14)); printk(KERN_DEBUG "%s: RX %sssocResp from " MAC_FMT " (capab=0x%x " "status=%d aid=%d)\n", dev->name, reassoc ? "Rea" : "A", MAC_ARG(mgmt->sa), capab_info, status_code, aid); if (status_code != WLAN_STATUS_SUCCESS) { printk(KERN_DEBUG "%s: AP denied association (code=%d)\n", dev->name, status_code); if (status_code == WLAN_STATUS_REASSOC_NO_ASSOC) ifsta->prev_bssid_set = 0; return; } pos = mgmt->u.assoc_resp.variable; if (ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), &elems) == ParseFailed) { printk(KERN_DEBUG "%s: failed to parse AssocResp\n", dev->name); return; } if (!elems.supp_rates) { printk(KERN_DEBUG "%s: no SuppRates element in AssocResp\n", dev->name); return; } /* it probably doesn't, but if the frame includes an ERP value then * update our stored copy */ if (elems.erp_info && elems.erp_info_len >= 1) { struct ieee80211_sta_bss *bss = ieee80211_rx_bss_get(dev, ifsta->bssid); if (bss) { bss->erp_value = elems.erp_info[0]; bss->has_erp_value = 1; ieee80211_rx_bss_put(dev, bss); } } printk(KERN_DEBUG "%s: associated\n", dev->name); ifsta->aid = aid; ifsta->ap_capab = capab_info; kfree(ifsta->assocresp_ies); ifsta->assocresp_ies_len = len - (pos - (u8 *) mgmt); ifsta->assocresp_ies = (u8*)kmalloc(ifsta->assocresp_ies_len, GFP_ATOMIC); if (ifsta->assocresp_ies) memcpy(ifsta->assocresp_ies, pos, ifsta->assocresp_ies_len); ieee80211_set_associated(dev, ifsta, 1); /* Add STA entry for the AP */ sta = sta_info_get(local, ifsta->bssid); if (!sta) { struct ieee80211_sta_bss *bss; sta = sta_info_add(local, dev, ifsta->bssid, GFP_ATOMIC); if (!sta) { printk(KERN_DEBUG "%s: failed to add STA entry for the" " AP\n", dev->name); return; } bss = ieee80211_rx_bss_get(dev, ifsta->bssid); if (bss) { sta->last_rssi = bss->rssi; sta->last_signal = bss->signal; sta->last_noise = bss->noise; ieee80211_rx_bss_put(dev, bss); } } sta->dev = dev; sta->flags |= WLAN_STA_AUTH | WLAN_STA_ASSOC; sta->assoc_ap = 1; rates = 0; mode = local->oper_hw_mode; for (i = 0; i < elems.supp_rates_len; i++) { int rate = (elems.supp_rates[i] & 0x7f) * 5; if (mode->mode == MODE_ATHEROS_TURBO) rate *= 2; for (j = 0; j < mode->num_rates; j++) if (mode->rates[j].rate == rate) rates |= BIT(j); } for (i = 0; i < elems.ext_supp_rates_len; i++) { int rate = (elems.ext_supp_rates[i] & 0x7f) * 5; if (mode->mode == MODE_ATHEROS_TURBO) rate *= 2; for (j = 0; j < mode->num_rates; j++) if (mode->rates[j].rate == rate) rates |= BIT(j); } sta->supp_rates = rates; rate_control_rate_init(sta, local); if (elems.wmm_param && ifsta->wmm_enabled) { sta->flags |= WLAN_STA_WME; ieee80211_sta_wmm_params(dev, ifsta, elems.wmm_param, elems.wmm_param_len); } sta_info_put(sta); ieee80211_associated(dev, ifsta); } ieee80211_txrx_result ieee80211_rx_h_parse_qos(struct ieee80211_txrx_data *rx) { IM_HERE_NOW(); return TXRX_CONTINUE; } static ieee80211_txrx_result ieee80211_rx_h_load_stats(struct ieee80211_txrx_data *rx) { IM_HERE_NOW(); struct ieee80211_local *local = rx->local; struct sk_buff *skb = rx->skb; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb_data(skb); u32 load = 0, hdrtime; struct ieee80211_rate *rate; struct ieee80211_hw_mode *mode = local->hw.conf.mode; int i; /* Estimate total channel use caused by this frame */ if (unlikely(mode->num_rates < 0)) return TXRX_CONTINUE; rate = &mode->rates[0]; for (i = 0; i < mode->num_rates; i++) { if (mode->rates[i].val == rx->u.rx.status->rate) { rate = &mode->rates[i]; break; } } /* 1 bit at 1 Mbit/s takes 1 usec; in channel_use values, * 1 usec = 1/8 * (1080 / 10) = 13.5 */ if (mode->mode == MODE_IEEE80211A || mode->mode == MODE_ATHEROS_TURBO || mode->mode == MODE_ATHEROS_TURBOG || (mode->mode == MODE_IEEE80211G && rate->flags & IEEE80211_RATE_ERP)) hdrtime = CHAN_UTIL_HDR_SHORT; else hdrtime = CHAN_UTIL_HDR_LONG; load = hdrtime; if (!is_multicast_ether_addr(hdr->addr1)) load += hdrtime; load += skb_len(skb) * rate->rate_inv; /* Divide channel_use by 8 to avoid wrapping around the counter */ load >>= CHAN_UTIL_SHIFT; local->channel_use_raw += load; if (rx->sta) rx->sta->channel_use_raw += load; rx->u.rx.load = load; return TXRX_CONTINUE; } /* TODO: implement register/unregister functions for adding TX/RX handlers * into ordered list */ /* rx_pre handlers don't have dev and sdata fields available in * ieee80211_txrx_data */ static ieee80211_rx_handler ieee80211_rx_pre_handlers[] = { ieee80211_rx_h_parse_qos, ieee80211_rx_h_load_stats, NULL }; static ieee80211_txrx_result ieee80211_rx_h_if_stats(struct ieee80211_txrx_data *rx) { IM_HERE_NOW(); rx->sdata->channel_use_raw += rx->u.rx.load; return TXRX_CONTINUE; } static ieee80211_txrx_result ieee80211_rx_h_monitor(struct ieee80211_txrx_data *rx) { IM_HERE_NOW(); return TXRX_CONTINUE; } static ieee80211_txrx_result ieee80211_rx_h_passive_scan(struct ieee80211_txrx_data *rx) { IM_HERE_NOW(); struct ieee80211_local *local = rx->local; struct sk_buff *skb = rx->skb; if (unlikely(local->sta_scanning != 0)) { ieee80211_sta_rx_scan(rx->dev, skb, rx->u.rx.status); return TXRX_QUEUED; } if (unlikely(rx->u.rx.in_scan)) { /* scanning finished during invoking of handlers */ I802_DEBUG_INC(local->rx_handlers_drop_passive_scan); return TXRX_DROP; } return TXRX_CONTINUE; } static void ieee80211_key_threshold_notify(struct net_device *dev, struct ieee80211_key *key, struct sta_info *sta) { IM_HERE_NOW(); struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); struct sk_buff *skb; struct ieee80211_msg_key_notification *msg; /* if no one will get it anyway, don't even allocate it. * unlikely because this is only relevant for APs * where the device must be open... */ if (unlikely(!local->apdev)) return; skb = dev_alloc_skb(sizeof(struct ieee80211_frame_info) + sizeof(struct ieee80211_msg_key_notification)); if (!skb) return; skb_reserve(skb, sizeof(struct ieee80211_frame_info)); msg = (struct ieee80211_msg_key_notification *) skb_put(skb, sizeof(struct ieee80211_msg_key_notification)); msg->tx_rx_count = key->tx_rx_count; memcpy(msg->ifname, dev->name, IFNAMSIZ); if (sta) memcpy(msg->addr, sta->addr, ETH_ALEN); else memset(msg->addr, 0xff, ETH_ALEN); key->tx_rx_count = 0; ieee80211_rx_mgmt(local, skb, NULL, ieee80211_msg_key_threshold_notification); } static ieee80211_txrx_result ieee80211_rx_h_check(struct ieee80211_txrx_data *rx) { IM_HERE_NOW(); struct ieee80211_hdr *hdr; int always_sta_key; hdr = (struct ieee80211_hdr *) skb_data(rx->skb); /* Drop duplicate 802.11 retransmissions (IEEE 802.11 Chap. 9.2.9) */ if (rx->sta && !is_multicast_ether_addr(hdr->addr1)) { if (unlikely(rx->fc & IEEE80211_FCTL_RETRY && rx->sta->last_seq_ctrl[rx->u.rx.queue] == hdr->seq_ctrl)) { if (rx->u.rx.ra_match) { rx->local->dot11FrameDuplicateCou