[ath] commit initial bluetooth coexistence support for the MCI NICs.

This is the initial framework to call into the MCI HAL routines and drive
the basic state engine.

The MCI bluetooth coex model uses a command channel between wlan and
bluetooth, rather than a 2-wire or 3-wire signaling protocol to control things.
This means the wlan and bluetooth chip exchange a lot more information and
signaling, even at the per-packet level.  The NICs in question can share
the input LNA and output PA on the die, so they absolutely can't stomp
on each other in a silly fashion.  It also allows for the bluetooth side
to signal when profiles come and go, so the driver can take appropriate
control.  There's also the possibility of dynamic bluetooth/wlan duty cycle
control which I haven't yet really played with.

It configures things up with a static "wlan wins everything" coexistence,
configures up the available 2GHz channel map for bluetooth, sets a static
duty cycle for bluetooth/wifi traffic priority and drives the basics needed to
keep the MCI HAL code happy.

It doesn't do any actual coexistence except to default to "wlan wins everything",
which at least demonstrates that things do indeed work.  Bluetooth inquiry frames
still trump wifi (including beacons), so that demonstrates things really do
indeed seem to work.

Tested:

* AR9462 (WB222), STA mode + bt
* QCA9565 (WB335), STA mode + bt

TODO:

* .. the rest of coexistence.  yes, bluetooth, not people.  That stuff's hard.
* It doesn't do the initial BT side calibration, which requires a WLAN chip
  reset.  I'll fix up the reset path a bit more first before I enable that.
* The 1-ant and 2-ant configuration bits aren't being set correctly in
  if_ath_btcoex.c - I'll dig into that and fix it in a subsequent commit.
* It's not enabled by default for WB222/WB225 even though I believe it now
  can be - I'll chase that up in a subsequent commit.

Obtained from:	Qualcomm Atheros, Linux ath9k
This commit is contained in:
Adrian Chadd 2016-06-02 00:51:36 +00:00
parent a25ffb5ab7
commit bcf5fc498a
9 changed files with 826 additions and 76 deletions

View File

@ -765,6 +765,8 @@ dev/ath/if_ath_beacon.c optional ath \
compile-with "${NORMAL_C} -I$S/dev/ath"
dev/ath/if_ath_btcoex.c optional ath \
compile-with "${NORMAL_C} -I$S/dev/ath"
dev/ath/if_ath_btcoex_mci.c optional ath \
compile-with "${NORMAL_C} -I$S/dev/ath"
dev/ath/if_ath_debug.c optional ath \
compile-with "${NORMAL_C} -I$S/dev/ath"
dev/ath/if_ath_descdma.c optional ath \

View File

