Commit 5f4f9f11 authored by Arend van Spriel's avatar Arend van Spriel Committed by John W. Linville

brcmfmac: implement support for deleting virtual interfaces

Deletion of the virtual interface was not fully supported so
adding it now. Tested using p2p_connect command in wpa_supplicant.
Reviewed-by: default avatarPieter-Paul Giesberts <pieterpg@broadcom.com>
Reviewed-by: default avatarHante Meuleman <meuleman@broadcom.com>
Signed-off-by: default avatarArend van Spriel <arend@broadcom.com>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent de66efca
...@@ -150,6 +150,7 @@ ...@@ -150,6 +150,7 @@
#define BRCMF_E_REASON_MINTXRATE 9 #define BRCMF_E_REASON_MINTXRATE 9
#define BRCMF_E_REASON_TXFAIL 10 #define BRCMF_E_REASON_TXFAIL 10
#define BRCMF_E_REASON_LINK_BSSCFG_DIS 4
#define BRCMF_E_REASON_FAST_ROAM_FAILED 5 #define BRCMF_E_REASON_FAST_ROAM_FAILED 5
#define BRCMF_E_REASON_DIRECTED_ROAM 6 #define BRCMF_E_REASON_DIRECTED_ROAM 6
#define BRCMF_E_REASON_TSPEC_REJECTED 7 #define BRCMF_E_REASON_TSPEC_REJECTED 7
......
...@@ -827,6 +827,26 @@ static int brcmf_p2p_request_p2p_if(struct brcmf_if *ifp, u8 ea[ETH_ALEN], ...@@ -827,6 +827,26 @@ static int brcmf_p2p_request_p2p_if(struct brcmf_if *ifp, u8 ea[ETH_ALEN],
return err; return err;
} }
static int brcmf_p2p_disable_p2p_if(struct brcmf_cfg80211_vif *vif)
{
struct brcmf_cfg80211_info *cfg = wdev_to_cfg(&vif->wdev);
struct net_device *pri_ndev = cfg_to_ndev(cfg);
struct brcmf_if *ifp = netdev_priv(pri_ndev);
u8 *addr = vif->wdev.netdev->dev_addr;
return brcmf_fil_iovar_data_set(ifp, "p2p_ifdis", addr, ETH_ALEN);
}
static int brcmf_p2p_release_p2p_if(struct brcmf_cfg80211_vif *vif)
{
struct brcmf_cfg80211_info *cfg = wdev_to_cfg(&vif->wdev);
struct net_device *pri_ndev = cfg_to_ndev(cfg);
struct brcmf_if *ifp = netdev_priv(pri_ndev);
u8 *addr = vif->wdev.netdev->dev_addr;
return brcmf_fil_iovar_data_set(ifp, "p2p_ifdel", addr, ETH_ALEN);
}
/** /**
* brcmf_p2p_add_vif() - create a new P2P virtual interface. * brcmf_p2p_add_vif() - create a new P2P virtual interface.
* *
...@@ -910,23 +930,43 @@ int brcmf_p2p_del_vif(struct wiphy *wiphy, struct wireless_dev *wdev) ...@@ -910,23 +930,43 @@ int brcmf_p2p_del_vif(struct wiphy *wiphy, struct wireless_dev *wdev)
{ {
struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy); struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
struct brcmf_cfg80211_vif *vif; struct brcmf_cfg80211_vif *vif;
unsigned long jiffie_timeout = msecs_to_jiffies(1500);
bool wait_for_disable = false;
int err; int err;
brcmf_dbg(TRACE, "delete P2P vif\n");
vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev); vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
if (brcmf_cfg80211_vif_event_armed(cfg)) switch (vif->wdev.iftype) {
return -EBUSY; case NL80211_IFTYPE_P2P_CLIENT:
if (test_bit(BRCMF_VIF_STATUS_DISCONNECTING, &vif->sme_state))
wait_for_disable = true;
break;
case NL80211_IFTYPE_P2P_GO:
if (!brcmf_p2p_disable_p2p_if(vif))
wait_for_disable = true;
break;
case NL80211_IFTYPE_P2P_DEVICE:
default:
return -ENOTSUPP;
break;
}
if (wait_for_disable)
wait_for_completion_timeout(&cfg->vif_disabled, 500);
brcmf_vif_clear_mgmt_ies(vif);
brcmf_cfg80211_arm_vif_event(cfg, vif); brcmf_cfg80211_arm_vif_event(cfg, vif);
/* wait for firmware event */ err = brcmf_p2p_release_p2p_if(vif);
err = brcmf_cfg80211_wait_vif_event_timeout(cfg, BRCMF_E_IF_DEL, if (!err)
msecs_to_jiffies(1500)); /* wait for firmware event */
err = brcmf_cfg80211_wait_vif_event_timeout(cfg, BRCMF_E_IF_DEL,
jiffie_timeout);
brcmf_cfg80211_arm_vif_event(cfg, NULL); brcmf_cfg80211_arm_vif_event(cfg, NULL);
if (wdev->netdev) brcmf_free_vif(vif);
brcmf_dbg(INFO, "deleting vif \"%s\"\n", wdev->netdev->name);
else return err;
brcmf_dbg(INFO, "deleting vif \"wdev-%u\"\n",
wdev->identifier);
brcmf_err("enter - not supported yet\n");
return -EOPNOTSUPP;
} }
...@@ -445,9 +445,103 @@ static struct wireless_dev *brcmf_cfg80211_add_iface(struct wiphy *wiphy, ...@@ -445,9 +445,103 @@ static struct wireless_dev *brcmf_cfg80211_add_iface(struct wiphy *wiphy,
} }
} }
static void brcmf_set_mpc(struct net_device *ndev, int mpc)
{
struct brcmf_if *ifp = netdev_priv(ndev);
s32 err = 0;
if (check_vif_up(ifp->vif)) {
err = brcmf_fil_iovar_int_set(ifp, "mpc", mpc);
if (err) {
brcmf_err("fail to set mpc\n");
return;
}
brcmf_dbg(INFO, "MPC : %d\n", mpc);
}
}
static s32
brcmf_notify_escan_complete(struct brcmf_cfg80211_info *cfg,
struct net_device *ndev,
bool aborted, bool fw_abort)
{
struct brcmf_scan_params_le params_le;
struct cfg80211_scan_request *scan_request;
s32 err = 0;
brcmf_dbg(SCAN, "Enter\n");
/* clear scan request, because the FW abort can cause a second call */
/* to this functon and might cause a double cfg80211_scan_done */
scan_request = cfg->scan_request;
cfg->scan_request = NULL;
if (timer_pending(&cfg->escan_timeout))
del_timer_sync(&cfg->escan_timeout);
if (fw_abort) {
/* Do a scan abort to stop the driver's scan engine */
brcmf_dbg(SCAN, "ABORT scan in firmware\n");
memset(&params_le, 0, sizeof(params_le));
memset(params_le.bssid, 0xFF, ETH_ALEN);
params_le.bss_type = DOT11_BSSTYPE_ANY;
params_le.scan_type = 0;
params_le.channel_num = cpu_to_le32(1);
params_le.nprobes = cpu_to_le32(1);
params_le.active_time = cpu_to_le32(-1);
params_le.passive_time = cpu_to_le32(-1);
params_le.home_time = cpu_to_le32(-1);
/* Scan is aborted by setting channel_list[0] to -1 */
params_le.channel_list[0] = cpu_to_le16(-1);
/* E-Scan (or anyother type) can be aborted by SCAN */
err = brcmf_fil_cmd_data_set(netdev_priv(ndev), BRCMF_C_SCAN,
&params_le, sizeof(params_le));
if (err)
brcmf_err("Scan abort failed\n");
}
/*
* e-scan can be initiated by scheduled scan
* which takes precedence.
*/
if (cfg->sched_escan) {
brcmf_dbg(SCAN, "scheduled scan completed\n");
cfg->sched_escan = false;
if (!aborted)
cfg80211_sched_scan_results(cfg_to_wiphy(cfg));
brcmf_set_mpc(ndev, 1);
} else if (scan_request) {
brcmf_dbg(SCAN, "ESCAN Completed scan: %s\n",
aborted ? "Aborted" : "Done");
cfg80211_scan_done(scan_request, aborted);
brcmf_set_mpc(ndev, 1);
}
if (!test_and_clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) {
brcmf_err("Scan complete while device not scanning\n");
return -EPERM;
}
return err;
}
static static
int brcmf_cfg80211_del_iface(struct wiphy *wiphy, struct wireless_dev *wdev) int brcmf_cfg80211_del_iface(struct wiphy *wiphy, struct wireless_dev *wdev)
{ {
struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
struct net_device *ndev = wdev->netdev;
/* vif event pending in firmware */
if (brcmf_cfg80211_vif_event_armed(cfg))
return -EBUSY;
if (ndev) {
if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status) &&
cfg->escan_info.ndev == ndev)
brcmf_notify_escan_complete(cfg, ndev, true,
true);
brcmf_fil_iovar_int_set(netdev_priv(ndev), "mpc", 1);
}
switch (wdev->iftype) { switch (wdev->iftype) {
case NL80211_IFTYPE_ADHOC: case NL80211_IFTYPE_ADHOC:
case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_STATION:
...@@ -525,21 +619,6 @@ brcmf_cfg80211_change_iface(struct wiphy *wiphy, struct net_device *ndev, ...@@ -525,21 +619,6 @@ brcmf_cfg80211_change_iface(struct wiphy *wiphy, struct net_device *ndev,
return err; return err;
} }
static void brcmf_set_mpc(struct net_device *ndev, int mpc)
{
struct brcmf_if *ifp = netdev_priv(ndev);
s32 err = 0;
if (check_vif_up(ifp->vif)) {
err = brcmf_fil_iovar_int_set(ifp, "mpc", mpc);
if (err) {
brcmf_err("fail to set mpc\n");
return;
}
brcmf_dbg(INFO, "MPC : %d\n", mpc);
}
}
static void brcmf_escan_prep(struct brcmf_scan_params_le *params_le, static void brcmf_escan_prep(struct brcmf_scan_params_le *params_le,
struct cfg80211_scan_request *request) struct cfg80211_scan_request *request)
{ {
...@@ -619,69 +698,6 @@ static void brcmf_escan_prep(struct brcmf_scan_params_le *params_le, ...@@ -619,69 +698,6 @@ static void brcmf_escan_prep(struct brcmf_scan_params_le *params_le,
(n_channels & BRCMF_SCAN_PARAMS_COUNT_MASK)); (n_channels & BRCMF_SCAN_PARAMS_COUNT_MASK));
} }
static s32
brcmf_notify_escan_complete(struct brcmf_cfg80211_info *cfg,
struct net_device *ndev,
bool aborted, bool fw_abort)
{
struct brcmf_scan_params_le params_le;
struct cfg80211_scan_request *scan_request;
s32 err = 0;
brcmf_dbg(SCAN, "Enter\n");
/* clear scan request, because the FW abort can cause a second call */
/* to this functon and might cause a double cfg80211_scan_done */
scan_request = cfg->scan_request;
cfg->scan_request = NULL;
if (timer_pending(&cfg->escan_timeout))
del_timer_sync(&cfg->escan_timeout);
if (fw_abort) {
/* Do a scan abort to stop the driver's scan engine */
brcmf_dbg(SCAN, "ABORT scan in firmware\n");
memset(&params_le, 0, sizeof(params_le));
memset(params_le.bssid, 0xFF, ETH_ALEN);
params_le.bss_type = DOT11_BSSTYPE_ANY;
params_le.scan_type = 0;
params_le.channel_num = cpu_to_le32(1);
params_le.nprobes = cpu_to_le32(1);
params_le.active_time = cpu_to_le32(-1);
params_le.passive_time = cpu_to_le32(-1);
params_le.home_time = cpu_to_le32(-1);
/* Scan is aborted by setting channel_list[0] to -1 */
params_le.channel_list[0] = cpu_to_le16(-1);
/* E-Scan (or anyother type) can be aborted by SCAN */
err = brcmf_fil_cmd_data_set(netdev_priv(ndev), BRCMF_C_SCAN,
&params_le, sizeof(params_le));
if (err)
brcmf_err("Scan abort failed\n");
}
/*
* e-scan can be initiated by scheduled scan
* which takes precedence.
*/
if (cfg->sched_escan) {
brcmf_dbg(SCAN, "scheduled scan completed\n");
cfg->sched_escan = false;
if (!aborted)
cfg80211_sched_scan_results(cfg_to_wiphy(cfg));
brcmf_set_mpc(ndev, 1);
} else if (scan_request) {
brcmf_dbg(SCAN, "ESCAN Completed scan: %s\n",
aborted ? "Aborted" : "Done");
cfg80211_scan_done(scan_request, aborted);
brcmf_set_mpc(ndev, 1);
}
if (!test_and_clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) {
brcmf_err("Scan complete while device not scanning\n");
return -EPERM;
}
return err;
}
static s32 static s32
brcmf_run_escan(struct brcmf_cfg80211_info *cfg, struct net_device *ndev, brcmf_run_escan(struct brcmf_cfg80211_info *cfg, struct net_device *ndev,
struct cfg80211_scan_request *request, u16 action) struct cfg80211_scan_request *request, u16 action)
...@@ -3474,6 +3490,22 @@ s32 brcmf_vif_set_mgmt_ie(struct brcmf_cfg80211_vif *vif, s32 pktflag, ...@@ -3474,6 +3490,22 @@ s32 brcmf_vif_set_mgmt_ie(struct brcmf_cfg80211_vif *vif, s32 pktflag,
return err; return err;
} }
s32 brcmf_vif_clear_mgmt_ies(struct brcmf_cfg80211_vif *vif)
{
s32 pktflags[] = {
BRCMF_VNDR_IE_PRBREQ_FLAG,
BRCMF_VNDR_IE_PRBRSP_FLAG,
BRCMF_VNDR_IE_BEACON_FLAG
};
int i;
for (i = 0; i < ARRAY_SIZE(pktflags); i++)
brcmf_vif_set_mgmt_ie(vif, pktflags[i], NULL, 0);
memset(&vif->saved_ie, 0, sizeof(vif->saved_ie));
return 0;
}
static s32 static s32
brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev, brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev,
struct cfg80211_ap_settings *settings) struct cfg80211_ap_settings *settings)
...@@ -4260,6 +4292,12 @@ brcmf_notify_connect_status_ap(struct brcmf_cfg80211_info *cfg, ...@@ -4260,6 +4292,12 @@ brcmf_notify_connect_status_ap(struct brcmf_cfg80211_info *cfg,
struct station_info sinfo; struct station_info sinfo;
brcmf_dbg(CONN, "event %d, reason %d\n", event, reason); brcmf_dbg(CONN, "event %d, reason %d\n", event, reason);
if (event == BRCMF_E_LINK && reason == BRCMF_E_REASON_LINK_BSSCFG_DIS &&
ndev != cfg_to_ndev(cfg)) {
brcmf_dbg(CONN, "AP mode link down\n");
complete(&cfg->vif_disabled);
return 0;
}
if (((event == BRCMF_E_ASSOC_IND) || (event == BRCMF_E_REASSOC_IND)) && if (((event == BRCMF_E_ASSOC_IND) || (event == BRCMF_E_REASSOC_IND)) &&
(reason == BRCMF_E_STATUS_SUCCESS)) { (reason == BRCMF_E_STATUS_SUCCESS)) {
...@@ -4316,6 +4354,8 @@ brcmf_notify_connect_status(struct brcmf_if *ifp, ...@@ -4316,6 +4354,8 @@ brcmf_notify_connect_status(struct brcmf_if *ifp,
} }
brcmf_link_down(ifp->vif); brcmf_link_down(ifp->vif);
brcmf_init_prof(ndev_to_prof(ndev)); brcmf_init_prof(ndev_to_prof(ndev));
if (ndev != cfg_to_ndev(cfg))
complete(&cfg->vif_disabled);
} else if (brcmf_is_nonetwork(cfg, e)) { } else if (brcmf_is_nonetwork(cfg, e)) {
if (brcmf_is_ibssmode(ifp->vif)) if (brcmf_is_ibssmode(ifp->vif))
clear_bit(BRCMF_VIF_STATUS_CONNECTING, clear_bit(BRCMF_VIF_STATUS_CONNECTING,
...@@ -4401,7 +4441,6 @@ static s32 brcmf_notify_vif_event(struct brcmf_if *ifp, ...@@ -4401,7 +4441,6 @@ static s32 brcmf_notify_vif_event(struct brcmf_if *ifp,
case BRCMF_E_IF_DEL: case BRCMF_E_IF_DEL:
ifp->vif = NULL; ifp->vif = NULL;
brcmf_free_vif(vif);
mutex_unlock(&event->vif_event_lock); mutex_unlock(&event->vif_event_lock);
/* event may not be upon user request */ /* event may not be upon user request */
if (brcmf_cfg80211_vif_event_armed(cfg)) if (brcmf_cfg80211_vif_event_armed(cfg))
...@@ -4507,7 +4546,7 @@ static s32 wl_init_priv(struct brcmf_cfg80211_info *cfg) ...@@ -4507,7 +4546,7 @@ static s32 wl_init_priv(struct brcmf_cfg80211_info *cfg)
mutex_init(&cfg->usr_sync); mutex_init(&cfg->usr_sync);
brcmf_init_escan(cfg); brcmf_init_escan(cfg);
brcmf_init_conf(cfg->conf); brcmf_init_conf(cfg->conf);
init_completion(&cfg->vif_disabled);
return err; return err;
} }
......
...@@ -148,6 +148,7 @@ struct brcmf_cfg80211_profile { ...@@ -148,6 +148,7 @@ struct brcmf_cfg80211_profile {
* @BRCMF_VIF_STATUS_READY: ready for operation. * @BRCMF_VIF_STATUS_READY: ready for operation.
* @BRCMF_VIF_STATUS_CONNECTING: connect/join in progress. * @BRCMF_VIF_STATUS_CONNECTING: connect/join in progress.
* @BRCMF_VIF_STATUS_CONNECTED: connected/joined succesfully. * @BRCMF_VIF_STATUS_CONNECTED: connected/joined succesfully.
* @BRCMF_VIF_STATUS_DISCONNECTING: disconnect/disable in progress.
* @BRCMF_VIF_STATUS_AP_CREATING: interface configured for AP operation. * @BRCMF_VIF_STATUS_AP_CREATING: interface configured for AP operation.
* @BRCMF_VIF_STATUS_AP_CREATED: AP operation started. * @BRCMF_VIF_STATUS_AP_CREATED: AP operation started.
*/ */
...@@ -155,6 +156,7 @@ enum brcmf_vif_status { ...@@ -155,6 +156,7 @@ enum brcmf_vif_status {
BRCMF_VIF_STATUS_READY, BRCMF_VIF_STATUS_READY,
BRCMF_VIF_STATUS_CONNECTING, BRCMF_VIF_STATUS_CONNECTING,
BRCMF_VIF_STATUS_CONNECTED, BRCMF_VIF_STATUS_CONNECTED,
BRCMF_VIF_STATUS_DISCONNECTING,
BRCMF_VIF_STATUS_AP_CREATING, BRCMF_VIF_STATUS_AP_CREATING,
BRCMF_VIF_STATUS_AP_CREATED BRCMF_VIF_STATUS_AP_CREATED
}; };
...@@ -405,6 +407,7 @@ struct brcmf_cfg80211_info { ...@@ -405,6 +407,7 @@ struct brcmf_cfg80211_info {
struct list_head vif_list; struct list_head vif_list;
u8 vif_cnt; u8 vif_cnt;
struct brcmf_cfg80211_vif_event vif_event; struct brcmf_cfg80211_vif_event vif_event;
struct completion vif_disabled;
}; };
/** /**
...@@ -479,6 +482,7 @@ void brcmf_free_vif(struct brcmf_cfg80211_vif *vif); ...@@ -479,6 +482,7 @@ void brcmf_free_vif(struct brcmf_cfg80211_vif *vif);
s32 brcmf_vif_set_mgmt_ie(struct brcmf_cfg80211_vif *vif, s32 pktflag, s32 brcmf_vif_set_mgmt_ie(struct brcmf_cfg80211_vif *vif, s32 pktflag,
const u8 *vndr_ie_buf, u32 vndr_ie_len); const u8 *vndr_ie_buf, u32 vndr_ie_len);
s32 brcmf_vif_clear_mgmt_ies(struct brcmf_cfg80211_vif *vif);
struct brcmf_tlv *brcmf_parse_tlvs(void *buf, int buflen, uint key); struct brcmf_tlv *brcmf_parse_tlvs(void *buf, int buflen, uint key);
u16 channel_to_chanspec(struct ieee80211_channel *ch); u16 channel_to_chanspec(struct ieee80211_channel *ch);
u32 wl_get_vif_state_all(struct brcmf_cfg80211_info *cfg, unsigned long state); u32 wl_get_vif_state_all(struct brcmf_cfg80211_info *cfg, unsigned long state);
......
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