wimax/i2400m: if a device reboot happens during probe, handle it
[linux-2.6.git] / drivers / net / wimax / i2400m / driver.c
index 69a816e..e8d022d 100644 (file)
@@ -62,6 +62,7 @@
  *   unregister_netdev()
  */
 #include "i2400m.h"
+#include <linux/etherdevice.h>
 #include <linux/wimax/i2400m.h>
 #include <linux/module.h>
 #include <linux/moduleparam.h>
@@ -76,6 +77,19 @@ MODULE_PARM_DESC(idle_mode_disabled,
                 "If true, the device will not enable idle mode negotiation "
                 "with the base station (when connected) to save power.");
 
+int i2400m_rx_reorder_disabled;        /* 0 (rx reorder enabled) by default */
+module_param_named(rx_reorder_disabled, i2400m_rx_reorder_disabled, int, 0644);
+MODULE_PARM_DESC(rx_reorder_disabled,
+                "If true, RX reordering will be disabled.");
+
+int i2400m_power_save_disabled;        /* 0 (power saving enabled) by default */
+module_param_named(power_save_disabled, i2400m_power_save_disabled, int, 0644);
+MODULE_PARM_DESC(power_save_disabled,
+                "If true, the driver will not tell the device to enter "
+                "power saving mode when it reports it is ready for it. "
+                "False by default (so the device is told to do power "
+                "saving).");
+
 /**
  * i2400m_queue_work - schedule work on a i2400m's queue
  *
@@ -166,7 +180,6 @@ int i2400m_schedule_work(struct i2400m *i2400m,
        int result;
        struct i2400m_work *iw;
 
-       BUG_ON(i2400m->work_queue == NULL);
        result = -ENOMEM;
        iw = kzalloc(sizeof(*iw), gfp_flags);
        if (iw == NULL)
@@ -229,9 +242,6 @@ int i2400m_op_msg_from_user(struct wimax_dev *wimax_dev,
        result = PTR_ERR(ack_skb);
        if (IS_ERR(ack_skb))
                goto error_msg_to_dev;
-       if (unlikely(i2400m->trace_msg_from_user))
-               wimax_msg(&i2400m->wimax_dev, "trace",
-                         msg_buf, msg_len, GFP_KERNEL);
        result = wimax_msg_send(&i2400m->wimax_dev, ack_skb);
 error_msg_to_dev:
        d_fnend(4, dev, "(wimax_dev %p [i2400m %p] msg_buf %p msg_len %zu "
@@ -374,6 +384,11 @@ error:
  * Uploads firmware and brings up all the resources needed to be able
  * to communicate with the device.
  *
+ * The workqueue has to be setup early, at least before RX handling
+ * (it's only real user for now) so it can process reports as they
+ * arrive. We also want to destroy it if we retry, to make sure it is
+ * flushed...easier like this.
+ *
  * TX needs to be setup before the bus-specific code (otherwise on
  * shutdown, the bus-tx code could try to access it).
  */
@@ -396,15 +411,18 @@ retry:
        result = i2400m_tx_setup(i2400m);
        if (result < 0)
                goto error_tx_setup;
-       result = i2400m->bus_dev_start(i2400m);
+       result = i2400m_rx_setup(i2400m);
        if (result < 0)
-               goto error_bus_dev_start;
+               goto error_rx_setup;
        i2400m->work_queue = create_singlethread_workqueue(wimax_dev->name);
        if (i2400m->work_queue == NULL) {
                result = -ENOMEM;
                dev_err(dev, "cannot create workqueue\n");
                goto error_create_workqueue;
        }
+       result = i2400m->bus_dev_start(i2400m);
+       if (result < 0)
+               goto error_bus_dev_start;
        result = i2400m_firmware_check(i2400m); /* fw versions ok? */
        if (result < 0)
                goto error_fw_check;
@@ -426,10 +444,12 @@ retry:
 error_dev_initialize:
 error_check_mac_addr:
 error_fw_check:
-       destroy_workqueue(i2400m->work_queue);
-error_create_workqueue:
        i2400m->bus_dev_stop(i2400m);
 error_bus_dev_start:
+       destroy_workqueue(i2400m->work_queue);
+error_create_workqueue:
+       i2400m_rx_release(i2400m);
+error_rx_setup:
        i2400m_tx_release(i2400m);
 error_tx_setup:
 error_bootstrap:
