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