Commit | Line | Data |
---|---|---|
247e9cff SA |
1 | /* |
2 | * Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved. | |
3 | * | |
4 | * This software is licensed under the terms of the GNU General Public | |
5 | * License version 2, as published by the Free Software Foundation, and | |
6 | * may be copied, distributed, and modified under those terms. | |
7 | * | |
8 | * This program is distributed in the hope that it will be useful, | |
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
11 | * GNU General Public License for more details. | |
12 | */ | |
13 | ||
14 | #include <linux/module.h> | |
247e9cff SA |
15 | #include <linux/kernel.h> |
16 | #include <linux/usb.h> | |
17 | #include <asm/byteorder.h> | |
ff5e4a1d | 18 | #include <linux/kthread.h> |
247e9cff | 19 | |
247e9cff SA |
20 | #include "gdm_usb.h" |
21 | #include "gdm_wimax.h" | |
22 | #include "usb_boot.h" | |
23 | #include "hci.h" | |
24 | ||
25 | #include "usb_ids.h" | |
26 | ||
27 | MODULE_DEVICE_TABLE(usb, id_table); | |
28 | ||
0d660025 | 29 | #define TX_BUF_SIZE 2048 |
247e9cff | 30 | #if defined(CONFIG_WIMAX_GDM72XX_WIMAX2) |
0d660025 | 31 | #define RX_BUF_SIZE (128*1024) /* For packet aggregation */ |
247e9cff | 32 | #else |
0d660025 | 33 | #define RX_BUF_SIZE 2048 |
247e9cff SA |
34 | #endif |
35 | ||
36 | #define GDM7205_PADDING 256 | |
37 | ||
38 | #define H2B(x) __cpu_to_be16(x) | |
39 | #define B2H(x) __be16_to_cpu(x) | |
40 | #define DB2H(x) __be32_to_cpu(x) | |
41 | ||
0d660025 | 42 | #define DOWNLOAD_CONF_VALUE 0x21 |
247e9cff SA |
43 | |
44 | #ifdef CONFIG_WIMAX_GDM72XX_K_MODE | |
45 | ||
46 | static DECLARE_WAIT_QUEUE_HEAD(k_wait); | |
47 | static LIST_HEAD(k_list); | |
48 | static DEFINE_SPINLOCK(k_lock); | |
49 | static int k_mode_stop; | |
50 | ||
0d660025 | 51 | #define K_WAIT_TIME (2 * HZ / 100) |
247e9cff SA |
52 | |
53 | #endif /* CONFIG_WIMAX_GDM72XX_K_MODE */ | |
54 | ||
55 | static int init_usb(struct usbwm_dev *udev); | |
56 | static void release_usb(struct usbwm_dev *udev); | |
57 | ||
58 | /*#define DEBUG */ | |
59 | #ifdef DEBUG | |
60 | static void hexdump(char *title, u8 *data, int len) | |
61 | { | |
62 | int i; | |
63 | ||
64 | printk(KERN_DEBUG "%s: length = %d\n", title, len); | |
65 | for (i = 0; i < len; i++) { | |
66 | printk(KERN_DEBUG "%02x ", data[i]); | |
67 | if ((i & 0xf) == 0xf) | |
68 | printk(KERN_DEBUG "\n"); | |
69 | } | |
70 | printk(KERN_DEBUG "\n"); | |
71 | } | |
72 | #endif | |
73 | ||
74 | static struct usb_tx *alloc_tx_struct(struct tx_cxt *tx) | |
75 | { | |
129575f2 | 76 | struct usb_tx *t = kzalloc(sizeof(*t), GFP_ATOMIC); |
247e9cff | 77 | |
129575f2 BC |
78 | if (!t) |
79 | return NULL; | |
247e9cff | 80 | |
247e9cff SA |
81 | t->urb = usb_alloc_urb(0, GFP_ATOMIC); |
82 | t->buf = kmalloc(TX_BUF_SIZE, GFP_ATOMIC); | |
129575f2 | 83 | if (!t->urb || !t->buf) { |
247e9cff SA |
84 | usb_free_urb(t->urb); |
85 | kfree(t->buf); | |
86 | kfree(t); | |
129575f2 | 87 | return NULL; |
247e9cff | 88 | } |
129575f2 BC |
89 | |
90 | t->tx_cxt = tx; | |
91 | ||
92 | return t; | |
247e9cff SA |
93 | } |
94 | ||
95 | static void free_tx_struct(struct usb_tx *t) | |
96 | { | |
97 | if (t) { | |
98 | usb_free_urb(t->urb); | |
99 | kfree(t->buf); | |
100 | kfree(t); | |
101 | } | |
102 | } | |
103 | ||
104 | static struct usb_rx *alloc_rx_struct(struct rx_cxt *rx) | |
105 | { | |
129575f2 | 106 | struct usb_rx *r = kzalloc(sizeof(*r), GFP_ATOMIC); |
247e9cff | 107 | |
129575f2 BC |
108 | if (!r) |
109 | return NULL; | |
247e9cff SA |
110 | |
111 | r->urb = usb_alloc_urb(0, GFP_ATOMIC); | |
112 | r->buf = kmalloc(RX_BUF_SIZE, GFP_ATOMIC); | |
129575f2 | 113 | if (!r->urb || !r->buf) { |
247e9cff SA |
114 | usb_free_urb(r->urb); |
115 | kfree(r->buf); | |
116 | kfree(r); | |
129575f2 | 117 | return NULL; |
247e9cff | 118 | } |
129575f2 BC |
119 | |
120 | r->rx_cxt = rx; | |
121 | return r; | |
247e9cff SA |
122 | } |
123 | ||
124 | static void free_rx_struct(struct usb_rx *r) | |
125 | { | |
126 | if (r) { | |
127 | usb_free_urb(r->urb); | |
128 | kfree(r->buf); | |
129 | kfree(r); | |
130 | } | |
131 | } | |
132 | ||
133 | /* Before this function is called, spin lock should be locked. */ | |
134 | static struct usb_tx *get_tx_struct(struct tx_cxt *tx, int *no_spc) | |
135 | { | |
136 | struct usb_tx *t; | |
137 | ||
138 | if (list_empty(&tx->free_list)) { | |
139 | *no_spc = 1; | |
140 | return NULL; | |
141 | } | |
142 | ||
143 | t = list_entry(tx->free_list.next, struct usb_tx, list); | |
144 | list_del(&t->list); | |
145 | ||
146 | *no_spc = list_empty(&tx->free_list) ? 1 : 0; | |
147 | ||
148 | return t; | |
149 | } | |
150 | ||
151 | /* Before this function is called, spin lock should be locked. */ | |
152 | static void put_tx_struct(struct tx_cxt *tx, struct usb_tx *t) | |
153 | { | |
154 | list_add_tail(&t->list, &tx->free_list); | |
155 | } | |
156 | ||
157 | /* Before this function is called, spin lock should be locked. */ | |
158 | static struct usb_rx *get_rx_struct(struct rx_cxt *rx) | |
159 | { | |
160 | struct usb_rx *r; | |
161 | ||
162 | if (list_empty(&rx->free_list)) { | |
163 | r = alloc_rx_struct(rx); | |
164 | if (r == NULL) | |
165 | return NULL; | |
166 | ||
167 | list_add(&r->list, &rx->free_list); | |
168 | } | |
169 | ||
170 | r = list_entry(rx->free_list.next, struct usb_rx, list); | |
e5d2cb4a | 171 | list_move_tail(&r->list, &rx->used_list); |
247e9cff SA |
172 | |
173 | return r; | |
174 | } | |
175 | ||
176 | /* Before this function is called, spin lock should be locked. */ | |
177 | static void put_rx_struct(struct rx_cxt *rx, struct usb_rx *r) | |
178 | { | |
73295fe1 | 179 | list_move(&r->list, &rx->free_list); |
247e9cff SA |
180 | } |
181 | ||
182 | static int init_usb(struct usbwm_dev *udev) | |
183 | { | |
184 | int ret = 0, i; | |
185 | struct tx_cxt *tx = &udev->tx; | |
186 | struct rx_cxt *rx = &udev->rx; | |
187 | struct usb_tx *t; | |
188 | struct usb_rx *r; | |
189 | ||
190 | INIT_LIST_HEAD(&tx->free_list); | |
191 | INIT_LIST_HEAD(&tx->sdu_list); | |
192 | INIT_LIST_HEAD(&tx->hci_list); | |
193 | #if defined(CONFIG_WIMAX_GDM72XX_USB_PM) || defined(CONFIG_WIMAX_GDM72XX_K_MODE) | |
194 | INIT_LIST_HEAD(&tx->pending_list); | |
195 | #endif | |
196 | ||
197 | INIT_LIST_HEAD(&rx->free_list); | |
198 | INIT_LIST_HEAD(&rx->used_list); | |
199 | ||
200 | spin_lock_init(&tx->lock); | |
201 | spin_lock_init(&rx->lock); | |
202 | ||
203 | for (i = 0; i < MAX_NR_SDU_BUF; i++) { | |
204 | t = alloc_tx_struct(tx); | |
205 | if (t == NULL) { | |
206 | ret = -ENOMEM; | |
207 | goto fail; | |
208 | } | |
209 | list_add(&t->list, &tx->free_list); | |
210 | } | |
211 | ||
212 | r = alloc_rx_struct(rx); | |
213 | if (r == NULL) { | |
214 | ret = -ENOMEM; | |
215 | goto fail; | |
216 | } | |
217 | ||
218 | list_add(&r->list, &rx->free_list); | |
219 | return ret; | |
220 | ||
221 | fail: | |
222 | release_usb(udev); | |
223 | return ret; | |
224 | } | |
225 | ||
226 | static void release_usb(struct usbwm_dev *udev) | |
227 | { | |
228 | struct tx_cxt *tx = &udev->tx; | |
229 | struct rx_cxt *rx = &udev->rx; | |
230 | struct usb_tx *t, *t_next; | |
231 | struct usb_rx *r, *r_next; | |
232 | ||
233 | list_for_each_entry_safe(t, t_next, &tx->sdu_list, list) { | |
234 | list_del(&t->list); | |
235 | free_tx_struct(t); | |
236 | } | |
237 | ||
238 | list_for_each_entry_safe(t, t_next, &tx->hci_list, list) { | |
239 | list_del(&t->list); | |
240 | free_tx_struct(t); | |
241 | } | |
242 | ||
243 | list_for_each_entry_safe(t, t_next, &tx->free_list, list) { | |
244 | list_del(&t->list); | |
245 | free_tx_struct(t); | |
246 | } | |
247 | ||
248 | list_for_each_entry_safe(r, r_next, &rx->free_list, list) { | |
249 | list_del(&r->list); | |
250 | free_rx_struct(r); | |
251 | } | |
252 | ||
253 | list_for_each_entry_safe(r, r_next, &rx->used_list, list) { | |
254 | list_del(&r->list); | |
255 | free_rx_struct(r); | |
256 | } | |
257 | } | |
258 | ||
0c16ae76 | 259 | static void __gdm_usb_send_complete(struct urb *urb) |
247e9cff SA |
260 | { |
261 | struct usb_tx *t = urb->context; | |
262 | struct tx_cxt *tx = t->tx_cxt; | |
263 | u8 *pkt = t->buf; | |
264 | u16 cmd_evt; | |
247e9cff SA |
265 | |
266 | /* Completion by usb_unlink_urb */ | |
267 | if (urb->status == -ECONNRESET) | |
268 | return; | |
269 | ||
247e9cff SA |
270 | if (t->callback) |
271 | t->callback(t->cb_data); | |
272 | ||
273 | /* Delete from sdu list or hci list. */ | |
274 | list_del(&t->list); | |
275 | ||
276 | cmd_evt = (pkt[0] << 8) | pkt[1]; | |
277 | if (cmd_evt == WIMAX_TX_SDU) | |
278 | put_tx_struct(tx, t); | |
279 | else | |
280 | free_tx_struct(t); | |
dd13c86b BC |
281 | } |
282 | ||
283 | static void gdm_usb_send_complete(struct urb *urb) | |
284 | { | |
0c16ae76 BC |
285 | struct usb_tx *t = urb->context; |
286 | struct tx_cxt *tx = t->tx_cxt; | |
287 | unsigned long flags; | |
dd13c86b | 288 | |
0c16ae76 BC |
289 | spin_lock_irqsave(&tx->lock, flags); |
290 | __gdm_usb_send_complete(urb); | |
291 | spin_unlock_irqrestore(&tx->lock, flags); | |
247e9cff SA |
292 | } |
293 | ||
294 | static int gdm_usb_send(void *priv_dev, void *data, int len, | |
295 | void (*cb)(void *data), void *cb_data) | |
296 | { | |
297 | struct usbwm_dev *udev = priv_dev; | |
298 | struct usb_device *usbdev = udev->usbdev; | |
299 | struct tx_cxt *tx = &udev->tx; | |
300 | struct usb_tx *t; | |
301 | int padding = udev->padding; | |
302 | int no_spc = 0, ret; | |
303 | u8 *pkt = data; | |
304 | u16 cmd_evt; | |
305 | unsigned long flags; | |
306 | ||
307 | if (!udev->usbdev) { | |
308 | printk(KERN_ERR "%s: No such device\n", __func__); | |
309 | return -ENODEV; | |
310 | } | |
311 | ||
312 | BUG_ON(len > TX_BUF_SIZE - padding - 1); | |
313 | ||
314 | spin_lock_irqsave(&tx->lock, flags); | |
315 | ||
316 | cmd_evt = (pkt[0] << 8) | pkt[1]; | |
317 | if (cmd_evt == WIMAX_TX_SDU) { | |
318 | t = get_tx_struct(tx, &no_spc); | |
319 | if (t == NULL) { | |
320 | /* This case must not happen. */ | |
321 | spin_unlock_irqrestore(&tx->lock, flags); | |
322 | return -ENOSPC; | |
323 | } | |
324 | list_add_tail(&t->list, &tx->sdu_list); | |
325 | } else { | |
326 | t = alloc_tx_struct(tx); | |
327 | if (t == NULL) { | |
328 | spin_unlock_irqrestore(&tx->lock, flags); | |
329 | return -ENOMEM; | |
330 | } | |
331 | list_add_tail(&t->list, &tx->hci_list); | |
332 | } | |
333 | ||
334 | memcpy(t->buf + padding, data, len); | |
335 | t->callback = cb; | |
336 | t->cb_data = cb_data; | |
337 | ||
338 | /* | |
339 | * In some cases, USB Module of WiMax is blocked when data size is | |
340 | * the multiple of 512. So, increment length by one in that case. | |
341 | */ | |
342 | if ((len % 512) == 0) | |
343 | len++; | |
344 | ||
345 | usb_fill_bulk_urb(t->urb, | |
346 | usbdev, | |
347 | usb_sndbulkpipe(usbdev, 1), | |
348 | t->buf, | |
349 | len + padding, | |
350 | gdm_usb_send_complete, | |
351 | t); | |
352 | ||
353 | #ifdef DEBUG | |
354 | hexdump("usb_send", t->buf, len + padding); | |
355 | #endif | |
356 | #ifdef CONFIG_WIMAX_GDM72XX_USB_PM | |
357 | if (usbdev->state & USB_STATE_SUSPENDED) { | |
358 | list_add_tail(&t->p_list, &tx->pending_list); | |
359 | schedule_work(&udev->pm_ws); | |
360 | goto out; | |
361 | } | |
362 | #endif /* CONFIG_WIMAX_GDM72XX_USB_PM */ | |
363 | ||
364 | #ifdef CONFIG_WIMAX_GDM72XX_K_MODE | |
365 | if (udev->bw_switch) { | |
366 | list_add_tail(&t->p_list, &tx->pending_list); | |
367 | goto out; | |
368 | } else if (cmd_evt == WIMAX_SCAN) { | |
369 | struct rx_cxt *rx; | |
370 | struct usb_rx *r; | |
371 | ||
372 | rx = &udev->rx; | |
373 | ||
374 | list_for_each_entry(r, &rx->used_list, list) | |
375 | usb_unlink_urb(r->urb); | |
376 | udev->bw_switch = 1; | |
377 | ||
378 | spin_lock(&k_lock); | |
379 | list_add_tail(&udev->list, &k_list); | |
380 | spin_unlock(&k_lock); | |
381 | ||
382 | wake_up(&k_wait); | |
383 | } | |
384 | #endif /* CONFIG_WIMAX_GDM72XX_K_MODE */ | |
385 | ||
386 | ret = usb_submit_urb(t->urb, GFP_ATOMIC); | |
387 | if (ret) | |
388 | goto send_fail; | |
389 | ||
390 | #ifdef CONFIG_WIMAX_GDM72XX_USB_PM | |
391 | usb_mark_last_busy(usbdev); | |
392 | #endif /* CONFIG_WIMAX_GDM72XX_USB_PM */ | |
393 | ||
394 | #if defined(CONFIG_WIMAX_GDM72XX_USB_PM) || defined(CONFIG_WIMAX_GDM72XX_K_MODE) | |
395 | out: | |
396 | #endif | |
397 | spin_unlock_irqrestore(&tx->lock, flags); | |
398 | ||
399 | if (no_spc) | |
400 | return -ENOSPC; | |
401 | ||
402 | return 0; | |
403 | ||
404 | send_fail: | |
405 | t->callback = NULL; | |
0c16ae76 | 406 | __gdm_usb_send_complete(t->urb); |
247e9cff SA |
407 | spin_unlock_irqrestore(&tx->lock, flags); |
408 | return ret; | |
409 | } | |
410 | ||
411 | static void gdm_usb_rcv_complete(struct urb *urb) | |
412 | { | |
413 | struct usb_rx *r = urb->context; | |
414 | struct rx_cxt *rx = r->rx_cxt; | |
415 | struct usbwm_dev *udev = container_of(r->rx_cxt, struct usbwm_dev, rx); | |
416 | struct tx_cxt *tx = &udev->tx; | |
417 | struct usb_tx *t; | |
418 | u16 cmd_evt; | |
419 | unsigned long flags; | |
420 | ||
421 | #ifdef CONFIG_WIMAX_GDM72XX_USB_PM | |
422 | struct usb_device *dev = urb->dev; | |
423 | #endif | |
424 | ||
425 | /* Completion by usb_unlink_urb */ | |
426 | if (urb->status == -ECONNRESET) | |
427 | return; | |
428 | ||
429 | spin_lock_irqsave(&tx->lock, flags); | |
430 | ||
431 | if (!urb->status) { | |
432 | cmd_evt = (r->buf[0] << 8) | (r->buf[1]); | |
433 | #ifdef DEBUG | |
434 | hexdump("usb_receive", r->buf, urb->actual_length); | |
435 | #endif | |
436 | if (cmd_evt == WIMAX_SDU_TX_FLOW) { | |
437 | if (r->buf[4] == 0) { | |
438 | #ifdef DEBUG | |
439 | printk(KERN_DEBUG "WIMAX ==> STOP SDU TX\n"); | |
440 | #endif | |
441 | list_for_each_entry(t, &tx->sdu_list, list) | |
442 | usb_unlink_urb(t->urb); | |
443 | } else if (r->buf[4] == 1) { | |
444 | #ifdef DEBUG | |
445 | printk(KERN_DEBUG "WIMAX ==> START SDU TX\n"); | |
446 | #endif | |
447 | list_for_each_entry(t, &tx->sdu_list, list) { | |
448 | usb_submit_urb(t->urb, GFP_ATOMIC); | |
449 | } | |
450 | /* | |
451 | * If free buffer for sdu tx doesn't | |
452 | * exist, then tx queue should not be | |
453 | * woken. For this reason, don't pass | |
454 | * the command, START_SDU_TX. | |
455 | */ | |
456 | if (list_empty(&tx->free_list)) | |
457 | urb->actual_length = 0; | |
458 | } | |
459 | } | |
460 | } | |
461 | ||
462 | if (!urb->status && r->callback) | |
463 | r->callback(r->cb_data, r->buf, urb->actual_length); | |
464 | ||
465 | spin_lock(&rx->lock); | |
466 | put_rx_struct(rx, r); | |
467 | spin_unlock(&rx->lock); | |
468 | ||
469 | spin_unlock_irqrestore(&tx->lock, flags); | |
470 | ||
471 | #ifdef CONFIG_WIMAX_GDM72XX_USB_PM | |
472 | usb_mark_last_busy(dev); | |
473 | #endif | |
474 | } | |
475 | ||
476 | static int gdm_usb_receive(void *priv_dev, | |
477 | void (*cb)(void *cb_data, void *data, int len), | |
478 | void *cb_data) | |
479 | { | |
480 | struct usbwm_dev *udev = priv_dev; | |
481 | struct usb_device *usbdev = udev->usbdev; | |
482 | struct rx_cxt *rx = &udev->rx; | |
483 | struct usb_rx *r; | |
484 | unsigned long flags; | |
485 | ||
486 | if (!udev->usbdev) { | |
487 | printk(KERN_ERR "%s: No such device\n", __func__); | |
488 | return -ENODEV; | |
489 | } | |
490 | ||
491 | spin_lock_irqsave(&rx->lock, flags); | |
492 | r = get_rx_struct(rx); | |
493 | spin_unlock_irqrestore(&rx->lock, flags); | |
494 | ||
495 | if (r == NULL) | |
496 | return -ENOMEM; | |
497 | ||
498 | r->callback = cb; | |
499 | r->cb_data = cb_data; | |
500 | ||
501 | usb_fill_bulk_urb(r->urb, | |
502 | usbdev, | |
503 | usb_rcvbulkpipe(usbdev, 0x82), | |
504 | r->buf, | |
505 | RX_BUF_SIZE, | |
506 | gdm_usb_rcv_complete, | |
507 | r); | |
508 | ||
509 | return usb_submit_urb(r->urb, GFP_ATOMIC); | |
510 | } | |
511 | ||
512 | #ifdef CONFIG_WIMAX_GDM72XX_USB_PM | |
513 | static void do_pm_control(struct work_struct *work) | |
514 | { | |
515 | struct usbwm_dev *udev = container_of(work, struct usbwm_dev, pm_ws); | |
516 | struct tx_cxt *tx = &udev->tx; | |
517 | int ret; | |
518 | unsigned long flags; | |
519 | ||
520 | ret = usb_autopm_get_interface(udev->intf); | |
521 | if (!ret) | |
522 | usb_autopm_put_interface(udev->intf); | |
523 | ||
524 | spin_lock_irqsave(&tx->lock, flags); | |
525 | if (!(udev->usbdev->state & USB_STATE_SUSPENDED) | |
526 | && (!list_empty(&tx->hci_list) || !list_empty(&tx->sdu_list))) { | |
527 | struct usb_tx *t, *temp; | |
528 | ||
529 | list_for_each_entry_safe(t, temp, &tx->pending_list, p_list) { | |
530 | list_del(&t->p_list); | |
531 | ret = usb_submit_urb(t->urb, GFP_ATOMIC); | |
532 | ||
533 | if (ret) { | |
534 | t->callback = NULL; | |
0c16ae76 | 535 | __gdm_usb_send_complete(t->urb); |
247e9cff SA |
536 | } |
537 | } | |
538 | } | |
539 | spin_unlock_irqrestore(&tx->lock, flags); | |
540 | } | |
541 | #endif /* CONFIG_WIMAX_GDM72XX_USB_PM */ | |
542 | ||
543 | static int gdm_usb_probe(struct usb_interface *intf, | |
544 | const struct usb_device_id *id) | |
545 | { | |
546 | int ret = 0; | |
547 | u8 bConfigurationValue; | |
548 | struct phy_dev *phy_dev = NULL; | |
549 | struct usbwm_dev *udev = NULL; | |
550 | u16 idVendor, idProduct, bcdDevice; | |
551 | ||
552 | struct usb_device *usbdev = interface_to_usbdev(intf); | |
553 | ||
554 | usb_get_dev(usbdev); | |
555 | bConfigurationValue = usbdev->actconfig->desc.bConfigurationValue; | |
556 | ||
557 | /*USB description is set up with Little-Endian*/ | |
558 | idVendor = L2H(usbdev->descriptor.idVendor); | |
559 | idProduct = L2H(usbdev->descriptor.idProduct); | |
560 | bcdDevice = L2H(usbdev->descriptor.bcdDevice); | |
561 | ||
562 | printk(KERN_INFO "Found GDM USB VID = 0x%04x PID = 0x%04x...\n", | |
563 | idVendor, idProduct); | |
564 | printk(KERN_INFO "GCT WiMax driver version %s\n", DRIVER_VERSION); | |
565 | ||
566 | ||
567 | if (idProduct == EMERGENCY_PID) { | |
568 | ret = usb_emergency(usbdev); | |
569 | goto out; | |
570 | } | |
571 | ||
572 | /* Support for EEPROM bootloader */ | |
573 | if (bConfigurationValue == DOWNLOAD_CONF_VALUE || | |
574 | idProduct & B_DOWNLOAD) { | |
575 | ret = usb_boot(usbdev, bcdDevice); | |
576 | goto out; | |
577 | } | |
578 | ||
7fc03add | 579 | phy_dev = kzalloc(sizeof(*phy_dev), GFP_KERNEL); |
247e9cff SA |
580 | if (phy_dev == NULL) { |
581 | ret = -ENOMEM; | |
582 | goto out; | |
583 | } | |
7fc03add | 584 | udev = kzalloc(sizeof(*udev), GFP_KERNEL); |
247e9cff SA |
585 | if (udev == NULL) { |
586 | ret = -ENOMEM; | |
587 | goto out; | |
588 | } | |
589 | ||
247e9cff SA |
590 | if (idProduct == 0x7205 || idProduct == 0x7206) |
591 | udev->padding = GDM7205_PADDING; | |
592 | else | |
593 | udev->padding = 0; | |
594 | ||
595 | phy_dev->priv_dev = (void *)udev; | |
596 | phy_dev->send_func = gdm_usb_send; | |
597 | phy_dev->rcv_func = gdm_usb_receive; | |
598 | ||
599 | ret = init_usb(udev); | |
600 | if (ret < 0) | |
601 | goto out; | |
602 | ||
603 | udev->usbdev = usbdev; | |
604 | ||
605 | #ifdef CONFIG_WIMAX_GDM72XX_USB_PM | |
606 | udev->intf = intf; | |
607 | ||
608 | intf->needs_remote_wakeup = 1; | |
609 | device_init_wakeup(&intf->dev, 1); | |
610 | ||
611 | pm_runtime_set_autosuspend_delay(&usbdev->dev, 10 * 1000); /* msec */ | |
612 | ||
613 | INIT_WORK(&udev->pm_ws, do_pm_control); | |
614 | #endif /* CONFIG_WIMAX_GDM72XX_USB_PM */ | |
615 | ||
54bc1ff1 | 616 | ret = register_wimax_device(phy_dev, &intf->dev); |
247e9cff SA |
617 | |
618 | out: | |
619 | if (ret) { | |
620 | kfree(phy_dev); | |
621 | kfree(udev); | |
622 | } | |
623 | usb_set_intfdata(intf, phy_dev); | |
624 | return ret; | |
625 | } | |
626 | ||
627 | static void gdm_usb_disconnect(struct usb_interface *intf) | |
628 | { | |
629 | u8 bConfigurationValue; | |
630 | struct phy_dev *phy_dev; | |
631 | struct usbwm_dev *udev; | |
632 | u16 idProduct; | |
633 | struct usb_device *usbdev = interface_to_usbdev(intf); | |
634 | ||
635 | bConfigurationValue = usbdev->actconfig->desc.bConfigurationValue; | |
636 | phy_dev = usb_get_intfdata(intf); | |
637 | ||
638 | /*USB description is set up with Little-Endian*/ | |
639 | idProduct = L2H(usbdev->descriptor.idProduct); | |
640 | ||
641 | if (idProduct != EMERGENCY_PID && | |
642 | bConfigurationValue != DOWNLOAD_CONF_VALUE && | |
643 | (idProduct & B_DOWNLOAD) == 0) { | |
644 | udev = phy_dev->priv_dev; | |
645 | udev->usbdev = NULL; | |
646 | ||
647 | unregister_wimax_device(phy_dev); | |
648 | release_usb(udev); | |
649 | kfree(udev); | |
650 | kfree(phy_dev); | |
651 | } | |
652 | ||
653 | usb_put_dev(usbdev); | |
654 | } | |
655 | ||
656 | #ifdef CONFIG_WIMAX_GDM72XX_USB_PM | |
657 | static int gdm_suspend(struct usb_interface *intf, pm_message_t pm_msg) | |
658 | { | |
659 | struct phy_dev *phy_dev; | |
660 | struct usbwm_dev *udev; | |
661 | struct rx_cxt *rx; | |
662 | struct usb_rx *r; | |
663 | ||
664 | phy_dev = usb_get_intfdata(intf); | |
665 | udev = phy_dev->priv_dev; | |
666 | rx = &udev->rx; | |
667 | ||
668 | list_for_each_entry(r, &rx->used_list, list) | |
669 | usb_unlink_urb(r->urb); | |
670 | ||
671 | return 0; | |
672 | } | |
673 | ||
674 | static int gdm_resume(struct usb_interface *intf) | |
675 | { | |
676 | struct phy_dev *phy_dev; | |
677 | struct usbwm_dev *udev; | |
678 | struct rx_cxt *rx; | |
679 | struct usb_rx *r; | |
680 | ||
681 | phy_dev = usb_get_intfdata(intf); | |
682 | udev = phy_dev->priv_dev; | |
683 | rx = &udev->rx; | |
684 | ||
685 | list_for_each_entry(r, &rx->used_list, list) | |
686 | usb_submit_urb(r->urb, GFP_ATOMIC); | |
687 | ||
688 | return 0; | |
689 | } | |
690 | ||
691 | #endif /* CONFIG_WIMAX_GDM72XX_USB_PM */ | |
692 | ||
693 | #ifdef CONFIG_WIMAX_GDM72XX_K_MODE | |
694 | static int k_mode_thread(void *arg) | |
695 | { | |
696 | struct usbwm_dev *udev; | |
697 | struct tx_cxt *tx; | |
698 | struct rx_cxt *rx; | |
699 | struct usb_tx *t, *temp; | |
700 | struct usb_rx *r; | |
701 | unsigned long flags, flags2, expire; | |
702 | int ret; | |
703 | ||
704 | daemonize("k_mode_wimax"); | |
705 | ||
706 | while (!k_mode_stop) { | |
707 | ||
708 | spin_lock_irqsave(&k_lock, flags2); | |
709 | while (!list_empty(&k_list)) { | |
710 | ||
711 | udev = list_entry(k_list.next, struct usbwm_dev, list); | |
712 | tx = &udev->tx; | |
713 | rx = &udev->rx; | |
714 | ||
715 | list_del(&udev->list); | |
716 | spin_unlock_irqrestore(&k_lock, flags2); | |
717 | ||
718 | expire = jiffies + K_WAIT_TIME; | |
719 | while (jiffies < expire) | |
720 | schedule_timeout(K_WAIT_TIME); | |
721 | ||
722 | list_for_each_entry(r, &rx->used_list, list) | |
723 | usb_submit_urb(r->urb, GFP_ATOMIC); | |
724 | ||
725 | spin_lock_irqsave(&tx->lock, flags); | |
726 | ||
727 | list_for_each_entry_safe(t, temp, &tx->pending_list, | |
728 | p_list) { | |
729 | list_del(&t->p_list); | |
730 | ret = usb_submit_urb(t->urb, GFP_ATOMIC); | |
731 | ||
732 | if (ret) { | |
733 | t->callback = NULL; | |
0c16ae76 | 734 | __gdm_usb_send_complete(t->urb); |
247e9cff SA |
735 | } |
736 | } | |
737 | ||
738 | udev->bw_switch = 0; | |
739 | spin_unlock_irqrestore(&tx->lock, flags); | |
740 | ||
741 | spin_lock_irqsave(&k_lock, flags2); | |
742 | } | |
743 | spin_unlock_irqrestore(&k_lock, flags2); | |
744 | ||
745 | interruptible_sleep_on(&k_wait); | |
746 | } | |
747 | return 0; | |
748 | } | |
749 | #endif /* CONFIG_WIMAX_GDM72XX_K_MODE */ | |
750 | ||
751 | static struct usb_driver gdm_usb_driver = { | |
752 | .name = "gdm_wimax", | |
753 | .probe = gdm_usb_probe, | |
754 | .disconnect = gdm_usb_disconnect, | |
755 | .id_table = id_table, | |
756 | #ifdef CONFIG_WIMAX_GDM72XX_USB_PM | |
757 | .supports_autosuspend = 1, | |
758 | .suspend = gdm_suspend, | |
759 | .resume = gdm_resume, | |
760 | .reset_resume = gdm_resume, | |
761 | #endif | |
762 | }; | |
763 | ||
764 | static int __init usb_gdm_wimax_init(void) | |
765 | { | |
766 | #ifdef CONFIG_WIMAX_GDM72XX_K_MODE | |
ff5e4a1d | 767 | kthread_run(k_mode_thread, NULL, "WiMax_thread"); |
247e9cff SA |
768 | #endif /* CONFIG_WIMAX_GDM72XX_K_MODE */ |
769 | return usb_register(&gdm_usb_driver); | |
770 | } | |
771 | ||
772 | static void __exit usb_gdm_wimax_exit(void) | |
773 | { | |
774 | #ifdef CONFIG_WIMAX_GDM72XX_K_MODE | |
775 | k_mode_stop = 1; | |
776 | wake_up(&k_wait); | |
777 | #endif | |
778 | usb_deregister(&gdm_usb_driver); | |
779 | } | |
780 | ||
781 | module_init(usb_gdm_wimax_init); | |
782 | module_exit(usb_gdm_wimax_exit); | |
783 | ||
784 | MODULE_VERSION(DRIVER_VERSION); | |
785 | MODULE_DESCRIPTION("GCT WiMax Device Driver"); | |
786 | MODULE_AUTHOR("Ethan Park"); | |
787 | MODULE_LICENSE("GPL"); |