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