Commit 6eda4e2c authored by Hante Meuleman's avatar Hante Meuleman Committed by John W. Linville

brcmfmac: Add tx p2p off-channel support.

With this patch off-channel action frames can be transmitted. This
is needed for p2p when two devices need to find each other while
using different social channels.
Reviewed-by: default avatarArend Van Spriel <arend@broadcom.com>
Reviewed-by: default avatarPieter-Paul Giesberts <pieterpg@broadcom.com>
Reviewed-by: default avatarPiotr Haber <phaber@broadcom.com>
Signed-off-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 4b3a89de
......@@ -58,6 +58,10 @@ struct p2p_bss {
* @BRCMF_P2P_STATUS_ACTION_TX_NOACK: action frame tx not acked.
* @BRCMF_P2P_STATUS_GO_NEG_PHASE: P2P GO negotiation ongoing.
* @BRCMF_P2P_STATUS_DISCOVER_LISTEN: P2P listen, remaining on channel.
* @BRCMF_P2P_STATUS_SENDING_ACT_FRAME: In the process of sending action frame.
* @BRCMF_P2P_STATUS_WAITING_NEXT_AF_LISTEN: extra listen time for af tx.
* @BRCMF_P2P_STATUS_WAITING_NEXT_ACT_FRAME: waiting for action frame response.
* @BRCMF_P2P_STATUS_FINDING_COMMON_CHANNEL: search channel for AF active.
*/
enum brcmf_p2p_status {
BRCMF_P2P_STATUS_ENABLED,
......@@ -69,7 +73,34 @@ enum brcmf_p2p_status {
BRCMF_P2P_STATUS_ACTION_TX_COMPLETED,
BRCMF_P2P_STATUS_ACTION_TX_NOACK,
BRCMF_P2P_STATUS_GO_NEG_PHASE,
BRCMF_P2P_STATUS_DISCOVER_LISTEN
BRCMF_P2P_STATUS_DISCOVER_LISTEN,
BRCMF_P2P_STATUS_SENDING_ACT_FRAME,
BRCMF_P2P_STATUS_WAITING_NEXT_AF_LISTEN,
BRCMF_P2P_STATUS_WAITING_NEXT_ACT_FRAME,
BRCMF_P2P_STATUS_FINDING_COMMON_CHANNEL
};
/**
* struct afx_hdl - action frame off channel storage.
*
* @afx_work: worker thread for searching channel
* @act_frm_scan: thread synchronizing struct.
* @is_active: channel searching active.
* @peer_chan: current channel.
* @is_listen: sets mode for afx worker.
* @my_listen_chan: this peers listen channel.
* @peer_listen_chan: remote peers listen channel.
* @tx_dst_addr: mac address where tx af should be sent to.
*/
struct afx_hdl {
struct work_struct afx_work;
struct completion act_frm_scan;
bool is_active;
s32 peer_chan;
bool is_listen;
u16 my_listen_chan;
u16 peer_listen_chan;
u8 tx_dst_addr[ETH_ALEN];
};
/**
......@@ -87,6 +118,12 @@ enum brcmf_p2p_status {
* @remain_on_channel_cookie: cookie counter for remain on channel cmd
* @next_af_subtype: expected action frame subtype.
* @send_af_done: indication that action frame tx is complete.
* @afx_hdl: action frame search handler info.
* @af_sent_channel: channel action frame is sent.
* @af_tx_sent_jiffies: jiffies time when af tx was transmitted.
* @wait_next_af: thread synchronizing struct.
* @gon_req_action: about to send go negotiation requets frame.
* @block_gon_req_tx: drop tx go negotiation requets frame.
*/
struct brcmf_p2p_info {
struct brcmf_cfg80211_info *cfg;
......@@ -101,6 +138,12 @@ struct brcmf_p2p_info {
u32 remain_on_channel_cookie;
u8 next_af_subtype;
struct completion send_af_done;
struct afx_hdl afx_hdl;
u32 af_sent_channel;
unsigned long af_tx_sent_jiffies;
struct completion wait_next_af;
bool gon_req_action;
bool block_gon_req_tx;
};
s32 brcmf_p2p_attach(struct brcmf_cfg80211_info *cfg);
......@@ -132,5 +175,9 @@ int brcmf_p2p_notify_action_tx_complete(struct brcmf_if *ifp,
bool brcmf_p2p_send_action_frame(struct brcmf_cfg80211_info *cfg,
struct net_device *ndev,
struct brcmf_fil_af_params_le *af_params);
bool brcmf_p2p_scan_finding_common_channel(struct brcmf_cfg80211_info *cfg,
struct brcmf_bss_info_le *bi);
s32 brcmf_p2p_notify_rx_mgmt_p2p_probereq(struct brcmf_if *ifp,
const struct brcmf_event_msg *e,
void *data);
#endif /* WL_CFGP2P_H_ */
......@@ -601,10 +601,8 @@ brcmf_notify_escan_complete(struct brcmf_cfg80211_info *cfg,
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;
}
if (!test_and_clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status))
brcmf_dbg(SCAN, "Scan complete, probably P2P scan\n");
return err;
}
......@@ -2525,8 +2523,7 @@ static void brcmf_cfg80211_escan_timeout_worker(struct work_struct *work)
container_of(work, struct brcmf_cfg80211_info,
escan_timeout_work);
brcmf_notify_escan_complete(cfg,
cfg->escan_info.ndev, true, true);
brcmf_notify_escan_complete(cfg, cfg->escan_info.ndev, true, true);
}
static void brcmf_escan_timeout(unsigned long data)
......@@ -2603,11 +2600,6 @@ brcmf_cfg80211_escan_handler(struct brcmf_if *ifp,
brcmf_err("Invalid escan result (NULL pointer)\n");
goto exit;
}
if (!cfg->scan_request) {
brcmf_dbg(SCAN, "result without cfg80211 request\n");
goto exit;
}
if (le16_to_cpu(escan_result_le->bss_count) != 1) {
brcmf_err("Invalid bss_count %d: ignoring\n",
escan_result_le->bss_count);
......@@ -2615,6 +2607,14 @@ brcmf_cfg80211_escan_handler(struct brcmf_if *ifp,
}
bss_info_le = &escan_result_le->bss_info_le;
if (brcmf_p2p_scan_finding_common_channel(cfg, bss_info_le))
goto exit;
if (!cfg->scan_request) {
brcmf_dbg(SCAN, "result without cfg80211 request\n");
goto exit;
}
bi_length = le32_to_cpu(bss_info_le->length);
if (bi_length != (le32_to_cpu(escan_result_le->buflen) -
WL_ESCAN_RESULTS_FIXED_SIZE)) {
......@@ -2653,6 +2653,8 @@ brcmf_cfg80211_escan_handler(struct brcmf_if *ifp,
list->count++;
} else {
cfg->escan_info.escan_state = WL_ESCAN_STATE_IDLE;
if (brcmf_p2p_scan_finding_common_channel(cfg, NULL))
goto exit;
if (cfg->scan_request) {
cfg->bss_list = (struct brcmf_scan_results *)
cfg->escan_info.escan_buf;
......@@ -2661,7 +2663,8 @@ brcmf_cfg80211_escan_handler(struct brcmf_if *ifp,
brcmf_notify_escan_complete(cfg, ndev, aborted,
false);
} else
brcmf_err("Unexpected scan result 0x%x\n", status);
brcmf_dbg(SCAN, "Ignored scan complete result 0x%x\n",
status);
}
exit:
return err;
......@@ -4038,50 +4041,6 @@ brcmf_cfg80211_cancel_remain_on_channel(struct wiphy *wiphy,
return err;
}
static s32 brcmf_notify_rx_mgmt_p2p_probereq(struct brcmf_if *ifp,
const struct brcmf_event_msg *e,
void *data)
{
struct wireless_dev *wdev;
struct brcmf_cfg80211_vif *vif = ifp->vif;
struct brcmf_rx_mgmt_data *rxframe = (struct brcmf_rx_mgmt_data *)data;
u16 chanspec = be16_to_cpu(rxframe->chanspec);
u8 *mgmt_frame;
u32 mgmt_frame_len;
s32 freq;
u16 mgmt_type;
brcmf_dbg(INFO,
"Enter: event %d reason %d\n", e->event_code, e->reason);
/* Firmware sends us two proberesponses for each idx one. At the */
/* moment anything but bsscfgidx 0 is passed up to supplicant */
if (e->bsscfgidx == 0)
return 0;
/* Check if wpa_supplicant has registered for this frame */
brcmf_dbg(INFO, "vif->mgmt_rx_reg %04x\n", vif->mgmt_rx_reg);
mgmt_type = (IEEE80211_STYPE_PROBE_REQ & IEEE80211_FCTL_STYPE) >> 4;
if ((vif->mgmt_rx_reg & BIT(mgmt_type)) == 0)
return 0;
mgmt_frame = (u8 *)(rxframe + 1);
mgmt_frame_len = e->datalen - sizeof(*rxframe);
freq = ieee80211_channel_to_frequency(CHSPEC_CHANNEL(chanspec),
CHSPEC_IS2G(chanspec) ?
IEEE80211_BAND_2GHZ :
IEEE80211_BAND_5GHZ);
wdev = ifp->ndev->ieee80211_ptr;
cfg80211_rx_mgmt(wdev, freq, 0, mgmt_frame, mgmt_frame_len, GFP_ATOMIC);
brcmf_dbg(INFO,
"mgmt_frame_len (%d) , e->datalen (%d), chanspec (%04x), freq (%d)\n",
mgmt_frame_len, e->datalen, chanspec, freq);
return 0;
}
static struct cfg80211_ops wl_cfg80211_ops = {
.add_virtual_intf = brcmf_cfg80211_add_iface,
.del_virtual_intf = brcmf_cfg80211_del_iface,
......@@ -4240,6 +4199,7 @@ static struct wiphy *brcmf_setup_wiphy(struct device *phydev)
wiphy->cipher_suites = __wl_cipher_suites;
wiphy->n_cipher_suites = ARRAY_SIZE(__wl_cipher_suites);
wiphy->flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT |
WIPHY_FLAG_OFFCHAN_TX |
WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
wiphy->mgmt_stypes = brcmf_txrx_stypes;
wiphy->max_remain_on_channel_duration = 5000;
......@@ -4721,13 +4681,15 @@ static void brcmf_register_event_handlers(struct brcmf_cfg80211_info *cfg)
brcmf_fweh_register(cfg->pub, BRCMF_E_IF,
brcmf_notify_vif_event);
brcmf_fweh_register(cfg->pub, BRCMF_E_P2P_PROBEREQ_MSG,
brcmf_notify_rx_mgmt_p2p_probereq);
brcmf_p2p_notify_rx_mgmt_p2p_probereq);
brcmf_fweh_register(cfg->pub, BRCMF_E_P2P_DISC_LISTEN_COMPLETE,
brcmf_p2p_notify_listen_complete);
brcmf_fweh_register(cfg->pub, BRCMF_E_ACTION_FRAME_RX,
brcmf_p2p_notify_action_frame_rx);
brcmf_fweh_register(cfg->pub, BRCMF_E_ACTION_FRAME_COMPLETE,
brcmf_p2p_notify_action_tx_complete);
brcmf_fweh_register(cfg->pub, BRCMF_E_ACTION_FRAME_OFF_CHAN_COMPLETE,
brcmf_p2p_notify_action_tx_complete);
}
static void brcmf_deinit_priv_mem(struct brcmf_cfg80211_info *cfg)
......
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