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