1 /* $NetBSD: ucycom.c,v 1.23 2008/05/24 16:40:58 cube 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.23 2008/05/24 16:40:58 cube 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 interrupt 371 * XXX out transfer at this point means everything gets stuck!?! 372 */ 373 /* 374 * Turn on DTR. We must always do this, even if carrier is not 375 * present, because otherwise we'd have to use TIOCSDTR 376 * immediately after setting CLOCAL, which applications do not 377 * expect. We always assert DTR while the device is open 378 * unless explicitly requested to deassert it. 379 */ 380 ucycom_dtr(sc, 1); 381 #endif 382 383 #if 0 384 /* XXX CLR(sc->sc_rx_flags, RX_ANY_BLOCK);*/ 385 ucycom_hwiflow(sc); 386 #endif 387 388 } 389 splx(s); 390 391 err = ttyopen(tp, UCYCOMDIALOUT(dev), ISSET(flag, O_NONBLOCK)); 392 if (err) 393 goto bad; 394 395 err = (*tp->t_linesw->l_open)(dev, tp); 396 if (err) 397 goto bad; 398 399 return (0); 400 401 bad: 402 if (!ISSET(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0) { 403 /* 404 * We failed to open the device, and nobody else had it opened. 405 * Clean up the state as appropriate. 406 */ 407 ucycom_cleanup(sc); 408 } 409 410 return (err); 411 412 } 413 414 415 int 416 ucycomclose(dev_t dev, int flag, int mode, struct lwp *l) 417 { 418 struct ucycom_softc *sc = 419 device_lookup_private(&ucycom_cd, UCYCOMUNIT(dev)); 420 struct tty *tp = sc->sc_tty; 421 422 DPRINTF(("ucycomclose: unit=%d\n", UCYCOMUNIT(dev))); 423 if (!ISSET(tp->t_state, TS_ISOPEN)) 424 return (0); 425 426 (*tp->t_linesw->l_close)(tp, flag); 427 ttyclose(tp); 428 429 if (!ISSET(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0) { 430 /* 431 * Although we got a last close, the device may still be in 432 * use; e.g. if this was the dialout node, and there are still 433 * processes waiting for carrier on the non-dialout node. 434 */ 435 ucycom_cleanup(sc); 436 } 437 438 return (0); 439 } 440 441 Static void 442 ucycomstart(struct tty *tp) 443 { 444 struct ucycom_softc *sc = 445 device_lookup_private(&ucycom_cd, UCYCOMUNIT(tp->t_dev)); 446 u_char *data; 447 int cnt, len, err, s; 448 449 if (sc->sc_dying) 450 return; 451 452 s = spltty(); 453 if (ISSET(tp->t_state, TS_BUSY | TS_TIMEOUT | TS_TTSTOP)) { 454 DPRINTFN(4,("ucycomstart: no go, state=0x%x\n", tp->t_state)); 455 goto out; 456 } 457 458 #if 0 459 /* HW FLOW CTL */ 460 if (sc->sc_tx_stopped) 461 goto out; 462 #endif 463 464 if (ttypull(tp) == 0) 465 goto out; 466 467 /* Grab the first contiguous region of buffer space. */ 468 data = tp->t_outq.c_cf; 469 cnt = ndqb(&tp->t_outq, 0); 470 471 if (cnt == 0) { 472 DPRINTF(("ucycomstart: cnt == 0\n")); 473 goto out; 474 } 475 476 SET(tp->t_state, TS_BUSY); 477 478 /* 479 * The 8 byte output report uses byte 0 for control and byte 480 * count. 481 * 482 * The 32 byte output report uses byte 0 for control. Byte 1 483 * is used for byte count. 484 */ 485 memset(sc->sc_obuf, 0, sc->sc_olen); 486 len = cnt; 487 switch (sc->sc_olen) { 488 case 8: 489 if (cnt > sc->sc_olen - 1) { 490 DPRINTF(("ucycomstart(8): big buffer %d chars\n", len)); 491 len = sc->sc_olen - 1; 492 } 493 494 memcpy(sc->sc_obuf + 1, data, len); 495 sc->sc_obuf[0] = len | sc->sc_mcr; 496 497 DPRINTF(("ucycomstart(8): sc->sc_obuf[0] = %d | %d = %d\n", len, sc->sc_mcr, sc->sc_obuf[0])); 498 #ifdef UCYCOM_DEBUG 499 if (ucycomdebug > 10) { 500 u_int32_t i; 501 u_int8_t *d = data; 502 503 DPRINTF(("ucycomstart(8): data =")); 504 for (i = 0; i < len; i++) 505 DPRINTF((" %02x", d[i])); 506 DPRINTF(("\n")); 507 } 508 #endif 509 break; 510 511 case 32: 512 if (cnt > sc->sc_olen - 2) { 513 DPRINTF(("ucycomstart(32): big buffer %d chars\n", len)); 514 len = sc->sc_olen - 2; 515 } 516 517 memcpy(sc->sc_obuf + 2, data, len); 518 sc->sc_obuf[0] = sc->sc_mcr; 519 sc->sc_obuf[1] = len; 520 DPRINTF(("ucycomstart(32): sc->sc_obuf[0] = %d\nsc->sc_obuf[1] = %d\n", sc->sc_obuf[0], sc->sc_obuf[1])); 521 #ifdef UCYCOM_DEBUG 522 if (ucycomdebug > 10) { 523 u_int32_t i; 524 u_int8_t *d = data; 525 526 DPRINTF(("ucycomstart(32): data =")); 527 for (i = 0; i < len; i++) 528 DPRINTF((" %02x", d[i])); 529 DPRINTF(("\n")); 530 } 531 #endif 532 break; 533 534 default: 535 DPRINTFN(2,("ucycomstart: unknown output report size (%zd)\n", 536 sc->sc_olen)); 537 goto out; 538 } 539 splx(s); 540 541 #ifdef UCYCOM_DEBUG 542 if (ucycomdebug > 5) { 543 int i; 544 545 if (len != 0) { 546 DPRINTF(("ucycomstart: sc->sc_obuf[0..%zd) =", sc->sc_olen)); 547 for (i = 0; i < sc->sc_olen; i++) 548 DPRINTF((" %02x", sc->sc_obuf[i])); 549 DPRINTF(("\n")); 550 } 551 } 552 #endif 553 err = uhidev_write(sc->sc_hdev.sc_parent, sc->sc_obuf, sc->sc_olen); 554 555 if (err) { 556 DPRINTF(("ucycomstart: error doing uhidev_write = %d\n", err)); 557 } 558 559 #ifdef UCYCOM_DEBUG 560 ucycom_get_cfg(sc); 561 #endif 562 DPRINTFN(4,("ucycomstart: req %d chars did %d chars\n", cnt, len)); 563 564 s = spltty(); 565 CLR(tp->t_state, TS_BUSY); 566 if (ISSET(tp->t_state, TS_FLUSH)) 567 CLR(tp->t_state, TS_FLUSH); 568 else 569 ndflush(&tp->t_outq, len); 570 (*tp->t_linesw->l_start)(tp); 571 572 out: 573 splx(s); 574 } 575 576 Static int 577 ucycomparam(struct tty *tp, struct termios *t) 578 { 579 struct ucycom_softc *sc = tp->t_sc; 580 uint32_t baud; 581 uint8_t cfg; 582 int err; 583 584 if (t->c_ospeed < 0) { 585 DPRINTF(("ucycomparam: c_ospeed < 0\n")); 586 return (EINVAL); 587 } 588 589 /* Check requested parameters. */ 590 if (t->c_ispeed && t->c_ispeed != t->c_ospeed) 591 return (EINVAL); 592 593 /* 594 * For the console, always force CLOCAL and !HUPCL, so that the port 595 * is always active. 596 */ 597 if (ISSET(sc->sc_swflags, TIOCFLAG_SOFTCAR)) { 598 SET(t->c_cflag, CLOCAL); 599 CLR(t->c_cflag, HUPCL); 600 } 601 602 /* 603 * If there were no changes, don't do anything. This avoids dropping 604 * input and improves performance when all we did was frob things like 605 * VMIN and VTIME. 606 */ 607 if (tp->t_ospeed == t->c_ospeed && 608 tp->t_cflag == t->c_cflag) 609 return (0); 610 611 /* XXX lcr = ISSET(sc->sc_lcr, LCR_SBREAK) | cflag2lcr(t->c_cflag); */ 612 613 /* And copy to tty. */ 614 tp->t_ispeed = 0; 615 tp->t_ospeed = t->c_ospeed; 616 tp->t_cflag = t->c_cflag; 617 618 baud = t->c_ispeed; 619 DPRINTF(("ucycomparam: baud=%d\n", baud)); 620 621 if (t->c_cflag & CIGNORE) { 622 cfg = sc->sc_cfg; 623 } else { 624 cfg = 0; 625 switch (t->c_cflag & CSIZE) { 626 case CS8: 627 cfg |= UCYCOM_DATA_BITS_8; 628 break; 629 case CS7: 630 cfg |= UCYCOM_DATA_BITS_7; 631 break; 632 case CS6: 633 cfg |= UCYCOM_DATA_BITS_6; 634 break; 635 case CS5: 636 cfg |= UCYCOM_DATA_BITS_5; 637 break; 638 default: 639 return (EINVAL); 640 } 641 cfg |= ISSET(t->c_cflag, CSTOPB) ? 642 UCYCOM_STOP_BITS_2 : UCYCOM_STOP_BITS_1; 643 cfg |= ISSET(t->c_cflag, PARENB) ? 644 UCYCOM_PARITY_ON : UCYCOM_PARITY_OFF; 645 cfg |= ISSET(t->c_cflag, PARODD) ? 646 UCYCOM_PARITY_ODD : UCYCOM_PARITY_EVEN; 647 } 648 649 /* 650 * Update the tty layer's idea of the carrier bit, in case we changed 651 * CLOCAL or MDMBUF. We don't hang up here; we only do that by 652 * explicit request. 653 */ 654 DPRINTF(("ucycomparam: l_modem\n")); 655 (void) (*tp->t_linesw->l_modem)(tp, 1 /* XXX carrier */ ); 656 657 err = ucycom_configure(sc, baud, cfg); 658 return (err); 659 } 660 661 void 662 ucycomstop(struct tty *tp, int flag) 663 { 664 DPRINTF(("ucycomstop: flag=%d\n", flag)); 665 } 666 667 int 668 ucycomread(dev_t dev, struct uio *uio, int flag) 669 { 670 struct ucycom_softc *sc = 671 device_lookup_private(&ucycom_cd, UCYCOMUNIT(dev)); 672 struct tty *tp = sc->sc_tty; 673 int err; 674 675 DPRINTF(("ucycomread: sc=%p, tp=%p, uio=%p, flag=%d\n", sc, tp, uio, flag)); 676 if (sc->sc_dying) 677 return (EIO); 678 679 err = ((*tp->t_linesw->l_read)(tp, uio, flag)); 680 return (err); 681 } 682 683 684 int 685 ucycomwrite(dev_t dev, struct uio *uio, int flag) 686 { 687 struct ucycom_softc *sc = 688 device_lookup_private(&ucycom_cd, UCYCOMUNIT(dev)); 689 struct tty *tp = sc->sc_tty; 690 int err; 691 692 DPRINTF(("ucycomwrite: 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_write)(tp, uio, flag)); 697 return (err); 698 } 699 700 struct tty * 701 ucycomtty(dev_t dev) 702 { 703 struct ucycom_softc *sc = 704 device_lookup_private(&ucycom_cd, UCYCOMUNIT(dev)); 705 struct tty *tp = sc->sc_tty; 706 707 DPRINTF(("ucycomtty: sc=%p, tp=%p\n", sc, tp)); 708 709 return (tp); 710 } 711 712 int 713 ucycomioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l) 714 { 715 struct ucycom_softc *sc = 716 device_lookup_private(&ucycom_cd, 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 = 792 device_lookup_private(&ucycom_cd, UCYCOMUNIT(dev)); 793 struct tty *tp = sc->sc_tty; 794 int err; 795 796 DPRINTF(("ucycompoll: sc=%p, tp=%p, events=%d, lwp=%p\n", sc, tp, events, l)); 797 798 if (sc->sc_dying) 799 return (EIO); 800 801 err = ((*tp->t_linesw->l_poll)(tp, events, l)); 802 return (err); 803 } 804 805 Static int 806 ucycom_configure(struct ucycom_softc *sc, uint32_t baud, uint8_t cfg) 807 { 808 uint8_t report[5]; 809 int err; 810 811 switch (baud) { 812 case 600: 813 case 1200: 814 case 2400: 815 case 4800: 816 case 9600: 817 case 19200: 818 case 38400: 819 case 57600: 820 #if 0 821 /* 822 * Stock chips only support standard baud rates in the 600 - 57600 823 * range, but higher rates can be achieved using custom firmware. 824 */ 825 case 115200: 826 case 153600: 827 case 192000: 828 #endif 829 break; 830 default: 831 return (EINVAL); 832 } 833 834 DPRINTF(("ucycom_configure: setting %d baud, %d-%c-%d (%d)\n", baud, 835 5 + (cfg & UCYCOM_DATA_MASK), 836 (cfg & UCYCOM_PARITY_MASK) ? 837 ((cfg & UCYCOM_PARITY_TYPE_MASK) ? 'O' : 'E') : 'N', 838 (cfg & UCYCOM_STOP_MASK) ? 2 : 1, cfg)); 839 840 report[0] = baud & 0xff; 841 report[1] = (baud >> 8) & 0xff; 842 report[2] = (baud >> 16) & 0xff; 843 report[3] = (baud >> 24) & 0xff; 844 report[4] = cfg; 845 err = uhidev_set_report(&sc->sc_hdev, UHID_FEATURE_REPORT, 846 report, sc->sc_flen); 847 if (err != 0) { 848 DPRINTF(("%s\n", usbd_errstr(err))); 849 return EIO; 850 } 851 sc->sc_baud = baud; 852 sc->sc_cfg = cfg; 853 854 return 0; 855 } 856 857 Static void 858 ucycom_intr(struct uhidev *addr, void *ibuf, u_int len) 859 { 860 struct ucycom_softc *sc = (struct ucycom_softc *)addr; 861 struct tty *tp = sc->sc_tty; 862 int (*rint)(int , struct tty *) = tp->t_linesw->l_rint; 863 uint8_t *cp = ibuf; 864 int s, n, st, chg; 865 866 /* We understand 8 byte and 32 byte input records */ 867 switch (len) { 868 case 8: 869 n = cp[0] & UCYCOM_LMASK; 870 st = cp[0] & ~UCYCOM_LMASK; 871 cp++; 872 break; 873 874 case 32: 875 st = cp[0]; 876 n = cp[1]; 877 cp += 2; 878 break; 879 880 default: 881 DPRINTFN(3,("ucycom_intr: Unknown input report length\n")); 882 return; 883 } 884 885 #ifdef UCYCOM_DEBUG 886 if (ucycomdebug > 5) { 887 u_int32_t i; 888 889 if (n != 0) { 890 DPRINTF(("ucycom_intr: ibuf[0..%d) =", n)); 891 for (i = 0; i < n; i++) 892 DPRINTF((" %02x", cp[i])); 893 DPRINTF(("\n")); 894 } 895 } 896 #endif 897 s = spltty(); 898 899 /* Give characters to tty layer. */ 900 while (n-- > 0) { 901 DPRINTFN(7,("ucycom_intr: char=0x%02x\n", *cp)); 902 if ((*rint)(*cp++, tp) == -1) { 903 /* XXX what should we do? */ 904 aprint_error_dev(sc->sc_hdev.sc_dev, 905 "lost a character\n"); 906 break; 907 } 908 } 909 splx(s); 910 chg = st ^ sc->sc_msr; 911 sc->sc_msr = st; 912 if (ISSET(chg, UCYCOM_DCD)) 913 (*tp->t_linesw->l_modem)(tp, 914 ISSET(sc->sc_msr, UCYCOM_DCD)); 915 916 } 917 918 Static void 919 tiocm_to_ucycom(struct ucycom_softc *sc, u_long how, int ttybits) 920 { 921 u_char combits; 922 u_char before = sc->sc_mcr; 923 924 combits = 0; 925 if (ISSET(ttybits, TIOCM_DTR)) 926 SET(combits, UCYCOM_DTR); 927 if (ISSET(ttybits, TIOCM_RTS)) 928 SET(combits, UCYCOM_RTS); 929 930 switch (how) { 931 case TIOCMBIC: 932 CLR(sc->sc_mcr, combits); 933 break; 934 935 case TIOCMBIS: 936 SET(sc->sc_mcr, combits); 937 break; 938 939 case TIOCMSET: 940 CLR(sc->sc_mcr, UCYCOM_DTR | UCYCOM_RTS); 941 SET(sc->sc_mcr, combits); 942 break; 943 } 944 if (before ^ sc->sc_mcr) { 945 DPRINTF(("tiocm_to_ucycom: something has changed\n")); 946 ucycom_set_status(sc); 947 } 948 } 949 950 Static int 951 ucycom_to_tiocm(struct ucycom_softc *sc) 952 { 953 u_char combits; 954 int ttybits = 0; 955 956 combits = sc->sc_mcr; 957 if (ISSET(combits, UCYCOM_DTR)) 958 SET(ttybits, TIOCM_DTR); 959 if (ISSET(combits, UCYCOM_RTS)) 960 SET(ttybits, TIOCM_RTS); 961 962 combits = sc->sc_msr; 963 if (ISSET(combits, UCYCOM_DCD)) 964 SET(ttybits, TIOCM_CD); 965 if (ISSET(combits, UCYCOM_CTS)) 966 SET(ttybits, TIOCM_CTS); 967 if (ISSET(combits, UCYCOM_DSR)) 968 SET(ttybits, TIOCM_DSR); 969 if (ISSET(combits, UCYCOM_RI)) 970 SET(ttybits, TIOCM_RI); 971 972 return (ttybits); 973 } 974 975 Static void 976 ucycom_dtr(struct ucycom_softc *sc, int set) 977 { 978 uint8_t old; 979 980 old = sc->sc_mcr; 981 if (set) 982 SET(sc->sc_mcr, UCYCOM_DTR); 983 else 984 CLR(sc->sc_mcr, UCYCOM_DTR); 985 986 if (old ^ sc->sc_mcr) 987 ucycom_set_status(sc); 988 } 989 990 #if 0 991 Static void 992 ucycom_rts(struct ucycom_softc *sc, int set) 993 { 994 uint8_t old; 995 996 old = sc->sc_msr; 997 if (set) 998 SET(sc->sc_mcr, UCYCOM_RTS); 999 else 1000 CLR(sc->sc_mcr, UCYCOM_RTS); 1001 1002 if (old ^ sc->sc_mcr) 1003 ucycom_set_status(sc); 1004 } 1005 #endif 1006 1007 Static void 1008 ucycom_set_status(struct ucycom_softc *sc) 1009 { 1010 int err; 1011 1012 if (sc->sc_olen != 8 && sc->sc_olen != 32) { 1013 DPRINTFN(2,("ucycom_set_status: unknown output report size (%zd)\n", 1014 sc->sc_olen)); 1015 return; 1016 } 1017 1018 DPRINTF(("ucycom_set_status: %d\n", sc->sc_mcr)); 1019 1020 memset(sc->sc_obuf, 0, sc->sc_olen); 1021 sc->sc_obuf[0] = sc->sc_mcr; 1022 1023 err = uhidev_write(sc->sc_hdev.sc_parent, sc->sc_obuf, sc->sc_olen); 1024 if (err) { 1025 DPRINTF(("ucycom_set_status: err=%d\n", err)); 1026 } 1027 } 1028 1029 #ifdef UCYCOM_DEBUG 1030 Static void 1031 ucycom_get_cfg(struct ucycom_softc *sc) 1032 { 1033 int err, cfg, baud; 1034 uint8_t report[5]; 1035 1036 err = uhidev_get_report(&sc->sc_hdev, UHID_FEATURE_REPORT, 1037 report, sc->sc_flen); 1038 cfg = report[4]; 1039 baud = (report[3] << 24) + (report[2] << 16) + (report[1] << 8) + report[0]; 1040 DPRINTF(("ucycom_configure: device reports %d baud, %d-%c-%d (%d)\n", baud, 1041 5 + (cfg & UCYCOM_DATA_MASK), 1042 (cfg & UCYCOM_PARITY_MASK) ? 1043 ((cfg & UCYCOM_PARITY_TYPE_MASK) ? 'O' : 'E') : 'N', 1044 (cfg & UCYCOM_STOP_MASK) ? 2 : 1, cfg)); 1045 } 1046 #endif 1047 1048 Static void 1049 ucycom_cleanup(struct ucycom_softc *sc) 1050 { 1051 DPRINTF(("ucycom_cleanup: closing uhidev\n")); 1052 1053 if (sc->sc_obuf !=NULL) 1054 free (sc->sc_obuf, M_USBDEV); 1055 uhidev_close(&sc->sc_hdev); 1056 } 1057