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/uslcom.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 /*      $OpenBSD: uslcom.c,v 1.17 2007/11/24 10:52:12 jsg Exp $ */
    2 
    3 #include <sys/cdefs.h>
    4 __FBSDID("$FreeBSD$");
    5 
    6 /*
    7  * Copyright (c) 2006 Jonathan Gray <jsg@openbsd.org>
    8  *
    9  * Permission to use, copy, modify, and distribute this software for any
   10  * purpose with or without fee is hereby granted, provided that the above
   11  * copyright notice and this permission notice appear in all copies.
   12  *
   13  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
   14  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
   15  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
   16  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
   17  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
   18  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
   19  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
   20  */
   21 
   22 /*
   23  * Driver for Silicon Laboratories CP2101/CP2102/CP2103/CP2104/CP2105
   24  * USB-Serial adapters.  Based on datasheet AN571, publicly available from
   25  * http://www.silabs.com/Support%20Documents/TechnicalDocs/AN571.pdf
   26  */
   27 
   28 #include <sys/stdint.h>
   29 #include <sys/stddef.h>
   30 #include <sys/param.h>
   31 #include <sys/queue.h>
   32 #include <sys/types.h>
   33 #include <sys/systm.h>
   34 #include <sys/kernel.h>
   35 #include <sys/bus.h>
   36 #include <sys/module.h>
   37 #include <sys/lock.h>
   38 #include <sys/mutex.h>
   39 #include <sys/condvar.h>
   40 #include <sys/sysctl.h>
   41 #include <sys/sx.h>
   42 #include <sys/unistd.h>
   43 #include <sys/callout.h>
   44 #include <sys/malloc.h>
   45 #include <sys/priv.h>
   46 
   47 #include <dev/usb/usb.h>
   48 #include <dev/usb/usbdi.h>
   49 #include <dev/usb/usbdi_util.h>
   50 #include <dev/usb/usb_ioctl.h>
   51 #include "usbdevs.h"
   52 
   53 #define USB_DEBUG_VAR uslcom_debug
   54 #include <dev/usb/usb_debug.h>
   55 #include <dev/usb/usb_process.h>
   56 
   57 #include <dev/usb/serial/usb_serial.h>
   58 
   59 #ifdef USB_DEBUG
   60 static int uslcom_debug = 0;
   61 
   62 static SYSCTL_NODE(_hw_usb, OID_AUTO, uslcom, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
   63     "USB uslcom");
   64 SYSCTL_INT(_hw_usb_uslcom, OID_AUTO, debug, CTLFLAG_RWTUN,
   65     &uslcom_debug, 0, "Debug level");
   66 #endif
   67 
   68 #define USLCOM_BULK_BUF_SIZE    1024
   69 #define USLCOM_CONFIG_INDEX     0
   70 
   71 /* Request types */
   72 #define USLCOM_WRITE            0x41
   73 #define USLCOM_READ             0xc1
   74 
   75 /* Request codes */
   76 #define USLCOM_IFC_ENABLE       0x00
   77 #define USLCOM_SET_BAUDDIV      0x01
   78 #define USLCOM_SET_LINE_CTL     0x03
   79 #define USLCOM_SET_BREAK        0x05
   80 #define USLCOM_SET_MHS          0x07
   81 #define USLCOM_GET_MDMSTS       0x08
   82 #define USLCOM_SET_FLOW         0x13
   83 #define USLCOM_SET_BAUDRATE     0x1e
   84 #define USLCOM_VENDOR_SPECIFIC  0xff
   85 
   86 /* USLCOM_IFC_ENABLE values */
   87 #define USLCOM_IFC_ENABLE_DIS   0x00
   88 #define USLCOM_IFC_ENABLE_EN    0x01
   89 
   90 /* USLCOM_SET_MHS/USLCOM_GET_MDMSTS values */
   91 #define USLCOM_MHS_DTR_ON       0x0001
   92 #define USLCOM_MHS_DTR_SET      0x0100
   93 #define USLCOM_MHS_RTS_ON       0x0002
   94 #define USLCOM_MHS_RTS_SET      0x0200
   95 #define USLCOM_MHS_CTS          0x0010
   96 #define USLCOM_MHS_DSR          0x0020
   97 #define USLCOM_MHS_RI           0x0040
   98 #define USLCOM_MHS_DCD          0x0080
   99 
  100 /* USLCOM_SET_BAUDDIV values */
  101 #define USLCOM_BAUDDIV_REF      3686400 /* 3.6864 MHz */
  102 
  103 /* USLCOM_SET_LINE_CTL values */
  104 #define USLCOM_STOP_BITS_1      0x00
  105 #define USLCOM_STOP_BITS_2      0x02
  106 #define USLCOM_PARITY_NONE      0x00
  107 #define USLCOM_PARITY_ODD       0x10
  108 #define USLCOM_PARITY_EVEN      0x20
  109 #define USLCOM_SET_DATA_BITS(x) ((x) << 8)
  110 
  111 /* USLCOM_SET_BREAK values */
  112 #define USLCOM_SET_BREAK_OFF    0x00
  113 #define USLCOM_SET_BREAK_ON     0x01
  114 
  115 /* USLCOM_SET_FLOW values - 1st word */
  116 #define USLCOM_FLOW_DTR_ON      0x00000001 /* DTR static active */
  117 #define USLCOM_FLOW_CTS_HS      0x00000008 /* CTS handshake */
  118 /* USLCOM_SET_FLOW values - 2nd word */
  119 #define USLCOM_FLOW_RTS_ON      0x00000040 /* RTS static active */
  120 #define USLCOM_FLOW_RTS_HS      0x00000080 /* RTS handshake */
  121 
  122 /* USLCOM_VENDOR_SPECIFIC values */
  123 #define USLCOM_GET_PARTNUM      0x370B
  124 #define USLCOM_WRITE_LATCH      0x37E1
  125 #define USLCOM_READ_LATCH       0x00C2
  126 
  127 /* USLCOM_GET_PARTNUM values from hardware */
  128 #define USLCOM_PARTNUM_CP2101   1
  129 #define USLCOM_PARTNUM_CP2102   2
  130 #define USLCOM_PARTNUM_CP2103   3
  131 #define USLCOM_PARTNUM_CP2104   4
  132 #define USLCOM_PARTNUM_CP2105   5
  133 
  134 enum {
  135         USLCOM_BULK_DT_WR,
  136         USLCOM_BULK_DT_RD,
  137         USLCOM_CTRL_DT_RD,
  138         USLCOM_N_TRANSFER,
  139 };
  140 
  141 struct uslcom_softc {
  142         struct ucom_super_softc sc_super_ucom;
  143         struct ucom_softc sc_ucom;
  144         struct usb_callout sc_watchdog;
  145 
  146         struct usb_xfer *sc_xfer[USLCOM_N_TRANSFER];
  147         struct usb_device *sc_udev;
  148         struct mtx sc_mtx;
  149 
  150         uint8_t sc_msr;
  151         uint8_t sc_lsr;
  152         uint8_t sc_iface_no;
  153         uint8_t sc_partnum;
  154 };
  155 
  156 static device_probe_t uslcom_probe;
  157 static device_attach_t uslcom_attach;
  158 static device_detach_t uslcom_detach;
  159 static void uslcom_free_softc(struct uslcom_softc *);
  160 
  161 static usb_callback_t uslcom_write_callback;
  162 static usb_callback_t uslcom_read_callback;
  163 static usb_callback_t uslcom_control_callback;
  164 
  165 static void uslcom_free(struct ucom_softc *);
  166 static uint8_t uslcom_get_partnum(struct uslcom_softc *);
  167 static void uslcom_cfg_open(struct ucom_softc *);
  168 static void uslcom_cfg_close(struct ucom_softc *);
  169 static void uslcom_cfg_set_dtr(struct ucom_softc *, uint8_t);
  170 static void uslcom_cfg_set_rts(struct ucom_softc *, uint8_t);
  171 static void uslcom_cfg_set_break(struct ucom_softc *, uint8_t);
  172 static void uslcom_cfg_param(struct ucom_softc *, struct termios *);
  173 static void uslcom_cfg_get_status(struct ucom_softc *, uint8_t *, uint8_t *);
  174 static int uslcom_ioctl(struct ucom_softc *, uint32_t, caddr_t, int,
  175     struct thread *);
  176 static int uslcom_pre_param(struct ucom_softc *, struct termios *);
  177 static void uslcom_start_read(struct ucom_softc *);
  178 static void uslcom_stop_read(struct ucom_softc *);
  179 static void uslcom_start_write(struct ucom_softc *);
  180 static void uslcom_stop_write(struct ucom_softc *);
  181 static void uslcom_poll(struct ucom_softc *ucom);
  182 
  183 static const struct usb_config uslcom_config[USLCOM_N_TRANSFER] = {
  184         [USLCOM_BULK_DT_WR] = {
  185                 .type = UE_BULK,
  186                 .endpoint = UE_ADDR_ANY,
  187                 .direction = UE_DIR_OUT,
  188                 .bufsize = USLCOM_BULK_BUF_SIZE,
  189                .flags = {.pipe_bof = 1,},
  190                 .callback = &uslcom_write_callback,
  191         },
  192 
  193         [USLCOM_BULK_DT_RD] = {
  194                 .type = UE_BULK,
  195                 .endpoint = UE_ADDR_ANY,
  196                 .direction = UE_DIR_IN,
  197                 .bufsize = USLCOM_BULK_BUF_SIZE,
  198                 .flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
  199                 .callback = &uslcom_read_callback,
  200         },
  201         [USLCOM_CTRL_DT_RD] = {
  202                 .type = UE_CONTROL,
  203                 .endpoint = 0x00,
  204                 .direction = UE_DIR_ANY,
  205                 .bufsize = sizeof(struct usb_device_request) + 8,
  206                 .flags = {.pipe_bof = 1,},
  207                 .callback = &uslcom_control_callback,
  208                 .timeout = 1000,        /* 1 second timeout */
  209         },
  210 };
  211 
  212 static struct ucom_callback uslcom_callback = {
  213         .ucom_cfg_get_status = &uslcom_cfg_get_status,
  214         .ucom_cfg_set_dtr = &uslcom_cfg_set_dtr,
  215         .ucom_cfg_set_rts = &uslcom_cfg_set_rts,
  216         .ucom_cfg_set_break = &uslcom_cfg_set_break,
  217         .ucom_cfg_open = &uslcom_cfg_open,
  218         .ucom_cfg_close = &uslcom_cfg_close,
  219         .ucom_cfg_param = &uslcom_cfg_param,
  220         .ucom_pre_param = &uslcom_pre_param,
  221         .ucom_ioctl = &uslcom_ioctl,
  222         .ucom_start_read = &uslcom_start_read,
  223         .ucom_stop_read = &uslcom_stop_read,
  224         .ucom_start_write = &uslcom_start_write,
  225         .ucom_stop_write = &uslcom_stop_write,
  226         .ucom_poll = &uslcom_poll,
  227         .ucom_free = &uslcom_free,
  228 };
  229 
  230 static const STRUCT_USB_HOST_ID uslcom_devs[] = {
  231 #define USLCOM_DEV(v,p)  { USB_VP(USB_VENDOR_##v, USB_PRODUCT_##v##_##p) }
  232     USLCOM_DEV(BALTECH, CARDREADER),
  233     USLCOM_DEV(CLIPSAL, 5000CT2),
  234     USLCOM_DEV(CLIPSAL, 5500PACA),
  235     USLCOM_DEV(CLIPSAL, 5500PCU),
  236     USLCOM_DEV(CLIPSAL, 560884),
  237     USLCOM_DEV(CLIPSAL, 5800PC),
  238     USLCOM_DEV(CLIPSAL, C5000CT2),
  239     USLCOM_DEV(CLIPSAL, L51xx),
  240     USLCOM_DEV(DATAAPEX, MULTICOM),
  241     USLCOM_DEV(DELL, DW700),
  242     USLCOM_DEV(DIGIANSWER, ZIGBEE802154),
  243     USLCOM_DEV(DYNASTREAM, ANTDEVBOARD),
  244     USLCOM_DEV(DYNASTREAM, ANTDEVBOARD2),
  245     USLCOM_DEV(DYNASTREAM, ANT2USB),
  246     USLCOM_DEV(ELV, USBI2C),
  247     USLCOM_DEV(FESTO, CMSP),
  248     USLCOM_DEV(FESTO, CPX_USB),
  249     USLCOM_DEV(FOXCONN, PIRELLI_DP_L10),
  250     USLCOM_DEV(FOXCONN, TCOM_TC_300),
  251     USLCOM_DEV(GEMALTO, PROXPU),
  252     USLCOM_DEV(JABLOTRON, PC60B),
  253     USLCOM_DEV(KAMSTRUP, OPTICALEYE),
  254     USLCOM_DEV(KAMSTRUP, MBUS_250D),
  255     USLCOM_DEV(LAKESHORE, 121),
  256     USLCOM_DEV(LAKESHORE, 218A),
  257     USLCOM_DEV(LAKESHORE, 219),
  258     USLCOM_DEV(LAKESHORE, 233),
  259     USLCOM_DEV(LAKESHORE, 235),
  260     USLCOM_DEV(LAKESHORE, 335),
  261     USLCOM_DEV(LAKESHORE, 336),
  262     USLCOM_DEV(LAKESHORE, 350),
  263     USLCOM_DEV(LAKESHORE, 371),
  264     USLCOM_DEV(LAKESHORE, 411),
  265     USLCOM_DEV(LAKESHORE, 425),
  266     USLCOM_DEV(LAKESHORE, 455A),
  267     USLCOM_DEV(LAKESHORE, 465),
  268     USLCOM_DEV(LAKESHORE, 475A),
  269     USLCOM_DEV(LAKESHORE, 625A),
  270     USLCOM_DEV(LAKESHORE, 642A),
  271     USLCOM_DEV(LAKESHORE, 648),
  272     USLCOM_DEV(LAKESHORE, 737),
  273     USLCOM_DEV(LAKESHORE, 776),
  274     USLCOM_DEV(LINKINSTRUMENTS, MSO19),
  275     USLCOM_DEV(LINKINSTRUMENTS, MSO28),
  276     USLCOM_DEV(LINKINSTRUMENTS, MSO28_2),
  277     USLCOM_DEV(MEI, CASHFLOW_SC),
  278     USLCOM_DEV(MEI, S2000),
  279     USLCOM_DEV(NETGEAR, M4100),
  280     USLCOM_DEV(OWEN, AC4),
  281     USLCOM_DEV(OWL, CM_160),
  282     USLCOM_DEV(PHILIPS, ACE1001),
  283     USLCOM_DEV(PLX, CA42),
  284     USLCOM_DEV(RENESAS, RX610),
  285     USLCOM_DEV(SEL, C662),
  286     USLCOM_DEV(SILABS, AC_SERV_CAN),
  287     USLCOM_DEV(SILABS, AC_SERV_CIS),
  288     USLCOM_DEV(SILABS, AC_SERV_IBUS),
  289     USLCOM_DEV(SILABS, AC_SERV_OBD),
  290     USLCOM_DEV(SILABS, AEROCOMM),
  291     USLCOM_DEV(SILABS, AMBER_AMB2560),
  292     USLCOM_DEV(SILABS, ARGUSISP),
  293     USLCOM_DEV(SILABS, ARKHAM_DS101_A),
  294     USLCOM_DEV(SILABS, ARKHAM_DS101_M),
  295     USLCOM_DEV(SILABS, ARYGON_MIFARE),
  296     USLCOM_DEV(SILABS, AVIT_USB_TTL),
  297     USLCOM_DEV(SILABS, B_G_H3000),
  298     USLCOM_DEV(SILABS, BALLUFF_RFID),
  299     USLCOM_DEV(SILABS, BEI_VCP),
  300     USLCOM_DEV(SILABS, BSM7DUSB),
  301     USLCOM_DEV(SILABS, BURNSIDE),
  302     USLCOM_DEV(SILABS, C2_EDGE_MODEM),
  303     USLCOM_DEV(SILABS, CP2102),
  304     USLCOM_DEV(SILABS, CP210X_2),
  305     USLCOM_DEV(SILABS, CP210X_3),
  306     USLCOM_DEV(SILABS, CP210X_4),
  307     USLCOM_DEV(SILABS, CRUMB128),
  308     USLCOM_DEV(SILABS, CYGNAL),
  309     USLCOM_DEV(SILABS, CYGNAL_DEBUG),
  310     USLCOM_DEV(SILABS, CYGNAL_GPS),
  311     USLCOM_DEV(SILABS, DEGREE),
  312     USLCOM_DEV(SILABS, DEKTEK_DTAPLUS),
  313     USLCOM_DEV(SILABS, EMS_C1007),
  314     USLCOM_DEV(SILABS, HAMLINKUSB),
  315     USLCOM_DEV(SILABS, HELICOM),
  316     USLCOM_DEV(SILABS, HUBZ),
  317     USLCOM_DEV(SILABS, BV_AV2010_10),
  318     USLCOM_DEV(SILABS, IMS_USB_RS422),
  319     USLCOM_DEV(SILABS, INFINITY_MIC),
  320     USLCOM_DEV(SILABS, INGENI_ZIGBEE),
  321     USLCOM_DEV(SILABS, INSYS_MODEM),
  322     USLCOM_DEV(SILABS, IRZ_SG10),
  323     USLCOM_DEV(SILABS, KYOCERA_GPS),
  324     USLCOM_DEV(SILABS, LIPOWSKY_HARP),
  325     USLCOM_DEV(SILABS, LIPOWSKY_JTAG),
  326     USLCOM_DEV(SILABS, LIPOWSKY_LIN),
  327     USLCOM_DEV(SILABS, MC35PU),
  328     USLCOM_DEV(SILABS, MMB_ZIGBEE),
  329     USLCOM_DEV(SILABS, MJS_TOSLINK),
  330     USLCOM_DEV(SILABS, MSD_DASHHAWK),
  331     USLCOM_DEV(SILABS, MULTIPLEX_RC),
  332     USLCOM_DEV(SILABS, OPTRIS_MSPRO),
  333     USLCOM_DEV(SILABS, POLOLU),
  334     USLCOM_DEV(SILABS, PROCYON_AVS),
  335     USLCOM_DEV(SILABS, SB_PARAMOUNT_ME),
  336     USLCOM_DEV(SILABS, SUUNTO),
  337     USLCOM_DEV(SILABS, TAMSMASTER),
  338     USLCOM_DEV(SILABS, TELEGESIS_ETRX2),
  339     USLCOM_DEV(SILABS, TRACIENT),
  340     USLCOM_DEV(SILABS, TRAQMATE),
  341     USLCOM_DEV(SILABS, USBCOUNT50),
  342     USLCOM_DEV(SILABS, USBPULSE100),
  343     USLCOM_DEV(SILABS, USBSCOPE50),
  344     USLCOM_DEV(SILABS, USBWAVE12),
  345     USLCOM_DEV(SILABS, V_PREON32),
  346     USLCOM_DEV(SILABS, VSTABI),
  347     USLCOM_DEV(SILABS, WAVIT),
  348     USLCOM_DEV(SILABS, WMRBATT),
  349     USLCOM_DEV(SILABS, WMRRIGBLASTER),
  350     USLCOM_DEV(SILABS, WMRRIGTALK),
  351     USLCOM_DEV(SILABS, ZEPHYR_BIO),
  352     USLCOM_DEV(SILABS2, DCU11CLONE),
  353     USLCOM_DEV(SILABS3, GPRS_MODEM),
  354     USLCOM_DEV(SILABS4, 100EU_MODEM),
  355     USLCOM_DEV(SYNTECH, CYPHERLAB100),
  356     USLCOM_DEV(USI, MC60),
  357     USLCOM_DEV(VAISALA, CABLE),
  358     USLCOM_DEV(WAGO, SERVICECABLE),
  359     USLCOM_DEV(WAVESENSE, JAZZ),
  360     USLCOM_DEV(WESTMOUNTAIN, RIGBLASTER_ADVANTAGE),
  361     USLCOM_DEV(WIENERPLEINBAUS, PL512),
  362     USLCOM_DEV(WIENERPLEINBAUS, RCM),
  363     USLCOM_DEV(WIENERPLEINBAUS, MPOD),
  364     USLCOM_DEV(WIENERPLEINBAUS, CML),
  365 #undef USLCOM_DEV
  366 };
  367 
  368 static device_method_t uslcom_methods[] = {
  369         DEVMETHOD(device_probe, uslcom_probe),
  370         DEVMETHOD(device_attach, uslcom_attach),
  371         DEVMETHOD(device_detach, uslcom_detach),
  372         DEVMETHOD_END
  373 };
  374 
  375 static driver_t uslcom_driver = {
  376         .name = "uslcom",
  377         .methods = uslcom_methods,
  378         .size = sizeof(struct uslcom_softc),
  379 };
  380 
  381 DRIVER_MODULE(uslcom, uhub, uslcom_driver, NULL, NULL);
  382 MODULE_DEPEND(uslcom, ucom, 1, 1, 1);
  383 MODULE_DEPEND(uslcom, usb, 1, 1, 1);
  384 MODULE_VERSION(uslcom, 1);
  385 USB_PNP_HOST_INFO(uslcom_devs);
  386 
  387 static void
  388 uslcom_watchdog(void *arg)
  389 {
  390         struct uslcom_softc *sc = arg;
  391 
  392         mtx_assert(&sc->sc_mtx, MA_OWNED);
  393 
  394         usbd_transfer_start(sc->sc_xfer[USLCOM_CTRL_DT_RD]);
  395 
  396         usb_callout_reset(&sc->sc_watchdog,
  397             hz / 4, &uslcom_watchdog, sc);
  398 }
  399 
  400 static int
  401 uslcom_probe(device_t dev)
  402 {
  403         struct usb_attach_arg *uaa = device_get_ivars(dev);
  404 
  405         DPRINTFN(11, "\n");
  406 
  407         if (uaa->usb_mode != USB_MODE_HOST) {
  408                 return (ENXIO);
  409         }
  410         if (uaa->info.bConfigIndex != USLCOM_CONFIG_INDEX) {
  411                 return (ENXIO);
  412         }
  413         return (usbd_lookup_id_by_uaa(uslcom_devs, sizeof(uslcom_devs), uaa));
  414 }
  415 
  416 static int
  417 uslcom_attach(device_t dev)
  418 {
  419         struct usb_attach_arg *uaa = device_get_ivars(dev);
  420         struct uslcom_softc *sc = device_get_softc(dev);
  421         int error;
  422 
  423         DPRINTFN(11, "\n");
  424 
  425         device_set_usb_desc(dev);
  426         mtx_init(&sc->sc_mtx, "uslcom", NULL, MTX_DEF);
  427         ucom_ref(&sc->sc_super_ucom);
  428         usb_callout_init_mtx(&sc->sc_watchdog, &sc->sc_mtx, 0);
  429 
  430         sc->sc_udev = uaa->device;
  431         /* use the interface number from the USB interface descriptor */
  432         sc->sc_iface_no = uaa->info.bIfaceNum;
  433 
  434         error = usbd_transfer_setup(uaa->device,
  435             &uaa->info.bIfaceIndex, sc->sc_xfer, uslcom_config,
  436             USLCOM_N_TRANSFER, sc, &sc->sc_mtx);
  437         if (error) {
  438                 DPRINTF("one or more missing USB endpoints, "
  439                     "error=%s\n", usbd_errstr(error));
  440                 goto detach;
  441         }
  442         sc->sc_partnum = uslcom_get_partnum(sc);
  443 
  444         error = ucom_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc,
  445             &uslcom_callback, &sc->sc_mtx);
  446         if (error) {
  447                 goto detach;
  448         }
  449         ucom_set_pnpinfo_usb(&sc->sc_super_ucom, dev);
  450 
  451         return (0);
  452 
  453 detach:
  454         uslcom_detach(dev);
  455         return (ENXIO);
  456 }
  457 
  458 static int
  459 uslcom_detach(device_t dev)
  460 {
  461         struct uslcom_softc *sc = device_get_softc(dev);
  462 
  463         DPRINTF("sc=%p\n", sc);
  464 
  465         ucom_detach(&sc->sc_super_ucom, &sc->sc_ucom);
  466         usbd_transfer_unsetup(sc->sc_xfer, USLCOM_N_TRANSFER);
  467 
  468         usb_callout_drain(&sc->sc_watchdog);
  469 
  470         device_claim_softc(dev);
  471 
  472         uslcom_free_softc(sc);
  473 
  474         return (0);
  475 }
  476 
  477 UCOM_UNLOAD_DRAIN(uslcom);
  478 
  479 static void
  480 uslcom_free_softc(struct uslcom_softc *sc)
  481 {
  482         if (ucom_unref(&sc->sc_super_ucom)) {
  483                 mtx_destroy(&sc->sc_mtx);
  484                 device_free_softc(sc);
  485         }
  486 }
  487 
  488 static void
  489 uslcom_free(struct ucom_softc *ucom)
  490 {
  491         uslcom_free_softc(ucom->sc_parent);
  492 }
  493 
  494 static void
  495 uslcom_cfg_open(struct ucom_softc *ucom)
  496 {
  497         struct uslcom_softc *sc = ucom->sc_parent;
  498         struct usb_device_request req;
  499 
  500         req.bmRequestType = USLCOM_WRITE;
  501         req.bRequest = USLCOM_IFC_ENABLE;
  502         USETW(req.wValue, USLCOM_IFC_ENABLE_EN);
  503         USETW(req.wIndex, sc->sc_iface_no);
  504         USETW(req.wLength, 0);
  505 
  506         if (ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
  507             &req, NULL, 0, 1000)) {
  508                 DPRINTF("UART enable failed (ignored)\n");
  509         }
  510 
  511         /* clear stall */
  512         usbd_xfer_set_stall(sc->sc_xfer[USLCOM_BULK_DT_WR]);
  513         usbd_xfer_set_stall(sc->sc_xfer[USLCOM_BULK_DT_RD]);
  514 
  515         /* start polling status */
  516         uslcom_watchdog(sc);
  517 }
  518 
  519 static void
  520 uslcom_cfg_close(struct ucom_softc *ucom)
  521 {
  522         struct uslcom_softc *sc = ucom->sc_parent;
  523         struct usb_device_request req;
  524 
  525         /* stop polling status */
  526         usb_callout_stop(&sc->sc_watchdog);
  527 
  528         req.bmRequestType = USLCOM_WRITE;
  529         req.bRequest = USLCOM_IFC_ENABLE;
  530         USETW(req.wValue, USLCOM_IFC_ENABLE_DIS);
  531         USETW(req.wIndex, sc->sc_iface_no);
  532         USETW(req.wLength, 0);
  533 
  534         if (ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
  535             &req, NULL, 0, 1000)) {
  536                 DPRINTF("UART disable failed (ignored)\n");
  537         }
  538 }
  539 
  540 static uint8_t
  541 uslcom_get_partnum(struct uslcom_softc *sc)
  542 {
  543         struct usb_device_request req;
  544         uint8_t partnum;
  545 
  546         /* Find specific chip type */
  547         partnum = 0;
  548         req.bmRequestType = USLCOM_READ;
  549         req.bRequest = USLCOM_VENDOR_SPECIFIC;
  550         USETW(req.wValue, USLCOM_GET_PARTNUM);
  551         USETW(req.wIndex, sc->sc_iface_no);
  552         USETW(req.wLength, sizeof(partnum));
  553 
  554         if (usbd_do_request_flags(sc->sc_udev, NULL,
  555             &req, &partnum, 0, NULL, 1000)) {
  556                 DPRINTF("GET_PARTNUM failed\n");
  557         }
  558 
  559         return(partnum);
  560 }
  561 
  562 static void
  563 uslcom_cfg_set_dtr(struct ucom_softc *ucom, uint8_t onoff)
  564 {
  565         struct uslcom_softc *sc = ucom->sc_parent;
  566         struct usb_device_request req;
  567         uint16_t ctl;
  568 
  569         DPRINTF("onoff = %d\n", onoff);
  570 
  571         ctl = onoff ? USLCOM_MHS_DTR_ON : 0;
  572         ctl |= USLCOM_MHS_DTR_SET;
  573 
  574         req.bmRequestType = USLCOM_WRITE;
  575         req.bRequest = USLCOM_SET_MHS;
  576         USETW(req.wValue, ctl);
  577         USETW(req.wIndex, sc->sc_iface_no);
  578         USETW(req.wLength, 0);
  579 
  580         if (ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
  581             &req, NULL, 0, 1000)) {
  582                 DPRINTF("Setting DTR failed (ignored)\n");
  583         }
  584 }
  585 
  586 static void
  587 uslcom_cfg_set_rts(struct ucom_softc *ucom, uint8_t onoff)
  588 {
  589         struct uslcom_softc *sc = ucom->sc_parent;
  590         struct usb_device_request req;
  591         uint16_t ctl;
  592 
  593         DPRINTF("onoff = %d\n", onoff);
  594 
  595         ctl = onoff ? USLCOM_MHS_RTS_ON : 0;
  596         ctl |= USLCOM_MHS_RTS_SET;
  597 
  598         req.bmRequestType = USLCOM_WRITE;
  599         req.bRequest = USLCOM_SET_MHS;
  600         USETW(req.wValue, ctl);
  601         USETW(req.wIndex, sc->sc_iface_no);
  602         USETW(req.wLength, 0);
  603 
  604         if (ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
  605             &req, NULL, 0, 1000)) {
  606                 DPRINTF("Setting DTR failed (ignored)\n");
  607         }
  608 }
  609 
  610 static int
  611 uslcom_pre_param(struct ucom_softc *ucom, struct termios *t)
  612 {
  613         struct uslcom_softc *sc = ucom->sc_parent;
  614         uint32_t maxspeed;
  615 
  616         switch (sc->sc_partnum) {
  617         case USLCOM_PARTNUM_CP2104:
  618         case USLCOM_PARTNUM_CP2105:
  619                 maxspeed = 2000000;
  620                 break;
  621         case USLCOM_PARTNUM_CP2101:
  622         case USLCOM_PARTNUM_CP2102:
  623         case USLCOM_PARTNUM_CP2103:
  624         default:
  625                 /*
  626                  * Datasheet for cp2102 says 921600 max.  Testing shows that
  627                  * 1228800 and 1843200 work fine.
  628                  */
  629                 maxspeed = 1843200;
  630                 break;
  631         }
  632         if (t->c_ospeed <= 0 || t->c_ospeed > maxspeed)
  633                 return (EINVAL);
  634         return (0);
  635 }
  636 
  637 static void
  638 uslcom_cfg_param(struct ucom_softc *ucom, struct termios *t)
  639 {
  640         struct uslcom_softc *sc = ucom->sc_parent;
  641         struct usb_device_request req;
  642         uint32_t baudrate, flowctrl[4];
  643         uint16_t data;
  644 
  645         DPRINTF("\n");
  646 
  647         baudrate = t->c_ospeed;
  648         req.bmRequestType = USLCOM_WRITE;
  649         req.bRequest = USLCOM_SET_BAUDRATE;
  650         USETW(req.wValue, 0);
  651         USETW(req.wIndex, sc->sc_iface_no);
  652         USETW(req.wLength, sizeof(baudrate));
  653 
  654         if (ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
  655             &req, &baudrate, 0, 1000)) {
  656                 printf("Set baudrate failed (ignored)\n");
  657         }
  658 
  659         if (t->c_cflag & CSTOPB)
  660                 data = USLCOM_STOP_BITS_2;
  661         else
  662                 data = USLCOM_STOP_BITS_1;
  663         if (t->c_cflag & PARENB) {
  664                 if (t->c_cflag & PARODD)
  665                         data |= USLCOM_PARITY_ODD;
  666                 else
  667                         data |= USLCOM_PARITY_EVEN;
  668         } else
  669                 data |= USLCOM_PARITY_NONE;
  670         switch (t->c_cflag & CSIZE) {
  671         case CS5:
  672                 data |= USLCOM_SET_DATA_BITS(5);
  673                 break;
  674         case CS6:
  675                 data |= USLCOM_SET_DATA_BITS(6);
  676                 break;
  677         case CS7:
  678                 data |= USLCOM_SET_DATA_BITS(7);
  679                 break;
  680         case CS8:
  681                 data |= USLCOM_SET_DATA_BITS(8);
  682                 break;
  683         }
  684 
  685         req.bmRequestType = USLCOM_WRITE;
  686         req.bRequest = USLCOM_SET_LINE_CTL;
  687         USETW(req.wValue, data);
  688         USETW(req.wIndex, sc->sc_iface_no);
  689         USETW(req.wLength, 0);
  690 
  691         if (ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
  692             &req, NULL, 0, 1000)) {
  693                 DPRINTF("Set format failed (ignored)\n");
  694         }
  695 
  696         if (t->c_cflag & CRTSCTS) {
  697                 flowctrl[0] = htole32(USLCOM_FLOW_DTR_ON | USLCOM_FLOW_CTS_HS);
  698                 flowctrl[1] = htole32(USLCOM_FLOW_RTS_HS);
  699         } else {
  700                 flowctrl[0] = htole32(USLCOM_FLOW_DTR_ON);
  701                 flowctrl[1] = htole32(USLCOM_FLOW_RTS_ON);
  702         }
  703         flowctrl[2] = 0;
  704         flowctrl[3] = 0;
  705         req.bmRequestType = USLCOM_WRITE;
  706         req.bRequest = USLCOM_SET_FLOW;
  707         USETW(req.wValue, 0);
  708         USETW(req.wIndex, sc->sc_iface_no);
  709         USETW(req.wLength, sizeof(flowctrl));
  710 
  711         if (ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
  712             &req, flowctrl, 0, 1000)) {
  713                 DPRINTF("Set flowcontrol failed (ignored)\n");
  714         }
  715 }
  716 
  717 static void
  718 uslcom_cfg_get_status(struct ucom_softc *ucom, uint8_t *lsr, uint8_t *msr)
  719 {
  720         struct uslcom_softc *sc = ucom->sc_parent;
  721 
  722         DPRINTF("\n");
  723 
  724         /* XXX Note: sc_lsr is always zero */
  725         *lsr = sc->sc_lsr;
  726         *msr = sc->sc_msr;
  727 }
  728 
  729 static void
  730 uslcom_cfg_set_break(struct ucom_softc *ucom, uint8_t onoff)
  731 {
  732         struct uslcom_softc *sc = ucom->sc_parent;
  733         struct usb_device_request req;
  734         uint16_t brk = onoff ? USLCOM_SET_BREAK_ON : USLCOM_SET_BREAK_OFF;
  735 
  736         req.bmRequestType = USLCOM_WRITE;
  737         req.bRequest = USLCOM_SET_BREAK;
  738         USETW(req.wValue, brk);
  739         USETW(req.wIndex, sc->sc_iface_no);
  740         USETW(req.wLength, 0);
  741 
  742         if (ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
  743             &req, NULL, 0, 1000)) {
  744                 DPRINTF("Set BREAK failed (ignored)\n");
  745         }
  746 }
  747 
  748 static int
  749 uslcom_ioctl(struct ucom_softc *ucom, uint32_t cmd, caddr_t data,
  750     int flag, struct thread *td)
  751 {
  752         struct uslcom_softc *sc = ucom->sc_parent;
  753         struct usb_device_request req;
  754         int error = 0;
  755         uint8_t latch;
  756 
  757         DPRINTF("cmd=0x%08x\n", cmd);
  758 
  759         switch (cmd) {
  760         case USB_GET_GPIO:
  761                 if (sc->sc_partnum < USLCOM_PARTNUM_CP2103) {
  762                         error = ENODEV;
  763                         break;
  764                 }
  765                 req.bmRequestType = USLCOM_READ;
  766                 req.bRequest = USLCOM_VENDOR_SPECIFIC;
  767                 USETW(req.wValue, USLCOM_READ_LATCH);
  768                 USETW(req.wIndex, sc->sc_iface_no);
  769                 USETW(req.wLength, sizeof(latch));
  770 
  771                 if (ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
  772                     &req, &latch, 0, 1000)) {
  773                         DPRINTF("Get LATCH failed\n");
  774                         error = EIO;
  775                 }
  776                 *(int *)data = latch;
  777                 break;
  778 
  779         case USB_SET_GPIO:
  780                 if (sc->sc_partnum < USLCOM_PARTNUM_CP2103)
  781                         error = ENODEV;
  782                 else if ((sc->sc_partnum == USLCOM_PARTNUM_CP2103) ||
  783                     (sc->sc_partnum == USLCOM_PARTNUM_CP2104)) {
  784                         req.bmRequestType = USLCOM_WRITE;
  785                         req.bRequest = USLCOM_VENDOR_SPECIFIC;
  786                         USETW(req.wValue, USLCOM_WRITE_LATCH);
  787                         USETW(req.wIndex, (*(int *)data));
  788                         USETW(req.wLength, 0);
  789 
  790                         if (ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
  791                             &req, NULL, 0, 1000)) {
  792                                 DPRINTF("Set LATCH failed\n");
  793                                 error = EIO;
  794                         }
  795                 } else
  796                         error = ENODEV; /* Not yet */
  797                 break;
  798 
  799         default:
  800                 DPRINTF("Unknown IOCTL\n");
  801                 error = ENOIOCTL;
  802                 break;
  803         }
  804         return (error);
  805 }
  806 
  807 static void
  808 uslcom_write_callback(struct usb_xfer *xfer, usb_error_t error)
  809 {
  810         struct uslcom_softc *sc = usbd_xfer_softc(xfer);
  811         struct usb_page_cache *pc;
  812         uint32_t actlen;
  813 
  814         switch (USB_GET_STATE(xfer)) {
  815         case USB_ST_SETUP:
  816         case USB_ST_TRANSFERRED:
  817 tr_setup:
  818                 pc = usbd_xfer_get_frame(xfer, 0);
  819                 if (ucom_get_data(&sc->sc_ucom, pc, 0,
  820                     USLCOM_BULK_BUF_SIZE, &actlen)) {
  821                         DPRINTF("actlen = %d\n", actlen);
  822 
  823                         usbd_xfer_set_frame_len(xfer, 0, actlen);
  824                         usbd_transfer_submit(xfer);
  825                 }
  826                 return;
  827 
  828         default:                        /* Error */
  829                 if (error != USB_ERR_CANCELLED) {
  830                         /* try to clear stall first */
  831                         usbd_xfer_set_stall(xfer);
  832                         goto tr_setup;
  833                 }
  834                 return;
  835         }
  836 }
  837 
  838 static void
  839 uslcom_read_callback(struct usb_xfer *xfer, usb_error_t error)
  840 {
  841         struct uslcom_softc *sc = usbd_xfer_softc(xfer);
  842         struct usb_page_cache *pc;
  843         int actlen;
  844 
  845         usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
  846 
  847         switch (USB_GET_STATE(xfer)) {
  848         case USB_ST_TRANSFERRED:
  849                 pc = usbd_xfer_get_frame(xfer, 0);
  850                 ucom_put_data(&sc->sc_ucom, pc, 0, actlen);
  851 
  852         case USB_ST_SETUP:
  853 tr_setup:
  854                 usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
  855                 usbd_transfer_submit(xfer);
  856                 return;
  857 
  858         default:                        /* Error */
  859                 if (error != USB_ERR_CANCELLED) {
  860                         /* try to clear stall first */
  861                         usbd_xfer_set_stall(xfer);
  862                         goto tr_setup;
  863                 }
  864                 return;
  865         }
  866 }
  867 
  868 static void
  869 uslcom_control_callback(struct usb_xfer *xfer, usb_error_t error)
  870 {
  871         struct uslcom_softc *sc = usbd_xfer_softc(xfer);
  872         struct usb_page_cache *pc;
  873         struct usb_device_request req;
  874         uint8_t msr = 0;
  875         uint8_t buf;
  876 
  877         switch (USB_GET_STATE(xfer)) {
  878         case USB_ST_TRANSFERRED:
  879                 pc = usbd_xfer_get_frame(xfer, 1);
  880                 usbd_copy_out(pc, 0, &buf, sizeof(buf));
  881                 if (buf & USLCOM_MHS_CTS)
  882                         msr |= SER_CTS;
  883                 if (buf & USLCOM_MHS_DSR)
  884                         msr |= SER_DSR;
  885                 if (buf & USLCOM_MHS_RI)
  886                         msr |= SER_RI;
  887                 if (buf & USLCOM_MHS_DCD)
  888                         msr |= SER_DCD;
  889 
  890                 if (msr != sc->sc_msr) {
  891                         DPRINTF("status change msr=0x%02x "
  892                             "(was 0x%02x)\n", msr, sc->sc_msr);
  893                         sc->sc_msr = msr;
  894                         ucom_status_change(&sc->sc_ucom);
  895                 }
  896                 break;
  897 
  898         case USB_ST_SETUP:
  899                 req.bmRequestType = USLCOM_READ;
  900                 req.bRequest = USLCOM_GET_MDMSTS;
  901                 USETW(req.wValue, 0);
  902                 USETW(req.wIndex, sc->sc_iface_no);
  903                 USETW(req.wLength, sizeof(buf));
  904 
  905                 usbd_xfer_set_frames(xfer, 2);
  906                 usbd_xfer_set_frame_len(xfer, 0, sizeof(req));
  907                 usbd_xfer_set_frame_len(xfer, 1, sizeof(buf));
  908 
  909                 pc = usbd_xfer_get_frame(xfer, 0);
  910                 usbd_copy_in(pc, 0, &req, sizeof(req));
  911                 usbd_transfer_submit(xfer);
  912                 break;
  913 
  914         default:                /* error */
  915                 if (error != USB_ERR_CANCELLED)
  916                         DPRINTF("error=%s\n", usbd_errstr(error));
  917                 break;
  918         }
  919 }
  920 
  921 static void
  922 uslcom_start_read(struct ucom_softc *ucom)
  923 {
  924         struct uslcom_softc *sc = ucom->sc_parent;
  925 
  926         /* start read endpoint */
  927         usbd_transfer_start(sc->sc_xfer[USLCOM_BULK_DT_RD]);
  928 }
  929 
  930 static void
  931 uslcom_stop_read(struct ucom_softc *ucom)
  932 {
  933         struct uslcom_softc *sc = ucom->sc_parent;
  934 
  935         /* stop read endpoint */
  936         usbd_transfer_stop(sc->sc_xfer[USLCOM_BULK_DT_RD]);
  937 }
  938 
  939 static void
  940 uslcom_start_write(struct ucom_softc *ucom)
  941 {
  942         struct uslcom_softc *sc = ucom->sc_parent;
  943 
  944         usbd_transfer_start(sc->sc_xfer[USLCOM_BULK_DT_WR]);
  945 }
  946 
  947 static void
  948 uslcom_stop_write(struct ucom_softc *ucom)
  949 {
  950         struct uslcom_softc *sc = ucom->sc_parent;
  951 
  952         usbd_transfer_stop(sc->sc_xfer[USLCOM_BULK_DT_WR]);
  953 }
  954 
  955 static void
  956 uslcom_poll(struct ucom_softc *ucom)
  957 {
  958         struct uslcom_softc *sc = ucom->sc_parent;
  959         usbd_transfer_poll(sc->sc_xfer, USLCOM_N_TRANSFER);
  960 }

Cache object: 8760addec33e468a5368d6b79b1b00e2


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