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