Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 277644
b: refs/heads/master
c: 7dc181c
h: refs/heads/master
v: v3
  • Loading branch information
Rajkumar Manoharan authored and John W. Linville committed Nov 8, 2011
1 parent 38f90d1 commit 262c390
Show file tree
Hide file tree
Showing 12 changed files with 404 additions and 8 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 38df2f07b7bc5309ebb159438b435d1f25f31e35
refs/heads/master: 7dc181c273861c4d96991f59a4fdcda3a3eaccae
1 change: 1 addition & 0 deletions trunk/drivers/net/wireless/ath/ath.h
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,7 @@ enum ATH_DEBUG {
ATH_DBG_BTCOEX = 0x00002000,
ATH_DBG_WMI = 0x00004000,
ATH_DBG_BSTUCK = 0x00008000,
ATH_DBG_MCI = 0x00010000,
ATH_DBG_ANY = 0xffffffff
};

Expand Down
1 change: 1 addition & 0 deletions trunk/drivers/net/wireless/ath/ath9k/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ ath9k-y += beacon.o \
main.o \
recv.o \
xmit.o \
mci.o \

ath9k-$(CONFIG_ATH9K_RATE_CONTROL) += rc.o
ath9k-$(CONFIG_ATH9K_PCI) += pci.o
Expand Down
3 changes: 3 additions & 0 deletions trunk/drivers/net/wireless/ath/ath9k/ath9k.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@

#include "debug.h"
#include "common.h"
#include "mci.h"

/*
* Header for the ath9k.ko driver core *only* -- hw code nor any other driver
Expand Down Expand Up @@ -443,7 +444,9 @@ struct ath_btcoex {
u32 btcoex_no_stomp; /* in usec */
u32 btcoex_period; /* in usec */
u32 btscan_no_stomp; /* in usec */
u32 duty_cycle;
struct ath_gen_timer *no_stomp_timer; /* Timer for no BT stomping */
struct ath_mci_profile mci;
};

int ath_init_btcoex_timer(struct ath_softc *sc);
Expand Down
7 changes: 4 additions & 3 deletions trunk/drivers/net/wireless/ath/ath9k/gpio.c
Original file line number Diff line number Diff line change
Expand Up @@ -189,8 +189,8 @@ static void ath_btcoex_period_timer(unsigned long data)
bool is_btscan;

ath9k_ps_wakeup(sc);
ath_detect_bt_priority(sc);

if (!(ah->caps.hw_caps & ATH9K_HW_CAP_MCI))
ath_detect_bt_priority(sc);
is_btscan = sc->sc_flags & SC_OP_BT_SCAN;

spin_lock_bh(&btcoex->btcoex_lock);
Expand All @@ -212,8 +212,9 @@ static void ath_btcoex_period_timer(unsigned long data)
}

ath9k_ps_restore(sc);
timer_period = btcoex->btcoex_period / 1000;
mod_timer(&btcoex->period_timer, jiffies +
msecs_to_jiffies(ATH_BTCOEX_DEF_BT_PERIOD));
msecs_to_jiffies(timer_period));
}

