Files

582 lines
17 KiB
C
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* Mac80211 driver for BES2600 device
*
* Copyright (c) 2022, Bestechnic
* Author:
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/types.h>
#include <linux/version.h>
#include "bes2600.h"
#include "epta_coex.h"
#include "epta_request.h"
#include "bes_log.h"
static bool coex_ps_en;
static bool coex_fdd_mode; /* fdd or fdd hybrid */
static uint8_t epta_conn_state; /* dafault invalid stat */
static uint32_t epta_freeze_bitmap; /*bit0: conn, bit1: tp, bit2: tts */
static int epta_freezed_wlan_duration[EPTA_FREEZE_MAX] = {0};
static int epta_freezed_wlan_duration_cfg = 0;
static int wlan_duration_cfg;
static int bt_duration_cfg;
static int hw_epta_enable_cfg; /* bit0~bit1: epta mode, bit7: prot flag, other bits: reserved */
static int epta_ps_mode; /* 0 - normal mode, 1 - power save mode */
static int epta_adjust_cnt;
static int coex_bt_state;
static uint32_t g_coex_mode;
#define EPTA_MODE_CFG (hw_epta_enable_cfg & 0x03)
/*
* This function will send wsm command to set epta module with inputing parameters.
* The inputing parameters may not be used really if epta_freeze_bitmap is not zero.
* @hw_epta_enable: when hw_epta_enable is 1, use hardware pta, epta does not work,
wlan_duration and bt_duration mean nothing.
* @wlan_duration and bt_duration: need set hw_epta_enable to 0, use software epta.
Allow 10~100000 us.
* e.g.
* coex_set_epta_params(20000, 20000, 0); means use software epta, wlan get 20ms, bt get 20ms.
* coex_set_epta_params(20000, 20000, 1); means use hardware pta, hardware arbitrate.
*/
int coex_set_epta_params(struct bes2600_common *hw_priv, int wlan_duration, int bt_duration, int hw_epta_enable)
{
struct wsm_epta_msg msg;
if (hw_priv == NULL) {
bes_devel("hw_priv is NULL\n");
return -1;
}
bes_devel("set epta w:%d hw:%d wc:%d fbit:%x fw:%d\n", wlan_duration, hw_epta_enable,
wlan_duration_cfg, epta_freeze_bitmap, epta_freezed_wlan_duration_cfg);
if (wlan_duration_cfg == wlan_duration && bt_duration_cfg == bt_duration && hw_epta_enable_cfg == hw_epta_enable) {
bes_devel("same epta params\n");
return 0;
}
wlan_duration_cfg = wlan_duration;
bt_duration_cfg = bt_duration;
hw_epta_enable_cfg = hw_epta_enable;
/* If in freeze mode, use freezed wlan duration; */
if (epta_freeze_bitmap) {
if (epta_freeze_bitmap & EPTA_FREEZE_SCANNING) {
bes_devel("skip bt state in wifi scanning");
return 0;
}
/*
if new wlan duration is more than freezed one, update fw settings with new duration
if new wlan duration is less than freezed one, update fw settings with freezed duration
*/
if (wlan_duration < epta_freezed_wlan_duration_cfg) {
bes_devel("epta_freeze_bitmap:0x%x, wlan:%u < %u",
epta_freeze_bitmap, wlan_duration, epta_freezed_wlan_duration_cfg);
wlan_duration = epta_freezed_wlan_duration_cfg;
bt_duration = EPTA_FREEZE_TDD_PERIOD - epta_freezed_wlan_duration_cfg;
}
}
/* epta disconnect status,force use HW epta */
if (epta_conn_state == EPTA_STATE_WIFI_DISCONNECTED && hw_epta_enable == 0)
hw_epta_enable = 1;
msg.wlan_duration = wlan_duration;
msg.bt_duration = bt_duration;
msg.hw_epta_enable = hw_epta_enable;
return wsm_epta_cmd(hw_priv, &msg);
}
static int coex_epta_freeze_update(struct bes2600_common *hw_priv)
{
struct wsm_epta_msg msg;
int i = 0;
int max_freeze = 0;
if (hw_priv == NULL) {
bes_devel("hw_priv is NULL\n");
return -1;
}
bes_devel("epta update. bitmap:0x%x. fw:%d",
epta_freeze_bitmap, epta_freezed_wlan_duration_cfg);
bes_devel("%s, %d", __FUNCTION__, hw_epta_enable_cfg);
/* Exit freeze mode and recover the configuration of epta module; */
if (epta_freeze_bitmap == 0) {
epta_freezed_wlan_duration_cfg = 0;
return -1;
}
/* Enter freeze mode and set the configuration of the epta module; */
/* select max wifi freeze value */
for (i = 0; i < EPTA_FREEZE_MAX; i++) {
if (max_freeze < epta_freezed_wlan_duration[i])
max_freeze = epta_freezed_wlan_duration[i];
}
/* same cfg don't change it */
if (max_freeze == epta_freezed_wlan_duration_cfg)
return 0;
epta_freezed_wlan_duration_cfg = max_freeze;
/* only support epta_freezed_wlan_duration_cfg > wlan_duration_cfg */
// wlan_duration_cfg may be override by wsm_epta_cmd
// if (max_freeze <= wlan_duration_cfg) {
// return 0;
// }
msg.wlan_duration = max_freeze;
msg.bt_duration = EPTA_FREEZE_TDD_PERIOD - max_freeze;
msg.hw_epta_enable = 0x00;
return wsm_epta_cmd(hw_priv, &msg);
}
/*
* coex_epta_freeze
* freeze coex duration for specific senario
* @wlan_duration: 20~100ms
* @type:
* EPTA_FREEZE_CONENCTING,
* EPTA_FREEZE_TTS,
* EPTA_FREEZE_THP,
* note: the value of wlan_duration should be less than 100000.
*/
static int coex_epta_freeze(struct bes2600_common *hw_priv, int wlan_duration, uint32_t type)
{
uint32_t i;
epta_freeze_bitmap |= type;
for (i = 0; i < EPTA_FREEZE_MAX; ++i) {
if (type & (1 << i))
epta_freezed_wlan_duration[i] = wlan_duration;
}
/* reset epta value */
epta_ps_mode = 0;
epta_adjust_cnt = 0;
return coex_epta_freeze_update(hw_priv);
}
static int coex_epta_recover_update(struct bes2600_common *hw_priv)
{
struct wsm_epta_msg msg;
int i = 0;
int max_freeze = 0;
if (hw_priv == NULL) {
bes_devel("hw_priv is NULL\n");
return -1;
}
bes_devel("epta update. bitmap:0x%x. fw:%d",
epta_freeze_bitmap, epta_freezed_wlan_duration_cfg);
bes_devel("%s, %d", __FUNCTION__, hw_epta_enable_cfg);
/* Exit freeze mode and recover the configuration of epta module; */
if (epta_freeze_bitmap == 0) {
if (epta_freezed_wlan_duration_cfg == 0)
return -1;
epta_freezed_wlan_duration_cfg = 0;
// coex_set_epta_params will skip epta:0 after got_ip(use connected epta3)
msg.wlan_duration = wlan_duration_cfg;
msg.bt_duration = bt_duration_cfg;
msg.hw_epta_enable = hw_epta_enable_cfg;
return wsm_epta_cmd(hw_priv, &msg);
}
/* Enter freeze mode and set the configuration of the epta module; */
/* select max wifi freeze value */
for (i = 0; i < EPTA_FREEZE_MAX; i++) {
if (max_freeze < epta_freezed_wlan_duration[i])
max_freeze = epta_freezed_wlan_duration[i];
}
epta_freezed_wlan_duration_cfg = max_freeze;
msg.wlan_duration = max_freeze;
msg.bt_duration = EPTA_FREEZE_TDD_PERIOD - max_freeze;
msg.hw_epta_enable = hw_epta_enable_cfg;
return wsm_epta_cmd(hw_priv, &msg);
}
static int coex_epta_recover(struct bes2600_common *hw_priv, uint32_t type)
{
uint32_t i;
if (epta_freeze_bitmap == 0)
return 0;
if (type == EPTA_FREEZE_ALL) {
for (i = 0; i < EPTA_FREEZE_MAX; i++) {
epta_freezed_wlan_duration[i] = 0;
}
epta_freeze_bitmap = 0;
} else {
epta_freeze_bitmap &= (~type);
for (i = 0; i < EPTA_FREEZE_MAX; ++i) {
if (type & (1 << i))
epta_freezed_wlan_duration[i] = 0;
}
}
return coex_epta_recover_update(hw_priv);
}
static int coex_epta_ps(struct bes2600_common *hw_priv, uint8_t enable)
{
struct wsm_epta_msg msg;
if (hw_priv == NULL) {
bes_devel("hw_priv is NULL\n");
return -1;
}
if (enable && !epta_ps_mode) {
epta_ps_mode = 1;
msg.wlan_duration = EPTA_PS_WLAN_DURATION;
msg.bt_duration = EPTA_PS_BT_DURATION;
msg.hw_epta_enable = 0;
return wsm_epta_cmd(hw_priv, &msg);
} else if (!enable && epta_ps_mode) {
epta_ps_mode = 0;
msg.wlan_duration = wlan_duration_cfg;
msg.bt_duration = bt_duration_cfg;
msg.hw_epta_enable = 0;
return wsm_epta_cmd(hw_priv, &msg);
}
return 0;
}
static int coex_epta_set_connect(struct bes2600_common *hw_priv, int wlan_duration, int bt_duration, int epta)
{
struct wsm_epta_msg msg;
if (!hw_priv) {
bes_devel("hw_priv is NULL\n");
return -1;
}
if (epta != 4) {
msg.wlan_duration = (wlan_duration_cfg >= wlan_duration) ? wlan_duration_cfg : wlan_duration;
msg.bt_duration = (wlan_duration_cfg >= wlan_duration) ? bt_duration_cfg : bt_duration;
} else {
msg.wlan_duration = wlan_duration;
msg.bt_duration = bt_duration;
}
msg.hw_epta_enable = epta;
return wsm_epta_cmd(hw_priv, &msg);
}
static void coex_set_wifi_state_to_fw(struct bes2600_common *hw_priv, uint8_t epta_conn_state)
{
static uint8_t last_state;
switch (epta_conn_state) {
case EPTA_STATE_WIFI_DISCONNECTED:
case EPTA_STATE_WIFI_CONNECTED:
if (last_state != epta_conn_state) {
last_state = epta_conn_state;
break;
} else {
return;
}
default:
return;
}
bes_devel("%s state%u\r", __func__, last_state);
switch (last_state) {
case EPTA_STATE_WIFI_DISCONNECTED:
wsm_wifi_status_cmd(hw_priv, (uint32_t)BWIFI_STATUS_IDLE);
break;
case EPTA_STATE_WIFI_CONNECTED:
wsm_wifi_status_cmd(hw_priv, (uint32_t)BWIFI_STATUS_CONNECTED);
break;
default:
break;
}
}
/* TDD mode conn status change */
void coex_set_wifi_conn(struct bes2600_common *hw_priv, uint8_t connect_status)
{
if (connect_status != EPTA_STATE_WIFI_SCAN_COMP) {
if (epta_conn_state == connect_status) {
bes_devel("same connect_status:%d\r", connect_status);
return;
}
}
if (connect_status == EPTA_STATE_WIFI_GOT_IP) {
if (epta_conn_state == EPTA_STATE_WIFI_DISCONNECTED || epta_conn_state == EPTA_STATE_WIFI_CONNECTING) {
bes_devel("ignore got ip in disconnected\r");
return;
}
}
epta_conn_state = connect_status;
bes_devel("%s connect_status=%d coex_mode=%d\r", __func__, connect_status, g_coex_mode);
coex_set_wifi_state_to_fw(hw_priv, epta_conn_state);
if (g_coex_mode & WIFI_COEX_MODE_FDD_BIT) {
if (g_coex_mode & WIFI_COEX_MODE_FDD_HYBRID_BIT) {
if (connect_status == EPTA_STATE_WIFI_CONNECTED) {
coex_epta_set_connect(hw_priv, wlan_duration_cfg, bt_duration_cfg, 3);
} else if (connect_status == EPTA_STATE_WIFI_DISCONNECTED) {
coex_epta_recover(hw_priv, EPTA_FREEZE_ALL); // wifi disconect need to recover all requests
/* HYBRID MODE: if wlan is disconnect, default use hw epta */
coex_epta_set_connect(hw_priv, 100000, 0, 1);
}
} else {
if (connect_status == EPTA_STATE_WIFI_CONNECTED) {
// set bt bad channel according to wifi channel
if (hw_priv->channel->band == NL80211_BAND_2GHZ)
wsm_epta_wifi_chan_cmd(hw_priv, hw_priv->channel->hw_value, hw_priv->ht_info.channel_type);
} else if (connect_status == EPTA_STATE_WIFI_DISCONNECTED) {
coex_epta_recover(hw_priv, EPTA_FREEZE_ALL); // wifi disconect need to recover all requests
coex_epta_set_connect(hw_priv, 100000, 0, 1);
wsm_epta_wifi_chan_cmd(hw_priv, 0, 0);
}
}
} else {
int wlan_tdd_duration = EPTA_TDD_CONNECT_WIFI;
int bt_tdd_duration = EPTA_TDD_CONNECT_BT;
/* if wlan_duration_cfg> EPTA_TDD_CONNECT_WIFI; chose wlan_duration_cfg, in TDD mode */
if (wlan_duration_cfg > EPTA_TDD_CONNECT_WIFI) {
wlan_tdd_duration = wlan_duration_cfg;
bt_tdd_duration = bt_duration_cfg;
}
if (connect_status == EPTA_STATE_WIFI_CONNECTED) {
coex_epta_set_connect(hw_priv, wlan_duration_cfg, bt_duration_cfg, 3);
} else if (connect_status == EPTA_STATE_WIFI_SCANNING) {
/* in scan status, only valid param 0, others wlan_tdd_duration,bt_tdd_duratio is invalid */
coex_epta_freeze(hw_priv, wlan_duration_cfg, EPTA_FREEZE_SCANNING);
} else if (connect_status == EPTA_STATE_WIFI_SCAN_COMP) {
coex_epta_set_connect(hw_priv, wlan_duration_cfg, bt_duration_cfg, 0);
} else if (connect_status == EPTA_STATE_WIFI_GOT_IP) {
coex_epta_set_connect(hw_priv, 20000, 80000, 3);
coex_epta_recover(hw_priv, EPTA_FREEZE_SCANNING | EPTA_FREEZE_CONNECTING);
} else if (connect_status == EPTA_STATE_WIFI_CONNECTING) {
coex_epta_freeze(hw_priv, wlan_tdd_duration, EPTA_FREEZE_CONNECTING);
if (coex_fdd_mode == 0 && bt_duration_cfg > 50000) //bt audio
coex_epta_set_connect(hw_priv, 30000, 15000, 4);
} else if (connect_status == EPTA_STATE_WIFI_DISCONNECTED) {
coex_epta_recover(hw_priv, EPTA_FREEZE_ALL); // wifi disconect need to recover all requests
/* TDD MODE: if wlan is disconnect, default use hw epta */
// coex_epta_set_connect(hw_priv, 100000, 0, 1);
} else {
/* do nothing */
}
}
}
bool coex_is_wifi_inactive()
{
bes_devel("%s, epta_conn_state:%d", __FUNCTION__, epta_conn_state);
return epta_conn_state == EPTA_STATE_WIFI_DISCONNECTED;
}
/*
* coex_set_epta_tts
* @tts_state: 0 - tts start, 1 - tts end
*/
void coex_set_epta_tts(struct bes2600_common *hw_priv, uint32_t tts_state)
{
if (EPTA_MODE_CFG == 0) {
if (tts_state) {
coex_epta_recover(hw_priv, EPTA_FREEZE_TTS);
} else {
coex_epta_freeze(hw_priv, 50000, EPTA_FREEZE_TTS);
}
}
}
static void coex_set_epta_thp(struct bes2600_common *hw_priv, uint32_t total_bps)
{
int epta_adjust_inv = 3;
int wlan_tp_low;
if (coex_fdd_mode && epta_conn_state >= EPTA_STATE_WIFI_GOT_IP)
return;
//bes_devel("coex_set_epta_thp %d bt_state %d epta_freeze_bitmap=%d epta_adjust_cnt=%d coex_ps_en=%d fw=%d\n",
// total_bps, coex_bt_state, epta_freeze_bitmap, epta_adjust_cnt, coex_ps_en, epta_freezed_wlan_duration_cfg);
/* sniffer mode always work with ble and tts, need adjust more quickly */
// wlan_tp_low = netdev_sniffer_get_stat() ? EPTA_WLAN_TP_LOW : 500;
wlan_tp_low = 500;
/* only need adjust in sw epta mode */
if (EPTA_MODE_CFG != 0)
return;
if ((coex_bt_state == 0) && (total_bps > EPTA_WLAN_TP_HIGH)) {
coex_epta_freeze(hw_priv, EPTA_ADJUST_WLAN_DURATION_HIGH, EPTA_FREEZE_THP);
} else if (total_bps > EPTA_WLAN_TP_MEDIUM) {
coex_epta_freeze(hw_priv, EPTA_ADJUST_WLAN_DURATION_MEDIUM, EPTA_FREEZE_THP);
} else if (total_bps > wlan_tp_low) {
coex_epta_freeze(hw_priv, EPTA_ADJUST_WLAN_DURATION_LOW, EPTA_FREEZE_THP);
} else {
if ((epta_freeze_bitmap & (1 << EPTA_FREEZE_THP)) &&
(((++epta_adjust_cnt) % epta_adjust_inv) == 0)) {
// recover if wifi idle 3 secs
epta_adjust_cnt = 0;
coex_epta_recover(hw_priv, EPTA_FREEZE_THP);
} else if (coex_ps_en && !epta_freeze_bitmap) {
if ((total_bps > EPTA_WLAN_TP_PS)) {
coex_epta_ps(hw_priv, 0);
epta_adjust_cnt = 0;
} else if ((total_bps < EPTA_WLAN_TP_PS) &&
(((++epta_adjust_cnt) % epta_adjust_inv) == 0)) {
coex_epta_ps(hw_priv, 1);
}
}
}
}
// void coex_band_update(struct bes2600_common *hw_priv, enum nl80211_band band)
// {
// bes_devel("coex_band_update band:%u\n", (uint32_t)band);
// }
void coex_rssi_update(struct bes2600_common *hw_priv, int rssi, int channel, int connected)
{
bool fdd_en = 0;
if (g_coex_mode == 0 || // tdd
g_coex_mode == (WIFI_COEX_MODE_FDD_HYBRID_BIT | WIFI_COEX_MODE_FDD_BIT)) {
bes_devel("coex_rssi_update rssi:%d, ch:%d, con:%d\n",
rssi, channel, connected);
if (channel > 14) {
fdd_en = 1;
///TODO: HYBRID mode
// } else if (rssi >= COEX_FDD_RSSI_THR) {
// fdd_en = 1;
// } else if (rssi < COEX_TDD_RSSI_THR) {
// fdd_en = 0;
}
if (fdd_en != coex_fdd_mode) {
coex_fdd_mode = fdd_en;
coex_set_epta_params(hw_priv, wlan_duration_cfg, bt_duration_cfg, hw_epta_enable_cfg);
}
}
}
void coex_set_bt_state(struct bes2600_common *hw_priv, int state)
{
bes_devel("coex_set_bt_state %d\n", state);
coex_bt_state = state;
}
void coex_peroid_handle(struct bes2600_common *hw_priv, int connected, int rssi, int channel, uint32_t tp)
{
if (connected) {
/*
* Adjust wifi/bt duration dynamically according to throughput for a better performance;
*/
coex_set_epta_thp(hw_priv, tp);
}
if (g_coex_mode == 0 || // tdd
g_coex_mode == (WIFI_COEX_MODE_FDD_HYBRID_BIT | WIFI_COEX_MODE_FDD_BIT)) {
coex_rssi_update(hw_priv, rssi, channel, connected);
}
}
/*
* set fdd or fdd hybrid
* 1: fdd
* 0: fddhybrid
*/
void coex_set_fdd_mode(bool fdd_mode)
{
if (g_coex_mode == 0 || // tdd
g_coex_mode == (WIFI_COEX_MODE_FDD_HYBRID_BIT | WIFI_COEX_MODE_FDD_BIT)) { //hybrid
bes_devel("%s, %d", __FUNCTION__, fdd_mode);
coex_fdd_mode = fdd_mode;
}
}
bool coex_is_fdd_mode()
{
bes_devel("%s, %d", __FUNCTION__, coex_fdd_mode);
return coex_fdd_mode;
}
bool coex_is_bt_a2dp()
{
bes_devel("%s, coex_bt_state:%d", __FUNCTION__, coex_bt_state);
return coex_bt_state == 3;
}
bool coex_is_bt_inactive()
{
bes_devel("%s, coex_bt_state:%d", __FUNCTION__, coex_bt_state);
return coex_bt_state == 0;
}
int coex_init_mode(struct bes2600_common *hw_priv, int coex_mode)
{
bes_devel("coex_init_mode coex_mode %d\n", coex_mode);
g_coex_mode = coex_mode;
if (coex_mode & WIFI_COEX_MODE_FDD_BIT)
coex_fdd_mode = true;
else
coex_fdd_mode = false;
coex_wifi_bt_ts_thread_init(hw_priv);
return 0;
}
int coex_deinit_mode(struct bes2600_common *hw_priv)
{
bes_devel("coex_deinit_mode\n");
coex_wifi_bt_ts_thread_deinit(hw_priv);
return 0;
}
int coex_start(struct bes2600_common *hw_priv)
{
bes_devel("%s\n", __FUNCTION__);
coex_ps_en = false;
if (g_coex_mode & WIFI_COEX_MODE_FDD_BIT)
coex_fdd_mode = true;
else
coex_fdd_mode = false;
epta_conn_state = 0xff;
epta_freeze_bitmap = 0;
epta_freezed_wlan_duration_cfg = 0;
wlan_duration_cfg = 20000;
bt_duration_cfg = 80000;
hw_epta_enable_cfg = 0;
epta_ps_mode = 0;
epta_adjust_cnt = 0;
coex_bt_state = 0;
return 0;
}
int coex_stop(struct bes2600_common *hw_priv)
{
bes_devel("%s\n", __FUNCTION__);
return 0;
}