From 70b68f807564aa47146551263a35ecee62fcca50 Mon Sep 17 00:00:00 2001 From: gaurank kathpalia Date: Thu, 16 Apr 2020 17:44:25 +0530 Subject: [PATCH] qcacld-3.0: MCL specific movement from cmn to cld of stats Move the MCL specific files related to cp stats from the cmn to cld folder. Change-Id: I00ce5d650780f9b74c64923c8392f06222e651a5 CRs-fixed: 2552458 --- Kbuild | 38 +- .../dispatcher/inc/wlan_cp_stats_mc_defs.h | 532 ++++++++++ .../dispatcher/inc/wlan_cp_stats_mc_tgt_api.h | 65 ++ .../inc/wlan_cp_stats_mc_ucfg_api.h | 289 ++++++ .../dispatcher/src/wlan_cp_stats_mc_tgt_api.c | 953 ++++++++++++++++++ .../src/wlan_cp_stats_mc_ucfg_api.c | 725 +++++++++++++ .../cp_stats/src/target_if_mc_cp_stats.c | 729 ++++++++++++++ .../cp_stats/src/wlan_cfg80211_mc_cp_stats.c | 734 ++++++++++++++ 8 files changed, 4046 insertions(+), 19 deletions(-) create mode 100644 components/cp_stats/dispatcher/inc/wlan_cp_stats_mc_defs.h create mode 100644 components/cp_stats/dispatcher/inc/wlan_cp_stats_mc_tgt_api.h create mode 100644 components/cp_stats/dispatcher/inc/wlan_cp_stats_mc_ucfg_api.h create mode 100644 components/cp_stats/dispatcher/src/wlan_cp_stats_mc_tgt_api.c create mode 100644 components/cp_stats/dispatcher/src/wlan_cp_stats_mc_ucfg_api.c create mode 100644 components/target_if/cp_stats/src/target_if_mc_cp_stats.c create mode 100644 os_if/cp_stats/src/wlan_cfg80211_mc_cp_stats.c diff --git a/Kbuild b/Kbuild index f1b24822a450..e97e12231d40 100644 --- a/Kbuild +++ b/Kbuild @@ -1655,24 +1655,27 @@ WIFI_POS_OBJS := $(WIFI_POS_CORE_DIR)/wifi_pos_api.o \ endif ###### CP STATS ######## -CP_STATS_OS_IF_SRC := $(WLAN_COMMON_ROOT)/os_if/linux/cp_stats/src -CP_STATS_TGT_SRC := $(WLAN_COMMON_ROOT)/target_if/cp_stats/src -CP_STATS_CORE_SRC := $(WLAN_COMMON_ROOT)/umac/cp_stats/core/src -CP_STATS_DISPATCHER_SRC := $(WLAN_COMMON_ROOT)/umac/cp_stats/dispatcher/src +CP_MC_STATS_OS_IF_SRC := os_if/cp_stats/src +CP_STATS_TGT_SRC := $(WLAN_COMMON_ROOT)/target_if/cp_stats/src +CP_STATS_CORE_SRC := $(WLAN_COMMON_ROOT)/umac/cp_stats/core/src +CP_STATS_DISPATCHER_SRC := $(WLAN_COMMON_ROOT)/umac/cp_stats/dispatcher/src +CP_MC_STATS_COMPONENT_SRC := components/cp_stats/dispatcher/src +CP_MC_STATS_COMPONENT_TGT_SRC := $(CLD_TARGET_IF_DIR)/cp_stats/src -CP_STATS_OS_IF_INC := -I$(WLAN_COMMON_INC)/os_if/linux/cp_stats/inc -CP_STATS_TGT_INC := -I$(WLAN_COMMON_INC)/target_if/cp_stats/inc -CP_STATS_DISPATCHER_INC := -I$(WLAN_COMMON_INC)/umac/cp_stats/dispatcher/inc +CP_STATS_OS_IF_INC := -I$(WLAN_COMMON_INC)/os_if/linux/cp_stats/inc +CP_STATS_TGT_INC := -I$(WLAN_COMMON_INC)/target_if/cp_stats/inc +CP_STATS_DISPATCHER_INC := -I$(WLAN_COMMON_INC)/umac/cp_stats/dispatcher/inc +CP_MC_STATS_COMPONENT_INC := -I$(WLAN_ROOT)/components/cp_stats/dispatcher/inc ifeq ($(CONFIG_CP_STATS), y) -CP_STATS_OBJS := $(CP_STATS_TGT_SRC)/target_if_mc_cp_stats.o \ - $(CP_STATS_CORE_SRC)/wlan_cp_stats_comp_handler.o \ - $(CP_STATS_CORE_SRC)/wlan_cp_stats_obj_mgr_handler.o \ - $(CP_STATS_CORE_SRC)/wlan_cp_stats_ol_api.o \ - $(CP_STATS_OS_IF_SRC)/wlan_cfg80211_mc_cp_stats.o \ - $(CP_STATS_DISPATCHER_SRC)/wlan_cp_stats_utils_api.o \ - $(CP_STATS_DISPATCHER_SRC)/wlan_cp_stats_mc_tgt_api.o \ - $(CP_STATS_DISPATCHER_SRC)/wlan_cp_stats_mc_ucfg_api.o +CP_STATS_OBJS := $(CP_MC_STATS_COMPONENT_SRC)/wlan_cp_stats_mc_tgt_api.o \ + $(CP_MC_STATS_COMPONENT_SRC)/wlan_cp_stats_mc_ucfg_api.o \ + $(CP_MC_STATS_COMPONENT_TGT_SRC)/target_if_mc_cp_stats.o \ + $(CP_STATS_CORE_SRC)/wlan_cp_stats_comp_handler.o \ + $(CP_STATS_CORE_SRC)/wlan_cp_stats_obj_mgr_handler.o \ + $(CP_STATS_CORE_SRC)/wlan_cp_stats_ol_api.o \ + $(CP_MC_STATS_OS_IF_SRC)/wlan_cfg80211_mc_cp_stats.o \ + $(CP_STATS_DISPATCHER_SRC)/wlan_cp_stats_utils_api.o endif ###### DCS ###### @@ -1691,9 +1694,6 @@ DCS_OBJS := $(DCS_TGT_IF_SRC)/target_if_dcs.o \ $(DCS_DISP_SRC)/wlan_dcs_tgt_api.o endif -###### COMPONENT CP STATS ######## -COMP_CP_STATS_DISPATCHER_INC := -I$(WLAN_ROOT)/components/cp_stats/dispatcher/inc - ###### INTEROP ISSUES AP ######## INTEROP_ISSUES_AP_OS_IF_SRC := os_if/interop_issues_ap/src INTEROP_ISSUES_AP_TGT_SRC := components/target_if/interop_issues_ap/src @@ -2136,7 +2136,7 @@ INCS += $(WIFI_POS_OS_IF_INC) INCS += $(CP_STATS_OS_IF_INC) INCS += $(CP_STATS_TGT_INC) INCS += $(CP_STATS_DISPATCHER_INC) -INCS += $(COMP_CP_STATS_DISPATCHER_INC) +INCS += $(CP_MC_STATS_COMPONENT_INC) ################ Dynamic ACS #################### INCS += $(DCS_TGT_IF_INC) INCS += $(DCS_DISP_INC) diff --git a/components/cp_stats/dispatcher/inc/wlan_cp_stats_mc_defs.h b/components/cp_stats/dispatcher/inc/wlan_cp_stats_mc_defs.h new file mode 100644 index 000000000000..1279ca895ec8 --- /dev/null +++ b/components/cp_stats/dispatcher/inc/wlan_cp_stats_mc_defs.h @@ -0,0 +1,532 @@ +/* + * Copyright (c) 2012-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_cp_stats_mc_defs.h + * + * This file provide definition for structure/enums/defines related to control + * path stats componenet + */ + +#ifndef __WLAN_CP_STATS_MC_DEFS_H__ +#define __WLAN_CP_STATS_MC_DEFS_H__ + +#include "wlan_cmn.h" +#include "qdf_event.h" + +#define MAX_NUM_CHAINS 2 + +#define MAX_MIB_STATS 1 + +#define IS_MSB_SET(__num) ((__num) & BIT(31)) +#define IS_LSB_SET(__num) ((__num) & BIT(0)) + +/** + * enum stats_req_type - enum indicating bit position of various stats type in + * request map + * @TYPE_CONNECTION_TX_POWER: tx power was requested + * @TYPE_STATION_STATS: station stats was requested + * @TYPE_PEER_STATS: peer stats was requested + * @TYPE_MIB_STATS: MIB stats was requested + */ +enum stats_req_type { + TYPE_CONNECTION_TX_POWER = 0, + TYPE_STATION_STATS, + TYPE_PEER_STATS, + TYPE_MIB_STATS, + TYPE_MAX, +}; + +/** + * enum tx_rate_info - tx rate flags + * @TX_RATE_LEGACY: Legacy rates + * @TX_RATE_HT20: HT20 rates + * @TX_RATE_HT40: HT40 rates + * @TX_RATE_SGI: Rate with Short guard interval + * @TX_RATE_LGI: Rate with Long guard interval + * @TX_RATE_VHT20: VHT 20 rates + * @TX_RATE_VHT40: VHT 40 rates + * @TX_RATE_VHT80: VHT 80 rates + * @TX_RATE_HE20: HE 20 rates + * @TX_RATE_HE40: HE 40 rates + * @TX_RATE_HE80: HE 80 rates + * @TX_RATE_HE160: HE 160 rates + * @TX_RATE_VHT160: VHT 160 rates + */ +enum tx_rate_info { + TX_RATE_LEGACY = 0x1, + TX_RATE_HT20 = 0x2, + TX_RATE_HT40 = 0x4, + TX_RATE_SGI = 0x8, + TX_RATE_LGI = 0x10, + TX_RATE_VHT20 = 0x20, + TX_RATE_VHT40 = 0x40, + TX_RATE_VHT80 = 0x80, + TX_RATE_HE20 = 0x100, + TX_RATE_HE40 = 0x200, + TX_RATE_HE80 = 0x400, + TX_RATE_HE160 = 0x800, + TX_RATE_VHT160 = 0x1000, +}; + +/** + * enum - txrate_gi + * @txrate_gi_0_8_US: guard interval 0.8 us + * @txrate_gi_0_4_US: guard interval 0.4 us for legacy + * @txrate_gi_1_6_US: guard interval 1.6 us + * @txrate_gi_3_2_US: guard interval 3.2 us + */ +enum txrate_gi { + TXRATE_GI_0_8_US = 0, + TXRATE_GI_0_4_US, + TXRATE_GI_1_6_US, + TXRATE_GI_3_2_US, +}; + +/** + * struct wake_lock_stats - wake lock stats structure + * @ucast_wake_up_count: Unicast wakeup count + * @bcast_wake_up_count: Broadcast wakeup count + * @ipv4_mcast_wake_up_count: ipv4 multicast wakeup count + * @ipv6_mcast_wake_up_count: ipv6 multicast wakeup count + * @ipv6_mcast_ra_stats: ipv6 multicast ra stats + * @ipv6_mcast_ns_stats: ipv6 multicast ns stats + * @ipv6_mcast_na_stats: ipv6 multicast na stats + * @icmpv4_count: ipv4 icmp packet count + * @icmpv6_count: ipv6 icmp packet count + * @rssi_breach_wake_up_count: rssi breach wakeup count + * @low_rssi_wake_up_count: low rssi wakeup count + * @gscan_wake_up_count: gscan wakeup count + * @pno_complete_wake_up_count: pno complete wakeup count + * @pno_match_wake_up_count: pno match wakeup count + * @oem_response_wake_up_count: oem response wakeup count + * @pwr_save_fail_detected: pwr save fail detected wakeup count + * @scan_11d 11d scan wakeup count + * @mgmt_assoc: association request management frame + * @mgmt_disassoc: disassociation management frame + * @mgmt_assoc_resp: association response management frame + * @mgmt_reassoc: reassociate request management frame + * @mgmt_reassoc_resp: reassociate response management frame + * @mgmt_auth: authentication managament frame + * @mgmt_deauth: deauthentication management frame + * @mgmt_action: action managament frame + */ +struct wake_lock_stats { + uint32_t ucast_wake_up_count; + uint32_t bcast_wake_up_count; + uint32_t ipv4_mcast_wake_up_count; + uint32_t ipv6_mcast_wake_up_count; + uint32_t ipv6_mcast_ra_stats; + uint32_t ipv6_mcast_ns_stats; + uint32_t ipv6_mcast_na_stats; + uint32_t icmpv4_count; + uint32_t icmpv6_count; + uint32_t rssi_breach_wake_up_count; + uint32_t low_rssi_wake_up_count; + uint32_t gscan_wake_up_count; + uint32_t pno_complete_wake_up_count; + uint32_t pno_match_wake_up_count; + uint32_t oem_response_wake_up_count; + uint32_t pwr_save_fail_detected; + uint32_t scan_11d; + uint32_t mgmt_assoc; + uint32_t mgmt_disassoc; + uint32_t mgmt_assoc_resp; + uint32_t mgmt_reassoc; + uint32_t mgmt_reassoc_resp; + uint32_t mgmt_auth; + uint32_t mgmt_deauth; + uint32_t mgmt_action; +}; + +struct stats_event; + +/** + * struct request_info: details of each request + * @cookie: identifier for os_if request + * @u: unified data type for callback to process tx power/peer rssi/ + * station stats/mib stats request when response comes. + * @vdev_id: vdev_id of request + * @pdev_id: pdev_id of request + * @peer_mac_addr: peer mac address + */ +struct request_info { + void *cookie; + union { + void (*get_tx_power_cb)(int tx_power, void *cookie); + void (*get_peer_rssi_cb)(struct stats_event *ev, void *cookie); + void (*get_station_stats_cb)(struct stats_event *ev, + void *cookie); + void (*get_mib_stats_cb)(struct stats_event *ev, + void *cookie); + } u; + uint32_t vdev_id; + uint32_t pdev_id; + uint8_t peer_mac_addr[QDF_MAC_ADDR_SIZE]; +}; + +/** + * struct pending_stats_requests: details of pending requests + * @type_map: map indicating type of outstanding requests + * @req: array of info for outstanding request of each type + */ +struct pending_stats_requests { + uint32_t type_map; + struct request_info req[TYPE_MAX]; +}; + +/** + * struct cca_stats - cca stats + * @congestion: the congestion percentage = (busy_time/total_time)*100 + * for the interval from when the vdev was started to the current time + * (or the time at which the vdev was stopped). + */ +struct cca_stats { + uint32_t congestion; +}; + +/** + * struct psoc_mc_cp_stats: psoc specific stats + * @is_cp_stats_suspended: is cp stats suspended or not + * @pending: details of pending requests + * @wow_unspecified_wake_up_count: number of non-wow related wake ups + * @wow_stats: wake_lock stats for vdev + */ +struct psoc_mc_cp_stats { + bool is_cp_stats_suspended; + struct pending_stats_requests pending; + uint32_t wow_unspecified_wake_up_count; + struct wake_lock_stats wow_stats; +}; + +/** + * struct pdev_mc_cp_stats: pdev specific stats + * @max_pwr: max tx power for vdev + */ +struct pdev_mc_cp_stats { + int32_t max_pwr; +}; + +/** + * struct summary_stats - summary stats + * @snr: snr of vdev + * @rssi: rssi of vdev + * @retry_cnt: retry count + * @multiple_retry_cnt: multiple_retry_cnt + * @tx_frm_cnt: num of tx frames + * @rx_frm_cnt: num of rx frames + * @frm_dup_cnt: duplicate frame count + * @fail_cnt: fail count + * @rts_fail_cnt: rts fail count + * @ack_fail_cnt: ack fail count + * @rts_succ_cnt: rts success count + * @rx_discard_cnt: rx frames discarded + * @rx_error_cnt: rx frames with error + */ +struct summary_stats { + uint32_t snr; + int8_t rssi; + uint32_t retry_cnt[4]; + uint32_t multiple_retry_cnt[4]; + uint32_t tx_frm_cnt[4]; + uint32_t rx_frm_cnt; + uint32_t frm_dup_cnt; + uint32_t fail_cnt[4]; + uint32_t rts_fail_cnt; + uint32_t ack_fail_cnt; + uint32_t rts_succ_cnt; + uint32_t rx_discard_cnt; + uint32_t rx_error_cnt; +}; + +/** + * struct vdev_mc_cp_stats - vdev specific stats + * @cca: cca stats + * @tx_rate_flags: tx rate flags (enum tx_rate_info) + * @chain_rssi: chain rssi + * @vdev_summary_stats: vdev's summary stats + */ +struct vdev_mc_cp_stats { + struct cca_stats cca; + uint32_t tx_rate_flags; + int8_t chain_rssi[MAX_NUM_CHAINS]; + struct summary_stats vdev_summary_stats; +}; + +/** + * struct peer_extd_stats - Peer extension statistics + * @peer_macaddr: peer MAC address + * @rx_duration: lower 32 bits of rx duration in microseconds + * @peer_tx_bytes: Total TX bytes (including dot11 header) sent to peer + * @peer_rx_bytes: Total RX bytes (including dot11 header) received from peer + * @last_tx_rate_code: last TX ratecode + * @last_tx_power: TX power used by peer - units are 0.5 dBm + * @rx_mc_bc_cnt: Total number of received multicast & broadcast data frames + * corresponding to this peer, 1 in the MSB of rx_mc_bc_cnt represents a + * valid data + */ +struct peer_extd_stats { + uint8_t peer_macaddr[QDF_MAC_ADDR_SIZE]; + uint32_t rx_duration; + uint32_t peer_tx_bytes; + uint32_t peer_rx_bytes; + uint32_t last_tx_rate_code; + int32_t last_tx_power; + uint32_t rx_mc_bc_cnt; +}; + +/** + * struct peer_mc_cp_stats - peer specific stats + * @tx_rate: tx rate + * @rx_rate: rx rate + * @peer_rssi: rssi + * @peer_macaddr: mac address + * @extd_stats: Pointer to peer extended stats + * @adv_stats: Pointer to peer adv (extd2) stats + */ +struct peer_mc_cp_stats { + uint32_t tx_rate; + uint32_t rx_rate; + int8_t peer_rssi; + uint8_t peer_macaddr[QDF_MAC_ADDR_SIZE]; + struct peer_extd_stats *extd_stats; + struct peer_adv_mc_cp_stats *adv_stats; +}; + +/** + * struct peer_adv_mc_cp_stats - peer specific adv stats + * @peer_macaddr: mac address + * @fcs_count: fcs count + * @rx_bytes: rx bytes + * @rx_count: rx count + */ +struct peer_adv_mc_cp_stats { + uint8_t peer_macaddr[QDF_MAC_ADDR_SIZE]; + uint32_t fcs_count; + uint32_t rx_count; + uint64_t rx_bytes; +}; + +#ifdef WLAN_FEATURE_MIB_STATS +/** + * struct dot11_counters - mib group containing attributes that are MAC counters + * @tx_frags: successfully transmitted fragments + * @group_tx_frames: transmitted group addressed frames + * @failed_cnt: MSDUs not transmitted successfully + * @rx_frags: fragments successfully received + * @group_rx_frames: group addressed frames received + * @fcs_error_cnt: FCS errors detected + * @tx_frames: frames successfully transmitted + */ +struct dot11_counters { + uint32_t tx_frags; + uint32_t group_tx_frames; + uint32_t failed_cnt; + uint32_t rx_frags; + uint32_t group_rx_frames; + uint32_t fcs_error_cnt; + uint32_t tx_frames; +}; + +/** + * struct dot11_mac_statistics - mib stats information on the operation of MAC + * @retry_cnt: retries done by mac for successful transmition + * @multi_retry_cnt: multiple retries done before successful transmition + * @frame_dup_cnt: duplicate no of frames + * @rts_success_cnt: number of CTS received (in response to RTS) + * @rts_fail_cnt: number of CTS not received (in response to RTS) + * @tx_ack_fail_cnt: number of ACK not received + */ +struct dot11_mac_statistics { + uint32_t retry_cnt; + uint32_t multi_retry_cnt; + uint32_t frame_dup_cnt; + uint32_t rts_success_cnt; + uint32_t rts_fail_cnt; + uint32_t tx_ack_fail_cnt; +}; + +/** + * dot11_qos_counters - qos mac counters + * @qos_tx_frag_cnt: transmitted QoS fragments + * @qos_failed_cnt: failed Qos fragments + * @qos_retry_cnt: Qos frames transmitted after retransmissions + * @qos_multi_retry_cnt: Qos frames transmitted after more than + * one retransmissions + * @qos_frame_dup_cnt: duplicate frames + * @qos_rts_success_cnt: number of CTS received (in response to RTS) + * @qos_rts_fail_cnt: number of CTS not received (in response to RTS) + * @tx_qos_ack_fail_cnt_up: number of ACK not received + * (in response to Qos frame) + * @qos_rx_frag_cnt: number of received MPDU of type Data + * @qos_tx_frame_cnt: number of transmitted MPDU of type Data + * @qos_discarded_frame_cnt: total Discarded MSDUs + * @qos_mpdu_rx_cnt: total received MPDU + * @qos_retries_rx_cnt: received MPDU with retry bit equal to 1 + */ +struct dot11_qos_counters { + uint32_t qos_tx_frag_cnt; + uint32_t qos_failed_cnt; + uint32_t qos_retry_cnt; + uint32_t qos_multi_retry_cnt; + uint32_t qos_frame_dup_cnt; + uint32_t qos_rts_success_cnt; + uint32_t qos_rts_fail_cnt; + uint32_t tx_qos_ack_fail_cnt_up; + uint32_t qos_rx_frag_cnt; + uint32_t qos_tx_frame_cnt; + uint32_t qos_discarded_frame_cnt; + uint32_t qos_mpdu_rx_cnt; + uint32_t qos_retries_rx_cnt; +}; + +/** + * dot11_rsna_stats - mib rsn stats + * @rm_ccmp_replays: received robust management CCMP MPDUs discarded + * by the replay mechanism + * @tkip_icv_err: TKIP ICV errors encountered + * @tkip_replays: TKIP replay errors detected + * @ccmp_decrypt_err: MPDUs discarded by the CCMP decryption algorithm + * @ccmp_replays: received CCMP MPDUs discarded by the replay mechanism + * @cmac_icv_err: MPDUs discarded by the CMAC integrity check algorithm + * @cmac_replays: MPDUs discarded by the CMAC replay errors + */ +struct dot11_rsna_stats { + uint32_t rm_ccmp_replays; + uint32_t tkip_icv_err; + uint32_t tkip_replays; + uint32_t ccmp_decrypt_err; + uint32_t ccmp_replays; + uint32_t cmac_icv_err; + uint32_t cmac_replays; +}; + +/** + * dot11_counters_group3 - dot11 group3 stats + * @tx_ampdu_cnt: transmitted AMPDUs + * @tx_mpdus_in_ampdu_cnt: number of MPDUs in the A-MPDU in transmitted AMPDUs + * @tx_octets_in_ampdu_cnt: octets in the transmitted A-MPDUs + * @ampdu_rx_cnt: received A-MPDU + * @mpdu_in_rx_ampdu_cnt: MPDUs received in the A-MPDU + * @rx_octets_in_ampdu_cnt: octets in the received A-MPDU + * @rx_ampdu_deli_crc_err_cnt: number of MPDUs delimiter with CRC error + */ +struct dot11_counters_group3 { + uint32_t tx_ampdu_cnt; + uint32_t tx_mpdus_in_ampdu_cnt; + uint64_t tx_octets_in_ampdu_cnt; + uint32_t ampdu_rx_cnt; + uint32_t mpdu_in_rx_ampdu_cnt; + uint64_t rx_octets_in_ampdu_cnt; + uint32_t rx_ampdu_deli_crc_err_cnt; +}; + +/** + * mib_stats_metrics - mib stats counters + * @mib_counters: dot11Counters group + * @mib_mac_statistics: dot11MACStatistics group + * @mib_qos_counters: dot11QoSCounters group + * @mib_rsna_stats: dot11RSNAStats group + * @mib_counters_group3: dot11CountersGroup3 group + */ +struct mib_stats_metrics { + struct dot11_counters mib_counters; + struct dot11_mac_statistics mib_mac_statistics; + struct dot11_qos_counters mib_qos_counters; + struct dot11_rsna_stats mib_rsna_stats; + struct dot11_counters_group3 mib_counters_group3; +}; +#endif + +/** + * struct congestion_stats_event: congestion stats event param + * @vdev_id: vdev_id of the event + * @congestion: the congestion percentage + */ +struct congestion_stats_event { + uint8_t vdev_id; + uint32_t congestion; +}; + +/** + * struct summary_stats_event - summary_stats event param + * @vdev_id: vdev_id of the event + * @stats: summary stats + */ +struct summary_stats_event { + uint8_t vdev_id; + struct summary_stats stats; +}; + +/** + * struct chain_rssi_event - chain_rssi event param + * @vdev_id: vdev_id of the event + * @chain_rssi: chain_rssi + */ +struct chain_rssi_event { + uint8_t vdev_id; + int8_t chain_rssi[MAX_NUM_CHAINS]; +}; + +/** + * struct stats_event - parameters populated by stats event + * @num_pdev_stats: num pdev stats + * @pdev_stats: if populated array indicating pdev stats (index = pdev_id) + * @num_peer_stats: num peer stats + * @peer_stats: if populated array indicating peer stats + * @peer_adv_stats: if populated, indicates peer adv (extd2) stats + * @num_peer_adv_stats: number of peer adv (extd2) stats + * @num_peer_extd_stats: Num peer extended stats + * @peer_extended_stats: Peer extended stats + * @cca_stats: if populated indicates congestion stats + * @num_summary_stats: number of summary stats + * @vdev_summary_stats: if populated indicates array of summary stats per vdev + * @num_mib_stats: number of mib stats + * @mib_stats: if populated indicates array of mib stats per vdev + * @num_chain_rssi_stats: number of chain rssi stats + * @vdev_chain_rssi: if populated indicates array of chain rssi per vdev + * @tx_rate: tx rate (kbps) + * @tx_rate_flags: tx rate flags, (enum tx_rate_info) + * @last_event: The LSB indicates if the event is the last event or not and the + * MSB indicates if this feature is supported by FW or not. + */ +struct stats_event { + uint32_t num_pdev_stats; + struct pdev_mc_cp_stats *pdev_stats; + uint32_t num_peer_stats; + struct peer_mc_cp_stats *peer_stats; + uint32_t num_peer_adv_stats; + struct peer_adv_mc_cp_stats *peer_adv_stats; + uint32_t num_peer_extd_stats; + struct peer_extd_stats *peer_extended_stats; + struct congestion_stats_event *cca_stats; + uint32_t num_summary_stats; + struct summary_stats_event *vdev_summary_stats; +#ifdef WLAN_FEATURE_MIB_STATS + uint32_t num_mib_stats; + struct mib_stats_metrics *mib_stats; +#endif + uint32_t num_chain_rssi_stats; + struct chain_rssi_event *vdev_chain_rssi; + uint32_t tx_rate; + uint32_t rx_rate; + enum tx_rate_info tx_rate_flags; + uint32_t last_event; +}; + +#endif /* __WLAN_CP_STATS_MC_DEFS_H__ */ diff --git a/components/cp_stats/dispatcher/inc/wlan_cp_stats_mc_tgt_api.h b/components/cp_stats/dispatcher/inc/wlan_cp_stats_mc_tgt_api.h new file mode 100644 index 000000000000..651314ad31a8 --- /dev/null +++ b/components/cp_stats/dispatcher/inc/wlan_cp_stats_mc_tgt_api.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2018, 2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_cp_stats_mc_tgt_api.h + * + * This header file provide with API declarations to interface with Southbound + */ +#ifndef __WLAN_CP_STATS_MC_TGT_API_H__ +#define __WLAN_CP_STATS_MC_TGT_API_H__ + +#ifdef QCA_SUPPORT_CP_STATS +#include "wlan_cp_stats_mc_defs.h" + +/** + * tgt_mc_cp_stats_process_stats_event(): API to process stats event + * @psoc: pointer to psoc object + * @event: event parameters + * + * Return: status of operation + */ +QDF_STATUS tgt_mc_cp_stats_process_stats_event(struct wlan_objmgr_psoc *psoc, + struct stats_event *event); + +/** + * tgt_send_mc_cp_stats_req(): API to send stats request to lmac + * @psoc: pointer to psoc object + * + * Return: status of operation + */ +QDF_STATUS tgt_send_mc_cp_stats_req(struct wlan_objmgr_psoc *psoc, + enum stats_req_type type, + struct request_info *req); + +/** + * tgt_mc_cp_stats_inc_wake_lock_stats() : API to increment wake lock stats + * given the wake reason code + * @psoc: pointer to psoc object + * @reason: wake reason + * @stats: vdev wow stats to update + * @unspecified_wake_count: unspecified wake count to update + * + * Return : status of operation + */ +QDF_STATUS tgt_mc_cp_stats_inc_wake_lock_stats(struct wlan_objmgr_psoc *psoc, + uint32_t reason, struct wake_lock_stats *stats, + uint32_t *unspecified_wake_count); + +#endif /* QCA_SUPPORT_CP_STATS */ +#endif /* __WLAN_CP_STATS_MC_TGT_API_H__ */ diff --git a/components/cp_stats/dispatcher/inc/wlan_cp_stats_mc_ucfg_api.h b/components/cp_stats/dispatcher/inc/wlan_cp_stats_mc_ucfg_api.h new file mode 100644 index 000000000000..9420168ce09d --- /dev/null +++ b/components/cp_stats/dispatcher/inc/wlan_cp_stats_mc_ucfg_api.h @@ -0,0 +1,289 @@ +/* + * Copyright (c) 2018-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_cp_stats_mc_ucfg_api.h + * + * This header file maintain API declaration required for northbound interaction + */ + +#ifndef __WLAN_CP_STATS_MC_UCFG_API_H__ +#define __WLAN_CP_STATS_MC_UCFG_API_H__ + +#ifdef QCA_SUPPORT_CP_STATS + +#include +#include +#include + +struct psoc_cp_stats; +struct vdev_cp_stats; + +/** + * ucfg_mc_cp_stats_get_psoc_wake_lock_stats() : API to get wake lock stats from + * psoc + * @psoc: pointer to psoc object + * @stats: stats object to populate + * + * Return : status of operation + */ +QDF_STATUS ucfg_mc_cp_stats_get_psoc_wake_lock_stats( + struct wlan_objmgr_psoc *psoc, + struct wake_lock_stats *stats); + +/** + * ucfg_mc_cp_stats_get_vdev_wake_lock_stats() : API to get wake lock stats from + * vdev + * @vdev: pointer to vdev object + * @stats: stats object to populate + * + * Return : status of operation + */ +QDF_STATUS ucfg_mc_cp_stats_get_vdev_wake_lock_stats( + struct wlan_objmgr_vdev *vdev, + struct wake_lock_stats *stats); + +/** + * ucfg_mc_cp_stats_inc_wake_lock_stats_by_protocol() : API to increment wake + * lock stats given the protocol of the packet that was received. + * @psoc: pointer to psoc object + * @vdev_id: vdev_id for which the packet was received + * @protocol: protocol of the packet that was received + * + * Return : status of operation + */ +QDF_STATUS ucfg_mc_cp_stats_inc_wake_lock_stats_by_protocol( + struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id, + enum qdf_proto_subtype protocol); + +/** + * ucfg_mc_cp_stats_inc_wake_lock_stats_by_protocol() : API to increment wake + * lock stats given destnation of packet that was received. + * @psoc: pointer to psoc object + * @dest_mac: destinamtion mac address of packet that was received + * + * Return : status of operation + */ +QDF_STATUS ucfg_mc_cp_stats_inc_wake_lock_stats_by_dst_addr( + struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id, uint8_t *dest_mac); + +/** + * ucfg_mc_cp_stats_inc_wake_lock_stats() : API to increment wake lock stats + * given wake reason. + * @psoc: pointer to psoc object + * @vdev_id: vdev_id on with WOW was received + * @reason: reason of WOW + * + * Return : status of operation + */ +QDF_STATUS ucfg_mc_cp_stats_inc_wake_lock_stats(struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id, + uint32_t reason); + +/** + * ucfg_mc_cp_stats_write_wow_stats() - Writes WOW stats to buffer + * @psoc: pointer to psoc object + * @buffer: The char buffer to write to + * @max_len: The maximum number of chars to write + * @ret: number of bytes written + * + * Return: status of operation + */ +QDF_STATUS ucfg_mc_cp_stats_write_wow_stats( + struct wlan_objmgr_psoc *psoc, + char *buffer, uint16_t max_len, int *ret); + +/** + * ucfg_mc_cp_stats_send_tx_power_request() - API to send tx_power request to + * lmac + * @vdev: pointer to vdev object + * @type: request type + * + * Return: status of operation + */ +QDF_STATUS ucfg_mc_cp_stats_send_stats_request(struct wlan_objmgr_vdev *vdev, + enum stats_req_type type, + struct request_info *info); + +/** + * ucfg_mc_cp_stats_get_tx_power() - API to fetch tx_power + * @vdev: pointer to vdev object + * @dbm: pointer to tx power in dbm + * + * Return: status of operation + */ +QDF_STATUS ucfg_mc_cp_stats_get_tx_power(struct wlan_objmgr_vdev *vdev, + int *dbm); + +/** + * ucfg_mc_cp_stats_is_req_pending() - API to tell if given request is pending + * @psoc: pointer to psoc object + * @type: request type to check + * + * Return: true of request is pending, false otherwise + */ +bool ucfg_mc_cp_stats_is_req_pending(struct wlan_objmgr_psoc *psoc, + enum stats_req_type type); + +/** + * ucfg_mc_cp_stats_set_pending_req() - API to set pending request + * @psoc: pointer to psoc object + * @type: request to update + * @req: value to update + * + * Return: status of operation + */ +QDF_STATUS ucfg_mc_cp_stats_set_pending_req(struct wlan_objmgr_psoc *psoc, + enum stats_req_type type, + struct request_info *req); + +/** + * ucfg_mc_cp_stats_reset_pending_req() - API to reset pending request + * @psoc: pointer to psoc object + * @type: request to update + * @last_req: last request + * @pending: pending request present + * + * The function is an atomic operation of "reset" and "get" last request. + * + * Return: status of operation + */ +QDF_STATUS ucfg_mc_cp_stats_reset_pending_req(struct wlan_objmgr_psoc *psoc, + enum stats_req_type type, + struct request_info *last_req, + bool *pending); + +/** + * ucfg_mc_cp_stats_get_pending_req() - API to get pending request + * @psoc: pointer to psoc object + * @type: request to update + * @info: buffer to populate + * + * Return: status of operation + */ +QDF_STATUS ucfg_mc_cp_stats_get_pending_req(struct wlan_objmgr_psoc *psoc, + enum stats_req_type type, + struct request_info *info); + +/** + * ucfg_mc_cp_stats_free_stats_resources() - API to free buffers within stats_event + * structure + * @ev: strcture whose buffer are to freed + * + * Return: none + */ +void ucfg_mc_cp_stats_free_stats_resources(struct stats_event *ev); + +/** + * ucfg_mc_cp_stats_cca_stats_get() - API to fetch cca stats + * @vdev: pointer to vdev object + * @cca_stats: pointer to cca info + * + * Return: status of operation + */ +QDF_STATUS ucfg_mc_cp_stats_cca_stats_get(struct wlan_objmgr_vdev *vdev, + struct cca_stats *cca_stats); + +/** + * ucfg_mc_cp_stats_set_rate_flags() - API to set rate flags + * @vdev: pointer to vdev object + * @flags: value to set (enum tx_rate_info) + * + * Return: status of operation + */ +QDF_STATUS ucfg_mc_cp_stats_set_rate_flags(struct wlan_objmgr_vdev *vdev, + enum tx_rate_info flags); + +/** + * ucfg_mc_cp_stats_register_lost_link_info_cb() - API to register lost link + * info callback + * @psoc: pointer to psoc object + * @lost_link_cp_stats_info_cb: Lost link info callback to be registered + * + */ +void ucfg_mc_cp_stats_register_lost_link_info_cb( + struct wlan_objmgr_psoc *psoc, + void (*lost_link_cp_stats_info_cb)(void *stats_ev)); + +#ifdef WLAN_POWER_MANAGEMENT_OFFLOAD +/** + * ucfg_mc_cp_stats_register_pmo_handler() - API to register pmo handler + * + * Return: none + */ +void ucfg_mc_cp_stats_register_pmo_handler(void); +#else +void static inline ucfg_mc_cp_stats_register_pmo_handler(void) { }; +#endif /* WLAN_POWER_MANAGEMENT_OFFLOAD */ +#else +void static inline ucfg_mc_cp_stats_register_pmo_handler(void) { }; +static inline QDF_STATUS ucfg_mc_cp_stats_send_stats_request( + struct wlan_objmgr_vdev *vdev, + enum stats_req_type type, + struct request_info *info) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS ucfg_mc_cp_stats_set_rate_flags( + struct wlan_objmgr_vdev *vdev, + enum tx_rate_info flags) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS ucfg_mc_cp_stats_get_psoc_wake_lock_stats( + struct wlan_objmgr_psoc *psoc, + struct wake_lock_stats *stats) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS ucfg_mc_cp_stats_inc_wake_lock_stats_by_protocol( + struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id, + enum qdf_proto_subtype protocol) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS ucfg_mc_cp_stats_inc_wake_lock_stats( + struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id, + uint32_t reason) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS ucfg_mc_cp_stats_inc_wake_lock_stats_by_dst_addr( + struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id, uint8_t *dest_mac) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS ucfg_mc_cp_stats_get_vdev_wake_lock_stats( + struct wlan_objmgr_vdev *vdev, + struct wake_lock_stats *stats) +{ + return QDF_STATUS_SUCCESS; +} +#endif /* QCA_SUPPORT_CP_STATS */ +#endif /* __WLAN_CP_STATS_MC_UCFG_API_H__ */ diff --git a/components/cp_stats/dispatcher/src/wlan_cp_stats_mc_tgt_api.c b/components/cp_stats/dispatcher/src/wlan_cp_stats_mc_tgt_api.c new file mode 100644 index 000000000000..7b0606a4bef3 --- /dev/null +++ b/components/cp_stats/dispatcher/src/wlan_cp_stats_mc_tgt_api.c @@ -0,0 +1,953 @@ +/* + * Copyright (c) 2018-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC:wlan_cp_stats_mc_tgt_api.c + * + * This file provide API definitions to update control plane statistics received + * from southbound interface + */ + +#include "wlan_cp_stats_mc_defs.h" +#include "target_if_cp_stats.h" +#include "wlan_cp_stats_tgt_api.h" +#include "wlan_cp_stats_mc_tgt_api.h" +#include +#include +#include "../../core/src/wlan_cp_stats_defs.h" + +static bool tgt_mc_cp_stats_is_last_event(struct stats_event *ev, + enum stats_req_type stats_type) +{ + bool is_last_event; + + if (IS_MSB_SET(ev->last_event)) { + is_last_event = IS_LSB_SET(ev->last_event); + } else { + if (stats_type == TYPE_CONNECTION_TX_POWER) + is_last_event = true; + else + is_last_event = !!ev->peer_stats; + } + + if (is_last_event) + cp_stats_debug("Last stats event"); + + return is_last_event; +} + +void tgt_cp_stats_register_rx_ops(struct wlan_lmac_if_rx_ops *rx_ops) +{ + rx_ops->cp_stats_rx_ops.process_stats_event = + tgt_mc_cp_stats_process_stats_event; +} + +static void tgt_mc_cp_stats_extract_tx_power(struct wlan_objmgr_psoc *psoc, + struct stats_event *ev, + bool is_station_stats) +{ + int32_t max_pwr; + uint8_t pdev_id; + QDF_STATUS status; + struct wlan_objmgr_pdev *pdev; + struct request_info last_req = {0}; + struct wlan_objmgr_vdev *vdev = NULL; + struct pdev_mc_cp_stats *pdev_mc_stats; + struct pdev_cp_stats *pdev_cp_stats_priv; + bool pending = false; + + if (!ev->pdev_stats) + return; + + if (is_station_stats) + status = ucfg_mc_cp_stats_get_pending_req(psoc, + TYPE_STATION_STATS, &last_req); + else + status = ucfg_mc_cp_stats_get_pending_req(psoc, + TYPE_CONNECTION_TX_POWER, &last_req); + + if (QDF_IS_STATUS_ERROR(status)) { + cp_stats_err("ucfg_mc_cp_stats_get_pending_req failed"); + goto end; + } + + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, last_req.vdev_id, + WLAN_CP_STATS_ID); + if (!vdev) { + cp_stats_err("vdev is null"); + goto end; + } + + pdev = wlan_vdev_get_pdev(vdev); + if (!pdev) { + cp_stats_err("pdev is null"); + goto end; + } + + pdev_id = wlan_objmgr_pdev_get_pdev_id(pdev); + if (pdev_id >= ev->num_pdev_stats) { + cp_stats_err("pdev_id: %d invalid", pdev_id); + goto end; + } + + pdev_cp_stats_priv = wlan_cp_stats_get_pdev_stats_obj(pdev); + if (!pdev_cp_stats_priv) { + cp_stats_err("pdev_cp_stats_priv is null"); + goto end; + } + + wlan_cp_stats_pdev_obj_lock(pdev_cp_stats_priv); + pdev_mc_stats = pdev_cp_stats_priv->pdev_stats; + max_pwr = pdev_mc_stats->max_pwr = ev->pdev_stats[pdev_id].max_pwr; + wlan_cp_stats_pdev_obj_unlock(pdev_cp_stats_priv); + if (is_station_stats) + goto end; + + if (tgt_mc_cp_stats_is_last_event(ev, TYPE_CONNECTION_TX_POWER)) { + ucfg_mc_cp_stats_reset_pending_req(psoc, + TYPE_CONNECTION_TX_POWER, + &last_req, + &pending); + if (last_req.u.get_tx_power_cb && pending) + last_req.u.get_tx_power_cb(max_pwr, last_req.cookie); + } +end: + if (vdev) + wlan_objmgr_vdev_release_ref(vdev, WLAN_CP_STATS_ID); +} + +static void peer_rssi_iterator(struct wlan_objmgr_pdev *pdev, + void *peer, void *arg) +{ + struct stats_event *ev; + struct peer_mc_cp_stats *peer_mc_stats; + struct peer_cp_stats *peer_cp_stats_priv; + struct peer_extd_stats *peer_extd_mc_stats; + + if (WLAN_PEER_SELF == wlan_peer_get_peer_type(peer)) { + cp_stats_debug("ignore self peer: %pM", + wlan_peer_get_macaddr(peer)); + return; + } + + peer_cp_stats_priv = wlan_cp_stats_get_peer_stats_obj(peer); + if (!peer_cp_stats_priv) { + cp_stats_err("peer cp stats object is null"); + return; + } + + wlan_cp_stats_peer_obj_lock(peer_cp_stats_priv); + peer_mc_stats = peer_cp_stats_priv->peer_stats; + ev = arg; + ev->peer_stats[ev->num_peer_stats] = *peer_mc_stats; + ev->num_peer_stats++; + + peer_extd_mc_stats = peer_mc_stats->extd_stats; + ev->peer_extended_stats[ev->num_peer_extd_stats] = *peer_extd_mc_stats; + ev->num_peer_extd_stats++; + wlan_cp_stats_peer_obj_unlock(peer_cp_stats_priv); +} + +static void +tgt_mc_cp_stats_prepare_raw_peer_rssi(struct wlan_objmgr_psoc *psoc, + struct request_info *last_req) +{ + uint8_t *mac_addr; + uint16_t peer_count; + struct stats_event ev = {0}; + struct wlan_objmgr_pdev *pdev; + struct wlan_objmgr_vdev *vdev; + struct wlan_objmgr_peer *peer = NULL; + struct peer_mc_cp_stats *peer_mc_stats; + struct peer_extd_stats *peer_mc_extd_stats; + struct peer_cp_stats *peer_cp_stats_priv; + void (*get_peer_rssi_cb)(struct stats_event *ev, void *cookie); + + get_peer_rssi_cb = last_req->u.get_peer_rssi_cb; + if (!get_peer_rssi_cb) { + cp_stats_err("get_peer_rssi_cb is null"); + return; + } + + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, last_req->vdev_id, + WLAN_CP_STATS_ID); + if (!vdev) { + cp_stats_err("vdev is null"); + goto end; + } + + mac_addr = last_req->peer_mac_addr; + if (QDF_IS_ADDR_BROADCAST(mac_addr)) { + pdev = wlan_vdev_get_pdev(vdev); + peer_count = wlan_pdev_get_peer_count(pdev); + ev.peer_stats = qdf_mem_malloc(sizeof(*ev.peer_stats) * + peer_count); + if (!ev.peer_stats) + goto end; + + ev.peer_extended_stats = + qdf_mem_malloc(sizeof(*ev.peer_extended_stats) * + peer_count); + if (!ev.peer_extended_stats) + goto end; + + wlan_objmgr_pdev_iterate_obj_list(pdev, WLAN_PEER_OP, + peer_rssi_iterator, &ev, + true, WLAN_CP_STATS_ID); + } else { + peer = wlan_objmgr_get_peer(psoc, last_req->pdev_id, + mac_addr, WLAN_CP_STATS_ID); + if (!peer) { + cp_stats_err("peer[%pM] is null", mac_addr); + goto end; + } + + peer_cp_stats_priv = wlan_cp_stats_get_peer_stats_obj(peer); + if (!peer_cp_stats_priv) { + cp_stats_err("peer cp stats object is null"); + goto end; + } + + ev.peer_stats = qdf_mem_malloc(sizeof(*ev.peer_stats)); + if (!ev.peer_stats) + goto end; + + ev.num_peer_stats = 1; + + ev.peer_extended_stats = + qdf_mem_malloc(sizeof(*ev.peer_extended_stats)); + if (!ev.peer_extended_stats) + goto end; + + ev.num_peer_extd_stats = 1; + + wlan_cp_stats_peer_obj_lock(peer_cp_stats_priv); + peer_mc_stats = peer_cp_stats_priv->peer_stats; + *ev.peer_stats = *peer_mc_stats; + + peer_mc_extd_stats = peer_mc_stats->extd_stats; + *ev.peer_extended_stats = *peer_mc_extd_stats; + wlan_cp_stats_peer_obj_unlock(peer_cp_stats_priv); + } + +end: + if (ev.peer_stats) + get_peer_rssi_cb(&ev, last_req->cookie); + + ucfg_mc_cp_stats_free_stats_resources(&ev); + + if (vdev) + wlan_objmgr_vdev_release_ref(vdev, WLAN_CP_STATS_ID); + if (peer) + wlan_objmgr_peer_release_ref(peer, WLAN_CP_STATS_ID); +} + +static QDF_STATUS +tgt_mc_cp_stats_update_peer_adv_stats(struct wlan_objmgr_psoc *psoc, + struct peer_adv_mc_cp_stats + *peer_adv_stats, uint32_t size) +{ + uint8_t *peer_mac_addr; + struct wlan_objmgr_peer *peer; + struct peer_mc_cp_stats *peer_mc_stats; + struct peer_adv_mc_cp_stats *peer_adv_mc_stats; + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct peer_cp_stats *peer_cp_stats_priv; + + if (!peer_adv_stats) + return QDF_STATUS_E_INVAL; + + peer_mac_addr = peer_adv_stats->peer_macaddr; + peer = wlan_objmgr_get_peer_by_mac(psoc, peer_mac_addr, + WLAN_CP_STATS_ID); + if (!peer) { + cp_stats_debug("peer is null"); + return QDF_STATUS_E_EXISTS; + } + peer_cp_stats_priv = wlan_cp_stats_get_peer_stats_obj(peer); + if (!peer_cp_stats_priv) { + cp_stats_err("peer_cp_stats_priv is null"); + status = QDF_STATUS_E_EXISTS; + goto end; + } + wlan_cp_stats_peer_obj_lock(peer_cp_stats_priv); + peer_mc_stats = peer_cp_stats_priv->peer_stats; + peer_adv_mc_stats = peer_mc_stats->adv_stats; + + qdf_mem_copy(peer_adv_mc_stats->peer_macaddr, + peer_adv_stats->peer_macaddr, + QDF_MAC_ADDR_SIZE); + if (peer_adv_stats->fcs_count) + peer_adv_mc_stats->fcs_count = peer_adv_stats->fcs_count; + if (peer_adv_stats->rx_bytes) + peer_adv_mc_stats->rx_bytes = peer_adv_stats->rx_bytes; + if (peer_adv_stats->rx_count) + peer_adv_mc_stats->rx_count = peer_adv_stats->rx_count; + wlan_cp_stats_peer_obj_unlock(peer_cp_stats_priv); + +end: + if (peer) + wlan_objmgr_peer_release_ref(peer, WLAN_CP_STATS_ID); + + return status; +} + +static QDF_STATUS +tgt_mc_cp_stats_update_peer_stats(struct wlan_objmgr_psoc *psoc, + struct peer_mc_cp_stats *peer_stats) +{ + uint8_t *peer_mac_addr; + struct wlan_objmgr_peer *peer; + struct peer_mc_cp_stats *peer_mc_stats; + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct peer_cp_stats *peer_cp_stats_priv; + + if (!peer_stats) + return QDF_STATUS_E_INVAL; + + peer_mac_addr = peer_stats->peer_macaddr; + peer = wlan_objmgr_get_peer_by_mac(psoc, peer_mac_addr, + WLAN_CP_STATS_ID); + if (!peer) { + cp_stats_debug("peer is null"); + return QDF_STATUS_E_EXISTS; + } + + peer_cp_stats_priv = wlan_cp_stats_get_peer_stats_obj(peer); + if (!peer_cp_stats_priv) { + cp_stats_err("peer_cp_stats_priv is null"); + status = QDF_STATUS_E_EXISTS; + goto end; + } + + wlan_cp_stats_peer_obj_lock(peer_cp_stats_priv); + peer_mc_stats = peer_cp_stats_priv->peer_stats; + qdf_mem_copy(peer_mc_stats->peer_macaddr, + peer_stats->peer_macaddr, + QDF_MAC_ADDR_SIZE); + if (peer_stats->tx_rate) + peer_mc_stats->tx_rate = peer_stats->tx_rate; + if (peer_stats->rx_rate) + peer_mc_stats->rx_rate = peer_stats->rx_rate; + if (peer_stats->peer_rssi) + peer_mc_stats->peer_rssi = peer_stats->peer_rssi; + cp_stats_nofl_debug("PEER STATS: peer_mac=%pM, tx_rate=%u, rx_rate=%u, peer_rssi=%d", + peer_mc_stats->peer_macaddr, peer_mc_stats->tx_rate, + peer_mc_stats->rx_rate, peer_mc_stats->peer_rssi); + wlan_cp_stats_peer_obj_unlock(peer_cp_stats_priv); + +end: + if (peer) + wlan_objmgr_peer_release_ref(peer, WLAN_CP_STATS_ID); + + return status; +} + +static QDF_STATUS +tgt_mc_cp_stats_update_peer_extd_stats( + struct wlan_objmgr_psoc *psoc, + struct peer_extd_stats *peer_extended_stats) +{ + uint8_t *peer_mac_addr; + struct wlan_objmgr_peer *peer; + struct peer_mc_cp_stats *peer_mc_stats; + struct peer_extd_stats *peer_extd_mc_stats; + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct peer_cp_stats *peer_cp_stats_priv; + + if (!peer_extended_stats) + return QDF_STATUS_E_INVAL; + + peer_mac_addr = peer_extended_stats->peer_macaddr; + peer = wlan_objmgr_get_peer_by_mac(psoc, peer_mac_addr, + WLAN_CP_STATS_ID); + if (!peer) { + cp_stats_debug("peer is null"); + return QDF_STATUS_E_EXISTS; + } + + peer_cp_stats_priv = wlan_cp_stats_get_peer_stats_obj(peer); + if (!peer_cp_stats_priv) { + cp_stats_err("peer_cp_stats_priv is null"); + status = QDF_STATUS_E_EXISTS; + goto end; + } + + wlan_cp_stats_peer_obj_lock(peer_cp_stats_priv); + peer_mc_stats = peer_cp_stats_priv->peer_stats; + peer_extd_mc_stats = peer_mc_stats->extd_stats; + if (!peer_extd_mc_stats) { + wlan_cp_stats_peer_obj_unlock(peer_cp_stats_priv); + cp_stats_err("No peer_extd_mc_stats"); + status = QDF_STATUS_E_INVAL; + goto end; + } + qdf_mem_copy(peer_extd_mc_stats->peer_macaddr, + peer_extended_stats->peer_macaddr, + QDF_MAC_ADDR_SIZE); + if (peer_extended_stats->rx_mc_bc_cnt) + peer_extd_mc_stats->rx_mc_bc_cnt = + peer_extended_stats->rx_mc_bc_cnt; + wlan_cp_stats_peer_obj_unlock(peer_cp_stats_priv); + + cp_stats_debug("peer_mac=%pM, rx_mc_bc_cnt=%u", + peer_extended_stats->peer_macaddr, + peer_extended_stats->rx_mc_bc_cnt); + +end: + wlan_objmgr_peer_release_ref(peer, WLAN_CP_STATS_ID); + + return status; +} + +static void tgt_mc_cp_stats_extract_peer_extd_stats( + struct wlan_objmgr_psoc *psoc, + struct stats_event *ev) +{ + uint32_t i, selected; + QDF_STATUS status; + struct request_info last_req = {0}; + + status = ucfg_mc_cp_stats_get_pending_req(psoc, + TYPE_PEER_STATS, + &last_req); + + if (QDF_IS_STATUS_ERROR(status)) { + cp_stats_err("ucfg_mc_cp_stats_get_pending_req failed"); + return; + } + + selected = ev->num_peer_extd_stats; + for (i = 0; i < ev->num_peer_extd_stats; i++) { + status = tgt_mc_cp_stats_update_peer_extd_stats( + psoc, + &ev->peer_extended_stats[i]); + + if (!QDF_IS_ADDR_BROADCAST(last_req.peer_mac_addr) && + !qdf_mem_cmp(ev->peer_extended_stats[i].peer_macaddr, + last_req.peer_mac_addr, + QDF_MAC_ADDR_SIZE)) { + /* mac is specified, but failed to update the peer */ + if (QDF_IS_STATUS_ERROR(status)) + return; + + selected = i; + } + } + + /* no matched peer */ + if (!QDF_IS_ADDR_BROADCAST(last_req.peer_mac_addr) && + selected == ev->num_peer_extd_stats) { + cp_stats_err("peer not found stats"); + return; + } +} + +static void tgt_mc_cp_stats_extract_peer_stats(struct wlan_objmgr_psoc *psoc, + struct stats_event *ev, + bool is_station_stats) +{ + uint32_t i; + QDF_STATUS status; + struct request_info last_req = {0}; + bool pending = false; + uint32_t selected; + + if (is_station_stats) + status = ucfg_mc_cp_stats_get_pending_req(psoc, + TYPE_STATION_STATS, + &last_req); + else + status = ucfg_mc_cp_stats_get_pending_req(psoc, + TYPE_PEER_STATS, + &last_req); + + if (QDF_IS_STATUS_ERROR(status)) { + cp_stats_err("ucfg_mc_cp_stats_get_pending_req failed"); + return; + } + + if (!ev->peer_stats) + goto extd2_stats; + + selected = ev->num_peer_stats; + for (i = 0; i < ev->num_peer_stats; i++) { + status = tgt_mc_cp_stats_update_peer_stats(psoc, + &ev->peer_stats[i]); + if (!QDF_IS_ADDR_BROADCAST(last_req.peer_mac_addr) && + !qdf_mem_cmp(ev->peer_stats[i].peer_macaddr, + last_req.peer_mac_addr, + QDF_MAC_ADDR_SIZE)) { + /* mac is specified, but failed to update the peer */ + if (QDF_IS_STATUS_ERROR(status)) + return; + + selected = i; + } + } + + /* no matched peer */ + if (!QDF_IS_ADDR_BROADCAST(last_req.peer_mac_addr) && + selected == ev->num_peer_stats) { + cp_stats_err("peer not found for stats"); + } + +extd2_stats: + + if (!ev->peer_adv_stats) + goto complete; + + selected = ev->num_peer_adv_stats; + for (i = 0; i < ev->num_peer_adv_stats; i++) { + status = tgt_mc_cp_stats_update_peer_adv_stats( + psoc, &ev->peer_adv_stats[i], + ev->num_peer_adv_stats); + if (!QDF_IS_ADDR_BROADCAST(last_req.peer_mac_addr) && + !qdf_mem_cmp(ev->peer_adv_stats[i].peer_macaddr, + last_req.peer_mac_addr, + QDF_MAC_ADDR_SIZE)) { + /* mac is specified, but failed to update the peer */ + if (QDF_IS_STATUS_ERROR(status)) + return; + + selected = i; + } + } + + /* no matched peer */ + if (!QDF_IS_ADDR_BROADCAST(last_req.peer_mac_addr) && + selected == ev->num_peer_adv_stats) { + cp_stats_debug("peer not found for extd stats"); + return; + } + +complete: + if (is_station_stats) + return; + + tgt_mc_cp_stats_extract_peer_extd_stats(psoc, ev); + if (tgt_mc_cp_stats_is_last_event(ev, TYPE_PEER_STATS)) { + ucfg_mc_cp_stats_reset_pending_req(psoc, TYPE_PEER_STATS, + &last_req, &pending); + if (pending && last_req.u.get_peer_rssi_cb) + tgt_mc_cp_stats_prepare_raw_peer_rssi(psoc, &last_req); + } +} + +#ifdef WLAN_FEATURE_MIB_STATS +static void tgt_mc_cp_stats_extract_mib_stats(struct wlan_objmgr_psoc *psoc, + struct stats_event *ev) +{ + QDF_STATUS status; + struct request_info last_req = {0}; + bool pending = false; + + if (!ev->mib_stats) { + cp_stats_debug("no mib stats"); + return; + } + + status = ucfg_mc_cp_stats_get_pending_req(psoc, + TYPE_MIB_STATS, &last_req); + + if (QDF_IS_STATUS_ERROR(status)) { + cp_stats_err("ucfg_mc_cp_stats_get_pending_req failed"); + return; + } + + if (tgt_mc_cp_stats_is_last_event(ev, TYPE_MIB_STATS)) { + ucfg_mc_cp_stats_reset_pending_req(psoc, TYPE_MIB_STATS, + &last_req, &pending); + if (last_req.u.get_mib_stats_cb && pending) + last_req.u.get_mib_stats_cb(ev, last_req.cookie); + } +} +#else +static void tgt_mc_cp_stats_extract_mib_stats(struct wlan_objmgr_psoc *psoc, + struct stats_event *ev) +{ +} +#endif + +static void tgt_mc_cp_stats_extract_cca_stats(struct wlan_objmgr_psoc *psoc, + struct stats_event *ev) +{ + struct wlan_objmgr_vdev *vdev; + struct vdev_mc_cp_stats *vdev_mc_stats; + struct vdev_cp_stats *vdev_cp_stats_priv; + + if (!ev->cca_stats) + return; + + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, + ev->cca_stats->vdev_id, + WLAN_CP_STATS_ID); + if (!vdev) { + cp_stats_err("vdev is null"); + return; + } + + vdev_cp_stats_priv = wlan_cp_stats_get_vdev_stats_obj(vdev); + if (!vdev_cp_stats_priv) { + cp_stats_err("vdev cp stats object is null"); + goto end; + } + + wlan_cp_stats_vdev_obj_lock(vdev_cp_stats_priv); + vdev_mc_stats = vdev_cp_stats_priv->vdev_stats; + vdev_mc_stats->cca.congestion = ev->cca_stats->congestion; + wlan_cp_stats_vdev_obj_unlock(vdev_cp_stats_priv); + +end: + wlan_objmgr_vdev_release_ref(vdev, WLAN_CP_STATS_ID); +} + +static void tgt_mc_cp_stats_extract_vdev_summary_stats( + struct wlan_objmgr_psoc *psoc, + struct stats_event *ev) +{ + uint8_t i; + QDF_STATUS status; + struct wlan_objmgr_peer *peer = NULL; + struct request_info last_req = {0}; + struct wlan_objmgr_vdev *vdev; + struct peer_mc_cp_stats *peer_mc_stats; + struct vdev_mc_cp_stats *vdev_mc_stats; + struct peer_cp_stats *peer_cp_stats_priv; + struct vdev_cp_stats *vdev_cp_stats_priv; + + if (!ev->vdev_summary_stats) + return; + + status = ucfg_mc_cp_stats_get_pending_req(psoc, + TYPE_STATION_STATS, + &last_req); + if (QDF_IS_STATUS_ERROR(status)) { + cp_stats_err("ucfg_mc_cp_stats_get_pending_req failed"); + return; + } + + for (i = 0; i < ev->num_summary_stats; i++) { + if (ev->vdev_summary_stats[i].vdev_id == last_req.vdev_id) + break; + } + + if (i == ev->num_summary_stats) { + cp_stats_debug("vdev_id %d not found", last_req.vdev_id); + return; + } + + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, last_req.vdev_id, + WLAN_CP_STATS_ID); + if (!vdev) { + cp_stats_err("vdev is null"); + return; + } + + vdev_cp_stats_priv = wlan_cp_stats_get_vdev_stats_obj(vdev); + if (!vdev_cp_stats_priv) { + cp_stats_err("vdev cp stats object is null"); + goto end; + } + + wlan_cp_stats_vdev_obj_lock(vdev_cp_stats_priv); + vdev_mc_stats = vdev_cp_stats_priv->vdev_stats; + qdf_mem_copy(&vdev_mc_stats->vdev_summary_stats, + &ev->vdev_summary_stats[i].stats, + sizeof(vdev_mc_stats->vdev_summary_stats)); + wlan_cp_stats_vdev_obj_unlock(vdev_cp_stats_priv); + + peer = wlan_objmgr_get_peer(psoc, last_req.pdev_id, + last_req.peer_mac_addr, WLAN_CP_STATS_ID); + if (!peer) { + cp_stats_debug("peer is null %pM", last_req.peer_mac_addr); + goto end; + } + + peer_cp_stats_priv = wlan_cp_stats_get_peer_stats_obj(peer); + if (!peer_cp_stats_priv) { + cp_stats_err("peer cp stats object is null"); + goto end; + } + + wlan_cp_stats_peer_obj_lock(peer_cp_stats_priv); + peer_mc_stats = peer_cp_stats_priv->peer_stats; + peer_mc_stats->peer_rssi = ev->vdev_summary_stats[i].stats.rssi; + wlan_cp_stats_peer_obj_unlock(peer_cp_stats_priv); + +end: + if (peer) + wlan_objmgr_peer_release_ref(peer, WLAN_CP_STATS_ID); + wlan_objmgr_vdev_release_ref(vdev, WLAN_CP_STATS_ID); +} + +static void tgt_mc_cp_stats_extract_vdev_chain_rssi_stats( + struct wlan_objmgr_psoc *psoc, + struct stats_event *ev) +{ + uint8_t i, j; + QDF_STATUS status; + struct request_info last_req = {0}; + struct wlan_objmgr_vdev *vdev; + struct vdev_mc_cp_stats *vdev_mc_stats; + struct vdev_cp_stats *vdev_cp_stats_priv; + + if (!ev->vdev_chain_rssi) + return; + + status = ucfg_mc_cp_stats_get_pending_req(psoc, + TYPE_STATION_STATS, + &last_req); + if (QDF_IS_STATUS_ERROR(status)) { + cp_stats_err("ucfg_mc_cp_stats_get_pending_req failed"); + return; + } + + for (i = 0; i < ev->num_chain_rssi_stats; i++) { + if (ev->vdev_chain_rssi[i].vdev_id == last_req.vdev_id) + break; + } + + if (i == ev->num_chain_rssi_stats) { + cp_stats_debug("vdev_id %d not found", last_req.vdev_id); + return; + } + + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, last_req.vdev_id, + WLAN_CP_STATS_ID); + if (!vdev) { + cp_stats_err("vdev is null"); + return; + } + + vdev_cp_stats_priv = wlan_cp_stats_get_vdev_stats_obj(vdev); + if (!vdev_cp_stats_priv) { + cp_stats_err("vdev cp stats object is null"); + goto end; + } + + wlan_cp_stats_vdev_obj_lock(vdev_cp_stats_priv); + vdev_mc_stats = vdev_cp_stats_priv->vdev_stats; + for (j = 0; j < MAX_NUM_CHAINS; j++) { + vdev_mc_stats->chain_rssi[j] = + ev->vdev_chain_rssi[i].chain_rssi[j]; + } + wlan_cp_stats_vdev_obj_unlock(vdev_cp_stats_priv); + +end: + wlan_objmgr_vdev_release_ref(vdev, WLAN_CP_STATS_ID); +} + +static void +tgt_mc_cp_stats_prepare_n_send_raw_station_stats(struct wlan_objmgr_psoc *psoc, + struct request_info *last_req) +{ + /* station_stats to be given to userspace thread */ + struct stats_event info = {0}; + struct wlan_objmgr_vdev *vdev; + struct wlan_objmgr_peer *peer; + struct peer_mc_cp_stats *peer_mc_stats; + struct vdev_mc_cp_stats *vdev_mc_stats; + struct peer_cp_stats *peer_cp_stats_priv; + struct vdev_cp_stats *vdev_cp_stats_priv; + void (*get_station_stats_cb)(struct stats_event *info, void *cookie); + + get_station_stats_cb = last_req->u.get_station_stats_cb; + if (!get_station_stats_cb) { + cp_stats_err("callback is null"); + return; + } + + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, last_req->vdev_id, + WLAN_CP_STATS_ID); + if (!vdev) { + cp_stats_err("vdev object is null"); + return; + } + + peer = wlan_objmgr_get_peer(psoc, last_req->pdev_id, + last_req->peer_mac_addr, WLAN_CP_STATS_ID); + if (!peer) { + cp_stats_err("peer object is null"); + goto end; + } + + vdev_cp_stats_priv = wlan_cp_stats_get_vdev_stats_obj(vdev); + if (!vdev_cp_stats_priv) { + cp_stats_err("vdev cp stats object is null"); + goto end; + } + + peer_cp_stats_priv = wlan_cp_stats_get_peer_stats_obj(peer); + if (!peer_cp_stats_priv) { + cp_stats_err("peer cp stats object is null"); + goto end; + } + + info.num_summary_stats = 1; + info.vdev_summary_stats = qdf_mem_malloc( + sizeof(*info.vdev_summary_stats)); + if (!info.vdev_summary_stats) + goto end; + + info.num_chain_rssi_stats = 1; + info.vdev_chain_rssi = qdf_mem_malloc(sizeof(*info.vdev_chain_rssi));; + if (!info.vdev_chain_rssi) + goto end; + + wlan_cp_stats_vdev_obj_lock(vdev_cp_stats_priv); + vdev_mc_stats = vdev_cp_stats_priv->vdev_stats; + info.vdev_summary_stats[0].vdev_id = last_req->vdev_id; + info.vdev_summary_stats[0].stats = vdev_mc_stats->vdev_summary_stats; + info.vdev_chain_rssi[0].vdev_id = last_req->vdev_id; + qdf_mem_copy(info.vdev_chain_rssi[0].chain_rssi, + vdev_mc_stats->chain_rssi, + sizeof(vdev_mc_stats->chain_rssi)); + info.tx_rate_flags = vdev_mc_stats->tx_rate_flags; + wlan_cp_stats_vdev_obj_unlock(vdev_cp_stats_priv); + + info.peer_adv_stats = qdf_mem_malloc(sizeof(*info.peer_adv_stats)); + if (!info.peer_adv_stats) + goto end; + + wlan_cp_stats_peer_obj_lock(peer_cp_stats_priv); + peer_mc_stats = peer_cp_stats_priv->peer_stats; + /* + * The linkspeed returned by fw is in kbps so convert + * it in units of 100kbps which is expected by UMAC + */ + info.tx_rate = peer_mc_stats->tx_rate / 100; + info.rx_rate = peer_mc_stats->rx_rate / 100; + + if (peer_mc_stats->adv_stats) { + info.num_peer_adv_stats = 1; + qdf_mem_copy(info.peer_adv_stats, + peer_mc_stats->adv_stats, + sizeof(peer_mc_stats->adv_stats)); + } + + wlan_cp_stats_peer_obj_unlock(peer_cp_stats_priv); + +end: + if (info.vdev_summary_stats && info.vdev_chain_rssi) + get_station_stats_cb(&info, last_req->cookie); + + ucfg_mc_cp_stats_free_stats_resources(&info); + + if (peer) + wlan_objmgr_peer_release_ref(peer, WLAN_CP_STATS_ID); + + wlan_objmgr_vdev_release_ref(vdev, WLAN_CP_STATS_ID); +} + +static void tgt_mc_cp_stats_extract_station_stats( + struct wlan_objmgr_psoc *psoc, + struct stats_event *ev) +{ + QDF_STATUS status; + struct request_info last_req = {0}; + bool pending = false; + + status = ucfg_mc_cp_stats_get_pending_req(psoc, + TYPE_STATION_STATS, + &last_req); + if (QDF_IS_STATUS_ERROR(status)) { + cp_stats_err("ucfg_mc_cp_stats_get_pending_req failed"); + return; + } + + tgt_mc_cp_stats_extract_tx_power(psoc, ev, true); + tgt_mc_cp_stats_extract_peer_stats(psoc, ev, true); + tgt_mc_cp_stats_extract_vdev_summary_stats(psoc, ev); + tgt_mc_cp_stats_extract_vdev_chain_rssi_stats(psoc, ev); + + /* + * PEER stats are the last stats sent for get_station statistics. + * reset type_map bit for station stats . + */ + if (tgt_mc_cp_stats_is_last_event(ev, TYPE_STATION_STATS)) { + ucfg_mc_cp_stats_reset_pending_req(psoc, TYPE_STATION_STATS, + &last_req, + &pending); + if (pending && last_req.u.get_station_stats_cb) + tgt_mc_cp_stats_prepare_n_send_raw_station_stats( + psoc, &last_req); + } +} + +static void tgt_mc_cp_send_lost_link_stats(struct wlan_objmgr_psoc *psoc, + struct stats_event *ev) +{ + struct psoc_cp_stats *psoc_cp_stats_priv; + + psoc_cp_stats_priv = wlan_cp_stats_get_psoc_stats_obj(psoc); + if (psoc_cp_stats_priv && psoc_cp_stats_priv->legacy_stats_cb) + psoc_cp_stats_priv->legacy_stats_cb(ev); +} + +QDF_STATUS tgt_mc_cp_stats_process_stats_event(struct wlan_objmgr_psoc *psoc, + struct stats_event *ev) +{ + if (ucfg_mc_cp_stats_is_req_pending(psoc, TYPE_CONNECTION_TX_POWER)) + tgt_mc_cp_stats_extract_tx_power(psoc, ev, false); + + if (ucfg_mc_cp_stats_is_req_pending(psoc, TYPE_PEER_STATS)) + tgt_mc_cp_stats_extract_peer_stats(psoc, ev, false); + + if (ucfg_mc_cp_stats_is_req_pending(psoc, TYPE_STATION_STATS)) + tgt_mc_cp_stats_extract_station_stats(psoc, ev); + + if (ucfg_mc_cp_stats_is_req_pending(psoc, TYPE_MIB_STATS)) + tgt_mc_cp_stats_extract_mib_stats(psoc, ev); + + tgt_mc_cp_stats_extract_cca_stats(psoc, ev); + + tgt_mc_cp_send_lost_link_stats(psoc, ev); + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS tgt_mc_cp_stats_inc_wake_lock_stats(struct wlan_objmgr_psoc *psoc, + uint32_t reason, + struct wake_lock_stats *stats, + uint32_t *unspecified_wake_count) +{ + struct wlan_lmac_if_cp_stats_tx_ops *tx_ops; + + tx_ops = target_if_cp_stats_get_tx_ops(psoc); + if (!tx_ops) + return QDF_STATUS_E_NULL_VALUE; + + tx_ops->inc_wake_lock_stats(reason, stats, unspecified_wake_count); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS tgt_send_mc_cp_stats_req(struct wlan_objmgr_psoc *psoc, + enum stats_req_type type, + struct request_info *req) +{ + struct wlan_lmac_if_cp_stats_tx_ops *tx_ops; + + tx_ops = target_if_cp_stats_get_tx_ops(psoc); + if (!tx_ops || !tx_ops->send_req_stats) { + cp_stats_err("could not get tx_ops"); + return QDF_STATUS_E_NULL_VALUE; + } + + return tx_ops->send_req_stats(psoc, type, req); +} diff --git a/components/cp_stats/dispatcher/src/wlan_cp_stats_mc_ucfg_api.c b/components/cp_stats/dispatcher/src/wlan_cp_stats_mc_ucfg_api.c new file mode 100644 index 000000000000..44c517de4c30 --- /dev/null +++ b/components/cp_stats/dispatcher/src/wlan_cp_stats_mc_ucfg_api.c @@ -0,0 +1,725 @@ +/* + * Copyright (c) 2018-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_cp_stats_mc_ucfg_api.c + * + * This file provide API definitions required for northbound interaction + */ + +#include +#include "wlan_cp_stats_mc_defs.h" +#include +#include +#include +#include "../../core/src/wlan_cp_stats_defs.h" +#include "../../core/src/wlan_cp_stats_defs.h" +#include "../../core/src/wlan_cp_stats_cmn_api_i.h" +#ifdef WLAN_POWER_MANAGEMENT_OFFLOAD +#include +#endif + +QDF_STATUS wlan_cp_stats_psoc_cs_init(struct psoc_cp_stats *psoc_cs) +{ + psoc_cs->obj_stats = qdf_mem_malloc(sizeof(struct psoc_mc_cp_stats)); + if (!psoc_cs->obj_stats) + return QDF_STATUS_E_NOMEM; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlan_cp_stats_psoc_cs_deinit(struct psoc_cp_stats *psoc_cs) +{ + qdf_mem_free(psoc_cs->obj_stats); + psoc_cs->obj_stats = NULL; + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlan_cp_stats_vdev_cs_init(struct vdev_cp_stats *vdev_cs) +{ + vdev_cs->vdev_stats = qdf_mem_malloc(sizeof(struct vdev_mc_cp_stats)); + if (!vdev_cs->vdev_stats) + return QDF_STATUS_E_NOMEM; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlan_cp_stats_vdev_cs_deinit(struct vdev_cp_stats *vdev_cs) +{ + qdf_mem_free(vdev_cs->vdev_stats); + vdev_cs->vdev_stats = NULL; + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlan_cp_stats_pdev_cs_init(struct pdev_cp_stats *pdev_cs) +{ + pdev_cs->pdev_stats = qdf_mem_malloc(sizeof(struct pdev_mc_cp_stats)); + if (!pdev_cs->pdev_stats) + return QDF_STATUS_E_NOMEM; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlan_cp_stats_pdev_cs_deinit(struct pdev_cp_stats *pdev_cs) +{ + qdf_mem_free(pdev_cs->pdev_stats); + pdev_cs->pdev_stats = NULL; + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlan_cp_stats_peer_cs_init(struct peer_cp_stats *peer_cs) +{ + struct peer_mc_cp_stats *peer_mc_stats; + + peer_mc_stats = qdf_mem_malloc(sizeof(struct peer_mc_cp_stats)); + if (!peer_mc_stats) + return QDF_STATUS_E_NOMEM; + + peer_mc_stats->adv_stats = + qdf_mem_malloc(sizeof(struct peer_adv_mc_cp_stats)); + + if (!peer_mc_stats->adv_stats) { + qdf_mem_free(peer_mc_stats); + peer_mc_stats = NULL; + return QDF_STATUS_E_NOMEM; + } + + peer_mc_stats->extd_stats = + qdf_mem_malloc(sizeof(struct peer_extd_stats)); + + if (!peer_mc_stats->extd_stats) { + qdf_mem_free(peer_mc_stats->adv_stats); + peer_mc_stats->adv_stats = NULL; + qdf_mem_free(peer_mc_stats); + peer_mc_stats = NULL; + return QDF_STATUS_E_NOMEM; + } + peer_cs->peer_stats = peer_mc_stats; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS wlan_cp_stats_peer_cs_deinit(struct peer_cp_stats *peer_cs) +{ + struct peer_mc_cp_stats *peer_mc_stats = peer_cs->peer_stats; + + qdf_mem_free(peer_mc_stats->adv_stats); + peer_mc_stats->adv_stats = NULL; + qdf_mem_free(peer_mc_stats->extd_stats); + peer_mc_stats->extd_stats = NULL; + qdf_mem_free(peer_cs->peer_stats); + peer_cs->peer_stats = NULL; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS ucfg_mc_cp_stats_inc_wake_lock_stats_by_protocol( + struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id, + enum qdf_proto_subtype protocol) +{ + struct wake_lock_stats *stats; + struct psoc_cp_stats *psoc_cp_stats_priv; + struct psoc_mc_cp_stats *psoc_mc_stats; + + psoc_cp_stats_priv = wlan_cp_stats_get_psoc_stats_obj(psoc); + if (!psoc_cp_stats_priv) { + cp_stats_err("psoc cp stats object is null"); + return QDF_STATUS_E_NULL_VALUE; + } + + wlan_cp_stats_psoc_obj_lock(psoc_cp_stats_priv); + psoc_mc_stats = psoc_cp_stats_priv->obj_stats; + + if (!psoc_mc_stats) { + cp_stats_err("psoc mc stats is null"); + return QDF_STATUS_E_NULL_VALUE; + } + + stats = &psoc_mc_stats->wow_stats; + switch (protocol) { + case QDF_PROTO_ICMP_REQ: + case QDF_PROTO_ICMP_RES: + stats->icmpv4_count++; + break; + case QDF_PROTO_ICMPV6_REQ: + case QDF_PROTO_ICMPV6_RES: + case QDF_PROTO_ICMPV6_RS: + stats->icmpv6_count++; + break; + case QDF_PROTO_ICMPV6_RA: + stats->icmpv6_count++; + stats->ipv6_mcast_ra_stats++; + break; + case QDF_PROTO_ICMPV6_NS: + stats->icmpv6_count++; + stats->ipv6_mcast_ns_stats++; + break; + case QDF_PROTO_ICMPV6_NA: + stats->icmpv6_count++; + stats->ipv6_mcast_na_stats++; + break; + default: + break; + } + wlan_cp_stats_psoc_obj_unlock(psoc_cp_stats_priv); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS ucfg_mc_cp_stats_inc_wake_lock_stats_by_dst_addr( + struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id, uint8_t *dest_mac) +{ + struct psoc_cp_stats *psoc_cp_stats_priv; + struct psoc_mc_cp_stats *psoc_mc_stats; + struct wake_lock_stats *stats; + + psoc_cp_stats_priv = wlan_cp_stats_get_psoc_stats_obj(psoc); + if (!psoc_cp_stats_priv) { + cp_stats_err("psoc cp stats object is null"); + return QDF_STATUS_E_NULL_VALUE; + } + + wlan_cp_stats_psoc_obj_lock(psoc_cp_stats_priv); + psoc_mc_stats = psoc_cp_stats_priv->obj_stats; + if (!psoc_mc_stats) { + cp_stats_err("psoc mc stats is null"); + return QDF_STATUS_E_NULL_VALUE; + } + + stats = &psoc_mc_stats->wow_stats; + + switch (*dest_mac) { + case QDF_BCAST_MAC_ADDR: + stats->bcast_wake_up_count++; + break; + case QDF_MCAST_IPV4_MAC_ADDR: + stats->ipv4_mcast_wake_up_count++; + break; + case QDF_MCAST_IPV6_MAC_ADDR: + stats->ipv6_mcast_wake_up_count++; + break; + default: + stats->ucast_wake_up_count++; + break; + } + wlan_cp_stats_psoc_obj_unlock(psoc_cp_stats_priv); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS ucfg_mc_cp_stats_inc_wake_lock_stats(struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id, + uint32_t reason) +{ + struct wake_lock_stats *stats; + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct psoc_mc_cp_stats *psoc_mc_stats; + struct psoc_cp_stats *psoc_cp_stats_priv; + + psoc_cp_stats_priv = wlan_cp_stats_get_psoc_stats_obj(psoc); + if (!psoc_cp_stats_priv) { + cp_stats_err("psoc cp stats object is null"); + return QDF_STATUS_E_NULL_VALUE; + } + + wlan_cp_stats_psoc_obj_lock(psoc_cp_stats_priv); + + psoc_mc_stats = psoc_cp_stats_priv->obj_stats; + + if (!psoc_mc_stats) { + cp_stats_err("psoc mc stats is null"); + return QDF_STATUS_E_NULL_VALUE; + } + + stats = &psoc_mc_stats->wow_stats; + + status = tgt_mc_cp_stats_inc_wake_lock_stats(psoc, reason, stats, + &psoc_mc_stats->wow_unspecified_wake_up_count); + wlan_cp_stats_psoc_obj_unlock(psoc_cp_stats_priv); + + return status; +} + +/** + * vdev_iterator() - iterator function to collect wake_lock_stats from all vdev + * @psoc: pointer to psoc object + * @vdev: pointer to vdev object + * @arg: stats object pointer passed as arg + * + * Return - none + */ +static void vdev_iterator(struct wlan_objmgr_psoc *psoc, void *vdev, void *arg) +{ + struct wake_lock_stats *vdev_stats; + struct wake_lock_stats *stats = arg; + struct psoc_cp_stats *psoc_cp_stats_priv; + struct psoc_mc_cp_stats *psoc_mc_stats; + + psoc_cp_stats_priv = wlan_cp_stats_get_psoc_stats_obj(psoc); + if (!psoc_cp_stats_priv) { + cp_stats_err("psoc cp stats object is null"); + return; + } + + psoc_mc_stats = psoc_cp_stats_priv->obj_stats; + if (!psoc_mc_stats) { + cp_stats_err("psoc mc stats is null"); + return; + } + + vdev_stats = &psoc_mc_stats->wow_stats; + + stats->ucast_wake_up_count += vdev_stats->ucast_wake_up_count; + stats->bcast_wake_up_count += vdev_stats->bcast_wake_up_count; + stats->ipv4_mcast_wake_up_count += vdev_stats->ipv4_mcast_wake_up_count; + stats->ipv6_mcast_wake_up_count += vdev_stats->ipv6_mcast_wake_up_count; + stats->ipv6_mcast_ra_stats += vdev_stats->ipv6_mcast_ra_stats; + stats->ipv6_mcast_ns_stats += vdev_stats->ipv6_mcast_ns_stats; + stats->ipv6_mcast_na_stats += vdev_stats->ipv6_mcast_na_stats; + stats->icmpv4_count += vdev_stats->icmpv4_count; + stats->icmpv6_count += vdev_stats->icmpv6_count; + stats->rssi_breach_wake_up_count += + vdev_stats->rssi_breach_wake_up_count; + stats->low_rssi_wake_up_count += vdev_stats->low_rssi_wake_up_count; + stats->gscan_wake_up_count += vdev_stats->gscan_wake_up_count; + stats->pno_complete_wake_up_count += + vdev_stats->pno_complete_wake_up_count; + stats->pno_match_wake_up_count += vdev_stats->pno_match_wake_up_count; + stats->oem_response_wake_up_count += + vdev_stats->oem_response_wake_up_count; + stats->pwr_save_fail_detected += vdev_stats->pwr_save_fail_detected; + stats->scan_11d += vdev_stats->scan_11d; +} + +QDF_STATUS ucfg_mc_cp_stats_get_psoc_wake_lock_stats( + struct wlan_objmgr_psoc *psoc, + struct wake_lock_stats *stats) +{ + struct psoc_cp_stats *psoc_cp_stats_priv; + struct psoc_mc_cp_stats *psoc_mc_stats; + + psoc_cp_stats_priv = wlan_cp_stats_get_psoc_stats_obj(psoc); + if (!psoc_cp_stats_priv) { + cp_stats_err("psoc cp stats object is null"); + return QDF_STATUS_E_NULL_VALUE; + } + + wlan_cp_stats_psoc_obj_lock(psoc_cp_stats_priv); + psoc_mc_stats = psoc_cp_stats_priv->obj_stats; + /* iterate through all vdevs, and get wow stats from vdev_cs object */ + wlan_objmgr_iterate_obj_list(psoc, WLAN_VDEV_OP, vdev_iterator, + stats, true, WLAN_CP_STATS_ID); + wlan_cp_stats_psoc_obj_unlock(psoc_cp_stats_priv); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS ucfg_mc_cp_stats_get_vdev_wake_lock_stats( + struct wlan_objmgr_vdev *vdev, + struct wake_lock_stats *stats) +{ + struct wlan_objmgr_psoc *psoc; + struct psoc_cp_stats *psoc_cp_stats_priv; + struct psoc_mc_cp_stats *psoc_mc_stats; + + wlan_vdev_obj_lock(vdev); + psoc = wlan_vdev_get_psoc(vdev); + if (!psoc) { + wlan_vdev_obj_unlock(vdev); + cp_stats_err("psoc NULL"); + return QDF_STATUS_E_INVAL; + } + wlan_vdev_obj_unlock(vdev); + + psoc_cp_stats_priv = wlan_cp_stats_get_psoc_stats_obj(psoc); + if (!psoc_cp_stats_priv) { + cp_stats_err("psoc cp stats object is null"); + return QDF_STATUS_E_NULL_VALUE; + } + + wlan_cp_stats_psoc_obj_lock(psoc_cp_stats_priv); + psoc_mc_stats = psoc_cp_stats_priv->obj_stats; + + if (!psoc_mc_stats) { + cp_stats_err("psoc mc stats is null"); + return QDF_STATUS_E_NULL_VALUE; + } + + qdf_mem_copy(stats, &psoc_mc_stats->wow_stats, sizeof(*stats)); + + wlan_cp_stats_psoc_obj_unlock(psoc_cp_stats_priv); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS ucfg_mc_cp_stats_write_wow_stats( + struct wlan_objmgr_psoc *psoc, + char *buffer, uint16_t max_len, int *ret) +{ + QDF_STATUS status; + uint32_t unspecified_wake_count; + struct wake_lock_stats wow_stats = {0}; + struct psoc_mc_cp_stats *psoc_mc_stats; + struct psoc_cp_stats *psoc_cp_stats_priv; + + psoc_cp_stats_priv = wlan_cp_stats_get_psoc_stats_obj(psoc); + if (!psoc_cp_stats_priv) { + cp_stats_err("psoc cp stats object is null"); + return QDF_STATUS_E_NULL_VALUE; + } + + /* get stats from psoc */ + status = ucfg_mc_cp_stats_get_psoc_wake_lock_stats(psoc, &wow_stats); + if (QDF_IS_STATUS_ERROR(status)) { + cp_stats_err("Failed to get WoW stats"); + return status; + } + + wlan_cp_stats_psoc_obj_lock(psoc_cp_stats_priv); + psoc_mc_stats = psoc_cp_stats_priv->obj_stats; + unspecified_wake_count = psoc_mc_stats->wow_unspecified_wake_up_count; + wlan_cp_stats_psoc_obj_unlock(psoc_cp_stats_priv); + + *ret = qdf_scnprintf(buffer, max_len, + "WoW Wake Reasons\n" + "\tunspecified wake count: %u\n" + "\tunicast: %u\n" + "\tbroadcast: %u\n" + "\tIPv4 multicast: %u\n" + "\tIPv6 multicast: %u\n" + "\tIPv6 multicast RA: %u\n" + "\tIPv6 multicast NS: %u\n" + "\tIPv6 multicast NA: %u\n" + "\tICMPv4: %u\n" + "\tICMPv6: %u\n" + "\tRSSI Breach: %u\n" + "\tLow RSSI: %u\n" + "\tG-Scan: %u\n" + "\tPNO Complete: %u\n" + "\tPNO Match: %u\n" + "\tOEM rsp wake_count: %u\n" + "\twake count due to pwr_save_fail_detected: %u\n" + "\twake count due to 11d scan: %u\n", + unspecified_wake_count, + wow_stats.ucast_wake_up_count, + wow_stats.bcast_wake_up_count, + wow_stats.ipv4_mcast_wake_up_count, + wow_stats.ipv6_mcast_wake_up_count, + wow_stats.ipv6_mcast_ra_stats, + wow_stats.ipv6_mcast_ns_stats, + wow_stats.ipv6_mcast_na_stats, + wow_stats.icmpv4_count, + wow_stats.icmpv6_count, + wow_stats.rssi_breach_wake_up_count, + wow_stats.low_rssi_wake_up_count, + wow_stats.gscan_wake_up_count, + wow_stats.pno_complete_wake_up_count, + wow_stats.pno_match_wake_up_count, + wow_stats.oem_response_wake_up_count, + wow_stats.pwr_save_fail_detected, + wow_stats.scan_11d); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS ucfg_mc_cp_stats_send_stats_request(struct wlan_objmgr_vdev *vdev, + enum stats_req_type type, + struct request_info *info) +{ + QDF_STATUS status; + + status = ucfg_mc_cp_stats_set_pending_req(wlan_vdev_get_psoc(vdev), + type, info); + if (QDF_IS_STATUS_ERROR(status)) { + cp_stats_err("ucfg_mc_cp_stats_set_pending_req pdev failed: %d", + status); + return status; + } + + return tgt_send_mc_cp_stats_req(wlan_vdev_get_psoc(vdev), type, info); +} + +QDF_STATUS ucfg_mc_cp_stats_get_tx_power(struct wlan_objmgr_vdev *vdev, + int *dbm) +{ + struct wlan_objmgr_pdev *pdev; + struct pdev_mc_cp_stats *pdev_mc_stats; + struct pdev_cp_stats *pdev_cp_stats_priv; + + pdev = wlan_vdev_get_pdev(vdev); + pdev_cp_stats_priv = wlan_cp_stats_get_pdev_stats_obj(pdev); + if (!pdev_cp_stats_priv) { + cp_stats_err("pdev cp stats object is null"); + return QDF_STATUS_E_NULL_VALUE; + } + + wlan_cp_stats_pdev_obj_lock(pdev_cp_stats_priv); + pdev_mc_stats = pdev_cp_stats_priv->pdev_stats; + *dbm = pdev_mc_stats->max_pwr; + wlan_cp_stats_pdev_obj_unlock(pdev_cp_stats_priv); + + return QDF_STATUS_SUCCESS; +} + +bool ucfg_mc_cp_stats_is_req_pending(struct wlan_objmgr_psoc *psoc, + enum stats_req_type type) +{ + uint32_t pending_req_map; + struct psoc_mc_cp_stats *psoc_mc_stats; + struct psoc_cp_stats *psoc_cp_stats_priv; + + psoc_cp_stats_priv = wlan_cp_stats_get_psoc_stats_obj(psoc); + if (!psoc_cp_stats_priv) { + cp_stats_err("psoc cp stats object is null"); + return false; + } + + wlan_cp_stats_psoc_obj_lock(psoc_cp_stats_priv); + psoc_mc_stats = psoc_cp_stats_priv->obj_stats; + pending_req_map = psoc_mc_stats->pending.type_map; + wlan_cp_stats_psoc_obj_unlock(psoc_cp_stats_priv); + + return (pending_req_map & (1 << type)); +} + +QDF_STATUS ucfg_mc_cp_stats_set_pending_req(struct wlan_objmgr_psoc *psoc, + enum stats_req_type type, + struct request_info *req) +{ + struct psoc_mc_cp_stats *psoc_mc_stats; + struct psoc_cp_stats *psoc_cp_stats_priv; + + psoc_cp_stats_priv = wlan_cp_stats_get_psoc_stats_obj(psoc); + if (!psoc_cp_stats_priv) { + cp_stats_err("psoc cp stats object is null"); + return QDF_STATUS_E_NULL_VALUE; + } + + if (type >= TYPE_MAX) { + cp_stats_err("Invalid type index: %d", type); + return QDF_STATUS_E_INVAL; + } + + wlan_cp_stats_psoc_obj_lock(psoc_cp_stats_priv); + psoc_mc_stats = psoc_cp_stats_priv->obj_stats; + if (psoc_mc_stats->is_cp_stats_suspended) { + cp_stats_debug("cp stats is suspended try again after resume"); + wlan_cp_stats_psoc_obj_unlock(psoc_cp_stats_priv); + return QDF_STATUS_E_AGAIN; + } + psoc_mc_stats->pending.type_map |= (1 << type); + psoc_mc_stats->pending.req[type] = *req; + wlan_cp_stats_psoc_obj_unlock(psoc_cp_stats_priv); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS ucfg_mc_cp_stats_reset_pending_req(struct wlan_objmgr_psoc *psoc, + enum stats_req_type type, + struct request_info *last_req, + bool *pending) +{ + struct psoc_mc_cp_stats *psoc_mc_stats; + struct psoc_cp_stats *psoc_cp_stats_priv; + + psoc_cp_stats_priv = wlan_cp_stats_get_psoc_stats_obj(psoc); + if (!psoc_cp_stats_priv) { + cp_stats_err("psoc cp stats object is null"); + return QDF_STATUS_E_NULL_VALUE; + } + + if (type >= TYPE_MAX) { + cp_stats_err("Invalid type index: %d", type); + return QDF_STATUS_E_INVAL; + } + + wlan_cp_stats_psoc_obj_lock(psoc_cp_stats_priv); + psoc_mc_stats = psoc_cp_stats_priv->obj_stats; + if (psoc_mc_stats->pending.type_map & (1 << type)) { + *last_req = psoc_mc_stats->pending.req[type]; + *pending = true; + } else { + *pending = false; + } + psoc_mc_stats->pending.type_map &= ~(1 << type); + qdf_mem_zero(&psoc_mc_stats->pending.req[type], + sizeof(psoc_mc_stats->pending.req[type])); + wlan_cp_stats_psoc_obj_unlock(psoc_cp_stats_priv); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS ucfg_mc_cp_stats_get_pending_req(struct wlan_objmgr_psoc *psoc, + enum stats_req_type type, + struct request_info *info) +{ + struct psoc_mc_cp_stats *psoc_mc_stats; + struct psoc_cp_stats *psoc_cp_stats_priv; + + psoc_cp_stats_priv = wlan_cp_stats_get_psoc_stats_obj(psoc); + if (!psoc_cp_stats_priv) { + cp_stats_err("psoc cp stats object is null"); + return QDF_STATUS_E_NULL_VALUE; + } + + if (type >= TYPE_MAX) { + cp_stats_err("Invalid type index: %d", type); + return QDF_STATUS_E_INVAL; + } + wlan_cp_stats_psoc_obj_lock(psoc_cp_stats_priv); + psoc_mc_stats = psoc_cp_stats_priv->obj_stats; + *info = psoc_mc_stats->pending.req[type]; + wlan_cp_stats_psoc_obj_unlock(psoc_cp_stats_priv); + + return QDF_STATUS_SUCCESS; +} + +void ucfg_mc_cp_stats_free_stats_resources(struct stats_event *ev) +{ + if (!ev) + return; + + qdf_mem_free(ev->pdev_stats); + qdf_mem_free(ev->peer_adv_stats); + qdf_mem_free(ev->peer_stats); + qdf_mem_free(ev->cca_stats); + qdf_mem_free(ev->vdev_summary_stats); + qdf_mem_free(ev->vdev_chain_rssi); + qdf_mem_free(ev->peer_extended_stats); + qdf_mem_zero(ev, sizeof(*ev)); +} + +QDF_STATUS ucfg_mc_cp_stats_cca_stats_get(struct wlan_objmgr_vdev *vdev, + struct cca_stats *cca_stats) +{ + struct vdev_cp_stats *vdev_cp_stats_priv; + struct vdev_mc_cp_stats *vdev_mc_stats; + + vdev_cp_stats_priv = wlan_cp_stats_get_vdev_stats_obj(vdev); + if (!vdev_cp_stats_priv) { + cp_stats_err("vdev cp stats object is null"); + return QDF_STATUS_E_NULL_VALUE; + } + + wlan_cp_stats_vdev_obj_lock(vdev_cp_stats_priv); + vdev_mc_stats = vdev_cp_stats_priv->vdev_stats; + cca_stats->congestion = vdev_mc_stats->cca.congestion; + wlan_cp_stats_vdev_obj_unlock(vdev_cp_stats_priv); + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS ucfg_mc_cp_stats_set_rate_flags(struct wlan_objmgr_vdev *vdev, + uint32_t flags) +{ + struct vdev_mc_cp_stats *vdev_mc_stats; + struct vdev_cp_stats *vdev_cp_stats_priv; + + vdev_cp_stats_priv = wlan_cp_stats_get_vdev_stats_obj(vdev); + if (!vdev_cp_stats_priv) { + cp_stats_err("vdev cp stats object is null"); + return QDF_STATUS_E_NULL_VALUE; + } + + wlan_cp_stats_vdev_obj_lock(vdev_cp_stats_priv); + vdev_mc_stats = vdev_cp_stats_priv->vdev_stats; + vdev_mc_stats->tx_rate_flags = flags; + wlan_cp_stats_vdev_obj_unlock(vdev_cp_stats_priv); + + return QDF_STATUS_SUCCESS; +} + +void ucfg_mc_cp_stats_register_lost_link_info_cb( + struct wlan_objmgr_psoc *psoc, + void (*lost_link_cp_stats_info_cb)(void *stats_ev)) +{ + struct psoc_cp_stats *psoc_cp_stats_priv; + + psoc_cp_stats_priv = wlan_cp_stats_get_psoc_stats_obj(psoc); + if (!psoc_cp_stats_priv) { + cp_stats_err("psoc cp stats object is null"); + return; + } + + psoc_cp_stats_priv->legacy_stats_cb = lost_link_cp_stats_info_cb; +} + +#ifdef WLAN_POWER_MANAGEMENT_OFFLOAD +static QDF_STATUS +ucfg_mc_cp_stats_suspend_req_handler(struct wlan_objmgr_psoc *psoc) +{ + struct psoc_mc_cp_stats *psoc_mc_stats; + struct psoc_cp_stats *psoc_cp_stats_priv; + + psoc_cp_stats_priv = wlan_cp_stats_get_psoc_stats_obj(psoc); + if (!psoc_cp_stats_priv) { + cp_stats_err("psoc cp stats object is null"); + return QDF_STATUS_E_NULL_VALUE; + } + + wlan_cp_stats_psoc_obj_lock(psoc_cp_stats_priv); + psoc_mc_stats = psoc_cp_stats_priv->obj_stats; + psoc_mc_stats->is_cp_stats_suspended = true; + wlan_cp_stats_psoc_obj_unlock(psoc_cp_stats_priv); + + return QDF_STATUS_SUCCESS; +} + +static QDF_STATUS +ucfg_mc_cp_stats_resume_req_handler(struct wlan_objmgr_psoc *psoc) +{ + struct psoc_mc_cp_stats *psoc_mc_stats; + struct psoc_cp_stats *psoc_cp_stats_priv; + + psoc_cp_stats_priv = wlan_cp_stats_get_psoc_stats_obj(psoc); + if (!psoc_cp_stats_priv) { + cp_stats_err("psoc cp stats object is null"); + return QDF_STATUS_E_NULL_VALUE; + } + + wlan_cp_stats_psoc_obj_lock(psoc_cp_stats_priv); + psoc_mc_stats = psoc_cp_stats_priv->obj_stats; + psoc_mc_stats->is_cp_stats_suspended = false; + wlan_cp_stats_psoc_obj_unlock(psoc_cp_stats_priv); + + return QDF_STATUS_SUCCESS; +} + +static QDF_STATUS +ucfg_mc_cp_stats_resume_handler(struct wlan_objmgr_psoc *psoc, + void *arg) +{ + return ucfg_mc_cp_stats_resume_req_handler(psoc); +} + +static QDF_STATUS +ucfg_mc_cp_stats_suspend_handler(struct wlan_objmgr_psoc *psoc, + void *arg) +{ + return ucfg_mc_cp_stats_suspend_req_handler(psoc); +} + +void ucfg_mc_cp_stats_register_pmo_handler(void) +{ + pmo_register_suspend_handler(WLAN_UMAC_COMP_CP_STATS, + ucfg_mc_cp_stats_suspend_handler, NULL); + pmo_register_resume_handler(WLAN_UMAC_COMP_CP_STATS, + ucfg_mc_cp_stats_resume_handler, NULL); +} +#endif diff --git a/components/target_if/cp_stats/src/target_if_mc_cp_stats.c b/components/target_if/cp_stats/src/target_if_mc_cp_stats.c new file mode 100644 index 000000000000..bd6f65981f4b --- /dev/null +++ b/components/target_if/cp_stats/src/target_if_mc_cp_stats.c @@ -0,0 +1,729 @@ +/* + * Copyright (c) 2013-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: target_if_cp_stats.c + * + * This file provide definition for APIs registered through lmac Tx Ops + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../../../umac/cmn_services/utils/inc/wlan_utility.h" + +#ifdef WLAN_FEATURE_MIB_STATS +static void target_if_cp_stats_free_mib_stats(struct stats_event *ev) +{ + qdf_mem_free(ev->mib_stats); + ev->mib_stats = NULL; +} +#else +static void target_if_cp_stats_free_mib_stats(struct stats_event *ev) +{ +} +#endif +static void target_if_cp_stats_free_stats_event(struct stats_event *ev) +{ + qdf_mem_free(ev->pdev_stats); + ev->pdev_stats = NULL; + qdf_mem_free(ev->peer_stats); + ev->peer_stats = NULL; + qdf_mem_free(ev->peer_adv_stats); + ev->peer_adv_stats = NULL; + qdf_mem_free(ev->peer_extended_stats); + ev->peer_extended_stats = NULL; + qdf_mem_free(ev->cca_stats); + ev->cca_stats = NULL; + qdf_mem_free(ev->vdev_summary_stats); + ev->vdev_summary_stats = NULL; + qdf_mem_free(ev->vdev_chain_rssi); + ev->vdev_chain_rssi = NULL; + target_if_cp_stats_free_mib_stats(ev); +} + +static QDF_STATUS target_if_cp_stats_extract_pdev_stats( + struct wmi_unified *wmi_hdl, + wmi_host_stats_event *stats_param, + struct stats_event *ev, + uint8_t *data) +{ + uint32_t i; + QDF_STATUS status; + wmi_host_pdev_stats pdev_stats; + + ev->num_pdev_stats = stats_param->num_pdev_stats; + if (!ev->num_pdev_stats) + return QDF_STATUS_SUCCESS; + + /* + * num_pdev_stats is validated within function wmi_extract_stats_param + * which is called to populated wmi_host_stats_event stats_param + */ + ev->pdev_stats = qdf_mem_malloc(sizeof(*ev->pdev_stats) * + ev->num_pdev_stats); + if (!ev->pdev_stats) + return QDF_STATUS_E_NOMEM; + + for (i = 0; i < ev->num_pdev_stats; i++) { + status = wmi_extract_pdev_stats(wmi_hdl, data, i, &pdev_stats); + if (QDF_IS_STATUS_ERROR(status)) { + cp_stats_err("wmi_extract_pdev_stats failed"); + return status; + } + ev->pdev_stats[i].max_pwr = pdev_stats.chan_tx_pwr; + } + + return QDF_STATUS_SUCCESS; +} + +static void target_if_cp_stats_extract_peer_extd_stats( + struct wmi_unified *wmi_hdl, + wmi_host_stats_event *stats_param, + struct stats_event *ev, + uint8_t *data) + +{ + QDF_STATUS status; + uint32_t i; + wmi_host_peer_extd_stats peer_extd_stats; + + if (!stats_param->num_peer_extd_stats) + return; + + ev->peer_extended_stats = + qdf_mem_malloc(sizeof(*ev->peer_extended_stats) * + stats_param->num_peer_extd_stats); + if (!ev->peer_extended_stats) + return; + + ev->num_peer_extd_stats = stats_param->num_peer_extd_stats; + + for (i = 0; i < ev->num_peer_extd_stats; i++) { + status = wmi_extract_peer_extd_stats(wmi_hdl, data, i, + &peer_extd_stats); + if (QDF_IS_STATUS_ERROR(status)) { + cp_stats_err("wmi_extract_peer_extd_stats failed"); + continue; + } + WMI_MAC_ADDR_TO_CHAR_ARRAY( + &peer_extd_stats.peer_macaddr, + ev->peer_extended_stats[i].peer_macaddr); + ev->peer_extended_stats[i].rx_mc_bc_cnt = + peer_extd_stats.rx_mc_bc_cnt; + } +} + +static QDF_STATUS target_if_cp_stats_extract_peer_stats( + struct wmi_unified *wmi_hdl, + wmi_host_stats_event *stats_param, + struct stats_event *ev, + uint8_t *data) +{ + uint32_t i; + QDF_STATUS status; + wmi_host_peer_stats peer_stats; + bool db2dbm_enabled; + struct wmi_host_peer_adv_stats *peer_adv_stats; + + /* Extract peer_stats */ + if (!stats_param->num_peer_stats) + return QDF_STATUS_SUCCESS; + + ev->peer_stats = qdf_mem_malloc(sizeof(*ev->peer_stats) * + stats_param->num_peer_stats); + if (!ev->peer_stats) + return QDF_STATUS_E_NOMEM; + ev->num_peer_stats = stats_param->num_peer_stats; + + db2dbm_enabled = wmi_service_enabled(wmi_hdl, + wmi_service_hw_db2dbm_support); + for (i = 0; i < ev->num_peer_stats; i++) { + status = wmi_extract_peer_stats(wmi_hdl, data, i, &peer_stats); + if (QDF_IS_STATUS_ERROR(status)) { + cp_stats_err("wmi_extract_peer_stats failed"); + continue; + } + WMI_MAC_ADDR_TO_CHAR_ARRAY(&peer_stats.peer_macaddr, + ev->peer_stats[i].peer_macaddr); + ev->peer_stats[i].tx_rate = peer_stats.peer_tx_rate; + ev->peer_stats[i].rx_rate = peer_stats.peer_rx_rate; + if (db2dbm_enabled) + ev->peer_stats[i].peer_rssi = peer_stats.peer_rssi; + else + ev->peer_stats[i].peer_rssi = peer_stats.peer_rssi + + TGT_NOISE_FLOOR_DBM; + } + + target_if_cp_stats_extract_peer_extd_stats(wmi_hdl, stats_param, ev, + data); + + /* Extract peer_adv_stats */ + ev->num_peer_adv_stats = stats_param->num_peer_adv_stats; + if (!ev->num_peer_adv_stats) + return QDF_STATUS_SUCCESS; + + ev->peer_adv_stats = qdf_mem_malloc(sizeof(*ev->peer_adv_stats) * + ev->num_peer_adv_stats); + if (!ev->peer_adv_stats) + return QDF_STATUS_E_NOMEM; + + peer_adv_stats = qdf_mem_malloc(sizeof(*peer_adv_stats) * + ev->num_peer_adv_stats); + if (!peer_adv_stats) { + qdf_mem_free(ev->peer_adv_stats); + return QDF_STATUS_E_NOMEM; + } + + status = wmi_extract_peer_adv_stats(wmi_hdl, data, peer_adv_stats); + if (QDF_IS_STATUS_ERROR(status)) { + cp_stats_err("wmi_extract_peer_stats failed"); + qdf_mem_free(peer_adv_stats); + qdf_mem_free(ev->peer_adv_stats); + ev->peer_adv_stats = NULL; + return QDF_STATUS_SUCCESS; + } + + for (i = 0; i < ev->num_peer_adv_stats; i++) { + qdf_mem_copy(&ev->peer_adv_stats[i].peer_macaddr, + &peer_adv_stats[i].peer_macaddr, + QDF_MAC_ADDR_SIZE); + ev->peer_adv_stats[i].fcs_count = peer_adv_stats[i].fcs_count; + ev->peer_adv_stats[i].rx_bytes = peer_adv_stats[i].rx_bytes; + ev->peer_adv_stats[i].rx_count = peer_adv_stats[i].rx_count; + } + qdf_mem_free(peer_adv_stats); + return QDF_STATUS_SUCCESS; +} + +static QDF_STATUS target_if_cp_stats_extract_cca_stats( + struct wmi_unified *wmi_hdl, + wmi_host_stats_event *stats_param, + struct stats_event *ev, uint8_t *data) +{ + QDF_STATUS status; + struct wmi_host_congestion_stats stats = {0}; + + status = wmi_extract_cca_stats(wmi_hdl, data, &stats); + if (QDF_IS_STATUS_ERROR(status)) + return QDF_STATUS_SUCCESS; + + ev->cca_stats = qdf_mem_malloc(sizeof(*ev->cca_stats)); + if (!ev->cca_stats) + return QDF_STATUS_E_NOMEM; + + ev->cca_stats->vdev_id = stats.vdev_id; + ev->cca_stats->congestion = stats.congestion; + + return QDF_STATUS_SUCCESS; +} + +#ifdef WLAN_FEATURE_MIB_STATS +static QDF_STATUS target_if_cp_stats_extract_mib_stats( + struct wmi_unified *wmi_hdl, + wmi_host_stats_event *stats_param, + struct stats_event *ev, uint8_t *data) +{ + QDF_STATUS status; + + if (!stats_param->num_mib_stats) + return QDF_STATUS_SUCCESS; + + if (stats_param->num_mib_stats != MAX_MIB_STATS || + (stats_param->num_mib_extd_stats && + stats_param->num_mib_extd_stats != MAX_MIB_STATS)) { + cp_stats_err("number of mib stats wrong, num_mib_stats %d, num_mib_extd_stats %d", + stats_param->num_mib_stats, + stats_param->num_mib_extd_stats); + return QDF_STATUS_E_INVAL; + } + + ev->num_mib_stats = stats_param->num_mib_stats; + + ev->mib_stats = qdf_mem_malloc(sizeof(*ev->mib_stats)); + if (!ev->mib_stats) + return QDF_STATUS_E_NOMEM; + + status = wmi_extract_mib_stats(wmi_hdl, data, ev->mib_stats); + if (QDF_IS_STATUS_ERROR(status)) { + cp_stats_err("wmi_extract_mib_stats failed"); + return status; + } + + return QDF_STATUS_SUCCESS; +} +#else +static QDF_STATUS target_if_cp_stats_extract_mib_stats( + struct wmi_unified *wmi_hdl, + wmi_host_stats_event *stats_param, + struct stats_event *ev, uint8_t *data) +{ + return QDF_STATUS_SUCCESS; +} +#endif + +static QDF_STATUS target_if_cp_stats_extract_vdev_summary_stats( + struct wmi_unified *wmi_hdl, + wmi_host_stats_event *stats_param, + struct stats_event *ev, uint8_t *data) +{ + uint32_t i, j; + QDF_STATUS status; + int32_t bcn_snr, dat_snr; + wmi_host_vdev_stats vdev_stats; + bool db2dbm_enabled; + + ev->num_summary_stats = stats_param->num_vdev_stats; + if (!ev->num_summary_stats) + return QDF_STATUS_SUCCESS; + + ev->vdev_summary_stats = qdf_mem_malloc(sizeof(*ev->vdev_summary_stats) + * ev->num_summary_stats); + + if (!ev->vdev_summary_stats) + return QDF_STATUS_E_NOMEM; + + db2dbm_enabled = wmi_service_enabled(wmi_hdl, + wmi_service_hw_db2dbm_support); + for (i = 0; i < ev->num_summary_stats; i++) { + status = wmi_extract_vdev_stats(wmi_hdl, data, i, &vdev_stats); + if (QDF_IS_STATUS_ERROR(status)) + continue; + + bcn_snr = vdev_stats.vdev_snr.bcn_snr; + dat_snr = vdev_stats.vdev_snr.dat_snr; + ev->vdev_summary_stats[i].vdev_id = vdev_stats.vdev_id; + + for (j = 0; j < 4; j++) { + ev->vdev_summary_stats[i].stats.tx_frm_cnt[j] + = vdev_stats.tx_frm_cnt[j]; + ev->vdev_summary_stats[i].stats.fail_cnt[j] + = vdev_stats.fail_cnt[j]; + ev->vdev_summary_stats[i].stats.multiple_retry_cnt[j] + = vdev_stats.multiple_retry_cnt[j]; + } + + ev->vdev_summary_stats[i].stats.rx_frm_cnt = + vdev_stats.rx_frm_cnt; + ev->vdev_summary_stats[i].stats.rx_error_cnt = + vdev_stats.rx_err_cnt; + ev->vdev_summary_stats[i].stats.rx_discard_cnt = + vdev_stats.rx_discard_cnt; + ev->vdev_summary_stats[i].stats.ack_fail_cnt = + vdev_stats.ack_fail_cnt; + ev->vdev_summary_stats[i].stats.rts_succ_cnt = + vdev_stats.rts_succ_cnt; + ev->vdev_summary_stats[i].stats.rts_fail_cnt = + vdev_stats.rts_fail_cnt; + /* Update SNR and RSSI in SummaryStats */ + wlan_util_stats_get_rssi(db2dbm_enabled, bcn_snr, dat_snr, + &ev->vdev_summary_stats[i].stats.rssi); + ev->vdev_summary_stats[i].stats.snr = + ev->vdev_summary_stats[i].stats.rssi - + TGT_NOISE_FLOOR_DBM; + } + + return QDF_STATUS_SUCCESS; +} + + +static QDF_STATUS target_if_cp_stats_extract_vdev_chain_rssi_stats( + struct wmi_unified *wmi_hdl, + wmi_host_stats_event *stats_param, + struct stats_event *ev, uint8_t *data) +{ + uint32_t i, j; + QDF_STATUS status; + int32_t bcn_snr, dat_snr; + struct wmi_host_per_chain_rssi_stats rssi_stats; + bool db2dbm_enabled; + + ev->num_chain_rssi_stats = stats_param->num_rssi_stats; + if (!ev->num_chain_rssi_stats) + return QDF_STATUS_SUCCESS; + + ev->vdev_chain_rssi = qdf_mem_malloc(sizeof(*ev->vdev_chain_rssi) * + ev->num_chain_rssi_stats); + if (!ev->vdev_chain_rssi) + return QDF_STATUS_E_NOMEM; + + db2dbm_enabled = wmi_service_enabled(wmi_hdl, + wmi_service_hw_db2dbm_support); + for (i = 0; i < ev->num_chain_rssi_stats; i++) { + status = wmi_extract_per_chain_rssi_stats(wmi_hdl, data, i, + &rssi_stats); + if (QDF_IS_STATUS_ERROR(status)) + continue; + ev->vdev_chain_rssi[i].vdev_id = rssi_stats.vdev_id; + + for (j = 0; j < MAX_NUM_CHAINS; j++) { + dat_snr = rssi_stats.rssi_avg_data[j]; + bcn_snr = rssi_stats.rssi_avg_beacon[j]; + cp_stats_nofl_debug("Chain %d SNR bcn: %d data: %d", j, + bcn_snr, dat_snr); + /* + * Get the absolute rssi value from the current rssi + * value the snr value is hardcoded into 0 in the + * qcacld-new/CORE stack + */ + wlan_util_stats_get_rssi(db2dbm_enabled, bcn_snr, + dat_snr, + &ev->vdev_chain_rssi[i]. + chain_rssi[j]); + } + } + + return QDF_STATUS_SUCCESS; +} + +static QDF_STATUS target_if_cp_stats_extract_event(struct wmi_unified *wmi_hdl, + struct stats_event *ev, + uint8_t *data) +{ + QDF_STATUS status; + wmi_host_stats_event stats_param = {0}; + + status = wmi_extract_stats_param(wmi_hdl, data, &stats_param); + if (QDF_IS_STATUS_ERROR(status)) { + cp_stats_err("stats param extract failed: %d", status); + return status; + } + cp_stats_nofl_debug("num: pdev: %d, pdev_extd: %d, vdev: %d, peer: %d," + "peer_extd: %d rssi: %d, mib %d, mib_extd %d, " + "bcnflt: %d, channel: %d, bcn: %d, peer_extd2: %d," + "last_event: %x", + stats_param.num_pdev_stats, + stats_param.num_pdev_ext_stats, + stats_param.num_vdev_stats, + stats_param.num_peer_stats, + stats_param.num_peer_extd_stats, + stats_param.num_rssi_stats, + stats_param.num_mib_stats, + stats_param.num_mib_extd_stats, + stats_param.num_bcnflt_stats, + stats_param.num_chan_stats, + stats_param.num_bcn_stats, + stats_param.num_peer_adv_stats, stats_param.last_event); + + ev->last_event = stats_param.last_event; + status = target_if_cp_stats_extract_pdev_stats(wmi_hdl, &stats_param, + ev, data); + if (QDF_IS_STATUS_ERROR(status)) + return status; + + status = target_if_cp_stats_extract_peer_stats(wmi_hdl, &stats_param, + ev, data); + if (QDF_IS_STATUS_ERROR(status)) + return status; + + status = target_if_cp_stats_extract_cca_stats(wmi_hdl, &stats_param, + ev, data); + if (QDF_IS_STATUS_ERROR(status)) + return status; + + status = target_if_cp_stats_extract_vdev_summary_stats(wmi_hdl, + &stats_param, + ev, data); + if (QDF_IS_STATUS_ERROR(status)) + return status; + + status = target_if_cp_stats_extract_vdev_chain_rssi_stats(wmi_hdl, + &stats_param, + ev, data); + if (QDF_IS_STATUS_ERROR(status)) + return status; + + status = target_if_cp_stats_extract_mib_stats(wmi_hdl, + &stats_param, + ev, data); + if (QDF_IS_STATUS_ERROR(status)) + return status; + + return QDF_STATUS_SUCCESS; +} + +/** + * target_if_mc_cp_stats_stats_event_handler() - function to handle stats event + * from firmware. + * @scn: scn handle + * @data: data buffer for event + * @datalen: data length + * + * Return: status of operation. + */ +static int target_if_mc_cp_stats_stats_event_handler(ol_scn_t scn, + uint8_t *data, + uint32_t datalen) +{ + QDF_STATUS status; + struct stats_event ev = {0}; + struct wlan_objmgr_psoc *psoc; + struct wmi_unified *wmi_handle; + struct wlan_lmac_if_cp_stats_rx_ops *rx_ops; + + if (!scn || !data) { + cp_stats_err("scn: 0x%pK, data: 0x%pK", scn, data); + return -EINVAL; + } + psoc = target_if_get_psoc_from_scn_hdl(scn); + if (!psoc) { + cp_stats_err("null psoc"); + return -EINVAL; + } + + rx_ops = target_if_cp_stats_get_rx_ops(psoc); + if (!rx_ops || !rx_ops->process_stats_event) { + cp_stats_err("callback not registered"); + return -EINVAL; + } + + wmi_handle = get_wmi_unified_hdl_from_psoc(psoc); + if (!wmi_handle) { + cp_stats_err("wmi_handle is null"); + return -EINVAL; + } + + status = target_if_cp_stats_extract_event(wmi_handle, &ev, data); + if (QDF_IS_STATUS_ERROR(status)) { + cp_stats_err("extract event failed"); + goto end; + } + + status = rx_ops->process_stats_event(psoc, &ev); + +end: + target_if_cp_stats_free_stats_event(&ev); + + return qdf_status_to_os_return(status); +} + +static void target_if_cp_stats_inc_wake_lock_stats(uint32_t reason, + struct wake_lock_stats *stats, + uint32_t *unspecified_wake_count) +{ + switch (reason) { + case WOW_REASON_UNSPECIFIED: + (*unspecified_wake_count)++; + break; + + case WOW_REASON_ASSOC_REQ_RECV: + stats->mgmt_assoc++; + break; + + case WOW_REASON_DISASSOC_RECVD: + stats->mgmt_disassoc++; + break; + + case WOW_REASON_ASSOC_RES_RECV: + stats->mgmt_assoc_resp++; + break; + + case WOW_REASON_REASSOC_REQ_RECV: + stats->mgmt_reassoc++; + break; + + case WOW_REASON_REASSOC_RES_RECV: + stats->mgmt_reassoc_resp++; + break; + + case WOW_REASON_AUTH_REQ_RECV: + stats->mgmt_auth++; + break; + + case WOW_REASON_DEAUTH_RECVD: + stats->mgmt_deauth++; + break; + + case WOW_REASON_ACTION_FRAME_RECV: + stats->mgmt_action++; + break; + + case WOW_REASON_NLOD: + stats->pno_match_wake_up_count++; + break; + + case WOW_REASON_NLO_SCAN_COMPLETE: + stats->pno_complete_wake_up_count++; + break; + + case WOW_REASON_LOW_RSSI: + stats->low_rssi_wake_up_count++; + break; + + case WOW_REASON_EXTSCAN: + stats->gscan_wake_up_count++; + break; + + case WOW_REASON_RSSI_BREACH_EVENT: + stats->rssi_breach_wake_up_count++; + break; + + case WOW_REASON_OEM_RESPONSE_EVENT: + stats->oem_response_wake_up_count++; + break; + + case WOW_REASON_11D_SCAN: + stats->scan_11d++; + break; + + case WOW_REASON_CHIP_POWER_FAILURE_DETECT: + stats->pwr_save_fail_detected++; + break; + + default: + break; + } +} + +static QDF_STATUS +target_if_cp_stats_register_event_handler(struct wlan_objmgr_psoc *psoc) +{ + int ret_val; + struct wmi_unified *wmi_handle; + + if (!psoc) { + cp_stats_err("PSOC is NULL!"); + return QDF_STATUS_E_NULL_VALUE; + } + + wmi_handle = get_wmi_unified_hdl_from_psoc(psoc); + if (!wmi_handle) { + cp_stats_err("wmi_handle is null"); + return QDF_STATUS_E_INVAL; + } + + ret_val = wmi_unified_register_event_handler( + wmi_handle, + wmi_update_stats_event_id, + target_if_mc_cp_stats_stats_event_handler, + WMI_RX_WORK_CTX); + if (ret_val) + cp_stats_err("Failed to register stats event cb"); + + return qdf_status_from_os_return(ret_val); +} + +static QDF_STATUS +target_if_cp_stats_unregister_event_handler(struct wlan_objmgr_psoc *psoc) +{ + struct wmi_unified *wmi_handle; + + if (!psoc) { + cp_stats_err("PSOC is NULL!"); + return QDF_STATUS_E_INVAL; + } + + wmi_handle = get_wmi_unified_hdl_from_psoc(psoc); + if (!wmi_handle) { + cp_stats_err("wmi_handle is null"); + return QDF_STATUS_E_INVAL; + } + wmi_unified_unregister_event_handler(wmi_handle, + wmi_update_stats_event_id); + + return QDF_STATUS_SUCCESS; +} + +static uint32_t get_stats_id(enum stats_req_type type) +{ + switch (type) { + default: + break; + case TYPE_CONNECTION_TX_POWER: + return WMI_REQUEST_PDEV_STAT; + case TYPE_PEER_STATS: + return WMI_REQUEST_PEER_STAT | WMI_REQUEST_PEER_EXTD_STAT; + case TYPE_STATION_STATS: + return (WMI_REQUEST_AP_STAT | + WMI_REQUEST_PEER_STAT | + WMI_REQUEST_VDEV_STAT | + WMI_REQUEST_PDEV_STAT | + WMI_REQUEST_PEER_EXTD2_STAT | + WMI_REQUEST_RSSI_PER_CHAIN_STAT); + case TYPE_MIB_STATS: + return (WMI_REQUEST_MIB_STAT | WMI_REQUEST_MIB_EXTD_STAT); + } + + return 0; +} + +/** + * target_if_cp_stats_send_stats_req() - API to send stats request to wmi + * @psoc: pointer to psoc object + * @req: pointer to object containing stats request parameters + * + * Return: status of operation. + */ +static QDF_STATUS target_if_cp_stats_send_stats_req( + struct wlan_objmgr_psoc *psoc, + enum stats_req_type type, + struct request_info *req) + +{ + struct wmi_unified *wmi_handle; + struct stats_request_params param = {0}; + + wmi_handle = get_wmi_unified_hdl_from_psoc(psoc); + if (!wmi_handle) { + cp_stats_err("wmi_handle is null."); + return QDF_STATUS_E_NULL_VALUE; + } + /* refer (WMI_REQUEST_STATS_CMDID) */ + param.stats_id = get_stats_id(type); + param.vdev_id = req->vdev_id; + param.pdev_id = req->pdev_id; + + return wmi_unified_stats_request_send(wmi_handle, req->peer_mac_addr, + ¶m); +} + +QDF_STATUS +target_if_cp_stats_register_tx_ops(struct wlan_lmac_if_tx_ops *tx_ops) +{ + struct wlan_lmac_if_cp_stats_tx_ops *cp_stats_tx_ops; + + if (!tx_ops) { + cp_stats_err("lmac tx ops is NULL!"); + return QDF_STATUS_E_INVAL; + } + + cp_stats_tx_ops = &tx_ops->cp_stats_tx_ops; + if (!cp_stats_tx_ops) { + cp_stats_err("lmac tx ops is NULL!"); + return QDF_STATUS_E_FAILURE; + } + + cp_stats_tx_ops->cp_stats_attach = + target_if_cp_stats_register_event_handler; + cp_stats_tx_ops->cp_stats_detach = + target_if_cp_stats_unregister_event_handler; + cp_stats_tx_ops->inc_wake_lock_stats = + target_if_cp_stats_inc_wake_lock_stats; + cp_stats_tx_ops->send_req_stats = target_if_cp_stats_send_stats_req; + + return QDF_STATUS_SUCCESS; +} + diff --git a/os_if/cp_stats/src/wlan_cfg80211_mc_cp_stats.c b/os_if/cp_stats/src/wlan_cfg80211_mc_cp_stats.c new file mode 100644 index 000000000000..bd880a2a1e94 --- /dev/null +++ b/os_if/cp_stats/src/wlan_cfg80211_mc_cp_stats.c @@ -0,0 +1,734 @@ +/* + * Copyright (c) 2011-2020 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_cfg80211_mc_cp_stats.c + * + * This file provide definitions to cp stats supported cfg80211 cmd handlers + */ + +#include +#include +#include +#include +#include "wlan_osif_request_manager.h" +#include "wlan_objmgr_peer_obj.h" + +/* max time in ms, caller may wait for stats request get serviced */ +#define CP_STATS_WAIT_TIME_STAT 800 + +#ifdef WLAN_FEATURE_MIB_STATS +/** + * wlan_free_mib_stats() - free allocations for mib stats + * @stats: Pointer to stats event statucture + * + * Return: None + */ +static void wlan_free_mib_stats(struct stats_event *stats) +{ + qdf_mem_free(stats->mib_stats); + stats->mib_stats = NULL; +} +#else +static void wlan_free_mib_stats(struct stats_event *stats) +{ +} +#endif + +/** + * wlan_cfg80211_mc_cp_stats_dealloc() - callback to free priv + * allocations for stats + * @priv: Pointer to priv data statucture + * + * Return: None + */ +static void wlan_cfg80211_mc_cp_stats_dealloc(void *priv) +{ + struct stats_event *stats = priv; + + if (!stats) { + osif_err("stats is NULL"); + return; + } + + qdf_mem_free(stats->pdev_stats); + qdf_mem_free(stats->peer_stats); + qdf_mem_free(stats->cca_stats); + qdf_mem_free(stats->vdev_summary_stats); + qdf_mem_free(stats->vdev_chain_rssi); + qdf_mem_free(stats->peer_adv_stats); + wlan_free_mib_stats(stats); +} + +/** + * wlan_cfg80211_mc_cp_stats_send_wake_lock_stats() - API to send wakelock stats + * @wiphy: wiphy pointer + * @stats: stats data to be sent + * + * Return: 0 on success, error number otherwise. + */ +static int wlan_cfg80211_mc_cp_stats_send_wake_lock_stats(struct wiphy *wiphy, + struct wake_lock_stats *stats) +{ + struct sk_buff *skb; + uint32_t nl_buf_len; + uint32_t icmpv6_cnt; + uint32_t ipv6_rx_multicast_addr_cnt; + uint32_t total_rx_data_wake, rx_multicast_cnt; + + nl_buf_len = NLMSG_HDRLEN; + nl_buf_len += QCA_WLAN_VENDOR_GET_WAKE_STATS_MAX * + (NLMSG_HDRLEN + sizeof(uint32_t)); + + skb = wlan_cfg80211_vendor_cmd_alloc_reply_skb(wiphy, nl_buf_len); + + if (!skb) { + osif_err("cfg80211_vendor_cmd_alloc_reply_skb failed"); + return -ENOMEM; + } + + osif_debug("wow_ucast_wake_up_count %d", + stats->ucast_wake_up_count); + osif_debug("wow_bcast_wake_up_count %d", + stats->bcast_wake_up_count); + osif_debug("wow_ipv4_mcast_wake_up_count %d", + stats->ipv4_mcast_wake_up_count); + osif_debug("wow_ipv6_mcast_wake_up_count %d", + stats->ipv6_mcast_wake_up_count); + osif_debug("wow_ipv6_mcast_ra_stats %d", + stats->ipv6_mcast_ra_stats); + osif_debug("wow_ipv6_mcast_ns_stats %d", + stats->ipv6_mcast_ns_stats); + osif_debug("wow_ipv6_mcast_na_stats %d", + stats->ipv6_mcast_na_stats); + osif_debug("wow_icmpv4_count %d", + stats->icmpv4_count); + osif_debug("wow_icmpv6_count %d", + stats->icmpv6_count); + osif_debug("wow_rssi_breach_wake_up_count %d", + stats->rssi_breach_wake_up_count); + osif_debug("wow_low_rssi_wake_up_count %d", + stats->low_rssi_wake_up_count); + osif_debug("wow_gscan_wake_up_count %d", + stats->gscan_wake_up_count); + osif_debug("wow_pno_complete_wake_up_count %d", + stats->pno_complete_wake_up_count); + osif_debug("wow_pno_match_wake_up_count %d", + stats->pno_match_wake_up_count); + + ipv6_rx_multicast_addr_cnt = stats->ipv6_mcast_wake_up_count; + icmpv6_cnt = stats->icmpv6_count; + rx_multicast_cnt = stats->ipv4_mcast_wake_up_count + + ipv6_rx_multicast_addr_cnt; + total_rx_data_wake = stats->ucast_wake_up_count + + stats->bcast_wake_up_count + rx_multicast_cnt; + + if (nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_TOTAL_CMD_EVENT_WAKE, 0) || + nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_CMD_EVENT_WAKE_CNT_PTR, 0) || + nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_CMD_EVENT_WAKE_CNT_SZ, 0) || + nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_TOTAL_DRIVER_FW_LOCAL_WAKE, + 0) || + nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_DRIVER_FW_LOCAL_WAKE_CNT_PTR, + 0) || + nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_DRIVER_FW_LOCAL_WAKE_CNT_SZ, + 0) || + nla_put_u32(skb, + QCA_WLAN_VENDOR_ATTR_TOTAL_RX_DATA_WAKE, + total_rx_data_wake) || + nla_put_u32(skb, + QCA_WLAN_VENDOR_ATTR_RX_UNICAST_CNT, + stats->ucast_wake_up_count) || + nla_put_u32(skb, + QCA_WLAN_VENDOR_ATTR_RX_MULTICAST_CNT, + rx_multicast_cnt) || + nla_put_u32(skb, + QCA_WLAN_VENDOR_ATTR_RX_BROADCAST_CNT, + stats->bcast_wake_up_count) || + nla_put_u32(skb, + QCA_WLAN_VENDOR_ATTR_ICMP_PKT, + stats->icmpv4_count) || + nla_put_u32(skb, + QCA_WLAN_VENDOR_ATTR_ICMP6_PKT, + icmpv6_cnt) || + nla_put_u32(skb, + QCA_WLAN_VENDOR_ATTR_ICMP6_RA, + stats->ipv6_mcast_ra_stats) || + nla_put_u32(skb, + QCA_WLAN_VENDOR_ATTR_ICMP6_NA, + stats->ipv6_mcast_na_stats) || + nla_put_u32(skb, + QCA_WLAN_VENDOR_ATTR_ICMP6_NS, + stats->ipv6_mcast_ns_stats) || + nla_put_u32(skb, + QCA_WLAN_VENDOR_ATTR_ICMP4_RX_MULTICAST_CNT, + stats->ipv4_mcast_wake_up_count) || + nla_put_u32(skb, + QCA_WLAN_VENDOR_ATTR_ICMP6_RX_MULTICAST_CNT, + ipv6_rx_multicast_addr_cnt) || + nla_put_u32(skb, + QCA_WLAN_VENDOR_ATTR_RSSI_BREACH_CNT, + stats->rssi_breach_wake_up_count) || + nla_put_u32(skb, + QCA_WLAN_VENDOR_ATTR_LOW_RSSI_CNT, + stats->low_rssi_wake_up_count) || + nla_put_u32(skb, + QCA_WLAN_VENDOR_ATTR_GSCAN_CNT, + stats->gscan_wake_up_count) || + nla_put_u32(skb, + QCA_WLAN_VENDOR_ATTR_PNO_COMPLETE_CNT, + stats->pno_complete_wake_up_count) || + nla_put_u32(skb, + QCA_WLAN_VENDOR_ATTR_PNO_MATCH_CNT, + stats->pno_match_wake_up_count)) { + osif_err("nla put fail"); + goto nla_put_failure; + } + + wlan_cfg80211_vendor_cmd_reply(skb); + return 0; + +nla_put_failure: + wlan_cfg80211_vendor_free_skb(skb); + return -EINVAL; +} + +int wlan_cfg80211_mc_cp_stats_get_wakelock_stats(struct wlan_objmgr_psoc *psoc, + struct wiphy *wiphy) +{ + /* refer __wlan_hdd_cfg80211_get_wakelock_stats */ + QDF_STATUS status; + struct wake_lock_stats stats = {0}; + + status = ucfg_mc_cp_stats_get_psoc_wake_lock_stats(psoc, &stats); + if (QDF_IS_STATUS_ERROR(status)) + return qdf_status_to_os_return(status); + + return wlan_cfg80211_mc_cp_stats_send_wake_lock_stats(wiphy, &stats); +} + +struct tx_power_priv { + int dbm; +}; + +/** + * get_tx_power_cb() - "Get tx power" callback function + * @tx_power: tx_power + * @cookie: a cookie for the request context + * + * Return: None + */ +static void get_tx_power_cb(int tx_power, void *cookie) +{ + struct osif_request *request; + struct tx_power_priv *priv; + + request = osif_request_get(cookie); + if (!request) { + osif_err("Obsolete request"); + return; + } + + priv = osif_request_priv(request); + priv->dbm = tx_power; + osif_request_complete(request); + osif_request_put(request); +} + +int wlan_cfg80211_mc_cp_stats_get_tx_power(struct wlan_objmgr_vdev *vdev, + int *dbm) +{ + int ret = 0; + void *cookie; + QDF_STATUS status; + struct request_info info = {0}; + struct wlan_objmgr_peer *peer; + struct tx_power_priv *priv = NULL; + struct osif_request *request = NULL; + static const struct osif_request_params params = { + .priv_size = sizeof(*priv), + .timeout_ms = CP_STATS_WAIT_TIME_STAT, + }; + + request = osif_request_alloc(¶ms); + if (!request) { + osif_err("Request allocation failure, return cached value"); + goto fetch_tx_power; + } + + cookie = osif_request_cookie(request); + info.cookie = cookie; + info.u.get_tx_power_cb = get_tx_power_cb; + info.vdev_id = wlan_vdev_get_id(vdev); + info.pdev_id = wlan_objmgr_pdev_get_pdev_id(wlan_vdev_get_pdev(vdev)); + peer = wlan_objmgr_vdev_try_get_bsspeer(vdev, WLAN_CP_STATS_ID); + if (!peer) { + ret = -EINVAL; + goto peer_is_null; + } + qdf_mem_copy(info.peer_mac_addr, peer->macaddr, QDF_MAC_ADDR_SIZE); + + wlan_objmgr_peer_release_ref(peer, WLAN_CP_STATS_ID); + + status = ucfg_mc_cp_stats_send_stats_request(vdev, + TYPE_CONNECTION_TX_POWER, + &info); + if (QDF_IS_STATUS_ERROR(status)) { + osif_err("wlan_mc_cp_stats_request_tx_power status: %d", + status); + ret = qdf_status_to_os_return(status); + } else { + ret = osif_request_wait_for_response(request); + if (ret) + osif_err("wait failed or timed out ret: %d", ret); + else + priv = osif_request_priv(request); + } + +fetch_tx_power: + if (priv) { + *dbm = priv->dbm; + } else { + status = ucfg_mc_cp_stats_get_tx_power(vdev, dbm); + if (QDF_IS_STATUS_ERROR(status)) { + osif_err("ucfg_mc_cp_stats_get_tx_power status: %d", + status); + ret = qdf_status_to_os_return(status); + } + } + +peer_is_null: + /* + * either we never sent a request, we sent a request and + * received a response or we sent a request and timed out. + * regardless we are done with the request. + */ + if (request) + osif_request_put(request); + + return ret; +} + +/** + * get_peer_rssi_cb() - get_peer_rssi_cb callback function + * @ev: peer stats buffer + * @cookie: a cookie for the request context + * + * Return: None + */ +static void get_peer_rssi_cb(struct stats_event *ev, void *cookie) +{ + struct stats_event *priv; + struct osif_request *request; + uint32_t rssi_size; + + request = osif_request_get(cookie); + if (!request) { + osif_err("Obsolete request"); + return; + } + + priv = osif_request_priv(request); + rssi_size = sizeof(*ev->peer_stats) * ev->num_peer_stats; + if (rssi_size == 0) { + osif_err("Invalid rssi stats"); + goto get_peer_rssi_cb_fail; + } + + priv->peer_stats = qdf_mem_malloc(rssi_size); + if (!priv->peer_stats) + goto get_peer_rssi_cb_fail; + + priv->num_peer_stats = ev->num_peer_stats; + qdf_mem_copy(priv->peer_stats, ev->peer_stats, rssi_size); + +get_peer_rssi_cb_fail: + osif_request_complete(request); + osif_request_put(request); +} + +struct stats_event * +wlan_cfg80211_mc_cp_stats_get_peer_rssi(struct wlan_objmgr_vdev *vdev, + uint8_t *mac_addr, + int *errno) +{ + void *cookie; + QDF_STATUS status; + struct stats_event *priv, *out; + struct request_info info = {0}; + struct osif_request *request = NULL; + static const struct osif_request_params params = { + .priv_size = sizeof(*priv), + .timeout_ms = CP_STATS_WAIT_TIME_STAT, + .dealloc = wlan_cfg80211_mc_cp_stats_dealloc, + }; + + out = qdf_mem_malloc(sizeof(*out)); + if (!out) { + *errno = -ENOMEM; + return NULL; + } + + request = osif_request_alloc(¶ms); + if (!request) { + osif_err("Request allocation failure, return cached value"); + *errno = -ENOMEM; + qdf_mem_free(out); + return NULL; + } + + cookie = osif_request_cookie(request); + priv = osif_request_priv(request); + info.cookie = cookie; + info.u.get_peer_rssi_cb = get_peer_rssi_cb; + info.vdev_id = wlan_vdev_get_id(vdev); + info.pdev_id = wlan_objmgr_pdev_get_pdev_id(wlan_vdev_get_pdev(vdev)); + qdf_mem_copy(info.peer_mac_addr, mac_addr, QDF_MAC_ADDR_SIZE); + status = ucfg_mc_cp_stats_send_stats_request(vdev, TYPE_PEER_STATS, + &info); + if (QDF_IS_STATUS_ERROR(status)) { + osif_err("stats req failed: %d", status); + *errno = qdf_status_to_os_return(status); + goto get_peer_rssi_fail; + } + + *errno = osif_request_wait_for_response(request); + if (*errno) { + osif_err("wait failed or timed out ret: %d", *errno); + goto get_peer_rssi_fail; + } + + if (!priv->peer_stats || priv->num_peer_stats == 0) { + osif_err("Invalid peer stats, count %d, data %pK", + priv->num_peer_stats, priv->peer_stats); + *errno = -EINVAL; + goto get_peer_rssi_fail; + } + out->num_peer_stats = priv->num_peer_stats; + out->peer_stats = priv->peer_stats; + priv->peer_stats = NULL; + osif_request_put(request); + + return out; + +get_peer_rssi_fail: + osif_request_put(request); + wlan_cfg80211_mc_cp_stats_free_stats_event(out); + + return NULL; +} + +/** + * get_station_stats_cb() - get_station_stats_cb callback function + * @ev: station stats buffer + * @cookie: a cookie for the request context + * + * Return: None + */ +static void get_station_stats_cb(struct stats_event *ev, void *cookie) +{ + struct stats_event *priv; + struct osif_request *request; + uint32_t summary_size, rssi_size, peer_adv_size; + + request = osif_request_get(cookie); + if (!request) { + osif_err("Obsolete request"); + return; + } + + priv = osif_request_priv(request); + summary_size = sizeof(*ev->vdev_summary_stats) * ev->num_summary_stats; + rssi_size = sizeof(*ev->vdev_chain_rssi) * ev->num_chain_rssi_stats; + peer_adv_size = sizeof(*ev->peer_adv_stats) * ev->num_peer_adv_stats; + + if (summary_size == 0 || rssi_size == 0) { + osif_err("Invalid stats, summary %d rssi %d", + summary_size, rssi_size); + goto station_stats_cb_fail; + } + if (priv->vdev_summary_stats || priv->vdev_chain_rssi || + priv->peer_adv_stats) { + osif_err("invalid context cookie %pK request %pK", + cookie, request); + goto station_stats_cb_fail; + } + + priv->vdev_summary_stats = qdf_mem_malloc(summary_size); + if (!priv->vdev_summary_stats) + goto station_stats_cb_fail; + + priv->vdev_chain_rssi = qdf_mem_malloc(rssi_size); + if (!priv->vdev_chain_rssi) + goto station_stats_cb_fail; + + if (peer_adv_size) { + priv->peer_adv_stats = qdf_mem_malloc(peer_adv_size); + if (!priv->peer_adv_stats) + goto station_stats_cb_fail; + + qdf_mem_copy(priv->peer_adv_stats, ev->peer_adv_stats, + peer_adv_size); + } + + priv->num_summary_stats = ev->num_summary_stats; + priv->num_chain_rssi_stats = ev->num_chain_rssi_stats; + priv->tx_rate = ev->tx_rate; + priv->rx_rate = ev->rx_rate; + priv->tx_rate_flags = ev->tx_rate_flags; + priv->num_peer_adv_stats = ev->num_peer_adv_stats; + qdf_mem_copy(priv->vdev_chain_rssi, ev->vdev_chain_rssi, rssi_size); + qdf_mem_copy(priv->vdev_summary_stats, ev->vdev_summary_stats, + summary_size); + +station_stats_cb_fail: + osif_request_complete(request); + osif_request_put(request); +} + +struct stats_event * +wlan_cfg80211_mc_cp_stats_get_station_stats(struct wlan_objmgr_vdev *vdev, + int *errno) +{ + void *cookie; + QDF_STATUS status; + struct stats_event *priv, *out; + struct wlan_objmgr_peer *peer; + struct osif_request *request; + struct request_info info = {0}; + static const struct osif_request_params params = { + .priv_size = sizeof(*priv), + .timeout_ms = 2 * CP_STATS_WAIT_TIME_STAT, + .dealloc = wlan_cfg80211_mc_cp_stats_dealloc, + }; + + osif_debug("Enter"); + + out = qdf_mem_malloc(sizeof(*out)); + if (!out) { + *errno = -ENOMEM; + return NULL; + } + + request = osif_request_alloc(¶ms); + if (!request) { + qdf_mem_free(out); + *errno = -ENOMEM; + return NULL; + } + + cookie = osif_request_cookie(request); + priv = osif_request_priv(request); + info.cookie = cookie; + info.u.get_station_stats_cb = get_station_stats_cb; + info.vdev_id = wlan_vdev_get_id(vdev); + info.pdev_id = wlan_objmgr_pdev_get_pdev_id(wlan_vdev_get_pdev(vdev)); + peer = wlan_objmgr_vdev_try_get_bsspeer(vdev, WLAN_CP_STATS_ID); + if (!peer) { + osif_err("peer is null"); + *errno = -EINVAL; + goto get_station_stats_fail; + } + qdf_mem_copy(info.peer_mac_addr, peer->macaddr, QDF_MAC_ADDR_SIZE); + + wlan_objmgr_peer_release_ref(peer, WLAN_CP_STATS_ID); + + status = ucfg_mc_cp_stats_send_stats_request(vdev, TYPE_STATION_STATS, + &info); + if (QDF_IS_STATUS_ERROR(status)) { + osif_err("Failed to send stats request status: %d", status); + *errno = qdf_status_to_os_return(status); + goto get_station_stats_fail; + } + + *errno = osif_request_wait_for_response(request); + if (*errno) { + osif_err("wait failed or timed out ret: %d", *errno); + goto get_station_stats_fail; + } + + if (!priv->vdev_summary_stats || !priv->vdev_chain_rssi || + priv->num_summary_stats == 0 || priv->num_chain_rssi_stats == 0) { + osif_err("Invalid stats"); + osif_err("summary %d:%pK, rssi %d:%pK", + priv->num_summary_stats, priv->vdev_summary_stats, + priv->num_chain_rssi_stats, priv->vdev_chain_rssi); + *errno = -EINVAL; + goto get_station_stats_fail; + } + + out->tx_rate = priv->tx_rate; + out->rx_rate = priv->rx_rate; + out->tx_rate_flags = priv->tx_rate_flags; + out->num_summary_stats = priv->num_summary_stats; + out->num_chain_rssi_stats = priv->num_chain_rssi_stats; + out->vdev_summary_stats = priv->vdev_summary_stats; + priv->vdev_summary_stats = NULL; + out->vdev_chain_rssi = priv->vdev_chain_rssi; + priv->vdev_chain_rssi = NULL; + out->num_peer_adv_stats = priv->num_peer_adv_stats; + if (priv->peer_adv_stats) + out->peer_adv_stats = priv->peer_adv_stats; + priv->peer_adv_stats = NULL; + osif_request_put(request); + + osif_debug("Exit"); + + return out; + +get_station_stats_fail: + osif_request_put(request); + wlan_cfg80211_mc_cp_stats_free_stats_event(out); + + osif_debug("Exit"); + + return NULL; +} + +#ifdef WLAN_FEATURE_MIB_STATS +/** + * get_mib_stats_cb() - get mib stats from fw callback function + * @ev: mib stats buffer + * @cookie: a cookie for the request context + * + * Return: None + */ +static void get_mib_stats_cb(struct stats_event *ev, void *cookie) +{ + struct stats_event *priv; + struct osif_request *request; + + request = osif_request_get(cookie); + if (!request) { + osif_err("Obsolete request"); + return; + } + + priv = osif_request_priv(request); + + priv->mib_stats = qdf_mem_malloc(sizeof(*ev->mib_stats)); + if (!priv->mib_stats) + goto get_mib_stats_cb_fail; + + priv->num_mib_stats = ev->num_mib_stats; + qdf_mem_copy(priv->mib_stats, ev->mib_stats, sizeof(*ev->mib_stats)); + +get_mib_stats_cb_fail: + osif_request_complete(request); + osif_request_put(request); +} + +struct stats_event * +wlan_cfg80211_mc_cp_stats_get_mib_stats(struct wlan_objmgr_vdev *vdev, + int *errno) +{ + void *cookie; + QDF_STATUS status; + struct stats_event *priv, *out; + struct wlan_objmgr_peer *peer; + struct osif_request *request; + struct request_info info = {0}; + static const struct osif_request_params params = { + .priv_size = sizeof(*priv), + .timeout_ms = 2 * CP_STATS_WAIT_TIME_STAT, + .dealloc = wlan_cfg80211_mc_cp_stats_dealloc, + }; + + out = qdf_mem_malloc(sizeof(*out)); + if (!out) { + *errno = -ENOMEM; + return NULL; + } + + request = osif_request_alloc(¶ms); + if (!request) { + qdf_mem_free(out); + *errno = -ENOMEM; + return NULL; + } + + cookie = osif_request_cookie(request); + priv = osif_request_priv(request); + info.cookie = cookie; + info.u.get_mib_stats_cb = get_mib_stats_cb; + info.vdev_id = wlan_vdev_get_id(vdev); + info.pdev_id = wlan_objmgr_pdev_get_pdev_id(wlan_vdev_get_pdev(vdev)); + peer = wlan_objmgr_vdev_try_get_bsspeer(vdev, WLAN_CP_STATS_ID); + if (!peer) { + osif_err("peer is null"); + *errno = -EINVAL; + goto get_mib_stats_fail; + } + qdf_mem_copy(info.peer_mac_addr, peer->macaddr, QDF_MAC_ADDR_SIZE); + + osif_debug("vdev id %d, pdev id %d, peer " QDF_MAC_ADDR_STR, + info.vdev_id, info.pdev_id, + QDF_MAC_ADDR_ARRAY(info.peer_mac_addr)); + + wlan_objmgr_peer_release_ref(peer, WLAN_CP_STATS_ID); + + status = ucfg_mc_cp_stats_send_stats_request(vdev, TYPE_MIB_STATS, + &info); + if (QDF_IS_STATUS_ERROR(status)) { + osif_err("Failed to send stats request status: %d", status); + *errno = qdf_status_to_os_return(status); + goto get_mib_stats_fail; + } + + *errno = osif_request_wait_for_response(request); + if (*errno) { + osif_err("wait failed or timed out ret: %d", *errno); + goto get_mib_stats_fail; + } + + if (!priv->mib_stats || priv->num_mib_stats == 0 ) { + osif_err("Invalid mib stats %d:%pK", + priv->num_mib_stats, priv->mib_stats); + *errno = -EINVAL; + goto get_mib_stats_fail; + } + + out->num_mib_stats = priv->num_mib_stats; + out->mib_stats = priv->mib_stats; + priv->mib_stats = NULL; + + osif_request_put(request); + + return out; + +get_mib_stats_fail: + osif_request_put(request); + wlan_cfg80211_mc_cp_stats_free_stats_event(out); + + return NULL; +} +#endif + +void wlan_cfg80211_mc_cp_stats_free_stats_event(struct stats_event *stats) +{ + if (!stats) + return; + + qdf_mem_free(stats->pdev_stats); + qdf_mem_free(stats->peer_stats); + qdf_mem_free(stats->cca_stats); + qdf_mem_free(stats->vdev_summary_stats); + qdf_mem_free(stats->vdev_chain_rssi); + qdf_mem_free(stats->peer_adv_stats); + wlan_free_mib_stats(stats); + qdf_mem_free(stats); +}