]> git.karo-electronics.de Git - mv-sheeva.git/blobdiff - drivers/net/wireless/libertas/cmd.c
libertas: remove unused Automatic Frequency Control command
[mv-sheeva.git] / drivers / net / wireless / libertas / cmd.c
index cdb9b9650d73aaf1de0114966e398defa4cae8d1..15cfc52d6fd14614a4da9e4bdf26f07d77441e6d 100644 (file)
@@ -6,16 +6,14 @@
 #include <linux/kfifo.h>
 #include <linux/sched.h>
 #include <linux/slab.h>
+#include <linux/if_arp.h>
 
-#include "host.h"
 #include "decl.h"
-#include "defs.h"
-#include "dev.h"
-#include "assoc.h"
-#include "wext.h"
-#include "scan.h"
+#include "cfg.h"
 #include "cmd.h"
 
+#define CAL_NF(nf)             ((s32)(-(s32)(nf)))
+#define CAL_RSSI(snr, nf)      ((s32)((s32)(snr) + CAL_NF(nf)))
 
 static struct cmd_ctrl_node *lbs_get_cmd_ctrl_node(struct lbs_private *priv);
 
@@ -70,6 +68,8 @@ static u8 is_command_allowed_in_ps(u16 cmd)
        switch (cmd) {
        case CMD_802_11_RSSI:
                return 1;
+       case CMD_802_11_HOST_SLEEP_CFG:
+               return 1;
        default:
                break;
        }
@@ -175,16 +175,28 @@ int lbs_update_hw_spec(struct lbs_private *priv)
        if (priv->mesh_dev)
                memcpy(priv->mesh_dev->dev_addr, priv->current_addr, ETH_ALEN);
 
-       if (lbs_set_regiontable(priv, priv->regioncode, 0)) {
-               ret = -1;
-               goto out;
-       }
-
 out:
        lbs_deb_leave(LBS_DEB_CMD);
        return ret;
 }
 
