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