869 lines
20 KiB
C
Raw Normal View History

2025-05-10 21:58:58 +08:00
/*
* Copyright (C) 2016 Spreadtrum Communications Inc.
*
* Authors :
* star.liu <star.liu@spreadtrum.com>
* yifei.li <yifei.li@spreadtrum.com>
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* 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.
*/
#include "qos.h"
#include "msg.h"
#include "sprdwl.h"
unsigned int g_qos_enable;
#if 0
/*initial array of dscp map to priority
*map array will be changed by qos remap
*/
#define DSCP_MAX_VALUE 64
const unsigned char default_dscp2priomap[DSCP_MAX_VALUE] = {
0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 4, 0, 4, 0, 4, 0,
4, 0, 4, 0, 4, 0, 4, 0,
4, 0, 0, 0, 0, 0, 6, 0,
6, 0, 0, 0, 0, 0, 0, 0,
6, 0, 0, 0, 0, 0, 0, 0
};
#endif
#ifdef WMMAC_WFA_CERTIFICATION
bool g_wmmac_available[NUM_AC] = {false, false, false, false};
u32 g_wmmac_admittedtime[NUM_AC] = {0};
u32 g_wmmac_usedtime[NUM_AC] = {0};
struct wmm_ac_ts_t g_sta_ts_info[NUM_TID];
unsigned int wmmac_ratio = 10;
#endif
struct qos_map_set g_11u_qos_map;
void init_default_qos_map(void)
{
u8 index;
for (index = 0; index < QOS_MAP_MAX_DSCP_EXCEPTION; index++) {
g_11u_qos_map.qos_exceptions[index].dscp = 0xFF;
g_11u_qos_map.qos_exceptions[index].up = prio_0;
}
index = 0;
g_11u_qos_map.qos_ranges[index].low = 0x0; /*IP-PL0*/
g_11u_qos_map.qos_ranges[index].high = 0x0;
g_11u_qos_map.qos_ranges[index].up = prio_0;
index++;
g_11u_qos_map.qos_ranges[index].low = 0x3; /*IP-PL3*/
g_11u_qos_map.qos_ranges[index].high = 0x3;
g_11u_qos_map.qos_ranges[index].up = prio_0;
index++;
g_11u_qos_map.qos_ranges[index].low = 0x1; /*IP-PL1*/
g_11u_qos_map.qos_ranges[index].high = 0x2; /*IP-PL2*/
g_11u_qos_map.qos_ranges[index].up = prio_1;
index++;
g_11u_qos_map.qos_ranges[index].low = 0x4; /*IP-PL4*/
g_11u_qos_map.qos_ranges[index].high = 0x5; /*IP-PL5*/
g_11u_qos_map.qos_ranges[index].up = prio_4;
index++;
g_11u_qos_map.qos_ranges[index].low = 0x6; /*IP-PL6*/
g_11u_qos_map.qos_ranges[index].high = 0x7; /*IP-PL7*/
g_11u_qos_map.qos_ranges[index].up = prio_6;
}
unsigned int pkt_get_prio(void *skb, int data_offset, unsigned char *tos)
{
struct ether_header *eh;
struct ethervlan_header *evh;
unsigned char *pktdata;
unsigned int priority = prio_6;
pktdata = ((struct sk_buff *)(skb))->data + data_offset;
eh = (struct ether_header *)pktdata;
if (eh->ether_type == cpu_to_be16(ETHER_TYPE_8021Q)) {
unsigned short vlan_tag;
int vlan_prio;
evh = (struct ethervlan_header *)eh;
vlan_tag = be16_to_cpu(evh->vlan_tag);
vlan_prio = (int)(vlan_tag >> VLAN_PRI_SHIFT) & VLAN_PRI_MASK;
priority = vlan_prio;
} else {
unsigned char *ip_body = pktdata + sizeof(struct ether_header);
unsigned char tos_tc = IP_TOS46(ip_body) & 0xE0;
*tos = IP_TOS46(ip_body);
switch (tos_tc) {
case 0x00:
case 0x60:
priority = prio_0;/*BE*/
break;
case 0x20:
case 0x40:
priority = prio_1;/*BK*/
break;
case 0x80:
case 0xA0:
priority = prio_4;/*VI*/
break;
default:
priority = prio_6;/*VO*/
break;
}
}
PKT_SET_PRIO(skb, priority);
return priority;
}
static const u8 up_to_ac[] = {
0, /*SPRDWL_AC_BE*/
1, /*SPRDWL_AC_BK*/
1, /*SPRDWL_AC_BK*/
0, /*SPRDWL_AC_BE*/
4, /*SPRDWL_AC_VI*/
4, /*SPRDWL_AC_VI*/
6, /*SPRDWL_AC_VO*/
6 /*SPRDWL_AC_VO*/
};
#if 0
/* up range from low to high with up value */
static bool
qos_up_table_set(u8 *dscp2up_table, u8 up, struct dscp_range *dscp_range)
{
int i;
if (up > 7 || dscp_range->low > dscp_range->high ||
dscp_range->low >= DSCP_MAX_VALUE ||
dscp_range->high >= DSCP_MAX_VALUE) {
return false;
}
for (i = dscp_range->low; i <= dscp_range->high; i++)
dscp2up_table[i] = up_to_ac[up];
return true;
}
/* set user priority table */
void qos_set_dscp2up_table(unsigned char *dscp2up_table,
struct qos_capab_info *qos_map_ie)
{
unsigned char len;
struct dscp_range dscp_range;
int i;
struct dscp_range *range;
unsigned char except_len;
u8 *except_ptr;
u8 *range_ptr;
if (dscp2up_table == NULL || qos_map_ie == NULL)
return;
/* length of QoS Map IE must be 16+n*2, n is number of exceptions */
if (qos_map_ie != NULL && qos_map_ie->id == DOT11_MNG_QOS_MAP_ID &&
qos_map_ie->len >= QOS_MAP_FIXED_LENGTH &&
(qos_map_ie->len % 2) == 0) {
except_ptr = (u8 *)qos_map_ie->qos_info;
len = qos_map_ie->len;
except_len = len - QOS_MAP_FIXED_LENGTH;
range_ptr = except_ptr + except_len;
/* fill in ranges */
for (i = 0; i < QOS_MAP_FIXED_LENGTH; i += 2) {
range = (struct dscp_range *)(&range_ptr[i]);
if (range->low == 255 && range->high == 255)
continue;
if (!qos_up_table_set(dscp2up_table, i / 2, range)) {
/* reset the table on failure */
memcpy(dscp2up_table, default_dscp2priomap,
DSCP_MAX_VALUE);
return;
}
}
/* update exceptions */
for (i = 0; i < except_len; i += 2) {
struct dscp_exception *exception =
(struct dscp_exception *)(&except_ptr[i]);
unsigned char dscp = exception->dscp;
unsigned char up = exception->up;
dscp_range.high = dscp;
dscp_range.low = dscp;
/* exceptions with invalid dscp/up are ignored */
qos_up_table_set(dscp2up_table, up, &dscp_range);
}
}
wl_hex_dump(L_DBG, "qos up table: ", DUMP_PREFIX_OFFSET,
16, 1, dscp2up_table, DSCP_MAX_VALUE, 0);
}
/*Todo*/
int fq_table[5][4] = {
{0, 0, 0, 0},
{30, 0, 0, 0},
{30, 20, 0, 0},
{40, 25, 20, 0},
{40, 30, 20, 10}
};
/*Todo*/
int wfq_table[5][4] = {
{0, 0, 0, 0},
{10, 0, 0, 0},
{20, 10, 0, 0},
{30, 20, 10, 0},
{40, 30, 20, 10}
};
/*Todo*/
int fd_special_table[2][2] = {
{30, 10},
{30, 10}
};
/*time slot ratio based on WFA spec*/
int fd_ratio_table[3] = {7, /*vo: 87%, vi:13%*/
9, /*vi:90%, be:10%*/
5};/*be:81%, bk:19%*/
#endif
void qos_enable(int flag)
{
g_qos_enable = flag;
}
void qos_init(struct tx_t *tx_t_list)
{
int i, j;
/*tx_t_list->index = SPRDWL_AC_VO;*/
for (i = 0; i < SPRDWL_AC_MAX; i++) {
for (j = 0; j < MAX_LUT_NUM; j++) {
INIT_LIST_HEAD(&tx_t_list->q_list[i].p_list[j].head_list);
spin_lock_init(&tx_t_list->q_list[i].p_list[j].p_lock);
atomic_set(&tx_t_list->q_list[i].p_list[j].l_num, 0);
}
}
#if 0
if (tx_t_list->dscp2up_table == NULL) {
tx_t_list->dscp2up_table = kzalloc(DSCP_MAX_VALUE, GFP_KERNEL);
if (tx_t_list->dscp2up_table == NULL)
wl_err("%s malloc dscp2up_table fail\n", __func__);
else
memcpy(tx_t_list->dscp2up_table, default_dscp2priomap,
DSCP_MAX_VALUE);
}
#endif
}
#if 0
void qos_deinit(struct tx_t *qos)
{
if (qos->dscp2up_table != NULL) {
kfree(qos->dscp2up_table);
qos->dscp2up_table = NULL;
}
}
struct qos_capab_info *qos_parse_capab_info(void *buf, int buflen, uint key)
{
struct qos_capab_info *capab_info;
int totlen;
capab_info = (struct qos_capab_info *)buf;
if (capab_info == NULL)
return NULL;
totlen = buflen;
/* find tagged parameter */
while (totlen >= TLV_HDR_LEN) {
int len = capab_info->len;
/* validate remaining totlen */
if ((capab_info->id == key) &&
(totlen >= (int)(len + TLV_HDR_LEN)))
return capab_info;
capab_info = (struct qos_capab_info *)
((u8 *)capab_info + (len + TLV_HDR_LEN));
totlen -= (len + TLV_HDR_LEN);
}
return NULL;
}
#endif
unsigned int qos_match_q(void *skb, int data_offset)
{
int priority;
struct ether_header *eh;
qos_head_type_t data_type = SPRDWL_AC_BE;
unsigned char tos = 0;
if (0 == g_qos_enable)
return SPRDWL_AC_BE;
/* vo vi bk be*/
eh =
(struct ether_header *)(((struct sk_buff *)(skb))->data + data_offset);
if (cpu_to_be16(ETHER_TYPE_IP) != eh->ether_type &&
cpu_to_be16(ETHER_TYPE_IPV6) != eh->ether_type) {
goto OUT;
}
priority = pkt_get_prio(skb, data_offset, &tos);
switch (priority) {
case prio_1:
data_type = SPRDWL_AC_BK;
break;
case prio_4:
data_type = SPRDWL_AC_VI;
break;
case prio_6:
data_type = SPRDWL_AC_VO;
break;
default:
data_type = SPRDWL_AC_BE;
break;
}
OUT:
/*return data_type as qos queue index*/
return data_type;
}
unsigned int tid_map_to_qosindex(unsigned char tid)
{
qos_head_type_t qos_index = SPRDWL_AC_BE;
switch (tid) {
case prio_1:
qos_index = SPRDWL_AC_BK;
break;
case prio_4:
qos_index = SPRDWL_AC_VI;
break;
case prio_6:
qos_index = SPRDWL_AC_VO;
break;
default:
qos_index = SPRDWL_AC_BE;
break;
}
/*return data_type as qos queue index*/
return qos_index;
}
unsigned int get_tid_qosindex(void *skb, int data_offset, unsigned char *tid, unsigned char *tos)
{
int priority;
struct ether_header *eh;
if (0 == g_qos_enable)
return SPRDWL_AC_BE;
/* vo vi bk be*/
eh =
(struct ether_header *)(((struct sk_buff *)(skb))->data + data_offset);
/*if (cpu_to_be16(ETHER_TYPE_IP) != eh->ether_type &&
cpu_to_be16(ETHER_TYPE_IPV6) != eh->ether_type) {
goto OUT;
}*/
priority = pkt_get_prio(skb, data_offset, tos);
*tid = priority;
/*return data_type as qos queue index*/
return tid_map_to_qosindex(*tid);
}
#if 0
void qos_wfq(struct tx_t *qos)
{
int t, i, j, weight, q[4] = {0}, list_num[4] = {0, 0, 0, 0};
for (i = 0, t = 0, weight = 0; i < 4; i++) {
for (j = 0; j < MAX_LUT_NUM; j++)
list_num[i] += get_list_num(&qos->q_list[i].p_list[j].head_list);
if (list_num[i] > 0) {
q[t] = i;
t++;
}
}
if (0 == t)
return;
for (i = 0; i < t; i++)
weight += wfq_table[t][i];
for (i = 0; i < t; i++)
qos->going[q[i]] = wfq_table[t][i] *
list_num[i] / weight;
}
void qos_fq(struct tx_t *qos)
{
int i, j, t, k, q[4] = {0}, list_num[4] = {0, 0, 0, 0};
for (i = 0, t = 0; i < 4; i++) {
for (j = 0; j < MAX_LUT_NUM; j++)
list_num[i] += get_list_num(&qos->q_list[i].p_list[j].head_list);
if (list_num[i] > 0) {
q[t] = i;
t++;
}
}
if (0 == t)
return;
/* vi & bk*/
if ((2 == t) && (1 == q[0]) && (2 == q[1])) {
qos->going[SPRDWL_AC_VI] = fd_special_table[0][0];
qos->going[SPRDWL_AC_BE] = fd_special_table[0][1];
if (list_num[SPRDWL_AC_VI] < qos->going[1])
qos->going[SPRDWL_AC_VI] =
list_num[SPRDWL_AC_VI];
if (list_num[SPRDWL_AC_BE] < qos->going[2])
qos->going[SPRDWL_AC_BE] =
list_num[SPRDWL_AC_BE];
return;
}
/*bk & be*/
if ((2 == t) && (2 == q[0]) && (3 == q[1])) {
qos->going[2] = fd_special_table[1][0];
qos->going[3] = fd_special_table[1][1];
if (list_num[SPRDWL_AC_BE] < qos->going[2])
qos->going[SPRDWL_AC_BE] =
list_num[SPRDWL_AC_BE];
if (list_num[SPRDWL_AC_BK] < qos->going[3])
qos->going[SPRDWL_AC_BK] =
list_num[SPRDWL_AC_BK];
return;
}
for (i = 0; i < t; i++) {
k = 0;
qos->going[q[i]] = fq_table[t][i];
if (list_num[q[i]] < qos->going[q[i]]) {
k = list_num[q[i]];
qos->going[q[i]] = k;
}
}
}
/*get time slot ratio between higher priority stream and lower*/
int qos_fq_ratio(struct tx_t *qos)
{
int i, j, t, q[4] = {0};
for (i = 0, t = 0; i < 4; i++) {
for (j = 0; j < MAX_LUT_NUM; j++) {
if (!list_empty(&qos->q_list[i].p_list[j].head_list)) {
q[t] = i;
t++;
break;
}
}
}
if (0 == t)
return t;
/*vi & vo, two streams coexist based on WFA spec*/
if ((2 == t) && (0 == q[0]) && (1 == q[1])) {
qos->ratio = fd_ratio_table[0];
qos->ac_index = SPRDWL_AC_VO;
return t;
}
/* vi & be*/
if ((2 == t) && (1 == q[0]) && (2 == q[1])) {
qos->ratio = fd_ratio_table[1];
qos->ac_index = SPRDWL_AC_VI;
return t;
}
/*be & bk*/
if ((2 == t) && (2 == q[0]) && (3 == q[1])) {
qos->ratio = fd_ratio_table[2];
qos->ac_index = SPRDWL_AC_BE;
return t;
}
/*ac_index indicate which two qos streams coexist*/
qos->ac_index = SPRDWL_AC_MAX;
qos->ratio = 0;
return t;
}
void qos_sched(struct tx_t *qos, struct qos_list **q, int *num)
{
int round, j;
if (0 == g_qos_enable) {
*q = &qos->q_list[SPRDWL_AC_BE];
for (j = 0; j < MAX_LUT_NUM; j++)
*num += get_list_num(&qos->q_list[SPRDWL_AC_BE].p_list[j].head_list);
return;
}
for (round = 0; round < 4; round++) {
if ((SPRDWL_AC_VO == qos->index) &&
(0 == qos->going[SPRDWL_AC_VO]))
/*qos_fq(qos);*/
qos_wfq(qos);
if (qos->going[qos->index] > 0)
break;
qos->index = INCR_RING_BUFF_INDX(qos->index, 4);
}
*q = &qos->q_list[qos->index];
*num = qos->going[qos->index];
}
#endif
int get_list_num(struct list_head *list)
{
int num = 0;
struct list_head *pos;
struct list_head *n_list;
if (list_empty(list))
return 0;
list_for_each_safe(pos, n_list, list)
num++;
return num;
}
#ifdef WMMAC_WFA_CERTIFICATION
/*init wmmac params, include timer and ac params*/
void wmm_ac_init(struct sprdwl_priv *priv)
{
u8 ac;
for (ac = 0; ac < NUM_AC; ac++) {
g_wmmac_usedtime[ac] = 0;
g_wmmac_available[ac] = false;
g_wmmac_admittedtime[ac] = 0;
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0)
timer_setup(&priv->wmmac.wmmac_edcaf_timer, update_wmmac_edcaftime_timeout, 0);
timer_setup(&priv->wmmac.wmmac_vo_timer, update_wmmac_vo_timeout, 0);
timer_setup(&priv->wmmac.wmmac_vi_timer, update_wmmac_vi_timeout, 0);
#else
setup_timer(&priv->wmmac.wmmac_edcaf_timer, update_wmmac_edcaftime_timeout,
(unsigned long)priv);
setup_timer(&priv->wmmac.wmmac_vo_timer, update_wmmac_vo_timeout,
(unsigned long)priv);
setup_timer(&priv->wmmac.wmmac_vi_timer, update_wmmac_vi_timeout,
(unsigned long)priv);
#endif
memset(&priv->wmmac.ac[0], 0, 4*sizeof(struct wmm_ac_params));
}
void reset_wmmac_parameters(struct sprdwl_priv *priv)
{
u8 ac;
for (ac = 0; ac < NUM_AC; ac++) {
g_wmmac_usedtime[ac] = 0;
g_wmmac_available[ac] = false;
g_wmmac_admittedtime[ac] = 0;
}
if (timer_pending(&priv->wmmac.wmmac_edcaf_timer))
del_timer_sync(&priv->wmmac.wmmac_edcaf_timer);
if (timer_pending(&priv->wmmac.wmmac_vo_timer))
del_timer_sync(&priv->wmmac.wmmac_vo_timer);
if (timer_pending(&priv->wmmac.wmmac_vi_timer))
del_timer_sync(&priv->wmmac.wmmac_vi_timer);
memset(&priv->wmmac.ac[0], 0, 4*sizeof(struct wmm_ac_params));
}
void reset_wmmac_ts_info(void)
{
u8 tsid;
for (tsid = 0; tsid < NUM_TID; tsid++)
remove_wmmac_ts_info(tsid);
}
unsigned int priority_map_to_qos_index(int priority)
{
qos_head_type_t qos_index = SPRDWL_AC_BE;
switch (up_to_ac[priority]) {
case prio_1:
qos_index = SPRDWL_AC_BK;
break;
case prio_4:
qos_index = SPRDWL_AC_VI;
break;
case prio_6:
qos_index = SPRDWL_AC_VO;
break;
default:
qos_index = SPRDWL_AC_BE;
break;
}
/*return data_type as qos queue index*/
return qos_index;
}
unsigned int map_priority_to_edca_ac(int priority)
{
int ac;
switch (priority) {
case 01:
case 02:
ac = AC_BK;
break;
case 04:
case 05:
ac = AC_VI;
break;
case 06:
case 07:
ac = AC_VO;
break;
case 00:
case 03:
default:
ac = AC_BE;
break;
}
/*return data_type as qos queue index*/
return ac;
}
unsigned int map_edca_ac_to_priority(u8 ac)
{
unsigned int priority;
switch (ac) {
case AC_BK:
priority = prio_1;
break;
case AC_VI:
priority = prio_4;
break;
case AC_VO:
priority = prio_6;
break;
case AC_BE:
default:
priority = prio_0;
break;
}
return priority;
}
void update_wmmac_ts_info(u8 tsid, u8 up, u8 ac, bool status, u16 admitted_time)
{
g_sta_ts_info[tsid].exist = status;
g_sta_ts_info[tsid].ac = ac;
g_sta_ts_info[tsid].up = up;
g_sta_ts_info[tsid].admitted_time = admitted_time;
}
u16 get_wmmac_admitted_time(u8 tsid)
{
u16 value = 0;
if (g_sta_ts_info[tsid].exist == true)
value = g_sta_ts_info[tsid].admitted_time;
return value;
}
void remove_wmmac_ts_info(u8 tsid)
{
memset(&(g_sta_ts_info[tsid]), 0, sizeof(struct wmm_ac_ts_t));
}
void update_admitted_time(struct sprdwl_priv *priv, u8 tsid, u16 medium_time, bool increase)
{
u8 ac = g_sta_ts_info[tsid].ac;
if (true == increase) {
/*mediumtime is in unit of 32 us, admittedtime is in unit of us*/
g_wmmac_admittedtime[ac] += (medium_time<<5);
mod_timer(&priv->wmmac.wmmac_edcaf_timer,
jiffies + WMMAC_EDCA_TIMEOUT_MS * HZ / 1000);
/*replace the usedtime logic method with timer counter, just for simplify for the WFA certification*/
if (ac == AC_VO)
mod_timer(&priv->wmmac.wmmac_vo_timer,
jiffies + usecs_to_jiffies(g_wmmac_admittedtime[ac] * WMMAC_TIME_RATIO));
else if (ac == AC_VI)
mod_timer(&priv->wmmac.wmmac_vi_timer,
jiffies + usecs_to_jiffies(g_wmmac_admittedtime[ac] * WMMAC_TIME_RATIO));
} else {
if (g_wmmac_admittedtime[ac] > (medium_time<<5))
g_wmmac_admittedtime[ac] -= (medium_time<<5);
else {
g_wmmac_admittedtime[ac] = 0;
if (timer_pending(&priv->wmmac.wmmac_edcaf_timer))
del_timer_sync(&priv->wmmac.wmmac_edcaf_timer);
if (timer_pending(&priv->wmmac.wmmac_vo_timer))
del_timer_sync(&priv->wmmac.wmmac_vo_timer);
if (timer_pending(&priv->wmmac.wmmac_vi_timer))
del_timer_sync(&priv->wmmac.wmmac_vi_timer);
}
}
g_wmmac_available[ac] = (g_wmmac_usedtime[ac] < g_wmmac_admittedtime[ac]);
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0)
void update_wmmac_edcaftime_timeout(struct timer_list *t)
{
struct sprdwl_priv *priv = from_timer(priv, t, wmmac.wmmac_edcaf_timer);
#else
void update_wmmac_edcaftime_timeout(unsigned long data)
{
struct sprdwl_priv *priv = (struct sprdwl_priv *)data;
#endif
/*restart edcaf timer per second*/
mod_timer(&priv->wmmac.wmmac_edcaf_timer, jiffies + WMMAC_EDCA_TIMEOUT_MS * HZ / 1000);
if (g_wmmac_admittedtime[AC_VO] > 0) {
g_wmmac_usedtime[AC_VO] = 0;
g_wmmac_available[AC_VO] = true;
mod_timer(&priv->wmmac.wmmac_vo_timer,
jiffies + usecs_to_jiffies(g_wmmac_admittedtime[AC_VO] * WMMAC_TIME_RATIO));
}
if (g_wmmac_admittedtime[AC_VI] > 0) {
g_wmmac_usedtime[AC_VI] = 0;
g_wmmac_available[AC_VI] = true;
mod_timer(&priv->wmmac.wmmac_vi_timer,
jiffies + usecs_to_jiffies(g_wmmac_admittedtime[AC_VI] * WMMAC_TIME_RATIO));
}
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0)
void update_wmmac_vo_timeout(struct timer_list *t)
#else
void update_wmmac_vo_timeout(unsigned long data)
#endif
{
g_wmmac_usedtime[AC_VO] = g_wmmac_admittedtime[AC_VO];
g_wmmac_available[AC_VO] = false;
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0)
void update_wmmac_vi_timeout(struct timer_list *t)
#else
void update_wmmac_vi_timeout(unsigned long data)
#endif
{
g_wmmac_usedtime[AC_VI] = g_wmmac_admittedtime[AC_VI];
g_wmmac_available[AC_VI] = false;
}
/*change priority according to the g_wmmac_available value */
unsigned int change_priority_if(struct sprdwl_priv *priv, unsigned char *tid, unsigned char *tos, u16 len)
{
unsigned int qos_index, ac;
int match_index = 0;
unsigned char priority = *tos;
priority >>= 2;
for (match_index = 0; match_index < QOS_MAP_MAX_DSCP_EXCEPTION; match_index++) {
if (priority == g_11u_qos_map.qos_exceptions[match_index].dscp) {
*tid = g_11u_qos_map.qos_exceptions[match_index].up;
break;
}
}
if (match_index >= QOS_MAP_MAX_DSCP_EXCEPTION) {
for (match_index = 0; match_index < 8; match_index++) {
if ((priority >= g_11u_qos_map.qos_ranges[match_index].low) &&
(priority <= g_11u_qos_map.qos_ranges[match_index].high)) {
*tid = g_11u_qos_map.qos_ranges[match_index].up;
break;
}
}
}
if (1 == g_qos_enable) {
ac = map_priority_to_edca_ac(*tid);
while (ac != 0) {
if (!!(priv->wmmac.ac[ac].aci_aifsn & WMM_AC_ACM)) {
/*current ac is available, use it directly*/
if (true == g_wmmac_available[ac])
{
/* use wmmac_ratio to adjust ac used time */
/* it is rough calc method: (data_len * 8) * ratio / data_rate, here , use 54Mbps as common usage */
g_wmmac_usedtime[ac] += (len + 4) * 8 * wmmac_ratio / 10 / 54;
g_wmmac_available[ac] = (g_wmmac_usedtime[ac] < g_wmmac_admittedtime[ac]);
break;
}
if ((g_wmmac_available[ac] == false) && (g_wmmac_usedtime[ac] != 0))
return SPRDWL_AC_MAX;
/*current ac is not available, maybe usedtime > admitted time*/
/*downgrade to lower ac, then try again*/
ac--;
} else {
break;
}
}
*tid = map_edca_ac_to_priority(ac);
}
switch (*tid) {
case prio_1:
qos_index = SPRDWL_AC_BK;
break;
case prio_4:
qos_index = SPRDWL_AC_VI;
break;
case prio_6:
qos_index = SPRDWL_AC_VO;
break;
default:
qos_index = SPRDWL_AC_BE;
break;
}
/*return data_type as qos queue index*/
return qos_index;
}
const u8 *get_wmm_ie(u8 *res, u16 ie_len, u8 ie, uint oui, uint oui_type)
{
const u8 *end, *pos;
pos = res;
end = pos + ie_len;
while (pos + 1 < end) {
if (pos + 2 + pos[1] > end)
break;
/*try to find VENDOR_SPECIFIC ie, which wmm ie located*/
if (pos[0] == ie) {
/*match the OUI_MICROSOFT 0x0050f2 ie, and WMM ie*/
if ((((pos[2] << 16) | (pos[3] << 8) | pos[4]) == oui) &&
(pos[5] == WMM_OUI_TYPE)) {
pos += 2;
return pos;
}
break;
}
pos += 2 + pos[1];
}
return NULL;
}
#endif