blob: b3c4fbd80d8d42cc4af9f98758b871cf68b87b54 [file] [log] [blame]
Michael Wueff1a592007-09-25 18:11:01 -07001
2/*
3 * Linux device driver for USB based Prism54
4 *
5 * Copyright (c) 2006, Michael Wu <flamingice@sourmilk.net>
6 *
7 * Based on the islsm (softmac prism54) driver, which is:
8 * Copyright 2004-2006 Jean-Baptiste Note <jbnote@gmail.com>, et al.
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License version 2 as
12 * published by the Free Software Foundation.
13 */
14
15#include <linux/init.h>
16#include <linux/usb.h>
17#include <linux/pci.h>
18#include <linux/firmware.h>
19#include <linux/etherdevice.h>
20#include <linux/delay.h>
21#include <linux/crc32.h>
22#include <net/mac80211.h>
23
24#include "p54.h"
Christian Lamparterd8c92102009-06-23 10:39:45 -050025#include "lmac.h"
Michael Wueff1a592007-09-25 18:11:01 -070026#include "p54usb.h"
27
28MODULE_AUTHOR("Michael Wu <flamingice@sourmilk.net>");
29MODULE_DESCRIPTION("Prism54 USB wireless driver");
30MODULE_LICENSE("GPL");
31MODULE_ALIAS("prism54usb");
Christian Lamparter9a8675d2008-10-18 23:04:15 +020032MODULE_FIRMWARE("isl3886usb");
33MODULE_FIRMWARE("isl3887usb");
Michael Wueff1a592007-09-25 18:11:01 -070034
35static struct usb_device_id p54u_table[] __devinitdata = {
36 /* Version 1 devices (pci chip + net2280) */
37 {USB_DEVICE(0x0506, 0x0a11)}, /* 3COM 3CRWE254G72 */
38 {USB_DEVICE(0x0707, 0xee06)}, /* SMC 2862W-G */
Shimada Hirofumi15a69a82010-02-14 04:16:16 +090039 {USB_DEVICE(0x07aa, 0x001c)}, /* Corega CG-WLUSB2GT */
Michael Wueff1a592007-09-25 18:11:01 -070040 {USB_DEVICE(0x083a, 0x4501)}, /* Accton 802.11g WN4501 USB */
41 {USB_DEVICE(0x083a, 0x4502)}, /* Siemens Gigaset USB Adapter */
Ivo Couckuyt1a175822008-02-20 14:58:00 -050042 {USB_DEVICE(0x083a, 0x5501)}, /* Phillips CPWUA054 */
Michael Wueff1a592007-09-25 18:11:01 -070043 {USB_DEVICE(0x0846, 0x4200)}, /* Netgear WG121 */
44 {USB_DEVICE(0x0846, 0x4210)}, /* Netgear WG121 the second ? */
45 {USB_DEVICE(0x0846, 0x4220)}, /* Netgear WG111 */
Christian Lamparterec366eb2008-10-13 23:41:53 +020046 {USB_DEVICE(0x09aa, 0x1000)}, /* Spinnaker Proto board */
Michael Wueff1a592007-09-25 18:11:01 -070047 {USB_DEVICE(0x0cde, 0x0006)}, /* Medion 40900, Roper Europe */
48 {USB_DEVICE(0x124a, 0x4023)}, /* Shuttle PN15, Airvast WM168g, IOGear GWU513 */
49 {USB_DEVICE(0x1915, 0x2234)}, /* Linksys WUSB54G OEM */
50 {USB_DEVICE(0x1915, 0x2235)}, /* Linksys WUSB54G Portable OEM */
51 {USB_DEVICE(0x2001, 0x3701)}, /* DLink DWL-G120 Spinnaker */
52 {USB_DEVICE(0x2001, 0x3703)}, /* DLink DWL-G122 */
53 {USB_DEVICE(0x5041, 0x2234)}, /* Linksys WUSB54G */
54 {USB_DEVICE(0x5041, 0x2235)}, /* Linksys WUSB54G Portable */
55
56 /* Version 2 devices (3887) */
Felix Homann45460022008-05-29 00:36:45 -070057 {USB_DEVICE(0x0471, 0x1230)}, /* Philips CPWUA054/00 */
Michael Wueff1a592007-09-25 18:11:01 -070058 {USB_DEVICE(0x050d, 0x7050)}, /* Belkin F5D7050 ver 1000 */
59 {USB_DEVICE(0x0572, 0x2000)}, /* Cohiba Proto board */
60 {USB_DEVICE(0x0572, 0x2002)}, /* Cohiba Proto board */
Michiel878e6a42009-01-04 17:22:28 -060061 {USB_DEVICE(0x06b9, 0x0121)}, /* Thomson SpeedTouch 121g */
Michael Wueff1a592007-09-25 18:11:01 -070062 {USB_DEVICE(0x0707, 0xee13)}, /* SMC 2862W-G version 2 */
63 {USB_DEVICE(0x083a, 0x4521)}, /* Siemens Gigaset USB Adapter 54 version 2 */
Jean-François Moine5b9a9192010-02-17 10:59:31 -060064 {USB_DEVICE(0x083a, 0xf503)}, /* Accton FD7050E ver 1010ec */
Michael Wueff1a592007-09-25 18:11:01 -070065 {USB_DEVICE(0x0846, 0x4240)}, /* Netgear WG111 (v2) */
66 {USB_DEVICE(0x0915, 0x2000)}, /* Cohiba Proto board */
67 {USB_DEVICE(0x0915, 0x2002)}, /* Cohiba Proto board */
68 {USB_DEVICE(0x0baf, 0x0118)}, /* U.S. Robotics U5 802.11g Adapter*/
69 {USB_DEVICE(0x0bf8, 0x1009)}, /* FUJITSU E-5400 USB D1700*/
70 {USB_DEVICE(0x0cde, 0x0006)}, /* Medion MD40900 */
71 {USB_DEVICE(0x0cde, 0x0008)}, /* Sagem XG703A */
Christian Lamparterf7f71172009-09-14 23:08:43 +020072 {USB_DEVICE(0x0cde, 0x0015)}, /* Zcomax XG-705A */
Michael Wueff1a592007-09-25 18:11:01 -070073 {USB_DEVICE(0x0d8e, 0x3762)}, /* DLink DWL-G120 Cohiba */
Jan Slupski43557e12008-03-10 22:41:18 -070074 {USB_DEVICE(0x124a, 0x4025)}, /* IOGear GWU513 (GW3887IK chip) */
Christian Lamparterec366eb2008-10-13 23:41:53 +020075 {USB_DEVICE(0x1260, 0xee22)}, /* SMC 2862W-G version 2 */
John W. Linville387e1002008-02-20 15:06:02 -050076 {USB_DEVICE(0x13b1, 0x000a)}, /* Linksys WUSB54G ver 2 */
Martti Huttunenc1098102007-10-04 00:06:00 -040077 {USB_DEVICE(0x13B1, 0x000C)}, /* Linksys WUSB54AG */
Christian Lampartere3062402009-03-29 22:50:28 +020078 {USB_DEVICE(0x1413, 0x5400)}, /* Telsey 802.11g USB2.0 Adapter */
Michael Wueff1a592007-09-25 18:11:01 -070079 {USB_DEVICE(0x1435, 0x0427)}, /* Inventel UR054G */
80 {USB_DEVICE(0x2001, 0x3704)}, /* DLink DWL-G122 rev A2 */
81 {USB_DEVICE(0x413c, 0x8102)}, /* Spinnaker DUT */
82 {USB_DEVICE(0x413c, 0x8104)}, /* Cohiba Proto board */
83 {}
84};
85
86MODULE_DEVICE_TABLE(usb, p54u_table);
87
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +020088static const struct {
89 u32 intf;
90 enum p54u_hw_type type;
Samuel Ortiz328d84f2009-05-27 10:12:51 +020091 const char *fw;
92 const char *fw_legacy;
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +020093 char hw[20];
94} p54u_fwlist[__NUM_P54U_HWTYPES] = {
95 {
96 .type = P54U_NET2280,
97 .intf = FW_LM86,
98 .fw = "isl3886usb",
99 .fw_legacy = "isl3890usb",
100 .hw = "ISL3886 + net2280",
101 },
102 {
103 .type = P54U_3887,
104 .intf = FW_LM87,
105 .fw = "isl3887usb",
106 .fw_legacy = "isl3887usb_bare",
107 .hw = "ISL3887",
108 },
109};
110
Michael Wueff1a592007-09-25 18:11:01 -0700111static void p54u_rx_cb(struct urb *urb)
112{
113 struct sk_buff *skb = (struct sk_buff *) urb->context;
114 struct p54u_rx_info *info = (struct p54u_rx_info *)skb->cb;
115 struct ieee80211_hw *dev = info->dev;
116 struct p54u_priv *priv = dev->priv;
117
Christian Lamparterdd397dc2008-12-09 15:14:37 +0100118 skb_unlink(skb, &priv->rx_queue);
119
Michael Wueff1a592007-09-25 18:11:01 -0700120 if (unlikely(urb->status)) {
Christian Lamparterdd397dc2008-12-09 15:14:37 +0100121 dev_kfree_skb_irq(skb);
Michael Wueff1a592007-09-25 18:11:01 -0700122 return;
123 }
124
Michael Wueff1a592007-09-25 18:11:01 -0700125 skb_put(skb, urb->actual_length);
Christian Lamparter2b808482008-09-04 12:29:38 +0200126
127 if (priv->hw_type == P54U_NET2280)
128 skb_pull(skb, priv->common.tx_hdr_len);
129 if (priv->common.fw_interface == FW_LM87) {
130 skb_pull(skb, 4);
131 skb_put(skb, 4);
132 }
Michael Wueff1a592007-09-25 18:11:01 -0700133
134 if (p54_rx(dev, skb)) {
Christian Lamparter4e416a62008-09-01 22:48:41 +0200135 skb = dev_alloc_skb(priv->common.rx_mtu + 32);
Michael Wueff1a592007-09-25 18:11:01 -0700136 if (unlikely(!skb)) {
Michael Wueff1a592007-09-25 18:11:01 -0700137 /* TODO check rx queue length and refill *somewhere* */
138 return;
139 }
140
141 info = (struct p54u_rx_info *) skb->cb;
142 info->urb = urb;
143 info->dev = dev;
144 urb->transfer_buffer = skb_tail_pointer(skb);
145 urb->context = skb;
Michael Wueff1a592007-09-25 18:11:01 -0700146 } else {
Christian Lamparter2b808482008-09-04 12:29:38 +0200147 if (priv->hw_type == P54U_NET2280)
148 skb_push(skb, priv->common.tx_hdr_len);
149 if (priv->common.fw_interface == FW_LM87) {
150 skb_push(skb, 4);
151 skb_put(skb, 4);
152 }
Christian Lamparterd47c3ce2008-08-13 23:41:48 +0200153 skb_reset_tail_pointer(skb);
Michael Wueff1a592007-09-25 18:11:01 -0700154 skb_trim(skb, 0);
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200155 urb->transfer_buffer = skb_tail_pointer(skb);
Michael Wueff1a592007-09-25 18:11:01 -0700156 }
Christian Lamparterdd397dc2008-12-09 15:14:37 +0100157 skb_queue_tail(&priv->rx_queue, skb);
158 usb_anchor_urb(urb, &priv->submitted);
159 if (usb_submit_urb(urb, GFP_ATOMIC)) {
160 skb_unlink(skb, &priv->rx_queue);
161 usb_unanchor_urb(urb);
162 dev_kfree_skb_irq(skb);
163 }
Michael Wueff1a592007-09-25 18:11:01 -0700164}
165
Christian Lamparter0a5ec962008-12-14 15:05:42 +0100166static void p54u_tx_cb(struct urb *urb)
Christian Lamparterb92f30d2008-10-15 04:07:16 +0200167{
168 struct sk_buff *skb = urb->context;
169 struct ieee80211_hw *dev = (struct ieee80211_hw *)
170 usb_get_intfdata(usb_ifnum_to_if(urb->dev, 0));
171
Christian Lampartere2fe1542009-01-20 00:27:57 +0100172 p54_free_skb(dev, skb);
Christian Lamparterdd397dc2008-12-09 15:14:37 +0100173}
174
175static void p54u_tx_dummy_cb(struct urb *urb) { }
176
177static void p54u_free_urbs(struct ieee80211_hw *dev)
178{
179 struct p54u_priv *priv = dev->priv;
180 usb_kill_anchored_urbs(&priv->submitted);
Christian Lamparterb92f30d2008-10-15 04:07:16 +0200181}
182
Michael Wueff1a592007-09-25 18:11:01 -0700183static int p54u_init_urbs(struct ieee80211_hw *dev)
184{
185 struct p54u_priv *priv = dev->priv;
Christian Lamparterdd397dc2008-12-09 15:14:37 +0100186 struct urb *entry = NULL;
Michael Wueff1a592007-09-25 18:11:01 -0700187 struct sk_buff *skb;
188 struct p54u_rx_info *info;
Christian Lamparterdd397dc2008-12-09 15:14:37 +0100189 int ret = 0;
Michael Wueff1a592007-09-25 18:11:01 -0700190
191 while (skb_queue_len(&priv->rx_queue) < 32) {
Christian Lamparter4e416a62008-09-01 22:48:41 +0200192 skb = __dev_alloc_skb(priv->common.rx_mtu + 32, GFP_KERNEL);
Christian Lamparterdd397dc2008-12-09 15:14:37 +0100193 if (!skb) {
194 ret = -ENOMEM;
195 goto err;
196 }
Michael Wueff1a592007-09-25 18:11:01 -0700197 entry = usb_alloc_urb(0, GFP_KERNEL);
198 if (!entry) {
Christian Lamparterdd397dc2008-12-09 15:14:37 +0100199 ret = -ENOMEM;
200 goto err;
Michael Wueff1a592007-09-25 18:11:01 -0700201 }
Christian Lamparterdd397dc2008-12-09 15:14:37 +0100202
Christian Lamparter4e416a62008-09-01 22:48:41 +0200203 usb_fill_bulk_urb(entry, priv->udev,
204 usb_rcvbulkpipe(priv->udev, P54U_PIPE_DATA),
205 skb_tail_pointer(skb),
206 priv->common.rx_mtu + 32, p54u_rx_cb, skb);
Michael Wueff1a592007-09-25 18:11:01 -0700207 info = (struct p54u_rx_info *) skb->cb;
208 info->urb = entry;
209 info->dev = dev;
210 skb_queue_tail(&priv->rx_queue, skb);
Christian Lamparterdd397dc2008-12-09 15:14:37 +0100211
212 usb_anchor_urb(entry, &priv->submitted);
213 ret = usb_submit_urb(entry, GFP_KERNEL);
214 if (ret) {
215 skb_unlink(skb, &priv->rx_queue);
216 usb_unanchor_urb(entry);
217 goto err;
218 }
219 usb_free_urb(entry);
220 entry = NULL;
Michael Wueff1a592007-09-25 18:11:01 -0700221 }
222
223 return 0;
Michael Wueff1a592007-09-25 18:11:01 -0700224
Christian Lamparterdd397dc2008-12-09 15:14:37 +0100225 err:
226 usb_free_urb(entry);
227 kfree_skb(skb);
228 p54u_free_urbs(dev);
229 return ret;
Michael Wueff1a592007-09-25 18:11:01 -0700230}
231
Johannes Bergc9127652008-12-01 18:19:36 +0100232static __le32 p54u_lm87_chksum(const __le32 *data, size_t length)
Christian Lamparter2b808482008-09-04 12:29:38 +0200233{
Larry Finger1f1c0e32008-09-25 14:54:28 -0500234 u32 chk = 0;
Christian Lamparter2b808482008-09-04 12:29:38 +0200235
236 length >>= 2;
237 while (length--) {
Johannes Bergc9127652008-12-01 18:19:36 +0100238 chk ^= le32_to_cpu(*data++);
Christian Lamparter2b808482008-09-04 12:29:38 +0200239 chk = (chk >> 5) ^ (chk << 3);
240 }
241
Larry Finger1f1c0e32008-09-25 14:54:28 -0500242 return cpu_to_le32(chk);
Christian Lamparter2b808482008-09-04 12:29:38 +0200243}
244
Christian Lamparter0a5ec962008-12-14 15:05:42 +0100245static void p54u_tx_lm87(struct ieee80211_hw *dev, struct sk_buff *skb)
Christian Lamparter2b808482008-09-04 12:29:38 +0200246{
247 struct p54u_priv *priv = dev->priv;
248 struct urb *data_urb;
Christian Lampartere2fe1542009-01-20 00:27:57 +0100249 struct lm87_tx_hdr *hdr = (void *)skb->data - sizeof(*hdr);
Christian Lamparter2b808482008-09-04 12:29:38 +0200250
251 data_urb = usb_alloc_urb(0, GFP_ATOMIC);
Christian Lamparter6d541a62009-07-06 15:17:56 +0200252 if (!data_urb) {
253 p54_free_skb(dev, skb);
Christian Lamparter2b808482008-09-04 12:29:38 +0200254 return;
Christian Lamparter6d541a62009-07-06 15:17:56 +0200255 }
Christian Lamparter2b808482008-09-04 12:29:38 +0200256
Christian Lampartere2fe1542009-01-20 00:27:57 +0100257 hdr->chksum = p54u_lm87_chksum((__le32 *)skb->data, skb->len);
258 hdr->device_addr = ((struct p54_hdr *)skb->data)->req_id;
Christian Lamparter2b808482008-09-04 12:29:38 +0200259
260 usb_fill_bulk_urb(data_urb, priv->udev,
Christian Lamparterb92f30d2008-10-15 04:07:16 +0200261 usb_sndbulkpipe(priv->udev, P54U_PIPE_DATA),
Christian Lampartere2fe1542009-01-20 00:27:57 +0100262 hdr, skb->len + sizeof(*hdr), FREE_AFTER_TX(skb) ?
263 p54u_tx_cb : p54u_tx_dummy_cb, skb);
Christian Lamparter00627f22008-12-20 02:21:56 +0100264 data_urb->transfer_flags |= URB_ZERO_PACKET;
Christian Lamparter2b808482008-09-04 12:29:38 +0200265
Christian Lamparterdd397dc2008-12-09 15:14:37 +0100266 usb_anchor_urb(data_urb, &priv->submitted);
267 if (usb_submit_urb(data_urb, GFP_ATOMIC)) {
268 usb_unanchor_urb(data_urb);
Christian Lamparterdd397dc2008-12-09 15:14:37 +0100269 p54_free_skb(dev, skb);
270 }
271 usb_free_urb(data_urb);
Christian Lamparter2b808482008-09-04 12:29:38 +0200272}
273
Christian Lamparter0a5ec962008-12-14 15:05:42 +0100274static void p54u_tx_net2280(struct ieee80211_hw *dev, struct sk_buff *skb)
Michael Wueff1a592007-09-25 18:11:01 -0700275{
276 struct p54u_priv *priv = dev->priv;
Christian Lamparter6d541a62009-07-06 15:17:56 +0200277 struct urb *int_urb = NULL, *data_urb = NULL;
Christian Lampartere2fe1542009-01-20 00:27:57 +0100278 struct net2280_tx_hdr *hdr = (void *)skb->data - sizeof(*hdr);
Christian Lamparter6d541a62009-07-06 15:17:56 +0200279 struct net2280_reg_write *reg = NULL;
280 int err = -ENOMEM;
Michael Wueff1a592007-09-25 18:11:01 -0700281
282 reg = kmalloc(sizeof(*reg), GFP_ATOMIC);
283 if (!reg)
Christian Lamparter6d541a62009-07-06 15:17:56 +0200284 goto out;
Michael Wueff1a592007-09-25 18:11:01 -0700285
286 int_urb = usb_alloc_urb(0, GFP_ATOMIC);
Christian Lamparter6d541a62009-07-06 15:17:56 +0200287 if (!int_urb)
288 goto out;
Michael Wueff1a592007-09-25 18:11:01 -0700289
290 data_urb = usb_alloc_urb(0, GFP_ATOMIC);
Christian Lamparter6d541a62009-07-06 15:17:56 +0200291 if (!data_urb)
292 goto out;
Michael Wueff1a592007-09-25 18:11:01 -0700293
294 reg->port = cpu_to_le16(NET2280_DEV_U32);
295 reg->addr = cpu_to_le32(P54U_DEV_BASE);
296 reg->val = cpu_to_le32(ISL38XX_DEV_INT_DATA);
297
Michael Wueff1a592007-09-25 18:11:01 -0700298 memset(hdr, 0, sizeof(*hdr));
Christian Lampartere2fe1542009-01-20 00:27:57 +0100299 hdr->len = cpu_to_le16(skb->len);
300 hdr->device_addr = ((struct p54_hdr *) skb->data)->req_id;
Michael Wueff1a592007-09-25 18:11:01 -0700301
302 usb_fill_bulk_urb(int_urb, priv->udev,
303 usb_sndbulkpipe(priv->udev, P54U_PIPE_DEV), reg, sizeof(*reg),
Christian Lamparterdd397dc2008-12-09 15:14:37 +0100304 p54u_tx_dummy_cb, dev);
305
306 /*
Christian Lamparter6d541a62009-07-06 15:17:56 +0200307 * URB_FREE_BUFFER triggers a code path in the USB subsystem that will
308 * free what is inside the transfer_buffer after the last reference to
309 * the int_urb is dropped.
Christian Lamparterdd397dc2008-12-09 15:14:37 +0100310 */
Christian Lamparterb4068a82009-01-20 23:11:21 +0100311 int_urb->transfer_flags |= URB_FREE_BUFFER | URB_ZERO_PACKET;
Christian Lamparter6d541a62009-07-06 15:17:56 +0200312 reg = NULL;
Michael Wueff1a592007-09-25 18:11:01 -0700313
314 usb_fill_bulk_urb(data_urb, priv->udev,
Christian Lamparterb92f30d2008-10-15 04:07:16 +0200315 usb_sndbulkpipe(priv->udev, P54U_PIPE_DATA),
Christian Lampartere2fe1542009-01-20 00:27:57 +0100316 hdr, skb->len + sizeof(*hdr), FREE_AFTER_TX(skb) ?
317 p54u_tx_cb : p54u_tx_dummy_cb, skb);
Christian Lamparterb4068a82009-01-20 23:11:21 +0100318 data_urb->transfer_flags |= URB_ZERO_PACKET;
Christian Lamparterdd397dc2008-12-09 15:14:37 +0100319
320 usb_anchor_urb(int_urb, &priv->submitted);
321 err = usb_submit_urb(int_urb, GFP_ATOMIC);
322 if (err) {
323 usb_unanchor_urb(int_urb);
324 goto out;
325 }
326
327 usb_anchor_urb(data_urb, &priv->submitted);
328 err = usb_submit_urb(data_urb, GFP_ATOMIC);
329 if (err) {
330 usb_unanchor_urb(data_urb);
331 goto out;
332 }
Christian Lamparter6d541a62009-07-06 15:17:56 +0200333out:
Christian Lamparterdd397dc2008-12-09 15:14:37 +0100334 usb_free_urb(int_urb);
335 usb_free_urb(data_urb);
336
337 if (err) {
Christian Lamparter6d541a62009-07-06 15:17:56 +0200338 kfree(reg);
Christian Lamparterdd397dc2008-12-09 15:14:37 +0100339 p54_free_skb(dev, skb);
340 }
Michael Wueff1a592007-09-25 18:11:01 -0700341}
342
343static int p54u_write(struct p54u_priv *priv,
344 struct net2280_reg_write *buf,
345 enum net2280_op_type type,
346 __le32 addr, __le32 val)
347{
348 unsigned int ep;
349 int alen;
350
351 if (type & 0x0800)
352 ep = usb_sndbulkpipe(priv->udev, P54U_PIPE_DEV);
353 else
354 ep = usb_sndbulkpipe(priv->udev, P54U_PIPE_BRG);
355
356 buf->port = cpu_to_le16(type);
357 buf->addr = addr;
358 buf->val = val;
359
360 return usb_bulk_msg(priv->udev, ep, buf, sizeof(*buf), &alen, 1000);
361}
362
363static int p54u_read(struct p54u_priv *priv, void *buf,
364 enum net2280_op_type type,
365 __le32 addr, __le32 *val)
366{
367 struct net2280_reg_read *read = buf;
368 __le32 *reg = buf;
369 unsigned int ep;
370 int alen, err;
371
372 if (type & 0x0800)
373 ep = P54U_PIPE_DEV;
374 else
375 ep = P54U_PIPE_BRG;
376
377 read->port = cpu_to_le16(type);
378 read->addr = addr;
379
380 err = usb_bulk_msg(priv->udev, usb_sndbulkpipe(priv->udev, ep),
381 read, sizeof(*read), &alen, 1000);
382 if (err)
383 return err;
384
385 err = usb_bulk_msg(priv->udev, usb_rcvbulkpipe(priv->udev, ep),
386 reg, sizeof(*reg), &alen, 1000);
387 if (err)
388 return err;
389
390 *val = *reg;
391 return 0;
392}
393
394static int p54u_bulk_msg(struct p54u_priv *priv, unsigned int ep,
395 void *data, size_t len)
396{
397 int alen;
398 return usb_bulk_msg(priv->udev, usb_sndbulkpipe(priv->udev, ep),
399 data, len, &alen, 2000);
400}
401
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200402static int p54u_device_reset(struct ieee80211_hw *dev)
Christian Lamparter69828692008-12-26 19:08:31 +0100403{
404 struct p54u_priv *priv = dev->priv;
Christian Lamparterc88a7682009-01-16 20:24:31 +0100405 int ret, lock = (priv->intf->condition != USB_INTERFACE_BINDING);
Christian Lamparter69828692008-12-26 19:08:31 +0100406
Christian Lamparterc88a7682009-01-16 20:24:31 +0100407 if (lock) {
408 ret = usb_lock_device_for_reset(priv->udev, priv->intf);
409 if (ret < 0) {
410 dev_err(&priv->udev->dev, "(p54usb) unable to lock "
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200411 "device for reset (%d)!\n", ret);
Christian Lamparterc88a7682009-01-16 20:24:31 +0100412 return ret;
413 }
Christian Lamparter69828692008-12-26 19:08:31 +0100414 }
415
416 ret = usb_reset_device(priv->udev);
417 if (lock)
418 usb_unlock_device(priv->udev);
419
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200420 if (ret)
Christian Lamparter69828692008-12-26 19:08:31 +0100421 dev_err(&priv->udev->dev, "(p54usb) unable to reset "
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200422 "device (%d)!\n", ret);
423
424 return ret;
425}
426
427static const char p54u_romboot_3887[] = "~~~~";
428static int p54u_firmware_reset_3887(struct ieee80211_hw *dev)
429{
430 struct p54u_priv *priv = dev->priv;
Larry Finger21d6c272009-11-11 18:02:29 -0600431 u8 *buf;
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200432 int ret;
Christian Lamparter69828692008-12-26 19:08:31 +0100433
Larry Finger21d6c272009-11-11 18:02:29 -0600434 buf = kmalloc(4, GFP_KERNEL);
435 if (!buf)
436 return -ENOMEM;
437 memcpy(buf, p54u_romboot_3887, 4);
Christian Lamparter69828692008-12-26 19:08:31 +0100438 ret = p54u_bulk_msg(priv, P54U_PIPE_DATA,
Larry Finger21d6c272009-11-11 18:02:29 -0600439 buf, 4);
440 kfree(buf);
Christian Lamparter69828692008-12-26 19:08:31 +0100441 if (ret)
442 dev_err(&priv->udev->dev, "(p54usb) unable to jump to "
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200443 "boot ROM (%d)!\n", ret);
Christian Lamparter69828692008-12-26 19:08:31 +0100444
445 return ret;
446}
447
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200448static const char p54u_firmware_upload_3887[] = "<\r";
Michael Wueff1a592007-09-25 18:11:01 -0700449static int p54u_upload_firmware_3887(struct ieee80211_hw *dev)
450{
Michael Wueff1a592007-09-25 18:11:01 -0700451 struct p54u_priv *priv = dev->priv;
Michael Wueff1a592007-09-25 18:11:01 -0700452 int err, alen;
453 u8 carry = 0;
David Woodhouse8b72eb42008-05-24 00:08:55 +0100454 u8 *buf, *tmp;
455 const u8 *data;
Michael Wueff1a592007-09-25 18:11:01 -0700456 unsigned int left, remains, block_size;
457 struct x2_header *hdr;
458 unsigned long timeout;
459
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200460 err = p54u_firmware_reset_3887(dev);
461 if (err)
462 return err;
463
Michael Wueff1a592007-09-25 18:11:01 -0700464 tmp = buf = kmalloc(P54U_FW_BLOCK, GFP_KERNEL);
465 if (!buf) {
Christian Lamparter02e37ba2008-11-29 22:39:08 +0100466 dev_err(&priv->udev->dev, "(p54usb) cannot allocate firmware"
467 "upload buffer!\n");
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200468 return -ENOMEM;
Michael Wueff1a592007-09-25 18:11:01 -0700469 }
470
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200471 left = block_size = min((size_t)P54U_FW_BLOCK, priv->fw->size);
Christian Lamparter69828692008-12-26 19:08:31 +0100472 strcpy(buf, p54u_firmware_upload_3887);
473 left -= strlen(p54u_firmware_upload_3887);
474 tmp += strlen(p54u_firmware_upload_3887);
Michael Wueff1a592007-09-25 18:11:01 -0700475
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200476 data = priv->fw->data;
477 remains = priv->fw->size;
Michael Wueff1a592007-09-25 18:11:01 -0700478
Christian Lamparter69828692008-12-26 19:08:31 +0100479 hdr = (struct x2_header *)(buf + strlen(p54u_firmware_upload_3887));
Michael Wueff1a592007-09-25 18:11:01 -0700480 memcpy(hdr->signature, X2_SIGNATURE, X2_SIGNATURE_SIZE);
481 hdr->fw_load_addr = cpu_to_le32(ISL38XX_DEV_FIRMWARE_ADDR);
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200482 hdr->fw_length = cpu_to_le32(priv->fw->size);
Michael Wueff1a592007-09-25 18:11:01 -0700483 hdr->crc = cpu_to_le32(~crc32_le(~0, (void *)&hdr->fw_load_addr,
484 sizeof(u32)*2));
485 left -= sizeof(*hdr);
486 tmp += sizeof(*hdr);
487
488 while (remains) {
489 while (left--) {
490 if (carry) {
491 *tmp++ = carry;
492 carry = 0;
493 remains--;
494 continue;
495 }
496 switch (*data) {
497 case '~':
498 *tmp++ = '}';
499 carry = '^';
500 break;
501 case '}':
502 *tmp++ = '}';
503 carry = ']';
504 break;
505 default:
506 *tmp++ = *data;
507 remains--;
508 break;
509 }
510 data++;
511 }
512
513 err = p54u_bulk_msg(priv, P54U_PIPE_DATA, buf, block_size);
514 if (err) {
Christian Lamparter02e37ba2008-11-29 22:39:08 +0100515 dev_err(&priv->udev->dev, "(p54usb) firmware "
516 "upload failed!\n");
Michael Wueff1a592007-09-25 18:11:01 -0700517 goto err_upload_failed;
518 }
519
520 tmp = buf;
521 left = block_size = min((unsigned int)P54U_FW_BLOCK, remains);
522 }
523
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200524 *((__le32 *)buf) = cpu_to_le32(~crc32_le(~0, priv->fw->data,
525 priv->fw->size));
Michael Wueff1a592007-09-25 18:11:01 -0700526 err = p54u_bulk_msg(priv, P54U_PIPE_DATA, buf, sizeof(u32));
527 if (err) {
Christian Lamparter02e37ba2008-11-29 22:39:08 +0100528 dev_err(&priv->udev->dev, "(p54usb) firmware upload failed!\n");
Michael Wueff1a592007-09-25 18:11:01 -0700529 goto err_upload_failed;
530 }
Michael Wueff1a592007-09-25 18:11:01 -0700531 timeout = jiffies + msecs_to_jiffies(1000);
532 while (!(err = usb_bulk_msg(priv->udev,
533 usb_rcvbulkpipe(priv->udev, P54U_PIPE_DATA), buf, 128, &alen, 1000))) {
534 if (alen > 2 && !memcmp(buf, "OK", 2))
535 break;
536
537 if (alen > 5 && !memcmp(buf, "ERROR", 5)) {
Michael Wueff1a592007-09-25 18:11:01 -0700538 err = -EINVAL;
539 break;
540 }
541
542 if (time_after(jiffies, timeout)) {
Christian Lamparter02e37ba2008-11-29 22:39:08 +0100543 dev_err(&priv->udev->dev, "(p54usb) firmware boot "
544 "timed out!\n");
Michael Wueff1a592007-09-25 18:11:01 -0700545 err = -ETIMEDOUT;
546 break;
547 }
548 }
Christian Lamparter02e37ba2008-11-29 22:39:08 +0100549 if (err) {
550 dev_err(&priv->udev->dev, "(p54usb) firmware upload failed!\n");
Michael Wueff1a592007-09-25 18:11:01 -0700551 goto err_upload_failed;
Christian Lamparter02e37ba2008-11-29 22:39:08 +0100552 }
Michael Wueff1a592007-09-25 18:11:01 -0700553
554 buf[0] = 'g';
555 buf[1] = '\r';
556 err = p54u_bulk_msg(priv, P54U_PIPE_DATA, buf, 2);
557 if (err) {
Christian Lamparter02e37ba2008-11-29 22:39:08 +0100558 dev_err(&priv->udev->dev, "(p54usb) firmware boot failed!\n");
Michael Wueff1a592007-09-25 18:11:01 -0700559 goto err_upload_failed;
560 }
561
562 timeout = jiffies + msecs_to_jiffies(1000);
563 while (!(err = usb_bulk_msg(priv->udev,
564 usb_rcvbulkpipe(priv->udev, P54U_PIPE_DATA), buf, 128, &alen, 1000))) {
565 if (alen > 0 && buf[0] == 'g')
566 break;
567
568 if (time_after(jiffies, timeout)) {
569 err = -ETIMEDOUT;
570 break;
571 }
572 }
573 if (err)
574 goto err_upload_failed;
575
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200576err_upload_failed:
Michael Wueff1a592007-09-25 18:11:01 -0700577 kfree(buf);
Michael Wueff1a592007-09-25 18:11:01 -0700578 return err;
579}
580
581static int p54u_upload_firmware_net2280(struct ieee80211_hw *dev)
582{
583 struct p54u_priv *priv = dev->priv;
Michael Wueff1a592007-09-25 18:11:01 -0700584 const struct p54p_csr *devreg = (const struct p54p_csr *) P54U_DEV_BASE;
585 int err, alen;
586 void *buf;
587 __le32 reg;
588 unsigned int remains, offset;
David Woodhouse8b72eb42008-05-24 00:08:55 +0100589 const u8 *data;
Michael Wueff1a592007-09-25 18:11:01 -0700590
591 buf = kmalloc(512, GFP_KERNEL);
592 if (!buf) {
Christian Lamparter02e37ba2008-11-29 22:39:08 +0100593 dev_err(&priv->udev->dev, "(p54usb) firmware buffer "
594 "alloc failed!\n");
Michael Wueff1a592007-09-25 18:11:01 -0700595 return -ENOMEM;
596 }
597
Michael Wueff1a592007-09-25 18:11:01 -0700598#define P54U_WRITE(type, addr, data) \
599 do {\
600 err = p54u_write(priv, buf, type,\
601 cpu_to_le32((u32)(unsigned long)addr), data);\
602 if (err) \
603 goto fail;\
604 } while (0)
605
606#define P54U_READ(type, addr) \
607 do {\
608 err = p54u_read(priv, buf, type,\
609 cpu_to_le32((u32)(unsigned long)addr), &reg);\
610 if (err)\
611 goto fail;\
612 } while (0)
613
614 /* power down net2280 bridge */
615 P54U_READ(NET2280_BRG_U32, NET2280_GPIOCTL);
616 reg |= cpu_to_le32(P54U_BRG_POWER_DOWN);
617 reg &= cpu_to_le32(~P54U_BRG_POWER_UP);
618 P54U_WRITE(NET2280_BRG_U32, NET2280_GPIOCTL, reg);
619
620 mdelay(100);
621
622 /* power up bridge */
623 reg |= cpu_to_le32(P54U_BRG_POWER_UP);
624 reg &= cpu_to_le32(~P54U_BRG_POWER_DOWN);
625 P54U_WRITE(NET2280_BRG_U32, NET2280_GPIOCTL, reg);
626
627 mdelay(100);
628
629 P54U_WRITE(NET2280_BRG_U32, NET2280_DEVINIT,
630 cpu_to_le32(NET2280_CLK_30Mhz |
631 NET2280_PCI_ENABLE |
632 NET2280_PCI_SOFT_RESET));
633
634 mdelay(20);
635
636 P54U_WRITE(NET2280_BRG_CFG_U16, PCI_COMMAND,
637 cpu_to_le32(PCI_COMMAND_MEMORY |
638 PCI_COMMAND_MASTER));
639
640 P54U_WRITE(NET2280_BRG_CFG_U32, PCI_BASE_ADDRESS_0,
641 cpu_to_le32(NET2280_BASE));
642
643 P54U_READ(NET2280_BRG_CFG_U16, PCI_STATUS);
644 reg |= cpu_to_le32(PCI_STATUS_REC_MASTER_ABORT);
645 P54U_WRITE(NET2280_BRG_CFG_U16, PCI_STATUS, reg);
646
647 // TODO: we really need this?
648 P54U_READ(NET2280_BRG_U32, NET2280_RELNUM);
649
650 P54U_WRITE(NET2280_BRG_U32, NET2280_EPA_RSP,
651 cpu_to_le32(NET2280_CLEAR_NAK_OUT_PACKETS_MODE));
652 P54U_WRITE(NET2280_BRG_U32, NET2280_EPC_RSP,
653 cpu_to_le32(NET2280_CLEAR_NAK_OUT_PACKETS_MODE));
654
655 P54U_WRITE(NET2280_BRG_CFG_U32, PCI_BASE_ADDRESS_2,
656 cpu_to_le32(NET2280_BASE2));
657
658 /* finally done setting up the bridge */
659
660 P54U_WRITE(NET2280_DEV_CFG_U16, 0x10000 | PCI_COMMAND,
661 cpu_to_le32(PCI_COMMAND_MEMORY |
662 PCI_COMMAND_MASTER));
663
664 P54U_WRITE(NET2280_DEV_CFG_U16, 0x10000 | 0x40 /* TRDY timeout */, 0);
665 P54U_WRITE(NET2280_DEV_CFG_U32, 0x10000 | PCI_BASE_ADDRESS_0,
666 cpu_to_le32(P54U_DEV_BASE));
667
668 P54U_WRITE(NET2280_BRG_U32, NET2280_USBIRQENB1, 0);
669 P54U_WRITE(NET2280_BRG_U32, NET2280_IRQSTAT1,
670 cpu_to_le32(NET2280_PCI_INTA_INTERRUPT));
671
672 /* do romboot */
673 P54U_WRITE(NET2280_DEV_U32, &devreg->int_enable, 0);
674
675 P54U_READ(NET2280_DEV_U32, &devreg->ctrl_stat);
676 reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_RESET);
677 reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_RAMBOOT);
678 reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_CLKRUN);
679 P54U_WRITE(NET2280_DEV_U32, &devreg->ctrl_stat, reg);
680
681 mdelay(20);
682
683 reg |= cpu_to_le32(ISL38XX_CTRL_STAT_RESET);
684 P54U_WRITE(NET2280_DEV_U32, &devreg->ctrl_stat, reg);
685
686 mdelay(20);
687
688 reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_RESET);
689 P54U_WRITE(NET2280_DEV_U32, &devreg->ctrl_stat, reg);
690
691 mdelay(100);
692
693 P54U_READ(NET2280_DEV_U32, &devreg->int_ident);
694 P54U_WRITE(NET2280_DEV_U32, &devreg->int_ack, reg);
695
696 /* finally, we can upload firmware now! */
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200697 remains = priv->fw->size;
698 data = priv->fw->data;
Michael Wueff1a592007-09-25 18:11:01 -0700699 offset = ISL38XX_DEV_FIRMWARE_ADDR;
700
701 while (remains) {
702 unsigned int block_len = min(remains, (unsigned int)512);
703 memcpy(buf, data, block_len);
704
705 err = p54u_bulk_msg(priv, P54U_PIPE_DATA, buf, block_len);
706 if (err) {
Christian Lamparter02e37ba2008-11-29 22:39:08 +0100707 dev_err(&priv->udev->dev, "(p54usb) firmware block "
708 "upload failed\n");
Michael Wueff1a592007-09-25 18:11:01 -0700709 goto fail;
710 }
711
712 P54U_WRITE(NET2280_DEV_U32, &devreg->direct_mem_base,
713 cpu_to_le32(0xc0000f00));
714
715 P54U_WRITE(NET2280_DEV_U32,
716 0x0020 | (unsigned long)&devreg->direct_mem_win, 0);
717 P54U_WRITE(NET2280_DEV_U32,
718 0x0020 | (unsigned long)&devreg->direct_mem_win,
719 cpu_to_le32(1));
720
721 P54U_WRITE(NET2280_DEV_U32,
722 0x0024 | (unsigned long)&devreg->direct_mem_win,
723 cpu_to_le32(block_len));
724 P54U_WRITE(NET2280_DEV_U32,
725 0x0028 | (unsigned long)&devreg->direct_mem_win,
726 cpu_to_le32(offset));
727
728 P54U_WRITE(NET2280_DEV_U32, &devreg->dma_addr,
729 cpu_to_le32(NET2280_EPA_FIFO_PCI_ADDR));
730 P54U_WRITE(NET2280_DEV_U32, &devreg->dma_len,
731 cpu_to_le32(block_len >> 2));
732 P54U_WRITE(NET2280_DEV_U32, &devreg->dma_ctrl,
733 cpu_to_le32(ISL38XX_DMA_MASTER_CONTROL_TRIGGER));
734
735 mdelay(10);
736
737 P54U_READ(NET2280_DEV_U32,
738 0x002C | (unsigned long)&devreg->direct_mem_win);
739 if (!(reg & cpu_to_le32(ISL38XX_DMA_STATUS_DONE)) ||
740 !(reg & cpu_to_le32(ISL38XX_DMA_STATUS_READY))) {
Christian Lamparter02e37ba2008-11-29 22:39:08 +0100741 dev_err(&priv->udev->dev, "(p54usb) firmware DMA "
742 "transfer failed\n");
Michael Wueff1a592007-09-25 18:11:01 -0700743 goto fail;
744 }
745
746 P54U_WRITE(NET2280_BRG_U32, NET2280_EPA_STAT,
747 cpu_to_le32(NET2280_FIFO_FLUSH));
748
749 remains -= block_len;
750 data += block_len;
751 offset += block_len;
752 }
753
754 /* do ramboot */
755 P54U_READ(NET2280_DEV_U32, &devreg->ctrl_stat);
756 reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_RESET);
757 reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_CLKRUN);
758 reg |= cpu_to_le32(ISL38XX_CTRL_STAT_RAMBOOT);
759 P54U_WRITE(NET2280_DEV_U32, &devreg->ctrl_stat, reg);
760
761 mdelay(20);
762
763 reg |= cpu_to_le32(ISL38XX_CTRL_STAT_RESET);
764 P54U_WRITE(NET2280_DEV_U32, &devreg->ctrl_stat, reg);
765
766 reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_RESET);
767 P54U_WRITE(NET2280_DEV_U32, &devreg->ctrl_stat, reg);
768
769 mdelay(100);
770
771 P54U_READ(NET2280_DEV_U32, &devreg->int_ident);
772 P54U_WRITE(NET2280_DEV_U32, &devreg->int_ack, reg);
773
774 /* start up the firmware */
775 P54U_WRITE(NET2280_DEV_U32, &devreg->int_enable,
776 cpu_to_le32(ISL38XX_INT_IDENT_INIT));
777
778 P54U_WRITE(NET2280_BRG_U32, NET2280_IRQSTAT1,
779 cpu_to_le32(NET2280_PCI_INTA_INTERRUPT));
780
781 P54U_WRITE(NET2280_BRG_U32, NET2280_USBIRQENB1,
782 cpu_to_le32(NET2280_PCI_INTA_INTERRUPT_ENABLE |
783 NET2280_USB_INTERRUPT_ENABLE));
784
785 P54U_WRITE(NET2280_DEV_U32, &devreg->dev_int,
786 cpu_to_le32(ISL38XX_DEV_INT_RESET));
787
788 err = usb_interrupt_msg(priv->udev,
789 usb_rcvbulkpipe(priv->udev, P54U_PIPE_INT),
790 buf, sizeof(__le32), &alen, 1000);
791 if (err || alen != sizeof(__le32))
792 goto fail;
793
794 P54U_READ(NET2280_DEV_U32, &devreg->int_ident);
795 P54U_WRITE(NET2280_DEV_U32, &devreg->int_ack, reg);
796
797 if (!(reg & cpu_to_le32(ISL38XX_INT_IDENT_INIT)))
798 err = -EINVAL;
799
800 P54U_WRITE(NET2280_BRG_U32, NET2280_USBIRQENB1, 0);
801 P54U_WRITE(NET2280_BRG_U32, NET2280_IRQSTAT1,
802 cpu_to_le32(NET2280_PCI_INTA_INTERRUPT));
803
804#undef P54U_WRITE
805#undef P54U_READ
806
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200807fail:
Michael Wueff1a592007-09-25 18:11:01 -0700808 kfree(buf);
809 return err;
810}
811
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200812static int p54u_load_firmware(struct ieee80211_hw *dev)
813{
814 struct p54u_priv *priv = dev->priv;
815 int err, i;
816
817 BUILD_BUG_ON(ARRAY_SIZE(p54u_fwlist) != __NUM_P54U_HWTYPES);
818
819 for (i = 0; i < __NUM_P54U_HWTYPES; i++)
820 if (p54u_fwlist[i].type == priv->hw_type)
821 break;
822
823 if (i == __NUM_P54U_HWTYPES)
824 return -EOPNOTSUPP;
825
826 err = request_firmware(&priv->fw, p54u_fwlist[i].fw, &priv->udev->dev);
827 if (err) {
828 dev_err(&priv->udev->dev, "(p54usb) cannot load firmware %s "
829 "(%d)!\n", p54u_fwlist[i].fw, err);
830
831 err = request_firmware(&priv->fw, p54u_fwlist[i].fw_legacy,
832 &priv->udev->dev);
833 if (err)
834 return err;
835 }
836
837 err = p54_parse_firmware(dev, priv->fw);
838 if (err)
839 goto out;
840
841 if (priv->common.fw_interface != p54u_fwlist[i].intf) {
842 dev_err(&priv->udev->dev, "wrong firmware, please get "
843 "a firmware for \"%s\" and try again.\n",
844 p54u_fwlist[i].hw);
845 err = -EINVAL;
846 }
847
848out:
849 if (err)
850 release_firmware(priv->fw);
851
852 return err;
853}
854
Michael Wueff1a592007-09-25 18:11:01 -0700855static int p54u_open(struct ieee80211_hw *dev)
856{
857 struct p54u_priv *priv = dev->priv;
858 int err;
859
860 err = p54u_init_urbs(dev);
861 if (err) {
862 return err;
863 }
864
865 priv->common.open = p54u_init_urbs;
866
867 return 0;
868}
869
870static void p54u_stop(struct ieee80211_hw *dev)
871{
872 /* TODO: figure out how to reliably stop the 3887 and net2280 so
873 the hardware is still usable next time we want to start it.
874 until then, we just stop listening to the hardware.. */
875 p54u_free_urbs(dev);
876 return;
877}
878
879static int __devinit p54u_probe(struct usb_interface *intf,
880 const struct usb_device_id *id)
881{
882 struct usb_device *udev = interface_to_usbdev(intf);
883 struct ieee80211_hw *dev;
884 struct p54u_priv *priv;
885 int err;
886 unsigned int i, recognized_pipes;
Michael Wueff1a592007-09-25 18:11:01 -0700887
888 dev = p54_init_common(sizeof(*priv));
Christian Lamparter02e37ba2008-11-29 22:39:08 +0100889
Michael Wueff1a592007-09-25 18:11:01 -0700890 if (!dev) {
Christian Lamparter02e37ba2008-11-29 22:39:08 +0100891 dev_err(&udev->dev, "(p54usb) ieee80211 alloc failed\n");
Michael Wueff1a592007-09-25 18:11:01 -0700892 return -ENOMEM;
893 }
894
895 priv = dev->priv;
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200896 priv->hw_type = P54U_INVALID_HW;
Michael Wueff1a592007-09-25 18:11:01 -0700897
898 SET_IEEE80211_DEV(dev, &intf->dev);
899 usb_set_intfdata(intf, dev);
900 priv->udev = udev;
Christian Lamparter69828692008-12-26 19:08:31 +0100901 priv->intf = intf;
902 skb_queue_head_init(&priv->rx_queue);
903 init_usb_anchor(&priv->submitted);
Michael Wueff1a592007-09-25 18:11:01 -0700904
905 usb_get_dev(udev);
906
907 /* really lazy and simple way of figuring out if we're a 3887 */
908 /* TODO: should just stick the identification in the device table */
909 i = intf->altsetting->desc.bNumEndpoints;
910 recognized_pipes = 0;
911 while (i--) {
912 switch (intf->altsetting->endpoint[i].desc.bEndpointAddress) {
913 case P54U_PIPE_DATA:
914 case P54U_PIPE_MGMT:
915 case P54U_PIPE_BRG:
916 case P54U_PIPE_DEV:
917 case P54U_PIPE_DATA | USB_DIR_IN:
918 case P54U_PIPE_MGMT | USB_DIR_IN:
919 case P54U_PIPE_BRG | USB_DIR_IN:
920 case P54U_PIPE_DEV | USB_DIR_IN:
921 case P54U_PIPE_INT | USB_DIR_IN:
922 recognized_pipes++;
923 }
924 }
925 priv->common.open = p54u_open;
Christian Lamparter2b808482008-09-04 12:29:38 +0200926 priv->common.stop = p54u_stop;
Michael Wueff1a592007-09-25 18:11:01 -0700927 if (recognized_pipes < P54U_PIPE_NUMBER) {
Hauke Mehrtens13792572009-05-01 13:12:36 +0200928#ifdef CONFIG_PM
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200929 /* ISL3887 needs a full reset on resume */
930 udev->reset_resume = 1;
931 err = p54u_device_reset(dev);
Hauke Mehrtens13792572009-05-01 13:12:36 +0200932#endif
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200933
Michael Wueff1a592007-09-25 18:11:01 -0700934 priv->hw_type = P54U_3887;
Christian Lampartera406ac02009-04-25 21:11:55 +0200935 dev->extra_tx_headroom += sizeof(struct lm87_tx_hdr);
936 priv->common.tx_hdr_len = sizeof(struct lm87_tx_hdr);
937 priv->common.tx = p54u_tx_lm87;
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200938 priv->upload_fw = p54u_upload_firmware_3887;
Michael Wueff1a592007-09-25 18:11:01 -0700939 } else {
Christian Lamparter2b808482008-09-04 12:29:38 +0200940 priv->hw_type = P54U_NET2280;
Michael Wueff1a592007-09-25 18:11:01 -0700941 dev->extra_tx_headroom += sizeof(struct net2280_tx_hdr);
942 priv->common.tx_hdr_len = sizeof(struct net2280_tx_hdr);
943 priv->common.tx = p54u_tx_net2280;
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200944 priv->upload_fw = p54u_upload_firmware_net2280;
Christian Lamparter2b808482008-09-04 12:29:38 +0200945 }
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200946 err = p54u_load_firmware(dev);
Michael Wueff1a592007-09-25 18:11:01 -0700947 if (err)
948 goto err_free_dev;
949
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200950 err = priv->upload_fw(dev);
951 if (err)
952 goto err_free_fw;
953
Christian Lamparter7cb77072008-09-01 22:48:51 +0200954 p54u_open(dev);
955 err = p54_read_eeprom(dev);
956 p54u_stop(dev);
Michael Wueff1a592007-09-25 18:11:01 -0700957 if (err)
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200958 goto err_free_fw;
Michael Wueff1a592007-09-25 18:11:01 -0700959
Christian Lamparter2ac71072009-03-05 21:30:10 +0100960 err = p54_register_common(dev, &udev->dev);
961 if (err)
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200962 goto err_free_fw;
Michael Wueff1a592007-09-25 18:11:01 -0700963
Michael Wueff1a592007-09-25 18:11:01 -0700964 return 0;
965
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200966err_free_fw:
967 release_firmware(priv->fw);
968
969err_free_dev:
Christian Lamparterd8c92102009-06-23 10:39:45 -0500970 p54_free_common(dev);
Michael Wueff1a592007-09-25 18:11:01 -0700971 usb_set_intfdata(intf, NULL);
972 usb_put_dev(udev);
973 return err;
974}
975
976static void __devexit p54u_disconnect(struct usb_interface *intf)
977{
978 struct ieee80211_hw *dev = usb_get_intfdata(intf);
979 struct p54u_priv *priv;
980
981 if (!dev)
982 return;
983
Christian Lamparterd8c92102009-06-23 10:39:45 -0500984 p54_unregister_common(dev);
Michael Wueff1a592007-09-25 18:11:01 -0700985
986 priv = dev->priv;
987 usb_put_dev(interface_to_usbdev(intf));
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200988 release_firmware(priv->fw);
Michael Wueff1a592007-09-25 18:11:01 -0700989 p54_free_common(dev);
Michael Wueff1a592007-09-25 18:11:01 -0700990}
991
Christian Lamparter69828692008-12-26 19:08:31 +0100992static int p54u_pre_reset(struct usb_interface *intf)
993{
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200994 struct ieee80211_hw *dev = usb_get_intfdata(intf);
995
996 if (!dev)
997 return -ENODEV;
998
999 p54u_stop(dev);
Christian Lamparter69828692008-12-26 19:08:31 +01001000 return 0;
1001}
1002
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +02001003static int p54u_resume(struct usb_interface *intf)
1004{
1005 struct ieee80211_hw *dev = usb_get_intfdata(intf);
1006 struct p54u_priv *priv;
1007
1008 if (!dev)
1009 return -ENODEV;
1010
1011 priv = dev->priv;
1012 if (unlikely(!(priv->upload_fw && priv->fw)))
1013 return 0;
1014
1015 return priv->upload_fw(dev);
1016}
1017
Christian Lamparter69828692008-12-26 19:08:31 +01001018static int p54u_post_reset(struct usb_interface *intf)
1019{
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +02001020 struct ieee80211_hw *dev = usb_get_intfdata(intf);
1021 struct p54u_priv *priv;
1022 int err;
1023
1024 err = p54u_resume(intf);
1025 if (err)
1026 return err;
1027
1028 /* reinitialize old device state */
1029 priv = dev->priv;
1030 if (priv->common.mode != NL80211_IFTYPE_UNSPECIFIED)
1031 ieee80211_restart_hw(dev);
1032
Christian Lamparter69828692008-12-26 19:08:31 +01001033 return 0;
1034}
1035
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +02001036#ifdef CONFIG_PM
1037
1038static int p54u_suspend(struct usb_interface *intf, pm_message_t message)
1039{
1040 return p54u_pre_reset(intf);
1041}
1042
1043#endif /* CONFIG_PM */
1044
Michael Wueff1a592007-09-25 18:11:01 -07001045static struct usb_driver p54u_driver = {
Christian Lamparter32ddf072008-08-08 21:17:37 +02001046 .name = "p54usb",
Michael Wueff1a592007-09-25 18:11:01 -07001047 .id_table = p54u_table,
1048 .probe = p54u_probe,
1049 .disconnect = p54u_disconnect,
Christian Lamparter69828692008-12-26 19:08:31 +01001050 .pre_reset = p54u_pre_reset,
1051 .post_reset = p54u_post_reset,
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +02001052#ifdef CONFIG_PM
1053 .suspend = p54u_suspend,
1054 .resume = p54u_resume,
1055 .reset_resume = p54u_resume,
1056#endif /* CONFIG_PM */
Christian Lamparterfbf95292009-03-05 21:29:51 +01001057 .soft_unbind = 1,
Michael Wueff1a592007-09-25 18:11:01 -07001058};
1059
1060static int __init p54u_init(void)
1061{
1062 return usb_register(&p54u_driver);
1063}
1064
1065static void __exit p54u_exit(void)
1066{
1067 usb_deregister(&p54u_driver);
1068}
1069
1070module_init(p54u_init);
1071module_exit(p54u_exit);