| /**************************************************************************** |
| * 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; |
| } |