1 /* $NetBSD: ucycom.c,v 1.42 2015/03/07 20:20:55 mrg 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.42 2015/03/07 20:20:55 mrg 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/malloc.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(usbd_xfer_handle, usbd_private_handle, 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 *sc); 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->uaa->vendor, uha->uaa->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 = malloc(sc->sc_olen, M_USBDEV, M_WAITOK); 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 u_int32_t i; 522 u_int8_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 u_int32_t i; 547 u_int8_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, 580 sc->sc_hdev.sc_parent->sc_opipe, (usbd_private_handle)sc, 581 sc->sc_obuf, sc->sc_olen, 0 /* USBD_NO_COPY */, USBD_NO_TIMEOUT, 582 ucycomwritecb); 583 584 /* What can we do on error? */ 585 err = usbd_transfer(sc->sc_hdev.sc_parent->sc_oxfer); 586 587 #ifdef UCYCOM_DEBUG 588 if (err != USBD_IN_PROGRESS) 589 DPRINTF(("ucycomstart: err=%s\n", usbd_errstr(err))); 590 #endif 591 return; 592 593 out: 594 splx(s); 595 } 596 597 Static void 598 ucycomwritecb(usbd_xfer_handle xfer, usbd_private_handle p, usbd_status status) 599 { 600 struct ucycom_softc *sc = (struct ucycom_softc *)p; 601 struct tty *tp = sc->sc_tty; 602 usbd_status stat; 603 int len, s; 604 605 if (status == USBD_CANCELLED || sc->sc_dying) 606 goto error; 607 608 if (status) { 609 DPRINTF(("ucycomwritecb: status=%d\n", status)); 610 usbd_clear_endpoint_stall(sc->sc_hdev.sc_parent->sc_opipe); 611 /* XXX we should restart after some delay. */ 612 goto error; 613 } 614 615 usbd_get_xfer_status(xfer, NULL, NULL, &len, &stat); 616 617 if (status != USBD_NORMAL_COMPLETION) { 618 DPRINTFN(4,("ucycomwritecb: status = %d\n", status)); 619 goto error; 620 } 621 622 DPRINTFN(4,("ucycomwritecb: did %d/%d chars\n", sc->sc_wlen, len)); 623 624 s = spltty(); 625 CLR(tp->t_state, TS_BUSY); 626 if (ISSET(tp->t_state, TS_FLUSH)) 627 CLR(tp->t_state, TS_FLUSH); 628 else 629 ndflush(&tp->t_outq, sc->sc_wlen); 630 (*tp->t_linesw->l_start)(tp); 631 splx(s); 632 return; 633 634 error: 635 s = spltty(); 636 CLR(tp->t_state, TS_BUSY); 637 splx(s); 638 } 639 640 Static int 641 ucycomparam(struct tty *tp, struct termios *t) 642 { 643 struct ucycom_softc *sc = tp->t_sc; 644 uint32_t baud; 645 uint8_t cfg; 646 int err; 647 648 if (t->c_ospeed < 0) { 649 DPRINTF(("ucycomparam: c_ospeed < 0\n")); 650 return (EINVAL); 651 } 652 653 /* Check requested parameters. */ 654 if (t->c_ispeed && t->c_ispeed != t->c_ospeed) 655 return (EINVAL); 656 657 /* 658 * For the console, always force CLOCAL and !HUPCL, so that the port 659 * is always active. 660 */ 661 if (ISSET(sc->sc_swflags, TIOCFLAG_SOFTCAR)) { 662 SET(t->c_cflag, CLOCAL); 663 CLR(t->c_cflag, HUPCL); 664 } 665 666 /* 667 * If there were no changes, don't do anything. This avoids dropping 668 * input and improves performance when all we did was frob things like 669 * VMIN and VTIME. 670 */ 671 if (tp->t_ospeed == t->c_ospeed && 672 tp->t_cflag == t->c_cflag) 673 return (0); 674 675 /* XXX lcr = ISSET(sc->sc_lcr, LCR_SBREAK) | cflag2lcr(t->c_cflag); */ 676 677 /* And copy to tty. */ 678 tp->t_ispeed = 0; 679 tp->t_ospeed = t->c_ospeed; 680 tp->t_cflag = t->c_cflag; 681 682 baud = t->c_ispeed; 683 DPRINTF(("ucycomparam: baud=%d\n", baud)); 684 685 if (t->c_cflag & CIGNORE) { 686 cfg = sc->sc_cfg; 687 } else { 688 cfg = 0; 689 switch (t->c_cflag & CSIZE) { 690 case CS8: 691 cfg |= UCYCOM_DATA_BITS_8; 692 break; 693 case CS7: 694 cfg |= UCYCOM_DATA_BITS_7; 695 break; 696 case CS6: 697 cfg |= UCYCOM_DATA_BITS_6; 698 break; 699 case CS5: 700 cfg |= UCYCOM_DATA_BITS_5; 701 break; 702 default: 703 return (EINVAL); 704 } 705 cfg |= ISSET(t->c_cflag, CSTOPB) ? 706 UCYCOM_STOP_BITS_2 : UCYCOM_STOP_BITS_1; 707 cfg |= ISSET(t->c_cflag, PARENB) ? 708 UCYCOM_PARITY_ON : UCYCOM_PARITY_OFF; 709 cfg |= ISSET(t->c_cflag, PARODD) ? 710 UCYCOM_PARITY_ODD : UCYCOM_PARITY_EVEN; 711 } 712 713 /* 714 * Update the tty layer's idea of the carrier bit, in case we changed 715 * CLOCAL or MDMBUF. We don't hang up here; we only do that by 716 * explicit request. 717 */ 718 DPRINTF(("ucycomparam: l_modem\n")); 719 (void) (*tp->t_linesw->l_modem)(tp, 1 /* XXX carrier */ ); 720 721 err = ucycom_configure(sc, baud, cfg); 722 return (err); 723 } 724 725 void 726 ucycomstop(struct tty *tp, int flag) 727 { 728 DPRINTF(("ucycomstop: flag=%d\n", flag)); 729 } 730 731 int 732 ucycomread(dev_t dev, struct uio *uio, int flag) 733 { 734 struct ucycom_softc *sc = 735 device_lookup_private(&ucycom_cd, UCYCOMUNIT(dev)); 736 struct tty *tp = sc->sc_tty; 737 int err; 738 739 DPRINTF(("ucycomread: sc=%p, tp=%p, uio=%p, flag=%d\n", sc, tp, uio, 740 flag)); 741 if (sc->sc_dying) 742 return (EIO); 743 744 err = ((*tp->t_linesw->l_read)(tp, uio, flag)); 745 return (err); 746 } 747 748 749 int 750 ucycomwrite(dev_t dev, struct uio *uio, int flag) 751 { 752 struct ucycom_softc *sc = 753 device_lookup_private(&ucycom_cd, UCYCOMUNIT(dev)); 754 struct tty *tp = sc->sc_tty; 755 int err; 756 757 DPRINTF(("ucycomwrite: sc=%p, tp=%p, uio=%p, flag=%d\n", sc, tp, uio, 758 flag)); 759 if (sc->sc_dying) 760 return (EIO); 761 762 err = ((*tp->t_linesw->l_write)(tp, uio, flag)); 763 return (err); 764 } 765 766 struct tty * 767 ucycomtty(dev_t dev) 768 { 769 struct ucycom_softc *sc = 770 device_lookup_private(&ucycom_cd, UCYCOMUNIT(dev)); 771 struct tty *tp = sc->sc_tty; 772 773 DPRINTF(("ucycomtty: sc=%p, tp=%p\n", sc, tp)); 774 775 return (tp); 776 } 777 778 int 779 ucycomioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l) 780 { 781 struct ucycom_softc *sc = 782 device_lookup_private(&ucycom_cd, UCYCOMUNIT(dev)); 783 struct tty *tp = sc->sc_tty; 784 int err; 785 int s; 786 787 if (sc->sc_dying) 788 return (EIO); 789 790 DPRINTF(("ucycomioctl: sc=%p, tp=%p, data=%p\n", sc, tp, data)); 791 792 err = (*tp->t_linesw->l_ioctl)(tp, cmd, data, flag, l); 793 if (err != EPASSTHROUGH) 794 return (err); 795 796 err = ttioctl(tp, cmd, data, flag, l); 797 if (err != EPASSTHROUGH) 798 return (err); 799 800 err = 0; 801 802 DPRINTF(("ucycomioctl: our cmd=0x%08lx\n", cmd)); 803 s = spltty(); 804 805 switch (cmd) { 806 /* case TIOCSBRK: 807 ucycom_break(sc, 1); 808 break; 809 810 case TIOCCBRK: 811 ucycom_break(sc, 0); 812 break; 813 */ 814 case TIOCSDTR: 815 ucycom_dtr(sc, 1); 816 break; 817 818 case TIOCCDTR: 819 ucycom_dtr(sc, 0); 820 break; 821 822 case TIOCGFLAGS: 823 *(int *)data = sc->sc_swflags; 824 break; 825 826 case TIOCSFLAGS: 827 err = kauth_authorize_device_tty(l->l_cred, 828 KAUTH_DEVICE_TTY_PRIVSET, tp); 829 if (err) 830 break; 831 sc->sc_swflags = *(int *)data; 832 break; 833 834 case TIOCMSET: 835 case TIOCMBIS: 836 case TIOCMBIC: 837 tiocm_to_ucycom(sc, cmd, *(int *)data); 838 break; 839 840 case TIOCMGET: 841 *(int *)data = ucycom_to_tiocm(sc); 842 break; 843 844 default: 845 err = EPASSTHROUGH; 846 break; 847 } 848 849 splx(s); 850 851 return (err); 852 } 853 854 int 855 ucycompoll(dev_t dev, int events, struct lwp *l) 856 { 857 struct ucycom_softc *sc = 858 device_lookup_private(&ucycom_cd, UCYCOMUNIT(dev)); 859 struct tty *tp = sc->sc_tty; 860 int err; 861 862 DPRINTF(("ucycompoll: sc=%p, tp=%p, events=%d, lwp=%p\n", sc, tp, 863 events, l)); 864 865 if (sc->sc_dying) 866 return (EIO); 867 868 err = ((*tp->t_linesw->l_poll)(tp, events, l)); 869 return (err); 870 } 871 872 Static int 873 ucycom_configure(struct ucycom_softc *sc, uint32_t baud, uint8_t cfg) 874 { 875 uint8_t report[5]; 876 int err; 877 878 switch (baud) { 879 case 600: 880 case 1200: 881 case 2400: 882 case 4800: 883 case 9600: 884 case 19200: 885 case 38400: 886 case 57600: 887 #if 0 888 /* 889 * Stock chips only support standard baud rates in the 600 - 57600 890 * range, but higher rates can be achieved using custom firmware. 891 */ 892 case 115200: 893 case 153600: 894 case 192000: 895 #endif 896 break; 897 default: 898 return (EINVAL); 899 } 900 901 DPRINTF(("ucycom_configure: setting %d baud, %d-%c-%d (%d)\n", baud, 902 5 + (cfg & UCYCOM_DATA_MASK), 903 (cfg & UCYCOM_PARITY_MASK) ? 904 ((cfg & UCYCOM_PARITY_TYPE_MASK) ? 'O' : 'E') : 'N', 905 (cfg & UCYCOM_STOP_MASK) ? 2 : 1, cfg)); 906 907 report[0] = baud & 0xff; 908 report[1] = (baud >> 8) & 0xff; 909 report[2] = (baud >> 16) & 0xff; 910 report[3] = (baud >> 24) & 0xff; 911 report[4] = cfg; 912 err = uhidev_set_report(&sc->sc_hdev, UHID_FEATURE_REPORT, 913 report, sc->sc_flen); 914 if (err != 0) { 915 DPRINTF(("%s\n", usbd_errstr(err))); 916 return EIO; 917 } 918 sc->sc_baud = baud; 919 sc->sc_cfg = cfg; 920 921 #ifdef UCYCOM_DEBUG 922 ucycom_get_cfg(sc); 923 #endif 924 925 return 0; 926 } 927 928 Static void 929 ucycom_intr(struct uhidev *addr, void *ibuf, u_int len) 930 { 931 struct ucycom_softc *sc = (struct ucycom_softc *)addr; 932 struct tty *tp = sc->sc_tty; 933 int (*rint)(int , struct tty *) = tp->t_linesw->l_rint; 934 uint8_t *cp = ibuf; 935 int s, n, st, chg; 936 937 /* We understand 8 byte and 32 byte input records */ 938 switch (len) { 939 case 8: 940 n = cp[0] & UCYCOM_LMASK; 941 st = cp[0] & ~UCYCOM_LMASK; 942 cp++; 943 break; 944 945 case 32: 946 st = cp[0]; 947 n = cp[1]; 948 cp += 2; 949 break; 950 951 default: 952 DPRINTFN(3,("ucycom_intr: Unknown input report length\n")); 953 return; 954 } 955 956 #ifdef UCYCOM_DEBUG 957 if (ucycomdebug > 5) { 958 u_int32_t i; 959 960 if (n != 0) { 961 DPRINTF(("ucycom_intr: ibuf[0..%d) =", n)); 962 for (i = 0; i < n; i++) 963 DPRINTF((" %02x", cp[i])); 964 DPRINTF(("\n")); 965 } 966 } 967 #endif 968 969 /* Give characters to tty layer. */ 970 s = spltty(); 971 while (n-- > 0) { 972 DPRINTFN(7,("ucycom_intr: char=0x%02x\n", *cp)); 973 if ((*rint)(*cp++, tp) == -1) { 974 /* XXX what should we do? */ 975 aprint_error_dev(sc->sc_hdev.sc_dev, 976 "lost a character\n"); 977 break; 978 } 979 } 980 splx(s); 981 chg = st ^ sc->sc_msr; 982 sc->sc_msr = st; 983 if (ISSET(chg, UCYCOM_DCD)) 984 (*tp->t_linesw->l_modem)(tp, 985 ISSET(sc->sc_msr, UCYCOM_DCD)); 986 987 } 988 989 Static void 990 tiocm_to_ucycom(struct ucycom_softc *sc, u_long how, int ttybits) 991 { 992 u_char combits; 993 u_char before = sc->sc_mcr; 994 995 combits = 0; 996 if (ISSET(ttybits, TIOCM_DTR)) 997 SET(combits, UCYCOM_DTR); 998 if (ISSET(ttybits, TIOCM_RTS)) 999 SET(combits, UCYCOM_RTS); 1000 1001 switch (how) { 1002 case TIOCMBIC: 1003 CLR(sc->sc_mcr, combits); 1004 break; 1005 1006 case TIOCMBIS: 1007 SET(sc->sc_mcr, combits); 1008 break; 1009 1010 case TIOCMSET: 1011 CLR(sc->sc_mcr, UCYCOM_DTR | UCYCOM_RTS); 1012 SET(sc->sc_mcr, combits); 1013 break; 1014 } 1015 if (before ^ sc->sc_mcr) { 1016 DPRINTF(("tiocm_to_ucycom: something has changed\n")); 1017 ucycom_set_status(sc); 1018 } 1019 } 1020 1021 Static int 1022 ucycom_to_tiocm(struct ucycom_softc *sc) 1023 { 1024 u_char combits; 1025 int ttybits = 0; 1026 1027 combits = sc->sc_mcr; 1028 if (ISSET(combits, UCYCOM_DTR)) 1029 SET(ttybits, TIOCM_DTR); 1030 if (ISSET(combits, UCYCOM_RTS)) 1031 SET(ttybits, TIOCM_RTS); 1032 1033 combits = sc->sc_msr; 1034 if (ISSET(combits, UCYCOM_DCD)) 1035 SET(ttybits, TIOCM_CD); 1036 if (ISSET(combits, UCYCOM_CTS)) 1037 SET(ttybits, TIOCM_CTS); 1038 if (ISSET(combits, UCYCOM_DSR)) 1039 SET(ttybits, TIOCM_DSR); 1040 if (ISSET(combits, UCYCOM_RI)) 1041 SET(ttybits, TIOCM_RI); 1042 1043 return (ttybits); 1044 } 1045 1046 Static void 1047 ucycom_dtr(struct ucycom_softc *sc, int set) 1048 { 1049 uint8_t old; 1050 1051 old = sc->sc_mcr; 1052 if (set) 1053 SET(sc->sc_mcr, UCYCOM_DTR); 1054 else 1055 CLR(sc->sc_mcr, UCYCOM_DTR); 1056 1057 if (old ^ sc->sc_mcr) 1058 ucycom_set_status(sc); 1059 } 1060 1061 #if 0 1062 Static void 1063 ucycom_rts(struct ucycom_softc *sc, int set) 1064 { 1065 uint8_t old; 1066 1067 old = sc->sc_msr; 1068 if (set) 1069 SET(sc->sc_mcr, UCYCOM_RTS); 1070 else 1071 CLR(sc->sc_mcr, UCYCOM_RTS); 1072 1073 if (old ^ sc->sc_mcr) 1074 ucycom_set_status(sc); 1075 } 1076 #endif 1077 1078 Static void 1079 ucycom_set_status(struct ucycom_softc *sc) 1080 { 1081 int err; 1082 1083 if (sc->sc_olen != 8 && sc->sc_olen != 32) { 1084 DPRINTFN(2,("ucycom_set_status: unknown output report " 1085 "size (%zd)\n", sc->sc_olen)); 1086 return; 1087 } 1088 1089 DPRINTF(("ucycom_set_status: %d\n", sc->sc_mcr)); 1090 1091 memset(sc->sc_obuf, 0, sc->sc_olen); 1092 sc->sc_obuf[0] = sc->sc_mcr; 1093 1094 err = uhidev_write(sc->sc_hdev.sc_parent, sc->sc_obuf, sc->sc_olen); 1095 if (err) { 1096 DPRINTF(("ucycom_set_status: err=%d\n", err)); 1097 } 1098 } 1099 1100 #ifdef UCYCOM_DEBUG 1101 Static void 1102 ucycom_get_cfg(struct ucycom_softc *sc) 1103 { 1104 int err, cfg, baud; 1105 uint8_t report[5]; 1106 1107 err = uhidev_get_report(&sc->sc_hdev, UHID_FEATURE_REPORT, 1108 report, sc->sc_flen); 1109 if (err) { 1110 DPRINTF(("%s: failed\n", __func__)); 1111 return; 1112 } 1113 cfg = report[4]; 1114 baud = (report[3] << 24) + (report[2] << 16) + (report[1] << 8) + 1115 report[0]; 1116 DPRINTF(("ucycom_get_cfg: device reports %d baud, %d-%c-%d (%d)\n", 1117 baud, 5 + (cfg & UCYCOM_DATA_MASK), 1118 (cfg & UCYCOM_PARITY_MASK) ? 1119 ((cfg & UCYCOM_PARITY_TYPE_MASK) ? 'O' : 'E') : 'N', 1120 (cfg & UCYCOM_STOP_MASK) ? 2 : 1, cfg)); 1121 } 1122 #endif 1123 1124 Static void 1125 ucycom_cleanup(struct ucycom_softc *sc) 1126 { 1127 uint8_t *obuf; 1128 1129 DPRINTF(("ucycom_cleanup: closing uhidev\n")); 1130 1131 obuf = sc->sc_obuf; 1132 sc->sc_obuf = NULL; 1133 uhidev_close(&sc->sc_hdev); 1134 1135 if (obuf != NULL) 1136 free (obuf, M_USBDEV); 1137 } 1138