/***********************************************************************
 * Filename    : fxx_fsusb.c
 * Description : fsusb driver source file
 * Author(s)   : xwl  
 * version     : V1.0
 * Modify date : 2022-08-01  
 ***********************************************************************/
#include "acm32g103_spl_conf.h"       

static uint16_t ep1_stall[2]= {0};    // EP1 stall״̬
static uint16_t ep2_stall[2]= {0};    // EP2 stall״̬
static uint16_t ep3_stall[2]= {0};    // EP3 stall״̬
static uint16_t ep4_stall[2]= {0};    // EP3 stall״̬     

//GPIO initiation, configure PA11 and PA12 as analog function 
void GPIO_Configuration(void)  
{	
    GPIO_InitTypeDef gpio_init;  
    
	gpio_init.Pin = GPIO_PIN_11 | GPIO_PIN_12;   
	gpio_init.Mode = GPIO_MODE_ANALOG;          
	gpio_init.Pull = GPIO_NOPULL;    
	gpio_init.Alternate = GPIO_FUNCTION_0;      
    gpio_init.Drive = GPIO_DRIVE_LEVEL3;  
	GPIO_Init(GPIOC, &gpio_init);       
}

// enable USB bus clock, PHY clock, configure gpio as analog function, enable interrupt 
uint32_t FSUSB_MSP_Init(void)  
{
    GPIO_InitTypeDef    GPIO_init_para;  

    __RCC_SYSCFG_CLK_ENABLE();  
    __RCC_USB_CLK_ENABLE();   
   
           
    System_USB_PHY_Config();     
   
    GPIO_Configuration();  
    
    NVIC_ClearPendingIRQ(USB_IRQn);  
	NVIC_EnableIRQ(USB_IRQn); 
    
    return 0;    
}   

// call FSUSB_MSP_Init to do hardware initiation, enable EP0-EP4, enable USB interrupts, and Pull up DP to notify Host 
uint32_t FSUSB_Init(void)  
{
    FSUSB_MSP_Init(); 

    Delay_Simple(10);    
    
    USBCTRL->WORKING_MODE = 0x04;  //disconnect usb/ reset USBC    
	Delay_Simple(3000);   
	USBCTRL->WORKING_MODE = 0x09 ;  //auto reset, fullspeed  
    
    USBCTRL->EPxCSR[0] |= 1<<8;	//enable EP0  
    USBCTRL->EPxCSR[1] |= 1<<8;	//enable EP1  
    USBCTRL->EPxCSR[2] |= 1<<8;	//enable EP2  
    USBCTRL->EPxCSR[3] |= 1<<8;	//enable EP3  
    USBCTRL->EPxCSR[4] |= 1<<8;	//enable EP4  
    USBCTRL->EPADDR_CFG = 0x4321;  
    
    USBINT->INT_EN = 0x92427;   // enable Reset,Resume,Suspend,setup, EP1/2/3/4 OUT interrupt	  
     
	USBCTRL->WORKING_MODE  |= (1<<6)|(1<<4);	//connect   
    
    return 0;   
}  

// get the length of data received in FIFO 
uint16_t FSUSB_Get_FIFO_Length(uint8_t ep_index)
{
	return USBCTRL->EPxCSR[ep_index]&0xff;             	
}

// clear data received in FIFO 
void FSUSB_Clear_FIFO(uint8_t ep_index)  
{
	USBCTRL->EPxCSR[ep_index] |= 1<<9;	  
} 

