cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

btcoex.c (12736B)


      1// SPDX-License-Identifier: ISC
      2/*
      3 * Copyright (c) 2013 Broadcom Corporation
      4 */
      5#include <linux/slab.h>
      6#include <linux/netdevice.h>
      7#include <net/cfg80211.h>
      8
      9#include <brcmu_wifi.h>
     10#include <brcmu_utils.h>
     11#include <defs.h>
     12#include "core.h"
     13#include "debug.h"
     14#include "fwil.h"
     15#include "fwil_types.h"
     16#include "btcoex.h"
     17#include "p2p.h"
     18#include "cfg80211.h"
     19
     20/* T1 start SCO/eSCO priority suppression */
     21#define BRCMF_BTCOEX_OPPR_WIN_TIME   msecs_to_jiffies(2000)
     22
     23/* BT registers values during DHCP */
     24#define BRCMF_BT_DHCP_REG50 0x8022
     25#define BRCMF_BT_DHCP_REG51 0
     26#define BRCMF_BT_DHCP_REG64 0
     27#define BRCMF_BT_DHCP_REG65 0
     28#define BRCMF_BT_DHCP_REG71 0
     29#define BRCMF_BT_DHCP_REG66 0x2710
     30#define BRCMF_BT_DHCP_REG41 0x33
     31#define BRCMF_BT_DHCP_REG68 0x190
     32
     33/* number of samples for SCO detection */
     34#define BRCMF_BT_SCO_SAMPLES 12
     35
     36/**
     37* enum brcmf_btcoex_state - BT coex DHCP state machine states
     38* @BRCMF_BT_DHCP_IDLE: DCHP is idle
     39* @BRCMF_BT_DHCP_START: DHCP started, wait before
     40*	boosting wifi priority
     41* @BRCMF_BT_DHCP_OPPR_WIN: graceful DHCP opportunity ended,
     42*	boost wifi priority
     43* @BRCMF_BT_DHCP_FLAG_FORCE_TIMEOUT: wifi priority boost end,
     44*	restore defaults
     45*/
     46enum brcmf_btcoex_state {
     47	BRCMF_BT_DHCP_IDLE,
     48	BRCMF_BT_DHCP_START,
     49	BRCMF_BT_DHCP_OPPR_WIN,
     50	BRCMF_BT_DHCP_FLAG_FORCE_TIMEOUT
     51};
     52
     53/**
     54 * struct brcmf_btcoex_info - BT coex related information
     55 * @vif: interface for which request was done.
     56 * @timer: timer for DHCP state machine
     57 * @timeout: configured timeout.
     58 * @timer_on:  DHCP timer active
     59 * @dhcp_done: DHCP finished before T1/T2 timer expiration
     60 * @bt_state: DHCP state machine state
     61 * @work: DHCP state machine work
     62 * @cfg: driver private data for cfg80211 interface
     63 * @reg66: saved value of btc_params 66
     64 * @reg41: saved value of btc_params 41
     65 * @reg68: saved value of btc_params 68
     66 * @saved_regs_part1: flag indicating regs 66,41,68
     67 *	have been saved
     68 * @reg50: saved value of btc_params 50
     69 * @reg51: saved value of btc_params 51
     70 * @reg64: saved value of btc_params 64
     71 * @reg65: saved value of btc_params 65
     72 * @reg71: saved value of btc_params 71
     73 * @saved_regs_part2: flag indicating regs 50,51,64,65,71
     74 *	have been saved
     75 */
     76struct brcmf_btcoex_info {
     77	struct brcmf_cfg80211_vif *vif;
     78	struct timer_list timer;
     79	u16 timeout;
     80	bool timer_on;
     81	bool dhcp_done;
     82	enum brcmf_btcoex_state bt_state;
     83	struct work_struct work;
     84	struct brcmf_cfg80211_info *cfg;
     85	u32 reg66;
     86	u32 reg41;
     87	u32 reg68;
     88	bool saved_regs_part1;
     89	u32 reg50;
     90	u32 reg51;
     91	u32 reg64;
     92	u32 reg65;
     93	u32 reg71;
     94	bool saved_regs_part2;
     95};
     96
     97/**
     98 * brcmf_btcoex_params_write() - write btc_params firmware variable
     99 * @ifp: interface
    100 * @addr: btc_params register number
    101 * @data: data to write
    102 */
    103static s32 brcmf_btcoex_params_write(struct brcmf_if *ifp, u32 addr, u32 data)
    104{
    105	struct {
    106		__le32 addr;
    107		__le32 data;
    108	} reg_write;
    109
    110	reg_write.addr = cpu_to_le32(addr);
    111	reg_write.data = cpu_to_le32(data);
    112	return brcmf_fil_iovar_data_set(ifp, "btc_params",
    113					&reg_write, sizeof(reg_write));
    114}
    115
    116/**
    117 * brcmf_btcoex_params_read() - read btc_params firmware variable
    118 * @ifp: interface
    119 * @addr: btc_params register number
    120 * @data: read data
    121 */
    122static s32 brcmf_btcoex_params_read(struct brcmf_if *ifp, u32 addr, u32 *data)
    123{
    124	*data = addr;
    125
    126	return brcmf_fil_iovar_int_get(ifp, "btc_params", data);
    127}
    128
    129/**
    130 * brcmf_btcoex_boost_wifi() - control BT SCO/eSCO parameters
    131 * @btci: BT coex info
    132 * @trump_sco:
    133 *	true - set SCO/eSCO parameters for compatibility
    134 *		during DHCP window
    135 *	false - restore saved parameter values
    136 *
    137 * Enhanced BT COEX settings for eSCO compatibility during DHCP window
    138 */
    139static void brcmf_btcoex_boost_wifi(struct brcmf_btcoex_info *btci,
    140				    bool trump_sco)
    141{
    142	struct brcmf_if *ifp = brcmf_get_ifp(btci->cfg->pub, 0);
    143
    144	if (trump_sco && !btci->saved_regs_part2) {
    145		/* this should reduce eSCO agressive
    146		 * retransmit w/o breaking it
    147		 */
    148
    149		/* save current */
    150		brcmf_dbg(INFO, "new SCO/eSCO coex algo {save & override}\n");
    151		brcmf_btcoex_params_read(ifp, 50, &btci->reg50);
    152		brcmf_btcoex_params_read(ifp, 51, &btci->reg51);
    153		brcmf_btcoex_params_read(ifp, 64, &btci->reg64);
    154		brcmf_btcoex_params_read(ifp, 65, &btci->reg65);
    155		brcmf_btcoex_params_read(ifp, 71, &btci->reg71);
    156
    157		btci->saved_regs_part2 = true;
    158		brcmf_dbg(INFO,
    159			  "saved bt_params[50,51,64,65,71]: 0x%x 0x%x 0x%x 0x%x 0x%x\n",
    160			  btci->reg50, btci->reg51, btci->reg64,
    161			  btci->reg65, btci->reg71);
    162
    163		/* pacify the eSco   */
    164		brcmf_btcoex_params_write(ifp, 50, BRCMF_BT_DHCP_REG50);
    165		brcmf_btcoex_params_write(ifp, 51, BRCMF_BT_DHCP_REG51);
    166		brcmf_btcoex_params_write(ifp, 64, BRCMF_BT_DHCP_REG64);
    167		brcmf_btcoex_params_write(ifp, 65, BRCMF_BT_DHCP_REG65);
    168		brcmf_btcoex_params_write(ifp, 71, BRCMF_BT_DHCP_REG71);
    169
    170	} else if (btci->saved_regs_part2) {
    171		/* restore previously saved bt params */
    172		brcmf_dbg(INFO, "Do new SCO/eSCO coex algo {restore}\n");
    173		brcmf_btcoex_params_write(ifp, 50, btci->reg50);
    174		brcmf_btcoex_params_write(ifp, 51, btci->reg51);
    175		brcmf_btcoex_params_write(ifp, 64, btci->reg64);
    176		brcmf_btcoex_params_write(ifp, 65, btci->reg65);
    177		brcmf_btcoex_params_write(ifp, 71, btci->reg71);
    178
    179		brcmf_dbg(INFO,
    180			  "restored bt_params[50,51,64,65,71]: 0x%x 0x%x 0x%x 0x%x 0x%x\n",
    181			  btci->reg50, btci->reg51, btci->reg64,
    182			  btci->reg65, btci->reg71);
    183
    184		btci->saved_regs_part2 = false;
    185	} else {
    186		brcmf_dbg(INFO, "attempted to restore not saved BTCOEX params\n");
    187	}
    188}
    189
    190/**
    191 * brcmf_btcoex_is_sco_active() - check if SCO/eSCO is active
    192 * @ifp: interface
    193 *
    194 * return: true if SCO/eSCO session is active
    195 */
    196static bool brcmf_btcoex_is_sco_active(struct brcmf_if *ifp)
    197{
    198	int ioc_res = 0;
    199	bool res = false;
    200	int sco_id_cnt = 0;
    201	u32 param27;
    202	int i;
    203
    204	for (i = 0; i < BRCMF_BT_SCO_SAMPLES; i++) {
    205		ioc_res = brcmf_btcoex_params_read(ifp, 27, &param27);
    206
    207		if (ioc_res < 0) {
    208			brcmf_err("ioc read btc params error\n");
    209			break;
    210		}
    211
    212		brcmf_dbg(INFO, "sample[%d], btc_params 27:%x\n", i, param27);
    213
    214		if ((param27 & 0x6) == 2) { /* count both sco & esco  */
    215			sco_id_cnt++;
    216		}
    217
    218		if (sco_id_cnt > 2) {
    219			brcmf_dbg(INFO,
    220				  "sco/esco detected, pkt id_cnt:%d samples:%d\n",
    221				  sco_id_cnt, i);
    222			res = true;
    223			break;
    224		}
    225	}
    226	brcmf_dbg(TRACE, "exit: result=%d\n", res);
    227	return res;
    228}
    229
    230/*
    231 * btcmf_btcoex_save_part1() - save first step parameters.
    232 */
    233static void btcmf_btcoex_save_part1(struct brcmf_btcoex_info *btci)
    234{
    235	struct brcmf_if *ifp = btci->vif->ifp;
    236
    237	if (!btci->saved_regs_part1) {
    238		/* Retrieve and save original reg value */
    239		brcmf_btcoex_params_read(ifp, 66, &btci->reg66);
    240		brcmf_btcoex_params_read(ifp, 41, &btci->reg41);
    241		brcmf_btcoex_params_read(ifp, 68, &btci->reg68);
    242		btci->saved_regs_part1 = true;
    243		brcmf_dbg(INFO,
    244			  "saved btc_params regs (66,41,68) 0x%x 0x%x 0x%x\n",
    245			  btci->reg66, btci->reg41,
    246			  btci->reg68);
    247	}
    248}
    249
    250/*
    251 * brcmf_btcoex_restore_part1() - restore first step parameters.
    252 */
    253static void brcmf_btcoex_restore_part1(struct brcmf_btcoex_info *btci)
    254{
    255	struct brcmf_if *ifp;
    256
    257	if (btci->saved_regs_part1) {
    258		btci->saved_regs_part1 = false;
    259		ifp = btci->vif->ifp;
    260		brcmf_btcoex_params_write(ifp, 66, btci->reg66);
    261		brcmf_btcoex_params_write(ifp, 41, btci->reg41);
    262		brcmf_btcoex_params_write(ifp, 68, btci->reg68);
    263		brcmf_dbg(INFO,
    264			  "restored btc_params regs {66,41,68} 0x%x 0x%x 0x%x\n",
    265			  btci->reg66, btci->reg41,
    266			  btci->reg68);
    267	}
    268}
    269
    270/*
    271 * brcmf_btcoex_timerfunc() - BT coex timer callback
    272 */
    273static void brcmf_btcoex_timerfunc(struct timer_list *t)
    274{
    275	struct brcmf_btcoex_info *bt_local = from_timer(bt_local, t, timer);
    276	brcmf_dbg(TRACE, "enter\n");
    277
    278	bt_local->timer_on = false;
    279	schedule_work(&bt_local->work);
    280}
    281
    282/**
    283 * brcmf_btcoex_handler() - BT coex state machine work handler
    284 * @work: work
    285 */
    286static void brcmf_btcoex_handler(struct work_struct *work)
    287{
    288	struct brcmf_btcoex_info *btci;
    289	btci = container_of(work, struct brcmf_btcoex_info, work);
    290	if (btci->timer_on) {
    291		btci->timer_on = false;
    292		del_timer_sync(&btci->timer);
    293	}
    294
    295	switch (btci->bt_state) {
    296	case BRCMF_BT_DHCP_START:
    297		/* DHCP started provide OPPORTUNITY window
    298		   to get DHCP address
    299		*/
    300		brcmf_dbg(INFO, "DHCP started\n");
    301		btci->bt_state = BRCMF_BT_DHCP_OPPR_WIN;
    302		if (btci->timeout < BRCMF_BTCOEX_OPPR_WIN_TIME) {
    303			mod_timer(&btci->timer, btci->timer.expires);
    304		} else {
    305			btci->timeout -= BRCMF_BTCOEX_OPPR_WIN_TIME;
    306			mod_timer(&btci->timer,
    307				  jiffies + BRCMF_BTCOEX_OPPR_WIN_TIME);
    308		}
    309		btci->timer_on = true;
    310		break;
    311
    312	case BRCMF_BT_DHCP_OPPR_WIN:
    313		if (btci->dhcp_done) {
    314			brcmf_dbg(INFO, "DHCP done before T1 expiration\n");
    315			goto idle;
    316		}
    317
    318		/* DHCP is not over yet, start lowering BT priority */
    319		brcmf_dbg(INFO, "DHCP T1:%d expired\n",
    320			  jiffies_to_msecs(BRCMF_BTCOEX_OPPR_WIN_TIME));
    321		brcmf_btcoex_boost_wifi(btci, true);
    322
    323		btci->bt_state = BRCMF_BT_DHCP_FLAG_FORCE_TIMEOUT;
    324		mod_timer(&btci->timer, jiffies + btci->timeout);
    325		btci->timer_on = true;
    326		break;
    327
    328	case BRCMF_BT_DHCP_FLAG_FORCE_TIMEOUT:
    329		if (btci->dhcp_done)
    330			brcmf_dbg(INFO, "DHCP done before T2 expiration\n");
    331		else
    332			brcmf_dbg(INFO, "DHCP T2:%d expired\n",
    333				  BRCMF_BT_DHCP_FLAG_FORCE_TIMEOUT);
    334
    335		goto idle;
    336
    337	default:
    338		brcmf_err("invalid state=%d !!!\n", btci->bt_state);
    339		goto idle;
    340	}
    341
    342	return;
    343
    344idle:
    345	btci->bt_state = BRCMF_BT_DHCP_IDLE;
    346	btci->timer_on = false;
    347	brcmf_btcoex_boost_wifi(btci, false);
    348	cfg80211_crit_proto_stopped(&btci->vif->wdev, GFP_KERNEL);
    349	brcmf_btcoex_restore_part1(btci);
    350	btci->vif = NULL;
    351}
    352
    353/**
    354 * brcmf_btcoex_attach() - initialize BT coex data
    355 * @cfg: driver private cfg80211 data
    356 *
    357 * return: 0 on success
    358 */
    359int brcmf_btcoex_attach(struct brcmf_cfg80211_info *cfg)
    360{
    361	struct brcmf_btcoex_info *btci = NULL;
    362	brcmf_dbg(TRACE, "enter\n");
    363
    364	btci = kmalloc(sizeof(struct brcmf_btcoex_info), GFP_KERNEL);
    365	if (!btci)
    366		return -ENOMEM;
    367
    368	btci->bt_state = BRCMF_BT_DHCP_IDLE;
    369
    370	/* Set up timer for BT  */
    371	btci->timer_on = false;
    372	btci->timeout = BRCMF_BTCOEX_OPPR_WIN_TIME;
    373	timer_setup(&btci->timer, brcmf_btcoex_timerfunc, 0);
    374	btci->cfg = cfg;
    375	btci->saved_regs_part1 = false;
    376	btci->saved_regs_part2 = false;
    377
    378	INIT_WORK(&btci->work, brcmf_btcoex_handler);
    379
    380	cfg->btcoex = btci;
    381	return 0;
    382}
    383
    384/**
    385 * brcmf_btcoex_detach - clean BT coex data
    386 * @cfg: driver private cfg80211 data
    387 */
    388void brcmf_btcoex_detach(struct brcmf_cfg80211_info *cfg)
    389{
    390	brcmf_dbg(TRACE, "enter\n");
    391
    392	if (!cfg->btcoex)
    393		return;
    394
    395	if (cfg->btcoex->timer_on) {
    396		cfg->btcoex->timer_on = false;
    397		del_timer_sync(&cfg->btcoex->timer);
    398	}
    399
    400	cancel_work_sync(&cfg->btcoex->work);
    401
    402	brcmf_btcoex_boost_wifi(cfg->btcoex, false);
    403	brcmf_btcoex_restore_part1(cfg->btcoex);
    404
    405	kfree(cfg->btcoex);
    406	cfg->btcoex = NULL;
    407}
    408
    409static void brcmf_btcoex_dhcp_start(struct brcmf_btcoex_info *btci)
    410{
    411	struct brcmf_if *ifp = btci->vif->ifp;
    412
    413	btcmf_btcoex_save_part1(btci);
    414	/* set new regs values */
    415	brcmf_btcoex_params_write(ifp, 66, BRCMF_BT_DHCP_REG66);
    416	brcmf_btcoex_params_write(ifp, 41, BRCMF_BT_DHCP_REG41);
    417	brcmf_btcoex_params_write(ifp, 68, BRCMF_BT_DHCP_REG68);
    418	btci->dhcp_done = false;
    419	btci->bt_state = BRCMF_BT_DHCP_START;
    420	schedule_work(&btci->work);
    421	brcmf_dbg(TRACE, "enable BT DHCP Timer\n");
    422}
    423
    424static void brcmf_btcoex_dhcp_end(struct brcmf_btcoex_info *btci)
    425{
    426	/* Stop any bt timer because DHCP session is done */
    427	btci->dhcp_done = true;
    428	if (btci->timer_on) {
    429		brcmf_dbg(INFO, "disable BT DHCP Timer\n");
    430		btci->timer_on = false;
    431		del_timer_sync(&btci->timer);
    432
    433		/* schedule worker if transition to IDLE is needed */
    434		if (btci->bt_state != BRCMF_BT_DHCP_IDLE) {
    435			brcmf_dbg(INFO, "bt_state:%d\n",
    436				  btci->bt_state);
    437			schedule_work(&btci->work);
    438		}
    439	} else {
    440		/* Restore original values */
    441		brcmf_btcoex_restore_part1(btci);
    442	}
    443}
    444
    445/*
    446 * brcmf_btcoex_set_mode - set BT coex mode
    447 * @mode: Wifi-Bluetooth coexistence mode
    448 *
    449 * return: 0 on success
    450 */
    451int brcmf_btcoex_set_mode(struct brcmf_cfg80211_vif *vif,
    452			  enum brcmf_btcoex_mode mode, u16 duration)
    453{
    454	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(vif->wdev.wiphy);
    455	struct brcmf_btcoex_info *btci = cfg->btcoex;
    456	struct brcmf_if *ifp = brcmf_get_ifp(cfg->pub, 0);
    457
    458	switch (mode) {
    459	case BRCMF_BTCOEX_DISABLED:
    460		brcmf_dbg(INFO, "DHCP session starts\n");
    461		if (btci->bt_state != BRCMF_BT_DHCP_IDLE)
    462			return -EBUSY;
    463		/* Start BT timer only for SCO connection */
    464		if (brcmf_btcoex_is_sco_active(ifp)) {
    465			btci->timeout = msecs_to_jiffies(duration);
    466			btci->vif = vif;
    467			brcmf_btcoex_dhcp_start(btci);
    468		}
    469		break;
    470
    471	case BRCMF_BTCOEX_ENABLED:
    472		brcmf_dbg(INFO, "DHCP session ends\n");
    473		if (btci->bt_state != BRCMF_BT_DHCP_IDLE &&
    474		    vif == btci->vif) {
    475			brcmf_btcoex_dhcp_end(btci);
    476		}
    477		break;
    478	default:
    479		brcmf_dbg(INFO, "Unknown mode, ignored\n");
    480	}
    481	return 0;
    482}