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/umcs.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 /*-
    2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
    3  *
    4  * Copyright (c) 2010 Lev Serebryakov <lev@FreeBSD.org>.
    5  * All rights reserved.
    6  *
    7  * Redistribution and use in source and binary forms, with or without
    8  * modification, are permitted provided that the following conditions
    9  * are met:
   10  * 1. Redistributions of source code must retain the above copyright
   11  *    notice, this list of conditions and the following disclaimer.
   12  * 2. Redistributions in binary form must reproduce the above copyright
   13  *    notice, this list of conditions and the following disclaimer in the
   14  *    documentation and/or other materials provided with the distribution.
   15  *
   16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   26  * SUCH DAMAGE.
   27  */
   28 
   29 /*
   30  * This driver supports several multiport USB-to-RS232 serial adapters driven
   31  * by MosChip mos7820 and mos7840, bridge chips.
   32  * The adapters are sold under many different brand names.
   33  *
   34  * Datasheets are available at MosChip www site at
   35  * http://www.moschip.com.  The datasheets don't contain full
   36  * programming information for the chip.
   37  *
   38  * It is normal to have only two enabled ports in devices, based on
   39  * quad-port mos7840.
   40  *
   41  */
   42 #include <sys/cdefs.h>
   43 __FBSDID("$FreeBSD$");
   44 
   45 #include <sys/stdint.h>
   46 #include <sys/stddef.h>
   47 #include <sys/param.h>
   48 #include <sys/queue.h>
   49 #include <sys/types.h>
   50 #include <sys/systm.h>
   51 #include <sys/kernel.h>
   52 #include <sys/bus.h>
   53 #include <sys/linker_set.h>
   54 #include <sys/module.h>
   55 #include <sys/lock.h>
   56 #include <sys/mutex.h>
   57 #include <sys/condvar.h>
   58 #include <sys/sysctl.h>
   59 #include <sys/sx.h>
   60 #include <sys/unistd.h>
   61 #include <sys/callout.h>
   62 #include <sys/malloc.h>
   63 #include <sys/priv.h>
   64 
   65 #include <dev/usb/usb.h>
   66 #include <dev/usb/usbdi.h>
   67 #include <dev/usb/usbdi_util.h>
   68 #include <dev/usb/usb_cdc.h>
   69 #include "usbdevs.h"
   70 
   71 #define USB_DEBUG_VAR umcs_debug
   72 #include <dev/usb/usb_debug.h>
   73 #include <dev/usb/usb_process.h>
   74 
   75 #include <dev/usb/serial/usb_serial.h>
   76 
   77 #include <dev/usb/serial/umcs.h>
   78 
   79 #define UMCS7840_MODVER 1
   80 
   81 #ifdef USB_DEBUG
   82 static int umcs_debug = 0;
   83 
   84 static SYSCTL_NODE(_hw_usb, OID_AUTO, umcs, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
   85     "USB umcs quadport serial adapter");
   86 SYSCTL_INT(_hw_usb_umcs, OID_AUTO, debug, CTLFLAG_RWTUN, &umcs_debug, 0, "Debug level");
   87 #endif                                  /* USB_DEBUG */
   88 
   89 /*
   90  * Two-port devices (both with 7820 chip and 7840 chip configured as two-port)
   91  * have ports 0 and 2, with ports 1 and 3 omitted.
   92  * So,PHYSICAL port numbers (indexes) on two-port device will be 0 and 2.
   93  * This driver trys to use physical numbers as much as possible.
   94  */
   95 
   96 /*
   97  * Indexed by PHYSICAL port number.
   98  * Pack non-regular registers to array to easier if-less access.
   99  */
  100 struct umcs7840_port_registers {
  101         uint8_t reg_sp;                 /* SP register. */
  102         uint8_t reg_control;            /* CONTROL register. */
  103         uint8_t reg_dcr;                /* DCR0 register. DCR1 & DCR2 can be
  104                                          * calculated */
  105 };
  106 
  107 static const struct umcs7840_port_registers umcs7840_port_registers[UMCS7840_MAX_PORTS] = {
  108         {.reg_sp = MCS7840_DEV_REG_SP1,.reg_control = MCS7840_DEV_REG_CONTROL1,.reg_dcr = MCS7840_DEV_REG_DCR0_1},
  109         {.reg_sp = MCS7840_DEV_REG_SP2,.reg_control = MCS7840_DEV_REG_CONTROL2,.reg_dcr = MCS7840_DEV_REG_DCR0_2},
  110         {.reg_sp = MCS7840_DEV_REG_SP3,.reg_control = MCS7840_DEV_REG_CONTROL3,.reg_dcr = MCS7840_DEV_REG_DCR0_3},
  111         {.reg_sp = MCS7840_DEV_REG_SP4,.reg_control = MCS7840_DEV_REG_CONTROL4,.reg_dcr = MCS7840_DEV_REG_DCR0_4},
  112 };
  113 
  114 enum {
  115         UMCS7840_BULK_RD_EP,
  116         UMCS7840_BULK_WR_EP,
  117         UMCS7840_N_TRANSFERS
  118 };
  119 
  120 struct umcs7840_softc_oneport {
  121         struct usb_xfer *sc_xfer[UMCS7840_N_TRANSFERS]; /* Control structures
  122                                                          * for two transfers */
  123 
  124         uint8_t sc_lcr;                 /* local line control register */
  125         uint8_t sc_mcr;                 /* local modem control register */
  126 };
  127 
  128 struct umcs7840_softc {
  129         struct ucom_super_softc sc_super_ucom;
  130         struct ucom_softc sc_ucom[UMCS7840_MAX_PORTS];  /* Need to be continuous
  131                                                          * array, so indexed by
  132                                                          * LOGICAL port
  133                                                          * (subunit) number */
  134 
  135         struct usb_xfer *sc_intr_xfer;  /* Interrupt endpoint */
  136 
  137         device_t sc_dev;                /* Device for error prints */
  138         struct usb_device *sc_udev;     /* USB Device for all operations */
  139         struct mtx sc_mtx;              /* ucom requires this */
  140 
  141         uint8_t sc_driver_done;         /* Flag when enumeration is finished */
  142 
  143         uint8_t sc_numports;            /* Number of ports (subunits) */
  144         struct umcs7840_softc_oneport sc_ports[UMCS7840_MAX_PORTS];     /* Indexed by PHYSICAL
  145                                                                          * port number. */
  146 };
  147 
  148 /* prototypes */
  149 static usb_error_t umcs7840_get_reg_sync(struct umcs7840_softc *, uint8_t, uint8_t *);
  150 static usb_error_t umcs7840_set_reg_sync(struct umcs7840_softc *, uint8_t, uint8_t);
  151 static usb_error_t umcs7840_get_UART_reg_sync(struct umcs7840_softc *, uint8_t, uint8_t, uint8_t *);
  152 static usb_error_t umcs7840_set_UART_reg_sync(struct umcs7840_softc *, uint8_t, uint8_t, uint8_t);
  153 
  154 static usb_error_t umcs7840_set_baudrate(struct umcs7840_softc *, uint8_t, uint32_t);
  155 static usb_error_t umcs7840_calc_baudrate(uint32_t rate, uint16_t *, uint8_t *);
  156 
  157 static void     umcs7840_free(struct ucom_softc *);
  158 static void umcs7840_cfg_get_status(struct ucom_softc *, uint8_t *, uint8_t *);
  159 static void umcs7840_cfg_set_dtr(struct ucom_softc *, uint8_t);
  160 static void umcs7840_cfg_set_rts(struct ucom_softc *, uint8_t);
  161 static void umcs7840_cfg_set_break(struct ucom_softc *, uint8_t);
  162 static void umcs7840_cfg_param(struct ucom_softc *, struct termios *);
  163 static void umcs7840_cfg_open(struct ucom_softc *);
  164 static void umcs7840_cfg_close(struct ucom_softc *);
  165 
  166 static int umcs7840_pre_param(struct ucom_softc *, struct termios *);
  167 
  168 static void umcs7840_start_read(struct ucom_softc *);
  169 static void umcs7840_stop_read(struct ucom_softc *);
  170 
  171 static void umcs7840_start_write(struct ucom_softc *);
  172 static void umcs7840_stop_write(struct ucom_softc *);
  173 
  174 static void umcs7840_poll(struct ucom_softc *ucom);
  175 
  176 static device_probe_t umcs7840_probe;
  177 static device_attach_t umcs7840_attach;
  178 static device_detach_t umcs7840_detach;
  179 static void umcs7840_free_softc(struct umcs7840_softc *);
  180 
  181 static usb_callback_t umcs7840_intr_callback;
  182 static usb_callback_t umcs7840_read_callback1;
  183 static usb_callback_t umcs7840_read_callback2;
  184 static usb_callback_t umcs7840_read_callback3;
  185 static usb_callback_t umcs7840_read_callback4;
  186 static usb_callback_t umcs7840_write_callback1;
  187 static usb_callback_t umcs7840_write_callback2;
  188 static usb_callback_t umcs7840_write_callback3;
  189 static usb_callback_t umcs7840_write_callback4;
  190 
  191 static void umcs7840_read_callbackN(struct usb_xfer *, usb_error_t, uint8_t);
  192 static void umcs7840_write_callbackN(struct usb_xfer *, usb_error_t, uint8_t);
  193 
  194 /* Indexed by LOGICAL port number (subunit), so two-port device uses 0 & 1 */
  195 static usb_callback_t *umcs7840_rw_callbacks[UMCS7840_MAX_PORTS][UMCS7840_N_TRANSFERS] = {
  196         {&umcs7840_read_callback1, &umcs7840_write_callback1},
  197         {&umcs7840_read_callback2, &umcs7840_write_callback2},
  198         {&umcs7840_read_callback3, &umcs7840_write_callback3},
  199         {&umcs7840_read_callback4, &umcs7840_write_callback4},
  200 };
  201 
  202 static const struct usb_config umcs7840_bulk_config_data[UMCS7840_N_TRANSFERS] = {
  203         [UMCS7840_BULK_RD_EP] = {
  204                 .type = UE_BULK,
  205                 .endpoint = 0x01,
  206                 .direction = UE_DIR_IN,
  207                 .flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
  208                 .bufsize = 0,           /* use wMaxPacketSize */
  209                 .callback = &umcs7840_read_callback1,
  210                 .if_index = 0,
  211         },
  212 
  213         [UMCS7840_BULK_WR_EP] = {
  214                 .type = UE_BULK,
  215                 .endpoint = 0x02,
  216                 .direction = UE_DIR_OUT,
  217                 .flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
  218                 .bufsize = 0,           /* use wMaxPacketSize */
  219                 .callback = &umcs7840_write_callback1,
  220                 .if_index = 0,
  221         },
  222 };
  223 
  224 static const struct usb_config umcs7840_intr_config_data[1] = {
  225         [0] = {
  226                 .type = UE_INTERRUPT,
  227                 .endpoint = 0x09,
  228                 .direction = UE_DIR_IN,
  229                 .flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
  230                 .bufsize = 0,           /* use wMaxPacketSize */
  231                 .callback = &umcs7840_intr_callback,
  232                 .if_index = 0,
  233         },
  234 };
  235 
  236 static struct ucom_callback umcs7840_callback = {
  237         .ucom_cfg_get_status = &umcs7840_cfg_get_status,
  238 
  239         .ucom_cfg_set_dtr = &umcs7840_cfg_set_dtr,
  240         .ucom_cfg_set_rts = &umcs7840_cfg_set_rts,
  241         .ucom_cfg_set_break = &umcs7840_cfg_set_break,
  242 
  243         .ucom_cfg_param = &umcs7840_cfg_param,
  244         .ucom_cfg_open = &umcs7840_cfg_open,
  245         .ucom_cfg_close = &umcs7840_cfg_close,
  246 
  247         .ucom_pre_param = &umcs7840_pre_param,
  248 
  249         .ucom_start_read = &umcs7840_start_read,
  250         .ucom_stop_read = &umcs7840_stop_read,
  251 
  252         .ucom_start_write = &umcs7840_start_write,
  253         .ucom_stop_write = &umcs7840_stop_write,
  254 
  255         .ucom_poll = &umcs7840_poll,
  256         .ucom_free = &umcs7840_free,
  257 };
  258 
  259 static const STRUCT_USB_HOST_ID umcs7840_devs[] = {
  260         {USB_VPI(USB_VENDOR_MOSCHIP, USB_PRODUCT_MOSCHIP_MCS7820, 0)},
  261         {USB_VPI(USB_VENDOR_MOSCHIP, USB_PRODUCT_MOSCHIP_MCS7840, 0)},
  262 };
  263 
  264 static device_method_t umcs7840_methods[] = {
  265         DEVMETHOD(device_probe, umcs7840_probe),
  266         DEVMETHOD(device_attach, umcs7840_attach),
  267         DEVMETHOD(device_detach, umcs7840_detach),
  268         DEVMETHOD_END
  269 };
  270 
  271 static driver_t umcs7840_driver = {
  272         .name = "umcs7840",
  273         .methods = umcs7840_methods,
  274         .size = sizeof(struct umcs7840_softc),
  275 };
  276 
  277 DRIVER_MODULE(umcs7840, uhub, umcs7840_driver, 0, 0);
  278 MODULE_DEPEND(umcs7840, ucom, 1, 1, 1);
  279 MODULE_DEPEND(umcs7840, usb, 1, 1, 1);
  280 MODULE_VERSION(umcs7840, UMCS7840_MODVER);
  281 USB_PNP_HOST_INFO(umcs7840_devs);
  282 
  283 static int
  284 umcs7840_probe(device_t dev)
  285 {
  286         struct usb_attach_arg *uaa = device_get_ivars(dev);
  287 
  288         if (uaa->usb_mode != USB_MODE_HOST)
  289                 return (ENXIO);
  290         if (uaa->info.bConfigIndex != MCS7840_CONFIG_INDEX)
  291                 return (ENXIO);
  292         if (uaa->info.bIfaceIndex != MCS7840_IFACE_INDEX)
  293                 return (ENXIO);
  294         return (usbd_lookup_id_by_uaa(umcs7840_devs, sizeof(umcs7840_devs), uaa));
  295 }
  296 
  297 static int
  298 umcs7840_attach(device_t dev)
  299 {
  300         struct usb_config umcs7840_config_tmp[UMCS7840_N_TRANSFERS];
  301         struct usb_attach_arg *uaa = device_get_ivars(dev);
  302         struct umcs7840_softc *sc = device_get_softc(dev);
  303 
  304         uint8_t iface_index = MCS7840_IFACE_INDEX;
  305         int error;
  306         int subunit;
  307         int n;
  308         uint8_t data;
  309 
  310         for (n = 0; n < UMCS7840_N_TRANSFERS; ++n)
  311                 umcs7840_config_tmp[n] = umcs7840_bulk_config_data[n];
  312 
  313         device_set_usb_desc(dev);
  314         mtx_init(&sc->sc_mtx, "umcs7840", NULL, MTX_DEF);
  315         ucom_ref(&sc->sc_super_ucom);
  316 
  317         sc->sc_dev = dev;
  318         sc->sc_udev = uaa->device;
  319 
  320         /*
  321          * Get number of ports
  322          * Documentation (full datasheet) says, that number of ports is
  323          * set as MCS7840_DEV_MODE_SELECT24S bit in MODE R/Only
  324          * register. But vendor driver uses these undocumented
  325          * register & bit.
  326          *
  327          * Experiments show, that MODE register can have `0'
  328          * (4 ports) bit on 2-port device, so use vendor driver's way.
  329          *
  330          * Also, see notes in header file for these constants.
  331          */
  332         umcs7840_get_reg_sync(sc, MCS7840_DEV_REG_GPIO, &data);
  333         if (data & MCS7840_DEV_GPIO_4PORTS) {
  334                 sc->sc_numports = 4;
  335                 /* Store physical port numbers in sc_portno */
  336                 sc->sc_ucom[0].sc_portno = 0;
  337                 sc->sc_ucom[1].sc_portno = 1;
  338                 sc->sc_ucom[2].sc_portno = 2;
  339                 sc->sc_ucom[3].sc_portno = 3;
  340         } else {
  341                 sc->sc_numports = 2;
  342                 /* Store physical port numbers in sc_portno */
  343                 sc->sc_ucom[0].sc_portno = 0;
  344                 sc->sc_ucom[1].sc_portno = 2;   /* '1' is skipped */
  345         }
  346         device_printf(dev, "Chip mcs%04x, found %d active ports\n", uaa->info.idProduct, sc->sc_numports);
  347         if (!umcs7840_get_reg_sync(sc, MCS7840_DEV_REG_MODE, &data)) {
  348                 device_printf(dev, "On-die configuration: RST: active %s, HRD: %s, PLL: %s, POR: %s, Ports: %s, EEPROM write %s, IrDA is %savailable\n",
  349                     (data & MCS7840_DEV_MODE_RESET) ? "low" : "high",
  350                     (data & MCS7840_DEV_MODE_SER_PRSNT) ? "yes" : "no",
  351                     (data & MCS7840_DEV_MODE_PLLBYPASS) ? "bypassed" : "avail",
  352                     (data & MCS7840_DEV_MODE_PORBYPASS) ? "bypassed" : "avail",
  353                     (data & MCS7840_DEV_MODE_SELECT24S) ? "2" : "4",
  354                     (data & MCS7840_DEV_MODE_EEPROMWR) ? "enabled" : "disabled",
  355                     (data & MCS7840_DEV_MODE_IRDA) ? "" : "not ");
  356         }
  357         /* Setup all transfers */
  358         for (subunit = 0; subunit < sc->sc_numports; ++subunit) {
  359                 for (n = 0; n < UMCS7840_N_TRANSFERS; ++n) {
  360                         /* Set endpoint address */
  361                         umcs7840_config_tmp[n].endpoint = umcs7840_bulk_config_data[n].endpoint + 2 * sc->sc_ucom[subunit].sc_portno;
  362                         umcs7840_config_tmp[n].callback = umcs7840_rw_callbacks[subunit][n];
  363                 }
  364                 error = usbd_transfer_setup(uaa->device,
  365                     &iface_index, sc->sc_ports[sc->sc_ucom[subunit].sc_portno].sc_xfer, umcs7840_config_tmp,
  366                     UMCS7840_N_TRANSFERS, sc, &sc->sc_mtx);
  367                 if (error) {
  368                         device_printf(dev, "allocating USB transfers failed for subunit %d of %d\n",
  369                             subunit + 1, sc->sc_numports);
  370                         goto detach;
  371                 }
  372         }
  373         error = usbd_transfer_setup(uaa->device,
  374             &iface_index, &sc->sc_intr_xfer, umcs7840_intr_config_data,
  375             1, sc, &sc->sc_mtx);
  376         if (error) {
  377                 device_printf(dev, "allocating USB transfers failed for interrupt\n");
  378                 goto detach;
  379         }
  380         /* clear stall at first run */
  381         mtx_lock(&sc->sc_mtx);
  382         for (subunit = 0; subunit < sc->sc_numports; ++subunit) {
  383                 usbd_xfer_set_stall(sc->sc_ports[sc->sc_ucom[subunit].sc_portno].sc_xfer[UMCS7840_BULK_RD_EP]);
  384                 usbd_xfer_set_stall(sc->sc_ports[sc->sc_ucom[subunit].sc_portno].sc_xfer[UMCS7840_BULK_WR_EP]);
  385         }
  386         mtx_unlock(&sc->sc_mtx);
  387 
  388         error = ucom_attach(&sc->sc_super_ucom, sc->sc_ucom, sc->sc_numports, sc,
  389             &umcs7840_callback, &sc->sc_mtx);
  390         if (error)
  391                 goto detach;
  392 
  393         ucom_set_pnpinfo_usb(&sc->sc_super_ucom, dev);
  394 
  395         return (0);
  396 
  397 detach:
  398         umcs7840_detach(dev);
  399         return (ENXIO);
  400 }
  401 
  402 static int
  403 umcs7840_detach(device_t dev)
  404 {
  405         struct umcs7840_softc *sc = device_get_softc(dev);
  406         int subunit;
  407 
  408         ucom_detach(&sc->sc_super_ucom, sc->sc_ucom);
  409 
  410         for (subunit = 0; subunit < sc->sc_numports; ++subunit)
  411                 usbd_transfer_unsetup(sc->sc_ports[sc->sc_ucom[subunit].sc_portno].sc_xfer, UMCS7840_N_TRANSFERS);
  412         usbd_transfer_unsetup(&sc->sc_intr_xfer, 1);
  413 
  414         device_claim_softc(dev);
  415 
  416         umcs7840_free_softc(sc);
  417 
  418         return (0);
  419 }
  420 
  421 UCOM_UNLOAD_DRAIN(umcs7840);
  422 
  423 static void
  424 umcs7840_free_softc(struct umcs7840_softc *sc)
  425 {
  426         if (ucom_unref(&sc->sc_super_ucom)) {
  427                 mtx_destroy(&sc->sc_mtx);
  428                 device_free_softc(sc);
  429         }
  430 }
  431 
  432 static void
  433 umcs7840_free(struct ucom_softc *ucom)
  434 {
  435         umcs7840_free_softc(ucom->sc_parent);
  436 }
  437 
  438 static void
  439 umcs7840_cfg_open(struct ucom_softc *ucom)
  440 {
  441         struct umcs7840_softc *sc = ucom->sc_parent;
  442         uint16_t pn = ucom->sc_portno;
  443         uint8_t data;
  444 
  445         /* If it very first open, finish global configuration */
  446         if (!sc->sc_driver_done) {
  447                 /*
  448                  * USB enumeration is finished, pass internal memory to FIFOs
  449                  * If it is done in the end of "attach", kernel panics.
  450                  */
  451                 if (umcs7840_get_reg_sync(sc, MCS7840_DEV_REG_CONTROL1, &data))
  452                         return;
  453                 data |= MCS7840_DEV_CONTROL1_DRIVER_DONE;
  454                 if (umcs7840_set_reg_sync(sc, MCS7840_DEV_REG_CONTROL1, data))
  455                         return;
  456                 sc->sc_driver_done = 1;
  457         }
  458         /* Toggle reset bit on-off */
  459         if (umcs7840_get_reg_sync(sc, umcs7840_port_registers[pn].reg_sp, &data))
  460                 return;
  461         data |= MCS7840_DEV_SPx_UART_RESET;
  462         if (umcs7840_set_reg_sync(sc, umcs7840_port_registers[pn].reg_sp, data))
  463                 return;
  464         data &= ~MCS7840_DEV_SPx_UART_RESET;
  465         if (umcs7840_set_reg_sync(sc, umcs7840_port_registers[pn].reg_sp, data))
  466                 return;
  467 
  468         /* Set RS-232 mode */
  469         if (umcs7840_set_UART_reg_sync(sc, pn, MCS7840_UART_REG_SCRATCHPAD, MCS7840_UART_SCRATCHPAD_RS232))
  470                 return;
  471 
  472         /* Disable RX on time of initialization */
  473         if (umcs7840_get_reg_sync(sc, umcs7840_port_registers[pn].reg_control, &data))
  474                 return;
  475         data |= MCS7840_DEV_CONTROLx_RX_DISABLE;
  476         if (umcs7840_set_reg_sync(sc, umcs7840_port_registers[pn].reg_control, data))
  477                 return;
  478 
  479         /* Disable all interrupts */
  480         if (umcs7840_set_UART_reg_sync(sc, pn, MCS7840_UART_REG_IER, 0))
  481                 return;
  482 
  483         /* Reset FIFO -- documented */
  484         if (umcs7840_set_UART_reg_sync(sc, pn, MCS7840_UART_REG_FCR, 0))
  485                 return;
  486         if (umcs7840_set_UART_reg_sync(sc, pn, MCS7840_UART_REG_FCR,
  487             MCS7840_UART_FCR_ENABLE | MCS7840_UART_FCR_FLUSHRHR |
  488             MCS7840_UART_FCR_FLUSHTHR | MCS7840_UART_FCR_RTL_1_14))
  489                 return;
  490 
  491         /* Set 8 bit, no parity, 1 stop bit -- documented */
  492         sc->sc_ports[pn].sc_lcr = MCS7840_UART_LCR_DATALEN8 | MCS7840_UART_LCR_STOPB1;
  493         if (umcs7840_set_UART_reg_sync(sc, pn, MCS7840_UART_REG_LCR, sc->sc_ports[pn].sc_lcr))
  494                 return;
  495 
  496         /*
  497          * Enable DTR/RTS on modem control, enable modem interrupts --
  498          * documented
  499          */
  500         sc->sc_ports[pn].sc_mcr = MCS7840_UART_MCR_IE;
  501         if (ucom->sc_tty == NULL || (ucom->sc_tty->t_termios.c_cflag & CNO_RTSDTR) == 0)
  502                 sc->sc_ports[pn].sc_mcr |= MCS7840_UART_MCR_DTR | MCS7840_UART_MCR_RTS;
  503         if (umcs7840_set_UART_reg_sync(sc, pn, MCS7840_UART_REG_MCR, sc->sc_ports[pn].sc_mcr))
  504                 return;
  505 
  506         /* Clearing Bulkin and Bulkout FIFO */
  507         if (umcs7840_get_reg_sync(sc, umcs7840_port_registers[pn].reg_sp, &data))
  508                 return;
  509         data |= MCS7840_DEV_SPx_RESET_OUT_FIFO | MCS7840_DEV_SPx_RESET_IN_FIFO;
  510         if (umcs7840_set_reg_sync(sc, umcs7840_port_registers[pn].reg_sp, data))
  511                 return;
  512         data &= ~(MCS7840_DEV_SPx_RESET_OUT_FIFO | MCS7840_DEV_SPx_RESET_IN_FIFO);
  513         if (umcs7840_set_reg_sync(sc, umcs7840_port_registers[pn].reg_sp, data))
  514                 return;
  515 
  516         /* Set speed 9600 */
  517         if (umcs7840_set_baudrate(sc, pn, 9600))
  518                 return;
  519 
  520         /* Finally enable all interrupts -- documented */
  521         /*
  522          * Copied from vendor driver, I don't know why we should read LCR
  523          * here
  524          */
  525         if (umcs7840_get_UART_reg_sync(sc, pn, MCS7840_UART_REG_LCR, &sc->sc_ports[pn].sc_lcr))
  526                 return;
  527         if (umcs7840_set_UART_reg_sync(sc, pn, MCS7840_UART_REG_IER,
  528             MCS7840_UART_IER_RXSTAT | MCS7840_UART_IER_MODEM))
  529                 return;
  530 
  531         /* Enable RX */
  532         if (umcs7840_get_reg_sync(sc, umcs7840_port_registers[pn].reg_control, &data))
  533                 return;
  534         data &= ~MCS7840_DEV_CONTROLx_RX_DISABLE;
  535         if (umcs7840_set_reg_sync(sc, umcs7840_port_registers[pn].reg_control, data))
  536                 return;
  537 
  538         DPRINTF("Port %d has been opened\n", pn);
  539 }
  540 
  541 static void
  542 umcs7840_cfg_close(struct ucom_softc *ucom)
  543 {
  544         struct umcs7840_softc *sc = ucom->sc_parent;
  545         uint16_t pn = ucom->sc_portno;
  546         uint8_t data;
  547 
  548         umcs7840_stop_read(ucom);
  549         umcs7840_stop_write(ucom);
  550 
  551         umcs7840_set_UART_reg_sync(sc, pn, MCS7840_UART_REG_MCR, 0);
  552         umcs7840_set_UART_reg_sync(sc, pn, MCS7840_UART_REG_IER, 0);
  553 
  554         /* Disable RX */
  555         if (umcs7840_get_reg_sync(sc, umcs7840_port_registers[pn].reg_control, &data))
  556                 return;
  557         data |= MCS7840_DEV_CONTROLx_RX_DISABLE;
  558         if (umcs7840_set_reg_sync(sc, umcs7840_port_registers[pn].reg_control, data))
  559                 return;
  560         DPRINTF("Port %d has been closed\n", pn);
  561 }
  562 
  563 static void
  564 umcs7840_cfg_set_dtr(struct ucom_softc *ucom, uint8_t onoff)
  565 {
  566         struct umcs7840_softc *sc = ucom->sc_parent;
  567         uint8_t pn = ucom->sc_portno;
  568 
  569         if (onoff)
  570                 sc->sc_ports[pn].sc_mcr |= MCS7840_UART_MCR_DTR;
  571         else
  572                 sc->sc_ports[pn].sc_mcr &= ~MCS7840_UART_MCR_DTR;
  573 
  574         umcs7840_set_UART_reg_sync(sc, pn, MCS7840_UART_REG_MCR, sc->sc_ports[pn].sc_mcr);
  575         DPRINTF("Port %d DTR set to: %s\n", pn, onoff ? "on" : "off");
  576 }
  577 
  578 static void
  579 umcs7840_cfg_set_rts(struct ucom_softc *ucom, uint8_t onoff)
  580 {
  581         struct umcs7840_softc *sc = ucom->sc_parent;
  582         uint8_t pn = ucom->sc_portno;
  583 
  584         if (onoff)
  585                 sc->sc_ports[pn].sc_mcr |= MCS7840_UART_MCR_RTS;
  586         else
  587                 sc->sc_ports[pn].sc_mcr &= ~MCS7840_UART_MCR_RTS;
  588 
  589         umcs7840_set_UART_reg_sync(sc, pn, MCS7840_UART_REG_MCR, sc->sc_ports[pn].sc_mcr);
  590         DPRINTF("Port %d RTS set to: %s\n", pn, onoff ? "on" : "off");
  591 }
  592 
  593 static void
  594 umcs7840_cfg_set_break(struct ucom_softc *ucom, uint8_t onoff)
  595 {
  596         struct umcs7840_softc *sc = ucom->sc_parent;
  597         uint8_t pn = ucom->sc_portno;
  598 
  599         if (onoff)
  600                 sc->sc_ports[pn].sc_lcr |= MCS7840_UART_LCR_BREAK;
  601         else
  602                 sc->sc_ports[pn].sc_lcr &= ~MCS7840_UART_LCR_BREAK;
  603 
  604         umcs7840_set_UART_reg_sync(sc, pn, MCS7840_UART_REG_LCR, sc->sc_ports[pn].sc_lcr);
  605         DPRINTF("Port %d BREAK set to: %s\n", pn, onoff ? "on" : "off");
  606 }
  607 
  608 static void
  609 umcs7840_cfg_param(struct ucom_softc *ucom, struct termios *t)
  610 {
  611         struct umcs7840_softc *sc = ucom->sc_parent;
  612         uint8_t pn = ucom->sc_portno;
  613         uint8_t lcr = sc->sc_ports[pn].sc_lcr;
  614         uint8_t mcr = sc->sc_ports[pn].sc_mcr;
  615 
  616         DPRINTF("Port %d config:\n", pn);
  617         if (t->c_cflag & CSTOPB) {
  618                 DPRINTF("  2 stop bits\n");
  619                 lcr |= MCS7840_UART_LCR_STOPB2;
  620         } else {
  621                 lcr |= MCS7840_UART_LCR_STOPB1;
  622                 DPRINTF("  1 stop bit\n");
  623         }
  624 
  625         lcr &= ~MCS7840_UART_LCR_PARITYMASK;
  626         if (t->c_cflag & PARENB) {
  627                 lcr |= MCS7840_UART_LCR_PARITYON;
  628                 if (t->c_cflag & PARODD) {
  629                         lcr = MCS7840_UART_LCR_PARITYODD;
  630                         DPRINTF("  parity on - odd\n");
  631                 } else {
  632                         lcr = MCS7840_UART_LCR_PARITYEVEN;
  633                         DPRINTF("  parity on - even\n");
  634                 }
  635         } else {
  636                 lcr &= ~MCS7840_UART_LCR_PARITYON;
  637                 DPRINTF("  parity off\n");
  638         }
  639 
  640         lcr &= ~MCS7840_UART_LCR_DATALENMASK;
  641         switch (t->c_cflag & CSIZE) {
  642         case CS5:
  643                 lcr |= MCS7840_UART_LCR_DATALEN5;
  644                 DPRINTF("  5 bit\n");
  645                 break;
  646         case CS6:
  647                 lcr |= MCS7840_UART_LCR_DATALEN6;
  648                 DPRINTF("  6 bit\n");
  649                 break;
  650         case CS7:
  651                 lcr |= MCS7840_UART_LCR_DATALEN7;
  652                 DPRINTF("  7 bit\n");
  653                 break;
  654         case CS8:
  655                 lcr |= MCS7840_UART_LCR_DATALEN8;
  656                 DPRINTF("  8 bit\n");
  657                 break;
  658         }
  659 
  660         if (t->c_cflag & CRTSCTS) {
  661                 mcr |= MCS7840_UART_MCR_CTSRTS;
  662                 DPRINTF("  CTS/RTS\n");
  663         } else
  664                 mcr &= ~MCS7840_UART_MCR_CTSRTS;
  665 
  666         if (t->c_cflag & (CDTR_IFLOW | CDSR_OFLOW)) {
  667                 mcr |= MCS7840_UART_MCR_DTRDSR;
  668                 DPRINTF("  DTR/DSR\n");
  669         } else
  670                 mcr &= ~MCS7840_UART_MCR_DTRDSR;
  671 
  672         sc->sc_ports[pn].sc_lcr = lcr;
  673         umcs7840_set_UART_reg_sync(sc, pn, MCS7840_UART_REG_LCR, sc->sc_ports[pn].sc_lcr);
  674         DPRINTF("Port %d LCR=%02x\n", pn, sc->sc_ports[pn].sc_lcr);
  675 
  676         sc->sc_ports[pn].sc_mcr = mcr;
  677         umcs7840_set_UART_reg_sync(sc, pn, MCS7840_UART_REG_MCR, sc->sc_ports[pn].sc_mcr);
  678         DPRINTF("Port %d MCR=%02x\n", pn, sc->sc_ports[pn].sc_mcr);
  679 
  680         umcs7840_set_baudrate(sc, pn, t->c_ospeed);
  681 }
  682 
  683 static int
  684 umcs7840_pre_param(struct ucom_softc *ucom, struct termios *t)
  685 {
  686         uint8_t clk;
  687         uint16_t divisor;
  688 
  689         if (umcs7840_calc_baudrate(t->c_ospeed, &divisor, &clk) || !divisor)
  690                 return (EINVAL);
  691         return (0);
  692 }
  693 
  694 static void
  695 umcs7840_start_read(struct ucom_softc *ucom)
  696 {
  697         struct umcs7840_softc *sc = ucom->sc_parent;
  698         uint8_t pn = ucom->sc_portno;
  699 
  700         /* Start interrupt transfer */
  701         usbd_transfer_start(sc->sc_intr_xfer);
  702 
  703         /* Start read transfer */
  704         usbd_transfer_start(sc->sc_ports[pn].sc_xfer[UMCS7840_BULK_RD_EP]);
  705 }
  706 
  707 static void
  708 umcs7840_stop_read(struct ucom_softc *ucom)
  709 {
  710         struct umcs7840_softc *sc = ucom->sc_parent;
  711         uint8_t pn = ucom->sc_portno;
  712 
  713         /* Stop read transfer */
  714         usbd_transfer_stop(sc->sc_ports[pn].sc_xfer[UMCS7840_BULK_RD_EP]);
  715 }
  716 
  717 static void
  718 umcs7840_start_write(struct ucom_softc *ucom)
  719 {
  720         struct umcs7840_softc *sc = ucom->sc_parent;
  721         uint8_t pn = ucom->sc_portno;
  722 
  723         /* Start interrupt transfer */
  724         usbd_transfer_start(sc->sc_intr_xfer);
  725 
  726         /* Start write transfer */
  727         usbd_transfer_start(sc->sc_ports[pn].sc_xfer[UMCS7840_BULK_WR_EP]);
  728 }
  729 
  730 static void
  731 umcs7840_stop_write(struct ucom_softc *ucom)
  732 {
  733         struct umcs7840_softc *sc = ucom->sc_parent;
  734         uint8_t pn = ucom->sc_portno;
  735 
  736         /* Stop write transfer */
  737         usbd_transfer_stop(sc->sc_ports[pn].sc_xfer[UMCS7840_BULK_WR_EP]);
  738 }
  739 
  740 static void
  741 umcs7840_cfg_get_status(struct ucom_softc *ucom, uint8_t *lsr, uint8_t *msr)
  742 {
  743         struct umcs7840_softc *sc = ucom->sc_parent;
  744         uint8_t pn = ucom->sc_portno;
  745         uint8_t hw_msr = 0;     /* local modem status register */
  746 
  747         /*
  748          * Read status registers.  MSR bits need translation from ns16550 to
  749          * SER_* values.  LSR bits are ns16550 in hardware and ucom.
  750          */
  751         umcs7840_get_UART_reg_sync(sc, pn, MCS7840_UART_REG_LSR, lsr);
  752         umcs7840_get_UART_reg_sync(sc, pn, MCS7840_UART_REG_MSR, &hw_msr);
  753 
  754         if (hw_msr & MCS7840_UART_MSR_NEGCTS)
  755                 *msr |= SER_CTS;
  756 
  757         if (hw_msr & MCS7840_UART_MSR_NEGDCD)
  758                 *msr |= SER_DCD;
  759 
  760         if (hw_msr & MCS7840_UART_MSR_NEGRI)
  761                 *msr |= SER_RI;
  762 
  763         if (hw_msr & MCS7840_UART_MSR_NEGDSR)
  764                 *msr |= SER_DSR;
  765 
  766         DPRINTF("Port %d status: LSR=%02x MSR=%02x\n", ucom->sc_portno, *lsr, *msr);
  767 }
  768 
  769 static void
  770 umcs7840_intr_callback(struct usb_xfer *xfer, usb_error_t error)
  771 {
  772         struct umcs7840_softc *sc = usbd_xfer_softc(xfer);
  773         struct usb_page_cache *pc;
  774         uint8_t buf[13];
  775         int actlen;
  776         int subunit;
  777 
  778         usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
  779 
  780         switch (USB_GET_STATE(xfer)) {
  781         case USB_ST_TRANSFERRED:
  782                 if (actlen == 5 || actlen == 13) {
  783                         pc = usbd_xfer_get_frame(xfer, 0);
  784                         usbd_copy_out(pc, 0, buf, actlen);
  785                         /* Check status of all ports */
  786                         for (subunit = 0; subunit < sc->sc_numports; ++subunit) {
  787                                 uint8_t pn = sc->sc_ucom[subunit].sc_portno;
  788 
  789                                 if (buf[pn] & MCS7840_UART_ISR_NOPENDING)
  790                                         continue;
  791                                 DPRINTF("Port %d has pending interrupt: %02x (FIFO: %02x)\n", pn, buf[pn] & MCS7840_UART_ISR_INTMASK, buf[pn] & (~MCS7840_UART_ISR_INTMASK));
  792                                 switch (buf[pn] & MCS7840_UART_ISR_INTMASK) {
  793                                 case MCS7840_UART_ISR_RXERR:
  794                                 case MCS7840_UART_ISR_RXHASDATA:
  795                                 case MCS7840_UART_ISR_RXTIMEOUT:
  796                                 case MCS7840_UART_ISR_MSCHANGE:
  797                                         ucom_status_change(&sc->sc_ucom[subunit]);
  798                                         break;
  799                                 default:
  800                                         /* Do nothing */
  801                                         break;
  802                                 }
  803                         }
  804                 } else
  805                         device_printf(sc->sc_dev, "Invalid interrupt data length %d", actlen);
  806                 /* FALLTHROUGH */
  807         case USB_ST_SETUP:
  808 tr_setup:
  809                 usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
  810                 usbd_transfer_submit(xfer);
  811                 return;
  812 
  813         default:                        /* Error */
  814                 if (error != USB_ERR_CANCELLED) {
  815                         /* try to clear stall first */
  816                         usbd_xfer_set_stall(xfer);
  817                         goto tr_setup;
  818                 }
  819                 return;
  820         }
  821 }
  822 
  823 static void
  824 umcs7840_read_callback1(struct usb_xfer *xfer, usb_error_t error)
  825 {
  826         umcs7840_read_callbackN(xfer, error, 0);
  827 }
  828 
  829 static void
  830 umcs7840_read_callback2(struct usb_xfer *xfer, usb_error_t error)
  831 {
  832         umcs7840_read_callbackN(xfer, error, 1);
  833 }
  834 static void
  835 umcs7840_read_callback3(struct usb_xfer *xfer, usb_error_t error)
  836 {
  837         umcs7840_read_callbackN(xfer, error, 2);
  838 }
  839 
  840 static void
  841 umcs7840_read_callback4(struct usb_xfer *xfer, usb_error_t error)
  842 {
  843         umcs7840_read_callbackN(xfer, error, 3);
  844 }
  845 
  846 static void
  847 umcs7840_read_callbackN(struct usb_xfer *xfer, usb_error_t error, uint8_t subunit)
  848 {
  849         struct umcs7840_softc *sc = usbd_xfer_softc(xfer);
  850         struct ucom_softc *ucom = &sc->sc_ucom[subunit];
  851         struct usb_page_cache *pc;
  852         int actlen;
  853 
  854         usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
  855 
  856         DPRINTF("Port %d read, state = %d, data length = %d\n", ucom->sc_portno, USB_GET_STATE(xfer), actlen);
  857 
  858         switch (USB_GET_STATE(xfer)) {
  859         case USB_ST_TRANSFERRED:
  860                 pc = usbd_xfer_get_frame(xfer, 0);
  861                 ucom_put_data(ucom, pc, 0, actlen);
  862                 /* FALLTHROUGH */
  863         case USB_ST_SETUP:
  864 tr_setup:
  865                 usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
  866                 usbd_transfer_submit(xfer);
  867                 return;
  868 
  869         default:                        /* Error */
  870                 if (error != USB_ERR_CANCELLED) {
  871                         /* try to clear stall first */
  872                         usbd_xfer_set_stall(xfer);
  873                         goto tr_setup;
  874                 }
  875                 return;
  876         }
  877 }
  878 
  879 static void
  880 umcs7840_write_callback1(struct usb_xfer *xfer, usb_error_t error)
  881 {
  882         umcs7840_write_callbackN(xfer, error, 0);
  883 }
  884 
  885 static void
  886 umcs7840_write_callback2(struct usb_xfer *xfer, usb_error_t error)
  887 {
  888         umcs7840_write_callbackN(xfer, error, 1);
  889 }
  890 
  891 static void
  892 umcs7840_write_callback3(struct usb_xfer *xfer, usb_error_t error)
  893 {
  894         umcs7840_write_callbackN(xfer, error, 2);
  895 }
  896 
  897 static void
  898 umcs7840_write_callback4(struct usb_xfer *xfer, usb_error_t error)
  899 {
  900         umcs7840_write_callbackN(xfer, error, 3);
  901 }
  902 
  903 static void
  904 umcs7840_write_callbackN(struct usb_xfer *xfer, usb_error_t error, uint8_t subunit)
  905 {
  906         struct umcs7840_softc *sc = usbd_xfer_softc(xfer);
  907         struct ucom_softc *ucom = &sc->sc_ucom[subunit];
  908         struct usb_page_cache *pc;
  909         uint32_t actlen;
  910 
  911         DPRINTF("Port %d write, state = %d\n", ucom->sc_portno, USB_GET_STATE(xfer));
  912 
  913         switch (USB_GET_STATE(xfer)) {
  914         case USB_ST_SETUP:
  915         case USB_ST_TRANSFERRED:
  916 tr_setup:
  917                 pc = usbd_xfer_get_frame(xfer, 0);
  918                 if (ucom_get_data(ucom, pc, 0, usbd_xfer_max_len(xfer), &actlen)) {
  919                         DPRINTF("Port %d write, has %d bytes\n", ucom->sc_portno, actlen);
  920                         usbd_xfer_set_frame_len(xfer, 0, actlen);
  921                         usbd_transfer_submit(xfer);
  922                 }
  923                 return;
  924 
  925         default:                        /* Error */
  926                 if (error != USB_ERR_CANCELLED) {
  927                         /* try to clear stall first */
  928                         usbd_xfer_set_stall(xfer);
  929                         goto tr_setup;
  930                 }
  931                 return;
  932         }
  933 }
  934 
  935 static void
  936 umcs7840_poll(struct ucom_softc *ucom)
  937 {
  938         struct umcs7840_softc *sc = ucom->sc_parent;
  939 
  940         DPRINTF("Port %d poll\n", ucom->sc_portno);
  941         usbd_transfer_poll(sc->sc_ports[ucom->sc_portno].sc_xfer, UMCS7840_N_TRANSFERS);
  942         usbd_transfer_poll(&sc->sc_intr_xfer, 1);
  943 }
  944 
  945 static usb_error_t
  946 umcs7840_get_reg_sync(struct umcs7840_softc *sc, uint8_t reg, uint8_t *data)
  947 {
  948         struct usb_device_request req;
  949         usb_error_t err;
  950         uint16_t len;
  951 
  952         req.bmRequestType = UT_READ_VENDOR_DEVICE;
  953         req.bRequest = MCS7840_RDREQ;
  954         USETW(req.wValue, 0);
  955         USETW(req.wIndex, reg);
  956         USETW(req.wLength, UMCS7840_READ_LENGTH);
  957 
  958         err = usbd_do_request_proc(sc->sc_udev, &sc->sc_super_ucom.sc_tq, &req, (void *)data, 0, &len, UMCS7840_CTRL_TIMEOUT);
  959         if (err == USB_ERR_NORMAL_COMPLETION && len != 1) {
  960                 device_printf(sc->sc_dev, "Reading register %d failed: invalid length %d\n", reg, len);
  961                 return (USB_ERR_INVAL);
  962         } else if (err)
  963                 device_printf(sc->sc_dev, "Reading register %d failed: %s\n", reg, usbd_errstr(err));
  964         return (err);
  965 }
  966 
  967 static usb_error_t
  968 umcs7840_set_reg_sync(struct umcs7840_softc *sc, uint8_t reg, uint8_t data)
  969 {
  970         struct usb_device_request req;
  971         usb_error_t err;
  972 
  973         req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
  974         req.bRequest = MCS7840_WRREQ;
  975         USETW(req.wValue, data);
  976         USETW(req.wIndex, reg);
  977         USETW(req.wLength, 0);
  978 
  979         err = usbd_do_request_proc(sc->sc_udev, &sc->sc_super_ucom.sc_tq, &req, NULL, 0, NULL, UMCS7840_CTRL_TIMEOUT);
  980         if (err)
  981                 device_printf(sc->sc_dev, "Writing register %d failed: %s\n", reg, usbd_errstr(err));
  982 
  983         return (err);
  984 }
  985 
  986 static usb_error_t
  987 umcs7840_get_UART_reg_sync(struct umcs7840_softc *sc, uint8_t portno, uint8_t reg, uint8_t *data)
  988 {
  989         struct usb_device_request req;
  990         uint16_t wVal;
  991         usb_error_t err;
  992         uint16_t len;
  993 
  994         /* portno is port number */
  995         wVal = ((uint16_t)(portno + 1)) << 8;
  996 
  997         req.bmRequestType = UT_READ_VENDOR_DEVICE;
  998         req.bRequest = MCS7840_RDREQ;
  999         USETW(req.wValue, wVal);
 1000         USETW(req.wIndex, reg);
 1001         USETW(req.wLength, UMCS7840_READ_LENGTH);
 1002 
 1003         err = usbd_do_request_proc(sc->sc_udev, &sc->sc_super_ucom.sc_tq, &req, (void *)data, 0, &len, UMCS7840_CTRL_TIMEOUT);
 1004         if (err == USB_ERR_NORMAL_COMPLETION && len != 1) {
 1005                 device_printf(sc->sc_dev, "Reading UART%d register %d failed: invalid length %d\n", portno, reg, len);
 1006                 return (USB_ERR_INVAL);
 1007         } else if (err)
 1008                 device_printf(sc->sc_dev, "Reading UART%d register %d failed: %s\n", portno, reg, usbd_errstr(err));
 1009         return (err);
 1010 }
 1011 
 1012 static usb_error_t
 1013 umcs7840_set_UART_reg_sync(struct umcs7840_softc *sc, uint8_t portno, uint8_t reg, uint8_t data)
 1014 {
 1015         struct usb_device_request req;
 1016         usb_error_t err;
 1017         uint16_t wVal;
 1018 
 1019         /* portno is port number */
 1020         wVal = ((uint16_t)(portno + 1)) << 8 | data;
 1021 
 1022         req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
 1023         req.bRequest = MCS7840_WRREQ;
 1024         USETW(req.wValue, wVal);
 1025         USETW(req.wIndex, reg);
 1026         USETW(req.wLength, 0);
 1027 
 1028         err = usbd_do_request_proc(sc->sc_udev, &sc->sc_super_ucom.sc_tq, &req, NULL, 0, NULL, UMCS7840_CTRL_TIMEOUT);
 1029         if (err)
 1030                 device_printf(sc->sc_dev, "Writing UART%d register %d failed: %s\n", portno, reg, usbd_errstr(err));
 1031         return (err);
 1032 }
 1033 
 1034 static usb_error_t
 1035 umcs7840_set_baudrate(struct umcs7840_softc *sc, uint8_t portno, uint32_t rate)
 1036 {
 1037         usb_error_t err;
 1038         uint16_t divisor;
 1039         uint8_t clk;
 1040         uint8_t data;
 1041 
 1042         if (umcs7840_calc_baudrate(rate, &divisor, &clk)) {
 1043                 DPRINTF("Port %d bad speed: %d\n", portno, rate);
 1044                 return (-1);
 1045         }
 1046         if (divisor == 0 || (clk & MCS7840_DEV_SPx_CLOCK_MASK) != clk) {
 1047                 DPRINTF("Port %d bad speed calculation: %d\n", portno, rate);
 1048                 return (-1);
 1049         }
 1050         DPRINTF("Port %d set speed: %d (%02x / %d)\n", portno, rate, clk, divisor);
 1051 
 1052         /* Set clock source for standard BAUD frequences */
 1053         err = umcs7840_get_reg_sync(sc, umcs7840_port_registers[portno].reg_sp, &data);
 1054         if (err)
 1055                 return (err);
 1056         data &= MCS7840_DEV_SPx_CLOCK_MASK;
 1057         data |= clk;
 1058         err = umcs7840_set_reg_sync(sc, umcs7840_port_registers[portno].reg_sp, data);
 1059         if (err)
 1060                 return (err);
 1061 
 1062         /* Set divider */
 1063         sc->sc_ports[portno].sc_lcr |= MCS7840_UART_LCR_DIVISORS;
 1064         err = umcs7840_set_UART_reg_sync(sc, portno, MCS7840_UART_REG_LCR, sc->sc_ports[portno].sc_lcr);
 1065         if (err)
 1066                 return (err);
 1067 
 1068         err = umcs7840_set_UART_reg_sync(sc, portno, MCS7840_UART_REG_DLL, (uint8_t)(divisor & 0xff));
 1069         if (err)
 1070                 return (err);
 1071         err = umcs7840_set_UART_reg_sync(sc, portno, MCS7840_UART_REG_DLM, (uint8_t)((divisor >> 8) & 0xff));
 1072         if (err)
 1073                 return (err);
 1074 
 1075         /* Turn off access to DLL/DLM registers of UART */
 1076         sc->sc_ports[portno].sc_lcr &= ~MCS7840_UART_LCR_DIVISORS;
 1077         err = umcs7840_set_UART_reg_sync(sc, portno, MCS7840_UART_REG_LCR, sc->sc_ports[portno].sc_lcr);
 1078         if (err)
 1079                 return (err);
 1080         return (0);
 1081 }
 1082 
 1083 /* Maximum speeds for standard frequences, when PLL is not used */
 1084 static const uint32_t umcs7840_baudrate_divisors[] = {0, 115200, 230400, 403200, 460800, 806400, 921600, 1572864, 3145728,};
 1085 static const uint8_t umcs7840_baudrate_divisors_len = nitems(umcs7840_baudrate_divisors);
 1086 
 1087 static usb_error_t
 1088 umcs7840_calc_baudrate(uint32_t rate, uint16_t *divisor, uint8_t *clk)
 1089 {
 1090         uint8_t i = 0;
 1091 
 1092         if (rate > umcs7840_baudrate_divisors[umcs7840_baudrate_divisors_len - 1])
 1093                 return (-1);
 1094 
 1095         for (i = 0; i < umcs7840_baudrate_divisors_len - 1 &&
 1096             !(rate > umcs7840_baudrate_divisors[i] && rate <= umcs7840_baudrate_divisors[i + 1]); ++i);
 1097         if (rate == 0)
 1098                 *divisor = 1;   /* XXX */
 1099         else
 1100                 *divisor = umcs7840_baudrate_divisors[i + 1] / rate;
 1101         /* 0x00 .. 0x70 */
 1102         *clk = i << MCS7840_DEV_SPx_CLOCK_SHIFT;
 1103         return (0);
 1104 }

Cache object: d664f2ad0390945c721864c542aae1ae


[ 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.