]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/net/wireless/p54/p54usb.c
p54usb: fix packet loss with first generation devices
[linux-2.6-omap-h63xx.git] / drivers / net / wireless / p54 / p54usb.c
index e9630b949256a890fa130948eb5b6edecda8e289..5de2ebfb28c7ef9b722c3a4028a2c6e5c7c25497 100644 (file)
@@ -56,6 +56,7 @@ static struct usb_device_id p54u_table[] __devinitdata = {
        {USB_DEVICE(0x050d, 0x7050)},   /* Belkin F5D7050 ver 1000 */
        {USB_DEVICE(0x0572, 0x2000)},   /* Cohiba Proto board */
        {USB_DEVICE(0x0572, 0x2002)},   /* Cohiba Proto board */
+       {USB_DEVICE(0x06b9, 0x0121)},   /* Thomson SpeedTouch 121g */
        {USB_DEVICE(0x0707, 0xee13)},   /* SMC 2862W-G version 2 */
        {USB_DEVICE(0x083a, 0x4521)},   /* Siemens Gigaset USB Adapter 54 version 2 */
        {USB_DEVICE(0x0846, 0x4240)},   /* Netgear WG111 (v2) */
@@ -86,13 +87,13 @@ static void p54u_rx_cb(struct urb *urb)
        struct ieee80211_hw *dev = info->dev;
        struct p54u_priv *priv = dev->priv;
 
+       skb_unlink(skb, &priv->rx_queue);
+
        if (unlikely(urb->status)) {
-               info->urb = NULL;
-               usb_free_urb(urb);
+               dev_kfree_skb_irq(skb);
                return;
        }
 
-       skb_unlink(skb, &priv->rx_queue);
        skb_put(skb, urb->actual_length);
 
        if (priv->hw_type == P54U_NET2280)
@@ -105,7 +106,6 @@ static void p54u_rx_cb(struct urb *urb)
        if (p54_rx(dev, skb)) {
                skb = dev_alloc_skb(priv->common.rx_mtu + 32);
                if (unlikely(!skb)) {
-                       usb_free_urb(urb);
                        /* TODO check rx queue length and refill *somewhere* */
                        return;
                }
@@ -115,7 +115,6 @@ static void p54u_rx_cb(struct urb *urb)
                info->dev = dev;
                urb->transfer_buffer = skb_tail_pointer(skb);
                urb->context = skb;
-               skb_queue_tail(&priv->rx_queue, skb);
        } else {
                if (priv->hw_type == P54U_NET2280)
                        skb_push(skb, priv->common.tx_hdr_len);
@@ -130,60 +129,53 @@ static void p54u_rx_cb(struct urb *urb)
                        WARN_ON(1);
                        urb->transfer_buffer = skb_tail_pointer(skb);
                }
-
-               skb_queue_tail(&priv->rx_queue, skb);
        }
-
-       usb_submit_urb(urb, GFP_ATOMIC);
-}
-
-static void p54u_tx_reuse_skb_cb(struct urb *urb)
-{
-       struct sk_buff *skb = urb->context;
-       struct p54u_priv *priv = (struct p54u_priv *)((struct ieee80211_hw *)
-               usb_get_intfdata(usb_ifnum_to_if(urb->dev, 0)))->priv;
-
-       skb_pull(skb, priv->common.tx_hdr_len);
-       usb_free_urb(urb);
+       skb_queue_tail(&priv->rx_queue, skb);
+       usb_anchor_urb(urb, &priv->submitted);
+       if (usb_submit_urb(urb, GFP_ATOMIC)) {
+               skb_unlink(skb, &priv->rx_queue);
+               usb_unanchor_urb(urb);
+               dev_kfree_skb_irq(skb);
+       }
 }
 
 static void p54u_tx_cb(struct urb *urb)
