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