Commit | Line | Data |
---|---|---|
5b435de0 AS |
1 | /* |
2 | * Copyright (c) 2010 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 | */ | |
02f77195 | 16 | |
5b435de0 AS |
17 | #include <linux/kernel.h> |
18 | #include <linux/string.h> | |
5b435de0 | 19 | #include <linux/netdevice.h> |
5b435de0 AS |
20 | #include <brcmu_wifi.h> |
21 | #include <brcmu_utils.h> | |
22 | #include "dhd.h" | |
23 | #include "dhd_bus.h" | |
24 | #include "dhd_proto.h" | |
25 | #include "dhd_dbg.h" | |
0af29bf7 | 26 | #include "fwil.h" |
e5483576 | 27 | #include "tracepoint.h" |
5b435de0 | 28 | |
0af29bf7 | 29 | #define PKTFILTER_BUF_SIZE 128 |
0af29bf7 HM |
30 | #define BRCMF_DEFAULT_BCN_TIMEOUT 3 |
31 | #define BRCMF_DEFAULT_SCAN_CHANNEL_TIME 40 | |
32 | #define BRCMF_DEFAULT_SCAN_UNASSOC_TIME 40 | |
33 | #define BRCMF_DEFAULT_PACKET_FILTER "100 0 0 0 0x01 0x00" | |
5b435de0 | 34 | |
8ae74654 | 35 | #ifdef DEBUG |
5b435de0 AS |
36 | static const char brcmf_version[] = |
37 | "Dongle Host Driver, version " BRCMF_VERSION_STR "\nCompiled on " | |
38 | __DATE__ " at " __TIME__; | |
39 | #else | |
40 | static const char brcmf_version[] = | |
41 | "Dongle Host Driver, version " BRCMF_VERSION_STR; | |
42 | #endif | |
43 | ||
53a2277d | 44 | |
b63487ed | 45 | bool brcmf_c_prec_enq(struct device *dev, struct pktq *q, |
5b435de0 AS |
46 | struct sk_buff *pkt, int prec) |
47 | { | |
48 | struct sk_buff *p; | |
49 | int eprec = -1; /* precedence to evict from */ | |
50 | bool discard_oldest; | |
b63487ed FL |
51 | struct brcmf_bus *bus_if = dev_get_drvdata(dev); |
52 | struct brcmf_pub *drvr = bus_if->drvr; | |
5b435de0 AS |
53 | |
54 | /* Fast case, precedence queue is not full and we are also not | |
55 | * exceeding total queue length | |
56 | */ | |
57 | if (!pktq_pfull(q, prec) && !pktq_full(q)) { | |
58 | brcmu_pktq_penq(q, prec, pkt); | |
59 | return true; | |
60 | } | |
61 | ||
62 | /* Determine precedence from which to evict packet, if any */ | |
63 | if (pktq_pfull(q, prec)) | |
64 | eprec = prec; | |
65 | else if (pktq_full(q)) { | |
66 | p = brcmu_pktq_peek_tail(q, &eprec); | |
67 | if (eprec > prec) | |
68 | return false; | |
69 | } | |
70 | ||
71 | /* Evict if needed */ | |
72 | if (eprec >= 0) { | |
73 | /* Detect queueing to unconfigured precedence */ | |
74 | discard_oldest = ac_bitmap_tst(drvr->wme_dp, eprec); | |
75 | if (eprec == prec && !discard_oldest) | |
76 | return false; /* refuse newer (incoming) packet */ | |
77 | /* Evict packet according to discard policy */ | |
78 | p = discard_oldest ? brcmu_pktq_pdeq(q, eprec) : | |
79 | brcmu_pktq_pdeq_tail(q, eprec); | |
80 | if (p == NULL) | |
5e8149f5 | 81 | brcmf_err("brcmu_pktq_penq() failed, oldest %d\n", |
5b435de0 AS |
82 | discard_oldest); |
83 | ||
84 | brcmu_pkt_buf_free_skb(p); | |
85 | } | |
86 | ||
87 | /* Enqueue */ | |
88 | p = brcmu_pktq_penq(q, prec, pkt); | |
89 | if (p == NULL) | |
5e8149f5 | 90 | brcmf_err("brcmu_pktq_penq() failed\n"); |
5b435de0 AS |
91 | |
92 | return p != NULL; | |
93 | } | |
94 | ||
5b435de0 AS |
95 | /* Convert user's input in hex pattern to byte-size mask */ |
96 | static int brcmf_c_pattern_atoh(char *src, char *dst) | |
97 | { | |
98 | int i; | |
99 | if (strncmp(src, "0x", 2) != 0 && strncmp(src, "0X", 2) != 0) { | |
5e8149f5 | 100 | brcmf_err("Mask invalid format. Needs to start with 0x\n"); |
5b435de0 AS |
101 | return -EINVAL; |
102 | } | |
103 | src = src + 2; /* Skip past 0x */ | |
104 | if (strlen(src) % 2 != 0) { | |
5e8149f5 | 105 | brcmf_err("Mask invalid format. Length must be even.\n"); |
5b435de0 AS |
106 | return -EINVAL; |
107 | } | |
108 | for (i = 0; *src != '\0'; i++) { | |
109 | unsigned long res; | |
110 | char num[3]; | |
111 | strncpy(num, src, 2); | |
112 | num[2] = '\0'; | |
113 | if (kstrtoul(num, 16, &res)) | |
114 | return -EINVAL; | |
115 | dst[i] = (u8)res; | |
116 | src += 2; | |
117 | } | |
118 | return i; | |
119 | } | |
120 | ||
0af29bf7 HM |
121 | static void |
122 | brcmf_c_pktfilter_offload_enable(struct brcmf_if *ifp, char *arg, int enable, | |
123 | int master_mode) | |
5b435de0 AS |
124 | { |
125 | unsigned long res; | |
0af29bf7 | 126 | char *argv; |
5b435de0 | 127 | char *arg_save = NULL, *arg_org = NULL; |
0af29bf7 | 128 | s32 err; |
f7264adb | 129 | struct brcmf_pkt_filter_enable_le enable_parm; |
5b435de0 | 130 | |
0af29bf7 | 131 | arg_save = kstrdup(arg, GFP_ATOMIC); |
5b435de0 AS |
132 | if (!arg_save) |
133 | goto fail; | |
134 | ||
135 | arg_org = arg_save; | |
5b435de0 | 136 | |
0af29bf7 | 137 | argv = strsep(&arg_save, " "); |
5b435de0 | 138 | |
0af29bf7 | 139 | if (argv == NULL) { |
5e8149f5 | 140 | brcmf_err("No args provided\n"); |
5b435de0 AS |
141 | goto fail; |
142 | } | |
143 | ||
5b435de0 AS |
144 | /* Parse packet filter id. */ |
145 | enable_parm.id = 0; | |
0af29bf7 | 146 | if (!kstrtoul(argv, 0, &res)) |
f7264adb | 147 | enable_parm.id = cpu_to_le32((u32)res); |
5b435de0 | 148 | |
0af29bf7 | 149 | /* Enable/disable the specified filter. */ |
f7264adb | 150 | enable_parm.enable = cpu_to_le32(enable); |
5b435de0 | 151 | |
0af29bf7 HM |
152 | err = brcmf_fil_iovar_data_set(ifp, "pkt_filter_enable", &enable_parm, |
153 | sizeof(enable_parm)); | |
154 | if (err) | |
5e8149f5 | 155 | brcmf_err("Set pkt_filter_enable error (%d)\n", err); |
5b435de0 | 156 | |
0af29bf7 HM |
157 | /* Control the master mode */ |
158 | err = brcmf_fil_iovar_int_set(ifp, "pkt_filter_mode", master_mode); | |
159 | if (err) | |
5e8149f5 | 160 | brcmf_err("Set pkt_filter_mode error (%d)\n", err); |
5b435de0 AS |
161 | |
162 | fail: | |
163 | kfree(arg_org); | |
164 | } | |
165 | ||
0af29bf7 | 166 | static void brcmf_c_pktfilter_offload_set(struct brcmf_if *ifp, char *arg) |
5b435de0 | 167 | { |
0af29bf7 | 168 | struct brcmf_pkt_filter_le *pkt_filter; |
5b435de0 AS |
169 | unsigned long res; |
170 | int buf_len; | |
0af29bf7 | 171 | s32 err; |
5b435de0 AS |
172 | u32 mask_size; |
173 | u32 pattern_size; | |
174 | char *argv[8], *buf = NULL; | |
175 | int i = 0; | |
176 | char *arg_save = NULL, *arg_org = NULL; | |
177 | ||
178 | arg_save = kstrdup(arg, GFP_ATOMIC); | |
179 | if (!arg_save) | |
180 | goto fail; | |
181 | ||
182 | arg_org = arg_save; | |
183 | ||
184 | buf = kmalloc(PKTFILTER_BUF_SIZE, GFP_ATOMIC); | |
185 | if (!buf) | |
186 | goto fail; | |
187 | ||
188 | argv[i] = strsep(&arg_save, " "); | |
0af29bf7 HM |
189 | while (argv[i]) { |
190 | i++; | |
191 | if (i >= 8) { | |
5e8149f5 | 192 | brcmf_err("Too many parameters\n"); |
0af29bf7 HM |
193 | goto fail; |
194 | } | |
5b435de0 | 195 | argv[i] = strsep(&arg_save, " "); |
0af29bf7 | 196 | } |
5b435de0 | 197 | |
0af29bf7 | 198 | if (i != 6) { |
5e8149f5 | 199 | brcmf_err("Not enough args provided %d\n", i); |
5b435de0 AS |
200 | goto fail; |
201 | } | |
202 | ||
0af29bf7 | 203 | pkt_filter = (struct brcmf_pkt_filter_le *)buf; |
5b435de0 AS |
204 | |
205 | /* Parse packet filter id. */ | |
0af29bf7 HM |
206 | pkt_filter->id = 0; |
207 | if (!kstrtoul(argv[0], 0, &res)) | |
208 | pkt_filter->id = cpu_to_le32((u32)res); | |
5b435de0 AS |
209 | |
210 | /* Parse filter polarity. */ | |
0af29bf7 HM |
211 | pkt_filter->negate_match = 0; |
212 | if (!kstrtoul(argv[1], 0, &res)) | |
213 | pkt_filter->negate_match = cpu_to_le32((u32)res); | |
5b435de0 AS |
214 | |
215 | /* Parse filter type. */ | |
0af29bf7 HM |
216 | pkt_filter->type = 0; |
217 | if (!kstrtoul(argv[2], 0, &res)) | |
218 | pkt_filter->type = cpu_to_le32((u32)res); | |
5b435de0 AS |
219 | |
220 | /* Parse pattern filter offset. */ | |
0af29bf7 HM |
221 | pkt_filter->u.pattern.offset = 0; |
222 | if (!kstrtoul(argv[3], 0, &res)) | |
223 | pkt_filter->u.pattern.offset = cpu_to_le32((u32)res); | |
5b435de0 AS |
224 | |
225 | /* Parse pattern filter mask. */ | |
0af29bf7 HM |
226 | mask_size = brcmf_c_pattern_atoh(argv[4], |
227 | (char *)pkt_filter->u.pattern.mask_and_pattern); | |
5b435de0 AS |
228 | |
229 | /* Parse pattern filter pattern. */ | |
0af29bf7 HM |
230 | pattern_size = brcmf_c_pattern_atoh(argv[5], |
231 | (char *)&pkt_filter->u.pattern.mask_and_pattern[mask_size]); | |
5b435de0 AS |
232 | |
233 | if (mask_size != pattern_size) { | |
5e8149f5 | 234 | brcmf_err("Mask and pattern not the same size\n"); |
5b435de0 AS |
235 | goto fail; |
236 | } | |
237 | ||
0af29bf7 | 238 | pkt_filter->u.pattern.size_bytes = cpu_to_le32(mask_size); |
c697be5a HM |
239 | buf_len = offsetof(struct brcmf_pkt_filter_le, |
240 | u.pattern.mask_and_pattern); | |
0af29bf7 | 241 | buf_len += mask_size + pattern_size; |
5b435de0 | 242 | |
0af29bf7 HM |
243 | err = brcmf_fil_iovar_data_set(ifp, "pkt_filter_add", pkt_filter, |
244 | buf_len); | |
245 | if (err) | |
5e8149f5 | 246 | brcmf_err("Set pkt_filter_add error (%d)\n", err); |
5b435de0 AS |
247 | |
248 | fail: | |
249 | kfree(arg_org); | |
250 | ||
251 | kfree(buf); | |
252 | } | |
253 | ||
0af29bf7 | 254 | int brcmf_c_preinit_dcmds(struct brcmf_if *ifp) |
5b435de0 | 255 | { |
0af29bf7 HM |
256 | s8 eventmask[BRCMF_EVENTING_MASK_LEN]; |
257 | u8 buf[BRCMF_DCMD_SMLEN]; | |
258 | char *ptr; | |
259 | s32 err; | |
135e4c61 FL |
260 | struct brcmf_bus_dcmd *cmdlst; |
261 | struct list_head *cur, *q; | |
5b435de0 | 262 | |
0af29bf7 HM |
263 | /* retreive mac address */ |
264 | err = brcmf_fil_iovar_data_get(ifp, "cur_etheraddr", ifp->mac_addr, | |
265 | sizeof(ifp->mac_addr)); | |
266 | if (err < 0) { | |
5e8149f5 | 267 | brcmf_err("Retreiving cur_etheraddr failed, %d\n", |
0af29bf7 HM |
268 | err); |
269 | goto done; | |
5b435de0 | 270 | } |
0af29bf7 | 271 | memcpy(ifp->drvr->mac, ifp->mac_addr, sizeof(ifp->drvr->mac)); |
5b435de0 AS |
272 | |
273 | /* query for 'ver' to get version info from firmware */ | |
274 | memset(buf, 0, sizeof(buf)); | |
0af29bf7 HM |
275 | strcpy(buf, "ver"); |
276 | err = brcmf_fil_iovar_data_get(ifp, "ver", buf, sizeof(buf)); | |
277 | if (err < 0) { | |
5e8149f5 | 278 | brcmf_err("Retreiving version information failed, %d\n", |
0af29bf7 HM |
279 | err); |
280 | goto done; | |
281 | } | |
282 | ptr = (char *)buf; | |
5b435de0 AS |
283 | strsep(&ptr, "\n"); |
284 | /* Print fw version info */ | |
5e8149f5 | 285 | brcmf_err("Firmware version = %s\n", buf); |
5b435de0 | 286 | |
0af29bf7 HM |
287 | /* |
288 | * Setup timeout if Beacons are lost and roam is off to report | |
289 | * link down | |
290 | */ | |
291 | err = brcmf_fil_iovar_int_set(ifp, "bcn_timeout", | |
292 | BRCMF_DEFAULT_BCN_TIMEOUT); | |
293 | if (err) { | |
5e8149f5 | 294 | brcmf_err("bcn_timeout error (%d)\n", err); |
0af29bf7 HM |
295 | goto done; |
296 | } | |
5b435de0 AS |
297 | |
298 | /* Enable/Disable build-in roaming to allowed ext supplicant to take | |
0af29bf7 HM |
299 | * of romaing |
300 | */ | |
301 | err = brcmf_fil_iovar_int_set(ifp, "roam_off", 1); | |
302 | if (err) { | |
5e8149f5 | 303 | brcmf_err("roam_off error (%d)\n", err); |
0af29bf7 HM |
304 | goto done; |
305 | } | |
306 | ||
307 | /* Setup event_msgs, enable E_IF */ | |
308 | err = brcmf_fil_iovar_data_get(ifp, "event_msgs", eventmask, | |
309 | BRCMF_EVENTING_MASK_LEN); | |
310 | if (err) { | |
5e8149f5 | 311 | brcmf_err("Get event_msgs error (%d)\n", err); |
0af29bf7 HM |
312 | goto done; |
313 | } | |
314 | setbit(eventmask, BRCMF_E_IF); | |
315 | err = brcmf_fil_iovar_data_set(ifp, "event_msgs", eventmask, | |
316 | BRCMF_EVENTING_MASK_LEN); | |
317 | if (err) { | |
5e8149f5 | 318 | brcmf_err("Set event_msgs error (%d)\n", err); |
0af29bf7 HM |
319 | goto done; |
320 | } | |
321 | ||
322 | /* Setup default scan channel time */ | |
323 | err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_CHANNEL_TIME, | |
324 | BRCMF_DEFAULT_SCAN_CHANNEL_TIME); | |
325 | if (err) { | |
5e8149f5 | 326 | brcmf_err("BRCMF_C_SET_SCAN_CHANNEL_TIME error (%d)\n", |
0af29bf7 HM |
327 | err); |
328 | goto done; | |
329 | } | |
330 | ||
331 | /* Setup default scan unassoc time */ | |
332 | err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_UNASSOC_TIME, | |
333 | BRCMF_DEFAULT_SCAN_UNASSOC_TIME); | |
334 | if (err) { | |
5e8149f5 | 335 | brcmf_err("BRCMF_C_SET_SCAN_UNASSOC_TIME error (%d)\n", |
0af29bf7 HM |
336 | err); |
337 | goto done; | |
338 | } | |
339 | ||
0af29bf7 HM |
340 | /* Setup packet filter */ |
341 | brcmf_c_pktfilter_offload_set(ifp, BRCMF_DEFAULT_PACKET_FILTER); | |
342 | brcmf_c_pktfilter_offload_enable(ifp, BRCMF_DEFAULT_PACKET_FILTER, | |
343 | 0, true); | |
344 | ||
135e4c61 | 345 | /* set bus specific command if there is any */ |
0af29bf7 | 346 | list_for_each_safe(cur, q, &ifp->drvr->bus_if->dcmd_list) { |
135e4c61 FL |
347 | cmdlst = list_entry(cur, struct brcmf_bus_dcmd, list); |
348 | if (cmdlst->name && cmdlst->param && cmdlst->param_len) { | |
0af29bf7 HM |
349 | brcmf_fil_iovar_data_set(ifp, cmdlst->name, |
350 | cmdlst->param, | |
351 | cmdlst->param_len); | |
135e4c61 FL |
352 | } |
353 | list_del(cur); | |
354 | kfree(cmdlst); | |
355 | } | |
0af29bf7 HM |
356 | done: |
357 | return err; | |
5b435de0 | 358 | } |
e5483576 AS |
359 | |
360 | #ifdef CONFIG_BRCM_TRACING | |
361 | void __brcmf_err(const char *func, const char *fmt, ...) | |
362 | { | |
363 | struct va_format vaf = { | |
364 | .fmt = fmt, | |
365 | }; | |
366 | va_list args; | |
367 | ||
368 | va_start(args, fmt); | |
369 | vaf.va = &args; | |
370 | pr_err("%s: %pV", func, &vaf); | |
371 | trace_brcmf_err(func, &vaf); | |
372 | va_end(args); | |
373 | } | |
374 | #endif | |
375 | #if defined(CONFIG_BRCM_TRACING) || defined(CONFIG_BRCMDBG) | |
376 | void __brcmf_dbg(u32 level, const char *func, const char *fmt, ...) | |
377 | { | |
378 | struct va_format vaf = { | |
379 | .fmt = fmt, | |
380 | }; | |
381 | va_list args; | |
382 | ||
383 | va_start(args, fmt); | |
384 | vaf.va = &args; | |
385 | if (brcmf_msg_level & level) | |
386 | pr_debug("%s %pV", func, &vaf); | |
387 | trace_brcmf_dbg(level, func, &vaf); | |
388 | va_end(args); | |
389 | } | |
390 | #endif |