/**
  *********************************************************************************
  *
  * @file    ald_usb.c
  * @brief   USB module driver.
  *
  * @version V1.0
  * @date    25 Feb 2022
  * @author  AE Team
  * @note
  *          Change Logs:
  *          Date            Author          Notes
  *          25 Feb 2022     AE Team         The first version
  *
  * Copyright (C) Shanghai Eastsoft Microelectronics Co. Ltd. All rights reserved.
  *
  * SPDX-License-Identifier: Apache-2.0
  *
  * Licensed under the Apache License, Version 2.0 (the License); you may
  * not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
  * www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an AS IS BASIS, WITHOUT
  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  **********************************************************************************
  */

#include "ald_usb.h"

/** @addtogroup FS026_ALD
  * @{
  */

/** @defgroup USB USB
  * @brief USB module driver
  * @{
  */
#if 1//def ALD_USB
/**
  * @defgroup USB_Public_Functions USB Public Function
  * @{
  */
/** @defgroup USB_Public_Functions_Group1 Base functions
  * @brief Base functions
  * @{
  */

/**
  * @brief  Gets the number of current frame.
  * @retval Number of the frame.
  */
uint32_t ald_usb_frame_number_get(void)
{
  uint8_t framel = (uint8_t)USB->FRAME1;
  uint8_t frameh = (uint8_t)USB->FRAME2;
	return ((uint32_t)(framel & 0xFF) + ((uint32_t)(frameh & 0xFF) << 8));
}

/**
  * @brief  Request the session.
  * @param  start: true/false.
  * @retval None
  */
void ald_usb_otg_session_request(bool start)
{
	if (start)
		USB->DEVCON |= (uint8_t)ALD_USB_DEVCON_SESSION;
	else
		USB->DEVCON &= (uint8_t)(~(ALD_USB_DEVCON_SESSION));
	
	return;
}

/**
  * @brief  Gets the mode.
  * @retval Mode
  */
uint32_t ald_usb_mode_get(void)
{
	return (USB->DEVCON & ((uint8_t)(ALD_USB_DEVCON_HOST | ALD_USB_DEVCON_SESSION)));
}

/**
  * @brief  Start host require.
  * @retval Mode
  */
uint32_t ald_usb_mode_host_req(void)
{
	USB->DEVCON |= (uint8_t)ALD_USB_DEVCON_HOSTREQ;
	return 0;
}

/**
  * @brief  Clear host require.
  * @retval Mode
  */
uint32_t ald_usb_mode_host_req_clear(void)
{
	USB->DEVCON &= (uint8_t)(~ALD_USB_DEVCON_HOSTREQ);
	return 0;
}

/**
  * @brief  Enable/Disable the high mode.
  * @param  enable: ENABLE/DISABLE.
  * @retval None
  */
void ald_usb_high_speed_enable(bool enable)
{
	assert_param(enable == DISABLE);
	
	return;
}

/**
  * @brief  Gets the speed of the device.
  * @retval Type of the speed.
  */
uint32_t ald_usb_device_speed_get(void)
{
	return ALD_USB_FULL_SPEED;
}

/**
  * @brief  Gets the number of the endpoint.
  * @retval Number of the endpoint.
  */
uint32_t ald_usb_num_ep_get( void)
{
	return ALD_NUM_USB_EP;
}

/**
  * @brief  Reset USB Control.
  * @retval None
  */
void ald_usb_control_reset(void)
{
	//ALD_RCU_USB_RESET_ENABLE();
	
	return;
}

/**
  * @brief  Output USB clock, not support.
  * @retval None
  */
void ald_usb_clock_output(void)
{
	return;
}

/**
  * @brief  Starts eye diagram for high-speed host, not support.
  * @param  buf: Buffer for eye diagram.
  * @param  len: Length of the buffer.
  * @retval Status, 0 means success, other values means failure.
  */
int ald_usb_eye_diagram_start(uint8_t *buf, uint16_t len)
{
	return 0;
}

/**
  * @brief  Pull up or pull down USB dp line.
  * @param  pupd: USB_DPDM_FLOATING/USB_DPDM_PUSH_UP/USB_DPDM_PUSH_DOWN.
  * @retval None
  */
void ald_usb_dppud_set(ald_dpdm_push_t pupd)
{
	USB->DPDMCON  &= (uint8_t)(~USB_DPDMCON_DPPUD_MSK);
	USB->DPDMCON  |= (uint8_t)(pupd << USB_DPDMCON_DPPUD_POSS);
	return;
}

/**
  * @brief  Pull up or pull down USB dm line.
  * @param  pupd: USB_DPDM_FLOATING/USB_DPDM_PUSH_UP/USB_DPDM_PUSH_DOWN.
  * @retval None
  */
void ald_usb_dmpud_set(ald_dpdm_push_t pupd)
{
	USB->DPDMCON  &= (uint8_t)(~USB_DPDMCON_DMPUD_MSK);
	USB->DPDMCON  |= (uint8_t)(pupd << USB_DPDMCON_DMPUD_POSS);
	return;
}

/**
  * @brief  Switch usb mode by software.
  * @param  host: 0, indicates that software force to host
			host: 1, indicates that software force to device.
  * @retval None
  */
void ald_usb_swcid_host(uint8_t host)
{
	if (host)
		USB->SWCID  |= (uint8_t)(USB_SWCID_HOST_MSK);
	else
		USB->SWCID  &= (uint8_t)(~USB_SWCID_HOST_MSK);
	return;
}

/**
  * @brief  Switch the control method of CID.
  * @param  cid: 0, indicates that use the hardware control
			cid: 1, indicates that use the software control.
  * @retval None
  */
void ald_usb_swcid_cidctrl(uint8_t cid)
{
	if (cid)
		USB->SWCID  |= (uint8_t)(ALD_USB_SWCID_CIDCTRL);
	else
		USB->SWCID  &= (uint8_t)(~ALD_USB_SWCID_CIDCTRL);
	return;
}

/**
  * @}
  */

/** @defgroup USB_Public_Functions_Group2 Device functions
  * @brief Device functions
  * @{
  */

/**
  * @brief  Gets the address.
  * @retval Address.
  */
uint8_t ald_usb_dev_get_addr(void)
{
	return (USB->FADDR);
}

