These changes are the raw update to linux-4.4.6-rt14. Kernel sources
[kvmfornfv.git] / kernel / drivers / net / wireless / ath / wil6210 / cfg80211.c
index b971726..20d07ef 100644 (file)
@@ -289,6 +289,26 @@ static int wil_cfg80211_scan(struct wiphy *wiphy,
        }
 
        wil_dbg_misc(wil, "Start scan_request 0x%p\n", request);
+       wil_dbg_misc(wil, "SSID count: %d", request->n_ssids);
+
+       for (i = 0; i < request->n_ssids; i++) {
+               wil_dbg_misc(wil, "SSID[%d]", i);
+               print_hex_dump_bytes("SSID ", DUMP_PREFIX_OFFSET,
+                                    request->ssids[i].ssid,
+                                    request->ssids[i].ssid_len);
+       }
+
+       if (request->n_ssids)
+               rc = wmi_set_ssid(wil, request->ssids[0].ssid_len,
+                                 request->ssids[0].ssid);
+       else
+               rc = wmi_set_ssid(wil, 0, NULL);
+
+       if (rc) {
+               wil_err(wil, "set SSID for scan request failed: %d\n", rc);
+               return rc;
+       }
+
        wil->scan_request = request;
        mod_timer(&wil->scan_timer, jiffies + WIL6210_SCAN_TO);
 
