diff --git a/core/hdd/inc/wlan_hdd_cfg.h b/core/hdd/inc/wlan_hdd_cfg.h index fd581b48f6..39f0f2d014 100644 --- a/core/hdd/inc/wlan_hdd_cfg.h +++ b/core/hdd/inc/wlan_hdd_cfg.h @@ -3605,6 +3605,7 @@ struct hdd_config { bool ignore_peer_ht_opmode; uint32_t roam_dense_min_aps; bool enable_fatal_event; + bool bpf_enabled; }; #define VAR_OFFSET(_Struct, _Var) (offsetof(_Struct, _Var)) diff --git a/core/hdd/inc/wlan_hdd_main.h b/core/hdd/inc/wlan_hdd_main.h index 31cb452da2..be49ab0c6d 100644 --- a/core/hdd/inc/wlan_hdd_main.h +++ b/core/hdd/inc/wlan_hdd_main.h @@ -150,6 +150,8 @@ #define WLAN_WAIT_TIME_ANTENNA_MODE_REQ 3000 #define WLAN_WAIT_TIME_SET_DUAL_MAC_CFG 1500 +#define WLAN_WAIT_TIME_BPF 1000 + #define MAX_NUMBER_OF_ADAPTERS 4 #define MAX_CFG_STRING_LEN 255 @@ -315,6 +317,7 @@ extern spinlock_t hdd_context_lock; #define LINK_CONTEXT_MAGIC 0x4C494E4B /* LINKSPEED */ #define LINK_STATUS_MAGIC 0x4C4B5354 /* LINKSTATUS(LNST) */ #define TEMP_CONTEXT_MAGIC 0x74656d70 /* TEMP (temperature) */ +#define BPF_CONTEXT_MAGIC 0x4575354 /* BPF */ /* MAX OS Q block time value in msec * Prevent from permanent stall, resume OS Q if timer expired */ @@ -1132,6 +1135,18 @@ struct hdd_offloaded_packets_ctx { }; #endif +/** + * struct hdd_bpf_context - hdd Context for bpf + * @magic: magic number + * @completion: Completion variable for BPF Get Capability + * @capability_response: capabilities response received from fw + */ +struct hdd_bpf_context { + unsigned int magic; + struct completion completion; + struct sir_bpf_get_offload capability_response; +}; + /** Adapter structure definition */ struct hdd_context_s { @@ -1373,6 +1388,7 @@ struct hdd_context_s { struct completion set_antenna_mode_cmpl; /* Current number of TX X RX chains being used */ enum antenna_mode current_antenna_mode; + bool bpf_enabled; }; /*--------------------------------------------------------------------------- diff --git a/core/hdd/src/wlan_hdd_cfg80211.c b/core/hdd/src/wlan_hdd_cfg80211.c index cc8427103c..4019b623ad 100644 --- a/core/hdd/src/wlan_hdd_cfg80211.c +++ b/core/hdd/src/wlan_hdd_cfg80211.c @@ -585,7 +585,7 @@ static struct ieee80211_iface_combination }; static struct cfg80211_ops wlan_hdd_cfg80211_ops; - +struct hdd_bpf_context bpf_context; #ifdef WLAN_NL80211_TESTMODE enum wlan_hdd_tm_attr { @@ -4914,7 +4914,363 @@ static int wlan_hdd_cfg80211_txpower_scale_decr_db(struct wiphy *wiphy, return ret; } +/* + * define short names for the global vendor params + * used by __wlan_hdd_cfg80211_bpf_offload() + */ +#define BPF_INVALID \ + QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_INVALID +#define BPF_SET_RESET \ + QCA_WLAN_VENDOR_ATTR_SET_RESET_PACKET_FILTER +#define BPF_VERSION \ + QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_VERSION +#define BPF_FILTER_ID \ + QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_ID +#define BPF_PACKET_SIZE \ + QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_SIZE +#define BPF_CURRENT_OFFSET \ + QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_CURRENT_OFFSET +#define BPF_PROGRAM \ + QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_PROGRAM +#define BPF_MAX \ + QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_MAX +static const struct nla_policy +wlan_hdd_bpf_offload_policy[BPF_MAX + 1] = { + [BPF_SET_RESET] = {.type = NLA_U32}, + [BPF_VERSION] = {.type = NLA_U32}, + [BPF_FILTER_ID] = {.type = NLA_U32}, + [BPF_PACKET_SIZE] = {.type = NLA_U32}, + [BPF_CURRENT_OFFSET] = {.type = NLA_U32}, + [BPF_PROGRAM] = {.type = NLA_U8}, +}; + +/** + * hdd_get_bpf_offload_cb() - Callback function to BPF Offload + * @hdd_context: hdd_context + * @bpf_get_offload: struct for get offload + * + * This function receives the response/data from the lower layer and + * checks to see if the thread is still waiting then post the results to + * upper layer, if the request has timed out then ignore. + * + * Return: None + */ +void hdd_get_bpf_offload_cb(void *hdd_context, + struct sir_bpf_get_offload *data) +{ + hdd_context_t *hdd_ctx = hdd_context; + struct hdd_bpf_context *context; + + ENTER(); + + if (wlan_hdd_validate_context(hdd_ctx) || !data) { + hddLog(LOGE, FL("HDD context is invalid or data(%p) is null"), + data); + return; + } + + spin_lock(&hdd_context_lock); + + context = &bpf_context; + /* The caller presumably timed out so there is nothing we can do */ + if (context->magic != BPF_CONTEXT_MAGIC) { + spin_unlock(&hdd_context_lock); + return; + } + + /* context is valid so caller is still waiting */ + /* paranoia: invalidate the magic */ + context->magic = 0; + + context->capability_response = *data; + complete(&context->completion); + + spin_unlock(&hdd_context_lock); + + return; +} + +/** + * hdd_post_get_bpf_capabilities_rsp() - Callback function to BPF Offload + * @hdd_context: hdd_context + * @bpf_get_offload: struct for get offload + * + * Return: 0 on success, error number otherwise. + */ +static int hdd_post_get_bpf_capabilities_rsp(hdd_context_t *hdd_ctx, + struct sir_bpf_get_offload *bpf_get_offload) +{ + struct sk_buff *skb; + uint32_t nl_buf_len; + + ENTER(); + + nl_buf_len = NLMSG_HDRLEN; + nl_buf_len += + (sizeof(bpf_get_offload->max_bytes_for_bpf_inst) + NLA_HDRLEN) + + (sizeof(bpf_get_offload->bpf_version) + NLA_HDRLEN); + + skb = cfg80211_vendor_cmd_alloc_reply_skb(hdd_ctx->wiphy, nl_buf_len); + if (!skb) { + hddLog(LOGE, FL("cfg80211_vendor_cmd_alloc_reply_skb failed")); + return -ENOMEM; + } + + hddLog(LOG1, "BPF Version: %u BPF max bytes: %u", + bpf_get_offload->bpf_version, + bpf_get_offload->max_bytes_for_bpf_inst); + + if (nla_put_u32(skb, BPF_PACKET_SIZE, + bpf_get_offload->max_bytes_for_bpf_inst) || + nla_put_u32(skb, BPF_VERSION, bpf_get_offload->bpf_version)) { + hddLog(LOGE, FL("nla put failure")); + goto nla_put_failure; + } + + cfg80211_vendor_cmd_reply(skb); + EXIT(); + return 0; + +nla_put_failure: + kfree_skb(skb); + return -EINVAL; +} + +/** + * hdd_get_bpf_offload - Get BPF offload Capabilities + * @hdd_ctx: Hdd context + * + * Return: 0 on success, errno on failure + */ +static int hdd_get_bpf_offload(hdd_context_t *hdd_ctx) +{ + unsigned long rc; + struct hdd_bpf_context *context; + QDF_STATUS status; + int ret; + + ENTER(); + + spin_lock(&hdd_context_lock); + context = &bpf_context; + context->magic = BPF_CONTEXT_MAGIC; + INIT_COMPLETION(context->completion); + spin_unlock(&hdd_context_lock); + + status = sme_get_bpf_offload_capabilities(hdd_ctx->hHal); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hddLog(LOGE, FL("Unable to retrieve BPF caps")); + return -EINVAL; + } + /* request was sent -- wait for the response */ + rc = wait_for_completion_timeout(&context->completion, + msecs_to_jiffies(WLAN_WAIT_TIME_BPF)); + if (!rc) { + hddLog(LOGE, FL("Target response timed out")); + spin_lock(&hdd_context_lock); + context->magic = 0; + spin_unlock(&hdd_context_lock); + + return -ETIMEDOUT; + } + ret = hdd_post_get_bpf_capabilities_rsp(hdd_ctx, + &bpf_context.capability_response); + if (ret) + hddLog(LOGE, FL("Failed to post get bpf capabilities")); + + EXIT(); + return ret; +} + +/** + * hdd_set_reset_bpf_offload - Post set/reset bpf to SME + * @hdd_ctx: Hdd context + * @tb: Length of @data + * @session_id: Session identifier + * + * Return: 0 on success; errno on failure + */ +static int hdd_set_reset_bpf_offload(hdd_context_t *hdd_ctx, + struct nlattr **tb, + uint8_t session_id) +{ + struct sir_bpf_set_offload *bpf_set_offload; + QDF_STATUS status; + int prog_len; + + ENTER(); + + bpf_set_offload = qdf_mem_malloc(sizeof(*bpf_set_offload)); + if (bpf_set_offload == NULL) { + hddLog(LOGE, FL("qdf_mem_malloc failed for bpf_set_offload")); + return -ENOMEM; + } + qdf_mem_zero(bpf_set_offload, sizeof(*bpf_set_offload)); + + /* Parse and fetch bpf packet size */ + if (!tb[BPF_PACKET_SIZE]) { + hddLog(LOGE, FL("attr bpf packet size failed")); + goto fail; + } + bpf_set_offload->total_length = nla_get_u32(tb[BPF_PACKET_SIZE]); + + if (!bpf_set_offload->total_length) { + hddLog(LOG1, FL("BPF reset packet filter received")); + goto post_sme; + } + + /* Parse and fetch bpf program */ + if (!tb[BPF_PROGRAM]) { + hddLog(LOGE, FL("attr bpf program failed")); + goto fail; + } + + prog_len = nla_len(tb[BPF_PROGRAM]); + bpf_set_offload->program = qdf_mem_malloc(sizeof(uint8_t) * prog_len); + bpf_set_offload->current_length = prog_len; + nla_memcpy(bpf_set_offload->program, tb[BPF_PROGRAM], prog_len); + bpf_set_offload->session_id = session_id; + + /* Parse and fetch filter Id */ + if (!tb[BPF_FILTER_ID]) { + hddLog(LOGE, FL("attr filter id failed")); + goto fail; + } + bpf_set_offload->filter_id = nla_get_u32(tb[BPF_FILTER_ID]); + + /* Parse and fetch current offset */ + if (!tb[BPF_CURRENT_OFFSET]) { + hddLog(LOGE, FL("attr current offset failed")); + goto fail; + } + bpf_set_offload->current_offset = nla_get_u32(tb[BPF_CURRENT_OFFSET]); + +post_sme: + hddLog(LOG1, FL("Posting BPF SET/RESET to SME, session_id: %d Bpf Version: %d filter ID: %d total_length: %d current_length: %d current offset: %d"), + bpf_set_offload->session_id, + bpf_set_offload->version, + bpf_set_offload->filter_id, + bpf_set_offload->total_length, + bpf_set_offload->current_length, + bpf_set_offload->current_offset); + + status = sme_set_bpf_instructions(hdd_ctx->hHal, bpf_set_offload); + if (!QDF_IS_STATUS_SUCCESS(status)) { + hddLog(LOGE, + FL("sme_set_bpf_instructions failed(err=%d)"), status); + goto fail; + } + EXIT(); + if (bpf_set_offload->current_length) + qdf_mem_free(bpf_set_offload->program); + qdf_mem_free(bpf_set_offload); + return 0; + +fail: + if (bpf_set_offload->current_length) + qdf_mem_free(bpf_set_offload->program); + qdf_mem_free(bpf_set_offload); + return -EINVAL; +} + +/** + * wlan_hdd_cfg80211_bpf_offload() - Set/Reset to BPF Offload + * @wiphy: wiphy structure pointer + * @wdev: Wireless device structure pointer + * @data: Pointer to the data received + * @data_len: Length of @data + * + * Return: 0 on success; errno on failure + */ +static int +__wlan_hdd_cfg80211_bpf_offload(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int data_len) +{ + hdd_context_t *hdd_ctx = wiphy_priv(wiphy); + struct net_device *dev = wdev->netdev; + hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev); + struct nlattr *tb[BPF_MAX + 1]; + int ret_val, packet_filter_subcmd; + + ENTER(); + + ret_val = wlan_hdd_validate_context(hdd_ctx); + if (ret_val) + return ret_val; + + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { + hddLog(LOGE, FL("Command not allowed in FTM mode")); + return -EINVAL; + } + + if (!hdd_ctx->bpf_enabled) { + hddLog(LOGE, FL("BPF offload is not supported by firmware")); + return -ENOTSUPP; + } + + if (nla_parse(tb, BPF_MAX, data, data_len, + wlan_hdd_bpf_offload_policy)) { + hddLog(LOGE, FL("Invalid ATTR")); + return -EINVAL; + } + + if (!tb[BPF_SET_RESET]) { + hddLog(LOGE, FL("attr bpf set reset failed")); + return -EINVAL; + } + + packet_filter_subcmd = nla_get_u32(tb[BPF_SET_RESET]); + + if (packet_filter_subcmd == QCA_WLAN_GET_PACKET_FILTER) + return hdd_get_bpf_offload(hdd_ctx); + else + return hdd_set_reset_bpf_offload(hdd_ctx, tb, + pAdapter->sessionId); +} + +/** + * wlan_hdd_cfg80211_bpf_offload() - SSR Wrapper to BPF Offload + * @wiphy: wiphy structure pointer + * @wdev: Wireless device structure pointer + * @data: Pointer to the data received + * @data_len: Length of @data + * + * Return: 0 on success; errno on failure + */ + +static int wlan_hdd_cfg80211_bpf_offload(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int data_len) +{ + int ret; + + cds_ssr_protect(__func__); + ret = __wlan_hdd_cfg80211_bpf_offload(wiphy, wdev, data, data_len); + cds_ssr_unprotect(__func__); + + return ret; +} + +/** + * hdd_init_bpf_completion() - Initialize the completion event for bpf + * + * Return: None + */ +void hdd_init_bpf_completion(void) +{ + init_completion(&bpf_context.completion); +} + +#undef BPF_INVALID +#undef BPF_SET_RESET +#undef BPF_VERSION +#undef BPF_ID +#undef BPF_PACKET_SIZE +#undef BPF_CURRENT_OFFSET +#undef BPF_PROGRAM +#undef BPF_MAX const struct wiphy_vendor_command hdd_wiphy_vendor_commands[] = { { .info.vendor_id = QCA_NL80211_VENDOR_ID, @@ -5369,6 +5725,14 @@ const struct wiphy_vendor_command hdd_wiphy_vendor_commands[] = { WIPHY_VENDOR_CMD_NEED_RUNNING, .doit = wlan_hdd_cfg80211_txpower_scale_decr_db }, + { + .info.vendor_id = QCA_NL80211_VENDOR_ID, + .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_PACKET_FILTER, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_NETDEV | + WIPHY_VENDOR_CMD_NEED_RUNNING, + .doit = wlan_hdd_cfg80211_bpf_offload + }, }; /** diff --git a/core/hdd/src/wlan_hdd_cfg80211.h b/core/hdd/src/wlan_hdd_cfg80211.h index 9ba52bb317..1a185b0e30 100644 --- a/core/hdd/src/wlan_hdd_cfg80211.h +++ b/core/hdd/src/wlan_hdd_cfg80211.h @@ -344,6 +344,7 @@ enum qca_nl80211_vendor_subcmds { QCA_NL80211_VENDOR_SUBCMD_OFFLOADED_PACKETS = 79, QCA_NL80211_VENDOR_SUBCMD_MONITOR_RSSI = 80, + QCA_NL80211_VENDOR_SUBCMD_PACKET_FILTER = 83, /* OCB commands */ QCA_NL80211_VENDOR_SUBCMD_OCB_SET_CONFIG = 92, QCA_NL80211_VENDOR_SUBCMD_OCB_SET_UTC_TIME = 93, @@ -2138,6 +2139,40 @@ enum qca_wlan_vendor_attr_rssi_monitoring { QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_AFTER_LAST - 1, }; +/** + * enum set_reset_packet_filter - set packet filter control commands + * @QCA_WLAN_SET_PACKET_FILTER: Set Packet Filter + * @QCA_WLAN_GET_PACKET_FILTER: Get Packet filter + */ +enum set_reset_packet_filter { + QCA_WLAN_SET_PACKET_FILTER = 1, + QCA_WLAN_GET_PACKET_FILTER = 2, +}; + +/** + * enum qca_wlan_vendor_attr_packet_filter - BPF control commands + * @QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_INVALID: Invalid + * @QCA_WLAN_VENDOR_ATTR_SET_RESET_PACKET_FILTER: Filter ID + * @QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_VERSION: Filter Version + * @QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_SIZE: Total Length + * @QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_CURRENT_OFFSET: Current offset + * @QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_PROGRAM: length of BPF instructions + */ +enum qca_wlan_vendor_attr_packet_filter { + QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_SET_RESET_PACKET_FILTER, + QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_VERSION, + QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_ID, + QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_SIZE, + QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_CURRENT_OFFSET, + QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_PROGRAM, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_MAX = + QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_AFTER_LAST - 1, +}; + /** * enum qca_vendor_attr_get_preferred_freq_list - get preferred channel list * @QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_INVALID: invalid value @@ -2435,4 +2470,7 @@ int wlan_hdd_disable_dfs_chan_scan(hdd_context_t *hdd_ctx, int wlan_hdd_cfg80211_update_band(struct wiphy *wiphy, eCsrBand eBand); + +void hdd_get_bpf_offload_cb(void *hdd_context, struct sir_bpf_get_offload *); +void hdd_init_bpf_completion(void); #endif diff --git a/core/hdd/src/wlan_hdd_main.c b/core/hdd/src/wlan_hdd_main.c index e750b1a46e..c616f48260 100644 --- a/core/hdd/src/wlan_hdd_main.c +++ b/core/hdd/src/wlan_hdd_main.c @@ -1337,6 +1337,7 @@ void hdd_update_tgt_cfg(void *context, void *param) hdd_info("Init current antenna mode: %d", hdd_ctx->current_antenna_mode); + hdd_ctx->bpf_enabled = cfg->bpf_enabled; } /** @@ -5413,6 +5414,8 @@ static int hdd_context_init(hdd_context_t *hdd_ctx) init_completion(&hdd_ctx->mc_sus_event_var); init_completion(&hdd_ctx->ready_to_suspend); + hdd_init_bpf_completion(); + qdf_spinlock_create(&hdd_ctx->connection_status_lock); qdf_spinlock_create(&hdd_ctx->hdd_adapter_lock); @@ -6168,6 +6171,11 @@ int hdd_wlan_startup(struct device *dev, void *hif_sc) sme_set_rssi_threshold_breached_cb(hdd_ctx->hHal, hdd_rssi_threshold_breached); + status = sme_bpf_offload_register_callback(hdd_ctx->hHal, + hdd_get_bpf_offload_cb); + if (QDF_IS_STATUS_SUCCESS(status)) + hdd_err("set bpf offload callback failed"); + hdd_cfg80211_link_layer_stats_init(hdd_ctx); wlan_hdd_tsf_init(hdd_ctx); wlan_hdd_send_all_scan_intf_info(hdd_ctx); diff --git a/core/mac/inc/sir_api.h b/core/mac/inc/sir_api.h index af34c8ef78..0f6854fa15 100644 --- a/core/mac/inc/sir_api.h +++ b/core/mac/inc/sir_api.h @@ -5713,4 +5713,40 @@ struct obss_scanparam { uint16_t obss_activity_threshold; }; +/** + * struct sir_bpf_set_offload - set bpf filter instructions + * @session_id: session identifier + * @version: host bpf version + * @filter_id: Filter ID for BPF filter + * @total_length: The total length of the full instruction + * total_length equal to 0 means reset + * @current_offset: current offset, 0 means start a new setting + * @current_length: Length of current @program + * @program: BPF instructions + */ +struct sir_bpf_set_offload { + uint8_t session_id; + uint32_t version; + uint32_t filter_id; + uint32_t total_length; + uint32_t current_offset; + uint32_t current_length; + uint8_t *program; +}; + +/** + * struct sir_bpf_offload_capabilities - get bpf Capabilities + * @bpf_version: fw's implement version + * @max_bpf_filters: max filters that fw supports + * @max_bytes_for_bpf_inst: the max bytes that can be used as bpf instructions + * @remaining_bytes_for_bpf_inst: remaining bytes for bpf instructions + * + */ +struct sir_bpf_get_offload { + uint32_t bpf_version; + uint32_t max_bpf_filters; + uint32_t max_bytes_for_bpf_inst; + uint32_t remaining_bytes_for_bpf_inst; +}; + #endif /* __SIR_API_H */ diff --git a/core/mac/src/include/sir_params.h b/core/mac/src/include/sir_params.h index af4faed28d..9959dda2df 100644 --- a/core/mac/src/include/sir_params.h +++ b/core/mac/src/include/sir_params.h @@ -604,6 +604,9 @@ typedef struct sSirMbMsgP2p { #define SIR_HAL_ADD_BCN_FILTER_CMDID (SIR_HAL_ITC_MSG_TYPES_BEGIN + 339) #define SIR_HAL_REMOVE_BCN_FILTER_CMDID (SIR_HAL_ITC_MSG_TYPES_BEGIN + 340) +#define SIR_HAL_BPF_GET_CAPABILITIES_REQ (SIR_HAL_ITC_MSG_TYPES_BEGIN + 341) +#define SIR_HAL_BPF_SET_INSTRUCTIONS_REQ (SIR_HAL_ITC_MSG_TYPES_BEGIN + 342) + #define SIR_HAL_MSG_TYPES_END (SIR_HAL_MSG_TYPES_BEGIN + 0x1FF) /* CFG message types */ diff --git a/core/sme/inc/sme_api.h b/core/sme/inc/sme_api.h index 0906720997..64ef4362a7 100644 --- a/core/sme/inc/sme_api.h +++ b/core/sme/inc/sme_api.h @@ -1117,4 +1117,12 @@ bool sme_is_sta_smps_allowed(tHalHandle hHal, uint8_t session_id); QDF_STATUS sme_add_beacon_filter(tHalHandle hal, uint32_t session_id, uint32_t *ie_map); QDF_STATUS sme_remove_beacon_filter(tHalHandle hal, uint32_t session_id); +QDF_STATUS sme_bpf_offload_register_callback(tHalHandle hal, + void (*pbpf_get_offload_cb)(void *, + struct sir_bpf_get_offload *)); + +QDF_STATUS sme_get_bpf_offload_capabilities(tHalHandle hal); +QDF_STATUS sme_set_bpf_instructions(tHalHandle hal, + struct sir_bpf_set_offload *); + #endif /* #if !defined( __SME_API_H ) */ diff --git a/core/sme/inc/sme_internal.h b/core/sme/inc/sme_internal.h index 674e8b5978..6414ae8a24 100644 --- a/core/sme/inc/sme_internal.h +++ b/core/sme/inc/sme_internal.h @@ -234,6 +234,8 @@ typedef struct tagSmeStruct { ocb_callback dcc_stats_event_callback; sme_set_thermal_level_callback set_thermal_level_cb; void *saved_scan_cmd; + void (*pbpf_get_offload_cb)(void *context, + struct sir_bpf_get_offload *); } 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 c691401beb..120f306b8d 100644 --- a/core/sme/src/common/sme_api.c +++ b/core/sme/src/common/sme_api.c @@ -14445,7 +14445,7 @@ QDF_STATUS sme_wifi_start_logger(tHalHandle hal, status = cds_mq_post_message(CDS_MQ_ID_WMA, &cds_message); if (!QDF_IS_STATUS_SUCCESS(status)) { sms_log(mac, LOGE, - FL("vos_mq_post_message failed!(err=%d)"), + FL("cds_mq_post_message failed!(err=%d)"), status); qdf_mem_free(req_msg); status = QDF_STATUS_E_FAILURE; @@ -15629,3 +15629,128 @@ QDF_STATUS sme_remove_beacon_filter(tHalHandle hal, uint32_t session_id) } return qdf_status; } + + +/** + * sme_get_bpf_offload_capabilities() - Get length for BPF offload + * @hal: Global HAL handle + * This function constructs the cds message and fill in message type, + * post the same to WDA. + * Return: QDF_STATUS enumeration + */ +QDF_STATUS sme_get_bpf_offload_capabilities(tHalHandle hal) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + tpAniSirGlobal mac_ctx = PMAC_STRUCT(hal); + cds_msg_t cds_msg; + + sms_log(mac_ctx, LOG1, FL("enter")); + + status = sme_acquire_global_lock(&mac_ctx->sme); + if (QDF_STATUS_SUCCESS == status) { + /* Serialize the req through MC thread */ + cds_msg.bodyptr = NULL; + cds_msg.type = WDA_BPF_GET_CAPABILITIES_REQ; + status = cds_mq_post_message(QDF_MODULE_ID_WMA, &cds_msg); + if (!QDF_IS_STATUS_SUCCESS(status)) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + FL("Post bpf get offload msg fail")); + status = QDF_STATUS_E_FAILURE; + } + sme_release_global_lock(&mac_ctx->sme); + } else { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + FL("sme_acquire_global_lock error")); + } + sms_log(mac_ctx, LOG1, FL("exit")); + return status; +} + + +/** + * sme_set_bpf_instructions() - Set BPF bpf filter instructions. + * @hal: HAL handle + * @bpf_set_offload: struct to set bpf filter instructions. + * + * Return: QDF_STATUS enumeration. + */ +QDF_STATUS sme_set_bpf_instructions(tHalHandle hal, + struct sir_bpf_set_offload *req) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + tpAniSirGlobal mac_ctx = PMAC_STRUCT(hal); + cds_msg_t cds_msg; + struct sir_bpf_set_offload *set_offload; + + set_offload = qdf_mem_malloc(sizeof(*set_offload)); + + if (NULL == set_offload) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + FL("Failed to alloc set_offload")); + return QDF_STATUS_E_NOMEM; + } + + set_offload->session_id = req->session_id; + set_offload->filter_id = req->filter_id; + set_offload->current_offset = req->current_offset; + set_offload->total_length = req->total_length; + if (set_offload->total_length) { + set_offload->current_length = req->current_length; + set_offload->program = qdf_mem_malloc(sizeof(uint8_t) * + req->current_length); + qdf_mem_copy(set_offload->program, req->program, + set_offload->current_length); + } + status = sme_acquire_global_lock(&mac_ctx->sme); + if (QDF_STATUS_SUCCESS == status) { + /* Serialize the req through MC thread */ + cds_msg.bodyptr = set_offload; + cds_msg.type = WDA_BPF_SET_INSTRUCTIONS_REQ; + status = cds_mq_post_message(QDF_MODULE_ID_WMA, &cds_msg); + + if (!QDF_IS_STATUS_SUCCESS(status)) { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + FL("Post BPF set offload msg fail")); + status = QDF_STATUS_E_FAILURE; + if (set_offload->total_length) + qdf_mem_free(set_offload->program); + qdf_mem_free(set_offload); + } + sme_release_global_lock(&mac_ctx->sme); + } else { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + FL("sme_acquire_global_lock failed")); + if (set_offload->total_length) + qdf_mem_free(set_offload->program); + qdf_mem_free(set_offload); + } + return status; +} + +/** + * sme_bpf_offload_register_callback() - Register get bpf offload callbacK + * + * @hal - MAC global handle + * @callback_routine - callback routine from HDD + * + * This API is invoked by HDD to register its callback in SME + * + * Return: QDF_STATUS + */ +QDF_STATUS sme_bpf_offload_register_callback(tHalHandle hal, + void (*pbpf_get_offload_cb)(void *context, + struct sir_bpf_get_offload *)) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + tpAniSirGlobal mac = PMAC_STRUCT(hal); + + status = sme_acquire_global_lock(&mac->sme); + if (QDF_IS_STATUS_SUCCESS(status)) { + mac->sme.pbpf_get_offload_cb = pbpf_get_offload_cb; + sme_release_global_lock(&mac->sme); + } else { + QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR, + FL("sme_acquire_global_lock failed")); + } + return status; +} diff --git a/core/wma/inc/wma.h b/core/wma/inc/wma.h index a7959ae133..3f1ac3b5fa 100644 --- a/core/wma/inc/wma.h +++ b/core/wma/inc/wma.h @@ -2048,5 +2048,12 @@ QDF_STATUS wma_ht40_stop_obss_scan(tp_wma_handle wma_handle, int32_t vdev_id); QDF_STATUS wma_send_ht40_obss_scanind(tp_wma_handle wma, struct obss_ht40_scanind *req); + +int wma_get_bpf_caps_event_handler(void *handle, + u_int8_t *cmd_param_info, + u_int32_t len); +QDF_STATUS wma_get_bpf_capabilities(tp_wma_handle wma); +QDF_STATUS wma_set_bpf_instructions(tp_wma_handle wma, + struct sir_bpf_set_offload *bpf_set_offload); #endif struct wma_ini_config *wma_get_ini_handle(tp_wma_handle wma_handle); diff --git a/core/wma/inc/wma_tgt_cfg.h b/core/wma/inc/wma_tgt_cfg.h index 94c5b05fca..e6a8c86a9f 100644 --- a/core/wma/inc/wma_tgt_cfg.h +++ b/core/wma/inc/wma_tgt_cfg.h @@ -165,5 +165,6 @@ struct wma_tgt_cfg { bool egap_support; #endif uint32_t fine_time_measurement_cap; + bool bpf_enabled; }; #endif /* WMA_TGT_CFG_H */ diff --git a/core/wma/inc/wma_types.h b/core/wma/inc/wma_types.h index 791f0f8891..252d271220 100644 --- a/core/wma/inc/wma_types.h +++ b/core/wma/inc/wma_types.h @@ -468,6 +468,9 @@ #define WMA_ADD_BCN_FILTER_CMDID SIR_HAL_ADD_BCN_FILTER_CMDID #define WMA_REMOVE_BCN_FILTER_CMDID SIR_HAL_REMOVE_BCN_FILTER_CMDID +#define WDA_BPF_GET_CAPABILITIES_REQ SIR_HAL_BPF_GET_CAPABILITIES_REQ +#define WDA_BPF_SET_INSTRUCTIONS_REQ SIR_HAL_BPF_SET_INSTRUCTIONS_REQ + /* Bit 6 will be used to control BD rate for Management frames */ #define HAL_USE_BD_RATE2_FOR_MANAGEMENT_FRAME 0x40 diff --git a/core/wma/src/wma_features.c b/core/wma/src/wma_features.c index b8727ca1d3..00c2df030d 100644 --- a/core/wma/src/wma_features.c +++ b/core/wma/src/wma_features.c @@ -6904,3 +6904,172 @@ QDF_STATUS wma_process_set_ie_info(tp_wma_handle wma, return ret; } +/** + * wma_get_bpf_caps_event_handler() - Event handler for get bpf capability + * @handle: WMA global handle + * @cmd_param_info: command event data + * @len: Length of @cmd_param_info + * + * Return: 0 on Success or Errno on failure + */ +int wma_get_bpf_caps_event_handler(void *handle, + u_int8_t *cmd_param_info, + u_int32_t len) +{ + WMI_BPF_CAPABILIY_INFO_EVENTID_param_tlvs *param_buf; + wmi_bpf_capability_info_evt_fixed_param *event; + struct sir_bpf_get_offload *bpf_get_offload; + tpAniSirGlobal pmac = (tpAniSirGlobal)cds_get_context( + QDF_MODULE_ID_PE); + + if (!pmac) { + WMA_LOGE("%s: Invalid pmac", __func__); + return -EINVAL; + } + if (!pmac->sme.pbpf_get_offload_cb) { + WMA_LOGE("%s: Callback not registered", __func__); + return -EINVAL; + } + + param_buf = (WMI_BPF_CAPABILIY_INFO_EVENTID_param_tlvs *)cmd_param_info; + event = param_buf->fixed_param; + bpf_get_offload = qdf_mem_malloc(sizeof(*bpf_get_offload)); + + if (!bpf_get_offload) { + WMA_LOGP("%s: Memory allocation failed.", __func__); + return -ENOMEM; + } + + bpf_get_offload->bpf_version = event->bpf_version; + bpf_get_offload->max_bpf_filters = event->max_bpf_filters; + bpf_get_offload->max_bytes_for_bpf_inst = + event->max_bytes_for_bpf_inst; + WMA_LOGD("%s: BPF capabilities version: %d max bpf filter size: %d", + __func__, bpf_get_offload->bpf_version, + bpf_get_offload->max_bytes_for_bpf_inst); + + WMA_LOGD("%s: sending bpf capabilities event to hdd", __func__); + pmac->sme.pbpf_get_offload_cb(pmac->hHdd, bpf_get_offload); + qdf_mem_free(bpf_get_offload); + return 0; +} + +/** + * wma_get_bpf_capabilities - Send get bpf capability to firmware + * @wma_handle: wma handle + * + * Return: QDF_STATUS enumeration. + */ +QDF_STATUS wma_get_bpf_capabilities(tp_wma_handle wma) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + wmi_bpf_get_capability_cmd_fixed_param *cmd; + wmi_buf_t wmi_buf; + uint32_t len; + u_int8_t *buf_ptr; + + if (!wma || !wma->wmi_handle) { + WMA_LOGE(FL("WMA is closed, can not issue get BPF capab")); + return QDF_STATUS_E_INVAL; + } + + if (!WMI_SERVICE_IS_ENABLED(wma->wmi_service_bitmap, + WMI_SERVICE_BPF_OFFLOAD)) { + WMA_LOGE(FL("BPF cababilities feature bit not enabled")); + return QDF_STATUS_E_FAILURE; + } + + len = sizeof(*cmd); + wmi_buf = wmi_buf_alloc(wma->wmi_handle, len); + if (!wmi_buf) { + WMA_LOGE("%s: wmi_buf_alloc failed", __func__); + return QDF_STATUS_E_NOMEM; + } + + buf_ptr = (u_int8_t *) wmi_buf_data(wmi_buf); + cmd = (wmi_bpf_get_capability_cmd_fixed_param *) buf_ptr; + WMITLV_SET_HDR(&cmd->tlv_header, + WMITLV_TAG_STRUC_wmi_bpf_get_capability_cmd_fixed_param, + WMITLV_GET_STRUCT_TLVLEN( + wmi_bpf_get_capability_cmd_fixed_param)); + + if (wmi_unified_cmd_send(wma->wmi_handle, wmi_buf, len, + WMI_BPF_GET_CAPABILITY_CMDID)) { + WMA_LOGE(FL("Failed to send BPF capability command")); + wmi_buf_free(wmi_buf); + return QDF_STATUS_E_FAILURE; + } + return status; +} + +/** + * wma_set_bpf_instructions - Set bpf instructions to firmware + * @wma: wma handle + * @bpf_set_offload: Bpf offload information to set to firmware + * + * Return: QDF_STATUS enumeration + */ +QDF_STATUS wma_set_bpf_instructions(tp_wma_handle wma, + struct sir_bpf_set_offload *bpf_set_offload) +{ + wmi_bpf_set_vdev_instructions_cmd_fixed_param *cmd; + wmi_buf_t wmi_buf; + uint32_t len = 0, len_aligned = 0; + u_int8_t *buf_ptr; + + if (!wma || !wma->wmi_handle) { + WMA_LOGE("%s: WMA is closed, can not issue set BPF capability", + __func__); + return QDF_STATUS_E_INVAL; + } + + if (!WMI_SERVICE_IS_ENABLED(wma->wmi_service_bitmap, + WMI_SERVICE_BPF_OFFLOAD)) { + WMA_LOGE(FL("BPF offload feature Disabled")); + return QDF_STATUS_E_NOSUPPORT; + } + + if (bpf_set_offload->total_length) { + len_aligned = roundup(bpf_set_offload->current_length, + sizeof(A_UINT32)); + len = len_aligned + WMI_TLV_HDR_SIZE; + } + + len += sizeof(*cmd); + wmi_buf = wmi_buf_alloc(wma->wmi_handle, len); + if (!wmi_buf) { + WMA_LOGE("%s: wmi_buf_alloc failed", __func__); + return QDF_STATUS_E_NOMEM; + } + + buf_ptr = (u_int8_t *) wmi_buf_data(wmi_buf); + cmd = (wmi_bpf_set_vdev_instructions_cmd_fixed_param *) buf_ptr; + + WMITLV_SET_HDR(&cmd->tlv_header, + WMITLV_TAG_STRUC_wmi_bpf_set_vdev_instructions_cmd_fixed_param, + WMITLV_GET_STRUCT_TLVLEN( + wmi_bpf_set_vdev_instructions_cmd_fixed_param)); + cmd->vdev_id = bpf_set_offload->session_id; + cmd->filter_id = bpf_set_offload->filter_id; + cmd->total_length = bpf_set_offload->total_length; + cmd->current_offset = bpf_set_offload->current_offset; + cmd->current_length = bpf_set_offload->current_length; + + if (bpf_set_offload->total_length) { + buf_ptr += + sizeof(wmi_bpf_set_vdev_instructions_cmd_fixed_param); + WMITLV_SET_HDR(buf_ptr, WMITLV_TAG_ARRAY_BYTE, len_aligned); + buf_ptr += WMI_TLV_HDR_SIZE; + qdf_mem_copy(buf_ptr, bpf_set_offload->program, + bpf_set_offload->current_length); + qdf_mem_free(bpf_set_offload->program); + } + + if (wmi_unified_cmd_send(wma->wmi_handle, wmi_buf, len, + WMI_BPF_SET_VDEV_INSTRUCTIONS_CMDID)) { + WMA_LOGE(FL("Failed to send config bpf instructions command")); + wmi_buf_free(wmi_buf); + return QDF_STATUS_E_FAILURE; + } + return QDF_STATUS_SUCCESS; +} diff --git a/core/wma/src/wma_main.c b/core/wma/src/wma_main.c index 89889ddca5..4db22b02b5 100644 --- a/core/wma/src/wma_main.c +++ b/core/wma/src/wma_main.c @@ -2063,6 +2063,10 @@ QDF_STATUS wma_open(void *cds_context, WMI_PEER_DELETE_RESP_EVENTID, wma_peer_delete_handler, WMA_RX_SERIALIZER_CTX); + wmi_unified_register_event_handler(wma_handle->wmi_handle, + WMI_BPF_CAPABILIY_INFO_EVENTID, + wma_get_bpf_caps_event_handler, + WMA_RX_SERIALIZER_CTX); return QDF_STATUS_SUCCESS; err_dbglog_init: @@ -5315,6 +5319,13 @@ QDF_STATUS wma_mc_process_msg(void *cds_context, cds_msg_t *msg) wma_remove_beacon_filter(wma_handle, msg->bodyptr); qdf_mem_free(msg->bodyptr); break; + case WDA_BPF_GET_CAPABILITIES_REQ: + wma_get_bpf_capabilities(wma_handle); + break; + case WDA_BPF_SET_INSTRUCTIONS_REQ: + wma_set_bpf_instructions(wma_handle, msg->bodyptr); + qdf_mem_free(msg->bodyptr); + break; default: WMA_LOGD("unknow msg type %x", msg->type);