@@ -463,7 +483,9 @@ int i2400m_dev_start(struct i2400m *i2400m, enum i2400m_bri bm_flags)
  *
  * Returns: 0 if ok, < 0 errno code on error.
  *
- * Releases all the resources allocated to communicate with the device.
+ * Releases all the resources allocated to communicate with the
+ * device. Note we cannot destroy the workqueue earlier as until RX is
+ * fully destroyed, it could still try to schedule jobs.
  */
 static
 void __i2400m_dev_stop(struct i2400m *i2400m)
@@ -475,8 +497,9 @@ void __i2400m_dev_stop(struct i2400m *i2400m)
        wimax_state_change(wimax_dev, __WIMAX_ST_QUIESCING);
        i2400m_dev_shutdown(i2400m);
        i2400m->ready = 0;
-       destroy_workqueue(i2400m->work_queue);
        i2400m->bus_dev_stop(i2400m);
+       destroy_workqueue(i2400m->work_queue);
+       i2400m_rx_release(i2400m);
        i2400m_tx_release(i2400m);
        wimax_state_change(wimax_dev, WIMAX_ST_DOWN);
        d_fnend(3, dev, "(i2400m %p) = 0\n", i2400m);
@@ -587,6 +610,8 @@ out:
  */
 int i2400m_dev_reset_handle(struct i2400m *i2400m)
 {
+       i2400m->boot_mode = 1;
+       wmb();          /* Make sure i2400m_msg_to_dev() sees boot_mode */
        return i2400m_schedule_work(i2400m, __i2400m_dev_reset_handle,
                                    GFP_ATOMIC);
 }
@@ -618,7 +643,7 @@ int i2400m_setup(struct i2400m *i2400m, enum i2400m_bri bm_flags)
        d_fnstart(3, dev, "(i2400m %p)\n", i2400m);
 
        snprintf(wimax_dev->name, sizeof(wimax_dev->name),
-                "i2400m-%s:%s", dev->bus->name, dev->bus_id);
+                "i2400m-%s:%s", dev->bus->name, dev_name(dev));
 
        i2400m->bm_cmd_buf = kzalloc(I2400M_BM_CMD_BUF_SIZE, GFP_KERNEL);
        if (i2400m->bm_cmd_buf == NULL) {
@@ -639,6 +664,7 @@ int i2400m_setup(struct i2400m *i2400m, enum i2400m_bri bm_flags)
        result = i2400m_read_mac_addr(i2400m);
        if (result < 0)
                goto error_read_mac_addr;
+       random_ether_addr(i2400m->src_mac_addr);
 
        result = register_netdev(net_dev);      /* Okey dokey, bring it up */
        if (result < 0) {
@@ -662,6 +688,11 @@ int i2400m_setup(struct i2400m *i2400m, enum i2400m_bri bm_flags)
        wimax_state_change(wimax_dev, WIMAX_ST_UNINITIALIZED);
 
        /* Now setup all that requires a registered net and wimax device. */
+       result = sysfs_create_group(&net_dev->dev.kobj, &i2400m_dev_attr_group);
+       if (result < 0) {
+               dev_err(dev, "cannot setup i2400m's sysfs: %d\n", result);
+               goto error_sysfs_setup;
+       }
        result = i2400m_debugfs_add(i2400m);
        if (result < 0) {
                dev_err(dev, "cannot setup i2400m's debugfs: %d\n", result);
@@ -671,6 +702,9 @@ int i2400m_setup(struct i2400m *i2400m, enum i2400m_bri bm_flags)
        return result;
 
 error_debugfs_setup:
+       sysfs_remove_group(&i2400m->wimax_dev.net_dev->dev.kobj,
+                          &i2400m_dev_attr_group);
+error_sysfs_setup:
        wimax_dev_rm(&i2400m->wimax_dev);
 error_wimax_dev_add:
        i2400m_dev_stop(i2400m);
@@ -702,6 +736,8 @@ void i2400m_release(struct i2400m *i2400m)
        netif_stop_queue(i2400m->wimax_dev.net_dev);
 
        i2400m_debugfs_rm(i2400m);
+       sysfs_remove_group(&i2400m->wimax_dev.net_dev->dev.kobj,
+                          &i2400m_dev_attr_group);
        wimax_dev_rm(&i2400m->wimax_dev);
        i2400m_dev_stop(i2400m);
        unregister_netdev(i2400m->wimax_dev.net_dev);