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 #include <sys/cons.h> 90 #include <sys/kdb.h> 91 92 #include <dev/usb/usb.h> 93 #include <dev/usb/usbdi.h> 94 #include <dev/usb/usbdi_util.h> 95 96 #define USB_DEBUG_VAR ucom_debug 97 #include <dev/usb/usb_debug.h> 98 #include <dev/usb/usb_busdma.h> 99 #include <dev/usb/usb_process.h> 100 101 #include <dev/usb/serial/usb_serial.h> 102 103 #include "opt_gdb.h" 104 105 SYSCTL_NODE(_hw_usb, OID_AUTO, ucom, CTLFLAG_RW, 0, "USB ucom"); 106 107 #ifdef USB_DEBUG 108 static int ucom_debug = 0; 109 110 SYSCTL_INT(_hw_usb_ucom, OID_AUTO, debug, CTLFLAG_RW, 111 &ucom_debug, 0, "ucom debug level"); 112 #endif 113 114 #define UCOM_CONS_BUFSIZE 1024 115 116 static uint8_t ucom_cons_rx_buf[UCOM_CONS_BUFSIZE]; 117 static uint8_t ucom_cons_tx_buf[UCOM_CONS_BUFSIZE]; 118 119 static unsigned int ucom_cons_rx_low = 0; 120 static unsigned int ucom_cons_rx_high = 0; 121 122 static unsigned int ucom_cons_tx_low = 0; 123 static unsigned int ucom_cons_tx_high = 0; 124 125 static int ucom_cons_unit = -1; 126 static int ucom_cons_subunit = 0; 127 static int ucom_cons_baud = 9600; 128 static struct ucom_softc *ucom_cons_softc = NULL; 129 130 TUNABLE_INT("hw.usb.ucom.cons_unit", &ucom_cons_unit); 131 SYSCTL_INT(_hw_usb_ucom, OID_AUTO, cons_unit, CTLFLAG_RW, 132 &ucom_cons_unit, 0, "console unit number"); 133 TUNABLE_INT("hw.usb.ucom.cons_subunit", &ucom_cons_subunit); 134 SYSCTL_INT(_hw_usb_ucom, OID_AUTO, cons_subunit, CTLFLAG_RW, 135 &ucom_cons_subunit, 0, "console subunit number"); 136 TUNABLE_INT("hw.usb.ucom.cons_baud", &ucom_cons_baud); 137 SYSCTL_INT(_hw_usb_ucom, OID_AUTO, cons_baud, CTLFLAG_RW, 138 &ucom_cons_baud, 0, "console baud rate"); 139 140 static usb_proc_callback_t ucom_cfg_start_transfers; 141 static usb_proc_callback_t ucom_cfg_open; 142 static usb_proc_callback_t ucom_cfg_close; 143 static usb_proc_callback_t ucom_cfg_line_state; 144 static usb_proc_callback_t ucom_cfg_status_change; 145 static usb_proc_callback_t ucom_cfg_param; 146 147 static int ucom_unit_alloc(void); 148 static void ucom_unit_free(int); 149 static int ucom_attach_tty(struct ucom_super_softc *, struct ucom_softc *); 150 static void ucom_detach_tty(struct ucom_softc *); 151 static void ucom_queue_command(struct ucom_softc *, 152 usb_proc_callback_t *, struct termios *pt, 153 struct usb_proc_msg *t0, struct usb_proc_msg *t1); 154 static void ucom_shutdown(struct ucom_softc *); 155 static void ucom_ring(struct ucom_softc *, uint8_t); 156 static void ucom_break(struct ucom_softc *, uint8_t); 157 static void ucom_dtr(struct ucom_softc *, uint8_t); 158 static void ucom_rts(struct ucom_softc *, uint8_t); 159 160 static tsw_open_t ucom_open; 161 static tsw_close_t ucom_close; 162 static tsw_ioctl_t ucom_ioctl; 163 static tsw_modem_t ucom_modem; 164 static tsw_param_t ucom_param; 165 static tsw_outwakeup_t ucom_outwakeup; 166 static tsw_free_t ucom_free; 167 168 static struct ttydevsw ucom_class = { 169 .tsw_flags = TF_INITLOCK | TF_CALLOUT, 170 .tsw_open = ucom_open, 171 .tsw_close = ucom_close, 172 .tsw_outwakeup = ucom_outwakeup, 173 .tsw_ioctl = ucom_ioctl, 174 .tsw_param = ucom_param, 175 .tsw_modem = ucom_modem, 176 .tsw_free = ucom_free, 177 }; 178 179 MODULE_DEPEND(ucom, usb, 1, 1, 1); 180 MODULE_VERSION(ucom, 1); 181 182 #define UCOM_UNIT_MAX 128 /* limits size of ucom_bitmap */ 183 184 static uint8_t ucom_bitmap[(UCOM_UNIT_MAX + 7) / 8]; 185 static struct mtx ucom_bitmap_mtx; 186 MTX_SYSINIT(ucom_bitmap_mtx, &ucom_bitmap_mtx, "ucom bitmap", MTX_DEF); 187 188 #define UCOM_TTY_PREFIX "U" 189 190 /* 191 * Mark a unit number (the X in cuaUX) as in use. 192 * 193 * Note that devices using a different naming scheme (see ucom_tty_name() 194 * callback) still use this unit allocation. 195 */ 196 static int 197 ucom_unit_alloc(void) 198 { 199 int unit; 200 201 mtx_lock(&ucom_bitmap_mtx); 202 203 for (unit = 0; unit < UCOM_UNIT_MAX; unit++) { 204 if ((ucom_bitmap[unit / 8] & (1 << (unit % 8))) == 0) { 205 ucom_bitmap[unit / 8] |= (1 << (unit % 8)); 206 break; 207 } 208 } 209 210 mtx_unlock(&ucom_bitmap_mtx); 211 212 if (unit == UCOM_UNIT_MAX) 213 return -1; 214 else 215 return unit; 216 } 217 218 /* 219 * Mark the unit number as not in use. 220 */ 221 static void 222 ucom_unit_free(int unit) 223 { 224 mtx_lock(&ucom_bitmap_mtx); 225 226 ucom_bitmap[unit / 8] &= ~(1 << (unit % 8)); 227 228 mtx_unlock(&ucom_bitmap_mtx); 229 } 230 231 /* 232 * Setup a group of one or more serial ports. 233 * 234 * The mutex pointed to by "mtx" is applied before all 235 * callbacks are called back. Also "mtx" must be applied 236 * before calling into the ucom-layer! 237 */ 238 int 239 ucom_attach(struct ucom_super_softc *ssc, struct ucom_softc *sc, 240 uint32_t subunits, void *parent, 241 const struct ucom_callback *callback, struct mtx *mtx) 242 { 243 uint32_t subunit; 244 int error = 0; 245 246 if ((sc == NULL) || 247 (subunits == 0) || 248 (callback == NULL)) { 249 return (EINVAL); 250 } 251 252 ssc->sc_unit = ucom_unit_alloc(); 253 if (ssc->sc_unit == -1) 254 return (ENOMEM); 255 256 error = usb_proc_create(&ssc->sc_tq, mtx, "ucom", USB_PRI_MED); 257 if (error) { 258 ucom_unit_free(ssc->sc_unit); 259 return (error); 260 } 261 ssc->sc_subunits = subunits; 262 263 for (subunit = 0; subunit < ssc->sc_subunits; subunit++) { 264 sc[subunit].sc_subunit = subunit; 265 sc[subunit].sc_super = ssc; 266 sc[subunit].sc_mtx = mtx; 267 sc[subunit].sc_parent = parent; 268 sc[subunit].sc_callback = callback; 269 270 error = ucom_attach_tty(ssc, &sc[subunit]); 271 if (error) { 272 ucom_detach(ssc, &sc[0]); 273 return (error); 274 } 275 sc[subunit].sc_flag |= UCOM_FLAG_ATTACHED; 276 } 277 278 DPRINTF("tp = %p, unit = %d, subunits = %d\n", 279 sc->sc_tty, ssc->sc_unit, ssc->sc_subunits); 280 281 return (0); 282 } 283 284 /* 285 * NOTE: the following function will do nothing if 286 * the structure pointed to by "ssc" and "sc" is zero. 287 */ 288 void 289 ucom_detach(struct ucom_super_softc *ssc, struct ucom_softc *sc) 290 { 291 uint32_t subunit; 292 293 usb_proc_drain(&ssc->sc_tq); 294 295 for (subunit = 0; subunit < ssc->sc_subunits; subunit++) { 296 if (sc[subunit].sc_flag & UCOM_FLAG_ATTACHED) { 297 298 ucom_detach_tty(&sc[subunit]); 299 300 /* avoid duplicate detach */ 301 sc[subunit].sc_flag &= ~UCOM_FLAG_ATTACHED; 302 } 303 } 304 ucom_unit_free(ssc->sc_unit); 305 usb_proc_free(&ssc->sc_tq); 306 } 307 308 static int 309 ucom_attach_tty(struct ucom_super_softc *ssc, struct ucom_softc *sc) 310 { 311 struct tty *tp; 312 char buf[32]; /* temporary TTY device name buffer */ 313 314 tp = tty_alloc_mutex(&ucom_class, sc, sc->sc_mtx); 315 if (tp == NULL) 316 return (ENOMEM); 317 318 /* Check if the client has a custom TTY name */ 319 buf[0] = '\0'; 320 if (sc->sc_callback->ucom_tty_name) { 321 sc->sc_callback->ucom_tty_name(sc, buf, 322 sizeof(buf), ssc->sc_unit, sc->sc_subunit); 323 } 324 if (buf[0] == 0) { 325 /* Use default TTY name */ 326 if (ssc->sc_subunits > 1) { 327 /* multiple modems in one */ 328 snprintf(buf, sizeof(buf), UCOM_TTY_PREFIX "%u.%u", 329 ssc->sc_unit, sc->sc_subunit); 330 } else { 331 /* single modem */ 332 snprintf(buf, sizeof(buf), UCOM_TTY_PREFIX "%u", 333 ssc->sc_unit); 334 } 335 } 336 tty_makedev(tp, NULL, "%s", buf); 337 338 sc->sc_tty = tp; 339 340 DPRINTF("ttycreate: %s\n", buf); 341 cv_init(&sc->sc_cv, "ucom"); 342 343 /* Check if this device should be a console */ 344 if ((ucom_cons_softc == NULL) && 345 (ssc->sc_unit == ucom_cons_unit) && 346 (sc->sc_subunit == ucom_cons_subunit)) { 347 struct termios t; 348 349 DPRINTF("unit %d subunit %d is console", ssc->sc_unit, sc->sc_subunit); 350 351 ucom_cons_softc = sc; 352 353 memset(&t, 0, sizeof(t)); 354 t.c_ispeed = ucom_cons_baud; 355 t.c_ospeed = t.c_ispeed; 356 t.c_cflag = CS8; 357 358 mtx_lock(ucom_cons_softc->sc_mtx); 359 ucom_cons_rx_low = 0; 360 ucom_cons_rx_high = 0; 361 ucom_cons_tx_low = 0; 362 ucom_cons_tx_high = 0; 363 sc->sc_flag |= UCOM_FLAG_CONSOLE; 364 ucom_open(ucom_cons_softc->sc_tty); 365 ucom_param(ucom_cons_softc->sc_tty, &t); 366 mtx_unlock(ucom_cons_softc->sc_mtx); 367 } 368 369 return (0); 370 } 371 372 static void 373 ucom_detach_tty(struct ucom_softc *sc) 374 { 375 struct tty *tp = sc->sc_tty; 376 377 DPRINTF("sc = %p, tp = %p\n", sc, sc->sc_tty); 378 379 if (sc->sc_flag & UCOM_FLAG_CONSOLE) { 380 mtx_lock(ucom_cons_softc->sc_mtx); 381 ucom_close(ucom_cons_softc->sc_tty); 382 sc->sc_flag &= ~UCOM_FLAG_CONSOLE; 383 mtx_unlock(ucom_cons_softc->sc_mtx); 384 ucom_cons_softc = NULL; 385 } 386 387 /* the config thread has been stopped when we get here */ 388 389 mtx_lock(sc->sc_mtx); 390 sc->sc_flag |= UCOM_FLAG_GONE; 391 sc->sc_flag &= ~(UCOM_FLAG_HL_READY | UCOM_FLAG_LL_READY); 392 mtx_unlock(sc->sc_mtx); 393 if (tp) { 394 tty_lock(tp); 395 396 ucom_close(tp); /* close, if any */ 397 398 tty_rel_gone(tp); 399 400 mtx_lock(sc->sc_mtx); 401 /* Wait for the callback after the TTY is torn down */ 402 while (sc->sc_ttyfreed == 0) 403 cv_wait(&sc->sc_cv, sc->sc_mtx); 404 /* 405 * make sure that read and write transfers are stopped 406 */ 407 if (sc->sc_callback->ucom_stop_read) { 408 (sc->sc_callback->ucom_stop_read) (sc); 409 } 410 if (sc->sc_callback->ucom_stop_write) { 411 (sc->sc_callback->ucom_stop_write) (sc); 412 } 413 mtx_unlock(sc->sc_mtx); 414 } 415 cv_destroy(&sc->sc_cv); 416 } 417 418 void 419 ucom_set_pnpinfo_usb(struct ucom_super_softc *ssc, device_t dev) 420 { 421 char buf[64]; 422 uint8_t iface_index; 423 struct usb_attach_arg *uaa; 424 425 snprintf(buf, sizeof(buf), "ttyname=%s%d ttyports=%d", 426 UCOM_TTY_PREFIX, ssc->sc_unit, ssc->sc_subunits); 427 428 /* Store the PNP info in the first interface for the dev */ 429 uaa = device_get_ivars(dev); 430 iface_index = uaa->info.bIfaceIndex; 431 432 if (usbd_set_pnpinfo(uaa->device, iface_index, buf) != 0) 433 device_printf(dev, "Could not set PNP info\n"); 434 } 435 436 static void 437 ucom_queue_command(struct ucom_softc *sc, 438 usb_proc_callback_t *fn, struct termios *pt, 439 struct usb_proc_msg *t0, struct usb_proc_msg *t1) 440 { 441 struct ucom_super_softc *ssc = sc->sc_super; 442 struct ucom_param_task *task; 443 444 mtx_assert(sc->sc_mtx, MA_OWNED); 445 446 if (usb_proc_is_gone(&ssc->sc_tq)) { 447 DPRINTF("proc is gone\n"); 448 return; /* nothing to do */ 449 } 450 /* 451 * NOTE: The task cannot get executed before we drop the 452 * "sc_mtx" mutex. It is safe to update fields in the message 453 * structure after that the message got queued. 454 */ 455 task = (struct ucom_param_task *) 456 usb_proc_msignal(&ssc->sc_tq, t0, t1); 457 458 /* Setup callback and softc pointers */ 459 task->hdr.pm_callback = fn; 460 task->sc = sc; 461 462 /* 463 * Make a copy of the termios. This field is only present if 464 * the "pt" field is not NULL. 465 */ 466 if (pt != NULL) 467 task->termios_copy = *pt; 468 469 /* 470 * Closing the device should be synchronous. 471 */ 472 if (fn == ucom_cfg_close) 473 usb_proc_mwait(&ssc->sc_tq, t0, t1); 474 475 /* 476 * In case of multiple configure requests, 477 * keep track of the last one! 478 */ 479 if (fn == ucom_cfg_start_transfers) 480 sc->sc_last_start_xfer = &task->hdr; 481 } 482 483 static void 484 ucom_shutdown(struct ucom_softc *sc) 485 { 486 struct tty *tp = sc->sc_tty; 487 488 mtx_assert(sc->sc_mtx, MA_OWNED); 489 490 DPRINTF("\n"); 491 492 /* 493 * Hang up if necessary: 494 */ 495 if (tp->t_termios.c_cflag & HUPCL) { 496 ucom_modem(tp, 0, SER_DTR); 497 } 498 } 499 500 /* 501 * Return values: 502 * 0: normal 503 * else: taskqueue is draining or gone 504 */ 505 uint8_t 506 ucom_cfg_is_gone(struct ucom_softc *sc) 507 { 508 struct ucom_super_softc *ssc = sc->sc_super; 509 510 return (usb_proc_is_gone(&ssc->sc_tq)); 511 } 512 513 static void 514 ucom_cfg_start_transfers(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 if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) { 521 return; 522 } 523 if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) { 524 /* TTY device closed */ 525 return; 526 } 527 528 if (_task == sc->sc_last_start_xfer) 529 sc->sc_flag |= UCOM_FLAG_GP_DATA; 530 531 if (sc->sc_callback->ucom_start_read) { 532 (sc->sc_callback->ucom_start_read) (sc); 533 } 534 if (sc->sc_callback->ucom_start_write) { 535 (sc->sc_callback->ucom_start_write) (sc); 536 } 537 } 538 539 static void 540 ucom_start_transfers(struct ucom_softc *sc) 541 { 542 if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) { 543 return; 544 } 545 /* 546 * Make sure that data transfers are started in both 547 * directions: 548 */ 549 if (sc->sc_callback->ucom_start_read) { 550 (sc->sc_callback->ucom_start_read) (sc); 551 } 552 if (sc->sc_callback->ucom_start_write) { 553 (sc->sc_callback->ucom_start_write) (sc); 554 } 555 } 556 557 static void 558 ucom_cfg_open(struct usb_proc_msg *_task) 559 { 560 struct ucom_cfg_task *task = 561 (struct ucom_cfg_task *)_task; 562 struct ucom_softc *sc = task->sc; 563 564 DPRINTF("\n"); 565 566 if (sc->sc_flag & UCOM_FLAG_LL_READY) { 567 568 /* already opened */ 569 570 } else { 571 572 sc->sc_flag |= UCOM_FLAG_LL_READY; 573 574 if (sc->sc_callback->ucom_cfg_open) { 575 (sc->sc_callback->ucom_cfg_open) (sc); 576 577 /* wait a little */ 578 usb_pause_mtx(sc->sc_mtx, hz / 10); 579 } 580 } 581 } 582 583 static int 584 ucom_open(struct tty *tp) 585 { 586 struct ucom_softc *sc = tty_softc(tp); 587 int error; 588 589 mtx_assert(sc->sc_mtx, MA_OWNED); 590 591 if (sc->sc_flag & UCOM_FLAG_GONE) { 592 return (ENXIO); 593 } 594 if (sc->sc_flag & UCOM_FLAG_HL_READY) { 595 /* already opened */ 596 return (0); 597 } 598 DPRINTF("tp = %p\n", tp); 599 600 if (sc->sc_callback->ucom_pre_open) { 601 /* 602 * give the lower layer a chance to disallow TTY open, for 603 * example if the device is not present: 604 */ 605 error = (sc->sc_callback->ucom_pre_open) (sc); 606 if (error) { 607 return (error); 608 } 609 } 610 sc->sc_flag |= UCOM_FLAG_HL_READY; 611 612 /* Disable transfers */ 613 sc->sc_flag &= ~UCOM_FLAG_GP_DATA; 614 615 sc->sc_lsr = 0; 616 sc->sc_msr = 0; 617 sc->sc_mcr = 0; 618 619 /* reset programmed line state */ 620 sc->sc_pls_curr = 0; 621 sc->sc_pls_set = 0; 622 sc->sc_pls_clr = 0; 623 624 ucom_queue_command(sc, ucom_cfg_open, NULL, 625 &sc->sc_open_task[0].hdr, 626 &sc->sc_open_task[1].hdr); 627 628 /* Queue transfer enable command last */ 629 ucom_queue_command(sc, ucom_cfg_start_transfers, NULL, 630 &sc->sc_start_task[0].hdr, 631 &sc->sc_start_task[1].hdr); 632 633 ucom_modem(tp, SER_DTR | SER_RTS, 0); 634 635 ucom_ring(sc, 0); 636 637 ucom_break(sc, 0); 638 639 ucom_status_change(sc); 640 641 return (0); 642 } 643 644 static void 645 ucom_cfg_close(struct usb_proc_msg *_task) 646 { 647 struct ucom_cfg_task *task = 648 (struct ucom_cfg_task *)_task; 649 struct ucom_softc *sc = task->sc; 650 651 DPRINTF("\n"); 652 653 if (sc->sc_flag & UCOM_FLAG_LL_READY) { 654 sc->sc_flag &= ~UCOM_FLAG_LL_READY; 655 if (sc->sc_callback->ucom_cfg_close) 656 (sc->sc_callback->ucom_cfg_close) (sc); 657 } else { 658 /* already closed */ 659 } 660 } 661 662 static void 663 ucom_close(struct tty *tp) 664 { 665 struct ucom_softc *sc = tty_softc(tp); 666 667 mtx_assert(sc->sc_mtx, MA_OWNED); 668 669 DPRINTF("tp=%p\n", tp); 670 671 if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) { 672 DPRINTF("tp=%p already closed\n", tp); 673 return; 674 } 675 ucom_shutdown(sc); 676 677 ucom_queue_command(sc, ucom_cfg_close, NULL, 678 &sc->sc_close_task[0].hdr, 679 &sc->sc_close_task[1].hdr); 680 681 sc->sc_flag &= ~(UCOM_FLAG_HL_READY | UCOM_FLAG_RTS_IFLOW); 682 683 if (sc->sc_callback->ucom_stop_read) { 684 (sc->sc_callback->ucom_stop_read) (sc); 685 } 686 } 687 688 static int 689 ucom_ioctl(struct tty *tp, u_long cmd, caddr_t data, struct thread *td) 690 { 691 struct ucom_softc *sc = tty_softc(tp); 692 int error; 693 694 mtx_assert(sc->sc_mtx, MA_OWNED); 695 696 if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) { 697 return (EIO); 698 } 699 DPRINTF("cmd = 0x%08lx\n", cmd); 700 701 switch (cmd) { 702 #if 0 703 case TIOCSRING: 704 ucom_ring(sc, 1); 705 error = 0; 706 break; 707 case TIOCCRING: 708 ucom_ring(sc, 0); 709 error = 0; 710 break; 711 #endif 712 case TIOCSBRK: 713 ucom_break(sc, 1); 714 error = 0; 715 break; 716 case TIOCCBRK: 717 ucom_break(sc, 0); 718 error = 0; 719 break; 720 default: 721 if (sc->sc_callback->ucom_ioctl) { 722 error = (sc->sc_callback->ucom_ioctl) 723 (sc, cmd, data, 0, td); 724 } else { 725 error = ENOIOCTL; 726 } 727 break; 728 } 729 return (error); 730 } 731 732 static int 733 ucom_modem(struct tty *tp, int sigon, int sigoff) 734 { 735 struct ucom_softc *sc = tty_softc(tp); 736 uint8_t onoff; 737 738 mtx_assert(sc->sc_mtx, MA_OWNED); 739 740 if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) { 741 return (0); 742 } 743 if ((sigon == 0) && (sigoff == 0)) { 744 745 if (sc->sc_mcr & SER_DTR) { 746 sigon |= SER_DTR; 747 } 748 if (sc->sc_mcr & SER_RTS) { 749 sigon |= SER_RTS; 750 } 751 if (sc->sc_msr & SER_CTS) { 752 sigon |= SER_CTS; 753 } 754 if (sc->sc_msr & SER_DCD) { 755 sigon |= SER_DCD; 756 } 757 if (sc->sc_msr & SER_DSR) { 758 sigon |= SER_DSR; 759 } 760 if (sc->sc_msr & SER_RI) { 761 sigon |= SER_RI; 762 } 763 return (sigon); 764 } 765 if (sigon & SER_DTR) { 766 sc->sc_mcr |= SER_DTR; 767 } 768 if (sigoff & SER_DTR) { 769 sc->sc_mcr &= ~SER_DTR; 770 } 771 if (sigon & SER_RTS) { 772 sc->sc_mcr |= SER_RTS; 773 } 774 if (sigoff & SER_RTS) { 775 sc->sc_mcr &= ~SER_RTS; 776 } 777 onoff = (sc->sc_mcr & SER_DTR) ? 1 : 0; 778 ucom_dtr(sc, onoff); 779 780 onoff = (sc->sc_mcr & SER_RTS) ? 1 : 0; 781 ucom_rts(sc, onoff); 782 783 return (0); 784 } 785 786 static void 787 ucom_cfg_line_state(struct usb_proc_msg *_task) 788 { 789 struct ucom_cfg_task *task = 790 (struct ucom_cfg_task *)_task; 791 struct ucom_softc *sc = task->sc; 792 uint8_t notch_bits; 793 uint8_t any_bits; 794 uint8_t prev_value; 795 uint8_t last_value; 796 uint8_t mask; 797 798 if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) { 799 return; 800 } 801 802 mask = 0; 803 /* compute callback mask */ 804 if (sc->sc_callback->ucom_cfg_set_dtr) 805 mask |= UCOM_LS_DTR; 806 if (sc->sc_callback->ucom_cfg_set_rts) 807 mask |= UCOM_LS_RTS; 808 if (sc->sc_callback->ucom_cfg_set_break) 809 mask |= UCOM_LS_BREAK; 810 if (sc->sc_callback->ucom_cfg_set_ring) 811 mask |= UCOM_LS_RING; 812 813 /* compute the bits we are to program */ 814 notch_bits = (sc->sc_pls_set & sc->sc_pls_clr) & mask; 815 any_bits = (sc->sc_pls_set | sc->sc_pls_clr) & mask; 816 prev_value = sc->sc_pls_curr ^ notch_bits; 817 last_value = sc->sc_pls_curr; 818 819 /* reset programmed line state */ 820 sc->sc_pls_curr = 0; 821 sc->sc_pls_set = 0; 822 sc->sc_pls_clr = 0; 823 824 /* ensure that we don't loose any levels */ 825 if (notch_bits & UCOM_LS_DTR) 826 sc->sc_callback->ucom_cfg_set_dtr(sc, 827 (prev_value & UCOM_LS_DTR) ? 1 : 0); 828 if (notch_bits & UCOM_LS_RTS) 829 sc->sc_callback->ucom_cfg_set_rts(sc, 830 (prev_value & UCOM_LS_RTS) ? 1 : 0); 831 if (notch_bits & UCOM_LS_BREAK) 832 sc->sc_callback->ucom_cfg_set_break(sc, 833 (prev_value & UCOM_LS_BREAK) ? 1 : 0); 834 if (notch_bits & UCOM_LS_RING) 835 sc->sc_callback->ucom_cfg_set_ring(sc, 836 (prev_value & UCOM_LS_RING) ? 1 : 0); 837 838 /* set last value */ 839 if (any_bits & UCOM_LS_DTR) 840 sc->sc_callback->ucom_cfg_set_dtr(sc, 841 (last_value & UCOM_LS_DTR) ? 1 : 0); 842 if (any_bits & UCOM_LS_RTS) 843 sc->sc_callback->ucom_cfg_set_rts(sc, 844 (last_value & UCOM_LS_RTS) ? 1 : 0); 845 if (any_bits & UCOM_LS_BREAK) 846 sc->sc_callback->ucom_cfg_set_break(sc, 847 (last_value & UCOM_LS_BREAK) ? 1 : 0); 848 if (any_bits & UCOM_LS_RING) 849 sc->sc_callback->ucom_cfg_set_ring(sc, 850 (last_value & UCOM_LS_RING) ? 1 : 0); 851 } 852 853 static void 854 ucom_line_state(struct ucom_softc *sc, 855 uint8_t set_bits, uint8_t clear_bits) 856 { 857 mtx_assert(sc->sc_mtx, MA_OWNED); 858 859 if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) { 860 return; 861 } 862 863 DPRINTF("on=0x%02x, off=0x%02x\n", set_bits, clear_bits); 864 865 /* update current programmed line state */ 866 sc->sc_pls_curr |= set_bits; 867 sc->sc_pls_curr &= ~clear_bits; 868 sc->sc_pls_set |= set_bits; 869 sc->sc_pls_clr |= clear_bits; 870 871 /* defer driver programming */ 872 ucom_queue_command(sc, ucom_cfg_line_state, NULL, 873 &sc->sc_line_state_task[0].hdr, 874 &sc->sc_line_state_task[1].hdr); 875 } 876 877 static void 878 ucom_ring(struct ucom_softc *sc, uint8_t onoff) 879 { 880 DPRINTF("onoff = %d\n", onoff); 881 882 if (onoff) 883 ucom_line_state(sc, UCOM_LS_RING, 0); 884 else 885 ucom_line_state(sc, 0, UCOM_LS_RING); 886 } 887 888 static void 889 ucom_break(struct ucom_softc *sc, uint8_t onoff) 890 { 891 DPRINTF("onoff = %d\n", onoff); 892 893 if (onoff) 894 ucom_line_state(sc, UCOM_LS_BREAK, 0); 895 else 896 ucom_line_state(sc, 0, UCOM_LS_BREAK); 897 } 898 899 static void 900 ucom_dtr(struct ucom_softc *sc, uint8_t onoff) 901 { 902 DPRINTF("onoff = %d\n", onoff); 903 904 if (onoff) 905 ucom_line_state(sc, UCOM_LS_DTR, 0); 906 else 907 ucom_line_state(sc, 0, UCOM_LS_DTR); 908 } 909 910 static void 911 ucom_rts(struct ucom_softc *sc, uint8_t onoff) 912 { 913 DPRINTF("onoff = %d\n", onoff); 914 915 if (onoff) 916 ucom_line_state(sc, UCOM_LS_RTS, 0); 917 else 918 ucom_line_state(sc, 0, UCOM_LS_RTS); 919 } 920 921 static void 922 ucom_cfg_status_change(struct usb_proc_msg *_task) 923 { 924 struct ucom_cfg_task *task = 925 (struct ucom_cfg_task *)_task; 926 struct ucom_softc *sc = task->sc; 927 struct tty *tp; 928 uint8_t new_msr; 929 uint8_t new_lsr; 930 uint8_t onoff; 931 uint8_t lsr_delta; 932 933 tp = sc->sc_tty; 934 935 mtx_assert(sc->sc_mtx, MA_OWNED); 936 937 if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) { 938 return; 939 } 940 if (sc->sc_callback->ucom_cfg_get_status == NULL) { 941 return; 942 } 943 /* get status */ 944 945 new_msr = 0; 946 new_lsr = 0; 947 948 (sc->sc_callback->ucom_cfg_get_status) (sc, &new_lsr, &new_msr); 949 950 if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) { 951 /* TTY device closed */ 952 return; 953 } 954 onoff = ((sc->sc_msr ^ new_msr) & SER_DCD); 955 lsr_delta = (sc->sc_lsr ^ new_lsr); 956 957 sc->sc_msr = new_msr; 958 sc->sc_lsr = new_lsr; 959 960 if (onoff) { 961 962 onoff = (sc->sc_msr & SER_DCD) ? 1 : 0; 963 964 DPRINTF("DCD changed to %d\n", onoff); 965 966 ttydisc_modem(tp, onoff); 967 } 968 969 if ((lsr_delta & ULSR_BI) && (sc->sc_lsr & ULSR_BI)) { 970 971 DPRINTF("BREAK detected\n"); 972 973 ttydisc_rint(tp, 0, TRE_BREAK); 974 ttydisc_rint_done(tp); 975 } 976 977 if ((lsr_delta & ULSR_FE) && (sc->sc_lsr & ULSR_FE)) { 978 979 DPRINTF("Frame error detected\n"); 980 981 ttydisc_rint(tp, 0, TRE_FRAMING); 982 ttydisc_rint_done(tp); 983 } 984 985 if ((lsr_delta & ULSR_PE) && (sc->sc_lsr & ULSR_PE)) { 986 987 DPRINTF("Parity error detected\n"); 988 989 ttydisc_rint(tp, 0, TRE_PARITY); 990 ttydisc_rint_done(tp); 991 } 992 } 993 994 void 995 ucom_status_change(struct ucom_softc *sc) 996 { 997 mtx_assert(sc->sc_mtx, MA_OWNED); 998 999 if (sc->sc_flag & UCOM_FLAG_CONSOLE) 1000 return; /* not supported */ 1001 1002 if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) { 1003 return; 1004 } 1005 DPRINTF("\n"); 1006 1007 ucom_queue_command(sc, ucom_cfg_status_change, NULL, 1008 &sc->sc_status_task[0].hdr, 1009 &sc->sc_status_task[1].hdr); 1010 } 1011 1012 static void 1013 ucom_cfg_param(struct usb_proc_msg *_task) 1014 { 1015 struct ucom_param_task *task = 1016 (struct ucom_param_task *)_task; 1017 struct ucom_softc *sc = task->sc; 1018 1019 if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) { 1020 return; 1021 } 1022 if (sc->sc_callback->ucom_cfg_param == NULL) { 1023 return; 1024 } 1025 1026 (sc->sc_callback->ucom_cfg_param) (sc, &task->termios_copy); 1027 1028 /* wait a little */ 1029 usb_pause_mtx(sc->sc_mtx, hz / 10); 1030 } 1031 1032 static int 1033 ucom_param(struct tty *tp, struct termios *t) 1034 { 1035 struct ucom_softc *sc = tty_softc(tp); 1036 uint8_t opened; 1037 int error; 1038 1039 mtx_assert(sc->sc_mtx, MA_OWNED); 1040 1041 opened = 0; 1042 error = 0; 1043 1044 if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) { 1045 1046 /* XXX the TTY layer should call "open()" first! */ 1047 1048 error = ucom_open(tp); 1049 if (error) { 1050 goto done; 1051 } 1052 opened = 1; 1053 } 1054 DPRINTF("sc = %p\n", sc); 1055 1056 /* Check requested parameters. */ 1057 if (t->c_ospeed < 0) { 1058 DPRINTF("negative ospeed\n"); 1059 error = EINVAL; 1060 goto done; 1061 } 1062 if (t->c_ispeed && (t->c_ispeed != t->c_ospeed)) { 1063 DPRINTF("mismatch ispeed and ospeed\n"); 1064 error = EINVAL; 1065 goto done; 1066 } 1067 t->c_ispeed = t->c_ospeed; 1068 1069 if (sc->sc_callback->ucom_pre_param) { 1070 /* Let the lower layer verify the parameters */ 1071 error = (sc->sc_callback->ucom_pre_param) (sc, t); 1072 if (error) { 1073 DPRINTF("callback error = %d\n", error); 1074 goto done; 1075 } 1076 } 1077 1078 /* Disable transfers */ 1079 sc->sc_flag &= ~UCOM_FLAG_GP_DATA; 1080 1081 /* Queue baud rate programming command first */ 1082 ucom_queue_command(sc, ucom_cfg_param, t, 1083 &sc->sc_param_task[0].hdr, 1084 &sc->sc_param_task[1].hdr); 1085 1086 /* Queue transfer enable command last */ 1087 ucom_queue_command(sc, ucom_cfg_start_transfers, NULL, 1088 &sc->sc_start_task[0].hdr, 1089 &sc->sc_start_task[1].hdr); 1090 1091 if (t->c_cflag & CRTS_IFLOW) { 1092 sc->sc_flag |= UCOM_FLAG_RTS_IFLOW; 1093 } else if (sc->sc_flag & UCOM_FLAG_RTS_IFLOW) { 1094 sc->sc_flag &= ~UCOM_FLAG_RTS_IFLOW; 1095 ucom_modem(tp, SER_RTS, 0); 1096 } 1097 done: 1098 if (error) { 1099 if (opened) { 1100 ucom_close(tp); 1101 } 1102 } 1103 return (error); 1104 } 1105 1106 static void 1107 ucom_outwakeup(struct tty *tp) 1108 { 1109 struct ucom_softc *sc = tty_softc(tp); 1110 1111 mtx_assert(sc->sc_mtx, MA_OWNED); 1112 1113 DPRINTF("sc = %p\n", sc); 1114 1115 if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) { 1116 /* The higher layer is not ready */ 1117 return; 1118 } 1119 ucom_start_transfers(sc); 1120 } 1121 1122 /*------------------------------------------------------------------------* 1123 * ucom_get_data 1124 * 1125 * Return values: 1126 * 0: No data is available. 1127 * Else: Data is available. 1128 *------------------------------------------------------------------------*/ 1129 uint8_t 1130 ucom_get_data(struct ucom_softc *sc, struct usb_page_cache *pc, 1131 uint32_t offset, uint32_t len, uint32_t *actlen) 1132 { 1133 struct usb_page_search res; 1134 struct tty *tp = sc->sc_tty; 1135 uint32_t cnt; 1136 uint32_t offset_orig; 1137 1138 mtx_assert(sc->sc_mtx, MA_OWNED); 1139 1140 if (sc->sc_flag & UCOM_FLAG_CONSOLE) { 1141 unsigned int temp; 1142 1143 /* get total TX length */ 1144 1145 temp = ucom_cons_tx_high - ucom_cons_tx_low; 1146 temp %= UCOM_CONS_BUFSIZE; 1147 1148 /* limit TX length */ 1149 1150 if (temp > (UCOM_CONS_BUFSIZE - ucom_cons_tx_low)) 1151 temp = (UCOM_CONS_BUFSIZE - ucom_cons_tx_low); 1152 1153 if (temp > len) 1154 temp = len; 1155 1156 /* copy in data */ 1157 1158 usbd_copy_in(pc, offset, ucom_cons_tx_buf + ucom_cons_tx_low, temp); 1159 1160 /* update counters */ 1161 1162 ucom_cons_tx_low += temp; 1163 ucom_cons_tx_low %= UCOM_CONS_BUFSIZE; 1164 1165 /* store actual length */ 1166 1167 *actlen = temp; 1168 1169 return (temp ? 1 : 0); 1170 } 1171 1172 if (tty_gone(tp) || 1173 !(sc->sc_flag & UCOM_FLAG_GP_DATA)) { 1174 actlen[0] = 0; 1175 return (0); /* multiport device polling */ 1176 } 1177 offset_orig = offset; 1178 1179 while (len != 0) { 1180 1181 usbd_get_page(pc, offset, &res); 1182 1183 if (res.length > len) { 1184 res.length = len; 1185 } 1186 /* copy data directly into USB buffer */ 1187 cnt = ttydisc_getc(tp, res.buffer, res.length); 1188 1189 offset += cnt; 1190 len -= cnt; 1191 1192 if (cnt < res.length) { 1193 /* end of buffer */ 1194 break; 1195 } 1196 } 1197 1198 actlen[0] = offset - offset_orig; 1199 1200 DPRINTF("cnt=%d\n", actlen[0]); 1201 1202 if (actlen[0] == 0) { 1203 return (0); 1204 } 1205 return (1); 1206 } 1207 1208 void 1209 ucom_put_data(struct ucom_softc *sc, struct usb_page_cache *pc, 1210 uint32_t offset, uint32_t len) 1211 { 1212 struct usb_page_search res; 1213 struct tty *tp = sc->sc_tty; 1214 char *buf; 1215 uint32_t cnt; 1216 1217 mtx_assert(sc->sc_mtx, MA_OWNED); 1218 1219 if (sc->sc_flag & UCOM_FLAG_CONSOLE) { 1220 unsigned int temp; 1221 1222 /* get maximum RX length */ 1223 1224 temp = (UCOM_CONS_BUFSIZE - 1) - ucom_cons_rx_high + ucom_cons_rx_low; 1225 temp %= UCOM_CONS_BUFSIZE; 1226 1227 /* limit RX length */ 1228 1229 if (temp > (UCOM_CONS_BUFSIZE - ucom_cons_rx_high)) 1230 temp = (UCOM_CONS_BUFSIZE - ucom_cons_rx_high); 1231 1232 if (temp > len) 1233 temp = len; 1234 1235 /* copy out data */ 1236 1237 usbd_copy_out(pc, offset, ucom_cons_rx_buf + ucom_cons_rx_high, temp); 1238 1239 /* update counters */ 1240 1241 ucom_cons_rx_high += temp; 1242 ucom_cons_rx_high %= UCOM_CONS_BUFSIZE; 1243 1244 return; 1245 } 1246 1247 if (tty_gone(tp)) 1248 return; /* multiport device polling */ 1249 1250 if (len == 0) 1251 return; /* no data */ 1252 1253 /* set a flag to prevent recursation ? */ 1254 1255 while (len > 0) { 1256 1257 usbd_get_page(pc, offset, &res); 1258 1259 if (res.length > len) { 1260 res.length = len; 1261 } 1262 len -= res.length; 1263 offset += res.length; 1264 1265 /* pass characters to tty layer */ 1266 1267 buf = res.buffer; 1268 cnt = res.length; 1269 1270 /* first check if we can pass the buffer directly */ 1271 1272 if (ttydisc_can_bypass(tp)) { 1273 if (ttydisc_rint_bypass(tp, buf, cnt) != cnt) { 1274 DPRINTF("tp=%p, data lost\n", tp); 1275 } 1276 continue; 1277 } 1278 /* need to loop */ 1279 1280 for (cnt = 0; cnt != res.length; cnt++) { 1281 if (ttydisc_rint(tp, buf[cnt], 0) == -1) { 1282 /* XXX what should we do? */ 1283 1284 DPRINTF("tp=%p, lost %d " 1285 "chars\n", tp, res.length - cnt); 1286 break; 1287 } 1288 } 1289 } 1290 ttydisc_rint_done(tp); 1291 } 1292 1293 static void 1294 ucom_free(void *xsc) 1295 { 1296 struct ucom_softc *sc = xsc; 1297 1298 mtx_lock(sc->sc_mtx); 1299 sc->sc_ttyfreed = 1; 1300 cv_signal(&sc->sc_cv); 1301 mtx_unlock(sc->sc_mtx); 1302 } 1303 1304 static cn_probe_t ucom_cnprobe; 1305 static cn_init_t ucom_cninit; 1306 static cn_term_t ucom_cnterm; 1307 static cn_getc_t ucom_cngetc; 1308 static cn_putc_t ucom_cnputc; 1309 1310 CONSOLE_DRIVER(ucom); 1311 1312 static void 1313 ucom_cnprobe(struct consdev *cp) 1314 { 1315 if (ucom_cons_unit != -1) 1316 cp->cn_pri = CN_NORMAL; 1317 else 1318 cp->cn_pri = CN_DEAD; 1319 1320 strlcpy(cp->cn_name, "ucom", sizeof(cp->cn_name)); 1321 } 1322 1323 static void 1324 ucom_cninit(struct consdev *cp) 1325 { 1326 } 1327 1328 static void 1329 ucom_cnterm(struct consdev *cp) 1330 { 1331 } 1332 1333 static int 1334 ucom_cngetc(struct consdev *cd) 1335 { 1336 struct ucom_softc *sc = ucom_cons_softc; 1337 int c; 1338 1339 if (sc == NULL) 1340 return (-1); 1341 1342 mtx_lock(sc->sc_mtx); 1343 1344 if (ucom_cons_rx_low != ucom_cons_rx_high) { 1345 c = ucom_cons_rx_buf[ucom_cons_rx_low]; 1346 ucom_cons_rx_low ++; 1347 ucom_cons_rx_low %= UCOM_CONS_BUFSIZE; 1348 } else { 1349 c = -1; 1350 } 1351 1352 /* start USB transfers */ 1353 ucom_outwakeup(sc->sc_tty); 1354 1355 mtx_unlock(sc->sc_mtx); 1356 1357 /* poll if necessary */ 1358 if (kdb_active && sc->sc_callback->ucom_poll) 1359 (sc->sc_callback->ucom_poll) (sc); 1360 1361 return (c); 1362 } 1363 1364 static void 1365 ucom_cnputc(struct consdev *cd, int c) 1366 { 1367 struct ucom_softc *sc = ucom_cons_softc; 1368 unsigned int temp; 1369 1370 if (sc == NULL) 1371 return; 1372 1373 repeat: 1374 1375 mtx_lock(sc->sc_mtx); 1376 1377 /* compute maximum TX length */ 1378 1379 temp = (UCOM_CONS_BUFSIZE - 1) - ucom_cons_tx_high + ucom_cons_tx_low; 1380 temp %= UCOM_CONS_BUFSIZE; 1381 1382 if (temp) { 1383 ucom_cons_tx_buf[ucom_cons_tx_high] = c; 1384 ucom_cons_tx_high ++; 1385 ucom_cons_tx_high %= UCOM_CONS_BUFSIZE; 1386 } 1387 1388 /* start USB transfers */ 1389 ucom_outwakeup(sc->sc_tty); 1390 1391 mtx_unlock(sc->sc_mtx); 1392 1393 /* poll if necessary */ 1394 if (kdb_active && sc->sc_callback->ucom_poll) { 1395 (sc->sc_callback->ucom_poll) (sc); 1396 /* simple flow control */ 1397 if (temp == 0) 1398 goto repeat; 1399 } 1400 } 1401 1402 #if defined(GDB) 1403 1404 #include <gdb/gdb.h> 1405 1406 static gdb_probe_f ucom_gdbprobe; 1407 static gdb_init_f ucom_gdbinit; 1408 static gdb_term_f ucom_gdbterm; 1409 static gdb_getc_f ucom_gdbgetc; 1410 static gdb_putc_f ucom_gdbputc; 1411 1412 GDB_DBGPORT(sio, ucom_gdbprobe, ucom_gdbinit, ucom_gdbterm, ucom_gdbgetc, ucom_gdbputc); 1413 1414 static int 1415 ucom_gdbprobe(void) 1416 { 1417 return ((ucom_cons_softc != NULL) ? 0 : -1); 1418 } 1419 1420 static void 1421 ucom_gdbinit(void) 1422 { 1423 } 1424 1425 static void 1426 ucom_gdbterm(void) 1427 { 1428 } 1429 1430 static void 1431 ucom_gdbputc(int c) 1432 { 1433 ucom_cnputc(NULL, c); 1434 } 1435 1436 static int 1437 ucom_gdbgetc(void) 1438 { 1439 return (ucom_cngetc(NULL)); 1440 } 1441 1442 #endif 1443