From 990090a4cc9070837d31e66b58d40f0c3d038741 Mon Sep 17 00:00:00 2001 From: Yuval Adam Date: Sun, 16 Mar 2014 14:41:11 +0200 Subject: Add usblib and utils --- usblib/host/usbhhub.c | 1522 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1522 insertions(+) create mode 100644 usblib/host/usbhhub.c (limited to 'usblib/host/usbhhub.c') diff --git a/usblib/host/usbhhub.c b/usblib/host/usbhhub.c new file mode 100644 index 0000000..8042dfc --- /dev/null +++ b/usblib/host/usbhhub.c @@ -0,0 +1,1522 @@ +//***************************************************************************** +// +// usbhhub.c - This file contains the host HID driver. +// +// Copyright (c) 2011-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva USB Library. +// +//***************************************************************************** + +#include +#include +#include "inc/hw_types.h" +#include "inc/hw_ints.h" +#include "inc/hw_sysctl.h" +#include "driverlib/usb.h" +#include "driverlib/interrupt.h" +#include "driverlib/rom_map.h" +#include "driverlib/rtos_bindings.h" +#include "usblib/usblib.h" +#include "usblib/host/usbhost.h" +#include "usblib/host/usbhostpriv.h" +#include "usblib/host/usbhhub.h" +#ifdef INCLUDE_DEBUG_OUTPUT +#include "utils/uartstdio.h" +#define DEBUG_OUTPUT UARTprintf +#else +#define DEBUG_OUTPUT while(0)((int (*)(char *, ...))0) +#endif + +//***************************************************************************** +// +//! \addtogroup usblib_host_class +//! @{ +// +//***************************************************************************** + +#ifdef ewarm +#pragma pack(1) +#endif + +//***************************************************************************** +// +//! The USB standard hub descriptor structure. Full documentation for the +//! contents of this structure can be found in chapter 11.23.2.1 of the USB +//! 2.0 specification. +// +//***************************************************************************** +typedef struct +{ + // + //! The total number of bytes in the descriptor (including this field). + // + uint8_t bLength; + + // + //! The descriptor type. For a hub descriptor, this will be USB_DTYPE_HUB + //! (0x29 or 41 decimal). + // + uint8_t bDescType; + + // + //! The number of downstream-facing ports that the hub supports. + // + uint8_t bNbrPorts; + + // + //! Characteristics of the hub device including its power switching + //! capabilities and over-current protection mode. + // + uint16_t wHubCharacteristics; + + // + //! The time between the start of the power-on sequence for a port and + //! the power to the port becoming stable. This is expressed in 2mS units. + // + uint8_t bPwrOn2PwrGood; + + // + //! The maximum current requirement for the hub circuitry in mA. + // + uint8_t bHubContrCurrent; + + // + //! The last two fields in the structure are bit masks indicating which + //! downstream ports support removable devices and, following this, another + //! obsolete field from USB1.0 related to port power control. Each field + //! is byte aligned and contains a bit for each hub port. This structure + //! definition is set up with enough storage to handle ROOT_HUB_MAX_PORTS + //! ports but beware that the actual size of each field is dependent upon + //! the bNbrPorts field above. + // + uint8_t PortInfo[((ROOT_HUB_MAX_PORTS + 7) / 8) * 2]; +} +PACKED tUsbHubDescriptor; + +#ifdef ewarm +#pragma pack() +#endif + +//***************************************************************************** +// +// This structure holds all data specific to a single hub port. +// +//***************************************************************************** +typedef struct +{ + // + // The handle used by the HCD layer to identify this device. + // + uint32_t ui32DevHandle; + + // + // The current state of the port. + // + volatile tHubPortState iState; + + // + // General counter used in various states. + // + volatile uint32_t ui32Count; + + // + // A flag used to indicate that the downstream device is a low speed + // device. + // + bool bLowSpeed; + + // + // The speed of the device on this port. + // + uint32_t ui32Speed; + + // + // This flag is set if the hub reports that a change is pending on this + // port. + // + volatile bool bChanged; +} +tHubPort; + +//***************************************************************************** +// +// USB hub flags values for tHubInstance.ui32Flags. +// +//***************************************************************************** +#define USBLIB_HUB_ACTIVE 0x00000001 +#define USBLIB_HUB_HS 0x00000002 +#define USBLIB_HUB_MULTI_TT 0x00000004 + +//***************************************************************************** +// +// This is the structure that holds all of the data for a given instance of +// a Hub device. +// +//***************************************************************************** +struct tHubInstance +{ + // + // Save the device instance. + // + tUSBHostDevice *psDevice; + + // + // Used to save the callback function pointer. + // + tUSBHHubCallback pfnCallback; + + // + // Callback data provided by caller. + // + uint32_t ui32CBData; + + // + // Interrupt IN pipe. + // + uint32_t ui32IntInPipe; + + // + // Hub characteristics as reported in the class-specific hub descriptor. + // + uint16_t ui16HubCharacteristics; + + // + // The number of downstream-facing ports the hub supports. + // + uint8_t ui8NumPorts; + + // + // The number of ports on the hub that we can actually talk to. This will + // be the smaller of the number of ports on the hub and MAX_USB_DEVICES. + // + uint8_t ui8NumPortsInUse; + + // + // The size of a status change packet sent by the hub. This is determined + // from the number of ports supported by the hub. + // + uint8_t ui8ReportSize; + + // + // Flags indicating whether the hub is connected. + // + uint32_t ui32Flags; + + // + // Flag indicating that a device is currently in process of being + // enumerated. + // + volatile bool bEnumerationBusy; + + // + // This is valid if bEnumerationBusy is set and indicates the port + // that is in the process of enumeration. + // + uint8_t ui8EnumIdx; + + // + // The state of each of the ports we support on the hub. + // + tHubPort psPorts[MAX_USB_DEVICES]; + + // + // The interrupt number for this instance. + // + uint32_t ui32IntNum; +}; + +//***************************************************************************** +// +//! Forward references to the hub class driver functions. +// +//***************************************************************************** +static void *HubDriverOpen(tUSBHostDevice *psDevice); +static void HubDriverClose(void *pvHubDevice); + +//***************************************************************************** +// +//! This constant global structure defines the Hub Class Driver that is +//! provided with the USB library. +// +//***************************************************************************** +const tUSBHostClassDriver g_sUSBHubClassDriver = +{ + USB_CLASS_HUB, + HubDriverOpen, + HubDriverClose, + 0 +}; + +//***************************************************************************** +// +// The instance data storage for attached hub. +// +//***************************************************************************** +static tHubInstance g_sRootHub; + +//***************************************************************************** +// +// Hub and port state change flags as reported via the hub's IN endpoint. +// +//***************************************************************************** +static volatile uint32_t g_ui32ChangeFlags; + +// +// Note: The following assumes ROOT_HUB_MAX_PORTS is less than 32! +// +static uint32_t g_ui32HubChanges; + +//***************************************************************************** +// +// This function is called to set the operating speed of a given port. +// +// \param ui8Port is the port number for this request. +// \param ui32Speed is one of the HUB_FEATURE_PORT_* values. +// +// This function sets the operating speed of the hub port specified in the +// \e ui8Port parameter. A \e ui8Port value of 0 is an access to the hub +// itself and not one of the hub ports. The \e ui32Speed value is one of the +// \b USB_EP_SPEED_ values. +// +// \return None. +// +//***************************************************************************** +static void +USBHubPortSpeedSet(uint8_t ui8Port, uint32_t ui32Speed) +{ + g_sRootHub.psPorts[ui8Port].ui32Speed = ui32Speed; +} + +//***************************************************************************** +// +// This function is called to send a request to the hub to set a feature on +// a given port. +// +// \param psHubInstance is the hub device instance. +// \param ui8Port is the port number for this request. +// \param ui16Feature is one of the HUB_FEATURE_PORT_* values. +// +// This function will send the set feature request to the hub indicated by the +// \e psHubInstance parameter. The \e ui8Port value indicates which port +// number to send this request to and can range from 0 to the number of valid +// ports on the given hub. A \e ui8Port value of 0 is an access to the hub +// itself and not one of the hub ports. The \e ui16Feature is the feature +// request toset on the given port. For example, a \e ui16Feature value of +// \e HUB_FEATURE_PORT_RESET and \e ui8Port value of 1 will cause reset +// signaling to hub port 1. +// +// \return None. +// +//***************************************************************************** +static void +HubSetPortFeature(tHubInstance *psHubInstance, uint8_t ui8Port, + uint16_t ui16Feature) +{ + tUSBRequest sSetupPacket; + tUSBHostDevice *psDevice; + + // + // Retrieve the hub instance and device pointer. + // + psDevice = psHubInstance->psDevice; + + // + // This is a standard OUT request. + // + sSetupPacket.bmRequestType = USB_RTYPE_DIR_OUT | USB_RTYPE_CLASS | + USB_RTYPE_OTHER; + + // + // Set the field to clear the requested port feature. + // + sSetupPacket.bRequest = USBREQ_SET_FEATURE; + sSetupPacket.wValue = ui16Feature; + sSetupPacket.wIndex = ui8Port; + sSetupPacket.wLength = 0; + + // + // Send the request. + // + USBHCDControlTransfer(0, &sSetupPacket, psDevice, 0, 0, + psDevice->sDeviceDescriptor.bMaxPacketSize0); +} + +//***************************************************************************** +// +// This function is called to send a request to the hub to clear a feature on +// a given port. +// +// \param psHubInstance is the hub device instance. +// \param ui8Port is the port number for this request. +// \param ui16Feature is one of the HUB_FEATURE_PORT_* values. +// +// This function will send the clear feature request to the hub indicated by +// the \e psHubInstance parameter. The \e ui8Port value indicates which port +// number to send this request to and can range from 0 to the number of valid +// ports on the given hub. A \e ui8Port value of 0 is an access to the hub +// itself and not one of the hub ports. The \e ui16Feature is the feature +// request to clear on the given port. For example, a \e ui16Feature value of +// \e HUB_FEATURE_C_PORT_RESET and \e ui8Port value of 1 will clear the reset +// complete signaling on hub port 1. Values like the reset feature will +// remain set until actively cleared by this function. +// +// \return None. +// +//***************************************************************************** +static void +HubClearPortFeature(tHubInstance *psHubInstance, uint8_t ui8Port, + uint16_t ui16Feature) +{ + tUSBRequest sSetupPacket; + tUSBHostDevice *psDevice; + + // + // Retrieve the hub instance and device pointer. + // + psDevice = psHubInstance->psDevice; + + // + // This is a standard OUT request. + // + sSetupPacket.bmRequestType = USB_RTYPE_DIR_OUT | USB_RTYPE_CLASS | + USB_RTYPE_OTHER; + + // + // Set the field to clear the requested port feature. + // + sSetupPacket.bRequest = USBREQ_CLEAR_FEATURE; + sSetupPacket.wValue = ui16Feature; + sSetupPacket.wIndex = ui8Port; + sSetupPacket.wLength = 0; + + // + // Send the request. + // + USBHCDControlTransfer(0, &sSetupPacket, psDevice, 0, 0, + psDevice->sDeviceDescriptor.bMaxPacketSize0); +} + +//***************************************************************************** +// +// This function is used to retrieve the current status of a port on the +// hub. +// +// \param psHubInstance is the hub device instance. +// \param ui8Port is the port number for this request. +// \param pui16PortStatus is a pointer to the memory to store the current +// status of the port. +// \param pui16PortChange is a pointer to the memory to store the current +// change status of the ports. +// +// This function is used to retrieve the current overall status and change +// status for the port given in the \e ui8Port parameter. The \e ui8Port value +// indicates which port number to send this request to and can range from 0 to +// the number of valid ports on the given hub. A \e ui8Port value of 0 is an +// access to the hub itself and not one of the hub ports. +// +// \return None. +// +//***************************************************************************** +static bool +HubGetPortStatus(tHubInstance *psHubInstance, uint8_t ui8Port, + uint16_t *pui16PortStatus, uint16_t *pui16PortChange) +{ + uint32_t ui32Data, ui32Read; + tUSBRequest sSetupPacket; + tUSBHostDevice *psDevice; + + // + // Retrieve the device pointer. + // + psDevice = psHubInstance->psDevice; + + // + // This is a standard OUT request. + // + sSetupPacket.bmRequestType = USB_RTYPE_DIR_IN | USB_RTYPE_CLASS | + USB_RTYPE_OTHER; + + // + // Set the fields to get the hub status. + // + sSetupPacket.bRequest = USBREQ_GET_STATUS; + sSetupPacket.wValue = 0; + sSetupPacket.wIndex = (uint16_t)ui8Port; + sSetupPacket.wLength = 4; + + // + // Send the request. + // + ui32Read = USBHCDControlTransfer(0, &sSetupPacket, psDevice, + (uint8_t *)&ui32Data, 4, + psDevice->sDeviceDescriptor.bMaxPacketSize0); + + // + // Check that we received the correct number of bytes. + // + if(ui32Read != 4) + { + return(false); + } + else + { + // + // We got 4 bytes from the device. Now translate these into the 2 + // 16-bit values we pass back to the caller. + // + *pui16PortStatus = (uint16_t)(ui32Data & 0xFFFF); + *pui16PortChange = (uint16_t)(ui32Data >> 16); + + DEBUG_OUTPUT("Port %d, status 0x%04x, change 0x%04x\n", ui8Port, + *pui16PortStatus, *pui16PortChange); + } + + // + // All is well. + // + return(true); +} + +//***************************************************************************** +// +// This function handles callbacks for the interrupt IN endpoint for the hub +// device. +// +//***************************************************************************** +static void +HubIntINCallback(uint32_t ui32Pipe, uint32_t ui32Event) +{ + switch (ui32Event) + { + // + // Handles a request to schedule a new request on the interrupt IN + // pipe. + // + case USB_EVENT_SCHEDULER: + { + // + // Set things up to read the next change indication from the hub. + // + USBHCDPipeSchedule(ui32Pipe, (uint8_t *)&g_ui32HubChanges, + (uint32_t)g_sRootHub.ui8ReportSize); + break; + } + + // + // Called when new data is available on the interrupt IN pipe. + // + case USB_EVENT_RX_AVAILABLE: + { + // + // For data transfers on INT IN endpoints, we need to acknowledge + // the data from this callback. + // + USBHCDPipeDataAck(ui32Pipe); + + // + // Update our global "ports needing service" flags with the latest + // information we have just received. + // + g_ui32ChangeFlags |= g_ui32HubChanges; + + // + // Send the report data to the USB host hub device class driver if + // we have been given a callback function. + // + if(g_sRootHub.pfnCallback) + { + g_sRootHub.pfnCallback((void *)g_sRootHub.ui32CBData, + USB_EVENT_RX_AVAILABLE, + ui32Pipe, &g_ui32HubChanges); + } + + break; + } + case USB_EVENT_ERROR: + { + break; + } + } +} + +//***************************************************************************** +// +// Query the class-specific hub descriptor. +// +//***************************************************************************** +static bool +GetHubDescriptor(tUsbHubDescriptor *psDesc) +{ + uint32_t ui32Read; + tUSBRequest sSetupPacket; + tUSBHostDevice *psDevice; + + // + // Retrieve the device pointer. + // + psDevice = g_sRootHub.psDevice; + + // + // This is a standard OUT request. + // + sSetupPacket.bmRequestType = USB_RTYPE_DIR_IN | USB_RTYPE_CLASS | + USB_RTYPE_DEVICE; + + // + // Set the fields to get the hub descriptor. Initially, we request only + // the first 4 bytes of the descriptor. This will give us the size which + // we use to determine how many bytes to read to get the full descriptor. + // This is necessary since we don't know how many ports the hub can support + // and we only support up to MAX_USB_DEVICES. + // + sSetupPacket.bRequest = USBREQ_GET_DESCRIPTOR; + sSetupPacket.wValue = (USB_DTYPE_HUB << 8); + sSetupPacket.wIndex = 0; + sSetupPacket.wLength = sizeof(tUsbHubDescriptor); + + // + // Send the request. + // + ui32Read = USBHCDControlTransfer(0, &sSetupPacket, psDevice, + (void *)psDesc, sizeof(tUsbHubDescriptor), + psDevice->sDeviceDescriptor.bMaxPacketSize0); + + // + // Make sure we got at least some data. + // + if(ui32Read == 0) + { + return(false); + } + + // + // All is well. + // + return(true); +} + +//***************************************************************************** +// +// Open an instance of the hub driver. This is called when the USB host +// has enumerated a new hub device. +// +//***************************************************************************** +static void * +HubDriverOpen(tUSBHostDevice *psDevice) +{ + tEndpointDescriptor *psEndpointDescriptor; + tInterfaceDescriptor *psInterface; + tUsbHubDescriptor sHubDesc; + bool bRetcode; + uint32_t ui32Loop; + + // + // If we are already talking to a hub, fail the call. We only support + // a single hub. + // + if(g_sRootHub.ui32Flags & USBLIB_HUB_ACTIVE) + { + return(0); + } + + // + // Get pointers to the device descriptors we need to look at. + // + psInterface = USBDescGetInterface(psDevice->psConfigDescriptor, 0, 0); + psEndpointDescriptor = USBDescGetInterfaceEndpoint(psInterface, 0, + psDevice->ui32ConfigDescriptorSize); + + // + // If there are no endpoints, something is wrong since a hub must have + // a single INT endpoint for signaling. + // + if(psEndpointDescriptor == 0) + { + return 0; + } + + // + // Make sure we really are talking to a hub. + // + if((psInterface->bInterfaceClass != USB_CLASS_HUB) || + (psInterface->bInterfaceSubClass != 0)) + { + // + // Something is wrong - this isn't a hub or, if it is, we don't + // understand the protocol it is using. + // + return(0); + } + + // + // Remember that this is a high speed hub with either single or multiple + // transaction translators. + // + if(psInterface->bInterfaceProtocol == USB_HUB_PROTOCOL_SINGLE) + { + g_sRootHub.ui32Flags |= USBLIB_HUB_HS; + } + else if(psInterface->bInterfaceProtocol == USB_HUB_PROTOCOL_MULTI) + { + g_sRootHub.ui32Flags |= USBLIB_HUB_HS | USBLIB_HUB_MULTI_TT; + } + + // + // Remember the device information for later. + // + g_sRootHub.psDevice = psDevice; + + // + // A hub must support an interrupt endpoint so check this. + // + if((psEndpointDescriptor->bmAttributes & USB_EP_ATTR_TYPE_M) == + USB_EP_ATTR_INT) + { + // + // The endpoint is the correct type. Is it an IN endpoint? + // + if(psEndpointDescriptor->bEndpointAddress & USB_EP_DESC_IN) + { + // + // Yes - all is well with the hub endpoint so allocate a pipe to + // handle traffic from the hub. + // + g_sRootHub.ui32IntInPipe = USBHCDPipeAlloc(0, USBHCD_PIPE_INTR_IN, + psDevice, + HubIntINCallback); + USBHCDPipeConfig(g_sRootHub.ui32IntInPipe, + psEndpointDescriptor->wMaxPacketSize, + psEndpointDescriptor->bInterval, + psEndpointDescriptor->bEndpointAddress & + USB_EP_DESC_NUM_M); + } + } + + // + // Did we allocate the endpoint successfully? + // + if(!g_sRootHub.ui32IntInPipe) + { + // + // No - return an error. + // + return 0; + } + + // + // Assuming we have a callback, call it to tell the owner that a hub is + // now connected. + // + if(g_sRootHub.pfnCallback != 0) + { + g_sRootHub.pfnCallback((void *)g_sRootHub.ui32CBData, + USB_EVENT_CONNECTED, (uint32_t)&g_sRootHub, 0); + } + + // + // Get the hub descriptor and store information we'll need for later. + // + bRetcode = GetHubDescriptor(&sHubDesc); + if(bRetcode) + { + + // + // We read the descriptor successfully so extract the parts we need. + // + g_sRootHub.ui8NumPorts = sHubDesc.bNbrPorts; + g_sRootHub.ui16HubCharacteristics = sHubDesc.wHubCharacteristics; + g_sRootHub.ui8NumPortsInUse = + (sHubDesc.bNbrPorts > MAX_USB_DEVICES) ? MAX_USB_DEVICES : + sHubDesc.bNbrPorts; + + // + // The size of the status change report that the hub sends is dependent + // upon the number of ports that the hub supports. Calculate this by + // adding 1 to the number of ports (bit 0 of the report is the hub + // status, higher bits are one per port) then dividing by 8 (bits per + // byte) and rounding up. + // + g_sRootHub.ui8ReportSize = ((sHubDesc.bNbrPorts + 1) + 7) / 8; + + // + // Enable power to all ports on the hub. + // + for(ui32Loop = 1; ui32Loop <= sHubDesc.bNbrPorts; ui32Loop++) + { + // + // Turn on power to this port. + // + HubSetPortFeature(&g_sRootHub, ui32Loop, + HUB_FEATURE_PORT_POWER); + } + + // + // Clear out our port state structures. + // + for(ui32Loop = 0; ui32Loop < MAX_USB_DEVICES; ui32Loop++) + { + g_sRootHub.psPorts[ui32Loop].bChanged = false; + g_sRootHub.psPorts[ui32Loop].iState = ePortIdle; + } + } + else + { + // + // Oops - we can't read the hub descriptor! Tidy up and return + // an error. + // + USBHCDPipeFree(g_sRootHub.ui32IntInPipe); + g_sRootHub.pfnCallback = 0; + g_sRootHub.ui32Flags &= ~USBLIB_HUB_ACTIVE; + return(0); + } + + // + // If we get here, all is well so remember that the hub is connected and + // active. + // + g_sRootHub.ui32Flags |= USBLIB_HUB_ACTIVE; + + // + // Return our instance data pointer to the caller to use as a handle. + // + return((void *)&g_sRootHub); +} + +//***************************************************************************** +// +// Close an instance of the hub driver. +// +//***************************************************************************** +static void +HubDriverClose(void *pvHubDevice) +{ + uint32_t ui32Loop; + + // + // No device so just exit. + // + if(g_sRootHub.psDevice == 0) + { + return; + } + + // + // Disconnect any devices that are currently connected to the hub. + // + for(ui32Loop = 0; ui32Loop < MAX_USB_DEVICES; ui32Loop++) + { + // + // Does this port have a device connected to it that we have previously + // reported to the host control layer?h + // + if((g_sRootHub.psPorts[ui32Loop].iState == ePortActive) || + (g_sRootHub.psPorts[ui32Loop].iState == ePortResetWait) || + (g_sRootHub.psPorts[ui32Loop].iState == ePortEnumerated) || + (g_sRootHub.psPorts[ui32Loop].iState == ePortError)) + { + // + // Yes - tell the host controller to disconnect the device. + // + USBHCDHubDeviceDisconnected(0, + g_sRootHub.psPorts[ui32Loop].ui32DevHandle); + + } + + // + // Make sure that the state returns to idle. + // + g_sRootHub.psPorts[ui32Loop].iState = ePortIdle; + + } + + // + // Reset the device pointer. + // + g_sRootHub.psDevice = 0; + + // + // Mark the hub as absent. + // + g_sRootHub.ui32Flags &= ~USBLIB_HUB_ACTIVE; + + // + // Note that we are not in the middle of enumerating anything. + // + g_sRootHub.bEnumerationBusy = false; + + // + // Free the Interrupt IN pipe. + // + if(g_sRootHub.ui32IntInPipe != 0) + { + USBHCDPipeFree(g_sRootHub.ui32IntInPipe); + } + + // + // If the callback exists, call it with a DISCONNECTED event. + // + if(g_sRootHub.pfnCallback != 0) + { + g_sRootHub.pfnCallback((void *)g_sRootHub.ui32CBData, + USB_EVENT_DISCONNECTED, (uint32_t)&g_sRootHub, + 0); + } +} + +//***************************************************************************** +// +// Perform any processing required as a result of a change in the reset +// signaling for a given port. +// +//***************************************************************************** +static void +HubDriverReset(uint8_t ui8Port, bool bResetActive) +{ + // + // Did the reset sequence end or begin? + // + if(!bResetActive) + { + // + // The reset ended. Now wait for at least 10ms before signaling + // USB enumeration code that a new device is waiting to be enumerated. + // + g_sRootHub.psPorts[ui8Port].iState = ePortResetWait; + + // + // Set the wait to 10ms (10 frames) from now. + // + g_sRootHub.psPorts[ui8Port].ui32Count = 10; + } + else + { + // + // Was this device previously active? + // + if(g_sRootHub.psPorts[ui8Port].iState == ePortActive) + { + USBHCDHubDeviceDisconnected(0, + g_sRootHub.psPorts[ui8Port].ui32DevHandle); + } + + // + // The reset is active so mark our port as in reset. + // + g_sRootHub.psPorts[ui8Port].iState = ePortResetActive; + } +} + +//***************************************************************************** +// +// Start the process of enumerating a new device by issuing a reset to the +// appropriate downstream port. +// +//***************************************************************************** +static void +HubDriverDeviceReset(uint8_t ui8Port) +{ + DEBUG_OUTPUT("Starting enumeration for port %d\n", ui8Port); + + // + // Record the fact that we are in the process of enumerating a device. + // + g_sRootHub.bEnumerationBusy = true; + + // + // Save the port that is being enumerated. + // + g_sRootHub.ui8EnumIdx = ui8Port; + + // + // Mark the port as being reset. + // + g_sRootHub.psPorts[ui8Port].iState = ePortResetActive; + + // + // Initiate a reset on the relevant port to start the enumeration process. + // + HubSetPortFeature(&g_sRootHub, ui8Port, HUB_FEATURE_PORT_RESET); +} + +//***************************************************************************** +// +// A new device has been connected to the hub. Allocate resources to manage +// it and pass details back to the main USB host enumeration code to have the +// device enumerated. +// +//***************************************************************************** +static void +HubDriverDeviceConnect(uint8_t ui8Port) +{ + DEBUG_OUTPUT("HubDriverDeviceConnect\n"); + + // + // We've allocated a port table entry so fill it in then initiate a reset + // on the device. + // + g_sRootHub.psPorts[ui8Port].bChanged = false; + + // + // Mark the port as having a device present but not enumerated. + // + DEBUG_OUTPUT("Deferring enumeration for port %d\n", ui8Port); + g_sRootHub.psPorts[ui8Port].iState = ePortConnected; + + // + // Wait 100ms to reset the device. + // + g_sRootHub.psPorts[ui8Port].ui32Count = 100; +} + +//***************************************************************************** +// +// An existing device has been removed from the hub. Tidy up and let the main +// USB host code know so that it can free device resources. +// +//***************************************************************************** +static void +HubDriverDeviceDisconnect(uint8_t ui8Port) +{ + // + // This is a device we are currently managing. Have we already informed + // the host controller that it is present? + // + if((g_sRootHub.psPorts[ui8Port].iState == ePortActive) || + (g_sRootHub.psPorts[ui8Port].iState == ePortResetWait) || + (g_sRootHub.psPorts[ui8Port].iState == ePortEnumerated) || + (g_sRootHub.psPorts[ui8Port].iState == ePortError)) + { + // + // Yes - tell the host controller that the device is not longer + // connected. + // + USBHCDHubDeviceDisconnected(0, + g_sRootHub.psPorts[ui8Port].ui32DevHandle); + } + + // + // If the device was being enumerated, make sure we clear the flag + // indicating that an enumeration is still ongoing. + // + if((g_sRootHub.psPorts[ui8Port].iState == ePortResetActive) || + (g_sRootHub.psPorts[ui8Port].iState == ePortResetWait) || + (g_sRootHub.psPorts[ui8Port].iState == ePortActive)) + { + g_sRootHub.bEnumerationBusy = false; + } + + // + // Free up the port state structure. + // + g_sRootHub.psPorts[ui8Port].iState = ePortIdle; +} + +//***************************************************************************** +// +// This function is called periodically by USBHCDMain(). We use it to handle +// the hub port state machine. +// +//***************************************************************************** +void +USBHHubMain(void) +{ + uint16_t ui16Status, ui16Changed; + uint_fast8_t ui8Port; + bool bRetcode; + + // + // If the hub is not present, just return. + // + if((g_sRootHub.ui32Flags & USBLIB_HUB_ACTIVE) == 0) + { + return; + } + + // + // Initialize the status variables. + // + ui16Status = 0; + ui16Changed = 0; + + // + // The hub is active and something changed. Check to see which port changed + // state and handle as necessary. + // + for(ui8Port = 0; ui8Port <= g_sRootHub.ui8NumPortsInUse; ui8Port++) + { + // + // Decrement any wait counter if there is one present. + // + if(g_sRootHub.psPorts[ui8Port].ui32Count != 0) + { + g_sRootHub.psPorts[ui8Port].ui32Count--; + } + + // + // Is this port waiting to be enumerated and is the last device + // enumeration finished? + // + if((g_sRootHub.psPorts[ui8Port].iState == ePortConnected) && + (!g_sRootHub.bEnumerationBusy) && + (g_sRootHub.psPorts[ui8Port].ui32Count == 0)) + { + // + // Yes - start the enumeration processing for this device. + // + HubDriverDeviceReset(ui8Port); + } + + // + // If the state is ePortResetWait then the hub is waiting before + // accessing device as the USB 2.0 specification requires. + // + if((g_sRootHub.psPorts[ui8Port].iState == ePortResetWait) && + (g_sRootHub.psPorts[ui8Port].ui32Count == 0)) + { + // + // Start the enumeration process if the timeout has passed and + // the hub is waiting to start enumerating the device. + // + g_sRootHub.psPorts[ui8Port].iState = ePortActive; + + // + // Call the main host controller layer to have it enumerate the + // newly connected device. + // + g_sRootHub.psPorts[ui8Port].ui32DevHandle = + USBHCDHubDeviceConnected(0, 1, ui8Port, + g_sRootHub.psPorts[ui8Port].ui32Speed); + } + + // + // If an enumeration is in progress and the loop is not on the port + // being enumerated then skip the port. + // + if(g_sRootHub.bEnumerationBusy && + (g_sRootHub.ui8EnumIdx != ui8Port)) + { + continue; + } + + // + // Did something change for this particular port? + // + if(g_ui32ChangeFlags & (1 << ui8Port)) + { + // + // Yes - query the port status. + // + bRetcode = HubGetPortStatus(&g_sRootHub, ui8Port, &ui16Status, + &ui16Changed); + + // + // Clear this change with the USB interrupt temporarily disabled to + // ensure that we do not clear a flag that the interrupt routine + // has just set. + // + OS_INT_DISABLE(g_sRootHub.ui32IntNum); + g_ui32ChangeFlags &= ~(1 << ui8Port); + OS_INT_ENABLE(g_sRootHub.ui32IntNum); + + // + // If there was an error, go on and look at the next bit. + // + if(!bRetcode) + { + continue; + } + + // + // Now consider what changed and handle it as necessary. + // + + // + // Was a device connected to or disconnected from the port? + // + if(ui16Changed & HUB_PORT_CHANGE_DEVICE_PRESENT) + { + DEBUG_OUTPUT("Connection change on port %d\n", ui8Port); + + // + // Clear the condition. + // + HubClearPortFeature(&g_sRootHub, ui8Port, + HUB_FEATURE_C_PORT_CONNECTION); + + // + // Was a device connected or disconnected? + // + if(ui16Status & HUB_PORT_STATUS_DEVICE_PRESENT) + { + DEBUG_OUTPUT("Connected\n"); + + // + // A device was connected. + // + HubDriverDeviceConnect(ui8Port); + } + else + { + DEBUG_OUTPUT("Disconnected\n"); + + // + // A device was disconnected. + // + HubDriverDeviceDisconnect(ui8Port); + } + } + + // + // Did a reset on the port complete? + // + if(ui16Changed & HUB_PORT_CHANGE_RESET) + { + // + // Clear the condition. + // + HubClearPortFeature(&g_sRootHub, ui8Port, + HUB_FEATURE_C_PORT_RESET); + + // + // Yes - query the port status. + // + bRetcode = HubGetPortStatus(&g_sRootHub, ui8Port, + &ui16Status, &ui16Changed); + + DEBUG_OUTPUT("Reset %s for port %d\n", + ((ui16Status & HUB_PORT_STATUS_RESET) ? "asserted" : + "deasserted"), ui8Port); + + // + // Handle the reset case. + // + HubDriverReset(ui8Port, (ui16Status & HUB_PORT_STATUS_RESET) ? + true : false); + + // + // A device was connected. + // + if(ui16Status & HUB_PORT_STATUS_LOW_SPEED) + { + USBHubPortSpeedSet(ui8Port, USB_EP_SPEED_LOW); + } + else if(ui16Status & HUB_PORT_STATUS_HIGH_SPEED) + { + USBHubPortSpeedSet(ui8Port, USB_EP_SPEED_HIGH); + } + else + { + USBHubPortSpeedSet(ui8Port, USB_EP_SPEED_FULL); + } + } + + // + // Did an over-current reset on the port complete? + // + if(ui16Changed & HUB_PORT_CHANGE_OVER_CURRENT) + { + DEBUG_OUTPUT("Port %d over current.\n", ui8Port); + + // + // Currently we ignore this and just clear the condition. + // + HubClearPortFeature(&g_sRootHub, ui8Port, + HUB_FEATURE_C_PORT_OVER_CURRENT); + } + + // + // Has the port been enabled or disabled? + // + if(ui16Changed & HUB_PORT_CHANGE_ENABLED) + { + DEBUG_OUTPUT("Enable change for port %d.\n", ui8Port); + + // + // Currently we ignore this and just clear the condition. + // + HubClearPortFeature(&g_sRootHub, ui8Port, + HUB_FEATURE_C_PORT_ENABLE); + } + + // + // Has the port been suspended or resumed? + // + if(ui16Changed & HUB_PORT_CHANGE_SUSPENDED) + { + DEBUG_OUTPUT("Suspend change for port %d.\n", ui8Port); + + // + // Currently we ignore this and just clear the condition. + // + HubClearPortFeature(&g_sRootHub, ui8Port, + HUB_FEATURE_C_PORT_SUSPEND); + } + } + } +} + +//***************************************************************************** +// +//! Informs the hub class driver that a downstream device has been enumerated. +//! +//! \param ui8Hub is the address of the hub to which the downstream device +//! is attached. +//! \param ui8Port is the port on the hub to which the downstream device is +//! attached. +//! +//! This function is called by the host controller driver to inform the hub +//! class driver that a downstream device has been enumerated successfully. +//! The hub driver then moves on and continues enumeration of any other newly +//! connected devices. +//! +//! \return None. +// +//***************************************************************************** +void +USBHHubEnumerationComplete(uint8_t ui8Hub, uint8_t ui8Port) +{ + DEBUG_OUTPUT("Enumeration complete for hub %d, port %d\n", ui8Hub, ui8Port); + + // + // Record the fact that the device is up and running. + // + g_sRootHub.psPorts[ui8Port].iState = ePortEnumerated; + + // + // Clear the flag we use to defer further enumerations. This will cause + // the next connected device (if any) to start enumeration on the next + // call to USBHHubMain(). + // + g_sRootHub.bEnumerationBusy = false; +} + +//***************************************************************************** +// +//! Informs the hub class driver that a downstream device failed to enumerate. +//! +//! \param ui8Hub is the address of the hub to which the downstream device +//! is attached. +//! \param ui8Port is the port on the hub to which the downstream device is +//! attached. +//! +//! This function is called by the host controller driver to inform the hub +//! class driver that an attempt to enumerate a downstream device has failed. +//! The hub driver then cleans up and continues enumeration of any other newly +//! connected devices. +//! +//! \return None. +// +//***************************************************************************** +void +USBHHubEnumerationError(uint8_t ui8Hub, uint8_t ui8Port) +{ + DEBUG_OUTPUT("Enumeration error for hub %d, port %d\n", ui8Hub, ui8Port); + + // + // Record the fact that the device is not working correctly. + // + g_sRootHub.psPorts[ui8Port].iState = ePortError; + + // + // Clear the flag we use to defer further enumerations. This will cause + // the next connected device (if any) to start enumeration on the next + // call to USBHHubMain(). + // + g_sRootHub.bEnumerationBusy = false; +} + +//***************************************************************************** +// +//! This function is used to enable the host hub class driver before any +//! devices are present. +//! +//! \param pfnCallback is the driver call back for host hub events. +//! +//! This function is called to open an instance of a host hub device and +//! provides a valid callback function for host hub events in the +//! \e pfnCallback parameter. This function must be called before the USB +//! host code can successfully enumerate a hub device or any devices attached +//! to the hub. The \e pui8HubPool is memory provided to the hub class to +//! manage the devices that are connected to the hub. The \e ui32PoolSize is +//! the number of bytes and should be at least 32 bytes per device including +//! the hub device itself. A simple formula for providing memory to the hub +//! class is \b MAX_USB_DEVICES * 32 bytes of data to allow for proper +//! enumeration of connected devices. The value for \b MAX_USB_DEVICES is +//! defined in the usblib.h file and controls the number of devices +//! supported by the USB library. The \e ui32NumHubs parameter +//! defaults to one and only one buffer of size tHubInstance is required to +//! be passed in the \e psHubInstance parameter. +//! +//! \note Changing the value of \b MAX_USB_DEVICES requires a rebuild of the +//! USB library to have an effect on the library. +//! +//! \return This function returns the driver instance to use for the other +//! host hub functions. If there is no instance available at the time of +//! this call, this function returns zero. +// +//***************************************************************************** +tHubInstance * +USBHHubOpen(tUSBHHubCallback pfnCallback) +{ + // + // Only one hub is supported. + // + if(g_sRootHub.pfnCallback) + { + DEBUG_OUTPUT("USBHHubOpen failed - already connected.\n"); + return(0); + } + + // + // Save the instance data for this device. + // + g_sRootHub.pfnCallback = pfnCallback; + + DEBUG_OUTPUT("USBHHubOpen completed.\n"); + + // + // Return the device instance pointer. + // + return(&g_sRootHub); +} + +//***************************************************************************** +// +//! This function is used to release a hub device instance. +//! +//! \param psHubInstance is the hub device instance that is to be released. +//! +//! This function is called when an instance of the hub device must be +//! released. This function is typically made in preparation for shutdown or a +//! switch to function as a USB device when in OTG mode. Following this call, +//! the hub device is no longer available, but it can be opened again using a +//! call to USBHHubOpen(). After calling USBHHubClose(), the host hub driver +//! no longer provides any callbacks or accepts calls to other hub driver APIs. +//! +//! \return None. +// +//***************************************************************************** +void +USBHHubClose(tHubInstance *psHubInstance) +{ + // + // Forget the instance pointer and callback. + // + psHubInstance->psDevice = 0; + psHubInstance->pfnCallback = 0; + + DEBUG_OUTPUT("USBHHubClose completed.\n"); +} + +//***************************************************************************** +// +// This function is used to initialize the Hub driver. This is an internal +// function that should not be called by the application. +// +//***************************************************************************** +void +USBHHubInit(void) +{ + // + // Initialize Hub state. + // + g_ui32ChangeFlags = 0; + g_ui32HubChanges = 0; + + if(g_sRootHub.psDevice != 0) + { + // + // Save the USB interrupt number. + // + g_sRootHub.ui32IntNum = INT_USB0_TM4C123; + + // + // These devices have a different USB interrupt number. + // + if(CLASS_IS_TM4C129) + { + g_sRootHub.ui32IntNum = INT_USB0_TM4C129; + } + } +} + +//***************************************************************************** +// +//! This function forwards an LPM request for a device to enter L1 sleep state. +//! +//! \param psHubInstance is the hub device instance that was returned +//! from the call to USBHHubOpen(). +//! +//! This function forwards a request from an application to the hub device +//! class to request that a device enter the LPM L1 sleep state. The +//! caller must check the return value to see if the request can be +//! attempted at this time. If another LPM transaction is busy on this or +//! another device, then this function returns \b USBHCD_LPM_PENDING. If +//! the LPM request was scheduled to be sent the function returns +//! \b USBHCD_LPM_AVAIL. The caller should check the USBHCDLPMStatus() +//! function to determine if the request completed successfully or if there +//! was an error. +//! +//! \return This function returns the following values: +//! - \b USBHCD_LPM_AVAIL - The transition to L1 state is scheduled to be sent. +//! - \b USBHCD_LPM_PENDING - There is already an LPM request pending. +// +//***************************************************************************** +uint32_t +USBHHubLPMSleep(tHubInstance *psHubInstance) +{ + // + // Call the host controller function to send the sleep command. + // + return(USBHCDLPMSleep(psHubInstance->psDevice)); +} + +//***************************************************************************** +// +//! This function returns the current status of an LPM request. +//! +//! \param psHubInstance is the hub device instance that was returned +//! from the call to USBHHubOpen(). +//! +//! This function returns the current status of LPM requests for a given +//! device. This is called to determine if a previous request completed +//! successfully or if there was an error. +//! +//! \return This function returns the following values: +//! - \b USBHCD_LPM_AVAIL - There are no pending LPM requests on this specific +//! device or the last request completed successfully. +//! - \b USBHCD_LPM_ERROR - The last LPM request for this device did not +//! complete successfully. +//! - \b USBHCD_LPM_PENDING - The last LPM request has not completed. +// +//***************************************************************************** +uint32_t +USBHHubLPMStatus(tHubInstance *psHubInstance) +{ + // + // Call the host controller function to get the current LPM status. + // + return(USBHCDLPMStatus(psHubInstance->psDevice)); +} + +//***************************************************************************** +// +//! @} +// +//***************************************************************************** -- cgit v1.3.1