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