From 19d7c90d4e487bf711bec2ebffbbd35f014cf927 Mon Sep 17 00:00:00 2001 From: Gregory Nutt Date: Sun, 18 Aug 2013 14:31:57 -0600 Subject: [PATCH] USB host: Add device address management support in preparation for USB hub support --- ChangeLog | 5 +- arch/arm/src/sama5/chip/sam_ehci.h | 2 +- drivers/usbhost/Make.defs | 3 +- drivers/usbhost/usbhost_devaddr.c | 333 ++++++++++++++++++++++++++++ include/nuttx/usb/pl2303.h | 2 +- include/nuttx/usb/usbhost_devaddr.h | 150 +++++++++++++ 6 files changed, 491 insertions(+), 4 deletions(-) create mode 100755 drivers/usbhost/usbhost_devaddr.c create mode 100644 include/nuttx/usb/usbhost_devaddr.h diff --git a/ChangeLog b/ChangeLog index e6dc2994f1..d123596312 100644 --- a/ChangeLog +++ b/ChangeLog @@ -5417,4 +5417,7 @@ * arch/arm/src/stm32/stm32_i2c.c: Correct an error that crept into the STM32 F1 I2C driver with some recent changes. From Yiran Liao (2013-8-18). - + * drivers/usbhost/usbhost_devaddr.c and include/nuttx/usb/usbhost_devaddr.h: + Add logic for management of device addresses. This logic does not + currently hook into into anything. It will someday be a part of the + NuttX USB hub implementation (2013-8-18). diff --git a/arch/arm/src/sama5/chip/sam_ehci.h b/arch/arm/src/sama5/chip/sam_ehci.h index 15d8d58abc..9d1282e309 100644 --- a/arch/arm/src/sama5/chip/sam_ehci.h +++ b/arch/arm/src/sama5/chip/sam_ehci.h @@ -64,7 +64,7 @@ * However, for the case of the SAMA5 EHCI, we know apriori that the value * of 'caplength' is 0x10. We keep this structure, however, to faciltate * porting this driver to other environments where, perhaps, such knowledge - * is not availaable. + * is not available. */ /* Host Controller Capability Registers */ diff --git a/drivers/usbhost/Make.defs b/drivers/usbhost/Make.defs index ebb5226950..ac9eba6a64 100644 --- a/drivers/usbhost/Make.defs +++ b/drivers/usbhost/Make.defs @@ -41,11 +41,12 @@ ifeq ($(CONFIG_USBHOST),y) CSRCS += usbhost_registry.c usbhost_registerclass.c usbhost_findclass.c CSRCS += usbhost_enumerate.c usbhost_storage.c usbhost_hidkbd.c +CSRCS += usbhost_devaddr.c # Include add-on USB host driver logic (see misc/drivers) ifeq ($(CONFIG_NET),y) - RTL8187_CSRC := ${shell if [ -f usbhost$(DELIM)rtl8187x.c ]; then echo "rtl8187x.c"; fi} + RTL8187_CSRC := ${shell if [ -f usbhost$(DELIM)rtl8187x.c ]; then echo "rtl8187x.c"; fi} CSRCS += $(RTL8187_CSRC) endif endif diff --git a/drivers/usbhost/usbhost_devaddr.c b/drivers/usbhost/usbhost_devaddr.c new file mode 100755 index 0000000000..95523f78ef --- /dev/null +++ b/drivers/usbhost/usbhost_devaddr.c @@ -0,0 +1,333 @@ +/******************************************************************************* + * drivers/usbhost/usbhost_devaddr.c + * Manage USB device addresses + * + * Copyright (C) 2013 Gregory Nutt. All rights reserved. + * Authors: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + *******************************************************************************/ + +/******************************************************************************* + * Included Files + *******************************************************************************/ + +#include + +#include +#include +#include + +#include +#include + +/******************************************************************************* + * Pre-processor Definitions + *******************************************************************************/ + +/******************************************************************************* + * Private Functions + *******************************************************************************/ + +/**************************************************************************** + * Name: usbhost_takesem and usbhost_givesem + * + * Description: + * This is just a wrapper to handle the annoying behavior of semaphore + * waits that return due to the receipt of a signal. + * + *******************************************************************************/ + +static void usbhost_takesem(FAR struct usbhost_devaddr_s *hcd) +{ + /* Take the semaphore (perhaps waiting) */ + + while (sem_wait(&hcd->exclsem) != 0) + { + /* The only case that an error should occr here is if the wait was + * awakened by a signal. + */ + + ASSERT(errno == EINTR); + } +} + +#define usbhost_givesem(hcd) sem_post(&hcd->exclsem) + +/******************************************************************************* + * Name: usbhost_devaddr_hash + * + * Description: + * Create a hash value from a device address. + * + *******************************************************************************/ + +static inline uint8_t usbhost_devaddr_hash(uint8_t devaddr) +{ + uint8_t ret = devaddr; + + ret ^= (devaddr >> 2); + ret ^= (devaddr >> 3); + return ret & USBHOST_DEVADDR_HASHMASK; +} + +/******************************************************************************* + * Name: usbhost_devaddr_allocate + * + * Description: + * Allocate a new unique device address for this HCD. + * + * Assumptions: + * Caller hold the exclsem + * + *******************************************************************************/ + +static int usbhost_devaddr_allocate(FAR struct usbhost_devaddr_s *hcd) +{ + uint8_t startaddr = hcd->next; + uint8_t devaddr; + int index; + int bitno; + + /* Loop until we find a valid device address */ + + for (;;) + { + /* Try the next device address */ + + devaddr = hcd->next; + if (hcd->next >= 0x7f) + { + hcd->next = 1; + } + else + { + hcd->next++; + } + + /* Is this address already allocated? */ + + index = devaddr >> 5; + bitno = devaddr & 0x1f; + if ((hcd->alloctab[index] & (1 << bitno)) == 0) + { + /* No... allocate it now */ + + hcd->alloctab[index] |= (1 << bitno); + return (int)devaddr; + } + + /* This address has already been allocated. The followign logic will + * prevent (unexpected) infinite loops. + */ + + if (startaddr == devaddr) + { + /* We are back where we started... the are no free device address */ + + return -ENOMEM; + } + } +} + +/******************************************************************************* + * Public Functions + *******************************************************************************/ + +/******************************************************************************* + * Name: usbhost_devaddr_initialize + * + * Description: + * Initialize the caller provided struct usbhost_devaddr_s instance in + * preparation for the management of device addresses on behalf of an HCD. + * + *******************************************************************************/ + +void usbhost_devaddr_initialize(FAR struct usbhost_devaddr_s *hcd) +{ + DEBUGASSERT(hcd); + + memset(hcd, 0, sizeof(struct usbhost_devaddr_s)); + sem_init(&hcd->exclsem, 0, 1); + hcd->next = 1; +} + +/******************************************************************************* + * Name: usbhost_devaddr_create + * + * Description: + * Create a new unique device address for this HCD. Bind the void* arg to the + * the device address and return the newly allocated device address. + * + *******************************************************************************/ + +int usbhost_devaddr_create(FAR struct usbhost_devaddr_s *hcd, + FAR void *associate) +{ + FAR struct usbhost_devhash_s *hentry; + uint8_t hvalue; + int devaddr; + + /* Allocate a hash table entry */ + + hentry = (FAR struct usbhost_devhash_s *)kmalloc(sizeof(struct usbhost_devhash_s)); + if (!hentry) + { + udbg("ERROR: Failed to allocate a hash table entry\n"); + return -ENOMEM; + } + + /* Get exclusive access to the HCD device address data */ + + usbhost_takesem(hcd); + + /* Allocate a device address */ + + devaddr = usbhost_devaddr_allocate(hcd); + if (devaddr < 0) + { + udbg("ERROR: Failed to allocate a device address\n"); + free(hentry); + } + else + { + /* Initialize the hash table entry */ + + hentry->devaddr = devaddr; + hentry->payload = associate; + + /* Add the new device address to the hash table */ + + hvalue = usbhost_devaddr_hash(devaddr); + hentry->flink = hcd->hashtab[hvalue]; + hcd->hashtab[hvalue] = hentry; + + /* Try to re-use the lowest numbered device addresses */ + + if (hcd->next > devaddr) + { + hcd->next = devaddr; + } + } + + usbhost_givesem(hcd); + return devaddr; +} + +/******************************************************************************* + * Name: usbhost_devaddr_find + * + * Description: + * Given a device address, find the void* value that was bound to the device + * address by usbhost_devaddr_create() when the device address was allocated. + * + *******************************************************************************/ + +FAR void *usbhost_devaddr_find(FAR struct usbhost_devaddr_s *hcd, + uint8_t devaddr) +{ + FAR struct usbhost_devhash_s *hentry; + uint8_t hvalue; + + /* Get exclusive access to the HCD device address data */ + + hvalue = usbhost_devaddr_hash(devaddr); + usbhost_takesem(hcd); + + /* Check each entry in the hash table */ + + for (hentry = hcd->hashtab[hvalue]; hentry; hentry = hentry->flink) + { + /* Is this the address we are looking for? */ + + if (hentry->devaddr == devaddr) + { + /* Yes.. return the payload from the hash table entry */ + + usbhost_givesem(hcd); + return hentry->payload; + } + } + + /* Didn't find the device address */ + + usbhost_givesem(hcd); + return NULL; +} + +/******************************************************************************* + * Name: usbhost_devaddr_destroy + * + * Description: + * Release a device address previously allocated by usbhost_devaddr_destroy() + * and destroy the association with the void* data. + * + *******************************************************************************/ + +void usbhost_devaddr_destroy(FAR struct usbhost_devaddr_s *hcd, uint8_t devaddr) +{ + FAR struct usbhost_devhash_s *hentry; + FAR struct usbhost_devhash_s *prev; + uint8_t hvalue; + + /* Get exclusive access to the HCD device address data */ + + hvalue = usbhost_devaddr_hash(devaddr); + usbhost_takesem(hcd); + + /* Search the hast table for the matching entry */ + + for (hentry = hcd->hashtab[hvalue], prev = NULL; + hentry; + prev = hentry, hentry = hentry->flink) + { + /* Is this the address we are looking for? */ + + if (hentry->devaddr == devaddr) + { + /* Yes.. remove the entry from the hash list */ + + if (prev) + { + prev->flink = hentry->flink; + } + else + { + hcd->hashtab[hvalue] = hentry->flink; + } + + /* And release the entry */ + + kfree(hentry); + break; + } + } + + usbhost_givesem(hcd); +} diff --git a/include/nuttx/usb/pl2303.h b/include/nuttx/usb/pl2303.h index 4e755d10da..925010db02 100644 --- a/include/nuttx/usb/pl2303.h +++ b/include/nuttx/usb/pl2303.h @@ -81,7 +81,7 @@ extern "C" * ************************************************************************************/ -EXTERN int usbdev_serialinitialize(int minor); +int usbdev_serialinitialize(int minor); #undef EXTERN #if defined(__cplusplus) diff --git a/include/nuttx/usb/usbhost_devaddr.h b/include/nuttx/usb/usbhost_devaddr.h new file mode 100644 index 0000000000..4945f99e3e --- /dev/null +++ b/include/nuttx/usb/usbhost_devaddr.h @@ -0,0 +1,150 @@ +/******************************************************************************* + * include/nuttx/usb/usbhost_devaddr.h + * Manage USB device addresses + * + * Copyright (C) 2013 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * NOTE: This interface was inspired by the Linux gadget interface by + * David Brownell. That work was very helpful in determining a usable + * partitioning of functionality between standard class drivers and various + * implementations of USB controller drivers. This work, however, does + * not derive directly from that work and is licensed differently. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + *******************************************************************************/ + +#ifndef _INCLUDE_NUTTX_USB_USBHOST_DEVADDR_H +#define _INCLUDE_NUTTX_USB_USBHOST_DEVADDR_H + +/******************************************************************************* + * Included Files + *******************************************************************************/ + +#include + +/******************************************************************************* + * Pre-processor Definitions + *******************************************************************************/ +/* Configuration ***************************************************************/ + +#define USBHOST_DEVADDR_HASHSIZE 8 +#define USBHOST_DEVADDR_HASHMASK (USBHOST_DEVADDR_HASHSIZE-1) + +/******************************************************************************* + * Public Types + *******************************************************************************/ + +struct usbhost_devhash_s +{ + FAR struct usbhost_devhash_s *flink; + FAR void *payload; + uint8_t devaddr; +}; + +struct usbhost_devaddr_s +{ + uint8_t next; /* Next device address */ + sem_t exclsem; /* Enforces mutulaly exlusive access */ + uint32_t alloctab[4]; /* Bit allocation table */ + + /* Hash table */ + + FAR struct usbhost_devhash_s *hashtab[USBHOST_DEVADDR_HASHSIZE]; +}; + +/******************************************************************************* + * Public Data + *******************************************************************************/ + +#undef EXTERN +#if defined(__cplusplus) +# define EXTERN extern "C" +extern "C" +{ +#else +# define EXTERN extern +#endif + +/******************************************************************************* + * Public Functions + *******************************************************************************/ + +/******************************************************************************* + * Name: usbhost_devaddr_initialize + * + * Description: + * Initialize the caller provided struct usbhost_devaddr_s instance in + * preparation for the management of device addresses on behalf of an HCD. + * + *******************************************************************************/ + +void usbhost_devaddr_initialize(FAR struct usbhost_devaddr_s *hcd); + +/******************************************************************************* + * Name: usbhost_devaddr_create + * + * Description: + * Create a new unique device address for this HCD. Bind the void* arg to the + * the device address and return the newly allocated device address. + * + *******************************************************************************/ + +int usbhost_devaddr_create(FAR struct usbhost_devaddr_s *hcd, + FAR void *associate); + +/******************************************************************************* + * Name: usbhost_devaddr_find + * + * Description: + * Given a device address, find the void* value that was bound to the device + * address by usbhost_devaddr_create() when the device address was allocated. + * + *******************************************************************************/ + +FAR void *usbhost_devaddr_find(FAR struct usbhost_devaddr_s *hcd, + uint8_t devaddr); + +/******************************************************************************* + * Name: usbhost_devaddr_destroy + * + * Description: + * Release a device address previously allocated by usbhost_devaddr_destroy() + * and destroy the association with the void* data. + * + *******************************************************************************/ + +void usbhost_devaddr_destroy(FAR struct usbhost_devaddr_s *hcd, uint8_t devaddr); + +#undef EXTERN +#if defined(__cplusplus) +} +#endif + +#endif /* _INCLUDE_NUTTX_USB_USBHOST_DEVADDR_H */