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