blob: 90ed5c72c19155894c03b579efce04a17b1e9b7f [file] [log] [blame]
/**
******************************************************************************
* @file stm32g0xx_hal_hcd.c
* @author MCD Application Team
* @brief HCD HAL module driver.
* This file provides firmware functions to manage the following
* functionalities of the USB Peripheral Controller:
* + Initialization and de-initialization functions
* + IO operation functions
* + Peripheral Control functions
* + Peripheral State functions
*
******************************************************************************
* @attention
*
* Copyright (c) 2018 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
@verbatim
==============================================================================
##### How to use this driver #####
==============================================================================
[..]
(#)Declare a HCD_HandleTypeDef handle structure, for example:
HCD_HandleTypeDef hhcd;
(#)Fill parameters of Init structure in HCD handle
(#)Call HAL_HCD_Init() API to initialize the HCD peripheral (Core, Host core, ...)
(#)Initialize the HCD low level resources through the HAL_HCD_MspInit() API:
(##) Enable the HCD/USB Low Level interface clock using the following macros
(+++) __HAL_RCC_USB_CLK_ENABLE();
(##) Initialize the related GPIO clocks
(##) Configure HCD pin-out
(##) Configure HCD NVIC interrupt
(#)Associate the Upper USB Host stack to the HAL HCD Driver:
(##) hhcd.pData = phost;
(#)Enable HCD transmission and reception:
(##) HAL_HCD_Start();
@endverbatim
******************************************************************************
*/
/* Includes ------------------------------------------------------------------*/
#include "stm32g0xx_hal.h"
/** @addtogroup STM32G0xx_HAL_Driver
* @{
*/
#ifdef HAL_HCD_MODULE_ENABLED
#if defined (USB_DRD_FS)
/** @defgroup HCD HCD
* @brief HCD HAL module driver
* @{
*/
/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
/* Private function ----------------------------------------------------------*/
/** @defgroup HCD_Private_Functions HCD Private Functions
* @{
*/
static void HCD_HC_IN_IRQHandler(HCD_HandleTypeDef *hhcd, uint8_t chnum);
static void HCD_HC_OUT_IRQHandler(HCD_HandleTypeDef *hhcd, uint8_t chnum);
static void HCD_Port_IRQHandler(HCD_HandleTypeDef *hhcd);
static void HAL_HCD_ClearPhyChannel(HCD_HandleTypeDef *hhcd);
static uint8_t HAL_HCD_GetLogical_Channel(HCD_HandleTypeDef *hhcd, uint8_t phy_chnum, uint8_t dir);
static uint8_t HAL_HCD_Check_usedChannel(HCD_HandleTypeDef *hhcd, uint8_t ch_num);
static uint8_t HAL_HCD_Get_FreePhyChannel(HCD_HandleTypeDef *hhcd, uint8_t ch_num, uint8_t epnum, uint8_t ep_type);
#if (USE_USB_DOUBLE_BUFFER == 1U)
static void HCD_HC_IN_BulkDb(HCD_HandleTypeDef *hhcd, uint8_t ch_num, uint8_t phy_chnum, uint32_t regvalue);
static void HCD_HC_OUT_BulkDb(HCD_HandleTypeDef *hhcd, uint8_t ch_num, uint8_t phy_chnum, uint32_t regvalue);
#endif /* (USE_USB_DOUBLE_BUFFER == 1U) */
static uint16_t HAL_HCD_GetFreePMA(HCD_HandleTypeDef *hhcd, uint16_t mps);
static HAL_StatusTypeDef HAL_HCD_PMAFree(HCD_HandleTypeDef *hhcd, uint32_t pma_base, uint16_t mps);
static void inline HCD_HC_IN_ISO(HCD_HandleTypeDef *hhcd, uint8_t ch_num, uint8_t phy_chnum, uint32_t regvalue);
/**
* @}
*/
/* Exported functions --------------------------------------------------------*/
/** @defgroup HCD_Exported_Functions HCD Exported Functions
* @{
*/
/** @defgroup HCD_Exported_Functions_Group1 Initialization and de-initialization functions
* @brief Initialization and Configuration functions
*
@verbatim
===============================================================================
##### Initialization and de-initialization functions #####
===============================================================================
[..] This section provides functions allowing to:
@endverbatim
* @{
*/
/**
* @brief Initialize the host driver.
* @param hhcd HCD handle
* @retval HAL status
*/
HAL_StatusTypeDef HAL_HCD_Init(HCD_HandleTypeDef *hhcd)
{
/* Check the HCD handle allocation */
if (hhcd == NULL)
{
return HAL_ERROR;
}
/* Check the parameters */
assert_param(IS_HCD_ALL_INSTANCE(hhcd->Instance));
if (hhcd->State == HAL_HCD_STATE_RESET)
{
/* Allocate lock resource and initialize it */
hhcd->Lock = HAL_UNLOCKED;
#if (USE_HAL_HCD_REGISTER_CALLBACKS == 1U)
hhcd->SOFCallback = HAL_HCD_SOF_Callback;
hhcd->ConnectCallback = HAL_HCD_Connect_Callback;
hhcd->DisconnectCallback = HAL_HCD_Disconnect_Callback;
hhcd->PortEnabledCallback = HAL_HCD_PortEnabled_Callback;
hhcd->PortDisabledCallback = HAL_HCD_PortDisabled_Callback;
hhcd->HC_NotifyURBChangeCallback = HAL_HCD_HC_NotifyURBChange_Callback;
if (hhcd->MspInitCallback == NULL)
{
hhcd->MspInitCallback = HAL_HCD_MspInit;
}
/* Init the low level hardware */
hhcd->MspInitCallback(hhcd);
#else
/* Init the low level hardware : GPIO, CLOCK, NVIC... */
HAL_HCD_MspInit(hhcd);
#endif /* (USE_HAL_HCD_REGISTER_CALLBACKS) */
}
hhcd->State = HAL_HCD_STATE_BUSY;
/* Disable the Interrupts */
(void)__HAL_HCD_DISABLE(hhcd);
/* Dma not supported, force to zero */
hhcd->Init.dma_enable = 0U;
/* Init the Core (common init.) */
(void)USB_CoreInit(hhcd->Instance, hhcd->Init);
/* Force Host Mode */
(void)USB_SetCurrentMode(hhcd->Instance, USB_HOST_MODE);
/* Init Host */
(void)USB_HostInit(hhcd->Instance, hhcd->Init);
/* Deactivate the power down */
hhcd->Instance->CNTR &= ~USB_CNTR_PDWN;
hhcd->State = HAL_HCD_STATE_READY;
/* Host Port State */
hhcd->HostState = HCD_HCD_STATE_DISCONNECTED;
/* Init PMA Address */
(void)HAL_HCD_PMAReset(hhcd);
hhcd->State = HAL_HCD_STATE_READY;
return HAL_OK;
}
/**
* @brief Initialize a host channel.
* @param hhcd HCD handle
* @param ch_num Channel number.
* This parameter can be a value from 1 to 15
* @param epnum Endpoint number.
* This parameter can be a value from 1 to 15
* @param dev_address Current device address
* This parameter can be a value from 0 to 255
* @param speed Current device speed.
* This parameter can be one of these values:
* HCD_DEVICE_SPEED_HIGH High speed mode,
* HCD_DEVICE_SPEED_FULL Full speed mode,
* HCD_DEVICE_SPEED_LOW Low speed mode
* @param ep_type Endpoint Type.
* This parameter can be one of these values:
* USBH_EP_CONTROL Control type,
* USBH_EP_ISO Isochronous type,
* USBH_EP_BULK Bulk type,
* USBH_EP_INTERRUPT Interrupt type
* @param mps Max Packet Size.
* This parameter can be a value from 0 to32K
* @retval HAL status
*/
HAL_StatusTypeDef HAL_HCD_HC_Init(HCD_HandleTypeDef *hhcd, uint8_t ch_num,
uint8_t epnum, uint8_t dev_address,
uint8_t speed, uint8_t ep_type, uint16_t mps)
{
HAL_StatusTypeDef status;
uint8_t used_channel;
uint8_t ep0_virtual_channel;
__HAL_LOCK(hhcd);
/* Check if the logical channel are already allocated */
used_channel = HAL_HCD_Check_usedChannel(hhcd, ch_num);
/* Check if the channel is not already opened */
if (used_channel == 0U)
{
/* Allocate New Physical channel */
hhcd->hc[ch_num & 0xFU].phy_ch_num = HAL_HCD_Get_FreePhyChannel(hhcd, ch_num, epnum, ep_type);
/* No free Channel available, return error */
if (hhcd->hc[ch_num & 0xFU].phy_ch_num == HCD_FREE_CH_NOT_FOUND)
{
return HAL_ERROR;
}
}
/* Channel already opened */
else
{
/* Get Physical Channel number */
hhcd->hc[ch_num & 0xFU].phy_ch_num = (used_channel & 0xF0U) >> 4U;
}
if ((epnum & 0x80U) != 0U)
{
hhcd->hc[ch_num & 0xFU].ch_dir = CH_IN_DIR;
}
else
{
hhcd->hc[ch_num & 0xFU].ch_dir = CH_OUT_DIR;
}
hhcd->hc[ch_num & 0xFU].dev_addr = dev_address;
hhcd->hc[ch_num & 0xFU].max_packet = mps;
hhcd->hc[ch_num & 0xFU].ep_type = ep_type;
hhcd->hc[ch_num & 0xFU].ep_num = epnum & 0x7FU;
hhcd->hc[ch_num & 0xFU].speed = speed;
/* Check if the channel is not already opened */
if (used_channel == 0U)
{
if (((ep_type == EP_TYPE_ISOC) && (hhcd->Init.iso_singlebuffer_enable == 0U)) ||
((ep_type == EP_TYPE_BULK) && (hhcd->Init.bulk_doublebuffer_enable == 1U)))
{
/* PMA Dynamic Allocation */
status = HAL_HCD_PMAlloc(hhcd, ch_num, HCD_DBL_BUF, mps);
if (status == HAL_ERROR)
{
return HAL_ERROR;
}
/* Clear Channel DTOG_TX */
HCD_CLEAR_TX_DTOG(hhcd->Instance, hhcd->hc[ch_num & 0xFU].phy_ch_num);
/* Clear Channel DTOG RX */
HCD_CLEAR_RX_DTOG(hhcd->Instance, hhcd->hc[ch_num & 0xFU].phy_ch_num);
}
else
{
if (hhcd->hc[ch_num & 0xFU].ep_num != 0U)
{
status = HAL_HCD_PMAlloc(hhcd, ch_num, HCD_SNG_BUF, mps);
if (status == HAL_ERROR)
{
return HAL_ERROR;
}
}
else
{
if (ch_num == 0U)
{
ep0_virtual_channel = (uint8_t)(hhcd->ep0_PmaAllocState & 0xFU);
if ((ep0_virtual_channel != 0U) && (((hhcd->ep0_PmaAllocState & 0xF0U) >> 4) == CH_IN_DIR))
{
if (hhcd->hc[ch_num & 0xFU].ch_dir == CH_OUT_DIR)
{
status = HAL_HCD_PMAlloc(hhcd, ch_num, HCD_SNG_BUF, 64U);
if (status == HAL_ERROR)
{
return HAL_ERROR;
}
}
else
{
return HAL_ERROR;
}
}
else
{
/* PMA Dynamic Allocation for EP0 OUT direction */
hhcd->hc[ch_num & 0xFU].ch_dir = CH_OUT_DIR;
status = HAL_HCD_PMAlloc(hhcd, ch_num, HCD_SNG_BUF, 64U);
if (status == HAL_ERROR)
{
return HAL_ERROR;
}
/* PMA Dynamic Allocation for EP0 IN direction */
hhcd->hc[ch_num & 0xFU].ch_dir = CH_IN_DIR;
status = HAL_HCD_PMAlloc(hhcd, ch_num, HCD_SNG_BUF, 64U);
if (status == HAL_ERROR)
{
return HAL_ERROR;
}
}
}
else
{
if (((hhcd->ep0_PmaAllocState & 0xF00U) >> 8) == 1U)
{
ep0_virtual_channel = (uint8_t)(hhcd->ep0_PmaAllocState & 0xFU);
if (((hhcd->ep0_PmaAllocState & 0xF0U) >> 4) == CH_IN_DIR)
{
hhcd->hc[ch_num & 0xFU].pmaaddr1 = hhcd->hc[ep0_virtual_channel & 0xFU].pmaaddr1;
}
else
{
hhcd->hc[ch_num & 0xFU].pmaaddr0 = hhcd->hc[ep0_virtual_channel & 0xFU].pmaaddr0;
}
}
else
{
status = HAL_HCD_PMAlloc(hhcd, ch_num, HCD_SNG_BUF, 64U);
if (status == HAL_ERROR)
{
return HAL_ERROR;
}
}
}
}
}
}
if ((epnum & 0x80U) != 0U)
{
hhcd->hc[ch_num & 0xFU].ch_dir = CH_IN_DIR;
if (hhcd->hc[ch_num & 0xFU].ep_num == 0U)
{
hhcd->hc[ch_num & 0xFU].pmaadress = hhcd->hc[ch_num & 0xFU].pmaaddr1;
}
}
else
{
hhcd->hc[ch_num & 0xFU].ch_dir = CH_OUT_DIR;
if (hhcd->hc[ch_num & 0xFU].ep_num == 0U)
{
hhcd->hc[ch_num & 0xFU].pmaadress = hhcd->hc[ch_num & 0xFU].pmaaddr0;
}
}
/* Init the USB Channel CHEPRx */
status = USB_HC_Init(hhcd->Instance, hhcd->hc[ch_num & 0xFU].phy_ch_num,
epnum, dev_address, speed, ep_type, mps);
/* check single buffer for isochronous channel */
if (ep_type == EP_TYPE_ISOC)
{
if (hhcd->Init.iso_singlebuffer_enable == 1U)
{
(void)USB_HC_DoubleBuffer(hhcd->Instance, hhcd->hc[ch_num & 0xFU].phy_ch_num,
USB_DRD_ISOC_DBUFF_DISABLE);
}
}
/* Bulk double buffer check */
if (ep_type == EP_TYPE_BULK)
{
if (hhcd->Init.bulk_doublebuffer_enable == 1U)
{
(void)USB_HC_DoubleBuffer(hhcd->Instance, hhcd->hc[ch_num & 0xFU].phy_ch_num,
USB_DRD_BULK_DBUFF_ENBALE);
}
}
__HAL_UNLOCK(hhcd);
return status;
}
/**
* @brief HAL_HCD_HC_Close Pipe
* @param hhcd HCD handle
* @param ch_num Channel number.
* This parameter can be a value from 1 to 15
* @retval HAL status
*/
HAL_StatusTypeDef HAL_HCD_HC_Close(HCD_HandleTypeDef *hhcd, uint8_t ch_num)
{
/* Stop the channel */
(void) HAL_HCD_HC_Halt(hhcd, ch_num);
HAL_Delay(3U);
if (hhcd->hc[ch_num & 0xFU].ch_dir == CH_IN_DIR)
{
/* Free Allocated Channel */
hhcd->phy_chin_state[hhcd->hc[ch_num & 0xFU].phy_ch_num] = 0U;
}
else
{
/* Free Allocated Channel */
hhcd->phy_chout_state[hhcd->hc[ch_num & 0xFU].phy_ch_num] = 0U;
}
/* Reset PMA Channel_Allocation */
(void)HAL_HCD_PMADeAlloc(hhcd, ch_num);
return HAL_OK;
}
/**
* @brief Halt a host channel.
* @param hhcd HCD handle
* @param ch_num Channel number.
* This parameter can be a value from 1 to 15
* @retval HAL status
*/
HAL_StatusTypeDef HAL_HCD_HC_Halt(HCD_HandleTypeDef *hhcd, uint8_t ch_num)
{
HAL_StatusTypeDef status = HAL_OK;
__HAL_LOCK(hhcd);
if (hhcd->hc[ch_num & 0xFU].ch_dir == CH_IN_DIR)
{
(void)USB_HC_IN_Halt(hhcd->Instance, (uint8_t) hhcd->hc[ch_num & 0xFU].phy_ch_num);
}
else
{
(void)USB_HC_OUT_Halt(hhcd->Instance, (uint8_t) hhcd->hc[ch_num & 0xFU].phy_ch_num);
}
__HAL_UNLOCK(hhcd);
return status;
}
/**
* @brief DeInitialize the host driver.
* @param hhcd HCD handle
* @retval HAL status
*/
HAL_StatusTypeDef HAL_HCD_DeInit(HCD_HandleTypeDef *hhcd)
{
uint8_t idx;
/* Check the HCD handle allocation */
if (hhcd == NULL)
{
return HAL_ERROR;
}
/* Host Port State */
hhcd->HostState = HCD_HCD_STATE_DISCONNECTED;
/* Reset PMA Address */
(void)HAL_HCD_PMAReset(hhcd);
for (idx = 0U; idx < hhcd->Init.Host_channels; idx++)
{
hhcd->phy_chin_state[idx] = 0U;
hhcd->phy_chout_state[idx] = 0U;
}
/* reset Ep0 Pma allocation state */
hhcd->ep0_PmaAllocState = 0U;
hhcd->State = HAL_HCD_STATE_BUSY;
#if (USE_HAL_HCD_REGISTER_CALLBACKS == 1U)
if (hhcd->MspDeInitCallback == NULL)
{
hhcd->MspDeInitCallback = HAL_HCD_MspDeInit; /* Legacy weak MspDeInit */
}
/* DeInit the low level hardware */
hhcd->MspDeInitCallback(hhcd);
#else
/* DeInit the low level hardware: CLOCK, NVIC. */
HAL_HCD_MspDeInit(hhcd);
#endif /* USE_HAL_HCD_REGISTER_CALLBACKS */
hhcd->State = HAL_HCD_STATE_RESET;
return HAL_OK;
}
/**
* @brief Initialize the HCD MSP.
* @param hhcd HCD handle
* @retval None
*/
__weak void HAL_HCD_MspInit(HCD_HandleTypeDef *hhcd)
{
/* Prevent unused argument(s) compilation warning */
UNUSED(hhcd);
/* NOTE : This function Should not be modified, when the callback is needed,
the HAL_HCD_MspInit could be implemented in the user file
*/
}
/**
* @brief DeInitialize the HCD MSP.
* @param hhcd HCD handle
* @retval None
*/
__weak void HAL_HCD_MspDeInit(HCD_HandleTypeDef *hhcd)
{
/* Prevent unused argument(s) compilation warning */
UNUSED(hhcd);
/* NOTE : This function Should not be modified, when the callback is needed,
the HAL_HCD_MspDeInit could be implemented in the user file
*/
}
__weak void HAL_HCD_SuspendCallback(HCD_HandleTypeDef *hhcd)
{
/* Prevent unused argument(s) compilation warning */
UNUSED(hhcd);
/* NOTE : This function Should not be modified, when the callback is needed,
the HAL_HCD_SuspendCallback could be implemented in the user file
*/
}
__weak void HAL_HCD_ResumeCallback(HCD_HandleTypeDef *hhcd)
{
/* Prevent unused argument(s) compilation warning */
UNUSED(hhcd);
/* NOTE : This function Should not be modified, when the callback is needed,
the HAL_HCD_ResumeCallback could be implemented in the user file
*/
}
/**
* @}
*/
/** @defgroup HCD_Exported_Functions_Group2 Input and Output operation functions
* @brief HCD IO operation functions
*
@verbatim
===============================================================================
##### IO operation functions #####
===============================================================================
[..] This subsection provides a set of functions allowing to manage the USB Host Data
Transfer
@endverbatim
* @{
*/
/**
* @brief Submit a new URB for processing.
* @param hhcd HCD handle
* @param ch_num Channel number.
* This parameter can be a value from 1 to 15
* @param direction Channel number.
* This parameter can be one of these values:
* 0 : Output / 1 : Input
* @param ep_type Endpoint Type.
* This parameter can be one of these values:
* USBH_EP_CONTROL : Control type/
* USBH_EP_ISO : Isochronous type/
* USBH_EP_BULK : Bulk type/
* USBH_EP_INTERRUPT : Interrupt type/
* @param token Endpoint Type.
* This parameter can be one of these values:
* 0: HC_PID_SETUP / 1: HC_PID_DATA1
* @param pbuff pointer to URB data
* @param length Length of URB data
* @param do_ping activate do ping protocol (for high speed only).
* This parameter can be one of these values:
* 0 : do ping inactive / 1 : do ping active
* @retval HAL status
*/
HAL_StatusTypeDef HAL_HCD_HC_SubmitRequest(HCD_HandleTypeDef *hhcd, uint8_t ch_num,
uint8_t direction, uint8_t ep_type,
uint8_t token, uint8_t *pbuff,
uint16_t length, uint8_t do_ping)
{
UNUSED(do_ping);
if (token == 0U)
{
hhcd->hc[ch_num & 0xFU].data_pid = HC_PID_SETUP;
}
else
{
hhcd->hc[ch_num & 0xFU].data_pid = HC_PID_DATA1;
}
/* Manage Data Toggle */
switch (ep_type)
{
case EP_TYPE_CTRL:
if ((token == 1U) && (direction == 0U)) /* send data */
{
if (length == 0U)
{
/* For Status OUT stage, Length==0, Status Out PID = 1 */
hhcd->hc[ch_num & 0xFU].toggle_out = 1U;
}
/* Set the Data Toggle bit as per the Flag */
if (hhcd->hc[ch_num & 0xFU].toggle_out == 0U)
{
/* Put the PID 0 */
hhcd->hc[ch_num & 0xFU].data_pid = HC_PID_DATA0;
}
else
{
/* Put the PID 1 */
hhcd->hc[ch_num & 0xFU].data_pid = HC_PID_DATA1;
}
}
break;
case EP_TYPE_BULK:
if (direction == 0U)
{
/* Set the Data Toggle bit as per the Flag */
if (hhcd->hc[ch_num & 0xFU].toggle_out == 0U)
{
/* Put the PID 0 */
hhcd->hc[ch_num & 0xFU].data_pid = HC_PID_DATA0;
}
else
{
/* Put the PID 1 */
hhcd->hc[ch_num & 0xFU].data_pid = HC_PID_DATA1;
}
}
else
{
if (hhcd->hc[ch_num & 0xFU].toggle_in == 0U)
{
hhcd->hc[ch_num & 0xFU].data_pid = HC_PID_DATA0;
}
else
{
hhcd->hc[ch_num & 0xFU].data_pid = HC_PID_DATA1;
}
}
break;
case EP_TYPE_INTR:
if (direction == 0U)
{
/* Set the Data Toggle bit as per the Flag */
if (hhcd->hc[ch_num & 0xFU].toggle_out == 0U)
{
/* Put the PID 0 */
hhcd->hc[ch_num & 0xFU].data_pid = HC_PID_DATA0;
}
else
{
/* Put the PID 1 */
hhcd->hc[ch_num & 0xFU].data_pid = HC_PID_DATA1;
}
}
else
{
if (hhcd->hc[ch_num & 0xFU].toggle_in == 0U)
{
hhcd->hc[ch_num & 0xFU].data_pid = HC_PID_DATA0;
}
else
{
hhcd->hc[ch_num & 0xFU].data_pid = HC_PID_DATA1;
}
}
break;
case EP_TYPE_ISOC:
hhcd->hc[ch_num & 0xFU].data_pid = HC_PID_DATA0;
break;
default:
break;
}
hhcd->hc[ch_num & 0xFU].xfer_buff = pbuff;
hhcd->hc[ch_num & 0xFU].xfer_len = length;
hhcd->hc[ch_num & 0xFU].xfer_len_db = length;
hhcd->hc[ch_num & 0xFU].urb_state = URB_IDLE;
hhcd->hc[ch_num & 0xFU].xfer_count = 0U;
hhcd->hc[ch_num & 0xFU].state = HC_IDLE;
return USB_HC_StartXfer(hhcd->Instance, &hhcd->hc[ch_num & 0xFU]);
}
/**
* @brief Handle HCD interrupt request.
* @param hhcd HCD handle
* @retval None
*/
void HAL_HCD_IRQHandler(HCD_HandleTypeDef *hhcd)
{
uint8_t phy_chnum;
uint8_t chnum;
uint32_t epch_reg;
uint32_t wIstr = USB_ReadInterrupts(hhcd->Instance);
/* check if this is an USB pending IT */
if ((SYSCFG->IT_LINE_SR[8] & (0x1U << 2)) == 0U)
{
return;
}
/* Port Change Detected (Connection/Disconnection) */
if ((wIstr & USB_ISTR_DCON) == USB_ISTR_DCON)
{
/* Clear Flag */
__HAL_HCD_CLEAR_FLAG(hhcd, USB_ISTR_DCON);
/* Call Port IRQHandler */
HCD_Port_IRQHandler(hhcd);
return;
}
/* Correct Transaction Detected -------*/
if ((wIstr & USB_ISTR_CTR) == USB_ISTR_CTR)
{
/* Handle Host channel Interrupt */
for (phy_chnum = 0U; phy_chnum < hhcd->Init.Host_channels; phy_chnum++)
{
if ((HCD_GET_CHANNEL(hhcd->Instance, phy_chnum) & USB_CH_VTRX) != 0U)
{
/* Get Logical channel to check if the channel is already opened */
chnum = HAL_HCD_GetLogical_Channel(hhcd, phy_chnum, 1U);
if (chnum != HCD_LOGICAL_CH_NOT_OPENED)
{
/* Call Channel_IN_IRQ() */
HCD_HC_IN_IRQHandler(hhcd, chnum);
}
else
{
/*Channel was not closed correctly still have interrupt */
epch_reg = HCD_GET_CHANNEL(hhcd->Instance, phy_chnum);
epch_reg = (epch_reg & (USB_CHEP_REG_MASK & (~USB_CH_ERRRX) & (~USB_CH_VTRX))) |
(USB_CH_VTTX | USB_CH_ERRTX);
HCD_SET_CHANNEL(hhcd->Instance, phy_chnum, epch_reg);
}
}
if ((HCD_GET_CHANNEL(hhcd->Instance, phy_chnum) & USB_CH_VTTX) != 0U)
{
/* Get Logical channel to check if the channel is already opened */
chnum = HAL_HCD_GetLogical_Channel(hhcd, phy_chnum, 0U);
if (chnum != HCD_LOGICAL_CH_NOT_OPENED)
{
/*Call Channel_OUT_IRQ()*/
HCD_HC_OUT_IRQHandler(hhcd, chnum);
}
else
{
/* Clear Error & unwanted VTTX or Channel was not closed correctly */
epch_reg = HCD_GET_CHANNEL(hhcd->Instance, phy_chnum);
epch_reg = (epch_reg & (USB_CHEP_REG_MASK & (~USB_CH_ERRTX) & (~USB_CH_VTTX))) |
(USB_CH_VTRX | USB_CH_ERRRX);
HCD_SET_CHANNEL(hhcd->Instance, phy_chnum, epch_reg);
}
}
}
return;
}
/* Wakeup Flag Detected */
if ((wIstr & USB_ISTR_WKUP) == USB_ISTR_WKUP)
{
if (hhcd->HostState == HCD_HCD_STATE_SUSPEND)
{
/* Set The L2Resume bit */
hhcd->Instance->CNTR |= USB_CNTR_L2RES;
/* Clear the wake-up flag */
__HAL_HCD_CLEAR_FLAG(hhcd, USB_ISTR_WKUP);
/* Update the USB Software state machine */
HAL_HCD_ResumeCallback(hhcd);
hhcd->HostState = HCD_HCD_STATE_RESUME;
}
else
{
/* Clear the wake-up flag */
__HAL_HCD_CLEAR_FLAG(hhcd, USB_ISTR_WKUP);
}
return;
}
/* Global Error Flag Detected */
if ((wIstr & USB_ISTR_ERR) == USB_ISTR_ERR)
{
__HAL_HCD_CLEAR_FLAG(hhcd, USB_ISTR_ERR);
return;
}
/* PMA Overrun detected */
if ((wIstr & USB_ISTR_PMAOVR) == USB_ISTR_PMAOVR)
{
__HAL_HCD_CLEAR_FLAG(hhcd, USB_ISTR_PMAOVR);
return;
}
/* Suspend Detected */
if ((wIstr & USB_ISTR_SUSP) == USB_ISTR_SUSP)
{
/* Set HAL State to Suspend */
hhcd->HostState = HCD_HCD_STATE_SUSPEND;
/* Force low-power mode in the macrocell */
hhcd->Instance->CNTR |= USB_CNTR_SUSPEN;
/* clear of the ISTR bit must be done after setting of CNTR_FSUSP */
__HAL_HCD_CLEAR_FLAG(hhcd, USB_ISTR_SUSP);
/* Call suspend Callback */
HAL_HCD_SuspendCallback(hhcd);
return;
}
/* Start Of Frame Detected */
if ((wIstr & USB_ISTR_SOF) == USB_ISTR_SOF)
{
#if (USE_HAL_HCD_REGISTER_CALLBACKS == 1U)
hhcd->SOFCallback(hhcd);
#else
HAL_HCD_SOF_Callback(hhcd);
#endif /* USE_HAL_HCD_REGISTER_CALLBACKS */
__HAL_HCD_CLEAR_FLAG(hhcd, USB_ISTR_SOF);
/* when first SOF is detected after USB_RESET is asserted */
if (hhcd->HostState == HCD_HCD_STATE_RESETED)
{
/* HAL State */
hhcd->HostState = HCD_HCD_STATE_RUN;
#if (USE_HAL_HCD_REGISTER_CALLBACKS == 1U)
hhcd->PortEnabledCallback(hhcd);
#else
HAL_HCD_PortEnabled_Callback(hhcd);
#endif /* USE_HAL_HCD_REGISTER_CALLBACKS */
}
return;
}
}
/**
* @brief SOF callback.
* @param hhcd HCD handle
* @retval None
*/
__weak void HAL_HCD_SOF_Callback(HCD_HandleTypeDef *hhcd)
{
/* Prevent unused argument(s) compilation warning */
UNUSED(hhcd);
/* NOTE : This function Should not be modified, when the callback is needed,
the HAL_HCD_SOF_Callback could be implemented in the user file
*/
}
/**
* @brief Connection Event callback.
* @param hhcd HCD handle
* @retval None
*/
__weak void HAL_HCD_Connect_Callback(HCD_HandleTypeDef *hhcd)
{
/* Prevent unused argument(s) compilation warning */
UNUSED(hhcd);
/* NOTE : This function Should not be modified, when the callback is needed,
the HAL_HCD_Connect_Callback could be implemented in the user file
*/
}
/**
* @brief Disconnection Event callback.
* @param hhcd HCD handle
* @retval None
*/
__weak void HAL_HCD_Disconnect_Callback(HCD_HandleTypeDef *hhcd)
{
/* Prevent unused argument(s) compilation warning */
UNUSED(hhcd);
/* NOTE : This function Should not be modified, when the callback is needed,
the HAL_HCD_Disconnect_Callback could be implemented in the user file
*/
}
/**
* @brief Port Enabled Event callback.
* @param hhcd HCD handle
* @retval None
*/
__weak void HAL_HCD_PortEnabled_Callback(HCD_HandleTypeDef *hhcd)
{
/* Prevent unused argument(s) compilation warning */
UNUSED(hhcd);
/* NOTE : This function should not be modified, when the callback is needed,
the HAL_HCD_Disconnect_Callback could be implemented in the user file
*/
}
/**
* @brief Port Disabled Event callback.
* @param hhcd HCD handle
* @retval None
*/
__weak void HAL_HCD_PortDisabled_Callback(HCD_HandleTypeDef *hhcd)
{
/* Prevent unused argument(s) compilation warning */
UNUSED(hhcd);
/* NOTE : This function should not be modified, when the callback is needed,
the HAL_HCD_Disconnect_Callback could be implemented in the user file
*/
}
/**
* @brief Notify URB state change callback.
* @param hhcd HCD handle
* @param chnum Channel number.
* This parameter can be a value from 1 to 15
* @param urb_state
* This parameter can be one of these values:
* URB_IDLE/
* URB_DONE/
* URB_NOTREADY/
* URB_NYET/
* URB_ERROR/
* URB_STALL/
* @retval None
*/
__weak void HAL_HCD_HC_NotifyURBChange_Callback(HCD_HandleTypeDef *hhcd,
uint8_t chnum, HCD_URBStateTypeDef urb_state)
{
/* Prevent unused argument(s) compilation warning */
UNUSED(hhcd);
UNUSED(chnum);
UNUSED(urb_state);
/* NOTE : This function Should not be modified, when the callback is needed,
the HAL_HCD_HC_NotifyURBChange_Callback could be implemented in the user file
*/
}
#if (USE_HAL_HCD_REGISTER_CALLBACKS == 1U)
/**
* @brief Register a User USB HCD Callback
* To be used instead of the weak predefined callback
* @param hhcd USB HCD handle
* @param CallbackID ID of the callback to be registered
* This parameter can be one of the following values:
* @arg @ref HAL_HCD_SOF_CB_ID USB HCD SOF callback ID
* @arg @ref HAL_HCD_CONNECT_CB_ID USB HCD Connect callback ID
* @arg @ref HAL_HCD_DISCONNECT_CB_ID HCD Disconnect callback ID
* @arg @ref HAL_HCD_PORT_ENABLED_CB_ID USB HCD Port Enable callback ID
* @arg @ref HAL_HCD_PORT_DISABLED_CB_ID USB HCD Port Disable callback ID
* @arg @ref HAL_HCD_MSPINIT_CB_ID MspDeInit callback ID
* @arg @ref HAL_HCD_MSPDEINIT_CB_ID MspDeInit callback ID
* @param pCallback pointer to the Callback function
* @retval HAL status
*/
HAL_StatusTypeDef HAL_HCD_RegisterCallback(HCD_HandleTypeDef *hhcd,
HAL_HCD_CallbackIDTypeDef CallbackID,
pHCD_CallbackTypeDef pCallback)
{
HAL_StatusTypeDef status = HAL_OK;
if (pCallback == NULL)
{
/* Update the error code */
hhcd->ErrorCode |= HAL_HCD_ERROR_INVALID_CALLBACK;
return HAL_ERROR;
}
/* Process locked */
__HAL_LOCK(hhcd);
if (hhcd->State == HAL_HCD_STATE_READY)
{
switch (CallbackID)
{
case HAL_HCD_SOF_CB_ID :
hhcd->SOFCallback = pCallback;
break;
case HAL_HCD_CONNECT_CB_ID :
hhcd->ConnectCallback = pCallback;
break;
case HAL_HCD_DISCONNECT_CB_ID :
hhcd->DisconnectCallback = pCallback;
break;
case HAL_HCD_PORT_ENABLED_CB_ID :
hhcd->PortEnabledCallback = pCallback;
break;
case HAL_HCD_PORT_DISABLED_CB_ID :
hhcd->PortDisabledCallback = pCallback;
break;
case HAL_HCD_MSPINIT_CB_ID :
hhcd->MspInitCallback = pCallback;
break;
case HAL_HCD_MSPDEINIT_CB_ID :
hhcd->MspDeInitCallback = pCallback;
break;
default :
/* Update the error code */
hhcd->ErrorCode |= HAL_HCD_ERROR_INVALID_CALLBACK;
/* Return error status */
status = HAL_ERROR;
break;
}
}
else if (hhcd->State == HAL_HCD_STATE_RESET)
{
switch (CallbackID)
{
case HAL_HCD_MSPINIT_CB_ID :
hhcd->MspInitCallback = pCallback;
break;
case HAL_HCD_MSPDEINIT_CB_ID :
hhcd->MspDeInitCallback = pCallback;
break;
default :
/* Update the error code */
hhcd->ErrorCode |= HAL_HCD_ERROR_INVALID_CALLBACK;
/* Return error status */
status = HAL_ERROR;
break;
}
}
else
{
/* Update the error code */
hhcd->ErrorCode |= HAL_HCD_ERROR_INVALID_CALLBACK;
/* Return error status */
status = HAL_ERROR;
}
/* Release Lock */
__HAL_UNLOCK(hhcd);
return status;
}
/**
* @brief Unregister an USB HCD Callback
* USB HCD callback is redirected to the weak predefined callback
* @param hhcd USB HCD handle
* @param CallbackID ID of the callback to be unregistered
* This parameter can be one of the following values:
* @arg @ref HAL_HCD_SOF_CB_ID USB HCD SOF callback ID
* @arg @ref HAL_HCD_CONNECT_CB_ID USB HCD Connect callback ID
* @arg @ref HAL_HCD_DISCONNECT_CB_ID DRD HCD Disconnect callback ID
* @arg @ref HAL_HCD_PORT_ENABLED_CB_ID USB HCD Port Enabled callback ID
* @arg @ref HAL_HCD_PORT_DISABLED_CB_ID USB HCD Port Disabled callback ID
* @arg @ref HAL_HCD_MSPINIT_CB_ID MspDeInit callback ID
* @arg @ref HAL_HCD_MSPDEINIT_CB_ID MspDeInit callback ID
* @retval HAL status
*/
HAL_StatusTypeDef HAL_HCD_UnRegisterCallback(HCD_HandleTypeDef *hhcd,
HAL_HCD_CallbackIDTypeDef CallbackID)
{
HAL_StatusTypeDef status = HAL_OK;
/* Process locked */
__HAL_LOCK(hhcd);
/* Setup Legacy weak Callbacks */
if (hhcd->State == HAL_HCD_STATE_READY)
{
switch (CallbackID)
{
case HAL_HCD_SOF_CB_ID :
hhcd->SOFCallback = HAL_HCD_SOF_Callback;
break;
case HAL_HCD_CONNECT_CB_ID :
hhcd->ConnectCallback = HAL_HCD_Connect_Callback;
break;
case HAL_HCD_DISCONNECT_CB_ID :
hhcd->DisconnectCallback = HAL_HCD_Disconnect_Callback;
break;
case HAL_HCD_PORT_ENABLED_CB_ID :
hhcd->PortEnabledCallback = HAL_HCD_PortEnabled_Callback;
break;
case HAL_HCD_PORT_DISABLED_CB_ID :
hhcd->PortDisabledCallback = HAL_HCD_PortDisabled_Callback;
break;
case HAL_HCD_MSPINIT_CB_ID :
hhcd->MspInitCallback = HAL_HCD_MspInit;
break;
case HAL_HCD_MSPDEINIT_CB_ID :
hhcd->MspDeInitCallback = HAL_HCD_MspDeInit;
break;
default :
/* Update the error code */
hhcd->ErrorCode |= HAL_HCD_ERROR_INVALID_CALLBACK;
/* Return error status */
status = HAL_ERROR;
break;
}
}
else if (hhcd->State == HAL_HCD_STATE_RESET)
{
switch (CallbackID)
{
case HAL_HCD_MSPINIT_CB_ID :
hhcd->MspInitCallback = HAL_HCD_MspInit;
break;
case HAL_HCD_MSPDEINIT_CB_ID :
hhcd->MspDeInitCallback = HAL_HCD_MspDeInit;
break;
default :
/* Update the error code */
hhcd->ErrorCode |= HAL_HCD_ERROR_INVALID_CALLBACK;
/* Return error status */
status = HAL_ERROR;
break;
}
}
else
{
/* Update the error code */
hhcd->ErrorCode |= HAL_HCD_ERROR_INVALID_CALLBACK;
/* Return error status */
status = HAL_ERROR;
}
/* Release Lock */
__HAL_UNLOCK(hhcd);
return status;
}
/**
* @brief Register USB HCD Host Channel Notify URB Change Callback
* To be used instead of the weak HAL_HCD_HC_NotifyURBChange_Callback() predefined callback
* @param hhcd HCD handle
* @param pCallback pointer to the USB HCD Host Channel Notify URB Change Callback function
* @retval HAL status
*/
HAL_StatusTypeDef HAL_HCD_RegisterHC_NotifyURBChangeCallback(HCD_HandleTypeDef *hhcd,
pHCD_HC_NotifyURBChangeCallbackTypeDef pCallback)
{
HAL_StatusTypeDef status = HAL_OK;
if (pCallback == NULL)
{
/* Update the error code */
hhcd->ErrorCode |= HAL_HCD_ERROR_INVALID_CALLBACK;
return HAL_ERROR;
}
/* Process locked */
__HAL_LOCK(hhcd);
if (hhcd->State == HAL_HCD_STATE_READY)
{
hhcd->HC_NotifyURBChangeCallback = pCallback;
}
else
{
/* Update the error code */
hhcd->ErrorCode |= HAL_HCD_ERROR_INVALID_CALLBACK;
/* Return error status */
status = HAL_ERROR;
}
/* Release Lock */
__HAL_UNLOCK(hhcd);
return status;
}
/**
* @brief Unregister the USB HCD Host Channel Notify URB Change Callback
* USB HCD Host Channel Notify URB Change Callback is redirected
* to the weak HAL_HCD_HC_NotifyURBChange_Callback() predefined callback
* @param hhcd HCD handle
* @retval HAL status
*/
HAL_StatusTypeDef HAL_HCD_UnRegisterHC_NotifyURBChangeCallback(HCD_HandleTypeDef *hhcd)
{
HAL_StatusTypeDef status = HAL_OK;
/* Process locked */
__HAL_LOCK(hhcd);
if (hhcd->State == HAL_HCD_STATE_READY)
{
hhcd->HC_NotifyURBChangeCallback = HAL_HCD_HC_NotifyURBChange_Callback; /* Legacy weak DataOutStageCallback */
}
else
{
/* Update the error code */
hhcd->ErrorCode |= HAL_HCD_ERROR_INVALID_CALLBACK;
/* Return error status */
status = HAL_ERROR;
}
/* Release Lock */
__HAL_UNLOCK(hhcd);
return status;
}
#endif /* USE_HAL_HCD_REGISTER_CALLBACKS */
/**
* @}
*/
/** @defgroup HCD_Exported_Functions_Group3 Peripheral Control functions
* @brief Management functions
*
@verbatim
===============================================================================
##### Peripheral Control functions #####
===============================================================================
[..]
This subsection provides a set of functions allowing to control the HCD data
transfers.
@endverbatim
* @{
*/
/**
* @brief Start the host driver.
* @param hhcd HCD handle
* @retval HAL status
*/
HAL_StatusTypeDef HAL_HCD_Start(HCD_HandleTypeDef *hhcd)
{
__HAL_LOCK(hhcd);
/*Set the PullDown on the PHY */
hhcd->Instance->BCDR |= USB_BCDR_DPPD;
/* Clear Reset */
hhcd->Instance->CNTR &= ~USB_CNTR_USBRST;
/*Remove PowerDown */
hhcd->Instance->CNTR &= ~USB_CNTR_PDWN;
__HAL_UNLOCK(hhcd);
return HAL_OK;
}
/**
* @brief Stop the host driver.
* @param hhcd HCD handle
* @retval HAL status
*/
HAL_StatusTypeDef HAL_HCD_Stop(HCD_HandleTypeDef *hhcd)
{
__HAL_LOCK(hhcd);
/*Stop the Host IP: setting powerdown */
(void)USB_StopHost(hhcd->Instance);
/* clear all allocated virtual channel */
HAL_HCD_ClearPhyChannel(hhcd);
/* Reset the PMA current pointer */
(void)HAL_HCD_PMAReset(hhcd);
/* reset Ep0 Pma allocation state */
hhcd->ep0_PmaAllocState = 0U;
__HAL_UNLOCK(hhcd);
return HAL_OK;
}
/**
* @brief Put the Device in suspend mode
* @param hhcd HCD handle
* @retval HAL status
*/
HAL_StatusTypeDef HAL_HCD_Suspend(HCD_HandleTypeDef *hhcd)
{
__IO uint32_t count = 0U;
/* Set Suspend Mode */
hhcd->Instance->CNTR |= USB_CNTR_SUSPEN;
/* wait for Suspend Ready */
while ((hhcd->Instance->CNTR & USB_CNTR_SUSPRDY) == 0U)
{
if (++count > 0xFFFFFFU)
{
return HAL_TIMEOUT;
}
}
return HAL_OK;
}
/**
* @brief Resume host port
* @param hhcd HCD handle
* @retval HAL status
*/
HAL_StatusTypeDef HAL_HCD_Resume(HCD_HandleTypeDef *hhcd)
{
/* Set Resume bit */
hhcd->Instance->CNTR |= USB_CNTR_L2RES;
return HAL_OK;
}
/**
* @brief Reset the host port.
* @param hhcd HCD handle
* @retval HAL status
*/
HAL_StatusTypeDef HAL_HCD_ResetPort(HCD_HandleTypeDef *hhcd)
{
__HAL_LOCK(hhcd);
/* Reset the USB Port by inserting an SE0 on the bus */
(void)USB_ResetPort(hhcd->Instance);
if (hhcd->HostState == HCD_HCD_STATE_CONNECTED)
{
hhcd->HostState = HCD_HCD_STATE_RESETED;
}
__HAL_UNLOCK(hhcd);
return HAL_OK;
}
/**
* @brief Resme the host port.
* @param hhcd HCD handle
* @retval HAL status
*/
HAL_StatusTypeDef HAL_HCD_ResumePort(HCD_HandleTypeDef *hhcd)
{
/* Set Resume bit */
hhcd->Instance->CNTR |= USB_CNTR_L2RES;
HAL_Delay(30U);
/* Clear Resume bit */
hhcd->Instance->CNTR &= ~USB_CNTR_L2RES;
return HAL_OK;
}
/**
* @}
*/
/** @defgroup HCD_Exported_Functions_Group4 Peripheral State functions
* @brief Peripheral State functions
*
@verbatim
===============================================================================
##### Peripheral State functions #####
===============================================================================
[..]
This subsection permits to get in run-time the status of the peripheral
and the data flow.
@endverbatim
* @{
*/
/**
* @brief Return the HCD handle state.
* @param hhcd HCD handle
* @retval HAL state
*/
HCD_StateTypeDef HAL_HCD_GetState(HCD_HandleTypeDef *hhcd)
{
return hhcd->State;
}
/**
* @brief Return URB state for a channel.
* @param hhcd HCD handle
* @param chnum Channel number.
* This parameter can be a value from 1 to 15
* @retval URB state.
* This parameter can be one of these values:
* URB_IDLE/
* URB_DONE/
* URB_NOTREADY/
* URB_NYET/
* URB_ERROR/
* URB_STALL
*/
HCD_URBStateTypeDef HAL_HCD_HC_GetURBState(HCD_HandleTypeDef *hhcd, uint8_t chnum)
{
return hhcd->hc[chnum].urb_state;
}
/**
* @brief Return the last host transfer size.
* @param hhcd HCD handle
* @param chnum Channel number.
* This parameter can be a value from 1 to 15
* @retval last transfer size in byte
*/
uint32_t HAL_HCD_HC_GetXferCount(HCD_HandleTypeDef *hhcd, uint8_t chnum)
{
return hhcd->hc[chnum].xfer_count;
}
/**
* @brief Return the Host Channel state.
* @param hhcd HCD handle
* @param chnum Channel number.
* This parameter can be a value from 1 to 15
* @retval Host channel state
* This parameter can be one of these values:
* HC_IDLE/
* HC_XFRC/
* HC_HALTED/
* HC_NYET/
* HC_NAK/
* HC_STALL/
* HC_XACTERR/
* HC_BBLERR/
* HC_DATATGLERR
*/
HCD_HCStateTypeDef HAL_HCD_HC_GetState(HCD_HandleTypeDef *hhcd, uint8_t chnum)
{
return hhcd->hc[chnum].state;
}
/**
* @brief Return the current Host frame number.
* @param hhcd HCD handle
* @retval Current Host frame number
*/
uint32_t HAL_HCD_GetCurrentFrame(HCD_HandleTypeDef *hhcd)
{
return (USB_GetCurrentFrame(hhcd->Instance));
}
/**
* @brief Return the Host enumeration speed.
* @param hhcd HCD handle
* @retval speed : Device speed after Host enumeration
* This parameter can be one of these values:
* @arg HCD_DEVICE_SPEED_FULL: Full speed mode
* @arg HCD_DEVICE_SPEED_LOW: Low speed mode
*/
uint32_t HAL_HCD_GetCurrentSpeed(HCD_HandleTypeDef *hhcd)
{
return (USB_GetHostSpeed(hhcd->Instance));
}
#if (USE_USB_DOUBLE_BUFFER == 1U)
/**
* @brief Handle Host Channel OUT Double Buffer Bulk requests.
* @param hhcd HCD handle
* @param ch_num Channel number This parameter can be a value from 1 to 15
* @param phy_chnum Physical Channel number [0..7]
* @param regvalue contain Snapshot of the EPCHn register when ISR is detected
* @retval none
*/
static void HCD_HC_OUT_BulkDb(HCD_HandleTypeDef *hhcd, uint8_t ch_num,
uint8_t phy_chnum, uint32_t regvalue)
{
uint16_t data_xfr;
uint16_t len;
/* Send Buffer0 */
if ((regvalue & USB_CH_DTOG_TX) != 0U)
{
data_xfr = (uint16_t)(((USB_DRD_PMA_BUFF + phy_chnum)->TXBD & 0x03FF0000U) >> 16U);
if (hhcd->hc[ch_num & 0xFU].xfer_len >= data_xfr)
{
hhcd->hc[ch_num & 0xFU].xfer_len -= data_xfr;
}
else
{
hhcd->hc[ch_num & 0xFU].xfer_len = 0U;
}
/* Transfer no yet finished only one packet of mps is transferred and ACKed from device */
if (hhcd->hc[ch_num & 0xFU].xfer_len != 0U)
{
/* manage multiple Xfer */
hhcd->hc[ch_num & 0xFU].xfer_count += data_xfr;
/* check if we need to free user buffer */
if ((regvalue & USB_CH_DTOG_RX) != 0U)
{
/* Toggle SwBuff */
HCD_CLEAR_TX_DTOG(hhcd->Instance, phy_chnum);
HCD_CLEAR_RX_DTOG(hhcd->Instance, phy_chnum);
HCD_TX_DTOG(hhcd->Instance, phy_chnum);
}
/* hhcd->hc[ch_num&0xFU].xfer_len_db==0 ==> when all data are written in the PMA to yet transferred */
if (hhcd->hc[ch_num & 0xFU].xfer_len_db > 0U) /* Still data to fill in the buffer */
{
hhcd->hc[ch_num & 0xFU].xfer_buff += data_xfr;
/* calculate len of new buffer to fill */
if (hhcd->hc[ch_num & 0xFU].xfer_len_db > hhcd->hc[ch_num & 0xFU].max_packet)
{
len = (uint16_t)hhcd->hc[ch_num & 0xFU].max_packet;
hhcd->hc[ch_num & 0xFU].xfer_len_db -= len;
}
else
{
len = (uint16_t)hhcd->hc[ch_num & 0xFU].xfer_len_db;
hhcd->hc[ch_num & 0xFU].xfer_len_db = 0U; /* end of fill buffer */
}
/* Write remaining data to Buffer0 */
HCD_SET_CH_DBUF0_CNT(hhcd->Instance, phy_chnum, 1U, (uint16_t)len);
USB_WritePMA(hhcd->Instance, hhcd->hc[ch_num & 0xFU].xfer_buff,
hhcd->hc[ch_num & 0xFU].pmaaddr0, (uint16_t)len);
}
/* start a new transfer */
HCD_SET_CH_TX_STATUS(hhcd->Instance, phy_chnum, USB_CH_TX_VALID);
}
else
{
/* Transfer complete state */
hhcd->hc[ch_num & 0xFU].xfer_count += data_xfr;
hhcd->hc[ch_num & 0xFU].state = HC_XFRC;
hhcd->hc[ch_num & 0xFU].urb_state = URB_DONE;
hhcd->hc[ch_num & 0xFU].toggle_out ^= 1U;
/* Close the Channel */
HCD_SET_CH_TX_STATUS(hhcd->Instance, phy_chnum, USB_CH_TX_DIS);
}
}
else
{
/* Send Buffer1 */
data_xfr = (uint16_t)(((USB_DRD_PMA_BUFF + phy_chnum)->RXBD & 0x03FF0000U) >> 16U);
if (hhcd->hc[ch_num & 0xFU].xfer_len >= data_xfr) /* updated */
{
hhcd->hc[ch_num & 0xFU].xfer_len -= data_xfr;
}
/* Transfer no yet finished only one packet of mps is transferred and ACKed from device */
if (hhcd->hc[ch_num & 0xFU].xfer_len != 0U)
{
/* manage multiple Xfer */
hhcd->hc[ch_num & 0xFU].xfer_count += data_xfr;
/* check if we need to free user buffer */
if ((regvalue & USB_CH_DTOG_RX) == 0U)
{
/* Toggle SwBuff */
HCD_CLEAR_TX_DTOG(hhcd->Instance, phy_chnum);
HCD_CLEAR_RX_DTOG(hhcd->Instance, phy_chnum);
HCD_RX_DTOG(hhcd->Instance, phy_chnum);
}
/* hhcd->hc[ch_num&0xFU].xfer_len_db==0 ==> when all data are written in the PMA to yet transferred */
if (hhcd->hc[ch_num & 0xFU].xfer_len_db > 0U) /* Still data to fill in the buffer */
{
hhcd->hc[ch_num & 0xFU].xfer_buff += data_xfr;
/* calculate len of new buffer to fill */
if (hhcd->hc[ch_num & 0xFU].xfer_len_db > hhcd->hc[ch_num & 0xFU].max_packet)
{
len = hhcd->hc[ch_num & 0xFU].max_packet;
hhcd->hc[ch_num & 0xFU].xfer_len_db -= len;
}
else
{
len = (uint16_t)hhcd->hc[ch_num & 0xFU].xfer_len_db;
hhcd->hc[ch_num & 0xFU].xfer_len_db = 0U; /* end of fill buffer */
}
/* Write remaining data to Buffer0 */
HCD_SET_CH_DBUF1_CNT(hhcd->Instance, phy_chnum, 1U, (uint16_t)len);
USB_WritePMA(hhcd->Instance, hhcd->hc[ch_num & 0xFU].xfer_buff,
hhcd->hc[ch_num & 0xFU].pmaaddr1, (uint16_t)len);
}
/* start a new transfer */
HCD_SET_CH_TX_STATUS(hhcd->Instance, phy_chnum, USB_CH_TX_VALID);
}
else
{
/* Transfer complete state */
hhcd->hc[ch_num & 0xFU].xfer_count += data_xfr;
hhcd->hc[ch_num & 0xFU].state = HC_XFRC;
hhcd->hc[ch_num & 0xFU].urb_state = URB_DONE;
hhcd->hc[ch_num & 0xFU].toggle_out ^= 1U;
/* Close the channel */
HCD_SET_CH_TX_STATUS(hhcd->Instance, phy_chnum, USB_CH_TX_DIS);
}
}
}
/**
* @brief Handle Host Channel IN Double Buffer Bulk requests.
* @param hhcd HCD handle
* @param ch_num Channel number: This parameter can be a value from 1 to 15
* @param phy_chnum Physical Channel number [0..7]
* @param regvalue contain Snapshot of the EPCHn register when ISR is detected
* @retval none
*/
static void HCD_HC_IN_BulkDb(HCD_HandleTypeDef *hhcd,
uint8_t ch_num, uint8_t phy_chnum, uint32_t regvalue)
{
uint16_t received_bytes;
/* Read from Buffer 0 */
if ((regvalue & USB_CH_DTOG_RX) != 0U)
{
received_bytes = (uint16_t)HCD_GET_CH_DBUF0_CNT(hhcd->Instance, phy_chnum);
if (hhcd->hc[ch_num & 0xFU].xfer_len <= received_bytes)
{
hhcd->hc[ch_num & 0xFU].xfer_len = 0U;
}
else
{
hhcd->hc[ch_num & 0xFU].xfer_len -= received_bytes;
}
/* Check if we Need to free the other buffer for the IP */
if ((hhcd->hc[ch_num & 0xFU].xfer_len != 0U) && ((regvalue & USB_CH_DTOG_TX) != 0U))
{
/* Toggle SwBuff to Allow the IP to submit a new IN */
HCD_FREE_USER_BUFFER(hhcd->Instance, phy_chnum, 0U);
}
/* Read the byte from PMA to user Buffer(System Memory) */
USB_ReadPMA(hhcd->Instance, hhcd->hc[ch_num & 0xFU].xfer_buff,
hhcd->hc[ch_num & 0xFU].pmaaddr0, (uint16_t)received_bytes);
}
else
{
/* Read from Buffer 1 */
received_bytes = (uint16_t) HCD_GET_CH_DBUF1_CNT(hhcd->Instance, phy_chnum);
if (hhcd->hc[ch_num & 0xFU].xfer_len <= received_bytes)
{
hhcd->hc[ch_num & 0xFU].xfer_len = 0U;
}
else
{
hhcd->hc[ch_num & 0xFU].xfer_len -= received_bytes;
}
/* Check if we Need to free the other buffer for the IP */
if ((hhcd->hc[ch_num & 0xFU].xfer_len != 0U) && ((regvalue & USB_CH_DTOG_TX) == 0U))
{
/* Toggle SwBuff */
HCD_FREE_USER_BUFFER(hhcd->Instance, phy_chnum, 0U);
}
/* Read the byte from PMA to user Buffer(System Memory) */
USB_ReadPMA(hhcd->Instance, hhcd->hc[ch_num & 0xFU].xfer_buff,
hhcd->hc[ch_num & 0xFU].pmaaddr1, (uint16_t)received_bytes);
}
/* update the global number of all received bytes */
hhcd->hc[ch_num & 0xFU].xfer_count += received_bytes;
/* Transfer complete state */
hhcd->hc[ch_num & 0xFU].state = HC_ACK;
hhcd->hc[ch_num & 0xFU].ErrCnt = 0U;
if ((hhcd->hc[ch_num & 0xFU].xfer_len == 0U) ||
((received_bytes < hhcd->hc[ch_num & 0xFU].max_packet)))
{
hhcd->hc[ch_num & 0xFU].urb_state = URB_DONE;
hhcd->hc[ch_num & 0xFU].state = HC_XFRC;
/* disable channel */
HCD_SET_CH_RX_STATUS(hhcd->Instance, phy_chnum, USB_CH_RX_DIS);
}
else
{
hhcd->hc[ch_num & 0xFU].xfer_buff += received_bytes;
/* Reactivate the Channel Submit an other URB since the Transfer is not yet completed */
HCD_SET_CH_RX_STATUS(hhcd->Instance, phy_chnum, USB_CH_RX_STRX);
}
}
#endif /* (USE_USB_DOUBLE_BUFFER == 1U) */
/**
* @brief Handle Host Channel IN Isochronous Transaction
* @param hhcd HCD handle
* @param ch_num Channel number: This parameter can be a value from 1 to 15
* @param phy_chnum Physical Channel number [0..7]
* @param regvalue contain Snapshot of the EPCHn register when ISR is detected
* @retval none
*/
static void inline HCD_HC_IN_ISO(HCD_HandleTypeDef *hhcd, uint8_t ch_num,
uint8_t phy_chnum, uint32_t regvalue)
{
/* Check if Double buffer isochronous */
if ((regvalue & USB_CH_KIND) != 0U)
{
/* Get Data IN Packet */
hhcd->hc[ch_num & 0xFU].xfer_count = HCD_GET_CH_RX_CNT(hhcd->Instance, phy_chnum);
if (hhcd->hc[ch_num & 0xFU].xfer_count != 0U)
{
USB_ReadPMA(hhcd->Instance, hhcd->hc[ch_num & 0xFU].xfer_buff,
hhcd->hc[ch_num & 0xFU].pmaadress,
(uint16_t)hhcd->hc[ch_num & 0xFU].xfer_count);
hhcd->hc[ch_num & 0xFU].urb_state = URB_DONE;
}
}
#if (USE_USB_DOUBLE_BUFFER == 1U)
else /* double buffer isochronous */
{
/* Read from Buffer0 */
if ((regvalue & USB_CH_DTOG_RX) != 0U)
{
/* Get number of Received byte in buffer0 */
hhcd->hc[ch_num & 0xFU].xfer_count = HCD_GET_CH_DBUF0_CNT(hhcd->Instance, phy_chnum);
if (hhcd->hc[ch_num & 0xFU].xfer_count != 0U)
{
/* Read from Buffer0 */
USB_ReadPMA(hhcd->Instance, hhcd->hc[ch_num & 0xFU].xfer_buff,
hhcd->hc[ch_num & 0xFU].pmaaddr0,
(uint16_t)hhcd->hc[ch_num & 0xFU].xfer_count);
hhcd->hc[ch_num & 0xFU].urb_state = URB_DONE;
}
}
else
{
/* Get number of Received byte in buffer1 */
hhcd->hc[ch_num & 0xFU].xfer_count = HCD_GET_CH_DBUF1_CNT(hhcd->Instance, phy_chnum);
if (hhcd->hc[ch_num & 0xFU].xfer_count != 0U)
{
/* Read from Buffer1 */
USB_ReadPMA(hhcd->Instance, hhcd->hc[ch_num & 0xFU].xfer_buff,
hhcd->hc[ch_num & 0xFU].pmaaddr1,
(uint16_t)hhcd->hc[ch_num & 0xFU].xfer_count);
hhcd->hc[ch_num & 0xFU].urb_state = URB_DONE;
}
}
}
#endif /* (USE_USB_DOUBLE_BUFFER == 1U) */
/* Transfer complete state */
hhcd->hc[ch_num & 0xFU].state = HC_XFRC;
/* Clear VTRX */
HCD_CLEAR_RX_CH_CTR(hhcd->Instance, phy_chnum);
}
/**
* @brief Handle Host Channel IN interrupt requests.
* @param hhcd HCD handle
* @param ch_num Channel number
* This parameter can be a value from 1 to 15
* @retval none
*/
static void HCD_HC_IN_IRQHandler(HCD_HandleTypeDef *hhcd, uint8_t ch_num)
{
uint16_t received_bytes;
uint8_t phy_chnum = (uint8_t)__HAL_HCD_GET_CHNUM(hhcd);
/*Take a Flag snapshot from the CHEP register, due to STRX bits are used for both control and status */
uint32_t ch_reg = HCD_GET_CHANNEL(hhcd->Instance, phy_chnum);
/* Manage Correct Transaction */
if ((ch_reg & USB_CH_ERRRX) == 0U)
{
/* Isochronous Channel */
if ((ch_reg & USB_CH_UTYPE) == USB_EP_ISOCHRONOUS)
{
HCD_HC_IN_ISO(hhcd, ch_num, phy_chnum, ch_reg);
}
else
{
/* manage ACK response single buffer */
if (((ch_reg) & USB_CH_RX_STRX) == USB_CH_RX_ACK_SBUF)
{
/* Get Control Data OUT Packet */
received_bytes = (uint16_t)HCD_GET_CH_RX_CNT(hhcd->Instance, phy_chnum);
/* Read the byte from PMA to user Buffer(System Memory) */
USB_ReadPMA(hhcd->Instance, hhcd->hc[ch_num & 0xFU].xfer_buff,
hhcd->hc[ch_num & 0xFU].pmaadress, (uint16_t)received_bytes);
/* update the global number of all received bytes */
hhcd->hc[ch_num & 0xFU].xfer_count += received_bytes;
/* Transfer complete state */
hhcd->hc[ch_num & 0xFU].state = HC_ACK;
hhcd->hc[ch_num & 0xFU].ErrCnt = 0U;
if (hhcd->hc[ch_num & 0xFU].xfer_len <= received_bytes)
{
hhcd->hc[ch_num & 0xFU].xfer_len = 0U;
}
else
{
hhcd->hc[ch_num & 0xFU].xfer_len -= received_bytes;
}
if ((hhcd->hc[ch_num & 0xFU].xfer_len == 0U) ||
((received_bytes < hhcd->hc[ch_num & 0xFU].max_packet)))
{
hhcd->hc[ch_num & 0xFU].urb_state = URB_DONE;
hhcd->hc[ch_num & 0xFU].state = HC_XFRC;
}
else
{
hhcd->hc[ch_num & 0xFU].xfer_buff += received_bytes;
/* Reactivate the Channel to Submit another URB since the Transfer is not yet completed */
HCD_SET_CH_RX_STATUS(hhcd->Instance, phy_chnum, USB_CH_RX_STRX);
}
if ((hhcd->hc[ch_num & 0xFU].ep_type == EP_TYPE_BULK) ||
(hhcd->hc[ch_num & 0xFU].ep_type == EP_TYPE_INTR))
{
hhcd->hc[ch_num & 0xFU].toggle_out ^= 1U;
}
}
/* manage NACK Response */
else if (((ch_reg & USB_CH_RX_STRX) == USB_CH_RX_NAK)
&& (hhcd->hc[ch_num & 0xFU].urb_state != URB_DONE))
{
hhcd->hc[ch_num & 0xFU].urb_state = URB_NOTREADY;
hhcd->hc[ch_num & 0xFU].ErrCnt = 0U;
hhcd->hc[ch_num & 0xFU].state = HC_NAK;
}
/* manage STALL Response */
else if ((ch_reg & USB_CH_RX_STRX) == USB_CH_RX_STALL)
{
(void)HAL_HCD_HC_Halt(hhcd, (uint8_t)ch_num);
hhcd->hc[ch_num & 0xFU].state = HC_STALL;
hhcd->hc[ch_num & 0xFU].urb_state = URB_STALL;
/* Close the channel */
HCD_SET_CH_RX_STATUS(hhcd->Instance, phy_chnum, USB_CH_RX_DIS);
}
#if (USE_USB_DOUBLE_BUFFER == 1U)
/* Double Buffer Management in case of Bulk Transaction */
else if (((ch_reg & USB_CH_RX_STRX) == USB_CH_RX_ACK_DBUF)
&& ((ch_reg & USB_CH_KIND) != 0U))
{
/* Bulk IN Double Buffer ISR */
HCD_HC_IN_BulkDb(hhcd, ch_num, phy_chnum, ch_reg);
}
#endif /* (USE_USB_DOUBLE_BUFFER == 1U) */
else
{
/*....*/
/* not defined state: STRX=11 in single buffer no iso is not defined */
}
#if (USE_HAL_HCD_REGISTER_CALLBACKS == 1U)
hhcd->HC_NotifyURBChangeCallback(hhcd, (uint8_t)ch_num, hhcd->hc[ch_num & 0xFU].urb_state);
#else
HAL_HCD_HC_NotifyURBChange_Callback(hhcd, (uint8_t)ch_num, hhcd->hc[ch_num & 0xFU].urb_state);
#endif /* USE_HAL_HCD_REGISTER_CALLBACKS */
/*Clear VTRX */
HCD_CLEAR_RX_CH_CTR(hhcd->Instance, phy_chnum);
}
}
else /* Error detected during last transaction */
{
/* Set URB Error State */
hhcd->hc[ch_num & 0xFU].urb_state = URB_NOTREADY;
hhcd->hc[ch_num & 0xFU].ErrCnt++;
hhcd->hc[ch_num & 0xFU].state = HC_XACTERR;
/* Clear VTTRX & ERR_RX */
HCD_CLEAR_RX_CH_ERR(hhcd->Instance, phy_chnum);
/* Check Error number */
if (hhcd->hc[ch_num & 0xFU].ErrCnt > 3U)
{
hhcd->hc[ch_num & 0xFU].urb_state = URB_ERROR;
HCD_SET_CH_RX_STATUS(hhcd->Instance, phy_chnum, USB_CH_RX_DIS);
/* Clear pending err_tx */
HCD_CLEAR_RX_CH_ERR(hhcd->Instance, phy_chnum);
}
#if (USE_HAL_HCD_REGISTER_CALLBACKS == 1U)
hhcd->HC_NotifyURBChangeCallback(hhcd, (uint8_t)ch_num, hhcd->hc[ch_num & 0xFU].urb_state);
#else
HAL_HCD_HC_NotifyURBChange_Callback(hhcd, (uint8_t)ch_num, hhcd->hc[ch_num & 0xFU].urb_state);
#endif /* USE_HAL_HCD_REGISTER_CALLBACKS */
}
}
/**
* @brief Handle Host Channel OUT interrupt requests.
* @param hhcd HCD handle
* @param chnum Channel number
* This parameter can be a value from 1 to 15
* @retval none
*/
static void HCD_HC_OUT_IRQHandler(HCD_HandleTypeDef *hhcd, uint8_t chnum)
{
uint16_t data_xfr;
__IO uint32_t WregCh;
/* Get Physical Channel number */
uint32_t phy_chnum = (uint8_t)__HAL_HCD_GET_CHNUM(hhcd);
/* Take a Flag snapshot from the CHEP register, due to STRX bits are used for both control &status */
uint32_t ch_reg = *(__IO uint32_t *)(&(hhcd->Instance->CHEP0R) + phy_chnum);
/*------ Manage Correct Transaction ------*/
if ((ch_reg & USB_CH_ERRTX) == 0U)
{
/* Handle Isochronous channel */
if ((ch_reg & USB_CH_UTYPE) == USB_EP_ISOCHRONOUS)
{
/* correct transaction */
if ((hhcd->Instance->ISTR & USB_ISTR_ERR) == 0U)
{
/* Double buffer isochronous out */
if ((ch_reg & USB_CH_KIND) != 0U)
{
HCD_SET_CH_TX_CNT(hhcd->Instance, phy_chnum, 0U);
}
#if (USE_USB_DOUBLE_BUFFER == 1U)
else /* double buffer isochronous out */
{
/* Odd Transaction */
if ((ch_reg & USB_CH_DTOG_TX) != 0U)
{
HCD_SET_CH_TX_CNT(hhcd->Instance, phy_chnum, 0U);
}
/* Even Transaction */
else
{
HCD_SET_CH_RX_CNT(hhcd->Instance, phy_chnum, 0U);
}
USB_DRD_SET_CHEP_TX_STATUS(hhcd->Instance, phy_chnum, USB_CH_TX_DIS);
}
#endif /* (USE_USB_DOUBLE_BUFFER == 1U) */
/* Transfer complete state */
hhcd->hc[chnum & 0xFU].state = HC_XFRC;
hhcd->hc[chnum & 0xFU].urb_state = URB_DONE;
}
/*Clear Correct Transfer */
HCD_CLEAR_TX_CH_CTR(hhcd->Instance, phy_chnum);
/*TX COMPLETE*/
#if (USE_HAL_HCD_REGISTER_CALLBACKS == 1U)
hhcd->HC_NotifyURBChangeCallback(hhcd, (uint8_t)chnum, hhcd->hc[chnum & 0xFU].urb_state);
#else
HAL_HCD_HC_NotifyURBChange_Callback(hhcd, (uint8_t)chnum, hhcd->hc[chnum & 0xFU].urb_state);
#endif /* USE_HAL_HCD_REGISTER_CALLBACKS */
}
else /* Manage all Non Isochronous Transaction */
{
/* Check ACK response */
if ((ch_reg & USB_CH_TX_STTX) == USB_CH_TX_ACK_SBUF)
{
data_xfr = (uint16_t)(((USB_DRD_PMA_BUFF + phy_chnum)->TXBD & 0x03FF0000U) >> 16U);
if (hhcd->hc[chnum & 0xFU].xfer_len >= data_xfr)
{
hhcd->hc[chnum & 0xFU].xfer_len -= data_xfr;
}
else
{
hhcd->hc[chnum & 0xFU].xfer_len = 0U;
}
/* Transfer no yet finished only one packet of mps is transferred and ACKed from device */
if (hhcd->hc[chnum & 0xFU].xfer_len != 0U)
{
/* manage multiple Xfer */
hhcd->hc[chnum & 0xFU].xfer_buff += data_xfr;
hhcd->hc[chnum & 0xFU].xfer_count += data_xfr;
/* start a new transfer */
(void) USB_HC_StartXfer(hhcd->Instance, &hhcd->hc[chnum & 0xFU]);
}
else
{
/* Transfer complete */
hhcd->hc[chnum & 0xFU].xfer_count += data_xfr;
hhcd->hc[chnum & 0xFU].state = HC_XFRC;
hhcd->hc[chnum & 0xFU].urb_state = URB_DONE;
if ((hhcd->hc[chnum & 0xFU].ep_type == EP_TYPE_BULK) ||
(hhcd->hc[chnum & 0xFU].ep_type == EP_TYPE_INTR))
{
hhcd->hc[chnum & 0xFU].toggle_out ^= 1U;
}
}
}
/* Check NACK Response */
else if (((ch_reg & USB_CHEP_NAK) == USB_CHEP_NAK) ||
((ch_reg & USB_CH_TX_STTX) == USB_CH_TX_NAK))
{
/* Update Channel status */
hhcd->hc[chnum & 0xFU].state = HC_NAK;
hhcd->hc[chnum & 0xFU].urb_state = URB_NOTREADY;
hhcd->hc[chnum & 0xFU].ErrCnt = 0U;
/* Get Channel register value */
WregCh = *(__IO uint32_t *)(&(hhcd->Instance->CHEP0R) + phy_chnum);
/*clear NAK status*/
WregCh &= ~USB_CHEP_NAK & USB_CHEP_REG_MASK;
/* Update channel register Value */
HCD_SET_CHANNEL(hhcd->Instance, phy_chnum, WregCh);
if (hhcd->hc[chnum & 0xFU].doublebuffer == 0U)
{
#if (USE_HAL_HCD_REGISTER_CALLBACKS == 1U)
hhcd->HC_NotifyURBChangeCallback(hhcd, (uint8_t)chnum, hhcd->hc[chnum & 0xFU].urb_state);
#else
HAL_HCD_HC_NotifyURBChange_Callback(hhcd, (uint8_t)chnum, hhcd->hc[chnum & 0xFU].urb_state);
#endif /* USE_HAL_HCD_REGISTER_CALLBACKS */
}
}
/* Check STALL Response */
else if ((ch_reg & USB_CH_TX_STTX) == USB_CH_TX_STALL)
{
(void) HAL_HCD_HC_Halt(hhcd, (uint8_t)chnum);
hhcd->hc[chnum & 0xFU].state = HC_STALL;
hhcd->hc[chnum & 0xFU].urb_state = URB_STALL;
}
#if (USE_USB_DOUBLE_BUFFER == 1U)
/* Check double buffer ACK in case of bulk transaction */
else if ((ch_reg & USB_CH_TX_STTX) == USB_CH_TX_ACK_DBUF)
{
/* Double buffer management Bulk Out */
(void) HCD_HC_OUT_BulkDb(hhcd, chnum, (uint8_t)phy_chnum, ch_reg);
}
#endif /* (USE_USB_DOUBLE_BUFFER == 1U) */
else
{
/*...*/
}
if ((ch_reg & USB_CH_TX_STTX) != USB_CH_TX_NAK)
{
#if (USE_HAL_HCD_REGISTER_CALLBACKS == 1U)
hhcd->HC_NotifyURBChangeCallback(hhcd, (uint8_t)chnum, hhcd->hc[chnum & 0xFU].urb_state);
#else
HAL_HCD_HC_NotifyURBChange_Callback(hhcd, (uint8_t)chnum, hhcd->hc[chnum & 0xFU].urb_state);
#endif /* USE_HAL_HCD_REGISTER_CALLBACKS */
}
HCD_CLEAR_TX_CH_CTR(hhcd->Instance, phy_chnum);
} /* end no isochronous */
}
/*------ Manage Transaction Error------*/
else
{
hhcd->hc[chnum & 0xFU].ErrCnt++;
if (hhcd->hc[chnum & 0xFU].ErrCnt > 3U)
{
HCD_SET_CH_TX_STATUS(hhcd->Instance, phy_chnum, USB_CH_TX_DIS);
hhcd->hc[chnum & 0xFU].urb_state = URB_ERROR;
}
else
{
hhcd->hc[chnum & 0xFU].urb_state = URB_NOTREADY;
}
hhcd->hc[chnum & 0xFU].state = HC_XACTERR;
/*Clear ERR_TX*/
HCD_CLEAR_TX_CH_ERR(hhcd->Instance, phy_chnum);
#if (USE_HAL_HCD_REGISTER_CALLBACKS == 1U)
hhcd->HC_NotifyURBChangeCallback(hhcd, (uint8_t)chnum, hhcd->hc[chnum & 0xFU].urb_state);
#else
HAL_HCD_HC_NotifyURBChange_Callback(hhcd, (uint8_t)chnum, hhcd->hc[chnum & 0xFU].urb_state);
#endif /* USE_HAL_HCD_REGISTER_CALLBACKS */
}
}
/**
* @brief Handle Host Port interrupt requests.
* @param hhcd HCD handle
* @retval None
*/
static void HCD_Port_IRQHandler(HCD_HandleTypeDef *hhcd)
{
uint32_t FnrReg = hhcd->Instance->FNR;
uint32_t IstrReg = hhcd->Instance->ISTR;
/* SE0 detected USB Disconnected state */
if ((FnrReg & (USB_FNR_RXDP | USB_FNR_RXDM)) == 0U)
{
/* Host Port State */
hhcd->HostState = HCD_HCD_STATE_DISCONNECTED;
/* clear all allocated virtual channel */
HAL_HCD_ClearPhyChannel(hhcd);
/* Reset the PMA current pointer */
(void)HAL_HCD_PMAReset(hhcd);
/* reset Ep0 Pma allocation state */
hhcd->ep0_PmaAllocState = 0U;
/* Disconnection Callback */
#if (USE_HAL_HCD_REGISTER_CALLBACKS == 1U)
hhcd->DisconnectCallback(hhcd);
#else
HAL_HCD_Disconnect_Callback(hhcd);
#endif /* USE_HAL_HCD_REGISTER_CALLBACKS */
return;
}
if ((hhcd->HostState == HCD_HCD_STATE_DISCONNECTED) != 0U)
{
/* J-state or K-state detected & LastState=Disconnected */
if (((FnrReg & USB_FNR_RXDP) != 0U) || ((IstrReg & USB_ISTR_LS_DCONN) != 0U))
{
hhcd->HostState = HCD_HCD_STATE_CONNECTED;
#if (USE_HAL_HCD_REGISTER_CALLBACKS == 1U)
hhcd->ConnectCallback(hhcd);
#else
HAL_HCD_Connect_Callback(hhcd);
#endif /* USE_HAL_HCD_REGISTER_CALLBACKS */
}
}
else
{
/* J-state or K-state detected & lastState=Connected: a Missed disconnection is detected */
if (((FnrReg & USB_FNR_RXDP) != 0U) || ((IstrReg & USB_ISTR_LS_DCONN) != 0U))
{
/* Host Port State */
hhcd->HostState = HCD_HCD_STATE_DISCONNECTED;
/* clear all allocated virtual channel */
HAL_HCD_ClearPhyChannel(hhcd);
/* Reset the PMA current pointer */
(void)HAL_HCD_PMAReset(hhcd);
/* reset Ep0 PMA allocation state */
hhcd->ep0_PmaAllocState = 0U;
/* Disconnection Callback */
#if (USE_HAL_HCD_REGISTER_CALLBACKS == 1U)
hhcd->DisconnectCallback(hhcd);
#else
HAL_HCD_Disconnect_Callback(hhcd);
#endif /* USE_HAL_HCD_REGISTER_CALLBACKS */
}
}
}
/**
* @brief Check if the ch_num are already reserved to a physical channel
* @param hhcd HCD handle
* @param ch_num Channel number
* This parameter can be a value from 1 to 15
* @retval HAL status
*/
static uint8_t HAL_HCD_Check_usedChannel(HCD_HandleTypeDef *hhcd, uint8_t ch_num)
{
uint8_t idx;
/* Check if the logical channel are already opened */
for (idx = 0U; idx < hhcd->Init.Host_channels; idx++)
{
if ((((hhcd->phy_chin_state[idx] & 0xF0U) >> 4U) == ((uint16_t)ch_num + 1U)) &&
(hhcd->phy_chin_state[idx] != 0U))
{
return (1U | (idx << 4U));
}
if ((((hhcd->phy_chout_state[idx] & 0xF0U) >> 4U) == ((uint16_t)ch_num + 1U)) &&
(hhcd->phy_chout_state[idx] != 0U))
{
return (1U | (idx << 4U));
}
}
return 0U;
}
/**
* @brief Get a Logical Channel number from physical Channel
* @param hhcd HCD handle
* @param phy_chnum
* This parameter can be a value from 1 to 15
* @param dir Channel direction
* -0 OUT_Channel
* -1 IN_Channel
* @retval HAL status
*/
static uint8_t HAL_HCD_GetLogical_Channel(HCD_HandleTypeDef *hhcd,
uint8_t phy_chnum, uint8_t dir)
{
/* Out Channel Direction */
if (dir == 0U)
{
if (((hhcd->phy_chout_state[phy_chnum & 0x7U] & 0x00F0U) >> 4U) != 0U)
{
return ((uint8_t)((hhcd->phy_chout_state[phy_chnum & 0x7U] & 0x00F0U) >> 4U) - 1U);
}
else
{
/* Channel not registered Error */
return HCD_LOGICAL_CH_NOT_OPENED;
}
}
/* IN Channel Direction */
else
{
if (((hhcd->phy_chin_state[phy_chnum & 0x7U] & 0x00F0U) >> 4U) != 0U)
{
return ((uint8_t)((hhcd->phy_chin_state[phy_chnum & 0x7U] & 0x00F0U) >> 4U) - 1U);
}
else
{
/* Channel not registered Error */
return HCD_LOGICAL_CH_NOT_OPENED;
}
}
}
/**
* @brief Get a free physical Channel number according to the direction
* @param hhcd HCD handle
* @param ch_num Channel number
* This parameter can be a value from 1 to 15
* @param epnum Endpoint number
* This parameter can be a value from 1 to 15
* @param ep_type Endpoint Type
* This parameter can be one of these values:
* EP_TYPE_CTRL Control type,
* EP_TYPE_ISOC Isochronous type,
* EP_TYPE_BULK Bulk type,
* EP_TYPE_INTR Interrupt type
* @retval if physical channel is available return Phy_channel number
else return HCD_FREE_CH_NOT_FOUND
*/
static uint8_t HAL_HCD_Get_FreePhyChannel(HCD_HandleTypeDef *hhcd, uint8_t ch_num,
uint8_t epnum, uint8_t ep_type)
{
uint8_t idx;
if ((epnum & 0x7FU) == 0U)
{
idx = 0U;
if (ch_num == 0U)
{
if (hhcd->phy_chin_state[idx] == 0U)
{
/* chin_state to store the ep_type to be used for the same channel in OUT direction
* adding + 1 to ep_type avoid starting with a 0 value. ep_type take by default (0/1/2/3) */
hhcd->phy_chin_state[idx] = (((uint16_t)ch_num + 1U) << 4U) |
((uint16_t)ep_type + 1U) |
(((uint16_t)epnum & 0x0FU) << 8U);
}
if (hhcd->phy_chout_state[idx] == 0U)
{
/* chout_state will store the ep_type to be used for the same channel in IN direction
* adding + 1 to ep_type avoid starting with a 0 value. ep_type take by default (0/1/2/3) */
hhcd->phy_chout_state[idx] = (((uint16_t)ch_num + 1U) << 4U) |
((uint16_t)ep_type + 1U) |
(((uint16_t)epnum & 0x0FU) << 8U);
}
}
else
{
if ((epnum & 0x80U) != 0U)
{
if (((hhcd->phy_chin_state[idx] & 0xF0U) >> 4U) != ((uint16_t)ch_num + 1U))
{
/* chin_state to store the ep_type to be used for the same channel in OUT direction
* adding + 1 to ep_type avoid starting with a 0 value. ep_type take by default (0/1/2/3) */
hhcd->phy_chin_state[idx] = (((uint16_t)ch_num + 1U) << 4U) |
((uint16_t)ep_type + 1U) |
(((uint16_t)epnum & 0x0FU) << 8U);
}
}
else
{
if (((hhcd->phy_chout_state[idx] & 0xF0U) >> 4U) != ((uint16_t)ch_num + 1U))
{
/* chout_state will store the ep_type to be used for the same channel in IN direction
* adding + 1 to ep_type avoid starting with a 0 value. ep_type take by default (0/1/2/3) */
hhcd->phy_chout_state[idx] = (((uint16_t)ch_num + 1U) << 4U) |
((uint16_t)ep_type + 1U) |
(((uint16_t)epnum & 0x0FU) << 8U);
}
}
}
return idx;
}
if ((epnum & 0x80U) != 0U)
{
/* Find a new available physical in channel */
for (idx = 1U; idx < hhcd->Init.Host_channels; idx++)
{
/* Check if the same epnum is allocated then allocate the same physical channel OUT for IN Logical Channel */
if ((hhcd->phy_chin_state[idx] == 0U) &&
((((hhcd->phy_chout_state[idx] & 0x000FU) == ((uint16_t)ep_type + 1U)) &&
(((hhcd->phy_chout_state[idx] & 0x0F00U) == ((uint16_t)epnum & 0x0FU)))) ||
(hhcd->phy_chout_state[idx] == 0U)))
{
/* chin_state to store the ep_type to be used for the same channel in OUT direction
* adding + 1 to ep_type avoid starting with a 0 value. ep_type take by default (0/1/2/3) */
hhcd->phy_chin_state[idx] = (((uint16_t)ch_num + 1U) << 4U) |
((uint16_t)ep_type + 1U) |
(((uint16_t)epnum & 0x0FU) << 8U);
return idx;
}
}
}
else
{
/* Find a new available physical out channel */
for (idx = 1U; idx < hhcd->Init.Host_channels; idx++)
{
/* Check if the same epnum is allocated then allocate the same physical channel IN for OUT Logical Channel */
if ((hhcd->phy_chout_state[idx] == 0U) &&
((((hhcd->phy_chin_state[idx] & 0x0FU) == ((uint16_t)ep_type + 1U)) &&
((hhcd->phy_chin_state[idx] & 0x0F00U) == ((uint16_t)epnum & 0x0FU))) ||
(hhcd->phy_chin_state[idx] == 0U)))
{
/* chout_state will store the ep_type to be used for the same channel in IN direction
* adding + 1 to ep_type avoid starting with a 0 value. ep_type take by default (0/1/2/3) */
hhcd->phy_chout_state[idx] = (((uint16_t)ch_num + 1U) << 4U) |
((uint16_t)ep_type + 1U) |
(((uint16_t)epnum & 0x0FU) << 8U);
return idx;
}
}
}
/* in case of Error */
return HCD_FREE_CH_NOT_FOUND;
}
/**
* @brief Free All Channel allocation
* @param hhcd HCD handle
* @retval HAL status
*/
static void HAL_HCD_ClearPhyChannel(HCD_HandleTypeDef *hhcd)
{
uint8_t idx;
for (idx = 0U; idx < hhcd->Init.Host_channels; idx++)
{
/*Reset channel allocation value */
hhcd->phy_chout_state[idx] = 0U;
hhcd->phy_chin_state[idx] = 0U;
}
}
/*---------------------- PMA Allocation Section --------------------- */
/*
__col31________________col0__ Column-- >
lin0 | entry31.|....... | entry0 | Line
|---------|---------|--------| |
line1| entry63.|....... | entry32| |
|---------|---------|--------| \|/
| entry127|....... | entry64|
|---------|---------|--------|
| entry256|...... |entry128|
----------------------------
an allocation space of 64byte need 8 Free contiguous Entry in the Matrix
- a Free Entry is a bit with 0 Value/ a busy entry is a bit with 1 value. */
/**
* @brief Fetch in the PMA_LockupTable free space of number of mps byte
* @param hhcd Host instance
* @param mps Channel Max Packet Size
* @retval PMA_Address of the first free block containing mps byte
0xFFFF in case of no space available
*/
static uint16_t HAL_HCD_GetFreePMA(HCD_HandleTypeDef *hhcd, uint16_t mps)
{
uint32_t Entry;
uint32_t FreeBlocks = 0U;
uint8_t FirstFreeBlock_col = 0U;
uint8_t FirstFreeBlock_line = 0U;
uint8_t ColIndex;
uint16_t NbrReqBlocks;
uint16_t mps_t = mps;
/* since PMA buffer descriptor RXBD allocate address according to BLSIZE, BLSIZE=1==> mps>64
allocation in PMA is done in 32Bytes each entry */
if ((mps_t > 64U) && ((mps_t % 32U) != 0U))
{
/* Align the mps to 32byte block to match the allocation in PMA,
check Definition of allocation buffer memory in usb user spec */
mps_t = (uint16_t)(((mps_t / 32U) + 1U) * 32U);
}
/* calculate the number of block(8byte) to allocate */
NbrReqBlocks = mps_t / 8U;
/* check if we need remaining Block */
if ((mps_t % 8U) != 0U)
{
NbrReqBlocks++;
}
/* Look For NbrReqBlocks * Empty Block */
for (uint8_t i = 0U; ((i < PMA_BLOCKS) && (FreeBlocks != NbrReqBlocks)); i++)
{
Entry = hhcd->PMALookupTable[i];
/* when parse is in progress, check the first col to look for a contiguous block */
if ((FreeBlocks != 0U) && ((Entry & (uint32_t)1U) != 0U))
{
FreeBlocks = 0U;
}
uint8_t j = 0U;
while ((j <= 31U) && (FreeBlocks != NbrReqBlocks))
{
/* check if block j is free */
if ((Entry & ((uint32_t)1U << j)) == 0U)
{
if (FreeBlocks == 0U)
{
FirstFreeBlock_col = j;
FirstFreeBlock_line = i;
FreeBlocks++;
}
j++;
/* Parse Column PMALockTable */
while ((j <= 31U) && ((Entry & ((uint32_t)1U << j)) == 0U) && (FreeBlocks < NbrReqBlocks))
{
FreeBlocks++;
j++;
}
/* Free contiguous Blocks not found */
if (((FreeBlocks < NbrReqBlocks) && (j < 31U)) ||
((j == 31U) && ((Entry & ((uint32_t)1U << j)) != 0U)))
{
FreeBlocks = 0U;
}
}
j++;
} /* end for j */
} /* end for i */
/* Free block found */
if (FreeBlocks >= NbrReqBlocks)
{
ColIndex = FirstFreeBlock_col;
for (uint8_t i = FirstFreeBlock_line; ((i < PMA_BLOCKS) && (FreeBlocks > 0U)); i++)
{
for (uint8_t j = ColIndex; j <= 31U; j++)
{
hhcd->PMALookupTable[i] |= ((uint32_t)1U << j);
if (--FreeBlocks == 0U)
{
break;
}
}
ColIndex = 0U;
}
return (uint16_t)((FirstFreeBlock_line * (uint16_t)256U) + (FirstFreeBlock_col * (uint16_t)8U));
}
else
{
return 0xFFFFU;
}
}
/**
* @brief Allocate PMA buffer for Channel
* This API will fetch a free space
* @param hhcd Host instance
* @param ch_num Channel number
* @param ch_kind endpoint Kind
* USB_SNG_BUF Single Buffer used
* USB_DBL_BUF Double Buffer used
* @param mps Channel Max Packet Size
* @retval HAL status
*/
HAL_StatusTypeDef HAL_HCD_PMAlloc(HCD_HandleTypeDef *hhcd, uint8_t ch_num,
uint16_t ch_kind, uint16_t mps)
{
uint16_t pma_addr0;
#if (USE_USB_DOUBLE_BUFFER == 1U)
uint16_t pma_addr1; /* used for double buffer mode if enabled */
#endif /* (USE_USB_DOUBLE_BUFFER == 1U) */
/* Host Channel */
HCD_HCTypeDef *hc = &(hhcd->hc[ch_num]);
/* Get a FreePMA Address */
pma_addr0 = HAL_HCD_GetFreePMA(hhcd, mps);
/* if there is no free space to allocate */
if (pma_addr0 == 0xFFFFU)
{
return HAL_ERROR;
}
else
{
/* Here we check if the endpoint is single or double Buffer */
if (ch_kind == HCD_SNG_BUF)
{
/* Single Buffer */
hc->doublebuffer = 0U;
if (hc->ep_num == 0U)
{
hhcd->ep0_PmaAllocState = ch_num;
hhcd->ep0_PmaAllocState |= (1U << 8);
}
/* Configure the PMA */
if (hc->ch_dir == CH_IN_DIR)
{
hc->pmaaddr1 = pma_addr0;
(USB_DRD_PMA_BUFF + hc->phy_ch_num)->RXBD = hc->pmaaddr1;
if (hc->ep_num == 0U)
{
hhcd->ep0_PmaAllocState |= (CH_IN_DIR << 4);
}
}
else
{
hc->pmaaddr0 = pma_addr0;
(USB_DRD_PMA_BUFF + hc->phy_ch_num)->TXBD = hc->pmaaddr0;
}
/* Set the PmaAddress */
hc->pmaadress = pma_addr0;
}
#if (USE_USB_DOUBLE_BUFFER == 1U)
else /* USB_DBL_BUF */
{
/* Double Buffer Endpoint */
hc->doublebuffer = 1U;
/* Get a FreePMA Address for buffer 2 */
pma_addr1 = HAL_HCD_GetFreePMA(hhcd, mps);
if (pma_addr1 == 0xFFFFU)
{
/* Free the first buffer */
(void)HAL_HCD_PMAFree(hhcd, pma_addr0, mps);
return HAL_ERROR;
}
else
{
/* Configure the PMA */
hc->pmaaddr0 = (uint16_t)(pma_addr0);
hc->pmaaddr1 = (uint16_t)(pma_addr1);
/* Set Buffer0 pma address */
(USB_DRD_PMA_BUFF + hc->phy_ch_num)->TXBD = pma_addr0;
/* Set Buffer1 pma address */
(USB_DRD_PMA_BUFF + hc->phy_ch_num)->RXBD = pma_addr1;
/* Used for Bulk DB MPS < 64bytes */
if (hc->ch_dir == CH_IN_DIR)
{
hc->pmaadress = hc->pmaaddr1;
}
else
{
hc->pmaadress = hc->pmaaddr0;
}
}
}
#endif /* (USE_USB_DOUBLE_BUFFER == 1U) */
}
return HAL_OK;
}
/**
* @brief PMA De-Allocation for Channel Free the reserved block in the PMA-LookupTable
* @param hhcd Host instance
* @param ch_num Channel number
* @retval HAL status
*/
HAL_StatusTypeDef HAL_HCD_PMADeAlloc(HCD_HandleTypeDef *hhcd, uint8_t ch_num)
{
HAL_StatusTypeDef status;
#if (USE_USB_DOUBLE_BUFFER == 1U)
uint8_t Err = 0U;
#endif /* (USE_USB_DOUBLE_BUFFER == 1U) */
/* Host Channel */
HCD_HCTypeDef *hc = &(hhcd->hc[ch_num]);
/* Single Buffer */
if (hc->doublebuffer == 0U)
{
status = HAL_HCD_PMAFree(hhcd, hc->pmaadress, hc->max_packet);
}
else /* Double buffer */
{
#if (USE_USB_DOUBLE_BUFFER == 1U)
status = HAL_HCD_PMAFree(hhcd, hc->pmaaddr0, hc->max_packet);
if (status != HAL_OK)
{
Err++;
}
status = HAL_HCD_PMAFree(hhcd, hc->pmaaddr1, hc->max_packet);
if (status != HAL_OK)
{
Err++;
}
if (Err != 0U)
{
return HAL_ERROR;
}
#else
status = HAL_ERROR;
#endif /* (USE_USB_DOUBLE_BUFFER == 1U) */
}
return status;
}
/**
* @brief PMA Reset
* @param hhcd Host instance
* @retval HAL status
*/
HAL_StatusTypeDef HAL_HCD_PMAReset(HCD_HandleTypeDef *hhcd)
{
/* Reset All PMA Entry */
for (uint8_t i = 0U; i < PMA_BLOCKS; i++)
{
hhcd->PMALookupTable[i] = 0U;
}
/* Allocate a Space for buffer descriptor table depending on the Host channel number */
for (uint8_t i = 0U; i < hhcd->Init.Host_channels; i++)
{
hhcd->PMALookupTable[0] |= ((uint32_t)1U << i);
}
return HAL_OK;
}
/**
* @brief PMA Free
* @param hhcd Host instance
* @param pma_base PMA base offset stored in hhcd->hc.pmaaddr
* @param mps Max Packet Size
* @retval HAL status
*/
static HAL_StatusTypeDef HAL_HCD_PMAFree(HCD_HandleTypeDef *hhcd, uint32_t pma_base, uint16_t mps)
{
uint32_t block_nbr;
uint8_t ColIndex;
uint8_t LineIndex;
uint16_t mps_t = mps;
/* since PMA buffer descriptor RXBD allocate address according to BLSIZE, BLSIZE=1==> mps>64
allocation in PMA is done in 32Bytes each entry */
if ((mps_t > 64U) && ((mps_t % 32U) != 0U))
{
/* Align the mps to 32byte block to match the allocation in PMA,
check Definition of allocation buffer memory in usb user spec */
mps_t = (uint16_t)(((mps_t / 32U) + 1U) * 32U);
}
/* Calculate the number of needed block to Free */
if ((mps_t / 8U) != 0U)
{
block_nbr = ((uint32_t)mps_t / 8U);
if ((mps_t % 8U) != 0U)
{
block_nbr++;
}
}
else
{
block_nbr = 1U;
}
/* Decode Col/Line of PMA_Base position in the PMA_LookupTable */
if (pma_base > 256U)
{
LineIndex = (uint8_t)(pma_base / 256U);
ColIndex = (uint8_t)((pma_base - ((uint32_t)LineIndex * 256U)) / 8U);
}
else
{
LineIndex = 0U;
ColIndex = (uint8_t)(pma_base / 8U);
}
/* Reset the corresponding bit in the lookupTable */
for (uint8_t i = LineIndex; ((i < PMA_BLOCKS) && (block_nbr > 0U)); i++)
{
for (uint8_t j = ColIndex; j <= 31U; j++)
{
/* Check if the block is not already reserved or it was already closed */
if ((hhcd->PMALookupTable[i] & ((uint32_t)1U << j)) == 0U)
{
return HAL_ERROR;
}
/* Free the reserved block by resetting the corresponding bit */
hhcd->PMALookupTable[i] &= ~(1U << j);
if (--block_nbr == 0U)
{
break;
}
}
ColIndex = 0U;
}
return HAL_OK;
}
/**
* @}
*/
/**
* @}
*/
#endif /* defined (USB_DRD_FS) */
#endif /* HAL_HCD_MODULE_ENABLED */
/**
* @}
*/
/**
* @}
*/