@ -113,6 +113,7 @@ __FBSDID("$FreeBSD$");
#include <dev/ath/if_ath_tx_edma.h>
#include <dev/ath/if_ath_beacon.h>
#include <dev/ath/if_ath_btcoex.h>
#include <dev/ath/if_ath_btcoex_mci.h>
#include <dev/ath/if_ath_spectral.h>
#include <dev/ath/if_ath_lna_div.h>
#include <dev/ath/if_athdfs.h>
@ -475,6 +476,10 @@ ath_setup_hal_config(struct ath_softc *sc, HAL_OPS_CONFIG *ah_config)
if (sc->sc_pci_devinfo & ATH_PCI_AR9565_2ANT)
device_printf(sc->sc_dev, "WB335 2-ANT card detected\n");
if (sc->sc_pci_devinfo & ATH_PCI_BT_ANT_DIV)
device_printf(sc->sc_dev,
"Bluetooth Antenna Diversity card detected\n");
if (sc->sc_pci_devinfo & ATH_PCI_KILLER)
device_printf(sc->sc_dev, "Killer Wireless card detected\n");
@ -2254,6 +2259,9 @@ ath_intr(void *arg)
device_printf(sc->sc_dev, "%s: TSFOOR\n", __func__);
sc->sc_syncbeacon = 1;
}
if (status & HAL_INT_MCI) {
ath_btcoex_mci_intr(sc);
}
}
ATH_PCU_LOCK(sc);
sc->sc_intr_cnt--;
@ -2548,6 +2556,12 @@ ath_init(struct ath_softc *sc)
if (! sc->sc_isedma)
sc->sc_imask |= HAL_INT_RXEOL;
/*
* Enable MCI interrupt for MCI devices.
*/
if (sc->sc_btcoex_mci)
sc->sc_imask |= HAL_INT_MCI;
/*
* Enable MIB interrupts when there are hardware phy counters.
* Note we only do this (at the moment) for station mode.

View File

@ -47,9 +47,9 @@ __FBSDID("$FreeBSD$");
#include <sys/malloc.h>
#include <sys/mutex.h>
#include <sys/errno.h>
#include <machine/bus.h>
#include <machine/resource.h>
#include <sys/bus.h>
#include <sys/socket.h>
@ -71,6 +71,9 @@ __FBSDID("$FreeBSD$");
#include <dev/ath/if_athvar.h>
#include <dev/ath/if_ath_btcoex.h>
#include <dev/ath/if_ath_btcoex_mci.h>
MALLOC_DECLARE(M_ATHDEV);
/*
* Initial AR9285 / (WB195) bluetooth coexistence settings,
@ -188,14 +191,8 @@ ath_btcoex_cfg_wb225(struct ath_softc *sc)
return (0);
}
/*
* Initial AR9462 / (WB222) bluetooth coexistence settings,
* just for experimentation.
*
* Return 0 for OK; errno for error.
*/
static int
ath_btcoex_cfg_wb222(struct ath_softc *sc)
ath_btcoex_cfg_mci(struct ath_softc *sc, uint32_t mci_cfg, int do_btdiv)
{
HAL_BT_COEX_INFO btinfo;
HAL_BT_COEX_CONFIG btconfig;
@ -207,71 +204,12 @@ ath_btcoex_cfg_wb222(struct ath_softc *sc)
bzero(&btinfo, sizeof(btinfo));
bzero(&btconfig, sizeof(btconfig));
device_printf(sc->sc_dev, "Enabling WB222 BTCOEX\n");
sc->sc_ah->ah_config.ath_hal_mci_config = mci_cfg;
btinfo.bt_module = HAL_BT_MODULE_JANUS; /* XXX not used? */
btinfo.bt_coex_config = HAL_BT_COEX_CFG_MCI;
/*
* MCI uses a completely different interface to speak
* to the bluetooth module - it's a command based
* thing over a serial line, rather than
* state pins to/from the bluetooth module.
*
* So, the GPIO configuration, polarity, etc
* doesn't matter on MCI devices; it's just
* completely ignored by the HAL.
*/
btinfo.bt_gpio_bt_active = 4;
btinfo.bt_gpio_bt_priority = 8;
btinfo.bt_gpio_wlan_active = 5;
btinfo.bt_active_polarity = 1; /* XXX not used */
btinfo.bt_single_ant = 0; /* 2 antenna on WB222 */
btinfo.bt_isolation = 0; /* in dB, not used */
ath_hal_btcoex_set_info(ah, &btinfo);
btconfig.bt_time_extend = 0;
btconfig.bt_txstate_extend = 1; /* true */
btconfig.bt_txframe_extend = 1; /* true */
btconfig.bt_mode = HAL_BT_COEX_MODE_SLOTTED;
btconfig.bt_quiet_collision = 1; /* true */
btconfig.bt_rxclear_polarity = 1; /* true */
btconfig.bt_priority_time = 2;
btconfig.bt_first_slot_time = 5;
btconfig.bt_hold_rxclear = 1; /* true */
ath_hal_btcoex_set_config(ah, &btconfig);
/*
* Enable antenna diversity.
*/
ath_hal_btcoex_set_parameter(ah, HAL_BT_COEX_ANTENNA_DIVERSITY, 1);
return (0);
}
/*
* Initial QCA9565 / (WB335B) bluetooth coexistence settings,
* just for experimentation.
*
* Return 0 for OK; errno for error.
*/
static int
ath_btcoex_cfg_wb335b(struct ath_softc *sc)
{
HAL_BT_COEX_INFO btinfo;
HAL_BT_COEX_CONFIG btconfig;
struct ath_hal *ah = sc->sc_ah;
if (! ath_hal_btcoex_supported(ah))
if (ath_btcoex_mci_attach(sc) != 0) {
device_printf(sc->sc_dev, "Failed to setup btcoex\n");
return (EINVAL);
bzero(&btinfo, sizeof(btinfo));
bzero(&btconfig, sizeof(btconfig));
device_printf(sc->sc_dev, "Enabling WB335B BTCOEX\n");
}
btinfo.bt_module = HAL_BT_MODULE_JANUS; /* XXX not used? */
btinfo.bt_coex_config = HAL_BT_COEX_CFG_MCI;
@ -294,6 +232,10 @@ ath_btcoex_cfg_wb335b(struct ath_softc *sc)
btinfo.bt_single_ant = 0; /* 2 antenna on WB335 */
btinfo.bt_isolation = 0; /* in dB, not used */
/* Implement a default dutycycle/period */
btinfo.bt_dutyCycle = 55;
btinfo.bt_period = 40;
ath_hal_btcoex_set_info(ah, &btinfo);
btconfig.bt_time_extend = 0;
@ -308,14 +250,68 @@ ath_btcoex_cfg_wb335b(struct ath_softc *sc)
ath_hal_btcoex_set_config(ah, &btconfig);
/* Enable */
ath_hal_btcoex_enable(sc->sc_ah);
/* Stomp */
ath_hal_btcoex_set_weights(ah, HAL_BT_COEX_STOMP_NONE);
/*
* Enable antenna diversity.
*/
ath_hal_btcoex_set_parameter(ah, HAL_BT_COEX_ANTENNA_DIVERSITY, 1);
ath_hal_btcoex_set_parameter(ah, HAL_BT_COEX_ANTENNA_DIVERSITY,
do_btdiv);
return (0);
}
/*
* Initial AR9462 / (WB222) bluetooth coexistence settings.
*
* Return 0 for OK; errno for error.
*/
static int
ath_btcoex_cfg_wb222(struct ath_softc *sc)
{
device_printf(sc->sc_dev, "Enabling WB222 BTCOEX\n");
/* XXX from ath9k */
return (ath_btcoex_cfg_mci(sc, 0x2201, 1));
}
/*
* Initial QCA9565 / (WB335B) bluetooth coexistence settings.
*
* Return 0 for OK; errno for error.
*/
static int
ath_btcoex_cfg_wb335b(struct ath_softc *sc)
{
uint32_t flags;
int do_btdiv = 0;
/* ath9k default */
flags = 0xa4c1;
/* 1-ant and 2-ant AR9565 */
/*
* XXX TODO: ensure these actually make it down to the
* HAL correctly!
*/
if (sc->sc_pci_devinfo & ATH_PCI_AR9565_1ANT) {
flags |= ATH_MCI_ANT_ARCH_1_ANT_PA_LNA_SHARED;
} else if (sc->sc_pci_devinfo & ATH_PCI_AR9565_2ANT) {
flags |= ATH_MCI_ANT_ARCH_2_ANT_PA_LNA_NON_SHARED;
}
if (sc->sc_pci_devinfo & ATH_PCI_BT_ANT_DIV) {
do_btdiv = 1;
}
device_printf(sc->sc_dev, "Enabling WB335 BTCOEX\n");
/* XXX from ath9k */
return (ath_btcoex_cfg_mci(sc, flags, do_btdiv));
}
#if 0
/*
@ -396,6 +392,9 @@ ath_btcoex_attach(struct ath_softc *sc)
int
ath_btcoex_detach(struct ath_softc *sc)
{
if (sc->sc_btcoex_mci) {
ath_btcoex_mci_detach(sc);
}
return (0);
}
@ -412,6 +411,9 @@ ath_btcoex_detach(struct ath_softc *sc)
int
ath_btcoex_enable(struct ath_softc *sc, const struct ieee80211_channel *chan)
{
if (sc->sc_btcoex_mci) {
ath_btcoex_mci_enable(sc, chan);
}
return (0);
}

View File

@ -31,6 +31,10 @@
#ifndef __IF_ATH_BTCOEX_H__
#define __IF_ATH_BTCOEX_H__
typedef enum {
ATH_COEX_EVENT_BT_NOOP,
} ATH_BT_COEX_EVENT;
extern int ath_btcoex_attach(struct ath_softc *sc);
extern int ath_btcoex_detach(struct ath_softc *sc);
extern int ath_btcoex_ioctl(struct ath_softc *sc, struct ath_diag *ad);

View File

@ -0,0 +1,654 @@
/*-
* Copyright (c) 2014 Qualcomm Atheros, Inc.
* Copyright (c) 2016 Adrian Chadd <adrian@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer,
* without modification.
* 2. Redistributions in binary form must reproduce at minimum a disclaimer
* similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
* redistribution must be conditioned upon including a substantially
* similar Disclaimer requirement for further binary redistribution.
*
* NO WARRANTY
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGES.
*
* $FreeBSD$
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
/*
* This implements the MCI bluetooth coexistence handling.
*/
#include "opt_ath.h"
#include "opt_inet.h"
#include "opt_wlan.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/sysctl.h>
#include <sys/kernel.h>
#include <sys/lock.h>
#include <sys/malloc.h>
#include <sys/mutex.h>
#include <sys/errno.h>
#include <machine/bus.h>
#include <machine/resource.h>
#include <sys/bus.h>
#include <sys/socket.h>
#include <net/if.h>
#include <net/if_var.h>
#include <net/if_media.h>
#include <net/if_arp.h>
#include <net/ethernet.h> /* XXX for ether_sprintf */
#include <net80211/ieee80211_var.h>
#include <net/bpf.h>
#ifdef INET
#include <netinet/in.h>
#include <netinet/if_ether.h>
#endif
#include <dev/ath/if_athvar.h>
#include <dev/ath/if_ath_debug.h>
#include <dev/ath/if_ath_descdma.h>
#include <dev/ath/if_ath_btcoex.h>
#include <dev/ath/if_ath_btcoex_mci.h>
MALLOC_DECLARE(M_ATHDEV);
#define ATH_MCI_GPM_MAX_ENTRY 16
#define ATH_MCI_GPM_BUF_SIZE (ATH_MCI_GPM_MAX_ENTRY * 16)
#define ATH_MCI_SCHED_BUF_SIZE (16 * 16) /* 16 entries, 4 dword each */
static void ath_btcoex_mci_update_wlan_channels(struct ath_softc *sc);
int
ath_btcoex_mci_attach(struct ath_softc *sc)
{
int buflen, error;
buflen = ATH_MCI_GPM_BUF_SIZE + ATH_MCI_SCHED_BUF_SIZE;
error = ath_descdma_alloc_desc(sc, &sc->sc_btcoex.buf, NULL,
"MCI bufs", buflen, 1);
if (error != 0) {
device_printf(sc->sc_dev, "%s: failed to alloc MCI RAM\n",
__func__);
return (error);
}
/* Yes, we're going to do bluetooth MCI coex */
sc->sc_btcoex_mci = 1;
/* Initialise the wlan channel mapping */
sc->sc_btcoex.wlan_channels[0] = 0x00000000;
sc->sc_btcoex.wlan_channels[1] = 0xffffffff;
sc->sc_btcoex.wlan_channels[2] = 0xffffffff;
sc->sc_btcoex.wlan_channels[3] = 0x7fffffff;
/*
* Ok, so the API is a bit odd. It assumes sched_addr is
* after gpm_addr, and it does math to figure out the right
* sched_buf pointer.
*
* So, set gpm_addr to buf, sched_addr to gpm_addr + ATH_MCI_GPM_BUF_SIZE,
* the HAL call with do (gpm_buf + (sched_addr - gpm_addr)) to
* set sched_buf, and we're "golden".
*
* Note, it passes in 'len' here (gpm_len) as
* ATH_MCI_GPM_BUF_SIZE >> 4. My guess is that it's 16
* bytes per entry and we're storing 16 entries.
*/
sc->sc_btcoex.gpm_buf = (void *) sc->sc_btcoex.buf.dd_desc;
sc->sc_btcoex.sched_buf = sc->sc_btcoex.gpm_buf +
ATH_MCI_GPM_BUF_SIZE;
sc->sc_btcoex.gpm_paddr = sc->sc_btcoex.buf.dd_desc_paddr;
sc->sc_btcoex.sched_paddr = sc->sc_btcoex.gpm_paddr +
ATH_MCI_GPM_BUF_SIZE;
/* memset the gpm buffer with MCI_GPM_RSVD_PATTERN */
memset(sc->sc_btcoex.gpm_buf, 0xfe, buflen);
/*
* This is an unfortunate x86'ism in the HAL - the
* HAL code expects the passed in buffer to be
* coherent, and doesn't implement /any/ kind
* of buffer sync operations at all.
*
* So, this code will only work on dma coherent buffers
* and will behave poorly on non-coherent systems.
* Fixing this would require some HAL surgery so it
* actually /did/ the buffer flushing as appropriate.
*/
ath_hal_btcoex_mci_setup(sc->sc_ah,
sc->sc_btcoex.gpm_paddr,
sc->sc_btcoex.gpm_buf,
ATH_MCI_GPM_BUF_SIZE >> 4,
sc->sc_btcoex.sched_paddr);
return (0);
}
/*
* Detach btcoex from the given interface
*/
int
ath_btcoex_mci_detach(struct ath_softc *sc)
{
ath_hal_btcoex_mci_detach(sc->sc_ah);
ath_descdma_cleanup(sc, &sc->sc_btcoex.buf, NULL);
return (0);
}
/*
* Configure or disable bluetooth coexistence on the given channel.
*
* For MCI, we just use the top-level enable/disable flag, and
* then the MCI reset / channel update path will configure things
* appropriately based on the current band.
*/
int
ath_btcoex_mci_enable(struct ath_softc *sc,
const struct ieee80211_channel *chan)
{
/*
* Always reconfigure stomp-all for now, so wlan wins.
*
* The default weights still don't allow beacons to win,
* so unless you set net.wlan.X.bmiss_max to something higher,
* net80211 will disconnect you during a HCI INQUIRY command.
*
* The longer-term solution is to dynamically adjust whether
* bmiss happens based on bluetooth requirements, and look at
* making the individual stomp bits configurable.
*/
ath_hal_btcoex_set_weights(sc->sc_ah, HAL_BT_COEX_STOMP_ALL);
/*
* update wlan channels so the firmware knows what channels it
* can/can't use.
*/
ath_btcoex_mci_update_wlan_channels(sc);
return (0);
}
/*
* XXX TODO: turn into general btcoex, and then make this
* the MCI specific bits.
*/
static void
ath_btcoex_mci_event(struct ath_softc *sc, ATH_BT_COEX_EVENT nevent,
void *param)
{
if (! sc->sc_btcoex_mci)
return;
/*
* Check whether we need to flush our local profile cache.
* If we do, then at (XXX TODO) we should flush our state,
* then wait for the MCI response with the updated profile list.
*/
if (ath_hal_btcoex_mci_state(sc->sc_ah,
HAL_MCI_STATE_NEED_FLUSH_BT_INFO, NULL) != 0) {
uint32_t data = 0;
if (ath_hal_btcoex_mci_state(sc->sc_ah,
HAL_MCI_STATE_ENABLE, NULL) != 0) {
DPRINTF(sc, ATH_DEBUG_BTCOEX,
"(MCI) Flush BT profile\n");
/*
* XXX TODO: flush profile state on the ath(4)
* driver side; subsequent messages will come
* through with the current list of active
* profiles.
*/
ath_hal_btcoex_mci_state(sc->sc_ah,
HAL_MCI_STATE_NEED_FLUSH_BT_INFO, &data);
ath_hal_btcoex_mci_state(sc->sc_ah,
HAL_MCI_STATE_SEND_STATUS_QUERY, NULL);
}
}
if (nevent == ATH_COEX_EVENT_BT_NOOP) {
DPRINTF(sc, ATH_DEBUG_BTCOEX, "(MCI) BT_NOOP\n");
return;
}
}
static void
ath_btcoex_mci_send_gpm(struct ath_softc *sc, uint32_t *payload)
{
ath_hal_btcoex_mci_send_message(sc->sc_ah, MCI_GPM, 0, payload, 16,
AH_FALSE, AH_TRUE);
}
/*
* This starts a BT calibration. It requires a chip reset.
*/
static int
ath_btcoex_mci_bt_cal_do(struct ath_softc *sc, int tx_timeout, int rx_timeout)
{
device_printf(sc->sc_dev, "%s: TODO!\n", __func__);
return (0);
}
static void
ath_btcoex_mci_cal_msg(struct ath_softc *sc, uint8_t opcode,
uint8_t *rx_payload)
{
uint32_t payload[4] = {0, 0, 0, 0};
switch (opcode) {
case MCI_GPM_BT_CAL_REQ:
DPRINTF(sc, ATH_DEBUG_BTCOEX, "(MCI) receive BT_CAL_REQ\n");
if (ath_hal_btcoex_mci_state(sc->sc_ah, HAL_MCI_STATE_BT,
NULL) == MCI_BT_AWAKE) {
ath_hal_btcoex_mci_state(sc->sc_ah,
HAL_MCI_STATE_SET_BT_CAL_START, NULL);
ath_btcoex_mci_bt_cal_do(sc, 1000, 1000);
} else {
DPRINTF(sc, ATH_DEBUG_BTCOEX,
"(MCI) State mismatches: %d\n",
ath_hal_btcoex_mci_state(sc->sc_ah,
HAL_MCI_STATE_BT, NULL));
}
break;
case MCI_GPM_BT_CAL_DONE:
DPRINTF(sc, ATH_DEBUG_BTCOEX, "(MCI) receive BT_CAL_DONE\n");
if (ath_hal_btcoex_mci_state(sc->sc_ah, HAL_MCI_STATE_BT,
NULL) == MCI_BT_CAL) {
DPRINTF(sc, ATH_DEBUG_BTCOEX,
"(MCI) ERROR ILLEGAL!\n");
} else {
DPRINTF(sc, ATH_DEBUG_BTCOEX,
"(MCI) BT not in CAL state.\n");
}
break;
case MCI_GPM_BT_CAL_GRANT:
DPRINTF(sc, ATH_DEBUG_BTCOEX, "(MCI) receive BT_CAL_GRANT\n");
/* Send WLAN_CAL_DONE for now */
DPRINTF(sc, ATH_DEBUG_BTCOEX, "(MCI) Send WLAN_CAL_DONE\n");
MCI_GPM_SET_CAL_TYPE(payload, MCI_GPM_WLAN_CAL_DONE);
ath_btcoex_mci_send_gpm(sc, &payload[0]);
break;
default:
DPRINTF(sc, ATH_DEBUG_BTCOEX,
"(MCI) Unknown GPM CAL message.\n");
break;
}
}
/*
* Update the bluetooth channel map.
*
* This map tells the bluetooth device which bluetooth channels
* are available for data.
*
* For 5GHz, all channels are available.
* For 2GHz, the current wifi channel range is blocked out,
* and the rest are available.
*
* This narrows which frequencies are used by the device when
* it initiates a transfer, thus hopefully reducing the chances
* of collisions (both hopefully on the current device and
* other devices in the same channel.)
*/
static void
ath_btcoex_mci_update_wlan_channels(struct ath_softc *sc)
{
struct ieee80211com *ic = &sc->sc_ic;
struct ieee80211_channel *chan = ic->ic_curchan;
uint32_t channel_info[4] =
{ 0x00000000, 0xffffffff, 0xffffffff, 0x7fffffff };
int32_t wl_chan, bt_chan, bt_start = 0, bt_end = 79;
/* BT channel frequency is 2402 + k, k = 0 ~ 78 */
if (IEEE80211_IS_CHAN_2GHZ(chan)) {
wl_chan = chan->ic_freq - 2402;
if (IEEE80211_IS_CHAN_HT40U(chan)) {
bt_start = wl_chan - 10;
bt_end = wl_chan + 30;
} else if (IEEE80211_IS_CHAN_HT40D(chan)) {
bt_start = wl_chan - 30;
bt_end = wl_chan + 10;
} else {
/* Assume 20MHz */
bt_start = wl_chan - 10;
bt_end = wl_chan + 10;
}
bt_start -= 7;
bt_end += 7;
if (bt_start < 0) {
bt_start = 0;
}
if (bt_end > MCI_NUM_BT_CHANNELS) {
bt_end = MCI_NUM_BT_CHANNELS;
}
DPRINTF(sc, ATH_DEBUG_BTCOEX, "(MCI) WLAN use channel %d\n",
chan->ic_freq);
DPRINTF(sc, ATH_DEBUG_BTCOEX,
"(MCI) mask BT channel %d - %d\n", bt_start, bt_end);
for (bt_chan = bt_start; bt_chan < bt_end; bt_chan++) {
MCI_GPM_CLR_CHANNEL_BIT(&channel_info[0], bt_chan);
}
} else {
DPRINTF(sc, ATH_DEBUG_BTCOEX,
"(MCI) WLAN not use any 2G channel, unmask all for BT\n");
}
ath_hal_btcoex_mci_state(sc->sc_ah, HAL_MCI_STATE_SEND_WLAN_CHANNELS,
&channel_info[0]);
}
static void
ath_btcoex_mci_coex_msg(struct ath_softc *sc, uint8_t opcode,
uint8_t *rx_payload)
{
uint32_t version;
uint8_t major;
uint8_t minor;
uint32_t seq_num;
switch (opcode) {
case MCI_GPM_COEX_VERSION_QUERY:
DPRINTF(sc, ATH_DEBUG_BTCOEX,
"(MCI) Recv GPM COEX Version Query.\n");
version = ath_hal_btcoex_mci_state(sc->sc_ah,
HAL_MCI_STATE_SEND_WLAN_COEX_VERSION, NULL);
break;
case MCI_GPM_COEX_VERSION_RESPONSE:
DPRINTF(sc, ATH_DEBUG_BTCOEX,
"(MCI) Recv GPM COEX Version Response.\n");
major = *(rx_payload + MCI_GPM_COEX_B_MAJOR_VERSION);
minor = *(rx_payload + MCI_GPM_COEX_B_MINOR_VERSION);
DPRINTF(sc, ATH_DEBUG_BTCOEX,
"(MCI) BT Coex version: %d.%d\n", major, minor);
version = (major << 8) + minor;
version = ath_hal_btcoex_mci_state(sc->sc_ah,
HAL_MCI_STATE_SET_BT_COEX_VERSION, &version);
break;
case MCI_GPM_COEX_STATUS_QUERY:
DPRINTF(sc, ATH_DEBUG_BTCOEX,
"(MCI) Recv GPM COEX Status Query = 0x%02x.\n",
*(rx_payload + MCI_GPM_COEX_B_WLAN_BITMAP));
ath_hal_btcoex_mci_state(sc->sc_ah,
HAL_MCI_STATE_SEND_WLAN_CHANNELS, NULL);
break;
case MCI_GPM_COEX_BT_PROFILE_INFO:
/*
* XXX TODO: here is where we'd parse active profile
* info and make driver/stack choices as appropriate.
*/
DPRINTF(sc, ATH_DEBUG_BTCOEX,
"(MCI) TODO: Recv GPM COEX BT_Profile_Info.\n");
break;
case MCI_GPM_COEX_BT_STATUS_UPDATE:
seq_num = *((uint32_t *)(rx_payload + 12));
DPRINTF(sc, ATH_DEBUG_BTCOEX,
"(MCI) Recv GPM COEX BT_Status_Update: SEQ=%d\n",
seq_num);
break;
default:
DPRINTF(sc, ATH_DEBUG_BTCOEX,
"(MCI) Unknown GPM COEX message = 0x%02x\n", opcode);
break;
}
}
void
ath_btcoex_mci_intr(struct ath_softc *sc)
{
uint32_t mciInt, mciIntRxMsg;
uint32_t offset, subtype, opcode;
uint32_t *pGpm;
uint32_t more_data = HAL_MCI_GPM_MORE;
bool skip_gpm = false;
DPRINTF(sc, ATH_DEBUG_BTCOEX, "%s: called\n", __func__);
ath_hal_btcoex_mci_get_interrupt(sc->sc_ah, &mciInt, &mciIntRxMsg);
if (ath_hal_btcoex_mci_state(sc->sc_ah, HAL_MCI_STATE_ENABLE,
NULL) == 0) {
ath_hal_btcoex_mci_state(sc->sc_ah,
HAL_MCI_STATE_INIT_GPM_OFFSET, NULL);
DPRINTF(sc, ATH_DEBUG_BTCOEX,
"(MCI) INTR but MCI_disabled\n");
DPRINTF(sc, ATH_DEBUG_BTCOEX,
"(MCI) MCI interrupt: mciInt = 0x%x, mciIntRxMsg = 0x%x\n",
mciInt, mciIntRxMsg);
return;
}
if (mciIntRxMsg & HAL_MCI_INTERRUPT_RX_MSG_REQ_WAKE) {
uint32_t payload4[4] = { 0xffffffff, 0xffffffff, 0xffffffff,
0xffffff00};
/*
* The following REMOTE_RESET and SYS_WAKING used to sent
* only when BT wake up. Now they are always sent, as a
* recovery method to reset BT MCI's RX alignment.
*/
DPRINTF(sc, ATH_DEBUG_BTCOEX,
"(MCI) 1. INTR Send REMOTE_RESET\n");
ath_hal_btcoex_mci_send_message(sc->sc_ah,
MCI_REMOTE_RESET, 0, payload4, 16, AH_TRUE, AH_FALSE);
DPRINTF(sc, ATH_DEBUG_BTCOEX,
"(MCI) 1. INTR Send SYS_WAKING\n");
ath_hal_btcoex_mci_send_message(sc->sc_ah,
MCI_SYS_WAKING, 0, NULL, 0, AH_TRUE, AH_FALSE);
mciIntRxMsg &= ~HAL_MCI_INTERRUPT_RX_MSG_REQ_WAKE;
ath_hal_btcoex_mci_state(sc->sc_ah,
HAL_MCI_STATE_RESET_REQ_WAKE, NULL);
/* always do this for recovery and 2G/5G toggling and LNA_TRANS */
DPRINTF(sc, ATH_DEBUG_BTCOEX,
"(MCI) 1. Set BT state to AWAKE.\n");
ath_hal_btcoex_mci_state(sc->sc_ah,
HAL_MCI_STATE_SET_BT_AWAKE, NULL);
}
/* Processing SYS_WAKING/SYS_SLEEPING */
if (mciIntRxMsg & HAL_MCI_INTERRUPT_RX_MSG_SYS_WAKING) {
mciIntRxMsg &= ~HAL_MCI_INTERRUPT_RX_MSG_SYS_WAKING;
if (ath_hal_btcoex_mci_state(sc->sc_ah, HAL_MCI_STATE_BT,
NULL) == MCI_BT_SLEEP) {
if (ath_hal_btcoex_mci_state(sc->sc_ah,
HAL_MCI_STATE_REMOTE_SLEEP, NULL) == MCI_BT_SLEEP) {
DPRINTF(sc, ATH_DEBUG_BTCOEX,
"(MCI) 2. BT stays in SLEEP mode.\n");
} else {
DPRINTF(sc, ATH_DEBUG_BTCOEX,
"(MCI) 2. Set BT state to AWAKE.\n");
ath_hal_btcoex_mci_state(sc->sc_ah,
HAL_MCI_STATE_SET_BT_AWAKE, NULL);
}
} else {
DPRINTF(sc, ATH_DEBUG_BTCOEX,
"(MCI) 2. BT stays in AWAKE mode.\n");
}
}
if (mciIntRxMsg & HAL_MCI_INTERRUPT_RX_MSG_SYS_SLEEPING) {
mciIntRxMsg &= ~HAL_MCI_INTERRUPT_RX_MSG_SYS_SLEEPING;
if (ath_hal_btcoex_mci_state(sc->sc_ah, HAL_MCI_STATE_BT,
NULL) == MCI_BT_AWAKE) {
if (ath_hal_btcoex_mci_state(sc->sc_ah,
HAL_MCI_STATE_REMOTE_SLEEP, NULL) == MCI_BT_AWAKE) {
DPRINTF(sc, ATH_DEBUG_BTCOEX,
"(MCI) 3. BT stays in AWAKE mode.\n");
} else {
DPRINTF(sc, ATH_DEBUG_BTCOEX,
"(MCI) 3. Set BT state to SLEEP.\n");
ath_hal_btcoex_mci_state(sc->sc_ah,
HAL_MCI_STATE_SET_BT_SLEEP, NULL);
}
} else {
DPRINTF(sc, ATH_DEBUG_BTCOEX,
"(MCI) 3. BT stays in SLEEP mode.\n");
}
}
/*
* Recover from out-of-order / wrong-offset GPM messages.
*/
if ((mciInt & HAL_MCI_INTERRUPT_RX_INVALID_HDR) ||
(mciInt & HAL_MCI_INTERRUPT_CONT_INFO_TIMEOUT)) {
DPRINTF(sc, ATH_DEBUG_BTCOEX,
"(MCI) MCI RX broken, skip GPM messages\n");
ath_hal_btcoex_mci_state(sc->sc_ah,
HAL_MCI_STATE_RECOVER_RX, NULL);
skip_gpm = true;
}
if (mciIntRxMsg & HAL_MCI_INTERRUPT_RX_MSG_SCHD_INFO) {
mciIntRxMsg &= ~HAL_MCI_INTERRUPT_RX_MSG_SCHD_INFO;
offset = ath_hal_btcoex_mci_state(sc->sc_ah,
HAL_MCI_STATE_LAST_SCHD_MSG_OFFSET, NULL);
}
/*
* Parse GPM messages.
*/
if (mciIntRxMsg & HAL_MCI_INTERRUPT_RX_MSG_GPM) {
mciIntRxMsg &= ~HAL_MCI_INTERRUPT_RX_MSG_GPM;
while (more_data == HAL_MCI_GPM_MORE) {
pGpm = (void *) sc->sc_btcoex.gpm_buf;
offset = ath_hal_btcoex_mci_state(sc->sc_ah,
HAL_MCI_STATE_NEXT_GPM_OFFSET, &more_data);
if (offset == HAL_MCI_GPM_INVALID)
break;
pGpm += (offset >> 2);
/*
* The first DWORD is a timer.
* The real data starts from the second DWORD.
*/
subtype = MCI_GPM_TYPE(pGpm);
opcode = MCI_GPM_OPCODE(pGpm);
if (!skip_gpm) {
if (MCI_GPM_IS_CAL_TYPE(subtype)) {
ath_btcoex_mci_cal_msg(sc, subtype,
(uint8_t*) pGpm);
} else {
switch (subtype) {
case MCI_GPM_COEX_AGENT:
ath_btcoex_mci_coex_msg(sc,
opcode, (uint8_t*) pGpm);
break;
case MCI_GPM_BT_DEBUG:
device_printf(sc->sc_dev,
"(MCI) TODO: GPM_BT_DEBUG!\n");
break;
default:
DPRINTF(sc, ATH_DEBUG_BTCOEX,
"(MCI) Unknown GPM message.\n");
break;
}
}
}
MCI_GPM_RECYCLE(pGpm);
}
}
/*
* This is monitoring/management information messages, so the driver
* layer can hook in and dynamically adjust things like aggregation
* size, expected bluetooth/wifi traffic throughput, etc.
*
* None of that is done right now; it just passes off the values
* to the HAL so it can update its internal state as appropriate.
* This code just prints out the values for debugging purposes.
*/
if (mciIntRxMsg & HAL_MCI_INTERRUPT_RX_MSG_MONITOR) {
if (mciIntRxMsg & HAL_MCI_INTERRUPT_RX_MSG_LNA_CONTROL) {
mciIntRxMsg &= ~HAL_MCI_INTERRUPT_RX_MSG_LNA_CONTROL;
DPRINTF(sc, ATH_DEBUG_BTCOEX, "(MCI) LNA_CONTROL\n");
}
if (mciIntRxMsg & HAL_MCI_INTERRUPT_RX_MSG_LNA_INFO) {
mciIntRxMsg &= ~HAL_MCI_INTERRUPT_RX_MSG_LNA_INFO;
DPRINTF(sc, ATH_DEBUG_BTCOEX, "(MCI) LNA_INFO\n");
}
if (mciIntRxMsg & HAL_MCI_INTERRUPT_RX_MSG_CONT_INFO) {
int8_t value_dbm = ath_hal_btcoex_mci_state(sc->sc_ah,
HAL_MCI_STATE_CONT_RSSI_POWER, NULL);
mciIntRxMsg &= ~HAL_MCI_INTERRUPT_RX_MSG_CONT_INFO;
if (ath_hal_btcoex_mci_state(sc->sc_ah,
HAL_MCI_STATE_CONT_TXRX, NULL)) {
DPRINTF(sc, ATH_DEBUG_BTCOEX,
"(MCI) CONT_INFO: (tx) pri = %d, pwr = %d dBm\n",
ath_hal_btcoex_mci_state(sc->sc_ah,
HAL_MCI_STATE_CONT_PRIORITY, NULL),
value_dbm);
} else {
DPRINTF(sc, ATH_DEBUG_BTCOEX,
"(MCI) CONT_INFO: (rx) pri = %d, rssi = %d dBm\n",
ath_hal_btcoex_mci_state(sc->sc_ah,
HAL_MCI_STATE_CONT_PRIORITY, NULL),
value_dbm);
}
}
if (mciIntRxMsg & HAL_MCI_INTERRUPT_RX_MSG_CONT_NACK) {
mciIntRxMsg &= ~HAL_MCI_INTERRUPT_RX_MSG_CONT_NACK;
DPRINTF(sc, ATH_DEBUG_BTCOEX, "(MCI) CONT_NACK\n");
}
if (mciIntRxMsg & HAL_MCI_INTERRUPT_RX_MSG_CONT_RST) {
mciIntRxMsg &= ~HAL_MCI_INTERRUPT_RX_MSG_CONT_RST;
DPRINTF(sc, ATH_DEBUG_BTCOEX, "(MCI) CONT_RST\n");
}
}
/*
* Recover the state engine if we hit an invalid header/timeout.
* This is the final part of GPT out-of-sync recovery.
*/
if ((mciInt & HAL_MCI_INTERRUPT_RX_INVALID_HDR) ||
(mciInt & HAL_MCI_INTERRUPT_CONT_INFO_TIMEOUT)) {
ath_btcoex_mci_event(sc, ATH_COEX_EVENT_BT_NOOP, NULL);
mciInt &= ~(HAL_MCI_INTERRUPT_RX_INVALID_HDR |
HAL_MCI_INTERRUPT_CONT_INFO_TIMEOUT);
}
if (mciIntRxMsg & 0xfffffffe) {
DPRINTF(sc, ATH_DEBUG_BTCOEX,
"(MCI) Not processed IntRxMsg = 0x%x\n", mciIntRxMsg);
}
}

