1777 lines
50 KiB
C
Executable File
1777 lines
50 KiB
C
Executable File
/******************************************************************************
|
|
*
|
|
* Copyright(c) 2019 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_RX_C_
|
|
#include "phl_headers.h"
|
|
|
|
|
|
struct rtw_phl_rx_pkt *rtw_phl_query_phl_rx(void *phl)
|
|
{
|
|
struct phl_info_t *phl_info = (struct phl_info_t *)phl;
|
|
void *drv_priv = phl_to_drvpriv(phl_info);
|
|
struct phl_rx_pkt_pool *rx_pkt_pool = NULL;
|
|
struct rtw_phl_rx_pkt *phl_rx = NULL;
|
|
|
|
rx_pkt_pool = (struct phl_rx_pkt_pool *)phl_info->rx_pkt_pool;
|
|
|
|
_os_spinlock(drv_priv, &rx_pkt_pool->idle_lock, _bh, NULL);
|
|
|
|
if (false == list_empty(&rx_pkt_pool->idle)) {
|
|
phl_rx = list_first_entry(&rx_pkt_pool->idle,
|
|
struct rtw_phl_rx_pkt, list);
|
|
list_del(&phl_rx->list);
|
|
rx_pkt_pool->idle_cnt--;
|
|
}
|
|
|
|
_os_spinunlock(drv_priv, &rx_pkt_pool->idle_lock, _bh, NULL);
|
|
|
|
return phl_rx;
|
|
}
|
|
|
|
u8 rtw_phl_is_phl_rx_idle(struct phl_info_t *phl_info)
|
|
{
|
|
struct phl_rx_pkt_pool *rx_pkt_pool = NULL;
|
|
u8 res = false;
|
|
|
|
rx_pkt_pool = (struct phl_rx_pkt_pool *)phl_info->rx_pkt_pool;
|
|
|
|
_os_spinlock(phl_to_drvpriv(phl_info), &rx_pkt_pool->idle_lock, _bh, NULL);
|
|
|
|
if (MAX_PHL_RING_RX_PKT_NUM == rx_pkt_pool->idle_cnt)
|
|
res = true;
|
|
else
|
|
res = false;
|
|
|
|
_os_spinunlock(phl_to_drvpriv(phl_info), &rx_pkt_pool->idle_lock, _bh, NULL);
|
|
|
|
return res;
|
|
}
|
|
|
|
void phl_dump_rx_stats(struct rtw_stats *stats)
|
|
{
|
|
PHL_TRACE(COMP_PHL_XMIT, _PHL_DEBUG_,
|
|
"Dump Rx statistics\n"
|
|
"rx_byte_uni = %lld\n"
|
|
"rx_byte_total = %lld\n"
|
|
"rx_tp_kbits = %d\n"
|
|
"last_rx_time_ms = %d\n",
|
|
stats->rx_byte_uni,
|
|
stats->rx_byte_total,
|
|
stats->rx_tp_kbits,
|
|
stats->last_rx_time_ms);
|
|
}
|
|
|
|
void phl_reset_rx_stats(struct rtw_stats *stats)
|
|
{
|
|
stats->rx_byte_uni = 0;
|
|
stats->rx_byte_total = 0;
|
|
stats->rx_tp_kbits = 0;
|
|
stats->last_rx_time_ms = 0;
|
|
stats->rxtp.last_calc_time_ms = 0;
|
|
stats->rxtp.last_calc_time_ms = 0;
|
|
stats->rx_traffic.lvl = RTW_TFC_IDLE;
|
|
stats->rx_traffic.sts = 0;
|
|
stats->rx_tf_cnt = 0;
|
|
stats->pre_rx_tf_cnt = 0;
|
|
}
|
|
|
|
void
|
|
phl_rx_traffic_upd(struct rtw_stats *sts)
|
|
{
|
|
u32 tp_k = 0, tp_m = 0;
|
|
enum rtw_tfc_lvl rx_tfc_lvl = RTW_TFC_IDLE;
|
|
tp_k = sts->rx_tp_kbits;
|
|
tp_m = sts->rx_tp_kbits >> 10;
|
|
|
|
if (tp_m >= RX_HIGH_TP_THRES_MBPS)
|
|
rx_tfc_lvl = RTW_TFC_HIGH;
|
|
else if (tp_m >= RX_MID_TP_THRES_MBPS)
|
|
rx_tfc_lvl = RTW_TFC_MID;
|
|
else if (tp_m >= RX_LOW_TP_THRES_MBPS)
|
|
rx_tfc_lvl = RTW_TFC_LOW;
|
|
else if (tp_k >= RX_ULTRA_LOW_TP_THRES_KBPS)
|
|
rx_tfc_lvl = RTW_TFC_ULTRA_LOW;
|
|
else
|
|
rx_tfc_lvl = RTW_TFC_IDLE;
|
|
|
|
if (sts->rx_traffic.lvl > rx_tfc_lvl) {
|
|
sts->rx_traffic.sts = (TRAFFIC_CHANGED | TRAFFIC_DECREASE);
|
|
sts->rx_traffic.lvl = rx_tfc_lvl;
|
|
} else if (sts->rx_traffic.lvl < rx_tfc_lvl) {
|
|
sts->rx_traffic.sts = (TRAFFIC_CHANGED | TRAFFIC_INCREASE);
|
|
sts->rx_traffic.lvl = rx_tfc_lvl;
|
|
} else if (sts->rx_traffic.sts &
|
|
(TRAFFIC_CHANGED | TRAFFIC_INCREASE | TRAFFIC_DECREASE)) {
|
|
sts->rx_traffic.sts &= ~(TRAFFIC_CHANGED | TRAFFIC_INCREASE |
|
|
TRAFFIC_DECREASE);
|
|
}
|
|
}
|
|
|
|
void phl_update_rx_stats(struct rtw_stats *stats, struct rtw_recv_pkt *rx_pkt)
|
|
{
|
|
u32 diff_t = 0, cur_time = _os_get_cur_time_ms();
|
|
u64 diff_bits = 0;
|
|
|
|
stats->last_rx_time_ms = cur_time;
|
|
stats->rx_byte_total += rx_pkt->mdata.pktlen;
|
|
if (rx_pkt->mdata.bc == 0 && rx_pkt->mdata.mc == 0)
|
|
stats->rx_byte_uni += rx_pkt->mdata.pktlen;
|
|
|
|
if (0 == stats->rxtp.last_calc_time_ms ||
|
|
0 == stats->rxtp.last_calc_bits) {
|
|
stats->rxtp.last_calc_time_ms = stats->last_rx_time_ms;
|
|
stats->rxtp.last_calc_bits = stats->rx_byte_uni * 8;
|
|
} else {
|
|
if (cur_time >= stats->rxtp.last_calc_time_ms) {
|
|
diff_t = cur_time - stats->rxtp.last_calc_time_ms;
|
|
} else {
|
|
diff_t = RTW_U32_MAX - stats->rxtp.last_calc_time_ms +
|
|
cur_time + 1;
|
|
}
|
|
if (diff_t > RXTP_CALC_DIFF_MS && stats->rx_byte_uni != 0) {
|
|
diff_bits = (stats->rx_byte_uni * 8) -
|
|
stats->rxtp.last_calc_bits;
|
|
stats->rx_tp_kbits = (u32)_os_division64(diff_bits,
|
|
diff_t);
|
|
stats->rxtp.last_calc_bits = stats->rx_byte_uni * 8;
|
|
stats->rxtp.last_calc_time_ms = cur_time;
|
|
}
|
|
}
|
|
}
|
|
|
|
void phl_rx_statistics(struct phl_info_t *phl_info, struct rtw_recv_pkt *rx_pkt)
|
|
{
|
|
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 = rx_pkt->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_rx_stats(sta_stats, rx_pkt);
|
|
dev_stat:
|
|
phl_update_rx_stats(phl_stats, rx_pkt);
|
|
}
|
|
|
|
void phl_release_phl_rx(struct phl_info_t *phl_info,
|
|
struct rtw_phl_rx_pkt *phl_rx)
|
|
{
|
|
void *drv_priv = phl_to_drvpriv(phl_info);
|
|
struct phl_rx_pkt_pool *rx_pkt_pool = NULL;
|
|
|
|
rx_pkt_pool = (struct phl_rx_pkt_pool *)phl_info->rx_pkt_pool;
|
|
|
|
_os_spinlock(drv_priv, &rx_pkt_pool->idle_lock, _bh, NULL);
|
|
_os_mem_set(phl_to_drvpriv(phl_info), &phl_rx->r, 0, sizeof(phl_rx->r));
|
|
phl_rx->type = RTW_RX_TYPE_MAX;
|
|
phl_rx->rxbuf_ptr = NULL;
|
|
INIT_LIST_HEAD(&phl_rx->list);
|
|
list_add_tail(&phl_rx->list, &rx_pkt_pool->idle);
|
|
rx_pkt_pool->idle_cnt++;
|
|
_os_spinunlock(drv_priv, &rx_pkt_pool->idle_lock, _bh, NULL);
|
|
}
|
|
|
|
static void phl_free_recv_pkt_pool(struct phl_info_t *phl_info)
|
|
{
|
|
struct phl_rx_pkt_pool *rx_pkt_pool = NULL;
|
|
u32 buf_len = 0;
|
|
FUNCIN();
|
|
|
|
rx_pkt_pool = (struct phl_rx_pkt_pool *)phl_info->rx_pkt_pool;
|
|
if (NULL != rx_pkt_pool) {
|
|
_os_spinlock_free(phl_to_drvpriv(phl_info),
|
|
&rx_pkt_pool->idle_lock);
|
|
_os_spinlock_free(phl_to_drvpriv(phl_info),
|
|
&rx_pkt_pool->busy_lock);
|
|
|
|
buf_len = sizeof(*rx_pkt_pool);
|
|
_os_mem_free(phl_to_drvpriv(phl_info), rx_pkt_pool, buf_len);
|
|
}
|
|
|
|
FUNCOUT();
|
|
}
|
|
|
|
void phl_rx_deinit(struct phl_info_t *phl_info)
|
|
{
|
|
/* TODO: rx reorder deinit */
|
|
|
|
/* TODO: peer info deinit */
|
|
|
|
phl_free_recv_pkt_pool(phl_info);
|
|
}
|
|
|
|
|
|
static enum rtw_phl_status phl_alloc_recv_pkt_pool(struct phl_info_t *phl_info)
|
|
{
|
|
enum rtw_phl_status pstatus = RTW_PHL_STATUS_FAILURE;
|
|
struct phl_rx_pkt_pool *rx_pkt_pool = NULL;
|
|
struct rtw_phl_rx_pkt *phl_rx = NULL;
|
|
u32 buf_len = 0, i = 0;
|
|
FUNCIN_WSTS(pstatus);
|
|
|
|
buf_len = sizeof(*rx_pkt_pool);
|
|
rx_pkt_pool = _os_mem_alloc(phl_to_drvpriv(phl_info), buf_len);
|
|
|
|
if (NULL != rx_pkt_pool) {
|
|
_os_mem_set(phl_to_drvpriv(phl_info), rx_pkt_pool, 0, buf_len);
|
|
INIT_LIST_HEAD(&rx_pkt_pool->idle);
|
|
INIT_LIST_HEAD(&rx_pkt_pool->busy);
|
|
_os_spinlock_init(phl_to_drvpriv(phl_info),
|
|
&rx_pkt_pool->idle_lock);
|
|
_os_spinlock_init(phl_to_drvpriv(phl_info),
|
|
&rx_pkt_pool->busy_lock);
|
|
rx_pkt_pool->idle_cnt = 0;
|
|
|
|
for (i = 0; i < MAX_PHL_RING_RX_PKT_NUM; i++) {
|
|
phl_rx = &rx_pkt_pool->phl_rx[i];
|
|
INIT_LIST_HEAD(&phl_rx->list);
|
|
list_add_tail(&phl_rx->list, &rx_pkt_pool->idle);
|
|
rx_pkt_pool->idle_cnt++;
|
|
}
|
|
|
|
phl_info->rx_pkt_pool = rx_pkt_pool;
|
|
|
|
pstatus = RTW_PHL_STATUS_SUCCESS;
|
|
}
|
|
|
|
if (RTW_PHL_STATUS_SUCCESS != pstatus)
|
|
phl_free_recv_pkt_pool(phl_info);
|
|
FUNCOUT_WSTS(pstatus);
|
|
|
|
return pstatus;
|
|
}
|
|
|
|
enum rtw_phl_status phl_rx_init(struct phl_info_t *phl_info)
|
|
{
|
|
enum rtw_phl_status status;
|
|
|
|
/* Allocate rx packet pool */
|
|
status = phl_alloc_recv_pkt_pool(phl_info);
|
|
if (status != RTW_PHL_STATUS_SUCCESS)
|
|
return status;
|
|
|
|
/* TODO: Peer info init */
|
|
|
|
|
|
/* TODO: Rx reorder init */
|
|
|
|
return RTW_PHL_STATUS_SUCCESS;
|
|
}
|
|
|
|
void phl_recycle_rx_buf(struct phl_info_t *phl_info,
|
|
struct rtw_phl_rx_pkt *phl_rx)
|
|
{
|
|
enum rtw_phl_status pstatus = RTW_PHL_STATUS_FAILURE;
|
|
struct phl_hci_trx_ops *hci_trx_ops = phl_info->hci_trx_ops;
|
|
struct rtw_rx_buf *rx_buf = NULL;
|
|
|
|
do {
|
|
if (NULL == phl_rx) {
|
|
PHL_TRACE(COMP_PHL_RECV, _PHL_WARNING_, "[WARNING]phl_rx is NULL!\n");
|
|
break;
|
|
}
|
|
|
|
rx_buf = (struct rtw_rx_buf *)phl_rx->rxbuf_ptr;
|
|
|
|
PHL_TRACE(COMP_PHL_RECV, _PHL_DEBUG_, "[4] %s:: [%p]\n",
|
|
__FUNCTION__, rx_buf);
|
|
if (phl_rx->rxbuf_ptr) {
|
|
pstatus = hci_trx_ops->recycle_rx_buf(phl_info, rx_buf,
|
|
phl_rx->r.mdata.dma_ch,
|
|
phl_rx->type);
|
|
}
|
|
if (RTW_PHL_STATUS_SUCCESS != pstatus && phl_rx->rxbuf_ptr)
|
|
PHL_TRACE(COMP_PHL_RECV, _PHL_WARNING_, "[WARNING]recycle hci rx buf error!\n");
|
|
|
|
phl_release_phl_rx(phl_info, phl_rx);
|
|
|
|
} while (false);
|
|
|
|
}
|
|
|
|
void _phl_indic_new_rxpkt(struct phl_info_t *phl_info)
|
|
{
|
|
enum rtw_phl_status pstatus = RTW_PHL_STATUS_SUCCESS;
|
|
struct rtw_evt_info_t *evt_info = &phl_info->phl_com->evt_info;
|
|
void *drv_priv = phl_to_drvpriv(phl_info);
|
|
FUNCIN_WSTS(pstatus);
|
|
|
|
do {
|
|
_os_spinlock(drv_priv, &evt_info->evt_lock, _bh, NULL);
|
|
evt_info->evt_bitmap |= RTW_PHL_EVT_RX;
|
|
_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);
|
|
|
|
if (RTW_PHL_STATUS_SUCCESS != pstatus)
|
|
PHL_TRACE(COMP_PHL_RECV, _PHL_WARNING_, "[WARNING] Trigger rx indic event fail!\n");
|
|
|
|
FUNCOUT_WSTS(pstatus);
|
|
|
|
#ifdef PHL_RX_BATCH_IND
|
|
phl_info->rx_new_pending = 0;
|
|
#endif
|
|
}
|
|
|
|
void _phl_record_rx_stats(struct rtw_recv_pkt *recvpkt)
|
|
{
|
|
if(NULL == recvpkt)
|
|
return;
|
|
if (recvpkt->tx_sta)
|
|
recvpkt->tx_sta->stats.rx_rate = recvpkt->mdata.rx_rate;
|
|
}
|
|
|
|
enum rtw_phl_status _phl_add_rx_pkt(struct phl_info_t *phl_info,
|
|
struct rtw_phl_rx_pkt *phl_rx)
|
|
{
|
|
enum rtw_phl_status pstatus = RTW_PHL_STATUS_FAILURE;
|
|
struct rtw_phl_rx_ring *ring = &phl_info->phl_rx_ring;
|
|
struct rtw_recv_pkt *recvpkt = &phl_rx->r;
|
|
u16 ring_res = 0, wptr = 0, rptr = 0;
|
|
void *drv = phl_to_drvpriv(phl_info);
|
|
|
|
FUNCIN_WSTS(pstatus);
|
|
_os_spinlock(drv, &phl_info->rx_ring_lock, _bh, NULL);
|
|
|
|
if (!ring)
|
|
goto out;
|
|
|
|
wptr = (u16)_os_atomic_read(drv, &ring->phl_idx);
|
|
rptr = (u16)_os_atomic_read(drv, &ring->core_idx);
|
|
|
|
ring_res = phl_calc_avail_wptr(rptr, wptr, MAX_PHL_RING_ENTRY_NUM);
|
|
PHL_TRACE(COMP_PHL_RECV, _PHL_DEBUG_,
|
|
"[3] _phl_add_rx_pkt::[Query] phl_idx =%d , core_idx =%d , ring_res =%d\n",
|
|
_os_atomic_read(drv, &ring->phl_idx),
|
|
_os_atomic_read(drv, &ring->core_idx),
|
|
ring_res);
|
|
if (ring_res <= 0) {
|
|
PHL_TRACE(COMP_PHL_RECV, _PHL_INFO_, "no ring resource to add new rx pkt!\n");
|
|
pstatus = RTW_PHL_STATUS_RESOURCE;
|
|
goto out;
|
|
}
|
|
|
|
wptr = wptr + 1;
|
|
if (wptr >= MAX_PHL_RING_ENTRY_NUM)
|
|
wptr = 0;
|
|
|
|
ring->entry[wptr] = recvpkt;
|
|
|
|
if (wptr)
|
|
_os_atomic_inc(drv, &ring->phl_idx);
|
|
else
|
|
_os_atomic_set(drv, &ring->phl_idx, 0);
|
|
|
|
#ifdef PHL_RX_BATCH_IND
|
|
phl_info->rx_new_pending = 1;
|
|
pstatus = RTW_PHL_STATUS_SUCCESS;
|
|
#endif
|
|
|
|
out:
|
|
_os_spinunlock(drv, &phl_info->rx_ring_lock, _bh, NULL);
|
|
|
|
if(pstatus == RTW_PHL_STATUS_SUCCESS)
|
|
_phl_record_rx_stats(recvpkt);
|
|
|
|
FUNCOUT_WSTS(pstatus);
|
|
|
|
return pstatus;
|
|
}
|
|
|
|
void
|
|
phl_sta_ps_enter(struct phl_info_t *phl_info, struct rtw_phl_stainfo_t *sta,
|
|
struct rtw_wifi_role_t *role)
|
|
{
|
|
void *d = phl_to_drvpriv(phl_info);
|
|
enum rtw_hal_status hal_status;
|
|
struct rtw_phl_evt_ops *ops = &phl_info->phl_com->evt_ops;
|
|
|
|
_os_atomic_set(d, &sta->ps_sta, 1);
|
|
|
|
PHL_TRACE(COMP_PHL_PS, _PHL_INFO_,
|
|
"STA %02X:%02X:%02X:%02X:%02X:%02X enters PS mode, AID=%u, macid=%u, sta=0x%p\n",
|
|
sta->mac_addr[0], sta->mac_addr[1], sta->mac_addr[2],
|
|
sta->mac_addr[3], sta->mac_addr[4], sta->mac_addr[5],
|
|
sta->aid, sta->macid, sta);
|
|
|
|
hal_status = rtw_hal_set_macid_pause(phl_info->hal,
|
|
sta->macid, true);
|
|
if (RTW_HAL_STATUS_SUCCESS != hal_status) {
|
|
PHL_WARN("%s(): failed to pause macid tx, macid=%u\n",
|
|
__FUNCTION__, sta->macid);
|
|
}
|
|
|
|
if (ops->ap_ps_sta_ps_change)
|
|
ops->ap_ps_sta_ps_change(d, role->id, sta->mac_addr, true);
|
|
}
|
|
|
|
void
|
|
phl_sta_ps_exit(struct phl_info_t *phl_info, struct rtw_phl_stainfo_t *sta,
|
|
struct rtw_wifi_role_t *role)
|
|
{
|
|
void *d = phl_to_drvpriv(phl_info);
|
|
enum rtw_hal_status hal_status;
|
|
struct rtw_phl_evt_ops *ops = &phl_info->phl_com->evt_ops;
|
|
|
|
PHL_TRACE(COMP_PHL_PS, _PHL_INFO_,
|
|
"STA %02X:%02X:%02X:%02X:%02X:%02X leaves PS mode, AID=%u, macid=%u, sta=0x%p\n",
|
|
sta->mac_addr[0], sta->mac_addr[1], sta->mac_addr[2],
|
|
sta->mac_addr[3], sta->mac_addr[4], sta->mac_addr[5],
|
|
sta->aid, sta->macid, sta);
|
|
|
|
_os_atomic_set(d, &sta->ps_sta, 0);
|
|
|
|
hal_status = rtw_hal_set_macid_pause(phl_info->hal,
|
|
sta->macid, false);
|
|
if (RTW_HAL_STATUS_SUCCESS != hal_status) {
|
|
PHL_WARN("%s(): failed to resume macid tx, macid=%u\n",
|
|
__FUNCTION__, sta->macid);
|
|
}
|
|
|
|
if (ops->ap_ps_sta_ps_change)
|
|
ops->ap_ps_sta_ps_change(d, role->id, sta->mac_addr, false);
|
|
}
|
|
|
|
void
|
|
phl_rx_handle_sta_process(struct phl_info_t *phl_info,
|
|
struct rtw_phl_rx_pkt *rx)
|
|
{
|
|
struct rtw_r_meta_data *m = &rx->r.mdata;
|
|
struct rtw_wifi_role_t *role = NULL;
|
|
struct rtw_phl_stainfo_t *sta = NULL;
|
|
void *d = phl_to_drvpriv(phl_info);
|
|
|
|
if (!phl_info->phl_com->dev_sw_cap.ap_ps)
|
|
return;
|
|
|
|
if (m->addr_cam_vld) {
|
|
sta = rtw_phl_get_stainfo_by_macid(phl_info, m->macid);
|
|
if (sta && sta->wrole)
|
|
role = sta->wrole;
|
|
}
|
|
|
|
if (!sta) {
|
|
role = phl_get_wrole_by_addr(phl_info, m->mac_addr);
|
|
if (role)
|
|
sta = rtw_phl_get_stainfo_by_addr(phl_info,
|
|
role, m->ta);
|
|
}
|
|
|
|
if (!role || !sta)
|
|
return;
|
|
|
|
rx->r.tx_sta = sta;
|
|
rx->r.rx_role = role;
|
|
|
|
PHL_TRACE(COMP_PHL_PS, _PHL_DEBUG_,
|
|
"ap-ps: more_frag=%u, frame_type=%u, role_type=%d, pwr_bit=%u, seq=%u\n",
|
|
m->more_frag, m->frame_type, role->type, m->pwr_bit, m->seq);
|
|
|
|
/*
|
|
* Change STA PS state based on the PM bit in frame control
|
|
*/
|
|
if (!m->more_frag &&
|
|
(m->frame_type == RTW_FRAME_TYPE_DATA ||
|
|
m->frame_type == RTW_FRAME_TYPE_MGNT) &&
|
|
(role->type == PHL_RTYPE_AP ||
|
|
role->type == PHL_RTYPE_P2P_GO)) {
|
|
/* May get a @rx with macid set to our self macid, check if that
|
|
* happens here to avoid pausing self macid. This is put here so
|
|
* we wouldn't do it on our normal rx path, which degrades rx
|
|
* throughput significantly. */
|
|
if (phl_self_stainfo_chk(phl_info, role, sta))
|
|
return;
|
|
|
|
if (_os_atomic_read(d, &sta->ps_sta)) {
|
|
if (!m->pwr_bit)
|
|
phl_sta_ps_exit(phl_info, sta, role);
|
|
} else {
|
|
if (m->pwr_bit)
|
|
phl_sta_ps_enter(phl_info, sta, role);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
phl_handle_rx_frame_list(struct phl_info_t *phl_info,
|
|
_os_list *frames)
|
|
{
|
|
struct rtw_phl_rx_pkt *pos, *n;
|
|
enum rtw_phl_status status = RTW_PHL_STATUS_FAILURE;
|
|
struct phl_hci_trx_ops *hci_trx_ops = phl_info->hci_trx_ops;
|
|
|
|
phl_list_for_loop_safe(pos, n, struct rtw_phl_rx_pkt, frames, list) {
|
|
list_del(&pos->list);
|
|
phl_rx_handle_sta_process(phl_info, pos);
|
|
status = _phl_add_rx_pkt(phl_info, pos);
|
|
if (RTW_PHL_STATUS_RESOURCE == status) {
|
|
hci_trx_ops->recycle_rx_pkt(phl_info, pos);
|
|
}
|
|
}
|
|
#ifndef PHL_RX_BATCH_IND
|
|
_phl_indic_new_rxpkt(phl_info);
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
#define SEQ_MODULO 0x1000
|
|
#define SEQ_MASK 0xfff
|
|
|
|
static inline int seq_less(u16 sq1, u16 sq2)
|
|
{
|
|
return ((sq1 - sq2) & SEQ_MASK) > (SEQ_MODULO >> 1);
|
|
}
|
|
|
|
static inline u16 seq_inc(u16 sq)
|
|
{
|
|
return (sq + 1) & SEQ_MASK;
|
|
}
|
|
|
|
static inline u16 seq_sub(u16 sq1, u16 sq2)
|
|
{
|
|
return (sq1 - sq2) & SEQ_MASK;
|
|
}
|
|
|
|
static inline u16 reorder_index(struct phl_tid_ampdu_rx *r, u16 seq)
|
|
{
|
|
return seq_sub(seq, r->ssn) % r->buf_size;
|
|
}
|
|
|
|
static void phl_release_reorder_frame(struct phl_info_t *phl_info,
|
|
struct phl_tid_ampdu_rx *r,
|
|
int index, _os_list *frames)
|
|
{
|
|
struct rtw_phl_rx_pkt *pkt = r->reorder_buf[index];
|
|
|
|
if (!pkt)
|
|
goto out;
|
|
|
|
/* release the frame from the reorder ring buffer */
|
|
r->stored_mpdu_num--;
|
|
r->reorder_buf[index] = NULL;
|
|
list_add_tail(&pkt->list, frames);
|
|
|
|
out:
|
|
r->head_seq_num = seq_inc(r->head_seq_num);
|
|
}
|
|
|
|
#define HT_RX_REORDER_BUF_TIMEOUT_MS 100
|
|
|
|
/*
|
|
* If the MPDU at head_seq_num is ready,
|
|
* 1. release all subsequent MPDUs with consecutive SN and
|
|
* 2. if there's MPDU that is ready but left in the reordering
|
|
* buffer, find it and set reorder timer according to its reorder
|
|
* time
|
|
*
|
|
* If the MPDU at head_seq_num is not ready and there is no MPDU ready
|
|
* in the buffer at all, return.
|
|
*
|
|
* If the MPDU at head_seq_num is not ready but there is some MPDU in
|
|
* the buffer that is ready, check whether any frames in the reorder
|
|
* buffer have timed out in the following way.
|
|
*
|
|
* Basically, MPDUs that are not ready are purged and MPDUs that are
|
|
* ready are released.
|
|
*
|
|
* The process goes through all the buffer but the one at head_seq_num
|
|
* unless
|
|
* - there's a MPDU that is ready AND
|
|
* - there are one or more buffers that are not ready.
|
|
* In this case, the process is stopped, the head_seq_num becomes the
|
|
* first buffer that is not ready and the reorder_timer is reset based
|
|
* on the reorder_time of that ready MPDU.
|
|
*/
|
|
static void phl_reorder_release(struct phl_info_t *phl_info,
|
|
struct phl_tid_ampdu_rx *r, _os_list *frames)
|
|
{
|
|
/* ref ieee80211_sta_reorder_release() and wil_reorder_release() */
|
|
|
|
int index, i, j;
|
|
u32 cur_time = _os_get_cur_time_ms();
|
|
|
|
/* release the buffer until next missing frame */
|
|
index = reorder_index(r, r->head_seq_num);
|
|
if (!r->reorder_buf[index] && r->stored_mpdu_num) {
|
|
/*
|
|
* No buffers ready to be released, but check whether any
|
|
* frames in the reorder buffer have timed out.
|
|
*/
|
|
int skipped = 1;
|
|
for (j = (index + 1) % r->buf_size; j != index;
|
|
j = (j + 1) % r->buf_size) {
|
|
if (!r->reorder_buf[j]) {
|
|
skipped++;
|
|
continue;
|
|
}
|
|
if (skipped && cur_time < r->reorder_time[j] +
|
|
HT_RX_REORDER_BUF_TIMEOUT_MS)
|
|
goto set_release_timer;
|
|
|
|
/* don't leave incomplete A-MSDUs around */
|
|
for (i = (index + 1) % r->buf_size; i != j;
|
|
i = (i + 1) % r->buf_size)
|
|
phl_recycle_rx_buf(phl_info, r->reorder_buf[i]);
|
|
|
|
PHL_TRACE(COMP_PHL_RECV, _PHL_INFO_, "release an RX reorder frame due to timeout on earlier frames\n");
|
|
|
|
phl_release_reorder_frame(phl_info, r, j, frames);
|
|
|
|
/*
|
|
* Increment the head seq# also for the skipped slots.
|
|
*/
|
|
r->head_seq_num =
|
|
(r->head_seq_num + skipped) & SEQ_MASK;
|
|
skipped = 0;
|
|
}
|
|
} else while (r->reorder_buf[index]) {
|
|
phl_release_reorder_frame(phl_info, r, index, frames);
|
|
index = reorder_index(r, r->head_seq_num);
|
|
}
|
|
|
|
if (r->stored_mpdu_num) {
|
|
j = index = r->head_seq_num % r->buf_size;
|
|
|
|
for (; j != (index - 1) % r->buf_size;
|
|
j = (j + 1) % r->buf_size) {
|
|
if (r->reorder_buf[j])
|
|
break;
|
|
}
|
|
|
|
set_release_timer:
|
|
|
|
if (!r->removed)
|
|
_os_set_timer(r->drv_priv, &r->sta->reorder_timer,
|
|
HT_RX_REORDER_BUF_TIMEOUT_MS);
|
|
} else {
|
|
/* TODO: implementation of cancel timer on Linux is
|
|
del_timer_sync(), it can't be called with same spinlock
|
|
held with the expiration callback, that causes a potential
|
|
deadlock. */
|
|
_os_cancel_timer_async(r->drv_priv, &r->sta->reorder_timer);
|
|
}
|
|
}
|
|
|
|
void phl_sta_rx_reorder_timer_expired(void *t)
|
|
{
|
|
/* ref sta_rx_agg_reorder_timer_expired() */
|
|
|
|
struct rtw_phl_stainfo_t *sta = (struct rtw_phl_stainfo_t *)t;
|
|
struct rtw_phl_com_t *phl_com = sta->wrole->phl_com;
|
|
struct phl_info_t *phl_info = (struct phl_info_t *)phl_com->phl_priv;
|
|
void *drv_priv = phl_to_drvpriv(phl_info);
|
|
u8 i = 0;
|
|
|
|
PHL_INFO("Rx reorder timer expired, sta=0x%p\n", sta);
|
|
|
|
for (i = 0; i < ARRAY_SIZE(sta->tid_rx); i++) {
|
|
_os_list frames;
|
|
|
|
INIT_LIST_HEAD(&frames);
|
|
|
|
_os_spinlock(drv_priv, &sta->tid_rx_lock, _bh, NULL);
|
|
if (sta->tid_rx[i])
|
|
phl_reorder_release(phl_info, sta->tid_rx[i], &frames);
|
|
_os_spinunlock(drv_priv, &sta->tid_rx_lock, _bh, NULL);
|
|
|
|
phl_handle_rx_frame_list(phl_info, &frames);
|
|
#ifdef PHL_RX_BATCH_IND
|
|
_phl_indic_new_rxpkt(phl_info);
|
|
#endif
|
|
}
|
|
|
|
_os_event_set(drv_priv, &sta->comp_sync);
|
|
}
|
|
|
|
static void phl_release_reorder_frames(struct phl_info_t *phl_info,
|
|
struct phl_tid_ampdu_rx *r,
|
|
u16 head_seq_num, _os_list *frames)
|
|
{
|
|
/* ref ieee80211_release_reorder_frames() and
|
|
wil_release_reorder_frames() */
|
|
|
|
int index;
|
|
|
|
/* note: this function is never called with
|
|
* hseq preceding r->head_seq_num, i.e it is always true
|
|
* !seq_less(hseq, r->head_seq_num)
|
|
* and thus on loop exit it should be
|
|
* r->head_seq_num == hseq
|
|
*/
|
|
while (seq_less(r->head_seq_num, head_seq_num) &&
|
|
r->stored_mpdu_num) { /* Note: do we need to check this? */
|
|
index = reorder_index(r, r->head_seq_num);
|
|
phl_release_reorder_frame(phl_info, r, index, frames);
|
|
}
|
|
r->head_seq_num = head_seq_num;
|
|
}
|
|
|
|
void rtw_phl_flush_reorder_buf(void *phl, struct rtw_phl_stainfo_t *sta)
|
|
{
|
|
struct phl_info_t *phl_info = (struct phl_info_t *)phl;
|
|
void *drv_priv = phl_to_drvpriv(phl_info);
|
|
_os_list frames;
|
|
u8 i = 0;
|
|
|
|
PHL_INFO("%s: sta=0x%p\n", __FUNCTION__, sta);
|
|
|
|
INIT_LIST_HEAD(&frames);
|
|
|
|
_os_spinlock(drv_priv, &sta->tid_rx_lock, _bh, NULL);
|
|
for (i = 0; i < ARRAY_SIZE(sta->tid_rx); i++) {
|
|
if (sta->tid_rx[i])
|
|
phl_reorder_release(phl_info, sta->tid_rx[i], &frames);
|
|
}
|
|
_os_spinunlock(drv_priv, &sta->tid_rx_lock, _bh, NULL);
|
|
|
|
phl_handle_rx_frame_list(phl_info, &frames);
|
|
#ifdef PHL_RX_BATCH_IND
|
|
_phl_indic_new_rxpkt(phl_info);
|
|
#endif
|
|
|
|
}
|
|
|
|
static bool phl_manage_sta_reorder_buf(struct phl_info_t *phl_info,
|
|
struct rtw_phl_rx_pkt *pkt,
|
|
struct phl_tid_ampdu_rx *r,
|
|
_os_list *frames)
|
|
{
|
|
/* ref ieee80211_sta_manage_reorder_buf() and wil_rx_reorder() */
|
|
|
|
struct rtw_r_meta_data *meta = &pkt->r.mdata;
|
|
u16 mpdu_seq_num = meta->seq;
|
|
u16 head_seq_num, buf_size;
|
|
int index;
|
|
struct phl_hci_trx_ops *hci_trx_ops = phl_info->hci_trx_ops;
|
|
|
|
buf_size = r->buf_size;
|
|
head_seq_num = r->head_seq_num;
|
|
|
|
/*
|
|
* If the current MPDU's SN is smaller than the SSN, it shouldn't
|
|
* be reordered.
|
|
*/
|
|
if (!r->started) {
|
|
if (seq_less(mpdu_seq_num, head_seq_num))
|
|
return false;
|
|
r->started = true;
|
|
}
|
|
|
|
if (r->sleep) {
|
|
PHL_INFO("tid = %d reorder buffer handling after wake up\n",
|
|
r->tid);
|
|
PHL_INFO("Update head seq(0x%03x) to the first rx seq(0x%03x) after wake up\n",
|
|
r->head_seq_num, mpdu_seq_num);
|
|
r->head_seq_num = mpdu_seq_num;
|
|
head_seq_num = r->head_seq_num;
|
|
r->sleep = false;
|
|
}
|
|
|
|
/* frame with out of date sequence number */
|
|
if (seq_less(mpdu_seq_num, head_seq_num)) {
|
|
PHL_TRACE(COMP_PHL_RECV, _PHL_DEBUG_, "Rx drop: old seq 0x%03x head 0x%03x\n",
|
|
meta->seq, r->head_seq_num);
|
|
hci_trx_ops->recycle_rx_pkt(phl_info, pkt);
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
* If frame the sequence number exceeds our buffering window
|
|
* size release some previous frames to make room for this one.
|
|
*/
|
|
if (!seq_less(mpdu_seq_num, head_seq_num + buf_size)) {
|
|
head_seq_num = seq_inc(seq_sub(mpdu_seq_num, buf_size));
|
|
/* release stored frames up to new head to stack */
|
|
phl_release_reorder_frames(phl_info, r, head_seq_num, frames);
|
|
}
|
|
|
|
/* Now the new frame is always in the range of the reordering buffer */
|
|
|
|
index = reorder_index(r, mpdu_seq_num);
|
|
|
|
/* check if we already stored this frame */
|
|
if (r->reorder_buf[index]) {
|
|
PHL_TRACE(COMP_PHL_RECV, _PHL_DEBUG_, "Rx drop: old seq 0x%03x head 0x%03x\n",
|
|
meta->seq, r->head_seq_num);
|
|
hci_trx_ops->recycle_rx_pkt(phl_info, pkt);
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
* If the current MPDU is in the right order and nothing else
|
|
* is stored we can process it directly, no need to buffer it.
|
|
* If it is first but there's something stored, we may be able
|
|
* to release frames after this one.
|
|
*/
|
|
if (mpdu_seq_num == r->head_seq_num &&
|
|
r->stored_mpdu_num == 0) {
|
|
r->head_seq_num = seq_inc(r->head_seq_num);
|
|
return false;
|
|
}
|
|
|
|
/* put the frame in the reordering buffer */
|
|
r->reorder_buf[index] = pkt;
|
|
r->reorder_time[index] = _os_get_cur_time_ms();
|
|
r->stored_mpdu_num++;
|
|
phl_reorder_release(phl_info, r, frames);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
enum rtw_phl_status phl_rx_reorder(struct phl_info_t *phl_info,
|
|
struct rtw_phl_rx_pkt *phl_rx,
|
|
_os_list *frames)
|
|
{
|
|
/* ref wil_rx_reorder() and ieee80211_rx_reorder_ampdu() */
|
|
|
|
void *drv_priv = phl_to_drvpriv(phl_info);
|
|
struct rtw_r_meta_data *meta = &phl_rx->r.mdata;
|
|
u16 tid = meta->tid;
|
|
struct rtw_phl_stainfo_t *sta = NULL;
|
|
struct phl_tid_ampdu_rx *r;
|
|
struct phl_hci_trx_ops *hci_trx_ops = phl_info->hci_trx_ops;
|
|
|
|
/*
|
|
* Remove FCS if is is appended
|
|
* TODO: handle more than one in pkt_list
|
|
*/
|
|
if (phl_info->phl_com->append_fcs) {
|
|
/*
|
|
* Only last MSDU of A-MSDU includes FCS.
|
|
* TODO: If A-MSDU cut processing is in HAL, should only deduct
|
|
* FCS from length of last one of pkt_list. For such case,
|
|
* phl_rx->r should have pkt_list length.
|
|
*/
|
|
if (!(meta->amsdu_cut && !meta->last_msdu)) {
|
|
if (phl_rx->r.pkt_list[0].length <= 4) {
|
|
PHL_ERR("%s, pkt_list[0].length(%d) too short\n",
|
|
__func__, phl_rx->r.pkt_list[0].length);
|
|
goto drop_frame;
|
|
}
|
|
phl_rx->r.pkt_list[0].length -= 4;
|
|
}
|
|
}
|
|
|
|
if (phl_is_mp_mode(phl_info->phl_com))
|
|
goto dont_reorder;
|
|
|
|
if (meta->bc || meta->mc)
|
|
goto dont_reorder;
|
|
|
|
if (!meta->qos)
|
|
goto dont_reorder;
|
|
|
|
if (meta->q_null)
|
|
goto dont_reorder;
|
|
|
|
/* TODO: check ba policy is either ba or normal */
|
|
|
|
/* if the mpdu is fragmented, don't reorder */
|
|
if (meta->more_frag || meta->frag_num) {
|
|
PHL_TRACE(COMP_PHL_RECV, _PHL_ERR_,
|
|
"Receive QoS Data with more_frag=%u, frag_num=%u\n",
|
|
meta->more_frag, meta->frag_num);
|
|
goto dont_reorder;
|
|
}
|
|
|
|
/* Use MAC ID from address CAM if this packet is address CAM matched */
|
|
if (meta->addr_cam_vld)
|
|
sta = rtw_phl_get_stainfo_by_macid(phl_info, meta->macid);
|
|
|
|
/* Otherwise, search STA by TA */
|
|
if (!sta || !sta->wrole) {
|
|
struct rtw_wifi_role_t *wrole;
|
|
wrole = phl_get_wrole_by_addr(phl_info, meta->mac_addr);
|
|
if (wrole)
|
|
sta = rtw_phl_get_stainfo_by_addr(phl_info,
|
|
wrole, meta->ta);
|
|
if (!wrole || !sta) {
|
|
PHL_TRACE(COMP_PHL_RECV, _PHL_WARNING_,
|
|
"%s(): stainfo or wrole not found, cam=%u, macid=%u\n",
|
|
__FUNCTION__, meta->addr_cam, meta->macid);
|
|
goto dont_reorder;
|
|
}
|
|
}
|
|
|
|
phl_rx->r.tx_sta = sta;
|
|
phl_rx->r.rx_role = sta->wrole;
|
|
|
|
rtw_hal_set_sta_rx_sts(sta, false, meta);
|
|
|
|
if (tid >= ARRAY_SIZE(sta->tid_rx)) {
|
|
PHL_TRACE(COMP_PHL_RECV, _PHL_ERR_, "Fail: tid (%u) index out of range (%u)\n", tid, 8);
|
|
goto drop_frame;
|
|
}
|
|
|
|
_os_spinlock(drv_priv, &sta->tid_rx_lock, _bh, NULL);
|
|
|
|
r = sta->tid_rx[tid];
|
|
if (!r) {
|
|
_os_spinunlock(drv_priv, &sta->tid_rx_lock, _bh, NULL);
|
|
goto dont_reorder;
|
|
}
|
|
|
|
if (!phl_manage_sta_reorder_buf(phl_info, phl_rx, r, frames)) {
|
|
_os_spinunlock(drv_priv, &sta->tid_rx_lock, _bh, NULL);
|
|
goto dont_reorder;
|
|
}
|
|
|
|
_os_spinunlock(drv_priv, &sta->tid_rx_lock, _bh, NULL);
|
|
|
|
return RTW_PHL_STATUS_SUCCESS;
|
|
|
|
drop_frame:
|
|
hci_trx_ops->recycle_rx_pkt(phl_info, phl_rx);
|
|
return RTW_PHL_STATUS_FAILURE;
|
|
|
|
dont_reorder:
|
|
list_add_tail(&phl_rx->list, frames);
|
|
return RTW_PHL_STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
u8 phl_check_recv_ring_resource(struct phl_info_t *phl_info)
|
|
{
|
|
struct rtw_phl_rx_ring *ring = &phl_info->phl_rx_ring;
|
|
u16 avail = 0, wptr = 0, rptr = 0;
|
|
void *drv_priv = phl_to_drvpriv(phl_info);
|
|
|
|
wptr = (u16)_os_atomic_read(drv_priv, &ring->phl_idx);
|
|
rptr = (u16)_os_atomic_read(drv_priv, &ring->core_idx);
|
|
avail = phl_calc_avail_wptr(rptr, wptr, MAX_PHL_RING_ENTRY_NUM);
|
|
|
|
if (0 == avail)
|
|
return false;
|
|
else
|
|
return true;
|
|
}
|
|
|
|
void dump_phl_rx_ring(void *phl)
|
|
{
|
|
struct phl_info_t *phl_info = (struct phl_info_t *)phl;
|
|
void *drv_priv = phl_to_drvpriv(phl_info);
|
|
s16 diff = 0;
|
|
u16 idx = 0, endidx = 0;
|
|
u16 phl_idx = 0, core_idx = 0;
|
|
|
|
PHL_TRACE(COMP_PHL_RECV, _PHL_DEBUG_, "===Dump PHL RX Ring===\n");
|
|
phl_idx = (u16)_os_atomic_read(drv_priv, &phl_info->phl_rx_ring.phl_idx);
|
|
core_idx = (u16)_os_atomic_read(drv_priv, &phl_info->phl_rx_ring.core_idx);
|
|
PHL_TRACE(COMP_PHL_RECV, _PHL_DEBUG_,
|
|
"core_idx = %d\n"
|
|
"phl_idx = %d\n",
|
|
core_idx,
|
|
phl_idx);
|
|
|
|
diff= phl_idx-core_idx;
|
|
if(diff < 0)
|
|
diff= 4096+diff;
|
|
|
|
endidx = diff > 5 ? (core_idx+6): phl_idx;
|
|
for (idx = core_idx+1; idx < endidx; idx++) {
|
|
PHL_TRACE(COMP_PHL_RECV, _PHL_DEBUG_, "entry[%d] = %p\n", idx,
|
|
phl_info->phl_rx_ring.entry[idx%4096]);
|
|
}
|
|
}
|
|
|
|
|
|
void phl_event_indicator(void *context)
|
|
{
|
|
enum rtw_phl_status sts = RTW_PHL_STATUS_FAILURE;
|
|
struct rtw_phl_handler *phl_handler
|
|
= (struct rtw_phl_handler *)phl_container_of(context,
|
|
struct rtw_phl_handler,
|
|
os_handler);
|
|
struct phl_info_t *phl_info = (struct phl_info_t *)phl_handler->context;
|
|
struct rtw_phl_evt_ops *ops = NULL;
|
|
struct rtw_evt_info_t *evt_info = NULL;
|
|
void *drv_priv = NULL;
|
|
enum rtw_phl_evt evt_bitmap = 0;
|
|
FUNCIN_WSTS(sts);
|
|
|
|
if (NULL != phl_info) {
|
|
ops = &phl_info->phl_com->evt_ops;
|
|
evt_info = &phl_info->phl_com->evt_info;
|
|
drv_priv = phl_to_drvpriv(phl_info);
|
|
|
|
_os_spinlock(drv_priv, &evt_info->evt_lock, _bh, NULL);
|
|
evt_bitmap = evt_info->evt_bitmap;
|
|
evt_info->evt_bitmap = 0;
|
|
_os_spinunlock(drv_priv, &evt_info->evt_lock, _bh, NULL);
|
|
|
|
if (RTW_PHL_EVT_RX & evt_bitmap) {
|
|
if (NULL != ops->rx_process) {
|
|
sts = ops->rx_process(drv_priv);
|
|
}
|
|
dump_phl_rx_ring(phl_info);
|
|
}
|
|
}
|
|
FUNCOUT_WSTS(sts);
|
|
|
|
}
|
|
|
|
void _phl_rx_statistics_reset(struct phl_info_t *phl_info)
|
|
{
|
|
struct rtw_phl_com_t *phl_com = phl_info->phl_com;
|
|
struct rtw_phl_stainfo_t *sta = NULL;
|
|
struct rtw_wifi_role_t *role = NULL;
|
|
void *drv = phl_to_drvpriv(phl_info);
|
|
struct phl_queue *sta_queue;
|
|
u8 i;
|
|
|
|
for (i = 0; i< MAX_WIFI_ROLE_NUMBER; i++) {
|
|
role = &phl_com->wifi_roles[i];
|
|
if (role->active && (role->mstate == MLME_LINKED)) {
|
|
sta_queue = &role->assoc_sta_queue;
|
|
_os_spinlock(drv, &sta_queue->lock, _bh, NULL);
|
|
phl_list_for_loop(sta, struct rtw_phl_stainfo_t,
|
|
&sta_queue->queue, list) {
|
|
if (sta)
|
|
rtw_hal_set_sta_rx_sts(sta, true, NULL);
|
|
}
|
|
_os_spinunlock(drv, &sta_queue->lock, _bh, NULL);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
phl_rx_watchdog(struct phl_info_t *phl_info)
|
|
{
|
|
struct rtw_stats *phl_stats = &phl_info->phl_com->phl_stats;
|
|
|
|
phl_rx_traffic_upd(phl_stats);
|
|
phl_dump_rx_stats(phl_stats);
|
|
_phl_rx_statistics_reset(phl_info);
|
|
}
|
|
|
|
u16 rtw_phl_query_new_rx_num(void *phl)
|
|
{
|
|
struct phl_info_t *phl_info = (struct phl_info_t *)phl;
|
|
struct rtw_phl_rx_ring *ring = NULL;
|
|
u16 new_rx = 0, wptr = 0, rptr = 0;
|
|
|
|
if (NULL != phl_info) {
|
|
ring = &phl_info->phl_rx_ring;
|
|
wptr = (u16)_os_atomic_read(phl_to_drvpriv(phl_info),
|
|
&ring->phl_idx);
|
|
rptr = (u16)_os_atomic_read(phl_to_drvpriv(phl_info),
|
|
&ring->core_idx);
|
|
new_rx = phl_calc_avail_rptr(rptr, wptr,
|
|
MAX_PHL_RING_ENTRY_NUM);
|
|
}
|
|
|
|
return new_rx;
|
|
}
|
|
|
|
struct rtw_recv_pkt *rtw_phl_query_rx_pkt(void *phl)
|
|
{
|
|
struct phl_info_t *phl_info = (struct phl_info_t *)phl;
|
|
struct rtw_phl_rx_ring *ring = NULL;
|
|
struct rtw_recv_pkt *recvpkt = NULL;
|
|
void *drv_priv = NULL;
|
|
u16 ring_res = 0, wptr = 0, rptr = 0;
|
|
|
|
if (NULL != phl_info) {
|
|
ring = &phl_info->phl_rx_ring;
|
|
drv_priv = phl_to_drvpriv(phl_info);
|
|
|
|
wptr = (u16)_os_atomic_read(drv_priv, &ring->phl_idx);
|
|
rptr = (u16)_os_atomic_read(drv_priv, &ring->core_idx);
|
|
|
|
ring_res = phl_calc_avail_rptr(rptr, wptr,
|
|
MAX_PHL_RING_ENTRY_NUM);
|
|
|
|
PHL_TRACE(COMP_PHL_RECV, _PHL_DEBUG_,
|
|
"[4] %s::[Query] phl_idx =%d , core_idx =%d , ring_res =%d\n",
|
|
__FUNCTION__,
|
|
_os_atomic_read(drv_priv, &ring->phl_idx),
|
|
_os_atomic_read(drv_priv, &ring->core_idx),
|
|
ring_res);
|
|
|
|
if (ring_res > 0) {
|
|
rptr = rptr + 1;
|
|
|
|
if (rptr >= MAX_PHL_RING_ENTRY_NUM) {
|
|
rptr=0;
|
|
recvpkt = (struct rtw_recv_pkt *)ring->entry[rptr];
|
|
ring->entry[rptr]=NULL;
|
|
_os_atomic_set(drv_priv, &ring->core_idx, 0);
|
|
} else {
|
|
recvpkt = (struct rtw_recv_pkt *)ring->entry[rptr];
|
|
ring->entry[rptr]=NULL;
|
|
_os_atomic_inc(drv_priv, &ring->core_idx);
|
|
}
|
|
if (NULL == recvpkt)
|
|
PHL_TRACE(COMP_PHL_RECV, _PHL_WARNING_, "recvpkt is NULL!\n");
|
|
else
|
|
phl_rx_statistics(phl_info, recvpkt);
|
|
} else {
|
|
PHL_TRACE(COMP_PHL_RECV, _PHL_INFO_, "no available rx packet to query!\n");
|
|
}
|
|
}
|
|
|
|
return recvpkt;
|
|
}
|
|
|
|
enum rtw_phl_status rtw_phl_return_rxbuf(void *phl, u8* recvpkt)
|
|
{
|
|
enum rtw_phl_status pstatus = RTW_PHL_STATUS_FAILURE;
|
|
struct phl_info_t *phl_info = (struct phl_info_t *)phl;
|
|
struct rtw_phl_rx_pkt *phl_rx = NULL;
|
|
struct rtw_recv_pkt *r = (struct rtw_recv_pkt *)recvpkt;
|
|
|
|
do {
|
|
if (NULL == recvpkt)
|
|
break;
|
|
|
|
phl_rx = phl_container_of(r, struct rtw_phl_rx_pkt, r);
|
|
phl_recycle_rx_buf(phl_info, phl_rx);
|
|
pstatus = RTW_PHL_STATUS_SUCCESS;
|
|
} while (false);
|
|
|
|
return pstatus;
|
|
}
|
|
|
|
|
|
enum rtw_phl_status rtw_phl_start_rx_process(void *phl)
|
|
{
|
|
enum rtw_phl_status pstatus = RTW_PHL_STATUS_FAILURE;
|
|
struct phl_info_t *phl_info = (struct phl_info_t *)phl;
|
|
|
|
FUNCIN_WSTS(pstatus);
|
|
|
|
pstatus = phl_schedule_handler(phl_info->phl_com,
|
|
&phl_info->phl_rx_handler);
|
|
|
|
FUNCOUT_WSTS(pstatus);
|
|
|
|
return pstatus;
|
|
}
|
|
|
|
void rtw_phl_rx_bar(void *phl, struct rtw_phl_stainfo_t *sta, u8 tid, u16 seq)
|
|
{
|
|
/* ref ieee80211_rx_h_ctrl() and wil_rx_bar() */
|
|
|
|
struct phl_info_t *phl_info = (struct phl_info_t *)phl;
|
|
void *drv_priv = phl_to_drvpriv(phl_info);
|
|
struct phl_tid_ampdu_rx *r;
|
|
_os_list frames;
|
|
|
|
INIT_LIST_HEAD(&frames);
|
|
|
|
if (tid >= RTW_MAX_TID_NUM)
|
|
goto out;
|
|
|
|
_os_spinlock(drv_priv, &sta->tid_rx_lock, _bh, NULL);
|
|
|
|
r = sta->tid_rx[tid];
|
|
if (!r) {
|
|
PHL_TRACE(COMP_PHL_RECV, _PHL_ERR_, "BAR for non-existing TID %d\n", tid);
|
|
goto out;
|
|
}
|
|
|
|
if (seq_less(seq, r->head_seq_num)) {
|
|
PHL_TRACE(COMP_PHL_RECV, _PHL_ERR_, "BAR Seq 0x%03x preceding head 0x%03x\n",
|
|
seq, r->head_seq_num);
|
|
goto out;
|
|
}
|
|
|
|
PHL_TRACE(COMP_PHL_RECV, _PHL_INFO_, "BAR: TID %d Seq 0x%03x head 0x%03x\n",
|
|
tid, seq, r->head_seq_num);
|
|
|
|
phl_release_reorder_frames(phl_info, r, seq, &frames);
|
|
phl_handle_rx_frame_list(phl_info, &frames);
|
|
|
|
out:
|
|
_os_spinunlock(drv_priv, &sta->tid_rx_lock, _bh, NULL);
|
|
}
|
|
|
|
enum rtw_rx_status rtw_phl_get_rx_status(void *phl)
|
|
{
|
|
#ifdef CONFIG_USB_HCI
|
|
struct phl_info_t *phl_info = (struct phl_info_t *)phl;
|
|
enum rtw_hci_type hci_type = phl_info->phl_com->hci_type;
|
|
|
|
if (hci_type & RTW_HCI_USB)
|
|
return rtw_hal_get_usb_status(phl_info->hal);
|
|
#endif
|
|
|
|
return RTW_STATUS_RX_OK;
|
|
}
|
|
|
|
enum rtw_phl_status
|
|
rtw_phl_enter_mon_mode(void *phl, struct rtw_wifi_role_t *wrole)
|
|
{
|
|
struct phl_info_t *phl_info = (struct phl_info_t *)phl;
|
|
enum rtw_hal_status status;
|
|
|
|
status = rtw_hal_enter_mon_mode(phl_info->hal, wrole->hw_band);
|
|
if (status != RTW_HAL_STATUS_SUCCESS) {
|
|
PHL_TRACE(COMP_PHL_RECV, _PHL_ERR_,
|
|
"%s(): rtw_hal_enter_mon_mode() failed, status=%d",
|
|
__FUNCTION__, status);
|
|
return RTW_PHL_STATUS_FAILURE;
|
|
}
|
|
|
|
return RTW_PHL_STATUS_SUCCESS;
|
|
}
|
|
|
|
enum rtw_phl_status
|
|
rtw_phl_leave_mon_mode(void *phl, struct rtw_wifi_role_t *wrole)
|
|
{
|
|
struct phl_info_t *phl_info = (struct phl_info_t *)phl;
|
|
enum rtw_hal_status status;
|
|
|
|
status = rtw_hal_leave_mon_mode(phl_info->hal, wrole->hw_band);
|
|
if (status != RTW_HAL_STATUS_SUCCESS) {
|
|
PHL_TRACE(COMP_PHL_RECV, _PHL_ERR_,
|
|
"%s(): rtw_hal_leave_mon_mode() failed, status=%d",
|
|
__FUNCTION__, status);
|
|
return RTW_PHL_STATUS_FAILURE;
|
|
}
|
|
|
|
return RTW_PHL_STATUS_SUCCESS;
|
|
}
|
|
|
|
#ifdef CONFIG_PHL_RX_PSTS_PER_PKT
|
|
void
|
|
_phl_rx_proc_frame_list(struct phl_info_t *phl_info, struct phl_queue *pq)
|
|
{
|
|
void *d = phl_to_drvpriv(phl_info);
|
|
_os_list *pkt_list = NULL;
|
|
struct rtw_phl_rx_pkt *phl_rx = NULL;
|
|
|
|
if (NULL == pq)
|
|
return;
|
|
if (0 == pq->cnt)
|
|
return;
|
|
|
|
PHL_TRACE(COMP_PHL_PSTS, _PHL_INFO_,
|
|
"_phl_rx_proc_frame_list : queue ele cnt = %d\n",
|
|
pq->cnt);
|
|
|
|
while (true == pq_pop(d, pq, &pkt_list, _first, _bh)) {
|
|
phl_rx = (struct rtw_phl_rx_pkt *)pkt_list;
|
|
phl_info->hci_trx_ops->rx_handle_normal(phl_info, phl_rx);
|
|
}
|
|
}
|
|
|
|
enum rtw_phl_status
|
|
phl_rx_proc_phy_sts(struct phl_info_t *phl_info, struct rtw_phl_rx_pkt *ppdu_sts)
|
|
{
|
|
enum rtw_phl_status pstatus = RTW_PHL_STATUS_FAILURE;
|
|
struct rtw_phl_ppdu_sts_info *psts_info = &(phl_info->phl_com->ppdu_sts_info);
|
|
struct rtw_phl_ppdu_sts_ent *sts_entry = NULL;
|
|
struct rtw_phl_rx_pkt *phl_rx = NULL;
|
|
void *d = phl_to_drvpriv(phl_info);
|
|
struct rtw_phl_rssi_stat *rssi_stat = &phl_info->phl_com->rssi_stat;
|
|
_os_list *frame = NULL;
|
|
bool upt_psts = true;
|
|
u8 i = 0;
|
|
enum phl_band_idx band = HW_BAND_0;
|
|
|
|
if (NULL == ppdu_sts)
|
|
return pstatus;
|
|
|
|
if (false == psts_info->en_psts_per_pkt) {
|
|
return pstatus;
|
|
}
|
|
|
|
if (ppdu_sts->r.mdata.ppdu_cnt >= PHL_MAX_PPDU_CNT) {
|
|
PHL_TRACE(COMP_PHL_PSTS, _PHL_INFO_,
|
|
"ppdu_sts->r.mdata.ppdu_cnt >= PHL_MAX_PPDU_CNT!\n");
|
|
return pstatus;
|
|
}
|
|
|
|
band = (ppdu_sts->r.mdata.bb_sel > 0) ? HW_BAND_1 : HW_BAND_0;
|
|
|
|
if (false == psts_info->en_ppdu_sts[band])
|
|
return pstatus;
|
|
|
|
if (ppdu_sts->r.mdata.ppdu_cnt != psts_info->cur_ppdu_cnt[band]) {
|
|
PHL_TRACE(COMP_PHL_PSTS, _PHL_INFO_,
|
|
"ppdu_sts->r.mdata.ppdu_cnt != psts_info->cur_ppdu_cnt!\n");
|
|
upt_psts = false;
|
|
}
|
|
|
|
sts_entry = &psts_info->sts_ent[band][psts_info->cur_ppdu_cnt[band]];
|
|
/* check list empty */
|
|
if (0 == sts_entry->frames.cnt) {
|
|
PHL_TRACE(COMP_PHL_PSTS, _PHL_INFO_,
|
|
"cur_ppdu_cnt %d --> sts_entry->frames.cnt = 0\n",
|
|
psts_info->cur_ppdu_cnt[band]);
|
|
pstatus = RTW_PHL_STATUS_SUCCESS;
|
|
return pstatus;
|
|
}
|
|
|
|
/* start update phy info to per pkt*/
|
|
if (false == pq_get_front(d, &sts_entry->frames, &frame, _bh)) {
|
|
PHL_ERR(" %s list empty\n", __FUNCTION__);
|
|
return pstatus;
|
|
}
|
|
/**
|
|
* TODO : How to filter the case :
|
|
* pkt(ppdu_cnt = 0) --> missing :psts(ppdu_cnt = 0) --> (all of the pkt, psts dropped/missing)
|
|
* --> ppdu_sts(ppdu_cnt = 0)(not for the current buffered pkt.)
|
|
* workaround : check rate/bw/ppdu_type/... etc
|
|
**/
|
|
phl_rx = (struct rtw_phl_rx_pkt *)frame;
|
|
if (upt_psts &&
|
|
((phl_rx->r.mdata.rx_rate != ppdu_sts->r.mdata.rx_rate) ||
|
|
(phl_rx->r.mdata.bw != ppdu_sts->r.mdata.bw) ||
|
|
(phl_rx->r.mdata.rx_gi_ltf != ppdu_sts->r.mdata.rx_gi_ltf) ||
|
|
(phl_rx->r.mdata.ppdu_type != ppdu_sts->r.mdata.ppdu_type))) {
|
|
/**
|
|
* ppdu status is not for the buffered pkt,
|
|
* skip update phy status to phl_rx
|
|
**/
|
|
upt_psts = false;
|
|
}
|
|
/* Get Frame Type */
|
|
ppdu_sts->r.phy_info.frame_type =
|
|
PHL_GET_80211_HDR_TYPE(phl_rx->r.pkt_list[0].vir_addr);
|
|
|
|
if ((false == ppdu_sts->r.phy_info.is_valid) &&
|
|
(true == psts_info->en_fake_psts)) {
|
|
if (RTW_FRAME_TYPE_MGNT == phl_rx->r.mdata.frame_type) {
|
|
ppdu_sts->r.phy_info.rssi =
|
|
rssi_stat->ma_rssi[RTW_RSSI_MGNT_ACAM_A1M];
|
|
} else if (RTW_FRAME_TYPE_DATA == phl_rx->r.mdata.frame_type) {
|
|
ppdu_sts->r.phy_info.rssi =
|
|
rssi_stat->ma_rssi[RTW_RSSI_DATA_ACAM_A1M];
|
|
} else if (RTW_FRAME_TYPE_CTRL == phl_rx->r.mdata.frame_type) {
|
|
ppdu_sts->r.phy_info.rssi =
|
|
rssi_stat->ma_rssi[RTW_RSSI_CTRL_ACAM_A1M];
|
|
} else {
|
|
ppdu_sts->r.phy_info.rssi =
|
|
rssi_stat->ma_rssi[RTW_RSSI_UNKNOWN];
|
|
}
|
|
for(i = 0; i< RTW_PHL_MAX_RF_PATH ; i++) {
|
|
ppdu_sts->r.phy_info.rssi_path[i] =
|
|
ppdu_sts->r.phy_info.rssi;
|
|
}
|
|
ppdu_sts->r.phy_info.ch_idx = rtw_hal_get_cur_ch(phl_info->hal,
|
|
phl_rx->r.mdata.bb_sel);
|
|
ppdu_sts->r.phy_info.is_valid = true;
|
|
}
|
|
|
|
do {
|
|
if (false == upt_psts)
|
|
break;
|
|
phl_rx = (struct rtw_phl_rx_pkt *)frame;
|
|
_os_mem_cpy(d, &(phl_rx->r.phy_info), &(ppdu_sts->r.phy_info),
|
|
sizeof(struct rtw_phl_ppdu_phy_info));
|
|
} while ((true == psts_info->psts_ampdu) &&
|
|
(pq_get_next(d, &sts_entry->frames, frame, &frame, _bh)));
|
|
|
|
/*2. indicate the frame list*/
|
|
_phl_rx_proc_frame_list(phl_info, &sts_entry->frames);
|
|
/*3. reset the queue */
|
|
pq_reset(d, &(sts_entry->frames), _bh);
|
|
|
|
return pstatus;
|
|
}
|
|
|
|
bool
|
|
phl_rx_proc_wait_phy_sts(struct phl_info_t *phl_info,
|
|
struct rtw_phl_rx_pkt *phl_rx)
|
|
{
|
|
struct rtw_phl_ppdu_sts_info *psts_info = &(phl_info->phl_com->ppdu_sts_info);
|
|
struct rtw_phl_ppdu_sts_ent *sts_entry = NULL;
|
|
void *d = phl_to_drvpriv(phl_info);
|
|
u8 i = 0;
|
|
bool ret = false;
|
|
enum phl_band_idx band = HW_BAND_0;
|
|
|
|
if (false == psts_info->en_psts_per_pkt) {
|
|
return ret;
|
|
}
|
|
|
|
if (phl_rx->r.mdata.ppdu_cnt >= PHL_MAX_PPDU_CNT) {
|
|
PHL_ASSERT("phl_rx->r.mdata.ppdu_cnt >= PHL_MAX_PPDU_CNT!");
|
|
return ret;
|
|
}
|
|
|
|
band = (phl_rx->r.mdata.bb_sel > 0) ? HW_BAND_1 : HW_BAND_0;
|
|
|
|
if (false == psts_info->en_ppdu_sts[band])
|
|
return ret;
|
|
|
|
if (psts_info->cur_ppdu_cnt[band] != phl_rx->r.mdata.ppdu_cnt) {
|
|
/* start of PPDU */
|
|
/* 1. Check all of the buffer list is empty */
|
|
/* only check the target rx pkt band */
|
|
for (i = 0; i < PHL_MAX_PPDU_CNT; i++) {
|
|
sts_entry = &psts_info->sts_ent[band][i];
|
|
if (0 != sts_entry->frames.cnt) {
|
|
/* need indicate first */
|
|
PHL_TRACE(COMP_PHL_PSTS, _PHL_INFO_,
|
|
"band %d ; ppdu_cnt %d queue is not empty \n",
|
|
band, i);
|
|
_phl_rx_proc_frame_list(phl_info,
|
|
&sts_entry->frames);
|
|
pq_reset(d, &(sts_entry->frames), _bh);
|
|
}
|
|
}
|
|
|
|
/* 2. check ppdu status filter condition */
|
|
/* Filter function is supportted only if rxd = long_rxd */
|
|
if ((1 == phl_rx->r.mdata.long_rxd) &&
|
|
(0 != (psts_info->ppdu_sts_filter &
|
|
BIT(phl_rx->r.mdata.frame_type)))) {
|
|
/* 3. add new rx pkt to the tail of the queue */
|
|
sts_entry = &psts_info->sts_ent[band][phl_rx->r.mdata.ppdu_cnt];
|
|
pq_reset(d, &(sts_entry->frames), _bh);
|
|
pq_push(d, &(sts_entry->frames), &phl_rx->list,
|
|
_tail, _bh);
|
|
ret = true;
|
|
}
|
|
psts_info->cur_ppdu_cnt[band] = phl_rx->r.mdata.ppdu_cnt;
|
|
} else {
|
|
/* 1. check ppdu status filter condition */
|
|
/* Filter function is supportted only if rxd = long_rxd */
|
|
if ((1 == phl_rx->r.mdata.long_rxd) &&
|
|
(0 != (psts_info->ppdu_sts_filter &
|
|
BIT(phl_rx->r.mdata.frame_type)))) {
|
|
/* 2. add to frame list */
|
|
sts_entry = &psts_info->sts_ent[band][phl_rx->r.mdata.ppdu_cnt];
|
|
if (0 == sts_entry->frames.cnt) {
|
|
PHL_TRACE(COMP_PHL_PSTS, _PHL_INFO_,
|
|
"MPDU is not the start of PPDU, but the queue is empty!!!\n");
|
|
}
|
|
pq_push(d, &(sts_entry->frames), &phl_rx->list,
|
|
_tail, _bh);
|
|
ret = true;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
#ifdef CONFIG_PHY_INFO_NTFY
|
|
void _phl_rx_post_proc_ppdu_sts(void* priv, struct phl_msg* msg)
|
|
{
|
|
struct phl_info_t *phl_info = (struct phl_info_t *)priv;
|
|
if (msg->inbuf && msg->inlen){
|
|
_os_kmem_free(phl_to_drvpriv(phl_info), msg->inbuf, msg->inlen);
|
|
}
|
|
}
|
|
|
|
bool
|
|
_phl_rx_proc_aggr_psts_ntfy(struct phl_info_t *phl_info,
|
|
struct rtw_phl_ppdu_sts_ent *ppdu_sts_ent)
|
|
{
|
|
struct rtw_phl_ppdu_sts_info *ppdu_info =
|
|
&phl_info->phl_com->ppdu_sts_info;
|
|
struct rtw_phl_ppdu_sts_ntfy *psts_ntfy = NULL;
|
|
u8 i = 0;
|
|
bool ret = false;
|
|
|
|
if (ppdu_info->msg_aggr_cnt == 0) {
|
|
/* reset entry valid status */
|
|
for (i = 0; i < MAX_PSTS_MSG_AGGR_NUM; i++) {
|
|
ppdu_info->msg_aggr_buf[i].vld = false;
|
|
}
|
|
}
|
|
/* copy to the buf */
|
|
psts_ntfy = &ppdu_info->msg_aggr_buf[ppdu_info->msg_aggr_cnt];
|
|
psts_ntfy->frame_type = ppdu_sts_ent->frame_type;
|
|
_os_mem_cpy(phl_info->phl_com->drv_priv,
|
|
&psts_ntfy->phy_info,
|
|
&ppdu_sts_ent->phy_info,
|
|
sizeof(struct rtw_phl_ppdu_phy_info));
|
|
_os_mem_cpy(phl_info->phl_com->drv_priv,
|
|
psts_ntfy->src_mac_addr,
|
|
ppdu_sts_ent->src_mac_addr,
|
|
MAC_ADDRESS_LENGTH);
|
|
psts_ntfy->vld = true;
|
|
|
|
/* update counter */
|
|
ppdu_info->msg_aggr_cnt++;
|
|
if (ppdu_info->msg_aggr_cnt >= MAX_PSTS_MSG_AGGR_NUM) {
|
|
ppdu_info->msg_aggr_cnt = 0;
|
|
ret = true;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
void
|
|
phl_rx_proc_ppdu_sts(struct phl_info_t *phl_info, struct rtw_phl_rx_pkt *phl_rx)
|
|
{
|
|
u8 i = 0;
|
|
struct rtw_phl_ppdu_sts_info *ppdu_info = NULL;
|
|
struct rtw_phl_ppdu_sts_ent *ppdu_sts_ent = NULL;
|
|
struct rtw_phl_stainfo_t *psta = NULL;
|
|
#ifdef CONFIG_PHY_INFO_NTFY
|
|
struct rtw_phl_ppdu_sts_ntfy *psts_ntfy;
|
|
void *d = phl_to_drvpriv(phl_info);
|
|
#endif
|
|
enum phl_band_idx band = HW_BAND_0;
|
|
struct rtw_rssi_info *rssi_sts;
|
|
|
|
if ((NULL == phl_info) || (NULL == phl_rx))
|
|
return;
|
|
|
|
band = (phl_rx->r.mdata.bb_sel > 0) ? HW_BAND_1 : HW_BAND_0;
|
|
ppdu_info = &phl_info->phl_com->ppdu_sts_info;
|
|
ppdu_sts_ent = &ppdu_info->sts_ent[band][phl_rx->r.mdata.ppdu_cnt];
|
|
|
|
if (false == ppdu_sts_ent->valid)
|
|
return;
|
|
|
|
if (true == ppdu_sts_ent->phl_done)
|
|
return;
|
|
|
|
ppdu_sts_ent->phl_done = true;
|
|
|
|
/* update phl self varibles */
|
|
for(i = 0 ; i < ppdu_sts_ent->usr_num; i++) {
|
|
if (ppdu_sts_ent->sta[i].vld) {
|
|
psta = rtw_phl_get_stainfo_by_macid(phl_info,
|
|
ppdu_sts_ent->sta[i].macid);
|
|
if (psta == NULL)
|
|
continue;
|
|
rssi_sts = &psta->hal_sta->rssi_stat;
|
|
STA_UPDATE_MA_RSSI_FAST(rssi_sts->ma_rssi, ppdu_sts_ent->phy_info.rssi);
|
|
/* update (re)associate req/resp pkt rssi */
|
|
if (RTW_IS_ASOC_PKT(ppdu_sts_ent->frame_type)) {
|
|
rssi_sts->assoc_rssi =
|
|
ppdu_sts_ent->phy_info.rssi;
|
|
}
|
|
|
|
if (RTW_IS_BEACON_OR_PROBE_RESP_PKT(
|
|
ppdu_sts_ent->frame_type)) {
|
|
if (0 == rssi_sts->ma_rssi_mgnt) {
|
|
rssi_sts->ma_rssi_mgnt =
|
|
ppdu_sts_ent->phy_info.rssi;
|
|
} else {
|
|
STA_UPDATE_MA_RSSI_FAST(
|
|
rssi_sts->ma_rssi_mgnt,
|
|
ppdu_sts_ent->phy_info.rssi);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
if (RTW_IS_ASOC_REQ_PKT(ppdu_sts_ent->frame_type) &&
|
|
(ppdu_sts_ent->usr_num == 1)) {
|
|
psta = rtw_phl_get_stainfo_by_addr_ex(phl_info,
|
|
ppdu_sts_ent->src_mac_addr);
|
|
if (psta) {
|
|
psta->hal_sta->rssi_stat.assoc_rssi =
|
|
ppdu_sts_ent->phy_info.rssi;
|
|
|
|
#ifdef DBG_AP_CLIENT_ASSOC_RSSI
|
|
PHL_INFO("%s [Rx-ASOC_REQ] - macid:%d, MAC-Addr:%02x-%02x-%02x-%02x-%02x-%02x, assoc_rssi:%d\n",
|
|
__func__,
|
|
psta->macid,
|
|
ppdu_sts_ent->src_mac_addr[0],
|
|
ppdu_sts_ent->src_mac_addr[1],
|
|
ppdu_sts_ent->src_mac_addr[2],
|
|
ppdu_sts_ent->src_mac_addr[3],
|
|
ppdu_sts_ent->src_mac_addr[4],
|
|
ppdu_sts_ent->src_mac_addr[5],
|
|
psta->hal_sta->rssi_stat.assoc_rssi);
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef CONFIG_PHY_INFO_NTFY
|
|
/*2. prepare and send psts notify to core */
|
|
if((RTW_FRAME_TYPE_BEACON == ppdu_sts_ent->frame_type) ||
|
|
(RTW_FRAME_TYPE_PROBE_RESP == ppdu_sts_ent->frame_type)) {
|
|
|
|
if (false == _phl_rx_proc_aggr_psts_ntfy(phl_info,
|
|
ppdu_sts_ent)) {
|
|
return;
|
|
}
|
|
|
|
/* send aggr psts ntfy*/
|
|
psts_ntfy = (struct rtw_phl_ppdu_sts_ntfy *)_os_kmem_alloc(d,
|
|
MAX_PSTS_MSG_AGGR_NUM * sizeof(struct rtw_phl_ppdu_sts_ntfy));
|
|
if (psts_ntfy == NULL) {
|
|
PHL_ERR("%s: alloc ppdu sts for ntfy fail.\n", __func__);
|
|
return;
|
|
}
|
|
|
|
_os_mem_cpy(phl_info->phl_com->drv_priv,
|
|
psts_ntfy,
|
|
&ppdu_info->msg_aggr_buf,
|
|
(MAX_PSTS_MSG_AGGR_NUM *
|
|
sizeof(struct rtw_phl_ppdu_sts_ntfy)));
|
|
|
|
msg.inbuf = (u8 *)psts_ntfy;
|
|
msg.inlen = (MAX_PSTS_MSG_AGGR_NUM *
|
|
sizeof(struct rtw_phl_ppdu_sts_ntfy));
|
|
SET_MSG_MDL_ID_FIELD(msg.msg_id, PHL_MDL_PSTS);
|
|
SET_MSG_EVT_ID_FIELD(msg.msg_id, MSG_EVT_RX_PSTS);
|
|
attr.completion.completion = _phl_rx_post_proc_ppdu_sts;
|
|
attr.completion.priv = phl_info;
|
|
if (phl_msg_hub_send(phl_info, &attr, &msg) != RTW_PHL_STATUS_SUCCESS) {
|
|
PHL_ERR("%s: send msg_hub failed\n", __func__);
|
|
_os_kmem_free(d, psts_ntfy,
|
|
(MAX_PSTS_MSG_AGGR_NUM *
|
|
sizeof(struct rtw_phl_ppdu_sts_ntfy)));
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void phl_rx_wp_report_record_sts(struct phl_info_t *phl_info,
|
|
u8 macid, u16 ac_queue, u8 txsts)
|
|
{
|
|
struct rtw_phl_stainfo_t *phl_sta = NULL;
|
|
struct rtw_hal_stainfo_t *hal_sta = NULL;
|
|
struct rtw_wp_rpt_stats *wp_rpt_stats= NULL;
|
|
|
|
phl_sta = rtw_phl_get_stainfo_by_macid(phl_info, macid);
|
|
|
|
if (phl_sta) {
|
|
hal_sta = phl_sta->hal_sta;
|
|
|
|
if (hal_sta->trx_stat.wp_rpt_stats == NULL) {
|
|
PHL_ERR("rtp_stats NULL\n");
|
|
return;
|
|
}
|
|
/* Record Per ac queue statistics */
|
|
wp_rpt_stats = &hal_sta->trx_stat.wp_rpt_stats[ac_queue];
|
|
|
|
_os_spinlock(phl_to_drvpriv(phl_info), &hal_sta->trx_stat.tx_sts_lock, _bh, NULL);
|
|
if (TX_STATUS_TX_DONE == txsts) {
|
|
/* record total tx ok*/
|
|
hal_sta->trx_stat.tx_ok_cnt++;
|
|
/* record per ac queue tx ok*/
|
|
wp_rpt_stats->tx_ok_cnt++;
|
|
} else {
|
|
/* record total tx fail*/
|
|
hal_sta->trx_stat.tx_fail_cnt++;
|
|
/* record per ac queue tx fail*/
|
|
if (TX_STATUS_TX_FAIL_REACH_RTY_LMT == txsts)
|
|
wp_rpt_stats->rty_fail_cnt++;
|
|
else if (TX_STATUS_TX_FAIL_LIFETIME_DROP == txsts)
|
|
wp_rpt_stats->lifetime_drop_cnt++;
|
|
else if (TX_STATUS_TX_FAIL_MACID_DROP == txsts)
|
|
wp_rpt_stats->macid_drop_cnt++;
|
|
}
|
|
_os_spinunlock(phl_to_drvpriv(phl_info), &hal_sta->trx_stat.tx_sts_lock, _bh, NULL);
|
|
|
|
PHL_TRACE(COMP_PHL_DBG, _PHL_DEBUG_,"macid: %u, ac_queue: %u, tx_ok_cnt: %u, rty_fail_cnt: %u, "
|
|
"lifetime_drop_cnt: %u, macid_drop_cnt: %u\n"
|
|
, macid, ac_queue, wp_rpt_stats->tx_ok_cnt, wp_rpt_stats->rty_fail_cnt
|
|
, wp_rpt_stats->lifetime_drop_cnt, wp_rpt_stats->macid_drop_cnt);
|
|
PHL_TRACE(COMP_PHL_DBG, _PHL_DEBUG_,"totoal tx ok: %u \n totoal tx fail: %u\n"
|
|
, hal_sta->trx_stat.tx_ok_cnt, hal_sta->trx_stat.tx_fail_cnt);
|
|
} else {
|
|
PHL_TRACE(COMP_PHL_DBG, _PHL_DEBUG_, "%s: PHL_STA not found\n",
|
|
__FUNCTION__);
|
|
}
|
|
}
|
|
|
|
static void _dump_rx_reorder_info(struct phl_info_t *phl_info,
|
|
struct rtw_phl_stainfo_t *sta)
|
|
{
|
|
void *drv_priv = phl_to_drvpriv(phl_info);
|
|
_os_spinlockfg sp_flags;
|
|
u8 i;
|
|
|
|
PHL_INFO("dump rx reorder buffer info:\n");
|
|
for (i = 0; i < ARRAY_SIZE(sta->tid_rx); i++) {
|
|
|
|
_os_spinlock(drv_priv, &sta->tid_rx_lock, _irq, &sp_flags);
|
|
if (sta->tid_rx[i]) {
|
|
PHL_INFO("== tid = %d ==\n", sta->tid_rx[i]->tid);
|
|
PHL_INFO("head_seq_num = %d\n",
|
|
sta->tid_rx[i]->head_seq_num);
|
|
PHL_INFO("stored_mpdu_num = %d\n",
|
|
sta->tid_rx[i]->stored_mpdu_num);
|
|
PHL_INFO("ssn = %d\n", sta->tid_rx[i]->ssn);
|
|
PHL_INFO("buf_size = %d\n", sta->tid_rx[i]->buf_size);
|
|
PHL_INFO("started = %d\n", sta->tid_rx[i]->started);
|
|
PHL_INFO("removed = %d\n", sta->tid_rx[i]->removed);
|
|
}
|
|
_os_spinunlock(drv_priv, &sta->tid_rx_lock, _irq, &sp_flags);
|
|
}
|
|
}
|
|
|
|
void phl_dump_all_sta_rx_info(struct phl_info_t *phl_info)
|
|
{
|
|
struct rtw_phl_com_t *phl_com = phl_info->phl_com;
|
|
struct rtw_phl_stainfo_t *sta = NULL;
|
|
struct rtw_wifi_role_t *role = NULL;
|
|
void *drv = phl_to_drvpriv(phl_info);
|
|
struct phl_queue *sta_queue;
|
|
_os_spinlockfg sp_flags;
|
|
u8 i;
|
|
|
|
PHL_INFO("dump all sta rx info:\n");
|
|
for (i = 0; i < MAX_WIFI_ROLE_NUMBER; i++) {
|
|
role = &phl_com->wifi_roles[i];
|
|
if (role->active) {
|
|
PHL_INFO("wrole idx = %d\n", i);
|
|
PHL_INFO("wrole type = %d\n", role->type);
|
|
PHL_INFO("wrole mstate = %d\n", role->mstate);
|
|
|
|
sta_queue = &role->assoc_sta_queue;
|
|
_os_spinlock(drv, &sta_queue->lock, _irq, &sp_flags);
|
|
phl_list_for_loop(sta, struct rtw_phl_stainfo_t,
|
|
&sta_queue->queue, list) {
|
|
PHL_INFO("%s MACID:%d %02x:%02x:%02x:%02x:%02x:%02x \n",
|
|
__func__, sta->macid,
|
|
sta->mac_addr[0],
|
|
sta->mac_addr[1],
|
|
sta->mac_addr[2],
|
|
sta->mac_addr[3],
|
|
sta->mac_addr[4],
|
|
sta->mac_addr[5]);
|
|
_dump_rx_reorder_info(phl_info, sta);
|
|
}
|
|
_os_spinunlock(drv, &sta_queue->lock, _irq, &sp_flags);
|
|
}
|
|
}
|
|
}
|
|
|
|
void phl_rx_dbg_dump(struct phl_info_t *phl_info, u8 band_idx)
|
|
{
|
|
enum rtw_phl_status phl_status = RTW_PHL_STATUS_FAILURE;
|
|
|
|
phl_status = phl_cmd_enqueue(phl_info,
|
|
band_idx,
|
|
MSG_EVT_DBG_RX_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__);
|
|
}
|
|
}
|