-{
-       usb_free_urb(urb);
-}
-
-static void p54u_tx_free_cb(struct urb *urb)
-{
-       kfree(urb->transfer_buffer);
-       usb_free_urb(urb);
-}
-
-static void p54u_tx_free_skb_cb(struct urb *urb)
 {
        struct sk_buff *skb = urb->context;
        struct ieee80211_hw *dev = (struct ieee80211_hw *)
                usb_get_intfdata(usb_ifnum_to_if(urb->dev, 0));
 
        p54_free_skb(dev, skb);
-       usb_free_urb(urb);
+}
+
+static void p54u_tx_dummy_cb(struct urb *urb) { }
+
+static void p54u_free_urbs(struct ieee80211_hw *dev)
+{
+       struct p54u_priv *priv = dev->priv;
+       usb_kill_anchored_urbs(&priv->submitted);
 }
 
 static int p54u_init_urbs(struct ieee80211_hw *dev)
 {
        struct p54u_priv *priv = dev->priv;
-       struct urb *entry;
+       struct urb *entry = NULL;
        struct sk_buff *skb;
        struct p54u_rx_info *info;
+       int ret = 0;
 
        while (skb_queue_len(&priv->rx_queue) < 32) {
                skb = __dev_alloc_skb(priv->common.rx_mtu + 32, GFP_KERNEL);
-               if (!skb)
-                       break;
+               if (!skb) {
+                       ret = -ENOMEM;
+                       goto err;
+               }
                entry = usb_alloc_urb(0, GFP_KERNEL);
                if (!entry) {
-                       kfree_skb(skb);
-                       break;
+                       ret = -ENOMEM;
+                       goto err;
                }
+
                usb_fill_bulk_urb(entry, priv->udev,
                                  usb_rcvbulkpipe(priv->udev, P54U_PIPE_DATA),
                                  skb_tail_pointer(skb),
@@ -192,33 +184,32 @@ static int p54u_init_urbs(struct ieee80211_hw *dev)
                info->urb = entry;
                info->dev = dev;
                skb_queue_tail(&priv->rx_queue, skb);
-               usb_submit_urb(entry, GFP_KERNEL);
+
+               usb_anchor_urb(entry, &priv->submitted);
+               ret = usb_submit_urb(entry, GFP_KERNEL);
+               if (ret) {
+                       skb_unlink(skb, &priv->rx_queue);
+                       usb_unanchor_urb(entry);
+                       goto err;
+               }
+               usb_free_urb(entry);
+               entry = NULL;
        }
 
        return 0;
-}
 
-static void p54u_free_urbs(struct ieee80211_hw *dev)
-{
-       struct p54u_priv *priv = dev->priv;
-       struct p54u_rx_info *info;
-       struct sk_buff *skb;
-
-       while ((skb = skb_dequeue(&priv->rx_queue))) {
-               info = (struct p54u_rx_info *) skb->cb;
-               if (!info->urb)
-                       continue;
-
-               usb_kill_urb(info->urb);
-               kfree_skb(skb);
-       }
+ err:
+       usb_free_urb(entry);
+       kfree_skb(skb);
+       p54u_free_urbs(dev);
+       return ret;
 }
 
-static void p54u_tx_3887(struct ieee80211_hw *dev, struct sk_buff *skb,
-                        int free_on_tx)
+static void p54u_tx_3887(struct ieee80211_hw *dev, struct sk_buff *skb)
 {
        struct p54u_priv *priv = dev->priv;
        struct urb *addr_urb, *data_urb;
+       int err = 0;
 
        addr_urb = usb_alloc_urb(0, GFP_ATOMIC);
        if (!addr_urb)
@@ -233,64 +224,81 @@ static void p54u_tx_3887(struct ieee80211_hw *dev, struct sk_buff *skb,
        usb_fill_bulk_urb(addr_urb, priv->udev,
                          usb_sndbulkpipe(priv->udev, P54U_PIPE_DATA),
                          &((struct p54_hdr *)skb->data)->req_id, 4,
-                         p54u_tx_cb, dev);
+                         p54u_tx_dummy_cb, dev);
        usb_fill_bulk_urb(data_urb, priv->udev,
                          usb_sndbulkpipe(priv->udev, P54U_PIPE_DATA),
-                         skb->data, skb->len,
-                         free_on_tx ? p54u_tx_free_skb_cb :
-                                      p54u_tx_reuse_skb_cb, skb);
+                         skb->data, skb->len, FREE_AFTER_TX(skb) ?
+                         p54u_tx_cb : p54u_tx_dummy_cb, skb);
+       addr_urb->transfer_flags |= URB_ZERO_PACKET;
+       data_urb->transfer_flags |= URB_ZERO_PACKET;
+
+       usb_anchor_urb(addr_urb, &priv->submitted);
+       err = usb_submit_urb(addr_urb, GFP_ATOMIC);
+       if (err) {
+               usb_unanchor_urb(addr_urb);
+               goto out;
+       }
+
+       usb_anchor_urb(data_urb, &priv->submitted);
+       err = usb_submit_urb(data_urb, GFP_ATOMIC);
+       if (err)
+               usb_unanchor_urb(data_urb);
+
+ out:
+       usb_free_urb(addr_urb);
+       usb_free_urb(data_urb);
 
-       usb_submit_urb(addr_urb, GFP_ATOMIC);
-       usb_submit_urb(data_urb, GFP_ATOMIC);
+       if (err)
+               p54_free_skb(dev, skb);
 }
 
