libertas: fix spinlock recursion bug
[safe/jmp/linux-2.6] / drivers / net / wireless / libertas / if_cs.c
index 888f023..038c66a 100644 (file)
@@ -57,7 +57,7 @@ MODULE_LICENSE("GPL");
 
 struct if_cs_card {
        struct pcmcia_device *p_dev;
-       wlan_private *priv;
+       struct lbs_private *priv;
        void __iomem *iobase;
 };
 
@@ -148,11 +148,11 @@ static int if_cs_poll_while_fw_download(struct if_cs_card *card, uint addr, u8 r
 {
        int i;
 
-       for (i = 0; i < 500; i++) {
+       for (i = 0; i < 1000; i++) {
                u8 val = if_cs_read8(card, addr);
                if (val == reg)
                        return i;
-               udelay(100);
+               udelay(500);
        }
        return -ETIME;
 }
@@ -170,7 +170,8 @@ static int if_cs_poll_while_fw_download(struct if_cs_card *card, uint addr, u8 r
 #define IF_CS_H_IC_TX_OVER             0x0001
 #define IF_CS_H_IC_RX_OVER             0x0002
 #define IF_CS_H_IC_DNLD_OVER           0x0004
-#define IF_CS_H_IC_HOST_EVENT          0x0008
+#define IF_CS_H_IC_POWER_DOWN          0x0008
+#define IF_CS_H_IC_HOST_EVENT          0x0010
 #define IF_CS_H_IC_MASK                        0x001f
 
 #define IF_CS_H_INT_MASK               0x00000004
@@ -242,31 +243,30 @@ static inline void if_cs_disable_ints(struct if_cs_card *card)
 
 static irqreturn_t if_cs_interrupt(int irq, void *data)
 {
-       struct if_cs_card *card = (struct if_cs_card *)data;
+       struct if_cs_card *card = data;
        u16 int_cause;
 
        lbs_deb_enter(LBS_DEB_CS);
 
        int_cause = if_cs_read16(card, IF_CS_C_INT_CAUSE);
-       switch (int_cause) {
-       case 0x0000:
-               /* not for us */
+       if (int_cause == 0x0) {
+               /* Not for us */
                return IRQ_NONE;
-       case 0xffff:
-               /* if one reads junk, then probably the card was removed */
-               card->priv->adapter->surpriseremoved = 1;
-               break;
-       case IF_CS_H_IC_TX_OVER:
-               if (card->priv->adapter->connect_status == LIBERTAS_CONNECTED)
-                       netif_wake_queue(card->priv->dev);
-               /* fallthrought */
-       default:
+
+       } else if (int_cause == 0xffff) {
+               /* Read in junk, the card has probably been removed */
+               card->priv->surpriseremoved = 1;
+               return IRQ_HANDLED;
+       } else {
+               if (int_cause & IF_CS_H_IC_TX_OVER)
+                       lbs_host_to_card_done(card->priv);
+
                /* clear interrupt */
                if_cs_write16(card, IF_CS_C_INT_CAUSE, int_cause & IF_CS_C_IC_MASK);
-               if_cs_disable_ints(card);
        }
-
-       libertas_interrupt(card->priv->dev);
+       spin_lock(&card->priv->driver_lock);
+       lbs_interrupt(card->priv);
+       spin_unlock(&card->priv->driver_lock);
 
        return IRQ_HANDLED;
 }
@@ -281,7 +281,7 @@ static irqreturn_t if_cs_interrupt(int irq, void *data)
 /*
  * Called from if_cs_host_to_card to send a command to the hardware
  */
-static int if_cs_send_cmd(wlan_private *priv, u8 *buf, u16 nb)
+static int if_cs_send_cmd(struct lbs_private *priv, u8 *buf, u16 nb)
 {
        struct if_cs_card *card = (struct if_cs_card *)priv->card;
        int ret = -1;
@@ -326,7 +326,7 @@ done:
 /*
  * Called from if_cs_host_to_card to send a data to the hardware
  */
-static void if_cs_send_data(wlan_private *priv, u8 *buf, u16 nb)
+static void if_cs_send_data(struct lbs_private *priv, u8 *buf, u16 nb)
 {
        struct if_cs_card *card = (struct if_cs_card *)priv->card;
 
@@ -349,7 +349,7 @@ static void if_cs_send_data(wlan_private *priv, u8 *buf, u16 nb)
 /*
  * Get the command result out of the card.
  */
-static int if_cs_receive_cmdres(wlan_private *priv, u8* data, u32 *len)
+static int if_cs_receive_cmdres(struct lbs_private *priv, u8 *data, u32 *len)
 {
        int ret = -1;
        u16 val;
@@ -364,7 +364,7 @@ static int if_cs_receive_cmdres(wlan_private *priv, u8* data, u32 *len)
        }
 
        *len = if_cs_read16(priv->card, IF_CS_C_CMD_LEN);
-       if ((*len == 0) || (*len > MRVDRV_SIZE_OF_CMD_BUFFER)) {
+       if ((*len == 0) || (*len > LBS_CMD_BUFFER_SIZE)) {
                lbs_pr_err("card cmd buffer has invalid # of bytes (%d)\n", *len);
                goto out;
        }
@@ -374,6 +374,9 @@ static int if_cs_receive_cmdres(wlan_private *priv, u8* data, u32 *len)
        if (*len & 1)
                data[*len-1] = if_cs_read8(priv->card, IF_CS_C_CMD);
 
+       /* This is a workaround for a firmware that reports too much
+        * bytes */
+       *len -= 8;
        ret = 0;
 out:
        lbs_deb_leave_args(LBS_DEB_CS, "ret %d, len %d", ret, *len);
@@ -381,7 +384,7 @@ out:
 }
 
 
-static struct sk_buff *if_cs_receive_data(wlan_private *priv)
+static struct sk_buff *if_cs_receive_data(struct lbs_private *priv)
 {
        struct sk_buff *skb = NULL;
        u16 len;
@@ -398,10 +401,12 @@ static struct sk_buff *if_cs_receive_data(wlan_private *priv)
        }
 
        //TODO: skb = dev_alloc_skb(len+ETH_FRAME_LEN+MRVDRV_SNAP_HEADER_LEN+EXTRA_LEN);
-       skb = dev_alloc_skb(MRVDRV_ETH_RX_PACKET_BUFFER_SIZE);
+       skb = dev_alloc_skb(MRVDRV_ETH_RX_PACKET_BUFFER_SIZE + 2);
        if (!skb)
                goto out;
-       data = skb_put(skb, len);
+       skb_put(skb, len);
+       skb_reserve(skb, 2);/* 16 byte align */
+       data = skb->data;
 
        /* read even number of bytes, then odd byte if necessary */
        if_cs_read16_rep(priv->card, IF_CS_H_READ, data, len/sizeof(u16));
@@ -459,7 +464,7 @@ static int if_cs_prog_helper(struct if_cs_card *card)
                ret = -ENODEV;
                goto done;
        }
-       lbs_deb_cs("helper size %d\n", fw->size);
+       lbs_deb_cs("helper size %td\n", fw->size);
 
        /* "Set the 5 bytes of the helper image to 0" */
        /* Not needed, this contains an ARM branch instruction */
@@ -535,7 +540,7 @@ static int if_cs_prog_real(struct if_cs_card *card)
                ret = -ENODEV;
                goto done;
        }
-       lbs_deb_cs("fw size %d\n", fw->size);
+       lbs_deb_cs("fw size %td\n", fw->size);
 
        ret = if_cs_poll_while_fw_download(card, IF_CS_C_SQ_READ_LOW, IF_CS_C_SQ_HELPER_OK);
        if (ret < 0) {
@@ -608,53 +613,11 @@ done:
 /* Callback functions for libertas.ko                               */
 /********************************************************************/
 
-static int if_cs_register_dev(wlan_private *priv)
-{
-       struct if_cs_card *card = (struct if_cs_card *)priv->card;
-
-       lbs_deb_enter(LBS_DEB_CS);
-
-       card->priv = priv;
-
-       return 0;
-}
-
-
-static int if_cs_unregister_dev(wlan_private *priv)
-{
-       lbs_deb_enter(LBS_DEB_CS);
-
-       /*
-        * Nothing special here. Because the device's power gets turned off
-        * anyway, there's no need to send a RESET command like in if_usb.c
-        */
-
-       return 0;
-}
-
-
-/*
- * This callback is a dummy. The reason is that the USB code needs
- * to have various things set up in order to be able to download the
- * firmware. That's not needed in our case.
- *
- * On the contrary, if libertas_add_card() has been called and we're
- * then later called via libertas_activate_card(), but without a valid
- * firmware, then it's quite tedious to tear down the half-installed
- * card. Therefore, we download the firmware before calling adding/
- * activating the card in the first place. If that doesn't work, we
- * won't call into libertas.ko at all.
- */
-
-static int if_cs_prog_firmware(wlan_private *priv)
-{
-       priv->adapter->fw_ready = 1;
-       return 0;
-}
-
-
 /* Send commands or data packets to the card */
-static int if_cs_host_to_card(wlan_private *priv, u8 type, u8 *buf, u16 nb)
+static int if_cs_host_to_card(struct lbs_private *priv,
+       u8 type,
+       u8 *buf,
+       u16 nb)
 {
        int ret = -1;
 
@@ -662,11 +625,12 @@ static int if_cs_host_to_card(wlan_private *priv, u8 type, u8 *buf, u16 nb)
 
        switch (type) {
        case MVMS_DAT:
-               priv->dnld_sent = DNLD_CMD_SENT;
+               priv->dnld_sent = DNLD_DATA_SENT;
                if_cs_send_data(priv, buf, nb);
                ret = 0;
                break;
        case MVMS_CMD:
+               priv->dnld_sent = DNLD_CMD_SENT;
                ret = if_cs_send_cmd(priv, buf, nb);
                break;
        default:
@@ -678,25 +642,22 @@ static int if_cs_host_to_card(wlan_private *priv, u8 type, u8 *buf, u16 nb)
 }
 
 
-static int if_cs_get_int_status(wlan_private *priv, u8 *ireg)
+static int if_cs_get_int_status(struct lbs_private *priv, u8 *ireg)
 {
        struct if_cs_card *card = (struct if_cs_card *)priv->card;
-       //wlan_adapter *adapter = priv->adapter;
        int ret = 0;
        u16 int_cause;
-       u8 *cmdbuf;
        *ireg = 0;
 
        lbs_deb_enter(LBS_DEB_CS);
 
-       if (priv->adapter->surpriseremoved)
+       if (priv->surpriseremoved)
                goto out;
 
        int_cause = if_cs_read16(card, IF_CS_C_INT_CAUSE) & IF_CS_C_IC_MASK;
        if_cs_write16(card, IF_CS_C_INT_CAUSE, int_cause);
 
        *ireg = if_cs_read16(card, IF_CS_C_STATUS) & IF_CS_C_S_MASK;
-       if_cs_enable_ints(card);
 
        if (!*ireg)
                goto sbi_get_int_status_exit;
@@ -706,7 +667,7 @@ sbi_get_int_status_exit:
        /* is there a data packet for us? */
        if (*ireg & IF_CS_C_S_RX_UPLD_RDY) {
                struct sk_buff *skb = if_cs_receive_data(priv);
-               libertas_process_rxed_packet(priv, skb);
+               lbs_process_rxed_packet(priv, skb);
                *ireg &= ~IF_CS_C_S_RX_UPLD_RDY;
        }
 
@@ -716,31 +677,22 @@ sbi_get_int_status_exit:
 
        /* Card has a command result for us */
        if (*ireg & IF_CS_C_S_CMD_UPLD_RDY) {
-               spin_lock(&priv->adapter->driver_lock);
-               if (!priv->adapter->cur_cmd) {
-                       cmdbuf = priv->upld_buf;
-                       priv->adapter->hisregcpy &= ~IF_CS_C_S_RX_UPLD_RDY;
-               } else {
-                       cmdbuf = priv->adapter->cur_cmd->bufvirtualaddr;
-               }
-
-               ret = if_cs_receive_cmdres(priv, cmdbuf, &priv->upld_len);
-               spin_unlock(&priv->adapter->driver_lock);
+               ret = if_cs_receive_cmdres(priv, priv->upld_buf, &priv->upld_len);
                if (ret < 0)
                        lbs_pr_err("could not receive cmd from card\n");
        }
 
 out:
-       lbs_deb_leave_args(LBS_DEB_CS, "ret %d, ireg 0x%x, hisregcpy 0x%x", ret, *ireg, priv->adapter->hisregcpy);
+       lbs_deb_leave_args(LBS_DEB_CS, "ret %d, ireg 0x%x, hisregcpy 0x%x", ret, *ireg, priv->hisregcpy);
        return ret;
 }
 
 
-static int if_cs_read_event_cause(wlan_private *priv)
+static int if_cs_read_event_cause(struct lbs_private *priv)
 {
        lbs_deb_enter(LBS_DEB_CS);
 
-       priv->adapter->eventcause = (if_cs_read16(priv->card, IF_CS_C_STATUS) & IF_CS_C_S_STATUS_MASK) >> 5;
+       priv->eventcause = (if_cs_read16(priv->card, IF_CS_C_STATUS) & IF_CS_C_S_STATUS_MASK) >> 5;
        if_cs_write16(priv->card, IF_CS_H_INT_CAUSE, IF_CS_H_IC_HOST_EVENT);
 
        return 0;
@@ -763,8 +715,8 @@ static void if_cs_release(struct pcmcia_device *p_dev)
 
        lbs_deb_enter(LBS_DEB_CS);
 
-       pcmcia_disable_device(p_dev);
        free_irq(p_dev->irq.AssignedIRQ, card);
+       pcmcia_disable_device(p_dev);
        if (card->iobase)
                ioport_unmap(card->iobase);
 
@@ -784,7 +736,7 @@ static void if_cs_release(struct pcmcia_device *p_dev)
 static int if_cs_probe(struct pcmcia_device *p_dev)
 {
        int ret = -ENOMEM;
-       wlan_private *priv;
+       struct lbs_private *priv;
        struct if_cs_card *card;
        /* CIS parsing */
        tuple_t tuple;
@@ -885,7 +837,6 @@ static int if_cs_probe(struct pcmcia_device *p_dev)
               p_dev->irq.AssignedIRQ, p_dev->io.BasePort1,
               p_dev->io.BasePort1 + p_dev->io.NumPorts1 - 1);
 
-       if_cs_enable_ints(card);
 
        /* Load the firmware early, before calling into libertas.ko */
        ret = if_cs_prog_helper(card);
@@ -895,21 +846,21 @@ static int if_cs_probe(struct pcmcia_device *p_dev)
                goto out2;
 
        /* Make this card known to the libertas driver */
-       priv = libertas_add_card(card, &p_dev->dev);
+       priv = lbs_add_card(card, &p_dev->dev);
        if (!priv) {
                ret = -ENOMEM;
                goto out2;
        }
 
        /* Store pointers to our call-back functions */
+       card->priv = priv;
        priv->card = card;
-       priv->hw_register_dev     = if_cs_register_dev;
-       priv->hw_unregister_dev   = if_cs_unregister_dev;
-       priv->hw_prog_firmware    = if_cs_prog_firmware;
        priv->hw_host_to_card     = if_cs_host_to_card;
        priv->hw_get_int_status   = if_cs_get_int_status;
        priv->hw_read_event_cause = if_cs_read_event_cause;
 
+       priv->fw_ready = 1;
+
        /* Now actually get the IRQ */
        ret = request_irq(p_dev->irq.AssignedIRQ, if_cs_interrupt,
                IRQF_SHARED, DRV_NAME, card);
@@ -918,8 +869,13 @@ static int if_cs_probe(struct pcmcia_device *p_dev)
                goto out3;
        }
 
+       /* Clear any interrupt cause that happend while sending
+        * firmware/initializing card */
+       if_cs_write16(card, IF_CS_C_INT_CAUSE, IF_CS_C_IC_MASK);
+       if_cs_enable_ints(card);
+
        /* And finally bring the card up */
-       if (libertas_activate_card(priv) != 0) {
+       if (lbs_start_card(priv) != 0) {
                lbs_pr_err("could not activate card\n");
                goto out3;
        }
@@ -928,7 +884,7 @@ static int if_cs_probe(struct pcmcia_device *p_dev)
        goto out;
 
 out3:
-       libertas_remove_card(priv);
+       lbs_remove_card(priv);
 out2:
        ioport_unmap(card->iobase);
 out1:
@@ -951,7 +907,9 @@ static void if_cs_detach(struct pcmcia_device *p_dev)
 
        lbs_deb_enter(LBS_DEB_CS);
 
-       libertas_remove_card(card->priv);
+       lbs_stop_card(card->priv);
+       lbs_remove_card(card->priv);
+       if_cs_disable_ints(card);
        if_cs_release(p_dev);
        kfree(card);
 
@@ -971,7 +929,7 @@ static struct pcmcia_device_id if_cs_ids[] = {
 MODULE_DEVICE_TABLE(pcmcia, if_cs_ids);
 
 
-static struct pcmcia_driver libertas_driver = {
+static struct pcmcia_driver lbs_driver = {
        .owner          = THIS_MODULE,
        .drv            = {
                .name   = DRV_NAME,
@@ -987,7 +945,7 @@ static int __init if_cs_init(void)
        int ret;
 
        lbs_deb_enter(LBS_DEB_CS);
-       ret = pcmcia_register_driver(&libertas_driver);
+       ret = pcmcia_register_driver(&lbs_driver);
        lbs_deb_leave(LBS_DEB_CS);
        return ret;
 }
@@ -996,7 +954,7 @@ static int __init if_cs_init(void)
 static void __exit if_cs_exit(void)
 {
        lbs_deb_enter(LBS_DEB_CS);
-       pcmcia_unregister_driver(&libertas_driver);
+       pcmcia_unregister_driver(&lbs_driver);
        lbs_deb_leave(LBS_DEB_CS);
 }