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