diff options
| author | Yun Park <yunp@codeaurora.org> | 2016-10-05 10:51:33 -0700 |
|---|---|---|
| committer | Yun Park <yunp@codeaurora.org> | 2016-12-27 14:25:35 -0800 |
| commit | 08649bd7b811d26e221f811f89d23af29cc10f34 (patch) | |
| tree | c1698490cff07eab505423624a128bd2ca1bc0c4 | |
| parent | d25bfa4bde18b6973c99d1aec0114a6a89d96041 (diff) | |
qcacld-3.0: Add interface to WDI-stats and quota limit
Add structure changes for querying WDI-stats, register the callback
functions for IPA to query WDI stats and sets the quota limit
Change-Id: I05d9d50fad735fa0a57c4de23c81461f6e12b648
CRs-Fixed: 1095710
| -rw-r--r-- | Kbuild | 6 | ||||
| -rw-r--r-- | core/dp/htt/htt_h2t.c | 139 | ||||
| -rw-r--r-- | core/dp/ol/inc/ol_htt_api.h | 29 | ||||
| -rw-r--r-- | core/dp/txrx/ol_txrx.c | 11 | ||||
| -rw-r--r-- | core/hdd/inc/wlan_hdd_ipa.h | 4 | ||||
| -rw-r--r-- | core/hdd/src/wlan_hdd_ipa.c | 400 | ||||
| -rw-r--r-- | core/wma/inc/wma.h | 4 | ||||
| -rw-r--r-- | core/wma/src/wma_main.c | 34 | ||||
| -rw-r--r-- | target/inc/htt.h | 137 |
9 files changed, 737 insertions, 27 deletions
@@ -106,6 +106,7 @@ ifeq ($(KERNEL_BUILD), 0) endif ifeq ($(CONFIG_ARCH_MSM8998), y) CONFIG_QCACLD_FEATURE_GREEN_AP := y + CONFIG_QCACLD_FEATURE_METERING := y endif #Flag to enable Fast Transition (11r) feature @@ -1462,6 +1463,11 @@ ifeq ($(CONFIG_QCACLD_FEATURE_GREEN_AP),y) CDEFINES += -DFEATURE_GREEN_AP endif +#Stats & Quota Metering feature +ifeq ($(CONFIG_QCACLD_FEATURE_METERING),y) +CDEFINES += -DFEATURE_METERING +endif + #Enable RX Full re-order OL feature only "LL and NON-MDM9630 platform" ifneq ($(CONFIG_ARCH_MDM9630), y) ifeq ($(CONFIG_HIF_PCI), 1) diff --git a/core/dp/htt/htt_h2t.c b/core/dp/htt/htt_h2t.c index 0296ec967839..dba5f365110f 100644 --- a/core/dp/htt/htt_h2t.c +++ b/core/dp/htt/htt_h2t.c @@ -1320,4 +1320,143 @@ int htt_h2t_ipa_uc_get_stats(struct htt_pdev_t *pdev) HTT_SEND_HTC_PKT(pdev, pkt); return A_OK; } + +/** + * htt_h2t_ipa_uc_get_share_stats() - WDI UC wifi sharing state request to FW + * @pdev: handle to the HTT instance + * + * Return: A_OK success + * A_NO_MEMORY No memory fail + */ +int htt_h2t_ipa_uc_get_share_stats(struct htt_pdev_t *pdev, uint8_t reset_stats) +{ + struct htt_htc_pkt *pkt; + qdf_nbuf_t msg; + uint32_t *msg_word; + + pkt = htt_htc_pkt_alloc(pdev); + if (!pkt) + return -A_NO_MEMORY; + + /* show that this is not a tx frame download + * (not required, but helpful) + */ + pkt->msdu_id = HTT_TX_COMPL_INV_MSDU_ID; + pkt->pdev_ctxt = NULL; /* not used during send-done callback */ + + /* reserve room for HTC header */ + msg = qdf_nbuf_alloc(pdev->osdev, + HTT_MSG_BUF_SIZE(HTT_WDI_IPA_OP_REQUEST_SZ)+ + HTT_MSG_BUF_SIZE(HTT_WDI_IPA_OP_REQ_GET_SHARING_STATS_SZ), + HTC_HEADER_LEN + HTC_HDR_ALIGNMENT_PADDING, 4, false); + if (!msg) { + htt_htc_pkt_free(pdev, pkt); + return -A_NO_MEMORY; + } + /* set the length of the message */ + qdf_nbuf_put_tail(msg, HTT_WDI_IPA_OP_REQUEST_SZ+ + HTT_WDI_IPA_OP_REQ_GET_SHARING_STATS_SZ); + + /* fill in the message contents */ + msg_word = (uint32_t *) qdf_nbuf_data(msg); + + /* rewind beyond alignment pad to get to the HTC header reserved area */ + qdf_nbuf_push_head(msg, HTC_HDR_ALIGNMENT_PADDING); + + *msg_word = 0; + HTT_WDI_IPA_OP_REQUEST_OP_CODE_SET(*msg_word, + HTT_WDI_IPA_OPCODE_GET_SHARING_STATS); + HTT_H2T_MSG_TYPE_SET(*msg_word, HTT_H2T_MSG_TYPE_WDI_IPA_OP_REQ); + + msg_word++; + *msg_word = 0; + HTT_WDI_IPA_OP_REQ_GET_SHARING_STATS_RESET_STATS_SET(*msg_word, + reset_stats); + + SET_HTC_PACKET_INFO_TX(&pkt->htc_pkt, + htt_h2t_send_complete_free_netbuf, + qdf_nbuf_data(msg), + qdf_nbuf_len(msg), + pdev->htc_tx_endpoint, + 1); /* tag - not relevant here */ + + SET_HTC_PACKET_NET_BUF_CONTEXT(&pkt->htc_pkt, msg); + HTT_SEND_HTC_PKT(pdev, pkt); + return A_OK; +} + +/** + * htt_h2t_ipa_uc_set_quota() - WDI UC state query request to firmware + * @pdev: handle to the HTT instance + * + * Return: A_OK success + * A_NO_MEMORY No memory fail + */ +int htt_h2t_ipa_uc_set_quota(struct htt_pdev_t *pdev, uint64_t quota_bytes) +{ + struct htt_htc_pkt *pkt; + qdf_nbuf_t msg; + uint32_t *msg_word; + + pkt = htt_htc_pkt_alloc(pdev); + if (!pkt) + return -A_NO_MEMORY; + + /* show that this is not a tx frame download + * (not required, but helpful) + */ + pkt->msdu_id = HTT_TX_COMPL_INV_MSDU_ID; + pkt->pdev_ctxt = NULL; /* not used during send-done callback */ + + /* reserve room for HTC header */ + msg = qdf_nbuf_alloc(pdev->osdev, + HTT_MSG_BUF_SIZE(HTT_WDI_IPA_OP_REQUEST_SZ)+ + HTT_MSG_BUF_SIZE(HTT_WDI_IPA_OP_REQ_SET_QUOTA_SZ), + HTC_HEADER_LEN + HTC_HDR_ALIGNMENT_PADDING, 4, false); + if (!msg) { + htt_htc_pkt_free(pdev, pkt); + return -A_NO_MEMORY; + } + /* set the length of the message */ + qdf_nbuf_put_tail(msg, HTT_WDI_IPA_OP_REQUEST_SZ+ + HTT_WDI_IPA_OP_REQ_SET_QUOTA_SZ); + + /* fill in the message contents */ + msg_word = (uint32_t *) qdf_nbuf_data(msg); + + /* rewind beyond alignment pad to get to the HTC header reserved area */ + qdf_nbuf_push_head(msg, HTC_HDR_ALIGNMENT_PADDING); + + *msg_word = 0; + HTT_WDI_IPA_OP_REQUEST_OP_CODE_SET(*msg_word, + HTT_WDI_IPA_OPCODE_SET_QUOTA); + HTT_H2T_MSG_TYPE_SET(*msg_word, HTT_H2T_MSG_TYPE_WDI_IPA_OP_REQ); + + msg_word++; + *msg_word = 0; + HTT_WDI_IPA_OP_REQ_SET_QUOTA_SET_QUOTA_SET(*msg_word, quota_bytes > 0); + + msg_word++; + *msg_word = 0; + HTT_WDI_IPA_OP_REQ_SET_QUOTA_QUOTA_LO_SET(*msg_word, + (uint32_t)(quota_bytes & + HTT_WDI_IPA_OP_REQ_SET_QUOTA_QUOTA_LO_M)); + + msg_word++; + *msg_word = 0; + HTT_WDI_IPA_OP_REQ_SET_QUOTA_QUOTA_HI_SET(*msg_word, + (uint32_t)(quota_bytes>>32 & + HTT_WDI_IPA_OP_REQ_SET_QUOTA_QUOTA_HI_M)); + + SET_HTC_PACKET_INFO_TX(&pkt->htc_pkt, + htt_h2t_send_complete_free_netbuf, + qdf_nbuf_data(msg), + qdf_nbuf_len(msg), + pdev->htc_tx_endpoint, + 1); /* tag - not relevant here */ + + SET_HTC_PACKET_NET_BUF_CONTEXT(&pkt->htc_pkt, msg); + HTT_SEND_HTC_PKT(pdev, pkt); + return A_OK; +} #endif /* IPA_OFFLOAD */ diff --git a/core/dp/ol/inc/ol_htt_api.h b/core/dp/ol/inc/ol_htt_api.h index 311de8896708..7adbf244206c 100644 --- a/core/dp/ol/inc/ol_htt_api.h +++ b/core/dp/ol/inc/ol_htt_api.h @@ -242,6 +242,11 @@ htt_h2t_ipa_uc_set_active(struct htt_pdev_t *pdev, bool uc_active, bool is_tx); int htt_h2t_ipa_uc_get_stats(struct htt_pdev_t *pdev); +int htt_h2t_ipa_uc_get_share_stats(struct htt_pdev_t *pdev, + uint8_t reset_stats); + +int htt_h2t_ipa_uc_set_quota(struct htt_pdev_t *pdev, uint64_t quota_bytes); + int htt_ipa_uc_attach(struct htt_pdev_t *pdev); void htt_ipa_uc_detach(struct htt_pdev_t *pdev); @@ -340,6 +345,30 @@ static inline int htt_h2t_ipa_uc_get_stats(struct htt_pdev_t *pdev) } /** + * htt_h2t_ipa_uc_get_share_stats() - WDI UC wifi sharing state request to FW + * @pdev: handle to the HTT instance + * + * Return: 0 success + */ +static inline int htt_h2t_ipa_uc_get_share_stats(struct htt_pdev_t *pdev, + uint8_t reset_stats) +{ + return 0; +} + +/** + * htt_h2t_ipa_uc_set_quota() - WDI UC set quota request to firmware + * @pdev: handle to the HTT instance + * + * Return: 0 success + */ +static inline int htt_h2t_ipa_uc_set_quota(struct htt_pdev_t *pdev, + uint64_t quota_bytes) +{ + return 0; +} + +/** * htt_ipa_uc_attach() - Allocate UC data path resources * @pdev: handle to the HTT instance * diff --git a/core/dp/txrx/ol_txrx.c b/core/dp/txrx/ol_txrx.c index 9cabed53a1b0..92c005bf03fe 100644 --- a/core/dp/txrx/ol_txrx.c +++ b/core/dp/txrx/ol_txrx.c @@ -4202,6 +4202,17 @@ void ol_txrx_ipa_uc_get_stat(ol_txrx_pdev_handle pdev) { htt_h2t_ipa_uc_get_stats(pdev->htt_pdev); } + +void ol_txrx_ipa_uc_get_share_stats(ol_txrx_pdev_handle pdev, + uint8_t reset_stats) +{ + htt_h2t_ipa_uc_get_share_stats(pdev->htt_pdev, reset_stats); +} + +void ol_txrx_ipa_uc_set_quota(ol_txrx_pdev_handle pdev, uint64_t quota_bytes) +{ + htt_h2t_ipa_uc_set_quota(pdev->htt_pdev, quota_bytes); +} #endif /* IPA_UC_OFFLOAD */ /** diff --git a/core/hdd/inc/wlan_hdd_ipa.h b/core/hdd/inc/wlan_hdd_ipa.h index 52958669b403..0fec6b85e9d4 100644 --- a/core/hdd/inc/wlan_hdd_ipa.h +++ b/core/hdd/inc/wlan_hdd_ipa.h @@ -98,6 +98,10 @@ void hdd_ipa_uc_stat_query(hdd_context_t *hdd_ctx, uint32_t *ipa_tx_diff, uint32_t *ipa_rx_diff); void hdd_ipa_uc_rt_debug_host_dump(hdd_context_t *hdd_ctx); void hdd_ipa_uc_stat_request(hdd_adapter_t *adapter, uint8_t reason); +void hdd_ipa_uc_sharing_stats_request(hdd_adapter_t *adapter, + uint8_t reset_stats); +void hdd_ipa_uc_set_quota(hdd_adapter_t *adapter, uint8_t set_quota, + uint64_t quota_bytes); bool hdd_ipa_is_enabled(hdd_context_t *pHddCtx); bool hdd_ipa_uc_is_enabled(hdd_context_t *pHddCtx); #ifndef QCA_LL_TX_FLOW_CONTROL_V2 diff --git a/core/hdd/src/wlan_hdd_ipa.c b/core/hdd/src/wlan_hdd_ipa.c index bd6efc14fa0d..72aec6901739 100644 --- a/core/hdd/src/wlan_hdd_ipa.c +++ b/core/hdd/src/wlan_hdd_ipa.c @@ -96,6 +96,11 @@ typedef enum { HDD_IPA_UC_OPCODE_RX_SUSPEND = 2, HDD_IPA_UC_OPCODE_RX_RESUME = 3, HDD_IPA_UC_OPCODE_STATS = 4, +#ifdef FEATURE_METERING + HDD_IPA_UC_OPCODE_SHARING_STATS = 5, + HDD_IPA_UC_OPCODE_QUOTA_RSP = 6, + HDD_IPA_UC_OPCODE_QUOTA_IND = 7, +#endif /* keep this last */ HDD_IPA_UC_OPCODE_MAX } hdd_ipa_uc_op_code; @@ -388,6 +393,30 @@ struct hdd_ipa_tx_desc { struct ipa_rx_data *ipa_tx_desc_ptr; }; +#ifdef FEATURE_METERING +struct ipa_uc_sharing_stats { + uint64_t ipv4_rx_packets; + uint64_t ipv4_rx_bytes; + uint64_t ipv6_rx_packets; + uint64_t ipv6_rx_bytes; + uint64_t ipv4_tx_packets; + uint64_t ipv4_tx_bytes; + uint64_t ipv6_tx_packets; + uint64_t ipv6_tx_bytes; +}; + +struct ipa_uc_quota_rsp { + uint8_t success; + uint8_t reserved[3]; + uint32_t quota_lo; /* quota limit low bytes */ + uint32_t quota_hi; /* quota limit high bytes */ +}; + +struct ipa_uc_quota_ind { + uint64_t quota_bytes; /* quota limit in bytes */ +}; +#endif + struct hdd_ipa_priv { struct hdd_ipa_sys_pipe sys_pipe[HDD_IPA_MAX_SYSBAM_PIPE]; struct hdd_ipa_iface_context iface_context[HDD_IPA_MAX_IFACE]; @@ -460,9 +489,15 @@ struct hdd_ipa_priv { /* IPA UC doorbell registers paddr */ qdf_dma_addr_t tx_comp_doorbell_paddr; qdf_dma_addr_t rx_ready_doorbell_paddr; - uint8_t vdev_to_iface[CSR_ROAM_SESSION_MAX]; bool vdev_offload_enabled[CSR_ROAM_SESSION_MAX]; +#ifdef FEATURE_METERING + struct ipa_uc_sharing_stats ipa_sharing_stats; + struct ipa_uc_quota_rsp ipa_quota_rsp; + struct ipa_uc_quota_ind ipa_quota_ind; + struct completion ipa_uc_sharing_stats_comp; + struct completion ipa_uc_set_quota_comp; +#endif }; #define HDD_IPA_WLAN_FRAG_HEADER sizeof(struct frag_header) @@ -585,6 +620,11 @@ static const char *op_string[] = { "STATS", }; +#ifdef FEATURE_METERING +#define IPA_UC_SHARING_STATES_WAIT_TIME 500 +#define IPA_UC_SET_QUOTA_WAIT_TIME 500 +#endif + static struct hdd_ipa_priv *ghdd_ipa; /* Local Function Prototypes */ @@ -635,20 +675,13 @@ static inline bool hdd_ipa_uc_sta_is_enabled(hdd_context_t *hdd_ctx) * * Return: None */ -#ifdef IPA_UC_STA_OFFLOAD static inline void hdd_ipa_uc_sta_reset_sta_connected( struct hdd_ipa_priv *hdd_ipa) { - vos_lock_acquire(&hdd_ipa->event_lock); + qdf_mutex_acquire(&hdd_ipa->ipa_lock); hdd_ipa->sta_connected = 0; - vos_lock_release(&hdd_ipa->event_lock); -} -#else -static inline void hdd_ipa_uc_sta_reset_sta_connected( - struct hdd_ipa_priv *hdd_ipa) -{ + qdf_mutex_release(&hdd_ipa->ipa_lock); } -#endif /** * hdd_ipa_is_pre_filter_enabled() - Is IPA pre-filter enabled? @@ -853,7 +886,8 @@ static void hdd_ipa_uc_rt_debug_handler(void *ctext) hdd_alert("Dummy alloc fail"); hdd_ipa_uc_rt_debug_host_dump(hdd_ctx); hdd_ipa_uc_stat_request( - hdd_get_adapter(hdd_ctx, QDF_SAP_MODE), 1); + hdd_get_adapter(hdd_ctx, QDF_SAP_MODE), + HDD_IPA_UC_STAT_REASON_DEBUG); } else { kfree(dummy_ptr); } @@ -1261,9 +1295,8 @@ static void __hdd_ipa_uc_stat_request(hdd_adapter_t *adapter, uint8_t reason) hdd_context_t *hdd_ctx; struct hdd_ipa_priv *hdd_ipa; - if (!adapter) { + if (!adapter) return; - } hdd_ctx = (hdd_context_t *)adapter->pHddCtx; @@ -1281,12 +1314,14 @@ static void __hdd_ipa_uc_stat_request(hdd_adapter_t *adapter, uint8_t reason) if ((HDD_IPA_UC_NUM_WDI_PIPE == hdd_ipa->activated_fw_pipe) && (false == hdd_ipa->resource_loading)) { hdd_ipa->stat_req_reason = reason; + qdf_mutex_release(&hdd_ipa->ipa_lock); wma_cli_set_command( (int)adapter->sessionId, (int)WMA_VDEV_TXRX_GET_IPA_UC_FW_STATS_CMDID, 0, VDEV_CMD); + } else { + qdf_mutex_release(&hdd_ipa->ipa_lock); } - qdf_mutex_release(&hdd_ipa->ipa_lock); } /** @@ -1303,6 +1338,85 @@ void hdd_ipa_uc_stat_request(hdd_adapter_t *adapter, uint8_t reason) cds_ssr_unprotect(__func__); } +#ifdef FEATURE_METERING +/** + * hdd_ipa_uc_sharing_stats_request() - Get IPA stats from IPA. + * @adapter: network adapter + * @reset_stats: reset stat countis after response + * + * Return: None + */ +void hdd_ipa_uc_sharing_stats_request(hdd_adapter_t *adapter, + uint8_t reset_stats) +{ + hdd_context_t *pHddCtx; + struct hdd_ipa_priv *hdd_ipa; + + if (!adapter) + return; + + pHddCtx = adapter->pHddCtx; + hdd_ipa = pHddCtx->hdd_ipa; + if (!hdd_ipa_is_enabled(pHddCtx) || + !(hdd_ipa_uc_is_enabled(pHddCtx))) { + return; + } + + HDD_IPA_LOG(LOG1, "SHARING_STATS: reset_stats=%d", reset_stats); + qdf_mutex_acquire(&hdd_ipa->ipa_lock); + if ((HDD_IPA_UC_NUM_WDI_PIPE == hdd_ipa->activated_fw_pipe) && + (false == hdd_ipa->resource_loading)) { + qdf_mutex_release(&hdd_ipa->ipa_lock); + wma_cli_set_command( + (int)adapter->sessionId, + (int)WMA_VDEV_TXRX_GET_IPA_UC_SHARING_STATS_CMDID, + reset_stats, VDEV_CMD); + } else { + qdf_mutex_release(&hdd_ipa->ipa_lock); + } +} + +/** + * hdd_ipa_uc_set_quota() - Set quota limit bytes from IPA. + * @adapter: network adapter + * @set_quota: when 1, FW starts quota monitoring + * @quota_bytes: quota limit in bytes + * + * Return: None + */ +void hdd_ipa_uc_set_quota(hdd_adapter_t *adapter, uint8_t set_quota, + uint64_t quota_bytes) +{ + hdd_context_t *pHddCtx; + struct hdd_ipa_priv *hdd_ipa; + + if (!adapter) + return; + + pHddCtx = adapter->pHddCtx; + hdd_ipa = pHddCtx->hdd_ipa; + if (!hdd_ipa_is_enabled(pHddCtx) || + !(hdd_ipa_uc_is_enabled(pHddCtx))) { + return; + } + + HDD_IPA_LOG(LOG1, "SET_QUOTA: set_quota=%d, quota_bytes=%llu", + set_quota, quota_bytes); + + qdf_mutex_acquire(&hdd_ipa->ipa_lock); + if ((HDD_IPA_UC_NUM_WDI_PIPE == hdd_ipa->activated_fw_pipe) && + (false == hdd_ipa->resource_loading)) { + qdf_mutex_release(&hdd_ipa->ipa_lock); + wma_cli_set_command( + (int)adapter->sessionId, + (int)WMA_VDEV_TXRX_SET_IPA_UC_QUOTA_CMDID, + (set_quota ? quota_bytes : 0), VDEV_CMD); + } else { + qdf_mutex_release(&hdd_ipa->ipa_lock); + } +} +#endif + /** * hdd_ipa_uc_find_add_assoc_sta() - Find associated station * @hdd_ipa: Global HDD IPA context @@ -1611,6 +1725,97 @@ static void hdd_ipa_uc_rm_notify_defer(struct work_struct *work) } /** + * hdd_ipa_uc_op_metering() - IPA uC operation for stats and quota limit + * @hdd_ctx: Global HDD context + * @op_msg: operation message received from firmware + * + * Return: QDF_STATUS enumeration + */ +#ifdef FEATURE_METERING +static QDF_STATUS hdd_ipa_uc_op_metering(hdd_context_t *hdd_ctx, + struct op_msg_type *op_msg) +{ + struct op_msg_type *msg = op_msg; + struct ipa_uc_sharing_stats *uc_sharing_stats; + struct ipa_uc_quota_rsp *uc_quota_rsp; + struct ipa_uc_quota_ind *uc_quota_ind; + struct hdd_ipa_priv *hdd_ipa; + hdd_adapter_t *adapter; + + hdd_ipa = (struct hdd_ipa_priv *)hdd_ctx->hdd_ipa; + + if (HDD_IPA_UC_OPCODE_SHARING_STATS == msg->op_code) { + /* fill-up ipa_uc_sharing_stats structure from FW */ + uc_sharing_stats = (struct ipa_uc_sharing_stats *) + ((uint8_t *)op_msg + sizeof(struct op_msg_type)); + + memcpy(&(hdd_ipa->ipa_sharing_stats), uc_sharing_stats, + sizeof(struct ipa_uc_sharing_stats)); + + complete(&hdd_ipa->ipa_uc_sharing_stats_comp); + + HDD_IPA_DP_LOG(QDF_TRACE_LEVEL_DEBUG, + "%s: %llu,%llu,%llu,%llu,%llu,%llu,%llu,%llu", + "HDD_IPA_UC_OPCODE_SHARING_STATS", + hdd_ipa->ipa_sharing_stats.ipv4_rx_packets, + hdd_ipa->ipa_sharing_stats.ipv4_rx_bytes, + hdd_ipa->ipa_sharing_stats.ipv6_rx_packets, + hdd_ipa->ipa_sharing_stats.ipv6_rx_bytes, + hdd_ipa->ipa_sharing_stats.ipv4_tx_packets, + hdd_ipa->ipa_sharing_stats.ipv4_tx_bytes, + hdd_ipa->ipa_sharing_stats.ipv6_tx_packets, + hdd_ipa->ipa_sharing_stats.ipv6_tx_bytes); + } else if (HDD_IPA_UC_OPCODE_QUOTA_RSP == msg->op_code) { + /* received set quota response */ + uc_quota_rsp = (struct ipa_uc_quota_rsp *) + ((uint8_t *)op_msg + sizeof(struct op_msg_type)); + + memcpy(&(hdd_ipa->ipa_quota_rsp), uc_quota_rsp, + sizeof(struct ipa_uc_quota_rsp)); + + complete(&hdd_ipa->ipa_uc_set_quota_comp); + HDD_IPA_DP_LOG(QDF_TRACE_LEVEL_DEBUG, + "%s: success=%d, quota_bytes=%llu", + "HDD_IPA_UC_OPCODE_QUOTA_RSP", + hdd_ipa->ipa_quota_rsp.success, + ((uint64_t)(hdd_ipa->ipa_quota_rsp.quota_hi)<<32)| + hdd_ipa->ipa_quota_rsp.quota_lo); + } else if (HDD_IPA_UC_OPCODE_QUOTA_IND == msg->op_code) { + /* hit quota limit */ + uc_quota_ind = (struct ipa_uc_quota_ind *) + ((uint8_t *)op_msg + sizeof(struct op_msg_type)); + + hdd_ipa->ipa_quota_ind.quota_bytes = + uc_quota_ind->quota_bytes; + + /* send quota exceeded indication to IPA */ + HDD_IPA_DP_LOG(QDF_TRACE_LEVEL_DEBUG, + "OPCODE_QUOTA_IND: quota exceed! (quota_bytes=%llu)", + hdd_ipa->ipa_quota_ind.quota_bytes); + + adapter = hdd_get_adapter(hdd_ipa->hdd_ctx, QDF_STA_MODE); + if (adapter) + ipa_broadcast_wdi_quota_reach_ind( + adapter->dev->ifindex, + uc_quota_ind->quota_bytes); + else + HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, + "Failed quota_reach_ind: NULL adapter"); + } else { + return QDF_STATUS_E_INVAL; + } + + return QDF_STATUS_SUCCESS; +} +#else +static QDF_STATUS hdd_ipa_uc_op_metering(hdd_context_t *hdd_ctx, + struct op_msg_type *op_msg) +{ + return QDF_STATUS_E_INVAL; +} +#endif + +/** * hdd_ipa_uc_op_cb() - IPA uC operation callback * @op_msg: operation message received from firmware * @usr_ctxt: user context registered with TL (we register the HDD Global @@ -1652,7 +1857,7 @@ static void hdd_ipa_uc_op_cb(struct op_msg_type *op_msg, void *usr_ctxt) hdd_ipa = (struct hdd_ipa_priv *)hdd_ctx->hdd_ipa; HDD_IPA_DP_LOG(QDF_TRACE_LEVEL_DEBUG, - "%s, OPCODE %s", __func__, op_string[msg->op_code]); + "OPCODE %s", op_string[msg->op_code]); if ((HDD_IPA_UC_OPCODE_TX_RESUME == msg->op_code) || (HDD_IPA_UC_OPCODE_RX_RESUME == msg->op_code)) { @@ -1885,7 +2090,7 @@ static void hdd_ipa_uc_op_cb(struct op_msg_type *op_msg, void *usr_ctxt) uc_fw_stat->rx_num_ind_drop_no_buf + uc_fw_stat->rx_num_pkts_indicated); qdf_mutex_release(&hdd_ipa->ipa_lock); - } else { + } else if (hdd_ipa_uc_op_metering(hdd_ctx, op_msg)) { HDD_IPA_LOG(LOGE, "Invalid message: op_code=%d, reason=%d", msg->op_code, hdd_ipa->stat_req_reason); } @@ -2044,11 +2249,164 @@ end: * Return: none */ static void hdd_ipa_init_uc_op_work(struct work_struct *work, - work_func_t work_handler) + work_func_t work_handler) { INIT_WORK(work, work_handler); } +#ifdef FEATURE_METERING +/** + * __hdd_ipa_wdi_meter_notifier_cb() - WLAN to IPA callback handler. + * IPA calls to get WLAN stats or set quota limit. + * @priv: pointer to private data registered with IPA (we register a + *» pointer to the global IPA context) + * @evt: the IPA event which triggered the callback + * @data: data associated with the event + * + * Return: None + */ +static void __hdd_ipa_wdi_meter_notifier_cb(enum ipa_wdi_meter_evt_type evt, + void *data) +{ + struct hdd_ipa_priv *hdd_ipa = ghdd_ipa; + hdd_adapter_t *adapter = NULL; + struct ipa_get_wdi_sap_stats *wdi_sap_stats; + struct ipa_set_wifi_quota *ipa_set_quota; + int ret = 0; + + if (wlan_hdd_validate_context(hdd_ipa->hdd_ctx)) + return; + + adapter = hdd_get_adapter(hdd_ipa->hdd_ctx, QDF_STA_MODE); + + HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO, "event=%d", evt); + + switch (evt) { + case IPA_GET_WDI_SAP_STATS: + /* fill-up ipa_get_wdi_sap_stats structure after getting + ipa_uc_fw_stats from FW */ + wdi_sap_stats = data; + + if (!adapter) { + HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, + "IPA uC share stats failed - no adapter"); + wdi_sap_stats->stats_valid = 0; + return; + } + + INIT_COMPLETION(hdd_ipa->ipa_uc_sharing_stats_comp); + INIT_COMPLETION(hdd_ipa->ipa_uc_set_quota_comp); + hdd_ipa_uc_sharing_stats_request(adapter, + wdi_sap_stats->reset_stats); + ret = wait_for_completion_timeout( + &hdd_ipa->ipa_uc_sharing_stats_comp, + msecs_to_jiffies(IPA_UC_SHARING_STATES_WAIT_TIME)); + if (!ret) { + HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, + "IPA uC share stats request timed out"); + wdi_sap_stats->stats_valid = 0; + } else { + wdi_sap_stats->stats_valid = 1; + + wdi_sap_stats->ipv4_rx_packets = + hdd_ipa->ipa_sharing_stats.ipv4_rx_packets; + wdi_sap_stats->ipv4_rx_bytes = + hdd_ipa->ipa_sharing_stats.ipv4_rx_bytes; + wdi_sap_stats->ipv6_rx_packets = + hdd_ipa->ipa_sharing_stats.ipv6_rx_packets; + wdi_sap_stats->ipv6_rx_bytes = + hdd_ipa->ipa_sharing_stats.ipv6_rx_bytes; + wdi_sap_stats->ipv4_tx_packets = + hdd_ipa->ipa_sharing_stats.ipv4_tx_packets; + wdi_sap_stats->ipv4_tx_bytes = + hdd_ipa->ipa_sharing_stats.ipv4_tx_bytes; + wdi_sap_stats->ipv6_tx_packets = + hdd_ipa->ipa_sharing_stats.ipv6_tx_packets; + wdi_sap_stats->ipv6_tx_bytes = + hdd_ipa->ipa_sharing_stats.ipv6_tx_bytes; + HDD_IPA_DP_LOG(QDF_TRACE_LEVEL_DEBUG, + "%s:%d,%llu,%llu,%llu,%llu,%llu,%llu,%llu,%llu", + "IPA_GET_WDI_SAP_STATS", + wdi_sap_stats->stats_valid, + wdi_sap_stats->ipv4_rx_packets, + wdi_sap_stats->ipv4_rx_bytes, + wdi_sap_stats->ipv6_rx_packets, + wdi_sap_stats->ipv6_rx_bytes, + wdi_sap_stats->ipv4_tx_packets, + wdi_sap_stats->ipv4_tx_bytes, + wdi_sap_stats->ipv6_tx_packets, + wdi_sap_stats->ipv6_tx_bytes); + } + break; + case IPA_SET_WIFI_QUOTA: + /* get ipa_set_wifi_quota structure from IPA and pass to FW + through quota_exceeded field in ipa_uc_fw_stats */ + ipa_set_quota = data; + + if (!adapter) { + HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, + "IPA uC set quota failed - no adapter"); + ipa_set_quota->set_valid = 0; + return; + } + + hdd_ipa_uc_set_quota(adapter, ipa_set_quota->set_quota, + ipa_set_quota->quota_bytes); + + ret = wait_for_completion_timeout( + &hdd_ipa->ipa_uc_set_quota_comp, + msecs_to_jiffies(IPA_UC_SET_QUOTA_WAIT_TIME)); + if (!ret) { + HDD_IPA_LOG(QDF_TRACE_LEVEL_ERROR, + "IPA uC set quota request timed out"); + ipa_set_quota->set_valid = 0; + } else { + ipa_set_quota->quota_bytes = + ((uint64_t)(hdd_ipa->ipa_quota_rsp.quota_hi) + <<32)|hdd_ipa->ipa_quota_rsp.quota_lo; + ipa_set_quota->set_valid = + hdd_ipa->ipa_quota_rsp.success; + } + + HDD_IPA_DP_LOG(QDF_TRACE_LEVEL_DEBUG, "SET_QUOTA: %llu, %d", + ipa_set_quota->quota_bytes, + ipa_set_quota->set_valid); + break; + } +} + +/** + * hdd_ipa_wdi_meter_notifier_cb() - WLAN to IPA callback handler. + * IPA calls to get WLAN stats or set quota limit. + * @priv: pointer to private data registered with IPA (we register a + *» pointer to the global IPA context) + * @evt: the IPA event which triggered the callback + * @data: data associated with the event + * + * Return: None + */ +static void hdd_ipa_wdi_meter_notifier_cb(enum ipa_wdi_meter_evt_type evt, + void *data) +{ + cds_ssr_protect(__func__); + __hdd_ipa_wdi_meter_notifier_cb(evt, data); + cds_ssr_unprotect(__func__); +} + +static void hdd_ipa_init_metering(struct hdd_ipa_priv *ipa_ctxt, + struct ipa_wdi_in_params *pipe_in) +{ + pipe_in->wdi_notify = hdd_ipa_wdi_meter_notifier_cb; + + init_completion(&ipa_ctxt->ipa_uc_sharing_stats_comp); + init_completion(&ipa_ctxt->ipa_uc_set_quota_comp); +} +#else +static void hdd_ipa_init_metering(struct hdd_ipa_priv *ipa_ctxt, + struct ipa_wdi_in_params *pipe_in) +{ +} +#endif /** * hdd_ipa_uc_ol_init() - Initialize IPA uC offload @@ -2105,12 +2463,14 @@ static QDF_STATUS hdd_ipa_uc_ol_init(hdd_context_t *hdd_ctx) /* Connect WDI IPA PIPE */ ipa_connect_wdi_pipe(&pipe_in, &pipe_out); + /* Micro Controller Doorbell register */ HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO_HIGH, "%s CONS DB pipe out 0x%x TX PIPE Handle 0x%x", __func__, (unsigned int)pipe_out.uc_door_bell_pa, ipa_ctxt->tx_pipe_handle); ipa_ctxt->tx_comp_doorbell_paddr = pipe_out.uc_door_bell_pa; + /* WLAN TX PIPE Handle */ ipa_ctxt->tx_pipe_handle = pipe_out.clnt_hdl; HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO_HIGH, @@ -2147,7 +2507,11 @@ static QDF_STATUS hdd_ipa_uc_ol_init(hdd_context_t *hdd_ctx) pipe_in.u.ul.rdy_ring_rp_pa = ipa_ctxt->ipa_resource.rx_proc_done_idx_paddr; HDD_IPA_WDI2_SET(pipe_in, ipa_ctxt); + + hdd_ipa_init_metering(ipa_ctxt, &pipe_in); + ipa_connect_wdi_pipe(&pipe_in, &pipe_out); + ipa_ctxt->rx_ready_doorbell_paddr = pipe_out.uc_door_bell_pa; ipa_ctxt->rx_pipe_handle = pipe_out.clnt_hdl; HDD_IPA_LOG(QDF_TRACE_LEVEL_INFO_HIGH, @@ -3131,7 +3495,7 @@ static enum hdd_ipa_forward_type hdd_ipa_intrabss_forward( } /** - * hdd_ipa_w2i_cb() - WLAN to IPA callback handler + * __hdd_ipa_w2i_cb() - WLAN to IPA callback handler * @priv: pointer to private data registered with IPA (we register a * pointer to the global IPA context) * @evt: the IPA event which triggered the callback diff --git a/core/wma/inc/wma.h b/core/wma/inc/wma.h index 64f8628e8eee..49e46047d0e5 100644 --- a/core/wma/inc/wma.h +++ b/core/wma/inc/wma.h @@ -1824,6 +1824,8 @@ typedef struct { * @WMA_VDEV_IBSS_PS_SET_1RX_CHAIN_IN_ATIM_WINDOW: set IBSS power save ATIM * @WMA_VDEV_DFS_CONTROL_CMDID: DFS control command * @WMA_VDEV_TXRX_GET_IPA_UC_FW_STATS_CMDID: get IPA microcontroller fw stats + * @WMA_VDEV_TXRX_GET_IPA_UC_SHARING_STATS_CMDID: get IPA uC wifi-sharing stats + * @WMA_VDEV_TXRX_SET_IPA_UC_QUOTA_CMDID: set IPA uC quota limit * * wma command ids for configuration request which * does not involve sending a wmi command. @@ -1843,6 +1845,8 @@ enum wma_cfg_cmd_id { WMA_VDEV_IBSS_PS_SET_1RX_CHAIN_IN_ATIM_WINDOW, WMA_VDEV_DFS_CONTROL_CMDID, WMA_VDEV_TXRX_GET_IPA_UC_FW_STATS_CMDID, + WMA_VDEV_TXRX_GET_IPA_UC_SHARING_STATS_CMDID, + WMA_VDEV_TXRX_SET_IPA_UC_QUOTA_CMDID, WMA_CMD_ID_MAX }; diff --git a/core/wma/src/wma_main.c b/core/wma/src/wma_main.c index 5031be19e043..2cb0cb82575b 100644 --- a/core/wma/src/wma_main.c +++ b/core/wma/src/wma_main.c @@ -752,6 +752,40 @@ static int32_t wma_set_priv_cfg(tp_wma_handle wma_handle, } break; + case WMA_VDEV_TXRX_GET_IPA_UC_SHARING_STATS_CMDID: + { + ol_txrx_pdev_handle pdev; + uint8_t reset_stats = privcmd->param_value; + + WMA_LOGE("%s: reset_stats=%d", + "WMA_VDEV_TXRX_GET_IPA_UC_SHARING_STATS_CMDID", + reset_stats); + pdev = cds_get_context(QDF_MODULE_ID_TXRX); + if (!pdev) { + WMA_LOGE("pdev NULL for uc stat"); + return -EINVAL; + } + ol_txrx_ipa_uc_get_share_stats(pdev, reset_stats); + } + break; + + case WMA_VDEV_TXRX_SET_IPA_UC_QUOTA_CMDID: + { + ol_txrx_pdev_handle pdev; + uint64_t quota_bytes = privcmd->param_value; + + WMA_LOGE("%s: quota_bytes=%llu", + "WMA_VDEV_TXRX_SET_IPA_UC_QUOTA_CMDID", + quota_bytes); + pdev = cds_get_context(QDF_MODULE_ID_TXRX); + if (!pdev) { + WMA_LOGE("pdev NULL for uc stat"); + return -EINVAL; + } + ol_txrx_ipa_uc_set_quota(pdev, quota_bytes); + } + break; + default: WMA_LOGE("Invalid wma config command id:%d", privcmd->param_id); ret = -EINVAL; diff --git a/target/inc/htt.h b/target/inc/htt.h index 731d1de68019..b264e096a05e 100644 --- a/target/inc/htt.h +++ b/target/inc/htt.h @@ -3879,11 +3879,14 @@ TEMPLATE_HTT_WDI_IPA_CONFIG_T(64, HTT_VAR_PADDR64_LE(tx_comp_ring_base_addr), #endif enum htt_wdi_ipa_op_code { - HTT_WDI_IPA_OPCODE_TX_SUSPEND = 0, - HTT_WDI_IPA_OPCODE_TX_RESUME = 1, - HTT_WDI_IPA_OPCODE_RX_SUSPEND = 2, - HTT_WDI_IPA_OPCODE_RX_RESUME = 3, - HTT_WDI_IPA_OPCODE_DBG_STATS = 4, + HTT_WDI_IPA_OPCODE_TX_SUSPEND = 0, + HTT_WDI_IPA_OPCODE_TX_RESUME = 1, + HTT_WDI_IPA_OPCODE_RX_SUSPEND = 2, + HTT_WDI_IPA_OPCODE_RX_RESUME = 3, + HTT_WDI_IPA_OPCODE_DBG_STATS = 4, + HTT_WDI_IPA_OPCODE_GET_SHARING_STATS = 5, + HTT_WDI_IPA_OPCODE_SET_QUOTA = 6, + HTT_WDI_IPA_OPCODE_IND_QUOTA = 7, /* keep this last */ HTT_WDI_IPA_OPCODE_MAX }; @@ -3896,7 +3899,7 @@ enum htt_wdi_ipa_op_code { * to either suspend or resume WDI_IPA TX or RX path. * |31 24|23 16|15 8|7 0| * |----------------+----------------+----------------+----------------| - * | op_code | Rsvd | msg_type | + * | op_code | Rsvd | msg_type | * |-------------------------------------------------------------------| * * Header fields: @@ -3913,12 +3916,12 @@ enum htt_wdi_ipa_op_code { PREPACK struct htt_wdi_ipa_op_request_t { /* DWORD 0: flags and meta-data */ A_UINT32 - msg_type:8, /* HTT_H2T_MSG_TYPE_WDI_IPA_OP_REQUEST */ + msg_type:8, /* HTT_H2T_MSG_TYPE_WDI_IPA_OP_REQ */ reserved:8, op_code:16; } POSTPACK; -#define HTT_WDI_IPA_OP_REQUEST_SZ 4 /* bytes */ +#define HTT_WDI_IPA_OP_REQUEST_SZ 4 #define HTT_WDI_IPA_OP_REQUEST_OP_CODE_M 0xffff0000 #define HTT_WDI_IPA_OP_REQUEST_OP_CODE_S 16 @@ -3932,6 +3935,122 @@ PREPACK struct htt_wdi_ipa_op_request_t { ((_var) |= ((_val) << HTT_WDI_IPA_OP_REQUEST_OP_CODE_S)); \ } while (0) +/** + * @brief WLAN_WDI_IPA_GET_SHARING_STATS_REQ + * |31 24|23 16|15 8|7 0| + * |----------------+----------------+----------------+----------------| + * | reserved | reset_stats | + * |-------------------------------------------------------------------| + * Header fields: + * - RESET_STATS + * Bits 7:0 + * Purpose: when 1, FW clears sharing stats + * - RESERVED + * Bits 31:8 + * Purpose: reserved bits + */ + +PREPACK struct htt_wdi_ipa_get_sharing_stats_t { + A_UINT32 + reset_stats:8, /* reset stat countis after response */ + reserved:24; +} POSTPACK; + +#define HTT_WDI_IPA_OP_REQ_GET_SHARING_STATS_SZ \ + (sizeof(struct htt_wdi_ipa_get_sharing_stats_t)) + +#define HTT_WDI_IPA_OP_REQ_GET_SHARING_STATS_RESET_STATS_M 0x000000ff +#define HTT_WDI_IPA_OP_REQ_GET_SHARING_STATS_RESET_STATS_S 0 +#define HTT_WDI_IPA_OP_REQ_GET_SHARING_STATS_RESET_STATS_GET(_var) \ + (((_var) & HTT_WDI_IPA_OP_REQ_GET_SHARING_STATS_RESET_STATS_M) >>\ + HTT_WDI_IPA_OP_REQ_GET_SHARING_STATS_RESET_STATS_S) +#define HTT_WDI_IPA_OP_REQ_GET_SHARING_STATS_RESET_STATS_SET(_var, _val)\ + do { \ + HTT_CHECK_SET_VAL( \ + HTT_WDI_IPA_OP_REQ_GET_SHARING_STATS_RESET_STATS,\ + _val); \ + ((_var) |= ((_val) << \ + HTT_WDI_IPA_OP_REQ_GET_SHARING_STATS_RESET_STATS_S)); \ + } while (0) + +/** + * @brief WLAN_WDI_IPA_SET_QUOTA_REQ + * + * |31 24|23 16|15 8|7 0| + * |----------------+----------------+----------------+----------------| + * | reserved | set_quota | + * |-------------------------------------------------------------------| + * | quota_lo | + * |-------------------------------------------------------------------| + * | quota_hi | + * |-------------------------------------------------------------------| + * Header fields: + * - set_quota + * Bits 7:0 + * Purpose: when 1, FW configures quota and starts quota monitoring. + * when 0, FW stops. + * - RESERVED + * Bits 31:8 + * Purpose: reserved bits + * - quota_lo + * Bits 31:0 + * Purpose: bytes of quota to be set, low 32-bit. + * It is accumulated number of bytes from when quota is configured. + * - quota_hi + * Bits 31:0 + * Purpose: bytes of quota to be set, high 32-bit + */ + +PREPACK struct htt_wdi_ipa_set_quota_t { + A_UINT32 + set_quota:8, /* enable quota monitoring */ + reserved:24; + A_UINT32 quota_lo; /* quota limit in bytes */ + A_UINT32 quota_hi; +} POSTPACK; + +#define HTT_WDI_IPA_OP_REQ_SET_QUOTA_SZ \ + (sizeof(struct htt_wdi_ipa_set_quota_t)) + +#define HTT_WDI_IPA_OP_REQ_SET_QUOTA_SET_QUOTA_M 0x000000ff +#define HTT_WDI_IPA_OP_REQ_SET_QUOTA_SET_QUOTA_S 0 +#define HTT_WDI_IPA_OP_REQ_SET_QUOTA_SET_QUOTA_GET(_var) \ + (((_var) & HTT_WDI_IPA_OP_REQ_SET_QUOTA_SET_QUOTA_M) >> \ + HTT_WDI_IPA_OP_REQ_SET_QUOTA_SET_QUOTA_S) +#define HTT_WDI_IPA_OP_REQ_SET_QUOTA_SET_QUOTA_SET(_var, _val) \ + do { \ + HTT_CHECK_SET_VAL(HTT_WDI_IPA_OP_REQ_SET_QUOTA_SET_QUOTA,\ + _val); \ + ((_var) |= ((_val) << \ + HTT_WDI_IPA_OP_REQ_SET_QUOTA_SET_QUOTA_S)); \ + } while (0) + +#define HTT_WDI_IPA_OP_REQ_SET_QUOTA_QUOTA_LO_M 0xffffffff +#define HTT_WDI_IPA_OP_REQ_SET_QUOTA_QUOTA_LO_S 0 +#define HTT_WDI_IPA_OP_REQ_SET_QUOTA_QUOTA_LO_GET(_var) \ + (((_var) & HTT_WDI_IPA_OP_REQ_SET_QUOTA_QUOTA_LO_M) >> \ + HTT_WDI_IPA_OP_REQ_SET_QUOTA_QUOTA_LO_S) +#define HTT_WDI_IPA_OP_REQ_SET_QUOTA_QUOTA_LO_SET(_var, _val) \ + do { \ + HTT_CHECK_SET_VAL(HTT_WDI_IPA_OP_REQ_SET_QUOTA_QUOTA_LO,\ + _val); \ + ((_var) |= ((_val) << \ + HTT_WDI_IPA_OP_REQ_SET_QUOTA_QUOTA_LO_S)); \ + } while (0) + +#define HTT_WDI_IPA_OP_REQ_SET_QUOTA_QUOTA_HI_M 0xffffffff +#define HTT_WDI_IPA_OP_REQ_SET_QUOTA_QUOTA_HI_S 0 +#define HTT_WDI_IPA_OP_REQ_SET_QUOTA_QUOTA_HI_GET(_var) \ + (((_var) & HTT_WDI_IPA_OP_REQ_SET_QUOTA_QUOTA_HI_M) >> \ + HTT_WDI_IPA_OP_REQ_SET_QUOTA_QUOTA_HI_S) +#define HTT_WDI_IPA_OP_REQ_SET_QUOTA_QUOTA_HI_SET(_var, _val) \ + do { \ + HTT_CHECK_SET_VAL(HTT_WDI_IPA_OP_REQ_SET_QUOTA_QUOTA_HI,\ + _val); \ + ((_var) |= ((_val) << \ + HTT_WDI_IPA_OP_REQ_SET_QUOTA_QUOTA_HI_S)); \ + } while (0) + /* * @brief host -> target HTT_SRING_SETUP message * @@ -8647,7 +8766,7 @@ enum htt_phy_mode { PREPACK struct htt_chan_change_t { /* DWORD 0: flags and meta-data */ - A_UINT32 msg_type:8, /* HTT_T2H_MSG_TYPE_WDI_IPA_OP_RESPONSE */ + A_UINT32 msg_type:8, /* HTT_T2H_MSG_TYPE_CHAN_CHANGE */ reserved1:24; A_UINT32 primary_chan_center_freq_mhz; A_UINT32 contig_chan1_center_freq_mhz; |
