diff options
Diffstat (limited to 'drivers/net/wimax/i2400m/driver.c')
-rw-r--r-- | drivers/net/wimax/i2400m/driver.c | 500 |
1 files changed, 364 insertions, 136 deletions
diff --git a/drivers/net/wimax/i2400m/driver.c b/drivers/net/wimax/i2400m/driver.c index 304f0443ca4b..96a615fe09de 100644 --- a/drivers/net/wimax/i2400m/driver.c +++ b/drivers/net/wimax/i2400m/driver.c | |||
@@ -41,8 +41,10 @@ | |||
41 | * __i2400m_dev_start() | 41 | * __i2400m_dev_start() |
42 | * | 42 | * |
43 | * i2400m_setup() | 43 | * i2400m_setup() |
44 | * i2400m->bus_setup() | ||
44 | * i2400m_bootrom_init() | 45 | * i2400m_bootrom_init() |
45 | * register_netdev() | 46 | * register_netdev() |
47 | * wimax_dev_add() | ||
46 | * i2400m_dev_start() | 48 | * i2400m_dev_start() |
47 | * __i2400m_dev_start() | 49 | * __i2400m_dev_start() |
48 | * i2400m_dev_bootstrap() | 50 | * i2400m_dev_bootstrap() |
@@ -50,15 +52,15 @@ | |||
50 | * i2400m->bus_dev_start() | 52 | * i2400m->bus_dev_start() |
51 | * i2400m_firmware_check() | 53 | * i2400m_firmware_check() |
52 | * i2400m_check_mac_addr() | 54 | * i2400m_check_mac_addr() |
53 | * wimax_dev_add() | ||
54 | * | 55 | * |
55 | * i2400m_release() | 56 | * i2400m_release() |
56 | * wimax_dev_rm() | ||
57 | * i2400m_dev_stop() | 57 | * i2400m_dev_stop() |
58 | * __i2400m_dev_stop() | 58 | * __i2400m_dev_stop() |
59 | * i2400m_dev_shutdown() | 59 | * i2400m_dev_shutdown() |
60 | * i2400m->bus_dev_stop() | 60 | * i2400m->bus_dev_stop() |
61 | * i2400m_tx_release() | 61 | * i2400m_tx_release() |
62 | * i2400m->bus_release() | ||
63 | * wimax_dev_rm() | ||
62 | * unregister_netdev() | 64 | * unregister_netdev() |
63 | */ | 65 | */ |
64 | #include "i2400m.h" | 66 | #include "i2400m.h" |
@@ -66,6 +68,7 @@ | |||
66 | #include <linux/wimax/i2400m.h> | 68 | #include <linux/wimax/i2400m.h> |
67 | #include <linux/module.h> | 69 | #include <linux/module.h> |
68 | #include <linux/moduleparam.h> | 70 | #include <linux/moduleparam.h> |
71 | #include <linux/suspend.h> | ||
69 | 72 | ||
70 | #define D_SUBMODULE driver | 73 | #define D_SUBMODULE driver |
71 | #include "debug-levels.h" | 74 | #include "debug-levels.h" |
@@ -90,76 +93,39 @@ MODULE_PARM_DESC(power_save_disabled, | |||
90 | "False by default (so the device is told to do power " | 93 | "False by default (so the device is told to do power " |
91 | "saving)."); | 94 | "saving)."); |
92 | 95 | ||
93 | /** | 96 | static char i2400m_debug_params[128]; |
94 | * i2400m_queue_work - schedule work on a i2400m's queue | 97 | module_param_string(debug, i2400m_debug_params, sizeof(i2400m_debug_params), |
95 | * | 98 | 0644); |
96 | * @i2400m: device descriptor | 99 | MODULE_PARM_DESC(debug, |
97 | * | 100 | "String of space-separated NAME:VALUE pairs, where NAMEs " |
98 | * @fn: function to run to execute work. It gets passed a 'struct | 101 | "are the different debug submodules and VALUE are the " |
99 | * work_struct' that is wrapped in a 'struct i2400m_work'. Once | 102 | "initial debug value to set."); |
100 | * done, you have to (1) i2400m_put(i2400m_work->i2400m) and then | 103 | |
101 | * (2) kfree(i2400m_work). | 104 | static char i2400m_barkers_params[128]; |
102 | * | 105 | module_param_string(barkers, i2400m_barkers_params, |
103 | * @gfp_flags: GFP flags for memory allocation. | 106 | sizeof(i2400m_barkers_params), 0644); |
104 | * | 107 | MODULE_PARM_DESC(barkers, |
105 | * @pl: pointer to a payload buffer that you want to pass to the _work | 108 | "String of comma-separated 32-bit values; each is " |
106 | * function. Use this to pack (for example) a struct with extra | 109 | "recognized as the value the device sends as a reboot " |
107 | * arguments. | 110 | "signal; values are appended to a list--setting one value " |
108 | * | 111 | "as zero cleans the existing list and starts a new one."); |
109 | * @pl_size: size of the payload buffer. | 112 | |
110 | * | 113 | static |
111 | * We do this quite often, so this just saves typing; allocate a | 114 | struct i2400m_work *__i2400m_work_setup( |
112 | * wrapper for a i2400m, get a ref to it, pack arguments and launch | 115 | struct i2400m *i2400m, void (*fn)(struct work_struct *), |
113 | * the work. | 116 | gfp_t gfp_flags, const void *pl, size_t pl_size) |
114 | * | ||
115 | * A usual workflow is: | ||
116 | * | ||
117 | * struct my_work_args { | ||
118 | * void *something; | ||
119 | * int whatever; | ||
120 | * }; | ||
121 | * ... | ||
122 | * | ||
123 | * struct my_work_args my_args = { | ||
124 | * .something = FOO, | ||
125 | * .whaetever = BLAH | ||
126 | * }; | ||
127 | * i2400m_queue_work(i2400m, 1, my_work_function, GFP_KERNEL, | ||
128 | * &args, sizeof(args)) | ||
129 | * | ||
130 | * And now the work function can unpack the arguments and call the | ||
131 | * real function (or do the job itself): | ||
132 | * | ||
133 | * static | ||
134 | * void my_work_fn((struct work_struct *ws) | ||
135 | * { | ||
136 | * struct i2400m_work *iw = | ||
137 | * container_of(ws, struct i2400m_work, ws); | ||
138 | * struct my_work_args *my_args = (void *) iw->pl; | ||
139 | * | ||
140 | * my_work(iw->i2400m, my_args->something, my_args->whatevert); | ||
141 | * } | ||
142 | */ | ||
143 | int i2400m_queue_work(struct i2400m *i2400m, | ||
144 | void (*fn)(struct work_struct *), gfp_t gfp_flags, | ||
145 | const void *pl, size_t pl_size) | ||
146 | { | 117 | { |
147 | int result; | ||
148 | struct i2400m_work *iw; | 118 | struct i2400m_work *iw; |
149 | 119 | ||
150 | BUG_ON(i2400m->work_queue == NULL); | ||
151 | result = -ENOMEM; | ||
152 | iw = kzalloc(sizeof(*iw) + pl_size, gfp_flags); | 120 | iw = kzalloc(sizeof(*iw) + pl_size, gfp_flags); |
153 | if (iw == NULL) | 121 | if (iw == NULL) |
154 | goto error_kzalloc; | 122 | return NULL; |
155 | iw->i2400m = i2400m_get(i2400m); | 123 | iw->i2400m = i2400m_get(i2400m); |
124 | iw->pl_size = pl_size; | ||
156 | memcpy(iw->pl, pl, pl_size); | 125 | memcpy(iw->pl, pl, pl_size); |
157 | INIT_WORK(&iw->ws, fn); | 126 | INIT_WORK(&iw->ws, fn); |
158 | result = queue_work(i2400m->work_queue, &iw->ws); | 127 | return iw; |
159 | error_kzalloc: | ||
160 | return result; | ||
161 | } | 128 | } |
162 | EXPORT_SYMBOL_GPL(i2400m_queue_work); | ||
163 | 129 | ||
164 | 130 | ||
165 | /* | 131 | /* |
@@ -175,21 +141,19 @@ EXPORT_SYMBOL_GPL(i2400m_queue_work); | |||
175 | * it should not happen. | 141 | * it should not happen. |
176 | */ | 142 | */ |
177 | int i2400m_schedule_work(struct i2400m *i2400m, | 143 | int i2400m_schedule_work(struct i2400m *i2400m, |
178 | void (*fn)(struct work_struct *), gfp_t gfp_flags) | 144 | void (*fn)(struct work_struct *), gfp_t gfp_flags, |
145 | const void *pl, size_t pl_size) | ||
179 | { | 146 | { |
180 | int result; | 147 | int result; |
181 | struct i2400m_work *iw; | 148 | struct i2400m_work *iw; |
182 | 149 | ||
183 | result = -ENOMEM; | 150 | result = -ENOMEM; |
184 | iw = kzalloc(sizeof(*iw), gfp_flags); | 151 | iw = __i2400m_work_setup(i2400m, fn, gfp_flags, pl, pl_size); |
185 | if (iw == NULL) | 152 | if (iw != NULL) { |
186 | goto error_kzalloc; | 153 | result = schedule_work(&iw->ws); |
187 | iw->i2400m = i2400m_get(i2400m); | 154 | if (WARN_ON(result == 0)) |
188 | INIT_WORK(&iw->ws, fn); | 155 | result = -ENXIO; |
189 | result = schedule_work(&iw->ws); | 156 | } |
190 | if (result == 0) | ||
191 | result = -ENXIO; | ||
192 | error_kzalloc: | ||
193 | return result; | 157 | return result; |
194 | } | 158 | } |
195 | 159 | ||
@@ -291,7 +255,7 @@ int i2400m_op_reset(struct wimax_dev *wimax_dev) | |||
291 | mutex_lock(&i2400m->init_mutex); | 255 | mutex_lock(&i2400m->init_mutex); |
292 | i2400m->reset_ctx = &ctx; | 256 | i2400m->reset_ctx = &ctx; |
293 | mutex_unlock(&i2400m->init_mutex); | 257 | mutex_unlock(&i2400m->init_mutex); |
294 | result = i2400m->bus_reset(i2400m, I2400M_RT_WARM); | 258 | result = i2400m_reset(i2400m, I2400M_RT_WARM); |
295 | if (result < 0) | 259 | if (result < 0) |
296 | goto out; | 260 | goto out; |
297 | result = wait_for_completion_timeout(&ctx.completion, 4*HZ); | 261 | result = wait_for_completion_timeout(&ctx.completion, 4*HZ); |
@@ -420,9 +384,15 @@ retry: | |||
420 | dev_err(dev, "cannot create workqueue\n"); | 384 | dev_err(dev, "cannot create workqueue\n"); |
421 | goto error_create_workqueue; | 385 | goto error_create_workqueue; |
422 | } | 386 | } |
423 | result = i2400m->bus_dev_start(i2400m); | 387 | if (i2400m->bus_dev_start) { |
424 | if (result < 0) | 388 | result = i2400m->bus_dev_start(i2400m); |
425 | goto error_bus_dev_start; | 389 | if (result < 0) |
390 | goto error_bus_dev_start; | ||
391 | } | ||
392 | i2400m->ready = 1; | ||
393 | wmb(); /* see i2400m->ready's documentation */ | ||
394 | /* process pending reports from the device */ | ||
395 | queue_work(i2400m->work_queue, &i2400m->rx_report_ws); | ||
426 | result = i2400m_firmware_check(i2400m); /* fw versions ok? */ | 396 | result = i2400m_firmware_check(i2400m); /* fw versions ok? */ |
427 | if (result < 0) | 397 | if (result < 0) |
428 | goto error_fw_check; | 398 | goto error_fw_check; |
@@ -430,8 +400,6 @@ retry: | |||
430 | result = i2400m_check_mac_addr(i2400m); | 400 | result = i2400m_check_mac_addr(i2400m); |
431 | if (result < 0) | 401 | if (result < 0) |
432 | goto error_check_mac_addr; | 402 | goto error_check_mac_addr; |
433 | i2400m->ready = 1; | ||
434 | wimax_state_change(wimax_dev, WIMAX_ST_UNINITIALIZED); | ||
435 | result = i2400m_dev_initialize(i2400m); | 403 | result = i2400m_dev_initialize(i2400m); |
436 | if (result < 0) | 404 | if (result < 0) |
437 | goto error_dev_initialize; | 405 | goto error_dev_initialize; |
@@ -443,8 +411,12 @@ retry: | |||
443 | 411 | ||
444 | error_dev_initialize: | 412 | error_dev_initialize: |
445 | error_check_mac_addr: | 413 | error_check_mac_addr: |
414 | i2400m->ready = 0; | ||
415 | wmb(); /* see i2400m->ready's documentation */ | ||
416 | flush_workqueue(i2400m->work_queue); | ||
446 | error_fw_check: | 417 | error_fw_check: |
447 | i2400m->bus_dev_stop(i2400m); | 418 | if (i2400m->bus_dev_stop) |
419 | i2400m->bus_dev_stop(i2400m); | ||
448 | error_bus_dev_start: | 420 | error_bus_dev_start: |
449 | destroy_workqueue(i2400m->work_queue); | 421 | destroy_workqueue(i2400m->work_queue); |
450 | error_create_workqueue: | 422 | error_create_workqueue: |
@@ -466,11 +438,15 @@ error_bootstrap: | |||
466 | static | 438 | static |
467 | int i2400m_dev_start(struct i2400m *i2400m, enum i2400m_bri bm_flags) | 439 | int i2400m_dev_start(struct i2400m *i2400m, enum i2400m_bri bm_flags) |
468 | { | 440 | { |
469 | int result; | 441 | int result = 0; |
470 | mutex_lock(&i2400m->init_mutex); /* Well, start the device */ | 442 | mutex_lock(&i2400m->init_mutex); /* Well, start the device */ |
471 | result = __i2400m_dev_start(i2400m, bm_flags); | 443 | if (i2400m->updown == 0) { |
472 | if (result >= 0) | 444 | result = __i2400m_dev_start(i2400m, bm_flags); |
473 | i2400m->updown = 1; | 445 | if (result >= 0) { |
446 | i2400m->updown = 1; | ||
447 | wmb(); /* see i2400m->updown's documentation */ | ||
448 | } | ||
449 | } | ||
474 | mutex_unlock(&i2400m->init_mutex); | 450 | mutex_unlock(&i2400m->init_mutex); |
475 | return result; | 451 | return result; |
476 | } | 452 | } |
@@ -495,9 +471,20 @@ void __i2400m_dev_stop(struct i2400m *i2400m) | |||
495 | 471 | ||
496 | d_fnstart(3, dev, "(i2400m %p)\n", i2400m); | 472 | d_fnstart(3, dev, "(i2400m %p)\n", i2400m); |
497 | wimax_state_change(wimax_dev, __WIMAX_ST_QUIESCING); | 473 | wimax_state_change(wimax_dev, __WIMAX_ST_QUIESCING); |
474 | i2400m_msg_to_dev_cancel_wait(i2400m, -EL3RST); | ||
475 | complete(&i2400m->msg_completion); | ||
476 | i2400m_net_wake_stop(i2400m); | ||
498 | i2400m_dev_shutdown(i2400m); | 477 | i2400m_dev_shutdown(i2400m); |
499 | i2400m->ready = 0; | 478 | /* |
500 | i2400m->bus_dev_stop(i2400m); | 479 | * Make sure no report hooks are running *before* we stop the |
480 | * communication infrastructure with the device. | ||
481 | */ | ||
482 | i2400m->ready = 0; /* nobody can queue work anymore */ | ||
483 | wmb(); /* see i2400m->ready's documentation */ | ||
484 | flush_workqueue(i2400m->work_queue); | ||
485 | |||
486 | if (i2400m->bus_dev_stop) | ||
487 | i2400m->bus_dev_stop(i2400m); | ||
501 | destroy_workqueue(i2400m->work_queue); | 488 | destroy_workqueue(i2400m->work_queue); |
502 | i2400m_rx_release(i2400m); | 489 | i2400m_rx_release(i2400m); |
503 | i2400m_tx_release(i2400m); | 490 | i2400m_tx_release(i2400m); |
@@ -518,12 +505,139 @@ void i2400m_dev_stop(struct i2400m *i2400m) | |||
518 | if (i2400m->updown) { | 505 | if (i2400m->updown) { |
519 | __i2400m_dev_stop(i2400m); | 506 | __i2400m_dev_stop(i2400m); |
520 | i2400m->updown = 0; | 507 | i2400m->updown = 0; |
508 | wmb(); /* see i2400m->updown's documentation */ | ||
521 | } | 509 | } |
522 | mutex_unlock(&i2400m->init_mutex); | 510 | mutex_unlock(&i2400m->init_mutex); |
523 | } | 511 | } |
524 | 512 | ||
525 | 513 | ||
526 | /* | 514 | /* |
515 | * Listen to PM events to cache the firmware before suspend/hibernation | ||
516 | * | ||
517 | * When the device comes out of suspend, it might go into reset and | ||
518 | * firmware has to be uploaded again. At resume, most of the times, we | ||
519 | * can't load firmware images from disk, so we need to cache it. | ||
520 | * | ||
521 | * i2400m_fw_cache() will allocate a kobject and attach the firmware | ||
522 | * to it; that way we don't have to worry too much about the fw loader | ||
523 | * hitting a race condition. | ||
524 | * | ||
525 | * Note: modus operandi stolen from the Orinoco driver; thx. | ||
526 | */ | ||
527 | static | ||
528 | int i2400m_pm_notifier(struct notifier_block *notifier, | ||
529 | unsigned long pm_event, | ||
530 | void *unused) | ||
531 | { | ||
532 | struct i2400m *i2400m = | ||
533 | container_of(notifier, struct i2400m, pm_notifier); | ||
534 | struct device *dev = i2400m_dev(i2400m); | ||
535 | |||
536 | d_fnstart(3, dev, "(i2400m %p pm_event %lx)\n", i2400m, pm_event); | ||
537 | switch (pm_event) { | ||
538 | case PM_HIBERNATION_PREPARE: | ||
539 | case PM_SUSPEND_PREPARE: | ||
540 | i2400m_fw_cache(i2400m); | ||
541 | break; | ||
542 | case PM_POST_RESTORE: | ||
543 | /* Restore from hibernation failed. We need to clean | ||
544 | * up in exactly the same way, so fall through. */ | ||
545 | case PM_POST_HIBERNATION: | ||
546 | case PM_POST_SUSPEND: | ||
547 | i2400m_fw_uncache(i2400m); | ||
548 | break; | ||
549 | |||
550 | case PM_RESTORE_PREPARE: | ||
551 | default: | ||
552 | break; | ||
553 | } | ||
554 | d_fnend(3, dev, "(i2400m %p pm_event %lx) = void\n", i2400m, pm_event); | ||
555 | return NOTIFY_DONE; | ||
556 | } | ||
557 | |||
558 | |||
559 | /* | ||
560 | * pre-reset is called before a device is going on reset | ||
561 | * | ||
562 | * This has to be followed by a call to i2400m_post_reset(), otherwise | ||
563 | * bad things might happen. | ||
564 | */ | ||
565 | int i2400m_pre_reset(struct i2400m *i2400m) | ||
566 | { | ||
567 | int result; | ||
568 | struct device *dev = i2400m_dev(i2400m); | ||
569 | |||
570 | d_fnstart(3, dev, "(i2400m %p)\n", i2400m); | ||
571 | d_printf(1, dev, "pre-reset shut down\n"); | ||
572 | |||
573 | result = 0; | ||
574 | mutex_lock(&i2400m->init_mutex); | ||
575 | if (i2400m->updown) { | ||
576 | netif_tx_disable(i2400m->wimax_dev.net_dev); | ||
577 | __i2400m_dev_stop(i2400m); | ||
578 | result = 0; | ||
579 | /* down't set updown to zero -- this way | ||
580 | * post_reset can restore properly */ | ||
581 | } | ||
582 | mutex_unlock(&i2400m->init_mutex); | ||
583 | if (i2400m->bus_release) | ||
584 | i2400m->bus_release(i2400m); | ||
585 | d_fnend(3, dev, "(i2400m %p) = %d\n", i2400m, result); | ||
586 | return result; | ||
587 | } | ||
588 | EXPORT_SYMBOL_GPL(i2400m_pre_reset); | ||
589 | |||
590 | |||
591 | /* | ||
592 | * Restore device state after a reset | ||
593 | * | ||
594 | * Do the work needed after a device reset to bring it up to the same | ||
595 | * state as it was before the reset. | ||
596 | * | ||
597 | * NOTE: this requires i2400m->init_mutex taken | ||
598 | */ | ||
599 | int i2400m_post_reset(struct i2400m *i2400m) | ||
600 | { | ||
601 | int result = 0; | ||
602 | struct device *dev = i2400m_dev(i2400m); | ||
603 | |||
604 | d_fnstart(3, dev, "(i2400m %p)\n", i2400m); | ||
605 | d_printf(1, dev, "post-reset start\n"); | ||
606 | if (i2400m->bus_setup) { | ||
607 | result = i2400m->bus_setup(i2400m); | ||
608 | if (result < 0) { | ||
609 | dev_err(dev, "bus-specific setup failed: %d\n", | ||
610 | result); | ||
611 | goto error_bus_setup; | ||
612 | } | ||
613 | } | ||
614 | mutex_lock(&i2400m->init_mutex); | ||
615 | if (i2400m->updown) { | ||
616 | result = __i2400m_dev_start( | ||
617 | i2400m, I2400M_BRI_SOFT | I2400M_BRI_MAC_REINIT); | ||
618 | if (result < 0) | ||
619 | goto error_dev_start; | ||
620 | } | ||
621 | mutex_unlock(&i2400m->init_mutex); | ||
622 | d_fnend(3, dev, "(i2400m %p) = %d\n", i2400m, result); | ||
623 | return result; | ||
624 | |||
625 | error_dev_start: | ||
626 | if (i2400m->bus_release) | ||
627 | i2400m->bus_release(i2400m); | ||
628 | error_bus_setup: | ||
629 | /* even if the device was up, it could not be recovered, so we | ||
630 | * mark it as down. */ | ||
631 | i2400m->updown = 0; | ||
632 | wmb(); /* see i2400m->updown's documentation */ | ||
633 | mutex_unlock(&i2400m->init_mutex); | ||
634 | d_fnend(3, dev, "(i2400m %p) = %d\n", i2400m, result); | ||
635 | return result; | ||
636 | } | ||
637 | EXPORT_SYMBOL_GPL(i2400m_post_reset); | ||
638 | |||
639 | |||
640 | /* | ||
527 | * The device has rebooted; fix up the device and the driver | 641 | * The device has rebooted; fix up the device and the driver |
528 | * | 642 | * |
529 | * Tear down the driver communication with the device, reload the | 643 | * Tear down the driver communication with the device, reload the |
@@ -542,56 +656,69 @@ void i2400m_dev_stop(struct i2400m *i2400m) | |||
542 | * _stop()], don't do anything, let it fail and handle it. | 656 | * _stop()], don't do anything, let it fail and handle it. |
543 | * | 657 | * |
544 | * This function is ran always in a thread context | 658 | * This function is ran always in a thread context |
659 | * | ||
660 | * This function gets passed, as payload to i2400m_work() a 'const | ||
661 | * char *' ptr with a "reason" why the reset happened (for messages). | ||
545 | */ | 662 | */ |
546 | static | 663 | static |
547 | void __i2400m_dev_reset_handle(struct work_struct *ws) | 664 | void __i2400m_dev_reset_handle(struct work_struct *ws) |
548 | { | 665 | { |
549 | int result; | 666 | int result; |
550 | struct i2400m_work *iw = container_of(ws, struct i2400m_work, ws); | 667 | struct i2400m_work *iw = container_of(ws, struct i2400m_work, ws); |
668 | const char *reason; | ||
551 | struct i2400m *i2400m = iw->i2400m; | 669 | struct i2400m *i2400m = iw->i2400m; |
552 | struct device *dev = i2400m_dev(i2400m); | 670 | struct device *dev = i2400m_dev(i2400m); |
553 | enum wimax_st wimax_state; | ||
554 | struct i2400m_reset_ctx *ctx = i2400m->reset_ctx; | 671 | struct i2400m_reset_ctx *ctx = i2400m->reset_ctx; |
555 | 672 | ||
556 | d_fnstart(3, dev, "(ws %p i2400m %p)\n", ws, i2400m); | 673 | if (WARN_ON(iw->pl_size != sizeof(reason))) |
674 | reason = "SW BUG: reason n/a"; | ||
675 | else | ||
676 | memcpy(&reason, iw->pl, sizeof(reason)); | ||
677 | |||
678 | d_fnstart(3, dev, "(ws %p i2400m %p reason %s)\n", ws, i2400m, reason); | ||
679 | |||
557 | result = 0; | 680 | result = 0; |
558 | if (mutex_trylock(&i2400m->init_mutex) == 0) { | 681 | if (mutex_trylock(&i2400m->init_mutex) == 0) { |
559 | /* We are still in i2400m_dev_start() [let it fail] or | 682 | /* We are still in i2400m_dev_start() [let it fail] or |
560 | * i2400m_dev_stop() [we are shutting down anyway, so | 683 | * i2400m_dev_stop() [we are shutting down anyway, so |
561 | * ignore it] or we are resetting somewhere else. */ | 684 | * ignore it] or we are resetting somewhere else. */ |
562 | dev_err(dev, "device rebooted\n"); | 685 | dev_err(dev, "device rebooted somewhere else?\n"); |
563 | i2400m_msg_to_dev_cancel_wait(i2400m, -EL3RST); | 686 | i2400m_msg_to_dev_cancel_wait(i2400m, -EL3RST); |
564 | complete(&i2400m->msg_completion); | 687 | complete(&i2400m->msg_completion); |
565 | goto out; | 688 | goto out; |
566 | } | 689 | } |
567 | wimax_state = wimax_state_get(&i2400m->wimax_dev); | 690 | if (i2400m->updown == 0) { |
568 | if (wimax_state < WIMAX_ST_UNINITIALIZED) { | 691 | dev_info(dev, "%s: device is down, doing nothing\n", reason); |
569 | dev_info(dev, "device rebooted: it is down, ignoring\n"); | 692 | goto out_unlock; |
570 | goto out_unlock; /* ifconfig up/down wasn't called */ | ||
571 | } | 693 | } |
572 | dev_err(dev, "device rebooted: reinitializing driver\n"); | 694 | dev_err(dev, "%s: reinitializing driver\n", reason); |
573 | __i2400m_dev_stop(i2400m); | 695 | __i2400m_dev_stop(i2400m); |
574 | i2400m->updown = 0; | ||
575 | result = __i2400m_dev_start(i2400m, | 696 | result = __i2400m_dev_start(i2400m, |
576 | I2400M_BRI_SOFT | I2400M_BRI_MAC_REINIT); | 697 | I2400M_BRI_SOFT | I2400M_BRI_MAC_REINIT); |
577 | if (result < 0) { | 698 | if (result < 0) { |
578 | dev_err(dev, "device reboot: cannot start the device: %d\n", | 699 | i2400m->updown = 0; |
579 | result); | 700 | wmb(); /* see i2400m->updown's documentation */ |
580 | result = i2400m->bus_reset(i2400m, I2400M_RT_BUS); | 701 | dev_err(dev, "%s: cannot start the device: %d\n", |
581 | if (result >= 0) | 702 | reason, result); |
582 | result = -ENODEV; | 703 | result = -EUCLEAN; |
583 | } else | 704 | } |
584 | i2400m->updown = 1; | ||
585 | out_unlock: | 705 | out_unlock: |
586 | if (i2400m->reset_ctx) { | 706 | if (i2400m->reset_ctx) { |
587 | ctx->result = result; | 707 | ctx->result = result; |
588 | complete(&ctx->completion); | 708 | complete(&ctx->completion); |
589 | } | 709 | } |
590 | mutex_unlock(&i2400m->init_mutex); | 710 | mutex_unlock(&i2400m->init_mutex); |
711 | if (result == -EUCLEAN) { | ||
712 | /* ops, need to clean up [w/ init_mutex not held] */ | ||
713 | result = i2400m_reset(i2400m, I2400M_RT_BUS); | ||
714 | if (result >= 0) | ||
715 | result = -ENODEV; | ||
716 | } | ||
591 | out: | 717 | out: |
592 | i2400m_put(i2400m); | 718 | i2400m_put(i2400m); |
593 | kfree(iw); | 719 | kfree(iw); |
594 | d_fnend(3, dev, "(ws %p i2400m %p) = void\n", ws, i2400m); | 720 | d_fnend(3, dev, "(ws %p i2400m %p reason %s) = void\n", |
721 | ws, i2400m, reason); | ||
595 | return; | 722 | return; |
596 | } | 723 | } |
597 | 724 | ||
@@ -608,16 +735,104 @@ out: | |||
608 | * reinitializing the driver to handle the reset, calling into the | 735 | * reinitializing the driver to handle the reset, calling into the |
609 | * bus-specific functions ops as needed. | 736 | * bus-specific functions ops as needed. |
610 | */ | 737 | */ |
611 | int i2400m_dev_reset_handle(struct i2400m *i2400m) | 738 | int i2400m_dev_reset_handle(struct i2400m *i2400m, const char *reason) |
612 | { | 739 | { |
613 | i2400m->boot_mode = 1; | 740 | i2400m->boot_mode = 1; |
614 | wmb(); /* Make sure i2400m_msg_to_dev() sees boot_mode */ | 741 | wmb(); /* Make sure i2400m_msg_to_dev() sees boot_mode */ |
615 | return i2400m_schedule_work(i2400m, __i2400m_dev_reset_handle, | 742 | return i2400m_schedule_work(i2400m, __i2400m_dev_reset_handle, |
616 | GFP_ATOMIC); | 743 | GFP_ATOMIC, &reason, sizeof(reason)); |
617 | } | 744 | } |
618 | EXPORT_SYMBOL_GPL(i2400m_dev_reset_handle); | 745 | EXPORT_SYMBOL_GPL(i2400m_dev_reset_handle); |
619 | 746 | ||
620 | 747 | ||
748 | /* | ||
749 | * Alloc the command and ack buffers for boot mode | ||
750 | * | ||
751 | * Get the buffers needed to deal with boot mode messages. These | ||
752 | * buffers need to be allocated before the sdio recieve irq is setup. | ||
753 | */ | ||
754 | static | ||
755 | int i2400m_bm_buf_alloc(struct i2400m *i2400m) | ||
756 | { | ||
757 | int result; | ||
758 | |||
759 | result = -ENOMEM; | ||
760 | i2400m->bm_cmd_buf = kzalloc(I2400M_BM_CMD_BUF_SIZE, GFP_KERNEL); | ||
761 | if (i2400m->bm_cmd_buf == NULL) | ||
762 | goto error_bm_cmd_kzalloc; | ||
763 | i2400m->bm_ack_buf = kzalloc(I2400M_BM_ACK_BUF_SIZE, GFP_KERNEL); | ||
764 | if (i2400m->bm_ack_buf == NULL) | ||
765 | goto error_bm_ack_buf_kzalloc; | ||
766 | return 0; | ||
767 | |||
768 | error_bm_ack_buf_kzalloc: | ||
769 | kfree(i2400m->bm_cmd_buf); | ||
770 | error_bm_cmd_kzalloc: | ||
771 | return result; | ||
772 | } | ||
773 | |||
774 | |||
775 | /* | ||
776 | * Free boot mode command and ack buffers. | ||
777 | */ | ||
778 | static | ||
779 | void i2400m_bm_buf_free(struct i2400m *i2400m) | ||
780 | { | ||
781 | kfree(i2400m->bm_ack_buf); | ||
782 | kfree(i2400m->bm_cmd_buf); | ||
783 | } | ||
784 | |||
785 | |||
786 | /** | ||
787 | * i2400m_init - Initialize a 'struct i2400m' from all zeroes | ||
788 | * | ||
789 | * This is a bus-generic API call. | ||
790 | */ | ||
791 | void i2400m_init(struct i2400m *i2400m) | ||
792 | { | ||
793 | wimax_dev_init(&i2400m->wimax_dev); | ||
794 | |||
795 | i2400m->boot_mode = 1; | ||
796 | i2400m->rx_reorder = 1; | ||
797 | init_waitqueue_head(&i2400m->state_wq); | ||
798 | |||
799 | spin_lock_init(&i2400m->tx_lock); | ||
800 | i2400m->tx_pl_min = UINT_MAX; | ||
801 | i2400m->tx_size_min = UINT_MAX; | ||
802 | |||
803 | spin_lock_init(&i2400m->rx_lock); | ||
804 | i2400m->rx_pl_min = UINT_MAX; | ||
805 | i2400m->rx_size_min = UINT_MAX; | ||
806 | INIT_LIST_HEAD(&i2400m->rx_reports); | ||
807 | INIT_WORK(&i2400m->rx_report_ws, i2400m_report_hook_work); | ||
808 | |||
809 | mutex_init(&i2400m->msg_mutex); | ||
810 | init_completion(&i2400m->msg_completion); | ||
811 | |||
812 | mutex_init(&i2400m->init_mutex); | ||
813 | /* wake_tx_ws is initialized in i2400m_tx_setup() */ | ||
814 | } | ||
815 | EXPORT_SYMBOL_GPL(i2400m_init); | ||
816 | |||
817 | |||
818 | int i2400m_reset(struct i2400m *i2400m, enum i2400m_reset_type rt) | ||
819 | { | ||
820 | struct net_device *net_dev = i2400m->wimax_dev.net_dev; | ||
821 | |||
822 | /* | ||
823 | * Make sure we stop TXs and down the carrier before | ||
824 | * resetting; this is needed to avoid things like | ||
825 | * i2400m_wake_tx() scheduling stuff in parallel. | ||
826 | */ | ||
827 | if (net_dev->reg_state == NETREG_REGISTERED) { | ||
828 | netif_tx_disable(net_dev); | ||
829 | netif_carrier_off(net_dev); | ||
830 | } | ||
831 | return i2400m->bus_reset(i2400m, rt); | ||
832 | } | ||
833 | EXPORT_SYMBOL_GPL(i2400m_reset); | ||
834 | |||
835 | |||
621 | /** | 836 | /** |
622 | * i2400m_setup - bus-generic setup function for the i2400m device | 837 | * i2400m_setup - bus-generic setup function for the i2400m device |
623 | * | 838 | * |
@@ -625,13 +840,9 @@ EXPORT_SYMBOL_GPL(i2400m_dev_reset_handle); | |||
625 | * | 840 | * |
626 | * Returns: 0 if ok, < 0 errno code on error. | 841 | * Returns: 0 if ok, < 0 errno code on error. |
627 | * | 842 | * |
628 | * Initializes the bus-generic parts of the i2400m driver; the | 843 | * Sets up basic device comunication infrastructure, boots the ROM to |
629 | * bus-specific parts have been initialized, function pointers filled | 844 | * read the MAC address, registers with the WiMAX and network stacks |
630 | * out by the bus-specific probe function. | 845 | * and then brings up the device. |
631 | * | ||
632 | * As well, this registers the WiMAX and net device nodes. Once this | ||
633 | * function returns, the device is operative and has to be ready to | ||
634 | * receive and send network traffic and WiMAX control operations. | ||
635 | */ | 846 | */ |
636 | int i2400m_setup(struct i2400m *i2400m, enum i2400m_bri bm_flags) | 847 | int i2400m_setup(struct i2400m *i2400m, enum i2400m_bri bm_flags) |
637 | { | 848 | { |
@@ -645,16 +856,21 @@ int i2400m_setup(struct i2400m *i2400m, enum i2400m_bri bm_flags) | |||
645 | snprintf(wimax_dev->name, sizeof(wimax_dev->name), | 856 | snprintf(wimax_dev->name, sizeof(wimax_dev->name), |
646 | "i2400m-%s:%s", dev->bus->name, dev_name(dev)); | 857 | "i2400m-%s:%s", dev->bus->name, dev_name(dev)); |
647 | 858 | ||
648 | i2400m->bm_cmd_buf = kzalloc(I2400M_BM_CMD_BUF_SIZE, GFP_KERNEL); | 859 | result = i2400m_bm_buf_alloc(i2400m); |
649 | if (i2400m->bm_cmd_buf == NULL) { | 860 | if (result < 0) { |
650 | dev_err(dev, "cannot allocate USB command buffer\n"); | 861 | dev_err(dev, "cannot allocate bootmode scratch buffers\n"); |
651 | goto error_bm_cmd_kzalloc; | 862 | goto error_bm_buf_alloc; |
652 | } | 863 | } |
653 | i2400m->bm_ack_buf = kzalloc(I2400M_BM_ACK_BUF_SIZE, GFP_KERNEL); | 864 | |
654 | if (i2400m->bm_ack_buf == NULL) { | 865 | if (i2400m->bus_setup) { |
655 | dev_err(dev, "cannot allocate USB ack buffer\n"); | 866 | result = i2400m->bus_setup(i2400m); |
656 | goto error_bm_ack_buf_kzalloc; | 867 | if (result < 0) { |
868 | dev_err(dev, "bus-specific setup failed: %d\n", | ||
869 | result); | ||
870 | goto error_bus_setup; | ||
871 | } | ||
657 | } | 872 | } |
873 | |||
658 | result = i2400m_bootrom_init(i2400m, bm_flags); | 874 | result = i2400m_bootrom_init(i2400m, bm_flags); |
659 | if (result < 0) { | 875 | if (result < 0) { |
660 | dev_err(dev, "read mac addr: bootrom init " | 876 | dev_err(dev, "read mac addr: bootrom init " |
@@ -666,6 +882,9 @@ int i2400m_setup(struct i2400m *i2400m, enum i2400m_bri bm_flags) | |||
666 | goto error_read_mac_addr; | 882 | goto error_read_mac_addr; |
667 | random_ether_addr(i2400m->src_mac_addr); | 883 | random_ether_addr(i2400m->src_mac_addr); |
668 | 884 | ||
885 | i2400m->pm_notifier.notifier_call = i2400m_pm_notifier; | ||
886 | register_pm_notifier(&i2400m->pm_notifier); | ||
887 | |||
669 | result = register_netdev(net_dev); /* Okey dokey, bring it up */ | 888 | result = register_netdev(net_dev); /* Okey dokey, bring it up */ |
670 | if (result < 0) { | 889 | if (result < 0) { |
671 | dev_err(dev, "cannot register i2400m network device: %d\n", | 890 | dev_err(dev, "cannot register i2400m network device: %d\n", |
@@ -674,18 +893,13 @@ int i2400m_setup(struct i2400m *i2400m, enum i2400m_bri bm_flags) | |||
674 | } | 893 | } |
675 | netif_carrier_off(net_dev); | 894 | netif_carrier_off(net_dev); |
676 | 895 | ||
677 | result = i2400m_dev_start(i2400m, bm_flags); | ||
678 | if (result < 0) | ||
679 | goto error_dev_start; | ||
680 | |||
681 | i2400m->wimax_dev.op_msg_from_user = i2400m_op_msg_from_user; | 896 | i2400m->wimax_dev.op_msg_from_user = i2400m_op_msg_from_user; |
682 | i2400m->wimax_dev.op_rfkill_sw_toggle = i2400m_op_rfkill_sw_toggle; | 897 | i2400m->wimax_dev.op_rfkill_sw_toggle = i2400m_op_rfkill_sw_toggle; |
683 | i2400m->wimax_dev.op_reset = i2400m_op_reset; | 898 | i2400m->wimax_dev.op_reset = i2400m_op_reset; |
899 | |||
684 | result = wimax_dev_add(&i2400m->wimax_dev, net_dev); | 900 | result = wimax_dev_add(&i2400m->wimax_dev, net_dev); |
685 | if (result < 0) | 901 | if (result < 0) |
686 | goto error_wimax_dev_add; | 902 | goto error_wimax_dev_add; |
687 | /* User space needs to do some init stuff */ | ||
688 | wimax_state_change(wimax_dev, WIMAX_ST_UNINITIALIZED); | ||
689 | 903 | ||
690 | /* Now setup all that requires a registered net and wimax device. */ | 904 | /* Now setup all that requires a registered net and wimax device. */ |
691 | result = sysfs_create_group(&net_dev->dev.kobj, &i2400m_dev_attr_group); | 905 | result = sysfs_create_group(&net_dev->dev.kobj, &i2400m_dev_attr_group); |
@@ -693,30 +907,37 @@ int i2400m_setup(struct i2400m *i2400m, enum i2400m_bri bm_flags) | |||
693 | dev_err(dev, "cannot setup i2400m's sysfs: %d\n", result); | 907 | dev_err(dev, "cannot setup i2400m's sysfs: %d\n", result); |
694 | goto error_sysfs_setup; | 908 | goto error_sysfs_setup; |
695 | } | 909 | } |
910 | |||
696 | result = i2400m_debugfs_add(i2400m); | 911 | result = i2400m_debugfs_add(i2400m); |
697 | if (result < 0) { | 912 | if (result < 0) { |
698 | dev_err(dev, "cannot setup i2400m's debugfs: %d\n", result); | 913 | dev_err(dev, "cannot setup i2400m's debugfs: %d\n", result); |
699 | goto error_debugfs_setup; | 914 | goto error_debugfs_setup; |
700 | } | 915 | } |
916 | |||
917 | result = i2400m_dev_start(i2400m, bm_flags); | ||
918 | if (result < 0) | ||
919 | goto error_dev_start; | ||
701 | d_fnend(3, dev, "(i2400m %p) = %d\n", i2400m, result); | 920 | d_fnend(3, dev, "(i2400m %p) = %d\n", i2400m, result); |
702 | return result; | 921 | return result; |
703 | 922 | ||
923 | error_dev_start: | ||
924 | i2400m_debugfs_rm(i2400m); | ||
704 | error_debugfs_setup: | 925 | error_debugfs_setup: |
705 | sysfs_remove_group(&i2400m->wimax_dev.net_dev->dev.kobj, | 926 | sysfs_remove_group(&i2400m->wimax_dev.net_dev->dev.kobj, |
706 | &i2400m_dev_attr_group); | 927 | &i2400m_dev_attr_group); |
707 | error_sysfs_setup: | 928 | error_sysfs_setup: |
708 | wimax_dev_rm(&i2400m->wimax_dev); | 929 | wimax_dev_rm(&i2400m->wimax_dev); |
709 | error_wimax_dev_add: | 930 | error_wimax_dev_add: |
710 | i2400m_dev_stop(i2400m); | ||
711 | error_dev_start: | ||
712 | unregister_netdev(net_dev); | 931 | unregister_netdev(net_dev); |
713 | error_register_netdev: | 932 | error_register_netdev: |
933 | unregister_pm_notifier(&i2400m->pm_notifier); | ||
714 | error_read_mac_addr: | 934 | error_read_mac_addr: |
715 | error_bootrom_init: | 935 | error_bootrom_init: |
716 | kfree(i2400m->bm_ack_buf); | 936 | if (i2400m->bus_release) |
717 | error_bm_ack_buf_kzalloc: | 937 | i2400m->bus_release(i2400m); |
718 | kfree(i2400m->bm_cmd_buf); | 938 | error_bus_setup: |
719 | error_bm_cmd_kzalloc: | 939 | i2400m_bm_buf_free(i2400m); |
940 | error_bm_buf_alloc: | ||
720 | d_fnend(3, dev, "(i2400m %p) = %d\n", i2400m, result); | 941 | d_fnend(3, dev, "(i2400m %p) = %d\n", i2400m, result); |
721 | return result; | 942 | return result; |
722 | } | 943 | } |
@@ -735,14 +956,17 @@ void i2400m_release(struct i2400m *i2400m) | |||
735 | d_fnstart(3, dev, "(i2400m %p)\n", i2400m); | 956 | d_fnstart(3, dev, "(i2400m %p)\n", i2400m); |
736 | netif_stop_queue(i2400m->wimax_dev.net_dev); | 957 | netif_stop_queue(i2400m->wimax_dev.net_dev); |
737 | 958 | ||
959 | i2400m_dev_stop(i2400m); | ||
960 | |||
738 | i2400m_debugfs_rm(i2400m); | 961 | i2400m_debugfs_rm(i2400m); |
739 | sysfs_remove_group(&i2400m->wimax_dev.net_dev->dev.kobj, | 962 | sysfs_remove_group(&i2400m->wimax_dev.net_dev->dev.kobj, |
740 | &i2400m_dev_attr_group); | 963 | &i2400m_dev_attr_group); |
741 | wimax_dev_rm(&i2400m->wimax_dev); | 964 | wimax_dev_rm(&i2400m->wimax_dev); |
742 | i2400m_dev_stop(i2400m); | ||
743 | unregister_netdev(i2400m->wimax_dev.net_dev); | 965 | unregister_netdev(i2400m->wimax_dev.net_dev); |
744 | kfree(i2400m->bm_ack_buf); | 966 | unregister_pm_notifier(&i2400m->pm_notifier); |
745 | kfree(i2400m->bm_cmd_buf); | 967 | if (i2400m->bus_release) |
968 | i2400m->bus_release(i2400m); | ||
969 | i2400m_bm_buf_free(i2400m); | ||
746 | d_fnend(3, dev, "(i2400m %p) = void\n", i2400m); | 970 | d_fnend(3, dev, "(i2400m %p) = void\n", i2400m); |
747 | } | 971 | } |
748 | EXPORT_SYMBOL_GPL(i2400m_release); | 972 | EXPORT_SYMBOL_GPL(i2400m_release); |
@@ -759,6 +983,7 @@ struct d_level D_LEVEL[] = { | |||
759 | D_SUBMODULE_DEFINE(netdev), | 983 | D_SUBMODULE_DEFINE(netdev), |
760 | D_SUBMODULE_DEFINE(rfkill), | 984 | D_SUBMODULE_DEFINE(rfkill), |
761 | D_SUBMODULE_DEFINE(rx), | 985 | D_SUBMODULE_DEFINE(rx), |
986 | D_SUBMODULE_DEFINE(sysfs), | ||
762 | D_SUBMODULE_DEFINE(tx), | 987 | D_SUBMODULE_DEFINE(tx), |
763 | }; | 988 | }; |
764 | size_t D_LEVEL_SIZE = ARRAY_SIZE(D_LEVEL); | 989 | size_t D_LEVEL_SIZE = ARRAY_SIZE(D_LEVEL); |
@@ -767,7 +992,9 @@ size_t D_LEVEL_SIZE = ARRAY_SIZE(D_LEVEL); | |||
767 | static | 992 | static |
768 | int __init i2400m_driver_init(void) | 993 | int __init i2400m_driver_init(void) |
769 | { | 994 | { |
770 | return 0; | 995 | d_parse_params(D_LEVEL, D_LEVEL_SIZE, i2400m_debug_params, |
996 | "i2400m.debug"); | ||
997 | return i2400m_barker_db_init(i2400m_barkers_params); | ||
771 | } | 998 | } |
772 | module_init(i2400m_driver_init); | 999 | module_init(i2400m_driver_init); |
773 | 1000 | ||
@@ -776,6 +1003,7 @@ void __exit i2400m_driver_exit(void) | |||
776 | { | 1003 | { |
777 | /* for scheds i2400m_dev_reset_handle() */ | 1004 | /* for scheds i2400m_dev_reset_handle() */ |
778 | flush_scheduled_work(); | 1005 | flush_scheduled_work(); |
1006 | i2400m_barker_db_exit(); | ||
779 | return; | 1007 | return; |
780 | } | 1008 | } |
781 | module_exit(i2400m_driver_exit); | 1009 | module_exit(i2400m_driver_exit); |