xref: /openbsd/sys/dev/usb/uow.c (revision 404b540a)
1 /*	$OpenBSD: uow.c,v 1.26 2009/10/13 19:33:19 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  * Maxim/Dallas DS2490 USB 1-Wire adapter 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/proc.h>
28 
29 #include <dev/onewire/onewirereg.h>
30 #include <dev/onewire/onewirevar.h>
31 
32 #include <dev/usb/usb.h>
33 #include <dev/usb/usbdevs.h>
34 #include <dev/usb/usbdi.h>
35 #include <dev/usb/usbdi_util.h>
36 
37 #include <dev/usb/uowreg.h>
38 
39 #ifdef UOW_DEBUG
40 #define DPRINTF(x) printf x
41 #else
42 #define DPRINTF(x)
43 #endif
44 
45 #define UOW_TIMEOUT	1000	/* ms */
46 
47 struct uow_softc {
48 	struct device		 sc_dev;
49 
50 	struct onewire_bus	 sc_ow_bus;
51 	struct device		*sc_ow_dev;
52 
53 	usbd_device_handle	 sc_udev;
54 	usbd_interface_handle	 sc_iface;
55 	usbd_pipe_handle	 sc_ph_ibulk;
56 	usbd_pipe_handle	 sc_ph_obulk;
57 	usbd_pipe_handle	 sc_ph_intr;
58 	u_int8_t		 sc_regs[DS2490_NREGS];
59 	usbd_xfer_handle	 sc_xfer;
60 	u_int8_t		 sc_fifo[DS2490_DATAFIFOSIZE];
61 };
62 
63 int uow_match(struct device *, void *, void *);
64 void uow_attach(struct device *, struct device *, void *);
65 int uow_detach(struct device *, int);
66 int uow_activate(struct device *, int);
67 
68 struct cfdriver uow_cd = {
69 	NULL, "uow", DV_DULL
70 };
71 
72 const struct cfattach uow_ca = {
73 	sizeof(struct uow_softc),
74 	uow_match,
75 	uow_attach,
76 	uow_detach,
77 	uow_activate,
78 };
79 
80 /* List of supported devices */
81 static const struct usb_devno uow_devs[] = {
82 	{ USB_VENDOR_DALLAS,		USB_PRODUCT_DALLAS_USB_FOB_IBUTTON }
83 };
84 
85 int	uow_ow_reset(void *);
86 int	uow_ow_bit(void *, int);
87 int	uow_ow_read_byte(void *);
88 void	uow_ow_write_byte(void *, int);
89 void	uow_ow_read_block(void *, void *, int);
90 void	uow_ow_write_block(void *, const void *, int);
91 void	uow_ow_matchrom(void *, u_int64_t);
92 int	uow_ow_search(void *, u_int64_t *, int, u_int64_t);
93 
94 int	uow_cmd(struct uow_softc *, int, int, int);
95 #define uow_ctlcmd(s, c, p)	uow_cmd((s), DS2490_CONTROL_CMD, (c), (p))
96 #define uow_commcmd(s, c, p)	uow_cmd((s), DS2490_COMM_CMD, (c), (p))
97 #define uow_modecmd(s, c, p)	uow_cmd((s), DS2490_MODE_CMD, (c), (p))
98 
99 void	uow_intr(usbd_xfer_handle, usbd_private_handle, usbd_status);
100 int	uow_read(struct uow_softc *, void *, int);
101 int	uow_write(struct uow_softc *, const void *, int);
102 int	uow_reset(struct uow_softc *);
103 
104 int
105 uow_match(struct device *parent, void *match, void *aux)
106 {
107 	struct usb_attach_arg *uaa = aux;
108 
109 	if (uaa->iface != NULL)
110 		return (UMATCH_NONE);
111 
112 	return ((usb_lookup(uow_devs, uaa->vendor, uaa->product) != NULL) ?
113 	    UMATCH_VENDOR_PRODUCT : UMATCH_NONE);
114 }
115 
116 void
117 uow_attach(struct device *parent, struct device *self, void *aux)
118 {
119 	struct uow_softc *sc = (struct uow_softc *)self;
120 	struct usb_attach_arg *uaa = aux;
121 	usb_interface_descriptor_t *id;
122 	usb_endpoint_descriptor_t *ed;
123 	int ep_ibulk = -1, ep_obulk = -1, ep_intr = -1;
124 	struct onewirebus_attach_args oba;
125 	usbd_status error;
126 	int i;
127 
128 	sc->sc_udev = uaa->device;
129 
130 	/* Set USB configuration */
131 	if ((error = usbd_set_config_no(sc->sc_udev,
132 	    DS2490_USB_CONFIG, 0)) != 0) {
133 		printf("%s: failed to set config %d: %s\n",
134 		    sc->sc_dev.dv_xname, DS2490_USB_CONFIG,
135 		    usbd_errstr(error));
136 		return;
137 	}
138 
139 	/* Get interface handle */
140 	if ((error = usbd_device2interface_handle(sc->sc_udev,
141 	    DS2490_USB_IFACE, &sc->sc_iface)) != 0) {
142 		printf("%s: failed to get iface %d: %s\n",
143 		    sc->sc_dev.dv_xname, DS2490_USB_IFACE,
144 		    usbd_errstr(error));
145 		return;
146 	}
147 
148 	/* Find endpoints */
149 	id = usbd_get_interface_descriptor(sc->sc_iface);
150 	for (i = 0; i < id->bNumEndpoints; i++) {
151 		ed = usbd_interface2endpoint_descriptor(sc->sc_iface, i);
152 		if (ed == NULL) {
153 			printf("%s: failed to get endpoint %d descriptor\n",
154 			    sc->sc_dev.dv_xname, i);
155 			return;
156 		}
157 
158 		if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
159 		    UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK)
160 			ep_ibulk = ed->bEndpointAddress;
161 		if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT &&
162 		    UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK)
163 			ep_obulk = ed->bEndpointAddress;
164 		if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
165 		    UE_GET_XFERTYPE(ed->bmAttributes) == UE_INTERRUPT)
166 			ep_intr = ed->bEndpointAddress;
167 	}
168 	if (ep_ibulk == -1 || ep_obulk == -1 || ep_intr == -1) {
169 		printf("%s: missing endpoint: ibulk %d, obulk %d, intr %d\n",
170 		   sc->sc_dev.dv_xname, ep_ibulk, ep_obulk, ep_intr);
171 		return;
172 	}
173 
174 	/* Open pipes */
175 	if ((error = usbd_open_pipe(sc->sc_iface, ep_ibulk, USBD_EXCLUSIVE_USE,
176 	    &sc->sc_ph_ibulk)) != 0) {
177 		printf("%s: failed to open bulk-in pipe: %s\n",
178 		    sc->sc_dev.dv_xname, usbd_errstr(error));
179 		return;
180 	}
181 	if ((error = usbd_open_pipe(sc->sc_iface, ep_obulk, USBD_EXCLUSIVE_USE,
182 	    &sc->sc_ph_obulk)) != 0) {
183 		printf("%s: failed to open bulk-out pipe: %s\n",
184 		    sc->sc_dev.dv_xname, usbd_errstr(error));
185 		goto fail;
186 	}
187 	if ((error = usbd_open_pipe_intr(sc->sc_iface, ep_intr,
188 	    USBD_SHORT_XFER_OK, &sc->sc_ph_intr, sc,
189 	    sc->sc_regs, sizeof(sc->sc_regs), uow_intr,
190 	    USBD_DEFAULT_INTERVAL)) != 0) {
191 		printf("%s: failed to open intr pipe: %s\n",
192 		    sc->sc_dev.dv_xname, usbd_errstr(error));
193 		goto fail;
194 	}
195 
196 #if 0
197 	/* Allocate xfer for bulk transfers */
198 	if ((sc->sc_xfer = usbd_alloc_xfer(sc->sc_udev)) == NULL) {
199 		printf("%s: failed to alloc bulk xfer\n",
200 		    sc->sc_dev.dv_xname);
201 		goto fail;
202 	}
203 #endif
204 
205 	memset(sc->sc_fifo, 0xff, sizeof(sc->sc_fifo));
206 
207 	/* Reset device */
208 	uow_reset(sc);
209 
210 	/* Attach 1-Wire bus */
211 	sc->sc_ow_bus.bus_cookie = sc;
212 	sc->sc_ow_bus.bus_reset = uow_ow_reset;
213 	sc->sc_ow_bus.bus_bit = uow_ow_bit;
214 	sc->sc_ow_bus.bus_read_byte = uow_ow_read_byte;
215 	sc->sc_ow_bus.bus_write_byte = uow_ow_write_byte;
216 	sc->sc_ow_bus.bus_read_block = uow_ow_read_block;
217 	sc->sc_ow_bus.bus_write_block = uow_ow_write_block;
218 	sc->sc_ow_bus.bus_matchrom = uow_ow_matchrom;
219 #if 0
220 	sc->sc_ow_bus.bus_search = uow_ow_search;
221 #endif
222 
223 	bzero(&oba, sizeof(oba));
224 	oba.oba_bus = &sc->sc_ow_bus;
225 	sc->sc_ow_dev = config_found(self, &oba, onewirebus_print);
226 
227 	return;
228 
229 fail:
230 	if (sc->sc_ph_ibulk != NULL)
231 		usbd_close_pipe(sc->sc_ph_ibulk);
232 	if (sc->sc_ph_obulk != NULL)
233 		usbd_close_pipe(sc->sc_ph_obulk);
234 	if (sc->sc_ph_intr != NULL)
235 		usbd_close_pipe(sc->sc_ph_intr);
236 	if (sc->sc_xfer != NULL)
237 		usbd_free_xfer(sc->sc_xfer);
238 }
239 
240 int
241 uow_detach(struct device *self, int flags)
242 {
243 	struct uow_softc *sc = (struct uow_softc *)self;
244 	int rv = 0, s;
245 
246 	s = splusb();
247 
248 	if (sc->sc_ph_ibulk != NULL) {
249 		usbd_abort_pipe(sc->sc_ph_ibulk);
250 		usbd_close_pipe(sc->sc_ph_ibulk);
251 	}
252 	if (sc->sc_ph_obulk != NULL) {
253 		usbd_abort_pipe(sc->sc_ph_obulk);
254 		usbd_close_pipe(sc->sc_ph_obulk);
255 	}
256 	if (sc->sc_ph_intr != NULL) {
257 		usbd_abort_pipe(sc->sc_ph_intr);
258 		usbd_close_pipe(sc->sc_ph_intr);
259 	}
260 
261 	if (sc->sc_xfer != NULL)
262 		usbd_free_xfer(sc->sc_xfer);
263 
264 	if (sc->sc_ow_dev != NULL)
265 		rv = config_detach(sc->sc_ow_dev, flags);
266 
267 	splx(s);
268 
269 	usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev,
270 	    &sc->sc_dev);
271 
272 	return (rv);
273 }
274 
275 int
276 uow_activate(struct device *self, int act)
277 {
278 	struct uow_softc *sc = (struct uow_softc *)self;
279 	int rv = 0;
280 
281 	switch (act) {
282 	case DVACT_ACTIVATE:
283 		break;
284 	case DVACT_DEACTIVATE:
285 		if (sc->sc_ow_dev != NULL)
286 			rv = config_deactivate(sc->sc_ow_dev);
287 		break;
288 	}
289 
290 	return (rv);
291 }
292 
293 int
294 uow_ow_reset(void *arg)
295 {
296 	struct uow_softc *sc = arg;
297 
298 	if (uow_commcmd(sc, DS2490_COMM_1WIRE_RESET | DS2490_BIT_IM, 0) != 0)
299 		return (1);
300 
301 	/* XXX: check presence pulse */
302 	return (0);
303 }
304 
305 int
306 uow_ow_bit(void *arg, int value)
307 {
308 	struct uow_softc *sc = arg;
309 	u_int8_t data;
310 
311 	if (uow_commcmd(sc, DS2490_COMM_BIT_IO | DS2490_BIT_IM |
312 	    (value ? DS2490_BIT_D : 0), 0) != 0)
313 		return (1);
314 	if (uow_read(sc, &data, 1) != 1)
315 		return (1);
316 
317 	return (data);
318 }
319 
320 int
321 uow_ow_read_byte(void *arg)
322 {
323 	struct uow_softc *sc = arg;
324 	u_int8_t data;
325 
326 	if (uow_commcmd(sc, DS2490_COMM_BYTE_IO | DS2490_BIT_IM, 0xff) != 0)
327 		return (-1);
328 	if (uow_read(sc, &data, 1) != 1)
329 		return (-1);
330 
331 	return (data);
332 }
333 
334 void
335 uow_ow_write_byte(void *arg, int value)
336 {
337 	struct uow_softc *sc = arg;
338 	u_int8_t data;
339 
340 	if (uow_commcmd(sc, DS2490_COMM_BYTE_IO | DS2490_BIT_IM, value) != 0)
341 		return;
342 	uow_read(sc, &data, sizeof(data));
343 }
344 
345 void
346 uow_ow_read_block(void *arg, void *buf, int len)
347 {
348 	struct uow_softc *sc = arg;
349 
350 	if (uow_write(sc, sc->sc_fifo, len) != 0)
351 		return;
352 	if (uow_commcmd(sc, DS2490_COMM_BLOCK_IO | DS2490_BIT_IM, len) != 0)
353 		return;
354 	uow_read(sc, buf, len);
355 }
356 
357 void
358 uow_ow_write_block(void *arg, const void *buf, int len)
359 {
360 	struct uow_softc *sc = arg;
361 
362 	if (uow_write(sc, buf, len) != 0)
363 		return;
364 	if (uow_commcmd(sc, DS2490_COMM_BLOCK_IO | DS2490_BIT_IM, len) != 0)
365 		return;
366 }
367 
368 void
369 uow_ow_matchrom(void *arg, u_int64_t rom)
370 {
371 	struct uow_softc *sc = arg;
372 	u_int8_t data[8];
373 	int i;
374 
375 	for (i = 0; i < 8; i++)
376 		data[i] = (rom >> (i * 8)) & 0xff;
377 
378 	if (uow_write(sc, data, 8) != 0)
379 		return;
380 	if (uow_commcmd(sc, DS2490_COMM_MATCH_ACCESS | DS2490_BIT_IM,
381 	    ONEWIRE_CMD_MATCH_ROM) != 0)
382 		return;
383 }
384 
385 int
386 uow_ow_search(void *arg, u_int64_t *buf, int size, u_int64_t startrom)
387 {
388 	struct uow_softc *sc = arg;
389 	u_int8_t data[8];
390 	int i, rv;
391 
392 	for (i = 0; i < 8; i++)
393 		data[i] = (startrom >> (i * 8)) & 0xff;
394 
395 	if (uow_write(sc, data, 8) != 0)
396 		return (-1);
397 	if (uow_commcmd(sc, DS2490_COMM_SEARCH_ACCESS | DS2490_BIT_IM |
398 	    DS2490_BIT_SM | DS2490_BIT_RST | DS2490_BIT_F, size << 8 |
399 	    ONEWIRE_CMD_SEARCH_ROM) != 0)
400 		return (-1);
401 
402 	if ((rv = uow_read(sc, buf, size * 8)) == -1)
403 		return (-1);
404 
405 	return (rv / 8);
406 }
407 
408 int
409 uow_cmd(struct uow_softc *sc, int type, int cmd, int param)
410 {
411 	usb_device_request_t req;
412 	usbd_status error;
413 
414 	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
415 	req.bRequest = type;
416 	USETW(req.wValue, cmd);
417 	USETW(req.wIndex, param);
418 	USETW(req.wLength, 0);
419 	if ((error = usbd_do_request(sc->sc_udev, &req, NULL)) != 0) {
420 		printf("%s: cmd failed, type 0x%02x, cmd 0x%04x, "
421 		    "param 0x%04x: %s\n", sc->sc_dev.dv_xname, type, cmd,
422 		    param, usbd_errstr(error));
423 		if (cmd != DS2490_CTL_RESET_DEVICE)
424 			uow_reset(sc);
425 		return (1);
426 	}
427 
428 again:
429 	if (tsleep(sc->sc_regs, PRIBIO, "uowcmd",
430 	    (UOW_TIMEOUT * hz) / 1000) != 0) {
431 		printf("%s: cmd timeout, type 0x%02x, cmd 0x%04x, "
432 		    "param 0x%04x\n", sc->sc_dev.dv_xname, type, cmd,
433 		    param);
434 		return (1);
435 	}
436 	if ((sc->sc_regs[DS2490_ST_STFL] & DS2490_ST_STFL_IDLE) == 0)
437 		goto again;
438 
439 	return (0);
440 }
441 
442 void
443 uow_intr(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
444 {
445 	struct uow_softc *sc = priv;
446 
447 	if (status != USBD_NORMAL_COMPLETION) {
448 		if (status == USBD_NOT_STARTED || status == USBD_CANCELLED)
449 			return;
450 		if (status == USBD_STALLED)
451 			usbd_clear_endpoint_stall_async(sc->sc_ph_intr);
452 		return;
453 	}
454 
455 	wakeup(sc->sc_regs);
456 }
457 
458 int
459 uow_read(struct uow_softc *sc, void *buf, int len)
460 {
461 	usbd_status error;
462 	int count;
463 
464 	/* XXX: implement FIFO status monitoring */
465 	if (len > DS2490_DATAFIFOSIZE) {
466 		printf("%s: read %d bytes, xfer too big\n",
467 		    sc->sc_dev.dv_xname, len);
468 		return (-1);
469 	}
470 
471 	if ((sc->sc_xfer = usbd_alloc_xfer(sc->sc_udev)) == NULL) {
472 		printf("%s: failed to alloc xfer\n", sc->sc_dev.dv_xname);
473 		return (-1);
474 	}
475 	usbd_setup_xfer(sc->sc_xfer, sc->sc_ph_ibulk, sc, buf, len,
476 	    USBD_SHORT_XFER_OK, UOW_TIMEOUT, NULL);
477 	error = usbd_sync_transfer(sc->sc_xfer);
478 	usbd_free_xfer(sc->sc_xfer);
479 	if (error != 0) {
480 		printf("%s: read failed, len %d: %s\n",
481 		    sc->sc_dev.dv_xname, len, usbd_errstr(error));
482 		uow_reset(sc);
483 		return (-1);
484 	}
485 
486 	usbd_get_xfer_status(sc->sc_xfer, NULL, NULL, &count, &error);
487 	return (count);
488 }
489 
490 int
491 uow_write(struct uow_softc *sc, const void *buf, int len)
492 {
493 	usbd_status error;
494 
495 	/* XXX: implement FIFO status monitoring */
496 	if (len > DS2490_DATAFIFOSIZE) {
497 		printf("%s: write %d bytes, xfer too big\n",
498 		    sc->sc_dev.dv_xname, len);
499 		return (1);
500 	}
501 
502 	if ((sc->sc_xfer = usbd_alloc_xfer(sc->sc_udev)) == NULL) {
503 		printf("%s: failed to alloc xfer\n", sc->sc_dev.dv_xname);
504 		return (-1);
505 	}
506 	usbd_setup_xfer(sc->sc_xfer, sc->sc_ph_obulk, sc, (void *)buf, len, 0,
507 	    UOW_TIMEOUT, NULL);
508 	error = usbd_sync_transfer(sc->sc_xfer);
509 	usbd_free_xfer(sc->sc_xfer);
510 	if (error != 0) {
511 		printf("%s: write failed, len %d: %s\n",
512 		    sc->sc_dev.dv_xname, len, usbd_errstr(error));
513 		uow_reset(sc);
514 		return (1);
515 	}
516 
517 	return (0);
518 }
519 
520 int
521 uow_reset(struct uow_softc *sc)
522 {
523 	return (uow_ctlcmd(sc, DS2490_CTL_RESET_DEVICE, 0));
524 }
525