|
@@ -1,5 +1,6 @@
|
|
|
/*
|
|
|
* Copyright (c) 2012-2017 Qualcomm Atheros, Inc.
|
|
|
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
|
|
|
*
|
|
|
* Permission to use, copy, modify, and/or distribute this software for any
|
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
@@ -17,8 +18,10 @@
|
|
|
#include <linux/etherdevice.h>
|
|
|
#include <linux/moduleparam.h>
|
|
|
#include <net/netlink.h>
|
|
|
+#include <net/cfg80211.h>
|
|
|
#include "wil6210.h"
|
|
|
#include "wmi.h"
|
|
|
+#include "fw.h"
|
|
|
|
|
|
#define WIL_MAX_ROC_DURATION_MS 5000
|
|
|
|
|
@@ -258,9 +261,10 @@ int wil_iftype_nl2wmi(enum nl80211_iftype type)
|
|
|
return -EOPNOTSUPP;
|
|
|
}
|
|
|
|
|
|
-int wil_cid_fill_sinfo(struct wil6210_priv *wil, int cid,
|
|
|
+int wil_cid_fill_sinfo(struct wil6210_vif *vif, int cid,
|
|
|
struct station_info *sinfo)
|
|
|
{
|
|
|
+ struct wil6210_priv *wil = vif_to_wil(vif);
|
|
|
struct wmi_notify_req_cmd cmd = {
|
|
|
.cid = cid,
|
|
|
.interval_usec = 0,
|
|
@@ -272,17 +276,17 @@ int wil_cid_fill_sinfo(struct wil6210_priv *wil, int cid,
|
|
|
struct wil_net_stats *stats = &wil->sta[cid].stats;
|
|
|
int rc;
|
|
|
|
|
|
- rc = wmi_call(wil, WMI_NOTIFY_REQ_CMDID, &cmd, sizeof(cmd),
|
|
|
+ rc = wmi_call(wil, WMI_NOTIFY_REQ_CMDID, vif->mid, &cmd, sizeof(cmd),
|
|
|
WMI_NOTIFY_REQ_DONE_EVENTID, &reply, sizeof(reply), 20);
|
|
|
if (rc)
|
|
|
return rc;
|
|
|
|
|
|
- wil_dbg_wmi(wil, "Link status for CID %d: {\n"
|
|
|
+ wil_dbg_wmi(wil, "Link status for CID %d MID %d: {\n"
|
|
|
" MCS %d TSF 0x%016llx\n"
|
|
|
" BF status 0x%08x RSSI %d SQI %d%%\n"
|
|
|
" Tx Tpt %d goodput %d Rx goodput %d\n"
|
|
|
" Sectors(rx:tx) my %d:%d peer %d:%d\n""}\n",
|
|
|
- cid, le16_to_cpu(reply.evt.bf_mcs),
|
|
|
+ cid, vif->mid, le16_to_cpu(reply.evt.bf_mcs),
|
|
|
le64_to_cpu(reply.evt.tsf), reply.evt.status,
|
|
|
reply.evt.rssi,
|
|
|
reply.evt.sqi,
|
|
@@ -315,7 +319,7 @@ int wil_cid_fill_sinfo(struct wil6210_priv *wil, int cid,
|
|
|
sinfo->tx_packets = stats->tx_packets;
|
|
|
sinfo->tx_failed = stats->tx_errors;
|
|
|
|
|
|
- if (test_bit(wil_status_fwconnected, wil->status)) {
|
|
|
+ if (test_bit(wil_vif_fwconnected, vif->status)) {
|
|
|
sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL);
|
|
|
if (test_bit(WMI_FW_CAPABILITY_RSSI_REPORTING,
|
|
|
wil->fw_capabilities))
|
|
@@ -331,30 +335,34 @@ static int wil_cfg80211_get_station(struct wiphy *wiphy,
|
|
|
struct net_device *ndev,
|
|
|
const u8 *mac, struct station_info *sinfo)
|
|
|
{
|
|
|
+ struct wil6210_vif *vif = ndev_to_vif(ndev);
|
|
|
struct wil6210_priv *wil = wiphy_to_wil(wiphy);
|
|
|
int rc;
|
|
|
|
|
|
- int cid = wil_find_cid(wil, mac);
|
|
|
+ int cid = wil_find_cid(wil, vif->mid, mac);
|
|
|
|
|
|
- wil_dbg_misc(wil, "get_station: %pM CID %d\n", mac, cid);
|
|
|
+ wil_dbg_misc(wil, "get_station: %pM CID %d MID %d\n", mac, cid,
|
|
|
+ vif->mid);
|
|
|
if (cid < 0)
|
|
|
return cid;
|
|
|
|
|
|
- rc = wil_cid_fill_sinfo(wil, cid, sinfo);
|
|
|
+ rc = wil_cid_fill_sinfo(vif, cid, sinfo);
|
|
|
|
|
|
return rc;
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
- * Find @idx-th active STA for station dump.
|
|
|
+ * Find @idx-th active STA for specific MID for station dump.
|
|
|
*/
|
|
|
-static int wil_find_cid_by_idx(struct wil6210_priv *wil, int idx)
|
|
|
+static int wil_find_cid_by_idx(struct wil6210_priv *wil, u8 mid, int idx)
|
|
|
{
|
|
|
int i;
|
|
|
|
|
|
for (i = 0; i < ARRAY_SIZE(wil->sta); i++) {
|
|
|
if (wil->sta[i].status == wil_sta_unused)
|
|
|
continue;
|
|
|
+ if (wil->sta[i].mid != mid)
|
|
|
+ continue;
|
|
|
if (idx == 0)
|
|
|
return i;
|
|
|
idx--;
|
|
@@ -367,17 +375,19 @@ static int wil_cfg80211_dump_station(struct wiphy *wiphy,
|
|
|
struct net_device *dev, int idx,
|
|
|
u8 *mac, struct station_info *sinfo)
|
|
|
{
|
|
|
+ struct wil6210_vif *vif = ndev_to_vif(dev);
|
|
|
struct wil6210_priv *wil = wiphy_to_wil(wiphy);
|
|
|
int rc;
|
|
|
- int cid = wil_find_cid_by_idx(wil, idx);
|
|
|
+ int cid = wil_find_cid_by_idx(wil, vif->mid, idx);
|
|
|
|
|
|
if (cid < 0)
|
|
|
return -ENOENT;
|
|
|
|
|
|
ether_addr_copy(mac, wil->sta[cid].addr);
|
|
|
- wil_dbg_misc(wil, "dump_station: %pM CID %d\n", mac, cid);
|
|
|
+ wil_dbg_misc(wil, "dump_station: %pM CID %d MID %d\n", mac, cid,
|
|
|
+ vif->mid);
|
|
|
|
|
|
- rc = wil_cid_fill_sinfo(wil, cid, sinfo);
|
|
|
+ rc = wil_cid_fill_sinfo(vif, cid, sinfo);
|
|
|
|
|
|
return rc;
|
|
|
}
|
|
@@ -388,7 +398,7 @@ static int wil_cfg80211_start_p2p_device(struct wiphy *wiphy,
|
|
|
struct wil6210_priv *wil = wiphy_to_wil(wiphy);
|
|
|
|
|
|
wil_dbg_misc(wil, "start_p2p_device: entered\n");
|
|
|
- wil->p2p.p2p_dev_started = 1;
|
|
|
+ wil->p2p_dev_started = 1;
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
@@ -396,20 +406,66 @@ static void wil_cfg80211_stop_p2p_device(struct wiphy *wiphy,
|
|
|
struct wireless_dev *wdev)
|
|
|
{
|
|
|
struct wil6210_priv *wil = wiphy_to_wil(wiphy);
|
|
|
- struct wil_p2p_info *p2p = &wil->p2p;
|
|
|
|
|
|
- if (!p2p->p2p_dev_started)
|
|
|
+ if (!wil->p2p_dev_started)
|
|
|
return;
|
|
|
|
|
|
wil_dbg_misc(wil, "stop_p2p_device: entered\n");
|
|
|
mutex_lock(&wil->mutex);
|
|
|
- mutex_lock(&wil->p2p_wdev_mutex);
|
|
|
+ mutex_lock(&wil->vif_mutex);
|
|
|
wil_p2p_stop_radio_operations(wil);
|
|
|
- p2p->p2p_dev_started = 0;
|
|
|
- mutex_unlock(&wil->p2p_wdev_mutex);
|
|
|
+ wil->p2p_dev_started = 0;
|
|
|
+ mutex_unlock(&wil->vif_mutex);
|
|
|
mutex_unlock(&wil->mutex);
|
|
|
}
|
|
|
|
|
|
+static int wil_cfg80211_validate_add_iface(struct wil6210_priv *wil,
|
|
|
+ enum nl80211_iftype new_type)
|
|
|
+{
|
|
|
+ int i;
|
|
|
+ struct wireless_dev *wdev;
|
|
|
+ struct iface_combination_params params = {
|
|
|
+ .num_different_channels = 1,
|
|
|
+ };
|
|
|
+
|
|
|
+ for (i = 0; i < wil->max_vifs; i++) {
|
|
|
+ if (wil->vifs[i]) {
|
|
|
+ wdev = vif_to_wdev(wil->vifs[i]);
|
|
|
+ params.iftype_num[wdev->iftype]++;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ params.iftype_num[new_type]++;
|
|
|
+ return cfg80211_check_combinations(wil->wiphy, ¶ms);
|
|
|
+}
|
|
|
+
|
|
|
+static int wil_cfg80211_validate_change_iface(struct wil6210_priv *wil,
|
|
|
+ struct wil6210_vif *vif,
|
|
|
+ enum nl80211_iftype new_type)
|
|
|
+{
|
|
|
+ int i, ret = 0;
|
|
|
+ struct wireless_dev *wdev;
|
|
|
+ struct iface_combination_params params = {
|
|
|
+ .num_different_channels = 1,
|
|
|
+ };
|
|
|
+ bool check_combos = false;
|
|
|
+
|
|
|
+ for (i = 0; i < wil->max_vifs; i++) {
|
|
|
+ struct wil6210_vif *vif_pos = wil->vifs[i];
|
|
|
+
|
|
|
+ if (vif_pos && vif != vif_pos) {
|
|
|
+ wdev = vif_to_wdev(vif_pos);
|
|
|
+ params.iftype_num[wdev->iftype]++;
|
|
|
+ check_combos = true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (check_combos) {
|
|
|
+ params.iftype_num[new_type]++;
|
|
|
+ ret = cfg80211_check_combinations(wil->wiphy, ¶ms);
|
|
|
+ }
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
static struct wireless_dev *
|
|
|
wil_cfg80211_add_iface(struct wiphy *wiphy, const char *name,
|
|
|
unsigned char name_assign_type,
|
|
@@ -417,51 +473,137 @@ wil_cfg80211_add_iface(struct wiphy *wiphy, const char *name,
|
|
|
struct vif_params *params)
|
|
|
{
|
|
|
struct wil6210_priv *wil = wiphy_to_wil(wiphy);
|
|
|
- struct net_device *ndev = wil_to_ndev(wil);
|
|
|
- struct wireless_dev *p2p_wdev;
|
|
|
+ struct net_device *ndev_main = wil->main_ndev, *ndev;
|
|
|
+ struct wil6210_vif *vif;
|
|
|
+ struct wireless_dev *p2p_wdev, *wdev;
|
|
|
+ int rc;
|
|
|
|
|
|
- wil_dbg_misc(wil, "add_iface\n");
|
|
|
+ wil_dbg_misc(wil, "add_iface, type %d\n", type);
|
|
|
|
|
|
- if (type != NL80211_IFTYPE_P2P_DEVICE) {
|
|
|
- wil_err(wil, "unsupported iftype %d\n", type);
|
|
|
- return ERR_PTR(-EINVAL);
|
|
|
+ /* P2P device is not a real virtual interface, it is a management-only
|
|
|
+ * interface that shares the main interface.
|
|
|
+ * Skip concurrency checks here.
|
|
|
+ */
|
|
|
+ if (type == NL80211_IFTYPE_P2P_DEVICE) {
|
|
|
+ if (wil->p2p_wdev) {
|
|
|
+ wil_err(wil, "P2P_DEVICE interface already created\n");
|
|
|
+ return ERR_PTR(-EINVAL);
|
|
|
+ }
|
|
|
+
|
|
|
+ p2p_wdev = kzalloc(sizeof(*p2p_wdev), GFP_KERNEL);
|
|
|
+ if (!p2p_wdev)
|
|
|
+ return ERR_PTR(-ENOMEM);
|
|
|
+
|
|
|
+ p2p_wdev->iftype = type;
|
|
|
+ p2p_wdev->wiphy = wiphy;
|
|
|
+ /* use our primary ethernet address */
|
|
|
+ ether_addr_copy(p2p_wdev->address, ndev_main->perm_addr);
|
|
|
+
|
|
|
+ wil->p2p_wdev = p2p_wdev;
|
|
|
+
|
|
|
+ return p2p_wdev;
|
|
|
}
|
|
|
|
|
|
- if (wil->p2p_wdev) {
|
|
|
- wil_err(wil, "P2P_DEVICE interface already created\n");
|
|
|
+ if (!wil->wiphy->n_iface_combinations) {
|
|
|
+ wil_err(wil, "virtual interfaces not supported\n");
|
|
|
return ERR_PTR(-EINVAL);
|
|
|
}
|
|
|
|
|
|
- p2p_wdev = kzalloc(sizeof(*p2p_wdev), GFP_KERNEL);
|
|
|
- if (!p2p_wdev)
|
|
|
- return ERR_PTR(-ENOMEM);
|
|
|
+ rc = wil_cfg80211_validate_add_iface(wil, type);
|
|
|
+ if (rc) {
|
|
|
+ wil_err(wil, "iface validation failed, err=%d\n", rc);
|
|
|
+ return ERR_PTR(rc);
|
|
|
+ }
|
|
|
|
|
|
- p2p_wdev->iftype = type;
|
|
|
- p2p_wdev->wiphy = wiphy;
|
|
|
- /* use our primary ethernet address */
|
|
|
- ether_addr_copy(p2p_wdev->address, ndev->perm_addr);
|
|
|
+ vif = wil_vif_alloc(wil, name, name_assign_type, type);
|
|
|
+ if (IS_ERR(vif))
|
|
|
+ return ERR_CAST(vif);
|
|
|
+
|
|
|
+ ndev = vif_to_ndev(vif);
|
|
|
+ ether_addr_copy(ndev->perm_addr, ndev_main->perm_addr);
|
|
|
+ if (is_valid_ether_addr(params->macaddr)) {
|
|
|
+ ether_addr_copy(ndev->dev_addr, params->macaddr);
|
|
|
+ } else {
|
|
|
+ ether_addr_copy(ndev->dev_addr, ndev_main->perm_addr);
|
|
|
+ ndev->dev_addr[0] = (ndev->dev_addr[0] ^ (1 << vif->mid)) |
|
|
|
+ 0x2; /* locally administered */
|
|
|
+ }
|
|
|
+ wdev = vif_to_wdev(vif);
|
|
|
+ ether_addr_copy(wdev->address, ndev->dev_addr);
|
|
|
|
|
|
- wil->p2p_wdev = p2p_wdev;
|
|
|
+ rc = wil_vif_add(wil, vif);
|
|
|
+ if (rc)
|
|
|
+ goto out;
|
|
|
|
|
|
- return p2p_wdev;
|
|
|
+ wil_info(wil, "added VIF, mid %d iftype %d MAC %pM\n",
|
|
|
+ vif->mid, type, wdev->address);
|
|
|
+ return wdev;
|
|
|
+out:
|
|
|
+ wil_vif_free(vif);
|
|
|
+ return ERR_PTR(rc);
|
|
|
+}
|
|
|
+
|
|
|
+int wil_vif_prepare_stop(struct wil6210_vif *vif)
|
|
|
+{
|
|
|
+ struct wil6210_priv *wil = vif_to_wil(vif);
|
|
|
+ struct wireless_dev *wdev = vif_to_wdev(vif);
|
|
|
+ struct net_device *ndev;
|
|
|
+ int rc;
|
|
|
+
|
|
|
+ if (wdev->iftype != NL80211_IFTYPE_AP)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ ndev = vif_to_ndev(vif);
|
|
|
+ if (netif_carrier_ok(ndev)) {
|
|
|
+ rc = wmi_pcp_stop(vif);
|
|
|
+ if (rc) {
|
|
|
+ wil_info(wil, "failed to stop AP, status %d\n",
|
|
|
+ rc);
|
|
|
+ /* continue */
|
|
|
+ }
|
|
|
+ wil_bcast_fini(vif);
|
|
|
+ netif_carrier_off(ndev);
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
}
|
|
|
|
|
|
static int wil_cfg80211_del_iface(struct wiphy *wiphy,
|
|
|
struct wireless_dev *wdev)
|
|
|
{
|
|
|
struct wil6210_priv *wil = wiphy_to_wil(wiphy);
|
|
|
+ struct wil6210_vif *vif = wdev_to_vif(wil, wdev);
|
|
|
+ int rc;
|
|
|
|
|
|
wil_dbg_misc(wil, "del_iface\n");
|
|
|
|
|
|
- if (wdev != wil->p2p_wdev) {
|
|
|
- wil_err(wil, "delete of incorrect interface 0x%p\n", wdev);
|
|
|
+ if (wdev->iftype == NL80211_IFTYPE_P2P_DEVICE) {
|
|
|
+ if (wdev != wil->p2p_wdev) {
|
|
|
+ wil_err(wil, "delete of incorrect interface 0x%p\n",
|
|
|
+ wdev);
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ wil_cfg80211_stop_p2p_device(wiphy, wdev);
|
|
|
+ wil_p2p_wdev_free(wil);
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (vif->mid == 0) {
|
|
|
+ wil_err(wil, "cannot remove the main interface\n");
|
|
|
return -EINVAL;
|
|
|
}
|
|
|
|
|
|
- wil_cfg80211_stop_p2p_device(wiphy, wdev);
|
|
|
- wil_p2p_wdev_free(wil);
|
|
|
+ rc = wil_vif_prepare_stop(vif);
|
|
|
+ if (rc)
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ wil_info(wil, "deleted VIF, mid %d iftype %d MAC %pM\n",
|
|
|
+ vif->mid, wdev->iftype, wdev->address);
|
|
|
|
|
|
- return 0;
|
|
|
+ wil_vif_remove(wil, vif->mid);
|
|
|
+out:
|
|
|
+ return rc;
|
|
|
}
|
|
|
|
|
|
static int wil_cfg80211_change_iface(struct wiphy *wiphy,
|
|
@@ -470,12 +612,26 @@ static int wil_cfg80211_change_iface(struct wiphy *wiphy,
|
|
|
struct vif_params *params)
|
|
|
{
|
|
|
struct wil6210_priv *wil = wiphy_to_wil(wiphy);
|
|
|
- struct wireless_dev *wdev = wil_to_wdev(wil);
|
|
|
+ struct wil6210_vif *vif = ndev_to_vif(ndev);
|
|
|
+ struct wireless_dev *wdev = vif_to_wdev(vif);
|
|
|
int rc;
|
|
|
+ bool fw_reset = false;
|
|
|
|
|
|
wil_dbg_misc(wil, "change_iface: type=%d\n", type);
|
|
|
|
|
|
- if (netif_running(wil_to_ndev(wil)) && !wil_is_recovery_blocked(wil)) {
|
|
|
+ if (wiphy->n_iface_combinations) {
|
|
|
+ rc = wil_cfg80211_validate_change_iface(wil, vif, type);
|
|
|
+ if (rc) {
|
|
|
+ wil_err(wil, "iface validation failed, err=%d\n", rc);
|
|
|
+ return rc;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /* do not reset FW when there are active VIFs,
|
|
|
+ * because it can cause significant disruption
|
|
|
+ */
|
|
|
+ if (!wil_has_other_active_ifaces(wil, ndev, true, false) &&
|
|
|
+ netif_running(ndev) && !wil_is_recovery_blocked(wil)) {
|
|
|
wil_dbg_misc(wil, "interface is up. resetting...\n");
|
|
|
mutex_lock(&wil->mutex);
|
|
|
__wil_down(wil);
|
|
@@ -484,6 +640,7 @@ static int wil_cfg80211_change_iface(struct wiphy *wiphy,
|
|
|
|
|
|
if (rc)
|
|
|
return rc;
|
|
|
+ fw_reset = true;
|
|
|
}
|
|
|
|
|
|
switch (type) {
|
|
@@ -500,8 +657,18 @@ static int wil_cfg80211_change_iface(struct wiphy *wiphy,
|
|
|
return -EOPNOTSUPP;
|
|
|
}
|
|
|
|
|
|
- wdev->iftype = type;
|
|
|
+ if (vif->mid != 0 && wil_has_active_ifaces(wil, true, false)) {
|
|
|
+ if (!fw_reset)
|
|
|
+ wil_vif_prepare_stop(vif);
|
|
|
+ rc = wmi_port_delete(wil, vif->mid);
|
|
|
+ if (rc)
|
|
|
+ return rc;
|
|
|
+ rc = wmi_port_allocate(wil, vif->mid, ndev->dev_addr, type);
|
|
|
+ if (rc)
|
|
|
+ return rc;
|
|
|
+ }
|
|
|
|
|
|
+ wdev->iftype = type;
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
@@ -510,6 +677,7 @@ static int wil_cfg80211_scan(struct wiphy *wiphy,
|
|
|
{
|
|
|
struct wil6210_priv *wil = wiphy_to_wil(wiphy);
|
|
|
struct wireless_dev *wdev = request->wdev;
|
|
|
+ struct wil6210_vif *vif = wdev_to_vif(wil, wdev);
|
|
|
struct {
|
|
|
struct wmi_start_scan_cmd cmd;
|
|
|
u16 chnl[4];
|
|
@@ -537,35 +705,38 @@ static int wil_cfg80211_scan(struct wiphy *wiphy,
|
|
|
|
|
|
mutex_lock(&wil->mutex);
|
|
|
|
|
|
- mutex_lock(&wil->p2p_wdev_mutex);
|
|
|
- if (wil->scan_request || wil->p2p.discovery_started) {
|
|
|
+ mutex_lock(&wil->vif_mutex);
|
|
|
+ if (vif->scan_request || vif->p2p.discovery_started) {
|
|
|
wil_err(wil, "Already scanning\n");
|
|
|
- mutex_unlock(&wil->p2p_wdev_mutex);
|
|
|
+ mutex_unlock(&wil->vif_mutex);
|
|
|
rc = -EAGAIN;
|
|
|
goto out;
|
|
|
}
|
|
|
- mutex_unlock(&wil->p2p_wdev_mutex);
|
|
|
+ mutex_unlock(&wil->vif_mutex);
|
|
|
|
|
|
if (wdev->iftype == NL80211_IFTYPE_P2P_DEVICE) {
|
|
|
- if (!wil->p2p.p2p_dev_started) {
|
|
|
+ if (!wil->p2p_dev_started) {
|
|
|
wil_err(wil, "P2P search requested on stopped P2P device\n");
|
|
|
rc = -EIO;
|
|
|
goto out;
|
|
|
}
|
|
|
/* social scan on P2P_DEVICE is handled as p2p search */
|
|
|
if (wil_p2p_is_social_scan(request)) {
|
|
|
- wil->scan_request = request;
|
|
|
- wil->radio_wdev = wdev;
|
|
|
- rc = wil_p2p_search(wil, request);
|
|
|
+ vif->scan_request = request;
|
|
|
+ if (vif->mid == 0)
|
|
|
+ wil->radio_wdev = wdev;
|
|
|
+ rc = wil_p2p_search(vif, request);
|
|
|
if (rc) {
|
|
|
- wil->radio_wdev = wil_to_wdev(wil);
|
|
|
- wil->scan_request = NULL;
|
|
|
+ if (vif->mid == 0)
|
|
|
+ wil->radio_wdev =
|
|
|
+ wil->main_ndev->ieee80211_ptr;
|
|
|
+ vif->scan_request = NULL;
|
|
|
}
|
|
|
goto out;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- (void)wil_p2p_stop_discovery(wil);
|
|
|
+ (void)wil_p2p_stop_discovery(vif);
|
|
|
|
|
|
wil_dbg_misc(wil, "Start scan_request 0x%p\n", request);
|
|
|
wil_dbg_misc(wil, "SSID count: %d", request->n_ssids);
|
|
@@ -578,18 +749,18 @@ static int wil_cfg80211_scan(struct wiphy *wiphy,
|
|
|
}
|
|
|
|
|
|
if (request->n_ssids)
|
|
|
- rc = wmi_set_ssid(wil, request->ssids[0].ssid_len,
|
|
|
+ rc = wmi_set_ssid(vif, request->ssids[0].ssid_len,
|
|
|
request->ssids[0].ssid);
|
|
|
else
|
|
|
- rc = wmi_set_ssid(wil, 0, NULL);
|
|
|
+ rc = wmi_set_ssid(vif, 0, NULL);
|
|
|
|
|
|
if (rc) {
|
|
|
wil_err(wil, "set SSID for scan request failed: %d\n", rc);
|
|
|
goto out;
|
|
|
}
|
|
|
|
|
|
- wil->scan_request = request;
|
|
|
- mod_timer(&wil->scan_timer, jiffies + WIL6210_SCAN_TO);
|
|
|
+ vif->scan_request = request;
|
|
|
+ mod_timer(&vif->scan_timer, jiffies + WIL6210_SCAN_TO);
|
|
|
|
|
|
memset(&cmd, 0, sizeof(cmd));
|
|
|
cmd.cmd.scan_type = WMI_ACTIVE_SCAN;
|
|
@@ -616,7 +787,8 @@ 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);
|
|
|
+ rc = wmi_set_ie(vif, WMI_FRAME_PROBE_REQ,
|
|
|
+ request->ie_len, request->ie);
|
|
|
if (rc)
|
|
|
goto out_restore;
|
|
|
|
|
@@ -625,15 +797,18 @@ static int wil_cfg80211_scan(struct wiphy *wiphy,
|
|
|
wil_dbg_misc(wil, "active scan with discovery_mode=1\n");
|
|
|
}
|
|
|
|
|
|
- wil->radio_wdev = wdev;
|
|
|
- rc = wmi_send(wil, WMI_START_SCAN_CMDID, &cmd, sizeof(cmd.cmd) +
|
|
|
- cmd.cmd.num_channels * sizeof(cmd.cmd.channel_list[0]));
|
|
|
+ if (vif->mid == 0)
|
|
|
+ wil->radio_wdev = wdev;
|
|
|
+ rc = wmi_send(wil, WMI_START_SCAN_CMDID, vif->mid,
|
|
|
+ &cmd, sizeof(cmd.cmd) +
|
|
|
+ cmd.cmd.num_channels * sizeof(cmd.cmd.channel_list[0]));
|
|
|
|
|
|
out_restore:
|
|
|
if (rc) {
|
|
|
- del_timer_sync(&wil->scan_timer);
|
|
|
- wil->radio_wdev = wil_to_wdev(wil);
|
|
|
- wil->scan_request = NULL;
|
|
|
+ del_timer_sync(&vif->scan_timer);
|
|
|
+ if (vif->mid == 0)
|
|
|
+ wil->radio_wdev = wil->main_ndev->ieee80211_ptr;
|
|
|
+ vif->scan_request = NULL;
|
|
|
}
|
|
|
out:
|
|
|
mutex_unlock(&wil->mutex);
|
|
@@ -644,27 +819,28 @@ static void wil_cfg80211_abort_scan(struct wiphy *wiphy,
|
|
|
struct wireless_dev *wdev)
|
|
|
{
|
|
|
struct wil6210_priv *wil = wiphy_to_wil(wiphy);
|
|
|
+ struct wil6210_vif *vif = wdev_to_vif(wil, wdev);
|
|
|
|
|
|
wil_dbg_misc(wil, "wdev=0x%p iftype=%d\n", wdev, wdev->iftype);
|
|
|
|
|
|
mutex_lock(&wil->mutex);
|
|
|
- mutex_lock(&wil->p2p_wdev_mutex);
|
|
|
+ mutex_lock(&wil->vif_mutex);
|
|
|
|
|
|
- if (!wil->scan_request)
|
|
|
+ if (!vif->scan_request)
|
|
|
goto out;
|
|
|
|
|
|
- if (wdev != wil->scan_request->wdev) {
|
|
|
+ if (wdev != vif->scan_request->wdev) {
|
|
|
wil_dbg_misc(wil, "abort scan was called on the wrong iface\n");
|
|
|
goto out;
|
|
|
}
|
|
|
|
|
|
- if (wil->radio_wdev == wil->p2p_wdev)
|
|
|
+ if (wdev == wil->p2p_wdev && wil->radio_wdev == wil->p2p_wdev)
|
|
|
wil_p2p_stop_radio_operations(wil);
|
|
|
else
|
|
|
- wil_abort_scan(wil, true);
|
|
|
+ wil_abort_scan(vif, true);
|
|
|
|
|
|
out:
|
|
|
- mutex_unlock(&wil->p2p_wdev_mutex);
|
|
|
+ mutex_unlock(&wil->vif_mutex);
|
|
|
mutex_unlock(&wil->mutex);
|
|
|
}
|
|
|
|
|
@@ -715,6 +891,7 @@ static int wil_cfg80211_connect(struct wiphy *wiphy,
|
|
|
struct cfg80211_connect_params *sme)
|
|
|
{
|
|
|
struct wil6210_priv *wil = wiphy_to_wil(wiphy);
|
|
|
+ struct wil6210_vif *vif = ndev_to_vif(ndev);
|
|
|
struct cfg80211_bss *bss;
|
|
|
struct wmi_connect_cmd conn;
|
|
|
const u8 *ssid_eid;
|
|
@@ -723,11 +900,11 @@ static int wil_cfg80211_connect(struct wiphy *wiphy,
|
|
|
int rc = 0;
|
|
|
enum ieee80211_bss_type bss_type = IEEE80211_BSS_TYPE_ESS;
|
|
|
|
|
|
- wil_dbg_misc(wil, "connect\n");
|
|
|
+ wil_dbg_misc(wil, "connect, mid=%d\n", vif->mid);
|
|
|
wil_print_connect_params(wil, sme);
|
|
|
|
|
|
- if (test_bit(wil_status_fwconnecting, wil->status) ||
|
|
|
- test_bit(wil_status_fwconnected, wil->status))
|
|
|
+ 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) {
|
|
@@ -758,18 +935,18 @@ static int wil_cfg80211_connect(struct wiphy *wiphy,
|
|
|
rc = -ENOENT;
|
|
|
goto out;
|
|
|
}
|
|
|
- wil->privacy = sme->privacy;
|
|
|
- wil->pbss = sme->pbss;
|
|
|
+ vif->privacy = sme->privacy;
|
|
|
+ vif->pbss = sme->pbss;
|
|
|
|
|
|
- if (wil->privacy) {
|
|
|
+ if (vif->privacy) {
|
|
|
/* For secure assoc, remove old keys */
|
|
|
- rc = wmi_del_cipher_key(wil, 0, bss->bssid,
|
|
|
+ rc = wmi_del_cipher_key(vif, 0, bss->bssid,
|
|
|
WMI_KEY_USE_PAIRWISE);
|
|
|
if (rc) {
|
|
|
wil_err(wil, "WMI_DELETE_CIPHER_KEY_CMD(PTK) failed\n");
|
|
|
goto out;
|
|
|
}
|
|
|
- rc = wmi_del_cipher_key(wil, 0, bss->bssid,
|
|
|
+ rc = wmi_del_cipher_key(vif, 0, bss->bssid,
|
|
|
WMI_KEY_USE_RX_GROUP);
|
|
|
if (rc) {
|
|
|
wil_err(wil, "WMI_DELETE_CIPHER_KEY_CMD(GTK) failed\n");
|
|
@@ -781,7 +958,7 @@ static int wil_cfg80211_connect(struct wiphy *wiphy,
|
|
|
* elements. Send it also in case it's empty, to erase previously set
|
|
|
* ies in FW.
|
|
|
*/
|
|
|
- rc = wmi_set_ie(wil, WMI_FRAME_ASSOC_REQ, sme->ie_len, sme->ie);
|
|
|
+ rc = wmi_set_ie(vif, WMI_FRAME_ASSOC_REQ, sme->ie_len, sme->ie);
|
|
|
if (rc)
|
|
|
goto out;
|
|
|
|
|
@@ -799,7 +976,7 @@ static int wil_cfg80211_connect(struct wiphy *wiphy,
|
|
|
bss->capability);
|
|
|
goto out;
|
|
|
}
|
|
|
- if (wil->privacy) {
|
|
|
+ if (vif->privacy) {
|
|
|
if (rsn_eid) { /* regular secure connection */
|
|
|
conn.dot11_auth_mode = WMI_AUTH11_SHARED;
|
|
|
conn.auth_mode = WMI_AUTH_WPA2_PSK;
|
|
@@ -831,18 +1008,19 @@ static int wil_cfg80211_connect(struct wiphy *wiphy,
|
|
|
ether_addr_copy(conn.bssid, bss->bssid);
|
|
|
ether_addr_copy(conn.dst_mac, bss->bssid);
|
|
|
|
|
|
- set_bit(wil_status_fwconnecting, wil->status);
|
|
|
+ set_bit(wil_vif_fwconnecting, vif->status);
|
|
|
|
|
|
- rc = wmi_send(wil, WMI_CONNECT_CMDID, &conn, sizeof(conn));
|
|
|
+ rc = wmi_send(wil, WMI_CONNECT_CMDID, vif->mid, &conn, sizeof(conn));
|
|
|
if (rc == 0) {
|
|
|
netif_carrier_on(ndev);
|
|
|
- wil6210_bus_request(wil, WIL_MAX_BUS_REQUEST_KBPS);
|
|
|
- wil->bss = bss;
|
|
|
+ if (!wil_has_other_active_ifaces(wil, ndev, false, true))
|
|
|
+ wil6210_bus_request(wil, WIL_MAX_BUS_REQUEST_KBPS);
|
|
|
+ vif->bss = bss;
|
|
|
/* Connect can take lots of time */
|
|
|
- mod_timer(&wil->connect_timer,
|
|
|
+ mod_timer(&vif->connect_timer,
|
|
|
jiffies + msecs_to_jiffies(5000));
|
|
|
} else {
|
|
|
- clear_bit(wil_status_fwconnecting, wil->status);
|
|
|
+ clear_bit(wil_vif_fwconnecting, vif->status);
|
|
|
}
|
|
|
|
|
|
out:
|
|
@@ -857,17 +1035,19 @@ static int wil_cfg80211_disconnect(struct wiphy *wiphy,
|
|
|
{
|
|
|
int rc;
|
|
|
struct wil6210_priv *wil = wiphy_to_wil(wiphy);
|
|
|
+ struct wil6210_vif *vif = ndev_to_vif(ndev);
|
|
|
|
|
|
- wil_dbg_misc(wil, "disconnect: reason=%d\n", reason_code);
|
|
|
+ wil_dbg_misc(wil, "disconnect: reason=%d, mid=%d\n",
|
|
|
+ reason_code, vif->mid);
|
|
|
|
|
|
- if (!(test_bit(wil_status_fwconnecting, wil->status) ||
|
|
|
- test_bit(wil_status_fwconnected, wil->status))) {
|
|
|
+ if (!(test_bit(wil_vif_fwconnecting, vif->status) ||
|
|
|
+ test_bit(wil_vif_fwconnected, vif->status))) {
|
|
|
wil_err(wil, "Disconnect was called while disconnected\n");
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
- wil->locally_generated_disc = true;
|
|
|
- rc = wmi_call(wil, WMI_DISCONNECT_CMDID, NULL, 0,
|
|
|
+ vif->locally_generated_disc = true;
|
|
|
+ rc = wmi_call(wil, WMI_DISCONNECT_CMDID, vif->mid, NULL, 0,
|
|
|
WMI_DISCONNECT_EVENTID, NULL, 0,
|
|
|
WIL6210_DISCONNECT_TO_MS);
|
|
|
if (rc)
|
|
@@ -903,6 +1083,7 @@ int wil_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
|
|
|
const u8 *buf = params->buf;
|
|
|
size_t len = params->len, total;
|
|
|
struct wil6210_priv *wil = wiphy_to_wil(wiphy);
|
|
|
+ struct wil6210_vif *vif = wdev_to_vif(wil, wdev);
|
|
|
int rc;
|
|
|
bool tx_status = false;
|
|
|
struct ieee80211_mgmt *mgmt_frame = (void *)buf;
|
|
@@ -919,7 +1100,7 @@ int wil_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
|
|
|
* different from currently "listened" channel and fail if it is.
|
|
|
*/
|
|
|
|
|
|
- wil_dbg_misc(wil, "mgmt_tx\n");
|
|
|
+ wil_dbg_misc(wil, "mgmt_tx mid %d\n", vif->mid);
|
|
|
wil_hex_dump_misc("mgmt tx frame ", DUMP_PREFIX_OFFSET, 16, 1, buf,
|
|
|
len, true);
|
|
|
|
|
@@ -940,7 +1121,7 @@ int wil_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
|
|
|
cmd->len = cpu_to_le16(len);
|
|
|
memcpy(cmd->payload, buf, len);
|
|
|
|
|
|
- rc = wmi_call(wil, WMI_SW_TX_REQ_CMDID, cmd, total,
|
|
|
+ rc = wmi_call(wil, WMI_SW_TX_REQ_CMDID, vif->mid, cmd, total,
|
|
|
WMI_SW_TX_COMPLETE_EVENTID, &evt, sizeof(evt), 2000);
|
|
|
if (rc == 0)
|
|
|
tx_status = !evt.evt.status;
|
|
@@ -962,10 +1143,10 @@ static int wil_cfg80211_set_channel(struct wiphy *wiphy,
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-static enum wmi_key_usage wil_detect_key_usage(struct wil6210_priv *wil,
|
|
|
+static enum wmi_key_usage wil_detect_key_usage(struct wireless_dev *wdev,
|
|
|
bool pairwise)
|
|
|
{
|
|
|
- struct wireless_dev *wdev = wil_to_wdev(wil);
|
|
|
+ struct wil6210_priv *wil = wdev_to_wil(wdev);
|
|
|
enum wmi_key_usage rc;
|
|
|
|
|
|
if (pairwise) {
|
|
@@ -993,7 +1174,7 @@ static enum wmi_key_usage wil_detect_key_usage(struct wil6210_priv *wil,
|
|
|
}
|
|
|
|
|
|
static struct wil_sta_info *
|
|
|
-wil_find_sta_by_key_usage(struct wil6210_priv *wil,
|
|
|
+wil_find_sta_by_key_usage(struct wil6210_priv *wil, u8 mid,
|
|
|
enum wmi_key_usage key_usage, const u8 *mac_addr)
|
|
|
{
|
|
|
int cid = -EINVAL;
|
|
@@ -1003,9 +1184,9 @@ wil_find_sta_by_key_usage(struct wil6210_priv *wil,
|
|
|
|
|
|
/* supplicant provides Rx group key in STA mode with NULL MAC address */
|
|
|
if (mac_addr)
|
|
|
- cid = wil_find_cid(wil, mac_addr);
|
|
|
+ cid = wil_find_cid(wil, mid, mac_addr);
|
|
|
else if (key_usage == WMI_KEY_USE_RX_GROUP)
|
|
|
- cid = wil_find_cid_by_idx(wil, 0);
|
|
|
+ cid = wil_find_cid_by_idx(wil, mid, 0);
|
|
|
if (cid < 0) {
|
|
|
wil_err(wil, "No CID for %pM %s\n", mac_addr,
|
|
|
key_usage_str[key_usage]);
|
|
@@ -1082,9 +1263,12 @@ static int wil_cfg80211_add_key(struct wiphy *wiphy,
|
|
|
struct key_params *params)
|
|
|
{
|
|
|
int rc;
|
|
|
+ struct wil6210_vif *vif = ndev_to_vif(ndev);
|
|
|
struct wil6210_priv *wil = wiphy_to_wil(wiphy);
|
|
|
- enum wmi_key_usage key_usage = wil_detect_key_usage(wil, pairwise);
|
|
|
- struct wil_sta_info *cs = wil_find_sta_by_key_usage(wil, key_usage,
|
|
|
+ struct wireless_dev *wdev = vif_to_wdev(vif);
|
|
|
+ enum wmi_key_usage key_usage = wil_detect_key_usage(wdev, pairwise);
|
|
|
+ struct wil_sta_info *cs = wil_find_sta_by_key_usage(wil, vif->mid,
|
|
|
+ key_usage,
|
|
|
mac_addr);
|
|
|
|
|
|
if (!params) {
|
|
@@ -1114,7 +1298,7 @@ static int wil_cfg80211_add_key(struct wiphy *wiphy,
|
|
|
return -EINVAL;
|
|
|
}
|
|
|
|
|
|
- rc = wmi_add_cipher_key(wil, key_index, mac_addr, params->key_len,
|
|
|
+ rc = wmi_add_cipher_key(vif, key_index, mac_addr, params->key_len,
|
|
|
params->key, key_usage);
|
|
|
if (!rc)
|
|
|
wil_set_crypto_rx(key_index, key_usage, cs, params);
|
|
@@ -1127,9 +1311,12 @@ static int wil_cfg80211_del_key(struct wiphy *wiphy,
|
|
|
u8 key_index, bool pairwise,
|
|
|
const u8 *mac_addr)
|
|
|
{
|
|
|
+ struct wil6210_vif *vif = ndev_to_vif(ndev);
|
|
|
struct wil6210_priv *wil = wiphy_to_wil(wiphy);
|
|
|
- enum wmi_key_usage key_usage = wil_detect_key_usage(wil, pairwise);
|
|
|
- struct wil_sta_info *cs = wil_find_sta_by_key_usage(wil, key_usage,
|
|
|
+ struct wireless_dev *wdev = vif_to_wdev(vif);
|
|
|
+ enum wmi_key_usage key_usage = wil_detect_key_usage(wdev, pairwise);
|
|
|
+ struct wil_sta_info *cs = wil_find_sta_by_key_usage(wil, vif->mid,
|
|
|
+ key_usage,
|
|
|
mac_addr);
|
|
|
|
|
|
wil_dbg_misc(wil, "del_key: %pM %s[%d]\n", mac_addr,
|
|
@@ -1142,7 +1329,7 @@ static int wil_cfg80211_del_key(struct wiphy *wiphy,
|
|
|
if (!IS_ERR_OR_NULL(cs))
|
|
|
wil_del_rx_key(key_index, key_usage, cs);
|
|
|
|
|
|
- return wmi_del_cipher_key(wil, key_index, mac_addr, key_usage);
|
|
|
+ return wmi_del_cipher_key(vif, key_index, mac_addr, key_usage);
|
|
|
}
|
|
|
|
|
|
/* Need to be present or wiphy_new() will WARN */
|
|
@@ -1179,10 +1366,11 @@ static int wil_cancel_remain_on_channel(struct wiphy *wiphy,
|
|
|
u64 cookie)
|
|
|
{
|
|
|
struct wil6210_priv *wil = wiphy_to_wil(wiphy);
|
|
|
+ struct wil6210_vif *vif = wdev_to_vif(wil, wdev);
|
|
|
|
|
|
wil_dbg_misc(wil, "cancel_remain_on_channel\n");
|
|
|
|
|
|
- return wil_p2p_cancel_listen(wil, cookie);
|
|
|
+ return wil_p2p_cancel_listen(vif, cookie);
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -1275,11 +1463,10 @@ 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 wiphy *wiphy,
|
|
|
+static int _wil_cfg80211_set_ies(struct wil6210_vif *vif,
|
|
|
struct cfg80211_beacon_data *bcon)
|
|
|
{
|
|
|
int rc;
|
|
|
- struct wil6210_priv *wil = wiphy_to_wil(wiphy);
|
|
|
u16 len = 0, proberesp_len = 0;
|
|
|
u8 *ies = NULL, *proberesp = NULL;
|
|
|
|
|
@@ -1300,20 +1487,21 @@ static int _wil_cfg80211_set_ies(struct wiphy *wiphy,
|
|
|
if (rc)
|
|
|
goto out;
|
|
|
|
|
|
- rc = wmi_set_ie(wil, WMI_FRAME_PROBE_RESP, len, ies);
|
|
|
+ rc = wmi_set_ie(vif, WMI_FRAME_PROBE_RESP, len, ies);
|
|
|
if (rc)
|
|
|
goto out;
|
|
|
|
|
|
if (bcon->assocresp_ies)
|
|
|
- rc = wmi_set_ie(wil, WMI_FRAME_ASSOC_RESP,
|
|
|
+ rc = wmi_set_ie(vif, WMI_FRAME_ASSOC_RESP,
|
|
|
bcon->assocresp_ies_len, bcon->assocresp_ies);
|
|
|
else
|
|
|
- rc = wmi_set_ie(wil, WMI_FRAME_ASSOC_RESP, len, ies);
|
|
|
+ rc = wmi_set_ie(vif, WMI_FRAME_ASSOC_RESP, len, ies);
|
|
|
#if 0 /* to use beacon IE's, remove this #if 0 */
|
|
|
if (rc)
|
|
|
goto out;
|
|
|
|
|
|
- rc = wmi_set_ie(wil, WMI_FRAME_BEACON, bcon->tail_len, bcon->tail);
|
|
|
+ rc = wmi_set_ie(vif, WMI_FRAME_BEACON,
|
|
|
+ bcon->tail_len, bcon->tail);
|
|
|
#endif
|
|
|
out:
|
|
|
kfree(ies);
|
|
@@ -1328,6 +1516,7 @@ static int _wil_cfg80211_start_ap(struct wiphy *wiphy,
|
|
|
u8 hidden_ssid, u32 pbss)
|
|
|
{
|
|
|
struct wil6210_priv *wil = wiphy_to_wil(wiphy);
|
|
|
+ struct wil6210_vif *vif = ndev_to_vif(ndev);
|
|
|
int rc;
|
|
|
struct wireless_dev *wdev = ndev->ieee80211_ptr;
|
|
|
u8 wmi_nettype = wil_iftype_nl2wmi(wdev->iftype);
|
|
@@ -1336,7 +1525,7 @@ static int _wil_cfg80211_start_ap(struct wiphy *wiphy,
|
|
|
if (pbss)
|
|
|
wmi_nettype = WMI_NETTYPE_P2P;
|
|
|
|
|
|
- wil_dbg_misc(wil, "start_ap: is_go=%d\n", is_go);
|
|
|
+ wil_dbg_misc(wil, "start_ap: mid=%d, is_go=%d\n", vif->mid, is_go);
|
|
|
if (is_go && !pbss) {
|
|
|
wil_err(wil, "P2P GO must be in PBSS\n");
|
|
|
return -ENOTSUPP;
|
|
@@ -1346,42 +1535,46 @@ static int _wil_cfg80211_start_ap(struct wiphy *wiphy,
|
|
|
|
|
|
mutex_lock(&wil->mutex);
|
|
|
|
|
|
- __wil_down(wil);
|
|
|
- rc = __wil_up(wil);
|
|
|
- if (rc)
|
|
|
- goto out;
|
|
|
+ if (!wil_has_other_active_ifaces(wil, ndev, true, false)) {
|
|
|
+ __wil_down(wil);
|
|
|
+ rc = __wil_up(wil);
|
|
|
+ if (rc)
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
|
|
|
- rc = wmi_set_ssid(wil, ssid_len, ssid);
|
|
|
+ rc = wmi_set_ssid(vif, ssid_len, ssid);
|
|
|
if (rc)
|
|
|
goto out;
|
|
|
|
|
|
- rc = _wil_cfg80211_set_ies(wiphy, bcon);
|
|
|
+ rc = _wil_cfg80211_set_ies(vif, bcon);
|
|
|
if (rc)
|
|
|
goto out;
|
|
|
|
|
|
- wil->privacy = privacy;
|
|
|
- wil->channel = chan;
|
|
|
- wil->hidden_ssid = hidden_ssid;
|
|
|
- wil->pbss = pbss;
|
|
|
+ vif->privacy = privacy;
|
|
|
+ vif->channel = chan;
|
|
|
+ vif->hidden_ssid = hidden_ssid;
|
|
|
+ vif->pbss = pbss;
|
|
|
|
|
|
netif_carrier_on(ndev);
|
|
|
- wil6210_bus_request(wil, WIL_MAX_BUS_REQUEST_KBPS);
|
|
|
+ if (!wil_has_other_active_ifaces(wil, ndev, false, true))
|
|
|
+ wil6210_bus_request(wil, WIL_MAX_BUS_REQUEST_KBPS);
|
|
|
|
|
|
- rc = wmi_pcp_start(wil, bi, wmi_nettype, chan, hidden_ssid, is_go);
|
|
|
+ rc = wmi_pcp_start(vif, bi, wmi_nettype, chan, hidden_ssid, is_go);
|
|
|
if (rc)
|
|
|
goto err_pcp_start;
|
|
|
|
|
|
- rc = wil_bcast_init(wil);
|
|
|
+ rc = wil_bcast_init(vif);
|
|
|
if (rc)
|
|
|
goto err_bcast;
|
|
|
|
|
|
goto out; /* success */
|
|
|
|
|
|
err_bcast:
|
|
|
- wmi_pcp_stop(wil);
|
|
|
+ wmi_pcp_stop(vif);
|
|
|
err_pcp_start:
|
|
|
netif_carrier_off(ndev);
|
|
|
- wil6210_bus_request(wil, WIL_DEFAULT_BUS_REQUEST_KBPS);
|
|
|
+ if (!wil_has_other_active_ifaces(wil, ndev, false, true))
|
|
|
+ wil6210_bus_request(wil, WIL_DEFAULT_BUS_REQUEST_KBPS);
|
|
|
out:
|
|
|
mutex_unlock(&wil->mutex);
|
|
|
return rc;
|
|
@@ -1392,10 +1585,11 @@ static int wil_cfg80211_change_beacon(struct wiphy *wiphy,
|
|
|
struct cfg80211_beacon_data *bcon)
|
|
|
{
|
|
|
struct wil6210_priv *wil = wiphy_to_wil(wiphy);
|
|
|
+ struct wil6210_vif *vif = ndev_to_vif(ndev);
|
|
|
int rc;
|
|
|
u32 privacy = 0;
|
|
|
|
|
|
- wil_dbg_misc(wil, "change_beacon\n");
|
|
|
+ wil_dbg_misc(wil, "change_beacon, mid=%d\n", vif->mid);
|
|
|
wil_print_bcon_data(bcon);
|
|
|
|
|
|
if (bcon->tail &&
|
|
@@ -1404,20 +1598,20 @@ static int wil_cfg80211_change_beacon(struct wiphy *wiphy,
|
|
|
privacy = 1;
|
|
|
|
|
|
/* in case privacy has changed, need to restart the AP */
|
|
|
- if (wil->privacy != privacy) {
|
|
|
+ if (vif->privacy != privacy) {
|
|
|
struct wireless_dev *wdev = ndev->ieee80211_ptr;
|
|
|
|
|
|
wil_dbg_misc(wil, "privacy changed %d=>%d. Restarting AP\n",
|
|
|
- wil->privacy, privacy);
|
|
|
+ vif->privacy, privacy);
|
|
|
|
|
|
rc = _wil_cfg80211_start_ap(wiphy, ndev, wdev->ssid,
|
|
|
wdev->ssid_len, privacy,
|
|
|
wdev->beacon_interval,
|
|
|
- wil->channel, bcon,
|
|
|
- wil->hidden_ssid,
|
|
|
- wil->pbss);
|
|
|
+ vif->channel, bcon,
|
|
|
+ vif->hidden_ssid,
|
|
|
+ vif->pbss);
|
|
|
} else {
|
|
|
- rc = _wil_cfg80211_set_ies(wiphy, bcon);
|
|
|
+ rc = _wil_cfg80211_set_ies(vif, bcon);
|
|
|
}
|
|
|
|
|
|
return rc;
|
|
@@ -1484,20 +1678,27 @@ static int wil_cfg80211_stop_ap(struct wiphy *wiphy,
|
|
|
struct net_device *ndev)
|
|
|
{
|
|
|
struct wil6210_priv *wil = wiphy_to_wil(wiphy);
|
|
|
+ struct wil6210_vif *vif = ndev_to_vif(ndev);
|
|
|
+ bool last;
|
|
|
|
|
|
- wil_dbg_misc(wil, "stop_ap\n");
|
|
|
+ wil_dbg_misc(wil, "stop_ap, mid=%d\n", vif->mid);
|
|
|
|
|
|
netif_carrier_off(ndev);
|
|
|
- wil6210_bus_request(wil, WIL_DEFAULT_BUS_REQUEST_KBPS);
|
|
|
- wil_set_recovery_state(wil, fw_recovery_idle);
|
|
|
-
|
|
|
- set_bit(wil_status_resetting, wil->status);
|
|
|
+ last = !wil_has_other_active_ifaces(wil, ndev, false, true);
|
|
|
+ if (last) {
|
|
|
+ wil6210_bus_request(wil, WIL_DEFAULT_BUS_REQUEST_KBPS);
|
|
|
+ wil_set_recovery_state(wil, fw_recovery_idle);
|
|
|
+ set_bit(wil_status_resetting, wil->status);
|
|
|
+ }
|
|
|
|
|
|
mutex_lock(&wil->mutex);
|
|
|
|
|
|
- wmi_pcp_stop(wil);
|
|
|
+ wmi_pcp_stop(vif);
|
|
|
|
|
|
- __wil_down(wil);
|
|
|
+ if (last)
|
|
|
+ __wil_down(wil);
|
|
|
+ else
|
|
|
+ wil_bcast_fini(vif);
|
|
|
|
|
|
mutex_unlock(&wil->mutex);
|
|
|
|
|
@@ -1509,9 +1710,11 @@ static int wil_cfg80211_add_station(struct wiphy *wiphy,
|
|
|
const u8 *mac,
|
|
|
struct station_parameters *params)
|
|
|
{
|
|
|
+ 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\n", mac, params->aid);
|
|
|
+ wil_dbg_misc(wil, "add station %pM aid %d mid %d\n",
|
|
|
+ mac, params->aid, vif->mid);
|
|
|
|
|
|
if (!disable_ap_sme) {
|
|
|
wil_err(wil, "not supported with AP SME enabled\n");
|
|
@@ -1523,20 +1726,21 @@ static int wil_cfg80211_add_station(struct wiphy *wiphy,
|
|
|
return -EINVAL;
|
|
|
}
|
|
|
|
|
|
- return wmi_new_sta(wil, mac, params->aid);
|
|
|
+ return wmi_new_sta(vif, mac, params->aid);
|
|
|
}
|
|
|
|
|
|
static int wil_cfg80211_del_station(struct wiphy *wiphy,
|
|
|
struct net_device *dev,
|
|
|
struct station_del_parameters *params)
|
|
|
{
|
|
|
+ struct wil6210_vif *vif = ndev_to_vif(dev);
|
|
|
struct wil6210_priv *wil = wiphy_to_wil(wiphy);
|
|
|
|
|
|
- wil_dbg_misc(wil, "del_station: %pM, reason=%d\n", params->mac,
|
|
|
- params->reason_code);
|
|
|
+ wil_dbg_misc(wil, "del_station: %pM, reason=%d mid=%d\n",
|
|
|
+ params->mac, params->reason_code, vif->mid);
|
|
|
|
|
|
mutex_lock(&wil->mutex);
|
|
|
- wil6210_disconnect(wil, params->mac, params->reason_code, false);
|
|
|
+ wil6210_disconnect(vif, params->mac, params->reason_code, false);
|
|
|
mutex_unlock(&wil->mutex);
|
|
|
|
|
|
return 0;
|
|
@@ -1547,13 +1751,15 @@ static int wil_cfg80211_change_station(struct wiphy *wiphy,
|
|
|
const u8 *mac,
|
|
|
struct station_parameters *params)
|
|
|
{
|
|
|
+ struct wil6210_vif *vif = ndev_to_vif(dev);
|
|
|
struct wil6210_priv *wil = wiphy_to_wil(wiphy);
|
|
|
int authorize;
|
|
|
int cid, i;
|
|
|
struct vring_tx_data *txdata = NULL;
|
|
|
|
|
|
- wil_dbg_misc(wil, "change station %pM mask 0x%x set 0x%x\n", mac,
|
|
|
- params->sta_flags_mask, params->sta_flags_set);
|
|
|
+ wil_dbg_misc(wil, "change station %pM mask 0x%x set 0x%x mid %d\n",
|
|
|
+ mac, params->sta_flags_mask, params->sta_flags_set,
|
|
|
+ vif->mid);
|
|
|
|
|
|
if (!disable_ap_sme) {
|
|
|
wil_dbg_misc(wil, "not supported with AP SME enabled\n");
|
|
@@ -1563,7 +1769,7 @@ static int wil_cfg80211_change_station(struct wiphy *wiphy,
|
|
|
if (!(params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED)))
|
|
|
return 0;
|
|
|
|
|
|
- cid = wil_find_cid(wil, mac);
|
|
|
+ cid = wil_find_cid(wil, vif->mid, mac);
|
|
|
if (cid < 0) {
|
|
|
wil_err(wil, "station not found\n");
|
|
|
return -ENOLINK;
|
|
@@ -1590,9 +1796,10 @@ static int wil_cfg80211_change_station(struct wiphy *wiphy,
|
|
|
|
|
|
/* probe_client handling */
|
|
|
static void wil_probe_client_handle(struct wil6210_priv *wil,
|
|
|
+ struct wil6210_vif *vif,
|
|
|
struct wil_probe_client_req *req)
|
|
|
{
|
|
|
- struct net_device *ndev = wil_to_ndev(wil);
|
|
|
+ struct net_device *ndev = vif_to_ndev(vif);
|
|
|
struct wil_sta_info *sta = &wil->sta[req->cid];
|
|
|
/* assume STA is alive if it is still connected,
|
|
|
* else FW will disconnect it
|
|
@@ -1603,51 +1810,53 @@ static void wil_probe_client_handle(struct wil6210_priv *wil,
|
|
|
0, false, GFP_KERNEL);
|
|
|
}
|
|
|
|
|
|
-static struct list_head *next_probe_client(struct wil6210_priv *wil)
|
|
|
+static struct list_head *next_probe_client(struct wil6210_vif *vif)
|
|
|
{
|
|
|
struct list_head *ret = NULL;
|
|
|
|
|
|
- mutex_lock(&wil->probe_client_mutex);
|
|
|
+ mutex_lock(&vif->probe_client_mutex);
|
|
|
|
|
|
- if (!list_empty(&wil->probe_client_pending)) {
|
|
|
- ret = wil->probe_client_pending.next;
|
|
|
+ if (!list_empty(&vif->probe_client_pending)) {
|
|
|
+ ret = vif->probe_client_pending.next;
|
|
|
list_del(ret);
|
|
|
}
|
|
|
|
|
|
- mutex_unlock(&wil->probe_client_mutex);
|
|
|
+ mutex_unlock(&vif->probe_client_mutex);
|
|
|
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
void wil_probe_client_worker(struct work_struct *work)
|
|
|
{
|
|
|
- struct wil6210_priv *wil = container_of(work, struct wil6210_priv,
|
|
|
- probe_client_worker);
|
|
|
+ struct wil6210_vif *vif = container_of(work, struct wil6210_vif,
|
|
|
+ probe_client_worker);
|
|
|
+ struct wil6210_priv *wil = vif_to_wil(vif);
|
|
|
struct wil_probe_client_req *req;
|
|
|
struct list_head *lh;
|
|
|
|
|
|
- while ((lh = next_probe_client(wil)) != NULL) {
|
|
|
+ while ((lh = next_probe_client(vif)) != NULL) {
|
|
|
req = list_entry(lh, struct wil_probe_client_req, list);
|
|
|
|
|
|
- wil_probe_client_handle(wil, req);
|
|
|
+ wil_probe_client_handle(wil, vif, req);
|
|
|
kfree(req);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-void wil_probe_client_flush(struct wil6210_priv *wil)
|
|
|
+void wil_probe_client_flush(struct wil6210_vif *vif)
|
|
|
{
|
|
|
struct wil_probe_client_req *req, *t;
|
|
|
+ struct wil6210_priv *wil = vif_to_wil(vif);
|
|
|
|
|
|
wil_dbg_misc(wil, "probe_client_flush\n");
|
|
|
|
|
|
- mutex_lock(&wil->probe_client_mutex);
|
|
|
+ mutex_lock(&vif->probe_client_mutex);
|
|
|
|
|
|
- list_for_each_entry_safe(req, t, &wil->probe_client_pending, list) {
|
|
|
+ list_for_each_entry_safe(req, t, &vif->probe_client_pending, list) {
|
|
|
list_del(&req->list);
|
|
|
kfree(req);
|
|
|
}
|
|
|
|
|
|
- mutex_unlock(&wil->probe_client_mutex);
|
|
|
+ mutex_unlock(&vif->probe_client_mutex);
|
|
|
}
|
|
|
|
|
|
static int wil_cfg80211_probe_client(struct wiphy *wiphy,
|
|
@@ -1655,10 +1864,12 @@ static int wil_cfg80211_probe_client(struct wiphy *wiphy,
|
|
|
const u8 *peer, u64 *cookie)
|
|
|
{
|
|
|
struct wil6210_priv *wil = wiphy_to_wil(wiphy);
|
|
|
+ struct wil6210_vif *vif = ndev_to_vif(dev);
|
|
|
struct wil_probe_client_req *req;
|
|
|
- int cid = wil_find_cid(wil, peer);
|
|
|
+ int cid = wil_find_cid(wil, vif->mid, peer);
|
|
|
|
|
|
- wil_dbg_misc(wil, "probe_client: %pM => CID %d\n", peer, cid);
|
|
|
+ wil_dbg_misc(wil, "probe_client: %pM => CID %d MID %d\n",
|
|
|
+ peer, cid, vif->mid);
|
|
|
|
|
|
if (cid < 0)
|
|
|
return -ENOLINK;
|
|
@@ -1670,12 +1881,12 @@ static int wil_cfg80211_probe_client(struct wiphy *wiphy,
|
|
|
req->cid = cid;
|
|
|
req->cookie = cid;
|
|
|
|
|
|
- mutex_lock(&wil->probe_client_mutex);
|
|
|
- list_add_tail(&req->list, &wil->probe_client_pending);
|
|
|
- mutex_unlock(&wil->probe_client_mutex);
|
|
|
+ mutex_lock(&vif->probe_client_mutex);
|
|
|
+ list_add_tail(&req->list, &vif->probe_client_pending);
|
|
|
+ mutex_unlock(&vif->probe_client_mutex);
|
|
|
|
|
|
*cookie = req->cookie;
|
|
|
- queue_work(wil->wq_service, &wil->probe_client_worker);
|
|
|
+ queue_work(wil->wq_service, &vif->probe_client_worker);
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
@@ -1684,11 +1895,12 @@ static int wil_cfg80211_change_bss(struct wiphy *wiphy,
|
|
|
struct bss_parameters *params)
|
|
|
{
|
|
|
struct wil6210_priv *wil = wiphy_to_wil(wiphy);
|
|
|
+ struct wil6210_vif *vif = ndev_to_vif(dev);
|
|
|
|
|
|
if (params->ap_isolate >= 0) {
|
|
|
- wil_dbg_misc(wil, "change_bss: ap_isolate %d => %d\n",
|
|
|
- wil->ap_isolate, params->ap_isolate);
|
|
|
- wil->ap_isolate = params->ap_isolate;
|
|
|
+ wil_dbg_misc(wil, "change_bss: ap_isolate MID %d, %d => %d\n",
|
|
|
+ vif->mid, vif->ap_isolate, params->ap_isolate);
|
|
|
+ vif->ap_isolate = params->ap_isolate;
|
|
|
}
|
|
|
|
|
|
return 0;
|
|
@@ -1732,10 +1944,10 @@ static int wil_cfg80211_suspend(struct wiphy *wiphy,
|
|
|
wil_dbg_pm(wil, "suspending\n");
|
|
|
|
|
|
mutex_lock(&wil->mutex);
|
|
|
- mutex_lock(&wil->p2p_wdev_mutex);
|
|
|
+ mutex_lock(&wil->vif_mutex);
|
|
|
wil_p2p_stop_radio_operations(wil);
|
|
|
- wil_abort_scan(wil, true);
|
|
|
- mutex_unlock(&wil->p2p_wdev_mutex);
|
|
|
+ wil_abort_scan_all_vifs(wil, true);
|
|
|
+ mutex_unlock(&wil->vif_mutex);
|
|
|
mutex_unlock(&wil->mutex);
|
|
|
|
|
|
out:
|
|
@@ -1757,8 +1969,12 @@ wil_cfg80211_sched_scan_start(struct wiphy *wiphy,
|
|
|
struct cfg80211_sched_scan_request *request)
|
|
|
{
|
|
|
struct wil6210_priv *wil = wiphy_to_wil(wiphy);
|
|
|
+ struct wil6210_vif *vif = ndev_to_vif(dev);
|
|
|
int i, rc;
|
|
|
|
|
|
+ if (vif->mid != 0)
|
|
|
+ return -EOPNOTSUPP;
|
|
|
+
|
|
|
wil_dbg_misc(wil,
|
|
|
"sched scan start: n_ssids %d, ie_len %zu, flags 0x%x\n",
|
|
|
request->n_ssids, request->ie_len, request->flags);
|
|
@@ -1792,7 +2008,8 @@ wil_cfg80211_sched_scan_start(struct wiphy *wiphy,
|
|
|
i, sp->interval, sp->iterations);
|
|
|
}
|
|
|
|
|
|
- rc = wmi_set_ie(wil, WMI_FRAME_PROBE_REQ, request->ie_len, request->ie);
|
|
|
+ rc = wmi_set_ie(vif, WMI_FRAME_PROBE_REQ,
|
|
|
+ request->ie_len, request->ie);
|
|
|
if (rc)
|
|
|
return rc;
|
|
|
return wmi_start_sched_scan(wil, request);
|
|
@@ -1803,8 +2020,12 @@ wil_cfg80211_sched_scan_stop(struct wiphy *wiphy, struct net_device *dev,
|
|
|
u64 reqid)
|
|
|
{
|
|
|
struct wil6210_priv *wil = wiphy_to_wil(wiphy);
|
|
|
+ struct wil6210_vif *vif = ndev_to_vif(dev);
|
|
|
int rc;
|
|
|
|
|
|
+ if (vif->mid != 0)
|
|
|
+ return -EOPNOTSUPP;
|
|
|
+
|
|
|
rc = wmi_stop_sched_scan(wil);
|
|
|
/* device would return error if it thinks PNO is already stopped.
|
|
|
* ignore the return code so user space and driver gets back in-sync
|
|
@@ -1893,57 +2114,132 @@ static void wil_wiphy_init(struct wiphy *wiphy)
|
|
|
#endif
|
|
|
}
|
|
|
|
|
|
-struct wireless_dev *wil_cfg80211_init(struct device *dev)
|
|
|
+int wil_cfg80211_iface_combinations_from_fw(
|
|
|
+ struct wil6210_priv *wil, const struct wil_fw_record_concurrency *conc)
|
|
|
{
|
|
|
- int rc = 0;
|
|
|
- struct wireless_dev *wdev;
|
|
|
+ struct wiphy *wiphy = wil_to_wiphy(wil);
|
|
|
+ u32 total_limits = 0;
|
|
|
+ u16 n_combos;
|
|
|
+ const struct wil_fw_concurrency_combo *combo;
|
|
|
+ const struct wil_fw_concurrency_limit *limit;
|
|
|
+ struct ieee80211_iface_combination *iface_combinations;
|
|
|
+ struct ieee80211_iface_limit *iface_limit;
|
|
|
+ int i, j;
|
|
|
+
|
|
|
+ if (wiphy->iface_combinations) {
|
|
|
+ wil_dbg_misc(wil, "iface_combinations already set, skipping\n");
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
|
|
|
- dev_dbg(dev, "%s()\n", __func__);
|
|
|
+ combo = conc->combos;
|
|
|
+ n_combos = le16_to_cpu(conc->n_combos);
|
|
|
+ for (i = 0; i < n_combos; i++) {
|
|
|
+ total_limits += combo->n_limits;
|
|
|
+ limit = combo->limits + combo->n_limits;
|
|
|
+ combo = (struct wil_fw_concurrency_combo *)limit;
|
|
|
+ }
|
|
|
|
|
|
- wdev = kzalloc(sizeof(*wdev), GFP_KERNEL);
|
|
|
- if (!wdev)
|
|
|
- return ERR_PTR(-ENOMEM);
|
|
|
+ iface_combinations =
|
|
|
+ kzalloc(n_combos * sizeof(struct ieee80211_iface_combination) +
|
|
|
+ total_limits * sizeof(struct ieee80211_iface_limit),
|
|
|
+ GFP_KERNEL);
|
|
|
+ if (!iface_combinations)
|
|
|
+ return -ENOMEM;
|
|
|
+ iface_limit = (struct ieee80211_iface_limit *)(iface_combinations +
|
|
|
+ n_combos);
|
|
|
+ combo = conc->combos;
|
|
|
+ for (i = 0; i < n_combos; i++) {
|
|
|
+ iface_combinations[i].max_interfaces = combo->max_interfaces;
|
|
|
+ iface_combinations[i].num_different_channels =
|
|
|
+ combo->n_diff_channels;
|
|
|
+ iface_combinations[i].beacon_int_infra_match =
|
|
|
+ combo->same_bi;
|
|
|
+ iface_combinations[i].n_limits = combo->n_limits;
|
|
|
+ wil_dbg_misc(wil,
|
|
|
+ "iface_combination %d: max_if %d, num_ch %d, bi_match %d\n",
|
|
|
+ i, iface_combinations[i].max_interfaces,
|
|
|
+ iface_combinations[i].num_different_channels,
|
|
|
+ iface_combinations[i].beacon_int_infra_match);
|
|
|
+ limit = combo->limits;
|
|
|
+ for (j = 0; j < combo->n_limits; j++) {
|
|
|
+ iface_limit[j].max = le16_to_cpu(limit[j].max);
|
|
|
+ iface_limit[j].types = le16_to_cpu(limit[j].types);
|
|
|
+ wil_dbg_misc(wil,
|
|
|
+ "limit %d: max %d types 0x%x\n", j,
|
|
|
+ iface_limit[j].max, iface_limit[j].types);
|
|
|
+ }
|
|
|
+ iface_combinations[i].limits = iface_limit;
|
|
|
+ iface_limit += combo->n_limits;
|
|
|
+ limit += combo->n_limits;
|
|
|
+ combo = (struct wil_fw_concurrency_combo *)limit;
|
|
|
+ }
|
|
|
|
|
|
- wdev->wiphy = wiphy_new(&wil_cfg80211_ops,
|
|
|
- sizeof(struct wil6210_priv));
|
|
|
- if (!wdev->wiphy) {
|
|
|
- rc = -ENOMEM;
|
|
|
- goto out;
|
|
|
+ wil_dbg_misc(wil, "multiple VIFs supported, n_mids %d\n", conc->n_mids);
|
|
|
+ wil->max_vifs = conc->n_mids + 1; /* including main interface */
|
|
|
+ if (wil->max_vifs > WIL_MAX_VIFS) {
|
|
|
+ wil_info(wil, "limited number of VIFs supported(%d, FW %d)\n",
|
|
|
+ WIL_MAX_VIFS, wil->max_vifs);
|
|
|
+ wil->max_vifs = WIL_MAX_VIFS;
|
|
|
}
|
|
|
+ wiphy->n_iface_combinations = n_combos;
|
|
|
+ wiphy->iface_combinations = iface_combinations;
|
|
|
+ return 0;
|
|
|
+}
|
|
|
|
|
|
- set_wiphy_dev(wdev->wiphy, dev);
|
|
|
- wil_wiphy_init(wdev->wiphy);
|
|
|
+struct wil6210_priv *wil_cfg80211_init(struct device *dev)
|
|
|
+{
|
|
|
+ struct wiphy *wiphy;
|
|
|
+ struct wil6210_priv *wil;
|
|
|
+ struct ieee80211_channel *ch;
|
|
|
|
|
|
- return wdev;
|
|
|
+ dev_dbg(dev, "%s()\n", __func__);
|
|
|
|
|
|
-out:
|
|
|
- kfree(wdev);
|
|
|
+ /* Note: the wireless_dev structure is no longer allocated here.
|
|
|
+ * Instead, it is allocated as part of the net_device structure
|
|
|
+ * for main interface and each VIF.
|
|
|
+ */
|
|
|
+ wiphy = wiphy_new(&wil_cfg80211_ops, sizeof(struct wil6210_priv));
|
|
|
+ if (!wiphy)
|
|
|
+ return ERR_PTR(-ENOMEM);
|
|
|
|
|
|
- return ERR_PTR(rc);
|
|
|
+ set_wiphy_dev(wiphy, dev);
|
|
|
+ wil_wiphy_init(wiphy);
|
|
|
+
|
|
|
+ wil = wiphy_to_wil(wiphy);
|
|
|
+ wil->wiphy = wiphy;
|
|
|
+
|
|
|
+ /* default monitor channel */
|
|
|
+ ch = wiphy->bands[NL80211_BAND_60GHZ]->channels;
|
|
|
+ cfg80211_chandef_create(&wil->monitor_chandef, ch, NL80211_CHAN_NO_HT);
|
|
|
+
|
|
|
+ return wil;
|
|
|
}
|
|
|
|
|
|
-void wil_wdev_free(struct wil6210_priv *wil)
|
|
|
+void wil_cfg80211_deinit(struct wil6210_priv *wil)
|
|
|
{
|
|
|
- struct wireless_dev *wdev = wil_to_wdev(wil);
|
|
|
+ struct wiphy *wiphy = wil_to_wiphy(wil);
|
|
|
|
|
|
dev_dbg(wil_to_dev(wil), "%s()\n", __func__);
|
|
|
|
|
|
- if (!wdev)
|
|
|
+ if (!wiphy)
|
|
|
return;
|
|
|
|
|
|
- wiphy_free(wdev->wiphy);
|
|
|
- kfree(wdev);
|
|
|
+ kfree(wiphy->iface_combinations);
|
|
|
+ wiphy->iface_combinations = NULL;
|
|
|
+
|
|
|
+ wiphy_free(wiphy);
|
|
|
+ /* do not access wil6210_priv after returning from here */
|
|
|
}
|
|
|
|
|
|
void wil_p2p_wdev_free(struct wil6210_priv *wil)
|
|
|
{
|
|
|
struct wireless_dev *p2p_wdev;
|
|
|
|
|
|
- mutex_lock(&wil->p2p_wdev_mutex);
|
|
|
+ mutex_lock(&wil->vif_mutex);
|
|
|
p2p_wdev = wil->p2p_wdev;
|
|
|
wil->p2p_wdev = NULL;
|
|
|
- wil->radio_wdev = wil_to_wdev(wil);
|
|
|
- mutex_unlock(&wil->p2p_wdev_mutex);
|
|
|
+ wil->radio_wdev = wil->main_ndev->ieee80211_ptr;
|
|
|
+ mutex_unlock(&wil->vif_mutex);
|
|
|
if (p2p_wdev) {
|
|
|
cfg80211_unregister_wdev(p2p_wdev);
|
|
|
kfree(p2p_wdev);
|
|
@@ -1971,6 +2267,7 @@ static int wil_rf_sector_get_cfg(struct wiphy *wiphy,
|
|
|
const void *data, int data_len)
|
|
|
{
|
|
|
struct wil6210_priv *wil = wdev_to_wil(wdev);
|
|
|
+ struct wil6210_vif *vif = wdev_to_vif(wil, wdev);
|
|
|
int rc;
|
|
|
struct nlattr *tb[QCA_ATTR_DMG_RF_SECTOR_MAX + 1];
|
|
|
u16 sector_index;
|
|
@@ -2027,8 +2324,8 @@ static int wil_rf_sector_get_cfg(struct wiphy *wiphy,
|
|
|
cmd.sector_type = sector_type;
|
|
|
cmd.rf_modules_vec = rf_modules_vec & 0xFF;
|
|
|
memset(&reply, 0, sizeof(reply));
|
|
|
- rc = wmi_call(wil, WMI_GET_RF_SECTOR_PARAMS_CMDID, &cmd, sizeof(cmd),
|
|
|
- WMI_GET_RF_SECTOR_PARAMS_DONE_EVENTID,
|
|
|
+ rc = wmi_call(wil, WMI_GET_RF_SECTOR_PARAMS_CMDID, vif->mid,
|
|
|
+ &cmd, sizeof(cmd), WMI_GET_RF_SECTOR_PARAMS_DONE_EVENTID,
|
|
|
&reply, sizeof(reply),
|
|
|
500);
|
|
|
if (rc)
|
|
@@ -2090,6 +2387,7 @@ static int wil_rf_sector_set_cfg(struct wiphy *wiphy,
|
|
|
const void *data, int data_len)
|
|
|
{
|
|
|
struct wil6210_priv *wil = wdev_to_wil(wdev);
|
|
|
+ struct wil6210_vif *vif = wdev_to_vif(wil, wdev);
|
|
|
int rc, tmp;
|
|
|
struct nlattr *tb[QCA_ATTR_DMG_RF_SECTOR_MAX + 1];
|
|
|
struct nlattr *tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_MAX + 1];
|
|
@@ -2184,8 +2482,8 @@ static int wil_rf_sector_set_cfg(struct wiphy *wiphy,
|
|
|
|
|
|
cmd.rf_modules_vec = rf_modules_vec & 0xFF;
|
|
|
memset(&reply, 0, sizeof(reply));
|
|
|
- rc = wmi_call(wil, WMI_SET_RF_SECTOR_PARAMS_CMDID, &cmd, sizeof(cmd),
|
|
|
- WMI_SET_RF_SECTOR_PARAMS_DONE_EVENTID,
|
|
|
+ rc = wmi_call(wil, WMI_SET_RF_SECTOR_PARAMS_CMDID, vif->mid,
|
|
|
+ &cmd, sizeof(cmd), WMI_SET_RF_SECTOR_PARAMS_DONE_EVENTID,
|
|
|
&reply, sizeof(reply),
|
|
|
500);
|
|
|
if (rc)
|
|
@@ -2198,6 +2496,7 @@ static int wil_rf_sector_get_selected(struct wiphy *wiphy,
|
|
|
const void *data, int data_len)
|
|
|
{
|
|
|
struct wil6210_priv *wil = wdev_to_wil(wdev);
|
|
|
+ struct wil6210_vif *vif = wdev_to_vif(wil, wdev);
|
|
|
int rc;
|
|
|
struct nlattr *tb[QCA_ATTR_DMG_RF_SECTOR_MAX + 1];
|
|
|
u8 sector_type, mac_addr[ETH_ALEN];
|
|
@@ -2231,13 +2530,13 @@ static int wil_rf_sector_get_selected(struct wiphy *wiphy,
|
|
|
|
|
|
if (tb[QCA_ATTR_MAC_ADDR]) {
|
|
|
ether_addr_copy(mac_addr, nla_data(tb[QCA_ATTR_MAC_ADDR]));
|
|
|
- cid = wil_find_cid(wil, mac_addr);
|
|
|
+ cid = wil_find_cid(wil, vif->mid, mac_addr);
|
|
|
if (cid < 0) {
|
|
|
wil_err(wil, "invalid MAC address %pM\n", mac_addr);
|
|
|
return -ENOENT;
|
|
|
}
|
|
|
} else {
|
|
|
- if (test_bit(wil_status_fwconnected, wil->status)) {
|
|
|
+ if (test_bit(wil_vif_fwconnected, vif->status)) {
|
|
|
wil_err(wil, "must specify MAC address when connected\n");
|
|
|
return -EINVAL;
|
|
|
}
|
|
@@ -2247,7 +2546,7 @@ static int wil_rf_sector_get_selected(struct wiphy *wiphy,
|
|
|
cmd.cid = (u8)cid;
|
|
|
cmd.sector_type = sector_type;
|
|
|
memset(&reply, 0, sizeof(reply));
|
|
|
- rc = wmi_call(wil, WMI_GET_SELECTED_RF_SECTOR_INDEX_CMDID,
|
|
|
+ rc = wmi_call(wil, WMI_GET_SELECTED_RF_SECTOR_INDEX_CMDID, vif->mid,
|
|
|
&cmd, sizeof(cmd),
|
|
|
WMI_GET_SELECTED_RF_SECTOR_INDEX_DONE_EVENTID,
|
|
|
&reply, sizeof(reply),
|
|
@@ -2280,7 +2579,7 @@ nla_put_failure:
|
|
|
}
|
|
|
|
|
|
static int wil_rf_sector_wmi_set_selected(struct wil6210_priv *wil,
|
|
|
- u16 sector_index,
|
|
|
+ u8 mid, u16 sector_index,
|
|
|
u8 sector_type, u8 cid)
|
|
|
{
|
|
|
struct wmi_set_selected_rf_sector_index_cmd cmd;
|
|
@@ -2295,7 +2594,7 @@ static int wil_rf_sector_wmi_set_selected(struct wil6210_priv *wil,
|
|
|
cmd.sector_type = sector_type;
|
|
|
cmd.cid = (u8)cid;
|
|
|
memset(&reply, 0, sizeof(reply));
|
|
|
- rc = wmi_call(wil, WMI_SET_SELECTED_RF_SECTOR_INDEX_CMDID,
|
|
|
+ rc = wmi_call(wil, WMI_SET_SELECTED_RF_SECTOR_INDEX_CMDID, mid,
|
|
|
&cmd, sizeof(cmd),
|
|
|
WMI_SET_SELECTED_RF_SECTOR_INDEX_DONE_EVENTID,
|
|
|
&reply, sizeof(reply),
|
|
@@ -2310,6 +2609,7 @@ static int wil_rf_sector_set_selected(struct wiphy *wiphy,
|
|
|
const void *data, int data_len)
|
|
|
{
|
|
|
struct wil6210_priv *wil = wdev_to_wil(wdev);
|
|
|
+ struct wil6210_vif *vif = wdev_to_vif(wil, wdev);
|
|
|
int rc;
|
|
|
struct nlattr *tb[QCA_ATTR_DMG_RF_SECTOR_MAX + 1];
|
|
|
u16 sector_index;
|
|
@@ -2349,7 +2649,7 @@ static int wil_rf_sector_set_selected(struct wiphy *wiphy,
|
|
|
if (tb[QCA_ATTR_MAC_ADDR]) {
|
|
|
ether_addr_copy(mac_addr, nla_data(tb[QCA_ATTR_MAC_ADDR]));
|
|
|
if (!is_broadcast_ether_addr(mac_addr)) {
|
|
|
- cid = wil_find_cid(wil, mac_addr);
|
|
|
+ cid = wil_find_cid(wil, vif->mid, mac_addr);
|
|
|
if (cid < 0) {
|
|
|
wil_err(wil, "invalid MAC address %pM\n",
|
|
|
mac_addr);
|
|
@@ -2363,7 +2663,7 @@ static int wil_rf_sector_set_selected(struct wiphy *wiphy,
|
|
|
cid = -1;
|
|
|
}
|
|
|
} else {
|
|
|
- if (test_bit(wil_status_fwconnected, wil->status)) {
|
|
|
+ if (test_bit(wil_vif_fwconnected, vif->status)) {
|
|
|
wil_err(wil, "must specify MAC address when connected\n");
|
|
|
return -EINVAL;
|
|
|
}
|
|
@@ -2371,17 +2671,20 @@ static int wil_rf_sector_set_selected(struct wiphy *wiphy,
|
|
|
}
|
|
|
|
|
|
if (cid >= 0) {
|
|
|
- rc = wil_rf_sector_wmi_set_selected(wil, sector_index,
|
|
|
+ rc = wil_rf_sector_wmi_set_selected(wil, vif->mid, sector_index,
|
|
|
sector_type, cid);
|
|
|
} else {
|
|
|
/* unlock all cids */
|
|
|
rc = wil_rf_sector_wmi_set_selected(
|
|
|
- wil, WMI_INVALID_RF_SECTOR_INDEX, sector_type,
|
|
|
- WIL_CID_ALL);
|
|
|
+ wil, vif->mid, WMI_INVALID_RF_SECTOR_INDEX,
|
|
|
+ sector_type, WIL_CID_ALL);
|
|
|
if (rc == -EINVAL) {
|
|
|
for (i = 0; i < WIL6210_MAX_CID; i++) {
|
|
|
+ if (wil->sta[i].mid != vif->mid)
|
|
|
+ continue;
|
|
|
rc = wil_rf_sector_wmi_set_selected(
|
|
|
- wil, WMI_INVALID_RF_SECTOR_INDEX,
|
|
|
+ wil, vif->mid,
|
|
|
+ WMI_INVALID_RF_SECTOR_INDEX,
|
|
|
sector_type, i);
|
|
|
/* the FW will silently ignore and return
|
|
|
* success for unused cid, so abort the loop
|