/*
Expand Down
2 changes: 1 addition & 1 deletion trunk/drivers/net/wireless/ath/ath9k/hw.c
Original file line number Diff line number Diff line change
Expand Up @@ -2331,7 +2331,7 @@ int ath9k_hw_fill_cap_info(struct ath_hw *ah)
ah->enabled_cals |= TX_IQ_ON_AGC_CAL;
}
if (AR_SREV_9462(ah))
pCap->hw_caps |= ATH9K_HW_CAP_RTT;
pCap->hw_caps |= ATH9K_HW_CAP_RTT | ATH9K_HW_CAP_MCI;

return 0;
}
Expand Down
11 changes: 11 additions & 0 deletions trunk/drivers/net/wireless/ath/ath9k/hw.h
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,7 @@ enum ath9k_hw_caps {
ATH9K_HW_CAP_5GHZ = BIT(14),
ATH9K_HW_CAP_APM = BIT(15),
ATH9K_HW_CAP_RTT = BIT(16),
ATH9K_HW_CAP_MCI = BIT(17),
};

struct ath9k_hw_capabilities {
Expand Down Expand Up @@ -419,6 +420,16 @@ enum ath9k_rx_qtype {
ATH9K_RX_QUEUE_MAX,
};

enum ath_mci_gpm_coex_profile_type {
MCI_GPM_COEX_PROFILE_UNKNOWN,
MCI_GPM_COEX_PROFILE_RFCOMM,
MCI_GPM_COEX_PROFILE_A2DP,
MCI_GPM_COEX_PROFILE_HID,
MCI_GPM_COEX_PROFILE_BNEP,
MCI_GPM_COEX_PROFILE_VOICE,
MCI_GPM_COEX_PROFILE_MAX
};

struct ath9k_beacon_state {
u32 bs_nexttbtt;
u32 bs_nextdtim;
Expand Down
2 changes: 2 additions & 0 deletions trunk/drivers/net/wireless/ath/ath9k/init.c
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,8 @@ static int ath9k_init_btcoex(struct ath_softc *sc)
txq = sc->tx.txq_map[WME_AC_BE];
ath9k_hw_init_btcoex_hw(sc->sc_ah, txq->axq_qnum);
sc->btcoex.bt_stomp_type = ATH_BTCOEX_STOMP_LOW;
sc->btcoex.duty_cycle = ATH_BTCOEX_DEF_DUTY_CYCLE;
INIT_LIST_HEAD(&sc->btcoex.mci.info);
break;
default:
WARN_ON(1);
Expand Down
6 changes: 4 additions & 2 deletions trunk/drivers/net/wireless/ath/ath9k/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -1133,8 +1133,9 @@ static int ath9k_start(struct ieee80211_hw *hw)

if ((ah->btcoex_hw.scheme != ATH_BTCOEX_CFG_NONE) &&
!ah->btcoex_hw.enabled) {
ath9k_hw_btcoex_set_weight(ah, AR_BT_COEX_WGHT,
AR_STOMP_LOW_WLAN_WGHT);
if (!(sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_MCI))
ath9k_hw_btcoex_set_weight(ah, AR_BT_COEX_WGHT,
AR_STOMP_LOW_WLAN_WGHT);
ath9k_hw_btcoex_enable(ah);

if (ah->btcoex_hw.scheme == ATH_BTCOEX_CFG_3WIRE)
Expand Down Expand Up @@ -1237,6 +1238,7 @@ static void ath9k_stop(struct ieee80211_hw *hw)
ath9k_hw_btcoex_disable(ah);
if (ah->btcoex_hw.scheme == ATH_BTCOEX_CFG_3WIRE)
ath9k_btcoex_timer_pause(sc);
ath_mci_flush_profile(&sc->btcoex.mci);
}

spin_lock_bh(&sc->sc_pcu_lock);
Expand Down
254 changes: 254 additions & 0 deletions trunk/drivers/net/wireless/ath/ath9k/mci.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,254 @@
/*
* Copyright (c) 2010-2011 Atheros Communications Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

#include "ath9k.h"
#include "mci.h"

u8 ath_mci_duty_cycle[] = { 0, 50, 60, 70, 80, 85, 90, 95, 98 };

static struct ath_mci_profile_info*
ath_mci_find_profile(struct ath_mci_profile *mci,
struct ath_mci_profile_info *info)
{
struct ath_mci_profile_info *entry;

list_for_each_entry(entry, &mci->info, list) {
if (entry->conn_handle == info->conn_handle)
break;
}
return entry;
}

static bool ath_mci_add_profile(struct ath_common *common,
struct ath_mci_profile *mci,
struct ath_mci_profile_info *info)
{
struct ath_mci_profile_info *entry;

if ((mci->num_sco == ATH_MCI_MAX_SCO_PROFILE) &&
(info->type == MCI_GPM_COEX_PROFILE_VOICE)) {
ath_dbg(common, ATH_DBG_MCI,
"Too many SCO profile, failed to add new profile\n");
return false;
}

if (((NUM_PROF(mci) - mci->num_sco) == ATH_MCI_MAX_ACL_PROFILE) &&
(info->type != MCI_GPM_COEX_PROFILE_VOICE)) {
ath_dbg(common, ATH_DBG_MCI,
"Too many ACL profile, failed to add new profile\n");
return false;
}

entry = ath_mci_find_profile(mci, info);

if (entry)
memcpy(entry, info, 10);
else {
entry = kzalloc(sizeof(*entry), GFP_KERNEL);
if (!entry)
return false;

memcpy(entry, info, 10);
INC_PROF(mci, info);
list_add_tail(&info->list, &mci->info);
}
return true;
}

static void ath_mci_del_profile(struct ath_common *common,
struct ath_mci_profile *mci,
struct ath_mci_profile_info *info)
{
struct ath_mci_profile_info *entry;

entry = ath_mci_find_profile(mci, info);

if (!entry) {
ath_dbg(common, ATH_DBG_MCI,
"Profile to be deleted not found\n");
return;
}
DEC_PROF(mci, entry);
list_del(&entry->list);
kfree(entry);
}

void ath_mci_flush_profile(struct ath_mci_profile *mci)
{
struct ath_mci_profile_info *info, *tinfo;

list_for_each_entry_safe(info, tinfo, &mci->info, list) {
list_del(&info->list);
DEC_PROF(mci, info);
kfree(info);
}
mci->aggr_limit = 0;
}

static void ath_mci_adjust_aggr_limit(struct ath_btcoex *btcoex)
{
struct ath_mci_profile *mci = &btcoex->mci;
u32 wlan_airtime = btcoex->btcoex_period *
(100 - btcoex->duty_cycle) / 100;

/*
* Scale: wlan_airtime is in ms, aggr_limit is in 0.25 ms.
* When wlan_airtime is less than 4ms, aggregation limit has to be
* adjusted half of wlan_airtime to ensure that the aggregation can fit
* without collision with BT traffic.
*/
if ((wlan_airtime <= 4) &&
(!mci->aggr_limit || (mci->aggr_limit > (2 * wlan_airtime))))
mci->aggr_limit = 2 * wlan_airtime;
}