/**
  * @brief  Sets the address.
  * @param  addr: The address which will be set.
  * @retval None
  */
void ald_usb_dev_set_addr(uint8_t addr)
{
	USB->FADDR = addr;
	return;
}

/**
  * @brief  Enable connection.
  * @retval None
  */
void ald_usb_dev_connect(void)
{
	USB->DPDMCON |= (uint8_t)(ALD_USB_DPDMCON_PHYPWREN);
	return;
}

/**
  * @brief  Disable connection.
  * @retval None
  */
void ald_usb_dev_disconnect(void)
{
	USB->DPDMCON &= (uint8_t)(~(ALD_USB_DPDMCON_PHYPWREN));
	return;
}

/**
  * @brief  Enable the devices suspend.
  * @retval None
  */
void ald_usb_dev_suspend_enable(void)
{
	USB->POWER |= (uint8_t)USB_POWER_SUSPENDEN_MSK;
	return;
}

/**
  * @brief  Configure the endpoint in device mode.
  * @param  ep_idx: Index of the endpoint
  * @param  p_max: Size of the maximum package.
  * @param  flags: Flags of the endpoint.
  * @retval None
  */
void ald_usb_dev_ep_config(uint32_t ep_idx, uint32_t p_max, uint32_t flags)
{
	uint32_t tmp = 0U;	
	
	USB->INDEX = (uint8_t)ep_idx;
	
	if (flags & ALD_USB_EP_DEV_IN) {
		USB->TXMAXP = (uint8_t)(p_max);
		if (flags & ALD_USB_EP_AUTO_SET)
			tmp |= USB_TXCSRH_AUTOSET_MSK;
		if ((flags & ALD_USB_EP_MODE_MASK) == ALD_USB_EP_MODE_ISOC)
			tmp |= USB_TXCSRH_ISO_MSK;

		USB->CSR0H_TXCSRH |= (uint8_t)tmp;
		USB->CSR0L_TXCSRL |= (uint8_t)USB_TXCSRL_CLRDT_MSK;
	}
	else {
		USB->RXMAXP = (uint8_t)(p_max);
		if (flags & ALD_USB_EP_AUTO_CLEAR)
			tmp |= USB_RXCSRH_AUTOCLR_MSK;
		if ((flags & ALD_USB_EP_MODE_MASK) == ALD_USB_EP_MODE_ISOC)
			tmp |= USB_TXCSRH_ISO_MSK;

		USB->RXCSRH |= (uint8_t)tmp;
		USB->RXCSRL |= (uint8_t)USB_RXCSRL_CLRDT_MSK;
	}
	
	return;
}

/**
  * @brief  Gets the parameters of the endpoint.
  * @param  ep_idx: Index of the endpoint
  * @param  p_max: Size of the maximum package.
  * @param  flags: Flags of the endpoint.
  * @retval None
  */
void ald_usb_dev_ep_get_config(uint32_t ep_idx, uint32_t *p_max, uint32_t *flags)
{
	uint32_t tmp;	
	
	USB->INDEX = (uint8_t)ep_idx;
	
	if (*flags & ALD_USB_EP_DEV_IN) {
		*flags = ALD_USB_EP_DEV_IN;
		*p_max = (uint32_t)USB->TXMAXP;
		tmp    = (uint32_t)USB->CSR0H_TXCSRH;
		if (tmp & USB_TXCSRH_AUTOSET_MSK)
			*flags |= ALD_USB_EP_AUTO_SET;
		if (tmp & USB_TXCSRH_ISO_MSK)
			*flags |= ALD_USB_EP_MODE_ISOC;
		else
			*flags |= ALD_USB_EP_MODE_BULK;
	}
	else {
		*flags = ALD_USB_EP_DEV_OUT;
		*p_max = (uint32_t)USB->RXMAXP;
		tmp    = (uint32_t)USB->RXCSRH;

		if (tmp & USB_RXCSRH_AUTOCLR_MSK)
			*flags |= ALD_USB_EP_AUTO_CLEAR;
		if (tmp & USB_RXCSRH_ISO_MSK)
			*flags |= ALD_USB_EP_MODE_ISOC;
		else
			*flags |= ALD_USB_EP_MODE_BULK;
	}
	return;
}
extern void printf_e(const char *fmt, ...);
/**
  * @brief  Acknowledge the data from host.
  * @param  ep_idx: Index of the endpoint
  * @param  last: true/false
  * @retval None
  */
void ald_usb_dev_ep_data_ack(uint32_t ep_idx, bool last)
{	
	USB->INDEX = (uint8_t)ep_idx;
	
	if (ep_idx == ALD_USB_EP_0)
		USB->CSR0L_TXCSRL |= (uint8_t)(USB_CSR0L_RXRDYC_MSK | (last ? USB_CSR0L_DATAEND_MSK : 0));
	else
		USB->RXCSRL &= (uint8_t)(~USB_RXCSRL_RXRDY_MSK);
	
	return;
}

/**
  * @brief  Stall the endpoint.
  * @param  ep_idx: Index of the endpoint
  * @param  flags: Flags.
  * @retval None
  */
void ald_usb_dev_ep_stall(uint32_t ep_idx, uint32_t flags)
{
	USB->INDEX = (uint8_t)ep_idx;
	
	if (ep_idx == ALD_USB_EP_0)
		USB->CSR0L_TXCSRL |= (uint8_t)(USB_CSR0L_RXRDYC_MSK | USB_CSR0L_STALL_MSK);
	else if (flags == ALD_USB_EP_DEV_IN)
		USB->CSR0L_TXCSRL |= (uint8_t)USB_TXCSRL_STALL_MSK;
	else
		USB->RXCSRL |= (uint8_t)USB_RXCSRL_STALL_MSK;
	
	return;
}

/**
  * @brief  Cancel the stall status.
  * @param  ep_idx: Index of the endpoint
  * @param  flags: Flags.
  * @retval None
  */
