/****************************************************************************** * * Copyright(c) 2019 - 2022 Realtek Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * *****************************************************************************/ #define _PHL_TX_C_ #include "phl_headers.h" /** * this function will be used in read / write pointer mechanism and * return the number of available read pointer * @rptr: input, the read pointer * @wptr: input, the write pointer * @bndy: input, the boundary of read / write pointer mechanism */ u16 phl_calc_avail_rptr(u16 rptr, u16 wptr, u16 bndy) { u16 avail_rptr = 0; if (wptr >= rptr) avail_rptr = wptr - rptr; else if (rptr > wptr) avail_rptr = wptr + (bndy - rptr); return avail_rptr; } /** * this function will be used in read / write pointer mechanism and * return the number of available write pointer * @rptr: input, the read pointer * @wptr: input, the write pointer * @bndy: input, the boundary of read / write pointer mechanism */ u16 phl_calc_avail_wptr(u16 rptr, u16 wptr, u16 bndy) { u16 avail_wptr = 0; if (rptr > wptr) avail_wptr = rptr - wptr - 1; else if (wptr >= rptr) avail_wptr = rptr + (bndy - wptr) - 1; return avail_wptr; } void phl_dump_sorted_ring(_os_list *sorted_ring) { struct phl_ring_status *ring_sts; u16 i = 0; PHL_TRACE(COMP_PHL_XMIT, _PHL_INFO_, "==dump sorted ring==\n"); phl_list_for_loop(ring_sts, struct phl_ring_status, sorted_ring, list) { i++; PHL_TRACE(COMP_PHL_XMIT, _PHL_INFO_, "==ring %d==\n", i); PHL_TRACE(COMP_PHL_XMIT, _PHL_INFO_, "ring_sts->req_busy = %d\n", ring_sts->req_busy); PHL_TRACE(COMP_PHL_XMIT, _PHL_INFO_, "ring_sts->ring_ptr->cat = %d\n", ring_sts->ring_ptr->cat); } } void phl_dump_tx_plan(_os_list *sta_list) { struct phl_tx_plan *tx_plan; u16 i = 0; PHL_TRACE(COMP_PHL_XMIT, _PHL_INFO_, "==dump tx plan==\n"); phl_list_for_loop(tx_plan, struct phl_tx_plan, sta_list, list) { i++; PHL_TRACE(COMP_PHL_XMIT, _PHL_INFO_, "==tx plan %d==\n", i); PHL_TRACE(COMP_PHL_XMIT, _PHL_INFO_, "tx_plan->sleep = %d\n", tx_plan->sleep); PHL_TRACE(COMP_PHL_XMIT, _PHL_INFO_, "tx_plan->has_mgnt = %d\n", tx_plan->has_mgnt); PHL_TRACE(COMP_PHL_XMIT, _PHL_INFO_, "tx_plan->has_hiq = %d\n", tx_plan->has_hiq); phl_dump_sorted_ring(&tx_plan->sorted_ring); } } void phl_dump_t_fctrl_result(_os_list *t_fctrl_result) { struct phl_ring_status *ring_sts; u16 i = 0; PHL_TRACE(COMP_PHL_XMIT, _PHL_INFO_, "==dump tx flow control result==\n"); phl_list_for_loop(ring_sts, struct phl_ring_status, t_fctrl_result, list) { i++; PHL_TRACE(COMP_PHL_XMIT, _PHL_INFO_, "==ring %d==\n", i); PHL_TRACE(COMP_PHL_XMIT, _PHL_INFO_, "ring_sts->req_busy = %d\n", ring_sts->req_busy); PHL_TRACE(COMP_PHL_XMIT, _PHL_INFO_, "ring_sts->ring_ptr->cat = %d\n", ring_sts->ring_ptr->cat); } } void phl_dump_tx_stats(struct rtw_stats *stats) { PHL_TRACE(COMP_PHL_XMIT, _PHL_DEBUG_, "Dump Tx statistics\n" "tx_byte_uni = %lld\n" "tx_byte_total = %lld\n" "tx_tp_kbits = %d\n" "last_tx_time_ms = %d\n", stats->tx_byte_uni, stats->tx_byte_total, stats->tx_tp_kbits, stats->last_tx_time_ms); } void phl_dump_h2c_pool_stats(struct phl_h2c_pkt_pool *h2c_pkt_pool) { PHL_INFO("[h2c_stats] idle cmd %d, idle data %d, idle ldata %d, busy h2c %d.\n", h2c_pkt_pool->idle_h2c_pkt_cmd_list.cnt, h2c_pkt_pool->idle_h2c_pkt_data_list.cnt, h2c_pkt_pool->idle_h2c_pkt_ldata_list.cnt, h2c_pkt_pool->busy_h2c_pkt_list.cnt); } #ifdef CONFIG_PHL_H2C_PKT_POOL_STATS_CHECK struct phl_h2c_pkt_alloc_cnt * rtw_phl_dump_h2c_pool_alloc_stats(struct phl_info_t *phl_info) { struct phl_h2c_pkt_pool *h2c_pkt_pool = phl_info->h2c_pool; struct phl_h2c_pkt_alloc_cnt *h2c_pkt_alloc_cnt = &phl_info->h2c_alloc_cnt; struct rtw_h2c_pkt_return_list *h2c_pkt_return_list = &phl_info->h2c_pkt_return_list; void *drv = phl_to_drvpriv(phl_info); int i; _os_atomic_set(drv, &h2c_pkt_alloc_cnt->busy_h2c_pkt_cmd_cnt, _H2CB_CMD_QLEN - h2c_pkt_pool->idle_h2c_pkt_cmd_list.cnt); _os_atomic_set(drv, &h2c_pkt_alloc_cnt->busy_h2c_pkt_data_cnt, _H2CB_DATA_QLEN - h2c_pkt_pool->idle_h2c_pkt_data_list.cnt); _os_atomic_set(drv, &h2c_pkt_alloc_cnt->busy_h2c_pkt_ldata_cnt, _H2CB_LONG_DATA_QLEN - h2c_pkt_pool->idle_h2c_pkt_ldata_list.cnt); PHL_PRINT("[PHL][H2C_STATS] alloc cmd=%d, alloc data=%d, alloc ldata=%d\n", _os_atomic_read(drv, &h2c_pkt_alloc_cnt->busy_h2c_pkt_cmd_cnt), _os_atomic_read(drv, &h2c_pkt_alloc_cnt->busy_h2c_pkt_data_cnt), _os_atomic_read(drv, &h2c_pkt_alloc_cnt->busy_h2c_pkt_ldata_cnt)); PHL_PRINT("[PHL][H2C_STATS] query=%d, mac=%d, bb=%d, rf=%d, btc=%d\n", _os_atomic_read(drv, &h2c_pkt_alloc_cnt->h2c_query_cnt), _os_atomic_read(drv, &h2c_pkt_alloc_cnt->h2c_mac_cnt), _os_atomic_read(drv, &h2c_pkt_alloc_cnt->h2c_bb_cnt), _os_atomic_read(drv, &h2c_pkt_alloc_cnt->h2c_rf_cnt), _os_atomic_read(drv, &h2c_pkt_alloc_cnt->h2c_btc_cnt)); PHL_PRINT("[PHL][H2C_STATS] last return %d h2c pkt\n", H2C_LAST_RETURN_NUM); _os_spinlock(drv, &h2c_pkt_return_list->lock, _bh, NULL); i = h2c_pkt_return_list->oldest_index; do { PHL_PRINT("[PHL][H2C_STATS] timestamp=%u, src=%d, id=0x%x\n", h2c_pkt_return_list->h2c_pkt_return[i].timestamp, h2c_pkt_return_list->h2c_pkt_return[i].pkt_src, h2c_pkt_return_list->h2c_pkt_return[i].id); i = (i + 1) % H2C_LAST_RETURN_NUM; } while (i != h2c_pkt_return_list->oldest_index); _os_spinunlock(drv, &h2c_pkt_return_list->lock, _bh, NULL); return h2c_pkt_alloc_cnt; } #endif void phl_reset_tx_stats(struct rtw_stats *stats) { stats->tx_byte_uni = 0; stats->tx_byte_total = 0; stats->tx_tp_kbits = 0; stats->last_tx_time_ms = 0; stats->txtp.last_calc_time_ms = 0; stats->txtp.last_calc_time_ms = 0; stats->tx_traffic.lvl = RTW_TFC_IDLE; stats->tx_traffic.sts = 0; } const char *phl_tfc_lvl_to_str(u8 lvl) { switch (lvl) { case RTW_TFC_IDLE: return "IDLE"; case RTW_TFC_ULTRA_LOW: return "ULTRA_LOW"; case RTW_TFC_LOW: return "LOW"; case RTW_TFC_MID: return "MID"; case RTW_TFC_HIGH: return "HIGH"; default: return "-"; } } void phl_tx_traffic_upd(struct rtw_stats *sts) { u32 tp_k = 0, tp_m = 0; enum rtw_tfc_lvl tx_tfc_lvl = RTW_TFC_IDLE; tp_k = sts->tx_tp_kbits; tp_m = sts->tx_tp_kbits >> 10; if (tp_m >= TX_HIGH_TP_THRES_MBPS) tx_tfc_lvl = RTW_TFC_HIGH; else if (tp_m >= TX_MID_TP_THRES_MBPS) tx_tfc_lvl = RTW_TFC_MID; else if (tp_m >= TX_LOW_TP_THRES_MBPS) tx_tfc_lvl = RTW_TFC_LOW; else if (tp_k >= TX_ULTRA_LOW_TP_THRES_KBPS) tx_tfc_lvl = RTW_TFC_ULTRA_LOW; else tx_tfc_lvl = RTW_TFC_IDLE; if (sts->tx_traffic.lvl > tx_tfc_lvl) { sts->tx_traffic.sts = (TRAFFIC_CHANGED | TRAFFIC_DECREASE); sts->tx_traffic.lvl = tx_tfc_lvl; } else if (sts->tx_traffic.lvl < tx_tfc_lvl) { sts->tx_traffic.sts = (TRAFFIC_CHANGED | TRAFFIC_INCREASE); sts->tx_traffic.lvl = tx_tfc_lvl; } else if (sts->tx_traffic.sts & (TRAFFIC_CHANGED | TRAFFIC_INCREASE | TRAFFIC_DECREASE)) { sts->tx_traffic.sts &= ~(TRAFFIC_CHANGED | TRAFFIC_INCREASE | TRAFFIC_DECREASE); } } void phl_update_tx_stats(struct rtw_stats *stats, struct rtw_xmit_req *tx_req) { u32 diff_t = 0, cur_time = _os_get_cur_time_ms(); u64 diff_bits = 0; stats->last_tx_time_ms = cur_time; stats->tx_byte_total += tx_req->total_len; stats->txreq_num++; if (tx_req->mdata.bc == 0 && tx_req->mdata.mc == 0) stats->tx_byte_uni += tx_req->total_len; if (0 == stats->txtp.last_calc_time_ms || 0 == stats->txtp.last_calc_bits) { stats->txtp.last_calc_time_ms = stats->last_tx_time_ms; stats->txtp.last_calc_bits = stats->tx_byte_uni * 8; } else { if (cur_time >= stats->txtp.last_calc_time_ms) { diff_t = cur_time - stats->txtp.last_calc_time_ms; } else { diff_t = RTW_U32_MAX - stats->txtp.last_calc_time_ms + cur_time + 1; } if (diff_t > TXTP_CALC_DIFF_MS && stats->tx_byte_uni != 0) { diff_bits = (stats->tx_byte_uni * 8) - stats->txtp.last_calc_bits; stats->tx_tp_kbits = (u32)_os_division64(diff_bits, diff_t); stats->txtp.last_calc_bits = stats->tx_byte_uni * 8; stats->txtp.last_calc_time_ms = cur_time; } } } void phl_tx_statistics(struct phl_info_t *phl_info, struct rtw_xmit_req *tx_req) { struct rtw_phl_com_t *phl_com = phl_info->phl_com; struct rtw_stats *phl_stats = &phl_com->phl_stats; struct rtw_stats *sta_stats = NULL; struct rtw_phl_stainfo_t *sta = NULL; u16 macid = tx_req->mdata.macid; if (!phl_macid_is_valid(phl_info, macid)) goto dev_stat; sta = rtw_phl_get_stainfo_by_macid(phl_info, macid); if (NULL == sta) goto dev_stat; sta_stats = &sta->stats; phl_update_tx_stats(sta_stats, tx_req); dev_stat: phl_update_tx_stats(phl_stats, tx_req); } static void _phl_free_phl_tring_list(void *phl, struct rtw_phl_tring_list *ring_list) { struct phl_info_t *phl_info = (struct phl_info_t *)phl; void *drv_priv = phl_to_drvpriv(phl_info); struct rtw_phl_evt_ops *ops = &phl_info->phl_com->evt_ops; struct rtw_phl_tx_ring *ring; struct rtw_xmit_req *tx_req; u16 rptr = 0; u8 i = 0; if(ring_list==NULL) return ; for (i = 0; i < MAX_PHL_RING_CAT_NUM; i++) { ring = &ring_list->phl_ring[i]; rptr = (u16)_os_atomic_read(drv_priv, &ring->phl_idx); if (ring->entry==NULL) { PHL_DBG("tx ring %d entry is empty!\n", i); continue; } while (rptr != ring->core_idx) { rptr += 1; if (rptr >= MAX_PHL_TX_RING_ENTRY_NUM) rptr = 0; tx_req = (struct rtw_xmit_req *)ring->entry[rptr]; if (NULL == tx_req) break; ops->tx_recycle(drv_priv, tx_req); } _os_kmem_free(drv_priv, ring->entry, MAX_PHL_TX_RING_ENTRY_NUM * sizeof(u8 *)); ring->entry = NULL; } _os_kmem_free(drv_priv, ring_list, sizeof(*ring_list)); } void _phl_init_tx_plan(struct phl_tx_plan * tx_plan) { INIT_LIST_HEAD(&tx_plan->list); tx_plan->sleep = false; tx_plan->has_mgnt = false; tx_plan->has_hiq = false; INIT_LIST_HEAD(&tx_plan->sorted_ring); } static struct rtw_phl_tring_list * _phl_allocate_phl_tring_list(void *phl, u16 macid, u8 hw_band, u8 hw_wmm, u8 hw_port) { struct phl_info_t *phl_info = (struct phl_info_t *)phl; struct rtw_phl_tring_list *phl_tring_list = NULL; void *drv_priv = NULL; u32 buf_len = 0; u8 i = 0, dma_ch = 0; u8 fail = 1; drv_priv = phl_to_drvpriv(phl_info); buf_len = sizeof(struct rtw_phl_tring_list); phl_tring_list = (struct rtw_phl_tring_list *)_os_kmem_alloc(drv_priv, buf_len); if (NULL != phl_tring_list) { _os_mem_set(drv_priv, phl_tring_list, 0, buf_len); INIT_LIST_HEAD(&phl_tring_list->list); phl_tring_list->macid = macid; phl_tring_list->band = hw_band; phl_tring_list->wmm = hw_wmm; phl_tring_list->port = hw_port; /*phl_tring_list->mbssid = hw_mbssid*/ for (i = 0; i < MAX_PHL_RING_CAT_NUM; i++) { phl_tring_list->phl_ring[i].cat = i; dma_ch = rtw_hal_tx_chnl_mapping(phl_info->hal, macid, i, hw_band); phl_tring_list->phl_ring[i].dma_ch = dma_ch; phl_tring_list->phl_ring[i].entry = (u8 **)_os_kmem_alloc(drv_priv, MAX_PHL_TX_RING_ENTRY_NUM * sizeof(u8 *)); if (phl_tring_list->phl_ring[i].entry == NULL) { PHL_DBG("cannot malloc %lu bytes, stop!", (unsigned long int)(MAX_PHL_TX_RING_ENTRY_NUM * sizeof(u8 *))); goto exit; } } _phl_init_tx_plan(&phl_tring_list->tx_plan); fail = 0; } exit: if (fail) { _phl_free_phl_tring_list(phl_info, phl_tring_list); phl_tring_list = NULL; } return phl_tring_list; } enum rtw_phl_status phl_register_tx_ring(void *phl, u16 macid, u8 hw_band, u8 hw_wmm, u8 hw_port) { struct phl_info_t *phl_info = (struct phl_info_t *)phl; void *drv_priv = phl_to_drvpriv(phl_info); struct rtw_phl_tring_list *phl_tring_list = NULL; enum rtw_phl_status phl_status = RTW_PHL_STATUS_FAILURE; _os_list *ring_list = NULL; phl_tring_list = _phl_allocate_phl_tring_list(phl, macid, hw_band, hw_wmm, hw_port); if (NULL != phl_tring_list) { ring_list = &phl_info->t_ring_list; _os_spinlock(drv_priv, &phl_info->t_ring_list_lock, _bh, NULL); list_add_tail(&phl_tring_list->list, ring_list); _os_spinunlock(drv_priv, &phl_info->t_ring_list_lock, _bh, NULL); phl_status = RTW_PHL_STATUS_SUCCESS; } return phl_status; } enum rtw_phl_status phl_deregister_tx_ring(void *phl, u16 macid) { struct phl_info_t *phl_info = (struct phl_info_t *)phl; void *drv_priv = phl_to_drvpriv(phl_info); struct rtw_phl_tring_list *phl_tring_list = NULL, *t; enum rtw_phl_status phl_status = RTW_PHL_STATUS_FAILURE; _os_list *ring_list = NULL; ring_list = &phl_info->t_ring_list; _os_spinlock(drv_priv, &phl_info->t_ring_list_lock, _bh, NULL); phl_list_for_loop_safe(phl_tring_list, t, struct rtw_phl_tring_list, ring_list, list) { if (macid == phl_tring_list->macid) { list_del(&phl_tring_list->list); phl_status = RTW_PHL_STATUS_SUCCESS; break; } } _os_spinunlock(drv_priv, &phl_info->t_ring_list_lock, _bh, NULL); if (RTW_PHL_STATUS_SUCCESS == phl_status) { /* defer the free operation to avoid racing with _phl_tx_callback_xxx */ pq_push(drv_priv, &phl_info->t_ring_free_q, &phl_tring_list->list, _tail, _bh); } return phl_status; } enum rtw_phl_status phl_re_register_tx_ring(void *phl, u16 macid, u8 hw_band, u8 hw_wmm, u8 hw_port) { struct phl_info_t *phl_info = (struct phl_info_t *)phl; void *drv_priv = phl_to_drvpriv(phl_info); struct rtw_phl_tring_list *phl_tring_list = NULL, *t; enum rtw_phl_status phl_status = RTW_PHL_STATUS_FAILURE; _os_list *ring_list = NULL; u8 i = 0, dma_ch = 0; ring_list = &phl_info->t_ring_list; _os_spinlock(drv_priv, &phl_info->t_ring_list_lock, _bh, NULL); phl_list_for_loop_safe(phl_tring_list, t, struct rtw_phl_tring_list, ring_list, list) { if (macid == phl_tring_list->macid) { phl_tring_list->band = hw_band; phl_tring_list->wmm = hw_wmm; phl_tring_list->port = hw_port; for (i = 0; i < MAX_PHL_RING_CAT_NUM; i++) { phl_tring_list->phl_ring[i].cat = i; dma_ch = rtw_hal_tx_chnl_mapping(phl_info->hal, macid, i, hw_band); phl_tring_list->phl_ring[i].dma_ch = dma_ch; } phl_status = RTW_PHL_STATUS_SUCCESS; break; } } _os_spinunlock(drv_priv, &phl_info->t_ring_list_lock, _bh, NULL); return phl_status; } void phl_free_deferred_tx_ring(struct phl_info_t *phl_info) { void *drv_priv = phl_to_drvpriv(phl_info); _os_list *ring_list = NULL; while (pq_pop(drv_priv, &phl_info->t_ring_free_q, &ring_list, _first, _bh)) { _phl_free_phl_tring_list(phl_info, (struct rtw_phl_tring_list *)ring_list); } } struct phl_ring_status *phl_alloc_ring_sts(struct phl_info_t *phl_info) { struct phl_ring_sts_pool *ring_sts_pool = phl_info->ring_sts_pool; struct phl_ring_status *ring_sts = NULL; _os_spinlock(phl_to_drvpriv(phl_info), &ring_sts_pool->idle_lock, _bh, NULL); if (false == list_empty(&ring_sts_pool->idle)) { ring_sts = list_first_entry(&ring_sts_pool->idle, struct phl_ring_status, list); list_del(&ring_sts->list); } _os_spinunlock(phl_to_drvpriv(phl_info), &ring_sts_pool->idle_lock, _bh, NULL); return ring_sts; } void phl_release_ring_sts(struct phl_info_t *phl_info, struct phl_ring_status *ring_sts) { struct phl_ring_sts_pool *ring_sts_pool = phl_info->ring_sts_pool; void *drv_priv = NULL; drv_priv = phl_to_drvpriv(phl_info); _os_spinlock(drv_priv, &ring_sts_pool->idle_lock, _bh, NULL); _os_mem_set(drv_priv, ring_sts, 0, sizeof(*ring_sts)); INIT_LIST_HEAD(&ring_sts->list); list_add_tail(&ring_sts->list, &ring_sts_pool->idle); _os_spinunlock(drv_priv, &ring_sts_pool->idle_lock, _bh, NULL); } void _phl_ring_status_deinit(struct phl_info_t *phl_info) { struct phl_ring_sts_pool *ring_sts_pool = NULL; u16 buf_len = 0; void *drv_priv = NULL; FUNCIN(); drv_priv = phl_to_drvpriv(phl_info); ring_sts_pool = (struct phl_ring_sts_pool *)phl_info->ring_sts_pool; if (NULL != ring_sts_pool) { buf_len = sizeof(struct phl_ring_sts_pool); _os_spinlock_free(drv_priv, &ring_sts_pool->idle_lock); _os_spinlock_free(drv_priv, &ring_sts_pool->busy_lock); _os_mem_free(drv_priv, ring_sts_pool, buf_len); } FUNCOUT(); } enum rtw_phl_status _phl_ring_status_init(struct phl_info_t *phl_info) { enum rtw_phl_status pstatus = RTW_PHL_STATUS_FAILURE; struct phl_ring_sts_pool *ring_sts_pool = NULL; struct phl_ring_status *ring_sts = NULL; void *drv_priv = NULL; u16 buf_len = 0; u16 i = 0; FUNCIN_WSTS(pstatus); drv_priv = phl_to_drvpriv(phl_info); buf_len = sizeof(struct phl_ring_sts_pool); ring_sts_pool = (struct phl_ring_sts_pool *)_os_mem_alloc(drv_priv, buf_len); if (NULL != ring_sts_pool) { _os_mem_set(drv_priv, ring_sts_pool, 0, buf_len); INIT_LIST_HEAD(&ring_sts_pool->idle); INIT_LIST_HEAD(&ring_sts_pool->busy); _os_spinlock_init(drv_priv, &ring_sts_pool->idle_lock); _os_spinlock_init(drv_priv, &ring_sts_pool->busy_lock); for (i = 0; i < MAX_PHL_RING_STATUS_NUMBER; i++) { ring_sts = &ring_sts_pool->ring_sts[i]; INIT_LIST_HEAD(&ring_sts->list); _os_spinlock(drv_priv, (void *)&ring_sts_pool->idle_lock, _bh, NULL); list_add_tail(&ring_sts->list, &ring_sts_pool->idle); _os_spinunlock(drv_priv, (void *)&ring_sts_pool->idle_lock, _bh, NULL); } phl_info->ring_sts_pool = ring_sts_pool; pstatus = RTW_PHL_STATUS_SUCCESS; } if (RTW_PHL_STATUS_SUCCESS != pstatus) _phl_ring_status_deinit(phl_info); FUNCOUT_WSTS(pstatus); return pstatus; } struct phl_ring_status * _phl_check_ring_status(struct phl_info_t *phl_info, struct rtw_phl_tx_ring *ring, struct rtw_phl_tring_list *tring_list) { enum rtw_phl_status pstatus = RTW_PHL_STATUS_FAILURE; struct phl_ring_status *ring_sts = NULL; u16 avail = 0, rptr = 0; void *drv_priv = phl_to_drvpriv(phl_info); do { rptr = (u16)_os_atomic_read(drv_priv, &ring->phl_idx); avail = phl_calc_avail_rptr(rptr, ring->core_idx, MAX_PHL_TX_RING_ENTRY_NUM); if (0 == avail) { ring_sts = NULL; pstatus = RTW_PHL_STATUS_SUCCESS; break; } else { ring_sts = phl_alloc_ring_sts(phl_info); if (NULL == ring_sts) { PHL_ERR("query ring status fail!\n"); pstatus = RTW_PHL_STATUS_RESOURCE; break; } ring_sts->band = tring_list->band; ring_sts->wmm = tring_list->wmm; ring_sts->port = tring_list->port; /*ring_sts->mbssid = tring_list->mbssid;*/ ring_sts->req_busy = avail; ring_sts->ring_ptr = ring; rptr += 1; if (rptr >= MAX_PHL_TX_RING_ENTRY_NUM) _os_atomic_set(drv_priv, &ring->phl_next_idx, 0); else _os_atomic_set(drv_priv, &ring->phl_next_idx, rptr); pstatus = RTW_PHL_STATUS_SUCCESS; break; } } while (false); return ring_sts; } void _phl_reset_tx_plan(struct phl_info_t *phl_info, struct phl_tx_plan *tx_plan) { struct phl_ring_status *ring_sts, *t; INIT_LIST_HEAD(&tx_plan->list); tx_plan->sleep = false; tx_plan->has_mgnt = false; tx_plan->has_hiq = false; phl_list_for_loop_safe(ring_sts, t, struct phl_ring_status, &tx_plan->sorted_ring, list) { list_del(&ring_sts->list); phl_release_ring_sts(phl_info, ring_sts); } INIT_LIST_HEAD(&tx_plan->sorted_ring); } void _phl_sort_ring_by_tid(struct phl_ring_status *ring_sts, struct phl_tx_plan *tx_plan, enum rtw_phl_ring_cat cat) { struct phl_ring_status *last_sts = NULL; if (ring_sts->ring_ptr->cat == RTW_PHL_RING_CAT_TID1) { list_add_tail(&ring_sts->list, &tx_plan->sorted_ring); } else if (ring_sts->ring_ptr->cat == RTW_PHL_RING_CAT_TID2) { if (list_empty(&tx_plan->sorted_ring)) { list_add_tail(&ring_sts->list, &tx_plan->sorted_ring); } else { last_sts = list_last_entry(&tx_plan->sorted_ring, struct phl_ring_status, list); if (RTW_PHL_RING_CAT_TID1 == last_sts->ring_ptr->cat) { __list_add(&ring_sts->list, _get_prev(&last_sts->list), &last_sts->list); } else { list_add_tail(&ring_sts->list, &tx_plan->sorted_ring); } } } else { list_add(&ring_sts->list, &tx_plan->sorted_ring); if (RTW_PHL_RING_CAT_MGNT == cat) tx_plan->has_mgnt = true; else if (RTW_PHL_RING_CAT_HIQ == cat) tx_plan->has_hiq = true; } } void _phl_check_tring_list(struct phl_info_t *phl_info, struct rtw_phl_tring_list *tring_list, _os_list *sta_list) { struct phl_ring_status *ring_sts = NULL; struct rtw_phl_tx_ring *ring = NULL; struct phl_tx_plan *tx_plan = &tring_list->tx_plan; u8 i = 0; for (i = 0; i < MAX_PHL_RING_CAT_NUM; i++) { ring = &tring_list->phl_ring[i]; ring_sts = _phl_check_ring_status(phl_info, ring, tring_list); if (NULL != ring_sts) { _phl_sort_ring_by_tid(ring_sts, tx_plan, i); } else { continue; } } /* hana_todo: check this macid is sleep or not */ if (!list_empty(&tx_plan->sorted_ring)) { list_add_tail(&tx_plan->list, sta_list); } } u8 phl_check_xmit_ring_resource(struct phl_info_t *phl_info, _os_list *sta_list) { void *drvpriv = phl_to_drvpriv(phl_info); _os_list *tring_list_head = &phl_info->t_ring_list; struct rtw_phl_tring_list *tring_list, *t; _os_spinlock(drvpriv, &phl_info->t_ring_list_lock, _bh, NULL); phl_list_for_loop_safe(tring_list, t, struct rtw_phl_tring_list, tring_list_head, list) { _phl_check_tring_list(phl_info, tring_list, sta_list); /* use the first phl tx ring */ if (phl_info->use_onetxring) break; } #ifdef SDIO_TX_THREAD /** * when SDIO_TX_THREAD is enabled, * clearing variable "phl_sw_tx_more" in function "phl_tx_sdio_thrd_hdl" */ #else _os_atomic_set(drvpriv, &phl_info->phl_sw_tx_more, 0); #endif _os_spinunlock(drvpriv, &phl_info->t_ring_list_lock, _bh, NULL); if (true == list_empty(sta_list)) return false; else return true; } void phl_tx_flow_ctrl(struct phl_info_t *phl_info, _os_list *sta_list) { _os_list *t_fctrl_result = &phl_info->t_fctrl_result; _os_list *tid_entry[MAX_PHL_RING_CAT_NUM] = {0}; struct phl_tx_plan *tx_plan, *tp; struct phl_ring_status *ring_sts = NULL, *ts; u8 tid = 0; _os_mem_set(phl_to_drvpriv(phl_info), tid_entry, 0, sizeof(_os_list *) * MAX_PHL_RING_CAT_NUM); phl_list_for_loop_safe(tx_plan, tp, struct phl_tx_plan, sta_list, list) { /* drop power saving station */ if (true == tx_plan->sleep) { list_del(&tx_plan->list); _phl_reset_tx_plan(phl_info, tx_plan); continue; } if (true == tx_plan->has_hiq) { ring_sts = list_first_entry(&tx_plan->sorted_ring, struct phl_ring_status, list); list_del(&ring_sts->list); list_add(&ring_sts->list, t_fctrl_result); } if (true == tx_plan->has_mgnt) { ring_sts = list_first_entry(&tx_plan->sorted_ring, struct phl_ring_status, list); list_del(&ring_sts->list); list_add(&ring_sts->list, t_fctrl_result); } /* todo: drop station which has reached tx limit */ phl_list_for_loop_safe(ring_sts, ts, struct phl_ring_status, &tx_plan->sorted_ring, list) { list_del(&ring_sts->list); tid = rtw_phl_cvt_cat_to_tid(ring_sts->ring_ptr->cat); /* todo: drop tid which has reached tx limit */ /* sw tx cnt limit */ if (NULL == tid_entry[tid]) { list_add_tail(&ring_sts->list, t_fctrl_result); } else { __list_add(&ring_sts->list, tid_entry[tid], _get_next(tid_entry[tid])); } tid_entry[tid] = &ring_sts->list; } /* clear tx plan */ list_del(&tx_plan->list); _phl_reset_tx_plan(phl_info, tx_plan); } } enum rtw_phl_status phl_register_handler(struct rtw_phl_com_t *phl_com, struct rtw_phl_handler *handler) { enum rtw_phl_status phl_status = RTW_PHL_STATUS_FAILURE; _os_tasklet *tasklet = NULL; _os_workitem *workitem = NULL; void *drv_priv = phlcom_to_drvpriv(phl_com); FUNCIN_WSTS(phl_status); #ifdef CONFIG_RTW_OS_HANDLER_EXT _os_init_handler_ext(drv_priv, handler); #endif /* CONFIG_RTW_OS_HANDLER_EXT */ if (handler->type == RTW_PHL_HANDLER_PRIO_HIGH) { tasklet = &handler->os_handler.u.tasklet; phl_status = _os_tasklet_init(drv_priv, tasklet, handler->callback, handler); } else if (handler->type == RTW_PHL_HANDLER_PRIO_LOW) { workitem = &handler->os_handler.u.workitem; phl_status = _os_workitem_init(drv_priv, workitem, handler->callback, workitem); } else if (handler->type == RTW_PHL_HANDLER_PRIO_NORMAL) { _os_sema_init(drv_priv, &(handler->os_handler.hdlr_sema), 0); handler->os_handler.hdlr_created = false; phl_status = RTW_PHL_STATUS_SUCCESS; } else { PHL_TRACE(COMP_PHL_DBG, _PHL_INFO_, "[WARNING] unknown handle type(%d)\n", handler->type); } if (RTW_PHL_STATUS_SUCCESS != phl_status) phl_deregister_handler(phl_com, handler); FUNCOUT_WSTS(phl_status); return phl_status; } enum rtw_phl_status phl_deregister_handler( struct rtw_phl_com_t *phl_com, struct rtw_phl_handler *handler) { enum rtw_phl_status phl_status = RTW_PHL_STATUS_FAILURE; _os_tasklet *tasklet = NULL; _os_workitem *workitem = NULL; _os_thread *thread = NULL; void *drv_priv = phlcom_to_drvpriv(phl_com); FUNCIN_WSTS(phl_status); #ifdef CONFIG_RTW_OS_HANDLER_EXT _os_deinit_handler_ext(drv_priv, handler); #endif /* CONFIG_RTW_OS_HANDLER_EXT */ if (handler->type == RTW_PHL_HANDLER_PRIO_HIGH) { tasklet = &handler->os_handler.u.tasklet; phl_status = _os_tasklet_deinit(drv_priv, tasklet); } else if (handler->type == RTW_PHL_HANDLER_PRIO_LOW) { workitem = &handler->os_handler.u.workitem; phl_status = _os_workitem_deinit(drv_priv, workitem); } else if (handler->type == RTW_PHL_HANDLER_PRIO_NORMAL) { thread = &handler->os_handler.u.thread; if (handler->os_handler.hdlr_created == true) { _os_thread_stop(drv_priv, thread); _os_sema_up(drv_priv, &(handler->os_handler.hdlr_sema)); phl_status = _os_thread_deinit(drv_priv, thread); } else { phl_status = RTW_PHL_STATUS_SUCCESS; } _os_sema_free(drv_priv, &(handler->os_handler.hdlr_sema)); } else { PHL_TRACE(COMP_PHL_DBG, _PHL_INFO_, "[WARNING] unknown handle type(%d)\n", handler->type); } if (RTW_PHL_STATUS_SUCCESS != phl_status) { PHL_TRACE(COMP_PHL_DBG, _PHL_INFO_, "[WARNING] deregister handler fail (status = 0x%08X)\n", phl_status); } FUNCOUT_WSTS(phl_status); return phl_status; } static int _phl_thread_handler(void *context) { struct rtw_phl_handler *phl_handler; struct phl_info_t *phl_info; void *d; phl_handler = (struct rtw_phl_handler *)phl_container_of(context, struct rtw_phl_handler, os_handler); phl_info = (struct phl_info_t *)phl_handler->context; d = phl_to_drvpriv(phl_info); PHL_TRACE(COMP_PHL_DBG, _PHL_INFO_, "%s start\n", phl_handler->cb_name); while (1) { _os_sema_down(d, &phl_handler->os_handler.hdlr_sema); if (_os_thread_check_stop(d, (_os_thread*)context)) break; phl_handler->callback(context); } _os_thread_wait_stop(d, (_os_thread*)context); _os_sema_free(d, &phl_handler->os_handler.hdlr_sema); PHL_TRACE(COMP_PHL_DBG, _PHL_INFO_, "%s down\n", phl_handler->cb_name); return 0; } enum rtw_phl_status phl_schedule_handler( struct rtw_phl_com_t *phl_com, struct rtw_phl_handler *handler) { enum rtw_phl_status phl_status = RTW_PHL_STATUS_FAILURE; _os_tasklet *tasklet = NULL; _os_workitem *workitem = NULL; _os_thread *thread = NULL; void *drv_priv = phlcom_to_drvpriv(phl_com); FUNCIN_WSTS(phl_status); if (handler->type == RTW_PHL_HANDLER_PRIO_HIGH) { tasklet = &handler->os_handler.u.tasklet; phl_status = _os_tasklet_schedule(drv_priv, tasklet); } else if (handler->type == RTW_PHL_HANDLER_PRIO_LOW) { workitem = &handler->os_handler.u.workitem; phl_status = _os_workitem_schedule(drv_priv, workitem); } else if (handler->type == RTW_PHL_HANDLER_PRIO_NORMAL) { thread = &handler->os_handler.u.thread; if (handler->os_handler.hdlr_created == false) { phl_status = _os_thread_init(drv_priv, thread, _phl_thread_handler, thread, handler->cb_name); if (phl_status == RTW_PHL_STATUS_SUCCESS) { handler->os_handler.hdlr_created = true; _os_thread_schedule(drv_priv, thread); _os_sema_up(drv_priv, &(handler->os_handler.hdlr_sema)); } } else { _os_sema_up(drv_priv, &(handler->os_handler.hdlr_sema)); phl_status = RTW_PHL_STATUS_SUCCESS; } } else { PHL_TRACE(COMP_PHL_DBG, _PHL_INFO_, "[WARNING] unknown handle type(%d)\n", handler->type); } FUNCOUT_WSTS(phl_status); return phl_status; } #ifdef CONFIG_PHL_H2C_PKT_POOL_STATS_CHECK void phl_set_h2c_pkt_alloc_cnt(struct phl_info_t *phl_info, struct rtw_h2c_pkt *h2c_pkt) { struct phl_h2c_pkt_alloc_cnt *h2c_alloc_cnt = &(phl_info->h2c_alloc_cnt); enum rtw_phl_comm_module pkt_src = h2c_pkt->pkt_src; _os_atomic_dec(phl_to_drvpriv(phl_info), &h2c_alloc_cnt->h2c_query_cnt); switch (pkt_src) { case RTW_MODULE_MAC: _os_atomic_inc(phl_to_drvpriv(phl_info), &h2c_alloc_cnt->h2c_mac_cnt); break; case RTW_MODULE_BB: _os_atomic_inc(phl_to_drvpriv(phl_info), &h2c_alloc_cnt->h2c_bb_cnt); break; case RTW_MODULE_RF: _os_atomic_inc(phl_to_drvpriv(phl_info), &h2c_alloc_cnt->h2c_rf_cnt); break; case RTW_MODULE_BTC: _os_atomic_inc(phl_to_drvpriv(phl_info), &h2c_alloc_cnt->h2c_btc_cnt); break; } } void phl_unset_h2c_pkt_alloc_cnt(struct phl_info_t *phl_info, struct rtw_h2c_pkt *h2c_pkt) { struct phl_h2c_pkt_alloc_cnt *h2c_alloc_cnt = &(phl_info->h2c_alloc_cnt); switch (h2c_pkt->pkt_src) { case RTW_MODULE_MAC: _os_atomic_dec(phl_to_drvpriv(phl_info), &h2c_alloc_cnt->h2c_mac_cnt); break; case RTW_MODULE_BB: _os_atomic_dec(phl_to_drvpriv(phl_info), &h2c_alloc_cnt->h2c_bb_cnt); break; case RTW_MODULE_RF: _os_atomic_dec(phl_to_drvpriv(phl_info), &h2c_alloc_cnt->h2c_rf_cnt); break; case RTW_MODULE_BTC: _os_atomic_dec(phl_to_drvpriv(phl_info), &h2c_alloc_cnt->h2c_btc_cnt); break; } } void phl_set_return_h2c_pkt(struct phl_info_t *phl_info, struct rtw_h2c_pkt *h2c_pkt) { struct rtw_h2c_pkt_return_list *h2c_pkt_return_list = &phl_info->h2c_pkt_return_list; struct rtw_h2c_pkt_return *h2c_pkt_return = h2c_pkt_return_list->h2c_pkt_return; void *drv = phl_to_drvpriv(phl_info); _os_spinlockfg sp_flags; int oldest_index; _os_spinlock(drv, &h2c_pkt_return_list->lock, _irq, &sp_flags); oldest_index = h2c_pkt_return_list->oldest_index; h2c_pkt_return[oldest_index].id = h2c_pkt->id; h2c_pkt_return[oldest_index].pkt_src = h2c_pkt->pkt_src; h2c_pkt_return[oldest_index].timestamp = _os_get_cur_time_us(); h2c_pkt_return_list->oldest_index = (oldest_index + 1) % H2C_LAST_RETURN_NUM; _os_spinunlock(drv, &h2c_pkt_return_list->lock, _irq, &sp_flags); } #endif static enum rtw_phl_status enqueue_h2c_pkt( struct phl_info_t *phl_info, struct phl_queue *pool_list, struct rtw_h2c_pkt *h2c_pkt, u8 pos) { enum rtw_phl_status pstatus = RTW_PHL_STATUS_FAILURE; void *drv = phl_to_drvpriv(phl_info); _os_spinlockfg sp_flags; if (h2c_pkt != NULL) { _os_spinlock(drv, &pool_list->lock, _irq, &sp_flags); if (_tail == pos) list_add_tail(&h2c_pkt->list, &pool_list->queue); else if (_first == pos) list_add(&h2c_pkt->list, &pool_list->queue); pool_list->cnt++; _os_spinunlock(drv, &pool_list->lock, _irq, &sp_flags); pstatus = RTW_PHL_STATUS_SUCCESS; } return pstatus; } static struct rtw_h2c_pkt *dequeue_h2c_pkt(struct phl_info_t *phl_info, struct phl_queue *pool_list) { struct rtw_h2c_pkt *h2c_pkt = NULL; void *drv = phl_to_drvpriv(phl_info); _os_spinlockfg sp_flags; _os_spinlock(drv, &pool_list->lock, _irq, &sp_flags); if (list_empty(&pool_list->queue)) { h2c_pkt = NULL; } else { h2c_pkt = list_first_entry(&pool_list->queue, struct rtw_h2c_pkt, list); list_del(&h2c_pkt->list); pool_list->cnt--; } _os_spinunlock(drv, &pool_list->lock, _irq, &sp_flags); return h2c_pkt; } static void _phl_reset_h2c_pkt(struct phl_info_t *phl_info, struct rtw_h2c_pkt *h2c_pkt, u32 buf_len) { enum rtw_h2c_pkt_type type = h2c_pkt->type; _os_mem_set(phl_to_drvpriv(phl_info), h2c_pkt->vir_head, 0, buf_len); h2c_pkt->buf_len = buf_len; h2c_pkt->id = 0; h2c_pkt->host_idx = 0; h2c_pkt->data_len = 0; h2c_pkt->h2c_seq = 0; switch (type) { case H2CB_TYPE_CMD: h2c_pkt->vir_data = h2c_pkt->vir_head + FWCMD_HDR_LEN + _WD_BODY_LEN; h2c_pkt->vir_tail = h2c_pkt->vir_data; h2c_pkt->vir_end = h2c_pkt->vir_data + H2C_CMD_LEN; break; case H2CB_TYPE_DATA: h2c_pkt->vir_data = h2c_pkt->vir_head + FWCMD_HDR_LEN + _WD_BODY_LEN; h2c_pkt->vir_tail = h2c_pkt->vir_data; h2c_pkt->vir_end = h2c_pkt->vir_data + H2C_DATA_LEN; break; case H2CB_TYPE_LONG_DATA: h2c_pkt->vir_data = h2c_pkt->vir_head + FWCMD_HDR_LEN + _WD_BODY_LEN; h2c_pkt->vir_tail = h2c_pkt->vir_data; h2c_pkt->vir_end = h2c_pkt->vir_data + H2C_LONG_DATA_LEN; break; case H2CB_TYPE_MAX: PHL_TRACE(COMP_PHL_DBG, _PHL_DEBUG_, "_phl_reset_h2c_pkt(): Unsupported case:%d, please check it\n", type); break; default: PHL_TRACE(COMP_PHL_DBG, _PHL_DEBUG_, "_phl_reset_h2c_pkt(): Unrecognize case:%d, please check it\n", type); break; } } enum rtw_phl_status phl_enqueue_busy_h2c_pkt(struct phl_info_t *phl_info, struct rtw_h2c_pkt *h2c_pkt, u8 pos) { struct phl_h2c_pkt_pool *h2c_pkt_pool = (struct phl_h2c_pkt_pool *)phl_info->h2c_pool; struct phl_queue *queue = &h2c_pkt_pool->busy_h2c_pkt_list; return enqueue_h2c_pkt(phl_info, queue, h2c_pkt, pos); } enum rtw_phl_status phl_enqueue_idle_h2c_pkt( struct phl_info_t *phl_info, struct rtw_h2c_pkt *h2c_pkt) { enum rtw_phl_status pstatus = RTW_PHL_STATUS_FAILURE; struct phl_h2c_pkt_pool *h2c_pkt_pool = (struct phl_h2c_pkt_pool *)phl_info->h2c_pool; struct phl_queue *queue = NULL; int *idle_cnt = NULL; u32 buf_len = 0; if (!h2c_pkt) return pstatus; switch (h2c_pkt->type) { case H2CB_TYPE_CMD: buf_len = FWCMD_HDR_LEN + _WD_BODY_LEN + H2C_CMD_LEN; queue = &h2c_pkt_pool->idle_h2c_pkt_cmd_list; idle_cnt = &h2c_pkt_pool->idle_h2c_pkt_cmd_list.cnt; break; case H2CB_TYPE_DATA: buf_len = FWCMD_HDR_LEN + _WD_BODY_LEN + H2C_DATA_LEN; queue = &h2c_pkt_pool->idle_h2c_pkt_data_list; idle_cnt = &h2c_pkt_pool->idle_h2c_pkt_data_list.cnt; break; case H2CB_TYPE_LONG_DATA: buf_len = FWCMD_HDR_LEN + _WD_BODY_LEN + H2C_LONG_DATA_LEN; queue = &h2c_pkt_pool->idle_h2c_pkt_ldata_list; idle_cnt = &h2c_pkt_pool->idle_h2c_pkt_ldata_list.cnt; break; case H2CB_TYPE_MAX: PHL_ERR("%s : cannot find the matching case(%d).\n", __func__, h2c_pkt->type); break; default: PHL_ERR("%s : cannot find the matching cases(%d).\n", __func__, h2c_pkt->type); break; } if (!queue) return pstatus; #ifdef CONFIG_PHL_H2C_PKT_POOL_STATS_CHECK phl_set_return_h2c_pkt(phl_info, h2c_pkt); phl_unset_h2c_pkt_alloc_cnt(phl_info, h2c_pkt); #endif _phl_reset_h2c_pkt(phl_info, h2c_pkt, buf_len); pstatus = enqueue_h2c_pkt(phl_info, queue, h2c_pkt, _tail); PHL_TRACE(COMP_PHL_DBG, _PHL_DEBUG_, "%s : remaining %d (type %d).\n", __func__, *idle_cnt, h2c_pkt->type); return pstatus; } struct rtw_h2c_pkt *phl_query_busy_h2c_pkt(struct phl_info_t *phl_info) { struct phl_h2c_pkt_pool *h2c_pkt_pool = NULL; struct rtw_h2c_pkt *h2c_pkt = NULL; struct phl_queue *queue = NULL; h2c_pkt_pool = (struct phl_h2c_pkt_pool *)phl_info->h2c_pool; queue = &h2c_pkt_pool->busy_h2c_pkt_list; h2c_pkt = dequeue_h2c_pkt(phl_info, queue); return h2c_pkt; } struct rtw_h2c_pkt *phl_query_idle_h2c_pkt(struct phl_info_t *phl_info, u8 type) { struct phl_h2c_pkt_pool *h2c_pkt_pool = NULL; struct rtw_h2c_pkt *h2c_pkt = NULL; enum rtw_h2c_pkt_type h2c_type = (enum rtw_h2c_pkt_type)type; struct phl_queue *queue = NULL; int *idle_cnt = NULL; h2c_pkt_pool = (struct phl_h2c_pkt_pool *)phl_info->h2c_pool; switch (h2c_type) { case H2CB_TYPE_CMD: queue = &h2c_pkt_pool->idle_h2c_pkt_cmd_list; idle_cnt = &h2c_pkt_pool->idle_h2c_pkt_cmd_list.cnt; break; case H2CB_TYPE_DATA: queue = &h2c_pkt_pool->idle_h2c_pkt_data_list; idle_cnt = &h2c_pkt_pool->idle_h2c_pkt_data_list.cnt; break; case H2CB_TYPE_LONG_DATA: queue = &h2c_pkt_pool->idle_h2c_pkt_ldata_list; idle_cnt = &h2c_pkt_pool->idle_h2c_pkt_ldata_list.cnt; break; case H2CB_TYPE_MAX: PHL_TRACE(COMP_PHL_DBG, _PHL_DEBUG_, "phl_query_idle_h2c_pkt(): Unsupported case:%d, please check it\n", h2c_type); break; default: PHL_TRACE(COMP_PHL_DBG, _PHL_DEBUG_, "phl_query_idle_h2c_pkt(): Unrecognize case:%d, please check it\n", h2c_type); break; } if (!queue) return NULL; PHL_TRACE(COMP_PHL_DBG, _PHL_DEBUG_, "phl_query_idle_h2c_pkt => remaining %d (type %d).\n", *idle_cnt, h2c_type); h2c_pkt = dequeue_h2c_pkt(phl_info, queue); #ifdef CONFIG_PHL_H2C_PKT_POOL_STATS_CHECK if (h2c_pkt) _os_atomic_inc(phl_to_drvpriv(phl_info), &(phl_info->h2c_alloc_cnt.h2c_query_cnt)); #endif return h2c_pkt; } #if 0 static enum rtw_phl_status phl_release_target_h2c_pkt( struct phl_info_t *phl_info, struct phl_h2c_pkt_pool *h2c_pkt_pool, struct rtw_h2c_pkt *h2c_pkt) { enum rtw_phl_status pstatus = RTW_PHL_STATUS_FAILURE; if (h2c_pkt_pool != NULL && h2c_pkt != NULL) { phl_enqueue_idle_h2c_pkt(phl_info, h2c_pkt); pstatus = RTW_PHL_STATUS_SUCCESS; } return pstatus; } #endif static void _phl_free_h2c_pkt(struct phl_info_t *phl_info, struct rtw_h2c_pkt *h2c_pkt_buf) { u16 i = 0; struct rtw_h2c_pkt *h2c_pkt = h2c_pkt_buf; struct phl_hci_trx_ops *hci_trx_ops = phl_info->hci_trx_ops; if (NULL != h2c_pkt) { for (i = 0; i < MAX_H2C_PKT_NUM; i++) { if (NULL == h2c_pkt->vir_head) continue; hci_trx_ops->free_h2c_pkt_buf(phl_info, h2c_pkt); h2c_pkt->vir_head = NULL; h2c_pkt++; } _os_mem_free(phl_to_drvpriv(phl_info), h2c_pkt_buf, sizeof(struct rtw_h2c_pkt) * MAX_H2C_PKT_NUM); h2c_pkt_buf = NULL; } } struct rtw_h2c_pkt *_phl_alloc_h2c_pkt(struct phl_info_t *phl_info, struct phl_h2c_pkt_pool *h2c_pool) { enum rtw_phl_status pstatus = RTW_PHL_STATUS_FAILURE; struct phl_hci_trx_ops *hci_trx_ops = phl_info->hci_trx_ops; struct rtw_h2c_pkt *h2c_pkt = NULL; struct rtw_h2c_pkt *h2c_pkt_root = NULL; struct phl_h2c_pkt_pool *h2c_pkt_pool = h2c_pool; #ifdef CONFIG_H2C_NONCACHE_ADDR enum cache_addr_type cache = NONCACHE_ADDR; #else enum cache_addr_type cache = CACHE_ADDR; #endif u32 buf_len = 0; int i; buf_len = sizeof(struct rtw_h2c_pkt) * MAX_H2C_PKT_NUM; h2c_pkt_root = _os_mem_alloc(phl_to_drvpriv(phl_info), buf_len); h2c_pkt = h2c_pkt_root; if (h2c_pkt != NULL) { for (i = 0; i < MAX_H2C_PKT_NUM; i++) { h2c_pkt->cache = cache; buf_len = get_h2c_size_by_range(i); h2c_pkt->buf_len = buf_len; hci_trx_ops->alloc_h2c_pkt_buf(phl_info, h2c_pkt, buf_len); if (NULL == h2c_pkt->vir_head) { pstatus = RTW_PHL_STATUS_RESOURCE; break; } h2c_pkt->vir_data = h2c_pkt->vir_head + FWCMD_HDR_LEN + _WD_BODY_LEN; h2c_pkt->vir_tail = h2c_pkt->vir_data; INIT_LIST_HEAD(&h2c_pkt->list); if (i < _H2CB_CMD_QLEN) { h2c_pkt->type = H2CB_TYPE_CMD; h2c_pkt->vir_end = h2c_pkt->vir_data + H2C_CMD_LEN; enqueue_h2c_pkt(phl_info, &h2c_pkt_pool->idle_h2c_pkt_cmd_list, h2c_pkt, _tail); } else if (i < _H2CB_CMD_QLEN + _H2CB_DATA_QLEN) { h2c_pkt->type = H2CB_TYPE_DATA; h2c_pkt->vir_end = h2c_pkt->vir_data + H2C_DATA_LEN; enqueue_h2c_pkt(phl_info, &h2c_pkt_pool->idle_h2c_pkt_data_list, h2c_pkt, _tail); } else { h2c_pkt->type = H2CB_TYPE_LONG_DATA; h2c_pkt->vir_end = h2c_pkt->vir_data + H2C_LONG_DATA_LEN; enqueue_h2c_pkt(phl_info, &h2c_pkt_pool->idle_h2c_pkt_ldata_list, h2c_pkt, _tail); } h2c_pkt++; pstatus = RTW_PHL_STATUS_SUCCESS; } } if (RTW_PHL_STATUS_SUCCESS != pstatus) { _phl_free_h2c_pkt(phl_info, h2c_pkt_root); h2c_pkt_root = NULL; } return h2c_pkt_root; } static void _phl_free_h2c_pool(struct phl_info_t *phl_info) { struct phl_h2c_pkt_pool *h2c_pkt_pool = NULL; void *drv_priv = phl_to_drvpriv(phl_info); FUNCIN(); h2c_pkt_pool = phl_info->h2c_pool; if (NULL != h2c_pkt_pool) { h2c_pkt_pool->idle_h2c_pkt_cmd_list.cnt = 0; h2c_pkt_pool->idle_h2c_pkt_data_list.cnt = 0; h2c_pkt_pool->idle_h2c_pkt_ldata_list.cnt = 0; _phl_free_h2c_pkt(phl_info, h2c_pkt_pool->h2c_pkt_buf); h2c_pkt_pool->h2c_pkt_buf = NULL; _os_spinlock_free(drv_priv, &h2c_pkt_pool->idle_h2c_pkt_cmd_list.lock); _os_spinlock_free(drv_priv, &h2c_pkt_pool->idle_h2c_pkt_data_list.lock); _os_spinlock_free(drv_priv, &h2c_pkt_pool->idle_h2c_pkt_ldata_list.lock); _os_spinlock_free(drv_priv, &h2c_pkt_pool->busy_h2c_pkt_list.lock); _os_spinlock_free(drv_priv, &h2c_pkt_pool->recycle_lock); _os_mem_free(phl_to_drvpriv(phl_info), h2c_pkt_pool, sizeof(struct phl_h2c_pkt_pool)); } FUNCOUT(); } enum rtw_phl_status _phl_alloc_h2c_pool(struct phl_info_t *phl_info) { enum rtw_phl_status pstatus = RTW_PHL_STATUS_FAILURE; struct phl_h2c_pkt_pool *h2c_pkt_pool = NULL; struct rtw_h2c_pkt *h2c_pkt_buf = NULL; void *drv_priv = NULL; FUNCIN_WSTS(pstatus); drv_priv = phl_to_drvpriv(phl_info); h2c_pkt_pool = _os_mem_alloc(drv_priv, sizeof(struct phl_h2c_pkt_pool)); if (NULL != h2c_pkt_pool) { INIT_LIST_HEAD(&h2c_pkt_pool->idle_h2c_pkt_cmd_list.queue); h2c_pkt_pool->idle_h2c_pkt_cmd_list.cnt = 0; INIT_LIST_HEAD(&h2c_pkt_pool->idle_h2c_pkt_data_list.queue); h2c_pkt_pool->idle_h2c_pkt_data_list.cnt = 0; INIT_LIST_HEAD(&h2c_pkt_pool->idle_h2c_pkt_ldata_list.queue); h2c_pkt_pool->idle_h2c_pkt_ldata_list.cnt = 0; INIT_LIST_HEAD(&h2c_pkt_pool->busy_h2c_pkt_list.queue); h2c_pkt_pool->busy_h2c_pkt_list.cnt = 0; _os_spinlock_init(drv_priv, &h2c_pkt_pool->idle_h2c_pkt_cmd_list.lock); _os_spinlock_init(drv_priv, &h2c_pkt_pool->idle_h2c_pkt_data_list.lock); _os_spinlock_init(drv_priv, &h2c_pkt_pool->idle_h2c_pkt_ldata_list.lock); _os_spinlock_init(drv_priv, &h2c_pkt_pool->busy_h2c_pkt_list.lock); _os_spinlock_init(drv_priv, &h2c_pkt_pool->recycle_lock); h2c_pkt_buf = _phl_alloc_h2c_pkt(phl_info, h2c_pkt_pool); if (NULL == h2c_pkt_buf) { _os_spinlock_free(drv_priv, &h2c_pkt_pool->idle_h2c_pkt_cmd_list.lock); _os_spinlock_free(drv_priv, &h2c_pkt_pool->idle_h2c_pkt_data_list.lock); _os_spinlock_free(drv_priv, &h2c_pkt_pool->idle_h2c_pkt_ldata_list.lock); _os_spinlock_free(drv_priv, &h2c_pkt_pool->busy_h2c_pkt_list.lock); _os_spinlock_free(drv_priv, &h2c_pkt_pool->recycle_lock); _os_mem_free(drv_priv, h2c_pkt_pool, sizeof(struct phl_h2c_pkt_pool)); h2c_pkt_pool = NULL; pstatus = RTW_PHL_STATUS_RESOURCE; } else { h2c_pkt_pool->h2c_pkt_buf = h2c_pkt_buf; pstatus = RTW_PHL_STATUS_SUCCESS; } } if (RTW_PHL_STATUS_SUCCESS == pstatus) phl_info->h2c_pool = h2c_pkt_pool; FUNCOUT_WSTS(pstatus); return pstatus; } #ifdef CONFIG_PHL_H2C_PKT_POOL_STATS_CHECK static enum rtw_phl_status phl_alloc_h2c_cnt(struct phl_info_t *phl_info) { struct phl_h2c_pkt_alloc_cnt h2c_pkt_alloc_cnt = phl_info->h2c_alloc_cnt; struct rtw_h2c_pkt_return_list *h2c_pkt_return_list = &phl_info->h2c_pkt_return_list; struct rtw_h2c_pkt_return *h2c_pkt_return = phl_info->h2c_pkt_return_list.h2c_pkt_return; void *drv = phl_to_drvpriv(phl_info); _os_spinlockfg sp_flags; u16 i; _os_atomic_set(drv, &h2c_pkt_alloc_cnt.busy_h2c_pkt_cmd_cnt, 0); _os_atomic_set(drv, &h2c_pkt_alloc_cnt.busy_h2c_pkt_data_cnt, 0); _os_atomic_set(drv, &h2c_pkt_alloc_cnt.busy_h2c_pkt_ldata_cnt, 0); _os_atomic_set(drv, &h2c_pkt_alloc_cnt.h2c_query_cnt, 0); _os_atomic_set(drv, &h2c_pkt_alloc_cnt.h2c_mac_cnt, 0); _os_atomic_set(drv, &h2c_pkt_alloc_cnt.h2c_bb_cnt, 0); _os_atomic_set(drv, &h2c_pkt_alloc_cnt.h2c_rf_cnt, 0); _os_atomic_set(drv, &h2c_pkt_alloc_cnt.h2c_btc_cnt, 0); _os_spinlock_init(drv, &h2c_pkt_return_list->lock); _os_spinlock(drv, &h2c_pkt_return_list->lock, _irq, &sp_flags); for (i = 0; i < H2C_LAST_RETURN_NUM; i++) { h2c_pkt_return[i].id = 0; h2c_pkt_return[i].pkt_src = 0; h2c_pkt_return[i].timestamp = 0; } h2c_pkt_return_list->oldest_index = 0; _os_spinunlock(drv, &h2c_pkt_return_list->lock, _irq, &sp_flags); return RTW_PHL_STATUS_SUCCESS; } #endif void phl_trx_free_handler(void *phl) { struct phl_info_t *phl_info = (struct phl_info_t *)phl; struct rtw_phl_handler *tx_handler = &phl_info->phl_tx_handler; struct rtw_phl_handler *rx_handler = &phl_info->phl_rx_handler; struct rtw_phl_handler *event_handler = &phl_info->phl_event_handler; FUNCIN(); phl_deregister_handler(phl_info->phl_com, event_handler); phl_deregister_handler(phl_info->phl_com, rx_handler); phl_deregister_handler(phl_info->phl_com, tx_handler); FUNCOUT(); } void phl_trx_free_sw_rsc(void *phl) { struct phl_info_t *phl_info = (struct phl_info_t *)phl; struct phl_hci_trx_ops *hci_trx_ops = phl_info->hci_trx_ops; void *drv_priv = NULL; FUNCIN(); drv_priv = phl_to_drvpriv(phl_info); _phl_free_h2c_pool(phl_info); hci_trx_ops->hci_trx_deinit(phl_info); phl_rx_deinit(phl_info); _phl_ring_status_deinit(phl_info); #ifdef CONFIG_PHL_H2C_PKT_POOL_STATS_CHECK _os_spinlock_free(drv_priv, &phl_info->h2c_pkt_return_list.lock); #endif _os_spinlock_free(drv_priv, &phl_info->t_ring_list_lock); _os_spinlock_free(drv_priv, &phl_info->rx_ring_lock); _os_spinlock_free(drv_priv, &phl_info->t_fctrl_result_lock); pq_deinit(drv_priv, &phl_info->t_ring_free_q); FUNCOUT(); } enum rtw_phl_status phl_datapath_start_hw(struct phl_info_t *phl_info) { enum rtw_phl_status pstatus = RTW_PHL_STATUS_FAILURE; struct phl_hci_trx_ops *hci_trx_ops = phl_info->hci_trx_ops; do { pstatus = hci_trx_ops->trx_cfg(phl_info); if (RTW_PHL_STATUS_SUCCESS != pstatus) break; rtw_hal_notification(phl_info->hal, MSG_EVT_DATA_PATH_START, HW_BAND_MAX); } while (false); return pstatus; } void phl_datapath_stop_hw(struct phl_info_t *phl_info) { struct phl_hci_trx_ops *hci_trx_ops = phl_info->hci_trx_ops; hci_trx_ops->trx_stop(phl_info); rtw_hal_notification(phl_info->hal, MSG_EVT_DATA_PATH_STOP, HW_BAND_MAX); } void phl_datapath_reset(struct phl_info_t *phl_info, u8 type) { struct phl_hci_trx_ops *hci_trx_ops = phl_info->hci_trx_ops; hci_trx_ops->trx_reset(phl_info, type); } enum rtw_phl_status phl_datapath_start_sw(struct phl_info_t *phl_info, enum phl_module_id id) { struct phl_data_ctl_t ctl = {0}; ctl.id = id; ctl.cmd = PHL_DATA_CTL_SW_TX_RESUME; if (phl_data_ctrler(phl_info, &ctl, NULL) != RTW_PHL_STATUS_SUCCESS) PHL_WARN("%s: tx resume fail!\n", __func__); ctl.cmd = PHL_DATA_CTL_SW_RX_RESUME; if (phl_data_ctrler(phl_info, &ctl, NULL) != RTW_PHL_STATUS_SUCCESS) PHL_WARN("%s: rx resume fail!\n", __func__); return RTW_PHL_STATUS_SUCCESS; } void phl_datapath_stop_sw(struct phl_info_t *phl_info, enum phl_module_id id) { struct phl_data_ctl_t ctl = {0}; ctl.id = id; ctl.cmd = PHL_DATA_CTL_SW_TX_PAUSE; if (phl_data_ctrler(phl_info, &ctl, NULL) != RTW_PHL_STATUS_SUCCESS) PHL_WARN("%s: tx pause fail!\n", __func__); ctl.cmd = PHL_DATA_CTL_SW_RX_PAUSE; if (phl_data_ctrler(phl_info, &ctl, NULL) != RTW_PHL_STATUS_SUCCESS) PHL_WARN("%s: rx pause fail!\n", __func__); } void phl_datapath_deinit(struct phl_info_t *phl_info) { phl_trx_free_handler(phl_info); phl_trx_free_sw_rsc(phl_info); } enum rtw_phl_status phl_datapath_init(struct phl_info_t *phl_info) { enum rtw_phl_status pstatus = RTW_PHL_STATUS_FAILURE; struct phl_hci_trx_ops *hci_trx_ops = phl_info->hci_trx_ops; struct rtw_phl_handler *event_handler = &phl_info->phl_event_handler; #ifdef CONFIG_RTW_EVENT_HDL_USE_WQ _os_workitem *workitem = &event_handler->os_handler.u.workitem; #endif void *drv_priv = NULL; FUNCIN_WSTS(pstatus); drv_priv = phl_to_drvpriv(phl_info); do { #ifdef CONFIG_RTW_EVENT_HDL_USE_WQ _os_workitem_config_cpu(drv_priv, workitem, "EVENT_HDL", CPU_ID_EVENT_HDL); #endif INIT_LIST_HEAD(&phl_info->t_ring_list); INIT_LIST_HEAD(&phl_info->t_fctrl_result); _os_spinlock_init(drv_priv, &phl_info->t_ring_list_lock); _os_spinlock_init(drv_priv, &phl_info->rx_ring_lock); _os_spinlock_init(drv_priv, &phl_info->t_fctrl_result_lock); pq_init(drv_priv, &phl_info->t_ring_free_q); #if defined(CONFIG_RTW_EVENT_HDL_USE_WQ) || defined(CONFIG_PHL_HANDLER_WQ_HIGHPRI) event_handler->type = RTW_PHL_HANDLER_PRIO_LOW; #else #ifdef CONFIG_RTW_RX_EVENT_USE_THREAD event_handler->type = RTW_PHL_HANDLER_PRIO_NORMAL; #else event_handler->type = RTW_PHL_HANDLER_PRIO_HIGH; #endif #endif event_handler->callback = phl_event_indicator; event_handler->context = phl_info; event_handler->drv_priv = drv_priv; event_handler->status = 0; #ifdef CONFIG_RTW_OS_HANDLER_EXT event_handler->id = RTW_PHL_EVT_HANDLER; #endif /* CONFIG_RTW_OS_HANDLER_EXT */ pstatus = phl_register_handler(phl_info->phl_com, event_handler); if (RTW_PHL_STATUS_SUCCESS != pstatus) break; pstatus = _phl_ring_status_init(phl_info); if (RTW_PHL_STATUS_SUCCESS != pstatus) break; pstatus = phl_rx_init(phl_info); if (RTW_PHL_STATUS_SUCCESS != pstatus) break; pstatus = hci_trx_ops->hci_trx_init(phl_info); if (RTW_PHL_STATUS_SUCCESS != pstatus) break; /* allocate h2c pkt */ pstatus = _phl_alloc_h2c_pool(phl_info); if (RTW_PHL_STATUS_SUCCESS != pstatus) break; #ifdef CONFIG_PHL_H2C_PKT_POOL_STATS_CHECK pstatus = phl_alloc_h2c_cnt(phl_info); if (RTW_PHL_STATUS_SUCCESS != pstatus) break; #endif }while (false); if (RTW_PHL_STATUS_SUCCESS != pstatus) phl_datapath_deinit(phl_info); FUNCOUT_WSTS(pstatus); return pstatus; } static enum rtw_phl_status _phl_tx_pwr_notify(void *phl) { enum rtw_phl_status pstatus = RTW_PHL_STATUS_SUCCESS; pstatus = rtw_phl_tx_req_notify(phl); return pstatus; } #ifdef CONFIG_POWER_SAVE static void _phl_req_pwr_cb(void *priv, struct phl_msg *msg) { struct phl_info_t *phl_info = (struct phl_info_t *)priv; if (MSG_MDL_ID_FIELD(msg->msg_id) == PHL_MDL_TX) _os_atomic_set(phl_to_drvpriv(phl_info), &phl_info->phl_sw_tx_req_pwr, 0); else _os_atomic_set(phl_to_drvpriv(phl_info), &phl_info->phl_sw_rx_req_pwr, 0); if (IS_MSG_FAIL(msg->msg_id) || IS_MSG_CANCEL(msg->msg_id)) { PHL_WARN("%s(): request power failure.\n", __func__); return; } if (MSG_MDL_ID_FIELD(msg->msg_id) == PHL_MDL_TX) _phl_tx_pwr_notify(priv); else if (MSG_MDL_ID_FIELD(msg->msg_id) == PHL_MDL_RX) rtw_phl_start_rx_process(priv); } static void _phl_datapath_req_pwr(struct phl_info_t *phl_info, u8 type) { enum rtw_phl_status psts = RTW_PHL_STATUS_FAILURE; struct phl_msg msg = {0}; struct phl_msg_attribute attr = {0}; PHL_TRACE(COMP_PHL_DBG, _PHL_DEBUG_, "%s(): [DATA_CTRL] SW datapath paused by ps module and request power\n", __func__); SET_MSG_MDL_ID_FIELD(msg.msg_id, ((type == PHL_CTRL_TX) ? PHL_MDL_TX : PHL_MDL_RX)); SET_MSG_EVT_ID_FIELD(msg.msg_id, MSG_EVT_TRX_PWR_REQ); attr.completion.completion = _phl_req_pwr_cb; attr.completion.priv = phl_info; /* shall set req_pwr flag first before sending req_pwr msg */ if (PHL_CTRL_TX == type) _os_atomic_set(phl_to_drvpriv(phl_info), &phl_info->phl_sw_tx_req_pwr, 1); else _os_atomic_set(phl_to_drvpriv(phl_info), &phl_info->phl_sw_rx_req_pwr, 1); psts = phl_disp_eng_send_msg(phl_info, &msg, &attr, NULL); if (RTW_PHL_STATUS_SUCCESS != psts) { PHL_WARN("%s(): CANNOT send msg to request power.\n", __func__); if (PHL_CTRL_TX == type) _os_atomic_set(phl_to_drvpriv(phl_info), &phl_info->phl_sw_tx_req_pwr, 0); else _os_atomic_set(phl_to_drvpriv(phl_info), &phl_info->phl_sw_rx_req_pwr, 0); } } static bool _phl_datapath_chk_pwr(struct phl_info_t *phl_info, u8 type) { void *drvpriv = phl_to_drvpriv(phl_info); enum data_ctrl_mdl pause_id = 0; _os_atomic *trx_more; _os_atomic *req_pwr; struct rtw_ps_cap_t *ps_cap = &phl_info->phl_com->dev_cap.ps_cap; if (type == PHL_CTRL_TX) { pause_id = phl_info->pause_tx_id; trx_more = &phl_info->phl_sw_tx_more; req_pwr = &phl_info->phl_sw_tx_req_pwr; } else { pause_id = phl_info->pause_rx_id; trx_more = &phl_info->phl_sw_rx_more; req_pwr = &phl_info->phl_sw_rx_req_pwr; } if (pause_id & ~(DATA_CTRL_MDL_PS)) { PHL_TRACE(COMP_PHL_DBG, _PHL_DEBUG_, "%s(): [DATA_CTRL] SW datapath paused by module(0x%x)\n", __func__, pause_id); return false; } /** * For some ICs, we may pause datapath during the configuration flow of * entering/leaving low power state. In this case, the pause_id will also be * "DATA_CTRL_MDL_PS" but we don't need to send msg "MSG_EVT_TRX_PWR_REQ" * to leave low power state for this situation. Thus we check the capability * "ps_pause_tx" before sending msg "MSG_EVT_TRX_PWR_REQ". */ if (!ps_cap->ps_pause_tx) return false; /* only paused by ps module */ if (true == _os_atomic_read(drvpriv, trx_more) && false == _os_atomic_read(drvpriv, req_pwr)) _phl_datapath_req_pwr(phl_info, type); return true; } #endif bool phl_datapath_chk_trx_pause(struct phl_info_t *phl_info, u8 type) { void *drvpriv = phl_to_drvpriv(phl_info); _os_atomic *sw_sts; if (type == PHL_CTRL_TX) sw_sts = &phl_info->phl_sw_tx_sts; else sw_sts = &phl_info->phl_sw_rx_sts; if (PHL_TX_STATUS_SW_PAUSE == _os_atomic_read(drvpriv, sw_sts)) { #ifdef CONFIG_POWER_SAVE _phl_datapath_chk_pwr(phl_info, type); #endif return true; } return false; } void rtw_phl_tx_stop(void *phl) { struct phl_info_t *phl_info = (struct phl_info_t *)phl; struct phl_hci_trx_ops *hci_trx_ops = phl_info->hci_trx_ops; /* Pause SW Tx */ hci_trx_ops->req_tx_stop(phl_info); } void rtw_phl_tx_resume(void *phl) { struct phl_info_t *phl_info = (struct phl_info_t *)phl; struct phl_hci_trx_ops *hci_trx_ops = phl_info->hci_trx_ops; /* Resume SW Tx */ hci_trx_ops->trx_resume(phl_info, PHL_CTRL_TX); } enum rtw_phl_status rtw_phl_tx_req_notify(void *phl) { enum rtw_phl_status pstatus = RTW_PHL_STATUS_FAILURE; struct phl_info_t *phl_info = (struct phl_info_t *)phl; #ifdef SDIO_TX_THREAD phl_tx_sdio_wake_thrd(phl_info); #endif pstatus = phl_schedule_handler(phl_info->phl_com, &phl_info->phl_tx_handler); return pstatus; } struct rtw_phl_tx_ring * _phl_select_tx_ring(struct phl_info_t *phl_info, struct rtw_xmit_req *tx_req) { struct rtw_phl_tring_list *tring_list, *t; _os_list *list_head = &phl_info->t_ring_list; struct rtw_phl_tx_ring *ring = NULL; u16 macid = tx_req->mdata.macid; if (phl_info->use_onetxring) { /*just pick the first entry*/ tring_list = list_first_entry(list_head, struct rtw_phl_tring_list, list); ring = &tring_list->phl_ring[tx_req->mdata.cat]; } else { phl_list_for_loop_safe(tring_list, t, struct rtw_phl_tring_list, list_head, list) { if (macid != tring_list->macid) { continue; } else { ring = &tring_list->phl_ring[tx_req->mdata.cat]; break; } } } return ring; } enum rtw_phl_status rtw_phl_add_tx_req(void *phl, struct rtw_xmit_req *tx_req) { enum rtw_phl_status pstatus = RTW_PHL_STATUS_FAILURE; struct rtw_phl_tx_ring *ring = NULL; struct phl_info_t *phl_info = (struct phl_info_t *)phl; void *drv_priv = NULL; u16 ring_res = 0, rptr = 0; drv_priv = phl_to_drvpriv(phl_info); _os_spinlock(drv_priv, &phl_info->t_ring_list_lock, _bh, NULL); ring = _phl_select_tx_ring(phl_info, tx_req); if (NULL != ring) { rptr = (u16)_os_atomic_read(drv_priv, &ring->phl_idx); ring_res = phl_calc_avail_wptr(rptr, ring->core_idx, MAX_PHL_TX_RING_ENTRY_NUM); if (ring_res > 0) { ring->core_idx = (ring->core_idx + 1) % MAX_PHL_TX_RING_ENTRY_NUM; ring->entry[ring->core_idx] = (u8 *)tx_req; phl_tx_statistics(phl_info, tx_req); #ifdef CONFIG_PHL_TX_DBG if (tx_req->tx_dbg.en_dbg) { tx_req->tx_dbg.core_add_tx_t = _os_get_cur_time_us(); } #endif /* CONFIG_PHL_TX_DBG */ _os_atomic_set(drv_priv, &phl_info->phl_sw_tx_more, 1); pstatus = RTW_PHL_STATUS_SUCCESS; } else { PHL_TRACE(COMP_PHL_DBG, _PHL_INFO_, "no ring resource to add new tx request!\n"); pstatus = RTW_PHL_STATUS_RESOURCE; } } _os_spinunlock(drv_priv, &phl_info->t_ring_list_lock, _bh, NULL); return pstatus; } u16 rtw_phl_tring_rsc(void *phl, u16 macid, u8 tid) { struct rtw_phl_tring_list *tring_list, *t; struct rtw_phl_tx_ring *ring = NULL; struct phl_info_t *phl_info = (struct phl_info_t *)phl; void *drv_priv = NULL; _os_list *list_head = &phl_info->t_ring_list; u16 ring_res = 0, rptr = 0; drv_priv = phl_to_drvpriv(phl_info); phl_list_for_loop_safe(tring_list, t, struct rtw_phl_tring_list, list_head, list) { if (macid != tring_list->macid) { continue; } else { /* hana_todo check mgnt frame case */ ring = &tring_list->phl_ring[tid]; break; } } if (NULL != ring) { rptr = (u16)_os_atomic_read(drv_priv, &ring->phl_idx); ring_res = phl_calc_avail_rptr(rptr, ring->core_idx, MAX_PHL_TX_RING_ENTRY_NUM); } return ring_res; } enum rtw_phl_status phl_indic_pkt_complete(void *phl) { enum rtw_phl_status pstatus = RTW_PHL_STATUS_FAILURE; struct phl_info_t *phl_info = (struct phl_info_t *)phl; struct rtw_evt_info_t *evt_info = &phl_info->phl_com->evt_info; void *drv_priv = phl_to_drvpriv(phl_info); do { _os_spinlock(drv_priv, &evt_info->evt_lock, _bh, NULL); evt_info->evt_bitmap |= RTW_PHL_EVT_TX_RECYCLE; _os_spinunlock(drv_priv, &evt_info->evt_lock, _bh, NULL); pstatus = phl_schedule_handler(phl_info->phl_com, &phl_info->phl_event_handler); } while (false); return pstatus; } enum rtw_phl_status rtw_phl_recycle_tx_buf(void *phl, u8 *tx_buf_ptr) { enum rtw_phl_status pstatus = RTW_PHL_STATUS_FAILURE; #ifdef CONFIG_USB_HCI struct phl_info_t *phl_info = (struct phl_info_t *)phl; struct phl_hci_trx_ops *hci_trx_ops = phl_info->hci_trx_ops; pstatus = hci_trx_ops->recycle_tx_buf(phl, tx_buf_ptr); #endif return pstatus; } static enum rtw_phl_status _phl_cfg_tx_ampdu(void *phl, struct rtw_phl_stainfo_t *sta) { struct phl_info_t *phl_info = (struct phl_info_t *)phl; enum rtw_hal_status hsts = RTW_HAL_STATUS_FAILURE; hsts = rtw_hal_upd_ampdu_cctrl_info(phl_info->hal, sta); if (RTW_HAL_STATUS_SUCCESS != hsts) goto fail; hsts = rtw_hal_cfg_tx_ampdu(phl_info->hal, sta); if (RTW_HAL_STATUS_SUCCESS != hsts) goto fail; return RTW_PHL_STATUS_SUCCESS; fail: return RTW_PHL_STATUS_FAILURE; } #ifdef CONFIG_CMD_DISP struct cmd_amsdu_param { u8 macid; u8 enable; u8 amsdu_max_len; u8 qos_field_h; u8 qos_field_h_en; u8 mhdr_len; u8 vlan_tag_valid; }; struct cmd_hwseq_param { u16 macid; u8 ref_sel; u8 ssn_sel; u16 *hw_seq; }; enum rtw_phl_status phl_cmd_cfg_ampdu_hdl(struct phl_info_t *phl_info, u8 *param) { struct rtw_phl_stainfo_t *sta = (struct rtw_phl_stainfo_t *)param; PHL_INFO(" %s(), sta = %p !\n", __func__, sta); return _phl_cfg_tx_ampdu(phl_info, sta); } static enum rtw_phl_status _phl_cfg_tx_amsdu(void *phl, struct cmd_amsdu_param *pamsdu_param) { struct phl_info_t *phl_info = (struct phl_info_t *)phl; enum rtw_hal_status hsts = RTW_HAL_STATUS_FAILURE; hsts = rtw_hal_cfg_tx_amsdu(phl_info->hal, pamsdu_param->macid, pamsdu_param->enable, pamsdu_param->amsdu_max_len, pamsdu_param->qos_field_h, pamsdu_param->qos_field_h_en, pamsdu_param->mhdr_len, pamsdu_param->vlan_tag_valid); if (RTW_HAL_STATUS_SUCCESS != hsts) goto fail; return RTW_PHL_STATUS_SUCCESS; fail: return RTW_PHL_STATUS_FAILURE; } enum rtw_phl_status phl_cmd_cfg_amsdu_tx_hdl(struct phl_info_t *phl_info, u8 *param) { struct cmd_amsdu_param *pamsdu_param = (struct cmd_amsdu_param *)param; PHL_INFO(" %s(), pamsdu_param = %p !\n", __func__, pamsdu_param); return _phl_cfg_tx_amsdu(phl_info, pamsdu_param); } static void _phl_cfg_amsdu_tx_done(void *drv_priv, u8 *cmd, u32 cmd_len, enum rtw_phl_status status) { if (cmd) { _os_kmem_free(drv_priv, cmd, cmd_len); cmd = NULL; PHL_INFO("%s.....\n", __func__); } } enum rtw_phl_status phl_cmd_get_hwseq_hdl(struct phl_info_t *phl_info, u8 *param) { struct cmd_hwseq_param *hwseq_param = (struct cmd_hwseq_param *)param; enum rtw_hal_status hsts = RTW_HAL_STATUS_FAILURE; hsts = rtw_hal_get_hwseq(phl_info->hal, hwseq_param->macid, hwseq_param->ref_sel, hwseq_param->ssn_sel, hwseq_param->hw_seq); if (RTW_HAL_STATUS_SUCCESS != hsts) goto fail; return RTW_PHL_STATUS_SUCCESS; fail: return RTW_PHL_STATUS_FAILURE; } static void _phl_get_hw_sequence_done(void *drv_priv, u8 *cmd, u32 cmd_len, enum rtw_phl_status status) { if (cmd) { _os_kmem_free(drv_priv, cmd, cmd_len); cmd = NULL; PHL_INFO("%s.....\n", __func__); } } #endif /** * rtw_phl_get_hw_sequence - get hw sequence form CR or DMAC table * @phl: see struct phl_info_t * @cur_hw_seq: hw sequence value * @updated: if hw sequnece successfully updated, let caller know * @hw_ssn_mode: ssn mode; * HW_SSN_MODE_CR : 0 * HW_SSN_MODE_DMAC : 1 * @hw_ssn_sel: ssn select; currently each CR set or DMAC entry include 4 entry * HW_SSN_SEL0 : 0 * HW_SSN_SEL1 : 1 * HW_SSN_SEL2 : 2 * HW_SSN_SEL3 : 3 * returns enum RTW_PHL_STATUS */ enum rtw_phl_status rtw_phl_get_hw_sequence(void *phl, u16 *cur_hw_seq, u16 macid, u8 hw_ssn_mode, u8 hw_ssn_sel) { enum rtw_phl_status sts = RTW_PHL_STATUS_FAILURE; #ifdef CONFIG_CMD_DISP struct phl_info_t *phl_info = (struct phl_info_t *)phl; void *drv = phl_to_drvpriv(phl_info); u32 param_len = sizeof(struct cmd_hwseq_param); struct cmd_hwseq_param *param = _os_kmem_alloc(drv, param_len); if (NULL == param) { /* alloc fail */ PHL_ERR("%s : param alloc fail !! \n", __func__); return RTW_PHL_STATUS_FAILURE; } param->macid = macid; param->ref_sel = hw_ssn_mode; param->ssn_sel = hw_ssn_sel; param->hw_seq = cur_hw_seq; sts = phl_cmd_enqueue(phl_info, HW_BAND_0, MSG_EVT_HWSEQ_GET_HW_SEQUENCE, (u8 *)param, param_len, _phl_get_hw_sequence_done, PHL_CMD_WAIT, 0); if (is_cmd_failure(sts)) { /* Send cmd success, but wait cmd fail*/ PHL_ERR("%s : command send success but exec fail, sts = %d\n", __func__, sts); sts = RTW_PHL_STATUS_FAILURE; } else if (sts != RTW_PHL_STATUS_SUCCESS) { /* Send cmd fail */ PHL_ERR("%s : send command fail, sts = %d\n", __func__, sts); _os_kmem_free(phl_to_drvpriv(phl_info), param, param_len); sts = RTW_PHL_STATUS_CMD_DROP; } #else PHL_ERR("%s : CONFIG_CMD_DISP need to be enabled for MSG_EVT_HWSEQ_GET_HW_SEQUENCE !! \n", __func__); #endif return sts; } enum rtw_phl_status rtw_phl_cmd_cfg_amsdu_tx(struct rtw_wifi_role_t *wrole, struct rtw_phl_stainfo_t *sta, struct rtw_amsdu_tx_param *input_param, bool cmd_wait, u32 cmd_timeout) { enum rtw_phl_status sts = RTW_PHL_STATUS_FAILURE; #ifdef CONFIG_CMD_DISP struct phl_info_t *phl = wrole->phl_com->phl_priv; void *drv = phl_to_drvpriv(phl); u32 param_len = sizeof(struct cmd_amsdu_param); struct cmd_amsdu_param *param = _os_kmem_alloc(drv, param_len); if (param == NULL) { PHL_ERR("%s: alloc param failed!\n", __func__); return sts; } param->macid = input_param->macid; param->enable = input_param->enable; param->amsdu_max_len = input_param->amsdu_max_len; param->qos_field_h = input_param->qos_field_h; param->qos_field_h_en = input_param->qos_field_h_en; param->mhdr_len = input_param->mhdr_len; param->vlan_tag_valid = input_param->vlan_tag_valid; sts = phl_cmd_enqueue(phl, sta->rlink->hw_band, MSG_EVT_CFG_AMSDU_TX, (u8 *)param, param_len, _phl_cfg_amsdu_tx_done, PHL_CMD_NO_WAIT, 0); if (is_cmd_failure(sts)) { /* Send cmd success, but wait cmd fail*/ PHL_ERR("%s : command send success but exec fail, sts = %d\n", __func__, sts); sts = RTW_PHL_STATUS_FAILURE; } else if (sts != RTW_PHL_STATUS_SUCCESS) { /* Send cmd fail */ PHL_ERR("%s : send command fail, sts = %d\n", __func__, sts); _os_kmem_free(drv, param, param_len); sts = RTW_PHL_STATUS_CMD_DROP; } #else PHL_ERR("%s : CONFIG_CMD_DISP need to be enabled for MSG_EVT_CFG_AMSDU_TX !! \n", __func__); #endif return sts; } enum rtw_phl_status rtw_phl_cmd_cfg_ampdu(void *phl, struct rtw_wifi_role_t *wrole, struct rtw_phl_stainfo_t *sta, enum phl_cmd_type cmd_type, u32 cmd_timeout) { enum rtw_phl_status sts = RTW_PHL_STATUS_FAILURE; #ifdef CONFIG_CMD_DISP sts = phl_cmd_enqueue(phl, sta->rlink->hw_band, MSG_EVT_CFG_AMPDU, (u8 *)sta, 0, NULL, cmd_type, cmd_timeout); if (is_cmd_failure(sts)) { /* Send cmd success, but wait cmd fail*/ sts = RTW_PHL_STATUS_FAILURE; } else if (sts != RTW_PHL_STATUS_SUCCESS) { /* Send cmd fail */ sts = RTW_PHL_STATUS_FAILURE; } return sts; #else PHL_ERR("%s : CONFIG_CMD_DISP need to be enabled for MSG_EVT_CFG_AMPDU !! \n", __func__); return sts; #endif } void rtw_phl_cfg_hwamsdu_init(void *phl, u8 hwamsdu_en, u8 pkt_num, u8 single_amsdu_en, u8 last_padding_en) { struct phl_info_t *phl_info = (struct phl_info_t *)phl; rtw_hal_enable_hwamsdu(phl_info->hal, hwamsdu_en, pkt_num, single_amsdu_en, last_padding_en); PHL_TRACE(COMP_PHL_P2PPS, _PHL_INFO_, "rtw_phl_cfg_hwamsdu_init(): init ok\n"); } void phl_tx_watchdog(struct phl_info_t *phl_info) { struct phl_hci_trx_ops *trx_ops = phl_info->hci_trx_ops; struct rtw_stats *phl_stats = &phl_info->phl_com->phl_stats; phl_tx_traffic_upd(phl_stats); trx_ops->tx_watchdog(phl_info); } u8 rtw_phl_cvt_cat_to_tid(enum rtw_phl_ring_cat cat) { u8 tid = 0; switch (cat) { case RTW_PHL_RING_CAT_TID0: tid = 0; break; case RTW_PHL_RING_CAT_TID1: tid = 1; break; case RTW_PHL_RING_CAT_TID2: tid = 2; break; case RTW_PHL_RING_CAT_TID3: tid = 3; break; case RTW_PHL_RING_CAT_TID4: tid = 4; break; case RTW_PHL_RING_CAT_TID5: tid = 5; break; case RTW_PHL_RING_CAT_TID6: tid = 6; break; case RTW_PHL_RING_CAT_TID7: tid = 7; break; case RTW_PHL_RING_CAT_MGNT: case RTW_PHL_RING_CAT_HIQ: tid = 0; break; default: PHL_WARN("%s: this category(%d) is invalid to convert to tid\n", __func__, cat); break; } return tid; } /* note: this fucntion shall only be used on data frame */ enum rtw_phl_ring_cat rtw_phl_cvt_tid_to_cat(u8 tid) { enum rtw_phl_ring_cat cat = RTW_PHL_RING_CAT_TID0; switch (tid) { case 0: cat = RTW_PHL_RING_CAT_TID0; break; case 1: cat = RTW_PHL_RING_CAT_TID1; break; case 2: cat = RTW_PHL_RING_CAT_TID2; break; case 3: cat = RTW_PHL_RING_CAT_TID3; break; case 4: cat = RTW_PHL_RING_CAT_TID4; break; case 5: cat = RTW_PHL_RING_CAT_TID5; break; case 6: cat = RTW_PHL_RING_CAT_TID6; break; case 7: cat = RTW_PHL_RING_CAT_TID7; break; default: PHL_WARN("%s: this tid(%d) is invalid to convert to category\n", __func__, tid); break; } return cat; } enum data_ctrl_mdl _phl_get_ctrl_mdl(enum phl_module_id id) { enum data_ctrl_mdl ctrl_mdl = DATA_CTRL_MDL_NONE; switch (id) { case PHL_MDL_PHY_MGNT: ctrl_mdl = DATA_CTRL_MDL_CMD_CTRLER; break; case PHL_MDL_SER: ctrl_mdl = DATA_CTRL_MDL_SER; break; case PHL_MDL_POWER_MGNT: ctrl_mdl = DATA_CTRL_MDL_PS; break; case PHL_MDL_MRC: ctrl_mdl = DATA_CTRL_MDL_MRC; break; case PHL_FG_MDL_ECSA: ctrl_mdl = DATA_CTRL_MDL_ECSA; break; default: PHL_WARN("Unknown PHL module(%d) try to control datapath and is skipped!\n", id); ctrl_mdl = DATA_CTRL_MDL_NONE; break; } return ctrl_mdl; } enum rtw_phl_status _phl_poll_hw_tx_done(void) { PHL_TRACE(COMP_PHL_DBG, _PHL_ERR_, "[DATA_CTRL] Polling hw tx done is not supported now\n"); return RTW_PHL_STATUS_FAILURE; } enum rtw_phl_status _phl_hw_tx_resume(void) { PHL_TRACE(COMP_PHL_DBG, _PHL_ERR_, "[DATA_CTRL] Resume hw tx not is supported now\n"); return RTW_PHL_STATUS_FAILURE; } enum rtw_phl_status _phl_sw_tx_resume(struct phl_info_t *phl_info, struct phl_data_ctl_t *ctl) { enum rtw_phl_status sts = RTW_PHL_STATUS_FAILURE; struct phl_hci_trx_ops *ops = phl_info->hci_trx_ops; enum data_ctrl_mdl ctrl_mdl = _phl_get_ctrl_mdl(ctl->id); if (PHL_TX_STATUS_IDLE == _os_atomic_read(phl_to_drvpriv(phl_info), &phl_info->phl_sw_tx_sts)) { ops->trx_resume(phl_info, PHL_CTRL_TX); return RTW_PHL_STATUS_SUCCESS; } if (!TEST_STATUS_FLAG(phl_info->pause_tx_id, ctrl_mdl)) { PHL_WARN("[DATA_CTRL] module %d do not pause tx previously (pause_tx_id 0x%x)\n", ctl->id, phl_info->pause_tx_id); return sts; } CLEAR_STATUS_FLAG(phl_info->pause_tx_id, ctrl_mdl); if (DATA_CTRL_MDL_NONE != phl_info->pause_tx_id) { PHL_WARN("[DATA_CTRL] sw tx is still paused by tx pause id = 0x%x\n", phl_info->pause_tx_id); sts = RTW_PHL_STATUS_SUCCESS; } else { ops->trx_resume(phl_info, PHL_CTRL_TX); sts = rtw_phl_tx_req_notify(phl_info); } return sts; } void _phl_sw_tx_rst(struct phl_info_t *phl_info) { struct phl_hci_trx_ops *ops = phl_info->hci_trx_ops; ops->trx_reset(phl_info, PHL_CTRL_TX); } enum rtw_phl_status _phl_sw_tx_pause(struct phl_info_t *phl_info, struct phl_data_ctl_t *ctl, bool rst_sw) { enum rtw_phl_status sts = RTW_PHL_STATUS_FAILURE; struct phl_hci_trx_ops *ops = phl_info->hci_trx_ops; void *drv = phl_to_drvpriv(phl_info); u32 i = 0; enum data_ctrl_mdl ctrl_mdl = _phl_get_ctrl_mdl(ctl->id); #ifdef DBG_DBCC_MONITOR_TIME u32 start_t = 0; PHL_FUN_MON_START(&start_t); #endif /* DBG_DBCC_MONITOR_TIME */ if (PHL_TX_STATUS_SW_PAUSE == _os_atomic_read(drv, &phl_info->phl_sw_tx_sts)) { PHL_TRACE(COMP_PHL_DBG, _PHL_INFO_, "[DATA_CTRL] SW tx has been paused by module(0x%x)\n", phl_info->pause_tx_id); SET_STATUS_FLAG(phl_info->pause_tx_id, ctrl_mdl); PHL_TRACE(COMP_PHL_DBG, _PHL_INFO_, "[DATA_CTRL] Update pause sw tx id(0x%x) by module(%d)\n", phl_info->pause_tx_id, ctl->id); sts = RTW_PHL_STATUS_SUCCESS; goto exit; } if (PHL_TX_STATUS_STOP_INPROGRESS == _os_atomic_read(drv, &phl_info->phl_sw_tx_sts)) { PHL_TRACE(COMP_PHL_DBG, _PHL_INFO_, "[DATA_CTRL] SW tx has been requested to pause by module(0x%x)\n", phl_info->pause_tx_id); SET_STATUS_FLAG(phl_info->pause_tx_id, ctrl_mdl); PHL_TRACE(COMP_PHL_DBG, _PHL_INFO_, "[DATA_CTRL] Update pause sw tx id(0x%x) by module(%d)\n", phl_info->pause_tx_id, ctl->id); sts = RTW_PHL_STATUS_SUCCESS; goto exit; } /* requset sw tx to stop */ ops->req_tx_stop(phl_info); /* * notify sw tx one last time * and poll if it receviced the stop request and paused itself */ if (RTW_PHL_STATUS_SUCCESS == rtw_phl_tx_req_notify(phl_info)) { u32 start = _os_get_cur_time_ms(); u32 cnt = (POLL_SW_TX_PAUSE_MAX_MS * 1000) / 50; for (i = 0; (i < cnt) && (phl_get_passing_time_ms(start) < POLL_SW_TX_PAUSE_MAX_MS); i++) { if (true == ops->is_tx_pause(phl_info)) { SET_STATUS_FLAG(phl_info->pause_tx_id, ctrl_mdl); sts = RTW_PHL_STATUS_SUCCESS; break; } _os_sleep_us(drv, 50); } if (RTW_PHL_STATUS_SUCCESS != sts) { SET_STATUS_FLAG(phl_info->pause_tx_id, ctrl_mdl); sts = RTW_PHL_STATUS_CMD_TIMEOUT; PHL_TRACE(COMP_PHL_DBG, _PHL_ERR_, "[DATA_CTRL] Module(%d) polling sw tx pause timeout (%d ms)!\n", ctl->id, POLL_SW_TX_PAUSE_MAX_MS); } else { if (true == rst_sw) { PHL_TRACE(COMP_PHL_DBG, _PHL_WARNING_, "[DATA_CTRL] Pause Tx with reset is not supported now! requested by module(%d)\n", ctl->id); } } } else { PHL_TRACE(COMP_PHL_DBG, _PHL_WARNING_, "[DATA_CTRL] Schedule sw tx process fail!\n"); } exit: #ifdef DBG_DBCC_MONITOR_TIME PHL_FUNC_MON_END(phl_info->phl_com, &start_t, TIME_PHL_MAX); #endif /* DBG_DBCC_MONITOR_TIME */ return sts; } enum rtw_phl_status _phl_poll_hw_rx_done(void) { PHL_TRACE(COMP_PHL_DBG, _PHL_ERR_, "[DATA_CTRL] Polling hw rx done is not supported now\n"); return RTW_PHL_STATUS_FAILURE; } enum rtw_phl_status _phl_hw_rx_resume(void) { PHL_TRACE(COMP_PHL_DBG, _PHL_ERR_, "[DATA_CTRL] Resume hw rx not is supported now\n"); return RTW_PHL_STATUS_FAILURE; } enum rtw_phl_status _phl_sw_rx_resume(struct phl_info_t *phl_info, struct phl_data_ctl_t *ctl) { enum rtw_phl_status sts = RTW_PHL_STATUS_FAILURE; struct phl_hci_trx_ops *ops = phl_info->hci_trx_ops; enum data_ctrl_mdl ctrl_mdl = _phl_get_ctrl_mdl(ctl->id); if (PHL_RX_STATUS_IDLE == _os_atomic_read(phl_to_drvpriv(phl_info), &phl_info->phl_sw_rx_sts)) { ops->trx_resume(phl_info, PHL_CTRL_RX); return RTW_PHL_STATUS_SUCCESS; } if (!TEST_STATUS_FLAG(phl_info->pause_rx_id, ctrl_mdl)) { PHL_WARN("[DATA_CTRL] module %d do not pause rx previously (pause_rx_id 0x%x)\n", ctl->id, phl_info->pause_rx_id); return sts; } CLEAR_STATUS_FLAG(phl_info->pause_rx_id, ctrl_mdl); if (DATA_CTRL_MDL_NONE != phl_info->pause_rx_id) { PHL_WARN("[DATA_CTRL] sw rx is still paused by rx pause id = 0x%x\n", phl_info->pause_rx_id); sts = RTW_PHL_STATUS_SUCCESS; } else { ops->trx_resume(phl_info, PHL_CTRL_RX); sts = rtw_phl_start_rx_process(phl_info); } return sts; } void _phl_sw_rx_rst(struct phl_info_t *phl_info) { struct phl_hci_trx_ops *ops = phl_info->hci_trx_ops; ops->trx_reset(phl_info, PHL_CTRL_RX); } enum rtw_phl_status _phl_sw_rx_pause(struct phl_info_t *phl_info, struct phl_data_ctl_t *ctl, bool rst_sw) { enum rtw_phl_status sts = RTW_PHL_STATUS_FAILURE; struct phl_hci_trx_ops *ops = phl_info->hci_trx_ops; void *drv = phl_to_drvpriv(phl_info); u32 i = 0; enum data_ctrl_mdl ctrl_mdl = _phl_get_ctrl_mdl(ctl->id); #ifdef DBG_DBCC_MONITOR_TIME u32 start_t = 0; PHL_FUN_MON_START(&start_t); #endif /* DBG_DBCC_MONITOR_TIME */ if (PHL_RX_STATUS_SW_PAUSE == _os_atomic_read(drv, &phl_info->phl_sw_rx_sts)) { PHL_TRACE(COMP_PHL_DBG, _PHL_INFO_, "[DATA_CTRL] SW rx has been paused by module(0x%x)\n", phl_info->pause_rx_id); SET_STATUS_FLAG(phl_info->pause_rx_id, ctrl_mdl); PHL_TRACE(COMP_PHL_DBG, _PHL_INFO_, "[DATA_CTRL] Update pause sw rx id(0x%x) by module(%d)\n", phl_info->pause_rx_id, ctl->id); sts = RTW_PHL_STATUS_SUCCESS; goto exit; } if (PHL_RX_STATUS_STOP_INPROGRESS == _os_atomic_read(drv, &phl_info->phl_sw_rx_sts)) { PHL_TRACE(COMP_PHL_DBG, _PHL_INFO_, "[DATA_CTRL] SW rx has been requested to pause by module(0x%x)\n", phl_info->pause_rx_id); SET_STATUS_FLAG(phl_info->pause_rx_id, ctrl_mdl); PHL_TRACE(COMP_PHL_DBG, _PHL_INFO_, "[DATA_CTRL] Update pause sw rx id(0x%x) by module(%d)\n", phl_info->pause_rx_id, ctl->id); sts = RTW_PHL_STATUS_SUCCESS; goto exit; } /* requset sw rx to stop */ ops->req_rx_stop(phl_info); /* * notify sw rx one last time * and poll if it receviced the stop request and paused itself */ if (RTW_PHL_STATUS_SUCCESS == rtw_phl_start_rx_process(phl_info)) { u32 start = _os_get_cur_time_ms(); u32 cnt = (POLL_SW_RX_PAUSE_MAX_MS * 1000) / 50; for (i = 0; (i < cnt) && (phl_get_passing_time_ms(start) < POLL_SW_TX_PAUSE_MAX_MS); i++) { if (false == ops->is_rx_pause(phl_info)) { _os_sleep_us(drv, 50); continue; } phl_handle_queued_rx(phl_info); if (false == rtw_phl_is_phl_rx_idle(phl_info)) { PHL_INFO("[DATA_CTRL] Still have running rx pkt\n"); phl_dump_all_sta_rx_info(phl_info); PHL_INFO("[DATA_CTRL] phl_rx_ring stored rx number = %d\n", rtw_phl_query_new_rx_num(phl_info)); } SET_STATUS_FLAG(phl_info->pause_rx_id, ctrl_mdl); sts = RTW_PHL_STATUS_SUCCESS; break; } if (RTW_PHL_STATUS_SUCCESS != sts) { SET_STATUS_FLAG(phl_info->pause_rx_id, ctrl_mdl); sts = RTW_PHL_STATUS_CMD_TIMEOUT; PHL_TRACE(COMP_PHL_DBG, _PHL_ERR_, "[DATA_CTRL] Module(%d) polling sw rx pause timeout (%d ms)!\n", ctl->id, POLL_SW_RX_PAUSE_MAX_MS); } else { if (true == rst_sw) { PHL_TRACE(COMP_PHL_DBG, _PHL_WARNING_, "[DATA_CTRL] Pause Rx with reset is not supported now! requested by module(%d)\n", ctl->id); } } } else { PHL_TRACE(COMP_PHL_DBG, _PHL_WARNING_, "[DATA_CTRL] Schedule sw rx process fail!\n"); } exit: #ifdef DBG_DBCC_MONITOR_TIME PHL_FUNC_MON_END(phl_info->phl_com, &start_t, TIME_PHL_MAX); #endif /* DBG_DBCC_MONITOR_TIME */ return sts; } enum rtw_phl_status _phl_hw_trx_rst_resume(struct phl_info_t *phl_info) { void *drv = phl_to_drvpriv(phl_info); if (false == _os_atomic_read(drv, &phl_info->is_hw_trx_pause)) { PHL_TRACE(COMP_PHL_DBG, _PHL_WARNING_, "[DATA_CTRL] HW T/Rx is not paused\n"); } if (rtw_hal_lv1_rcvy(phl_info->hal, RTW_PHL_SER_LV1_SER_RCVY_STEP_2) != RTW_HAL_STATUS_SUCCESS) { PHL_TRACE(COMP_PHL_DBG, _PHL_ERR_, "[DATA_CTRL] Reset and Resume HW T/Rx fail\n"); return RTW_PHL_STATUS_FAILURE; } else { _os_atomic_set(drv, &phl_info->is_hw_trx_pause, false); return RTW_PHL_STATUS_SUCCESS; } } enum rtw_phl_status _phl_hw_trx_pause(struct phl_info_t *phl_info) { void *drv = phl_to_drvpriv(phl_info); if (true == _os_atomic_read(drv, &phl_info->is_hw_trx_pause)) { PHL_TRACE(COMP_PHL_DBG, _PHL_WARNING_, "[DATA_CTRL] HW T/Rx is already paused\n"); } if (rtw_hal_lv1_rcvy(phl_info->hal, RTW_PHL_SER_LV1_RCVY_STEP_1) != RTW_HAL_STATUS_SUCCESS) { PHL_TRACE(COMP_PHL_DBG, _PHL_ERR_, "[DATA_CTRL] Pause HW T/Rx fail\n"); return RTW_PHL_STATUS_FAILURE; } else { _os_atomic_set(drv, &phl_info->is_hw_trx_pause, true); return RTW_PHL_STATUS_SUCCESS; } } enum rtw_phl_status _phl_trx_sw_pause(struct phl_info_t *phl_info, struct phl_data_ctl_t *ctl) { enum rtw_phl_status sts = RTW_PHL_STATUS_FAILURE; do { sts = _phl_sw_tx_pause(phl_info, ctl, false); if (RTW_PHL_STATUS_SUCCESS != sts) { PHL_TRACE(COMP_PHL_DBG, _PHL_WARNING_, "[DATA_CTRL] Pause SW Tx fail in PHL_DATA_CTL_TRX_SW_PAUSE!\n"); break; } sts = _phl_sw_rx_pause(phl_info, ctl, false); if (RTW_PHL_STATUS_SUCCESS != sts) { PHL_TRACE(COMP_PHL_DBG, _PHL_WARNING_, "[DATA_CTRL] Pause SW Rx fail in PHL_DATA_CTL_TRX_SW_PAUSE!\n"); break; } } while (false); return sts; } enum rtw_phl_status _phl_trx_sw_resume(struct phl_info_t *phl_info, struct phl_data_ctl_t *ctl) { enum rtw_phl_status sts = RTW_PHL_STATUS_FAILURE; do { sts = _phl_sw_tx_resume(phl_info, ctl); if (RTW_PHL_STATUS_SUCCESS != sts) { PHL_TRACE(COMP_PHL_DBG, _PHL_WARNING_, "[DATA_CTRL] Resume SW Tx fail in PHL_DATA_CTL_TRX_SW_RESUME!\n"); break; } sts = _phl_sw_rx_resume(phl_info, ctl); if (RTW_PHL_STATUS_SUCCESS != sts) { PHL_TRACE(COMP_PHL_DBG, _PHL_WARNING_, "[DATA_CTRL] Resume SW Rx fail in PHL_DATA_CTL_TRX_SW_RESUME!\n"); break; } } while (false); return sts; } enum rtw_phl_status _phl_trx_pause_w_rst(struct phl_info_t *phl_info, struct phl_data_ctl_t *ctl, struct phl_msg *msg) { enum rtw_phl_status sts = RTW_PHL_STATUS_FAILURE; enum data_ctrl_err_code *err_sts = NULL; if (msg->outbuf && msg->outlen == sizeof(*err_sts)) err_sts = (enum data_ctrl_err_code *)msg->outbuf; do { sts = _phl_sw_tx_pause(phl_info, ctl, false); if (RTW_PHL_STATUS_SUCCESS != sts) { if (err_sts) { if (RTW_PHL_STATUS_CMD_TIMEOUT == sts) *err_sts = CTRL_ERR_SW_TX_PAUSE_POLLTO; else *err_sts = CTRL_ERR_SW_TX_PAUSE_FAIL; } PHL_TRACE(COMP_PHL_DBG, _PHL_WARNING_, "[DATA_CTRL] Pause SW Tx fail in PHL_DATA_CTL_TRX_PAUSE_W_RST!\n"); break; } sts = _phl_hw_trx_pause(phl_info); if (RTW_PHL_STATUS_SUCCESS != sts) { if (err_sts) *err_sts = CTRL_ERR_HW_TRX_PAUSE_FAIL; PHL_TRACE(COMP_PHL_DBG, _PHL_WARNING_, "[DATA_CTRL] Pause HW T/Rx fail in PHL_DATA_CTL_TRX_PAUSE_W_RST!\n"); break; } sts = _phl_sw_rx_pause(phl_info, ctl, false); if (RTW_PHL_STATUS_SUCCESS != sts) { if (err_sts) { if (RTW_PHL_STATUS_CMD_TIMEOUT == sts) *err_sts = CTRL_ERR_SW_RX_PAUSE_POLLTO; else *err_sts = CTRL_ERR_SW_RX_PAUSE_FAIL; } PHL_TRACE(COMP_PHL_DBG, _PHL_WARNING_, "[DATA_CTRL] Pause SW Rx fail in PHL_DATA_CTL_TRX_PAUSE_W_RST!\n"); break; } _phl_sw_tx_rst(phl_info); _phl_sw_rx_rst(phl_info); } while (false); return sts; } enum rtw_phl_status _phl_trx_resume_w_rst(struct phl_info_t *phl_info, struct phl_data_ctl_t *ctl, struct phl_msg *msg) { enum rtw_phl_status sts = RTW_PHL_STATUS_FAILURE; enum data_ctrl_err_code *err_sts = NULL; if (msg->outbuf && msg->outlen == sizeof(*err_sts)) err_sts = (enum data_ctrl_err_code *)msg->outbuf; do { sts = _phl_sw_rx_resume(phl_info, ctl); if (RTW_PHL_STATUS_SUCCESS != sts) { if (err_sts) *err_sts = CTRL_ERR_SW_RX_RESUME_FAIL; PHL_TRACE(COMP_PHL_DBG, _PHL_WARNING_, "[DATA_CTRL] Resume SW Rx fail in PHL_DATA_CTL_TRX_RESUME_W_RST!\n"); break; } sts = _phl_hw_trx_rst_resume(phl_info); if (RTW_PHL_STATUS_SUCCESS != sts) { if (err_sts) *err_sts = CTRL_ERR_HW_TRX_RESUME_FAIL; PHL_TRACE(COMP_PHL_DBG, _PHL_WARNING_, "[DATA_CTRL] Resume HW T/Rx fail in PHL_DATA_CTL_TRX_RESUME_W_RST!\n"); break; } sts = _phl_sw_tx_resume(phl_info, ctl); if (RTW_PHL_STATUS_SUCCESS != sts) { if (err_sts) *err_sts = CTRL_ERR_SW_TX_RESUME_FAIL; PHL_TRACE(COMP_PHL_DBG, _PHL_WARNING_, "[DATA_CTRL] Resume SW Tx fail in PHL_DATA_CTL_TRX_RESUME_W_RST!\n"); break; } } while (false); return sts; } enum rtw_phl_status phl_data_ctrler(struct phl_info_t *phl_info, struct phl_data_ctl_t *ctl, struct phl_msg *msg) { enum rtw_phl_status sts = RTW_PHL_STATUS_FAILURE; if (NULL == ctl) { PHL_WARN("phl_tx_ctrler(): input ctl is NULL\n"); return RTW_PHL_STATUS_FAILURE; } switch (ctl->cmd) { case PHL_DATA_CTL_HW_TRX_RST_RESUME: sts = _phl_hw_trx_rst_resume(phl_info); break; case PHL_DATA_CTL_HW_TRX_PAUSE: sts = _phl_hw_trx_pause(phl_info); break; case PHL_DATA_CTL_SW_TX_RESUME: sts = _phl_sw_tx_resume(phl_info, ctl); break; case PHL_DATA_CTL_SW_RX_RESUME: sts = _phl_sw_rx_resume(phl_info, ctl); break; case PHL_DATA_CTL_SW_TX_PAUSE: sts = _phl_sw_tx_pause(phl_info, ctl, false); break; case PHL_DATA_CTL_SW_RX_PAUSE: sts = _phl_sw_rx_pause(phl_info, ctl, false); break; case PHL_DATA_CTL_SW_TX_RESET: _phl_sw_tx_rst(phl_info); sts = RTW_PHL_STATUS_SUCCESS; break; case PHL_DATA_CTL_SW_RX_RESET: _phl_sw_rx_rst(phl_info); sts = RTW_PHL_STATUS_SUCCESS; break; case PHL_DATA_CTL_TRX_SW_PAUSE: sts = _phl_trx_sw_pause(phl_info, ctl); break; case PHL_DATA_CTL_TRX_SW_RESUME: sts = _phl_trx_sw_resume(phl_info, ctl); break; case PHL_DATA_CTL_TRX_PAUSE_W_RST: sts = _phl_trx_pause_w_rst(phl_info, ctl, msg); break; case PHL_DATA_CTL_TRX_RESUME_W_RST: sts = _phl_trx_resume_w_rst(phl_info, ctl, msg); break; default: PHL_TRACE(COMP_PHL_DBG, _PHL_INFO_, "Unknown data control command(%d)!\n", ctl->cmd); break; } return sts; } static enum rtw_phl_status _phl_set_lifetime(void *phl, u8 hw_band, u8 enable, u16 val) { struct phl_info_t *phl_info = (struct phl_info_t *)phl; if (rtw_hal_com_set_tx_lifetime(phl_info->hal, hw_band, enable, val) == RTW_HAL_STATUS_SUCCESS) return RTW_PHL_STATUS_SUCCESS; else return RTW_PHL_STATUS_FAILURE; } #ifdef CONFIG_CMD_DISP enum rtw_phl_status phl_cmd_cfg_lifetime_hdl(struct phl_info_t *phl_info, u8 *param) { struct lifetime_ctx *lt_ctx = (struct lifetime_ctx *)param; return _phl_set_lifetime((void *)phl_info, lt_ctx->hw_band, lt_ctx->en, lt_ctx->val); } #endif enum rtw_phl_status rtw_phl_cmd_cfg_lifetime(void *phl, struct rtw_wifi_role_link_t *rlink, u8 enable, u16 acq_val, enum phl_cmd_type cmd_type, u32 cmd_timeout) { enum rtw_phl_status sts = RTW_PHL_STATUS_FAILURE; struct phl_info_t *phl_info = (struct phl_info_t *)phl; struct lifetime_ctx *lt_ctx = (struct lifetime_ctx *) &phl_info->lt_ctx; lt_ctx->hw_band = rlink->hw_band; lt_ctx->en = enable; lt_ctx->val = acq_val; #ifdef CONFIG_CMD_DISP sts = phl_cmd_enqueue(phl, rlink->hw_band, MSG_EVT_LIFETIME_SETUP, (u8 *)lt_ctx, 0, NULL, cmd_type, cmd_timeout); if (is_cmd_failure(sts)) { /* Send cmd success, but wait cmd fail*/ sts = RTW_PHL_STATUS_FAILURE; } else if (sts != RTW_PHL_STATUS_SUCCESS) { /* Send cmd fail */ sts = RTW_PHL_STATUS_FAILURE; } return sts; #else PHL_ERR("%s : CONFIG_CMD_DISP not set for MSG_EVT_LIFETIME_SETUP\n", __func__); return sts; #endif } static enum rtw_phl_status _phl_set_power_offset(void *phl, u8 hw_band, s8 ofst_mode, s8 ofst_bw) { struct phl_info_t *phl_info = (struct phl_info_t *)phl; if (rtw_hal_com_set_power_offset(phl_info->hal, hw_band, ofst_mode, ofst_bw) == RTW_HAL_STATUS_SUCCESS) return RTW_PHL_STATUS_SUCCESS; else return RTW_PHL_STATUS_FAILURE; } #ifdef CONFIG_CMD_DISP enum rtw_phl_status phl_cmd_cfg_power_offset_hdl(struct phl_info_t *phl_info, u8 *param) { struct power_offset_ctx *pwr_ctx = (struct power_offset_ctx *)param; return _phl_set_power_offset((void *)phl_info, pwr_ctx->hw_band, pwr_ctx->ofst_mode, pwr_ctx->ofst_bw); } #endif enum rtw_phl_status rtw_phl_cmd_cfg_power_offset(void *phl, struct rtw_wifi_role_link_t *rlink, s8 ofst_mode, s8 ofst_bw, enum phl_cmd_type cmd_type, u32 cmd_timeout) { enum rtw_phl_status sts = RTW_PHL_STATUS_FAILURE; struct phl_info_t *phl_info = (struct phl_info_t *)phl; struct power_offset_ctx *pwr_ctx = (struct power_offset_ctx *)&phl_info->pwr_ofst_ctx; pwr_ctx->hw_band = rlink->hw_band; pwr_ctx->ofst_mode = ofst_mode; pwr_ctx->ofst_bw = ofst_bw; #ifdef CONFIG_CMD_DISP sts = phl_cmd_enqueue(phl, rlink->hw_band, MSG_EVT_POWER_OFFSET_SETUP, (u8 *)pwr_ctx, 0, NULL, cmd_type, cmd_timeout); if (is_cmd_failure(sts)) { /* Send cmd success, but wait cmd fail*/ sts = RTW_PHL_STATUS_FAILURE; } else if (sts != RTW_PHL_STATUS_SUCCESS) { /* Send cmd fail */ sts = RTW_PHL_STATUS_FAILURE; } return sts; #else PHL_ERR("%s : CONFIG_CMD_DISP not set for MSG_EVT_POWER_OFFSET_SETUP\n", __func__); return sts; #endif } static enum rtw_phl_status _phl_set_gt3_with_type(void *phl, u8 en, u32 timeout) { struct phl_info_t *phl_info = (struct phl_info_t *)phl; void *hal = phl_info->hal; if(rtw_hal_com_set_gt3(hal, en, timeout) == RTW_HAL_STATUS_SUCCESS) return RTW_PHL_STATUS_SUCCESS; else return RTW_PHL_STATUS_FAILURE; } static enum rtw_phl_status _phl_set_gt3(void *phl, u8 en, u32 timeout) { struct phl_info_t *phl_info = (struct phl_info_t *)phl; struct gtimer_ctx *gt_ctx = (struct gtimer_ctx *) &phl_info->gt3_ctx; gt_ctx->en = en; gt_ctx->timer_type = _GT3_TYPE_SH_TASK; gt_ctx->duration = timeout; return _phl_set_gt3_with_type(phl, en, timeout); } #ifdef CONFIG_CMD_DISP enum rtw_phl_status phl_cmd_cfg_gt3_hdl(struct phl_info_t *phl_info, u8 *param) { struct gtimer_ctx *gt_ctx = (struct gtimer_ctx *)param; PHL_INFO(" %s(), gt_ctx = %p !\n", __func__, gt_ctx); if (0 == gt_ctx->timer_type) return _phl_set_gt3((void *)phl_info, gt_ctx->en, gt_ctx->duration); else return _phl_set_gt3_with_type((void *)phl_info, gt_ctx->en, gt_ctx->duration); } #endif enum rtw_phl_status rtw_phl_cfg_gtimer_register(void *phl, struct rtw_wifi_role_link_t *rlink, u8 type, u8 enable, u32 timeout, enum phl_cmd_type cmd_type, u32 cmd_timeout) { enum rtw_phl_status sts = RTW_PHL_STATUS_FAILURE; struct phl_info_t *phl_info = (struct phl_info_t *)phl; struct gtimer_ctx *gt_ctx = (struct gtimer_ctx *) &phl_info->gt3_ctx; gt_ctx->en = enable; gt_ctx->timer_type = type; gt_ctx->duration = timeout; #ifdef CONFIG_CMD_DISP sts = phl_cmd_enqueue(phl, rlink->hw_band, MSG_EVT_GT3_SETUP, (u8 *)gt_ctx, 0, NULL, cmd_type, cmd_timeout); if (is_cmd_failure(sts)) { /* Send cmd success, but wait cmd fail*/ sts = RTW_PHL_STATUS_FAILURE; } else if (sts != RTW_PHL_STATUS_SUCCESS) { /* Send cmd fail */ sts = RTW_PHL_STATUS_FAILURE; } return sts; #else PHL_ERR("%s : CONFIG_CMD_DISP not set for MSG_EVT_GT3_SETUP\n", __func__); return sts; #endif } #ifdef CONFIG_CMD_DISP enum rtw_phl_status phl_cmd_cfg_hw_seq_hdl(struct phl_info_t *phl_info, u8 *param) { struct rtw_phl_stainfo_t *sta = (struct rtw_phl_stainfo_t *)param; PHL_INFO(" %s(), sta = %p !\n", __func__, sta); return rtw_hal_set_dctrl_tbl_seq((void *)phl_info->hal, sta, sta->hw_seq); } #endif enum rtw_phl_status rtw_phl_cmd_cfg_hw_seq(void *phl, struct rtw_wifi_role_link_t *rlink, struct rtw_phl_stainfo_t *sta, u32 seq, u16 rts_rate, enum phl_cmd_type cmd_type, u32 cmd_timeout) { enum rtw_phl_status sts = RTW_PHL_STATUS_FAILURE; if (NULL == sta) return RTW_PHL_STATUS_FAILURE; sta->hw_seq = seq; sta->set_rts_init_rate = rts_rate; #ifdef CONFIG_CMD_DISP sts = phl_cmd_enqueue(phl, rlink->hw_band, MSG_EVT_HW_SEQ_SETUP, (u8 *)sta, 0, NULL, cmd_type, cmd_timeout); if (is_cmd_failure(sts)) { /* Send cmd success, but wait cmd fail*/ sts = RTW_PHL_STATUS_FAILURE; } else if (sts != RTW_PHL_STATUS_SUCCESS) { /* Send cmd fail */ sts = RTW_PHL_STATUS_FAILURE; } return sts; #else PHL_ERR("%s : CMD_DISP not set for MSG_EVT_HW_SEQ_SETUP\n", __func__); return sts; #endif } #ifdef CONFIG_PCI_HCI void rtw_phl_get_hw_cnt_tx_fail(void *phl, u32 *tx_fail, u32 *tx_fail_mgmt) { struct phl_info_t *phl_info = (struct phl_info_t *)phl; struct rtw_hal_com_t *hal_com = rtw_hal_get_halcom(phl_info->hal); struct rtw_wp_rpt_stats *rpt_sts = (struct rtw_wp_rpt_stats *)hal_com->trx_stat.wp_rpt_stats; u8 txch_num = rtw_hal_query_txch_num(phl_info->hal); u8 mgmt_ch = 0; u8 i; *tx_fail = 0; *tx_fail_mgmt = 0; if (rpt_sts) { for (i = 0; i < txch_num; i++) *tx_fail += rpt_sts[i].tx_fail_cnt; /* sum up mgmt queue counters of all HW bands */ for (i = 0; i < HW_BAND_MAX; i++) { mgmt_ch = rtw_hal_tx_chnl_mapping(phl_info->hal, 0, RTW_PHL_RING_CAT_MGNT, i); *tx_fail_mgmt += rpt_sts[mgmt_ch].tx_fail_cnt; } } } void rtw_phl_get_hw_cnt_tx_ok(void *phl, u32 *tx_ok, u32 *tx_ok_mgmt) { struct phl_info_t *phl_info = (struct phl_info_t *)phl; struct rtw_hal_com_t *hal_com = rtw_hal_get_halcom(phl_info->hal); struct rtw_wp_rpt_stats *rpt_sts = (struct rtw_wp_rpt_stats *)hal_com->trx_stat.wp_rpt_stats; u8 txch_num = rtw_hal_query_txch_num(phl_info->hal); u8 mgmt_ch = 0; u8 i; *tx_ok = 0; *tx_ok_mgmt = 0; if (rpt_sts) { for(i = 0; i < txch_num; i++) { *tx_ok += rpt_sts[i].tx_ok_cnt; *tx_ok += rpt_sts[i].delay_tx_ok_cnt; } /* sum up mgmt queue counters of all HW bands */ for (i = 0; i < HW_BAND_MAX; i++) { mgmt_ch = rtw_hal_tx_chnl_mapping(phl_info->hal, 0, RTW_PHL_RING_CAT_MGNT, i); *tx_ok_mgmt += rpt_sts[mgmt_ch].tx_ok_cnt; *tx_ok_mgmt += rpt_sts[mgmt_ch].delay_tx_ok_cnt; } } } #endif #ifdef CONFIG_CMD_DISP static void _phl_tx_packet_notify_done(void *drv_priv, u8 *cmd, u32 cmd_len, enum rtw_phl_status status) { if (cmd && cmd_len){ _os_kmem_free(drv_priv, cmd, cmd_len); cmd = NULL; PHL_INFO("%s.....\n", __func__); } } void rtw_phl_tx_packet_event_notify(void *phl, struct rtw_wifi_role_link_t *rlink, enum phl_pkt_evt_type pkt_evt) { struct phl_info_t *phl_info = (struct phl_info_t *)phl; enum phl_pkt_evt_type *pkt_evt_type = NULL; enum rtw_phl_status phl_status = RTW_PHL_STATUS_FAILURE; pkt_evt_type = (enum phl_pkt_evt_type *)_os_kmem_alloc( phl_to_drvpriv(phl_info), sizeof(enum phl_pkt_evt_type)); if (pkt_evt_type == NULL) { PHL_ERR("%s: alloc packet cmd fail.\n", __func__); return; } *pkt_evt_type = pkt_evt; phl_status = phl_cmd_enqueue(phl_info, rlink->hw_band, MSG_EVT_PKT_EVT_NTFY, (u8 *)pkt_evt_type, sizeof(enum phl_pkt_evt_type), _phl_tx_packet_notify_done, PHL_CMD_NO_WAIT, 0); if (is_cmd_failure(phl_status)) { /* Send cmd success, but wait cmd fail*/ PHL_ERR("%s event: %d status: %d\n", __func__, pkt_evt, phl_status); } else if (phl_status != RTW_PHL_STATUS_SUCCESS) { /* Send cmd fail */ PHL_ERR("%s event: %d status: %d\n", __func__, pkt_evt, phl_status); _os_kmem_free(phl_to_drvpriv(phl_info), pkt_evt_type, sizeof(enum phl_pkt_evt_type)); } } static void _phl_packet_notify_done(void *drv_priv, u8 *cmd, u32 cmd_len, enum rtw_phl_status status) { if (cmd && cmd_len){ _os_kmem_free(drv_priv, cmd, cmd_len); cmd = NULL; PHL_INFO("%s.....\n", __func__); } } void rtw_phl_packet_event_notify(void *phl, struct rtw_wifi_role_link_t *rlink, enum phl_pkt_evt_type pkt_evt) { struct phl_info_t *phl_info = (struct phl_info_t *)phl; enum phl_pkt_evt_type *pkt_evt_type = NULL; enum rtw_phl_status phl_status = RTW_PHL_STATUS_FAILURE; pkt_evt_type = (enum phl_pkt_evt_type *)_os_kmem_alloc( phl_to_drvpriv(phl_info), sizeof(enum phl_pkt_evt_type)); if (pkt_evt_type == NULL) { PHL_ERR("%s: alloc packet cmd fail.\n", __func__); return; } *pkt_evt_type = pkt_evt; phl_status = phl_cmd_enqueue(phl_info, rlink->hw_band, MSG_EVT_PKT_EVT_NTFY, (u8 *)pkt_evt_type, sizeof(enum phl_pkt_evt_type), _phl_packet_notify_done, PHL_CMD_NO_WAIT, 0); if (is_cmd_failure(phl_status)) { /* Send cmd success, but wait cmd fail*/ PHL_ERR("%s event: %d status: %d\n", __func__, pkt_evt, phl_status); } else if (phl_status != RTW_PHL_STATUS_SUCCESS) { /* Send cmd fail */ PHL_ERR("%s event: %d status: %d\n", __func__, pkt_evt, phl_status); _os_kmem_free(phl_to_drvpriv(phl_info), pkt_evt_type, sizeof(enum phl_pkt_evt_type)); } } #define TX_DBG_STATUS_DUMP_INTERVAL 30000 /* ms */ void phl_tx_dbg_status_dump(struct phl_info_t *phl_info, enum phl_band_idx hwband) { enum rtw_phl_status phl_status = RTW_PHL_STATUS_FAILURE; static u32 last_dump_t = 0; if (phl_get_passing_time_ms(last_dump_t) >= TX_DBG_STATUS_DUMP_INTERVAL) { phl_status = phl_cmd_enqueue(phl_info, hwband, MSG_EVT_DBG_TX_DUMP, NULL, 0, NULL, PHL_CMD_NO_WAIT, 0); if (phl_status != RTW_PHL_STATUS_SUCCESS) { PHL_TRACE(COMP_PHL_DBG, _PHL_ERR_, "%s: cmd enqueue fail!\n", __func__); } last_dump_t = _os_get_cur_time_ms(); } } static enum rtw_phl_status _phl_cfg_hw_cts2self(struct phl_info_t *phl_info, struct rtw_phl_hw_cts2self_cfg *cts_cfg) { enum rtw_hal_status hsts = RTW_HAL_STATUS_FAILURE; hsts = rtw_hal_cfg_hw_cts2self(phl_info->hal, cts_cfg->band_sel, cts_cfg->enable, cts_cfg->non_sec_threshold, cts_cfg->sec_threshold); if (RTW_HAL_STATUS_SUCCESS != hsts) goto fail; return RTW_PHL_STATUS_SUCCESS; fail: return RTW_PHL_STATUS_FAILURE; } enum rtw_phl_status phl_cmd_cfg_hw_cts2self_hdl(struct phl_info_t *phl_info, u8 *param) { struct rtw_phl_hw_cts2self_cfg *cts_cfg = (struct rtw_phl_hw_cts2self_cfg *)param; PHL_INFO(" %s(), cts_cfg = %p !\n", __func__, cts_cfg); return _phl_cfg_hw_cts2self(phl_info, cts_cfg); } static void _phl_hw_cts2self_done(void *drv_priv, u8 *cmd, u32 cmd_len, enum rtw_phl_status status) { if (cmd) { _os_mem_free(drv_priv, cmd, cmd_len); cmd = NULL; } } enum rtw_phl_status rtw_phl_hw_cts2self_cfg(void *phl, u8 enable, u8 band_sel, u8 non_sec_thr, u8 sec_thr) { #ifdef CONFIG_CMD_DISP struct phl_info_t *phl_info = (struct phl_info_t *)phl; void *drv_priv = phl_to_drvpriv(phl_info); struct rtw_phl_hw_cts2self_cfg *cts_cfg = NULL; enum rtw_phl_status psts = RTW_PHL_STATUS_FAILURE; u32 cfg_len = sizeof(struct rtw_phl_hw_cts2self_cfg); cts_cfg = _os_mem_alloc(drv_priv, cfg_len); if (cts_cfg == NULL) { PHL_ERR("%s: alloc cts_cfg failed!\n", __func__); goto _exit; } cts_cfg->enable = enable; cts_cfg->band_sel = band_sel; cts_cfg->non_sec_threshold = non_sec_thr; cts_cfg->sec_threshold = sec_thr; psts = phl_cmd_enqueue(phl_info, HW_BAND_0, MSG_EVT_HW_CTS2SELF, (u8 *)cts_cfg, cfg_len, _phl_hw_cts2self_done, PHL_CMD_NO_WAIT, 0); if (is_cmd_failure(psts)) { /* Send cmd success, but wait cmd fail*/ psts = RTW_PHL_STATUS_FAILURE; } else if (psts != RTW_PHL_STATUS_SUCCESS) { /* Send cmd fail */ psts = RTW_PHL_STATUS_FAILURE; _os_mem_free(drv_priv, cts_cfg, cfg_len); } _exit: return psts; #else PHL_ERR("phl_fsm not support %s\n", __func__); return RTW_PHL_STATUS_FAILURE; #endif /*CONFIG_CMD_DISP*/ } #endif /* #ifdef CONFIG_CMD_DISP */ bool rtw_phl_check_sta_has_busy_wp(struct rtw_phl_stainfo_t *sta) { return rtw_hal_check_sta_has_busy_wp(sta); }