usb_serial.c revision 193045
1184610Salfred/* $NetBSD: ucom.c,v 1.40 2001/11/13 06:24:54 lukem Exp $ */ 2184610Salfred 3184610Salfred/*- 4184610Salfred * Copyright (c) 2001-2003, 2005, 2008 5184610Salfred * Shunsuke Akiyama <akiyama@jp.FreeBSD.org>. 6184610Salfred * All rights reserved. 7184610Salfred * 8184610Salfred * Redistribution and use in source and binary forms, with or without 9184610Salfred * modification, are permitted provided that the following conditions 10184610Salfred * are met: 11184610Salfred * 1. Redistributions of source code must retain the above copyright 12184610Salfred * notice, this list of conditions and the following disclaimer. 13184610Salfred * 2. Redistributions in binary form must reproduce the above copyright 14184610Salfred * notice, this list of conditions and the following disclaimer in the 15184610Salfred * documentation and/or other materials provided with the distribution. 16184610Salfred * 17184610Salfred * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18184610Salfred * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19184610Salfred * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20184610Salfred * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21184610Salfred * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22184610Salfred * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23184610Salfred * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24184610Salfred * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25184610Salfred * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26184610Salfred * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27184610Salfred * SUCH DAMAGE. 28184610Salfred */ 29184610Salfred 30184610Salfred#include <sys/cdefs.h> 31184610Salfred__FBSDID("$FreeBSD: head/sys/dev/usb/serial/usb_serial.c 193045 2009-05-29 18:46:57Z thompsa $"); 32184610Salfred 33184610Salfred/*- 34184610Salfred * Copyright (c) 1998, 2000 The NetBSD Foundation, Inc. 35184610Salfred * All rights reserved. 36184610Salfred * 37184610Salfred * This code is derived from software contributed to The NetBSD Foundation 38184610Salfred * by Lennart Augustsson (lennart@augustsson.net) at 39184610Salfred * Carlstedt Research & Technology. 40184610Salfred * 41184610Salfred * Redistribution and use in source and binary forms, with or without 42184610Salfred * modification, are permitted provided that the following conditions 43184610Salfred * are met: 44184610Salfred * 1. Redistributions of source code must retain the above copyright 45184610Salfred * notice, this list of conditions and the following disclaimer. 46184610Salfred * 2. Redistributions in binary form must reproduce the above copyright 47184610Salfred * notice, this list of conditions and the following disclaimer in the 48184610Salfred * documentation and/or other materials provided with the distribution. 49184610Salfred * 3. All advertising materials mentioning features or use of this software 50184610Salfred * must display the following acknowledgement: 51184610Salfred * This product includes software developed by the NetBSD 52184610Salfred * Foundation, Inc. and its contributors. 53184610Salfred * 4. Neither the name of The NetBSD Foundation nor the names of its 54184610Salfred * contributors may be used to endorse or promote products derived 55184610Salfred * from this software without specific prior written permission. 56184610Salfred * 57184610Salfred * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 58184610Salfred * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 59184610Salfred * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 60184610Salfred * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 61184610Salfred * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 62184610Salfred * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 63184610Salfred * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 64184610Salfred * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 65184610Salfred * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 66184610Salfred * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 67184610Salfred * POSSIBILITY OF SUCH DAMAGE. 68184610Salfred */ 69184610Salfred 70188942Sthompsa#include <dev/usb/usb.h> 71188942Sthompsa#include <dev/usb/usb_mfunc.h> 72188942Sthompsa#include <dev/usb/usb_error.h> 73188942Sthompsa#include <dev/usb/usb_cdc.h> 74188942Sthompsa#include <dev/usb/usb_ioctl.h> 75184610Salfred 76184610Salfred#define USB_DEBUG_VAR usb2_com_debug 77184610Salfred 78188942Sthompsa#include <dev/usb/usb_core.h> 79188942Sthompsa#include <dev/usb/usb_debug.h> 80188942Sthompsa#include <dev/usb/usb_process.h> 81188942Sthompsa#include <dev/usb/usb_request.h> 82188942Sthompsa#include <dev/usb/usb_busdma.h> 83188942Sthompsa#include <dev/usb/usb_util.h> 84184610Salfred 85188942Sthompsa#include <dev/usb/serial/usb_serial.h> 86184610Salfred 87184610Salfred#if USB_DEBUG 88184610Salfredstatic int usb2_com_debug = 0; 89184610Salfred 90192502SthompsaSYSCTL_NODE(_hw_usb, OID_AUTO, ucom, CTLFLAG_RW, 0, "USB ucom"); 91192502SthompsaSYSCTL_INT(_hw_usb_ucom, OID_AUTO, debug, CTLFLAG_RW, 92184610Salfred &usb2_com_debug, 0, "ucom debug level"); 93184610Salfred#endif 94184610Salfred 95193045Sthompsastatic usb_proc_callback_t usb2_com_cfg_start_transfers; 96193045Sthompsastatic usb_proc_callback_t usb2_com_cfg_open; 97193045Sthompsastatic usb_proc_callback_t usb2_com_cfg_close; 98193045Sthompsastatic usb_proc_callback_t usb2_com_cfg_line_state; 99193045Sthompsastatic usb_proc_callback_t usb2_com_cfg_status_change; 100193045Sthompsastatic usb_proc_callback_t usb2_com_cfg_param; 101184610Salfred 102185948Sthompsastatic uint8_t usb2_com_units_alloc(uint32_t, uint32_t *); 103185948Sthompsastatic void usb2_com_units_free(uint32_t, uint32_t); 104192984Sthompsastatic int usb2_com_attach_tty(struct ucom_softc *, uint32_t); 105192984Sthompsastatic void usb2_com_detach_tty(struct ucom_softc *); 106192984Sthompsastatic void usb2_com_queue_command(struct ucom_softc *, 107193045Sthompsa usb_proc_callback_t *, struct termios *pt, 108192984Sthompsa struct usb_proc_msg *t0, struct usb_proc_msg *t1); 109192984Sthompsastatic void usb2_com_shutdown(struct ucom_softc *); 110192984Sthompsastatic void usb2_com_break(struct ucom_softc *, uint8_t); 111192984Sthompsastatic void usb2_com_dtr(struct ucom_softc *, uint8_t); 112192984Sthompsastatic void usb2_com_rts(struct ucom_softc *, uint8_t); 113184610Salfred 114184610Salfredstatic tsw_open_t usb2_com_open; 115184610Salfredstatic tsw_close_t usb2_com_close; 116184610Salfredstatic tsw_ioctl_t usb2_com_ioctl; 117184610Salfredstatic tsw_modem_t usb2_com_modem; 118184610Salfredstatic tsw_param_t usb2_com_param; 119188413Sthompsastatic tsw_outwakeup_t usb2_com_outwakeup; 120184610Salfredstatic tsw_free_t usb2_com_free; 121184610Salfred 122184610Salfredstatic struct ttydevsw usb2_com_class = { 123184610Salfred .tsw_flags = TF_INITLOCK | TF_CALLOUT, 124184610Salfred .tsw_open = usb2_com_open, 125184610Salfred .tsw_close = usb2_com_close, 126188413Sthompsa .tsw_outwakeup = usb2_com_outwakeup, 127184610Salfred .tsw_ioctl = usb2_com_ioctl, 128184610Salfred .tsw_param = usb2_com_param, 129184610Salfred .tsw_modem = usb2_com_modem, 130184610Salfred .tsw_free = usb2_com_free, 131184610Salfred}; 132184610Salfred 133188942SthompsaMODULE_DEPEND(ucom, usb, 1, 1, 1); 134188942SthompsaMODULE_VERSION(ucom, 1); 135184610Salfred 136184610Salfred#define UCOM_UNIT_MAX 0x1000 /* exclusive */ 137184610Salfred#define UCOM_SUB_UNIT_MAX 0x100 /* exclusive */ 138184610Salfred 139184610Salfredstatic uint8_t usb2_com_bitmap[(UCOM_UNIT_MAX + 7) / 8]; 140184610Salfred 141184610Salfredstatic uint8_t 142184610Salfredusb2_com_units_alloc(uint32_t sub_units, uint32_t *p_root_unit) 143184610Salfred{ 144184610Salfred uint32_t n; 145184610Salfred uint32_t o; 146184610Salfred uint32_t x; 147184610Salfred uint32_t max = UCOM_UNIT_MAX - (UCOM_UNIT_MAX % sub_units); 148184610Salfred uint8_t error = 1; 149184610Salfred 150184610Salfred mtx_lock(&Giant); 151184610Salfred 152184610Salfred for (n = 0; n < max; n += sub_units) { 153184610Salfred 154184610Salfred /* check for free consecutive bits */ 155184610Salfred 156184610Salfred for (o = 0; o < sub_units; o++) { 157184610Salfred 158184610Salfred x = n + o; 159184610Salfred 160184610Salfred if (usb2_com_bitmap[x / 8] & (1 << (x % 8))) { 161184610Salfred goto skip; 162184610Salfred } 163184610Salfred } 164184610Salfred 165184610Salfred /* allocate */ 166184610Salfred 167184610Salfred for (o = 0; o < sub_units; o++) { 168184610Salfred 169184610Salfred x = n + o; 170184610Salfred 171184610Salfred usb2_com_bitmap[x / 8] |= (1 << (x % 8)); 172184610Salfred } 173184610Salfred 174184610Salfred error = 0; 175184610Salfred 176184610Salfred break; 177184610Salfred 178184610Salfredskip: ; 179184610Salfred } 180184610Salfred 181184610Salfred mtx_unlock(&Giant); 182184610Salfred 183184610Salfred /* 184184610Salfred * Always set the variable pointed to by "p_root_unit" so that 185184610Salfred * the compiler does not think that it is used uninitialised: 186184610Salfred */ 187184610Salfred *p_root_unit = n; 188184610Salfred 189184610Salfred return (error); 190184610Salfred} 191184610Salfred 192184610Salfredstatic void 193184610Salfredusb2_com_units_free(uint32_t root_unit, uint32_t sub_units) 194184610Salfred{ 195184610Salfred uint32_t x; 196184610Salfred 197184610Salfred mtx_lock(&Giant); 198184610Salfred 199184610Salfred while (sub_units--) { 200184610Salfred x = root_unit + sub_units; 201184610Salfred usb2_com_bitmap[x / 8] &= ~(1 << (x % 8)); 202184610Salfred } 203184610Salfred 204184610Salfred mtx_unlock(&Giant); 205184610Salfred} 206184610Salfred 207184610Salfred/* 208184610Salfred * "N" sub_units are setup at a time. All sub-units will 209184610Salfred * be given sequential unit numbers. The number of 210184610Salfred * sub-units can be used to differentiate among 211184610Salfred * different types of devices. 212184610Salfred * 213188413Sthompsa * The mutex pointed to by "mtx" is applied before all 214188413Sthompsa * callbacks are called back. Also "mtx" must be applied 215188413Sthompsa * before calling into the ucom-layer! 216184610Salfred */ 217184610Salfredint 218192984Sthompsausb2_com_attach(struct ucom_super_softc *ssc, struct ucom_softc *sc, 219184610Salfred uint32_t sub_units, void *parent, 220192984Sthompsa const struct ucom_callback *callback, struct mtx *mtx) 221184610Salfred{ 222184610Salfred uint32_t n; 223184610Salfred uint32_t root_unit; 224184610Salfred int error = 0; 225184610Salfred 226184610Salfred if ((sc == NULL) || 227184610Salfred (sub_units == 0) || 228184610Salfred (sub_units > UCOM_SUB_UNIT_MAX) || 229184610Salfred (callback == NULL)) { 230184610Salfred return (EINVAL); 231184610Salfred } 232188413Sthompsa 233188413Sthompsa /* XXX unit management does not really belong here */ 234184610Salfred if (usb2_com_units_alloc(sub_units, &root_unit)) { 235184610Salfred return (ENOMEM); 236184610Salfred } 237188413Sthompsa 238188413Sthompsa error = usb2_proc_create(&ssc->sc_tq, mtx, "ucom", USB_PRI_MED); 239188413Sthompsa if (error) { 240184610Salfred usb2_com_units_free(root_unit, sub_units); 241188413Sthompsa return (error); 242184610Salfred } 243188413Sthompsa 244188413Sthompsa for (n = 0; n != sub_units; n++, sc++) { 245184610Salfred sc->sc_unit = root_unit + n; 246184610Salfred sc->sc_local_unit = n; 247184610Salfred sc->sc_super = ssc; 248188413Sthompsa sc->sc_mtx = mtx; 249184610Salfred sc->sc_parent = parent; 250184610Salfred sc->sc_callback = callback; 251184610Salfred 252188413Sthompsa error = usb2_com_attach_tty(sc, sub_units); 253184610Salfred if (error) { 254184610Salfred usb2_com_detach(ssc, sc - n, n); 255184610Salfred usb2_com_units_free(root_unit + n, sub_units - n); 256188413Sthompsa return (error); 257184610Salfred } 258184610Salfred sc->sc_flag |= UCOM_FLAG_ATTACHED; 259184610Salfred } 260188413Sthompsa return (0); 261184610Salfred} 262184610Salfred 263188413Sthompsa/* 264188413Sthompsa * NOTE: the following function will do nothing if 265184610Salfred * the structure pointed to by "ssc" and "sc" is zero. 266184610Salfred */ 267184610Salfredvoid 268192984Sthompsausb2_com_detach(struct ucom_super_softc *ssc, struct ucom_softc *sc, 269184610Salfred uint32_t sub_units) 270184610Salfred{ 271184610Salfred uint32_t n; 272184610Salfred 273188413Sthompsa usb2_proc_drain(&ssc->sc_tq); 274184610Salfred 275188413Sthompsa for (n = 0; n != sub_units; n++, sc++) { 276184610Salfred if (sc->sc_flag & UCOM_FLAG_ATTACHED) { 277184610Salfred 278188413Sthompsa usb2_com_detach_tty(sc); 279184610Salfred 280184610Salfred usb2_com_units_free(sc->sc_unit, 1); 281184610Salfred 282184610Salfred /* avoid duplicate detach: */ 283184610Salfred sc->sc_flag &= ~UCOM_FLAG_ATTACHED; 284184610Salfred } 285184610Salfred } 286188413Sthompsa usb2_proc_free(&ssc->sc_tq); 287184610Salfred} 288184610Salfred 289184610Salfredstatic int 290192984Sthompsausb2_com_attach_tty(struct ucom_softc *sc, uint32_t sub_units) 291184610Salfred{ 292184610Salfred struct tty *tp; 293184610Salfred int error = 0; 294184610Salfred char buf[32]; /* temporary TTY device name buffer */ 295184610Salfred 296193018Sed tp = tty_alloc_mutex(&usb2_com_class, sc, sc->sc_mtx); 297184610Salfred if (tp == NULL) { 298184610Salfred error = ENOMEM; 299184610Salfred goto done; 300184610Salfred } 301184610Salfred DPRINTF("tp = %p, unit = %d\n", tp, sc->sc_unit); 302184610Salfred 303184610Salfred buf[0] = 0; /* set some default value */ 304184610Salfred 305184610Salfred /* Check if the client has a custom TTY name */ 306184610Salfred if (sc->sc_callback->usb2_com_tty_name) { 307184610Salfred sc->sc_callback->usb2_com_tty_name(sc, buf, 308184610Salfred sizeof(buf), sc->sc_local_unit); 309184610Salfred } 310184610Salfred if (buf[0] == 0) { 311184610Salfred /* Use default TTY name */ 312188413Sthompsa if (sub_units > 1) { 313188413Sthompsa /* multiple modems in one */ 314188413Sthompsa if (snprintf(buf, sizeof(buf), "U%u.%u", 315188413Sthompsa sc->sc_unit - sc->sc_local_unit, 316188413Sthompsa sc->sc_local_unit)) { 317188413Sthompsa /* ignore */ 318188413Sthompsa } 319188413Sthompsa } else { 320188413Sthompsa /* single modem */ 321188413Sthompsa if (snprintf(buf, sizeof(buf), "U%u", sc->sc_unit)) { 322188413Sthompsa /* ignore */ 323188413Sthompsa } 324184610Salfred } 325184610Salfred } 326184610Salfred tty_makedev(tp, NULL, "%s", buf); 327184610Salfred 328184610Salfred sc->sc_tty = tp; 329184610Salfred 330184610Salfred DPRINTF("ttycreate: %s\n", buf); 331184610Salfred usb2_cv_init(&sc->sc_cv, "usb2_com"); 332184610Salfred 333184610Salfreddone: 334184610Salfred return (error); 335184610Salfred} 336184610Salfred 337184610Salfredstatic void 338192984Sthompsausb2_com_detach_tty(struct ucom_softc *sc) 339184610Salfred{ 340184610Salfred struct tty *tp = sc->sc_tty; 341184610Salfred 342184610Salfred DPRINTF("sc = %p, tp = %p\n", sc, sc->sc_tty); 343184610Salfred 344184610Salfred /* the config thread has been stopped when we get here */ 345184610Salfred 346188413Sthompsa mtx_lock(sc->sc_mtx); 347184610Salfred sc->sc_flag |= UCOM_FLAG_GONE; 348184610Salfred sc->sc_flag &= ~(UCOM_FLAG_HL_READY | 349184610Salfred UCOM_FLAG_LL_READY); 350188413Sthompsa mtx_unlock(sc->sc_mtx); 351184610Salfred if (tp) { 352184610Salfred tty_lock(tp); 353184610Salfred 354184610Salfred usb2_com_close(tp); /* close, if any */ 355184610Salfred 356184610Salfred tty_rel_gone(tp); 357184610Salfred 358188413Sthompsa mtx_lock(sc->sc_mtx); 359184610Salfred /* Wait for the callback after the TTY is torn down */ 360184610Salfred while (sc->sc_ttyfreed == 0) 361188413Sthompsa usb2_cv_wait(&sc->sc_cv, sc->sc_mtx); 362184610Salfred /* 363184610Salfred * make sure that read and write transfers are stopped 364184610Salfred */ 365184610Salfred if (sc->sc_callback->usb2_com_stop_read) { 366184610Salfred (sc->sc_callback->usb2_com_stop_read) (sc); 367184610Salfred } 368184610Salfred if (sc->sc_callback->usb2_com_stop_write) { 369184610Salfred (sc->sc_callback->usb2_com_stop_write) (sc); 370184610Salfred } 371188413Sthompsa mtx_unlock(sc->sc_mtx); 372184610Salfred } 373184610Salfred usb2_cv_destroy(&sc->sc_cv); 374184610Salfred} 375184610Salfred 376184610Salfredstatic void 377192984Sthompsausb2_com_queue_command(struct ucom_softc *sc, 378193045Sthompsa usb_proc_callback_t *fn, struct termios *pt, 379192984Sthompsa struct usb_proc_msg *t0, struct usb_proc_msg *t1) 380184610Salfred{ 381192984Sthompsa struct ucom_super_softc *ssc = sc->sc_super; 382192984Sthompsa struct ucom_param_task *task; 383187176Sthompsa 384188413Sthompsa mtx_assert(sc->sc_mtx, MA_OWNED); 385188413Sthompsa 386188413Sthompsa if (usb2_proc_is_gone(&ssc->sc_tq)) { 387187176Sthompsa DPRINTF("proc is gone\n"); 388188413Sthompsa return; /* nothing to do */ 389187176Sthompsa } 390188413Sthompsa /* 391188413Sthompsa * NOTE: The task cannot get executed before we drop the 392188413Sthompsa * "sc_mtx" mutex. It is safe to update fields in the message 393188413Sthompsa * structure after that the message got queued. 394188413Sthompsa */ 395192984Sthompsa task = (struct ucom_param_task *) 396188413Sthompsa usb2_proc_msignal(&ssc->sc_tq, t0, t1); 397187176Sthompsa 398188413Sthompsa /* Setup callback and softc pointers */ 399188413Sthompsa task->hdr.pm_callback = fn; 400188413Sthompsa task->sc = sc; 401184610Salfred 402188413Sthompsa /* 403188413Sthompsa * Make a copy of the termios. This field is only present if 404188413Sthompsa * the "pt" field is not NULL. 405188413Sthompsa */ 406188413Sthompsa if (pt != NULL) 407188413Sthompsa task->termios_copy = *pt; 408184610Salfred 409188413Sthompsa /* 410188413Sthompsa * Closing the device should be synchronous. 411188413Sthompsa */ 412188413Sthompsa if (fn == usb2_com_cfg_close) 413188413Sthompsa usb2_proc_mwait(&ssc->sc_tq, t0, t1); 414188413Sthompsa 415190742Sthompsa /* 416190742Sthompsa * In case of multiple configure requests, 417190742Sthompsa * keep track of the last one! 418190742Sthompsa */ 419190742Sthompsa if (fn == usb2_com_cfg_start_transfers) 420190742Sthompsa sc->sc_last_start_xfer = &task->hdr; 421184610Salfred} 422184610Salfred 423184610Salfredstatic void 424192984Sthompsausb2_com_shutdown(struct ucom_softc *sc) 425184610Salfred{ 426184610Salfred struct tty *tp = sc->sc_tty; 427184610Salfred 428188413Sthompsa mtx_assert(sc->sc_mtx, MA_OWNED); 429184610Salfred 430184610Salfred DPRINTF("\n"); 431184610Salfred 432184610Salfred /* 433184610Salfred * Hang up if necessary: 434184610Salfred */ 435184610Salfred if (tp->t_termios.c_cflag & HUPCL) { 436184610Salfred usb2_com_modem(tp, 0, SER_DTR); 437184610Salfred } 438184610Salfred} 439184610Salfred 440184610Salfred/* 441184610Salfred * Return values: 442184610Salfred * 0: normal 443188413Sthompsa * else: taskqueue is draining or gone 444184610Salfred */ 445184610Salfreduint8_t 446192984Sthompsausb2_com_cfg_is_gone(struct ucom_softc *sc) 447184610Salfred{ 448192984Sthompsa struct ucom_super_softc *ssc = sc->sc_super; 449184610Salfred 450188413Sthompsa return (usb2_proc_is_gone(&ssc->sc_tq)); 451184610Salfred} 452184610Salfred 453184610Salfredstatic void 454192984Sthompsausb2_com_cfg_start_transfers(struct usb_proc_msg *_task) 455184610Salfred{ 456192984Sthompsa struct ucom_cfg_task *task = 457192984Sthompsa (struct ucom_cfg_task *)_task; 458192984Sthompsa struct ucom_softc *sc = task->sc; 459187176Sthompsa 460184610Salfred if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) { 461184610Salfred return; 462184610Salfred } 463184610Salfred if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) { 464184610Salfred /* TTY device closed */ 465184610Salfred return; 466184610Salfred } 467184610Salfred 468190742Sthompsa if (_task == sc->sc_last_start_xfer) 469190742Sthompsa sc->sc_flag |= UCOM_FLAG_GP_DATA; 470190742Sthompsa 471184610Salfred if (sc->sc_callback->usb2_com_start_read) { 472184610Salfred (sc->sc_callback->usb2_com_start_read) (sc); 473184610Salfred } 474184610Salfred if (sc->sc_callback->usb2_com_start_write) { 475184610Salfred (sc->sc_callback->usb2_com_start_write) (sc); 476184610Salfred } 477184610Salfred} 478184610Salfred 479184610Salfredstatic void 480192984Sthompsausb2_com_start_transfers(struct ucom_softc *sc) 481184610Salfred{ 482184610Salfred if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) { 483184610Salfred return; 484184610Salfred } 485184610Salfred /* 486188413Sthompsa * Make sure that data transfers are started in both 487188413Sthompsa * directions: 488184610Salfred */ 489184610Salfred if (sc->sc_callback->usb2_com_start_read) { 490184610Salfred (sc->sc_callback->usb2_com_start_read) (sc); 491184610Salfred } 492184610Salfred if (sc->sc_callback->usb2_com_start_write) { 493184610Salfred (sc->sc_callback->usb2_com_start_write) (sc); 494184610Salfred } 495184610Salfred} 496184610Salfred 497184610Salfredstatic void 498192984Sthompsausb2_com_cfg_open(struct usb_proc_msg *_task) 499184610Salfred{ 500192984Sthompsa struct ucom_cfg_task *task = 501192984Sthompsa (struct ucom_cfg_task *)_task; 502192984Sthompsa struct ucom_softc *sc = task->sc; 503187176Sthompsa 504184610Salfred DPRINTF("\n"); 505184610Salfred 506184610Salfred if (sc->sc_flag & UCOM_FLAG_LL_READY) { 507184610Salfred 508184610Salfred /* already opened */ 509184610Salfred 510184610Salfred } else { 511184610Salfred 512184610Salfred sc->sc_flag |= UCOM_FLAG_LL_READY; 513184610Salfred 514184610Salfred if (sc->sc_callback->usb2_com_cfg_open) { 515184610Salfred (sc->sc_callback->usb2_com_cfg_open) (sc); 516184610Salfred 517184610Salfred /* wait a little */ 518188413Sthompsa usb2_pause_mtx(sc->sc_mtx, hz / 10); 519184610Salfred } 520184610Salfred } 521184610Salfred} 522184610Salfred 523184610Salfredstatic int 524184610Salfredusb2_com_open(struct tty *tp) 525184610Salfred{ 526192984Sthompsa struct ucom_softc *sc = tty_softc(tp); 527184610Salfred int error; 528184610Salfred 529188413Sthompsa mtx_assert(sc->sc_mtx, MA_OWNED); 530184610Salfred 531184610Salfred if (sc->sc_flag & UCOM_FLAG_GONE) { 532184610Salfred return (ENXIO); 533184610Salfred } 534184610Salfred if (sc->sc_flag & UCOM_FLAG_HL_READY) { 535184610Salfred /* already opened */ 536184610Salfred return (0); 537184610Salfred } 538184610Salfred DPRINTF("tp = %p\n", tp); 539184610Salfred 540184610Salfred if (sc->sc_callback->usb2_com_pre_open) { 541184610Salfred /* 542184610Salfred * give the lower layer a chance to disallow TTY open, for 543184610Salfred * example if the device is not present: 544184610Salfred */ 545184610Salfred error = (sc->sc_callback->usb2_com_pre_open) (sc); 546184610Salfred if (error) { 547184610Salfred return (error); 548184610Salfred } 549184610Salfred } 550184610Salfred sc->sc_flag |= UCOM_FLAG_HL_READY; 551184610Salfred 552184610Salfred /* Disable transfers */ 553184610Salfred sc->sc_flag &= ~UCOM_FLAG_GP_DATA; 554184610Salfred 555184610Salfred sc->sc_lsr = 0; 556184610Salfred sc->sc_msr = 0; 557184610Salfred sc->sc_mcr = 0; 558184610Salfred 559188413Sthompsa /* reset programmed line state */ 560188413Sthompsa sc->sc_pls_curr = 0; 561188413Sthompsa sc->sc_pls_set = 0; 562188413Sthompsa sc->sc_pls_clr = 0; 563184610Salfred 564188413Sthompsa usb2_com_queue_command(sc, usb2_com_cfg_open, NULL, 565188413Sthompsa &sc->sc_open_task[0].hdr, 566188413Sthompsa &sc->sc_open_task[1].hdr); 567184610Salfred 568188413Sthompsa /* Queue transfer enable command last */ 569188413Sthompsa usb2_com_queue_command(sc, usb2_com_cfg_start_transfers, NULL, 570188413Sthompsa &sc->sc_start_task[0].hdr, 571188413Sthompsa &sc->sc_start_task[1].hdr); 572188413Sthompsa 573184610Salfred usb2_com_modem(tp, SER_DTR | SER_RTS, 0); 574184610Salfred 575184610Salfred usb2_com_break(sc, 0); 576184610Salfred 577184610Salfred usb2_com_status_change(sc); 578184610Salfred 579184610Salfred return (0); 580184610Salfred} 581184610Salfred 582184610Salfredstatic void 583192984Sthompsausb2_com_cfg_close(struct usb_proc_msg *_task) 584184610Salfred{ 585192984Sthompsa struct ucom_cfg_task *task = 586192984Sthompsa (struct ucom_cfg_task *)_task; 587192984Sthompsa struct ucom_softc *sc = task->sc; 588187176Sthompsa 589184610Salfred DPRINTF("\n"); 590184610Salfred 591184610Salfred if (sc->sc_flag & UCOM_FLAG_LL_READY) { 592192820Sthompsa sc->sc_flag &= ~UCOM_FLAG_LL_READY; 593192820Sthompsa if (sc->sc_callback->usb2_com_cfg_close) 594184610Salfred (sc->sc_callback->usb2_com_cfg_close) (sc); 595184610Salfred } else { 596184610Salfred /* already closed */ 597184610Salfred } 598184610Salfred} 599184610Salfred 600184610Salfredstatic void 601184610Salfredusb2_com_close(struct tty *tp) 602184610Salfred{ 603192984Sthompsa struct ucom_softc *sc = tty_softc(tp); 604184610Salfred 605188413Sthompsa mtx_assert(sc->sc_mtx, MA_OWNED); 606188413Sthompsa 607184610Salfred DPRINTF("tp=%p\n", tp); 608184610Salfred 609184610Salfred if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) { 610184610Salfred DPRINTF("tp=%p already closed\n", tp); 611184610Salfred return; 612184610Salfred } 613184610Salfred usb2_com_shutdown(sc); 614184610Salfred 615188413Sthompsa usb2_com_queue_command(sc, usb2_com_cfg_close, NULL, 616188413Sthompsa &sc->sc_close_task[0].hdr, 617188413Sthompsa &sc->sc_close_task[1].hdr); 618184610Salfred 619192820Sthompsa sc->sc_flag &= ~(UCOM_FLAG_HL_READY | UCOM_FLAG_RTS_IFLOW); 620184610Salfred 621184610Salfred if (sc->sc_callback->usb2_com_stop_read) { 622184610Salfred (sc->sc_callback->usb2_com_stop_read) (sc); 623184610Salfred } 624184610Salfred} 625184610Salfred 626184610Salfredstatic int 627184610Salfredusb2_com_ioctl(struct tty *tp, u_long cmd, caddr_t data, struct thread *td) 628184610Salfred{ 629192984Sthompsa struct ucom_softc *sc = tty_softc(tp); 630184610Salfred int error; 631184610Salfred 632188413Sthompsa mtx_assert(sc->sc_mtx, MA_OWNED); 633184610Salfred 634184610Salfred if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) { 635184610Salfred return (EIO); 636184610Salfred } 637184610Salfred DPRINTF("cmd = 0x%08lx\n", cmd); 638184610Salfred 639184610Salfred switch (cmd) { 640184610Salfred case TIOCSBRK: 641184610Salfred usb2_com_break(sc, 1); 642184610Salfred error = 0; 643184610Salfred break; 644184610Salfred case TIOCCBRK: 645184610Salfred usb2_com_break(sc, 0); 646184610Salfred error = 0; 647184610Salfred break; 648184610Salfred default: 649184610Salfred if (sc->sc_callback->usb2_com_ioctl) { 650184610Salfred error = (sc->sc_callback->usb2_com_ioctl) 651184610Salfred (sc, cmd, data, 0, td); 652184610Salfred } else { 653184610Salfred error = ENOIOCTL; 654184610Salfred } 655184610Salfred break; 656184610Salfred } 657184610Salfred return (error); 658184610Salfred} 659184610Salfred 660184610Salfredstatic int 661184610Salfredusb2_com_modem(struct tty *tp, int sigon, int sigoff) 662184610Salfred{ 663192984Sthompsa struct ucom_softc *sc = tty_softc(tp); 664184610Salfred uint8_t onoff; 665184610Salfred 666188413Sthompsa mtx_assert(sc->sc_mtx, MA_OWNED); 667184610Salfred 668184610Salfred if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) { 669184610Salfred return (0); 670184610Salfred } 671184610Salfred if ((sigon == 0) && (sigoff == 0)) { 672184610Salfred 673184610Salfred if (sc->sc_mcr & SER_DTR) { 674184610Salfred sigon |= SER_DTR; 675184610Salfred } 676184610Salfred if (sc->sc_mcr & SER_RTS) { 677184610Salfred sigon |= SER_RTS; 678184610Salfred } 679184610Salfred if (sc->sc_msr & SER_CTS) { 680184610Salfred sigon |= SER_CTS; 681184610Salfred } 682184610Salfred if (sc->sc_msr & SER_DCD) { 683184610Salfred sigon |= SER_DCD; 684184610Salfred } 685184610Salfred if (sc->sc_msr & SER_DSR) { 686184610Salfred sigon |= SER_DSR; 687184610Salfred } 688184610Salfred if (sc->sc_msr & SER_RI) { 689184610Salfred sigon |= SER_RI; 690184610Salfred } 691184610Salfred return (sigon); 692184610Salfred } 693184610Salfred if (sigon & SER_DTR) { 694184610Salfred sc->sc_mcr |= SER_DTR; 695184610Salfred } 696184610Salfred if (sigoff & SER_DTR) { 697184610Salfred sc->sc_mcr &= ~SER_DTR; 698184610Salfred } 699184610Salfred if (sigon & SER_RTS) { 700184610Salfred sc->sc_mcr |= SER_RTS; 701184610Salfred } 702184610Salfred if (sigoff & SER_RTS) { 703184610Salfred sc->sc_mcr &= ~SER_RTS; 704184610Salfred } 705184610Salfred onoff = (sc->sc_mcr & SER_DTR) ? 1 : 0; 706184610Salfred usb2_com_dtr(sc, onoff); 707184610Salfred 708184610Salfred onoff = (sc->sc_mcr & SER_RTS) ? 1 : 0; 709184610Salfred usb2_com_rts(sc, onoff); 710184610Salfred 711184610Salfred return (0); 712184610Salfred} 713184610Salfred 714184610Salfredstatic void 715192984Sthompsausb2_com_cfg_line_state(struct usb_proc_msg *_task) 716184610Salfred{ 717192984Sthompsa struct ucom_cfg_task *task = 718192984Sthompsa (struct ucom_cfg_task *)_task; 719192984Sthompsa struct ucom_softc *sc = task->sc; 720188413Sthompsa uint8_t notch_bits; 721188413Sthompsa uint8_t any_bits; 722188413Sthompsa uint8_t prev_value; 723188413Sthompsa uint8_t last_value; 724188413Sthompsa uint8_t mask; 725187176Sthompsa 726184610Salfred if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) { 727184610Salfred return; 728184610Salfred } 729184610Salfred 730188413Sthompsa mask = 0; 731188413Sthompsa /* compute callback mask */ 732188413Sthompsa if (sc->sc_callback->usb2_com_cfg_set_dtr) 733188413Sthompsa mask |= UCOM_LS_DTR; 734188413Sthompsa if (sc->sc_callback->usb2_com_cfg_set_rts) 735188413Sthompsa mask |= UCOM_LS_RTS; 736188413Sthompsa if (sc->sc_callback->usb2_com_cfg_set_break) 737188413Sthompsa mask |= UCOM_LS_BREAK; 738184610Salfred 739188413Sthompsa /* compute the bits we are to program */ 740188413Sthompsa notch_bits = (sc->sc_pls_set & sc->sc_pls_clr) & mask; 741188413Sthompsa any_bits = (sc->sc_pls_set | sc->sc_pls_clr) & mask; 742188413Sthompsa prev_value = sc->sc_pls_curr ^ notch_bits; 743188413Sthompsa last_value = sc->sc_pls_curr; 744187176Sthompsa 745188413Sthompsa /* reset programmed line state */ 746188413Sthompsa sc->sc_pls_curr = 0; 747188413Sthompsa sc->sc_pls_set = 0; 748188413Sthompsa sc->sc_pls_clr = 0; 749188413Sthompsa 750188413Sthompsa /* ensure that we don't loose any levels */ 751188413Sthompsa if (notch_bits & UCOM_LS_DTR) 752188413Sthompsa sc->sc_callback->usb2_com_cfg_set_dtr(sc, 753188413Sthompsa (prev_value & UCOM_LS_DTR) ? 1 : 0); 754188413Sthompsa if (notch_bits & UCOM_LS_RTS) 755188413Sthompsa sc->sc_callback->usb2_com_cfg_set_rts(sc, 756188413Sthompsa (prev_value & UCOM_LS_RTS) ? 1 : 0); 757188413Sthompsa if (notch_bits & UCOM_LS_BREAK) 758188413Sthompsa sc->sc_callback->usb2_com_cfg_set_break(sc, 759188413Sthompsa (prev_value & UCOM_LS_BREAK) ? 1 : 0); 760188413Sthompsa 761188413Sthompsa /* set last value */ 762188413Sthompsa if (any_bits & UCOM_LS_DTR) 763188413Sthompsa sc->sc_callback->usb2_com_cfg_set_dtr(sc, 764188413Sthompsa (last_value & UCOM_LS_DTR) ? 1 : 0); 765188413Sthompsa if (any_bits & UCOM_LS_RTS) 766188413Sthompsa sc->sc_callback->usb2_com_cfg_set_rts(sc, 767188413Sthompsa (last_value & UCOM_LS_RTS) ? 1 : 0); 768188413Sthompsa if (any_bits & UCOM_LS_BREAK) 769188413Sthompsa sc->sc_callback->usb2_com_cfg_set_break(sc, 770188413Sthompsa (last_value & UCOM_LS_BREAK) ? 1 : 0); 771187176Sthompsa} 772187176Sthompsa 773187176Sthompsastatic void 774192984Sthompsausb2_com_line_state(struct ucom_softc *sc, 775188413Sthompsa uint8_t set_bits, uint8_t clear_bits) 776184610Salfred{ 777188413Sthompsa mtx_assert(sc->sc_mtx, MA_OWNED); 778184610Salfred 779184610Salfred if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) { 780184610Salfred return; 781184610Salfred } 782184610Salfred 783188413Sthompsa DPRINTF("on=0x%02x, off=0x%02x\n", set_bits, clear_bits); 784184610Salfred 785188413Sthompsa /* update current programmed line state */ 786188413Sthompsa sc->sc_pls_curr |= set_bits; 787188413Sthompsa sc->sc_pls_curr &= ~clear_bits; 788188413Sthompsa sc->sc_pls_set |= set_bits; 789188413Sthompsa sc->sc_pls_clr |= clear_bits; 790187176Sthompsa 791188413Sthompsa /* defer driver programming */ 792188413Sthompsa usb2_com_queue_command(sc, usb2_com_cfg_line_state, NULL, 793188413Sthompsa &sc->sc_line_state_task[0].hdr, 794188413Sthompsa &sc->sc_line_state_task[1].hdr); 795184610Salfred} 796184610Salfred 797184610Salfredstatic void 798192984Sthompsausb2_com_break(struct ucom_softc *sc, uint8_t onoff) 799187176Sthompsa{ 800188413Sthompsa DPRINTF("onoff = %d\n", onoff); 801187176Sthompsa 802188413Sthompsa if (onoff) 803188413Sthompsa usb2_com_line_state(sc, UCOM_LS_BREAK, 0); 804188413Sthompsa else 805188413Sthompsa usb2_com_line_state(sc, 0, UCOM_LS_BREAK); 806187176Sthompsa} 807187176Sthompsa 808187176Sthompsastatic void 809192984Sthompsausb2_com_dtr(struct ucom_softc *sc, uint8_t onoff) 810184610Salfred{ 811184610Salfred DPRINTF("onoff = %d\n", onoff); 812184610Salfred 813188413Sthompsa if (onoff) 814188413Sthompsa usb2_com_line_state(sc, UCOM_LS_DTR, 0); 815188413Sthompsa else 816188413Sthompsa usb2_com_line_state(sc, 0, UCOM_LS_DTR); 817184610Salfred} 818184610Salfred 819184610Salfredstatic void 820192984Sthompsausb2_com_rts(struct ucom_softc *sc, uint8_t onoff) 821184610Salfred{ 822184610Salfred DPRINTF("onoff = %d\n", onoff); 823184610Salfred 824188413Sthompsa if (onoff) 825188413Sthompsa usb2_com_line_state(sc, UCOM_LS_RTS, 0); 826188413Sthompsa else 827188413Sthompsa usb2_com_line_state(sc, 0, UCOM_LS_RTS); 828184610Salfred} 829184610Salfred 830184610Salfredstatic void 831192984Sthompsausb2_com_cfg_status_change(struct usb_proc_msg *_task) 832184610Salfred{ 833192984Sthompsa struct ucom_cfg_task *task = 834192984Sthompsa (struct ucom_cfg_task *)_task; 835192984Sthompsa struct ucom_softc *sc = task->sc; 836184610Salfred struct tty *tp; 837184610Salfred uint8_t new_msr; 838184610Salfred uint8_t new_lsr; 839184610Salfred uint8_t onoff; 840184610Salfred 841184610Salfred tp = sc->sc_tty; 842184610Salfred 843188413Sthompsa mtx_assert(sc->sc_mtx, MA_OWNED); 844184610Salfred 845184610Salfred if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) { 846184610Salfred return; 847184610Salfred } 848184610Salfred if (sc->sc_callback->usb2_com_cfg_get_status == NULL) { 849184610Salfred return; 850184610Salfred } 851184610Salfred /* get status */ 852184610Salfred 853184610Salfred new_msr = 0; 854184610Salfred new_lsr = 0; 855184610Salfred 856184610Salfred (sc->sc_callback->usb2_com_cfg_get_status) (sc, &new_lsr, &new_msr); 857184610Salfred 858184610Salfred if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) { 859184610Salfred /* TTY device closed */ 860184610Salfred return; 861184610Salfred } 862184610Salfred onoff = ((sc->sc_msr ^ new_msr) & SER_DCD); 863184610Salfred 864184610Salfred sc->sc_msr = new_msr; 865184610Salfred sc->sc_lsr = new_lsr; 866184610Salfred 867184610Salfred if (onoff) { 868184610Salfred 869184610Salfred onoff = (sc->sc_msr & SER_DCD) ? 1 : 0; 870184610Salfred 871184610Salfred DPRINTF("DCD changed to %d\n", onoff); 872184610Salfred 873184610Salfred ttydisc_modem(tp, onoff); 874184610Salfred } 875184610Salfred} 876184610Salfred 877184610Salfredvoid 878192984Sthompsausb2_com_status_change(struct ucom_softc *sc) 879184610Salfred{ 880188413Sthompsa mtx_assert(sc->sc_mtx, MA_OWNED); 881184610Salfred 882184610Salfred if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) { 883184610Salfred return; 884184610Salfred } 885184610Salfred DPRINTF("\n"); 886184610Salfred 887188413Sthompsa usb2_com_queue_command(sc, usb2_com_cfg_status_change, NULL, 888188413Sthompsa &sc->sc_status_task[0].hdr, 889188413Sthompsa &sc->sc_status_task[1].hdr); 890184610Salfred} 891184610Salfred 892184610Salfredstatic void 893192984Sthompsausb2_com_cfg_param(struct usb_proc_msg *_task) 894184610Salfred{ 895192984Sthompsa struct ucom_param_task *task = 896192984Sthompsa (struct ucom_param_task *)_task; 897192984Sthompsa struct ucom_softc *sc = task->sc; 898184610Salfred 899184610Salfred if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) { 900184610Salfred return; 901184610Salfred } 902184610Salfred if (sc->sc_callback->usb2_com_cfg_param == NULL) { 903184610Salfred return; 904184610Salfred } 905184610Salfred 906188413Sthompsa (sc->sc_callback->usb2_com_cfg_param) (sc, &task->termios_copy); 907184610Salfred 908184610Salfred /* wait a little */ 909188413Sthompsa usb2_pause_mtx(sc->sc_mtx, hz / 10); 910184610Salfred} 911184610Salfred 912184610Salfredstatic int 913184610Salfredusb2_com_param(struct tty *tp, struct termios *t) 914184610Salfred{ 915192984Sthompsa struct ucom_softc *sc = tty_softc(tp); 916184610Salfred uint8_t opened; 917184610Salfred int error; 918184610Salfred 919188413Sthompsa mtx_assert(sc->sc_mtx, MA_OWNED); 920184610Salfred 921184610Salfred opened = 0; 922184610Salfred error = 0; 923184610Salfred 924184610Salfred if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) { 925184610Salfred 926184610Salfred /* XXX the TTY layer should call "open()" first! */ 927184610Salfred 928184610Salfred error = usb2_com_open(tp); 929184610Salfred if (error) { 930184610Salfred goto done; 931184610Salfred } 932184610Salfred opened = 1; 933184610Salfred } 934184610Salfred DPRINTF("sc = %p\n", sc); 935184610Salfred 936184610Salfred /* Check requested parameters. */ 937184610Salfred if (t->c_ospeed < 0) { 938184610Salfred DPRINTF("negative ospeed\n"); 939184610Salfred error = EINVAL; 940184610Salfred goto done; 941184610Salfred } 942184610Salfred if (t->c_ispeed && (t->c_ispeed != t->c_ospeed)) { 943184610Salfred DPRINTF("mismatch ispeed and ospeed\n"); 944184610Salfred error = EINVAL; 945184610Salfred goto done; 946184610Salfred } 947184610Salfred t->c_ispeed = t->c_ospeed; 948184610Salfred 949184610Salfred if (sc->sc_callback->usb2_com_pre_param) { 950184610Salfred /* Let the lower layer verify the parameters */ 951184610Salfred error = (sc->sc_callback->usb2_com_pre_param) (sc, t); 952184610Salfred if (error) { 953184610Salfred DPRINTF("callback error = %d\n", error); 954184610Salfred goto done; 955184610Salfred } 956184610Salfred } 957184610Salfred 958184610Salfred /* Disable transfers */ 959184610Salfred sc->sc_flag &= ~UCOM_FLAG_GP_DATA; 960184610Salfred 961184610Salfred /* Queue baud rate programming command first */ 962188413Sthompsa usb2_com_queue_command(sc, usb2_com_cfg_param, t, 963188413Sthompsa &sc->sc_param_task[0].hdr, 964188413Sthompsa &sc->sc_param_task[1].hdr); 965184610Salfred 966184610Salfred /* Queue transfer enable command last */ 967188413Sthompsa usb2_com_queue_command(sc, usb2_com_cfg_start_transfers, NULL, 968188413Sthompsa &sc->sc_start_task[0].hdr, 969188413Sthompsa &sc->sc_start_task[1].hdr); 970184610Salfred 971184610Salfred if (t->c_cflag & CRTS_IFLOW) { 972184610Salfred sc->sc_flag |= UCOM_FLAG_RTS_IFLOW; 973184610Salfred } else if (sc->sc_flag & UCOM_FLAG_RTS_IFLOW) { 974184610Salfred sc->sc_flag &= ~UCOM_FLAG_RTS_IFLOW; 975184610Salfred usb2_com_modem(tp, SER_RTS, 0); 976184610Salfred } 977184610Salfreddone: 978184610Salfred if (error) { 979184610Salfred if (opened) { 980184610Salfred usb2_com_close(tp); 981184610Salfred } 982184610Salfred } 983184610Salfred return (error); 984184610Salfred} 985184610Salfred 986184610Salfredstatic void 987188413Sthompsausb2_com_outwakeup(struct tty *tp) 988184610Salfred{ 989192984Sthompsa struct ucom_softc *sc = tty_softc(tp); 990184610Salfred 991188413Sthompsa mtx_assert(sc->sc_mtx, MA_OWNED); 992184610Salfred 993184610Salfred DPRINTF("sc = %p\n", sc); 994184610Salfred 995184610Salfred if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) { 996184610Salfred /* The higher layer is not ready */ 997184610Salfred return; 998184610Salfred } 999184610Salfred usb2_com_start_transfers(sc); 1000184610Salfred} 1001184610Salfred 1002184610Salfred/*------------------------------------------------------------------------* 1003184610Salfred * usb2_com_get_data 1004184610Salfred * 1005184610Salfred * Return values: 1006184610Salfred * 0: No data is available. 1007184610Salfred * Else: Data is available. 1008184610Salfred *------------------------------------------------------------------------*/ 1009184610Salfreduint8_t 1010192984Sthompsausb2_com_get_data(struct ucom_softc *sc, struct usb_page_cache *pc, 1011184610Salfred uint32_t offset, uint32_t len, uint32_t *actlen) 1012184610Salfred{ 1013192984Sthompsa struct usb_page_search res; 1014184610Salfred struct tty *tp = sc->sc_tty; 1015184610Salfred uint32_t cnt; 1016184610Salfred uint32_t offset_orig; 1017184610Salfred 1018188413Sthompsa mtx_assert(sc->sc_mtx, MA_OWNED); 1019184610Salfred 1020192820Sthompsa if (tty_gone(tp) || 1021192820Sthompsa !(sc->sc_flag & UCOM_FLAG_GP_DATA)) { 1022184610Salfred actlen[0] = 0; 1023184610Salfred return (0); /* multiport device polling */ 1024184610Salfred } 1025184610Salfred offset_orig = offset; 1026184610Salfred 1027184610Salfred while (len != 0) { 1028184610Salfred 1029184610Salfred usb2_get_page(pc, offset, &res); 1030184610Salfred 1031184610Salfred if (res.length > len) { 1032184610Salfred res.length = len; 1033184610Salfred } 1034184610Salfred /* copy data directly into USB buffer */ 1035184610Salfred cnt = ttydisc_getc(tp, res.buffer, res.length); 1036184610Salfred 1037184610Salfred offset += cnt; 1038184610Salfred len -= cnt; 1039184610Salfred 1040184610Salfred if (cnt < res.length) { 1041184610Salfred /* end of buffer */ 1042184610Salfred break; 1043184610Salfred } 1044184610Salfred } 1045184610Salfred 1046184610Salfred actlen[0] = offset - offset_orig; 1047184610Salfred 1048184610Salfred DPRINTF("cnt=%d\n", actlen[0]); 1049184610Salfred 1050184610Salfred if (actlen[0] == 0) { 1051184610Salfred return (0); 1052184610Salfred } 1053184610Salfred return (1); 1054184610Salfred} 1055184610Salfred 1056184610Salfredvoid 1057192984Sthompsausb2_com_put_data(struct ucom_softc *sc, struct usb_page_cache *pc, 1058184610Salfred uint32_t offset, uint32_t len) 1059184610Salfred{ 1060192984Sthompsa struct usb_page_search res; 1061184610Salfred struct tty *tp = sc->sc_tty; 1062184610Salfred char *buf; 1063184610Salfred uint32_t cnt; 1064184610Salfred 1065188413Sthompsa mtx_assert(sc->sc_mtx, MA_OWNED); 1066184610Salfred 1067192820Sthompsa if (tty_gone(tp)) 1068184610Salfred return; /* multiport device polling */ 1069192820Sthompsa 1070184610Salfred if (len == 0) 1071184610Salfred return; /* no data */ 1072184610Salfred 1073184610Salfred /* set a flag to prevent recursation ? */ 1074184610Salfred 1075184610Salfred while (len > 0) { 1076184610Salfred 1077184610Salfred usb2_get_page(pc, offset, &res); 1078184610Salfred 1079184610Salfred if (res.length > len) { 1080184610Salfred res.length = len; 1081184610Salfred } 1082184610Salfred len -= res.length; 1083184610Salfred offset += res.length; 1084184610Salfred 1085184610Salfred /* pass characters to tty layer */ 1086184610Salfred 1087184610Salfred buf = res.buffer; 1088184610Salfred cnt = res.length; 1089184610Salfred 1090184610Salfred /* first check if we can pass the buffer directly */ 1091184610Salfred 1092184610Salfred if (ttydisc_can_bypass(tp)) { 1093184610Salfred if (ttydisc_rint_bypass(tp, buf, cnt) != cnt) { 1094184610Salfred DPRINTF("tp=%p, data lost\n", tp); 1095184610Salfred } 1096184610Salfred continue; 1097184610Salfred } 1098184610Salfred /* need to loop */ 1099184610Salfred 1100184610Salfred for (cnt = 0; cnt != res.length; cnt++) { 1101184610Salfred if (ttydisc_rint(tp, buf[cnt], 0) == -1) { 1102184610Salfred /* XXX what should we do? */ 1103184610Salfred 1104184610Salfred DPRINTF("tp=%p, lost %d " 1105184610Salfred "chars\n", tp, res.length - cnt); 1106184610Salfred break; 1107184610Salfred } 1108184610Salfred } 1109184610Salfred } 1110184610Salfred ttydisc_rint_done(tp); 1111184610Salfred} 1112184610Salfred 1113184610Salfredstatic void 1114184610Salfredusb2_com_free(void *xsc) 1115184610Salfred{ 1116192984Sthompsa struct ucom_softc *sc = xsc; 1117184610Salfred 1118188413Sthompsa mtx_lock(sc->sc_mtx); 1119184610Salfred sc->sc_ttyfreed = 1; 1120184610Salfred usb2_cv_signal(&sc->sc_cv); 1121188413Sthompsa mtx_unlock(sc->sc_mtx); 1122184610Salfred} 1123