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

          Line data    Source code
       1             : /* $OpenBSD: umcs.c,v 1.6 2017/10/09 08:26:16 mpi Exp $ */
       2             : /* $NetBSD: umcs.c,v 1.8 2014/08/23 21:37:56 martin Exp $ */
       3             : /* $FreeBSD: head/sys/dev/usb/serial/umcs.c 260559 2014-01-12 11:44:28Z hselasky $ */
       4             : 
       5             : /*-
       6             :  * Copyright (c) 2010 Lev Serebryakov <lev@FreeBSD.org>.
       7             :  * All rights reserved.
       8             :  *
       9             :  * Redistribution and use in source and binary forms, with or without
      10             :  * modification, are permitted provided that the following conditions
      11             :  * are met:
      12             :  * 1. Redistributions of source code must retain the above copyright
      13             :  *    notice, this list of conditions and the following disclaimer.
      14             :  * 2. Redistributions in binary form must reproduce the above copyright
      15             :  *    notice, this list of conditions and the following disclaimer in the
      16             :  *    documentation and/or other materials provided with the distribution.
      17             :  *
      18             :  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
      19             :  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
      20             :  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
      21             :  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
      22             :  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
      23             :  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
      24             :  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
      25             :  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
      26             :  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
      27             :  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
      28             :  * SUCH DAMAGE.
      29             :  */
      30             : 
      31             : /*
      32             :  * This driver supports several multiport USB-to-RS232 serial adapters driven
      33             :  * by MosChip mos7820 and mos7840, bridge chips.  The adapters are sold under
      34             :  * many different brand names.
      35             :  *
      36             :  * Datasheets are available at MosChip www site at http://www.moschip.com.
      37             :  * The datasheets don't contain full programming information for the chip.
      38             :  *
      39             :  * It is nornal to have only two enabled ports in devices, based on quad-port
      40             :  * mos7840.
      41             :  */
      42             : 
      43             : #include <sys/param.h>
      44             : #include <sys/systm.h>
      45             : #include <sys/kernel.h>
      46             : #include <sys/malloc.h>
      47             : #include <sys/tty.h>
      48             : #include <sys/device.h>
      49             : #include <sys/task.h>
      50             : 
      51             : #include <dev/usb/usb.h>
      52             : #include <dev/usb/usbdi.h>
      53             : #include <dev/usb/usbdi_util.h>
      54             : #include <dev/usb/usbdevs.h>
      55             : 
      56             : #include <dev/usb/ucomvar.h>
      57             : 
      58             : #include "umcs.h"
      59             : 
      60             : #ifdef UMCS_DEBUG
      61             : #define DPRINTF(x...)   printf(x)
      62             : #else
      63             : #define DPRINTF(x...)
      64             : #endif
      65             : 
      66             : #define DEVNAME(_sc) ((_sc)->sc_dev.dv_xname)
      67             : 
      68             : /*
      69             :  * Two-port devices (both with 7820 chip and 7840 chip configured as two-port)
      70             :  * have ports 0 and 2, with ports 1 and 3 omitted.
      71             :  * So, PHYSICAL port numbers on two-port device will be 0 and 2.
      72             :  *
      73             :  * We use an array of the following struct, indexed by ucom port index,
      74             :  * and include the physical port number in it.
      75             :  */
      76             : struct umcs_port {
      77             :         struct ucom_softc       *ucom;          /* ucom subdevice */
      78             :         unsigned int             pn;            /* physical port number */
      79             :         int                      flags;
      80             : #define UMCS_STATCHG             0x01
      81             : 
      82             :         uint8_t                  lcr;           /* local line control reg. */
      83             :         uint8_t                  mcr;           /* local modem control reg. */
      84             : };
      85             : 
      86             : struct umcs_softc {
      87             :         struct device            sc_dev;
      88             :         struct usbd_device      *sc_udev;       /* the usb device */
      89             :         struct usbd_pipe        *sc_ipipe;      /* interrupt pipe */
      90             :         uint8_t                 *sc_ibuf;       /* buffer for interrupt xfer */
      91             :         unsigned int             sc_isize;      /* size of buffer */
      92             : 
      93             :         struct umcs_port         sc_subdevs[UMCS_MAX_PORTS];
      94             :         uint8_t                  sc_numports;   /* number of ports */
      95             : 
      96             :         int                      sc_init_done;
      97             :         struct task              sc_status_task;
      98             : };
      99             : 
     100             : int     umcs_get_reg(struct umcs_softc *, uint8_t, uint8_t *);
     101             : int     umcs_set_reg(struct umcs_softc *, uint8_t, uint8_t);
     102             : int     umcs_get_uart_reg(struct umcs_softc *, uint8_t, uint8_t, uint8_t *);
     103             : int     umcs_set_uart_reg(struct umcs_softc *, uint8_t, uint8_t, uint8_t);
     104             : int     umcs_calc_baudrate(uint32_t, uint16_t *, uint8_t *);
     105             : int     umcs_set_baudrate(struct umcs_softc *, uint8_t, uint32_t);
     106             : void    umcs_dtr(struct umcs_softc *, int, int);
     107             : void    umcs_rts(struct umcs_softc *, int, int);
     108             : void    umcs_break(struct umcs_softc *, int, int);
     109             : 
     110             : int     umcs_match(struct device *, void *, void *);
     111             : void    umcs_attach(struct device *, struct device *, void *);
     112             : int     umcs_detach(struct device *, int);
     113             : void    umcs_intr(struct usbd_xfer *, void *, usbd_status);
     114             : void    umcs_status_task(void *);
     115             : 
     116             : void    umcs_get_status(void *, int, uint8_t *, uint8_t *);
     117             : void    umcs_set(void *, int, int, int);
     118             : int     umcs_param(void *, int, struct termios *);
     119             : int     umcs_open(void *, int);
     120             : void    umcs_close(void *, int);
     121             : 
     122             : struct ucom_methods umcs_methods = {
     123             :         umcs_get_status,
     124             :         umcs_set,
     125             :         umcs_param,
     126             :         NULL,
     127             :         umcs_open,
     128             :         umcs_close,
     129             :         NULL,
     130             :         NULL,
     131             : };
     132             : 
     133             : const struct usb_devno umcs_devs[] = {
     134             :         { USB_VENDOR_MOSCHIP,           USB_PRODUCT_MOSCHIP_MCS7810 },
     135             :         { USB_VENDOR_MOSCHIP,           USB_PRODUCT_MOSCHIP_MCS7820 },
     136             :         { USB_VENDOR_MOSCHIP,           USB_PRODUCT_MOSCHIP_MCS7840 },
     137             :         { USB_VENDOR_ATEN,              USB_PRODUCT_ATEN_UC2324 }
     138             : };
     139             : 
     140             : struct cfdriver umcs_cd = {
     141             :         NULL, "umcs", DV_DULL
     142             : };
     143             : 
     144             : const struct cfattach umcs_ca = {
     145             :         sizeof(struct umcs_softc), umcs_match, umcs_attach, umcs_detach
     146             : };
     147             : 
     148             : 
     149             : static inline int
     150           0 : umcs_reg_sp(int pn)
     151             : {
     152           0 :         KASSERT(pn >= 0 && pn < 4);
     153           0 :         switch (pn) {
     154             :         default:
     155           0 :         case 0: return UMCS_SP1;
     156           0 :         case 1: return UMCS_SP2;
     157           0 :         case 2: return UMCS_SP3;
     158           0 :         case 3: return UMCS_SP4;
     159             :         }
     160           0 : }
     161             : 
     162             : static inline int
     163           0 : umcs_reg_ctrl(int pn)
     164             : {
     165           0 :         KASSERT(pn >= 0 && pn < 4);
     166           0 :         switch (pn) {
     167             :         default:
     168           0 :         case 0: return UMCS_CTRL1;
     169           0 :         case 1: return UMCS_CTRL2;
     170           0 :         case 2: return UMCS_CTRL3;
     171           0 :         case 3: return UMCS_CTRL4;
     172             :         }
     173           0 : }
     174             : 
     175             : int
     176           0 : umcs_match(struct device *dev, void *match, void *aux)
     177             : {
     178           0 :         struct usb_attach_arg *uaa = aux;
     179             : 
     180           0 :         if (uaa->iface == NULL || uaa->ifaceno != UMCS_IFACE_NO)
     181           0 :                 return (UMATCH_NONE);
     182             : 
     183           0 :         return (usb_lookup(umcs_devs, uaa->vendor, uaa->product) != NULL) ?
     184             :             UMATCH_VENDOR_PRODUCT : UMATCH_NONE;
     185           0 : }
     186             : 
     187             : void
     188           0 : umcs_attach(struct device *parent, struct device *self, void *aux)
     189             : {
     190           0 :         struct umcs_softc *sc = (struct umcs_softc *)self;
     191           0 :         struct usb_attach_arg *uaa = aux;
     192             :         usb_interface_descriptor_t *id;
     193             :         usb_endpoint_descriptor_t *ed;
     194           0 :         struct ucom_attach_args uca;
     195             :         int error, i, intr_addr;
     196           0 :         uint8_t data;
     197             : 
     198           0 :         sc->sc_udev = uaa->device;
     199             : 
     200             :         /*
     201             :          * Get number of ports
     202             :          * Documentation (full datasheet) says, that number of ports is
     203             :          * set as UMCS_MODE_SELECT24S bit in MODE R/Only
     204             :          * register. But vendor driver uses these undocumented
     205             :          * register & bit.
     206             :          *
     207             :          * Experiments show, that MODE register can have `0'
     208             :          * (4 ports) bit on 2-port device, so use vendor driver's way.
     209             :          *
     210             :          * Also, see notes in header file for these constants.
     211             :          */
     212           0 :         if (umcs_get_reg(sc, UMCS_GPIO, &data)) {
     213           0 :                 printf("%s: unable to get number of ports\n", DEVNAME(sc));
     214           0 :                 usbd_deactivate(sc->sc_udev);
     215           0 :                 return;
     216             :         }
     217           0 :         if (data & UMCS_GPIO_4PORTS)
     218           0 :                 sc->sc_numports = 4; /* physical port no are : 0, 1, 2, 3 */
     219           0 :         else if (uaa->product == USB_PRODUCT_MOSCHIP_MCS7810)
     220           0 :                 sc->sc_numports = 1;
     221             :         else
     222           0 :                 sc->sc_numports = 2; /* physical port no are: 0 and 2 */
     223             : 
     224             : #ifdef UMCS_DEBUG
     225             :         if (!umcs_get_reg(sc, UMCS_MODE, &data)) {
     226             :                 printf("%s: On-die confguration: RST: active %s, "
     227             :                     "HRD: %s, PLL: %s, POR: %s, Ports: %s, EEPROM write %s, "
     228             :                     "IrDA is %savailable\n", DEVNAME(sc),
     229             :                     (data & UMCS_MODE_RESET) ? "low" : "high",
     230             :                     (data & UMCS_MODE_SER_PRSNT) ? "yes" : "no",
     231             :                     (data & UMCS_MODE_PLLBYPASS) ? "bypassed" : "avail",
     232             :                     (data & UMCS_MODE_PORBYPASS) ? "bypassed" : "avail",
     233             :                     (data & UMCS_MODE_SELECT24S) ? "2" : "4",
     234             :                     (data & UMCS_MODE_EEPROMWR) ? "enabled" : "disabled",
     235             :                     (data & UMCS_MODE_IRDA) ? "" : "not ");
     236             :         }
     237             : #endif
     238             : 
     239             :         /* Set up the interrupt pipe */
     240           0 :         id = usbd_get_interface_descriptor(uaa->iface);
     241             :         intr_addr = -1;
     242           0 :         for (i = 0 ; i < id->bNumEndpoints ; i++) {
     243           0 :                 ed = usbd_interface2endpoint_descriptor(uaa->iface, i);
     244           0 :                 if (ed == NULL) {
     245           0 :                         printf("%s: no endpoint descriptor found for %d\n",
     246           0 :                             DEVNAME(sc), i);
     247           0 :                         usbd_deactivate(sc->sc_udev);
     248           0 :                         return;
     249             :                 }
     250             : 
     251           0 :                 if (UE_GET_DIR(ed->bEndpointAddress) != UE_DIR_IN ||
     252           0 :                     UE_GET_XFERTYPE(ed->bmAttributes) != UE_INTERRUPT)
     253             :                         continue;
     254           0 :                 sc->sc_isize = UGETW(ed->wMaxPacketSize);
     255           0 :                 intr_addr = ed->bEndpointAddress;
     256           0 :                 break;
     257             :         }
     258           0 :         if (intr_addr < 0) {
     259           0 :                 printf("%s: missing endpoint\n", DEVNAME(sc));
     260           0 :                 usbd_deactivate(sc->sc_udev);
     261           0 :                 return;
     262             :         }
     263           0 :         sc->sc_ibuf = malloc(sc->sc_isize, M_USBDEV, M_WAITOK);
     264             : 
     265           0 :         error = usbd_open_pipe_intr(uaa->iface, intr_addr,
     266           0 :                     USBD_SHORT_XFER_OK, &sc->sc_ipipe, sc, sc->sc_ibuf,
     267           0 :                     sc->sc_isize, umcs_intr, 100 /* XXX */);
     268           0 :         if (error) {
     269           0 :                 printf("%s: cannot open interrupt pipe (addr %d)\n",
     270           0 :                     DEVNAME(sc), intr_addr);
     271           0 :                 usbd_deactivate(sc->sc_udev);
     272           0 :                 return;
     273             :         }
     274             : 
     275           0 :         memset(&uca, 0, sizeof uca);
     276           0 :         uca.ibufsize = 256;
     277           0 :         uca.obufsize = 256;
     278           0 :         uca.ibufsizepad = 256;
     279           0 :         uca.opkthdrlen = 0;
     280           0 :         uca.device = sc->sc_udev;
     281           0 :         uca.iface = uaa->iface;
     282           0 :         uca.methods = &umcs_methods;
     283           0 :         uca.arg = sc;
     284             : 
     285           0 :         for (i = 0; i < sc->sc_numports; i++) {
     286           0 :                 uca.bulkin = uca.bulkout = -1;
     287             : 
     288             :                 /*
     289             :                  * On 4 port cards, endpoints are 0/1, 2/3, 4/5, and 6/7.
     290             :                  * On 2 port cards, they are 0/1 and 4/5.
     291             :                  * On single port, just 0/1 will be used.
     292             :                  */
     293           0 :                 int pn = i * (sc->sc_numports == 2 ? 2 : 1);
     294             : 
     295           0 :                 ed = usbd_interface2endpoint_descriptor(uaa->iface, pn*2);
     296           0 :                 if (ed == NULL) {
     297           0 :                         printf("%s: no bulk in endpoint found for %d\n",
     298           0 :                             DEVNAME(sc), i);
     299           0 :                         usbd_deactivate(sc->sc_udev);
     300           0 :                         return;
     301             :                 }
     302           0 :                 uca.bulkin = ed->bEndpointAddress;
     303             : 
     304           0 :                 ed = usbd_interface2endpoint_descriptor(uaa->iface, pn*2+1);
     305           0 :                 if (ed == NULL) {
     306           0 :                         printf("%s: no bulk out endpoint found for %d\n",
     307           0 :                             DEVNAME(sc), i);
     308           0 :                         usbd_deactivate(sc->sc_udev);
     309           0 :                         return;
     310             :                 }
     311           0 :                 uca.bulkout = ed->bEndpointAddress;
     312           0 :                 uca.portno = i;
     313             : 
     314           0 :                 sc->sc_subdevs[i].pn = pn;
     315           0 :                 sc->sc_subdevs[i].ucom = (struct ucom_softc *)
     316           0 :                     config_found_sm(self, &uca, ucomprint, ucomsubmatch);
     317           0 :         }
     318             : 
     319           0 :         task_set(&sc->sc_status_task, umcs_status_task, sc);
     320           0 : }
     321             : 
     322             : int
     323           0 : umcs_get_reg(struct umcs_softc *sc, uint8_t reg, uint8_t *data)
     324             : {
     325           0 :         usb_device_request_t req;
     326             : 
     327           0 :         req.bmRequestType = UT_READ_VENDOR_DEVICE;
     328           0 :         req.bRequest = UMCS_READ;
     329           0 :         USETW(req.wValue, 0);
     330           0 :         USETW(req.wIndex, reg);
     331           0 :         USETW(req.wLength, UMCS_READ_LENGTH);
     332             : 
     333           0 :         if (usbd_do_request(sc->sc_udev, &req, data))
     334           0 :                 return (EIO);
     335             : 
     336           0 :         return (0);
     337           0 : }
     338             : 
     339             : int
     340           0 : umcs_set_reg(struct umcs_softc *sc, uint8_t reg, uint8_t data)
     341             : {
     342           0 :         usb_device_request_t req;
     343             : 
     344           0 :         req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
     345           0 :         req.bRequest = UMCS_WRITE;
     346           0 :         USETW(req.wValue, data);
     347           0 :         USETW(req.wIndex, reg);
     348           0 :         USETW(req.wLength, 0);
     349             : 
     350           0 :         if (usbd_do_request(sc->sc_udev, &req, NULL))
     351           0 :                 return (EIO);
     352             : 
     353           0 :         return (0);
     354           0 : }
     355             : 
     356             : int
     357           0 : umcs_get_uart_reg(struct umcs_softc *sc, uint8_t portno, uint8_t reg,
     358             :     uint8_t *data)
     359             : {
     360           0 :         usb_device_request_t req;
     361             :         uint16_t wVal;
     362             : 
     363           0 :         wVal = ((uint16_t)(sc->sc_subdevs[portno].pn + 1)) << 8;
     364             : 
     365           0 :         req.bmRequestType = UT_READ_VENDOR_DEVICE;
     366           0 :         req.bRequest = UMCS_READ;
     367           0 :         USETW(req.wValue, wVal);
     368           0 :         USETW(req.wIndex, reg);
     369           0 :         USETW(req.wLength, UMCS_READ_LENGTH);
     370             : 
     371           0 :         if (usbd_do_request(sc->sc_udev, &req, data))
     372           0 :                 return (EIO);
     373             : 
     374           0 :         return (0);
     375           0 : }
     376             : 
     377             : int
     378           0 : umcs_set_uart_reg(struct umcs_softc *sc, uint8_t portno, uint8_t reg,
     379             :     uint8_t data)
     380             : {
     381           0 :         usb_device_request_t req;
     382             :         uint16_t wVal;
     383             : 
     384           0 :         wVal = ((uint16_t)(sc->sc_subdevs[portno].pn + 1)) << 8 | data;
     385             : 
     386           0 :         req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
     387           0 :         req.bRequest = UMCS_WRITE;
     388           0 :         USETW(req.wValue, wVal);
     389           0 :         USETW(req.wIndex, reg);
     390           0 :         USETW(req.wLength, 0);
     391             : 
     392           0 :         if (usbd_do_request(sc->sc_udev, &req, NULL))
     393           0 :                 return (EIO);
     394             : 
     395           0 :         return (0);
     396           0 : }
     397             : 
     398             : int
     399           0 : umcs_set_baudrate(struct umcs_softc *sc, uint8_t portno, uint32_t rate)
     400             : {
     401           0 :         int pn = sc->sc_subdevs[portno].pn;
     402           0 :         int spreg = umcs_reg_sp(pn);
     403           0 :         uint8_t lcr = sc->sc_subdevs[portno].lcr;
     404           0 :         uint8_t clk, data;
     405           0 :         uint16_t div;
     406             : 
     407           0 :         if (umcs_calc_baudrate(rate, &div, &clk))
     408           0 :                 return (EINVAL);
     409             : 
     410             :         DPRINTF("%s: portno %d set speed: %d (%02x/%d)\n", DEVNAME(sc), portno,
     411             :             rate, clk, div);
     412             : 
     413             :         /* Set clock source for standard BAUD frequences */
     414           0 :         if (umcs_get_reg(sc, spreg, &data))
     415           0 :                 return (EIO);
     416           0 :         data &= UMCS_SPx_CLK_MASK;
     417           0 :         if (umcs_set_reg(sc, spreg, data | clk))
     418           0 :                 return (EIO);
     419             : 
     420             :         /* Set divider */
     421           0 :         lcr |= UMCS_LCR_DIVISORS;
     422           0 :         if (umcs_set_uart_reg(sc, portno, UMCS_REG_LCR, lcr))
     423           0 :                 return (EIO);
     424           0 :         sc->sc_subdevs[portno].lcr = lcr;
     425             : 
     426           0 :         if (umcs_set_uart_reg(sc, portno, UMCS_REG_DLL, div & 0xff) ||
     427           0 :             umcs_set_uart_reg(sc, portno, UMCS_REG_DLM, (div >> 8) & 0xff))
     428           0 :                 return (EIO);
     429             : 
     430             :         /* Turn off access to DLL/DLM registers of UART */
     431           0 :         lcr &= ~UMCS_LCR_DIVISORS;
     432           0 :         if (umcs_set_uart_reg(sc, portno, UMCS_REG_LCR, lcr))
     433           0 :                 return (EIO);
     434           0 :         sc->sc_subdevs[portno].lcr = lcr;
     435             : 
     436           0 :         return (0);
     437           0 : }
     438             : 
     439             : /* Maximum speeds for standard frequences, when PLL is not used */
     440             : static const uint32_t umcs_baudrate_divisors[] = {
     441             :     0, 115200, 230400, 403200, 460800, 806400, 921600, 1572864, 3145728,
     442             : };
     443             : 
     444             : int
     445           0 : umcs_calc_baudrate(uint32_t rate, uint16_t *divisor, uint8_t *clk)
     446             : {
     447             :         const uint8_t divisors_len = nitems(umcs_baudrate_divisors);
     448             :         uint8_t i = 0;
     449             : 
     450           0 :         if (rate > umcs_baudrate_divisors[divisors_len - 1])
     451           0 :                 return (-1);
     452             : 
     453           0 :         for (i = 0; i < divisors_len - 1; i++) {
     454           0 :              if (rate > umcs_baudrate_divisors[i] &&
     455           0 :                  rate <= umcs_baudrate_divisors[i + 1])
     456             :                      break;
     457             :         }
     458             : 
     459           0 :         *divisor = umcs_baudrate_divisors[i + 1] / rate;
     460             :         /* 0x00 .. 0x70 */
     461           0 :         *clk = i << UMCS_SPx_CLK_SHIFT;
     462             : 
     463           0 :         return (0);
     464           0 : }
     465             : 
     466             : int
     467           0 : umcs_detach(struct device *self, int flags)
     468             : {
     469           0 :         struct umcs_softc *sc = (struct umcs_softc *)self;
     470             : 
     471           0 :         task_del(systq, &sc->sc_status_task);
     472             : 
     473           0 :         if (sc->sc_ipipe != NULL) {
     474           0 :                 usbd_abort_pipe(sc->sc_ipipe);
     475           0 :                 usbd_close_pipe(sc->sc_ipipe);
     476           0 :                 sc->sc_ipipe = NULL;
     477           0 :         }
     478             : 
     479           0 :         if (sc->sc_ibuf != NULL) {
     480           0 :                 free(sc->sc_ibuf, M_USBDEV, sc->sc_isize);
     481           0 :                 sc->sc_ibuf = NULL;
     482           0 :         }
     483             : 
     484           0 :         return (config_detach_children(self, flags));
     485             : }
     486             : 
     487             : void
     488           0 : umcs_get_status(void *self, int portno, uint8_t *lsr, uint8_t *msr)
     489             : {
     490           0 :         struct umcs_softc *sc = self;
     491           0 :         uint8_t hw_lsr = 0;     /* local line status register */
     492           0 :         uint8_t hw_msr = 0;     /* local modem status register */
     493             : 
     494           0 :         if (usbd_is_dying(sc->sc_udev))
     495           0 :                 return;
     496             : 
     497             :         /* Read LSR & MSR */
     498           0 :         if (umcs_get_uart_reg(sc, portno, UMCS_REG_LSR, &hw_lsr) ||
     499           0 :             umcs_get_uart_reg(sc, portno, UMCS_REG_MSR, &hw_msr))
     500           0 :                 return;
     501             : 
     502           0 :         *lsr = hw_lsr;
     503           0 :         *msr = hw_msr;
     504           0 : }
     505             : 
     506             : void
     507           0 : umcs_set(void *self, int portno, int reg, int onoff)
     508             : {
     509           0 :         struct umcs_softc *sc = self;
     510             : 
     511           0 :         if (usbd_is_dying(sc->sc_udev))
     512           0 :                 return;
     513             : 
     514           0 :         switch (reg) {
     515             :         case UCOM_SET_DTR:
     516           0 :                 umcs_dtr(sc, portno, onoff);
     517           0 :                 break;
     518             :         case UCOM_SET_RTS:
     519           0 :                 umcs_rts(sc, portno, onoff);
     520           0 :                 break;
     521             :         case UCOM_SET_BREAK:
     522           0 :                 umcs_break(sc, portno, onoff);
     523           0 :                 break;
     524             :         default:
     525             :                 break;
     526             :         }
     527           0 : }
     528             : 
     529             : int
     530           0 : umcs_param(void *self, int portno, struct termios *t)
     531             : {
     532           0 :         struct umcs_softc *sc = self;
     533           0 :         uint8_t lcr = sc->sc_subdevs[portno].lcr;
     534           0 :         uint8_t mcr = sc->sc_subdevs[portno].mcr;
     535             :         int error = 0;
     536             : 
     537           0 :         if (t->c_cflag & CSTOPB)
     538           0 :                 lcr |= UMCS_LCR_STOPB2;
     539             :         else
     540             :                 lcr |= UMCS_LCR_STOPB1;
     541             : 
     542           0 :         lcr &= ~UMCS_LCR_PARITYMASK;
     543           0 :         if (t->c_cflag & PARENB) {
     544           0 :                 lcr |= UMCS_LCR_PARITYON;
     545           0 :                 if (t->c_cflag & PARODD) {
     546             :                         lcr = UMCS_LCR_PARITYODD;
     547           0 :                 } else {
     548             :                         lcr = UMCS_LCR_PARITYEVEN;
     549             :                 }
     550             :         } else {
     551           0 :                 lcr &= ~UMCS_LCR_PARITYON;
     552             :         }
     553             : 
     554           0 :         lcr &= ~UMCS_LCR_DATALENMASK;
     555           0 :         switch (t->c_cflag & CSIZE) {
     556             :         case CS5:
     557           0 :                 lcr |= UMCS_LCR_DATALEN5;
     558           0 :                 break;
     559             :         case CS6:
     560           0 :                 lcr |= UMCS_LCR_DATALEN6;
     561           0 :                 break;
     562             :         case CS7:
     563           0 :                 lcr |= UMCS_LCR_DATALEN7;
     564           0 :                 break;
     565             :         case CS8:
     566           0 :                 lcr |= UMCS_LCR_DATALEN8;
     567           0 :                 break;
     568             :         }
     569             : 
     570           0 :         if (t->c_cflag & CRTSCTS)
     571           0 :                 mcr |= UMCS_MCR_CTSRTS;
     572             :         else
     573           0 :                 mcr &= ~UMCS_MCR_CTSRTS;
     574             : 
     575           0 :         if (t->c_cflag & CLOCAL)
     576           0 :                 mcr &= ~UMCS_MCR_DTRDSR;
     577             :         else
     578           0 :                 mcr |= UMCS_MCR_DTRDSR;
     579             : 
     580           0 :         if (umcs_set_uart_reg(sc, portno, UMCS_REG_LCR, lcr))
     581           0 :                 return (EIO);
     582           0 :         sc->sc_subdevs[portno].lcr = lcr;
     583             : 
     584           0 :         if (umcs_set_uart_reg(sc, portno, UMCS_REG_MCR, mcr))
     585           0 :                 return (EIO);
     586           0 :         sc->sc_subdevs[portno].mcr = mcr;
     587             : 
     588           0 :         error = umcs_set_baudrate(sc, portno, t->c_ospeed);
     589             : 
     590           0 :         return (error);
     591           0 : }
     592             : 
     593             : void
     594           0 : umcs_dtr(struct umcs_softc *sc, int portno, int onoff)
     595             : {
     596           0 :         uint8_t mcr = sc->sc_subdevs[portno].mcr;
     597             : 
     598           0 :         if (onoff)
     599           0 :                 mcr |= UMCS_MCR_DTR;
     600             :         else
     601           0 :                 mcr &= ~UMCS_MCR_DTR;
     602             : 
     603           0 :         if (umcs_set_uart_reg(sc, portno, UMCS_REG_MCR, mcr))
     604           0 :                 return;
     605           0 :         sc->sc_subdevs[portno].mcr = mcr;
     606           0 : }
     607             : 
     608             : void
     609           0 : umcs_rts(struct umcs_softc *sc, int portno, int onoff)
     610             : {
     611           0 :         uint8_t mcr = sc->sc_subdevs[portno].mcr;
     612             : 
     613           0 :         if (onoff)
     614           0 :                 mcr |= UMCS_MCR_RTS;
     615             :         else
     616           0 :                 mcr &= ~UMCS_MCR_RTS;
     617             : 
     618           0 :         if (umcs_set_uart_reg(sc, portno, UMCS_REG_MCR, mcr))
     619           0 :                 return;
     620           0 :         sc->sc_subdevs[portno].mcr = mcr;
     621           0 : }
     622             : 
     623             : void
     624           0 : umcs_break(struct umcs_softc *sc, int portno, int onoff)
     625             : {
     626           0 :         uint8_t lcr = sc->sc_subdevs[portno].lcr;
     627             : 
     628           0 :         if (onoff)
     629           0 :                 lcr |= UMCS_LCR_BREAK;
     630             :         else
     631           0 :                 lcr &= ~UMCS_LCR_BREAK;
     632             : 
     633           0 :         if (umcs_set_uart_reg(sc, portno, UMCS_REG_LCR, lcr))
     634           0 :                 return;
     635           0 :         sc->sc_subdevs[portno].lcr = lcr;
     636           0 : }
     637             : 
     638             : int
     639           0 : umcs_open(void *self, int portno)
     640             : {
     641           0 :         struct umcs_softc *sc = self;
     642           0 :         int pn = sc->sc_subdevs[portno].pn;
     643           0 :         int spreg = umcs_reg_sp(pn);
     644           0 :         int ctrlreg = umcs_reg_ctrl(pn);
     645           0 :         uint8_t mcr = sc->sc_subdevs[portno].mcr;
     646           0 :         uint8_t lcr = sc->sc_subdevs[portno].lcr;
     647           0 :         uint8_t data;
     648             :         int error;
     649             : 
     650           0 :         if (usbd_is_dying(sc->sc_udev))
     651           0 :                 return (EIO);
     652             : 
     653             :         /* If it very first open, finish global configuration */
     654           0 :         if (!sc->sc_init_done) {
     655           0 :                 if (umcs_get_reg(sc, UMCS_CTRL1, &data) ||
     656           0 :                     umcs_set_reg(sc, UMCS_CTRL1, data | UMCS_CTRL1_DRIVER_DONE))
     657           0 :                         return (EIO);
     658           0 :                 sc->sc_init_done = 1;
     659           0 :         }
     660             : 
     661             :         /* Toggle reset bit on-off */
     662           0 :         if (umcs_get_reg(sc, spreg, &data) ||
     663           0 :             umcs_set_reg(sc, spreg, data | UMCS_SPx_UART_RESET) ||
     664           0 :             umcs_set_reg(sc, spreg, data & ~UMCS_SPx_UART_RESET))
     665           0 :                 return (EIO);
     666             : 
     667             :         /* Set RS-232 mode */
     668           0 :         if (umcs_set_uart_reg(sc, portno, UMCS_REG_SCRATCHPAD,
     669             :             UMCS_SCRATCHPAD_RS232))
     670           0 :                 return (EIO);
     671             : 
     672             :         /* Disable RX on time of initialization */
     673           0 :         if (umcs_get_reg(sc, ctrlreg, &data) ||
     674           0 :             umcs_set_reg(sc, ctrlreg, data | UMCS_CTRL_RX_DISABLE))
     675           0 :                 return (EIO);
     676             : 
     677             :         /* Disable all interrupts */
     678           0 :         if (umcs_set_uart_reg(sc, portno, UMCS_REG_IER, 0))
     679           0 :                 return (EIO);
     680             : 
     681             :         /* Reset FIFO -- documented */
     682           0 :         if (umcs_set_uart_reg(sc, portno, UMCS_REG_FCR, 0) ||
     683           0 :             umcs_set_uart_reg(sc, portno, UMCS_REG_FCR,
     684             :             UMCS_FCR_ENABLE | UMCS_FCR_FLUSHRHR |
     685             :             UMCS_FCR_FLUSHTHR | UMCS_FCR_RTL_1_14))
     686           0 :                 return (EIO);
     687             : 
     688             :         /* Set 8 bit, no parity, 1 stop bit -- documented */
     689             :         lcr = UMCS_LCR_DATALEN8 | UMCS_LCR_STOPB1;
     690           0 :         if (umcs_set_uart_reg(sc, portno, UMCS_REG_LCR, lcr))
     691           0 :                 return (EIO);
     692           0 :         sc->sc_subdevs[portno].lcr = lcr;
     693             : 
     694             :         /*
     695             :          * Enable DTR/RTS on modem control, enable modem interrupts --
     696             :          * documented
     697             :          */
     698             :         mcr = UMCS_MCR_DTR | UMCS_MCR_RTS | UMCS_MCR_IE;
     699           0 :         if (umcs_set_uart_reg(sc, portno, UMCS_REG_MCR, mcr))
     700           0 :                 return (EIO);
     701           0 :         sc->sc_subdevs[portno].mcr = mcr;
     702             : 
     703             :         /* Clearing Bulkin and Bulkout FIFO */
     704           0 :         if (umcs_get_reg(sc, spreg, &data))
     705           0 :                 return (EIO);
     706           0 :         data |= UMCS_SPx_RESET_OUT_FIFO|UMCS_SPx_RESET_IN_FIFO;
     707           0 :         if (umcs_set_reg(sc, spreg, data))
     708           0 :                 return (EIO);
     709           0 :         data &= ~(UMCS_SPx_RESET_OUT_FIFO|UMCS_SPx_RESET_IN_FIFO);
     710           0 :         if (umcs_set_reg(sc, spreg, data))
     711           0 :                 return (EIO);
     712             : 
     713             :         /* Set speed 9600 */
     714           0 :         if ((error = umcs_set_baudrate(sc, portno, 9600)) != 0)
     715           0 :                 return (error);
     716             : 
     717             :         /* Finally enable all interrupts -- documented */
     718             :         /*
     719             :          * Copied from vendor driver, I don't know why we should read LCR
     720             :          * here
     721             :          */
     722           0 :         if (umcs_get_uart_reg(sc, portno, UMCS_REG_LCR,
     723             :             &sc->sc_subdevs[portno].lcr))
     724           0 :                 return (EIO);
     725           0 :         if (umcs_set_uart_reg(sc, portno, UMCS_REG_IER,
     726             :             UMCS_IER_RXSTAT | UMCS_IER_MODEM))
     727           0 :                 return (EIO);
     728             : 
     729             :         /* Enable RX */
     730           0 :         if (umcs_get_reg(sc, ctrlreg, &data) ||
     731           0 :             umcs_set_reg(sc, ctrlreg, data & ~UMCS_CTRL_RX_DISABLE))
     732           0 :                 return (EIO);
     733             : 
     734           0 :         return (0);
     735           0 : }
     736             : 
     737             : void
     738           0 : umcs_close(void *self, int portno)
     739             : {
     740           0 :         struct umcs_softc *sc = self;
     741           0 :         int pn = sc->sc_subdevs[portno].pn;
     742           0 :         int ctrlreg = umcs_reg_ctrl(pn);
     743           0 :         uint8_t data;
     744             : 
     745           0 :         if (usbd_is_dying(sc->sc_udev))
     746           0 :                 return;
     747             : 
     748           0 :         umcs_set_uart_reg(sc, portno, UMCS_REG_MCR, 0);
     749           0 :         umcs_set_uart_reg(sc, portno, UMCS_REG_IER, 0);
     750             : 
     751             :         /* Disable RX */
     752           0 :         if (umcs_get_reg(sc, ctrlreg, &data) ||
     753           0 :             umcs_set_reg(sc, ctrlreg, data | UMCS_CTRL_RX_DISABLE))
     754           0 :                 return;
     755           0 : }
     756             : 
     757             : void
     758           0 : umcs_intr(struct usbd_xfer *xfer, void *priv, usbd_status status)
     759             : {
     760           0 :         struct umcs_softc *sc = priv;
     761           0 :         uint8_t *buf = sc->sc_ibuf;
     762           0 :         int actlen, i;
     763             : 
     764           0 :         if (usbd_is_dying(sc->sc_udev))
     765           0 :                 return;
     766             : 
     767           0 :         if (status == USBD_CANCELLED || status == USBD_IOERROR)
     768           0 :                 return;
     769             : 
     770           0 :         if (status != USBD_NORMAL_COMPLETION) {
     771             :                 DPRINTF("%s: interrupt status=%d\n", DEVNAME(sc), status);
     772           0 :                 usbd_clear_endpoint_stall_async(sc->sc_ipipe);
     773           0 :                 return;
     774             :         }
     775             : 
     776           0 :         usbd_get_xfer_status(xfer, NULL, NULL, &actlen, NULL);
     777           0 :         if (actlen != 5 && actlen != 13) {
     778           0 :                 printf("%s: invalid interrupt data length %d\n", DEVNAME(sc),
     779             :                     actlen);
     780           0 :                 return;
     781             :         }
     782             : 
     783             :         /* Check status of all ports */
     784           0 :         for (i = 0; i < sc->sc_numports; i++) {
     785           0 :                 uint8_t pn = sc->sc_subdevs[i].pn;
     786             : 
     787           0 :                 if (buf[pn] & UMCS_ISR_NOPENDING)
     788           0 :                         continue;
     789             : 
     790             :                 DPRINTF("%s: port %d has pending interrupt: %02x, FIFO=%02x\n",
     791             :                     DEVNAME(sc), i, buf[pn] & UMCS_ISR_INTMASK,
     792             :                     buf[pn] & (~UMCS_ISR_INTMASK));
     793             : 
     794           0 :                 switch (buf[pn] & UMCS_ISR_INTMASK) {
     795             :                 case UMCS_ISR_RXERR:
     796             :                 case UMCS_ISR_RXHASDATA:
     797             :                 case UMCS_ISR_RXTIMEOUT:
     798             :                 case UMCS_ISR_MSCHANGE:
     799           0 :                         sc->sc_subdevs[i].flags |= UMCS_STATCHG;
     800           0 :                         task_add(systq, &sc->sc_status_task);
     801           0 :                         break;
     802             :                 default:
     803             :                         /* Do nothing */
     804             :                         break;
     805             :                 }
     806           0 :         }
     807           0 : }
     808             : 
     809             : void
     810           0 : umcs_status_task(void *arg)
     811             : {
     812           0 :         struct umcs_softc *sc = arg;
     813             :         int i;
     814             : 
     815           0 :         for (i = 0; i < sc->sc_numports; i++) {
     816           0 :                 if ((sc->sc_subdevs[i].flags & UMCS_STATCHG) == 0)
     817             :                         continue;
     818             : 
     819           0 :                 sc->sc_subdevs[i].flags &= ~UMCS_STATCHG;
     820           0 :                 ucom_status_change(sc->sc_subdevs[i].ucom);
     821           0 :         }
     822           0 : }

Generated by: LCOV version 1.13