void ald_usb_dev_ep_stall_clear(uint32_t ep_idx, uint32_t flags)
{
	USB->INDEX = (uint8_t)ep_idx;
	
	if (ep_idx == ALD_USB_EP_0)
		USB->CSR0L_TXCSRL &= (uint8_t)(~USB_CSR0L_STALLED_MSK);
	else if (flags == ALD_USB_EP_DEV_IN){
		USB->CSR0L_TXCSRL &= (uint8_t)(~(USB_TXCSRL_STALL_MSK | USB_TXCSRL_STALLED_MSK));
		USB->CSR0L_TXCSRL |= (uint8_t)USB_TXCSRL_CLRDT_MSK;
	}
	else{
		USB->RXCSRL &= (uint8_t)(~(USB_RXCSRL_STALL_MSK | USB_RXCSRL_STALLED_MSK));
		USB->RXCSRL |= (uint8_t)USB_RXCSRL_CLRDT_MSK;
	}
	
	return;
}

/**
  * @brief  Clear the status of the endpoint.
  * @param  ep_idx: Index of the endpoint
  * @param  flags: Flags.
  * @retval None
  */
void ald_usb_dev_ep_status_clear(uint32_t ep_idx, uint32_t flags)
{
	USB->INDEX = (uint8_t)ep_idx;
	
	if (ep_idx == ALD_USB_EP_0) {
		if (flags & ALD_USB_DEV_EP0_OUT_PKTRDY)
			USB->CSR0L_TXCSRL |= (uint8_t)USB_CSR0L_RXRDYC_MSK;
		if (flags & ALD_USB_DEV_EP0_SETUP_END)
			USB->CSR0L_TXCSRL |= (uint8_t)USB_CSR0L_SETENDC_MSK;
		if (flags & ALD_USB_DEV_EP0_SENT_STALL)
			USB->CSR0L_TXCSRL &= (uint8_t)(~USB_CSR0L_STALLED_MSK);
	}
	else {
		USB->CSR0L_TXCSRL &= (uint8_t)(~(flags & (ALD_USB_DEV_TX_SENT_STALL | ALD_USB_DEV_TX_UNDERRUN)));
		USB->RXCSRL &= (uint8_t)(~((flags & (ALD_USB_DEV_RX_SENT_STALL | ALD_USB_DEV_RX_DATA_ERROR
						 | ALD_USB_DEV_RX_OVERRUN)) >> ALD_USB_RX_EPSTATUS_SHIFT));
	}
	
	return;
}

/**
  * @}
  */

/** @defgroup USB_Public_Functions_Group3 Host functions
  * @brief Host functions
  * @{
  */
/**
  * @brief  Gets the device's address.
  * @param  ep_idx: Index of the endpoint
  * @param  flags: Flags.
  * @retval Address
  */
uint32_t ald_usb_host_addr_get(uint32_t ep_idx, uint32_t flags)
{
	USB->INDEX = (uint8_t)ep_idx;
	
	return (USB->FADDR);
}

/**
  * @brief  Sets the device's address.
  * @param  ep_idx: Index of the endpoint.
  * @param  addr: The device's address.
  * @param  flags: Flags.
  * @retval None
  */
void ald_usb_host_addr_set(uint32_t ep_idx, uint32_t addr, uint32_t flags)
{
	USB->INDEX = (uint8_t)ep_idx;
	
	USB->FADDR = (uint8_t)addr;
	
	return;
}

/**
  * @brief  Configure the endpoint in host mode.
  * @param  ep_idx: Index of the endpoint.
  * @param  p_max: Size of the maximum package.
  * @param  nak_val: Value of the nack.
  * @param  t_ep: Target endpoint.
  * @param  flags: Flags.
  * @retval None
  */
void ald_usb_host_ep_config(uint32_t ep_idx, uint32_t p_max, uint32_t nak_val, uint32_t t_ep, uint32_t flags)
{
	uint32_t tmp = 0U;

	USB->INDEX = (uint8_t)ep_idx;
	
	if (ep_idx == ALD_USB_EP_0) {
		USB->NAKLIMIT0_TXINTERVAL = (uint8_t)nak_val;

		if (flags & ALD_USB_EP_SPEED_HIGH)
			;
		else if (flags & ALD_USB_EP_SPEED_FULL)
			;
		else
			;
	}
	else {
		tmp = t_ep;

		if (flags & ALD_USB_EP_SPEED_HIGH)
			;
		else if (flags & ALD_USB_EP_SPEED_FULL)
			;
		else
			;

		switch (flags & ALD_USB_EP_MODE_MASK) {
			case ALD_USB_EP_MODE_BULK:
				tmp |= ALD_USB_TXTYPE1_PROTO_BULK;
				break;
			
			case ALD_USB_EP_MODE_ISOC:
				tmp |= ALD_USB_TXTYPE1_PROTO_ISOC;
				break;
			
			case ALD_USB_EP_MODE_INT:
				tmp |= ALD_USB_TXTYPE1_PROTO_INT;
				break;
			
			case ALD_USB_EP_MODE_CTRL:
				tmp |= ALD_USB_TXTYPE1_PROTO_CTRL;
				break;
		}
		
		if (flags & ALD_USB_EP_HOST_OUT) {
			USB->TXTYPE = (uint8_t)tmp;
			USB->NAKLIMIT0_TXINTERVAL = (uint8_t)nak_val;
			USB->TXMAXP = (uint8_t)p_max;

			tmp = 0;
			if (flags & ALD_USB_EP_AUTO_SET)
				tmp = (uint8_t)ALD_USB_TXCSRH_AUTOSET;
			USB->CSR0H_TXCSRH |= (uint8_t)tmp;
		}
		else {
			USB->RXTYPE = (uint8_t)tmp;
			USB->RXINTERVAL = (uint8_t)nak_val;
			USB->RXMAXP = (uint8_t)p_max;

			tmp = 0;
			if (flags & ALD_USB_EP_AUTO_CLEAR)
				tmp |= (uint8_t)USB_RXCSRH_AUTOCLR_MSK;
			if (flags & ALD_USB_EP_AUTO_REQUEST)
				tmp |= (uint8_t)USB_RXCSRH_AUTOREQ_MSK;

			USB->RXCSRH |= (uint8_t)tmp;
		}
	}
	
	return;
}

/**
  * @brief  Acknowledge the data in host mode.
  * @param  ep_idx: Index of the endpoint.
  * @retval None
  */
