857 lines
23 KiB
C
857 lines
23 KiB
C
#include "usbd.h"
|
|
#include "ch32fun.h"
|
|
#include "usb_config.h"
|
|
|
|
#include <stdio.h>
|
|
|
|
// clang-format off
|
|
#define ANYPRINTF (defined( FUNCONF_USE_DEBUGPRINTF ) && FUNCONF_USE_DEBUGPRINTF) || \
|
|
(defined( FUNCONF_USE_UARTPRINTF ) && FUNCONF_USE_UARTPRINTF) || \
|
|
(defined( FUNCONF_USE_USBPRINTF ) && FUNCONF_USE_USBPRINTF)
|
|
|
|
volatile uint8_t usb_debug = 0;
|
|
// Uncomment to enable debugging
|
|
// #define DEBUG
|
|
#define USBD_DEBUGPRINTF( ... ) {if ( usb_debug ) { printf( __VA_ARGS__ ); }}
|
|
#define min(a, b) (((a)<(b))?(a):(b))
|
|
// clang-format on
|
|
|
|
struct _USBState USBDCTX;
|
|
|
|
void USBD_InternalFinishSetup();
|
|
|
|
static inline void SetEPR_Status( const int ep, const uint16_t mask, const uint16_t value )
|
|
{
|
|
const uint16_t reg = USBD->EPR[ep];
|
|
const uint16_t current_stat = reg & mask;
|
|
// Which bits we need to toggle
|
|
const uint16_t toggle = current_stat ^ value;
|
|
|
|
// Conserve EA, TYPE and KIND (Non-toggle)
|
|
uint16_t write_val = ( reg & ( USBD_EPR_EA | USBD_EPR_EP_TYPE_MASK | USBD_EPR_EP_KIND ) );
|
|
write_val |= toggle;
|
|
// Write 1 has no effect for CTR_RX and CTR_TX (0 clears)
|
|
write_val |= ( USBD_EPR_CTR_RX | USBD_EPR_CTR_TX );
|
|
|
|
USBD->EPR[ep] = write_val;
|
|
}
|
|
|
|
int USBDSetup( void )
|
|
{
|
|
// Initialize USB pins to pull-down to avoid mis-detection
|
|
GPIOA->CFGHR &= ~( ( 0xf << ( 4 * 3 ) ) | ( 0xf << ( 4 * 4 ) ) );
|
|
GPIOA->CFGHR |= ( GPIO_Speed_2MHz | GPIO_CNF_OUT_PP ) << ( 4 * 3 ) | ( GPIO_Speed_2MHz | GPIO_CNF_OUT_PP )
|
|
<< ( 4 * 4 );
|
|
GPIOA->BSHR = ( 1 << ( 16 + 11 ) ) | ( 1 << ( 16 + 12 ) );
|
|
|
|
// We need 48MHz clock for USB
|
|
#if FUNCONF_SYSTEM_CORE_CLOCK == 240000000
|
|
RCC->CFGR0 = ( RCC->CFGR0 & ~RCC_USBPRE ) | RCC_USBPRE_DIV5;
|
|
#elif FUNCONF_SYSTEM_CORE_CLOCK == 144000000
|
|
RCC->CFGR0 = ( RCC->CFGR0 & ~RCC_USBPRE ) | RCC_USBPRE_DIV3;
|
|
#elif FUNCONF_SYSTEM_CORE_CLOCK == 96000000
|
|
RCC->CFGR0 = ( RCC->CFGR0 & ~RCC_USBPRE ) | RCC_USBPRE_DIV2;
|
|
#elif FUNCONF_SYSTEM_CORE_CLOCK == 48000000
|
|
RCC->CFGR0 = ( RCC->CFGR0 & ~RCC_USBPRE ) | RCC_USBPRE_DIV1;
|
|
#else
|
|
#error CH32V20x/30x need 240/144/96/48MHz main clock for USB to work
|
|
#endif
|
|
|
|
RCC->APB2PCENR |= RCC_AFIOEN | RCC_IOPAEN;
|
|
RCC->AHBPCENR = RCC_AHBPeriph_SRAM;
|
|
RCC->APB1PCENR |= RCC_USBEN;
|
|
|
|
#if defined( CH32V203F8 ) || defined( CH32V203F8U6 )
|
|
// Sometimes USB shares pins w/ SWD
|
|
// SWD must be disabled in such cases
|
|
Delay_Ms( 100 );
|
|
AFIO->PCFR1 |= AFIO_PCFR1_SWJ_CFG_DISABLE;
|
|
#endif
|
|
|
|
// Suspend & disable all interrupts
|
|
USBD->CNTR = USBD_FRES;
|
|
USBD->CNTR = 0;
|
|
|
|
// Delay a tad (Slightly more optimized)
|
|
for ( volatile int i = 0; i < 1000; i++ );
|
|
|
|
// Initialize required registers & packet buffer description table
|
|
USBD_InternalFinishSetup();
|
|
|
|
EXTEN->EXTEN_CTR |= EXTEN_USBD_PU_EN;
|
|
USBD->CNTR = USBD_CTRM | USBD_RESETM | USBD_SUSPM | USBD_WKUPM;
|
|
USBD->ISTR = 0;
|
|
NVIC_EnableIRQ( USB_LP_CAN1_RX0_IRQn );
|
|
|
|
return 0;
|
|
}
|
|
|
|
WEAK void USBD_InternalFinishSetup( void )
|
|
{
|
|
USBD->BTABLE = 0;
|
|
|
|
// EP0 has to be RTX for control transfers
|
|
USBDCTX.endpoints[0].mode = USBD_EP_MODE_RX | USBD_EP_MODE_TX;
|
|
|
|
#if FUSB_EP1_MODE
|
|
USBDCTX.endpoints[1].mode = FUSB_EP1_MODE;
|
|
#endif
|
|
|
|
#if FUSB_EP2_MODE
|
|
USBDCTX.endpoints[2].mode = FUSB_EP2_MODE;
|
|
#endif
|
|
|
|
#if FUSB_EP3_MODE
|
|
USBDCTX.endpoints[3].mode = FUSB_EP3_MODE;
|
|
#endif
|
|
|
|
#if FUSB_EP4_MODE
|
|
USBDCTX.endpoints[4].mode = FUSB_EP4_MODE;
|
|
#endif
|
|
|
|
#if FUSB_EP5_MODE
|
|
USBDCTX.endpoints[5].mode = FUSB_EP5_MODE;
|
|
#endif
|
|
|
|
#if FUSB_EP6_MODE
|
|
USBDCTX.endpoints[6].mode = FUSB_EP6_MODE;
|
|
#endif
|
|
|
|
#if FUSB_EP7_MODE
|
|
USBDCTX.endpoints[7].mode = FUSB_EP7_MODE;
|
|
#endif
|
|
|
|
// 64 bytes for each buffer
|
|
uint16_t pma = USBD_PMA_BASE; // running allocation cursor
|
|
for ( uint8_t ep = 0; ep < FUSB_MAX_EP_CNT; ++ep )
|
|
{
|
|
USBDCTX.usbd_ep_data_ptr[ep] = (uint32_t *)( CAN_USBD_SHARED_BASE + pma * 2 );
|
|
|
|
if ( USBDCTX.endpoints[ep].mode & USBD_EP_MODE_TX )
|
|
{
|
|
USBD_BDT->EP[ep].ADDn_TX = pma;
|
|
// Configured on the fly
|
|
USBD_BDT->EP[ep].COUNTn_TX = 0;
|
|
|
|
// If we have an RX node, we double-buffer
|
|
if ( !( USBDCTX.endpoints[ep].mode & USBD_EP_MODE_RX ) )
|
|
{
|
|
pma += USBD_PACKET_SIZE;
|
|
}
|
|
}
|
|
|
|
if ( USBDCTX.endpoints[ep].mode & USBD_EP_MODE_RX )
|
|
{
|
|
USBD_BDT->EP[ep].ADDn_RX = pma;
|
|
// USBD_BLSIZE: Each cnt is 32 bytes
|
|
USBD_BDT->EP[ep].COUNTn_RX = USBD_BLSIZE | ( ( USBD_PACKET_SIZE / 32 - 1 ) << 10 );
|
|
pma += USBD_PACKET_SIZE;
|
|
}
|
|
}
|
|
}
|
|
|
|
void USBDReset( void )
|
|
{
|
|
NVIC_DisableIRQ( USB_LP_CAN1_RX0_IRQn );
|
|
USBD->CNTR = USBD_FRES;
|
|
for ( volatile int i = 0; i < 1000; i++ );
|
|
USBD->CNTR = USBD_CTRM | USBD_RESETM | USBD_SUSPM | USBD_WKUPM;
|
|
USBD->ISTR = 0;
|
|
// Delay 100us for the USB to fully reset
|
|
// Not recognized otherwise
|
|
Delay_Us( 100 );
|
|
NVIC_EnableIRQ( USB_LP_CAN1_RX0_IRQn );
|
|
}
|
|
|
|
// It seems that even WCH doesn't use the high-priority usb interrupt
|
|
// maybe it's only for CAN?
|
|
#if FUSB_USE_HPE
|
|
// There is an issue with some registers apparently getting lost with HPE, just do it the slow way.
|
|
void USB_LP_CAN1_RX0_IRQHandler( void ) __attribute__( ( section( ".text.vector_handler" ) ) )
|
|
__attribute( ( interrupt ) );
|
|
#else
|
|
#if defined( FUSB_FROM_RAM ) && ( FUSB_FROM_RAM )
|
|
void USB_LP_CAN1_RX0_IRQHandler( void ) __USBFS_FUN_ATTRIBUTE __attribute( ( interrupt ) );
|
|
#else
|
|
void USB_LP_CAN1_RX0_IRQHandler( void ) __attribute__( ( section( ".text.vector_handler" ) ) )
|
|
__attribute( ( interrupt ) );
|
|
#endif
|
|
#endif
|
|
|
|
void USB_LP_CAN1_RX0_IRQHandler( void )
|
|
{
|
|
static uint8_t zeros[] = { 0, 0 }; // Macro to reply 0s
|
|
const uint32_t istr = USBD->ISTR;
|
|
int len = 0;
|
|
|
|
// Correct transfer
|
|
if ( istr & USBD_CTR )
|
|
{
|
|
const int ep = istr & USBD_EP_ID;
|
|
const int epr = USBD->EPR[ep];
|
|
|
|
#ifdef USB_DEBUG
|
|
if ( epr & USBD_CTR_RX && epr & USBD_CTR_TX )
|
|
{
|
|
printf( "Both RX & TX flags are set! Processing is too slow\n" );
|
|
}
|
|
#endif
|
|
|
|
if ( epr & USBD_CTR_RX )
|
|
{
|
|
// EP0 Setup Packet
|
|
if ( ep == 0 && epr & USBD_SETUP )
|
|
{
|
|
// Memory in USBD is stored in ranks of 16 bits (even though they can store
|
|
// 32 bits)
|
|
// What a weird design
|
|
const uint8_t USBD_request_type = USBDCTX.USBD_SetupReqType = USBDCTX.usbd_ep_data_ptr[0][0] & 0xFF;
|
|
const uint8_t USBD_request = USBDCTX.USBD_SetupReqCode = USBDCTX.usbd_ep_data_ptr[0][0] >> 8;
|
|
const uint32_t USBD_index = ( (uint32_t)USBDCTX.usbd_ep_data_ptr[0][2] );
|
|
const uint32_t USBD_indexValue = USBDCTX.USBD_IndexValue =
|
|
( ( (uint32_t)( USBDCTX.usbd_ep_data_ptr[0][2] ) ) << 16 ) | USBDCTX.usbd_ep_data_ptr[0][1];
|
|
const uint16_t USBD_length = USBDCTX.USBD_SetupReqLen = USBDCTX.usbd_ep_data_ptr[0][3];
|
|
|
|
// Copy to CTRL0BUFF
|
|
// Can be removed if CTRL0BUFF unused
|
|
for ( int i = 0; i < min( USBD_length, 64 ); ++i )
|
|
{
|
|
CTRL0BUFF[i] = ( USBDCTX.usbd_ep_data_ptr[0][i / 2] >> ( ( i & 1 ) << 3 ) ) & 0xFF;
|
|
}
|
|
|
|
// Request 0b00100001 0x0A = SET IDLE, can be ignored
|
|
if ( ( USBD_request_type & USBD_REQ_TYP_MASK ) == USBD_REQ_TYP_STANDARD )
|
|
{
|
|
// Standard setup
|
|
switch ( USBD_request )
|
|
{
|
|
case USB_GET_STATUS:
|
|
// Non remote wakeup and non self powered -> 0
|
|
USBDCTX.pCtrlPayloadPtr = zeros;
|
|
USBDCTX.USBD_SetupReqLen = 2;
|
|
break;
|
|
case USB_SET_ADDRESS:
|
|
USBDCTX.USBD_DevAddr = USBD_indexValue & 0xFF;
|
|
USBDCTX.USBD_SetupReqLen = USBD_SEND_ACK;
|
|
break;
|
|
case USB_GET_DESCRIPTOR:
|
|
const struct descriptor_list_struct *e = descriptor_list;
|
|
const struct descriptor_list_struct *e_end = e + DESCRIPTOR_LIST_ENTRIES;
|
|
for ( ; e != e_end; e++ )
|
|
{
|
|
if ( e->lIndexValue == (uint32_t)USBD_indexValue )
|
|
{
|
|
USBDCTX.pCtrlPayloadPtr = (uint8_t *)e->addr;
|
|
USBDCTX.USBD_SetupReqLen = min( e->length, USBD_length );
|
|
break;
|
|
}
|
|
}
|
|
if ( e == e_end ) goto stall;
|
|
break;
|
|
case USB_GET_CONFIG:
|
|
USBDCTX.pCtrlPayloadPtr = &USBDCTX.USBD_DevConfig;
|
|
USBDCTX.USBD_SetupReqLen = 1;
|
|
break;
|
|
case USB_SET_CONFIG:
|
|
USBDCTX.USBD_DevConfig = USBD_indexValue & 0xFF;
|
|
USBDCTX.USBD_DevEnumStatus = 0x01;
|
|
USBDCTX.USBD_SetupReqLen = USBD_SEND_ACK;
|
|
|
|
// Reset DTOG bits
|
|
for ( int ep = 1; ep < FUSB_MAX_EP_CNT; ep++ )
|
|
{
|
|
if ( USBD->EPR[ep] & USBD_DTOG_RX )
|
|
{
|
|
USBD->EPR[ep] = ( USBD->EPR[ep] & ( USBD_EA | USBD_EPKIND | USBD_EPTYPE ) ) |
|
|
USBD_CTR_TX | USBD_CTR_RX | USBD_DTOG_RX;
|
|
}
|
|
if ( USBD->EPR[ep] & USBD_DTOG_TX )
|
|
{
|
|
USBD->EPR[ep] = ( USBD->EPR[ep] & ( USBD_EA | USBD_EPKIND | USBD_EPTYPE ) ) |
|
|
USBD_CTR_TX | USBD_CTR_RX | USBD_DTOG_TX;
|
|
}
|
|
}
|
|
break;
|
|
case USB_GET_INTERFACE:
|
|
USBDCTX.pCtrlPayloadPtr = zeros;
|
|
USBDCTX.USBD_SetupReqLen = 1;
|
|
break;
|
|
case USB_CLEAR_FEATURE:
|
|
#if FUSB_SUPPORTS_SLEEP
|
|
if ( ( USBD_request_type & USB_REQ_RECIP_MASK ) == USB_REQ_RECIP_DEVICE )
|
|
{
|
|
/* clear one device feature */
|
|
if ( (uint8_t)( USBD_indexValue & 0xFF ) == USB_REQ_FEAT_REMOTE_WAKEUP )
|
|
{
|
|
/* clear usb sleep status, device not prepare to sleep */
|
|
USBDCTX.USBD_DevSleepStatus &= ~0x01;
|
|
}
|
|
else
|
|
{
|
|
goto stall;
|
|
}
|
|
}
|
|
else
|
|
#endif
|
|
if ( ( USBD_request_type & USB_REQ_RECIP_MASK ) == USB_REQ_RECIP_ENDP )
|
|
{
|
|
if ( (uint8_t)( USBD_indexValue & 0xFF ) == USB_REQ_FEAT_ENDP_HALT )
|
|
{
|
|
/* Clear End-point Feature */
|
|
if ( USBDCTX.endpoints[ep].mode )
|
|
{
|
|
if ( USBD_index & DEF_UEP_IN &&
|
|
( USBDCTX.endpoints[ep].mode & USBD_EP_MODE_TX ) )
|
|
SetEPR_Status( ep, USBD_EPR_STAT_TX_MASK, USBD_EPR_STAT_TX_NAK );
|
|
else if ( USBD_index & DEF_UEP_OUT &&
|
|
( USBDCTX.endpoints[ep].mode & USBD_EP_MODE_RX ) )
|
|
SetEPR_Status( ep, USBD_EPR_STAT_RX_MASK, USBD_EPR_STAT_RX_ACK );
|
|
else
|
|
goto stall;
|
|
}
|
|
else
|
|
{
|
|
goto stall;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
goto stall;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
goto stall;
|
|
}
|
|
break;
|
|
case USB_SET_FEATURE:
|
|
if ( ( USBD_request_type & USB_REQ_RECIP_MASK ) == USB_REQ_RECIP_DEVICE )
|
|
{
|
|
#if FUSB_SUPPORTS_SLEEP
|
|
/* Set Device Feature */
|
|
if ( (uint8_t)( USBD_indexValue & 0xFF ) == USB_REQ_FEAT_REMOTE_WAKEUP )
|
|
{
|
|
/* Set Wake-up flag, device prepare to sleep */
|
|
USBD_DevSleepStatus |= 0x01;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
goto stall;
|
|
}
|
|
}
|
|
else if ( ( USBD_request_type & USB_REQ_RECIP_MASK ) == USB_REQ_RECIP_ENDP )
|
|
{
|
|
/* Set Endpoint Feature */
|
|
if ( (uint8_t)( USBD_indexValue & 0xFF ) == USB_REQ_FEAT_ENDP_HALT )
|
|
{
|
|
if ( USBDCTX.endpoints[ep].mode )
|
|
{
|
|
if ( ( USBD_index & DEF_UEP_IN ) &&
|
|
( USBDCTX.endpoints[ep].mode & USBD_EP_MODE_TX ) )
|
|
SetEPR_Status( ep, USBD_EPR_STAT_TX_MASK, USBD_EPR_STAT_TX_STALL );
|
|
else if ( ( USBD_index & DEF_UEP_OUT ) &&
|
|
( USBDCTX.endpoints[ep].mode & USBD_EP_MODE_RX ) )
|
|
SetEPR_Status( ep, USBD_EPR_STAT_RX_MASK, USBD_EPR_STAT_RX_STALL );
|
|
else
|
|
goto stall;
|
|
}
|
|
}
|
|
else
|
|
goto stall;
|
|
}
|
|
else
|
|
goto stall;
|
|
break;
|
|
|
|
default: goto stall;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// EP0 Setup non-standard
|
|
#if FUSB_HID_INTERFACES > 0 || FUSB_USER_HANDLERS
|
|
#if FUSB_HID_USER_REPORTS
|
|
const uint16_t packet_len = USBD_BDT->EP[ep].COUNTn_RX & 0x3FF;
|
|
uint8_t buff[USBD_PACKET_SIZE];
|
|
|
|
for ( int i = 0; i < packet_len; ++i )
|
|
{
|
|
buff[i] = ( USBDCTX.usbd_ep_data_ptr[ep][i / 2] >> ( ( i & 1 ) << 3 ) ) & 0xFF;
|
|
}
|
|
#endif
|
|
|
|
len = 0;
|
|
switch ( USBD_request )
|
|
{
|
|
#if FUSB_HID_INTERFACES > 0
|
|
case HID_SET_REPORT:
|
|
#if FUSB_HID_USER_REPORTS
|
|
len = HandleHidUserSetReportSetup( &USBDCTX, (tusb_control_request_t *)buff );
|
|
if ( len < 0 ) goto stall;
|
|
USBDCTX.USBD_SetupReqLen = 0;
|
|
break;
|
|
|
|
case HID_GET_REPORT:
|
|
len = HandleHidUserGetReportSetup( &USBDCTX, (tusb_control_request_t *)buff );
|
|
if ( len < 0 ) goto stall;
|
|
if ( len == 0 )
|
|
{
|
|
USBDCTX.USBD_SetupReqLen = USBD_SEND_ACK;
|
|
break;
|
|
}
|
|
|
|
// len > 0:
|
|
// We would like to send something it seems
|
|
USBDCTX.USBD_SetupReqLen = len;
|
|
len = min( USBD_PACKET_SIZE, len );
|
|
|
|
// If HandleHidUserGetReportSetup hasn't provided data to send,
|
|
// we ask HandleHidUserReportDataIn for data
|
|
if ( !USBDCTX.pCtrlPayloadPtr )
|
|
{
|
|
len = HandleHidUserReportDataIn( &USBDCTX, buff, len );
|
|
USBDCTX.pCtrlPayloadPtr = buff;
|
|
}
|
|
|
|
#endif
|
|
break;
|
|
case HID_SET_IDLE:
|
|
if ( USBD_index < FUSB_HID_INTERFACES )
|
|
USBDCTX.USBD_HidIdle[USBD_index] = (uint8_t)( USBD_indexValue >> 8 );
|
|
|
|
// ACK
|
|
USBDCTX.USBD_SetupReqLen = USBD_SEND_ACK;
|
|
break;
|
|
case HID_SET_PROTOCOL:
|
|
if ( USBD_index < FUSB_HID_INTERFACES )
|
|
USBDCTX.USBD_HidProtocol[USBD_index] = (uint8_t)USBD_indexValue;
|
|
USBDCTX.USBD_SetupReqLen = USBD_SEND_ACK;
|
|
break;
|
|
case HID_GET_IDLE:
|
|
if ( USBD_index < FUSB_HID_INTERFACES )
|
|
{
|
|
USBDCTX.pCtrlPayloadPtr = &USBDCTX.USBD_HidIdle[USBD_index];
|
|
USBDCTX.USBD_SetupReqLen = 1;
|
|
}
|
|
break;
|
|
|
|
case HID_GET_PROTOCOL:
|
|
if ( USBD_index < FUSB_HID_INTERFACES )
|
|
{
|
|
USBDCTX.pCtrlPayloadPtr = &USBDCTX.USBD_HidProtocol[USBD_index];
|
|
USBDCTX.USBD_SetupReqLen = 1;
|
|
}
|
|
break;
|
|
#endif
|
|
default:
|
|
#if FUSB_USER_HANDLERS
|
|
len = HandleSetupCustom( &USBDCTX, USBD_request );
|
|
|
|
if ( len )
|
|
{
|
|
if ( len < 0 )
|
|
{
|
|
// ACK
|
|
USBDCTX.USBD_SetupReqLen = USBD_SEND_ACK;
|
|
}
|
|
else
|
|
{
|
|
USBDCTX.USBD_SetupReqLen = min( len, USBD_length );
|
|
}
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
goto stall;
|
|
}
|
|
break;
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// OUT RX packet
|
|
const uint16_t packet_len = USBD_BDT->EP[ep].COUNTn_RX & 0x3FF;
|
|
|
|
#if FUSB_HID_USER_REPORTS || FUSB_USER_HANDLERS
|
|
uint8_t buff[USBD_PACKET_SIZE];
|
|
|
|
for ( int i = 0; i < packet_len; ++i )
|
|
{
|
|
buff[i] = ( USBDCTX.usbd_ep_data_ptr[ep][i / 2] >> ( ( i & 1 ) << 3 ) ) & 0xFF;
|
|
}
|
|
#endif
|
|
#if FUSB_HID_USER_REPORTS
|
|
if ( USBDCTX.USBD_SetupReqCode == HID_SET_REPORT )
|
|
{
|
|
HandleHidUserReportDataOut( &USBDCTX, buff, len );
|
|
}
|
|
#endif
|
|
#if FUSB_USER_HANDLERS
|
|
if ( USBDCTX.USBD_SetupReqCode != HID_SET_REPORT )
|
|
{
|
|
HandleDataOut( &USBDCTX, ep, buff, packet_len );
|
|
}
|
|
#endif
|
|
#if FUSB_HID_USER_REPORTS
|
|
// Transaction finished
|
|
if ( USBDCTX.USBD_SetupReqLen == 0 )
|
|
{
|
|
if ( USBDCTX.USBD_SetupReqCode == HID_SET_REPORT ) HandleHidUserReportOutComplete( &USBDCTX );
|
|
}
|
|
#endif
|
|
|
|
if ( ep == 0 && ( USBDCTX.USBD_SetupReqLen == 0 || packet_len != 0 ) )
|
|
{
|
|
USBDCTX.USBD_SetupReqLen = USBD_SEND_ACK;
|
|
}
|
|
|
|
SetEPR_Status( ep, USBD_EPR_STAT_RX_MASK, USBD_EPR_STAT_RX_ACK );
|
|
}
|
|
|
|
// Clear RX (Toggle, 1 conserves bit)
|
|
USBD->EPR[ep] = ( USBD->EPR[ep] & ( USBD_EA | USBD_EPKIND | USBD_EPTYPE ) ) | USBD_CTR_TX;
|
|
}
|
|
|
|
// length < sizeof -> Return start
|
|
// length = sizeof -> Return all
|
|
// length > sizeof -> Return partial then 0
|
|
|
|
// 1st load for RX and auto-reload for TX
|
|
// We need to send something:
|
|
// - Set tx_buf and tx_pending to non-null and set TX to ACK
|
|
// - This piece of code shall handle loading to buffer
|
|
// - TX part shall start next transaction if we need to
|
|
// TX detects if the operation should be continued by checking
|
|
// if the tx_buf is null
|
|
|
|
// We are always handling either a TX or a RX transaction
|
|
// This is only for the control endpoint
|
|
if ( ep == 0 )
|
|
{
|
|
if ( USBDCTX.USBD_SetupReqLen == 0 )
|
|
{
|
|
USBDCTX.pCtrlPayloadPtr = NULL;
|
|
// Enable recieving data
|
|
SetEPR_Status( 0, USBD_EPR_STAT_RX_MASK, USBD_EPR_STAT_RX_ACK );
|
|
}
|
|
else
|
|
{
|
|
// USBD_SEND_ACK is special variable to say 0 byte tx
|
|
if ( USBDCTX.USBD_SetupReqLen == USBD_SEND_ACK )
|
|
{
|
|
USBDCTX.USBD_SetupReqLen = 0;
|
|
}
|
|
|
|
const uint16_t tx_len = min( USBDCTX.USBD_SetupReqLen, USBD_PACKET_SIZE );
|
|
// Overreads but eh
|
|
for ( int i = 0; i < tx_len; i += 2 )
|
|
{
|
|
USBDCTX.usbd_ep_data_ptr[0][i / 2] = *( (const uint16_t *)( USBDCTX.pCtrlPayloadPtr + i ) );
|
|
}
|
|
USBD_BDT->EP[0].COUNTn_TX = tx_len;
|
|
USBDCTX.pCtrlPayloadPtr += tx_len;
|
|
USBDCTX.USBD_SetupReqLen -= tx_len;
|
|
SetEPR_Status( 0, USBD_EPR_STAT_TX_MASK, USBD_EPR_STAT_TX_ACK );
|
|
}
|
|
}
|
|
|
|
// IN request
|
|
if ( epr & USBD_CTR_TX )
|
|
{
|
|
if ( ep == 0 )
|
|
{
|
|
if ( USBDCTX.USBD_SetupReqCode == USB_SET_ADDRESS )
|
|
{
|
|
USBD->DADDR = 0x80 | USBDCTX.USBD_DevAddr;
|
|
USBDCTX.USBD_DevAddr = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Send data for non-zero endpoint
|
|
#if FUSB_USER_HANDLERS
|
|
len = HandleInRequest( &USBDCTX, ep, USBDCTX.endpoints[ep].in, 0 );
|
|
#endif
|
|
|
|
if ( len )
|
|
{
|
|
if ( len < 0 ) len = 0;
|
|
SetEPR_Status( ep, USBD_EPR_STAT_TX_MASK, USBD_EPR_STAT_TX_ACK );
|
|
}
|
|
else
|
|
{
|
|
SetEPR_Status( ep, USBD_EPR_STAT_RX_MASK, USBD_EPR_STAT_RX_NAK );
|
|
}
|
|
}
|
|
|
|
// Sent, no longer busy
|
|
USBDCTX.endpoints[ep].busy = 0;
|
|
|
|
// Clear TX (Toggle)
|
|
USBD->EPR[ep] = ( USBD->EPR[ep] & ( USBD_EA | USBD_EPKIND | USBD_EPTYPE ) ) | USBD_CTR_RX;
|
|
}
|
|
|
|
USBD->ISTR = ~USBD_CTR;
|
|
return;
|
|
|
|
stall:
|
|
SetEPR_Status( 0, USBD_EPR_STAT_TX_MASK, USBD_EPR_STAT_TX_STALL );
|
|
SetEPR_Status( 0, USBD_EPR_STAT_RX_MASK, USBD_EPR_STAT_RX_STALL );
|
|
USBD->EPR[ep] = ( epr & ( USBD_EPR_EA | USBD_EPR_EP_TYPE_MASK | USBD_EPR_EP_KIND ) );
|
|
}
|
|
|
|
if ( istr & USBD_RESET )
|
|
{
|
|
USBD->ISTR = ~USBD_RESET;
|
|
USBD->BTABLE = 0;
|
|
|
|
for ( uint8_t ep = 0; ep < FUSB_MAX_EP_CNT; ++ep )
|
|
{
|
|
USBD->EPR[ep] = ep;
|
|
|
|
// TODO: Add method to set EP type
|
|
// Currently the user must override the function
|
|
// It seems that using TYPE_BULK is fine for all types though
|
|
SetEPR_Status( ep, USBD_EPR_EP_TYPE_MASK, USBD_EPR_EP_TYPE_BULK );
|
|
SetEPR_Status( ep, USBD_EPR_STAT_RX_MASK, USBD_EPR_STAT_RX_ACK );
|
|
SetEPR_Status( ep, USBD_EPR_STAT_TX_MASK, USBD_EPR_STAT_TX_NAK );
|
|
|
|
// Disable unused modes
|
|
if ( !( USBDCTX.endpoints[ep].mode & USBD_EP_MODE_RX ) )
|
|
{
|
|
SetEPR_Status( ep, USBD_EPR_STAT_RX_MASK, USBD_EPR_STAT_RX_DIS );
|
|
}
|
|
|
|
if ( !( USBDCTX.endpoints[ep].mode & USBD_EP_MODE_TX ) )
|
|
{
|
|
SetEPR_Status( ep, USBD_EPR_STAT_TX_MASK, USBD_EPR_STAT_TX_DIS );
|
|
}
|
|
USBDCTX.endpoints[ep].busy = 0;
|
|
}
|
|
|
|
// Override EP0 to control - mandatory
|
|
SetEPR_Status( 0, USBD_EPR_EP_TYPE_MASK, USBD_EPR_EP_TYPE_CTRL );
|
|
|
|
// Reset context
|
|
USBDCTX.USBD_DevConfig = 0;
|
|
USBDCTX.USBD_DevAddr = 0;
|
|
USBDCTX.USBD_DevSleepStatus = 0;
|
|
USBDCTX.USBD_DevEnumStatus = 0;
|
|
|
|
// Enable USB function (we default to address 0)
|
|
USBD->DADDR = USBD_EF;
|
|
return;
|
|
}
|
|
|
|
// If size is a concern, merge this with the ISTR set bellow
|
|
if ( istr & ( USBD_ERR | USBD_PMAOVR ) )
|
|
{
|
|
#ifdef DEBUG
|
|
printf( "ERROR! USB error\n" );
|
|
#endif
|
|
USBD->ISTR = ~( USBD_ERR | USBD_PMAOVR );
|
|
return;
|
|
}
|
|
|
|
// We don't need the rest of the events, so we just default to resetting them
|
|
USBD->ISTR = ~( USBD_ESOF | USBD_SOF | USBD_WKUP | USBD_SUSP );
|
|
}
|
|
|
|
static uint8_t EP_buffer[USBD_PACKET_SIZE];
|
|
|
|
uint8_t *USBD_GetEPBufferIfAvailable( int endp )
|
|
{
|
|
if ( USBDCTX.endpoints[endp].busy ) return 0;
|
|
return EP_buffer;
|
|
}
|
|
|
|
int USBD_SendEndpoint( int endp, int len )
|
|
{
|
|
if ( USBDCTX.endpoints[endp].busy ) return -1;
|
|
if ( USBDCTX.USBD_SetupReqLen > 0 ) return -2;
|
|
|
|
for ( int i = 0; i < len; i += 2 )
|
|
{
|
|
USBDCTX.usbd_ep_data_ptr[endp][i / 2] = *( (const uint16_t *)( EP_buffer + i ) );
|
|
}
|
|
|
|
USBD_BDT->EP[endp].COUNTn_TX = len;
|
|
USBDCTX.endpoints[endp].busy = 1;
|
|
SetEPR_Status( endp, USBD_EPR_STAT_TX_MASK, USBD_EPR_STAT_TX_ACK );
|
|
|
|
return 0;
|
|
}
|
|
|
|
int USBD_SendEndpointNEW( int endp, uint8_t *data, int len, int copy )
|
|
{
|
|
if ( USBDCTX.endpoints[endp].busy ) return -1;
|
|
// This prevents sending while ep0 is receiving
|
|
if ( USBDCTX.USBD_SetupReqLen > 0 ) return USBDCTX.USBD_SetupReqLen;
|
|
|
|
// For FS compatability
|
|
USBDCTX.endpoints[endp].in = data;
|
|
|
|
for ( int i = 0; i + 1 < len; i += 2 )
|
|
{
|
|
USBDCTX.usbd_ep_data_ptr[endp][i / 2] = data[i] | ( (uint16_t)data[i + 1] << 8 );
|
|
}
|
|
if ( len % 2 != 0 )
|
|
{
|
|
USBDCTX.usbd_ep_data_ptr[endp][len / 2] = data[len - 1];
|
|
}
|
|
|
|
USBD_BDT->EP[endp].COUNTn_TX = len;
|
|
USBDCTX.endpoints[endp].busy = 1;
|
|
SetEPR_Status( endp, USBD_EPR_STAT_TX_MASK, USBD_EPR_STAT_TX_ACK );
|
|
|
|
return 0;
|
|
}
|
|
|
|
int USBD_SendACK( int endp, int tx )
|
|
{
|
|
if ( tx )
|
|
{
|
|
SetEPR_Status( endp, USBD_EPR_STAT_TX_MASK, USBD_EPR_STAT_TX_ACK );
|
|
}
|
|
else
|
|
{
|
|
SetEPR_Status( endp, USBD_EPR_STAT_RX_MASK, USBD_EPR_STAT_RX_ACK );
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int USBD_SendNAK( int endp, int tx )
|
|
{
|
|
if ( tx )
|
|
{
|
|
SetEPR_Status( endp, USBD_EPR_STAT_TX_MASK, USBD_EPR_STAT_TX_NAK );
|
|
}
|
|
else
|
|
{
|
|
SetEPR_Status( endp, USBD_EPR_STAT_RX_MASK, USBD_EPR_STAT_RX_NAK );
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#if defined( FUNCONF_USE_USBPRINTF ) && FUNCONF_USE_USBPRINTF
|
|
uint8_t usb_inputbuffer[USBD_PACKET_SIZE];
|
|
int usb_inbuf_idx;
|
|
|
|
WEAK int HandleInRequest( struct _USBState *ctx, int endp, uint8_t *data, int len )
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
WEAK void HandleDataOut( struct _USBState *ctx, int endp, uint8_t *data, int len )
|
|
{
|
|
if ( endp == 0 )
|
|
{
|
|
ctx->USBD_SetupReqLen = 0; // To ACK
|
|
}
|
|
else if ( endp == 2 )
|
|
{
|
|
// discard oldest if polling is too slow
|
|
int headroom = ( sizeof( usb_inputbuffer ) - usb_inbuf_idx ) - len;
|
|
if ( headroom < 0 )
|
|
{
|
|
// not enough space left, free up some
|
|
int offset = -headroom;
|
|
for ( int i = offset; i < sizeof( usb_inputbuffer ); i++ )
|
|
{
|
|
usb_inputbuffer[i - offset] = usb_inputbuffer[i];
|
|
}
|
|
usb_inbuf_idx -= offset;
|
|
}
|
|
|
|
for ( int i = 0; i < len; i++ )
|
|
{
|
|
usb_inputbuffer[usb_inbuf_idx++] = data[i];
|
|
}
|
|
}
|
|
}
|
|
|
|
void poll_input()
|
|
{
|
|
if ( usb_inbuf_idx )
|
|
{
|
|
HandleUSBInput( usb_inbuf_idx, usb_inputbuffer );
|
|
usb_inbuf_idx = 0;
|
|
}
|
|
}
|
|
|
|
WEAK int HandleSetupCustom( struct _USBState *ctx, int setup_code )
|
|
{
|
|
int ret = -1;
|
|
if ( ctx->USBD_SetupReqType & USB_REQ_TYP_CLASS )
|
|
{
|
|
switch ( setup_code )
|
|
{
|
|
case CDC_SET_LINE_CODING:
|
|
case CDC_SET_LINE_CTLSTE:
|
|
case CDC_SEND_BREAK: ret = ( ctx->USBD_SetupReqLen ) ? ctx->USBD_SetupReqLen : -1; break;
|
|
case CDC_GET_LINE_CODING: ret = ctx->USBD_SetupReqLen; break;
|
|
default: ret = 0; break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ret = 0; // Go to STALL
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
#if defined( FUNCONF_USE_USBPRINTF ) && FUNCONF_USE_USBPRINTF
|
|
int USBFS_SendEndpointNEW( int endp, uint8_t *data, int len, int copy )
|
|
{
|
|
return USBD_SendEndpointNEW( endp, data, len, copy );
|
|
}
|
|
#endif
|
|
|
|
#if __STDC_VERSION__ >= 201112L || defined( __GNUC__ ) || defined( __clang__ )
|
|
|
|
// Very hacky way to ensure that we don't get a BDT overflow
|
|
// I miss C++...
|
|
// This should more logically be in usbd.h, but we get multiple objects
|
|
// if we do that
|
|
//
|
|
// No checking on compilers w/o static assert :(
|
|
static const char _number_of_endpoints[] = {
|
|
#if FUSB_EP1_MODE
|
|
FUSB_EP1_MODE,
|
|
#endif
|
|
#if FUSB_EP2_MODE
|
|
FUSB_EP2_MODE,
|
|
#endif
|
|
#if FUSB_EP3_MODE
|
|
FUSB_EP3_MODE,
|
|
#endif
|
|
#if FUSB_EP4_MODE
|
|
FUSB_EP4_MODE,
|
|
#endif
|
|
#if FUSB_EP5_MODE
|
|
FUSB_EP5_MODE,
|
|
#endif
|
|
#if FUSB_EP6_MODE
|
|
FUSB_EP6_MODE,
|
|
#endif
|
|
#if FUSB_EP7_MODE
|
|
FUSB_EP7_MODE,
|
|
#endif
|
|
};
|
|
|
|
#if __STDC_VERSION__ >= 202311L
|
|
static_assert( ( sizeof( _number_of_endpoints ) * USBD_PACKET_SIZE + USBD_PMA_BASE ) <= 512,
|
|
"USBD BDT overflow! Please use less endpoints, or make usbd packet size smaller" );
|
|
#elif defined( __GNUC__ ) || defined( __clang__ ) || __STDC_VERSION__ >= 201112L
|
|
_Static_assert( ( sizeof( _number_of_endpoints ) * USBD_PACKET_SIZE + USBD_PMA_BASE ) <= 512,
|
|
"USBD BDT overflow! Please use less endpoints, or make usbd packet size smaller" );
|
|
#endif
|
|
|
|
#endif
|