/**************************************************************************** * drivers/usbdev/mtp.c * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. The * ASF licenses this file to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance with the * License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. * ****************************************************************************/ /**************************************************************************** * Included Files ****************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #ifdef CONFIG_BOARD_USBDEV_SERIALSTR #include #endif #include "usbdev_fs.h" /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ #define USBMTP_CHARDEV_PATH "/dev/mtp" /* USB Controller */ #ifdef CONFIG_USBDEV_SELFPOWERED # define USBMTP_SELFPOWERED USB_CONFIG_ATTR_SELFPOWER #else # define USBMTP_SELFPOWERED (0) #endif #ifdef CONFIG_USBDEV_REMOTEWAKEUP # define USBMTP_REMOTEWAKEUP USB_CONFIG_ATTR_WAKEUP #else # define USBMTP_REMOTEWAKEUP (0) #endif /* Buffer big enough for any of our descriptors (the config descriptor is the * biggest). */ #define USBMTP_MXDESCLEN (64) #define USBMTP_MAXSTRLEN (USBMTP_MXDESCLEN - 2) /* Device descriptor values */ #define USBMTP_VERSIONNO (0x0101) /* Device version number 1.1 (BCD) */ /* String language */ #define USBMTP_STR_LANGUAGE (0x0409) /* en-us */ /* Descriptor strings. If this MTP device is part of a composite device * then the manufacturer, product, and serial number strings will be provided * by the composite logic. */ #ifndef CONFIG_USBMTP_COMPOSITE # define USBMTP_MANUFACTURERSTRID (1) # define USBMTP_PRODUCTSTRID (2) # define USBMTP_SERIALSTRID (3) # define USBMTP_CONFIGSTRID (4) # define USBMTP_INTERFACESTRID (5) # define USBMTP_NSTRIDS (5) #else # define USBMTP_INTERFACESTRID (1) # define USBMTP_NSTRIDS (1) #endif #define USBMTP_NCONFIGS (1) /**************************************************************************** * Private Data ****************************************************************************/ /* USB descriptor ***********************************************************/ #ifndef CONFIG_USBMTP_COMPOSITE static const struct usb_devdesc_s g_mtp_devdesc = { .len = USB_SIZEOF_DEVDESC, /* Descriptor length */ .type = USB_DESC_TYPE_DEVICE, /* Descriptor type */ .usb = /* USB version */ { LSBYTE(0x0200), MSBYTE(0x0200) }, .classid = 0, /* Device class */ .subclass = 0, /* Device sub-class */ .protocol = 0, /* Device protocol */ .mxpacketsize = CONFIG_USBMTP_EP0MAXPACKET, /* Max packet size (ep0) */ .vendor = /* Vendor ID */ { LSBYTE(CONFIG_USBMTP_VENDORID), MSBYTE(CONFIG_USBMTP_VENDORID) }, .product = /* Product ID */ { LSBYTE(CONFIG_USBMTP_PRODUCTID), MSBYTE(CONFIG_USBMTP_PRODUCTID) }, .device = /* Device ID */ { LSBYTE(USBMTP_VERSIONNO), MSBYTE(USBMTP_VERSIONNO) }, .imfgr = USBMTP_MANUFACTURERSTRID, /* Manufacturer */ .iproduct = USBMTP_PRODUCTSTRID, /* Product */ .serno = USBMTP_SERIALSTRID, /* Serial number */ .nconfigs = USBMTP_NCONFIGS, /* Number of configurations */ }; static const struct usb_cfgdesc_s g_mtp_cfgdesc = { .len = USB_SIZEOF_CFGDESC, /* Descriptor length */ .type = USB_DESC_TYPE_CONFIG, /* Descriptor type */ .cfgvalue = 1, /* Configuration value */ .icfg = USBMTP_CONFIGSTRID, /* Configuration */ .attr = USB_CONFIG_ATTR_ONE | USBMTP_SELFPOWERED | USBMTP_REMOTEWAKEUP, /* Attributes */ .mxpower = (CONFIG_USBDEV_MAXPOWER + 1) / 2 }; # ifdef CONFIG_USBDEV_DUALSPEED static const struct usb_qualdesc_s g_mtp_qualdesc = { .len = USB_SIZEOF_QUALDESC, /* Descriptor length */ .type = USB_DESC_TYPE_DEVICEQUALIFIER, /* Descriptor type */ .usb = /* USB version */ { LSBYTE(0x0200), MSBYTE(0x0200) }, .classid = 0, /* Device class */ .subclass = 0, /* Device sub-class */ .protocol = 0, /* Device protocol */ .mxpacketsize = CONFIG_USBMTP_EP0MAXPACKET, /* Max packet size (ep0) */ .nconfigs = USBMTP_NCONFIGS, /* Number of configurations */ .reserved = 0, }; # endif static const struct usbdev_strdesc_s g_mtp_strdesc[] = { {USBMTP_MANUFACTURERSTRID, CONFIG_USBMTP_VENDORSTR}, {USBMTP_PRODUCTSTRID, CONFIG_USBMTP_PRODUCTSTR}, # ifdef CONFIG_USBMTP_SERIALSTR {USBMTP_SERIALSTRID, CONFIG_USBMTP_SERIALSTR}, # else {USBMTP_SERIALSTRID, ""}, # endif {USBMTP_CONFIGSTRID, CONFIG_USBMTP_CONFIGSTR}, {} }; static const struct usbdev_strdescs_s g_mtp_strdescs = { .language = USBMTP_STR_LANGUAGE, .strdesc = g_mtp_strdesc, }; static const struct usbdev_devdescs_s g_mtp_devdescs = { .cfgdesc = &g_mtp_cfgdesc, .strdescs = &g_mtp_strdescs, .devdesc = &g_mtp_devdesc, # ifdef CONFIG_USBDEV_DUALSPEED .qualdesc = &g_mtp_qualdesc, # endif }; #endif static const struct usb_ifdesc_s g_mtp_ifdesc = { .len = USB_SIZEOF_IFDESC, .type = USB_DESC_TYPE_INTERFACE, .ifno = 0, .alt = 0, .neps = 3, .classid = USB_CLASS_STILL_IMAGE, .subclass = 0x01, .protocol = 0x01, .iif = 0x05 }; static const struct usbdev_epinfo_s g_mtp_epbulkin = { .desc = { .len = USB_SIZEOF_EPDESC, .type = USB_DESC_TYPE_ENDPOINT, .addr = USB_DIR_IN, .attr = USB_EP_ATTR_XFER_BULK | USB_EP_ATTR_NO_SYNC | USB_EP_ATTR_USAGE_DATA, .interval = 0, }, .reqnum = CONFIG_USBMTP_NWRREQS, .fssize = CONFIG_USBMTP_EPBULKIN_FSSIZE, #ifdef CONFIG_USBDEV_DUALSPEED .hssize = CONFIG_USBMTP_EPBULKIN_HSSIZE, #endif #ifdef CONFIG_USBDEV_SUPERSPEED .sssize = CONFIG_USBMTP_EPBULKIN_SSSIZE, .compdesc = { .len = USB_SIZEOF_SS_EPCOMPDESC, .type = USB_DESC_TYPE_ENDPOINT_COMPANION, .mxburst = CONFIG_USBMTP_EPBULKIN_MAXBURST, .attr = CONFIG_USBMTP_EPBULKIN_MAXSTREAM, .wbytes[0] = 0, .wbytes[1] = 0, }, #endif }; static const struct usbdev_epinfo_s g_mtp_epbulkout = { .desc = { .len = USB_SIZEOF_EPDESC, .type = USB_DESC_TYPE_ENDPOINT, .addr = USB_DIR_OUT, .attr = USB_EP_ATTR_XFER_BULK | USB_EP_ATTR_NO_SYNC | USB_EP_ATTR_USAGE_DATA, .interval = 0, }, .reqnum = CONFIG_USBMTP_NRDREQS, .fssize = CONFIG_USBMTP_EPBULKOUT_FSSIZE, #ifdef CONFIG_USBDEV_DUALSPEED .hssize = CONFIG_USBMTP_EPBULKOUT_HSSIZE, #endif #ifdef CONFIG_USBDEV_SUPERSPEED .sssize = CONFIG_USBMTP_EPBULKOUT_SSSIZE, .compdesc = { .len = USB_SIZEOF_SS_EPCOMPDESC, .type = USB_DESC_TYPE_ENDPOINT_COMPANION, .mxburst = CONFIG_USBMTP_EPBULKOUT_MAXBURST, .attr = CONFIG_USBMTP_EPBULKOUT_MAXSTREAM, .wbytes[0] = 0, .wbytes[1] = 0, }, #endif }; static const struct usbdev_epinfo_s g_mtp_epintin = { .desc = { .len = USB_SIZEOF_EPDESC, .type = USB_DESC_TYPE_ENDPOINT, .addr = USB_DIR_IN, .attr = USB_EP_ATTR_XFER_INT | USB_EP_ATTR_NO_SYNC | USB_EP_ATTR_USAGE_DATA, .interval = CONFIG_USBMTP_EPINTIN_INTERVAL, }, .reqnum = CONFIG_USBMTP_NWRREQS, .fssize = CONFIG_USBMTP_EPINTIN_SIZE, #ifdef CONFIG_USBDEV_DUALSPEED .hssize = CONFIG_USBMTP_EPINTIN_SIZE, #endif #ifdef CONFIG_USBDEV_SUPERSPEED .sssize = CONFIG_USBMTP_EPINTIN_SIZE, .compdesc = { .len = USB_SIZEOF_SS_EPCOMPDESC, .type = USB_DESC_TYPE_ENDPOINT_COMPANION, .mxburst = CONFIG_USBMTP_EPINTIN_MAXBURST, .attr = 0, .wbytes[0] = LSBYTE((CONFIG_USBMTP_EPINTIN_MAXBURST + 1) * CONFIG_USBMTP_EPINTIN_SIZE), .wbytes[1] = MSBYTE((CONFIG_USBMTP_EPINTIN_MAXBURST + 1) * CONFIG_USBMTP_EPINTIN_SIZE), }, #endif }; static const FAR struct usbdev_epinfo_s *g_mtp_epinfos[USBMTP_NUM_EPS] = { &g_mtp_epbulkin, &g_mtp_epbulkout, &g_mtp_epintin, }; /**************************************************************************** * Private Functions ****************************************************************************/ /**************************************************************************** * Name: usbclass_mkcfgdesc * * Description: * Construct the configuration descriptor * ****************************************************************************/ static int16_t usbclass_mkcfgdesc(FAR uint8_t *buf, FAR struct usbdev_devinfo_s *devinfo, uint8_t speed, uint8_t type) { FAR uint8_t *epdesc; FAR struct usb_ifdesc_s *dest; uint32_t totallen = 0; int ret; /* Check for switches between high and full speed */ if (type == USB_DESC_TYPE_OTHERSPEEDCONFIG && speed < USB_SPEED_SUPER) { speed = speed == USB_SPEED_HIGH ? USB_SPEED_FULL : USB_SPEED_HIGH; } dest = (FAR struct usb_ifdesc_s *)buf; epdesc = (FAR uint8_t *)(buf + sizeof(g_mtp_ifdesc)); memcpy(dest, &g_mtp_ifdesc, sizeof(g_mtp_ifdesc)); totallen += sizeof(struct usb_ifdesc_s); ret = usbdev_copy_epdesc((FAR struct usb_epdesc_s *)epdesc, devinfo->epno[USBMTP_EP_BULKIN_IDX], speed, &g_mtp_epbulkin); totallen += ret; epdesc += ret; ret = usbdev_copy_epdesc((FAR struct usb_epdesc_s *)epdesc, devinfo->epno[USBMTP_EP_BULKOUT_IDX], speed, &g_mtp_epbulkout); totallen += ret; epdesc += ret; ret = usbdev_copy_epdesc((FAR struct usb_epdesc_s *)epdesc, devinfo->epno[USBMTP_EP_INTIN_IDX], speed, &g_mtp_epintin); totallen += ret; epdesc += ret; #ifdef CONFIG_USBMTP_COMPOSITE /* For composite device, apply possible offset to the interface numbers */ dest->ifno = devinfo->ifnobase; dest->iif = devinfo->strbase + USBMTP_INTERFACESTRID; #endif return totallen; } /**************************************************************************** * Name: usbclass_mkstrdesc * * Description: * Construct the string descriptor * ****************************************************************************/ static int usbclass_mkstrdesc(uint8_t id, FAR struct usb_strdesc_s *strdesc) { FAR uint8_t *data = (FAR uint8_t *)(strdesc + 1); FAR const char *str; int len; int ndata; int i; switch (id) { /* Composite driver removes offset before calling mkstrdesc() */ case USBMTP_INTERFACESTRID: str = CONFIG_USBMTP_INTERFACESTR; break; 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); if (len > (USBMTP_MAXSTRLEN / 2)) { len = (USBMTP_MAXSTRLEN / 2); } for (i = 0, ndata = 0; i < len; i++, ndata += 2) { data[ndata] = str[i]; data[ndata + 1] = 0; } strdesc->len = ndata + 2; strdesc->type = USB_DESC_TYPE_STRING; return strdesc->len; } /**************************************************************************** * Public Functions ****************************************************************************/ #ifndef CONFIG_USBMTP_COMPOSITE /**************************************************************************** * Name: usbdev_mtp_initialize * * Description: * Initialize the Media Transfer protocol USB device driver. * * Returned Value: * A non-NULL "handle" is returned on success. * ****************************************************************************/ FAR void *usbdev_mtp_initialize(void) { struct composite_devdesc_s devdesc; usbdev_mtp_get_composite_devdesc(&devdesc); devdesc.devinfo.epno[USBMTP_EP_BULKIN_IDX] = USB_EPNO(CONFIG_USBMTP_EPBULKIN); devdesc.devinfo.epno[USBMTP_EP_BULKOUT_IDX] = USB_EPNO(CONFIG_USBMTP_EPBULKOUT); devdesc.devinfo.epno[USBMTP_EP_INTIN_IDX] = USB_EPNO(CONFIG_USBMTP_EPINTIN); return usbdev_fs_initialize(&g_mtp_devdescs, &devdesc); } /**************************************************************************** * Name: usbdev_mtp_uninitialize * * Description: * Uninitialize the Media Transfer protocol USB device driver. * ****************************************************************************/ void usbdev_mtp_uninitialize(FAR void *handle) { usbdev_fs_uninitialize(handle); } #endif /**************************************************************************** * Name: usbdev_mtp_get_composite_devdesc * * Description: * Helper function to fill in some constants into the composite * configuration struct. * * Input Parameters: * dev - Pointer to the configuration struct we should fill * * Returned Value: * None * ****************************************************************************/ void usbdev_mtp_get_composite_devdesc(FAR struct composite_devdesc_s *dev) { memset(dev, 0, sizeof(struct composite_devdesc_s)); dev->mkconfdesc = usbclass_mkcfgdesc; dev->mkstrdesc = usbclass_mkstrdesc; dev->classobject = usbdev_fs_classobject; dev->uninitialize = usbdev_fs_classuninitialize; dev->nconfigs = USBMTP_NCONFIGS; dev->configid = 1; #ifdef CONFIG_USBDEV_SUPERSPEED dev->cfgdescsize = sizeof(g_mtp_ifdesc) + USB_SIZEOF_EPDESC * 3 + USB_SIZEOF_SS_EPCOMPDESC * 3; #else dev->cfgdescsize = sizeof(g_mtp_ifdesc) + 3 * USB_SIZEOF_EPDESC; #endif dev->devinfo.ninterfaces = 1; dev->devinfo.nstrings = USBMTP_NSTRIDS; dev->devinfo.nendpoints = USBMTP_NUM_EPS; dev->devinfo.epinfos = g_mtp_epinfos; dev->devinfo.name = USBMTP_CHARDEV_PATH; }