1 /* $NetBSD: ucycom.c,v 1.51 2020/03/14 02:35:33 christos Exp $ */ 2 3 /* 4 * Copyright (c) 2005 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Nick Hudson 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 /* 32 * This code is based on the ucom driver. 33 */ 34 35 /* 36 * Device driver for Cypress CY7C637xx and CY7C640/1xx series USB to 37 * RS232 bridges. 38 */ 39 40 #include <sys/cdefs.h> 41 __KERNEL_RCSID(0, "$NetBSD: ucycom.c,v 1.51 2020/03/14 02:35:33 christos Exp $"); 42 43 #ifdef _KERNEL_OPT 44 #include "opt_usb.h" 45 #endif 46 47 #include <sys/param.h> 48 #include <sys/systm.h> 49 #include <sys/conf.h> 50 #include <sys/kernel.h> 51 #include <sys/kmem.h> 52 #include <sys/device.h> 53 #include <sys/sysctl.h> 54 #include <sys/tty.h> 55 #include <sys/file.h> 56 #include <sys/vnode.h> 57 #include <sys/kauth.h> 58 #include <sys/lwp.h> 59 60 #include <dev/usb/usb.h> 61 #include <dev/usb/usbhid.h> 62 63 #include <dev/usb/usbdi.h> 64 #include <dev/usb/usbdi_util.h> 65 #include <dev/usb/usbdevs.h> 66 #include <dev/usb/uhidev.h> 67 #include <dev/hid/hid.h> 68 69 #include "ioconf.h" 70 71 #ifdef UCYCOM_DEBUG 72 #define DPRINTF(x) if (ucycomdebug) printf x 73 #define DPRINTFN(n, x) if (ucycomdebug > (n)) printf x 74 int ucycomdebug = 20; 75 #else 76 #define DPRINTF(x) 77 #define DPRINTFN(n,x) 78 #endif 79 80 81 #define UCYCOMCALLUNIT_MASK TTCALLUNIT_MASK 82 #define UCYCOMUNIT_MASK TTUNIT_MASK 83 #define UCYCOMDIALOUT_MASK TTDIALOUT_MASK 84 85 #define UCYCOMCALLUNIT(x) TTCALLUNIT(x) 86 #define UCYCOMUNIT(x) TTUNIT(x) 87 #define UCYCOMDIALOUT(x) TTDIALOUT(x) 88 89 /* Configuration Byte */ 90 #define UCYCOM_RESET 0x80 91 #define UCYCOM_PARITY_TYPE_MASK 0x20 92 #define UCYCOM_PARITY_ODD 0x20 93 #define UCYCOM_PARITY_EVEN 0x00 94 #define UCYCOM_PARITY_MASK 0x10 95 #define UCYCOM_PARITY_ON 0x10 96 #define UCYCOM_PARITY_OFF 0x00 97 #define UCYCOM_STOP_MASK 0x08 98 #define UCYCOM_STOP_BITS_2 0x08 99 #define UCYCOM_STOP_BITS_1 0x00 100 #define UCYCOM_DATA_MASK 0x03 101 #define UCYCOM_DATA_BITS_8 0x03 102 #define UCYCOM_DATA_BITS_7 0x02 103 #define UCYCOM_DATA_BITS_6 0x01 104 #define UCYCOM_DATA_BITS_5 0x00 105 106 /* Modem (Input) status byte */ 107 #define UCYCOM_RI 0x80 108 #define UCYCOM_DCD 0x40 109 #define UCYCOM_DSR 0x20 110 #define UCYCOM_CTS 0x10 111 #define UCYCOM_ERROR 0x08 112 #define UCYCOM_LMASK 0x07 113 114 /* Modem (Output) control byte */ 115 #define UCYCOM_DTR 0x20 116 #define UCYCOM_RTS 0x10 117 #define UCYCOM_ORESET 0x08 118 119 struct ucycom_softc { 120 struct uhidev sc_hdev; 121 122 struct tty *sc_tty; 123 124 enum { 125 UCYCOM_INIT_NONE, 126 UCYCOM_INIT_INITED 127 } sc_init_state; 128 129 kmutex_t sc_lock; /* protects refcnt, others */ 130 131 /* uhidev parameters */ 132 size_t sc_flen; /* feature report length */ 133 size_t sc_olen; /* output report length */ 134 135 uint8_t *sc_obuf; 136 int sc_wlen; 137 138 /* settings */ 139 uint32_t sc_baud; 140 uint8_t sc_cfg; /* Data format */ 141 uint8_t sc_mcr; /* Modem control */ 142 uint8_t sc_msr; /* Modem status */ 143 int sc_swflags; 144 145 /* flags */ 146 char sc_dying; 147 }; 148 149 static dev_type_open(ucycomopen); 150 static dev_type_close(ucycomclose); 151 static dev_type_read(ucycomread); 152 static dev_type_write(ucycomwrite); 153 static dev_type_ioctl(ucycomioctl); 154 static dev_type_stop(ucycomstop); 155 static dev_type_tty(ucycomtty); 156 static dev_type_poll(ucycompoll); 157 158 const struct cdevsw ucycom_cdevsw = { 159 .d_open = ucycomopen, 160 .d_close = ucycomclose, 161 .d_read = ucycomread, 162 .d_write = ucycomwrite, 163 .d_ioctl = ucycomioctl, 164 .d_stop = ucycomstop, 165 .d_tty = ucycomtty, 166 .d_poll = ucycompoll, 167 .d_mmap = nommap, 168 .d_kqfilter = ttykqfilter, 169 .d_discard = nodiscard, 170 .d_flag = D_TTY 171 }; 172 173 Static int ucycomparam(struct tty *, struct termios *); 174 Static void ucycomstart(struct tty *); 175 Static void ucycomwritecb(struct usbd_xfer *, void *, usbd_status); 176 Static void ucycom_intr(struct uhidev *, void *, u_int); 177 Static int ucycom_configure(struct ucycom_softc *, uint32_t, uint8_t); 178 Static void tiocm_to_ucycom(struct ucycom_softc *, u_long, int); 179 Static int ucycom_to_tiocm(struct ucycom_softc *); 180 Static void ucycom_set_status(struct ucycom_softc *); 181 Static void ucycom_dtr(struct ucycom_softc *, int); 182 #if 0 183 Static void ucycom_rts(struct ucycom_softc *, int); 184 #endif 185 Static void ucycom_cleanup(struct ucycom_softc *); 186 187 #ifdef UCYCOM_DEBUG 188 Static void ucycom_get_cfg(struct ucycom_softc *); 189 #endif 190 191 Static const struct usb_devno ucycom_devs[] = { 192 { USB_VENDOR_CYPRESS, USB_PRODUCT_CYPRESS_USBRS232 }, 193 { USB_VENDOR_DELORME, USB_PRODUCT_DELORME_EARTHMATE }, 194 }; 195 #define ucycom_lookup(v, p) usb_lookup(ucycom_devs, v, p) 196 197 static int ucycom_match(device_t, cfdata_t, void *); 198 static void ucycom_attach(device_t, device_t, void *); 199 static int ucycom_detach(device_t, int); 200 static int ucycom_activate(device_t, enum devact); 201 202 CFATTACH_DECL_NEW(ucycom, sizeof(struct ucycom_softc), ucycom_match, 203 ucycom_attach, ucycom_detach, ucycom_activate); 204 205 static int 206 ucycom_match(device_t parent, cfdata_t match, void *aux) 207 { 208 struct uhidev_attach_arg *uha = aux; 209 210 return ucycom_lookup(uha->uiaa->uiaa_vendor, uha->uiaa->uiaa_product) 211 != NULL ? UMATCH_VENDOR_PRODUCT : UMATCH_NONE; 212 } 213 214 static void 215 ucycom_attach(device_t parent, device_t self, void *aux) 216 { 217 struct ucycom_softc *sc = device_private(self); 218 struct uhidev_attach_arg *uha = aux; 219 int size, repid; 220 void *desc; 221 222 sc->sc_hdev.sc_dev = self; 223 sc->sc_hdev.sc_intr = ucycom_intr; 224 sc->sc_hdev.sc_parent = uha->parent; 225 sc->sc_hdev.sc_report_id = uha->reportid; 226 sc->sc_init_state = UCYCOM_INIT_NONE; 227 228 uhidev_get_report_desc(uha->parent, &desc, &size); 229 repid = uha->reportid; 230 sc->sc_olen = hid_report_size(desc, size, hid_output, repid); 231 sc->sc_flen = hid_report_size(desc, size, hid_feature, repid); 232 233 if (sc->sc_olen != 8 && sc->sc_olen != 32) 234 return; 235 if (sc->sc_flen != 5) 236 return; 237 238 sc->sc_msr = sc->sc_mcr = 0; 239 240 /* set up tty */ 241 sc->sc_tty = tty_alloc(); 242 sc->sc_tty->t_sc = sc; 243 sc->sc_tty->t_oproc = ucycomstart; 244 sc->sc_tty->t_param = ucycomparam; 245 246 tty_attach(sc->sc_tty); 247 248 /* Nothing interesting to report */ 249 aprint_normal("\n"); 250 251 sc->sc_init_state = UCYCOM_INIT_INITED; 252 } 253 254 255 static int 256 ucycom_detach(device_t self, int flags) 257 { 258 struct ucycom_softc *sc = device_private(self); 259 struct tty *tp = sc->sc_tty; 260 int maj, mn; 261 int s; 262 263 DPRINTF(("ucycom_detach: sc=%p flags=%d tp=%p\n", sc, flags, tp)); 264 265 sc->sc_dying = 1; 266 267 s = splusb(); 268 if (tp != NULL) { 269 mutex_spin_enter(&tty_lock); 270 CLR(tp->t_state, TS_CARR_ON); 271 CLR(tp->t_cflag, CLOCAL | MDMBUF); 272 ttyflush(tp, FREAD|FWRITE); 273 mutex_spin_exit(&tty_lock); 274 } 275 /* Wait for processes to go away. */ 276 usb_detach_waitold(sc->sc_hdev.sc_dev); 277 splx(s); 278 279 /* locate the major number */ 280 maj = cdevsw_lookup_major(&ucycom_cdevsw); 281 282 /* Nuke the vnodes for any open instances. */ 283 mn = device_unit(self); 284 285 DPRINTFN(2, ("ucycom_detach: maj=%d mn=%d\n", maj, mn)); 286 vdevgone(maj, mn, mn, VCHR); 287 vdevgone(maj, mn | UCYCOMDIALOUT_MASK, mn | UCYCOMDIALOUT_MASK, VCHR); 288 vdevgone(maj, mn | UCYCOMCALLUNIT_MASK, mn | UCYCOMCALLUNIT_MASK, VCHR); 289 290 /* Detach and free the tty. */ 291 if (tp != NULL) { 292 DPRINTF(("ucycom_detach: tty_detach %p\n", tp)); 293 tty_detach(tp); 294 tty_free(tp); 295 sc->sc_tty = NULL; 296 } 297 298 return 0; 299 } 300 301 static int 302 ucycom_activate(device_t self, enum devact act) 303 { 304 struct ucycom_softc *sc = device_private(self); 305 306 DPRINTFN(5,("ucycom_activate: %d\n", act)); 307 308 switch (act) { 309 case DVACT_DEACTIVATE: 310 sc->sc_dying = 1; 311 return 0; 312 default: 313 return EOPNOTSUPP; 314 } 315 } 316 317 #if 0 318 void 319 ucycom_shutdown(struct ucycom_softc *sc) 320 { 321 struct tty *tp = sc->sc_tty; 322 323 DPRINTF(("ucycom_shutdown\n")); 324 /* 325 * Hang up if necessary. Wait a bit, so the other side has time to 326 * notice even if we immediately open the port again. 327 */ 328 if (ISSET(tp->t_cflag, HUPCL)) { 329 ucycom_dtr(sc, 0); 330 (void)tsleep(sc, TTIPRI, ttclos, hz); 331 } 332 } 333 #endif 334 335 static int 336 ucycomopen(dev_t dev, int flag, int mode, struct lwp *l) 337 { 338 struct ucycom_softc *sc = 339 device_lookup_private(&ucycom_cd, UCYCOMUNIT(dev)); 340 struct tty *tp; 341 int s, err; 342 343 DPRINTF(("ucycomopen: unit=%d\n", UCYCOMUNIT(dev))); 344 DPRINTF(("ucycomopen: sc=%p\n", sc)); 345 346 if (sc == NULL) 347 return ENXIO; 348 if (sc->sc_dying) 349 return EIO; 350 if (sc->sc_init_state != UCYCOM_INIT_INITED) 351 return ENXIO; 352 if (!device_is_active(sc->sc_hdev.sc_dev)) 353 return ENXIO; 354 355 tp = sc->sc_tty; 356 357 DPRINTF(("ucycomopen: tp=%p\n", tp)); 358 359 if (kauth_authorize_device_tty(l->l_cred, KAUTH_DEVICE_TTY_OPEN, tp)) 360 return EBUSY; 361 362 s = spltty(); 363 364 if (!ISSET(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0) { 365 struct termios t; 366 367 tp->t_dev = dev; 368 369 err = uhidev_open(&sc->sc_hdev); 370 if (err) { 371 /* Any cleanup? */ 372 splx(s); 373 return err; 374 } 375 376 /* 377 * Initialize the termios status to the defaults. Add in the 378 * sticky bits from TIOCSFLAGS. 379 */ 380 t.c_ispeed = 0; 381 t.c_ospeed = TTYDEF_SPEED; 382 t.c_cflag = TTYDEF_CFLAG; 383 if (ISSET(sc->sc_swflags, TIOCFLAG_CLOCAL)) 384 SET(t.c_cflag, CLOCAL); 385 if (ISSET(sc->sc_swflags, TIOCFLAG_CRTSCTS)) 386 SET(t.c_cflag, CRTSCTS); 387 if (ISSET(sc->sc_swflags, TIOCFLAG_MDMBUF)) 388 SET(t.c_cflag, MDMBUF); 389 390 tp->t_ospeed = 0; 391 (void) ucycomparam(tp, &t); 392 tp->t_iflag = TTYDEF_IFLAG; 393 tp->t_oflag = TTYDEF_OFLAG; 394 tp->t_lflag = TTYDEF_LFLAG; 395 ttychars(tp); 396 ttsetwater(tp); 397 398 /* Allocate an output report buffer */ 399 sc->sc_obuf = kmem_alloc(sc->sc_olen, KM_SLEEP); 400 401 DPRINTF(("ucycomopen: sc->sc_obuf=%p\n", sc->sc_obuf)); 402 403 #if 0 404 /* XXX Don't do this as for some reason trying to do an 405 * XXX interrupt out transfer at this point means everything 406 * XXX gets stuck!?! 407 */ 408 /* 409 * Turn on DTR. We must always do this, even if carrier is not 410 * present, because otherwise we'd have to use TIOCSDTR 411 * immediately after setting CLOCAL, which applications do not 412 * expect. We always assert DTR while the device is open 413 * unless explicitly requested to deassert it. 414 */ 415 ucycom_dtr(sc, 1); 416 #endif 417 418 #if 0 419 /* XXX CLR(sc->sc_rx_flags, RX_ANY_BLOCK);*/ 420 ucycom_hwiflow(sc); 421 #endif 422 423 } 424 splx(s); 425 426 err = ttyopen(tp, UCYCOMDIALOUT(dev), ISSET(flag, O_NONBLOCK)); 427 if (err) 428 goto bad; 429 430 err = (*tp->t_linesw->l_open)(dev, tp); 431 if (err) 432 goto bad; 433 434 return 0; 435 436 bad: 437 if (!ISSET(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0) { 438 /* 439 * We failed to open the device, and nobody else had it opened. 440 * Clean up the state as appropriate. 441 */ 442 ucycom_cleanup(sc); 443 } 444 445 return err; 446 447 } 448 449 450 static int 451 ucycomclose(dev_t dev, int flag, int mode, struct lwp *l) 452 { 453 struct ucycom_softc *sc = 454 device_lookup_private(&ucycom_cd, UCYCOMUNIT(dev)); 455 struct tty *tp = sc->sc_tty; 456 457 DPRINTF(("ucycomclose: unit=%d\n", UCYCOMUNIT(dev))); 458 if (!ISSET(tp->t_state, TS_ISOPEN)) 459 return 0; 460 461 (*tp->t_linesw->l_close)(tp, flag); 462 ttyclose(tp); 463 464 if (!ISSET(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0) { 465 /* 466 * Although we got a last close, the device may still be in 467 * use; e.g. if this was the dialout node, and there are still 468 * processes waiting for carrier on the non-dialout node. 469 */ 470 ucycom_cleanup(sc); 471 } 472 473 return 0; 474 } 475 476 Static void 477 ucycomstart(struct tty *tp) 478 { 479 struct ucycom_softc *sc = 480 device_lookup_private(&ucycom_cd, UCYCOMUNIT(tp->t_dev)); 481 usbd_status err __unused; 482 u_char *data; 483 int cnt, len, s; 484 485 if (sc->sc_dying) 486 return; 487 488 s = spltty(); 489 if (ISSET(tp->t_state, TS_BUSY | TS_TIMEOUT | TS_TTSTOP)) { 490 DPRINTFN(4,("ucycomstart: no go, state=%#x\n", tp->t_state)); 491 goto out; 492 } 493 494 #if 0 495 /* HW FLOW CTL */ 496 if (sc->sc_tx_stopped) 497 goto out; 498 #endif 499 500 if (ttypull(tp) == 0) 501 goto out; 502 503 /* Grab the first contiguous region of buffer space. */ 504 data = tp->t_outq.c_cf; 505 cnt = ndqb(&tp->t_outq, 0); 506 507 if (cnt == 0) { 508 DPRINTF(("ucycomstart: cnt == 0\n")); 509 goto out; 510 } 511 512 SET(tp->t_state, TS_BUSY); 513 514 /* 515 * The 8 byte output report uses byte 0 for control and byte 516 * count. 517 * 518 * The 32 byte output report uses byte 0 for control. Byte 1 519 * is used for byte count. 520 */ 521 memset(sc->sc_obuf, 0, sc->sc_olen); 522 len = cnt; 523 switch (sc->sc_olen) { 524 case 8: 525 if (cnt > sc->sc_olen - 1) { 526 DPRINTF(("ucycomstart(8): big buffer %d chars\n", len)); 527 len = sc->sc_olen - 1; 528 } 529 530 memcpy(sc->sc_obuf + 1, data, len); 531 sc->sc_obuf[0] = len | sc->sc_mcr; 532 533 DPRINTF(("ucycomstart(8): sc->sc_obuf[0] = %d | %d = %d\n", 534 len, sc->sc_mcr, sc->sc_obuf[0])); 535 #ifdef UCYCOM_DEBUG 536 if (ucycomdebug > 10) { 537 uint32_t i; 538 uint8_t *d = data; 539 540 DPRINTF(("ucycomstart(8): data =")); 541 for (i = 0; i < len; i++) 542 DPRINTF((" %02x", d[i])); 543 DPRINTF(("\n")); 544 } 545 #endif 546 break; 547 548 case 32: 549 if (cnt > sc->sc_olen - 2) { 550 DPRINTF(("ucycomstart(32): big buffer %d chars\n", 551 len)); 552 len = sc->sc_olen - 2; 553 } 554 555 memcpy(sc->sc_obuf + 2, data, len); 556 sc->sc_obuf[0] = sc->sc_mcr; 557 sc->sc_obuf[1] = len; 558 DPRINTF(("ucycomstart(32): sc->sc_obuf[0] = %d\n" 559 "sc->sc_obuf[1] = %d\n", sc->sc_obuf[0], sc->sc_obuf[1])); 560 #ifdef UCYCOM_DEBUG 561 if (ucycomdebug > 10) { 562 uint32_t i; 563 uint8_t *d = data; 564 565 DPRINTF(("ucycomstart(32): data =")); 566 for (i = 0; i < len; i++) 567 DPRINTF((" %02x", d[i])); 568 DPRINTF(("\n")); 569 } 570 #endif 571 break; 572 573 default: 574 DPRINTFN(2,("ucycomstart: unknown output report size (%zd)\n", 575 sc->sc_olen)); 576 goto out; 577 } 578 splx(s); 579 sc->sc_wlen = len; 580 581 #ifdef UCYCOM_DEBUG 582 if (ucycomdebug > 5) { 583 int i; 584 585 if (len != 0) { 586 DPRINTF(("ucycomstart: sc->sc_obuf[0..%zd) =", 587 sc->sc_olen)); 588 for (i = 0; i < sc->sc_olen; i++) 589 DPRINTF((" %02x", sc->sc_obuf[i])); 590 DPRINTF(("\n")); 591 } 592 } 593 #endif 594 DPRINTFN(4,("ucycomstart: %d chars\n", len)); 595 usbd_setup_xfer(sc->sc_hdev.sc_parent->sc_oxfer, sc, sc->sc_obuf, 596 sc->sc_olen, 0, USBD_NO_TIMEOUT, ucycomwritecb); 597 598 /* What can we do on error? */ 599 err = usbd_transfer(sc->sc_hdev.sc_parent->sc_oxfer); 600 601 #ifdef UCYCOM_DEBUG 602 if (err != USBD_IN_PROGRESS) 603 DPRINTF(("ucycomstart: err=%s\n", usbd_errstr(err))); 604 #endif 605 return; 606 607 out: 608 splx(s); 609 } 610 611 Static void 612 ucycomwritecb(struct usbd_xfer *xfer, void *p, usbd_status status) 613 { 614 struct ucycom_softc *sc = (struct ucycom_softc *)p; 615 struct tty *tp = sc->sc_tty; 616 usbd_status stat; 617 int len, s; 618 619 if (status == USBD_CANCELLED || sc->sc_dying) 620 goto error; 621 622 if (status) { 623 DPRINTF(("ucycomwritecb: status=%d\n", status)); 624 usbd_clear_endpoint_stall(sc->sc_hdev.sc_parent->sc_opipe); 625 /* XXX we should restart after some delay. */ 626 goto error; 627 } 628 629 usbd_get_xfer_status(xfer, NULL, NULL, &len, &stat); 630 631 if (status != USBD_NORMAL_COMPLETION) { 632 DPRINTFN(4,("ucycomwritecb: status = %d\n", status)); 633 goto error; 634 } 635 636 DPRINTFN(4,("ucycomwritecb: did %d/%d chars\n", sc->sc_wlen, len)); 637 638 s = spltty(); 639 CLR(tp->t_state, TS_BUSY); 640 if (ISSET(tp->t_state, TS_FLUSH)) 641 CLR(tp->t_state, TS_FLUSH); 642 else 643 ndflush(&tp->t_outq, sc->sc_wlen); 644 (*tp->t_linesw->l_start)(tp); 645 splx(s); 646 return; 647 648 error: 649 s = spltty(); 650 CLR(tp->t_state, TS_BUSY); 651 splx(s); 652 } 653 654 Static int 655 ucycomparam(struct tty *tp, struct termios *t) 656 { 657 struct ucycom_softc *sc = tp->t_sc; 658 uint32_t baud; 659 uint8_t cfg; 660 int err; 661 662 if (t->c_ospeed < 0) { 663 DPRINTF(("ucycomparam: c_ospeed < 0\n")); 664 return EINVAL; 665 } 666 667 /* Check requested parameters. */ 668 if (t->c_ispeed && t->c_ispeed != t->c_ospeed) 669 return EINVAL; 670 671 /* 672 * For the console, always force CLOCAL and !HUPCL, so that the port 673 * is always active. 674 */ 675 if (ISSET(sc->sc_swflags, TIOCFLAG_SOFTCAR)) { 676 SET(t->c_cflag, CLOCAL); 677 CLR(t->c_cflag, HUPCL); 678 } 679 680 /* 681 * If there were no changes, don't do anything. This avoids dropping 682 * input and improves performance when all we did was frob things like 683 * VMIN and VTIME. 684 */ 685 if (tp->t_ospeed == t->c_ospeed && 686 tp->t_cflag == t->c_cflag) 687 return 0; 688 689 /* XXX lcr = ISSET(sc->sc_lcr, LCR_SBREAK) | cflag2lcr(t->c_cflag); */ 690 691 /* And copy to tty. */ 692 tp->t_ispeed = 0; 693 tp->t_ospeed = t->c_ospeed; 694 tp->t_cflag = t->c_cflag; 695 696 baud = t->c_ispeed; 697 DPRINTF(("ucycomparam: baud=%d\n", baud)); 698 699 if (t->c_cflag & CIGNORE) { 700 cfg = sc->sc_cfg; 701 } else { 702 cfg = 0; 703 switch (t->c_cflag & CSIZE) { 704 case CS8: 705 cfg |= UCYCOM_DATA_BITS_8; 706 break; 707 case CS7: 708 cfg |= UCYCOM_DATA_BITS_7; 709 break; 710 case CS6: 711 cfg |= UCYCOM_DATA_BITS_6; 712 break; 713 case CS5: 714 cfg |= UCYCOM_DATA_BITS_5; 715 break; 716 default: 717 return EINVAL; 718 } 719 cfg |= ISSET(t->c_cflag, CSTOPB) ? 720 UCYCOM_STOP_BITS_2 : UCYCOM_STOP_BITS_1; 721 cfg |= ISSET(t->c_cflag, PARENB) ? 722 UCYCOM_PARITY_ON : UCYCOM_PARITY_OFF; 723 cfg |= ISSET(t->c_cflag, PARODD) ? 724 UCYCOM_PARITY_ODD : UCYCOM_PARITY_EVEN; 725 } 726 727 /* 728 * Update the tty layer's idea of the carrier bit, in case we changed 729 * CLOCAL or MDMBUF. We don't hang up here; we only do that by 730 * explicit request. 731 */ 732 DPRINTF(("ucycomparam: l_modem\n")); 733 (void) (*tp->t_linesw->l_modem)(tp, 1 /* XXX carrier */ ); 734 735 err = ucycom_configure(sc, baud, cfg); 736 return err; 737 } 738 739 static void 740 ucycomstop(struct tty *tp, int flag) 741 { 742 DPRINTF(("ucycomstop: flag=%d\n", flag)); 743 } 744 745 static int 746 ucycomread(dev_t dev, struct uio *uio, int flag) 747 { 748 struct ucycom_softc *sc = 749 device_lookup_private(&ucycom_cd, UCYCOMUNIT(dev)); 750 struct tty *tp = sc->sc_tty; 751 int err; 752 753 DPRINTF(("ucycomread: sc=%p, tp=%p, uio=%p, flag=%d\n", sc, tp, uio, 754 flag)); 755 if (sc->sc_dying) 756 return EIO; 757 758 err = ((*tp->t_linesw->l_read)(tp, uio, flag)); 759 return err; 760 } 761 762 763 static int 764 ucycomwrite(dev_t dev, struct uio *uio, int flag) 765 { 766 struct ucycom_softc *sc = 767 device_lookup_private(&ucycom_cd, UCYCOMUNIT(dev)); 768 struct tty *tp = sc->sc_tty; 769 int err; 770 771 DPRINTF(("ucycomwrite: sc=%p, tp=%p, uio=%p, flag=%d\n", sc, tp, uio, 772 flag)); 773 if (sc->sc_dying) 774 return EIO; 775 776 err = ((*tp->t_linesw->l_write)(tp, uio, flag)); 777 return err; 778 } 779 780 static struct tty * 781 ucycomtty(dev_t dev) 782 { 783 struct ucycom_softc *sc = 784 device_lookup_private(&ucycom_cd, UCYCOMUNIT(dev)); 785 struct tty *tp = sc->sc_tty; 786 787 DPRINTF(("ucycomtty: sc=%p, tp=%p\n", sc, tp)); 788 789 return tp; 790 } 791 792 static int 793 ucycomioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l) 794 { 795 struct ucycom_softc *sc = 796 device_lookup_private(&ucycom_cd, UCYCOMUNIT(dev)); 797 struct tty *tp = sc->sc_tty; 798 int err; 799 int s; 800 801 if (sc->sc_dying) 802 return EIO; 803 804 DPRINTF(("ucycomioctl: sc=%p, tp=%p, data=%p\n", sc, tp, data)); 805 806 err = (*tp->t_linesw->l_ioctl)(tp, cmd, data, flag, l); 807 if (err != EPASSTHROUGH) 808 return err; 809 810 err = ttioctl(tp, cmd, data, flag, l); 811 if (err != EPASSTHROUGH) 812 return err; 813 814 err = 0; 815 816 DPRINTF(("ucycomioctl: our cmd=0x%08lx\n", cmd)); 817 s = spltty(); 818 819 switch (cmd) { 820 /* case TIOCSBRK: 821 ucycom_break(sc, 1); 822 break; 823 824 case TIOCCBRK: 825 ucycom_break(sc, 0); 826 break; 827 */ 828 case TIOCSDTR: 829 ucycom_dtr(sc, 1); 830 break; 831 832 case TIOCCDTR: 833 ucycom_dtr(sc, 0); 834 break; 835 836 case TIOCGFLAGS: 837 *(int *)data = sc->sc_swflags; 838 break; 839 840 case TIOCSFLAGS: 841 err = kauth_authorize_device_tty(l->l_cred, 842 KAUTH_DEVICE_TTY_PRIVSET, tp); 843 if (err) 844 break; 845 sc->sc_swflags = *(int *)data; 846 break; 847 848 case TIOCMSET: 849 case TIOCMBIS: 850 case TIOCMBIC: 851 tiocm_to_ucycom(sc, cmd, *(int *)data); 852 break; 853 854 case TIOCMGET: 855 *(int *)data = ucycom_to_tiocm(sc); 856 break; 857 858 default: 859 err = EPASSTHROUGH; 860 break; 861 } 862 863 splx(s); 864 865 return err; 866 } 867 868 static int 869 ucycompoll(dev_t dev, int events, struct lwp *l) 870 { 871 struct ucycom_softc *sc = 872 device_lookup_private(&ucycom_cd, UCYCOMUNIT(dev)); 873 struct tty *tp = sc->sc_tty; 874 int err; 875 876 DPRINTF(("ucycompoll: sc=%p, tp=%p, events=%d, lwp=%p\n", sc, tp, 877 events, l)); 878 879 if (sc->sc_dying) 880 return EIO; 881 882 err = ((*tp->t_linesw->l_poll)(tp, events, l)); 883 return err; 884 } 885 886 Static int 887 ucycom_configure(struct ucycom_softc *sc, uint32_t baud, uint8_t cfg) 888 { 889 uint8_t report[5]; 890 int err; 891 892 switch (baud) { 893 case 600: 894 case 1200: 895 case 2400: 896 case 4800: 897 case 9600: 898 case 19200: 899 case 38400: 900 case 57600: 901 #if 0 902 /* 903 * Stock chips only support standard baud rates in the 600 - 57600 904 * range, but higher rates can be achieved using custom firmware. 905 */ 906 case 115200: 907 case 153600: 908 case 192000: 909 #endif 910 break; 911 default: 912 return EINVAL; 913 } 914 915 DPRINTF(("ucycom_configure: setting %d baud, %d-%c-%d (%d)\n", baud, 916 5 + (cfg & UCYCOM_DATA_MASK), 917 (cfg & UCYCOM_PARITY_MASK) ? 918 ((cfg & UCYCOM_PARITY_TYPE_MASK) ? 'O' : 'E') : 'N', 919 (cfg & UCYCOM_STOP_MASK) ? 2 : 1, cfg)); 920 921 report[0] = baud & 0xff; 922 report[1] = (baud >> 8) & 0xff; 923 report[2] = (baud >> 16) & 0xff; 924 report[3] = (baud >> 24) & 0xff; 925 report[4] = cfg; 926 err = uhidev_set_report(&sc->sc_hdev, UHID_FEATURE_REPORT, 927 report, sc->sc_flen); 928 if (err != 0) { 929 DPRINTF(("%s\n", usbd_errstr(err))); 930 return EIO; 931 } 932 sc->sc_baud = baud; 933 sc->sc_cfg = cfg; 934 935 #ifdef UCYCOM_DEBUG 936 ucycom_get_cfg(sc); 937 #endif 938 939 return 0; 940 } 941 942 Static void 943 ucycom_intr(struct uhidev *addr, void *ibuf, u_int len) 944 { 945 struct ucycom_softc *sc = (struct ucycom_softc *)addr; 946 struct tty *tp = sc->sc_tty; 947 int (*rint)(int , struct tty *) = tp->t_linesw->l_rint; 948 uint8_t *cp = ibuf; 949 int s, n, st, chg; 950 951 /* We understand 8 byte and 32 byte input records */ 952 switch (len) { 953 case 8: 954 n = cp[0] & UCYCOM_LMASK; 955 st = cp[0] & ~UCYCOM_LMASK; 956 cp++; 957 break; 958 959 case 32: 960 st = cp[0]; 961 n = cp[1]; 962 cp += 2; 963 break; 964 965 default: 966 DPRINTFN(3,("ucycom_intr: Unknown input report length\n")); 967 return; 968 } 969 970 #ifdef UCYCOM_DEBUG 971 if (ucycomdebug > 5) { 972 uint32_t i; 973 974 if (n != 0) { 975 DPRINTF(("ucycom_intr: ibuf[0..%d) =", n)); 976 for (i = 0; i < n; i++) 977 DPRINTF((" %02x", cp[i])); 978 DPRINTF(("\n")); 979 } 980 } 981 #endif 982 983 /* Give characters to tty layer. */ 984 s = spltty(); 985 while (n-- > 0) { 986 DPRINTFN(7,("ucycom_intr: char=0x%02x\n", *cp)); 987 if ((*rint)(*cp++, tp) == -1) { 988 /* XXX what should we do? */ 989 aprint_error_dev(sc->sc_hdev.sc_dev, 990 "lost a character\n"); 991 break; 992 } 993 } 994 splx(s); 995 chg = st ^ sc->sc_msr; 996 sc->sc_msr = st; 997 if (ISSET(chg, UCYCOM_DCD)) 998 (*tp->t_linesw->l_modem)(tp, 999 ISSET(sc->sc_msr, UCYCOM_DCD)); 1000 1001 } 1002 1003 Static void 1004 tiocm_to_ucycom(struct ucycom_softc *sc, u_long how, int ttybits) 1005 { 1006 u_char combits; 1007 u_char before = sc->sc_mcr; 1008 1009 combits = 0; 1010 if (ISSET(ttybits, TIOCM_DTR)) 1011 SET(combits, UCYCOM_DTR); 1012 if (ISSET(ttybits, TIOCM_RTS)) 1013 SET(combits, UCYCOM_RTS); 1014 1015 switch (how) { 1016 case TIOCMBIC: 1017 CLR(sc->sc_mcr, combits); 1018 break; 1019 1020 case TIOCMBIS: 1021 SET(sc->sc_mcr, combits); 1022 break; 1023 1024 case TIOCMSET: 1025 CLR(sc->sc_mcr, UCYCOM_DTR | UCYCOM_RTS); 1026 SET(sc->sc_mcr, combits); 1027 break; 1028 } 1029 if (before ^ sc->sc_mcr) { 1030 DPRINTF(("tiocm_to_ucycom: something has changed\n")); 1031 ucycom_set_status(sc); 1032 } 1033 } 1034 1035 Static int 1036 ucycom_to_tiocm(struct ucycom_softc *sc) 1037 { 1038 u_char combits; 1039 int ttybits = 0; 1040 1041 combits = sc->sc_mcr; 1042 if (ISSET(combits, UCYCOM_DTR)) 1043 SET(ttybits, TIOCM_DTR); 1044 if (ISSET(combits, UCYCOM_RTS)) 1045 SET(ttybits, TIOCM_RTS); 1046 1047 combits = sc->sc_msr; 1048 if (ISSET(combits, UCYCOM_DCD)) 1049 SET(ttybits, TIOCM_CD); 1050 if (ISSET(combits, UCYCOM_CTS)) 1051 SET(ttybits, TIOCM_CTS); 1052 if (ISSET(combits, UCYCOM_DSR)) 1053 SET(ttybits, TIOCM_DSR); 1054 if (ISSET(combits, UCYCOM_RI)) 1055 SET(ttybits, TIOCM_RI); 1056 1057 return ttybits; 1058 } 1059 1060 Static void 1061 ucycom_dtr(struct ucycom_softc *sc, int set) 1062 { 1063 uint8_t old; 1064 1065 old = sc->sc_mcr; 1066 if (set) 1067 SET(sc->sc_mcr, UCYCOM_DTR); 1068 else 1069 CLR(sc->sc_mcr, UCYCOM_DTR); 1070 1071 if (old ^ sc->sc_mcr) 1072 ucycom_set_status(sc); 1073 } 1074 1075 #if 0 1076 Static void 1077 ucycom_rts(struct ucycom_softc *sc, int set) 1078 { 1079 uint8_t old; 1080 1081 old = sc->sc_msr; 1082 if (set) 1083 SET(sc->sc_mcr, UCYCOM_RTS); 1084 else 1085 CLR(sc->sc_mcr, UCYCOM_RTS); 1086 1087 if (old ^ sc->sc_mcr) 1088 ucycom_set_status(sc); 1089 } 1090 #endif 1091 1092 Static void 1093 ucycom_set_status(struct ucycom_softc *sc) 1094 { 1095 int err; 1096 1097 if (sc->sc_olen != 8 && sc->sc_olen != 32) { 1098 DPRINTFN(2,("ucycom_set_status: unknown output report " 1099 "size (%zd)\n", sc->sc_olen)); 1100 return; 1101 } 1102 1103 DPRINTF(("ucycom_set_status: %d\n", sc->sc_mcr)); 1104 1105 memset(sc->sc_obuf, 0, sc->sc_olen); 1106 sc->sc_obuf[0] = sc->sc_mcr; 1107 1108 err = uhidev_write(sc->sc_hdev.sc_parent, sc->sc_obuf, sc->sc_olen); 1109 if (err) { 1110 DPRINTF(("ucycom_set_status: err=%d\n", err)); 1111 } 1112 } 1113 1114 #ifdef UCYCOM_DEBUG 1115 Static void 1116 ucycom_get_cfg(struct ucycom_softc *sc) 1117 { 1118 int err, cfg, baud; 1119 uint8_t report[5]; 1120 1121 err = uhidev_get_report(&sc->sc_hdev, UHID_FEATURE_REPORT, 1122 report, sc->sc_flen); 1123 if (err) { 1124 DPRINTF(("%s: failed\n", __func__)); 1125 return; 1126 } 1127 cfg = report[4]; 1128 baud = (report[3] << 24) + (report[2] << 16) + (report[1] << 8) + 1129 report[0]; 1130 DPRINTF(("ucycom_get_cfg: device reports %d baud, %d-%c-%d (%d)\n", 1131 baud, 5 + (cfg & UCYCOM_DATA_MASK), 1132 (cfg & UCYCOM_PARITY_MASK) ? 1133 ((cfg & UCYCOM_PARITY_TYPE_MASK) ? 'O' : 'E') : 'N', 1134 (cfg & UCYCOM_STOP_MASK) ? 2 : 1, cfg)); 1135 } 1136 #endif 1137 1138 Static void 1139 ucycom_cleanup(struct ucycom_softc *sc) 1140 { 1141 uint8_t *obuf; 1142 1143 DPRINTF(("ucycom_cleanup: closing uhidev\n")); 1144 1145 obuf = sc->sc_obuf; 1146 sc->sc_obuf = NULL; 1147 uhidev_close(&sc->sc_hdev); 1148 1149 if (obuf != NULL) 1150 kmem_free(obuf, sc->sc_olen); 1151 } 1152