summaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/ath/wil6210/cfg80211.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/wireless/ath/wil6210/cfg80211.c')
-rw-r--r--drivers/net/wireless/ath/wil6210/cfg80211.c333
1 files changed, 280 insertions, 53 deletions
diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c
index f79c337105cb..d18e81fae5f1 100644
--- a/drivers/net/wireless/ath/wil6210/cfg80211.c
+++ b/drivers/net/wireless/ath/wil6210/cfg80211.c
@@ -48,9 +48,29 @@ static struct ieee80211_channel wil_60ghz_channels[] = {
CHAN60G(1, 0),
CHAN60G(2, 0),
CHAN60G(3, 0),
-/* channel 4 not supported yet */
+ CHAN60G(4, 0),
};
+static int wil_num_supported_channels(struct wil6210_priv *wil)
+{
+ int num_channels = ARRAY_SIZE(wil_60ghz_channels);
+
+ if (!test_bit(WMI_FW_CAPABILITY_CHANNEL_4, wil->fw_capabilities))
+ num_channels--;
+
+ return num_channels;
+}
+
+void update_supported_bands(struct wil6210_priv *wil)
+{
+ struct wiphy *wiphy = wil_to_wiphy(wil);
+
+ wil_dbg_misc(wil, "update supported bands");
+
+ wiphy->bands[NL80211_BAND_60GHZ]->n_channels =
+ wil_num_supported_channels(wil);
+}
+
/* Vendor id to be used in vendor specific command and events
* to user space.
* NOTE: The authoritative place for definition of QCA_NL80211_VENDOR_ID,
@@ -199,7 +219,9 @@ wil_mgmt_stypes[NUM_NL80211_IFTYPES] = {
.tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
BIT(IEEE80211_STYPE_PROBE_RESP >> 4) |
BIT(IEEE80211_STYPE_ASSOC_RESP >> 4) |
- BIT(IEEE80211_STYPE_DISASSOC >> 4),
+ BIT(IEEE80211_STYPE_DISASSOC >> 4) |
+ BIT(IEEE80211_STYPE_AUTH >> 4) |
+ BIT(IEEE80211_STYPE_REASSOC_RESP >> 4),
.rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
BIT(IEEE80211_STYPE_PROBE_REQ >> 4) |
BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) |
@@ -871,6 +893,26 @@ static void wil_print_crypto(struct wil6210_priv *wil,
c->control_port_no_encrypt);
}
+static const char *
+wil_get_auth_type_name(enum nl80211_auth_type auth_type)
+{
+ switch (auth_type) {
+ case NL80211_AUTHTYPE_OPEN_SYSTEM:
+ return "OPEN_SYSTEM";
+ case NL80211_AUTHTYPE_SHARED_KEY:
+ return "SHARED_KEY";
+ case NL80211_AUTHTYPE_FT:
+ return "FT";
+ case NL80211_AUTHTYPE_NETWORK_EAP:
+ return "NETWORK_EAP";
+ case NL80211_AUTHTYPE_SAE:
+ return "SAE";
+ case NL80211_AUTHTYPE_AUTOMATIC:
+ return "AUTOMATIC";
+ default:
+ return "unknown";
+ }
+}
static void wil_print_connect_params(struct wil6210_priv *wil,
struct cfg80211_connect_params *sme)
{
@@ -884,11 +926,73 @@ static void wil_print_connect_params(struct wil6210_priv *wil,
if (sme->ssid)
print_hex_dump(KERN_INFO, " SSID: ", DUMP_PREFIX_OFFSET,
16, 1, sme->ssid, sme->ssid_len, true);
+ if (sme->prev_bssid)
+ wil_info(wil, " Previous BSSID=%pM\n", sme->prev_bssid);
+ wil_info(wil, " Auth Type: %s\n",
+ wil_get_auth_type_name(sme->auth_type));
wil_info(wil, " Privacy: %s\n", sme->privacy ? "secure" : "open");
wil_info(wil, " PBSS: %d\n", sme->pbss);
wil_print_crypto(wil, &sme->crypto);
}
+static int wil_ft_connect(struct wiphy *wiphy,
+ struct net_device *ndev,
+ struct cfg80211_connect_params *sme)
+{
+ struct wil6210_priv *wil = wiphy_to_wil(wiphy);
+ struct wil6210_vif *vif = ndev_to_vif(ndev);
+ struct wmi_ft_auth_cmd auth_cmd;
+ int rc;
+
+ if (!test_bit(WMI_FW_CAPABILITY_FT_ROAMING, wil->fw_capabilities)) {
+ wil_err(wil, "FT: FW does not support FT roaming\n");
+ return -EOPNOTSUPP;
+ }
+
+ if (!sme->prev_bssid) {
+ wil_err(wil, "FT: prev_bssid was not set\n");
+ return -EINVAL;
+ }
+
+ if (ether_addr_equal(sme->prev_bssid, sme->bssid)) {
+ wil_err(wil, "FT: can not roam to same AP\n");
+ return -EINVAL;
+ }
+
+ if (!test_bit(wil_vif_fwconnected, vif->status)) {
+ wil_err(wil, "FT: roam while not connected\n");
+ return -EINVAL;
+ }
+
+ if (vif->privacy != sme->privacy) {
+ wil_err(wil, "FT: privacy mismatch, current (%d) roam (%d)\n",
+ vif->privacy, sme->privacy);
+ return -EINVAL;
+ }
+
+ if (sme->pbss) {
+ wil_err(wil, "FT: roam is not valid for PBSS\n");
+ return -EINVAL;
+ }
+
+ memset(&auth_cmd, 0, sizeof(auth_cmd));
+ auth_cmd.channel = sme->channel->hw_value - 1;
+ ether_addr_copy(auth_cmd.bssid, sme->bssid);
+
+ wil_info(wil, "FT: roaming\n");
+
+ set_bit(wil_vif_ft_roam, vif->status);
+ rc = wmi_send(wil, WMI_FT_AUTH_CMDID, vif->mid,
+ &auth_cmd, sizeof(auth_cmd));
+ if (rc == 0)
+ mod_timer(&vif->connect_timer,
+ jiffies + msecs_to_jiffies(5000));
+ else
+ clear_bit(wil_vif_ft_roam, vif->status);
+
+ return rc;
+}
+
static int wil_cfg80211_connect(struct wiphy *wiphy,
struct net_device *ndev,
struct cfg80211_connect_params *sme)
@@ -901,14 +1005,23 @@ static int wil_cfg80211_connect(struct wiphy *wiphy,
const u8 *rsn_eid;
int ch;
int rc = 0;
+ bool is_ft_roam = false;
+ u8 network_type;
enum ieee80211_bss_type bss_type = IEEE80211_BSS_TYPE_ESS;
wil_dbg_misc(wil, "connect, mid=%d\n", vif->mid);
wil_print_connect_params(wil, sme);
- if (test_bit(wil_vif_fwconnecting, vif->status) ||
+ if (sme->auth_type == NL80211_AUTHTYPE_FT)
+ is_ft_roam = true;
+ if (sme->auth_type == NL80211_AUTHTYPE_AUTOMATIC &&
test_bit(wil_vif_fwconnected, vif->status))
- return -EALREADY;
+ is_ft_roam = true;
+
+ if (!is_ft_roam)
+ if (test_bit(wil_vif_fwconnecting, vif->status) ||
+ test_bit(wil_vif_fwconnected, vif->status))
+ return -EALREADY;
if (sme->ie_len > WMI_MAX_IE_LEN) {
wil_err(wil, "IE too large (%td bytes)\n", sme->ie_len);
@@ -918,8 +1031,13 @@ 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)
+ if (sme->privacy && !rsn_eid) {
wil_info(wil, "WSC connection\n");
+ if (is_ft_roam) {
+ wil_err(wil, "No WSC with FT roam\n");
+ return -EINVAL;
+ }
+ }
if (sme->pbss)
bss_type = IEEE80211_BSS_TYPE_PBSS;
@@ -941,6 +1059,45 @@ static int wil_cfg80211_connect(struct wiphy *wiphy,
vif->privacy = sme->privacy;
vif->pbss = sme->pbss;
+ rc = wmi_set_ie(vif, WMI_FRAME_ASSOC_REQ, sme->ie_len, sme->ie);
+ if (rc)
+ goto out;
+
+ switch (bss->capability & WLAN_CAPABILITY_DMG_TYPE_MASK) {
+ case WLAN_CAPABILITY_DMG_TYPE_AP:
+ network_type = WMI_NETTYPE_INFRA;
+ break;
+ case WLAN_CAPABILITY_DMG_TYPE_PBSS:
+ network_type = WMI_NETTYPE_P2P;
+ break;
+ default:
+ wil_err(wil, "Unsupported BSS type, capability= 0x%04x\n",
+ bss->capability);
+ rc = -EINVAL;
+ goto out;
+ }
+
+ ch = bss->channel->hw_value;
+ if (ch == 0) {
+ wil_err(wil, "BSS at unknown frequency %dMhz\n",
+ bss->channel->center_freq);
+ rc = -EOPNOTSUPP;
+ goto out;
+ }
+
+ if (is_ft_roam) {
+ if (network_type != WMI_NETTYPE_INFRA) {
+ wil_err(wil, "FT: Unsupported BSS type, capability= 0x%04x\n",
+ bss->capability);
+ rc = -EINVAL;
+ goto out;
+ }
+ rc = wil_ft_connect(wiphy, ndev, sme);
+ if (rc == 0)
+ vif->bss = bss;
+ goto out;
+ }
+
if (vif->privacy) {
/* For secure assoc, remove old keys */
rc = wmi_del_cipher_key(vif, 0, bss->bssid,
@@ -957,28 +1114,9 @@ static int wil_cfg80211_connect(struct wiphy *wiphy,
}
}
- /* WMI_SET_APPIE_CMD. ie may contain rsn info as well as other info
- * elements. Send it also in case it's empty, to erase previously set
- * ies in FW.
- */
- rc = wmi_set_ie(vif, WMI_FRAME_ASSOC_REQ, sme->ie_len, sme->ie);
- if (rc)
- goto out;
-
/* WMI_CONNECT_CMD */
memset(&conn, 0, sizeof(conn));
- switch (bss->capability & WLAN_CAPABILITY_DMG_TYPE_MASK) {
- case WLAN_CAPABILITY_DMG_TYPE_AP:
- conn.network_type = WMI_NETTYPE_INFRA;
- break;
- case WLAN_CAPABILITY_DMG_TYPE_PBSS:
- conn.network_type = WMI_NETTYPE_P2P;
- break;
- default:
- wil_err(wil, "Unsupported BSS type, capability= 0x%04x\n",
- bss->capability);
- goto out;
- }
+ conn.network_type = network_type;
if (vif->privacy) {
if (rsn_eid) { /* regular secure connection */
conn.dot11_auth_mode = WMI_AUTH11_SHARED;
@@ -998,14 +1136,6 @@ static int wil_cfg80211_connect(struct wiphy *wiphy,
conn.ssid_len = min_t(u8, ssid_eid[1], 32);
memcpy(conn.ssid, ssid_eid+2, conn.ssid_len);
-
- ch = bss->channel->hw_value;
- if (ch == 0) {
- wil_err(wil, "BSS at unknown frequency %dMhz\n",
- bss->channel->center_freq);
- rc = -EOPNOTSUPP;
- goto out;
- }
conn.channel = ch - 1;
ether_addr_copy(conn.bssid, bss->bssid);
@@ -1201,9 +1331,9 @@ wil_find_sta_by_key_usage(struct wil6210_priv *wil, u8 mid,
return &wil->sta[cid];
}
-static void wil_set_crypto_rx(u8 key_index, enum wmi_key_usage key_usage,
- struct wil_sta_info *cs,
- struct key_params *params)
+void wil_set_crypto_rx(u8 key_index, enum wmi_key_usage key_usage,
+ struct wil_sta_info *cs,
+ struct key_params *params)
{
struct wil_tid_crypto_rx_single *cc;
int tid;
@@ -1286,13 +1416,19 @@ static int wil_cfg80211_add_key(struct wiphy *wiphy,
params->seq_len, params->seq);
if (IS_ERR(cs)) {
- wil_err(wil, "Not connected, %pM %s[%d] PN %*phN\n",
- mac_addr, key_usage_str[key_usage], key_index,
- params->seq_len, params->seq);
- return -EINVAL;
+ /* in FT, sta info may not be available as add_key may be
+ * sent by host before FW sends WMI_CONNECT_EVENT
+ */
+ if (!test_bit(wil_vif_ft_roam, vif->status)) {
+ wil_err(wil, "Not connected, %pM %s[%d] PN %*phN\n",
+ mac_addr, key_usage_str[key_usage], key_index,
+ params->seq_len, params->seq);
+ return -EINVAL;
+ }
}
- wil_del_rx_key(key_index, key_usage, cs);
+ if (!IS_ERR(cs))
+ wil_del_rx_key(key_index, key_usage, cs);
if (params->seq && params->seq_len != IEEE80211_GCMP_PN_LEN) {
wil_err(wil,
@@ -1305,7 +1441,10 @@ static int wil_cfg80211_add_key(struct wiphy *wiphy,
rc = wmi_add_cipher_key(vif, key_index, mac_addr, params->key_len,
params->key, key_usage);
- if (!rc)
+ if (!rc && !IS_ERR(cs))
+ /* in FT set crypto will take place upon receiving
+ * WMI_RING_EN_EVENTID event
+ */
wil_set_crypto_rx(key_index, key_usage, cs, params);
return rc;
@@ -1468,21 +1607,36 @@ static void wil_print_bcon_data(struct cfg80211_beacon_data *b)
}
/* internal functions for device reset and starting AP */
-static int _wil_cfg80211_set_ies(struct wil6210_vif *vif,
- struct cfg80211_beacon_data *bcon)
+static u8 *
+_wil_cfg80211_get_proberesp_ies(const u8 *proberesp, u16 proberesp_len,
+ u16 *ies_len)
{
- int rc;
- u16 len = 0, proberesp_len = 0;
- u8 *ies = NULL, *proberesp = NULL;
+ u8 *ies = NULL;
- if (bcon->probe_resp) {
+ if (proberesp) {
struct ieee80211_mgmt *f =
- (struct ieee80211_mgmt *)bcon->probe_resp;
+ (struct ieee80211_mgmt *)proberesp;
size_t hlen = offsetof(struct ieee80211_mgmt,
u.probe_resp.variable);
- proberesp = f->u.probe_resp.variable;
- proberesp_len = bcon->probe_resp_len - hlen;
+
+ ies = f->u.probe_resp.variable;
+ if (ies_len)
+ *ies_len = proberesp_len - hlen;
}
+
+ return ies;
+}
+
+static int _wil_cfg80211_set_ies(struct wil6210_vif *vif,
+ struct cfg80211_beacon_data *bcon)
+{
+ int rc;
+ u16 len = 0, proberesp_len = 0;
+ u8 *ies = NULL, *proberesp;
+
+ proberesp = _wil_cfg80211_get_proberesp_ies(bcon->probe_resp,
+ bcon->probe_resp_len,
+ &proberesp_len);
rc = _wil_cfg80211_merge_extra_ies(proberesp,
proberesp_len,
bcon->proberesp_ies,
@@ -1526,6 +1680,9 @@ static int _wil_cfg80211_start_ap(struct wiphy *wiphy,
struct wireless_dev *wdev = ndev->ieee80211_ptr;
u8 wmi_nettype = wil_iftype_nl2wmi(wdev->iftype);
u8 is_go = (wdev->iftype == NL80211_IFTYPE_P2P_GO);
+ u16 proberesp_len = 0;
+ u8 *proberesp;
+ bool ft = false;
if (pbss)
wmi_nettype = WMI_NETTYPE_P2P;
@@ -1538,6 +1695,25 @@ static int _wil_cfg80211_start_ap(struct wiphy *wiphy,
wil_set_recovery_state(wil, fw_recovery_idle);
+ proberesp = _wil_cfg80211_get_proberesp_ies(bcon->probe_resp,
+ bcon->probe_resp_len,
+ &proberesp_len);
+ /* check that the probe response IEs has a MDE */
+ if ((proberesp && proberesp_len > 0 &&
+ cfg80211_find_ie(WLAN_EID_MOBILITY_DOMAIN,
+ proberesp,
+ proberesp_len)))
+ ft = true;
+
+ if (ft) {
+ if (!test_bit(WMI_FW_CAPABILITY_FT_ROAMING,
+ wil->fw_capabilities)) {
+ wil_err(wil, "FW does not support FT roaming\n");
+ return -ENOTSUPP;
+ }
+ set_bit(wil_vif_ft_roam, vif->status);
+ }
+
mutex_lock(&wil->mutex);
if (!wil_has_other_active_ifaces(wil, ndev, true, false)) {
@@ -1699,6 +1875,7 @@ static int wil_cfg80211_stop_ap(struct wiphy *wiphy,
mutex_lock(&wil->mutex);
wmi_pcp_stop(vif);
+ clear_bit(wil_vif_ft_roam, vif->status);
if (last)
__wil_down(wil);
@@ -1718,8 +1895,9 @@ static int wil_cfg80211_add_station(struct wiphy *wiphy,
struct wil6210_vif *vif = ndev_to_vif(dev);
struct wil6210_priv *wil = wiphy_to_wil(wiphy);
- wil_dbg_misc(wil, "add station %pM aid %d mid %d\n",
- mac, params->aid, vif->mid);
+ wil_dbg_misc(wil, "add station %pM aid %d mid %d mask 0x%x set 0x%x\n",
+ mac, params->aid, vif->mid,
+ params->sta_flags_mask, params->sta_flags_set);
if (!disable_ap_sme) {
wil_err(wil, "not supported with AP SME enabled\n");
@@ -2040,6 +2218,54 @@ wil_cfg80211_sched_scan_stop(struct wiphy *wiphy, struct net_device *dev,
return 0;
}
+static int
+wil_cfg80211_update_ft_ies(struct wiphy *wiphy, struct net_device *dev,
+ struct cfg80211_update_ft_ies_params *ftie)
+{
+ struct wil6210_priv *wil = wiphy_to_wil(wiphy);
+ struct wil6210_vif *vif = ndev_to_vif(dev);
+ struct cfg80211_bss *bss;
+ struct wmi_ft_reassoc_cmd reassoc;
+ int rc = 0;
+
+ wil_dbg_misc(wil, "update ft ies, mid=%d\n", vif->mid);
+ wil_hex_dump_misc("FT IE ", DUMP_PREFIX_OFFSET, 16, 1,
+ ftie->ie, ftie->ie_len, true);
+
+ if (!test_bit(WMI_FW_CAPABILITY_FT_ROAMING, wil->fw_capabilities)) {
+ wil_err(wil, "FW does not support FT roaming\n");
+ return -EOPNOTSUPP;
+ }
+
+ rc = wmi_update_ft_ies(vif, ftie->ie_len, ftie->ie);
+ if (rc)
+ return rc;
+
+ if (!test_bit(wil_vif_ft_roam, vif->status))
+ /* vif is not roaming */
+ return 0;
+
+ /* wil_vif_ft_roam is set. wil_cfg80211_update_ft_ies is used as
+ * a trigger for reassoc
+ */
+
+ bss = vif->bss;
+ if (!bss) {
+ wil_err(wil, "FT: bss is NULL\n");
+ return -EINVAL;
+ }
+
+ memset(&reassoc, 0, sizeof(reassoc));
+ ether_addr_copy(reassoc.bssid, bss->bssid);
+
+ rc = wmi_send(wil, WMI_FT_REASSOC_CMDID, vif->mid,
+ &reassoc, sizeof(reassoc));
+ if (rc)
+ wil_err(wil, "FT: reassoc failed (%d)\n", rc);
+
+ return rc;
+}
+
static const struct cfg80211_ops wil_cfg80211_ops = {
.add_virtual_intf = wil_cfg80211_add_iface,
.del_virtual_intf = wil_cfg80211_del_iface,
@@ -2075,6 +2301,7 @@ static const struct cfg80211_ops wil_cfg80211_ops = {
.resume = wil_cfg80211_resume,
.sched_scan_start = wil_cfg80211_sched_scan_start,
.sched_scan_stop = wil_cfg80211_sched_scan_stop,
+ .update_ft_ies = wil_cfg80211_update_ft_ies,
};
static void wil_wiphy_init(struct wiphy *wiphy)
OpenPOWER on IntegriCloud