/****************************************************************************** * * 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_STA_C_ #include "phl_headers.h" /*********** macid ctrl section ***********/ enum rtw_phl_status phl_macid_ctrl_init(struct phl_info_t *phl) { struct rtw_phl_com_t *phl_com = phl->phl_com; struct hal_spec_t *hal_spec = phl_get_ic_spec(phl_com); struct macid_ctl_t *macid_ctl = phl_to_mac_ctrl(phl); enum rtw_phl_status phl_status = RTW_PHL_STATUS_FAILURE; u8 i = 0; /* check invalid value or not */ if (hal_spec->macid_num == 0) { PHL_ERR("Cannot get macid_num of hal\n"); goto exit; } _os_spinlock_init(phl_to_drvpriv(phl), &macid_ctl->lock); macid_ctl->max_num = MIN(hal_spec->macid_num, PHL_MACID_MAX_NUM); PHL_INFO("%s macid max_num:%d\n", __func__, macid_ctl->max_num); for (i = 0; i < MAX_WIFI_ROLE_NUMBER; i++) macid_ctl->wrole_bmc[i] = macid_ctl->max_num; phl_status = RTW_PHL_STATUS_SUCCESS; exit: return phl_status; } enum rtw_phl_status phl_macid_ctrl_deinit(struct phl_info_t *phl) { struct macid_ctl_t *macid_ctl = phl_to_mac_ctrl(phl); _os_spinlock_free(phl_to_drvpriv(phl), &macid_ctl->lock); macid_ctl->max_num = 0; return RTW_PHL_STATUS_SUCCESS; } static u8 _phl_macid_is_used(u32 *map, const u16 id) { int map_idx = (int)id / 32; if (map[map_idx] & BIT(id % 32)) return true; else return false; } static void _phl_macid_map_set(u32 *map, const u16 id) { int map_idx = (int)id / 32; map[map_idx] |= BIT(id % 32); } static void _phl_macid_map_clr(u32 *map, const u16 id) { int map_idx = (int)id / 32; map[map_idx] &= ~BIT(id % 32); } static void _phl_wrole_bcmc_id_set(struct macid_ctl_t *macid_ctl, struct rtw_wifi_role_t *wrole, const u16 id) { macid_ctl->wrole_bmc[wrole->id] = id; } static enum rtw_phl_status _phl_alloc_macid(struct phl_info_t *phl_info, struct rtw_phl_stainfo_t *phl_sta) { struct macid_ctl_t *mc = phl_to_mac_ctrl(phl_info); struct rtw_wifi_role_t *wrole = phl_sta->wrole; u8 bc_addr[MAC_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; u16 mid = 0; u16 max_macid_num = 0; bool bmc_sta = false; if (wrole == NULL) { PHL_ERR("%s wrole=NULL!\n", __func__); return RTW_PHL_STATUS_FAILURE; } if (_os_mem_cmp(phl_to_drvpriv(phl_info), bc_addr, phl_sta->mac_addr, MAC_ALEN) == 0) bmc_sta = true; /* TODO if (wrole->type == PHL_RTYPE_STATION) else if (wrole->type == PHL_RTYPE_AP)*/ /*TODO - struct mac_ax_hw_info-> u16 macid_num; need to check */ max_macid_num = mc->max_num; _os_spinlock(phl_to_drvpriv(phl_info), &mc->lock, _bh, NULL); for(mid = 0; mid < max_macid_num; mid++) { if (!_phl_macid_is_used(mc->used_map, mid)) { _phl_macid_map_set(mc->used_map, mid); _phl_macid_map_set(&mc->wifi_role_usedmap[wrole->id][0], mid); mc->sta[mid] = phl_sta; if (bmc_sta) { _phl_macid_map_set(mc->bmc_map, mid); _phl_wrole_bcmc_id_set(mc, wrole, mid); } break; } } _os_spinunlock(phl_to_drvpriv(phl_info), &mc->lock, _bh, NULL); if (mid == max_macid_num) { phl_sta->macid = max_macid_num; PHL_ERR("%s cannot get macid\n", __func__); return RTW_PHL_STATUS_FAILURE; } phl_sta->macid = mid; PHL_INFO("%s allocate %02x:%02x:%02x:%02x:%02x:%02x for macid:%u\n", __func__, phl_sta->mac_addr[0], phl_sta->mac_addr[1], phl_sta->mac_addr[2], phl_sta->mac_addr[3], phl_sta->mac_addr[4], phl_sta->mac_addr[5], phl_sta->macid); return RTW_PHL_STATUS_SUCCESS; } static enum rtw_phl_status _phl_release_macid(struct phl_info_t *phl_info, struct rtw_phl_stainfo_t *phl_sta) { struct macid_ctl_t *macid_ctl = phl_to_mac_ctrl(phl_info); enum rtw_phl_status phl_status = RTW_PHL_STATUS_FAILURE; struct rtw_wifi_role_t *wrole = phl_sta->wrole; u8 bc_addr[MAC_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; u16 invalid_macid = macid_ctl->max_num; if (phl_sta->macid >= invalid_macid) { PHL_ERR("_phl_release_macid macid error (%d\n)", phl_sta->macid); phl_status = RTW_PHL_STATUS_FAILURE; goto exit; } _os_spinlock(phl_to_drvpriv(phl_info), &macid_ctl->lock, _bh, NULL); if (!_phl_macid_is_used(macid_ctl->used_map, phl_sta->macid)) { PHL_WARN("_phl_release_macid macid unused (%d\n)", phl_sta->macid); _os_spinunlock(phl_to_drvpriv(phl_info), &macid_ctl->lock, _bh, NULL); phl_status = RTW_PHL_STATUS_FAILURE; goto exit; } _phl_macid_map_clr(macid_ctl->used_map, phl_sta->macid); _phl_macid_map_clr(&macid_ctl->wifi_role_usedmap[wrole->id][0], phl_sta->macid); macid_ctl->sta[phl_sta->macid] = NULL; if (_os_mem_cmp(phl_to_drvpriv(phl_info), bc_addr, phl_sta->mac_addr, MAC_ALEN) == 0) _phl_macid_map_clr(macid_ctl->bmc_map, phl_sta->macid); phl_status = RTW_PHL_STATUS_SUCCESS; _os_spinunlock(phl_to_drvpriv(phl_info), &macid_ctl->lock, _bh, NULL); exit: PHL_INFO("%s release macid:%d - %02x:%02x:%02x:%02x:%02x:%02x \n", __func__, phl_sta->macid, phl_sta->mac_addr[0], phl_sta->mac_addr[1], phl_sta->mac_addr[2], phl_sta->mac_addr[3], phl_sta->mac_addr[4], phl_sta->mac_addr[5]); phl_sta->macid = invalid_macid; return phl_status; } u16 _phl_get_macid(struct phl_info_t *phl_info, struct rtw_phl_stainfo_t *phl_sta) { /* TODO: macid management */ return phl_sta->macid; } /** * This function export to core layer use * to get phl role bmc macid * @phl: see phl_info_t * @wrole: wifi role */ u16 rtw_phl_wrole_bcmc_id_get(void *phl, struct rtw_wifi_role_t *wrole) { struct phl_info_t *phl_info = (struct phl_info_t *)phl; struct macid_ctl_t *macid_ctl = phl_to_mac_ctrl(phl_info); return macid_ctl->wrole_bmc[wrole->id]; } /** * This function export to core layer use * to get maximum macid number * @phl: see phl_info_t */ u16 rtw_phl_get_macid_max_num(void *phl) { struct phl_info_t *phl_info = (struct phl_info_t *)phl; struct macid_ctl_t *macid_ctl = phl_to_mac_ctrl(phl_info); return macid_ctl->max_num; } /** * This function export to core layer use * to check macid is bmc or not * @phl: see phl_info_t * @macid: macid */ u8 rtw_phl_macid_is_bmc(void *phl, u16 macid) { struct phl_info_t *phl_info = (struct phl_info_t *)phl; struct macid_ctl_t *macid_ctl = phl_to_mac_ctrl(phl_info); if (macid >= macid_ctl->max_num) { PHL_ERR("%s macid(%d) is invalid\n", __func__, macid); return true; } return _phl_macid_is_used(macid_ctl->bmc_map, macid); } /** * This function export to core layer use * to check macid is used or not * @phl: see phl_info_t * @macid: macid */ u8 rtw_phl_macid_is_used(void *phl, u16 macid) { struct phl_info_t *phl_info = (struct phl_info_t *)phl; struct macid_ctl_t *macid_ctl = phl_to_mac_ctrl(phl_info); if (macid >= macid_ctl->max_num) { PHL_ERR("%s macid(%d) is invalid\n", __func__, macid); return true; } return _phl_macid_is_used(macid_ctl->used_map, macid); } /** * This function is used to * check macid shared by all wifi role * @phl: see phl_info_t * @macid: macid */ u8 rtw_phl_macid_is_wrole_shared(void *phl, u16 macid) { struct phl_info_t *phl_info = (struct phl_info_t *)phl; struct macid_ctl_t *macid_ctl = phl_to_mac_ctrl(phl_info); int i = 0; u8 iface_bmp = 0; if (macid >= macid_ctl->max_num) { PHL_ERR("%s macid(%d) is invalid\n", __func__, macid); return false; } for (i = 0; i < MAX_WIFI_ROLE_NUMBER; i++) { if (_phl_macid_is_used(&macid_ctl->wifi_role_usedmap[i][0], macid)) { if (iface_bmp) return true; iface_bmp |= BIT(i); } } return false; } /** * This function is used to * check macid not shared by all wifi role * and belong to wifi role * @phl: see phl_info_t * @macid: macid * @wrole: check id belong to this wifi role */ u8 rtw_phl_macid_is_wrole_specific(void *phl, u16 macid, struct rtw_wifi_role_t *wrole) { struct phl_info_t *phl_info = (struct phl_info_t *)phl; struct macid_ctl_t *macid_ctl = phl_to_mac_ctrl(phl_info); int i = 0; u8 iface_bmp = 0; if (macid >= macid_ctl->max_num) { PHL_ERR("%s macid(%d) invalid\n", __func__, macid); return false; } for (i = 0; i < MAX_WIFI_ROLE_NUMBER; i++) { if (_phl_macid_is_used(&macid_ctl->wifi_role_usedmap[i][0], macid)) { if (iface_bmp || i != wrole->id) return false; iface_bmp |= BIT(i); } } return iface_bmp ? true : false; } /*********** stainfo_ctrl section ***********/ static enum rtw_phl_status _phl_stainfo_init(struct phl_info_t *phl_info, struct rtw_phl_stainfo_t *phl_sta) { void *drv = phl_to_drvpriv(phl_info); INIT_LIST_HEAD(&phl_sta->list); _os_spinlock_init(drv, &phl_sta->tid_rx_lock); _os_mem_set(drv, phl_sta->tid_rx, 0, sizeof(phl_sta->tid_rx)); _os_event_init(drv, &phl_sta->comp_sync); _os_init_timer(drv, &phl_sta->reorder_timer, phl_sta_rx_reorder_timer_expired, phl_sta, "reorder_timer"); _os_atomic_set(drv, &phl_sta->ps_sta, 0); if (rtw_hal_stainfo_init(phl_info->hal, phl_sta) != RTW_HAL_STATUS_SUCCESS) { PHL_ERR("hal_stainfo_init failed\n"); FUNCOUT(); return RTW_PHL_STATUS_FAILURE; } phl_sta->active = false; return RTW_PHL_STATUS_SUCCESS; } enum rtw_phl_status _phl_stainfo_deinit(struct phl_info_t *phl_info, struct rtw_phl_stainfo_t *phl_sta) { void *drv = phl_to_drvpriv(phl_info); _os_release_timer(drv, &phl_sta->reorder_timer); _os_spinlock_free(phl_to_drvpriv(phl_info), &phl_sta->tid_rx_lock); _os_event_free(drv, &phl_sta->comp_sync); if (rtw_hal_stainfo_deinit(phl_info->hal, phl_sta)!= RTW_HAL_STATUS_SUCCESS) { PHL_ERR("hal_stainfo_deinit failed\n"); FUNCOUT(); return RTW_PHL_STATUS_FAILURE; } return RTW_PHL_STATUS_SUCCESS; } enum rtw_phl_status phl_stainfo_enqueue(struct phl_info_t *phl_info, struct phl_queue *sta_queue, struct rtw_phl_stainfo_t *psta) { void *drv = phl_to_drvpriv(phl_info); if (!psta) return RTW_PHL_STATUS_FAILURE; _os_spinlock(drv, &sta_queue->lock, _bh, NULL); list_add_tail(&psta->list, &sta_queue->queue); sta_queue->cnt++; _os_spinunlock(drv, &sta_queue->lock, _bh, NULL); return RTW_PHL_STATUS_SUCCESS; } struct rtw_phl_stainfo_t * phl_stainfo_dequeue(struct phl_info_t *phl_info, struct phl_queue *sta_queue) { struct rtw_phl_stainfo_t *psta = NULL; void *drv = phl_to_drvpriv(phl_info); _os_spinlock(drv, &sta_queue->lock, _bh, NULL); if (list_empty(&sta_queue->queue)) { psta = NULL; } else { psta = list_first_entry(&sta_queue->queue, struct rtw_phl_stainfo_t, list); list_del(&psta->list); sta_queue->cnt--; } _os_spinunlock(drv, &sta_queue->lock, _bh, NULL); return psta; } enum rtw_phl_status phl_stainfo_queue_del(struct phl_info_t *phl_info, struct phl_queue *sta_queue, struct rtw_phl_stainfo_t *psta) { void *drv = phl_to_drvpriv(phl_info); if (!psta) return RTW_PHL_STATUS_FAILURE; _os_spinlock(drv, &sta_queue->lock, _bh, NULL); if (sta_queue->cnt) { list_del(&psta->list); sta_queue->cnt--; } _os_spinunlock(drv, &sta_queue->lock, _bh, NULL); return RTW_PHL_STATUS_SUCCESS; } struct rtw_phl_stainfo_t * phl_stainfo_queue_search(struct phl_info_t *phl_info, struct phl_queue *sta_queue, u8 *addr) { struct rtw_phl_stainfo_t *sta = NULL; _os_list *sta_list = &sta_queue->queue; void *drv = phl_to_drvpriv(phl_info); bool sta_found = false; _os_spinlock(drv, &sta_queue->lock, _bh, NULL); if (list_empty(sta_list) == true) goto _exit; phl_list_for_loop(sta, struct rtw_phl_stainfo_t, sta_list, list) { if (_os_mem_cmp(phl_to_drvpriv(phl_info), sta->mac_addr, addr, MAC_ALEN) == 0) { sta_found = true; break; } } _exit: _os_spinunlock(drv, &sta_queue->lock, _bh, NULL); if (sta_found == false) sta = NULL; return sta; } struct rtw_phl_stainfo_t * phl_stainfo_queue_get_first(struct phl_info_t *phl_info, struct phl_queue *sta_queue) { _os_list *sta_list = &sta_queue->queue; void *drv = phl_to_drvpriv(phl_info); struct rtw_phl_stainfo_t *sta = NULL; /* first sta info in assoc_sta_queu is self sta info */ _os_spinlock(drv, &sta_queue->lock, _bh, NULL); if (list_empty(sta_list) == true) goto _exit; sta = list_first_entry(sta_list, struct rtw_phl_stainfo_t, list); _exit : _os_spinunlock(drv, &sta_queue->lock, _bh, NULL); return sta; } enum rtw_phl_status phl_stainfo_ctrl_deinie(struct phl_info_t *phl_info) { struct stainfo_ctl_t *sta_ctrl = phl_to_sta_ctrl(phl_info); void *drv = phl_to_drvpriv(phl_info); struct rtw_phl_stainfo_t *psta = NULL; struct phl_queue *fsta_queue = &sta_ctrl->free_sta_queue; FUNCIN(); do { psta = phl_stainfo_dequeue(phl_info, fsta_queue); if (psta) _phl_stainfo_deinit(phl_info, psta); }while (psta != NULL); pq_deinit(drv, fsta_queue); if (sta_ctrl->allocated_stainfo_buf) _os_mem_free(drv, sta_ctrl->allocated_stainfo_buf, sta_ctrl->allocated_stainfo_sz); FUNCOUT(); return RTW_PHL_STATUS_SUCCESS; } enum rtw_phl_status phl_stainfo_ctrl_init(struct phl_info_t *phl_info) { struct stainfo_ctl_t *sta_ctrl = phl_to_sta_ctrl(phl_info); void *drv = phl_to_drvpriv(phl_info); struct rtw_phl_stainfo_t *psta = NULL; enum rtw_phl_status pstatus = RTW_PHL_STATUS_FAILURE; struct phl_queue *fsta_queue = NULL; u16 i; bool sta_init_fail = false; FUNCIN(); sta_ctrl->phl_info = phl_info; sta_ctrl->allocated_stainfo_sz = sizeof(struct rtw_phl_stainfo_t) * PHL_MAX_STA_NUM; #ifdef MEM_ALIGNMENT sta_ctrl->allocated_stainfo_sz += MEM_ALIGNMENT_OFFSET; #endif sta_ctrl->allocated_stainfo_buf = _os_mem_alloc(drv, sta_ctrl->allocated_stainfo_sz); if (!sta_ctrl->allocated_stainfo_buf) { PHL_ERR("allocate stainfo buf failed\n"); goto _exit; } sta_ctrl->stainfo_buf = sta_ctrl->allocated_stainfo_buf; #ifdef MEM_ALIGNMENT if (sta_ctrl->stainfo_buf & MEM_ALIGNMENT_PADDING) sta_ctrl->stainfo_buf += MEM_ALIGNMENT_OFFSET - (sta_ctrl->stainfo_buf & MEM_ALIGNMENT_PADDING); #endif fsta_queue = &sta_ctrl->free_sta_queue; pq_init(drv, fsta_queue); psta = (struct rtw_phl_stainfo_t *)(sta_ctrl->stainfo_buf); for (i = 0; i < PHL_MAX_STA_NUM; i++) { if (_phl_stainfo_init(phl_info, psta) != RTW_PHL_STATUS_SUCCESS) { sta_init_fail = true; break; } phl_stainfo_enqueue(phl_info, fsta_queue, psta); psta++; } if (sta_init_fail == true) { PHL_ERR("sta_init failed\n"); phl_stainfo_ctrl_deinie(phl_info); goto _exit; } PHL_DUMP_STACTRL_EX(phl_info); pstatus = RTW_PHL_STATUS_SUCCESS; _exit: FUNCOUT(); return pstatus; } /*********** phl stainfo section ***********/ #ifdef DBG_PHL_STAINFO void phl_dump_stactrl(const char *caller, const int line, bool show_caller, struct phl_info_t *phl_info) { struct rtw_phl_com_t *phl_com = phl_info->phl_com; u8 ridx = MAX_WIFI_ROLE_NUMBER; struct rtw_wifi_role_t *role; struct stainfo_ctl_t *sta_ctrl = NULL; sta_ctrl = phl_to_sta_ctrl(phl_info); if (show_caller) PHL_INFO("[PSTA] ###### FUN - %s LINE - %d #######\n", caller, line); PHL_INFO("[PSTA] PHL_MAX_STA_NUM:%d\n", PHL_MAX_STA_NUM); PHL_INFO("[PSTA] sta_ctrl - q_cnt :%d\n", sta_ctrl->free_sta_queue.cnt); for (ridx = 0; ridx < MAX_WIFI_ROLE_NUMBER; ridx++) { role = &(phl_com->wifi_roles[ridx]); PHL_INFO("[PSTA] wrole_%d asoc_q cnt :%d\n", ridx, role->assoc_sta_queue.cnt); } if (show_caller) PHL_INFO("#################################\n"); } static void _phl_dump_stainfo(struct rtw_phl_stainfo_t *phl_sta) { PHL_INFO("\t[STA] MAC-ID:%d, AID:%d, MAC-ADDR:%02x-%02x-%02x-%02x-%02x-%02x, Active:%s\n", phl_sta->macid, phl_sta->aid, phl_sta->mac_addr[0],phl_sta->mac_addr[1],phl_sta->mac_addr[2], phl_sta->mac_addr[3],phl_sta->mac_addr[4],phl_sta->mac_addr[5], (phl_sta->active) ? "Y" : "N"); PHL_INFO("\t[STA] WROLE-IDX:%d wlan_mode:0x%02x\n", phl_sta->wrole->id, phl_sta->wmode); PHL_DUMP_CHAN_DEF(&phl_sta->chandef); /****** statistic ******/ PHL_INFO("\t[STA] TP -[Tx:%d Rx :%d BI:N/A] (KBits)\n", phl_sta->stats.tx_tp_kbits, phl_sta->stats.rx_tp_kbits); PHL_INFO("\t[STA] Total -[Tx:%llu Rx :%llu BI:N/A] (Bytes)\n", phl_sta->stats.tx_byte_total, phl_sta->stats.rx_byte_total); /****** asoc_cap ******/ /****** protect ******/ /****** sec_mode ******/ /****** rssi_stat ******/ PHL_INFO("\t\t[HAL STA] rssi:%d assoc_rssi:%d, ofdm:%d, cck:%d, rssi_ma:%d, ma_rssi:%d\n", (phl_sta->hal_sta->rssi_stat.rssi >> 1), phl_sta->hal_sta->rssi_stat.assoc_rssi, (phl_sta->hal_sta->rssi_stat.rssi_ofdm >> 1), (phl_sta->hal_sta->rssi_stat.rssi_cck >> 1), (phl_sta->hal_sta->rssi_stat.rssi_ma >> 5), phl_sta->hal_sta->rssi_stat.ma_rssi); /****** ra_info ******/ PHL_INFO("\t\t[HAL STA] - RA info\n"); PHL_INFO("\t\t[HAL STA] Tx rate:0x%04x ra_bw_mode:%d, curr_tx_bw:%d\n", phl_sta->hal_sta->ra_info.curr_tx_rate, phl_sta->hal_sta->ra_info.ra_bw_mode, phl_sta->hal_sta->ra_info.curr_tx_bw); PHL_INFO("\t\t[HAL STA] dis_ra:%s ra_registered:%s\n", (phl_sta->hal_sta->ra_info.dis_ra) ? "Y" : "N", (phl_sta->hal_sta->ra_info.ra_registered) ? "Y" : "N"); PHL_INFO("\t\t[HAL STA] ra_mask:0x%08llx cur_ra_mask:0x%08llx, retry_ratio:%d\n", phl_sta->hal_sta->ra_info.ra_mask, phl_sta->hal_sta->ra_info.cur_ra_mask, phl_sta->hal_sta->ra_info.curr_retry_ratio); /****** ra_info - Report ******/ PHL_INFO("\t\t[HAL STA] RA Report: gi_ltf:%d rate_mode:%d, bw:%d, mcs_ss_idx:%d\n", phl_sta->hal_sta->ra_info.rpt_rt_i.gi_ltf, phl_sta->hal_sta->ra_info.rpt_rt_i.mode, phl_sta->hal_sta->ra_info.rpt_rt_i.bw, phl_sta->hal_sta->ra_info.rpt_rt_i.mcs_ss_idx); PHL_INFO("\t\t[HAL STA] HAL rx_ok_cnt:%d rx_err_cnt:%d, rx_rate_plurality:%d\n\n", phl_sta->hal_sta->trx_stat.rx_ok_cnt, phl_sta->hal_sta->trx_stat.rx_err_cnt, phl_sta->hal_sta->trx_stat.rx_rate_plurality); } void phl_dump_stainfo_all(const char *caller, const int line, bool show_caller, struct phl_info_t *phl_info) { struct macid_ctl_t *macid_ctl = phl_to_mac_ctrl(phl_info); struct rtw_phl_stainfo_t *phl_sta = NULL; u16 max_macid_num = 0; u16 mid = 0; if (show_caller) PHL_INFO("###### FUN - %s LINE - %d #######\n", caller, line); max_macid_num = macid_ctl->max_num; PHL_INFO("max_macid_num:%d\n", max_macid_num); _os_spinlock(phl_to_drvpriv(phl_info), &macid_ctl->lock, _bh, NULL); for(mid = 0; mid < max_macid_num; mid++) { if (_phl_macid_is_used(macid_ctl->used_map, mid)) { phl_sta = macid_ctl->sta[mid]; if (phl_sta) _phl_dump_stainfo(phl_sta); } } _os_spinunlock(phl_to_drvpriv(phl_info), &macid_ctl->lock, _bh, NULL); if (show_caller) PHL_INFO("#################################\n"); } const char *const _rtype_str[] = { "NONE", "STA", "AP", "VAP", "ADHOC", "MASTER", "MESH", "MONITOR", "PD", "GC", "GO", "TDLS", "NAN", "NONE" }; void phl_dump_stainfo_per_role(const char *caller, const int line, bool show_caller, struct phl_info_t *phl_info, struct rtw_wifi_role_t *wrole) { void *drv = phl_to_drvpriv(phl_info); struct rtw_phl_stainfo_t *sta = NULL; int sta_cnt = 0; if (show_caller) PHL_INFO("[STA] ###### FUN - %s LINE - %d #######\n", caller, line); PHL_INFO("WR_IDX:%d RTYPE:%s, mac-addr:%02x-%02x-%02x-%02x-%02x-%02x\n", wrole->id, _rtype_str[wrole->type], wrole->mac_addr[0], wrole->mac_addr[1], wrole->mac_addr[2], wrole->mac_addr[3], wrole->mac_addr[4], wrole->mac_addr[5]); _os_spinlock(drv, &wrole->assoc_sta_queue.lock, _bh, NULL); if (wrole->type == PHL_RTYPE_STATION && wrole->mstate == MLME_LINKED) sta_cnt = 1; else if (wrole->type == PHL_RTYPE_TDLS) sta_cnt = wrole->assoc_sta_queue.cnt; else sta_cnt = wrole->assoc_sta_queue.cnt - 1; PHL_INFO("assoced STA num: %d\n", sta_cnt); phl_list_for_loop(sta, struct rtw_phl_stainfo_t, &wrole->assoc_sta_queue.queue, list) { if (sta) _phl_dump_stainfo(sta); } _os_spinunlock(drv, &wrole->assoc_sta_queue.lock, _bh, NULL); if (show_caller) PHL_INFO("#################################\n"); } void rtw_phl_sta_dump_info(void *phl, bool show_caller, struct rtw_wifi_role_t *wr, u8 mode) { struct phl_info_t *phl_info = (struct phl_info_t *)phl; if (mode == 1) { if (show_caller) { PHL_DUMP_STACTRL_EX(phl_info); } else { PHL_DUMP_STACTRL(phl_info); } } else if (mode == 2) { if (show_caller) { PHL_DUMP_STAINFO_EX(phl_info); } else { PHL_DUMP_STAINFO(phl_info); } } else if (mode == 3) { if (show_caller) { PHL_DUMP_ROLE_STAINFO_EX(phl_info, wr); } else { PHL_DUMP_ROLE_STAINFO(phl_info, wr); } } else { if (show_caller) { PHL_DUMP_STACTRL_EX(phl_info); PHL_DUMP_STAINFO_EX(phl_info); PHL_DUMP_ROLE_STAINFO_EX(phl_info, wr); } else { PHL_DUMP_STACTRL(phl_info); PHL_DUMP_STAINFO(phl_info); PHL_DUMP_ROLE_STAINFO(phl_info, wr); } } } #endif /*DBG_PHL_STAINFO*/ static bool _phl_self_stainfo_chk(struct phl_info_t *phl_info, struct rtw_wifi_role_t *wrole, struct rtw_phl_stainfo_t *sta) { void *drv = phl_to_drvpriv(phl_info); bool is_self = false; switch (wrole->type) { case PHL_RTYPE_STATION: case PHL_RTYPE_P2P_GC: _os_mem_cpy(drv, sta->mac_addr, wrole->mac_addr, MAC_ALEN); is_self = true; break; case PHL_RTYPE_AP: case PHL_RTYPE_MESH: case PHL_RTYPE_P2P_GO: case PHL_RTYPE_TDLS: if (_os_mem_cmp(drv, wrole->mac_addr, sta->mac_addr, MAC_ALEN) == 0) is_self = true; break; case PHL_RTYPE_NONE: case PHL_RTYPE_VAP: case PHL_RTYPE_ADHOC: case PHL_RTYPE_ADHOC_MASTER: case PHL_RTYPE_MONITOR: case PHL_RTYPE_P2P_DEVICE: case PHL_RTYPE_NAN: case PHL_MLME_MAX: PHL_TRACE(COMP_PHL_DBG, _PHL_ERR_, "_phl_self_stainfo_chk(): Unsupported case:%d, please check it\n", wrole->type); break; default: PHL_TRACE(COMP_PHL_DBG, _PHL_ERR_, "_phl_self_stainfo_chk(): role-type(%d) not recognize\n", wrole->type); break; } return is_self; } enum rtw_phl_status phl_free_stainfo_sw(struct phl_info_t *phl_info, struct rtw_phl_stainfo_t *sta) { enum rtw_phl_status pstatus = RTW_PHL_STATUS_FAILURE; if(sta == NULL) { PHL_ERR("%s sta is NULL\n", __func__); return RTW_PHL_STATUS_FAILURE; } phl_free_rx_reorder(phl_info, sta); pstatus = phl_deregister_tx_ring((void *)phl_info, sta->macid); if (pstatus != RTW_PHL_STATUS_SUCCESS) { PHL_ERR("macid(%d) phl_deregister_tx_ring failed\n", sta->macid); } /* release macid for used_map */ pstatus = _phl_release_macid(phl_info, sta); if (pstatus != RTW_PHL_STATUS_SUCCESS) PHL_ERR("_phl_release_macid failed\n"); return pstatus; } enum rtw_phl_status __phl_free_stainfo_sw(struct phl_info_t *phl_info, struct rtw_phl_stainfo_t *sta) { enum rtw_phl_status pstatus = RTW_PHL_STATUS_FAILURE; struct stainfo_ctl_t *sta_ctrl = phl_to_sta_ctrl(phl_info); struct rtw_wifi_role_t *wrole = NULL; FUNCIN(); if(sta == NULL) { PHL_ERR("%s sta is NULL\n", __func__); goto _exit; } wrole = sta->wrole; if (!is_broadcast_mac_addr(sta->mac_addr)) { if (_phl_self_stainfo_chk(phl_info, wrole, sta) == true) { pstatus = RTW_PHL_STATUS_SUCCESS; goto _exit; } } pstatus = phl_stainfo_queue_del(phl_info, &wrole->assoc_sta_queue, sta); if (pstatus != RTW_PHL_STATUS_SUCCESS) { PHL_ERR("phl_stainfo_queue_del failed\n"); } pstatus = phl_free_stainfo_sw(phl_info, sta); if (pstatus != RTW_PHL_STATUS_SUCCESS) { PHL_ERR("macid(%d) _phl_free_stainfo_sw failed\n", sta->macid); } pstatus = phl_stainfo_enqueue(phl_info, &sta_ctrl->free_sta_queue, sta); if (pstatus != RTW_PHL_STATUS_SUCCESS) PHL_ERR("phl_stainfo_enqueue to free queue failed\n"); #ifdef RTW_WKARD_AP_CLIENT_ADD_DEL_NTY if ((wrole->type == PHL_RTYPE_AP) || (wrole->type == PHL_RTYPE_VAP) || (wrole->type == PHL_RTYPE_MESH) || (wrole->type == PHL_RTYPE_P2P_GO)) phl_role_ap_client_notify(phl_info, wrole, MLME_NO_LINK); #endif _exit: PHL_DUMP_STACTRL_EX(phl_info); FUNCOUT(); return pstatus; } enum rtw_phl_status rtw_phl_free_stainfo_sw(void *phl, struct rtw_phl_stainfo_t *sta) { return __phl_free_stainfo_sw((struct phl_info_t *)phl, sta); } enum rtw_phl_status phl_free_stainfo_hw(struct phl_info_t *phl_info, struct rtw_phl_stainfo_t *sta) { enum rtw_phl_status pstatus = RTW_PHL_STATUS_FAILURE; if (sta == NULL) { PHL_ERR("%s sta == NULL\n", __func__); goto _exit; } phl_pkt_ofld_del_entry(phl_info, sta->macid); sta->active = false; if (rtw_hal_del_sta_entry(phl_info->hal, sta) == RTW_HAL_STATUS_SUCCESS) pstatus = RTW_PHL_STATUS_SUCCESS; else PHL_ERR("rtw_hal_del_sta_entry failed\n"); _exit: return pstatus; } enum rtw_phl_status __phl_free_stainfo_hw(struct phl_info_t *phl_info, struct rtw_phl_stainfo_t *sta) { struct rtw_wifi_role_t *wrole = sta->wrole; if (!is_broadcast_mac_addr(sta->mac_addr)) { if (_phl_self_stainfo_chk(phl_info, wrole, sta) == true) return RTW_PHL_STATUS_SUCCESS; } return phl_free_stainfo_hw(phl_info, sta); } static enum rtw_phl_status __phl_free_stainfo(struct phl_info_t *phl, struct rtw_phl_stainfo_t *sta) { enum rtw_phl_status pstatus = RTW_PHL_STATUS_FAILURE; pstatus = __phl_free_stainfo_hw(phl, sta); if (pstatus != RTW_PHL_STATUS_SUCCESS) PHL_ERR("__phl_free_stainfo_hw failed\n"); pstatus = __phl_free_stainfo_sw(phl, sta); if (pstatus != RTW_PHL_STATUS_SUCCESS) PHL_ERR("__phl_free_stainfo_sw failed\n"); return pstatus; } static enum rtw_phl_status _phl_alloc_stainfo_sw(struct phl_info_t *phl_info,struct rtw_phl_stainfo_t *sta) { enum rtw_phl_status pstatus = RTW_PHL_STATUS_FAILURE; pstatus = _phl_alloc_macid(phl_info, sta); if (pstatus != RTW_PHL_STATUS_SUCCESS) { PHL_ERR("%s allocate macid failure!\n", __func__); goto error_alloc_macid; } pstatus = phl_register_tx_ring(phl_info, sta->macid, sta->wrole->hw_band, sta->wrole->hw_wmm, sta->wrole->hw_port); if (pstatus != RTW_PHL_STATUS_SUCCESS) { PHL_ERR("%s register_tx_ring failure!\n", __func__); goto error_register_tx_ring; } pstatus = RTW_PHL_STATUS_SUCCESS; return pstatus; error_register_tx_ring: _phl_release_macid(phl_info, sta); error_alloc_macid: return pstatus; } static void _phl_sta_set_default_value(struct phl_info_t *phl_info, struct rtw_phl_stainfo_t *phl_sta) { phl_sta->bcn_hit_cond = 0; /* beacon:A3 probersp: A1 & A3 */ /* fit rule * 0: A1 & A2 * 1: A1 & A3 * * Rule 0 should be used for both AP and STA modes. * * For STA, A3 is source address(SA) which can be any peer on the LAN. * * For AP, A3 is destination address(DA) which can also be any node * on the LAN. A1 & A2 match find the address CAM entry that contains the * correct security CAM ID and MAC ID. */ phl_sta->hit_rule = 0; } struct rtw_phl_stainfo_t * phl_alloc_stainfo_sw(struct phl_info_t *phl_info, u8 *sta_addr, struct rtw_wifi_role_t *wrole) { struct stainfo_ctl_t *sta_ctrl = phl_to_sta_ctrl(phl_info); struct rtw_phl_stainfo_t *phl_sta = NULL; void *drv = phl_to_drvpriv(phl_info); bool bmc_sta = false; FUNCIN(); if (is_broadcast_mac_addr(sta_addr)) bmc_sta = true; /* if sta_addr is bmc addr, allocate new sta_info */ if (wrole->type == PHL_RTYPE_STATION && !bmc_sta) { phl_sta = rtw_phl_get_stainfo_self(phl_info, wrole); if (phl_sta) { _os_mem_cpy(drv, phl_sta->mac_addr, sta_addr, MAC_ALEN); goto _exit; } } /* check station info exist */ phl_sta = rtw_phl_get_stainfo_by_addr(phl_info, wrole, sta_addr); if (phl_sta) { PHL_INFO("%s phl_sta(%02x:%02x:%02x:%02x:%02x:%02x) exist\n", __func__, sta_addr[0], sta_addr[1], sta_addr[2], sta_addr[3], sta_addr[4], sta_addr[5]); goto _exit; } phl_sta = phl_stainfo_dequeue(phl_info, &sta_ctrl->free_sta_queue); if (phl_sta == NULL) { PHL_ERR("allocate phl_sta failure!\n"); goto _exit; } _os_mem_cpy(drv, phl_sta->mac_addr, sta_addr, MAC_ALEN); phl_sta->wrole = wrole; if (_phl_alloc_stainfo_sw(phl_info, phl_sta) != RTW_PHL_STATUS_SUCCESS) { PHL_ERR("_phl_alloc_stainfo_sw failed\n"); goto error_alloc_sta; } _phl_sta_set_default_value(phl_info, phl_sta); phl_stainfo_enqueue(phl_info, &wrole->assoc_sta_queue, phl_sta); #ifdef RTW_WKARD_AP_CLIENT_ADD_DEL_NTY if (_phl_self_stainfo_chk(phl_info, wrole, phl_sta) == false) { if ((wrole->type == PHL_RTYPE_AP) || (wrole->type == PHL_RTYPE_VAP) || (wrole->type == PHL_RTYPE_MESH) || (wrole->type == PHL_RTYPE_P2P_GO)) { phl_role_ap_client_notify(phl_info, wrole, MLME_LINKING); } } #endif _exit: PHL_DUMP_STACTRL_EX(phl_info); FUNCOUT(); return phl_sta; error_alloc_sta: phl_stainfo_enqueue(phl_info, &sta_ctrl->free_sta_queue, phl_sta); phl_sta = NULL; PHL_DUMP_STACTRL_EX(phl_info); FUNCOUT(); return phl_sta; } struct rtw_phl_stainfo_t * rtw_phl_alloc_stainfo_sw(void *phl, u8 *sta_addr, struct rtw_wifi_role_t *wrole) { return phl_alloc_stainfo_sw((struct phl_info_t *)phl, sta_addr, wrole); } enum rtw_phl_status phl_alloc_stainfo_hw(struct phl_info_t *phl_info, struct rtw_phl_stainfo_t *sta) { enum rtw_phl_status pstatus = RTW_PHL_STATUS_FAILURE; if (sta == NULL) { PHL_ERR("%s sta == NULL\n", __func__); goto _exit; } if (rtw_hal_add_sta_entry(phl_info->hal, sta) != RTW_HAL_STATUS_SUCCESS) { PHL_ERR("%s rtw_hal_add_sta_entry failure!\n", __func__); goto _exit; } sta->active = true; pstatus = phl_pkt_ofld_add_entry(phl_info, sta->macid); if (RTW_PHL_STATUS_SUCCESS != pstatus) PHL_ERR("%s phl_pkt_ofld_add_entry failure!\n", __func__); _exit: return pstatus; } enum rtw_phl_status __phl_alloc_stainfo_hw(struct phl_info_t *phl_info, struct rtw_phl_stainfo_t *sta) { return phl_alloc_stainfo_hw(phl_info, sta); } static enum rtw_phl_status __phl_alloc_stainfo(struct phl_info_t *phl, struct rtw_phl_stainfo_t **sta, u8 *sta_addr, struct rtw_wifi_role_t *wrole) { struct rtw_phl_stainfo_t *alloc_sta = NULL; enum rtw_phl_status pstatus = RTW_PHL_STATUS_FAILURE; alloc_sta = phl_alloc_stainfo_sw(phl, sta_addr, wrole); if (alloc_sta == NULL) { PHL_ERR("%s can't alloc stainfo\n", __func__); *sta = alloc_sta; goto _exit; } if (alloc_sta->active == false) { pstatus = __phl_alloc_stainfo_hw(phl, alloc_sta); if (pstatus != RTW_PHL_STATUS_SUCCESS) { PHL_ERR("__phl_alloc_stainfo_hw failed\n"); goto _err_alloc_sta_hw; } } PHL_INFO("%s success - macid:%u %02x:%02x:%02x:%02x:%02x:%02x\n", __func__, alloc_sta->macid, alloc_sta->mac_addr[0], alloc_sta->mac_addr[1], alloc_sta->mac_addr[2], alloc_sta->mac_addr[3], alloc_sta->mac_addr[4], alloc_sta->mac_addr[5]); *sta = alloc_sta; return RTW_PHL_STATUS_SUCCESS; _err_alloc_sta_hw: __phl_free_stainfo_sw(phl, alloc_sta); *sta = alloc_sta = NULL; _exit: return RTW_PHL_STATUS_FAILURE; } static enum rtw_phl_status _phl_alloc_stainfo(struct phl_info_t *phl, struct rtw_phl_stainfo_t **sta, u8 *sta_addr, struct rtw_wifi_role_t *wrole, bool alloc, bool only_hw) { enum rtw_phl_status pstatus = RTW_PHL_STATUS_FAILURE; if (alloc) { if (only_hw) pstatus = __phl_alloc_stainfo_hw(phl, *sta); else pstatus = __phl_alloc_stainfo(phl, sta, sta_addr, wrole); } else { if (only_hw) pstatus = __phl_free_stainfo_hw(phl, *sta); else pstatus = __phl_free_stainfo(phl, *sta); } return pstatus; } #ifdef CONFIG_CMD_DISP struct cmd_stainfo_param { struct rtw_phl_stainfo_t **sta; u8 sta_addr[MAC_ALEN]; struct rtw_wifi_role_t *wrole; bool alloc; bool only_hw; }; static void _phl_cmd_alloc_stainfo_done(void *drv_priv, u8 *cmd, u32 cmd_len, enum rtw_phl_status status) { if (cmd) _os_kmem_free(drv_priv, cmd, cmd_len); } static enum rtw_phl_status _phl_cmd_alloc_stainfo(struct phl_info_t *phl_info, struct rtw_phl_stainfo_t **sta, u8 *sta_addr, struct rtw_wifi_role_t *wrole, bool alloc, bool only_hw, enum phl_cmd_type cmd_type, u32 cmd_timeout) { void *drv = phl_to_drvpriv(phl_info); enum rtw_phl_status psts = RTW_PHL_STATUS_FAILURE; struct cmd_stainfo_param *param = NULL; u32 param_len = 0; if (cmd_type == PHL_CMD_DIRECTLY) { psts = _phl_alloc_stainfo(phl_info, sta, sta_addr, wrole, alloc, only_hw); goto _exit; } param_len = sizeof(struct cmd_stainfo_param); param = _os_kmem_alloc(drv, param_len); if (param == NULL) { PHL_ERR("%s: alloc param failed!\n", __func__); psts = RTW_PHL_STATUS_RESOURCE; goto _exit; } _os_mem_set(drv, param, 0, param_len); param->sta = sta; _os_mem_cpy(drv, param->sta_addr, sta_addr, MAC_ALEN); param->wrole = wrole; param->alloc = alloc; param->only_hw = only_hw; psts = phl_cmd_enqueue(phl_info, wrole->hw_band, MSG_EVT_STA_INFO_CTRL, (u8 *)param, param_len, _phl_cmd_alloc_stainfo_done, cmd_type, cmd_timeout); 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_kmem_free(drv, param, param_len); } _exit: return psts; } enum rtw_phl_status phl_cmd_alloc_stainfo_hdl(struct phl_info_t *phl_info, u8 *param) { struct cmd_stainfo_param *cmd_sta_param = (struct cmd_stainfo_param *)param; return _phl_alloc_stainfo(phl_info, cmd_sta_param->sta, cmd_sta_param->sta_addr, cmd_sta_param->wrole, cmd_sta_param->alloc, cmd_sta_param->only_hw); } #endif /* CONFIG_CMD_DISP */ enum rtw_phl_status rtw_phl_cmd_alloc_stainfo(void *phl, struct rtw_phl_stainfo_t **sta, u8 *sta_addr, struct rtw_wifi_role_t *wrole, bool alloc, bool only_hw, enum phl_cmd_type cmd_type, u32 cmd_timeout) { #ifdef CONFIG_CMD_DISP return _phl_cmd_alloc_stainfo(phl, sta, sta_addr, wrole, alloc, only_hw, cmd_type, cmd_timeout); #else PHL_TRACE(COMP_PHL_DBG, _PHL_INFO_, "%s: not support alloc stainfo cmd\n", __func__); return _phl_alloc_stainfo((struct phl_info_t *)phl, sta, sta_addr, wrole, alloc, only_hw); #endif /* CONFIG_CMD_DISP */ } enum rtw_phl_status phl_wifi_role_free_stainfo_hw(struct phl_info_t *phl_info, struct rtw_wifi_role_t *wrole) { struct macid_ctl_t *mc = phl_to_mac_ctrl(phl_info); u16 max_macid_num = mc->max_num; struct rtw_phl_stainfo_t *sta = NULL; u32 *used_map; u16 mid; used_map = &mc->wifi_role_usedmap[wrole->id][0]; for(mid = 0; mid < max_macid_num; mid++) { if (_phl_macid_is_used(used_map, mid)) { sta = mc->sta[mid]; if (sta) { PHL_INFO("%s [WR-%d] free sta_info(MID:%d)\n", __func__, wrole->id, sta->macid); phl_free_stainfo_hw(phl_info, sta); } } } return RTW_PHL_STATUS_SUCCESS; } enum rtw_phl_status phl_wifi_role_free_stainfo_sw(struct phl_info_t *phl_info, struct rtw_wifi_role_t *role) { struct rtw_phl_stainfo_t *phl_sta = NULL; struct stainfo_ctl_t *sta_ctrl = phl_to_sta_ctrl(phl_info); PHL_DUMP_STACTRL_EX(phl_info); do { phl_sta = phl_stainfo_dequeue(phl_info, &role->assoc_sta_queue); if (phl_sta) { phl_free_stainfo_sw(phl_info, phl_sta); phl_stainfo_enqueue(phl_info, &sta_ctrl->free_sta_queue, phl_sta); } } while(phl_sta != NULL); return RTW_PHL_STATUS_SUCCESS; } enum rtw_phl_status phl_wifi_role_free_stainfo(struct phl_info_t *phl_info, struct rtw_wifi_role_t *role) { struct rtw_phl_stainfo_t *phl_sta = NULL; struct stainfo_ctl_t *sta_ctrl = phl_to_sta_ctrl(phl_info); PHL_DUMP_STACTRL_EX(phl_info); do { phl_sta = phl_stainfo_dequeue(phl_info, &role->assoc_sta_queue); if (phl_sta) { phl_free_stainfo_hw(phl_info, phl_sta); phl_free_stainfo_sw(phl_info, phl_sta); phl_stainfo_enqueue(phl_info, &sta_ctrl->free_sta_queue, phl_sta); } } while(phl_sta != NULL); return RTW_PHL_STATUS_SUCCESS; } /** * According to 802.11 spec 26.5.2.3.2 * We shall not transmit HE TB PPDU with RU-26 on DFS channel */ static void _phl_set_dfs_tb_ctrl(struct phl_info_t *phl_info, struct rtw_wifi_role_t *wrole) { struct rtw_regulation_channel reg_ch = {0}; enum band_type band = wrole->chandef.band; u8 channel = wrole->chandef.chan; bool is_dfs = false; if (rtw_phl_regulation_query_ch(phl_info, band, channel, ®_ch)) { if (reg_ch.property & CH_DFS) is_dfs = true; rtw_hal_set_dfs_tb_ctrl(phl_info->hal, is_dfs); } } static void _phl_no_link_reset_sta_info(struct phl_info_t *phl_info, struct rtw_phl_stainfo_t *sta) { void *drv = phl_to_drvpriv(phl_info); /* asoc cap */ _os_mem_set(drv, &sta->asoc_cap, 0, sizeof(struct protocol_cap_t)); /* other capabilities under stainfo need to reset with default value */ sta->tf_trs = 0; /* protection mode */ sta->protect = RTW_PROTECT_DISABLE; } /** * This function is called once station associated with AP * or incoming station got associated under AP mode. * Before calling this function, update address / net_type / ... * information of stainfo * It will configure some hw register, ex * address cam * @phl: see phl_info_t * @stainfo: information is updated through phl_station_info */ static enum rtw_phl_status phl_update_media_status(struct phl_info_t *phl_info, struct rtw_phl_stainfo_t *sta, u8 *sta_addr, bool is_connect) { struct rtw_wifi_role_t *wrole = sta->wrole; void *drv = phl_to_drvpriv(phl_info); enum rtw_hal_status hstatus = RTW_HAL_STATUS_FAILURE; enum rtw_phl_status pstatus = RTW_PHL_STATUS_FAILURE; bool is_sta_linked = false; is_sta_linked = rtw_hal_is_sta_linked(phl_info->hal, sta); if (is_connect == true && is_sta_linked == true) { PHL_ERR("%s STA (MAC_ID:%d) had connected\n", __func__, sta->macid); goto _exit; } if (is_connect == false && is_sta_linked == false) { /* handle connect abort case */ if (wrole->mstate == MLME_LINKING) { PHL_INFO("%s MAC_ID(%d) connect abort\n", __func__, sta->macid); pstatus = RTW_PHL_STATUS_SUCCESS; } else { PHL_ERR("%s MAC_ID(%d) had disconnected\n", __func__, sta->macid); } if (wrole->type == PHL_RTYPE_STATION || wrole->type == PHL_RTYPE_P2P_GC) wrole->mstate = MLME_NO_LINK; goto _exit; } /* reset trx statistics */ if (is_connect == false) { phl_reset_tx_stats(&sta->stats); phl_reset_rx_stats(&sta->stats); _phl_no_link_reset_sta_info(phl_info, sta); CLEAR_STATUS_FLAG(wrole->status, WR_STATUS_TSF_SYNC); } else { phl_clean_sta_bcn_info(phl_info, sta); } /* Configure address cam, including net_type and sync_tsf */ if ((wrole->type == PHL_RTYPE_STATION) || (wrole->type == PHL_RTYPE_P2P_GC) #ifdef CONFIG_PHL_TDLS /* STA disconnects with the associated AP before tearing down with TDLS peers */ || ((wrole->type == PHL_RTYPE_TDLS) && (!sta_addr)) #endif ) { if (is_connect) { wrole->mstate = MLME_LINKED; _os_mem_cpy(drv, sta->mac_addr, sta_addr, MAC_ALEN); _phl_set_dfs_tb_ctrl(phl_info, wrole); } else { wrole->mstate = MLME_NO_LINK; } } #ifdef RTW_WKARD_AP_CLIENT_ADD_DEL_NTY else if ((wrole->type == PHL_RTYPE_AP) || (wrole->type == PHL_RTYPE_VAP) || (wrole->type == PHL_RTYPE_MESH) || (wrole->type == PHL_RTYPE_P2P_GO)) { if (is_connect) phl_role_ap_client_notify(phl_info, wrole, MLME_LINKED); } #endif hstatus = rtw_hal_update_sta_entry(phl_info->hal, sta, is_connect); if (hstatus != RTW_HAL_STATUS_SUCCESS) { PHL_ERR("rtw_hal_update_sta_entry failure!\n"); goto _exit; } if (wrole->type == PHL_RTYPE_STATION #ifdef CONFIG_PHL_TDLS /* STA disconnects with the associated AP before tearing down with TDLS peers */ || ((wrole->type == PHL_RTYPE_TDLS) && (!sta_addr)) #endif ) { hstatus = rtw_hal_role_cfg(phl_info->hal, wrole); if (hstatus != RTW_HAL_STATUS_SUCCESS) { PHL_ERR("rtw_hal_role_cfg failure!\n"); goto _exit; } } pstatus = RTW_PHL_STATUS_SUCCESS; /* TODO: Configure RCR */ _exit: return pstatus; } #ifdef CONFIG_CMD_DISP struct sta_media_param { struct rtw_phl_stainfo_t *sta; u8 sta_addr[MAC_ALEN]; bool is_connect; }; enum rtw_phl_status phl_update_media_status_hdl(struct phl_info_t *phl_info, u8 *param) { struct sta_media_param *media_sts = (struct sta_media_param *)param; return phl_update_media_status(phl_info, media_sts->sta, media_sts->sta_addr, media_sts->is_connect); } void phl_update_media_status_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; } } #endif enum rtw_phl_status rtw_phl_cmd_update_media_status(void *phl, struct rtw_phl_stainfo_t *sta, u8 *sta_addr, bool is_connect, enum phl_cmd_type cmd_type, u32 cmd_timeout) { #ifdef CONFIG_CMD_DISP enum rtw_phl_status psts = RTW_PHL_STATUS_FAILURE; struct phl_info_t *phl_info = (struct phl_info_t *)phl; void *drv = phl_to_drvpriv(phl_info); struct rtw_wifi_role_t *wrole = NULL; struct sta_media_param *sta_ms = NULL; u32 sta_ms_len = 0; if (cmd_type == PHL_CMD_DIRECTLY) { psts = phl_update_media_status(phl_info, sta, sta_addr, is_connect); goto _exit; } sta_ms_len = sizeof(struct sta_media_param); sta_ms = _os_kmem_alloc(drv, sta_ms_len); if (sta_ms == NULL) { PHL_ERR("%s: alloc sta media status param failed!\n", __func__); psts = RTW_PHL_STATUS_RESOURCE; goto _exit; } _os_mem_set(drv, sta_ms, 0, sta_ms_len); sta_ms->sta = sta; sta_ms->is_connect = is_connect; if (is_connect && sta_addr) _os_mem_cpy(drv, sta_ms->sta_addr, sta_addr, MAC_ALEN); wrole = sta->wrole; psts = phl_cmd_enqueue(phl_info, wrole->hw_band, MSG_EVT_STA_MEDIA_STATUS_UPT, (u8*)sta_ms, sta_ms_len, phl_update_media_status_done, cmd_type, cmd_timeout); 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_kmem_free(drv, sta_ms, sta_ms_len); } _exit: return psts; #else PHL_TRACE(COMP_PHL_DBG, _PHL_INFO_, "%s: not support cmd to update media status\n", __func__); return phl_update_media_status((struct phl_info_t *)phl, sta, sta_addr, is_connect); #endif } /** * This function is called once station info changed * (BW/NSS/RAMASK/SEC/ROLE/MACADDR........) * @phl: see phl_info_t * @stainfo: information is updated through phl_station_info * @mode: see phl_upd_mode */ enum rtw_phl_status phl_change_stainfo(struct phl_info_t *phl_info, struct rtw_phl_stainfo_t *sta, enum phl_upd_mode mode) { enum rtw_hal_status hstatus = RTW_HAL_STATUS_FAILURE; hstatus = rtw_hal_change_sta_entry(phl_info->hal, sta, mode); if (hstatus != RTW_HAL_STATUS_SUCCESS) { PHL_ERR("rtw_hal_change_sta_entry failure!\n"); return RTW_PHL_STATUS_FAILURE; } return RTW_PHL_STATUS_SUCCESS; } static enum rtw_phl_status _change_stainfo(struct phl_info_t *phl_info, struct rtw_phl_stainfo_t *sta, enum sta_chg_id chg_id, u8 *chg_info, u8 chg_info_len) { enum phl_upd_mode mode = PHL_UPD_STA_INFO_CHANGE; switch (chg_id) { case STA_CHG_BW: case STA_CHG_NSS: case STA_CHG_RAMASK: { PHL_INFO("%s MACID:%d %02x:%02x:%02x:%02x:%02x:%02x update bw\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]); } break; case STA_CHG_SEC_MODE: sta->sec_mode = *((u8*)chg_info); break; case STA_CHG_MBSSID: sta->addr_sel = 1; sta->addr_msk = *((u8*)chg_info); break; case STA_CHG_RA_GILTF: sta->hal_sta->ra_info.cal_giltf = *((u8*)chg_info); sta->hal_sta->ra_info.fix_giltf_en = true; PHL_INFO("%s: Config RA GI LTF = %d\n", __FUNCTION__, *((u8*)chg_info)); break; case STA_CHG_MAX: PHL_TRACE(COMP_PHL_DBG, _PHL_DEBUG_, "rtw_phl_change_stainfo(): Unsupported case:%d, please check it\n", chg_id); break; default: PHL_TRACE(COMP_PHL_DBG, _PHL_DEBUG_, "rtw_phl_change_stainfo(): Unrecognize case:%d, please check it\n", chg_id); break; } return phl_change_stainfo(phl_info, sta, mode); } #ifdef CONFIG_CMD_DISP struct sta_chg_param { struct rtw_phl_stainfo_t *sta; enum sta_chg_id id; u8 *info; u8 info_len; }; enum rtw_phl_status phl_cmd_change_stainfo_hdl(struct phl_info_t *phl_info, u8 *param) { struct sta_chg_param *sta_param = (struct sta_chg_param *)param; return _change_stainfo(phl_info, sta_param->sta, sta_param->id, sta_param->info, sta_param->info_len); } static void _phl_cmd_change_stainfo_done(void *drv_priv, u8 *cmd, u32 cmd_len, enum rtw_phl_status status) { struct sta_chg_param *sta_chg_info = NULL; if (cmd == NULL || cmd_len == 0) { PHL_ERR("%s buf == NULL || buf_len == 0\n", __func__); _os_warn_on(1); return; } sta_chg_info = (struct sta_chg_param *)cmd; PHL_INFO("%s - id:%d .....\n", __func__, sta_chg_info->id); if (sta_chg_info->info && sta_chg_info->info_len > 0) _os_kmem_free(drv_priv, sta_chg_info->info, sta_chg_info->info_len); _os_kmem_free(drv_priv, cmd, cmd_len); cmd = NULL; } static enum rtw_phl_status _phl_cmd_change_stainfo(struct phl_info_t *phl_info, struct rtw_phl_stainfo_t *sta, enum sta_chg_id chg_id, u8 *chg_info, u8 chg_info_len, enum phl_cmd_type cmd_type, u32 cmd_timeout) { void *drv = phl_to_drvpriv(phl_info); enum rtw_phl_status psts = RTW_PHL_STATUS_FAILURE; struct rtw_wifi_role_t *wrole = sta->wrole; struct sta_chg_param *param = NULL; u8 param_len = 0; if (cmd_type == PHL_CMD_DIRECTLY) { psts = _change_stainfo(phl_info, sta, chg_id, chg_info, chg_info_len); goto _exit; } param_len = sizeof(struct sta_chg_param); param = _os_kmem_alloc(drv, param_len); if (param == NULL) { PHL_ERR("%s: alloc param failed!\n", __func__); psts = RTW_PHL_STATUS_RESOURCE; goto _exit; } _os_mem_set(drv, param, 0, param_len); param->sta = sta; param->id = chg_id; param->info_len = chg_info_len; if (chg_info_len > 0) { param->info = _os_kmem_alloc(drv, chg_info_len); if (param->info == NULL) { PHL_ERR("%s: alloc param->info failed!\n", __func__); psts = RTW_PHL_STATUS_RESOURCE; goto _err_info; } _os_mem_set(drv, param->info, 0, chg_info_len); _os_mem_cpy(drv, param->info, chg_info, chg_info_len); } else { param->info = NULL; } psts = phl_cmd_enqueue(phl_info, wrole->hw_band, MSG_EVT_STA_CHG_STAINFO, (u8 *)param, param_len, _phl_cmd_change_stainfo_done, cmd_type, cmd_timeout); 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; goto _err_cmd; } return psts; _err_cmd: if (param->info) _os_kmem_free(drv, param->info, param->info_len); _err_info: if (param) _os_kmem_free(drv, param, param_len); _exit: return psts; } #endif enum rtw_phl_status rtw_phl_cmd_change_stainfo(void *phl, struct rtw_phl_stainfo_t *sta, enum sta_chg_id chg_id, u8 *chg_info, u8 chg_info_len, enum phl_cmd_type cmd_type, u32 cmd_timeout) { struct phl_info_t *phl_info = (struct phl_info_t *)phl; #ifdef CONFIG_CMD_DISP return _phl_cmd_change_stainfo(phl_info, sta, chg_id, chg_info, chg_info_len, cmd_type, cmd_timeout); #else PHL_TRACE(COMP_PHL_DBG, _PHL_INFO_, "%s: not support alloc stainfo cmd\n", __func__); return _change_stainfo(phl_info, sta, chg_id, chg_info, chg_info_len); #endif /* CONFIG_CMD_DISP */ } /** * This function updates tx/rx traffic status of each active station info */ void phl_sta_trx_tfc_upd(struct phl_info_t *phl_info) { struct macid_ctl_t *macid_ctl = phl_to_mac_ctrl(phl_info); struct rtw_phl_stainfo_t *phl_sta = NULL; struct rtw_stats *sta_stats = NULL; u16 max_macid_num = 0; u16 mid = 0; max_macid_num = macid_ctl->max_num; _os_spinlock(phl_to_drvpriv(phl_info), &macid_ctl->lock, _bh, NULL); for(mid = 0; mid < max_macid_num; mid++) { if (_phl_macid_is_used(macid_ctl->used_map, mid)) { phl_sta = macid_ctl->sta[mid]; if (phl_sta) { #ifdef CONFIG_PHL_RA_TXSTS_DBG /* issue H2C to get ra txsts report */ rtw_phl_txsts_rpt_config(phl_info, phl_sta); #endif sta_stats = &phl_sta->stats; phl_tx_traffic_upd(sta_stats); phl_rx_traffic_upd(sta_stats); } } } _os_spinunlock(phl_to_drvpriv(phl_info), &macid_ctl->lock, _bh, NULL); } /** * This function is used to get phl sta info * by macid * @phl: see phl_info_t * @macid: macid */ struct rtw_phl_stainfo_t * rtw_phl_get_stainfo_by_macid(void *phl, u16 macid) { struct phl_info_t *phl_info = (struct phl_info_t *)phl; struct macid_ctl_t *macid_ctl = phl_to_mac_ctrl(phl_info); struct rtw_phl_stainfo_t *phl_sta = NULL; if (macid >= macid_ctl->max_num) { PHL_ERR("%s macid(%d) is invalid\n", __func__, macid); return NULL; } _os_spinlock(phl_to_drvpriv(phl_info), &macid_ctl->lock, _bh, NULL); if (_phl_macid_is_used(macid_ctl->used_map, macid)) phl_sta = macid_ctl->sta[macid]; if (phl_sta == NULL) { PHL_TRACE(COMP_PHL_DBG, _PHL_DEBUG_,"%s sta info (macid:%d) is NULL\n", __func__, macid); #ifdef CONFIG_PHL_USB_RELEASE_RPT_ENABLE /* comment temporarily since release report may report unused macid */ /* and trigger call tracing */ /* _os_warn_on(1); */ #else #ifdef CONFIG_RTW_DEBUG if (_PHL_DEBUG_ <= phl_log_level) _os_warn_on(1); #endif /*CONFIG_RTW_DEBUG*/ #endif /* CONFIG_PHL_USB_RELEASE_RPT_ENABLE */ } _os_spinunlock(phl_to_drvpriv(phl_info), &macid_ctl->lock, _bh, NULL); return phl_sta; } struct rtw_phl_stainfo_t * rtw_phl_get_stainfo_by_addr_ex(void *phl, u8 *addr) { struct phl_info_t *phl_info = (struct phl_info_t *)phl; struct macid_ctl_t *mc = phl_to_mac_ctrl(phl_info); struct rtw_phl_stainfo_t *sta = NULL; u16 mid = 0; u16 max_macid_num = mc->max_num; bool sta_found = false; _os_spinlock(phl_to_drvpriv(phl_info), &mc->lock, _bh, NULL); for(mid = 0; mid < max_macid_num; mid++) { if (_phl_macid_is_used(mc->used_map, mid)) { sta = mc->sta[mid]; if (_os_mem_cmp(phl_to_drvpriv(phl_info), sta->mac_addr, addr, MAC_ALEN) == 0) { sta_found = true; break; } } } _os_spinunlock(phl_to_drvpriv(phl_info), &mc->lock, _bh, NULL); if (sta_found == false) sta = NULL; return sta; } u16 rtw_phl_get_macid_by_addr(void *phl, u8 *addr) { struct phl_info_t *phl_info = (struct phl_info_t *)phl; struct macid_ctl_t *mc = phl_to_mac_ctrl(phl_info); struct rtw_phl_stainfo_t *sta = NULL; sta = rtw_phl_get_stainfo_by_addr_ex(phl, addr); if (sta) return sta->macid; return mc->max_num; } /** * This function is called to create phl_station_info * return pointer to rtw_phl_stainfo_t * @phl: see phl_info_t * @roleidx: index of wifi role(linux) port nubmer(windows) * @addr: current address of this station */ struct rtw_phl_stainfo_t * rtw_phl_get_stainfo_by_addr(void *phl, struct rtw_wifi_role_t *wrole, u8 *addr) { struct phl_info_t *phl_info = (struct phl_info_t *)phl; struct macid_ctl_t *macid_ctl = phl_to_mac_ctrl(phl_info); struct rtw_phl_stainfo_t *sta = NULL; if (is_broadcast_mac_addr(addr)) { u16 macid = macid_ctl->wrole_bmc[wrole->id]; if (macid >= macid_ctl->max_num) sta = NULL; else sta = macid_ctl->sta[macid]; goto _exit; } sta = phl_stainfo_queue_search(phl_info, &wrole->assoc_sta_queue, addr); _exit: return sta; } struct rtw_phl_stainfo_t * rtw_phl_get_stainfo_self(void *phl, struct rtw_wifi_role_t *wrole) { struct phl_info_t *phl_info = (struct phl_info_t *)phl; struct rtw_phl_stainfo_t *sta = NULL; #if 0 if ((wrole->type == PHL_RTYPE_STATION) && (wrole->mstate == MLME_LINKED)) //???? else sta = phl_stainfo_queue_search(phl_info, &wrole->assoc_sta_queue, wrole->mac_addr); } #else sta = phl_stainfo_queue_get_first(phl_info, &wrole->assoc_sta_queue); if (sta == NULL) PHL_ERR("%s sta == NULL\n", __func__); #endif return sta; } u8 rtw_phl_get_sta_rssi(struct rtw_phl_stainfo_t *sta) { u8 rssi = rtw_hal_get_sta_rssi(sta); return rssi; } u8 phl_get_min_rssi_bcn(struct phl_info_t *phl_info) { struct macid_ctl_t *macid_ctl = phl_to_mac_ctrl(phl_info); struct rtw_phl_stainfo_t *sta = NULL; u8 rssi_bcn_min = 0xFF; u16 i = 0; u8 rssi = 0; for (i = 0; i < macid_ctl->max_num; i++) { if (!_phl_macid_is_used(macid_ctl->used_map, i)) continue; sta = rtw_phl_get_stainfo_by_macid(phl_info, i); if (NULL == sta) continue; rssi = rtw_hal_get_sta_rssi_bcn(sta); PHL_DBG("%s macid(%d) with rssi_bcn = %d\n", __func__, i, rssi); if (rssi == 0) continue; rssi_bcn_min = MIN(rssi, rssi_bcn_min); } return rssi_bcn_min; } enum rtw_phl_status rtw_phl_query_rainfo(void *phl, struct rtw_phl_stainfo_t *phl_sta, struct rtw_phl_rainfo *ra_info) { struct phl_info_t *phl_info = (struct phl_info_t *)phl; enum rtw_phl_status phl_sts = RTW_PHL_STATUS_FAILURE; do { if (NULL == phl_sta) { PHL_TRACE(COMP_PHL_XMIT, _PHL_ERR_, "%s : phl_sta is NULL\n", __func__); break; } if (NULL == ra_info) { PHL_TRACE(COMP_PHL_XMIT, _PHL_ERR_, "%s : Input parameter is NULL\n", __func__); break; } if (RTW_HAL_STATUS_SUCCESS == rtw_hal_query_rainfo(phl_info->hal, phl_sta->hal_sta, ra_info)) { phl_sts = RTW_PHL_STATUS_SUCCESS; break; } else { break; } } while (false); return phl_sts; } /** * rtw_phl_txsts_rpt_config() - issue h2c for txok and tx retry info * @phl: struct phl_info_t * * @phl_sta: indicate the first macid that you want to query. * Return rtw_phl_txsts_rpt_config's return value in enum rtw_phl_status type. */ enum rtw_phl_status rtw_phl_txsts_rpt_config(void *phl, struct rtw_phl_stainfo_t *phl_sta) { struct phl_info_t *phl_info = (struct phl_info_t *)phl; enum rtw_phl_status phl_sts = RTW_PHL_STATUS_FAILURE; if (phl_sta) { if (RTW_HAL_STATUS_SUCCESS == rtw_hal_query_txsts_rpt(phl_info->hal, phl_sta->macid)) phl_sts = RTW_PHL_STATUS_SUCCESS; } return phl_sts; } #ifdef CONFIG_USB_HCI /** * rtw_phl_get_tx_ok_rpt() - get txok info. * @phl: struct phl_info_t * * @phl_sta: information is updated through phl_station_info. * @tx_ok_cnt: buffer address that we used to store tx ok statistics. * @qsel indicate which AC queue, or fetch all by PHL_AC_QUEUE_TOTAL * * Return rtw_phl_get_tx_ok_rpt's return value in enum rtw_phl_status type. */ enum rtw_phl_status rtw_phl_get_tx_ok_rpt(void *phl, struct rtw_phl_stainfo_t *phl_sta, u32 *tx_ok_cnt, enum phl_ac_queue qsel) { struct phl_info_t *phl_info = (struct phl_info_t *)phl; enum rtw_phl_status phl_sts = RTW_PHL_STATUS_SUCCESS; struct rtw_hal_stainfo_t *hal_sta; if(phl_sta) { hal_sta = phl_sta->hal_sta; if (tx_ok_cnt && qsel <= PHL_AC_QUEUE_TOTAL) { if (qsel == PHL_AC_QUEUE_TOTAL) { /* copy all AC counter */ tx_ok_cnt[PHL_BE_QUEUE_SEL] = hal_sta->trx_stat.wp_rpt_stats[PHL_BE_QUEUE_SEL].tx_ok_cnt; tx_ok_cnt[PHL_BK_QUEUE_SEL] = hal_sta->trx_stat.wp_rpt_stats[PHL_BK_QUEUE_SEL].tx_ok_cnt; tx_ok_cnt[PHL_VI_QUEUE_SEL] = hal_sta->trx_stat.wp_rpt_stats[PHL_VI_QUEUE_SEL].tx_ok_cnt; tx_ok_cnt[PHL_VO_QUEUE_SEL] = hal_sta->trx_stat.wp_rpt_stats[PHL_VO_QUEUE_SEL].tx_ok_cnt; /* reset all counter */ _os_spinlock(phl_to_drvpriv(phl_info), &hal_sta->trx_stat.tx_sts_lock, _bh, NULL); hal_sta->trx_stat.wp_rpt_stats[PHL_BE_QUEUE_SEL].tx_ok_cnt = 0; hal_sta->trx_stat.wp_rpt_stats[PHL_BK_QUEUE_SEL].tx_ok_cnt = 0; hal_sta->trx_stat.wp_rpt_stats[PHL_VI_QUEUE_SEL].tx_ok_cnt = 0; hal_sta->trx_stat.wp_rpt_stats[PHL_VO_QUEUE_SEL].tx_ok_cnt = 0; _os_spinunlock(phl_to_drvpriv(phl_info), &hal_sta->trx_stat.tx_sts_lock, _bh, NULL); } else { /*copy target AC queue counter*/ *tx_ok_cnt = hal_sta->trx_stat.wp_rpt_stats[qsel].tx_ok_cnt; /* reset target AC queue counter */ _os_spinlock(phl_to_drvpriv(phl_info), &hal_sta->trx_stat.tx_sts_lock, _bh, NULL); hal_sta->trx_stat.wp_rpt_stats[qsel].tx_ok_cnt = 0; _os_spinunlock(phl_to_drvpriv(phl_info), &hal_sta->trx_stat.tx_sts_lock, _bh, NULL); } } else { phl_sts = RTW_PHL_STATUS_FAILURE; PHL_ERR("tx_ok_cnt = %p, qsel = %d\n", tx_ok_cnt, qsel); } } else { phl_sts = RTW_PHL_STATUS_FAILURE; PHL_ERR("PHL STA NULL.\n"); } return phl_sts; } static u32 rtw_phl_get_hw_tx_fail_cnt(struct rtw_hal_stainfo_t *hal_sta, enum phl_ac_queue qsel) { u32 total = 0; if (hal_sta) { total = hal_sta->trx_stat.wp_rpt_stats[qsel].rty_fail_cnt\ + hal_sta->trx_stat.wp_rpt_stats[qsel].lifetime_drop_cnt \ + hal_sta->trx_stat.wp_rpt_stats[qsel].macid_drop_cnt; } return total; } static void rtw_phl_reset_tx_fail_cnt(struct phl_info_t *phl_info, struct rtw_hal_stainfo_t *hal_sta, enum phl_ac_queue qsel) { if (hal_sta) { _os_spinlock(phl_to_drvpriv(phl_info), &hal_sta->trx_stat.tx_sts_lock, _bh, NULL); hal_sta->trx_stat.wp_rpt_stats[qsel].rty_fail_cnt = 0; hal_sta->trx_stat.wp_rpt_stats[qsel].lifetime_drop_cnt = 0; hal_sta->trx_stat.wp_rpt_stats[qsel].macid_drop_cnt = 0; _os_spinunlock(phl_to_drvpriv(phl_info), &hal_sta->trx_stat.tx_sts_lock, _bh, NULL); } } /** * rtw_phl_get_tx_fail_rpt() - get tx fail info. * @phl: struct phl_info_t * * @phl_sta: information is updated through phl_station_info. * @tx_fail_cnt: buffer address that we used to store tx fail statistics. * @qsel indicate which AC queue, or fetch all by PHL_AC_QUEUE_TOTAL * * Return rtw_phl_get_tx_fail_rpt's return value in enum rtw_phl_status type. */ enum rtw_phl_status rtw_phl_get_tx_fail_rpt(void *phl, struct rtw_phl_stainfo_t *phl_sta, u32 *tx_fail_cnt, enum phl_ac_queue qsel) { struct phl_info_t *phl_info = (struct phl_info_t *)phl; enum rtw_phl_status phl_sts = RTW_PHL_STATUS_SUCCESS; struct rtw_hal_stainfo_t *hal_sta; if(phl_sta) { hal_sta = phl_sta->hal_sta; if (tx_fail_cnt && qsel <= PHL_AC_QUEUE_TOTAL) { if (qsel == PHL_AC_QUEUE_TOTAL) { /* copy all AC counter */ tx_fail_cnt[PHL_BE_QUEUE_SEL] = rtw_phl_get_hw_tx_fail_cnt(hal_sta, PHL_BE_QUEUE_SEL); tx_fail_cnt[PHL_BK_QUEUE_SEL] = rtw_phl_get_hw_tx_fail_cnt(hal_sta, PHL_BK_QUEUE_SEL); tx_fail_cnt[PHL_VI_QUEUE_SEL] = rtw_phl_get_hw_tx_fail_cnt(hal_sta, PHL_VI_QUEUE_SEL); tx_fail_cnt[PHL_VO_QUEUE_SEL] = rtw_phl_get_hw_tx_fail_cnt(hal_sta, PHL_VO_QUEUE_SEL); /* reset all counter */ rtw_phl_reset_tx_fail_cnt(phl_info, hal_sta, PHL_BE_QUEUE_SEL); rtw_phl_reset_tx_fail_cnt(phl_info, hal_sta, PHL_BK_QUEUE_SEL); rtw_phl_reset_tx_fail_cnt(phl_info, hal_sta, PHL_VI_QUEUE_SEL); rtw_phl_reset_tx_fail_cnt(phl_info, hal_sta, PHL_VO_QUEUE_SEL); } else { /*copy target AC queue counter*/ tx_fail_cnt[qsel] = rtw_phl_get_hw_tx_fail_cnt(hal_sta, qsel); /* reset target AC queue counter */ rtw_phl_reset_tx_fail_cnt(phl_info, hal_sta, qsel); } } else { phl_sts = RTW_PHL_STATUS_FAILURE; PHL_ERR("tx_fail_cnt = %p, qsel = %d\n", tx_fail_cnt, qsel); } } else { phl_sts = RTW_PHL_STATUS_FAILURE; PHL_ERR("PHL STA NULL.\n"); } return phl_sts; } /** * rtw_phl_get_tx_retry_rpt() - get tx retry info. * @phl: struct phl_info_t * * @phl_sta: information is updated through phl_station_info. * @tx_retry_cnt: buffer address that we used to store tx fail statistics. * @qsel indicate which AC queue, or fetch all by PHL_AC_QUEUE_TOTAL * * Return rtw_phl_get_tx_retry_rpt's return value in enum rtw_phl_status type. */ enum rtw_phl_status rtw_phl_get_tx_retry_rpt(void *phl, struct rtw_phl_stainfo_t *phl_sta, u32 *tx_retry_cnt, enum phl_ac_queue qsel) { struct phl_info_t *phl_info = (struct phl_info_t *)phl; void *drv = phl_to_drvpriv(phl_info); enum rtw_phl_status phl_sts = RTW_PHL_STATUS_SUCCESS; struct rtw_hal_stainfo_t *hal_sta; if(phl_sta) { hal_sta = phl_sta->hal_sta; if (tx_retry_cnt && qsel <= PHL_AC_QUEUE_TOTAL) { if (qsel == PHL_AC_QUEUE_TOTAL) { /* copy all AC counter */ _os_mem_cpy(drv, tx_retry_cnt, hal_sta->ra_info.tx_retry_cnt, sizeof(u32)*PHL_AC_QUEUE_TOTAL); /* reset all counter */ /* TODO: Here needs lock, and so does halbb_get_txsts_rpt */ _os_mem_set(drv, hal_sta->ra_info.tx_retry_cnt, 0, sizeof(u32)*PHL_AC_QUEUE_TOTAL); } else { /*copy target AC queue counter*/ *tx_retry_cnt = hal_sta->ra_info.tx_retry_cnt[qsel]; /* reset target AC queue counter */ /* TODO: Here needs lock, and so does halbb_get_txsts_rpt */ hal_sta->ra_info.tx_retry_cnt[qsel] = 0; } } else { phl_sts = RTW_PHL_STATUS_FAILURE; PHL_ERR("tx_retry_cnt = %p, qsel = %d\n", tx_retry_cnt, qsel); } } else { phl_sts = RTW_PHL_STATUS_FAILURE; PHL_ERR("PHL STA NULL.\n"); } return phl_sts; } #endif /* CONFIG_USB_HCI */ /* * Get next idx */ u8 _get_fidx(u8 num, u8 cur_idx) { u8 idx = 0; if (num == 0) idx = cur_idx; else { idx = cur_idx + 1; if (idx >= MAX_STORE_BCN_NUM) idx = 0; } return idx; } /* * Get previous idx */ u8 _get_bidx(u8 num, u8 cur_idx) { u8 idx = 0; if (cur_idx == 0) { idx = num - 1; } else { idx = cur_idx - 1; } return idx; } void _phl_sta_up_bcn_offset_info(struct phl_info_t *phl, struct rtw_rx_bcn_info *bcn_i, u16 bcn_intvl) { struct rtw_bcn_offset *offset_i = &bcn_i->offset_i; u16 offset = bcn_intvl; u16 similar_th = 2;/*Unit: TU*/ u64 diff = 0; u8 idx = 0, jdx = 0, cur_idx = 0, bidx = 0, start_idx = 0; if (bcn_i->num == 1) { offset_i->offset = (u16)bcn_i->info[1][bcn_i->idx]; offset_i->conf_lvl = CONF_LVL_LOW; PHL_TRACE(COMP_PHL_DBG, _PHL_WARNING_, "_phl_sta_up_bcn_offset_info(): bcn_i->num ==1, conf_lvl = CONF_LVL_LOW, offset(%d)\n", offset_i->offset); goto exit; } cur_idx = bcn_i->idx; start_idx = cur_idx; for (idx = 0; idx < bcn_i->num; idx++) { bidx = cur_idx; for (jdx = 1; jdx < bcn_i->num; jdx++) { bidx = _get_bidx(bcn_i->num, bidx); if (start_idx == bidx) break; diff = bcn_i->info[0][cur_idx] - bcn_i->info[0][bidx]; diff = _os_division64( _os_modular64(diff, bcn_intvl * TU), TU); /*ex: diff = 99, BcnIntvl = 100, It's similar case * diff = 2, BcnIntvl = 100, It's similar case */ if (!((diff < similar_th) || ((bcn_intvl - diff) < similar_th))) { continue; } if (offset > bcn_i->info[1][cur_idx]) offset = (u16)bcn_i->info[1][cur_idx]; if (offset > bcn_i->info[1][bidx]) offset = (u16)bcn_i->info[1][bidx]; } cur_idx = _get_bidx(bcn_i->num, cur_idx); } if (offset != bcn_intvl) { offset_i->conf_lvl = CONF_LVL_HIGH; if (offset < offset_i->offset) { offset_i->offset = offset; } goto exit; } for (idx = 0; idx < bcn_i->num; idx++) { if (bcn_i->info[1][idx] < offset_i->offset) { offset_i->offset = (u16)bcn_i->info[1][idx]; offset_i->conf_lvl = CONF_LVL_MID; } } exit: /* if offset is small, maybe impact by environment, offset < 5% bcn_intvl, we consider offset is 0 */ if ((offset_i->offset != 0) && (offset_i->offset < ((bcn_intvl * 5) / 100))) { PHL_TRACE(COMP_PHL_MCC, _PHL_WARNING_, "_phl_sta_up_bcn_offset_info(): offset(%d) < (%d), set offset = 0\n", offset_i->offset, (bcn_intvl * 5) / 100); offset_i->offset = 0; } PHL_TRACE(COMP_PHL_DBG, _PHL_DEBUG_, "_phl_sta_up_bcn_offset_info(): bcn num(%d), offset(%d), conf_lvl(%d), current CR(%d)\n", bcn_i->num, offset_i->offset, offset_i->conf_lvl, offset_i->cr_tbtt_shift); return; } void rtw_phl_sta_up_rx_bcn(void *phl, struct rtw_bcn_pkt_info *info) { struct rtw_rx_bcn_info *bcn_i = &info->sta->bcn_i; u16 bcn_intvl = info->sta->asoc_cap.bcn_interval; if (bcn_intvl == 0) { PHL_TRACE(COMP_PHL_DBG, _PHL_WARNING_, "bcn_intvl == 0"); return; } bcn_i->idx = _get_fidx(bcn_i->num, bcn_i->idx); if (bcn_i->num < MAX_STORE_BCN_NUM) bcn_i->num++; bcn_i->info[0][bcn_i->idx] = info->tsf; bcn_i->info[1][bcn_i->idx] = _os_division64( _os_modular64(info->tsf, bcn_intvl * TU), TU); bcn_i->info[2][bcn_i->idx] = info->hw_tsf; _phl_sta_up_bcn_offset_info(phl, bcn_i, bcn_intvl); } void phl_clean_sta_bcn_info(struct phl_info_t *phl, struct rtw_phl_stainfo_t *sta) { void *priv = phl_to_drvpriv(phl); struct rtw_rx_bcn_info *bcn_i = &sta->bcn_i; PHL_TRACE(COMP_PHL_DBG, _PHL_INFO_, "phl_clean_sta_bcn_info(): sta->wrole->id(%d)\n", sta->wrole->id); _os_mem_set(priv, bcn_i, 0, sizeof(struct rtw_rx_bcn_info)); } struct rtw_bcn_offset * phl_get_sta_bcn_offset_info(struct phl_info_t *phl, struct rtw_wifi_role_t *wrole) { struct rtw_phl_stainfo_t *sta = rtw_phl_get_stainfo_self(phl, wrole); struct rtw_bcn_offset *offset_i = &sta->bcn_i.offset_i; return offset_i; } void phl_bcn_watchdog(struct phl_info_t *phl) { u8 ridx = MAX_WIFI_ROLE_NUMBER; struct rtw_wifi_role_t *wrole = NULL; struct rtw_bcn_offset *b_ofst_i = NULL; enum rtw_hal_status hstatus = RTW_HAL_STATUS_SUCCESS; for (ridx = 0; ridx < MAX_WIFI_ROLE_NUMBER; ridx++) { wrole = rtw_phl_get_wrole_by_ridx(phl->phl_com, ridx); if (wrole == NULL) continue; if (rtw_phl_role_is_client_category(wrole) && wrole->mstate == MLME_LINKED) { b_ofst_i = phl_get_sta_bcn_offset_info(phl, wrole); if (b_ofst_i->conf_lvl >= CONF_LVL_MID && b_ofst_i->offset != b_ofst_i->cr_tbtt_shift) { PHL_TRACE(COMP_PHL_DBG, _PHL_INFO_, "%s(): update bcn offset to %d TU\n", __func__, b_ofst_i->offset); hstatus = rtw_hal_role_cfg_ex(phl->hal, wrole, PCFG_TBTT_SHIFT, &(b_ofst_i->offset)); if (hstatus == RTW_HAL_STATUS_SUCCESS) b_ofst_i->cr_tbtt_shift = b_ofst_i->offset; else PHL_ERR("%s(): role cfg fail, status: %d\n", __func__, hstatus); } } } } /* * calculate the value between current TSF and TBTT * TSF 0 50 180 150 250 * TBTT ^ ^ ^ * Curr T | * | 30 | * * TSF 0 80 120 180 280 * TBTT ^ ^ ^ * Curr T | * | 40 | * @wrole: specific role, we get bcn offset info from the role. * @cur_t: current TSF * @ofst: output value, unit: TU */ bool phl_calc_offset_from_tbtt(struct phl_info_t *phl, struct rtw_wifi_role_t *wrole, u64 cur_t, u16 *ofst) { struct rtw_bcn_offset *b_ofst_i = phl_get_sta_bcn_offset_info(phl, wrole); struct rtw_phl_stainfo_t *sta = rtw_phl_get_stainfo_self(phl, wrole); u64 b_ofst = b_ofst_i->offset; u64 b_intvl = 0; u32 mod = 0; /*TU*/ #ifdef RTW_PHL_BCN if (phl_role_is_ap_category(wrole)) b_intvl = (u16)wrole->bcn_cmn.bcn_interval; else #endif b_intvl = sta->asoc_cap.bcn_interval; if (0 == b_intvl) { PHL_TRACE(COMP_PHL_DBG, _PHL_ERR_, "phl_calc_offset_from_tbtt(): Fail, b_intvl ==0, wrole->id(%d), type(%d)\n", wrole->id, wrole->type); return false; } mod = (u32)_os_division64(_os_modular64(cur_t, b_intvl * TU), TU); if (mod < b_ofst) { *ofst = (u16)(mod + (b_intvl - b_ofst)); } else { *ofst = (u16)(mod - b_ofst); } PHL_TRACE(COMP_PHL_DBG, _PHL_INFO_, "phl_calc_offset_from_tbtt(): wrole->id(%d), ofst(%d), cur_t: 0x%08x %08x modular(%d, TU), Bcn offset: conf_lvl(%d), offset(%d)\n", wrole->id, *ofst, (u32)(cur_t >> 32), (u32)cur_t, mod, b_ofst_i->conf_lvl, (u32)b_ofst); return true; } /* * Synchronize TBTT of target role with TBTT of sourec role * Assume TBTT of target role is locate in Mod(Tgt Tsf) = 0 * @sync_ofst: Offset between TBTT of target role and TBTT of sourec role. Unit: TU * @sync_now_once: Sync once time right now. * @*diff_t : output diff_tsf. Unit: TU */ enum rtw_phl_status rtw_phl_tbtt_sync(struct phl_info_t *phl, struct rtw_wifi_role_t *src_role, struct rtw_wifi_role_t *tgt_role, u16 sync_ofst, bool sync_now_once, u16 *diff_t) { enum rtw_phl_status status = RTW_PHL_STATUS_FAILURE; u32 tsf_h = 0, tsf_l = 0; u64 tsf = 0, tgt_tsf = 0, bcn_intvl = 0; u16 ofst = 0; u64 diff_tsf = 0; enum hal_tsf_sync_act act = sync_now_once ? HAL_TSF_SYNC_NOW_ONCE : HAL_TSF_EN_SYNC_AUTO; if (RTW_HAL_STATUS_SUCCESS != rtw_hal_get_tsf(phl->hal, src_role->hw_port, &tsf_h, &tsf_l)) { PHL_TRACE(COMP_PHL_DBG, _PHL_INFO_, "rtw_phl_tbtt_sync(): Get tsf fail, src_role->id(%d)\n", src_role->id); goto exit; } bcn_intvl = phl_role_get_bcn_intvl(phl, tgt_role); if (bcn_intvl == 0) { PHL_TRACE(COMP_PHL_DBG, _PHL_ERR_, "rtw_phl_tbtt_sync(): bcn_intvl == 0, tgt_role->id(%d)\n", tgt_role->id); goto exit; } tsf = tsf_h; tsf = tsf << 32; tsf |= tsf_l; /*calculate the value between current TSF and TBTT*/ phl_calc_offset_from_tbtt(phl, src_role, tsf, &ofst); tgt_tsf = (tsf + sync_ofst * TU) - ofst * TU; /*Find diff_tsf, let Mod((tgt_tsf + diff_tsf), bcn_intvl) = 0*/ diff_tsf = bcn_intvl * TU - _os_modular64(tgt_tsf, bcn_intvl * TU); diff_tsf = _os_division64(diff_tsf, TU); PHL_TRACE(COMP_PHL_DBG, _PHL_INFO_, "rtw_phl_tbtt_sync(): diff_tsf(%d), sync_ofst(%d), ofst(%d)\n", (u32)diff_tsf, sync_ofst, (u32)ofst); if (RTW_HAL_STATUS_SUCCESS != rtw_hal_tsf_sync(phl->hal, src_role->hw_port, tgt_role->hw_port, src_role->hw_band, (s32)diff_tsf, act)) { PHL_TRACE(COMP_PHL_DBG, _PHL_ERR_, "rtw_phl_tbtt_sync(): Sync tsf fail\n"); goto exit; } if (RTW_HAL_STATUS_SUCCESS == rtw_hal_get_tsf(phl->hal, src_role->hw_port, &tsf_h, &tsf_l)) { PHL_TRACE(COMP_PHL_DBG, _PHL_INFO_, "rtw_phl_tbtt_sync(): tsf_src(0x%08x %08x)\n", tsf_h, tsf_l); } if (RTW_HAL_STATUS_SUCCESS == rtw_hal_get_tsf(phl->hal, tgt_role->hw_port, &tsf_h, &tsf_l)) { PHL_TRACE(COMP_PHL_DBG, _PHL_INFO_, "rtw_phl_tbtt_sync(): tsf_tgt(0x%08x %08x)\n", tsf_h, tsf_l); } *diff_t = (u16)diff_tsf; status = RTW_PHL_STATUS_SUCCESS; exit: return status; }