View File

@ -0,0 +1,42 @@
/*-
* Copyright (c) 2016 Adrian Chadd <adrian@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer,
* without modification.
* 2. Redistributions in binary form must reproduce at minimum a disclaimer
* similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
* redistribution must be conditioned upon including a substantially
* similar Disclaimer requirement for further binary redistribution.
*
* NO WARRANTY
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGES.
*
* $FreeBSD$
*/
#ifndef __IF_ATH_BTCOEX_MCI_H__
#define __IF_ATH_BTCOEX_MCI_H__
#define ATH_MCI_NUM_BT_CHANNELS 79
extern int ath_btcoex_mci_attach(struct ath_softc *sc);
extern int ath_btcoex_mci_detach(struct ath_softc *sc);
extern int ath_btcoex_mci_enable(struct ath_softc *sc,
const struct ieee80211_channel *chan);
extern void ath_btcoex_mci_intr(struct ath_softc *sc);
#endif /* __IF_ATH_BTCOEX_MCI_H__ */

View File

@ -1738,6 +1738,15 @@ ath_tx_normal_setup(struct ath_softc *sc, struct ieee80211_node *ni,
}
#endif
#if 0
/*
* Placeholder: if you want to transmit with the azimuth
* timestamp in the end of the payload, here's where you
* should set the TXDESC field.
*/
flags |= HAL_TXDESC_HWTS;
#endif
/*
* Determine if a tx interrupt should be generated for
* this descriptor. We take a tx interrupt to reap

View File

@ -656,7 +656,8 @@ struct ath_softc {
sc_has_ldpc : 1,
sc_hasenforcetxop : 1, /* support enforce TxOP */
sc_hasdivcomb : 1, /* RX diversity combining */
sc_rx_lnamixer : 1; /* RX using LNA mixing */
sc_rx_lnamixer : 1, /* RX using LNA mixing */
sc_btcoex_mci : 1; /* MCI bluetooth coex */
int sc_cabq_enable; /* Enable cabq transmission */
@ -908,6 +909,19 @@ struct ath_softc {
/* ATH_PCI_* flags */
uint32_t sc_pci_devinfo;
/* BT coex */
struct {
struct ath_descdma buf;
/* gpm/sched buffer, saved pointers */
char *sched_buf;
bus_addr_t sched_paddr;
char *gpm_buf;
bus_addr_t gpm_paddr;
uint32_t wlan_channels[4];
} sc_btcoex;
};
#define ATH_LOCK_INIT(_sc) \
@ -1488,8 +1502,6 @@ void ath_intr(void *);
((*(_ah)->ah_btCoexSetQcuThresh)((_ah), (_qcuid)))
#define ath_hal_btcoex_set_weights(_ah, _weight) \
((*(_ah)->ah_btCoexSetWeights)((_ah), (_weight)))
#define ath_hal_btcoex_set_weights(_ah, _weight) \
((*(_ah)->ah_btCoexSetWeights)((_ah), (_weight)))
#define ath_hal_btcoex_set_bmiss_thresh(_ah, _thr) \
((*(_ah)->ah_btCoexSetBmissThresh)((_ah), (_thr)))
#define ath_hal_btcoex_set_parameter(_ah, _attrib, _val) \
@ -1499,6 +1511,17 @@ void ath_intr(void *);
#define ath_hal_btcoex_disable(_ah) \
((*(_ah)->ah_btCoexDisable)((_ah)))
#define ath_hal_btcoex_mci_setup(_ah, _gp, _gb, _gl, _sp) \
((*(_ah)->ah_btMciSetup)((_ah), (_gp), (_gb), (_gl), (_sp)))
#define ath_hal_btcoex_mci_send_message(_ah, _h, _f, _p, _l, _wd, _cbt) \
((*(_ah)->ah_btMciSendMessage)((_ah), (_h), (_f), (_p), (_l), (_wd), (_cbt)))
#define ath_hal_btcoex_mci_get_interrupt(_ah, _mi, _mm) \
((*(_ah)->ah_btMciGetInterrupt)((_ah), (_mi), (_mm)))
#define ath_hal_btcoex_mci_state(_ah, _st, _pd) \
((*(_ah)->ah_btMciState)((_ah), (_st), (_pd)))
#define ath_hal_btcoex_mci_detach(_ah) \
((*(_ah)->ah_btMciDetach)((_ah)))
#define ath_hal_div_comb_conf_get(_ah, _conf) \
((*(_ah)->ah_divLnaConfGet)((_ah), (_conf)))
#define ath_hal_div_comb_conf_set(_ah, _conf) \

View File

@ -38,7 +38,7 @@ KMOD= if_ath
SRCS= if_ath.c if_ath_alq.c if_ath_debug.c if_ath_keycache.c if_ath_sysctl.c
SRCS+= if_ath_tx.c if_ath_tx_ht.c if_ath_led.c if_ath_rx.c if_ath_tdma.c
SRCS+= if_ath_beacon.c if_ath_rx_edma.c if_ath_tx_edma.c if_ath_spectral.c
SRCS+= if_ath_btcoex.c if_ath_lna_div.c if_ath_ioctl.c if_ath_descdma.c
SRCS+= if_ath_btcoex.c if_ath_btcoex_mci.c if_ath_lna_div.c if_ath_ioctl.c if_ath_descdma.c
# NB: v3 eeprom support used by both AR5211 and AR5212; just include it
SRCS+= ah_osdep.c ah.c ah_regdomain.c ah_eeprom_v3.c
SRCS+= device_if.h bus_if.h pci_if.h opt_inet.h opt_ath.h opt_ah.h opt_wlan.h