CHROMIUM: mwifiex: Dereference bss only if it is non-null
[cascardo/linux.git] / drivers / net / wireless / mwifiex / sta_ioctl.c
index d7b11de..1474a02 100644 (file)
@@ -26,6 +26,9 @@
 #include "11n.h"
 #include "cfg80211.h"
 
+static int disconnect_on_suspend = 1;
+module_param(disconnect_on_suspend, int, 0644);
+
 /*
  * Copies the multicast address list from device to driver.
  *
@@ -53,7 +56,6 @@ int mwifiex_copy_mcast_addr(struct mwifiex_multicast_list *mlist,
  */
 int mwifiex_wait_queue_complete(struct mwifiex_adapter *adapter)
 {
-       bool cancel_flag = false;
        int status;
        struct cmd_ctrl_node *cmd_queued;
 
@@ -66,18 +68,12 @@ int mwifiex_wait_queue_complete(struct mwifiex_adapter *adapter)
        dev_dbg(adapter->dev, "cmd pending\n");
        atomic_inc(&adapter->cmd_pending);
 
-       /* Status pending, wake up main process */
-       queue_work(adapter->workqueue, &adapter->main_work);
-
        /* Wait for completion */
-       wait_event_interruptible(adapter->cmd_wait_q.wait,
-                                *(cmd_queued->condition));
-       if (!*(cmd_queued->condition))
-               cancel_flag = true;
-
-       if (cancel_flag) {
-               mwifiex_cancel_pending_ioctl(adapter);
-               dev_dbg(adapter->dev, "cmd cancel\n");
+       status = wait_event_interruptible(adapter->cmd_wait_q.wait,
+                                         *(cmd_queued->condition));
+       if (status) {
+               dev_err(adapter->dev, "cmd_wait_q terminated: %d\n", status);
+               return status;
        }
 
        status = adapter->cmd_wait_q.status;
@@ -155,20 +151,21 @@ int mwifiex_request_set_multicast_list(struct mwifiex_private *priv,
  * information.
  */
 int mwifiex_fill_new_bss_desc(struct mwifiex_private *priv,
-                             u8 *bssid, s32 rssi, u8 *ie_buf,
-                             size_t ie_len, u16 beacon_period,
-                             u16 cap_info_bitmap, u8 band,
-                             struct mwifiex_bssdescriptor *bss_desc)
+                             struct cfg80211_bss *bss,
+                             struct mwifiex_bssdescriptor *bss_desc,
+                             u8 *beacon_ie, size_t beacon_ie_len)
 {
-       int ret;
-
-       memcpy(bss_desc->mac_address, bssid, ETH_ALEN);
-       bss_desc->rssi = rssi;
-       bss_desc->beacon_buf = ie_buf;
-       bss_desc->beacon_buf_size = ie_len;
-       bss_desc->beacon_period = beacon_period;
-       bss_desc->cap_info_bitmap = cap_info_bitmap;
-       bss_desc->bss_band = band;
+       struct mwifiex_bss_priv *bss_priv = (void *)bss->priv;
+
+       memcpy(bss_desc->mac_address, bss->bssid, ETH_ALEN);
+       bss_desc->rssi = bss->signal;
+       bss_desc->beacon_buf = beacon_ie;
+       bss_desc->beacon_buf_size = beacon_ie_len;
+       bss_desc->beacon_period = bss->beacon_interval;
+       bss_desc->cap_info_bitmap = bss->capability;
+       bss_desc->bss_band = bss_priv->band;
+       bss_desc->fw_tsf = bss_priv->fw_tsf;
+       bss_desc->timestamp = bss->tsf;
        if (bss_desc->cap_info_bitmap & WLAN_CAPABILITY_PRIVACY) {
                dev_dbg(priv->adapter->dev, "info: InterpretIE: AP WEP enabled\n");
                bss_desc->privacy = MWIFIEX_802_11_PRIV_FILTER_8021X_WEP;
@@ -180,10 +177,45 @@ int mwifiex_fill_new_bss_desc(struct mwifiex_private *priv,
        else
                bss_desc->bss_mode = NL80211_IFTYPE_STATION;
 
-       ret = mwifiex_update_bss_desc_with_ie(priv->adapter, bss_desc,
-                                             ie_buf, ie_len);
+       return mwifiex_update_bss_desc_with_ie(priv->adapter, bss_desc);
+}
 
-       return ret;
+static int mwifiex_process_country_ie(struct mwifiex_private *priv,
+                                     struct cfg80211_bss *bss)
+{
+       u8 *country_ie, country_ie_len;
+       struct mwifiex_802_11d_domain_reg *domain_info =
+                                       &priv->adapter->domain_reg;
+
+       country_ie = (u8 *)ieee80211_bss_get_ie(bss, WLAN_EID_COUNTRY);
+
+       if (!country_ie)
+               return 0;
+
+       country_ie_len = country_ie[1];
+       if (country_ie_len < IEEE80211_COUNTRY_IE_MIN_LEN)
+               return 0;
+
+       domain_info->country_code[0] = country_ie[2];
+       domain_info->country_code[1] = country_ie[3];
+       domain_info->country_code[2] = ' ';
+
+       country_ie_len -= IEEE80211_COUNTRY_STRING_LEN;
+
+       domain_info->no_of_triplet =
+               country_ie_len / sizeof(struct ieee80211_country_ie_triplet);
+
+       memcpy((u8 *)domain_info->triplet,
+              &country_ie[2] + IEEE80211_COUNTRY_STRING_LEN, country_ie_len);
+
+       if (mwifiex_send_cmd_async(priv, HostCmd_CMD_802_11D_DOMAIN_INFO,
+                                  HostCmd_ACT_GEN_SET, 0, NULL)) {
+               wiphy_err(priv->wdev->wiphy,
+                         "11D: setting domain info in FW\n");
+               return -1;
+       }
+
+       return 0;
 }
 
 /*
@@ -198,10 +230,13 @@ int mwifiex_bss_start(struct mwifiex_private *priv, struct cfg80211_bss *bss,
        struct mwifiex_adapter *adapter = priv->adapter;
        struct mwifiex_bssdescriptor *bss_desc = NULL;
        u8 *beacon_ie = NULL;
+       size_t beacon_ie_len;
 
        priv->scan_block = false;
 
        if (bss) {
+               mwifiex_process_country_ie(priv, bss);
+
                /* Allocate and fill new bss descriptor */
                bss_desc = kzalloc(sizeof(struct mwifiex_bssdescriptor),
                                GFP_KERNEL);
@@ -210,19 +245,16 @@ int mwifiex_bss_start(struct mwifiex_private *priv, struct cfg80211_bss *bss,
                        return -ENOMEM;
                }
 
-               beacon_ie = kmemdup(bss->information_elements,
-                                       bss->len_beacon_ies, GFP_KERNEL);
+               beacon_ie_len = bss->len_information_elements;
+               beacon_ie = kmemdup(bss->information_elements, beacon_ie_len,
+                                   GFP_KERNEL);
                if (!beacon_ie) {
                        kfree(bss_desc);
-                       dev_err(priv->adapter->dev, " failed to alloc beacon_ie\n");
                        return -ENOMEM;
                }
 
-               ret = mwifiex_fill_new_bss_desc(priv, bss->bssid, bss->signal,
-                                               beacon_ie, bss->len_beacon_ies,
-                                               bss->beacon_interval,
-                                               bss->capability,
-                                               *(u8 *)bss->priv, bss_desc);
+               ret = mwifiex_fill_new_bss_desc(priv, bss, bss_desc, beacon_ie,
+                                               beacon_ie_len);
                if (ret)
                        goto done;
        }
@@ -240,8 +272,7 @@ int mwifiex_bss_start(struct mwifiex_private *priv, struct cfg80211_bss *bss,
                dev_dbg(adapter->dev, "info: SSID found in scan list ... "
                                      "associating...\n");
 
-               if (!netif_queue_stopped(priv->netdev))
-                       mwifiex_stop_net_dev_queue(priv->netdev, adapter);
+               mwifiex_stop_net_dev_queue(priv->netdev, adapter);
                if (netif_carrier_ok(priv->netdev))
                        netif_carrier_off(priv->netdev);
 
@@ -268,9 +299,8 @@ int mwifiex_bss_start(struct mwifiex_private *priv, struct cfg80211_bss *bss,
                if (bss_desc && bss_desc->ssid.ssid_len &&
                    (!mwifiex_ssid_cmp(&priv->curr_bss_params.bss_descriptor.
                                       ssid, &bss_desc->ssid))) {
-                       kfree(bss_desc);
-                       kfree(beacon_ie);
-                       return 0;
+                       ret = 0;
+                       goto done;
                }
 
                /* Exit Adhoc mode first */
@@ -283,8 +313,7 @@ int mwifiex_bss_start(struct mwifiex_private *priv, struct cfg80211_bss *bss,
 
                ret = mwifiex_check_network_compatibility(priv, bss_desc);
 
-               if (!netif_queue_stopped(priv->netdev))
-                       mwifiex_stop_net_dev_queue(priv->netdev, adapter);
+               mwifiex_stop_net_dev_queue(priv->netdev, adapter);
                if (netif_carrier_ok(priv->netdev))
                        netif_carrier_off(priv->netdev);
 
@@ -303,8 +332,8 @@ int mwifiex_bss_start(struct mwifiex_private *priv, struct cfg80211_bss *bss,
        }
 
 done:
-       kfree(bss_desc);
        kfree(beacon_ie);
+       kfree(bss_desc);
        return ret;
 }
 
@@ -417,6 +446,16 @@ EXPORT_SYMBOL_GPL(mwifiex_cancel_hs);
 int mwifiex_enable_hs(struct mwifiex_adapter *adapter)
 {
        struct mwifiex_ds_hs_cfg hscfg;
+       struct mwifiex_private *priv;
+       int i;
+
+       if (disconnect_on_suspend) {
+               for (i = 0; i < adapter->priv_num; i++) {
+                       priv = adapter->priv[i];
+                       if (priv && priv->media_connected)
+                               mwifiex_deauthenticate(priv, NULL);
+               }
+       }
 
        if (adapter->hs_activated) {
                dev_dbg(adapter->dev, "cmd: HS Already actived\n");
@@ -436,8 +475,11 @@ int mwifiex_enable_hs(struct mwifiex_adapter *adapter)
                return false;
        }
 
-       wait_event_interruptible(adapter->hs_activate_wait_q,
-                                adapter->hs_activate_wait_q_woken);
+       if (wait_event_interruptible(adapter->hs_activate_wait_q,
+                                    adapter->hs_activate_wait_q_woken)) {
+               dev_err(adapter->dev, "hs_activate_wait_q terminated\n");
+               return false;
+       }
 
        return true;
 }
@@ -468,7 +510,8 @@ int mwifiex_get_bss_info(struct mwifiex_private *priv,
 
        info->bss_chan = bss_desc->channel;
 
-       info->region_code = adapter->region_code;
+       memcpy(info->country_code, priv->country_code,
+              IEEE80211_COUNTRY_STRING_LEN);
 
        info->media_connected = priv->media_connected;
 
@@ -995,6 +1038,39 @@ static int mwifiex_set_wapi_ie(struct mwifiex_private *priv,
        return 0;
 }
 
+/*
+ * IOCTL request handler to set/reset WPS IE.
+ *
+ * The supplied WPS IE is treated as a opaque buffer. Only the first field
+ * is checked to internally enable WPS. If buffer length is zero, the existing
+ * WPS IE is reset.
+ */
+static int mwifiex_set_wps_ie(struct mwifiex_private *priv,
+                              u8 *ie_data_ptr, u16 ie_len)
+{
+       if (ie_len) {
+               priv->wps_ie = kzalloc(MWIFIEX_MAX_VSIE_LEN, GFP_KERNEL);
+               if (!priv->wps_ie)
+                       return -ENOMEM;
+               if (ie_len > sizeof(priv->wps_ie)) {
+                       dev_dbg(priv->adapter->dev,
+                               "info: failed to copy WPS IE, too big\n");
+                       kfree(priv->wps_ie);
+                       return -1;
+               }
+               memcpy(priv->wps_ie, ie_data_ptr, ie_len);
+               priv->wps_ie_len = ie_len;
+               dev_dbg(priv->adapter->dev, "cmd: Set wps_ie_len=%d IE=%#x\n",
+                       priv->wps_ie_len, priv->wps_ie[0]);
+       } else {
+               kfree(priv->wps_ie);
+               priv->wps_ie_len = ie_len;
+               dev_dbg(priv->adapter->dev,
+                       "info: Reset wps_ie_len=%d\n", priv->wps_ie_len);
+       }
+       return 0;
+}
+
 /*
  * IOCTL request handler to set WAPI key.
  *
@@ -1184,39 +1260,6 @@ mwifiex_drv_get_driver_version(struct mwifiex_adapter *adapter, char *version,
        return 0;
 }
 
-/*
- * Sends IOCTL request to get signal information.
- *
- * This function allocates the IOCTL request buffer, fills it
- * with requisite parameters and calls the IOCTL handler.
- */
-int mwifiex_get_signal_info(struct mwifiex_private *priv,
-                           struct mwifiex_ds_get_signal *signal)
-{
-       int status;
-
-       signal->selector = ALL_RSSI_INFO_MASK;
-
-       /* Signal info can be obtained only if connected */
-       if (!priv->media_connected) {
-               dev_dbg(priv->adapter->dev,
-                       "info: Can not get signal in disconnected state\n");
-               return -1;
-       }
-
-       status = mwifiex_send_cmd_sync(priv, HostCmd_CMD_RSSI_INFO,
-                                      HostCmd_ACT_GEN_GET, 0, signal);
-
-       if (!status) {
-               if (signal->selector & BCN_RSSI_AVG_MASK)
-                       priv->qual_level = signal->bcn_rssi_avg;
-               if (signal->selector & BCN_NF_AVG_MASK)
-                       priv->qual_noise = signal->bcn_nf_avg;
-       }
-
-       return status;
-}
-
 /*
  * Sends IOCTL request to set encoding parameters.
  *
@@ -1441,6 +1484,7 @@ mwifiex_set_gen_ie_helper(struct mwifiex_private *priv, u8 *ie_data_ptr,
                        priv->wps.session_enable = true;
                        dev_dbg(priv->adapter->dev,
                                "info: WPS Session Enabled.\n");
+                       ret = mwifiex_set_wps_ie(priv, ie_data_ptr, ie_len);
                }
 
                /* Append the passed data to the end of the