1 | /* $NetBSD: ath_netbsd.c,v 1.22 2014/02/25 18:30:09 pooka Exp $ */ |
2 | |
3 | /*- |
4 | * Copyright (c) 2003, 2004 David Young |
5 | * All rights reserved. |
6 | * |
7 | * Redistribution and use in source and binary forms, with or without |
8 | * modification, are permitted provided that the following conditions |
9 | * are met: |
10 | * 1. Redistributions of source code must retain the above copyright |
11 | * notice, this list of conditions and the following disclaimer. |
12 | * 2. Redistributions in binary form must reproduce the above copyright |
13 | * notice, this list of conditions and the following disclaimer in the |
14 | * documentation and/or other materials provided with the distribution. |
15 | * |
16 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
17 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
18 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
19 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
20 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
21 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
22 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
23 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
25 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
26 | */ |
27 | |
28 | #include <sys/cdefs.h> |
29 | __KERNEL_RCSID(0, "$NetBSD: ath_netbsd.c,v 1.22 2014/02/25 18:30:09 pooka Exp $" ); |
30 | |
31 | #include <sys/param.h> |
32 | #include <sys/types.h> |
33 | #include <sys/errno.h> |
34 | #include <sys/systm.h> |
35 | #include <sys/sysctl.h> |
36 | #include <sys/mbuf.h> |
37 | #include <sys/malloc.h> |
38 | #include <sys/kernel.h> |
39 | #include <sys/socket.h> |
40 | #include <sys/sockio.h> |
41 | #include <sys/sysctl.h> |
42 | #include <sys/callout.h> |
43 | #include <sys/bus.h> |
44 | #include <sys/endian.h> |
45 | #include <sys/device.h> |
46 | #include <sys/module.h> |
47 | |
48 | #include <net/if.h> |
49 | #include <net/if_dl.h> |
50 | #include <net/if_media.h> |
51 | #include <net/if_arp.h> |
52 | #include <net/if_ether.h> |
53 | #include <net/if_llc.h> |
54 | |
55 | #include <net80211/ieee80211_netbsd.h> |
56 | #include <net80211/ieee80211_var.h> |
57 | #include <dev/ic/ath_netbsd.h> |
58 | #include <dev/ic/athvar.h> |
59 | |
60 | /* |
61 | * Setup sysctl(3) MIB, hw.ath.*. |
62 | * |
63 | * TBD condition CTLFLAG_PERMANENT on being a module or not |
64 | */ |
65 | SYSCTL_SETUP(sysctl_ath, "sysctl ath subtree setup" ) |
66 | { |
67 | int rc; |
68 | const struct sysctlnode *cnode, *rnode; |
69 | |
70 | if ((rnode = ath_sysctl_treetop(clog)) == NULL) |
71 | return; |
72 | |
73 | if ((rc = SYSCTL_GLOBAL_INT(CTLFLAG_READWRITE, "dwell" , |
74 | "channel dwell time (ms) for AP/station scanning" , |
75 | dwelltime)) != 0) |
76 | goto err; |
77 | |
78 | if ((rc = SYSCTL_GLOBAL_INT(CTLFLAG_READWRITE, "calibrate" , |
79 | "chip calibration interval (secs)" , calinterval)) != 0) |
80 | goto err; |
81 | |
82 | if ((rc = SYSCTL_GLOBAL_INT(CTLFLAG_READWRITE, "outdoor" , |
83 | "outdoor operation" , outdoor)) != 0) |
84 | goto err; |
85 | |
86 | /* country code */ |
87 | if ((rc = SYSCTL_GLOBAL_INT(CTLFLAG_READWRITE, |
88 | "countrycode" , "country code" , countrycode)) != 0) |
89 | goto err; |
90 | |
91 | /* regulatory domain */ |
92 | if ((rc = SYSCTL_GLOBAL_INT(CTLFLAG_READWRITE, "regdomain" , |
93 | "EEPROM regdomain code" , regdomain)) != 0) |
94 | goto err; |
95 | |
96 | if ((rc = SYSCTL_GLOBAL_INT(CTLFLAG_READWRITE, "debug" , |
97 | "control debugging printfs" , debug)) != 0) |
98 | goto err; |
99 | |
100 | if ((rc = SYSCTL_GLOBAL_INT(CTLFLAG_READONLY, "rxbuf" , |
101 | "rx buffers allocated" , rxbuf)) != 0) |
102 | goto err; |
103 | if ((rc = SYSCTL_GLOBAL_INT(CTLFLAG_READONLY, "txbuf" , |
104 | "tx buffers allocated" , txbuf)) != 0) |
105 | goto err; |
106 | |
107 | return; |
108 | err: |
109 | printf("%s: sysctl_createv failed (rc = %d)\n" , __func__, rc); |
110 | } |
111 | |
112 | static int |
113 | ath_sysctl_slottime(SYSCTLFN_ARGS) |
114 | { |
115 | struct ath_softc *sc; |
116 | struct sysctlnode node; |
117 | u_int slottime; |
118 | int error; |
119 | |
120 | node = *rnode; |
121 | sc = (struct ath_softc *)node.sysctl_data; |
122 | slottime = ath_hal_getslottime(sc->sc_ah); |
123 | node.sysctl_data = &slottime; |
124 | error = sysctl_lookup(SYSCTLFN_CALL(&node)); |
125 | if (error || newp == NULL) |
126 | return error; |
127 | return !ath_hal_setslottime(sc->sc_ah, slottime) ? EINVAL : 0; |
128 | } |
129 | |
130 | static int |
131 | ath_sysctl_acktimeout(SYSCTLFN_ARGS) |
132 | { |
133 | struct ath_softc *sc; |
134 | struct sysctlnode node; |
135 | u_int acktimeout; |
136 | int error; |
137 | |
138 | node = *rnode; |
139 | sc = (struct ath_softc *)node.sysctl_data; |
140 | acktimeout = ath_hal_getacktimeout(sc->sc_ah); |
141 | node.sysctl_data = &acktimeout; |
142 | error = sysctl_lookup(SYSCTLFN_CALL(&node)); |
143 | if (error || newp == NULL) |
144 | return error; |
145 | return !ath_hal_setacktimeout(sc->sc_ah, acktimeout) ? EINVAL : 0; |
146 | } |
147 | |
148 | static int |
149 | ath_sysctl_ctstimeout(SYSCTLFN_ARGS) |
150 | { |
151 | struct ath_softc *sc; |
152 | struct sysctlnode node; |
153 | u_int ctstimeout; |
154 | int error; |
155 | |
156 | node = *rnode; |
157 | sc = (struct ath_softc *)node.sysctl_data; |
158 | ctstimeout = ath_hal_getctstimeout(sc->sc_ah); |
159 | node.sysctl_data = &ctstimeout; |
160 | error = sysctl_lookup(SYSCTLFN_CALL(&node)); |
161 | if (error || newp == NULL) |
162 | return error; |
163 | return !ath_hal_setctstimeout(sc->sc_ah, ctstimeout) ? EINVAL : 0; |
164 | } |
165 | |
166 | static int |
167 | ath_sysctl_softled(SYSCTLFN_ARGS) |
168 | { |
169 | struct ath_softc *sc; |
170 | struct sysctlnode node; |
171 | int softled; |
172 | int error; |
173 | |
174 | node = *rnode; |
175 | sc = (struct ath_softc *)node.sysctl_data; |
176 | softled = sc->sc_softled; |
177 | node.sysctl_data = &softled; |
178 | error = sysctl_lookup(SYSCTLFN_CALL(&node)); |
179 | if (error || newp == NULL) |
180 | return error; |
181 | softled = (softled != 0); |
182 | if (softled != sc->sc_softled) { |
183 | if (softled) { |
184 | /* NB: handle any sc_ledpin change */ |
185 | ath_hal_gpioCfgOutput(sc->sc_ah, sc->sc_ledpin, |
186 | HAL_GPIO_MUX_MAC_NETWORK_LED); |
187 | ath_hal_gpioset(sc->sc_ah, sc->sc_ledpin, |
188 | !sc->sc_ledon); |
189 | } |
190 | sc->sc_softled = softled; |
191 | } |
192 | return 0; |
193 | } |
194 | |
195 | static int |
196 | ath_sysctl_rxantenna(SYSCTLFN_ARGS) |
197 | { |
198 | struct ath_softc *sc; |
199 | struct sysctlnode node; |
200 | u_int defantenna; |
201 | int error; |
202 | |
203 | node = *rnode; |
204 | sc = (struct ath_softc *)node.sysctl_data; |
205 | defantenna = ath_hal_getdefantenna(sc->sc_ah); |
206 | node.sysctl_data = &defantenna; |
207 | error = sysctl_lookup(SYSCTLFN_CALL(&node)); |
208 | if (error || newp == NULL) |
209 | return error; |
210 | ath_hal_setdefantenna(sc->sc_ah, defantenna); |
211 | return 0; |
212 | } |
213 | |
214 | static int |
215 | ath_sysctl_diversity(SYSCTLFN_ARGS) |
216 | { |
217 | struct ath_softc *sc; |
218 | struct sysctlnode node; |
219 | u_int diversity; |
220 | int error; |
221 | |
222 | node = *rnode; |
223 | sc = (struct ath_softc *)node.sysctl_data; |
224 | diversity = sc->sc_diversity; |
225 | node.sysctl_data = &diversity; |
226 | error = sysctl_lookup(SYSCTLFN_CALL(&node)); |
227 | if (error || newp == NULL) |
228 | return error; |
229 | if (!ath_hal_setdiversity(sc->sc_ah, diversity)) |
230 | return EINVAL; |
231 | sc->sc_diversity = diversity; |
232 | return 0; |
233 | } |
234 | |
235 | static int |
236 | ath_sysctl_diag(SYSCTLFN_ARGS) |
237 | { |
238 | struct ath_softc *sc; |
239 | struct sysctlnode node; |
240 | u_int32_t diag; |
241 | int error; |
242 | |
243 | node = *rnode; |
244 | sc = (struct ath_softc *)node.sysctl_data; |
245 | if (!ath_hal_getdiag(sc->sc_ah, &diag)) |
246 | return EINVAL; |
247 | node.sysctl_data = &diag; |
248 | error = sysctl_lookup(SYSCTLFN_CALL(&node)); |
249 | if (error || newp == NULL) |
250 | return error; |
251 | return !ath_hal_setdiag(sc->sc_ah, diag) ? EINVAL : 0; |
252 | } |
253 | |
254 | static int |
255 | ath_sysctl_tpscale(SYSCTLFN_ARGS) |
256 | { |
257 | struct ath_softc *sc; |
258 | struct sysctlnode node; |
259 | u_int32_t scale; |
260 | int error; |
261 | |
262 | node = *rnode; |
263 | sc = (struct ath_softc *)node.sysctl_data; |
264 | (void)ath_hal_gettpscale(sc->sc_ah, &scale); |
265 | node.sysctl_data = &scale; |
266 | error = sysctl_lookup(SYSCTLFN_CALL(&node)); |
267 | if (error || newp == NULL) |
268 | return error; |
269 | return !ath_hal_settpscale(sc->sc_ah, scale) |
270 | ? EINVAL |
271 | : ath_reset(&sc->sc_if); |
272 | } |
273 | |
274 | static int |
275 | ath_sysctl_tpc(SYSCTLFN_ARGS) |
276 | { |
277 | struct ath_softc *sc; |
278 | struct sysctlnode node; |
279 | u_int tpc; |
280 | int error; |
281 | |
282 | node = *rnode; |
283 | sc = (struct ath_softc *)node.sysctl_data; |
284 | tpc = ath_hal_gettpc(sc->sc_ah); |
285 | node.sysctl_data = &tpc; |
286 | error = sysctl_lookup(SYSCTLFN_CALL(&node)); |
287 | if (error || newp == NULL) |
288 | return error; |
289 | return !ath_hal_settpc(sc->sc_ah, tpc) ? EINVAL : 0; |
290 | } |
291 | |
292 | static int |
293 | ath_sysctl_rfkill(SYSCTLFN_ARGS) |
294 | { |
295 | struct ath_softc *sc; |
296 | struct sysctlnode node; |
297 | u_int rfkill; |
298 | int error; |
299 | |
300 | node = *rnode; |
301 | sc = (struct ath_softc *)node.sysctl_data; |
302 | rfkill = ath_hal_getrfkill(sc->sc_ah); |
303 | node.sysctl_data = &rfkill; |
304 | error = sysctl_lookup(SYSCTLFN_CALL(&node)); |
305 | if (error || newp == NULL) |
306 | return error; |
307 | return !ath_hal_setrfkill(sc->sc_ah, rfkill) ? EINVAL : 0; |
308 | } |
309 | |
310 | static int |
311 | ath_sysctl_rfsilent(SYSCTLFN_ARGS) |
312 | { |
313 | struct ath_softc *sc; |
314 | struct sysctlnode node; |
315 | u_int rfsilent; |
316 | int error; |
317 | |
318 | node = *rnode; |
319 | sc = (struct ath_softc *)node.sysctl_data; |
320 | (void)ath_hal_getrfsilent(sc->sc_ah, &rfsilent); |
321 | node.sysctl_data = &rfsilent; |
322 | error = sysctl_lookup(SYSCTLFN_CALL(&node)); |
323 | if (error || newp == NULL) |
324 | return error; |
325 | return !ath_hal_setrfsilent(sc->sc_ah, rfsilent) ? EINVAL : 0; |
326 | } |
327 | |
328 | static int |
329 | ath_sysctl_regdomain(SYSCTLFN_ARGS) |
330 | { |
331 | struct ath_softc *sc; |
332 | struct sysctlnode node; |
333 | u_int32_t rd; |
334 | int error; |
335 | |
336 | node = *rnode; |
337 | sc = (struct ath_softc *)node.sysctl_data; |
338 | if (!ath_hal_getregdomain(sc->sc_ah, &rd)) |
339 | return EINVAL; |
340 | node.sysctl_data = &rd; |
341 | error = sysctl_lookup(SYSCTLFN_CALL(&node)); |
342 | if (error || newp == NULL) |
343 | return error; |
344 | return !ath_hal_setregdomain(sc->sc_ah, rd) ? EINVAL : 0; |
345 | } |
346 | |
347 | static int |
348 | ath_sysctl_tpack(SYSCTLFN_ARGS) |
349 | { |
350 | struct ath_softc *sc; |
351 | struct sysctlnode node; |
352 | u_int32_t tpack; |
353 | int error; |
354 | |
355 | node = *rnode; |
356 | sc = (struct ath_softc *)node.sysctl_data; |
357 | (void)ath_hal_gettpack(sc->sc_ah, &tpack); |
358 | node.sysctl_data = &tpack; |
359 | error = sysctl_lookup(SYSCTLFN_CALL(&node)); |
360 | if (error || newp == NULL) |
361 | return error; |
362 | return !ath_hal_settpack(sc->sc_ah, tpack) ? EINVAL : 0; |
363 | } |
364 | |
365 | static int |
366 | ath_sysctl_tpcts(SYSCTLFN_ARGS) |
367 | { |
368 | struct ath_softc *sc; |
369 | struct sysctlnode node; |
370 | u_int32_t tpcts; |
371 | int error; |
372 | |
373 | node = *rnode; |
374 | sc = (struct ath_softc *)node.sysctl_data; |
375 | (void)ath_hal_gettpcts(sc->sc_ah, &tpcts); |
376 | node.sysctl_data = &tpcts; |
377 | error = sysctl_lookup(SYSCTLFN_CALL(&node)); |
378 | if (error || newp == NULL) |
379 | return error; |
380 | return !ath_hal_settpcts(sc->sc_ah, tpcts) ? EINVAL : 0; |
381 | } |
382 | |
383 | const struct sysctlnode * |
384 | ath_sysctl_instance(const char *dvname, struct sysctllog **log) |
385 | { |
386 | int rc; |
387 | const struct sysctlnode *rnode; |
388 | |
389 | if ((rc = sysctl_createv(log, 0, NULL, &rnode, |
390 | CTLFLAG_PERMANENT, CTLTYPE_NODE, dvname, |
391 | SYSCTL_DESCR("ath information and options" ), |
392 | NULL, 0, NULL, 0, CTL_HW, CTL_CREATE, CTL_EOL)) != 0) |
393 | goto err; |
394 | |
395 | return rnode; |
396 | err: |
397 | printf("%s: sysctl_createv failed, rc = %d\n" , __func__, rc); |
398 | return NULL; |
399 | } |
400 | |
401 | const struct sysctlnode * |
402 | ath_sysctl_treetop(struct sysctllog **log) |
403 | { |
404 | int rc; |
405 | const struct sysctlnode *rnode; |
406 | |
407 | if ((rc = sysctl_createv(log, 0, NULL, &rnode, |
408 | CTLFLAG_PERMANENT, CTLTYPE_NODE, "ath" , |
409 | SYSCTL_DESCR("ath information and options" ), |
410 | NULL, 0, NULL, 0, CTL_HW, CTL_CREATE, CTL_EOL)) != 0) |
411 | goto err; |
412 | |
413 | return rnode; |
414 | err: |
415 | printf("%s: sysctl_createv failed, rc = %d\n" , __func__, rc); |
416 | return NULL; |
417 | } |
418 | |
419 | void |
420 | ath_sysctlattach(struct ath_softc *sc) |
421 | { |
422 | int rc; |
423 | struct sysctllog **log = &sc->sc_sysctllog; |
424 | const struct sysctlnode *cnode, *rnode; |
425 | |
426 | ath_hal_getcountrycode(sc->sc_ah, &sc->sc_countrycode); |
427 | (void)ath_hal_getregdomain(sc->sc_ah, &sc->sc_regdomain); |
428 | sc->sc_debug = ath_debug; |
429 | sc->sc_txintrperiod = ATH_TXINTR_PERIOD; |
430 | |
431 | if ((rnode = ath_sysctl_instance(device_xname(sc->sc_dev), log)) == NULL) |
432 | return; |
433 | |
434 | if ((rc = SYSCTL_INT(0, countrycode, "EEPROM country code" )) != 0) |
435 | goto err; |
436 | |
437 | if ((rc = SYSCTL_INT(CTLFLAG_READWRITE, debug, |
438 | "control debugging printfs" )) != 0) |
439 | goto err; |
440 | |
441 | #if 0 |
442 | /* channel dwell time (ms) for AP/station scanning */ |
443 | if ((rc = SYSCTL_INT(CTLFLAG_READWRITE, dwell, |
444 | "Channel dwell time (ms) for scanning" )) != 0) |
445 | goto err; |
446 | #endif |
447 | |
448 | if ((rc = SYSCTL_INT_SUBR(CTLFLAG_READWRITE, slottime, |
449 | "802.11 slot time (us)" )) != 0) |
450 | goto err; |
451 | if ((rc = SYSCTL_INT_SUBR(CTLFLAG_READWRITE, acktimeout, |
452 | "802.11 ACK timeout (us)" )) != 0) |
453 | goto err; |
454 | if ((rc = SYSCTL_INT_SUBR(CTLFLAG_READWRITE, ctstimeout, |
455 | "802.11 CTS timeout (us)" )) != 0) |
456 | goto err; |
457 | if ((rc = SYSCTL_INT_SUBR(CTLFLAG_READWRITE, softled, |
458 | "enable/disable software LED support" )) != 0) |
459 | goto err; |
460 | if ((rc = SYSCTL_INT(CTLFLAG_READWRITE, ledpin, |
461 | "GPIO pin connected to LED" )) != 0) |
462 | goto err; |
463 | if ((rc = SYSCTL_INT(CTLFLAG_READWRITE, ledon, |
464 | "setting to turn LED on" )) != 0) |
465 | goto err; |
466 | if ((rc = SYSCTL_INT(CTLFLAG_READWRITE, ledidle, |
467 | "idle time for inactivity LED (ticks)" )) != 0) |
468 | goto err; |
469 | if ((rc = SYSCTL_INT(CTLFLAG_READWRITE, txantenna, |
470 | "tx antenna (0=auto)" )) != 0) |
471 | goto err; |
472 | if ((rc = SYSCTL_INT_SUBR(CTLFLAG_READWRITE, rxantenna, |
473 | "default/rx antenna" )) != 0) |
474 | goto err; |
475 | if (ath_hal_hasdiversity(sc->sc_ah)) { |
476 | if ((rc = SYSCTL_INT_SUBR(CTLFLAG_READWRITE, diversity, |
477 | "antenna diversity" )) != 0) |
478 | goto err; |
479 | } |
480 | if ((rc = SYSCTL_INT(CTLFLAG_READWRITE, txintrperiod, |
481 | "tx descriptor batching" )) != 0) |
482 | goto err; |
483 | if ((rc = SYSCTL_INT_SUBR(CTLFLAG_READWRITE, diag, |
484 | "h/w diagnostic control" )) != 0) |
485 | goto err; |
486 | if ((rc = SYSCTL_INT_SUBR(CTLFLAG_READWRITE, tpscale, |
487 | "tx power scaling" )) != 0) |
488 | goto err; |
489 | if (ath_hal_hastpc(sc->sc_ah)) { |
490 | if ((rc = SYSCTL_INT_SUBR(CTLFLAG_READWRITE, tpc, |
491 | "enable/disable per-packet TPC" )) != 0) |
492 | goto err; |
493 | if ((rc = SYSCTL_INT_SUBR(CTLFLAG_READWRITE, tpack, |
494 | "tx power for ack frames" )) != 0) |
495 | goto err; |
496 | if ((rc = SYSCTL_INT_SUBR(CTLFLAG_READWRITE, tpcts, |
497 | "tx power for cts frames" )) != 0) |
498 | goto err; |
499 | } |
500 | if (ath_hal_hasrfsilent(sc->sc_ah)) { |
501 | if ((rc = SYSCTL_INT_SUBR(CTLFLAG_READWRITE, rfsilent, |
502 | "h/w RF silent config" )) != 0) |
503 | goto err; |
504 | if ((rc = SYSCTL_INT_SUBR(CTLFLAG_READWRITE, rfkill, |
505 | "enable/disable RF kill switch" )) != 0) |
506 | goto err; |
507 | } |
508 | if ((rc = SYSCTL_INT_SUBR(CTLFLAG_READWRITE, regdomain, |
509 | "EEPROM regdomain code" )) != 0) |
510 | goto err; |
511 | return; |
512 | err: |
513 | printf("%s: sysctl_createv failed, rc = %d\n" , __func__, rc); |
514 | } |
515 | |
516 | MODULE(MODULE_CLASS_MISC, ath, "ath_hal" ); |
517 | |
518 | static int |
519 | ath_modcmd(modcmd_t cmd, void *opaque) |
520 | { |
521 | switch (cmd) { |
522 | case MODULE_CMD_INIT: |
523 | case MODULE_CMD_FINI: |
524 | return 0; |
525 | default: |
526 | return ENOTTY; |
527 | } |
528 | } |
529 | |