| /* |
| * Copyright (c) 2018-2019 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 <wlan_cp_stats_mc_ucfg_api.h> |
| #include <wlan_cp_stats_utils_api.h> |
| #include "../../core/src/wlan_cp_stats_defs.h" |
| |
| 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; |
| |
| if (!ev->pdev_stats) { |
| cp_stats_err("no 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; |
| |
| ucfg_mc_cp_stats_reset_pending_req(psoc, TYPE_CONNECTION_TX_POWER); |
| if (last_req.u.get_tx_power_cb) |
| 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_err("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_cp_stats_priv->peer_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_cp_stats_priv->peer_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_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_err("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_adv_mc_stats = peer_cp_stats_priv->peer_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_err("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_debug("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_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_err("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_extd_mc_stats = peer_cp_stats_priv->peer_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_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}; |
| 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) { |
| cp_stats_debug("no 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) { |
| cp_stats_err("no peer_extd2 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_err("peer not found for extd stats"); |
| return; |
| } |
| |
| complete: |
| if (is_station_stats) |
| return; |
| |
| tgt_mc_cp_stats_extract_peer_extd_stats(psoc, ev); |
| tgt_mc_cp_stats_prepare_raw_peer_rssi(psoc, &last_req); |
| ucfg_mc_cp_stats_reset_pending_req(psoc, TYPE_PEER_STATS); |
| } |
| |
| 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) { |
| cp_stats_err("no 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_err("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_err("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) { |
| cp_stats_err("no vdev chain rssi 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_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_err("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_cp_stats_priv->peer_adv_stats) { |
| info.num_peer_adv_stats = 1; |
| qdf_mem_copy(info.peer_adv_stats, |
| peer_cp_stats_priv->peer_adv_stats, |
| sizeof(peer_cp_stats_priv->peer_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; |
| bool is_last_event; |
| struct request_info last_req = {0}; |
| |
| if (IS_MSB_SET(ev->last_event)) |
| is_last_event = IS_LSB_SET(ev->last_event); |
| else |
| is_last_event = !!ev->peer_stats; |
| |
| 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 (is_last_event) { |
| tgt_mc_cp_stats_prepare_n_send_raw_station_stats(psoc, |
| &last_req); |
| ucfg_mc_cp_stats_reset_pending_req(psoc, TYPE_STATION_STATS); |
| } |
| } |
| |
| 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); |
| |
| tgt_mc_cp_stats_extract_cca_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); |
| } |