From: Daniel Ritz in 2.4.20+ airo.c is broken and can even kill keventd. this patch fixes it: - sane locking with spinlocks and irqs disabled instead of the buggy locking with semaphores - fix transmit code - safer unload ordering of disable irq, unregister_netdev, kfree - fix possible loop-forever bug - fix a buffer overflow a kernel 2.4 version of the patch is tested by some people with good results. 25-akpm/drivers/net/wireless/airo.c | 533 ++++++++++++++---------------------- 1 files changed, 219 insertions(+), 314 deletions(-) diff -puN drivers/net/wireless/airo.c~airo-fixes drivers/net/wireless/airo.c --- 25/drivers/net/wireless/airo.c~airo-fixes Fri Jul 18 14:08:21 2003 +++ 25-akpm/drivers/net/wireless/airo.c Fri Jul 18 14:08:21 2003 @@ -927,9 +927,8 @@ static int enable_MAC( struct airo_info static void disable_MAC(struct airo_info *ai); static void enable_interrupts(struct airo_info*); static void disable_interrupts(struct airo_info*); -static u16 issuecommand(struct airo_info*, Cmd *pCmd, Resp *pRsp); -static u16 sendcommand(struct airo_info *ai, Cmd *pCmd); -static void completecommand(struct airo_info *ai, Resp *pRsp); +static int issuecommand(struct airo_info*, Cmd *pCmd, Resp *pRsp); +static int issuecommand_nolock(struct airo_info*, Cmd *pCmd, Resp *pRsp); static int bap_setup(struct airo_info*, u16 rid, u16 offset, int whichbap); static int aux_bap_read(struct airo_info*, u16 *pu16Dst, int bytelen, int whichbap); @@ -939,13 +938,14 @@ static int bap_write(struct airo_info*, int whichbap); static int PC4500_accessrid(struct airo_info*, u16 rid, u16 accmd); static int PC4500_readrid(struct airo_info*, u16 rid, void *pBuf, int len); +static int PC4500_readrid_nolock(struct airo_info*, u16 rid, void *pBuf, int len); static int PC4500_writerid(struct airo_info*, u16 rid, const void *pBuf, int len); static int do_writerid( struct airo_info*, u16 rid, const void *rid_data, int len ); static u16 transmit_allocate(struct airo_info*, int lenPayload, int raw); -static int transmit_802_3_packet(struct airo_info*, int len, char *pPacket); -static int transmit_802_11_packet(struct airo_info*, int len, char *pPacket); +static int transmit_802_3_packet(struct airo_info*, u16 txFid, char *pPacket, int len); +static int transmit_802_11_packet(struct airo_info*, u16 txFid, char *pPacket, int len); static irqreturn_t airo_interrupt( int irq, void* dev_id, struct pt_regs *regs); @@ -981,12 +981,11 @@ struct airo_info { struct timer_list timer; struct proc_dir_entry *proc_entry; struct airo_info *next; - spinlock_t aux_lock; + spinlock_t main_lock; unsigned long flags; #define FLAG_PROMISC IFF_PROMISC /* 0x100 - include/linux/if.h */ #define FLAG_RADIO_OFF 0x02 /* User disabling of MAC */ #define FLAG_RADIO_DOWN 0x08 /* ifup/ifdown disabling of MAC */ -#define FLAG_LOCKED 2 /* 0x04 - use as a bit offset */ #define FLAG_FLASHING 0x10 #define FLAG_ADHOC 0x01 /* Needed by MIC */ #define FLAG_MIC_CAPABLE 0x20 @@ -997,14 +996,8 @@ struct airo_info { int whichbap); unsigned short *flash; tdsRssiEntry *rssi; - struct semaphore sem; struct task_struct *task; struct work_struct promisc_task; - struct { - struct sk_buff *skb; - int fid; - struct work_struct task; - } xmit, xmit11; struct net_device *wifidev; #ifdef WIRELESS_EXT struct iw_statistics wstats; // wireless stats @@ -1045,10 +1038,8 @@ static int readBSSListRid(struct airo_in if (first == 1) { memset(&cmd, 0, sizeof(cmd)); cmd.cmd=CMD_LISTBSS; - if (down_interruptible(&ai->sem)) - return -ERESTARTSYS; issuecommand(ai, &cmd, &rsp); - up(&ai->sem); + /* Let the command take effect */ set_current_state (TASK_INTERRUPTIBLE); ai->task = current; @@ -1193,7 +1184,7 @@ static int readStatusRid(struct airo_inf statr->len = le16_to_cpu(statr->len); for(s = &statr->mode; s <= &statr->SSIDlen; s++) *s = le16_to_cpu(*s); - for(s = &statr->beaconPeriod; s <= &statr->_reserved[9]; s++) + for(s = &statr->beaconPeriod; s <= &statr->_reserved1; s++) *s = le16_to_cpu(*s); return rc; @@ -1306,44 +1297,15 @@ static void get_tx_error(struct airo_inf } } -static void airo_do_xmit(struct net_device *dev) { - u16 status; - int i; - struct airo_info *priv = dev->priv; - struct sk_buff *skb = priv->xmit.skb; - int fid = priv->xmit.fid; - u32 *fids = priv->fids; - - if (down_trylock(&priv->sem) != 0) { - netif_stop_queue(dev); - priv->xmit.task.func = (void (*)(void *))airo_do_xmit; - priv->xmit.task.data = (void *)dev; - schedule_work(&priv->xmit.task); - return; - } - status = transmit_802_3_packet (priv, fids[fid], skb->data); - up(&priv->sem); - - i = 0; - if ( status == SUCCESS ) { - dev->trans_start = jiffies; - for (; i < MAX_FIDS / 2 && (priv->fids[i] & 0xffff0000); i++); - } else { - priv->fids[fid] &= 0xffff; - priv->stats.tx_window_errors++; - } - if (i < MAX_FIDS / 2) - netif_wake_queue(dev); - else - netif_stop_queue(dev); - dev_kfree_skb(skb); -} - -static int airo_start_xmit(struct sk_buff *skb, struct net_device *dev) { - s16 len; +static int airo_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + int len; int i; + int ret = 0; + int status; + unsigned long flags; struct airo_info *priv = dev->priv; - u32 *fids = priv->fids; + int *fids = priv->fids; if ( skb == NULL ) { printk( KERN_ERR "airo: skb == NULL!!!\n" ); @@ -1351,61 +1313,50 @@ static int airo_start_xmit(struct sk_buf } /* Find a vacant FID */ - for( i = 0; i < MAX_FIDS / 2 && (fids[i] & 0xffff0000); i++ ); + spin_lock_irqsave(&priv->main_lock, flags); + for (i = 0; i < MAX_FIDS / 2 && (fids[i] & 0xffff0000); i++); - if ( i == MAX_FIDS / 2 ) { - priv->stats.tx_fifo_errors++; - dev_kfree_skb(skb); - } else { - /* check min length*/ - len = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN; - /* Mark fid as used & save length for later */ - fids[i] |= (len << 16); - priv->xmit.skb = skb; - priv->xmit.fid = i; - airo_do_xmit(dev); - } - return 0; -} - -static void airo_do_xmit11(struct net_device *dev) { - u16 status; - int i; - struct airo_info *priv = dev->priv; - struct sk_buff *skb = priv->xmit11.skb; - int fid = priv->xmit11.fid; - u32 *fids = priv->fids; - - if (down_trylock(&priv->sem) != 0) { + if (i + 1 >= MAX_FIDS / 2) { netif_stop_queue(dev); - priv->xmit11.task.func = (void (*)(void *))airo_do_xmit11; - priv->xmit11.task.data = (void *)dev; - schedule_work(&priv->xmit11.task); - return; + + /* we cannot transmit */ + if (i == MAX_FIDS / 2) { + priv->stats.tx_fifo_errors++; + ret = 1; + goto tx_done; + } } - status = transmit_802_11_packet (priv, fids[fid], skb->data); - up(&priv->sem); + + /* check min length*/ + len = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN; + status = transmit_802_3_packet (priv, fids[i], skb->data, len); - i = MAX_FIDS / 2; - if ( status == SUCCESS ) { + if (status == SUCCESS) { + /* Mark fid as used & save length for later */ + fids[i] |= (len << 16); dev->trans_start = jiffies; - for (; i < MAX_FIDS && (priv->fids[i] & 0xffff0000); i++); - } else { - priv->fids[fid] &= 0xffff; + } + else { priv->stats.tx_window_errors++; + ret = 1; } - if (i < MAX_FIDS) - netif_wake_queue(dev); - else - netif_stop_queue(dev); - dev_kfree_skb(skb); + +tx_done: + spin_unlock_irqrestore(&priv->main_lock, flags); + if (!ret) + dev_kfree_skb(skb); + return ret; } -static int airo_start_xmit11(struct sk_buff *skb, struct net_device *dev) { - s16 len; +static int airo_start_xmit11(struct sk_buff *skb, struct net_device *dev) +{ + int len; int i; + int ret = 0; + int status; + unsigned long flags; struct airo_info *priv = dev->priv; - u32 *fids = priv->fids; + int *fids = priv->fids; if ( skb == NULL ) { printk( KERN_ERR "airo: skb == NULL!!!\n" ); @@ -1413,21 +1364,39 @@ static int airo_start_xmit11(struct sk_b } /* Find a vacant FID */ + spin_lock_irqsave(&priv->main_lock, flags); for( i = MAX_FIDS / 2; i < MAX_FIDS && (fids[i] & 0xffff0000); i++ ); - if ( i == MAX_FIDS ) { - priv->stats.tx_fifo_errors++; - dev_kfree_skb(skb); - } else { - /* check min length*/ - len = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN; - /* Mark fid as used & save length for later */ + if (i + 1 >= MAX_FIDS) { + netif_stop_queue(dev); + + /* we cannot transmit */ + if (i == MAX_FIDS) { + priv->stats.tx_fifo_errors++; + ret = 1; + goto tx_done; + } + } + + /* check min length*/ + len = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN; + status = transmit_802_11_packet (priv, fids[i], skb->data, len); + + if (status == SUCCESS) { + /* Mark fid as used & save length for later */ fids[i] |= (len << 16); - priv->xmit11.skb = skb; - priv->xmit11.fid = i; - airo_do_xmit11(dev); + dev->trans_start = jiffies; } - return 0; + else { + priv->stats.tx_window_errors++; + ret = 1; + } + +tx_done: + spin_unlock_irqrestore(&priv->main_lock, flags); + if (!ret) + dev_kfree_skb(skb); + return ret; } struct net_device_stats *airo_get_stats(struct net_device *dev) @@ -1457,36 +1426,19 @@ struct net_device_stats *airo_get_stats( return &local->stats; } -static void airo_end_promisc(struct airo_info *ai) { - Resp rsp; - - if ((IN4500(ai, EVSTAT) & EV_CMD) != 0) { - completecommand(ai, &rsp); - up(&ai->sem); - } else { - ai->promisc_task.func = (void (*)(void *))airo_end_promisc; - ai->promisc_task.data = (void *)ai; - schedule_work(&ai->promisc_task); - } -} - -static void airo_set_promisc(struct airo_info *ai) { +static void airo_set_promisc(struct airo_info *ai) +{ Cmd cmd; + Resp rsp; - if (down_trylock(&ai->sem) == 0) { - memset(&cmd, 0, sizeof(cmd)); - cmd.cmd=CMD_SETMODE; - cmd.parm0=(ai->flags&IFF_PROMISC) ? PROMISC : NOPROMISC; - sendcommand(ai, &cmd); - airo_end_promisc(ai); - } else { - ai->promisc_task.func = (void (*)(void *))airo_set_promisc; - ai->promisc_task.data = (void *)ai; - schedule_work(&ai->promisc_task); - } + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd = CMD_SETMODE; + cmd.parm0 = (ai->flags & IFF_PROMISC) ? PROMISC : NOPROMISC; + issuecommand(ai, &cmd, &rsp); } -static void airo_set_multicast_list(struct net_device *dev) { +static void airo_set_multicast_list(struct net_device *dev) +{ struct airo_info *ai = dev->priv; if ((dev->flags ^ ai->flags) & IFF_PROMISC) { @@ -1551,11 +1503,10 @@ void stop_airo_card( struct net_device * { struct airo_info *ai = dev->priv; flush_scheduled_work(); - if (ai->flash) - kfree(ai->flash); - if (ai->rssi) - kfree(ai->rssi); - takedown_proc_entry( dev, ai ); + + disable_interrupts(ai); + free_irq(dev->irq, dev); + if (ai->registered) { unregister_netdev( dev ); if (ai->wifidev) { @@ -1565,9 +1516,15 @@ void stop_airo_card( struct net_device * } ai->registered = 0; } - disable_interrupts(ai); - free_irq( dev->irq, dev ); - if (auto_wep) del_timer_sync(&ai->timer); + + if (ai->flash) + kfree(ai->flash); + if (ai->rssi) + kfree(ai->rssi); + takedown_proc_entry( dev, ai ); + + if (auto_wep) + del_timer_sync(&ai->timer); if (freeres) { /* PCMCIA frees this stuff, so only for PCI and ISA */ release_region( dev->base_addr, 64 ); @@ -1664,8 +1621,7 @@ struct net_device *init_airo_card( unsig ai->wifidev = 0; ai->registered = 0; ai->dev = dev; - ai->aux_lock = SPIN_LOCK_UNLOCKED; - sema_init(&ai->sem, 1); + ai->main_lock = SPIN_LOCK_UNLOCKED; ai->need_commit = 0; ai->config.len = 0; rc = add_airo_dev( dev ); @@ -1730,7 +1686,6 @@ struct net_device *init_airo_card( unsig ai->fids[i] = transmit_allocate(ai,2312,i>=MAX_FIDS/2); setup_proc_entry( dev, dev->priv ); /* XXX check for failure */ - netif_start_queue(dev); SET_MODULE_OWNER(dev); return dev; @@ -1794,47 +1749,31 @@ int reset_airo_card( struct net_device * EXPORT_SYMBOL(reset_airo_card); #if WIRELESS_EXT > 13 +/* must be called with lock held */ static void airo_send_event(struct net_device *dev) { struct airo_info *ai = dev->priv; union iwreq_data wrqu; StatusRid status_rid; - if (down_trylock(&ai->sem) == 0) { - __set_bit(FLAG_LOCKED, &ai->flags); - PC4500_readrid(ai, RID_STATUS, &status_rid, sizeof(status_rid)); - clear_bit(FLAG_LOCKED, &ai->flags); - up(&ai->sem); - wrqu.data.length = 0; - wrqu.data.flags = 0; - memcpy(wrqu.ap_addr.sa_data, status_rid.bssid[0], ETH_ALEN); - wrqu.ap_addr.sa_family = ARPHRD_ETHER; + PC4500_readrid_nolock(ai, RID_STATUS, &status_rid, sizeof(status_rid)); - /* Send event to user space */ - wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); - } else { - ai->event_task.func = (void (*)(void *))airo_send_event; - ai->event_task.data = (void *)dev; - schedule_work(&ai->event_task); - } + wrqu.data.length = 0; + wrqu.data.flags = 0; + memcpy(wrqu.ap_addr.sa_data, status_rid.bssid[0], ETH_ALEN); + wrqu.ap_addr.sa_family = ARPHRD_ETHER; + + /* Send event to user space */ + wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); } #endif static void airo_read_mic(struct airo_info *ai) { +#ifdef MICSUPPORT MICRid mic_rid; - if (down_trylock(&ai->sem) == 0) { - __set_bit(FLAG_LOCKED, &ai->flags); - PC4500_readrid(ai, RID_MIC, &mic_rid, sizeof(mic_rid)); - clear_bit(FLAG_LOCKED, &ai->flags); - up(&ai->sem); -#ifdef MICSUPPORT - micinit (ai, &mic_rid); + PC4500_readrid(ai, RID_MIC, &mic_rid, sizeof(mic_rid)); + micinit (ai, &mic_rid); #endif - } else { - ai->mic_task.func = (void (*)(void *))airo_read_mic; - ai->mic_task.data = (void *)ai; - schedule_work(&ai->mic_task); - } } static irqreturn_t airo_interrupt ( int irq, void* dev_id, struct pt_regs *regs) { @@ -1848,6 +1787,7 @@ static irqreturn_t airo_interrupt ( int if (!netif_device_present(dev)) return IRQ_NONE; + spin_lock(&apriv->main_lock); for (;;) { status = IN4500( apriv, EVSTAT ); if ( !(status & STATUS_INTS) || status == 0xffff ) break; @@ -1866,7 +1806,8 @@ static irqreturn_t airo_interrupt ( int if ( status & EV_MIC ) { OUT4500( apriv, EVACK, EV_MIC ); - airo_read_mic( apriv ); + if (apriv->flags & FLAG_MIC_CAPABLE) + airo_read_mic( apriv ); } if ( status & EV_LINK ) { #if WIRELESS_EXT > 13 @@ -2115,10 +2056,14 @@ static irqreturn_t airo_interrupt ( int index = i; /* Set up to be used again */ apriv->fids[i] &= 0xffff; + + if (i < MAX_FIDS / 2) + netif_wake_queue(dev); + else + netif_wake_queue(apriv->wifidev); } } if (index != -1) { - netif_wake_queue(dev); if (status & EV_TXEXC) get_tx_error(apriv, index); } @@ -2134,6 +2079,7 @@ static irqreturn_t airo_interrupt ( int if (savedInterrupts) OUT4500( apriv, EVINTEN, savedInterrupts ); + spin_unlock(&apriv->main_lock); /* done.. */ return IRQ_RETVAL(handled); @@ -2169,8 +2115,8 @@ static u16 IN4500( struct airo_info *ai, return rc; } -static int enable_MAC( struct airo_info *ai, Resp *rsp ) { - int rc; +static int enable_MAC( struct airo_info *ai, Resp *rsp ) +{ Cmd cmd; /* FLAG_RADIO_OFF : Radio disabled via /proc or Wireless Extensions @@ -2182,45 +2128,41 @@ static int enable_MAC( struct airo_info if (ai->flags & (FLAG_RADIO_OFF|FLAG_RADIO_DOWN)) return SUCCESS; memset(&cmd, 0, sizeof(cmd)); cmd.cmd = MAC_ENABLE; - if (test_bit(FLAG_LOCKED, &ai->flags) != 0) - return issuecommand(ai, &cmd, rsp); - - if (down_interruptible(&ai->sem)) - return -ERESTARTSYS; - rc = issuecommand(ai, &cmd, rsp); - up(&ai->sem); - return rc; + return issuecommand(ai, &cmd, rsp); } -static void disable_MAC( struct airo_info *ai ) { +static void disable_MAC(struct airo_info *ai) +{ Cmd cmd; Resp rsp; memset(&cmd, 0, sizeof(cmd)); cmd.cmd = MAC_DISABLE; // disable in case already enabled - if (test_bit(FLAG_LOCKED, &ai->flags) != 0) { - issuecommand(ai, &cmd, &rsp); - return; - } - - if (down_interruptible(&ai->sem)) - return; issuecommand(ai, &cmd, &rsp); - up(&ai->sem); } -static void enable_interrupts( struct airo_info *ai ) { +static void enable_interrupts(struct airo_info *ai) +{ + unsigned long flags; + u16 status; + spin_lock_irqsave(&ai->main_lock, flags); + /* Reset the status register */ - u16 status = IN4500( ai, EVSTAT ); + status = IN4500( ai, EVSTAT ); OUT4500( ai, EVACK, status ); + /* Enable the interrupts */ OUT4500( ai, EVINTEN, STATUS_INTS ); - /* Note there is a race condition between the last two lines that - I don't know how to get rid of right now... */ + + spin_unlock_irqrestore(&ai->main_lock, flags); } -static void disable_interrupts( struct airo_info *ai ) { +static void disable_interrupts(struct airo_info *ai) +{ + unsigned long flags; + spin_lock_irqsave(&ai->main_lock, flags); OUT4500( ai, EVINTEN, 0 ); + spin_unlock_irqrestore(&ai->main_lock, flags); } static u16 setup_card(struct airo_info *ai, u8 *mac) @@ -2243,23 +2185,20 @@ static u16 setup_card(struct airo_info * /* The NOP is the first step in getting the card going */ cmd.cmd = NOP; cmd.parm0 = cmd.parm1 = cmd.parm2 = 0; - if (down_interruptible(&ai->sem)) + if (spin_is_locked(&ai->main_lock)) return ERROR; - if ( issuecommand( ai, &cmd, &rsp ) != SUCCESS ) { - up(&ai->sem); + if (issuecommand(ai, &cmd, &rsp) != SUCCESS) return ERROR; - } + memset(&cmd, 0, sizeof(cmd)); cmd.cmd = MAC_DISABLE; // disable in case already enabled - if ( issuecommand( ai, &cmd, &rsp ) != SUCCESS ) { - up(&ai->sem); + if (issuecommand( ai, &cmd, &rsp) != SUCCESS ) return ERROR; - } + // Let's figure out if we need to use the AUX port cmd.cmd = CMD_ENABLEAUX; if (issuecommand(ai, &cmd, &rsp) != SUCCESS) { - up(&ai->sem); printk(KERN_ERR "airo: Error checking for AUX port\n"); return ERROR; } @@ -2270,7 +2209,7 @@ static u16 setup_card(struct airo_info * ai->bap_read = aux_bap_read; printk(KERN_DEBUG "airo: Doing AUX bap_reads\n"); } - up(&ai->sem); + if (ai->config.len == 0) { tdsRssiRid rssi_rid; CapabilityRid cap_rid; @@ -2375,50 +2314,35 @@ static u16 setup_card(struct airo_info * } return SUCCESS; } +static int issuecommand(struct airo_info *ai, Cmd *pCmd, Resp *pRsp) { + int rc; + unsigned long flags; -static u16 issuecommand(struct airo_info *ai, Cmd *pCmd, Resp *pRsp) { - // Im really paranoid about letting it run forever! - int max_tries = 600000; - - if (sendcommand(ai, pCmd) == (u16)ERROR) - return ERROR; - - while (max_tries-- && (IN4500(ai, EVSTAT) & EV_CMD) == 0) { - if (!in_interrupt() && (max_tries & 255) == 0) - schedule(); - } - if ( max_tries == -1 ) { - printk( KERN_ERR - "airo: Max tries exceeded waiting for command\n" ); - return ERROR; - } - completecommand(ai, pRsp); - return SUCCESS; + spin_lock_irqsave(&ai->main_lock, flags); + rc = issuecommand_nolock(ai, pCmd, pRsp); + spin_unlock_irqrestore(&ai->main_lock, flags); + return rc; } -static u16 sendcommand(struct airo_info *ai, Cmd *pCmd) { - // Im really paranoid about letting it run forever! +static int issuecommand_nolock(struct airo_info *ai, Cmd *pCmd, Resp *pRsp) { + // Im really paranoid about letting it run forever! int max_tries = 600000; - u16 cmd; OUT4500(ai, PARAM0, pCmd->parm0); OUT4500(ai, PARAM1, pCmd->parm1); OUT4500(ai, PARAM2, pCmd->parm2); OUT4500(ai, COMMAND, pCmd->cmd); - while ( max_tries-- && (IN4500(ai, EVSTAT) & EV_CMD) == 0 && - (cmd = IN4500(ai, COMMAND)) != 0 ) - if (cmd == pCmd->cmd) - // PC4500 didn't notice command, try again - OUT4500(ai, COMMAND, pCmd->cmd); - if ( max_tries == -1 ) { + while (max_tries-- && (IN4500(ai, EVSTAT) & EV_CMD) == 0) { + if (IN4500(ai, COMMAND) == pCmd->cmd) { + // PC4500 didn't notice command, try again + OUT4500(ai, COMMAND, pCmd->cmd); + } + } + if (max_tries == -1) { printk( KERN_ERR "airo: Max tries exceeded when issueing command\n" ); return ERROR; } - return SUCCESS; -} - -static void completecommand(struct airo_info *ai, Resp *pRsp) { // command completed pRsp->status = IN4500(ai, STATUS); pRsp->rsp0 = IN4500(ai, RESP0); @@ -2431,8 +2355,10 @@ static void completecommand(struct airo_ } // acknowledge processing the status/response OUT4500(ai, EVACK, EV_CMD); + return SUCCESS; } + /* Sets up the bap to start exchange data. whichbap should * be one of the BAP0 or BAP1 defines. Locks should be held before * calling! */ @@ -2497,9 +2423,7 @@ static int aux_bap_read(struct airo_info u16 next; int words; int i; - unsigned long flags; - spin_lock_irqsave(&ai->aux_lock, flags); page = IN4500(ai, SWS0+whichbap); offset = IN4500(ai, SWS2+whichbap); next = aux_setup(ai, page, offset, &len); @@ -2519,7 +2443,6 @@ static int aux_bap_read(struct airo_info next = aux_setup(ai, next, 4, &len); } } - spin_unlock_irqrestore(&ai->aux_lock, flags); return SUCCESS; } @@ -2558,7 +2481,7 @@ static int PC4500_accessrid(struct airo_ memset(&cmd, 0, sizeof(cmd)); cmd.cmd = accmd; cmd.parm0 = rid; - status = issuecommand(ai, &cmd, &rsp); + status = issuecommand_nolock(ai, &cmd, &rsp); if (status != 0) return status; if ( (rsp.status & 0x7F00) != 0) { return (accmd << 8) + (rsp.rsp0 & 0xFF); @@ -2567,25 +2490,16 @@ static int PC4500_accessrid(struct airo_ } /* Note, that we are using BAP1 which is also used by transmit, so - * we must get a lock. */ -static int PC4500_readrid(struct airo_info *ai, u16 rid, void *pBuf, int len) + * it must be called with main_lock held. */ +static int PC4500_readrid_nolock(struct airo_info *ai, u16 rid, void *pBuf, int len) { - u16 status, dolock = 0; - int rc = SUCCESS; + u16 status; + + if ((status = PC4500_accessrid(ai, rid, CMD_ACCESS)) != SUCCESS) + return status; + if (bap_setup(ai, rid, 0, BAP1) != SUCCESS) + return ERROR; - if (test_bit(FLAG_LOCKED, &ai->flags) == 0) { - dolock = 1; - if (down_interruptible(&ai->sem)) - return ERROR; - } - if ( (status = PC4500_accessrid(ai, rid, CMD_ACCESS)) != SUCCESS) { - rc = status; - goto done; - } - if (bap_setup(ai, rid, 0, BAP1) != SUCCESS) { - rc = ERROR; - goto done; - } // read the rid length field bap_read(ai, pBuf, 2, BAP1); // length for remaining part of rid @@ -2596,30 +2510,34 @@ static int PC4500_readrid(struct airo_in "airo: Rid %x has a length of %d which is too short\n", (int)rid, (int)len ); - rc = ERROR; - goto done; + return ERROR; } // read remainder of the rid - rc = bap_read(ai, ((u16*)pBuf)+1, len, BAP1); -done: - if (dolock) - up(&ai->sem); - return rc; + return bap_read(ai, ((u16*)pBuf)+1, len, BAP1); } +static int PC4500_readrid(struct airo_info *ai, u16 rid, void *pBuf, int len) +{ + unsigned long flags; + int rc; + + spin_lock_irqsave(&ai->main_lock, flags); + rc = PC4500_readrid_nolock(ai, rid, pBuf, len); + spin_unlock_irqrestore(&ai->main_lock, flags); + return rc; +} /* Note, that we are using BAP1 which is also used by transmit, so * make sure this isnt called when a transmit is happening */ static int PC4500_writerid(struct airo_info *ai, u16 rid, const void *pBuf, int len) { - u16 status, dolock = 0; + u16 status; + unsigned long flags; int rc = SUCCESS; - if (test_bit(FLAG_LOCKED, &ai->flags) == 0) { - dolock = 1; - if (down_interruptible(&ai->sem)) - return ERROR; - } + *(u16*)pBuf = cpu_to_le16((u16)len); + + spin_lock_irqsave(&ai->main_lock, flags); // --- first access so that we can write the rid data if ( (status = PC4500_accessrid(ai, rid, CMD_ACCESS)) != 0) { rc = status; @@ -2633,9 +2551,8 @@ static int PC4500_writerid(struct airo_i bap_write(ai, pBuf, len, BAP1); // ---now commit the rid data rc = PC4500_accessrid(ai, rid, 0x100|CMD_ACCESS); - done: - if (dolock) - up(&ai->sem); +done: + spin_unlock_irqrestore(&ai->main_lock, flags); return rc; } @@ -2643,6 +2560,8 @@ static int PC4500_writerid(struct airo_i one for now. */ static u16 transmit_allocate(struct airo_info *ai, int lenPayload, int raw) { + unsigned long flags; + unsigned int loop = 3000; Cmd cmd; Resp rsp; u16 txFid; @@ -2650,20 +2569,25 @@ static u16 transmit_allocate(struct airo cmd.cmd = CMD_ALLOCATETX; cmd.parm0 = lenPayload; - if (down_interruptible(&ai->sem)) - return ERROR; - if (issuecommand(ai, &cmd, &rsp) != SUCCESS) { - txFid = 0; + spin_lock_irqsave(&ai->main_lock, flags); + if (issuecommand_nolock(ai, &cmd, &rsp) != SUCCESS) { + txFid = ERROR; goto done; } if ( (rsp.status & 0xFF00) != 0) { - txFid = 0; + txFid = ERROR; goto done; } + /* wait for the allocate event/indication - * It makes me kind of nervous that this can just sit here and spin, - * but in practice it only loops like four times. */ - while ( (IN4500(ai, EVSTAT) & EV_ALLOC) == 0) ; + * in practice it only loops like four times. */ + while (((IN4500(ai, EVSTAT) & EV_ALLOC) == 0) && --loop) + ; /* nada */ + if (!loop) { + txFid = ERROR; + goto done; + } + // get the allocated fid and acknowledge txFid = IN4500(ai, TXALLOCFID); OUT4500(ai, EVACK, EV_ALLOC); @@ -2685,7 +2609,7 @@ static u16 transmit_allocate(struct airo bap_write(ai, &txControl, sizeof(txControl), BAP1); done: - up(&ai->sem); + spin_unlock_irqrestore(&ai->main_lock, flags); return txFid; } @@ -2693,17 +2617,14 @@ done: /* In general BAP1 is dedicated to transmiting packets. However, since we need a BAP when accessing RIDs, we also use BAP1 for that. Make sure the BAP1 spinlock is held when this is called. */ -static int transmit_802_3_packet(struct airo_info *ai, int len, char *pPacket) +static int transmit_802_3_packet(struct airo_info *ai, u16 txFid, char *pPacket, int len) { u16 payloadLen; Cmd cmd; Resp rsp; int miclen = 0; - u16 txFid = len; MICBuffer pMic; - len >>= 16; - if (len < ETH_ALEN * 2) { printk( KERN_WARNING "Short packet %d\n", len ); return ERROR; @@ -2734,12 +2655,12 @@ static int transmit_802_3_packet(struct memset( &cmd, 0, sizeof( cmd ) ); cmd.cmd = CMD_TRANSMIT; cmd.parm0 = txFid; - if (issuecommand(ai, &cmd, &rsp) != SUCCESS) return ERROR; + if (issuecommand_nolock(ai, &cmd, &rsp) != SUCCESS) return ERROR; if ( (rsp.status & 0xFF00) != 0) return ERROR; return SUCCESS; } -static int transmit_802_11_packet(struct airo_info *ai, int len, char *pPacket) +static int transmit_802_11_packet(struct airo_info *ai, u16 txFid, char *pPacket, int len) { u16 fc, payloadLen; Cmd cmd; @@ -2750,8 +2671,6 @@ static int transmit_802_11_packet(struct u16 gaplen; u8 gap[6]; } gap; - u16 txFid = len; - len >>= 16; gap.gaplen = 6; fc = le16_to_cpu(*(const u16*)pPacket); @@ -2793,7 +2712,7 @@ static int transmit_802_11_packet(struct memset( &cmd, 0, sizeof( cmd ) ); cmd.cmd = CMD_TRANSMIT; cmd.parm0 = txFid; - if (issuecommand(ai, &cmd, &rsp) != SUCCESS) return ERROR; + if (issuecommand_nolock(ai, &cmd, &rsp) != SUCCESS) return ERROR; if ( (rsp.status & 0xFF00) != 0) return ERROR; return SUCCESS; } @@ -3857,10 +3776,7 @@ static int proc_BSSList_open( struct ino memset(&cmd, 0, sizeof(cmd)); cmd.cmd=CMD_LISTBSS; - if (down_interruptible(&ai->sem)) - return -ERESTARTSYS; issuecommand(ai, &cmd, &rsp); - up(&ai->sem); data->readlen = 0; return 0; } @@ -3923,13 +3839,6 @@ static void timer_func( u_long data ) { if (!(apriv->flags & FLAG_FLASHING) && (linkstat != 0x400)) { /* We don't have a link so try changing the authtype */ - if (down_trylock(&apriv->sem) != 0) { - apriv->timer.expires = RUN_AT(1); - add_timer(&apriv->timer); - return; - } - __set_bit(FLAG_LOCKED, &apriv->flags); - readConfigRid(apriv); disable_MAC(apriv); switch(apriv->config.authType) { @@ -3955,8 +3864,6 @@ static void timer_func( u_long data ) { apriv->need_commit = 1; writeConfigRid(apriv); enable_MAC(apriv, &rsp); - clear_bit(FLAG_LOCKED, &apriv->flags); - up(&apriv->sem); /* Schedule check to see if the change worked */ apriv->timer.expires = RUN_AT(HZ*3); @@ -4135,7 +4042,11 @@ static int airo_get_freq(struct net_devi struct airo_info *local = dev->priv; StatusRid status_rid; /* Card status info */ - readStatusRid(local, &status_rid); + if (local->config.opmode & MODE_STA_ESS) + status_rid.channel = local->config.channelSet; + else + readStatusRid(local, &status_rid); + /* Will return zero in infrastructure mode */ #ifdef WEXT_USECHANNELS @@ -4246,11 +4157,8 @@ static int airo_set_wap(struct net_devic return -EINVAL; else if (!memcmp(bcast, awrq->sa_data, ETH_ALEN)) { memset(&cmd, 0, sizeof(cmd)); - cmd.cmd=CMD_LOSE_SYNC; - if (down_interruptible(&local->sem)) - return -ERESTARTSYS; + cmd.cmd = CMD_LOSE_SYNC; issuecommand(local, &cmd, &rsp); - up(&local->sem); } else { memset(&APList_rid, 0, sizeof(APList_rid)); APList_rid.len = sizeof(APList_rid); @@ -5132,11 +5040,8 @@ static int airo_set_scan(struct net_devi /* Initiate a scan command */ memset(&cmd, 0, sizeof(cmd)); cmd.cmd=CMD_LISTBSS; - if (down_interruptible(&ai->sem)) - return -ERESTARTSYS; issuecommand(ai, &cmd, &rsp); ai->scan_timestamp = jiffies; - up(&ai->sem); /* At this point, just return to the user. */ _