RT1050_FreeRTOS_USB_Hello/middleware/usb/host/class/usb_host_hub.c

623 lines
25 KiB
C

/*
* Copyright (c) 2015 - 2016, Freescale Semiconductor, Inc.
* Copyright 2016,2019 NXP
* All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "usb_host_config.h"
#if ((defined USB_HOST_CONFIG_HUB) && (USB_HOST_CONFIG_HUB))
#include "usb_host.h"
#include "usb_host_hub.h"
/*******************************************************************************
* Definitions
******************************************************************************/
/*******************************************************************************
* Prototypes
******************************************************************************/
/*!
* @brief hub control transfer callback.
*
* @param param callback parameter.
* @param transfer callback transfer.
* @param status transfer status.
*/
static void USB_HostHubControlCallback(void *param, usb_host_transfer_t *transfer, usb_status_t status);
/*!
* @brief hub interrupt in transfer callback.
*
* @param param callback parameter.
* @param transfer callback transfer.
* @param status transfer status.
*/
static void USB_HostHubInPipeCallback(void *param, usb_host_transfer_t *transfer, usb_status_t status);
/*!
* @brief USB_HostHubSendPortReset's transfer callback.
*
* @param param callback parameter.
* @param transfer callback transfer.
* @param status transfer status.
*/
static void USB_HostHubResetCallback(void *param, usb_host_transfer_t *transfer, usb_status_t status);
/*!
* @brief hub control transfer common code.
*
* @param classHandle the class handle.
* @param requestType request type.
* @param request setup packet request field.
* @param wvalue setup packet wValue field.
* @param windex setup packet wIndex field.
* @param buffer the buffer pointer.
* @param bufferLength the buffer length.
* @param callbackFn this callback is called after this function completes.
* @param callbackParam the first parameter in the callback function.
*
* @return kStatus_USB_Success or error codes.
*/
static usb_status_t USB_HostHubClassRequestCommon(usb_host_class_handle classHandle,
uint8_t requestType,
uint8_t request,
uint16_t wvalue,
uint16_t windex,
uint8_t *buffer,
uint16_t bufferLength,
transfer_callback_t callbackFn,
void *callbackParam);
/*******************************************************************************
* Variables
******************************************************************************/
/*******************************************************************************
* Code
******************************************************************************/
static void USB_HostHubControlCallback(void *param, usb_host_transfer_t *transfer, usb_status_t status)
{
usb_host_hub_instance_t *hubInstance = (usb_host_hub_instance_t *)param;
hubInstance->controlTransfer = NULL;
if (NULL != hubInstance->controlCallbackFn)
{
/* callback to application, callback function is initialized in the USB_HostPrinterControl,
USB_HostPrinterSetInterface
or USB_HostHubClassRequestCommon, but is the same function */
hubInstance->controlCallbackFn(hubInstance->controlCallbackParam, transfer->transferBuffer,
transfer->transferSofar, status); /* callback to application */
}
(void)USB_HostFreeTransfer(hubInstance->hostHandle, transfer);
}
static void USB_HostHubInPipeCallback(void *param, usb_host_transfer_t *transfer, usb_status_t status)
{
usb_host_hub_instance_t *hubInstance = (usb_host_hub_instance_t *)param;
if (NULL != hubInstance->inCallbackFn)
{
/* callback to application, callback function is initialized in the USB_HostHubInterruptRecv */
hubInstance->inCallbackFn(hubInstance->inCallbackParam, transfer->transferBuffer, transfer->transferSofar,
status); /* callback to application */
}
(void)USB_HostFreeTransfer(hubInstance->hostHandle, transfer);
}
static void USB_HostHubResetCallback(void *param, usb_host_transfer_t *transfer, usb_status_t status)
{
usb_host_hub_instance_t *hubInstance = (usb_host_hub_instance_t *)param;
/* note: there is not callback to application, the re-enumeration will start automatically after reset. */
(void)USB_HostFreeTransfer(hubInstance->hostHandle, transfer);
}
static usb_status_t USB_HostHubClassRequestCommon(usb_host_class_handle classHandle,
uint8_t requestType,
uint8_t request,
uint16_t wvalue,
uint16_t windex,
uint8_t *buffer,
uint16_t bufferLength,
transfer_callback_t callbackFn,
void *callbackParam)
{
usb_host_hub_instance_t *hubInstance = (usb_host_hub_instance_t *)classHandle;
usb_host_transfer_t *transfer;
if (hubInstance->controlTransfer != NULL)
{
return kStatus_USB_Busy;
}
/* get transfer */
if (USB_HostMallocTransfer(hubInstance->hostHandle, &transfer) != kStatus_USB_Success)
{
#ifdef HOST_ECHO
usb_echo("error to get transfer\r\n");
#endif
return kStatus_USB_Error;
}
/* save hub application callback */
hubInstance->controlCallbackFn = callbackFn;
hubInstance->controlCallbackParam = callbackParam;
/* initialize transfer */
transfer->transferBuffer = buffer;
transfer->transferLength = bufferLength;
transfer->callbackFn = USB_HostHubControlCallback;
transfer->callbackParam = hubInstance;
transfer->setupPacket->bmRequestType = requestType;
transfer->setupPacket->bRequest = request;
transfer->setupPacket->wValue = USB_SHORT_TO_LITTLE_ENDIAN(wvalue);
transfer->setupPacket->wIndex = USB_SHORT_TO_LITTLE_ENDIAN(windex);
transfer->setupPacket->wLength = USB_SHORT_TO_LITTLE_ENDIAN(bufferLength);
/* send transfer */
if (USB_HostSendSetup(hubInstance->hostHandle, hubInstance->controlPipe, transfer) != kStatus_USB_Success)
{
#ifdef HOST_ECHO
usb_echo("Error in hid get report descriptor\r\n");
#endif
(void)USB_HostFreeTransfer(hubInstance->hostHandle, transfer);
return kStatus_USB_Error;
}
hubInstance->controlTransfer = transfer; /* record the on-going setup transfer */
return kStatus_USB_Success;
}
usb_status_t USB_HostHubInit(usb_device_handle deviceHandle, usb_host_class_handle *classHandle)
{
/* malloc the hub instance */
usb_host_hub_instance_t *hubInstance =
(usb_host_hub_instance_t *)OSA_MemoryAllocate(sizeof(usb_host_hub_instance_t));
uint32_t *temp;
uint32_t infoValue;
if (hubInstance == NULL)
{
return kStatus_USB_AllocFail;
}
#if ((defined(USB_HOST_CONFIG_BUFFER_PROPERTY_CACHEABLE)) && (USB_HOST_CONFIG_BUFFER_PROPERTY_CACHEABLE > 0U))
hubInstance->hubDescriptor = (uint8_t *)SDK_Malloc(7 + (USB_HOST_HUB_MAX_PORT >> 3) + 1, USB_CACHE_LINESIZE);
hubInstance->portStatusBuffer = (uint8_t *)SDK_Malloc(4, USB_CACHE_LINESIZE);
hubInstance->hubStatusBuffer = (uint8_t *)SDK_Malloc(4, USB_CACHE_LINESIZE);
hubInstance->hubBitmapBuffer = (uint8_t *)SDK_Malloc((USB_HOST_HUB_MAX_PORT >> 3) + 1, USB_CACHE_LINESIZE);
#endif
/* initialize hub instance structure */
hubInstance->deviceHandle = deviceHandle;
hubInstance->interfaceHandle = NULL;
(void)USB_HostHelperGetPeripheralInformation(deviceHandle, (uint32_t)kUSB_HostGetHostHandle, &infoValue);
temp = (uint32_t *)infoValue;
hubInstance->hostHandle = (usb_host_handle)temp;
(void)USB_HostHelperGetPeripheralInformation(deviceHandle, (uint32_t)kUSB_HostGetDeviceControlPipe, &infoValue);
temp = (uint32_t *)infoValue;
hubInstance->controlPipe = (usb_host_pipe_handle)temp;
(void)USB_HostHelperGetPeripheralInformation(deviceHandle, (uint32_t)kUSB_HostGetDeviceLevel, &infoValue);
hubInstance->hubLevel = (uint8_t)infoValue;
*classHandle = hubInstance; /* return the hub class handle */
return kStatus_USB_Success;
}
usb_status_t USB_HostHubSetInterface(usb_host_class_handle classHandle,
usb_host_interface_handle interfaceHandle,
uint8_t alternateSetting,
transfer_callback_t callbackFn,
void *callbackParam)
{
usb_status_t status;
usb_host_hub_instance_t *hubInstance = (usb_host_hub_instance_t *)classHandle;
usb_host_interface_t *interface = (usb_host_interface_t *)interfaceHandle;
usb_descriptor_endpoint_t *epDesc = NULL;
usb_host_pipe_init_t pipeInit;
uint8_t epIndex;
if (classHandle == NULL)
{
return kStatus_USB_InvalidHandle;
}
hubInstance->interfaceHandle = interfaceHandle; /* save the interface handle */
/* notify the host driver that the interface is used by class */
status = USB_HostOpenDeviceInterface(hubInstance->deviceHandle, interfaceHandle);
if (status != kStatus_USB_Success)
{
return status;
}
/* close opened hub interrupt pipe */
if (hubInstance->interruptPipe != NULL)
{
status = USB_HostCancelTransfer(hubInstance->hostHandle, hubInstance->interruptPipe, NULL);
if (status != kStatus_USB_Success)
{
#ifdef HOST_ECHO
usb_echo("error when Cancel pipe\r\n");
#endif
}
status = USB_HostClosePipe(hubInstance->hostHandle, hubInstance->interruptPipe);
if (status != kStatus_USB_Success)
{
#ifdef HOST_ECHO
usb_echo("error when close pipe\r\n");
#endif
}
hubInstance->interruptPipe = NULL;
}
/* open hub interrupt pipe */
for (epIndex = 0; epIndex < interface->epCount; ++epIndex)
{
epDesc = interface->epList[epIndex].epDesc;
if (((epDesc->bEndpointAddress & USB_DESCRIPTOR_ENDPOINT_ADDRESS_DIRECTION_MASK) ==
USB_DESCRIPTOR_ENDPOINT_ADDRESS_DIRECTION_IN) &&
((epDesc->bmAttributes & USB_DESCRIPTOR_ENDPOINT_ATTRIBUTE_TYPE_MASK) == USB_ENDPOINT_INTERRUPT))
{
/* get pipe information from endpoint descriptor */
pipeInit.devInstance = hubInstance->deviceHandle;
pipeInit.pipeType = USB_ENDPOINT_INTERRUPT;
pipeInit.direction = USB_IN;
pipeInit.endpointAddress = (epDesc->bEndpointAddress & USB_DESCRIPTOR_ENDPOINT_ADDRESS_NUMBER_MASK);
pipeInit.interval = epDesc->bInterval;
pipeInit.maxPacketSize = (uint16_t)((USB_SHORT_FROM_LITTLE_ENDIAN_ADDRESS(epDesc->wMaxPacketSize) &
USB_DESCRIPTOR_ENDPOINT_MAXPACKETSIZE_SIZE_MASK));
pipeInit.numberPerUframe = (uint8_t)((USB_SHORT_FROM_LITTLE_ENDIAN_ADDRESS(epDesc->wMaxPacketSize) &
USB_DESCRIPTOR_ENDPOINT_MAXPACKETSIZE_MULT_TRANSACTIONS_MASK));
pipeInit.nakCount = USB_HOST_CONFIG_MAX_NAK;
/* open hub interrupt in pipe */
status = USB_HostOpenPipe(hubInstance->hostHandle, &hubInstance->interruptPipe, &pipeInit);
if (status != kStatus_USB_Success)
{
#ifdef HOST_ECHO
usb_echo("usb_host_hid_set_interface fail to open pipe\r\n");
#endif
return kStatus_USB_Error;
}
break;
}
}
/* hub don't support alternatesetting that is not 0 */
if (alternateSetting == 0U)
{
if (callbackFn != NULL)
{
callbackFn(callbackParam, NULL, 0, kStatus_USB_Success);
}
}
else
{
#ifdef HOST_ECHO
usb_echo("host don't support alternate setting\r\n");
#endif
return kStatus_USB_Error;
}
return status;
}
usb_status_t USB_HostHubDeinit(usb_device_handle deviceHandle, usb_host_class_handle classHandle)
{
usb_host_hub_instance_t *hubInstance = (usb_host_hub_instance_t *)classHandle;
usb_status_t status = kStatus_USB_Success;
if (deviceHandle == NULL)
{
return kStatus_USB_InvalidHandle;
}
if (classHandle != NULL)
{
/* close opened hub interrupt pipe */
if (hubInstance->interruptPipe != NULL)
{
status = USB_HostCancelTransfer(hubInstance->hostHandle, hubInstance->interruptPipe, NULL);
if (status != kStatus_USB_Success)
{
#ifdef HOST_ECHO
usb_echo("error when Cancel pipe\r\n");
#endif
}
status = USB_HostClosePipe(hubInstance->hostHandle, hubInstance->interruptPipe);
if (status != kStatus_USB_Success)
{
#ifdef HOST_ECHO
usb_echo("hub close interrupt pipe error\r\n");
#endif
}
hubInstance->interruptPipe = NULL;
}
/* cancel control transfer if exist */
if ((hubInstance->controlPipe != NULL) && (hubInstance->controlTransfer != NULL))
{
status =
USB_HostCancelTransfer(hubInstance->hostHandle, hubInstance->controlPipe, hubInstance->controlTransfer);
}
/* notify host driver that the interface will not be used */
(void)USB_HostCloseDeviceInterface(deviceHandle, hubInstance->interfaceHandle);
#if ((defined(USB_HOST_CONFIG_BUFFER_PROPERTY_CACHEABLE)) && (USB_HOST_CONFIG_BUFFER_PROPERTY_CACHEABLE > 0U))
SDK_Free(hubInstance->hubDescriptor);
SDK_Free(hubInstance->portStatusBuffer);
SDK_Free(hubInstance->hubStatusBuffer);
SDK_Free(hubInstance->hubBitmapBuffer);
#endif
OSA_MemoryFree(hubInstance);
}
else
{
/* notify host driver that the interface will not be used */
(void)USB_HostCloseDeviceInterface(deviceHandle, NULL);
}
return status;
}
usb_status_t USB_HostHubInterruptRecv(usb_host_class_handle classHandle,
uint8_t *buffer,
uint16_t bufferLength,
transfer_callback_t callbackFn,
void *callbackParam)
{
usb_host_hub_instance_t *hubInstance = (usb_host_hub_instance_t *)classHandle;
usb_host_transfer_t *transfer;
if (classHandle == NULL)
{
return kStatus_USB_InvalidHandle;
}
/* get transfer */
if (USB_HostMallocTransfer(hubInstance->hostHandle, &transfer) != kStatus_USB_Success)
{
#ifdef HOST_ECHO
usb_echo("error to get transfer\r\n");
#endif
return kStatus_USB_Error;
}
/* save hub application callback */
hubInstance->inCallbackFn = callbackFn;
hubInstance->inCallbackParam = callbackParam;
/* initialize transfer */
transfer->transferBuffer = buffer;
transfer->transferLength = bufferLength;
transfer->callbackFn = USB_HostHubInPipeCallback;
transfer->callbackParam = hubInstance;
/* call host driver API to receive data */
if (USB_HostRecv(hubInstance->hostHandle, hubInstance->interruptPipe, transfer) != kStatus_USB_Success)
{
#ifdef HOST_ECHO
usb_echo("failed to USB_HostRecv\r\n");
#endif
(void)USB_HostFreeTransfer(hubInstance->hostHandle, transfer);
return kStatus_USB_Error;
}
return kStatus_USB_Success;
}
usb_status_t USB_HostHubSendPortReset(usb_host_class_handle classHandle, uint8_t portNumber)
{
usb_host_hub_instance_t *hubInstance = (usb_host_hub_instance_t *)classHandle;
usb_host_transfer_t *transfer;
if (classHandle == NULL)
{
return kStatus_USB_InvalidHandle;
}
/* get transfer */
if (USB_HostMallocTransfer(hubInstance->hostHandle, &transfer) != kStatus_USB_Success)
{
#ifdef HOST_ECHO
usb_echo("error to get transfer\r\n");
#endif
return kStatus_USB_Busy;
}
/* initialize transfer */
transfer->transferBuffer = NULL;
transfer->transferLength = 0;
transfer->callbackFn = USB_HostHubResetCallback;
transfer->callbackParam = hubInstance;
transfer->setupPacket->bmRequestType =
USB_REQUEST_TYPE_DIR_OUT | USB_REQUEST_TYPE_TYPE_CLASS | USB_REQUEST_TYPE_RECIPIENT_OTHER;
transfer->setupPacket->bRequest = USB_REQUEST_STANDARD_SET_FEATURE;
transfer->setupPacket->wValue = USB_SHORT_TO_LITTLE_ENDIAN(PORT_RESET);
transfer->setupPacket->wIndex = USB_SHORT_TO_LITTLE_ENDIAN(portNumber);
transfer->setupPacket->wLength = 0;
/* send the transfer */
if (USB_HostSendSetup(hubInstance->hostHandle, hubInstance->controlPipe, transfer) != kStatus_USB_Success)
{
#ifdef HOST_ECHO
usb_echo("Error in hid get report descriptor\r\n");
#endif
(void)USB_HostFreeTransfer(hubInstance->hostHandle, transfer);
return kStatus_USB_Error;
}
return kStatus_USB_Success;
}
/*!
* @brief hub get descriptor.
*
* This function implements get hub descriptor specific request.
*
* @param classHandle the class handle.
* @param buffer the buffer pointer.
* @param bufferLength the buffer length.
* @param callbackFn this callback is called after this function completes.
* @param callbackParam the first parameter in the callback function.
*
* @retval kStatus_USB_Success send successfully.
* @retval kStatus_USB_InvalidHandle The classHandle is NULL pointer.
* @retval kStatus_USB_Busy There is no idle transfer.
* @retval kStatus_USB_Error pipe is not initialized.
* Or, send transfer fail, please reference to USB_HostSendSetup.
*/
usb_status_t USB_HostHubGetDescriptor(usb_host_class_handle classHandle,
uint8_t *buffer,
uint16_t bufferLength,
transfer_callback_t callbackFn,
void *callbackParam)
{
return USB_HostHubClassRequestCommon(
classHandle, USB_REQUEST_TYPE_DIR_IN | USB_REQUEST_TYPE_TYPE_CLASS | USB_REQUEST_TYPE_RECIPIENT_DEVICE,
USB_REQUEST_STANDARD_GET_DESCRIPTOR, 0x00, 0, buffer, bufferLength, callbackFn, callbackParam);
}
/*!
* @brief hub clear feature.
*
* This function implements clear hub feature specific request.
*
* @param classHandle the class handle.
* @param buffer the buffer pointer.
* @param bufferLength the buffer length.
* @param callbackFn this callback is called after this function completes.
* @param callbackParam the first parameter in the callback function.
*
* @retval kStatus_USB_Success send successfully.
* @retval kStatus_USB_InvalidHandle The classHandle is NULL pointer.
* @retval kStatus_USB_Busy There is no idle transfer.
* @retval kStatus_USB_Error pipe is not initialized.
* Or, send transfer fail, please reference to USB_HostSendSetup.
*/
usb_status_t USB_HostHubClearFeature(usb_host_class_handle classHandle,
uint8_t feature,
transfer_callback_t callbackFn,
void *callbackParam)
{
return USB_HostHubClassRequestCommon(classHandle, USB_REQUEST_TYPE_DIR_OUT | USB_REQUEST_TYPE_TYPE_CLASS,
USB_REQUEST_STANDARD_CLEAR_FEATURE, feature, 0, NULL, 0, callbackFn,
callbackParam);
}
/*!
* @brief hub get status.
*
* This function implements get hub status specific request.
*
* @param classHandle the class handle.
* @param buffer the buffer pointer.
* @param bufferLength the buffer length.
* @param callbackFn this callback is called after this function completes.
* @param callbackParam the first parameter in the callback function.
*
* @retval kStatus_USB_Success send successfully.
* @retval kStatus_USB_InvalidHandle The classHandle is NULL pointer.
* @retval kStatus_USB_Busy There is no idle transfer.
* @retval kStatus_USB_Error pipe is not initialized.
* Or, send transfer fail, please reference to USB_HostSendSetup.
*/
usb_status_t USB_HostHubGetStatus(usb_host_class_handle classHandle,
uint8_t *buffer,
uint16_t bufferLength,
transfer_callback_t callbackFn,
void *callbackParam)
{
return USB_HostHubClassRequestCommon(classHandle, USB_REQUEST_TYPE_DIR_IN | USB_REQUEST_TYPE_TYPE_CLASS,
USB_REQUEST_STANDARD_GET_STATUS, 0, 0, buffer, bufferLength, callbackFn,
callbackParam);
}
/*!
* @brief hub set feature.
*
* This function implements set hub feature specific request.
*
* @param classHandle the class handle.
* @param buffer the buffer pointer.
* @param bufferLength the buffer length.
* @param callbackFn this callback is called after this function completes.
* @param callbackParam the first parameter in the callback function.
*
* @retval kStatus_USB_Success send successfully.
* @retval kStatus_USB_InvalidHandle The classHandle is NULL pointer.
* @retval kStatus_USB_Busy There is no idle transfer.
* @retval kStatus_USB_Error pipe is not initialized.
* Or, send transfer fail, please reference to USB_HostSendSetup.
*/
usb_status_t USB_HostHubSetPortFeature(usb_host_class_handle classHandle,
uint8_t portNumber,
uint8_t feature,
transfer_callback_t callbackFn,
void *callbackParam)
{
return USB_HostHubClassRequestCommon(
classHandle, USB_REQUEST_TYPE_DIR_OUT | USB_REQUEST_TYPE_TYPE_CLASS | USB_REQUEST_TYPE_RECIPIENT_OTHER,
USB_REQUEST_STANDARD_SET_FEATURE, feature, portNumber, NULL, 0, callbackFn, callbackParam);
}
/*!
* @brief hub clear port feature.
*
* This function implements clear hub port feature specific request.
*
* @param classHandle the class handle.
* @param buffer the buffer pointer.
* @param bufferLength the buffer length.
* @param callbackFn this callback is called after this function completes.
* @param callbackParam the first parameter in the callback function.
*
* @retval kStatus_USB_Success send successfully.
* @retval kStatus_USB_InvalidHandle The classHandle is NULL pointer.
* @retval kStatus_USB_Busy There is no idle transfer.
* @retval kStatus_USB_Error pipe is not initialized.
* Or, send transfer fail, please reference to USB_HostSendSetup.
*/
usb_status_t USB_HostHubClearPortFeature(usb_host_class_handle classHandle,
uint8_t portNumber,
uint8_t feature,
transfer_callback_t callbackFn,
void *callbackParam)
{
return USB_HostHubClassRequestCommon(
classHandle, USB_REQUEST_TYPE_DIR_OUT | USB_REQUEST_TYPE_TYPE_CLASS | USB_REQUEST_TYPE_RECIPIENT_OTHER,
USB_REQUEST_STANDARD_CLEAR_FEATURE, feature, portNumber, NULL, 0, callbackFn, callbackParam);
}
/*!
* @brief hub port get status.
*
* This function implements get hub port status specific request.
*
* @param classHandle the class handle.
* @param buffer the buffer pointer.
* @param bufferLength the buffer length.
* @param callbackFn this callback is called after this function completes.
* @param callbackParam the first parameter in the callback function.
*
* @retval kStatus_USB_Success send successfully.
* @retval kStatus_USB_InvalidHandle The classHandle is NULL pointer.
* @retval kStatus_USB_Busy There is no idle transfer.
* @retval kStatus_USB_Error pipe is not initialized.
* Or, send transfer fail, please reference to USB_HostSendSetup.
*/
usb_status_t USB_HostHubGetPortStatus(usb_host_class_handle classHandle,
uint8_t portNumber,
uint8_t *buffer,
uint16_t bufferLength,
transfer_callback_t callbackFn,
void *callbackParam)
{
return USB_HostHubClassRequestCommon(
classHandle, USB_REQUEST_TYPE_DIR_IN | USB_REQUEST_TYPE_TYPE_CLASS | USB_REQUEST_TYPE_RECIPIENT_OTHER,
USB_REQUEST_STANDARD_GET_STATUS, 0, portNumber, buffer, bufferLength, callbackFn, callbackParam);
}
#endif /* USB_HOST_CONFIG_HUB */