1 /* $NetBSD: ucycom.c,v 1.38 2014/03/16 05:20:29 dholland 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.38 2014/03/16 05:20:29 dholland 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 UCYCOMUNIT_MASK 0x3ffff 78 #define UCYCOMDIALOUT_MASK 0x80000 79 #define UCYCOMCALLUNIT_MASK 0x40000 80 81 #define UCYCOMUNIT(x) (minor(x) & UCYCOMUNIT_MASK) 82 #define UCYCOMDIALOUT(x) (minor(x) & UCYCOMDIALOUT_MASK) 83 #define UCYCOMCALLUNIT(x) (minor(x) & UCYCOMCALLUNIT_MASK) 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_flag = D_TTY 162 }; 163 164 Static int ucycomparam(struct tty *, struct termios *); 165 Static void ucycomstart(struct tty *); 166 Static void ucycomwritecb(usbd_xfer_handle, usbd_private_handle, usbd_status); 167 Static void ucycom_intr(struct uhidev *, void *, u_int); 168 Static int ucycom_configure(struct ucycom_softc *, uint32_t, uint8_t); 169 Static void tiocm_to_ucycom(struct ucycom_softc *, u_long, int); 170 Static int ucycom_to_tiocm(struct ucycom_softc *); 171 Static void ucycom_set_status(struct ucycom_softc *); 172 Static void ucycom_dtr(struct ucycom_softc *, int); 173 #if 0 174 Static void ucycom_rts(struct ucycom_softc *, int); 175 #endif 176 Static void ucycom_cleanup(struct ucycom_softc *sc); 177 178 #ifdef UCYCOM_DEBUG 179 Static void ucycom_get_cfg(struct ucycom_softc *); 180 #endif 181 182 Static const struct usb_devno ucycom_devs[] = { 183 { USB_VENDOR_CYPRESS, USB_PRODUCT_CYPRESS_USBRS232 }, 184 { USB_VENDOR_DELORME, USB_PRODUCT_DELORME_EARTHMATE }, 185 }; 186 #define ucycom_lookup(v, p) usb_lookup(ucycom_devs, v, p) 187 188 int ucycom_match(device_t, cfdata_t, void *); 189 void ucycom_attach(device_t, device_t, void *); 190 int ucycom_detach(device_t, int); 191 int ucycom_activate(device_t, enum devact); 192 extern struct cfdriver ucycom_cd; 193 CFATTACH_DECL_NEW(ucycom, sizeof(struct ucycom_softc), ucycom_match, ucycom_attach, ucycom_detach, ucycom_activate); 194 195 int 196 ucycom_match(device_t parent, cfdata_t match, void *aux) 197 { 198 struct uhidev_attach_arg *uha = aux; 199 200 return (ucycom_lookup(uha->uaa->vendor, uha->uaa->product) != NULL ? 201 UMATCH_VENDOR_PRODUCT : UMATCH_NONE); 202 } 203 204 void 205 ucycom_attach(device_t parent, device_t self, void *aux) 206 { 207 struct ucycom_softc *sc = device_private(self); 208 struct uhidev_attach_arg *uha = aux; 209 int size, repid; 210 void *desc; 211 212 sc->sc_hdev.sc_dev = self; 213 sc->sc_hdev.sc_intr = ucycom_intr; 214 sc->sc_hdev.sc_parent = uha->parent; 215 sc->sc_hdev.sc_report_id = uha->reportid; 216 217 uhidev_get_report_desc(uha->parent, &desc, &size); 218 repid = uha->reportid; 219 sc->sc_ilen = hid_report_size(desc, size, hid_input, repid); 220 sc->sc_olen = hid_report_size(desc, size, hid_output, repid); 221 sc->sc_flen = hid_report_size(desc, size, hid_feature, repid); 222 223 sc->sc_msr = sc->sc_mcr = 0; 224 225 /* set up tty */ 226 sc->sc_tty = tty_alloc(); 227 sc->sc_tty->t_sc = sc; 228 sc->sc_tty->t_oproc = ucycomstart; 229 sc->sc_tty->t_param = ucycomparam; 230 231 tty_attach(sc->sc_tty); 232 233 /* Nothing interesting to report */ 234 aprint_normal("\n"); 235 } 236 237 238 int 239 ucycom_detach(device_t self, int flags) 240 { 241 struct ucycom_softc *sc = device_private(self); 242 struct tty *tp = sc->sc_tty; 243 int maj, mn; 244 int s; 245 246 DPRINTF(("ucycom_detach: sc=%p flags=%d tp=%p\n", sc, flags, tp)); 247 248 sc->sc_dying = 1; 249 250 s = splusb(); 251 if (tp != NULL) { 252 mutex_spin_enter(&tty_lock); 253 CLR(tp->t_state, TS_CARR_ON); 254 CLR(tp->t_cflag, CLOCAL | MDMBUF); 255 ttyflush(tp, FREAD|FWRITE); 256 mutex_spin_exit(&tty_lock); 257 } 258 /* Wait for processes to go away. */ 259 usb_detach_waitold(sc->sc_hdev.sc_dev); 260 splx(s); 261 262 /* locate the major number */ 263 maj = cdevsw_lookup_major(&ucycom_cdevsw); 264 265 /* Nuke the vnodes for any open instances. */ 266 mn = device_unit(self); 267 268 DPRINTFN(2, ("ucycom_detach: maj=%d mn=%d\n", maj, mn)); 269 vdevgone(maj, mn, mn, VCHR); 270 vdevgone(maj, mn | UCYCOMDIALOUT_MASK, mn | UCYCOMDIALOUT_MASK, VCHR); 271 vdevgone(maj, mn | UCYCOMCALLUNIT_MASK, mn | UCYCOMCALLUNIT_MASK, VCHR); 272 273 /* Detach and free the tty. */ 274 if (tp != NULL) { 275 DPRINTF(("ucycom_detach: tty_detach %p\n", tp)); 276 tty_detach(tp); 277 tty_free(tp); 278 sc->sc_tty = NULL; 279 } 280 281 return 0; 282 } 283 284 int 285 ucycom_activate(device_t self, enum devact act) 286 { 287 struct ucycom_softc *sc = device_private(self); 288 289 DPRINTFN(5,("ucycom_activate: %d\n", act)); 290 291 switch (act) { 292 case DVACT_DEACTIVATE: 293 sc->sc_dying = 1; 294 return 0; 295 default: 296 return EOPNOTSUPP; 297 } 298 } 299 300 #if 0 301 void 302 ucycom_shutdown(struct ucycom_softc *sc) 303 { 304 struct tty *tp = sc->sc_tty; 305 306 DPRINTF(("ucycom_shutdown\n")); 307 /* 308 * Hang up if necessary. Wait a bit, so the other side has time to 309 * notice even if we immediately open the port again. 310 */ 311 if (ISSET(tp->t_cflag, HUPCL)) { 312 ucycom_dtr(sc, 0); 313 (void)tsleep(sc, TTIPRI, ttclos, hz); 314 } 315 } 316 #endif 317 318 int 319 ucycomopen(dev_t dev, int flag, int mode, struct lwp *l) 320 { 321 struct ucycom_softc *sc = 322 device_lookup_private(&ucycom_cd, UCYCOMUNIT(dev)); 323 struct tty *tp; 324 int s, err; 325 326 DPRINTF(("ucycomopen: unit=%d\n", UCYCOMUNIT(dev))); 327 DPRINTF(("ucycomopen: sc=%p\n", sc)); 328 329 if (sc == NULL) 330 return (ENXIO); 331 332 if (sc->sc_dying) 333 return (EIO); 334 335 if (!device_is_active(sc->sc_hdev.sc_dev)) 336 return (ENXIO); 337 338 tp = sc->sc_tty; 339 340 DPRINTF(("ucycomopen: tp=%p\n", tp)); 341 342 if (kauth_authorize_device_tty(l->l_cred, KAUTH_DEVICE_TTY_OPEN, tp)) 343 return (EBUSY); 344 345 s = spltty(); 346 347 if (!ISSET(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0) { 348 struct termios t; 349 350 tp->t_dev = dev; 351 352 err = uhidev_open(&sc->sc_hdev); 353 if (err) { 354 /* Any cleanup? */ 355 splx(s); 356 return (err); 357 } 358 359 /* 360 * Initialize the termios status to the defaults. Add in the 361 * sticky bits from TIOCSFLAGS. 362 */ 363 t.c_ispeed = 0; 364 t.c_ospeed = TTYDEF_SPEED; 365 t.c_cflag = TTYDEF_CFLAG; 366 if (ISSET(sc->sc_swflags, TIOCFLAG_CLOCAL)) 367 SET(t.c_cflag, CLOCAL); 368 if (ISSET(sc->sc_swflags, TIOCFLAG_CRTSCTS)) 369 SET(t.c_cflag, CRTSCTS); 370 if (ISSET(sc->sc_swflags, TIOCFLAG_MDMBUF)) 371 SET(t.c_cflag, MDMBUF); 372 373 tp->t_ospeed = 0; 374 (void) ucycomparam(tp, &t); 375 tp->t_iflag = TTYDEF_IFLAG; 376 tp->t_oflag = TTYDEF_OFLAG; 377 tp->t_lflag = TTYDEF_LFLAG; 378 ttychars(tp); 379 ttsetwater(tp); 380 381 /* Allocate an output report buffer */ 382 sc->sc_obuf = malloc(sc->sc_olen, M_USBDEV, M_WAITOK); 383 384 DPRINTF(("ucycomopen: sc->sc_obuf=%p\n", sc->sc_obuf)); 385 386 #if 0 387 /* XXX Don't do this as for some reason trying to do an 388 * XXX interrupt out transfer at this point means everything 389 * XXX gets stuck!?! 390 */ 391 /* 392 * Turn on DTR. We must always do this, even if carrier is not 393 * present, because otherwise we'd have to use TIOCSDTR 394 * immediately after setting CLOCAL, which applications do not 395 * expect. We always assert DTR while the device is open 396 * unless explicitly requested to deassert it. 397 */ 398 ucycom_dtr(sc, 1); 399 #endif 400 401 #if 0 402 /* XXX CLR(sc->sc_rx_flags, RX_ANY_BLOCK);*/ 403 ucycom_hwiflow(sc); 404 #endif 405 406 } 407 splx(s); 408 409 err = ttyopen(tp, UCYCOMDIALOUT(dev), ISSET(flag, O_NONBLOCK)); 410 if (err) 411 goto bad; 412 413 err = (*tp->t_linesw->l_open)(dev, tp); 414 if (err) 415 goto bad; 416 417 return (0); 418 419 bad: 420 if (!ISSET(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0) { 421 /* 422 * We failed to open the device, and nobody else had it opened. 423 * Clean up the state as appropriate. 424 */ 425 ucycom_cleanup(sc); 426 } 427 428 return (err); 429 430 } 431 432 433 int 434 ucycomclose(dev_t dev, int flag, int mode, struct lwp *l) 435 { 436 struct ucycom_softc *sc = 437 device_lookup_private(&ucycom_cd, UCYCOMUNIT(dev)); 438 struct tty *tp = sc->sc_tty; 439 440 DPRINTF(("ucycomclose: unit=%d\n", UCYCOMUNIT(dev))); 441 if (!ISSET(tp->t_state, TS_ISOPEN)) 442 return (0); 443 444 (*tp->t_linesw->l_close)(tp, flag); 445 ttyclose(tp); 446 447 if (!ISSET(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0) { 448 /* 449 * Although we got a last close, the device may still be in 450 * use; e.g. if this was the dialout node, and there are still 451 * processes waiting for carrier on the non-dialout node. 452 */ 453 ucycom_cleanup(sc); 454 } 455 456 return (0); 457 } 458 459 Static void 460 ucycomstart(struct tty *tp) 461 { 462 struct ucycom_softc *sc = 463 device_lookup_private(&ucycom_cd, UCYCOMUNIT(tp->t_dev)); 464 usbd_status err __unused; 465 u_char *data; 466 int cnt, len, s; 467 468 if (sc->sc_dying) 469 return; 470 471 s = spltty(); 472 if (ISSET(tp->t_state, TS_BUSY | TS_TIMEOUT | TS_TTSTOP)) { 473 DPRINTFN(4,("ucycomstart: no go, state=0x%x\n", tp->t_state)); 474 goto out; 475 } 476 477 #if 0 478 /* HW FLOW CTL */ 479 if (sc->sc_tx_stopped) 480 goto out; 481 #endif 482 483 if (ttypull(tp) == 0) 484 goto out; 485 486 /* Grab the first contiguous region of buffer space. */ 487 data = tp->t_outq.c_cf; 488 cnt = ndqb(&tp->t_outq, 0); 489 490 if (cnt == 0) { 491 DPRINTF(("ucycomstart: cnt == 0\n")); 492 goto out; 493 } 494 495 SET(tp->t_state, TS_BUSY); 496 497 /* 498 * The 8 byte output report uses byte 0 for control and byte 499 * count. 500 * 501 * The 32 byte output report uses byte 0 for control. Byte 1 502 * is used for byte count. 503 */ 504 memset(sc->sc_obuf, 0, sc->sc_olen); 505 len = cnt; 506 switch (sc->sc_olen) { 507 case 8: 508 if (cnt > sc->sc_olen - 1) { 509 DPRINTF(("ucycomstart(8): big buffer %d chars\n", len)); 510 len = sc->sc_olen - 1; 511 } 512 513 memcpy(sc->sc_obuf + 1, data, len); 514 sc->sc_obuf[0] = len | sc->sc_mcr; 515 516 DPRINTF(("ucycomstart(8): sc->sc_obuf[0] = %d | %d = %d\n", len, 517 sc->sc_mcr, sc->sc_obuf[0])); 518 #ifdef UCYCOM_DEBUG 519 if (ucycomdebug > 10) { 520 u_int32_t i; 521 u_int8_t *d = data; 522 523 DPRINTF(("ucycomstart(8): data =")); 524 for (i = 0; i < len; i++) 525 DPRINTF((" %02x", d[i])); 526 DPRINTF(("\n")); 527 } 528 #endif 529 break; 530 531 case 32: 532 if (cnt > sc->sc_olen - 2) { 533 DPRINTF(("ucycomstart(32): big buffer %d chars\n", 534 len)); 535 len = sc->sc_olen - 2; 536 } 537 538 memcpy(sc->sc_obuf + 2, data, len); 539 sc->sc_obuf[0] = sc->sc_mcr; 540 sc->sc_obuf[1] = len; 541 DPRINTF(("ucycomstart(32): sc->sc_obuf[0] = %d\n" 542 "sc->sc_obuf[1] = %d\n", sc->sc_obuf[0], sc->sc_obuf[1])); 543 #ifdef UCYCOM_DEBUG 544 if (ucycomdebug > 10) { 545 u_int32_t i; 546 u_int8_t *d = data; 547 548 DPRINTF(("ucycomstart(32): data =")); 549 for (i = 0; i < len; i++) 550 DPRINTF((" %02x", d[i])); 551 DPRINTF(("\n")); 552 } 553 #endif 554 break; 555 556 default: 557 DPRINTFN(2,("ucycomstart: unknown output report size (%zd)\n", 558 sc->sc_olen)); 559 goto out; 560 } 561 splx(s); 562 sc->sc_wlen = len; 563 564 #ifdef UCYCOM_DEBUG 565 if (ucycomdebug > 5) { 566 int i; 567 568 if (len != 0) { 569 DPRINTF(("ucycomstart: sc->sc_obuf[0..%zd) =", 570 sc->sc_olen)); 571 for (i = 0; i < sc->sc_olen; i++) 572 DPRINTF((" %02x", sc->sc_obuf[i])); 573 DPRINTF(("\n")); 574 } 575 } 576 #endif 577 DPRINTFN(4,("ucycomstart: %d chars\n", len)); 578 usbd_setup_xfer(sc->sc_hdev.sc_parent->sc_oxfer, 579 sc->sc_hdev.sc_parent->sc_opipe, (usbd_private_handle)sc, 580 sc->sc_obuf, sc->sc_olen, 0 /* USBD_NO_COPY */, USBD_NO_TIMEOUT, 581 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(usbd_xfer_handle xfer, usbd_private_handle 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 u_int32_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 DPRINTF(("ucycom_cleanup: closing uhidev\n")); 1127 1128 if (sc->sc_obuf !=NULL) 1129 free (sc->sc_obuf, M_USBDEV); 1130 uhidev_close(&sc->sc_hdev); 1131 } 1132