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