1 /* $NetBSD: ucom.c,v 1.40 2001/11/13 06:24:54 lukem Exp $ */ 2 3 /*- 4 * Copyright (c) 2001-2003, 2005, 2008 5 * Shunsuke Akiyama <akiyama@jp.FreeBSD.org>. 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 */ 29 30 #include <sys/cdefs.h> 31 __FBSDID("$FreeBSD$"); 32 33 /*- 34 * Copyright (c) 1998, 2000 The NetBSD Foundation, Inc. 35 * All rights reserved. 36 * 37 * This code is derived from software contributed to The NetBSD Foundation 38 * by Lennart Augustsson (lennart@augustsson.net) at 39 * Carlstedt Research & Technology. 40 * 41 * Redistribution and use in source and binary forms, with or without 42 * modification, are permitted provided that the following conditions 43 * are met: 44 * 1. Redistributions of source code must retain the above copyright 45 * notice, this list of conditions and the following disclaimer. 46 * 2. Redistributions in binary form must reproduce the above copyright 47 * notice, this list of conditions and the following disclaimer in the 48 * documentation and/or other materials provided with the distribution. 49 * 3. All advertising materials mentioning features or use of this software 50 * must display the following acknowledgement: 51 * This product includes software developed by the NetBSD 52 * Foundation, Inc. and its contributors. 53 * 4. Neither the name of The NetBSD Foundation nor the names of its 54 * contributors may be used to endorse or promote products derived 55 * from this software without specific prior written permission. 56 * 57 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 58 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 59 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 60 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 61 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 62 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 63 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 64 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 65 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 66 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 67 * POSSIBILITY OF SUCH DAMAGE. 68 */ 69 70 #include <sys/stdint.h> 71 #include <sys/stddef.h> 72 #include <sys/param.h> 73 #include <sys/queue.h> 74 #include <sys/types.h> 75 #include <sys/systm.h> 76 #include <sys/kernel.h> 77 #include <sys/bus.h> 78 #include <sys/linker_set.h> 79 #include <sys/module.h> 80 #include <sys/lock.h> 81 #include <sys/mutex.h> 82 #include <sys/condvar.h> 83 #include <sys/sysctl.h> 84 #include <sys/sx.h> 85 #include <sys/unistd.h> 86 #include <sys/callout.h> 87 #include <sys/malloc.h> 88 #include <sys/priv.h> 89 90 #include <dev/usb/usb.h> 91 #include <dev/usb/usbdi.h> 92 #include <dev/usb/usbdi_util.h> 93 94 #define USB_DEBUG_VAR ucom_debug 95 #include <dev/usb/usb_debug.h> 96 #include <dev/usb/usb_busdma.h> 97 #include <dev/usb/usb_process.h> 98 99 #include <dev/usb/serial/usb_serial.h> 100 101 #if USB_DEBUG 102 static int ucom_debug = 0; 103 104 SYSCTL_NODE(_hw_usb, OID_AUTO, ucom, CTLFLAG_RW, 0, "USB ucom"); 105 SYSCTL_INT(_hw_usb_ucom, OID_AUTO, debug, CTLFLAG_RW, 106 &ucom_debug, 0, "ucom debug level"); 107 #endif 108 109 static usb_proc_callback_t ucom_cfg_start_transfers; 110 static usb_proc_callback_t ucom_cfg_open; 111 static usb_proc_callback_t ucom_cfg_close; 112 static usb_proc_callback_t ucom_cfg_line_state; 113 static usb_proc_callback_t ucom_cfg_status_change; 114 static usb_proc_callback_t ucom_cfg_param; 115 116 static uint8_t ucom_units_alloc(uint32_t, uint32_t *); 117 static void ucom_units_free(uint32_t, uint32_t); 118 static int ucom_attach_tty(struct ucom_softc *, uint32_t); 119 static void ucom_detach_tty(struct ucom_softc *); 120 static void ucom_queue_command(struct ucom_softc *, 121 usb_proc_callback_t *, struct termios *pt, 122 struct usb_proc_msg *t0, struct usb_proc_msg *t1); 123 static void ucom_shutdown(struct ucom_softc *); 124 static void ucom_break(struct ucom_softc *, uint8_t); 125 static void ucom_dtr(struct ucom_softc *, uint8_t); 126 static void ucom_rts(struct ucom_softc *, uint8_t); 127 128 static tsw_open_t ucom_open; 129 static tsw_close_t ucom_close; 130 static tsw_ioctl_t ucom_ioctl; 131 static tsw_modem_t ucom_modem; 132 static tsw_param_t ucom_param; 133 static tsw_outwakeup_t ucom_outwakeup; 134 static tsw_free_t ucom_free; 135 136 static struct ttydevsw ucom_class = { 137 .tsw_flags = TF_INITLOCK | TF_CALLOUT, 138 .tsw_open = ucom_open, 139 .tsw_close = ucom_close, 140 .tsw_outwakeup = ucom_outwakeup, 141 .tsw_ioctl = ucom_ioctl, 142 .tsw_param = ucom_param, 143 .tsw_modem = ucom_modem, 144 .tsw_free = ucom_free, 145 }; 146 147 MODULE_DEPEND(ucom, usb, 1, 1, 1); 148 MODULE_VERSION(ucom, 1); 149 150 #define UCOM_UNIT_MAX 0x1000 /* exclusive */ 151 #define UCOM_SUB_UNIT_MAX 0x100 /* exclusive */ 152 153 static uint8_t ucom_bitmap[(UCOM_UNIT_MAX + 7) / 8]; 154 static struct mtx ucom_bitmap_mtx; 155 MTX_SYSINIT(ucom_bitmap_mtx, &ucom_bitmap_mtx, "ucom bitmap", MTX_DEF); 156 157 static uint8_t 158 ucom_units_alloc(uint32_t sub_units, uint32_t *p_root_unit) 159 { 160 uint32_t n; 161 uint32_t o; 162 uint32_t x; 163 uint32_t max = UCOM_UNIT_MAX - (UCOM_UNIT_MAX % sub_units); 164 uint8_t error = 1; 165 166 mtx_lock(&ucom_bitmap_mtx); 167 168 for (n = 0; n < max; n += sub_units) { 169 170 /* check for free consecutive bits */ 171 172 for (o = 0; o < sub_units; o++) { 173 174 x = n + o; 175 176 if (ucom_bitmap[x / 8] & (1 << (x % 8))) { 177 goto skip; 178 } 179 } 180 181 /* allocate */ 182 183 for (o = 0; o < sub_units; o++) { 184 185 x = n + o; 186 187 ucom_bitmap[x / 8] |= (1 << (x % 8)); 188 } 189 190 error = 0; 191 192 break; 193 194 skip: ; 195 } 196 197 mtx_unlock(&ucom_bitmap_mtx); 198 199 /* 200 * Always set the variable pointed to by "p_root_unit" so that 201 * the compiler does not think that it is used uninitialised: 202 */ 203 *p_root_unit = n; 204 205 return (error); 206 } 207 208 static void 209 ucom_units_free(uint32_t root_unit, uint32_t sub_units) 210 { 211 uint32_t x; 212 213 mtx_lock(&ucom_bitmap_mtx); 214 215 while (sub_units--) { 216 x = root_unit + sub_units; 217 ucom_bitmap[x / 8] &= ~(1 << (x % 8)); 218 } 219 220 mtx_unlock(&ucom_bitmap_mtx); 221 } 222 223 /* 224 * "N" sub_units are setup at a time. All sub-units will 225 * be given sequential unit numbers. The number of 226 * sub-units can be used to differentiate among 227 * different types of devices. 228 * 229 * The mutex pointed to by "mtx" is applied before all 230 * callbacks are called back. Also "mtx" must be applied 231 * before calling into the ucom-layer! 232 */ 233 int 234 ucom_attach(struct ucom_super_softc *ssc, struct ucom_softc *sc, 235 uint32_t sub_units, void *parent, 236 const struct ucom_callback *callback, struct mtx *mtx) 237 { 238 uint32_t n; 239 uint32_t root_unit; 240 int error = 0; 241 242 if ((sc == NULL) || 243 (sub_units == 0) || 244 (sub_units > UCOM_SUB_UNIT_MAX) || 245 (callback == NULL)) { 246 return (EINVAL); 247 } 248 249 /* XXX unit management does not really belong here */ 250 if (ucom_units_alloc(sub_units, &root_unit)) { 251 return (ENOMEM); 252 } 253 254 error = usb_proc_create(&ssc->sc_tq, mtx, "ucom", USB_PRI_MED); 255 if (error) { 256 ucom_units_free(root_unit, sub_units); 257 return (error); 258 } 259 260 for (n = 0; n != sub_units; n++, sc++) { 261 sc->sc_unit = root_unit + n; 262 sc->sc_local_unit = n; 263 sc->sc_super = ssc; 264 sc->sc_mtx = mtx; 265 sc->sc_parent = parent; 266 sc->sc_callback = callback; 267 268 error = ucom_attach_tty(sc, sub_units); 269 if (error) { 270 ucom_detach(ssc, sc - n, n); 271 ucom_units_free(root_unit + n, sub_units - n); 272 return (error); 273 } 274 sc->sc_flag |= UCOM_FLAG_ATTACHED; 275 } 276 return (0); 277 } 278 279 /* 280 * NOTE: the following function will do nothing if 281 * the structure pointed to by "ssc" and "sc" is zero. 282 */ 283 void 284 ucom_detach(struct ucom_super_softc *ssc, struct ucom_softc *sc, 285 uint32_t sub_units) 286 { 287 uint32_t n; 288 289 usb_proc_drain(&ssc->sc_tq); 290 291 for (n = 0; n != sub_units; n++, sc++) { 292 if (sc->sc_flag & UCOM_FLAG_ATTACHED) { 293 294 ucom_detach_tty(sc); 295 296 ucom_units_free(sc->sc_unit, 1); 297 298 /* avoid duplicate detach: */ 299 sc->sc_flag &= ~UCOM_FLAG_ATTACHED; 300 } 301 } 302 usb_proc_free(&ssc->sc_tq); 303 } 304 305 static int 306 ucom_attach_tty(struct ucom_softc *sc, uint32_t sub_units) 307 { 308 struct tty *tp; 309 int error = 0; 310 char buf[32]; /* temporary TTY device name buffer */ 311 312 tp = tty_alloc_mutex(&ucom_class, sc, sc->sc_mtx); 313 if (tp == NULL) { 314 error = ENOMEM; 315 goto done; 316 } 317 DPRINTF("tp = %p, unit = %d\n", tp, sc->sc_unit); 318 319 buf[0] = 0; /* set some default value */ 320 321 /* Check if the client has a custom TTY name */ 322 if (sc->sc_callback->ucom_tty_name) { 323 sc->sc_callback->ucom_tty_name(sc, buf, 324 sizeof(buf), sc->sc_local_unit); 325 } 326 if (buf[0] == 0) { 327 /* Use default TTY name */ 328 if (sub_units > 1) { 329 /* multiple modems in one */ 330 if (snprintf(buf, sizeof(buf), "U%u.%u", 331 sc->sc_unit - sc->sc_local_unit, 332 sc->sc_local_unit)) { 333 /* ignore */ 334 } 335 } else { 336 /* single modem */ 337 if (snprintf(buf, sizeof(buf), "U%u", sc->sc_unit)) { 338 /* ignore */ 339 } 340 } 341 } 342 tty_makedev(tp, NULL, "%s", buf); 343 344 sc->sc_tty = tp; 345 346 DPRINTF("ttycreate: %s\n", buf); 347 cv_init(&sc->sc_cv, "ucom"); 348 349 done: 350 return (error); 351 } 352 353 static void 354 ucom_detach_tty(struct ucom_softc *sc) 355 { 356 struct tty *tp = sc->sc_tty; 357 358 DPRINTF("sc = %p, tp = %p\n", sc, sc->sc_tty); 359 360 /* the config thread has been stopped when we get here */ 361 362 mtx_lock(sc->sc_mtx); 363 sc->sc_flag |= UCOM_FLAG_GONE; 364 sc->sc_flag &= ~(UCOM_FLAG_HL_READY | 365 UCOM_FLAG_LL_READY); 366 mtx_unlock(sc->sc_mtx); 367 if (tp) { 368 tty_lock(tp); 369 370 ucom_close(tp); /* close, if any */ 371 372 tty_rel_gone(tp); 373 374 mtx_lock(sc->sc_mtx); 375 /* Wait for the callback after the TTY is torn down */ 376 while (sc->sc_ttyfreed == 0) 377 cv_wait(&sc->sc_cv, sc->sc_mtx); 378 /* 379 * make sure that read and write transfers are stopped 380 */ 381 if (sc->sc_callback->ucom_stop_read) { 382 (sc->sc_callback->ucom_stop_read) (sc); 383 } 384 if (sc->sc_callback->ucom_stop_write) { 385 (sc->sc_callback->ucom_stop_write) (sc); 386 } 387 mtx_unlock(sc->sc_mtx); 388 } 389 cv_destroy(&sc->sc_cv); 390 } 391 392 static void 393 ucom_queue_command(struct ucom_softc *sc, 394 usb_proc_callback_t *fn, struct termios *pt, 395 struct usb_proc_msg *t0, struct usb_proc_msg *t1) 396 { 397 struct ucom_super_softc *ssc = sc->sc_super; 398 struct ucom_param_task *task; 399 400 mtx_assert(sc->sc_mtx, MA_OWNED); 401 402 if (usb_proc_is_gone(&ssc->sc_tq)) { 403 DPRINTF("proc is gone\n"); 404 return; /* nothing to do */ 405 } 406 /* 407 * NOTE: The task cannot get executed before we drop the 408 * "sc_mtx" mutex. It is safe to update fields in the message 409 * structure after that the message got queued. 410 */ 411 task = (struct ucom_param_task *) 412 usb_proc_msignal(&ssc->sc_tq, t0, t1); 413 414 /* Setup callback and softc pointers */ 415 task->hdr.pm_callback = fn; 416 task->sc = sc; 417 418 /* 419 * Make a copy of the termios. This field is only present if 420 * the "pt" field is not NULL. 421 */ 422 if (pt != NULL) 423 task->termios_copy = *pt; 424 425 /* 426 * Closing the device should be synchronous. 427 */ 428 if (fn == ucom_cfg_close) 429 usb_proc_mwait(&ssc->sc_tq, t0, t1); 430 431 /* 432 * In case of multiple configure requests, 433 * keep track of the last one! 434 */ 435 if (fn == ucom_cfg_start_transfers) 436 sc->sc_last_start_xfer = &task->hdr; 437 } 438 439 static void 440 ucom_shutdown(struct ucom_softc *sc) 441 { 442 struct tty *tp = sc->sc_tty; 443 444 mtx_assert(sc->sc_mtx, MA_OWNED); 445 446 DPRINTF("\n"); 447 448 /* 449 * Hang up if necessary: 450 */ 451 if (tp->t_termios.c_cflag & HUPCL) { 452 ucom_modem(tp, 0, SER_DTR); 453 } 454 } 455 456 /* 457 * Return values: 458 * 0: normal 459 * else: taskqueue is draining or gone 460 */ 461 uint8_t 462 ucom_cfg_is_gone(struct ucom_softc *sc) 463 { 464 struct ucom_super_softc *ssc = sc->sc_super; 465 466 return (usb_proc_is_gone(&ssc->sc_tq)); 467 } 468 469 static void 470 ucom_cfg_start_transfers(struct usb_proc_msg *_task) 471 { 472 struct ucom_cfg_task *task = 473 (struct ucom_cfg_task *)_task; 474 struct ucom_softc *sc = task->sc; 475 476 if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) { 477 return; 478 } 479 if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) { 480 /* TTY device closed */ 481 return; 482 } 483 484 if (_task == sc->sc_last_start_xfer) 485 sc->sc_flag |= UCOM_FLAG_GP_DATA; 486 487 if (sc->sc_callback->ucom_start_read) { 488 (sc->sc_callback->ucom_start_read) (sc); 489 } 490 if (sc->sc_callback->ucom_start_write) { 491 (sc->sc_callback->ucom_start_write) (sc); 492 } 493 } 494 495 static void 496 ucom_start_transfers(struct ucom_softc *sc) 497 { 498 if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) { 499 return; 500 } 501 /* 502 * Make sure that data transfers are started in both 503 * directions: 504 */ 505 if (sc->sc_callback->ucom_start_read) { 506 (sc->sc_callback->ucom_start_read) (sc); 507 } 508 if (sc->sc_callback->ucom_start_write) { 509 (sc->sc_callback->ucom_start_write) (sc); 510 } 511 } 512 513 static void 514 ucom_cfg_open(struct usb_proc_msg *_task) 515 { 516 struct ucom_cfg_task *task = 517 (struct ucom_cfg_task *)_task; 518 struct ucom_softc *sc = task->sc; 519 520 DPRINTF("\n"); 521 522 if (sc->sc_flag & UCOM_FLAG_LL_READY) { 523 524 /* already opened */ 525 526 } else { 527 528 sc->sc_flag |= UCOM_FLAG_LL_READY; 529 530 if (sc->sc_callback->ucom_cfg_open) { 531 (sc->sc_callback->ucom_cfg_open) (sc); 532 533 /* wait a little */ 534 usb_pause_mtx(sc->sc_mtx, hz / 10); 535 } 536 } 537 } 538 539 static int 540 ucom_open(struct tty *tp) 541 { 542 struct ucom_softc *sc = tty_softc(tp); 543 int error; 544 545 mtx_assert(sc->sc_mtx, MA_OWNED); 546 547 if (sc->sc_flag & UCOM_FLAG_GONE) { 548 return (ENXIO); 549 } 550 if (sc->sc_flag & UCOM_FLAG_HL_READY) { 551 /* already opened */ 552 return (0); 553 } 554 DPRINTF("tp = %p\n", tp); 555 556 if (sc->sc_callback->ucom_pre_open) { 557 /* 558 * give the lower layer a chance to disallow TTY open, for 559 * example if the device is not present: 560 */ 561 error = (sc->sc_callback->ucom_pre_open) (sc); 562 if (error) { 563 return (error); 564 } 565 } 566 sc->sc_flag |= UCOM_FLAG_HL_READY; 567 568 /* Disable transfers */ 569 sc->sc_flag &= ~UCOM_FLAG_GP_DATA; 570 571 sc->sc_lsr = 0; 572 sc->sc_msr = 0; 573 sc->sc_mcr = 0; 574 575 /* reset programmed line state */ 576 sc->sc_pls_curr = 0; 577 sc->sc_pls_set = 0; 578 sc->sc_pls_clr = 0; 579 580 ucom_queue_command(sc, ucom_cfg_open, NULL, 581 &sc->sc_open_task[0].hdr, 582 &sc->sc_open_task[1].hdr); 583 584 /* Queue transfer enable command last */ 585 ucom_queue_command(sc, ucom_cfg_start_transfers, NULL, 586 &sc->sc_start_task[0].hdr, 587 &sc->sc_start_task[1].hdr); 588 589 ucom_modem(tp, SER_DTR | SER_RTS, 0); 590 591 ucom_break(sc, 0); 592 593 ucom_status_change(sc); 594 595 return (0); 596 } 597 598 static void 599 ucom_cfg_close(struct usb_proc_msg *_task) 600 { 601 struct ucom_cfg_task *task = 602 (struct ucom_cfg_task *)_task; 603 struct ucom_softc *sc = task->sc; 604 605 DPRINTF("\n"); 606 607 if (sc->sc_flag & UCOM_FLAG_LL_READY) { 608 sc->sc_flag &= ~UCOM_FLAG_LL_READY; 609 if (sc->sc_callback->ucom_cfg_close) 610 (sc->sc_callback->ucom_cfg_close) (sc); 611 } else { 612 /* already closed */ 613 } 614 } 615 616 static void 617 ucom_close(struct tty *tp) 618 { 619 struct ucom_softc *sc = tty_softc(tp); 620 621 mtx_assert(sc->sc_mtx, MA_OWNED); 622 623 DPRINTF("tp=%p\n", tp); 624 625 if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) { 626 DPRINTF("tp=%p already closed\n", tp); 627 return; 628 } 629 ucom_shutdown(sc); 630 631 ucom_queue_command(sc, ucom_cfg_close, NULL, 632 &sc->sc_close_task[0].hdr, 633 &sc->sc_close_task[1].hdr); 634 635 sc->sc_flag &= ~(UCOM_FLAG_HL_READY | UCOM_FLAG_RTS_IFLOW); 636 637 if (sc->sc_callback->ucom_stop_read) { 638 (sc->sc_callback->ucom_stop_read) (sc); 639 } 640 } 641 642 static int 643 ucom_ioctl(struct tty *tp, u_long cmd, caddr_t data, struct thread *td) 644 { 645 struct ucom_softc *sc = tty_softc(tp); 646 int error; 647 648 mtx_assert(sc->sc_mtx, MA_OWNED); 649 650 if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) { 651 return (EIO); 652 } 653 DPRINTF("cmd = 0x%08lx\n", cmd); 654 655 switch (cmd) { 656 case TIOCSBRK: 657 ucom_break(sc, 1); 658 error = 0; 659 break; 660 case TIOCCBRK: 661 ucom_break(sc, 0); 662 error = 0; 663 break; 664 default: 665 if (sc->sc_callback->ucom_ioctl) { 666 error = (sc->sc_callback->ucom_ioctl) 667 (sc, cmd, data, 0, td); 668 } else { 669 error = ENOIOCTL; 670 } 671 break; 672 } 673 return (error); 674 } 675 676 static int 677 ucom_modem(struct tty *tp, int sigon, int sigoff) 678 { 679 struct ucom_softc *sc = tty_softc(tp); 680 uint8_t onoff; 681 682 mtx_assert(sc->sc_mtx, MA_OWNED); 683 684 if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) { 685 return (0); 686 } 687 if ((sigon == 0) && (sigoff == 0)) { 688 689 if (sc->sc_mcr & SER_DTR) { 690 sigon |= SER_DTR; 691 } 692 if (sc->sc_mcr & SER_RTS) { 693 sigon |= SER_RTS; 694 } 695 if (sc->sc_msr & SER_CTS) { 696 sigon |= SER_CTS; 697 } 698 if (sc->sc_msr & SER_DCD) { 699 sigon |= SER_DCD; 700 } 701 if (sc->sc_msr & SER_DSR) { 702 sigon |= SER_DSR; 703 } 704 if (sc->sc_msr & SER_RI) { 705 sigon |= SER_RI; 706 } 707 return (sigon); 708 } 709 if (sigon & SER_DTR) { 710 sc->sc_mcr |= SER_DTR; 711 } 712 if (sigoff & SER_DTR) { 713 sc->sc_mcr &= ~SER_DTR; 714 } 715 if (sigon & SER_RTS) { 716 sc->sc_mcr |= SER_RTS; 717 } 718 if (sigoff & SER_RTS) { 719 sc->sc_mcr &= ~SER_RTS; 720 } 721 onoff = (sc->sc_mcr & SER_DTR) ? 1 : 0; 722 ucom_dtr(sc, onoff); 723 724 onoff = (sc->sc_mcr & SER_RTS) ? 1 : 0; 725 ucom_rts(sc, onoff); 726 727 return (0); 728 } 729 730 static void 731 ucom_cfg_line_state(struct usb_proc_msg *_task) 732 { 733 struct ucom_cfg_task *task = 734 (struct ucom_cfg_task *)_task; 735 struct ucom_softc *sc = task->sc; 736 uint8_t notch_bits; 737 uint8_t any_bits; 738 uint8_t prev_value; 739 uint8_t last_value; 740 uint8_t mask; 741 742 if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) { 743 return; 744 } 745 746 mask = 0; 747 /* compute callback mask */ 748 if (sc->sc_callback->ucom_cfg_set_dtr) 749 mask |= UCOM_LS_DTR; 750 if (sc->sc_callback->ucom_cfg_set_rts) 751 mask |= UCOM_LS_RTS; 752 if (sc->sc_callback->ucom_cfg_set_break) 753 mask |= UCOM_LS_BREAK; 754 755 /* compute the bits we are to program */ 756 notch_bits = (sc->sc_pls_set & sc->sc_pls_clr) & mask; 757 any_bits = (sc->sc_pls_set | sc->sc_pls_clr) & mask; 758 prev_value = sc->sc_pls_curr ^ notch_bits; 759 last_value = sc->sc_pls_curr; 760 761 /* reset programmed line state */ 762 sc->sc_pls_curr = 0; 763 sc->sc_pls_set = 0; 764 sc->sc_pls_clr = 0; 765 766 /* ensure that we don't loose any levels */ 767 if (notch_bits & UCOM_LS_DTR) 768 sc->sc_callback->ucom_cfg_set_dtr(sc, 769 (prev_value & UCOM_LS_DTR) ? 1 : 0); 770 if (notch_bits & UCOM_LS_RTS) 771 sc->sc_callback->ucom_cfg_set_rts(sc, 772 (prev_value & UCOM_LS_RTS) ? 1 : 0); 773 if (notch_bits & UCOM_LS_BREAK) 774 sc->sc_callback->ucom_cfg_set_break(sc, 775 (prev_value & UCOM_LS_BREAK) ? 1 : 0); 776 777 /* set last value */ 778 if (any_bits & UCOM_LS_DTR) 779 sc->sc_callback->ucom_cfg_set_dtr(sc, 780 (last_value & UCOM_LS_DTR) ? 1 : 0); 781 if (any_bits & UCOM_LS_RTS) 782 sc->sc_callback->ucom_cfg_set_rts(sc, 783 (last_value & UCOM_LS_RTS) ? 1 : 0); 784 if (any_bits & UCOM_LS_BREAK) 785 sc->sc_callback->ucom_cfg_set_break(sc, 786 (last_value & UCOM_LS_BREAK) ? 1 : 0); 787 } 788 789 static void 790 ucom_line_state(struct ucom_softc *sc, 791 uint8_t set_bits, uint8_t clear_bits) 792 { 793 mtx_assert(sc->sc_mtx, MA_OWNED); 794 795 if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) { 796 return; 797 } 798 799 DPRINTF("on=0x%02x, off=0x%02x\n", set_bits, clear_bits); 800 801 /* update current programmed line state */ 802 sc->sc_pls_curr |= set_bits; 803 sc->sc_pls_curr &= ~clear_bits; 804 sc->sc_pls_set |= set_bits; 805 sc->sc_pls_clr |= clear_bits; 806 807 /* defer driver programming */ 808 ucom_queue_command(sc, ucom_cfg_line_state, NULL, 809 &sc->sc_line_state_task[0].hdr, 810 &sc->sc_line_state_task[1].hdr); 811 } 812 813 static void 814 ucom_break(struct ucom_softc *sc, uint8_t onoff) 815 { 816 DPRINTF("onoff = %d\n", onoff); 817 818 if (onoff) 819 ucom_line_state(sc, UCOM_LS_BREAK, 0); 820 else 821 ucom_line_state(sc, 0, UCOM_LS_BREAK); 822 } 823 824 static void 825 ucom_dtr(struct ucom_softc *sc, uint8_t onoff) 826 { 827 DPRINTF("onoff = %d\n", onoff); 828 829 if (onoff) 830 ucom_line_state(sc, UCOM_LS_DTR, 0); 831 else 832 ucom_line_state(sc, 0, UCOM_LS_DTR); 833 } 834 835 static void 836 ucom_rts(struct ucom_softc *sc, uint8_t onoff) 837 { 838 DPRINTF("onoff = %d\n", onoff); 839 840 if (onoff) 841 ucom_line_state(sc, UCOM_LS_RTS, 0); 842 else 843 ucom_line_state(sc, 0, UCOM_LS_RTS); 844 } 845 846 static void 847 ucom_cfg_status_change(struct usb_proc_msg *_task) 848 { 849 struct ucom_cfg_task *task = 850 (struct ucom_cfg_task *)_task; 851 struct ucom_softc *sc = task->sc; 852 struct tty *tp; 853 uint8_t new_msr; 854 uint8_t new_lsr; 855 uint8_t onoff; 856 857 tp = sc->sc_tty; 858 859 mtx_assert(sc->sc_mtx, MA_OWNED); 860 861 if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) { 862 return; 863 } 864 if (sc->sc_callback->ucom_cfg_get_status == NULL) { 865 return; 866 } 867 /* get status */ 868 869 new_msr = 0; 870 new_lsr = 0; 871 872 (sc->sc_callback->ucom_cfg_get_status) (sc, &new_lsr, &new_msr); 873 874 if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) { 875 /* TTY device closed */ 876 return; 877 } 878 onoff = ((sc->sc_msr ^ new_msr) & SER_DCD); 879 880 sc->sc_msr = new_msr; 881 sc->sc_lsr = new_lsr; 882 883 if (onoff) { 884 885 onoff = (sc->sc_msr & SER_DCD) ? 1 : 0; 886 887 DPRINTF("DCD changed to %d\n", onoff); 888 889 ttydisc_modem(tp, onoff); 890 } 891 } 892 893 void 894 ucom_status_change(struct ucom_softc *sc) 895 { 896 mtx_assert(sc->sc_mtx, MA_OWNED); 897 898 if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) { 899 return; 900 } 901 DPRINTF("\n"); 902 903 ucom_queue_command(sc, ucom_cfg_status_change, NULL, 904 &sc->sc_status_task[0].hdr, 905 &sc->sc_status_task[1].hdr); 906 } 907 908 static void 909 ucom_cfg_param(struct usb_proc_msg *_task) 910 { 911 struct ucom_param_task *task = 912 (struct ucom_param_task *)_task; 913 struct ucom_softc *sc = task->sc; 914 915 if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) { 916 return; 917 } 918 if (sc->sc_callback->ucom_cfg_param == NULL) { 919 return; 920 } 921 922 (sc->sc_callback->ucom_cfg_param) (sc, &task->termios_copy); 923 924 /* wait a little */ 925 usb_pause_mtx(sc->sc_mtx, hz / 10); 926 } 927 928 static int 929 ucom_param(struct tty *tp, struct termios *t) 930 { 931 struct ucom_softc *sc = tty_softc(tp); 932 uint8_t opened; 933 int error; 934 935 mtx_assert(sc->sc_mtx, MA_OWNED); 936 937 opened = 0; 938 error = 0; 939 940 if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) { 941 942 /* XXX the TTY layer should call "open()" first! */ 943 944 error = ucom_open(tp); 945 if (error) { 946 goto done; 947 } 948 opened = 1; 949 } 950 DPRINTF("sc = %p\n", sc); 951 952 /* Check requested parameters. */ 953 if (t->c_ospeed < 0) { 954 DPRINTF("negative ospeed\n"); 955 error = EINVAL; 956 goto done; 957 } 958 if (t->c_ispeed && (t->c_ispeed != t->c_ospeed)) { 959 DPRINTF("mismatch ispeed and ospeed\n"); 960 error = EINVAL; 961 goto done; 962 } 963 t->c_ispeed = t->c_ospeed; 964 965 if (sc->sc_callback->ucom_pre_param) { 966 /* Let the lower layer verify the parameters */ 967 error = (sc->sc_callback->ucom_pre_param) (sc, t); 968 if (error) { 969 DPRINTF("callback error = %d\n", error); 970 goto done; 971 } 972 } 973 974 /* Disable transfers */ 975 sc->sc_flag &= ~UCOM_FLAG_GP_DATA; 976 977 /* Queue baud rate programming command first */ 978 ucom_queue_command(sc, ucom_cfg_param, t, 979 &sc->sc_param_task[0].hdr, 980 &sc->sc_param_task[1].hdr); 981 982 /* Queue transfer enable command last */ 983 ucom_queue_command(sc, ucom_cfg_start_transfers, NULL, 984 &sc->sc_start_task[0].hdr, 985 &sc->sc_start_task[1].hdr); 986 987 if (t->c_cflag & CRTS_IFLOW) { 988 sc->sc_flag |= UCOM_FLAG_RTS_IFLOW; 989 } else if (sc->sc_flag & UCOM_FLAG_RTS_IFLOW) { 990 sc->sc_flag &= ~UCOM_FLAG_RTS_IFLOW; 991 ucom_modem(tp, SER_RTS, 0); 992 } 993 done: 994 if (error) { 995 if (opened) { 996 ucom_close(tp); 997 } 998 } 999 return (error); 1000 } 1001 1002 static void 1003 ucom_outwakeup(struct tty *tp) 1004 { 1005 struct ucom_softc *sc = tty_softc(tp); 1006 1007 mtx_assert(sc->sc_mtx, MA_OWNED); 1008 1009 DPRINTF("sc = %p\n", sc); 1010 1011 if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) { 1012 /* The higher layer is not ready */ 1013 return; 1014 } 1015 ucom_start_transfers(sc); 1016 } 1017 1018 /*------------------------------------------------------------------------* 1019 * ucom_get_data 1020 * 1021 * Return values: 1022 * 0: No data is available. 1023 * Else: Data is available. 1024 *------------------------------------------------------------------------*/ 1025 uint8_t 1026 ucom_get_data(struct ucom_softc *sc, struct usb_page_cache *pc, 1027 uint32_t offset, uint32_t len, uint32_t *actlen) 1028 { 1029 struct usb_page_search res; 1030 struct tty *tp = sc->sc_tty; 1031 uint32_t cnt; 1032 uint32_t offset_orig; 1033 1034 mtx_assert(sc->sc_mtx, MA_OWNED); 1035 1036 if (tty_gone(tp) || 1037 !(sc->sc_flag & UCOM_FLAG_GP_DATA)) { 1038 actlen[0] = 0; 1039 return (0); /* multiport device polling */ 1040 } 1041 offset_orig = offset; 1042 1043 while (len != 0) { 1044 1045 usbd_get_page(pc, offset, &res); 1046 1047 if (res.length > len) { 1048 res.length = len; 1049 } 1050 /* copy data directly into USB buffer */ 1051 cnt = ttydisc_getc(tp, res.buffer, res.length); 1052 1053 offset += cnt; 1054 len -= cnt; 1055 1056 if (cnt < res.length) { 1057 /* end of buffer */ 1058 break; 1059 } 1060 } 1061 1062 actlen[0] = offset - offset_orig; 1063 1064 DPRINTF("cnt=%d\n", actlen[0]); 1065 1066 if (actlen[0] == 0) { 1067 return (0); 1068 } 1069 return (1); 1070 } 1071 1072 void 1073 ucom_put_data(struct ucom_softc *sc, struct usb_page_cache *pc, 1074 uint32_t offset, uint32_t len) 1075 { 1076 struct usb_page_search res; 1077 struct tty *tp = sc->sc_tty; 1078 char *buf; 1079 uint32_t cnt; 1080 1081 mtx_assert(sc->sc_mtx, MA_OWNED); 1082 1083 if (tty_gone(tp)) 1084 return; /* multiport device polling */ 1085 1086 if (len == 0) 1087 return; /* no data */ 1088 1089 /* set a flag to prevent recursation ? */ 1090 1091 while (len > 0) { 1092 1093 usbd_get_page(pc, offset, &res); 1094 1095 if (res.length > len) { 1096 res.length = len; 1097 } 1098 len -= res.length; 1099 offset += res.length; 1100 1101 /* pass characters to tty layer */ 1102 1103 buf = res.buffer; 1104 cnt = res.length; 1105 1106 /* first check if we can pass the buffer directly */ 1107 1108 if (ttydisc_can_bypass(tp)) { 1109 if (ttydisc_rint_bypass(tp, buf, cnt) != cnt) { 1110 DPRINTF("tp=%p, data lost\n", tp); 1111 } 1112 continue; 1113 } 1114 /* need to loop */ 1115 1116 for (cnt = 0; cnt != res.length; cnt++) { 1117 if (ttydisc_rint(tp, buf[cnt], 0) == -1) { 1118 /* XXX what should we do? */ 1119 1120 DPRINTF("tp=%p, lost %d " 1121 "chars\n", tp, res.length - cnt); 1122 break; 1123 } 1124 } 1125 } 1126 ttydisc_rint_done(tp); 1127 } 1128 1129 static void 1130 ucom_free(void *xsc) 1131 { 1132 struct ucom_softc *sc = xsc; 1133 1134 mtx_lock(sc->sc_mtx); 1135 sc->sc_ttyfreed = 1; 1136 cv_signal(&sc->sc_cv); 1137 mtx_unlock(sc->sc_mtx); 1138 } 1139