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 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 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 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 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 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 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 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 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 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 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