//***************************************************************************** // // usbhhub.c - This file contains the host HID driver. // // Copyright (c) 2011-2013 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 1.1 of the Tiva USB Library. // //***************************************************************************** #include #include #include "inc/hw_types.h" #include "inc/hw_ints.h" #include "inc/hw_sysctl.h" #include "driverlib/usbdrv.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; // // 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 //***************************************************************************** // // 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 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 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, bool bLowSpeed) { 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; g_sRootHub.psPorts[ui8Port].bLowSpeed = bLowSpeed; // // 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].bLowSpeed); } // // 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, ((ui16Status & HUB_PORT_STATUS_LOW_SPEED) ? true : false)); } 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); } // // 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_BLIZZARD; } } //***************************************************************************** // //! @} // //*****************************************************************************