btcoex.c 13 KB

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