static void ath_mci_update_scheme(struct ath_softc *sc)
{
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
struct ath_btcoex *btcoex = &sc->btcoex;
struct ath_mci_profile *mci = &btcoex->mci;
struct ath_mci_profile_info *info;
u32 num_profile = NUM_PROF(mci);

if (num_profile == 1) {
info = list_first_entry(&mci->info,
struct ath_mci_profile_info,
list);
if (mci->num_sco && info->T == 12) {
mci->aggr_limit = 8;
ath_dbg(common, ATH_DBG_MCI,
"Single SCO, aggregation limit 2 ms\n");
} else if ((info->type == MCI_GPM_COEX_PROFILE_BNEP) &&
!info->master) {
btcoex->btcoex_period = 60;
ath_dbg(common, ATH_DBG_MCI,
"Single slave PAN/FTP, bt period 60 ms\n");
} else if ((info->type == MCI_GPM_COEX_PROFILE_HID) &&
(info->T > 0 && info->T < 50) &&
(info->A > 1 || info->W > 1)) {
btcoex->duty_cycle = 30;
mci->aggr_limit = 8;
ath_dbg(common, ATH_DBG_MCI,
"Multiple attempt/timeout single HID "
"aggregation limit 2 ms dutycycle 30%%\n");
}
} else if ((num_profile == 2) && (mci->num_hid == 2)) {
btcoex->duty_cycle = 30;
mci->aggr_limit = 8;
ath_dbg(common, ATH_DBG_MCI,
"Two HIDs aggregation limit 2 ms dutycycle 30%%\n");
} else if (num_profile > 3) {
mci->aggr_limit = 6;
ath_dbg(common, ATH_DBG_MCI,
"Three or more profiles aggregation limit 1.5 ms\n");
}

if (IS_CHAN_2GHZ(sc->sc_ah->curchan)) {
if (IS_CHAN_HT(sc->sc_ah->curchan))
ath_mci_adjust_aggr_limit(btcoex);
else
btcoex->btcoex_period >>= 1;
}

ath9k_hw_btcoex_disable(sc->sc_ah);
ath9k_btcoex_timer_pause(sc);

if (IS_CHAN_5GHZ(sc->sc_ah->curchan))
return;

btcoex->duty_cycle += (mci->num_bdr ? ATH_MCI_MAX_DUTY_CYCLE : 0);
if (btcoex->duty_cycle > ATH_MCI_MAX_DUTY_CYCLE)
btcoex->duty_cycle = ATH_MCI_MAX_DUTY_CYCLE;

btcoex->btcoex_period *= 1000;
btcoex->btcoex_no_stomp = btcoex->btcoex_period *
(100 - btcoex->duty_cycle) / 100;

ath9k_hw_btcoex_enable(sc->sc_ah);
ath9k_btcoex_timer_resume(sc);
}