void ald_usb_host_ep_data_ack(uint32_t ep_idx)
{
	USB->INDEX = (uint8_t)ep_idx;
	
	if (ep_idx == ALD_USB_EP_0)
		USB->CSR0L_TXCSRL &= (uint8_t)(~(USB_CSR0L_RXRDY_MSK));
	else
		USB->RXCSRL &= (uint8_t)(~(USB_RXCSRL_RXRDY_MSK));
	
	return;
}

/**
  * @brief  Toggle the data in host mode.
  *         The function is used to force the state of the data toggle in host mode.
  *         If the value passed in the bDataToggle parameter is false, then the data
  *         toggle is set to the DATA0 state, and if it is true it is set to the DATA1
  *         state.
  * @param  ep_idx: Index of the endpoint.
  * @param  toggle: true/false.
  * @param  flags: can be USB_EP_HOST_IN or USB_EP_HOST_OUT.
  * @retval None
  */
void ald_usb_host_ep_data_toggle(uint32_t ep_idx, bool toggle, uint32_t flags)
{
	/* not supported by es32f0271. */
	return;
}

/**
  * @brief  Clear the status of endpoint in host mode.
  * @param  ep_idx: Index of the endpoint.
  * @param  flags: Flags.
  * @retval None
  */
void ald_usb_host_ep_status_clear(uint32_t ep_idx, uint32_t flags)
{	
	USB->INDEX = (uint8_t)ep_idx;

	if (ep_idx == ALD_USB_EP_0) {
		USB->CSR0L_TXCSRL &= (uint8_t)(~flags & 0xFF);
	}
	else {
		USB->CSR0L_TXCSRL &= (uint8_t)(~flags & 0xFF);
		USB->RXCSRL &= (uint8_t)(~(flags >> 16) & 0xFF);
	}
	
	return;
}

/**
  * @brief  Gets the HUB's address.
  * @param  ep_idx: Index of the endpoint.
  * @param  flags: Flags.
  * @retval Address
  */
uint32_t ald_usb_host_hub_addr_get(uint32_t ep_idx, uint32_t flags)
{
	/* not supported by es32f0271. */
	return 0;
}

/**
  * @brief  Sets the HUB's address.
  * @param  ep_idx: Index of the endpoint.
  * @param  addr: HUB's address which will be set.
  * @param  flags: Flags.
  * @retval Address
  */
void ald_usb_host_hub_addr_set(uint32_t ep_idx, uint32_t addr, uint32_t flags)
{
	/* not supported by es32f0271. */
	return;
}

/**
  * @brief  Disable power.
  * @retval None
  */
void ald_usb_host_pwr_disable(void)
{
	USB->DPDMCON &= (uint8_t)(~(ALD_USB_DPDMCON_PHYPWREN));
	return;
}

/**
  * @brief  Enable power.
  * @retval None
  */
void ald_usb_host_pwr_enable(void)
{
	USB->DPDMCON |= (uint8_t)(ALD_USB_DPDMCON_PHYPWREN);
	return;
}

/**
  * @brief  Configure power in host mode.
  * @param  flags: Flags
  * @retval None
  */
void ald_usb_host_pwr_config(uint32_t flags)
{
	return;
}

/**
  * @brief  Disable the fault parameters of the power.
  * @retval None
  */
void ald_usb_host_pwr_fault_disable(void)
{
	return;
}

/**
  * @brief  Enable the fault parameters of the power.
  * @retval None
  */
void ald_usb_host_pwr_fault_enable(void)
{
	return;
}

/**
  * @brief  Request data IN(from device to host)
  * @param  ep_idx: Index of the endpoint.
  * @retval None
  */
void ald_usb_host_request_in(uint32_t ep_idx)
{	
	USB->INDEX = (uint8_t)ep_idx;

	if (ep_idx == ALD_USB_EP_0)
		USB->CSR0L_TXCSRL |= (uint8_t)USB_CSR0L_REQPKT_MSK;
	else
		USB->RXCSRL |= (uint8_t)USB_RXCSRL_REQPKT_MSK;
	
	return;
}

/**
  * @brief  Clear the status of request IN.
  * @param  ep_idx: Index of the endpoint.
  * @retval None
  */
void ald_usb_host_request_in_clear(uint32_t ep_idx)
{	
	USB->INDEX = (uint8_t)ep_idx;

	if (ep_idx == ALD_USB_EP_0)
		USB->CSR0L_TXCSRL &= (uint8_t)(~USB_CSR0L_REQPKT_MSK);
	else
		USB->RXCSRL &= (uint8_t)(~USB_RXCSRL_REQPKT_MSK);
	
	return;
}

/**
  * @brief  Set the request for a status IN transaction.
  * @retval None
  */
void ald_usb_host_request_status(void)
{
	USB->CSR0L_TXCSRL |= (uint8_t)(ALD_USB_CSR0L_REQPKT | ALD_USB_CSR0L_STATUSPKT);
	
	return;
}

/**
  * @brief  Reset the USB's bus.
  * @param  start: true/false.
  * @retval None
  */
void ald_usb_host_reset(bool start)
{
	if (start)
		USB->POWER |= ALD_USB_POWER_RESET;
	else
		USB->POWER &= ~(ALD_USB_POWER_RESET);
	
	return;
}

/**
  * @brief  Resume the devices.
  * @param  start: true/false.
  * @retval None
  */
void ald_usb_host_resume(bool start)
{
	if (start)
		USB->POWER |= (uint8_t)USB_POWER_RESUME_MSK;
	else
		USB->POWER &= (uint8_t)(~(ALD_USB_POWER_RESUME));
	
	return;
}

/**
  * @brief  Suspend the devices.
  * @retval None
  */
void ald_usb_host_suspend(void)
{
	USB->POWER |= (uint8_t)USB_POWER_SUSPEND_MSK;
	return;
}

/**
  * @brief  Gets the device's speed.
  * @retval Type of the speed.
  */
uint32_t ald_usb_host_speed_get(void)
{
	if (USB->DEVCON & ALD_USB_DEVCON_FSDEV)
		return ALD_USB_FULL_SPEED;

	if (USB->DEVCON & ALD_USB_DEVCON_LSDEV)
		return ALD_USB_LOW_SPEED;

	return ALD_USB_UNDEF_SPEED;
}

/**
  * @brief  Sets the endpoint speed.
  * @param  ep_idx: Index of the endpoint.
  * @param  flags: Type of the speed.
  * @retval None
  */
