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


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

FreeBSD/Linux Kernel Cross Reference
sys/dev/usb/ucycom.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) 2004 Dag-Erling Coïdan Smørgrav
    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  *    in this position and unchanged.
   11  * 2. Redistributions in binary form must reproduce the above copyright
   12  *    notice, this list of conditions and the following disclaimer in the
   13  *    documentation and/or other materials provided with the distribution.
   14  * 3. The name of the author may not be used to endorse or promote products
   15  *    derived from this software without specific prior written permission.
   16  *
   17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
   18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   19  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
   20  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
   21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
   22  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
   23  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
   24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
   25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
   26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   27  *
   28  * $FreeBSD: releng/7.3/sys/dev/usb/ucycom.c 170969 2007-06-21 14:42:34Z imp $
   29  */
   30 
   31 /*
   32  * Device driver for Cypress CY7C637xx and CY7C640/1xx series USB to
   33  * RS232 bridges.
   34  *
   35  * Normally, a driver for a USB-to-serial chip would hang off the ucom(4)
   36  * driver, but ucom(4) was written under the assumption that all USB-to-
   37  * serial chips use bulk pipes for I/O, while the Cypress parts use HID
   38  * reports.
   39  */
   40 
   41 #include <sys/cdefs.h>
   42 __FBSDID("$FreeBSD: releng/7.3/sys/dev/usb/ucycom.c 170969 2007-06-21 14:42:34Z imp $");
   43 
   44 #include <sys/param.h>
   45 #include <sys/systm.h>
   46 #include <sys/conf.h>
   47 #include <sys/kernel.h>
   48 #include <sys/module.h>
   49 #include <sys/sysctl.h>
   50 #include <sys/bus.h>
   51 #include <sys/tty.h>
   52 
   53 #include "usbdevs.h"
   54 #include <dev/usb/usb.h>
   55 #include <dev/usb/usbdi.h>
   56 #include <dev/usb/usbdi_util.h>
   57 #include <dev/usb/usbhid.h>
   58 #include <dev/usb/hid.h>
   59 
   60 #define UCYCOM_EP_INPUT          0
   61 #define UCYCOM_EP_OUTPUT         1
   62 
   63 #define UCYCOM_MAX_IOLEN         32U
   64 
   65 struct ucycom_softc {
   66         device_t                 sc_dev;
   67         struct tty              *sc_tty;
   68         int                      sc_error;
   69         unsigned long            sc_cintr;
   70         unsigned long            sc_cin;
   71         unsigned long            sc_clost;
   72         unsigned long            sc_cout;
   73 
   74         /* usb parameters */
   75         usbd_device_handle       sc_usbdev;
   76         usbd_interface_handle    sc_iface;
   77         usbd_pipe_handle         sc_pipe;
   78         uint8_t                  sc_iep; /* input endpoint */
   79         uint8_t                  sc_fid; /* feature report id*/
   80         uint8_t                  sc_iid; /* input report id */
   81         uint8_t                  sc_oid; /* output report id */
   82         size_t                   sc_flen; /* feature report length */
   83         size_t                   sc_ilen; /* input report length */
   84         size_t                   sc_olen; /* output report length */
   85         uint8_t                  sc_ibuf[UCYCOM_MAX_IOLEN];
   86 
   87         /* model and settings */
   88         uint32_t                 sc_model;
   89 #define MODEL_CY7C63743          0x63743
   90 #define MODEL_CY7C64013          0x64013
   91         uint32_t                 sc_baud;
   92         uint8_t                  sc_cfg;
   93 #define UCYCOM_CFG_RESET         0x80
   94 #define UCYCOM_CFG_PARODD        0x20
   95 #define UCYCOM_CFG_PAREN         0x10
   96 #define UCYCOM_CFG_STOPB         0x08
   97 #define UCYCOM_CFG_DATAB         0x03
   98         uint8_t                  sc_ist; /* status flags from last input */
   99         uint8_t                  sc_ost; /* status flags for next output */
  100 
  101         /* flags */
  102         char                     sc_dying;
  103 };
  104 
  105 static device_probe_t ucycom_probe;
  106 static device_attach_t ucycom_attach;
  107 static device_detach_t ucycom_detach;
  108 static t_open_t ucycom_open;
  109 static t_close_t ucycom_close;
  110 static void ucycom_start(struct tty *);
  111 static void ucycom_stop(struct tty *, int);
  112 static int ucycom_param(struct tty *, struct termios *);
  113 static int ucycom_configure(struct ucycom_softc *, uint32_t, uint8_t);
  114 static void ucycom_intr(usbd_xfer_handle, usbd_private_handle, usbd_status);
  115 
  116 static device_method_t ucycom_methods[] = {
  117         DEVMETHOD(device_probe, ucycom_probe),
  118         DEVMETHOD(device_attach, ucycom_attach),
  119         DEVMETHOD(device_detach, ucycom_detach),
  120         { 0, 0 }
  121 };
  122 
  123 static driver_t ucycom_driver = {
  124         "ucycom",
  125         ucycom_methods,
  126         sizeof(struct ucycom_softc),
  127 };
  128 
  129 static devclass_t ucycom_devclass;
  130 
  131 DRIVER_MODULE(ucycom, uhub, ucycom_driver, ucycom_devclass, usbd_driver_load, 0);
  132 MODULE_VERSION(ucycom, 1);
  133 MODULE_DEPEND(ucycom, usb, 1, 1, 1);
  134 
  135 /*
  136  * Supported devices
  137  */
  138 
  139 static struct ucycom_device {
  140         uint16_t                 vendor;
  141         uint16_t                 product;
  142         uint32_t                 model;
  143 } ucycom_devices[] = {
  144         { USB_VENDOR_DELORME, USB_PRODUCT_DELORME_EARTHMATE, MODEL_CY7C64013 },
  145         { 0, 0, 0 },
  146 };
  147 
  148 #define UCYCOM_DEFAULT_RATE      4800
  149 #define UCYCOM_DEFAULT_CFG       0x03 /* N-8-1 */
  150 
  151 /*****************************************************************************
  152  *
  153  * Driver interface
  154  *
  155  */
  156 
  157 static int
  158 ucycom_probe(device_t dev)
  159 {
  160         struct usb_attach_arg *uaa;
  161         struct ucycom_device *ud;
  162 
  163         uaa = device_get_ivars(dev);
  164         if (uaa->iface != NULL)
  165                 return (UMATCH_NONE);
  166         for (ud = ucycom_devices; ud->model != 0; ++ud)
  167                 if (ud->vendor == uaa->vendor && ud->product == uaa->product)
  168                         return (UMATCH_VENDOR_PRODUCT);
  169         return (UMATCH_NONE);
  170 }
  171 
  172 static int
  173 ucycom_attach(device_t dev)
  174 {
  175         struct usb_attach_arg *uaa;
  176         struct ucycom_softc *sc;
  177         struct ucycom_device *ud;
  178         usb_endpoint_descriptor_t *ued;
  179         void *urd;
  180         int error, urdlen;
  181 
  182         /* get arguments and softc */
  183         uaa = device_get_ivars(dev);
  184         sc = device_get_softc(dev);
  185         bzero(sc, sizeof *sc);
  186         sc->sc_dev = dev;
  187         sc->sc_usbdev = uaa->device;
  188 
  189         /* get chip model */
  190         for (ud = ucycom_devices; ud->model != 0; ++ud)
  191                 if (ud->vendor == uaa->vendor && ud->product == uaa->product)
  192                         sc->sc_model = ud->model;
  193         if (sc->sc_model == 0) {
  194                 device_printf(dev, "unsupported device\n");
  195                 return (ENXIO);
  196         }
  197         device_printf(dev, "Cypress CY7C%X USB to RS232 bridge\n", sc->sc_model);
  198 
  199         /* select configuration */
  200         error = usbd_set_config_index(sc->sc_usbdev, 0, 1 /* verbose */);
  201         if (error != 0) {
  202                 device_printf(dev, "failed to select configuration: %s\n",
  203                     usbd_errstr(error));
  204                 return (ENXIO);
  205         }
  206 
  207         /* get first interface handle */
  208         error = usbd_device2interface_handle(sc->sc_usbdev, 0, &sc->sc_iface);
  209         if (error != 0) {
  210                 device_printf(dev, "failed to get interface handle: %s\n",
  211                     usbd_errstr(error));
  212                 return (ENXIO);
  213         }
  214 
  215         /* get report descriptor */
  216         error = usbd_read_report_desc(sc->sc_iface, &urd, &urdlen, M_USBDEV);
  217         if (error != 0) {
  218                 device_printf(dev, "failed to get report descriptor: %s\n",
  219                     usbd_errstr(error));
  220                 return (ENXIO);
  221         }
  222 
  223         /* get report sizes */
  224         sc->sc_flen = hid_report_size(urd, urdlen, hid_feature, &sc->sc_fid);
  225         sc->sc_ilen = hid_report_size(urd, urdlen, hid_input, &sc->sc_iid);
  226         sc->sc_olen = hid_report_size(urd, urdlen, hid_output, &sc->sc_oid);
  227 
  228         if (sc->sc_ilen > UCYCOM_MAX_IOLEN || sc->sc_olen > UCYCOM_MAX_IOLEN) {
  229                 device_printf(dev, "I/O report size too big (%zu, %zu, %u)\n",
  230                     sc->sc_ilen, sc->sc_olen, UCYCOM_MAX_IOLEN);
  231                 return (ENXIO);
  232         }
  233 
  234         /* get and verify input endpoint descriptor */
  235         ued = usbd_interface2endpoint_descriptor(sc->sc_iface, UCYCOM_EP_INPUT);
  236         if (ued == NULL) {
  237                 device_printf(dev, "failed to get input endpoint descriptor\n");
  238                 return (ENXIO);
  239         }
  240         if (UE_GET_DIR(ued->bEndpointAddress) != UE_DIR_IN) {
  241                 device_printf(dev, "not an input endpoint\n");
  242                 return (ENXIO);
  243         }
  244         if ((ued->bmAttributes & UE_XFERTYPE) != UE_INTERRUPT) {
  245                 device_printf(dev, "not an interrupt endpoint\n");
  246                 return (ENXIO);
  247         }
  248         sc->sc_iep = ued->bEndpointAddress;
  249 
  250         /* set up tty */
  251         sc->sc_tty = ttyalloc();
  252         sc->sc_tty->t_sc = sc;
  253         sc->sc_tty->t_oproc = ucycom_start;
  254         sc->sc_tty->t_stop = ucycom_stop;
  255         sc->sc_tty->t_param = ucycom_param;
  256         sc->sc_tty->t_open = ucycom_open;
  257         sc->sc_tty->t_close = ucycom_close;
  258 
  259         SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
  260             SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
  261             OID_AUTO, "intr", CTLFLAG_RD, &sc->sc_cintr, 0,
  262             "interrupt count");
  263         SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
  264             SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
  265             OID_AUTO, "in", CTLFLAG_RD, &sc->sc_cin, 0,
  266             "input bytes read");
  267         SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
  268             SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
  269             OID_AUTO, "lost", CTLFLAG_RD, &sc->sc_clost, 0,
  270             "input bytes lost");
  271         SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
  272             SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
  273             OID_AUTO, "out", CTLFLAG_RD, &sc->sc_cout, 0,
  274             "output bytes");
  275 
  276         /* create character device node */
  277         ttycreate(sc->sc_tty, 0, "y%r", device_get_unit(sc->sc_dev));
  278 
  279         return (0);
  280 }
  281 
  282 static int
  283 ucycom_detach(device_t dev)
  284 {
  285         struct ucycom_softc *sc;
  286 
  287         sc = device_get_softc(dev);
  288 
  289         ttyfree(sc->sc_tty);
  290 
  291         return (0);
  292 }
  293 
  294 /*****************************************************************************
  295  *
  296  * Device interface
  297  *
  298  */
  299 
  300 static int
  301 ucycom_open(struct tty *tp, struct cdev *cdev)
  302 {
  303         struct ucycom_softc *sc = tp->t_sc;
  304         int error;
  305 
  306         /* set default configuration */
  307         ucycom_configure(sc, UCYCOM_DEFAULT_RATE, UCYCOM_DEFAULT_CFG);
  308 
  309         /* open interrupt pipe */
  310         error = usbd_open_pipe_intr(sc->sc_iface, sc->sc_iep, 0,
  311             &sc->sc_pipe, sc, sc->sc_ibuf, sc->sc_ilen,
  312             ucycom_intr, USBD_DEFAULT_INTERVAL);
  313         if (error != 0) {
  314                 device_printf(sc->sc_dev, "failed to open interrupt pipe: %s\n",
  315                     usbd_errstr(error));
  316                 return (ENXIO);
  317         }
  318 
  319         if (bootverbose)
  320                 device_printf(sc->sc_dev, "%s bypass l_rint()\n",
  321                     (sc->sc_tty->t_state & TS_CAN_BYPASS_L_RINT) ?
  322                     "can" : "can't");
  323 
  324         /* done! */
  325         return (0);
  326 }
  327 
  328 static void
  329 ucycom_close(struct tty *tp)
  330 {
  331         struct ucycom_softc *sc = tp->t_sc;
  332 
  333         /* stop interrupts and close the interrupt pipe */
  334         usbd_abort_pipe(sc->sc_pipe);
  335         usbd_close_pipe(sc->sc_pipe);
  336         sc->sc_pipe = 0;
  337 
  338         return;
  339 }
  340 
  341 /*****************************************************************************
  342  *
  343  * TTY interface
  344  *
  345  */
  346 
  347 static void
  348 ucycom_start(struct tty *tty)
  349 {
  350         struct ucycom_softc *sc = tty->t_sc;
  351         uint8_t report[sc->sc_olen];
  352         int error, len;
  353 
  354         while (sc->sc_error == 0 && sc->sc_tty->t_outq.c_cc > 0) {
  355                 switch (sc->sc_model) {
  356                 case MODEL_CY7C63743:
  357                         len = q_to_b(&sc->sc_tty->t_outq,
  358                             report + 1, sc->sc_olen - 1);
  359                         sc->sc_cout += len;
  360                         report[0] = len;
  361                         len += 1;
  362                         break;
  363                 case MODEL_CY7C64013:
  364                         len = q_to_b(&sc->sc_tty->t_outq,
  365                             report + 2, sc->sc_olen - 2);
  366                         sc->sc_cout += len;
  367                         report[0] = 0;
  368                         report[1] = len;
  369                         len += 2;
  370                         break;
  371                 default:
  372                         panic("unsupported model (driver error)");
  373                 }
  374 
  375                 while (len < sc->sc_olen)
  376                         report[len++] = 0;
  377                 error = usbd_set_report(sc->sc_iface, UHID_OUTPUT_REPORT,
  378                     sc->sc_oid, report, sc->sc_olen);
  379 #if 0
  380                 if (error != 0) {
  381                         device_printf(sc->sc_dev,
  382                             "failed to set output report: %s\n",
  383                             usbd_errstr(error));
  384                         sc->sc_error = error;
  385                 }
  386 #endif
  387         }
  388 }
  389 
  390 static void
  391 ucycom_stop(struct tty *tty, int flags)
  392 {
  393         struct ucycom_softc *sc;
  394 
  395         sc = tty->t_sc;
  396         if (bootverbose)
  397                 device_printf(sc->sc_dev, "%s()\n", __func__);
  398 }
  399 
  400 static int
  401 ucycom_param(struct tty *tty, struct termios *t)
  402 {
  403         struct ucycom_softc *sc;
  404         uint32_t baud;
  405         uint8_t cfg;
  406         int error;
  407 
  408         sc = tty->t_sc;
  409 
  410         if (t->c_ispeed != t->c_ospeed)
  411                 return (EINVAL);
  412         baud = t->c_ispeed;
  413 
  414         if (t->c_cflag & CIGNORE) {
  415                 cfg = sc->sc_cfg;
  416         } else {
  417                 cfg = 0;
  418                 switch (t->c_cflag & CSIZE) {
  419                 case CS8:
  420                         ++cfg;
  421                 case CS7:
  422                         ++cfg;
  423                 case CS6:
  424                         ++cfg;
  425                 case CS5:
  426                         break;
  427                 default:
  428                         return (EINVAL);
  429                 }
  430                 if (t->c_cflag & CSTOPB)
  431                         cfg |= UCYCOM_CFG_STOPB;
  432                 if (t->c_cflag & PARENB)
  433                         cfg |= UCYCOM_CFG_PAREN;
  434                 if (t->c_cflag & PARODD)
  435                         cfg |= UCYCOM_CFG_PARODD;
  436         }
  437 
  438         error = ucycom_configure(sc, baud, cfg);
  439         return (error);
  440 }
  441 
  442 /*****************************************************************************
  443  *
  444  * Hardware interface
  445  *
  446  */
  447 
  448 static int
  449 ucycom_configure(struct ucycom_softc *sc, uint32_t baud, uint8_t cfg)
  450 {
  451         uint8_t report[sc->sc_flen];
  452         int error;
  453 
  454         switch (baud) {
  455         case 600:
  456         case 1200:
  457         case 2400:
  458         case 4800:
  459         case 9600:
  460         case 19200:
  461         case 38400:
  462         case 57600:
  463 #if 0
  464         /*
  465          * Stock chips only support standard baud rates in the 600 - 57600
  466          * range, but higher rates can be achieved using custom firmware.
  467          */
  468         case 115200:
  469         case 153600:
  470         case 192000:
  471 #endif
  472                 break;
  473         default:
  474                 return (EINVAL);
  475         }
  476 
  477         if (bootverbose)
  478                 device_printf(sc->sc_dev, "%d baud, %c-%d-%d\n", baud,
  479                     (cfg & UCYCOM_CFG_PAREN) ?
  480                     ((cfg & UCYCOM_CFG_PARODD) ? 'O' : 'E') : 'N',
  481                     5 + (cfg & UCYCOM_CFG_DATAB),
  482                     (cfg & UCYCOM_CFG_STOPB) ? 2 : 1);
  483         report[0] = baud & 0xff;
  484         report[1] = (baud >> 8) & 0xff;
  485         report[2] = (baud >> 16) & 0xff;
  486         report[3] = (baud >> 24) & 0xff;
  487         report[4] = cfg;
  488         error = usbd_set_report(sc->sc_iface, UHID_FEATURE_REPORT,
  489             sc->sc_fid, report, sc->sc_flen);
  490         if (error != 0) {
  491                 device_printf(sc->sc_dev, "%s\n", usbd_errstr(error));
  492                 return (EIO);
  493         }
  494         sc->sc_baud = baud;
  495         sc->sc_cfg = cfg;
  496         return (0);
  497 }
  498 
  499 static void
  500 ucycom_intr(usbd_xfer_handle xfer, usbd_private_handle scp, usbd_status status)
  501 {
  502         struct ucycom_softc *sc = scp;
  503         uint8_t *data;
  504         int i, len, lost;
  505 
  506         sc->sc_cintr++;
  507 
  508         switch (sc->sc_model) {
  509         case MODEL_CY7C63743:
  510                 sc->sc_ist = sc->sc_ibuf[0] & ~0x07;
  511                 len = sc->sc_ibuf[0] & 0x07;
  512                 data = sc->sc_ibuf + 1;
  513                 break;
  514         case MODEL_CY7C64013:
  515                 sc->sc_ist = sc->sc_ibuf[0] & ~0x07;
  516                 len = sc->sc_ibuf[1];
  517                 data = sc->sc_ibuf + 2;
  518                 break;
  519         default:
  520                 panic("unsupported model (driver error)");
  521         }
  522 
  523         switch (status) {
  524         case USBD_NORMAL_COMPLETION:
  525                 break;
  526         default:
  527                 /* XXX */
  528                 return;
  529         }
  530 
  531         if (sc->sc_tty->t_state & TS_CAN_BYPASS_L_RINT) {
  532                 /* XXX flow control! */
  533                 lost = b_to_q(data, len, &sc->sc_tty->t_rawq);
  534                 sc->sc_tty->t_rawcc += len - lost;
  535                 ttwakeup(sc->sc_tty);
  536         } else {
  537                 for (i = 0, lost = len; i < len; ++i, --lost)
  538                         if (ttyld_rint(sc->sc_tty, data[i]) != 0)
  539                                 break;
  540         }
  541         sc->sc_cin += len - lost;
  542         sc->sc_clost += lost;
  543 }

Cache object: 92bcd84601737e6ad8582f569ec29983


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