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