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