incubator-nuttx/drivers/usbdev/mtp.c

511 lines
15 KiB
C

/****************************************************************************
* 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 <nuttx/config.h>
#include <assert.h>
#include <errno.h>
#include <debug.h>
#include <nuttx/nuttx.h>
#include <nuttx/kmalloc.h>
#include <nuttx/usb/usb.h>
#include <nuttx/usb/usbdev.h>
#include <nuttx/usb/usbdev_trace.h>
#include <nuttx/usb/mtp.h>
#include <nuttx/fs/fs.h>
#ifdef CONFIG_BOARD_USBDEV_SERIALSTR
#include <nuttx/board.h>
#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;
}