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