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

          Line data    Source code
       1             : /*      $OpenBSD: uchcom.c,v 1.27 2017/04/08 02:57:25 deraadt Exp $     */
       2             : /*      $NetBSD: uchcom.c,v 1.1 2007/09/03 17:57:37 tshiozak Exp $      */
       3             : 
       4             : /*
       5             :  * Copyright (c) 2007 The NetBSD Foundation, Inc.
       6             :  * All rights reserved.
       7             :  *
       8             :  * This code is derived from software contributed to The NetBSD Foundation
       9             :  * by Takuya SHIOZAKI (tshiozak@netbsd.org).
      10             :  *
      11             :  * Redistribution and use in source and binary forms, with or without
      12             :  * modification, are permitted provided that the following conditions
      13             :  * are met:
      14             :  * 1. Redistributions of source code must retain the above copyright
      15             :  *    notice, this list of conditions and the following disclaimer.
      16             :  * 2. Redistributions in binary form must reproduce the above copyright
      17             :  *    notice, this list of conditions and the following disclaimer in the
      18             :  *    documentation and/or other materials provided with the distribution.
      19             :  *
      20             :  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
      21             :  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
      22             :  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
      23             :  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
      24             :  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
      25             :  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
      26             :  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
      27             :  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
      28             :  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
      29             :  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
      30             :  * POSSIBILITY OF SUCH DAMAGE.
      31             :  */
      32             : 
      33             : /*
      34             :  * driver for WinChipHead CH341/340, the worst USB-serial chip in the world.
      35             :  */
      36             : 
      37             : #include <sys/param.h>
      38             : #include <sys/systm.h>
      39             : #include <sys/kernel.h>
      40             : #include <sys/malloc.h>
      41             : #include <sys/tty.h>
      42             : #include <sys/device.h>
      43             : 
      44             : #include <dev/usb/usb.h>
      45             : #include <dev/usb/usbdi.h>
      46             : #include <dev/usb/usbdi_util.h>
      47             : #include <dev/usb/usbdevs.h>
      48             : #include <dev/usb/ucomvar.h>
      49             : 
      50             : #ifdef UCHCOM_DEBUG
      51             : #define DPRINTFN(n, x)  do { if (uchcomdebug > (n)) printf x; } while (0)
      52             : int     uchcomdebug = 0;
      53             : #else
      54             : #define DPRINTFN(n, x)
      55             : #endif
      56             : #define DPRINTF(x) DPRINTFN(0, x)
      57             : 
      58             : #define UCHCOM_IFACE_INDEX      0
      59             : 
      60             : #define UCHCOM_REV_CH340        0x0250
      61             : #define UCHCOM_INPUT_BUF_SIZE   8
      62             : 
      63             : #define UCHCOM_REQ_GET_VERSION  0x5F
      64             : #define UCHCOM_REQ_READ_REG     0x95
      65             : #define UCHCOM_REQ_WRITE_REG    0x9A
      66             : #define UCHCOM_REQ_RESET        0xA1
      67             : #define UCHCOM_REQ_SET_DTRRTS   0xA4
      68             : 
      69             : #define UCHCOM_REG_STAT1        0x06
      70             : #define UCHCOM_REG_STAT2        0x07
      71             : #define UCHCOM_REG_BPS_PRE      0x12
      72             : #define UCHCOM_REG_BPS_DIV      0x13
      73             : #define UCHCOM_REG_BPS_MOD      0x14
      74             : #define UCHCOM_REG_BPS_PAD      0x0F
      75             : #define UCHCOM_REG_BREAK1       0x05
      76             : #define UCHCOM_REG_BREAK2       0x18
      77             : #define UCHCOM_REG_LCR1         0x18
      78             : #define UCHCOM_REG_LCR2         0x25
      79             : 
      80             : #define UCHCOM_VER_20           0x20
      81             : 
      82             : #define UCHCOM_BASE_UNKNOWN     0
      83             : #define UCHCOM_BPS_MOD_BASE     20000000
      84             : #define UCHCOM_BPS_MOD_BASE_OFS 1100
      85             : 
      86             : #define UCHCOM_DTR_MASK         0x20
      87             : #define UCHCOM_RTS_MASK         0x40
      88             : 
      89             : #define UCHCOM_BRK1_MASK        0x01
      90             : #define UCHCOM_BRK2_MASK        0x40
      91             : 
      92             : #define UCHCOM_INTR_STAT1       0x02
      93             : #define UCHCOM_INTR_STAT2       0x03
      94             : #define UCHCOM_INTR_LEAST       4
      95             : 
      96             : /*
      97             :  * XXX - these magic numbers come from Linux (drivers/usb/serial/ch341.c).
      98             :  * The manufacturer was unresponsive when asked for documentation.
      99             :  */
     100             : #define UCHCOM_RESET_VALUE      0x501F  /* line mode? */
     101             : #define UCHCOM_RESET_INDEX      0xD90A  /* baud rate? */
     102             : 
     103             : #define UCHCOMIBUFSIZE 256
     104             : #define UCHCOMOBUFSIZE 256
     105             : 
     106             : struct uchcom_softc
     107             : {
     108             :         struct device            sc_dev;
     109             :         struct usbd_device      *sc_udev;
     110             :         struct device           *sc_subdev;
     111             :         struct usbd_interface   *sc_iface;
     112             :         /* */
     113             :         int                      sc_intr_endpoint;
     114             :         struct usbd_pipe        *sc_intr_pipe;
     115             :         u_char                  *sc_intr_buf;
     116             :         int                      sc_isize;
     117             :         /* */
     118             :         uint8_t                  sc_version;
     119             :         int                      sc_dtr;
     120             :         int                      sc_rts;
     121             :         u_char                   sc_lsr;
     122             :         u_char                   sc_msr;
     123             :         int                      sc_lcr1;
     124             :         int                      sc_lcr2;
     125             : };
     126             : 
     127             : struct uchcom_endpoints
     128             : {
     129             :         int             ep_bulkin;
     130             :         int             ep_bulkout;
     131             :         int             ep_intr;
     132             :         int             ep_intr_size;
     133             : };
     134             : 
     135             : struct uchcom_divider
     136             : {
     137             :         uint8_t         dv_prescaler;
     138             :         uint8_t         dv_div;
     139             :         uint8_t         dv_mod;
     140             : };
     141             : 
     142             : struct uchcom_divider_record
     143             : {
     144             :         uint32_t                dvr_high;
     145             :         uint32_t                dvr_low;
     146             :         uint32_t                dvr_base_clock;
     147             :         struct uchcom_divider   dvr_divider;
     148             : };
     149             : 
     150             : static const struct uchcom_divider_record dividers[] =
     151             : {
     152             :         {  307200, 307200, UCHCOM_BASE_UNKNOWN, { 7, 0xD9, 0 } },
     153             :         {  921600, 921600, UCHCOM_BASE_UNKNOWN, { 7, 0xF3, 0 } },
     154             :         { 2999999,  23530,             6000000, { 3,    0, 0 } },
     155             :         {   23529,   2942,              750000, { 2,    0, 0 } },
     156             :         {    2941,    368,               93750, { 1,    0, 0 } },
     157             :         {     367,      1,               11719, { 0,    0, 0 } },
     158             : };
     159             : 
     160             : void            uchcom_get_status(void *, int, u_char *, u_char *);
     161             : void            uchcom_set(void *, int, int, int);
     162             : int             uchcom_param(void *, int, struct termios *);
     163             : int             uchcom_open(void *, int);
     164             : void            uchcom_close(void *, int);
     165             : void            uchcom_intr(struct usbd_xfer *, void *, usbd_status);
     166             : 
     167             : int             uchcom_find_ifaces(struct uchcom_softc *,
     168             :                     struct usbd_interface **);
     169             : int             uchcom_find_endpoints(struct uchcom_softc *,
     170             :                     struct uchcom_endpoints *);
     171             : void            uchcom_close_intr_pipe(struct uchcom_softc *);
     172             : 
     173             : 
     174             : usbd_status     uchcom_generic_control_out(struct uchcom_softc *sc,
     175             :                     uint8_t reqno, uint16_t value, uint16_t index);
     176             : usbd_status     uchcom_generic_control_in(struct uchcom_softc *, uint8_t, 
     177             :                     uint16_t, uint16_t, void *, int, int *);
     178             : usbd_status     uchcom_write_reg(struct uchcom_softc *, uint8_t, uint8_t,
     179             :                     uint8_t, uint8_t);
     180             : usbd_status     uchcom_read_reg(struct uchcom_softc *, uint8_t, uint8_t *,
     181             :                     uint8_t, uint8_t *);
     182             : usbd_status     uchcom_get_version(struct uchcom_softc *, uint8_t *); 
     183             : usbd_status     uchcom_read_status(struct uchcom_softc *, uint8_t *);
     184             : usbd_status     uchcom_set_dtrrts_10(struct uchcom_softc *, uint8_t);
     185             : usbd_status     uchcom_set_dtrrts_20(struct uchcom_softc *, uint8_t);
     186             : int             uchcom_update_version(struct uchcom_softc *);
     187             : void            uchcom_convert_status(struct uchcom_softc *, uint8_t);
     188             : int             uchcom_update_status(struct uchcom_softc *);
     189             : int             uchcom_set_dtrrts(struct uchcom_softc *, int, int);
     190             : int             uchcom_set_break(struct uchcom_softc *, int);
     191             : int             uchcom_calc_divider_settings(struct uchcom_divider *, uint32_t);
     192             : int             uchcom_set_dte_rate(struct uchcom_softc *, uint32_t);
     193             : int             uchcom_set_line_control(struct uchcom_softc *, tcflag_t);
     194             : int             uchcom_clear_chip(struct uchcom_softc *);
     195             : int             uchcom_reset_chip(struct uchcom_softc *);
     196             : int             uchcom_setup_comm(struct uchcom_softc *);
     197             : int             uchcom_setup_intr_pipe(struct uchcom_softc *);
     198             : 
     199             : 
     200             : int             uchcom_match(struct device *, void *, void *);
     201             : void            uchcom_attach(struct device *, struct device *, void *);
     202             : int             uchcom_detach(struct device *, int);
     203             : 
     204             : struct  ucom_methods uchcom_methods = {
     205             :         uchcom_get_status,
     206             :         uchcom_set,
     207             :         uchcom_param,
     208             :         NULL,
     209             :         uchcom_open,
     210             :         uchcom_close,
     211             :         NULL,
     212             :         NULL,
     213             : };
     214             : 
     215             : static const struct usb_devno uchcom_devs[] = {
     216             :         { USB_VENDOR_WCH, USB_PRODUCT_WCH_CH341 },
     217             :         { USB_VENDOR_WCH2, USB_PRODUCT_WCH2_CH340 },
     218             :         { USB_VENDOR_WCH2, USB_PRODUCT_WCH2_CH341A }
     219             : };
     220             : 
     221             : struct cfdriver uchcom_cd = {
     222             :         NULL, "uchcom", DV_DULL
     223             : };
     224             : 
     225             : const struct cfattach uchcom_ca = {
     226             :         sizeof(struct uchcom_softc), uchcom_match, uchcom_attach, uchcom_detach
     227             : };
     228             : 
     229             : /* ----------------------------------------------------------------------
     230             :  * driver entry points
     231             :  */
     232             : 
     233             : int
     234           0 : uchcom_match(struct device *parent, void *match, void *aux)
     235             : {
     236           0 :         struct usb_attach_arg *uaa = aux;
     237             : 
     238           0 :         if (uaa->iface == NULL)
     239           0 :                 return UMATCH_NONE;
     240             : 
     241           0 :         return (usb_lookup(uchcom_devs, uaa->vendor, uaa->product) != NULL ?
     242             :             UMATCH_VENDOR_PRODUCT : UMATCH_NONE);
     243           0 : }
     244             : 
     245             : void
     246           0 : uchcom_attach(struct device *parent, struct device *self, void *aux)
     247             : {
     248           0 :         struct uchcom_softc *sc = (struct uchcom_softc *)self;
     249           0 :         struct usb_attach_arg *uaa = aux;
     250           0 :         struct ucom_attach_args uca;
     251           0 :         struct usbd_device *dev = uaa->device;
     252           0 :         struct uchcom_endpoints endpoints;
     253             : 
     254           0 :         sc->sc_udev = dev;
     255           0 :         sc->sc_dtr = sc->sc_rts = -1;
     256             : 
     257             :         DPRINTF(("\n\nuchcom attach: sc=%p\n", sc));
     258             : 
     259           0 :         switch (uaa->release) {
     260             :         case UCHCOM_REV_CH340:
     261           0 :                 printf("%s: CH340\n", sc->sc_dev.dv_xname);
     262           0 :                 break;
     263             :         default:
     264           0 :                 printf("%s: CH341\n", sc->sc_dev.dv_xname);
     265           0 :                 break;
     266             :         }
     267             : 
     268           0 :         if (uchcom_find_ifaces(sc, &sc->sc_iface))
     269             :                 goto failed;
     270             : 
     271           0 :         if (uchcom_find_endpoints(sc, &endpoints))
     272             :                 goto failed;
     273             : 
     274           0 :         sc->sc_intr_endpoint = endpoints.ep_intr;
     275           0 :         sc->sc_isize = endpoints.ep_intr_size;
     276             : 
     277             :         /* setup ucom layer */
     278           0 :         uca.portno = UCOM_UNK_PORTNO;
     279           0 :         uca.bulkin = endpoints.ep_bulkin;
     280           0 :         uca.bulkout = endpoints.ep_bulkout;
     281           0 :         uca.ibufsize = UCHCOMIBUFSIZE;
     282           0 :         uca.obufsize = UCHCOMOBUFSIZE;
     283           0 :         uca.ibufsizepad = UCHCOMIBUFSIZE;
     284           0 :         uca.opkthdrlen = 0;
     285           0 :         uca.device = dev;
     286           0 :         uca.iface = sc->sc_iface;
     287           0 :         uca.methods = &uchcom_methods;
     288           0 :         uca.arg = sc;
     289           0 :         uca.info = NULL;
     290             :         
     291           0 :         sc->sc_subdev = config_found_sm(self, &uca, ucomprint, ucomsubmatch);
     292             : 
     293           0 :         return;
     294             : 
     295             : failed:
     296           0 :         usbd_deactivate(sc->sc_udev);
     297           0 : }
     298             : 
     299             : int
     300           0 : uchcom_detach(struct device *self, int flags)
     301             : {
     302           0 :         struct uchcom_softc *sc = (struct uchcom_softc *)self;
     303             :         int rv = 0;
     304             : 
     305             :         DPRINTF(("uchcom_detach: sc=%p flags=%d\n", sc, flags));
     306             : 
     307           0 :         uchcom_close_intr_pipe(sc);
     308             : 
     309           0 :         if (sc->sc_subdev != NULL) {
     310           0 :                 rv = config_detach(sc->sc_subdev, flags);
     311           0 :                 sc->sc_subdev = NULL;
     312           0 :         }
     313             : 
     314           0 :         return rv;
     315             : }
     316             : 
     317             : int
     318           0 : uchcom_find_ifaces(struct uchcom_softc *sc, struct usbd_interface **riface)
     319             : {
     320             :         usbd_status err;
     321             : 
     322           0 :         err = usbd_device2interface_handle(sc->sc_udev, UCHCOM_IFACE_INDEX,
     323             :                                            riface);
     324           0 :         if (err) {
     325           0 :                 printf("\n%s: failed to get interface: %s\n",
     326           0 :                         sc->sc_dev.dv_xname, usbd_errstr(err));
     327           0 :                 return -1;
     328             :         }
     329             : 
     330           0 :         return 0;
     331           0 : }
     332             : 
     333             : int
     334           0 : uchcom_find_endpoints(struct uchcom_softc *sc,
     335             :     struct uchcom_endpoints *endpoints)
     336             : {
     337             :         int i, bin=-1, bout=-1, intr=-1, isize=0;
     338             :         usb_interface_descriptor_t *id;
     339             :         usb_endpoint_descriptor_t *ed;
     340             : 
     341           0 :         id = usbd_get_interface_descriptor(sc->sc_iface);
     342             : 
     343           0 :         for (i = 0; i < id->bNumEndpoints; i++) {
     344           0 :                 ed = usbd_interface2endpoint_descriptor(sc->sc_iface, i);
     345           0 :                 if (ed == NULL) {
     346           0 :                         printf("%s: no endpoint descriptor for %d\n",
     347           0 :                                 sc->sc_dev.dv_xname, i);
     348           0 :                         return -1;
     349             :                 }
     350             : 
     351           0 :                 if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
     352           0 :                     UE_GET_XFERTYPE(ed->bmAttributes) == UE_INTERRUPT) {
     353             :                         intr = ed->bEndpointAddress;
     354           0 :                         isize = UGETW(ed->wMaxPacketSize);
     355           0 :                 } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
     356           0 :                            UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
     357             :                         bin = ed->bEndpointAddress;
     358           0 :                 } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT &&
     359           0 :                            UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
     360             :                         bout = ed->bEndpointAddress;
     361           0 :                 }
     362             :         }
     363             : 
     364           0 :         if (intr == -1 || bin == -1 || bout == -1) {
     365           0 :                 if (intr == -1) {
     366           0 :                         printf("%s: no interrupt end point\n",
     367           0 :                                sc->sc_dev.dv_xname);
     368           0 :                 }
     369           0 :                 if (bin == -1) {
     370           0 :                         printf("%s: no data bulk in end point\n",
     371           0 :                                sc->sc_dev.dv_xname);
     372           0 :                 }
     373           0 :                 if (bout == -1) {
     374           0 :                         printf("%s: no data bulk out end point\n",
     375           0 :                                sc->sc_dev.dv_xname);
     376           0 :                 }
     377           0 :                 return -1;
     378             :         }
     379           0 :         if (isize < UCHCOM_INTR_LEAST) {
     380           0 :                 printf("%s: intr pipe is too short", sc->sc_dev.dv_xname);
     381           0 :                 return -1;
     382             :         }
     383             : 
     384             :         DPRINTF(("%s: bulkin=%d, bulkout=%d, intr=%d, isize=%d\n",
     385             :                  sc->sc_dev.dv_xname, bin, bout, intr, isize));
     386             : 
     387           0 :         endpoints->ep_intr = intr;
     388           0 :         endpoints->ep_intr_size = isize;
     389           0 :         endpoints->ep_bulkin = bin;
     390           0 :         endpoints->ep_bulkout = bout;
     391             : 
     392           0 :         return 0;
     393           0 : }
     394             : 
     395             : 
     396             : /* ----------------------------------------------------------------------
     397             :  * low level i/o
     398             :  */
     399             : 
     400             : usbd_status
     401           0 : uchcom_generic_control_out(struct uchcom_softc *sc, uint8_t reqno,
     402             :     uint16_t value, uint16_t index)
     403             : {
     404           0 :         usb_device_request_t req;
     405             : 
     406           0 :         req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
     407           0 :         req.bRequest = reqno;
     408           0 :         USETW(req.wValue, value);
     409           0 :         USETW(req.wIndex, index);
     410           0 :         USETW(req.wLength, 0);
     411             : 
     412           0 :         return usbd_do_request(sc->sc_udev, &req, 0);
     413           0 : }
     414             : 
     415             : usbd_status
     416           0 : uchcom_generic_control_in(struct uchcom_softc *sc, uint8_t reqno,
     417             :     uint16_t value, uint16_t index, void *buf, int buflen, int *actlen)
     418             : {
     419           0 :         usb_device_request_t req;
     420             : 
     421           0 :         req.bmRequestType = UT_READ_VENDOR_DEVICE;
     422           0 :         req.bRequest = reqno;
     423           0 :         USETW(req.wValue, value);
     424           0 :         USETW(req.wIndex, index);
     425           0 :         USETW(req.wLength, (uint16_t)buflen);
     426             : 
     427           0 :         return usbd_do_request_flags(sc->sc_udev, &req, buf,
     428             :                                      USBD_SHORT_XFER_OK, actlen,
     429             :                                      USBD_DEFAULT_TIMEOUT);
     430           0 : }
     431             : 
     432             : usbd_status
     433           0 : uchcom_write_reg(struct uchcom_softc *sc,
     434             :     uint8_t reg1, uint8_t val1, uint8_t reg2, uint8_t val2)
     435             : {
     436             :         DPRINTF(("uchcom: write reg 0x%02X<-0x%02X, 0x%02X<-0x%02X\n",
     437             :                  (unsigned)reg1, (unsigned)val1,
     438             :                  (unsigned)reg2, (unsigned)val2));
     439           0 :         return uchcom_generic_control_out(
     440             :                 sc, UCHCOM_REQ_WRITE_REG,
     441           0 :                 reg1|((uint16_t)reg2<<8), val1|((uint16_t)val2<<8));
     442             : }
     443             : 
     444             : usbd_status
     445           0 : uchcom_read_reg(struct uchcom_softc *sc,
     446             :     uint8_t reg1, uint8_t *rval1, uint8_t reg2, uint8_t *rval2)
     447             : {
     448           0 :         uint8_t buf[UCHCOM_INPUT_BUF_SIZE];
     449             :         usbd_status err;
     450           0 :         int actin;
     451             : 
     452           0 :         err = uchcom_generic_control_in(
     453             :                 sc, UCHCOM_REQ_READ_REG,
     454           0 :                 reg1|((uint16_t)reg2<<8), 0, buf, sizeof buf, &actin);
     455           0 :         if (err)
     456           0 :                 return err;
     457             : 
     458             :         DPRINTF(("uchcom: read reg 0x%02X->0x%02X, 0x%02X->0x%02X\n",
     459             :                  (unsigned)reg1, (unsigned)buf[0],
     460             :                  (unsigned)reg2, (unsigned)buf[1]));
     461             : 
     462           0 :         if (rval1) *rval1 = buf[0];
     463           0 :         if (rval2) *rval2 = buf[1];
     464             : 
     465           0 :         return USBD_NORMAL_COMPLETION;
     466           0 : }
     467             : 
     468             : usbd_status
     469           0 : uchcom_get_version(struct uchcom_softc *sc, uint8_t *rver)
     470             : {
     471           0 :         uint8_t buf[UCHCOM_INPUT_BUF_SIZE];
     472             :         usbd_status err;
     473           0 :         int actin;
     474             : 
     475           0 :         err = uchcom_generic_control_in(
     476           0 :                 sc, UCHCOM_REQ_GET_VERSION, 0, 0, buf, sizeof buf, &actin);
     477           0 :         if (err)
     478           0 :                 return err;
     479             : 
     480           0 :         if (rver) *rver = buf[0];
     481             : 
     482           0 :         return USBD_NORMAL_COMPLETION;
     483           0 : }
     484             : 
     485             : usbd_status
     486           0 : uchcom_read_status(struct uchcom_softc *sc, uint8_t *rval)
     487             : {
     488           0 :         return uchcom_read_reg(sc, UCHCOM_REG_STAT1, rval, UCHCOM_REG_STAT2,
     489             :             NULL);
     490             : }
     491             : 
     492             : usbd_status
     493           0 : uchcom_set_dtrrts_10(struct uchcom_softc *sc, uint8_t val)
     494             : {
     495           0 :         return uchcom_write_reg(sc, UCHCOM_REG_STAT1, val, UCHCOM_REG_STAT1,
     496             :             val);
     497             : }
     498             : 
     499             : usbd_status
     500           0 : uchcom_set_dtrrts_20(struct uchcom_softc *sc, uint8_t val)
     501             : {
     502           0 :         return uchcom_generic_control_out(sc, UCHCOM_REQ_SET_DTRRTS, val, 0);
     503             : }
     504             : 
     505             : 
     506             : /* ----------------------------------------------------------------------
     507             :  * middle layer
     508             :  */
     509             : 
     510             : int
     511           0 : uchcom_update_version(struct uchcom_softc *sc)
     512             : {
     513             :         usbd_status err;
     514             : 
     515           0 :         err = uchcom_get_version(sc, &sc->sc_version);
     516           0 :         if (err) {
     517           0 :                 printf("%s: cannot get version: %s\n",
     518           0 :                        sc->sc_dev.dv_xname, usbd_errstr(err));
     519           0 :                 return EIO;
     520             :         }
     521             : 
     522           0 :         return 0;
     523           0 : }
     524             : 
     525             : void
     526           0 : uchcom_convert_status(struct uchcom_softc *sc, uint8_t cur)
     527             : {
     528           0 :         sc->sc_dtr = !(cur & UCHCOM_DTR_MASK);
     529           0 :         sc->sc_rts = !(cur & UCHCOM_RTS_MASK);
     530             : 
     531           0 :         cur = ~cur & 0x0F;
     532           0 :         sc->sc_msr = (cur << 4) | ((sc->sc_msr >> 4) ^ cur);
     533           0 : }
     534             : 
     535             : int
     536           0 : uchcom_update_status(struct uchcom_softc *sc)
     537             : {
     538             :         usbd_status err;
     539           0 :         uint8_t cur;
     540             : 
     541           0 :         err = uchcom_read_status(sc, &cur);
     542           0 :         if (err) {
     543           0 :                 printf("%s: cannot update status: %s\n",
     544           0 :                        sc->sc_dev.dv_xname, usbd_errstr(err));
     545           0 :                 return EIO;
     546             :         }
     547           0 :         uchcom_convert_status(sc, cur);
     548             : 
     549           0 :         return 0;
     550           0 : }
     551             : 
     552             : 
     553             : int
     554           0 : uchcom_set_dtrrts(struct uchcom_softc *sc, int dtr, int rts)
     555             : {
     556             :         usbd_status err;
     557             :         uint8_t val = 0;
     558             : 
     559           0 :         if (dtr) val |= UCHCOM_DTR_MASK;
     560           0 :         if (rts) val |= UCHCOM_RTS_MASK;
     561             : 
     562           0 :         if (sc->sc_version < UCHCOM_VER_20)
     563           0 :                 err = uchcom_set_dtrrts_10(sc, ~val);
     564             :         else
     565           0 :                 err = uchcom_set_dtrrts_20(sc, ~val);
     566             : 
     567           0 :         if (err) {
     568           0 :                 printf("%s: cannot set DTR/RTS: %s\n",
     569           0 :                        sc->sc_dev.dv_xname, usbd_errstr(err));
     570           0 :                 return EIO;
     571             :         }
     572             : 
     573           0 :         return 0;
     574           0 : }
     575             : 
     576             : int
     577           0 : uchcom_set_break(struct uchcom_softc *sc, int onoff)
     578             : {
     579             :         usbd_status err;
     580           0 :         uint8_t brk1, brk2;
     581             : 
     582           0 :         err = uchcom_read_reg(sc, UCHCOM_REG_BREAK1, &brk1, UCHCOM_REG_BREAK2,
     583             :             &brk2);
     584           0 :         if (err)
     585           0 :                 return EIO;
     586           0 :         if (onoff) {
     587             :                 /* on - clear bits */
     588           0 :                 brk1 &= ~UCHCOM_BRK1_MASK;
     589           0 :                 brk2 &= ~UCHCOM_BRK2_MASK;
     590           0 :         } else {
     591             :                 /* off - set bits */
     592           0 :                 brk1 |= UCHCOM_BRK1_MASK;
     593           0 :                 brk2 |= UCHCOM_BRK2_MASK;
     594             :         }
     595           0 :         err = uchcom_write_reg(sc, UCHCOM_REG_BREAK1, brk1, UCHCOM_REG_BREAK2,
     596           0 :             brk2);
     597           0 :         if (err)
     598           0 :                 return EIO;
     599             : 
     600           0 :         return 0;
     601           0 : }
     602             : 
     603             : int
     604           0 : uchcom_calc_divider_settings(struct uchcom_divider *dp, uint32_t rate)
     605             : {
     606             :         int i;
     607             :         const struct uchcom_divider_record *rp;
     608             :         uint32_t div, rem, mod;
     609             : 
     610             :         /* find record */
     611           0 :         for (i=0; i<nitems(dividers); i++) {
     612           0 :                 if (dividers[i].dvr_high >= rate &&
     613           0 :                     dividers[i].dvr_low <= rate) {
     614             :                         rp = &dividers[i];
     615             :                         goto found;
     616             :                 }
     617             :         }
     618           0 :         return -1;
     619             : 
     620             : found:
     621           0 :         dp->dv_prescaler = rp->dvr_divider.dv_prescaler;
     622           0 :         if (rp->dvr_base_clock == UCHCOM_BASE_UNKNOWN)
     623           0 :                 dp->dv_div = rp->dvr_divider.dv_div;
     624             :         else {
     625           0 :                 div = rp->dvr_base_clock / rate;
     626           0 :                 rem = rp->dvr_base_clock % rate;
     627           0 :                 if (div==0 || div>=0xFF)
     628           0 :                         return -1;
     629           0 :                 if ((rem<<1) >= rate)
     630           0 :                         div += 1;
     631           0 :                 dp->dv_div = (uint8_t)-div;
     632             :         }
     633             : 
     634           0 :         mod = UCHCOM_BPS_MOD_BASE/rate + UCHCOM_BPS_MOD_BASE_OFS;
     635           0 :         mod = mod + mod/2;
     636             : 
     637           0 :         dp->dv_mod = mod / 0x100;
     638             : 
     639           0 :         return 0;
     640           0 : }
     641             : 
     642             : int
     643           0 : uchcom_set_dte_rate(struct uchcom_softc *sc, uint32_t rate)
     644             : {
     645             :         usbd_status err;
     646           0 :         struct uchcom_divider dv;
     647             : 
     648           0 :         if (uchcom_calc_divider_settings(&dv, rate))
     649           0 :                 return EINVAL;
     650             : 
     651           0 :         if ((err = uchcom_write_reg(sc,
     652           0 :                              UCHCOM_REG_BPS_PRE, dv.dv_prescaler,
     653           0 :                              UCHCOM_REG_BPS_DIV, dv.dv_div)) ||
     654           0 :             (err = uchcom_write_reg(sc,
     655           0 :                              UCHCOM_REG_BPS_MOD, dv.dv_mod,
     656             :                              UCHCOM_REG_BPS_PAD, 0))) {
     657           0 :                 printf("%s: cannot set DTE rate: %s\n",
     658           0 :                        sc->sc_dev.dv_xname, usbd_errstr(err));
     659           0 :                 return EIO;
     660             :         }
     661             : 
     662           0 :         return 0;
     663           0 : }
     664             : 
     665             : int
     666           0 : uchcom_set_line_control(struct uchcom_softc *sc, tcflag_t cflag)
     667             : {
     668             :         /*
     669             :          * XXX: it is difficult to handle the line control appropriately:
     670             :          *   work as chip default - CS8, no parity, !CSTOPB
     671             :          *   other modes are not supported.
     672             :          */
     673             : 
     674           0 :         switch (ISSET(cflag, CSIZE)) {
     675             :         case CS5:
     676             :         case CS6:
     677             :         case CS7:
     678           0 :                 return EINVAL;
     679             :         case CS8:
     680             :                 break;
     681             :         }
     682             : 
     683           0 :         if (ISSET(cflag, PARENB) || ISSET(cflag, CSTOPB))
     684           0 :                 return EINVAL;
     685             : 
     686           0 :         return 0;
     687           0 : }
     688             : 
     689             : int
     690           0 : uchcom_clear_chip(struct uchcom_softc *sc)
     691             : {
     692             :         usbd_status err;
     693             : 
     694             :         DPRINTF(("%s: clear\n", sc->sc_dev.dv_xname));
     695           0 :         err = uchcom_generic_control_out(sc, UCHCOM_REQ_RESET, 0, 0);
     696           0 :         if (err) {
     697           0 :                 printf("%s: cannot clear: %s\n",
     698           0 :                        sc->sc_dev.dv_xname, usbd_errstr(err));
     699           0 :                 return EIO;
     700             :         }
     701             : 
     702           0 :         return 0;
     703           0 : }
     704             : 
     705             : int
     706           0 : uchcom_reset_chip(struct uchcom_softc *sc)
     707             : {
     708             :         usbd_status err;
     709             : 
     710             :         DPRINTF(("%s: reset\n", sc->sc_dev.dv_xname));
     711             : 
     712           0 :         err = uchcom_generic_control_out(sc, UCHCOM_REQ_RESET,
     713             :                                          UCHCOM_RESET_VALUE,
     714             :                                          UCHCOM_RESET_INDEX);
     715           0 :         if (err)
     716             :                 goto failed;
     717             : 
     718           0 :         return 0;
     719             : 
     720             : failed:
     721           0 :         printf("%s: cannot reset: %s\n",
     722           0 :                sc->sc_dev.dv_xname, usbd_errstr(err));
     723           0 :         return EIO;
     724           0 : }
     725             : 
     726             : int
     727           0 : uchcom_setup_comm(struct uchcom_softc *sc)
     728             : {
     729             :         int ret;
     730             : 
     731           0 :         ret = uchcom_update_version(sc);
     732           0 :         if (ret)
     733           0 :                 return ret;
     734             : 
     735           0 :         ret = uchcom_clear_chip(sc);
     736           0 :         if (ret)
     737           0 :                 return ret;
     738             : 
     739           0 :         ret = uchcom_set_dte_rate(sc, TTYDEF_SPEED);
     740           0 :         if (ret)
     741           0 :                 return ret;
     742             : 
     743           0 :         ret = uchcom_set_line_control(sc, CS8);
     744           0 :         if (ret)
     745           0 :                 return ret;
     746             : 
     747           0 :         ret = uchcom_update_status(sc);
     748           0 :         if (ret)
     749           0 :                 return ret;
     750             : 
     751           0 :         ret = uchcom_reset_chip(sc);
     752           0 :         if (ret)
     753           0 :                 return ret;
     754             : 
     755           0 :         ret = uchcom_set_dte_rate(sc, TTYDEF_SPEED); /* XXX */
     756           0 :         if (ret)
     757           0 :                 return ret;
     758             : 
     759           0 :         sc->sc_dtr = sc->sc_rts = 1;
     760           0 :         ret = uchcom_set_dtrrts(sc, sc->sc_dtr, sc->sc_rts);
     761           0 :         if (ret)
     762           0 :                 return ret;
     763             : 
     764           0 :         return 0;
     765           0 : }
     766             : 
     767             : int
     768           0 : uchcom_setup_intr_pipe(struct uchcom_softc *sc)
     769             : {
     770             :         usbd_status err;
     771             : 
     772           0 :         if (sc->sc_intr_endpoint != -1 && sc->sc_intr_pipe == NULL) {
     773           0 :                 sc->sc_intr_buf = malloc(sc->sc_isize, M_USBDEV, M_WAITOK);
     774           0 :                 err = usbd_open_pipe_intr(sc->sc_iface,
     775           0 :                                           sc->sc_intr_endpoint,
     776             :                                           USBD_SHORT_XFER_OK,
     777           0 :                                           &sc->sc_intr_pipe, sc,
     778             :                                           sc->sc_intr_buf,
     779           0 :                                           sc->sc_isize,
     780             :                                           uchcom_intr, USBD_DEFAULT_INTERVAL);
     781           0 :                 if (err) {
     782           0 :                         printf("%s: cannot open interrupt pipe: %s\n",
     783           0 :                                sc->sc_dev.dv_xname,
     784           0 :                                usbd_errstr(err));
     785           0 :                         return EIO;
     786             :                 }
     787             :         }
     788           0 :         return 0;
     789           0 : }
     790             : 
     791             : void
     792           0 : uchcom_close_intr_pipe(struct uchcom_softc *sc)
     793             : {
     794             :         usbd_status err;
     795             : 
     796           0 :         if (sc->sc_intr_pipe != NULL) {
     797           0 :                 usbd_abort_pipe(sc->sc_intr_pipe);
     798           0 :                 err = usbd_close_pipe(sc->sc_intr_pipe);
     799           0 :                 if (err)
     800           0 :                         printf("%s: close interrupt pipe failed: %s\n",
     801           0 :                                sc->sc_dev.dv_xname, usbd_errstr(err));
     802           0 :                 free(sc->sc_intr_buf, M_USBDEV, sc->sc_isize);
     803           0 :                 sc->sc_intr_pipe = NULL;
     804           0 :         }
     805           0 : }
     806             : 
     807             : 
     808             : /* ----------------------------------------------------------------------
     809             :  * methods for ucom
     810             :  */
     811             : void
     812           0 : uchcom_get_status(void *arg, int portno, u_char *rlsr, u_char *rmsr)
     813             : {
     814           0 :         struct uchcom_softc *sc = arg;
     815             : 
     816           0 :         if (usbd_is_dying(sc->sc_udev))
     817           0 :                 return;
     818             : 
     819           0 :         *rlsr = sc->sc_lsr;
     820           0 :         *rmsr = sc->sc_msr;
     821           0 : }
     822             : 
     823             : void
     824           0 : uchcom_set(void *arg, int portno, int reg, int onoff)
     825             : {
     826           0 :         struct uchcom_softc *sc = arg;
     827             : 
     828           0 :         if (usbd_is_dying(sc->sc_udev))
     829           0 :                 return;
     830             : 
     831           0 :         switch (reg) {
     832             :         case UCOM_SET_DTR:
     833           0 :                 sc->sc_dtr = !!onoff;
     834           0 :                 uchcom_set_dtrrts(sc, sc->sc_dtr, sc->sc_rts);
     835           0 :                 break;
     836             :         case UCOM_SET_RTS:
     837           0 :                 sc->sc_rts = !!onoff;
     838           0 :                 uchcom_set_dtrrts(sc, sc->sc_dtr, sc->sc_rts);
     839           0 :                 break;
     840             :         case UCOM_SET_BREAK:
     841           0 :                 uchcom_set_break(sc, onoff);
     842           0 :                 break;
     843             :         }
     844           0 : }
     845             : 
     846             : int
     847           0 : uchcom_param(void *arg, int portno, struct termios *t)
     848             : {
     849           0 :         struct uchcom_softc *sc = arg;
     850             :         int ret;
     851             : 
     852           0 :         if (usbd_is_dying(sc->sc_udev))
     853           0 :                 return 0;
     854             : 
     855           0 :         ret = uchcom_set_line_control(sc, t->c_cflag);
     856           0 :         if (ret)
     857           0 :                 return ret;
     858             : 
     859           0 :         ret = uchcom_set_dte_rate(sc, t->c_ospeed);
     860           0 :         if (ret)
     861           0 :                 return ret;
     862             : 
     863           0 :         return 0;
     864           0 : }
     865             : 
     866             : int
     867           0 : uchcom_open(void *arg, int portno)
     868             : {
     869             :         int ret;
     870           0 :         struct uchcom_softc *sc = arg;
     871             : 
     872           0 :         if (usbd_is_dying(sc->sc_udev))
     873           0 :                 return EIO;
     874             : 
     875           0 :         ret = uchcom_setup_intr_pipe(sc);
     876           0 :         if (ret)
     877           0 :                 return ret;
     878             : 
     879           0 :         ret = uchcom_setup_comm(sc);
     880           0 :         if (ret)
     881           0 :                 return ret;
     882             : 
     883           0 :         return 0;
     884           0 : }
     885             : 
     886             : void
     887           0 : uchcom_close(void *arg, int portno)
     888             : {
     889           0 :         struct uchcom_softc *sc = arg;
     890             : 
     891           0 :         uchcom_close_intr_pipe(sc);
     892           0 : }
     893             : 
     894             : 
     895             : /* ----------------------------------------------------------------------
     896             :  * callback when the modem status is changed.
     897             :  */
     898             : void
     899           0 : uchcom_intr(struct usbd_xfer *xfer, void *priv, usbd_status status)
     900             : {
     901           0 :         struct uchcom_softc *sc = priv;
     902           0 :         u_char *buf = sc->sc_intr_buf;
     903             : 
     904           0 :         if (usbd_is_dying(sc->sc_udev))
     905           0 :                 return;
     906             : 
     907           0 :         if (status != USBD_NORMAL_COMPLETION) {
     908           0 :                 if (status == USBD_NOT_STARTED || status == USBD_CANCELLED)
     909           0 :                         return;
     910             : 
     911             :                 DPRINTF(("%s: abnormal status: %s\n",
     912             :                          sc->sc_dev.dv_xname, usbd_errstr(status)));
     913           0 :                 usbd_clear_endpoint_stall_async(sc->sc_intr_pipe);
     914           0 :                 return;
     915             :         }
     916             :         DPRINTF(("%s: intr: 0x%02X 0x%02X 0x%02X 0x%02X "
     917             :                  "0x%02X 0x%02X 0x%02X 0x%02X\n",
     918             :                  sc->sc_dev.dv_xname,
     919             :                  (unsigned)buf[0], (unsigned)buf[1],
     920             :                  (unsigned)buf[2], (unsigned)buf[3],
     921             :                  (unsigned)buf[4], (unsigned)buf[5],
     922             :                  (unsigned)buf[6], (unsigned)buf[7]));
     923             : 
     924           0 :         uchcom_convert_status(sc, buf[UCHCOM_INTR_STAT1]);
     925           0 :         ucom_status_change((struct ucom_softc *) sc->sc_subdev);
     926           0 : }

Generated by: LCOV version 1.13