Commit 4aebd3bd authored by Lior David's avatar Lior David Committed by Kalle Valo

wil6210: add support for adding and removing virtual interfaces

Add generic support in cfg80211 operations add_virtual_intf
and del_virtual_intf for adding/removing VIFs of any
interface type, and fix change_virtual_intf to allow changing
the interface type of a VIF. Previously these operations
only worked for the P2P_DEVICE interface which is not a real
VIF(it is management-only and shares radio with the main
interface).
Currently the interface combination is validated, the VIF is
added/removed in the firmware and the appropriate net/wireless
device is also added/removed.
Added minimal support for proper interface up/down and module
unload but most operations still work only on the main interface.
Signed-off-by: default avatarLior David <liord@codeaurora.org>
Signed-off-by: default avatarMaya Erez <merez@codeaurora.org>
Signed-off-by: default avatarKalle Valo <kvalo@codeaurora.org>
parent e00243fa
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
#include <linux/etherdevice.h> #include <linux/etherdevice.h>
#include <linux/moduleparam.h> #include <linux/moduleparam.h>
#include <net/netlink.h> #include <net/netlink.h>
#include <net/cfg80211.h>
#include "wil6210.h" #include "wil6210.h"
#include "wmi.h" #include "wmi.h"
#include "fw.h" #include "fw.h"
...@@ -418,6 +419,53 @@ static void wil_cfg80211_stop_p2p_device(struct wiphy *wiphy, ...@@ -418,6 +419,53 @@ static void wil_cfg80211_stop_p2p_device(struct wiphy *wiphy,
mutex_unlock(&wil->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, &params);
}
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, &params);
}
return ret;
}
static struct wireless_dev * static struct wireless_dev *
wil_cfg80211_add_iface(struct wiphy *wiphy, const char *name, wil_cfg80211_add_iface(struct wiphy *wiphy, const char *name,
unsigned char name_assign_type, unsigned char name_assign_type,
...@@ -425,53 +473,136 @@ wil_cfg80211_add_iface(struct wiphy *wiphy, const char *name, ...@@ -425,53 +473,136 @@ wil_cfg80211_add_iface(struct wiphy *wiphy, const char *name,
struct vif_params *params) struct vif_params *params)
{ {
struct wil6210_priv *wil = wiphy_to_wil(wiphy); struct wil6210_priv *wil = wiphy_to_wil(wiphy);
struct net_device *ndev = wil->main_ndev; struct net_device *ndev_main = wil->main_ndev, *ndev;
struct wil6210_vif *vif; struct wil6210_vif *vif;
struct wireless_dev *p2p_wdev; 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) { /* P2P device is not a real virtual interface, it is a management-only
wil_err(wil, "unsupported iftype %d\n", type); * interface that shares the main interface.
return ERR_PTR(-EINVAL); * 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);
}
vif = kzalloc(sizeof(*vif), GFP_KERNEL);
if (!vif)
return ERR_PTR(-ENOMEM);
p2p_wdev = vif_to_wdev(vif);
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) { if (!wil->wiphy->n_iface_combinations) {
wil_err(wil, "P2P_DEVICE interface already created\n"); wil_err(wil, "virtual interfaces not supported\n");
return ERR_PTR(-EINVAL); return ERR_PTR(-EINVAL);
} }
vif = kzalloc(sizeof(*vif), GFP_KERNEL); rc = wil_cfg80211_validate_add_iface(wil, type);
if (!vif) if (rc) {
return ERR_PTR(-ENOMEM); wil_err(wil, "iface validation failed, err=%d\n", rc);
return ERR_PTR(rc);
}
p2p_wdev = &vif->wdev; vif = wil_vif_alloc(wil, name, name_assign_type, type);
p2p_wdev->iftype = type; if (IS_ERR(vif))
p2p_wdev->wiphy = wiphy; return ERR_CAST(vif);
/* use our primary ethernet address */
ether_addr_copy(p2p_wdev->address, ndev->perm_addr);
wil->p2p_wdev = p2p_wdev; 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);
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_priv *wil, struct wil6210_vif *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 */
}
netif_carrier_off(ndev);
}
return 0;
} }
static int wil_cfg80211_del_iface(struct wiphy *wiphy, static int wil_cfg80211_del_iface(struct wiphy *wiphy,
struct wireless_dev *wdev) struct wireless_dev *wdev)
{ {
struct wil6210_priv *wil = wiphy_to_wil(wiphy); 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"); wil_dbg_misc(wil, "del_iface\n");
if (wdev != wil->p2p_wdev) { if (wdev->iftype == NL80211_IFTYPE_P2P_DEVICE) {
wil_err(wil, "delete of incorrect interface 0x%p\n", wdev); 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; return -EINVAL;
} }
wil_cfg80211_stop_p2p_device(wiphy, wdev); rc = wil_vif_prepare_stop(wil, vif);
wil_p2p_wdev_free(wil); if (rc)
goto out;
return 0; wil_info(wil, "deleted VIF, mid %d iftype %d MAC %pM\n",
vif->mid, wdev->iftype, wdev->address);
wil_vif_remove(wil, vif->mid);
out:
return rc;
} }
static int wil_cfg80211_change_iface(struct wiphy *wiphy, static int wil_cfg80211_change_iface(struct wiphy *wiphy,
...@@ -486,7 +617,19 @@ static int wil_cfg80211_change_iface(struct wiphy *wiphy, ...@@ -486,7 +617,19 @@ static int wil_cfg80211_change_iface(struct wiphy *wiphy,
wil_dbg_misc(wil, "change_iface: type=%d\n", type); wil_dbg_misc(wil, "change_iface: type=%d\n", type);
if (netif_running(ndev) && !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_up_ifaces(wil, ndev) &&
netif_running(ndev) && !wil_is_recovery_blocked(wil)) {
wil_dbg_misc(wil, "interface is up. resetting...\n"); wil_dbg_misc(wil, "interface is up. resetting...\n");
mutex_lock(&wil->mutex); mutex_lock(&wil->mutex);
__wil_down(wil); __wil_down(wil);
...@@ -511,8 +654,17 @@ static int wil_cfg80211_change_iface(struct wiphy *wiphy, ...@@ -511,8 +654,17 @@ static int wil_cfg80211_change_iface(struct wiphy *wiphy,
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }
wdev->iftype = type; if (vif->mid != 0 && wil_has_up_ifaces(wil)) {
wil_vif_prepare_stop(wil, 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; return 0;
} }
...@@ -2007,6 +2159,13 @@ int wil_cfg80211_iface_combinations_from_fw( ...@@ -2007,6 +2159,13 @@ int wil_cfg80211_iface_combinations_from_fw(
combo = (struct wil_fw_concurrency_combo *)limit; combo = (struct wil_fw_concurrency_combo *)limit;
} }
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->n_iface_combinations = n_combos;
wiphy->iface_combinations = iface_combinations; wiphy->iface_combinations = iface_combinations;
return 0; return 0;
......
...@@ -565,6 +565,7 @@ int wil_priv_init(struct wil6210_priv *wil) ...@@ -565,6 +565,7 @@ int wil_priv_init(struct wil6210_priv *wil)
wil->vring_idle_trsh = 16; wil->vring_idle_trsh = 16;
wil->reply_mid = U8_MAX; wil->reply_mid = U8_MAX;
wil->max_vifs = 1;
return 0; return 0;
...@@ -1115,6 +1116,33 @@ static void wil_pre_fw_config(struct wil6210_priv *wil) ...@@ -1115,6 +1116,33 @@ static void wil_pre_fw_config(struct wil6210_priv *wil)
} }
} }
static int wil_restore_vifs(struct wil6210_priv *wil)
{
struct wil6210_vif *vif;
struct net_device *ndev;
struct wireless_dev *wdev;
int i, rc;
for (i = 0; i < wil->max_vifs; i++) {
vif = wil->vifs[i];
if (!vif)
continue;
if (vif->mid) {
ndev = vif_to_ndev(vif);
wdev = vif_to_wdev(vif);
rc = wmi_port_allocate(wil, vif->mid, ndev->dev_addr,
wdev->iftype);
if (rc) {
wil_err(wil, "fail to restore VIF %d type %d, rc %d\n",
i, wdev->iftype, rc);
return rc;
}
}
}
return 0;
}
/* /*
* We reset all the structures, and we reset the UMAC. * We reset all the structures, and we reset the UMAC.
* After calling this routine, you're expected to reload * After calling this routine, you're expected to reload
...@@ -1277,6 +1305,12 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw) ...@@ -1277,6 +1305,12 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw)
return rc; return rc;
} }
rc = wil_restore_vifs(wil);
if (rc) {
wil_err(wil, "failed to restore vifs, rc %d\n", rc);
return rc;
}
wil_collect_fw_info(wil); wil_collect_fw_info(wil);
if (wil->ps_profile != WMI_PS_PROFILE_TYPE_DEFAULT) if (wil->ps_profile != WMI_PS_PROFILE_TYPE_DEFAULT)
......
...@@ -16,13 +16,38 @@ ...@@ -16,13 +16,38 @@
*/ */
#include <linux/etherdevice.h> #include <linux/etherdevice.h>
#include <linux/rtnetlink.h>
#include "wil6210.h" #include "wil6210.h"
#include "txrx.h" #include "txrx.h"
bool wil_has_other_up_ifaces(struct wil6210_priv *wil,
struct net_device *ndev)
{
int i;
struct wil6210_vif *vif;
struct net_device *ndev_i;
for (i = 0; i < wil->max_vifs; i++) {
vif = wil->vifs[i];
if (vif) {
ndev_i = vif_to_ndev(vif);
if (ndev_i != ndev && ndev_i->flags & IFF_UP)
return true;
}
}
return false;
}
bool wil_has_up_ifaces(struct wil6210_priv *wil)
{
return wil_has_other_up_ifaces(wil, NULL);
}
static int wil_open(struct net_device *ndev) static int wil_open(struct net_device *ndev)
{ {
struct wil6210_priv *wil = ndev_to_wil(ndev); struct wil6210_priv *wil = ndev_to_wil(ndev);
int rc; int rc = 0;
wil_dbg_misc(wil, "open\n"); wil_dbg_misc(wil, "open\n");
...@@ -32,13 +57,16 @@ static int wil_open(struct net_device *ndev) ...@@ -32,13 +57,16 @@ static int wil_open(struct net_device *ndev)
return -EINVAL; return -EINVAL;
} }
rc = wil_pm_runtime_get(wil); if (!wil_has_other_up_ifaces(wil, ndev)) {
if (rc < 0) wil_dbg_misc(wil, "open, first iface\n");
return rc; rc = wil_pm_runtime_get(wil);
if (rc < 0)
return rc;
rc = wil_up(wil); rc = wil_up(wil);
if (rc) if (rc)
wil_pm_runtime_put(wil); wil_pm_runtime_put(wil);
}
return rc; return rc;
} }
...@@ -46,13 +74,16 @@ static int wil_open(struct net_device *ndev) ...@@ -46,13 +74,16 @@ static int wil_open(struct net_device *ndev)
static int wil_stop(struct net_device *ndev) static int wil_stop(struct net_device *ndev)
{ {
struct wil6210_priv *wil = ndev_to_wil(ndev); struct wil6210_priv *wil = ndev_to_wil(ndev);
int rc; int rc = 0;
wil_dbg_misc(wil, "stop\n"); wil_dbg_misc(wil, "stop\n");
rc = wil_down(wil); if (!wil_has_other_up_ifaces(wil, ndev)) {
if (!rc) wil_dbg_misc(wil, "stop, last iface\n");
wil_pm_runtime_put(wil); rc = wil_down(wil);
if (!rc)
wil_pm_runtime_put(wil);
}
return rc; return rc;
} }
...@@ -201,14 +232,32 @@ static void wil_vif_init(struct wil6210_vif *vif) ...@@ -201,14 +232,32 @@ static void wil_vif_init(struct wil6210_vif *vif)
INIT_LIST_HEAD(&vif->probe_client_pending); INIT_LIST_HEAD(&vif->probe_client_pending);
} }
static u8 wil_vif_find_free_mid(struct wil6210_priv *wil)
{
u8 i;
for (i = 0; i < wil->max_vifs; i++) {
if (!wil->vifs[i])
return i;
}
return U8_MAX;
}
struct wil6210_vif * struct wil6210_vif *
wil_vif_alloc(struct wil6210_priv *wil, const char *name, wil_vif_alloc(struct wil6210_priv *wil, const char *name,
unsigned char name_assign_type, enum nl80211_iftype iftype, unsigned char name_assign_type, enum nl80211_iftype iftype)
u8 mid)
{ {
struct net_device *ndev; struct net_device *ndev;
struct wireless_dev *wdev; struct wireless_dev *wdev;
struct wil6210_vif *vif; struct wil6210_vif *vif;
u8 mid;
mid = wil_vif_find_free_mid(wil);
if (mid == U8_MAX) {
wil_err(wil, "no available virtual interface\n");
return ERR_PTR(-EINVAL);
}
ndev = alloc_netdev(sizeof(*vif), name, name_assign_type, ndev = alloc_netdev(sizeof(*vif), name, name_assign_type,
wil_dev_setup); wil_dev_setup);
...@@ -216,8 +265,13 @@ wil_vif_alloc(struct wil6210_priv *wil, const char *name, ...@@ -216,8 +265,13 @@ wil_vif_alloc(struct wil6210_priv *wil, const char *name,
dev_err(wil_to_dev(wil), "alloc_netdev failed\n"); dev_err(wil_to_dev(wil), "alloc_netdev failed\n");
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
} }
if (mid == 0) if (mid == 0) {
wil->main_ndev = ndev; wil->main_ndev = ndev;
} else {
ndev->priv_destructor = wil_ndev_destructor;
ndev->needs_free_netdev = true;
}
vif = ndev_to_vif(ndev); vif = ndev_to_vif(ndev);
vif->ndev = ndev; vif->ndev = ndev;
vif->wil = wil; vif->wil = wil;
...@@ -263,7 +317,7 @@ void *wil_if_alloc(struct device *dev) ...@@ -263,7 +317,7 @@ void *wil_if_alloc(struct device *dev)
wil_dbg_misc(wil, "if_alloc\n"); wil_dbg_misc(wil, "if_alloc\n");
vif = wil_vif_alloc(wil, "wlan%d", NET_NAME_UNKNOWN, vif = wil_vif_alloc(wil, "wlan%d", NET_NAME_UNKNOWN,
NL80211_IFTYPE_STATION, 0); NL80211_IFTYPE_STATION);
if (IS_ERR(vif)) { if (IS_ERR(vif)) {
dev_err(dev, "wil_vif_alloc failed\n"); dev_err(dev, "wil_vif_alloc failed\n");
rc = -ENOMEM; rc = -ENOMEM;
...@@ -301,10 +355,43 @@ void wil_if_free(struct wil6210_priv *wil) ...@@ -301,10 +355,43 @@ void wil_if_free(struct wil6210_priv *wil)
wil_cfg80211_deinit(wil); wil_cfg80211_deinit(wil);
} }
int wil_vif_add(struct wil6210_priv *wil, struct wil6210_vif *vif)
{
struct net_device *ndev = vif_to_ndev(vif);
struct wireless_dev *wdev = vif_to_wdev(vif);
bool any_active = wil_has_up_ifaces(wil);
int rc;
ASSERT_RTNL();
if (wil->vifs[vif->mid]) {
dev_err(&ndev->dev, "VIF with mid %d already in use\n",
vif->mid);
return -EEXIST;
}
if (any_active && vif->mid != 0) {
rc = wmi_port_allocate(wil, vif->mid, ndev->dev_addr,
wdev->iftype);
if (rc)
return rc;
}
rc = register_netdevice(ndev);
if (rc < 0) {
dev_err(&ndev->dev, "Failed to register netdev: %d\n", rc);
if (any_active && vif->mid != 0)
wmi_port_delete(wil, vif->mid);
return rc;
}
wil->vifs[vif->mid] = vif;
return 0;
}
int wil_if_add(struct wil6210_priv *wil) int wil_if_add(struct wil6210_priv *wil)
{ {
struct wiphy *wiphy = wil->wiphy; struct wiphy *wiphy = wil->wiphy;
struct net_device *ndev = wil->main_ndev; struct net_device *ndev = wil->main_ndev;
struct wil6210_vif *vif = ndev_to_vif(ndev);
int rc; int rc;
wil_dbg_misc(wil, "entered"); wil_dbg_misc(wil, "entered");
...@@ -324,11 +411,11 @@ int wil_if_add(struct wil6210_priv *wil) ...@@ -324,11 +411,11 @@ int wil_if_add(struct wil6210_priv *wil)
wil_update_net_queues_bh(wil, NULL, true); wil_update_net_queues_bh(wil, NULL, true);
rc = register_netdev(ndev); rtnl_lock();
if (rc < 0) { rc = wil_vif_add(wil, vif);
dev_err(&ndev->dev, "Failed to register netdev: %d\n", rc); rtnl_unlock();
if (rc < 0)
goto out_wiphy; goto out_wiphy;
}
return 0; return 0;
...@@ -337,6 +424,40 @@ int wil_if_add(struct wil6210_priv *wil) ...@@ -337,6 +424,40 @@ int wil_if_add(struct wil6210_priv *wil)
return rc; return rc;
} }
void wil_vif_remove(struct wil6210_priv *wil, u8 mid)
{
struct wil6210_vif *vif;
struct net_device *ndev;
bool any_active = wil_has_up_ifaces(wil);
ASSERT_RTNL();
if (mid >= wil->max_vifs) {
wil_err(wil, "invalid MID: %d\n", mid);
return;
}
vif = wil->vifs[mid];
if (!vif) {
wil_err(wil, "MID %d not registered\n", mid);
return;
}
ndev = vif_to_ndev(vif);
/* during unregister_netdevice cfg80211_leave may perform operations
* such as stop AP, disconnect, so we only clear the VIF afterwards
*/
unregister_netdevice(ndev);
if (any_active && vif->mid != 0)
wmi_port_delete(wil, vif->mid);
wil->vifs[mid] = NULL;
/* for VIFs, ndev will be freed by destructor after RTNL is unlocked.
* the main interface will be freed in wil_if_free, we need to keep it
* a bit longer so logging macros will work.
*/
}
void wil_if_remove(struct wil6210_priv *wil) void wil_if_remove(struct wil6210_priv *wil)
{ {
struct net_device *ndev = wil->main_ndev; struct net_device *ndev = wil->main_ndev;
...@@ -344,6 +465,8 @@ void wil_if_remove(struct wil6210_priv *wil) ...@@ -344,6 +465,8 @@ void wil_if_remove(struct wil6210_priv *wil)
wil_dbg_misc(wil, "if_remove\n"); wil_dbg_misc(wil, "if_remove\n");
unregister_netdev(ndev); rtnl_lock();
wil_vif_remove(wil, 0);
rtnl_unlock();
wiphy_unregister(wdev->wiphy); wiphy_unregister(wdev->wiphy);
} }
...@@ -137,6 +137,20 @@ void wil_enable_irq(struct wil6210_priv *wil) ...@@ -137,6 +137,20 @@ void wil_enable_irq(struct wil6210_priv *wil)
enable_irq(wil->pdev->irq); enable_irq(wil->pdev->irq);
} }
static void wil_remove_all_additional_vifs(struct wil6210_priv *wil)
{
struct wil6210_vif *vif;
int i;
for (i = 1; i < wil->max_vifs; i++) {
vif = wil->vifs[i];
if (vif) {
wil_vif_prepare_stop(wil, vif);
wil_vif_remove(wil, vif->mid);
}
}
}
/* Bus ops */ /* Bus ops */
static int wil_if_pcie_enable(struct wil6210_priv *wil) static int wil_if_pcie_enable(struct wil6210_priv *wil)
{ {
...@@ -148,10 +162,8 @@ static int wil_if_pcie_enable(struct wil6210_priv *wil) ...@@ -148,10 +162,8 @@ static int wil_if_pcie_enable(struct wil6210_priv *wil)
*/ */
int msi_only = pdev->msi_enabled; int msi_only = pdev->msi_enabled;
bool _use_msi = use_msi; bool _use_msi = use_msi;
bool wmi_only = test_bit(WMI_FW_CAPABILITY_WMI_ONLY,
wil->fw_capabilities);
wil_dbg_misc(wil, "if_pcie_enable, wmi_only %d\n", wmi_only); wil_dbg_misc(wil, "if_pcie_enable\n");
pci_set_master(pdev); pci_set_master(pdev);
...@@ -172,11 +184,9 @@ static int wil_if_pcie_enable(struct wil6210_priv *wil) ...@@ -172,11 +184,9 @@ static int wil_if_pcie_enable(struct wil6210_priv *wil)
if (rc) if (rc)
goto stop_master; goto stop_master;
/* need reset here to obtain MAC or in case of WMI-only FW, full reset /* need reset here to obtain MAC */
* and fw loading takes place
*/
mutex_lock(&wil->mutex); mutex_lock(&wil->mutex);
rc = wil_reset(wil, wmi_only); rc = wil_reset(wil, false);
mutex_unlock(&wil->mutex); mutex_unlock(&wil->mutex);
if (rc) if (rc)
goto release_irq; goto release_irq;
...@@ -356,6 +366,18 @@ static int wil_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id) ...@@ -356,6 +366,18 @@ static int wil_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id)
goto bus_disable; goto bus_disable;
} }
/* in case of WMI-only FW, perform full reset and FW loading */
if (test_bit(WMI_FW_CAPABILITY_WMI_ONLY, wil->fw_capabilities)) {
wil_dbg_misc(wil, "Loading WMI only FW\n");
mutex_lock(&wil->mutex);
rc = wil_reset(wil, true);
mutex_unlock(&wil->mutex);
if (rc) {
wil_err(wil, "failed to load WMI only FW\n");
goto if_remove;
}
}
if (IS_ENABLED(CONFIG_PM)) if (IS_ENABLED(CONFIG_PM))
wil->pm_notify.notifier_call = wil6210_pm_notify; wil->pm_notify.notifier_call = wil6210_pm_notify;
...@@ -372,6 +394,8 @@ static int wil_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id) ...@@ -372,6 +394,8 @@ static int wil_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id)
return 0; return 0;
if_remove:
wil_if_remove(wil);
bus_disable: bus_disable:
wil_if_pcie_disable(wil); wil_if_pcie_disable(wil);
err_iounmap: err_iounmap:
...@@ -402,6 +426,7 @@ static void wil_pcie_remove(struct pci_dev *pdev) ...@@ -402,6 +426,7 @@ static void wil_pcie_remove(struct pci_dev *pdev)
wil6210_debugfs_remove(wil); wil6210_debugfs_remove(wil);
rtnl_lock(); rtnl_lock();
wil_p2p_wdev_free(wil); wil_p2p_wdev_free(wil);
wil_remove_all_additional_vifs(wil);
rtnl_unlock(); rtnl_unlock();
wil_if_remove(wil); wil_if_remove(wil);
wil_if_pcie_disable(wil); wil_if_pcie_disable(wil);
......
...@@ -50,6 +50,11 @@ extern bool disable_ap_sme; ...@@ -50,6 +50,11 @@ extern bool disable_ap_sme;
#define WIL_DEFAULT_BUS_REQUEST_KBPS 128000 /* ~1Gbps */ #define WIL_DEFAULT_BUS_REQUEST_KBPS 128000 /* ~1Gbps */
#define WIL_MAX_BUS_REQUEST_KBPS 800000 /* ~6.1Gbps */ #define WIL_MAX_BUS_REQUEST_KBPS 800000 /* ~6.1Gbps */
/* maximum number of virtual interfaces the driver supports
* (including the main interface)
*/
#define WIL_MAX_VIFS 4
/** /**
* extract bits [@b0:@b1] (inclusive) from the value @x * extract bits [@b0:@b1] (inclusive) from the value @x
* it should be @b0 <= @b1, or result is incorrect * it should be @b0 <= @b1, or result is incorrect
...@@ -714,11 +719,12 @@ struct wil6210_priv { ...@@ -714,11 +719,12 @@ struct wil6210_priv {
DECLARE_BITMAP(hw_capa, hw_capa_last); DECLARE_BITMAP(hw_capa, hw_capa_last);
DECLARE_BITMAP(fw_capabilities, WMI_FW_CAPABILITY_MAX); DECLARE_BITMAP(fw_capabilities, WMI_FW_CAPABILITY_MAX);
DECLARE_BITMAP(platform_capa, WIL_PLATFORM_CAPA_MAX); DECLARE_BITMAP(platform_capa, WIL_PLATFORM_CAPA_MAX);
u8 n_mids; /* number of additional MIDs as reported by FW */
u32 recovery_count; /* num of FW recovery attempts in a short time */ u32 recovery_count; /* num of FW recovery attempts in a short time */
u32 recovery_state; /* FW recovery state machine */ u32 recovery_state; /* FW recovery state machine */
unsigned long last_fw_recovery; /* jiffies of last fw recovery */ unsigned long last_fw_recovery; /* jiffies of last fw recovery */
wait_queue_head_t wq; /* for all wait_event() use */ wait_queue_head_t wq; /* for all wait_event() use */
u8 max_vifs; /* maximum number of interfaces, including main */
struct wil6210_vif *vifs[WIL_MAX_VIFS];
/* profile */ /* profile */
struct cfg80211_chan_def monitor_chandef; struct cfg80211_chan_def monitor_chandef;
u32 monitor_flags; u32 monitor_flags;
...@@ -935,11 +941,16 @@ void wil_memcpy_toio_32(volatile void __iomem *dst, const void *src, ...@@ -935,11 +941,16 @@ void wil_memcpy_toio_32(volatile void __iomem *dst, const void *src,
struct wil6210_vif * struct wil6210_vif *
wil_vif_alloc(struct wil6210_priv *wil, const char *name, wil_vif_alloc(struct wil6210_priv *wil, const char *name,
unsigned char name_assign_type, enum nl80211_iftype iftype, unsigned char name_assign_type, enum nl80211_iftype iftype);
u8 mid); void wil_vif_free(struct wil6210_vif *vif);
void *wil_if_alloc(struct device *dev); void *wil_if_alloc(struct device *dev);
bool wil_has_other_up_ifaces(struct wil6210_priv *wil,
struct net_device *ndev);
bool wil_has_up_ifaces(struct wil6210_priv *wil);
void wil_if_free(struct wil6210_priv *wil); void wil_if_free(struct wil6210_priv *wil);
int wil_vif_add(struct wil6210_priv *wil, struct wil6210_vif *vif);
int wil_if_add(struct wil6210_priv *wil); int wil_if_add(struct wil6210_priv *wil);
void wil_vif_remove(struct wil6210_priv *wil, u8 mid);
void wil_if_remove(struct wil6210_priv *wil); void wil_if_remove(struct wil6210_priv *wil);
int wil_priv_init(struct wil6210_priv *wil); int wil_priv_init(struct wil6210_priv *wil);
void wil_priv_deinit(struct wil6210_priv *wil); void wil_priv_deinit(struct wil6210_priv *wil);
...@@ -998,6 +1009,9 @@ int wmi_ps_dev_profile_cfg(struct wil6210_priv *wil, ...@@ -998,6 +1009,9 @@ int wmi_ps_dev_profile_cfg(struct wil6210_priv *wil,
int wmi_set_mgmt_retry(struct wil6210_priv *wil, u8 retry_short); int wmi_set_mgmt_retry(struct wil6210_priv *wil, u8 retry_short);
int wmi_get_mgmt_retry(struct wil6210_priv *wil, u8 *retry_short); int wmi_get_mgmt_retry(struct wil6210_priv *wil, u8 *retry_short);
int wmi_new_sta(struct wil6210_vif *vif, const u8 *mac, u8 aid); int wmi_new_sta(struct wil6210_vif *vif, const u8 *mac, u8 aid);
int wmi_port_allocate(struct wil6210_priv *wil, u8 mid,
const u8 *mac, enum nl80211_iftype iftype);
int wmi_port_delete(struct wil6210_priv *wil, u8 mid);
int wil_addba_rx_request(struct wil6210_priv *wil, u8 mid, int wil_addba_rx_request(struct wil6210_priv *wil, u8 mid,
u8 cidxtid, u8 dialog_token, __le16 ba_param_set, u8 cidxtid, u8 dialog_token, __le16 ba_param_set,
__le16 ba_timeout, __le16 ba_seq_ctrl); __le16 ba_timeout, __le16 ba_seq_ctrl);
...@@ -1039,6 +1053,7 @@ int wil_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, ...@@ -1039,6 +1053,7 @@ int wil_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
int wil_cfg80211_iface_combinations_from_fw( int wil_cfg80211_iface_combinations_from_fw(
struct wil6210_priv *wil, struct wil6210_priv *wil,
const struct wil_fw_record_concurrency *conc); const struct wil_fw_record_concurrency *conc);
int wil_vif_prepare_stop(struct wil6210_priv *wil, struct wil6210_vif *vif);
#if defined(CONFIG_WIL6210_DEBUGFS) #if defined(CONFIG_WIL6210_DEBUGFS)
int wil6210_debugfs_init(struct wil6210_priv *wil); int wil6210_debugfs_init(struct wil6210_priv *wil);
......
...@@ -656,11 +656,14 @@ static void wmi_evt_ready(struct wil6210_vif *vif, int id, void *d, int len) ...@@ -656,11 +656,14 @@ static void wmi_evt_ready(struct wil6210_vif *vif, int id, void *d, int len)
struct wiphy *wiphy = wil_to_wiphy(wil); struct wiphy *wiphy = wil_to_wiphy(wil);
struct wmi_ready_event *evt = d; struct wmi_ready_event *evt = d;
wil->n_mids = evt->numof_additional_mids;
wil_info(wil, "FW ver. %s(SW %d); MAC %pM; %d MID's\n", wil_info(wil, "FW ver. %s(SW %d); MAC %pM; %d MID's\n",
wil->fw_version, le32_to_cpu(evt->sw_version), wil->fw_version, le32_to_cpu(evt->sw_version),
evt->mac, wil->n_mids); evt->mac, evt->numof_additional_mids);
if (evt->numof_additional_mids + 1 < wil->max_vifs) {
wil_err(wil, "FW does not support enough MIDs (need %d)",
wil->max_vifs - 1);
return; /* FW load will fail after timeout */
}
/* ignore MAC address, we already have it from the boot loader */ /* ignore MAC address, we already have it from the boot loader */
strlcpy(wiphy->fw_version, wil->fw_version, sizeof(wiphy->fw_version)); strlcpy(wiphy->fw_version, wil->fw_version, sizeof(wiphy->fw_version));
...@@ -2372,6 +2375,92 @@ int wmi_resume(struct wil6210_priv *wil) ...@@ -2372,6 +2375,92 @@ int wmi_resume(struct wil6210_priv *wil)
return reply.evt.status; return reply.evt.status;
} }
int wmi_port_allocate(struct wil6210_priv *wil, u8 mid,
const u8 *mac, enum nl80211_iftype iftype)
{
int rc;
struct wmi_port_allocate_cmd cmd = {
.mid = mid,
};
struct {
struct wmi_cmd_hdr wmi;
struct wmi_port_allocated_event evt;
} __packed reply;
wil_dbg_misc(wil, "port allocate, mid %d iftype %d, mac %pM\n",
mid, iftype, mac);
ether_addr_copy(cmd.mac, mac);
switch (iftype) {
case NL80211_IFTYPE_STATION:
cmd.port_role = WMI_PORT_STA;
break;
case NL80211_IFTYPE_AP:
cmd.port_role = WMI_PORT_AP;
break;
case NL80211_IFTYPE_P2P_CLIENT:
cmd.port_role = WMI_PORT_P2P_CLIENT;
break;
case NL80211_IFTYPE_P2P_GO:
cmd.port_role = WMI_PORT_P2P_GO;
break;
/* what about monitor??? */
default:
wil_err(wil, "unsupported iftype: %d\n", iftype);
return -EINVAL;
}
reply.evt.status = WMI_FW_STATUS_FAILURE;
rc = wmi_call(wil, WMI_PORT_ALLOCATE_CMDID, mid,
&cmd, sizeof(cmd),
WMI_PORT_ALLOCATED_EVENTID, &reply,
sizeof(reply), 300);
if (rc) {
wil_err(wil, "failed to allocate port, status %d\n", rc);
return rc;
}
if (reply.evt.status != WMI_FW_STATUS_SUCCESS) {
wil_err(wil, "WMI_PORT_ALLOCATE returned status %d\n",
reply.evt.status);
return -EINVAL;
}
return 0;
}
int wmi_port_delete(struct wil6210_priv *wil, u8 mid)
{
int rc;
struct wmi_port_delete_cmd cmd = {
.mid = mid,
};
struct {
struct wmi_cmd_hdr wmi;
struct wmi_port_deleted_event evt;
} __packed reply;
wil_dbg_misc(wil, "port delete, mid %d\n", mid);
reply.evt.status = WMI_FW_STATUS_FAILURE;
rc = wmi_call(wil, WMI_PORT_DELETE_CMDID, mid,
&cmd, sizeof(cmd),
WMI_PORT_DELETED_EVENTID, &reply,
sizeof(reply), 2000);
if (rc) {
wil_err(wil, "failed to delete port, status %d\n", rc);
return rc;
}
if (reply.evt.status != WMI_FW_STATUS_SUCCESS) {
wil_err(wil, "WMI_PORT_DELETE returned status %d\n",
reply.evt.status);
return -EINVAL;
}
return 0;
}
static bool wmi_evt_call_handler(struct wil6210_vif *vif, int id, static bool wmi_evt_call_handler(struct wil6210_vif *vif, int id,
void *d, int len) void *d, int len)
{ {
...@@ -2391,7 +2480,7 @@ static void wmi_event_handle(struct wil6210_priv *wil, ...@@ -2391,7 +2480,7 @@ static void wmi_event_handle(struct wil6210_priv *wil,
struct wil6210_mbox_hdr *hdr) struct wil6210_mbox_hdr *hdr)
{ {
u16 len = le16_to_cpu(hdr->len); u16 len = le16_to_cpu(hdr->len);
struct wil6210_vif *vif = ndev_to_vif(wil->main_ndev); struct wil6210_vif *vif;
if ((hdr->type == WIL_MBOX_HDR_TYPE_WMI) && if ((hdr->type == WIL_MBOX_HDR_TYPE_WMI) &&
(len >= sizeof(struct wmi_cmd_hdr))) { (len >= sizeof(struct wmi_cmd_hdr))) {
...@@ -2404,6 +2493,20 @@ static void wmi_event_handle(struct wil6210_priv *wil, ...@@ -2404,6 +2493,20 @@ static void wmi_event_handle(struct wil6210_priv *wil,
eventid2name(id), id, wil->reply_id, eventid2name(id), id, wil->reply_id,
wil->reply_mid); wil->reply_mid);
if (mid == MID_BROADCAST)
mid = 0;
if (mid >= wil->max_vifs) {
wil_dbg_wmi(wil, "invalid mid %d, event skipped\n",
mid);
return;
}
vif = wil->vifs[mid];
if (!vif) {
wil_dbg_wmi(wil, "event for empty VIF(%d), skipped\n",
mid);
return;
}
/* check if someone waits for this event */ /* check if someone waits for this event */
if (wil->reply_id && wil->reply_id == id && if (wil->reply_id && wil->reply_id == id &&
wil->reply_mid == mid) { wil->reply_mid == mid) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment