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