blob: 27e0a1ff8362ff8eb43134c3dad10bd753edce29 [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>
Paul Gortmaker9d9779e2011-07-03 15:21:01 -040023#include <linux/module.h>
Michael Wueff1a592007-09-25 18:11:01 -070024#include <net/mac80211.h>
25
26#include "p54.h"
Christian Lamparterd8c92102009-06-23 10:39:45 -050027#include "lmac.h"
Michael Wueff1a592007-09-25 18:11:01 -070028#include "p54usb.h"
29
30MODULE_AUTHOR("Michael Wu <flamingice@sourmilk.net>");
31MODULE_DESCRIPTION("Prism54 USB wireless driver");
32MODULE_LICENSE("GPL");
33MODULE_ALIAS("prism54usb");
Christian Lamparter9a8675d2008-10-18 23:04:15 +020034MODULE_FIRMWARE("isl3886usb");
35MODULE_FIRMWARE("isl3887usb");
Michael Wueff1a592007-09-25 18:11:01 -070036
Christian Lamparter1a927952010-10-01 22:01:24 +020037/*
38 * Note:
39 *
40 * Always update our wiki's device list (located at:
41 * http://wireless.kernel.org/en/users/Drivers/p54/devices ),
42 * whenever you add a new device.
43 */
44
Greg Kroah-Hartman37037e12012-08-17 17:48:28 -070045static struct usb_device_id p54u_table[] = {
Michael Wueff1a592007-09-25 18:11:01 -070046 /* Version 1 devices (pci chip + net2280) */
Christian Lamparter16cad7f2010-12-11 12:19:48 +010047 {USB_DEVICE(0x0411, 0x0050)}, /* Buffalo WLI2-USB2-G54 */
Christian Lamparter1a927952010-10-01 22:01:24 +020048 {USB_DEVICE(0x045e, 0x00c2)}, /* Microsoft MN-710 */
Michael Wueff1a592007-09-25 18:11:01 -070049 {USB_DEVICE(0x0506, 0x0a11)}, /* 3COM 3CRWE254G72 */
Hans de Goede05a9a162010-03-17 14:37:16 +010050 {USB_DEVICE(0x06b9, 0x0120)}, /* Thomson SpeedTouch 120g */
Michael Wueff1a592007-09-25 18:11:01 -070051 {USB_DEVICE(0x0707, 0xee06)}, /* SMC 2862W-G */
Shimada Hirofumi15a69a82010-02-14 04:16:16 +090052 {USB_DEVICE(0x07aa, 0x001c)}, /* Corega CG-WLUSB2GT */
Michael Wueff1a592007-09-25 18:11:01 -070053 {USB_DEVICE(0x083a, 0x4501)}, /* Accton 802.11g WN4501 USB */
54 {USB_DEVICE(0x083a, 0x4502)}, /* Siemens Gigaset USB Adapter */
Ivo Couckuyt1a175822008-02-20 14:58:00 -050055 {USB_DEVICE(0x083a, 0x5501)}, /* Phillips CPWUA054 */
Michael Wueff1a592007-09-25 18:11:01 -070056 {USB_DEVICE(0x0846, 0x4200)}, /* Netgear WG121 */
57 {USB_DEVICE(0x0846, 0x4210)}, /* Netgear WG121 the second ? */
58 {USB_DEVICE(0x0846, 0x4220)}, /* Netgear WG111 */
Christian Lamparterec366eb2008-10-13 23:41:53 +020059 {USB_DEVICE(0x09aa, 0x1000)}, /* Spinnaker Proto board */
Christian Lamparter22010762011-04-02 11:31:29 +020060 {USB_DEVICE(0x0bf8, 0x1007)}, /* Fujitsu E-5400 USB */
Michael Wueff1a592007-09-25 18:11:01 -070061 {USB_DEVICE(0x0cde, 0x0006)}, /* Medion 40900, Roper Europe */
Christian Lamparter16cad7f2010-12-11 12:19:48 +010062 {USB_DEVICE(0x0db0, 0x6826)}, /* MSI UB54G (MS-6826) */
Christian Lamparter1a927952010-10-01 22:01:24 +020063 {USB_DEVICE(0x107b, 0x55f2)}, /* Gateway WGU-210 (Gemtek) */
Michael Wueff1a592007-09-25 18:11:01 -070064 {USB_DEVICE(0x124a, 0x4023)}, /* Shuttle PN15, Airvast WM168g, IOGear GWU513 */
Christian Lamparter16cad7f2010-12-11 12:19:48 +010065 {USB_DEVICE(0x1435, 0x0210)}, /* Inventel UR054G */
Eduardo Costa56e64172010-12-14 14:37:59 -060066 {USB_DEVICE(0x15a9, 0x0002)}, /* Gemtek WUBI-100GW 802.11g */
Christian Lamparter1a927952010-10-01 22:01:24 +020067 {USB_DEVICE(0x1630, 0x0005)}, /* 2Wire 802.11g USB (v1) / Z-Com */
Christian Lamparter16cad7f2010-12-11 12:19:48 +010068 {USB_DEVICE(0x182d, 0x096b)}, /* Sitecom WL-107 */
Michael Wueff1a592007-09-25 18:11:01 -070069 {USB_DEVICE(0x1915, 0x2234)}, /* Linksys WUSB54G OEM */
70 {USB_DEVICE(0x1915, 0x2235)}, /* Linksys WUSB54G Portable OEM */
71 {USB_DEVICE(0x2001, 0x3701)}, /* DLink DWL-G120 Spinnaker */
72 {USB_DEVICE(0x2001, 0x3703)}, /* DLink DWL-G122 */
Christian Lamparter22010762011-04-02 11:31:29 +020073 {USB_DEVICE(0x2001, 0x3762)}, /* Conceptronic C54U */
Michael Wueff1a592007-09-25 18:11:01 -070074 {USB_DEVICE(0x5041, 0x2234)}, /* Linksys WUSB54G */
75 {USB_DEVICE(0x5041, 0x2235)}, /* Linksys WUSB54G Portable */
76
77 /* Version 2 devices (3887) */
Felix Homann45460022008-05-29 00:36:45 -070078 {USB_DEVICE(0x0471, 0x1230)}, /* Philips CPWUA054/00 */
Michael Wueff1a592007-09-25 18:11:01 -070079 {USB_DEVICE(0x050d, 0x7050)}, /* Belkin F5D7050 ver 1000 */
80 {USB_DEVICE(0x0572, 0x2000)}, /* Cohiba Proto board */
81 {USB_DEVICE(0x0572, 0x2002)}, /* Cohiba Proto board */
Christian Lamparter1a927952010-10-01 22:01:24 +020082 {USB_DEVICE(0x06a9, 0x000e)}, /* Westell 802.11g USB (A90-211WG-01) */
Michiel878e6a42009-01-04 17:22:28 -060083 {USB_DEVICE(0x06b9, 0x0121)}, /* Thomson SpeedTouch 121g */
Michael Wueff1a592007-09-25 18:11:01 -070084 {USB_DEVICE(0x0707, 0xee13)}, /* SMC 2862W-G version 2 */
Tomasz Guszkowski0e701112012-12-22 18:30:01 +010085 {USB_DEVICE(0x083a, 0x4503)}, /* T-Com Sinus 154 data II */
Michael Wueff1a592007-09-25 18:11:01 -070086 {USB_DEVICE(0x083a, 0x4521)}, /* Siemens Gigaset USB Adapter 54 version 2 */
Christian Lamparter9368a9a2011-05-13 21:47:23 +020087 {USB_DEVICE(0x083a, 0xc501)}, /* Zoom Wireless-G 4410 */
Jean-François Moine5b9a9192010-02-17 10:59:31 -060088 {USB_DEVICE(0x083a, 0xf503)}, /* Accton FD7050E ver 1010ec */
Michael Wueff1a592007-09-25 18:11:01 -070089 {USB_DEVICE(0x0846, 0x4240)}, /* Netgear WG111 (v2) */
90 {USB_DEVICE(0x0915, 0x2000)}, /* Cohiba Proto board */
91 {USB_DEVICE(0x0915, 0x2002)}, /* Cohiba Proto board */
92 {USB_DEVICE(0x0baf, 0x0118)}, /* U.S. Robotics U5 802.11g Adapter*/
93 {USB_DEVICE(0x0bf8, 0x1009)}, /* FUJITSU E-5400 USB D1700*/
Leann Ogasawara7484bdc2010-06-15 14:01:51 -070094 /* {USB_DEVICE(0x0cde, 0x0006)}, * Medion MD40900 already listed above,
95 * just noting it here for clarity */
Michael Wueff1a592007-09-25 18:11:01 -070096 {USB_DEVICE(0x0cde, 0x0008)}, /* Sagem XG703A */
Christian Lamparterf7f71172009-09-14 23:08:43 +020097 {USB_DEVICE(0x0cde, 0x0015)}, /* Zcomax XG-705A */
Michael Wueff1a592007-09-25 18:11:01 -070098 {USB_DEVICE(0x0d8e, 0x3762)}, /* DLink DWL-G120 Cohiba */
Jan Slupski43557e12008-03-10 22:41:18 -070099 {USB_DEVICE(0x124a, 0x4025)}, /* IOGear GWU513 (GW3887IK chip) */
Christian Lamparterec366eb2008-10-13 23:41:53 +0200100 {USB_DEVICE(0x1260, 0xee22)}, /* SMC 2862W-G version 2 */
John W. Linville387e1002008-02-20 15:06:02 -0500101 {USB_DEVICE(0x13b1, 0x000a)}, /* Linksys WUSB54G ver 2 */
Martti Huttunenc1098102007-10-04 00:06:00 -0400102 {USB_DEVICE(0x13B1, 0x000C)}, /* Linksys WUSB54AG */
Christian Lampartere3062402009-03-29 22:50:28 +0200103 {USB_DEVICE(0x1413, 0x5400)}, /* Telsey 802.11g USB2.0 Adapter */
Michael Wueff1a592007-09-25 18:11:01 -0700104 {USB_DEVICE(0x1435, 0x0427)}, /* Inventel UR054G */
Christian Lamparter1a927952010-10-01 22:01:24 +0200105 {USB_DEVICE(0x1668, 0x1050)}, /* Actiontec 802UIG-1 */
Christian Lamparter2b799a62011-02-26 12:58:06 +0100106 {USB_DEVICE(0x1740, 0x1000)}, /* Senao NUB-350 */
Michael Wueff1a592007-09-25 18:11:01 -0700107 {USB_DEVICE(0x2001, 0x3704)}, /* DLink DWL-G122 rev A2 */
Christian Lamparter16cad7f2010-12-11 12:19:48 +0100108 {USB_DEVICE(0x2001, 0x3705)}, /* D-Link DWL-G120 rev C1 */
Jason Dravet0f666a02010-06-05 15:08:29 -0500109 {USB_DEVICE(0x413c, 0x5513)}, /* Dell WLA3310 USB Wireless Adapter */
Michael Wueff1a592007-09-25 18:11:01 -0700110 {USB_DEVICE(0x413c, 0x8102)}, /* Spinnaker DUT */
111 {USB_DEVICE(0x413c, 0x8104)}, /* Cohiba Proto board */
112 {}
113};
114
115MODULE_DEVICE_TABLE(usb, p54u_table);
116
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200117static const struct {
118 u32 intf;
119 enum p54u_hw_type type;
Samuel Ortiz328d84f2009-05-27 10:12:51 +0200120 const char *fw;
121 const char *fw_legacy;
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200122 char hw[20];
123} p54u_fwlist[__NUM_P54U_HWTYPES] = {
124 {
125 .type = P54U_NET2280,
126 .intf = FW_LM86,
127 .fw = "isl3886usb",
128 .fw_legacy = "isl3890usb",
129 .hw = "ISL3886 + net2280",
130 },
131 {
132 .type = P54U_3887,
133 .intf = FW_LM87,
134 .fw = "isl3887usb",
135 .fw_legacy = "isl3887usb_bare",
136 .hw = "ISL3887",
137 },
138};
139
Michael Wueff1a592007-09-25 18:11:01 -0700140static void p54u_rx_cb(struct urb *urb)
141{
142 struct sk_buff *skb = (struct sk_buff *) urb->context;
143 struct p54u_rx_info *info = (struct p54u_rx_info *)skb->cb;
144 struct ieee80211_hw *dev = info->dev;
145 struct p54u_priv *priv = dev->priv;
146
Christian Lamparterdd397dc2008-12-09 15:14:37 +0100147 skb_unlink(skb, &priv->rx_queue);
148
Michael Wueff1a592007-09-25 18:11:01 -0700149 if (unlikely(urb->status)) {
Christian Lamparterdd397dc2008-12-09 15:14:37 +0100150 dev_kfree_skb_irq(skb);
Michael Wueff1a592007-09-25 18:11:01 -0700151 return;
152 }
153
Michael Wueff1a592007-09-25 18:11:01 -0700154 skb_put(skb, urb->actual_length);
Christian Lamparter2b808482008-09-04 12:29:38 +0200155
156 if (priv->hw_type == P54U_NET2280)
157 skb_pull(skb, priv->common.tx_hdr_len);
158 if (priv->common.fw_interface == FW_LM87) {
159 skb_pull(skb, 4);
160 skb_put(skb, 4);
161 }
Michael Wueff1a592007-09-25 18:11:01 -0700162
163 if (p54_rx(dev, skb)) {
Christian Lamparter4e416a62008-09-01 22:48:41 +0200164 skb = dev_alloc_skb(priv->common.rx_mtu + 32);
Michael Wueff1a592007-09-25 18:11:01 -0700165 if (unlikely(!skb)) {
Michael Wueff1a592007-09-25 18:11:01 -0700166 /* TODO check rx queue length and refill *somewhere* */
167 return;
168 }
169
170 info = (struct p54u_rx_info *) skb->cb;
171 info->urb = urb;
172 info->dev = dev;
173 urb->transfer_buffer = skb_tail_pointer(skb);
174 urb->context = skb;
Michael Wueff1a592007-09-25 18:11:01 -0700175 } else {
Christian Lamparter2b808482008-09-04 12:29:38 +0200176 if (priv->hw_type == P54U_NET2280)
177 skb_push(skb, priv->common.tx_hdr_len);
178 if (priv->common.fw_interface == FW_LM87) {
179 skb_push(skb, 4);
180 skb_put(skb, 4);
181 }
Christian Lamparterd47c3ce2008-08-13 23:41:48 +0200182 skb_reset_tail_pointer(skb);
Michael Wueff1a592007-09-25 18:11:01 -0700183 skb_trim(skb, 0);
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200184 urb->transfer_buffer = skb_tail_pointer(skb);
Michael Wueff1a592007-09-25 18:11:01 -0700185 }
Christian Lamparterdd397dc2008-12-09 15:14:37 +0100186 skb_queue_tail(&priv->rx_queue, skb);
187 usb_anchor_urb(urb, &priv->submitted);
188 if (usb_submit_urb(urb, GFP_ATOMIC)) {
189 skb_unlink(skb, &priv->rx_queue);
190 usb_unanchor_urb(urb);
191 dev_kfree_skb_irq(skb);
192 }
Michael Wueff1a592007-09-25 18:11:01 -0700193}
194
Christian Lamparter0a5ec962008-12-14 15:05:42 +0100195static void p54u_tx_cb(struct urb *urb)
Christian Lamparterb92f30d2008-10-15 04:07:16 +0200196{
197 struct sk_buff *skb = urb->context;
Joe Perchesb2767362010-11-30 13:42:08 -0800198 struct ieee80211_hw *dev =
Christian Lamparterb92f30d2008-10-15 04:07:16 +0200199 usb_get_intfdata(usb_ifnum_to_if(urb->dev, 0));
200
Christian Lampartere2fe1542009-01-20 00:27:57 +0100201 p54_free_skb(dev, skb);
Christian Lamparterdd397dc2008-12-09 15:14:37 +0100202}
203
204static void p54u_tx_dummy_cb(struct urb *urb) { }
205
206static void p54u_free_urbs(struct ieee80211_hw *dev)
207{
208 struct p54u_priv *priv = dev->priv;
209 usb_kill_anchored_urbs(&priv->submitted);
Christian Lamparterb92f30d2008-10-15 04:07:16 +0200210}
211
Michael Wueff1a592007-09-25 18:11:01 -0700212static int p54u_init_urbs(struct ieee80211_hw *dev)
213{
214 struct p54u_priv *priv = dev->priv;
Christian Lamparterdd397dc2008-12-09 15:14:37 +0100215 struct urb *entry = NULL;
Michael Wueff1a592007-09-25 18:11:01 -0700216 struct sk_buff *skb;
217 struct p54u_rx_info *info;
Christian Lamparterdd397dc2008-12-09 15:14:37 +0100218 int ret = 0;
Michael Wueff1a592007-09-25 18:11:01 -0700219
220 while (skb_queue_len(&priv->rx_queue) < 32) {
Christian Lamparter4e416a62008-09-01 22:48:41 +0200221 skb = __dev_alloc_skb(priv->common.rx_mtu + 32, GFP_KERNEL);
Christian Lamparterdd397dc2008-12-09 15:14:37 +0100222 if (!skb) {
223 ret = -ENOMEM;
224 goto err;
225 }
Michael Wueff1a592007-09-25 18:11:01 -0700226 entry = usb_alloc_urb(0, GFP_KERNEL);
227 if (!entry) {
Christian Lamparterdd397dc2008-12-09 15:14:37 +0100228 ret = -ENOMEM;
229 goto err;
Michael Wueff1a592007-09-25 18:11:01 -0700230 }
Christian Lamparterdd397dc2008-12-09 15:14:37 +0100231
Christian Lamparter4e416a62008-09-01 22:48:41 +0200232 usb_fill_bulk_urb(entry, priv->udev,
233 usb_rcvbulkpipe(priv->udev, P54U_PIPE_DATA),
234 skb_tail_pointer(skb),
235 priv->common.rx_mtu + 32, p54u_rx_cb, skb);
Michael Wueff1a592007-09-25 18:11:01 -0700236 info = (struct p54u_rx_info *) skb->cb;
237 info->urb = entry;
238 info->dev = dev;
239 skb_queue_tail(&priv->rx_queue, skb);
Christian Lamparterdd397dc2008-12-09 15:14:37 +0100240
241 usb_anchor_urb(entry, &priv->submitted);
242 ret = usb_submit_urb(entry, GFP_KERNEL);
243 if (ret) {
244 skb_unlink(skb, &priv->rx_queue);
245 usb_unanchor_urb(entry);
246 goto err;
247 }
248 usb_free_urb(entry);
249 entry = NULL;
Michael Wueff1a592007-09-25 18:11:01 -0700250 }
251
252 return 0;
Michael Wueff1a592007-09-25 18:11:01 -0700253
Christian Lamparterdd397dc2008-12-09 15:14:37 +0100254 err:
255 usb_free_urb(entry);
256 kfree_skb(skb);
257 p54u_free_urbs(dev);
258 return ret;
Michael Wueff1a592007-09-25 18:11:01 -0700259}
260
Johannes Bergc9127652008-12-01 18:19:36 +0100261static __le32 p54u_lm87_chksum(const __le32 *data, size_t length)
Christian Lamparter2b808482008-09-04 12:29:38 +0200262{
Larry Finger1f1c0e32008-09-25 14:54:28 -0500263 u32 chk = 0;
Christian Lamparter2b808482008-09-04 12:29:38 +0200264
265 length >>= 2;
266 while (length--) {
Johannes Bergc9127652008-12-01 18:19:36 +0100267 chk ^= le32_to_cpu(*data++);
Christian Lamparter2b808482008-09-04 12:29:38 +0200268 chk = (chk >> 5) ^ (chk << 3);
269 }
270
Larry Finger1f1c0e32008-09-25 14:54:28 -0500271 return cpu_to_le32(chk);
Christian Lamparter2b808482008-09-04 12:29:38 +0200272}
273
Christian Lamparter0a5ec962008-12-14 15:05:42 +0100274static void p54u_tx_lm87(struct ieee80211_hw *dev, struct sk_buff *skb)
Christian Lamparter2b808482008-09-04 12:29:38 +0200275{
276 struct p54u_priv *priv = dev->priv;
277 struct urb *data_urb;
Christian Lampartere2fe1542009-01-20 00:27:57 +0100278 struct lm87_tx_hdr *hdr = (void *)skb->data - sizeof(*hdr);
Christian Lamparter2b808482008-09-04 12:29:38 +0200279
280 data_urb = usb_alloc_urb(0, GFP_ATOMIC);
Christian Lamparter6d541a62009-07-06 15:17:56 +0200281 if (!data_urb) {
282 p54_free_skb(dev, skb);
Christian Lamparter2b808482008-09-04 12:29:38 +0200283 return;
Christian Lamparter6d541a62009-07-06 15:17:56 +0200284 }
Christian Lamparter2b808482008-09-04 12:29:38 +0200285
Christian Lampartere2fe1542009-01-20 00:27:57 +0100286 hdr->chksum = p54u_lm87_chksum((__le32 *)skb->data, skb->len);
287 hdr->device_addr = ((struct p54_hdr *)skb->data)->req_id;
Christian Lamparter2b808482008-09-04 12:29:38 +0200288
289 usb_fill_bulk_urb(data_urb, priv->udev,
Christian Lamparterb92f30d2008-10-15 04:07:16 +0200290 usb_sndbulkpipe(priv->udev, P54U_PIPE_DATA),
Christian Lampartere2fe1542009-01-20 00:27:57 +0100291 hdr, skb->len + sizeof(*hdr), FREE_AFTER_TX(skb) ?
292 p54u_tx_cb : p54u_tx_dummy_cb, skb);
Christian Lamparter00627f22008-12-20 02:21:56 +0100293 data_urb->transfer_flags |= URB_ZERO_PACKET;
Christian Lamparter2b808482008-09-04 12:29:38 +0200294
Christian Lamparterdd397dc2008-12-09 15:14:37 +0100295 usb_anchor_urb(data_urb, &priv->submitted);
296 if (usb_submit_urb(data_urb, GFP_ATOMIC)) {
297 usb_unanchor_urb(data_urb);
Christian Lamparterdd397dc2008-12-09 15:14:37 +0100298 p54_free_skb(dev, skb);
299 }
300 usb_free_urb(data_urb);
Christian Lamparter2b808482008-09-04 12:29:38 +0200301}
302
Christian Lamparter0a5ec962008-12-14 15:05:42 +0100303static void p54u_tx_net2280(struct ieee80211_hw *dev, struct sk_buff *skb)
Michael Wueff1a592007-09-25 18:11:01 -0700304{
305 struct p54u_priv *priv = dev->priv;
Christian Lamparter6d541a62009-07-06 15:17:56 +0200306 struct urb *int_urb = NULL, *data_urb = NULL;
Christian Lampartere2fe1542009-01-20 00:27:57 +0100307 struct net2280_tx_hdr *hdr = (void *)skb->data - sizeof(*hdr);
Christian Lamparter6d541a62009-07-06 15:17:56 +0200308 struct net2280_reg_write *reg = NULL;
309 int err = -ENOMEM;
Michael Wueff1a592007-09-25 18:11:01 -0700310
311 reg = kmalloc(sizeof(*reg), GFP_ATOMIC);
312 if (!reg)
Christian Lamparter6d541a62009-07-06 15:17:56 +0200313 goto out;
Michael Wueff1a592007-09-25 18:11:01 -0700314
315 int_urb = usb_alloc_urb(0, GFP_ATOMIC);
Christian Lamparter6d541a62009-07-06 15:17:56 +0200316 if (!int_urb)
317 goto out;
Michael Wueff1a592007-09-25 18:11:01 -0700318
319 data_urb = usb_alloc_urb(0, GFP_ATOMIC);
Christian Lamparter6d541a62009-07-06 15:17:56 +0200320 if (!data_urb)
321 goto out;
Michael Wueff1a592007-09-25 18:11:01 -0700322
323 reg->port = cpu_to_le16(NET2280_DEV_U32);
324 reg->addr = cpu_to_le32(P54U_DEV_BASE);
325 reg->val = cpu_to_le32(ISL38XX_DEV_INT_DATA);
326
Michael Wueff1a592007-09-25 18:11:01 -0700327 memset(hdr, 0, sizeof(*hdr));
Christian Lampartere2fe1542009-01-20 00:27:57 +0100328 hdr->len = cpu_to_le16(skb->len);
329 hdr->device_addr = ((struct p54_hdr *) skb->data)->req_id;
Michael Wueff1a592007-09-25 18:11:01 -0700330
331 usb_fill_bulk_urb(int_urb, priv->udev,
332 usb_sndbulkpipe(priv->udev, P54U_PIPE_DEV), reg, sizeof(*reg),
Christian Lamparterdd397dc2008-12-09 15:14:37 +0100333 p54u_tx_dummy_cb, dev);
334
335 /*
Christian Lamparter6d541a62009-07-06 15:17:56 +0200336 * URB_FREE_BUFFER triggers a code path in the USB subsystem that will
337 * free what is inside the transfer_buffer after the last reference to
338 * the int_urb is dropped.
Christian Lamparterdd397dc2008-12-09 15:14:37 +0100339 */
Christian Lamparterb4068a82009-01-20 23:11:21 +0100340 int_urb->transfer_flags |= URB_FREE_BUFFER | URB_ZERO_PACKET;
Christian Lamparter6d541a62009-07-06 15:17:56 +0200341 reg = NULL;
Michael Wueff1a592007-09-25 18:11:01 -0700342
343 usb_fill_bulk_urb(data_urb, priv->udev,
Christian Lamparterb92f30d2008-10-15 04:07:16 +0200344 usb_sndbulkpipe(priv->udev, P54U_PIPE_DATA),
Christian Lampartere2fe1542009-01-20 00:27:57 +0100345 hdr, skb->len + sizeof(*hdr), FREE_AFTER_TX(skb) ?
346 p54u_tx_cb : p54u_tx_dummy_cb, skb);
Christian Lamparterb4068a82009-01-20 23:11:21 +0100347 data_urb->transfer_flags |= URB_ZERO_PACKET;
Christian Lamparterdd397dc2008-12-09 15:14:37 +0100348
349 usb_anchor_urb(int_urb, &priv->submitted);
350 err = usb_submit_urb(int_urb, GFP_ATOMIC);
351 if (err) {
352 usb_unanchor_urb(int_urb);
353 goto out;
354 }
355
356 usb_anchor_urb(data_urb, &priv->submitted);
357 err = usb_submit_urb(data_urb, GFP_ATOMIC);
358 if (err) {
359 usb_unanchor_urb(data_urb);
360 goto out;
361 }
Christian Lamparter6d541a62009-07-06 15:17:56 +0200362out:
Christian Lamparterdd397dc2008-12-09 15:14:37 +0100363 usb_free_urb(int_urb);
364 usb_free_urb(data_urb);
365
366 if (err) {
Christian Lamparter6d541a62009-07-06 15:17:56 +0200367 kfree(reg);
Christian Lamparterdd397dc2008-12-09 15:14:37 +0100368 p54_free_skb(dev, skb);
369 }
Michael Wueff1a592007-09-25 18:11:01 -0700370}
371
372static int p54u_write(struct p54u_priv *priv,
373 struct net2280_reg_write *buf,
374 enum net2280_op_type type,
375 __le32 addr, __le32 val)
376{
377 unsigned int ep;
378 int alen;
379
380 if (type & 0x0800)
381 ep = usb_sndbulkpipe(priv->udev, P54U_PIPE_DEV);
382 else
383 ep = usb_sndbulkpipe(priv->udev, P54U_PIPE_BRG);
384
385 buf->port = cpu_to_le16(type);
386 buf->addr = addr;
387 buf->val = val;
388
389 return usb_bulk_msg(priv->udev, ep, buf, sizeof(*buf), &alen, 1000);
390}
391
392static int p54u_read(struct p54u_priv *priv, void *buf,
393 enum net2280_op_type type,
394 __le32 addr, __le32 *val)
395{
396 struct net2280_reg_read *read = buf;
397 __le32 *reg = buf;
398 unsigned int ep;
399 int alen, err;
400
401 if (type & 0x0800)
402 ep = P54U_PIPE_DEV;
403 else
404 ep = P54U_PIPE_BRG;
405
406 read->port = cpu_to_le16(type);
407 read->addr = addr;
408
409 err = usb_bulk_msg(priv->udev, usb_sndbulkpipe(priv->udev, ep),
410 read, sizeof(*read), &alen, 1000);
411 if (err)
412 return err;
413
414 err = usb_bulk_msg(priv->udev, usb_rcvbulkpipe(priv->udev, ep),
415 reg, sizeof(*reg), &alen, 1000);
416 if (err)
417 return err;
418
419 *val = *reg;
420 return 0;
421}
422
423static int p54u_bulk_msg(struct p54u_priv *priv, unsigned int ep,
424 void *data, size_t len)
425{
426 int alen;
427 return usb_bulk_msg(priv->udev, usb_sndbulkpipe(priv->udev, ep),
428 data, len, &alen, 2000);
429}
430
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200431static int p54u_device_reset(struct ieee80211_hw *dev)
Christian Lamparter69828692008-12-26 19:08:31 +0100432{
433 struct p54u_priv *priv = dev->priv;
Christian Lamparterc88a7682009-01-16 20:24:31 +0100434 int ret, lock = (priv->intf->condition != USB_INTERFACE_BINDING);
Christian Lamparter69828692008-12-26 19:08:31 +0100435
Christian Lamparterc88a7682009-01-16 20:24:31 +0100436 if (lock) {
437 ret = usb_lock_device_for_reset(priv->udev, priv->intf);
438 if (ret < 0) {
439 dev_err(&priv->udev->dev, "(p54usb) unable to lock "
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200440 "device for reset (%d)!\n", ret);
Christian Lamparterc88a7682009-01-16 20:24:31 +0100441 return ret;
442 }
Christian Lamparter69828692008-12-26 19:08:31 +0100443 }
444
445 ret = usb_reset_device(priv->udev);
446 if (lock)
447 usb_unlock_device(priv->udev);
448
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200449 if (ret)
Christian Lamparter69828692008-12-26 19:08:31 +0100450 dev_err(&priv->udev->dev, "(p54usb) unable to reset "
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200451 "device (%d)!\n", ret);
452
453 return ret;
454}
455
456static const char p54u_romboot_3887[] = "~~~~";
457static int p54u_firmware_reset_3887(struct ieee80211_hw *dev)
458{
459 struct p54u_priv *priv = dev->priv;
Larry Finger21d6c272009-11-11 18:02:29 -0600460 u8 *buf;
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200461 int ret;
Christian Lamparter69828692008-12-26 19:08:31 +0100462
Julia Lawall27b81bb2010-05-15 23:22:55 +0200463 buf = kmemdup(p54u_romboot_3887, 4, GFP_KERNEL);
Larry Finger21d6c272009-11-11 18:02:29 -0600464 if (!buf)
465 return -ENOMEM;
Christian Lamparter69828692008-12-26 19:08:31 +0100466 ret = p54u_bulk_msg(priv, P54U_PIPE_DATA,
Larry Finger21d6c272009-11-11 18:02:29 -0600467 buf, 4);
468 kfree(buf);
Christian Lamparter69828692008-12-26 19:08:31 +0100469 if (ret)
470 dev_err(&priv->udev->dev, "(p54usb) unable to jump to "
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200471 "boot ROM (%d)!\n", ret);
Christian Lamparter69828692008-12-26 19:08:31 +0100472
473 return ret;
474}
475
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200476static const char p54u_firmware_upload_3887[] = "<\r";
Michael Wueff1a592007-09-25 18:11:01 -0700477static int p54u_upload_firmware_3887(struct ieee80211_hw *dev)
478{
Michael Wueff1a592007-09-25 18:11:01 -0700479 struct p54u_priv *priv = dev->priv;
Michael Wueff1a592007-09-25 18:11:01 -0700480 int err, alen;
481 u8 carry = 0;
David Woodhouse8b72eb42008-05-24 00:08:55 +0100482 u8 *buf, *tmp;
483 const u8 *data;
Michael Wueff1a592007-09-25 18:11:01 -0700484 unsigned int left, remains, block_size;
485 struct x2_header *hdr;
486 unsigned long timeout;
487
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200488 err = p54u_firmware_reset_3887(dev);
489 if (err)
490 return err;
491
Michael Wueff1a592007-09-25 18:11:01 -0700492 tmp = buf = kmalloc(P54U_FW_BLOCK, GFP_KERNEL);
493 if (!buf) {
Christian Lamparter02e37ba2008-11-29 22:39:08 +0100494 dev_err(&priv->udev->dev, "(p54usb) cannot allocate firmware"
495 "upload buffer!\n");
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200496 return -ENOMEM;
Michael Wueff1a592007-09-25 18:11:01 -0700497 }
498
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200499 left = block_size = min((size_t)P54U_FW_BLOCK, priv->fw->size);
Christian Lamparter69828692008-12-26 19:08:31 +0100500 strcpy(buf, p54u_firmware_upload_3887);
501 left -= strlen(p54u_firmware_upload_3887);
502 tmp += strlen(p54u_firmware_upload_3887);
Michael Wueff1a592007-09-25 18:11:01 -0700503
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200504 data = priv->fw->data;
505 remains = priv->fw->size;
Michael Wueff1a592007-09-25 18:11:01 -0700506
Christian Lamparter69828692008-12-26 19:08:31 +0100507 hdr = (struct x2_header *)(buf + strlen(p54u_firmware_upload_3887));
Michael Wueff1a592007-09-25 18:11:01 -0700508 memcpy(hdr->signature, X2_SIGNATURE, X2_SIGNATURE_SIZE);
509 hdr->fw_load_addr = cpu_to_le32(ISL38XX_DEV_FIRMWARE_ADDR);
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200510 hdr->fw_length = cpu_to_le32(priv->fw->size);
Michael Wueff1a592007-09-25 18:11:01 -0700511 hdr->crc = cpu_to_le32(~crc32_le(~0, (void *)&hdr->fw_load_addr,
512 sizeof(u32)*2));
513 left -= sizeof(*hdr);
514 tmp += sizeof(*hdr);
515
516 while (remains) {
517 while (left--) {
518 if (carry) {
519 *tmp++ = carry;
520 carry = 0;
521 remains--;
522 continue;
523 }
524 switch (*data) {
525 case '~':
526 *tmp++ = '}';
527 carry = '^';
528 break;
529 case '}':
530 *tmp++ = '}';
531 carry = ']';
532 break;
533 default:
534 *tmp++ = *data;
535 remains--;
536 break;
537 }
538 data++;
539 }
540
541 err = p54u_bulk_msg(priv, P54U_PIPE_DATA, buf, block_size);
542 if (err) {
Christian Lamparter02e37ba2008-11-29 22:39:08 +0100543 dev_err(&priv->udev->dev, "(p54usb) firmware "
544 "upload failed!\n");
Michael Wueff1a592007-09-25 18:11:01 -0700545 goto err_upload_failed;
546 }
547
548 tmp = buf;
549 left = block_size = min((unsigned int)P54U_FW_BLOCK, remains);
550 }
551
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200552 *((__le32 *)buf) = cpu_to_le32(~crc32_le(~0, priv->fw->data,
553 priv->fw->size));
Michael Wueff1a592007-09-25 18:11:01 -0700554 err = p54u_bulk_msg(priv, P54U_PIPE_DATA, buf, sizeof(u32));
555 if (err) {
Christian Lamparter02e37ba2008-11-29 22:39:08 +0100556 dev_err(&priv->udev->dev, "(p54usb) firmware upload failed!\n");
Michael Wueff1a592007-09-25 18:11:01 -0700557 goto err_upload_failed;
558 }
Michael Wueff1a592007-09-25 18:11:01 -0700559 timeout = jiffies + msecs_to_jiffies(1000);
560 while (!(err = usb_bulk_msg(priv->udev,
561 usb_rcvbulkpipe(priv->udev, P54U_PIPE_DATA), buf, 128, &alen, 1000))) {
562 if (alen > 2 && !memcmp(buf, "OK", 2))
563 break;
564
565 if (alen > 5 && !memcmp(buf, "ERROR", 5)) {
Michael Wueff1a592007-09-25 18:11:01 -0700566 err = -EINVAL;
567 break;
568 }
569
570 if (time_after(jiffies, timeout)) {
Christian Lamparter02e37ba2008-11-29 22:39:08 +0100571 dev_err(&priv->udev->dev, "(p54usb) firmware boot "
572 "timed out!\n");
Michael Wueff1a592007-09-25 18:11:01 -0700573 err = -ETIMEDOUT;
574 break;
575 }
576 }
Christian Lamparter02e37ba2008-11-29 22:39:08 +0100577 if (err) {
578 dev_err(&priv->udev->dev, "(p54usb) firmware upload failed!\n");
Michael Wueff1a592007-09-25 18:11:01 -0700579 goto err_upload_failed;
Christian Lamparter02e37ba2008-11-29 22:39:08 +0100580 }
Michael Wueff1a592007-09-25 18:11:01 -0700581
582 buf[0] = 'g';
583 buf[1] = '\r';
584 err = p54u_bulk_msg(priv, P54U_PIPE_DATA, buf, 2);
585 if (err) {
Christian Lamparter02e37ba2008-11-29 22:39:08 +0100586 dev_err(&priv->udev->dev, "(p54usb) firmware boot failed!\n");
Michael Wueff1a592007-09-25 18:11:01 -0700587 goto err_upload_failed;
588 }
589
590 timeout = jiffies + msecs_to_jiffies(1000);
591 while (!(err = usb_bulk_msg(priv->udev,
592 usb_rcvbulkpipe(priv->udev, P54U_PIPE_DATA), buf, 128, &alen, 1000))) {
593 if (alen > 0 && buf[0] == 'g')
594 break;
595
596 if (time_after(jiffies, timeout)) {
597 err = -ETIMEDOUT;
598 break;
599 }
600 }
601 if (err)
602 goto err_upload_failed;
603
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200604err_upload_failed:
Michael Wueff1a592007-09-25 18:11:01 -0700605 kfree(buf);
Michael Wueff1a592007-09-25 18:11:01 -0700606 return err;
607}
608
609static int p54u_upload_firmware_net2280(struct ieee80211_hw *dev)
610{
611 struct p54u_priv *priv = dev->priv;
Michael Wueff1a592007-09-25 18:11:01 -0700612 const struct p54p_csr *devreg = (const struct p54p_csr *) P54U_DEV_BASE;
613 int err, alen;
614 void *buf;
615 __le32 reg;
616 unsigned int remains, offset;
David Woodhouse8b72eb42008-05-24 00:08:55 +0100617 const u8 *data;
Michael Wueff1a592007-09-25 18:11:01 -0700618
619 buf = kmalloc(512, GFP_KERNEL);
620 if (!buf) {
Christian Lamparter02e37ba2008-11-29 22:39:08 +0100621 dev_err(&priv->udev->dev, "(p54usb) firmware buffer "
622 "alloc failed!\n");
Michael Wueff1a592007-09-25 18:11:01 -0700623 return -ENOMEM;
624 }
625
Michael Wueff1a592007-09-25 18:11:01 -0700626#define P54U_WRITE(type, addr, data) \
627 do {\
628 err = p54u_write(priv, buf, type,\
629 cpu_to_le32((u32)(unsigned long)addr), data);\
630 if (err) \
631 goto fail;\
632 } while (0)
633
634#define P54U_READ(type, addr) \
635 do {\
636 err = p54u_read(priv, buf, type,\
637 cpu_to_le32((u32)(unsigned long)addr), &reg);\
638 if (err)\
639 goto fail;\
640 } while (0)
641
642 /* power down net2280 bridge */
643 P54U_READ(NET2280_BRG_U32, NET2280_GPIOCTL);
644 reg |= cpu_to_le32(P54U_BRG_POWER_DOWN);
645 reg &= cpu_to_le32(~P54U_BRG_POWER_UP);
646 P54U_WRITE(NET2280_BRG_U32, NET2280_GPIOCTL, reg);
647
648 mdelay(100);
649
650 /* power up bridge */
651 reg |= cpu_to_le32(P54U_BRG_POWER_UP);
652 reg &= cpu_to_le32(~P54U_BRG_POWER_DOWN);
653 P54U_WRITE(NET2280_BRG_U32, NET2280_GPIOCTL, reg);
654
655 mdelay(100);
656
657 P54U_WRITE(NET2280_BRG_U32, NET2280_DEVINIT,
658 cpu_to_le32(NET2280_CLK_30Mhz |
659 NET2280_PCI_ENABLE |
660 NET2280_PCI_SOFT_RESET));
661
662 mdelay(20);
663
664 P54U_WRITE(NET2280_BRG_CFG_U16, PCI_COMMAND,
665 cpu_to_le32(PCI_COMMAND_MEMORY |
666 PCI_COMMAND_MASTER));
667
668 P54U_WRITE(NET2280_BRG_CFG_U32, PCI_BASE_ADDRESS_0,
669 cpu_to_le32(NET2280_BASE));
670
671 P54U_READ(NET2280_BRG_CFG_U16, PCI_STATUS);
672 reg |= cpu_to_le32(PCI_STATUS_REC_MASTER_ABORT);
673 P54U_WRITE(NET2280_BRG_CFG_U16, PCI_STATUS, reg);
674
675 // TODO: we really need this?
676 P54U_READ(NET2280_BRG_U32, NET2280_RELNUM);
677
678 P54U_WRITE(NET2280_BRG_U32, NET2280_EPA_RSP,
679 cpu_to_le32(NET2280_CLEAR_NAK_OUT_PACKETS_MODE));
680 P54U_WRITE(NET2280_BRG_U32, NET2280_EPC_RSP,
681 cpu_to_le32(NET2280_CLEAR_NAK_OUT_PACKETS_MODE));
682
683 P54U_WRITE(NET2280_BRG_CFG_U32, PCI_BASE_ADDRESS_2,
684 cpu_to_le32(NET2280_BASE2));
685
686 /* finally done setting up the bridge */
687
688 P54U_WRITE(NET2280_DEV_CFG_U16, 0x10000 | PCI_COMMAND,
689 cpu_to_le32(PCI_COMMAND_MEMORY |
690 PCI_COMMAND_MASTER));
691
692 P54U_WRITE(NET2280_DEV_CFG_U16, 0x10000 | 0x40 /* TRDY timeout */, 0);
693 P54U_WRITE(NET2280_DEV_CFG_U32, 0x10000 | PCI_BASE_ADDRESS_0,
694 cpu_to_le32(P54U_DEV_BASE));
695
696 P54U_WRITE(NET2280_BRG_U32, NET2280_USBIRQENB1, 0);
697 P54U_WRITE(NET2280_BRG_U32, NET2280_IRQSTAT1,
698 cpu_to_le32(NET2280_PCI_INTA_INTERRUPT));
699
700 /* do romboot */
701 P54U_WRITE(NET2280_DEV_U32, &devreg->int_enable, 0);
702
703 P54U_READ(NET2280_DEV_U32, &devreg->ctrl_stat);
704 reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_RESET);
705 reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_RAMBOOT);
706 reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_CLKRUN);
707 P54U_WRITE(NET2280_DEV_U32, &devreg->ctrl_stat, reg);
708
709 mdelay(20);
710
711 reg |= cpu_to_le32(ISL38XX_CTRL_STAT_RESET);
712 P54U_WRITE(NET2280_DEV_U32, &devreg->ctrl_stat, reg);
713
714 mdelay(20);
715
716 reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_RESET);
717 P54U_WRITE(NET2280_DEV_U32, &devreg->ctrl_stat, reg);
718
719 mdelay(100);
720
721 P54U_READ(NET2280_DEV_U32, &devreg->int_ident);
722 P54U_WRITE(NET2280_DEV_U32, &devreg->int_ack, reg);
723
724 /* finally, we can upload firmware now! */
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200725 remains = priv->fw->size;
726 data = priv->fw->data;
Michael Wueff1a592007-09-25 18:11:01 -0700727 offset = ISL38XX_DEV_FIRMWARE_ADDR;
728
729 while (remains) {
730 unsigned int block_len = min(remains, (unsigned int)512);
731 memcpy(buf, data, block_len);
732
733 err = p54u_bulk_msg(priv, P54U_PIPE_DATA, buf, block_len);
734 if (err) {
Christian Lamparter02e37ba2008-11-29 22:39:08 +0100735 dev_err(&priv->udev->dev, "(p54usb) firmware block "
736 "upload failed\n");
Michael Wueff1a592007-09-25 18:11:01 -0700737 goto fail;
738 }
739
740 P54U_WRITE(NET2280_DEV_U32, &devreg->direct_mem_base,
741 cpu_to_le32(0xc0000f00));
742
743 P54U_WRITE(NET2280_DEV_U32,
744 0x0020 | (unsigned long)&devreg->direct_mem_win, 0);
745 P54U_WRITE(NET2280_DEV_U32,
746 0x0020 | (unsigned long)&devreg->direct_mem_win,
747 cpu_to_le32(1));
748
749 P54U_WRITE(NET2280_DEV_U32,
750 0x0024 | (unsigned long)&devreg->direct_mem_win,
751 cpu_to_le32(block_len));
752 P54U_WRITE(NET2280_DEV_U32,
753 0x0028 | (unsigned long)&devreg->direct_mem_win,
754 cpu_to_le32(offset));
755
756 P54U_WRITE(NET2280_DEV_U32, &devreg->dma_addr,
757 cpu_to_le32(NET2280_EPA_FIFO_PCI_ADDR));
758 P54U_WRITE(NET2280_DEV_U32, &devreg->dma_len,
759 cpu_to_le32(block_len >> 2));
760 P54U_WRITE(NET2280_DEV_U32, &devreg->dma_ctrl,
761 cpu_to_le32(ISL38XX_DMA_MASTER_CONTROL_TRIGGER));
762
763 mdelay(10);
764
765 P54U_READ(NET2280_DEV_U32,
766 0x002C | (unsigned long)&devreg->direct_mem_win);
767 if (!(reg & cpu_to_le32(ISL38XX_DMA_STATUS_DONE)) ||
768 !(reg & cpu_to_le32(ISL38XX_DMA_STATUS_READY))) {
Christian Lamparter02e37ba2008-11-29 22:39:08 +0100769 dev_err(&priv->udev->dev, "(p54usb) firmware DMA "
770 "transfer failed\n");
Michael Wueff1a592007-09-25 18:11:01 -0700771 goto fail;
772 }
773
774 P54U_WRITE(NET2280_BRG_U32, NET2280_EPA_STAT,
775 cpu_to_le32(NET2280_FIFO_FLUSH));
776
777 remains -= block_len;
778 data += block_len;
779 offset += block_len;
780 }
781
782 /* do ramboot */
783 P54U_READ(NET2280_DEV_U32, &devreg->ctrl_stat);
784 reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_RESET);
785 reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_CLKRUN);
786 reg |= cpu_to_le32(ISL38XX_CTRL_STAT_RAMBOOT);
787 P54U_WRITE(NET2280_DEV_U32, &devreg->ctrl_stat, reg);
788
789 mdelay(20);
790
791 reg |= cpu_to_le32(ISL38XX_CTRL_STAT_RESET);
792 P54U_WRITE(NET2280_DEV_U32, &devreg->ctrl_stat, reg);
793
794 reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_RESET);
795 P54U_WRITE(NET2280_DEV_U32, &devreg->ctrl_stat, reg);
796
797 mdelay(100);
798
799 P54U_READ(NET2280_DEV_U32, &devreg->int_ident);
800 P54U_WRITE(NET2280_DEV_U32, &devreg->int_ack, reg);
801
802 /* start up the firmware */
803 P54U_WRITE(NET2280_DEV_U32, &devreg->int_enable,
804 cpu_to_le32(ISL38XX_INT_IDENT_INIT));
805
806 P54U_WRITE(NET2280_BRG_U32, NET2280_IRQSTAT1,
807 cpu_to_le32(NET2280_PCI_INTA_INTERRUPT));
808
809 P54U_WRITE(NET2280_BRG_U32, NET2280_USBIRQENB1,
810 cpu_to_le32(NET2280_PCI_INTA_INTERRUPT_ENABLE |
811 NET2280_USB_INTERRUPT_ENABLE));
812
813 P54U_WRITE(NET2280_DEV_U32, &devreg->dev_int,
814 cpu_to_le32(ISL38XX_DEV_INT_RESET));
815
816 err = usb_interrupt_msg(priv->udev,
817 usb_rcvbulkpipe(priv->udev, P54U_PIPE_INT),
818 buf, sizeof(__le32), &alen, 1000);
819 if (err || alen != sizeof(__le32))
820 goto fail;
821
822 P54U_READ(NET2280_DEV_U32, &devreg->int_ident);
823 P54U_WRITE(NET2280_DEV_U32, &devreg->int_ack, reg);
824
825 if (!(reg & cpu_to_le32(ISL38XX_INT_IDENT_INIT)))
826 err = -EINVAL;
827
828 P54U_WRITE(NET2280_BRG_U32, NET2280_USBIRQENB1, 0);
829 P54U_WRITE(NET2280_BRG_U32, NET2280_IRQSTAT1,
830 cpu_to_le32(NET2280_PCI_INTA_INTERRUPT));
831
832#undef P54U_WRITE
833#undef P54U_READ
834
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200835fail:
Michael Wueff1a592007-09-25 18:11:01 -0700836 kfree(buf);
837 return err;
838}
839
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200840static int p54u_load_firmware(struct ieee80211_hw *dev)
841{
842 struct p54u_priv *priv = dev->priv;
843 int err, i;
844
845 BUILD_BUG_ON(ARRAY_SIZE(p54u_fwlist) != __NUM_P54U_HWTYPES);
846
847 for (i = 0; i < __NUM_P54U_HWTYPES; i++)
848 if (p54u_fwlist[i].type == priv->hw_type)
849 break;
850
851 if (i == __NUM_P54U_HWTYPES)
852 return -EOPNOTSUPP;
853
854 err = request_firmware(&priv->fw, p54u_fwlist[i].fw, &priv->udev->dev);
855 if (err) {
856 dev_err(&priv->udev->dev, "(p54usb) cannot load firmware %s "
857 "(%d)!\n", p54u_fwlist[i].fw, err);
858
859 err = request_firmware(&priv->fw, p54u_fwlist[i].fw_legacy,
860 &priv->udev->dev);
861 if (err)
862 return err;
863 }
864
865 err = p54_parse_firmware(dev, priv->fw);
866 if (err)
867 goto out;
868
869 if (priv->common.fw_interface != p54u_fwlist[i].intf) {
870 dev_err(&priv->udev->dev, "wrong firmware, please get "
871 "a firmware for \"%s\" and try again.\n",
872 p54u_fwlist[i].hw);
873 err = -EINVAL;
874 }
875
876out:
877 if (err)
878 release_firmware(priv->fw);
879
880 return err;
881}
882
Michael Wueff1a592007-09-25 18:11:01 -0700883static int p54u_open(struct ieee80211_hw *dev)
884{
885 struct p54u_priv *priv = dev->priv;
886 int err;
887
888 err = p54u_init_urbs(dev);
889 if (err) {
890 return err;
891 }
892
893 priv->common.open = p54u_init_urbs;
894
895 return 0;
896}
897
898static void p54u_stop(struct ieee80211_hw *dev)
899{
900 /* TODO: figure out how to reliably stop the 3887 and net2280 so
901 the hardware is still usable next time we want to start it.
902 until then, we just stop listening to the hardware.. */
903 p54u_free_urbs(dev);
Michael Wueff1a592007-09-25 18:11:01 -0700904}
905
906static int __devinit p54u_probe(struct usb_interface *intf,
907 const struct usb_device_id *id)
908{
909 struct usb_device *udev = interface_to_usbdev(intf);
910 struct ieee80211_hw *dev;
911 struct p54u_priv *priv;
912 int err;
913 unsigned int i, recognized_pipes;
Michael Wueff1a592007-09-25 18:11:01 -0700914
915 dev = p54_init_common(sizeof(*priv));
Christian Lamparter02e37ba2008-11-29 22:39:08 +0100916
Michael Wueff1a592007-09-25 18:11:01 -0700917 if (!dev) {
Christian Lamparter02e37ba2008-11-29 22:39:08 +0100918 dev_err(&udev->dev, "(p54usb) ieee80211 alloc failed\n");
Michael Wueff1a592007-09-25 18:11:01 -0700919 return -ENOMEM;
920 }
921
922 priv = dev->priv;
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200923 priv->hw_type = P54U_INVALID_HW;
Michael Wueff1a592007-09-25 18:11:01 -0700924
925 SET_IEEE80211_DEV(dev, &intf->dev);
926 usb_set_intfdata(intf, dev);
927 priv->udev = udev;
Christian Lamparter69828692008-12-26 19:08:31 +0100928 priv->intf = intf;
929 skb_queue_head_init(&priv->rx_queue);
930 init_usb_anchor(&priv->submitted);
Michael Wueff1a592007-09-25 18:11:01 -0700931
932 usb_get_dev(udev);
933
934 /* really lazy and simple way of figuring out if we're a 3887 */
935 /* TODO: should just stick the identification in the device table */
936 i = intf->altsetting->desc.bNumEndpoints;
937 recognized_pipes = 0;
938 while (i--) {
939 switch (intf->altsetting->endpoint[i].desc.bEndpointAddress) {
940 case P54U_PIPE_DATA:
941 case P54U_PIPE_MGMT:
942 case P54U_PIPE_BRG:
943 case P54U_PIPE_DEV:
944 case P54U_PIPE_DATA | USB_DIR_IN:
945 case P54U_PIPE_MGMT | USB_DIR_IN:
946 case P54U_PIPE_BRG | USB_DIR_IN:
947 case P54U_PIPE_DEV | USB_DIR_IN:
948 case P54U_PIPE_INT | USB_DIR_IN:
949 recognized_pipes++;
950 }
951 }
952 priv->common.open = p54u_open;
Christian Lamparter2b808482008-09-04 12:29:38 +0200953 priv->common.stop = p54u_stop;
Michael Wueff1a592007-09-25 18:11:01 -0700954 if (recognized_pipes < P54U_PIPE_NUMBER) {
Hauke Mehrtens13792572009-05-01 13:12:36 +0200955#ifdef CONFIG_PM
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200956 /* ISL3887 needs a full reset on resume */
957 udev->reset_resume = 1;
Christian Lamparter11791a62010-08-22 22:41:33 +0200958#endif /* CONFIG_PM */
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200959 err = p54u_device_reset(dev);
960
Michael Wueff1a592007-09-25 18:11:01 -0700961 priv->hw_type = P54U_3887;
Christian Lampartera406ac02009-04-25 21:11:55 +0200962 dev->extra_tx_headroom += sizeof(struct lm87_tx_hdr);
963 priv->common.tx_hdr_len = sizeof(struct lm87_tx_hdr);
964 priv->common.tx = p54u_tx_lm87;
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200965 priv->upload_fw = p54u_upload_firmware_3887;
Michael Wueff1a592007-09-25 18:11:01 -0700966 } else {
Christian Lamparter2b808482008-09-04 12:29:38 +0200967 priv->hw_type = P54U_NET2280;
Michael Wueff1a592007-09-25 18:11:01 -0700968 dev->extra_tx_headroom += sizeof(struct net2280_tx_hdr);
969 priv->common.tx_hdr_len = sizeof(struct net2280_tx_hdr);
970 priv->common.tx = p54u_tx_net2280;
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200971 priv->upload_fw = p54u_upload_firmware_net2280;
Christian Lamparter2b808482008-09-04 12:29:38 +0200972 }
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200973 err = p54u_load_firmware(dev);
Michael Wueff1a592007-09-25 18:11:01 -0700974 if (err)
975 goto err_free_dev;
976
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200977 err = priv->upload_fw(dev);
978 if (err)
979 goto err_free_fw;
980
Christian Lamparter7cb77072008-09-01 22:48:51 +0200981 p54u_open(dev);
982 err = p54_read_eeprom(dev);
983 p54u_stop(dev);
Michael Wueff1a592007-09-25 18:11:01 -0700984 if (err)
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200985 goto err_free_fw;
Michael Wueff1a592007-09-25 18:11:01 -0700986
Christian Lamparter2ac71072009-03-05 21:30:10 +0100987 err = p54_register_common(dev, &udev->dev);
988 if (err)
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200989 goto err_free_fw;
Michael Wueff1a592007-09-25 18:11:01 -0700990
Michael Wueff1a592007-09-25 18:11:01 -0700991 return 0;
992
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200993err_free_fw:
994 release_firmware(priv->fw);
995
996err_free_dev:
Christian Lamparterd8c92102009-06-23 10:39:45 -0500997 p54_free_common(dev);
Michael Wueff1a592007-09-25 18:11:01 -0700998 usb_set_intfdata(intf, NULL);
999 usb_put_dev(udev);
1000 return err;
1001}
1002
1003static void __devexit p54u_disconnect(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;
1010
Christian Lamparterd8c92102009-06-23 10:39:45 -05001011 p54_unregister_common(dev);
Michael Wueff1a592007-09-25 18:11:01 -07001012
1013 priv = dev->priv;
1014 usb_put_dev(interface_to_usbdev(intf));
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +02001015 release_firmware(priv->fw);
Michael Wueff1a592007-09-25 18:11:01 -07001016 p54_free_common(dev);
Michael Wueff1a592007-09-25 18:11:01 -07001017}
1018
Christian Lamparter69828692008-12-26 19:08:31 +01001019static int p54u_pre_reset(struct usb_interface *intf)
1020{
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +02001021 struct ieee80211_hw *dev = usb_get_intfdata(intf);
1022
1023 if (!dev)
1024 return -ENODEV;
1025
1026 p54u_stop(dev);
Christian Lamparter69828692008-12-26 19:08:31 +01001027 return 0;
1028}
1029
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +02001030static int p54u_resume(struct usb_interface *intf)
1031{
1032 struct ieee80211_hw *dev = usb_get_intfdata(intf);
1033 struct p54u_priv *priv;
1034
1035 if (!dev)
1036 return -ENODEV;
1037
1038 priv = dev->priv;
1039 if (unlikely(!(priv->upload_fw && priv->fw)))
1040 return 0;
1041
1042 return priv->upload_fw(dev);
1043}
1044
Christian Lamparter69828692008-12-26 19:08:31 +01001045static int p54u_post_reset(struct usb_interface *intf)
1046{
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +02001047 struct ieee80211_hw *dev = usb_get_intfdata(intf);
1048 struct p54u_priv *priv;
1049 int err;
1050
1051 err = p54u_resume(intf);
1052 if (err)
1053 return err;
1054
1055 /* reinitialize old device state */
1056 priv = dev->priv;
1057 if (priv->common.mode != NL80211_IFTYPE_UNSPECIFIED)
1058 ieee80211_restart_hw(dev);
1059
Christian Lamparter69828692008-12-26 19:08:31 +01001060 return 0;
1061}
1062
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +02001063#ifdef CONFIG_PM
1064
1065static int p54u_suspend(struct usb_interface *intf, pm_message_t message)
1066{
1067 return p54u_pre_reset(intf);
1068}
1069
1070#endif /* CONFIG_PM */
1071
Michael Wueff1a592007-09-25 18:11:01 -07001072static struct usb_driver p54u_driver = {
Christian Lamparter32ddf072008-08-08 21:17:37 +02001073 .name = "p54usb",
Michael Wueff1a592007-09-25 18:11:01 -07001074 .id_table = p54u_table,
1075 .probe = p54u_probe,
1076 .disconnect = p54u_disconnect,
Christian Lamparter69828692008-12-26 19:08:31 +01001077 .pre_reset = p54u_pre_reset,
1078 .post_reset = p54u_post_reset,
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +02001079#ifdef CONFIG_PM
1080 .suspend = p54u_suspend,
1081 .resume = p54u_resume,
1082 .reset_resume = p54u_resume,
1083#endif /* CONFIG_PM */
Christian Lamparterfbf95292009-03-05 21:29:51 +01001084 .soft_unbind = 1,
Michael Wueff1a592007-09-25 18:11:01 -07001085};
1086
Greg Kroah-Hartmand632eb12011-11-18 09:44:20 -08001087module_usb_driver(p54u_driver);