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