usb_serial.c revision 194677
1121934Sharti/* $NetBSD: ucom.c,v 1.40 2001/11/13 06:24:54 lukem Exp $ */ 2121934Sharti 3121934Sharti/*- 4121934Sharti * Copyright (c) 2001-2003, 2005, 2008 5121934Sharti * Shunsuke Akiyama <akiyama@jp.FreeBSD.org>. 6131826Sharti * All rights reserved. 7131826Sharti * 8121934Sharti * Redistribution and use in source and binary forms, with or without 9121934Sharti * modification, are permitted provided that the following conditions 10121934Sharti * are met: 11121934Sharti * 1. Redistributions of source code must retain the above copyright 12121934Sharti * notice, this list of conditions and the following disclaimer. 13121934Sharti * 2. Redistributions in binary form must reproduce the above copyright 14121934Sharti * notice, this list of conditions and the following disclaimer in the 15121934Sharti * documentation and/or other materials provided with the distribution. 16121934Sharti * 17121934Sharti * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18121934Sharti * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19121934Sharti * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20121934Sharti * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21121934Sharti * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22121934Sharti * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23121934Sharti * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24121934Sharti * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25121934Sharti * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26121934Sharti * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27121934Sharti * SUCH DAMAGE. 28121934Sharti */ 29131826Sharti 30121934Sharti#include <sys/cdefs.h> 31121934Sharti__FBSDID("$FreeBSD: head/sys/dev/usb/serial/usb_serial.c 194677 2009-06-23 02:19:59Z thompsa $"); 32121934Sharti 33121934Sharti/*- 34121934Sharti * Copyright (c) 1998, 2000 The NetBSD Foundation, Inc. 35121934Sharti * All rights reserved. 36121934Sharti * 37121934Sharti * This code is derived from software contributed to The NetBSD Foundation 38121934Sharti * by Lennart Augustsson (lennart@augustsson.net) at 39121934Sharti * Carlstedt Research & Technology. 40121934Sharti * 41121934Sharti * Redistribution and use in source and binary forms, with or without 42121934Sharti * modification, are permitted provided that the following conditions 43121934Sharti * are met: 44121934Sharti * 1. Redistributions of source code must retain the above copyright 45121934Sharti * notice, this list of conditions and the following disclaimer. 46121934Sharti * 2. Redistributions in binary form must reproduce the above copyright 47121934Sharti * notice, this list of conditions and the following disclaimer in the 48121934Sharti * documentation and/or other materials provided with the distribution. 49121934Sharti * 3. All advertising materials mentioning features or use of this software 50121934Sharti * must display the following acknowledgement: 51121934Sharti * This product includes software developed by the NetBSD 52121934Sharti * Foundation, Inc. and its contributors. 53121934Sharti * 4. Neither the name of The NetBSD Foundation nor the names of its 54121934Sharti * contributors may be used to endorse or promote products derived 55121934Sharti * from this software without specific prior written permission. 56121934Sharti * 57121934Sharti * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 58121934Sharti * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 59121934Sharti * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 60121934Sharti * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 61121934Sharti * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 62121934Sharti * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 63121934Sharti * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 64121934Sharti * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 65121934Sharti * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 66121934Sharti * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 67121934Sharti * POSSIBILITY OF SUCH DAMAGE. 68121934Sharti */ 69121934Sharti 70121934Sharti#include <sys/stdint.h> 71121934Sharti#include <sys/stddef.h> 72121934Sharti#include <sys/param.h> 73121934Sharti#include <sys/queue.h> 74121934Sharti#include <sys/types.h> 75121934Sharti#include <sys/systm.h> 76121934Sharti#include <sys/kernel.h> 77121934Sharti#include <sys/bus.h> 78121934Sharti#include <sys/linker_set.h> 79121934Sharti#include <sys/module.h> 80121934Sharti#include <sys/lock.h> 81121934Sharti#include <sys/mutex.h> 82121934Sharti#include <sys/condvar.h> 83121934Sharti#include <sys/sysctl.h> 84121934Sharti#include <sys/sx.h> 85121934Sharti#include <sys/unistd.h> 86121934Sharti#include <sys/callout.h> 87121934Sharti#include <sys/malloc.h> 88121934Sharti#include <sys/priv.h> 89121934Sharti 90121934Sharti#include <dev/usb/usb.h> 91121934Sharti#include <dev/usb/usbdi.h> 92121934Sharti#include <dev/usb/usbdi_util.h> 93121934Sharti 94121934Sharti#define USB_DEBUG_VAR ucom_debug 95121934Sharti#include <dev/usb/usb_debug.h> 96121934Sharti#include <dev/usb/usb_busdma.h> 97121934Sharti#include <dev/usb/usb_process.h> 98121934Sharti 99121934Sharti#include <dev/usb/serial/usb_serial.h> 100121934Sharti 101121934Sharti#if USB_DEBUG 102121934Shartistatic int ucom_debug = 0; 103121934Sharti 104121934ShartiSYSCTL_NODE(_hw_usb, OID_AUTO, ucom, CTLFLAG_RW, 0, "USB ucom"); 105121934ShartiSYSCTL_INT(_hw_usb_ucom, OID_AUTO, debug, CTLFLAG_RW, 106121934Sharti &ucom_debug, 0, "ucom debug level"); 107121934Sharti#endif 108121934Sharti 109121934Shartistatic usb_proc_callback_t ucom_cfg_start_transfers; 110121934Shartistatic usb_proc_callback_t ucom_cfg_open; 111121934Shartistatic usb_proc_callback_t ucom_cfg_close; 112121934Shartistatic usb_proc_callback_t ucom_cfg_line_state; 113121934Shartistatic usb_proc_callback_t ucom_cfg_status_change; 114121934Shartistatic usb_proc_callback_t ucom_cfg_param; 115121934Sharti 116121934Shartistatic uint8_t ucom_units_alloc(uint32_t, uint32_t *); 117121934Shartistatic void ucom_units_free(uint32_t, uint32_t); 118121934Shartistatic int ucom_attach_tty(struct ucom_softc *, uint32_t); 119121934Shartistatic void ucom_detach_tty(struct ucom_softc *); 120121934Shartistatic void ucom_queue_command(struct ucom_softc *, 121121934Sharti usb_proc_callback_t *, struct termios *pt, 122121934Sharti struct usb_proc_msg *t0, struct usb_proc_msg *t1); 123121934Shartistatic void ucom_shutdown(struct ucom_softc *); 124121934Shartistatic void ucom_break(struct ucom_softc *, uint8_t); 125121934Shartistatic void ucom_dtr(struct ucom_softc *, uint8_t); 126121934Shartistatic void ucom_rts(struct ucom_softc *, uint8_t); 127121934Sharti 128121934Shartistatic tsw_open_t ucom_open; 129121934Shartistatic tsw_close_t ucom_close; 130121934Shartistatic tsw_ioctl_t ucom_ioctl; 131121934Shartistatic tsw_modem_t ucom_modem; 132121934Shartistatic tsw_param_t ucom_param; 133121934Shartistatic tsw_outwakeup_t ucom_outwakeup; 134121934Shartistatic tsw_free_t ucom_free; 135121934Sharti 136121934Shartistatic struct ttydevsw ucom_class = { 137121934Sharti .tsw_flags = TF_INITLOCK | TF_CALLOUT, 138121934Sharti .tsw_open = ucom_open, 139121934Sharti .tsw_close = ucom_close, 140121934Sharti .tsw_outwakeup = ucom_outwakeup, 141121934Sharti .tsw_ioctl = ucom_ioctl, 142121934Sharti .tsw_param = ucom_param, 143121934Sharti .tsw_modem = ucom_modem, 144121934Sharti .tsw_free = ucom_free, 145121934Sharti}; 146121934Sharti 147121934ShartiMODULE_DEPEND(ucom, usb, 1, 1, 1); 148121934ShartiMODULE_VERSION(ucom, 1); 149121934Sharti 150121934Sharti#define UCOM_UNIT_MAX 0x1000 /* exclusive */ 151121934Sharti#define UCOM_SUB_UNIT_MAX 0x100 /* exclusive */ 152121934Sharti 153121934Shartistatic uint8_t ucom_bitmap[(UCOM_UNIT_MAX + 7) / 8]; 154121934Sharti 155121934Shartistatic uint8_t 156121934Shartiucom_units_alloc(uint32_t sub_units, uint32_t *p_root_unit) 157121934Sharti{ 158121934Sharti uint32_t n; 159121934Sharti uint32_t o; 160121934Sharti uint32_t x; 161121934Sharti uint32_t max = UCOM_UNIT_MAX - (UCOM_UNIT_MAX % sub_units); 162121934Sharti uint8_t error = 1; 163121934Sharti 164121934Sharti mtx_lock(&Giant); 165121934Sharti 166121934Sharti for (n = 0; n < max; n += sub_units) { 167121934Sharti 168121934Sharti /* check for free consecutive bits */ 169121934Sharti 170121934Sharti for (o = 0; o < sub_units; o++) { 171121934Sharti 172121934Sharti x = n + o; 173121934Sharti 174121934Sharti if (ucom_bitmap[x / 8] & (1 << (x % 8))) { 175121934Sharti goto skip; 176121934Sharti } 177121934Sharti } 178121934Sharti 179121934Sharti /* allocate */ 180121934Sharti 181121934Sharti for (o = 0; o < sub_units; o++) { 182121934Sharti 183121934Sharti x = n + o; 184121934Sharti 185121934Sharti ucom_bitmap[x / 8] |= (1 << (x % 8)); 186121934Sharti } 187121934Sharti 188121934Sharti error = 0; 189121934Sharti 190121934Sharti break; 191121934Sharti 192121934Shartiskip: ; 193121934Sharti } 194121934Sharti 195121934Sharti mtx_unlock(&Giant); 196121934Sharti 197121934Sharti /* 198121934Sharti * Always set the variable pointed to by "p_root_unit" so that 199121934Sharti * the compiler does not think that it is used uninitialised: 200121934Sharti */ 201121934Sharti *p_root_unit = n; 202121934Sharti 203121934Sharti return (error); 204121934Sharti} 205121934Sharti 206121934Shartistatic void 207121934Shartiucom_units_free(uint32_t root_unit, uint32_t sub_units) 208121934Sharti{ 209121934Sharti uint32_t x; 210121934Sharti 211121934Sharti mtx_lock(&Giant); 212121934Sharti 213121934Sharti while (sub_units--) { 214121934Sharti x = root_unit + sub_units; 215121934Sharti ucom_bitmap[x / 8] &= ~(1 << (x % 8)); 216121934Sharti } 217121934Sharti 218121934Sharti mtx_unlock(&Giant); 219121934Sharti} 220121934Sharti 221121934Sharti/* 222121934Sharti * "N" sub_units are setup at a time. All sub-units will 223121934Sharti * be given sequential unit numbers. The number of 224121934Sharti * sub-units can be used to differentiate among 225121934Sharti * different types of devices. 226121934Sharti * 227121934Sharti * The mutex pointed to by "mtx" is applied before all 228121934Sharti * callbacks are called back. Also "mtx" must be applied 229121934Sharti * before calling into the ucom-layer! 230121934Sharti */ 231121934Shartiint 232121934Shartiucom_attach(struct ucom_super_softc *ssc, struct ucom_softc *sc, 233121934Sharti uint32_t sub_units, void *parent, 234121934Sharti const struct ucom_callback *callback, struct mtx *mtx) 235121934Sharti{ 236 uint32_t n; 237 uint32_t root_unit; 238 int error = 0; 239 240 if ((sc == NULL) || 241 (sub_units == 0) || 242 (sub_units > UCOM_SUB_UNIT_MAX) || 243 (callback == NULL)) { 244 return (EINVAL); 245 } 246 247 /* XXX unit management does not really belong here */ 248 if (ucom_units_alloc(sub_units, &root_unit)) { 249 return (ENOMEM); 250 } 251 252 error = usb_proc_create(&ssc->sc_tq, mtx, "ucom", USB_PRI_MED); 253 if (error) { 254 ucom_units_free(root_unit, sub_units); 255 return (error); 256 } 257 258 for (n = 0; n != sub_units; n++, sc++) { 259 sc->sc_unit = root_unit + n; 260 sc->sc_local_unit = n; 261 sc->sc_super = ssc; 262 sc->sc_mtx = mtx; 263 sc->sc_parent = parent; 264 sc->sc_callback = callback; 265 266 error = ucom_attach_tty(sc, sub_units); 267 if (error) { 268 ucom_detach(ssc, sc - n, n); 269 ucom_units_free(root_unit + n, sub_units - n); 270 return (error); 271 } 272 sc->sc_flag |= UCOM_FLAG_ATTACHED; 273 } 274 return (0); 275} 276 277/* 278 * NOTE: the following function will do nothing if 279 * the structure pointed to by "ssc" and "sc" is zero. 280 */ 281void 282ucom_detach(struct ucom_super_softc *ssc, struct ucom_softc *sc, 283 uint32_t sub_units) 284{ 285 uint32_t n; 286 287 usb_proc_drain(&ssc->sc_tq); 288 289 for (n = 0; n != sub_units; n++, sc++) { 290 if (sc->sc_flag & UCOM_FLAG_ATTACHED) { 291 292 ucom_detach_tty(sc); 293 294 ucom_units_free(sc->sc_unit, 1); 295 296 /* avoid duplicate detach: */ 297 sc->sc_flag &= ~UCOM_FLAG_ATTACHED; 298 } 299 } 300 usb_proc_free(&ssc->sc_tq); 301} 302 303static int 304ucom_attach_tty(struct ucom_softc *sc, uint32_t sub_units) 305{ 306 struct tty *tp; 307 int error = 0; 308 char buf[32]; /* temporary TTY device name buffer */ 309 310 tp = tty_alloc_mutex(&ucom_class, sc, sc->sc_mtx); 311 if (tp == NULL) { 312 error = ENOMEM; 313 goto done; 314 } 315 DPRINTF("tp = %p, unit = %d\n", tp, sc->sc_unit); 316 317 buf[0] = 0; /* set some default value */ 318 319 /* Check if the client has a custom TTY name */ 320 if (sc->sc_callback->ucom_tty_name) { 321 sc->sc_callback->ucom_tty_name(sc, buf, 322 sizeof(buf), sc->sc_local_unit); 323 } 324 if (buf[0] == 0) { 325 /* Use default TTY name */ 326 if (sub_units > 1) { 327 /* multiple modems in one */ 328 if (snprintf(buf, sizeof(buf), "U%u.%u", 329 sc->sc_unit - sc->sc_local_unit, 330 sc->sc_local_unit)) { 331 /* ignore */ 332 } 333 } else { 334 /* single modem */ 335 if (snprintf(buf, sizeof(buf), "U%u", sc->sc_unit)) { 336 /* ignore */ 337 } 338 } 339 } 340 tty_makedev(tp, NULL, "%s", buf); 341 342 sc->sc_tty = tp; 343 344 DPRINTF("ttycreate: %s\n", buf); 345 cv_init(&sc->sc_cv, "ucom"); 346 347done: 348 return (error); 349} 350 351static void 352ucom_detach_tty(struct ucom_softc *sc) 353{ 354 struct tty *tp = sc->sc_tty; 355 356 DPRINTF("sc = %p, tp = %p\n", sc, sc->sc_tty); 357 358 /* the config thread has been stopped when we get here */ 359 360 mtx_lock(sc->sc_mtx); 361 sc->sc_flag |= UCOM_FLAG_GONE; 362 sc->sc_flag &= ~(UCOM_FLAG_HL_READY | 363 UCOM_FLAG_LL_READY); 364 mtx_unlock(sc->sc_mtx); 365 if (tp) { 366 tty_lock(tp); 367 368 ucom_close(tp); /* close, if any */ 369 370 tty_rel_gone(tp); 371 372 mtx_lock(sc->sc_mtx); 373 /* Wait for the callback after the TTY is torn down */ 374 while (sc->sc_ttyfreed == 0) 375 cv_wait(&sc->sc_cv, sc->sc_mtx); 376 /* 377 * make sure that read and write transfers are stopped 378 */ 379 if (sc->sc_callback->ucom_stop_read) { 380 (sc->sc_callback->ucom_stop_read) (sc); 381 } 382 if (sc->sc_callback->ucom_stop_write) { 383 (sc->sc_callback->ucom_stop_write) (sc); 384 } 385 mtx_unlock(sc->sc_mtx); 386 } 387 cv_destroy(&sc->sc_cv); 388} 389 390static void 391ucom_queue_command(struct ucom_softc *sc, 392 usb_proc_callback_t *fn, struct termios *pt, 393 struct usb_proc_msg *t0, struct usb_proc_msg *t1) 394{ 395 struct ucom_super_softc *ssc = sc->sc_super; 396 struct ucom_param_task *task; 397 398 mtx_assert(sc->sc_mtx, MA_OWNED); 399 400 if (usb_proc_is_gone(&ssc->sc_tq)) { 401 DPRINTF("proc is gone\n"); 402 return; /* nothing to do */ 403 } 404 /* 405 * NOTE: The task cannot get executed before we drop the 406 * "sc_mtx" mutex. It is safe to update fields in the message 407 * structure after that the message got queued. 408 */ 409 task = (struct ucom_param_task *) 410 usb_proc_msignal(&ssc->sc_tq, t0, t1); 411 412 /* Setup callback and softc pointers */ 413 task->hdr.pm_callback = fn; 414 task->sc = sc; 415 416 /* 417 * Make a copy of the termios. This field is only present if 418 * the "pt" field is not NULL. 419 */ 420 if (pt != NULL) 421 task->termios_copy = *pt; 422 423 /* 424 * Closing the device should be synchronous. 425 */ 426 if (fn == ucom_cfg_close) 427 usb_proc_mwait(&ssc->sc_tq, t0, t1); 428 429 /* 430 * In case of multiple configure requests, 431 * keep track of the last one! 432 */ 433 if (fn == ucom_cfg_start_transfers) 434 sc->sc_last_start_xfer = &task->hdr; 435} 436 437static void 438ucom_shutdown(struct ucom_softc *sc) 439{ 440 struct tty *tp = sc->sc_tty; 441 442 mtx_assert(sc->sc_mtx, MA_OWNED); 443 444 DPRINTF("\n"); 445 446 /* 447 * Hang up if necessary: 448 */ 449 if (tp->t_termios.c_cflag & HUPCL) { 450 ucom_modem(tp, 0, SER_DTR); 451 } 452} 453 454/* 455 * Return values: 456 * 0: normal 457 * else: taskqueue is draining or gone 458 */ 459uint8_t 460ucom_cfg_is_gone(struct ucom_softc *sc) 461{ 462 struct ucom_super_softc *ssc = sc->sc_super; 463 464 return (usb_proc_is_gone(&ssc->sc_tq)); 465} 466 467static void 468ucom_cfg_start_transfers(struct usb_proc_msg *_task) 469{ 470 struct ucom_cfg_task *task = 471 (struct ucom_cfg_task *)_task; 472 struct ucom_softc *sc = task->sc; 473 474 if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) { 475 return; 476 } 477 if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) { 478 /* TTY device closed */ 479 return; 480 } 481 482 if (_task == sc->sc_last_start_xfer) 483 sc->sc_flag |= UCOM_FLAG_GP_DATA; 484 485 if (sc->sc_callback->ucom_start_read) { 486 (sc->sc_callback->ucom_start_read) (sc); 487 } 488 if (sc->sc_callback->ucom_start_write) { 489 (sc->sc_callback->ucom_start_write) (sc); 490 } 491} 492 493static void 494ucom_start_transfers(struct ucom_softc *sc) 495{ 496 if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) { 497 return; 498 } 499 /* 500 * Make sure that data transfers are started in both 501 * directions: 502 */ 503 if (sc->sc_callback->ucom_start_read) { 504 (sc->sc_callback->ucom_start_read) (sc); 505 } 506 if (sc->sc_callback->ucom_start_write) { 507 (sc->sc_callback->ucom_start_write) (sc); 508 } 509} 510 511static void 512ucom_cfg_open(struct usb_proc_msg *_task) 513{ 514 struct ucom_cfg_task *task = 515 (struct ucom_cfg_task *)_task; 516 struct ucom_softc *sc = task->sc; 517 518 DPRINTF("\n"); 519 520 if (sc->sc_flag & UCOM_FLAG_LL_READY) { 521 522 /* already opened */ 523 524 } else { 525 526 sc->sc_flag |= UCOM_FLAG_LL_READY; 527 528 if (sc->sc_callback->ucom_cfg_open) { 529 (sc->sc_callback->ucom_cfg_open) (sc); 530 531 /* wait a little */ 532 usb_pause_mtx(sc->sc_mtx, hz / 10); 533 } 534 } 535} 536 537static int 538ucom_open(struct tty *tp) 539{ 540 struct ucom_softc *sc = tty_softc(tp); 541 int error; 542 543 mtx_assert(sc->sc_mtx, MA_OWNED); 544 545 if (sc->sc_flag & UCOM_FLAG_GONE) { 546 return (ENXIO); 547 } 548 if (sc->sc_flag & UCOM_FLAG_HL_READY) { 549 /* already opened */ 550 return (0); 551 } 552 DPRINTF("tp = %p\n", tp); 553 554 if (sc->sc_callback->ucom_pre_open) { 555 /* 556 * give the lower layer a chance to disallow TTY open, for 557 * example if the device is not present: 558 */ 559 error = (sc->sc_callback->ucom_pre_open) (sc); 560 if (error) { 561 return (error); 562 } 563 } 564 sc->sc_flag |= UCOM_FLAG_HL_READY; 565 566 /* Disable transfers */ 567 sc->sc_flag &= ~UCOM_FLAG_GP_DATA; 568 569 sc->sc_lsr = 0; 570 sc->sc_msr = 0; 571 sc->sc_mcr = 0; 572 573 /* reset programmed line state */ 574 sc->sc_pls_curr = 0; 575 sc->sc_pls_set = 0; 576 sc->sc_pls_clr = 0; 577 578 ucom_queue_command(sc, ucom_cfg_open, NULL, 579 &sc->sc_open_task[0].hdr, 580 &sc->sc_open_task[1].hdr); 581 582 /* Queue transfer enable command last */ 583 ucom_queue_command(sc, ucom_cfg_start_transfers, NULL, 584 &sc->sc_start_task[0].hdr, 585 &sc->sc_start_task[1].hdr); 586 587 ucom_modem(tp, SER_DTR | SER_RTS, 0); 588 589 ucom_break(sc, 0); 590 591 ucom_status_change(sc); 592 593 return (0); 594} 595 596static void 597ucom_cfg_close(struct usb_proc_msg *_task) 598{ 599 struct ucom_cfg_task *task = 600 (struct ucom_cfg_task *)_task; 601 struct ucom_softc *sc = task->sc; 602 603 DPRINTF("\n"); 604 605 if (sc->sc_flag & UCOM_FLAG_LL_READY) { 606 sc->sc_flag &= ~UCOM_FLAG_LL_READY; 607 if (sc->sc_callback->ucom_cfg_close) 608 (sc->sc_callback->ucom_cfg_close) (sc); 609 } else { 610 /* already closed */ 611 } 612} 613 614static void 615ucom_close(struct tty *tp) 616{ 617 struct ucom_softc *sc = tty_softc(tp); 618 619 mtx_assert(sc->sc_mtx, MA_OWNED); 620 621 DPRINTF("tp=%p\n", tp); 622 623 if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) { 624 DPRINTF("tp=%p already closed\n", tp); 625 return; 626 } 627 ucom_shutdown(sc); 628 629 ucom_queue_command(sc, ucom_cfg_close, NULL, 630 &sc->sc_close_task[0].hdr, 631 &sc->sc_close_task[1].hdr); 632 633 sc->sc_flag &= ~(UCOM_FLAG_HL_READY | UCOM_FLAG_RTS_IFLOW); 634 635 if (sc->sc_callback->ucom_stop_read) { 636 (sc->sc_callback->ucom_stop_read) (sc); 637 } 638} 639 640static int 641ucom_ioctl(struct tty *tp, u_long cmd, caddr_t data, struct thread *td) 642{ 643 struct ucom_softc *sc = tty_softc(tp); 644 int error; 645 646 mtx_assert(sc->sc_mtx, MA_OWNED); 647 648 if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) { 649 return (EIO); 650 } 651 DPRINTF("cmd = 0x%08lx\n", cmd); 652 653 switch (cmd) { 654 case TIOCSBRK: 655 ucom_break(sc, 1); 656 error = 0; 657 break; 658 case TIOCCBRK: 659 ucom_break(sc, 0); 660 error = 0; 661 break; 662 default: 663 if (sc->sc_callback->ucom_ioctl) { 664 error = (sc->sc_callback->ucom_ioctl) 665 (sc, cmd, data, 0, td); 666 } else { 667 error = ENOIOCTL; 668 } 669 break; 670 } 671 return (error); 672} 673 674static int 675ucom_modem(struct tty *tp, int sigon, int sigoff) 676{ 677 struct ucom_softc *sc = tty_softc(tp); 678 uint8_t onoff; 679 680 mtx_assert(sc->sc_mtx, MA_OWNED); 681 682 if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) { 683 return (0); 684 } 685 if ((sigon == 0) && (sigoff == 0)) { 686 687 if (sc->sc_mcr & SER_DTR) { 688 sigon |= SER_DTR; 689 } 690 if (sc->sc_mcr & SER_RTS) { 691 sigon |= SER_RTS; 692 } 693 if (sc->sc_msr & SER_CTS) { 694 sigon |= SER_CTS; 695 } 696 if (sc->sc_msr & SER_DCD) { 697 sigon |= SER_DCD; 698 } 699 if (sc->sc_msr & SER_DSR) { 700 sigon |= SER_DSR; 701 } 702 if (sc->sc_msr & SER_RI) { 703 sigon |= SER_RI; 704 } 705 return (sigon); 706 } 707 if (sigon & SER_DTR) { 708 sc->sc_mcr |= SER_DTR; 709 } 710 if (sigoff & SER_DTR) { 711 sc->sc_mcr &= ~SER_DTR; 712 } 713 if (sigon & SER_RTS) { 714 sc->sc_mcr |= SER_RTS; 715 } 716 if (sigoff & SER_RTS) { 717 sc->sc_mcr &= ~SER_RTS; 718 } 719 onoff = (sc->sc_mcr & SER_DTR) ? 1 : 0; 720 ucom_dtr(sc, onoff); 721 722 onoff = (sc->sc_mcr & SER_RTS) ? 1 : 0; 723 ucom_rts(sc, onoff); 724 725 return (0); 726} 727 728static void 729ucom_cfg_line_state(struct usb_proc_msg *_task) 730{ 731 struct ucom_cfg_task *task = 732 (struct ucom_cfg_task *)_task; 733 struct ucom_softc *sc = task->sc; 734 uint8_t notch_bits; 735 uint8_t any_bits; 736 uint8_t prev_value; 737 uint8_t last_value; 738 uint8_t mask; 739 740 if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) { 741 return; 742 } 743 744 mask = 0; 745 /* compute callback mask */ 746 if (sc->sc_callback->ucom_cfg_set_dtr) 747 mask |= UCOM_LS_DTR; 748 if (sc->sc_callback->ucom_cfg_set_rts) 749 mask |= UCOM_LS_RTS; 750 if (sc->sc_callback->ucom_cfg_set_break) 751 mask |= UCOM_LS_BREAK; 752 753 /* compute the bits we are to program */ 754 notch_bits = (sc->sc_pls_set & sc->sc_pls_clr) & mask; 755 any_bits = (sc->sc_pls_set | sc->sc_pls_clr) & mask; 756 prev_value = sc->sc_pls_curr ^ notch_bits; 757 last_value = sc->sc_pls_curr; 758 759 /* reset programmed line state */ 760 sc->sc_pls_curr = 0; 761 sc->sc_pls_set = 0; 762 sc->sc_pls_clr = 0; 763 764 /* ensure that we don't loose any levels */ 765 if (notch_bits & UCOM_LS_DTR) 766 sc->sc_callback->ucom_cfg_set_dtr(sc, 767 (prev_value & UCOM_LS_DTR) ? 1 : 0); 768 if (notch_bits & UCOM_LS_RTS) 769 sc->sc_callback->ucom_cfg_set_rts(sc, 770 (prev_value & UCOM_LS_RTS) ? 1 : 0); 771 if (notch_bits & UCOM_LS_BREAK) 772 sc->sc_callback->ucom_cfg_set_break(sc, 773 (prev_value & UCOM_LS_BREAK) ? 1 : 0); 774 775 /* set last value */ 776 if (any_bits & UCOM_LS_DTR) 777 sc->sc_callback->ucom_cfg_set_dtr(sc, 778 (last_value & UCOM_LS_DTR) ? 1 : 0); 779 if (any_bits & UCOM_LS_RTS) 780 sc->sc_callback->ucom_cfg_set_rts(sc, 781 (last_value & UCOM_LS_RTS) ? 1 : 0); 782 if (any_bits & UCOM_LS_BREAK) 783 sc->sc_callback->ucom_cfg_set_break(sc, 784 (last_value & UCOM_LS_BREAK) ? 1 : 0); 785} 786 787static void 788ucom_line_state(struct ucom_softc *sc, 789 uint8_t set_bits, uint8_t clear_bits) 790{ 791 mtx_assert(sc->sc_mtx, MA_OWNED); 792 793 if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) { 794 return; 795 } 796 797 DPRINTF("on=0x%02x, off=0x%02x\n", set_bits, clear_bits); 798 799 /* update current programmed line state */ 800 sc->sc_pls_curr |= set_bits; 801 sc->sc_pls_curr &= ~clear_bits; 802 sc->sc_pls_set |= set_bits; 803 sc->sc_pls_clr |= clear_bits; 804 805 /* defer driver programming */ 806 ucom_queue_command(sc, ucom_cfg_line_state, NULL, 807 &sc->sc_line_state_task[0].hdr, 808 &sc->sc_line_state_task[1].hdr); 809} 810 811static void 812ucom_break(struct ucom_softc *sc, uint8_t onoff) 813{ 814 DPRINTF("onoff = %d\n", onoff); 815 816 if (onoff) 817 ucom_line_state(sc, UCOM_LS_BREAK, 0); 818 else 819 ucom_line_state(sc, 0, UCOM_LS_BREAK); 820} 821 822static void 823ucom_dtr(struct ucom_softc *sc, uint8_t onoff) 824{ 825 DPRINTF("onoff = %d\n", onoff); 826 827 if (onoff) 828 ucom_line_state(sc, UCOM_LS_DTR, 0); 829 else 830 ucom_line_state(sc, 0, UCOM_LS_DTR); 831} 832 833static void 834ucom_rts(struct ucom_softc *sc, uint8_t onoff) 835{ 836 DPRINTF("onoff = %d\n", onoff); 837 838 if (onoff) 839 ucom_line_state(sc, UCOM_LS_RTS, 0); 840 else 841 ucom_line_state(sc, 0, UCOM_LS_RTS); 842} 843 844static void 845ucom_cfg_status_change(struct usb_proc_msg *_task) 846{ 847 struct ucom_cfg_task *task = 848 (struct ucom_cfg_task *)_task; 849 struct ucom_softc *sc = task->sc; 850 struct tty *tp; 851 uint8_t new_msr; 852 uint8_t new_lsr; 853 uint8_t onoff; 854 855 tp = sc->sc_tty; 856 857 mtx_assert(sc->sc_mtx, MA_OWNED); 858 859 if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) { 860 return; 861 } 862 if (sc->sc_callback->ucom_cfg_get_status == NULL) { 863 return; 864 } 865 /* get status */ 866 867 new_msr = 0; 868 new_lsr = 0; 869 870 (sc->sc_callback->ucom_cfg_get_status) (sc, &new_lsr, &new_msr); 871 872 if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) { 873 /* TTY device closed */ 874 return; 875 } 876 onoff = ((sc->sc_msr ^ new_msr) & SER_DCD); 877 878 sc->sc_msr = new_msr; 879 sc->sc_lsr = new_lsr; 880 881 if (onoff) { 882 883 onoff = (sc->sc_msr & SER_DCD) ? 1 : 0; 884 885 DPRINTF("DCD changed to %d\n", onoff); 886 887 ttydisc_modem(tp, onoff); 888 } 889} 890 891void 892ucom_status_change(struct ucom_softc *sc) 893{ 894 mtx_assert(sc->sc_mtx, MA_OWNED); 895 896 if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) { 897 return; 898 } 899 DPRINTF("\n"); 900 901 ucom_queue_command(sc, ucom_cfg_status_change, NULL, 902 &sc->sc_status_task[0].hdr, 903 &sc->sc_status_task[1].hdr); 904} 905 906static void 907ucom_cfg_param(struct usb_proc_msg *_task) 908{ 909 struct ucom_param_task *task = 910 (struct ucom_param_task *)_task; 911 struct ucom_softc *sc = task->sc; 912 913 if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) { 914 return; 915 } 916 if (sc->sc_callback->ucom_cfg_param == NULL) { 917 return; 918 } 919 920 (sc->sc_callback->ucom_cfg_param) (sc, &task->termios_copy); 921 922 /* wait a little */ 923 usb_pause_mtx(sc->sc_mtx, hz / 10); 924} 925 926static int 927ucom_param(struct tty *tp, struct termios *t) 928{ 929 struct ucom_softc *sc = tty_softc(tp); 930 uint8_t opened; 931 int error; 932 933 mtx_assert(sc->sc_mtx, MA_OWNED); 934 935 opened = 0; 936 error = 0; 937 938 if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) { 939 940 /* XXX the TTY layer should call "open()" first! */ 941 942 error = ucom_open(tp); 943 if (error) { 944 goto done; 945 } 946 opened = 1; 947 } 948 DPRINTF("sc = %p\n", sc); 949 950 /* Check requested parameters. */ 951 if (t->c_ospeed < 0) { 952 DPRINTF("negative ospeed\n"); 953 error = EINVAL; 954 goto done; 955 } 956 if (t->c_ispeed && (t->c_ispeed != t->c_ospeed)) { 957 DPRINTF("mismatch ispeed and ospeed\n"); 958 error = EINVAL; 959 goto done; 960 } 961 t->c_ispeed = t->c_ospeed; 962 963 if (sc->sc_callback->ucom_pre_param) { 964 /* Let the lower layer verify the parameters */ 965 error = (sc->sc_callback->ucom_pre_param) (sc, t); 966 if (error) { 967 DPRINTF("callback error = %d\n", error); 968 goto done; 969 } 970 } 971 972 /* Disable transfers */ 973 sc->sc_flag &= ~UCOM_FLAG_GP_DATA; 974 975 /* Queue baud rate programming command first */ 976 ucom_queue_command(sc, ucom_cfg_param, t, 977 &sc->sc_param_task[0].hdr, 978 &sc->sc_param_task[1].hdr); 979 980 /* Queue transfer enable command last */ 981 ucom_queue_command(sc, ucom_cfg_start_transfers, NULL, 982 &sc->sc_start_task[0].hdr, 983 &sc->sc_start_task[1].hdr); 984 985 if (t->c_cflag & CRTS_IFLOW) { 986 sc->sc_flag |= UCOM_FLAG_RTS_IFLOW; 987 } else if (sc->sc_flag & UCOM_FLAG_RTS_IFLOW) { 988 sc->sc_flag &= ~UCOM_FLAG_RTS_IFLOW; 989 ucom_modem(tp, SER_RTS, 0); 990 } 991done: 992 if (error) { 993 if (opened) { 994 ucom_close(tp); 995 } 996 } 997 return (error); 998} 999 1000static void 1001ucom_outwakeup(struct tty *tp) 1002{ 1003 struct ucom_softc *sc = tty_softc(tp); 1004 1005 mtx_assert(sc->sc_mtx, MA_OWNED); 1006 1007 DPRINTF("sc = %p\n", sc); 1008 1009 if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) { 1010 /* The higher layer is not ready */ 1011 return; 1012 } 1013 ucom_start_transfers(sc); 1014} 1015 1016/*------------------------------------------------------------------------* 1017 * ucom_get_data 1018 * 1019 * Return values: 1020 * 0: No data is available. 1021 * Else: Data is available. 1022 *------------------------------------------------------------------------*/ 1023uint8_t 1024ucom_get_data(struct ucom_softc *sc, struct usb_page_cache *pc, 1025 uint32_t offset, uint32_t len, uint32_t *actlen) 1026{ 1027 struct usb_page_search res; 1028 struct tty *tp = sc->sc_tty; 1029 uint32_t cnt; 1030 uint32_t offset_orig; 1031 1032 mtx_assert(sc->sc_mtx, MA_OWNED); 1033 1034 if (tty_gone(tp) || 1035 !(sc->sc_flag & UCOM_FLAG_GP_DATA)) { 1036 actlen[0] = 0; 1037 return (0); /* multiport device polling */ 1038 } 1039 offset_orig = offset; 1040 1041 while (len != 0) { 1042 1043 usbd_get_page(pc, offset, &res); 1044 1045 if (res.length > len) { 1046 res.length = len; 1047 } 1048 /* copy data directly into USB buffer */ 1049 cnt = ttydisc_getc(tp, res.buffer, res.length); 1050 1051 offset += cnt; 1052 len -= cnt; 1053 1054 if (cnt < res.length) { 1055 /* end of buffer */ 1056 break; 1057 } 1058 } 1059 1060 actlen[0] = offset - offset_orig; 1061 1062 DPRINTF("cnt=%d\n", actlen[0]); 1063 1064 if (actlen[0] == 0) { 1065 return (0); 1066 } 1067 return (1); 1068} 1069 1070void 1071ucom_put_data(struct ucom_softc *sc, struct usb_page_cache *pc, 1072 uint32_t offset, uint32_t len) 1073{ 1074 struct usb_page_search res; 1075 struct tty *tp = sc->sc_tty; 1076 char *buf; 1077 uint32_t cnt; 1078 1079 mtx_assert(sc->sc_mtx, MA_OWNED); 1080 1081 if (tty_gone(tp)) 1082 return; /* multiport device polling */ 1083 1084 if (len == 0) 1085 return; /* no data */ 1086 1087 /* set a flag to prevent recursation ? */ 1088 1089 while (len > 0) { 1090 1091 usbd_get_page(pc, offset, &res); 1092 1093 if (res.length > len) { 1094 res.length = len; 1095 } 1096 len -= res.length; 1097 offset += res.length; 1098 1099 /* pass characters to tty layer */ 1100 1101 buf = res.buffer; 1102 cnt = res.length; 1103 1104 /* first check if we can pass the buffer directly */ 1105 1106 if (ttydisc_can_bypass(tp)) { 1107 if (ttydisc_rint_bypass(tp, buf, cnt) != cnt) { 1108 DPRINTF("tp=%p, data lost\n", tp); 1109 } 1110 continue; 1111 } 1112 /* need to loop */ 1113 1114 for (cnt = 0; cnt != res.length; cnt++) { 1115 if (ttydisc_rint(tp, buf[cnt], 0) == -1) { 1116 /* XXX what should we do? */ 1117 1118 DPRINTF("tp=%p, lost %d " 1119 "chars\n", tp, res.length - cnt); 1120 break; 1121 } 1122 } 1123 } 1124 ttydisc_rint_done(tp); 1125} 1126 1127static void 1128ucom_free(void *xsc) 1129{ 1130 struct ucom_softc *sc = xsc; 1131 1132 mtx_lock(sc->sc_mtx); 1133 sc->sc_ttyfreed = 1; 1134 cv_signal(&sc->sc_cv); 1135 mtx_unlock(sc->sc_mtx); 1136} 1137