void ath_mci_process_profile(struct ath_softc *sc,
struct ath_mci_profile_info *info)
{
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
struct ath_btcoex *btcoex = &sc->btcoex;
struct ath_mci_profile *mci = &btcoex->mci;

if (info->start) {
if (!ath_mci_add_profile(common, mci, info))
return;
} else
ath_mci_del_profile(common, mci, info);

btcoex->btcoex_period = ATH_MCI_DEF_BT_PERIOD;
mci->aggr_limit = mci->num_sco ? 6 : 0;
if (NUM_PROF(mci)) {
btcoex->bt_stomp_type = ATH_BTCOEX_STOMP_LOW;
btcoex->duty_cycle = ath_mci_duty_cycle[NUM_PROF(mci)];
} else {
btcoex->bt_stomp_type = mci->num_mgmt ? ATH_BTCOEX_STOMP_ALL :
ATH_BTCOEX_STOMP_LOW;
btcoex->duty_cycle = ATH_BTCOEX_DEF_DUTY_CYCLE;
}

ath_mci_update_scheme(sc);
}

void ath_mci_process_status(struct ath_softc *sc,
struct ath_mci_profile_status *status)
{
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
struct ath_btcoex *btcoex = &sc->btcoex;
struct ath_mci_profile *mci = &btcoex->mci;
struct ath_mci_profile_info info;
int i = 0, old_num_mgmt = mci->num_mgmt;

/* Link status type are not handled */
if (status->is_link) {
ath_dbg(common, ATH_DBG_MCI,
"Skip link type status update\n");
return;
}

memset(&info, 0, sizeof(struct ath_mci_profile_info));

info.conn_handle = status->conn_handle;
if (ath_mci_find_profile(mci, &info)) {
ath_dbg(common, ATH_DBG_MCI,
"Skip non link state update for existing profile %d\n",
status->conn_handle);
return;
}
if (status->conn_handle >= ATH_MCI_MAX_PROFILE) {
ath_dbg(common, ATH_DBG_MCI,
"Ignore too many non-link update\n");
return;
}
if (status->is_critical)
__set_bit(status->conn_handle, mci->status);
else
__clear_bit(status->conn_handle, mci->status);

mci->num_mgmt = 0;
do {
if (test_bit(i, mci->status))
mci->num_mgmt++;
} while (++i < ATH_MCI_MAX_PROFILE);

if (old_num_mgmt != mci->num_mgmt)
ath_mci_update_scheme(sc);
}
Loading

0 comments on commit 262c390

Please sign in to comment.