-static __le32 p54u_lm87_chksum(const u32 *data, size_t length)
+static __le32 p54u_lm87_chksum(const __le32 *data, size_t length)
 {
        u32 chk = 0;
 
        length >>= 2;
        while (length--) {
-               chk ^= *data++;
+               chk ^= le32_to_cpu(*data++);
                chk = (chk >> 5) ^ (chk << 3);
        }
 
        return cpu_to_le32(chk);
 }
 
-static void p54u_tx_lm87(struct ieee80211_hw *dev, struct sk_buff *skb,
-                        int free_on_tx)
+static void p54u_tx_lm87(struct ieee80211_hw *dev, struct sk_buff *skb)
 {
        struct p54u_priv *priv = dev->priv;
        struct urb *data_urb;
-       struct lm87_tx_hdr *hdr;
-       __le32 checksum;
-       __le32 addr = ((struct p54_hdr *)skb->data)->req_id;
+       struct lm87_tx_hdr *hdr = (void *)skb->data - sizeof(*hdr);
 
        data_urb = usb_alloc_urb(0, GFP_ATOMIC);
        if (!data_urb)
                return;
 
-       checksum = p54u_lm87_chksum((u32 *)skb->data, skb->len);
-       hdr = (struct lm87_tx_hdr *)skb_push(skb, sizeof(*hdr));
-       hdr->chksum = checksum;
-       hdr->device_addr = addr;
+       hdr->chksum = p54u_lm87_chksum((__le32 *)skb->data, skb->len);
+       hdr->device_addr = ((struct p54_hdr *)skb->data)->req_id;
 
        usb_fill_bulk_urb(data_urb, priv->udev,
                          usb_sndbulkpipe(priv->udev, P54U_PIPE_DATA),
-                         skb->data, skb->len,
-                         free_on_tx ? p54u_tx_free_skb_cb :
-                                      p54u_tx_reuse_skb_cb, skb);
-
-       usb_submit_urb(data_urb, GFP_ATOMIC);
+                         hdr, skb->len + sizeof(*hdr),  FREE_AFTER_TX(skb) ?
+                         p54u_tx_cb : p54u_tx_dummy_cb, skb);
+       data_urb->transfer_flags |= URB_ZERO_PACKET;
+
+       usb_anchor_urb(data_urb, &priv->submitted);
+       if (usb_submit_urb(data_urb, GFP_ATOMIC)) {
+               usb_unanchor_urb(data_urb);
+               p54_free_skb(dev, skb);
+       }
+       usb_free_urb(data_urb);
 }
 
