forked from Minki/linux
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: Pieter-Paul Giesberts <pieterpg@broadcom.com> Reviewed-by: Hante Meuleman <meuleman@broadcom.com> Signed-off-by: Arend van Spriel <arend@broadcom.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
This commit is contained in:
parent
de66efcadb
commit
5f4f9f114e
@ -150,6 +150,7 @@
|
||||
#define BRCMF_E_REASON_MINTXRATE 9
|
||||
#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_DIRECTED_ROAM 6
|
||||
#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],
|
||||
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.
|
||||
*
|
||||
@ -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_vif *vif;
|
||||
unsigned long jiffie_timeout = msecs_to_jiffies(1500);
|
||||
bool wait_for_disable = false;
|
||||
int err;
|
||||
|
||||
brcmf_dbg(TRACE, "delete P2P vif\n");
|
||||
vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
|
||||
|
||||
if (brcmf_cfg80211_vif_event_armed(cfg))
|
||||
return -EBUSY;
|
||||
switch (vif->wdev.iftype) {
|
||||
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);
|
||||
/* wait for firmware event */
|
||||
err = brcmf_cfg80211_wait_vif_event_timeout(cfg, BRCMF_E_IF_DEL,
|
||||
msecs_to_jiffies(1500));
|
||||
err = brcmf_p2p_release_p2p_if(vif);
|
||||
if (!err)
|
||||
/* 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);
|
||||
if (wdev->netdev)
|
||||
brcmf_dbg(INFO, "deleting vif \"%s\"\n", wdev->netdev->name);
|
||||
else
|
||||
brcmf_dbg(INFO, "deleting vif \"wdev-%u\"\n",
|
||||
wdev->identifier);
|
||||
brcmf_err("enter - not supported yet\n");
|
||||
return -EOPNOTSUPP;
|
||||
brcmf_free_vif(vif);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
@ -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(¶ms_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,
|
||||
¶ms_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
|
||||
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) {
|
||||
case NL80211_IFTYPE_ADHOC:
|
||||
case NL80211_IFTYPE_STATION:
|
||||
@ -525,21 +619,6 @@ done:
|
||||
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,
|
||||
struct cfg80211_scan_request *request)
|
||||
{
|
||||
@ -619,69 +698,6 @@ static void brcmf_escan_prep(struct brcmf_scan_params_le *params_le,
|
||||
(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(¶ms_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,
|
||||
¶ms_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
|
||||
brcmf_run_escan(struct brcmf_cfg80211_info *cfg, struct net_device *ndev,
|
||||
struct cfg80211_scan_request *request, u16 action)
|
||||
@ -3474,6 +3490,22 @@ exit:
|
||||
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
|
||||
brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev,
|
||||
struct cfg80211_ap_settings *settings)
|
||||
@ -4260,6 +4292,12 @@ brcmf_notify_connect_status_ap(struct brcmf_cfg80211_info *cfg,
|
||||
struct station_info sinfo;
|
||||
|
||||
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)) &&
|
||||
(reason == BRCMF_E_STATUS_SUCCESS)) {
|
||||
@ -4316,6 +4354,8 @@ brcmf_notify_connect_status(struct brcmf_if *ifp,
|
||||
}
|
||||
brcmf_link_down(ifp->vif);
|
||||
brcmf_init_prof(ndev_to_prof(ndev));
|
||||
if (ndev != cfg_to_ndev(cfg))
|
||||
complete(&cfg->vif_disabled);
|
||||
} else if (brcmf_is_nonetwork(cfg, e)) {
|
||||
if (brcmf_is_ibssmode(ifp->vif))
|
||||
clear_bit(BRCMF_VIF_STATUS_CONNECTING,
|
||||
@ -4401,7 +4441,6 @@ static s32 brcmf_notify_vif_event(struct brcmf_if *ifp,
|
||||
|
||||
case BRCMF_E_IF_DEL:
|
||||
ifp->vif = NULL;
|
||||
brcmf_free_vif(vif);
|
||||
mutex_unlock(&event->vif_event_lock);
|
||||
/* event may not be upon user request */
|
||||
if (brcmf_cfg80211_vif_event_armed(cfg))
|
||||
@ -4507,7 +4546,7 @@ static s32 wl_init_priv(struct brcmf_cfg80211_info *cfg)
|
||||
mutex_init(&cfg->usr_sync);
|
||||
brcmf_init_escan(cfg);
|
||||
brcmf_init_conf(cfg->conf);
|
||||
|
||||
init_completion(&cfg->vif_disabled);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
@ -148,6 +148,7 @@ struct brcmf_cfg80211_profile {
|
||||
* @BRCMF_VIF_STATUS_READY: ready for operation.
|
||||
* @BRCMF_VIF_STATUS_CONNECTING: connect/join in progress.
|
||||
* @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_CREATED: AP operation started.
|
||||
*/
|
||||
@ -155,6 +156,7 @@ enum brcmf_vif_status {
|
||||
BRCMF_VIF_STATUS_READY,
|
||||
BRCMF_VIF_STATUS_CONNECTING,
|
||||
BRCMF_VIF_STATUS_CONNECTED,
|
||||
BRCMF_VIF_STATUS_DISCONNECTING,
|
||||
BRCMF_VIF_STATUS_AP_CREATING,
|
||||
BRCMF_VIF_STATUS_AP_CREATED
|
||||
};
|
||||
@ -405,6 +407,7 @@ struct brcmf_cfg80211_info {
|
||||
struct list_head vif_list;
|
||||
u8 vif_cnt;
|
||||
struct brcmf_cfg80211_vif_event vif_event;
|
||||
struct completion vif_disabled;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -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,
|
||||
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);
|
||||
u16 channel_to_chanspec(struct ieee80211_channel *ch);
|
||||
u32 wl_get_vif_state_all(struct brcmf_cfg80211_info *cfg, unsigned long state);
|
||||
|
Loading…
Reference in New Issue
Block a user