@@ -316,12 +336,9 @@ static int wil_cfg80211_scan(struct wiphy *wiphy,
        else
                wil_dbg_misc(wil, "Scan has no IE's\n");
 
-       rc = wmi_set_ie(wil, WMI_FRAME_PROBE_REQ, request->ie_len,
-                       request->ie);
-       if (rc) {
-               wil_err(wil, "Aborting scan, set_ie failed: %d\n", rc);
+       rc = wmi_set_ie(wil, WMI_FRAME_PROBE_REQ, request->ie_len, request->ie);
+       if (rc)
                goto out;
-       }
 
        rc = wmi_send(wil, WMI_START_SCAN_CMDID, &cmd, sizeof(cmd.cmd) +
                        cmd.cmd.num_channels * sizeof(cmd.cmd.channel_list[0]));
@@ -402,11 +419,8 @@ static int wil_cfg80211_connect(struct wiphy *wiphy,
        rsn_eid = sme->ie ?
                        cfg80211_find_ie(WLAN_EID_RSN, sme->ie, sme->ie_len) :
                        NULL;
-
-       if (sme->privacy && !rsn_eid) {
-               wil_err(wil, "Missing RSN IE for secure connection\n");
-               return -EINVAL;
-       }
+       if (sme->privacy && !rsn_eid)
+               wil_info(wil, "WSC connection\n");
 
        bss = cfg80211_get_bss(wiphy, sme->channel, sme->bssid,
                               sme->ssid, sme->ssid_len,
@@ -425,10 +439,17 @@ static int wil_cfg80211_connect(struct wiphy *wiphy,
        wil->privacy = sme->privacy;
 
        if (wil->privacy) {
-               /* For secure assoc, send WMI_DELETE_CIPHER_KEY_CMD */
-               rc = wmi_del_cipher_key(wil, 0, bss->bssid);
+               /* For secure assoc, remove old keys */
+               rc = wmi_del_cipher_key(wil, 0, bss->bssid,
+                                       WMI_KEY_USE_PAIRWISE);
                if (rc) {
-                       wil_err(wil, "WMI_DELETE_CIPHER_KEY_CMD failed\n");
+                       wil_err(wil, "WMI_DELETE_CIPHER_KEY_CMD(PTK) failed\n");
+                       goto out;
+               }
+               rc = wmi_del_cipher_key(wil, 0, bss->bssid,
+                                       WMI_KEY_USE_RX_GROUP);
+               if (rc) {
+                       wil_err(wil, "WMI_DELETE_CIPHER_KEY_CMD(GTK) failed\n");
                        goto out;
                }
        }
@@ -438,10 +459,8 @@ static int wil_cfg80211_connect(struct wiphy *wiphy,
         * ies in FW.
         */
        rc = wmi_set_ie(wil, WMI_FRAME_ASSOC_REQ, sme->ie_len, sme->ie);
-       if (rc) {
-               wil_err(wil, "WMI_SET_APPIE_CMD failed\n");
+       if (rc)
                goto out;
-       }
 
        /* WMI_CONNECT_CMD */
        memset(&conn, 0, sizeof(conn));
@@ -458,11 +477,18 @@ static int wil_cfg80211_connect(struct wiphy *wiphy,
                goto out;
        }
        if (wil->privacy) {
-               conn.dot11_auth_mode = WMI_AUTH11_SHARED;
-               conn.auth_mode = WMI_AUTH_WPA2_PSK;
-               conn.pairwise_crypto_type = WMI_CRYPT_AES_GCMP;
-               conn.pairwise_crypto_len = 16;
-       } else {
+               if (rsn_eid) { /* regular secure connection */
+                       conn.dot11_auth_mode = WMI_AUTH11_SHARED;
+                       conn.auth_mode = WMI_AUTH_WPA2_PSK;
+                       conn.pairwise_crypto_type = WMI_CRYPT_AES_GCMP;
+                       conn.pairwise_crypto_len = 16;
+                       conn.group_crypto_type = WMI_CRYPT_AES_GCMP;
+                       conn.group_crypto_len = 16;
+               } else { /* WSC */
+                       conn.dot11_auth_mode = WMI_AUTH11_WSC;
+                       conn.auth_mode = WMI_AUTH_NONE;
+               }
+       } else { /* insecure connection */
                conn.dot11_auth_mode = WMI_AUTH11_OPEN;
                conn.auth_mode = WMI_AUTH_NONE;
        }
@@ -507,6 +533,8 @@ static int wil_cfg80211_disconnect(struct wiphy *wiphy,
        int rc;
        struct wil6210_priv *wil = wiphy_to_wil(wiphy);
 
+       wil_dbg_misc(wil, "%s(reason=%d)\n", __func__, reason_code);
+
        rc = wmi_send(wil, WMI_DISCONNECT_CMDID, NULL, 0);
 
        return rc;
@@ -561,6 +589,39 @@ static int wil_cfg80211_set_channel(struct wiphy *wiphy,
        return 0;
 }
 
+static enum wmi_key_usage wil_detect_key_usage(struct wil6210_priv *wil,
+                                              bool pairwise)
+{
+       struct wireless_dev *wdev = wil->wdev;
+       enum wmi_key_usage rc;
+       static const char * const key_usage_str[] = {
+               [WMI_KEY_USE_PAIRWISE]  = "WMI_KEY_USE_PAIRWISE",
+               [WMI_KEY_USE_RX_GROUP]  = "WMI_KEY_USE_RX_GROUP",
+               [WMI_KEY_USE_TX_GROUP]  = "WMI_KEY_USE_TX_GROUP",
+       };
+
+       if (pairwise) {
+               rc = WMI_KEY_USE_PAIRWISE;
+       } else {
+               switch (wdev->iftype) {
+               case NL80211_IFTYPE_STATION:
+                       rc = WMI_KEY_USE_RX_GROUP;
+                       break;
+               case NL80211_IFTYPE_AP:
+                       rc = WMI_KEY_USE_TX_GROUP;
+                       break;
+               default:
+                       /* TODO: Rx GTK or Tx GTK? */
+                       wil_err(wil, "Can't determine GTK type\n");
+                       rc = WMI_KEY_USE_RX_GROUP;
+                       break;
+               }
+       }
+       wil_dbg_misc(wil, "%s() -> %s\n", __func__, key_usage_str[rc]);
+
+       return rc;
+}
+
 static int wil_cfg80211_add_key(struct wiphy *wiphy,
                                struct net_device *ndev,
                                u8 key_index, bool pairwise,
@@ -568,13 +629,13 @@ static int wil_cfg80211_add_key(struct wiphy *wiphy,
                                struct key_params *params)
 {
        struct wil6210_priv *wil = wiphy_to_wil(wiphy);
+       enum wmi_key_usage key_usage = wil_detect_key_usage(wil, pairwise);
 
-       /* group key is not used */
-       if (!pairwise)
-               return 0;
+       wil_dbg_misc(wil, "%s(%pM[%d] %s)\n", __func__, mac_addr, key_index,
+                    pairwise ? "PTK" : "GTK");
 
-       return wmi_add_cipher_key(wil, key_index, mac_addr,
-                                 params->key_len, params->key);
+       return wmi_add_cipher_key(wil, key_index, mac_addr, params->key_len,
+                                 params->key, key_usage);
 }
 
 static int wil_cfg80211_del_key(struct wiphy *wiphy,
@@ -583,12 +644,12 @@ static int wil_cfg80211_del_key(struct wiphy *wiphy,
                                const u8 *mac_addr)
 {
        struct wil6210_priv *wil = wiphy_to_wil(wiphy);
+       enum wmi_key_usage key_usage = wil_detect_key_usage(wil, pairwise);
 
-       /* group key is not used */
-       if (!pairwise)
-               return 0;
+       wil_dbg_misc(wil, "%s(%pM[%d] %s)\n", __func__, mac_addr, key_index,
+                    pairwise ? "PTK" : "GTK");
 
-       return wmi_del_cipher_key(wil, key_index, mac_addr);
+       return wmi_del_cipher_key(wil, key_index, mac_addr, key_usage);
 }
 
 /* Need to be present or wiphy_new() will WARN */
@@ -656,22 +717,98 @@ static int wil_fix_bcon(struct wil6210_priv *wil,
 {
        struct ieee80211_mgmt *f = (struct ieee80211_mgmt *)bcon->probe_resp;
        size_t hlen = offsetof(struct ieee80211_mgmt, u.probe_resp.variable);
-       int rc = 0;
 
        if (bcon->probe_resp_len <= hlen)
                return 0;
 
-       if (!bcon->proberesp_ies) {
-               bcon->proberesp_ies = f->u.probe_resp.variable;
-               bcon->proberesp_ies_len = bcon->probe_resp_len - hlen;
-               rc = 1;
-       }
+/* always use IE's from full probe frame, they has more info
+ * notable RSN
+ */
+       bcon->proberesp_ies = f->u.probe_resp.variable;
+       bcon->proberesp_ies_len = bcon->probe_resp_len - hlen;
        if (!bcon->assocresp_ies) {
-               bcon->assocresp_ies = f->u.probe_resp.variable;
-               bcon->assocresp_ies_len = bcon->probe_resp_len - hlen;
-               rc = 1;
+               bcon->assocresp_ies = bcon->proberesp_ies;
+               bcon->assocresp_ies_len = bcon->proberesp_ies_len;
        }
 
+       return 1;
+}
+
+/* internal functions for device reset and starting AP */
+static int _wil_cfg80211_set_ies(struct wiphy *wiphy,
+                                struct cfg80211_beacon_data *bcon)
+{
+       int rc;
+       struct wil6210_priv *wil = wiphy_to_wil(wiphy);
+
+       rc = wmi_set_ie(wil, WMI_FRAME_PROBE_RESP, bcon->proberesp_ies_len,
+                       bcon->proberesp_ies);
+       if (rc)
+               return rc;
+
+       rc = wmi_set_ie(wil, WMI_FRAME_ASSOC_RESP, bcon->assocresp_ies_len,
+                       bcon->assocresp_ies);
+#if 0 /* to use beacon IE's, remove this #if 0 */
+       if (rc)
+               return rc;
+
+       rc = wmi_set_ie(wil, WMI_FRAME_BEACON, bcon->tail_len, bcon->tail);
+#endif
+
+       return rc;
+}
+
+static int _wil_cfg80211_start_ap(struct wiphy *wiphy,
+                                 struct net_device *ndev,
+                                 const u8 *ssid, size_t ssid_len, u32 privacy,
+                                 int bi, u8 chan,
+                                 struct cfg80211_beacon_data *bcon,
+                                 u8 hidden_ssid)
+{
+       struct wil6210_priv *wil = wiphy_to_wil(wiphy);
+       int rc;
+       struct wireless_dev *wdev = ndev->ieee80211_ptr;
+       u8 wmi_nettype = wil_iftype_nl2wmi(wdev->iftype);
+
+       wil_set_recovery_state(wil, fw_recovery_idle);
+
+       mutex_lock(&wil->mutex);
+
+       __wil_down(wil);
+       rc = __wil_up(wil);
+       if (rc)
+               goto out;
+
+       rc = wmi_set_ssid(wil, ssid_len, ssid);
+       if (rc)
+               goto out;
+
+       rc = _wil_cfg80211_set_ies(wiphy, bcon);
+       if (rc)
+               goto out;
+
+       wil->privacy = privacy;
+       wil->channel = chan;
+       wil->hidden_ssid = hidden_ssid;
+
+       netif_carrier_on(ndev);
+
+       rc = wmi_pcp_start(wil, bi, wmi_nettype, chan, hidden_ssid);
+       if (rc)
+               goto err_pcp_start;
+
+       rc = wil_bcast_init(wil);
+       if (rc)
+               goto err_bcast;
+
+       goto out; /* success */
+
+err_bcast:
+       wmi_pcp_stop(wil);
+err_pcp_start:
+       netif_carrier_off(ndev);
+out:
+       mutex_unlock(&wil->mutex);
        return rc;
 }
 
@@ -681,50 +818,50 @@ static int wil_cfg80211_change_beacon(struct wiphy *wiphy,
 {
        struct wil6210_priv *wil = wiphy_to_wil(wiphy);
        int rc;
+       u32 privacy = 0;
 
        wil_dbg_misc(wil, "%s()\n", __func__);
+       wil_print_bcon_data(bcon);
 
        if (wil_fix_bcon(wil, bcon)) {
                wil_dbg_misc(wil, "Fixed bcon\n");
                wil_print_bcon_data(bcon);
        }
 
-       /* FW do not form regular beacon, so bcon IE's are not set
-        * For the DMG bcon, when it will be supported, bcon IE's will
-        * be reused; add something like:
-        * wmi_set_ie(wil, WMI_FRAME_BEACON, bcon->beacon_ies_len,
-        * bcon->beacon_ies);
-        */
-       rc = wmi_set_ie(wil, WMI_FRAME_PROBE_RESP,
-                       bcon->proberesp_ies_len,
-                       bcon->proberesp_ies);
-       if (rc) {
-               wil_err(wil, "set_ie(PROBE_RESP) failed\n");
-               return rc;
-       }
+       if (bcon->proberesp_ies &&
+           cfg80211_find_ie(WLAN_EID_RSN, bcon->proberesp_ies,
+                            bcon->proberesp_ies_len))
+               privacy = 1;
 
-       rc = wmi_set_ie(wil, WMI_FRAME_ASSOC_RESP,
-                       bcon->assocresp_ies_len,
-                       bcon->assocresp_ies);
-       if (rc) {
-               wil_err(wil, "set_ie(ASSOC_RESP) failed\n");
-               return rc;
+       /* in case privacy has changed, need to restart the AP */
+       if (wil->privacy != privacy) {
+               struct wireless_dev *wdev = ndev->ieee80211_ptr;
+
+               wil_dbg_misc(wil, "privacy changed %d=>%d. Restarting AP\n",
+                            wil->privacy, privacy);
+
+               rc = _wil_cfg80211_start_ap(wiphy, ndev, wdev->ssid,
+                                           wdev->ssid_len, privacy,
+                                           wdev->beacon_interval,
+                                           wil->channel, bcon,
+                                           wil->hidden_ssid);
+       } else {
+               rc = _wil_cfg80211_set_ies(wiphy, bcon);
        }
 
-       return 0;
+       return rc;
 }
 
 static int wil_cfg80211_start_ap(struct wiphy *wiphy,
                                 struct net_device *ndev,
                                 struct cfg80211_ap_settings *info)
 {
-       int rc = 0;
+       int rc;
        struct wil6210_priv *wil = wiphy_to_wil(wiphy);
-       struct wireless_dev *wdev = ndev->ieee80211_ptr;
        struct ieee80211_channel *channel = info->chandef.chan;
        struct cfg80211_beacon_data *bcon = &info->beacon;
        struct cfg80211_crypto_settings *crypto = &info->crypto;
-       u8 wmi_nettype = wil_iftype_nl2wmi(wdev->iftype);
+       u8 hidden_ssid;
 
        wil_dbg_misc(wil, "%s()\n", __func__);
 
@@ -733,10 +870,29 @@ static int wil_cfg80211_start_ap(struct wiphy *wiphy,
                return -EINVAL;
        }
 
+       switch (info->hidden_ssid) {
+       case NL80211_HIDDEN_SSID_NOT_IN_USE:
+               hidden_ssid = WMI_HIDDEN_SSID_DISABLED;
+               break;
+
+       case NL80211_HIDDEN_SSID_ZERO_LEN:
+               hidden_ssid = WMI_HIDDEN_SSID_SEND_EMPTY;
+               break;
+
+       case NL80211_HIDDEN_SSID_ZERO_CONTENTS:
+               hidden_ssid = WMI_HIDDEN_SSID_CLEAR;
+               break;
+
+       default:
+               wil_err(wil, "AP: Invalid hidden SSID %d\n", info->hidden_ssid);
+               return -EOPNOTSUPP;
+       }
        wil_dbg_misc(wil, "AP on Channel %d %d MHz, %s\n", channel->hw_value,
                     channel->center_freq, info->privacy ? "secure" : "open");
        wil_dbg_misc(wil, "Privacy: %d auth_type %d\n",
                     info->privacy, info->auth_type);
+       wil_dbg_misc(wil, "Hidden SSID mode: %d\n",
+                    info->hidden_ssid);
        wil_dbg_misc(wil, "BI %d DTIM %d\n", info->beacon_interval,
                     info->dtim_period);
        print_hex_dump_bytes("SSID ", DUMP_PREFIX_OFFSET,
@@ -749,53 +905,11 @@ static int wil_cfg80211_start_ap(struct wiphy *wiphy,
                wil_print_bcon_data(bcon);
        }
 
-       wil_set_recovery_state(wil, fw_recovery_idle);
-
-       mutex_lock(&wil->mutex);
-
-       __wil_down(wil);
-       rc = __wil_up(wil);
-       if (rc)
-               goto out;
-
-       rc = wmi_set_ssid(wil, info->ssid_len, info->ssid);
-       if (rc)
-               goto out;
-
-       /* IE's */
-       /* bcon 'head IE's are not relevant for 60g band */
-       /*
-        * FW do not form regular beacon, so bcon IE's are not set
-        * For the DMG bcon, when it will be supported, bcon IE's will
-        * be reused; add something like:
-        * wmi_set_ie(wil, WMI_FRAME_BEACON, bcon->beacon_ies_len,
-        * bcon->beacon_ies);
-        */
-       wmi_set_ie(wil, WMI_FRAME_PROBE_RESP, bcon->proberesp_ies_len,
-                  bcon->proberesp_ies);
-       wmi_set_ie(wil, WMI_FRAME_ASSOC_RESP, bcon->assocresp_ies_len,
-                  bcon->assocresp_ies);
+       rc = _wil_cfg80211_start_ap(wiphy, ndev,
+                                   info->ssid, info->ssid_len, info->privacy,
+                                   info->beacon_interval, channel->hw_value,
+                                   bcon, hidden_ssid);
 
-       wil->privacy = info->privacy;
-
-       netif_carrier_on(ndev);
-
-       rc = wmi_pcp_start(wil, info->beacon_interval, wmi_nettype,
-                          channel->hw_value);
-       if (rc)
-               goto err_pcp_start;
-
-       rc = wil_bcast_init(wil);
-       if (rc)
-               goto err_bcast;
-
-       goto out; /* success */
-err_bcast:
-       wmi_pcp_stop(wil);
-err_pcp_start:
-       netif_carrier_off(ndev);
-out:
-       mutex_unlock(&wil->mutex);
        return rc;
 }
 
@@ -814,13 +928,9 @@ static int wil_cfg80211_stop_ap(struct wiphy *wiphy,
        wmi_pcp_stop(wil);
 
        __wil_down(wil);
-       __wil_up(wil);
 
        mutex_unlock(&wil->mutex);
 
-       /* some functions above might fail (e.g. __wil_up). Nevertheless, we
-        * return success because AP has stopped
-        */
        return 0;
 }
 
@@ -830,6 +940,9 @@ static int wil_cfg80211_del_station(struct wiphy *wiphy,
 {
        struct wil6210_priv *wil = wiphy_to_wil(wiphy);
 
+       wil_dbg_misc(wil, "%s(%pM, reason=%d)\n", __func__, params->mac,
+                    params->reason_code);
+
        mutex_lock(&wil->mutex);
        wil6210_disconnect(wil, params->mac, params->reason_code, false);
        mutex_unlock(&wil->mutex);
@@ -967,8 +1080,7 @@ static struct cfg80211_ops wil_cfg80211_ops = {
 
 static void wil_wiphy_init(struct wiphy *wiphy)
 {
-       /* TODO: set real value */
-       wiphy->max_scan_ssids = 10;
+       wiphy->max_scan_ssids = 1;
        wiphy->max_scan_ie_len = WMI_MAX_IE_LEN;
        wiphy->max_num_pmkids = 0 /* TODO: */;
        wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |