diff options
Diffstat (limited to 'drivers/net/wimax/i2400m/usb.c')
-rw-r--r-- | drivers/net/wimax/i2400m/usb.c | 189 |
1 files changed, 162 insertions, 27 deletions
diff --git a/drivers/net/wimax/i2400m/usb.c b/drivers/net/wimax/i2400m/usb.c index 7eadd11c815b..47e84ef355c5 100644 --- a/drivers/net/wimax/i2400m/usb.c +++ b/drivers/net/wimax/i2400m/usb.c | |||
@@ -58,7 +58,7 @@ | |||
58 | * i2400mu_rx_release() | 58 | * i2400mu_rx_release() |
59 | * i2400mu_tx_release() | 59 | * i2400mu_tx_release() |
60 | * | 60 | * |
61 | * i2400mu_bus_reset() Called by i2400m->bus_reset | 61 | * i2400mu_bus_reset() Called by i2400m_reset |
62 | * __i2400mu_reset() | 62 | * __i2400mu_reset() |
63 | * __i2400mu_send_barker() | 63 | * __i2400mu_send_barker() |
64 | * usb_reset_device() | 64 | * usb_reset_device() |
@@ -71,13 +71,25 @@ | |||
71 | #define D_SUBMODULE usb | 71 | #define D_SUBMODULE usb |
72 | #include "usb-debug-levels.h" | 72 | #include "usb-debug-levels.h" |
73 | 73 | ||
74 | static char i2400mu_debug_params[128]; | ||
75 | module_param_string(debug, i2400mu_debug_params, sizeof(i2400mu_debug_params), | ||
76 | 0644); | ||
77 | MODULE_PARM_DESC(debug, | ||
78 | "String of space-separated NAME:VALUE pairs, where NAMEs " | ||
79 | "are the different debug submodules and VALUE are the " | ||
80 | "initial debug value to set."); | ||
74 | 81 | ||
75 | /* Our firmware file name */ | 82 | /* Our firmware file name */ |
76 | static const char *i2400mu_bus_fw_names[] = { | 83 | static const char *i2400mu_bus_fw_names_5x50[] = { |
77 | #define I2400MU_FW_FILE_NAME_v1_4 "i2400m-fw-usb-1.4.sbcf" | 84 | #define I2400MU_FW_FILE_NAME_v1_4 "i2400m-fw-usb-1.4.sbcf" |
78 | I2400MU_FW_FILE_NAME_v1_4, | 85 | I2400MU_FW_FILE_NAME_v1_4, |
79 | #define I2400MU_FW_FILE_NAME_v1_3 "i2400m-fw-usb-1.3.sbcf" | 86 | NULL, |
80 | I2400MU_FW_FILE_NAME_v1_3, | 87 | }; |
88 | |||
89 | |||
90 | static const char *i2400mu_bus_fw_names_6050[] = { | ||
91 | #define I6050U_FW_FILE_NAME_v1_5 "i6050-fw-usb-1.5.sbcf" | ||
92 | I6050U_FW_FILE_NAME_v1_5, | ||
81 | NULL, | 93 | NULL, |
82 | }; | 94 | }; |
83 | 95 | ||
@@ -160,14 +172,59 @@ int __i2400mu_send_barker(struct i2400mu *i2400mu, | |||
160 | epd = usb_get_epd(i2400mu->usb_iface, endpoint); | 172 | epd = usb_get_epd(i2400mu->usb_iface, endpoint); |
161 | pipe = usb_sndbulkpipe(i2400mu->usb_dev, epd->bEndpointAddress); | 173 | pipe = usb_sndbulkpipe(i2400mu->usb_dev, epd->bEndpointAddress); |
162 | memcpy(buffer, barker, barker_size); | 174 | memcpy(buffer, barker, barker_size); |
175 | retry: | ||
163 | ret = usb_bulk_msg(i2400mu->usb_dev, pipe, buffer, barker_size, | 176 | ret = usb_bulk_msg(i2400mu->usb_dev, pipe, buffer, barker_size, |
164 | &actual_len, HZ); | 177 | &actual_len, 200); |
165 | if (ret < 0) { | 178 | switch (ret) { |
166 | if (ret != -EINVAL) | 179 | case 0: |
167 | dev_err(dev, "E: barker error: %d\n", ret); | 180 | if (actual_len != barker_size) { /* Too short? drop it */ |
168 | } else if (actual_len != barker_size) { | 181 | dev_err(dev, "E: %s: short write (%d B vs %zu " |
169 | dev_err(dev, "E: only %d bytes transmitted\n", actual_len); | 182 | "expected)\n", |
170 | ret = -EIO; | 183 | __func__, actual_len, barker_size); |
184 | ret = -EIO; | ||
185 | } | ||
186 | break; | ||
187 | case -EPIPE: | ||
188 | /* | ||
189 | * Stall -- maybe the device is choking with our | ||
190 | * requests. Clear it and give it some time. If they | ||
191 | * happen to often, it might be another symptom, so we | ||
192 | * reset. | ||
193 | * | ||
194 | * No error handling for usb_clear_halt(0; if it | ||
195 | * works, the retry works; if it fails, this switch | ||
196 | * does the error handling for us. | ||
197 | */ | ||
198 | if (edc_inc(&i2400mu->urb_edc, | ||
199 | 10 * EDC_MAX_ERRORS, EDC_ERROR_TIMEFRAME)) { | ||
200 | dev_err(dev, "E: %s: too many stalls in " | ||
201 | "URB; resetting device\n", __func__); | ||
202 | usb_queue_reset_device(i2400mu->usb_iface); | ||
203 | /* fallthrough */ | ||
204 | } else { | ||
205 | usb_clear_halt(i2400mu->usb_dev, pipe); | ||
206 | msleep(10); /* give the device some time */ | ||
207 | goto retry; | ||
208 | } | ||
209 | case -EINVAL: /* while removing driver */ | ||
210 | case -ENODEV: /* dev disconnect ... */ | ||
211 | case -ENOENT: /* just ignore it */ | ||
212 | case -ESHUTDOWN: /* and exit */ | ||
213 | case -ECONNRESET: | ||
214 | ret = -ESHUTDOWN; | ||
215 | break; | ||
216 | default: /* Some error? */ | ||
217 | if (edc_inc(&i2400mu->urb_edc, | ||
218 | EDC_MAX_ERRORS, EDC_ERROR_TIMEFRAME)) { | ||
219 | dev_err(dev, "E: %s: maximum errors in URB " | ||
220 | "exceeded; resetting device\n", | ||
221 | __func__); | ||
222 | usb_queue_reset_device(i2400mu->usb_iface); | ||
223 | } else { | ||
224 | dev_warn(dev, "W: %s: cannot send URB: %d\n", | ||
225 | __func__, ret); | ||
226 | goto retry; | ||
227 | } | ||
171 | } | 228 | } |
172 | kfree(buffer); | 229 | kfree(buffer); |
173 | error_kzalloc: | 230 | error_kzalloc: |
@@ -232,15 +289,16 @@ int i2400mu_bus_reset(struct i2400m *i2400m, enum i2400m_reset_type rt) | |||
232 | 289 | ||
233 | d_fnstart(3, dev, "(i2400m %p rt %u)\n", i2400m, rt); | 290 | d_fnstart(3, dev, "(i2400m %p rt %u)\n", i2400m, rt); |
234 | if (rt == I2400M_RT_WARM) | 291 | if (rt == I2400M_RT_WARM) |
235 | result = __i2400mu_send_barker(i2400mu, i2400m_WARM_BOOT_BARKER, | 292 | result = __i2400mu_send_barker( |
236 | sizeof(i2400m_WARM_BOOT_BARKER), | 293 | i2400mu, i2400m_WARM_BOOT_BARKER, |
237 | I2400MU_EP_BULK_OUT); | 294 | sizeof(i2400m_WARM_BOOT_BARKER), |
295 | i2400mu->endpoint_cfg.bulk_out); | ||
238 | else if (rt == I2400M_RT_COLD) | 296 | else if (rt == I2400M_RT_COLD) |
239 | result = __i2400mu_send_barker(i2400mu, i2400m_COLD_BOOT_BARKER, | 297 | result = __i2400mu_send_barker( |
240 | sizeof(i2400m_COLD_BOOT_BARKER), | 298 | i2400mu, i2400m_COLD_BOOT_BARKER, |
241 | I2400MU_EP_RESET_COLD); | 299 | sizeof(i2400m_COLD_BOOT_BARKER), |
300 | i2400mu->endpoint_cfg.reset_cold); | ||
242 | else if (rt == I2400M_RT_BUS) { | 301 | else if (rt == I2400M_RT_BUS) { |
243 | do_bus_reset: | ||
244 | result = usb_reset_device(i2400mu->usb_dev); | 302 | result = usb_reset_device(i2400mu->usb_dev); |
245 | switch (result) { | 303 | switch (result) { |
246 | case 0: | 304 | case 0: |
@@ -248,7 +306,7 @@ do_bus_reset: | |||
248 | case -ENODEV: | 306 | case -ENODEV: |
249 | case -ENOENT: | 307 | case -ENOENT: |
250 | case -ESHUTDOWN: | 308 | case -ESHUTDOWN: |
251 | result = rt == I2400M_RT_WARM ? -ENODEV : 0; | 309 | result = 0; |
252 | break; /* We assume the device is disconnected */ | 310 | break; /* We assume the device is disconnected */ |
253 | default: | 311 | default: |
254 | dev_err(dev, "USB reset failed (%d), giving up!\n", | 312 | dev_err(dev, "USB reset failed (%d), giving up!\n", |
@@ -261,10 +319,17 @@ do_bus_reset: | |||
261 | if (result < 0 | 319 | if (result < 0 |
262 | && result != -EINVAL /* device is gone */ | 320 | && result != -EINVAL /* device is gone */ |
263 | && rt != I2400M_RT_BUS) { | 321 | && rt != I2400M_RT_BUS) { |
322 | /* | ||
323 | * Things failed -- resort to lower level reset, that | ||
324 | * we queue in another context; the reason for this is | ||
325 | * that the pre and post reset functionality requires | ||
326 | * the i2400m->init_mutex; RT_WARM and RT_COLD can | ||
327 | * come from areas where i2400m->init_mutex is taken. | ||
328 | */ | ||
264 | dev_err(dev, "%s reset failed (%d); trying USB reset\n", | 329 | dev_err(dev, "%s reset failed (%d); trying USB reset\n", |
265 | rt == I2400M_RT_WARM ? "warm" : "cold", result); | 330 | rt == I2400M_RT_WARM ? "warm" : "cold", result); |
266 | rt = I2400M_RT_BUS; | 331 | usb_queue_reset_device(i2400mu->usb_iface); |
267 | goto do_bus_reset; | 332 | result = -ENODEV; |
268 | } | 333 | } |
269 | d_fnend(3, dev, "(i2400m %p rt %u) = %d\n", i2400m, rt, result); | 334 | d_fnend(3, dev, "(i2400m %p rt %u) = %d\n", i2400m, rt, result); |
270 | return result; | 335 | return result; |
@@ -402,20 +467,33 @@ int i2400mu_probe(struct usb_interface *iface, | |||
402 | 467 | ||
403 | i2400m->bus_tx_block_size = I2400MU_BLK_SIZE; | 468 | i2400m->bus_tx_block_size = I2400MU_BLK_SIZE; |
404 | i2400m->bus_pl_size_max = I2400MU_PL_SIZE_MAX; | 469 | i2400m->bus_pl_size_max = I2400MU_PL_SIZE_MAX; |
470 | i2400m->bus_setup = NULL; | ||
405 | i2400m->bus_dev_start = i2400mu_bus_dev_start; | 471 | i2400m->bus_dev_start = i2400mu_bus_dev_start; |
406 | i2400m->bus_dev_stop = i2400mu_bus_dev_stop; | 472 | i2400m->bus_dev_stop = i2400mu_bus_dev_stop; |
473 | i2400m->bus_release = NULL; | ||
407 | i2400m->bus_tx_kick = i2400mu_bus_tx_kick; | 474 | i2400m->bus_tx_kick = i2400mu_bus_tx_kick; |
408 | i2400m->bus_reset = i2400mu_bus_reset; | 475 | i2400m->bus_reset = i2400mu_bus_reset; |
409 | i2400m->bus_bm_retries = I2400M_BOOT_RETRIES; | 476 | i2400m->bus_bm_retries = I2400M_USB_BOOT_RETRIES; |
410 | i2400m->bus_bm_cmd_send = i2400mu_bus_bm_cmd_send; | 477 | i2400m->bus_bm_cmd_send = i2400mu_bus_bm_cmd_send; |
411 | i2400m->bus_bm_wait_for_ack = i2400mu_bus_bm_wait_for_ack; | 478 | i2400m->bus_bm_wait_for_ack = i2400mu_bus_bm_wait_for_ack; |
412 | i2400m->bus_fw_names = i2400mu_bus_fw_names; | ||
413 | i2400m->bus_bm_mac_addr_impaired = 0; | 479 | i2400m->bus_bm_mac_addr_impaired = 0; |
414 | 480 | ||
481 | if (id->idProduct == USB_DEVICE_ID_I6050) { | ||
482 | i2400m->bus_fw_names = i2400mu_bus_fw_names_6050; | ||
483 | i2400mu->endpoint_cfg.bulk_out = 0; | ||
484 | i2400mu->endpoint_cfg.notification = 3; | ||
485 | i2400mu->endpoint_cfg.reset_cold = 2; | ||
486 | i2400mu->endpoint_cfg.bulk_in = 1; | ||
487 | } else { | ||
488 | i2400m->bus_fw_names = i2400mu_bus_fw_names_5x50; | ||
489 | i2400mu->endpoint_cfg.bulk_out = 0; | ||
490 | i2400mu->endpoint_cfg.notification = 1; | ||
491 | i2400mu->endpoint_cfg.reset_cold = 2; | ||
492 | i2400mu->endpoint_cfg.bulk_in = 3; | ||
493 | } | ||
415 | #ifdef CONFIG_PM | 494 | #ifdef CONFIG_PM |
416 | iface->needs_remote_wakeup = 1; /* autosuspend (15s delay) */ | 495 | iface->needs_remote_wakeup = 1; /* autosuspend (15s delay) */ |
417 | device_init_wakeup(dev, 1); | 496 | device_init_wakeup(dev, 1); |
418 | usb_autopm_enable(i2400mu->usb_iface); | ||
419 | usb_dev->autosuspend_delay = 15 * HZ; | 497 | usb_dev->autosuspend_delay = 15 * HZ; |
420 | usb_dev->autosuspend_disabled = 0; | 498 | usb_dev->autosuspend_disabled = 0; |
421 | #endif | 499 | #endif |
@@ -483,7 +561,10 @@ void i2400mu_disconnect(struct usb_interface *iface) | |||
483 | * So at the end, the three cases require common handling. | 561 | * So at the end, the three cases require common handling. |
484 | * | 562 | * |
485 | * If at the time of this call the device's firmware is not loaded, | 563 | * If at the time of this call the device's firmware is not loaded, |
486 | * nothing has to be done. | 564 | * nothing has to be done. Note we can be "loose" about not reading |
565 | * i2400m->updown under i2400m->init_mutex. If it happens to change | ||
566 | * inmediately, other parts of the call flow will fail and effectively | ||
567 | * catch it. | ||
487 | * | 568 | * |
488 | * If the firmware is loaded, we need to: | 569 | * If the firmware is loaded, we need to: |
489 | * | 570 | * |
@@ -522,6 +603,7 @@ int i2400mu_suspend(struct usb_interface *iface, pm_message_t pm_msg) | |||
522 | #endif | 603 | #endif |
523 | 604 | ||
524 | d_fnstart(3, dev, "(iface %p pm_msg %u)\n", iface, pm_msg.event); | 605 | d_fnstart(3, dev, "(iface %p pm_msg %u)\n", iface, pm_msg.event); |
606 | rmb(); /* see i2400m->updown's documentation */ | ||
525 | if (i2400m->updown == 0) | 607 | if (i2400m->updown == 0) |
526 | goto no_firmware; | 608 | goto no_firmware; |
527 | if (i2400m->state == I2400M_SS_DATA_PATH_CONNECTED && is_autosuspend) { | 609 | if (i2400m->state == I2400M_SS_DATA_PATH_CONNECTED && is_autosuspend) { |
@@ -575,6 +657,7 @@ int i2400mu_resume(struct usb_interface *iface) | |||
575 | struct i2400m *i2400m = &i2400mu->i2400m; | 657 | struct i2400m *i2400m = &i2400mu->i2400m; |
576 | 658 | ||
577 | d_fnstart(3, dev, "(iface %p)\n", iface); | 659 | d_fnstart(3, dev, "(iface %p)\n", iface); |
660 | rmb(); /* see i2400m->updown's documentation */ | ||
578 | if (i2400m->updown == 0) { | 661 | if (i2400m->updown == 0) { |
579 | d_printf(1, dev, "fw was down, no resume neeed\n"); | 662 | d_printf(1, dev, "fw was down, no resume neeed\n"); |
580 | goto out; | 663 | goto out; |
@@ -591,7 +674,54 @@ out: | |||
591 | 674 | ||
592 | 675 | ||
593 | static | 676 | static |
677 | int i2400mu_reset_resume(struct usb_interface *iface) | ||
678 | { | ||
679 | int result; | ||
680 | struct device *dev = &iface->dev; | ||
681 | struct i2400mu *i2400mu = usb_get_intfdata(iface); | ||
682 | struct i2400m *i2400m = &i2400mu->i2400m; | ||
683 | |||
684 | d_fnstart(3, dev, "(iface %p)\n", iface); | ||
685 | result = i2400m_dev_reset_handle(i2400m, "device reset on resume"); | ||
686 | d_fnend(3, dev, "(iface %p) = %d\n", iface, result); | ||
687 | return result < 0 ? result : 0; | ||
688 | } | ||
689 | |||
690 | |||
691 | /* | ||
692 | * Another driver or user space is triggering a reset on the device | ||
693 | * which contains the interface passed as an argument. Cease IO and | ||
694 | * save any device state you need to restore. | ||
695 | * | ||
696 | * If you need to allocate memory here, use GFP_NOIO or GFP_ATOMIC, if | ||
697 | * you are in atomic context. | ||
698 | */ | ||
699 | static | ||
700 | int i2400mu_pre_reset(struct usb_interface *iface) | ||
701 | { | ||
702 | struct i2400mu *i2400mu = usb_get_intfdata(iface); | ||
703 | return i2400m_pre_reset(&i2400mu->i2400m); | ||
704 | } | ||
705 | |||
706 | |||
707 | /* | ||
708 | * The reset has completed. Restore any saved device state and begin | ||
709 | * using the device again. | ||
710 | * | ||
711 | * If you need to allocate memory here, use GFP_NOIO or GFP_ATOMIC, if | ||
712 | * you are in atomic context. | ||
713 | */ | ||
714 | static | ||
715 | int i2400mu_post_reset(struct usb_interface *iface) | ||
716 | { | ||
717 | struct i2400mu *i2400mu = usb_get_intfdata(iface); | ||
718 | return i2400m_post_reset(&i2400mu->i2400m); | ||
719 | } | ||
720 | |||
721 | |||
722 | static | ||
594 | struct usb_device_id i2400mu_id_table[] = { | 723 | struct usb_device_id i2400mu_id_table[] = { |
724 | { USB_DEVICE(0x8086, USB_DEVICE_ID_I6050) }, | ||
595 | { USB_DEVICE(0x8086, 0x0181) }, | 725 | { USB_DEVICE(0x8086, 0x0181) }, |
596 | { USB_DEVICE(0x8086, 0x1403) }, | 726 | { USB_DEVICE(0x8086, 0x1403) }, |
597 | { USB_DEVICE(0x8086, 0x1405) }, | 727 | { USB_DEVICE(0x8086, 0x1405) }, |
@@ -609,8 +739,11 @@ struct usb_driver i2400mu_driver = { | |||
609 | .name = KBUILD_MODNAME, | 739 | .name = KBUILD_MODNAME, |
610 | .suspend = i2400mu_suspend, | 740 | .suspend = i2400mu_suspend, |
611 | .resume = i2400mu_resume, | 741 | .resume = i2400mu_resume, |
742 | .reset_resume = i2400mu_reset_resume, | ||
612 | .probe = i2400mu_probe, | 743 | .probe = i2400mu_probe, |
613 | .disconnect = i2400mu_disconnect, | 744 | .disconnect = i2400mu_disconnect, |
745 | .pre_reset = i2400mu_pre_reset, | ||
746 | .post_reset = i2400mu_post_reset, | ||
614 | .id_table = i2400mu_id_table, | 747 | .id_table = i2400mu_id_table, |
615 | .supports_autosuspend = 1, | 748 | .supports_autosuspend = 1, |
616 | }; | 749 | }; |
@@ -618,6 +751,8 @@ struct usb_driver i2400mu_driver = { | |||
618 | static | 751 | static |
619 | int __init i2400mu_driver_init(void) | 752 | int __init i2400mu_driver_init(void) |
620 | { | 753 | { |
754 | d_parse_params(D_LEVEL, D_LEVEL_SIZE, i2400mu_debug_params, | ||
755 | "i2400m_usb.debug"); | ||
621 | return usb_register(&i2400mu_driver); | 756 | return usb_register(&i2400mu_driver); |
622 | } | 757 | } |
623 | module_init(i2400mu_driver_init); | 758 | module_init(i2400mu_driver_init); |
@@ -632,7 +767,7 @@ void __exit i2400mu_driver_exit(void) | |||
632 | module_exit(i2400mu_driver_exit); | 767 | module_exit(i2400mu_driver_exit); |
633 | 768 | ||
634 | MODULE_AUTHOR("Intel Corporation <linux-wimax@intel.com>"); | 769 | MODULE_AUTHOR("Intel Corporation <linux-wimax@intel.com>"); |
635 | MODULE_DESCRIPTION("Intel 2400M WiMAX networking for USB"); | 770 | MODULE_DESCRIPTION("Driver for USB based Intel Wireless WiMAX Connection 2400M " |
771 | "(5x50 & 6050)"); | ||
636 | MODULE_LICENSE("GPL"); | 772 | MODULE_LICENSE("GPL"); |
637 | MODULE_FIRMWARE(I2400MU_FW_FILE_NAME_v1_4); | 773 | MODULE_FIRMWARE(I2400MU_FW_FILE_NAME_v1_4); |
638 | MODULE_FIRMWARE(I2400MU_FW_FILE_NAME_v1_3); | ||