xref: /openbmc/linux/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c (revision 9a87ffc99ec8eb8d35eed7c4f816d75f5cc9662e)
1daeccac2SArend van Spriel // SPDX-License-Identifier: ISC
205491d2cSKalle Valo /*
305491d2cSKalle Valo  * Copyright (c) 2012 Broadcom Corporation
405491d2cSKalle Valo  */
505491d2cSKalle Valo #include <linux/slab.h>
605491d2cSKalle Valo #include <linux/netdevice.h>
705491d2cSKalle Valo #include <linux/etherdevice.h>
805491d2cSKalle Valo #include <linux/rtnetlink.h>
905491d2cSKalle Valo #include <net/cfg80211.h>
1005491d2cSKalle Valo 
1105491d2cSKalle Valo #include <brcmu_wifi.h>
1205491d2cSKalle Valo #include <brcmu_utils.h>
1305491d2cSKalle Valo #include <defs.h>
1405491d2cSKalle Valo #include "core.h"
1505491d2cSKalle Valo #include "debug.h"
1605491d2cSKalle Valo #include "fwil.h"
1705491d2cSKalle Valo #include "fwil_types.h"
1805491d2cSKalle Valo #include "p2p.h"
1905491d2cSKalle Valo #include "cfg80211.h"
20babfd3caSWright Feng #include "feature.h"
2105491d2cSKalle Valo 
2205491d2cSKalle Valo /* parameters used for p2p escan */
2305491d2cSKalle Valo #define P2PAPI_SCAN_NPROBES 1
2405491d2cSKalle Valo #define P2PAPI_SCAN_DWELL_TIME_MS 80
2505491d2cSKalle Valo #define P2PAPI_SCAN_SOCIAL_DWELL_TIME_MS 40
2605491d2cSKalle Valo #define P2PAPI_SCAN_HOME_TIME_MS 60
2705491d2cSKalle Valo #define P2PAPI_SCAN_NPROBS_TIME_MS 30
2805491d2cSKalle Valo #define P2PAPI_SCAN_AF_SEARCH_DWELL_TIME_MS 100
2905491d2cSKalle Valo #define WL_SCAN_CONNECT_DWELL_TIME_MS 200
3005491d2cSKalle Valo #define WL_SCAN_JOIN_PROBE_INTERVAL_MS 20
3105491d2cSKalle Valo 
3205491d2cSKalle Valo #define BRCMF_P2P_WILDCARD_SSID		"DIRECT-"
3305491d2cSKalle Valo #define BRCMF_P2P_WILDCARD_SSID_LEN	(sizeof(BRCMF_P2P_WILDCARD_SSID) - 1)
3405491d2cSKalle Valo 
3505491d2cSKalle Valo #define SOCIAL_CHAN_1		1
3605491d2cSKalle Valo #define SOCIAL_CHAN_2		6
3705491d2cSKalle Valo #define SOCIAL_CHAN_3		11
3805491d2cSKalle Valo #define IS_P2P_SOCIAL_CHANNEL(channel) ((channel == SOCIAL_CHAN_1) || \
3905491d2cSKalle Valo 					 (channel == SOCIAL_CHAN_2) || \
4005491d2cSKalle Valo 					 (channel == SOCIAL_CHAN_3))
4105491d2cSKalle Valo #define BRCMF_P2P_TEMP_CHAN	SOCIAL_CHAN_3
4205491d2cSKalle Valo #define SOCIAL_CHAN_CNT		3
4305491d2cSKalle Valo #define AF_PEER_SEARCH_CNT	2
4405491d2cSKalle Valo 
4505491d2cSKalle Valo #define BRCMF_SCB_TIMEOUT_VALUE	20
4605491d2cSKalle Valo 
4705491d2cSKalle Valo #define P2P_VER			9	/* P2P version: 9=WiFi P2P v1.0 */
4805491d2cSKalle Valo #define P2P_PUB_AF_CATEGORY	0x04
4905491d2cSKalle Valo #define P2P_PUB_AF_ACTION	0x09
5005491d2cSKalle Valo #define P2P_AF_CATEGORY		0x7f
5105491d2cSKalle Valo #define P2P_OUI			"\x50\x6F\x9A"	/* P2P OUI */
5205491d2cSKalle Valo #define P2P_OUI_LEN		3		/* P2P OUI length */
5305491d2cSKalle Valo 
5405491d2cSKalle Valo /* Action Frame Constants */
5505491d2cSKalle Valo #define DOT11_ACTION_HDR_LEN	2	/* action frame category + action */
5605491d2cSKalle Valo #define DOT11_ACTION_CAT_OFF	0	/* category offset */
5705491d2cSKalle Valo #define DOT11_ACTION_ACT_OFF	1	/* action offset */
5805491d2cSKalle Valo 
5905491d2cSKalle Valo #define P2P_AF_DWELL_TIME		200
6005491d2cSKalle Valo #define P2P_AF_MIN_DWELL_TIME		100
6105491d2cSKalle Valo #define P2P_AF_MED_DWELL_TIME		400
6205491d2cSKalle Valo #define P2P_AF_LONG_DWELL_TIME		1000
639c29da3fSJoseph Chuang #define P2P_AF_TX_MAX_RETRY		5
6463ce3d5dSArend van Spriel #define P2P_AF_MAX_WAIT_TIME		msecs_to_jiffies(2000)
6505491d2cSKalle Valo #define P2P_INVALID_CHANNEL		-1
6605491d2cSKalle Valo #define P2P_CHANNEL_SYNC_RETRY		5
67edb6d688SChung-Hsien Hsu #define P2P_AF_FRM_SCAN_MAX_WAIT	msecs_to_jiffies(450)
6805491d2cSKalle Valo #define P2P_DEFAULT_SLEEP_TIME_VSDB	200
697f26cedfSJustin Li #define P2P_AF_RETRY_DELAY_TIME		40
7005491d2cSKalle Valo 
7105491d2cSKalle Valo /* WiFi P2P Public Action Frame OUI Subtypes */
7205491d2cSKalle Valo #define P2P_PAF_GON_REQ		0	/* Group Owner Negotiation Req */
7305491d2cSKalle Valo #define P2P_PAF_GON_RSP		1	/* Group Owner Negotiation Rsp */
7405491d2cSKalle Valo #define P2P_PAF_GON_CONF	2	/* Group Owner Negotiation Confirm */
7505491d2cSKalle Valo #define P2P_PAF_INVITE_REQ	3	/* P2P Invitation Request */
7605491d2cSKalle Valo #define P2P_PAF_INVITE_RSP	4	/* P2P Invitation Response */
7705491d2cSKalle Valo #define P2P_PAF_DEVDIS_REQ	5	/* Device Discoverability Request */
7805491d2cSKalle Valo #define P2P_PAF_DEVDIS_RSP	6	/* Device Discoverability Response */
7905491d2cSKalle Valo #define P2P_PAF_PROVDIS_REQ	7	/* Provision Discovery Request */
8005491d2cSKalle Valo #define P2P_PAF_PROVDIS_RSP	8	/* Provision Discovery Response */
8105491d2cSKalle Valo #define P2P_PAF_SUBTYPE_INVALID	255	/* Invalid Subtype */
8205491d2cSKalle Valo 
8305491d2cSKalle Valo /* WiFi P2P Action Frame OUI Subtypes */
8405491d2cSKalle Valo #define P2P_AF_NOTICE_OF_ABSENCE	0	/* Notice of Absence */
8505491d2cSKalle Valo #define P2P_AF_PRESENCE_REQ		1	/* P2P Presence Request */
8605491d2cSKalle Valo #define P2P_AF_PRESENCE_RSP		2	/* P2P Presence Response */
8705491d2cSKalle Valo #define P2P_AF_GO_DISC_REQ		3	/* GO Discoverability Request */
8805491d2cSKalle Valo 
8905491d2cSKalle Valo /* P2P Service Discovery related */
9005491d2cSKalle Valo #define P2PSD_ACTION_CATEGORY		0x04	/* Public action frame */
9105491d2cSKalle Valo #define P2PSD_ACTION_ID_GAS_IREQ	0x0a	/* GAS Initial Request AF */
9205491d2cSKalle Valo #define P2PSD_ACTION_ID_GAS_IRESP	0x0b	/* GAS Initial Response AF */
937f5f00cdSColin Ian King #define P2PSD_ACTION_ID_GAS_CREQ	0x0c	/* GAS Comeback Request AF */
947f5f00cdSColin Ian King #define P2PSD_ACTION_ID_GAS_CRESP	0x0d	/* GAS Comeback Response AF */
9505491d2cSKalle Valo 
9663ce3d5dSArend van Spriel #define BRCMF_P2P_DISABLE_TIMEOUT	msecs_to_jiffies(500)
979c29da3fSJoseph Chuang 
989c29da3fSJoseph Chuang /* Mask for retry counter of custom dwell time */
999c29da3fSJoseph Chuang #define CUSTOM_RETRY_MASK 0xff000000
10005491d2cSKalle Valo /**
10105491d2cSKalle Valo  * struct brcmf_p2p_disc_st_le - set discovery state in firmware.
10205491d2cSKalle Valo  *
10305491d2cSKalle Valo  * @state: requested discovery state (see enum brcmf_p2p_disc_state).
10405491d2cSKalle Valo  * @chspec: channel parameter for %WL_P2P_DISC_ST_LISTEN state.
10505491d2cSKalle Valo  * @dwell: dwell time in ms for %WL_P2P_DISC_ST_LISTEN state.
10605491d2cSKalle Valo  */
10705491d2cSKalle Valo struct brcmf_p2p_disc_st_le {
10805491d2cSKalle Valo 	u8 state;
10905491d2cSKalle Valo 	__le16 chspec;
11005491d2cSKalle Valo 	__le16 dwell;
11105491d2cSKalle Valo };
11205491d2cSKalle Valo 
11305491d2cSKalle Valo /**
11405491d2cSKalle Valo  * enum brcmf_p2p_disc_state - P2P discovery state values
11505491d2cSKalle Valo  *
11605491d2cSKalle Valo  * @WL_P2P_DISC_ST_SCAN: P2P discovery with wildcard SSID and P2P IE.
11705491d2cSKalle Valo  * @WL_P2P_DISC_ST_LISTEN: P2P discovery off-channel for specified time.
11805491d2cSKalle Valo  * @WL_P2P_DISC_ST_SEARCH: P2P discovery with P2P wildcard SSID and P2P IE.
11905491d2cSKalle Valo  */
12005491d2cSKalle Valo enum brcmf_p2p_disc_state {
12105491d2cSKalle Valo 	WL_P2P_DISC_ST_SCAN,
12205491d2cSKalle Valo 	WL_P2P_DISC_ST_LISTEN,
12305491d2cSKalle Valo 	WL_P2P_DISC_ST_SEARCH
12405491d2cSKalle Valo };
12505491d2cSKalle Valo 
12605491d2cSKalle Valo /**
12705491d2cSKalle Valo  * struct brcmf_p2p_scan_le - P2P specific scan request.
12805491d2cSKalle Valo  *
12905491d2cSKalle Valo  * @type: type of scan method requested (values: 'E' or 'S').
13005491d2cSKalle Valo  * @reserved: reserved (ignored).
13105491d2cSKalle Valo  * @eparams: parameters used for type 'E'.
13205491d2cSKalle Valo  * @sparams: parameters used for type 'S'.
13305491d2cSKalle Valo  */
13405491d2cSKalle Valo struct brcmf_p2p_scan_le {
13505491d2cSKalle Valo 	u8 type;
13605491d2cSKalle Valo 	u8 reserved[3];
13705491d2cSKalle Valo 	union {
13805491d2cSKalle Valo 		struct brcmf_escan_params_le eparams;
13905491d2cSKalle Valo 		struct brcmf_scan_params_le sparams;
14005491d2cSKalle Valo 	};
14105491d2cSKalle Valo };
14205491d2cSKalle Valo 
14305491d2cSKalle Valo /**
14405491d2cSKalle Valo  * struct brcmf_p2p_pub_act_frame - WiFi P2P Public Action Frame
14505491d2cSKalle Valo  *
14605491d2cSKalle Valo  * @category: P2P_PUB_AF_CATEGORY
14705491d2cSKalle Valo  * @action: P2P_PUB_AF_ACTION
14857636058SLee Jones  * @oui: P2P_OUI
14905491d2cSKalle Valo  * @oui_type: OUI type - P2P_VER
15005491d2cSKalle Valo  * @subtype: OUI subtype - P2P_TYPE_*
15105491d2cSKalle Valo  * @dialog_token: nonzero, identifies req/rsp transaction
15257636058SLee Jones  * @elts: Variable length information elements.
15305491d2cSKalle Valo  */
15405491d2cSKalle Valo struct brcmf_p2p_pub_act_frame {
15505491d2cSKalle Valo 	u8	category;
15605491d2cSKalle Valo 	u8	action;
15705491d2cSKalle Valo 	u8	oui[3];
15805491d2cSKalle Valo 	u8	oui_type;
15905491d2cSKalle Valo 	u8	subtype;
16005491d2cSKalle Valo 	u8	dialog_token;
161f3c04fffSGustavo A. R. Silva 	u8	elts[];
16205491d2cSKalle Valo };
16305491d2cSKalle Valo 
16405491d2cSKalle Valo /**
16505491d2cSKalle Valo  * struct brcmf_p2p_action_frame - WiFi P2P Action Frame
16605491d2cSKalle Valo  *
16705491d2cSKalle Valo  * @category: P2P_AF_CATEGORY
16857636058SLee Jones  * @oui: OUI - P2P_OUI
16905491d2cSKalle Valo  * @type: OUI Type - P2P_VER
17005491d2cSKalle Valo  * @subtype: OUI Subtype - P2P_AF_*
17105491d2cSKalle Valo  * @dialog_token: nonzero, identifies req/resp tranaction
17257636058SLee Jones  * @elts: Variable length information elements.
17305491d2cSKalle Valo  */
17405491d2cSKalle Valo struct brcmf_p2p_action_frame {
17505491d2cSKalle Valo 	u8	category;
17605491d2cSKalle Valo 	u8	oui[3];
17705491d2cSKalle Valo 	u8	type;
17805491d2cSKalle Valo 	u8	subtype;
17905491d2cSKalle Valo 	u8	dialog_token;
180f3c04fffSGustavo A. R. Silva 	u8	elts[];
18105491d2cSKalle Valo };
18205491d2cSKalle Valo 
18305491d2cSKalle Valo /**
18405491d2cSKalle Valo  * struct brcmf_p2psd_gas_pub_act_frame - Wi-Fi GAS Public Action Frame
18505491d2cSKalle Valo  *
18605491d2cSKalle Valo  * @category: 0x04 Public Action Frame
18705491d2cSKalle Valo  * @action: 0x6c Advertisement Protocol
18805491d2cSKalle Valo  * @dialog_token: nonzero, identifies req/rsp transaction
18957636058SLee Jones  * @query_data: Query Data. SD gas ireq SD gas iresp
19005491d2cSKalle Valo  */
19105491d2cSKalle Valo struct brcmf_p2psd_gas_pub_act_frame {
19205491d2cSKalle Valo 	u8	category;
19305491d2cSKalle Valo 	u8	action;
19405491d2cSKalle Valo 	u8	dialog_token;
195f3c04fffSGustavo A. R. Silva 	u8	query_data[];
19605491d2cSKalle Valo };
19705491d2cSKalle Valo 
19805491d2cSKalle Valo /**
19905491d2cSKalle Valo  * struct brcmf_config_af_params - Action Frame Parameters for tx.
20005491d2cSKalle Valo  *
20105491d2cSKalle Valo  * @mpc_onoff: To make sure to send successfully action frame, we have to
20205491d2cSKalle Valo  *             turn off mpc  0: off, 1: on,  (-1): do nothing
20305491d2cSKalle Valo  * @search_channel: 1: search peer's channel to send af
20457636058SLee Jones  * @extra_listen: keep the dwell time to get af response frame.
20505491d2cSKalle Valo  */
20605491d2cSKalle Valo struct brcmf_config_af_params {
20705491d2cSKalle Valo 	s32 mpc_onoff;
20805491d2cSKalle Valo 	bool search_channel;
20905491d2cSKalle Valo 	bool extra_listen;
21005491d2cSKalle Valo };
21105491d2cSKalle Valo 
21205491d2cSKalle Valo /**
21305491d2cSKalle Valo  * brcmf_p2p_is_pub_action() - true if p2p public type frame.
21405491d2cSKalle Valo  *
21505491d2cSKalle Valo  * @frame: action frame data.
21605491d2cSKalle Valo  * @frame_len: length of action frame data.
21705491d2cSKalle Valo  *
21805491d2cSKalle Valo  * Determine if action frame is p2p public action type
21905491d2cSKalle Valo  */
brcmf_p2p_is_pub_action(void * frame,u32 frame_len)22005491d2cSKalle Valo static bool brcmf_p2p_is_pub_action(void *frame, u32 frame_len)
22105491d2cSKalle Valo {
22205491d2cSKalle Valo 	struct brcmf_p2p_pub_act_frame *pact_frm;
22305491d2cSKalle Valo 
22405491d2cSKalle Valo 	if (frame == NULL)
22505491d2cSKalle Valo 		return false;
22605491d2cSKalle Valo 
22705491d2cSKalle Valo 	pact_frm = (struct brcmf_p2p_pub_act_frame *)frame;
228f3c04fffSGustavo A. R. Silva 	if (frame_len < sizeof(*pact_frm))
22905491d2cSKalle Valo 		return false;
23005491d2cSKalle Valo 
23105491d2cSKalle Valo 	if (pact_frm->category == P2P_PUB_AF_CATEGORY &&
23205491d2cSKalle Valo 	    pact_frm->action == P2P_PUB_AF_ACTION &&
23305491d2cSKalle Valo 	    pact_frm->oui_type == P2P_VER &&
23405491d2cSKalle Valo 	    memcmp(pact_frm->oui, P2P_OUI, P2P_OUI_LEN) == 0)
23505491d2cSKalle Valo 		return true;
23605491d2cSKalle Valo 
23705491d2cSKalle Valo 	return false;
23805491d2cSKalle Valo }
23905491d2cSKalle Valo 
24005491d2cSKalle Valo /**
24105491d2cSKalle Valo  * brcmf_p2p_is_p2p_action() - true if p2p action type frame.
24205491d2cSKalle Valo  *
24305491d2cSKalle Valo  * @frame: action frame data.
24405491d2cSKalle Valo  * @frame_len: length of action frame data.
24505491d2cSKalle Valo  *
24605491d2cSKalle Valo  * Determine if action frame is p2p action type
24705491d2cSKalle Valo  */
brcmf_p2p_is_p2p_action(void * frame,u32 frame_len)24805491d2cSKalle Valo static bool brcmf_p2p_is_p2p_action(void *frame, u32 frame_len)
24905491d2cSKalle Valo {
25005491d2cSKalle Valo 	struct brcmf_p2p_action_frame *act_frm;
25105491d2cSKalle Valo 
25205491d2cSKalle Valo 	if (frame == NULL)
25305491d2cSKalle Valo 		return false;
25405491d2cSKalle Valo 
25505491d2cSKalle Valo 	act_frm = (struct brcmf_p2p_action_frame *)frame;
256f3c04fffSGustavo A. R. Silva 	if (frame_len < sizeof(*act_frm))
25705491d2cSKalle Valo 		return false;
25805491d2cSKalle Valo 
25905491d2cSKalle Valo 	if (act_frm->category == P2P_AF_CATEGORY &&
26005491d2cSKalle Valo 	    act_frm->type  == P2P_VER &&
26105491d2cSKalle Valo 	    memcmp(act_frm->oui, P2P_OUI, P2P_OUI_LEN) == 0)
26205491d2cSKalle Valo 		return true;
26305491d2cSKalle Valo 
26405491d2cSKalle Valo 	return false;
26505491d2cSKalle Valo }
26605491d2cSKalle Valo 
26705491d2cSKalle Valo /**
26805491d2cSKalle Valo  * brcmf_p2p_is_gas_action() - true if p2p gas action type frame.
26905491d2cSKalle Valo  *
27005491d2cSKalle Valo  * @frame: action frame data.
27105491d2cSKalle Valo  * @frame_len: length of action frame data.
27205491d2cSKalle Valo  *
27305491d2cSKalle Valo  * Determine if action frame is p2p gas action type
27405491d2cSKalle Valo  */
brcmf_p2p_is_gas_action(void * frame,u32 frame_len)27505491d2cSKalle Valo static bool brcmf_p2p_is_gas_action(void *frame, u32 frame_len)
27605491d2cSKalle Valo {
27705491d2cSKalle Valo 	struct brcmf_p2psd_gas_pub_act_frame *sd_act_frm;
27805491d2cSKalle Valo 
27905491d2cSKalle Valo 	if (frame == NULL)
28005491d2cSKalle Valo 		return false;
28105491d2cSKalle Valo 
28205491d2cSKalle Valo 	sd_act_frm = (struct brcmf_p2psd_gas_pub_act_frame *)frame;
283f3c04fffSGustavo A. R. Silva 	if (frame_len < sizeof(*sd_act_frm))
28405491d2cSKalle Valo 		return false;
28505491d2cSKalle Valo 
28605491d2cSKalle Valo 	if (sd_act_frm->category != P2PSD_ACTION_CATEGORY)
28705491d2cSKalle Valo 		return false;
28805491d2cSKalle Valo 
28905491d2cSKalle Valo 	if (sd_act_frm->action == P2PSD_ACTION_ID_GAS_IREQ ||
29005491d2cSKalle Valo 	    sd_act_frm->action == P2PSD_ACTION_ID_GAS_IRESP ||
29105491d2cSKalle Valo 	    sd_act_frm->action == P2PSD_ACTION_ID_GAS_CREQ ||
29205491d2cSKalle Valo 	    sd_act_frm->action == P2PSD_ACTION_ID_GAS_CRESP)
29305491d2cSKalle Valo 		return true;
29405491d2cSKalle Valo 
29505491d2cSKalle Valo 	return false;
29605491d2cSKalle Valo }
29705491d2cSKalle Valo 
29805491d2cSKalle Valo /**
29905491d2cSKalle Valo  * brcmf_p2p_print_actframe() - debug print routine.
30005491d2cSKalle Valo  *
30105491d2cSKalle Valo  * @tx: Received or to be transmitted
30205491d2cSKalle Valo  * @frame: action frame data.
30305491d2cSKalle Valo  * @frame_len: length of action frame data.
30405491d2cSKalle Valo  *
30505491d2cSKalle Valo  * Print information about the p2p action frame
30605491d2cSKalle Valo  */
30705491d2cSKalle Valo 
30805491d2cSKalle Valo #ifdef DEBUG
30905491d2cSKalle Valo 
brcmf_p2p_print_actframe(bool tx,void * frame,u32 frame_len)31005491d2cSKalle Valo static void brcmf_p2p_print_actframe(bool tx, void *frame, u32 frame_len)
31105491d2cSKalle Valo {
31205491d2cSKalle Valo 	struct brcmf_p2p_pub_act_frame *pact_frm;
31305491d2cSKalle Valo 	struct brcmf_p2p_action_frame *act_frm;
31405491d2cSKalle Valo 	struct brcmf_p2psd_gas_pub_act_frame *sd_act_frm;
31505491d2cSKalle Valo 
31605491d2cSKalle Valo 	if (!frame || frame_len <= 2)
31705491d2cSKalle Valo 		return;
31805491d2cSKalle Valo 
31905491d2cSKalle Valo 	if (brcmf_p2p_is_pub_action(frame, frame_len)) {
32005491d2cSKalle Valo 		pact_frm = (struct brcmf_p2p_pub_act_frame *)frame;
32105491d2cSKalle Valo 		switch (pact_frm->subtype) {
32205491d2cSKalle Valo 		case P2P_PAF_GON_REQ:
32305491d2cSKalle Valo 			brcmf_dbg(TRACE, "%s P2P Group Owner Negotiation Req Frame\n",
32405491d2cSKalle Valo 				  (tx) ? "TX" : "RX");
32505491d2cSKalle Valo 			break;
32605491d2cSKalle Valo 		case P2P_PAF_GON_RSP:
32705491d2cSKalle Valo 			brcmf_dbg(TRACE, "%s P2P Group Owner Negotiation Rsp Frame\n",
32805491d2cSKalle Valo 				  (tx) ? "TX" : "RX");
32905491d2cSKalle Valo 			break;
33005491d2cSKalle Valo 		case P2P_PAF_GON_CONF:
33105491d2cSKalle Valo 			brcmf_dbg(TRACE, "%s P2P Group Owner Negotiation Confirm Frame\n",
33205491d2cSKalle Valo 				  (tx) ? "TX" : "RX");
33305491d2cSKalle Valo 			break;
33405491d2cSKalle Valo 		case P2P_PAF_INVITE_REQ:
33505491d2cSKalle Valo 			brcmf_dbg(TRACE, "%s P2P Invitation Request  Frame\n",
33605491d2cSKalle Valo 				  (tx) ? "TX" : "RX");
33705491d2cSKalle Valo 			break;
33805491d2cSKalle Valo 		case P2P_PAF_INVITE_RSP:
33905491d2cSKalle Valo 			brcmf_dbg(TRACE, "%s P2P Invitation Response Frame\n",
34005491d2cSKalle Valo 				  (tx) ? "TX" : "RX");
34105491d2cSKalle Valo 			break;
34205491d2cSKalle Valo 		case P2P_PAF_DEVDIS_REQ:
34305491d2cSKalle Valo 			brcmf_dbg(TRACE, "%s P2P Device Discoverability Request Frame\n",
34405491d2cSKalle Valo 				  (tx) ? "TX" : "RX");
34505491d2cSKalle Valo 			break;
34605491d2cSKalle Valo 		case P2P_PAF_DEVDIS_RSP:
34705491d2cSKalle Valo 			brcmf_dbg(TRACE, "%s P2P Device Discoverability Response Frame\n",
34805491d2cSKalle Valo 				  (tx) ? "TX" : "RX");
34905491d2cSKalle Valo 			break;
35005491d2cSKalle Valo 		case P2P_PAF_PROVDIS_REQ:
35105491d2cSKalle Valo 			brcmf_dbg(TRACE, "%s P2P Provision Discovery Request Frame\n",
35205491d2cSKalle Valo 				  (tx) ? "TX" : "RX");
35305491d2cSKalle Valo 			break;
35405491d2cSKalle Valo 		case P2P_PAF_PROVDIS_RSP:
35505491d2cSKalle Valo 			brcmf_dbg(TRACE, "%s P2P Provision Discovery Response Frame\n",
35605491d2cSKalle Valo 				  (tx) ? "TX" : "RX");
35705491d2cSKalle Valo 			break;
35805491d2cSKalle Valo 		default:
35905491d2cSKalle Valo 			brcmf_dbg(TRACE, "%s Unknown P2P Public Action Frame\n",
36005491d2cSKalle Valo 				  (tx) ? "TX" : "RX");
36105491d2cSKalle Valo 			break;
36205491d2cSKalle Valo 		}
36305491d2cSKalle Valo 	} else if (brcmf_p2p_is_p2p_action(frame, frame_len)) {
36405491d2cSKalle Valo 		act_frm = (struct brcmf_p2p_action_frame *)frame;
36505491d2cSKalle Valo 		switch (act_frm->subtype) {
36605491d2cSKalle Valo 		case P2P_AF_NOTICE_OF_ABSENCE:
36705491d2cSKalle Valo 			brcmf_dbg(TRACE, "%s P2P Notice of Absence Frame\n",
36805491d2cSKalle Valo 				  (tx) ? "TX" : "RX");
36905491d2cSKalle Valo 			break;
37005491d2cSKalle Valo 		case P2P_AF_PRESENCE_REQ:
37105491d2cSKalle Valo 			brcmf_dbg(TRACE, "%s P2P Presence Request Frame\n",
37205491d2cSKalle Valo 				  (tx) ? "TX" : "RX");
37305491d2cSKalle Valo 			break;
37405491d2cSKalle Valo 		case P2P_AF_PRESENCE_RSP:
37505491d2cSKalle Valo 			brcmf_dbg(TRACE, "%s P2P Presence Response Frame\n",
37605491d2cSKalle Valo 				  (tx) ? "TX" : "RX");
37705491d2cSKalle Valo 			break;
37805491d2cSKalle Valo 		case P2P_AF_GO_DISC_REQ:
37905491d2cSKalle Valo 			brcmf_dbg(TRACE, "%s P2P Discoverability Request Frame\n",
38005491d2cSKalle Valo 				  (tx) ? "TX" : "RX");
38105491d2cSKalle Valo 			break;
38205491d2cSKalle Valo 		default:
38305491d2cSKalle Valo 			brcmf_dbg(TRACE, "%s Unknown P2P Action Frame\n",
38405491d2cSKalle Valo 				  (tx) ? "TX" : "RX");
38505491d2cSKalle Valo 		}
38605491d2cSKalle Valo 
38705491d2cSKalle Valo 	} else if (brcmf_p2p_is_gas_action(frame, frame_len)) {
38805491d2cSKalle Valo 		sd_act_frm = (struct brcmf_p2psd_gas_pub_act_frame *)frame;
38905491d2cSKalle Valo 		switch (sd_act_frm->action) {
39005491d2cSKalle Valo 		case P2PSD_ACTION_ID_GAS_IREQ:
39105491d2cSKalle Valo 			brcmf_dbg(TRACE, "%s P2P GAS Initial Request\n",
39205491d2cSKalle Valo 				  (tx) ? "TX" : "RX");
39305491d2cSKalle Valo 			break;
39405491d2cSKalle Valo 		case P2PSD_ACTION_ID_GAS_IRESP:
39505491d2cSKalle Valo 			brcmf_dbg(TRACE, "%s P2P GAS Initial Response\n",
39605491d2cSKalle Valo 				  (tx) ? "TX" : "RX");
39705491d2cSKalle Valo 			break;
39805491d2cSKalle Valo 		case P2PSD_ACTION_ID_GAS_CREQ:
3997f5f00cdSColin Ian King 			brcmf_dbg(TRACE, "%s P2P GAS Comeback Request\n",
40005491d2cSKalle Valo 				  (tx) ? "TX" : "RX");
40105491d2cSKalle Valo 			break;
40205491d2cSKalle Valo 		case P2PSD_ACTION_ID_GAS_CRESP:
4037f5f00cdSColin Ian King 			brcmf_dbg(TRACE, "%s P2P GAS Comeback Response\n",
40405491d2cSKalle Valo 				  (tx) ? "TX" : "RX");
40505491d2cSKalle Valo 			break;
40605491d2cSKalle Valo 		default:
40705491d2cSKalle Valo 			brcmf_dbg(TRACE, "%s Unknown P2P GAS Frame\n",
40805491d2cSKalle Valo 				  (tx) ? "TX" : "RX");
40905491d2cSKalle Valo 			break;
41005491d2cSKalle Valo 		}
41105491d2cSKalle Valo 	}
41205491d2cSKalle Valo }
41305491d2cSKalle Valo 
41405491d2cSKalle Valo #else
41505491d2cSKalle Valo 
brcmf_p2p_print_actframe(bool tx,void * frame,u32 frame_len)41605491d2cSKalle Valo static void brcmf_p2p_print_actframe(bool tx, void *frame, u32 frame_len)
41705491d2cSKalle Valo {
41805491d2cSKalle Valo }
41905491d2cSKalle Valo 
42005491d2cSKalle Valo #endif
42105491d2cSKalle Valo 
42205491d2cSKalle Valo 
42305491d2cSKalle Valo /**
42405491d2cSKalle Valo  * brcmf_p2p_set_firmware() - prepare firmware for peer-to-peer operation.
42505491d2cSKalle Valo  *
42605491d2cSKalle Valo  * @ifp: ifp to use for iovars (primary).
42705491d2cSKalle Valo  * @p2p_mac: mac address to configure for p2p_da_override
42805491d2cSKalle Valo  */
brcmf_p2p_set_firmware(struct brcmf_if * ifp,u8 * p2p_mac)42905491d2cSKalle Valo static int brcmf_p2p_set_firmware(struct brcmf_if *ifp, u8 *p2p_mac)
43005491d2cSKalle Valo {
431dcb1471bSRafał Miłecki 	struct brcmf_pub *drvr = ifp->drvr;
43205491d2cSKalle Valo 	s32 ret = 0;
43305491d2cSKalle Valo 
43405491d2cSKalle Valo 	brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1);
43505491d2cSKalle Valo 	brcmf_fil_iovar_int_set(ifp, "apsta", 1);
43605491d2cSKalle Valo 	brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 1);
43705491d2cSKalle Valo 
43805491d2cSKalle Valo 	/* In case of COB type, firmware has default mac address
43905491d2cSKalle Valo 	 * After Initializing firmware, we have to set current mac address to
44005491d2cSKalle Valo 	 * firmware for P2P device address. This must be done with discovery
44105491d2cSKalle Valo 	 * disabled.
44205491d2cSKalle Valo 	 */
44305491d2cSKalle Valo 	brcmf_fil_iovar_int_set(ifp, "p2p_disc", 0);
44405491d2cSKalle Valo 
44505491d2cSKalle Valo 	ret = brcmf_fil_iovar_data_set(ifp, "p2p_da_override", p2p_mac,
44605491d2cSKalle Valo 				       ETH_ALEN);
44705491d2cSKalle Valo 	if (ret)
448dcb1471bSRafał Miłecki 		bphy_err(drvr, "failed to update device address ret %d\n", ret);
44905491d2cSKalle Valo 
45005491d2cSKalle Valo 	return ret;
45105491d2cSKalle Valo }
45205491d2cSKalle Valo 
45305491d2cSKalle Valo /**
45405491d2cSKalle Valo  * brcmf_p2p_generate_bss_mac() - derive mac addresses for P2P.
45505491d2cSKalle Valo  *
45605491d2cSKalle Valo  * @p2p: P2P specific data.
45705491d2cSKalle Valo  * @dev_addr: optional device address.
45805491d2cSKalle Valo  *
45905491d2cSKalle Valo  * P2P needs mac addresses for P2P device and interface. If no device
460455f3e76SArend Van Spriel  * address it specified, these are derived from a random ethernet
461455f3e76SArend Van Spriel  * address.
46205491d2cSKalle Valo  */
brcmf_p2p_generate_bss_mac(struct brcmf_p2p_info * p2p,u8 * dev_addr)46305491d2cSKalle Valo static void brcmf_p2p_generate_bss_mac(struct brcmf_p2p_info *p2p, u8 *dev_addr)
46405491d2cSKalle Valo {
465053ac9e1SChi-Hsien Lin 	struct brcmf_if *pri_ifp = p2p->bss_idx[P2PAPI_BSSCFG_PRIMARY].vif->ifp;
466455f3e76SArend Van Spriel 	bool random_addr = false;
467053ac9e1SChi-Hsien Lin 	bool local_admin = false;
46805491d2cSKalle Valo 
469053ac9e1SChi-Hsien Lin 	if (!dev_addr || is_zero_ether_addr(dev_addr)) {
470053ac9e1SChi-Hsien Lin 		/* If the primary interface address is already locally
471053ac9e1SChi-Hsien Lin 		 * administered, create a new random address.
472053ac9e1SChi-Hsien Lin 		 */
473053ac9e1SChi-Hsien Lin 		if (pri_ifp->mac_addr[0] & 0x02) {
474455f3e76SArend Van Spriel 			random_addr = true;
475053ac9e1SChi-Hsien Lin 		} else {
476053ac9e1SChi-Hsien Lin 			dev_addr = pri_ifp->mac_addr;
477053ac9e1SChi-Hsien Lin 			local_admin = true;
478053ac9e1SChi-Hsien Lin 		}
479053ac9e1SChi-Hsien Lin 	}
48005491d2cSKalle Valo 
481455f3e76SArend Van Spriel 	/* Generate the P2P Device Address obtaining a random ethernet
482455f3e76SArend Van Spriel 	 * address with the locally administered bit set.
48305491d2cSKalle Valo 	 */
484455f3e76SArend Van Spriel 	if (random_addr)
485455f3e76SArend Van Spriel 		eth_random_addr(p2p->dev_addr);
486455f3e76SArend Van Spriel 	else
48705491d2cSKalle Valo 		memcpy(p2p->dev_addr, dev_addr, ETH_ALEN);
48805491d2cSKalle Valo 
489053ac9e1SChi-Hsien Lin 	if (local_admin)
490053ac9e1SChi-Hsien Lin 		p2p->dev_addr[0] |= 0x02;
491053ac9e1SChi-Hsien Lin 
49205491d2cSKalle Valo 	/* Generate the P2P Interface Address.  If the discovery and connection
49305491d2cSKalle Valo 	 * BSSCFGs need to simultaneously co-exist, then this address must be
49405491d2cSKalle Valo 	 * different from the P2P Device Address, but also locally administered.
49505491d2cSKalle Valo 	 */
496babfd3caSWright Feng 	memcpy(p2p->conn_int_addr, p2p->dev_addr, ETH_ALEN);
497babfd3caSWright Feng 	p2p->conn_int_addr[0] |= 0x02;
498babfd3caSWright Feng 	p2p->conn_int_addr[4] ^= 0x80;
499babfd3caSWright Feng 
500babfd3caSWright Feng 	memcpy(p2p->conn2_int_addr, p2p->dev_addr, ETH_ALEN);
501babfd3caSWright Feng 	p2p->conn2_int_addr[0] |= 0x02;
502babfd3caSWright Feng 	p2p->conn2_int_addr[4] ^= 0x90;
50305491d2cSKalle Valo }
50405491d2cSKalle Valo 
50505491d2cSKalle Valo /**
50605491d2cSKalle Valo  * brcmf_p2p_scan_is_p2p_request() - is cfg80211 scan request a P2P scan.
50705491d2cSKalle Valo  *
50805491d2cSKalle Valo  * @request: the scan request as received from cfg80211.
50905491d2cSKalle Valo  *
51005491d2cSKalle Valo  * returns true if one of the ssids in the request matches the
51105491d2cSKalle Valo  * P2P wildcard ssid; otherwise returns false.
51205491d2cSKalle Valo  */
brcmf_p2p_scan_is_p2p_request(struct cfg80211_scan_request * request)51305491d2cSKalle Valo static bool brcmf_p2p_scan_is_p2p_request(struct cfg80211_scan_request *request)
51405491d2cSKalle Valo {
51505491d2cSKalle Valo 	struct cfg80211_ssid *ssids = request->ssids;
51605491d2cSKalle Valo 	int i;
51705491d2cSKalle Valo 
51805491d2cSKalle Valo 	for (i = 0; i < request->n_ssids; i++) {
51905491d2cSKalle Valo 		if (ssids[i].ssid_len != BRCMF_P2P_WILDCARD_SSID_LEN)
52005491d2cSKalle Valo 			continue;
52105491d2cSKalle Valo 
52205491d2cSKalle Valo 		brcmf_dbg(INFO, "comparing ssid \"%s\"", ssids[i].ssid);
52305491d2cSKalle Valo 		if (!memcmp(BRCMF_P2P_WILDCARD_SSID, ssids[i].ssid,
52405491d2cSKalle Valo 			    BRCMF_P2P_WILDCARD_SSID_LEN))
52505491d2cSKalle Valo 			return true;
52605491d2cSKalle Valo 	}
52705491d2cSKalle Valo 	return false;
52805491d2cSKalle Valo }
52905491d2cSKalle Valo 
53005491d2cSKalle Valo /**
53105491d2cSKalle Valo  * brcmf_p2p_set_discover_state - set discover state in firmware.
53205491d2cSKalle Valo  *
53305491d2cSKalle Valo  * @ifp: low-level interface object.
53405491d2cSKalle Valo  * @state: discover state to set.
53505491d2cSKalle Valo  * @chanspec: channel parameters (for state @WL_P2P_DISC_ST_LISTEN only).
53605491d2cSKalle Valo  * @listen_ms: duration to listen (for state @WL_P2P_DISC_ST_LISTEN only).
53705491d2cSKalle Valo  */
brcmf_p2p_set_discover_state(struct brcmf_if * ifp,u8 state,u16 chanspec,u16 listen_ms)53805491d2cSKalle Valo static s32 brcmf_p2p_set_discover_state(struct brcmf_if *ifp, u8 state,
53905491d2cSKalle Valo 					u16 chanspec, u16 listen_ms)
54005491d2cSKalle Valo {
54105491d2cSKalle Valo 	struct brcmf_p2p_disc_st_le discover_state;
54205491d2cSKalle Valo 	s32 ret = 0;
54305491d2cSKalle Valo 	brcmf_dbg(TRACE, "enter\n");
54405491d2cSKalle Valo 
54505491d2cSKalle Valo 	discover_state.state = state;
54605491d2cSKalle Valo 	discover_state.chspec = cpu_to_le16(chanspec);
54705491d2cSKalle Valo 	discover_state.dwell = cpu_to_le16(listen_ms);
54805491d2cSKalle Valo 	ret = brcmf_fil_bsscfg_data_set(ifp, "p2p_state", &discover_state,
54905491d2cSKalle Valo 					sizeof(discover_state));
55005491d2cSKalle Valo 	return ret;
55105491d2cSKalle Valo }
55205491d2cSKalle Valo 
55305491d2cSKalle Valo /**
55405491d2cSKalle Valo  * brcmf_p2p_deinit_discovery() - disable P2P device discovery.
55505491d2cSKalle Valo  *
55605491d2cSKalle Valo  * @p2p: P2P specific data.
55705491d2cSKalle Valo  *
55805491d2cSKalle Valo  * Resets the discovery state and disables it in firmware.
55905491d2cSKalle Valo  */
brcmf_p2p_deinit_discovery(struct brcmf_p2p_info * p2p)56005491d2cSKalle Valo static s32 brcmf_p2p_deinit_discovery(struct brcmf_p2p_info *p2p)
56105491d2cSKalle Valo {
56205491d2cSKalle Valo 	struct brcmf_cfg80211_vif *vif;
56305491d2cSKalle Valo 
56405491d2cSKalle Valo 	brcmf_dbg(TRACE, "enter\n");
56505491d2cSKalle Valo 
56605491d2cSKalle Valo 	/* Set the discovery state to SCAN */
56705491d2cSKalle Valo 	vif = p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif;
56805491d2cSKalle Valo 	(void)brcmf_p2p_set_discover_state(vif->ifp, WL_P2P_DISC_ST_SCAN, 0, 0);
56905491d2cSKalle Valo 
57005491d2cSKalle Valo 	/* Disable P2P discovery in the firmware */
57105491d2cSKalle Valo 	vif = p2p->bss_idx[P2PAPI_BSSCFG_PRIMARY].vif;
57205491d2cSKalle Valo 	(void)brcmf_fil_iovar_int_set(vif->ifp, "p2p_disc", 0);
57305491d2cSKalle Valo 
57405491d2cSKalle Valo 	return 0;
57505491d2cSKalle Valo }
57605491d2cSKalle Valo 
57705491d2cSKalle Valo /**
57805491d2cSKalle Valo  * brcmf_p2p_enable_discovery() - initialize and configure discovery.
57905491d2cSKalle Valo  *
58005491d2cSKalle Valo  * @p2p: P2P specific data.
58105491d2cSKalle Valo  *
58205491d2cSKalle Valo  * Initializes the discovery device and configure the virtual interface.
58305491d2cSKalle Valo  */
brcmf_p2p_enable_discovery(struct brcmf_p2p_info * p2p)58405491d2cSKalle Valo static int brcmf_p2p_enable_discovery(struct brcmf_p2p_info *p2p)
58505491d2cSKalle Valo {
586dcb1471bSRafał Miłecki 	struct brcmf_pub *drvr = p2p->cfg->pub;
58705491d2cSKalle Valo 	struct brcmf_cfg80211_vif *vif;
58805491d2cSKalle Valo 	s32 ret = 0;
58905491d2cSKalle Valo 
59005491d2cSKalle Valo 	brcmf_dbg(TRACE, "enter\n");
59105491d2cSKalle Valo 	vif = p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif;
59205491d2cSKalle Valo 	if (!vif) {
593dcb1471bSRafał Miłecki 		bphy_err(drvr, "P2P config device not available\n");
59405491d2cSKalle Valo 		ret = -EPERM;
59505491d2cSKalle Valo 		goto exit;
59605491d2cSKalle Valo 	}
59705491d2cSKalle Valo 
59805491d2cSKalle Valo 	if (test_bit(BRCMF_P2P_STATUS_ENABLED, &p2p->status)) {
59905491d2cSKalle Valo 		brcmf_dbg(INFO, "P2P config device already configured\n");
60005491d2cSKalle Valo 		goto exit;
60105491d2cSKalle Valo 	}
60205491d2cSKalle Valo 
60305491d2cSKalle Valo 	/* Re-initialize P2P Discovery in the firmware */
60405491d2cSKalle Valo 	vif = p2p->bss_idx[P2PAPI_BSSCFG_PRIMARY].vif;
60505491d2cSKalle Valo 	ret = brcmf_fil_iovar_int_set(vif->ifp, "p2p_disc", 1);
60605491d2cSKalle Valo 	if (ret < 0) {
607dcb1471bSRafał Miłecki 		bphy_err(drvr, "set p2p_disc error\n");
60805491d2cSKalle Valo 		goto exit;
60905491d2cSKalle Valo 	}
61005491d2cSKalle Valo 	vif = p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif;
61105491d2cSKalle Valo 	ret = brcmf_p2p_set_discover_state(vif->ifp, WL_P2P_DISC_ST_SCAN, 0, 0);
61205491d2cSKalle Valo 	if (ret < 0) {
613dcb1471bSRafał Miłecki 		bphy_err(drvr, "unable to set WL_P2P_DISC_ST_SCAN\n");
61405491d2cSKalle Valo 		goto exit;
61505491d2cSKalle Valo 	}
61605491d2cSKalle Valo 
61705491d2cSKalle Valo 	/*
61805491d2cSKalle Valo 	 * Set wsec to any non-zero value in the discovery bsscfg
61905491d2cSKalle Valo 	 * to ensure our P2P probe responses have the privacy bit
62005491d2cSKalle Valo 	 * set in the 802.11 WPA IE. Some peer devices may not
62105491d2cSKalle Valo 	 * initiate WPS with us if this bit is not set.
62205491d2cSKalle Valo 	 */
62305491d2cSKalle Valo 	ret = brcmf_fil_bsscfg_int_set(vif->ifp, "wsec", AES_ENABLED);
62405491d2cSKalle Valo 	if (ret < 0) {
625dcb1471bSRafał Miłecki 		bphy_err(drvr, "wsec error %d\n", ret);
62605491d2cSKalle Valo 		goto exit;
62705491d2cSKalle Valo 	}
62805491d2cSKalle Valo 
62905491d2cSKalle Valo 	set_bit(BRCMF_P2P_STATUS_ENABLED, &p2p->status);
63005491d2cSKalle Valo exit:
63105491d2cSKalle Valo 	return ret;
63205491d2cSKalle Valo }
63305491d2cSKalle Valo 
63405491d2cSKalle Valo /**
63505491d2cSKalle Valo  * brcmf_p2p_escan() - initiate a P2P scan.
63605491d2cSKalle Valo  *
63705491d2cSKalle Valo  * @p2p: P2P specific data.
63805491d2cSKalle Valo  * @num_chans: number of channels to scan.
63905491d2cSKalle Valo  * @chanspecs: channel parameters for @num_chans channels.
64005491d2cSKalle Valo  * @search_state: P2P discover state to use.
64105491d2cSKalle Valo  * @bss_type: type of P2P bss.
64205491d2cSKalle Valo  */
brcmf_p2p_escan(struct brcmf_p2p_info * p2p,u32 num_chans,u16 chanspecs[],s32 search_state,enum p2p_bss_type bss_type)64305491d2cSKalle Valo static s32 brcmf_p2p_escan(struct brcmf_p2p_info *p2p, u32 num_chans,
644c4958106SHante Meuleman 			   u16 chanspecs[], s32 search_state,
64505491d2cSKalle Valo 			   enum p2p_bss_type bss_type)
64605491d2cSKalle Valo {
647dcb1471bSRafał Miłecki 	struct brcmf_pub *drvr = p2p->cfg->pub;
64805491d2cSKalle Valo 	s32 ret = 0;
64905491d2cSKalle Valo 	s32 memsize = offsetof(struct brcmf_p2p_scan_le,
65005491d2cSKalle Valo 			       eparams.params_le.channel_list);
65105491d2cSKalle Valo 	s32 nprobes;
65205491d2cSKalle Valo 	s32 active;
65305491d2cSKalle Valo 	u32 i;
65405491d2cSKalle Valo 	u8 *memblk;
65505491d2cSKalle Valo 	struct brcmf_cfg80211_vif *vif;
65605491d2cSKalle Valo 	struct brcmf_p2p_scan_le *p2p_params;
65705491d2cSKalle Valo 	struct brcmf_scan_params_le *sparams;
65805491d2cSKalle Valo 
65905491d2cSKalle Valo 	memsize += num_chans * sizeof(__le16);
66005491d2cSKalle Valo 	memblk = kzalloc(memsize, GFP_KERNEL);
66105491d2cSKalle Valo 	if (!memblk)
66205491d2cSKalle Valo 		return -ENOMEM;
66305491d2cSKalle Valo 
66405491d2cSKalle Valo 	vif = p2p->bss_idx[bss_type].vif;
66505491d2cSKalle Valo 	if (vif == NULL) {
666dcb1471bSRafał Miłecki 		bphy_err(drvr, "no vif for bss type %d\n", bss_type);
66705491d2cSKalle Valo 		ret = -EINVAL;
66805491d2cSKalle Valo 		goto exit;
66905491d2cSKalle Valo 	}
670e9a6ca82SHante Meuleman 	p2p_params = (struct brcmf_p2p_scan_le *)memblk;
671e9a6ca82SHante Meuleman 	sparams = &p2p_params->eparams.params_le;
67205491d2cSKalle Valo 
67305491d2cSKalle Valo 	switch (search_state) {
67405491d2cSKalle Valo 	case WL_P2P_DISC_ST_SEARCH:
67505491d2cSKalle Valo 		/*
67605491d2cSKalle Valo 		 * If we in SEARCH STATE, we don't need to set SSID explictly
677e9a6ca82SHante Meuleman 		 * because dongle use P2P WILDCARD internally by default, use
678e9a6ca82SHante Meuleman 		 * null ssid, which it is already due to kzalloc.
67905491d2cSKalle Valo 		 */
68005491d2cSKalle Valo 		break;
68105491d2cSKalle Valo 	case WL_P2P_DISC_ST_SCAN:
68205491d2cSKalle Valo 		/*
68305491d2cSKalle Valo 		 * wpa_supplicant has p2p_find command with type social or
68405491d2cSKalle Valo 		 * progressive. For progressive, we need to set the ssid to
68505491d2cSKalle Valo 		 * P2P WILDCARD because we just do broadcast scan unless
68605491d2cSKalle Valo 		 * setting SSID.
68705491d2cSKalle Valo 		 */
688e9a6ca82SHante Meuleman 		sparams->ssid_le.SSID_len =
689e9a6ca82SHante Meuleman 				cpu_to_le32(BRCMF_P2P_WILDCARD_SSID_LEN);
690e9a6ca82SHante Meuleman 		memcpy(sparams->ssid_le.SSID, BRCMF_P2P_WILDCARD_SSID,
691e9a6ca82SHante Meuleman 		       BRCMF_P2P_WILDCARD_SSID_LEN);
69205491d2cSKalle Valo 		break;
69305491d2cSKalle Valo 	default:
694dcb1471bSRafał Miłecki 		bphy_err(drvr, " invalid search state %d\n", search_state);
69505491d2cSKalle Valo 		ret = -EINVAL;
69605491d2cSKalle Valo 		goto exit;
69705491d2cSKalle Valo 	}
69805491d2cSKalle Valo 
69905491d2cSKalle Valo 	brcmf_p2p_set_discover_state(vif->ifp, search_state, 0, 0);
70005491d2cSKalle Valo 
70105491d2cSKalle Valo 	/*
70205491d2cSKalle Valo 	 * set p2p scan parameters.
70305491d2cSKalle Valo 	 */
70405491d2cSKalle Valo 	p2p_params->type = 'E';
70505491d2cSKalle Valo 
70605491d2cSKalle Valo 	/* determine the scan engine parameters */
70705491d2cSKalle Valo 	sparams->bss_type = DOT11_BSSTYPE_ANY;
708bbf35414SArend Van Spriel 	sparams->scan_type = BRCMF_SCANTYPE_ACTIVE;
70905491d2cSKalle Valo 
71005491d2cSKalle Valo 	eth_broadcast_addr(sparams->bssid);
71105491d2cSKalle Valo 	sparams->home_time = cpu_to_le32(P2PAPI_SCAN_HOME_TIME_MS);
71205491d2cSKalle Valo 
71305491d2cSKalle Valo 	/*
71405491d2cSKalle Valo 	 * SOCIAL_CHAN_CNT + 1 takes care of the Progressive scan
71505491d2cSKalle Valo 	 * supported by the supplicant.
71605491d2cSKalle Valo 	 */
71705491d2cSKalle Valo 	if (num_chans == SOCIAL_CHAN_CNT || num_chans == (SOCIAL_CHAN_CNT + 1))
71805491d2cSKalle Valo 		active = P2PAPI_SCAN_SOCIAL_DWELL_TIME_MS;
71905491d2cSKalle Valo 	else if (num_chans == AF_PEER_SEARCH_CNT)
72005491d2cSKalle Valo 		active = P2PAPI_SCAN_AF_SEARCH_DWELL_TIME_MS;
72105491d2cSKalle Valo 	else if (brcmf_get_vif_state_any(p2p->cfg, BRCMF_VIF_STATUS_CONNECTED))
72205491d2cSKalle Valo 		active = -1;
72305491d2cSKalle Valo 	else
72405491d2cSKalle Valo 		active = P2PAPI_SCAN_DWELL_TIME_MS;
72505491d2cSKalle Valo 
72605491d2cSKalle Valo 	/* Override scan params to find a peer for a connection */
72705491d2cSKalle Valo 	if (num_chans == 1) {
72805491d2cSKalle Valo 		active = WL_SCAN_CONNECT_DWELL_TIME_MS;
72905491d2cSKalle Valo 		/* WAR to sync with presence period of VSDB GO.
73005491d2cSKalle Valo 		 * send probe request more frequently
73105491d2cSKalle Valo 		 */
73205491d2cSKalle Valo 		nprobes = active / WL_SCAN_JOIN_PROBE_INTERVAL_MS;
73305491d2cSKalle Valo 	} else {
73405491d2cSKalle Valo 		nprobes = active / P2PAPI_SCAN_NPROBS_TIME_MS;
73505491d2cSKalle Valo 	}
73605491d2cSKalle Valo 
73705491d2cSKalle Valo 	if (nprobes <= 0)
73805491d2cSKalle Valo 		nprobes = 1;
73905491d2cSKalle Valo 
74005491d2cSKalle Valo 	brcmf_dbg(INFO, "nprobes # %d, active_time %d\n", nprobes, active);
74105491d2cSKalle Valo 	sparams->active_time = cpu_to_le32(active);
74205491d2cSKalle Valo 	sparams->nprobes = cpu_to_le32(nprobes);
74305491d2cSKalle Valo 	sparams->passive_time = cpu_to_le32(-1);
74405491d2cSKalle Valo 	sparams->channel_num = cpu_to_le32(num_chans &
74505491d2cSKalle Valo 					   BRCMF_SCAN_PARAMS_COUNT_MASK);
74605491d2cSKalle Valo 	for (i = 0; i < num_chans; i++)
74705491d2cSKalle Valo 		sparams->channel_list[i] = cpu_to_le16(chanspecs[i]);
74805491d2cSKalle Valo 
74905491d2cSKalle Valo 	/* set the escan specific parameters */
75005491d2cSKalle Valo 	p2p_params->eparams.version = cpu_to_le32(BRCMF_ESCAN_REQ_VERSION);
751c4958106SHante Meuleman 	p2p_params->eparams.action =  cpu_to_le16(WL_ESCAN_ACTION_START);
75205491d2cSKalle Valo 	p2p_params->eparams.sync_id = cpu_to_le16(0x1234);
75305491d2cSKalle Valo 	/* perform p2p scan on primary device */
75405491d2cSKalle Valo 	ret = brcmf_fil_bsscfg_data_set(vif->ifp, "p2p_scan", memblk, memsize);
75505491d2cSKalle Valo 	if (!ret)
75605491d2cSKalle Valo 		set_bit(BRCMF_SCAN_STATUS_BUSY, &p2p->cfg->scan_status);
75705491d2cSKalle Valo exit:
75805491d2cSKalle Valo 	kfree(memblk);
75905491d2cSKalle Valo 	return ret;
76005491d2cSKalle Valo }
76105491d2cSKalle Valo 
76205491d2cSKalle Valo /**
76305491d2cSKalle Valo  * brcmf_p2p_run_escan() - escan callback for peer-to-peer.
76405491d2cSKalle Valo  *
76505491d2cSKalle Valo  * @cfg: driver private data for cfg80211 interface.
76657636058SLee Jones  * @ifp: interface control.
76705491d2cSKalle Valo  * @request: scan request from cfg80211.
76805491d2cSKalle Valo  *
76905491d2cSKalle Valo  * Determines the P2P discovery state based to scan request parameters and
77005491d2cSKalle Valo  * validates the channels in the request.
77105491d2cSKalle Valo  */
brcmf_p2p_run_escan(struct brcmf_cfg80211_info * cfg,struct brcmf_if * ifp,struct cfg80211_scan_request * request)77205491d2cSKalle Valo static s32 brcmf_p2p_run_escan(struct brcmf_cfg80211_info *cfg,
77305491d2cSKalle Valo 			       struct brcmf_if *ifp,
774c4958106SHante Meuleman 			       struct cfg80211_scan_request *request)
77505491d2cSKalle Valo {
77605491d2cSKalle Valo 	struct brcmf_p2p_info *p2p = &cfg->p2p;
777dcb1471bSRafał Miłecki 	struct brcmf_pub *drvr = cfg->pub;
77805491d2cSKalle Valo 	s32 err = 0;
77905491d2cSKalle Valo 	s32 search_state = WL_P2P_DISC_ST_SCAN;
78005491d2cSKalle Valo 	struct brcmf_cfg80211_vif *vif;
78105491d2cSKalle Valo 	struct net_device *dev = NULL;
78205491d2cSKalle Valo 	int i, num_nodfs = 0;
78305491d2cSKalle Valo 	u16 *chanspecs;
78405491d2cSKalle Valo 
78505491d2cSKalle Valo 	brcmf_dbg(TRACE, "enter\n");
78605491d2cSKalle Valo 
78705491d2cSKalle Valo 	if (!request) {
78805491d2cSKalle Valo 		err = -EINVAL;
78905491d2cSKalle Valo 		goto exit;
79005491d2cSKalle Valo 	}
79105491d2cSKalle Valo 
79205491d2cSKalle Valo 	if (request->n_channels) {
79305491d2cSKalle Valo 		chanspecs = kcalloc(request->n_channels, sizeof(*chanspecs),
79405491d2cSKalle Valo 				    GFP_KERNEL);
79505491d2cSKalle Valo 		if (!chanspecs) {
79605491d2cSKalle Valo 			err = -ENOMEM;
79705491d2cSKalle Valo 			goto exit;
79805491d2cSKalle Valo 		}
79905491d2cSKalle Valo 		vif = p2p->bss_idx[P2PAPI_BSSCFG_CONNECTION].vif;
80005491d2cSKalle Valo 		if (vif)
80105491d2cSKalle Valo 			dev = vif->wdev.netdev;
80205491d2cSKalle Valo 		if (request->n_channels == 3 &&
80305491d2cSKalle Valo 		    request->channels[0]->hw_value == SOCIAL_CHAN_1 &&
80405491d2cSKalle Valo 		    request->channels[1]->hw_value == SOCIAL_CHAN_2 &&
80505491d2cSKalle Valo 		    request->channels[2]->hw_value == SOCIAL_CHAN_3) {
80605491d2cSKalle Valo 			/* SOCIAL CHANNELS 1, 6, 11 */
80705491d2cSKalle Valo 			search_state = WL_P2P_DISC_ST_SEARCH;
80805491d2cSKalle Valo 			brcmf_dbg(INFO, "P2P SEARCH PHASE START\n");
80905491d2cSKalle Valo 		} else if (dev != NULL &&
81005491d2cSKalle Valo 			   vif->wdev.iftype == NL80211_IFTYPE_P2P_GO) {
81105491d2cSKalle Valo 			/* If you are already a GO, then do SEARCH only */
81205491d2cSKalle Valo 			brcmf_dbg(INFO, "Already a GO. Do SEARCH Only\n");
81305491d2cSKalle Valo 			search_state = WL_P2P_DISC_ST_SEARCH;
81405491d2cSKalle Valo 		} else {
81505491d2cSKalle Valo 			brcmf_dbg(INFO, "P2P SCAN STATE START\n");
81605491d2cSKalle Valo 		}
81705491d2cSKalle Valo 
81805491d2cSKalle Valo 		/*
81905491d2cSKalle Valo 		 * no P2P scanning on passive or DFS channels.
82005491d2cSKalle Valo 		 */
82105491d2cSKalle Valo 		for (i = 0; i < request->n_channels; i++) {
82205491d2cSKalle Valo 			struct ieee80211_channel *chan = request->channels[i];
82305491d2cSKalle Valo 
82405491d2cSKalle Valo 			if (chan->flags & (IEEE80211_CHAN_RADAR |
82505491d2cSKalle Valo 					   IEEE80211_CHAN_NO_IR))
82605491d2cSKalle Valo 				continue;
82705491d2cSKalle Valo 
82805491d2cSKalle Valo 			chanspecs[i] = channel_to_chanspec(&p2p->cfg->d11inf,
82905491d2cSKalle Valo 							   chan);
83005491d2cSKalle Valo 			brcmf_dbg(INFO, "%d: chan=%d, channel spec=%x\n",
83105491d2cSKalle Valo 				  num_nodfs, chan->hw_value, chanspecs[i]);
83205491d2cSKalle Valo 			num_nodfs++;
83305491d2cSKalle Valo 		}
83405491d2cSKalle Valo 		err = brcmf_p2p_escan(p2p, num_nodfs, chanspecs, search_state,
835c4958106SHante Meuleman 				      P2PAPI_BSSCFG_DEVICE);
83605491d2cSKalle Valo 		kfree(chanspecs);
83705491d2cSKalle Valo 	}
83805491d2cSKalle Valo exit:
83905491d2cSKalle Valo 	if (err)
840dcb1471bSRafał Miłecki 		bphy_err(drvr, "error (%d)\n", err);
84105491d2cSKalle Valo 	return err;
84205491d2cSKalle Valo }
84305491d2cSKalle Valo 
84405491d2cSKalle Valo 
84505491d2cSKalle Valo /**
84605491d2cSKalle Valo  * brcmf_p2p_find_listen_channel() - find listen channel in ie string.
84705491d2cSKalle Valo  *
84805491d2cSKalle Valo  * @ie: string of information elements.
84905491d2cSKalle Valo  * @ie_len: length of string.
85005491d2cSKalle Valo  *
85105491d2cSKalle Valo  * Scan ie for p2p ie and look for attribute 6 channel. If available determine
85205491d2cSKalle Valo  * channel and return it.
85305491d2cSKalle Valo  */
brcmf_p2p_find_listen_channel(const u8 * ie,u32 ie_len)85405491d2cSKalle Valo static s32 brcmf_p2p_find_listen_channel(const u8 *ie, u32 ie_len)
85505491d2cSKalle Valo {
85605491d2cSKalle Valo 	u8 channel_ie[5];
85705491d2cSKalle Valo 	s32 listen_channel;
85805491d2cSKalle Valo 	s32 err;
85905491d2cSKalle Valo 
86005491d2cSKalle Valo 	err = cfg80211_get_p2p_attr(ie, ie_len,
86105491d2cSKalle Valo 				    IEEE80211_P2P_ATTR_LISTEN_CHANNEL,
86205491d2cSKalle Valo 				    channel_ie, sizeof(channel_ie));
86305491d2cSKalle Valo 	if (err < 0)
86405491d2cSKalle Valo 		return err;
86505491d2cSKalle Valo 
86605491d2cSKalle Valo 	/* listen channel subel length format:     */
86705491d2cSKalle Valo 	/* 3(country) + 1(op. class) + 1(chan num) */
86805491d2cSKalle Valo 	listen_channel = (s32)channel_ie[3 + 1];
86905491d2cSKalle Valo 
87005491d2cSKalle Valo 	if (listen_channel == SOCIAL_CHAN_1 ||
87105491d2cSKalle Valo 	    listen_channel == SOCIAL_CHAN_2 ||
87205491d2cSKalle Valo 	    listen_channel == SOCIAL_CHAN_3) {
87305491d2cSKalle Valo 		brcmf_dbg(INFO, "Found my Listen Channel %d\n", listen_channel);
87405491d2cSKalle Valo 		return listen_channel;
87505491d2cSKalle Valo 	}
87605491d2cSKalle Valo 
87705491d2cSKalle Valo 	return -EPERM;
87805491d2cSKalle Valo }
87905491d2cSKalle Valo 
88005491d2cSKalle Valo 
88105491d2cSKalle Valo /**
88205491d2cSKalle Valo  * brcmf_p2p_scan_prep() - prepare scan based on request.
88305491d2cSKalle Valo  *
88405491d2cSKalle Valo  * @wiphy: wiphy device.
88505491d2cSKalle Valo  * @request: scan request from cfg80211.
88605491d2cSKalle Valo  * @vif: vif on which scan request is to be executed.
88705491d2cSKalle Valo  *
88805491d2cSKalle Valo  * Prepare the scan appropriately for type of scan requested. Overrides the
88905491d2cSKalle Valo  * escan .run() callback for peer-to-peer scanning.
89005491d2cSKalle Valo  */
brcmf_p2p_scan_prep(struct wiphy * wiphy,struct cfg80211_scan_request * request,struct brcmf_cfg80211_vif * vif)89105491d2cSKalle Valo int brcmf_p2p_scan_prep(struct wiphy *wiphy,
89205491d2cSKalle Valo 			struct cfg80211_scan_request *request,
89305491d2cSKalle Valo 			struct brcmf_cfg80211_vif *vif)
89405491d2cSKalle Valo {
89505491d2cSKalle Valo 	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
89605491d2cSKalle Valo 	struct brcmf_p2p_info *p2p = &cfg->p2p;
897bd99a301SArend Van Spriel 	int err;
89805491d2cSKalle Valo 
89905491d2cSKalle Valo 	if (brcmf_p2p_scan_is_p2p_request(request)) {
90005491d2cSKalle Valo 		/* find my listen channel */
90105491d2cSKalle Valo 		err = brcmf_p2p_find_listen_channel(request->ie,
90205491d2cSKalle Valo 						    request->ie_len);
90305491d2cSKalle Valo 		if (err < 0)
90405491d2cSKalle Valo 			return err;
90505491d2cSKalle Valo 
90605491d2cSKalle Valo 		p2p->afx_hdl.my_listen_chan = err;
90705491d2cSKalle Valo 
90805491d2cSKalle Valo 		clear_bit(BRCMF_P2P_STATUS_GO_NEG_PHASE, &p2p->status);
90905491d2cSKalle Valo 		brcmf_dbg(INFO, "P2P: GO_NEG_PHASE status cleared\n");
91005491d2cSKalle Valo 
91105491d2cSKalle Valo 		err = brcmf_p2p_enable_discovery(p2p);
91205491d2cSKalle Valo 		if (err)
91305491d2cSKalle Valo 			return err;
91405491d2cSKalle Valo 
91505491d2cSKalle Valo 		/* override .run_escan() callback. */
91605491d2cSKalle Valo 		cfg->escan_info.run = brcmf_p2p_run_escan;
91705491d2cSKalle Valo 	}
918bd99a301SArend Van Spriel 	return 0;
91905491d2cSKalle Valo }
92005491d2cSKalle Valo 
92105491d2cSKalle Valo 
92205491d2cSKalle Valo /**
92305491d2cSKalle Valo  * brcmf_p2p_discover_listen() - set firmware to discover listen state.
92405491d2cSKalle Valo  *
92505491d2cSKalle Valo  * @p2p: p2p device.
92605491d2cSKalle Valo  * @channel: channel nr for discover listen.
92705491d2cSKalle Valo  * @duration: time in ms to stay on channel.
92805491d2cSKalle Valo  *
92905491d2cSKalle Valo  */
93005491d2cSKalle Valo static s32
brcmf_p2p_discover_listen(struct brcmf_p2p_info * p2p,u16 channel,u32 duration)93105491d2cSKalle Valo brcmf_p2p_discover_listen(struct brcmf_p2p_info *p2p, u16 channel, u32 duration)
93205491d2cSKalle Valo {
933dcb1471bSRafał Miłecki 	struct brcmf_pub *drvr = p2p->cfg->pub;
93405491d2cSKalle Valo 	struct brcmf_cfg80211_vif *vif;
93505491d2cSKalle Valo 	struct brcmu_chan ch;
93605491d2cSKalle Valo 	s32 err = 0;
93705491d2cSKalle Valo 
93805491d2cSKalle Valo 	vif = p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif;
93905491d2cSKalle Valo 	if (!vif) {
940dcb1471bSRafał Miłecki 		bphy_err(drvr, "Discovery is not set, so we have nothing to do\n");
94105491d2cSKalle Valo 		err = -EPERM;
94205491d2cSKalle Valo 		goto exit;
94305491d2cSKalle Valo 	}
94405491d2cSKalle Valo 
94505491d2cSKalle Valo 	if (test_bit(BRCMF_P2P_STATUS_DISCOVER_LISTEN, &p2p->status)) {
946dcb1471bSRafał Miłecki 		bphy_err(drvr, "Previous LISTEN is not completed yet\n");
94705491d2cSKalle Valo 		/* WAR: prevent cookie mismatch in wpa_supplicant return OK */
94805491d2cSKalle Valo 		goto exit;
94905491d2cSKalle Valo 	}
95005491d2cSKalle Valo 
95105491d2cSKalle Valo 	ch.chnum = channel;
95205491d2cSKalle Valo 	ch.bw = BRCMU_CHAN_BW_20;
95305491d2cSKalle Valo 	p2p->cfg->d11inf.encchspec(&ch);
95405491d2cSKalle Valo 	err = brcmf_p2p_set_discover_state(vif->ifp, WL_P2P_DISC_ST_LISTEN,
95505491d2cSKalle Valo 					   ch.chspec, (u16)duration);
95605491d2cSKalle Valo 	if (!err) {
95705491d2cSKalle Valo 		set_bit(BRCMF_P2P_STATUS_DISCOVER_LISTEN, &p2p->status);
95805491d2cSKalle Valo 		p2p->remain_on_channel_cookie++;
95905491d2cSKalle Valo 	}
96005491d2cSKalle Valo exit:
96105491d2cSKalle Valo 	return err;
96205491d2cSKalle Valo }
96305491d2cSKalle Valo 
96405491d2cSKalle Valo 
96505491d2cSKalle Valo /**
96605491d2cSKalle Valo  * brcmf_p2p_remain_on_channel() - put device on channel and stay there.
96705491d2cSKalle Valo  *
96805491d2cSKalle Valo  * @wiphy: wiphy device.
96978211e02SLee Jones  * @wdev: wireless device.
97005491d2cSKalle Valo  * @channel: channel to stay on.
97105491d2cSKalle Valo  * @duration: time in ms to remain on channel.
97257636058SLee Jones  * @cookie: cookie.
97305491d2cSKalle Valo  */
brcmf_p2p_remain_on_channel(struct wiphy * wiphy,struct wireless_dev * wdev,struct ieee80211_channel * channel,unsigned int duration,u64 * cookie)97405491d2cSKalle Valo int brcmf_p2p_remain_on_channel(struct wiphy *wiphy, struct wireless_dev *wdev,
97505491d2cSKalle Valo 				struct ieee80211_channel *channel,
97605491d2cSKalle Valo 				unsigned int duration, u64 *cookie)
97705491d2cSKalle Valo {
97805491d2cSKalle Valo 	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
97905491d2cSKalle Valo 	struct brcmf_p2p_info *p2p = &cfg->p2p;
98005491d2cSKalle Valo 	s32 err;
98105491d2cSKalle Valo 	u16 channel_nr;
98205491d2cSKalle Valo 
98305491d2cSKalle Valo 	channel_nr = ieee80211_frequency_to_channel(channel->center_freq);
98405491d2cSKalle Valo 	brcmf_dbg(TRACE, "Enter, channel: %d, duration ms (%d)\n", channel_nr,
98505491d2cSKalle Valo 		  duration);
98605491d2cSKalle Valo 
98705491d2cSKalle Valo 	err = brcmf_p2p_enable_discovery(p2p);
98805491d2cSKalle Valo 	if (err)
98905491d2cSKalle Valo 		goto exit;
99005491d2cSKalle Valo 	err = brcmf_p2p_discover_listen(p2p, channel_nr, duration);
99105491d2cSKalle Valo 	if (err)
99205491d2cSKalle Valo 		goto exit;
99305491d2cSKalle Valo 
99405491d2cSKalle Valo 	memcpy(&p2p->remain_on_channel, channel, sizeof(*channel));
99505491d2cSKalle Valo 	*cookie = p2p->remain_on_channel_cookie;
99605491d2cSKalle Valo 	cfg80211_ready_on_channel(wdev, *cookie, channel, duration, GFP_KERNEL);
99705491d2cSKalle Valo 
99805491d2cSKalle Valo exit:
99905491d2cSKalle Valo 	return err;
100005491d2cSKalle Valo }
100105491d2cSKalle Valo 
100205491d2cSKalle Valo 
100305491d2cSKalle Valo /**
100405491d2cSKalle Valo  * brcmf_p2p_notify_listen_complete() - p2p listen has completed.
100505491d2cSKalle Valo  *
100605491d2cSKalle Valo  * @ifp: interfac control.
100705491d2cSKalle Valo  * @e: event message. Not used, to make it usable for fweh event dispatcher.
100805491d2cSKalle Valo  * @data: payload of message. Not used.
100905491d2cSKalle Valo  *
101005491d2cSKalle Valo  */
brcmf_p2p_notify_listen_complete(struct brcmf_if * ifp,const struct brcmf_event_msg * e,void * data)101105491d2cSKalle Valo int brcmf_p2p_notify_listen_complete(struct brcmf_if *ifp,
101205491d2cSKalle Valo 				     const struct brcmf_event_msg *e,
101305491d2cSKalle Valo 				     void *data)
101405491d2cSKalle Valo {
101505491d2cSKalle Valo 	struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
101605491d2cSKalle Valo 	struct brcmf_p2p_info *p2p = &cfg->p2p;
101705491d2cSKalle Valo 
101805491d2cSKalle Valo 	brcmf_dbg(TRACE, "Enter\n");
101905491d2cSKalle Valo 	if (test_and_clear_bit(BRCMF_P2P_STATUS_DISCOVER_LISTEN,
102005491d2cSKalle Valo 			       &p2p->status)) {
102105491d2cSKalle Valo 		if (test_and_clear_bit(BRCMF_P2P_STATUS_WAITING_NEXT_AF_LISTEN,
102205491d2cSKalle Valo 				       &p2p->status)) {
102305491d2cSKalle Valo 			clear_bit(BRCMF_P2P_STATUS_WAITING_NEXT_ACT_FRAME,
102405491d2cSKalle Valo 				  &p2p->status);
102505491d2cSKalle Valo 			brcmf_dbg(INFO, "Listen DONE, wake up wait_next_af\n");
102605491d2cSKalle Valo 			complete(&p2p->wait_next_af);
102705491d2cSKalle Valo 		}
102805491d2cSKalle Valo 
102905491d2cSKalle Valo 		cfg80211_remain_on_channel_expired(&ifp->vif->wdev,
103005491d2cSKalle Valo 						   p2p->remain_on_channel_cookie,
103105491d2cSKalle Valo 						   &p2p->remain_on_channel,
103205491d2cSKalle Valo 						   GFP_KERNEL);
103305491d2cSKalle Valo 	}
103405491d2cSKalle Valo 	return 0;
103505491d2cSKalle Valo }
103605491d2cSKalle Valo 
103705491d2cSKalle Valo 
103805491d2cSKalle Valo /**
103905491d2cSKalle Valo  * brcmf_p2p_cancel_remain_on_channel() - cancel p2p listen state.
104005491d2cSKalle Valo  *
104105491d2cSKalle Valo  * @ifp: interfac control.
104205491d2cSKalle Valo  *
104305491d2cSKalle Valo  */
brcmf_p2p_cancel_remain_on_channel(struct brcmf_if * ifp)104405491d2cSKalle Valo void brcmf_p2p_cancel_remain_on_channel(struct brcmf_if *ifp)
104505491d2cSKalle Valo {
104605491d2cSKalle Valo 	if (!ifp)
104705491d2cSKalle Valo 		return;
104805491d2cSKalle Valo 	brcmf_p2p_set_discover_state(ifp, WL_P2P_DISC_ST_SCAN, 0, 0);
104905491d2cSKalle Valo 	brcmf_p2p_notify_listen_complete(ifp, NULL, NULL);
105005491d2cSKalle Valo }
105105491d2cSKalle Valo 
105205491d2cSKalle Valo 
105305491d2cSKalle Valo /**
105405491d2cSKalle Valo  * brcmf_p2p_act_frm_search() - search function for action frame.
105505491d2cSKalle Valo  *
105605491d2cSKalle Valo  * @p2p: p2p device.
105757636058SLee Jones  * @channel: channel on which action frame is to be trasmitted.
105805491d2cSKalle Valo  *
105905491d2cSKalle Valo  * search function to reach at common channel to send action frame. When
106005491d2cSKalle Valo  * channel is 0 then all social channels will be used to send af
106105491d2cSKalle Valo  */
brcmf_p2p_act_frm_search(struct brcmf_p2p_info * p2p,u16 channel)106205491d2cSKalle Valo static s32 brcmf_p2p_act_frm_search(struct brcmf_p2p_info *p2p, u16 channel)
106305491d2cSKalle Valo {
1064dcb1471bSRafał Miłecki 	struct brcmf_pub *drvr = p2p->cfg->pub;
106505491d2cSKalle Valo 	s32 err;
106605491d2cSKalle Valo 	u32 channel_cnt;
106705491d2cSKalle Valo 	u16 *default_chan_list;
106805491d2cSKalle Valo 	u32 i;
106905491d2cSKalle Valo 	struct brcmu_chan ch;
107005491d2cSKalle Valo 
107105491d2cSKalle Valo 	brcmf_dbg(TRACE, "Enter\n");
107205491d2cSKalle Valo 
107305491d2cSKalle Valo 	if (channel)
107405491d2cSKalle Valo 		channel_cnt = AF_PEER_SEARCH_CNT;
107505491d2cSKalle Valo 	else
107605491d2cSKalle Valo 		channel_cnt = SOCIAL_CHAN_CNT;
10776396bb22SKees Cook 	default_chan_list = kcalloc(channel_cnt, sizeof(*default_chan_list),
107805491d2cSKalle Valo 				    GFP_KERNEL);
107905491d2cSKalle Valo 	if (default_chan_list == NULL) {
1080dcb1471bSRafał Miłecki 		bphy_err(drvr, "channel list allocation failed\n");
108105491d2cSKalle Valo 		err = -ENOMEM;
108205491d2cSKalle Valo 		goto exit;
108305491d2cSKalle Valo 	}
108405491d2cSKalle Valo 	ch.bw = BRCMU_CHAN_BW_20;
108505491d2cSKalle Valo 	if (channel) {
108605491d2cSKalle Valo 		ch.chnum = channel;
108705491d2cSKalle Valo 		p2p->cfg->d11inf.encchspec(&ch);
108805491d2cSKalle Valo 		/* insert same channel to the chan_list */
108905491d2cSKalle Valo 		for (i = 0; i < channel_cnt; i++)
109005491d2cSKalle Valo 			default_chan_list[i] = ch.chspec;
109105491d2cSKalle Valo 	} else {
109205491d2cSKalle Valo 		ch.chnum = SOCIAL_CHAN_1;
109305491d2cSKalle Valo 		p2p->cfg->d11inf.encchspec(&ch);
109405491d2cSKalle Valo 		default_chan_list[0] = ch.chspec;
109505491d2cSKalle Valo 		ch.chnum = SOCIAL_CHAN_2;
109605491d2cSKalle Valo 		p2p->cfg->d11inf.encchspec(&ch);
109705491d2cSKalle Valo 		default_chan_list[1] = ch.chspec;
109805491d2cSKalle Valo 		ch.chnum = SOCIAL_CHAN_3;
109905491d2cSKalle Valo 		p2p->cfg->d11inf.encchspec(&ch);
110005491d2cSKalle Valo 		default_chan_list[2] = ch.chspec;
110105491d2cSKalle Valo 	}
110205491d2cSKalle Valo 	err = brcmf_p2p_escan(p2p, channel_cnt, default_chan_list,
1103c4958106SHante Meuleman 			      WL_P2P_DISC_ST_SEARCH, P2PAPI_BSSCFG_DEVICE);
110405491d2cSKalle Valo 	kfree(default_chan_list);
110505491d2cSKalle Valo exit:
110605491d2cSKalle Valo 	return err;
110705491d2cSKalle Valo }
110805491d2cSKalle Valo 
110905491d2cSKalle Valo 
111005491d2cSKalle Valo /**
111105491d2cSKalle Valo  * brcmf_p2p_afx_handler() - afx worker thread.
111205491d2cSKalle Valo  *
111305491d2cSKalle Valo  * @work:
111405491d2cSKalle Valo  *
111505491d2cSKalle Valo  */
brcmf_p2p_afx_handler(struct work_struct * work)111605491d2cSKalle Valo static void brcmf_p2p_afx_handler(struct work_struct *work)
111705491d2cSKalle Valo {
111805491d2cSKalle Valo 	struct afx_hdl *afx_hdl = container_of(work, struct afx_hdl, afx_work);
111905491d2cSKalle Valo 	struct brcmf_p2p_info *p2p = container_of(afx_hdl,
112005491d2cSKalle Valo 						  struct brcmf_p2p_info,
112105491d2cSKalle Valo 						  afx_hdl);
1122dcb1471bSRafał Miłecki 	struct brcmf_pub *drvr = p2p->cfg->pub;
112305491d2cSKalle Valo 	s32 err;
112405491d2cSKalle Valo 
112505491d2cSKalle Valo 	if (!afx_hdl->is_active)
112605491d2cSKalle Valo 		return;
112705491d2cSKalle Valo 
112805491d2cSKalle Valo 	if (afx_hdl->is_listen && afx_hdl->my_listen_chan)
112905491d2cSKalle Valo 		/* 100ms ~ 300ms */
113005491d2cSKalle Valo 		err = brcmf_p2p_discover_listen(p2p, afx_hdl->my_listen_chan,
1131e8a533cbSJason A. Donenfeld 						100 * get_random_u32_inclusive(1, 3));
113205491d2cSKalle Valo 	else
113305491d2cSKalle Valo 		err = brcmf_p2p_act_frm_search(p2p, afx_hdl->peer_listen_chan);
113405491d2cSKalle Valo 
113505491d2cSKalle Valo 	if (err) {
1136dcb1471bSRafał Miłecki 		bphy_err(drvr, "ERROR occurred! value is (%d)\n", err);
113705491d2cSKalle Valo 		if (test_bit(BRCMF_P2P_STATUS_FINDING_COMMON_CHANNEL,
113805491d2cSKalle Valo 			     &p2p->status))
113905491d2cSKalle Valo 			complete(&afx_hdl->act_frm_scan);
114005491d2cSKalle Valo 	}
114105491d2cSKalle Valo }
114205491d2cSKalle Valo 
114305491d2cSKalle Valo 
114405491d2cSKalle Valo /**
114505491d2cSKalle Valo  * brcmf_p2p_af_searching_channel() - search channel.
114605491d2cSKalle Valo  *
114705491d2cSKalle Valo  * @p2p: p2p device info struct.
114805491d2cSKalle Valo  *
114905491d2cSKalle Valo  */
brcmf_p2p_af_searching_channel(struct brcmf_p2p_info * p2p)115005491d2cSKalle Valo static s32 brcmf_p2p_af_searching_channel(struct brcmf_p2p_info *p2p)
115105491d2cSKalle Valo {
115205491d2cSKalle Valo 	struct afx_hdl *afx_hdl = &p2p->afx_hdl;
115305491d2cSKalle Valo 	struct brcmf_cfg80211_vif *pri_vif;
115405491d2cSKalle Valo 	s32 retry;
115505491d2cSKalle Valo 
115605491d2cSKalle Valo 	brcmf_dbg(TRACE, "Enter\n");
115705491d2cSKalle Valo 
115805491d2cSKalle Valo 	pri_vif = p2p->bss_idx[P2PAPI_BSSCFG_PRIMARY].vif;
115905491d2cSKalle Valo 
116005491d2cSKalle Valo 	reinit_completion(&afx_hdl->act_frm_scan);
116105491d2cSKalle Valo 	set_bit(BRCMF_P2P_STATUS_FINDING_COMMON_CHANNEL, &p2p->status);
116205491d2cSKalle Valo 	afx_hdl->is_active = true;
116305491d2cSKalle Valo 	afx_hdl->peer_chan = P2P_INVALID_CHANNEL;
116405491d2cSKalle Valo 
116505491d2cSKalle Valo 	/* Loop to wait until we find a peer's channel or the
116605491d2cSKalle Valo 	 * pending action frame tx is cancelled.
116705491d2cSKalle Valo 	 */
116805491d2cSKalle Valo 	retry = 0;
116905491d2cSKalle Valo 	while ((retry < P2P_CHANNEL_SYNC_RETRY) &&
117005491d2cSKalle Valo 	       (afx_hdl->peer_chan == P2P_INVALID_CHANNEL)) {
117105491d2cSKalle Valo 		afx_hdl->is_listen = false;
117205491d2cSKalle Valo 		brcmf_dbg(TRACE, "Scheduling action frame for sending.. (%d)\n",
117305491d2cSKalle Valo 			  retry);
117405491d2cSKalle Valo 		/* search peer on peer's listen channel */
117505491d2cSKalle Valo 		schedule_work(&afx_hdl->afx_work);
1176edb6d688SChung-Hsien Hsu 		wait_for_completion_timeout(&afx_hdl->act_frm_scan,
1177edb6d688SChung-Hsien Hsu 					    P2P_AF_FRM_SCAN_MAX_WAIT);
117805491d2cSKalle Valo 		if ((afx_hdl->peer_chan != P2P_INVALID_CHANNEL) ||
117905491d2cSKalle Valo 		    (!test_bit(BRCMF_P2P_STATUS_FINDING_COMMON_CHANNEL,
118005491d2cSKalle Valo 			       &p2p->status)))
118105491d2cSKalle Valo 			break;
118205491d2cSKalle Valo 
118305491d2cSKalle Valo 		if (afx_hdl->my_listen_chan) {
118405491d2cSKalle Valo 			brcmf_dbg(TRACE, "Scheduling listen peer, channel=%d\n",
118505491d2cSKalle Valo 				  afx_hdl->my_listen_chan);
118605491d2cSKalle Valo 			/* listen on my listen channel */
118705491d2cSKalle Valo 			afx_hdl->is_listen = true;
118805491d2cSKalle Valo 			schedule_work(&afx_hdl->afx_work);
118905491d2cSKalle Valo 			wait_for_completion_timeout(&afx_hdl->act_frm_scan,
1190edb6d688SChung-Hsien Hsu 						    P2P_AF_FRM_SCAN_MAX_WAIT);
119105491d2cSKalle Valo 		}
119205491d2cSKalle Valo 		if ((afx_hdl->peer_chan != P2P_INVALID_CHANNEL) ||
119305491d2cSKalle Valo 		    (!test_bit(BRCMF_P2P_STATUS_FINDING_COMMON_CHANNEL,
119405491d2cSKalle Valo 			       &p2p->status)))
119505491d2cSKalle Valo 			break;
119605491d2cSKalle Valo 		retry++;
119705491d2cSKalle Valo 
119805491d2cSKalle Valo 		/* if sta is connected or connecting, sleep for a while before
119905491d2cSKalle Valo 		 * retry af tx or finding a peer
120005491d2cSKalle Valo 		 */
120105491d2cSKalle Valo 		if (test_bit(BRCMF_VIF_STATUS_CONNECTED, &pri_vif->sme_state) ||
120205491d2cSKalle Valo 		    test_bit(BRCMF_VIF_STATUS_CONNECTING, &pri_vif->sme_state))
120305491d2cSKalle Valo 			msleep(P2P_DEFAULT_SLEEP_TIME_VSDB);
120405491d2cSKalle Valo 	}
120505491d2cSKalle Valo 
120605491d2cSKalle Valo 	brcmf_dbg(TRACE, "Completed search/listen peer_chan=%d\n",
120705491d2cSKalle Valo 		  afx_hdl->peer_chan);
120805491d2cSKalle Valo 	afx_hdl->is_active = false;
120905491d2cSKalle Valo 
121005491d2cSKalle Valo 	clear_bit(BRCMF_P2P_STATUS_FINDING_COMMON_CHANNEL, &p2p->status);
121105491d2cSKalle Valo 
121205491d2cSKalle Valo 	return afx_hdl->peer_chan;
121305491d2cSKalle Valo }
121405491d2cSKalle Valo 
121505491d2cSKalle Valo 
121605491d2cSKalle Valo /**
121705491d2cSKalle Valo  * brcmf_p2p_scan_finding_common_channel() - was escan used for finding channel
121805491d2cSKalle Valo  *
121905491d2cSKalle Valo  * @cfg: common configuration struct.
122005491d2cSKalle Valo  * @bi: bss info struct, result from scan.
122105491d2cSKalle Valo  *
122205491d2cSKalle Valo  */
brcmf_p2p_scan_finding_common_channel(struct brcmf_cfg80211_info * cfg,struct brcmf_bss_info_le * bi)122305491d2cSKalle Valo bool brcmf_p2p_scan_finding_common_channel(struct brcmf_cfg80211_info *cfg,
122405491d2cSKalle Valo 					   struct brcmf_bss_info_le *bi)
122505491d2cSKalle Valo 
122605491d2cSKalle Valo {
122705491d2cSKalle Valo 	struct brcmf_p2p_info *p2p = &cfg->p2p;
122805491d2cSKalle Valo 	struct afx_hdl *afx_hdl = &p2p->afx_hdl;
122905491d2cSKalle Valo 	struct brcmu_chan ch;
123005491d2cSKalle Valo 	u8 *ie;
123105491d2cSKalle Valo 	s32 err;
123205491d2cSKalle Valo 	u8 p2p_dev_addr[ETH_ALEN];
123305491d2cSKalle Valo 
123405491d2cSKalle Valo 	if (!test_bit(BRCMF_P2P_STATUS_FINDING_COMMON_CHANNEL, &p2p->status))
123505491d2cSKalle Valo 		return false;
123605491d2cSKalle Valo 
123705491d2cSKalle Valo 	if (bi == NULL) {
123805491d2cSKalle Valo 		brcmf_dbg(TRACE, "ACTION FRAME SCAN Done\n");
123905491d2cSKalle Valo 		if (afx_hdl->peer_chan == P2P_INVALID_CHANNEL)
124005491d2cSKalle Valo 			complete(&afx_hdl->act_frm_scan);
124105491d2cSKalle Valo 		return true;
124205491d2cSKalle Valo 	}
124305491d2cSKalle Valo 
124405491d2cSKalle Valo 	ie = ((u8 *)bi) + le16_to_cpu(bi->ie_offset);
124505491d2cSKalle Valo 	memset(p2p_dev_addr, 0, sizeof(p2p_dev_addr));
124605491d2cSKalle Valo 	err = cfg80211_get_p2p_attr(ie, le32_to_cpu(bi->ie_length),
124705491d2cSKalle Valo 				    IEEE80211_P2P_ATTR_DEVICE_INFO,
124805491d2cSKalle Valo 				    p2p_dev_addr, sizeof(p2p_dev_addr));
124905491d2cSKalle Valo 	if (err < 0)
125005491d2cSKalle Valo 		err = cfg80211_get_p2p_attr(ie, le32_to_cpu(bi->ie_length),
125105491d2cSKalle Valo 					    IEEE80211_P2P_ATTR_DEVICE_ID,
125205491d2cSKalle Valo 					    p2p_dev_addr, sizeof(p2p_dev_addr));
125305491d2cSKalle Valo 	if ((err >= 0) &&
125405491d2cSKalle Valo 	    (ether_addr_equal(p2p_dev_addr, afx_hdl->tx_dst_addr))) {
125505491d2cSKalle Valo 		if (!bi->ctl_ch) {
125605491d2cSKalle Valo 			ch.chspec = le16_to_cpu(bi->chanspec);
125705491d2cSKalle Valo 			cfg->d11inf.decchspec(&ch);
12584712d88aSRafał Miłecki 			bi->ctl_ch = ch.control_ch_num;
125905491d2cSKalle Valo 		}
126005491d2cSKalle Valo 		afx_hdl->peer_chan = bi->ctl_ch;
126105491d2cSKalle Valo 		brcmf_dbg(TRACE, "ACTION FRAME SCAN : Peer %pM found, channel : %d\n",
126205491d2cSKalle Valo 			  afx_hdl->tx_dst_addr, afx_hdl->peer_chan);
126305491d2cSKalle Valo 		complete(&afx_hdl->act_frm_scan);
126405491d2cSKalle Valo 	}
126505491d2cSKalle Valo 	return true;
126605491d2cSKalle Valo }
126705491d2cSKalle Valo 
126805491d2cSKalle Valo /**
126930fb1b27SRyohei Kondo  * brcmf_p2p_abort_action_frame() - abort action frame.
127030fb1b27SRyohei Kondo  *
127130fb1b27SRyohei Kondo  * @cfg: common configuration struct.
127230fb1b27SRyohei Kondo  *
127330fb1b27SRyohei Kondo  */
brcmf_p2p_abort_action_frame(struct brcmf_cfg80211_info * cfg)127430fb1b27SRyohei Kondo static s32 brcmf_p2p_abort_action_frame(struct brcmf_cfg80211_info *cfg)
127530fb1b27SRyohei Kondo {
127630fb1b27SRyohei Kondo 	struct brcmf_p2p_info *p2p = &cfg->p2p;
127730fb1b27SRyohei Kondo 	struct brcmf_cfg80211_vif *vif;
127830fb1b27SRyohei Kondo 	s32 err;
127930fb1b27SRyohei Kondo 	s32 int_val = 1;
128030fb1b27SRyohei Kondo 
128130fb1b27SRyohei Kondo 	brcmf_dbg(TRACE, "Enter\n");
128230fb1b27SRyohei Kondo 
128330fb1b27SRyohei Kondo 	vif = p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif;
128430fb1b27SRyohei Kondo 	err = brcmf_fil_bsscfg_data_set(vif->ifp, "actframe_abort", &int_val,
128530fb1b27SRyohei Kondo 					sizeof(s32));
128630fb1b27SRyohei Kondo 	if (err)
128730fb1b27SRyohei Kondo 		brcmf_err(" aborting action frame has failed (%d)\n", err);
128830fb1b27SRyohei Kondo 
128930fb1b27SRyohei Kondo 	return err;
129030fb1b27SRyohei Kondo }
129130fb1b27SRyohei Kondo 
129230fb1b27SRyohei Kondo /**
129305491d2cSKalle Valo  * brcmf_p2p_stop_wait_next_action_frame() - finish scan if af tx complete.
129405491d2cSKalle Valo  *
129505491d2cSKalle Valo  * @cfg: common configuration struct.
129605491d2cSKalle Valo  *
129705491d2cSKalle Valo  */
129805491d2cSKalle Valo static void
brcmf_p2p_stop_wait_next_action_frame(struct brcmf_cfg80211_info * cfg)129905491d2cSKalle Valo brcmf_p2p_stop_wait_next_action_frame(struct brcmf_cfg80211_info *cfg)
130005491d2cSKalle Valo {
130105491d2cSKalle Valo 	struct brcmf_p2p_info *p2p = &cfg->p2p;
13022aec2c9dSHante Meuleman 	struct brcmf_if *ifp = p2p->bss_idx[P2PAPI_BSSCFG_PRIMARY].vif->ifp;
130330fb1b27SRyohei Kondo 	s32 err;
130405491d2cSKalle Valo 
130505491d2cSKalle Valo 	if (test_bit(BRCMF_P2P_STATUS_SENDING_ACT_FRAME, &p2p->status) &&
130605491d2cSKalle Valo 	    (test_bit(BRCMF_P2P_STATUS_ACTION_TX_COMPLETED, &p2p->status) ||
130705491d2cSKalle Valo 	     test_bit(BRCMF_P2P_STATUS_ACTION_TX_NOACK, &p2p->status))) {
130805491d2cSKalle Valo 		brcmf_dbg(TRACE, "*** Wake UP ** abort actframe iovar\n");
130905491d2cSKalle Valo 		/* if channel is not zero, "actfame" uses off channel scan.
131005491d2cSKalle Valo 		 * So abort scan for off channel completion.
131105491d2cSKalle Valo 		 */
131230fb1b27SRyohei Kondo 		if (p2p->af_sent_channel) {
131330fb1b27SRyohei Kondo 			/* abort actframe using actframe_abort or abort scan */
131430fb1b27SRyohei Kondo 			err = brcmf_p2p_abort_action_frame(cfg);
131530fb1b27SRyohei Kondo 			if (err)
131630fb1b27SRyohei Kondo 				brcmf_notify_escan_complete(cfg, ifp, true,
131730fb1b27SRyohei Kondo 							    true);
131830fb1b27SRyohei Kondo 		}
131905491d2cSKalle Valo 	} else if (test_bit(BRCMF_P2P_STATUS_WAITING_NEXT_AF_LISTEN,
132005491d2cSKalle Valo 			    &p2p->status)) {
132105491d2cSKalle Valo 		brcmf_dbg(TRACE, "*** Wake UP ** abort listen for next af frame\n");
132205491d2cSKalle Valo 		/* So abort scan to cancel listen */
132305491d2cSKalle Valo 		brcmf_notify_escan_complete(cfg, ifp, true, true);
132405491d2cSKalle Valo 	}
132505491d2cSKalle Valo }
132605491d2cSKalle Valo 
132705491d2cSKalle Valo 
132805491d2cSKalle Valo /**
132905491d2cSKalle Valo  * brcmf_p2p_gon_req_collision() - Check if go negotiaton collission
133005491d2cSKalle Valo  *
133105491d2cSKalle Valo  * @p2p: p2p device info struct.
133257636058SLee Jones  * @mac: MAC address.
133305491d2cSKalle Valo  *
133405491d2cSKalle Valo  * return true if recevied action frame is to be dropped.
133505491d2cSKalle Valo  */
133605491d2cSKalle Valo static bool
brcmf_p2p_gon_req_collision(struct brcmf_p2p_info * p2p,u8 * mac)133705491d2cSKalle Valo brcmf_p2p_gon_req_collision(struct brcmf_p2p_info *p2p, u8 *mac)
133805491d2cSKalle Valo {
133905491d2cSKalle Valo 	struct brcmf_cfg80211_info *cfg = p2p->cfg;
134005491d2cSKalle Valo 	struct brcmf_if *ifp;
134105491d2cSKalle Valo 
134205491d2cSKalle Valo 	brcmf_dbg(TRACE, "Enter\n");
134305491d2cSKalle Valo 
134405491d2cSKalle Valo 	if (!test_bit(BRCMF_P2P_STATUS_WAITING_NEXT_ACT_FRAME, &p2p->status) ||
134505491d2cSKalle Valo 	    !p2p->gon_req_action)
134605491d2cSKalle Valo 		return false;
134705491d2cSKalle Valo 
134805491d2cSKalle Valo 	brcmf_dbg(TRACE, "GO Negotiation Request COLLISION !!!\n");
134905491d2cSKalle Valo 	/* if sa(peer) addr is less than da(my) addr, then this device
135005491d2cSKalle Valo 	 * process peer's gon request and block to send gon req.
135105491d2cSKalle Valo 	 * if not (sa addr > da addr),
135205491d2cSKalle Valo 	 * this device will process gon request and drop gon req of peer.
135305491d2cSKalle Valo 	 */
135405491d2cSKalle Valo 	ifp = p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif->ifp;
135505491d2cSKalle Valo 	if (memcmp(mac, ifp->mac_addr, ETH_ALEN) < 0) {
135605491d2cSKalle Valo 		brcmf_dbg(INFO, "Block transmit gon req !!!\n");
135705491d2cSKalle Valo 		p2p->block_gon_req_tx = true;
135805491d2cSKalle Valo 		/* if we are finding a common channel for sending af,
135905491d2cSKalle Valo 		 * do not scan more to block to send current gon req
136005491d2cSKalle Valo 		 */
136105491d2cSKalle Valo 		if (test_and_clear_bit(BRCMF_P2P_STATUS_FINDING_COMMON_CHANNEL,
136205491d2cSKalle Valo 				       &p2p->status))
136305491d2cSKalle Valo 			complete(&p2p->afx_hdl.act_frm_scan);
136405491d2cSKalle Valo 		if (test_and_clear_bit(BRCMF_P2P_STATUS_WAITING_NEXT_ACT_FRAME,
136505491d2cSKalle Valo 				       &p2p->status))
136605491d2cSKalle Valo 			brcmf_p2p_stop_wait_next_action_frame(cfg);
136705491d2cSKalle Valo 		return false;
136805491d2cSKalle Valo 	}
136905491d2cSKalle Valo 
137005491d2cSKalle Valo 	/* drop gon request of peer to process gon request by this device. */
137105491d2cSKalle Valo 	brcmf_dbg(INFO, "Drop received gon req !!!\n");
137205491d2cSKalle Valo 
137305491d2cSKalle Valo 	return true;
137405491d2cSKalle Valo }
137505491d2cSKalle Valo 
137605491d2cSKalle Valo 
137705491d2cSKalle Valo /**
137805491d2cSKalle Valo  * brcmf_p2p_notify_action_frame_rx() - received action frame.
137905491d2cSKalle Valo  *
138005491d2cSKalle Valo  * @ifp: interfac control.
138105491d2cSKalle Valo  * @e: event message. Not used, to make it usable for fweh event dispatcher.
138205491d2cSKalle Valo  * @data: payload of message, containing action frame data.
138305491d2cSKalle Valo  *
138405491d2cSKalle Valo  */
brcmf_p2p_notify_action_frame_rx(struct brcmf_if * ifp,const struct brcmf_event_msg * e,void * data)138505491d2cSKalle Valo int brcmf_p2p_notify_action_frame_rx(struct brcmf_if *ifp,
138605491d2cSKalle Valo 				     const struct brcmf_event_msg *e,
138705491d2cSKalle Valo 				     void *data)
138805491d2cSKalle Valo {
1389dcb1471bSRafał Miłecki 	struct brcmf_pub *drvr = ifp->drvr;
1390dcb1471bSRafał Miłecki 	struct brcmf_cfg80211_info *cfg = drvr->config;
139105491d2cSKalle Valo 	struct brcmf_p2p_info *p2p = &cfg->p2p;
139205491d2cSKalle Valo 	struct afx_hdl *afx_hdl = &p2p->afx_hdl;
139305491d2cSKalle Valo 	struct wireless_dev *wdev;
139405491d2cSKalle Valo 	u32 mgmt_frame_len = e->datalen - sizeof(struct brcmf_rx_mgmt_data);
139505491d2cSKalle Valo 	struct brcmf_rx_mgmt_data *rxframe = (struct brcmf_rx_mgmt_data *)data;
139605491d2cSKalle Valo 	u8 *frame = (u8 *)(rxframe + 1);
139705491d2cSKalle Valo 	struct brcmf_p2p_pub_act_frame *act_frm;
139805491d2cSKalle Valo 	struct brcmf_p2psd_gas_pub_act_frame *sd_act_frm;
139905491d2cSKalle Valo 	struct brcmu_chan ch;
140005491d2cSKalle Valo 	struct ieee80211_mgmt *mgmt_frame;
140105491d2cSKalle Valo 	s32 freq;
140205491d2cSKalle Valo 	u16 mgmt_type;
140305491d2cSKalle Valo 	u8 action;
140405491d2cSKalle Valo 
14050aedbcafSHante Meuleman 	if (e->datalen < sizeof(*rxframe)) {
14060aedbcafSHante Meuleman 		brcmf_dbg(SCAN, "Event data to small. Ignore\n");
14070aedbcafSHante Meuleman 		return 0;
14080aedbcafSHante Meuleman 	}
14090aedbcafSHante Meuleman 
141005491d2cSKalle Valo 	ch.chspec = be16_to_cpu(rxframe->chanspec);
141105491d2cSKalle Valo 	cfg->d11inf.decchspec(&ch);
141205491d2cSKalle Valo 	/* Check if wpa_supplicant has registered for this frame */
141305491d2cSKalle Valo 	brcmf_dbg(INFO, "ifp->vif->mgmt_rx_reg %04x\n", ifp->vif->mgmt_rx_reg);
141405491d2cSKalle Valo 	mgmt_type = (IEEE80211_STYPE_ACTION & IEEE80211_FCTL_STYPE) >> 4;
141505491d2cSKalle Valo 	if ((ifp->vif->mgmt_rx_reg & BIT(mgmt_type)) == 0)
141605491d2cSKalle Valo 		return 0;
141705491d2cSKalle Valo 
141805491d2cSKalle Valo 	brcmf_p2p_print_actframe(false, frame, mgmt_frame_len);
141905491d2cSKalle Valo 
142005491d2cSKalle Valo 	action = P2P_PAF_SUBTYPE_INVALID;
142105491d2cSKalle Valo 	if (brcmf_p2p_is_pub_action(frame, mgmt_frame_len)) {
142205491d2cSKalle Valo 		act_frm = (struct brcmf_p2p_pub_act_frame *)frame;
142305491d2cSKalle Valo 		action = act_frm->subtype;
142405491d2cSKalle Valo 		if ((action == P2P_PAF_GON_REQ) &&
142505491d2cSKalle Valo 		    (brcmf_p2p_gon_req_collision(p2p, (u8 *)e->addr))) {
142605491d2cSKalle Valo 			if (test_bit(BRCMF_P2P_STATUS_FINDING_COMMON_CHANNEL,
142705491d2cSKalle Valo 				     &p2p->status) &&
142805491d2cSKalle Valo 			    (ether_addr_equal(afx_hdl->tx_dst_addr, e->addr))) {
14294712d88aSRafał Miłecki 				afx_hdl->peer_chan = ch.control_ch_num;
143005491d2cSKalle Valo 				brcmf_dbg(INFO, "GON request: Peer found, channel=%d\n",
143105491d2cSKalle Valo 					  afx_hdl->peer_chan);
143205491d2cSKalle Valo 				complete(&afx_hdl->act_frm_scan);
143305491d2cSKalle Valo 			}
143405491d2cSKalle Valo 			return 0;
143505491d2cSKalle Valo 		}
143605491d2cSKalle Valo 		/* After complete GO Negotiation, roll back to mpc mode */
143705491d2cSKalle Valo 		if ((action == P2P_PAF_GON_CONF) ||
143805491d2cSKalle Valo 		    (action == P2P_PAF_PROVDIS_RSP))
143905491d2cSKalle Valo 			brcmf_set_mpc(ifp, 1);
144005491d2cSKalle Valo 		if (action == P2P_PAF_GON_CONF) {
144105491d2cSKalle Valo 			brcmf_dbg(TRACE, "P2P: GO_NEG_PHASE status cleared\n");
144205491d2cSKalle Valo 			clear_bit(BRCMF_P2P_STATUS_GO_NEG_PHASE, &p2p->status);
144305491d2cSKalle Valo 		}
144405491d2cSKalle Valo 	} else if (brcmf_p2p_is_gas_action(frame, mgmt_frame_len)) {
144505491d2cSKalle Valo 		sd_act_frm = (struct brcmf_p2psd_gas_pub_act_frame *)frame;
144605491d2cSKalle Valo 		action = sd_act_frm->action;
144705491d2cSKalle Valo 	}
144805491d2cSKalle Valo 
144905491d2cSKalle Valo 	if (test_bit(BRCMF_P2P_STATUS_WAITING_NEXT_ACT_FRAME, &p2p->status) &&
145005491d2cSKalle Valo 	    (p2p->next_af_subtype == action)) {
145105491d2cSKalle Valo 		brcmf_dbg(TRACE, "We got a right next frame! (%d)\n", action);
145205491d2cSKalle Valo 		clear_bit(BRCMF_P2P_STATUS_WAITING_NEXT_ACT_FRAME,
145305491d2cSKalle Valo 			  &p2p->status);
145405491d2cSKalle Valo 		/* Stop waiting for next AF. */
145505491d2cSKalle Valo 		brcmf_p2p_stop_wait_next_action_frame(cfg);
145605491d2cSKalle Valo 	}
145705491d2cSKalle Valo 
145805491d2cSKalle Valo 	mgmt_frame = kzalloc(offsetof(struct ieee80211_mgmt, u) +
145905491d2cSKalle Valo 			     mgmt_frame_len, GFP_KERNEL);
146005491d2cSKalle Valo 	if (!mgmt_frame) {
1461dcb1471bSRafał Miłecki 		bphy_err(drvr, "No memory available for action frame\n");
146205491d2cSKalle Valo 		return -ENOMEM;
146305491d2cSKalle Valo 	}
146405491d2cSKalle Valo 	memcpy(mgmt_frame->da, ifp->mac_addr, ETH_ALEN);
146505491d2cSKalle Valo 	brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSSID, mgmt_frame->bssid,
146605491d2cSKalle Valo 			       ETH_ALEN);
146705491d2cSKalle Valo 	memcpy(mgmt_frame->sa, e->addr, ETH_ALEN);
146805491d2cSKalle Valo 	mgmt_frame->frame_control = cpu_to_le16(IEEE80211_STYPE_ACTION);
1469*1a30a6b2SKees Cook 	memcpy(mgmt_frame->u.body, frame, mgmt_frame_len);
1470*1a30a6b2SKees Cook 	mgmt_frame_len += offsetof(struct ieee80211_mgmt, u.body);
147105491d2cSKalle Valo 
14724712d88aSRafał Miłecki 	freq = ieee80211_channel_to_frequency(ch.control_ch_num,
147305491d2cSKalle Valo 					      ch.band == BRCMU_CHAN_BAND_2G ?
147457fbcce3SJohannes Berg 					      NL80211_BAND_2GHZ :
147557fbcce3SJohannes Berg 					      NL80211_BAND_5GHZ);
147605491d2cSKalle Valo 
147705491d2cSKalle Valo 	wdev = &ifp->vif->wdev;
147805491d2cSKalle Valo 	cfg80211_rx_mgmt(wdev, freq, 0, (u8 *)mgmt_frame, mgmt_frame_len, 0);
147905491d2cSKalle Valo 
148005491d2cSKalle Valo 	kfree(mgmt_frame);
148105491d2cSKalle Valo 	return 0;
148205491d2cSKalle Valo }
148305491d2cSKalle Valo 
148405491d2cSKalle Valo 
148505491d2cSKalle Valo /**
148605491d2cSKalle Valo  * brcmf_p2p_notify_action_tx_complete() - transmit action frame complete
148705491d2cSKalle Valo  *
148805491d2cSKalle Valo  * @ifp: interfac control.
148905491d2cSKalle Valo  * @e: event message. Not used, to make it usable for fweh event dispatcher.
149005491d2cSKalle Valo  * @data: not used.
149105491d2cSKalle Valo  *
149205491d2cSKalle Valo  */
brcmf_p2p_notify_action_tx_complete(struct brcmf_if * ifp,const struct brcmf_event_msg * e,void * data)149305491d2cSKalle Valo int brcmf_p2p_notify_action_tx_complete(struct brcmf_if *ifp,
149405491d2cSKalle Valo 					const struct brcmf_event_msg *e,
149505491d2cSKalle Valo 					void *data)
149605491d2cSKalle Valo {
149705491d2cSKalle Valo 	struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
149805491d2cSKalle Valo 	struct brcmf_p2p_info *p2p = &cfg->p2p;
149905491d2cSKalle Valo 
150005491d2cSKalle Valo 	brcmf_dbg(INFO, "Enter: event %s, status=%d\n",
150105491d2cSKalle Valo 		  e->event_code == BRCMF_E_ACTION_FRAME_OFF_CHAN_COMPLETE ?
150205491d2cSKalle Valo 		  "ACTION_FRAME_OFF_CHAN_COMPLETE" : "ACTION_FRAME_COMPLETE",
150305491d2cSKalle Valo 		  e->status);
150405491d2cSKalle Valo 
150505491d2cSKalle Valo 	if (!test_bit(BRCMF_P2P_STATUS_SENDING_ACT_FRAME, &p2p->status))
150605491d2cSKalle Valo 		return 0;
150705491d2cSKalle Valo 
150805491d2cSKalle Valo 	if (e->event_code == BRCMF_E_ACTION_FRAME_COMPLETE) {
1509fbf07000SChung-Hsien Hsu 		if (e->status == BRCMF_E_STATUS_SUCCESS) {
151005491d2cSKalle Valo 			set_bit(BRCMF_P2P_STATUS_ACTION_TX_COMPLETED,
151105491d2cSKalle Valo 				&p2p->status);
1512fbf07000SChung-Hsien Hsu 			if (!p2p->wait_for_offchan_complete)
1513fbf07000SChung-Hsien Hsu 				complete(&p2p->send_af_done);
1514fbf07000SChung-Hsien Hsu 		} else {
151505491d2cSKalle Valo 			set_bit(BRCMF_P2P_STATUS_ACTION_TX_NOACK, &p2p->status);
151605491d2cSKalle Valo 			/* If there is no ack, we don't need to wait for
151705491d2cSKalle Valo 			 * WLC_E_ACTION_FRAME_OFFCHAN_COMPLETE event
151805491d2cSKalle Valo 			 */
151905491d2cSKalle Valo 			brcmf_p2p_stop_wait_next_action_frame(cfg);
152005491d2cSKalle Valo 		}
152105491d2cSKalle Valo 
152205491d2cSKalle Valo 	} else {
152305491d2cSKalle Valo 		complete(&p2p->send_af_done);
152405491d2cSKalle Valo 	}
152505491d2cSKalle Valo 	return 0;
152605491d2cSKalle Valo }
152705491d2cSKalle Valo 
152805491d2cSKalle Valo 
152905491d2cSKalle Valo /**
153005491d2cSKalle Valo  * brcmf_p2p_tx_action_frame() - send action frame over fil.
153105491d2cSKalle Valo  *
153205491d2cSKalle Valo  * @p2p: p2p info struct for vif.
153305491d2cSKalle Valo  * @af_params: action frame data/info.
153405491d2cSKalle Valo  *
153505491d2cSKalle Valo  * Send an action frame immediately without doing channel synchronization.
153605491d2cSKalle Valo  *
153705491d2cSKalle Valo  * This function waits for a completion event before returning.
153805491d2cSKalle Valo  * The WLC_E_ACTION_FRAME_COMPLETE event will be received when the action
153905491d2cSKalle Valo  * frame is transmitted.
154005491d2cSKalle Valo  */
brcmf_p2p_tx_action_frame(struct brcmf_p2p_info * p2p,struct brcmf_fil_af_params_le * af_params)154105491d2cSKalle Valo static s32 brcmf_p2p_tx_action_frame(struct brcmf_p2p_info *p2p,
154205491d2cSKalle Valo 				     struct brcmf_fil_af_params_le *af_params)
154305491d2cSKalle Valo {
1544dcb1471bSRafał Miłecki 	struct brcmf_pub *drvr = p2p->cfg->pub;
154505491d2cSKalle Valo 	struct brcmf_cfg80211_vif *vif;
1546d524d5ceSMadhan Mohan R 	struct brcmf_p2p_action_frame *p2p_af;
154705491d2cSKalle Valo 	s32 err = 0;
154805491d2cSKalle Valo 
154905491d2cSKalle Valo 	brcmf_dbg(TRACE, "Enter\n");
155005491d2cSKalle Valo 
155105491d2cSKalle Valo 	reinit_completion(&p2p->send_af_done);
155205491d2cSKalle Valo 	clear_bit(BRCMF_P2P_STATUS_ACTION_TX_COMPLETED, &p2p->status);
155305491d2cSKalle Valo 	clear_bit(BRCMF_P2P_STATUS_ACTION_TX_NOACK, &p2p->status);
155405491d2cSKalle Valo 
1555d524d5ceSMadhan Mohan R 	/* check if it is a p2p_presence response */
1556d524d5ceSMadhan Mohan R 	p2p_af = (struct brcmf_p2p_action_frame *)af_params->action_frame.data;
1557d524d5ceSMadhan Mohan R 	if (p2p_af->subtype == P2P_AF_PRESENCE_RSP)
1558d524d5ceSMadhan Mohan R 		vif = p2p->bss_idx[P2PAPI_BSSCFG_CONNECTION].vif;
1559d524d5ceSMadhan Mohan R 	else
156005491d2cSKalle Valo 		vif = p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif;
1561d524d5ceSMadhan Mohan R 
156205491d2cSKalle Valo 	err = brcmf_fil_bsscfg_data_set(vif->ifp, "actframe", af_params,
156305491d2cSKalle Valo 					sizeof(*af_params));
156405491d2cSKalle Valo 	if (err) {
1565dcb1471bSRafał Miłecki 		bphy_err(drvr, " sending action frame has failed\n");
156605491d2cSKalle Valo 		goto exit;
156705491d2cSKalle Valo 	}
156805491d2cSKalle Valo 
156905491d2cSKalle Valo 	p2p->af_sent_channel = le32_to_cpu(af_params->channel);
157005491d2cSKalle Valo 	p2p->af_tx_sent_jiffies = jiffies;
157105491d2cSKalle Valo 
1572fbf07000SChung-Hsien Hsu 	if (test_bit(BRCMF_P2P_STATUS_DISCOVER_LISTEN, &p2p->status) &&
1573fbf07000SChung-Hsien Hsu 	    p2p->af_sent_channel ==
1574fbf07000SChung-Hsien Hsu 	    ieee80211_frequency_to_channel(p2p->remain_on_channel.center_freq))
1575fbf07000SChung-Hsien Hsu 		p2p->wait_for_offchan_complete = false;
1576fbf07000SChung-Hsien Hsu 	else
1577fbf07000SChung-Hsien Hsu 		p2p->wait_for_offchan_complete = true;
1578fbf07000SChung-Hsien Hsu 
1579fbf07000SChung-Hsien Hsu 	brcmf_dbg(TRACE, "Waiting for %s tx completion event\n",
1580fbf07000SChung-Hsien Hsu 		  (p2p->wait_for_offchan_complete) ?
1581fbf07000SChung-Hsien Hsu 		   "off-channel" : "on-channel");
1582fbf07000SChung-Hsien Hsu 
15832de64ca7SLee Jones 	wait_for_completion_timeout(&p2p->send_af_done, P2P_AF_MAX_WAIT_TIME);
158405491d2cSKalle Valo 
158505491d2cSKalle Valo 	if (test_bit(BRCMF_P2P_STATUS_ACTION_TX_COMPLETED, &p2p->status)) {
158605491d2cSKalle Valo 		brcmf_dbg(TRACE, "TX action frame operation is success\n");
158705491d2cSKalle Valo 	} else {
158805491d2cSKalle Valo 		err = -EIO;
158905491d2cSKalle Valo 		brcmf_dbg(TRACE, "TX action frame operation has failed\n");
159005491d2cSKalle Valo 	}
159105491d2cSKalle Valo 	/* clear status bit for action tx */
159205491d2cSKalle Valo 	clear_bit(BRCMF_P2P_STATUS_ACTION_TX_COMPLETED, &p2p->status);
159305491d2cSKalle Valo 	clear_bit(BRCMF_P2P_STATUS_ACTION_TX_NOACK, &p2p->status);
159405491d2cSKalle Valo 
159505491d2cSKalle Valo exit:
159605491d2cSKalle Valo 	return err;
159705491d2cSKalle Valo }
159805491d2cSKalle Valo 
159905491d2cSKalle Valo 
160005491d2cSKalle Valo /**
160105491d2cSKalle Valo  * brcmf_p2p_pub_af_tx() - public action frame tx routine.
160205491d2cSKalle Valo  *
160305491d2cSKalle Valo  * @cfg: driver private data for cfg80211 interface.
160405491d2cSKalle Valo  * @af_params: action frame data/info.
160505491d2cSKalle Valo  * @config_af_params: configuration data for action frame.
160605491d2cSKalle Valo  *
160705491d2cSKalle Valo  * routine which transmits ation frame public type.
160805491d2cSKalle Valo  */
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)160905491d2cSKalle Valo static s32 brcmf_p2p_pub_af_tx(struct brcmf_cfg80211_info *cfg,
161005491d2cSKalle Valo 			       struct brcmf_fil_af_params_le *af_params,
161105491d2cSKalle Valo 			       struct brcmf_config_af_params *config_af_params)
161205491d2cSKalle Valo {
161305491d2cSKalle Valo 	struct brcmf_p2p_info *p2p = &cfg->p2p;
1614dcb1471bSRafał Miłecki 	struct brcmf_pub *drvr = cfg->pub;
161505491d2cSKalle Valo 	struct brcmf_fil_action_frame_le *action_frame;
161605491d2cSKalle Valo 	struct brcmf_p2p_pub_act_frame *act_frm;
161705491d2cSKalle Valo 	s32 err = 0;
161805491d2cSKalle Valo 	u16 ie_len;
161905491d2cSKalle Valo 
162005491d2cSKalle Valo 	action_frame = &af_params->action_frame;
162105491d2cSKalle Valo 	act_frm = (struct brcmf_p2p_pub_act_frame *)(action_frame->data);
162205491d2cSKalle Valo 
162305491d2cSKalle Valo 	config_af_params->extra_listen = true;
162405491d2cSKalle Valo 
162505491d2cSKalle Valo 	switch (act_frm->subtype) {
162605491d2cSKalle Valo 	case P2P_PAF_GON_REQ:
162705491d2cSKalle Valo 		brcmf_dbg(TRACE, "P2P: GO_NEG_PHASE status set\n");
162805491d2cSKalle Valo 		set_bit(BRCMF_P2P_STATUS_GO_NEG_PHASE, &p2p->status);
162905491d2cSKalle Valo 		config_af_params->mpc_onoff = 0;
163005491d2cSKalle Valo 		config_af_params->search_channel = true;
163105491d2cSKalle Valo 		p2p->next_af_subtype = act_frm->subtype + 1;
163205491d2cSKalle Valo 		p2p->gon_req_action = true;
163305491d2cSKalle Valo 		/* increase dwell time to wait for RESP frame */
163405491d2cSKalle Valo 		af_params->dwell_time = cpu_to_le32(P2P_AF_MED_DWELL_TIME);
163505491d2cSKalle Valo 		break;
163605491d2cSKalle Valo 	case P2P_PAF_GON_RSP:
163705491d2cSKalle Valo 		p2p->next_af_subtype = act_frm->subtype + 1;
163805491d2cSKalle Valo 		/* increase dwell time to wait for CONF frame */
163905491d2cSKalle Valo 		af_params->dwell_time = cpu_to_le32(P2P_AF_MED_DWELL_TIME);
164005491d2cSKalle Valo 		break;
164105491d2cSKalle Valo 	case P2P_PAF_GON_CONF:
164205491d2cSKalle Valo 		/* If we reached till GO Neg confirmation reset the filter */
164305491d2cSKalle Valo 		brcmf_dbg(TRACE, "P2P: GO_NEG_PHASE status cleared\n");
164405491d2cSKalle Valo 		clear_bit(BRCMF_P2P_STATUS_GO_NEG_PHASE, &p2p->status);
164505491d2cSKalle Valo 		/* turn on mpc again if go nego is done */
164605491d2cSKalle Valo 		config_af_params->mpc_onoff = 1;
164705491d2cSKalle Valo 		/* minimize dwell time */
164805491d2cSKalle Valo 		af_params->dwell_time = cpu_to_le32(P2P_AF_MIN_DWELL_TIME);
164905491d2cSKalle Valo 		config_af_params->extra_listen = false;
165005491d2cSKalle Valo 		break;
165105491d2cSKalle Valo 	case P2P_PAF_INVITE_REQ:
165205491d2cSKalle Valo 		config_af_params->search_channel = true;
165305491d2cSKalle Valo 		p2p->next_af_subtype = act_frm->subtype + 1;
165405491d2cSKalle Valo 		/* increase dwell time */
165505491d2cSKalle Valo 		af_params->dwell_time = cpu_to_le32(P2P_AF_MED_DWELL_TIME);
165605491d2cSKalle Valo 		break;
165705491d2cSKalle Valo 	case P2P_PAF_INVITE_RSP:
165805491d2cSKalle Valo 		/* minimize dwell time */
165905491d2cSKalle Valo 		af_params->dwell_time = cpu_to_le32(P2P_AF_MIN_DWELL_TIME);
166005491d2cSKalle Valo 		config_af_params->extra_listen = false;
166105491d2cSKalle Valo 		break;
166205491d2cSKalle Valo 	case P2P_PAF_DEVDIS_REQ:
166305491d2cSKalle Valo 		config_af_params->search_channel = true;
166405491d2cSKalle Valo 		p2p->next_af_subtype = act_frm->subtype + 1;
166505491d2cSKalle Valo 		/* maximize dwell time to wait for RESP frame */
166605491d2cSKalle Valo 		af_params->dwell_time = cpu_to_le32(P2P_AF_LONG_DWELL_TIME);
166705491d2cSKalle Valo 		break;
166805491d2cSKalle Valo 	case P2P_PAF_DEVDIS_RSP:
166905491d2cSKalle Valo 		/* minimize dwell time */
167005491d2cSKalle Valo 		af_params->dwell_time = cpu_to_le32(P2P_AF_MIN_DWELL_TIME);
167105491d2cSKalle Valo 		config_af_params->extra_listen = false;
167205491d2cSKalle Valo 		break;
167305491d2cSKalle Valo 	case P2P_PAF_PROVDIS_REQ:
167405491d2cSKalle Valo 		ie_len = le16_to_cpu(action_frame->len) -
167505491d2cSKalle Valo 			 offsetof(struct brcmf_p2p_pub_act_frame, elts);
167605491d2cSKalle Valo 		if (cfg80211_get_p2p_attr(&act_frm->elts[0], ie_len,
167705491d2cSKalle Valo 					  IEEE80211_P2P_ATTR_GROUP_ID,
167805491d2cSKalle Valo 					  NULL, 0) < 0)
167905491d2cSKalle Valo 			config_af_params->search_channel = true;
168005491d2cSKalle Valo 		config_af_params->mpc_onoff = 0;
168105491d2cSKalle Valo 		p2p->next_af_subtype = act_frm->subtype + 1;
168205491d2cSKalle Valo 		/* increase dwell time to wait for RESP frame */
168305491d2cSKalle Valo 		af_params->dwell_time = cpu_to_le32(P2P_AF_MED_DWELL_TIME);
168405491d2cSKalle Valo 		break;
168505491d2cSKalle Valo 	case P2P_PAF_PROVDIS_RSP:
168605491d2cSKalle Valo 		/* wpa_supplicant send go nego req right after prov disc */
168705491d2cSKalle Valo 		p2p->next_af_subtype = P2P_PAF_GON_REQ;
168805491d2cSKalle Valo 		/* increase dwell time to MED level */
168905491d2cSKalle Valo 		af_params->dwell_time = cpu_to_le32(P2P_AF_MED_DWELL_TIME);
169005491d2cSKalle Valo 		config_af_params->extra_listen = false;
169105491d2cSKalle Valo 		break;
169205491d2cSKalle Valo 	default:
1693dcb1471bSRafał Miłecki 		bphy_err(drvr, "Unknown p2p pub act frame subtype: %d\n",
169405491d2cSKalle Valo 			 act_frm->subtype);
169505491d2cSKalle Valo 		err = -EINVAL;
169605491d2cSKalle Valo 	}
169705491d2cSKalle Valo 	return err;
169805491d2cSKalle Valo }
169905491d2cSKalle Valo 
brcmf_p2p_check_dwell_overflow(u32 requested_dwell,unsigned long dwell_jiffies)1700ad96bc27SJoseph Chuang static bool brcmf_p2p_check_dwell_overflow(u32 requested_dwell,
17019c29da3fSJoseph Chuang 					   unsigned long dwell_jiffies)
17029c29da3fSJoseph Chuang {
17039c29da3fSJoseph Chuang 	if ((requested_dwell & CUSTOM_RETRY_MASK) &&
17049c29da3fSJoseph Chuang 	    (jiffies_to_msecs(jiffies - dwell_jiffies) >
17059c29da3fSJoseph Chuang 	    (requested_dwell & ~CUSTOM_RETRY_MASK))) {
17069c29da3fSJoseph Chuang 		brcmf_err("Action frame TX retry time over dwell time!\n");
17079c29da3fSJoseph Chuang 		return true;
17089c29da3fSJoseph Chuang 	}
17099c29da3fSJoseph Chuang 	return false;
17109c29da3fSJoseph Chuang }
171105491d2cSKalle Valo /**
171205491d2cSKalle Valo  * brcmf_p2p_send_action_frame() - send action frame .
171305491d2cSKalle Valo  *
171405491d2cSKalle Valo  * @cfg: driver private data for cfg80211 interface.
171505491d2cSKalle Valo  * @ndev: net device to transmit on.
171605491d2cSKalle Valo  * @af_params: configuration data for action frame.
171705491d2cSKalle Valo  */
brcmf_p2p_send_action_frame(struct brcmf_cfg80211_info * cfg,struct net_device * ndev,struct brcmf_fil_af_params_le * af_params)171805491d2cSKalle Valo bool brcmf_p2p_send_action_frame(struct brcmf_cfg80211_info *cfg,
171905491d2cSKalle Valo 				 struct net_device *ndev,
172005491d2cSKalle Valo 				 struct brcmf_fil_af_params_le *af_params)
172105491d2cSKalle Valo {
172205491d2cSKalle Valo 	struct brcmf_p2p_info *p2p = &cfg->p2p;
172305491d2cSKalle Valo 	struct brcmf_if *ifp = netdev_priv(ndev);
172405491d2cSKalle Valo 	struct brcmf_fil_action_frame_le *action_frame;
172505491d2cSKalle Valo 	struct brcmf_config_af_params config_af_params;
172605491d2cSKalle Valo 	struct afx_hdl *afx_hdl = &p2p->afx_hdl;
1727dcb1471bSRafał Miłecki 	struct brcmf_pub *drvr = cfg->pub;
172805491d2cSKalle Valo 	u16 action_frame_len;
172905491d2cSKalle Valo 	bool ack = false;
173005491d2cSKalle Valo 	u8 category;
173105491d2cSKalle Valo 	u8 action;
173205491d2cSKalle Valo 	s32 tx_retry;
173305491d2cSKalle Valo 	s32 extra_listen_time;
173405491d2cSKalle Valo 	uint delta_ms;
17359c29da3fSJoseph Chuang 	unsigned long dwell_jiffies = 0;
17369c29da3fSJoseph Chuang 	bool dwell_overflow = false;
17379c29da3fSJoseph Chuang 
1738ad96bc27SJoseph Chuang 	u32 requested_dwell = le32_to_cpu(af_params->dwell_time);
173905491d2cSKalle Valo 	action_frame = &af_params->action_frame;
174005491d2cSKalle Valo 	action_frame_len = le16_to_cpu(action_frame->len);
174105491d2cSKalle Valo 
174205491d2cSKalle Valo 	brcmf_p2p_print_actframe(true, action_frame->data, action_frame_len);
174305491d2cSKalle Valo 
174405491d2cSKalle Valo 	/* Add the default dwell time. Dwell time to stay off-channel */
174505491d2cSKalle Valo 	/* to wait for a response action frame after transmitting an  */
174605491d2cSKalle Valo 	/* GO Negotiation action frame                                */
174705491d2cSKalle Valo 	af_params->dwell_time = cpu_to_le32(P2P_AF_DWELL_TIME);
174805491d2cSKalle Valo 
174905491d2cSKalle Valo 	category = action_frame->data[DOT11_ACTION_CAT_OFF];
175005491d2cSKalle Valo 	action = action_frame->data[DOT11_ACTION_ACT_OFF];
175105491d2cSKalle Valo 
175205491d2cSKalle Valo 	/* initialize variables */
175305491d2cSKalle Valo 	p2p->next_af_subtype = P2P_PAF_SUBTYPE_INVALID;
175405491d2cSKalle Valo 	p2p->gon_req_action = false;
175505491d2cSKalle Valo 
175605491d2cSKalle Valo 	/* config parameters */
175705491d2cSKalle Valo 	config_af_params.mpc_onoff = -1;
175805491d2cSKalle Valo 	config_af_params.search_channel = false;
175905491d2cSKalle Valo 	config_af_params.extra_listen = false;
176005491d2cSKalle Valo 
176105491d2cSKalle Valo 	if (brcmf_p2p_is_pub_action(action_frame->data, action_frame_len)) {
176205491d2cSKalle Valo 		/* p2p public action frame process */
176305491d2cSKalle Valo 		if (brcmf_p2p_pub_af_tx(cfg, af_params, &config_af_params)) {
176405491d2cSKalle Valo 			/* Just send unknown subtype frame with */
176505491d2cSKalle Valo 			/* default parameters.                  */
1766dcb1471bSRafał Miłecki 			bphy_err(drvr, "P2P Public action frame, unknown subtype.\n");
176705491d2cSKalle Valo 		}
176805491d2cSKalle Valo 	} else if (brcmf_p2p_is_gas_action(action_frame->data,
176905491d2cSKalle Valo 					   action_frame_len)) {
177005491d2cSKalle Valo 		/* service discovery process */
177105491d2cSKalle Valo 		if (action == P2PSD_ACTION_ID_GAS_IREQ ||
177205491d2cSKalle Valo 		    action == P2PSD_ACTION_ID_GAS_CREQ) {
177305491d2cSKalle Valo 			/* configure service discovery query frame */
177405491d2cSKalle Valo 			config_af_params.search_channel = true;
177505491d2cSKalle Valo 
177605491d2cSKalle Valo 			/* save next af suptype to cancel */
177705491d2cSKalle Valo 			/* remaining dwell time           */
177805491d2cSKalle Valo 			p2p->next_af_subtype = action + 1;
177905491d2cSKalle Valo 
178005491d2cSKalle Valo 			af_params->dwell_time =
178105491d2cSKalle Valo 				cpu_to_le32(P2P_AF_MED_DWELL_TIME);
178205491d2cSKalle Valo 		} else if (action == P2PSD_ACTION_ID_GAS_IRESP ||
178305491d2cSKalle Valo 			   action == P2PSD_ACTION_ID_GAS_CRESP) {
178405491d2cSKalle Valo 			/* configure service discovery response frame */
178505491d2cSKalle Valo 			af_params->dwell_time =
178605491d2cSKalle Valo 				cpu_to_le32(P2P_AF_MIN_DWELL_TIME);
178705491d2cSKalle Valo 		} else {
1788dcb1471bSRafał Miłecki 			bphy_err(drvr, "Unknown action type: %d\n", action);
178905491d2cSKalle Valo 			goto exit;
179005491d2cSKalle Valo 		}
179105491d2cSKalle Valo 	} else if (brcmf_p2p_is_p2p_action(action_frame->data,
179205491d2cSKalle Valo 					   action_frame_len)) {
179305491d2cSKalle Valo 		/* do not configure anything. it will be */
179405491d2cSKalle Valo 		/* sent with a default configuration     */
179505491d2cSKalle Valo 	} else {
1796dcb1471bSRafał Miłecki 		bphy_err(drvr, "Unknown Frame: category 0x%x, action 0x%x\n",
179705491d2cSKalle Valo 			 category, action);
179805491d2cSKalle Valo 		return false;
179905491d2cSKalle Valo 	}
180005491d2cSKalle Valo 
180105491d2cSKalle Valo 	/* if connecting on primary iface, sleep for a while before sending
180205491d2cSKalle Valo 	 * af tx for VSDB
180305491d2cSKalle Valo 	 */
180405491d2cSKalle Valo 	if (test_bit(BRCMF_VIF_STATUS_CONNECTING,
180505491d2cSKalle Valo 		     &p2p->bss_idx[P2PAPI_BSSCFG_PRIMARY].vif->sme_state))
180605491d2cSKalle Valo 		msleep(50);
180705491d2cSKalle Valo 
180805491d2cSKalle Valo 	/* if scan is ongoing, abort current scan. */
180905491d2cSKalle Valo 	if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status))
181005491d2cSKalle Valo 		brcmf_abort_scanning(cfg);
181105491d2cSKalle Valo 
181205491d2cSKalle Valo 	memcpy(afx_hdl->tx_dst_addr, action_frame->da, ETH_ALEN);
181305491d2cSKalle Valo 
181405491d2cSKalle Valo 	/* To make sure to send successfully action frame, turn off mpc */
181505491d2cSKalle Valo 	if (config_af_params.mpc_onoff == 0)
181605491d2cSKalle Valo 		brcmf_set_mpc(ifp, 0);
181705491d2cSKalle Valo 
181805491d2cSKalle Valo 	/* set status and destination address before sending af */
181905491d2cSKalle Valo 	if (p2p->next_af_subtype != P2P_PAF_SUBTYPE_INVALID) {
182005491d2cSKalle Valo 		/* set status to cancel the remained dwell time in rx process */
182105491d2cSKalle Valo 		set_bit(BRCMF_P2P_STATUS_WAITING_NEXT_ACT_FRAME, &p2p->status);
182205491d2cSKalle Valo 	}
182305491d2cSKalle Valo 
182405491d2cSKalle Valo 	p2p->af_sent_channel = 0;
182505491d2cSKalle Valo 	set_bit(BRCMF_P2P_STATUS_SENDING_ACT_FRAME, &p2p->status);
182605491d2cSKalle Valo 	/* validate channel and p2p ies */
182705491d2cSKalle Valo 	if (config_af_params.search_channel &&
182805491d2cSKalle Valo 	    IS_P2P_SOCIAL_CHANNEL(le32_to_cpu(af_params->channel)) &&
182905491d2cSKalle Valo 	    p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif->saved_ie.probe_req_ie_len) {
183005491d2cSKalle Valo 		afx_hdl = &p2p->afx_hdl;
183105491d2cSKalle Valo 		afx_hdl->peer_listen_chan = le32_to_cpu(af_params->channel);
183205491d2cSKalle Valo 
183305491d2cSKalle Valo 		if (brcmf_p2p_af_searching_channel(p2p) ==
183405491d2cSKalle Valo 							P2P_INVALID_CHANNEL) {
1835dcb1471bSRafał Miłecki 			bphy_err(drvr, "Couldn't find peer's channel.\n");
183605491d2cSKalle Valo 			goto exit;
183705491d2cSKalle Valo 		}
183805491d2cSKalle Valo 
183905491d2cSKalle Valo 		/* Abort scan even for VSDB scenarios. Scan gets aborted in
184005491d2cSKalle Valo 		 * firmware but after the check of piggyback algorithm. To take
184105491d2cSKalle Valo 		 * care of current piggback algo, lets abort the scan here
184205491d2cSKalle Valo 		 * itself.
184305491d2cSKalle Valo 		 */
184405491d2cSKalle Valo 		brcmf_notify_escan_complete(cfg, ifp, true, true);
184505491d2cSKalle Valo 
184605491d2cSKalle Valo 		/* update channel */
184705491d2cSKalle Valo 		af_params->channel = cpu_to_le32(afx_hdl->peer_chan);
184805491d2cSKalle Valo 	}
18499c29da3fSJoseph Chuang 	dwell_jiffies = jiffies;
18509c29da3fSJoseph Chuang 	dwell_overflow = brcmf_p2p_check_dwell_overflow(requested_dwell,
18519c29da3fSJoseph Chuang 							dwell_jiffies);
185205491d2cSKalle Valo 
185305491d2cSKalle Valo 	tx_retry = 0;
185405491d2cSKalle Valo 	while (!p2p->block_gon_req_tx &&
18559c29da3fSJoseph Chuang 	       (!ack) && (tx_retry < P2P_AF_TX_MAX_RETRY) &&
18569c29da3fSJoseph Chuang 		!dwell_overflow) {
18577f26cedfSJustin Li 		if (af_params->channel)
18587f26cedfSJustin Li 			msleep(P2P_AF_RETRY_DELAY_TIME);
18597f26cedfSJustin Li 
186005491d2cSKalle Valo 		ack = !brcmf_p2p_tx_action_frame(p2p, af_params);
186105491d2cSKalle Valo 		tx_retry++;
18629c29da3fSJoseph Chuang 		dwell_overflow = brcmf_p2p_check_dwell_overflow(requested_dwell,
18639c29da3fSJoseph Chuang 								dwell_jiffies);
186405491d2cSKalle Valo 	}
186578a6fb42SJason Yan 	if (!ack) {
1866dcb1471bSRafał Miłecki 		bphy_err(drvr, "Failed to send Action Frame(retry %d)\n",
1867dcb1471bSRafał Miłecki 			 tx_retry);
186805491d2cSKalle Valo 		clear_bit(BRCMF_P2P_STATUS_GO_NEG_PHASE, &p2p->status);
186905491d2cSKalle Valo 	}
187005491d2cSKalle Valo 
187105491d2cSKalle Valo exit:
187205491d2cSKalle Valo 	clear_bit(BRCMF_P2P_STATUS_SENDING_ACT_FRAME, &p2p->status);
187305491d2cSKalle Valo 
187405491d2cSKalle Valo 	/* WAR: sometimes dongle does not keep the dwell time of 'actframe'.
187505491d2cSKalle Valo 	 * if we coundn't get the next action response frame and dongle does
187605491d2cSKalle Valo 	 * not keep the dwell time, go to listen state again to get next action
187705491d2cSKalle Valo 	 * response frame.
187805491d2cSKalle Valo 	 */
187905491d2cSKalle Valo 	if (ack && config_af_params.extra_listen && !p2p->block_gon_req_tx &&
188005491d2cSKalle Valo 	    test_bit(BRCMF_P2P_STATUS_WAITING_NEXT_ACT_FRAME, &p2p->status) &&
188105491d2cSKalle Valo 	    p2p->af_sent_channel == afx_hdl->my_listen_chan) {
188205491d2cSKalle Valo 		delta_ms = jiffies_to_msecs(jiffies - p2p->af_tx_sent_jiffies);
188305491d2cSKalle Valo 		if (le32_to_cpu(af_params->dwell_time) > delta_ms)
188405491d2cSKalle Valo 			extra_listen_time = le32_to_cpu(af_params->dwell_time) -
188505491d2cSKalle Valo 					    delta_ms;
188605491d2cSKalle Valo 		else
188705491d2cSKalle Valo 			extra_listen_time = 0;
188805491d2cSKalle Valo 		if (extra_listen_time > 50) {
188905491d2cSKalle Valo 			set_bit(BRCMF_P2P_STATUS_WAITING_NEXT_AF_LISTEN,
189005491d2cSKalle Valo 				&p2p->status);
189105491d2cSKalle Valo 			brcmf_dbg(INFO, "Wait more time! actual af time:%d, calculated extra listen:%d\n",
189205491d2cSKalle Valo 				  le32_to_cpu(af_params->dwell_time),
189305491d2cSKalle Valo 				  extra_listen_time);
189405491d2cSKalle Valo 			extra_listen_time += 100;
189505491d2cSKalle Valo 			if (!brcmf_p2p_discover_listen(p2p,
189605491d2cSKalle Valo 						       p2p->af_sent_channel,
189705491d2cSKalle Valo 						       extra_listen_time)) {
189805491d2cSKalle Valo 				unsigned long duration;
189905491d2cSKalle Valo 
190005491d2cSKalle Valo 				extra_listen_time += 100;
190105491d2cSKalle Valo 				duration = msecs_to_jiffies(extra_listen_time);
190205491d2cSKalle Valo 				wait_for_completion_timeout(&p2p->wait_next_af,
190305491d2cSKalle Valo 							    duration);
190405491d2cSKalle Valo 			}
190505491d2cSKalle Valo 			clear_bit(BRCMF_P2P_STATUS_WAITING_NEXT_AF_LISTEN,
190605491d2cSKalle Valo 				  &p2p->status);
190705491d2cSKalle Valo 		}
190805491d2cSKalle Valo 	}
190905491d2cSKalle Valo 
191005491d2cSKalle Valo 	if (p2p->block_gon_req_tx) {
191105491d2cSKalle Valo 		/* if ack is true, supplicant will wait more time(100ms).
191205491d2cSKalle Valo 		 * so we will return it as a success to get more time .
191305491d2cSKalle Valo 		 */
191405491d2cSKalle Valo 		p2p->block_gon_req_tx = false;
191505491d2cSKalle Valo 		ack = true;
191605491d2cSKalle Valo 	}
191705491d2cSKalle Valo 
191805491d2cSKalle Valo 	clear_bit(BRCMF_P2P_STATUS_WAITING_NEXT_ACT_FRAME, &p2p->status);
191905491d2cSKalle Valo 	/* if all done, turn mpc on again */
192005491d2cSKalle Valo 	if (config_af_params.mpc_onoff == 1)
192105491d2cSKalle Valo 		brcmf_set_mpc(ifp, 1);
192205491d2cSKalle Valo 
192305491d2cSKalle Valo 	return ack;
192405491d2cSKalle Valo }
192505491d2cSKalle Valo 
192605491d2cSKalle Valo /**
192705491d2cSKalle Valo  * brcmf_p2p_notify_rx_mgmt_p2p_probereq() - Event handler for p2p probe req.
192805491d2cSKalle Valo  *
192905491d2cSKalle Valo  * @ifp: interface pointer for which event was received.
193005491d2cSKalle Valo  * @e: even message.
193105491d2cSKalle Valo  * @data: payload of event message (probe request).
193205491d2cSKalle Valo  */
brcmf_p2p_notify_rx_mgmt_p2p_probereq(struct brcmf_if * ifp,const struct brcmf_event_msg * e,void * data)193305491d2cSKalle Valo s32 brcmf_p2p_notify_rx_mgmt_p2p_probereq(struct brcmf_if *ifp,
193405491d2cSKalle Valo 					  const struct brcmf_event_msg *e,
193505491d2cSKalle Valo 					  void *data)
193605491d2cSKalle Valo {
193705491d2cSKalle Valo 	struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
193805491d2cSKalle Valo 	struct brcmf_p2p_info *p2p = &cfg->p2p;
193905491d2cSKalle Valo 	struct afx_hdl *afx_hdl = &p2p->afx_hdl;
194005491d2cSKalle Valo 	struct brcmf_cfg80211_vif *vif = ifp->vif;
194105491d2cSKalle Valo 	struct brcmf_rx_mgmt_data *rxframe = (struct brcmf_rx_mgmt_data *)data;
194205491d2cSKalle Valo 	struct brcmu_chan ch;
194305491d2cSKalle Valo 	u8 *mgmt_frame;
194405491d2cSKalle Valo 	u32 mgmt_frame_len;
194505491d2cSKalle Valo 	s32 freq;
194605491d2cSKalle Valo 	u16 mgmt_type;
194705491d2cSKalle Valo 
194805491d2cSKalle Valo 	brcmf_dbg(INFO, "Enter: event %d reason %d\n", e->event_code,
194905491d2cSKalle Valo 		  e->reason);
195005491d2cSKalle Valo 
19510aedbcafSHante Meuleman 	if (e->datalen < sizeof(*rxframe)) {
19520aedbcafSHante Meuleman 		brcmf_dbg(SCAN, "Event data to small. Ignore\n");
19530aedbcafSHante Meuleman 		return 0;
19540aedbcafSHante Meuleman 	}
19550aedbcafSHante Meuleman 
195605491d2cSKalle Valo 	ch.chspec = be16_to_cpu(rxframe->chanspec);
195705491d2cSKalle Valo 	cfg->d11inf.decchspec(&ch);
195805491d2cSKalle Valo 
195905491d2cSKalle Valo 	if (test_bit(BRCMF_P2P_STATUS_FINDING_COMMON_CHANNEL, &p2p->status) &&
196005491d2cSKalle Valo 	    (ether_addr_equal(afx_hdl->tx_dst_addr, e->addr))) {
19614712d88aSRafał Miłecki 		afx_hdl->peer_chan = ch.control_ch_num;
196205491d2cSKalle Valo 		brcmf_dbg(INFO, "PROBE REQUEST: Peer found, channel=%d\n",
196305491d2cSKalle Valo 			  afx_hdl->peer_chan);
196405491d2cSKalle Valo 		complete(&afx_hdl->act_frm_scan);
196505491d2cSKalle Valo 	}
196605491d2cSKalle Valo 
196705491d2cSKalle Valo 	/* Firmware sends us two proberesponses for each idx one. At the */
196805491d2cSKalle Valo 	/* moment anything but bsscfgidx 0 is passed up to supplicant    */
196905491d2cSKalle Valo 	if (e->bsscfgidx == 0)
197005491d2cSKalle Valo 		return 0;
197105491d2cSKalle Valo 
197205491d2cSKalle Valo 	/* Filter any P2P probe reqs arriving during the GO-NEG Phase */
197305491d2cSKalle Valo 	if (test_bit(BRCMF_P2P_STATUS_GO_NEG_PHASE, &p2p->status)) {
197405491d2cSKalle Valo 		brcmf_dbg(INFO, "Filtering P2P probe_req in GO-NEG phase\n");
197505491d2cSKalle Valo 		return 0;
197605491d2cSKalle Valo 	}
197705491d2cSKalle Valo 
197805491d2cSKalle Valo 	/* Check if wpa_supplicant has registered for this frame */
197905491d2cSKalle Valo 	brcmf_dbg(INFO, "vif->mgmt_rx_reg %04x\n", vif->mgmt_rx_reg);
198005491d2cSKalle Valo 	mgmt_type = (IEEE80211_STYPE_PROBE_REQ & IEEE80211_FCTL_STYPE) >> 4;
198105491d2cSKalle Valo 	if ((vif->mgmt_rx_reg & BIT(mgmt_type)) == 0)
198205491d2cSKalle Valo 		return 0;
198305491d2cSKalle Valo 
198405491d2cSKalle Valo 	mgmt_frame = (u8 *)(rxframe + 1);
198505491d2cSKalle Valo 	mgmt_frame_len = e->datalen - sizeof(*rxframe);
19864712d88aSRafał Miłecki 	freq = ieee80211_channel_to_frequency(ch.control_ch_num,
198705491d2cSKalle Valo 					      ch.band == BRCMU_CHAN_BAND_2G ?
198857fbcce3SJohannes Berg 					      NL80211_BAND_2GHZ :
198957fbcce3SJohannes Berg 					      NL80211_BAND_5GHZ);
199005491d2cSKalle Valo 
199105491d2cSKalle Valo 	cfg80211_rx_mgmt(&vif->wdev, freq, 0, mgmt_frame, mgmt_frame_len, 0);
199205491d2cSKalle Valo 
199305491d2cSKalle Valo 	brcmf_dbg(INFO, "mgmt_frame_len (%d) , e->datalen (%d), chanspec (%04x), freq (%d)\n",
199473f2c8e9SKevin Cernekee 		  mgmt_frame_len, e->datalen, ch.chspec, freq);
199505491d2cSKalle Valo 
199605491d2cSKalle Valo 	return 0;
199705491d2cSKalle Valo }
199805491d2cSKalle Valo 
199905491d2cSKalle Valo 
200005491d2cSKalle Valo /**
200105491d2cSKalle Valo  * brcmf_p2p_get_current_chanspec() - Get current operation channel.
200205491d2cSKalle Valo  *
200305491d2cSKalle Valo  * @p2p: P2P specific data.
200405491d2cSKalle Valo  * @chanspec: chanspec to be returned.
200505491d2cSKalle Valo  */
brcmf_p2p_get_current_chanspec(struct brcmf_p2p_info * p2p,u16 * chanspec)200605491d2cSKalle Valo static void brcmf_p2p_get_current_chanspec(struct brcmf_p2p_info *p2p,
200705491d2cSKalle Valo 					   u16 *chanspec)
200805491d2cSKalle Valo {
200905491d2cSKalle Valo 	struct brcmf_if *ifp;
201005491d2cSKalle Valo 	u8 mac_addr[ETH_ALEN];
201105491d2cSKalle Valo 	struct brcmu_chan ch;
201205491d2cSKalle Valo 	struct brcmf_bss_info_le *bi;
201305491d2cSKalle Valo 	u8 *buf;
201405491d2cSKalle Valo 
201505491d2cSKalle Valo 	ifp = p2p->bss_idx[P2PAPI_BSSCFG_PRIMARY].vif->ifp;
201605491d2cSKalle Valo 
201705491d2cSKalle Valo 	if (brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSSID, mac_addr,
201805491d2cSKalle Valo 				   ETH_ALEN) == 0) {
201905491d2cSKalle Valo 		buf = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL);
202005491d2cSKalle Valo 		if (buf != NULL) {
202105491d2cSKalle Valo 			*(__le32 *)buf = cpu_to_le32(WL_BSS_INFO_MAX);
202205491d2cSKalle Valo 			if (brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSS_INFO,
202305491d2cSKalle Valo 						   buf, WL_BSS_INFO_MAX) == 0) {
202405491d2cSKalle Valo 				bi = (struct brcmf_bss_info_le *)(buf + 4);
202505491d2cSKalle Valo 				*chanspec = le16_to_cpu(bi->chanspec);
202605491d2cSKalle Valo 				kfree(buf);
202705491d2cSKalle Valo 				return;
202805491d2cSKalle Valo 			}
202905491d2cSKalle Valo 			kfree(buf);
203005491d2cSKalle Valo 		}
203105491d2cSKalle Valo 	}
203205491d2cSKalle Valo 	/* Use default channel for P2P */
203305491d2cSKalle Valo 	ch.chnum = BRCMF_P2P_TEMP_CHAN;
203405491d2cSKalle Valo 	ch.bw = BRCMU_CHAN_BW_20;
203505491d2cSKalle Valo 	p2p->cfg->d11inf.encchspec(&ch);
203605491d2cSKalle Valo 	*chanspec = ch.chspec;
203705491d2cSKalle Valo }
203805491d2cSKalle Valo 
203905491d2cSKalle Valo /**
2040ea3f903cSYang Shen  * brcmf_p2p_ifchange - Change a P2P Role.
204178211e02SLee Jones  * @cfg: driver private data for cfg80211 interface.
204257636058SLee Jones  * @if_type: interface type.
204305491d2cSKalle Valo  * Returns 0 if success.
204405491d2cSKalle Valo  */
brcmf_p2p_ifchange(struct brcmf_cfg80211_info * cfg,enum brcmf_fil_p2p_if_types if_type)204505491d2cSKalle Valo int brcmf_p2p_ifchange(struct brcmf_cfg80211_info *cfg,
204605491d2cSKalle Valo 		       enum brcmf_fil_p2p_if_types if_type)
204705491d2cSKalle Valo {
204805491d2cSKalle Valo 	struct brcmf_p2p_info *p2p = &cfg->p2p;
2049dcb1471bSRafał Miłecki 	struct brcmf_pub *drvr = cfg->pub;
205005491d2cSKalle Valo 	struct brcmf_cfg80211_vif *vif;
205105491d2cSKalle Valo 	struct brcmf_fil_p2p_if_le if_request;
205205491d2cSKalle Valo 	s32 err;
205305491d2cSKalle Valo 	u16 chanspec;
205405491d2cSKalle Valo 
205505491d2cSKalle Valo 	brcmf_dbg(TRACE, "Enter\n");
205605491d2cSKalle Valo 
205705491d2cSKalle Valo 	vif = p2p->bss_idx[P2PAPI_BSSCFG_PRIMARY].vif;
205805491d2cSKalle Valo 	if (!vif) {
2059dcb1471bSRafał Miłecki 		bphy_err(drvr, "vif for P2PAPI_BSSCFG_PRIMARY does not exist\n");
206005491d2cSKalle Valo 		return -EPERM;
206105491d2cSKalle Valo 	}
206205491d2cSKalle Valo 	brcmf_notify_escan_complete(cfg, vif->ifp, true, true);
206305491d2cSKalle Valo 	vif = p2p->bss_idx[P2PAPI_BSSCFG_CONNECTION].vif;
206405491d2cSKalle Valo 	if (!vif) {
2065dcb1471bSRafał Miłecki 		bphy_err(drvr, "vif for P2PAPI_BSSCFG_CONNECTION does not exist\n");
206605491d2cSKalle Valo 		return -EPERM;
206705491d2cSKalle Valo 	}
206805491d2cSKalle Valo 	brcmf_set_mpc(vif->ifp, 0);
206905491d2cSKalle Valo 
207005491d2cSKalle Valo 	/* In concurrency case, STA may be already associated in a particular */
207105491d2cSKalle Valo 	/* channel. so retrieve the current channel of primary interface and  */
207205491d2cSKalle Valo 	/* then start the virtual interface on that.                          */
207305491d2cSKalle Valo 	brcmf_p2p_get_current_chanspec(p2p, &chanspec);
207405491d2cSKalle Valo 
207505491d2cSKalle Valo 	if_request.type = cpu_to_le16((u16)if_type);
207605491d2cSKalle Valo 	if_request.chspec = cpu_to_le16(chanspec);
2077babfd3caSWright Feng 	memcpy(if_request.addr, p2p->conn_int_addr, sizeof(if_request.addr));
207805491d2cSKalle Valo 
207905491d2cSKalle Valo 	brcmf_cfg80211_arm_vif_event(cfg, vif);
208005491d2cSKalle Valo 	err = brcmf_fil_iovar_data_set(vif->ifp, "p2p_ifupd", &if_request,
208105491d2cSKalle Valo 				       sizeof(if_request));
208205491d2cSKalle Valo 	if (err) {
2083dcb1471bSRafał Miłecki 		bphy_err(drvr, "p2p_ifupd FAILED, err=%d\n", err);
208405491d2cSKalle Valo 		brcmf_cfg80211_arm_vif_event(cfg, NULL);
208505491d2cSKalle Valo 		return err;
208605491d2cSKalle Valo 	}
2087a9eb0c4bSArend van Spriel 	err = brcmf_cfg80211_wait_vif_event(cfg, BRCMF_E_IF_CHANGE,
208863ce3d5dSArend van Spriel 					    BRCMF_VIF_EVENT_TIMEOUT);
208905491d2cSKalle Valo 	brcmf_cfg80211_arm_vif_event(cfg, NULL);
209005491d2cSKalle Valo 	if (!err)  {
2091dcb1471bSRafał Miłecki 		bphy_err(drvr, "No BRCMF_E_IF_CHANGE event received\n");
209205491d2cSKalle Valo 		return -EIO;
209305491d2cSKalle Valo 	}
209405491d2cSKalle Valo 
209505491d2cSKalle Valo 	err = brcmf_fil_cmd_int_set(vif->ifp, BRCMF_C_SET_SCB_TIMEOUT,
209605491d2cSKalle Valo 				    BRCMF_SCB_TIMEOUT_VALUE);
209705491d2cSKalle Valo 
209805491d2cSKalle Valo 	return err;
209905491d2cSKalle Valo }
210005491d2cSKalle Valo 
brcmf_p2p_request_p2p_if(struct brcmf_p2p_info * p2p,struct brcmf_if * ifp,u8 ea[ETH_ALEN],enum brcmf_fil_p2p_if_types iftype)210105491d2cSKalle Valo static int brcmf_p2p_request_p2p_if(struct brcmf_p2p_info *p2p,
210205491d2cSKalle Valo 				    struct brcmf_if *ifp, u8 ea[ETH_ALEN],
210305491d2cSKalle Valo 				    enum brcmf_fil_p2p_if_types iftype)
210405491d2cSKalle Valo {
210505491d2cSKalle Valo 	struct brcmf_fil_p2p_if_le if_request;
210605491d2cSKalle Valo 	int err;
210705491d2cSKalle Valo 	u16 chanspec;
210805491d2cSKalle Valo 
210905491d2cSKalle Valo 	/* we need a default channel */
211005491d2cSKalle Valo 	brcmf_p2p_get_current_chanspec(p2p, &chanspec);
211105491d2cSKalle Valo 
211205491d2cSKalle Valo 	/* fill the firmware request */
211305491d2cSKalle Valo 	memcpy(if_request.addr, ea, ETH_ALEN);
211405491d2cSKalle Valo 	if_request.type = cpu_to_le16((u16)iftype);
211505491d2cSKalle Valo 	if_request.chspec = cpu_to_le16(chanspec);
211605491d2cSKalle Valo 
211705491d2cSKalle Valo 	err = brcmf_fil_iovar_data_set(ifp, "p2p_ifadd", &if_request,
211805491d2cSKalle Valo 				       sizeof(if_request));
211905491d2cSKalle Valo 
212005491d2cSKalle Valo 	return err;
212105491d2cSKalle Valo }
212205491d2cSKalle Valo 
brcmf_p2p_disable_p2p_if(struct brcmf_cfg80211_vif * vif)212305491d2cSKalle Valo static int brcmf_p2p_disable_p2p_if(struct brcmf_cfg80211_vif *vif)
212405491d2cSKalle Valo {
212505491d2cSKalle Valo 	struct brcmf_cfg80211_info *cfg = wdev_to_cfg(&vif->wdev);
212605491d2cSKalle Valo 	struct net_device *pri_ndev = cfg_to_ndev(cfg);
212705491d2cSKalle Valo 	struct brcmf_if *ifp = netdev_priv(pri_ndev);
2128fba610c5SJakub Kicinski 	const u8 *addr = vif->wdev.netdev->dev_addr;
212905491d2cSKalle Valo 
213005491d2cSKalle Valo 	return brcmf_fil_iovar_data_set(ifp, "p2p_ifdis", addr, ETH_ALEN);
213105491d2cSKalle Valo }
213205491d2cSKalle Valo 
brcmf_p2p_release_p2p_if(struct brcmf_cfg80211_vif * vif)213305491d2cSKalle Valo static int brcmf_p2p_release_p2p_if(struct brcmf_cfg80211_vif *vif)
213405491d2cSKalle Valo {
213505491d2cSKalle Valo 	struct brcmf_cfg80211_info *cfg = wdev_to_cfg(&vif->wdev);
213605491d2cSKalle Valo 	struct net_device *pri_ndev = cfg_to_ndev(cfg);
213705491d2cSKalle Valo 	struct brcmf_if *ifp = netdev_priv(pri_ndev);
2138fba610c5SJakub Kicinski 	const u8 *addr = vif->wdev.netdev->dev_addr;
213905491d2cSKalle Valo 
214005491d2cSKalle Valo 	return brcmf_fil_iovar_data_set(ifp, "p2p_ifdel", addr, ETH_ALEN);
214105491d2cSKalle Valo }
214205491d2cSKalle Valo 
214305491d2cSKalle Valo /**
214405491d2cSKalle Valo  * brcmf_p2p_create_p2pdev() - create a P2P_DEVICE virtual interface.
214505491d2cSKalle Valo  *
214605491d2cSKalle Valo  * @p2p: P2P specific data.
214705491d2cSKalle Valo  * @wiphy: wiphy device of new interface.
214805491d2cSKalle Valo  * @addr: mac address for this new interface.
214905491d2cSKalle Valo  */
brcmf_p2p_create_p2pdev(struct brcmf_p2p_info * p2p,struct wiphy * wiphy,u8 * addr)215005491d2cSKalle Valo static struct wireless_dev *brcmf_p2p_create_p2pdev(struct brcmf_p2p_info *p2p,
215105491d2cSKalle Valo 						    struct wiphy *wiphy,
215205491d2cSKalle Valo 						    u8 *addr)
215305491d2cSKalle Valo {
2154dcb1471bSRafał Miłecki 	struct brcmf_pub *drvr = p2p->cfg->pub;
215505491d2cSKalle Valo 	struct brcmf_cfg80211_vif *p2p_vif;
215605491d2cSKalle Valo 	struct brcmf_if *p2p_ifp;
215705491d2cSKalle Valo 	struct brcmf_if *pri_ifp;
215805491d2cSKalle Valo 	int err;
215937a869ecSHante Meuleman 	u32 bsscfgidx;
216005491d2cSKalle Valo 
216105491d2cSKalle Valo 	if (p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif)
216205491d2cSKalle Valo 		return ERR_PTR(-ENOSPC);
216305491d2cSKalle Valo 
216426072330SRafał Miłecki 	p2p_vif = brcmf_alloc_vif(p2p->cfg, NL80211_IFTYPE_P2P_DEVICE);
216505491d2cSKalle Valo 	if (IS_ERR(p2p_vif)) {
2166dcb1471bSRafał Miłecki 		bphy_err(drvr, "could not create discovery vif\n");
216705491d2cSKalle Valo 		return (struct wireless_dev *)p2p_vif;
216805491d2cSKalle Valo 	}
216905491d2cSKalle Valo 
217005491d2cSKalle Valo 	pri_ifp = p2p->bss_idx[P2PAPI_BSSCFG_PRIMARY].vif->ifp;
2171cb746e47SArend Van Spriel 
2172cb746e47SArend Van Spriel 	/* firmware requires unique mac address for p2pdev interface */
2173cb746e47SArend Van Spriel 	if (addr && ether_addr_equal(addr, pri_ifp->mac_addr)) {
2174dcb1471bSRafał Miłecki 		bphy_err(drvr, "discovery vif must be different from primary interface\n");
21755cc509aaSNavid Emamdoost 		err = -EINVAL;
21765cc509aaSNavid Emamdoost 		goto fail;
2177cb746e47SArend Van Spriel 	}
2178cb746e47SArend Van Spriel 
217905491d2cSKalle Valo 	brcmf_p2p_generate_bss_mac(p2p, addr);
218005491d2cSKalle Valo 	brcmf_p2p_set_firmware(pri_ifp, p2p->dev_addr);
218105491d2cSKalle Valo 
218205491d2cSKalle Valo 	brcmf_cfg80211_arm_vif_event(p2p->cfg, p2p_vif);
218305491d2cSKalle Valo 	brcmf_fweh_p2pdev_setup(pri_ifp, true);
218405491d2cSKalle Valo 
218505491d2cSKalle Valo 	/* Initialize P2P Discovery in the firmware */
218605491d2cSKalle Valo 	err = brcmf_fil_iovar_int_set(pri_ifp, "p2p_disc", 1);
218705491d2cSKalle Valo 	if (err < 0) {
2188dcb1471bSRafał Miłecki 		bphy_err(drvr, "set p2p_disc error\n");
218905491d2cSKalle Valo 		brcmf_fweh_p2pdev_setup(pri_ifp, false);
219005491d2cSKalle Valo 		brcmf_cfg80211_arm_vif_event(p2p->cfg, NULL);
219105491d2cSKalle Valo 		goto fail;
219205491d2cSKalle Valo 	}
219305491d2cSKalle Valo 
219405491d2cSKalle Valo 	/* wait for firmware event */
2195a9eb0c4bSArend van Spriel 	err = brcmf_cfg80211_wait_vif_event(p2p->cfg, BRCMF_E_IF_ADD,
219663ce3d5dSArend van Spriel 					    BRCMF_VIF_EVENT_TIMEOUT);
219705491d2cSKalle Valo 	brcmf_cfg80211_arm_vif_event(p2p->cfg, NULL);
219805491d2cSKalle Valo 	brcmf_fweh_p2pdev_setup(pri_ifp, false);
219905491d2cSKalle Valo 	if (!err) {
2200dcb1471bSRafał Miłecki 		bphy_err(drvr, "timeout occurred\n");
220105491d2cSKalle Valo 		err = -EIO;
220205491d2cSKalle Valo 		goto fail;
220305491d2cSKalle Valo 	}
220405491d2cSKalle Valo 
220505491d2cSKalle Valo 	/* discovery interface created */
220605491d2cSKalle Valo 	p2p_ifp = p2p_vif->ifp;
220705491d2cSKalle Valo 	p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif = p2p_vif;
220805491d2cSKalle Valo 	memcpy(p2p_ifp->mac_addr, p2p->dev_addr, ETH_ALEN);
220905491d2cSKalle Valo 	memcpy(&p2p_vif->wdev.address, p2p->dev_addr, sizeof(p2p->dev_addr));
221005491d2cSKalle Valo 
221105491d2cSKalle Valo 	/* verify bsscfg index for P2P discovery */
221237a869ecSHante Meuleman 	err = brcmf_fil_iovar_int_get(pri_ifp, "p2p_dev", &bsscfgidx);
221305491d2cSKalle Valo 	if (err < 0) {
2214dcb1471bSRafał Miłecki 		bphy_err(drvr, "retrieving discover bsscfg index failed\n");
221505491d2cSKalle Valo 		goto fail;
221605491d2cSKalle Valo 	}
221705491d2cSKalle Valo 
221837a869ecSHante Meuleman 	WARN_ON(p2p_ifp->bsscfgidx != bsscfgidx);
221905491d2cSKalle Valo 
222005491d2cSKalle Valo 	init_completion(&p2p->send_af_done);
222105491d2cSKalle Valo 	INIT_WORK(&p2p->afx_hdl.afx_work, brcmf_p2p_afx_handler);
222205491d2cSKalle Valo 	init_completion(&p2p->afx_hdl.act_frm_scan);
222305491d2cSKalle Valo 	init_completion(&p2p->wait_next_af);
222405491d2cSKalle Valo 
222505491d2cSKalle Valo 	return &p2p_vif->wdev;
222605491d2cSKalle Valo 
222705491d2cSKalle Valo fail:
222805491d2cSKalle Valo 	brcmf_free_vif(p2p_vif);
222905491d2cSKalle Valo 	return ERR_PTR(err);
223005491d2cSKalle Valo }
223105491d2cSKalle Valo 
brcmf_p2p_get_conn_idx(struct brcmf_cfg80211_info * cfg)22327294ee6fSChen Zhou static int brcmf_p2p_get_conn_idx(struct brcmf_cfg80211_info *cfg)
2233babfd3caSWright Feng {
2234babfd3caSWright Feng 	int i;
2235babfd3caSWright Feng 	struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
2236babfd3caSWright Feng 
2237babfd3caSWright Feng 	if (!ifp)
2238babfd3caSWright Feng 		return -ENODEV;
2239babfd3caSWright Feng 
2240babfd3caSWright Feng 	for (i = P2PAPI_BSSCFG_CONNECTION; i < P2PAPI_BSSCFG_MAX; i++) {
2241babfd3caSWright Feng 		if (!cfg->p2p.bss_idx[i].vif) {
2242babfd3caSWright Feng 			if (i == P2PAPI_BSSCFG_CONNECTION2 &&
2243babfd3caSWright Feng 			    !(brcmf_feat_is_enabled(ifp, BRCMF_FEAT_RSDB))) {
2244babfd3caSWright Feng 				brcmf_err("Multi p2p not supported");
2245babfd3caSWright Feng 				return -EIO;
2246babfd3caSWright Feng 			}
2247babfd3caSWright Feng 			return i;
2248babfd3caSWright Feng 		}
2249babfd3caSWright Feng 	}
2250babfd3caSWright Feng 	return -EIO;
2251babfd3caSWright Feng }
2252babfd3caSWright Feng 
225305491d2cSKalle Valo /**
225405491d2cSKalle Valo  * brcmf_p2p_add_vif() - create a new P2P virtual interface.
225505491d2cSKalle Valo  *
225605491d2cSKalle Valo  * @wiphy: wiphy device of new interface.
225705491d2cSKalle Valo  * @name: name of the new interface.
225805491d2cSKalle Valo  * @name_assign_type: origin of the interface name
225905491d2cSKalle Valo  * @type: nl80211 interface type.
226005491d2cSKalle Valo  * @params: contains mac address for P2P device.
226105491d2cSKalle Valo  */
brcmf_p2p_add_vif(struct wiphy * wiphy,const char * name,unsigned char name_assign_type,enum nl80211_iftype type,struct vif_params * params)226205491d2cSKalle Valo struct wireless_dev *brcmf_p2p_add_vif(struct wiphy *wiphy, const char *name,
226305491d2cSKalle Valo 				       unsigned char name_assign_type,
2264818a986eSJohannes Berg 				       enum nl80211_iftype type,
226505491d2cSKalle Valo 				       struct vif_params *params)
226605491d2cSKalle Valo {
226705491d2cSKalle Valo 	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
226805491d2cSKalle Valo 	struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
2269dcb1471bSRafał Miłecki 	struct brcmf_pub *drvr = cfg->pub;
227005491d2cSKalle Valo 	struct brcmf_cfg80211_vif *vif;
227105491d2cSKalle Valo 	enum brcmf_fil_p2p_if_types iftype;
2272babfd3caSWright Feng 	int err = 0;
2273babfd3caSWright Feng 	int connidx;
2274babfd3caSWright Feng 	u8 *p2p_intf_addr;
227505491d2cSKalle Valo 
227605491d2cSKalle Valo 	if (brcmf_cfg80211_vif_event_armed(cfg))
227705491d2cSKalle Valo 		return ERR_PTR(-EBUSY);
227805491d2cSKalle Valo 
227905491d2cSKalle Valo 	brcmf_dbg(INFO, "adding vif \"%s\" (type=%d)\n", name, type);
228005491d2cSKalle Valo 
228105491d2cSKalle Valo 	switch (type) {
228205491d2cSKalle Valo 	case NL80211_IFTYPE_P2P_CLIENT:
228305491d2cSKalle Valo 		iftype = BRCMF_FIL_P2P_IF_CLIENT;
228405491d2cSKalle Valo 		break;
228505491d2cSKalle Valo 	case NL80211_IFTYPE_P2P_GO:
228605491d2cSKalle Valo 		iftype = BRCMF_FIL_P2P_IF_GO;
228705491d2cSKalle Valo 		break;
228805491d2cSKalle Valo 	case NL80211_IFTYPE_P2P_DEVICE:
228905491d2cSKalle Valo 		return brcmf_p2p_create_p2pdev(&cfg->p2p, wiphy,
229005491d2cSKalle Valo 					       params->macaddr);
229105491d2cSKalle Valo 	default:
229205491d2cSKalle Valo 		return ERR_PTR(-EOPNOTSUPP);
229305491d2cSKalle Valo 	}
229405491d2cSKalle Valo 
229526072330SRafał Miłecki 	vif = brcmf_alloc_vif(cfg, type);
229605491d2cSKalle Valo 	if (IS_ERR(vif))
229705491d2cSKalle Valo 		return (struct wireless_dev *)vif;
229805491d2cSKalle Valo 	brcmf_cfg80211_arm_vif_event(cfg, vif);
229905491d2cSKalle Valo 
2300babfd3caSWright Feng 	connidx = brcmf_p2p_get_conn_idx(cfg);
2301babfd3caSWright Feng 
2302babfd3caSWright Feng 	if (connidx == P2PAPI_BSSCFG_CONNECTION)
2303babfd3caSWright Feng 		p2p_intf_addr = cfg->p2p.conn_int_addr;
2304babfd3caSWright Feng 	else if (connidx == P2PAPI_BSSCFG_CONNECTION2)
2305babfd3caSWright Feng 		p2p_intf_addr = cfg->p2p.conn2_int_addr;
2306babfd3caSWright Feng 	else
2307babfd3caSWright Feng 		err = -EINVAL;
2308babfd3caSWright Feng 
2309babfd3caSWright Feng 	if (!err)
2310babfd3caSWright Feng 		err =  brcmf_p2p_request_p2p_if(&cfg->p2p, ifp,
2311babfd3caSWright Feng 						p2p_intf_addr, iftype);
2312babfd3caSWright Feng 
231305491d2cSKalle Valo 	if (err) {
2314babfd3caSWright Feng 		brcmf_err("request p2p interface failed\n");
231505491d2cSKalle Valo 		brcmf_cfg80211_arm_vif_event(cfg, NULL);
231605491d2cSKalle Valo 		goto fail;
231705491d2cSKalle Valo 	}
231805491d2cSKalle Valo 
231905491d2cSKalle Valo 	/* wait for firmware event */
2320a9eb0c4bSArend van Spriel 	err = brcmf_cfg80211_wait_vif_event(cfg, BRCMF_E_IF_ADD,
232163ce3d5dSArend van Spriel 					    BRCMF_VIF_EVENT_TIMEOUT);
232205491d2cSKalle Valo 	brcmf_cfg80211_arm_vif_event(cfg, NULL);
232305491d2cSKalle Valo 	if (!err) {
2324dcb1471bSRafał Miłecki 		bphy_err(drvr, "timeout occurred\n");
232505491d2cSKalle Valo 		err = -EIO;
232605491d2cSKalle Valo 		goto fail;
232705491d2cSKalle Valo 	}
232805491d2cSKalle Valo 
232905491d2cSKalle Valo 	/* interface created in firmware */
233005491d2cSKalle Valo 	ifp = vif->ifp;
233105491d2cSKalle Valo 	if (!ifp) {
2332dcb1471bSRafał Miłecki 		bphy_err(drvr, "no if pointer provided\n");
233305491d2cSKalle Valo 		err = -ENOENT;
233405491d2cSKalle Valo 		goto fail;
233505491d2cSKalle Valo 	}
233605491d2cSKalle Valo 
233705491d2cSKalle Valo 	strncpy(ifp->ndev->name, name, sizeof(ifp->ndev->name) - 1);
233805491d2cSKalle Valo 	ifp->ndev->name_assign_type = name_assign_type;
233905491d2cSKalle Valo 	err = brcmf_net_attach(ifp, true);
234005491d2cSKalle Valo 	if (err) {
2341dcb1471bSRafał Miłecki 		bphy_err(drvr, "Registering netdevice failed\n");
2342dca2307eSArend Van Spriel 		free_netdev(ifp->ndev);
234305491d2cSKalle Valo 		goto fail;
234405491d2cSKalle Valo 	}
234505491d2cSKalle Valo 
2346babfd3caSWright Feng 	cfg->p2p.bss_idx[connidx].vif = vif;
234705491d2cSKalle Valo 	/* Disable firmware roaming for P2P interface  */
234805491d2cSKalle Valo 	brcmf_fil_iovar_int_set(ifp, "roam_off", 1);
234905491d2cSKalle Valo 	if (iftype == BRCMF_FIL_P2P_IF_GO) {
235005491d2cSKalle Valo 		/* set station timeout for p2p */
235105491d2cSKalle Valo 		brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCB_TIMEOUT,
235205491d2cSKalle Valo 				      BRCMF_SCB_TIMEOUT_VALUE);
235305491d2cSKalle Valo 	}
235405491d2cSKalle Valo 	return &ifp->vif->wdev;
235505491d2cSKalle Valo 
235605491d2cSKalle Valo fail:
235705491d2cSKalle Valo 	brcmf_free_vif(vif);
235805491d2cSKalle Valo 	return ERR_PTR(err);
235905491d2cSKalle Valo }
236005491d2cSKalle Valo 
236105491d2cSKalle Valo /**
236205491d2cSKalle Valo  * brcmf_p2p_del_vif() - delete a P2P virtual interface.
236305491d2cSKalle Valo  *
236405491d2cSKalle Valo  * @wiphy: wiphy device of interface.
236505491d2cSKalle Valo  * @wdev: wireless device of interface.
236605491d2cSKalle Valo  */
brcmf_p2p_del_vif(struct wiphy * wiphy,struct wireless_dev * wdev)236705491d2cSKalle Valo int brcmf_p2p_del_vif(struct wiphy *wiphy, struct wireless_dev *wdev)
236805491d2cSKalle Valo {
2369856d5a01SArend Van Spriel 	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
237005491d2cSKalle Valo 	struct brcmf_p2p_info *p2p = &cfg->p2p;
237105491d2cSKalle Valo 	struct brcmf_cfg80211_vif *vif;
2372d77facb8SArend Van Spriel 	enum nl80211_iftype iftype;
237305491d2cSKalle Valo 	bool wait_for_disable = false;
237405491d2cSKalle Valo 	int err;
237505491d2cSKalle Valo 
237605491d2cSKalle Valo 	brcmf_dbg(TRACE, "delete P2P vif\n");
237705491d2cSKalle Valo 	vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
237805491d2cSKalle Valo 
2379d77facb8SArend Van Spriel 	iftype = vif->wdev.iftype;
238005491d2cSKalle Valo 	brcmf_cfg80211_arm_vif_event(cfg, vif);
2381d77facb8SArend Van Spriel 	switch (iftype) {
238205491d2cSKalle Valo 	case NL80211_IFTYPE_P2P_CLIENT:
238305491d2cSKalle Valo 		if (test_bit(BRCMF_VIF_STATUS_DISCONNECTING, &vif->sme_state))
238405491d2cSKalle Valo 			wait_for_disable = true;
238505491d2cSKalle Valo 		break;
238605491d2cSKalle Valo 
238705491d2cSKalle Valo 	case NL80211_IFTYPE_P2P_GO:
238805491d2cSKalle Valo 		if (!brcmf_p2p_disable_p2p_if(vif))
238905491d2cSKalle Valo 			wait_for_disable = true;
239005491d2cSKalle Valo 		break;
239105491d2cSKalle Valo 
239205491d2cSKalle Valo 	case NL80211_IFTYPE_P2P_DEVICE:
239305491d2cSKalle Valo 		if (!p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif)
239405491d2cSKalle Valo 			return 0;
239505491d2cSKalle Valo 		brcmf_p2p_cancel_remain_on_channel(vif->ifp);
239605491d2cSKalle Valo 		brcmf_p2p_deinit_discovery(p2p);
239720856adfSRafał Miłecki 		break;
239820856adfSRafał Miłecki 
239905491d2cSKalle Valo 	default:
240005491d2cSKalle Valo 		return -ENOTSUPP;
240105491d2cSKalle Valo 	}
240205491d2cSKalle Valo 
240305491d2cSKalle Valo 	clear_bit(BRCMF_P2P_STATUS_GO_NEG_PHASE, &p2p->status);
240405491d2cSKalle Valo 	brcmf_dbg(INFO, "P2P: GO_NEG_PHASE status cleared\n");
240505491d2cSKalle Valo 
240605491d2cSKalle Valo 	if (wait_for_disable)
240705491d2cSKalle Valo 		wait_for_completion_timeout(&cfg->vif_disabled,
240863ce3d5dSArend van Spriel 					    BRCMF_P2P_DISABLE_TIMEOUT);
240905491d2cSKalle Valo 
241005491d2cSKalle Valo 	err = 0;
2411d77facb8SArend Van Spriel 	if (iftype != NL80211_IFTYPE_P2P_DEVICE) {
241205491d2cSKalle Valo 		brcmf_vif_clear_mgmt_ies(vif);
241305491d2cSKalle Valo 		err = brcmf_p2p_release_p2p_if(vif);
241405491d2cSKalle Valo 	}
241505491d2cSKalle Valo 	if (!err) {
241605491d2cSKalle Valo 		/* wait for firmware event */
2417a9eb0c4bSArend van Spriel 		err = brcmf_cfg80211_wait_vif_event(cfg, BRCMF_E_IF_DEL,
241863ce3d5dSArend van Spriel 						    BRCMF_VIF_EVENT_TIMEOUT);
241905491d2cSKalle Valo 		if (!err)
242005491d2cSKalle Valo 			err = -EIO;
242105491d2cSKalle Valo 		else
242205491d2cSKalle Valo 			err = 0;
242305491d2cSKalle Valo 	}
2424b50ddfa8SRafał Miłecki 	brcmf_remove_interface(vif->ifp, true);
242505491d2cSKalle Valo 
242605491d2cSKalle Valo 	brcmf_cfg80211_arm_vif_event(cfg, NULL);
2427c7caaa6fSBrian Henriquez 	if (iftype != NL80211_IFTYPE_P2P_DEVICE) {
2428c7caaa6fSBrian Henriquez 		if (vif == p2p->bss_idx[P2PAPI_BSSCFG_CONNECTION].vif)
242905491d2cSKalle Valo 			p2p->bss_idx[P2PAPI_BSSCFG_CONNECTION].vif = NULL;
2430c7caaa6fSBrian Henriquez 		if (vif == p2p->bss_idx[P2PAPI_BSSCFG_CONNECTION2].vif)
2431c7caaa6fSBrian Henriquez 			p2p->bss_idx[P2PAPI_BSSCFG_CONNECTION2].vif = NULL;
2432c7caaa6fSBrian Henriquez 	}
243305491d2cSKalle Valo 
243405491d2cSKalle Valo 	return err;
243505491d2cSKalle Valo }
243605491d2cSKalle Valo 
brcmf_p2p_ifp_removed(struct brcmf_if * ifp,bool locked)2437a05829a7SJohannes Berg void brcmf_p2p_ifp_removed(struct brcmf_if *ifp, bool locked)
243805491d2cSKalle Valo {
243905491d2cSKalle Valo 	struct brcmf_cfg80211_info *cfg;
244005491d2cSKalle Valo 	struct brcmf_cfg80211_vif *vif;
244105491d2cSKalle Valo 
244205491d2cSKalle Valo 	brcmf_dbg(INFO, "P2P: device interface removed\n");
244305491d2cSKalle Valo 	vif = ifp->vif;
244405491d2cSKalle Valo 	cfg = wdev_to_cfg(&vif->wdev);
244505491d2cSKalle Valo 	cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif = NULL;
2446bd83a2fcSHans de Goede 	if (!locked) {
244705491d2cSKalle Valo 		rtnl_lock();
2448a05829a7SJohannes Berg 		wiphy_lock(cfg->wiphy);
244905491d2cSKalle Valo 		cfg80211_unregister_wdev(&vif->wdev);
2450a05829a7SJohannes Berg 		wiphy_unlock(cfg->wiphy);
245105491d2cSKalle Valo 		rtnl_unlock();
2452a05829a7SJohannes Berg 	} else {
2453a05829a7SJohannes Berg 		cfg80211_unregister_wdev(&vif->wdev);
2454a05829a7SJohannes Berg 	}
245505491d2cSKalle Valo 	brcmf_free_vif(vif);
245605491d2cSKalle Valo }
245705491d2cSKalle Valo 
brcmf_p2p_start_device(struct wiphy * wiphy,struct wireless_dev * wdev)245805491d2cSKalle Valo int brcmf_p2p_start_device(struct wiphy *wiphy, struct wireless_dev *wdev)
245905491d2cSKalle Valo {
246005491d2cSKalle Valo 	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
246105491d2cSKalle Valo 	struct brcmf_p2p_info *p2p = &cfg->p2p;
246205491d2cSKalle Valo 	struct brcmf_cfg80211_vif *vif;
246305491d2cSKalle Valo 	int err;
246405491d2cSKalle Valo 
246505491d2cSKalle Valo 	vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
246605491d2cSKalle Valo 	mutex_lock(&cfg->usr_sync);
246705491d2cSKalle Valo 	err = brcmf_p2p_enable_discovery(p2p);
246805491d2cSKalle Valo 	if (!err)
246905491d2cSKalle Valo 		set_bit(BRCMF_VIF_STATUS_READY, &vif->sme_state);
247005491d2cSKalle Valo 	mutex_unlock(&cfg->usr_sync);
247105491d2cSKalle Valo 	return err;
247205491d2cSKalle Valo }
247305491d2cSKalle Valo 
brcmf_p2p_stop_device(struct wiphy * wiphy,struct wireless_dev * wdev)247405491d2cSKalle Valo void brcmf_p2p_stop_device(struct wiphy *wiphy, struct wireless_dev *wdev)
247505491d2cSKalle Valo {
247605491d2cSKalle Valo 	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
247705491d2cSKalle Valo 	struct brcmf_p2p_info *p2p = &cfg->p2p;
247805491d2cSKalle Valo 	struct brcmf_cfg80211_vif *vif;
247905491d2cSKalle Valo 
248005491d2cSKalle Valo 	vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
248105491d2cSKalle Valo 	/* This call can be result of the unregister_wdev call. In that case
248205491d2cSKalle Valo 	 * we dont want to do anything anymore. Just return. The config vif
248305491d2cSKalle Valo 	 * will have been cleared at this point.
248405491d2cSKalle Valo 	 */
248505491d2cSKalle Valo 	if (p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif == vif) {
248605491d2cSKalle Valo 		mutex_lock(&cfg->usr_sync);
248705491d2cSKalle Valo 		/* Set the discovery state to SCAN */
248805491d2cSKalle Valo 		(void)brcmf_p2p_set_discover_state(vif->ifp,
248905491d2cSKalle Valo 						   WL_P2P_DISC_ST_SCAN, 0, 0);
249005491d2cSKalle Valo 		brcmf_abort_scanning(cfg);
249105491d2cSKalle Valo 		clear_bit(BRCMF_VIF_STATUS_READY, &vif->sme_state);
249205491d2cSKalle Valo 		mutex_unlock(&cfg->usr_sync);
249305491d2cSKalle Valo 	}
249405491d2cSKalle Valo }
249505491d2cSKalle Valo 
249605491d2cSKalle Valo /**
249705491d2cSKalle Valo  * brcmf_p2p_attach() - attach for P2P.
249805491d2cSKalle Valo  *
249905491d2cSKalle Valo  * @cfg: driver private data for cfg80211 interface.
250005491d2cSKalle Valo  * @p2pdev_forced: create p2p device interface at attach.
250105491d2cSKalle Valo  */
brcmf_p2p_attach(struct brcmf_cfg80211_info * cfg,bool p2pdev_forced)250205491d2cSKalle Valo s32 brcmf_p2p_attach(struct brcmf_cfg80211_info *cfg, bool p2pdev_forced)
250305491d2cSKalle Valo {
2504dcb1471bSRafał Miłecki 	struct brcmf_pub *drvr = cfg->pub;
250505491d2cSKalle Valo 	struct brcmf_p2p_info *p2p;
250605491d2cSKalle Valo 	struct brcmf_if *pri_ifp;
250705491d2cSKalle Valo 	s32 err = 0;
250805491d2cSKalle Valo 	void *err_ptr;
250905491d2cSKalle Valo 
251005491d2cSKalle Valo 	p2p = &cfg->p2p;
251105491d2cSKalle Valo 	p2p->cfg = cfg;
251205491d2cSKalle Valo 
251305491d2cSKalle Valo 	pri_ifp = brcmf_get_ifp(cfg->pub, 0);
251405491d2cSKalle Valo 	p2p->bss_idx[P2PAPI_BSSCFG_PRIMARY].vif = pri_ifp->vif;
251505491d2cSKalle Valo 
251605491d2cSKalle Valo 	if (p2pdev_forced) {
251705491d2cSKalle Valo 		err_ptr = brcmf_p2p_create_p2pdev(p2p, NULL, NULL);
251805491d2cSKalle Valo 		if (IS_ERR(err_ptr)) {
2519dcb1471bSRafał Miłecki 			bphy_err(drvr, "P2P device creation failed.\n");
252005491d2cSKalle Valo 			err = PTR_ERR(err_ptr);
252105491d2cSKalle Valo 		}
252205491d2cSKalle Valo 	} else {
252305491d2cSKalle Valo 		p2p->p2pdev_dynamically = true;
252405491d2cSKalle Valo 	}
252505491d2cSKalle Valo 	return err;
252605491d2cSKalle Valo }
252705491d2cSKalle Valo 
252805491d2cSKalle Valo /**
252905491d2cSKalle Valo  * brcmf_p2p_detach() - detach P2P.
253005491d2cSKalle Valo  *
253105491d2cSKalle Valo  * @p2p: P2P specific data.
253205491d2cSKalle Valo  */
brcmf_p2p_detach(struct brcmf_p2p_info * p2p)253305491d2cSKalle Valo void brcmf_p2p_detach(struct brcmf_p2p_info *p2p)
253405491d2cSKalle Valo {
253505491d2cSKalle Valo 	struct brcmf_cfg80211_vif *vif;
253605491d2cSKalle Valo 
253705491d2cSKalle Valo 	vif = p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif;
253805491d2cSKalle Valo 	if (vif != NULL) {
253905491d2cSKalle Valo 		brcmf_p2p_cancel_remain_on_channel(vif->ifp);
254005491d2cSKalle Valo 		brcmf_p2p_deinit_discovery(p2p);
2541b50ddfa8SRafał Miłecki 		brcmf_remove_interface(vif->ifp, false);
254205491d2cSKalle Valo 	}
254305491d2cSKalle Valo 	/* just set it all to zero */
254405491d2cSKalle Valo 	memset(p2p, 0, sizeof(*p2p));
254505491d2cSKalle Valo }
254605491d2cSKalle Valo 
2547