| /** |
| ****************************************************************************** |
| * @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 */ |
| |
| /** |
| * @} |
| */ |
| |
| /** |
| * @} |
| */ |