+static int lbs_ret_host_sleep_cfg(struct lbs_private *priv, unsigned long dummy,
+                       struct cmd_header *resp)
+{
+       lbs_deb_enter(LBS_DEB_CMD);
+       if (priv->is_host_sleep_activated) {
+               priv->is_host_sleep_configured = 0;
+               if (priv->psstate == PS_STATE_FULL_POWER) {
+                       priv->is_host_sleep_activated = 0;
+                       wake_up_interruptible(&priv->host_sleep_q);
+               }
+       } else {
+               priv->is_host_sleep_configured = 1;
+       }
+       lbs_deb_leave(LBS_DEB_CMD);
+       return 0;
+}
+
 int lbs_host_sleep_cfg(struct lbs_private *priv, uint32_t criteria,
                struct wol_config *p_wol_config)
 {
@@ -202,12 +214,11 @@ int lbs_host_sleep_cfg(struct lbs_private *priv, uint32_t criteria,
        else
                cmd_config.wol_conf.action = CMD_ACT_ACTION_NONE;
 
-       ret = lbs_cmd_with_response(priv, CMD_802_11_HOST_SLEEP_CFG, &cmd_config);
+       ret = __lbs_cmd(priv, CMD_802_11_HOST_SLEEP_CFG, &cmd_config.hdr,
+                       le16_to_cpu(cmd_config.hdr.size),
+                       lbs_ret_host_sleep_cfg, 0);
        if (!ret) {
-               if (criteria) {
-                       lbs_deb_cmd("Set WOL criteria to %x\n", criteria);
-                       priv->wol_criteria = criteria;
-               } else
+               if (p_wol_config)
                        memcpy((uint8_t *) p_wol_config,
                                        (uint8_t *)&cmd_config.wol_conf,
                                        sizeof(struct wol_config));
@@ -353,6 +364,65 @@ int lbs_set_deep_sleep(struct lbs_private *priv, int deep_sleep)
        return ret;
 }
 
+static int lbs_ret_host_sleep_activate(struct lbs_private *priv,
+               unsigned long dummy,
+               struct cmd_header *cmd)
+{
+       lbs_deb_enter(LBS_DEB_FW);
+       priv->is_host_sleep_activated = 1;
+       wake_up_interruptible(&priv->host_sleep_q);
+       lbs_deb_leave(LBS_DEB_FW);
+       return 0;
+}
+
+int lbs_set_host_sleep(struct lbs_private *priv, int host_sleep)
+{
+       struct cmd_header cmd;
+       int ret = 0;
+       uint32_t criteria = EHS_REMOVE_WAKEUP;
+
+       lbs_deb_enter(LBS_DEB_CMD);
+
+       if (host_sleep) {
+               if (priv->is_host_sleep_activated != 1) {
+                       memset(&cmd, 0, sizeof(cmd));
+                       ret = lbs_host_sleep_cfg(priv, priv->wol_criteria,
+                                       (struct wol_config *)NULL);
+                       if (ret) {
+                               lbs_pr_info("Host sleep configuration failed: "
+                                               "%d\n", ret);
+                               return ret;
+                       }
+                       if (priv->psstate == PS_STATE_FULL_POWER) {
+                               ret = __lbs_cmd(priv,
+                                               CMD_802_11_HOST_SLEEP_ACTIVATE,
+                                               &cmd,
+                                               sizeof(cmd),
+                                               lbs_ret_host_sleep_activate, 0);
+                               if (ret)
+                                       lbs_pr_info("HOST_SLEEP_ACTIVATE "
+                                                       "failed: %d\n", ret);
+                       }
+
+                       if (!wait_event_interruptible_timeout(
+                                               priv->host_sleep_q,
+                                               priv->is_host_sleep_activated,
+                                               (10 * HZ))) {
+                               lbs_pr_err("host_sleep_q: timer expired\n");
+                               ret = -1;
+                       }
+               } else {
+                       lbs_pr_err("host sleep: already enabled\n");
+               }
+       } else {
+               if (priv->is_host_sleep_activated)
+                       ret = lbs_host_sleep_cfg(priv, criteria,
+                                       (struct wol_config *)NULL);
+       }
+
+       return ret;
+}
+
 /**
  *  @brief Set an SNMP MIB value
  *
@@ -509,23 +579,35 @@ int lbs_set_tx_power(struct lbs_private *priv, s16 dbm)
        return ret;
 }
 
-static int lbs_cmd_802_11_monitor_mode(struct cmd_ds_command *cmd,
-                                     u16 cmd_action, void *pdata_buf)
+/**
+ *  @brief Enable or disable monitor mode (only implemented on OLPC usb8388 FW)
+ *
+ *  @param priv        A pointer to struct lbs_private structure
+ *  @param enable      1 to enable monitor mode, 0 to disable
+ *
+ *  @return            0 on success, error on failure
+ */
+int lbs_set_monitor_mode(struct lbs_private *priv, int enable)
 {
-       struct cmd_ds_802_11_monitor_mode *monitor = &cmd->params.monitor;
+       struct cmd_ds_802_11_monitor_mode cmd;
+       int ret;
+
+       memset(&cmd, 0, sizeof(cmd));
+       cmd.hdr.size = cpu_to_le16(sizeof(cmd));
+       cmd.action = cpu_to_le16(CMD_ACT_SET);
+       if (enable)
+               cmd.mode = cpu_to_le16(0x1);
 
-       cmd->command = cpu_to_le16(CMD_802_11_MONITOR_MODE);
-       cmd->size =
-           cpu_to_le16(sizeof(struct cmd_ds_802_11_monitor_mode) +
-                            sizeof(struct cmd_header));
+       lbs_deb_cmd("SET_MONITOR_MODE: %d\n", enable);
 
-       monitor->action = cpu_to_le16(cmd_action);
-       if (cmd_action == CMD_ACT_SET) {
-               monitor->mode =
-                   cpu_to_le16((u16) (*(u32 *) pdata_buf));
+       ret = lbs_cmd_with_response(priv, CMD_802_11_MONITOR_MODE, &cmd);
+       if (ret == 0) {
+               priv->dev->type = enable ? ARPHRD_IEEE80211_RADIOTAP :
+                                               ARPHRD_ETHER;
        }
 
-       return 0;
+       lbs_deb_leave(LBS_DEB_CMD);
+       return ret;
 }
 
 /**
@@ -610,6 +692,162 @@ out:
        return ret;
 }
 
+/**
+ *  @brief Get current RSSI and noise floor
+ *
+ *  @param priv                A pointer to struct lbs_private structure
+ *  @param rssi                On successful return, signal level in mBm
+ *
+ *  @return            The channel on success, error on failure
+ */
+int lbs_get_rssi(struct lbs_private *priv, s8 *rssi, s8 *nf)
+{
+       struct cmd_ds_802_11_rssi cmd;
+       int ret = 0;
+
+       lbs_deb_enter(LBS_DEB_CMD);
+
+       BUG_ON(rssi == NULL);
+       BUG_ON(nf == NULL);
+
+       memset(&cmd, 0, sizeof(cmd));
+       cmd.hdr.size = cpu_to_le16(sizeof(cmd));
+       /* Average SNR over last 8 beacons */
+       cmd.n_or_snr = cpu_to_le16(8);
+
+       ret = lbs_cmd_with_response(priv, CMD_802_11_RSSI, &cmd);
+       if (ret == 0) {
+               *nf = CAL_NF(le16_to_cpu(cmd.nf));
+               *rssi = CAL_RSSI(le16_to_cpu(cmd.n_or_snr), le16_to_cpu(cmd.nf));
+       }
+
+       lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
+       return ret;
+}
+
+/**
+ *  @brief Send regulatory and 802.11d domain information to the firmware
+ *
+ *  @param priv                pointer to struct lbs_private
+ *  @param request     cfg80211 regulatory request structure
+ *  @param bands       the device's supported bands and channels
+ *
+ *  @return            0 on success, error code on failure
+*/
+int lbs_set_11d_domain_info(struct lbs_private *priv,
+                           struct regulatory_request *request,
+                           struct ieee80211_supported_band **bands)
+{
+       struct cmd_ds_802_11d_domain_info cmd;
+       struct mrvl_ie_domain_param_set *domain = &cmd.domain;
+       struct ieee80211_country_ie_triplet *t;
+       enum ieee80211_band band;
+       struct ieee80211_channel *ch;
+       u8 num_triplet = 0;
+       u8 num_parsed_chan = 0;
+       u8 first_channel = 0, next_chan = 0, max_pwr = 0;
+       u8 i, flag = 0;
+       size_t triplet_size;
+       int ret;
+
+       lbs_deb_enter(LBS_DEB_11D);
+
+       memset(&cmd, 0, sizeof(cmd));
+       cmd.action = cpu_to_le16(CMD_ACT_SET);
+
+       lbs_deb_11d("Setting country code '%c%c'\n",
+                   request->alpha2[0], request->alpha2[1]);
+
+       domain->header.type = cpu_to_le16(TLV_TYPE_DOMAIN);
+
+       /* Set country code */
+       domain->country_code[0] = request->alpha2[0];
+       domain->country_code[1] = request->alpha2[1];
+       domain->country_code[2] = ' ';
+
+       /* Now set up the channel triplets; firmware is somewhat picky here
+        * and doesn't validate channel numbers and spans; hence it would
+        * interpret a triplet of (36, 4, 20) as channels 36, 37, 38, 39.  Since
+        * the last 3 aren't valid channels, the driver is responsible for
+        * splitting that up into 4 triplet pairs of (36, 1, 20) + (40, 1, 20)
+        * etc.
+        */
+       for (band = 0;
+            (band < IEEE80211_NUM_BANDS) && (num_triplet < MAX_11D_TRIPLETS);
+            band++) {
+
+               if (!bands[band])
+                       continue;
+
+               for (i = 0;
+                    (i < bands[band]->n_channels) && (num_triplet < MAX_11D_TRIPLETS);
+                    i++) {
+                       ch = &bands[band]->channels[i];
+                       if (ch->flags & IEEE80211_CHAN_DISABLED)
+                               continue;
+
+                       if (!flag) {
+                               flag = 1;
+                               next_chan = first_channel = (u32) ch->hw_value;
+                               max_pwr = ch->max_power;
+                               num_parsed_chan = 1;
+                               continue;
+                       }
+
+                       if ((ch->hw_value == next_chan + 1) &&
+                                       (ch->max_power == max_pwr)) {
+                               /* Consolidate adjacent channels */
+                               next_chan++;
+                               num_parsed_chan++;
+                       } else {
+                               /* Add this triplet */
+                               lbs_deb_11d("11D triplet (%d, %d, %d)\n",
+                                       first_channel, num_parsed_chan,
+                                       max_pwr);
+                               t = &domain->triplet[num_triplet];
+                               t->chans.first_channel = first_channel;
+                               t->chans.num_channels = num_parsed_chan;
+                               t->chans.max_power = max_pwr;
+                               num_triplet++;
+                               flag = 0;
+                       }
+               }
+
+               if (flag) {
+                       /* Add last triplet */
+                       lbs_deb_11d("11D triplet (%d, %d, %d)\n", first_channel,
+                               num_parsed_chan, max_pwr);
+                       t = &domain->triplet[num_triplet];
+                       t->chans.first_channel = first_channel;
+                       t->chans.num_channels = num_parsed_chan;
+                       t->chans.max_power = max_pwr;
+                       num_triplet++;
+               }
+       }
+
+       lbs_deb_11d("# triplets %d\n", num_triplet);
+
+       /* Set command header sizes */
+       triplet_size = num_triplet * sizeof(struct ieee80211_country_ie_triplet);
+       domain->header.len = cpu_to_le16(sizeof(domain->country_code) +
+                                       triplet_size);
+
+       lbs_deb_hex(LBS_DEB_11D, "802.11D domain param set",
+                       (u8 *) &cmd.domain.country_code,
+                       le16_to_cpu(domain->header.len));
+
+       cmd.hdr.size = cpu_to_le16(sizeof(cmd.hdr) +
+                                  sizeof(cmd.action) +
+                                  sizeof(cmd.domain.header) +
+                                  sizeof(cmd.domain.country_code) +
+                                  triplet_size);
+
+       ret = lbs_cmd_with_response(priv, CMD_802_11D_DOMAIN_INFO, &cmd);
+
+       lbs_deb_leave_args(LBS_DEB_11D, "ret %d", ret);
+       return ret;
+}
+
 static int lbs_cmd_reg_access(struct cmd_ds_command *cmdptr,
                               u8 cmd_action, void *pdata_buf)
 {
@@ -712,6 +950,10 @@ static void lbs_queue_cmd(struct lbs_private *priv,
                }
        }
 
+       if (le16_to_cpu(cmdnode->cmdbuf->command) ==
+                       CMD_802_11_WAKEUP_CONFIRM)
+               addtail = 0;
+
        spin_lock_irqsave(&priv->driver_lock, flags);
 
        if (addtail)
@@ -962,40 +1204,6 @@ int lbs_prepare_and_send_command(struct lbs_private *priv,
                ret = lbs_cmd_reg_access(cmdptr, cmd_action, pdata_buf);
                break;
 
-       case CMD_802_11_MONITOR_MODE:
-               ret = lbs_cmd_802_11_monitor_mode(cmdptr,
-                                         cmd_action, pdata_buf);
-               break;
-
-       case CMD_802_11_RSSI:
-               ret = lbs_cmd_802_11_rssi(priv, cmdptr);
-               break;
-
-       case CMD_802_11_SET_AFC:
-       case CMD_802_11_GET_AFC:
-
-               cmdptr->command = cpu_to_le16(cmd_no);
-               cmdptr->size = cpu_to_le16(sizeof(struct cmd_ds_802_11_afc) +
-                                          sizeof(struct cmd_header));
-
-               memmove(&cmdptr->params.afc,
-                       pdata_buf, sizeof(struct cmd_ds_802_11_afc));
-
-               ret = 0;
-               goto done;
-
-       case CMD_802_11_TPC_CFG:
-               cmdptr->command = cpu_to_le16(CMD_802_11_TPC_CFG);
-               cmdptr->size =
-                   cpu_to_le16(sizeof(struct cmd_ds_802_11_tpc_cfg) +
-                                    sizeof(struct cmd_header));
-
-               memmove(&cmdptr->params.tpccfg,
-                       pdata_buf, sizeof(struct cmd_ds_802_11_tpc_cfg));
-
-               ret = 0;
-               break;
-
 #ifdef CONFIG_LIBERTAS_MESH
 
        case CMD_BT_ACCESS:
@@ -1303,6 +1511,15 @@ int lbs_execute_next_command(struct lbs_private *priv)
                 * check if in power save mode, if yes, put the device back
                 * to PS mode
                 */
+#ifdef TODO
+               /*
+                * This was the old code for libertas+wext. Someone that
+                * understands this beast should re-code it in a sane way.
+                *
+                * I actually don't understand why this is related to WPA
+                * and to connection status, shouldn't powering should be
+                * independ of such things?
+                */
                if ((priv->psmode != LBS802_11POWERMODECAM) &&
                    (priv->psstate == PS_STATE_FULL_POWER) &&
                    ((priv->connect_status == LBS_CONNECTED) ||
@@ -1324,6 +1541,7 @@ int lbs_execute_next_command(struct lbs_private *priv)
                                lbs_ps_sleep(priv, 0);
                        }
                }
+#endif
        }
 
        ret = 0;
@@ -1353,6 +1571,11 @@ static void lbs_send_confirmsleep(struct lbs_private *priv)
        /* We don't get a response on the sleep-confirmation */
        priv->dnld_sent = DNLD_RES_RECEIVED;
 
+       if (priv->is_host_sleep_configured) {
+               priv->is_host_sleep_activated = 1;
+               wake_up_interruptible(&priv->host_sleep_q);
+       }
+
        /* If nothing to do, go back to sleep (?) */
        if (!kfifo_len(&priv->event_fifo) && !priv->resp_len[priv->resp_idx])
                priv->psstate = PS_STATE_SLEEP;