void ald_usb_host_ep_speed_set(uint32_t ep_idx, uint32_t flags)
{
	/* not support in F0271 */
	return;
}

/**
  * @brief  Ping the endpoint.
  * @param  ep_idx: Index of the endpoint.
  * @param  enable: ENABLE/DISABLE.
  * @retval None
  */
void ald_usb_host_ep_ping(uint32_t ep_idx, bool enable)
{
	/* not support in F0271 */
	return;
}

/**
  * @}
  */

/** @defgroup USB_Public_Functions_Group4 Endpoint functions
  * @brief Endpoint functions
  * @{
  */
/**
  * @brief  Gets the size of the available data.
  * @param  ep_idx: Index of the endpoint
  * @retval Size in bytes.
  */
uint32_t ald_usb_ep_data_avail(uint32_t ep_idx)
{
	USB->INDEX = (uint8_t)ep_idx;
		
	if (ep_idx == ALD_USB_EP_0) {
		if ((USB->CSR0L_TXCSRL & ALD_USB_CSR0L_RXRDY) == 0){
			return 0;
		}

		return USB->COUNT0_RX1;
	}
	else {
		if ((USB->RXCSRL & ALD_USB_CSR0L_RXRDY) == 0)
		{
			return 0;
		}

		return (USB->COUNT0_RX1 + (((uint32_t)USB->RXCOUNT2) << 8));
	}
}

/**
  * @brief  Gets the data from FIFO.
  * @param  ep_idx: Index of the endpoint
  * @param  data: Pointer to the buffer.
  * @param  size: Size of the data.
  * @retval Status.
  */
int32_t ald_usb_ep_data_get(uint32_t ep_idx, uint8_t *data, uint32_t *size)
{
	uint32_t i, rx_fifo_addr;
	
	USB->INDEX = (uint8_t)ep_idx;
	
	if (ep_idx == ALD_USB_EP_0) {
		if ((USB->CSR0L_TXCSRL & ALD_USB_CSR0L_RXRDY) == 0) {
			*size = 0;
			return -1;
		}
		i = USB->COUNT0_RX1;
	}
	else {		
		if ((USB->RXCSRL & ALD_USB_CSR0L_RXRDY) == 0) {
			*size = 0;
			return -1;
		}
		i = USB->COUNT0_RX1 + (((uint32_t)USB->RXCOUNT2) << 8);
	}
	
	i = (i < *size) ? i : *size;
	*size = i;
	
	rx_fifo_addr = (uint32_t)(&USB->EP0FIFO) + 4 * ep_idx;
	
	for (; i > 0; i--)
		*data++ = *(volatile uint8_t *)(rx_fifo_addr);
	
	return 0;
}

/**
  * @brief  Puts data to the FIFO.
  * @param  ep_idx: Index of the endpoint
  * @param  data: Pointer to the data.
  * @param  size: Size of the data.
  * @retval Status.
  */
int32_t ald_usb_ep_data_put(uint32_t ep_idx, uint8_t *data, uint32_t size)
{
	uint32_t tx_fifo_addr;
	
	USB->INDEX = (uint8_t)ep_idx;
	
	if (ep_idx == ALD_USB_EP_0) {
		if (USB->CSR0L_TXCSRL & ALD_USB_CSR0L_TXRDY)
			return -1;
	}
	else {
		if (USB->CSR0L_TXCSRL & ALD_USB_TXCSRL_TXRDY)
			return -1;
	}
	
	tx_fifo_addr = (uint32_t)(&USB->EP0FIFO) + 4 * ep_idx;
	
	for (; size > 0; size--)
		*(volatile uint8_t *)tx_fifo_addr = *data++;

	return 0;
}

/**
  * @brief  Send data.
  * @param  ep_idx: Index of the endpoint
  * @param  tx_type: Type.
  * @retval Status.
  */
int32_t ald_usb_ep_data_send(uint32_t ep_idx, uint32_t tx_type)
{
	uint32_t tmp;
	
	USB->INDEX = (uint8_t)ep_idx;
	
	if (ep_idx == ALD_USB_EP_0) {
		if (USB->CSR0L_TXCSRL & ALD_USB_CSR0L_TXRDY)
			return -1;

		tmp = tx_type & 0xFF;
	}
	else {
		if (USB->CSR0L_TXCSRL & ALD_USB_TXCSRL_TXRDY)
			return -1;

		tmp = (tx_type >> 8) & 0xff;
	}
	
	USB->CSR0L_TXCSRL = tmp;

	return 0;
}

/**
  * @brief  Clear the status of the toggle.
  * @param  ep_idx: Index of the endpoint
  * @param  flags: Flags.
  * @retval None
  */
void ald_usb_ep_data_toggle_clear(uint32_t ep_idx, uint32_t flags)
{
	USB->INDEX = (uint8_t)ep_idx;
	
	if (flags & (ALD_USB_EP_HOST_OUT | ALD_USB_EP_DEV_IN))
		USB->CSR0L_TXCSRL |= (uint8_t)ALD_USB_TXCSRL_CLRDT;
	else
		USB->RXCSRL |= (uint8_t)ALD_USB_RXCSRL_CLRDT;
	
	return;
}

/**
  * @brief  Sets the size of request data IN
  * @param  ep_idx: Index of the endpoint
  * @param  count: Size of request data IN.
  * @retval None
  */
void ald_usb_ep_req_packet_count(uint32_t ep_idx, uint32_t count)
{
	/* not support in f0271 */
	return;
}

/**
  * @brief  Gets the status of the endpoint.
  * @param  ep_idx: Index of the endpoint
  * @retval Status.
  */
uint32_t ald_usb_ep_status(uint32_t ep_idx)
{
	uint32_t status;
	
	USB->INDEX = (uint8_t)ep_idx;
	
	status = (ep_idx == ALD_USB_EP_0) ? (USB->CSR0L_TXCSRL): (USB->CSR0L_TXCSRL | (USB->RXCSRL << 16));
	
	return status;
}

/**
  * @brief  Configure the endpoint in DMA mode.
  * @param  ep_idx: Index of the endpoint
  * @param  flag: Flags.
  * @param  en: ENABLE/DISABLE.
  * @retval None
  */
