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