Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 18e2f61d authored by Hante Meuleman's avatar Hante Meuleman Committed by John W. Linville
Browse files

brcmfmac: P2P action frame tx.



With this patch it is possible to send action frames.

Reviewed-by: default avatarArend Van Spriel <arend@broadcom.com>
Reviewed-by: default avatarPieter-Paul Giesberts <pieterpg@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 e6da3400
Loading
Loading
Loading
Loading
+19 −0
Original line number Diff line number Diff line
@@ -20,6 +20,10 @@

#include <linux/if_ether.h>


#define BRCMF_FIL_ACTION_FRAME_SIZE	1800


enum brcmf_fil_p2p_if_types {
	BRCMF_FIL_P2P_IF_CLIENT,
	BRCMF_FIL_P2P_IF_GO,
@@ -39,6 +43,21 @@ struct brcmf_fil_chan_info_le {
	__le32 scan_channel;
};

struct brcmf_fil_action_frame_le {
	u8	da[ETH_ALEN];
	__le16	len;
	__le32	packet_id;
	u8	data[BRCMF_FIL_ACTION_FRAME_SIZE];
};

struct brcmf_fil_af_params_le {
	__le32					channel;
	__le32					dwell_time;
	u8					bssid[ETH_ALEN];
	u8					pad[2];
	struct brcmf_fil_action_frame_le	action_frame;
};

struct brcmf_fil_bss_enable_le {
	__le32 bsscfg_idx;
	__le32 enable;
+291 −3
Original line number Diff line number Diff line
@@ -55,6 +55,21 @@
#define P2P_OUI			"\x50\x6F\x9A"	/* P2P OUI */
#define P2P_OUI_LEN		3		/* P2P OUI length */

/* Action Frame Constants */
#define DOT11_ACTION_HDR_LEN	2	/* action frame category + action */
#define DOT11_ACTION_CAT_OFF	0	/* category offset */
#define DOT11_ACTION_ACT_OFF	1	/* action offset */

#define P2P_AF_DWELL_TIME		200
#define P2P_AF_MIN_DWELL_TIME		100
#define P2P_AF_MED_DWELL_TIME		400
#define P2P_AF_LONG_DWELL_TIME		1000
#define P2P_AF_TX_MAX_RETRY		5
#define P2P_AF_MAX_WAIT_TIME		2000
#define P2P_INVALID_CHANNEL		-1
#define P2P_CHANNEL_SYNC_RETRY		5
#define P2P_AF_FRM_SCAN_MAX_WAIT	1500

/* WiFi P2P Public Action Frame OUI Subtypes */
#define P2P_PAF_GON_REQ		0	/* Group Owner Negotiation Req */
#define P2P_PAF_GON_RSP		1	/* Group Owner Negotiation Rsp */
@@ -178,6 +193,17 @@ struct brcmf_p2psd_gas_pub_act_frame {
	u8	query_data[1];
};

/**
 * struct brcmf_config_af_params - Action Frame Parameters for tx.
 *
 * @max_tx_retry: max tx retry count if tx no ack.
 * @mpc_onoff: To make sure to send successfully action frame, we have to
 *             turn off mpc  0: off, 1: on,  (-1): do nothing
 */
struct brcmf_config_af_params {
	s32 max_tx_retry;
	s32 mpc_onoff;
};

/**
 * brcmf_p2p_is_pub_action() - true if p2p public type frame.
@@ -590,8 +616,6 @@ static s32 brcmf_p2p_deinit_discovery(struct brcmf_p2p_info *p2p)
 * brcmf_p2p_enable_discovery() - initialize and configure discovery.
 *
 * @p2p: P2P specific data.
 * @ie: buffer containing information elements.
 * @ie_len: length of @ie buffer.
 *
 * Initializes the discovery device and configure the virtual interface.
 */
@@ -731,7 +755,7 @@ static s32 brcmf_p2p_escan(struct brcmf_p2p_info *p2p, u32 num_chans,
	/* Override scan params to find a peer for a connection */
	if (num_chans == 1) {
		active = WL_SCAN_CONNECT_DWELL_TIME_MS;
		/* XXX WAR to sync with presence period of VSDB GO.
		/* WAR to sync with presence period of VSDB GO.
		 * send probe request more frequently
		 */
		nprobes = active / WL_SCAN_JOIN_PROBE_INTERVAL_MS;
@@ -1031,6 +1055,269 @@ int brcmf_p2p_notify_action_frame_rx(struct brcmf_if *ifp,
}


/**
 * brcmf_p2p_notify_action_tx_complete() - transmit action frame complete
 *
 * @ifp: interfac control.
 * @e: event message. Not used, to make it usable for fweh event dispatcher.
 * @data: not used.
 *
 */
int brcmf_p2p_notify_action_tx_complete(struct brcmf_if *ifp,
					const struct brcmf_event_msg *e,
					void *data)
{
	struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
	struct brcmf_p2p_info *p2p = &cfg->p2p;

	brcmf_dbg(INFO, "Enter: status %d\n", e->status);

	if (e->status == BRCMF_E_STATUS_SUCCESS)
		set_bit(BRCMF_P2P_STATUS_ACTION_TX_COMPLETED, &p2p->status);
	else
		set_bit(BRCMF_P2P_STATUS_ACTION_TX_NOACK, &p2p->status);
	/* for now complete the receiver process here !! */
	complete(&p2p->send_af_done);

	return 0;
}


/**
 * brcmf_p2p_tx_action_frame() - send action frame over fil.
 *
 * @p2p: p2p info struct for vif.
 * @af_params: action frame data/info.
 *
 * Send an action frame immediately without doing channel synchronization.
 *
 * This function waits for a completion event before returning.
 * The WLC_E_ACTION_FRAME_COMPLETE event will be received when the action
 * frame is transmitted.
 */
static s32 brcmf_p2p_tx_action_frame(struct brcmf_p2p_info *p2p,
				     struct brcmf_fil_af_params_le *af_params)
{
	struct brcmf_cfg80211_vif *vif;
	s32 err = 0;
	s32 timeout = 0;

	brcmf_dbg(TRACE, "Enter\n");

	clear_bit(BRCMF_P2P_STATUS_ACTION_TX_COMPLETED, &p2p->status);
	clear_bit(BRCMF_P2P_STATUS_ACTION_TX_NOACK, &p2p->status);

	vif = p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif;
	err = brcmf_fil_bsscfg_data_set(vif->ifp, "actframe", af_params,
					sizeof(*af_params));
	if (err) {
		brcmf_err(" sending action frame has failed\n");
		goto exit;
	}

	timeout = wait_for_completion_timeout(&p2p->send_af_done,
					msecs_to_jiffies(P2P_AF_MAX_WAIT_TIME));

	if (test_bit(BRCMF_P2P_STATUS_ACTION_TX_COMPLETED, &p2p->status)) {
		brcmf_dbg(TRACE, "TX action frame operation is success\n");
	} else {
		err = -EIO;
		brcmf_dbg(TRACE, "TX action frame operation has failed\n");
	}
	/* clear status bit for action tx */
	clear_bit(BRCMF_P2P_STATUS_ACTION_TX_COMPLETED, &p2p->status);
	clear_bit(BRCMF_P2P_STATUS_ACTION_TX_NOACK, &p2p->status);

exit:
	return err;
}


/**
 * brcmf_p2p_pub_af_tx() - public action frame tx routine.
 *
 * @cfg: driver private data for cfg80211 interface.
 * @af_params: action frame data/info.
 * @config_af_params: configuration data for action frame.
 *
 * routine which transmits ation frame public type.
 */
static s32 brcmf_p2p_pub_af_tx(struct brcmf_cfg80211_info *cfg,
			       struct brcmf_fil_af_params_le *af_params,
			       struct brcmf_config_af_params *config_af_params)
{
	struct brcmf_p2p_info *p2p = &cfg->p2p;
	struct brcmf_fil_action_frame_le *action_frame;
	struct brcmf_p2p_pub_act_frame *act_frm;
	s32 err = 0;

	action_frame = &af_params->action_frame;
	act_frm = (struct brcmf_p2p_pub_act_frame *)(action_frame->data);

	switch (act_frm->subtype) {
	case P2P_PAF_GON_REQ:
		brcmf_dbg(TRACE, "P2P: GO_NEG_PHASE status set\n");
		set_bit(BRCMF_P2P_STATUS_GO_NEG_PHASE, &p2p->status);
		config_af_params->mpc_onoff = 0;
		p2p->next_af_subtype = act_frm->subtype + 1;
		/* increase dwell time to wait for RESP frame */
		af_params->dwell_time = cpu_to_le32(P2P_AF_MED_DWELL_TIME);
		break;
	case P2P_PAF_GON_RSP:
		p2p->next_af_subtype = act_frm->subtype + 1;
		/* increase dwell time to wait for CONF frame */
		af_params->dwell_time = cpu_to_le32(P2P_AF_MED_DWELL_TIME);
		break;
	case P2P_PAF_GON_CONF:
		/* If we reached till GO Neg confirmation reset the filter */
		brcmf_dbg(TRACE, "P2P: GO_NEG_PHASE status cleared\n");
		clear_bit(BRCMF_P2P_STATUS_GO_NEG_PHASE, &p2p->status);
		/* turn on mpc again if go nego is done */
		config_af_params->mpc_onoff = 1;
		/* minimize dwell time */
		af_params->dwell_time = cpu_to_le32(P2P_AF_MIN_DWELL_TIME);
		break;
	case P2P_PAF_INVITE_REQ:
		p2p->next_af_subtype = act_frm->subtype + 1;
		/* increase dwell time */
		af_params->dwell_time = cpu_to_le32(P2P_AF_MED_DWELL_TIME);
		break;
	case P2P_PAF_INVITE_RSP:
		/* minimize dwell time */
		af_params->dwell_time = cpu_to_le32(P2P_AF_MIN_DWELL_TIME);
		break;
	case P2P_PAF_DEVDIS_REQ:
		p2p->next_af_subtype = act_frm->subtype + 1;
		/* maximize dwell time to wait for RESP frame */
		af_params->dwell_time = cpu_to_le32(P2P_AF_LONG_DWELL_TIME);
		break;
	case P2P_PAF_DEVDIS_RSP:
		/* minimize dwell time */
		af_params->dwell_time = cpu_to_le32(P2P_AF_MIN_DWELL_TIME);
		break;
	case P2P_PAF_PROVDIS_REQ:
		config_af_params->mpc_onoff = 0;
		p2p->next_af_subtype = act_frm->subtype + 1;
		/* increase dwell time to wait for RESP frame */
		af_params->dwell_time = cpu_to_le32(P2P_AF_MED_DWELL_TIME);
		break;
	case P2P_PAF_PROVDIS_RSP:
		/* wpa_supplicant send go nego req right after prov disc */
		p2p->next_af_subtype = P2P_PAF_GON_REQ;
		/* increase dwell time to MED level */
		af_params->dwell_time = cpu_to_le32(P2P_AF_MED_DWELL_TIME);
		break;
	default:
		brcmf_err("Unknown p2p pub act frame subtype: %d\n",
			  act_frm->subtype);
		err = -EINVAL;
	}
	return err;
}

/**
 * brcmf_p2p_send_action_frame() - send action frame .
 *
 * @cfg: driver private data for cfg80211 interface.
 * @ndev: net device to transmit on.
 * @af_params: configuration data for action frame.
 */
bool brcmf_p2p_send_action_frame(struct brcmf_cfg80211_info *cfg,
				 struct net_device *ndev,
				 struct brcmf_fil_af_params_le *af_params)
{
	struct brcmf_p2p_info *p2p = &cfg->p2p;
	struct brcmf_fil_action_frame_le *action_frame;
	struct brcmf_config_af_params config_af_params;
	u16 action_frame_len;
	bool ack = false;
	u8 category;
	u8 action;
	s32 tx_retry;

	action_frame = &af_params->action_frame;
	action_frame_len = le16_to_cpu(action_frame->len);

	brcmf_p2p_print_actframe(true, action_frame->data, action_frame_len);

	/* Add the default dwell time. Dwell time to stay off-channel */
	/* to wait for a response action frame after transmitting an  */
	/* GO Negotiation action frame                                */
	af_params->dwell_time = cpu_to_le32(P2P_AF_DWELL_TIME);

	category = action_frame->data[DOT11_ACTION_CAT_OFF];
	action = action_frame->data[DOT11_ACTION_ACT_OFF];

	/* initialize variables */
	p2p->next_af_subtype = P2P_PAF_SUBTYPE_INVALID;

	/* config parameters */
	config_af_params.max_tx_retry = P2P_AF_TX_MAX_RETRY;
	config_af_params.mpc_onoff = -1;

	if (brcmf_p2p_is_pub_action(action_frame->data, action_frame_len)) {
		/* p2p public action frame process */
		if (brcmf_p2p_pub_af_tx(cfg, af_params, &config_af_params)) {
			/* Just send unknown subtype frame with */
			/* default parameters.                  */
			brcmf_err("P2P Public action frame, unknown subtype.\n");
		}
	} else if (brcmf_p2p_is_gas_action(action_frame->data,
					   action_frame_len)) {
		/* service discovery process */
		if (action == P2PSD_ACTION_ID_GAS_IREQ ||
		    action == P2PSD_ACTION_ID_GAS_CREQ) {
			/* save next af suptype to cancel */
			/* remaining dwell time           */
			p2p->next_af_subtype = action + 1;

			af_params->dwell_time =
				cpu_to_le32(P2P_AF_MED_DWELL_TIME);
		} else if (action == P2PSD_ACTION_ID_GAS_IRESP ||
			   action == P2PSD_ACTION_ID_GAS_CRESP) {
			/* configure service discovery response frame */
			af_params->dwell_time =
				cpu_to_le32(P2P_AF_MIN_DWELL_TIME);
		} else {
			brcmf_err("Unknown action type: %d\n", action);
			goto exit;
		}
	} else if (brcmf_p2p_is_p2p_action(action_frame->data,
					   action_frame_len)) {
		/* do not configure anything. it will be */
		/* sent with a default configuration     */
	} else {
		brcmf_err("Unknown Frame: category 0x%x, action 0x%x\n",
			  category, action);
		return false;
	}

	/* if scan is ongoing, abort current scan. */
	if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status))
		brcmf_abort_scanning(cfg);

	/* To make sure to send successfully action frame, turn off mpc */
	if (config_af_params.mpc_onoff == 0)
		brcmf_set_mpc(ndev, 0);

	/* if failed, retry it. tx_retry_max value is configure by .... */
	tx_retry = 0;
	while ((ack == false) && (tx_retry < config_af_params.max_tx_retry)) {
		ack = !brcmf_p2p_tx_action_frame(p2p, af_params);
		tx_retry++;
	}
	if (ack == false)
		brcmf_err("Failed to send Action Frame(retry %d)\n", tx_retry);

exit:
	/* if all done, turn mpc on again */
	if (config_af_params.mpc_onoff == 1)
		brcmf_set_mpc(ndev, 1);

	return ack;
}


/**
 * brcmf_p2p_attach() - attach for P2P.
 *
@@ -1047,6 +1334,7 @@ void brcmf_p2p_attach(struct brcmf_cfg80211_info *cfg,
	p2p->bss_idx[P2PAPI_BSSCFG_PRIMARY].vif = vif;
	brcmf_p2p_generate_bss_mac(p2p);
	brcmf_p2p_set_firmware(p2p);
	init_completion(&p2p->send_af_done);
}

/**
+10 −0
Original line number Diff line number Diff line
@@ -89,6 +89,8 @@ enum brcmf_p2p_status {
 * @ssid: ssid for P2P GO.
 * @listen_channel: channel for @WL_P2P_DISC_ST_LISTEN discover state.
 * @remain_on_channel: contains copy of struct used by cfg80211.
 * @next_af_subtype: expected action frame subtype.
 * @send_af_done: indication that action frame tx is complete.
 */
struct brcmf_p2p_info {
	struct brcmf_cfg80211_info *cfg;
@@ -100,6 +102,8 @@ struct brcmf_p2p_info {
	struct brcmf_ssid ssid;
	u8 listen_channel;
	struct ieee80211_channel remain_on_channel;
	u8 next_af_subtype;
	struct completion send_af_done;
};

void brcmf_p2p_attach(struct brcmf_cfg80211_info *cfg,
@@ -126,5 +130,11 @@ void brcmf_p2p_cancel_remain_on_channel(struct brcmf_if *ifp);
int brcmf_p2p_notify_action_frame_rx(struct brcmf_if *ifp,
				     const struct brcmf_event_msg *e,
				     void *data);
int brcmf_p2p_notify_action_tx_complete(struct brcmf_if *ifp,
					const struct brcmf_event_msg *e,
					void *data);
bool brcmf_p2p_send_action_frame(struct brcmf_cfg80211_info *cfg,
				 struct net_device *ndev,
				 struct brcmf_fil_af_params_le *af_params);

#endif /* WL_CFGP2P_H_ */
+40 −1
Original line number Diff line number Diff line
@@ -2494,7 +2494,7 @@ static s32 brcmf_update_bss_info(struct brcmf_cfg80211_info *cfg,
	return err;
}

static void brcmf_abort_scanning(struct brcmf_cfg80211_info *cfg)
void brcmf_abort_scanning(struct brcmf_cfg80211_info *cfg)
{
	struct escan_info *escan = &cfg->escan_info;

@@ -3949,6 +3949,10 @@ brcmf_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
	s32 err = 0;
	s32 ie_offset;
	s32 ie_len;
	struct brcmf_fil_action_frame_le *action_frame;
	struct brcmf_fil_af_params_le *af_params;
	bool ack;
	s32 chan_nr;

	brcmf_dbg(TRACE, "Enter\n");

@@ -3986,11 +3990,44 @@ brcmf_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
					    ie_len);
		cfg80211_mgmt_tx_status(wdev, *cookie, buf, len, true,
					GFP_KERNEL);
	} else if (ieee80211_is_action(mgmt->frame_control)) {
		af_params = kzalloc(sizeof(*af_params), GFP_KERNEL);
		if (af_params == NULL) {
			brcmf_err("unable to allocate frame\n");
			err = -ENOMEM;
			goto exit;
		}
		action_frame = &af_params->action_frame;
		/* Add the packet Id */
		action_frame->packet_id = cpu_to_le32(*cookie);
		/* Add BSSID */
		memcpy(&action_frame->da[0], &mgmt->da[0], ETH_ALEN);
		memcpy(&af_params->bssid[0], &mgmt->bssid[0], ETH_ALEN);
		/* Add the length exepted for 802.11 header  */
		action_frame->len = cpu_to_le16(len - DOT11_MGMT_HDR_LEN);
		/* Add the channel */
		chan_nr = ieee80211_frequency_to_channel(chan->center_freq);
		af_params->channel = cpu_to_le32(chan_nr);

		memcpy(action_frame->data, &buf[DOT11_MGMT_HDR_LEN],
		       le16_to_cpu(action_frame->len));

		brcmf_dbg(TRACE, "Action frame, cookie=%lld, len=%d, freq=%d\n",
			  *cookie, le16_to_cpu(action_frame->len),
			  chan->center_freq);

		ack = brcmf_p2p_send_action_frame(cfg, wdev->netdev,
						  af_params);

		cfg80211_mgmt_tx_status(wdev, *cookie, buf, len, ack,
					GFP_KERNEL);
		kfree(af_params);
	} else {
		brcmf_dbg(TRACE, "Unhandled, fc=%04x!!\n", mgmt->frame_control);
		brcmf_dbg_hex_dump(true, buf, len, "payload, len=%Zu\n", len);
	}

exit:
	return err;
}

@@ -4703,6 +4740,8 @@ static void brcmf_register_event_handlers(struct brcmf_cfg80211_info *cfg)
			    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);
}

static void brcmf_deinit_priv_mem(struct brcmf_cfg80211_info *cfg)
+1 −0
Original line number Diff line number Diff line
@@ -498,5 +498,6 @@ s32 brcmf_notify_escan_complete(struct brcmf_cfg80211_info *cfg,
				struct net_device *ndev,
				bool aborted, bool fw_abort);
void brcmf_set_mpc(struct net_device *ndev, int mpc);
void brcmf_abort_scanning(struct brcmf_cfg80211_info *cfg);

#endif				/* _wl_cfg80211_h_ */