858 lines
23 KiB
C
858 lines
23 KiB
C
|
/**
|
||
|
******************************************************************************
|
||
|
* @file usbh_core.c
|
||
|
* @author MCD Application Team
|
||
|
* @version V2.1.0
|
||
|
* @date 19-March-2012
|
||
|
* @brief This file implements the functions for the core state machine process
|
||
|
* the enumeration and the control transfer process
|
||
|
******************************************************************************
|
||
|
* @attention
|
||
|
*
|
||
|
* <h2><center>© COPYRIGHT 2012 STMicroelectronics</center></h2>
|
||
|
*
|
||
|
* Licensed under MCD-ST Liberty SW License Agreement V2, (the "License");
|
||
|
* You may not use this file except in compliance with the License.
|
||
|
* You may obtain a copy of the License at:
|
||
|
*
|
||
|
* http://www.st.com/software_license_agreement_liberty_v2
|
||
|
*
|
||
|
* Unless required by applicable law or agreed to in writing, software
|
||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
|
* See the License for the specific language governing permissions and
|
||
|
* limitations under the License.
|
||
|
*
|
||
|
******************************************************************************
|
||
|
*/
|
||
|
/* Includes ------------------------------------------------------------------*/
|
||
|
|
||
|
#include "usbh_ioreq.h"
|
||
|
#include "usb_bsp.h"
|
||
|
#include "usbh_hcs.h"
|
||
|
#include "usbh_stdreq.h"
|
||
|
#include "usbh_core.h"
|
||
|
#include "usb_hcd_int.h"
|
||
|
|
||
|
|
||
|
/** @addtogroup USBH_LIB
|
||
|
* @{
|
||
|
*/
|
||
|
|
||
|
/** @addtogroup USBH_LIB_CORE
|
||
|
* @{
|
||
|
*/
|
||
|
|
||
|
/** @defgroup USBH_CORE
|
||
|
* @brief TThis file handles the basic enumaration when a device is connected
|
||
|
* to the host.
|
||
|
* @{
|
||
|
*/
|
||
|
|
||
|
/** @defgroup USBH_CORE_Private_TypesDefinitions
|
||
|
* @{
|
||
|
*/
|
||
|
uint8_t USBH_Disconnected (USB_OTG_CORE_HANDLE *pdev);
|
||
|
uint8_t USBH_Connected (USB_OTG_CORE_HANDLE *pdev);
|
||
|
uint8_t USBH_SOF (USB_OTG_CORE_HANDLE *pdev);
|
||
|
|
||
|
USBH_HCD_INT_cb_TypeDef USBH_HCD_INT_cb =
|
||
|
{
|
||
|
USBH_SOF,
|
||
|
USBH_Connected,
|
||
|
USBH_Disconnected,
|
||
|
};
|
||
|
|
||
|
USBH_HCD_INT_cb_TypeDef *USBH_HCD_INT_fops = &USBH_HCD_INT_cb;
|
||
|
/**
|
||
|
* @}
|
||
|
*/
|
||
|
|
||
|
|
||
|
/** @defgroup USBH_CORE_Private_Defines
|
||
|
* @{
|
||
|
*/
|
||
|
/**
|
||
|
* @}
|
||
|
*/
|
||
|
|
||
|
|
||
|
/** @defgroup USBH_CORE_Private_Macros
|
||
|
* @{
|
||
|
*/
|
||
|
/**
|
||
|
* @}
|
||
|
*/
|
||
|
|
||
|
|
||
|
/** @defgroup USBH_CORE_Private_Variables
|
||
|
* @{
|
||
|
*/
|
||
|
/**
|
||
|
* @}
|
||
|
*/
|
||
|
|
||
|
|
||
|
/** @defgroup USBH_CORE_Private_FunctionPrototypes
|
||
|
* @{
|
||
|
*/
|
||
|
static USBH_Status USBH_HandleEnum(USB_OTG_CORE_HANDLE *pdev, USBH_HOST *phost);
|
||
|
USBH_Status USBH_HandleControl (USB_OTG_CORE_HANDLE *pdev, USBH_HOST *phost);
|
||
|
|
||
|
/**
|
||
|
* @}
|
||
|
*/
|
||
|
|
||
|
|
||
|
/** @defgroup USBH_CORE_Private_Functions
|
||
|
* @{
|
||
|
*/
|
||
|
|
||
|
|
||
|
/**
|
||
|
* @brief USBH_Connected
|
||
|
* USB Connect callback function from the Interrupt.
|
||
|
* @param selected device
|
||
|
* @retval Status
|
||
|
*/
|
||
|
uint8_t USBH_Connected (USB_OTG_CORE_HANDLE *pdev)
|
||
|
{
|
||
|
pdev->host.ConnSts = 1;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @brief USBH_Disconnected
|
||
|
* USB Disconnect callback function from the Interrupt.
|
||
|
* @param selected device
|
||
|
* @retval Status
|
||
|
*/
|
||
|
|
||
|
uint8_t USBH_Disconnected (USB_OTG_CORE_HANDLE *pdev)
|
||
|
{
|
||
|
pdev->host.ConnSts = 0;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @brief USBH_SOF
|
||
|
* USB SOF callback function from the Interrupt.
|
||
|
* @param selected device
|
||
|
* @retval Status
|
||
|
*/
|
||
|
|
||
|
uint8_t USBH_SOF (USB_OTG_CORE_HANDLE *pdev)
|
||
|
{
|
||
|
/* This callback could be used to implement a scheduler process */
|
||
|
return 0;
|
||
|
}
|
||
|
/**
|
||
|
* @brief USBH_Init
|
||
|
* Host hardware and stack initializations
|
||
|
* @param class_cb: Class callback structure address
|
||
|
* @param usr_cb: User callback structure address
|
||
|
* @retval None
|
||
|
*/
|
||
|
void USBH_Init(USB_OTG_CORE_HANDLE *pdev,
|
||
|
USB_OTG_CORE_ID_TypeDef coreID,
|
||
|
USBH_HOST *phost,
|
||
|
USBH_Class_cb_TypeDef *class_cb,
|
||
|
USBH_Usr_cb_TypeDef *usr_cb)
|
||
|
{
|
||
|
|
||
|
/* Hardware Init */
|
||
|
USB_OTG_BSP_Init(pdev);
|
||
|
|
||
|
/* configure GPIO pin used for switching VBUS power */
|
||
|
USB_OTG_BSP_ConfigVBUS(0);
|
||
|
|
||
|
|
||
|
/* Host de-initializations */
|
||
|
USBH_DeInit(pdev, phost);
|
||
|
|
||
|
/*Register class and user callbacks */
|
||
|
phost->class_cb = class_cb;
|
||
|
phost->usr_cb = usr_cb;
|
||
|
|
||
|
/* Start the USB OTG core */
|
||
|
HCD_Init(pdev , coreID);
|
||
|
|
||
|
/* Upon Init call usr call back */
|
||
|
phost->usr_cb->Init();
|
||
|
|
||
|
/* Enable Interrupts */
|
||
|
USB_OTG_BSP_EnableInterrupt(pdev);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @brief USBH_DeInit
|
||
|
* Re-Initialize Host
|
||
|
* @param None
|
||
|
* @retval status: USBH_Status
|
||
|
*/
|
||
|
USBH_Status USBH_DeInit(USB_OTG_CORE_HANDLE *pdev, USBH_HOST *phost)
|
||
|
{
|
||
|
/* Software Init */
|
||
|
|
||
|
phost->gState = HOST_IDLE;
|
||
|
phost->gStateBkp = HOST_IDLE;
|
||
|
phost->EnumState = ENUM_IDLE;
|
||
|
phost->RequestState = CMD_SEND;
|
||
|
|
||
|
phost->Control.state = CTRL_SETUP;
|
||
|
phost->Control.ep0size = USB_OTG_MAX_EP0_SIZE;
|
||
|
|
||
|
phost->device_prop.address = USBH_DEVICE_ADDRESS_DEFAULT;
|
||
|
phost->device_prop.speed = HPRT0_PRTSPD_FULL_SPEED;
|
||
|
|
||
|
USBH_Free_Channel (pdev, phost->Control.hc_num_in);
|
||
|
USBH_Free_Channel (pdev, phost->Control.hc_num_out);
|
||
|
return USBH_OK;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @brief USBH_Process
|
||
|
* USB Host core main state machine process
|
||
|
* @param None
|
||
|
* @retval None
|
||
|
*/
|
||
|
void USBH_Process(USB_OTG_CORE_HANDLE *pdev , USBH_HOST *phost)
|
||
|
{
|
||
|
volatile USBH_Status status = USBH_FAIL;
|
||
|
|
||
|
|
||
|
/* check for Host port events */
|
||
|
if ((HCD_IsDeviceConnected(pdev) == 0)&& (phost->gState != HOST_IDLE))
|
||
|
{
|
||
|
if(phost->gState != HOST_DEV_DISCONNECTED)
|
||
|
{
|
||
|
phost->gState = HOST_DEV_DISCONNECTED;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
switch (phost->gState)
|
||
|
{
|
||
|
|
||
|
case HOST_IDLE :
|
||
|
|
||
|
if (HCD_IsDeviceConnected(pdev))
|
||
|
{
|
||
|
phost->gState = HOST_DEV_ATTACHED;
|
||
|
USB_OTG_BSP_mDelay(100);
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case HOST_DEV_ATTACHED :
|
||
|
|
||
|
phost->usr_cb->DeviceAttached();
|
||
|
phost->Control.hc_num_out = USBH_Alloc_Channel(pdev, 0x00);
|
||
|
phost->Control.hc_num_in = USBH_Alloc_Channel(pdev, 0x80);
|
||
|
|
||
|
/* Reset USB Device */
|
||
|
if ( HCD_ResetPort(pdev) == 0)
|
||
|
{
|
||
|
phost->usr_cb->ResetDevice();
|
||
|
/* Wait for USB USBH_ISR_PrtEnDisableChange()
|
||
|
Host is Now ready to start the Enumeration
|
||
|
*/
|
||
|
|
||
|
phost->device_prop.speed = HCD_GetCurrentSpeed(pdev);
|
||
|
|
||
|
phost->gState = HOST_ENUMERATION;
|
||
|
phost->usr_cb->DeviceSpeedDetected(phost->device_prop.speed);
|
||
|
|
||
|
/* Open Control pipes */
|
||
|
USBH_Open_Channel (pdev,
|
||
|
phost->Control.hc_num_in,
|
||
|
phost->device_prop.address,
|
||
|
phost->device_prop.speed,
|
||
|
EP_TYPE_CTRL,
|
||
|
phost->Control.ep0size);
|
||
|
|
||
|
/* Open Control pipes */
|
||
|
USBH_Open_Channel (pdev,
|
||
|
phost->Control.hc_num_out,
|
||
|
phost->device_prop.address,
|
||
|
phost->device_prop.speed,
|
||
|
EP_TYPE_CTRL,
|
||
|
phost->Control.ep0size);
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case HOST_ENUMERATION:
|
||
|
/* Check for enumeration status */
|
||
|
if ( USBH_HandleEnum(pdev , phost) == USBH_OK)
|
||
|
{
|
||
|
/* The function shall return USBH_OK when full enumeration is complete */
|
||
|
|
||
|
/* user callback for end of device basic enumeration */
|
||
|
phost->usr_cb->EnumerationDone();
|
||
|
|
||
|
phost->gState = HOST_USR_INPUT;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case HOST_USR_INPUT:
|
||
|
/*The function should return user response true to move to class state */
|
||
|
if ( phost->usr_cb->UserInput() == USBH_USR_RESP_OK)
|
||
|
{
|
||
|
if((phost->class_cb->Init(pdev, phost))\
|
||
|
== USBH_OK)
|
||
|
{
|
||
|
phost->gState = HOST_CLASS_REQUEST;
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case HOST_CLASS_REQUEST:
|
||
|
/* process class standard contol requests state machine */
|
||
|
status = phost->class_cb->Requests(pdev, phost);
|
||
|
|
||
|
if(status == USBH_OK)
|
||
|
{
|
||
|
phost->gState = HOST_CLASS;
|
||
|
}
|
||
|
|
||
|
else
|
||
|
{
|
||
|
USBH_ErrorHandle(phost, status);
|
||
|
}
|
||
|
|
||
|
|
||
|
break;
|
||
|
case HOST_CLASS:
|
||
|
/* process class state machine */
|
||
|
status = phost->class_cb->Machine(pdev, phost);
|
||
|
USBH_ErrorHandle(phost, status);
|
||
|
break;
|
||
|
|
||
|
case HOST_CTRL_XFER:
|
||
|
/* process control transfer state machine */
|
||
|
USBH_HandleControl(pdev, phost);
|
||
|
break;
|
||
|
|
||
|
case HOST_SUSPENDED:
|
||
|
break;
|
||
|
|
||
|
case HOST_ERROR_STATE:
|
||
|
/* Re-Initilaize Host for new Enumeration */
|
||
|
USBH_DeInit(pdev, phost);
|
||
|
phost->usr_cb->DeInit();
|
||
|
phost->class_cb->DeInit(pdev, &phost->device_prop);
|
||
|
break;
|
||
|
|
||
|
case HOST_DEV_DISCONNECTED :
|
||
|
|
||
|
/* Manage User disconnect operations*/
|
||
|
phost->usr_cb->DeviceDisconnected();
|
||
|
|
||
|
/* Re-Initilaize Host for new Enumeration */
|
||
|
USBH_DeInit(pdev, phost);
|
||
|
phost->usr_cb->DeInit();
|
||
|
phost->class_cb->DeInit(pdev, &phost->device_prop);
|
||
|
USBH_DeAllocate_AllChannel(pdev);
|
||
|
phost->gState = HOST_IDLE;
|
||
|
|
||
|
break;
|
||
|
|
||
|
default :
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* @brief USBH_ErrorHandle
|
||
|
* This function handles the Error on Host side.
|
||
|
* @param errType : Type of Error or Busy/OK state
|
||
|
* @retval None
|
||
|
*/
|
||
|
void USBH_ErrorHandle(USBH_HOST *phost, USBH_Status errType)
|
||
|
{
|
||
|
/* Error unrecovered or not supported device speed */
|
||
|
if ( (errType == USBH_ERROR_SPEED_UNKNOWN) ||
|
||
|
(errType == USBH_UNRECOVERED_ERROR) )
|
||
|
{
|
||
|
phost->usr_cb->UnrecoveredError();
|
||
|
phost->gState = HOST_ERROR_STATE;
|
||
|
}
|
||
|
/* USB host restart requested from application layer */
|
||
|
else if(errType == USBH_APPLY_DEINIT)
|
||
|
{
|
||
|
phost->gState = HOST_ERROR_STATE;
|
||
|
/* user callback for initalization */
|
||
|
phost->usr_cb->Init();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* @brief USBH_HandleEnum
|
||
|
* This function includes the complete enumeration process
|
||
|
* @param pdev: Selected device
|
||
|
* @retval USBH_Status
|
||
|
*/
|
||
|
static USBH_Status USBH_HandleEnum(USB_OTG_CORE_HANDLE *pdev, USBH_HOST *phost)
|
||
|
{
|
||
|
USBH_Status Status = USBH_BUSY;
|
||
|
uint8_t Local_Buffer[64];
|
||
|
|
||
|
switch (phost->EnumState)
|
||
|
{
|
||
|
case ENUM_IDLE:
|
||
|
/* Get Device Desc for only 1st 8 bytes : To get EP0 MaxPacketSize */
|
||
|
if ( USBH_Get_DevDesc(pdev , phost, 8) == USBH_OK)
|
||
|
{
|
||
|
phost->Control.ep0size = phost->device_prop.Dev_Desc.bMaxPacketSize;
|
||
|
|
||
|
/* Issue Reset */
|
||
|
HCD_ResetPort(pdev);
|
||
|
phost->EnumState = ENUM_GET_FULL_DEV_DESC;
|
||
|
|
||
|
/* modify control channels configuration for MaxPacket size */
|
||
|
USBH_Modify_Channel (pdev,
|
||
|
phost->Control.hc_num_out,
|
||
|
0,
|
||
|
0,
|
||
|
0,
|
||
|
phost->Control.ep0size);
|
||
|
|
||
|
USBH_Modify_Channel (pdev,
|
||
|
phost->Control.hc_num_in,
|
||
|
0,
|
||
|
0,
|
||
|
0,
|
||
|
phost->Control.ep0size);
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case ENUM_GET_FULL_DEV_DESC:
|
||
|
/* Get FULL Device Desc */
|
||
|
if ( USBH_Get_DevDesc(pdev, phost, USB_DEVICE_DESC_SIZE)\
|
||
|
== USBH_OK)
|
||
|
{
|
||
|
/* user callback for device descriptor available */
|
||
|
phost->usr_cb->DeviceDescAvailable(&phost->device_prop.Dev_Desc);
|
||
|
phost->EnumState = ENUM_SET_ADDR;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case ENUM_SET_ADDR:
|
||
|
/* set address */
|
||
|
if ( USBH_SetAddress(pdev, phost, USBH_DEVICE_ADDRESS) == USBH_OK)
|
||
|
{
|
||
|
USB_OTG_BSP_mDelay(2);
|
||
|
phost->device_prop.address = USBH_DEVICE_ADDRESS;
|
||
|
|
||
|
/* user callback for device address assigned */
|
||
|
phost->usr_cb->DeviceAddressAssigned();
|
||
|
phost->EnumState = ENUM_GET_CFG_DESC;
|
||
|
|
||
|
/* modify control channels to update device address */
|
||
|
USBH_Modify_Channel (pdev,
|
||
|
phost->Control.hc_num_in,
|
||
|
phost->device_prop.address,
|
||
|
0,
|
||
|
0,
|
||
|
0);
|
||
|
|
||
|
USBH_Modify_Channel (pdev,
|
||
|
phost->Control.hc_num_out,
|
||
|
phost->device_prop.address,
|
||
|
0,
|
||
|
0,
|
||
|
0);
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case ENUM_GET_CFG_DESC:
|
||
|
/* get standard configuration descriptor */
|
||
|
if ( USBH_Get_CfgDesc(pdev,
|
||
|
phost,
|
||
|
USB_CONFIGURATION_DESC_SIZE) == USBH_OK)
|
||
|
{
|
||
|
phost->EnumState = ENUM_GET_FULL_CFG_DESC;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case ENUM_GET_FULL_CFG_DESC:
|
||
|
/* get FULL config descriptor (config, interface, endpoints) */
|
||
|
if (USBH_Get_CfgDesc(pdev,
|
||
|
phost,
|
||
|
phost->device_prop.Cfg_Desc.wTotalLength) == USBH_OK)
|
||
|
{
|
||
|
/* User callback for configuration descriptors available */
|
||
|
phost->usr_cb->ConfigurationDescAvailable(&phost->device_prop.Cfg_Desc,
|
||
|
phost->device_prop.Itf_Desc,
|
||
|
phost->device_prop.Ep_Desc[0]);
|
||
|
|
||
|
phost->EnumState = ENUM_GET_MFC_STRING_DESC;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case ENUM_GET_MFC_STRING_DESC:
|
||
|
if (phost->device_prop.Dev_Desc.iManufacturer != 0)
|
||
|
{ /* Check that Manufacturer String is available */
|
||
|
|
||
|
if ( USBH_Get_StringDesc(pdev,
|
||
|
phost,
|
||
|
phost->device_prop.Dev_Desc.iManufacturer,
|
||
|
Local_Buffer ,
|
||
|
0xff) == USBH_OK)
|
||
|
{
|
||
|
/* User callback for Manufacturing string */
|
||
|
phost->usr_cb->ManufacturerString(Local_Buffer);
|
||
|
phost->EnumState = ENUM_GET_PRODUCT_STRING_DESC;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
phost->usr_cb->ManufacturerString("N/A");
|
||
|
phost->EnumState = ENUM_GET_PRODUCT_STRING_DESC;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case ENUM_GET_PRODUCT_STRING_DESC:
|
||
|
if (phost->device_prop.Dev_Desc.iProduct != 0)
|
||
|
{ /* Check that Product string is available */
|
||
|
if ( USBH_Get_StringDesc(pdev,
|
||
|
phost,
|
||
|
phost->device_prop.Dev_Desc.iProduct,
|
||
|
Local_Buffer,
|
||
|
0xff) == USBH_OK)
|
||
|
{
|
||
|
/* User callback for Product string */
|
||
|
phost->usr_cb->ProductString(Local_Buffer);
|
||
|
phost->EnumState = ENUM_GET_SERIALNUM_STRING_DESC;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
phost->usr_cb->ProductString("N/A");
|
||
|
phost->EnumState = ENUM_GET_SERIALNUM_STRING_DESC;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case ENUM_GET_SERIALNUM_STRING_DESC:
|
||
|
if (phost->device_prop.Dev_Desc.iSerialNumber != 0)
|
||
|
{ /* Check that Serial number string is available */
|
||
|
if ( USBH_Get_StringDesc(pdev,
|
||
|
phost,
|
||
|
phost->device_prop.Dev_Desc.iSerialNumber,
|
||
|
Local_Buffer,
|
||
|
0xff) == USBH_OK)
|
||
|
{
|
||
|
/* User callback for Serial number string */
|
||
|
phost->usr_cb->SerialNumString(Local_Buffer);
|
||
|
phost->EnumState = ENUM_SET_CONFIGURATION;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
phost->usr_cb->SerialNumString("N/A");
|
||
|
phost->EnumState = ENUM_SET_CONFIGURATION;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case ENUM_SET_CONFIGURATION:
|
||
|
/* set configuration (default config) */
|
||
|
if (USBH_SetCfg(pdev,
|
||
|
phost,
|
||
|
phost->device_prop.Cfg_Desc.bConfigurationValue) == USBH_OK)
|
||
|
{
|
||
|
phost->EnumState = ENUM_DEV_CONFIGURED;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
|
||
|
case ENUM_DEV_CONFIGURED:
|
||
|
/* user callback for enumeration done */
|
||
|
Status = USBH_OK;
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
return Status;
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* @brief USBH_HandleControl
|
||
|
* Handles the USB control transfer state machine
|
||
|
* @param pdev: Selected device
|
||
|
* @retval Status
|
||
|
*/
|
||
|
USBH_Status USBH_HandleControl (USB_OTG_CORE_HANDLE *pdev, USBH_HOST *phost)
|
||
|
{
|
||
|
uint8_t direction;
|
||
|
static uint16_t timeout = 0;
|
||
|
USBH_Status status = USBH_OK;
|
||
|
URB_STATE URB_Status = URB_IDLE;
|
||
|
|
||
|
phost->Control.status = CTRL_START;
|
||
|
|
||
|
|
||
|
switch (phost->Control.state)
|
||
|
{
|
||
|
case CTRL_SETUP:
|
||
|
/* send a SETUP packet */
|
||
|
USBH_CtlSendSetup (pdev,
|
||
|
phost->Control.setup.d8 ,
|
||
|
phost->Control.hc_num_out);
|
||
|
phost->Control.state = CTRL_SETUP_WAIT;
|
||
|
break;
|
||
|
|
||
|
case CTRL_SETUP_WAIT:
|
||
|
|
||
|
URB_Status = HCD_GetURB_State(pdev , phost->Control.hc_num_out);
|
||
|
/* case SETUP packet sent successfully */
|
||
|
if(URB_Status == URB_DONE)
|
||
|
{
|
||
|
direction = (phost->Control.setup.b.bmRequestType & USB_REQ_DIR_MASK);
|
||
|
|
||
|
/* check if there is a data stage */
|
||
|
if (phost->Control.setup.b.wLength.w != 0 )
|
||
|
{
|
||
|
timeout = DATA_STAGE_TIMEOUT;
|
||
|
if (direction == USB_D2H)
|
||
|
{
|
||
|
/* Data Direction is IN */
|
||
|
phost->Control.state = CTRL_DATA_IN;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
/* Data Direction is OUT */
|
||
|
phost->Control.state = CTRL_DATA_OUT;
|
||
|
}
|
||
|
}
|
||
|
/* No DATA stage */
|
||
|
else
|
||
|
{
|
||
|
timeout = NODATA_STAGE_TIMEOUT;
|
||
|
|
||
|
/* If there is No Data Transfer Stage */
|
||
|
if (direction == USB_D2H)
|
||
|
{
|
||
|
/* Data Direction is IN */
|
||
|
phost->Control.state = CTRL_STATUS_OUT;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
/* Data Direction is OUT */
|
||
|
phost->Control.state = CTRL_STATUS_IN;
|
||
|
}
|
||
|
}
|
||
|
/* Set the delay timer to enable timeout for data stage completion */
|
||
|
phost->Control.timer = HCD_GetCurrentFrame(pdev);
|
||
|
}
|
||
|
else if(URB_Status == URB_ERROR)
|
||
|
{
|
||
|
phost->Control.state = CTRL_ERROR;
|
||
|
phost->Control.status = CTRL_XACTERR;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case CTRL_DATA_IN:
|
||
|
/* Issue an IN token */
|
||
|
USBH_CtlReceiveData(pdev,
|
||
|
phost->Control.buff,
|
||
|
phost->Control.length,
|
||
|
phost->Control.hc_num_in);
|
||
|
|
||
|
phost->Control.state = CTRL_DATA_IN_WAIT;
|
||
|
break;
|
||
|
|
||
|
case CTRL_DATA_IN_WAIT:
|
||
|
|
||
|
URB_Status = HCD_GetURB_State(pdev , phost->Control.hc_num_in);
|
||
|
|
||
|
/* check is DATA packet transfered successfully */
|
||
|
if (URB_Status == URB_DONE)
|
||
|
{
|
||
|
phost->Control.state = CTRL_STATUS_OUT;
|
||
|
}
|
||
|
|
||
|
/* manage error cases*/
|
||
|
if (URB_Status == URB_STALL)
|
||
|
{
|
||
|
/* In stall case, return to previous machine state*/
|
||
|
phost->gState = phost->gStateBkp;
|
||
|
}
|
||
|
else if (URB_Status == URB_ERROR)
|
||
|
{
|
||
|
/* Device error */
|
||
|
phost->Control.state = CTRL_ERROR;
|
||
|
}
|
||
|
else if ((HCD_GetCurrentFrame(pdev)- phost->Control.timer) > timeout)
|
||
|
{
|
||
|
/* timeout for IN transfer */
|
||
|
phost->Control.state = CTRL_ERROR;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case CTRL_DATA_OUT:
|
||
|
/* Start DATA out transfer (only one DATA packet)*/
|
||
|
pdev->host.hc[phost->Control.hc_num_out].toggle_out = 1;
|
||
|
|
||
|
USBH_CtlSendData (pdev,
|
||
|
phost->Control.buff,
|
||
|
phost->Control.length ,
|
||
|
phost->Control.hc_num_out);
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
phost->Control.state = CTRL_DATA_OUT_WAIT;
|
||
|
break;
|
||
|
|
||
|
case CTRL_DATA_OUT_WAIT:
|
||
|
|
||
|
URB_Status = HCD_GetURB_State(pdev , phost->Control.hc_num_out);
|
||
|
if (URB_Status == URB_DONE)
|
||
|
{ /* If the Setup Pkt is sent successful, then change the state */
|
||
|
phost->Control.state = CTRL_STATUS_IN;
|
||
|
}
|
||
|
|
||
|
/* handle error cases */
|
||
|
else if (URB_Status == URB_STALL)
|
||
|
{
|
||
|
/* In stall case, return to previous machine state*/
|
||
|
phost->gState = phost->gStateBkp;
|
||
|
phost->Control.state = CTRL_STALLED;
|
||
|
}
|
||
|
else if (URB_Status == URB_NOTREADY)
|
||
|
{
|
||
|
/* Nack received from device */
|
||
|
phost->Control.state = CTRL_DATA_OUT;
|
||
|
}
|
||
|
else if (URB_Status == URB_ERROR)
|
||
|
{
|
||
|
/* device error */
|
||
|
phost->Control.state = CTRL_ERROR;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
|
||
|
case CTRL_STATUS_IN:
|
||
|
/* Send 0 bytes out packet */
|
||
|
USBH_CtlReceiveData (pdev,
|
||
|
0,
|
||
|
0,
|
||
|
phost->Control.hc_num_in);
|
||
|
|
||
|
phost->Control.state = CTRL_STATUS_IN_WAIT;
|
||
|
|
||
|
break;
|
||
|
|
||
|
case CTRL_STATUS_IN_WAIT:
|
||
|
|
||
|
URB_Status = HCD_GetURB_State(pdev , phost->Control.hc_num_in);
|
||
|
|
||
|
if ( URB_Status == URB_DONE)
|
||
|
{ /* Control transfers completed, Exit the State Machine */
|
||
|
phost->gState = phost->gStateBkp;
|
||
|
phost->Control.state = CTRL_COMPLETE;
|
||
|
}
|
||
|
|
||
|
else if (URB_Status == URB_ERROR)
|
||
|
{
|
||
|
phost->Control.state = CTRL_ERROR;
|
||
|
}
|
||
|
|
||
|
else if((HCD_GetCurrentFrame(pdev)\
|
||
|
- phost->Control.timer) > timeout)
|
||
|
{
|
||
|
phost->Control.state = CTRL_ERROR;
|
||
|
}
|
||
|
else if(URB_Status == URB_STALL)
|
||
|
{
|
||
|
/* Control transfers completed, Exit the State Machine */
|
||
|
phost->gState = phost->gStateBkp;
|
||
|
phost->Control.status = CTRL_STALL;
|
||
|
status = USBH_NOT_SUPPORTED;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case CTRL_STATUS_OUT:
|
||
|
pdev->host.hc[phost->Control.hc_num_out].toggle_out ^= 1;
|
||
|
USBH_CtlSendData (pdev,
|
||
|
0,
|
||
|
0,
|
||
|
phost->Control.hc_num_out);
|
||
|
|
||
|
phost->Control.state = CTRL_STATUS_OUT_WAIT;
|
||
|
break;
|
||
|
|
||
|
case CTRL_STATUS_OUT_WAIT:
|
||
|
|
||
|
URB_Status = HCD_GetURB_State(pdev , phost->Control.hc_num_out);
|
||
|
if (URB_Status == URB_DONE)
|
||
|
{
|
||
|
phost->gState = phost->gStateBkp;
|
||
|
phost->Control.state = CTRL_COMPLETE;
|
||
|
}
|
||
|
else if (URB_Status == URB_NOTREADY)
|
||
|
{
|
||
|
phost->Control.state = CTRL_STATUS_OUT;
|
||
|
}
|
||
|
else if (URB_Status == URB_ERROR)
|
||
|
{
|
||
|
phost->Control.state = CTRL_ERROR;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case CTRL_ERROR:
|
||
|
/*
|
||
|
After a halt condition is encountered or an error is detected by the
|
||
|
host, a control endpoint is allowed to recover by accepting the next Setup
|
||
|
PID; i.e., recovery actions via some other pipe are not required for control
|
||
|
endpoints. For the Default Control Pipe, a device reset will ultimately be
|
||
|
required to clear the halt or error condition if the next Setup PID is not
|
||
|
accepted.
|
||
|
*/
|
||
|
if (++ phost->Control.errorcount <= USBH_MAX_ERROR_COUNT)
|
||
|
{
|
||
|
/* Do the transmission again, starting from SETUP Packet */
|
||
|
phost->Control.state = CTRL_SETUP;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
phost->Control.status = CTRL_FAIL;
|
||
|
phost->gState = phost->gStateBkp;
|
||
|
|
||
|
status = USBH_FAIL;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
return status;
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* @}
|
||
|
*/
|
||
|
|
||
|
/**
|
||
|
* @}
|
||
|
*/
|
||
|
|
||
|
/**
|
||
|
* @}
|
||
|
*/
|
||
|
|
||
|
/**
|
||
|
* @}
|
||
|
*/
|
||
|
|
||
|
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
|
||
|
|
||
|
|
||
|
|
||
|
|