usb_serial.c revision 185948
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/usb2/serial/usb2_serial.c 185948 2008-12-11 23:13:02Z 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 70184610Salfred/* 71184610Salfred * NOTE: all function names beginning like "usb2_com_cfg_" can only 72184610Salfred * be called from within the config thread function ! 73184610Salfred */ 74184610Salfred 75184610Salfred#include <dev/usb2/include/usb2_standard.h> 76184610Salfred#include <dev/usb2/include/usb2_mfunc.h> 77184610Salfred#include <dev/usb2/include/usb2_error.h> 78184610Salfred#include <dev/usb2/include/usb2_cdc.h> 79184610Salfred 80184610Salfred#define USB_DEBUG_VAR usb2_com_debug 81184610Salfred#define usb2_config_td_cc usb2_com_config_copy 82184610Salfred#define usb2_config_td_softc usb2_com_softc 83184610Salfred 84184610Salfred#include <dev/usb2/core/usb2_core.h> 85184610Salfred#include <dev/usb2/core/usb2_debug.h> 86184610Salfred#include <dev/usb2/core/usb2_process.h> 87184610Salfred#include <dev/usb2/core/usb2_config_td.h> 88184610Salfred#include <dev/usb2/core/usb2_request.h> 89184610Salfred#include <dev/usb2/core/usb2_busdma.h> 90184610Salfred#include <dev/usb2/core/usb2_util.h> 91184610Salfred 92184610Salfred#include <dev/usb2/serial/usb2_serial.h> 93184610Salfred 94184610Salfred#if USB_DEBUG 95184610Salfredstatic int usb2_com_debug = 0; 96184610Salfred 97184610SalfredSYSCTL_NODE(_hw_usb2, OID_AUTO, ucom, CTLFLAG_RW, 0, "USB ucom"); 98184610SalfredSYSCTL_INT(_hw_usb2_ucom, OID_AUTO, debug, CTLFLAG_RW, 99184610Salfred &usb2_com_debug, 0, "ucom debug level"); 100184610Salfred#endif 101184610Salfred 102184610Salfredstruct usb2_com_config_copy { 103184610Salfred struct usb2_com_softc *cc_softc; 104184610Salfred uint8_t cc_flag0; 105184610Salfred uint8_t cc_flag1; 106184610Salfred uint8_t cc_flag2; 107184610Salfred uint8_t cc_flag3; 108184610Salfred}; 109184610Salfred 110184610Salfredstatic usb2_config_td_command_t usb2_com_config_copy; 111184610Salfredstatic usb2_config_td_command_t usb2_com_cfg_start_transfers; 112184610Salfredstatic usb2_config_td_command_t usb2_com_cfg_open; 113184610Salfredstatic usb2_config_td_command_t usb2_com_cfg_close; 114184610Salfredstatic usb2_config_td_command_t usb2_com_cfg_break; 115184610Salfredstatic usb2_config_td_command_t usb2_com_cfg_dtr; 116184610Salfredstatic usb2_config_td_command_t usb2_com_cfg_rts; 117184610Salfredstatic usb2_config_td_command_t usb2_com_cfg_status_change; 118184610Salfredstatic usb2_config_td_command_t usb2_com_cfg_param; 119184610Salfred 120185948Sthompsastatic uint8_t usb2_com_units_alloc(uint32_t, uint32_t *); 121185948Sthompsastatic void usb2_com_units_free(uint32_t, uint32_t); 122185948Sthompsastatic int usb2_com_attach_sub(struct usb2_com_softc *); 123185948Sthompsastatic void usb2_com_detach_sub(struct usb2_com_softc *); 124185948Sthompsastatic void usb2_com_queue_command(struct usb2_com_softc *, 125185948Sthompsa usb2_config_td_command_t *, int); 126185948Sthompsastatic void usb2_com_shutdown(struct usb2_com_softc *); 127185948Sthompsastatic void usb2_com_start_transfers(struct usb2_com_softc *); 128185948Sthompsastatic void usb2_com_break(struct usb2_com_softc *, uint8_t); 129185948Sthompsastatic void usb2_com_dtr(struct usb2_com_softc *, uint8_t); 130185948Sthompsastatic void usb2_com_rts(struct usb2_com_softc *, uint8_t); 131184610Salfred 132184610Salfredstatic tsw_open_t usb2_com_open; 133184610Salfredstatic tsw_close_t usb2_com_close; 134184610Salfredstatic tsw_ioctl_t usb2_com_ioctl; 135184610Salfredstatic tsw_modem_t usb2_com_modem; 136184610Salfredstatic tsw_param_t usb2_com_param; 137184610Salfredstatic tsw_outwakeup_t usb2_com_start_write; 138184610Salfredstatic tsw_free_t usb2_com_free; 139184610Salfred 140184610Salfredstatic struct ttydevsw usb2_com_class = { 141184610Salfred .tsw_flags = TF_INITLOCK | TF_CALLOUT, 142184610Salfred .tsw_open = usb2_com_open, 143184610Salfred .tsw_close = usb2_com_close, 144184610Salfred .tsw_outwakeup = usb2_com_start_write, 145184610Salfred .tsw_ioctl = usb2_com_ioctl, 146184610Salfred .tsw_param = usb2_com_param, 147184610Salfred .tsw_modem = usb2_com_modem, 148184610Salfred .tsw_free = usb2_com_free, 149184610Salfred}; 150184610Salfred 151184736SimpMODULE_DEPEND(usb2_serial, usb2_core, 1, 1, 1); 152184610SalfredMODULE_VERSION(usb2_serial, 1); 153184610Salfred 154184610Salfred#define UCOM_UNIT_MAX 0x1000 /* exclusive */ 155184610Salfred#define UCOM_SUB_UNIT_MAX 0x100 /* exclusive */ 156184610Salfred 157184610Salfredstatic uint8_t usb2_com_bitmap[(UCOM_UNIT_MAX + 7) / 8]; 158184610Salfred 159184610Salfredstatic uint8_t 160184610Salfredusb2_com_units_alloc(uint32_t sub_units, uint32_t *p_root_unit) 161184610Salfred{ 162184610Salfred uint32_t n; 163184610Salfred uint32_t o; 164184610Salfred uint32_t x; 165184610Salfred uint32_t max = UCOM_UNIT_MAX - (UCOM_UNIT_MAX % sub_units); 166184610Salfred uint8_t error = 1; 167184610Salfred 168184610Salfred mtx_lock(&Giant); 169184610Salfred 170184610Salfred for (n = 0; n < max; n += sub_units) { 171184610Salfred 172184610Salfred /* check for free consecutive bits */ 173184610Salfred 174184610Salfred for (o = 0; o < sub_units; o++) { 175184610Salfred 176184610Salfred x = n + o; 177184610Salfred 178184610Salfred if (usb2_com_bitmap[x / 8] & (1 << (x % 8))) { 179184610Salfred goto skip; 180184610Salfred } 181184610Salfred } 182184610Salfred 183184610Salfred /* allocate */ 184184610Salfred 185184610Salfred for (o = 0; o < sub_units; o++) { 186184610Salfred 187184610Salfred x = n + o; 188184610Salfred 189184610Salfred usb2_com_bitmap[x / 8] |= (1 << (x % 8)); 190184610Salfred } 191184610Salfred 192184610Salfred error = 0; 193184610Salfred 194184610Salfred break; 195184610Salfred 196184610Salfredskip: ; 197184610Salfred } 198184610Salfred 199184610Salfred mtx_unlock(&Giant); 200184610Salfred 201184610Salfred /* 202184610Salfred * Always set the variable pointed to by "p_root_unit" so that 203184610Salfred * the compiler does not think that it is used uninitialised: 204184610Salfred */ 205184610Salfred *p_root_unit = n; 206184610Salfred 207184610Salfred return (error); 208184610Salfred} 209184610Salfred 210184610Salfredstatic void 211184610Salfredusb2_com_units_free(uint32_t root_unit, uint32_t sub_units) 212184610Salfred{ 213184610Salfred uint32_t x; 214184610Salfred 215184610Salfred mtx_lock(&Giant); 216184610Salfred 217184610Salfred while (sub_units--) { 218184610Salfred x = root_unit + sub_units; 219184610Salfred usb2_com_bitmap[x / 8] &= ~(1 << (x % 8)); 220184610Salfred } 221184610Salfred 222184610Salfred mtx_unlock(&Giant); 223184610Salfred 224184610Salfred return; 225184610Salfred} 226184610Salfred 227184610Salfred/* 228184610Salfred * "N" sub_units are setup at a time. All sub-units will 229184610Salfred * be given sequential unit numbers. The number of 230184610Salfred * sub-units can be used to differentiate among 231184610Salfred * different types of devices. 232184610Salfred * 233184610Salfred * The mutex pointed to by "p_mtx" is applied before all 234184610Salfred * callbacks are called back. Also "p_mtx" must be applied 235184610Salfred * before calling into the ucom-layer! Currently only Giant 236184610Salfred * is supported. 237184610Salfred */ 238184610Salfredint 239184610Salfredusb2_com_attach(struct usb2_com_super_softc *ssc, struct usb2_com_softc *sc, 240184610Salfred uint32_t sub_units, void *parent, 241184610Salfred const struct usb2_com_callback *callback, struct mtx *p_mtx) 242184610Salfred{ 243184610Salfred uint32_t n; 244184610Salfred uint32_t root_unit; 245184610Salfred int error = 0; 246184610Salfred 247184610Salfred if ((sc == NULL) || 248184610Salfred (sub_units == 0) || 249184610Salfred (sub_units > UCOM_SUB_UNIT_MAX) || 250184610Salfred (callback == NULL)) { 251184610Salfred return (EINVAL); 252184610Salfred } 253184610Salfred if (usb2_com_units_alloc(sub_units, &root_unit)) { 254184610Salfred return (ENOMEM); 255184610Salfred } 256184610Salfred if (usb2_config_td_setup 257184610Salfred (&ssc->sc_config_td, sc, p_mtx, NULL, 258184610Salfred sizeof(struct usb2_com_config_copy), 24 * sub_units)) { 259184610Salfred usb2_com_units_free(root_unit, sub_units); 260184610Salfred return (ENOMEM); 261184610Salfred } 262184610Salfred for (n = 0; n < sub_units; n++, sc++) { 263184610Salfred sc->sc_unit = root_unit + n; 264184610Salfred sc->sc_local_unit = n; 265184610Salfred sc->sc_super = ssc; 266184610Salfred sc->sc_parent_mtx = p_mtx; 267184610Salfred sc->sc_parent = parent; 268184610Salfred sc->sc_callback = callback; 269184610Salfred 270184610Salfred error = usb2_com_attach_sub(sc); 271184610Salfred if (error) { 272184610Salfred usb2_com_detach(ssc, sc - n, n); 273184610Salfred usb2_com_units_free(root_unit + n, sub_units - n); 274184610Salfred break; 275184610Salfred } 276184610Salfred sc->sc_flag |= UCOM_FLAG_ATTACHED; 277184610Salfred } 278184610Salfred return (error); 279184610Salfred} 280184610Salfred 281184610Salfred/* NOTE: the following function will do nothing if 282184610Salfred * the structure pointed to by "ssc" and "sc" is zero. 283184610Salfred */ 284184610Salfredvoid 285184610Salfredusb2_com_detach(struct usb2_com_super_softc *ssc, struct usb2_com_softc *sc, 286184610Salfred uint32_t sub_units) 287184610Salfred{ 288184610Salfred uint32_t n; 289184610Salfred 290184610Salfred usb2_config_td_drain(&ssc->sc_config_td); 291184610Salfred 292184610Salfred for (n = 0; n < sub_units; n++, sc++) { 293184610Salfred if (sc->sc_flag & UCOM_FLAG_ATTACHED) { 294184610Salfred 295184610Salfred usb2_com_detach_sub(sc); 296184610Salfred 297184610Salfred usb2_com_units_free(sc->sc_unit, 1); 298184610Salfred 299184610Salfred /* avoid duplicate detach: */ 300184610Salfred sc->sc_flag &= ~UCOM_FLAG_ATTACHED; 301184610Salfred } 302184610Salfred } 303184610Salfred 304184610Salfred usb2_config_td_unsetup(&ssc->sc_config_td); 305184610Salfred 306184610Salfred return; 307184610Salfred} 308184610Salfred 309184610Salfredstatic int 310184610Salfredusb2_com_attach_sub(struct usb2_com_softc *sc) 311184610Salfred{ 312184610Salfred struct tty *tp; 313184610Salfred int error = 0; 314184610Salfred char buf[32]; /* temporary TTY device name buffer */ 315184610Salfred 316184610Salfred tp = tty_alloc(&usb2_com_class, sc, sc->sc_parent_mtx); 317184610Salfred if (tp == NULL) { 318184610Salfred error = ENOMEM; 319184610Salfred goto done; 320184610Salfred } 321184610Salfred DPRINTF("tp = %p, unit = %d\n", tp, sc->sc_unit); 322184610Salfred 323184610Salfred buf[0] = 0; /* set some default value */ 324184610Salfred 325184610Salfred /* Check if the client has a custom TTY name */ 326184610Salfred if (sc->sc_callback->usb2_com_tty_name) { 327184610Salfred sc->sc_callback->usb2_com_tty_name(sc, buf, 328184610Salfred sizeof(buf), sc->sc_local_unit); 329184610Salfred } 330184610Salfred if (buf[0] == 0) { 331184610Salfred /* Use default TTY name */ 332184610Salfred if (snprintf(buf, sizeof(buf), "U%u", sc->sc_unit)) { 333184610Salfred /* ignore */ 334184610Salfred } 335184610Salfred } 336184610Salfred tty_makedev(tp, NULL, "%s", buf); 337184610Salfred 338184610Salfred sc->sc_tty = tp; 339184610Salfred 340184610Salfred DPRINTF("ttycreate: %s\n", buf); 341184610Salfred usb2_cv_init(&sc->sc_cv, "usb2_com"); 342184610Salfred 343184610Salfreddone: 344184610Salfred return (error); 345184610Salfred} 346184610Salfred 347184610Salfredstatic void 348184610Salfredusb2_com_detach_sub(struct usb2_com_softc *sc) 349184610Salfred{ 350184610Salfred struct tty *tp = sc->sc_tty; 351184610Salfred 352184610Salfred DPRINTF("sc = %p, tp = %p\n", sc, sc->sc_tty); 353184610Salfred 354184610Salfred /* the config thread has been stopped when we get here */ 355184610Salfred 356184610Salfred mtx_lock(sc->sc_parent_mtx); 357184610Salfred sc->sc_flag |= UCOM_FLAG_GONE; 358184610Salfred sc->sc_flag &= ~(UCOM_FLAG_HL_READY | 359184610Salfred UCOM_FLAG_LL_READY); 360184610Salfred mtx_unlock(sc->sc_parent_mtx); 361184610Salfred if (tp) { 362184610Salfred tty_lock(tp); 363184610Salfred 364184610Salfred usb2_com_close(tp); /* close, if any */ 365184610Salfred 366184610Salfred tty_rel_gone(tp); 367184610Salfred 368184610Salfred mtx_lock(sc->sc_parent_mtx); 369184610Salfred /* Wait for the callback after the TTY is torn down */ 370184610Salfred while (sc->sc_ttyfreed == 0) 371184610Salfred usb2_cv_wait(&sc->sc_cv, sc->sc_parent_mtx); 372184610Salfred /* 373184610Salfred * make sure that read and write transfers are stopped 374184610Salfred */ 375184610Salfred if (sc->sc_callback->usb2_com_stop_read) { 376184610Salfred (sc->sc_callback->usb2_com_stop_read) (sc); 377184610Salfred } 378184610Salfred if (sc->sc_callback->usb2_com_stop_write) { 379184610Salfred (sc->sc_callback->usb2_com_stop_write) (sc); 380184610Salfred } 381184610Salfred mtx_unlock(sc->sc_parent_mtx); 382184610Salfred } 383184610Salfred usb2_cv_destroy(&sc->sc_cv); 384184610Salfred return; 385184610Salfred} 386184610Salfred 387184610Salfredstatic void 388184610Salfredusb2_com_config_copy(struct usb2_com_softc *sc, struct usb2_com_config_copy *cc, 389184610Salfred uint16_t refcount) 390184610Salfred{ 391184610Salfred cc->cc_softc = sc + (refcount % UCOM_SUB_UNIT_MAX); 392184610Salfred cc->cc_flag0 = (refcount / (1 * UCOM_SUB_UNIT_MAX)) % 2; 393184610Salfred cc->cc_flag1 = (refcount / (2 * UCOM_SUB_UNIT_MAX)) % 2; 394184610Salfred cc->cc_flag2 = (refcount / (4 * UCOM_SUB_UNIT_MAX)) % 2; 395184610Salfred cc->cc_flag3 = (refcount / (8 * UCOM_SUB_UNIT_MAX)) % 2; 396184610Salfred return; 397184610Salfred} 398184610Salfred 399184610Salfredstatic void 400184610Salfredusb2_com_queue_command(struct usb2_com_softc *sc, usb2_config_td_command_t *cmd, int flag) 401184610Salfred{ 402184610Salfred struct usb2_com_super_softc *ssc = sc->sc_super; 403184610Salfred 404184610Salfred usb2_config_td_queue_command 405184610Salfred (&ssc->sc_config_td, &usb2_com_config_copy, 406184610Salfred cmd, (cmd == &usb2_com_cfg_status_change) ? 1 : 0, 407184610Salfred ((sc->sc_local_unit % UCOM_SUB_UNIT_MAX) + 408184610Salfred (flag ? UCOM_SUB_UNIT_MAX : 0))); 409184610Salfred return; 410184610Salfred} 411184610Salfred 412184610Salfredstatic void 413184610Salfredusb2_com_shutdown(struct usb2_com_softc *sc) 414184610Salfred{ 415184610Salfred struct tty *tp = sc->sc_tty; 416184610Salfred 417184610Salfred mtx_assert(sc->sc_parent_mtx, MA_OWNED); 418184610Salfred 419184610Salfred DPRINTF("\n"); 420184610Salfred 421184610Salfred /* 422184610Salfred * Hang up if necessary: 423184610Salfred */ 424184610Salfred if (tp->t_termios.c_cflag & HUPCL) { 425184610Salfred usb2_com_modem(tp, 0, SER_DTR); 426184610Salfred } 427184610Salfred return; 428184610Salfred} 429184610Salfred 430184610Salfred/* 431184610Salfred * Return values: 432184610Salfred * 0: normal delay 433184610Salfred * else: config thread is gone 434184610Salfred */ 435184610Salfreduint8_t 436184610Salfredusb2_com_cfg_sleep(struct usb2_com_softc *sc, uint32_t timeout) 437184610Salfred{ 438184610Salfred struct usb2_com_super_softc *ssc = sc->sc_super; 439184610Salfred 440184610Salfred return (usb2_config_td_sleep(&ssc->sc_config_td, timeout)); 441184610Salfred} 442184610Salfred 443184610Salfred/* 444184610Salfred * Return values: 445184610Salfred * 0: normal 446184610Salfred * else: config thread is gone 447184610Salfred */ 448184610Salfreduint8_t 449184610Salfredusb2_com_cfg_is_gone(struct usb2_com_softc *sc) 450184610Salfred{ 451184610Salfred struct usb2_com_super_softc *ssc = sc->sc_super; 452184610Salfred 453184610Salfred return (usb2_config_td_is_gone(&ssc->sc_config_td)); 454184610Salfred} 455184610Salfred 456184610Salfredstatic void 457184610Salfredusb2_com_cfg_start_transfers(struct usb2_com_softc *sc, struct usb2_com_config_copy *cc, 458184610Salfred uint16_t refcount) 459184610Salfred{ 460184610Salfred sc = cc->cc_softc; 461184610Salfred 462184610Salfred if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) { 463184610Salfred return; 464184610Salfred } 465184610Salfred if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) { 466184610Salfred /* TTY device closed */ 467184610Salfred return; 468184610Salfred } 469184610Salfred sc->sc_flag |= UCOM_FLAG_GP_DATA; 470184610Salfred 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 return; 478184610Salfred} 479184610Salfred 480184610Salfredstatic void 481184610Salfredusb2_com_start_transfers(struct usb2_com_softc *sc) 482184610Salfred{ 483184610Salfred if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) { 484184610Salfred return; 485184610Salfred } 486184610Salfred /* 487184610Salfred * do a direct call first, to get hardware buffers flushed 488184610Salfred */ 489184610Salfred 490184610Salfred if (sc->sc_callback->usb2_com_start_read) { 491184610Salfred (sc->sc_callback->usb2_com_start_read) (sc); 492184610Salfred } 493184610Salfred if (sc->sc_callback->usb2_com_start_write) { 494184610Salfred (sc->sc_callback->usb2_com_start_write) (sc); 495184610Salfred } 496184610Salfred if (!(sc->sc_flag & UCOM_FLAG_GP_DATA)) { 497184610Salfred usb2_com_queue_command(sc, &usb2_com_cfg_start_transfers, 0); 498184610Salfred } 499184610Salfred return; 500184610Salfred} 501184610Salfred 502184610Salfredstatic void 503184610Salfredusb2_com_cfg_open(struct usb2_com_softc *sc, struct usb2_com_config_copy *cc, 504184610Salfred uint16_t refcount) 505184610Salfred{ 506184610Salfred sc = cc->cc_softc; 507184610Salfred 508184610Salfred DPRINTF("\n"); 509184610Salfred 510184610Salfred if (sc->sc_flag & UCOM_FLAG_LL_READY) { 511184610Salfred 512184610Salfred /* already opened */ 513184610Salfred 514184610Salfred } else { 515184610Salfred 516184610Salfred sc->sc_flag |= UCOM_FLAG_LL_READY; 517184610Salfred 518184610Salfred if (sc->sc_callback->usb2_com_cfg_open) { 519184610Salfred (sc->sc_callback->usb2_com_cfg_open) (sc); 520184610Salfred 521184610Salfred /* wait a little */ 522184610Salfred usb2_com_cfg_sleep(sc, hz / 10); 523184610Salfred } 524184610Salfred } 525184610Salfred return; 526184610Salfred} 527184610Salfred 528184610Salfredstatic int 529184610Salfredusb2_com_open(struct tty *tp) 530184610Salfred{ 531184610Salfred struct usb2_com_softc *sc = tty_softc(tp); 532184610Salfred int error; 533184610Salfred 534184610Salfred mtx_assert(sc->sc_parent_mtx, MA_OWNED); 535184610Salfred 536184610Salfred if (sc->sc_flag & UCOM_FLAG_GONE) { 537184610Salfred return (ENXIO); 538184610Salfred } 539184610Salfred if (sc->sc_flag & UCOM_FLAG_HL_READY) { 540184610Salfred /* already opened */ 541184610Salfred return (0); 542184610Salfred } 543184610Salfred DPRINTF("tp = %p\n", tp); 544184610Salfred 545184610Salfred if (sc->sc_callback->usb2_com_pre_open) { 546184610Salfred /* 547184610Salfred * give the lower layer a chance to disallow TTY open, for 548184610Salfred * example if the device is not present: 549184610Salfred */ 550184610Salfred error = (sc->sc_callback->usb2_com_pre_open) (sc); 551184610Salfred if (error) { 552184610Salfred return (error); 553184610Salfred } 554184610Salfred } 555184610Salfred sc->sc_flag |= UCOM_FLAG_HL_READY; 556184610Salfred 557184610Salfred /* Disable transfers */ 558184610Salfred sc->sc_flag &= ~UCOM_FLAG_GP_DATA; 559184610Salfred 560184610Salfred sc->sc_lsr = 0; 561184610Salfred sc->sc_msr = 0; 562184610Salfred sc->sc_mcr = 0; 563184610Salfred 564184610Salfred usb2_com_queue_command(sc, &usb2_com_cfg_open, 0); 565184610Salfred 566184610Salfred usb2_com_start_transfers(sc); 567184610Salfred 568184610Salfred usb2_com_modem(tp, SER_DTR | SER_RTS, 0); 569184610Salfred 570184610Salfred usb2_com_break(sc, 0); 571184610Salfred 572184610Salfred usb2_com_status_change(sc); 573184610Salfred 574184610Salfred return (0); 575184610Salfred} 576184610Salfred 577184610Salfredstatic void 578184610Salfredusb2_com_cfg_close(struct usb2_com_softc *sc, struct usb2_com_config_copy *cc, 579184610Salfred uint16_t refcount) 580184610Salfred{ 581184610Salfred sc = cc->cc_softc; 582184610Salfred 583184610Salfred DPRINTF("\n"); 584184610Salfred 585184610Salfred if (sc->sc_flag & UCOM_FLAG_LL_READY) { 586184610Salfred 587184610Salfred sc->sc_flag &= ~(UCOM_FLAG_LL_READY | 588184610Salfred UCOM_FLAG_GP_DATA); 589184610Salfred 590184610Salfred if (sc->sc_callback->usb2_com_cfg_close) { 591184610Salfred (sc->sc_callback->usb2_com_cfg_close) (sc); 592184610Salfred } 593184610Salfred } else { 594184610Salfred /* already closed */ 595184610Salfred } 596184610Salfred return; 597184610Salfred} 598184610Salfred 599184610Salfredstatic void 600184610Salfredusb2_com_close(struct tty *tp) 601184610Salfred{ 602184610Salfred struct usb2_com_softc *sc = tty_softc(tp); 603184610Salfred 604184610Salfred mtx_assert(sc->sc_parent_mtx, MA_OWNED); 605184610Salfred 606184610Salfred DPRINTF("tp=%p\n", tp); 607184610Salfred 608184610Salfred if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) { 609184610Salfred DPRINTF("tp=%p already closed\n", tp); 610184610Salfred return; 611184610Salfred } 612184610Salfred usb2_com_shutdown(sc); 613184610Salfred 614184610Salfred usb2_com_queue_command(sc, &usb2_com_cfg_close, 0); 615184610Salfred 616184610Salfred sc->sc_flag &= ~(UCOM_FLAG_HL_READY | 617184610Salfred UCOM_FLAG_WR_START | 618184610Salfred UCOM_FLAG_RTS_IFLOW); 619184610Salfred 620184610Salfred if (sc->sc_callback->usb2_com_stop_read) { 621184610Salfred (sc->sc_callback->usb2_com_stop_read) (sc); 622184610Salfred } 623184610Salfred if (sc->sc_callback->usb2_com_stop_write) { 624184610Salfred (sc->sc_callback->usb2_com_stop_write) (sc); 625184610Salfred } 626184610Salfred return; 627184610Salfred} 628184610Salfred 629184610Salfredstatic int 630184610Salfredusb2_com_ioctl(struct tty *tp, u_long cmd, caddr_t data, struct thread *td) 631184610Salfred{ 632184610Salfred struct usb2_com_softc *sc = tty_softc(tp); 633184610Salfred int error; 634184610Salfred 635184610Salfred mtx_assert(sc->sc_parent_mtx, MA_OWNED); 636184610Salfred 637184610Salfred if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) { 638184610Salfred return (EIO); 639184610Salfred } 640184610Salfred DPRINTF("cmd = 0x%08lx\n", cmd); 641184610Salfred 642184610Salfred switch (cmd) { 643184610Salfred case TIOCSBRK: 644184610Salfred usb2_com_break(sc, 1); 645184610Salfred error = 0; 646184610Salfred break; 647184610Salfred case TIOCCBRK: 648184610Salfred usb2_com_break(sc, 0); 649184610Salfred error = 0; 650184610Salfred break; 651184610Salfred default: 652184610Salfred if (sc->sc_callback->usb2_com_ioctl) { 653184610Salfred error = (sc->sc_callback->usb2_com_ioctl) 654184610Salfred (sc, cmd, data, 0, td); 655184610Salfred } else { 656184610Salfred error = ENOIOCTL; 657184610Salfred } 658184610Salfred break; 659184610Salfred } 660184610Salfred return (error); 661184610Salfred} 662184610Salfred 663184610Salfredstatic int 664184610Salfredusb2_com_modem(struct tty *tp, int sigon, int sigoff) 665184610Salfred{ 666184610Salfred struct usb2_com_softc *sc = tty_softc(tp); 667184610Salfred uint8_t onoff; 668184610Salfred 669184610Salfred mtx_assert(sc->sc_parent_mtx, MA_OWNED); 670184610Salfred 671184610Salfred if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) { 672184610Salfred return (0); 673184610Salfred } 674184610Salfred if ((sigon == 0) && (sigoff == 0)) { 675184610Salfred 676184610Salfred if (sc->sc_mcr & SER_DTR) { 677184610Salfred sigon |= SER_DTR; 678184610Salfred } 679184610Salfred if (sc->sc_mcr & SER_RTS) { 680184610Salfred sigon |= SER_RTS; 681184610Salfred } 682184610Salfred if (sc->sc_msr & SER_CTS) { 683184610Salfred sigon |= SER_CTS; 684184610Salfred } 685184610Salfred if (sc->sc_msr & SER_DCD) { 686184610Salfred sigon |= SER_DCD; 687184610Salfred } 688184610Salfred if (sc->sc_msr & SER_DSR) { 689184610Salfred sigon |= SER_DSR; 690184610Salfred } 691184610Salfred if (sc->sc_msr & SER_RI) { 692184610Salfred sigon |= SER_RI; 693184610Salfred } 694184610Salfred return (sigon); 695184610Salfred } 696184610Salfred if (sigon & SER_DTR) { 697184610Salfred sc->sc_mcr |= SER_DTR; 698184610Salfred } 699184610Salfred if (sigoff & SER_DTR) { 700184610Salfred sc->sc_mcr &= ~SER_DTR; 701184610Salfred } 702184610Salfred if (sigon & SER_RTS) { 703184610Salfred sc->sc_mcr |= SER_RTS; 704184610Salfred } 705184610Salfred if (sigoff & SER_RTS) { 706184610Salfred sc->sc_mcr &= ~SER_RTS; 707184610Salfred } 708184610Salfred onoff = (sc->sc_mcr & SER_DTR) ? 1 : 0; 709184610Salfred usb2_com_dtr(sc, onoff); 710184610Salfred 711184610Salfred onoff = (sc->sc_mcr & SER_RTS) ? 1 : 0; 712184610Salfred usb2_com_rts(sc, onoff); 713184610Salfred 714184610Salfred return (0); 715184610Salfred} 716184610Salfred 717184610Salfredstatic void 718184610Salfredusb2_com_cfg_break(struct usb2_com_softc *sc, struct usb2_com_config_copy *cc, 719184610Salfred uint16_t refcount) 720184610Salfred{ 721184610Salfred sc = cc->cc_softc; 722184610Salfred 723184610Salfred if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) { 724184610Salfred return; 725184610Salfred } 726184610Salfred DPRINTF("onoff=%d\n", cc->cc_flag0); 727184610Salfred 728184610Salfred if (sc->sc_callback->usb2_com_cfg_set_break) { 729184610Salfred (sc->sc_callback->usb2_com_cfg_set_break) (sc, cc->cc_flag0); 730184610Salfred } 731184610Salfred return; 732184610Salfred} 733184610Salfred 734184610Salfredstatic void 735184610Salfredusb2_com_break(struct usb2_com_softc *sc, uint8_t onoff) 736184610Salfred{ 737184610Salfred mtx_assert(sc->sc_parent_mtx, MA_OWNED); 738184610Salfred 739184610Salfred if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) { 740184610Salfred return; 741184610Salfred } 742184610Salfred DPRINTF("onoff = %d\n", onoff); 743184610Salfred 744184610Salfred usb2_com_queue_command(sc, &usb2_com_cfg_break, onoff); 745184610Salfred return; 746184610Salfred} 747184610Salfred 748184610Salfredstatic void 749184610Salfredusb2_com_cfg_dtr(struct usb2_com_softc *sc, struct usb2_com_config_copy *cc, 750184610Salfred uint16_t refcount) 751184610Salfred{ 752184610Salfred sc = cc->cc_softc; 753184610Salfred 754184610Salfred if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) { 755184610Salfred return; 756184610Salfred } 757184610Salfred DPRINTF("onoff=%d\n", cc->cc_flag0); 758184610Salfred 759184610Salfred if (sc->sc_callback->usb2_com_cfg_set_dtr) { 760184610Salfred (sc->sc_callback->usb2_com_cfg_set_dtr) (sc, cc->cc_flag0); 761184610Salfred } 762184610Salfred return; 763184610Salfred} 764184610Salfred 765184610Salfredstatic void 766184610Salfredusb2_com_dtr(struct usb2_com_softc *sc, uint8_t onoff) 767184610Salfred{ 768184610Salfred mtx_assert(sc->sc_parent_mtx, MA_OWNED); 769184610Salfred 770184610Salfred if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) { 771184610Salfred return; 772184610Salfred } 773184610Salfred DPRINTF("onoff = %d\n", onoff); 774184610Salfred 775184610Salfred usb2_com_queue_command(sc, &usb2_com_cfg_dtr, onoff); 776184610Salfred return; 777184610Salfred} 778184610Salfred 779184610Salfredstatic void 780184610Salfredusb2_com_cfg_rts(struct usb2_com_softc *sc, struct usb2_com_config_copy *cc, 781184610Salfred uint16_t refcount) 782184610Salfred{ 783184610Salfred sc = cc->cc_softc; 784184610Salfred 785184610Salfred DPRINTF("onoff=%d\n", cc->cc_flag0); 786184610Salfred 787184610Salfred if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) { 788184610Salfred return; 789184610Salfred } 790184610Salfred if (sc->sc_callback->usb2_com_cfg_set_rts) { 791184610Salfred (sc->sc_callback->usb2_com_cfg_set_rts) (sc, cc->cc_flag0); 792184610Salfred } 793184610Salfred return; 794184610Salfred} 795184610Salfred 796184610Salfredstatic void 797184610Salfredusb2_com_rts(struct usb2_com_softc *sc, uint8_t onoff) 798184610Salfred{ 799184610Salfred mtx_assert(sc->sc_parent_mtx, MA_OWNED); 800184610Salfred 801184610Salfred if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) { 802184610Salfred return; 803184610Salfred } 804184610Salfred DPRINTF("onoff = %d\n", onoff); 805184610Salfred 806184610Salfred usb2_com_queue_command(sc, &usb2_com_cfg_rts, onoff); 807184610Salfred 808184610Salfred return; 809184610Salfred} 810184610Salfred 811184610Salfredstatic void 812184610Salfredusb2_com_cfg_status_change(struct usb2_com_softc *sc, 813184610Salfred struct usb2_com_config_copy *cc, uint16_t refcount) 814184610Salfred{ 815184610Salfred struct tty *tp; 816184610Salfred 817184610Salfred uint8_t new_msr; 818184610Salfred uint8_t new_lsr; 819184610Salfred uint8_t onoff; 820184610Salfred 821184610Salfred sc = cc->cc_softc; 822184610Salfred tp = sc->sc_tty; 823184610Salfred 824184610Salfred mtx_assert(sc->sc_parent_mtx, MA_OWNED); 825184610Salfred 826184610Salfred if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) { 827184610Salfred return; 828184610Salfred } 829184610Salfred if (sc->sc_callback->usb2_com_cfg_get_status == NULL) { 830184610Salfred return; 831184610Salfred } 832184610Salfred /* get status */ 833184610Salfred 834184610Salfred new_msr = 0; 835184610Salfred new_lsr = 0; 836184610Salfred 837184610Salfred (sc->sc_callback->usb2_com_cfg_get_status) (sc, &new_lsr, &new_msr); 838184610Salfred 839184610Salfred if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) { 840184610Salfred /* TTY device closed */ 841184610Salfred return; 842184610Salfred } 843184610Salfred onoff = ((sc->sc_msr ^ new_msr) & SER_DCD); 844184610Salfred 845184610Salfred sc->sc_msr = new_msr; 846184610Salfred sc->sc_lsr = new_lsr; 847184610Salfred 848184610Salfred if (onoff) { 849184610Salfred 850184610Salfred onoff = (sc->sc_msr & SER_DCD) ? 1 : 0; 851184610Salfred 852184610Salfred DPRINTF("DCD changed to %d\n", onoff); 853184610Salfred 854184610Salfred ttydisc_modem(tp, onoff); 855184610Salfred } 856184610Salfred return; 857184610Salfred} 858184610Salfred 859184610Salfredvoid 860184610Salfredusb2_com_status_change(struct usb2_com_softc *sc) 861184610Salfred{ 862184610Salfred mtx_assert(sc->sc_parent_mtx, MA_OWNED); 863184610Salfred 864184610Salfred if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) { 865184610Salfred return; 866184610Salfred } 867184610Salfred DPRINTF("\n"); 868184610Salfred 869184610Salfred usb2_com_queue_command(sc, &usb2_com_cfg_status_change, 0); 870184610Salfred return; 871184610Salfred} 872184610Salfred 873184610Salfredstatic void 874184610Salfredusb2_com_cfg_param(struct usb2_com_softc *sc, struct usb2_com_config_copy *cc, 875184610Salfred uint16_t refcount) 876184610Salfred{ 877184610Salfred struct termios t_copy; 878184610Salfred 879184610Salfred sc = cc->cc_softc; 880184610Salfred 881184610Salfred if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) { 882184610Salfred return; 883184610Salfred } 884184610Salfred if (sc->sc_callback->usb2_com_cfg_param == NULL) { 885184610Salfred return; 886184610Salfred } 887184610Salfred t_copy = sc->sc_termios_copy; 888184610Salfred 889184610Salfred (sc->sc_callback->usb2_com_cfg_param) (sc, &t_copy); 890184610Salfred 891184610Salfred /* wait a little */ 892184610Salfred usb2_com_cfg_sleep(sc, hz / 10); 893184610Salfred 894184610Salfred return; 895184610Salfred} 896184610Salfred 897184610Salfredstatic int 898184610Salfredusb2_com_param(struct tty *tp, struct termios *t) 899184610Salfred{ 900184610Salfred struct usb2_com_softc *sc = tty_softc(tp); 901184610Salfred uint8_t opened; 902184610Salfred int error; 903184610Salfred 904184610Salfred mtx_assert(sc->sc_parent_mtx, MA_OWNED); 905184610Salfred 906184610Salfred opened = 0; 907184610Salfred error = 0; 908184610Salfred 909184610Salfred if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) { 910184610Salfred 911184610Salfred /* XXX the TTY layer should call "open()" first! */ 912184610Salfred 913184610Salfred error = usb2_com_open(tp); 914184610Salfred if (error) { 915184610Salfred goto done; 916184610Salfred } 917184610Salfred opened = 1; 918184610Salfred } 919184610Salfred DPRINTF("sc = %p\n", sc); 920184610Salfred 921184610Salfred /* Check requested parameters. */ 922184610Salfred if (t->c_ospeed < 0) { 923184610Salfred DPRINTF("negative ospeed\n"); 924184610Salfred error = EINVAL; 925184610Salfred goto done; 926184610Salfred } 927184610Salfred if (t->c_ispeed && (t->c_ispeed != t->c_ospeed)) { 928184610Salfred DPRINTF("mismatch ispeed and ospeed\n"); 929184610Salfred error = EINVAL; 930184610Salfred goto done; 931184610Salfred } 932184610Salfred t->c_ispeed = t->c_ospeed; 933184610Salfred 934184610Salfred if (sc->sc_callback->usb2_com_pre_param) { 935184610Salfred /* Let the lower layer verify the parameters */ 936184610Salfred error = (sc->sc_callback->usb2_com_pre_param) (sc, t); 937184610Salfred if (error) { 938184610Salfred DPRINTF("callback error = %d\n", error); 939184610Salfred goto done; 940184610Salfred } 941184610Salfred } 942184610Salfred /* Make a copy of the termios parameters */ 943184610Salfred sc->sc_termios_copy = *t; 944184610Salfred 945184610Salfred /* Disable transfers */ 946184610Salfred sc->sc_flag &= ~UCOM_FLAG_GP_DATA; 947184610Salfred 948184610Salfred /* Queue baud rate programming command first */ 949184610Salfred usb2_com_queue_command(sc, &usb2_com_cfg_param, 0); 950184610Salfred 951184610Salfred /* Queue transfer enable command last */ 952184610Salfred usb2_com_start_transfers(sc); 953184610Salfred 954184610Salfred if (t->c_cflag & CRTS_IFLOW) { 955184610Salfred sc->sc_flag |= UCOM_FLAG_RTS_IFLOW; 956184610Salfred } else if (sc->sc_flag & UCOM_FLAG_RTS_IFLOW) { 957184610Salfred sc->sc_flag &= ~UCOM_FLAG_RTS_IFLOW; 958184610Salfred usb2_com_modem(tp, SER_RTS, 0); 959184610Salfred } 960184610Salfreddone: 961184610Salfred if (error) { 962184610Salfred if (opened) { 963184610Salfred usb2_com_close(tp); 964184610Salfred } 965184610Salfred } 966184610Salfred return (error); 967184610Salfred} 968184610Salfred 969184610Salfredstatic void 970184610Salfredusb2_com_start_write(struct tty *tp) 971184610Salfred{ 972184610Salfred struct usb2_com_softc *sc = tty_softc(tp); 973184610Salfred 974184610Salfred mtx_assert(sc->sc_parent_mtx, MA_OWNED); 975184610Salfred 976184610Salfred DPRINTF("sc = %p\n", sc); 977184610Salfred 978184610Salfred if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) { 979184610Salfred /* The higher layer is not ready */ 980184610Salfred return; 981184610Salfred } 982184610Salfred sc->sc_flag |= UCOM_FLAG_WR_START; 983184610Salfred 984184610Salfred usb2_com_start_transfers(sc); 985184610Salfred 986184610Salfred return; 987184610Salfred} 988184610Salfred 989184610Salfred/*------------------------------------------------------------------------* 990184610Salfred * usb2_com_get_data 991184610Salfred * 992184610Salfred * Return values: 993184610Salfred * 0: No data is available. 994184610Salfred * Else: Data is available. 995184610Salfred *------------------------------------------------------------------------*/ 996184610Salfreduint8_t 997184610Salfredusb2_com_get_data(struct usb2_com_softc *sc, struct usb2_page_cache *pc, 998184610Salfred uint32_t offset, uint32_t len, uint32_t *actlen) 999184610Salfred{ 1000184610Salfred struct usb2_page_search res; 1001184610Salfred struct tty *tp = sc->sc_tty; 1002184610Salfred uint32_t cnt; 1003184610Salfred uint32_t offset_orig; 1004184610Salfred 1005184610Salfred mtx_assert(sc->sc_parent_mtx, MA_OWNED); 1006184610Salfred 1007184610Salfred if ((!(sc->sc_flag & UCOM_FLAG_HL_READY)) || 1008184610Salfred (!(sc->sc_flag & UCOM_FLAG_GP_DATA)) || 1009184610Salfred (!(sc->sc_flag & UCOM_FLAG_WR_START))) { 1010184610Salfred actlen[0] = 0; 1011184610Salfred return (0); /* multiport device polling */ 1012184610Salfred } 1013184610Salfred offset_orig = offset; 1014184610Salfred 1015184610Salfred while (len != 0) { 1016184610Salfred 1017184610Salfred usb2_get_page(pc, offset, &res); 1018184610Salfred 1019184610Salfred if (res.length > len) { 1020184610Salfred res.length = len; 1021184610Salfred } 1022184610Salfred /* copy data directly into USB buffer */ 1023184610Salfred cnt = ttydisc_getc(tp, res.buffer, res.length); 1024184610Salfred 1025184610Salfred offset += cnt; 1026184610Salfred len -= cnt; 1027184610Salfred 1028184610Salfred if (cnt < res.length) { 1029184610Salfred /* end of buffer */ 1030184610Salfred break; 1031184610Salfred } 1032184610Salfred } 1033184610Salfred 1034184610Salfred actlen[0] = offset - offset_orig; 1035184610Salfred 1036184610Salfred DPRINTF("cnt=%d\n", actlen[0]); 1037184610Salfred 1038184610Salfred if (actlen[0] == 0) { 1039184610Salfred return (0); 1040184610Salfred } 1041184610Salfred return (1); 1042184610Salfred} 1043184610Salfred 1044184610Salfredvoid 1045184610Salfredusb2_com_put_data(struct usb2_com_softc *sc, struct usb2_page_cache *pc, 1046184610Salfred uint32_t offset, uint32_t len) 1047184610Salfred{ 1048184610Salfred struct usb2_page_search res; 1049184610Salfred struct tty *tp = sc->sc_tty; 1050184610Salfred char *buf; 1051184610Salfred uint32_t cnt; 1052184610Salfred 1053184610Salfred mtx_assert(sc->sc_parent_mtx, MA_OWNED); 1054184610Salfred 1055184610Salfred if ((!(sc->sc_flag & UCOM_FLAG_HL_READY)) || 1056184610Salfred (!(sc->sc_flag & UCOM_FLAG_GP_DATA))) { 1057184610Salfred return; /* multiport device polling */ 1058184610Salfred } 1059184610Salfred if (len == 0) 1060184610Salfred return; /* no data */ 1061184610Salfred 1062184610Salfred /* set a flag to prevent recursation ? */ 1063184610Salfred 1064184610Salfred while (len > 0) { 1065184610Salfred 1066184610Salfred usb2_get_page(pc, offset, &res); 1067184610Salfred 1068184610Salfred if (res.length > len) { 1069184610Salfred res.length = len; 1070184610Salfred } 1071184610Salfred len -= res.length; 1072184610Salfred offset += res.length; 1073184610Salfred 1074184610Salfred /* pass characters to tty layer */ 1075184610Salfred 1076184610Salfred buf = res.buffer; 1077184610Salfred cnt = res.length; 1078184610Salfred 1079184610Salfred /* first check if we can pass the buffer directly */ 1080184610Salfred 1081184610Salfred if (ttydisc_can_bypass(tp)) { 1082184610Salfred if (ttydisc_rint_bypass(tp, buf, cnt) != cnt) { 1083184610Salfred DPRINTF("tp=%p, data lost\n", tp); 1084184610Salfred } 1085184610Salfred continue; 1086184610Salfred } 1087184610Salfred /* need to loop */ 1088184610Salfred 1089184610Salfred for (cnt = 0; cnt != res.length; cnt++) { 1090184610Salfred if (ttydisc_rint(tp, buf[cnt], 0) == -1) { 1091184610Salfred /* XXX what should we do? */ 1092184610Salfred 1093184610Salfred DPRINTF("tp=%p, lost %d " 1094184610Salfred "chars\n", tp, res.length - cnt); 1095184610Salfred break; 1096184610Salfred } 1097184610Salfred } 1098184610Salfred } 1099184610Salfred ttydisc_rint_done(tp); 1100184610Salfred return; 1101184610Salfred} 1102184610Salfred 1103184610Salfredstatic void 1104184610Salfredusb2_com_free(void *xsc) 1105184610Salfred{ 1106184610Salfred struct usb2_com_softc *sc = xsc; 1107184610Salfred 1108184610Salfred mtx_lock(sc->sc_parent_mtx); 1109184610Salfred sc->sc_ttyfreed = 1; 1110184610Salfred usb2_cv_signal(&sc->sc_cv); 1111184610Salfred mtx_unlock(sc->sc_parent_mtx); 1112184610Salfred} 1113