1 | /* $NetBSD: onewire.c,v 1.16 2014/07/25 08:10:38 dholland Exp $ */ |
2 | /* $OpenBSD: onewire.c,v 1.1 2006/03/04 16:27:03 grange Exp $ */ |
3 | |
4 | /* |
5 | * Copyright (c) 2006 Alexander Yurchenko <grange@openbsd.org> |
6 | * |
7 | * Permission to use, copy, modify, and distribute this software for any |
8 | * purpose with or without fee is hereby granted, provided that the above |
9 | * copyright notice and this permission notice appear in all copies. |
10 | * |
11 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
12 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
13 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
14 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
15 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
16 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
17 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
18 | */ |
19 | |
20 | #include <sys/cdefs.h> |
21 | __KERNEL_RCSID(0, "$NetBSD: onewire.c,v 1.16 2014/07/25 08:10:38 dholland Exp $" ); |
22 | |
23 | /* |
24 | * 1-Wire bus driver. |
25 | */ |
26 | |
27 | #include <sys/param.h> |
28 | #include <sys/systm.h> |
29 | #include <sys/conf.h> |
30 | #include <sys/device.h> |
31 | #include <sys/kernel.h> |
32 | #include <sys/kthread.h> |
33 | #include <sys/rwlock.h> |
34 | #include <sys/malloc.h> |
35 | #include <sys/proc.h> |
36 | #include <sys/queue.h> |
37 | #include <sys/module.h> |
38 | |
39 | #include <dev/onewire/onewirereg.h> |
40 | #include <dev/onewire/onewirevar.h> |
41 | |
42 | #ifdef ONEWIRE_DEBUG |
43 | #define DPRINTF(x) printf x |
44 | #else |
45 | #define DPRINTF(x) |
46 | #endif |
47 | |
48 | //#define ONEWIRE_MAXDEVS 256 |
49 | #define ONEWIRE_MAXDEVS 8 |
50 | #define ONEWIRE_SCANTIME 3 |
51 | |
52 | struct onewire_softc { |
53 | device_t sc_dev; |
54 | |
55 | struct onewire_bus * sc_bus; |
56 | krwlock_t sc_rwlock; |
57 | struct lwp * sc_thread; |
58 | TAILQ_HEAD(, onewire_device) sc_devs; |
59 | |
60 | int sc_dying; |
61 | }; |
62 | |
63 | struct onewire_device { |
64 | TAILQ_ENTRY(onewire_device) d_list; |
65 | device_t d_dev; |
66 | u_int64_t d_rom; |
67 | int d_present; |
68 | }; |
69 | |
70 | static int onewire_match(device_t, cfdata_t, void *); |
71 | static void onewire_attach(device_t, device_t, void *); |
72 | static int onewire_detach(device_t, int); |
73 | static int onewire_activate(device_t, enum devact); |
74 | int onewire_print(void *, const char *); |
75 | |
76 | static void onewire_thread(void *); |
77 | static void onewire_scan(struct onewire_softc *); |
78 | |
79 | CFATTACH_DECL_NEW(onewire, sizeof(struct onewire_softc), |
80 | onewire_match, onewire_attach, onewire_detach, onewire_activate); |
81 | |
82 | const struct cdevsw onewire_cdevsw = { |
83 | .d_open = noopen, |
84 | .d_close = noclose, |
85 | .d_read = noread, |
86 | .d_write = nowrite, |
87 | .d_ioctl = noioctl, |
88 | .d_stop = nostop, |
89 | .d_tty = notty, |
90 | .d_poll = nopoll, |
91 | .d_mmap = nommap, |
92 | .d_kqfilter = nokqfilter, |
93 | .d_discard = nodiscard, |
94 | .d_flag = D_OTHER |
95 | }; |
96 | |
97 | extern struct cfdriver onewire_cd; |
98 | |
99 | static int |
100 | onewire_match(device_t parent, cfdata_t cf, void *aux) |
101 | { |
102 | return 1; |
103 | } |
104 | |
105 | static void |
106 | onewire_attach(device_t parent, device_t self, void *aux) |
107 | { |
108 | struct onewire_softc *sc = device_private(self); |
109 | struct onewirebus_attach_args *oba = aux; |
110 | |
111 | sc->sc_dev = self; |
112 | sc->sc_bus = oba->oba_bus; |
113 | rw_init(&sc->sc_rwlock); |
114 | TAILQ_INIT(&sc->sc_devs); |
115 | |
116 | aprint_normal("\n" ); |
117 | |
118 | if (kthread_create(PRI_NONE, 0, NULL, onewire_thread, sc, |
119 | &sc->sc_thread, "%s" , device_xname(self)) != 0) |
120 | aprint_error_dev(self, "can't create kernel thread\n" ); |
121 | } |
122 | |
123 | static int |
124 | onewire_detach(device_t self, int flags) |
125 | { |
126 | struct onewire_softc *sc = device_private(self); |
127 | int rv; |
128 | |
129 | sc->sc_dying = 1; |
130 | if (sc->sc_thread != NULL) { |
131 | wakeup(sc->sc_thread); |
132 | tsleep(&sc->sc_dying, PWAIT, "owdt" , 0); |
133 | } |
134 | |
135 | onewire_lock(sc); |
136 | //rv = config_detach_children(self, flags); |
137 | rv = 0; /* XXX riz */ |
138 | onewire_unlock(sc); |
139 | rw_destroy(&sc->sc_rwlock); |
140 | |
141 | return rv; |
142 | } |
143 | |
144 | static int |
145 | onewire_activate(device_t self, enum devact act) |
146 | { |
147 | struct onewire_softc *sc = device_private(self); |
148 | |
149 | switch (act) { |
150 | case DVACT_DEACTIVATE: |
151 | sc->sc_dying = 1; |
152 | return 0; |
153 | default: |
154 | return EOPNOTSUPP; |
155 | } |
156 | } |
157 | |
158 | int |
159 | onewire_print(void *aux, const char *pnp) |
160 | { |
161 | struct onewire_attach_args *oa = aux; |
162 | const char *famname; |
163 | |
164 | if (pnp == NULL) |
165 | aprint_normal(" " ); |
166 | |
167 | famname = onewire_famname(ONEWIRE_ROM_FAMILY_TYPE(oa->oa_rom)); |
168 | if (famname == NULL) |
169 | aprint_normal("family 0x%02x" , |
170 | (uint)ONEWIRE_ROM_FAMILY_TYPE(oa->oa_rom)); |
171 | else |
172 | aprint_normal("\"%s\"" , famname); |
173 | aprint_normal(" sn %012llx" , ONEWIRE_ROM_SN(oa->oa_rom)); |
174 | |
175 | if (pnp != NULL) |
176 | aprint_normal(" at %s" , pnp); |
177 | |
178 | return UNCONF; |
179 | } |
180 | |
181 | int |
182 | onewirebus_print(void *aux, const char *pnp) |
183 | { |
184 | if (pnp != NULL) |
185 | aprint_normal("onewire at %s" , pnp); |
186 | |
187 | return UNCONF; |
188 | } |
189 | |
190 | void |
191 | onewire_lock(void *arg) |
192 | { |
193 | struct onewire_softc *sc = arg; |
194 | |
195 | rw_enter(&sc->sc_rwlock, RW_WRITER); |
196 | } |
197 | |
198 | void |
199 | onewire_unlock(void *arg) |
200 | { |
201 | struct onewire_softc *sc = arg; |
202 | |
203 | rw_exit(&sc->sc_rwlock); |
204 | } |
205 | |
206 | int |
207 | onewire_reset(void *arg) |
208 | { |
209 | struct onewire_softc *sc = arg; |
210 | struct onewire_bus *bus = sc->sc_bus; |
211 | |
212 | return bus->bus_reset(bus->bus_cookie); |
213 | } |
214 | |
215 | int |
216 | onewire_bit(void *arg, int value) |
217 | { |
218 | struct onewire_softc *sc = arg; |
219 | struct onewire_bus *bus = sc->sc_bus; |
220 | |
221 | return bus->bus_bit(bus->bus_cookie, value); |
222 | } |
223 | |
224 | int |
225 | onewire_read_byte(void *arg) |
226 | { |
227 | struct onewire_softc *sc = arg; |
228 | struct onewire_bus *bus = sc->sc_bus; |
229 | uint8_t value = 0; |
230 | int i; |
231 | |
232 | if (bus->bus_read_byte != NULL) |
233 | return bus->bus_read_byte(bus->bus_cookie); |
234 | |
235 | for (i = 0; i < 8; i++) |
236 | value |= (bus->bus_bit(bus->bus_cookie, 1) << i); |
237 | |
238 | return value; |
239 | } |
240 | |
241 | void |
242 | onewire_write_byte(void *arg, int value) |
243 | { |
244 | struct onewire_softc *sc = arg; |
245 | struct onewire_bus *bus = sc->sc_bus; |
246 | int i; |
247 | |
248 | if (bus->bus_write_byte != NULL) |
249 | return bus->bus_write_byte(bus->bus_cookie, value); |
250 | |
251 | for (i = 0; i < 8; i++) |
252 | bus->bus_bit(bus->bus_cookie, (value >> i) & 0x1); |
253 | } |
254 | |
255 | int |
256 | onewire_triplet(void *arg, int dir) |
257 | { |
258 | struct onewire_softc *sc = arg; |
259 | struct onewire_bus *bus = sc->sc_bus; |
260 | int rv; |
261 | |
262 | if (bus->bus_triplet != NULL) |
263 | return bus->bus_triplet(bus->bus_cookie, dir); |
264 | |
265 | rv = bus->bus_bit(bus->bus_cookie, 1); |
266 | rv <<= 1; |
267 | rv |= bus->bus_bit(bus->bus_cookie, 1); |
268 | |
269 | switch (rv) { |
270 | case 0x0: |
271 | bus->bus_bit(bus->bus_cookie, dir); |
272 | break; |
273 | case 0x1: |
274 | bus->bus_bit(bus->bus_cookie, 0); |
275 | break; |
276 | default: |
277 | bus->bus_bit(bus->bus_cookie, 1); |
278 | } |
279 | |
280 | return rv; |
281 | } |
282 | |
283 | void |
284 | onewire_read_block(void *arg, void *buf, int len) |
285 | { |
286 | uint8_t *p = buf; |
287 | |
288 | while (len--) |
289 | *p++ = onewire_read_byte(arg); |
290 | } |
291 | |
292 | void |
293 | onewire_write_block(void *arg, const void *buf, int len) |
294 | { |
295 | const uint8_t *p = buf; |
296 | |
297 | while (len--) |
298 | onewire_write_byte(arg, *p++); |
299 | } |
300 | |
301 | void |
302 | onewire_matchrom(void *arg, u_int64_t rom) |
303 | { |
304 | int i; |
305 | |
306 | onewire_write_byte(arg, ONEWIRE_CMD_MATCH_ROM); |
307 | for (i = 0; i < 8; i++) |
308 | onewire_write_byte(arg, (rom >> (i * 8)) & 0xff); |
309 | } |
310 | |
311 | static void |
312 | onewire_thread(void *arg) |
313 | { |
314 | struct onewire_softc *sc = arg; |
315 | |
316 | while (!sc->sc_dying) { |
317 | onewire_scan(sc); |
318 | tsleep(sc->sc_thread, PWAIT, "owidle" , ONEWIRE_SCANTIME * hz); |
319 | } |
320 | |
321 | sc->sc_thread = NULL; |
322 | wakeup(&sc->sc_dying); |
323 | kthread_exit(0); |
324 | } |
325 | |
326 | static void |
327 | onewire_scan(struct onewire_softc *sc) |
328 | { |
329 | struct onewire_device *d, *next, *nd; |
330 | struct onewire_attach_args oa; |
331 | device_t dev; |
332 | int search = 1, count = 0, present; |
333 | int dir, rv; |
334 | uint64_t mask, rom = 0, lastrom; |
335 | uint8_t data[8]; |
336 | int i, i0 = -1, lastd = -1; |
337 | |
338 | TAILQ_FOREACH(d, &sc->sc_devs, d_list) |
339 | d->d_present = 0; |
340 | |
341 | while (search && count++ < ONEWIRE_MAXDEVS) { |
342 | /* XXX: yield processor */ |
343 | tsleep(sc, PWAIT, "owscan" , hz / 10); |
344 | |
345 | /* |
346 | * Reset the bus. If there's no presence pulse |
347 | * don't search for any devices. |
348 | */ |
349 | onewire_lock(sc); |
350 | if (onewire_reset(sc) != 0) { |
351 | DPRINTF(("%s: scan: no presence pulse\n" , |
352 | device_xname(sc->sc_dev))); |
353 | onewire_unlock(sc); |
354 | break; |
355 | } |
356 | |
357 | /* |
358 | * Start new search. Go through the previous path to |
359 | * the point we made a decision last time and make an |
360 | * opposite decision. If we didn't make any decision |
361 | * stop searching. |
362 | */ |
363 | search = 0; |
364 | lastrom = rom; |
365 | rom = 0; |
366 | onewire_write_byte(sc, ONEWIRE_CMD_SEARCH_ROM); |
367 | for (i = 0,i0 = -1; i < 64; i++) { |
368 | dir = (lastrom >> i) & 0x1; |
369 | if (i == lastd) |
370 | dir = 1; |
371 | else if (i > lastd) |
372 | dir = 0; |
373 | rv = onewire_triplet(sc, dir); |
374 | switch (rv) { |
375 | case 0x0: |
376 | if (i != lastd) { |
377 | if (dir == 0) |
378 | i0 = i; |
379 | search = 1; |
380 | } |
381 | mask = dir; |
382 | break; |
383 | case 0x1: |
384 | mask = 0; |
385 | break; |
386 | case 0x2: |
387 | mask = 1; |
388 | break; |
389 | default: |
390 | DPRINTF(("%s: scan: triplet error 0x%x, " |
391 | "step %d\n" , |
392 | device_xname(sc->sc_dev), rv, i)); |
393 | onewire_unlock(sc); |
394 | return; |
395 | } |
396 | rom |= (mask << i); |
397 | } |
398 | lastd = i0; |
399 | onewire_unlock(sc); |
400 | |
401 | if (rom == 0) |
402 | continue; |
403 | |
404 | /* |
405 | * The last byte of the ROM code contains a CRC calculated |
406 | * from the first 7 bytes. Re-calculate it to make sure |
407 | * we found a valid device. |
408 | */ |
409 | for (i = 0; i < 8; i++) |
410 | data[i] = (rom >> (i * 8)) & 0xff; |
411 | if (onewire_crc(data, 7) != data[7]) |
412 | continue; |
413 | |
414 | /* |
415 | * Go through the list of attached devices to see if we |
416 | * found a new one. |
417 | */ |
418 | present = 0; |
419 | TAILQ_FOREACH(d, &sc->sc_devs, d_list) { |
420 | if (d->d_rom == rom) { |
421 | d->d_present = 1; |
422 | present = 1; |
423 | break; |
424 | } |
425 | } |
426 | if (!present) { |
427 | memset(&oa, 0, sizeof(oa)); |
428 | oa.oa_onewire = sc; |
429 | oa.oa_rom = rom; |
430 | if ((dev = config_found(sc->sc_dev, &oa, |
431 | onewire_print)) == NULL) |
432 | continue; |
433 | |
434 | nd = malloc(sizeof(struct onewire_device), |
435 | M_DEVBUF, M_NOWAIT); |
436 | if (nd == NULL) |
437 | continue; |
438 | nd->d_dev = dev; |
439 | nd->d_rom = rom; |
440 | nd->d_present = 1; |
441 | TAILQ_INSERT_TAIL(&sc->sc_devs, nd, d_list); |
442 | } |
443 | } |
444 | |
445 | /* Detach disappeared devices */ |
446 | onewire_lock(sc); |
447 | for (d = TAILQ_FIRST(&sc->sc_devs); |
448 | d != NULL; d = next) { |
449 | next = TAILQ_NEXT(d, d_list); |
450 | if (!d->d_present) { |
451 | config_detach(d->d_dev, DETACH_FORCE); |
452 | TAILQ_REMOVE(&sc->sc_devs, d, d_list); |
453 | free(d, M_DEVBUF); |
454 | } |
455 | } |
456 | onewire_unlock(sc); |
457 | } |
458 | |
459 | MODULE(MODULE_CLASS_DRIVER, onewire, NULL); |
460 | |
461 | #ifdef _MODULE |
462 | #include "ioconf.c" |
463 | #endif |
464 | |
465 | static int |
466 | onewire_modcmd(modcmd_t cmd, void *opaque) |
467 | { |
468 | int error; |
469 | |
470 | error = 0; |
471 | switch (cmd) { |
472 | case MODULE_CMD_INIT: |
473 | #ifdef _MODULE |
474 | error = config_init_component(cfdriver_ioconf_onewire, |
475 | cfattach_ioconf_onewire, cfdata_ioconf_onewire); |
476 | if (error) |
477 | aprint_error("%s: unable to init component\n" , |
478 | onewire_cd.cd_name); |
479 | #endif |
480 | break; |
481 | case MODULE_CMD_FINI: |
482 | #ifdef _MODULE |
483 | config_fini_component(cfdriver_ioconf_onewire, |
484 | cfattach_ioconf_onewire, cfdata_ioconf_onewire); |
485 | #endif |
486 | break; |
487 | default: |
488 | error = ENOTTY; |
489 | } |
490 | return error; |
491 | } |
492 | |