Skip to content

Commit

Permalink
brcmfmac: implement support for deleting virtual interfaces
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Arend van Spriel authored and John W. Linville committed Feb 8, 2013
1 parent de66efc commit 5f4f9f1
Show file tree
Hide file tree
Showing 4 changed files with 176 additions and 92 deletions.
1 change: 1 addition & 0 deletions drivers/net/wireless/brcm80211/brcmfmac/dhd.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
64 changes: 52 additions & 12 deletions drivers/net/wireless/brcm80211/brcmfmac/p2p.c
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*
Expand Down Expand Up @@ -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;
}
199 changes: 119 additions & 80 deletions drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
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:
Expand Down Expand Up @@ -525,21 +619,6 @@ brcmf_cfg80211_change_iface(struct wiphy *wiphy, struct net_device *ndev,
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)
{
Expand Down Expand Up @@ -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(&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
brcmf_run_escan(struct brcmf_cfg80211_info *cfg, struct net_device *ndev,
struct cfg80211_scan_request *request, u16 action)
Expand Down Expand Up @@ -3474,6 +3490,22 @@ s32 brcmf_vif_set_mgmt_ie(struct brcmf_cfg80211_vif *vif, s32 pktflag,
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)
Expand Down Expand Up @@ -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)) {
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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))
Expand Down Expand Up @@ -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;
}

Expand Down
4 changes: 4 additions & 0 deletions drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h
Original file line number Diff line number Diff line change
Expand Up @@ -148,13 +148,15 @@ 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.
*/
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
};
Expand Down Expand Up @@ -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;
};

/**
Expand Down Expand Up @@ -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);
Expand Down

0 comments on commit 5f4f9f1

Please sign in to comment.