LCOV - code coverage report
Current view: top level - dev/usb - uslhcom.c (source / functions) Hit Total Coverage
Test: 6.4 Lines: 0 209 0.0 %
Date: 2018-10-19 03:25:38 Functions: 0 20 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*      $OpenBSD: uslhcom.c,v 1.6 2017/04/08 02:57:25 deraadt Exp $     */
       2             : 
       3             : /*
       4             :  * Copyright (c) 2015 SASANO Takayoshi <uaa@openbsd.org>
       5             :  *
       6             :  * Permission to use, copy, modify, and distribute this software for any
       7             :  * purpose with or without fee is hereby granted, provided that the above
       8             :  * copyright notice and this permission notice appear in all copies.
       9             :  *
      10             :  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
      11             :  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
      12             :  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
      13             :  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
      14             :  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
      15             :  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
      16             :  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
      17             :  */
      18             : 
      19             : /*
      20             :  * Device driver for Silicon Labs CP2110 USB HID-UART bridge.
      21             :  */
      22             : 
      23             : #include <sys/param.h>
      24             : #include <sys/systm.h>
      25             : #include <sys/kernel.h>
      26             : #include <sys/malloc.h>
      27             : #include <sys/conf.h>
      28             : #include <sys/tty.h>
      29             : #include <sys/device.h>
      30             : 
      31             : #include <dev/usb/usb.h>
      32             : #include <dev/usb/usbdi.h>
      33             : #include <dev/usb/usbdi_util.h>
      34             : #include <dev/usb/usbdevs.h>
      35             : 
      36             : #include <dev/usb/usbhid.h>
      37             : #include <dev/usb/uhidev.h>
      38             : 
      39             : #include <dev/usb/ucomvar.h>
      40             : #include <dev/usb/uslhcomreg.h>
      41             : 
      42             : #ifdef USLHCOM_DEBUG
      43             : #define DPRINTF(x)      if (uslhcomdebug) printf x
      44             : #else
      45             : #define DPRINTF(x)
      46             : #endif
      47             : 
      48             : struct uslhcom_softc {
      49             :         struct uhidev            sc_hdev;
      50             :         struct usbd_device      *sc_udev;
      51             : 
      52             :         u_char                  *sc_ibuf;
      53             :         u_int                    sc_icnt;
      54             : 
      55             :         u_char                   sc_lsr;
      56             :         u_char                   sc_msr;
      57             : 
      58             :         struct device           *sc_subdev;
      59             : };
      60             : 
      61             : void            uslhcom_get_status(void *, int, u_char *, u_char *);
      62             : void            uslhcom_set(void *, int, int, int);
      63             : int             uslhcom_param(void *, int, struct termios *);
      64             : int             uslhcom_open(void *, int);
      65             : void            uslhcom_close(void *, int);
      66             : void            uslhcom_write(void *, int, u_char *, u_char *, u_int32_t *);
      67             : void            uslhcom_read(void *, int, u_char **, u_int32_t *);
      68             : void            uslhcom_intr(struct uhidev *, void *, u_int);
      69             : 
      70             : int             uslhcom_match(struct device *, void *, void *);
      71             : void            uslhcom_attach(struct device *, struct device *, void *);
      72             : int             uslhcom_detach(struct device *, int);
      73             : 
      74             : int             uslhcom_uart_endis(struct uslhcom_softc *, int);
      75             : int             uslhcom_clear_fifo(struct uslhcom_softc *, int);
      76             : int             uslhcom_get_version(struct uslhcom_softc *, struct uslhcom_version_info *);
      77             : int             uslhcom_get_uart_status(struct uslhcom_softc *, struct uslhcom_uart_status *);
      78             : int             uslhcom_set_break(struct uslhcom_softc *, int);
      79             : int             uslhcom_set_config(struct uslhcom_softc *, struct uslhcom_uart_config *);
      80             : void            uslhcom_set_baud_rate(struct uslhcom_uart_config *, u_int32_t);
      81             : int             uslhcom_create_config(struct uslhcom_uart_config *, struct termios *);
      82             : int             uslhcom_setup(struct uslhcom_softc *, struct uslhcom_uart_config *);
      83             : 
      84             : struct ucom_methods uslhcom_methods = {
      85             :         uslhcom_get_status,
      86             :         uslhcom_set,
      87             :         uslhcom_param,
      88             :         NULL,
      89             :         uslhcom_open,
      90             :         uslhcom_close,
      91             :         uslhcom_read,
      92             :         uslhcom_write,
      93             : };
      94             : 
      95             : static const struct usb_devno uslhcom_devs[] = {
      96             :         { USB_VENDOR_SILABS, USB_PRODUCT_SILABS_CP2110 },
      97             : };
      98             : 
      99             : struct cfdriver uslhcom_cd = {
     100             :         NULL, "uslhcom", DV_DULL
     101             : };
     102             : 
     103             : const struct cfattach uslhcom_ca = {
     104             :         sizeof(struct uslhcom_softc),
     105             :         uslhcom_match, uslhcom_attach, uslhcom_detach
     106             : };
     107             : 
     108             : /* ----------------------------------------------------------------------
     109             :  * driver entry points
     110             :  */
     111             : 
     112             : int
     113           0 : uslhcom_match(struct device *parent, void *match, void *aux)
     114             : {
     115           0 :         struct uhidev_attach_arg *uha = aux;
     116             : 
     117             :         /* use all report IDs */
     118           0 :         if (uha->reportid != UHIDEV_CLAIM_ALLREPORTID)
     119           0 :                 return UMATCH_NONE;
     120             : 
     121           0 :         return (usb_lookup(uslhcom_devs,
     122           0 :                            uha->uaa->vendor, uha->uaa->product) != NULL ?
     123             :                 UMATCH_VENDOR_PRODUCT : UMATCH_NONE);
     124           0 : }
     125             : 
     126             : void
     127           0 : uslhcom_attach(struct device *parent, struct device *self, void *aux)
     128             : {
     129           0 :         struct uslhcom_softc *sc = (struct uslhcom_softc *)self;
     130           0 :         struct uhidev_attach_arg *uha = aux;
     131           0 :         struct usbd_device *dev = uha->parent->sc_udev;
     132           0 :         struct ucom_attach_args uca;
     133           0 :         struct uslhcom_version_info version;
     134           0 :         int err, repid, size, rsize;
     135           0 :         void *desc;
     136             : 
     137           0 :         sc->sc_hdev.sc_intr = uslhcom_intr;
     138           0 :         sc->sc_hdev.sc_parent = uha->parent;
     139           0 :         sc->sc_hdev.sc_report_id = uha->reportid;
     140             : 
     141           0 :         uhidev_get_report_desc(uha->parent, &desc, &size);
     142           0 :         for (repid = 0; repid < uha->parent->sc_nrepid; repid++) {
     143           0 :                 rsize = hid_report_size(desc, size, hid_input, repid);
     144           0 :                 if (sc->sc_hdev.sc_isize < rsize) sc->sc_hdev.sc_isize = rsize;
     145           0 :                 rsize = hid_report_size(desc, size, hid_output, repid);
     146           0 :                 if (sc->sc_hdev.sc_osize < rsize) sc->sc_hdev.sc_osize = rsize;
     147           0 :                 rsize = hid_report_size(desc, size, hid_feature, repid);
     148           0 :                 if (sc->sc_hdev.sc_fsize < rsize) sc->sc_hdev.sc_fsize = rsize;
     149             :         }
     150             : 
     151           0 :         printf("\n");
     152             : 
     153           0 :         sc->sc_udev = dev;
     154             : 
     155           0 :         err = uhidev_open(&sc->sc_hdev);
     156           0 :         if (err) {
     157             :                 DPRINTF(("uslhcom_attach: uhidev_open %d\n", err));
     158           0 :                 return;
     159             :         }
     160             : 
     161             :         DPRINTF(("uslhcom_attach: sc %p opipe %p ipipe %p report_id %d\n",
     162             :                  sc, sc->sc_hdev.sc_parent->sc_opipe,
     163             :                  sc->sc_hdev.sc_parent->sc_ipipe, uha->reportid));
     164             :         DPRINTF(("uslhcom_attach: isize %d osize %d fsize %d\n",
     165             :                  sc->sc_hdev.sc_isize, sc->sc_hdev.sc_osize,
     166             :                  sc->sc_hdev.sc_fsize));
     167             : 
     168           0 :         uslhcom_uart_endis(sc, UART_DISABLE);
     169           0 :         uslhcom_get_version(sc, &version);
     170           0 :         printf("%s: pid %#x rev %#x\n", sc->sc_hdev.sc_dev.dv_xname,
     171           0 :                version.product_id, version.product_revision);
     172             : 
     173             :         /* setup ucom layer */
     174           0 :         bzero(&uca, sizeof uca);
     175           0 :         uca.portno = UCOM_UNK_PORTNO;
     176           0 :         uca.bulkin = uca.bulkout = -1;
     177           0 :         uca.ibufsize = uca.ibufsizepad = 0;
     178           0 :         uca.obufsize = sc->sc_hdev.sc_osize;
     179           0 :         uca.opkthdrlen = USLHCOM_TX_HEADER_SIZE;
     180           0 :         uca.uhidev = sc->sc_hdev.sc_parent;
     181           0 :         uca.device = uha->uaa->device;
     182           0 :         uca.iface = uha->uaa->iface;
     183           0 :         uca.methods = &uslhcom_methods;
     184           0 :         uca.arg = sc;
     185           0 :         uca.info = NULL;
     186             : 
     187           0 :         sc->sc_subdev = config_found_sm(self, &uca, ucomprint, ucomsubmatch);
     188           0 : }
     189             : 
     190             : int
     191           0 : uslhcom_detach(struct device *self, int flags)
     192             : {
     193           0 :         struct uslhcom_softc *sc = (struct uslhcom_softc *)self;
     194             : 
     195             :         DPRINTF(("uslhcom_detach: sc=%p flags=%d\n", sc, flags));
     196           0 :         if (sc->sc_subdev != NULL) {
     197           0 :                 config_detach(sc->sc_subdev, flags);
     198           0 :                 sc->sc_subdev = NULL;
     199           0 :         }
     200             : 
     201           0 :         if (sc->sc_hdev.sc_state & UHIDEV_OPEN)
     202           0 :                 uhidev_close(&sc->sc_hdev);
     203             : 
     204           0 :         return 0;
     205             : }
     206             : 
     207             : /* ----------------------------------------------------------------------
     208             :  * low level I/O
     209             :  */
     210             : 
     211             : int
     212           0 : uslhcom_uart_endis(struct uslhcom_softc *sc, int enable)
     213             : {
     214             :         int len;
     215           0 :         u_char val;
     216             : 
     217             :         len = sizeof(val);
     218           0 :         val = enable;
     219             : 
     220           0 :         return uhidev_set_report(sc->sc_hdev.sc_parent, UHID_FEATURE_REPORT,
     221           0 :                                  GET_SET_UART_ENABLE, &val, len) != len;
     222           0 : }
     223             : 
     224             : int
     225           0 : uslhcom_clear_fifo(struct uslhcom_softc *sc, int fifo)
     226             : {
     227             :         int len;
     228           0 :         u_char val;
     229             : 
     230             :         len = sizeof(val);
     231           0 :         val = fifo;
     232             : 
     233           0 :         return uhidev_set_report(sc->sc_hdev.sc_parent, UHID_FEATURE_REPORT,
     234           0 :                                  SET_CLEAR_FIFOS, &val, len) != len;
     235           0 : }
     236             : 
     237             : int
     238           0 : uslhcom_get_version(struct uslhcom_softc *sc, struct uslhcom_version_info *version)
     239             : {
     240             :         int len;
     241             : 
     242             :         len = sizeof(*version);
     243             : 
     244           0 :         return uhidev_get_report(sc->sc_hdev.sc_parent, UHID_FEATURE_REPORT,
     245           0 :                                  GET_VERSION, version, len) < len;
     246             : }
     247             : 
     248             : int
     249           0 : uslhcom_get_uart_status(struct uslhcom_softc *sc, struct uslhcom_uart_status *status)
     250             : {
     251             :         int len;
     252             : 
     253             :         len = sizeof(*status);
     254             : 
     255           0 :         return uhidev_get_report(sc->sc_hdev.sc_parent, UHID_FEATURE_REPORT,
     256           0 :                                  GET_UART_STATUS, status, len) < len;
     257             : }
     258             : 
     259             : int
     260           0 : uslhcom_set_break(struct uslhcom_softc *sc, int onoff)
     261             : {
     262             :         int len, reportid;
     263           0 :         u_char val;
     264             : 
     265             :         len = sizeof(val);
     266             : 
     267           0 :         if (onoff) {
     268             :                 val = 0; /* send break until SET_STOP_LINE_BREAK */
     269             :                 reportid = SET_TRANSMIT_LINE_BREAK;
     270           0 :         } else {
     271             :                 val = 0; /* any value can be accepted */
     272             :                 reportid = SET_STOP_LINE_BREAK;
     273             :         }
     274             : 
     275           0 :         return uhidev_set_report(sc->sc_hdev.sc_parent, UHID_FEATURE_REPORT,
     276           0 :                                  reportid, &val, len) != len;
     277           0 : }
     278             : 
     279             : int
     280           0 : uslhcom_set_config(struct uslhcom_softc *sc, struct uslhcom_uart_config *config)
     281             : {
     282             :         int len;
     283             : 
     284             :         len = sizeof(*config);
     285             : 
     286           0 :         return uhidev_set_report(sc->sc_hdev.sc_parent, UHID_FEATURE_REPORT,
     287           0 :                                  GET_SET_UART_CONFIG, config, len) != len;
     288             : }
     289             : 
     290             : void
     291           0 : uslhcom_set_baud_rate(struct uslhcom_uart_config *config, u_int32_t baud_rate)
     292             : {
     293           0 :         config->baud_rate[0] = baud_rate >> 24;
     294           0 :         config->baud_rate[1] = baud_rate >> 16;
     295           0 :         config->baud_rate[2] = baud_rate >> 8;
     296           0 :         config->baud_rate[3] = baud_rate >> 0;
     297           0 : }
     298             : 
     299             : int
     300           0 : uslhcom_create_config(struct uslhcom_uart_config *config, struct termios *t)
     301             : {
     302           0 :         if (t->c_ospeed < UART_CONFIG_BAUD_RATE_MIN ||
     303           0 :             t->c_ospeed > UART_CONFIG_BAUD_RATE_MAX)
     304           0 :                 return EINVAL;
     305             : 
     306           0 :         uslhcom_set_baud_rate(config, t->c_ospeed);
     307             : 
     308           0 :         if (ISSET(t->c_cflag, PARENB)) {
     309           0 :                 if (ISSET(t->c_cflag, PARODD))
     310           0 :                         config->parity = UART_CONFIG_PARITY_ODD;
     311             :                 else
     312           0 :                         config->parity = UART_CONFIG_PARITY_EVEN;
     313             :         } else
     314           0 :                 config->parity = UART_CONFIG_PARITY_NONE;
     315             : 
     316           0 :         if (ISSET(t->c_cflag, CRTSCTS))
     317           0 :                 config->data_control = UART_CONFIG_DATA_CONTROL_HARD;
     318             :         else
     319           0 :                 config->data_control = UART_CONFIG_DATA_CONTROL_NONE;
     320             : 
     321           0 :         switch (ISSET(t->c_cflag, CSIZE)) {
     322             :         case CS5:
     323           0 :                 config->data_bits = UART_CONFIG_DATA_BITS_5;
     324           0 :                 break;
     325             :         case CS6:
     326           0 :                 config->data_bits = UART_CONFIG_DATA_BITS_6;
     327           0 :                 break;
     328             :         case CS7:
     329           0 :                 config->data_bits = UART_CONFIG_DATA_BITS_7;
     330           0 :                 break;
     331             :         case CS8:
     332           0 :                 config->data_bits = UART_CONFIG_DATA_BITS_8;
     333           0 :                 break;
     334             :         default:
     335             :                 return EINVAL;
     336             :         }
     337             : 
     338           0 :         if (ISSET(t->c_cflag, CSTOPB))
     339           0 :                 config->stop_bits = UART_CONFIG_STOP_BITS_2;
     340             :         else
     341           0 :                 config->stop_bits = UART_CONFIG_STOP_BITS_1;
     342             : 
     343           0 :         return 0;
     344           0 : }
     345             : 
     346             : int
     347           0 : uslhcom_setup(struct uslhcom_softc *sc, struct uslhcom_uart_config *config)
     348             : {
     349           0 :         struct uslhcom_uart_status status;
     350             : 
     351           0 :         if (uslhcom_uart_endis(sc, UART_DISABLE))
     352           0 :                 return EIO;
     353             : 
     354           0 :         if (uslhcom_set_config(sc, config))
     355           0 :                 return EIO;
     356             : 
     357           0 :         if (uslhcom_clear_fifo(sc, CLEAR_TX_FIFO | CLEAR_RX_FIFO))
     358           0 :                 return EIO;
     359             : 
     360           0 :         if (uslhcom_get_uart_status(sc, &status))
     361           0 :                 return EIO;
     362             : 
     363           0 :         if (uslhcom_uart_endis(sc, UART_ENABLE))
     364           0 :                 return EIO;
     365             : 
     366           0 :         return 0;
     367           0 : }
     368             : 
     369             : /* ----------------------------------------------------------------------
     370             :  * methods for ucom
     371             :  */
     372             : 
     373             : void
     374           0 : uslhcom_get_status(void *arg, int portno, u_char *rlsr, u_char *rmsr)
     375             : {
     376           0 :         struct uslhcom_softc *sc = arg;
     377             : 
     378           0 :         if (usbd_is_dying(sc->sc_udev))
     379           0 :                 return;
     380             : 
     381           0 :         *rlsr = sc->sc_lsr;
     382           0 :         *rmsr = sc->sc_msr;
     383           0 : }
     384             : 
     385             : void
     386           0 : uslhcom_set(void *arg, int portno, int reg, int onoff)
     387             : {
     388           0 :         struct uslhcom_softc *sc = arg;
     389             : 
     390           0 :         if (usbd_is_dying(sc->sc_udev))
     391           0 :                 return;
     392             : 
     393           0 :         switch (reg) {
     394             :         case UCOM_SET_DTR:
     395             :         case UCOM_SET_RTS:
     396             :                 /* no support, do nothing */
     397             :                 break;
     398             :         case UCOM_SET_BREAK:
     399           0 :                 uslhcom_set_break(sc, onoff);
     400           0 :                 break;
     401             :         }
     402           0 : }
     403             : 
     404             : int
     405           0 : uslhcom_param(void *arg, int portno, struct termios *t)
     406             : {
     407           0 :         struct uslhcom_softc *sc = arg;
     408           0 :         struct uslhcom_uart_config config;
     409             :         int ret;
     410             : 
     411           0 :         if (usbd_is_dying(sc->sc_udev))
     412           0 :                 return 0;
     413             : 
     414           0 :         ret = uslhcom_create_config(&config, t);
     415           0 :         if (ret)
     416           0 :                 return ret;
     417             : 
     418           0 :         ret = uslhcom_setup(sc, &config);
     419           0 :         if (ret)
     420           0 :                 return ret;
     421             : 
     422           0 :         return 0;
     423           0 : }
     424             : 
     425             : int
     426           0 : uslhcom_open(void *arg, int portno)
     427             : {
     428           0 :         struct uslhcom_softc *sc = arg;
     429           0 :         struct uslhcom_uart_config config;
     430             :         int ret;
     431             : 
     432           0 :         if (usbd_is_dying(sc->sc_udev))
     433           0 :                 return EIO;
     434             : 
     435           0 :         sc->sc_ibuf = malloc(sc->sc_hdev.sc_isize, M_USBDEV, M_WAITOK);
     436             : 
     437           0 :         uslhcom_set_baud_rate(&config, 9600);
     438           0 :         config.parity = UART_CONFIG_PARITY_NONE;
     439           0 :         config.data_control = UART_CONFIG_DATA_CONTROL_NONE;
     440           0 :         config.data_bits = UART_CONFIG_DATA_BITS_8;
     441           0 :         config.stop_bits = UART_CONFIG_STOP_BITS_1;
     442             : 
     443           0 :         ret = uslhcom_set_config(sc, &config);
     444           0 :         if (ret)
     445           0 :                 return ret;
     446             : 
     447           0 :         return 0;
     448           0 : }
     449             : 
     450             : void
     451           0 : uslhcom_close(void *arg, int portno)
     452             : {
     453           0 :         struct uslhcom_softc *sc = arg;
     454             :         int s;
     455             : 
     456           0 :         if (usbd_is_dying(sc->sc_udev))
     457           0 :                 return;
     458             : 
     459           0 :         uslhcom_uart_endis(sc, UART_DISABLE);
     460             : 
     461           0 :         s = splusb();
     462           0 :         if (sc->sc_ibuf != NULL) {
     463           0 :                 free(sc->sc_ibuf, M_USBDEV, sc->sc_hdev.sc_isize);
     464           0 :                 sc->sc_ibuf = NULL;
     465           0 :         }
     466           0 :         splx(s);
     467           0 : }
     468             : 
     469             : void
     470           0 : uslhcom_read(void *arg, int portno, u_char **ptr, u_int32_t *cnt)
     471             : {
     472           0 :         struct uslhcom_softc *sc = arg;
     473             : 
     474           0 :         *ptr = sc->sc_ibuf;
     475           0 :         *cnt = sc->sc_icnt;
     476           0 : }
     477             : 
     478             : void
     479           0 : uslhcom_write(void *arg, int portno, u_char *to, u_char *data, u_int32_t *cnt)
     480             : {
     481           0 :         bcopy(data, &to[USLHCOM_TX_HEADER_SIZE], *cnt);
     482           0 :         to[0] = *cnt;   /* add Report ID (= transmit length) */
     483           0 :         *cnt += USLHCOM_TX_HEADER_SIZE;
     484           0 : }
     485             : 
     486             : void
     487           0 : uslhcom_intr(struct uhidev *addr, void *ibuf, u_int len)
     488             : {
     489             :         extern void ucomreadcb(struct usbd_xfer *, void *, usbd_status);
     490           0 :         struct uslhcom_softc *sc = (struct uslhcom_softc *)addr;
     491             :         int s;
     492             : 
     493           0 :         if (sc->sc_ibuf == NULL)
     494           0 :                 return;
     495             : 
     496           0 :         s = spltty();
     497           0 :         sc->sc_icnt = len;   /* Report ID is already stripped */
     498           0 :         bcopy(ibuf, sc->sc_ibuf, len);
     499           0 :         ucomreadcb(addr->sc_parent->sc_ixfer, sc->sc_subdev,
     500             :                    USBD_NORMAL_COMPLETION);
     501           0 :         splx(s);
     502           0 : }

Generated by: LCOV version 1.13