diff options
Diffstat (limited to 'nuttx/drivers/usbdev/cdc_serdesc.c')
-rw-r--r-- | nuttx/drivers/usbdev/cdc_serdesc.c | 591 |
1 files changed, 591 insertions, 0 deletions
diff --git a/nuttx/drivers/usbdev/cdc_serdesc.c b/nuttx/drivers/usbdev/cdc_serdesc.c new file mode 100644 index 000000000..03bac3006 --- /dev/null +++ b/nuttx/drivers/usbdev/cdc_serdesc.c @@ -0,0 +1,591 @@ +/**************************************************************************** + * drivers/usbdev/cdc_serdesc.c + * + * Copyright (C) 2011 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt <spudmonkey@racsa.co.cr> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> + +#include <sys/types.h> +#include <stdint.h> +#include <string.h> +#include <errno.h> +#include <debug.h> + +#include <nuttx/usb/usb.h> +#include <nuttx/usb/cdc.h> +#include <nuttx/usb/cdc_serial.h> +#include <nuttx/usb/usbdev_trace.h> + +#include "cdc_serial.h" + +/**************************************************************************** + * Definitions + ****************************************************************************/ +/* Descriptors **************************************************************/ +/* These settings are not modifiable via the NuttX configuration */ + +#define CDC_VERSIONNO 0x0110 /* CDC version number 1.10 (BCD) */ + +/* Device descriptor values */ + +#define CDCSER_VERSIONNO (0x0101) /* Device version number 1.1 (BCD) */ +#define CDCSER_NCONFIGS (1) /* Number of configurations supported */ + +/* Configuration descriptor values */ + +#define CDCSER_NINTERFACES (2) /* Number of interfaces in the configuration */ + +/* String language */ + +#define CDCSER_STR_LANGUAGE (0x0409) /* en-us */ + +/* Descriptor strings */ + +#define CDCSER_MANUFACTURERSTRID (1) +#define CDCSER_PRODUCTSTRID (2) +#define CDCSER_SERIALSTRID (3) +#define CDCSER_CONFIGSTRID (4) +#define CDCSER_NOTIFSTRID (5) +#define CDCSER_DATAIFSTRID (6) + +/* Number of individual descriptors in the configuration descriptor */ + +#define CDCSER_CFGGROUP_SIZE (9) + +/* The size of the config descriptor: (9 + 2*9 + 3*7 + 4 + 5 + 5) = 62 */ + +#define SIZEOF_CDCSER_CFGDESC \ + (USB_SIZEOF_CFGDESC + 2*USB_SIZEOF_IFDESC + 3*USB_SIZEOF_EPDESC + SIZEOF_ACM_FUNCDESC + SIZEOF_HDR_FUNCDESC + SIZEOF_UNION_FUNCDESC(1)) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* Describes one description in the group of descriptors forming the + * total configuration descriptor. + */ + +struct cfgdecsc_group_s +{ + uint16_t descsize; /* Size of the descriptor in bytes */ + uint16_t hsepsize; /* High speed max packet size */ + FAR void *desc; /* A pointer to the descriptor */ +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* USB descriptor templates these will be copied and modified **************/ + +static const struct usb_devdesc_s g_devdesc = +{ + USB_SIZEOF_DEVDESC, /* len */ + USB_DESC_TYPE_DEVICE, /* type */ + { /* usb */ + LSBYTE(0x0200), + MSBYTE(0x0200) + }, + USB_CLASS_CDC, /* class */ + CDC_SUBCLASS_NONE, /* subclass */ + CDC_PROTO_NONE, /* protocol */ + CONFIG_CDCSER_EP0MAXPACKET, /* maxpacketsize */ + { + LSBYTE(CONFIG_CDCSER_VENDORID), /* vendor */ + MSBYTE(CONFIG_CDCSER_VENDORID) + }, + { + LSBYTE(CONFIG_CDCSER_PRODUCTID), /* product */ + MSBYTE(CONFIG_CDCSER_PRODUCTID) + }, + { + LSBYTE(CDCSER_VERSIONNO), /* device */ + MSBYTE(CDCSER_VERSIONNO) + }, + CDCSER_MANUFACTURERSTRID, /* imfgr */ + CDCSER_PRODUCTSTRID, /* iproduct */ + CDCSER_SERIALSTRID, /* serno */ + CDCSER_NCONFIGS /* nconfigs */ +}; + +/* Configuration descriptor */ + +static const struct usb_cfgdesc_s g_cfgdesc = +{ + USB_SIZEOF_CFGDESC, /* len */ + USB_DESC_TYPE_CONFIG, /* type */ + { + LSBYTE(SIZEOF_CDCSER_CFGDESC), /* LS totallen */ + MSBYTE(SIZEOF_CDCSER_CFGDESC) /* MS totallen */ + }, + CDCSER_NINTERFACES, /* ninterfaces */ + CDCSER_CONFIGID, /* cfgvalue */ + CDCSER_CONFIGSTRID, /* icfg */ + USB_CONFIG_ATTR_ONE|SELFPOWERED|REMOTEWAKEUP, /* attr */ + (CONFIG_USBDEV_MAXPOWER + 1) / 2 /* mxpower */ +}; + +/* Notification interface */ + +static const struct usb_ifdesc_s g_notifdesc = +{ + USB_SIZEOF_IFDESC, /* len */ + USB_DESC_TYPE_INTERFACE, /* type */ + 0, /* ifno */ + 0, /* alt */ + 1, /* neps */ + USB_CLASS_CDC, /* class */ + CDC_SUBCLASS_ACM, /* subclass */ + CDC_PROTO_ATM, /* proto */ +#ifdef CONFIG_CDCSER_NOTIFSTR + CDCSER_NOTIFSTRID /* iif */ +#else + 0 /* iif */ +#endif +}; + +/* Header functional descriptor */ + +static const struct cdc_hdr_funcdesc_s g_funchdr = +{ + SIZEOF_HDR_FUNCDESC, /* size */ + USB_DESC_TYPE_CSINTERFACE, /* type */ + CDC_DSUBTYPE_HDR, /* subtype */ + { + LSBYTE(CDC_VERSIONNO), /* LS cdc */ + MSBYTE(CDC_VERSIONNO) /* MS cdc */ + } +}; + +/* ACM functional descriptor */ + +static const struct cdc_acm_funcdesc_s g_acmfunc = +{ + SIZEOF_ACM_FUNCDESC, /* size */ + USB_DESC_TYPE_CSINTERFACE, /* type */ + CDC_DSUBTYPE_ACM, /* subtype */ + 0x06 /* caps */ +}; + +/* Union functional descriptor */ + +static const struct cdc_union_funcdesc_s g_unionfunc = +{ + SIZEOF_UNION_FUNCDESC(1), /* size */ + USB_DESC_TYPE_CSINTERFACE, /* type */ + CDC_DSUBTYPE_UNION, /* subtype */ + 0, /* master */ + {1} /* slave[0] */ +}; + +/* Interrupt IN endpoint descriptor */ + +static const struct usb_epdesc_s g_epintindesc = +{ + USB_SIZEOF_EPDESC, /* len */ + USB_DESC_TYPE_ENDPOINT, /* type */ + CDCSER_EPINTIN_ADDR, /* addr */ + CDCSER_EPINTIN_ATTR, /* attr */ + { + LSBYTE(CONFIG_CDCSER_EPINTIN_FSSIZE), /* maxpacket (full speed) */ + MSBYTE(CONFIG_CDCSER_EPINTIN_FSSIZE) + }, + 0xff /* interval */ +}; + +/* Data interface descriptor */ + +static const struct usb_ifdesc_s g_dataifdesc = +{ + USB_SIZEOF_IFDESC, /* len */ + USB_DESC_TYPE_INTERFACE, /* type */ + 1, /* ifno */ + 0, /* alt */ + 2, /* neps */ + USB_CLASS_CDC_DATA, /* class */ + CDC_DATA_SUBCLASS_NONE, /* subclass */ + CDC_DATA_PROTO_NONE, /* proto */ +#ifdef CONFIG_CDCSER_DATAIFSTR + CDCSER_DATAIFSTRID /* iif */ +#else + 0 /* iif */ +#endif +}; + +/* Bulk OUT endpoint descriptor */ + +static const struct usb_epdesc_s g_epbulkoutdesc = +{ + USB_SIZEOF_EPDESC, /* len */ + USB_DESC_TYPE_ENDPOINT, /* type */ + CDCSER_EPOUTBULK_ADDR, /* addr */ + CDCSER_EPOUTBULK_ATTR, /* attr */ + { + LSBYTE(CONFIG_CDCSER_EPBULKOUT_FSSIZE), /* maxpacket (full speed) */ + MSBYTE(CONFIG_CDCSER_EPBULKOUT_FSSIZE) + }, + 1 /* interval */ +}; + +/* Bulk IN endpoint descriptor */ + +static const struct usb_epdesc_s g_epbulkindesc = +{ + USB_SIZEOF_EPDESC, /* len */ + USB_DESC_TYPE_ENDPOINT, /* type */ + CDCSER_EPINBULK_ADDR, /* addr */ + CDCSER_EPINBULK_ATTR, /* attr */ + { + LSBYTE(CONFIG_CDCSER_EPBULKIN_FSSIZE), /* maxpacket (full speed) */ + MSBYTE(CONFIG_CDCSER_EPBULKIN_FSSIZE) + }, + 1 /* interval */ +}; + +/* The components of the the configuration descriptor are maintained as + * a collection of separate descriptor structure coordinated by the + * following array. These descriptors could have been combined into + * one larger "super" configuration descriptor structure. However, I + * have concerns about compiler-dependent alignment and packing. Since + * the individual structures consist only of byte types, alignment and + * packing is not an issue. And since the are concatentated at run time + * instead of compile time, there should no issues there either. + */ + +static const struct cfgdecsc_group_s g_cfggroup[CDCSER_CFGGROUP_SIZE] = { + { + USB_SIZEOF_CFGDESC, /* 1. Configuration descriptor */ + 0, + (FAR void *)&g_cfgdesc + }, + { + USB_SIZEOF_IFDESC, /* 2. Notification interface */ + 0, + (FAR void *)&g_notifdesc + }, + { + SIZEOF_HDR_FUNCDESC, /* 3. Header functional descriptor */ + 0, + (FAR void *)&g_funchdr + }, + { + SIZEOF_ACM_FUNCDESC, /* 4. ACM functional descriptor */ + 0, + (FAR void *)&g_acmfunc + }, + { + SIZEOF_UNION_FUNCDESC(1), /* 5. Union functional descriptor */ + 0, + (FAR void *)&g_unionfunc + }, + { + USB_SIZEOF_EPDESC, /* 6. Interrupt IN endpoint descriptor */ + CONFIG_CDCSER_EPINTIN_HSSIZE, + (FAR void *)&g_epintindesc + }, + { + USB_SIZEOF_IFDESC, /* 7. Data interface descriptor */ + 0, + (FAR void *)&g_dataifdesc + }, + { + USB_SIZEOF_EPDESC, /* 8. Bulk OUT endpoint descriptor */ + CONFIG_CDCSER_EPBULKOUT_HSSIZE, + (FAR void *)&g_epbulkoutdesc + }, + { + USB_SIZEOF_EPDESC, /* 9. Bulk OUT endpoint descriptor */ + CONFIG_CDCSER_EPBULKIN_HSSIZE, + (FAR void *)&g_epbulkindesc + } +}; + +#ifdef CONFIG_USBDEV_DUALSPEED +static const struct usb_qualdesc_s g_qualdesc = +{ + USB_SIZEOF_QUALDESC, /* len */ + USB_DESC_TYPE_DEVICEQUALIFIER, /* type */ + { /* usb */ + LSBYTE(0x0200), + MSBYTE(0x0200) + }, + USB_CLASS_VENDOR_SPEC, /* class */ + 0, /* subclass */ + 0, /* protocol */ + CONFIG_CDCSER_EP0MAXPACKET, /* mxpacketsize */ + CDCSER_NCONFIGS, /* nconfigs */ + 0, /* reserved */ +}; +#endif + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: usbclass_mkstrdesc + * + * Description: + * Construct a string descriptor + * + ****************************************************************************/ + +int usbclass_mkstrdesc(uint8_t id, struct usb_strdesc_s *strdesc) +{ + const char *str; + int len; + int ndata; + int i; + + switch (id) + { + case 0: + { + /* Descriptor 0 is the language id */ + + strdesc->len = 4; + strdesc->type = USB_DESC_TYPE_STRING; + strdesc->data[0] = LSBYTE(CDCSER_STR_LANGUAGE); + strdesc->data[1] = MSBYTE(CDCSER_STR_LANGUAGE); + return 4; + } + + case CDCSER_MANUFACTURERSTRID: + str = CONFIG_CDCSER_VENDORSTR; + break; + + case CDCSER_PRODUCTSTRID: + str = CONFIG_CDCSER_PRODUCTSTR; + break; + + case CDCSER_SERIALSTRID: + str = CONFIG_CDCSER_SERIALSTR; + break; + + case CDCSER_CONFIGSTRID: + str = CONFIG_CDCSER_CONFIGSTR; + break; + +#ifdef CONFIG_CDCSER_NOTIFSTR + case CDCSER_NOTIFSTRID: + str = CONFIG_CDCSER_NOTIFSTR; + break; +#endif + +#ifdef CONFIG_CDCSER_DATAIFSTR + case CDCSER_DATAIFSTRID: + str = CONFIG_CDCSER_DATAIFSTR; + break; +#endif + + default: + return -EINVAL; + } + + /* The string is utf16-le. The poor man's utf-8 to utf16-le + * conversion below will only handle 7-bit en-us ascii + */ + + len = strlen(str); + for (i = 0, ndata = 0; i < len; i++, ndata += 2) + { + strdesc->data[ndata] = str[i]; + strdesc->data[ndata+1] = 0; + } + + strdesc->len = ndata+2; + strdesc->type = USB_DESC_TYPE_STRING; + return strdesc->len; +} + +/**************************************************************************** + * Name: usbclass_getepdesc + * + * Description: + * Return a pointer to the raw device descriptor + * + ****************************************************************************/ + +FAR const struct usb_devdesc_s *usbclass_getdevdesc(void) +{ + return &g_devdesc; +} + +/**************************************************************************** + * Name: usbclass_getepdesc + * + * Description: + * Return a pointer to the raw endpoint struct (used for configuring + * endpoints) + * + ****************************************************************************/ + +FAR const struct usb_epdesc_s *usbclass_getepdesc(enum cdcser_epdesc_e epid) +{ + switch (epid) + { + case CDCSER_EPINTIN: /* Interrupt IN endpoint */ + return &g_epintindesc; + + case CDCSER_EPBULKOUT: /* Bulk OUT endpoint */ + return &g_epbulkoutdesc; + + case CDCSER_EPBULKIN: /* Bulk IN endpoint */ + return &g_epbulkindesc; + + default: + return NULL; + } +} + +/**************************************************************************** + * Name: usbclass_mkepdesc + * + * Description: + * Construct the endpoint descriptor using the correct max packet size. + * + ****************************************************************************/ + +#ifdef CONFIG_USBDEV_DUALSPEED +void usbclass_mkepdesc(num cdcser_epdesc_e epid, + uint16_t mxpacket, + FAR struct usb_epdesc_s *outdesc) +{ + FAR const struct usb_epdesc_s *indesc; + + /* Copy the "canned" descriptor */ + + indesc = usbclass_getepdesc(epid) + memcpy(outdesc, indesc, USB_SIZEOF_EPDESC); + + /* Then add the correct max packet size */ + + outdesc->mxpacketsize[0] = LSBYTE(mxpacket); + outdesc->mxpacketsize[1] = MSBYTE(mxpacket); +} +#endif + +/**************************************************************************** + * Name: usbclass_mkcfgdesc + * + * Description: + * Construct the configuration descriptor + * + ****************************************************************************/ + +#ifdef CONFIG_USBDEV_DUALSPEED +int16_t usbclass_mkcfgdesc(FAR uint8_t *buf, uint8_t speed, uint8_t type) +#else +int16_t usbclass_mkcfgdesc(FAR uint8_t *buf) +#endif +{ + FAR const struct cfgdecsc_group_s *group; + FAR uint8_t *dest = buf; + int i; + +#ifdef CONFIG_USBDEV_DUALSPEED + bool hispeed = (speed == USB_SPEED_HIGH); + + /* Check for switches between high and full speed */ + + if (type == USB_DESC_TYPE_OTHERSPEEDCONFIG) + { + hispeed = !hispeed; + } +#endif + + /* Copy all of the descriptors in the group */ + + for (i = 0, dest = buf; i < CDCSER_CFGGROUP_SIZE; i++) + { + group = &g_cfggroup[i]; + + /* The "canned" descriptors all have full speed endpoint maxpacket + * sizes. If high speed is selected, we will have to change the + * endpoint maxpacket size. + * + * Is there a alternative high speed maxpacket size in the table? + * If so, that is sufficient proof that the descriptor that we + * just copied is an endpoint descriptor and needs the fixup + */ + +#ifdef CONFIG_USBDEV_DUALSPEED + if (highspeed && group->hsepsize != 0) + { + usbclass_mkepdesc(group->desc, group->hsepsize, + (FAR struct usb_epdesc_s*)dest); + } + else +#endif + /* Copy the "canned" descriptor with the full speed max packet + * size + */ + + { + memcpy(dest, group->desc, group->descsize); + } + + /* Advance to the destination location for the next descriptor */ + + dest += group->descsize; + } + + return SIZEOF_CDCSER_CFGDESC; +} + +/**************************************************************************** + * Name: usbclass_getqualdesc + * + * Description: + * Return a pointer to the raw qual descriptor + * + ****************************************************************************/ + +#ifdef CONFIG_USBDEV_DUALSPEED +FAR const struct usb_qualdesc_s *usbclass_getqualdesc(void) +{ + return &g_qualdesc; +} +#endif |