diff options
author | Christian Lamparter <chunkeey@web.de> | 2008-04-08 15:38:00 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2008-04-08 16:44:45 -0400 |
commit | fb26971058845868f7c45b720636180d14c058e4 (patch) | |
tree | dd1463cfdad7b837b592a888847edb4200034ebd /drivers/net/wireless/p54/p54usb.c | |
parent | 2c8dccc77420fb7433da5674818959d3499d35be (diff) |
p54: move to separate directory
Signed-off-by: Christian Lamparter <chunkeey@web.de>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net/wireless/p54/p54usb.c')
-rw-r--r-- | drivers/net/wireless/p54/p54usb.c | 910 |
1 files changed, 910 insertions, 0 deletions
diff --git a/drivers/net/wireless/p54/p54usb.c b/drivers/net/wireless/p54/p54usb.c new file mode 100644 index 000000000000..98ddbb3b3273 --- /dev/null +++ b/drivers/net/wireless/p54/p54usb.c | |||
@@ -0,0 +1,910 @@ | |||
1 | |||
2 | /* | ||
3 | * Linux device driver for USB based Prism54 | ||
4 | * | ||
5 | * Copyright (c) 2006, Michael Wu <flamingice@sourmilk.net> | ||
6 | * | ||
7 | * Based on the islsm (softmac prism54) driver, which is: | ||
8 | * Copyright 2004-2006 Jean-Baptiste Note <jbnote@gmail.com>, et al. | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License version 2 as | ||
12 | * published by the Free Software Foundation. | ||
13 | */ | ||
14 | |||
15 | #include <linux/init.h> | ||
16 | #include <linux/usb.h> | ||
17 | #include <linux/pci.h> | ||
18 | #include <linux/firmware.h> | ||
19 | #include <linux/etherdevice.h> | ||
20 | #include <linux/delay.h> | ||
21 | #include <linux/crc32.h> | ||
22 | #include <net/mac80211.h> | ||
23 | |||
24 | #include "p54.h" | ||
25 | #include "p54usb.h" | ||
26 | |||
27 | MODULE_AUTHOR("Michael Wu <flamingice@sourmilk.net>"); | ||
28 | MODULE_DESCRIPTION("Prism54 USB wireless driver"); | ||
29 | MODULE_LICENSE("GPL"); | ||
30 | MODULE_ALIAS("prism54usb"); | ||
31 | |||
32 | static struct usb_device_id p54u_table[] __devinitdata = { | ||
33 | /* Version 1 devices (pci chip + net2280) */ | ||
34 | {USB_DEVICE(0x0506, 0x0a11)}, /* 3COM 3CRWE254G72 */ | ||
35 | {USB_DEVICE(0x0707, 0xee06)}, /* SMC 2862W-G */ | ||
36 | {USB_DEVICE(0x083a, 0x4501)}, /* Accton 802.11g WN4501 USB */ | ||
37 | {USB_DEVICE(0x083a, 0x4502)}, /* Siemens Gigaset USB Adapter */ | ||
38 | {USB_DEVICE(0x083a, 0x5501)}, /* Phillips CPWUA054 */ | ||
39 | {USB_DEVICE(0x0846, 0x4200)}, /* Netgear WG121 */ | ||
40 | {USB_DEVICE(0x0846, 0x4210)}, /* Netgear WG121 the second ? */ | ||
41 | {USB_DEVICE(0x0846, 0x4220)}, /* Netgear WG111 */ | ||
42 | {USB_DEVICE(0x0cde, 0x0006)}, /* Medion 40900, Roper Europe */ | ||
43 | {USB_DEVICE(0x124a, 0x4023)}, /* Shuttle PN15, Airvast WM168g, IOGear GWU513 */ | ||
44 | {USB_DEVICE(0x1915, 0x2234)}, /* Linksys WUSB54G OEM */ | ||
45 | {USB_DEVICE(0x1915, 0x2235)}, /* Linksys WUSB54G Portable OEM */ | ||
46 | {USB_DEVICE(0x2001, 0x3701)}, /* DLink DWL-G120 Spinnaker */ | ||
47 | {USB_DEVICE(0x2001, 0x3703)}, /* DLink DWL-G122 */ | ||
48 | {USB_DEVICE(0x5041, 0x2234)}, /* Linksys WUSB54G */ | ||
49 | {USB_DEVICE(0x5041, 0x2235)}, /* Linksys WUSB54G Portable */ | ||
50 | |||
51 | /* Version 2 devices (3887) */ | ||
52 | {USB_DEVICE(0x050d, 0x7050)}, /* Belkin F5D7050 ver 1000 */ | ||
53 | {USB_DEVICE(0x0572, 0x2000)}, /* Cohiba Proto board */ | ||
54 | {USB_DEVICE(0x0572, 0x2002)}, /* Cohiba Proto board */ | ||
55 | {USB_DEVICE(0x0707, 0xee13)}, /* SMC 2862W-G version 2 */ | ||
56 | {USB_DEVICE(0x083a, 0x4521)}, /* Siemens Gigaset USB Adapter 54 version 2 */ | ||
57 | {USB_DEVICE(0x0846, 0x4240)}, /* Netgear WG111 (v2) */ | ||
58 | {USB_DEVICE(0x0915, 0x2000)}, /* Cohiba Proto board */ | ||
59 | {USB_DEVICE(0x0915, 0x2002)}, /* Cohiba Proto board */ | ||
60 | {USB_DEVICE(0x0baf, 0x0118)}, /* U.S. Robotics U5 802.11g Adapter*/ | ||
61 | {USB_DEVICE(0x0bf8, 0x1009)}, /* FUJITSU E-5400 USB D1700*/ | ||
62 | {USB_DEVICE(0x0cde, 0x0006)}, /* Medion MD40900 */ | ||
63 | {USB_DEVICE(0x0cde, 0x0008)}, /* Sagem XG703A */ | ||
64 | {USB_DEVICE(0x0d8e, 0x3762)}, /* DLink DWL-G120 Cohiba */ | ||
65 | {USB_DEVICE(0x09aa, 0x1000)}, /* Spinnaker Proto board */ | ||
66 | {USB_DEVICE(0x124a, 0x4025)}, /* IOGear GWU513 (GW3887IK chip) */ | ||
67 | {USB_DEVICE(0x13b1, 0x000a)}, /* Linksys WUSB54G ver 2 */ | ||
68 | {USB_DEVICE(0x13B1, 0x000C)}, /* Linksys WUSB54AG */ | ||
69 | {USB_DEVICE(0x1435, 0x0427)}, /* Inventel UR054G */ | ||
70 | {USB_DEVICE(0x2001, 0x3704)}, /* DLink DWL-G122 rev A2 */ | ||
71 | {USB_DEVICE(0x413c, 0x8102)}, /* Spinnaker DUT */ | ||
72 | {USB_DEVICE(0x413c, 0x8104)}, /* Cohiba Proto board */ | ||
73 | {} | ||
74 | }; | ||
75 | |||
76 | MODULE_DEVICE_TABLE(usb, p54u_table); | ||
77 | |||
78 | static void p54u_rx_cb(struct urb *urb) | ||
79 | { | ||
80 | struct sk_buff *skb = (struct sk_buff *) urb->context; | ||
81 | struct p54u_rx_info *info = (struct p54u_rx_info *)skb->cb; | ||
82 | struct ieee80211_hw *dev = info->dev; | ||
83 | struct p54u_priv *priv = dev->priv; | ||
84 | |||
85 | if (unlikely(urb->status)) { | ||
86 | info->urb = NULL; | ||
87 | usb_free_urb(urb); | ||
88 | return; | ||
89 | } | ||
90 | |||
91 | skb_unlink(skb, &priv->rx_queue); | ||
92 | skb_put(skb, urb->actual_length); | ||
93 | if (!priv->hw_type) | ||
94 | skb_pull(skb, sizeof(struct net2280_tx_hdr)); | ||
95 | |||
96 | if (p54_rx(dev, skb)) { | ||
97 | skb = dev_alloc_skb(MAX_RX_SIZE); | ||
98 | if (unlikely(!skb)) { | ||
99 | usb_free_urb(urb); | ||
100 | /* TODO check rx queue length and refill *somewhere* */ | ||
101 | return; | ||
102 | } | ||
103 | |||
104 | info = (struct p54u_rx_info *) skb->cb; | ||
105 | info->urb = urb; | ||
106 | info->dev = dev; | ||
107 | urb->transfer_buffer = skb_tail_pointer(skb); | ||
108 | urb->context = skb; | ||
109 | skb_queue_tail(&priv->rx_queue, skb); | ||
110 | } else { | ||
111 | skb_trim(skb, 0); | ||
112 | skb_queue_tail(&priv->rx_queue, skb); | ||
113 | } | ||
114 | |||
115 | usb_submit_urb(urb, GFP_ATOMIC); | ||
116 | } | ||
117 | |||
118 | static void p54u_tx_cb(struct urb *urb) | ||
119 | { | ||
120 | usb_free_urb(urb); | ||
121 | } | ||
122 | |||
123 | static void p54u_tx_free_cb(struct urb *urb) | ||
124 | { | ||
125 | kfree(urb->transfer_buffer); | ||
126 | usb_free_urb(urb); | ||
127 | } | ||
128 | |||
129 | static int p54u_init_urbs(struct ieee80211_hw *dev) | ||
130 | { | ||
131 | struct p54u_priv *priv = dev->priv; | ||
132 | struct urb *entry; | ||
133 | struct sk_buff *skb; | ||
134 | struct p54u_rx_info *info; | ||
135 | |||
136 | while (skb_queue_len(&priv->rx_queue) < 32) { | ||
137 | skb = __dev_alloc_skb(MAX_RX_SIZE, GFP_KERNEL); | ||
138 | if (!skb) | ||
139 | break; | ||
140 | entry = usb_alloc_urb(0, GFP_KERNEL); | ||
141 | if (!entry) { | ||
142 | kfree_skb(skb); | ||
143 | break; | ||
144 | } | ||
145 | usb_fill_bulk_urb(entry, priv->udev, usb_rcvbulkpipe(priv->udev, P54U_PIPE_DATA), skb_tail_pointer(skb), MAX_RX_SIZE, p54u_rx_cb, skb); | ||
146 | info = (struct p54u_rx_info *) skb->cb; | ||
147 | info->urb = entry; | ||
148 | info->dev = dev; | ||
149 | skb_queue_tail(&priv->rx_queue, skb); | ||
150 | usb_submit_urb(entry, GFP_KERNEL); | ||
151 | } | ||
152 | |||
153 | return 0; | ||
154 | } | ||
155 | |||
156 | static void p54u_free_urbs(struct ieee80211_hw *dev) | ||
157 | { | ||
158 | struct p54u_priv *priv = dev->priv; | ||
159 | struct p54u_rx_info *info; | ||
160 | struct sk_buff *skb; | ||
161 | |||
162 | while ((skb = skb_dequeue(&priv->rx_queue))) { | ||
163 | info = (struct p54u_rx_info *) skb->cb; | ||
164 | if (!info->urb) | ||
165 | continue; | ||
166 | |||
167 | usb_kill_urb(info->urb); | ||
168 | kfree_skb(skb); | ||
169 | } | ||
170 | } | ||
171 | |||
172 | static void p54u_tx_3887(struct ieee80211_hw *dev, struct p54_control_hdr *data, | ||
173 | size_t len, int free_on_tx) | ||
174 | { | ||
175 | struct p54u_priv *priv = dev->priv; | ||
176 | struct urb *addr_urb, *data_urb; | ||
177 | |||
178 | addr_urb = usb_alloc_urb(0, GFP_ATOMIC); | ||
179 | if (!addr_urb) | ||
180 | return; | ||
181 | |||
182 | data_urb = usb_alloc_urb(0, GFP_ATOMIC); | ||
183 | if (!data_urb) { | ||
184 | usb_free_urb(addr_urb); | ||
185 | return; | ||
186 | } | ||
187 | |||
188 | usb_fill_bulk_urb(addr_urb, priv->udev, | ||
189 | usb_sndbulkpipe(priv->udev, P54U_PIPE_DATA), &data->req_id, | ||
190 | sizeof(data->req_id), p54u_tx_cb, dev); | ||
191 | usb_fill_bulk_urb(data_urb, priv->udev, | ||
192 | usb_sndbulkpipe(priv->udev, P54U_PIPE_DATA), data, len, | ||
193 | free_on_tx ? p54u_tx_free_cb : p54u_tx_cb, dev); | ||
194 | |||
195 | usb_submit_urb(addr_urb, GFP_ATOMIC); | ||
196 | usb_submit_urb(data_urb, GFP_ATOMIC); | ||
197 | } | ||
198 | |||
199 | static void p54u_tx_net2280(struct ieee80211_hw *dev, struct p54_control_hdr *data, | ||
200 | size_t len, int free_on_tx) | ||
201 | { | ||
202 | struct p54u_priv *priv = dev->priv; | ||
203 | struct urb *int_urb, *data_urb; | ||
204 | struct net2280_tx_hdr *hdr; | ||
205 | struct net2280_reg_write *reg; | ||
206 | |||
207 | reg = kmalloc(sizeof(*reg), GFP_ATOMIC); | ||
208 | if (!reg) | ||
209 | return; | ||
210 | |||
211 | int_urb = usb_alloc_urb(0, GFP_ATOMIC); | ||
212 | if (!int_urb) { | ||
213 | kfree(reg); | ||
214 | return; | ||
215 | } | ||
216 | |||
217 | data_urb = usb_alloc_urb(0, GFP_ATOMIC); | ||
218 | if (!data_urb) { | ||
219 | kfree(reg); | ||
220 | usb_free_urb(int_urb); | ||
221 | return; | ||
222 | } | ||
223 | |||
224 | reg->port = cpu_to_le16(NET2280_DEV_U32); | ||
225 | reg->addr = cpu_to_le32(P54U_DEV_BASE); | ||
226 | reg->val = cpu_to_le32(ISL38XX_DEV_INT_DATA); | ||
227 | |||
228 | len += sizeof(*data); | ||
229 | hdr = (void *)data - sizeof(*hdr); | ||
230 | memset(hdr, 0, sizeof(*hdr)); | ||
231 | hdr->device_addr = data->req_id; | ||
232 | hdr->len = cpu_to_le16(len); | ||
233 | |||
234 | usb_fill_bulk_urb(int_urb, priv->udev, | ||
235 | usb_sndbulkpipe(priv->udev, P54U_PIPE_DEV), reg, sizeof(*reg), | ||
236 | p54u_tx_free_cb, dev); | ||
237 | usb_submit_urb(int_urb, GFP_ATOMIC); | ||
238 | |||
239 | usb_fill_bulk_urb(data_urb, priv->udev, | ||
240 | usb_sndbulkpipe(priv->udev, P54U_PIPE_DATA), hdr, len + sizeof(*hdr), | ||
241 | free_on_tx ? p54u_tx_free_cb : p54u_tx_cb, dev); | ||
242 | usb_submit_urb(data_urb, GFP_ATOMIC); | ||
243 | } | ||
244 | |||
245 | static int p54u_write(struct p54u_priv *priv, | ||
246 | struct net2280_reg_write *buf, | ||
247 | enum net2280_op_type type, | ||
248 | __le32 addr, __le32 val) | ||
249 | { | ||
250 | unsigned int ep; | ||
251 | int alen; | ||
252 | |||
253 | if (type & 0x0800) | ||
254 | ep = usb_sndbulkpipe(priv->udev, P54U_PIPE_DEV); | ||
255 | else | ||
256 | ep = usb_sndbulkpipe(priv->udev, P54U_PIPE_BRG); | ||
257 | |||
258 | buf->port = cpu_to_le16(type); | ||
259 | buf->addr = addr; | ||
260 | buf->val = val; | ||
261 | |||
262 | return usb_bulk_msg(priv->udev, ep, buf, sizeof(*buf), &alen, 1000); | ||
263 | } | ||
264 | |||
265 | static int p54u_read(struct p54u_priv *priv, void *buf, | ||
266 | enum net2280_op_type type, | ||
267 | __le32 addr, __le32 *val) | ||
268 | { | ||
269 | struct net2280_reg_read *read = buf; | ||
270 | __le32 *reg = buf; | ||
271 | unsigned int ep; | ||
272 | int alen, err; | ||
273 | |||
274 | if (type & 0x0800) | ||
275 | ep = P54U_PIPE_DEV; | ||
276 | else | ||
277 | ep = P54U_PIPE_BRG; | ||
278 | |||
279 | read->port = cpu_to_le16(type); | ||
280 | read->addr = addr; | ||
281 | |||
282 | err = usb_bulk_msg(priv->udev, usb_sndbulkpipe(priv->udev, ep), | ||
283 | read, sizeof(*read), &alen, 1000); | ||
284 | if (err) | ||
285 | return err; | ||
286 | |||
287 | err = usb_bulk_msg(priv->udev, usb_rcvbulkpipe(priv->udev, ep), | ||
288 | reg, sizeof(*reg), &alen, 1000); | ||
289 | if (err) | ||
290 | return err; | ||
291 | |||
292 | *val = *reg; | ||
293 | return 0; | ||
294 | } | ||
295 | |||
296 | static int p54u_bulk_msg(struct p54u_priv *priv, unsigned int ep, | ||
297 | void *data, size_t len) | ||
298 | { | ||
299 | int alen; | ||
300 | return usb_bulk_msg(priv->udev, usb_sndbulkpipe(priv->udev, ep), | ||
301 | data, len, &alen, 2000); | ||
302 | } | ||
303 | |||
304 | static int p54u_read_eeprom(struct ieee80211_hw *dev) | ||
305 | { | ||
306 | struct p54u_priv *priv = dev->priv; | ||
307 | void *buf; | ||
308 | struct p54_control_hdr *hdr; | ||
309 | int err, alen; | ||
310 | size_t offset = priv->hw_type ? 0x10 : 0x20; | ||
311 | |||
312 | buf = kmalloc(0x2020, GFP_KERNEL); | ||
313 | if (!buf) { | ||
314 | printk(KERN_ERR "prism54usb: cannot allocate memory for " | ||
315 | "eeprom readback!\n"); | ||
316 | return -ENOMEM; | ||
317 | } | ||
318 | |||
319 | if (priv->hw_type) { | ||
320 | *((u32 *) buf) = priv->common.rx_start; | ||
321 | err = p54u_bulk_msg(priv, P54U_PIPE_DATA, buf, sizeof(u32)); | ||
322 | if (err) { | ||
323 | printk(KERN_ERR "prism54usb: addr send failed\n"); | ||
324 | goto fail; | ||
325 | } | ||
326 | } else { | ||
327 | struct net2280_reg_write *reg = buf; | ||
328 | reg->port = cpu_to_le16(NET2280_DEV_U32); | ||
329 | reg->addr = cpu_to_le32(P54U_DEV_BASE); | ||
330 | reg->val = cpu_to_le32(ISL38XX_DEV_INT_DATA); | ||
331 | err = p54u_bulk_msg(priv, P54U_PIPE_DEV, buf, sizeof(*reg)); | ||
332 | if (err) { | ||
333 | printk(KERN_ERR "prism54usb: dev_int send failed\n"); | ||
334 | goto fail; | ||
335 | } | ||
336 | } | ||
337 | |||
338 | hdr = buf + priv->common.tx_hdr_len; | ||
339 | p54_fill_eeprom_readback(hdr); | ||
340 | hdr->req_id = cpu_to_le32(priv->common.rx_start); | ||
341 | if (priv->common.tx_hdr_len) { | ||
342 | struct net2280_tx_hdr *tx_hdr = buf; | ||
343 | tx_hdr->device_addr = hdr->req_id; | ||
344 | tx_hdr->len = cpu_to_le16(EEPROM_READBACK_LEN); | ||
345 | } | ||
346 | |||
347 | /* we can just pretend to send 0x2000 bytes of nothing in the headers */ | ||
348 | err = p54u_bulk_msg(priv, P54U_PIPE_DATA, buf, | ||
349 | EEPROM_READBACK_LEN + priv->common.tx_hdr_len); | ||
350 | if (err) { | ||
351 | printk(KERN_ERR "prism54usb: eeprom req send failed\n"); | ||
352 | goto fail; | ||
353 | } | ||
354 | |||
355 | err = usb_bulk_msg(priv->udev, | ||
356 | usb_rcvbulkpipe(priv->udev, P54U_PIPE_DATA), | ||
357 | buf, 0x2020, &alen, 1000); | ||
358 | if (!err && alen > offset) { | ||
359 | p54_parse_eeprom(dev, (u8 *)buf + offset, alen - offset); | ||
360 | } else { | ||
361 | printk(KERN_ERR "prism54usb: eeprom read failed!\n"); | ||
362 | err = -EINVAL; | ||
363 | goto fail; | ||
364 | } | ||
365 | |||
366 | fail: | ||
367 | kfree(buf); | ||
368 | return err; | ||
369 | } | ||
370 | |||
371 | static int p54u_upload_firmware_3887(struct ieee80211_hw *dev) | ||
372 | { | ||
373 | static char start_string[] = "~~~~<\r"; | ||
374 | struct p54u_priv *priv = dev->priv; | ||
375 | const struct firmware *fw_entry = NULL; | ||
376 | int err, alen; | ||
377 | u8 carry = 0; | ||
378 | u8 *buf, *tmp, *data; | ||
379 | unsigned int left, remains, block_size; | ||
380 | struct x2_header *hdr; | ||
381 | unsigned long timeout; | ||
382 | |||
383 | tmp = buf = kmalloc(P54U_FW_BLOCK, GFP_KERNEL); | ||
384 | if (!buf) { | ||
385 | printk(KERN_ERR "p54usb: cannot allocate firmware upload buffer!\n"); | ||
386 | err = -ENOMEM; | ||
387 | goto err_bufalloc; | ||
388 | } | ||
389 | |||
390 | memcpy(buf, start_string, 4); | ||
391 | err = p54u_bulk_msg(priv, P54U_PIPE_DATA, buf, 4); | ||
392 | if (err) { | ||
393 | printk(KERN_ERR "p54usb: reset failed! (%d)\n", err); | ||
394 | goto err_reset; | ||
395 | } | ||
396 | |||
397 | err = request_firmware(&fw_entry, "isl3887usb_bare", &priv->udev->dev); | ||
398 | if (err) { | ||
399 | printk(KERN_ERR "p54usb: cannot find firmware (isl3887usb_bare)!\n"); | ||
400 | goto err_req_fw_failed; | ||
401 | } | ||
402 | |||
403 | p54_parse_firmware(dev, fw_entry); | ||
404 | |||
405 | left = block_size = min((size_t)P54U_FW_BLOCK, fw_entry->size); | ||
406 | strcpy(buf, start_string); | ||
407 | left -= strlen(start_string); | ||
408 | tmp += strlen(start_string); | ||
409 | |||
410 | data = fw_entry->data; | ||
411 | remains = fw_entry->size; | ||
412 | |||
413 | hdr = (struct x2_header *)(buf + strlen(start_string)); | ||
414 | memcpy(hdr->signature, X2_SIGNATURE, X2_SIGNATURE_SIZE); | ||
415 | hdr->fw_load_addr = cpu_to_le32(ISL38XX_DEV_FIRMWARE_ADDR); | ||
416 | hdr->fw_length = cpu_to_le32(fw_entry->size); | ||
417 | hdr->crc = cpu_to_le32(~crc32_le(~0, (void *)&hdr->fw_load_addr, | ||
418 | sizeof(u32)*2)); | ||
419 | left -= sizeof(*hdr); | ||
420 | tmp += sizeof(*hdr); | ||
421 | |||
422 | while (remains) { | ||
423 | while (left--) { | ||
424 | if (carry) { | ||
425 | *tmp++ = carry; | ||
426 | carry = 0; | ||
427 | remains--; | ||
428 | continue; | ||
429 | } | ||
430 | switch (*data) { | ||
431 | case '~': | ||
432 | *tmp++ = '}'; | ||
433 | carry = '^'; | ||
434 | break; | ||
435 | case '}': | ||
436 | *tmp++ = '}'; | ||
437 | carry = ']'; | ||
438 | break; | ||
439 | default: | ||
440 | *tmp++ = *data; | ||
441 | remains--; | ||
442 | break; | ||
443 | } | ||
444 | data++; | ||
445 | } | ||
446 | |||
447 | err = p54u_bulk_msg(priv, P54U_PIPE_DATA, buf, block_size); | ||
448 | if (err) { | ||
449 | printk(KERN_ERR "prism54usb: firmware upload failed!\n"); | ||
450 | goto err_upload_failed; | ||
451 | } | ||
452 | |||
453 | tmp = buf; | ||
454 | left = block_size = min((unsigned int)P54U_FW_BLOCK, remains); | ||
455 | } | ||
456 | |||
457 | *((__le32 *)buf) = cpu_to_le32(~crc32_le(~0, fw_entry->data, fw_entry->size)); | ||
458 | err = p54u_bulk_msg(priv, P54U_PIPE_DATA, buf, sizeof(u32)); | ||
459 | if (err) { | ||
460 | printk(KERN_ERR "prism54usb: firmware upload failed!\n"); | ||
461 | goto err_upload_failed; | ||
462 | } | ||
463 | |||
464 | timeout = jiffies + msecs_to_jiffies(1000); | ||
465 | while (!(err = usb_bulk_msg(priv->udev, | ||
466 | usb_rcvbulkpipe(priv->udev, P54U_PIPE_DATA), buf, 128, &alen, 1000))) { | ||
467 | if (alen > 2 && !memcmp(buf, "OK", 2)) | ||
468 | break; | ||
469 | |||
470 | if (alen > 5 && !memcmp(buf, "ERROR", 5)) { | ||
471 | printk(KERN_INFO "prism54usb: firmware upload failed!\n"); | ||
472 | err = -EINVAL; | ||
473 | break; | ||
474 | } | ||
475 | |||
476 | if (time_after(jiffies, timeout)) { | ||
477 | printk(KERN_ERR "prism54usb: firmware boot timed out!\n"); | ||
478 | err = -ETIMEDOUT; | ||
479 | break; | ||
480 | } | ||
481 | } | ||
482 | if (err) | ||
483 | goto err_upload_failed; | ||
484 | |||
485 | buf[0] = 'g'; | ||
486 | buf[1] = '\r'; | ||
487 | err = p54u_bulk_msg(priv, P54U_PIPE_DATA, buf, 2); | ||
488 | if (err) { | ||
489 | printk(KERN_ERR "prism54usb: firmware boot failed!\n"); | ||
490 | goto err_upload_failed; | ||
491 | } | ||
492 | |||
493 | timeout = jiffies + msecs_to_jiffies(1000); | ||
494 | while (!(err = usb_bulk_msg(priv->udev, | ||
495 | usb_rcvbulkpipe(priv->udev, P54U_PIPE_DATA), buf, 128, &alen, 1000))) { | ||
496 | if (alen > 0 && buf[0] == 'g') | ||
497 | break; | ||
498 | |||
499 | if (time_after(jiffies, timeout)) { | ||
500 | err = -ETIMEDOUT; | ||
501 | break; | ||
502 | } | ||
503 | } | ||
504 | if (err) | ||
505 | goto err_upload_failed; | ||
506 | |||
507 | err_upload_failed: | ||
508 | release_firmware(fw_entry); | ||
509 | err_req_fw_failed: | ||
510 | err_reset: | ||
511 | kfree(buf); | ||
512 | err_bufalloc: | ||
513 | return err; | ||
514 | } | ||
515 | |||
516 | static int p54u_upload_firmware_net2280(struct ieee80211_hw *dev) | ||
517 | { | ||
518 | struct p54u_priv *priv = dev->priv; | ||
519 | const struct firmware *fw_entry = NULL; | ||
520 | const struct p54p_csr *devreg = (const struct p54p_csr *) P54U_DEV_BASE; | ||
521 | int err, alen; | ||
522 | void *buf; | ||
523 | __le32 reg; | ||
524 | unsigned int remains, offset; | ||
525 | u8 *data; | ||
526 | |||
527 | buf = kmalloc(512, GFP_KERNEL); | ||
528 | if (!buf) { | ||
529 | printk(KERN_ERR "p54usb: firmware buffer alloc failed!\n"); | ||
530 | return -ENOMEM; | ||
531 | } | ||
532 | |||
533 | err = request_firmware(&fw_entry, "isl3890usb", &priv->udev->dev); | ||
534 | if (err) { | ||
535 | printk(KERN_ERR "p54usb: cannot find firmware (isl3890usb)!\n"); | ||
536 | kfree(buf); | ||
537 | return err; | ||
538 | } | ||
539 | |||
540 | p54_parse_firmware(dev, fw_entry); | ||
541 | |||
542 | #define P54U_WRITE(type, addr, data) \ | ||
543 | do {\ | ||
544 | err = p54u_write(priv, buf, type,\ | ||
545 | cpu_to_le32((u32)(unsigned long)addr), data);\ | ||
546 | if (err) \ | ||
547 | goto fail;\ | ||
548 | } while (0) | ||
549 | |||
550 | #define P54U_READ(type, addr) \ | ||
551 | do {\ | ||
552 | err = p54u_read(priv, buf, type,\ | ||
553 | cpu_to_le32((u32)(unsigned long)addr), ®);\ | ||
554 | if (err)\ | ||
555 | goto fail;\ | ||
556 | } while (0) | ||
557 | |||
558 | /* power down net2280 bridge */ | ||
559 | P54U_READ(NET2280_BRG_U32, NET2280_GPIOCTL); | ||
560 | reg |= cpu_to_le32(P54U_BRG_POWER_DOWN); | ||
561 | reg &= cpu_to_le32(~P54U_BRG_POWER_UP); | ||
562 | P54U_WRITE(NET2280_BRG_U32, NET2280_GPIOCTL, reg); | ||
563 | |||
564 | mdelay(100); | ||
565 | |||
566 | /* power up bridge */ | ||
567 | reg |= cpu_to_le32(P54U_BRG_POWER_UP); | ||
568 | reg &= cpu_to_le32(~P54U_BRG_POWER_DOWN); | ||
569 | P54U_WRITE(NET2280_BRG_U32, NET2280_GPIOCTL, reg); | ||
570 | |||
571 | mdelay(100); | ||
572 | |||
573 | P54U_WRITE(NET2280_BRG_U32, NET2280_DEVINIT, | ||
574 | cpu_to_le32(NET2280_CLK_30Mhz | | ||
575 | NET2280_PCI_ENABLE | | ||
576 | NET2280_PCI_SOFT_RESET)); | ||
577 | |||
578 | mdelay(20); | ||
579 | |||
580 | P54U_WRITE(NET2280_BRG_CFG_U16, PCI_COMMAND, | ||
581 | cpu_to_le32(PCI_COMMAND_MEMORY | | ||
582 | PCI_COMMAND_MASTER)); | ||
583 | |||
584 | P54U_WRITE(NET2280_BRG_CFG_U32, PCI_BASE_ADDRESS_0, | ||
585 | cpu_to_le32(NET2280_BASE)); | ||
586 | |||
587 | P54U_READ(NET2280_BRG_CFG_U16, PCI_STATUS); | ||
588 | reg |= cpu_to_le32(PCI_STATUS_REC_MASTER_ABORT); | ||
589 | P54U_WRITE(NET2280_BRG_CFG_U16, PCI_STATUS, reg); | ||
590 | |||
591 | // TODO: we really need this? | ||
592 | P54U_READ(NET2280_BRG_U32, NET2280_RELNUM); | ||
593 | |||
594 | P54U_WRITE(NET2280_BRG_U32, NET2280_EPA_RSP, | ||
595 | cpu_to_le32(NET2280_CLEAR_NAK_OUT_PACKETS_MODE)); | ||
596 | P54U_WRITE(NET2280_BRG_U32, NET2280_EPC_RSP, | ||
597 | cpu_to_le32(NET2280_CLEAR_NAK_OUT_PACKETS_MODE)); | ||
598 | |||
599 | P54U_WRITE(NET2280_BRG_CFG_U32, PCI_BASE_ADDRESS_2, | ||
600 | cpu_to_le32(NET2280_BASE2)); | ||
601 | |||
602 | /* finally done setting up the bridge */ | ||
603 | |||
604 | P54U_WRITE(NET2280_DEV_CFG_U16, 0x10000 | PCI_COMMAND, | ||
605 | cpu_to_le32(PCI_COMMAND_MEMORY | | ||
606 | PCI_COMMAND_MASTER)); | ||
607 | |||
608 | P54U_WRITE(NET2280_DEV_CFG_U16, 0x10000 | 0x40 /* TRDY timeout */, 0); | ||
609 | P54U_WRITE(NET2280_DEV_CFG_U32, 0x10000 | PCI_BASE_ADDRESS_0, | ||
610 | cpu_to_le32(P54U_DEV_BASE)); | ||
611 | |||
612 | P54U_WRITE(NET2280_BRG_U32, NET2280_USBIRQENB1, 0); | ||
613 | P54U_WRITE(NET2280_BRG_U32, NET2280_IRQSTAT1, | ||
614 | cpu_to_le32(NET2280_PCI_INTA_INTERRUPT)); | ||
615 | |||
616 | /* do romboot */ | ||
617 | P54U_WRITE(NET2280_DEV_U32, &devreg->int_enable, 0); | ||
618 | |||
619 | P54U_READ(NET2280_DEV_U32, &devreg->ctrl_stat); | ||
620 | reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_RESET); | ||
621 | reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_RAMBOOT); | ||
622 | reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_CLKRUN); | ||
623 | P54U_WRITE(NET2280_DEV_U32, &devreg->ctrl_stat, reg); | ||
624 | |||
625 | mdelay(20); | ||
626 | |||
627 | reg |= cpu_to_le32(ISL38XX_CTRL_STAT_RESET); | ||
628 | P54U_WRITE(NET2280_DEV_U32, &devreg->ctrl_stat, reg); | ||
629 | |||
630 | mdelay(20); | ||
631 | |||
632 | reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_RESET); | ||
633 | P54U_WRITE(NET2280_DEV_U32, &devreg->ctrl_stat, reg); | ||
634 | |||
635 | mdelay(100); | ||
636 | |||
637 | P54U_READ(NET2280_DEV_U32, &devreg->int_ident); | ||
638 | P54U_WRITE(NET2280_DEV_U32, &devreg->int_ack, reg); | ||
639 | |||
640 | /* finally, we can upload firmware now! */ | ||
641 | remains = fw_entry->size; | ||
642 | data = fw_entry->data; | ||
643 | offset = ISL38XX_DEV_FIRMWARE_ADDR; | ||
644 | |||
645 | while (remains) { | ||
646 | unsigned int block_len = min(remains, (unsigned int)512); | ||
647 | memcpy(buf, data, block_len); | ||
648 | |||
649 | err = p54u_bulk_msg(priv, P54U_PIPE_DATA, buf, block_len); | ||
650 | if (err) { | ||
651 | printk(KERN_ERR "prism54usb: firmware block upload " | ||
652 | "failed\n"); | ||
653 | goto fail; | ||
654 | } | ||
655 | |||
656 | P54U_WRITE(NET2280_DEV_U32, &devreg->direct_mem_base, | ||
657 | cpu_to_le32(0xc0000f00)); | ||
658 | |||
659 | P54U_WRITE(NET2280_DEV_U32, | ||
660 | 0x0020 | (unsigned long)&devreg->direct_mem_win, 0); | ||
661 | P54U_WRITE(NET2280_DEV_U32, | ||
662 | 0x0020 | (unsigned long)&devreg->direct_mem_win, | ||
663 | cpu_to_le32(1)); | ||
664 | |||
665 | P54U_WRITE(NET2280_DEV_U32, | ||
666 | 0x0024 | (unsigned long)&devreg->direct_mem_win, | ||
667 | cpu_to_le32(block_len)); | ||
668 | P54U_WRITE(NET2280_DEV_U32, | ||
669 | 0x0028 | (unsigned long)&devreg->direct_mem_win, | ||
670 | cpu_to_le32(offset)); | ||
671 | |||
672 | P54U_WRITE(NET2280_DEV_U32, &devreg->dma_addr, | ||
673 | cpu_to_le32(NET2280_EPA_FIFO_PCI_ADDR)); | ||
674 | P54U_WRITE(NET2280_DEV_U32, &devreg->dma_len, | ||
675 | cpu_to_le32(block_len >> 2)); | ||
676 | P54U_WRITE(NET2280_DEV_U32, &devreg->dma_ctrl, | ||
677 | cpu_to_le32(ISL38XX_DMA_MASTER_CONTROL_TRIGGER)); | ||
678 | |||
679 | mdelay(10); | ||
680 | |||
681 | P54U_READ(NET2280_DEV_U32, | ||
682 | 0x002C | (unsigned long)&devreg->direct_mem_win); | ||
683 | if (!(reg & cpu_to_le32(ISL38XX_DMA_STATUS_DONE)) || | ||
684 | !(reg & cpu_to_le32(ISL38XX_DMA_STATUS_READY))) { | ||
685 | printk(KERN_ERR "prism54usb: firmware DMA transfer " | ||
686 | "failed\n"); | ||
687 | goto fail; | ||
688 | } | ||
689 | |||
690 | P54U_WRITE(NET2280_BRG_U32, NET2280_EPA_STAT, | ||
691 | cpu_to_le32(NET2280_FIFO_FLUSH)); | ||
692 | |||
693 | remains -= block_len; | ||
694 | data += block_len; | ||
695 | offset += block_len; | ||
696 | } | ||
697 | |||
698 | /* do ramboot */ | ||
699 | P54U_READ(NET2280_DEV_U32, &devreg->ctrl_stat); | ||
700 | reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_RESET); | ||
701 | reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_CLKRUN); | ||
702 | reg |= cpu_to_le32(ISL38XX_CTRL_STAT_RAMBOOT); | ||
703 | P54U_WRITE(NET2280_DEV_U32, &devreg->ctrl_stat, reg); | ||
704 | |||
705 | mdelay(20); | ||
706 | |||
707 | reg |= cpu_to_le32(ISL38XX_CTRL_STAT_RESET); | ||
708 | P54U_WRITE(NET2280_DEV_U32, &devreg->ctrl_stat, reg); | ||
709 | |||
710 | reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_RESET); | ||
711 | P54U_WRITE(NET2280_DEV_U32, &devreg->ctrl_stat, reg); | ||
712 | |||
713 | mdelay(100); | ||
714 | |||
715 | P54U_READ(NET2280_DEV_U32, &devreg->int_ident); | ||
716 | P54U_WRITE(NET2280_DEV_U32, &devreg->int_ack, reg); | ||
717 | |||
718 | /* start up the firmware */ | ||
719 | P54U_WRITE(NET2280_DEV_U32, &devreg->int_enable, | ||
720 | cpu_to_le32(ISL38XX_INT_IDENT_INIT)); | ||
721 | |||
722 | P54U_WRITE(NET2280_BRG_U32, NET2280_IRQSTAT1, | ||
723 | cpu_to_le32(NET2280_PCI_INTA_INTERRUPT)); | ||
724 | |||
725 | P54U_WRITE(NET2280_BRG_U32, NET2280_USBIRQENB1, | ||
726 | cpu_to_le32(NET2280_PCI_INTA_INTERRUPT_ENABLE | | ||
727 | NET2280_USB_INTERRUPT_ENABLE)); | ||
728 | |||
729 | P54U_WRITE(NET2280_DEV_U32, &devreg->dev_int, | ||
730 | cpu_to_le32(ISL38XX_DEV_INT_RESET)); | ||
731 | |||
732 | err = usb_interrupt_msg(priv->udev, | ||
733 | usb_rcvbulkpipe(priv->udev, P54U_PIPE_INT), | ||
734 | buf, sizeof(__le32), &alen, 1000); | ||
735 | if (err || alen != sizeof(__le32)) | ||
736 | goto fail; | ||
737 | |||
738 | P54U_READ(NET2280_DEV_U32, &devreg->int_ident); | ||
739 | P54U_WRITE(NET2280_DEV_U32, &devreg->int_ack, reg); | ||
740 | |||
741 | if (!(reg & cpu_to_le32(ISL38XX_INT_IDENT_INIT))) | ||
742 | err = -EINVAL; | ||
743 | |||
744 | P54U_WRITE(NET2280_BRG_U32, NET2280_USBIRQENB1, 0); | ||
745 | P54U_WRITE(NET2280_BRG_U32, NET2280_IRQSTAT1, | ||
746 | cpu_to_le32(NET2280_PCI_INTA_INTERRUPT)); | ||
747 | |||
748 | #undef P54U_WRITE | ||
749 | #undef P54U_READ | ||
750 | |||
751 | fail: | ||
752 | release_firmware(fw_entry); | ||
753 | kfree(buf); | ||
754 | return err; | ||
755 | } | ||
756 | |||
757 | static int p54u_open(struct ieee80211_hw *dev) | ||
758 | { | ||
759 | struct p54u_priv *priv = dev->priv; | ||
760 | int err; | ||
761 | |||
762 | err = p54u_init_urbs(dev); | ||
763 | if (err) { | ||
764 | return err; | ||
765 | } | ||
766 | |||
767 | priv->common.open = p54u_init_urbs; | ||
768 | |||
769 | return 0; | ||
770 | } | ||
771 | |||
772 | static void p54u_stop(struct ieee80211_hw *dev) | ||
773 | { | ||
774 | /* TODO: figure out how to reliably stop the 3887 and net2280 so | ||
775 | the hardware is still usable next time we want to start it. | ||
776 | until then, we just stop listening to the hardware.. */ | ||
777 | p54u_free_urbs(dev); | ||
778 | return; | ||
779 | } | ||
780 | |||
781 | static int __devinit p54u_probe(struct usb_interface *intf, | ||
782 | const struct usb_device_id *id) | ||
783 | { | ||
784 | struct usb_device *udev = interface_to_usbdev(intf); | ||
785 | struct ieee80211_hw *dev; | ||
786 | struct p54u_priv *priv; | ||
787 | int err; | ||
788 | unsigned int i, recognized_pipes; | ||
789 | DECLARE_MAC_BUF(mac); | ||
790 | |||
791 | dev = p54_init_common(sizeof(*priv)); | ||
792 | if (!dev) { | ||
793 | printk(KERN_ERR "prism54usb: ieee80211 alloc failed\n"); | ||
794 | return -ENOMEM; | ||
795 | } | ||
796 | |||
797 | priv = dev->priv; | ||
798 | |||
799 | SET_IEEE80211_DEV(dev, &intf->dev); | ||
800 | usb_set_intfdata(intf, dev); | ||
801 | priv->udev = udev; | ||
802 | |||
803 | usb_get_dev(udev); | ||
804 | |||
805 | /* really lazy and simple way of figuring out if we're a 3887 */ | ||
806 | /* TODO: should just stick the identification in the device table */ | ||
807 | i = intf->altsetting->desc.bNumEndpoints; | ||
808 | recognized_pipes = 0; | ||
809 | while (i--) { | ||
810 | switch (intf->altsetting->endpoint[i].desc.bEndpointAddress) { | ||
811 | case P54U_PIPE_DATA: | ||
812 | case P54U_PIPE_MGMT: | ||
813 | case P54U_PIPE_BRG: | ||
814 | case P54U_PIPE_DEV: | ||
815 | case P54U_PIPE_DATA | USB_DIR_IN: | ||
816 | case P54U_PIPE_MGMT | USB_DIR_IN: | ||
817 | case P54U_PIPE_BRG | USB_DIR_IN: | ||
818 | case P54U_PIPE_DEV | USB_DIR_IN: | ||
819 | case P54U_PIPE_INT | USB_DIR_IN: | ||
820 | recognized_pipes++; | ||
821 | } | ||
822 | } | ||
823 | priv->common.open = p54u_open; | ||
824 | |||
825 | if (recognized_pipes < P54U_PIPE_NUMBER) { | ||
826 | priv->hw_type = P54U_3887; | ||
827 | priv->common.tx = p54u_tx_3887; | ||
828 | } else { | ||
829 | dev->extra_tx_headroom += sizeof(struct net2280_tx_hdr); | ||
830 | priv->common.tx_hdr_len = sizeof(struct net2280_tx_hdr); | ||
831 | priv->common.tx = p54u_tx_net2280; | ||
832 | } | ||
833 | priv->common.stop = p54u_stop; | ||
834 | |||
835 | if (priv->hw_type) | ||
836 | err = p54u_upload_firmware_3887(dev); | ||
837 | else | ||
838 | err = p54u_upload_firmware_net2280(dev); | ||
839 | if (err) | ||
840 | goto err_free_dev; | ||
841 | |||
842 | err = p54u_read_eeprom(dev); | ||
843 | if (err) | ||
844 | goto err_free_dev; | ||
845 | |||
846 | if (!is_valid_ether_addr(dev->wiphy->perm_addr)) { | ||
847 | u8 perm_addr[ETH_ALEN]; | ||
848 | |||
849 | printk(KERN_WARNING "prism54usb: Invalid hwaddr! Using randomly generated MAC addr\n"); | ||
850 | random_ether_addr(perm_addr); | ||
851 | SET_IEEE80211_PERM_ADDR(dev, perm_addr); | ||
852 | } | ||
853 | |||
854 | skb_queue_head_init(&priv->rx_queue); | ||
855 | |||
856 | err = ieee80211_register_hw(dev); | ||
857 | if (err) { | ||
858 | printk(KERN_ERR "prism54usb: Cannot register netdevice\n"); | ||
859 | goto err_free_dev; | ||
860 | } | ||
861 | |||
862 | printk(KERN_INFO "%s: hwaddr %s, isl38%02x\n", | ||
863 | wiphy_name(dev->wiphy), | ||
864 | print_mac(mac, dev->wiphy->perm_addr), | ||
865 | priv->common.version); | ||
866 | |||
867 | return 0; | ||
868 | |||
869 | err_free_dev: | ||
870 | ieee80211_free_hw(dev); | ||
871 | usb_set_intfdata(intf, NULL); | ||
872 | usb_put_dev(udev); | ||
873 | return err; | ||
874 | } | ||
875 | |||
876 | static void __devexit p54u_disconnect(struct usb_interface *intf) | ||
877 | { | ||
878 | struct ieee80211_hw *dev = usb_get_intfdata(intf); | ||
879 | struct p54u_priv *priv; | ||
880 | |||
881 | if (!dev) | ||
882 | return; | ||
883 | |||
884 | ieee80211_unregister_hw(dev); | ||
885 | |||
886 | priv = dev->priv; | ||
887 | usb_put_dev(interface_to_usbdev(intf)); | ||
888 | p54_free_common(dev); | ||
889 | ieee80211_free_hw(dev); | ||
890 | } | ||
891 | |||
892 | static struct usb_driver p54u_driver = { | ||
893 | .name = "prism54usb", | ||
894 | .id_table = p54u_table, | ||
895 | .probe = p54u_probe, | ||
896 | .disconnect = p54u_disconnect, | ||
897 | }; | ||
898 | |||
899 | static int __init p54u_init(void) | ||
900 | { | ||
901 | return usb_register(&p54u_driver); | ||
902 | } | ||
903 | |||
904 | static void __exit p54u_exit(void) | ||
905 | { | ||
906 | usb_deregister(&p54u_driver); | ||
907 | } | ||
908 | |||
909 | module_init(p54u_init); | ||
910 | module_exit(p54u_exit); | ||