void ald_usb_ep_dma_config(uint32_t ep_idx, uint32_t flag, TypeFunc en)
{
	/* Not supported in F0271 */

	return;
}
/**
  * @}
  */

/** @defgroup USB_Public_Functions_Group5 FIFO functions
  * @brief FIFO functions
  * @{
  */
/**
  * @brief  Gets the address of the FIFO.
  * @param  ep_idx: Index of the endpoint
  * @retval Address
  */
uint32_t ald_usb_fifo_addr_get(uint32_t ep_idx)
{
	/* Not supported in F0271 */
	
	return 0;
}

/**
  * @brief  Gets the parameters of the FIFO.
  * @param  ep_idx: Index of the endpoint
  * @param  addr: Address.
  * @param  size: Size of FIFO.
  * @param  flags: Flags.
  * @retval None
  */
void ald_usb_fifo_config_get(uint32_t ep_idx, uint32_t *addr, uint32_t *size, uint32_t flags)
{
	USB->INDEX = ep_idx;

	if (flags & (ALD_USB_EP_HOST_OUT | ALD_USB_EP_DEV_IN)) {
		*addr = ((uint32_t)USB->TXFIFO1 | ((uint32_t)USB->TXFIFO2 & 0x03)) << 3;
		*size = ((USB->TXFIFO2 & ALD_USB_TXFIFO2_DPB) == 0x00) ? (USB->TXFIFO2 & ALD_USB_TXFIFO2_MAXPKTSIZE_1024) : (2 * (USB->TXFIFO2 & ALD_USB_TXFIFO2_MAXPKTSIZE_1024));
	}
	else {
		*addr = ((uint32_t)USB->RXFIFO1 | ((uint32_t)USB->RXFIFO2 & 0x03)) << 3;
		*size = ((USB->RXFIFO2 & ALD_USB_RXFIFO2_DPB) == 0x00) ? (USB->RXFIFO2 & ALD_USB_RXFIFO2_MAXPKTSIZE_1024) : (2 * (USB->RXFIFO2 & ALD_USB_RXFIFO2_MAXPKTSIZE_1024));
	}

	return;
}

/**
  * @brief  Sets the parameters of the FIFO.
  * @param  ep_idx: Index of the endpoint
  * @param  addr: Address.
  * @param  size: Size of FIFO, valid parameter has defined in usb_lowlayer_api.h.
  * @param  flags: Flags.
  * @retval None
  */
void ald_usb_fifo_config_set(uint32_t ep_idx, uint32_t addr, uint32_t size, uint32_t flags)
{
	USB->INDEX = ep_idx;
	
	if (flags & (ALD_USB_EP_HOST_OUT | ALD_USB_EP_DEV_IN)) {
		USB->TXFIFO1 = (uint8_t)((addr >> 3) & 0xFF);
		USB->TXFIFO2 |= (uint8_t)(((addr >> 3) >> 8) & 0x0F);
		
		USB->TXFIFO2 |= (uint8_t)(size << USB_TXFIFO2_MAXPKTSIZE_POSS);
		
		USB->CSR0L_TXCSRL |= (uint8_t)ALD_USB_TXCSRL_FLUSH;
	}
	else {
		USB->RXFIFO1 = (uint8_t)((addr >> 3) & 0xFF);
		USB->RXFIFO2 |= (uint8_t)(((addr >> 3) >> 8) & 0x0F);
		
		USB->RXFIFO2 |= (uint8_t)(size << USB_RXFIFO2_MAXPKTSIZE_POSS);
		
		USB->RXCSRL |= (uint8_t)ALD_USB_RXCSRL_FLUSH;
	}

	return;
}

/**
  * @brief  Flush the FIFO
  * @param  ep_idx: Index of the endpoint
  * @param  flags: Flags.
  * @retval None
  */
void ald_usb_fifo_flush(uint32_t ep_idx, uint32_t flags)
{
	USB->INDEX = ep_idx;
	
	if (ep_idx == ALD_USB_EP_0) {
		if ((USB->CSR0L_TXCSRL & (ALD_USB_CSR0L_RXRDY | ALD_USB_CSR0L_TXRDY)) != 0)
			USB->CSR0H_TXCSRH |= ALD_USB_CSR0H_FLUSH;
	}
	else {
		if (flags & (ALD_USB_EP_HOST_OUT | ALD_USB_EP_DEV_IN)) {
			if (USB->CSR0L_TXCSRL & ALD_USB_TXCSRL_TXRDY)
				USB->CSR0L_TXCSRL |= ALD_USB_TXCSRL_FLUSH;
		}
		else {
			if (USB->RXCSRL & ALD_USB_RXCSRL_RXRDY)
				USB->RXCSRL |= ALD_USB_RXCSRL_FLUSH;
		}
	}
	return;
}

/**
  * @}
  */

/** @defgroup USB_Public_Functions_Group6 Interrupt functions
  * @brief Interrupt functions
  * @{
  */
/**
  * @brief  Disable interrupt.
  * @param  flags: Type of the interrupt.
  * @retval None
  */
void ald_usb_int_disable(uint32_t flags)
{
	if (flags & ALD_USB_IDR_STATUS)
		USB->IDR |= (uint8_t)(flags & ALD_USB_IDR_STATUS);
	
	return;
}

/**
  * @brief  Enable interrupt.
  * @param  flags: Type of the interrupt.
  * @retval None
  */
void ald_usb_int_enable(uint32_t flags)
{
	if (flags & ALD_USB_IER_STATUS)
		USB->IER |= (uint8_t)(flags & ALD_USB_IER_STATUS);
	
	return;
}

/**
  * @brief  Gets the status of the interrupt.
  * @retval Status.
  */
uint32_t ald_usb_int_status_get(void)
{
	uint32_t Status;
	
	Status = USB->IFM & 0x7F;
	USB->ICR |= Status;
	
	return Status;
}

/**
  * @brief  Disable interrupt of the endpoint.
  * @param  flags: Type of the interrupt.
  * @retval None
  */
void ald_usb_int_disable_ep(uint32_t flags)
{
	USB->TXIDR |= (uint8_t)(flags & (ALD_USB_INTEP_HOST_OUT | ALD_USB_INTEP_DEV_IN | ALD_USB_INTEP_0));
	USB->RXIDR |= (uint8_t)((flags & (ALD_USB_INTEP_HOST_IN | ALD_USB_INTEP_DEV_OUT)) >> ALD_USB_INTEP_RX_SHIFT);
	return;
}

