Rajkumar Manoharan | 7dc181c | 2011-10-24 18:19:49 +0530 | [diff] [blame^] | 1 | /* |
| 2 | * Copyright (c) 2010-2011 Atheros Communications Inc. |
| 3 | * |
| 4 | * Permission to use, copy, modify, and/or distribute this software for any |
| 5 | * purpose with or without fee is hereby granted, provided that the above |
| 6 | * copyright notice and this permission notice appear in all copies. |
| 7 | * |
| 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
| 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
| 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
| 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
| 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
| 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
| 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
| 15 | */ |
| 16 | |
| 17 | #include "ath9k.h" |
| 18 | #include "mci.h" |
| 19 | |
| 20 | u8 ath_mci_duty_cycle[] = { 0, 50, 60, 70, 80, 85, 90, 95, 98 }; |
| 21 | |
| 22 | static struct ath_mci_profile_info* |
| 23 | ath_mci_find_profile(struct ath_mci_profile *mci, |
| 24 | struct ath_mci_profile_info *info) |
| 25 | { |
| 26 | struct ath_mci_profile_info *entry; |
| 27 | |
| 28 | list_for_each_entry(entry, &mci->info, list) { |
| 29 | if (entry->conn_handle == info->conn_handle) |
| 30 | break; |
| 31 | } |
| 32 | return entry; |
| 33 | } |
| 34 | |
| 35 | static bool ath_mci_add_profile(struct ath_common *common, |
| 36 | struct ath_mci_profile *mci, |
| 37 | struct ath_mci_profile_info *info) |
| 38 | { |
| 39 | struct ath_mci_profile_info *entry; |
| 40 | |
| 41 | if ((mci->num_sco == ATH_MCI_MAX_SCO_PROFILE) && |
| 42 | (info->type == MCI_GPM_COEX_PROFILE_VOICE)) { |
| 43 | ath_dbg(common, ATH_DBG_MCI, |
| 44 | "Too many SCO profile, failed to add new profile\n"); |
| 45 | return false; |
| 46 | } |
| 47 | |
| 48 | if (((NUM_PROF(mci) - mci->num_sco) == ATH_MCI_MAX_ACL_PROFILE) && |
| 49 | (info->type != MCI_GPM_COEX_PROFILE_VOICE)) { |
| 50 | ath_dbg(common, ATH_DBG_MCI, |
| 51 | "Too many ACL profile, failed to add new profile\n"); |
| 52 | return false; |
| 53 | } |
| 54 | |
| 55 | entry = ath_mci_find_profile(mci, info); |
| 56 | |
| 57 | if (entry) |
| 58 | memcpy(entry, info, 10); |
| 59 | else { |
| 60 | entry = kzalloc(sizeof(*entry), GFP_KERNEL); |
| 61 | if (!entry) |
| 62 | return false; |
| 63 | |
| 64 | memcpy(entry, info, 10); |
| 65 | INC_PROF(mci, info); |
| 66 | list_add_tail(&info->list, &mci->info); |
| 67 | } |
| 68 | return true; |
| 69 | } |
| 70 | |
| 71 | static void ath_mci_del_profile(struct ath_common *common, |
| 72 | struct ath_mci_profile *mci, |
| 73 | struct ath_mci_profile_info *info) |
| 74 | { |
| 75 | struct ath_mci_profile_info *entry; |
| 76 | |
| 77 | entry = ath_mci_find_profile(mci, info); |
| 78 | |
| 79 | if (!entry) { |
| 80 | ath_dbg(common, ATH_DBG_MCI, |
| 81 | "Profile to be deleted not found\n"); |
| 82 | return; |
| 83 | } |
| 84 | DEC_PROF(mci, entry); |
| 85 | list_del(&entry->list); |
| 86 | kfree(entry); |
| 87 | } |
| 88 | |
| 89 | void ath_mci_flush_profile(struct ath_mci_profile *mci) |
| 90 | { |
| 91 | struct ath_mci_profile_info *info, *tinfo; |
| 92 | |
| 93 | list_for_each_entry_safe(info, tinfo, &mci->info, list) { |
| 94 | list_del(&info->list); |
| 95 | DEC_PROF(mci, info); |
| 96 | kfree(info); |
| 97 | } |
| 98 | mci->aggr_limit = 0; |
| 99 | } |
| 100 | |
| 101 | static void ath_mci_adjust_aggr_limit(struct ath_btcoex *btcoex) |
| 102 | { |
| 103 | struct ath_mci_profile *mci = &btcoex->mci; |
| 104 | u32 wlan_airtime = btcoex->btcoex_period * |
| 105 | (100 - btcoex->duty_cycle) / 100; |
| 106 | |
| 107 | /* |
| 108 | * Scale: wlan_airtime is in ms, aggr_limit is in 0.25 ms. |
| 109 | * When wlan_airtime is less than 4ms, aggregation limit has to be |
| 110 | * adjusted half of wlan_airtime to ensure that the aggregation can fit |
| 111 | * without collision with BT traffic. |
| 112 | */ |
| 113 | if ((wlan_airtime <= 4) && |
| 114 | (!mci->aggr_limit || (mci->aggr_limit > (2 * wlan_airtime)))) |
| 115 | mci->aggr_limit = 2 * wlan_airtime; |
| 116 | } |
| 117 | |
| 118 | static void ath_mci_update_scheme(struct ath_softc *sc) |
| 119 | { |
| 120 | struct ath_common *common = ath9k_hw_common(sc->sc_ah); |
| 121 | struct ath_btcoex *btcoex = &sc->btcoex; |
| 122 | struct ath_mci_profile *mci = &btcoex->mci; |
| 123 | struct ath_mci_profile_info *info; |
| 124 | u32 num_profile = NUM_PROF(mci); |
| 125 | |
| 126 | if (num_profile == 1) { |
| 127 | info = list_first_entry(&mci->info, |
| 128 | struct ath_mci_profile_info, |
| 129 | list); |
| 130 | if (mci->num_sco && info->T == 12) { |
| 131 | mci->aggr_limit = 8; |
| 132 | ath_dbg(common, ATH_DBG_MCI, |
| 133 | "Single SCO, aggregation limit 2 ms\n"); |
| 134 | } else if ((info->type == MCI_GPM_COEX_PROFILE_BNEP) && |
| 135 | !info->master) { |
| 136 | btcoex->btcoex_period = 60; |
| 137 | ath_dbg(common, ATH_DBG_MCI, |
| 138 | "Single slave PAN/FTP, bt period 60 ms\n"); |
| 139 | } else if ((info->type == MCI_GPM_COEX_PROFILE_HID) && |
| 140 | (info->T > 0 && info->T < 50) && |
| 141 | (info->A > 1 || info->W > 1)) { |
| 142 | btcoex->duty_cycle = 30; |
| 143 | mci->aggr_limit = 8; |
| 144 | ath_dbg(common, ATH_DBG_MCI, |
| 145 | "Multiple attempt/timeout single HID " |
| 146 | "aggregation limit 2 ms dutycycle 30%%\n"); |
| 147 | } |
| 148 | } else if ((num_profile == 2) && (mci->num_hid == 2)) { |
| 149 | btcoex->duty_cycle = 30; |
| 150 | mci->aggr_limit = 8; |
| 151 | ath_dbg(common, ATH_DBG_MCI, |
| 152 | "Two HIDs aggregation limit 2 ms dutycycle 30%%\n"); |
| 153 | } else if (num_profile > 3) { |
| 154 | mci->aggr_limit = 6; |
| 155 | ath_dbg(common, ATH_DBG_MCI, |
| 156 | "Three or more profiles aggregation limit 1.5 ms\n"); |
| 157 | } |
| 158 | |
| 159 | if (IS_CHAN_2GHZ(sc->sc_ah->curchan)) { |
| 160 | if (IS_CHAN_HT(sc->sc_ah->curchan)) |
| 161 | ath_mci_adjust_aggr_limit(btcoex); |
| 162 | else |
| 163 | btcoex->btcoex_period >>= 1; |
| 164 | } |
| 165 | |
| 166 | ath9k_hw_btcoex_disable(sc->sc_ah); |
| 167 | ath9k_btcoex_timer_pause(sc); |
| 168 | |
| 169 | if (IS_CHAN_5GHZ(sc->sc_ah->curchan)) |
| 170 | return; |
| 171 | |
| 172 | btcoex->duty_cycle += (mci->num_bdr ? ATH_MCI_MAX_DUTY_CYCLE : 0); |
| 173 | if (btcoex->duty_cycle > ATH_MCI_MAX_DUTY_CYCLE) |
| 174 | btcoex->duty_cycle = ATH_MCI_MAX_DUTY_CYCLE; |
| 175 | |
| 176 | btcoex->btcoex_period *= 1000; |
| 177 | btcoex->btcoex_no_stomp = btcoex->btcoex_period * |
| 178 | (100 - btcoex->duty_cycle) / 100; |
| 179 | |
| 180 | ath9k_hw_btcoex_enable(sc->sc_ah); |
| 181 | ath9k_btcoex_timer_resume(sc); |
| 182 | } |
| 183 | |
| 184 | void ath_mci_process_profile(struct ath_softc *sc, |
| 185 | struct ath_mci_profile_info *info) |
| 186 | { |
| 187 | struct ath_common *common = ath9k_hw_common(sc->sc_ah); |
| 188 | struct ath_btcoex *btcoex = &sc->btcoex; |
| 189 | struct ath_mci_profile *mci = &btcoex->mci; |
| 190 | |
| 191 | if (info->start) { |
| 192 | if (!ath_mci_add_profile(common, mci, info)) |
| 193 | return; |
| 194 | } else |
| 195 | ath_mci_del_profile(common, mci, info); |
| 196 | |
| 197 | btcoex->btcoex_period = ATH_MCI_DEF_BT_PERIOD; |
| 198 | mci->aggr_limit = mci->num_sco ? 6 : 0; |
| 199 | if (NUM_PROF(mci)) { |
| 200 | btcoex->bt_stomp_type = ATH_BTCOEX_STOMP_LOW; |
| 201 | btcoex->duty_cycle = ath_mci_duty_cycle[NUM_PROF(mci)]; |
| 202 | } else { |
| 203 | btcoex->bt_stomp_type = mci->num_mgmt ? ATH_BTCOEX_STOMP_ALL : |
| 204 | ATH_BTCOEX_STOMP_LOW; |
| 205 | btcoex->duty_cycle = ATH_BTCOEX_DEF_DUTY_CYCLE; |
| 206 | } |
| 207 | |
| 208 | ath_mci_update_scheme(sc); |
| 209 | } |
| 210 | |
| 211 | void ath_mci_process_status(struct ath_softc *sc, |
| 212 | struct ath_mci_profile_status *status) |
| 213 | { |
| 214 | struct ath_common *common = ath9k_hw_common(sc->sc_ah); |
| 215 | struct ath_btcoex *btcoex = &sc->btcoex; |
| 216 | struct ath_mci_profile *mci = &btcoex->mci; |
| 217 | struct ath_mci_profile_info info; |
| 218 | int i = 0, old_num_mgmt = mci->num_mgmt; |
| 219 | |
| 220 | /* Link status type are not handled */ |
| 221 | if (status->is_link) { |
| 222 | ath_dbg(common, ATH_DBG_MCI, |
| 223 | "Skip link type status update\n"); |
| 224 | return; |
| 225 | } |
| 226 | |
| 227 | memset(&info, 0, sizeof(struct ath_mci_profile_info)); |
| 228 | |
| 229 | info.conn_handle = status->conn_handle; |
| 230 | if (ath_mci_find_profile(mci, &info)) { |
| 231 | ath_dbg(common, ATH_DBG_MCI, |
| 232 | "Skip non link state update for existing profile %d\n", |
| 233 | status->conn_handle); |
| 234 | return; |
| 235 | } |
| 236 | if (status->conn_handle >= ATH_MCI_MAX_PROFILE) { |
| 237 | ath_dbg(common, ATH_DBG_MCI, |
| 238 | "Ignore too many non-link update\n"); |
| 239 | return; |
| 240 | } |
| 241 | if (status->is_critical) |
| 242 | __set_bit(status->conn_handle, mci->status); |
| 243 | else |
| 244 | __clear_bit(status->conn_handle, mci->status); |
| 245 | |
| 246 | mci->num_mgmt = 0; |
| 247 | do { |
| 248 | if (test_bit(i, mci->status)) |
| 249 | mci->num_mgmt++; |
| 250 | } while (++i < ATH_MCI_MAX_PROFILE); |
| 251 | |
| 252 | if (old_num_mgmt != mci->num_mgmt) |
| 253 | ath_mci_update_scheme(sc); |
| 254 | } |