diff --git a/qdf/inc/qdf_util.h b/qdf/inc/qdf_util.h index 4f498068fb..8b878f667f 100644 --- a/qdf/inc/qdf_util.h +++ b/qdf/inc/qdf_util.h @@ -105,6 +105,18 @@ typedef __qdf_wait_queue_head_t qdf_wait_queue_head_t; */ #define QDF_MIN(_x, _y) (((_x) < (_y)) ? (_x) : (_y)) +/** + * QDF_IS_ADDR_BROADCAST - is mac address broadcast mac address + * @_a: pointer to mac address + */ +#define QDF_IS_ADDR_BROADCAST(_a) \ + ((_a)[0] == 0xff && \ + (_a)[1] == 0xff && \ + (_a)[2] == 0xff && \ + (_a)[3] == 0xff && \ + (_a)[4] == 0xff && \ + (_a)[5] == 0xff) + /** * qdf_status_to_os_return - returns the status to OS. * @status: enum QDF_STATUS diff --git a/umac/cp_stats/dispatcher/inc/wlan_cp_stats_mc_defs.h b/umac/cp_stats/dispatcher/inc/wlan_cp_stats_mc_defs.h index 9c21101f83..4e2abdf496 100644 --- a/umac/cp_stats/dispatcher/inc/wlan_cp_stats_mc_defs.h +++ b/umac/cp_stats/dispatcher/inc/wlan_cp_stats_mc_defs.h @@ -41,14 +41,16 @@ #include "qdf_event.h" /** - * enum stats_req_type: enum indicating bit position of various stats type in + * 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 */ enum stats_req_type { TYPE_CONNECTION_TX_POWER = 0, TYPE_STATION_STATS, + TYPE_PEER_STATS, TYPE_MAX, }; @@ -92,6 +94,8 @@ struct wake_lock_stats { uint32_t scan_11d; }; +struct stats_event; + /** * struct request_info: details of each request * @cookie: identifier for os_if request @@ -104,6 +108,7 @@ 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); } u; uint32_t vdev_id; uint32_t pdev_id; @@ -146,14 +151,32 @@ struct vdev_mc_cp_stats { struct wake_lock_stats wow_stats; }; +/** + * struct peer_mc_cp_stats - peer specific stats + * @tx_rate: tx rate + * @rx_rate: rx rate + * @peer_rssi: rssi + * @peer_macaddr: mac address + */ +struct peer_mc_cp_stats { + uint32_t tx_rate; + uint32_t rx_rate; + uint32_t peer_rssi; + uint8_t peer_macaddr[WLAN_MACADDR_LEN]; +}; + /** * struct stats_event - parameters populated by stats event - * @num_pdev_stats: number of pdev stats - * @pdev_stats: array of per pdev stats (index = pdev_id) + * @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 */ 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; }; #endif /* CONFIG_MCL */ diff --git a/umac/cp_stats/dispatcher/inc/wlan_cp_stats_mc_ucfg_api.h b/umac/cp_stats/dispatcher/inc/wlan_cp_stats_mc_ucfg_api.h index 75d694e9c8..afbfd2f369 100644 --- a/umac/cp_stats/dispatcher/inc/wlan_cp_stats_mc_ucfg_api.h +++ b/umac/cp_stats/dispatcher/inc/wlan_cp_stats_mc_ucfg_api.h @@ -177,4 +177,4 @@ QDF_STATUS ucfg_mc_cp_stats_get_pending_req(struct wlan_objmgr_psoc *psoc, struct request_info *info); #endif /* QCA_SUPPORT_CP_STATS */ -#endif /* __WLAN_CP_STATS_UCFG_API_H__ */ +#endif /* __WLAN_CP_STATS_MC_UCFG_API_H__ */ diff --git a/umac/cp_stats/dispatcher/src/wlan_cp_stats_mc_tgt_api.c b/umac/cp_stats/dispatcher/src/wlan_cp_stats_mc_tgt_api.c index b0ecef7fdc..cff2ec1f62 100644 --- a/umac/cp_stats/dispatcher/src/wlan_cp_stats_mc_tgt_api.c +++ b/umac/cp_stats/dispatcher/src/wlan_cp_stats_mc_tgt_api.c @@ -107,8 +107,220 @@ static void tgt_mc_cp_stats_extract_tx_power(struct wlan_objmgr_psoc *psoc, end: if (vdev) wlan_objmgr_vdev_release_ref(vdev, WLAN_CP_STATS_ID); - qdf_mem_free(ev->pdev_stats); - ev->pdev_stats = NULL; +} + +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; + + 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++; + 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_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) { + cp_stats_err("malloc failed"); + 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, 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) { + cp_stats_err("malloc failed"); + goto end; + } + + ev.num_peer_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; + wlan_cp_stats_peer_obj_unlock(peer_cp_stats_priv); + } + + get_peer_rssi_cb(&ev, last_req->cookie); + return; +end: + if (vdev) + wlan_objmgr_vdev_release_ref(vdev, WLAN_CP_STATS_ID); + if (peer) + wlan_objmgr_peer_release_ref(peer, WLAN_CP_STATS_ID); + ucfg_mc_cp_stats_free_stats_event(&ev); +} + +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(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, + WLAN_MACADDR_LEN); + 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=%u", + 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 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 (!ev->peer_stats) { + cp_stats_err("no peer 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_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_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, + WLAN_MACADDR_LEN)) { + /* 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 stats"); + return; + } + + if (is_station_stats) + return; + + tgt_mc_cp_stats_prepare_raw_peer_rssi(psoc, &last_req); + ucfg_mc_cp_stats_reset_pending_req(psoc, TYPE_PEER_STATS); } QDF_STATUS tgt_mc_cp_stats_process_stats_event(struct wlan_objmgr_psoc *psoc, @@ -117,6 +329,8 @@ QDF_STATUS tgt_mc_cp_stats_process_stats_event(struct wlan_objmgr_psoc *psoc, 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); return QDF_STATUS_SUCCESS; } @@ -144,7 +358,7 @@ QDF_STATUS tgt_send_mc_cp_stats_req(struct wlan_objmgr_psoc *psoc, struct wlan_lmac_if_cp_stats_tx_ops *tx_ops; tx_ops = target_if_cp_stats_get_tx_ops(psoc); - if (!tx_ops) { + if (!tx_ops || !tx_ops->send_req_stats) { cp_stats_err("could not get tx_ops"); return QDF_STATUS_E_NULL_VALUE; }