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