Add USB ID for Thomson SpeedTouch 120g to p54usb id table
[safe/jmp/linux-2.6] / drivers / net / wireless / p54 / p54usb.c
index c2789e5..e3cfc00 100644 (file)
@@ -22,6 +22,7 @@
 #include <net/mac80211.h>
 
 #include "p54.h"
+#include "lmac.h"
 #include "p54usb.h"
 
 MODULE_AUTHOR("Michael Wu <flamingice@sourmilk.net>");
@@ -34,7 +35,9 @@ MODULE_FIRMWARE("isl3887usb");
 static struct usb_device_id p54u_table[] __devinitdata = {
        /* Version 1 devices (pci chip + net2280) */
        {USB_DEVICE(0x0506, 0x0a11)},   /* 3COM 3CRWE254G72 */
+       {USB_DEVICE(0x06b9, 0x0120)},   /* Thomson SpeedTouch 120g */
        {USB_DEVICE(0x0707, 0xee06)},   /* SMC 2862W-G */
+       {USB_DEVICE(0x07aa, 0x001c)},   /* Corega CG-WLUSB2GT */
        {USB_DEVICE(0x083a, 0x4501)},   /* Accton 802.11g WN4501 USB */
        {USB_DEVICE(0x083a, 0x4502)},   /* Siemens Gigaset USB Adapter */
        {USB_DEVICE(0x083a, 0x5501)},   /* Phillips CPWUA054 */
@@ -56,8 +59,10 @@ 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(0x083a, 0xf503)},   /* Accton FD7050E ver 1010ec  */
        {USB_DEVICE(0x0846, 0x4240)},   /* Netgear WG111 (v2) */
        {USB_DEVICE(0x0915, 0x2000)},   /* Cohiba Proto board */
        {USB_DEVICE(0x0915, 0x2002)},   /* Cohiba Proto board */
@@ -65,11 +70,13 @@ static struct usb_device_id p54u_table[] __devinitdata = {
        {USB_DEVICE(0x0bf8, 0x1009)},   /* FUJITSU E-5400 USB D1700*/
        {USB_DEVICE(0x0cde, 0x0006)},   /* Medion MD40900 */
        {USB_DEVICE(0x0cde, 0x0008)},   /* Sagem XG703A */
+       {USB_DEVICE(0x0cde, 0x0015)},   /* Zcomax XG-705A */
        {USB_DEVICE(0x0d8e, 0x3762)},   /* DLink DWL-G120 Cohiba */
        {USB_DEVICE(0x124a, 0x4025)},   /* IOGear GWU513 (GW3887IK chip) */
        {USB_DEVICE(0x1260, 0xee22)},   /* SMC 2862W-G version 2 */
        {USB_DEVICE(0x13b1, 0x000a)},   /* Linksys WUSB54G ver 2 */
        {USB_DEVICE(0x13B1, 0x000C)},   /* Linksys WUSB54AG */
+       {USB_DEVICE(0x1413, 0x5400)},   /* Telsey 802.11g USB2.0 Adapter */
        {USB_DEVICE(0x1435, 0x0427)},   /* Inventel UR054G */
        {USB_DEVICE(0x2001, 0x3704)},   /* DLink DWL-G122 rev A2 */
        {USB_DEVICE(0x413c, 0x8102)},   /* Spinnaker DUT */
@@ -79,6 +86,29 @@ static struct usb_device_id p54u_table[] __devinitdata = {
 
 MODULE_DEVICE_TABLE(usb, p54u_table);
 
+static const struct {
+       u32 intf;
+       enum p54u_hw_type type;
+       const char *fw;
+       const char *fw_legacy;
+       char hw[20];
+} p54u_fwlist[__NUM_P54U_HWTYPES] = {
+       {
+               .type = P54U_NET2280,
+               .intf = FW_LM86,
+               .fw = "isl3886usb",
+               .fw_legacy = "isl3890usb",
+               .hw = "ISL3886 + net2280",
+       },
+       {
+               .type = P54U_3887,
+               .intf = FW_LM87,
+               .fw = "isl3887usb",
+               .fw_legacy = "isl3887usb_bare",
+               .hw = "ISL3887",
+       },
+};
+
 static void p54u_rx_cb(struct urb *urb)
 {
        struct sk_buff *skb = (struct sk_buff *) urb->context;
@@ -123,11 +153,7 @@ static void p54u_rx_cb(struct urb *urb)
                }
                skb_reset_tail_pointer(skb);
                skb_trim(skb, 0);
-               if (urb->transfer_buffer != skb_tail_pointer(skb)) {
-                       /* this should not happen */
-                       WARN_ON(1);
-                       urb->transfer_buffer = skb_tail_pointer(skb);
-               }
+               urb->transfer_buffer = skb_tail_pointer(skb);
        }
        skb_queue_tail(&priv->rx_queue, skb);
        usb_anchor_urb(urb, &priv->submitted);
@@ -138,16 +164,7 @@ static void p54u_rx_cb(struct urb *urb)
        }
 }
 
