X-Git-Url: http://ftp.safe.ca/?a=blobdiff_plain;f=drivers%2Fnet%2Fwireless%2Fp54%2Fp54usb.c;h=e3cfc001d2fda275a2caf19a0851f2c37170d440;hb=05a9a1617026977422c7c5ed3aeac6f46fa2132c;hp=75d749bccb0d13e17104b67e348c3723bbb1032b;hpb=ec366eba3b8d96fdb2e629c64fa2750be2723ba7;p=safe%2Fjmp%2Flinux-2.6 diff --git a/drivers/net/wireless/p54/p54usb.c b/drivers/net/wireless/p54/p54usb.c index 75d749b..e3cfc00 100644 --- a/drivers/net/wireless/p54/p54usb.c +++ b/drivers/net/wireless/p54/p54usb.c @@ -22,17 +22,22 @@ #include #include "p54.h" +#include "lmac.h" #include "p54usb.h" MODULE_AUTHOR("Michael Wu "); MODULE_DESCRIPTION("Prism54 USB wireless driver"); MODULE_LICENSE("GPL"); MODULE_ALIAS("prism54usb"); +MODULE_FIRMWARE("isl3886usb"); +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 */ @@ -54,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 */ @@ -63,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 */ @@ -77,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; @@ -84,13 +116,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) @@ -103,7 +135,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; } @@ -113,7 +144,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); @@ -123,45 +153,54 @@ 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); - } - - skb_queue_tail(&priv->rx_queue, skb); + urb->transfer_buffer = skb_tail_pointer(skb); + } + 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); } - - usb_submit_urb(urb, GFP_ATOMIC); } static void p54u_tx_cb(struct urb *urb) { - usb_free_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); } -static void p54u_tx_free_cb(struct urb *urb) +static void p54u_tx_dummy_cb(struct urb *urb) { } + +static void p54u_free_urbs(struct ieee80211_hw *dev) { - kfree(urb->transfer_buffer); - usb_free_urb(urb); + 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), @@ -170,135 +209,136 @@ 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); - } - - 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); - } -} - -static void p54u_tx_3887(struct ieee80211_hw *dev, struct p54_control_hdr *data, - size_t len, int free_on_tx) -{ - struct p54u_priv *priv = dev->priv; - struct urb *addr_urb, *data_urb; - - 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_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; } - usb_fill_bulk_urb(addr_urb, priv->udev, - usb_sndbulkpipe(priv->udev, P54U_PIPE_DATA), &data->req_id, - sizeof(data->req_id), p54u_tx_cb, dev); - usb_fill_bulk_urb(data_urb, priv->udev, - usb_sndbulkpipe(priv->udev, P54U_PIPE_DATA), data, len, - free_on_tx ? p54u_tx_free_cb : p54u_tx_cb, dev); + return 0; - usb_submit_urb(addr_urb, GFP_ATOMIC); - usb_submit_urb(data_urb, GFP_ATOMIC); + err: + usb_free_urb(entry); + kfree_skb(skb); + p54u_free_urbs(dev); + return ret; } -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 p54_control_hdr *data, - size_t len, 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 = (void *)data - sizeof(*hdr); + 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; + } - hdr->chksum = p54u_lm87_chksum((u32 *)data, len); - hdr->device_addr = data->req_id; + 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), hdr, - len + sizeof(*hdr), free_on_tx ? p54u_tx_free_cb : p54u_tx_cb, - dev); - - usb_submit_urb(data_urb, GFP_ATOMIC); + usb_sndbulkpipe(priv->udev, P54U_PIPE_DATA), + 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 p54_control_hdr *data, - size_t len, 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; + 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); - len += sizeof(*data); - hdr = (void *)data - sizeof(*hdr); memset(hdr, 0, sizeof(*hdr)); - hdr->device_addr = data->req_id; - hdr->len = cpu_to_le16(len); + 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); + + /* + * 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 | URB_ZERO_PACKET; + reg = NULL; usb_fill_bulk_urb(data_urb, priv->udev, - usb_sndbulkpipe(priv->udev, P54U_PIPE_DATA), hdr, len + sizeof(*hdr), - free_on_tx ? p54u_tx_free_cb : p54u_tx_cb, dev); - usb_submit_urb(data_urb, GFP_ATOMIC); + usb_sndbulkpipe(priv->udev, P54U_PIPE_DATA), + 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) { + kfree(reg); + p54_free_skb(dev, skb); + } } static int p54u_write(struct p54u_priv *priv, @@ -360,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; @@ -373,42 +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) { - printk(KERN_ERR "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) { - printk(KERN_ERR "p54usb: reset failed! (%d)\n", err); - goto err_reset; - } - - err = request_firmware(&fw_entry, "isl3887usb_bare", &priv->udev->dev); - if (err) { - printk(KERN_ERR "p54usb: cannot find firmware (isl3887usb_bare)!\n"); - goto err_req_fw_failed; + dev_err(&priv->udev->dev, "(p54usb) cannot allocate firmware" + "upload buffer!\n"); + 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); @@ -441,7 +513,8 @@ static int p54u_upload_firmware_3887(struct ieee80211_hw *dev) err = p54u_bulk_msg(priv, P54U_PIPE_DATA, buf, block_size); if (err) { - printk(KERN_ERR "p54usb: firmware upload failed!\n"); + dev_err(&priv->udev->dev, "(p54usb) firmware " + "upload failed!\n"); goto err_upload_failed; } @@ -449,13 +522,13 @@ 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) { - printk(KERN_ERR "p54usb: firmware upload failed!\n"); + dev_err(&priv->udev->dev, "(p54usb) firmware upload failed!\n"); goto err_upload_failed; } - timeout = jiffies + msecs_to_jiffies(1000); while (!(err = usb_bulk_msg(priv->udev, usb_rcvbulkpipe(priv->udev, P54U_PIPE_DATA), buf, 128, &alen, 1000))) { @@ -463,25 +536,27 @@ static int p54u_upload_firmware_3887(struct ieee80211_hw *dev) break; if (alen > 5 && !memcmp(buf, "ERROR", 5)) { - printk(KERN_INFO "p54usb: firmware upload failed!\n"); err = -EINVAL; break; } if (time_after(jiffies, timeout)) { - printk(KERN_ERR "p54usb: firmware boot timed out!\n"); + dev_err(&priv->udev->dev, "(p54usb) firmware boot " + "timed out!\n"); err = -ETIMEDOUT; break; } } - if (err) + if (err) { + dev_err(&priv->udev->dev, "(p54usb) firmware upload failed!\n"); goto err_upload_failed; + } buf[0] = 'g'; buf[1] = '\r'; err = p54u_bulk_msg(priv, P54U_PIPE_DATA, buf, 2); if (err) { - printk(KERN_ERR "p54usb: firmware boot failed!\n"); + dev_err(&priv->udev->dev, "(p54usb) firmware boot failed!\n"); goto err_upload_failed; } @@ -499,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; @@ -521,24 +591,11 @@ static int p54u_upload_firmware_net2280(struct ieee80211_hw *dev) buf = kmalloc(512, GFP_KERNEL); if (!buf) { - printk(KERN_ERR "p54usb: firmware buffer alloc failed!\n"); + dev_err(&priv->udev->dev, "(p54usb) firmware buffer " + "alloc failed!\n"); return -ENOMEM; } - err = request_firmware(&fw_entry, "isl3890usb", &priv->udev->dev); - if (err) { - printk(KERN_ERR "p54usb: cannot find firmware (isl3890usb)!\n"); - 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,\ @@ -638,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) { @@ -648,8 +705,8 @@ static int p54u_upload_firmware_net2280(struct ieee80211_hw *dev) err = p54u_bulk_msg(priv, P54U_PIPE_DATA, buf, block_len); if (err) { - printk(KERN_ERR "p54usb: firmware block upload " - "failed\n"); + dev_err(&priv->udev->dev, "(p54usb) firmware block " + "upload failed\n"); goto fail; } @@ -682,8 +739,8 @@ static int p54u_upload_firmware_net2280(struct ieee80211_hw *dev) 0x002C | (unsigned long)&devreg->direct_mem_win); if (!(reg & cpu_to_le32(ISL38XX_DMA_STATUS_DONE)) || !(reg & cpu_to_le32(ISL38XX_DMA_STATUS_READY))) { - printk(KERN_ERR "p54usb: firmware DMA transfer " - "failed\n"); + dev_err(&priv->udev->dev, "(p54usb) firmware DMA " + "transfer failed\n"); goto fail; } @@ -748,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; @@ -786,19 +885,23 @@ static int __devinit p54u_probe(struct usb_interface *intf, struct p54u_priv *priv; int err; unsigned int i, recognized_pipes; - DECLARE_MAC_BUF(mac); dev = p54_init_common(sizeof(*priv)); + if (!dev) { - printk(KERN_ERR "p54usb: ieee80211 alloc failed\n"); + dev_err(&udev->dev, "(p54usb) ieee80211 alloc failed\n"); return -ENOMEM; } 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); @@ -823,42 +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); + 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) { - printk(KERN_ERR "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; @@ -872,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)