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