476 lines
14 KiB
C
476 lines
14 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,
|
|
},
|
|
.reqnum = CONFIG_USBADB_NWRREQS,
|
|
.fssize = CONFIG_USBADB_EPBULKIN_FSSIZE,
|
|
#ifdef CONFIG_USBDEV_DUALSPEED
|
|
.hssize = CONFIG_USBADB_EPBULKIN_HSSIZE,
|
|
#endif
|
|
#ifdef CONFIG_USBDEV_SUPERSPEED
|
|
.sssize = CONFIG_USBADB_EPBULKIN_SSSIZE,
|
|
.compdesc =
|
|
{
|
|
.len = USB_SIZEOF_SS_EPCOMPDESC,
|
|
.type = USB_DESC_TYPE_ENDPOINT_COMPANION,
|
|
.mxburst = CONFIG_USBADB_EPBULKIN_MAXBURST,
|
|
.attr = CONFIG_USBADB_EPBULKIN_MAXSTREAM,
|
|
.wbytes[0] = 0,
|
|
.wbytes[1] = 0,
|
|
},
|
|
#endif
|
|
};
|
|
|
|
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,
|
|
},
|
|
.reqnum = CONFIG_USBADB_NRDREQS,
|
|
.fssize = CONFIG_USBADB_EPBULKOUT_FSSIZE,
|
|
#ifdef CONFIG_USBDEV_DUALSPEED
|
|
.hssize = CONFIG_USBADB_EPBULKOUT_HSSIZE,
|
|
#endif
|
|
#ifdef CONFIG_USBDEV_SUPERSPEED
|
|
.sssize = CONFIG_USBADB_EPBULKOUT_SSSIZE,
|
|
.compdesc =
|
|
{
|
|
.len = USB_SIZEOF_SS_EPCOMPDESC,
|
|
.type = USB_DESC_TYPE_ENDPOINT_COMPANION,
|
|
.mxburst = CONFIG_USBADB_EPBULKOUT_MAXBURST,
|
|
.attr = CONFIG_USBADB_EPBULKOUT_MAXSTREAM,
|
|
.wbytes[0] = 0,
|
|
.wbytes[1] = 0,
|
|
},
|
|
#endif
|
|
};
|
|
|
|
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
|
|
*
|
|
****************************************************************************/
|
|
|
|
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_adb_ifdesc));
|
|
|
|
memcpy(dest, &g_adb_ifdesc, sizeof(g_adb_ifdesc));
|
|
totallen += sizeof(g_adb_ifdesc);
|
|
|
|
ret = usbdev_copy_epdesc((FAR struct usb_epdesc_s *)epdesc,
|
|
devinfo->epno[USBADB_EP_BULKIN_IDX],
|
|
speed,
|
|
&g_adb_epbulkin);
|
|
totallen += ret;
|
|
epdesc += ret;
|
|
|
|
ret = usbdev_copy_epdesc((FAR struct usb_epdesc_s *)epdesc,
|
|
devinfo->epno[USBADB_EP_BULKOUT_IDX],
|
|
speed,
|
|
&g_adb_epbulkout);
|
|
totallen += ret;
|
|
epdesc += ret;
|
|
|
|
#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 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 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;
|
|
#ifdef CONFIG_USBDEV_SUPERSPEED
|
|
dev->cfgdescsize = sizeof(g_adb_ifdesc) +
|
|
USB_SIZEOF_EPDESC * 2 +
|
|
USB_SIZEOF_SS_EPCOMPDESC * 2;
|
|
#else
|
|
dev->cfgdescsize = sizeof(g_adb_ifdesc) + 2 * USB_SIZEOF_EPDESC;
|
|
#endif
|
|
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;
|
|
}
|