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