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