/**
  * @brief  Enable interrupt of the endpoint.
  * @param  flags: Type of the interrupt.
  * @retval None
  */
void ald_usb_int_enable_ep(uint32_t flags)
{
	USB->TXIER |= (uint8_t)(flags & (ALD_USB_INTEP_HOST_OUT | ALD_USB_INTEP_DEV_IN | ALD_USB_INTEP_0));
	USB->RXIER |= (uint8_t)((flags & (ALD_USB_INTEP_HOST_IN | ALD_USB_INTEP_DEV_OUT)) >> ALD_USB_INTEP_RX_SHIFT);
	return;
}

/**
  * @brief  Gets the ststus of the endpoint interrupt.
  * @retval Status.
  */
uint32_t ald_usb_int_status_ep_get(void)
{
	uint32_t status;

	status = USB->TXIFM;
	status |= (USB->RXIFM << ALD_USB_INTEP_RX_SHIFT);
	
	USB->TXICR |= (uint8_t)(status & 0xFF);
	USB->RXICR |= (uint8_t)((status >> ALD_USB_INTEP_RX_SHIFT) & 0xFF);

	return status;
}

/**
  * @brief  Register USB's interrupt.
  * @retval None
  */
void ald_usb_int_register(void)
{
	//ald_mcu_irq_config(USB_IRQn, 2, ENABLE);
	NVIC_SetPriority(USB_IRQn, 3);
	NVIC_EnableIRQ(USB_IRQn);
	return;
}

/**
  * @brief  Unregister USB's interrupt.
  * @retval None
  */
void ald_usb_int_unregister(void)
{
	//ald_mcu_irq_config(USB_IRQn, 2, DISABLE);
	NVIC_DisableIRQ(USB_IRQn);
	return;
}

/**
  * @brief  Get USB's interrupt number.
  * @retval None
  */
uint32_t ald_usb_int_num_get(void)
{
	return USB_IRQn;
}
/**
  * @}
  */

/** @defgroup USB_Public_Functions_Group7 DMA functions
  * @brief DMA functions
  * @{
  */
#if defined(__ALD_MDA_H__)
/**
  * @brief  Configure DMA's channel.
  * @param  ch: Channel.
  * @param  addr: Address.
  * @param  count: Size of the data to be moved.
  * @param  ctrl: Parameters of the DMA's controler
  * @retval None
  */
void ald_usb_dma_channel_config(uint8_t ch, uint32_t addr, uint32_t count, uint32_t ctrl)
{
	return;
}

/**
  * @brief  Start multiple receive.
  * @param  ep_idx: Index of the endpoint
  * @retval None
  */
void ald_usb_dma_mult_recv_start(uint32_t ep_idx)
{
	return;
}

/**
  * @brief  Start DMA's machine.
  * @param  ch: Channel.
  * @retval None
  */
void ald_usb_dma_channel_start(uint8_t ch)
{
	return;
}

/**
  * @brief  Stop DMA's machine.
  * @param  ch: Channel.
  * @retval None
  */
void ald_usb_dma_channel_stop(uint8_t ch)
{
	return;
}

/**
  * @brief  Gets flags of the interrupt.
  * @retval Flags of the interrupt.
  */
uint32_t ald_usb_dma_get_interrupt_flag(void)
{
	return;
}

/**
  * @brief  Gets the status of the error.
  * @param  ch: Channel.
  * @retval Status.
  */
uint32_t ald_usb_dma_get_channel_error(uint8_t ch)
{
	return 0;
}

/**
  * @brief  Clear the status of the error.
  * @param  ch: Channel.
  * @retval None
  */
void ald_usb_dma_clear_channel_error(uint8_t ch)
{
	return;
}

#endif
/**
  * @}
  */

/** @defgroup USB_Public_Functions_Group8 LPM functions
  * @brief LPM functions
  * @{
  */
/**
  * @brief  Gets status of remote wakeup.
  * @retval Status.
  */
uint32_t ald_usb_lpm_remote_wake_is_enable(void)
{
	return 1;
}

/**
  * @brief  Gets the link status
  * @retval Status
  */
uint32_t ald_usb_lpm_link_status_get(void)
{
	return 0;
}

/**
  * @brief  Gets the index of the endpoint.
  * @retval Index of the endpoint.
  */
uint32_t ald_usb_lpm_ep_get(void)
{
	return 0;
}

/**
  * @brief  Gets the status of the interrupt.
  * @retval Status.
  */
uint32_t ald_usb_lpm_int_status_get(void)
{
	return 0;
}

/**
  * @brief  Disable the LPM interrupt.
  * @retval None
  */
void ald_usb_lpm_int_disable(uint32_t ints)
{
	return;
}

/**
  * @brief  Enable the LPM interrupt.
  * @retval None
  */
void ald_usb_lpm_int_enable(uint32_t ints)
{
	return;
}

/**
  * @brief  Transmit a LPM transaction in host mode.
  * @param  addr: Address.
  * @param  ep_idx: Index of the endpoint.
  * @retval None
  */
void ald_usb_host_lpm_send(uint32_t addr, uint32_t ep_idx)
{
	return;
}

/**
  * @brief  Configure the LPM parameters in host mode.
  * @param  resume_time: Resume time.
  * @param  config: Parameters
  * @retval None
  */
void ald_usb_host_lpm_config(uint32_t resume_time, uint32_t config)
{
	return;
}

/**
  * @brief  Initiate a RESUME from the L1 state in host mode.
  * @retval None
  */
void ald_usb_host_lpm_resume(void)
{
	return;
}

/**
  * @brief  Enable remote wakeup in device mode.
  * @retval None
  */
void ald_usb_dev_lpm_remote_wake(void)
{
	return;
}

/**
  * @brief  Enable remote wakeup in device mode.
  * @retval None
  */
void ald_usb_dev_lpm_config(uint32_t config)
{
	return;
}

/**
  * @brief  Enable LPM in device mode.
  * @retval None
  */
void ald_usb_dev_lpm_enable(void)
{
	return;
}

