]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/net/wireless/libertas/main.c
libertas: convert DATA_RATE to a direct command
[linux-2.6-omap-h63xx.git] / drivers / net / wireless / libertas / main.c
index e9c6b4ffd8e13cbd0c22ac45656f7bce7dc3ed5c..2fe7ad0f8327aab4df520566ab39c2f129112894 100644 (file)
@@ -22,6 +22,7 @@
 #include "debugfs.h"
 #include "assoc.h"
 #include "join.h"
+#include "cmd.h"
 
 #define DRIVER_RELEASE_VERSION "323.p0"
 const char lbs_driver_version[] = "COMM-USB8388-" DRIVER_RELEASE_VERSION
@@ -216,13 +217,15 @@ u8 lbs_data_rate_to_fw_index(u32 rate)
 static ssize_t lbs_anycast_get(struct device *dev,
                struct device_attribute *attr, char * buf)
 {
+       struct lbs_private *priv = to_net_dev(dev)->priv;
        struct cmd_ds_mesh_access mesh_access;
+       int ret;
 
        memset(&mesh_access, 0, sizeof(mesh_access));
-       lbs_prepare_and_send_command(to_net_dev(dev)->priv,
-                       CMD_MESH_ACCESS,
-                       CMD_ACT_MESH_GET_ANYCAST,
-                       CMD_OPTION_WAITFORRSP, 0, (void *)&mesh_access);
+
+       ret = lbs_mesh_access(priv, CMD_ACT_MESH_GET_ANYCAST, &mesh_access);
+       if (ret)
+               return ret;
 
        return snprintf(buf, 12, "0x%X\n", le32_to_cpu(mesh_access.data[0]));
 }