uint16_t FSUSB_Get_Stall_Status(uint8_t ep_index, uint8_t ep_dir) 
{
	switch(ep_index)
	{
		case USB_EP1: 
		{    
			if(ep_dir == EP_DIR_IN)		return ep1_stall[0];	  //in
			else			  			return ep1_stall[1];	  //out	    
		}
		case USB_EP2: 
		{    
			if(ep_dir == EP_DIR_IN)		return ep2_stall[0];	  //in
			else			  			return ep2_stall[1];	  //out	    
		}
		case USB_EP3: 
		{    
			if(ep_dir == EP_DIR_IN)	  	return ep3_stall[0];	  //in
			else			  			return ep3_stall[1];	  //out	    
		}
		case USB_EP4: 
		{    
			if(ep_dir == EP_DIR_IN)	  	return ep4_stall[0];	  //in
			else			  			return ep4_stall[1];	  //out	    
		}

		default:    	 	  return 0xff;
	}	
}

void FSUSB_Clear_Stall(uint8_t ep_index, uint8_t ep_dir)
{
	switch(ep_index)
	{
		case USB_EP1: 
		{    
			if(ep_dir == EP_DIR_IN)	 	ep1_stall[0]=0x0000;	  //in
			else			  			ep1_stall[1]=0x0000;	  //out	
			break;  
		}
		case USB_EP2: 
		{    
			if(ep_dir == EP_DIR_IN)	  	ep2_stall[0]=0x0000;	  //in
			else			  			ep2_stall[1]=0x0000;	  //out				  
			break;   
		}
		case USB_EP3: 
		{    
			if(ep_dir == EP_DIR_IN)	  	ep3_stall[0]=0x0000;	  //in
			else			  			ep3_stall[1]=0x0000;	  //out		  
			break;   
		}
		case USB_EP4: 
		{    
			if(ep_dir == EP_DIR_IN)	  	ep4_stall[0]=0x0000;	  //in
			else			  			ep4_stall[1]=0x0000;	  //out					  
			break;    
		}

		default: return;
	}

	USBCTRL->EPxCSR[ep_index] = 0x02100;		   //clear in/out toggle,stall,stall status
	USBCTRL->EPxCSR[ep_index] |= (1<<18)|(1<<15);  //enable change
//	flag_clear_stall=0;  		 
}



void FSUSB_Send_Stall(uint8_t ep_index, uint8_t ep_dir)
{
	switch(ep_index)
	{
		case USB_EP1: 
		{    
			if(ep_dir == EP_DIR_IN)	 	ep1_stall[0]=0x0001;	  //in
			else			  			ep1_stall[1]=0x0001;	  //out	
			break;  
		}
		case USB_EP2: 
		{    
			if(ep_dir == EP_DIR_IN)	  	ep2_stall[0]=0x0001;	  //in
			else			  			ep2_stall[1]=0x0001;	  //out				  
			break;   
		}
		case USB_EP3: 
		{    
			if(ep_dir == EP_DIR_IN)	  	ep3_stall[0]=0x0001;	  //in
			else			  			ep3_stall[1]=0x0001;	  //out		  
			break;   
		}
		case USB_EP4: 
		{    
			if(ep_dir == EP_DIR_IN)	  	ep4_stall[0]=0x0001;	  //in
			else			  			ep4_stall[1]=0x0001;	  //out					  
			break;    
		}

		default: return;
	}

	USBCTRL->EPxCSR[ep_index] |= (1<<12);    
}

/*================================================================
Function: read data from the EP received buffer
fifo_offset: EP received buffer offset, calculated in bytes 
ep_index: EP index
=====================================================================*/
void FSUSB_Read_EP_MEM8(uint8_t *dst, uint32_t length, uint32_t fifo_offset, uint8_t ep_index)
{
	uint8_t *src;

	src = (uint8_t *)(USB_BASE_ADDR+0x200+(ep_index<<6)+fifo_offset);       
	while(length--)
	{
		*dst++ = *src++;
	}		 
}

/*================================================================
Function: write data to the EP transmit buffer
fifo_offset: EP transmit buffer offset, calculated in bytes 
ep_index: EP index
=====================================================================*/
void FSUSB_Write_EP_MEM8(uint8_t *src, uint32_t length, uint32_t fifo_offset, uint8_t ep_index)
{

	uint8_t *dst;

	dst = (uint8_t *)(USB_BASE_ADDR+0x200+(ep_index<<6)+fifo_offset); 

	while(length--)
	{
		*dst++ = *src++;
	}
}