-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);
-}
-
-static void p54u_tx_free_skb_cb(struct urb *urb)
+static void p54u_tx_cb(struct urb *urb)
 {
        struct sk_buff *skb = urb->context;
        struct ieee80211_hw *dev = (struct ieee80211_hw *)
@@ -213,53 +230,6 @@ static int p54u_init_urbs(struct ieee80211_hw *dev)
        return ret;
 }
 
-static void p54u_tx_3887(struct ieee80211_hw *dev, struct sk_buff *skb,
-                        int free_on_tx)
-{
-       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)
-               return;
-
-       data_urb = usb_alloc_urb(0, GFP_ATOMIC);
-       if (!data_urb) {
-               usb_free_urb(addr_urb);
-               return;
-       }
-
-       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_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);
-
-       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(addr_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);
-
-       if (err)
-               p54_free_skb(dev, skb);
-}
-
 static __le32 p54u_lm87_chksum(const __le32 *data, size_t length)
 {
        u32 chk = 0;
@@ -273,90 +243,80 @@ static __le32 p54u_lm87_chksum(const __le32 *data, size_t length)
        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)
+       if (!data_urb) {
+               p54_free_skb(dev, skb);
                return;
+       }
 
-       checksum = p54u_lm87_chksum((__le32 *)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);
+                         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);
-               skb_pull(skb, sizeof(*hdr));
                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_reg_write *reg;
-       int err = 0;
+       struct urb *int_urb = NULL, *data_urb = NULL;
+       struct net2280_tx_hdr *hdr = (void *)skb->data - sizeof(*hdr);
+       struct net2280_reg_write *reg = NULL;
+       int err = -ENOMEM;
 
        reg = kmalloc(sizeof(*reg), GFP_ATOMIC);
        if (!reg)
-               return;
+               goto out;
 
        int_urb = usb_alloc_urb(0, GFP_ATOMIC);
-       if (!int_urb) {
-               kfree(reg);
-               return;
-       }
+       if (!int_urb)
+               goto out;
 
        data_urb = usb_alloc_urb(0, GFP_ATOMIC);
-       if (!data_urb) {
-               kfree(reg);
-               usb_free_urb(int_urb);
-               return;
-       }
+       if (!data_urb)
+               goto out;
 
        reg->port = cpu_to_le16(NET2280_DEV_U32);
        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_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.
+        * URB_FREE_BUFFER triggers a code path in the USB subsystem that will
+        * free what is inside the transfer_buffer after the last reference to
+        * the int_urb is dropped.
         */
-       int_urb->transfer_flags |= URB_FREE_BUFFER;
+       int_urb->transfer_flags |= URB_FREE_BUFFER | URB_ZERO_PACKET;
+       reg = NULL;
 
        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);
