blob: 7e2f23873d153e8e9744b5889269d5000ae1b05b [file] [log] [blame]
/*
* Copyright (c) 2012 Broadcom Corporation
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "p2p.h"
#include <threads.h>
#include "bits.h"
#include "brcmu_utils.h"
#include "brcmu_wifi.h"
#include "cfg80211.h"
#include "core.h"
#include "debug.h"
#include "defs.h"
#include "fwil.h"
#include "fwil_types.h"
#include "linuxisms.h"
#include "workqueue.h"
/* parameters used for p2p escan */
#define P2PAPI_SCAN_NPROBES 1
#define P2PAPI_SCAN_DWELL_TIME_MS 80
#define P2PAPI_SCAN_SOCIAL_DWELL_TIME_MS 40
#define P2PAPI_SCAN_HOME_TIME_MS 60
#define P2PAPI_SCAN_NPROBS_TIME_MS 30
#define P2PAPI_SCAN_AF_SEARCH_DWELL_TIME_MS 100
#define WL_SCAN_CONNECT_DWELL_TIME_MS 200
#define WL_SCAN_JOIN_PROBE_INTERVAL_MS 20
#define BRCMF_P2P_WILDCARD_SSID "DIRECT-"
#define BRCMF_P2P_WILDCARD_SSID_LEN (sizeof(BRCMF_P2P_WILDCARD_SSID) - 1)
#define SOCIAL_CHAN_1 1
#define SOCIAL_CHAN_2 6
#define SOCIAL_CHAN_3 11
#define IS_P2P_SOCIAL_CHANNEL(channel) \
((channel == SOCIAL_CHAN_1) || (channel == SOCIAL_CHAN_2) || (channel == SOCIAL_CHAN_3))
#define BRCMF_P2P_TEMP_CHAN SOCIAL_CHAN_3
#define SOCIAL_CHAN_CNT 3
#define AF_PEER_SEARCH_CNT 2
#define BRCMF_SCB_TIMEOUT_VALUE 20
#define P2P_VER 9 /* P2P version: 9=WiFi P2P v1.0 */
#define P2P_PUB_AF_CATEGORY 0x04
#define P2P_PUB_AF_ACTION 0x09
#define P2P_AF_CATEGORY 0x7f
#define P2P_OUI "\x50\x6F\x9A" /* P2P OUI */
#define P2P_OUI_LEN 3 /* P2P OUI length */
/* Action Frame Constants */
#define DOT11_ACTION_HDR_LEN 2 /* action frame category + action */
#define DOT11_ACTION_CAT_OFF 0 /* category offset */
#define DOT11_ACTION_ACT_OFF 1 /* action offset */
#define P2P_AF_DWELL_TIME 200
#define P2P_AF_MIN_DWELL_TIME 100
#define P2P_AF_MED_DWELL_TIME 400
#define P2P_AF_LONG_DWELL_TIME 1000
#define P2P_AF_TX_MAX_RETRY 1
#define P2P_AF_MAX_WAIT_TIME_MSEC (2000)
#define P2P_INVALID_CHANNEL -1
#define P2P_CHANNEL_SYNC_RETRY 5
#define P2P_AF_FRM_SCAN_MAX_WAIT_MSEC (1500)
#define P2P_DEFAULT_SLEEP_TIME_VSDB 200
/* WiFi P2P Public Action Frame OUI Subtypes */
#define P2P_PAF_GON_REQ 0 /* Group Owner Negotiation Req */
#define P2P_PAF_GON_RSP 1 /* Group Owner Negotiation Rsp */
#define P2P_PAF_GON_CONF 2 /* Group Owner Negotiation Confirm */
#define P2P_PAF_INVITE_REQ 3 /* P2P Invitation Request */
#define P2P_PAF_INVITE_RSP 4 /* P2P Invitation Response */
#define P2P_PAF_DEVDIS_REQ 5 /* Device Discoverability Request */
#define P2P_PAF_DEVDIS_RSP 6 /* Device Discoverability Response */
#define P2P_PAF_PROVDIS_REQ 7 /* Provision Discovery Request */
#define P2P_PAF_PROVDIS_RSP 8 /* Provision Discovery Response */
#define P2P_PAF_SUBTYPE_INVALID 255 /* Invalid Subtype */
/* WiFi P2P Action Frame OUI Subtypes */
#define P2P_AF_NOTICE_OF_ABSENCE 0 /* Notice of Absence */
#define P2P_AF_PRESENCE_REQ 1 /* P2P Presence Request */
#define P2P_AF_PRESENCE_RSP 2 /* P2P Presence Response */
#define P2P_AF_GO_DISC_REQ 3 /* GO Discoverability Request */
/* P2P Service Discovery related */
#define P2PSD_ACTION_CATEGORY 0x04 /* Public action frame */
#define P2PSD_ACTION_ID_GAS_IREQ 0x0a /* GAS Initial Request AF */
#define P2PSD_ACTION_ID_GAS_IRESP 0x0b /* GAS Initial Response AF */
#define P2PSD_ACTION_ID_GAS_CREQ 0x0c /* GAS Comback Request AF */
#define P2PSD_ACTION_ID_GAS_CRESP 0x0d /* GAS Comback Response AF */
#define BRCMF_P2P_DISABLE_TIMEOUT_MSEC (500)
/**
* struct brcmf_p2p_disc_st_le - set discovery state in firmware.
*
* @state: requested discovery state (see enum brcmf_p2p_disc_state).
* @chspec: channel parameter for %WL_P2P_DISC_ST_LISTEN state.
* @dwell: dwell time in ms for %WL_P2P_DISC_ST_LISTEN state.
*/
struct brcmf_p2p_disc_st_le {
uint8_t state;
uint16_t chspec;
uint16_t dwell;
};
/**
* enum brcmf_p2p_disc_state - P2P discovery state values
*
* @WL_P2P_DISC_ST_SCAN: P2P discovery with wildcard SSID and P2P IE.
* @WL_P2P_DISC_ST_LISTEN: P2P discovery off-channel for specified time.
* @WL_P2P_DISC_ST_SEARCH: P2P discovery with P2P wildcard SSID and P2P IE.
*/
enum brcmf_p2p_disc_state { WL_P2P_DISC_ST_SCAN, WL_P2P_DISC_ST_LISTEN, WL_P2P_DISC_ST_SEARCH };
/**
* struct brcmf_p2p_scan_le - P2P specific scan request.
*
* @type: type of scan method requested (values: 'E' or 'S').
* @reserved: reserved (ignored).
* @eparams: parameters used for type 'E'.
* @sparams: parameters used for type 'S'.
*/
struct brcmf_p2p_scan_le {
uint8_t type;
uint8_t reserved[3];
union {
struct brcmf_escan_params_le eparams;
struct brcmf_scan_params_le sparams;
};
};
/**
* struct brcmf_p2p_pub_act_frame - WiFi P2P Public Action Frame
*
* @category: P2P_PUB_AF_CATEGORY
* @action: P2P_PUB_AF_ACTION
* @oui[3]: P2P_OUI
* @oui_type: OUI type - P2P_VER
* @subtype: OUI subtype - P2P_TYPE_*
* @dialog_token: nonzero, identifies req/rsp transaction
* @elts[1]: Variable length information elements.
*/
struct brcmf_p2p_pub_act_frame {
uint8_t category;
uint8_t action;
uint8_t oui[3];
uint8_t oui_type;
uint8_t subtype;
uint8_t dialog_token;
uint8_t elts[1];
};
/**
* struct brcmf_p2p_action_frame - WiFi P2P Action Frame
*
* @category: P2P_AF_CATEGORY
* @OUI[3]: OUI - P2P_OUI
* @type: OUI Type - P2P_VER
* @subtype: OUI Subtype - P2P_AF_*
* @dialog_token: nonzero, identifies req/resp transaction
* @elts[1]: Variable length information elements.
*/
struct brcmf_p2p_action_frame {
uint8_t category;
uint8_t oui[3];
uint8_t type;
uint8_t subtype;
uint8_t dialog_token;
uint8_t elts[1];
};
/**
* struct brcmf_p2psd_gas_pub_act_frame - Wi-Fi GAS Public Action Frame
*
* @category: 0x04 Public Action Frame
* @action: 0x6c Advertisement Protocol
* @dialog_token: nonzero, identifies req/rsp transaction
* @query_data[1]: Query Data. SD gas ireq SD gas iresp
*/
struct brcmf_p2psd_gas_pub_act_frame {
uint8_t category;
uint8_t action;
uint8_t dialog_token;
uint8_t query_data[1];
};
/**
* struct brcmf_config_af_params - Action Frame Parameters for tx.
*
* @mpc_onoff: To make sure to send successfully action frame, we have to
* turn off mpc 0: off, 1: on, (-1): do nothing
* @search_channel: 1: search peer's channel to send af
* extra_listen: keep the dwell time to get af response frame.
*/
struct brcmf_config_af_params {
int32_t mpc_onoff;
bool search_channel;
bool extra_listen;
};
/**
* brcmf_p2p_set_discover_state - set discover state in firmware.
*
* @ifp: low-level interface object.
* @state: discover state to set.
* @chanspec: channel parameters (for state @WL_P2P_DISC_ST_LISTEN only).
* @listen_ms: duration to listen (for state @WL_P2P_DISC_ST_LISTEN only).
*/
static zx_status_t brcmf_p2p_set_discover_state(struct brcmf_if* ifp, uint8_t state,
uint16_t chanspec, uint16_t listen_ms) {
struct brcmf_p2p_disc_st_le discover_state;
zx_status_t ret = ZX_OK;
BRCMF_DBG(TRACE, "enter\n");
discover_state.state = state;
discover_state.chspec = chanspec;
discover_state.dwell = listen_ms;
ret = brcmf_fil_bsscfg_data_set(ifp, "p2p_state", &discover_state, sizeof(discover_state));
return ret;
}
/**
* brcmf_p2p_enable_discovery() - initialize and configure discovery.
*
* @p2p: P2P specific data.
*
* Initializes the discovery device and configure the virtual interface.
*/
static zx_status_t brcmf_p2p_enable_discovery(struct brcmf_p2p_info* p2p) {
struct brcmf_cfg80211_vif* vif;
zx_status_t ret = ZX_OK;
BRCMF_DBG(TRACE, "enter\n");
vif = p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif;
if (!vif) {
BRCMF_ERR("P2P config device not available\n");
ret = ZX_ERR_UNAVAILABLE;
goto exit;
}
if (brcmf_test_bit_in_array(BRCMF_P2P_STATUS_ENABLED, &p2p->status)) {
BRCMF_DBG(INFO, "P2P config device already configured\n");
goto exit;
}
/* Re-initialize P2P Discovery in the firmware */
vif = p2p->bss_idx[P2PAPI_BSSCFG_PRIMARY].vif;
ret = brcmf_fil_iovar_int_set(vif->ifp, "p2p_disc", 1, nullptr);
if (ret != ZX_OK) {
BRCMF_ERR("set p2p_disc error\n");
goto exit;
}
vif = p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif;
ret = brcmf_p2p_set_discover_state(vif->ifp, WL_P2P_DISC_ST_SCAN, 0, 0);
if (ret != ZX_OK) {
BRCMF_ERR("unable to set WL_P2P_DISC_ST_SCAN\n");
goto exit;
}
/*
* Set wsec to any non-zero value in the discovery bsscfg
* to ensure our P2P probe responses have the privacy bit
* set in the 802.11 WPA IE. Some peer devices may not
* initiate WPS with us if this bit is not set.
*/
ret = brcmf_fil_bsscfg_int_set(vif->ifp, "wsec", AES_ENABLED);
if (ret != ZX_OK) {
BRCMF_ERR("wsec error %d\n", ret);
goto exit;
}
brcmf_set_bit_in_array(BRCMF_P2P_STATUS_ENABLED, &p2p->status);
exit:
return ret;
}
/**
* brcmf_p2p_scan_finding_common_channel() - was escan used for finding channel
*
* @cfg: common configuration struct.
* @bi: bss info struct, result from scan.
*
*/
bool brcmf_p2p_scan_finding_common_channel(struct brcmf_cfg80211_info* cfg,
struct brcmf_bss_info_le* bi)
{
struct brcmf_p2p_info* p2p = &cfg->p2p;
struct afx_hdl* afx_hdl = &p2p->afx_hdl;
struct brcmu_chan ch;
uint8_t* ie;
zx_status_t err;
uint8_t p2p_dev_addr[ETH_ALEN];
if (!brcmf_test_bit_in_array(BRCMF_P2P_STATUS_FINDING_COMMON_CHANNEL, &p2p->status)) {
return false;
}
if (bi == NULL) {
BRCMF_DBG(TRACE, "ACTION FRAME SCAN Done\n");
if (afx_hdl->peer_chan == P2P_INVALID_CHANNEL) {
sync_completion_signal(&afx_hdl->act_frm_scan);
}
return true;
}
ie = ((uint8_t*)bi) + bi->ie_offset;
memset(p2p_dev_addr, 0, sizeof(p2p_dev_addr));
err = cfg80211_get_p2p_attr(ie, bi->ie_length, IEEE80211_P2P_ATTR_DEVICE_INFO, p2p_dev_addr,
sizeof(p2p_dev_addr));
if (err != ZX_OK)
err = cfg80211_get_p2p_attr(ie, bi->ie_length, IEEE80211_P2P_ATTR_DEVICE_ID, p2p_dev_addr,
sizeof(p2p_dev_addr));
if ((err == ZX_OK) && (ether_addr_equal(p2p_dev_addr, afx_hdl->tx_dst_addr))) {
if (!bi->ctl_ch) {
ch.chspec = bi->chanspec;
cfg->d11inf.decchspec(&ch);
bi->ctl_ch = ch.control_ch_num;
}
afx_hdl->peer_chan = bi->ctl_ch;
BRCMF_DBG(TRACE, "ACTION FRAME SCAN : Peer %pM found, channel : %d\n", afx_hdl->tx_dst_addr,
afx_hdl->peer_chan);
sync_completion_signal(&afx_hdl->act_frm_scan);
}
return true;
}
/**
* brcmf_p2p_get_current_chanspec() - Get current operation channel.
*
* @p2p: P2P specific data.
* @chanspec: chanspec to be returned.
*/
static void brcmf_p2p_get_current_chanspec(struct brcmf_p2p_info* p2p, uint16_t* chanspec) {
struct brcmf_if* ifp;
uint8_t mac_addr[ETH_ALEN];
struct brcmu_chan ch;
struct brcmf_bss_info_le* bi;
uint8_t* buf;
ifp = p2p->bss_idx[P2PAPI_BSSCFG_PRIMARY].vif->ifp;
if (brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSSID, mac_addr, ETH_ALEN, nullptr) == ZX_OK) {
buf = static_cast<decltype(buf)>(calloc(1, WL_BSS_INFO_MAX));
if (buf != NULL) {
*(uint32_t*)buf = WL_BSS_INFO_MAX;
if (brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSS_INFO, buf, WL_BSS_INFO_MAX, nullptr) ==
ZX_OK) {
bi = (struct brcmf_bss_info_le*)(buf + 4);
*chanspec = bi->chanspec;
free(buf);
return;
}
free(buf);
}
}
/* Use default channel for P2P */
ch.chnum = BRCMF_P2P_TEMP_CHAN;
ch.bw = BRCMU_CHAN_BW_20;
p2p->cfg->d11inf.encchspec(&ch);
*chanspec = ch.chspec;
}
/**
* Change a P2P Role.
* Parameters:
* @mac: MAC address of the BSS to change a role
* Returns 0 if success.
*/
zx_status_t brcmf_p2p_ifchange(struct brcmf_cfg80211_info* cfg,
enum brcmf_fil_p2p_if_types if_type) {
struct brcmf_p2p_info* p2p = &cfg->p2p;
struct brcmf_cfg80211_vif* vif;
struct brcmf_fil_p2p_if_le if_request;
zx_status_t err;
uint16_t chanspec;
BRCMF_DBG(TRACE, "Enter\n");
vif = p2p->bss_idx[P2PAPI_BSSCFG_PRIMARY].vif;
if (!vif) {
BRCMF_ERR("vif for P2PAPI_BSSCFG_PRIMARY does not exist\n");
return ZX_ERR_UNAVAILABLE;
}
brcmf_notify_escan_complete(cfg, vif->ifp, true, true);
vif = p2p->bss_idx[P2PAPI_BSSCFG_CONNECTION].vif;
if (!vif) {
BRCMF_ERR("vif for P2PAPI_BSSCFG_CONNECTION does not exist\n");
return ZX_ERR_UNAVAILABLE;
}
brcmf_set_mpc(vif->ifp, 0);
/* In concurrency case, STA may be already associated in a particular */
/* channel. so retrieve the current channel of primary interface and */
/* then start the virtual interface on that. */
brcmf_p2p_get_current_chanspec(p2p, &chanspec);
if_request.type = (uint16_t)if_type;
if_request.chspec = chanspec;
memcpy(if_request.addr, p2p->int_addr, sizeof(if_request.addr));
// Every code path but this one returns ZX_ERR_UNAVAILABLE if another event was left armed.
// It may be a bug that the test was omitted here, or maybe this is a special case that
// needs to be concurrent (but I doubt it, because the code doesn't support concurrency
// at all). After we get the wlan-generic driver, and see whether we need to support
// concurrency, either put in a paranoid-test like in all the other code paths, or support
// it properly.
if (brcmf_cfg80211_vif_event_armed(cfg)) {
// TODO(cphoenix): Deal with this better, or prevent it
BRCMF_ERR(" * * Concurrent vif events should never happen.");
}
brcmf_cfg80211_arm_vif_event(cfg, vif, BRCMF_E_IF_CHANGE);
err = brcmf_fil_iovar_data_set(vif->ifp, "p2p_ifupd", &if_request, sizeof(if_request), nullptr);
if (err != ZX_OK) {
BRCMF_ERR("p2p_ifupd FAILED, err=%d\n", err);
brcmf_cfg80211_disarm_vif_event(cfg);
return err;
}
err = brcmf_cfg80211_wait_vif_event(cfg, ZX_MSEC(BRCMF_VIF_EVENT_TIMEOUT_MSEC));
brcmf_cfg80211_disarm_vif_event(cfg);
if (err != ZX_OK) {
BRCMF_ERR("No BRCMF_E_IF_CHANGE event received\n");
return ZX_ERR_IO;
}
err = brcmf_fil_cmd_int_set(vif->ifp, BRCMF_C_SET_SCB_TIMEOUT, BRCMF_SCB_TIMEOUT_VALUE, nullptr);
return err;
}
zx_status_t brcmf_p2p_start_device(struct wiphy* wiphy, struct wireless_dev* wdev) {
struct brcmf_cfg80211_info* cfg = wiphy_to_cfg(wiphy);
struct brcmf_p2p_info* p2p = &cfg->p2p;
struct brcmf_cfg80211_vif* vif;
zx_status_t err;
vif = containerof(wdev, struct brcmf_cfg80211_vif, wdev);
mtx_lock(&cfg->usr_sync);
err = brcmf_p2p_enable_discovery(p2p);
if (err == ZX_OK) {
brcmf_set_bit_in_array(BRCMF_VIF_STATUS_READY, &vif->sme_state);
}
mtx_unlock(&cfg->usr_sync);
return err;
}
void brcmf_p2p_stop_device(struct wiphy* wiphy, struct wireless_dev* wdev) {
struct brcmf_cfg80211_info* cfg = wiphy_to_cfg(wiphy);
struct brcmf_p2p_info* p2p = &cfg->p2p;
struct brcmf_cfg80211_vif* vif;
vif = containerof(wdev, struct brcmf_cfg80211_vif, wdev);
/* This call can be result of the unregister_wdev call. In that case
* we dont want to do anything anymore. Just return. The config vif
* will have been cleared at this point.
*/
if (p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif == vif) {
mtx_lock(&cfg->usr_sync);
/* Set the discovery state to SCAN */
(void)brcmf_p2p_set_discover_state(vif->ifp, WL_P2P_DISC_ST_SCAN, 0, 0);
brcmf_abort_scanning(cfg);
brcmf_clear_bit_in_array(BRCMF_VIF_STATUS_READY, &vif->sme_state);
mtx_unlock(&cfg->usr_sync);
}
}