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