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