nuttx/drivers/usbhost/usbhost_composite.c
Gregory Nutt d09f6aaa72 Correct more improper use of malloc(), zalloc(), and free()
malloc() and free() should never be used within the OS. This will work in the FLAT build because there is only a single heap, but will cause problems in PROTECTED and KERNEL build modes where there are separate heaps for user and kernel memory.

Typically kmm_malloc(), kmm_zalloc(), and kmm_free() should be called within the kernel in those build modes to use the kernel heap.

Memory is never free.  Possible memory leak:

    ./boards/arm/cxd56xx/common/src/cxd56_crashdump.c:  pdump = malloc(sizeof(fullcontext_t));

Memory allocated with malloc(), but freed with kmm_free():

    ./drivers/usbhost/usbhost_composite.c:  cfgbuffer = (FAR uint8_t *)malloc(CUSTOM_CONFIG_BUFSIZE);

Memory is never freed in these cases.  It is allocated in the driver initialization logic, but there is no corresponding uninitialization logic; memory is not freed on error conditions:

    ./arch/arm/src/lc823450/lc823450_i2s.c:  priv = (struct lc823450_i2s_s *)zalloc(sizeof(struct lc823450_i2s_s));
    ./arch/arm/src/sam34/sam_spi.c:  spics = (struct sam_spics_s *)zalloc(sizeof(struct sam_spics_s));
    ./arch/arm/src/sama5/sam_spi.c:  spics = (struct sam_spics_s *)zalloc(sizeof(struct sam_spics_s));
    ./arch/arm/src/samv7/sam_spi.c:  spics = (struct sam_spics_s *)zalloc(sizeof(struct sam_spics_s));

Memory is allocated with zalloc() but freed on error conditions with kmm_free():

    ./arch/arm/src/sama5/sam_ssc.c:  priv = (struct sam_ssc_s *)zalloc(sizeof(struct sam_ssc_s));
    ./arch/arm/src/samv7/sam_ssc.c:  priv = (struct sam_ssc_s *)zalloc(sizeof(struct sam_ssc_s));
    ./arch/arm/src/stm32/stm32_i2s.c:  priv = (struct stm32_i2s_s *)zalloc(sizeof(struct stm32_i2s_s));

Memory is never freed:

    ./drivers/spi/spi_bitbang.c:  priv = (FAR struct spi_bitbang_s *)zalloc(sizeof(struct spi_bitbang_s));
2020-08-04 20:41:43 +01:00

884 lines
27 KiB
C

