incubator-nuttx/drivers/usbdev/adb.c

444 lines
13 KiB
C

/****************************************************************************
* drivers/usbdev/adb.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/adb.h>
#include <nuttx/fs/fs.h>
#ifdef CONFIG_BOARD_USBDEV_SERIALSTR
#include <nuttx/board.h>
#endif
#include "usbdev_fs.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/* FIXME use minor for char device npath */
#ifdef CONFIG_USBFASTBOOT
# define USBADB_CHARDEV_PATH "/dev/fastboot"
#else
# define USBADB_CHARDEV_PATH "/dev/adb0"
#endif
/* USB Controller */
#ifdef CONFIG_USBDEV_SELFPOWERED
# define USBADB_SELFPOWERED USB_CONFIG_ATTR_SELFPOWER
#else
# define USBADB_SELFPOWERED (0)
#endif
#ifdef CONFIG_USBDEV_REMOTEWAKEUP
# define USBADB_REMOTEWAKEUP USB_CONFIG_ATTR_WAKEUP
#else
# define USBADB_REMOTEWAKEUP (0)
#endif
/* Buffer big enough for any of our descriptors (the config descriptor is the
* biggest).
*/
#define USBADB_MXDESCLEN (64)
#define USBADB_MAXSTRLEN (USBADB_MXDESCLEN-2)
/* Device descriptor values */
#define USBADB_VERSIONNO (0x0101) /* Device version number 1.1 (BCD) */
/* String language */
#define USBADB_STR_LANGUAGE (0x0409) /* en-us */
/* Descriptor strings. If there serial device is part of a composite device
* then the manufacturer, product, and serial number strings will be provided
* by the composite logic.
*/
#ifndef CONFIG_USBADB_COMPOSITE
# define USBADB_MANUFACTURERSTRID (1)
# define USBADB_PRODUCTSTRID (2)
# define USBADB_SERIALSTRID (3)
# define USBADB_CONFIGSTRID (4)
# define USBADB_INTERFACESTRID (5)
# define USBADB_NSTRIDS (5)
#else
# define USBADB_INTERFACESTRID (1)
# define USBADB_NSTRIDS (1)
#endif
#define USBADB_NCONFIGS (1)
#ifdef CONFIG_USBFASTBOOT
# define USBADB_INTERFACEPROTOCOL (3)
#else
# define USBADB_INTERFACEPROTOCOL (1)
#endif
/****************************************************************************
* Private Data
****************************************************************************/
/* USB descriptor ***********************************************************/
#ifndef CONFIG_USBADB_COMPOSITE
static const struct usb_devdesc_s g_adb_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_USBADB_EP0MAXPACKET, /* Max packet size (ep0) */
.vendor = /* Vendor ID */
{
LSBYTE(CONFIG_USBADB_VENDORID),
MSBYTE(CONFIG_USBADB_VENDORID)
},
.product = /* Product ID */
{
LSBYTE(CONFIG_USBADB_PRODUCTID),
MSBYTE(CONFIG_USBADB_PRODUCTID)
},
.device = /* Device ID */
{
LSBYTE(USBADB_VERSIONNO),
MSBYTE(USBADB_VERSIONNO)
},
.imfgr = USBADB_MANUFACTURERSTRID, /* Manufacturer */
.iproduct = USBADB_PRODUCTSTRID, /* Product */
.serno = USBADB_SERIALSTRID, /* Serial number */
.nconfigs = USBADB_NCONFIGS, /* Number of configurations */
};
# ifdef CONFIG_USBDEV_DUALSPEED
static const struct usb_qualdesc_s g_adb_qualdesc =
{
USB_SIZEOF_QUALDESC, /* len */
USB_DESC_TYPE_DEVICEQUALIFIER, /* type */
{ /* usb */
LSBYTE(0x0200),
MSBYTE(0x0200)
},
0, /* classid */
0, /* subclass */
0, /* protocol */
CONFIG_USBADB_EP0MAXPACKET, /* mxpacketsize */
USBADB_NCONFIGS, /* nconfigs */
0, /* reserved */
};
# endif
static const struct usbdev_strdesc_s g_adb_strdesc[] =
{
{USBADB_MANUFACTURERSTRID, CONFIG_USBADB_VENDORSTR},
{USBADB_PRODUCTSTRID, CONFIG_USBADB_PRODUCTSTR},
# ifdef CONFIG_USBADB_SERIALSTR
{USBADB_SERIALSTRID, CONFIG_USBADB_SERIALSTR},
# else
{USBADB_SERIALSTRID, ""},
# endif
{USBADB_CONFIGSTRID, CONFIG_USBADB_CONFIGSTR},
{}
};
static const struct usbdev_strdescs_s g_adb_strdescs =
{
.language = USBADB_STR_LANGUAGE,
.strdesc = g_adb_strdesc,
};
static const struct usb_cfgdesc_s g_adb_cfgdesc =
{
.len = USB_SIZEOF_CFGDESC, /* Descriptor length */
.type = USB_DESC_TYPE_CONFIG, /* Descriptor type */
.cfgvalue = 1, /* Configuration value */
.icfg = USBADB_CONFIGSTRID, /* Configuration */
.attr = USB_CONFIG_ATTR_ONE |
USBADB_SELFPOWERED |
USBADB_REMOTEWAKEUP, /* Attributes */
.mxpower = (CONFIG_USBDEV_MAXPOWER + 1) / 2 /* Max power (mA/2) */
};
static const struct usbdev_devdescs_s g_adb_devdescs =
{
.cfgdesc = &g_adb_cfgdesc,
.strdescs = &g_adb_strdescs,
.devdesc = &g_adb_devdesc,
#ifdef CONFIG_USBDEV_DUALSPEED
.qualdesc = &g_adb_qualdesc,
#endif
};
#endif
static const struct usb_ifdesc_s g_adb_ifdesc =
{
.len = USB_SIZEOF_IFDESC,
.type = USB_DESC_TYPE_INTERFACE,
.ifno = 0,
.alt = 0,
.neps = 2,
.classid = USB_CLASS_VENDOR_SPEC,
.subclass = 0x42,
.protocol = USBADB_INTERFACEPROTOCOL,
.iif = USBADB_INTERFACESTRID
};
static const struct usbdev_epinfo_s g_adb_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,
},
.fssize = CONFIG_USBADB_EPBULKIN_FSSIZE,
#ifdef CONFIG_USBDEV_DUALSPEED
.hssize = CONFIG_USBADB_EPBULKIN_HSSIZE,
#endif
.reqnum = CONFIG_USBADB_NWRREQS,
};
static const struct usbdev_epinfo_s g_adb_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,
},
.fssize = CONFIG_USBADB_EPBULKOUT_FSSIZE,
#ifdef CONFIG_USBDEV_DUALSPEED
.hssize = CONFIG_USBADB_EPBULKOUT_HSSIZE,
#endif
.reqnum = CONFIG_USBADB_NRDREQS,
};
static const FAR struct usbdev_epinfo_s *g_adb_epinfos[USBADB_NUM_EPS] =
{
&g_adb_epbulkin,
&g_adb_epbulkout,
};
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: usbclass_mkcfgdesc
*
* Description:
* Construct the configuration descriptor
*
****************************************************************************/
#ifdef CONFIG_USBDEV_DUALSPEED
static int16_t usbclass_mkcfgdesc(FAR uint8_t *buf,
FAR struct usbdev_devinfo_s *devinfo,
uint8_t speed, uint8_t type)
#else
static int16_t usbclass_mkcfgdesc(FAR uint8_t *buf,
FAR struct usbdev_devinfo_s *devinfo)
#endif
{
bool hispeed = false;
FAR struct usb_epdesc_s *epdesc;
FAR struct usb_ifdesc_s *dest;
#ifdef CONFIG_USBDEV_DUALSPEED
hispeed = (speed == USB_SPEED_HIGH);
/* Check for switches between high and full speed */
if (type == USB_DESC_TYPE_OTHERSPEEDCONFIG)
{
hispeed = !hispeed;
}
#endif
dest = (FAR struct usb_ifdesc_s *)buf;
epdesc = (FAR struct usb_epdesc_s *)(buf + sizeof(g_adb_ifdesc));
memcpy(dest, &g_adb_ifdesc, sizeof(g_adb_ifdesc));
usbdev_copy_epdesc(&epdesc[0], devinfo->epno[USBADB_EP_BULKIN_IDX],
hispeed, &g_adb_epbulkin);
usbdev_copy_epdesc(&epdesc[1], devinfo->epno[USBADB_EP_BULKOUT_IDX],
hispeed, &g_adb_epbulkout);
#ifdef CONFIG_USBADB_COMPOSITE
/* For composite device, apply possible offset to the interface numbers */
dest->ifno = devinfo->ifnobase;
dest->iif = devinfo->strbase + USBADB_INTERFACESTRID;
#endif
return sizeof(g_adb_ifdesc) + 2 * USB_SIZEOF_EPDESC;
}
/****************************************************************************
* 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 USBADB_INTERFACESTRID:
str = CONFIG_USBADB_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 > (USBADB_MAXSTRLEN / 2))
{
len = (USBADB_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_USBADB_COMPOSITE
/****************************************************************************
* Name: usbdev_adb_initialize
*
* Description:
* Initialize the Android Debug Bridge USB device driver.
*
* Returned Value:
* A non-NULL "handle" is returned on success.
*
****************************************************************************/
FAR void *usbdev_adb_initialize(void)
{
struct composite_devdesc_s devdesc;
usbdev_adb_get_composite_devdesc(&devdesc);
devdesc.devinfo.epno[USBADB_EP_BULKIN_IDX] =
USB_EPNO(CONFIG_USBADB_EPBULKIN);
devdesc.devinfo.epno[USBADB_EP_BULKOUT_IDX] =
USB_EPNO(CONFIG_USBADB_EPBULKOUT);
return usbdev_fs_initialize(&g_adb_devdescs, &devdesc);
}
/****************************************************************************
* Name: usbdev_adb_uninitialize
*
* Description:
* Uninitialize the Android Debug Bridge USB device driver.
*
****************************************************************************/
void usbdev_adb_uninitialize(FAR void *handle)
{
usbdev_fs_uninitialize(handle);
}
#endif
/****************************************************************************
* Name: usbdev_adb_get_composite_devdesc
*
* Returned Value:
* None
*
****************************************************************************/
void usbdev_adb_get_composite_devdesc(FAR struct composite_devdesc_s *dev)
{
memset(dev, 0, sizeof(struct composite_devdesc_s));
dev->classobject = usbdev_fs_classobject;
dev->uninitialize = usbdev_fs_classuninitialize;
dev->mkconfdesc = usbclass_mkcfgdesc,
dev->mkstrdesc = usbclass_mkstrdesc,
dev->nconfigs = USBADB_NCONFIGS;
dev->configid = 1;
dev->cfgdescsize = sizeof(g_adb_ifdesc) + 2 * USB_SIZEOF_EPDESC;
dev->devinfo.ninterfaces = 1;
dev->devinfo.nstrings = USBADB_NSTRIDS;
dev->devinfo.nendpoints = USBADB_NUM_EPS;
dev->devinfo.epinfos = g_adb_epinfos;
dev->devinfo.name = USBADB_CHARDEV_PATH;
}