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/uchcom.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: uchcom.c,v 1.1 2007/09/03 17:57:37 tshiozak Exp $      */
    2 
    3 /*-
    4  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD AND BSD-2-Clause-NetBSD
    5  *
    6  * Copyright (c) 2007, Takanori Watanabe
    7  * All rights reserved.
    8  *
    9  * Redistribution and use in source and binary forms, with or without
   10  * modification, are permitted provided that the following conditions
   11  * are met:
   12  * 1. Redistributions of source code must retain the above copyright
   13  *    notice, this list of conditions and the following disclaimer.
   14  * 2. Redistributions in binary form must reproduce the above copyright
   15  *    notice, this list of conditions and the following disclaimer in the
   16  *    documentation and/or other materials provided with the distribution.
   17  *
   18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   21  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   28  * SUCH DAMAGE.
   29  */
   30 
   31 /*
   32  * Copyright (c) 2007 The NetBSD Foundation, Inc.
   33  * All rights reserved.
   34  *
   35  * This code is derived from software contributed to The NetBSD Foundation
   36  * by Takuya SHIOZAKI (tshiozak@netbsd.org).
   37  *
   38  * Redistribution and use in source and binary forms, with or without
   39  * modification, are permitted provided that the following conditions
   40  * are met:
   41  * 1. Redistributions of source code must retain the above copyright
   42  *    notice, this list of conditions and the following disclaimer.
   43  * 2. Redistributions in binary form must reproduce the above copyright
   44  *    notice, this list of conditions and the following disclaimer in the
   45  *    documentation and/or other materials provided with the distribution.
   46  *
   47  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
   48  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
   49  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
   50  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
   51  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
   52  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
   53  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
   54  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
   55  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
   56  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   57  * POSSIBILITY OF SUCH DAMAGE.
   58  */
   59 
   60 #include <sys/cdefs.h>
   61 __FBSDID("$FreeBSD$");
   62 
   63 /*
   64  * Driver for WinChipHead CH341/340, the worst USB-serial chip in the
   65  * world.
   66  */
   67 
   68 #include <sys/stdint.h>
   69 #include <sys/stddef.h>
   70 #include <sys/param.h>
   71 #include <sys/queue.h>
   72 #include <sys/types.h>
   73 #include <sys/systm.h>
   74 #include <sys/kernel.h>
   75 #include <sys/bus.h>
   76 #include <sys/module.h>
   77 #include <sys/lock.h>
   78 #include <sys/mutex.h>
   79 #include <sys/condvar.h>
   80 #include <sys/sysctl.h>
   81 #include <sys/sx.h>
   82 #include <sys/unistd.h>
   83 #include <sys/callout.h>
   84 #include <sys/malloc.h>
   85 #include <sys/priv.h>
   86 
   87 #include <dev/usb/usb.h>
   88 #include <dev/usb/usbdi.h>
   89 #include <dev/usb/usbdi_util.h>
   90 #include "usbdevs.h"
   91 
   92 #define USB_DEBUG_VAR uchcom_debug
   93 #include <dev/usb/usb_debug.h>
   94 #include <dev/usb/usb_process.h>
   95 
   96 #include <dev/usb/serial/usb_serial.h>
   97 
   98 #ifdef USB_DEBUG
   99 static int uchcom_debug = 0;
  100 
  101 static SYSCTL_NODE(_hw_usb, OID_AUTO, uchcom, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
  102     "USB uchcom");
  103 SYSCTL_INT(_hw_usb_uchcom, OID_AUTO, debug, CTLFLAG_RWTUN,
  104     &uchcom_debug, 0, "uchcom debug level");
  105 #endif
  106 
  107 #define UCHCOM_IFACE_INDEX      0
  108 #define UCHCOM_CONFIG_INDEX     0
  109 
  110 #define UCHCOM_REV_CH340        0x0250
  111 #define UCHCOM_INPUT_BUF_SIZE   8
  112 
  113 #define UCHCOM_REQ_GET_VERSION  0x5F
  114 #define UCHCOM_REQ_READ_REG     0x95
  115 #define UCHCOM_REQ_WRITE_REG    0x9A
  116 #define UCHCOM_REQ_RESET        0xA1
  117 #define UCHCOM_REQ_SET_DTRRTS   0xA4
  118 
  119 #define UCHCOM_REG_STAT1        0x06
  120 #define UCHCOM_REG_STAT2        0x07
  121 #define UCHCOM_REG_BPS_PRE      0x12
  122 #define UCHCOM_REG_BPS_DIV      0x13
  123 #define UCHCOM_REG_BPS_MOD      0x14
  124 #define UCHCOM_REG_BPS_PAD      0x0F
  125 #define UCHCOM_REG_BREAK1       0x05
  126 #define UCHCOM_REG_LCR1         0x18
  127 #define UCHCOM_REG_LCR2         0x25
  128 
  129 #define UCHCOM_VER_20           0x20
  130 #define UCHCOM_VER_30           0x30
  131 
  132 #define UCHCOM_BASE_UNKNOWN     0
  133 #define UCHCOM_BPS_MOD_BASE     20000000
  134 #define UCHCOM_BPS_MOD_BASE_OFS 1100
  135 
  136 #define UCHCOM_DTR_MASK         0x20
  137 #define UCHCOM_RTS_MASK         0x40
  138 
  139 #define UCHCOM_BRK_MASK         0x01
  140 
  141 #define UCHCOM_LCR1_MASK        0xAF
  142 #define UCHCOM_LCR2_MASK        0x07
  143 #define UCHCOM_LCR1_RX          0x80
  144 #define UCHCOM_LCR1_TX          0x40
  145 #define UCHCOM_LCR1_PARENB      0x08
  146 #define UCHCOM_LCR1_CS8         0x03
  147 #define UCHCOM_LCR2_PAREVEN     0x07
  148 #define UCHCOM_LCR2_PARODD      0x06
  149 #define UCHCOM_LCR2_PARMARK     0x05
  150 #define UCHCOM_LCR2_PARSPACE    0x04
  151 
  152 #define UCHCOM_INTR_STAT1       0x02
  153 #define UCHCOM_INTR_STAT2       0x03
  154 #define UCHCOM_INTR_LEAST       4
  155 
  156 #define UCHCOM_BULK_BUF_SIZE 1024       /* bytes */
  157 
  158 enum {
  159         UCHCOM_BULK_DT_WR,
  160         UCHCOM_BULK_DT_RD,
  161         UCHCOM_INTR_DT_RD,
  162         UCHCOM_N_TRANSFER,
  163 };
  164 
  165 struct uchcom_softc {
  166         struct ucom_super_softc sc_super_ucom;
  167         struct ucom_softc sc_ucom;
  168 
  169         struct usb_xfer *sc_xfer[UCHCOM_N_TRANSFER];
  170         struct usb_device *sc_udev;
  171         struct mtx sc_mtx;
  172 
  173         uint8_t sc_dtr;                 /* local copy */
  174         uint8_t sc_rts;                 /* local copy */
  175         uint8_t sc_version;
  176         uint8_t sc_msr;
  177         uint8_t sc_lsr;                 /* local status register */
  178 };
  179 
  180 struct uchcom_divider {
  181         uint8_t dv_prescaler;
  182         uint8_t dv_div;
  183         uint8_t dv_mod;
  184 };
  185 
  186 struct uchcom_divider_record {
  187         uint32_t dvr_high;
  188         uint32_t dvr_low;
  189         uint32_t dvr_base_clock;
  190         struct uchcom_divider dvr_divider;
  191 };
  192 
  193 static const struct uchcom_divider_record dividers[] =
  194 {
  195         {307200, 307200, UCHCOM_BASE_UNKNOWN, {7, 0xD9, 0}},
  196         {921600, 921600, UCHCOM_BASE_UNKNOWN, {7, 0xF3, 0}},
  197         {2999999, 23530, 6000000, {3, 0, 0}},
  198         {23529, 2942, 750000, {2, 0, 0}},
  199         {2941, 368, 93750, {1, 0, 0}},
  200         {367, 1, 11719, {0, 0, 0}},
  201 };
  202 
  203 #define NUM_DIVIDERS    nitems(dividers)
  204 
  205 static const STRUCT_USB_HOST_ID uchcom_devs[] = {
  206         {USB_VPI(USB_VENDOR_WCH, USB_PRODUCT_WCH_CH341SER, 0)},
  207         {USB_VPI(USB_VENDOR_WCH2, USB_PRODUCT_WCH2_CH341SER, 0)},
  208         {USB_VPI(USB_VENDOR_WCH2, USB_PRODUCT_WCH2_CH341SER_2, 0)},
  209         {USB_VPI(USB_VENDOR_WCH2, USB_PRODUCT_WCH2_CH341SER_3, 0)},
  210 };
  211 
  212 /* protypes */
  213 
  214 static void     uchcom_free(struct ucom_softc *);
  215 static int      uchcom_pre_param(struct ucom_softc *, struct termios *);
  216 static void     uchcom_cfg_get_status(struct ucom_softc *, uint8_t *,
  217                     uint8_t *);
  218 static void     uchcom_cfg_open(struct ucom_softc *ucom);
  219 static void     uchcom_cfg_param(struct ucom_softc *, struct termios *);
  220 static void     uchcom_cfg_set_break(struct ucom_softc *, uint8_t);
  221 static void     uchcom_cfg_set_dtr(struct ucom_softc *, uint8_t);
  222 static void     uchcom_cfg_set_rts(struct ucom_softc *, uint8_t);
  223 static void     uchcom_start_read(struct ucom_softc *);
  224 static void     uchcom_start_write(struct ucom_softc *);
  225 static void     uchcom_stop_read(struct ucom_softc *);
  226 static void     uchcom_stop_write(struct ucom_softc *);
  227 static void     uchcom_update_version(struct uchcom_softc *);
  228 static void     uchcom_convert_status(struct uchcom_softc *, uint8_t);
  229 static void     uchcom_update_status(struct uchcom_softc *);
  230 static void     uchcom_set_dtr_rts(struct uchcom_softc *);
  231 static int      uchcom_calc_divider_settings(struct uchcom_divider *, uint32_t);
  232 static void     uchcom_set_baudrate(struct uchcom_softc *, uint32_t);
  233 static void     uchcom_poll(struct ucom_softc *ucom);
  234 
  235 static device_probe_t uchcom_probe;
  236 static device_attach_t uchcom_attach;
  237 static device_detach_t uchcom_detach;
  238 static void uchcom_free_softc(struct uchcom_softc *);
  239 
  240 static usb_callback_t uchcom_intr_callback;
  241 static usb_callback_t uchcom_write_callback;
  242 static usb_callback_t uchcom_read_callback;
  243 
  244 static const struct usb_config uchcom_config_data[UCHCOM_N_TRANSFER] = {
  245         [UCHCOM_BULK_DT_WR] = {
  246                 .type = UE_BULK,
  247                 .endpoint = UE_ADDR_ANY,
  248                 .direction = UE_DIR_OUT,
  249                 .bufsize = UCHCOM_BULK_BUF_SIZE,
  250                 .flags = {.pipe_bof = 1,.force_short_xfer = 1,},
  251                 .callback = &uchcom_write_callback,
  252         },
  253 
  254         [UCHCOM_BULK_DT_RD] = {
  255                 .type = UE_BULK,
  256                 .endpoint = UE_ADDR_ANY,
  257                 .direction = UE_DIR_IN,
  258                 .bufsize = UCHCOM_BULK_BUF_SIZE,
  259                 .flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
  260                 .callback = &uchcom_read_callback,
  261         },
  262 
  263         [UCHCOM_INTR_DT_RD] = {
  264                 .type = UE_INTERRUPT,
  265                 .endpoint = UE_ADDR_ANY,
  266                 .direction = UE_DIR_IN,
  267                 .flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
  268                 .bufsize = 0,   /* use wMaxPacketSize */
  269                 .callback = &uchcom_intr_callback,
  270         },
  271 };
  272 
  273 static struct ucom_callback uchcom_callback = {
  274         .ucom_cfg_get_status = &uchcom_cfg_get_status,
  275         .ucom_cfg_set_dtr = &uchcom_cfg_set_dtr,
  276         .ucom_cfg_set_rts = &uchcom_cfg_set_rts,
  277         .ucom_cfg_set_break = &uchcom_cfg_set_break,
  278         .ucom_cfg_open = &uchcom_cfg_open,
  279         .ucom_cfg_param = &uchcom_cfg_param,
  280         .ucom_pre_param = &uchcom_pre_param,
  281         .ucom_start_read = &uchcom_start_read,
  282         .ucom_stop_read = &uchcom_stop_read,
  283         .ucom_start_write = &uchcom_start_write,
  284         .ucom_stop_write = &uchcom_stop_write,
  285         .ucom_poll = &uchcom_poll,
  286         .ucom_free = &uchcom_free,
  287 };
  288 
  289 /* ----------------------------------------------------------------------
  290  * driver entry points
  291  */
  292 
  293 static int
  294 uchcom_probe(device_t dev)
  295 {
  296         struct usb_attach_arg *uaa = device_get_ivars(dev);
  297 
  298         DPRINTFN(11, "\n");
  299 
  300         if (uaa->usb_mode != USB_MODE_HOST) {
  301                 return (ENXIO);
  302         }
  303         if (uaa->info.bConfigIndex != UCHCOM_CONFIG_INDEX) {
  304                 return (ENXIO);
  305         }
  306         if (uaa->info.bIfaceIndex != UCHCOM_IFACE_INDEX) {
  307                 return (ENXIO);
  308         }
  309         return (usbd_lookup_id_by_uaa(uchcom_devs, sizeof(uchcom_devs), uaa));
  310 }
  311 
  312 static int
  313 uchcom_attach(device_t dev)
  314 {
  315         struct uchcom_softc *sc = device_get_softc(dev);
  316         struct usb_attach_arg *uaa = device_get_ivars(dev);
  317         int error;
  318         uint8_t iface_index;
  319 
  320         DPRINTFN(11, "\n");
  321 
  322         device_set_usb_desc(dev);
  323         mtx_init(&sc->sc_mtx, "uchcom", NULL, MTX_DEF);
  324         ucom_ref(&sc->sc_super_ucom);
  325 
  326         sc->sc_udev = uaa->device;
  327 
  328         switch (uaa->info.idProduct) {
  329         case USB_PRODUCT_WCH2_CH341SER:
  330                 device_printf(dev, "CH340 detected\n");
  331                 break;
  332         case USB_PRODUCT_WCH2_CH341SER_2:
  333         case USB_PRODUCT_WCH2_CH341SER_3:
  334                 device_printf(dev, "CH341 detected\n");
  335                 break;
  336         default:
  337                 device_printf(dev, "New CH340/CH341 product 0x%04x detected\n",
  338                     uaa->info.idProduct);
  339                 break;
  340         }
  341 
  342         iface_index = UCHCOM_IFACE_INDEX;
  343         error = usbd_transfer_setup(uaa->device,
  344             &iface_index, sc->sc_xfer, uchcom_config_data,
  345             UCHCOM_N_TRANSFER, sc, &sc->sc_mtx);
  346 
  347         if (error) {
  348                 DPRINTF("one or more missing USB endpoints, "
  349                     "error=%s\n", usbd_errstr(error));
  350                 goto detach;
  351         }
  352 
  353         /* clear stall at first run */
  354         mtx_lock(&sc->sc_mtx);
  355         usbd_xfer_set_stall(sc->sc_xfer[UCHCOM_BULK_DT_WR]);
  356         usbd_xfer_set_stall(sc->sc_xfer[UCHCOM_BULK_DT_RD]);
  357         mtx_unlock(&sc->sc_mtx);
  358 
  359         error = ucom_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc,
  360             &uchcom_callback, &sc->sc_mtx);
  361         if (error) {
  362                 goto detach;
  363         }
  364         ucom_set_pnpinfo_usb(&sc->sc_super_ucom, dev);
  365 
  366         return (0);
  367 
  368 detach:
  369         uchcom_detach(dev);
  370         return (ENXIO);
  371 }
  372 
  373 static int
  374 uchcom_detach(device_t dev)
  375 {
  376         struct uchcom_softc *sc = device_get_softc(dev);
  377 
  378         DPRINTFN(11, "\n");
  379 
  380         ucom_detach(&sc->sc_super_ucom, &sc->sc_ucom);
  381         usbd_transfer_unsetup(sc->sc_xfer, UCHCOM_N_TRANSFER);
  382 
  383         device_claim_softc(dev);
  384 
  385         uchcom_free_softc(sc);
  386 
  387         return (0);
  388 }
  389 
  390 UCOM_UNLOAD_DRAIN(uchcom);
  391 
  392 static void
  393 uchcom_free_softc(struct uchcom_softc *sc)
  394 {
  395         if (ucom_unref(&sc->sc_super_ucom)) {
  396                 mtx_destroy(&sc->sc_mtx);
  397                 device_free_softc(sc);
  398         }
  399 }
  400 
  401 static void
  402 uchcom_free(struct ucom_softc *ucom)
  403 {
  404         uchcom_free_softc(ucom->sc_parent);
  405 }
  406 
  407 /* ----------------------------------------------------------------------
  408  * low level i/o
  409  */
  410 
  411 static void
  412 uchcom_ctrl_write(struct uchcom_softc *sc, uint8_t reqno,
  413     uint16_t value, uint16_t index)
  414 {
  415         struct usb_device_request req;
  416 
  417         req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
  418         req.bRequest = reqno;
  419         USETW(req.wValue, value);
  420         USETW(req.wIndex, index);
  421         USETW(req.wLength, 0);
  422 
  423         DPRINTF("WR REQ 0x%02X VAL 0x%04X IDX 0x%04X\n",
  424             reqno, value, index);
  425         ucom_cfg_do_request(sc->sc_udev,
  426             &sc->sc_ucom, &req, NULL, 0, 1000);
  427 }
  428 
  429 static void
  430 uchcom_ctrl_read(struct uchcom_softc *sc, uint8_t reqno,
  431     uint16_t value, uint16_t index, void *buf, uint16_t buflen)
  432 {
  433         struct usb_device_request req;
  434 
  435         req.bmRequestType = UT_READ_VENDOR_DEVICE;
  436         req.bRequest = reqno;
  437         USETW(req.wValue, value);
  438         USETW(req.wIndex, index);
  439         USETW(req.wLength, buflen);
  440 
  441         DPRINTF("RD REQ 0x%02X VAL 0x%04X IDX 0x%04X LEN %d\n",
  442             reqno, value, index, buflen);
  443         ucom_cfg_do_request(sc->sc_udev,
  444             &sc->sc_ucom, &req, buf, USB_SHORT_XFER_OK, 1000);
  445 }
  446 
  447 static void
  448 uchcom_write_reg(struct uchcom_softc *sc,
  449     uint8_t reg1, uint8_t val1, uint8_t reg2, uint8_t val2)
  450 {
  451         DPRINTF("0x%02X<-0x%02X, 0x%02X<-0x%02X\n",
  452             (unsigned)reg1, (unsigned)val1,
  453             (unsigned)reg2, (unsigned)val2);
  454         uchcom_ctrl_write(
  455             sc, UCHCOM_REQ_WRITE_REG,
  456             reg1 | ((uint16_t)reg2 << 8), val1 | ((uint16_t)val2 << 8));
  457 }
  458 
  459 static void
  460 uchcom_read_reg(struct uchcom_softc *sc,
  461     uint8_t reg1, uint8_t *rval1, uint8_t reg2, uint8_t *rval2)
  462 {
  463         uint8_t buf[UCHCOM_INPUT_BUF_SIZE];
  464 
  465         uchcom_ctrl_read(
  466             sc, UCHCOM_REQ_READ_REG,
  467             reg1 | ((uint16_t)reg2 << 8), 0, buf, sizeof(buf));
  468 
  469         DPRINTF("0x%02X->0x%02X, 0x%02X->0x%02X\n",
  470             (unsigned)reg1, (unsigned)buf[0],
  471             (unsigned)reg2, (unsigned)buf[1]);
  472 
  473         if (rval1)
  474                 *rval1 = buf[0];
  475         if (rval2)
  476                 *rval2 = buf[1];
  477 }
  478 
  479 static void
  480 uchcom_get_version(struct uchcom_softc *sc, uint8_t *rver)
  481 {
  482         uint8_t buf[UCHCOM_INPUT_BUF_SIZE];
  483 
  484         uchcom_ctrl_read(sc, UCHCOM_REQ_GET_VERSION, 0, 0, buf, sizeof(buf));
  485 
  486         if (rver)
  487                 *rver = buf[0];
  488 }
  489 
  490 static void
  491 uchcom_get_status(struct uchcom_softc *sc, uint8_t *rval)
  492 {
  493         uchcom_read_reg(sc, UCHCOM_REG_STAT1, rval, UCHCOM_REG_STAT2, NULL);
  494 }
  495 
  496 static void
  497 uchcom_set_dtr_rts_10(struct uchcom_softc *sc, uint8_t val)
  498 {
  499         uchcom_write_reg(sc, UCHCOM_REG_STAT1, val, UCHCOM_REG_STAT1, val);
  500 }
  501 
  502 static void
  503 uchcom_set_dtr_rts_20(struct uchcom_softc *sc, uint8_t val)
  504 {
  505         uchcom_ctrl_write(sc, UCHCOM_REQ_SET_DTRRTS, val, 0);
  506 }
  507 
  508 /* ----------------------------------------------------------------------
  509  * middle layer
  510  */
  511 
  512 static void
  513 uchcom_update_version(struct uchcom_softc *sc)
  514 {
  515         uchcom_get_version(sc, &sc->sc_version);
  516         DPRINTF("Chip version: 0x%02x\n", sc->sc_version);
  517 }
  518 
  519 static void
  520 uchcom_convert_status(struct uchcom_softc *sc, uint8_t cur)
  521 {
  522         sc->sc_dtr = !(cur & UCHCOM_DTR_MASK);
  523         sc->sc_rts = !(cur & UCHCOM_RTS_MASK);
  524 
  525         cur = ~cur & 0x0F;
  526         sc->sc_msr = (cur << 4) | ((sc->sc_msr >> 4) ^ cur);
  527 }
  528 
  529 static void
  530 uchcom_update_status(struct uchcom_softc *sc)
  531 {
  532         uint8_t cur;
  533 
  534         uchcom_get_status(sc, &cur);
  535         uchcom_convert_status(sc, cur);
  536 }
  537 
  538 static void
  539 uchcom_set_dtr_rts(struct uchcom_softc *sc)
  540 {
  541         uint8_t val = 0;
  542 
  543         if (sc->sc_dtr)
  544                 val |= UCHCOM_DTR_MASK;
  545         if (sc->sc_rts)
  546                 val |= UCHCOM_RTS_MASK;
  547 
  548         if (sc->sc_version < UCHCOM_VER_20)
  549                 uchcom_set_dtr_rts_10(sc, ~val);
  550         else
  551                 uchcom_set_dtr_rts_20(sc, ~val);
  552 }
  553 
  554 static void
  555 uchcom_cfg_set_break(struct ucom_softc *ucom, uint8_t onoff)
  556 {
  557         struct uchcom_softc *sc = ucom->sc_parent;
  558         uint8_t brk1;
  559         uint8_t brk2;
  560 
  561         uchcom_read_reg(sc, UCHCOM_REG_BREAK1, &brk1, UCHCOM_REG_LCR1, &brk2);
  562         if (onoff) {
  563                 /* on - clear bits */
  564                 brk1 &= ~UCHCOM_BRK_MASK;
  565                 brk2 &= ~UCHCOM_LCR1_TX;
  566         } else {
  567                 /* off - set bits */
  568                 brk1 |= UCHCOM_BRK_MASK;
  569                 brk2 |= UCHCOM_LCR1_TX;
  570         }
  571         uchcom_write_reg(sc, UCHCOM_REG_BREAK1, brk1, UCHCOM_REG_LCR1, brk2);
  572 }
  573 
  574 static int
  575 uchcom_calc_divider_settings(struct uchcom_divider *dp, uint32_t rate)
  576 {
  577         const struct uchcom_divider_record *rp;
  578         uint32_t div;
  579         uint32_t rem;
  580         uint32_t mod;
  581         uint8_t i;
  582 
  583         /* find record */
  584         for (i = 0; i != NUM_DIVIDERS; i++) {
  585                 if (dividers[i].dvr_high >= rate &&
  586                     dividers[i].dvr_low <= rate) {
  587                         rp = &dividers[i];
  588                         goto found;
  589                 }
  590         }
  591         return (-1);
  592 
  593 found:
  594         dp->dv_prescaler = rp->dvr_divider.dv_prescaler;
  595         if (rp->dvr_base_clock == UCHCOM_BASE_UNKNOWN)
  596                 dp->dv_div = rp->dvr_divider.dv_div;
  597         else {
  598                 div = rp->dvr_base_clock / rate;
  599                 rem = rp->dvr_base_clock % rate;
  600                 if (div == 0 || div >= 0xFF)
  601                         return (-1);
  602                 if ((rem << 1) >= rate)
  603                         div += 1;
  604                 dp->dv_div = (uint8_t)-div;
  605         }
  606 
  607         mod = (UCHCOM_BPS_MOD_BASE / rate) + UCHCOM_BPS_MOD_BASE_OFS;
  608         mod = mod + (mod / 2);
  609 
  610         dp->dv_mod = (mod + 0xFF) / 0x100;
  611 
  612         return (0);
  613 }
  614 
  615 static void
  616 uchcom_set_baudrate(struct uchcom_softc *sc, uint32_t rate)
  617 {
  618         struct uchcom_divider dv;
  619 
  620         if (uchcom_calc_divider_settings(&dv, rate))
  621                 return;
  622 
  623         /*
  624          * According to linux code we need to set bit 7 of UCHCOM_REG_BPS_PRE,
  625          * otherwise the chip will buffer data.
  626          */
  627         uchcom_write_reg(sc,
  628             UCHCOM_REG_BPS_PRE, dv.dv_prescaler | 0x80,
  629             UCHCOM_REG_BPS_DIV, dv.dv_div);
  630         uchcom_write_reg(sc,
  631             UCHCOM_REG_BPS_MOD, dv.dv_mod,
  632             UCHCOM_REG_BPS_PAD, 0);
  633 }
  634 
  635 /* ----------------------------------------------------------------------
  636  * methods for ucom
  637  */
  638 static void
  639 uchcom_cfg_get_status(struct ucom_softc *ucom, uint8_t *lsr, uint8_t *msr)
  640 {
  641         struct uchcom_softc *sc = ucom->sc_parent;
  642 
  643         DPRINTF("\n");
  644 
  645         /* XXX Note: sc_lsr is always zero */
  646         *lsr = sc->sc_lsr;
  647         *msr = sc->sc_msr;
  648 }
  649 
  650 static void
  651 uchcom_cfg_set_dtr(struct ucom_softc *ucom, uint8_t onoff)
  652 {
  653         struct uchcom_softc *sc = ucom->sc_parent;
  654 
  655         DPRINTF("onoff = %d\n", onoff);
  656 
  657         sc->sc_dtr = onoff;
  658         uchcom_set_dtr_rts(sc);
  659 }
  660 
  661 static void
  662 uchcom_cfg_set_rts(struct ucom_softc *ucom, uint8_t onoff)
  663 {
  664         struct uchcom_softc *sc = ucom->sc_parent;
  665 
  666         DPRINTF("onoff = %d\n", onoff);
  667 
  668         sc->sc_rts = onoff;
  669         uchcom_set_dtr_rts(sc);
  670 }
  671 
  672 static void
  673 uchcom_cfg_open(struct ucom_softc *ucom)
  674 {
  675         struct uchcom_softc *sc = ucom->sc_parent;
  676 
  677         DPRINTF("\n");
  678 
  679         uchcom_update_version(sc);
  680         uchcom_update_status(sc);
  681 }
  682 
  683 static int
  684 uchcom_pre_param(struct ucom_softc *ucom, struct termios *t)
  685 {
  686         struct uchcom_divider dv;
  687 
  688         switch (t->c_cflag & CSIZE) {
  689         case CS8:
  690                 break;
  691         default:
  692                 return (EIO);
  693         }
  694         if ((t->c_cflag & CSTOPB) != 0)
  695                 return (EIO);
  696         if ((t->c_cflag & PARENB) != 0)
  697                 return (EIO);
  698 
  699         if (uchcom_calc_divider_settings(&dv, t->c_ospeed)) {
  700                 return (EIO);
  701         }
  702         return (0);                     /* success */
  703 }
  704 
  705 static void
  706 uchcom_cfg_param(struct ucom_softc *ucom, struct termios *t)
  707 {
  708         struct uchcom_softc *sc = ucom->sc_parent;
  709 
  710         uchcom_get_version(sc, NULL);
  711         uchcom_ctrl_write(sc, UCHCOM_REQ_RESET, 0, 0);
  712         uchcom_set_baudrate(sc, t->c_ospeed);
  713         if (sc->sc_version < UCHCOM_VER_30) {
  714                 uchcom_read_reg(sc, UCHCOM_REG_LCR1, NULL,
  715                     UCHCOM_REG_LCR2, NULL);
  716                 uchcom_write_reg(sc, UCHCOM_REG_LCR1, 0x50,
  717                     UCHCOM_REG_LCR2, 0x00);
  718         } else {
  719                 /*
  720                  * Set up line control:
  721                  * - enable transmit and receive
  722                  * - set 8n1 mode
  723                  * To do: support other sizes, parity, stop bits.
  724                  */
  725                 uchcom_write_reg(sc,
  726                     UCHCOM_REG_LCR1,
  727                     UCHCOM_LCR1_RX | UCHCOM_LCR1_TX | UCHCOM_LCR1_CS8,
  728                     UCHCOM_REG_LCR2, 0x00);
  729         }
  730         uchcom_update_status(sc);
  731         uchcom_ctrl_write(sc, UCHCOM_REQ_RESET, 0x501f, 0xd90a);
  732         uchcom_set_baudrate(sc, t->c_ospeed);
  733         uchcom_set_dtr_rts(sc);
  734         uchcom_update_status(sc);
  735 }
  736 
  737 static void
  738 uchcom_start_read(struct ucom_softc *ucom)
  739 {
  740         struct uchcom_softc *sc = ucom->sc_parent;
  741 
  742         /* start interrupt endpoint */
  743         usbd_transfer_start(sc->sc_xfer[UCHCOM_INTR_DT_RD]);
  744 
  745         /* start read endpoint */
  746         usbd_transfer_start(sc->sc_xfer[UCHCOM_BULK_DT_RD]);
  747 }
  748 
  749 static void
  750 uchcom_stop_read(struct ucom_softc *ucom)
  751 {
  752         struct uchcom_softc *sc = ucom->sc_parent;
  753 
  754         /* stop interrupt endpoint */
  755         usbd_transfer_stop(sc->sc_xfer[UCHCOM_INTR_DT_RD]);
  756 
  757         /* stop read endpoint */
  758         usbd_transfer_stop(sc->sc_xfer[UCHCOM_BULK_DT_RD]);
  759 }
  760 
  761 static void
  762 uchcom_start_write(struct ucom_softc *ucom)
  763 {
  764         struct uchcom_softc *sc = ucom->sc_parent;
  765 
  766         usbd_transfer_start(sc->sc_xfer[UCHCOM_BULK_DT_WR]);
  767 }
  768 
  769 static void
  770 uchcom_stop_write(struct ucom_softc *ucom)
  771 {
  772         struct uchcom_softc *sc = ucom->sc_parent;
  773 
  774         usbd_transfer_stop(sc->sc_xfer[UCHCOM_BULK_DT_WR]);
  775 }
  776 
  777 /* ----------------------------------------------------------------------
  778  * callback when the modem status is changed.
  779  */
  780 static void
  781 uchcom_intr_callback(struct usb_xfer *xfer, usb_error_t error)
  782 {
  783         struct uchcom_softc *sc = usbd_xfer_softc(xfer);
  784         struct usb_page_cache *pc;
  785         uint8_t buf[UCHCOM_INTR_LEAST];
  786         int actlen;
  787 
  788         usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
  789 
  790         switch (USB_GET_STATE(xfer)) {
  791         case USB_ST_TRANSFERRED:
  792 
  793                 DPRINTF("actlen = %u\n", actlen);
  794 
  795                 if (actlen >= UCHCOM_INTR_LEAST) {
  796                         pc = usbd_xfer_get_frame(xfer, 0);
  797                         usbd_copy_out(pc, 0, buf, UCHCOM_INTR_LEAST);
  798 
  799                         DPRINTF("data = 0x%02X 0x%02X 0x%02X 0x%02X\n",
  800                             (unsigned)buf[0], (unsigned)buf[1],
  801                             (unsigned)buf[2], (unsigned)buf[3]);
  802 
  803                         uchcom_convert_status(sc, buf[UCHCOM_INTR_STAT1]);
  804                         ucom_status_change(&sc->sc_ucom);
  805                 }
  806         case USB_ST_SETUP:
  807 tr_setup:
  808                 usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
  809                 usbd_transfer_submit(xfer);
  810                 break;
  811 
  812         default:                        /* Error */
  813                 if (error != USB_ERR_CANCELLED) {
  814                         /* try to clear stall first */
  815                         usbd_xfer_set_stall(xfer);
  816                         goto tr_setup;
  817                 }
  818                 break;
  819         }
  820 }
  821 
  822 static void
  823 uchcom_write_callback(struct usb_xfer *xfer, usb_error_t error)
  824 {
  825         struct uchcom_softc *sc = usbd_xfer_softc(xfer);
  826         struct usb_page_cache *pc;
  827         uint32_t actlen;
  828 
  829         switch (USB_GET_STATE(xfer)) {
  830         case USB_ST_SETUP:
  831         case USB_ST_TRANSFERRED:
  832 tr_setup:
  833                 pc = usbd_xfer_get_frame(xfer, 0);
  834                 if (ucom_get_data(&sc->sc_ucom, pc, 0,
  835                     usbd_xfer_max_len(xfer), &actlen)) {
  836                         DPRINTF("actlen = %d\n", actlen);
  837 
  838                         usbd_xfer_set_frame_len(xfer, 0, actlen);
  839                         usbd_transfer_submit(xfer);
  840                 }
  841                 break;
  842 
  843         default:                        /* Error */
  844                 if (error != USB_ERR_CANCELLED) {
  845                         /* try to clear stall first */
  846                         usbd_xfer_set_stall(xfer);
  847                         goto tr_setup;
  848                 }
  849                 break;
  850         }
  851 }
  852 
  853 static void
  854 uchcom_read_callback(struct usb_xfer *xfer, usb_error_t error)
  855 {
  856         struct uchcom_softc *sc = usbd_xfer_softc(xfer);
  857         struct usb_page_cache *pc;
  858         int actlen;
  859 
  860         usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
  861 
  862         switch (USB_GET_STATE(xfer)) {
  863         case USB_ST_TRANSFERRED:
  864 
  865                 if (actlen > 0) {
  866                         pc = usbd_xfer_get_frame(xfer, 0);
  867                         ucom_put_data(&sc->sc_ucom, pc, 0, actlen);
  868                 }
  869 
  870         case USB_ST_SETUP:
  871 tr_setup:
  872                 usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
  873                 usbd_transfer_submit(xfer);
  874                 break;
  875 
  876         default:                        /* Error */
  877                 if (error != USB_ERR_CANCELLED) {
  878                         /* try to clear stall first */
  879                         usbd_xfer_set_stall(xfer);
  880                         goto tr_setup;
  881                 }
  882                 break;
  883         }
  884 }
  885 
  886 static void
  887 uchcom_poll(struct ucom_softc *ucom)
  888 {
  889         struct uchcom_softc *sc = ucom->sc_parent;
  890         usbd_transfer_poll(sc->sc_xfer, UCHCOM_N_TRANSFER);
  891 }
  892 
  893 static device_method_t uchcom_methods[] = {
  894         /* Device interface */
  895         DEVMETHOD(device_probe, uchcom_probe),
  896         DEVMETHOD(device_attach, uchcom_attach),
  897         DEVMETHOD(device_detach, uchcom_detach),
  898         DEVMETHOD_END
  899 };
  900 
  901 static driver_t uchcom_driver = {
  902         .name = "uchcom",
  903         .methods = uchcom_methods,
  904         .size = sizeof(struct uchcom_softc)
  905 };
  906 
  907 DRIVER_MODULE(uchcom, uhub, uchcom_driver, NULL, NULL);
  908 MODULE_DEPEND(uchcom, ucom, 1, 1, 1);
  909 MODULE_DEPEND(uchcom, usb, 1, 1, 1);
  910 MODULE_VERSION(uchcom, 1);
  911 USB_PNP_HOST_INFO(uchcom_devs);

Cache object: 91bfb5f67fb72582e85054e76133802e


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