+                         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);
@@ -371,12 +331,12 @@ static void p54u_tx_net2280(struct ieee80211_hw *dev, struct sk_buff *skb,
                usb_unanchor_urb(data_urb);
                goto out;
        }
- out:
+out:
        usb_free_urb(int_urb);
        usb_free_urb(data_urb);
 
        if (err) {
-               skb_pull(skb, sizeof(*hdr));
+               kfree(reg);
                p54_free_skb(dev, skb);
        }
 }
@@ -440,11 +400,56 @@ static int p54u_bulk_msg(struct p54u_priv *priv, unsigned int ep,
                            data, len, &alen, 2000);
 }
 
+static int p54u_device_reset(struct ieee80211_hw *dev)
+{
+       struct p54u_priv *priv = dev->priv;
+       int ret, lock = (priv->intf->condition != USB_INTERFACE_BINDING);
+
+       if (lock) {
+               ret = usb_lock_device_for_reset(priv->udev, priv->intf);
+               if (ret < 0) {
+                       dev_err(&priv->udev->dev, "(p54usb) unable to lock "
+                               "device for reset (%d)!\n", ret);
+                       return ret;
+               }
+       }
+
+       ret = usb_reset_device(priv->udev);
+       if (lock)
+               usb_unlock_device(priv->udev);
+
+       if (ret)
+               dev_err(&priv->udev->dev, "(p54usb) unable to reset "
+                       "device (%d)!\n", ret);
+
+       return ret;
+}
+
+static const char p54u_romboot_3887[] = "~~~~";
+static int p54u_firmware_reset_3887(struct ieee80211_hw *dev)
+{
+       struct p54u_priv *priv = dev->priv;
+       u8 *buf;
+       int ret;
+
+       buf = kmalloc(4, GFP_KERNEL);
+       if (!buf)
+               return -ENOMEM;
+       memcpy(buf, p54u_romboot_3887, 4);
+       ret = p54u_bulk_msg(priv, P54U_PIPE_DATA,
+                           buf, 4);
+       kfree(buf);
+       if (ret)
+               dev_err(&priv->udev->dev, "(p54usb) unable to jump to "
+                       "boot ROM (%d)!\n", ret);
+
+       return ret;
+}
+
+static const char p54u_firmware_upload_3887[] = "<\r";
 static int p54u_upload_firmware_3887(struct ieee80211_hw *dev)
 {
-       static char start_string[] = "~~~~<\r";
        struct p54u_priv *priv = dev->priv;
-       const struct firmware *fw_entry = NULL;
        int err, alen;
        u8 carry = 0;
        u8 *buf, *tmp;
@@ -453,47 +458,29 @@ static int p54u_upload_firmware_3887(struct ieee80211_hw *dev)
        struct x2_header *hdr;
        unsigned long timeout;
 
+       err = p54u_firmware_reset_3887(dev);
+       if (err)
+               return err;
+
        tmp = buf = kmalloc(P54U_FW_BLOCK, GFP_KERNEL);
        if (!buf) {
                dev_err(&priv->udev->dev, "(p54usb) cannot allocate firmware"
                                          "upload buffer!\n");
-               err = -ENOMEM;
-               goto err_bufalloc;
-       }
-
-       memcpy(buf, start_string, 4);
-       err = p54u_bulk_msg(priv, P54U_PIPE_DATA, buf, 4);
-       if (err) {
-               dev_err(&priv->udev->dev, "(p54usb) reset failed! (%d)\n", err);
-               goto err_reset;
-       }
-
-       err = request_firmware(&fw_entry, "isl3887usb", &priv->udev->dev);
-       if (err) {
-               dev_err(&priv->udev->dev, "p54usb: cannot find firmware "
-                                         "(isl3887usb)\n");
-               err = request_firmware(&fw_entry, "isl3887usb_bare",
-                       &priv->udev->dev);
-               if (err)
-                       goto err_req_fw_failed;
+               return -ENOMEM;
        }
 
-       err = p54_parse_firmware(dev, fw_entry);
-       if (err)
-               goto err_upload_failed;
-
-       left = block_size = min((size_t)P54U_FW_BLOCK, fw_entry->size);
-       strcpy(buf, start_string);
-       left -= strlen(start_string);
-       tmp += strlen(start_string);
+       left = block_size = min((size_t)P54U_FW_BLOCK, priv->fw->size);
+       strcpy(buf, p54u_firmware_upload_3887);
+       left -= strlen(p54u_firmware_upload_3887);
+       tmp += strlen(p54u_firmware_upload_3887);
 
-       data = fw_entry->data;
-       remains = fw_entry->size;
+       data = priv->fw->data;
+       remains = priv->fw->size;
 
-       hdr = (struct x2_header *)(buf + strlen(start_string));
+       hdr = (struct x2_header *)(buf + strlen(p54u_firmware_upload_3887));
        memcpy(hdr->signature, X2_SIGNATURE, X2_SIGNATURE_SIZE);
        hdr->fw_load_addr = cpu_to_le32(ISL38XX_DEV_FIRMWARE_ADDR);
-       hdr->fw_length = cpu_to_le32(fw_entry->size);
+       hdr->fw_length = cpu_to_le32(priv->fw->size);
        hdr->crc = cpu_to_le32(~crc32_le(~0, (void *)&hdr->fw_load_addr,
                                         sizeof(u32)*2));
        left -= sizeof(*hdr);
@@ -535,7 +522,8 @@ static int p54u_upload_firmware_3887(struct ieee80211_hw *dev)
                left = block_size = min((unsigned int)P54U_FW_BLOCK, remains);
        }
 