@@ -233,17 +236,19 @@ static ssize_t lbs_anycast_get(struct device *dev,
 static ssize_t lbs_anycast_set(struct device *dev,
                struct device_attribute *attr, const char * buf, size_t count)
 {
+       struct lbs_private *priv = to_net_dev(dev)->priv;
        struct cmd_ds_mesh_access mesh_access;
        uint32_t datum;
+       int ret;
 
        memset(&mesh_access, 0, sizeof(mesh_access));
        sscanf(buf, "%x", &datum);
        mesh_access.data[0] = cpu_to_le32(datum);
 
-       lbs_prepare_and_send_command((to_net_dev(dev))->priv,
-                       CMD_MESH_ACCESS,
-                       CMD_ACT_MESH_SET_ANYCAST,
-                       CMD_OPTION_WAITFORRSP, 0, (void *)&mesh_access);
+       ret = lbs_mesh_access(priv, CMD_ACT_MESH_SET_ANYCAST, &mesh_access);
+       if (ret)
+               return ret;
+
        return strlen(buf);
 }
 
@@ -274,6 +279,8 @@ static ssize_t lbs_rtap_set(struct device *dev,
                if(priv->monitormode == monitor_mode)
                        return strlen(buf);
                if (priv->monitormode == LBS_MONITOR_OFF) {
+                       if (priv->infra_open || priv->mesh_open)
+                               return -EBUSY;
                        if (priv->mode == IW_MODE_INFRA)
                                lbs_send_deauthentication(priv);
                        else if (priv->mode == IW_MODE_ADHOC)
@@ -320,14 +327,15 @@ static DEVICE_ATTR(anycast_mask, 0644, lbs_anycast_get, lbs_anycast_set);
 static ssize_t lbs_autostart_enabled_get(struct device *dev,
                struct device_attribute *attr, char * buf)
 {
+       struct lbs_private *priv = to_net_dev(dev)->priv;
        struct cmd_ds_mesh_access mesh_access;
+       int ret;
 
        memset(&mesh_access, 0, sizeof(mesh_access));
-       lbs_prepare_and_send_command(to_net_dev(dev)->priv,
-                       CMD_MESH_ACCESS,
-                       CMD_ACT_MESH_GET_AUTOSTART_ENABLED,
-                       CMD_OPTION_WAITFORRSP, 0, (void *)&mesh_access);
 
+       ret = lbs_mesh_access(priv, CMD_ACT_MESH_GET_AUTOSTART_ENABLED, &mesh_access);
+       if (ret)
+               return ret;
        return sprintf(buf, "%d\n", le32_to_cpu(mesh_access.data[0]));
 }
 
@@ -343,10 +351,7 @@ static ssize_t lbs_autostart_enabled_set(struct device *dev,
        sscanf(buf, "%d", &datum);
        mesh_access.data[0] = cpu_to_le32(datum);
 
-       ret = lbs_prepare_and_send_command(priv,
-                       CMD_MESH_ACCESS,
-                       CMD_ACT_MESH_SET_AUTOSTART_ENABLED,
-                       CMD_OPTION_WAITFORRSP, 0, (void *)&mesh_access);
+       ret = lbs_mesh_access(priv, CMD_ACT_MESH_SET_AUTOSTART_ENABLED, &mesh_access);
        if (ret == 0)
                priv->mesh_autostart_enabled = datum ? 1 : 0;
 
@@ -367,116 +372,42 @@ static struct attribute_group lbs_mesh_attr_group = {
 };
 
 /**
- *  @brief Check if the device can be open and wait if necessary.
+ *  @brief This function opens the ethX or mshX interface
  *
  *  @param dev     A pointer to net_device structure
- *  @return       0
- *
- * For USB adapter, on some systems the device open handler will be
- * called before FW ready. Use the following flag check and wait
- * function to work around the issue.
- *
- */
-static int pre_open_check(struct net_device *dev)
-{
-       struct lbs_private *priv = (struct lbs_private *) dev->priv;
-       int i = 0;
-
-       while (!priv->fw_ready && i < 20) {
-               i++;
-               msleep_interruptible(100);
-       }
-       if (!priv->fw_ready) {
-               lbs_pr_err("firmware not ready\n");
-               return -1;
-       }
-
-       return 0;
-}
-
-/**
- *  @brief This function opens the device
- *
- *  @param dev     A pointer to net_device structure
- *  @return       0
+ *  @return       0 or -EBUSY if monitor mode active
  */
 static int lbs_dev_open(struct net_device *dev)
 {
-       struct lbs_private *priv = (struct lbs_private *) dev->priv;
-
-       lbs_deb_enter(LBS_DEB_NET);
+       struct lbs_private *priv = (struct lbs_private *) dev->priv ;
+       int ret = 0;
 
-       priv->open = 1;
+       spin_lock_irq(&priv->driver_lock);
 
-       if (priv->connect_status == LBS_CONNECTED)
-               netif_carrier_on(priv->dev);
-       else
-               netif_carrier_off(priv->dev);
+       if (priv->monitormode != LBS_MONITOR_OFF) {
+               ret = -EBUSY;
+               goto out;
+       }
 
-       if (priv->mesh_dev) {
-               if (priv->mesh_connect_status == LBS_CONNECTED)
-                       netif_carrier_on(priv->mesh_dev);
+       if (dev == priv->mesh_dev) {
+               priv->mesh_open = 1;
+               priv->mesh_connect_status = LBS_CONNECTED;
+               netif_carrier_on(dev);
+       } else {
+               priv->infra_open = 1;
+               
+               if (priv->connect_status == LBS_CONNECTED)
+                       netif_carrier_on(dev);
                else
-                       netif_carrier_off(priv->mesh_dev);
+                       netif_carrier_off(dev);
        }
 
-       lbs_deb_leave(LBS_DEB_NET);
-       return 0;
-}
-/**
- *  @brief This function opens the mshX interface
- *
- *  @param dev     A pointer to net_device structure
- *  @return       0
- */
-static int lbs_mesh_open(struct net_device *dev)
-{
-       struct lbs_private *priv = (struct lbs_private *) dev->priv ;
-
-       if (pre_open_check(dev) == -1)
-               return -1;
-       priv->mesh_open = 1 ;
-       netif_wake_queue(priv->mesh_dev);
-
-       priv->mesh_connect_status = LBS_CONNECTED;
-
-       netif_carrier_on(priv->mesh_dev);
-       netif_wake_queue(priv->mesh_dev);
-       if (priv->infra_open == 0)
-               return lbs_dev_open(priv->dev) ;
-       return 0;
-}
-
-/**
- *  @brief This function opens the ethX interface
- *
- *  @param dev     A pointer to net_device structure
- *  @return       0
- */
-static int lbs_open(struct net_device *dev)
-{
-       struct lbs_private *priv = (struct lbs_private *) dev->priv ;
-
-       if(pre_open_check(dev) == -1)
-               return -1;
-       priv->infra_open = 1 ;
-       netif_wake_queue(priv->dev);
-       if (priv->open == 0)
-               return lbs_dev_open(priv->dev) ;
-       return 0;
-}
-
-static int lbs_dev_close(struct net_device *dev)
-{
-       struct lbs_private *priv = dev->priv;
-
-       lbs_deb_enter(LBS_DEB_NET);
-
-       netif_carrier_off(priv->dev);
-       priv->open = 0;
+       if (!priv->tx_pending_len)
+               netif_wake_queue(dev);
+ out:
 
-       lbs_deb_leave(LBS_DEB_NET);
-       return 0;
+       spin_unlock_irq(&priv->driver_lock);
+       return ret;
 }
 
 /**
@@ -485,16 +416,20 @@ static int lbs_dev_close(struct net_device *dev)
  *  @param dev     A pointer to net_device structure
  *  @return       0
  */
-static int lbs_mesh_close(struct net_device *dev)
+static int lbs_mesh_stop(struct net_device *dev)
 {
        struct lbs_private *priv = (struct lbs_private *) (dev->priv);
 
+       spin_lock_irq(&priv->driver_lock);
+
        priv->mesh_open = 0;
-       netif_stop_queue(priv->mesh_dev);
-       if (priv->infra_open == 0)
-               return lbs_dev_close(dev);
-       else
-               return 0;
+       priv->mesh_connect_status = LBS_DISCONNECTED;
+
+       netif_stop_queue(dev);
+       netif_carrier_off(dev);
+       
+       spin_unlock_irq(&priv->driver_lock);
+       return 0;
 }
 
 /**
@@ -503,16 +438,18 @@ static int lbs_mesh_close(struct net_device *dev)
  *  @param dev     A pointer to net_device structure
  *  @return       0
  */
-static int lbs_close(struct net_device *dev)
+static int lbs_eth_stop(struct net_device *dev)
 {
        struct lbs_private *priv = (struct lbs_private *) dev->priv;
 
-       netif_stop_queue(dev);
+       spin_lock_irq(&priv->driver_lock);
+
        priv->infra_open = 0;
-       if (priv->mesh_open == 0)
-               return lbs_dev_close(dev);
-       else
-               return 0;
+
+       netif_stop_queue(dev);
+       
+       spin_unlock_irq(&priv->driver_lock);
+       return 0;
 }
 
 static void lbs_tx_timeout(struct net_device *dev)
@@ -722,6 +659,8 @@ static int lbs_thread(void *data)
        set_freezable();
 
        for (;;) {
+               int shouldsleep;
+
                lbs_deb_thread( "main-thread 111: intcounter=%d currenttxskb=%p dnld_sent=%d\n",
                                priv->intcounter, priv->currenttxskb, priv->dnld_sent);
 
@@ -729,8 +668,26 @@ static int lbs_thread(void *data)
                set_current_state(TASK_INTERRUPTIBLE);
                spin_lock_irq(&priv->driver_lock);
 
-               if ((priv->psstate == PS_STATE_SLEEP) ||
-                   (!priv->intcounter && (priv->dnld_sent || priv->cur_cmd || list_empty(&priv->cmdpendingq)))) {
+               if (priv->surpriseremoved)
+                       shouldsleep = 0;        /* Bye */
+               else if (priv->psstate == PS_STATE_SLEEP)
+                       shouldsleep = 1;        /* Sleep mode. Nothing we can do till it wakes */
+               else if (priv->intcounter)
+                       shouldsleep = 0;        /* Interrupt pending. Deal with it now */
+               else if (!priv->fw_ready)
+                       shouldsleep = 1;        /* Firmware not ready. We're waiting for it */
+               else if (priv->dnld_sent)
+                       shouldsleep = 1;        /* Something is en route to the device already */
+               else if (priv->tx_pending_len > 0)
+                       shouldsleep = 0;        /* We've a packet to send */
+               else if (priv->cur_cmd)
+                       shouldsleep = 1;        /* Can't send a command; one already running */
+               else if (!list_empty(&priv->cmdpendingq))
+                       shouldsleep = 0;        /* We have a command to send */
+               else
+                       shouldsleep = 1;        /* No command */
+
+               if (shouldsleep) {
                        lbs_deb_thread("main-thread sleeping... Conn=%d IntC=%d PS_mode=%d PS_State=%d\n",
                                       priv->connect_status, priv->intcounter,
                                       priv->psmode, priv->psstate);
@@ -801,6 +758,9 @@ static int lbs_thread(void *data)
                } else
                        spin_unlock_irq(&priv->driver_lock);
 
+               if (!priv->fw_ready)
+                       continue;
+
                /* Check if we need to confirm Sleep Request received previously */
                if (priv->psstate == PS_STATE_PRE_SLEEP &&
                    !priv->dnld_sent && !priv->cur_cmd) {
@@ -836,6 +796,28 @@ static int lbs_thread(void *data)
                 */
                if (!list_empty(&priv->cmdpendingq))
                        wake_up_all(&priv->cmd_pending);
+
+               spin_lock_irq(&priv->driver_lock);
+               if (!priv->dnld_sent && priv->tx_pending_len > 0) {
+                       int ret = priv->hw_host_to_card(priv, MVMS_DAT,
+                                                       priv->tx_pending_buf,
+                                                       priv->tx_pending_len);
+                       if (ret) {
+                               lbs_deb_tx("host_to_card failed %d\n", ret);
+                               priv->dnld_sent = DNLD_RES_RECEIVED;
+                       }
+                       priv->tx_pending_len = 0;
+                       if (!priv->currenttxskb) {
+                               /* We can wake the queues immediately if we aren't
+                                  waiting for TX feedback */
+                               if (priv->connect_status == LBS_CONNECTED)
+                                       netif_wake_queue(priv->dev);
+                               if (priv->mesh_dev &&
+                                   priv->mesh_connect_status == LBS_CONNECTED)
+                                       netif_wake_queue(priv->mesh_dev);
+                       }
+               }
+               spin_unlock_irq(&priv->driver_lock);
        }
 
        del_timer(&priv->command_timer);
@@ -864,10 +846,7 @@ static int lbs_setup_firmware(struct lbs_private *priv)
         * Read MAC address from HW
         */
        memset(priv->current_addr, 0xff, ETH_ALEN);
-
-       ret = lbs_prepare_and_send_command(priv, CMD_GET_HW_SPEC,
-                                   0, CMD_OPTION_WAITFORRSP, 0, NULL);
-
+       ret = lbs_update_hw_spec(priv);
        if (ret) {
                ret = -1;
                goto done;
@@ -875,12 +854,8 @@ static int lbs_setup_firmware(struct lbs_private *priv)
 
        lbs_set_mac_packet_filter(priv);
 
-       /* Get the supported Data rates */
-       ret = lbs_prepare_and_send_command(priv, CMD_802_11_DATA_RATE,
-                                   CMD_ACT_GET_TX_RATE,
-                                   CMD_OPTION_WAITFORRSP, 0, NULL);
-
-       if (ret) {
+       ret = lbs_get_data_rate(priv);
+       if (ret < 0) {
                ret = -1;
                goto done;
        }
@@ -889,10 +864,8 @@ static int lbs_setup_firmware(struct lbs_private *priv)
        if (priv->mesh_dev) {
                memset(&mesh_access, 0, sizeof(mesh_access));
                mesh_access.data[0] = cpu_to_le32(0);
-               ret = lbs_prepare_and_send_command(priv,
-                               CMD_MESH_ACCESS,
-                               CMD_ACT_MESH_SET_AUTOSTART_ENABLED,
-                               CMD_OPTION_WAITFORRSP, 0, (void *)&mesh_access);
+               ret = lbs_mesh_access(priv, CMD_ACT_MESH_SET_AUTOSTART_ENABLED,
+                                     &mesh_access);
                if (ret) {
                        ret = -1;
                        goto done;
@@ -913,23 +886,21 @@ done:
 static void command_timer_fn(unsigned long data)
 {
        struct lbs_private *priv = (struct lbs_private *)data;
-       struct cmd_ctrl_node *ptempnode;
-       struct cmd_ds_command *cmd;
+       struct cmd_ctrl_node *node;
        unsigned long flags;
 
-       ptempnode = priv->cur_cmd;
-       if (ptempnode == NULL) {
+       node = priv->cur_cmd;
+       if (node == NULL) {
                lbs_deb_fw("ptempnode empty\n");
                return;
        }
 
-       cmd = (struct cmd_ds_command *)ptempnode->bufvirtualaddr;
-       if (!cmd) {
+       if (!node->cmdbuf) {
                lbs_deb_fw("cmd is NULL\n");
                return;
        }
 
-       lbs_deb_fw("command_timer_fn fired, cmd %x\n", cmd->command);
+       lbs_deb_fw("command_timer_fn fired, cmd %x\n", node->cmdbuf->command);
 
        if (!priv->fw_ready)
                return;
@@ -939,7 +910,7 @@ static void command_timer_fn(unsigned long data)
        spin_unlock_irqrestore(&priv->driver_lock, flags);
 
        lbs_deb_fw("re-sending same command because of timeout\n");
-       lbs_queue_cmd(priv, ptempnode, 0);
+       lbs_queue_cmd(priv, node, 0);
 
        wake_up_interruptible(&priv->waitq);
 
@@ -1057,9 +1028,9 @@ struct lbs_private *lbs_add_card(void *card, struct device *dmdev)
        priv->infra_open = 0;
 
        /* Setup the OS Interface to our functions */
-       dev->open = lbs_open;
+       dev->open = lbs_dev_open;
        dev->hard_start_xmit = lbs_hard_start_xmit;
-       dev->stop = lbs_close;
+       dev->stop = lbs_eth_stop;
        dev->set_mac_address = lbs_set_mac_address;
        dev->tx_timeout = lbs_tx_timeout;
        dev->get_stats = lbs_get_stats;
@@ -1112,7 +1083,6 @@ int lbs_remove_card(struct lbs_private *priv)
        lbs_remove_rtap(priv);
 
        dev = priv->dev;
-       device_remove_file(&dev->dev, &dev_attr_lbs_rtap);
 
        cancel_delayed_work(&priv->scan_work);
        cancel_delayed_work(&priv->assoc_work);
@@ -1190,6 +1160,7 @@ int lbs_stop_card(struct lbs_private *priv)
        netif_carrier_off(priv->dev);
 
        lbs_debugfs_remove_one(priv);
+       device_remove_file(&dev->dev, &dev_attr_lbs_rtap);
 
        /* Flush pending command nodes */
        spin_lock_irqsave(&priv->driver_lock, flags);
@@ -1229,9 +1200,9 @@ int lbs_add_mesh(struct lbs_private *priv, struct device *dev)
        mesh_dev->priv = priv;
        priv->mesh_dev = mesh_dev;
 
-       mesh_dev->open = lbs_mesh_open;
+       mesh_dev->open = lbs_dev_open;
        mesh_dev->hard_start_xmit = lbs_hard_start_xmit;
-       mesh_dev->stop = lbs_mesh_close;
+       mesh_dev->stop = lbs_mesh_stop;
        mesh_dev->get_stats = lbs_get_stats;
        mesh_dev->set_mac_address = lbs_set_mac_address;
        mesh_dev->ethtool_ops = &lbs_ethtool_ops;
@@ -1368,23 +1339,22 @@ out:
  *  @param dev     A pointer to net_device structure
  *  @return       n/a
  */
-void lbs_interrupt(struct net_device *dev)
+void lbs_interrupt(struct lbs_private *priv)
 {
-       struct lbs_private *priv = dev->priv;
-
        lbs_deb_enter(LBS_DEB_THREAD);
 
-       lbs_deb_thread("lbs_interrupt: intcounter=%d\n",
-              priv->intcounter);
+       lbs_deb_thread("lbs_interrupt: intcounter=%d\n", priv->intcounter);
+
+       if (spin_trylock(&priv->driver_lock)) {
+               spin_unlock(&priv->driver_lock);
+               printk(KERN_CRIT "%s called without driver_lock held\n", __func__);
+               WARN_ON(1);
+       }
 
        priv->intcounter++;
 
-       if (priv->psstate == PS_STATE_SLEEP) {
+       if (priv->psstate == PS_STATE_SLEEP)
                priv->psstate = PS_STATE_AWAKE;
-               netif_wake_queue(dev);
-               if (priv->mesh_dev)
-                       netif_wake_queue(priv->mesh_dev);
-       }
 
        wake_up_interruptible(&priv->waitq);
 
@@ -1429,6 +1399,7 @@ static void __exit lbs_exit_module(void)
 
 static int lbs_rtap_open(struct net_device *dev)
 {
+       /* Yes, _stop_ the queue. Because we don't support injection */
         netif_carrier_off(dev);
         netif_stop_queue(dev);
         return 0;
@@ -1442,7 +1413,7 @@ static int lbs_rtap_stop(struct net_device *dev)
 static int lbs_rtap_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
 {
         netif_stop_queue(dev);
-        return -EOPNOTSUPP;
+        return NETDEV_TX_BUSY;
 }
 
 static struct net_device_stats *lbs_rtap_get_stats(struct net_device *dev)