/**
  * @brief  Disable LPM in device mode.
  * @retval None
  */
void ald_usb_dev_lpm_disable(void)
{
	return;
}

/**
  * @}
  */

/** @defgroup USB_Public_Functions_Group10 LPM functions
  * @brief USB SWVBUS control
  * @{
  */
/**
  * @brief  Selet the control mode of VBUS.
  * @param  sigctl: 0, indicates that use the hardware control
  * 		sigctl: 1, indicates that use the software control.
  * @retval Status.
  */
void ald_usb_swvbus_sigctl_set(uint8_t sigctl)
{
	if (sigctl == 1)
	{
		USB->SWVBUS |= 0x01 << 0;
	}
	else
	{
		USB->SWVBUS &= ~(0x01 << 0);
	}
	return;
}

/**
  * @brief  Selet the control mode of VBUS.
  * @param  None.
  * @retval Status: 0, indicates that use the hardware control
  * 		Status: 1, indicates that use the software control.
  */
uint8_t ald_usb_swvbus_sigctl_get(void)
{
	return (USB->SWVBUS & (0x01 << 0));
}

/**
  * @brief  Set session end threshold.
  * @param  thd: 0, indicates that lower than the session end threshold
  *         thd: 1, indicates that higher than the session end threshold.
  * @retval Status.
  */
void ald_usb_swvbus_sesendth_set(uint8_t thd)
{
	if (thd == 1)
	{
		USB->SWVBUS |= 0x01 << 1;
	}
	else
	{
		USB->SWVBUS &= ~(0x01 << 1);
	}
	return;
}

/**
  * @brief  Set session end threshold.
  * @param  None.
  * @retval Status: 0, indicates that lower than the session end threshold
  *         Status: 1, indicates that higher than the session end threshold.
  */
uint8_t ald_usb_swvbus_sesendth_get(void)
{
	return (USB->SWVBUS & (0x01 << 1));
}

/**
  * @brief  Set session valid threshold.
  * @param  thd: 0, indicates that lower than the session valid threshold
			thd: 1, indicates that higher than the session valid threshold.
  * @retval Status.
  */
void ald_usb_swvbus_sesvalth_set(uint8_t thd)
{
	if (thd == 1)
	{
		USB->SWVBUS |= 0x01 << 2;
	}
	else
	{
		USB->SWVBUS &= ~(0x01 << 2);
	}
	return;
}

/**
  * @brief  Set session valid threshold.
  * @param  None.
  * @retval Status: 0, indicates that lower than the session valid threshold
			Status: 1, indicates that higher than the session valid threshold.
  */
uint8_t ald_usb_swvbus_sesvalth_get(void)
{
	return (USB->SWVBUS & (0x01 << 2));
}

/**
  * @brief  Set VBUS valid threshold.
  * @param  thd: 0, indicates that lower than the vbus valid threshold
			thd: 1, indicates that higher than the vbus valid threshold.
  * @retval Status.
  */
void ald_usb_swvbus_valth_set(uint8_t thd)
{
	if (thd == 1)
	{
		USB->SWVBUS |= 0x01 << 3;
	}
	else
	{
		USB->SWVBUS &= ~(0x01 << 3);
	}
	return;
}

/**
  * @brief  Set VBUS valid threshold.
  * @param  None.
  * @retval Status: 0, indicates that lower than the vbus valid threshold
			Status:thd: 1, indicates that higher than the vbus valid threshold.
  */
uint8_t ald_usb_swvbus_valth_get(void)
{
	return (USB->SWVBUS & (0x01 << 3));
}

/**
  * @}
  */

/** @defgroup USB_Public_Functions_Group11 components initialization functions
  * @brief USB components initialization
  * @{
  */
/**
  * @brief  Initialize usb host components.
  * @retval None
  */
void ald_usb_host_components_init(void)
{
	RCU->AHBRST |= 0x4000;
	RCU->AHBRST &= 0xFFFFBFFF;
	
	/* Config EP0 */
	ald_usb_host_ep_config(ALD_USB_EP_0, 64, 0, 0, (ALD_USB_EP_MODE_CTRL | ALD_USB_EP_SPEED_FULL | ALD_USB_EP_HOST_OUT));
	
	/* Clear interrupts */
	
	
	/* Enable PHY power */
	ald_usb_host_pwr_enable();
	/* clear hnp session */
	ald_usb_otg_session_request(false);

	/* set vbus control mode and threshold value */
	ald_usb_swvbus_sigctl_set(1);
	ald_usb_swvbus_sesendth_set(1);
	ald_usb_swvbus_sesvalth_set(1);
	ald_usb_swvbus_valth_set(1);
	/* Pull down DP and DM */
	ald_usb_dppud_set(ALD_USB_DPDM_PUSH_DOWN);
	ald_usb_dmpud_set(ALD_USB_DPDM_PUSH_DOWN);
	/* software control CID */
	ald_usb_swcid_cidctrl(1);
	/* force to host mode */
	ald_usb_swcid_host(0);
	/* start host request */
	ald_usb_mode_host_req();
	/* Start hnp */
	ald_usb_otg_session_request(true);
	
	/* Clear interrupts */
	ald_usb_int_status_get();
	/* Init interrupts */
	ald_usb_int_enable(ALD_USB_INTCTRL_SESSION | ALD_USB_INTCTRL_DISCONNECT | ALD_USB_INTCTRL_CONNECT | ALD_USB_INTCTRL_SOF |
				ALD_USB_INTCTRL_BABBLE | ALD_USB_INTCTRL_RESUME);
	ald_usb_int_enable_ep(ALD_USB_INTEP_ALL);
	ald_usb_int_register();
	
	return;
}

/**
  * @brief  Initialize usb device components.
  * @retval None
  */
void ald_usb_device_components_init(void)
{
	ald_usb_otg_session_request(true);
	ald_usb_dppud_set(ALD_USB_DPDM_PUSH_UP);
	/* software control CID */
	ald_usb_swcid_cidctrl(1);
	/* force to dev mode */
	ald_usb_swcid_host(1);
	ald_usb_dev_suspend_enable();
	
	return;
}

/**
  * @}
  */

/**
  * @}
  */
#endif /* ALD_USB */
/**
  * @}
  */

/**
  * @}
  */
