Loading files
This file is pinned to the selected release. Download the original to open it in your design tool.
// ===================================================================================
// USB Handler for CH551, CH552 and CH554
// ===================================================================================
#include "ch554.h"
#include "usb_handler.h"
uint16_t SetupLen;
uint8_t SetupReq, UsbConfig;
__code uint8_t *pDescr;
// ===================================================================================
// Fast Copy Function
// ===================================================================================
// Copy descriptor *pDescr to Ep0 using double pointer
// (Thanks to Ralph Doncaster)
#pragma callee_saves USB_EP0_copyDescr
void USB_EP0_copyDescr(uint8_t len) {
len; // stop unreferenced argument warning
__asm
push ar7 ; r7 -> stack
mov r7, dpl ; r7 <- len
inc _XBUS_AUX ; select dptr1
mov dptr, #_EP0_buffer ; dptr1 <- EP0_buffer
dec _XBUS_AUX ; select dptr0
mov dpl, _pDescr ; dptr0 <- *pDescr
mov dph, (_pDescr + 1)
01$:
clr a ; acc <- #0
movc a, @a+dptr ; acc <- *pDescr[dptr0]
inc dptr ; inc dptr0
.DB 0xA5 ; acc -> EP0_buffer[dptr1] & inc dptr1
djnz r7, 01$ ; repeat len times
pop ar7 ; r7 <- stack
__endasm;
}
// ===================================================================================
// Endpoint Handler
// ===================================================================================
void USB_EP0_SETUP(void) {
uint8_t len = USB_RX_LEN;
if(len == (sizeof(USB_SETUP_REQ))) {
SetupLen = ((uint16_t)USB_setupBuf->wLengthH<<8) | (USB_setupBuf->wLengthL);
len = 0; // default is success and upload 0 length
SetupReq = USB_setupBuf->bRequest;
if( (USB_setupBuf->bRequestType & USB_REQ_TYP_MASK) != USB_REQ_TYP_STANDARD ) {
#ifdef USB_CTRL_NS_handler
len = USB_CTRL_NS_handler(); // non-standard request
#else
len = 0xFF; // command not supported
#endif
}
else { // standard request
switch(SetupReq) { // request ccfType
case USB_GET_DESCRIPTOR:
switch(USB_setupBuf->wValueH) {
case USB_DESCR_TYP_DEVICE: // Device Descriptor
pDescr = (uint8_t*)&DevDescr; // put descriptor into out buffer
len = sizeof(DevDescr); // descriptor length
break;
case USB_DESCR_TYP_CONFIG: // Configuration Descriptor
pDescr = (uint8_t*)&CfgDescr; // put descriptor into out buffer
len = sizeof(CfgDescr); // descriptor length
break;
case USB_DESCR_TYP_STRING:
switch(USB_setupBuf->wValueL) { // String Descriptor Index
case 0: pDescr = USB_STR_DESCR_i0; break;
case 1: pDescr = USB_STR_DESCR_i1; break;
case 2: pDescr = USB_STR_DESCR_i2; break;
case 3: pDescr = USB_STR_DESCR_i3; break;
#ifdef USB_STR_DESCR_i4
case 4: pDescr = USB_STR_DESCR_i4; break;
#endif
#ifdef USB_STR_DESCR_i5
case 5: pDescr = USB_STR_DESCR_i5; break;
#endif
#ifdef USB_STR_DESCR_i6
case 6: pDescr = USB_STR_DESCR_i6; break;
#endif
#ifdef USB_STR_DESCR_i7
case 7: pDescr = USB_STR_DESCR_i7; break;
#endif
#ifdef USB_STR_DESCR_i8
case 8: pDescr = USB_STR_DESCR_i8; break;
#endif
#ifdef USB_STR_DESCR_i9
case 9: pDescr = USB_STR_DESCR_i9; break;
#endif
default: pDescr = USB_STR_DESCR_ix; break;
}
len = pDescr[0]; // descriptor length
break;
#ifdef USB_REPORT_DESCR
case USB_DESCR_TYP_REPORT:
if(USB_setupBuf->wValueL == 0) {
pDescr = USB_REPORT_DESCR;
len = USB_REPORT_DESCR_LEN;
}
else len = 0xff;
break;
#endif
default:
len = 0xff; // unsupported descriptors or error
break;
}
if(len != 0xff) {
if(SetupLen > len) SetupLen = len; // limit length
len = SetupLen >= EP0_SIZE ? EP0_SIZE : SetupLen;
USB_EP0_copyDescr(len); // copy descriptor to Ep0
SetupLen -= len;
pDescr += len;
}
break;
case USB_SET_ADDRESS:
SetupLen = USB_setupBuf->wValueL; // save the assigned address
break;
case USB_GET_CONFIGURATION:
EP0_buffer[0] = UsbConfig;
if (SetupLen >= 1) len = 1;
break;
case USB_SET_CONFIGURATION:
UsbConfig = USB_setupBuf->wValueL;
break;
case USB_GET_INTERFACE:
break;
case USB_SET_INTERFACE:
break;
case USB_CLEAR_FEATURE:
if( (USB_setupBuf->bRequestType & 0x1F) == USB_REQ_RECIP_DEVICE ) {
if( ( ( (uint16_t)USB_setupBuf->wValueH << 8 ) | USB_setupBuf->wValueL ) == 0x01 ) {
if( ((uint8_t*)&CfgDescr)[7] & 0x20) {
// wake up
}
else len = 0xFF; // failed
}
else len = 0xFF; // failed
}
else if( (USB_setupBuf->bRequestType & USB_REQ_RECIP_MASK) == USB_REQ_RECIP_ENDP ) {
switch(USB_setupBuf->wIndexL) {
#ifdef EP4_IN_callback
case 0x84:
UEP4_CTRL = UEP4_CTRL & ~ ( bUEP_T_TOG | MASK_UEP_T_RES ) | UEP_T_RES_NAK;
break;
#endif
#ifdef EP4_OUT_callback
case 0x04:
UEP4_CTRL = UEP4_CTRL & ~ ( bUEP_R_TOG | MASK_UEP_R_RES ) | UEP_R_RES_ACK;
break;
#endif
#ifdef EP3_IN_callback
case 0x83:
UEP3_CTRL = UEP3_CTRL & ~ ( bUEP_T_TOG | MASK_UEP_T_RES ) | UEP_T_RES_NAK;
break;
#endif
#ifdef EP3_OUT_callback
case 0x03:
UEP3_CTRL = UEP3_CTRL & ~ ( bUEP_R_TOG | MASK_UEP_R_RES ) | UEP_R_RES_ACK;
break;
#endif
#ifdef EP2_IN_callback
case 0x82:
UEP2_CTRL = UEP2_CTRL & ~ ( bUEP_T_TOG | MASK_UEP_T_RES ) | UEP_T_RES_NAK;
break;
#endif
#ifdef EP2_OUT_callback
case 0x02:
UEP2_CTRL = UEP2_CTRL & ~ ( bUEP_R_TOG | MASK_UEP_R_RES ) | UEP_R_RES_ACK;
break;
#endif
#ifdef EP1_IN_callback
case 0x81:
UEP1_CTRL = UEP1_CTRL & ~ ( bUEP_T_TOG | MASK_UEP_T_RES ) | UEP_T_RES_NAK;
break;
#endif
#ifdef EP1_OUT_callback
case 0x01:
UEP1_CTRL = UEP1_CTRL & ~ ( bUEP_R_TOG | MASK_UEP_R_RES ) | UEP_R_RES_ACK;
break;
#endif
default:
len = 0xFF; // unsupported endpoint
break;
}
}
else len = 0xFF; // unsupported for non-endpoint
break;
case USB_SET_FEATURE:
if( (USB_setupBuf->bRequestType & 0x1F) == USB_REQ_RECIP_DEVICE ) {
if( ( ( (uint16_t)USB_setupBuf->wValueH << 8 ) | USB_setupBuf->wValueL ) == 0x01 ) {
if( !(((uint8_t*)&CfgDescr)[7] & 0x20) ) len = 0xFF; // failed
}
else len = 0xFF; // failed
}
else if( (USB_setupBuf->bRequestType & 0x1F) == USB_REQ_RECIP_ENDP ) {
if( ( ( (uint16_t)USB_setupBuf->wValueH << 8 ) | USB_setupBuf->wValueL ) == 0x00 ) {
switch( ( (uint16_t)USB_setupBuf->wIndexH << 8 ) | USB_setupBuf->wIndexL ) {
#ifdef EP4_IN_callback
case 0x84:
UEP4_CTRL = UEP4_CTRL & (~bUEP_T_TOG) | UEP_T_RES_STALL;// Set EP4 IN STALL
break;
#endif
#ifdef EP4_OUT_callback
case 0x04:
UEP4_CTRL = UEP4_CTRL & (~bUEP_R_TOG) | UEP_R_RES_STALL;// Set EP4 OUT Stall
break;
#endif
#ifdef EP3_IN_callback
case 0x83:
UEP3_CTRL = UEP3_CTRL & (~bUEP_T_TOG) | UEP_T_RES_STALL;// Set EP3 IN STALL
break;
#endif
#ifdef EP3_OUT_callback
case 0x03:
UEP3_CTRL = UEP3_CTRL & (~bUEP_R_TOG) | UEP_R_RES_STALL;// Set EP3 OUT Stall
break;
#endif
#ifdef EP2_IN_callback
case 0x82:
UEP2_CTRL = UEP2_CTRL & (~bUEP_T_TOG) | UEP_T_RES_STALL;// Set EP2 IN STALL
break;
#endif
#ifdef EP2_OUT_callback
case 0x02:
UEP2_CTRL = UEP2_CTRL & (~bUEP_R_TOG) | UEP_R_RES_STALL;// Set EP2 OUT Stall
break;
#endif
#ifdef EP1_IN_callback
case 0x81:
UEP1_CTRL = UEP1_CTRL & (~bUEP_T_TOG) | UEP_T_RES_STALL;// Set EP1 IN STALL
break;
#endif
#ifdef EP1_OUT_callback
case 0x01:
UEP1_CTRL = UEP1_CTRL & (~bUEP_R_TOG) | UEP_R_RES_STALL;// Set EP1 OUT Stall
break;
#endif
default:
len = 0xFF; // failed
break;
}
}
else len = 0xFF; // failed
}
else len = 0xFF; // failed
break;
case USB_GET_STATUS:
EP0_buffer[0] = 0x00;
EP0_buffer[1] = 0x00;
if(SetupLen >= 2) len = 2;
else len = SetupLen;
break;
default:
len = 0xff; // failed
break;
}
}
}
else len = 0xff; // wrong packet length
if(len == 0xff) {
SetupReq = 0xFF;
UEP0_CTRL = bUEP_R_TOG | bUEP_T_TOG | UEP_R_RES_STALL | UEP_T_RES_STALL;//STALL
}
else if(len <= EP0_SIZE) { // Tx data to host or send 0-length packet
UEP0_T_LEN = len;
UEP0_CTRL = bUEP_R_TOG | bUEP_T_TOG | UEP_R_RES_ACK | UEP_T_RES_ACK;// Expect DATA1, Answer ACK
}
else {
UEP0_T_LEN = 0; // Tx data to host or send 0-length packet
UEP0_CTRL = bUEP_R_TOG | bUEP_T_TOG | UEP_R_RES_ACK | UEP_T_RES_ACK;// Expect DATA1, Answer ACK
}
}
void USB_EP0_IN(void) {
uint8_t len;
switch(SetupReq) {
case USB_GET_DESCRIPTOR:
len = SetupLen >= EP0_SIZE ? EP0_SIZE : SetupLen;
USB_EP0_copyDescr(len); // copy descriptor to Ep0
SetupLen -= len;
pDescr += len;
UEP0_T_LEN = len;
UEP0_CTRL ^= bUEP_T_TOG; // switch between DATA0 and DATA1
break;
case USB_SET_ADDRESS:
USB_DEV_AD = USB_DEV_AD & bUDA_GP_BIT | SetupLen;
UEP0_CTRL = UEP_R_RES_ACK | UEP_T_RES_NAK;
break;
default:
UEP0_T_LEN = 0; // end of transaction
UEP0_CTRL = UEP_R_RES_ACK | UEP_T_RES_NAK;
break;
}
}
void USB_EP0_OUT(void) {
UEP0_T_LEN = 0;
UEP0_CTRL |= UEP_R_RES_ACK | UEP_T_RES_NAK; // respond Nak
}
// ===================================================================================
// USB Interrupt Service Routine
// ===================================================================================
#pragma save
#pragma nooverlay
void USB_interrupt(void) { // inline not really working in multiple files in SDCC
if(UIF_TRANSFER) {
// Dispatch to service functions
uint8_t callIndex = USB_INT_ST & MASK_UIS_ENDP;
switch (USB_INT_ST & MASK_UIS_TOKEN) {
case UIS_TOKEN_OUT:
switch (callIndex) {
case 0: EP0_OUT_callback(); break;
#ifdef EP1_OUT_callback
case 1: EP1_OUT_callback(); break;
#endif
#ifdef EP2_OUT_callback
case 2: EP2_OUT_callback(); break;
#endif
#ifdef EP3_OUT_callback
case 3: EP3_OUT_callback(); break;
#endif
#ifdef EP4_OUT_callback
case 4: EP4_OUT_callback(); break;
#endif
default: break;
}
break;
case UIS_TOKEN_SOF:
switch (callIndex) {
#ifdef EP0_SOF_callback
case 0: EP0_SOF_callback(); break;
#endif
#ifdef EP1_SOF_callback
case 1: EP1_SOF_callback(); break;
#endif
#ifdef EP2_SOF_callback
case 2: EP2_SOF_callback(); break;
#endif
#ifdef EP3_SOF_callback
case 3: EP3_SOF_callback(); break;
#endif
#ifdef EP4_SOF_callback
case 4: EP4_SOF_callback(); break;
#endif
default: break;
}
break;
case UIS_TOKEN_IN:
switch (callIndex) {
case 0: EP0_IN_callback(); break;
#ifdef EP1_IN_callback
case 1: EP1_IN_callback(); break;
#endif
#ifdef EP2_IN_callback
case 2: EP2_IN_callback(); break;
#endif
#ifdef EP3_IN_callback
case 3: EP3_IN_callback(); break;
#endif
#ifdef EP4_IN_callback
case 4: EP4_IN_callback(); break;
#endif
default: break;
}
break;
case UIS_TOKEN_SETUP:
switch (callIndex) {
case 0: EP0_SETUP_callback(); break;
#ifdef EP1_SETUP_callback
case 1: EP1_SETUP_callback(); break;
#endif
#ifdef EP2_SETUP_callback
case 2: EP2_SETUP_callback(); break;
#endif
#ifdef EP3_SETUP_callback
case 3: EP3_SETUP_callback(); break;
#endif
#ifdef EP4_SETUP_callback
case 4: EP4_SETUP_callback(); break;
#endif
default: break;
}
break;
}
UIF_TRANSFER = 0; // clear interrupt flag
}
// Device mode USB bus reset
if(UIF_BUS_RST) {
UEP0_CTRL = UEP_R_RES_ACK | UEP_T_RES_NAK;
#ifdef USB_RESET_handler
USB_RESET_handler(); // custom reset handler
#endif
USB_DEV_AD = 0x00;
UIF_SUSPEND = 0;
UIF_TRANSFER = 0;
UIF_BUS_RST = 0; // clear interrupt flag
}
// USB bus suspend / wake up
if (UIF_SUSPEND) {
UIF_SUSPEND = 0;
if ( !(USB_MIS_ST & bUMS_SUSPEND) ) USB_INT_FG = 0xFF; // clear interrupt flag
}
}
#pragma restore
// ===================================================================================
// USB Init Function
// ===================================================================================
void USB_init(void) {
USB_CTRL = bUC_DEV_PU_EN // USB internal pull-up enable
| bUC_INT_BUSY // Return NAK if USB INT flag not clear
| bUC_DMA_EN; // DMA enable
UDEV_CTRL = bUD_PD_DIS // Disable UDP/UDM pulldown resistor
| bUD_PORT_EN; // Enable port, full-speed
#if F_CPU < 12000000 // Set low-speed mode if SysFreq < 12 MHz
USB_CTRL |= bUC_LOW_SPEED;
UDEV_CTRL |= bUD_LOW_SPEED;
#endif
UEP0_DMA = EP0_ADDR; // EP0 data transfer address
UEP0_CTRL = UEP_R_RES_ACK // EP0 Manual flip, OUT transaction returns ACK
| UEP_T_RES_NAK; // EP0 IN transaction returns NAK
#ifdef USB_INIT_handler
USB_INIT_handler(); // Custom EP init handler
#endif
USB_INT_EN |= bUIE_SUSPEND // Enable device hang interrupt
| bUIE_TRANSFER // Enable USB transfer completion interrupt
| bUIE_BUS_RST; // Enable device mode USB bus reset interrupt
USB_INT_FG |= 0x1F; // Clear interrupt flag
IE_USB = 1; // Enable USB interrupt
EA = 1; // Enable global interrupts
UEP0_T_LEN = 0; // Must be zero at start
}
31a16d4c1This file is pinned to the selected release. Download the original to open it in your design tool.