/*================================================================
Function: Transmit data written in EP FIFO. The data will actually send to host when receoved IN Token related with this EP. 
length: EP transmit length which should <= 64 bytes 
ep_index: EP index
=====================================================================*/
uint8_t FSUSB_Start_EP_Transfer(uint32_t length,uint8_t ep_index)
{
	uint8_t intoken_cnt;
	
	USBCTRL->EPxSENDBN[ep_index]= length;
	
	while(1)
	{
		// if a new out data packet received, return error to caller  
	    if( (USBINT->INT_STAT_RAW & MASK_EPX_OUT(ep_index)) && (USBINT->INT_STAT_RAW & MASK_EPX_ACK(ep_index)) )
		{
		    USBINT->INT_CLR  = MASK_EPX_OUT(ep_index);	
	        USBINT->INT_CLR  = MASK_EPX_ACK(ep_index);
			return ERROR_OUT_OUT;
		}
		// wait for IN token to start transfer 
        if(USBINT->INT_STAT_RAW & MASK_EPX_IN(ep_index) )
        {
		    USBINT->INT_CLR  = MASK_EPX_IN(ep_index);
			USBCTRL->WORKING_MODE  |= (1<<11);//return NAK when timeout
			USBCTRL->EPxCSR[ep_index] |= (1<<10);//data is ready for tx
			break;
        }			
	}
	

	while(1)
	{
		if( USBCTRL->EPxCSR[ep_index]&0x1000000 ) //received ACK from host 
		{
			USBINT->INT_CLR  = MASK_EPX_ACK(ep_index);
			USBINT->INT_CLR  = MASK_EPX_IN(ep_index);
			return 0;//pass
		}
		
		if(USBINT->INT_STAT_RAW & (1<<21) ) // timeout occurs when wait ACK 
		{
            USBINT->INT_CLR  = (1<<21);
			intoken_cnt = 4;
			while(intoken_cnt) // wait 3 SOF frame for bad signal, during this time, device will send NACK when IN token received  
			{               
                if(USBINT->INT_STAT_RAW & (1<<3))				
				{
				    intoken_cnt --;
					USBINT->INT_CLR  = (1<<3);
				}
			}
			USBINT->INT_CLR  = MASK_EPX_TIMEOUT(ep_index); // device recover to send data packet after IN token received  		
		}	
		
		if(USBINT->INT_STAT_RAW & MASK_EPX_OUT(ep_index))
		{
			return ERROR_IN_OUT; 
		}		
	}	
}

