1 /*-
2 * Copyright (c) 2014 Qualcomm Atheros, Inc.
3 * Copyright (c) 2016 Adrian Chadd <adrian@FreeBSD.org>
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer,
11 * without modification.
12 * 2. Redistributions in binary form must reproduce at minimum a disclaimer
13 * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
14 * redistribution must be conditioned upon including a substantially
15 * similar Disclaimer requirement for further binary redistribution.
16 *
17 * NO WARRANTY
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
21 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
22 * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
23 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
26 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
28 * THE POSSIBILITY OF SUCH DAMAGES.
29 */
30 #include <sys/cdefs.h>
31 /*
32 * This implements the MCI bluetooth coexistence handling.
33 */
34 #include "opt_ath.h"
35 #include "opt_inet.h"
36 #include "opt_wlan.h"
37
38 #include <sys/param.h>
39 #include <sys/systm.h>
40 #include <sys/sysctl.h>
41 #include <sys/kernel.h>
42 #include <sys/lock.h>
43 #include <sys/malloc.h>
44 #include <sys/mutex.h>
45 #include <sys/errno.h>
46
47 #include <machine/bus.h>
48 #include <machine/resource.h>
49
50 #include <sys/bus.h>
51
52 #include <sys/socket.h>
53
54 #include <net/if.h>
55 #include <net/if_var.h>
56 #include <net/if_media.h>
57 #include <net/if_arp.h>
58 #include <net/ethernet.h> /* XXX for ether_sprintf */
59
60 #include <net80211/ieee80211_var.h>
61
62 #include <net/bpf.h>
63
64 #ifdef INET
65 #include <netinet/in.h>
66 #include <netinet/if_ether.h>
67 #endif
68
69 #include <dev/ath/if_athvar.h>
70 #include <dev/ath/if_ath_debug.h>
71 #include <dev/ath/if_ath_descdma.h>
72 #include <dev/ath/if_ath_btcoex.h>
73
74 #include <dev/ath/if_ath_btcoex_mci.h>
75
76 MALLOC_DECLARE(M_ATHDEV);
77
78 #define ATH_MCI_GPM_MAX_ENTRY 16
79 #define ATH_MCI_GPM_BUF_SIZE (ATH_MCI_GPM_MAX_ENTRY * 16)
80 #define ATH_MCI_SCHED_BUF_SIZE (16 * 16) /* 16 entries, 4 dword each */
81
82 static void ath_btcoex_mci_update_wlan_channels(struct ath_softc *sc);
83
84 int
ath_btcoex_mci_attach(struct ath_softc * sc)85 ath_btcoex_mci_attach(struct ath_softc *sc)
86 {
87 int buflen, error;
88
89 buflen = ATH_MCI_GPM_BUF_SIZE + ATH_MCI_SCHED_BUF_SIZE;
90 error = ath_descdma_alloc_desc(sc, &sc->sc_btcoex.buf, NULL,
91 "MCI bufs", buflen, 1);
92 if (error != 0) {
93 device_printf(sc->sc_dev, "%s: failed to alloc MCI RAM\n",
94 __func__);
95 return (error);
96 }
97
98 /* Yes, we're going to do bluetooth MCI coex */
99 sc->sc_btcoex_mci = 1;
100
101 /* Initialise the wlan channel mapping */
102 sc->sc_btcoex.wlan_channels[0] = 0x00000000;
103 sc->sc_btcoex.wlan_channels[1] = 0xffffffff;
104 sc->sc_btcoex.wlan_channels[2] = 0xffffffff;
105 sc->sc_btcoex.wlan_channels[3] = 0x7fffffff;
106
107 /*
108 * Ok, so the API is a bit odd. It assumes sched_addr is
109 * after gpm_addr, and it does math to figure out the right
110 * sched_buf pointer.
111 *
112 * So, set gpm_addr to buf, sched_addr to gpm_addr + ATH_MCI_GPM_BUF_SIZE,
113 * the HAL call with do (gpm_buf + (sched_addr - gpm_addr)) to
114 * set sched_buf, and we're "golden".
115 *
116 * Note, it passes in 'len' here (gpm_len) as
117 * ATH_MCI_GPM_BUF_SIZE >> 4. My guess is that it's 16
118 * bytes per entry and we're storing 16 entries.
119 */
120 sc->sc_btcoex.gpm_buf = (void *) sc->sc_btcoex.buf.dd_desc;
121 sc->sc_btcoex.sched_buf = sc->sc_btcoex.gpm_buf +
122 ATH_MCI_GPM_BUF_SIZE;
123
124 sc->sc_btcoex.gpm_paddr = sc->sc_btcoex.buf.dd_desc_paddr;
125 sc->sc_btcoex.sched_paddr = sc->sc_btcoex.gpm_paddr +
126 ATH_MCI_GPM_BUF_SIZE;
127
128 /* memset the gpm buffer with MCI_GPM_RSVD_PATTERN */
129 memset(sc->sc_btcoex.gpm_buf, 0xfe, buflen);
130
131 /*
132 * This is an unfortunate x86'ism in the HAL - the
133 * HAL code expects the passed in buffer to be
134 * coherent, and doesn't implement /any/ kind
135 * of buffer sync operations at all.
136 *
137 * So, this code will only work on dma coherent buffers
138 * and will behave poorly on non-coherent systems.
139 * Fixing this would require some HAL surgery so it
140 * actually /did/ the buffer flushing as appropriate.
141 */
142 ath_hal_btcoex_mci_setup(sc->sc_ah,
143 sc->sc_btcoex.gpm_paddr,
144 sc->sc_btcoex.gpm_buf,
145 ATH_MCI_GPM_BUF_SIZE >> 4,
146 sc->sc_btcoex.sched_paddr);
147
148 return (0);
149 }
150
151 /*
152 * Detach btcoex from the given interface
153 */
154 int
ath_btcoex_mci_detach(struct ath_softc * sc)155 ath_btcoex_mci_detach(struct ath_softc *sc)
156 {
157
158 ath_hal_btcoex_mci_detach(sc->sc_ah);
159 ath_descdma_cleanup(sc, &sc->sc_btcoex.buf, NULL);
160 return (0);
161 }
162
163 /*
164 * Configure or disable bluetooth coexistence on the given channel.
165 *
166 * For MCI, we just use the top-level enable/disable flag, and
167 * then the MCI reset / channel update path will configure things
168 * appropriately based on the current band.
169 */
170 int
ath_btcoex_mci_enable(struct ath_softc * sc,const struct ieee80211_channel * chan)171 ath_btcoex_mci_enable(struct ath_softc *sc,
172 const struct ieee80211_channel *chan)
173 {
174
175 /*
176 * Always reconfigure stomp-all for now, so wlan wins.
177 *
178 * The default weights still don't allow beacons to win,
179 * so unless you set net.wlan.X.bmiss_max to something higher,
180 * net80211 will disconnect you during a HCI INQUIRY command.
181 *
182 * The longer-term solution is to dynamically adjust whether
183 * bmiss happens based on bluetooth requirements, and look at
184 * making the individual stomp bits configurable.
185 */
186 ath_hal_btcoex_set_weights(sc->sc_ah, HAL_BT_COEX_STOMP_ALL);
187
188 /*
189 * update wlan channels so the firmware knows what channels it
190 * can/can't use.
191 */
192 ath_btcoex_mci_update_wlan_channels(sc);
193
194 return (0);
195 }
196
197 /*
198 * XXX TODO: turn into general btcoex, and then make this
199 * the MCI specific bits.
200 */
201 static void
ath_btcoex_mci_event(struct ath_softc * sc,ATH_BT_COEX_EVENT nevent,void * param)202 ath_btcoex_mci_event(struct ath_softc *sc, ATH_BT_COEX_EVENT nevent,
203 void *param)
204 {
205
206 if (! sc->sc_btcoex_mci)
207 return;
208
209 /*
210 * Check whether we need to flush our local profile cache.
211 * If we do, then at (XXX TODO) we should flush our state,
212 * then wait for the MCI response with the updated profile list.
213 */
214 if (ath_hal_btcoex_mci_state(sc->sc_ah,
215 HAL_MCI_STATE_NEED_FLUSH_BT_INFO, NULL) != 0) {
216 uint32_t data = 0;
217
218 if (ath_hal_btcoex_mci_state(sc->sc_ah,
219 HAL_MCI_STATE_ENABLE, NULL) != 0) {
220 DPRINTF(sc, ATH_DEBUG_BTCOEX,
221 "(MCI) Flush BT profile\n");
222 /*
223 * XXX TODO: flush profile state on the ath(4)
224 * driver side; subsequent messages will come
225 * through with the current list of active
226 * profiles.
227 */
228 ath_hal_btcoex_mci_state(sc->sc_ah,
229 HAL_MCI_STATE_NEED_FLUSH_BT_INFO, &data);
230 ath_hal_btcoex_mci_state(sc->sc_ah,
231 HAL_MCI_STATE_SEND_STATUS_QUERY, NULL);
232 }
233 }
234 if (nevent == ATH_COEX_EVENT_BT_NOOP) {
235 DPRINTF(sc, ATH_DEBUG_BTCOEX, "(MCI) BT_NOOP\n");
236 return;
237 }
238 }
239
240 static void
ath_btcoex_mci_send_gpm(struct ath_softc * sc,uint32_t * payload)241 ath_btcoex_mci_send_gpm(struct ath_softc *sc, uint32_t *payload)
242 {
243
244 ath_hal_btcoex_mci_send_message(sc->sc_ah, MCI_GPM, 0, payload, 16,
245 AH_FALSE, AH_TRUE);
246 }
247
248 /*
249 * This starts a BT calibration. It requires a chip reset.
250 */
251 static int
ath_btcoex_mci_bt_cal_do(struct ath_softc * sc,int tx_timeout,int rx_timeout)252 ath_btcoex_mci_bt_cal_do(struct ath_softc *sc, int tx_timeout, int rx_timeout)
253 {
254
255 device_printf(sc->sc_dev, "%s: TODO!\n", __func__);
256 return (0);
257 }
258
259 static void
ath_btcoex_mci_cal_msg(struct ath_softc * sc,uint8_t opcode,uint8_t * rx_payload)260 ath_btcoex_mci_cal_msg(struct ath_softc *sc, uint8_t opcode,
261 uint8_t *rx_payload)
262 {
263 uint32_t payload[4] = {0, 0, 0, 0};
264
265 switch (opcode) {
266 case MCI_GPM_BT_CAL_REQ:
267 DPRINTF(sc, ATH_DEBUG_BTCOEX, "(MCI) receive BT_CAL_REQ\n");
268 if (ath_hal_btcoex_mci_state(sc->sc_ah, HAL_MCI_STATE_BT,
269 NULL) == MCI_BT_AWAKE) {
270 ath_hal_btcoex_mci_state(sc->sc_ah,
271 HAL_MCI_STATE_SET_BT_CAL_START, NULL);
272 ath_btcoex_mci_bt_cal_do(sc, 1000, 1000);
273 } else {
274 DPRINTF(sc, ATH_DEBUG_BTCOEX,
275 "(MCI) State mismatches: %d\n",
276 ath_hal_btcoex_mci_state(sc->sc_ah,
277 HAL_MCI_STATE_BT, NULL));
278 }
279 break;
280 case MCI_GPM_BT_CAL_DONE:
281 DPRINTF(sc, ATH_DEBUG_BTCOEX, "(MCI) receive BT_CAL_DONE\n");
282 if (ath_hal_btcoex_mci_state(sc->sc_ah, HAL_MCI_STATE_BT,
283 NULL) == MCI_BT_CAL) {
284 DPRINTF(sc, ATH_DEBUG_BTCOEX,
285 "(MCI) ERROR ILLEGAL!\n");
286 } else {
287 DPRINTF(sc, ATH_DEBUG_BTCOEX,
288 "(MCI) BT not in CAL state.\n");
289 }
290 break;
291 case MCI_GPM_BT_CAL_GRANT:
292 DPRINTF(sc, ATH_DEBUG_BTCOEX, "(MCI) receive BT_CAL_GRANT\n");
293 /* Send WLAN_CAL_DONE for now */
294 DPRINTF(sc, ATH_DEBUG_BTCOEX, "(MCI) Send WLAN_CAL_DONE\n");
295 MCI_GPM_SET_CAL_TYPE(payload, MCI_GPM_WLAN_CAL_DONE);
296 ath_btcoex_mci_send_gpm(sc, &payload[0]);
297 break;
298 default:
299 DPRINTF(sc, ATH_DEBUG_BTCOEX,
300 "(MCI) Unknown GPM CAL message.\n");
301 break;
302 }
303 }
304
305 /*
306 * Update the bluetooth channel map.
307 *
308 * This map tells the bluetooth device which bluetooth channels
309 * are available for data.
310 *
311 * For 5GHz, all channels are available.
312 * For 2GHz, the current wifi channel range is blocked out,
313 * and the rest are available.
314 *
315 * This narrows which frequencies are used by the device when
316 * it initiates a transfer, thus hopefully reducing the chances
317 * of collisions (both hopefully on the current device and
318 * other devices in the same channel.)
319 */
320 static void
ath_btcoex_mci_update_wlan_channels(struct ath_softc * sc)321 ath_btcoex_mci_update_wlan_channels(struct ath_softc *sc)
322 {
323 struct ieee80211com *ic = &sc->sc_ic;
324 struct ieee80211_channel *chan = ic->ic_curchan;
325 uint32_t channel_info[4] =
326 { 0x00000000, 0xffffffff, 0xffffffff, 0x7fffffff };
327 int32_t wl_chan, bt_chan, bt_start = 0, bt_end = 79;
328
329 /* BT channel frequency is 2402 + k, k = 0 ~ 78 */
330 if (IEEE80211_IS_CHAN_2GHZ(chan)) {
331 wl_chan = chan->ic_freq - 2402;
332 if (IEEE80211_IS_CHAN_HT40U(chan)) {
333 bt_start = wl_chan - 10;
334 bt_end = wl_chan + 30;
335 } else if (IEEE80211_IS_CHAN_HT40D(chan)) {
336 bt_start = wl_chan - 30;
337 bt_end = wl_chan + 10;
338 } else {
339 /* Assume 20MHz */
340 bt_start = wl_chan - 10;
341 bt_end = wl_chan + 10;
342 }
343
344 bt_start -= 7;
345 bt_end += 7;
346
347 if (bt_start < 0) {
348 bt_start = 0;
349 }
350 if (bt_end > MCI_NUM_BT_CHANNELS) {
351 bt_end = MCI_NUM_BT_CHANNELS;
352 }
353 DPRINTF(sc, ATH_DEBUG_BTCOEX, "(MCI) WLAN use channel %d\n",
354 chan->ic_freq);
355 DPRINTF(sc, ATH_DEBUG_BTCOEX,
356 "(MCI) mask BT channel %d - %d\n", bt_start, bt_end);
357 for (bt_chan = bt_start; bt_chan < bt_end; bt_chan++) {
358 MCI_GPM_CLR_CHANNEL_BIT(&channel_info[0], bt_chan);
359 }
360 } else {
361 DPRINTF(sc, ATH_DEBUG_BTCOEX,
362 "(MCI) WLAN not use any 2G channel, unmask all for BT\n");
363 }
364 ath_hal_btcoex_mci_state(sc->sc_ah, HAL_MCI_STATE_SEND_WLAN_CHANNELS,
365 &channel_info[0]);
366 }
367
368 static void
ath_btcoex_mci_coex_msg(struct ath_softc * sc,uint8_t opcode,uint8_t * rx_payload)369 ath_btcoex_mci_coex_msg(struct ath_softc *sc, uint8_t opcode,
370 uint8_t *rx_payload)
371 {
372 uint32_t version;
373 uint8_t major;
374 uint8_t minor;
375 uint32_t seq_num;
376
377 switch (opcode) {
378 case MCI_GPM_COEX_VERSION_QUERY:
379 DPRINTF(sc, ATH_DEBUG_BTCOEX,
380 "(MCI) Recv GPM COEX Version Query.\n");
381 version = ath_hal_btcoex_mci_state(sc->sc_ah,
382 HAL_MCI_STATE_SEND_WLAN_COEX_VERSION, NULL);
383 break;
384
385 case MCI_GPM_COEX_VERSION_RESPONSE:
386 DPRINTF(sc, ATH_DEBUG_BTCOEX,
387 "(MCI) Recv GPM COEX Version Response.\n");
388 major = *(rx_payload + MCI_GPM_COEX_B_MAJOR_VERSION);
389 minor = *(rx_payload + MCI_GPM_COEX_B_MINOR_VERSION);
390 DPRINTF(sc, ATH_DEBUG_BTCOEX,
391 "(MCI) BT Coex version: %d.%d\n", major, minor);
392 version = (major << 8) + minor;
393 version = ath_hal_btcoex_mci_state(sc->sc_ah,
394 HAL_MCI_STATE_SET_BT_COEX_VERSION, &version);
395 break;
396
397 case MCI_GPM_COEX_STATUS_QUERY:
398 DPRINTF(sc, ATH_DEBUG_BTCOEX,
399 "(MCI) Recv GPM COEX Status Query = 0x%02x.\n",
400 *(rx_payload + MCI_GPM_COEX_B_WLAN_BITMAP));
401 ath_hal_btcoex_mci_state(sc->sc_ah,
402 HAL_MCI_STATE_SEND_WLAN_CHANNELS, NULL);
403 break;
404
405 case MCI_GPM_COEX_BT_PROFILE_INFO:
406 /*
407 * XXX TODO: here is where we'd parse active profile
408 * info and make driver/stack choices as appropriate.
409 */
410 DPRINTF(sc, ATH_DEBUG_BTCOEX,
411 "(MCI) TODO: Recv GPM COEX BT_Profile_Info.\n");
412 break;
413
414 case MCI_GPM_COEX_BT_STATUS_UPDATE:
415 seq_num = *((uint32_t *)(rx_payload + 12));
416 DPRINTF(sc, ATH_DEBUG_BTCOEX,
417 "(MCI) Recv GPM COEX BT_Status_Update: SEQ=%d\n",
418 seq_num);
419 break;
420
421 default:
422 DPRINTF(sc, ATH_DEBUG_BTCOEX,
423 "(MCI) Unknown GPM COEX message = 0x%02x\n", opcode);
424 break;
425 }
426 }
427
428 void
ath_btcoex_mci_intr(struct ath_softc * sc)429 ath_btcoex_mci_intr(struct ath_softc *sc)
430 {
431 uint32_t mciInt, mciIntRxMsg;
432 uint32_t offset, subtype, opcode;
433 uint32_t *pGpm;
434 uint32_t more_data = HAL_MCI_GPM_MORE;
435 int8_t value_dbm;
436 bool skip_gpm = false;
437
438 DPRINTF(sc, ATH_DEBUG_BTCOEX, "%s: called\n", __func__);
439
440 ath_hal_btcoex_mci_get_interrupt(sc->sc_ah, &mciInt, &mciIntRxMsg);
441
442 if (ath_hal_btcoex_mci_state(sc->sc_ah, HAL_MCI_STATE_ENABLE,
443 NULL) == 0) {
444 ath_hal_btcoex_mci_state(sc->sc_ah,
445 HAL_MCI_STATE_INIT_GPM_OFFSET, NULL);
446 DPRINTF(sc, ATH_DEBUG_BTCOEX,
447 "(MCI) INTR but MCI_disabled\n");
448 DPRINTF(sc, ATH_DEBUG_BTCOEX,
449 "(MCI) MCI interrupt: mciInt = 0x%x, mciIntRxMsg = 0x%x\n",
450 mciInt, mciIntRxMsg);
451 return;
452 }
453
454 if (mciIntRxMsg & HAL_MCI_INTERRUPT_RX_MSG_REQ_WAKE) {
455 uint32_t payload4[4] = { 0xffffffff, 0xffffffff, 0xffffffff,
456 0xffffff00};
457
458 /*
459 * The following REMOTE_RESET and SYS_WAKING used to sent
460 * only when BT wake up. Now they are always sent, as a
461 * recovery method to reset BT MCI's RX alignment.
462 */
463 DPRINTF(sc, ATH_DEBUG_BTCOEX,
464 "(MCI) 1. INTR Send REMOTE_RESET\n");
465 ath_hal_btcoex_mci_send_message(sc->sc_ah,
466 MCI_REMOTE_RESET, 0, payload4, 16, AH_TRUE, AH_FALSE);
467 DPRINTF(sc, ATH_DEBUG_BTCOEX,
468 "(MCI) 1. INTR Send SYS_WAKING\n");
469 ath_hal_btcoex_mci_send_message(sc->sc_ah,
470 MCI_SYS_WAKING, 0, NULL, 0, AH_TRUE, AH_FALSE);
471
472 mciIntRxMsg &= ~HAL_MCI_INTERRUPT_RX_MSG_REQ_WAKE;
473 ath_hal_btcoex_mci_state(sc->sc_ah,
474 HAL_MCI_STATE_RESET_REQ_WAKE, NULL);
475
476 /* always do this for recovery and 2G/5G toggling and LNA_TRANS */
477 DPRINTF(sc, ATH_DEBUG_BTCOEX,
478 "(MCI) 1. Set BT state to AWAKE.\n");
479 ath_hal_btcoex_mci_state(sc->sc_ah,
480 HAL_MCI_STATE_SET_BT_AWAKE, NULL);
481 }
482
483 /* Processing SYS_WAKING/SYS_SLEEPING */
484 if (mciIntRxMsg & HAL_MCI_INTERRUPT_RX_MSG_SYS_WAKING) {
485 mciIntRxMsg &= ~HAL_MCI_INTERRUPT_RX_MSG_SYS_WAKING;
486 if (ath_hal_btcoex_mci_state(sc->sc_ah, HAL_MCI_STATE_BT,
487 NULL) == MCI_BT_SLEEP) {
488 if (ath_hal_btcoex_mci_state(sc->sc_ah,
489 HAL_MCI_STATE_REMOTE_SLEEP, NULL) == MCI_BT_SLEEP) {
490 DPRINTF(sc, ATH_DEBUG_BTCOEX,
491 "(MCI) 2. BT stays in SLEEP mode.\n");
492 } else {
493 DPRINTF(sc, ATH_DEBUG_BTCOEX,
494 "(MCI) 2. Set BT state to AWAKE.\n");
495 ath_hal_btcoex_mci_state(sc->sc_ah,
496 HAL_MCI_STATE_SET_BT_AWAKE, NULL);
497 }
498 } else {
499 DPRINTF(sc, ATH_DEBUG_BTCOEX,
500 "(MCI) 2. BT stays in AWAKE mode.\n");
501 }
502 }
503
504 if (mciIntRxMsg & HAL_MCI_INTERRUPT_RX_MSG_SYS_SLEEPING) {
505 mciIntRxMsg &= ~HAL_MCI_INTERRUPT_RX_MSG_SYS_SLEEPING;
506 if (ath_hal_btcoex_mci_state(sc->sc_ah, HAL_MCI_STATE_BT,
507 NULL) == MCI_BT_AWAKE) {
508 if (ath_hal_btcoex_mci_state(sc->sc_ah,
509 HAL_MCI_STATE_REMOTE_SLEEP, NULL) == MCI_BT_AWAKE) {
510 DPRINTF(sc, ATH_DEBUG_BTCOEX,
511 "(MCI) 3. BT stays in AWAKE mode.\n");
512 } else {
513 DPRINTF(sc, ATH_DEBUG_BTCOEX,
514 "(MCI) 3. Set BT state to SLEEP.\n");
515 ath_hal_btcoex_mci_state(sc->sc_ah,
516 HAL_MCI_STATE_SET_BT_SLEEP, NULL);
517 }
518 } else {
519 DPRINTF(sc, ATH_DEBUG_BTCOEX,
520 "(MCI) 3. BT stays in SLEEP mode.\n");
521 }
522 }
523
524 /*
525 * Recover from out-of-order / wrong-offset GPM messages.
526 */
527 if ((mciInt & HAL_MCI_INTERRUPT_RX_INVALID_HDR) ||
528 (mciInt & HAL_MCI_INTERRUPT_CONT_INFO_TIMEOUT)) {
529 DPRINTF(sc, ATH_DEBUG_BTCOEX,
530 "(MCI) MCI RX broken, skip GPM messages\n");
531 ath_hal_btcoex_mci_state(sc->sc_ah,
532 HAL_MCI_STATE_RECOVER_RX, NULL);
533 skip_gpm = true;
534 }
535
536 if (mciIntRxMsg & HAL_MCI_INTERRUPT_RX_MSG_SCHD_INFO) {
537 mciIntRxMsg &= ~HAL_MCI_INTERRUPT_RX_MSG_SCHD_INFO;
538 offset = ath_hal_btcoex_mci_state(sc->sc_ah,
539 HAL_MCI_STATE_LAST_SCHD_MSG_OFFSET, NULL);
540 }
541
542 /*
543 * Parse GPM messages.
544 */
545 if (mciIntRxMsg & HAL_MCI_INTERRUPT_RX_MSG_GPM) {
546 mciIntRxMsg &= ~HAL_MCI_INTERRUPT_RX_MSG_GPM;
547
548 while (more_data == HAL_MCI_GPM_MORE) {
549 pGpm = (void *) sc->sc_btcoex.gpm_buf;
550 offset = ath_hal_btcoex_mci_state(sc->sc_ah,
551 HAL_MCI_STATE_NEXT_GPM_OFFSET, &more_data);
552
553 if (offset == HAL_MCI_GPM_INVALID)
554 break;
555 pGpm += (offset >> 2);
556 /*
557 * The first DWORD is a timer.
558 * The real data starts from the second DWORD.
559 */
560 subtype = MCI_GPM_TYPE(pGpm);
561 opcode = MCI_GPM_OPCODE(pGpm);
562
563 if (!skip_gpm) {
564 if (MCI_GPM_IS_CAL_TYPE(subtype)) {
565 ath_btcoex_mci_cal_msg(sc, subtype,
566 (uint8_t*) pGpm);
567 } else {
568 switch (subtype) {
569 case MCI_GPM_COEX_AGENT:
570 ath_btcoex_mci_coex_msg(sc,
571 opcode, (uint8_t*) pGpm);
572 break;
573 case MCI_GPM_BT_DEBUG:
574 device_printf(sc->sc_dev,
575 "(MCI) TODO: GPM_BT_DEBUG!\n");
576 break;
577 default:
578 DPRINTF(sc, ATH_DEBUG_BTCOEX,
579 "(MCI) Unknown GPM message.\n");
580 break;
581 }
582 }
583 }
584 MCI_GPM_RECYCLE(pGpm);
585 }
586 }
587
588 /*
589 * This is monitoring/management information messages, so the driver
590 * layer can hook in and dynamically adjust things like aggregation
591 * size, expected bluetooth/wifi traffic throughput, etc.
592 *
593 * None of that is done right now; it just passes off the values
594 * to the HAL so it can update its internal state as appropriate.
595 * This code just prints out the values for debugging purposes.
596 */
597 if (mciIntRxMsg & HAL_MCI_INTERRUPT_RX_MSG_MONITOR) {
598 if (mciIntRxMsg & HAL_MCI_INTERRUPT_RX_MSG_LNA_CONTROL) {
599 mciIntRxMsg &= ~HAL_MCI_INTERRUPT_RX_MSG_LNA_CONTROL;
600 DPRINTF(sc, ATH_DEBUG_BTCOEX, "(MCI) LNA_CONTROL\n");
601 }
602 if (mciIntRxMsg & HAL_MCI_INTERRUPT_RX_MSG_LNA_INFO) {
603 mciIntRxMsg &= ~HAL_MCI_INTERRUPT_RX_MSG_LNA_INFO;
604 DPRINTF(sc, ATH_DEBUG_BTCOEX, "(MCI) LNA_INFO\n");
605 }
606 if (mciIntRxMsg & HAL_MCI_INTERRUPT_RX_MSG_CONT_INFO) {
607 value_dbm = ath_hal_btcoex_mci_state(sc->sc_ah,
608 HAL_MCI_STATE_CONT_RSSI_POWER, NULL);
609
610 mciIntRxMsg &= ~HAL_MCI_INTERRUPT_RX_MSG_CONT_INFO;
611 if (ath_hal_btcoex_mci_state(sc->sc_ah,
612 HAL_MCI_STATE_CONT_TXRX, NULL)) {
613 DPRINTF(sc, ATH_DEBUG_BTCOEX,
614 "(MCI) CONT_INFO: (tx) pri = %d, pwr = %d dBm\n",
615 ath_hal_btcoex_mci_state(sc->sc_ah,
616 HAL_MCI_STATE_CONT_PRIORITY, NULL),
617 value_dbm);
618 } else {
619 DPRINTF(sc, ATH_DEBUG_BTCOEX,
620 "(MCI) CONT_INFO: (rx) pri = %d, rssi = %d dBm\n",
621 ath_hal_btcoex_mci_state(sc->sc_ah,
622 HAL_MCI_STATE_CONT_PRIORITY, NULL),
623 value_dbm);
624 }
625 }
626 if (mciIntRxMsg & HAL_MCI_INTERRUPT_RX_MSG_CONT_NACK) {
627 mciIntRxMsg &= ~HAL_MCI_INTERRUPT_RX_MSG_CONT_NACK;
628 DPRINTF(sc, ATH_DEBUG_BTCOEX, "(MCI) CONT_NACK\n");
629 }
630 if (mciIntRxMsg & HAL_MCI_INTERRUPT_RX_MSG_CONT_RST) {
631 mciIntRxMsg &= ~HAL_MCI_INTERRUPT_RX_MSG_CONT_RST;
632 DPRINTF(sc, ATH_DEBUG_BTCOEX, "(MCI) CONT_RST\n");
633 }
634 }
635
636 /*
637 * Recover the state engine if we hit an invalid header/timeout.
638 * This is the final part of GPT out-of-sync recovery.
639 */
640 if ((mciInt & HAL_MCI_INTERRUPT_RX_INVALID_HDR) ||
641 (mciInt & HAL_MCI_INTERRUPT_CONT_INFO_TIMEOUT)) {
642 ath_btcoex_mci_event(sc, ATH_COEX_EVENT_BT_NOOP, NULL);
643 mciInt &= ~(HAL_MCI_INTERRUPT_RX_INVALID_HDR |
644 HAL_MCI_INTERRUPT_CONT_INFO_TIMEOUT);
645 }
646
647 if (mciIntRxMsg & 0xfffffffe) {
648 DPRINTF(sc, ATH_DEBUG_BTCOEX,
649 "(MCI) Not processed IntRxMsg = 0x%x\n", mciIntRxMsg);
650 }
651 }
652