1 /* $OpenBSD: ukspan.c,v 1.4 2022/04/09 20:07:44 naddy Exp $ */ 2 3 /* 4 * Copyright (c) 2019 Cody Cutler <ccutler@csail.mit.edu> 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 * I don't know of any technical documentation for the Keyspan USA-19HS. I 21 * inspected the Linux driver (drivers/usb/serial/keyspan_usa90msg.h) to learn 22 * the device message format and the procedure for setting the baud rate. 23 */ 24 25 #include <sys/param.h> 26 #include <sys/systm.h> 27 #include <sys/tty.h> 28 29 #include <dev/usb/usb.h> 30 #include <dev/usb/usbdevs.h> 31 #include <dev/usb/usbdi.h> 32 #include <dev/usb/usbdi_util.h> 33 34 #include <dev/usb/ucomvar.h> 35 36 /*#define UKSPAN_DEBUG */ 37 38 #ifdef UKSPAN_DEBUG 39 #define DPRINTF(x...) do { printf(x); } while (0) 40 #else 41 #define DPRINTF(x...) do { ; } while (0) 42 #endif 43 44 #define UKSPAN_PARITY_NONE 0x0 45 #define UKSPAN_PARITY_ODD 0x08 46 #define UKSPAN_PARITY_EVEN 0x18 47 48 #define UKSPAN_DATA_5 0x0 49 #define UKSPAN_DATA_6 0x1 50 #define UKSPAN_DATA_7 0x2 51 #define UKSPAN_DATA_8 0x3 52 53 #define UKSPAN_STOP_1 0x0 54 #define UKSPAN_STOP_2 0x4 55 56 #define UKSPAN_MAGIC 0x2 57 58 #define UKSPAN_CLOCK 14769231 59 60 /* 61 * The following USB indexes and endpoint addresses may be specific to the 62 * Keyspan USA19HS device 63 */ 64 #define UKSPAN_CONFIG_IDX 1 65 #define UKSPAN_IFACE_IDX 0 66 67 #define UKSPAN_EA_BULKIN (UE_DIR_IN | 1) 68 #define UKSPAN_EA_BULKOUT (UE_DIR_OUT | 1) 69 #define UKSPAN_EA_CONFIGIN (UE_DIR_IN | 2) 70 #define UKSPAN_EA_CONFIGOUT (UE_DIR_OUT | 2) 71 72 /* Sent to device on control out endpoint */ 73 struct ukspan_cmsg { 74 uint8_t setclock; 75 uint8_t baudlo; 76 uint8_t baudhi; 77 uint8_t setlcr; 78 uint8_t lcr; 79 uint8_t setrxmode; 80 uint8_t rxmode; 81 uint8_t settxmode; 82 uint8_t txmode; 83 uint8_t settxflowcontrol; 84 uint8_t txflowcontrol; 85 uint8_t setrxflowcontrol; 86 uint8_t rxflowcontrol; 87 uint8_t sendxoff; 88 uint8_t sendxon; 89 uint8_t xonchar; 90 uint8_t xoffchar; 91 uint8_t sendchar; 92 uint8_t txchar; 93 uint8_t setrts; 94 uint8_t rts; 95 uint8_t setdtr; 96 uint8_t dtr; 97 98 uint8_t rxforwardingchars; 99 uint8_t rxforwardingtimeoutms; 100 uint8_t txacksetting; 101 102 uint8_t portenabled; 103 uint8_t txflush; 104 uint8_t txbreak; 105 uint8_t loopbackmode; 106 107 uint8_t rxflush; 108 uint8_t rxforward; 109 uint8_t cancelrxoff; 110 uint8_t returnstatus; 111 } __packed; 112 113 /* Received from device on control in endpoint */ 114 struct ukspan_smsg { 115 uint8_t msr; 116 uint8_t cts; 117 uint8_t dcd; 118 uint8_t dsr; 119 uint8_t ri; 120 uint8_t txxoff; 121 uint8_t rxbreak; 122 uint8_t rxoverrun; 123 uint8_t rxparity; 124 uint8_t rxframe; 125 uint8_t portstate; 126 uint8_t messageack; 127 uint8_t charack; 128 uint8_t controlresp; 129 } __packed; 130 131 struct ukspan_softc { 132 struct device sc_dev; 133 struct usbd_device *udev; 134 struct usbd_interface *iface; 135 struct usbd_pipe *cout_pipe; 136 struct usbd_pipe *cin_pipe; 137 struct usbd_xfer *ixfer; 138 struct usbd_xfer *oxfer; 139 struct device *ucom_dev; 140 struct ukspan_smsg smsg; 141 struct ukspan_cmsg cmsg; 142 u_char lsr; 143 u_char msr; 144 }; 145 146 int ukspan_match(struct device *, void *, void *); 147 void ukspan_attach(struct device *, struct device *, void *); 148 int ukspan_detach(struct device *, int); 149 150 void ukspan_close(void *, int); 151 int ukspan_open(void *, int); 152 int ukspan_param(void *, int, struct termios *); 153 void ukspan_set(void *, int, int, int); 154 void ukspan_get_status(void *, int, u_char *, u_char *); 155 156 void ukspan_cmsg_init(bool, struct ukspan_cmsg *); 157 int ukspan_cmsg_send(struct ukspan_softc *); 158 void ukspan_incb(struct usbd_xfer *, void *, usbd_status); 159 void ukspan_outcb(struct usbd_xfer *, void *, usbd_status); 160 void ukspan_destroy(struct ukspan_softc *); 161 162 struct cfdriver ukspan_cd = { 163 NULL, "ukspan", DV_DULL 164 }; 165 166 const struct cfattach ukspan_ca = { 167 sizeof(struct ukspan_softc), ukspan_match, ukspan_attach, 168 ukspan_detach 169 }; 170 171 static const struct usb_devno ukspan_devs[] = { 172 { USB_VENDOR_KEYSPAN, USB_PRODUCT_KEYSPAN_USA19HS }, 173 }; 174 175 static const struct ucom_methods ukspan_methods = { 176 .ucom_get_status = ukspan_get_status, 177 .ucom_set = ukspan_set, 178 .ucom_param = ukspan_param, 179 .ucom_ioctl = NULL, 180 .ucom_open = ukspan_open, 181 .ucom_close = ukspan_close, 182 .ucom_read = NULL, 183 .ucom_write = NULL, 184 }; 185 186 int 187 ukspan_match(struct device *parent, void *match, void *aux) 188 { 189 struct usb_attach_arg *uaa = aux; 190 191 if (uaa->iface != NULL) 192 return UMATCH_NONE; 193 194 int found = usb_lookup(ukspan_devs, uaa->vendor, uaa->product) != NULL; 195 return found ? UMATCH_VENDOR_PRODUCT : UMATCH_NONE; 196 } 197 198 void 199 ukspan_attach(struct device *parent, struct device *self, void *aux) 200 { 201 struct ukspan_softc *sc = (struct ukspan_softc *)self; 202 struct usb_attach_arg *uaa = aux; 203 struct usbd_device *dev = uaa->device; 204 struct ucom_attach_args uca = {0}; 205 usb_endpoint_descriptor_t *ed; 206 const char *devname = sc->sc_dev.dv_xname; 207 usbd_status err; 208 int t1, t2, t3, t4; 209 210 DPRINTF("attach\n"); 211 212 sc->udev = dev; 213 sc->cin_pipe = sc->cout_pipe = NULL; 214 sc->ixfer = sc->oxfer = NULL; 215 sc->ucom_dev = NULL; 216 217 /* 218 * Switch to configuration 1 where the transfer mode of the input 219 * endpoints is bulk instead of interrupt, as ucom expects 220 */ 221 err = usbd_set_config_index(sc->udev, UKSPAN_CONFIG_IDX, 1); 222 if (err) { 223 printf("%s: set config failed\n", devname); 224 goto fail; 225 } 226 227 err = usbd_device2interface_handle(sc->udev, UKSPAN_IFACE_IDX, 228 &sc->iface); 229 if (err) { 230 printf("%s: get interface failed\n", devname); 231 goto fail; 232 } 233 234 ed = usbd_get_endpoint_descriptor(sc->iface, UKSPAN_EA_BULKIN); 235 t1 = UE_GET_XFERTYPE(ed->bmAttributes); 236 uca.ibufsize = UGETW(ed->wMaxPacketSize); 237 uca.bulkin = UKSPAN_EA_BULKIN; 238 239 ed = usbd_get_endpoint_descriptor(sc->iface, UKSPAN_EA_BULKOUT); 240 t2 = UE_GET_XFERTYPE(ed->bmAttributes); 241 uca.obufsize = UGETW(ed->wMaxPacketSize); 242 uca.bulkout = UKSPAN_EA_BULKOUT; 243 244 ed = usbd_get_endpoint_descriptor(sc->iface, UKSPAN_EA_CONFIGIN); 245 t3 = UE_GET_XFERTYPE(ed->bmAttributes); 246 if (UGETW(ed->wMaxPacketSize) < sizeof(struct ukspan_smsg)) { 247 printf("%s: in config packet size too small\n", devname); 248 goto fail; 249 } 250 251 ed = usbd_get_endpoint_descriptor(sc->iface, UKSPAN_EA_CONFIGOUT); 252 t4 = UE_GET_XFERTYPE(ed->bmAttributes); 253 if (UGETW(ed->wMaxPacketSize) < sizeof(struct ukspan_cmsg)) { 254 printf("%s: out config packet size too small\n", devname); 255 goto fail; 256 } 257 258 if (t1 != UE_BULK || t2 != UE_BULK || t3 != UE_BULK || t4 != UE_BULK) { 259 printf("%s: unexpected xfertypes %x %x %x %x != %x\n", devname, 260 t1, t2, t3, t4, UE_BULK); 261 goto fail; 262 } 263 264 /* Resource acquisition starts here */ 265 err = usbd_open_pipe(sc->iface, UKSPAN_EA_CONFIGOUT, USBD_EXCLUSIVE_USE, 266 &sc->cout_pipe); 267 if (err) { 268 printf("%s: failed to create control out pipe\n", devname); 269 goto fail; 270 } 271 272 err = usbd_open_pipe(sc->iface, UKSPAN_EA_CONFIGIN, USBD_EXCLUSIVE_USE, 273 &sc->cin_pipe); 274 if (err) { 275 printf("%s: failed to create control out pipe\n", devname); 276 goto fail; 277 } 278 279 sc->ixfer = usbd_alloc_xfer(sc->udev); 280 sc->oxfer = usbd_alloc_xfer(sc->udev); 281 if (!sc->ixfer || !sc->oxfer) { 282 printf("%s: failed to allocate xfers\n", devname); 283 goto fail; 284 } 285 286 usbd_setup_xfer(sc->ixfer, sc->cin_pipe, sc, &sc->smsg, 287 sizeof(sc->smsg), 0, USBD_NO_TIMEOUT, ukspan_incb); 288 err = usbd_transfer(sc->ixfer); 289 if (err && err != USBD_IN_PROGRESS) { 290 printf("%s: failed to start ixfer\n", devname); 291 goto fail; 292 } 293 294 uca.portno = UCOM_UNK_PORTNO; 295 uca.ibufsizepad = uca.ibufsize; 296 uca.opkthdrlen = 0; 297 uca.device = dev; 298 uca.iface = sc->iface; 299 uca.methods = &ukspan_methods; 300 uca.arg = sc; 301 uca.info = NULL; 302 303 sc->ucom_dev = config_found_sm(self, &uca, ucomprint, ucomsubmatch); 304 305 DPRINTF("attach done\n"); 306 307 return; 308 fail: 309 ukspan_destroy(sc); 310 usbd_deactivate(sc->udev); 311 } 312 313 int 314 ukspan_detach(struct device *self, int flags) 315 { 316 struct ukspan_softc *sc = (struct ukspan_softc *)self; 317 DPRINTF("detach\n"); 318 319 ukspan_destroy(sc); 320 321 if (sc->ucom_dev) { 322 config_detach(sc->ucom_dev, flags); 323 sc->ucom_dev = NULL; 324 } 325 return 0; 326 } 327 328 void 329 ukspan_outcb(struct usbd_xfer *xfer, void *priv, usbd_status status) 330 { 331 struct ukspan_softc *sc = priv; 332 const char *devname = sc->sc_dev.dv_xname; 333 334 DPRINTF("outcb\n"); 335 336 if (usbd_is_dying(sc->udev)) { 337 DPRINTF("usb dying\n"); 338 return; 339 } 340 if (status != USBD_NORMAL_COMPLETION) { 341 printf("%s: oxfer failed\n", devname); 342 return; 343 } 344 } 345 346 void 347 ukspan_incb(struct usbd_xfer *xfer, void *priv, usbd_status status) 348 { 349 struct ukspan_softc *sc = priv; 350 const char *devname = sc->sc_dev.dv_xname; 351 const struct ukspan_smsg *smsg = &sc->smsg; 352 usbd_status err; 353 u_int32_t len; 354 355 DPRINTF("incb\n"); 356 357 if (usbd_is_dying(sc->udev)) { 358 printf("%s: usb dying\n", devname); 359 return; 360 } 361 if (!sc->cin_pipe || !sc->ixfer) { 362 printf("%s: no cin_pipe, but not dying?\n", devname); 363 return; 364 } 365 if (status != USBD_NORMAL_COMPLETION) { 366 if (status != USBD_NOT_STARTED && status != USBD_CANCELLED) 367 printf("%s: ixfer failed\n", devname); 368 return; 369 } 370 371 usbd_get_xfer_status(xfer, NULL, NULL, &len, NULL); 372 if (len < sizeof(struct ukspan_smsg)) { 373 printf("%s: short read\n", devname); 374 return; 375 } 376 377 /* The device provides the actual MSR register */ 378 sc->msr = smsg->msr; 379 /* But not LSR... */ 380 sc->lsr = (smsg->rxoverrun ? ULSR_OE : 0) | 381 (smsg->rxparity ? ULSR_PE : 0) | 382 (smsg->rxframe ? ULSR_FE : 0) | 383 (smsg->rxbreak ? ULSR_BI : 0); 384 ucom_status_change((struct ucom_softc *)sc->ucom_dev); 385 386 usbd_setup_xfer(sc->ixfer, sc->cin_pipe, sc, &sc->smsg, 387 sizeof(sc->smsg), USBD_SHORT_XFER_OK, USBD_NO_TIMEOUT, 388 ukspan_incb); 389 err = usbd_transfer(sc->ixfer); 390 if (err && err != USBD_IN_PROGRESS) 391 printf("%s: usbd transfer failed\n", devname); 392 } 393 394 void 395 ukspan_get_status(void *addr, int portno, u_char *lsr, u_char *msr) 396 { 397 struct ukspan_softc *sc = addr; 398 DPRINTF("get status\n"); 399 if (lsr) 400 *lsr = sc->lsr; 401 if (msr) 402 *msr = sc->msr; 403 } 404 405 void 406 ukspan_cmsg_init(bool opening, struct ukspan_cmsg *omsg) 407 { 408 bzero(omsg, sizeof(*omsg)); 409 410 omsg->xonchar = 17; 411 omsg->xoffchar = 19; 412 413 omsg->rxforwardingchars = 16; 414 omsg->rxforwardingtimeoutms = 16; 415 omsg->txacksetting = 0; 416 omsg->txbreak = 0; 417 if (opening) { 418 omsg->portenabled = 1; 419 omsg->rxflush = 1; 420 } 421 } 422 423 int 424 ukspan_cmsg_send(struct ukspan_softc *sc) 425 { 426 const char *devname = sc->sc_dev.dv_xname; 427 usbd_status err; 428 429 usbd_setup_xfer(sc->oxfer, sc->cout_pipe, sc, &sc->cmsg, 430 sizeof(sc->cmsg), USBD_SYNCHRONOUS, USBD_NO_TIMEOUT, ukspan_outcb); 431 err = usbd_transfer(sc->oxfer); 432 if (err != USBD_NORMAL_COMPLETION) { 433 printf("%s: control xfer failed\n", devname); 434 return EIO; 435 } 436 return 0; 437 } 438 439 void 440 ukspan_set(void *addr, int portno, int reg, int onoff) 441 { 442 struct ukspan_softc *sc = addr; 443 const char *devname = sc->sc_dev.dv_xname; 444 DPRINTF("set %#x = %#x\n", reg, onoff); 445 int flag = !!onoff; 446 switch (reg) { 447 case UCOM_SET_DTR: 448 sc->cmsg.setdtr = 1; 449 sc->cmsg.dtr = flag; 450 break; 451 case UCOM_SET_RTS: 452 sc->cmsg.setrts = 1; 453 sc->cmsg.rts = flag; 454 break; 455 case UCOM_SET_BREAK: 456 sc->cmsg.txbreak = flag; 457 break; 458 default: 459 printf("%s: unhandled reg %#x\n", devname, reg); 460 return; 461 } 462 ukspan_cmsg_send(sc); 463 } 464 465 int 466 ukspan_param(void *addr, int portno, struct termios *ti) 467 { 468 struct ukspan_softc *sc = addr; 469 const char *devname = sc->sc_dev.dv_xname; 470 struct ukspan_cmsg *cmsg = &sc->cmsg; 471 speed_t baud; 472 tcflag_t cflag; 473 u_int32_t div; 474 u_int8_t lcr; 475 476 DPRINTF("param: %#x %#x %#x\n", ti->c_ospeed, ti->c_cflag, ti->c_iflag); 477 478 /* Set baud */ 479 div = 1; 480 baud = ti->c_ospeed; 481 switch (baud) { 482 case B300: 483 case B600: 484 case B1200: 485 case B2400: 486 case B4800: 487 case B9600: 488 case B19200: 489 case B38400: 490 case B57600: 491 case B115200: 492 case B230400: 493 div = UKSPAN_CLOCK / (baud * 16); 494 break; 495 default: 496 printf("%s: unexpected baud: %d\n", devname, baud); 497 return EINVAL; 498 } 499 500 cmsg->setclock = 1; 501 cmsg->baudlo = div & 0xff; 502 cmsg->baudhi = div >> 8; 503 504 cmsg->setrxmode = 1; 505 cmsg->settxmode = 1; 506 if (baud > 57600) 507 cmsg->rxmode = cmsg->txmode = UKSPAN_MAGIC; 508 else 509 cmsg->rxmode = cmsg->txmode = 0; 510 511 /* Set parity, data, and stop bits */ 512 cflag = ti->c_cflag; 513 if ((cflag & CIGNORE) == 0) { 514 if (cflag & PARENB) 515 lcr = (cflag & PARODD) ? UKSPAN_PARITY_ODD : 516 UKSPAN_PARITY_EVEN; 517 else 518 lcr = UKSPAN_PARITY_NONE; 519 switch (cflag & CSIZE) { 520 case CS5: 521 lcr |= UKSPAN_DATA_5; 522 break; 523 case CS6: 524 lcr |= UKSPAN_DATA_6; 525 break; 526 case CS7: 527 lcr |= UKSPAN_DATA_7; 528 break; 529 case CS8: 530 lcr |= UKSPAN_DATA_8; 531 break; 532 } 533 534 lcr |= (cflag & CSTOPB) ? UKSPAN_STOP_2 : UKSPAN_STOP_1; 535 536 cmsg->setlcr = 1; 537 cmsg->lcr = lcr; 538 } 539 540 /* XXX flow control? */ 541 542 ukspan_cmsg_send(sc); 543 return 0; 544 } 545 546 int 547 ukspan_open(void *addr, int portno) 548 { 549 struct ukspan_softc *sc = addr; 550 int ret; 551 552 DPRINTF("open\n"); 553 if (usbd_is_dying(sc->udev)) { 554 DPRINTF("usb dying\n"); 555 return ENXIO; 556 } 557 558 ukspan_cmsg_init(true, &sc->cmsg); 559 ret = ukspan_cmsg_send(sc); 560 return ret; 561 } 562 563 void 564 ukspan_close(void *addr, int portno) 565 { 566 struct ukspan_softc *sc = addr; 567 DPRINTF("close\n"); 568 if (usbd_is_dying(sc->udev)) { 569 DPRINTF("usb dying\n"); 570 return; 571 } 572 ukspan_cmsg_init(false, &sc->cmsg); 573 ukspan_cmsg_send(sc); 574 } 575 576 void 577 ukspan_destroy(struct ukspan_softc *sc) 578 { 579 DPRINTF("destroy\n"); 580 if (sc->cin_pipe) { 581 usbd_close_pipe(sc->cin_pipe); 582 sc->cin_pipe = NULL; 583 } 584 if (sc->cout_pipe) { 585 usbd_close_pipe(sc->cout_pipe); 586 sc->cout_pipe = NULL; 587 } 588 if (sc->oxfer) { 589 usbd_free_xfer(sc->oxfer); 590 sc->oxfer = NULL; 591 } 592 if (sc->ixfer) { 593 usbd_free_xfer(sc->ixfer); 594 sc->ixfer = NULL; 595 } 596 } 597