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