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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 501 uow_reset(struct uow_softc *sc) 502 { 503 return (uow_ctlcmd(sc, DS2490_CTL_RESET_DEVICE, 0)); 504 } 505