-       *((__le32 *)buf) = cpu_to_le32(~crc32_le(~0, fw_entry->data, fw_entry->size));
+       *((__le32 *)buf) = cpu_to_le32(~crc32_le(~0, priv->fw->data,
+                                                priv->fw->size));
        err = p54u_bulk_msg(priv, P54U_PIPE_DATA, buf, sizeof(u32));
        if (err) {
                dev_err(&priv->udev->dev, "(p54usb) firmware upload failed!\n");
@@ -586,19 +574,14 @@ static int p54u_upload_firmware_3887(struct ieee80211_hw *dev)
        if (err)
                goto err_upload_failed;
 
-  err_upload_failed:
-       release_firmware(fw_entry);
-  err_req_fw_failed:
-  err_reset:
+err_upload_failed:
        kfree(buf);
-  err_bufalloc:
        return err;
 }
 
 static int p54u_upload_firmware_net2280(struct ieee80211_hw *dev)
 {
        struct p54u_priv *priv = dev->priv;
-       const struct firmware *fw_entry = NULL;
        const struct p54p_csr *devreg = (const struct p54p_csr *) P54U_DEV_BASE;
        int err, alen;
        void *buf;
@@ -613,25 +596,6 @@ static int p54u_upload_firmware_net2280(struct ieee80211_hw *dev)
                return -ENOMEM;
        }
 
