blob: 9b344a921e742e29ce483450ea9313186f2d0e23 [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 */
Eduardo Costa56e64172010-12-14 14:37:59 -060064 {USB_DEVICE(0x15a9, 0x0002)}, /* Gemtek WUBI-100GW 802.11g */
Christian Lamparter1a927952010-10-01 22:01:24 +020065 {USB_DEVICE(0x1630, 0x0005)}, /* 2Wire 802.11g USB (v1) / Z-Com */
Christian Lamparter16cad7f2010-12-11 12:19:48 +010066 {USB_DEVICE(0x182d, 0x096b)}, /* Sitecom WL-107 */
Michael Wueff1a592007-09-25 18:11:01 -070067 {USB_DEVICE(0x1915, 0x2234)}, /* Linksys WUSB54G OEM */
68 {USB_DEVICE(0x1915, 0x2235)}, /* Linksys WUSB54G Portable OEM */
69 {USB_DEVICE(0x2001, 0x3701)}, /* DLink DWL-G120 Spinnaker */
70 {USB_DEVICE(0x2001, 0x3703)}, /* DLink DWL-G122 */
71 {USB_DEVICE(0x5041, 0x2234)}, /* Linksys WUSB54G */
72 {USB_DEVICE(0x5041, 0x2235)}, /* Linksys WUSB54G Portable */
73
74 /* Version 2 devices (3887) */
Felix Homann45460022008-05-29 00:36:45 -070075 {USB_DEVICE(0x0471, 0x1230)}, /* Philips CPWUA054/00 */
Michael Wueff1a592007-09-25 18:11:01 -070076 {USB_DEVICE(0x050d, 0x7050)}, /* Belkin F5D7050 ver 1000 */
77 {USB_DEVICE(0x0572, 0x2000)}, /* Cohiba Proto board */
78 {USB_DEVICE(0x0572, 0x2002)}, /* Cohiba Proto board */
Christian Lamparter1a927952010-10-01 22:01:24 +020079 {USB_DEVICE(0x06a9, 0x000e)}, /* Westell 802.11g USB (A90-211WG-01) */
Michiel878e6a42009-01-04 17:22:28 -060080 {USB_DEVICE(0x06b9, 0x0121)}, /* Thomson SpeedTouch 121g */
Michael Wueff1a592007-09-25 18:11:01 -070081 {USB_DEVICE(0x0707, 0xee13)}, /* SMC 2862W-G version 2 */
82 {USB_DEVICE(0x083a, 0x4521)}, /* Siemens Gigaset USB Adapter 54 version 2 */
Jean-François Moine5b9a9192010-02-17 10:59:31 -060083 {USB_DEVICE(0x083a, 0xf503)}, /* Accton FD7050E ver 1010ec */
Michael Wueff1a592007-09-25 18:11:01 -070084 {USB_DEVICE(0x0846, 0x4240)}, /* Netgear WG111 (v2) */
85 {USB_DEVICE(0x0915, 0x2000)}, /* Cohiba Proto board */
86 {USB_DEVICE(0x0915, 0x2002)}, /* Cohiba Proto board */
87 {USB_DEVICE(0x0baf, 0x0118)}, /* U.S. Robotics U5 802.11g Adapter*/
88 {USB_DEVICE(0x0bf8, 0x1009)}, /* FUJITSU E-5400 USB D1700*/
Leann Ogasawara7484bdc2010-06-15 14:01:51 -070089 /* {USB_DEVICE(0x0cde, 0x0006)}, * Medion MD40900 already listed above,
90 * just noting it here for clarity */
Michael Wueff1a592007-09-25 18:11:01 -070091 {USB_DEVICE(0x0cde, 0x0008)}, /* Sagem XG703A */
Christian Lamparterf7f71172009-09-14 23:08:43 +020092 {USB_DEVICE(0x0cde, 0x0015)}, /* Zcomax XG-705A */
Michael Wueff1a592007-09-25 18:11:01 -070093 {USB_DEVICE(0x0d8e, 0x3762)}, /* DLink DWL-G120 Cohiba */
Jan Slupski43557e12008-03-10 22:41:18 -070094 {USB_DEVICE(0x124a, 0x4025)}, /* IOGear GWU513 (GW3887IK chip) */
Christian Lamparterec366eb2008-10-13 23:41:53 +020095 {USB_DEVICE(0x1260, 0xee22)}, /* SMC 2862W-G version 2 */
John W. Linville387e1002008-02-20 15:06:02 -050096 {USB_DEVICE(0x13b1, 0x000a)}, /* Linksys WUSB54G ver 2 */
Martti Huttunenc1098102007-10-04 00:06:00 -040097 {USB_DEVICE(0x13B1, 0x000C)}, /* Linksys WUSB54AG */
Christian Lampartere3062402009-03-29 22:50:28 +020098 {USB_DEVICE(0x1413, 0x5400)}, /* Telsey 802.11g USB2.0 Adapter */
Michael Wueff1a592007-09-25 18:11:01 -070099 {USB_DEVICE(0x1435, 0x0427)}, /* Inventel UR054G */
Christian Lamparter1a927952010-10-01 22:01:24 +0200100 {USB_DEVICE(0x1668, 0x1050)}, /* Actiontec 802UIG-1 */
Christian Lamparter2b799a62011-02-26 12:58:06 +0100101 {USB_DEVICE(0x1740, 0x1000)}, /* Senao NUB-350 */
Michael Wueff1a592007-09-25 18:11:01 -0700102 {USB_DEVICE(0x2001, 0x3704)}, /* DLink DWL-G122 rev A2 */
Christian Lamparter16cad7f2010-12-11 12:19:48 +0100103 {USB_DEVICE(0x2001, 0x3705)}, /* D-Link DWL-G120 rev C1 */
Jason Dravet0f666a02010-06-05 15:08:29 -0500104 {USB_DEVICE(0x413c, 0x5513)}, /* Dell WLA3310 USB Wireless Adapter */
Michael Wueff1a592007-09-25 18:11:01 -0700105 {USB_DEVICE(0x413c, 0x8102)}, /* Spinnaker DUT */
106 {USB_DEVICE(0x413c, 0x8104)}, /* Cohiba Proto board */
107 {}
108};
109
110MODULE_DEVICE_TABLE(usb, p54u_table);
111
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200112static const struct {
113 u32 intf;
114 enum p54u_hw_type type;
Samuel Ortiz328d84f2009-05-27 10:12:51 +0200115 const char *fw;
116 const char *fw_legacy;
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200117 char hw[20];
118} p54u_fwlist[__NUM_P54U_HWTYPES] = {
119 {
120 .type = P54U_NET2280,
121 .intf = FW_LM86,
122 .fw = "isl3886usb",
123 .fw_legacy = "isl3890usb",
124 .hw = "ISL3886 + net2280",
125 },
126 {
127 .type = P54U_3887,
128 .intf = FW_LM87,
129 .fw = "isl3887usb",
130 .fw_legacy = "isl3887usb_bare",
131 .hw = "ISL3887",
132 },
133};
134
Michael Wueff1a592007-09-25 18:11:01 -0700135static void p54u_rx_cb(struct urb *urb)
136{
137 struct sk_buff *skb = (struct sk_buff *) urb->context;
138 struct p54u_rx_info *info = (struct p54u_rx_info *)skb->cb;
139 struct ieee80211_hw *dev = info->dev;
140 struct p54u_priv *priv = dev->priv;
141
Christian Lamparterdd397dc2008-12-09 15:14:37 +0100142 skb_unlink(skb, &priv->rx_queue);
143
Michael Wueff1a592007-09-25 18:11:01 -0700144 if (unlikely(urb->status)) {
Christian Lamparterdd397dc2008-12-09 15:14:37 +0100145 dev_kfree_skb_irq(skb);
Michael Wueff1a592007-09-25 18:11:01 -0700146 return;
147 }
148
Michael Wueff1a592007-09-25 18:11:01 -0700149 skb_put(skb, urb->actual_length);
Christian Lamparter2b808482008-09-04 12:29:38 +0200150
151 if (priv->hw_type == P54U_NET2280)
152 skb_pull(skb, priv->common.tx_hdr_len);
153 if (priv->common.fw_interface == FW_LM87) {
154 skb_pull(skb, 4);
155 skb_put(skb, 4);
156 }
Michael Wueff1a592007-09-25 18:11:01 -0700157
158 if (p54_rx(dev, skb)) {
Christian Lamparter4e416a62008-09-01 22:48:41 +0200159 skb = dev_alloc_skb(priv->common.rx_mtu + 32);
Michael Wueff1a592007-09-25 18:11:01 -0700160 if (unlikely(!skb)) {
Michael Wueff1a592007-09-25 18:11:01 -0700161 /* TODO check rx queue length and refill *somewhere* */
162 return;
163 }
164
165 info = (struct p54u_rx_info *) skb->cb;
166 info->urb = urb;
167 info->dev = dev;
168 urb->transfer_buffer = skb_tail_pointer(skb);
169 urb->context = skb;
Michael Wueff1a592007-09-25 18:11:01 -0700170 } else {
Christian Lamparter2b808482008-09-04 12:29:38 +0200171 if (priv->hw_type == P54U_NET2280)
172 skb_push(skb, priv->common.tx_hdr_len);
173 if (priv->common.fw_interface == FW_LM87) {
174 skb_push(skb, 4);
175 skb_put(skb, 4);
176 }
Christian Lamparterd47c3ce2008-08-13 23:41:48 +0200177 skb_reset_tail_pointer(skb);
Michael Wueff1a592007-09-25 18:11:01 -0700178 skb_trim(skb, 0);
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200179 urb->transfer_buffer = skb_tail_pointer(skb);
Michael Wueff1a592007-09-25 18:11:01 -0700180 }
Christian Lamparterdd397dc2008-12-09 15:14:37 +0100181 skb_queue_tail(&priv->rx_queue, skb);
182 usb_anchor_urb(urb, &priv->submitted);
183 if (usb_submit_urb(urb, GFP_ATOMIC)) {
184 skb_unlink(skb, &priv->rx_queue);
185 usb_unanchor_urb(urb);
186 dev_kfree_skb_irq(skb);
187 }
Michael Wueff1a592007-09-25 18:11:01 -0700188}
189
Christian Lamparter0a5ec962008-12-14 15:05:42 +0100190static void p54u_tx_cb(struct urb *urb)
Christian Lamparterb92f30d2008-10-15 04:07:16 +0200191{
192 struct sk_buff *skb = urb->context;
Joe Perchesb2767362010-11-30 13:42:08 -0800193 struct ieee80211_hw *dev =
Christian Lamparterb92f30d2008-10-15 04:07:16 +0200194 usb_get_intfdata(usb_ifnum_to_if(urb->dev, 0));
195
Christian Lampartere2fe1542009-01-20 00:27:57 +0100196 p54_free_skb(dev, skb);
Christian Lamparterdd397dc2008-12-09 15:14:37 +0100197}
198
199static void p54u_tx_dummy_cb(struct urb *urb) { }
200
201static void p54u_free_urbs(struct ieee80211_hw *dev)
202{
203 struct p54u_priv *priv = dev->priv;
204 usb_kill_anchored_urbs(&priv->submitted);
Christian Lamparterb92f30d2008-10-15 04:07:16 +0200205}
206
Michael Wueff1a592007-09-25 18:11:01 -0700207static int p54u_init_urbs(struct ieee80211_hw *dev)
208{
209 struct p54u_priv *priv = dev->priv;
Christian Lamparterdd397dc2008-12-09 15:14:37 +0100210 struct urb *entry = NULL;
Michael Wueff1a592007-09-25 18:11:01 -0700211 struct sk_buff *skb;
212 struct p54u_rx_info *info;
Christian Lamparterdd397dc2008-12-09 15:14:37 +0100213 int ret = 0;
Michael Wueff1a592007-09-25 18:11:01 -0700214
215 while (skb_queue_len(&priv->rx_queue) < 32) {
Christian Lamparter4e416a62008-09-01 22:48:41 +0200216 skb = __dev_alloc_skb(priv->common.rx_mtu + 32, GFP_KERNEL);
Christian Lamparterdd397dc2008-12-09 15:14:37 +0100217 if (!skb) {
218 ret = -ENOMEM;
219 goto err;
220 }
Michael Wueff1a592007-09-25 18:11:01 -0700221 entry = usb_alloc_urb(0, GFP_KERNEL);
222 if (!entry) {
Christian Lamparterdd397dc2008-12-09 15:14:37 +0100223 ret = -ENOMEM;
224 goto err;
Michael Wueff1a592007-09-25 18:11:01 -0700225 }
Christian Lamparterdd397dc2008-12-09 15:14:37 +0100226
Christian Lamparter4e416a62008-09-01 22:48:41 +0200227 usb_fill_bulk_urb(entry, priv->udev,
228 usb_rcvbulkpipe(priv->udev, P54U_PIPE_DATA),
229 skb_tail_pointer(skb),
230 priv->common.rx_mtu + 32, p54u_rx_cb, skb);
Michael Wueff1a592007-09-25 18:11:01 -0700231 info = (struct p54u_rx_info *) skb->cb;
232 info->urb = entry;
233 info->dev = dev;
234 skb_queue_tail(&priv->rx_queue, skb);
Christian Lamparterdd397dc2008-12-09 15:14:37 +0100235
236 usb_anchor_urb(entry, &priv->submitted);
237 ret = usb_submit_urb(entry, GFP_KERNEL);
238 if (ret) {
239 skb_unlink(skb, &priv->rx_queue);
240 usb_unanchor_urb(entry);
241 goto err;
242 }
243 usb_free_urb(entry);
244 entry = NULL;
Michael Wueff1a592007-09-25 18:11:01 -0700245 }
246
247 return 0;
Michael Wueff1a592007-09-25 18:11:01 -0700248
Christian Lamparterdd397dc2008-12-09 15:14:37 +0100249 err:
250 usb_free_urb(entry);
251 kfree_skb(skb);
252 p54u_free_urbs(dev);
253 return ret;
Michael Wueff1a592007-09-25 18:11:01 -0700254}
255
Johannes Bergc9127652008-12-01 18:19:36 +0100256static __le32 p54u_lm87_chksum(const __le32 *data, size_t length)
Christian Lamparter2b808482008-09-04 12:29:38 +0200257{
Larry Finger1f1c0e32008-09-25 14:54:28 -0500258 u32 chk = 0;
Christian Lamparter2b808482008-09-04 12:29:38 +0200259
260 length >>= 2;
261 while (length--) {
Johannes Bergc9127652008-12-01 18:19:36 +0100262 chk ^= le32_to_cpu(*data++);
Christian Lamparter2b808482008-09-04 12:29:38 +0200263 chk = (chk >> 5) ^ (chk << 3);
264 }
265
Larry Finger1f1c0e32008-09-25 14:54:28 -0500266 return cpu_to_le32(chk);
Christian Lamparter2b808482008-09-04 12:29:38 +0200267}
268
Christian Lamparter0a5ec962008-12-14 15:05:42 +0100269static void p54u_tx_lm87(struct ieee80211_hw *dev, struct sk_buff *skb)
Christian Lamparter2b808482008-09-04 12:29:38 +0200270{
271 struct p54u_priv *priv = dev->priv;
272 struct urb *data_urb;
Christian Lampartere2fe1542009-01-20 00:27:57 +0100273 struct lm87_tx_hdr *hdr = (void *)skb->data - sizeof(*hdr);
Christian Lamparter2b808482008-09-04 12:29:38 +0200274
275 data_urb = usb_alloc_urb(0, GFP_ATOMIC);
Christian Lamparter6d541a62009-07-06 15:17:56 +0200276 if (!data_urb) {
277 p54_free_skb(dev, skb);
Christian Lamparter2b808482008-09-04 12:29:38 +0200278 return;
Christian Lamparter6d541a62009-07-06 15:17:56 +0200279 }
Christian Lamparter2b808482008-09-04 12:29:38 +0200280
Christian Lampartere2fe1542009-01-20 00:27:57 +0100281 hdr->chksum = p54u_lm87_chksum((__le32 *)skb->data, skb->len);
282 hdr->device_addr = ((struct p54_hdr *)skb->data)->req_id;
Christian Lamparter2b808482008-09-04 12:29:38 +0200283
284 usb_fill_bulk_urb(data_urb, priv->udev,
Christian Lamparterb92f30d2008-10-15 04:07:16 +0200285 usb_sndbulkpipe(priv->udev, P54U_PIPE_DATA),
Christian Lampartere2fe1542009-01-20 00:27:57 +0100286 hdr, skb->len + sizeof(*hdr), FREE_AFTER_TX(skb) ?
287 p54u_tx_cb : p54u_tx_dummy_cb, skb);
Christian Lamparter00627f22008-12-20 02:21:56 +0100288 data_urb->transfer_flags |= URB_ZERO_PACKET;
Christian Lamparter2b808482008-09-04 12:29:38 +0200289
Christian Lamparterdd397dc2008-12-09 15:14:37 +0100290 usb_anchor_urb(data_urb, &priv->submitted);
291 if (usb_submit_urb(data_urb, GFP_ATOMIC)) {
292 usb_unanchor_urb(data_urb);
Christian Lamparterdd397dc2008-12-09 15:14:37 +0100293 p54_free_skb(dev, skb);
294 }
295 usb_free_urb(data_urb);
Christian Lamparter2b808482008-09-04 12:29:38 +0200296}
297
Christian Lamparter0a5ec962008-12-14 15:05:42 +0100298static void p54u_tx_net2280(struct ieee80211_hw *dev, struct sk_buff *skb)
Michael Wueff1a592007-09-25 18:11:01 -0700299{
300 struct p54u_priv *priv = dev->priv;
Christian Lamparter6d541a62009-07-06 15:17:56 +0200301 struct urb *int_urb = NULL, *data_urb = NULL;
Christian Lampartere2fe1542009-01-20 00:27:57 +0100302 struct net2280_tx_hdr *hdr = (void *)skb->data - sizeof(*hdr);
Christian Lamparter6d541a62009-07-06 15:17:56 +0200303 struct net2280_reg_write *reg = NULL;
304 int err = -ENOMEM;
Michael Wueff1a592007-09-25 18:11:01 -0700305
306 reg = kmalloc(sizeof(*reg), GFP_ATOMIC);
307 if (!reg)
Christian Lamparter6d541a62009-07-06 15:17:56 +0200308 goto out;
Michael Wueff1a592007-09-25 18:11:01 -0700309
310 int_urb = usb_alloc_urb(0, GFP_ATOMIC);
Christian Lamparter6d541a62009-07-06 15:17:56 +0200311 if (!int_urb)
312 goto out;
Michael Wueff1a592007-09-25 18:11:01 -0700313
314 data_urb = usb_alloc_urb(0, GFP_ATOMIC);
Christian Lamparter6d541a62009-07-06 15:17:56 +0200315 if (!data_urb)
316 goto out;
Michael Wueff1a592007-09-25 18:11:01 -0700317
318 reg->port = cpu_to_le16(NET2280_DEV_U32);
319 reg->addr = cpu_to_le32(P54U_DEV_BASE);
320 reg->val = cpu_to_le32(ISL38XX_DEV_INT_DATA);
321
Michael Wueff1a592007-09-25 18:11:01 -0700322 memset(hdr, 0, sizeof(*hdr));
Christian Lampartere2fe1542009-01-20 00:27:57 +0100323 hdr->len = cpu_to_le16(skb->len);
324 hdr->device_addr = ((struct p54_hdr *) skb->data)->req_id;
Michael Wueff1a592007-09-25 18:11:01 -0700325
326 usb_fill_bulk_urb(int_urb, priv->udev,
327 usb_sndbulkpipe(priv->udev, P54U_PIPE_DEV), reg, sizeof(*reg),
Christian Lamparterdd397dc2008-12-09 15:14:37 +0100328 p54u_tx_dummy_cb, dev);
329
330 /*
Christian Lamparter6d541a62009-07-06 15:17:56 +0200331 * URB_FREE_BUFFER triggers a code path in the USB subsystem that will
332 * free what is inside the transfer_buffer after the last reference to
333 * the int_urb is dropped.
Christian Lamparterdd397dc2008-12-09 15:14:37 +0100334 */
Christian Lamparterb4068a82009-01-20 23:11:21 +0100335 int_urb->transfer_flags |= URB_FREE_BUFFER | URB_ZERO_PACKET;
Christian Lamparter6d541a62009-07-06 15:17:56 +0200336 reg = NULL;
Michael Wueff1a592007-09-25 18:11:01 -0700337
338 usb_fill_bulk_urb(data_urb, priv->udev,
Christian Lamparterb92f30d2008-10-15 04:07:16 +0200339 usb_sndbulkpipe(priv->udev, P54U_PIPE_DATA),
Christian Lampartere2fe1542009-01-20 00:27:57 +0100340 hdr, skb->len + sizeof(*hdr), FREE_AFTER_TX(skb) ?
341 p54u_tx_cb : p54u_tx_dummy_cb, skb);
Christian Lamparterb4068a82009-01-20 23:11:21 +0100342 data_urb->transfer_flags |= URB_ZERO_PACKET;
Christian Lamparterdd397dc2008-12-09 15:14:37 +0100343
344 usb_anchor_urb(int_urb, &priv->submitted);
345 err = usb_submit_urb(int_urb, GFP_ATOMIC);
346 if (err) {
347 usb_unanchor_urb(int_urb);
348 goto out;
349 }
350
351 usb_anchor_urb(data_urb, &priv->submitted);
352 err = usb_submit_urb(data_urb, GFP_ATOMIC);
353 if (err) {
354 usb_unanchor_urb(data_urb);
355 goto out;
356 }
Christian Lamparter6d541a62009-07-06 15:17:56 +0200357out:
Christian Lamparterdd397dc2008-12-09 15:14:37 +0100358 usb_free_urb(int_urb);
359 usb_free_urb(data_urb);
360
361 if (err) {
Christian Lamparter6d541a62009-07-06 15:17:56 +0200362 kfree(reg);
Christian Lamparterdd397dc2008-12-09 15:14:37 +0100363 p54_free_skb(dev, skb);
364 }
Michael Wueff1a592007-09-25 18:11:01 -0700365}
366
367static int p54u_write(struct p54u_priv *priv,
368 struct net2280_reg_write *buf,
369 enum net2280_op_type type,
370 __le32 addr, __le32 val)
371{
372 unsigned int ep;
373 int alen;
374
375 if (type & 0x0800)
376 ep = usb_sndbulkpipe(priv->udev, P54U_PIPE_DEV);
377 else
378 ep = usb_sndbulkpipe(priv->udev, P54U_PIPE_BRG);
379
380 buf->port = cpu_to_le16(type);
381 buf->addr = addr;
382 buf->val = val;
383
384 return usb_bulk_msg(priv->udev, ep, buf, sizeof(*buf), &alen, 1000);
385}
386
387static int p54u_read(struct p54u_priv *priv, void *buf,
388 enum net2280_op_type type,
389 __le32 addr, __le32 *val)
390{
391 struct net2280_reg_read *read = buf;
392 __le32 *reg = buf;
393 unsigned int ep;
394 int alen, err;
395
396 if (type & 0x0800)
397 ep = P54U_PIPE_DEV;
398 else
399 ep = P54U_PIPE_BRG;
400
401 read->port = cpu_to_le16(type);
402 read->addr = addr;
403
404 err = usb_bulk_msg(priv->udev, usb_sndbulkpipe(priv->udev, ep),
405 read, sizeof(*read), &alen, 1000);
406 if (err)
407 return err;
408
409 err = usb_bulk_msg(priv->udev, usb_rcvbulkpipe(priv->udev, ep),
410 reg, sizeof(*reg), &alen, 1000);
411 if (err)
412 return err;
413
414 *val = *reg;
415 return 0;
416}
417
418static int p54u_bulk_msg(struct p54u_priv *priv, unsigned int ep,
419 void *data, size_t len)
420{
421 int alen;
422 return usb_bulk_msg(priv->udev, usb_sndbulkpipe(priv->udev, ep),
423 data, len, &alen, 2000);
424}
425
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200426static int p54u_device_reset(struct ieee80211_hw *dev)
Christian Lamparter69828692008-12-26 19:08:31 +0100427{
428 struct p54u_priv *priv = dev->priv;
Christian Lamparterc88a7682009-01-16 20:24:31 +0100429 int ret, lock = (priv->intf->condition != USB_INTERFACE_BINDING);
Christian Lamparter69828692008-12-26 19:08:31 +0100430
Christian Lamparterc88a7682009-01-16 20:24:31 +0100431 if (lock) {
432 ret = usb_lock_device_for_reset(priv->udev, priv->intf);
433 if (ret < 0) {
434 dev_err(&priv->udev->dev, "(p54usb) unable to lock "
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200435 "device for reset (%d)!\n", ret);
Christian Lamparterc88a7682009-01-16 20:24:31 +0100436 return ret;
437 }
Christian Lamparter69828692008-12-26 19:08:31 +0100438 }
439
440 ret = usb_reset_device(priv->udev);
441 if (lock)
442 usb_unlock_device(priv->udev);
443
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200444 if (ret)
Christian Lamparter69828692008-12-26 19:08:31 +0100445 dev_err(&priv->udev->dev, "(p54usb) unable to reset "
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200446 "device (%d)!\n", ret);
447
448 return ret;
449}
450
451static const char p54u_romboot_3887[] = "~~~~";
452static int p54u_firmware_reset_3887(struct ieee80211_hw *dev)
453{
454 struct p54u_priv *priv = dev->priv;
Larry Finger21d6c272009-11-11 18:02:29 -0600455 u8 *buf;
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200456 int ret;
Christian Lamparter69828692008-12-26 19:08:31 +0100457
Julia Lawall27b81bb2010-05-15 23:22:55 +0200458 buf = kmemdup(p54u_romboot_3887, 4, GFP_KERNEL);
Larry Finger21d6c272009-11-11 18:02:29 -0600459 if (!buf)
460 return -ENOMEM;
Christian Lamparter69828692008-12-26 19:08:31 +0100461 ret = p54u_bulk_msg(priv, P54U_PIPE_DATA,
Larry Finger21d6c272009-11-11 18:02:29 -0600462 buf, 4);
463 kfree(buf);
Christian Lamparter69828692008-12-26 19:08:31 +0100464 if (ret)
465 dev_err(&priv->udev->dev, "(p54usb) unable to jump to "
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200466 "boot ROM (%d)!\n", ret);
Christian Lamparter69828692008-12-26 19:08:31 +0100467
468 return ret;
469}
470
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200471static const char p54u_firmware_upload_3887[] = "<\r";
Michael Wueff1a592007-09-25 18:11:01 -0700472static int p54u_upload_firmware_3887(struct ieee80211_hw *dev)
473{
Michael Wueff1a592007-09-25 18:11:01 -0700474 struct p54u_priv *priv = dev->priv;
Michael Wueff1a592007-09-25 18:11:01 -0700475 int err, alen;
476 u8 carry = 0;
David Woodhouse8b72eb42008-05-24 00:08:55 +0100477 u8 *buf, *tmp;
478 const u8 *data;
Michael Wueff1a592007-09-25 18:11:01 -0700479 unsigned int left, remains, block_size;
480 struct x2_header *hdr;
481 unsigned long timeout;
482
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200483 err = p54u_firmware_reset_3887(dev);
484 if (err)
485 return err;
486
Michael Wueff1a592007-09-25 18:11:01 -0700487 tmp = buf = kmalloc(P54U_FW_BLOCK, GFP_KERNEL);
488 if (!buf) {
Christian Lamparter02e37ba2008-11-29 22:39:08 +0100489 dev_err(&priv->udev->dev, "(p54usb) cannot allocate firmware"
490 "upload buffer!\n");
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200491 return -ENOMEM;
Michael Wueff1a592007-09-25 18:11:01 -0700492 }
493
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200494 left = block_size = min((size_t)P54U_FW_BLOCK, priv->fw->size);
Christian Lamparter69828692008-12-26 19:08:31 +0100495 strcpy(buf, p54u_firmware_upload_3887);
496 left -= strlen(p54u_firmware_upload_3887);
497 tmp += strlen(p54u_firmware_upload_3887);
Michael Wueff1a592007-09-25 18:11:01 -0700498
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200499 data = priv->fw->data;
500 remains = priv->fw->size;
Michael Wueff1a592007-09-25 18:11:01 -0700501
Christian Lamparter69828692008-12-26 19:08:31 +0100502 hdr = (struct x2_header *)(buf + strlen(p54u_firmware_upload_3887));
Michael Wueff1a592007-09-25 18:11:01 -0700503 memcpy(hdr->signature, X2_SIGNATURE, X2_SIGNATURE_SIZE);
504 hdr->fw_load_addr = cpu_to_le32(ISL38XX_DEV_FIRMWARE_ADDR);
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200505 hdr->fw_length = cpu_to_le32(priv->fw->size);
Michael Wueff1a592007-09-25 18:11:01 -0700506 hdr->crc = cpu_to_le32(~crc32_le(~0, (void *)&hdr->fw_load_addr,
507 sizeof(u32)*2));
508 left -= sizeof(*hdr);
509 tmp += sizeof(*hdr);
510
511 while (remains) {
512 while (left--) {
513 if (carry) {
514 *tmp++ = carry;
515 carry = 0;
516 remains--;
517 continue;
518 }
519 switch (*data) {
520 case '~':
521 *tmp++ = '}';
522 carry = '^';
523 break;
524 case '}':
525 *tmp++ = '}';
526 carry = ']';
527 break;
528 default:
529 *tmp++ = *data;
530 remains--;
531 break;
532 }
533 data++;
534 }
535
536 err = p54u_bulk_msg(priv, P54U_PIPE_DATA, buf, block_size);
537 if (err) {
Christian Lamparter02e37ba2008-11-29 22:39:08 +0100538 dev_err(&priv->udev->dev, "(p54usb) firmware "
539 "upload failed!\n");
Michael Wueff1a592007-09-25 18:11:01 -0700540 goto err_upload_failed;
541 }
542
543 tmp = buf;
544 left = block_size = min((unsigned int)P54U_FW_BLOCK, remains);
545 }
546
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200547 *((__le32 *)buf) = cpu_to_le32(~crc32_le(~0, priv->fw->data,
548 priv->fw->size));
Michael Wueff1a592007-09-25 18:11:01 -0700549 err = p54u_bulk_msg(priv, P54U_PIPE_DATA, buf, sizeof(u32));
550 if (err) {
Christian Lamparter02e37ba2008-11-29 22:39:08 +0100551 dev_err(&priv->udev->dev, "(p54usb) firmware upload failed!\n");
Michael Wueff1a592007-09-25 18:11:01 -0700552 goto err_upload_failed;
553 }
Michael Wueff1a592007-09-25 18:11:01 -0700554 timeout = jiffies + msecs_to_jiffies(1000);
555 while (!(err = usb_bulk_msg(priv->udev,
556 usb_rcvbulkpipe(priv->udev, P54U_PIPE_DATA), buf, 128, &alen, 1000))) {
557 if (alen > 2 && !memcmp(buf, "OK", 2))
558 break;
559
560 if (alen > 5 && !memcmp(buf, "ERROR", 5)) {
Michael Wueff1a592007-09-25 18:11:01 -0700561 err = -EINVAL;
562 break;
563 }
564
565 if (time_after(jiffies, timeout)) {
Christian Lamparter02e37ba2008-11-29 22:39:08 +0100566 dev_err(&priv->udev->dev, "(p54usb) firmware boot "
567 "timed out!\n");
Michael Wueff1a592007-09-25 18:11:01 -0700568 err = -ETIMEDOUT;
569 break;
570 }
571 }
Christian Lamparter02e37ba2008-11-29 22:39:08 +0100572 if (err) {
573 dev_err(&priv->udev->dev, "(p54usb) firmware upload failed!\n");
Michael Wueff1a592007-09-25 18:11:01 -0700574 goto err_upload_failed;
Christian Lamparter02e37ba2008-11-29 22:39:08 +0100575 }
Michael Wueff1a592007-09-25 18:11:01 -0700576
577 buf[0] = 'g';
578 buf[1] = '\r';
579 err = p54u_bulk_msg(priv, P54U_PIPE_DATA, buf, 2);
580 if (err) {
Christian Lamparter02e37ba2008-11-29 22:39:08 +0100581 dev_err(&priv->udev->dev, "(p54usb) firmware boot failed!\n");
Michael Wueff1a592007-09-25 18:11:01 -0700582 goto err_upload_failed;
583 }
584
585 timeout = jiffies + msecs_to_jiffies(1000);
586 while (!(err = usb_bulk_msg(priv->udev,
587 usb_rcvbulkpipe(priv->udev, P54U_PIPE_DATA), buf, 128, &alen, 1000))) {
588 if (alen > 0 && buf[0] == 'g')
589 break;
590
591 if (time_after(jiffies, timeout)) {
592 err = -ETIMEDOUT;
593 break;
594 }
595 }
596 if (err)
597 goto err_upload_failed;
598
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200599err_upload_failed:
Michael Wueff1a592007-09-25 18:11:01 -0700600 kfree(buf);
Michael Wueff1a592007-09-25 18:11:01 -0700601 return err;
602}
603
604static int p54u_upload_firmware_net2280(struct ieee80211_hw *dev)
605{
606 struct p54u_priv *priv = dev->priv;
Michael Wueff1a592007-09-25 18:11:01 -0700607 const struct p54p_csr *devreg = (const struct p54p_csr *) P54U_DEV_BASE;
608 int err, alen;
609 void *buf;
610 __le32 reg;
611 unsigned int remains, offset;
David Woodhouse8b72eb42008-05-24 00:08:55 +0100612 const u8 *data;
Michael Wueff1a592007-09-25 18:11:01 -0700613
614 buf = kmalloc(512, GFP_KERNEL);
615 if (!buf) {
Christian Lamparter02e37ba2008-11-29 22:39:08 +0100616 dev_err(&priv->udev->dev, "(p54usb) firmware buffer "
617 "alloc failed!\n");
Michael Wueff1a592007-09-25 18:11:01 -0700618 return -ENOMEM;
619 }
620
Michael Wueff1a592007-09-25 18:11:01 -0700621#define P54U_WRITE(type, addr, data) \
622 do {\
623 err = p54u_write(priv, buf, type,\
624 cpu_to_le32((u32)(unsigned long)addr), data);\
625 if (err) \
626 goto fail;\
627 } while (0)
628
629#define P54U_READ(type, addr) \
630 do {\
631 err = p54u_read(priv, buf, type,\
632 cpu_to_le32((u32)(unsigned long)addr), &reg);\
633 if (err)\
634 goto fail;\
635 } while (0)
636
637 /* power down net2280 bridge */
638 P54U_READ(NET2280_BRG_U32, NET2280_GPIOCTL);
639 reg |= cpu_to_le32(P54U_BRG_POWER_DOWN);
640 reg &= cpu_to_le32(~P54U_BRG_POWER_UP);
641 P54U_WRITE(NET2280_BRG_U32, NET2280_GPIOCTL, reg);
642
643 mdelay(100);
644
645 /* power up bridge */
646 reg |= cpu_to_le32(P54U_BRG_POWER_UP);
647 reg &= cpu_to_le32(~P54U_BRG_POWER_DOWN);
648 P54U_WRITE(NET2280_BRG_U32, NET2280_GPIOCTL, reg);
649
650 mdelay(100);
651
652 P54U_WRITE(NET2280_BRG_U32, NET2280_DEVINIT,
653 cpu_to_le32(NET2280_CLK_30Mhz |
654 NET2280_PCI_ENABLE |
655 NET2280_PCI_SOFT_RESET));
656
657 mdelay(20);
658
659 P54U_WRITE(NET2280_BRG_CFG_U16, PCI_COMMAND,
660 cpu_to_le32(PCI_COMMAND_MEMORY |
661 PCI_COMMAND_MASTER));
662
663 P54U_WRITE(NET2280_BRG_CFG_U32, PCI_BASE_ADDRESS_0,
664 cpu_to_le32(NET2280_BASE));
665
666 P54U_READ(NET2280_BRG_CFG_U16, PCI_STATUS);
667 reg |= cpu_to_le32(PCI_STATUS_REC_MASTER_ABORT);
668 P54U_WRITE(NET2280_BRG_CFG_U16, PCI_STATUS, reg);
669
670 // TODO: we really need this?
671 P54U_READ(NET2280_BRG_U32, NET2280_RELNUM);
672
673 P54U_WRITE(NET2280_BRG_U32, NET2280_EPA_RSP,
674 cpu_to_le32(NET2280_CLEAR_NAK_OUT_PACKETS_MODE));
675 P54U_WRITE(NET2280_BRG_U32, NET2280_EPC_RSP,
676 cpu_to_le32(NET2280_CLEAR_NAK_OUT_PACKETS_MODE));
677
678 P54U_WRITE(NET2280_BRG_CFG_U32, PCI_BASE_ADDRESS_2,
679 cpu_to_le32(NET2280_BASE2));
680
681 /* finally done setting up the bridge */
682
683 P54U_WRITE(NET2280_DEV_CFG_U16, 0x10000 | PCI_COMMAND,
684 cpu_to_le32(PCI_COMMAND_MEMORY |
685 PCI_COMMAND_MASTER));
686
687 P54U_WRITE(NET2280_DEV_CFG_U16, 0x10000 | 0x40 /* TRDY timeout */, 0);
688 P54U_WRITE(NET2280_DEV_CFG_U32, 0x10000 | PCI_BASE_ADDRESS_0,
689 cpu_to_le32(P54U_DEV_BASE));
690
691 P54U_WRITE(NET2280_BRG_U32, NET2280_USBIRQENB1, 0);
692 P54U_WRITE(NET2280_BRG_U32, NET2280_IRQSTAT1,
693 cpu_to_le32(NET2280_PCI_INTA_INTERRUPT));
694
695 /* do romboot */
696 P54U_WRITE(NET2280_DEV_U32, &devreg->int_enable, 0);
697
698 P54U_READ(NET2280_DEV_U32, &devreg->ctrl_stat);
699 reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_RESET);
700 reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_RAMBOOT);
701 reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_CLKRUN);
702 P54U_WRITE(NET2280_DEV_U32, &devreg->ctrl_stat, reg);
703
704 mdelay(20);
705
706 reg |= cpu_to_le32(ISL38XX_CTRL_STAT_RESET);
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(100);
715
716 P54U_READ(NET2280_DEV_U32, &devreg->int_ident);
717 P54U_WRITE(NET2280_DEV_U32, &devreg->int_ack, reg);
718
719 /* finally, we can upload firmware now! */
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200720 remains = priv->fw->size;
721 data = priv->fw->data;
Michael Wueff1a592007-09-25 18:11:01 -0700722 offset = ISL38XX_DEV_FIRMWARE_ADDR;
723
724 while (remains) {
725 unsigned int block_len = min(remains, (unsigned int)512);
726 memcpy(buf, data, block_len);
727
728 err = p54u_bulk_msg(priv, P54U_PIPE_DATA, buf, block_len);
729 if (err) {
Christian Lamparter02e37ba2008-11-29 22:39:08 +0100730 dev_err(&priv->udev->dev, "(p54usb) firmware block "
731 "upload failed\n");
Michael Wueff1a592007-09-25 18:11:01 -0700732 goto fail;
733 }
734
735 P54U_WRITE(NET2280_DEV_U32, &devreg->direct_mem_base,
736 cpu_to_le32(0xc0000f00));
737
738 P54U_WRITE(NET2280_DEV_U32,
739 0x0020 | (unsigned long)&devreg->direct_mem_win, 0);
740 P54U_WRITE(NET2280_DEV_U32,
741 0x0020 | (unsigned long)&devreg->direct_mem_win,
742 cpu_to_le32(1));
743
744 P54U_WRITE(NET2280_DEV_U32,
745 0x0024 | (unsigned long)&devreg->direct_mem_win,
746 cpu_to_le32(block_len));
747 P54U_WRITE(NET2280_DEV_U32,
748 0x0028 | (unsigned long)&devreg->direct_mem_win,
749 cpu_to_le32(offset));
750
751 P54U_WRITE(NET2280_DEV_U32, &devreg->dma_addr,
752 cpu_to_le32(NET2280_EPA_FIFO_PCI_ADDR));
753 P54U_WRITE(NET2280_DEV_U32, &devreg->dma_len,
754 cpu_to_le32(block_len >> 2));
755 P54U_WRITE(NET2280_DEV_U32, &devreg->dma_ctrl,
756 cpu_to_le32(ISL38XX_DMA_MASTER_CONTROL_TRIGGER));
757
758 mdelay(10);
759
760 P54U_READ(NET2280_DEV_U32,
761 0x002C | (unsigned long)&devreg->direct_mem_win);
762 if (!(reg & cpu_to_le32(ISL38XX_DMA_STATUS_DONE)) ||
763 !(reg & cpu_to_le32(ISL38XX_DMA_STATUS_READY))) {
Christian Lamparter02e37ba2008-11-29 22:39:08 +0100764 dev_err(&priv->udev->dev, "(p54usb) firmware DMA "
765 "transfer failed\n");
Michael Wueff1a592007-09-25 18:11:01 -0700766 goto fail;
767 }
768
769 P54U_WRITE(NET2280_BRG_U32, NET2280_EPA_STAT,
770 cpu_to_le32(NET2280_FIFO_FLUSH));
771
772 remains -= block_len;
773 data += block_len;
774 offset += block_len;
775 }
776
777 /* do ramboot */
778 P54U_READ(NET2280_DEV_U32, &devreg->ctrl_stat);
779 reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_RESET);
780 reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_CLKRUN);
781 reg |= cpu_to_le32(ISL38XX_CTRL_STAT_RAMBOOT);
782 P54U_WRITE(NET2280_DEV_U32, &devreg->ctrl_stat, reg);
783
784 mdelay(20);
785
786 reg |= cpu_to_le32(ISL38XX_CTRL_STAT_RESET);
787 P54U_WRITE(NET2280_DEV_U32, &devreg->ctrl_stat, reg);
788
789 reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_RESET);
790 P54U_WRITE(NET2280_DEV_U32, &devreg->ctrl_stat, reg);
791
792 mdelay(100);
793
794 P54U_READ(NET2280_DEV_U32, &devreg->int_ident);
795 P54U_WRITE(NET2280_DEV_U32, &devreg->int_ack, reg);
796
797 /* start up the firmware */
798 P54U_WRITE(NET2280_DEV_U32, &devreg->int_enable,
799 cpu_to_le32(ISL38XX_INT_IDENT_INIT));
800
801 P54U_WRITE(NET2280_BRG_U32, NET2280_IRQSTAT1,
802 cpu_to_le32(NET2280_PCI_INTA_INTERRUPT));
803
804 P54U_WRITE(NET2280_BRG_U32, NET2280_USBIRQENB1,
805 cpu_to_le32(NET2280_PCI_INTA_INTERRUPT_ENABLE |
806 NET2280_USB_INTERRUPT_ENABLE));
807
808 P54U_WRITE(NET2280_DEV_U32, &devreg->dev_int,
809 cpu_to_le32(ISL38XX_DEV_INT_RESET));
810
811 err = usb_interrupt_msg(priv->udev,
812 usb_rcvbulkpipe(priv->udev, P54U_PIPE_INT),
813 buf, sizeof(__le32), &alen, 1000);
814 if (err || alen != sizeof(__le32))
815 goto fail;
816
817 P54U_READ(NET2280_DEV_U32, &devreg->int_ident);
818 P54U_WRITE(NET2280_DEV_U32, &devreg->int_ack, reg);
819
820 if (!(reg & cpu_to_le32(ISL38XX_INT_IDENT_INIT)))
821 err = -EINVAL;
822
823 P54U_WRITE(NET2280_BRG_U32, NET2280_USBIRQENB1, 0);
824 P54U_WRITE(NET2280_BRG_U32, NET2280_IRQSTAT1,
825 cpu_to_le32(NET2280_PCI_INTA_INTERRUPT));
826
827#undef P54U_WRITE
828#undef P54U_READ
829
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200830fail:
Michael Wueff1a592007-09-25 18:11:01 -0700831 kfree(buf);
832 return err;
833}
834
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200835static int p54u_load_firmware(struct ieee80211_hw *dev)
836{
837 struct p54u_priv *priv = dev->priv;
838 int err, i;
839
840 BUILD_BUG_ON(ARRAY_SIZE(p54u_fwlist) != __NUM_P54U_HWTYPES);
841
842 for (i = 0; i < __NUM_P54U_HWTYPES; i++)
843 if (p54u_fwlist[i].type == priv->hw_type)
844 break;
845
846 if (i == __NUM_P54U_HWTYPES)
847 return -EOPNOTSUPP;
848
849 err = request_firmware(&priv->fw, p54u_fwlist[i].fw, &priv->udev->dev);
850 if (err) {
851 dev_err(&priv->udev->dev, "(p54usb) cannot load firmware %s "
852 "(%d)!\n", p54u_fwlist[i].fw, err);
853
854 err = request_firmware(&priv->fw, p54u_fwlist[i].fw_legacy,
855 &priv->udev->dev);
856 if (err)
857 return err;
858 }
859
860 err = p54_parse_firmware(dev, priv->fw);
861 if (err)
862 goto out;
863
864 if (priv->common.fw_interface != p54u_fwlist[i].intf) {
865 dev_err(&priv->udev->dev, "wrong firmware, please get "
866 "a firmware for \"%s\" and try again.\n",
867 p54u_fwlist[i].hw);
868 err = -EINVAL;
869 }
870
871out:
872 if (err)
873 release_firmware(priv->fw);
874
875 return err;
876}
877
Michael Wueff1a592007-09-25 18:11:01 -0700878static int p54u_open(struct ieee80211_hw *dev)
879{
880 struct p54u_priv *priv = dev->priv;
881 int err;
882
883 err = p54u_init_urbs(dev);
884 if (err) {
885 return err;
886 }
887
888 priv->common.open = p54u_init_urbs;
889
890 return 0;
891}
892
893static void p54u_stop(struct ieee80211_hw *dev)
894{
895 /* TODO: figure out how to reliably stop the 3887 and net2280 so
896 the hardware is still usable next time we want to start it.
897 until then, we just stop listening to the hardware.. */
898 p54u_free_urbs(dev);
Michael Wueff1a592007-09-25 18:11:01 -0700899}
900
901static int __devinit p54u_probe(struct usb_interface *intf,
902 const struct usb_device_id *id)
903{
904 struct usb_device *udev = interface_to_usbdev(intf);
905 struct ieee80211_hw *dev;
906 struct p54u_priv *priv;
907 int err;
908 unsigned int i, recognized_pipes;
Michael Wueff1a592007-09-25 18:11:01 -0700909
910 dev = p54_init_common(sizeof(*priv));
Christian Lamparter02e37ba2008-11-29 22:39:08 +0100911
Michael Wueff1a592007-09-25 18:11:01 -0700912 if (!dev) {
Christian Lamparter02e37ba2008-11-29 22:39:08 +0100913 dev_err(&udev->dev, "(p54usb) ieee80211 alloc failed\n");
Michael Wueff1a592007-09-25 18:11:01 -0700914 return -ENOMEM;
915 }
916
917 priv = dev->priv;
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200918 priv->hw_type = P54U_INVALID_HW;
Michael Wueff1a592007-09-25 18:11:01 -0700919
920 SET_IEEE80211_DEV(dev, &intf->dev);
921 usb_set_intfdata(intf, dev);
922 priv->udev = udev;
Christian Lamparter69828692008-12-26 19:08:31 +0100923 priv->intf = intf;
924 skb_queue_head_init(&priv->rx_queue);
925 init_usb_anchor(&priv->submitted);
Michael Wueff1a592007-09-25 18:11:01 -0700926
927 usb_get_dev(udev);
928
929 /* really lazy and simple way of figuring out if we're a 3887 */
930 /* TODO: should just stick the identification in the device table */
931 i = intf->altsetting->desc.bNumEndpoints;
932 recognized_pipes = 0;
933 while (i--) {
934 switch (intf->altsetting->endpoint[i].desc.bEndpointAddress) {
935 case P54U_PIPE_DATA:
936 case P54U_PIPE_MGMT:
937 case P54U_PIPE_BRG:
938 case P54U_PIPE_DEV:
939 case P54U_PIPE_DATA | USB_DIR_IN:
940 case P54U_PIPE_MGMT | USB_DIR_IN:
941 case P54U_PIPE_BRG | USB_DIR_IN:
942 case P54U_PIPE_DEV | USB_DIR_IN:
943 case P54U_PIPE_INT | USB_DIR_IN:
944 recognized_pipes++;
945 }
946 }
947 priv->common.open = p54u_open;
Christian Lamparter2b808482008-09-04 12:29:38 +0200948 priv->common.stop = p54u_stop;
Michael Wueff1a592007-09-25 18:11:01 -0700949 if (recognized_pipes < P54U_PIPE_NUMBER) {
Hauke Mehrtens13792572009-05-01 13:12:36 +0200950#ifdef CONFIG_PM
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200951 /* ISL3887 needs a full reset on resume */
952 udev->reset_resume = 1;
Christian Lamparter11791a62010-08-22 22:41:33 +0200953#endif /* CONFIG_PM */
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200954 err = p54u_device_reset(dev);
955
Michael Wueff1a592007-09-25 18:11:01 -0700956 priv->hw_type = P54U_3887;
Christian Lampartera406ac02009-04-25 21:11:55 +0200957 dev->extra_tx_headroom += sizeof(struct lm87_tx_hdr);
958 priv->common.tx_hdr_len = sizeof(struct lm87_tx_hdr);
959 priv->common.tx = p54u_tx_lm87;
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200960 priv->upload_fw = p54u_upload_firmware_3887;
Michael Wueff1a592007-09-25 18:11:01 -0700961 } else {
Christian Lamparter2b808482008-09-04 12:29:38 +0200962 priv->hw_type = P54U_NET2280;
Michael Wueff1a592007-09-25 18:11:01 -0700963 dev->extra_tx_headroom += sizeof(struct net2280_tx_hdr);
964 priv->common.tx_hdr_len = sizeof(struct net2280_tx_hdr);
965 priv->common.tx = p54u_tx_net2280;
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200966 priv->upload_fw = p54u_upload_firmware_net2280;
Christian Lamparter2b808482008-09-04 12:29:38 +0200967 }
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200968 err = p54u_load_firmware(dev);
Michael Wueff1a592007-09-25 18:11:01 -0700969 if (err)
970 goto err_free_dev;
971
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200972 err = priv->upload_fw(dev);
973 if (err)
974 goto err_free_fw;
975
Christian Lamparter7cb77072008-09-01 22:48:51 +0200976 p54u_open(dev);
977 err = p54_read_eeprom(dev);
978 p54u_stop(dev);
Michael Wueff1a592007-09-25 18:11:01 -0700979 if (err)
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200980 goto err_free_fw;
Michael Wueff1a592007-09-25 18:11:01 -0700981
Christian Lamparter2ac71072009-03-05 21:30:10 +0100982 err = p54_register_common(dev, &udev->dev);
983 if (err)
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200984 goto err_free_fw;
Michael Wueff1a592007-09-25 18:11:01 -0700985
Michael Wueff1a592007-09-25 18:11:01 -0700986 return 0;
987
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +0200988err_free_fw:
989 release_firmware(priv->fw);
990
991err_free_dev:
Christian Lamparterd8c92102009-06-23 10:39:45 -0500992 p54_free_common(dev);
Michael Wueff1a592007-09-25 18:11:01 -0700993 usb_set_intfdata(intf, NULL);
994 usb_put_dev(udev);
995 return err;
996}
997
998static void __devexit p54u_disconnect(struct usb_interface *intf)
999{
1000 struct ieee80211_hw *dev = usb_get_intfdata(intf);
1001 struct p54u_priv *priv;
1002
1003 if (!dev)
1004 return;
1005
Christian Lamparterd8c92102009-06-23 10:39:45 -05001006 p54_unregister_common(dev);
Michael Wueff1a592007-09-25 18:11:01 -07001007
1008 priv = dev->priv;
1009 usb_put_dev(interface_to_usbdev(intf));
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +02001010 release_firmware(priv->fw);
Michael Wueff1a592007-09-25 18:11:01 -07001011 p54_free_common(dev);
Michael Wueff1a592007-09-25 18:11:01 -07001012}
1013
Christian Lamparter69828692008-12-26 19:08:31 +01001014static int p54u_pre_reset(struct usb_interface *intf)
1015{
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +02001016 struct ieee80211_hw *dev = usb_get_intfdata(intf);
1017
1018 if (!dev)
1019 return -ENODEV;
1020
1021 p54u_stop(dev);
Christian Lamparter69828692008-12-26 19:08:31 +01001022 return 0;
1023}
1024
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +02001025static int p54u_resume(struct usb_interface *intf)
1026{
1027 struct ieee80211_hw *dev = usb_get_intfdata(intf);
1028 struct p54u_priv *priv;
1029
1030 if (!dev)
1031 return -ENODEV;
1032
1033 priv = dev->priv;
1034 if (unlikely(!(priv->upload_fw && priv->fw)))
1035 return 0;
1036
1037 return priv->upload_fw(dev);
1038}
1039
Christian Lamparter69828692008-12-26 19:08:31 +01001040static int p54u_post_reset(struct usb_interface *intf)
1041{
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +02001042 struct ieee80211_hw *dev = usb_get_intfdata(intf);
1043 struct p54u_priv *priv;
1044 int err;
1045
1046 err = p54u_resume(intf);
1047 if (err)
1048 return err;
1049
1050 /* reinitialize old device state */
1051 priv = dev->priv;
1052 if (priv->common.mode != NL80211_IFTYPE_UNSPECIFIED)
1053 ieee80211_restart_hw(dev);
1054
Christian Lamparter69828692008-12-26 19:08:31 +01001055 return 0;
1056}
1057
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +02001058#ifdef CONFIG_PM
1059
1060static int p54u_suspend(struct usb_interface *intf, pm_message_t message)
1061{
1062 return p54u_pre_reset(intf);
1063}
1064
1065#endif /* CONFIG_PM */
1066
Michael Wueff1a592007-09-25 18:11:01 -07001067static struct usb_driver p54u_driver = {
Christian Lamparter32ddf072008-08-08 21:17:37 +02001068 .name = "p54usb",
Michael Wueff1a592007-09-25 18:11:01 -07001069 .id_table = p54u_table,
1070 .probe = p54u_probe,
1071 .disconnect = p54u_disconnect,
Christian Lamparter69828692008-12-26 19:08:31 +01001072 .pre_reset = p54u_pre_reset,
1073 .post_reset = p54u_post_reset,
Christian Lamparter1ca5f2e2009-04-25 21:12:09 +02001074#ifdef CONFIG_PM
1075 .suspend = p54u_suspend,
1076 .resume = p54u_resume,
1077 .reset_resume = p54u_resume,
1078#endif /* CONFIG_PM */
Christian Lamparterfbf95292009-03-05 21:29:51 +01001079 .soft_unbind = 1,
Michael Wueff1a592007-09-25 18:11:01 -07001080};
1081
1082static int __init p54u_init(void)
1083{
1084 return usb_register(&p54u_driver);
1085}
1086
1087static void __exit p54u_exit(void)
1088{
1089 usb_deregister(&p54u_driver);
1090}
1091
1092module_init(p54u_init);
1093module_exit(p54u_exit);