The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/dev/usbmisc/umct/umct.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*-
    2  * Copyright (c) 2003 Scott Long
    3  * All rights reserved.
    4  *
    5  * Redistribution and use in source and binary forms, with or without
    6  * modification, are permitted provided that the following conditions
    7  * are met:
    8  * 1. Redistributions of source code must retain the above copyright
    9  *    notice, this list of conditions and the following disclaimer.
   10  * 2. Redistributions in binary form must reproduce the above copyright
   11  *    notice, this list of conditions and the following disclaimer in the
   12  *    documentation and/or other materials provided with the distribution.
   13  *
   14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   24  * SUCH DAMAGE.
   25  *
   26  * $FreeBSD: src/sys/dev/usb/umct.c,v 1.12 2006/09/07 00:06:42 imp Exp $
   27  */
   28 
   29 /*
   30  * Driver for the MCT (Magic Control Technology) USB-RS232 Converter.
   31  * Based on the superb documentation from the linux mct_u232 driver by
   32  * Wolfgang Grandeggar <wolfgang@cec.ch>.
   33  * This device smells a lot like the Belkin F5U103, except that it has
   34  * suffered some mild brain-damage.  This driver is based off of the ubsa.c
   35  * driver from Alexander Kabaev <kan@freebsd.org>.  Merging the two together
   36  * might be useful, though the subtle differences might lead to lots of
   37  * #ifdef's.
   38  */
   39 
   40 #include <sys/param.h>
   41 #include <sys/systm.h>
   42 #include <sys/kernel.h>
   43 #include <sys/malloc.h>
   44 #include <sys/bus.h>
   45 #include <sys/tty.h>
   46 #include <sys/taskqueue.h>
   47 
   48 #include <bus/usb/usb.h>
   49 #include <bus/usb/usbdi.h>
   50 #include <bus/usb/usbdi_util.h>
   51 #include "../ucom/ucomvar.h"
   52 
   53 /* The UMCT advertises the standard 8250 UART registers */
   54 #define UMCT_GET_MSR            2       /* Get Modem Status Register */
   55 #define UMCT_GET_MSR_SIZE       1
   56 #define UMCT_GET_LCR            6       /* Get Line Control Register */
   57 #define UMCT_GET_LCR_SIZE       1
   58 #define UMCT_SET_BAUD           5       /* Set the Baud Rate Divisor */
   59 #define UMCT_SET_BAUD_SIZE      4
   60 #define UMCT_SET_LCR            7       /* Set Line Control Register */
   61 #define UMCT_SET_LCR_SIZE       1
   62 #define UMCT_SET_MCR            10      /* Set Modem Control Register */
   63 #define UMCT_SET_MCR_SIZE       1
   64 #define UMCT_SET_UNKNOWN1       11
   65 #define UMCT_SET_UNKNOWN1_SIZE  1
   66 #define UMCT_SET_UNKNOWN2       12
   67 #define UMCT_SET_UNKNOWN2_SIZE  1
   68 
   69 
   70 #define UMCT_INTR_INTERVAL      100
   71 #define UMCT_IFACE_INDEX        0
   72 #define UMCT_CONFIG_INDEX       1
   73 
   74 struct umct_softc {
   75         struct ucom_softc       sc_ucom;
   76         int                     sc_iface_number;
   77         usbd_interface_handle   sc_intr_iface;
   78         int                     sc_intr_number;
   79         usbd_pipe_handle        sc_intr_pipe;
   80         u_char                  *sc_intr_buf;
   81         int                     sc_isize;
   82         uint8_t                 sc_lsr;
   83         uint8_t                 sc_msr;
   84         uint8_t                 sc_lcr;
   85         uint8_t                 sc_mcr;
   86         struct task             sc_task;
   87 };
   88 
   89 static void umct_intr(usbd_xfer_handle, usbd_private_handle, usbd_status);
   90 static void umct_get_status(void *, int, u_char *, u_char *);
   91 static void umct_set(void *, int, int, int);
   92 static int  umct_param(void *, int, struct termios *);
   93 static int  umct_open(void *, int);
   94 static void umct_close(void *, int);
   95 static void umct_notify(void *, int);
   96 
   97 static struct ucom_callback umct_callback = {
   98         umct_get_status,        /* ucom_get_status */
   99         umct_set,               /* ucom_set */
  100         umct_param,             /* ucom_param */
  101         NULL,                   /* ucom_ioctl */
  102         umct_open,              /* ucom_open */
  103         umct_close,             /* ucom_close */
  104         NULL,                   /* ucom_read */
  105         NULL                    /* ucom_write */
  106 };
  107 
  108 static const struct usb_devno umct_products[] = {
  109         { USB_DEVICE(0x050d, 0x0109) }, /* Belkin F5U109 */
  110         { USB_DEVICE(0x050d, 0x0409) }, /* Belkin F5U409 */
  111         { USB_DEVICE(0x0711, 0x0200) }, /* D-Link DU-H3SP USB BAY Hub */
  112         { USB_DEVICE(0x0711, 0x0210) }, /* MCT USB-232 */
  113         { USB_DEVICE(0x0711, 0x0230) }, /* Sitecom USB-232 */
  114 };
  115 
  116 static device_probe_t   umct_match;
  117 static device_attach_t  umct_attach;
  118 static device_detach_t  umct_detach;
  119 
  120 static device_method_t umct_methods[] = {
  121         DEVMETHOD(device_probe, umct_match),
  122         DEVMETHOD(device_attach, umct_attach),
  123         DEVMETHOD(device_detach, umct_detach),
  124         DEVMETHOD_END
  125 };
  126 
  127 static driver_t umct_driver = {
  128         "ucom",
  129         umct_methods,
  130         sizeof(struct umct_softc)
  131 };
  132 
  133 DRIVER_MODULE(umct, uhub, umct_driver, ucom_devclass, usbd_driver_load, NULL);
  134 MODULE_DEPEND(umct, usb, 1, 1, 1);
  135 MODULE_DEPEND(umct, ucom, UCOM_MINVER, UCOM_PREFVER, UCOM_MAXVER);
  136 MODULE_VERSION(umct, 1);
  137 
  138 static int
  139 umct_match(device_t self)
  140 {
  141         struct usb_attach_arg *uaa = device_get_ivars(self);
  142 
  143         if (uaa->iface != NULL)
  144                 return (UMATCH_NONE);
  145 
  146         return (usb_lookup(umct_products, uaa->vendor, uaa->product) != NULL) ?
  147             UMATCH_VENDOR_PRODUCT : UMATCH_NONE;
  148 }
  149 
  150 static int
  151 umct_attach(device_t self)
  152 {
  153         struct umct_softc *sc = device_get_softc(self);
  154         struct usb_attach_arg *uaa = device_get_ivars(self);
  155         usbd_device_handle dev;
  156         struct ucom_softc *ucom;
  157         usb_config_descriptor_t *cdesc;
  158         usb_interface_descriptor_t *id;
  159         usb_endpoint_descriptor_t *ed;
  160         usbd_status err;
  161         int i;
  162 
  163         dev = uaa->device;
  164         bzero(sc, sizeof(struct umct_softc));
  165         ucom = &sc->sc_ucom;
  166         ucom->sc_dev = self;
  167         ucom->sc_udev = dev;
  168         ucom->sc_iface = uaa->iface;
  169 
  170         ucom->sc_bulkout_no = -1;
  171         ucom->sc_bulkin_no = -1;
  172         sc->sc_intr_number = -1;
  173         sc->sc_intr_pipe = NULL;
  174 
  175         err = usbd_set_config_index(dev, UMCT_CONFIG_INDEX, 1);
  176         if (err) {
  177                 device_printf(ucom->sc_dev, "failed to set configuration: %s\n",
  178                               usbd_errstr(err));
  179                 ucom->sc_dying = 1;
  180                 goto error;
  181         }
  182 
  183         cdesc = usbd_get_config_descriptor(ucom->sc_udev);
  184         if (cdesc == NULL) {
  185                 device_printf(ucom->sc_dev, "failed to get configuration "
  186                               "descriptor\n");
  187                 ucom->sc_dying = 1;
  188                 goto error;
  189         }
  190 
  191         err = usbd_device2interface_handle(dev, UMCT_IFACE_INDEX,
  192             &ucom->sc_iface);
  193         if (err) {
  194                 device_printf(ucom->sc_dev, "failed to get interface: %s\n",
  195                               usbd_errstr(err));
  196                 ucom->sc_dying = 1;
  197                 goto error;
  198         }
  199 
  200         id = usbd_get_interface_descriptor(ucom->sc_iface);
  201         sc->sc_iface_number = id->bInterfaceNumber;
  202 
  203         for (i = 0; i < id->bNumEndpoints; i++) {
  204                 ed = usbd_interface2endpoint_descriptor(ucom->sc_iface, i);
  205                 if (ed == NULL) {
  206                         device_printf(ucom->sc_dev, "no endpoint descriptor "
  207                                       "for %d\n", i);
  208                         ucom->sc_dying = 1;
  209                         goto error;
  210                 }
  211 
  212                 /*
  213                  * The real bulk-in endpoint is also marked as an interrupt.
  214                  * The only way to differentiate it from the real interrupt
  215                  * endpoint is to look at the wMaxPacketSize field.
  216                  */
  217                 if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN) {
  218                         if (UGETW(ed->wMaxPacketSize) == 0x2) {
  219                                 sc->sc_intr_number = ed->bEndpointAddress;
  220                                 sc->sc_isize = UGETW(ed->wMaxPacketSize);
  221                         } else {
  222                                 ucom->sc_bulkin_no = ed->bEndpointAddress;
  223                                 ucom->sc_ibufsize = UGETW(ed->wMaxPacketSize);
  224                         }
  225                         continue;
  226                 }
  227 
  228                 if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT) {
  229                         ucom->sc_bulkout_no = ed->bEndpointAddress;
  230                         /* MCT Sitecom USB-232 device is broken. The bulk out
  231                          * endpoint descriptor reports 32 bytes, but data will
  232                          * get dropped if this value is used. */
  233                         if (uaa->vendor == 0x0711 && uaa->product == 0x0230)
  234                                 ucom->sc_obufsize = 16;
  235                         else
  236                                 ucom->sc_obufsize = UGETW(ed->wMaxPacketSize);
  237                         continue;
  238                 }
  239 
  240                 device_printf(ucom->sc_dev, "warning - unsupported endpoint "
  241                               "0x%x\n", ed->bEndpointAddress);
  242         }
  243 
  244         if (sc->sc_intr_number == -1) {
  245                 device_printf(ucom->sc_dev, "could not find interrupt in\n");
  246                 ucom->sc_dying = 1;
  247                 goto error;
  248         }
  249 
  250         sc->sc_intr_iface = ucom->sc_iface;
  251 
  252         if (ucom->sc_bulkout_no == -1) {
  253                 device_printf(ucom->sc_dev, "rould not find data bulk out\n");
  254                 ucom->sc_dying = 1;
  255                 goto error;
  256         }
  257 
  258         ucom->sc_parent = sc;
  259         ucom->sc_portno = UCOM_UNK_PORTNO;
  260         ucom->sc_opkthdrlen = 0;
  261         ucom->sc_callback = &umct_callback;
  262         TASK_INIT(&sc->sc_task, 0, umct_notify, sc);
  263         ucom_attach(ucom);
  264 
  265         return 0;
  266 
  267 error:
  268         return ENXIO;
  269 }
  270 
  271 static int
  272 umct_detach(device_t self)
  273 {
  274         struct umct_softc *sc = device_get_softc(self);
  275         int rv;
  276 
  277         if (sc->sc_intr_pipe != NULL) {
  278                 usbd_abort_pipe(sc->sc_intr_pipe);
  279                 usbd_close_pipe(sc->sc_intr_pipe);
  280                 kfree(sc->sc_intr_buf, M_USBDEV);
  281                 sc->sc_intr_pipe = NULL;
  282         }
  283 
  284         sc->sc_ucom.sc_dying = 1;
  285         rv = ucom_detach(&sc->sc_ucom);
  286         return (rv);
  287 }
  288 
  289 static int
  290 umct_request(struct umct_softc *sc, uint8_t request, int len, uint32_t value)
  291 {
  292         usb_device_request_t req;
  293         usbd_status err;
  294         uint8_t oval[4];
  295 
  296         req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
  297         req.bRequest = request;
  298         USETW(req.wValue, 0);
  299         USETW(req.wIndex, sc->sc_iface_number);
  300         USETW(req.wLength, len);
  301         USETDW(oval, value);
  302 
  303         err = usbd_do_request(sc->sc_ucom.sc_udev, &req, oval);
  304         if (err)
  305                 device_printf(sc->sc_ucom.sc_dev, "ubsa_request: %s\n",
  306                               usbd_errstr(err));
  307         return (err);
  308 }
  309 
  310 static void
  311 umct_intr(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
  312 {
  313         struct umct_softc *sc;
  314         u_char *buf;
  315 
  316         sc = (struct umct_softc *)priv;
  317         buf = sc->sc_intr_buf;
  318         if (sc->sc_ucom.sc_dying)
  319                 return;
  320 
  321         if (status != USBD_NORMAL_COMPLETION) {
  322                 if (status == USBD_NOT_STARTED || status == USBD_CANCELLED)
  323                         return;
  324 
  325                 usbd_clear_endpoint_stall_async(sc->sc_intr_pipe);
  326                 return;
  327         }
  328 
  329         sc->sc_msr = buf[0];
  330         sc->sc_lsr = buf[1];
  331 
  332         /*
  333          * Defer notifying the ucom layer as it doesn't like to be bothered
  334          * from an interrupt context.
  335          */
  336         taskqueue_enqueue(taskqueue_swi, &sc->sc_task);
  337 }
  338 
  339 static void
  340 umct_notify(void *arg, int count)
  341 {
  342         struct umct_softc *sc;
  343 
  344         sc = (struct umct_softc *)arg;
  345         if (sc->sc_ucom.sc_dying == 0)
  346                 ucom_status_change(&sc->sc_ucom);
  347 }
  348 
  349 static void
  350 umct_get_status(void *addr, int portno, u_char *lsr, u_char *msr)
  351 {
  352         struct umct_softc *sc;
  353 
  354         sc = addr;
  355         if (lsr != NULL)
  356                 *lsr = sc->sc_lsr;
  357         if (msr != NULL)
  358                 *msr = sc->sc_msr;
  359 
  360         return;
  361 }
  362 
  363 static void
  364 umct_set(void *addr, int portno, int reg, int onoff)
  365 {
  366         struct umct_softc *sc;
  367 
  368         sc = addr;
  369         switch (reg) {
  370         case UCOM_SET_BREAK:
  371                 sc->sc_lcr &= ~0x40;
  372                 sc->sc_lcr |= (onoff) ? 0x40 : 0;
  373                 umct_request(sc, UMCT_SET_LCR, UMCT_SET_LCR_SIZE, sc->sc_lcr);
  374                 break;
  375         case UCOM_SET_DTR:
  376                 sc->sc_mcr &= ~0x01;
  377                 sc->sc_mcr |= (onoff) ? 0x01 : 0;
  378                 umct_request(sc, UMCT_SET_MCR, UMCT_SET_MCR_SIZE, sc->sc_mcr);
  379                 break;
  380         case UCOM_SET_RTS:
  381                 sc->sc_mcr &= ~0x2;
  382                 sc->sc_mcr |= (onoff) ? 0x02 : 0;
  383                 umct_request(sc, UMCT_SET_MCR, UMCT_SET_MCR_SIZE, sc->sc_mcr);
  384                 break;
  385         default:
  386                 break;
  387         }
  388 }
  389 
  390 static int
  391 umct_calc_baud(u_int baud)
  392 {
  393         switch(baud) {
  394         case B300: return (0x1);
  395         case B600: return (0x2);
  396         case B1200: return (0x3);
  397         case B2400: return (0x4);
  398         case B4800: return (0x6);
  399         case B9600: return (0x8);
  400         case B19200: return (0x9);
  401         case B38400: return (0xa);
  402         case B57600: return (0xb);
  403         case B115200: return (0xc);
  404         case B0:
  405         default:
  406                 break;
  407         }
  408 
  409         return (0x0);
  410 }
  411 
  412 static int
  413 umct_param(void *addr, int portno, struct termios *ti)
  414 {
  415         struct umct_softc *sc;
  416         uint32_t value;
  417 
  418         sc = addr;
  419         value = umct_calc_baud(ti->c_ospeed);
  420         umct_request(sc, UMCT_SET_BAUD, UMCT_SET_BAUD_SIZE, value);
  421         umct_request(sc, UMCT_SET_UNKNOWN1, UMCT_SET_UNKNOWN1_SIZE, 0);
  422         umct_request(sc, UMCT_SET_UNKNOWN2, UMCT_SET_UNKNOWN2_SIZE, 0);
  423 
  424         value = sc->sc_lcr & 0x40;
  425 
  426         switch (ti->c_cflag & CSIZE) {
  427         case CS5: value |= 0x0; break;
  428         case CS6: value |= 0x1; break;
  429         case CS7: value |= 0x2; break;
  430         case CS8: value |= 0x3; break;
  431         default: value |= 0x0; break;
  432         }
  433 
  434         value |= (ti->c_cflag & CSTOPB) ? 0x4 : 0;
  435         if (ti->c_cflag & PARENB) {
  436                 value |= 0x8;
  437                 value |= (ti->c_cflag & PARODD) ? 0x0 : 0x10;
  438         }
  439 
  440         /*
  441          * XXX There doesn't seem to be a way to tell the device to use flow
  442          * control.
  443          */
  444 
  445         sc->sc_lcr = value;
  446         umct_request(sc, UMCT_SET_LCR, UMCT_SET_LCR_SIZE, value);
  447 
  448         return (0);
  449 }
  450 
  451 static int
  452 umct_open(void *addr, int portno)
  453 {
  454         struct umct_softc *sc;
  455         int err;
  456 
  457         sc = addr;
  458         if (sc->sc_ucom.sc_dying) {
  459                 return (ENXIO);
  460         }
  461 
  462         if (sc->sc_intr_number != -1 && sc->sc_intr_pipe == NULL) {
  463                 sc->sc_intr_buf = kmalloc(sc->sc_isize, M_USBDEV, M_WAITOK);
  464                 err = usbd_open_pipe_intr(sc->sc_intr_iface, sc->sc_intr_number,
  465                     USBD_SHORT_XFER_OK, &sc->sc_intr_pipe, sc, sc->sc_intr_buf,
  466                     sc->sc_isize, umct_intr, UMCT_INTR_INTERVAL);
  467                 if (err) {
  468                         device_printf(sc->sc_ucom.sc_dev, "cannot open "
  469                                       "interrupt pipe (addr %d)\n",
  470                                       sc->sc_intr_number);
  471                         kfree(sc->sc_intr_buf, M_USBDEV);
  472                         return (EIO);
  473                 }
  474         }
  475 
  476         return (0);
  477 }
  478 
  479 static void
  480 umct_close(void *addr, int portno)
  481 {
  482         struct umct_softc *sc;
  483         int err;
  484 
  485         sc = addr;
  486         if (sc->sc_ucom.sc_dying)
  487                 return;
  488 
  489         if (sc->sc_intr_pipe != NULL) {
  490                 err = usbd_abort_pipe(sc->sc_intr_pipe);
  491                 if (err)
  492                         device_printf(sc->sc_ucom.sc_dev, "abort interrupt "
  493                                       "pipe failed: %s\n", usbd_errstr(err));
  494                 err = usbd_close_pipe(sc->sc_intr_pipe);
  495                 if (err)
  496                         device_printf(sc->sc_ucom.sc_dev, "close interrupt "
  497                                       "pipe failed: %s\n", usbd_errstr(err));
  498                 kfree(sc->sc_intr_buf, M_USBDEV);
  499                 sc->sc_intr_pipe = NULL;
  500         }
  501 }

Cache object: e0bb2a0f5bdd6626fa94aa4d9c18cba0


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.