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