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