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$"); 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 * 50184610Salfred * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 51184610Salfred * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 52184610Salfred * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 53184610Salfred * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 54184610Salfred * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 55184610Salfred * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 56184610Salfred * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 57184610Salfred * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 58184610Salfred * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 59184610Salfred * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 60184610Salfred * POSSIBILITY OF SUCH DAMAGE. 61184610Salfred */ 62184610Salfred 63194677Sthompsa#include <sys/stdint.h> 64194677Sthompsa#include <sys/stddef.h> 65194677Sthompsa#include <sys/param.h> 66194677Sthompsa#include <sys/queue.h> 67194677Sthompsa#include <sys/types.h> 68194677Sthompsa#include <sys/systm.h> 69194677Sthompsa#include <sys/kernel.h> 70194677Sthompsa#include <sys/bus.h> 71194677Sthompsa#include <sys/module.h> 72194677Sthompsa#include <sys/lock.h> 73194677Sthompsa#include <sys/mutex.h> 74194677Sthompsa#include <sys/condvar.h> 75194677Sthompsa#include <sys/sysctl.h> 76194677Sthompsa#include <sys/sx.h> 77194677Sthompsa#include <sys/unistd.h> 78194677Sthompsa#include <sys/callout.h> 79194677Sthompsa#include <sys/malloc.h> 80194677Sthompsa#include <sys/priv.h> 81197570Sthompsa#include <sys/cons.h> 82197570Sthompsa#include <sys/kdb.h> 83194677Sthompsa 84294637Sian#include <dev/uart/uart_ppstypes.h> 85294637Sian 86188942Sthompsa#include <dev/usb/usb.h> 87194677Sthompsa#include <dev/usb/usbdi.h> 88194677Sthompsa#include <dev/usb/usbdi_util.h> 89184610Salfred 90194228Sthompsa#define USB_DEBUG_VAR ucom_debug 91188942Sthompsa#include <dev/usb/usb_debug.h> 92194677Sthompsa#include <dev/usb/usb_busdma.h> 93188942Sthompsa#include <dev/usb/usb_process.h> 94184610Salfred 95188942Sthompsa#include <dev/usb/serial/usb_serial.h> 96184610Salfred 97197570Sthompsa#include "opt_gdb.h" 98197570Sthompsa 99227309Sedstatic SYSCTL_NODE(_hw_usb, OID_AUTO, ucom, CTLFLAG_RW, 0, "USB ucom"); 100197570Sthompsa 101283341Sianstatic int ucom_pps_mode; 102283341Sian 103283341SianSYSCTL_INT(_hw_usb_ucom, OID_AUTO, pps_mode, CTLFLAG_RWTUN, 104294637Sian &ucom_pps_mode, 0, 105294637Sian "pulse capture mode: 0/1/2=disabled/CTS/DCD; add 0x10 to invert"); 106283341Sian 107207077Sthompsa#ifdef USB_DEBUG 108194228Sthompsastatic int ucom_debug = 0; 109184610Salfred 110192502SthompsaSYSCTL_INT(_hw_usb_ucom, OID_AUTO, debug, CTLFLAG_RW, 111194228Sthompsa &ucom_debug, 0, "ucom debug level"); 112184610Salfred#endif 113184610Salfred 114197570Sthompsa#define UCOM_CONS_BUFSIZE 1024 115197570Sthompsa 116197570Sthompsastatic uint8_t ucom_cons_rx_buf[UCOM_CONS_BUFSIZE]; 117197570Sthompsastatic uint8_t ucom_cons_tx_buf[UCOM_CONS_BUFSIZE]; 118197570Sthompsa 119197570Sthompsastatic unsigned int ucom_cons_rx_low = 0; 120197570Sthompsastatic unsigned int ucom_cons_rx_high = 0; 121197570Sthompsa 122197570Sthompsastatic unsigned int ucom_cons_tx_low = 0; 123197570Sthompsastatic unsigned int ucom_cons_tx_high = 0; 124197570Sthompsa 125197570Sthompsastatic int ucom_cons_unit = -1; 126214761Sn_hibmastatic int ucom_cons_subunit = 0; 127197570Sthompsastatic int ucom_cons_baud = 9600; 128197570Sthompsastatic struct ucom_softc *ucom_cons_softc = NULL; 129197570Sthompsa 130197570SthompsaTUNABLE_INT("hw.usb.ucom.cons_unit", &ucom_cons_unit); 131242126ShselaskySYSCTL_INT(_hw_usb_ucom, OID_AUTO, cons_unit, CTLFLAG_RW | CTLFLAG_TUN, 132197570Sthompsa &ucom_cons_unit, 0, "console unit number"); 133214761Sn_hibmaTUNABLE_INT("hw.usb.ucom.cons_subunit", &ucom_cons_subunit); 134242126ShselaskySYSCTL_INT(_hw_usb_ucom, OID_AUTO, cons_subunit, CTLFLAG_RW | CTLFLAG_TUN, 135214761Sn_hibma &ucom_cons_subunit, 0, "console subunit number"); 136197570SthompsaTUNABLE_INT("hw.usb.ucom.cons_baud", &ucom_cons_baud); 137242126ShselaskySYSCTL_INT(_hw_usb_ucom, OID_AUTO, cons_baud, CTLFLAG_RW | CTLFLAG_TUN, 138197570Sthompsa &ucom_cons_baud, 0, "console baud rate"); 139197570Sthompsa 140194228Sthompsastatic usb_proc_callback_t ucom_cfg_start_transfers; 141194228Sthompsastatic usb_proc_callback_t ucom_cfg_open; 142194228Sthompsastatic usb_proc_callback_t ucom_cfg_close; 143194228Sthompsastatic usb_proc_callback_t ucom_cfg_line_state; 144194228Sthompsastatic usb_proc_callback_t ucom_cfg_status_change; 145194228Sthompsastatic usb_proc_callback_t ucom_cfg_param; 146184610Salfred 147214761Sn_hibmastatic int ucom_unit_alloc(void); 148214761Sn_hibmastatic void ucom_unit_free(int); 149214761Sn_hibmastatic int ucom_attach_tty(struct ucom_super_softc *, struct ucom_softc *); 150239179Shselaskystatic void ucom_detach_tty(struct ucom_super_softc *, struct ucom_softc *); 151194228Sthompsastatic void ucom_queue_command(struct ucom_softc *, 152193045Sthompsa usb_proc_callback_t *, struct termios *pt, 153192984Sthompsa struct usb_proc_msg *t0, struct usb_proc_msg *t1); 154194228Sthompsastatic void ucom_shutdown(struct ucom_softc *); 155197570Sthompsastatic void ucom_ring(struct ucom_softc *, uint8_t); 156194228Sthompsastatic void ucom_break(struct ucom_softc *, uint8_t); 157194228Sthompsastatic void ucom_dtr(struct ucom_softc *, uint8_t); 158194228Sthompsastatic void ucom_rts(struct ucom_softc *, uint8_t); 159184610Salfred 160194228Sthompsastatic tsw_open_t ucom_open; 161194228Sthompsastatic tsw_close_t ucom_close; 162194228Sthompsastatic tsw_ioctl_t ucom_ioctl; 163194228Sthompsastatic tsw_modem_t ucom_modem; 164194228Sthompsastatic tsw_param_t ucom_param; 165194228Sthompsastatic tsw_outwakeup_t ucom_outwakeup; 166242619Shselaskystatic tsw_inwakeup_t ucom_inwakeup; 167194228Sthompsastatic tsw_free_t ucom_free; 168184610Salfred 169194228Sthompsastatic struct ttydevsw ucom_class = { 170184610Salfred .tsw_flags = TF_INITLOCK | TF_CALLOUT, 171194228Sthompsa .tsw_open = ucom_open, 172194228Sthompsa .tsw_close = ucom_close, 173194228Sthompsa .tsw_outwakeup = ucom_outwakeup, 174242619Shselasky .tsw_inwakeup = ucom_inwakeup, 175194228Sthompsa .tsw_ioctl = ucom_ioctl, 176194228Sthompsa .tsw_param = ucom_param, 177194228Sthompsa .tsw_modem = ucom_modem, 178194228Sthompsa .tsw_free = ucom_free, 179184610Salfred}; 180184610Salfred 181188942SthompsaMODULE_DEPEND(ucom, usb, 1, 1, 1); 182188942SthompsaMODULE_VERSION(ucom, 1); 183184610Salfred 184239179Shselasky#define UCOM_UNIT_MAX 128 /* maximum number of units */ 185239179Shselasky#define UCOM_TTY_PREFIX "U" 186184610Salfred 187239179Shselaskystatic struct unrhdr *ucom_unrhdr; 188239179Shselaskystatic struct mtx ucom_mtx; 189239179Shselaskystatic int ucom_close_refs; 190184610Salfred 191239179Shselaskystatic void 192239179Shselaskyucom_init(void *arg) 193239179Shselasky{ 194239179Shselasky DPRINTF("\n"); 195239179Shselasky ucom_unrhdr = new_unrhdr(0, UCOM_UNIT_MAX - 1, NULL); 196239179Shselasky mtx_init(&ucom_mtx, "UCOM MTX", NULL, MTX_DEF); 197239179Shselasky} 198239179ShselaskySYSINIT(ucom_init, SI_SUB_KLD - 1, SI_ORDER_ANY, ucom_init, NULL); 199214843Sn_hibma 200239179Shselaskystatic void 201239179Shselaskyucom_uninit(void *arg) 202239179Shselasky{ 203239179Shselasky struct unrhdr *hdr; 204239179Shselasky hdr = ucom_unrhdr; 205239179Shselasky ucom_unrhdr = NULL; 206239179Shselasky 207239179Shselasky DPRINTF("\n"); 208239179Shselasky 209239179Shselasky if (hdr != NULL) 210239179Shselasky delete_unrhdr(hdr); 211239179Shselasky 212239179Shselasky mtx_destroy(&ucom_mtx); 213239179Shselasky} 214268206ShselaskySYSUNINIT(ucom_uninit, SI_SUB_KLD - 3, SI_ORDER_ANY, ucom_uninit, NULL); 215239179Shselasky 216214761Sn_hibma/* 217214761Sn_hibma * Mark a unit number (the X in cuaUX) as in use. 218214761Sn_hibma * 219214761Sn_hibma * Note that devices using a different naming scheme (see ucom_tty_name() 220214761Sn_hibma * callback) still use this unit allocation. 221214761Sn_hibma */ 222214761Sn_hibmastatic int 223214761Sn_hibmaucom_unit_alloc(void) 224184610Salfred{ 225214761Sn_hibma int unit; 226184610Salfred 227239179Shselasky /* sanity checks */ 228239179Shselasky if (ucom_unrhdr == NULL) { 229239179Shselasky DPRINTF("ucom_unrhdr is NULL\n"); 230239179Shselasky return (-1); 231214919Sn_hibma } 232239179Shselasky unit = alloc_unr(ucom_unrhdr); 233239179Shselasky DPRINTF("unit %d is allocated\n", unit); 234239179Shselasky return (unit); 235184610Salfred} 236184610Salfred 237214761Sn_hibma/* 238214761Sn_hibma * Mark the unit number as not in use. 239214761Sn_hibma */ 240184610Salfredstatic void 241214761Sn_hibmaucom_unit_free(int unit) 242184610Salfred{ 243239179Shselasky /* sanity checks */ 244239179Shselasky if (unit < 0 || unit >= UCOM_UNIT_MAX || ucom_unrhdr == NULL) { 245239179Shselasky DPRINTF("cannot free unit number\n"); 246239179Shselasky return; 247239179Shselasky } 248239179Shselasky DPRINTF("unit %d is freed\n", unit); 249239179Shselasky free_unr(ucom_unrhdr, unit); 250184610Salfred} 251184610Salfred 252184610Salfred/* 253214761Sn_hibma * Setup a group of one or more serial ports. 254184610Salfred * 255188413Sthompsa * The mutex pointed to by "mtx" is applied before all 256188413Sthompsa * callbacks are called back. Also "mtx" must be applied 257188413Sthompsa * before calling into the ucom-layer! 258184610Salfred */ 259184610Salfredint 260194228Sthompsaucom_attach(struct ucom_super_softc *ssc, struct ucom_softc *sc, 261233774Shselasky int subunits, void *parent, 262192984Sthompsa const struct ucom_callback *callback, struct mtx *mtx) 263184610Salfred{ 264233774Shselasky int subunit; 265184610Salfred int error = 0; 266184610Salfred 267184610Salfred if ((sc == NULL) || 268233774Shselasky (subunits <= 0) || 269239179Shselasky (callback == NULL) || 270239179Shselasky (mtx == NULL)) { 271184610Salfred return (EINVAL); 272184610Salfred } 273188413Sthompsa 274230209Shselasky /* allocate a uniq unit number */ 275214761Sn_hibma ssc->sc_unit = ucom_unit_alloc(); 276214761Sn_hibma if (ssc->sc_unit == -1) 277184610Salfred return (ENOMEM); 278188413Sthompsa 279230209Shselasky /* generate TTY name string */ 280230209Shselasky snprintf(ssc->sc_ttyname, sizeof(ssc->sc_ttyname), 281230209Shselasky UCOM_TTY_PREFIX "%d", ssc->sc_unit); 282230209Shselasky 283230209Shselasky /* create USB request handling process */ 284194228Sthompsa error = usb_proc_create(&ssc->sc_tq, mtx, "ucom", USB_PRI_MED); 285188413Sthompsa if (error) { 286214761Sn_hibma ucom_unit_free(ssc->sc_unit); 287188413Sthompsa return (error); 288184610Salfred } 289214761Sn_hibma ssc->sc_subunits = subunits; 290239299Shselasky ssc->sc_flag = UCOM_FLAG_ATTACHED | 291239299Shselasky UCOM_FLAG_FREE_UNIT; 292188413Sthompsa 293239299Shselasky if (callback->ucom_free == NULL) 294239299Shselasky ssc->sc_flag |= UCOM_FLAG_WAIT_REFS; 295239179Shselasky 296239299Shselasky /* increment reference count */ 297239299Shselasky ucom_ref(ssc); 298239299Shselasky 299214831Sn_hibma for (subunit = 0; subunit < ssc->sc_subunits; subunit++) { 300214761Sn_hibma sc[subunit].sc_subunit = subunit; 301214761Sn_hibma sc[subunit].sc_super = ssc; 302214761Sn_hibma sc[subunit].sc_mtx = mtx; 303214761Sn_hibma sc[subunit].sc_parent = parent; 304214761Sn_hibma sc[subunit].sc_callback = callback; 305184610Salfred 306214761Sn_hibma error = ucom_attach_tty(ssc, &sc[subunit]); 307184610Salfred if (error) { 308214761Sn_hibma ucom_detach(ssc, &sc[0]); 309188413Sthompsa return (error); 310184610Salfred } 311239179Shselasky /* increment reference count */ 312239179Shselasky ucom_ref(ssc); 313239179Shselasky 314239179Shselasky /* set subunit attached */ 315214761Sn_hibma sc[subunit].sc_flag |= UCOM_FLAG_ATTACHED; 316184610Salfred } 317214761Sn_hibma 318214831Sn_hibma DPRINTF("tp = %p, unit = %d, subunits = %d\n", 319214831Sn_hibma sc->sc_tty, ssc->sc_unit, ssc->sc_subunits); 320214761Sn_hibma 321188413Sthompsa return (0); 322184610Salfred} 323184610Salfred 324188413Sthompsa/* 325239299Shselasky * The following function will do nothing if the structure pointed to 326239299Shselasky * by "ssc" and "sc" is zero or has already been detached. 327184610Salfred */ 328184610Salfredvoid 329214761Sn_hibmaucom_detach(struct ucom_super_softc *ssc, struct ucom_softc *sc) 330184610Salfred{ 331233774Shselasky int subunit; 332184610Salfred 333239299Shselasky if (!(ssc->sc_flag & UCOM_FLAG_ATTACHED)) 334226219Shselasky return; /* not initialized */ 335226219Shselasky 336230209Shselasky if (ssc->sc_sysctl_ttyname != NULL) { 337230209Shselasky sysctl_remove_oid(ssc->sc_sysctl_ttyname, 1, 0); 338230209Shselasky ssc->sc_sysctl_ttyname = NULL; 339230204Shselasky } 340230204Shselasky 341230204Shselasky if (ssc->sc_sysctl_ttyports != NULL) { 342230204Shselasky sysctl_remove_oid(ssc->sc_sysctl_ttyports, 1, 0); 343230204Shselasky ssc->sc_sysctl_ttyports = NULL; 344230204Shselasky } 345230204Shselasky 346194228Sthompsa usb_proc_drain(&ssc->sc_tq); 347184610Salfred 348214831Sn_hibma for (subunit = 0; subunit < ssc->sc_subunits; subunit++) { 349214761Sn_hibma if (sc[subunit].sc_flag & UCOM_FLAG_ATTACHED) { 350184610Salfred 351239179Shselasky ucom_detach_tty(ssc, &sc[subunit]); 352184610Salfred 353214761Sn_hibma /* avoid duplicate detach */ 354214761Sn_hibma sc[subunit].sc_flag &= ~UCOM_FLAG_ATTACHED; 355184610Salfred } 356184610Salfred } 357194228Sthompsa usb_proc_free(&ssc->sc_tq); 358239179Shselasky 359239299Shselasky ucom_unref(ssc); 360239299Shselasky 361239299Shselasky if (ssc->sc_flag & UCOM_FLAG_WAIT_REFS) 362239179Shselasky ucom_drain(ssc); 363239299Shselasky 364239299Shselasky /* make sure we don't detach twice */ 365239299Shselasky ssc->sc_flag &= ~UCOM_FLAG_ATTACHED; 366184610Salfred} 367184610Salfred 368239179Shselaskyvoid 369239179Shselaskyucom_drain(struct ucom_super_softc *ssc) 370239179Shselasky{ 371239179Shselasky mtx_lock(&ucom_mtx); 372239299Shselasky while (ssc->sc_refs > 0) { 373239179Shselasky printf("ucom: Waiting for a TTY device to close.\n"); 374239179Shselasky usb_pause_mtx(&ucom_mtx, hz); 375239179Shselasky } 376239179Shselasky mtx_unlock(&ucom_mtx); 377239179Shselasky} 378239179Shselasky 379239179Shselaskyvoid 380239179Shselaskyucom_drain_all(void *arg) 381239179Shselasky{ 382239179Shselasky mtx_lock(&ucom_mtx); 383239179Shselasky while (ucom_close_refs > 0) { 384239179Shselasky printf("ucom: Waiting for all detached TTY " 385239179Shselasky "devices to have open fds closed.\n"); 386239179Shselasky usb_pause_mtx(&ucom_mtx, hz); 387239179Shselasky } 388239179Shselasky mtx_unlock(&ucom_mtx); 389239179Shselasky} 390239179Shselasky 391184610Salfredstatic int 392214761Sn_hibmaucom_attach_tty(struct ucom_super_softc *ssc, struct ucom_softc *sc) 393184610Salfred{ 394184610Salfred struct tty *tp; 395214831Sn_hibma char buf[32]; /* temporary TTY device name buffer */ 396184610Salfred 397194228Sthompsa tp = tty_alloc_mutex(&ucom_class, sc, sc->sc_mtx); 398214761Sn_hibma if (tp == NULL) 399214761Sn_hibma return (ENOMEM); 400184610Salfred 401184610Salfred /* Check if the client has a custom TTY name */ 402214761Sn_hibma buf[0] = '\0'; 403194228Sthompsa if (sc->sc_callback->ucom_tty_name) { 404194228Sthompsa sc->sc_callback->ucom_tty_name(sc, buf, 405214761Sn_hibma sizeof(buf), ssc->sc_unit, sc->sc_subunit); 406184610Salfred } 407184610Salfred if (buf[0] == 0) { 408184610Salfred /* Use default TTY name */ 409214761Sn_hibma if (ssc->sc_subunits > 1) { 410188413Sthompsa /* multiple modems in one */ 411214843Sn_hibma snprintf(buf, sizeof(buf), UCOM_TTY_PREFIX "%u.%u", 412214761Sn_hibma ssc->sc_unit, sc->sc_subunit); 413188413Sthompsa } else { 414188413Sthompsa /* single modem */ 415214843Sn_hibma snprintf(buf, sizeof(buf), UCOM_TTY_PREFIX "%u", 416214843Sn_hibma ssc->sc_unit); 417184610Salfred } 418184610Salfred } 419184610Salfred tty_makedev(tp, NULL, "%s", buf); 420184610Salfred 421184610Salfred sc->sc_tty = tp; 422184610Salfred 423283341Sian sc->sc_pps.ppscap = PPS_CAPTUREBOTH; 424283341Sian sc->sc_pps.driver_abi = PPS_ABI_VERSION; 425283341Sian sc->sc_pps.driver_mtx = sc->sc_mtx; 426283341Sian pps_init_abi(&sc->sc_pps); 427283341Sian 428184610Salfred DPRINTF("ttycreate: %s\n", buf); 429184610Salfred 430197570Sthompsa /* Check if this device should be a console */ 431197570Sthompsa if ((ucom_cons_softc == NULL) && 432214761Sn_hibma (ssc->sc_unit == ucom_cons_unit) && 433214761Sn_hibma (sc->sc_subunit == ucom_cons_subunit)) { 434197570Sthompsa 435242695Shselasky DPRINTF("unit %d subunit %d is console", 436242695Shselasky ssc->sc_unit, sc->sc_subunit); 437214761Sn_hibma 438197570Sthompsa ucom_cons_softc = sc; 439197570Sthompsa 440242695Shselasky tty_init_console(tp, ucom_cons_baud); 441197570Sthompsa 442239179Shselasky UCOM_MTX_LOCK(ucom_cons_softc); 443197570Sthompsa ucom_cons_rx_low = 0; 444197570Sthompsa ucom_cons_rx_high = 0; 445197570Sthompsa ucom_cons_tx_low = 0; 446197570Sthompsa ucom_cons_tx_high = 0; 447197570Sthompsa sc->sc_flag |= UCOM_FLAG_CONSOLE; 448197570Sthompsa ucom_open(ucom_cons_softc->sc_tty); 449242695Shselasky ucom_param(ucom_cons_softc->sc_tty, &tp->t_termios_init_in); 450239179Shselasky UCOM_MTX_UNLOCK(ucom_cons_softc); 451197570Sthompsa } 452214761Sn_hibma 453214761Sn_hibma return (0); 454184610Salfred} 455184610Salfred 456184610Salfredstatic void 457239179Shselaskyucom_detach_tty(struct ucom_super_softc *ssc, struct ucom_softc *sc) 458184610Salfred{ 459184610Salfred struct tty *tp = sc->sc_tty; 460184610Salfred 461184610Salfred DPRINTF("sc = %p, tp = %p\n", sc, sc->sc_tty); 462184610Salfred 463197570Sthompsa if (sc->sc_flag & UCOM_FLAG_CONSOLE) { 464239179Shselasky UCOM_MTX_LOCK(ucom_cons_softc); 465197570Sthompsa ucom_close(ucom_cons_softc->sc_tty); 466214761Sn_hibma sc->sc_flag &= ~UCOM_FLAG_CONSOLE; 467239179Shselasky UCOM_MTX_UNLOCK(ucom_cons_softc); 468197570Sthompsa ucom_cons_softc = NULL; 469197570Sthompsa } 470197570Sthompsa 471184610Salfred /* the config thread has been stopped when we get here */ 472184610Salfred 473239179Shselasky UCOM_MTX_LOCK(sc); 474184610Salfred sc->sc_flag |= UCOM_FLAG_GONE; 475197570Sthompsa sc->sc_flag &= ~(UCOM_FLAG_HL_READY | UCOM_FLAG_LL_READY); 476239179Shselasky UCOM_MTX_UNLOCK(sc); 477239179Shselasky 478184610Salfred if (tp) { 479239179Shselasky mtx_lock(&ucom_mtx); 480239179Shselasky ucom_close_refs++; 481239179Shselasky mtx_unlock(&ucom_mtx); 482239179Shselasky 483184610Salfred tty_lock(tp); 484184610Salfred 485194228Sthompsa ucom_close(tp); /* close, if any */ 486184610Salfred 487184610Salfred tty_rel_gone(tp); 488184610Salfred 489239179Shselasky UCOM_MTX_LOCK(sc); 490184610Salfred /* 491184610Salfred * make sure that read and write transfers are stopped 492184610Salfred */ 493239179Shselasky if (sc->sc_callback->ucom_stop_read) 494194228Sthompsa (sc->sc_callback->ucom_stop_read) (sc); 495239179Shselasky if (sc->sc_callback->ucom_stop_write) 496194228Sthompsa (sc->sc_callback->ucom_stop_write) (sc); 497239179Shselasky UCOM_MTX_UNLOCK(sc); 498184610Salfred } 499184610Salfred} 500184610Salfred 501214843Sn_hibmavoid 502214843Sn_hibmaucom_set_pnpinfo_usb(struct ucom_super_softc *ssc, device_t dev) 503214843Sn_hibma{ 504230204Shselasky char buf[64]; 505230204Shselasky uint8_t iface_index; 506230204Shselasky struct usb_attach_arg *uaa; 507214843Sn_hibma 508230209Shselasky snprintf(buf, sizeof(buf), "ttyname=" UCOM_TTY_PREFIX 509230209Shselasky "%d ttyports=%d", ssc->sc_unit, ssc->sc_subunits); 510214843Sn_hibma 511230204Shselasky /* Store the PNP info in the first interface for the device */ 512230204Shselasky uaa = device_get_ivars(dev); 513230204Shselasky iface_index = uaa->info.bIfaceIndex; 514214843Sn_hibma 515230204Shselasky if (usbd_set_pnpinfo(uaa->device, iface_index, buf) != 0) 516230204Shselasky device_printf(dev, "Could not set PNP info\n"); 517230204Shselasky 518230204Shselasky /* 519230209Shselasky * The following information is also replicated in the PNP-info 520230204Shselasky * string which is registered above: 521230204Shselasky */ 522230209Shselasky if (ssc->sc_sysctl_ttyname == NULL) { 523230209Shselasky ssc->sc_sysctl_ttyname = SYSCTL_ADD_STRING(NULL, 524230204Shselasky SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), 525230209Shselasky OID_AUTO, "ttyname", CTLFLAG_RD, ssc->sc_ttyname, 0, 526230209Shselasky "TTY device basename"); 527230204Shselasky } 528230204Shselasky if (ssc->sc_sysctl_ttyports == NULL) { 529230204Shselasky ssc->sc_sysctl_ttyports = SYSCTL_ADD_INT(NULL, 530230204Shselasky SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), 531230204Shselasky OID_AUTO, "ttyports", CTLFLAG_RD, 532230204Shselasky NULL, ssc->sc_subunits, "Number of ports"); 533230204Shselasky } 534214843Sn_hibma} 535214843Sn_hibma 536184610Salfredstatic void 537194228Sthompsaucom_queue_command(struct ucom_softc *sc, 538193045Sthompsa usb_proc_callback_t *fn, struct termios *pt, 539192984Sthompsa struct usb_proc_msg *t0, struct usb_proc_msg *t1) 540184610Salfred{ 541192984Sthompsa struct ucom_super_softc *ssc = sc->sc_super; 542192984Sthompsa struct ucom_param_task *task; 543187176Sthompsa 544239179Shselasky UCOM_MTX_ASSERT(sc, MA_OWNED); 545188413Sthompsa 546194228Sthompsa if (usb_proc_is_gone(&ssc->sc_tq)) { 547187176Sthompsa DPRINTF("proc is gone\n"); 548188413Sthompsa return; /* nothing to do */ 549187176Sthompsa } 550188413Sthompsa /* 551188413Sthompsa * NOTE: The task cannot get executed before we drop the 552188413Sthompsa * "sc_mtx" mutex. It is safe to update fields in the message 553188413Sthompsa * structure after that the message got queued. 554188413Sthompsa */ 555192984Sthompsa task = (struct ucom_param_task *) 556194228Sthompsa usb_proc_msignal(&ssc->sc_tq, t0, t1); 557187176Sthompsa 558188413Sthompsa /* Setup callback and softc pointers */ 559188413Sthompsa task->hdr.pm_callback = fn; 560188413Sthompsa task->sc = sc; 561184610Salfred 562188413Sthompsa /* 563188413Sthompsa * Make a copy of the termios. This field is only present if 564188413Sthompsa * the "pt" field is not NULL. 565188413Sthompsa */ 566188413Sthompsa if (pt != NULL) 567188413Sthompsa task->termios_copy = *pt; 568184610Salfred 569188413Sthompsa /* 570188413Sthompsa * Closing the device should be synchronous. 571188413Sthompsa */ 572194228Sthompsa if (fn == ucom_cfg_close) 573194228Sthompsa usb_proc_mwait(&ssc->sc_tq, t0, t1); 574188413Sthompsa 575190742Sthompsa /* 576190742Sthompsa * In case of multiple configure requests, 577190742Sthompsa * keep track of the last one! 578190742Sthompsa */ 579194228Sthompsa if (fn == ucom_cfg_start_transfers) 580190742Sthompsa sc->sc_last_start_xfer = &task->hdr; 581184610Salfred} 582184610Salfred 583184610Salfredstatic void 584194228Sthompsaucom_shutdown(struct ucom_softc *sc) 585184610Salfred{ 586184610Salfred struct tty *tp = sc->sc_tty; 587184610Salfred 588239179Shselasky UCOM_MTX_ASSERT(sc, MA_OWNED); 589184610Salfred 590184610Salfred DPRINTF("\n"); 591184610Salfred 592184610Salfred /* 593184610Salfred * Hang up if necessary: 594184610Salfred */ 595184610Salfred if (tp->t_termios.c_cflag & HUPCL) { 596194228Sthompsa ucom_modem(tp, 0, SER_DTR); 597184610Salfred } 598184610Salfred} 599184610Salfred 600184610Salfred/* 601184610Salfred * Return values: 602184610Salfred * 0: normal 603188413Sthompsa * else: taskqueue is draining or gone 604184610Salfred */ 605184610Salfreduint8_t 606194228Sthompsaucom_cfg_is_gone(struct ucom_softc *sc) 607184610Salfred{ 608192984Sthompsa struct ucom_super_softc *ssc = sc->sc_super; 609184610Salfred 610194228Sthompsa return (usb_proc_is_gone(&ssc->sc_tq)); 611184610Salfred} 612184610Salfred 613184610Salfredstatic void 614194228Sthompsaucom_cfg_start_transfers(struct usb_proc_msg *_task) 615184610Salfred{ 616192984Sthompsa struct ucom_cfg_task *task = 617192984Sthompsa (struct ucom_cfg_task *)_task; 618192984Sthompsa struct ucom_softc *sc = task->sc; 619187176Sthompsa 620184610Salfred if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) { 621184610Salfred return; 622184610Salfred } 623184610Salfred if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) { 624184610Salfred /* TTY device closed */ 625184610Salfred return; 626184610Salfred } 627184610Salfred 628190742Sthompsa if (_task == sc->sc_last_start_xfer) 629190742Sthompsa sc->sc_flag |= UCOM_FLAG_GP_DATA; 630190742Sthompsa 631194228Sthompsa if (sc->sc_callback->ucom_start_read) { 632194228Sthompsa (sc->sc_callback->ucom_start_read) (sc); 633184610Salfred } 634194228Sthompsa if (sc->sc_callback->ucom_start_write) { 635194228Sthompsa (sc->sc_callback->ucom_start_write) (sc); 636184610Salfred } 637184610Salfred} 638184610Salfred 639184610Salfredstatic void 640194228Sthompsaucom_start_transfers(struct ucom_softc *sc) 641184610Salfred{ 642184610Salfred if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) { 643184610Salfred return; 644184610Salfred } 645184610Salfred /* 646188413Sthompsa * Make sure that data transfers are started in both 647188413Sthompsa * directions: 648184610Salfred */ 649194228Sthompsa if (sc->sc_callback->ucom_start_read) { 650194228Sthompsa (sc->sc_callback->ucom_start_read) (sc); 651184610Salfred } 652194228Sthompsa if (sc->sc_callback->ucom_start_write) { 653194228Sthompsa (sc->sc_callback->ucom_start_write) (sc); 654184610Salfred } 655184610Salfred} 656184610Salfred 657184610Salfredstatic void 658194228Sthompsaucom_cfg_open(struct usb_proc_msg *_task) 659184610Salfred{ 660192984Sthompsa struct ucom_cfg_task *task = 661192984Sthompsa (struct ucom_cfg_task *)_task; 662192984Sthompsa struct ucom_softc *sc = task->sc; 663187176Sthompsa 664184610Salfred DPRINTF("\n"); 665184610Salfred 666184610Salfred if (sc->sc_flag & UCOM_FLAG_LL_READY) { 667184610Salfred 668184610Salfred /* already opened */ 669184610Salfred 670184610Salfred } else { 671184610Salfred 672184610Salfred sc->sc_flag |= UCOM_FLAG_LL_READY; 673184610Salfred 674194228Sthompsa if (sc->sc_callback->ucom_cfg_open) { 675194228Sthompsa (sc->sc_callback->ucom_cfg_open) (sc); 676184610Salfred 677184610Salfred /* wait a little */ 678194228Sthompsa usb_pause_mtx(sc->sc_mtx, hz / 10); 679184610Salfred } 680184610Salfred } 681184610Salfred} 682184610Salfred 683184610Salfredstatic int 684194228Sthompsaucom_open(struct tty *tp) 685184610Salfred{ 686192984Sthompsa struct ucom_softc *sc = tty_softc(tp); 687184610Salfred int error; 688184610Salfred 689239179Shselasky UCOM_MTX_ASSERT(sc, MA_OWNED); 690184610Salfred 691184610Salfred if (sc->sc_flag & UCOM_FLAG_GONE) { 692184610Salfred return (ENXIO); 693184610Salfred } 694184610Salfred if (sc->sc_flag & UCOM_FLAG_HL_READY) { 695184610Salfred /* already opened */ 696184610Salfred return (0); 697184610Salfred } 698184610Salfred DPRINTF("tp = %p\n", tp); 699184610Salfred 700194228Sthompsa if (sc->sc_callback->ucom_pre_open) { 701184610Salfred /* 702184610Salfred * give the lower layer a chance to disallow TTY open, for 703184610Salfred * example if the device is not present: 704184610Salfred */ 705194228Sthompsa error = (sc->sc_callback->ucom_pre_open) (sc); 706184610Salfred if (error) { 707184610Salfred return (error); 708184610Salfred } 709184610Salfred } 710184610Salfred sc->sc_flag |= UCOM_FLAG_HL_READY; 711184610Salfred 712184610Salfred /* Disable transfers */ 713184610Salfred sc->sc_flag &= ~UCOM_FLAG_GP_DATA; 714184610Salfred 715184610Salfred sc->sc_lsr = 0; 716184610Salfred sc->sc_msr = 0; 717184610Salfred sc->sc_mcr = 0; 718184610Salfred 719188413Sthompsa /* reset programmed line state */ 720188413Sthompsa sc->sc_pls_curr = 0; 721188413Sthompsa sc->sc_pls_set = 0; 722188413Sthompsa sc->sc_pls_clr = 0; 723184610Salfred 724242619Shselasky /* reset jitter buffer */ 725242619Shselasky sc->sc_jitterbuf_in = 0; 726242619Shselasky sc->sc_jitterbuf_out = 0; 727242619Shselasky 728194228Sthompsa ucom_queue_command(sc, ucom_cfg_open, NULL, 729188413Sthompsa &sc->sc_open_task[0].hdr, 730188413Sthompsa &sc->sc_open_task[1].hdr); 731184610Salfred 732188413Sthompsa /* Queue transfer enable command last */ 733194228Sthompsa ucom_queue_command(sc, ucom_cfg_start_transfers, NULL, 734188413Sthompsa &sc->sc_start_task[0].hdr, 735188413Sthompsa &sc->sc_start_task[1].hdr); 736188413Sthompsa 737194228Sthompsa ucom_modem(tp, SER_DTR | SER_RTS, 0); 738184610Salfred 739197570Sthompsa ucom_ring(sc, 0); 740197570Sthompsa 741194228Sthompsa ucom_break(sc, 0); 742184610Salfred 743194228Sthompsa ucom_status_change(sc); 744184610Salfred 745184610Salfred return (0); 746184610Salfred} 747184610Salfred 748184610Salfredstatic void 749194228Sthompsaucom_cfg_close(struct usb_proc_msg *_task) 750184610Salfred{ 751192984Sthompsa struct ucom_cfg_task *task = 752192984Sthompsa (struct ucom_cfg_task *)_task; 753192984Sthompsa struct ucom_softc *sc = task->sc; 754187176Sthompsa 755184610Salfred DPRINTF("\n"); 756184610Salfred 757184610Salfred if (sc->sc_flag & UCOM_FLAG_LL_READY) { 758192820Sthompsa sc->sc_flag &= ~UCOM_FLAG_LL_READY; 759194228Sthompsa if (sc->sc_callback->ucom_cfg_close) 760194228Sthompsa (sc->sc_callback->ucom_cfg_close) (sc); 761184610Salfred } else { 762184610Salfred /* already closed */ 763184610Salfred } 764184610Salfred} 765184610Salfred 766184610Salfredstatic void 767194228Sthompsaucom_close(struct tty *tp) 768184610Salfred{ 769192984Sthompsa struct ucom_softc *sc = tty_softc(tp); 770184610Salfred 771239179Shselasky UCOM_MTX_ASSERT(sc, MA_OWNED); 772188413Sthompsa 773184610Salfred DPRINTF("tp=%p\n", tp); 774184610Salfred 775184610Salfred if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) { 776184610Salfred DPRINTF("tp=%p already closed\n", tp); 777184610Salfred return; 778184610Salfred } 779194228Sthompsa ucom_shutdown(sc); 780184610Salfred 781194228Sthompsa ucom_queue_command(sc, ucom_cfg_close, NULL, 782188413Sthompsa &sc->sc_close_task[0].hdr, 783188413Sthompsa &sc->sc_close_task[1].hdr); 784184610Salfred 785192820Sthompsa sc->sc_flag &= ~(UCOM_FLAG_HL_READY | UCOM_FLAG_RTS_IFLOW); 786184610Salfred 787194228Sthompsa if (sc->sc_callback->ucom_stop_read) { 788194228Sthompsa (sc->sc_callback->ucom_stop_read) (sc); 789184610Salfred } 790184610Salfred} 791184610Salfred 792242619Shselaskystatic void 793242619Shselaskyucom_inwakeup(struct tty *tp) 794242619Shselasky{ 795242619Shselasky struct ucom_softc *sc = tty_softc(tp); 796242619Shselasky uint16_t pos; 797242619Shselasky 798242619Shselasky if (sc == NULL) 799242619Shselasky return; 800242619Shselasky 801242703Shselasky UCOM_MTX_ASSERT(sc, MA_OWNED); 802242619Shselasky 803242703Shselasky DPRINTF("tp=%p\n", tp); 804242702Shselasky 805242619Shselasky if (ttydisc_can_bypass(tp) != 0 || 806244489Shselasky (sc->sc_flag & UCOM_FLAG_HL_READY) == 0 || 807244489Shselasky (sc->sc_flag & UCOM_FLAG_INWAKEUP) != 0) { 808242619Shselasky return; 809242619Shselasky } 810242619Shselasky 811244489Shselasky /* prevent recursion */ 812244489Shselasky sc->sc_flag |= UCOM_FLAG_INWAKEUP; 813244489Shselasky 814242619Shselasky pos = sc->sc_jitterbuf_out; 815242619Shselasky 816242619Shselasky while (sc->sc_jitterbuf_in != pos) { 817242619Shselasky int c; 818242619Shselasky 819242619Shselasky c = (char)sc->sc_jitterbuf[pos]; 820242619Shselasky 821242619Shselasky if (ttydisc_rint(tp, c, 0) == -1) 822242619Shselasky break; 823242619Shselasky pos++; 824242619Shselasky if (pos >= UCOM_JITTERBUF_SIZE) 825242619Shselasky pos -= UCOM_JITTERBUF_SIZE; 826242619Shselasky } 827242619Shselasky 828242619Shselasky sc->sc_jitterbuf_out = pos; 829242619Shselasky 830242619Shselasky /* clear RTS in async fashion */ 831242619Shselasky if ((sc->sc_jitterbuf_in == pos) && 832242619Shselasky (sc->sc_flag & UCOM_FLAG_RTS_IFLOW)) 833242619Shselasky ucom_rts(sc, 0); 834244489Shselasky 835244489Shselasky sc->sc_flag &= ~UCOM_FLAG_INWAKEUP; 836242619Shselasky} 837242619Shselasky 838184610Salfredstatic int 839194228Sthompsaucom_ioctl(struct tty *tp, u_long cmd, caddr_t data, struct thread *td) 840184610Salfred{ 841192984Sthompsa struct ucom_softc *sc = tty_softc(tp); 842184610Salfred int error; 843184610Salfred 844239179Shselasky UCOM_MTX_ASSERT(sc, MA_OWNED); 845184610Salfred 846184610Salfred if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) { 847184610Salfred return (EIO); 848184610Salfred } 849184610Salfred DPRINTF("cmd = 0x%08lx\n", cmd); 850184610Salfred 851184610Salfred switch (cmd) { 852197570Sthompsa#if 0 853197570Sthompsa case TIOCSRING: 854197570Sthompsa ucom_ring(sc, 1); 855197570Sthompsa error = 0; 856197570Sthompsa break; 857197570Sthompsa case TIOCCRING: 858197570Sthompsa ucom_ring(sc, 0); 859197570Sthompsa error = 0; 860197570Sthompsa break; 861197570Sthompsa#endif 862184610Salfred case TIOCSBRK: 863194228Sthompsa ucom_break(sc, 1); 864184610Salfred error = 0; 865184610Salfred break; 866184610Salfred case TIOCCBRK: 867194228Sthompsa ucom_break(sc, 0); 868184610Salfred error = 0; 869184610Salfred break; 870184610Salfred default: 871194228Sthompsa if (sc->sc_callback->ucom_ioctl) { 872194228Sthompsa error = (sc->sc_callback->ucom_ioctl) 873184610Salfred (sc, cmd, data, 0, td); 874184610Salfred } else { 875184610Salfred error = ENOIOCTL; 876184610Salfred } 877283341Sian if (error == ENOIOCTL) 878283341Sian error = pps_ioctl(cmd, data, &sc->sc_pps); 879184610Salfred break; 880184610Salfred } 881184610Salfred return (error); 882184610Salfred} 883184610Salfred 884184610Salfredstatic int 885194228Sthompsaucom_modem(struct tty *tp, int sigon, int sigoff) 886184610Salfred{ 887192984Sthompsa struct ucom_softc *sc = tty_softc(tp); 888184610Salfred uint8_t onoff; 889184610Salfred 890239179Shselasky UCOM_MTX_ASSERT(sc, MA_OWNED); 891184610Salfred 892184610Salfred if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) { 893184610Salfred return (0); 894184610Salfred } 895184610Salfred if ((sigon == 0) && (sigoff == 0)) { 896184610Salfred 897184610Salfred if (sc->sc_mcr & SER_DTR) { 898184610Salfred sigon |= SER_DTR; 899184610Salfred } 900184610Salfred if (sc->sc_mcr & SER_RTS) { 901184610Salfred sigon |= SER_RTS; 902184610Salfred } 903184610Salfred if (sc->sc_msr & SER_CTS) { 904184610Salfred sigon |= SER_CTS; 905184610Salfred } 906184610Salfred if (sc->sc_msr & SER_DCD) { 907184610Salfred sigon |= SER_DCD; 908184610Salfred } 909184610Salfred if (sc->sc_msr & SER_DSR) { 910184610Salfred sigon |= SER_DSR; 911184610Salfred } 912184610Salfred if (sc->sc_msr & SER_RI) { 913184610Salfred sigon |= SER_RI; 914184610Salfred } 915184610Salfred return (sigon); 916184610Salfred } 917184610Salfred if (sigon & SER_DTR) { 918184610Salfred sc->sc_mcr |= SER_DTR; 919184610Salfred } 920184610Salfred if (sigoff & SER_DTR) { 921184610Salfred sc->sc_mcr &= ~SER_DTR; 922184610Salfred } 923184610Salfred if (sigon & SER_RTS) { 924184610Salfred sc->sc_mcr |= SER_RTS; 925184610Salfred } 926184610Salfred if (sigoff & SER_RTS) { 927184610Salfred sc->sc_mcr &= ~SER_RTS; 928184610Salfred } 929184610Salfred onoff = (sc->sc_mcr & SER_DTR) ? 1 : 0; 930194228Sthompsa ucom_dtr(sc, onoff); 931184610Salfred 932184610Salfred onoff = (sc->sc_mcr & SER_RTS) ? 1 : 0; 933194228Sthompsa ucom_rts(sc, onoff); 934184610Salfred 935184610Salfred return (0); 936184610Salfred} 937184610Salfred 938184610Salfredstatic void 939194228Sthompsaucom_cfg_line_state(struct usb_proc_msg *_task) 940184610Salfred{ 941192984Sthompsa struct ucom_cfg_task *task = 942192984Sthompsa (struct ucom_cfg_task *)_task; 943192984Sthompsa struct ucom_softc *sc = task->sc; 944188413Sthompsa uint8_t notch_bits; 945188413Sthompsa uint8_t any_bits; 946188413Sthompsa uint8_t prev_value; 947188413Sthompsa uint8_t last_value; 948188413Sthompsa uint8_t mask; 949187176Sthompsa 950184610Salfred if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) { 951184610Salfred return; 952184610Salfred } 953184610Salfred 954188413Sthompsa mask = 0; 955188413Sthompsa /* compute callback mask */ 956194228Sthompsa if (sc->sc_callback->ucom_cfg_set_dtr) 957188413Sthompsa mask |= UCOM_LS_DTR; 958194228Sthompsa if (sc->sc_callback->ucom_cfg_set_rts) 959188413Sthompsa mask |= UCOM_LS_RTS; 960194228Sthompsa if (sc->sc_callback->ucom_cfg_set_break) 961188413Sthompsa mask |= UCOM_LS_BREAK; 962197570Sthompsa if (sc->sc_callback->ucom_cfg_set_ring) 963197570Sthompsa mask |= UCOM_LS_RING; 964184610Salfred 965188413Sthompsa /* compute the bits we are to program */ 966188413Sthompsa notch_bits = (sc->sc_pls_set & sc->sc_pls_clr) & mask; 967188413Sthompsa any_bits = (sc->sc_pls_set | sc->sc_pls_clr) & mask; 968188413Sthompsa prev_value = sc->sc_pls_curr ^ notch_bits; 969188413Sthompsa last_value = sc->sc_pls_curr; 970187176Sthompsa 971188413Sthompsa /* reset programmed line state */ 972188413Sthompsa sc->sc_pls_curr = 0; 973188413Sthompsa sc->sc_pls_set = 0; 974188413Sthompsa sc->sc_pls_clr = 0; 975188413Sthompsa 976250576Seadler /* ensure that we don't lose any levels */ 977188413Sthompsa if (notch_bits & UCOM_LS_DTR) 978194228Sthompsa sc->sc_callback->ucom_cfg_set_dtr(sc, 979188413Sthompsa (prev_value & UCOM_LS_DTR) ? 1 : 0); 980188413Sthompsa if (notch_bits & UCOM_LS_RTS) 981194228Sthompsa sc->sc_callback->ucom_cfg_set_rts(sc, 982188413Sthompsa (prev_value & UCOM_LS_RTS) ? 1 : 0); 983188413Sthompsa if (notch_bits & UCOM_LS_BREAK) 984194228Sthompsa sc->sc_callback->ucom_cfg_set_break(sc, 985188413Sthompsa (prev_value & UCOM_LS_BREAK) ? 1 : 0); 986197570Sthompsa if (notch_bits & UCOM_LS_RING) 987197570Sthompsa sc->sc_callback->ucom_cfg_set_ring(sc, 988197570Sthompsa (prev_value & UCOM_LS_RING) ? 1 : 0); 989188413Sthompsa 990188413Sthompsa /* set last value */ 991188413Sthompsa if (any_bits & UCOM_LS_DTR) 992194228Sthompsa sc->sc_callback->ucom_cfg_set_dtr(sc, 993188413Sthompsa (last_value & UCOM_LS_DTR) ? 1 : 0); 994188413Sthompsa if (any_bits & UCOM_LS_RTS) 995194228Sthompsa sc->sc_callback->ucom_cfg_set_rts(sc, 996188413Sthompsa (last_value & UCOM_LS_RTS) ? 1 : 0); 997188413Sthompsa if (any_bits & UCOM_LS_BREAK) 998194228Sthompsa sc->sc_callback->ucom_cfg_set_break(sc, 999188413Sthompsa (last_value & UCOM_LS_BREAK) ? 1 : 0); 1000197570Sthompsa if (any_bits & UCOM_LS_RING) 1001197570Sthompsa sc->sc_callback->ucom_cfg_set_ring(sc, 1002197570Sthompsa (last_value & UCOM_LS_RING) ? 1 : 0); 1003187176Sthompsa} 1004187176Sthompsa 1005187176Sthompsastatic void 1006194228Sthompsaucom_line_state(struct ucom_softc *sc, 1007188413Sthompsa uint8_t set_bits, uint8_t clear_bits) 1008184610Salfred{ 1009239179Shselasky UCOM_MTX_ASSERT(sc, MA_OWNED); 1010184610Salfred 1011184610Salfred if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) { 1012184610Salfred return; 1013184610Salfred } 1014184610Salfred 1015188413Sthompsa DPRINTF("on=0x%02x, off=0x%02x\n", set_bits, clear_bits); 1016184610Salfred 1017188413Sthompsa /* update current programmed line state */ 1018188413Sthompsa sc->sc_pls_curr |= set_bits; 1019188413Sthompsa sc->sc_pls_curr &= ~clear_bits; 1020188413Sthompsa sc->sc_pls_set |= set_bits; 1021188413Sthompsa sc->sc_pls_clr |= clear_bits; 1022187176Sthompsa 1023188413Sthompsa /* defer driver programming */ 1024194228Sthompsa ucom_queue_command(sc, ucom_cfg_line_state, NULL, 1025188413Sthompsa &sc->sc_line_state_task[0].hdr, 1026188413Sthompsa &sc->sc_line_state_task[1].hdr); 1027184610Salfred} 1028184610Salfred 1029184610Salfredstatic void 1030197570Sthompsaucom_ring(struct ucom_softc *sc, uint8_t onoff) 1031197570Sthompsa{ 1032197570Sthompsa DPRINTF("onoff = %d\n", onoff); 1033197570Sthompsa 1034197570Sthompsa if (onoff) 1035197570Sthompsa ucom_line_state(sc, UCOM_LS_RING, 0); 1036197570Sthompsa else 1037197570Sthompsa ucom_line_state(sc, 0, UCOM_LS_RING); 1038197570Sthompsa} 1039197570Sthompsa 1040197570Sthompsastatic void 1041194228Sthompsaucom_break(struct ucom_softc *sc, uint8_t onoff) 1042187176Sthompsa{ 1043188413Sthompsa DPRINTF("onoff = %d\n", onoff); 1044187176Sthompsa 1045188413Sthompsa if (onoff) 1046194228Sthompsa ucom_line_state(sc, UCOM_LS_BREAK, 0); 1047188413Sthompsa else 1048194228Sthompsa ucom_line_state(sc, 0, UCOM_LS_BREAK); 1049187176Sthompsa} 1050187176Sthompsa 1051187176Sthompsastatic void 1052194228Sthompsaucom_dtr(struct ucom_softc *sc, uint8_t onoff) 1053184610Salfred{ 1054184610Salfred DPRINTF("onoff = %d\n", onoff); 1055184610Salfred 1056188413Sthompsa if (onoff) 1057194228Sthompsa ucom_line_state(sc, UCOM_LS_DTR, 0); 1058188413Sthompsa else 1059194228Sthompsa ucom_line_state(sc, 0, UCOM_LS_DTR); 1060184610Salfred} 1061184610Salfred 1062184610Salfredstatic void 1063194228Sthompsaucom_rts(struct ucom_softc *sc, uint8_t onoff) 1064184610Salfred{ 1065184610Salfred DPRINTF("onoff = %d\n", onoff); 1066184610Salfred 1067188413Sthompsa if (onoff) 1068194228Sthompsa ucom_line_state(sc, UCOM_LS_RTS, 0); 1069188413Sthompsa else 1070194228Sthompsa ucom_line_state(sc, 0, UCOM_LS_RTS); 1071184610Salfred} 1072184610Salfred 1073184610Salfredstatic void 1074194228Sthompsaucom_cfg_status_change(struct usb_proc_msg *_task) 1075184610Salfred{ 1076192984Sthompsa struct ucom_cfg_task *task = 1077192984Sthompsa (struct ucom_cfg_task *)_task; 1078192984Sthompsa struct ucom_softc *sc = task->sc; 1079184610Salfred struct tty *tp; 1080294637Sian int onoff; 1081184610Salfred uint8_t new_msr; 1082184610Salfred uint8_t new_lsr; 1083283341Sian uint8_t msr_delta; 1084213872Shselasky uint8_t lsr_delta; 1085294637Sian uint8_t pps_signal; 1086184610Salfred 1087184610Salfred tp = sc->sc_tty; 1088184610Salfred 1089239179Shselasky UCOM_MTX_ASSERT(sc, MA_OWNED); 1090184610Salfred 1091184610Salfred if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) { 1092184610Salfred return; 1093184610Salfred } 1094194228Sthompsa if (sc->sc_callback->ucom_cfg_get_status == NULL) { 1095184610Salfred return; 1096184610Salfred } 1097184610Salfred /* get status */ 1098184610Salfred 1099184610Salfred new_msr = 0; 1100184610Salfred new_lsr = 0; 1101184610Salfred 1102194228Sthompsa (sc->sc_callback->ucom_cfg_get_status) (sc, &new_lsr, &new_msr); 1103184610Salfred 1104184610Salfred if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) { 1105184610Salfred /* TTY device closed */ 1106184610Salfred return; 1107184610Salfred } 1108283341Sian msr_delta = (sc->sc_msr ^ new_msr); 1109213872Shselasky lsr_delta = (sc->sc_lsr ^ new_lsr); 1110184610Salfred 1111184610Salfred sc->sc_msr = new_msr; 1112184610Salfred sc->sc_lsr = new_lsr; 1113184610Salfred 1114283341Sian /* 1115294637Sian * Time pulse counting support. 1116283341Sian */ 1117294637Sian switch(ucom_pps_mode & UART_PPS_SIGNAL_MASK) { 1118294637Sian case UART_PPS_CTS: 1119294637Sian pps_signal = SER_CTS; 1120283341Sian break; 1121294637Sian case UART_PPS_DCD: 1122294637Sian pps_signal = SER_DCD; 1123283341Sian break; 1124283341Sian default: 1125294637Sian pps_signal = 0; 1126283341Sian break; 1127283341Sian } 1128184610Salfred 1129294637Sian if ((sc->sc_pps.ppsparam.mode & PPS_CAPTUREBOTH) && 1130294637Sian (msr_delta & pps_signal)) { 1131294637Sian pps_capture(&sc->sc_pps); 1132294637Sian onoff = (sc->sc_msr & pps_signal) ? 1 : 0; 1133294637Sian if (ucom_pps_mode & UART_PPS_INVERT_PULSE) 1134294637Sian onoff = !onoff; 1135294637Sian pps_event(&sc->sc_pps, onoff ? PPS_CAPTUREASSERT : 1136294637Sian PPS_CAPTURECLEAR); 1137294637Sian } 1138294637Sian 1139283341Sian if (msr_delta & SER_DCD) { 1140184610Salfred 1141294637Sian onoff = (sc->sc_msr & SER_DCD) ? 1 : 0; 1142283341Sian 1143184610Salfred DPRINTF("DCD changed to %d\n", onoff); 1144184610Salfred 1145184610Salfred ttydisc_modem(tp, onoff); 1146184610Salfred } 1147213872Shselasky 1148213872Shselasky if ((lsr_delta & ULSR_BI) && (sc->sc_lsr & ULSR_BI)) { 1149213872Shselasky 1150213872Shselasky DPRINTF("BREAK detected\n"); 1151213872Shselasky 1152213872Shselasky ttydisc_rint(tp, 0, TRE_BREAK); 1153213872Shselasky ttydisc_rint_done(tp); 1154213872Shselasky } 1155213872Shselasky 1156213872Shselasky if ((lsr_delta & ULSR_FE) && (sc->sc_lsr & ULSR_FE)) { 1157213872Shselasky 1158213872Shselasky DPRINTF("Frame error detected\n"); 1159213872Shselasky 1160213872Shselasky ttydisc_rint(tp, 0, TRE_FRAMING); 1161213872Shselasky ttydisc_rint_done(tp); 1162213872Shselasky } 1163213872Shselasky 1164213872Shselasky if ((lsr_delta & ULSR_PE) && (sc->sc_lsr & ULSR_PE)) { 1165213872Shselasky 1166213872Shselasky DPRINTF("Parity error detected\n"); 1167213872Shselasky 1168213872Shselasky ttydisc_rint(tp, 0, TRE_PARITY); 1169213872Shselasky ttydisc_rint_done(tp); 1170213872Shselasky } 1171184610Salfred} 1172184610Salfred 1173184610Salfredvoid 1174194228Sthompsaucom_status_change(struct ucom_softc *sc) 1175184610Salfred{ 1176239179Shselasky UCOM_MTX_ASSERT(sc, MA_OWNED); 1177184610Salfred 1178197570Sthompsa if (sc->sc_flag & UCOM_FLAG_CONSOLE) 1179197570Sthompsa return; /* not supported */ 1180197570Sthompsa 1181184610Salfred if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) { 1182184610Salfred return; 1183184610Salfred } 1184184610Salfred DPRINTF("\n"); 1185184610Salfred 1186194228Sthompsa ucom_queue_command(sc, ucom_cfg_status_change, NULL, 1187188413Sthompsa &sc->sc_status_task[0].hdr, 1188188413Sthompsa &sc->sc_status_task[1].hdr); 1189184610Salfred} 1190184610Salfred 1191184610Salfredstatic void 1192194228Sthompsaucom_cfg_param(struct usb_proc_msg *_task) 1193184610Salfred{ 1194192984Sthompsa struct ucom_param_task *task = 1195192984Sthompsa (struct ucom_param_task *)_task; 1196192984Sthompsa struct ucom_softc *sc = task->sc; 1197184610Salfred 1198184610Salfred if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) { 1199184610Salfred return; 1200184610Salfred } 1201194228Sthompsa if (sc->sc_callback->ucom_cfg_param == NULL) { 1202184610Salfred return; 1203184610Salfred } 1204184610Salfred 1205194228Sthompsa (sc->sc_callback->ucom_cfg_param) (sc, &task->termios_copy); 1206184610Salfred 1207184610Salfred /* wait a little */ 1208194228Sthompsa usb_pause_mtx(sc->sc_mtx, hz / 10); 1209184610Salfred} 1210184610Salfred 1211184610Salfredstatic int 1212194228Sthompsaucom_param(struct tty *tp, struct termios *t) 1213184610Salfred{ 1214192984Sthompsa struct ucom_softc *sc = tty_softc(tp); 1215184610Salfred uint8_t opened; 1216184610Salfred int error; 1217184610Salfred 1218239179Shselasky UCOM_MTX_ASSERT(sc, MA_OWNED); 1219184610Salfred 1220184610Salfred opened = 0; 1221184610Salfred error = 0; 1222184610Salfred 1223184610Salfred if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) { 1224184610Salfred 1225184610Salfred /* XXX the TTY layer should call "open()" first! */ 1226242695Shselasky /* 1227242695Shselasky * Not quite: Its ordering is partly backwards, but 1228242695Shselasky * some parameters must be set early in ttydev_open(), 1229242695Shselasky * possibly before calling ttydevsw_open(). 1230242695Shselasky */ 1231194228Sthompsa error = ucom_open(tp); 1232242695Shselasky if (error) 1233184610Salfred goto done; 1234242695Shselasky 1235184610Salfred opened = 1; 1236184610Salfred } 1237184610Salfred DPRINTF("sc = %p\n", sc); 1238184610Salfred 1239184610Salfred /* Check requested parameters. */ 1240184610Salfred if (t->c_ispeed && (t->c_ispeed != t->c_ospeed)) { 1241242695Shselasky /* XXX c_ospeed == 0 is perfectly valid. */ 1242184610Salfred DPRINTF("mismatch ispeed and ospeed\n"); 1243184610Salfred error = EINVAL; 1244184610Salfred goto done; 1245184610Salfred } 1246184610Salfred t->c_ispeed = t->c_ospeed; 1247184610Salfred 1248194228Sthompsa if (sc->sc_callback->ucom_pre_param) { 1249184610Salfred /* Let the lower layer verify the parameters */ 1250194228Sthompsa error = (sc->sc_callback->ucom_pre_param) (sc, t); 1251184610Salfred if (error) { 1252184610Salfred DPRINTF("callback error = %d\n", error); 1253184610Salfred goto done; 1254184610Salfred } 1255184610Salfred } 1256184610Salfred 1257184610Salfred /* Disable transfers */ 1258184610Salfred sc->sc_flag &= ~UCOM_FLAG_GP_DATA; 1259184610Salfred 1260184610Salfred /* Queue baud rate programming command first */ 1261194228Sthompsa ucom_queue_command(sc, ucom_cfg_param, t, 1262188413Sthompsa &sc->sc_param_task[0].hdr, 1263188413Sthompsa &sc->sc_param_task[1].hdr); 1264184610Salfred 1265184610Salfred /* Queue transfer enable command last */ 1266194228Sthompsa ucom_queue_command(sc, ucom_cfg_start_transfers, NULL, 1267188413Sthompsa &sc->sc_start_task[0].hdr, 1268188413Sthompsa &sc->sc_start_task[1].hdr); 1269184610Salfred 1270184610Salfred if (t->c_cflag & CRTS_IFLOW) { 1271184610Salfred sc->sc_flag |= UCOM_FLAG_RTS_IFLOW; 1272184610Salfred } else if (sc->sc_flag & UCOM_FLAG_RTS_IFLOW) { 1273184610Salfred sc->sc_flag &= ~UCOM_FLAG_RTS_IFLOW; 1274194228Sthompsa ucom_modem(tp, SER_RTS, 0); 1275184610Salfred } 1276184610Salfreddone: 1277184610Salfred if (error) { 1278184610Salfred if (opened) { 1279194228Sthompsa ucom_close(tp); 1280184610Salfred } 1281184610Salfred } 1282184610Salfred return (error); 1283184610Salfred} 1284184610Salfred 1285184610Salfredstatic void 1286194228Sthompsaucom_outwakeup(struct tty *tp) 1287184610Salfred{ 1288192984Sthompsa struct ucom_softc *sc = tty_softc(tp); 1289184610Salfred 1290239179Shselasky UCOM_MTX_ASSERT(sc, MA_OWNED); 1291184610Salfred 1292184610Salfred DPRINTF("sc = %p\n", sc); 1293184610Salfred 1294184610Salfred if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) { 1295184610Salfred /* The higher layer is not ready */ 1296184610Salfred return; 1297184610Salfred } 1298194228Sthompsa ucom_start_transfers(sc); 1299184610Salfred} 1300184610Salfred 1301184610Salfred/*------------------------------------------------------------------------* 1302194228Sthompsa * ucom_get_data 1303184610Salfred * 1304184610Salfred * Return values: 1305184610Salfred * 0: No data is available. 1306184610Salfred * Else: Data is available. 1307184610Salfred *------------------------------------------------------------------------*/ 1308184610Salfreduint8_t 1309194228Sthompsaucom_get_data(struct ucom_softc *sc, struct usb_page_cache *pc, 1310184610Salfred uint32_t offset, uint32_t len, uint32_t *actlen) 1311184610Salfred{ 1312192984Sthompsa struct usb_page_search res; 1313184610Salfred struct tty *tp = sc->sc_tty; 1314184610Salfred uint32_t cnt; 1315184610Salfred uint32_t offset_orig; 1316184610Salfred 1317239179Shselasky UCOM_MTX_ASSERT(sc, MA_OWNED); 1318184610Salfred 1319197570Sthompsa if (sc->sc_flag & UCOM_FLAG_CONSOLE) { 1320197570Sthompsa unsigned int temp; 1321197570Sthompsa 1322197570Sthompsa /* get total TX length */ 1323197570Sthompsa 1324197570Sthompsa temp = ucom_cons_tx_high - ucom_cons_tx_low; 1325197570Sthompsa temp %= UCOM_CONS_BUFSIZE; 1326197570Sthompsa 1327197570Sthompsa /* limit TX length */ 1328197570Sthompsa 1329197570Sthompsa if (temp > (UCOM_CONS_BUFSIZE - ucom_cons_tx_low)) 1330197570Sthompsa temp = (UCOM_CONS_BUFSIZE - ucom_cons_tx_low); 1331197570Sthompsa 1332197570Sthompsa if (temp > len) 1333197570Sthompsa temp = len; 1334197570Sthompsa 1335197570Sthompsa /* copy in data */ 1336197570Sthompsa 1337197570Sthompsa usbd_copy_in(pc, offset, ucom_cons_tx_buf + ucom_cons_tx_low, temp); 1338197570Sthompsa 1339197570Sthompsa /* update counters */ 1340197570Sthompsa 1341197570Sthompsa ucom_cons_tx_low += temp; 1342197570Sthompsa ucom_cons_tx_low %= UCOM_CONS_BUFSIZE; 1343197570Sthompsa 1344197570Sthompsa /* store actual length */ 1345197570Sthompsa 1346197570Sthompsa *actlen = temp; 1347197570Sthompsa 1348197570Sthompsa return (temp ? 1 : 0); 1349197570Sthompsa } 1350197570Sthompsa 1351192820Sthompsa if (tty_gone(tp) || 1352192820Sthompsa !(sc->sc_flag & UCOM_FLAG_GP_DATA)) { 1353184610Salfred actlen[0] = 0; 1354184610Salfred return (0); /* multiport device polling */ 1355184610Salfred } 1356184610Salfred offset_orig = offset; 1357184610Salfred 1358184610Salfred while (len != 0) { 1359184610Salfred 1360194228Sthompsa usbd_get_page(pc, offset, &res); 1361184610Salfred 1362184610Salfred if (res.length > len) { 1363184610Salfred res.length = len; 1364184610Salfred } 1365184610Salfred /* copy data directly into USB buffer */ 1366184610Salfred cnt = ttydisc_getc(tp, res.buffer, res.length); 1367184610Salfred 1368184610Salfred offset += cnt; 1369184610Salfred len -= cnt; 1370184610Salfred 1371184610Salfred if (cnt < res.length) { 1372184610Salfred /* end of buffer */ 1373184610Salfred break; 1374184610Salfred } 1375184610Salfred } 1376184610Salfred 1377184610Salfred actlen[0] = offset - offset_orig; 1378184610Salfred 1379184610Salfred DPRINTF("cnt=%d\n", actlen[0]); 1380184610Salfred 1381184610Salfred if (actlen[0] == 0) { 1382184610Salfred return (0); 1383184610Salfred } 1384184610Salfred return (1); 1385184610Salfred} 1386184610Salfred 1387184610Salfredvoid 1388194228Sthompsaucom_put_data(struct ucom_softc *sc, struct usb_page_cache *pc, 1389184610Salfred uint32_t offset, uint32_t len) 1390184610Salfred{ 1391192984Sthompsa struct usb_page_search res; 1392184610Salfred struct tty *tp = sc->sc_tty; 1393184610Salfred char *buf; 1394184610Salfred uint32_t cnt; 1395184610Salfred 1396239179Shselasky UCOM_MTX_ASSERT(sc, MA_OWNED); 1397184610Salfred 1398197570Sthompsa if (sc->sc_flag & UCOM_FLAG_CONSOLE) { 1399197570Sthompsa unsigned int temp; 1400197570Sthompsa 1401197570Sthompsa /* get maximum RX length */ 1402197570Sthompsa 1403197570Sthompsa temp = (UCOM_CONS_BUFSIZE - 1) - ucom_cons_rx_high + ucom_cons_rx_low; 1404197570Sthompsa temp %= UCOM_CONS_BUFSIZE; 1405197570Sthompsa 1406197570Sthompsa /* limit RX length */ 1407197570Sthompsa 1408197570Sthompsa if (temp > (UCOM_CONS_BUFSIZE - ucom_cons_rx_high)) 1409197570Sthompsa temp = (UCOM_CONS_BUFSIZE - ucom_cons_rx_high); 1410197570Sthompsa 1411197570Sthompsa if (temp > len) 1412197570Sthompsa temp = len; 1413197570Sthompsa 1414197570Sthompsa /* copy out data */ 1415197570Sthompsa 1416197570Sthompsa usbd_copy_out(pc, offset, ucom_cons_rx_buf + ucom_cons_rx_high, temp); 1417197570Sthompsa 1418197570Sthompsa /* update counters */ 1419197570Sthompsa 1420197570Sthompsa ucom_cons_rx_high += temp; 1421197570Sthompsa ucom_cons_rx_high %= UCOM_CONS_BUFSIZE; 1422197570Sthompsa 1423197570Sthompsa return; 1424197570Sthompsa } 1425197570Sthompsa 1426192820Sthompsa if (tty_gone(tp)) 1427184610Salfred return; /* multiport device polling */ 1428192820Sthompsa 1429184610Salfred if (len == 0) 1430184610Salfred return; /* no data */ 1431184610Salfred 1432184610Salfred /* set a flag to prevent recursation ? */ 1433184610Salfred 1434184610Salfred while (len > 0) { 1435184610Salfred 1436194228Sthompsa usbd_get_page(pc, offset, &res); 1437184610Salfred 1438184610Salfred if (res.length > len) { 1439184610Salfred res.length = len; 1440184610Salfred } 1441184610Salfred len -= res.length; 1442184610Salfred offset += res.length; 1443184610Salfred 1444184610Salfred /* pass characters to tty layer */ 1445184610Salfred 1446184610Salfred buf = res.buffer; 1447184610Salfred cnt = res.length; 1448184610Salfred 1449184610Salfred /* first check if we can pass the buffer directly */ 1450184610Salfred 1451184610Salfred if (ttydisc_can_bypass(tp)) { 1452242619Shselasky 1453242619Shselasky /* clear any jitter buffer */ 1454242619Shselasky sc->sc_jitterbuf_in = 0; 1455242619Shselasky sc->sc_jitterbuf_out = 0; 1456242619Shselasky 1457184610Salfred if (ttydisc_rint_bypass(tp, buf, cnt) != cnt) { 1458184610Salfred DPRINTF("tp=%p, data lost\n", tp); 1459184610Salfred } 1460184610Salfred continue; 1461184610Salfred } 1462184610Salfred /* need to loop */ 1463184610Salfred 1464184610Salfred for (cnt = 0; cnt != res.length; cnt++) { 1465242619Shselasky if (sc->sc_jitterbuf_in != sc->sc_jitterbuf_out || 1466242619Shselasky ttydisc_rint(tp, buf[cnt], 0) == -1) { 1467242619Shselasky uint16_t end; 1468242619Shselasky uint16_t pos; 1469184610Salfred 1470242619Shselasky pos = sc->sc_jitterbuf_in; 1471242619Shselasky end = sc->sc_jitterbuf_out + 1472242619Shselasky UCOM_JITTERBUF_SIZE - 1; 1473242619Shselasky if (end >= UCOM_JITTERBUF_SIZE) 1474242619Shselasky end -= UCOM_JITTERBUF_SIZE; 1475242619Shselasky 1476242619Shselasky for (; cnt != res.length; cnt++) { 1477242619Shselasky if (pos == end) 1478242619Shselasky break; 1479242619Shselasky sc->sc_jitterbuf[pos] = buf[cnt]; 1480242619Shselasky pos++; 1481242619Shselasky if (pos >= UCOM_JITTERBUF_SIZE) 1482242619Shselasky pos -= UCOM_JITTERBUF_SIZE; 1483242619Shselasky } 1484242619Shselasky 1485242619Shselasky sc->sc_jitterbuf_in = pos; 1486242619Shselasky 1487242619Shselasky /* set RTS in async fashion */ 1488242619Shselasky if (sc->sc_flag & UCOM_FLAG_RTS_IFLOW) 1489242619Shselasky ucom_rts(sc, 1); 1490242619Shselasky 1491184610Salfred DPRINTF("tp=%p, lost %d " 1492184610Salfred "chars\n", tp, res.length - cnt); 1493184610Salfred break; 1494184610Salfred } 1495184610Salfred } 1496184610Salfred } 1497184610Salfred ttydisc_rint_done(tp); 1498184610Salfred} 1499184610Salfred 1500184610Salfredstatic void 1501194228Sthompsaucom_free(void *xsc) 1502184610Salfred{ 1503192984Sthompsa struct ucom_softc *sc = xsc; 1504184610Salfred 1505239179Shselasky if (sc->sc_callback->ucom_free != NULL) 1506239179Shselasky sc->sc_callback->ucom_free(sc); 1507239179Shselasky else 1508239179Shselasky ucom_unref(sc->sc_super); 1509239179Shselasky 1510239179Shselasky mtx_lock(&ucom_mtx); 1511239179Shselasky ucom_close_refs--; 1512239179Shselasky mtx_unlock(&ucom_mtx); 1513184610Salfred} 1514197570Sthompsa 1515197570Sthompsastatic cn_probe_t ucom_cnprobe; 1516197570Sthompsastatic cn_init_t ucom_cninit; 1517197570Sthompsastatic cn_term_t ucom_cnterm; 1518197570Sthompsastatic cn_getc_t ucom_cngetc; 1519197570Sthompsastatic cn_putc_t ucom_cnputc; 1520228631Savgstatic cn_grab_t ucom_cngrab; 1521228631Savgstatic cn_ungrab_t ucom_cnungrab; 1522197570Sthompsa 1523197570SthompsaCONSOLE_DRIVER(ucom); 1524197570Sthompsa 1525197570Sthompsastatic void 1526197570Sthompsaucom_cnprobe(struct consdev *cp) 1527197570Sthompsa{ 1528198774Sthompsa if (ucom_cons_unit != -1) 1529198774Sthompsa cp->cn_pri = CN_NORMAL; 1530198774Sthompsa else 1531198774Sthompsa cp->cn_pri = CN_DEAD; 1532198774Sthompsa 1533198774Sthompsa strlcpy(cp->cn_name, "ucom", sizeof(cp->cn_name)); 1534197570Sthompsa} 1535197570Sthompsa 1536197570Sthompsastatic void 1537197570Sthompsaucom_cninit(struct consdev *cp) 1538197570Sthompsa{ 1539197570Sthompsa} 1540197570Sthompsa 1541197570Sthompsastatic void 1542197570Sthompsaucom_cnterm(struct consdev *cp) 1543197570Sthompsa{ 1544197570Sthompsa} 1545197570Sthompsa 1546228631Savgstatic void 1547228631Savgucom_cngrab(struct consdev *cp) 1548228631Savg{ 1549228631Savg} 1550228631Savg 1551228631Savgstatic void 1552228631Savgucom_cnungrab(struct consdev *cp) 1553228631Savg{ 1554228631Savg} 1555228631Savg 1556197570Sthompsastatic int 1557197570Sthompsaucom_cngetc(struct consdev *cd) 1558197570Sthompsa{ 1559197570Sthompsa struct ucom_softc *sc = ucom_cons_softc; 1560197570Sthompsa int c; 1561197570Sthompsa 1562197570Sthompsa if (sc == NULL) 1563197570Sthompsa return (-1); 1564197570Sthompsa 1565239179Shselasky UCOM_MTX_LOCK(sc); 1566197570Sthompsa 1567197570Sthompsa if (ucom_cons_rx_low != ucom_cons_rx_high) { 1568197570Sthompsa c = ucom_cons_rx_buf[ucom_cons_rx_low]; 1569197570Sthompsa ucom_cons_rx_low ++; 1570197570Sthompsa ucom_cons_rx_low %= UCOM_CONS_BUFSIZE; 1571197570Sthompsa } else { 1572197570Sthompsa c = -1; 1573197570Sthompsa } 1574197570Sthompsa 1575197570Sthompsa /* start USB transfers */ 1576197570Sthompsa ucom_outwakeup(sc->sc_tty); 1577197570Sthompsa 1578239179Shselasky UCOM_MTX_UNLOCK(sc); 1579197570Sthompsa 1580197570Sthompsa /* poll if necessary */ 1581197570Sthompsa if (kdb_active && sc->sc_callback->ucom_poll) 1582197570Sthompsa (sc->sc_callback->ucom_poll) (sc); 1583197570Sthompsa 1584197570Sthompsa return (c); 1585197570Sthompsa} 1586197570Sthompsa 1587197570Sthompsastatic void 1588197570Sthompsaucom_cnputc(struct consdev *cd, int c) 1589197570Sthompsa{ 1590197570Sthompsa struct ucom_softc *sc = ucom_cons_softc; 1591197570Sthompsa unsigned int temp; 1592197570Sthompsa 1593197570Sthompsa if (sc == NULL) 1594197570Sthompsa return; 1595197570Sthompsa 1596197570Sthompsa repeat: 1597197570Sthompsa 1598239179Shselasky UCOM_MTX_LOCK(sc); 1599197570Sthompsa 1600197570Sthompsa /* compute maximum TX length */ 1601197570Sthompsa 1602197570Sthompsa temp = (UCOM_CONS_BUFSIZE - 1) - ucom_cons_tx_high + ucom_cons_tx_low; 1603197570Sthompsa temp %= UCOM_CONS_BUFSIZE; 1604197570Sthompsa 1605197570Sthompsa if (temp) { 1606197570Sthompsa ucom_cons_tx_buf[ucom_cons_tx_high] = c; 1607197570Sthompsa ucom_cons_tx_high ++; 1608197570Sthompsa ucom_cons_tx_high %= UCOM_CONS_BUFSIZE; 1609197570Sthompsa } 1610197570Sthompsa 1611197570Sthompsa /* start USB transfers */ 1612197570Sthompsa ucom_outwakeup(sc->sc_tty); 1613197570Sthompsa 1614239179Shselasky UCOM_MTX_UNLOCK(sc); 1615197570Sthompsa 1616197570Sthompsa /* poll if necessary */ 1617197570Sthompsa if (kdb_active && sc->sc_callback->ucom_poll) { 1618197570Sthompsa (sc->sc_callback->ucom_poll) (sc); 1619197570Sthompsa /* simple flow control */ 1620197570Sthompsa if (temp == 0) 1621197570Sthompsa goto repeat; 1622197570Sthompsa } 1623197570Sthompsa} 1624197570Sthompsa 1625239179Shselasky/*------------------------------------------------------------------------* 1626239179Shselasky * ucom_ref 1627239179Shselasky * 1628239179Shselasky * This function will increment the super UCOM reference count. 1629239179Shselasky *------------------------------------------------------------------------*/ 1630239179Shselaskyvoid 1631239179Shselaskyucom_ref(struct ucom_super_softc *ssc) 1632239179Shselasky{ 1633239179Shselasky mtx_lock(&ucom_mtx); 1634239179Shselasky ssc->sc_refs++; 1635239179Shselasky mtx_unlock(&ucom_mtx); 1636239179Shselasky} 1637239179Shselasky 1638239179Shselasky/*------------------------------------------------------------------------* 1639239299Shselasky * ucom_free_unit 1640239299Shselasky * 1641239299Shselasky * This function will free the super UCOM's allocated unit 1642239299Shselasky * number. This function can be called on a zero-initialized 1643239299Shselasky * structure. This function can be called multiple times. 1644239299Shselasky *------------------------------------------------------------------------*/ 1645239299Shselaskystatic void 1646239299Shselaskyucom_free_unit(struct ucom_super_softc *ssc) 1647239299Shselasky{ 1648239299Shselasky if (!(ssc->sc_flag & UCOM_FLAG_FREE_UNIT)) 1649239299Shselasky return; 1650239299Shselasky 1651239299Shselasky ucom_unit_free(ssc->sc_unit); 1652239299Shselasky 1653239299Shselasky ssc->sc_flag &= ~UCOM_FLAG_FREE_UNIT; 1654239299Shselasky} 1655239299Shselasky 1656239299Shselasky/*------------------------------------------------------------------------* 1657239179Shselasky * ucom_unref 1658239179Shselasky * 1659239179Shselasky * This function will decrement the super UCOM reference count. 1660239179Shselasky * 1661239179Shselasky * Return values: 1662239179Shselasky * 0: UCOM structures are still referenced. 1663239179Shselasky * Else: UCOM structures are no longer referenced. 1664239179Shselasky *------------------------------------------------------------------------*/ 1665239179Shselaskyint 1666239179Shselaskyucom_unref(struct ucom_super_softc *ssc) 1667239179Shselasky{ 1668239179Shselasky int retval; 1669239179Shselasky 1670239179Shselasky mtx_lock(&ucom_mtx); 1671239179Shselasky retval = (ssc->sc_refs < 2); 1672239179Shselasky ssc->sc_refs--; 1673239179Shselasky mtx_unlock(&ucom_mtx); 1674239179Shselasky 1675239299Shselasky if (retval) 1676239299Shselasky ucom_free_unit(ssc); 1677239299Shselasky 1678239179Shselasky return (retval); 1679239179Shselasky} 1680239179Shselasky 1681197570Sthompsa#if defined(GDB) 1682197570Sthompsa 1683197570Sthompsa#include <gdb/gdb.h> 1684197570Sthompsa 1685197570Sthompsastatic gdb_probe_f ucom_gdbprobe; 1686197570Sthompsastatic gdb_init_f ucom_gdbinit; 1687197570Sthompsastatic gdb_term_f ucom_gdbterm; 1688197570Sthompsastatic gdb_getc_f ucom_gdbgetc; 1689197570Sthompsastatic gdb_putc_f ucom_gdbputc; 1690197570Sthompsa 1691197570SthompsaGDB_DBGPORT(sio, ucom_gdbprobe, ucom_gdbinit, ucom_gdbterm, ucom_gdbgetc, ucom_gdbputc); 1692197570Sthompsa 1693197570Sthompsastatic int 1694197570Sthompsaucom_gdbprobe(void) 1695197570Sthompsa{ 1696197570Sthompsa return ((ucom_cons_softc != NULL) ? 0 : -1); 1697197570Sthompsa} 1698197570Sthompsa 1699197570Sthompsastatic void 1700197570Sthompsaucom_gdbinit(void) 1701197570Sthompsa{ 1702197570Sthompsa} 1703197570Sthompsa 1704197570Sthompsastatic void 1705197570Sthompsaucom_gdbterm(void) 1706197570Sthompsa{ 1707197570Sthompsa} 1708197570Sthompsa 1709197570Sthompsastatic void 1710197570Sthompsaucom_gdbputc(int c) 1711197570Sthompsa{ 1712197570Sthompsa ucom_cnputc(NULL, c); 1713197570Sthompsa} 1714197570Sthompsa 1715197570Sthompsastatic int 1716197570Sthompsaucom_gdbgetc(void) 1717197570Sthompsa{ 1718197570Sthompsa return (ucom_cngetc(NULL)); 1719197570Sthompsa} 1720197570Sthompsa 1721197570Sthompsa#endif 1722