/*=============================================================================================================================
Function: call FSUSB_Write_EP_MEM8 to write data into transmit fifo, then call FSUSB_Start_EP_Transfer to send data to host. 
buffer: the pointer to data to be sent 
length: transmit length. If length is > 64 bytes, it will send in multi packets. 
ep_index: EP index
==============================================================================================================================*/
uint8_t FSUSB_Send_Data(uint8_t *buffer,uint32_t length,uint8_t ep_index)
{
	uint8_t ret;
	
	while(length>=EPX_MAX_PACKET_SIZE)
	{    		
		FSUSB_Write_EP_MEM8(buffer,EPX_MAX_PACKET_SIZE,0, ep_index);  		   
		ret = FSUSB_Start_EP_Transfer(EPX_MAX_PACKET_SIZE, ep_index);
		if(ret == ERROR_OUT_OUT)
		{
		    if( USBCTRL->EPxCSR[ep_index] & ( 1<< 19) )//Toggle error
		    {
		        USBCTRL->EPxCSR[ep_index] ^= (1<<17);  //out toggle want
			    USBCTRL->EPxCSR[ep_index] |= (1<<18);  //update want toggle;			 			             			
		    }
			USBCTRL->EPxCSR[ep_index] |= 1<<11; //set rx ready  	
            continue;	// received a same packet, has processed this packet, just fill respoonse to fifo and send it to host 		
		}
		else if(ret != 0)
		{
			return 1; // send data fail, exit with error code to let caller know 
		}
	 	length -= EPX_MAX_PACKET_SIZE;
	  	buffer += EPX_MAX_PACKET_SIZE;		
	}
	// remaining data, less than EPX_MAX_PACKET_SIZE  
	while(length>0)  
	{
		FSUSB_Write_EP_MEM8(buffer,length,0,ep_index);  	     
		ret = FSUSB_Start_EP_Transfer(length,ep_index);
		if(ret == ERROR_OUT_OUT)
		{
		    if( USBCTRL->EPxCSR[ep_index] & ( 1<< 19) )//Toggle error
		    {
		        USBCTRL->EPxCSR[ep_index] ^= (1<<17); //out toggle want
			    USBCTRL->EPxCSR[ep_index] |= (1<<18); //update want toggle;			 			             			
		    }
			USBCTRL->EPxCSR[ep_index] |= 1<<11; //set rx ready  
            continue;				
		}
		else if(ret != 0)
		{
			return 1; // send data fail, exit with error code to let caller know   
		}
	 	length -= length;
	  	buffer += length;
	}
	
	return 0;  
}

/*=============================================================================================================================
Function: call FSUSB_Read_EP_MEM8 to read data from EP FIFO.
buffer: the pointer to data to be written 
length: receive length. If length is > 64 bytes, it will receive in multi packets. 
ep_index: EP index
==============================================================================================================================*/
void FSUSB_Receive_Data(uint8_t *buffer,uint32_t length,uint8_t ep_index)  
{
	uint32_t len;

	while(length>0)	
	{			
		while(1)
		{
			// wait an out data packet and device has sent an ACK to HOST  
			if( (USBINT->INT_STAT_RAW & MASK_EPX_OUT(ep_index)) && (USBINT->INT_STAT_RAW & MASK_EPX_ACK(ep_index)) )
			{
				break;
			}
		}		
		USBINT->INT_CLR  = MASK_EPX_OUT(ep_index);	
	    USBINT->INT_CLR  = MASK_EPX_ACK(ep_index);		
		
		if( USBCTRL->EPxCSR[ep_index] & ( 1<< 19) )//Toggle error 
		{
		    USBCTRL->EPxCSR[ep_index] ^= (1<<17); //out toggle want
			USBCTRL->EPxCSR[ep_index] |= (1<<18); //update want toggle; 
			USBCTRL->EPxCSR[ep_index] |= 1<<11;   //set rx ready, wait for a new packet 
			continue;  //discard this packet          			
		}		
	
		len	=FSUSB_Get_FIFO_Length(ep_index);
		FSUSB_Read_EP_MEM8(buffer,len,0,ep_index);	
		USBCTRL->EPxCSR[ep_index] |= 1<<11; //set rx ready to wait next packet 

		length -= len;
		buffer += len;
	}		
} 


// Send Empty packet to host. In data phase/status phase, device may need send empty packet to host. 
void FSUSB_EP0_Send_Empty_Packet(void)
{
	FSUSB_Start_EP_Transfer(0,USB_EP0);    
} 

// Send stall packet to host indicates the request is not supported.  
void FSUSB_EP0_Send_Stall(void)
{
	USBCTRL->EPxCSR[0] |= 1<<12;
	while(!(USBCTRL->EPxCSR[0] &0x2000));
	USBCTRL->EPxCSR[0] |= 0x2000;    
}         

// enable or disable USB interrupts  
void FSUSB_Enable_Disable_INT(uint32_t Interrupt, uint32_t enable_disable)
{
    if (enable_disable)
    {
        USBINT->INT_EN |= Interrupt;   
    }
    else
    {
        USBINT->INT_EN &= (~Interrupt);      
    }
}








