blob: 610aafe207a32a5ea2087194d4e14bc642b36870 [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>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090018#include <linux/slab.h>
Michael Wueff1a592007-09-25 18:11:01 -070019#include <linux/firmware.h>
20#include <linux/etherdevice.h>
21#include <linux/delay.h>
22#include <linux/crc32.h>
23#include <net/mac80211.h>
24
25#include "p54.h"
Christian Lamparterd8c92102009-06-23 10:39:45 -050026#include "lmac.h"
Michael Wueff1a592007-09-25 18:11:01 -070027#include "p54usb.h"
28
29MODULE_AUTHOR("Michael Wu <flamingice@sourmilk.net>");
30MODULE_DESCRIPTION("Prism54 USB wireless driver");
31MODULE_LICENSE("GPL");
32MODULE_ALIAS("prism54usb");
Christian Lamparter9a8675d2008-10-18 23:04:15 +020033MODULE_FIRMWARE("isl3886usb");
34MODULE_FIRMWARE("isl3887usb");
Michael Wueff1a592007-09-25 18:11:01 -070035
Christian Lamparter1a927952010-10-01 22:01:24 +020036/*
37 * Note:
38 *
39 * Always update our wiki's device list (located at:
40 * http://wireless.kernel.org/en/users/Drivers/p54/devices ),
41 * whenever you add a new device.
42 */
43
Michael Wueff1a592007-09-25 18:11:01 -070044static struct usb_device_id p54u_table[] __devinitdata = {
45 /* Version 1 devices (pci chip + net2280) */
Christian Lamparter16cad7f2010-12-11 12:19:48 +010046 {USB_DEVICE(0x0411, 0x0050)}, /* Buffalo WLI2-USB2-G54 */
Christian Lamparter1a927952010-10-01 22:01:24 +020047 {USB_DEVICE(0x045e, 0x00c2)}, /* Microsoft MN-710 */
Michael Wueff1a592007-09-25 18:11:01 -070048 {USB_DEVICE(0x0506, 0x0a11)}, /* 3COM 3CRWE254G72 */
Hans de Goede05a9a162010-03-17 14:37:16 +010049 {USB_DEVICE(0x06b9, 0x0120)}, /* Thomson SpeedTouch 120g */
Michael Wueff1a592007-09-25 18:11:01 -070050 {USB_DEVICE(0x0707, 0xee06)}, /* SMC 2862W-G */
Shimada Hirofumi15a69a82010-02-14 04:16:16 +090051 {USB_DEVICE(0x07aa, 0x001c)}, /* Corega CG-WLUSB2GT */
Michael Wueff1a592007-09-25 18:11:01 -070052 {USB_DEVICE(0x083a, 0x4501)}, /* Accton 802.11g WN4501 USB */
53 {USB_DEVICE(0x083a, 0x4502)}, /* Siemens Gigaset USB Adapter */
Ivo Couckuyt1a175822008-02-20 14:58:00 -050054 {USB_DEVICE(0x083a, 0x5501)}, /* Phillips CPWUA054 */
Michael Wueff1a592007-09-25 18:11:01 -070055 {USB_DEVICE(0x0846, 0x4200)}, /* Netgear WG121 */
56 {USB_DEVICE(0x0846, 0x4210)}, /* Netgear WG121 the second ? */
57 {USB_DEVICE(0x0846, 0x4220)}, /* Netgear WG111 */
Christian Lamparterec366eb2008-10-13 23:41:53 +020058 {USB_DEVICE(0x09aa, 0x1000)}, /* Spinnaker Proto board */
Michael Wueff1a592007-09-25 18:11:01 -070059 {USB_DEVICE(0x0cde, 0x0006)}, /* Medion 40900, Roper Europe */
Christian Lamparter16cad7f2010-12-11 12:19:48 +010060 {USB_DEVICE(0x0db0, 0x6826)}, /* MSI UB54G (MS-6826) */
Christian Lamparter1a927952010-10-01 22:01:24 +020061 {USB_DEVICE(0x107b, 0x55f2)}, /* Gateway WGU-210 (Gemtek) */
Michael Wueff1a592007-09-25 18:11:01 -070062 {USB_DEVICE(0x124a, 0x4023)}, /* Shuttle PN15, Airvast WM168g, IOGear GWU513 */
Christian Lamparter16cad7f2010-12-11 12:19:48 +010063 {USB_DEVICE(0x1435, 0x0210)}, /* Inventel UR054G */
Christian Lamparter1a927952010-10-01 22:01:24 +020064 {USB_DEVICE(0x1630, 0x0005)}, /* 2Wire 802.11g USB (v1) / Z-Com */
Christian Lamparter16cad7f2010-12-11 12:19:48 +010065 {USB_DEVICE(0x182d, 0x096b)}, /* Sitecom WL-107 */
Michael Wueff1a592007-09-25 18:11:01 -070066 {USB_DEVICE(0x1915, 0x2234)}, /* Linksys WUSB54G OEM */
67 {USB_DEVICE(0x1915, 0x2235)}, /* Linksys WUSB54G Portable OEM */
68 {USB_DEVICE(0x2001, 0x3701)}, /* DLink DWL-G120 Spinnaker */
69 {USB_DEVICE(0x2001, 0x3703)}, /* DLink DWL-G122 */
70 {USB_DEVICE(0x5041, 0x2234)}, /* Linksys WUSB54G */
71 {USB_DEVICE(0x5041, 0x2235)}, /* Linksys WUSB54G Portable */
72
73 /* Version 2 devices (3887) */
Felix Homann45460022008-05-29 00:36:45 -070074 {USB_DEVICE(0x0471, 0x1230)}, /* Philips CPWUA054/00 */
Michael Wueff1a592007-09-25 18:11:01 -070075 {USB_DEVICE(0x050d, 0x7050)}, /* Belkin F5D7050 ver 1000 */
76 {USB_DEVICE(0x0572, 0x2000)}, /* Cohiba Proto board */
77 {USB_DEVICE(0x0572, 0x2002)}, /* Cohiba Proto board */
Christian Lamparter1a927952010-10-01 22:01:24 +020078 {USB_DEVICE(0x06a9, 0x000e)}, /* Westell 802.11g USB (A90-211WG-01) */
Michiel878e6a42009-01-04 17:22:28 -060079 {USB_DEVICE(0x06b9, 0x0121)}, /* Thomson SpeedTouch 121g */
Michael Wueff1a592007-09-25 18:11:01 -070080 {USB_DEVICE(0x0707, 0xee13)}, /* SMC 2862W-G version 2 */
81 {USB_DEVICE(0x083a, 0x4521)}, /* Siemens Gigaset USB Adapter 54 version 2 */
Jean-François Moine5b9a9192010-02-17 10:59:31 -060082 {USB_DEVICE(0x083a, 0xf503)}, /* Accton FD7050E ver 1010ec */
Michael Wueff1a592007-09-25 18:11:01 -070083 {USB_DEVICE(0x0846, 0x4240)}, /* Netgear WG111 (v2) */
84 {USB_DEVICE(0x0915, 0x2000)}, /* Cohiba Proto board */
85 {USB_DEVICE(0x0915, 0x2002)}, /* Cohiba Proto board */
86 {USB_DEVICE(0x0baf, 0x0118)}, /* U.S. Robotics U5 802.11g Adapter*/
87 {USB_DEVICE(0x0bf8, 0x1009)}, /* FUJITSU E-5400 USB D1700*/
Leann Ogasawara7484bdc2010-06-15 14:01:51 -070088 /* {USB_DEVICE(0x0cde, 0x0006)}, * Medion MD40900 already listed above,
89 * just noting it here for clarity */
Michael Wueff1a592007-09-25 18:11:01 -070090 {USB_DEVICE(0x0cde, 0x0008)}, /* Sagem XG703A */
Christian Lamparterf7f71172009-09-14 23:08:43 +020091 {USB_DEVICE(0x0cde, 0x0015)}, /* Zcomax XG-705A */
Michael Wueff1a592007-09-25 18:11:01 -070092 {USB_DEVICE(0x0d8e, 0x3762)}, /* DLink DWL-G120 Cohiba */
Jan Slupski43557e12008-03-10 22:41:18 -070093 {USB_DEVICE(0x124a, 0x4025)}, /* IOGear GWU513 (GW3887IK chip) */
Christian Lamparterec366eb2008-10-13 23:41:53 +020094 {USB_DEVICE(0x1260, 0xee22)}, /* SMC 2862W-G version 2 */
John W. Linville387e1002008-02-20 15:06:02 -050095 {USB_DEVICE(0x13b1, 0x000a)}, /* Linksys WUSB54G ver 2 */
Martti Huttunenc1098102007-10-04 00:06:00 -040096 {USB_DEVICE(0x13B1, 0x000C)}, /* Linksys WUSB54AG */
Christian Lampartere3062402009-03-29 22:50:28 +020097 {USB_DEVICE(0x1413, 0x5400)}, /* Telsey 802.11g USB2.0 Adapter */
Michael Wueff1a592007-09-25 18:11:01 -070098 {USB_DEVICE(0x1435, 0x0427)}, /* Inventel UR054G */
Christian Lamparter1a927952010-10-01 22:01:24 +020099 {USB_DEVICE(0x1668, 0x1050)}, /* Actiontec 802UIG-1 */
Michael Wueff1a592007-09-25 18:11:01 -0700100 {USB_DEVICE(0x2001, 0x3704)}, /* DLink DWL-G122 rev A2 */
Christian Lamparter16cad7f2010-12-11 12:19:48 +0100101 {USB_DEVICE(0x2001, 0x3705)}, /* D-Link DWL-G120 rev C1 */
Jason Dravet0f666a02010-06-05 15:08:29 -0500102 {USB_DEVICE(0x413c, 0x5513)}, /* Dell WLA3310 USB Wireless Adapter */
Michael Wueff1a592007-09-25 18:11:01 -0700103 {USB_DEVICE(0x413c, 0x8102)}, /* Spinnaker DUT */
104 {USB_DEVICE(0x413c, 0x8104)}, /* Cohiba Proto board */
105 {}
106};
107
108MODULE_DEVICE_TABLE(usb, p54u_table);
109
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200110static const struct {
111 u32 intf;
112 enum p54u_hw_type type;
Samuel Ortiz328d84f2009-05-27 10:12:51 +0200113 const char *fw;
114 const char *fw_legacy;
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200115 char hw[20];
116} p54u_fwlist[__NUM_P54U_HWTYPES] = {
117 {
118 .type = P54U_NET2280,
119 .intf = FW_LM86,
120 .fw = "isl3886usb",
121 .fw_legacy = "isl3890usb",
122 .hw = "ISL3886 + net2280",
123 },
124 {
125 .type = P54U_3887,
126 .intf = FW_LM87,
127 .fw = "isl3887usb",
128 .fw_legacy = "isl3887usb_bare",
129 .hw = "ISL3887",
130 },
131};
132
Michael Wueff1a592007-09-25 18:11:01 -0700133static void p54u_rx_cb(struct urb *urb)
134{
135 struct sk_buff *skb = (struct sk_buff *) urb->context;
136 struct p54u_rx_info *info = (struct p54u_rx_info *)skb->cb;
137 struct ieee80211_hw *dev = info->dev;
138 struct p54u_priv *priv = dev->priv;
139
Christian Lamparterdd397dc2008-12-09 15:14:37 +0100140 skb_unlink(skb, &priv->rx_queue);
141
Michael Wueff1a592007-09-25 18:11:01 -0700142 if (unlikely(urb->status)) {
Christian Lamparterdd397dc2008-12-09 15:14:37 +0100143 dev_kfree_skb_irq(skb);
Michael Wueff1a592007-09-25 18:11:01 -0700144 return;
145 }
146
Michael Wueff1a592007-09-25 18:11:01 -0700147 skb_put(skb, urb->actual_length);
Christian Lamparter2b808482008-09-04 12:29:38 +0200148
149 if (priv->hw_type == P54U_NET2280)
150 skb_pull(skb, priv->common.tx_hdr_len);
151 if (priv->common.fw_interface == FW_LM87) {
152 skb_pull(skb, 4);
153 skb_put(skb, 4);
154 }
Michael Wueff1a592007-09-25 18:11:01 -0700155
156 if (p54_rx(dev, skb)) {
Christian Lamparter4e416a62008-09-01 22:48:41 +0200157 skb = dev_alloc_skb(priv->common.rx_mtu + 32);
Michael Wueff1a592007-09-25 18:11:01 -0700158 if (unlikely(!skb)) {
Michael Wueff1a592007-09-25 18:11:01 -0700159 /* TODO check rx queue length and refill *somewhere* */
160 return;
161 }
162
163 info = (struct p54u_rx_info *) skb->cb;
164 info->urb = urb;
165 info->dev = dev;
166 urb->transfer_buffer = skb_tail_pointer(skb);
167 urb->context = skb;
Michael Wueff1a592007-09-25 18:11:01 -0700168 } else {
Christian Lamparter2b808482008-09-04 12:29:38 +0200169 if (priv->hw_type == P54U_NET2280)
170 skb_push(skb, priv->common.tx_hdr_len);
171 if (priv->common.fw_interface == FW_LM87) {
172 skb_push(skb, 4);
173 skb_put(skb, 4);
174 }
Christian Lamparterd47c3ce2008-08-13 23:41:48 +0200175 skb_reset_tail_pointer(skb);
Michael Wueff1a592007-09-25 18:11:01 -0700176 skb_trim(skb, 0);
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200177 urb->transfer_buffer = skb_tail_pointer(skb);
Michael Wueff1a592007-09-25 18:11:01 -0700178 }
Christian Lamparterdd397dc2008-12-09 15:14:37 +0100179 skb_queue_tail(&priv->rx_queue, skb);
180 usb_anchor_urb(urb, &priv->submitted);
181 if (usb_submit_urb(urb, GFP_ATOMIC)) {
182 skb_unlink(skb, &priv->rx_queue);
183 usb_unanchor_urb(urb);
184 dev_kfree_skb_irq(skb);
185 }
Michael Wueff1a592007-09-25 18:11:01 -0700186}
187
Christian Lamparter0a5ec962008-12-14 15:05:42 +0100188static void p54u_tx_cb(struct urb *urb)
Christian Lamparterb92f30d2008-10-15 04:07:16 +0200189{
190 struct sk_buff *skb = urb->context;
191 struct ieee80211_hw *dev = (struct ieee80211_hw *)
192 usb_get_intfdata(usb_ifnum_to_if(urb->dev, 0));
193
Christian Lampartere2fe1542009-01-20 00:27:57 +0100194 p54_free_skb(dev, skb);
Christian Lamparterdd397dc2008-12-09 15:14:37 +0100195}
196
197static void p54u_tx_dummy_cb(struct urb *urb) { }
198
199static void p54u_free_urbs(struct ieee80211_hw *dev)
200{
201 struct p54u_priv *priv = dev->priv;
202 usb_kill_anchored_urbs(&priv->submitted);
Christian Lamparterb92f30d2008-10-15 04:07:16 +0200203}
204
Michael Wueff1a592007-09-25 18:11:01 -0700205static int p54u_init_urbs(struct ieee80211_hw *dev)
206{
207 struct p54u_priv *priv = dev->priv;
Christian Lamparterdd397dc2008-12-09 15:14:37 +0100208 struct urb *entry = NULL;
Michael Wueff1a592007-09-25 18:11:01 -0700209 struct sk_buff *skb;
210 struct p54u_rx_info *info;
Christian Lamparterdd397dc2008-12-09 15:14:37 +0100211 int ret = 0;
Michael Wueff1a592007-09-25 18:11:01 -0700212
213 while (skb_queue_len(&priv->rx_queue) < 32) {
Christian Lamparter4e416a62008-09-01 22:48:41 +0200214 skb = __dev_alloc_skb(priv->common.rx_mtu + 32, GFP_KERNEL);
Christian Lamparterdd397dc2008-12-09 15:14:37 +0100215 if (!skb) {
216 ret = -ENOMEM;
217 goto err;
218 }
Michael Wueff1a592007-09-25 18:11:01 -0700219 entry = usb_alloc_urb(0, GFP_KERNEL);
220 if (!entry) {
Christian Lamparterdd397dc2008-12-09 15:14:37 +0100221 ret = -ENOMEM;
222 goto err;
Michael Wueff1a592007-09-25 18:11:01 -0700223 }
Christian Lamparterdd397dc2008-12-09 15:14:37 +0100224
Christian Lamparter4e416a62008-09-01 22:48:41 +0200225 usb_fill_bulk_urb(entry, priv->udev,
226 usb_rcvbulkpipe(priv->udev, P54U_PIPE_DATA),
227 skb_tail_pointer(skb),
228 priv->common.rx_mtu + 32, p54u_rx_cb, skb);
Michael Wueff1a592007-09-25 18:11:01 -0700229 info = (struct p54u_rx_info *) skb->cb;
230 info->urb = entry;
231 info->dev = dev;
232 skb_queue_tail(&priv->rx_queue, skb);
Christian Lamparterdd397dc2008-12-09 15:14:37 +0100233
234 usb_anchor_urb(entry, &priv->submitted);
235 ret = usb_submit_urb(entry, GFP_KERNEL);
236 if (ret) {
237 skb_unlink(skb, &priv->rx_queue);
238 usb_unanchor_urb(entry);
239 goto err;
240 }
241 usb_free_urb(entry);
242 entry = NULL;
Michael Wueff1a592007-09-25 18:11:01 -0700243 }
244
245 return 0;
Michael Wueff1a592007-09-25 18:11:01 -0700246
Christian Lamparterdd397dc2008-12-09 15:14:37 +0100247 err:
248 usb_free_urb(entry);
249 kfree_skb(skb);
250 p54u_free_urbs(dev);
251 return ret;
Michael Wueff1a592007-09-25 18:11:01 -0700252}
253
Johannes Bergc9127652008-12-01 18:19:36 +0100254static __le32 p54u_lm87_chksum(const __le32 *data, size_t length)
Christian Lamparter2b808482008-09-04 12:29:38 +0200255{
Larry Finger1f1c0e32008-09-25 14:54:28 -0500256 u32 chk = 0;
Christian Lamparter2b808482008-09-04 12:29:38 +0200257
258 length >>= 2;
259 while (length--) {
Johannes Bergc9127652008-12-01 18:19:36 +0100260 chk ^= le32_to_cpu(*data++);
Christian Lamparter2b808482008-09-04 12:29:38 +0200261 chk = (chk >> 5) ^ (chk << 3);
262 }
263
Larry Finger1f1c0e32008-09-25 14:54:28 -0500264 return cpu_to_le32(chk);
Christian Lamparter2b808482008-09-04 12:29:38 +0200265}
266
Christian Lamparter0a5ec962008-12-14 15:05:42 +0100267static void p54u_tx_lm87(struct ieee80211_hw *dev, struct sk_buff *skb)
Christian Lamparter2b808482008-09-04 12:29:38 +0200268{
269 struct p54u_priv *priv = dev->priv;
270 struct urb *data_urb;
Christian Lampartere2fe1542009-01-20 00:27:57 +0100271 struct lm87_tx_hdr *hdr = (void *)skb->data - sizeof(*hdr);
Christian Lamparter2b808482008-09-04 12:29:38 +0200272
273 data_urb = usb_alloc_urb(0, GFP_ATOMIC);
Christian Lamparter6d541a62009-07-06 15:17:56 +0200274 if (!data_urb) {
275 p54_free_skb(dev, skb);
Christian Lamparter2b808482008-09-04 12:29:38 +0200276 return;
Christian Lamparter6d541a62009-07-06 15:17:56 +0200277 }
Christian Lamparter2b808482008-09-04 12:29:38 +0200278
Christian Lampartere2fe1542009-01-20 00:27:57 +0100279 hdr->chksum = p54u_lm87_chksum((__le32 *)skb->data, skb->len);
280 hdr->device_addr = ((struct p54_hdr *)skb->data)->req_id;
Christian Lamparter2b808482008-09-04 12:29:38 +0200281
282 usb_fill_bulk_urb(data_urb, priv->udev,
Christian Lamparterb92f30d2008-10-15 04:07:16 +0200283 usb_sndbulkpipe(priv->udev, P54U_PIPE_DATA),
Christian Lampartere2fe1542009-01-20 00:27:57 +0100284 hdr, skb->len + sizeof(*hdr), FREE_AFTER_TX(skb) ?
285 p54u_tx_cb : p54u_tx_dummy_cb, skb);
Christian Lamparter00627f22008-12-20 02:21:56 +0100286 data_urb->transfer_flags |= URB_ZERO_PACKET;
Christian Lamparter2b808482008-09-04 12:29:38 +0200287
Christian Lamparterdd397dc2008-12-09 15:14:37 +0100288 usb_anchor_urb(data_urb, &priv->submitted);
289 if (usb_submit_urb(data_urb, GFP_ATOMIC)) {
290 usb_unanchor_urb(data_urb);
Christian Lamparterdd397dc2008-12-09 15:14:37 +0100291 p54_free_skb(dev, skb);
292 }
293 usb_free_urb(data_urb);
Christian Lamparter2b808482008-09-04 12:29:38 +0200294}
295
Christian Lamparter0a5ec962008-12-14 15:05:42 +0100296static void p54u_tx_net2280(struct ieee80211_hw *dev, struct sk_buff *skb)
Michael Wueff1a592007-09-25 18:11:01 -0700297{
298 struct p54u_priv *priv = dev->priv;
Christian Lamparter6d541a62009-07-06 15:17:56 +0200299 struct urb *int_urb = NULL, *data_urb = NULL;
Christian Lampartere2fe1542009-01-20 00:27:57 +0100300 struct net2280_tx_hdr *hdr = (void *)skb->data - sizeof(*hdr);
Christian Lamparter6d541a62009-07-06 15:17:56 +0200301 struct net2280_reg_write *reg = NULL;
302 int err = -ENOMEM;
Michael Wueff1a592007-09-25 18:11:01 -0700303
304 reg = kmalloc(sizeof(*reg), GFP_ATOMIC);
305 if (!reg)
Christian Lamparter6d541a62009-07-06 15:17:56 +0200306 goto out;
Michael Wueff1a592007-09-25 18:11:01 -0700307
308 int_urb = usb_alloc_urb(0, GFP_ATOMIC);
Christian Lamparter6d541a62009-07-06 15:17:56 +0200309 if (!int_urb)
310 goto out;
Michael Wueff1a592007-09-25 18:11:01 -0700311
312 data_urb = usb_alloc_urb(0, GFP_ATOMIC);
Christian Lamparter6d541a62009-07-06 15:17:56 +0200313 if (!data_urb)
314 goto out;
Michael Wueff1a592007-09-25 18:11:01 -0700315
316 reg->port = cpu_to_le16(NET2280_DEV_U32);
317 reg->addr = cpu_to_le32(P54U_DEV_BASE);
318 reg->val = cpu_to_le32(ISL38XX_DEV_INT_DATA);
319
Michael Wueff1a592007-09-25 18:11:01 -0700320 memset(hdr, 0, sizeof(*hdr));
Christian Lampartere2fe1542009-01-20 00:27:57 +0100321 hdr->len = cpu_to_le16(skb->len);
322 hdr->device_addr = ((struct p54_hdr *) skb->data)->req_id;
Michael Wueff1a592007-09-25 18:11:01 -0700323
324 usb_fill_bulk_urb(int_urb, priv->udev,
325 usb_sndbulkpipe(priv->udev, P54U_PIPE_DEV), reg, sizeof(*reg),
Christian Lamparterdd397dc2008-12-09 15:14:37 +0100326 p54u_tx_dummy_cb, dev);
327
328 /*
Christian Lamparter6d541a62009-07-06 15:17:56 +0200329 * URB_FREE_BUFFER triggers a code path in the USB subsystem that will
330 * free what is inside the transfer_buffer after the last reference to
331 * the int_urb is dropped.
Christian Lamparterdd397dc2008-12-09 15:14:37 +0100332 */
Christian Lamparterb4068a82009-01-20 23:11:21 +0100333 int_urb->transfer_flags |= URB_FREE_BUFFER | URB_ZERO_PACKET;
Christian Lamparter6d541a62009-07-06 15:17:56 +0200334 reg = NULL;
Michael Wueff1a592007-09-25 18:11:01 -0700335
336 usb_fill_bulk_urb(data_urb, priv->udev,
Christian Lamparterb92f30d2008-10-15 04:07:16 +0200337 usb_sndbulkpipe(priv->udev, P54U_PIPE_DATA),
Christian Lampartere2fe1542009-01-20 00:27:57 +0100338 hdr, skb->len + sizeof(*hdr), FREE_AFTER_TX(skb) ?
339 p54u_tx_cb : p54u_tx_dummy_cb, skb);
Christian Lamparterb4068a82009-01-20 23:11:21 +0100340 data_urb->transfer_flags |= URB_ZERO_PACKET;
Christian Lamparterdd397dc2008-12-09 15:14:37 +0100341
342 usb_anchor_urb(int_urb, &priv->submitted);
343 err = usb_submit_urb(int_urb, GFP_ATOMIC);
344 if (err) {
345 usb_unanchor_urb(int_urb);
346 goto out;
347 }
348
349 usb_anchor_urb(data_urb, &priv->submitted);
350 err = usb_submit_urb(data_urb, GFP_ATOMIC);
351 if (err) {
352 usb_unanchor_urb(data_urb);
353 goto out;
354 }
Christian Lamparter6d541a62009-07-06 15:17:56 +0200355out:
Christian Lamparterdd397dc2008-12-09 15:14:37 +0100356 usb_free_urb(int_urb);
357 usb_free_urb(data_urb);
358
359 if (err) {
Christian Lamparter6d541a62009-07-06 15:17:56 +0200360 kfree(reg);
Christian Lamparterdd397dc2008-12-09 15:14:37 +0100361 p54_free_skb(dev, skb);
362 }
Michael Wueff1a592007-09-25 18:11:01 -0700363}
364
365static int p54u_write(struct p54u_priv *priv,
366 struct net2280_reg_write *buf,
367 enum net2280_op_type type,
368 __le32 addr, __le32 val)
369{
370 unsigned int ep;
371 int alen;
372
373 if (type & 0x0800)
374 ep = usb_sndbulkpipe(priv->udev, P54U_PIPE_DEV);
375 else
376 ep = usb_sndbulkpipe(priv->udev, P54U_PIPE_BRG);
377
378 buf->port = cpu_to_le16(type);
379 buf->addr = addr;
380 buf->val = val;
381
382 return usb_bulk_msg(priv->udev, ep, buf, sizeof(*buf), &alen, 1000);
383}
384
385static int p54u_read(struct p54u_priv *priv, void *buf,
386 enum net2280_op_type type,
387 __le32 addr, __le32 *val)
388{
389 struct net2280_reg_read *read = buf;
390 __le32 *reg = buf;
391 unsigned int ep;
392 int alen, err;
393
394 if (type & 0x0800)
395 ep = P54U_PIPE_DEV;
396 else
397 ep = P54U_PIPE_BRG;
398
399 read->port = cpu_to_le16(type);
400 read->addr = addr;
401
402 err = usb_bulk_msg(priv->udev, usb_sndbulkpipe(priv->udev, ep),
403 read, sizeof(*read), &alen, 1000);
404 if (err)
405 return err;
406
407 err = usb_bulk_msg(priv->udev, usb_rcvbulkpipe(priv->udev, ep),
408 reg, sizeof(*reg), &alen, 1000);
409 if (err)
410 return err;
411
412 *val = *reg;
413 return 0;
414}
415
416static int p54u_bulk_msg(struct p54u_priv *priv, unsigned int ep,
417 void *data, size_t len)
418{
419 int alen;
420 return usb_bulk_msg(priv->udev, usb_sndbulkpipe(priv->udev, ep),
421 data, len, &alen, 2000);
422}
423
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200424static int p54u_device_reset(struct ieee80211_hw *dev)
Christian Lamparter69828692008-12-26 19:08:31 +0100425{
426 struct p54u_priv *priv = dev->priv;
Christian Lamparterc88a7682009-01-16 20:24:31 +0100427 int ret, lock = (priv->intf->condition != USB_INTERFACE_BINDING);
Christian Lamparter69828692008-12-26 19:08:31 +0100428
Christian Lamparterc88a7682009-01-16 20:24:31 +0100429 if (lock) {
430 ret = usb_lock_device_for_reset(priv->udev, priv->intf);
431 if (ret < 0) {
432 dev_err(&priv->udev->dev, "(p54usb) unable to lock "
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200433 "device for reset (%d)!\n", ret);
Christian Lamparterc88a7682009-01-16 20:24:31 +0100434 return ret;
435 }
Christian Lamparter69828692008-12-26 19:08:31 +0100436 }
437
438 ret = usb_reset_device(priv->udev);
439 if (lock)
440 usb_unlock_device(priv->udev);
441
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200442 if (ret)
Christian Lamparter69828692008-12-26 19:08:31 +0100443 dev_err(&priv->udev->dev, "(p54usb) unable to reset "
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200444 "device (%d)!\n", ret);
445
446 return ret;
447}
448
449static const char p54u_romboot_3887[] = "~~~~";
450static int p54u_firmware_reset_3887(struct ieee80211_hw *dev)
451{
452 struct p54u_priv *priv = dev->priv;
Larry Finger21d6c272009-11-11 18:02:29 -0600453 u8 *buf;
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200454 int ret;
Christian Lamparter69828692008-12-26 19:08:31 +0100455
Julia Lawall27b81bb2010-05-15 23:22:55 +0200456 buf = kmemdup(p54u_romboot_3887, 4, GFP_KERNEL);
Larry Finger21d6c272009-11-11 18:02:29 -0600457 if (!buf)
458 return -ENOMEM;
Christian Lamparter69828692008-12-26 19:08:31 +0100459 ret = p54u_bulk_msg(priv, P54U_PIPE_DATA,
Larry Finger21d6c272009-11-11 18:02:29 -0600460 buf, 4);
461 kfree(buf);
Christian Lamparter69828692008-12-26 19:08:31 +0100462 if (ret)
463 dev_err(&priv->udev->dev, "(p54usb) unable to jump to "
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200464 "boot ROM (%d)!\n", ret);
Christian Lamparter69828692008-12-26 19:08:31 +0100465
466 return ret;
467}
468
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200469static const char p54u_firmware_upload_3887[] = "<\r";
Michael Wueff1a592007-09-25 18:11:01 -0700470static int p54u_upload_firmware_3887(struct ieee80211_hw *dev)
471{
Michael Wueff1a592007-09-25 18:11:01 -0700472 struct p54u_priv *priv = dev->priv;
Michael Wueff1a592007-09-25 18:11:01 -0700473 int err, alen;
474 u8 carry = 0;
David Woodhouse8b72eb42008-05-24 00:08:55 +0100475 u8 *buf, *tmp;
476 const u8 *data;
Michael Wueff1a592007-09-25 18:11:01 -0700477 unsigned int left, remains, block_size;
478 struct x2_header *hdr;
479 unsigned long timeout;
480
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200481 err = p54u_firmware_reset_3887(dev);
482 if (err)
483 return err;
484
Michael Wueff1a592007-09-25 18:11:01 -0700485 tmp = buf = kmalloc(P54U_FW_BLOCK, GFP_KERNEL);
486 if (!buf) {
Christian Lamparter02e37ba2008-11-29 22:39:08 +0100487 dev_err(&priv->udev->dev, "(p54usb) cannot allocate firmware"
488 "upload buffer!\n");
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200489 return -ENOMEM;
Michael Wueff1a592007-09-25 18:11:01 -0700490 }
491
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200492 left = block_size = min((size_t)P54U_FW_BLOCK, priv->fw->size);
Christian Lamparter69828692008-12-26 19:08:31 +0100493 strcpy(buf, p54u_firmware_upload_3887);
494 left -= strlen(p54u_firmware_upload_3887);
495 tmp += strlen(p54u_firmware_upload_3887);
Michael Wueff1a592007-09-25 18:11:01 -0700496
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200497 data = priv->fw->data;
498 remains = priv->fw->size;
Michael Wueff1a592007-09-25 18:11:01 -0700499
Christian Lamparter69828692008-12-26 19:08:31 +0100500 hdr = (struct x2_header *)(buf + strlen(p54u_firmware_upload_3887));
Michael Wueff1a592007-09-25 18:11:01 -0700501 memcpy(hdr->signature, X2_SIGNATURE, X2_SIGNATURE_SIZE);
502 hdr->fw_load_addr = cpu_to_le32(ISL38XX_DEV_FIRMWARE_ADDR);
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200503 hdr->fw_length = cpu_to_le32(priv->fw->size);
Michael Wueff1a592007-09-25 18:11:01 -0700504 hdr->crc = cpu_to_le32(~crc32_le(~0, (void *)&hdr->fw_load_addr,
505 sizeof(u32)*2));
506 left -= sizeof(*hdr);
507 tmp += sizeof(*hdr);
508
509 while (remains) {
510 while (left--) {
511 if (carry) {
512 *tmp++ = carry;
513 carry = 0;
514 remains--;
515 continue;
516 }
517 switch (*data) {
518 case '~':
519 *tmp++ = '}';
520 carry = '^';
521 break;
522 case '}':
523 *tmp++ = '}';
524 carry = ']';
525 break;
526 default:
527 *tmp++ = *data;
528 remains--;
529 break;
530 }
531 data++;
532 }
533
534 err = p54u_bulk_msg(priv, P54U_PIPE_DATA, buf, block_size);
535 if (err) {
Christian Lamparter02e37ba2008-11-29 22:39:08 +0100536 dev_err(&priv->udev->dev, "(p54usb) firmware "
537 "upload failed!\n");
Michael Wueff1a592007-09-25 18:11:01 -0700538 goto err_upload_failed;
539 }
540
541 tmp = buf;
542 left = block_size = min((unsigned int)P54U_FW_BLOCK, remains);
543 }
544
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200545 *((__le32 *)buf) = cpu_to_le32(~crc32_le(~0, priv->fw->data,
546 priv->fw->size));
Michael Wueff1a592007-09-25 18:11:01 -0700547 err = p54u_bulk_msg(priv, P54U_PIPE_DATA, buf, sizeof(u32));
548 if (err) {
Christian Lamparter02e37ba2008-11-29 22:39:08 +0100549 dev_err(&priv->udev->dev, "(p54usb) firmware upload failed!\n");
Michael Wueff1a592007-09-25 18:11:01 -0700550 goto err_upload_failed;
551 }
Michael Wueff1a592007-09-25 18:11:01 -0700552 timeout = jiffies + msecs_to_jiffies(1000);
553 while (!(err = usb_bulk_msg(priv->udev,
554 usb_rcvbulkpipe(priv->udev, P54U_PIPE_DATA), buf, 128, &alen, 1000))) {
555 if (alen > 2 && !memcmp(buf, "OK", 2))
556 break;
557
558 if (alen > 5 && !memcmp(buf, "ERROR", 5)) {
Michael Wueff1a592007-09-25 18:11:01 -0700559 err = -EINVAL;
560 break;
561 }
562
563 if (time_after(jiffies, timeout)) {
Christian Lamparter02e37ba2008-11-29 22:39:08 +0100564 dev_err(&priv->udev->dev, "(p54usb) firmware boot "
565 "timed out!\n");
Michael Wueff1a592007-09-25 18:11:01 -0700566 err = -ETIMEDOUT;
567 break;
568 }
569 }
Christian Lamparter02e37ba2008-11-29 22:39:08 +0100570 if (err) {
571 dev_err(&priv->udev->dev, "(p54usb) firmware upload failed!\n");
Michael Wueff1a592007-09-25 18:11:01 -0700572 goto err_upload_failed;
Christian Lamparter02e37ba2008-11-29 22:39:08 +0100573 }
Michael Wueff1a592007-09-25 18:11:01 -0700574
575 buf[0] = 'g';
576 buf[1] = '\r';
577 err = p54u_bulk_msg(priv, P54U_PIPE_DATA, buf, 2);
578 if (err) {
Christian Lamparter02e37ba2008-11-29 22:39:08 +0100579 dev_err(&priv->udev->dev, "(p54usb) firmware boot failed!\n");
Michael Wueff1a592007-09-25 18:11:01 -0700580 goto err_upload_failed;
581 }
582
583 timeout = jiffies + msecs_to_jiffies(1000);
584 while (!(err = usb_bulk_msg(priv->udev,
585 usb_rcvbulkpipe(priv->udev, P54U_PIPE_DATA), buf, 128, &alen, 1000))) {
586 if (alen > 0 && buf[0] == 'g')
587 break;
588
589 if (time_after(jiffies, timeout)) {
590 err = -ETIMEDOUT;
591 break;
592 }
593 }
594 if (err)
595 goto err_upload_failed;
596
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200597err_upload_failed:
Michael Wueff1a592007-09-25 18:11:01 -0700598 kfree(buf);
Michael Wueff1a592007-09-25 18:11:01 -0700599 return err;
600}
601
602static int p54u_upload_firmware_net2280(struct ieee80211_hw *dev)
603{
604 struct p54u_priv *priv = dev->priv;
Michael Wueff1a592007-09-25 18:11:01 -0700605 const struct p54p_csr *devreg = (const struct p54p_csr *) P54U_DEV_BASE;
606 int err, alen;
607 void *buf;
608 __le32 reg;
609 unsigned int remains, offset;
David Woodhouse8b72eb42008-05-24 00:08:55 +0100610 const u8 *data;
Michael Wueff1a592007-09-25 18:11:01 -0700611
612 buf = kmalloc(512, GFP_KERNEL);
613 if (!buf) {
Christian Lamparter02e37ba2008-11-29 22:39:08 +0100614 dev_err(&priv->udev->dev, "(p54usb) firmware buffer "
615 "alloc failed!\n");
Michael Wueff1a592007-09-25 18:11:01 -0700616 return -ENOMEM;
617 }
618
Michael Wueff1a592007-09-25 18:11:01 -0700619#define P54U_WRITE(type, addr, data) \
620 do {\
621 err = p54u_write(priv, buf, type,\
622 cpu_to_le32((u32)(unsigned long)addr), data);\
623 if (err) \
624 goto fail;\
625 } while (0)
626
627#define P54U_READ(type, addr) \
628 do {\
629 err = p54u_read(priv, buf, type,\
630 cpu_to_le32((u32)(unsigned long)addr), &reg);\
631 if (err)\
632 goto fail;\
633 } while (0)
634
635 /* power down net2280 bridge */
636 P54U_READ(NET2280_BRG_U32, NET2280_GPIOCTL);
637 reg |= cpu_to_le32(P54U_BRG_POWER_DOWN);
638 reg &= cpu_to_le32(~P54U_BRG_POWER_UP);
639 P54U_WRITE(NET2280_BRG_U32, NET2280_GPIOCTL, reg);
640
641 mdelay(100);
642
643 /* power up bridge */
644 reg |= cpu_to_le32(P54U_BRG_POWER_UP);
645 reg &= cpu_to_le32(~P54U_BRG_POWER_DOWN);
646 P54U_WRITE(NET2280_BRG_U32, NET2280_GPIOCTL, reg);
647
648 mdelay(100);
649
650 P54U_WRITE(NET2280_BRG_U32, NET2280_DEVINIT,
651 cpu_to_le32(NET2280_CLK_30Mhz |
652 NET2280_PCI_ENABLE |
653 NET2280_PCI_SOFT_RESET));
654
655 mdelay(20);
656
657 P54U_WRITE(NET2280_BRG_CFG_U16, PCI_COMMAND,
658 cpu_to_le32(PCI_COMMAND_MEMORY |
659 PCI_COMMAND_MASTER));
660
661 P54U_WRITE(NET2280_BRG_CFG_U32, PCI_BASE_ADDRESS_0,
662 cpu_to_le32(NET2280_BASE));
663
664 P54U_READ(NET2280_BRG_CFG_U16, PCI_STATUS);
665 reg |= cpu_to_le32(PCI_STATUS_REC_MASTER_ABORT);
666 P54U_WRITE(NET2280_BRG_CFG_U16, PCI_STATUS, reg);
667
668 // TODO: we really need this?
669 P54U_READ(NET2280_BRG_U32, NET2280_RELNUM);
670
671 P54U_WRITE(NET2280_BRG_U32, NET2280_EPA_RSP,
672 cpu_to_le32(NET2280_CLEAR_NAK_OUT_PACKETS_MODE));
673 P54U_WRITE(NET2280_BRG_U32, NET2280_EPC_RSP,
674 cpu_to_le32(NET2280_CLEAR_NAK_OUT_PACKETS_MODE));
675
676 P54U_WRITE(NET2280_BRG_CFG_U32, PCI_BASE_ADDRESS_2,
677 cpu_to_le32(NET2280_BASE2));
678
679 /* finally done setting up the bridge */
680
681 P54U_WRITE(NET2280_DEV_CFG_U16, 0x10000 | PCI_COMMAND,
682 cpu_to_le32(PCI_COMMAND_MEMORY |
683 PCI_COMMAND_MASTER));
684
685 P54U_WRITE(NET2280_DEV_CFG_U16, 0x10000 | 0x40 /* TRDY timeout */, 0);
686 P54U_WRITE(NET2280_DEV_CFG_U32, 0x10000 | PCI_BASE_ADDRESS_0,
687 cpu_to_le32(P54U_DEV_BASE));
688
689 P54U_WRITE(NET2280_BRG_U32, NET2280_USBIRQENB1, 0);
690 P54U_WRITE(NET2280_BRG_U32, NET2280_IRQSTAT1,
691 cpu_to_le32(NET2280_PCI_INTA_INTERRUPT));
692
693 /* do romboot */
694 P54U_WRITE(NET2280_DEV_U32, &devreg->int_enable, 0);
695
696 P54U_READ(NET2280_DEV_U32, &devreg->ctrl_stat);
697 reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_RESET);
698 reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_RAMBOOT);
699 reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_CLKRUN);
700 P54U_WRITE(NET2280_DEV_U32, &devreg->ctrl_stat, reg);
701
702 mdelay(20);
703
704 reg |= cpu_to_le32(ISL38XX_CTRL_STAT_RESET);
705 P54U_WRITE(NET2280_DEV_U32, &devreg->ctrl_stat, reg);
706
707 mdelay(20);
708
709 reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_RESET);
710 P54U_WRITE(NET2280_DEV_U32, &devreg->ctrl_stat, reg);
711
712 mdelay(100);
713
714 P54U_READ(NET2280_DEV_U32, &devreg->int_ident);
715 P54U_WRITE(NET2280_DEV_U32, &devreg->int_ack, reg);
716
717 /* finally, we can upload firmware now! */
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200718 remains = priv->fw->size;
719 data = priv->fw->data;
Michael Wueff1a592007-09-25 18:11:01 -0700720 offset = ISL38XX_DEV_FIRMWARE_ADDR;
721
722 while (remains) {
723 unsigned int block_len = min(remains, (unsigned int)512);
724 memcpy(buf, data, block_len);
725
726 err = p54u_bulk_msg(priv, P54U_PIPE_DATA, buf, block_len);
727 if (err) {
Christian Lamparter02e37ba2008-11-29 22:39:08 +0100728 dev_err(&priv->udev->dev, "(p54usb) firmware block "
729 "upload failed\n");
Michael Wueff1a592007-09-25 18:11:01 -0700730 goto fail;
731 }
732
733 P54U_WRITE(NET2280_DEV_U32, &devreg->direct_mem_base,
734 cpu_to_le32(0xc0000f00));
735
736 P54U_WRITE(NET2280_DEV_U32,
737 0x0020 | (unsigned long)&devreg->direct_mem_win, 0);
738 P54U_WRITE(NET2280_DEV_U32,
739 0x0020 | (unsigned long)&devreg->direct_mem_win,
740 cpu_to_le32(1));
741
742 P54U_WRITE(NET2280_DEV_U32,
743 0x0024 | (unsigned long)&devreg->direct_mem_win,
744 cpu_to_le32(block_len));
745 P54U_WRITE(NET2280_DEV_U32,
746 0x0028 | (unsigned long)&devreg->direct_mem_win,
747 cpu_to_le32(offset));
748
749 P54U_WRITE(NET2280_DEV_U32, &devreg->dma_addr,
750 cpu_to_le32(NET2280_EPA_FIFO_PCI_ADDR));
751 P54U_WRITE(NET2280_DEV_U32, &devreg->dma_len,
752 cpu_to_le32(block_len >> 2));
753 P54U_WRITE(NET2280_DEV_U32, &devreg->dma_ctrl,
754 cpu_to_le32(ISL38XX_DMA_MASTER_CONTROL_TRIGGER));
755
756 mdelay(10);
757
758 P54U_READ(NET2280_DEV_U32,
759 0x002C | (unsigned long)&devreg->direct_mem_win);
760 if (!(reg & cpu_to_le32(ISL38XX_DMA_STATUS_DONE)) ||
761 !(reg & cpu_to_le32(ISL38XX_DMA_STATUS_READY))) {
Christian Lamparter02e37ba2008-11-29 22:39:08 +0100762 dev_err(&priv->udev->dev, "(p54usb) firmware DMA "
763 "transfer failed\n");
Michael Wueff1a592007-09-25 18:11:01 -0700764 goto fail;
765 }
766
767 P54U_WRITE(NET2280_BRG_U32, NET2280_EPA_STAT,
768 cpu_to_le32(NET2280_FIFO_FLUSH));
769
770 remains -= block_len;
771 data += block_len;
772 offset += block_len;
773 }
774
775 /* do ramboot */
776 P54U_READ(NET2280_DEV_U32, &devreg->ctrl_stat);
777 reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_RESET);
778 reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_CLKRUN);
779 reg |= cpu_to_le32(ISL38XX_CTRL_STAT_RAMBOOT);
780 P54U_WRITE(NET2280_DEV_U32, &devreg->ctrl_stat, reg);
781
782 mdelay(20);
783
784 reg |= cpu_to_le32(ISL38XX_CTRL_STAT_RESET);
785 P54U_WRITE(NET2280_DEV_U32, &devreg->ctrl_stat, reg);
786
787 reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_RESET);
788 P54U_WRITE(NET2280_DEV_U32, &devreg->ctrl_stat, reg);
789
790 mdelay(100);
791
792 P54U_READ(NET2280_DEV_U32, &devreg->int_ident);
793 P54U_WRITE(NET2280_DEV_U32, &devreg->int_ack, reg);
794
795 /* start up the firmware */
796 P54U_WRITE(NET2280_DEV_U32, &devreg->int_enable,
797 cpu_to_le32(ISL38XX_INT_IDENT_INIT));
798
799 P54U_WRITE(NET2280_BRG_U32, NET2280_IRQSTAT1,
800 cpu_to_le32(NET2280_PCI_INTA_INTERRUPT));
801
802 P54U_WRITE(NET2280_BRG_U32, NET2280_USBIRQENB1,
803 cpu_to_le32(NET2280_PCI_INTA_INTERRUPT_ENABLE |
804 NET2280_USB_INTERRUPT_ENABLE));
805
806 P54U_WRITE(NET2280_DEV_U32, &devreg->dev_int,
807 cpu_to_le32(ISL38XX_DEV_INT_RESET));
808
809 err = usb_interrupt_msg(priv->udev,
810 usb_rcvbulkpipe(priv->udev, P54U_PIPE_INT),
811 buf, sizeof(__le32), &alen, 1000);
812 if (err || alen != sizeof(__le32))
813 goto fail;
814
815 P54U_READ(NET2280_DEV_U32, &devreg->int_ident);
816 P54U_WRITE(NET2280_DEV_U32, &devreg->int_ack, reg);
817
818 if (!(reg & cpu_to_le32(ISL38XX_INT_IDENT_INIT)))
819 err = -EINVAL;
820
821 P54U_WRITE(NET2280_BRG_U32, NET2280_USBIRQENB1, 0);
822 P54U_WRITE(NET2280_BRG_U32, NET2280_IRQSTAT1,
823 cpu_to_le32(NET2280_PCI_INTA_INTERRUPT));
824
825#undef P54U_WRITE
826#undef P54U_READ
827
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200828fail:
Michael Wueff1a592007-09-25 18:11:01 -0700829 kfree(buf);
830 return err;
831}
832
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200833static int p54u_load_firmware(struct ieee80211_hw *dev)
834{
835 struct p54u_priv *priv = dev->priv;
836 int err, i;
837
838 BUILD_BUG_ON(ARRAY_SIZE(p54u_fwlist) != __NUM_P54U_HWTYPES);
839
840 for (i = 0; i < __NUM_P54U_HWTYPES; i++)
841 if (p54u_fwlist[i].type == priv->hw_type)
842 break;
843
844 if (i == __NUM_P54U_HWTYPES)
845 return -EOPNOTSUPP;
846
847 err = request_firmware(&priv->fw, p54u_fwlist[i].fw, &priv->udev->dev);
848 if (err) {
849 dev_err(&priv->udev->dev, "(p54usb) cannot load firmware %s "
850 "(%d)!\n", p54u_fwlist[i].fw, err);
851
852 err = request_firmware(&priv->fw, p54u_fwlist[i].fw_legacy,
853 &priv->udev->dev);
854 if (err)
855 return err;
856 }
857
858 err = p54_parse_firmware(dev, priv->fw);
859 if (err)
860 goto out;
861
862 if (priv->common.fw_interface != p54u_fwlist[i].intf) {
863 dev_err(&priv->udev->dev, "wrong firmware, please get "
864 "a firmware for \"%s\" and try again.\n",
865 p54u_fwlist[i].hw);
866 err = -EINVAL;
867 }
868
869out:
870 if (err)
871 release_firmware(priv->fw);
872
873 return err;
874}
875
Michael Wueff1a592007-09-25 18:11:01 -0700876static int p54u_open(struct ieee80211_hw *dev)
877{
878 struct p54u_priv *priv = dev->priv;
879 int err;
880
881 err = p54u_init_urbs(dev);
882 if (err) {
883 return err;
884 }
885
886 priv->common.open = p54u_init_urbs;
887
888 return 0;
889}
890
891static void p54u_stop(struct ieee80211_hw *dev)
892{
893 /* TODO: figure out how to reliably stop the 3887 and net2280 so
894 the hardware is still usable next time we want to start it.
895 until then, we just stop listening to the hardware.. */
896 p54u_free_urbs(dev);
Michael Wueff1a592007-09-25 18:11:01 -0700897}
898
899static int __devinit p54u_probe(struct usb_interface *intf,
900 const struct usb_device_id *id)
901{
902 struct usb_device *udev = interface_to_usbdev(intf);
903 struct ieee80211_hw *dev;
904 struct p54u_priv *priv;
905 int err;
906 unsigned int i, recognized_pipes;
Michael Wueff1a592007-09-25 18:11:01 -0700907
908 dev = p54_init_common(sizeof(*priv));
Christian Lamparter02e37ba2008-11-29 22:39:08 +0100909
Michael Wueff1a592007-09-25 18:11:01 -0700910 if (!dev) {
Christian Lamparter02e37ba2008-11-29 22:39:08 +0100911 dev_err(&udev->dev, "(p54usb) ieee80211 alloc failed\n");
Michael Wueff1a592007-09-25 18:11:01 -0700912 return -ENOMEM;
913 }
914
915 priv = dev->priv;
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200916 priv->hw_type = P54U_INVALID_HW;
Michael Wueff1a592007-09-25 18:11:01 -0700917
918 SET_IEEE80211_DEV(dev, &intf->dev);
919 usb_set_intfdata(intf, dev);
920 priv->udev = udev;
Christian Lamparter69828692008-12-26 19:08:31 +0100921 priv->intf = intf;
922 skb_queue_head_init(&priv->rx_queue);
923 init_usb_anchor(&priv->submitted);
Michael Wueff1a592007-09-25 18:11:01 -0700924
925 usb_get_dev(udev);
926
927 /* really lazy and simple way of figuring out if we're a 3887 */
928 /* TODO: should just stick the identification in the device table */
929 i = intf->altsetting->desc.bNumEndpoints;
930 recognized_pipes = 0;
931 while (i--) {
932 switch (intf->altsetting->endpoint[i].desc.bEndpointAddress) {
933 case P54U_PIPE_DATA:
934 case P54U_PIPE_MGMT:
935 case P54U_PIPE_BRG:
936 case P54U_PIPE_DEV:
937 case P54U_PIPE_DATA | USB_DIR_IN:
938 case P54U_PIPE_MGMT | USB_DIR_IN:
939 case P54U_PIPE_BRG | USB_DIR_IN:
940 case P54U_PIPE_DEV | USB_DIR_IN:
941 case P54U_PIPE_INT | USB_DIR_IN:
942 recognized_pipes++;
943 }
944 }
945 priv->common.open = p54u_open;
Christian Lamparter2b808482008-09-04 12:29:38 +0200946 priv->common.stop = p54u_stop;
Michael Wueff1a592007-09-25 18:11:01 -0700947 if (recognized_pipes < P54U_PIPE_NUMBER) {
Hauke Mehrtens13792572009-05-01 13:12:36 +0200948#ifdef CONFIG_PM
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200949 /* ISL3887 needs a full reset on resume */
950 udev->reset_resume = 1;
Christian Lamparter11791a62010-08-22 22:41:33 +0200951#endif /* CONFIG_PM */
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200952 err = p54u_device_reset(dev);
953
Michael Wueff1a592007-09-25 18:11:01 -0700954 priv->hw_type = P54U_3887;
Christian Lampartera406ac02009-04-25 21:11:55 +0200955 dev->extra_tx_headroom += sizeof(struct lm87_tx_hdr);
956 priv->common.tx_hdr_len = sizeof(struct lm87_tx_hdr);
957 priv->common.tx = p54u_tx_lm87;
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200958 priv->upload_fw = p54u_upload_firmware_3887;
Michael Wueff1a592007-09-25 18:11:01 -0700959 } else {
Christian Lamparter2b808482008-09-04 12:29:38 +0200960 priv->hw_type = P54U_NET2280;
Michael Wueff1a592007-09-25 18:11:01 -0700961 dev->extra_tx_headroom += sizeof(struct net2280_tx_hdr);
962 priv->common.tx_hdr_len = sizeof(struct net2280_tx_hdr);
963 priv->common.tx = p54u_tx_net2280;
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200964 priv->upload_fw = p54u_upload_firmware_net2280;
Christian Lamparter2b808482008-09-04 12:29:38 +0200965 }
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200966 err = p54u_load_firmware(dev);
Michael Wueff1a592007-09-25 18:11:01 -0700967 if (err)
968 goto err_free_dev;
969
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200970 err = priv->upload_fw(dev);
971 if (err)
972 goto err_free_fw;
973
Christian Lamparter7cb77072008-09-01 22:48:51 +0200974 p54u_open(dev);
975 err = p54_read_eeprom(dev);
976 p54u_stop(dev);
Michael Wueff1a592007-09-25 18:11:01 -0700977 if (err)
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200978 goto err_free_fw;
Michael Wueff1a592007-09-25 18:11:01 -0700979
Christian Lamparter2ac71072009-03-05 21:30:10 +0100980 err = p54_register_common(dev, &udev->dev);
981 if (err)
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200982 goto err_free_fw;
Michael Wueff1a592007-09-25 18:11:01 -0700983
Michael Wueff1a592007-09-25 18:11:01 -0700984 return 0;
985
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200986err_free_fw:
987 release_firmware(priv->fw);
988
989err_free_dev:
Christian Lamparterd8c92102009-06-23 10:39:45 -0500990 p54_free_common(dev);
Michael Wueff1a592007-09-25 18:11:01 -0700991 usb_set_intfdata(intf, NULL);
992 usb_put_dev(udev);
993 return err;
994}
995
996static void __devexit p54u_disconnect(struct usb_interface *intf)
997{
998 struct ieee80211_hw *dev = usb_get_intfdata(intf);
999 struct p54u_priv *priv;
1000
1001 if (!dev)
1002 return;
1003
Christian Lamparterd8c92102009-06-23 10:39:45 -05001004 p54_unregister_common(dev);
Michael Wueff1a592007-09-25 18:11:01 -07001005
1006 priv = dev->priv;
1007 usb_put_dev(interface_to_usbdev(intf));
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +02001008 release_firmware(priv->fw);
Michael Wueff1a592007-09-25 18:11:01 -07001009 p54_free_common(dev);
Michael Wueff1a592007-09-25 18:11:01 -07001010}
1011
Christian Lamparter69828692008-12-26 19:08:31 +01001012static int p54u_pre_reset(struct usb_interface *intf)
1013{
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +02001014 struct ieee80211_hw *dev = usb_get_intfdata(intf);
1015
1016 if (!dev)
1017 return -ENODEV;
1018
1019 p54u_stop(dev);
Christian Lamparter69828692008-12-26 19:08:31 +01001020 return 0;
1021}
1022
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +02001023static int p54u_resume(struct usb_interface *intf)
1024{
1025 struct ieee80211_hw *dev = usb_get_intfdata(intf);
1026 struct p54u_priv *priv;
1027
1028 if (!dev)
1029 return -ENODEV;
1030
1031 priv = dev->priv;
1032 if (unlikely(!(priv->upload_fw && priv->fw)))
1033 return 0;
1034
1035 return priv->upload_fw(dev);
1036}
1037
Christian Lamparter69828692008-12-26 19:08:31 +01001038static int p54u_post_reset(struct usb_interface *intf)
1039{
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +02001040 struct ieee80211_hw *dev = usb_get_intfdata(intf);
1041 struct p54u_priv *priv;
1042 int err;
1043
1044 err = p54u_resume(intf);
1045 if (err)
1046 return err;
1047
1048 /* reinitialize old device state */
1049 priv = dev->priv;
1050 if (priv->common.mode != NL80211_IFTYPE_UNSPECIFIED)
1051 ieee80211_restart_hw(dev);
1052
Christian Lamparter69828692008-12-26 19:08:31 +01001053 return 0;
1054}
1055
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +02001056#ifdef CONFIG_PM
1057
1058static int p54u_suspend(struct usb_interface *intf, pm_message_t message)
1059{
1060 return p54u_pre_reset(intf);
1061}
1062
1063#endif /* CONFIG_PM */
1064
Michael Wueff1a592007-09-25 18:11:01 -07001065static struct usb_driver p54u_driver = {
Christian Lamparter32ddf072008-08-08 21:17:37 +02001066 .name = "p54usb",
Michael Wueff1a592007-09-25 18:11:01 -07001067 .id_table = p54u_table,
1068 .probe = p54u_probe,
1069 .disconnect = p54u_disconnect,
Christian Lamparter69828692008-12-26 19:08:31 +01001070 .pre_reset = p54u_pre_reset,
1071 .post_reset = p54u_post_reset,
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +02001072#ifdef CONFIG_PM
1073 .suspend = p54u_suspend,
1074 .resume = p54u_resume,
1075 .reset_resume = p54u_resume,
1076#endif /* CONFIG_PM */
Christian Lamparterfbf95292009-03-05 21:29:51 +01001077 .soft_unbind = 1,
Michael Wueff1a592007-09-25 18:11:01 -07001078};
1079
1080static int __init p54u_init(void)
1081{
1082 return usb_register(&p54u_driver);
1083}
1084
1085static void __exit p54u_exit(void)
1086{
1087 usb_deregister(&p54u_driver);
1088}
1089
1090module_init(p54u_init);
1091module_exit(p54u_exit);