diff options
Diffstat (limited to 'drivers/net/wireless/ath/ath9k/mci.c')
-rw-r--r-- | drivers/net/wireless/ath/ath9k/mci.c | 254 |
1 files changed, 254 insertions, 0 deletions
diff --git a/drivers/net/wireless/ath/ath9k/mci.c b/drivers/net/wireless/ath/ath9k/mci.c new file mode 100644 index 000000000000..0fbb141bc302 --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/mci.c | |||
@@ -0,0 +1,254 @@ | |||
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 | } | ||