1 | /*- |
2 | * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting |
3 | * All rights reserved. |
4 | * |
5 | * Redistribution and use in source and binary forms, with or without |
6 | * modification, are permitted provided that the following conditions |
7 | * are met: |
8 | * 1. Redistributions of source code must retain the above copyright |
9 | * notice, this list of conditions and the following disclaimer, |
10 | * without modification. |
11 | * 2. Redistributions in binary form must reproduce at minimum a disclaimer |
12 | * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any |
13 | * redistribution must be conditioned upon including a substantially |
14 | * similar Disclaimer requirement for further binary redistribution. |
15 | * |
16 | * NO WARRANTY |
17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
18 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
19 | * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY |
20 | * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL |
21 | * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, |
22 | * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER |
25 | * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
27 | * THE POSSIBILITY OF SUCH DAMAGES. |
28 | * |
29 | * $Id: ah_osdep.c,v 1.6 2012/04/11 13:48:11 nakayama Exp $ |
30 | */ |
31 | |
32 | #include <sys/cdefs.h> |
33 | __KERNEL_RCSID(0, "$NetBSD: ah_osdep.c,v 1.6 2012/04/11 13:48:11 nakayama Exp $" ); |
34 | |
35 | #ifdef _KERNEL_OPT |
36 | #include "opt_athhal.h" |
37 | #endif |
38 | |
39 | #include <sys/param.h> |
40 | #include <sys/systm.h> |
41 | #include <sys/kernel.h> |
42 | #include <sys/sysctl.h> |
43 | #include <sys/malloc.h> |
44 | #include <sys/proc.h> |
45 | #include <sys/kauth.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 | |
54 | #include <external/isc/atheros_hal/dist/ah.h> |
55 | |
56 | extern void ath_hal_printf(struct ath_hal *, const char*, ...) |
57 | __printflike(2,3); |
58 | extern void ath_hal_vprintf(struct ath_hal *, const char*, va_list) |
59 | __printflike(2, 0); |
60 | extern const char* ath_hal_ether_sprintf(const u_int8_t *mac); |
61 | extern void *ath_hal_malloc(size_t); |
62 | extern void ath_hal_free(void *); |
63 | #ifdef ATHHAL_ASSERT |
64 | extern void ath_hal_assert_failed(const char* filename, |
65 | int lineno, const char* msg); |
66 | #endif |
67 | #ifdef ATHHAL_DEBUG |
68 | extern void HALDEBUG(struct ath_hal *ah, u_int mask, const char* fmt, ...) |
69 | __printflike(3,4); |
70 | #endif /* ATHHAL_DEBUG */ |
71 | |
72 | #ifdef ATHHAL_DEBUG |
73 | static int ath_hal_debug = 0; |
74 | #endif /* ATHHAL_DEBUG */ |
75 | |
76 | int ath_hal_dma_beacon_response_time = 2; /* in TU's */ |
77 | int ath_hal_sw_beacon_response_time = 10; /* in TU's */ |
78 | int ath_hal_additional_swba_backoff = 0; /* in TU's */ |
79 | |
80 | SYSCTL_SETUP(sysctl_ath_hal, "sysctl ath.hal subtree setup" ) |
81 | { |
82 | int rc; |
83 | const struct sysctlnode *cnode, *rnode; |
84 | |
85 | if ((rc = sysctl_createv(clog, 0, NULL, &rnode, CTLFLAG_PERMANENT, |
86 | CTLTYPE_NODE, "hw" , NULL, NULL, 0, NULL, 0, CTL_HW, CTL_EOL)) != 0) |
87 | goto err; |
88 | |
89 | if ((rc = sysctl_createv(clog, 0, &rnode, &rnode, CTLFLAG_PERMANENT, |
90 | CTLTYPE_NODE, "ath" , SYSCTL_DESCR("Atheros driver parameters" ), |
91 | NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL)) != 0) |
92 | goto err; |
93 | |
94 | if ((rc = sysctl_createv(clog, 0, &rnode, &rnode, CTLFLAG_PERMANENT, |
95 | CTLTYPE_NODE, "hal" , SYSCTL_DESCR("Atheros HAL parameters" ), |
96 | NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL)) != 0) |
97 | goto err; |
98 | |
99 | #if 0 |
100 | if ((rc = sysctl_createv(clog, 0, &rnode, &cnode, |
101 | CTLFLAG_PERMANENT|CTLFLAG_READONLY, CTLTYPE_STRING, "version" , |
102 | SYSCTL_DESCR("Atheros HAL version" ), NULL, 0, &ath_hal_version, 0, |
103 | CTL_CREATE, CTL_EOL)) != 0) |
104 | goto err; |
105 | #endif |
106 | |
107 | if ((rc = sysctl_createv(clog, 0, &rnode, &cnode, |
108 | CTLFLAG_PERMANENT|CTLFLAG_READWRITE, CTLTYPE_INT, "dma_brt" , |
109 | SYSCTL_DESCR("Atheros HAL DMA beacon response time" ), NULL, 0, |
110 | &ath_hal_dma_beacon_response_time, 0, CTL_CREATE, CTL_EOL)) != 0) |
111 | goto err; |
112 | |
113 | if ((rc = sysctl_createv(clog, 0, &rnode, &cnode, |
114 | CTLFLAG_PERMANENT|CTLFLAG_READWRITE, CTLTYPE_INT, "sw_brt" , |
115 | SYSCTL_DESCR("Atheros HAL software beacon response time" ), NULL, 0, |
116 | &ath_hal_sw_beacon_response_time, 0, CTL_CREATE, CTL_EOL)) != 0) |
117 | goto err; |
118 | |
119 | if ((rc = sysctl_createv(clog, 0, &rnode, &cnode, |
120 | CTLFLAG_PERMANENT|CTLFLAG_READWRITE, CTLTYPE_INT, "swba_backoff" , |
121 | SYSCTL_DESCR("Atheros HAL additional SWBA backoff time" ), NULL, 0, |
122 | &ath_hal_additional_swba_backoff, 0, CTL_CREATE, CTL_EOL)) != 0) |
123 | goto err; |
124 | |
125 | #ifdef ATHHAL_DEBUG |
126 | if ((rc = sysctl_createv(clog, 0, &rnode, &cnode, |
127 | CTLFLAG_PERMANENT|CTLFLAG_READWRITE, CTLTYPE_INT, "debug" , |
128 | SYSCTL_DESCR("Atheros HAL debugging printfs" ), NULL, 0, |
129 | &ath_hal_debug, 0, CTL_CREATE, CTL_EOL)) != 0) |
130 | goto err; |
131 | #endif /* ATHHAL_DEBUG */ |
132 | return; |
133 | err: |
134 | printf("%s: sysctl_createv failed (rc = %d)\n" , __func__, rc); |
135 | } |
136 | |
137 | MALLOC_DEFINE(M_ATH_HAL, "ath_hal" , "ath hal data" ); |
138 | |
139 | void* |
140 | ath_hal_malloc(size_t size) |
141 | { |
142 | return malloc(size, M_ATH_HAL, M_NOWAIT | M_ZERO); |
143 | } |
144 | |
145 | void |
146 | ath_hal_free(void* p) |
147 | { |
148 | free(p, M_ATH_HAL); |
149 | } |
150 | |
151 | void |
152 | ath_hal_vprintf(struct ath_hal *ah, const char* fmt, va_list ap) |
153 | { |
154 | vprintf(fmt, ap); |
155 | } |
156 | |
157 | void |
158 | ath_hal_printf(struct ath_hal *ah, const char* fmt, ...) |
159 | { |
160 | va_list ap; |
161 | va_start(ap, fmt); |
162 | ath_hal_vprintf(ah, fmt, ap); |
163 | va_end(ap); |
164 | } |
165 | |
166 | const char* |
167 | ath_hal_ether_sprintf(const u_int8_t *mac) |
168 | { |
169 | return ether_sprintf(mac); |
170 | } |
171 | |
172 | #ifdef ATHHAL_DEBUG |
173 | void |
174 | HALDEBUG(struct ath_hal *ah, u_int mask, const char* fmt, ...) |
175 | { |
176 | if (ath_hal_debug & mask) { |
177 | va_list ap; |
178 | va_start(ap, fmt); |
179 | ath_hal_vprintf(ah, fmt, ap); |
180 | va_end(ap); |
181 | } |
182 | } |
183 | #endif /* ATHHAL_DEBUG */ |
184 | |
185 | #ifdef ATHHAL_DEBUG_ALQ |
186 | /* |
187 | * ALQ register tracing support. |
188 | * |
189 | * Setting hw.ath.hal.alq=1 enables tracing of all register reads and |
190 | * writes to the file /tmp/ath_hal.log. The file format is a simple |
191 | * fixed-size array of records. When done logging set hw.ath.hal.alq=0 |
192 | * and then decode the file with the arcode program (that is part of the |
193 | * HAL). If you start+stop tracing the data will be appended to an |
194 | * existing file. |
195 | * |
196 | * NB: doesn't handle multiple devices properly; only one DEVICE record |
197 | * is emitted and the different devices are not identified. |
198 | */ |
199 | #include <sys/alq.h> |
200 | #include <sys/pcpu.h> |
201 | |
202 | static struct alq *ath_hal_alq; |
203 | static int ath_hal_alq_emitdev; /* need to emit DEVICE record */ |
204 | static u_int ath_hal_alq_lost; /* count of lost records */ |
205 | static const char *ath_hal_logfile = "/tmp/ath_hal.log" ; |
206 | static u_int ath_hal_alq_qsize = 64*1024; |
207 | |
208 | static int |
209 | ath_hal_setlogging(int enable) |
210 | { |
211 | int error; |
212 | |
213 | if (enable) { |
214 | error = kauth_authorize_network(curlwp->l_cred, |
215 | KAUTH_NETWORK_INTERFACE, |
216 | KAUTH_REQ_NETWORK_INTERFACE_SETPRIV, NULL, NULL, NULL); |
217 | if (error == 0) { |
218 | error = alq_open(&ath_hal_alq, ath_hal_logfile, |
219 | curproc->p_ucred, |
220 | sizeof (struct athregrec), ath_hal_alq_qsize); |
221 | ath_hal_alq_lost = 0; |
222 | ath_hal_alq_emitdev = 1; |
223 | printf("ath_hal: logging to %s enabled\n" , |
224 | ath_hal_logfile); |
225 | } |
226 | } else { |
227 | if (ath_hal_alq) |
228 | alq_close(ath_hal_alq); |
229 | ath_hal_alq = NULL; |
230 | printf("ath_hal: logging disabled\n" ); |
231 | error = 0; |
232 | } |
233 | return (error); |
234 | } |
235 | |
236 | static int |
237 | sysctl_hw_ath_hal_log(SYSCTL_HANDLER_ARGS) |
238 | { |
239 | int error, enable; |
240 | |
241 | enable = (ath_hal_alq != NULL); |
242 | error = sysctl_handle_int(oidp, &enable, 0, req); |
243 | if (error || !req->newptr) |
244 | return (error); |
245 | else |
246 | return (ath_hal_setlogging(enable)); |
247 | } |
248 | SYSCTL_PROC(_hw_ath_hal, OID_AUTO, alq, CTLTYPE_INT|CTLFLAG_RW, |
249 | 0, 0, sysctl_hw_ath_hal_log, "I" , "Enable HAL register logging" ); |
250 | SYSCTL_INT(_hw_ath_hal, OID_AUTO, alq_size, CTLFLAG_RW, |
251 | &ath_hal_alq_qsize, 0, "In-memory log size (#records)" ); |
252 | SYSCTL_INT(_hw_ath_hal, OID_AUTO, alq_lost, CTLFLAG_RW, |
253 | &ath_hal_alq_lost, 0, "Register operations not logged" ); |
254 | |
255 | static struct ale * |
256 | ath_hal_alq_get(struct ath_hal *ah) |
257 | { |
258 | struct ale *ale; |
259 | |
260 | if (ath_hal_alq_emitdev) { |
261 | ale = alq_get(ath_hal_alq, ALQ_NOWAIT); |
262 | if (ale) { |
263 | struct athregrec *r = |
264 | (struct athregrec *) ale->ae_data; |
265 | r->op = OP_DEVICE; |
266 | r->reg = 0; |
267 | r->val = ah->ah_devid; |
268 | alq_post(ath_hal_alq, ale); |
269 | ath_hal_alq_emitdev = 0; |
270 | } else |
271 | ath_hal_alq_lost++; |
272 | } |
273 | ale = alq_get(ath_hal_alq, ALQ_NOWAIT); |
274 | if (!ale) |
275 | ath_hal_alq_lost++; |
276 | return ale; |
277 | } |
278 | |
279 | void |
280 | ath_hal_reg_write(struct ath_hal *ah, u_int32_t reg, u_int32_t val) |
281 | { |
282 | bus_space_tag_t t = BUSTAG(ah); |
283 | |
284 | if (ath_hal_alq) { |
285 | struct ale *ale = ath_hal_alq_get(ah); |
286 | if (ale) { |
287 | struct athregrec *r = (struct athregrec *) ale->ae_data; |
288 | r->op = OP_WRITE; |
289 | r->reg = reg; |
290 | r->val = val; |
291 | alq_post(ath_hal_alq, ale); |
292 | } |
293 | } |
294 | #if _BYTE_ORDER == _BIG_ENDIAN |
295 | if (reg >= 0x4000 && reg < 0x5000) |
296 | bus_space_write_4(t, h, reg, val); |
297 | else |
298 | #endif |
299 | bus_space_write_stream_4(t, h, reg, val); |
300 | } |
301 | |
302 | u_int32_t |
303 | ath_hal_reg_read(struct ath_hal *ah, u_int32_t reg) |
304 | { |
305 | u_int32_t val; |
306 | bus_space_handle_t h = BUSHANDLE(ah); |
307 | bus_space_tag_t t = BUSTAG(ah); |
308 | |
309 | #if _BYTE_ORDER == _BIG_ENDIAN |
310 | if (reg >= 0x4000 && reg < 0x5000) |
311 | val = bus_space_read_4(t, h, reg); |
312 | else |
313 | #endif |
314 | val = bus_space_read_stream_4(t, h, reg); |
315 | |
316 | if (ath_hal_alq) { |
317 | struct ale *ale = ath_hal_alq_get(ah); |
318 | if (ale) { |
319 | struct athregrec *r = (struct athregrec *) ale->ae_data; |
320 | r->op = OP_READ; |
321 | r->reg = reg; |
322 | r->val = val; |
323 | alq_post(ath_hal_alq, ale); |
324 | } |
325 | } |
326 | |
327 | return val; |
328 | } |
329 | |
330 | void |
331 | OS_MARK(struct ath_hal *ah, u_int id, u_int32_t v) |
332 | { |
333 | if (ath_hal_alq) { |
334 | struct ale *ale = ath_hal_alq_get(ah); |
335 | |
336 | if (ale) { |
337 | struct athregrec *r = (struct athregrec *) ale->ae_data; |
338 | r->op = OP_MARK; |
339 | r->reg = id; |
340 | r->val = v; |
341 | alq_post(ath_hal_alq, ale); |
342 | } |
343 | } |
344 | } |
345 | #elif defined(ATHHAL_DEBUG) || defined(AH_REGOPS_FUNC) |
346 | /* |
347 | * Memory-mapped device register read/write. These are here |
348 | * as routines when debugging support is enabled and/or when |
349 | * explicitly configured to use function calls. The latter is |
350 | * for architectures that might need to do something before |
351 | * referencing memory (e.g. remap an i/o window). |
352 | * |
353 | * NB: see the comments in ah_osdep.h about byte-swapping register |
354 | * reads and writes to understand what's going on below. |
355 | */ |
356 | |
357 | void |
358 | ath_hal_reg_write(struct ath_hal *ah, u_int32_t reg, u_int32_t val) |
359 | { |
360 | bus_space_handle_t h = BUSHANDLE(ah); |
361 | bus_space_tag_t t = BUSTAG(ah); |
362 | |
363 | #if _BYTE_ORDER == _BIG_ENDIAN |
364 | if (reg >= 0x4000 && reg < 0x5000) |
365 | bus_space_write_4(t, h, reg, val); |
366 | else |
367 | #endif |
368 | bus_space_write_stream_4(t, h, reg, val); |
369 | } |
370 | |
371 | u_int32_t |
372 | ath_hal_reg_read(struct ath_hal *ah, u_int32_t reg) |
373 | { |
374 | bus_space_handle_t h = BUSHANDLE(ah); |
375 | bus_space_tag_t t = BUSTAG(ah); |
376 | uint32_t ret; |
377 | |
378 | #if _BYTE_ORDER == _BIG_ENDIAN |
379 | if (reg >= 0x4000 && reg < 0x5000) |
380 | ret = bus_space_read_4(t, h, reg); |
381 | else |
382 | #endif |
383 | ret = bus_space_read_stream_4(t, h, reg); |
384 | |
385 | return ret; |
386 | } |
387 | #endif /* ATHHAL_DEBUG || AH_REGOPS_FUNC */ |
388 | |
389 | #ifdef ATHHAL_ASSERT |
390 | void |
391 | ath_hal_assert_failed(const char* filename, int lineno, const char *msg) |
392 | { |
393 | printf("Atheros HAL assertion failure: %s: line %u: %s\n" , |
394 | filename, lineno, msg); |
395 | panic("ath_hal_assert" ); |
396 | } |
397 | #endif /* ATHHAL_ASSERT */ |
398 | |
399 | /* |
400 | * Delay n microseconds. |
401 | */ |
402 | void |
403 | ath_hal_delay(int n) |
404 | { |
405 | DELAY(n); |
406 | } |
407 | |
408 | u_int32_t |
409 | ath_hal_getuptime(struct ath_hal *ah) |
410 | { |
411 | struct bintime bt; |
412 | uint32_t ret; |
413 | getbinuptime(&bt); |
414 | ret = (bt.sec * 1000) + |
415 | (((uint64_t)1000 * (uint32_t)(bt.frac >> 32)) >> 32); |
416 | return ret; |
417 | } |
418 | |
419 | void |
420 | ath_hal_memzero(void *dst, size_t n) |
421 | { |
422 | (void)memset(dst, 0, n); |
423 | } |
424 | |
425 | void * |
426 | ath_hal_memcpy(void *dst, const void *src, size_t n) |
427 | { |
428 | return memcpy(dst, src, n); |
429 | } |
430 | |
431 | MODULE(MODULE_CLASS_MISC, ath_hal, NULL); |
432 | |
433 | static int |
434 | ath_hal_modcmd(modcmd_t cmd, void *opaque) |
435 | { |
436 | switch (cmd) { |
437 | case MODULE_CMD_INIT: |
438 | case MODULE_CMD_FINI: |
439 | return 0; |
440 | default: |
441 | return ENOTTY; |
442 | } |
443 | } |
444 | |