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