diff options
author | Piotr Haber <phaber@broadcom.com> | 2013-04-23 06:53:12 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2013-04-23 14:16:52 -0400 |
commit | 61730d4dfffc2cc9d3a49fad87633008105c18ba (patch) | |
tree | 04644a12e505a4d89a30dd49e9e47046e6c9782e /drivers | |
parent | 1687eee235d2c812f9ad433eae6b0aa04e0a39c2 (diff) |
brcmfmac: support critical protocol API for DHCP
Adds support for the critical protocol API provided by nl80211
which gives Wifi traffic priority over a Bluetooth (e)SCO connection
and disables scanning during DCHP negotiation.
Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
Reviewed-by: Hante Meuleman <meuleman@broadcom.com>
Reviewed-by: Arend van Spriel <arend@broadcom.com>
Signed-off-by: Piotr Haber <phaber@broadcom.com>
Signed-off-by: Arend van Spriel <arend@broadcom.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/net/wireless/brcm80211/brcmfmac/Makefile | 3 | ||||
-rw-r--r-- | drivers/net/wireless/brcm80211/brcmfmac/btcoex.c | 497 | ||||
-rw-r--r-- | drivers/net/wireless/brcm80211/brcmfmac/btcoex.h | 29 | ||||
-rw-r--r-- | drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c | 46 | ||||
-rw-r--r-- | drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h | 2 |
5 files changed, 576 insertions, 1 deletions
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/Makefile b/drivers/net/wireless/brcm80211/brcmfmac/Makefile index 598c8e2f8d2b..8e9b1221b32c 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/Makefile +++ b/drivers/net/wireless/brcm80211/brcmfmac/Makefile | |||
@@ -30,7 +30,8 @@ brcmfmac-objs += \ | |||
30 | p2p.o \ | 30 | p2p.o \ |
31 | dhd_cdc.o \ | 31 | dhd_cdc.o \ |
32 | dhd_common.o \ | 32 | dhd_common.o \ |
33 | dhd_linux.o | 33 | dhd_linux.o \ |
34 | btcoex.o | ||
34 | brcmfmac-$(CONFIG_BRCMFMAC_SDIO) += \ | 35 | brcmfmac-$(CONFIG_BRCMFMAC_SDIO) += \ |
35 | dhd_sdio.o \ | 36 | dhd_sdio.o \ |
36 | bcmsdh.o \ | 37 | bcmsdh.o \ |
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/btcoex.c b/drivers/net/wireless/brcm80211/brcmfmac/btcoex.c new file mode 100644 index 000000000000..0cb591b050b3 --- /dev/null +++ b/drivers/net/wireless/brcm80211/brcmfmac/btcoex.c | |||
@@ -0,0 +1,497 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2013 Broadcom Corporation | ||
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 ANY | ||
11 | * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION | ||
13 | * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | ||
14 | * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||
15 | */ | ||
16 | #include <linux/slab.h> | ||
17 | #include <linux/netdevice.h> | ||
18 | #include <net/cfg80211.h> | ||
19 | |||
20 | #include <brcmu_wifi.h> | ||
21 | #include <brcmu_utils.h> | ||
22 | #include <defs.h> | ||
23 | #include <dhd.h> | ||
24 | #include <dhd_dbg.h> | ||
25 | #include "fwil.h" | ||
26 | #include "fwil_types.h" | ||
27 | #include "btcoex.h" | ||
28 | #include "p2p.h" | ||
29 | #include "wl_cfg80211.h" | ||
30 | |||
31 | /* T1 start SCO/eSCO priority suppression */ | ||
32 | #define BRCMF_BTCOEX_OPPR_WIN_TIME 2000 | ||
33 | |||
34 | /* BT registers values during DHCP */ | ||
35 | #define BRCMF_BT_DHCP_REG50 0x8022 | ||
36 | #define BRCMF_BT_DHCP_REG51 0 | ||
37 | #define BRCMF_BT_DHCP_REG64 0 | ||
38 | #define BRCMF_BT_DHCP_REG65 0 | ||
39 | #define BRCMF_BT_DHCP_REG71 0 | ||
40 | #define BRCMF_BT_DHCP_REG66 0x2710 | ||
41 | #define BRCMF_BT_DHCP_REG41 0x33 | ||
42 | #define BRCMF_BT_DHCP_REG68 0x190 | ||
43 | |||
44 | /* number of samples for SCO detection */ | ||
45 | #define BRCMF_BT_SCO_SAMPLES 12 | ||
46 | |||
47 | /** | ||
48 | * enum brcmf_btcoex_state - BT coex DHCP state machine states | ||
49 | * @BRCMF_BT_DHCP_IDLE: DCHP is idle | ||
50 | * @BRCMF_BT_DHCP_START: DHCP started, wait before | ||
51 | * boosting wifi priority | ||
52 | * @BRCMF_BT_DHCP_OPPR_WIN: graceful DHCP opportunity ended, | ||
53 | * boost wifi priority | ||
54 | * @BRCMF_BT_DHCP_FLAG_FORCE_TIMEOUT: wifi priority boost end, | ||
55 | * restore defaults | ||
56 | */ | ||
57 | enum brcmf_btcoex_state { | ||
58 | BRCMF_BT_DHCP_IDLE, | ||
59 | BRCMF_BT_DHCP_START, | ||
60 | BRCMF_BT_DHCP_OPPR_WIN, | ||
61 | BRCMF_BT_DHCP_FLAG_FORCE_TIMEOUT | ||
62 | }; | ||
63 | |||
64 | /** | ||
65 | * struct brcmf_btcoex_info - BT coex related information | ||
66 | * @vif: interface for which request was done. | ||
67 | * @timer: timer for DHCP state machine | ||
68 | * @timeout: configured timeout. | ||
69 | * @timer_on: DHCP timer active | ||
70 | * @dhcp_done: DHCP finished before T1/T2 timer expiration | ||
71 | * @bt_state: DHCP state machine state | ||
72 | * @work: DHCP state machine work | ||
73 | * @cfg: driver private data for cfg80211 interface | ||
74 | * @reg66: saved value of btc_params 66 | ||
75 | * @reg41: saved value of btc_params 41 | ||
76 | * @reg68: saved value of btc_params 68 | ||
77 | * @saved_regs_part1: flag indicating regs 66,41,68 | ||
78 | * have been saved | ||
79 | * @reg51: saved value of btc_params 51 | ||
80 | * @reg64: saved value of btc_params 64 | ||
81 | * @reg65: saved value of btc_params 65 | ||
82 | * @reg71: saved value of btc_params 71 | ||
83 | * @saved_regs_part1: flag indicating regs 50,51,64,65,71 | ||
84 | * have been saved | ||
85 | */ | ||
86 | struct brcmf_btcoex_info { | ||
87 | struct brcmf_cfg80211_vif *vif; | ||
88 | struct timer_list timer; | ||
89 | u16 timeout; | ||
90 | bool timer_on; | ||
91 | bool dhcp_done; | ||
92 | enum brcmf_btcoex_state bt_state; | ||
93 | struct work_struct work; | ||
94 | struct brcmf_cfg80211_info *cfg; | ||
95 | u32 reg66; | ||
96 | u32 reg41; | ||
97 | u32 reg68; | ||
98 | bool saved_regs_part1; | ||
99 | u32 reg50; | ||
100 | u32 reg51; | ||
101 | u32 reg64; | ||
102 | u32 reg65; | ||
103 | u32 reg71; | ||
104 | bool saved_regs_part2; | ||
105 | }; | ||
106 | |||
107 | /** | ||
108 | * brcmf_btcoex_params_write() - write btc_params firmware variable | ||
109 | * @ifp: interface | ||
110 | * @addr: btc_params register number | ||
111 | * @data: data to write | ||
112 | */ | ||
113 | static s32 brcmf_btcoex_params_write(struct brcmf_if *ifp, u32 addr, u32 data) | ||
114 | { | ||
115 | struct { | ||
116 | __le32 addr; | ||
117 | __le32 data; | ||
118 | } reg_write; | ||
119 | |||
120 | reg_write.addr = cpu_to_le32(addr); | ||
121 | reg_write.data = cpu_to_le32(data); | ||
122 | return brcmf_fil_iovar_data_set(ifp, "btc_params", | ||
123 | ®_write, sizeof(reg_write)); | ||
124 | } | ||
125 | |||
126 | /** | ||
127 | * brcmf_btcoex_params_read() - read btc_params firmware variable | ||
128 | * @ifp: interface | ||
129 | * @addr: btc_params register number | ||
130 | * @data: read data | ||
131 | */ | ||
132 | static s32 brcmf_btcoex_params_read(struct brcmf_if *ifp, u32 addr, u32 *data) | ||
133 | { | ||
134 | *data = addr; | ||
135 | |||
136 | return brcmf_fil_iovar_int_get(ifp, "btc_params", data); | ||
137 | } | ||
138 | |||
139 | /** | ||
140 | * brcmf_btcoex_boost_wifi() - control BT SCO/eSCO parameters | ||
141 | * @btci: BT coex info | ||
142 | * @trump_sco: | ||
143 | * true - set SCO/eSCO parameters for compatibility | ||
144 | * during DHCP window | ||
145 | * false - restore saved parameter values | ||
146 | * | ||
147 | * Enhanced BT COEX settings for eSCO compatibility during DHCP window | ||
148 | */ | ||
149 | static void brcmf_btcoex_boost_wifi(struct brcmf_btcoex_info *btci, | ||
150 | bool trump_sco) | ||
151 | { | ||
152 | struct brcmf_if *ifp = btci->cfg->pub->iflist[0]; | ||
153 | |||
154 | if (trump_sco && !btci->saved_regs_part2) { | ||
155 | /* this should reduce eSCO agressive | ||
156 | * retransmit w/o breaking it | ||
157 | */ | ||
158 | |||
159 | /* save current */ | ||
160 | brcmf_dbg(TRACE, "new SCO/eSCO coex algo {save & override}\n"); | ||
161 | brcmf_btcoex_params_read(ifp, 50, &btci->reg50); | ||
162 | brcmf_btcoex_params_read(ifp, 51, &btci->reg51); | ||
163 | brcmf_btcoex_params_read(ifp, 64, &btci->reg64); | ||
164 | brcmf_btcoex_params_read(ifp, 65, &btci->reg65); | ||
165 | brcmf_btcoex_params_read(ifp, 71, &btci->reg71); | ||
166 | |||
167 | btci->saved_regs_part2 = true; | ||
168 | brcmf_dbg(TRACE, | ||
169 | "saved bt_params[50,51,64,65,71]: 0x%x 0x%x 0x%x 0x%x 0x%x\n", | ||
170 | btci->reg50, btci->reg51, btci->reg64, | ||
171 | btci->reg65, btci->reg71); | ||
172 | |||
173 | /* pacify the eSco */ | ||
174 | brcmf_btcoex_params_write(ifp, 50, BRCMF_BT_DHCP_REG50); | ||
175 | brcmf_btcoex_params_write(ifp, 51, BRCMF_BT_DHCP_REG51); | ||
176 | brcmf_btcoex_params_write(ifp, 64, BRCMF_BT_DHCP_REG64); | ||
177 | brcmf_btcoex_params_write(ifp, 65, BRCMF_BT_DHCP_REG65); | ||
178 | brcmf_btcoex_params_write(ifp, 71, BRCMF_BT_DHCP_REG71); | ||
179 | |||
180 | } else if (btci->saved_regs_part2) { | ||
181 | /* restore previously saved bt params */ | ||
182 | brcmf_dbg(TRACE, "Do new SCO/eSCO coex algo {restore}\n"); | ||
183 | brcmf_btcoex_params_write(ifp, 50, btci->reg50); | ||
184 | brcmf_btcoex_params_write(ifp, 51, btci->reg51); | ||
185 | brcmf_btcoex_params_write(ifp, 64, btci->reg64); | ||
186 | brcmf_btcoex_params_write(ifp, 65, btci->reg65); | ||
187 | brcmf_btcoex_params_write(ifp, 71, btci->reg71); | ||
188 | |||
189 | brcmf_dbg(TRACE, | ||
190 | "restored bt_params[50,51,64,65,71]: 0x%x 0x%x 0x%x 0x%x 0x%x\n", | ||
191 | btci->reg50, btci->reg51, btci->reg64, | ||
192 | btci->reg65, btci->reg71); | ||
193 | |||
194 | btci->saved_regs_part2 = false; | ||
195 | } else { | ||
196 | brcmf_err("attempted to restore not saved BTCOEX params\n"); | ||
197 | } | ||
198 | } | ||
199 | |||
200 | /** | ||
201 | * brcmf_btcoex_is_sco_active() - check if SCO/eSCO is active | ||
202 | * @ifp: interface | ||
203 | * | ||
204 | * return: true if SCO/eSCO session is active | ||
205 | */ | ||
206 | static bool brcmf_btcoex_is_sco_active(struct brcmf_if *ifp) | ||
207 | { | ||
208 | int ioc_res = 0; | ||
209 | bool res = false; | ||
210 | int sco_id_cnt = 0; | ||
211 | u32 param27; | ||
212 | int i; | ||
213 | |||
214 | for (i = 0; i < BRCMF_BT_SCO_SAMPLES; i++) { | ||
215 | ioc_res = brcmf_btcoex_params_read(ifp, 27, ¶m27); | ||
216 | |||
217 | if (ioc_res < 0) { | ||
218 | brcmf_err("ioc read btc params error\n"); | ||
219 | break; | ||
220 | } | ||
221 | |||
222 | brcmf_dbg(TRACE, "sample[%d], btc_params 27:%x\n", i, param27); | ||
223 | |||
224 | if ((param27 & 0x6) == 2) { /* count both sco & esco */ | ||
225 | sco_id_cnt++; | ||
226 | } | ||
227 | |||
228 | if (sco_id_cnt > 2) { | ||
229 | brcmf_dbg(TRACE, | ||
230 | "sco/esco detected, pkt id_cnt:%d samples:%d\n", | ||
231 | sco_id_cnt, i); | ||
232 | res = true; | ||
233 | break; | ||
234 | } | ||
235 | } | ||
236 | brcmf_dbg(TRACE, "exit: result=%d\n", res); | ||
237 | return res; | ||
238 | } | ||
239 | |||
240 | /** | ||
241 | * btcmf_btcoex_save_part1() - save first step parameters. | ||
242 | */ | ||
243 | static void btcmf_btcoex_save_part1(struct brcmf_btcoex_info *btci) | ||
244 | { | ||
245 | struct brcmf_if *ifp = btci->vif->ifp; | ||
246 | |||
247 | if (!btci->saved_regs_part1) { | ||
248 | /* Retrieve and save original reg value */ | ||
249 | brcmf_btcoex_params_read(ifp, 66, &btci->reg66); | ||
250 | brcmf_btcoex_params_read(ifp, 41, &btci->reg41); | ||
251 | brcmf_btcoex_params_read(ifp, 68, &btci->reg68); | ||
252 | btci->saved_regs_part1 = true; | ||
253 | brcmf_dbg(TRACE, | ||
254 | "saved btc_params regs (66,41,68) 0x%x 0x%x 0x%x\n", | ||
255 | btci->reg66, btci->reg41, | ||
256 | btci->reg68); | ||
257 | } | ||
258 | } | ||
259 | |||
260 | /** | ||
261 | * brcmf_btcoex_restore_part1() - restore first step parameters. | ||
262 | */ | ||
263 | static void brcmf_btcoex_restore_part1(struct brcmf_btcoex_info *btci) | ||
264 | { | ||
265 | struct brcmf_if *ifp; | ||
266 | |||
267 | if (btci->saved_regs_part1) { | ||
268 | btci->saved_regs_part1 = false; | ||
269 | ifp = btci->vif->ifp; | ||
270 | brcmf_btcoex_params_write(ifp, 66, btci->reg66); | ||
271 | brcmf_btcoex_params_write(ifp, 41, btci->reg41); | ||
272 | brcmf_btcoex_params_write(ifp, 68, btci->reg68); | ||
273 | brcmf_dbg(TRACE, | ||
274 | "restored btc_params regs {66,41,68} 0x%x 0x%x 0x%x\n", | ||
275 | btci->reg66, btci->reg41, | ||
276 | btci->reg68); | ||
277 | } | ||
278 | } | ||
279 | |||
280 | /** | ||
281 | * brcmf_btcoex_timerfunc() - BT coex timer callback | ||
282 | */ | ||
283 | static void brcmf_btcoex_timerfunc(ulong data) | ||
284 | { | ||
285 | struct brcmf_btcoex_info *bt_local = (struct brcmf_btcoex_info *)data; | ||
286 | brcmf_dbg(TRACE, "enter\n"); | ||
287 | |||
288 | bt_local->timer_on = false; | ||
289 | schedule_work(&bt_local->work); | ||
290 | } | ||
291 | |||
292 | /** | ||
293 | * brcmf_btcoex_handler() - BT coex state machine work handler | ||
294 | * @work: work | ||
295 | */ | ||
296 | static void brcmf_btcoex_handler(struct work_struct *work) | ||
297 | { | ||
298 | struct brcmf_btcoex_info *btci; | ||
299 | btci = container_of(work, struct brcmf_btcoex_info, work); | ||
300 | if (btci->timer_on) { | ||
301 | btci->timer_on = false; | ||
302 | del_timer_sync(&btci->timer); | ||
303 | } | ||
304 | |||
305 | switch (btci->bt_state) { | ||
306 | case BRCMF_BT_DHCP_START: | ||
307 | /* DHCP started provide OPPORTUNITY window | ||
308 | to get DHCP address | ||
309 | */ | ||
310 | brcmf_dbg(TRACE, "DHCP started\n"); | ||
311 | btci->bt_state = BRCMF_BT_DHCP_OPPR_WIN; | ||
312 | if (btci->timeout < BRCMF_BTCOEX_OPPR_WIN_TIME) { | ||
313 | mod_timer(&btci->timer, btci->timer.expires); | ||
314 | } else { | ||
315 | btci->timeout -= BRCMF_BTCOEX_OPPR_WIN_TIME; | ||
316 | mod_timer(&btci->timer, | ||
317 | jiffies + | ||
318 | msecs_to_jiffies(BRCMF_BTCOEX_OPPR_WIN_TIME)); | ||
319 | } | ||
320 | btci->timer_on = true; | ||
321 | break; | ||
322 | |||
323 | case BRCMF_BT_DHCP_OPPR_WIN: | ||
324 | if (btci->dhcp_done) { | ||
325 | brcmf_dbg(TRACE, "DHCP done before T1 expiration\n"); | ||
326 | goto idle; | ||
327 | } | ||
328 | |||
329 | /* DHCP is not over yet, start lowering BT priority */ | ||
330 | brcmf_dbg(TRACE, "DHCP T1:%d expired\n", | ||
331 | BRCMF_BTCOEX_OPPR_WIN_TIME); | ||
332 | brcmf_btcoex_boost_wifi(btci, true); | ||
333 | |||
334 | btci->bt_state = BRCMF_BT_DHCP_FLAG_FORCE_TIMEOUT; | ||
335 | mod_timer(&btci->timer, | ||
336 | jiffies + msecs_to_jiffies(btci->timeout)); | ||
337 | btci->timer_on = true; | ||
338 | break; | ||
339 | |||
340 | case BRCMF_BT_DHCP_FLAG_FORCE_TIMEOUT: | ||
341 | if (btci->dhcp_done) | ||
342 | brcmf_dbg(TRACE, "DHCP done before T2 expiration\n"); | ||
343 | else | ||
344 | brcmf_dbg(TRACE, "DHCP T2:%d expired\n", | ||
345 | BRCMF_BT_DHCP_FLAG_FORCE_TIMEOUT); | ||
346 | |||
347 | goto idle; | ||
348 | |||
349 | default: | ||
350 | brcmf_err("invalid state=%d !!!\n", btci->bt_state); | ||
351 | goto idle; | ||
352 | } | ||
353 | |||
354 | return; | ||
355 | |||
356 | idle: | ||
357 | btci->bt_state = BRCMF_BT_DHCP_IDLE; | ||
358 | btci->timer_on = false; | ||
359 | brcmf_btcoex_boost_wifi(btci, false); | ||
360 | cfg80211_crit_proto_stopped(&btci->vif->wdev, GFP_KERNEL); | ||
361 | brcmf_btcoex_restore_part1(btci); | ||
362 | btci->vif = NULL; | ||
363 | } | ||
364 | |||
365 | /** | ||
366 | * brcmf_btcoex_attach() - initialize BT coex data | ||
367 | * @cfg: driver private cfg80211 data | ||
368 | * | ||
369 | * return: 0 on success | ||
370 | */ | ||
371 | int brcmf_btcoex_attach(struct brcmf_cfg80211_info *cfg) | ||
372 | { | ||
373 | struct brcmf_btcoex_info *btci = NULL; | ||
374 | brcmf_dbg(TRACE, "enter\n"); | ||
375 | |||
376 | btci = kmalloc(sizeof(struct brcmf_btcoex_info), GFP_KERNEL); | ||
377 | if (!btci) | ||
378 | return -ENOMEM; | ||
379 | |||
380 | btci->bt_state = BRCMF_BT_DHCP_IDLE; | ||
381 | |||
382 | /* Set up timer for BT */ | ||
383 | btci->timer_on = false; | ||
384 | btci->timeout = BRCMF_BTCOEX_OPPR_WIN_TIME; | ||
385 | init_timer(&btci->timer); | ||
386 | btci->timer.data = (ulong)btci; | ||
387 | btci->timer.function = brcmf_btcoex_timerfunc; | ||
388 | btci->cfg = cfg; | ||
389 | btci->saved_regs_part1 = false; | ||
390 | btci->saved_regs_part2 = false; | ||
391 | |||
392 | INIT_WORK(&btci->work, brcmf_btcoex_handler); | ||
393 | |||
394 | cfg->btcoex = btci; | ||
395 | return 0; | ||
396 | } | ||
397 | |||
398 | /** | ||
399 | * brcmf_btcoex_detach - clean BT coex data | ||
400 | * @cfg: driver private cfg80211 data | ||
401 | */ | ||
402 | void brcmf_btcoex_detach(struct brcmf_cfg80211_info *cfg) | ||
403 | { | ||
404 | brcmf_dbg(TRACE, "enter\n"); | ||
405 | |||
406 | if (!cfg->btcoex) | ||
407 | return; | ||
408 | |||
409 | if (cfg->btcoex->timer_on) { | ||
410 | cfg->btcoex->timer_on = false; | ||
411 | del_timer_sync(&cfg->btcoex->timer); | ||
412 | } | ||
413 | |||
414 | cancel_work_sync(&cfg->btcoex->work); | ||
415 | |||
416 | brcmf_btcoex_boost_wifi(cfg->btcoex, false); | ||
417 | brcmf_btcoex_restore_part1(cfg->btcoex); | ||
418 | |||
419 | kfree(cfg->btcoex); | ||
420 | cfg->btcoex = NULL; | ||
421 | } | ||
422 | |||
423 | static void brcmf_btcoex_dhcp_start(struct brcmf_btcoex_info *btci) | ||
424 | { | ||
425 | struct brcmf_if *ifp = btci->vif->ifp; | ||
426 | |||
427 | btcmf_btcoex_save_part1(btci); | ||
428 | /* set new regs values */ | ||
429 | brcmf_btcoex_params_write(ifp, 66, BRCMF_BT_DHCP_REG66); | ||
430 | brcmf_btcoex_params_write(ifp, 41, BRCMF_BT_DHCP_REG41); | ||
431 | brcmf_btcoex_params_write(ifp, 68, BRCMF_BT_DHCP_REG68); | ||
432 | btci->dhcp_done = false; | ||
433 | btci->bt_state = BRCMF_BT_DHCP_START; | ||
434 | schedule_work(&btci->work); | ||
435 | brcmf_dbg(TRACE, "enable BT DHCP Timer\n"); | ||
436 | } | ||
437 | |||
438 | static void brcmf_btcoex_dhcp_end(struct brcmf_btcoex_info *btci) | ||
439 | { | ||
440 | /* Stop any bt timer because DHCP session is done */ | ||
441 | btci->dhcp_done = true; | ||
442 | if (btci->timer_on) { | ||
443 | brcmf_dbg(TRACE, "disable BT DHCP Timer\n"); | ||
444 | btci->timer_on = false; | ||
445 | del_timer_sync(&btci->timer); | ||
446 | |||
447 | /* schedule worker if transition to IDLE is needed */ | ||
448 | if (btci->bt_state != BRCMF_BT_DHCP_IDLE) { | ||
449 | brcmf_dbg(TRACE, "bt_state:%d\n", | ||
450 | btci->bt_state); | ||
451 | schedule_work(&btci->work); | ||
452 | } | ||
453 | } else { | ||
454 | /* Restore original values */ | ||
455 | brcmf_btcoex_restore_part1(btci); | ||
456 | } | ||
457 | } | ||
458 | |||
459 | /** | ||
460 | * brcmf_btcoex_set_mode - set BT coex mode | ||
461 | * @cfg: driver private cfg80211 data | ||
462 | * @mode: Wifi-Bluetooth coexistence mode | ||
463 | * | ||
464 | * return: 0 on success | ||
465 | */ | ||
466 | int brcmf_btcoex_set_mode(struct brcmf_cfg80211_vif *vif, | ||
467 | enum brcmf_btcoex_mode mode, u16 duration) | ||
468 | { | ||
469 | struct brcmf_cfg80211_info *cfg = wiphy_priv(vif->wdev.wiphy); | ||
470 | struct brcmf_btcoex_info *btci = cfg->btcoex; | ||
471 | struct brcmf_if *ifp = cfg->pub->iflist[0]; | ||
472 | |||
473 | switch (mode) { | ||
474 | case BRCMF_BTCOEX_DISABLED: | ||
475 | brcmf_dbg(TRACE, "DHCP session starts\n"); | ||
476 | if (btci->bt_state != BRCMF_BT_DHCP_IDLE) | ||
477 | return -EBUSY; | ||
478 | /* Start BT timer only for SCO connection */ | ||
479 | if (brcmf_btcoex_is_sco_active(ifp)) { | ||
480 | btci->timeout = duration; | ||
481 | btci->vif = vif; | ||
482 | brcmf_btcoex_dhcp_start(btci); | ||
483 | } | ||
484 | break; | ||
485 | |||
486 | case BRCMF_BTCOEX_ENABLED: | ||
487 | brcmf_dbg(TRACE, "DHCP session ends\n"); | ||
488 | if (btci->bt_state != BRCMF_BT_DHCP_IDLE && | ||
489 | vif == btci->vif) { | ||
490 | brcmf_btcoex_dhcp_end(btci); | ||
491 | } | ||
492 | break; | ||
493 | default: | ||
494 | brcmf_dbg(TRACE, "Unknown mode, ignored\n"); | ||
495 | } | ||
496 | return 0; | ||
497 | } | ||
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/btcoex.h b/drivers/net/wireless/brcm80211/brcmfmac/btcoex.h new file mode 100644 index 000000000000..19647c68aa9e --- /dev/null +++ b/drivers/net/wireless/brcm80211/brcmfmac/btcoex.h | |||
@@ -0,0 +1,29 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2013 Broadcom Corporation | ||
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 ANY | ||
11 | * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION | ||
13 | * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | ||
14 | * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||
15 | */ | ||
16 | #ifndef WL_BTCOEX_H_ | ||
17 | #define WL_BTCOEX_H_ | ||
18 | |||
19 | enum brcmf_btcoex_mode { | ||
20 | BRCMF_BTCOEX_DISABLED, | ||
21 | BRCMF_BTCOEX_ENABLED | ||
22 | }; | ||
23 | |||
24 | int brcmf_btcoex_attach(struct brcmf_cfg80211_info *cfg); | ||
25 | void brcmf_btcoex_detach(struct brcmf_cfg80211_info *cfg); | ||
26 | int brcmf_btcoex_set_mode(struct brcmf_cfg80211_vif *vif, | ||
27 | enum brcmf_btcoex_mode mode, u16 duration); | ||
28 | |||
29 | #endif /* WL_BTCOEX_H_ */ | ||
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c index 29b4ea43a40f..6d758f285352 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c | |||
@@ -29,6 +29,7 @@ | |||
29 | #include "tracepoint.h" | 29 | #include "tracepoint.h" |
30 | #include "fwil_types.h" | 30 | #include "fwil_types.h" |
31 | #include "p2p.h" | 31 | #include "p2p.h" |
32 | #include "btcoex.h" | ||
32 | #include "wl_cfg80211.h" | 33 | #include "wl_cfg80211.h" |
33 | #include "fwil.h" | 34 | #include "fwil.h" |
34 | 35 | ||
@@ -1051,6 +1052,7 @@ static void brcmf_init_prof(struct brcmf_cfg80211_profile *prof) | |||
1051 | 1052 | ||
1052 | static void brcmf_link_down(struct brcmf_cfg80211_vif *vif) | 1053 | static void brcmf_link_down(struct brcmf_cfg80211_vif *vif) |
1053 | { | 1054 | { |
1055 | struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(vif->wdev.wiphy); | ||
1054 | s32 err = 0; | 1056 | s32 err = 0; |
1055 | 1057 | ||
1056 | brcmf_dbg(TRACE, "Enter\n"); | 1058 | brcmf_dbg(TRACE, "Enter\n"); |
@@ -1064,6 +1066,8 @@ static void brcmf_link_down(struct brcmf_cfg80211_vif *vif) | |||
1064 | clear_bit(BRCMF_VIF_STATUS_CONNECTED, &vif->sme_state); | 1066 | clear_bit(BRCMF_VIF_STATUS_CONNECTED, &vif->sme_state); |
1065 | } | 1067 | } |
1066 | clear_bit(BRCMF_VIF_STATUS_CONNECTING, &vif->sme_state); | 1068 | clear_bit(BRCMF_VIF_STATUS_CONNECTING, &vif->sme_state); |
1069 | clear_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status); | ||
1070 | brcmf_btcoex_set_mode(vif, BRCMF_BTCOEX_ENABLED, 0); | ||
1067 | brcmf_dbg(TRACE, "Exit\n"); | 1071 | brcmf_dbg(TRACE, "Exit\n"); |
1068 | } | 1072 | } |
1069 | 1073 | ||
@@ -4013,6 +4017,39 @@ exit: | |||
4013 | return err; | 4017 | return err; |
4014 | } | 4018 | } |
4015 | 4019 | ||
4020 | static int brcmf_cfg80211_crit_proto_start(struct wiphy *wiphy, | ||
4021 | struct wireless_dev *wdev, | ||
4022 | enum nl80211_crit_proto_id proto, | ||
4023 | u16 duration) | ||
4024 | { | ||
4025 | struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); | ||
4026 | struct brcmf_cfg80211_vif *vif; | ||
4027 | |||
4028 | vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev); | ||
4029 | |||
4030 | /* only DHCP support for now */ | ||
4031 | if (proto != NL80211_CRIT_PROTO_DHCP) | ||
4032 | return -EINVAL; | ||
4033 | |||
4034 | /* suppress and abort scanning */ | ||
4035 | set_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status); | ||
4036 | brcmf_abort_scanning(cfg); | ||
4037 | |||
4038 | return brcmf_btcoex_set_mode(vif, BRCMF_BTCOEX_DISABLED, duration); | ||
4039 | } | ||
4040 | |||
4041 | static void brcmf_cfg80211_crit_proto_stop(struct wiphy *wiphy, | ||
4042 | struct wireless_dev *wdev) | ||
4043 | { | ||
4044 | struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); | ||
4045 | struct brcmf_cfg80211_vif *vif; | ||
4046 | |||
4047 | vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev); | ||
4048 | |||
4049 | brcmf_btcoex_set_mode(vif, BRCMF_BTCOEX_ENABLED, 0); | ||
4050 | clear_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status); | ||
4051 | } | ||
4052 | |||
4016 | static struct cfg80211_ops wl_cfg80211_ops = { | 4053 | static struct cfg80211_ops wl_cfg80211_ops = { |
4017 | .add_virtual_intf = brcmf_cfg80211_add_iface, | 4054 | .add_virtual_intf = brcmf_cfg80211_add_iface, |
4018 | .del_virtual_intf = brcmf_cfg80211_del_iface, | 4055 | .del_virtual_intf = brcmf_cfg80211_del_iface, |
@@ -4049,6 +4086,8 @@ static struct cfg80211_ops wl_cfg80211_ops = { | |||
4049 | .cancel_remain_on_channel = brcmf_cfg80211_cancel_remain_on_channel, | 4086 | .cancel_remain_on_channel = brcmf_cfg80211_cancel_remain_on_channel, |
4050 | .start_p2p_device = brcmf_p2p_start_device, | 4087 | .start_p2p_device = brcmf_p2p_start_device, |
4051 | .stop_p2p_device = brcmf_p2p_stop_device, | 4088 | .stop_p2p_device = brcmf_p2p_stop_device, |
4089 | .crit_proto_start = brcmf_cfg80211_crit_proto_start, | ||
4090 | .crit_proto_stop = brcmf_cfg80211_crit_proto_stop, | ||
4052 | #ifdef CONFIG_NL80211_TESTMODE | 4091 | #ifdef CONFIG_NL80211_TESTMODE |
4053 | .testmode_cmd = brcmf_cfg80211_testmode | 4092 | .testmode_cmd = brcmf_cfg80211_testmode |
4054 | #endif | 4093 | #endif |
@@ -4786,6 +4825,12 @@ struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr, | |||
4786 | brcmf_err("P2P initilisation failed (%d)\n", err); | 4825 | brcmf_err("P2P initilisation failed (%d)\n", err); |
4787 | goto cfg80211_p2p_attach_out; | 4826 | goto cfg80211_p2p_attach_out; |
4788 | } | 4827 | } |
4828 | err = brcmf_btcoex_attach(cfg); | ||
4829 | if (err) { | ||
4830 | brcmf_err("BT-coex initialisation failed (%d)\n", err); | ||
4831 | brcmf_p2p_detach(&cfg->p2p); | ||
4832 | goto cfg80211_p2p_attach_out; | ||
4833 | } | ||
4789 | 4834 | ||
4790 | err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_VERSION, | 4835 | err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_VERSION, |
4791 | &io_type); | 4836 | &io_type); |
@@ -4813,6 +4858,7 @@ void brcmf_cfg80211_detach(struct brcmf_cfg80211_info *cfg) | |||
4813 | struct brcmf_cfg80211_vif *tmp; | 4858 | struct brcmf_cfg80211_vif *tmp; |
4814 | 4859 | ||
4815 | wl_deinit_priv(cfg); | 4860 | wl_deinit_priv(cfg); |
4861 | brcmf_btcoex_detach(cfg); | ||
4816 | list_for_each_entry_safe(vif, tmp, &cfg->vif_list, list) { | 4862 | list_for_each_entry_safe(vif, tmp, &cfg->vif_list, list) { |
4817 | brcmf_free_vif(vif); | 4863 | brcmf_free_vif(vif); |
4818 | } | 4864 | } |
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h index e1983464e847..a71cff84cdcf 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h | |||
@@ -351,6 +351,7 @@ struct brcmf_cfg80211_vif_event { | |||
351 | * @wiphy: wiphy object for cfg80211 interface. | 351 | * @wiphy: wiphy object for cfg80211 interface. |
352 | * @conf: dongle configuration. | 352 | * @conf: dongle configuration. |
353 | * @p2p: peer-to-peer specific information. | 353 | * @p2p: peer-to-peer specific information. |
354 | * @btcoex: Bluetooth coexistence information. | ||
354 | * @scan_request: cfg80211 scan request object. | 355 | * @scan_request: cfg80211 scan request object. |
355 | * @usr_sync: mainly for dongle up/down synchronization. | 356 | * @usr_sync: mainly for dongle up/down synchronization. |
356 | * @bss_list: bss_list holding scanned ap information. | 357 | * @bss_list: bss_list holding scanned ap information. |
@@ -384,6 +385,7 @@ struct brcmf_cfg80211_info { | |||
384 | struct wiphy *wiphy; | 385 | struct wiphy *wiphy; |
385 | struct brcmf_cfg80211_conf *conf; | 386 | struct brcmf_cfg80211_conf *conf; |
386 | struct brcmf_p2p_info p2p; | 387 | struct brcmf_p2p_info p2p; |
388 | struct brcmf_btcoex_info *btcoex; | ||
387 | struct cfg80211_scan_request *scan_request; | 389 | struct cfg80211_scan_request *scan_request; |
388 | struct mutex usr_sync; | 390 | struct mutex usr_sync; |
389 | struct brcmf_scan_results *bss_list; | 391 | struct brcmf_scan_results *bss_list; |