diff --git a/core/hdd/src/wlan_hdd_cfg80211.c b/core/hdd/src/wlan_hdd_cfg80211.c index 741501ff4f..e27a1a3b80 100644 --- a/core/hdd/src/wlan_hdd_cfg80211.c +++ b/core/hdd/src/wlan_hdd_cfg80211.c @@ -1076,6 +1076,11 @@ static const struct nl80211_vendor_cmd_info wlan_hdd_cfg80211_vendor_events[] = .subcmd = QCA_NL80211_VENDOR_SUBCMD_NDP }, #endif /* WLAN_FEATURE_NAN_DATAPATH */ + + [QCA_NL80211_VENDOR_SUBCMD_P2P_LO_EVENT_INDEX] = { + .vendor_id = QCA_NL80211_VENDOR_ID, + .subcmd = QCA_NL80211_VENDOR_SUBCMD_P2P_LISTEN_OFFLOAD_STOP + }, }; /** @@ -2173,6 +2178,11 @@ __wlan_hdd_cfg80211_get_features(struct wiphy *wiphy, if (wma_is_scan_simultaneous_capable()) wlan_hdd_cfg80211_set_feature(feature_flags, QCA_WLAN_VENDOR_FEATURE_OFFCHANNEL_SIMULTANEOUS); + + if (wma_is_p2p_lo_capable()) + wlan_hdd_cfg80211_set_feature(feature_flags, + QCA_WLAN_VENDOR_FEATURE_P2P_LISTEN_OFFLOAD); + skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, sizeof(feature_flags) + NLMSG_HDRLEN); @@ -4982,6 +4992,247 @@ static int wlan_hdd_cfg80211_txpower_scale_decr_db(struct wiphy *wiphy, return ret; } + +/** + * __wlan_hdd_cfg80211_p2p_lo_start () - start P2P Listen Offload + * @wiphy: Pointer to wireless phy + * @wdev: Pointer to wireless device + * @data: Pointer to data + * @data_len: Data length + * + * This function is to process the p2p listen offload start vendor + * command. It parses the input parameters and invoke WMA API to + * send the command to firmware. + * + * Return: 0 on success, negative errno on failure + */ +static int __wlan_hdd_cfg80211_p2p_lo_start(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + int ret; + hdd_context_t *hdd_ctx = wiphy_priv(wiphy); + struct net_device *dev = wdev->netdev; + hdd_adapter_t *adapter; + struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_MAX + 1]; + struct sir_p2p_lo_start params; + QDF_STATUS status; + + ENTER_DEV(dev); + + ret = wlan_hdd_validate_context(hdd_ctx); + if (ret) + return ret; + + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { + hdd_err("Command not allowed in FTM mode"); + return -EPERM; + } + + adapter = WLAN_HDD_GET_PRIV_PTR(dev); + if ((adapter->device_mode != QDF_P2P_DEVICE_MODE) && + (adapter->device_mode != QDF_P2P_CLIENT_MODE) && + (adapter->device_mode != QDF_P2P_GO_MODE)) { + hdd_err("Invalid device mode %d", adapter->device_mode); + return -EINVAL; + } + + if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_MAX, + data, data_len, NULL)) { + hdd_err("Invalid ATTR"); + return -EINVAL; + } + + memset(¶ms, 0, sizeof(params)); + + if (!tb[QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_CTRL_FLAG]) + params.ctl_flags = 1; /* set to default value */ + else + params.ctl_flags = nla_get_u32(tb + [QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_CTRL_FLAG]); + + if (!tb[QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_CHANNEL] || + !tb[QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_PERIOD] || + !tb[QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_INTERVAL] || + !tb[QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_COUNT] || + !tb[QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_DEVICE_TYPES] || + !tb[QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_VENDOR_IE]) { + hdd_err("Attribute parsing failed"); + return -EINVAL; + } + + params.vdev_id = adapter->sessionId; + params.freq = nla_get_u32(tb + [QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_CHANNEL]); + if ((params.freq != 2412) && (params.freq != 2437) && + (params.freq != 2462)) { + hdd_err("Invalid listening channel: %d", params.freq); + return -EINVAL; + } + + params.period = nla_get_u32(tb + [QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_PERIOD]); + if (!((params.period > 0) && (params.period < UINT_MAX))) { + hdd_err("Invalid period: %d", params.period); + return -EINVAL; + } + + params.interval = nla_get_u32(tb + [QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_INTERVAL]); + if (!((params.interval > 0) && (params.interval < UINT_MAX))) { + hdd_err("Invalid interval: %d", params.interval); + return -EINVAL; + } + + params.count = nla_get_u32(tb + [QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_COUNT]); + if (!((params.count > 0) && (params.count < UINT_MAX))) { + hdd_err("Invalid count: %d", params.count); + return -EINVAL; + } + + params.device_types = nla_data(tb + [QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_DEVICE_TYPES]); + if (params.device_types == NULL) { + hdd_err("Invalid device types"); + return -EINVAL; + } + + params.dev_types_len = nla_len(tb + [QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_DEVICE_TYPES]); + if (params.dev_types_len < 8) { + hdd_err("Invalid device type length: %d", params.dev_types_len); + return -EINVAL; + } + + params.probe_resp_tmplt = nla_data(tb + [QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_VENDOR_IE]); + if (params.probe_resp_tmplt == NULL) { + hdd_err("Invalid probe response template"); + return -EINVAL; + } + + params.probe_resp_len = nla_len(tb + [QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_VENDOR_IE]); + if (params.probe_resp_len == 0) { + hdd_err("Invalid probe resp template length: %d", + params.probe_resp_len); + return -EINVAL; + } + + hdd_debug("P2P LO params: freq=%d, period=%d, interval=%d, count=%d", + params.freq, params.period, params.interval, params.count); + + status = wma_p2p_lo_start(¶ms); + + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("P2P LO start failed"); + return -EINVAL; + } + + return 0; +} + + +/** + * wlan_hdd_cfg80211_p2p_lo_start () - start P2P Listen Offload + * @wiphy: Pointer to wireless phy + * @wdev: Pointer to wireless device + * @data: Pointer to data + * @data_len: Data length + * + * This function inovkes internal __wlan_hdd_cfg80211_p2p_lo_start() + * to process p2p listen offload start vendor command. + * + * Return: 0 on success, negative errno on failure + */ +static int wlan_hdd_cfg80211_p2p_lo_start(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + int ret = 0; + + cds_ssr_protect(__func__); + ret = __wlan_hdd_cfg80211_p2p_lo_start(wiphy, wdev, + data, data_len); + cds_ssr_unprotect(__func__); + + return ret; +} + +/** + * __wlan_hdd_cfg80211_p2p_lo_stop () - stop P2P Listen Offload + * @wiphy: Pointer to wireless phy + * @wdev: Pointer to wireless device + * @data: Pointer to data + * @data_len: Data length + * + * This function is to process the p2p listen offload stop vendor + * command. It invokes WMA API to send command to firmware. + * + * Return: 0 on success, negative errno on failure + */ +static int __wlan_hdd_cfg80211_p2p_lo_stop(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + QDF_STATUS status; + hdd_adapter_t *adapter; + struct net_device *dev = wdev->netdev; + + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { + hdd_err("Command not allowed in FTM mode"); + return -EPERM; + } + + adapter = WLAN_HDD_GET_PRIV_PTR(dev); + if ((adapter->device_mode != QDF_P2P_DEVICE_MODE) && + (adapter->device_mode != QDF_P2P_CLIENT_MODE) && + (adapter->device_mode != QDF_P2P_GO_MODE)) { + hdd_err("Invalid device mode"); + return -EINVAL; + } + + status = wma_p2p_lo_stop(adapter->sessionId); + + if (!QDF_IS_STATUS_SUCCESS(status)) { + hdd_err("P2P LO stop failed"); + return -EINVAL; + } + + return 0; +} + +/** + * wlan_hdd_cfg80211_p2p_lo_stop () - stop P2P Listen Offload + * @wiphy: Pointer to wireless phy + * @wdev: Pointer to wireless device + * @data: Pointer to data + * @data_len: Data length + * + * This function inovkes internal __wlan_hdd_cfg80211_p2p_lo_stop() + * to process p2p listen offload stop vendor command. + * + * Return: 0 on success, negative errno on failure + */ +static int wlan_hdd_cfg80211_p2p_lo_stop(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + int ret = 0; + + cds_ssr_protect(__func__); + ret = __wlan_hdd_cfg80211_p2p_lo_stop(wiphy, wdev, + data, data_len); + cds_ssr_unprotect(__func__); + + return ret; +} + /* * define short names for the global vendor params * used by __wlan_hdd_cfg80211_bpf_offload() @@ -5967,6 +6218,23 @@ const struct wiphy_vendor_command hdd_wiphy_vendor_commands[] = { WIPHY_VENDOR_CMD_NEED_RUNNING, .doit = wlan_hdd_cfg80211_sap_configuration_set }, + { + .info.subcmd = + QCA_NL80211_VENDOR_SUBCMD_P2P_LISTEN_OFFLOAD_START, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_NETDEV | + WIPHY_VENDOR_CMD_NEED_RUNNING, + .doit = wlan_hdd_cfg80211_p2p_lo_start + }, + { + .info.vendor_id = QCA_NL80211_VENDOR_ID, + .info.subcmd = + QCA_NL80211_VENDOR_SUBCMD_P2P_LISTEN_OFFLOAD_STOP, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_NETDEV | + WIPHY_VENDOR_CMD_NEED_RUNNING, + .doit = wlan_hdd_cfg80211_p2p_lo_stop + }, #ifdef WLAN_FEATURE_NAN_DATAPATH { diff --git a/core/hdd/src/wlan_hdd_cfg80211.h b/core/hdd/src/wlan_hdd_cfg80211.h index cde0491b19..5c159b06ab 100644 --- a/core/hdd/src/wlan_hdd_cfg80211.h +++ b/core/hdd/src/wlan_hdd_cfg80211.h @@ -261,6 +261,14 @@ typedef enum { * @QCA_NL80211_VENDOR_SUBCMD_SET_SAP_CONFIG: SAP configuration * @QCA_NL80211_VENDOR_SUBCMD_TSF: TSF operations command * @QCA_NL80211_VENDOR_SUBCMD_WISA: WISA mode configuration + * @QCA_NL80211_VENDOR_SUBCMD_P2P_LISTEN_OFFLOAD_START: Command used to + * start the P2P Listen Offload function in device and pass the listen + * channel, period, interval, count, number of device types, device + * types and vendor information elements to device driver and firmware. + * @QCA_NL80211_VENDOR_SUBCMD_P2P_LISTEN_OFFLOAD_STOP: Command/event used to + * indicate stop request/response of the P2P Listen Offload function in + * device. As an event, it indicates either the feature stopped after it + * was already running or feature has actually failed to start. */ enum qca_nl80211_vendor_subcmds { @@ -384,6 +392,8 @@ enum qca_nl80211_vendor_subcmds { QCA_NL80211_VENDOR_SUBCMD_SET_SAP_CONFIG = 118, QCA_NL80211_VENDOR_SUBCMD_TSF = 119, QCA_NL80211_VENDOR_SUBCMD_WISA = 120, + QCA_NL80211_VENDOR_SUBCMD_P2P_LISTEN_OFFLOAD_START = 121, + QCA_NL80211_VENDOR_SUBCMD_P2P_LISTEN_OFFLOAD_STOP = 122, }; /** @@ -445,6 +455,8 @@ enum qca_nl80211_vendor_subcmds { * @QCA_NL80211_VENDOR_SUBCMD_GW_PARAM_CONFIG_INDEX: * update gateway parameters index * @QCA_NL80211_VENDOR_SUBCMD_TSF_INDEX: TSF response events index + * @QCA_NL80211_VENDOR_SUBCMD_P2P_LO_EVENT_INDEX: + * P2P listen offload index */ enum qca_nl80211_vendor_subcmds_index { @@ -520,6 +532,7 @@ enum qca_nl80211_vendor_subcmds_index { #ifdef WLAN_FEATURE_NAN_DATAPATH QCA_NL80211_VENDOR_SUBCMD_NDP_INDEX, #endif /* WLAN_FEATURE_NAN_DATAPATH */ + QCA_NL80211_VENDOR_SUBCMD_P2P_LO_EVENT_INDEX, }; /** @@ -1900,11 +1913,19 @@ enum qca_wlan_vendor_attr_link_properties { * after roaming, rather than having the supplicant do it. * @QCA_WLAN_VENDOR_FEATURE_OFFCHANNEL_SIMULTANEOUS: Device supports * simultaneous off-channel operations. + * @QQCA_WLAN_VENDOR_FEATURE_P2P_LISTEN_OFFLOAD: Device supports P2P + * Listen offload; a mechanism where the station's firmware + * takes care of responding to incoming Probe Request frames received + * from other P2P devices whilst in Listen state, rather than having the + * user space wpa_supplicant do it. Information from received P2P + * Requests are forwarded from firmware to host whenever the APPS + * processor exits power collapse state. */ enum qca_wlan_vendor_features { QCA_WLAN_VENDOR_FEATURE_KEY_MGMT_OFFLOAD = 0, QCA_WLAN_VENDOR_FEATURE_SUPPORT_HW_MODE_ANY = 1, QCA_WLAN_VENDOR_FEATURE_OFFCHANNEL_SIMULTANEOUS = 2, + QCA_WLAN_VENDOR_FEATURE_P2P_LISTEN_OFFLOAD = 3, /* Additional features need to be added above this */ NUM_QCA_WLAN_VENDOR_FEATURES }; @@ -2385,6 +2406,47 @@ enum qca_wlan_vendor_attr_sap_config { QCA_WLAN_VENDOR_ATTR_SAP_CONFIG_AFTER_LAST - 1, }; +/** + * enum qca_wlan_vendor_attr_p2p_listen_offload - vendor sub commands index + * @QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_INVALID: invalid value + * @QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_CHANNEL: + * A 32-bit unsigned value; the P2P listen frequency (MHz); must be one + * of the social channels. + * @QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_PERIOD: listen offload period + * A 32-bit unsigned value; the P2P listen offload period (ms). + * @QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_INTERVAL: + * A 32-bit unsigned value; the P2P listen interval duration (ms). + * @QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_COUNT: + * A 32-bit unsigned value; number of interval times the Firmware needs + * to run the offloaded P2P listen operation before it stops. + * @QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_DEVICE_TYPES: device types + * An array of unsigned 8-bit characters; vendor information elements. + * @QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_VENDOR_IE: vendor IEs + * @QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_CTRL_FLAG: control flag for FW + * A 32-bit unsigned value; a control flag to indicate whether listen + * results need to be flushed to wpa_supplicant. + * @QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_STOP_REASON: offload stop reason + * A 8-bit unsigned value; reason code for P2P listen offload stop + * event. + * @QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_AFTER_LAST: last value + * @QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_MAX: max value + */ +enum qca_wlan_vendor_attr_p2p_listen_offload { + QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_CHANNEL, + QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_PERIOD, + QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_INTERVAL, + QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_COUNT, + QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_DEVICE_TYPES, + QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_VENDOR_IE, + QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_CTRL_FLAG, + QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_STOP_REASON, + /* keep last */ + QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_MAX = + QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_AFTER_LAST - 1 +}; + struct cfg80211_bss *wlan_hdd_cfg80211_update_bss_db(hdd_adapter_t *pAdapter, tCsrRoamInfo *pRoamInfo); diff --git a/core/hdd/src/wlan_hdd_main.c b/core/hdd/src/wlan_hdd_main.c index d553efd22b..ee47eb13f6 100644 --- a/core/hdd/src/wlan_hdd_main.c +++ b/core/hdd/src/wlan_hdd_main.c @@ -6442,6 +6442,52 @@ out: return ret; } +/** + * wlan_hdd_p2p_lo_event_callback - P2P listen offload stop event handler + * @context_ptr - hdd context pointer + * @event_ptr - event structure pointer + * + * This is the p2p listen offload stop event handler, it sends vendor + * event back to supplicant to notify the stop reason. + * + * Return: None + */ +static void wlan_hdd_p2p_lo_event_callback(void *context_ptr, + void *event_ptr) +{ + hdd_context_t *hdd_ctx = (hdd_context_t *)context_ptr; + struct sir_p2p_lo_event *evt = event_ptr; + struct sk_buff *vendor_event; + + ENTER(); + + if (hdd_ctx == NULL) { + hdd_err("Invalid HDD context pointer"); + return; + } + + vendor_event = + cfg80211_vendor_event_alloc(hdd_ctx->wiphy, + NULL, sizeof(uint32_t) + NLMSG_HDRLEN, + QCA_NL80211_VENDOR_SUBCMD_P2P_LO_EVENT_INDEX, + GFP_KERNEL); + + if (!vendor_event) { + hdd_err("cfg80211_vendor_event_alloc failed"); + return; + } + + if (nla_put_u32(vendor_event, + QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_STOP_REASON, + evt->reason_code)) { + hdd_err("nla put failed"); + kfree_skb(vendor_event); + return; + } + + cfg80211_vendor_event(vendor_event, GFP_KERNEL); +} + /** * hdd_adaptive_dwelltime_init() - initialization for adaptive dwell time config * @hdd_ctx: HDD context @@ -6713,6 +6759,11 @@ int hdd_wlan_startup(struct device *dev, void *hif_sc) } } + /* register P2P Listen Offload event callback */ + if (wma_is_p2p_lo_capable()) + sme_register_p2p_lo_event(hdd_ctx->hHal, hdd_ctx, + wlan_hdd_p2p_lo_event_callback); + ret = hdd_register_notifiers(hdd_ctx); if (ret) goto err_debugfs_exit; diff --git a/core/mac/inc/sir_api.h b/core/mac/inc/sir_api.h index e2172422c8..6cb946b455 100644 --- a/core/mac/inc/sir_api.h +++ b/core/mac/inc/sir_api.h @@ -6227,4 +6227,39 @@ struct sme_ndp_peer_ind { #endif /* WLAN_FEATURE_NAN_DATAPATH */ +/** + * struct sir_p2p_lo_start - p2p listen offload start + * @vdev_id: vdev identifier + * @ctl_flags: control flag + * @freq: p2p listen frequency + * @period: listen offload period + * @interval: listen offload interval + * @count: number listen offload intervals + * @device_types: device types + * @dev_types_len: device types length + * @probe_resp_tmplt: probe response template + * @probe_resp_len: probe response template length + */ +struct sir_p2p_lo_start { + uint32_t vdev_id; + uint32_t ctl_flags; + uint32_t freq; + uint32_t period; + uint32_t interval; + uint32_t count; + uint8_t *device_types; + uint32_t dev_types_len; + uint8_t *probe_resp_tmplt; + uint32_t probe_resp_len; +}; + +/** + * struct sir_p2p_lo_event - P2P listen offload stop event + * @vdev_id: vdev identifier + * @reason_code: P2P listen offload stop reason + */ +struct sir_p2p_lo_event { + uint32_t vdev_id; + uint32_t reason_code; +}; #endif /* __SIR_API_H */ diff --git a/core/sme/inc/sme_api.h b/core/sme/inc/sme_api.h index 8656fe540d..804dc1e8ff 100644 --- a/core/sme/inc/sme_api.h +++ b/core/sme/inc/sme_api.h @@ -1129,4 +1129,6 @@ void sme_set_pdev_ht_vht_ies(tHalHandle hHal, bool enable2x2); void sme_update_vdev_type_nss(tHalHandle hal, uint8_t max_supp_nss, uint32_t vdev_type_nss, eCsrBand band); +void sme_register_p2p_lo_event(tHalHandle hHal, void *context, + p2p_lo_callback callback); #endif /* #if !defined( __SME_API_H ) */ diff --git a/core/sme/inc/sme_internal.h b/core/sme/inc/sme_internal.h index 6414ae8a24..d6d0e50f57 100644 --- a/core/sme/inc/sme_internal.h +++ b/core/sme/inc/sme_internal.h @@ -149,6 +149,7 @@ typedef void (*preferred_network_found_ind_cb)(void *callback_context, typedef void (*ocb_callback)(void *context, void *response); typedef void (*sme_set_thermal_level_callback)(void *context, u_int8_t level); +typedef void (*p2p_lo_callback)(void *context, void *event); typedef struct tagSmeStruct { eSmeState state; @@ -236,6 +237,8 @@ typedef struct tagSmeStruct { void *saved_scan_cmd; void (*pbpf_get_offload_cb)(void *context, struct sir_bpf_get_offload *); + p2p_lo_callback p2p_lo_event_callback; + void *p2p_lo_event_context; } tSmeStruct, *tpSmeStruct; #endif /* #if !defined( __SMEINTERNAL_H ) */ diff --git a/core/sme/src/common/sme_api.c b/core/sme/src/common/sme_api.c index 44d99f1c06..6c7766e5aa 100644 --- a/core/sme/src/common/sme_api.c +++ b/core/sme/src/common/sme_api.c @@ -15739,3 +15739,26 @@ void sme_update_vdev_type_nss(tHalHandle hal, uint8_t max_supp_nss, vdev_nss->p2p_go, vdev_nss->p2p_dev, vdev_nss->ibss, vdev_nss->tdls, vdev_nss->ocb); } + +/** + * sme_register_p2p_lo_event() - Register for the p2p lo event + * @hHal: reference to the HAL + * @context: the context of the call + * @callback: the callback to hdd + * + * This function registers the callback function for P2P listen + * offload stop event. + * + * Return: none + */ +void sme_register_p2p_lo_event(tHalHandle hHal, void *context, + p2p_lo_callback callback) +{ + tpAniSirGlobal pMac = PMAC_STRUCT(hHal); + QDF_STATUS status = QDF_STATUS_E_FAILURE; + + status = sme_acquire_global_lock(&pMac->sme); + pMac->sme.p2p_lo_event_callback = callback; + pMac->sme.p2p_lo_event_context = context; + sme_release_global_lock(&pMac->sme); +} diff --git a/core/wma/inc/wma.h b/core/wma/inc/wma.h index fb9ec3ac0c..17854d7e2a 100644 --- a/core/wma/inc/wma.h +++ b/core/wma/inc/wma.h @@ -938,6 +938,7 @@ struct wma_txrx_node { int32_t roam_synch_delay; uint8_t nss_2g; uint8_t nss_5g; + bool p2p_lo_in_progress; }; #if defined(QCA_WIFI_FTM) diff --git a/core/wma/inc/wma_api.h b/core/wma/inc/wma_api.h index a2f73b567a..9e3e6ab1f7 100644 --- a/core/wma/inc/wma_api.h +++ b/core/wma/inc/wma_api.h @@ -264,4 +264,7 @@ static inline QDF_STATUS wma_register_ndp_cb(QDF_STATUS (*pe_ndp_event_handler) } #endif +bool wma_is_p2p_lo_capable(void); +QDF_STATUS wma_p2p_lo_start(struct sir_p2p_lo_start *params); +QDF_STATUS wma_p2p_lo_stop(u_int32_t vdev_id); #endif diff --git a/core/wma/inc/wma_internal.h b/core/wma/inc/wma_internal.h index 6845bfda82..0cf62491f9 100644 --- a/core/wma/inc/wma_internal.h +++ b/core/wma/inc/wma_internal.h @@ -1214,4 +1214,6 @@ int wma_peer_delete_handler(void *handle, uint8_t *cmd_param_info, uint32_t len); void wma_remove_req(tp_wma_handle wma, uint8_t vdev_id, uint8_t type); +int wma_p2p_lo_event_handler(void *handle, uint8_t *event_buf, + uint32_t len); #endif diff --git a/core/wma/src/wma_features.c b/core/wma/src/wma_features.c index 3ebbe3d580..fdf3349504 100644 --- a/core/wma/src/wma_features.c +++ b/core/wma/src/wma_features.c @@ -3936,6 +3936,23 @@ bool wma_is_extscan_in_progress(tp_wma_handle wma, int vdev_id) } #endif +/** + * wma_is_p2plo_in_progress(): check if P2P listen offload is in progress + * @wma: wma handle + * @vdev_id: vdev_id + * + * This function is to check if p2p listen offload is in progress, + * true: p2p listen offload in progress + * false: otherwise + * + * Return: TRUE/FALSE + */ +static inline +bool wma_is_p2plo_in_progress(tp_wma_handle wma, int vdev_id) +{ + return wma->interfaces[vdev_id].p2p_lo_in_progress; +} + /** * wma_is_wow_applicable(): should enable wow * @wma: wma handle @@ -3945,6 +3962,7 @@ bool wma_is_extscan_in_progress(tp_wma_handle wma, int vdev_id) * 2) Is any one of vdev in connected state (in STA mode) ? * 3) Is PNO in progress in any one of vdev ? * 4) Is Extscan in progress in any one of vdev ? + * 5) Is P2P listen offload in any one of vdev? * If none of above conditions is true then return false * * Return: true if wma needs to configure wow false otherwise. @@ -3967,6 +3985,9 @@ bool wma_is_wow_applicable(tp_wma_handle wma) } else if (wma_is_extscan_in_progress(wma, vdev_id)) { WMA_LOGD("EXT is in progress, enabling wow"); return true; + } else if (wma_is_p2plo_in_progress(wma, vdev_id)) { + WMA_LOGD("P2P LO is in progress, enabling wow"); + return true; } } @@ -7316,3 +7337,182 @@ QDF_STATUS wma_set_bpf_instructions(tp_wma_handle wma, } return QDF_STATUS_SUCCESS; } + +/** + * wma_p2p_lo_start() - P2P listen offload start + * @params: p2p listen offload parameters + * + * This function sends WMI command to start P2P listen offload. + * + * Return: QDF_STATUS enumeration + */ +QDF_STATUS wma_p2p_lo_start(struct sir_p2p_lo_start *params) +{ + wmi_buf_t buf; + wmi_p2p_lo_start_cmd_fixed_param *cmd; + int32_t len = sizeof(*cmd); + tp_wma_handle wma = cds_get_context(QDF_MODULE_ID_WMA); + uint8_t *buf_ptr; + int ret; + + if (NULL == wma) { + WMA_LOGE("%s: wma context is NULL", __func__); + return QDF_STATUS_E_INVAL; + } + + len += 2 * WMI_TLV_HDR_SIZE + + qdf_roundup(params->dev_types_len, sizeof(A_UINT32)) + + qdf_roundup(params->probe_resp_len, sizeof(A_UINT32)); + + buf = wmi_buf_alloc(wma->wmi_handle, len); + if (!buf) { + WMA_LOGP("%s: failed to allocate memory for p2p lo start", + __func__); + return QDF_STATUS_E_NOMEM; + } + + cmd = (wmi_p2p_lo_start_cmd_fixed_param *)wmi_buf_data(buf); + buf_ptr = (uint8_t *) wmi_buf_data(buf); + + WMITLV_SET_HDR(&cmd->tlv_header, + WMITLV_TAG_STRUC_wmi_p2p_lo_start_cmd_fixed_param, + WMITLV_GET_STRUCT_TLVLEN( + wmi_p2p_lo_start_cmd_fixed_param)); + + cmd->vdev_id = params->vdev_id; + cmd->ctl_flags = params->ctl_flags; + cmd->channel = params->freq; + cmd->period = params->period; + cmd->interval = params->interval; + cmd->count = params->count; + + buf_ptr += sizeof(wmi_p2p_lo_start_cmd_fixed_param); + WMITLV_SET_HDR(buf_ptr, WMITLV_TAG_ARRAY_BYTE, + qdf_roundup(params->dev_types_len, sizeof(A_UINT32))); + buf_ptr += WMI_TLV_HDR_SIZE; + qdf_mem_copy(buf_ptr, params->device_types, params->dev_types_len); + + buf_ptr += qdf_roundup(params->dev_types_len, sizeof(A_UINT32)); + WMITLV_SET_HDR(buf_ptr, WMITLV_TAG_ARRAY_BYTE, + qdf_roundup(params->probe_resp_len, sizeof(A_UINT32))); + buf_ptr += WMI_TLV_HDR_SIZE; + qdf_mem_copy(buf_ptr, params->probe_resp_tmplt, params->probe_resp_len); + + WMA_LOGI("%s: Sending WMI_P2P_LO_START command, channel=%d, period=%d, interval=%d, count=%d", + __func__, cmd->channel, cmd->period, + cmd->interval, cmd->count); + + ret = wmi_unified_cmd_send(wma->wmi_handle, + buf, len, + WMI_P2P_LISTEN_OFFLOAD_START_CMDID); + if (ret) { + WMA_LOGE("Failed to send p2p lo start: %d", ret); + wmi_buf_free(buf); + } + + WMA_LOGI("%s: Successfully sent WMI_P2P_LO_START", __func__); + wma->interfaces[params->vdev_id].p2p_lo_in_progress = true; + + return ret; +} + +/** + * wma_p2p_lo_stop() - P2P listen offload stop + * @vdev_id: vdev identifier + * + * This function sends WMI command to stop P2P listen offload. + * + * Return: QDF_STATUS enumeration + */ +QDF_STATUS wma_p2p_lo_stop(u_int32_t vdev_id) +{ + wmi_buf_t buf; + wmi_p2p_lo_stop_cmd_fixed_param *cmd; + int32_t len; + tp_wma_handle wma = cds_get_context(QDF_MODULE_ID_WMA); + int ret; + + if (NULL == wma) { + WMA_LOGE("%s: wma context is NULL", __func__); + return QDF_STATUS_E_INVAL; + } + + len = sizeof(*cmd); + buf = wmi_buf_alloc(wma->wmi_handle, len); + if (!buf) { + WMA_LOGP("%s: failed to allocate memory for p2p lo stop", + __func__); + return QDF_STATUS_E_NOMEM; + } + cmd = (wmi_p2p_lo_stop_cmd_fixed_param *)wmi_buf_data(buf); + + WMITLV_SET_HDR(&cmd->tlv_header, + WMITLV_TAG_STRUC_wmi_p2p_lo_stop_cmd_fixed_param, + WMITLV_GET_STRUCT_TLVLEN( + wmi_p2p_lo_stop_cmd_fixed_param)); + + cmd->vdev_id = vdev_id; + + WMA_LOGI("%s: Sending WMI_P2P_LO_STOP command", __func__); + + ret = wmi_unified_cmd_send(wma->wmi_handle, + buf, len, + WMI_P2P_LISTEN_OFFLOAD_STOP_CMDID); + if (ret) { + WMA_LOGE("Failed to send p2p lo stop: %d", ret); + wmi_buf_free(buf); + } + + WMA_LOGI("%s: Successfully sent WMI_P2P_LO_STOP", __func__); + wma->interfaces[vdev_id].p2p_lo_in_progress = false; + + return ret; +} + +/** + * wma_p2p_lo_event_handler() - p2p lo event + * @handle: the WMA handle + * @event_buf: buffer with the event parameters + * @len: length of the buffer + * + * This function receives P2P listen offload stop event from FW and + * pass the event information to upper layer. + * + * Return: 0 on success + */ +int wma_p2p_lo_event_handler(void *handle, uint8_t *event_buf, + uint32_t len) +{ + tp_wma_handle wma = cds_get_context(QDF_MODULE_ID_WMA); + struct sir_p2p_lo_event *event; + WMI_P2P_LISTEN_OFFLOAD_STOPPED_EVENTID_param_tlvs *param_tlvs; + wmi_p2p_lo_stopped_event_fixed_param *fix_param; + tpAniSirGlobal p_mac = cds_get_context(QDF_MODULE_ID_PE); + + if (!p_mac) { + WMA_LOGE("%s: Invalid p_mac", __func__); + return -EINVAL; + } + + if (!p_mac->sme.p2p_lo_event_callback) { + WMA_LOGE("%s: Callback not registered", __func__); + return -EINVAL; + } + + param_tlvs = (WMI_P2P_LISTEN_OFFLOAD_STOPPED_EVENTID_param_tlvs *) + event_buf; + fix_param = param_tlvs->fixed_param; + event = qdf_mem_malloc(sizeof(*event)); + if (event == NULL) { + WMA_LOGE("Event allocation failed"); + return -ENOMEM; + } + event->vdev_id = fix_param->vdev_id; + event->reason_code = fix_param->reason; + + p_mac->sme.p2p_lo_event_callback(p_mac->hHdd, event); + + wma->interfaces[event->vdev_id].p2p_lo_in_progress = false; + + return 0; +} diff --git a/core/wma/src/wma_main.c b/core/wma/src/wma_main.c index bf0b5229cf..f38b2e3739 100644 --- a/core/wma/src/wma_main.c +++ b/core/wma/src/wma_main.c @@ -2858,6 +2858,17 @@ QDF_STATUS wma_start(void *cds_ctx) goto end; } + /* Initialize the P2P Listen Offload event handler */ + status = wmi_unified_register_event_handler(wma_handle->wmi_handle, + WMI_P2P_LISTEN_OFFLOAD_STOPPED_EVENTID, + wma_p2p_lo_event_handler, + WMA_RX_SERIALIZER_CTX); + if (!QDF_IS_STATUS_SUCCESS(status)) { + WMA_LOGE("Failed to register p2p lo event cb"); + qdf_status = QDF_STATUS_E_FAILURE; + goto end; + } + end: WMA_LOGD("%s: Exit", __func__); return qdf_status; diff --git a/core/wma/src/wma_utils.c b/core/wma/src/wma_utils.c index 0e607ecb06..75b678457b 100644 --- a/core/wma/src/wma_utils.c +++ b/core/wma/src/wma_utils.c @@ -3424,3 +3424,29 @@ wma_config_debug_module_cmd(wmi_unified_t wmi_handle, A_UINT32 param, return wmi_unified_dbglog_cmd_send(wmi_handle, &dbg_param); } + +/** + * wma_is_p2p_lo_capable() - if driver is capable of p2p listen offload + * + * This function checks if driver is capable of p2p listen offload + * true: capable of p2p offload + * false: not capable + * + * Return: true - capable, false - not capable + */ +bool wma_is_p2p_lo_capable(void) +{ + tp_wma_handle wma; + + wma = cds_get_context(QDF_MODULE_ID_WMA); + if (!wma) { + WMA_LOGE("%s: Invalid WMA handle", __func__); + return false; + } + + if (WMI_SERVICE_IS_ENABLED(wma->wmi_service_bitmap, + WMI_SERVICE_P2P_LISTEN_OFFLOAD_SUPPORT)) + return true; + + return false; +}