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