-       err = request_firmware(&fw_entry, "isl3886usb", &priv->udev->dev);
-       if (err) {
-               dev_err(&priv->udev->dev, "(p54usb) cannot find firmware "
-                                         "(isl3886usb)\n");
-               err = request_firmware(&fw_entry, "isl3890usb",
-                       &priv->udev->dev);
-               if (err) {
-                       kfree(buf);
-                       return err;
-                       }
-       }
-
-       err = p54_parse_firmware(dev, fw_entry);
-       if (err) {
-               kfree(buf);
-               release_firmware(fw_entry);
-               return err;
-       }
-
 #define P54U_WRITE(type, addr, data) \
        do {\
                err = p54u_write(priv, buf, type,\
@@ -731,8 +695,8 @@ static int p54u_upload_firmware_net2280(struct ieee80211_hw *dev)
        P54U_WRITE(NET2280_DEV_U32, &devreg->int_ack, reg);
 
        /* finally, we can upload firmware now! */
-       remains = fw_entry->size;
-       data = fw_entry->data;
+       remains = priv->fw->size;
+       data = priv->fw->data;
        offset = ISL38XX_DEV_FIRMWARE_ADDR;
 
        while (remains) {
@@ -841,12 +805,54 @@ static int p54u_upload_firmware_net2280(struct ieee80211_hw *dev)
 #undef P54U_WRITE
 #undef P54U_READ
 
- fail:
-       release_firmware(fw_entry);
+fail:
        kfree(buf);
        return err;
 }
 
+static int p54u_load_firmware(struct ieee80211_hw *dev)
+{
+       struct p54u_priv *priv = dev->priv;
+       int err, i;
+
+       BUILD_BUG_ON(ARRAY_SIZE(p54u_fwlist) != __NUM_P54U_HWTYPES);
+
+       for (i = 0; i < __NUM_P54U_HWTYPES; i++)
+               if (p54u_fwlist[i].type == priv->hw_type)
+                       break;
+
+       if (i == __NUM_P54U_HWTYPES)
+               return -EOPNOTSUPP;
+
+       err = request_firmware(&priv->fw, p54u_fwlist[i].fw, &priv->udev->dev);
+       if (err) {
+               dev_err(&priv->udev->dev, "(p54usb) cannot load firmware %s "
+                                         "(%d)!\n", p54u_fwlist[i].fw, err);
+
+               err = request_firmware(&priv->fw, p54u_fwlist[i].fw_legacy,
+                                      &priv->udev->dev);
+               if (err)
+                       return err;
+       }
+
+       err = p54_parse_firmware(dev, priv->fw);
+       if (err)
+               goto out;
+
+       if (priv->common.fw_interface != p54u_fwlist[i].intf) {
+               dev_err(&priv->udev->dev, "wrong firmware, please get "
+                       "a firmware for \"%s\" and try again.\n",
+                       p54u_fwlist[i].hw);
+               err = -EINVAL;
+       }
+
+out:
+       if (err)
+               release_firmware(priv->fw);
+
+       return err;
+}
+
 static int p54u_open(struct ieee80211_hw *dev)
 {
        struct p54u_priv *priv = dev->priv;
@@ -888,10 +894,14 @@ static int __devinit p54u_probe(struct usb_interface *intf,
        }
 
        priv = dev->priv;
+       priv->hw_type = P54U_INVALID_HW;
 
        SET_IEEE80211_DEV(dev, &intf->dev);
        usb_set_intfdata(intf, dev);
        priv->udev = udev;
+       priv->intf = intf;
+       skb_queue_head_init(&priv->rx_queue);
+       init_usb_anchor(&priv->submitted);
 
        usb_get_dev(udev);
 
@@ -916,43 +926,49 @@ static int __devinit p54u_probe(struct usb_interface *intf,
        priv->common.open = p54u_open;
        priv->common.stop = p54u_stop;
        if (recognized_pipes < P54U_PIPE_NUMBER) {
+#ifdef CONFIG_PM
+               /* ISL3887 needs a full reset on resume */
+               udev->reset_resume = 1;
+               err = p54u_device_reset(dev);
+#endif
+
                priv->hw_type = P54U_3887;
-               err = p54u_upload_firmware_3887(dev);
-               if (priv->common.fw_interface == FW_LM87) {
-                       dev->extra_tx_headroom += sizeof(struct lm87_tx_hdr);
-                       priv->common.tx_hdr_len = sizeof(struct lm87_tx_hdr);
-                       priv->common.tx = p54u_tx_lm87;
-               } else
-                       priv->common.tx = p54u_tx_3887;
+               dev->extra_tx_headroom += sizeof(struct lm87_tx_hdr);
+               priv->common.tx_hdr_len = sizeof(struct lm87_tx_hdr);
+               priv->common.tx = p54u_tx_lm87;
+               priv->upload_fw = p54u_upload_firmware_3887;
        } else {
                priv->hw_type = P54U_NET2280;
                dev->extra_tx_headroom += sizeof(struct net2280_tx_hdr);
                priv->common.tx_hdr_len = sizeof(struct net2280_tx_hdr);
                priv->common.tx = p54u_tx_net2280;
-               err = p54u_upload_firmware_net2280(dev);
+               priv->upload_fw = p54u_upload_firmware_net2280;
        }
+       err = p54u_load_firmware(dev);
        if (err)
                goto err_free_dev;
 
-       skb_queue_head_init(&priv->rx_queue);
-       init_usb_anchor(&priv->submitted);
+       err = priv->upload_fw(dev);
+       if (err)
+               goto err_free_fw;
 
        p54u_open(dev);
        err = p54_read_eeprom(dev);
        p54u_stop(dev);
        if (err)
-               goto err_free_dev;
+               goto err_free_fw;
 
-       err = ieee80211_register_hw(dev);
-       if (err) {
-               dev_err(&udev->dev, "(p54usb) Cannot register netdevice\n");
-               goto err_free_dev;
-       }
+       err = p54_register_common(dev, &udev->dev);
+       if (err)
+               goto err_free_fw;
 
        return 0;
 
- err_free_dev:
-       ieee80211_free_hw(dev);
+err_free_fw:
+       release_firmware(priv->fw);
+
+err_free_dev:
+       p54_free_common(dev);
        usb_set_intfdata(intf, NULL);
        usb_put_dev(udev);
        return err;
@@ -966,19 +982,80 @@ static void __devexit p54u_disconnect(struct usb_interface *intf)
        if (!dev)
                return;
 
-       ieee80211_unregister_hw(dev);
+       p54_unregister_common(dev);
 
        priv = dev->priv;
        usb_put_dev(interface_to_usbdev(intf));
+       release_firmware(priv->fw);
        p54_free_common(dev);
-       ieee80211_free_hw(dev);
 }
 
+static int p54u_pre_reset(struct usb_interface *intf)
+{
+       struct ieee80211_hw *dev = usb_get_intfdata(intf);
+
+       if (!dev)
+               return -ENODEV;
+
+       p54u_stop(dev);
+       return 0;
+}
+
+static int p54u_resume(struct usb_interface *intf)
+{
+       struct ieee80211_hw *dev = usb_get_intfdata(intf);
+       struct p54u_priv *priv;
+
+       if (!dev)
+               return -ENODEV;
+
+       priv = dev->priv;
+       if (unlikely(!(priv->upload_fw && priv->fw)))
+               return 0;
+
+       return priv->upload_fw(dev);
+}
+
+static int p54u_post_reset(struct usb_interface *intf)
+{
+       struct ieee80211_hw *dev = usb_get_intfdata(intf);
+       struct p54u_priv *priv;
+       int err;
+
+       err = p54u_resume(intf);
+       if (err)
+               return err;
+
+       /* reinitialize old device state */
+       priv = dev->priv;
+       if (priv->common.mode != NL80211_IFTYPE_UNSPECIFIED)
+               ieee80211_restart_hw(dev);
+
+       return 0;
+}
+
+#ifdef CONFIG_PM
+
+static int p54u_suspend(struct usb_interface *intf, pm_message_t message)
+{
+       return p54u_pre_reset(intf);
+}
+
+#endif /* CONFIG_PM */
+
 static struct usb_driver p54u_driver = {
        .name   = "p54usb",
        .id_table = p54u_table,
        .probe = p54u_probe,
        .disconnect = p54u_disconnect,
+       .pre_reset = p54u_pre_reset,
+       .post_reset = p54u_post_reset,
+#ifdef CONFIG_PM
+       .suspend = p54u_suspend,
+       .resume = p54u_resume,
+       .reset_resume = p54u_resume,
+#endif /* CONFIG_PM */
+       .soft_unbind = 1,
 };
 
 static int __init p54u_init(void)