/****************************************************************************
* drivers/usbhost/usbhost_composite.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 <string.h>
#include <assert.h>
#include <errno.h>
#include <debug.h>
#include <nuttx/kmalloc.h>
#include <nuttx/usb/usbhost.h>
#include "usbhost_composite.h"
#ifdef CONFIG_USBHOST_COMPOSITE
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/* This is the size of a large, allocated temporary buffer that we will use
* to construct custom configuration descriptors for each member class.
*/
#define CUSTOM_CONFIG_BUFSIZE \
(USB_SIZEOF_CFGDESC + 3 * USB_SIZEOF_IFDESC + 9 * USB_SIZEOF_EPDESC)
/****************************************************************************
* Private Types
****************************************************************************/
/* This structure describes one component class of the composite */
struct usbhost_member_s
{
/* This the classobject returned by each contained class */
FAR struct usbhost_class_s *usbclass;
/* This is the information that we need to do the registry lookup for this
* class member.
*/
struct usbhost_id_s id;
/* This information will be needed to construct a meaningful configuration
* for CLASS_CONNSET()
*/
uint8_t firstif; /* First interface */
uint8_t nifs; /* Number of interfaces */
};
/* This structure contains the internal, private state of the USB host
* CDC/ACM class.
*/
struct usbhost_composite_s
{
/* This is the externally visible portion of the state. The usbclass must
* the first element of the structure. It is then cast compatible with
* struct usbhost_composite_s.
*/
struct usbhost_class_s usbclass;
/* Class specific data follows */
uint16_t nclasses; /* Number of component classes in the composite */
/* The following points to an allocated array of type struct
* usbhost_member_s. Element element of the array corresponds to one
* component class in the composite.
*/
FAR struct usbhost_member_s *members;
};
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
/* struct usbhost_class_s methods */
static int usbhost_connect(FAR struct usbhost_class_s *usbclass,
FAR const uint8_t *configdesc, int desclen);
static int usbhost_disconnected(FAR struct usbhost_class_s *usbclass);
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: usbhost_disconnect_all
*
* Description:
* Disconnect all contained class instances.
*
* Input Parameters:
* priv - Reference to private, composite container state structure.
*
* Returned Value:
* None
*
****************************************************************************/
static void usbhost_disconnect_all(FAR struct usbhost_composite_s *priv)
{
FAR struct usbhost_member_s *member;
int i;
/* Loop, processing each class that has been included into the composite */
for (i = 0; i < priv->nclasses; i++)
{
member = &priv->members[i];
/* Has this member been included to the composite? */
if (member->usbclass != NULL)
{
/* Yes.. disconnect it, freeing all of the class resources */
CLASS_DISCONNECTED(member->usbclass);
member->usbclass = NULL;
}
}
}
/****************************************************************************
* Name: usbhost_connect
*
* Description:
* This function implements the connect() method of struct
* usbhost_class_s. This method is a callback into the class
* implementation from the common enumeration logic. It is normally used
* to provide the device's configuration descriptor to the class so that
* the class may initialize properly. That calling sequence is:
*
* 1. usbhost_enumerate()
* 2. usbhost_classbind()
* 3. CLASS_CONNECT()
*
* However, that applies only to the Non-composite device.
* usbhost_classbind() is not called for the composite device and, hence,
* this method is never called. Rather, the composite logic calls
* CLASS_CONNECT() for each member of the composite in a calling sequence
* like:
*
* 1. usbhost_enumerate()
* 2. usbhost_composite()
* 3. Call CLASS_CONNECT() for each composite member
*
* 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 usbhost_connect(FAR struct usbhost_class_s *usbclass,
FAR const uint8_t *configdesc, int desclen)
{
return -ENOSYS;
}
/****************************************************************************
* 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_composite_s *priv =
(FAR struct usbhost_composite_s *)usbclass;
DEBUGASSERT(priv != NULL);
/* Forward the disconnect event to each contained class in the composite. */
usbhost_disconnect_all(priv);
/* Free the allocate array of composite members */
if (priv->members != NULL)
{
kmm_free(priv->members);
}
/* The destroy the composite container itself */
kmm_free(priv);
return OK;
}
/****************************************************************************
* Name: usbhost_copyinterface
*
* Description:
* Find an interface descriptor and copy it along with all of its
* following endpoint and cs interface descriptors.
*
* Input Parameters:
* ifno - The interface ID to find.
* configdesc - The original configuration descriptor that contains the
* the interface descriptor.
* desclen - the length of configdesc.
* buffer - The buffer in which to return the descriptors
* buflen - The length of buffer
*
* Returned Value:
* On success, the number of bytes copied is returned. On a failure, a
* negated errno value is returned indicating the nature of the failure:
*
* -ENOENT: Did not find interface descriptor
* -EINVAL: Did not find all endpoint descriptors
* -ENOSPC: Provided buffer too small to hold all found descriptors
*
****************************************************************************/
static int usbhost_copyinterface(uint8_t ifno, FAR const uint8_t *configdesc,
int desclen, FAR uint8_t *buffer,
int buflen)
{
FAR struct usb_desc_s *desc;
FAR struct usb_ifdesc_s *ifdesc;
int retsize;
int offset;
int neps;
int len;
/* Make sure that the buffer will hold at least the interface descriptor */
if (buflen < USB_SIZEOF_IFDESC)
{
return -ENOSPC;
}
/* Search for the interface */
for (offset = 0, retsize = 0;
offset < desclen - sizeof(struct usb_desc_s);
offset += len)
{
desc = (FAR struct usb_desc_s *)&configdesc[offset];
len = desc->len;
/* Is this an interface descriptor? */
if (desc->type == USB_DESC_TYPE_INTERFACE)
{
ifdesc = (FAR struct usb_ifdesc_s *)&configdesc[offset];
/* Is it the one we are looking for? */
if (ifdesc->ifno == ifno && ifdesc->neps != 0)
{
/* Yes.. return the interface descriptor */
memcpy(buffer, desc, len);
buffer += len;
buflen -= len;
retsize += len;
/* Make sure that the buffer will hold at least the endpoint
* descriptors.
*/
neps = ifdesc->neps;
if (buflen < neps * USB_SIZEOF_EPDESC)
{
return -ENOSPC;
}
/* The CS and endpoint descriptors should immediately
* follow the interface descriptor.
*/
for (offset += len;
offset < desclen - sizeof(struct usb_desc_s);
offset += len)
{
desc = (FAR struct usb_desc_s *)&configdesc[offset];
len = desc->len;
/* Is this a class-specific interface descriptor? */
if (desc->type == USB_DESC_TYPE_CSINTERFACE)
{
/* Yes... return the descriptor */
if (buflen < len)
{
return -ENOSPC;
}
memcpy(buffer, desc, len);
buffer += len;
buflen -= len;
retsize += len;
}
/* Is this an endpoint descriptor? */
else if (desc->type == USB_DESC_TYPE_ENDPOINT)
{
/* Yes.. return the endpoint descriptor */
if (buflen < len)
{
return -ENOSPC;
}
memcpy(buffer, desc, len);
buffer += len;
buflen -= len;
retsize += len;
/* And reduce the number of endpoints we are looking
* for.
*/
if (--neps <= 0)
{
/* That is all of them! Return the total size
* copied.
*/
return retsize;
}
}
/* The endpoint descriptors following the interface
* descriptor should all be contiguous. But we will
* complain only if another interface descriptor is
* encountered before all of the endpoint descriptors have
* been found.
*/
else if (desc->type == USB_DESC_TYPE_INTERFACE)
{
break;
}
}
/* Did not find all of the interface descriptors */
return -EINVAL;
}
}
}
/* Could not find the interface descriptor */
return -ENOENT;
}
/****************************************************************************
* Name: usbhost_createconfig
*
* Description:
* Create a custom configuration for a member class.
*
* Input Parameters:
* configdesc - The original configuration descriptor that contains the
* the interface descriptor.
* desclen - the length of configdesc.
* buffer - The buffer in which to return the descriptors
* buflen - The length of buffer
*
* Returned Value:
* On success, the size of the new configuration descriptor is returned.
* On a failure, a negated errno value is returned indicating the nature
* of the failure:
*
* -ENOENT: Did not find interface descriptor
* -EINVAL: Did not find all endpoint descriptors
*
****************************************************************************/
static int usbhost_createconfig(FAR struct usbhost_member_s *member,
FAR const uint8_t *configdesc, int desclen,
FAR uint8_t *buffer, int buflen)
{
FAR struct usb_cfgdesc_s *cfgdesc;
int cfgsize;
int ifsize;
int ifno;
int nifs;
/* Copy and modify the original configuration descriptor */
if (buflen < USB_SIZEOF_CFGDESC)
{
return -ENOSPC;
}
memcpy(buffer, configdesc, USB_SIZEOF_CFGDESC);
cfgdesc = (FAR struct usb_cfgdesc_s *)buffer;
cfgsize = USB_SIZEOF_CFGDESC;
buffer += USB_SIZEOF_CFGDESC;
buflen -= USB_SIZEOF_CFGDESC;
/* Modify the copied configuration descriptor */
cfgdesc->len = USB_SIZEOF_CFGDESC;
cfgdesc->ninterfaces = member->nifs;
/* Then copy all of the interfaces to the configuration buffer */
for (nifs = 0, ifno = member->firstif; nifs < member->nifs; nifs++, ifno++)
{
ifsize = usbhost_copyinterface(ifno, configdesc, desclen,
buffer, buflen);
if (ifsize < 0)
{
uerr("ERROR: Failed to copy interface: %d\n", ifsize);
return ifsize;
}
/* Update sizes and pointers */
cfgsize += ifsize;
buffer += ifsize;
buflen -= ifsize;
}
/* Set the totallen of the configuration descriptor and return success */
cfgdesc->totallen[0] = cfgsize & 0xff; /* Little endian always */
cfgdesc->totallen[1] = cfgsize >> 8;
return cfgsize;
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: usbhost_composite
*
* Description:
* As the final steps in the device enumeration sequence this function
* will be called in order to determine (1) determine if the device is
* a composite device, and if so, (2) create the composite class which
* contains all of the individual class instances making up the composite.
*
* Input Parameters:
* hport - The downstream port to which the (potential) composite
* device has been connected.
* configdesc - The full configuration descriptor
* desclen - The length of the configuration descriptor
* id - Lookup information extracted from the device descriptor.
* for the case of the composite devices, we need only the
* vid and pid.
* usbclass - If the class driver for the device is successful located
* and bound to the hub port, the allocated class instance
* is returned into this caller-provided memory location.
*
* Returned Value:
* Zero (OK) is returned if (1) the device was determined to be a
* composite device and (2) the composite class wrapper was successfully
* created and bound to the HCD. A negated errno value is returned on
* any failure. The value -ENOENT, in particular means that the attached
* device is not a composite device. Other values would indicate other
* various, unexpected failures.
*
****************************************************************************/
int usbhost_composite(FAR struct usbhost_hubport_s *hport,
FAR const uint8_t *configdesc, int desclen,
FAR struct usbhost_id_s *id,
FAR struct usbhost_class_s **usbclass)
{
FAR struct usbhost_composite_s *priv;
FAR struct usbhost_member_s *member;
FAR const struct usbhost_registry_s *reg;
FAR struct usb_desc_s *desc;
FAR uint8_t *cfgbuffer;
uint32_t mergeset;
uint16_t nintfs;
uint16_t nmerged;
uint16_t nclasses;
int cfgsize;
int offset;
int ret;
int i;
/* Determine if this a composite device has been connected to the
* downstream port.
*
* First look at there device descriptor information. A composite
* device is only possible if:
*
* 1. Manufacturers of composite devices typically assign a value of zero
* to the device class (bDeviceClass), subclass (bDeviceSubClass), and
* protocol (bDeviceProtocol) fields in the device descriptor, as
* specified by the Universal Serial Bus Specification. This allows
* the manufacturer to associate each individual interface with a
* different device class and protocol.
*
* 2. The USB-IF core team has devised a special class and protocol code
* set that notifies the operating system that one or more IADs are
* present in device firmware. A device's device descriptor must have
* the values that appear in the following table:
*
* bDeviceClass 0xEF
* bDeviceSubClass 0x02
* bDeviceProtocol 0x01
*/
if (id->base != USB_CLASS_PER_INTERFACE && id->base != USB_CLASS_MISC)
{
return -ENOENT;
}
/* First, count the number of interface descriptors (nintfs) and the
* number of interfaces that are associated to one device via IAD
* descriptor (nmerged).
*/
mergeset = 0;
nintfs = 0;
nmerged = 0;
for (offset = 0; offset < desclen - sizeof(struct usb_desc_s); )
{
desc = (FAR struct usb_desc_s *)&configdesc[offset];
int len = desc->len;
if (offset + len <= desclen)
{
/* Is this an interface descriptor? */
if (desc->type == USB_DESC_TYPE_INTERFACE)
{
#ifdef CONFIG_DEBUG_ASSERTIONS
FAR struct usb_ifdesc_s *ifdesc =
(FAR struct usb_ifdesc_s *)desc;
DEBUGASSERT(ifdesc->ifno < 32);
#endif
/* Increment the count of interfaces */
nintfs++;
}
/* Check for IAD descriptors that will be used when it is
* necessary to associate multiple interfaces with a single
* class driver.
*/
else if (desc->type == USB_DESC_TYPE_INTERFACEASSOCIATION)
{
FAR struct usb_iaddesc_s *iad =
(FAR struct usb_iaddesc_s *)desc;
uint32_t mask;
/* Keep count of the number of interfaces that will be merged */
nmerged += (iad->nifs - 1);
/* Keep track of which interfaces will be merged */
DEBUGASSERT(iad->firstif + iad->nifs < 32);
mask = (1 << iad->nifs) - 1;
mergeset |= mask << iad->firstif;
}
}
offset += len;
}
if (nintfs < 2)
{
/* Only one interface descriptor. Can't be a composite device */
return -ENOENT;
}
#if 0 /* I think not needed, the device descriptor classid check should handle this */
/* Special case: Some NON-composite device have more than on interface: CDC/ACM
* and MSC both may have two interfaces.
*/
if (nintfs < 3 && nmerged == 0)
{
/* Do the special case checks */
#warning Missing logic
}
#endif
/* The total number of classes is then the number of interfaces minus the
* number of interfaces merged via the IAD descriptor.
*/
if (nintfs <= nmerged)
{
/* Should not happen. Means a bug. */
return -EINVAL;
}
nclasses = nintfs - nmerged;
/* Allocate the composite class container */
priv = (FAR struct usbhost_composite_s *)
kmm_zalloc(sizeof(struct usbhost_composite_s));
if (priv == NULL)
{
uerr("ERROR: Failed to allocate class container\n");
return -ENOMEM;
}
priv->members = (FAR struct usbhost_member_s *)
kmm_zalloc(nclasses * sizeof(struct usbhost_member_s));
if (priv->members == NULL)
{
uerr("ERROR: Failed to allocate class members\n");
ret = -ENOMEM;
goto errout_with_container;
}
/* Initialize the non-zero elements of the class container */
priv->usbclass.hport = hport;
priv->usbclass.connect = usbhost_connect;
priv->usbclass.disconnected = usbhost_disconnected;
priv->nclasses = nclasses;
/* Re-parse the configuration descriptor and save the CLASS ID information
* in the member structure: If the interface is defined by an interface
* descriptor, then we have to use the info in the interface descriptor;
* If the interface has a IAD, we have to use info in the IAD.
*/
for (i = 0, offset = 0; offset < desclen - sizeof(struct usb_desc_s); )
{
desc = (FAR struct usb_desc_s *)&configdesc[offset];
int len = desc->len;
if (offset + len <= desclen)
{
/* Is this an interface descriptor? */
if (desc->type == USB_DESC_TYPE_INTERFACE)
{
FAR struct usb_ifdesc_s *ifdesc =
(FAR struct usb_ifdesc_s *)desc;
/* Was the interface merged via an IAD descriptor? */
DEBUGASSERT(ifdesc->ifno < 32);
if ((mergeset & (1 << ifdesc->ifno)) == 0)
{
/* No, this interface was not merged. Save the registry
* lookup information from the interface descriptor.
*/
member = (FAR struct usbhost_member_s *)
&priv->members[i];
member->id.base = ifdesc->classid;
member->id.subclass = ifdesc->subclass;
member->id.proto = ifdesc->protocol;
member->id.vid = id->vid;
member->id.pid = id->pid;
member->firstif = ifdesc->ifno;
member->nifs = 1;
/* Increment the member index */
i++;
}
}
/* Check for IAD descriptors that will be used when it is
* necessary to associate multiple interfaces with a single
* device.
*/
else if (desc->type == USB_DESC_TYPE_INTERFACEASSOCIATION)
{
FAR struct usb_iaddesc_s *iad =
(FAR struct usb_iaddesc_s *)desc;
/* Yes.. Save the registry lookup information from the IAD. */
member = (FAR struct usbhost_member_s *)
&priv->members[i];
member->id.base = iad->classid;
member->id.subclass = iad->subclass;
member->id.proto = iad->protocol;
member->id.vid = id->vid;
member->id.pid = id->pid;
member->firstif = iad->firstif;
member->nifs = iad->nifs;
/* Increment the member index */
i++;
}
}
offset += len;
}
/* If everything worked, the final index must be the same as the pre-
* calculated number of member classes.
*/
DEBUGASSERT(i == nclasses);
/* Allocate a temporary buffer in which we can construct a custom
* configuration descriptor for each member class.
*/
cfgbuffer = (FAR uint8_t *)kmm_malloc(CUSTOM_CONFIG_BUFSIZE);
if (cfgbuffer == NULL)
{
uerr("ERROR: Failed to allocate configuration buffer");
ret = -ENOMEM;
goto errout_with_members;
}
/* Now loop, performing the registry lookup and initialization of each
* member class in the composite.
*/
for (i = 0; i < nclasses; i++)
{
member = &priv->members[i];
/* Is there is a class implementation registered to support this
* device.
*/
reg = usbhost_findclass(&member->id);
if (reg == NULL)
{
uerr("ERROR: usbhost_findclass failed\n");
#ifdef CONFIG_USBHOST_COMPOSITE_STRICT
ret = -EINVAL;
goto errout_with_cfgbuffer;
#else
continue;
#endif
}
/* Yes.. there is a class for this device. Get an instance of its
* interface.
*/
member->usbclass = CLASS_CREATE(reg, hport, id);
if (member->usbclass == NULL)
{
uerr("ERROR: CLASS_CREATE failed\n");
ret = -ENOMEM;
goto errout_with_cfgbuffer;
}
/* Construct a custom configuration descriptor for this member */
cfgsize = usbhost_createconfig(member, configdesc, desclen,
cfgbuffer, CUSTOM_CONFIG_BUFSIZE);
if (cfgsize < 0)
{
uerr("ERROR: Failed to create the custom configuration: %d\n",
cfgsize);
ret = cfgsize;
goto errout_with_cfgbuffer;
}
/* Call the newly instantiated classes connect() method provide it
* with the configuration information that it needs to initialize
* properly.
*/
ret = CLASS_CONNECT(member->usbclass, cfgbuffer, cfgsize);
if (ret < 0)
{
/* On failure, call the class disconnect method of each contained
* class which should then free the allocated usbclass instance.
*/
uerr("ERROR: CLASS_CONNECT failed: %d\n", ret);
goto errout_with_cfgbuffer;
}
}
/* Free the temporary buffer */
kmm_free(cfgbuffer);
/* Return our USB class structure */
*usbclass = &priv->usbclass;
return OK;
errout_with_cfgbuffer:
kmm_free(cfgbuffer);
errout_with_members:
/* On an failure, call the class disconnect method of each contained
* class which should then free the allocated usbclass instance.
*/
usbhost_disconnect_all(priv);
/* Free the allocate array of composite members */
if (priv->members != NULL)
{
kmm_free(priv->members);
}
errout_with_container:
/* Then free the composite container itself */
kmm_free(priv);
return ret;
}
#endif /* CONFIG_USBHOST_COMPOSITE */