diff --git a/boards/arm/sama5/sama5d3-xplained/configs/bluetooth/defconfig b/boards/arm/sama5/sama5d3-xplained/configs/bluetooth/defconfig new file mode 100644 index 00000000000..b4439e18dcf --- /dev/null +++ b/boards/arm/sama5/sama5d3-xplained/configs/bluetooth/defconfig @@ -0,0 +1,102 @@ +# +# This file is autogenerated: PLEASE DO NOT EDIT IT. +# +# You can use "make menuconfig" to make any modifications to the installed .config file. +# You can then do "make savedefconfig" to generate a new defconfig file that includes your +# modifications. +# +# CONFIG_DEBUG_INFO is not set +# CONFIG_NET_ETHERNET is not set +# CONFIG_NET_IPv4 is not set +# CONFIG_NSH_ARGCAT is not set +# CONFIG_NSH_CMDOPT_HEXDUMP is not set +# CONFIG_SAMA5_UART0 is not set +# CONFIG_SAMA5_UHPHS_RHPORT1 is not set +CONFIG_ALLOW_BSD_COMPONENTS=y +CONFIG_ALLOW_MIT_COMPONENTS=y +CONFIG_ARCH="arm" +CONFIG_ARCH_BOARD="sama5d3-xplained" +CONFIG_ARCH_BOARD_SAMA5D3_XPLAINED=y +CONFIG_ARCH_CHIP="sama5" +CONFIG_ARCH_CHIP_ATSAMA5D36=y +CONFIG_ARCH_CHIP_SAMA5=y +CONFIG_ARCH_CHIP_SAMA5D3=y +CONFIG_ARCH_INTERRUPTSTACK=2048 +CONFIG_ARCH_LOWVECTORS=y +CONFIG_BLUETOOTH_MAXSCANRESULT=100 +CONFIG_BOARDCTL_RESET=y +CONFIG_BOARD_LOOPSPERMSEC=65775 +CONFIG_BOOT_RUNFROMSDRAM=y +CONFIG_BTSAK=y +CONFIG_BUILTIN=y +CONFIG_DEBUG_FEATURES=y +CONFIG_DEBUG_FULLOPT=y +CONFIG_DEBUG_NET=y +CONFIG_DEBUG_NET_ERROR=y +CONFIG_DEBUG_SYMBOLS=y +CONFIG_DEBUG_USB=y +CONFIG_DEBUG_USB_ERROR=y +CONFIG_DEBUG_USB_WARN=y +CONFIG_DEBUG_WIRELESS=y +CONFIG_DEBUG_WIRELESS_ERROR=y +CONFIG_DEBUG_WIRELESS_WARN=y +CONFIG_DISABLE_ENVIRON=y +CONFIG_DRIVERS_BLUETOOTH=y +CONFIG_DRIVERS_WIRELESS=y +CONFIG_EXAMPLES_HIDKBD=y +CONFIG_EXAMPLES_TOUCHSCREEN=y +CONFIG_EXAMPLES_TOUCHSCREEN_DEVPATH="/dev/mouse0" +CONFIG_EXAMPLES_TOUCHSCREEN_MOUSE=y +CONFIG_FAT_LCNAMES=y +CONFIG_FAT_LFN=y +CONFIG_FS_FAT=y +CONFIG_FS_PROCFS=y +CONFIG_HAVE_CXX=y +CONFIG_HAVE_CXXINITIALIZE=y +CONFIG_HIDKBD_NOGETREPORT=y +CONFIG_INIT_ENTRYPOINT="nsh_main" +CONFIG_INTELHEX_BINARY=y +CONFIG_NET=y +CONFIG_NET_BLUETOOTH=y +CONFIG_NET_LL_GUARDSIZE=14 +CONFIG_NSH_ARCHINIT=y +CONFIG_NSH_BUILTIN_APPS=y +CONFIG_NSH_FILEIOSIZE=512 +CONFIG_NSH_READLINE=y +CONFIG_PREALLOC_MQ_MSGS=32 +CONFIG_PREALLOC_TIMERS=4 +CONFIG_RAM_SIZE=268435456 +CONFIG_RAM_START=0x20000000 +CONFIG_RAM_VSTART=0x20000000 +CONFIG_RAW_BINARY=y +CONFIG_READLINE_CMD_HISTORY=y +CONFIG_READLINE_TABCOMPLETION=y +CONFIG_RR_INTERVAL=200 +CONFIG_SAMA5D3XPLAINED_528MHZ=y +CONFIG_SAMA5_BOOT_SDRAM=y +CONFIG_SAMA5_DBGU=y +CONFIG_SAMA5_DBGU_CONSOLE=y +CONFIG_SAMA5_DBGU_NOCONFIG=y +CONFIG_SAMA5_HSMC=y +CONFIG_SAMA5_OHCI=y +CONFIG_SAMA5_OHCI_NEDS=20 +CONFIG_SAMA5_OHCI_NTDS=20 +CONFIG_SAMA5_OHCI_TDBUFFERS=20 +CONFIG_SAMA5_OHCI_TDBUFSIZE=1280 +CONFIG_SAMA5_SYSTEMRESET=y +CONFIG_SAMA5_UHPHS=y +CONFIG_SCHED_WAITPID=y +CONFIG_START_DAY=31 +CONFIG_START_MONTH=7 +CONFIG_START_YEAR=2014 +CONFIG_SYMTAB_ORDEREDBYNAME=y +CONFIG_SYSTEM_NSH=y +CONFIG_USBHOST_BTHCI=y +CONFIG_USBHOST_COMPOSITE=y +CONFIG_USBHOST_HID=y +CONFIG_USBHOST_HIDKBD=y +CONFIG_USBHOST_HIDMOUSE=y +CONFIG_USBHOST_HUB=y +CONFIG_USBHOST_MSC=y +CONFIG_WIRELESS=y +CONFIG_WIRELESS_BLUETOOTH=y diff --git a/boards/arm/sama5/sama5d3-xplained/src/sam_usb.c b/boards/arm/sama5/sama5d3-xplained/src/sam_usb.c index 94c9ac27d55..1a4e760f582 100644 --- a/boards/arm/sama5/sama5d3-xplained/src/sam_usb.c +++ b/boards/arm/sama5/sama5d3-xplained/src/sam_usb.c @@ -302,6 +302,16 @@ int sam_usbhost_initialize(void) } #endif +#ifdef CONFIG_USBHOST_BTHCI + /* Register USB Bluetooth support */ + + ret = usbhost_bthci_initialize(); + if (ret != OK) + { + uerr("ERROR: Failed to register the bt controller: %d\n", ret); + } +#endif + #ifdef CONFIG_USBHOST_MSC /* Register theUSB host Mass Storage Class */ diff --git a/drivers/usbhost/Kconfig b/drivers/usbhost/Kconfig index 38d5aa40faa..f0ee0beec3a 100644 --- a/drivers/usbhost/Kconfig +++ b/drivers/usbhost/Kconfig @@ -721,4 +721,13 @@ config USBHOST_FT232R_LATENCY endif # USBHOST_FT232R +config USBHOST_BTHCI + bool "Bluetooth HCI Driver" + default n + depends on WIRELESS_BLUETOOTH && USBHOST_HAVE_ASYNCH && !USBHOST_BULK_DISABLE && !USBHOST_INT_DISABLE + select USBHOST_ASYNCH + select SERIAL_REMOVABLE + ---help--- + Select this option to build in support for USB Bluetooth HCI devices. + endif # USBHOST diff --git a/drivers/usbhost/Make.defs b/drivers/usbhost/Make.defs index 1aa8cb80909..02b4ffa8112 100644 --- a/drivers/usbhost/Make.defs +++ b/drivers/usbhost/Make.defs @@ -70,6 +70,10 @@ ifeq ($(CONFIG_USBHOST_FT232R),y) CSRCS += usbhost_ft232r.c endif +ifeq ($(CONFIG_USBHOST_BTHCI),y) +CSRCS += usbhost_bthci.c +endif + # HCD debug/trace logic ifeq ($(CONFIG_USBHOST_TRACE),y) diff --git a/drivers/usbhost/usbhost_bthci.c b/drivers/usbhost/usbhost_bthci.c new file mode 100644 index 00000000000..9e23f4f21e3 --- /dev/null +++ b/drivers/usbhost/usbhost_bthci.c @@ -0,0 +1,1536 @@ +/**************************************************************************** + * drivers/usbhost/usbhost_bthci.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 +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Configuration ************************************************************/ + +#ifndef CONFIG_SCHED_WORKQUEUE +# warning "Worker thread support is required (CONFIG_SCHED_WORKQUEUE)" +#endif + +/* Driver support ***********************************************************/ + +/* This format is used to construct the /dev/bthci[n] device driver path. It + * defined here so that it will be used consistently in all places. + */ + +#define DEV_FORMAT "/dev/bthci%c" +#define DEV_NAMELEN 12 + +/* Used in usbhci_cfgdesc() */ + +#define USBHOST_IFFOUND 0x01 +#define USBHOST_BINFOUND 0x02 +#define USBHOST_BOUTFOUND 0x04 +#define USBHOST_IINFOUND 0x08 +#define USBHOST_ALLFOUND 0x0F + +#define USBHOST_MAX_CREFS 0x7fff + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* This structure contains the internal, private state of the USB host class + * driver. + */ + +struct usbhost_state_s +{ + /* This is the externally visible portion of the state */ + + struct usbhost_class_s usbclass; + struct bt_driver_s btdev; + + /* The remainder of the fields are provide to the class driver */ + + char devchar; /* Character identifying the /dev/bthci[n] device */ + volatile bool disconnected; /* TRUE: Device has been disconnected */ + uint8_t ifno; /* Interface number */ + sem_t exclsem; /* Used to maintain mutual exclusive access */ + struct work_s work; /* For interacting with the worker thread */ + FAR uint8_t *tbuffer; /* The allocated transfer buffer */ + size_t tbuflen; /* Size of the allocated transfer buffer */ + usbhost_ep_t bulkin; /* Bulk IN endpoint */ + usbhost_ep_t bulkout; /* Bulk OUT endpoint */ + usbhost_ep_t intin; /* Interrupt endpoint */ + FAR uint8_t *ctrlreq; /* Allocated ctrl request structure */ + FAR uint8_t *evbuffer; /* Allocated event buffer */ + size_t evbuflen; /* Size of the allocated event buffer */ + struct work_s acwork; /* For asynchronous event work */ + struct work_s evwork; /* For asynchronous event work */ + int16_t acbytes; /* The number of bytes actually transferred */ + int16_t evbytes; /* The number of bytes actually transferred */ +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/* Memory allocation services */ + +static inline FAR struct usbhost_state_s *usbhost_allocclass(void); +static inline void usbhost_freeclass(FAR struct usbhost_state_s *usbclass); + +/* Device name management */ + +static int usbhost_allocdevno(FAR struct usbhost_state_s *priv); +static void usbhost_freedevno(FAR struct usbhost_state_s *priv); +static inline void usbhost_mkdevname(FAR struct usbhost_state_s *priv, + FAR char *devname); + +/* Worker thread actions */ + +static void usbhost_destroy(FAR void *arg); + +/* Helpers for usbhci_connect() */ + +static inline int usbhci_cfgdesc(FAR struct usbhost_state_s *priv, + FAR const uint8_t *configdesc, + int desclen); +static inline int usbhost_devinit(FAR struct usbhost_state_s *priv); + +/* (Little Endian) Data helpers */ + +static inline uint16_t usbhost_getle16(const uint8_t *val); +static inline void usbhost_putle16(uint8_t *dest, uint16_t val); + +/* Transfer descriptor memory management */ + +static inline int usbhost_talloc(FAR struct usbhost_state_s *priv); +static inline int usbhost_tfree(FAR struct usbhost_state_s *priv); + +/* struct usbhost_registry_s methods */ + +static struct usbhost_class_s * + usbhost_create(FAR struct usbhost_hubport_s *hport, + FAR const struct usbhost_id_s *id); + +/* struct usbhost_class_s methods */ + +static int usbhci_connect(FAR struct usbhost_class_s *usbclass, + FAR const uint8_t *configdesc, int desclen); +static int usbhost_disconnected(FAR struct usbhost_class_s *usbclass); + +/* Driver methods -- + * depend upon the type of NuttX driver interface exported + */ + +static int usbhost_ctrl_cmd(FAR struct usbhost_state_s *priv, + uint8_t type, uint8_t req, uint16_t value, + uint16_t iface, uint8_t *payload, uint16_t len); +static void usbhost_event_work(FAR void *arg); +static void usbhost_event_callback(FAR void *arg, ssize_t nbytes); +static void usbhost_acl_callback(FAR void *arg, ssize_t nbytes); + +static ssize_t usbhost_cmd_tx(FAR struct usbhost_state_s *priv, + FAR const void *buffer, size_t buflen); +static ssize_t usbhost_acl_tx(FAR struct usbhost_state_s *priv, + FAR const void *buffer, size_t buflen); + +static int usbhost_bthci_send(FAR struct bt_driver_s *dev, + enum bt_buf_type_e type, + FAR void *data, size_t len); +static int usbhost_bthci_open(FAR struct bt_driver_s *dev); +static void usbhost_bthci_close(FAR struct bt_driver_s *dev); +static int usbhost_bthci_ioctl(FAR struct bt_driver_s *dev, + int cmd, unsigned long arg); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* This structure provides the registry entry ID information that will be + * used to associate the USB class driver to a connected USB device. + */ + +static struct usbhost_id_s g_id[] = +{ + { + USB_CLASS_WIRELESS_CONTROLLER, /* base */ + 0x01, /* subclass */ + 0x01, /* proto */ + 0, /* vid */ + 0 /* pid */ + }, +}; + +/* This is the USB host bthci class's registry entry */ + +static struct usbhost_registry_s g_bthci = +{ + NULL, /* flink */ + usbhost_create, /* create */ + 1, /* nids */ + &g_id[0] /* id[] */ +}; + +/* This is a bitmap that is used to allocate device names /dev/bthcia-z. */ + +static uint32_t g_devinuse; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: usbhost_allocclass + * + * Description: + * This is really part of the logic that implements the create() method + * of struct usbhost_registry_s. This function allocates memory for one + * new class instance. + * + * Input Parameters: + * None + * + * Returned Value: + * On success, this function will return a non-NULL instance of struct + * usbhost_class_s. NULL is returned on failure; this function will + * will fail only if there are insufficient resources to create another + * USB host class instance. + * + ****************************************************************************/ + +static inline FAR struct usbhost_state_s *usbhost_allocclass(void) +{ + FAR struct usbhost_state_s *priv; + + DEBUGASSERT(!up_interrupt_context()); + + priv = (FAR struct usbhost_state_s *) + kmm_malloc(sizeof(struct usbhost_state_s)); + + uinfo("Allocated: %p\n", priv); + return priv; +} + +/**************************************************************************** + * Name: usbhost_freeclass + * + * Description: + * Free a class instance previously allocated by usbhost_allocclass(). + * + * Input Parameters: + * usbclass - A reference to the class instance to be freed. + * + * Returned Value: + * None + * + ****************************************************************************/ + +static inline void usbhost_freeclass(FAR struct usbhost_state_s *usbclass) +{ + DEBUGASSERT(usbclass != NULL); + + /* Free the class instance (perhaps calling sched_kmm_free() in case we are + * executing from an interrupt handler. + */ + + uinfo("Freeing: %p\n", usbclass); + kmm_free(usbclass); +} + +/**************************************************************************** + * Name: Device name management + * + * Description: + * Some tiny functions to coordinate management of device names. + * + ****************************************************************************/ + +static int usbhost_allocdevno(FAR struct usbhost_state_s *priv) +{ + irqstate_t flags; + int devno; + + flags = enter_critical_section(); + for (devno = 0; devno < 26; devno++) + { + uint32_t bitno = 1 << devno; + if ((g_devinuse & bitno) == 0) + { + g_devinuse |= bitno; + priv->devchar = 'a' + devno; + leave_critical_section(flags); + return OK; + } + } + + leave_critical_section(flags); + return -EMFILE; +} + +static void usbhost_freedevno(FAR struct usbhost_state_s *priv) +{ + if (priv->devchar >= 'a') + { + int devno = priv->devchar - 'a'; + + if (devno >= 0 && devno < 26) + { + irqstate_t flags = enter_critical_section(); + g_devinuse &= ~(1 << devno); + leave_critical_section(flags); + } + } +} + +static inline void usbhost_mkdevname(FAR struct usbhost_state_s *priv, + FAR char *devname) +{ + snprintf(devname, DEV_NAMELEN, DEV_FORMAT, priv->devchar); +} + +/**************************************************************************** + * Name: usbhost_destroy + * + * Description: + * The USB device has been disconnected and the reference count on the USB + * host class instance has gone to 1.. Time to destroy the USB host class + * instance. + * + * Input Parameters: + * arg - A reference to the class instance to be destroyed. + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void usbhost_destroy(FAR void *arg) +{ + FAR struct usbhost_state_s *priv = (FAR struct usbhost_state_s *)arg; + FAR struct usbhost_hubport_s *hport; + FAR struct usbhost_driver_s *drvr; + + DEBUGASSERT(priv != NULL && priv->usbclass.hport != NULL); + hport = priv->usbclass.hport; + + DEBUGASSERT(hport->drvr); + drvr = hport->drvr; + + /* Release the device name used by this connection */ + + usbhost_freedevno(priv); + + /* Free the endpoints */ + + if (priv->bulkin) + { + DRVR_EPFREE(hport->drvr, priv->bulkin); + } + + if (priv->bulkout) + { + DRVR_EPFREE(hport->drvr, priv->bulkout); + } + + /* Cancel any pending asynchronous I/O */ + + if (priv->intin) + { + DRVR_EPFREE(hport->drvr, priv->intin); + } + + /* Free any transfer buffers */ + + usbhost_tfree(priv); + + /* Free the function address assigned to this device */ + + usbhost_devaddr_destroy(hport, hport->funcaddr); + hport->funcaddr = 0; + + /* Destroy the semaphores */ + + nxsem_destroy(&priv->exclsem); + + /* Disconnect the USB host device */ + + DRVR_DISCONNECT(drvr, hport); + + /* Free the function address assigned to this device */ + + usbhost_devaddr_destroy(hport, hport->funcaddr); + hport->funcaddr = 0; + + /* And free the class instance. Hmmm.. this may execute on the worker + * thread and the work structure is part of what is getting freed. That + * should be okay because once the work contained is removed from the + * queue, it should not longer be accessed by the worker thread. + */ + + usbhost_freeclass(priv); +} + +/**************************************************************************** + * Name: usbhci_cfgdesc + * + * Description: + * This function implements the connect() method of struct + * usbhost_class_s. This method is a callback into the class + * implementation. It is used to provide the device's configuration + * descriptor to the class so that the class may initialize properly + * + * Input Parameters: + * priv - The USB host class instance. + * configdesc - A pointer to a uint8_t buffer container the configuration + * descriptor. + * desclen - The length in bytes of the configuration descriptor. + * + * Returned Value: + * On success, zero (OK) is returned. On a failure, a negated errno value + * is returned indicating the nature of the failure + * + * Assumptions: + * This function will *not* be called from an interrupt handler. + * + ****************************************************************************/ + +static inline int usbhci_cfgdesc(FAR struct usbhost_state_s *priv, + FAR const uint8_t *configdesc, int desclen) +{ + FAR struct usbhost_hubport_s *hport; + FAR struct usb_cfgdesc_s *cfgdesc; + FAR struct usb_desc_s *desc; + FAR struct usbhost_epdesc_s bindesc; + FAR struct usbhost_epdesc_s boutdesc; + FAR struct usbhost_epdesc_s iindesc; + int remaining; + uint8_t found = 0; + int ret; + + DEBUGASSERT(priv != NULL && priv->usbclass.hport && + configdesc != NULL && desclen >= sizeof(struct usb_cfgdesc_s)); + hport = priv->usbclass.hport; + + /* Verify that we were passed a configuration descriptor */ + + cfgdesc = (FAR struct usb_cfgdesc_s *)configdesc; + if (cfgdesc->type != USB_DESC_TYPE_CONFIG) + { + return -EINVAL; + } + + /* Get the total length of the configuration descriptor (little endian). + * It might be a good check to get the number of interfaces here too. + */ + + remaining = (int)usbhost_getle16(cfgdesc->totallen); + + /* Skip to the next entry descriptor */ + + configdesc += cfgdesc->len; + remaining -= cfgdesc->len; + + /* Loop where there are more dscriptors to examine */ + + while (remaining >= sizeof(struct usb_desc_s)) + { + /* What is the next descriptor? */ + + desc = (FAR struct usb_desc_s *)configdesc; + switch (desc->type) + { + /* Interface descriptor. We really should get the number of endpoints + * from this descriptor too. + */ + + case USB_DESC_TYPE_INTERFACE: + { + FAR struct usb_ifdesc_s *ifdesc = + (FAR struct usb_ifdesc_s *)configdesc; + + DEBUGASSERT(remaining >= USB_SIZEOF_IFDESC); + + /* Save the interface number and mark ONLY the interface found */ + + if (ifdesc->ifno == 0) + { + priv->ifno = ifdesc->ifno; + found |= USBHOST_IFFOUND; + } + } + break; + + /* Endpoint descriptor. Here, we expect two bulk endpoints, an IN + * and an OUT. + */ + + case USB_DESC_TYPE_ENDPOINT: + { + FAR struct usb_epdesc_s *epdesc = + (FAR struct usb_epdesc_s *)configdesc; + + DEBUGASSERT(remaining >= USB_SIZEOF_EPDESC); + + if ((epdesc->attr & USB_EP_ATTR_XFERTYPE_MASK) == + USB_EP_ATTR_XFER_INT) + { + if (USB_ISEPIN(epdesc->addr)) + { + iindesc.hport = hport; + iindesc.addr = epdesc->addr & + USB_EP_ADDR_NUMBER_MASK; + iindesc.in = true; + iindesc.xfrtype = USB_EP_ATTR_XFER_INT; + iindesc.interval = epdesc->interval; + iindesc.mxpacketsize = + usbhost_getle16(epdesc->mxpacketsize); + + if (iindesc.addr == 1) + { + found |= USBHOST_IINFOUND; + } + } + } + + /* Check for a bulk endpoint. */ + + if ((epdesc->attr & USB_EP_ATTR_XFERTYPE_MASK) == + USB_EP_ATTR_XFER_BULK) + { + /* Yes.. it is a bulk endpoint. IN or OUT? */ + + if (USB_ISEPOUT(epdesc->addr)) + { + /* It is an OUT bulk endpoint. There should be only one + * bulk OUT endpoint. + */ + + if ((found & USBHOST_BOUTFOUND) != 0) + { + /* Oops.. more than one endpoint. We don't know + * what to do with this. + */ + + return -EINVAL; + } + + /* Save the bulk OUT endpoint information */ + + boutdesc.hport = hport; + boutdesc.addr = epdesc->addr & + USB_EP_ADDR_NUMBER_MASK; + boutdesc.in = false; + boutdesc.xfrtype = USB_EP_ATTR_XFER_BULK; + boutdesc.interval = epdesc->interval; + boutdesc.mxpacketsize = + usbhost_getle16(epdesc->mxpacketsize); + + if (boutdesc.addr == 2) + { + found |= USBHOST_BOUTFOUND; + } + } + else + { + /* It is an IN bulk endpoint. There should be only one + * bulk IN endpoint. + */ + + if ((found & USBHOST_BINFOUND) != 0) + { + /* Oops.. more than one endpoint. We don't know + * what to do with this. + */ + + return -EINVAL; + } + + /* Save the bulk IN endpoint information */ + + bindesc.hport = hport; + bindesc.addr = epdesc->addr & + USB_EP_ADDR_NUMBER_MASK; + bindesc.in = 1; + bindesc.xfrtype = USB_EP_ATTR_XFER_BULK; + bindesc.interval = epdesc->interval; + bindesc.mxpacketsize = + usbhost_getle16(epdesc->mxpacketsize); + + if (bindesc.addr == 2) + { + found |= USBHOST_BINFOUND; + } + } + } + } + break; + + /* Other descriptors are just ignored for now */ + + default: + break; + } + + /* If we found everything we need with this interface, then break out + * of the loop early. + */ + + if (found == USBHOST_ALLFOUND) + { + break; + } + + /* Increment the address of the next descriptor */ + + configdesc += desc->len; + remaining -= desc->len; + } + + /* Sanity checking... did we find all of things that we need? */ + + if (found != USBHOST_ALLFOUND) + { + uerr("ERROR: Found IF:%s IIN:%s BIN:%s BOUT:%s\n", + (found & USBHOST_IFFOUND) != 0 ? "YES" : "NO", + (found & USBHOST_IINFOUND) != 0 ? "YES" : "NO", + (found & USBHOST_BINFOUND) != 0 ? "YES" : "NO", + (found & USBHOST_BOUTFOUND) != 0 ? "YES" : "NO"); + return -EINVAL; + } + + /* We are good... Allocate the endpoints */ + + ret = DRVR_EPALLOC(hport->drvr, &boutdesc, &priv->bulkout); + if (ret < 0) + { + uerr("ERROR: Failed to allocate Bulk OUT endpoint\n"); + return ret; + } + + ret = DRVR_EPALLOC(hport->drvr, &bindesc, &priv->bulkin); + if (ret < 0) + { + uerr("ERROR: Failed to allocate Bulk IN endpoint\n"); + DRVR_EPFREE(hport->drvr, priv->bulkout); + return ret; + } + + ret = DRVR_EPALLOC(hport->drvr, &iindesc, &priv->intin); + if (ret < 0) + { + uerr("ERROR: Failed to allocate Interrupt IN endpoint\n"); + (void)DRVR_EPFREE(hport->drvr, priv->bulkout); + (void)DRVR_EPFREE(hport->drvr, priv->bulkin); + return ret; + } + + uinfo("Endpoints allocated\n"); + return OK; +} + +/**************************************************************************** + * Name: usbhost_ctrl_cmd + * + * Description: + * Do a USB control transfer. + * + * Input Parameters: + * priv - A reference to the USB host class instance. + * type - Transfer flags. + * req - control transfer type. + * value - Value for control transfer. + * index - Index for control transfer. + * payload - Data buffer. + * len - Length of data buffer. + * + * Returned Value: + * 0 on success. Negated errno on failure. + * + ****************************************************************************/ + +static int usbhost_ctrl_cmd(FAR struct usbhost_state_s *priv, + uint8_t type, uint8_t req, uint16_t value, + uint16_t indx, uint8_t *payload, uint16_t len) +{ + FAR struct usbhost_hubport_s *hport; + struct usb_ctrlreq_s *ctrlreq; + int ret; + + hport = priv->usbclass.hport; + + ctrlreq = (struct usb_ctrlreq_s *)priv->ctrlreq; + ctrlreq->type = type; + ctrlreq->req = req; + + usbhost_putle16(ctrlreq->value, value); + usbhost_putle16(ctrlreq->index, indx); + usbhost_putle16(ctrlreq->len, len); + + if (type & USB_REQ_DIR_IN) + { + ret = DRVR_CTRLIN(hport->drvr, hport->ep0, ctrlreq, payload); + } + else + { + ret = DRVR_CTRLOUT(hport->drvr, hport->ep0, ctrlreq, payload); + } + + return ret; +} + +/**************************************************************************** + * Name: usbhost_event_callback + * + * Description: + * Handle event packet reception + * + * Input Parameters: + * arg - The argument provided when the asynchronous I/O was setup + * nbytes - The number of bytes actually transferred (or a negated errno + * value). + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void usbhost_event_callback(FAR void *arg, ssize_t nbytes) +{ + FAR struct usbhost_state_s *priv; + + priv = (FAR struct usbhost_state_s *)arg; + DEBUGASSERT(priv); + + if (priv->intin) + { + priv->evbytes = (int16_t)nbytes; + + if (priv->evbytes >= 0 && work_available(&priv->evwork)) + { + work_queue(LPWORK, &priv->evwork, + usbhost_event_work, + priv, 0); + } + } +} + +/**************************************************************************** + * Name: usbhost_event_work + * + * Description: + * Receive bluetooth events. + * + * Input Parameters: + * arg - A reference to the USB host class instance. + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void usbhost_event_work(FAR void *arg) +{ + FAR struct usbhost_state_s *priv; + FAR struct usbhost_hubport_s *hport; + int ret; + + priv = (FAR struct usbhost_state_s *)arg; + DEBUGASSERT(priv); + + hport = priv->usbclass.hport; + DEBUGASSERT(hport); + + if (priv->evbytes > 0) + { + bt_netdev_receive(&priv->btdev, BT_EVT, priv->evbuffer, + priv->evbytes); + priv->evbytes = 0; + } + + if (priv->intin) + { + ret = DRVR_ASYNCH(hport->drvr, priv->intin, + (FAR uint8_t *)priv->evbuffer, + priv->evbuflen, usbhost_event_callback, + priv); + if (ret < 0) + { + uerr("ERROR: DRVR_ASYNCH failed: %d\n", ret); + } + } +} + +/**************************************************************************** + * Name: usbhost_cmd_tx + * + * Description: + * Send a bluetooth HCI command. + * + * Input Parameters: + * priv - A reference to the USB host class instance. + * buffer - The buffer to be sent by the driver. + * len - The length of the buffer. + * + * Returned Value: + * Zero is returned on success; a negated errno value is returned on any + * failure. + * + ****************************************************************************/ + +static ssize_t usbhost_cmd_tx(FAR struct usbhost_state_s *priv, + FAR const void *buffer, size_t buflen) +{ + int ret; + + nxsem_wait_uninterruptible(&priv->exclsem); + + ret = usbhost_ctrl_cmd(priv, + USB_REQ_DIR_OUT | USB_REQ_TYPE_CLASS | + USB_REQ_RECIPIENT_DEVICE, + 0, 0, 0, (uint8_t *)buffer, buflen); + + nxsem_post(&priv->exclsem); + + return ret; +} + +/**************************************************************************** + * Name: usbhost_acl_work + * + * Description: + * Receive bluetooth ACL packets. + * + * Input Parameters: + * arg - A reference to the USB host class instance. + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void usbhost_acl_work(FAR void *arg) +{ + FAR struct usbhost_state_s *priv; + FAR struct usbhost_hubport_s *hport; + int ret; + + priv = (FAR struct usbhost_state_s *)arg; + DEBUGASSERT(priv); + + hport = priv->usbclass.hport; + DEBUGASSERT(hport); + + if (priv->acbytes > 0) + { + bt_netdev_receive(&priv->btdev, BT_ACL_IN, priv->tbuffer, + priv->acbytes); + priv->acbytes = 0; + } + + if (priv->bulkin) + { + ret = DRVR_ASYNCH(hport->drvr, priv->bulkin, + (FAR uint8_t *)priv->tbuffer, + priv->tbuflen, usbhost_acl_callback, + priv); + if (ret < 0) + { + uerr("ERROR: DRVR_ASYNCH failed: %d\n", ret); + } + } +} + +/**************************************************************************** + * Name: usbhost_acl_callback + * + * Description: + * Handle ACL packet reception + * + * Input Parameters: + * arg - The argument provided when the asynchronous I/O was setup + * nbytes - The number of bytes actually transferred (or a negated errno + * value). + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void usbhost_acl_callback(FAR void *arg, ssize_t nbytes) +{ + FAR struct usbhost_state_s *priv; + + priv = (FAR struct usbhost_state_s *)arg; + DEBUGASSERT(priv); + + if (priv->bulkin) + { + priv->acbytes = (int16_t)nbytes; + + if (priv->acbytes >= 0 && work_available(&priv->acwork)) + { + work_queue(LPWORK, &priv->acwork, usbhost_acl_work, priv, 0); + } + } +} + +/**************************************************************************** + * Name: usbhost_acl_tx + * + * Description: + * Send a bluetooth ACL packet. + * + * Input Parameters: + * priv - A reference to the USB host class instance. + * buffer - The buffer to be sent by the driver. + * len - The length of the buffer. + * + * Returned Value: + * Zero is returned on success; a negated errno value is returned on any + * failure. + * + ****************************************************************************/ + +static ssize_t usbhost_acl_tx(FAR struct usbhost_state_s *priv, + FAR const void *buffer, size_t buflen) +{ + ssize_t nwritten = 0; + FAR struct usbhost_hubport_s *hport; + hport = priv->usbclass.hport; + DEBUGASSERT(hport); + + nxsem_wait_uninterruptible(&priv->exclsem); + + nwritten = DRVR_TRANSFER(hport->drvr, priv->bulkout, + (uint8_t *)buffer, buflen); + + if (nwritten < 0) + { + uerr("ERROR: DRVR_TRANSFER for ACL failed: %d\n", (int)nwritten); + } + else + { + nwritten = OK; + } + + nxsem_post(&priv->exclsem); + + return nwritten; +} + +/**************************************************************************** + * Name: usbhost_bthci_send + * + * Description: + * Send the packet in the provided buffer. + * + * Input Parameters: + * btdev - An instance of the BT low-level driver's interface structure. + * type - The type of packet in the buffer. + * data - The buffer to be sent by the driver. + * len - The length of the buffer. + * + * Returned Value: + * Zero is returned on success; a negated errno value is returned on any + * failure. + * + ****************************************************************************/ + +static int usbhost_bthci_send(FAR struct bt_driver_s *dev, + enum bt_buf_type_e type, + FAR void *data, size_t len) +{ + int ret; + FAR struct usbhost_state_s *priv; + priv = container_of(dev, struct usbhost_state_s, btdev); + + if (type == BT_CMD) + { + ret = usbhost_cmd_tx(priv, data, len); + } + else if (type == BT_ACL_OUT) + { + ret = usbhost_acl_tx(priv, data, len); + } + else + { + ret = -EINVAL; + uerr("ERROR: unexpected packet type %x\n", type); + } + + return ret; +} + +/**************************************************************************** + * Name: usbhost_bthci_open + * + * Description: + * Initialize the bluetooth hardware. + * + * Input Parameters: + * dev - An instance of the BT low-level driver's interface structure. + * + * Returned Value: + * Zero is returned on success; a negated errno value is returned on any + * failure. + * + ****************************************************************************/ + +static int usbhost_bthci_open(FAR struct bt_driver_s *dev) +{ + return OK; +} + +/**************************************************************************** + * Name: usbhost_bthci_close + * + * Description: + * Close the bluetooth hardware. + * + * Input Parameters: + * dev - An instance of the BT low-level driver's interface structure. + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void usbhost_bthci_close(FAR struct bt_driver_s *dev) +{ +} + +/**************************************************************************** + * Name: usbhost_bthci_ioctl + * + * Description: + * Handle IOCTL commands directed to this device. + * + * Input Parameters: + * dev - An instance of the BT low-level driver's interface structure. + * cmd - The IOCTL command + * arg - The argument for the IOCTL command + * + * Returned Value: + * Zero is returned on success; a negated errno value is returned on any + * failure. + * + ****************************************************************************/ + +static int usbhost_bthci_ioctl(FAR struct bt_driver_s *dev, + int cmd, unsigned long arg) +{ + return -ENOTTY; +} + +/**************************************************************************** + * Name: usbhost_devinit + * + * Description: + * The USB device has been successfully connected. This completes the + * initialization operations. It is first called after the + * configuration descriptor has been received. + * + * This function is called from the connect() method. This function always + * executes on the thread of the caller of connect(). + * + * Input Parameters: + * priv - A reference to the class instance. + * + * Returned Value: + * None + * + ****************************************************************************/ + +static inline int usbhost_devinit(FAR struct usbhost_state_s *priv) +{ + int ret = OK; + + if (priv->bulkin && work_available(&priv->acwork)) + { + /* Begin monitoring of acl packets */ + + work_queue(LPWORK, &priv->acwork, usbhost_acl_work, priv, 0); + } + + if (priv->intin && work_available(&priv->evwork)) + { + /* Begin monitoring of message available events */ + + work_queue(LPWORK, &priv->evwork, usbhost_event_work, priv, 0); + } + + if (ret >= 0 && priv->devchar == 'a') + { + /* Register the driver with the network stack. */ + + ret = bt_netdev_register(&priv->btdev); + if (ret < 0) + { + uerr("ERROR: bt_netdev_register failed: %d\n", ret); + } + } + + /* Check if we successfully initialized. */ + + if (ret >= 0) + { + ret = nxsem_wait_uninterruptible(&priv->exclsem); + if (ret < 0) + { + return ret; + } + + /* Ready for normal operation */ + + uinfo("Successfully initialized\n"); + + nxsem_post(&priv->exclsem); + } + + return ret; +} + +/**************************************************************************** + * Name: usbhost_getle16 + * + * Description: + * Get a (possibly unaligned) 16-bit little endian value. + * + * Input Parameters: + * val - A pointer to the first byte of the little endian value. + * + * Returned Value: + * A uint16_t representing the whole 16-bit integer value + * + ****************************************************************************/ + +static inline uint16_t usbhost_getle16(const uint8_t *val) +{ + return (uint16_t)val[1] << 8 | (uint16_t)val[0]; +} + +/**************************************************************************** + * Name: usbhost_putle16 + * + * Description: + * Put a (possibly unaligned) 16-bit little endian value. + * + * Input Parameters: + * dest - A pointer to the first byte to save the little endian value. + * val - The 16-bit value to be saved. + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void usbhost_putle16(uint8_t *dest, uint16_t val) +{ + /* Little endian means LSB first in byte stream */ + + dest[0] = val & 0xff; + dest[1] = val >> 8; +} + +/**************************************************************************** + * Name: usbhost_talloc + * + * Description: + * Allocate transfer buffer memory. + * + * Input Parameters: + * priv - A reference to the class instance. + * + * Returned Value: + * On success, zero (OK) is returned. On failure, an negated errno value + * is returned to indicate the nature of the failure. + * + ****************************************************************************/ + +static inline int usbhost_talloc(FAR struct usbhost_state_s *priv) +{ + FAR struct usbhost_hubport_s *hport; + size_t maxlen; + int ret; + + DEBUGASSERT(priv != NULL && priv->usbclass.hport != NULL && + priv->tbuffer == NULL); + hport = priv->usbclass.hport; + + /* Allocate buffer for events. */ + + if (priv->intin) + { + ret = DRVR_ALLOC(hport->drvr, &priv->evbuffer, &priv->evbuflen); + if (ret < 0) + { + uerr("ERROR: DRVR_IOALLOC of evbuffer failed: %d\n", ret); + return ret; + } + } + + /* Allocate memory for control requests */ + + ret = DRVR_ALLOC(hport->drvr, (FAR uint8_t **)&priv->ctrlreq, &maxlen); + if (ret < 0) + { + uerr("ERROR: DRVR_ALLOC of ctrlreq failed: %d\n", ret); + + if (priv->evbuffer) + { + DRVR_FREE(hport->drvr, priv->evbuffer); + priv->evbuffer = NULL; + priv->evbuflen = 0; + } + + return ret; + } + + DEBUGASSERT(maxlen >= sizeof(struct usb_ctrlreq_s)); + + return DRVR_ALLOC(hport->drvr, &priv->tbuffer, &priv->tbuflen); +} + +/**************************************************************************** + * Name: usbhost_tfree + * + * Description: + * Free transfer buffer memory. + * + * Input Parameters: + * priv - A reference to the class instance. + * + * Returned Value: + * On success, zero (OK) is returned. On failure, an negated errno value + * is returned to indicate the nature of the failure. + * + ****************************************************************************/ + +static inline int usbhost_tfree(FAR struct usbhost_state_s *priv) +{ + FAR struct usbhost_hubport_s *hport; + int result = OK; + + DEBUGASSERT(priv != NULL && priv->usbclass.hport != NULL); + hport = priv->usbclass.hport; + + if (priv->evbuffer) + { + DRVR_FREE(hport->drvr, priv->evbuffer); + priv->evbuffer = NULL; + priv->evbuflen = 0; + } + + if (priv->ctrlreq) + { + DRVR_FREE(hport->drvr, priv->ctrlreq); + priv->ctrlreq = NULL; + } + + if (priv->tbuffer) + { + result = DRVR_FREE(hport->drvr, priv->tbuffer); + priv->tbuffer = NULL; + priv->tbuflen = 0; + } + + return result; +} + +/**************************************************************************** + * struct usbhost_registry_s methods + ****************************************************************************/ + +/**************************************************************************** + * Name: usbhost_create + * + * Description: + * This function implements the create() method of struct + * usbhost_registry_s. The create() method is a callback into the class + * implementation. It is used to (1) create a new instance of the USB + * host class state and to (2) bind a USB host driver "session" to the + * class instance. Use of this create() method will support environments + * where there may be multiple USB ports and multiple USB devices + * simultaneously connected. + * + * Input Parameters: + * hport - The hub port that manages the new class instance. + * id - In the case where the device supports multiple base classes, + * subclasses, or protocols, this specifies which to configure for. + * + * Returned Value: + * On success, this function will return a non-NULL instance of struct + * usbhost_class_s that can be used by the USB host driver to communicate + * with the USB host class. NULL is returned on failure; this function + * will fail only if the hport input parameter is NULL or if there are + * insufficient resources to create another USB host class instance. + * + ****************************************************************************/ + +static FAR struct usbhost_class_s * + usbhost_create(FAR struct usbhost_hubport_s *hport, + FAR const struct usbhost_id_s *id) +{ + FAR struct usbhost_state_s *priv; + + /* Allocate a USB host class instance */ + + priv = usbhost_allocclass(); + if (priv) + { + /* Initialize the allocated storage class instance */ + + memset(priv, 0, sizeof(struct usbhost_state_s)); + + /* Assign a device number to this class instance */ + + if (usbhost_allocdevno(priv) == OK) + { + /* Initialize class method function pointers */ + + priv->usbclass.hport = hport; + priv->usbclass.connect = usbhci_connect; + priv->usbclass.disconnected = usbhost_disconnected; + + priv->btdev.head_reserve = 0; + priv->btdev.open = usbhost_bthci_open; + priv->btdev.send = usbhost_bthci_send; + priv->btdev.close = usbhost_bthci_close; + priv->btdev.ioctl = usbhost_bthci_ioctl; + + /* Initialize semaphores + * (this works okay in the interrupt context) + */ + + nxsem_init(&priv->exclsem, 0, 1); + + /* Return the instance of the USB class driver */ + + return &priv->usbclass; + } + } + + /* An error occurred. Free the allocation and return NULL on all failures */ + + if (priv) + { + usbhost_freeclass(priv); + } + + return NULL; +} + +/**************************************************************************** + * struct usbhost_class_s methods + ****************************************************************************/ + +/**************************************************************************** + * Name: usbhci_connect + * + * Description: + * This function implements the connect() method of struct + * usbhost_class_s. This method is a callback into the class + * implementation. It is used to provide the device's configuration + * descriptor to the class so that the class may initialize properly + * + * Input Parameters: + * usbclass - The USB host class entry previously obtained from a call to + * create(). + * configdesc - A pointer to a uint8_t buffer container the configuration + * descriptor. + * desclen - The length in bytes of the configuration descriptor. + * + * Returned Value: + * On success, zero (OK) is returned. On a failure, a negated errno value + * is returned indicating the nature of the failure + * + * NOTE that the class instance remains valid upon return with a failure. + * It is the responsibility of the higher level enumeration logic to call + * CLASS_DISCONNECTED to free up the class driver resources. + * + * Assumptions: + * - This function will *not* be called from an interrupt handler. + * - If this function returns an error, the USB host controller driver + * must call to DISCONNECTED method to recover from the error + * + ****************************************************************************/ + +static int usbhci_connect(FAR struct usbhost_class_s *usbclass, + FAR const uint8_t *configdesc, int desclen) +{ + FAR struct usbhost_state_s *priv = (FAR struct usbhost_state_s *)usbclass; + int ret; + + DEBUGASSERT(priv != NULL && + configdesc != NULL && + desclen >= sizeof(struct usb_cfgdesc_s)); + + /* Parse the configuration descriptor to get the endpoints */ + + ret = usbhci_cfgdesc(priv, configdesc, desclen); + if (ret < 0) + { + uerr("ERROR: usbhci_cfgdesc() failed: %d\n", ret); + } + else + { + ret = usbhost_talloc(priv); + if (ret < 0) + { + uerr("ERROR: Failed to allocate transfer buffer\n"); + return ret; + } + + /* Now configure the device and register the NuttX driver */ + + ret = usbhost_devinit(priv); + if (ret < 0) + { + uerr("ERROR: usbhost_devinit() failed: %d\n", ret); + } + } + + return ret; +} + +/**************************************************************************** + * Name: usbhost_disconnected + * + * Description: + * This function implements the disconnected() method of struct + * usbhost_class_s. This method is a callback into the class + * implementation. It is used to inform the class that the USB device has + * been disconnected. + * + * Input Parameters: + * usbclass - The USB host class entry previously obtained from a call to + * create(). + * + * Returned Value: + * On success, zero (OK) is returned. On a failure, a negated errno value + * is returned indicating the nature of the failure + * + * Assumptions: + * This function may be called from an interrupt handler. + * + ****************************************************************************/ + +static int usbhost_disconnected(struct usbhost_class_s *usbclass) +{ + FAR struct usbhost_state_s *priv = (FAR struct usbhost_state_s *)usbclass; + irqstate_t flags; + + DEBUGASSERT(priv != NULL); + + /* Set an indication to any users of the device that the device is no + * longer available. + */ + + flags = enter_critical_section(); + + priv->disconnected = true; + + if (priv->devchar == 'a') + { + bt_netdev_unregister(&priv->btdev); + } + + /* Cancel any ongoing transfers */ + + work_cancel(LPWORK, &priv->acwork); + work_cancel(LPWORK, &priv->evwork); + + /* Destroy the class instance. If we are executing from an interrupt + * handler, then defer the destruction to the worker thread. + * Otherwise, destroy the instance now. + */ + + if (up_interrupt_context()) + { + /* Destroy the instance on the worker thread. */ + + DEBUGASSERT(priv->work.worker == NULL); + work_queue(LPWORK, &priv->work, usbhost_destroy, priv, 0); + } + else + { + /* Do the work now */ + + usbhost_destroy(priv); + } + + leave_critical_section(flags); + return OK; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: usbhost_bthci_initialize + * + * Description: + * Initialize the USB class driver. This function should be called + * be platform-specific code in order to initialize and register support + * for the USB host class device. + * + * Input Parameters: + * None + * + * Returned Value: + * On success this function will return zero (OK); A negated errno value + * will be returned on failure. + * + ****************************************************************************/ + +int usbhost_bthci_initialize(void) +{ + /* Advertise our availability to support (certain) devices */ + + return usbhost_registerclass(&g_bthci); +} diff --git a/include/nuttx/usb/usbhost.h b/include/nuttx/usb/usbhost.h index b0bc71e6cda..c5a1110d95c 100644 --- a/include/nuttx/usb/usbhost.h +++ b/include/nuttx/usb/usbhost.h @@ -1013,7 +1013,7 @@ const struct usbhost_registry_s * * * Description: * Initialize the USB hub class. This function should be called - * be platform-specific code in order to initialize and register support + * by platform-specific code in order to initialize and register support * for the USB host storage class. * * Input Parameters: @@ -1034,7 +1034,7 @@ int usbhost_hub_initialize(void); * * Description: * Initialize the USB host storage class. This function should be called - * be platform-specific code in order to initialize and register support + * by platform-specific code in order to initialize and register support * for the USB host storage class. * * Input Parameters: @@ -1126,7 +1126,7 @@ void usbhost_msc_notifier_signal(uint8_t event, char sdchar); * * Description: * Initialize the USB host CDC/ACM class. This function should be called - * be platform-specific code in order to initialize and register support + * by platform-specific code in order to initialize and register support * for the USB host CDC/ACM class. * * Input Parameters: @@ -1147,7 +1147,7 @@ int usbhost_cdcacm_initialize(void); * * Description: * Initialize the USB FT232R driver. This function should be called - * be platform-specific code in order to initialize and register support + * by platform-specific code in order to initialize and register support * for the FT232R. * * Input Parameters: @@ -1168,7 +1168,7 @@ int usbhost_ft232r_initialize(void); * * Description: * Initialize the USB storage HID keyboard class driver. This function - * should be called be platform-specific code in order to initialize and + * should be called by platform-specific code in order to initialize and * register support for the USB host HID keyboard class device. * * Input Parameters: @@ -1189,7 +1189,7 @@ int usbhost_kbdinit(void); * * Description: * Initialize the USB storage HID mouse class driver. This function - * should be called be platform-specific code in order to initialize and + * should be called by platform-specific code in order to initialize and * register support for the USB host HID mouse class device. * * Input Parameters: @@ -1210,7 +1210,7 @@ int usbhost_mouse_init(void); * * Description: * Initialize the USB XBox controller driver. This function - * should be called be platform-specific code in order to initialize and + * should be called by platform-specific code in order to initialize and * register support for the USB XBox controller. * * Input Parameters: @@ -1225,12 +1225,33 @@ int usbhost_mouse_init(void); int usbhost_xboxcontroller_init(void); #endif +/**************************************************************************** + * Name: usbhost_bthci_initialize + * + * Description: + * Initialize the USB Bluetooth HCI class driver. This function should be + * called by platform-specific code in order to initialize and register + * support for the USB host class device. + * + * Input Parameters: + * None + * + * Returned Value: + * On success this function will return zero (OK); A negated errno value + * will be returned on failure. + * + ****************************************************************************/ + +#ifdef CONFIG_USBHOST_BTHCI +int usbhost_bthci_initialize(void); +#endif + /**************************************************************************** * Name: usbhost_wlaninit * * Description: * Initialize the USB WLAN class driver. This function should be called - * be platform-specific code in order to initialize and register support + * by platform-specific code in order to initialize and register support * for the USB host class device. * * Input Parameters: