usb_serial.c revision 190742
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 190742 2009-04-05 18:22:03Z 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 usb2_com_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 usb2_com_debug = 0; 89 90SYSCTL_NODE(_hw_usb2, OID_AUTO, ucom, CTLFLAG_RW, 0, "USB ucom"); 91SYSCTL_INT(_hw_usb2_ucom, OID_AUTO, debug, CTLFLAG_RW, 92 &usb2_com_debug, 0, "ucom debug level"); 93#endif 94 95static usb2_proc_callback_t usb2_com_cfg_start_transfers; 96static usb2_proc_callback_t usb2_com_cfg_open; 97static usb2_proc_callback_t usb2_com_cfg_close; 98static usb2_proc_callback_t usb2_com_cfg_line_state; 99static usb2_proc_callback_t usb2_com_cfg_status_change; 100static usb2_proc_callback_t usb2_com_cfg_param; 101 102static uint8_t usb2_com_units_alloc(uint32_t, uint32_t *); 103static void usb2_com_units_free(uint32_t, uint32_t); 104static int usb2_com_attach_tty(struct usb2_com_softc *, uint32_t); 105static void usb2_com_detach_tty(struct usb2_com_softc *); 106static void usb2_com_queue_command(struct usb2_com_softc *, 107 usb2_proc_callback_t *, struct termios *pt, 108 struct usb2_proc_msg *t0, struct usb2_proc_msg *t1); 109static void usb2_com_shutdown(struct usb2_com_softc *); 110static void usb2_com_break(struct usb2_com_softc *, uint8_t); 111static void usb2_com_dtr(struct usb2_com_softc *, uint8_t); 112static void usb2_com_rts(struct usb2_com_softc *, uint8_t); 113 114static tsw_open_t usb2_com_open; 115static tsw_close_t usb2_com_close; 116static tsw_ioctl_t usb2_com_ioctl; 117static tsw_modem_t usb2_com_modem; 118static tsw_param_t usb2_com_param; 119static tsw_outwakeup_t usb2_com_outwakeup; 120static tsw_free_t usb2_com_free; 121 122static struct ttydevsw usb2_com_class = { 123 .tsw_flags = TF_INITLOCK | TF_CALLOUT, 124 .tsw_open = usb2_com_open, 125 .tsw_close = usb2_com_close, 126 .tsw_outwakeup = usb2_com_outwakeup, 127 .tsw_ioctl = usb2_com_ioctl, 128 .tsw_param = usb2_com_param, 129 .tsw_modem = usb2_com_modem, 130 .tsw_free = usb2_com_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 usb2_com_bitmap[(UCOM_UNIT_MAX + 7) / 8]; 140 141static uint8_t 142usb2_com_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 (usb2_com_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 usb2_com_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 193usb2_com_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 usb2_com_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 218usb2_com_attach(struct usb2_com_super_softc *ssc, struct usb2_com_softc *sc, 219 uint32_t sub_units, void *parent, 220 const struct usb2_com_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 (usb2_com_units_alloc(sub_units, &root_unit)) { 235 return (ENOMEM); 236 } 237 238 error = usb2_proc_create(&ssc->sc_tq, mtx, "ucom", USB_PRI_MED); 239 if (error) { 240 usb2_com_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 = usb2_com_attach_tty(sc, sub_units); 253 if (error) { 254 usb2_com_detach(ssc, sc - n, n); 255 usb2_com_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 268usb2_com_detach(struct usb2_com_super_softc *ssc, struct usb2_com_softc *sc, 269 uint32_t sub_units) 270{ 271 uint32_t n; 272 273 usb2_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 usb2_com_detach_tty(sc); 279 280 usb2_com_units_free(sc->sc_unit, 1); 281 282 /* avoid duplicate detach: */ 283 sc->sc_flag &= ~UCOM_FLAG_ATTACHED; 284 } 285 } 286 usb2_proc_free(&ssc->sc_tq); 287} 288 289static int 290usb2_com_attach_tty(struct usb2_com_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(&usb2_com_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->usb2_com_tty_name) { 307 sc->sc_callback->usb2_com_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 usb2_cv_init(&sc->sc_cv, "usb2_com"); 332 333done: 334 return (error); 335} 336 337static void 338usb2_com_detach_tty(struct usb2_com_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 usb2_com_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 usb2_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->usb2_com_stop_read) { 366 (sc->sc_callback->usb2_com_stop_read) (sc); 367 } 368 if (sc->sc_callback->usb2_com_stop_write) { 369 (sc->sc_callback->usb2_com_stop_write) (sc); 370 } 371 mtx_unlock(sc->sc_mtx); 372 } 373 usb2_cv_destroy(&sc->sc_cv); 374} 375 376static void 377usb2_com_queue_command(struct usb2_com_softc *sc, 378 usb2_proc_callback_t *fn, struct termios *pt, 379 struct usb2_proc_msg *t0, struct usb2_proc_msg *t1) 380{ 381 struct usb2_com_super_softc *ssc = sc->sc_super; 382 struct usb2_com_param_task *task; 383 384 mtx_assert(sc->sc_mtx, MA_OWNED); 385 386 if (usb2_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 usb2_com_param_task *) 396 usb2_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 == usb2_com_cfg_close) 413 usb2_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 == usb2_com_cfg_start_transfers) 420 sc->sc_last_start_xfer = &task->hdr; 421} 422 423static void 424usb2_com_shutdown(struct usb2_com_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 usb2_com_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 446usb2_com_cfg_is_gone(struct usb2_com_softc *sc) 447{ 448 struct usb2_com_super_softc *ssc = sc->sc_super; 449 450 return (usb2_proc_is_gone(&ssc->sc_tq)); 451} 452 453static void 454usb2_com_cfg_start_transfers(struct usb2_proc_msg *_task) 455{ 456 struct usb2_com_cfg_task *task = 457 (struct usb2_com_cfg_task *)_task; 458 struct usb2_com_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->usb2_com_start_read) { 472 (sc->sc_callback->usb2_com_start_read) (sc); 473 } 474 if (sc->sc_callback->usb2_com_start_write) { 475 (sc->sc_callback->usb2_com_start_write) (sc); 476 } 477} 478 479static void 480usb2_com_start_transfers(struct usb2_com_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->usb2_com_start_read) { 490 (sc->sc_callback->usb2_com_start_read) (sc); 491 } 492 if (sc->sc_callback->usb2_com_start_write) { 493 (sc->sc_callback->usb2_com_start_write) (sc); 494 } 495} 496 497static void 498usb2_com_cfg_open(struct usb2_proc_msg *_task) 499{ 500 struct usb2_com_cfg_task *task = 501 (struct usb2_com_cfg_task *)_task; 502 struct usb2_com_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->usb2_com_cfg_open) { 515 (sc->sc_callback->usb2_com_cfg_open) (sc); 516 517 /* wait a little */ 518 usb2_pause_mtx(sc->sc_mtx, hz / 10); 519 } 520 } 521} 522 523static int 524usb2_com_open(struct tty *tp) 525{ 526 struct usb2_com_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->usb2_com_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->usb2_com_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 usb2_com_queue_command(sc, usb2_com_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 usb2_com_queue_command(sc, usb2_com_cfg_start_transfers, NULL, 570 &sc->sc_start_task[0].hdr, 571 &sc->sc_start_task[1].hdr); 572 573 usb2_com_modem(tp, SER_DTR | SER_RTS, 0); 574 575 usb2_com_break(sc, 0); 576 577 usb2_com_status_change(sc); 578 579 return (0); 580} 581 582static void 583usb2_com_cfg_close(struct usb2_proc_msg *_task) 584{ 585 struct usb2_com_cfg_task *task = 586 (struct usb2_com_cfg_task *)_task; 587 struct usb2_com_softc *sc = task->sc; 588 589 DPRINTF("\n"); 590 591 if (sc->sc_flag & UCOM_FLAG_LL_READY) { 592 593 sc->sc_flag &= ~(UCOM_FLAG_LL_READY | 594 UCOM_FLAG_GP_DATA); 595 596 if (sc->sc_callback->usb2_com_cfg_close) { 597 (sc->sc_callback->usb2_com_cfg_close) (sc); 598 } 599 } else { 600 /* already closed */ 601 } 602} 603 604static void 605usb2_com_close(struct tty *tp) 606{ 607 struct usb2_com_softc *sc = tty_softc(tp); 608 609 mtx_assert(sc->sc_mtx, MA_OWNED); 610 611 DPRINTF("tp=%p\n", tp); 612 613 if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) { 614 DPRINTF("tp=%p already closed\n", tp); 615 return; 616 } 617 usb2_com_shutdown(sc); 618 619 usb2_com_queue_command(sc, usb2_com_cfg_close, NULL, 620 &sc->sc_close_task[0].hdr, 621 &sc->sc_close_task[1].hdr); 622 623 sc->sc_flag &= ~(UCOM_FLAG_HL_READY | 624 UCOM_FLAG_WR_START | 625 UCOM_FLAG_RTS_IFLOW); 626 627 if (sc->sc_callback->usb2_com_stop_read) { 628 (sc->sc_callback->usb2_com_stop_read) (sc); 629 } 630 if (sc->sc_callback->usb2_com_stop_write) { 631 (sc->sc_callback->usb2_com_stop_write) (sc); 632 } 633} 634 635static int 636usb2_com_ioctl(struct tty *tp, u_long cmd, caddr_t data, struct thread *td) 637{ 638 struct usb2_com_softc *sc = tty_softc(tp); 639 int error; 640 641 mtx_assert(sc->sc_mtx, MA_OWNED); 642 643 if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) { 644 return (EIO); 645 } 646 DPRINTF("cmd = 0x%08lx\n", cmd); 647 648 switch (cmd) { 649 case TIOCSBRK: 650 usb2_com_break(sc, 1); 651 error = 0; 652 break; 653 case TIOCCBRK: 654 usb2_com_break(sc, 0); 655 error = 0; 656 break; 657 default: 658 if (sc->sc_callback->usb2_com_ioctl) { 659 error = (sc->sc_callback->usb2_com_ioctl) 660 (sc, cmd, data, 0, td); 661 } else { 662 error = ENOIOCTL; 663 } 664 break; 665 } 666 return (error); 667} 668 669static int 670usb2_com_modem(struct tty *tp, int sigon, int sigoff) 671{ 672 struct usb2_com_softc *sc = tty_softc(tp); 673 uint8_t onoff; 674 675 mtx_assert(sc->sc_mtx, MA_OWNED); 676 677 if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) { 678 return (0); 679 } 680 if ((sigon == 0) && (sigoff == 0)) { 681 682 if (sc->sc_mcr & SER_DTR) { 683 sigon |= SER_DTR; 684 } 685 if (sc->sc_mcr & SER_RTS) { 686 sigon |= SER_RTS; 687 } 688 if (sc->sc_msr & SER_CTS) { 689 sigon |= SER_CTS; 690 } 691 if (sc->sc_msr & SER_DCD) { 692 sigon |= SER_DCD; 693 } 694 if (sc->sc_msr & SER_DSR) { 695 sigon |= SER_DSR; 696 } 697 if (sc->sc_msr & SER_RI) { 698 sigon |= SER_RI; 699 } 700 return (sigon); 701 } 702 if (sigon & SER_DTR) { 703 sc->sc_mcr |= SER_DTR; 704 } 705 if (sigoff & SER_DTR) { 706 sc->sc_mcr &= ~SER_DTR; 707 } 708 if (sigon & SER_RTS) { 709 sc->sc_mcr |= SER_RTS; 710 } 711 if (sigoff & SER_RTS) { 712 sc->sc_mcr &= ~SER_RTS; 713 } 714 onoff = (sc->sc_mcr & SER_DTR) ? 1 : 0; 715 usb2_com_dtr(sc, onoff); 716 717 onoff = (sc->sc_mcr & SER_RTS) ? 1 : 0; 718 usb2_com_rts(sc, onoff); 719 720 return (0); 721} 722 723static void 724usb2_com_cfg_line_state(struct usb2_proc_msg *_task) 725{ 726 struct usb2_com_cfg_task *task = 727 (struct usb2_com_cfg_task *)_task; 728 struct usb2_com_softc *sc = task->sc; 729 uint8_t notch_bits; 730 uint8_t any_bits; 731 uint8_t prev_value; 732 uint8_t last_value; 733 uint8_t mask; 734 735 if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) { 736 return; 737 } 738 739 mask = 0; 740 /* compute callback mask */ 741 if (sc->sc_callback->usb2_com_cfg_set_dtr) 742 mask |= UCOM_LS_DTR; 743 if (sc->sc_callback->usb2_com_cfg_set_rts) 744 mask |= UCOM_LS_RTS; 745 if (sc->sc_callback->usb2_com_cfg_set_break) 746 mask |= UCOM_LS_BREAK; 747 748 /* compute the bits we are to program */ 749 notch_bits = (sc->sc_pls_set & sc->sc_pls_clr) & mask; 750 any_bits = (sc->sc_pls_set | sc->sc_pls_clr) & mask; 751 prev_value = sc->sc_pls_curr ^ notch_bits; 752 last_value = sc->sc_pls_curr; 753 754 /* reset programmed line state */ 755 sc->sc_pls_curr = 0; 756 sc->sc_pls_set = 0; 757 sc->sc_pls_clr = 0; 758 759 /* ensure that we don't loose any levels */ 760 if (notch_bits & UCOM_LS_DTR) 761 sc->sc_callback->usb2_com_cfg_set_dtr(sc, 762 (prev_value & UCOM_LS_DTR) ? 1 : 0); 763 if (notch_bits & UCOM_LS_RTS) 764 sc->sc_callback->usb2_com_cfg_set_rts(sc, 765 (prev_value & UCOM_LS_RTS) ? 1 : 0); 766 if (notch_bits & UCOM_LS_BREAK) 767 sc->sc_callback->usb2_com_cfg_set_break(sc, 768 (prev_value & UCOM_LS_BREAK) ? 1 : 0); 769 770 /* set last value */ 771 if (any_bits & UCOM_LS_DTR) 772 sc->sc_callback->usb2_com_cfg_set_dtr(sc, 773 (last_value & UCOM_LS_DTR) ? 1 : 0); 774 if (any_bits & UCOM_LS_RTS) 775 sc->sc_callback->usb2_com_cfg_set_rts(sc, 776 (last_value & UCOM_LS_RTS) ? 1 : 0); 777 if (any_bits & UCOM_LS_BREAK) 778 sc->sc_callback->usb2_com_cfg_set_break(sc, 779 (last_value & UCOM_LS_BREAK) ? 1 : 0); 780} 781 782static void 783usb2_com_line_state(struct usb2_com_softc *sc, 784 uint8_t set_bits, uint8_t clear_bits) 785{ 786 mtx_assert(sc->sc_mtx, MA_OWNED); 787 788 if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) { 789 return; 790 } 791 792 DPRINTF("on=0x%02x, off=0x%02x\n", set_bits, clear_bits); 793 794 /* update current programmed line state */ 795 sc->sc_pls_curr |= set_bits; 796 sc->sc_pls_curr &= ~clear_bits; 797 sc->sc_pls_set |= set_bits; 798 sc->sc_pls_clr |= clear_bits; 799 800 /* defer driver programming */ 801 usb2_com_queue_command(sc, usb2_com_cfg_line_state, NULL, 802 &sc->sc_line_state_task[0].hdr, 803 &sc->sc_line_state_task[1].hdr); 804} 805 806static void 807usb2_com_break(struct usb2_com_softc *sc, uint8_t onoff) 808{ 809 DPRINTF("onoff = %d\n", onoff); 810 811 if (onoff) 812 usb2_com_line_state(sc, UCOM_LS_BREAK, 0); 813 else 814 usb2_com_line_state(sc, 0, UCOM_LS_BREAK); 815} 816 817static void 818usb2_com_dtr(struct usb2_com_softc *sc, uint8_t onoff) 819{ 820 DPRINTF("onoff = %d\n", onoff); 821 822 if (onoff) 823 usb2_com_line_state(sc, UCOM_LS_DTR, 0); 824 else 825 usb2_com_line_state(sc, 0, UCOM_LS_DTR); 826} 827 828static void 829usb2_com_rts(struct usb2_com_softc *sc, uint8_t onoff) 830{ 831 DPRINTF("onoff = %d\n", onoff); 832 833 if (onoff) 834 usb2_com_line_state(sc, UCOM_LS_RTS, 0); 835 else 836 usb2_com_line_state(sc, 0, UCOM_LS_RTS); 837} 838 839static void 840usb2_com_cfg_status_change(struct usb2_proc_msg *_task) 841{ 842 struct usb2_com_cfg_task *task = 843 (struct usb2_com_cfg_task *)_task; 844 struct usb2_com_softc *sc = task->sc; 845 struct tty *tp; 846 uint8_t new_msr; 847 uint8_t new_lsr; 848 uint8_t onoff; 849 850 tp = sc->sc_tty; 851 852 mtx_assert(sc->sc_mtx, MA_OWNED); 853 854 if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) { 855 return; 856 } 857 if (sc->sc_callback->usb2_com_cfg_get_status == NULL) { 858 return; 859 } 860 /* get status */ 861 862 new_msr = 0; 863 new_lsr = 0; 864 865 (sc->sc_callback->usb2_com_cfg_get_status) (sc, &new_lsr, &new_msr); 866 867 if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) { 868 /* TTY device closed */ 869 return; 870 } 871 onoff = ((sc->sc_msr ^ new_msr) & SER_DCD); 872 873 sc->sc_msr = new_msr; 874 sc->sc_lsr = new_lsr; 875 876 if (onoff) { 877 878 onoff = (sc->sc_msr & SER_DCD) ? 1 : 0; 879 880 DPRINTF("DCD changed to %d\n", onoff); 881 882 ttydisc_modem(tp, onoff); 883 } 884} 885 886void 887usb2_com_status_change(struct usb2_com_softc *sc) 888{ 889 mtx_assert(sc->sc_mtx, MA_OWNED); 890 891 if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) { 892 return; 893 } 894 DPRINTF("\n"); 895 896 usb2_com_queue_command(sc, usb2_com_cfg_status_change, NULL, 897 &sc->sc_status_task[0].hdr, 898 &sc->sc_status_task[1].hdr); 899} 900 901static void 902usb2_com_cfg_param(struct usb2_proc_msg *_task) 903{ 904 struct usb2_com_param_task *task = 905 (struct usb2_com_param_task *)_task; 906 struct usb2_com_softc *sc = task->sc; 907 908 if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) { 909 return; 910 } 911 if (sc->sc_callback->usb2_com_cfg_param == NULL) { 912 return; 913 } 914 915 (sc->sc_callback->usb2_com_cfg_param) (sc, &task->termios_copy); 916 917 /* wait a little */ 918 usb2_pause_mtx(sc->sc_mtx, hz / 10); 919} 920 921static int 922usb2_com_param(struct tty *tp, struct termios *t) 923{ 924 struct usb2_com_softc *sc = tty_softc(tp); 925 uint8_t opened; 926 int error; 927 928 mtx_assert(sc->sc_mtx, MA_OWNED); 929 930 opened = 0; 931 error = 0; 932 933 if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) { 934 935 /* XXX the TTY layer should call "open()" first! */ 936 937 error = usb2_com_open(tp); 938 if (error) { 939 goto done; 940 } 941 opened = 1; 942 } 943 DPRINTF("sc = %p\n", sc); 944 945 /* Check requested parameters. */ 946 if (t->c_ospeed < 0) { 947 DPRINTF("negative ospeed\n"); 948 error = EINVAL; 949 goto done; 950 } 951 if (t->c_ispeed && (t->c_ispeed != t->c_ospeed)) { 952 DPRINTF("mismatch ispeed and ospeed\n"); 953 error = EINVAL; 954 goto done; 955 } 956 t->c_ispeed = t->c_ospeed; 957 958 if (sc->sc_callback->usb2_com_pre_param) { 959 /* Let the lower layer verify the parameters */ 960 error = (sc->sc_callback->usb2_com_pre_param) (sc, t); 961 if (error) { 962 DPRINTF("callback error = %d\n", error); 963 goto done; 964 } 965 } 966 967 /* Disable transfers */ 968 sc->sc_flag &= ~UCOM_FLAG_GP_DATA; 969 970 /* Queue baud rate programming command first */ 971 usb2_com_queue_command(sc, usb2_com_cfg_param, t, 972 &sc->sc_param_task[0].hdr, 973 &sc->sc_param_task[1].hdr); 974 975 /* Queue transfer enable command last */ 976 usb2_com_queue_command(sc, usb2_com_cfg_start_transfers, NULL, 977 &sc->sc_start_task[0].hdr, 978 &sc->sc_start_task[1].hdr); 979 980 if (t->c_cflag & CRTS_IFLOW) { 981 sc->sc_flag |= UCOM_FLAG_RTS_IFLOW; 982 } else if (sc->sc_flag & UCOM_FLAG_RTS_IFLOW) { 983 sc->sc_flag &= ~UCOM_FLAG_RTS_IFLOW; 984 usb2_com_modem(tp, SER_RTS, 0); 985 } 986done: 987 if (error) { 988 if (opened) { 989 usb2_com_close(tp); 990 } 991 } 992 return (error); 993} 994 995static void 996usb2_com_outwakeup(struct tty *tp) 997{ 998 struct usb2_com_softc *sc = tty_softc(tp); 999 1000 mtx_assert(sc->sc_mtx, MA_OWNED); 1001 1002 DPRINTF("sc = %p\n", sc); 1003 1004 if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) { 1005 /* The higher layer is not ready */ 1006 return; 1007 } 1008 sc->sc_flag |= UCOM_FLAG_WR_START; 1009 1010 usb2_com_start_transfers(sc); 1011} 1012 1013/*------------------------------------------------------------------------* 1014 * usb2_com_get_data 1015 * 1016 * Return values: 1017 * 0: No data is available. 1018 * Else: Data is available. 1019 *------------------------------------------------------------------------*/ 1020uint8_t 1021usb2_com_get_data(struct usb2_com_softc *sc, struct usb2_page_cache *pc, 1022 uint32_t offset, uint32_t len, uint32_t *actlen) 1023{ 1024 struct usb2_page_search res; 1025 struct tty *tp = sc->sc_tty; 1026 uint32_t cnt; 1027 uint32_t offset_orig; 1028 1029 mtx_assert(sc->sc_mtx, MA_OWNED); 1030 1031 if ((!(sc->sc_flag & UCOM_FLAG_HL_READY)) || 1032 (!(sc->sc_flag & UCOM_FLAG_GP_DATA)) || 1033 (!(sc->sc_flag & UCOM_FLAG_WR_START))) { 1034 actlen[0] = 0; 1035 return (0); /* multiport device polling */ 1036 } 1037 offset_orig = offset; 1038 1039 while (len != 0) { 1040 1041 usb2_get_page(pc, offset, &res); 1042 1043 if (res.length > len) { 1044 res.length = len; 1045 } 1046 /* copy data directly into USB buffer */ 1047 cnt = ttydisc_getc(tp, res.buffer, res.length); 1048 1049 offset += cnt; 1050 len -= cnt; 1051 1052 if (cnt < res.length) { 1053 /* end of buffer */ 1054 break; 1055 } 1056 } 1057 1058 actlen[0] = offset - offset_orig; 1059 1060 DPRINTF("cnt=%d\n", actlen[0]); 1061 1062 if (actlen[0] == 0) { 1063 return (0); 1064 } 1065 return (1); 1066} 1067 1068void 1069usb2_com_put_data(struct usb2_com_softc *sc, struct usb2_page_cache *pc, 1070 uint32_t offset, uint32_t len) 1071{ 1072 struct usb2_page_search res; 1073 struct tty *tp = sc->sc_tty; 1074 char *buf; 1075 uint32_t cnt; 1076 1077 mtx_assert(sc->sc_mtx, MA_OWNED); 1078 1079 if ((!(sc->sc_flag & UCOM_FLAG_HL_READY)) || 1080 (!(sc->sc_flag & UCOM_FLAG_GP_DATA))) { 1081 return; /* multiport device polling */ 1082 } 1083 if (len == 0) 1084 return; /* no data */ 1085 1086 /* set a flag to prevent recursation ? */ 1087 1088 while (len > 0) { 1089 1090 usb2_get_page(pc, offset, &res); 1091 1092 if (res.length > len) { 1093 res.length = len; 1094 } 1095 len -= res.length; 1096 offset += res.length; 1097 1098 /* pass characters to tty layer */ 1099 1100 buf = res.buffer; 1101 cnt = res.length; 1102 1103 /* first check if we can pass the buffer directly */ 1104 1105 if (ttydisc_can_bypass(tp)) { 1106 if (ttydisc_rint_bypass(tp, buf, cnt) != cnt) { 1107 DPRINTF("tp=%p, data lost\n", tp); 1108 } 1109 continue; 1110 } 1111 /* need to loop */ 1112 1113 for (cnt = 0; cnt != res.length; cnt++) { 1114 if (ttydisc_rint(tp, buf[cnt], 0) == -1) { 1115 /* XXX what should we do? */ 1116 1117 DPRINTF("tp=%p, lost %d " 1118 "chars\n", tp, res.length - cnt); 1119 break; 1120 } 1121 } 1122 } 1123 ttydisc_rint_done(tp); 1124} 1125 1126static void 1127usb2_com_free(void *xsc) 1128{ 1129 struct usb2_com_softc *sc = xsc; 1130 1131 mtx_lock(sc->sc_mtx); 1132 sc->sc_ttyfreed = 1; 1133 usb2_cv_signal(&sc->sc_cv); 1134 mtx_unlock(sc->sc_mtx); 1135} 1136