-static void p54u_tx_net2280(struct ieee80211_hw *dev, struct sk_buff *skb,
-                           int free_on_tx)
+static void p54u_tx_net2280(struct ieee80211_hw *dev, struct sk_buff *skb)
 {
        struct p54u_priv *priv = dev->priv;
        struct urb *int_urb, *data_urb;
-       struct net2280_tx_hdr *hdr;
+       struct net2280_tx_hdr *hdr = (void *)skb->data - sizeof(*hdr);
        struct net2280_reg_write *reg;
+       int err = 0;
 
        reg = kmalloc(sizeof(*reg), GFP_ATOMIC);
        if (!reg)
@@ -313,22 +321,48 @@ static void p54u_tx_net2280(struct ieee80211_hw *dev, struct sk_buff *skb,
        reg->addr = cpu_to_le32(P54U_DEV_BASE);
        reg->val = cpu_to_le32(ISL38XX_DEV_INT_DATA);
 
-       hdr = (void *)skb_push(skb, sizeof(*hdr));
        memset(hdr, 0, sizeof(*hdr));
-       hdr->device_addr = ((struct p54_hdr *)skb->data)->req_id;
-       hdr->len = cpu_to_le16(skb->len + sizeof(struct p54_hdr));
+       hdr->len = cpu_to_le16(skb->len);
+       hdr->device_addr = ((struct p54_hdr *) skb->data)->req_id;
 
        usb_fill_bulk_urb(int_urb, priv->udev,
                usb_sndbulkpipe(priv->udev, P54U_PIPE_DEV), reg, sizeof(*reg),
-               p54u_tx_free_cb, dev);
-       usb_submit_urb(int_urb, GFP_ATOMIC);
+               p54u_tx_dummy_cb, dev);
+
+       /*
+        * This flag triggers a code path in the USB subsystem that will
+        * free what's inside the transfer_buffer after the callback routine
+        * has completed.
+        */
+       int_urb->transfer_flags |= URB_FREE_BUFFER | URB_ZERO_PACKET;
 
        usb_fill_bulk_urb(data_urb, priv->udev,
                          usb_sndbulkpipe(priv->udev, P54U_PIPE_DATA),
-                         skb->data, skb->len,
-                         free_on_tx ? p54u_tx_free_skb_cb :
-                                      p54u_tx_reuse_skb_cb, skb);
-       usb_submit_urb(data_urb, GFP_ATOMIC);
+                         hdr, skb->len + sizeof(*hdr), FREE_AFTER_TX(skb) ?
+                         p54u_tx_cb : p54u_tx_dummy_cb, skb);
+       data_urb->transfer_flags |= URB_ZERO_PACKET;
+
+       usb_anchor_urb(int_urb, &priv->submitted);
+       err = usb_submit_urb(int_urb, GFP_ATOMIC);
+       if (err) {
+               usb_unanchor_urb(int_urb);
+               goto out;
+       }
+
+       usb_anchor_urb(data_urb, &priv->submitted);
+       err = usb_submit_urb(data_urb, GFP_ATOMIC);
+       if (err) {
+               usb_unanchor_urb(data_urb);
+               goto out;
+       }
+ out:
+       usb_free_urb(int_urb);
+       usb_free_urb(data_urb);
+
+       if (err) {
+               skb_pull(skb, sizeof(*hdr));
+               p54_free_skb(dev, skb);
+       }
 }
 
 static int p54u_write(struct p54u_priv *priv,
@@ -885,6 +919,7 @@ static int __devinit p54u_probe(struct usb_interface *intf,
                goto err_free_dev;
 
        skb_queue_head_init(&priv->rx_queue);
+       init_usb_anchor(&priv->submitted);
 
        p54u_open(dev);
        err = p54_read_eeprom(dev);