The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/dev/usb/serial/usb_serial.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*      $NetBSD: ucom.c,v 1.40 2001/11/13 06:24:54 lukem Exp $  */
    2 
    3 /*-
    4  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD AND BSD-2-Clause-NetBSD
    5  *
    6  * Copyright (c) 2001-2003, 2005, 2008
    7  *      Shunsuke Akiyama <akiyama@jp.FreeBSD.org>.
    8  * All rights reserved.
    9  *
   10  * Redistribution and use in source and binary forms, with or without
   11  * modification, are permitted provided that the following conditions
   12  * are met:
   13  * 1. Redistributions of source code must retain the above copyright
   14  *    notice, this list of conditions and the following disclaimer.
   15  * 2. Redistributions in binary form must reproduce the above copyright
   16  *    notice, this list of conditions and the following disclaimer in the
   17  *    documentation and/or other materials provided with the distribution.
   18  *
   19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   29  * SUCH DAMAGE.
   30  */
   31 
   32 #include <sys/cdefs.h>
   33 __FBSDID("$FreeBSD$");
   34 
   35 /*-
   36  * Copyright (c) 1998, 2000 The NetBSD Foundation, Inc.
   37  * All rights reserved.
   38  *
   39  * This code is derived from software contributed to The NetBSD Foundation
   40  * by Lennart Augustsson (lennart@augustsson.net) at
   41  * Carlstedt Research & Technology.
   42  *
   43  * Redistribution and use in source and binary forms, with or without
   44  * modification, are permitted provided that the following conditions
   45  * are met:
   46  * 1. Redistributions of source code must retain the above copyright
   47  *    notice, this list of conditions and the following disclaimer.
   48  * 2. Redistributions in binary form must reproduce the above copyright
   49  *    notice, this list of conditions and the following disclaimer in the
   50  *    documentation and/or other materials provided with the distribution.
   51  *
   52  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
   53  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
   54  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
   55  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
   56  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
   57  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
   58  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
   59  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
   60  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
   61  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   62  * POSSIBILITY OF SUCH DAMAGE.
   63  */
   64 
   65 #include <sys/stdint.h>
   66 #include <sys/stddef.h>
   67 #include <sys/param.h>
   68 #include <sys/queue.h>
   69 #include <sys/types.h>
   70 #include <sys/systm.h>
   71 #include <sys/kernel.h>
   72 #include <sys/bus.h>
   73 #include <sys/module.h>
   74 #include <sys/lock.h>
   75 #include <sys/mutex.h>
   76 #include <sys/condvar.h>
   77 #include <sys/sysctl.h>
   78 #include <sys/sx.h>
   79 #include <sys/unistd.h>
   80 #include <sys/callout.h>
   81 #include <sys/malloc.h>
   82 #include <sys/priv.h>
   83 #include <sys/cons.h>
   84 
   85 #include <dev/uart/uart_ppstypes.h>
   86 
   87 #include <dev/usb/usb.h>
   88 #include <dev/usb/usbdi.h>
   89 #include <dev/usb/usbdi_util.h>
   90 
   91 #define USB_DEBUG_VAR ucom_debug
   92 #include <dev/usb/usb_debug.h>
   93 #include <dev/usb/usb_busdma.h>
   94 #include <dev/usb/usb_process.h>
   95 
   96 #include <dev/usb/serial/usb_serial.h>
   97 
   98 #include "opt_gdb.h"
   99 
  100 static SYSCTL_NODE(_hw_usb, OID_AUTO, ucom, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
  101     "USB ucom");
  102 
  103 static int ucom_pps_mode;
  104 
  105 SYSCTL_INT(_hw_usb_ucom, OID_AUTO, pps_mode, CTLFLAG_RWTUN,
  106     &ucom_pps_mode, 0, 
  107     "pulse capture mode: 0/1/2=disabled/CTS/DCD; add 0x10 to invert");
  108 
  109 static int ucom_device_mode_console = 1;
  110 
  111 SYSCTL_INT(_hw_usb_ucom, OID_AUTO, device_mode_console, CTLFLAG_RW,
  112     &ucom_device_mode_console, 0,
  113     "set to 1 to mark terminals as consoles when in device mode");
  114 
  115 #ifdef USB_DEBUG
  116 static int ucom_debug = 0;
  117 
  118 SYSCTL_INT(_hw_usb_ucom, OID_AUTO, debug, CTLFLAG_RWTUN,
  119     &ucom_debug, 0, "ucom debug level");
  120 #endif
  121 
  122 #define UCOM_CONS_BUFSIZE 1024
  123 
  124 static uint8_t ucom_cons_rx_buf[UCOM_CONS_BUFSIZE];
  125 static uint8_t ucom_cons_tx_buf[UCOM_CONS_BUFSIZE];
  126 
  127 static unsigned ucom_cons_rx_low = 0;
  128 static unsigned ucom_cons_rx_high = 0;
  129 
  130 static unsigned ucom_cons_tx_low = 0;
  131 static unsigned ucom_cons_tx_high = 0;
  132 
  133 static int ucom_cons_unit = -1;
  134 static int ucom_cons_subunit = 0;
  135 static int ucom_cons_baud = 9600;
  136 static struct ucom_softc *ucom_cons_softc = NULL;
  137 
  138 SYSCTL_INT(_hw_usb_ucom, OID_AUTO, cons_unit, CTLFLAG_RWTUN,
  139     &ucom_cons_unit, 0, "console unit number");
  140 SYSCTL_INT(_hw_usb_ucom, OID_AUTO, cons_subunit, CTLFLAG_RWTUN,
  141     &ucom_cons_subunit, 0, "console subunit number");
  142 SYSCTL_INT(_hw_usb_ucom, OID_AUTO, cons_baud, CTLFLAG_RWTUN,
  143     &ucom_cons_baud, 0, "console baud rate");
  144 
  145 static usb_proc_callback_t ucom_cfg_start_transfers;
  146 static usb_proc_callback_t ucom_cfg_open;
  147 static usb_proc_callback_t ucom_cfg_close;
  148 static usb_proc_callback_t ucom_cfg_line_state;
  149 static usb_proc_callback_t ucom_cfg_status_change;
  150 static usb_proc_callback_t ucom_cfg_param;
  151 
  152 static int      ucom_unit_alloc(void);
  153 static void     ucom_unit_free(int);
  154 static int      ucom_attach_tty(struct ucom_super_softc *, struct ucom_softc *);
  155 static void     ucom_detach_tty(struct ucom_super_softc *, struct ucom_softc *);
  156 static void     ucom_queue_command(struct ucom_softc *,
  157                     usb_proc_callback_t *, struct termios *pt,
  158                     struct usb_proc_msg *t0, struct usb_proc_msg *t1);
  159 static void     ucom_shutdown(struct ucom_softc *);
  160 static void     ucom_ring(struct ucom_softc *, uint8_t);
  161 static void     ucom_break(struct ucom_softc *, uint8_t);
  162 static void     ucom_dtr(struct ucom_softc *, uint8_t);
  163 static void     ucom_rts(struct ucom_softc *, uint8_t);
  164 
  165 static tsw_open_t ucom_open;
  166 static tsw_close_t ucom_close;
  167 static tsw_ioctl_t ucom_ioctl;
  168 static tsw_modem_t ucom_modem;
  169 static tsw_param_t ucom_param;
  170 static tsw_outwakeup_t ucom_outwakeup;
  171 static tsw_inwakeup_t ucom_inwakeup;
  172 static tsw_free_t ucom_free;
  173 static tsw_busy_t ucom_busy;
  174 
  175 static struct ttydevsw ucom_class = {
  176         .tsw_flags = TF_INITLOCK | TF_CALLOUT,
  177         .tsw_open = ucom_open,
  178         .tsw_close = ucom_close,
  179         .tsw_outwakeup = ucom_outwakeup,
  180         .tsw_inwakeup = ucom_inwakeup,
  181         .tsw_ioctl = ucom_ioctl,
  182         .tsw_param = ucom_param,
  183         .tsw_modem = ucom_modem,
  184         .tsw_free = ucom_free,
  185         .tsw_busy = ucom_busy,
  186 };
  187 
  188 MODULE_DEPEND(ucom, usb, 1, 1, 1);
  189 MODULE_VERSION(ucom, 1);
  190 
  191 #define UCOM_UNIT_MAX           128     /* maximum number of units */
  192 #define UCOM_TTY_PREFIX         "U"
  193 
  194 static struct unrhdr *ucom_unrhdr;
  195 static struct mtx ucom_mtx;
  196 static int ucom_close_refs;
  197 
  198 static void
  199 ucom_init(void *arg)
  200 {
  201         DPRINTF("\n");
  202         ucom_unrhdr = new_unrhdr(0, UCOM_UNIT_MAX - 1, NULL);
  203         mtx_init(&ucom_mtx, "UCOM MTX", NULL, MTX_DEF);
  204 }
  205 SYSINIT(ucom_init, SI_SUB_KLD - 1, SI_ORDER_ANY, ucom_init, NULL);
  206 
  207 static void
  208 ucom_uninit(void *arg)
  209 {
  210         struct unrhdr *hdr;
  211         hdr = ucom_unrhdr;
  212         ucom_unrhdr = NULL;
  213 
  214         DPRINTF("\n");
  215 
  216         if (hdr != NULL)
  217                 delete_unrhdr(hdr);
  218 
  219         mtx_destroy(&ucom_mtx);
  220 }
  221 SYSUNINIT(ucom_uninit, SI_SUB_KLD - 3, SI_ORDER_ANY, ucom_uninit, NULL);
  222 
  223 /*
  224  * Mark a unit number (the X in cuaUX) as in use.
  225  *
  226  * Note that devices using a different naming scheme (see ucom_tty_name()
  227  * callback) still use this unit allocation.
  228  */
  229 static int
  230 ucom_unit_alloc(void)
  231 {
  232         int unit;
  233 
  234         /* sanity checks */
  235         if (ucom_unrhdr == NULL) {
  236                 DPRINTF("ucom_unrhdr is NULL\n");
  237                 return (-1);
  238         }
  239         unit = alloc_unr(ucom_unrhdr);
  240         DPRINTF("unit %d is allocated\n", unit);
  241         return (unit);
  242 }
  243 
  244 /*
  245  * Mark the unit number as not in use.
  246  */
  247 static void
  248 ucom_unit_free(int unit)
  249 {
  250         /* sanity checks */
  251         if (unit < 0 || unit >= UCOM_UNIT_MAX || ucom_unrhdr == NULL) {
  252                 DPRINTF("cannot free unit number\n");
  253                 return;
  254         }
  255         DPRINTF("unit %d is freed\n", unit);
  256         free_unr(ucom_unrhdr, unit);
  257 }
  258 
  259 /*
  260  * Setup a group of one or more serial ports.
  261  *
  262  * The mutex pointed to by "mtx" is applied before all
  263  * callbacks are called back. Also "mtx" must be applied
  264  * before calling into the ucom-layer!
  265  */
  266 int
  267 ucom_attach(struct ucom_super_softc *ssc, struct ucom_softc *sc,
  268     int subunits, void *parent,
  269     const struct ucom_callback *callback, struct mtx *mtx)
  270 {
  271         int subunit;
  272         int error = 0;
  273 
  274         if ((sc == NULL) ||
  275             (subunits <= 0) ||
  276             (callback == NULL) ||
  277             (mtx == NULL)) {
  278                 return (EINVAL);
  279         }
  280 
  281         /* allocate a uniq unit number */
  282         ssc->sc_unit = ucom_unit_alloc();
  283         if (ssc->sc_unit == -1)
  284                 return (ENOMEM);
  285 
  286         /* generate TTY name string */
  287         snprintf(ssc->sc_ttyname, sizeof(ssc->sc_ttyname),
  288             UCOM_TTY_PREFIX "%d", ssc->sc_unit);
  289 
  290         /* create USB request handling process */
  291         error = usb_proc_create(&ssc->sc_tq, mtx, "ucom", USB_PRI_MED);
  292         if (error) {
  293                 ucom_unit_free(ssc->sc_unit);
  294                 return (error);
  295         }
  296         ssc->sc_subunits = subunits;
  297         ssc->sc_flag = UCOM_FLAG_ATTACHED |
  298             UCOM_FLAG_FREE_UNIT | (ssc->sc_flag & UCOM_FLAG_DEVICE_MODE);
  299 
  300         if (callback->ucom_free == NULL)
  301                 ssc->sc_flag |= UCOM_FLAG_WAIT_REFS;
  302 
  303         /* increment reference count */
  304         ucom_ref(ssc);
  305 
  306         for (subunit = 0; subunit < ssc->sc_subunits; subunit++) {
  307                 sc[subunit].sc_subunit = subunit;
  308                 sc[subunit].sc_super = ssc;
  309                 sc[subunit].sc_mtx = mtx;
  310                 sc[subunit].sc_parent = parent;
  311                 sc[subunit].sc_callback = callback;
  312 
  313                 error = ucom_attach_tty(ssc, &sc[subunit]);
  314                 if (error) {
  315                         ucom_detach(ssc, &sc[0]);
  316                         return (error);
  317                 }
  318                 /* increment reference count */
  319                 ucom_ref(ssc);
  320 
  321                 /* set subunit attached */
  322                 sc[subunit].sc_flag |= UCOM_FLAG_ATTACHED;
  323         }
  324 
  325         DPRINTF("tp = %p, unit = %d, subunits = %d\n",
  326                 sc->sc_tty, ssc->sc_unit, ssc->sc_subunits);
  327 
  328         return (0);
  329 }
  330 
  331 /*
  332  * The following function will do nothing if the structure pointed to
  333  * by "ssc" and "sc" is zero or has already been detached.
  334  */
  335 void
  336 ucom_detach(struct ucom_super_softc *ssc, struct ucom_softc *sc)
  337 {
  338         int subunit;
  339 
  340         if (!(ssc->sc_flag & UCOM_FLAG_ATTACHED))
  341                 return;         /* not initialized */
  342 
  343         if (ssc->sc_sysctl_ttyname != NULL) {
  344                 sysctl_remove_oid(ssc->sc_sysctl_ttyname, 1, 0);
  345                 ssc->sc_sysctl_ttyname = NULL;
  346         }
  347 
  348         if (ssc->sc_sysctl_ttyports != NULL) {
  349                 sysctl_remove_oid(ssc->sc_sysctl_ttyports, 1, 0);
  350                 ssc->sc_sysctl_ttyports = NULL;
  351         }
  352 
  353         usb_proc_drain(&ssc->sc_tq);
  354 
  355         for (subunit = 0; subunit < ssc->sc_subunits; subunit++) {
  356                 if (sc[subunit].sc_flag & UCOM_FLAG_ATTACHED) {
  357                         ucom_detach_tty(ssc, &sc[subunit]);
  358 
  359                         /* avoid duplicate detach */
  360                         sc[subunit].sc_flag &= ~UCOM_FLAG_ATTACHED;
  361                 }
  362         }
  363         usb_proc_free(&ssc->sc_tq);
  364 
  365         ucom_unref(ssc);
  366 
  367         if (ssc->sc_flag & UCOM_FLAG_WAIT_REFS)
  368                 ucom_drain(ssc);
  369 
  370         /* make sure we don't detach twice */
  371         ssc->sc_flag &= ~UCOM_FLAG_ATTACHED;
  372 }
  373 
  374 void
  375 ucom_drain(struct ucom_super_softc *ssc)
  376 {
  377         mtx_lock(&ucom_mtx);
  378         while (ssc->sc_refs > 0) {
  379                 printf("ucom: Waiting for a TTY device to close.\n");
  380                 usb_pause_mtx(&ucom_mtx, hz);
  381         }
  382         mtx_unlock(&ucom_mtx);
  383 }
  384 
  385 void
  386 ucom_drain_all(void *arg)
  387 {
  388         mtx_lock(&ucom_mtx);
  389         while (ucom_close_refs > 0) {
  390                 printf("ucom: Waiting for all detached TTY "
  391                     "devices to have open fds closed.\n");
  392                 usb_pause_mtx(&ucom_mtx, hz);
  393         }
  394         mtx_unlock(&ucom_mtx);
  395 }
  396 
  397 static cn_probe_t ucom_cnprobe;
  398 static cn_init_t ucom_cninit;
  399 static cn_term_t ucom_cnterm;
  400 static cn_getc_t ucom_cngetc;
  401 static cn_putc_t ucom_cnputc;
  402 static cn_grab_t ucom_cngrab;
  403 static cn_ungrab_t ucom_cnungrab;
  404 
  405 const struct consdev_ops ucom_cnops = {
  406         .cn_probe       = ucom_cnprobe,
  407         .cn_init        = ucom_cninit,
  408         .cn_term        = ucom_cnterm,
  409         .cn_getc        = ucom_cngetc,
  410         .cn_putc        = ucom_cnputc,
  411         .cn_grab        = ucom_cngrab,
  412         .cn_ungrab      = ucom_cnungrab,
  413 };
  414 
  415 static int
  416 ucom_attach_tty(struct ucom_super_softc *ssc, struct ucom_softc *sc)
  417 {
  418         struct tty *tp;
  419         char buf[32];                   /* temporary TTY device name buffer */
  420 
  421         tp = tty_alloc_mutex(&ucom_class, sc, sc->sc_mtx);
  422         if (tp == NULL)
  423                 return (ENOMEM);
  424 
  425         /* Check if the client has a custom TTY name */
  426         buf[0] = '\0';
  427         if (sc->sc_callback->ucom_tty_name) {
  428                 sc->sc_callback->ucom_tty_name(sc, buf,
  429                     sizeof(buf), ssc->sc_unit, sc->sc_subunit);
  430         }
  431         if (buf[0] == 0) {
  432                 /* Use default TTY name */
  433                 if (ssc->sc_subunits > 1) {
  434                         /* multiple modems in one */
  435                         snprintf(buf, sizeof(buf), UCOM_TTY_PREFIX "%u.%u",
  436                             ssc->sc_unit, sc->sc_subunit);
  437                 } else {
  438                         /* single modem */
  439                         snprintf(buf, sizeof(buf), UCOM_TTY_PREFIX "%u",
  440                             ssc->sc_unit);
  441                 }
  442         }
  443         tty_makedev(tp, NULL, "%s", buf);
  444 
  445         sc->sc_tty = tp;
  446 
  447         sc->sc_pps.ppscap = PPS_CAPTUREBOTH;
  448         sc->sc_pps.driver_abi = PPS_ABI_VERSION;
  449         sc->sc_pps.driver_mtx = sc->sc_mtx;
  450         pps_init_abi(&sc->sc_pps);
  451 
  452         DPRINTF("ttycreate: %s\n", buf);
  453 
  454         /* Check if this device should be a console */
  455         if ((ucom_cons_softc == NULL) && 
  456             (ssc->sc_unit == ucom_cons_unit) &&
  457             (sc->sc_subunit == ucom_cons_subunit)) {
  458                 DPRINTF("unit %d subunit %d is console",
  459                     ssc->sc_unit, sc->sc_subunit);
  460 
  461                 ucom_cons_softc = sc;
  462 
  463                 tty_init_console(tp, ucom_cons_baud);
  464 
  465                 UCOM_MTX_LOCK(ucom_cons_softc);
  466                 ucom_cons_rx_low = 0;
  467                 ucom_cons_rx_high = 0;
  468                 ucom_cons_tx_low = 0;
  469                 ucom_cons_tx_high = 0;
  470                 sc->sc_flag |= UCOM_FLAG_CONSOLE;
  471                 ucom_open(ucom_cons_softc->sc_tty);
  472                 ucom_param(ucom_cons_softc->sc_tty, &tp->t_termios_init_in);
  473                 UCOM_MTX_UNLOCK(ucom_cons_softc);
  474         }
  475 
  476         if ((ssc->sc_flag & UCOM_FLAG_DEVICE_MODE) != 0 &&
  477             ucom_device_mode_console > 0 &&
  478             ucom_cons_softc == NULL) {
  479                 struct consdev *cp;
  480 
  481                 cp = malloc(sizeof(struct consdev), M_USBDEV,
  482                     M_WAITOK|M_ZERO);
  483                 cp->cn_ops = &ucom_cnops;
  484                 cp->cn_arg = NULL;
  485                 cp->cn_pri = CN_NORMAL;
  486                 strlcpy(cp->cn_name, "tty", sizeof(cp->cn_name));
  487                 strlcat(cp->cn_name, buf, sizeof(cp->cn_name));
  488 
  489                 sc->sc_consdev = cp;
  490 
  491                 cnadd(cp);
  492         }
  493 
  494         return (0);
  495 }
  496 
  497 static void
  498 ucom_detach_tty(struct ucom_super_softc *ssc, struct ucom_softc *sc)
  499 {
  500         struct tty *tp = sc->sc_tty;
  501 
  502         DPRINTF("sc = %p, tp = %p\n", sc, sc->sc_tty);
  503 
  504         if (sc->sc_consdev != NULL) {
  505                 cnremove(sc->sc_consdev);
  506                 free(sc->sc_consdev, M_USBDEV);
  507                 sc->sc_consdev = NULL;
  508         }
  509 
  510         if (sc->sc_flag & UCOM_FLAG_CONSOLE) {
  511                 UCOM_MTX_LOCK(ucom_cons_softc);
  512                 ucom_close(ucom_cons_softc->sc_tty);
  513                 sc->sc_flag &= ~UCOM_FLAG_CONSOLE;
  514                 UCOM_MTX_UNLOCK(ucom_cons_softc);
  515                 ucom_cons_softc = NULL;
  516         }
  517 
  518         /* the config thread has been stopped when we get here */
  519 
  520         UCOM_MTX_LOCK(sc);
  521         sc->sc_flag |= UCOM_FLAG_GONE;
  522         sc->sc_flag &= ~(UCOM_FLAG_HL_READY | UCOM_FLAG_LL_READY);
  523         UCOM_MTX_UNLOCK(sc);
  524 
  525         if (tp) {
  526                 mtx_lock(&ucom_mtx);
  527                 ucom_close_refs++;
  528                 mtx_unlock(&ucom_mtx);
  529 
  530                 tty_lock(tp);
  531 
  532                 ucom_close(tp); /* close, if any */
  533 
  534                 tty_rel_gone(tp);
  535 
  536                 UCOM_MTX_LOCK(sc);
  537                 /*
  538                  * make sure that read and write transfers are stopped
  539                  */
  540                 if (sc->sc_callback->ucom_stop_read)
  541                         (sc->sc_callback->ucom_stop_read) (sc);
  542                 if (sc->sc_callback->ucom_stop_write)
  543                         (sc->sc_callback->ucom_stop_write) (sc);
  544                 UCOM_MTX_UNLOCK(sc);
  545         }
  546 }
  547 
  548 void
  549 ucom_set_pnpinfo_usb(struct ucom_super_softc *ssc, device_t dev)
  550 {
  551         char buf[64];
  552         uint8_t iface_index;
  553         struct usb_attach_arg *uaa;
  554 
  555         snprintf(buf, sizeof(buf), "ttyname=" UCOM_TTY_PREFIX
  556             "%d ttyports=%d", ssc->sc_unit, ssc->sc_subunits);
  557 
  558         /* Store the PNP info in the first interface for the device */
  559         uaa = device_get_ivars(dev);
  560         iface_index = uaa->info.bIfaceIndex;
  561     
  562         if (usbd_set_pnpinfo(uaa->device, iface_index, buf) != 0)
  563                 device_printf(dev, "Could not set PNP info\n");
  564 
  565         /*
  566          * The following information is also replicated in the PNP-info
  567          * string which is registered above:
  568          */
  569         if (ssc->sc_sysctl_ttyname == NULL) {
  570                 ssc->sc_sysctl_ttyname = SYSCTL_ADD_STRING(NULL,
  571                     SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
  572                     OID_AUTO, "ttyname", CTLFLAG_RD, ssc->sc_ttyname, 0,
  573                     "TTY device basename");
  574         }
  575         if (ssc->sc_sysctl_ttyports == NULL) {
  576                 ssc->sc_sysctl_ttyports = SYSCTL_ADD_INT(NULL,
  577                     SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
  578                     OID_AUTO, "ttyports", CTLFLAG_RD,
  579                     NULL, ssc->sc_subunits, "Number of ports");
  580         }
  581 }
  582 
  583 void
  584 ucom_set_usb_mode(struct ucom_super_softc *ssc, enum usb_hc_mode usb_mode)
  585 {
  586 
  587         switch (usb_mode) {
  588         case USB_MODE_DEVICE:
  589                 ssc->sc_flag |= UCOM_FLAG_DEVICE_MODE;
  590                 break;
  591         default:
  592                 ssc->sc_flag &= ~UCOM_FLAG_DEVICE_MODE;
  593                 break;
  594         }
  595 }
  596 
  597 static void
  598 ucom_queue_command(struct ucom_softc *sc,
  599     usb_proc_callback_t *fn, struct termios *pt,
  600     struct usb_proc_msg *t0, struct usb_proc_msg *t1)
  601 {
  602         struct ucom_super_softc *ssc = sc->sc_super;
  603         struct ucom_param_task *task;
  604 
  605         UCOM_MTX_ASSERT(sc, MA_OWNED);
  606 
  607         if (usb_proc_is_gone(&ssc->sc_tq)) {
  608                 DPRINTF("proc is gone\n");
  609                 return;         /* nothing to do */
  610         }
  611         /* 
  612          * NOTE: The task cannot get executed before we drop the
  613          * "sc_mtx" mutex. It is safe to update fields in the message
  614          * structure after that the message got queued.
  615          */
  616         task = (struct ucom_param_task *)
  617           usb_proc_msignal(&ssc->sc_tq, t0, t1);
  618 
  619         /* Setup callback and softc pointers */
  620         task->hdr.pm_callback = fn;
  621         task->sc = sc;
  622 
  623         /* 
  624          * Make a copy of the termios. This field is only present if
  625          * the "pt" field is not NULL.
  626          */
  627         if (pt != NULL)
  628                 task->termios_copy = *pt;
  629 
  630         /*
  631          * Closing or opening the device should be synchronous.
  632          */
  633         if (fn == ucom_cfg_close || fn == ucom_cfg_open)
  634                 usb_proc_mwait(&ssc->sc_tq, t0, t1);
  635 
  636         /*
  637          * In case of multiple configure requests,
  638          * keep track of the last one!
  639          */
  640         if (fn == ucom_cfg_start_transfers)
  641                 sc->sc_last_start_xfer = &task->hdr;
  642 }
  643 
  644 static void
  645 ucom_shutdown(struct ucom_softc *sc)
  646 {
  647         struct tty *tp = sc->sc_tty;
  648 
  649         UCOM_MTX_ASSERT(sc, MA_OWNED);
  650 
  651         DPRINTF("\n");
  652 
  653         /*
  654          * Hang up if necessary:
  655          */
  656         if (tp->t_termios.c_cflag & HUPCL) {
  657                 ucom_modem(tp, 0, SER_DTR);
  658         }
  659 }
  660 
  661 /*
  662  * Return values:
  663  *    0: normal
  664  * else: taskqueue is draining or gone
  665  */
  666 uint8_t
  667 ucom_cfg_is_gone(struct ucom_softc *sc)
  668 {
  669         struct ucom_super_softc *ssc = sc->sc_super;
  670 
  671         return (usb_proc_is_gone(&ssc->sc_tq));
  672 }
  673 
  674 static void
  675 ucom_cfg_start_transfers(struct usb_proc_msg *_task)
  676 {
  677         struct ucom_cfg_task *task = 
  678             (struct ucom_cfg_task *)_task;
  679         struct ucom_softc *sc = task->sc;
  680 
  681         if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) {
  682                 return;
  683         }
  684         if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
  685                 /* TTY device closed */
  686                 return;
  687         }
  688 
  689         if (_task == sc->sc_last_start_xfer)
  690                 sc->sc_flag |= UCOM_FLAG_GP_DATA;
  691 
  692         if (sc->sc_callback->ucom_start_read) {
  693                 (sc->sc_callback->ucom_start_read) (sc);
  694         }
  695         if (sc->sc_callback->ucom_start_write) {
  696                 (sc->sc_callback->ucom_start_write) (sc);
  697         }
  698 }
  699 
  700 static void
  701 ucom_start_transfers(struct ucom_softc *sc)
  702 {
  703         if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
  704                 return;
  705         }
  706         /*
  707          * Make sure that data transfers are started in both
  708          * directions:
  709          */
  710         if (sc->sc_callback->ucom_start_read) {
  711                 (sc->sc_callback->ucom_start_read) (sc);
  712         }
  713         if (sc->sc_callback->ucom_start_write) {
  714                 (sc->sc_callback->ucom_start_write) (sc);
  715         }
  716 }
  717 
  718 static void
  719 ucom_cfg_open(struct usb_proc_msg *_task)
  720 {
  721         struct ucom_cfg_task *task = 
  722             (struct ucom_cfg_task *)_task;
  723         struct ucom_softc *sc = task->sc;
  724 
  725         DPRINTF("\n");
  726 
  727         if (sc->sc_flag & UCOM_FLAG_LL_READY) {
  728                 /* already opened */
  729 
  730         } else {
  731                 sc->sc_flag |= UCOM_FLAG_LL_READY;
  732 
  733                 if (sc->sc_callback->ucom_cfg_open) {
  734                         (sc->sc_callback->ucom_cfg_open) (sc);
  735 
  736                         /* wait a little */
  737                         usb_pause_mtx(sc->sc_mtx, hz / 10);
  738                 }
  739         }
  740 }
  741 
  742 static int
  743 ucom_open(struct tty *tp)
  744 {
  745         struct ucom_softc *sc = tty_softc(tp);
  746         int error;
  747 
  748         UCOM_MTX_ASSERT(sc, MA_OWNED);
  749 
  750         if (sc->sc_flag & UCOM_FLAG_GONE) {
  751                 return (ENXIO);
  752         }
  753         if (sc->sc_flag & UCOM_FLAG_HL_READY) {
  754                 /* already opened */
  755                 return (0);
  756         }
  757         DPRINTF("tp = %p\n", tp);
  758 
  759         if (sc->sc_callback->ucom_pre_open) {
  760                 /*
  761                  * give the lower layer a chance to disallow TTY open, for
  762                  * example if the device is not present:
  763                  */
  764                 error = (sc->sc_callback->ucom_pre_open) (sc);
  765                 if (error) {
  766                         return (error);
  767                 }
  768         }
  769         sc->sc_flag |= UCOM_FLAG_HL_READY;
  770 
  771         /* Disable transfers */
  772         sc->sc_flag &= ~UCOM_FLAG_GP_DATA;
  773 
  774         sc->sc_lsr = 0;
  775         sc->sc_msr = 0;
  776         sc->sc_mcr = 0;
  777 
  778         /* reset programmed line state */
  779         sc->sc_pls_curr = 0;
  780         sc->sc_pls_set = 0;
  781         sc->sc_pls_clr = 0;
  782 
  783         /* reset jitter buffer */
  784         sc->sc_jitterbuf_in = 0;
  785         sc->sc_jitterbuf_out = 0;
  786 
  787         ucom_queue_command(sc, ucom_cfg_open, NULL,
  788             &sc->sc_open_task[0].hdr,
  789             &sc->sc_open_task[1].hdr);
  790 
  791         /* Queue transfer enable command last */
  792         ucom_queue_command(sc, ucom_cfg_start_transfers, NULL,
  793             &sc->sc_start_task[0].hdr, 
  794             &sc->sc_start_task[1].hdr);
  795 
  796         if (sc->sc_tty == NULL || (sc->sc_tty->t_termios.c_cflag & CNO_RTSDTR) == 0)
  797                 ucom_modem(tp, SER_DTR | SER_RTS, 0);
  798 
  799         ucom_ring(sc, 0);
  800 
  801         ucom_break(sc, 0);
  802 
  803         ucom_status_change(sc);
  804 
  805         return (0);
  806 }
  807 
  808 static void
  809 ucom_cfg_close(struct usb_proc_msg *_task)
  810 {
  811         struct ucom_cfg_task *task = 
  812             (struct ucom_cfg_task *)_task;
  813         struct ucom_softc *sc = task->sc;
  814 
  815         DPRINTF("\n");
  816 
  817         if (sc->sc_flag & UCOM_FLAG_LL_READY) {
  818                 sc->sc_flag &= ~UCOM_FLAG_LL_READY;
  819                 if (sc->sc_callback->ucom_cfg_close)
  820                         (sc->sc_callback->ucom_cfg_close) (sc);
  821         } else {
  822                 /* already closed */
  823         }
  824 }
  825 
  826 static void
  827 ucom_close(struct tty *tp)
  828 {
  829         struct ucom_softc *sc = tty_softc(tp);
  830 
  831         UCOM_MTX_ASSERT(sc, MA_OWNED);
  832 
  833         DPRINTF("tp=%p\n", tp);
  834 
  835         if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
  836                 DPRINTF("tp=%p already closed\n", tp);
  837                 return;
  838         }
  839         ucom_shutdown(sc);
  840 
  841         ucom_queue_command(sc, ucom_cfg_close, NULL,
  842             &sc->sc_close_task[0].hdr,
  843             &sc->sc_close_task[1].hdr);
  844 
  845         sc->sc_flag &= ~(UCOM_FLAG_HL_READY | UCOM_FLAG_RTS_IFLOW);
  846 
  847         if (sc->sc_callback->ucom_stop_read) {
  848                 (sc->sc_callback->ucom_stop_read) (sc);
  849         }
  850 }
  851 
  852 static void
  853 ucom_inwakeup(struct tty *tp)
  854 {
  855         struct ucom_softc *sc = tty_softc(tp);
  856         uint16_t pos;
  857 
  858         if (sc == NULL)
  859                 return;
  860 
  861         UCOM_MTX_ASSERT(sc, MA_OWNED);
  862 
  863         DPRINTF("tp=%p\n", tp);
  864 
  865         if (ttydisc_can_bypass(tp) != 0 || 
  866             (sc->sc_flag & UCOM_FLAG_HL_READY) == 0 ||
  867             (sc->sc_flag & UCOM_FLAG_INWAKEUP) != 0) {
  868                 return;
  869         }
  870 
  871         /* prevent recursion */
  872         sc->sc_flag |= UCOM_FLAG_INWAKEUP;
  873 
  874         pos = sc->sc_jitterbuf_out;
  875 
  876         while (sc->sc_jitterbuf_in != pos) {
  877                 int c;
  878 
  879                 c = (char)sc->sc_jitterbuf[pos];
  880 
  881                 if (ttydisc_rint(tp, c, 0) == -1)
  882                         break;
  883                 pos++;
  884                 if (pos >= UCOM_JITTERBUF_SIZE)
  885                         pos -= UCOM_JITTERBUF_SIZE;
  886         }
  887 
  888         sc->sc_jitterbuf_out = pos;
  889 
  890         /* clear RTS in async fashion */
  891         if ((sc->sc_jitterbuf_in == pos) && 
  892             (sc->sc_flag & UCOM_FLAG_RTS_IFLOW))
  893                 ucom_rts(sc, 0);
  894 
  895         sc->sc_flag &= ~UCOM_FLAG_INWAKEUP;
  896 }
  897 
  898 static int
  899 ucom_ioctl(struct tty *tp, u_long cmd, caddr_t data, struct thread *td)
  900 {
  901         struct ucom_softc *sc = tty_softc(tp);
  902         int error;
  903 
  904         UCOM_MTX_ASSERT(sc, MA_OWNED);
  905 
  906         if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
  907                 return (EIO);
  908         }
  909         DPRINTF("cmd = 0x%08lx\n", cmd);
  910 
  911         switch (cmd) {
  912 #if 0
  913         case TIOCSRING:
  914                 ucom_ring(sc, 1);
  915                 error = 0;
  916                 break;
  917         case TIOCCRING:
  918                 ucom_ring(sc, 0);
  919                 error = 0;
  920                 break;
  921 #endif
  922         case TIOCSBRK:
  923                 ucom_break(sc, 1);
  924                 error = 0;
  925                 break;
  926         case TIOCCBRK:
  927                 ucom_break(sc, 0);
  928                 error = 0;
  929                 break;
  930         default:
  931                 if (sc->sc_callback->ucom_ioctl) {
  932                         error = (sc->sc_callback->ucom_ioctl)
  933                             (sc, cmd, data, 0, td);
  934                 } else {
  935                         error = ENOIOCTL;
  936                 }
  937                 if (error == ENOIOCTL)
  938                         error = pps_ioctl(cmd, data, &sc->sc_pps);
  939                 break;
  940         }
  941         return (error);
  942 }
  943 
  944 static int
  945 ucom_modem(struct tty *tp, int sigon, int sigoff)
  946 {
  947         struct ucom_softc *sc = tty_softc(tp);
  948         uint8_t onoff;
  949 
  950         UCOM_MTX_ASSERT(sc, MA_OWNED);
  951 
  952         if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
  953                 return (0);
  954         }
  955         if ((sigon == 0) && (sigoff == 0)) {
  956                 if (sc->sc_mcr & SER_DTR) {
  957                         sigon |= SER_DTR;
  958                 }
  959                 if (sc->sc_mcr & SER_RTS) {
  960                         sigon |= SER_RTS;
  961                 }
  962                 if (sc->sc_msr & SER_CTS) {
  963                         sigon |= SER_CTS;
  964                 }
  965                 if (sc->sc_msr & SER_DCD) {
  966                         sigon |= SER_DCD;
  967                 }
  968                 if (sc->sc_msr & SER_DSR) {
  969                         sigon |= SER_DSR;
  970                 }
  971                 if (sc->sc_msr & SER_RI) {
  972                         sigon |= SER_RI;
  973                 }
  974                 return (sigon);
  975         }
  976         if (sigon & SER_DTR) {
  977                 sc->sc_mcr |= SER_DTR;
  978         }
  979         if (sigoff & SER_DTR) {
  980                 sc->sc_mcr &= ~SER_DTR;
  981         }
  982         if (sigon & SER_RTS) {
  983                 sc->sc_mcr |= SER_RTS;
  984         }
  985         if (sigoff & SER_RTS) {
  986                 sc->sc_mcr &= ~SER_RTS;
  987         }
  988         onoff = (sc->sc_mcr & SER_DTR) ? 1 : 0;
  989         ucom_dtr(sc, onoff);
  990 
  991         onoff = (sc->sc_mcr & SER_RTS) ? 1 : 0;
  992         ucom_rts(sc, onoff);
  993 
  994         return (0);
  995 }
  996 
  997 static void
  998 ucom_cfg_line_state(struct usb_proc_msg *_task)
  999 {
 1000         struct ucom_cfg_task *task = 
 1001             (struct ucom_cfg_task *)_task;
 1002         struct ucom_softc *sc = task->sc;
 1003         uint8_t notch_bits;
 1004         uint8_t any_bits;
 1005         uint8_t prev_value;
 1006         uint8_t last_value;
 1007         uint8_t mask;
 1008 
 1009         if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) {
 1010                 return;
 1011         }
 1012 
 1013         mask = 0;
 1014         /* compute callback mask */
 1015         if (sc->sc_callback->ucom_cfg_set_dtr)
 1016                 mask |= UCOM_LS_DTR;
 1017         if (sc->sc_callback->ucom_cfg_set_rts)
 1018                 mask |= UCOM_LS_RTS;
 1019         if (sc->sc_callback->ucom_cfg_set_break)
 1020                 mask |= UCOM_LS_BREAK;
 1021         if (sc->sc_callback->ucom_cfg_set_ring)
 1022                 mask |= UCOM_LS_RING;
 1023 
 1024         /* compute the bits we are to program */
 1025         notch_bits = (sc->sc_pls_set & sc->sc_pls_clr) & mask;
 1026         any_bits = (sc->sc_pls_set | sc->sc_pls_clr) & mask;
 1027         prev_value = sc->sc_pls_curr ^ notch_bits;
 1028         last_value = sc->sc_pls_curr;
 1029 
 1030         /* reset programmed line state */
 1031         sc->sc_pls_curr = 0;
 1032         sc->sc_pls_set = 0;
 1033         sc->sc_pls_clr = 0;
 1034 
 1035         /* ensure that we don't lose any levels */
 1036         if (notch_bits & UCOM_LS_DTR)
 1037                 sc->sc_callback->ucom_cfg_set_dtr(sc,
 1038                     (prev_value & UCOM_LS_DTR) ? 1 : 0);
 1039         if (notch_bits & UCOM_LS_RTS)
 1040                 sc->sc_callback->ucom_cfg_set_rts(sc,
 1041                     (prev_value & UCOM_LS_RTS) ? 1 : 0);
 1042         if (notch_bits & UCOM_LS_BREAK)
 1043                 sc->sc_callback->ucom_cfg_set_break(sc,
 1044                     (prev_value & UCOM_LS_BREAK) ? 1 : 0);
 1045         if (notch_bits & UCOM_LS_RING)
 1046                 sc->sc_callback->ucom_cfg_set_ring(sc,
 1047                     (prev_value & UCOM_LS_RING) ? 1 : 0);
 1048 
 1049         /* set last value */
 1050         if (any_bits & UCOM_LS_DTR)
 1051                 sc->sc_callback->ucom_cfg_set_dtr(sc,
 1052                     (last_value & UCOM_LS_DTR) ? 1 : 0);
 1053         if (any_bits & UCOM_LS_RTS)
 1054                 sc->sc_callback->ucom_cfg_set_rts(sc,
 1055                     (last_value & UCOM_LS_RTS) ? 1 : 0);
 1056         if (any_bits & UCOM_LS_BREAK)
 1057                 sc->sc_callback->ucom_cfg_set_break(sc,
 1058                     (last_value & UCOM_LS_BREAK) ? 1 : 0);
 1059         if (any_bits & UCOM_LS_RING)
 1060                 sc->sc_callback->ucom_cfg_set_ring(sc,
 1061                     (last_value & UCOM_LS_RING) ? 1 : 0);
 1062 }
 1063 
 1064 static void
 1065 ucom_line_state(struct ucom_softc *sc,
 1066     uint8_t set_bits, uint8_t clear_bits)
 1067 {
 1068         UCOM_MTX_ASSERT(sc, MA_OWNED);
 1069 
 1070         if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
 1071                 return;
 1072         }
 1073 
 1074         DPRINTF("on=0x%02x, off=0x%02x\n", set_bits, clear_bits);
 1075 
 1076         /* update current programmed line state */
 1077         sc->sc_pls_curr |= set_bits;
 1078         sc->sc_pls_curr &= ~clear_bits;
 1079         sc->sc_pls_set |= set_bits;
 1080         sc->sc_pls_clr |= clear_bits;
 1081 
 1082         /* defer driver programming */
 1083         ucom_queue_command(sc, ucom_cfg_line_state, NULL,
 1084             &sc->sc_line_state_task[0].hdr, 
 1085             &sc->sc_line_state_task[1].hdr);
 1086 }
 1087 
 1088 static void
 1089 ucom_ring(struct ucom_softc *sc, uint8_t onoff)
 1090 {
 1091         DPRINTF("onoff = %d\n", onoff);
 1092 
 1093         if (onoff)
 1094                 ucom_line_state(sc, UCOM_LS_RING, 0);
 1095         else
 1096                 ucom_line_state(sc, 0, UCOM_LS_RING);
 1097 }
 1098 
 1099 static void
 1100 ucom_break(struct ucom_softc *sc, uint8_t onoff)
 1101 {
 1102         DPRINTF("onoff = %d\n", onoff);
 1103 
 1104         if (onoff)
 1105                 ucom_line_state(sc, UCOM_LS_BREAK, 0);
 1106         else
 1107                 ucom_line_state(sc, 0, UCOM_LS_BREAK);
 1108 }
 1109 
 1110 static void
 1111 ucom_dtr(struct ucom_softc *sc, uint8_t onoff)
 1112 {
 1113         DPRINTF("onoff = %d\n", onoff);
 1114 
 1115         if (onoff)
 1116                 ucom_line_state(sc, UCOM_LS_DTR, 0);
 1117         else
 1118                 ucom_line_state(sc, 0, UCOM_LS_DTR);
 1119 }
 1120 
 1121 static void
 1122 ucom_rts(struct ucom_softc *sc, uint8_t onoff)
 1123 {
 1124         DPRINTF("onoff = %d\n", onoff);
 1125 
 1126         if (onoff)
 1127                 ucom_line_state(sc, UCOM_LS_RTS, 0);
 1128         else
 1129                 ucom_line_state(sc, 0, UCOM_LS_RTS);
 1130 }
 1131 
 1132 static void
 1133 ucom_cfg_status_change(struct usb_proc_msg *_task)
 1134 {
 1135         struct ucom_cfg_task *task = 
 1136             (struct ucom_cfg_task *)_task;
 1137         struct ucom_softc *sc = task->sc;
 1138         struct tty *tp;
 1139         int onoff;
 1140         uint8_t new_msr;
 1141         uint8_t new_lsr;
 1142         uint8_t msr_delta;
 1143         uint8_t lsr_delta;
 1144         uint8_t pps_signal;
 1145 
 1146         tp = sc->sc_tty;
 1147 
 1148         UCOM_MTX_ASSERT(sc, MA_OWNED);
 1149 
 1150         if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) {
 1151                 return;
 1152         }
 1153         if (sc->sc_callback->ucom_cfg_get_status == NULL) {
 1154                 return;
 1155         }
 1156         /* get status */
 1157 
 1158         new_msr = 0;
 1159         new_lsr = 0;
 1160 
 1161         (sc->sc_callback->ucom_cfg_get_status) (sc, &new_lsr, &new_msr);
 1162 
 1163         if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
 1164                 /* TTY device closed */
 1165                 return;
 1166         }
 1167         msr_delta = (sc->sc_msr ^ new_msr);
 1168         lsr_delta = (sc->sc_lsr ^ new_lsr);
 1169 
 1170         sc->sc_msr = new_msr;
 1171         sc->sc_lsr = new_lsr;
 1172 
 1173         /*
 1174          * Time pulse counting support.
 1175          */
 1176         switch(ucom_pps_mode & UART_PPS_SIGNAL_MASK) {
 1177         case UART_PPS_CTS:
 1178                 pps_signal = SER_CTS;
 1179                 break;
 1180         case UART_PPS_DCD:
 1181                 pps_signal = SER_DCD;
 1182                 break;
 1183         default:
 1184                 pps_signal = 0;
 1185                 break;
 1186         }
 1187 
 1188         if ((sc->sc_pps.ppsparam.mode & PPS_CAPTUREBOTH) &&
 1189             (msr_delta & pps_signal)) {
 1190                 pps_capture(&sc->sc_pps);
 1191                 onoff = (sc->sc_msr & pps_signal) ? 1 : 0;
 1192                 if (ucom_pps_mode & UART_PPS_INVERT_PULSE)
 1193                         onoff = !onoff;
 1194                 pps_event(&sc->sc_pps, onoff ? PPS_CAPTUREASSERT :
 1195                     PPS_CAPTURECLEAR);
 1196         }
 1197 
 1198         if (msr_delta & SER_DCD) {
 1199                 onoff = (sc->sc_msr & SER_DCD) ? 1 : 0;
 1200 
 1201                 DPRINTF("DCD changed to %d\n", onoff);
 1202 
 1203                 ttydisc_modem(tp, onoff);
 1204         }
 1205 
 1206         if ((lsr_delta & ULSR_BI) && (sc->sc_lsr & ULSR_BI)) {
 1207                 DPRINTF("BREAK detected\n");
 1208 
 1209                 ttydisc_rint(tp, 0, TRE_BREAK);
 1210                 ttydisc_rint_done(tp);
 1211         }
 1212 
 1213         if ((lsr_delta & ULSR_FE) && (sc->sc_lsr & ULSR_FE)) {
 1214                 DPRINTF("Frame error detected\n");
 1215 
 1216                 ttydisc_rint(tp, 0, TRE_FRAMING);
 1217                 ttydisc_rint_done(tp);
 1218         }
 1219 
 1220         if ((lsr_delta & ULSR_PE) && (sc->sc_lsr & ULSR_PE)) {
 1221                 DPRINTF("Parity error detected\n");
 1222 
 1223                 ttydisc_rint(tp, 0, TRE_PARITY);
 1224                 ttydisc_rint_done(tp);
 1225         }
 1226 }
 1227 
 1228 void
 1229 ucom_status_change(struct ucom_softc *sc)
 1230 {
 1231         UCOM_MTX_ASSERT(sc, MA_OWNED);
 1232 
 1233         if (sc->sc_flag & UCOM_FLAG_CONSOLE)
 1234                 return;         /* not supported */
 1235 
 1236         if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
 1237                 return;
 1238         }
 1239         DPRINTF("\n");
 1240 
 1241         ucom_queue_command(sc, ucom_cfg_status_change, NULL,
 1242             &sc->sc_status_task[0].hdr,
 1243             &sc->sc_status_task[1].hdr);
 1244 }
 1245 
 1246 static void
 1247 ucom_cfg_param(struct usb_proc_msg *_task)
 1248 {
 1249         struct ucom_param_task *task = 
 1250             (struct ucom_param_task *)_task;
 1251         struct ucom_softc *sc = task->sc;
 1252 
 1253         if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) {
 1254                 return;
 1255         }
 1256         if (sc->sc_callback->ucom_cfg_param == NULL) {
 1257                 return;
 1258         }
 1259 
 1260         (sc->sc_callback->ucom_cfg_param) (sc, &task->termios_copy);
 1261 
 1262         /* wait a little */
 1263         usb_pause_mtx(sc->sc_mtx, hz / 10);
 1264 }
 1265 
 1266 static int
 1267 ucom_param(struct tty *tp, struct termios *t)
 1268 {
 1269         struct ucom_softc *sc = tty_softc(tp);
 1270         uint8_t opened;
 1271         int error;
 1272 
 1273         UCOM_MTX_ASSERT(sc, MA_OWNED);
 1274 
 1275         opened = 0;
 1276         error = 0;
 1277 
 1278         if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
 1279                 /* XXX the TTY layer should call "open()" first! */
 1280                 /*
 1281                  * Not quite: Its ordering is partly backwards, but
 1282                  * some parameters must be set early in ttydev_open(),
 1283                  * possibly before calling ttydevsw_open().
 1284                  */
 1285                 error = ucom_open(tp);
 1286                 if (error)
 1287                         goto done;
 1288 
 1289                 opened = 1;
 1290         }
 1291         DPRINTF("sc = %p\n", sc);
 1292 
 1293         /* Check requested parameters. */
 1294         if (t->c_ispeed && (t->c_ispeed != t->c_ospeed)) {
 1295                 /* XXX c_ospeed == 0 is perfectly valid. */
 1296                 DPRINTF("mismatch ispeed and ospeed\n");
 1297                 error = EINVAL;
 1298                 goto done;
 1299         }
 1300         t->c_ispeed = t->c_ospeed;
 1301 
 1302         if (sc->sc_callback->ucom_pre_param) {
 1303                 /* Let the lower layer verify the parameters */
 1304                 error = (sc->sc_callback->ucom_pre_param) (sc, t);
 1305                 if (error) {
 1306                         DPRINTF("callback error = %d\n", error);
 1307                         goto done;
 1308                 }
 1309         }
 1310 
 1311         /* Disable transfers */
 1312         sc->sc_flag &= ~UCOM_FLAG_GP_DATA;
 1313 
 1314         /* Queue baud rate programming command first */
 1315         ucom_queue_command(sc, ucom_cfg_param, t,
 1316             &sc->sc_param_task[0].hdr,
 1317             &sc->sc_param_task[1].hdr);
 1318 
 1319         /* Queue transfer enable command last */
 1320         ucom_queue_command(sc, ucom_cfg_start_transfers, NULL,
 1321             &sc->sc_start_task[0].hdr, 
 1322             &sc->sc_start_task[1].hdr);
 1323 
 1324         if (t->c_cflag & CRTS_IFLOW) {
 1325                 sc->sc_flag |= UCOM_FLAG_RTS_IFLOW;
 1326         } else if (sc->sc_flag & UCOM_FLAG_RTS_IFLOW) {
 1327                 sc->sc_flag &= ~UCOM_FLAG_RTS_IFLOW;
 1328                 ucom_modem(tp, SER_RTS, 0);
 1329         }
 1330 done:
 1331         if (error) {
 1332                 if (opened) {
 1333                         ucom_close(tp);
 1334                 }
 1335         }
 1336         return (error);
 1337 }
 1338 
 1339 static void
 1340 ucom_outwakeup(struct tty *tp)
 1341 {
 1342         struct ucom_softc *sc = tty_softc(tp);
 1343 
 1344         UCOM_MTX_ASSERT(sc, MA_OWNED);
 1345 
 1346         DPRINTF("sc = %p\n", sc);
 1347 
 1348         if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
 1349                 /* The higher layer is not ready */
 1350                 return;
 1351         }
 1352         ucom_start_transfers(sc);
 1353 }
 1354 
 1355 static bool
 1356 ucom_busy(struct tty *tp)
 1357 {
 1358         struct ucom_softc *sc = tty_softc(tp);
 1359         const uint8_t txidle = ULSR_TXRDY | ULSR_TSRE;
 1360 
 1361         UCOM_MTX_ASSERT(sc, MA_OWNED);
 1362 
 1363         DPRINTFN(3, "sc = %p lsr 0x%02x\n", sc, sc->sc_lsr);
 1364 
 1365         /*
 1366          * If the driver maintains the txidle bits in LSR, we can use them to
 1367          * determine whether the transmitter is busy or idle.  Otherwise we have
 1368          * to assume it is idle to avoid hanging forever on tcdrain(3).
 1369          */
 1370         if (sc->sc_flag & UCOM_FLAG_LSRTXIDLE)
 1371                 return ((sc->sc_lsr & txidle) != txidle);
 1372         else
 1373                 return (false);
 1374 }
 1375 
 1376 /*------------------------------------------------------------------------*
 1377  *      ucom_get_data
 1378  *
 1379  * Return values:
 1380  * 0: No data is available.
 1381  * Else: Data is available.
 1382  *------------------------------------------------------------------------*/
 1383 uint8_t
 1384 ucom_get_data(struct ucom_softc *sc, struct usb_page_cache *pc,
 1385     uint32_t offset, uint32_t len, uint32_t *actlen)
 1386 {
 1387         struct usb_page_search res;
 1388         struct tty *tp = sc->sc_tty;
 1389         uint32_t cnt;
 1390         uint32_t offset_orig;
 1391 
 1392         UCOM_MTX_ASSERT(sc, MA_OWNED);
 1393 
 1394         if (sc->sc_flag & UCOM_FLAG_CONSOLE) {
 1395                 unsigned temp;
 1396 
 1397                 /* get total TX length */
 1398 
 1399                 temp = ucom_cons_tx_high - ucom_cons_tx_low;
 1400                 temp %= UCOM_CONS_BUFSIZE;
 1401 
 1402                 /* limit TX length */
 1403 
 1404                 if (temp > (UCOM_CONS_BUFSIZE - ucom_cons_tx_low))
 1405                         temp = (UCOM_CONS_BUFSIZE - ucom_cons_tx_low);
 1406 
 1407                 if (temp > len)
 1408                         temp = len;
 1409 
 1410                 /* copy in data */
 1411 
 1412                 usbd_copy_in(pc, offset, ucom_cons_tx_buf + ucom_cons_tx_low, temp);
 1413 
 1414                 /* update counters */
 1415 
 1416                 ucom_cons_tx_low += temp;
 1417                 ucom_cons_tx_low %= UCOM_CONS_BUFSIZE;
 1418 
 1419                 /* store actual length */
 1420 
 1421                 *actlen = temp;
 1422 
 1423                 return (temp ? 1 : 0);
 1424         }
 1425 
 1426         if (tty_gone(tp) ||
 1427             !(sc->sc_flag & UCOM_FLAG_GP_DATA)) {
 1428                 actlen[0] = 0;
 1429                 return (0);             /* multiport device polling */
 1430         }
 1431         offset_orig = offset;
 1432 
 1433         while (len != 0) {
 1434                 usbd_get_page(pc, offset, &res);
 1435 
 1436                 if (res.length > len) {
 1437                         res.length = len;
 1438                 }
 1439                 /* copy data directly into USB buffer */
 1440                 cnt = ttydisc_getc(tp, res.buffer, res.length);
 1441 
 1442                 offset += cnt;
 1443                 len -= cnt;
 1444 
 1445                 if (cnt < res.length) {
 1446                         /* end of buffer */
 1447                         break;
 1448                 }
 1449         }
 1450 
 1451         actlen[0] = offset - offset_orig;
 1452 
 1453         DPRINTF("cnt=%d\n", actlen[0]);
 1454 
 1455         if (actlen[0] == 0) {
 1456                 return (0);
 1457         }
 1458         return (1);
 1459 }
 1460 
 1461 void
 1462 ucom_put_data(struct ucom_softc *sc, struct usb_page_cache *pc,
 1463     uint32_t offset, uint32_t len)
 1464 {
 1465         struct usb_page_search res;
 1466         struct tty *tp = sc->sc_tty;
 1467         char *buf;
 1468         uint32_t cnt;
 1469 
 1470         UCOM_MTX_ASSERT(sc, MA_OWNED);
 1471 
 1472         if (sc->sc_flag & UCOM_FLAG_CONSOLE) {
 1473                 unsigned temp;
 1474 
 1475                 /* get maximum RX length */
 1476 
 1477                 temp = (UCOM_CONS_BUFSIZE - 1) - ucom_cons_rx_high + ucom_cons_rx_low;
 1478                 temp %= UCOM_CONS_BUFSIZE;
 1479 
 1480                 /* limit RX length */
 1481 
 1482                 if (temp > (UCOM_CONS_BUFSIZE - ucom_cons_rx_high))
 1483                         temp = (UCOM_CONS_BUFSIZE - ucom_cons_rx_high);
 1484 
 1485                 if (temp > len)
 1486                         temp = len;
 1487 
 1488                 /* copy out data */
 1489 
 1490                 usbd_copy_out(pc, offset, ucom_cons_rx_buf + ucom_cons_rx_high, temp);
 1491 
 1492                 /* update counters */
 1493 
 1494                 ucom_cons_rx_high += temp;
 1495                 ucom_cons_rx_high %= UCOM_CONS_BUFSIZE;
 1496 
 1497                 return;
 1498         }
 1499 
 1500         if (tty_gone(tp))
 1501                 return;                 /* multiport device polling */
 1502 
 1503         if (len == 0)
 1504                 return;                 /* no data */
 1505 
 1506         /* set a flag to prevent recursation ? */
 1507 
 1508         while (len > 0) {
 1509                 usbd_get_page(pc, offset, &res);
 1510 
 1511                 if (res.length > len) {
 1512                         res.length = len;
 1513                 }
 1514                 len -= res.length;
 1515                 offset += res.length;
 1516 
 1517                 /* pass characters to tty layer */
 1518 
 1519                 buf = res.buffer;
 1520                 cnt = res.length;
 1521 
 1522                 /* first check if we can pass the buffer directly */
 1523 
 1524                 if (ttydisc_can_bypass(tp)) {
 1525                         /* clear any jitter buffer */
 1526                         sc->sc_jitterbuf_in = 0;
 1527                         sc->sc_jitterbuf_out = 0;
 1528 
 1529                         if (ttydisc_rint_bypass(tp, buf, cnt) != cnt) {
 1530                                 DPRINTF("tp=%p, data lost\n", tp);
 1531                         }
 1532                         continue;
 1533                 }
 1534                 /* need to loop */
 1535 
 1536                 for (cnt = 0; cnt != res.length; cnt++) {
 1537                         if (sc->sc_jitterbuf_in != sc->sc_jitterbuf_out ||
 1538                             ttydisc_rint(tp, buf[cnt], 0) == -1) {
 1539                                 uint16_t end;
 1540                                 uint16_t pos;
 1541 
 1542                                 pos = sc->sc_jitterbuf_in;
 1543                                 end = sc->sc_jitterbuf_out +
 1544                                     UCOM_JITTERBUF_SIZE - 1;
 1545                                 if (end >= UCOM_JITTERBUF_SIZE)
 1546                                         end -= UCOM_JITTERBUF_SIZE;
 1547 
 1548                                 for (; cnt != res.length; cnt++) {
 1549                                         if (pos == end)
 1550                                                 break;
 1551                                         sc->sc_jitterbuf[pos] = buf[cnt];
 1552                                         pos++;
 1553                                         if (pos >= UCOM_JITTERBUF_SIZE)
 1554                                                 pos -= UCOM_JITTERBUF_SIZE;
 1555                                 }
 1556 
 1557                                 sc->sc_jitterbuf_in = pos;
 1558 
 1559                                 /* set RTS in async fashion */
 1560                                 if (sc->sc_flag & UCOM_FLAG_RTS_IFLOW)
 1561                                         ucom_rts(sc, 1);
 1562 
 1563                                 DPRINTF("tp=%p, lost %d "
 1564                                     "chars\n", tp, res.length - cnt);
 1565                                 break;
 1566                         }
 1567                 }
 1568         }
 1569         ttydisc_rint_done(tp);
 1570 }
 1571 
 1572 static void
 1573 ucom_free(void *xsc)
 1574 {
 1575         struct ucom_softc *sc = xsc;
 1576 
 1577         if (sc->sc_callback->ucom_free != NULL)
 1578                 sc->sc_callback->ucom_free(sc);
 1579         else
 1580                 ucom_unref(sc->sc_super);
 1581 
 1582         mtx_lock(&ucom_mtx);
 1583         ucom_close_refs--;
 1584         mtx_unlock(&ucom_mtx);
 1585 }
 1586 
 1587 CONSOLE_DRIVER(ucom);
 1588 
 1589 static void
 1590 ucom_cnprobe(struct consdev  *cp)
 1591 {
 1592         if (ucom_cons_unit != -1)
 1593                 cp->cn_pri = CN_NORMAL;
 1594         else
 1595                 cp->cn_pri = CN_DEAD;
 1596 
 1597         strlcpy(cp->cn_name, "ucom", sizeof(cp->cn_name));
 1598 }
 1599 
 1600 static void
 1601 ucom_cninit(struct consdev  *cp)
 1602 {
 1603 }
 1604 
 1605 static void
 1606 ucom_cnterm(struct consdev  *cp)
 1607 {
 1608 }
 1609 
 1610 static void
 1611 ucom_cngrab(struct consdev *cp)
 1612 {
 1613 }
 1614 
 1615 static void
 1616 ucom_cnungrab(struct consdev *cp)
 1617 {
 1618 }
 1619 
 1620 static int
 1621 ucom_cngetc(struct consdev *cd)
 1622 {
 1623         struct ucom_softc *sc = ucom_cons_softc;
 1624         int c;
 1625 
 1626         if (sc == NULL)
 1627                 return (-1);
 1628 
 1629         UCOM_MTX_LOCK(sc);
 1630 
 1631         if (ucom_cons_rx_low != ucom_cons_rx_high) {
 1632                 c = ucom_cons_rx_buf[ucom_cons_rx_low];
 1633                 ucom_cons_rx_low ++;
 1634                 ucom_cons_rx_low %= UCOM_CONS_BUFSIZE;
 1635         } else {
 1636                 c = -1;
 1637         }
 1638 
 1639         /* start USB transfers */
 1640         ucom_outwakeup(sc->sc_tty);
 1641 
 1642         UCOM_MTX_UNLOCK(sc);
 1643 
 1644         /* poll if necessary */
 1645         if (USB_IN_POLLING_MODE_FUNC() && sc->sc_callback->ucom_poll)
 1646                 (sc->sc_callback->ucom_poll) (sc);
 1647 
 1648         return (c);
 1649 }
 1650 
 1651 static void
 1652 ucom_cnputc(struct consdev *cd, int c)
 1653 {
 1654         struct ucom_softc *sc = ucom_cons_softc;
 1655         unsigned temp;
 1656 
 1657         if (sc == NULL)
 1658                 return;
 1659 
 1660  repeat:
 1661 
 1662         UCOM_MTX_LOCK(sc);
 1663 
 1664         /* compute maximum TX length */
 1665 
 1666         temp = (UCOM_CONS_BUFSIZE - 1) - ucom_cons_tx_high + ucom_cons_tx_low;
 1667         temp %= UCOM_CONS_BUFSIZE;
 1668 
 1669         if (temp) {
 1670                 ucom_cons_tx_buf[ucom_cons_tx_high] = c;
 1671                 ucom_cons_tx_high ++;
 1672                 ucom_cons_tx_high %= UCOM_CONS_BUFSIZE;
 1673         }
 1674 
 1675         /* start USB transfers */
 1676         ucom_outwakeup(sc->sc_tty);
 1677 
 1678         UCOM_MTX_UNLOCK(sc);
 1679 
 1680         /* poll if necessary */
 1681         if (USB_IN_POLLING_MODE_FUNC() && sc->sc_callback->ucom_poll) {
 1682                 (sc->sc_callback->ucom_poll) (sc);
 1683                 /* simple flow control */
 1684                 if (temp == 0)
 1685                         goto repeat;
 1686         }
 1687 }
 1688 
 1689 /*------------------------------------------------------------------------*
 1690  *      ucom_ref
 1691  *
 1692  * This function will increment the super UCOM reference count.
 1693  *------------------------------------------------------------------------*/
 1694 void
 1695 ucom_ref(struct ucom_super_softc *ssc)
 1696 {
 1697         mtx_lock(&ucom_mtx);
 1698         ssc->sc_refs++;
 1699         mtx_unlock(&ucom_mtx);
 1700 }
 1701 
 1702 /*------------------------------------------------------------------------*
 1703  *      ucom_free_unit
 1704  *
 1705  * This function will free the super UCOM's allocated unit
 1706  * number. This function can be called on a zero-initialized
 1707  * structure. This function can be called multiple times.
 1708  *------------------------------------------------------------------------*/
 1709 static void
 1710 ucom_free_unit(struct ucom_super_softc *ssc)
 1711 {
 1712         if (!(ssc->sc_flag & UCOM_FLAG_FREE_UNIT))
 1713                 return;
 1714 
 1715         ucom_unit_free(ssc->sc_unit);
 1716 
 1717         ssc->sc_flag &= ~UCOM_FLAG_FREE_UNIT;
 1718 }
 1719 
 1720 /*------------------------------------------------------------------------*
 1721  *      ucom_unref
 1722  *
 1723  * This function will decrement the super UCOM reference count.
 1724  *
 1725  * Return values:
 1726  * 0: UCOM structures are still referenced.
 1727  * Else: UCOM structures are no longer referenced.
 1728  *------------------------------------------------------------------------*/
 1729 int
 1730 ucom_unref(struct ucom_super_softc *ssc)
 1731 {
 1732         int retval;
 1733 
 1734         mtx_lock(&ucom_mtx);
 1735         retval = (ssc->sc_refs < 2);
 1736         ssc->sc_refs--;
 1737         mtx_unlock(&ucom_mtx);
 1738 
 1739         if (retval)
 1740                 ucom_free_unit(ssc);
 1741 
 1742         return (retval);
 1743 }
 1744 
 1745 #if defined(GDB)
 1746 
 1747 #include <gdb/gdb.h>
 1748 
 1749 static gdb_probe_f ucom_gdbprobe;
 1750 static gdb_init_f ucom_gdbinit;
 1751 static gdb_term_f ucom_gdbterm;
 1752 static gdb_getc_f ucom_gdbgetc;
 1753 static gdb_putc_f ucom_gdbputc;
 1754 
 1755 GDB_DBGPORT(ucom, ucom_gdbprobe, ucom_gdbinit, ucom_gdbterm, ucom_gdbgetc, ucom_gdbputc);
 1756 
 1757 static int
 1758 ucom_gdbprobe(void)
 1759 {
 1760         return ((ucom_cons_softc != NULL) ? 0 : -1);
 1761 }
 1762 
 1763 static void
 1764 ucom_gdbinit(void)
 1765 {
 1766 }
 1767 
 1768 static void
 1769 ucom_gdbterm(void)
 1770 {
 1771 }
 1772 
 1773 static void
 1774 ucom_gdbputc(int c)
 1775 {
 1776         ucom_cnputc(NULL, c);
 1777 }
 1778 
 1779 static int
 1780 ucom_gdbgetc(void)
 1781 {
 1782         return (ucom_cngetc(NULL));
 1783 }
 1784 
 1785 #endif

Cache object: 86c38bdb599e67a705c32ec5bedc9a32


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.