2025-12-17 15:55:25 +08:00

1389 lines
42 KiB
C

/*
* Kneron USBD API
*
* Copyright (C) s/2019/2020/ Kneron, Inc. All rights reserved.
*
*/
// #define USBD2_DBG
#include <stdlib.h>
#include <string.h>
#include "cmsis_os2.h"
#include "base.h"
#include "kdrv_scu_ext.h"
#include "kdrv_usbd2v.h"
#ifdef USBD2_DBG
#include "kmdw_console.h"
#endif
#ifdef USBD2_DBG
#define usbd2_dbg(__format__, ...) kmdw_printf(__format__, ##__VA_ARGS__)
#else
#define usbd2_dbg(__format__, ...)
#endif
// OTG Control Status Register (0x80)
#define REG_OTG_CSR 0x80
#define SPD_TYPE (BIT22 | BIT23)
#define VBUS_VLD_RO BIT19
#define B_SESS_END_RO BIT16
// OTG Interrupt Stauts Register (0x84)
// OTG Interrupt Enable Register (0x88)
#define REG_OTG_ISR 0x84
#define REG_OTG_IER 0x88
#define OTG_APLGRMV_RW1C BIT12
#define OTG_A_WAIT_CON_RW1C BIT11
#define OTG_OVC_RW1C BIT10
#define OTG_IDCHG_RW1C BIT9
#define OTG_RLCHG_RW1C BIT8
#define OTG_B_SESS_END_RW1C BIT6
#define OTG_A_VBUS_ERR_RW1C BIT5
#define OTG_A_SRP_DET_RW1C BIT4
#define OTG_B_SRP_DN_RW1C BIT0
// Global HC/OTG/DEV Interrupt Status Register (0xC0)
// Global Mask of HC/OTG/DEV Interrupt Register (0xC4)
#define REG_GLB_ISR 0xC0
#define REG_GLB_INT 0xC4
#define INT_POLARITY BIT3
#define OTG_INT BIT1
#define DEV_INT BIT0
// Device Main Control Register (0x100)
#define REG_DEV_CTL 0x100
// Device Address register (0x104)
#define REG_DEV_ADR 0x104
#define AFT_CONF BIT7
// Device Test Register (0x108)
#define REG_DEV_TST 0x108
#define TST_CLRFF BIT0
// Device SOF Mask Timer Register (0x110)
#define REG_DEV_SMT 0x110
// PHY Test Mode Selector Register (0x114)
#define REG_PHY_TST 0x114
#define TST_JSTA BIT0
// Device CX configuration adn FIFO empty status (0x120)
#define REG_CXCFE 0x120
#define F_EMP_0 BIT8
#define CX_CLR BIT3
#define CX_STL BIT2
#define CX_DONE BIT0
// Device Idle Counter Register (0x124)
#define REG_DEV_ICR 0x124
// Group total interrupt mask (0x130)
// Group total interrupt status (0x140)
#define REG_DEV_MIGR 0x130
#define REG_DEV_IGR 0x140
#define GX_INT_G3_RO BIT3
#define GX_INT_G2_RO BIT2
#define GX_INT_G1_RO BIT1
#define GX_INT_G0_RO BIT0
// Group 0 interrupt mask (0x134)
// Group 0 interrupt status (0x144)
// control transfer
#define REG_DEV_MISG0 0x134
#define REG_DEV_ISG0 0x144
#define G0_CX_COMABT_INT_RW1C BIT5
#define G0_CX_COMFAIL_INT_RO BIT4
#define G0_CX_COMEND_INT_RO BIT3
#define G0_CX_OUT_INT_RO BIT2
#define G0_CX_IN_INT_RO BIT1
#define G0_CX_SETUP_INT_RO BIT0
// Group 1 interrupt mask (0x138)
// Group 1 interrupt status (0x148)
// FIFO interrupts
#define REG_DEV_MISG1 0x138
#define REG_DEV_ISG1 0x148
#define MF0_IN_INT BIT16
#define MF0_SPK_INT BIT1
#define MF0_OUT_INT BIT0
// Group 1 interrupts (0x148)
#define G1_F3_IN_INT_RO BIT19
#define G1_F2_IN_INT_RO BIT18
#define G1_F1_IN_INT_RO BIT17
#define G1_F0_IN_INT_RO BIT16
#define G1_F3_SPK_INT_RO BIT7
#define G1_F3_OUT_INT_RO BIT6
#define G1_F2_SPK_INT_RO BIT5
#define G1_F2_OUT_INT_RO BIT4
#define G1_F1_SPK_INT_RO BIT3
#define G1_F1_OUT_INT_RO BIT2
#define G1_F0_SPK_INT_RO BIT1
#define G1_F0_OUT_INT_RO BIT0
// Group 2 interrupt mask (0x13C)
// Group 2 interrupt source (0x14C)
#define REG_DEV_MISG2 0x13C
#define REG_DEV_ISG2 0x14C
#define G2_Dev_Wakeup_byVbus_RO BIT10
#define G2_Dev_Idle_RO BIT9
#define G2_DMA_ERROR_RW1C BIT8
#define G2_DMA_CMPLT_RW1C BIT7
#define G2_RX0BYTE_INT_RW1C BIT6
#define G2_TX0BYTE_INT_RW1C BIT5
#define G2_ISO_SEQ_ABORT_INT_RW1C BIT4
#define G2_ISO_SEQ_ERR_INT_RW1C BIT3
#define G2_RESM_INT_RW1C BIT2
#define G2_SUSP_INT_RW1C BIT1
#define G2_USBRST_INT_RW1C BIT0
// Devcie Receive Zero-Length Data Packet Register (0x150)
#define REG_DEV_RXZ 0x150
#define RX0BYTE_EP1 BIT0
// Device IN endpoint & MaxPacketSize (0x160 + 4(n-1))
#define REG_DEV_INMPS_1 0x160
#define TX0BYTE_IEPn BIT15
#define RSTG_IEPn BIT12
#define STL_IEPn BIT11
// Device IN endpoint & MaxPacketSize (0x180 + 4(n-1))
#define REG_DEV_OUTMPS_1 0x180
#define RSTG_OEPn BIT12
#define STL_IEPn BIT11
// Device Endpoint 1~4 Map Register (0x1A0)
#define REG_DEV_EPMAP0 0x1A0
// Device Endpoint 5~8 Map Register (0x1A4)
#define REG_DEV_EPMAP1 0x1A4
// Device FIFO Map Register (0x1A8)
#define REG_DEV_FMAP 0x1A8
// Device FIFO Configuration Register (0x1AC)
#define REG_DEV_FCFG 0x1AC
// Device FIFO Byte Count Register (0x1B0 + 4(fifo_no-1))
#define FFRST BIT12
#define BC_Fn 0x7ff
// DMA Target FIFO register (0x1C0)
#define REG_DMA_TFN 0x1C0
#define DMA_TARGET_ACC_CXF BIT4
#define DMA_TARGET_ACC_F3 BIT3
#define DMA_TARGET_ACC_F2 BIT2
#define DMA_TARGET_ACC_F1 BIT1
#define DMA_TARGET_ACC_F0 BIT0
#define DMA_TARGET_ACC_NONE 0x0
// DMA Controller Param 1 (0x1C8)
#define REG_DMA_CPS1 0x1C8
#define DMA_TYPE BIT1
#define DMA_START BIT0
// DMA Controller Param 2 (0x1CC)
#define REG_DMA_CPS2 0x1CC
// DMA Controller Param 3 (0x1D0)
// setup packet 8 bytes direct DMA read
#define REG_DMA_CPS3 0x1D0
#define MAX_ENP_NUM 4
#define FIFO_NUM 4 // we have 4 FIFOs, each has 1-KB
#define UsbRegRead(reg_offset) inw(USB_FOTG210_PA_BASE + reg_offset)
#define UsbRegWrite(reg_offset, val) outw(USB_FOTG210_PA_BASE + reg_offset, val)
#define UsbRegMaskedSet(reg_offset, val) masked_outw((USB_FOTG210_PA_BASE + reg_offset), (val), (val))
#define UsbRegMaskedClr(reg_offset, val) masked_outw((USB_FOTG210_PA_BASE + reg_offset), 0, (val))
#define VDMA_ONE_TXFER_SIZE (100 * 1024) // 100 KB
enum
{
CONFIG_DEFAULT_STATE = 0,
CONFIG_ADDRESS_STATE,
CONFIG_CONFIGURED_STATE,
};
// for FIFO_Ctrl_t:transferType
enum
{
TXFER_CONTROL = 0,
TXFER_ISO,
TXFER_BULK,
TXFER_INT,
};
// for SETUP packet request
enum
{
RESP_LATER = 1, /* busy now */
RESP_ACK, /* reqeust is done */
RESP_STALL, /* request is not supported */
};
typedef struct
{
uint8_t isTransferring;
uint8_t dir; // 0:OUT, 1:IN
uint32_t user_buf_len;
uint32_t received_length;
bool send_ZLP;
kdrv_status_t status;
} FIFO_context_t;
static FIFO_context_t _fifo_ctx[FIFO_NUM];
static int _enp_to_fifo[MAX_ENP_NUM + 1] = {-1};
static int _fifo_to_enp[FIFO_NUM] = {0};
static osEventFlagsId_t _evt_id;
static osSemaphoreId_t _enp_semaphore[MAX_ENP_NUM + 1] = {NULL};
static kdrv_usbd2v_device_descriptor_t *_dev_desc = NULL;
static kdrv_usbd2v_string_descriptor_t *_dev_str_desc;
static kdrv_usbd2v_link_status_callback_t _evt_cb;
static kdrv_usbd2v_user_control_callback_t _usr_cx_cb;
static uint32_t _config_state = CONFIG_DEFAULT_STATE;
static kdrv_usbd2v_link_status_t _link_status = USBD2_STATUS_DISCONNECTED;
#if 0
static bool _isInterrupt()
{
return (SCB->ICSR & SCB_ICSR_VECTACTIVE_Msk) != 0;
}
#endif
// for data SRAM, the address must be remapped
static uint32_t _dma_remap_addr(uint32_t addr)
{
uint32_t tmp;
if ((addr & (SdRAM_MEM_BASE)) == SdRAM_MEM_BASE)
{
tmp = ((addr) & (~0x10000000)) | 0x20000000;
return tmp;
}
return addr;
}
static void _set_link_status_detection(bool enable)
{
// use B_SEE_END for link status detection
if (enable)
{
UsbRegWrite(REG_OTG_ISR, OTG_B_SESS_END_RW1C);
UsbRegWrite(REG_OTG_IER, OTG_B_SESS_END_RW1C);
}
else
{
UsbRegWrite(REG_OTG_IER, 0x0);
}
}
static void _vdma_cx_write(uint32_t *addr, uint32_t len)
{
UsbRegWrite(0x304, _dma_remap_addr((uint32_t)addr));
UsbRegWrite(0x300, (len << 8) | BIT1 | BIT0);
}
static kdrv_status_t _reset_endpoint(int enp_no)
{
usbd2_dbg("%s() enp_no %d\n", __FUNCTION__, enp_no);
uint8_t fifo_no = _enp_to_fifo[enp_no];
UsbRegWrite(0x1B0 + 4 * fifo_no, FFRST);
// for IN endpoints
UsbRegMaskedSet(REG_DEV_INMPS_1 + 4 * (enp_no - 1), RSTG_IEPn);
UsbRegMaskedClr(REG_DEV_INMPS_1 + 4 * (enp_no - 1), RSTG_IEPn);
// for OUT endpoints
UsbRegMaskedSet(REG_DEV_OUTMPS_1 + 4 * (enp_no - 1), RSTG_OEPn);
UsbRegMaskedClr(REG_DEV_OUTMPS_1 + 4 * (enp_no - 1), RSTG_OEPn);
return KDRV_STATUS_OK;
}
static void _reset_all_endpoints()
{
usbd2_dbg("%s()\n", __FUNCTION__);
for (int enp_no = 1; enp_no <= MAX_ENP_NUM; enp_no++)
if (_enp_to_fifo[enp_no] >= 0)
_reset_endpoint(enp_no);
}
static int8_t _send_host_device_status()
{
uint8_t sData[2] = {0}; // self-power : 0, remote wakeup : 0
usbd2_dbg("[int] <Standard Request> send device status\n");
_vdma_cx_write((uint32_t *)sData, 2);
return RESP_LATER;
}
static int8_t _send_host_device_descriptor(kdrv_usbd2v_setup_packet_t *setup)
{
usbd2_dbg("[int] <Standard Request> send device descriptor\n");
uint16_t txLen = MIN(_dev_desc->bLength, setup->wLength);
_vdma_cx_write((uint32_t *)_dev_desc, txLen);
return RESP_LATER;
}
static int8_t _send_host_string_descriptor(kdrv_usbd2v_setup_packet_t *setup, uint8_t type)
{
usbd2_dbg("[int] <Standard Request> send string descriptor, type %d\n", type);
uint16_t txLen = 0;
uint32_t *buf = NULL;
if (type == 0) //language id
{
txLen = MIN(_dev_str_desc->bLength, setup->wLength);
buf = (uint32_t *)_dev_str_desc;
}
else if (type == 1 || type == 2 || type == 3) //iManufacturer,iProduct,iSerialNumber
{
txLen = MIN(_dev_str_desc->desc[type - 1]->bLength, setup->wLength);
buf = (uint32_t *)_dev_str_desc->desc[type - 1];
}
if (buf)
{
_vdma_cx_write((uint32_t *)buf, txLen);
return RESP_LATER;
}
else
return RESP_STALL;
}
static int8_t _send_host_configuration_descriptors(kdrv_usbd2v_setup_packet_t *setup)
{
uint32_t confIdx = setup->wValue & 0xFF;
usbd2_dbg("[int] <Standard Request> send configuration descriptor\n");
// some error checking
if (confIdx > 0)
return RESP_STALL;
kdrv_usbd2v_config_descriptor_t *conf_desc = _dev_desc->config;
uint16_t txLen = MIN(conf_desc->wTotalLength, setup->wLength);
// create a buffer for combining all sub-descriptors
static uint8_t *buf_desc = NULL;
if (!buf_desc)
{
buf_desc = malloc(conf_desc->wTotalLength);
// collect all sub-descriptos int one memory
uint8_t offset = 0;
memcpy(buf_desc, conf_desc, conf_desc->bLength);
offset += conf_desc->bLength;
kdrv_usbd2v_interface_descriptor_t *intf_desc = conf_desc->interface;
memcpy(buf_desc + offset, intf_desc, intf_desc->bLength);
offset += intf_desc->bLength;
for (int j = 0; j < intf_desc->bNumEndpoints; j++)
{
kdrv_usbd2v_endpoint_descriptor_t *endp_desc = intf_desc->endpoint[j];
memcpy(buf_desc + offset, endp_desc, endp_desc->bLength);
offset += endp_desc->bLength;
}
}
_vdma_cx_write((uint32_t *)buf_desc, txLen);
return RESP_LATER;
}
static void _init_fifo_configurations(kdrv_usbd2v_interface_descriptor_t *intf)
{
uint32_t fifo_map_val = 0x0; // for 0x1A8
uint32_t endp_map0_val = 0x0; // for 0x1A0
uint32_t fifo_config_val = 0x0; // for 0x1AC
int fifo_no = 0;
int fifo_add = 1;
for (int i = 0; i < MAX_ENP_NUM; i++)
{
_enp_to_fifo[i + 1] = -1;
_fifo_to_enp[i] = 0;
}
// here assume endpoint number order is ascending
for (int i = 0; i < intf->bNumEndpoints; i++)
{
uint8_t bEndpointAddress = intf->endpoint[i]->bEndpointAddress;
uint8_t bmAttributes = intf->endpoint[i]->bmAttributes;
uint16_t wMaxPacketSize = intf->endpoint[i]->wMaxPacketSize;
uint8_t isIn = !!(bEndpointAddress & 0x80);
uint8_t enp_no = bEndpointAddress & 0xF; // retrieve endpoint no without direction
uint32_t bitfield;
// set FIFO's direction and corresponding endpoint no.
bitfield = (isIn << 4) | enp_no;
fifo_map_val |= (bitfield << (fifo_no * 8));
// set endpoint's FIFO no.
// assume enp_no <= MAX_ENP_NUM
bitfield = isIn ? fifo_no : (fifo_no << 4);
endp_map0_val |= (bitfield << ((enp_no - 1) * 8));
// enable the corresponding FIFO and set transfer type
bitfield = (BIT5 | (bmAttributes & 0x3));
if (intf->bNumEndpoints <= 2 ||
(intf->bNumEndpoints == 3 && i == 0))
{
bitfield |= BIT2;
fifo_add = 2;
}
else
fifo_add = 1;
fifo_config_val |= bitfield << (fifo_no * 8);
// set max packet size & reset toggle bit
if (isIn)
{
// for IN endpoints
UsbRegWrite(REG_DEV_INMPS_1 + 4 * (enp_no - 1), wMaxPacketSize & 0x7ff);
UsbRegMaskedSet(REG_DEV_INMPS_1 + 4 * (enp_no - 1), RSTG_IEPn);
UsbRegMaskedClr(REG_DEV_INMPS_1 + 4 * (enp_no - 1), RSTG_IEPn);
}
else
{
// for OUT endpoints
UsbRegWrite(REG_DEV_OUTMPS_1 + 4 * (enp_no - 1), wMaxPacketSize & 0x7ff);
UsbRegMaskedSet(REG_DEV_OUTMPS_1 + 4 * (enp_no - 1), RSTG_OEPn);
UsbRegMaskedClr(REG_DEV_OUTMPS_1 + 4 * (enp_no - 1), RSTG_OEPn);
}
_enp_to_fifo[enp_no] = fifo_no;
_fifo_to_enp[fifo_no] = enp_no;
fifo_no += fifo_add;
}
// clear all FIFO
UsbRegMaskedSet(REG_DEV_TST, TST_CLRFF);
// set FIFO interrupt mask
// UsbRegWrite(REG_DEV_MISG1, fifo_int_mask);
// endpoint map 0
UsbRegWrite(REG_DEV_EPMAP0, endp_map0_val);
#ifdef USBD2_DBG
{
uint32_t reg_value = UsbRegRead(REG_DEV_EPMAP0);
usbd2_dbg("\nsetting endpoint -> FIFO map register [0x%03X]:\n", REG_DEV_EPMAP0);
for (int i = 0; i < 4; i++)
{
uint32_t temp = reg_value >> (i * 8);
usbd2_dbg("IN enp_%d maps to FIFO_%d\n", i + 1, temp & 0x3);
}
for (int i = 0; i < 4; i++)
{
uint32_t temp = reg_value >> (i * 8) + 4;
usbd2_dbg("OUT enp_%d maps to FIFO_%d\n", i + 1, temp & 0x3);
}
}
#endif
// fifo map
UsbRegWrite(REG_DEV_FMAP, fifo_map_val);
#ifdef USBD2_DBG
{
uint32_t reg_value = UsbRegRead(REG_DEV_FMAP);
usbd2_dbg("\nsetting FIFO -> endpoint map register [0x%03X]:\n", REG_DEV_FMAP);
for (int i = 0; i < 4; i++)
{
uint32_t temp = reg_value >> (i * 8);
char temp_str[4];
switch ((temp >> 4) & 0x3)
{
case 0:
strcpy(temp_str, "Out");
break;
case 1:
strcpy(temp_str, "In");
break;
case 2:
strcpy(temp_str, "Bi");
break;
}
usbd2_dbg("FIFO_%d map to %s enp_%d\n", i, temp_str, temp & 0xF);
}
}
#endif
// fifo config / enable
UsbRegWrite(REG_DEV_FCFG, fifo_config_val);
#ifdef USBD2_DBG
{
uint32_t reg_value = UsbRegRead(REG_DEV_FCFG);
usbd2_dbg("\nsetting FIFO config register [0x%03X]:\n", REG_DEV_FCFG);
for (int i = 0; i < 4; i++)
{
uint32_t temp = reg_value >> (i * 8);
usbd2_dbg("- FIFO %d configs:\n", i);
usbd2_dbg(" >> block enabled : %s\n", (temp & BIT5) ? "O" : "X");
if (!(temp & BIT5))
continue;
usbd2_dbg(" >> block size : %s\n", (temp & BIT4) ? "1024" : "512");
switch ((temp >> 2) & 0x3)
{
case 0:
usbd2_dbg(" >> block number: Single\n");
break;
case 1:
usbd2_dbg(" >> block number: Double\n");
break;
case 2:
usbd2_dbg(" >> block number: Triple\n");
break;
case 3:
usbd2_dbg(" >> block number: Reserved\n");
break;
}
switch (temp & 0x3)
{
case 0:
usbd2_dbg(" >> transfer type: Reserved\n");
break;
case 1:
usbd2_dbg(" >> transfer type: ISOCH\n");
break;
case 2:
usbd2_dbg(" >> transfer type: Bulk\n");
break;
case 3:
usbd2_dbg(" >> transfer type: Interrupt\n");
break;
}
}
usbd2_dbg("\n");
}
#endif
// set Device SOF Mask Timer value as data sheet recommended for HS
UsbRegWrite(REG_DEV_SMT, 0x44C);
// set configuration set bit, now allow HW to handle endpoint transfer
UsbRegMaskedSet(REG_DEV_ADR, AFT_CONF);
}
static int8_t _set_configuration(kdrv_usbd2v_setup_packet_t *setup)
{
int8_t resp = RESP_STALL;
uint8_t config_val = setup->wValue & 0xFF;
usbd2_dbg("[int] <Standard Request> set configuration, config = %d\n", config_val);
if (config_val == 0)
{
_config_state = CONFIG_ADDRESS_STATE;
// clear configuration set bit
UsbRegMaskedClr(REG_DEV_ADR, AFT_CONF);
resp = RESP_ACK;
// reset all endpoints and terminate all in-progress transfer
_reset_all_endpoints();
}
else if (config_val == 1)
{
kdrv_usbd2v_config_descriptor_t *config = _dev_desc->config;
if (_config_state != CONFIG_CONFIGURED_STATE)
{
_config_state = CONFIG_CONFIGURED_STATE;
kdrv_usbd2v_terminate_all_endpoint();
// init fifo and endpoint stuff
_init_fifo_configurations(config->interface);
_link_status = USBD2_STATUS_CONFIGURED;
_evt_cb(USBD2_STATUS_CONFIGURED);
// now that it is configured, we enable link status detection for disconnection status
_set_link_status_detection(true);
// unlock transfer API
for (int enp_no = 1; enp_no <= MAX_ENP_NUM; enp_no++)
osSemaphoreRelease(_enp_semaphore[enp_no]);
}
resp = RESP_ACK;
}
return resp;
}
static int8_t handle_standard_request(kdrv_usbd2v_setup_packet_t *setup)
{
int8_t resp = RESP_STALL;
// handle requests which are not affected by ep0 halt
switch (setup->bRequest)
{
case 0x0: // GET_STATUS
if (setup->wValue == 0x0)
{
resp = _send_host_device_status();
}
break;
case 0x1: // CLEAR_FEATURE
{
if (setup->wValue == 0x0)
{
// endpoint halt
_reset_endpoint(setup->wIndex);
resp = RESP_ACK;
}
break;
}
case 0x3: // SET_FEATURE
break;
}
switch (setup->bRequest)
{
case 0x5: // SET_ADDRESS
{
// USB2.0 spec says should not be greaten than 127
if (setup->wValue <= 127)
{
// set DEVADR and also clear AFT_CONF
UsbRegWrite(REG_DEV_ADR, setup->wValue);
resp = RESP_ACK;
}
}
break;
case 0x6: // GET_DESCRIPTOR
{
// low byte: index of specified descriptor type
uint8_t descp_idx = (setup->wValue & 0xFF);
// high byte: descriptor type
switch (setup->wValue >> 8)
{
case 1: // DEVICE descriptor
resp = _send_host_device_descriptor(setup);
break;
case 2: // CONFIGURATION descriptor
resp = _send_host_configuration_descriptors(setup);
break;
case 3: // STRING descriptor
resp = _send_host_string_descriptor(setup, descp_idx);
break;
case 4: // INTERFACE descriptor
break;
case 5: // ENDPOINT descriptor
break;
case 6: // DEVICE_QUALIFIER descriptor
break;
case 7: // OTHER_SPEED_CONFIGURATION descriptor
break;
case 8: // INTERFACE_POWER descriptor
break;
}
}
break;
case 0x7: // SET_DESCRIPTOR
break;
case 0x8: // GET_CONFIGURATION
break;
case 0x9: // SET_CONFIGURATION
resp = _set_configuration(setup);
break;
}
return resp;
}
static void _handle_device_interrupts()
{
uint32_t grp_x_int_status = UsbRegRead(REG_DEV_IGR);
#ifdef USBD2_DBG
usbd2_dbg("\n[int] !!!!!!!!!! from device group: ");
for (int i = 0; i < 4; i++)
if (grp_x_int_status & (0x1 << i))
usbd2_dbg("%d ", i);
usbd2_dbg("\n");
#endif
if (grp_x_int_status & GX_INT_G2_RO)
{
uint32_t grp_2_interrupts = (UsbRegRead(REG_DEV_ISG2) & ~(UsbRegRead(REG_DEV_MISG2)));
#ifdef USBD2_DBG
if (grp_2_interrupts & BIT0)
usbd2_dbg("[int] grp_2_interrupt: %s\n", "USB reset");
if (grp_2_interrupts & BIT1)
usbd2_dbg("[int] grp_2_interrupt: %s\n", "USB suspend");
if (grp_2_interrupts & BIT2)
usbd2_dbg("[int] grp_2_interrupt: %s\n", "USB resume");
if (grp_2_interrupts & BIT3)
usbd2_dbg("[int] grp_2_interrupt: %s\n", "ISOC seq error");
if (grp_2_interrupts & BIT4)
usbd2_dbg("[int] grp_2_interrupt: %s\n", "ISOC seq abort");
if (grp_2_interrupts & BIT5)
usbd2_dbg("[int] grp_2_interrupt: %s\n", "sent ZLP done");
if (grp_2_interrupts & BIT6)
usbd2_dbg("[int] grp_2_interrupt: %s\n", "received ZLP");
if (grp_2_interrupts & BIT7)
usbd2_dbg("[int] grp_2_interrupt: %s\n", "DMA complete");
if (grp_2_interrupts & BIT8)
usbd2_dbg("[int] grp_2_interrupt: %s\n", "DMA error");
if (grp_2_interrupts & BIT9)
usbd2_dbg("[int] grp_2_interrupt: %s\n", "device idle");
if (grp_2_interrupts & BIT10)
usbd2_dbg("[int] grp_2_interrupt: %s\n", "ake up by vbus");
usbd2_dbg("\n");
#endif
if (grp_2_interrupts & G2_USBRST_INT_RW1C)
{
// clear SET_CONFIG state and usb device address
UsbRegWrite(REG_DEV_ADR, 0x0);
// clear EP0 STALL bit
UsbRegMaskedClr(REG_CXCFE, CX_STL);
// disable (mask) all FIFOs interrupts
UsbRegWrite(REG_DEV_MISG1, 0xFFFFFFFF);
// clear all FIFO
UsbRegMaskedSet(REG_DEV_TST, TST_CLRFF);
// clear this interrupt bit
UsbRegWrite(REG_DEV_ISG2, G2_USBRST_INT_RW1C);
_config_state = CONFIG_DEFAULT_STATE;
}
if (grp_2_interrupts & G2_TX0BYTE_INT_RW1C)
{
uint32_t zlp_sent_interrupts = UsbRegRead(0x154);
UsbRegWrite(0x154, zlp_sent_interrupts);
for (int i = 0; i < MAX_ENP_NUM; i++)
{
if (zlp_sent_interrupts & (0x1 << i))
{
int fifo_no = _enp_to_fifo[i + 1];
usbd2_dbg("[int] ZLP done, fifo %d IN notify event\n", fifo_no);
_fifo_ctx[fifo_no].status = KDRV_STATUS_OK;
osEventFlagsSet(_evt_id, (0x1 << fifo_no));
}
}
// clear this interrupt bit
UsbRegWrite(REG_DEV_ISG2, G2_TX0BYTE_INT_RW1C);
}
}
if (grp_x_int_status & GX_INT_G0_RO)
{
uint32_t grp_0_interrupts = (UsbRegRead(REG_DEV_ISG0) & ~(UsbRegRead(REG_DEV_MISG0)));
#ifdef USBD2_DBG
if (grp_0_interrupts & BIT0)
usbd2_dbg("[int] grp_0_interrupt: %s\n", "CX SETUP packet");
if (grp_0_interrupts & BIT1)
usbd2_dbg("[int] grp_0_interrupt: %s\n", "CX IN");
if (grp_0_interrupts & BIT2)
usbd2_dbg("[int] grp_0_interrupt: %s\n", "CX OUT");
if (grp_0_interrupts & BIT3)
usbd2_dbg("[int] grp_0_interrupt: %s\n", "CX Status Packet");
if (grp_0_interrupts & BIT4)
usbd2_dbg("[int] grp_0_interrupt: %s\n", "CX Failed");
if (grp_0_interrupts & BIT5)
usbd2_dbg("[int] grp_0_interrupt: %s\n", "CX abort");
#endif
if (grp_0_interrupts & G0_CX_SETUP_INT_RO)
{
static kdrv_usbd2v_setup_packet_t setup_packet = {0};
memset(&setup_packet, 0, sizeof(setup_packet));
UsbRegWrite(0x304, _dma_remap_addr((uint32_t)&setup_packet));
UsbRegWrite(0x300, (8 << 8) | BIT0);
// polling VDMA complete
while ((UsbRegRead(0x328) & BIT0) == 0)
;
UsbRegWrite(0x328, BIT0);
usbd2_dbg("[int] VDMA recv CX SETUP : bmRequestType 0x%x bRequest 0x%x wValue 0x%x wIndex 0x%x wLength %d\n",
setup_packet.bmRequestType, setup_packet.bRequest, setup_packet.wValue,
setup_packet.wIndex, setup_packet.wLength);
int8_t resp;
uint8_t bmRequestType_type = ((setup_packet.bmRequestType & 0x60) >> 5);
switch (bmRequestType_type)
{
case 0: // Standard request
usbd2_dbg("[int] <Standard Request> ....\n");
resp = handle_standard_request(&setup_packet);
break;
case 1: // Class request
case 2: // Vendor request
usbd2_dbg("[int] <Class or Vendor Request> ....\n");
if (_usr_cx_cb(&setup_packet) == true)
resp = RESP_ACK;
else
resp = RESP_STALL;
break;
default:
usbd2_dbg("[int] <Unknown Request> ....\n");
resp = RESP_STALL;
break;
}
if (resp == RESP_ACK)
{
usbd2_dbg("[int] setting CX_DONE ....\n");
// indicate an OK request to host
UsbRegMaskedSet(REG_CXCFE, CX_DONE);
}
else if (resp == RESP_STALL)
{
usbd2_dbg("[int] setting CX_STL ....\n");
// indicate a request error to host
UsbRegMaskedSet(REG_CXCFE, CX_STL | CX_DONE);
}
// else RESP_LATER due to VDMA in transfer
}
}
if (grp_x_int_status & GX_INT_G3_RO)
{
usbd2_dbg("[int] VDMA interrupts = 0x%x\n", UsbRegRead(0x328));
uint32_t vdma_intrp = UsbRegRead(0x328);
UsbRegWrite(0x328, vdma_intrp);
if (vdma_intrp & BIT0)
{
// VDMA CX complete
usbd2_dbg("[int] VDMA CX complete\n");
usbd2_dbg("[int] setting CX_DONE ....\n");
UsbRegMaskedSet(REG_CXCFE, CX_DONE);
}
if (vdma_intrp & (BIT1 | BIT2 | BIT3 | BIT4))
{
// VDMA FIFO complete
for (uint32_t fifo_no = 0; fifo_no < FIFO_NUM; fifo_no++)
{
// find which FIFO
if (vdma_intrp & (0x2 << fifo_no))
{
// IN or OUT
if (_fifo_ctx[fifo_no].dir == 0)
{
usbd2_dbg("[int] VDMA fifo %d OUT complete\n", fifo_no);
uint32_t left_size = UsbRegRead(0x308 + 8 * fifo_no) >> 8;
if (left_size > 0 || _fifo_ctx[fifo_no].user_buf_len == 0)
{
usbd2_dbg("[int] VDMA fifo %d OUT transfer done, last recv size %d\n", fifo_no, VDMA_ONE_TXFER_SIZE - left_size);
_fifo_ctx[fifo_no].received_length -= left_size;
_fifo_ctx[fifo_no].status = KDRV_STATUS_OK;
osEventFlagsSet(_evt_id, (0x1 << fifo_no));
continue;
}
uint32_t vdma_txfer_size = MIN(_fifo_ctx[fifo_no].user_buf_len, VDMA_ONE_TXFER_SIZE);
usbd2_dbg("[int] VDMA fifo %d OUT next transfer addr 0x%x size %d\n",
fifo_no, UsbRegRead(0x30C + 8 * fifo_no), vdma_txfer_size);
_fifo_ctx[fifo_no].user_buf_len -= vdma_txfer_size;
_fifo_ctx[fifo_no].received_length += vdma_txfer_size;
UsbRegWrite(0x308 + 8 * fifo_no, (vdma_txfer_size << 8) | 0x1);
}
else
{
if (_fifo_ctx[fifo_no].user_buf_len == 0)
{
usbd2_dbg("[int] VDMA fifo %d IN transfer all done\n", fifo_no);
if (_fifo_ctx[fifo_no].send_ZLP)
{
usbd2_dbg("[int] fifo %d IN send ZLP\n", fifo_no);
int enp_no = _fifo_to_enp[fifo_no];
UsbRegMaskedSet(REG_DEV_INMPS_1 + 4 * (enp_no - 1), TX0BYTE_IEPn);
}
else
{
usbd2_dbg("[int] short pkt, fifo %d IN notify event\n", fifo_no);
_fifo_ctx[fifo_no].status = KDRV_STATUS_OK;
osEventFlagsSet(_evt_id, (0x1 << fifo_no));
}
continue;
}
usbd2_dbg("[int] VDMA fifo %d IN complete\n", fifo_no);
uint32_t left_size = UsbRegRead(0x308 + 8 * fifo_no) >> 8;
if (left_size != 0)
usbd2_dbg("[int] VDMA fifo %d IN error !!!, left_size = %d\n", fifo_no, left_size);
uint32_t vdma_txfer_size = MIN(_fifo_ctx[fifo_no].user_buf_len, VDMA_ONE_TXFER_SIZE);
usbd2_dbg("[int] VDMA fifo %d IN next transfer addr 0x%x size %d\n",
fifo_no, UsbRegRead(0x30C + 8 * fifo_no), vdma_txfer_size);
_fifo_ctx[fifo_no].user_buf_len -= vdma_txfer_size;
UsbRegWrite(0x308 + 8 * fifo_no, (vdma_txfer_size << 8) | BIT1 | BIT0);
}
}
}
}
if (vdma_intrp & (BIT16 | BIT17 | BIT18 | BIT19 | BIT20))
{
usbd2_dbg("[int] VDMA error !!!!!!!!\n");
}
}
}
// USB ISR
static void _usbd2_isr(void)
{
uint32_t global_int = UsbRegRead(REG_GLB_ISR);
if (global_int & DEV_INT)
_handle_device_interrupts();
if (global_int & OTG_INT)
{
// turn off status detection because it can be triggered forever if disconnected
_set_link_status_detection(false);
_link_status = USBD2_STATUS_DISCONNECTED;
_evt_cb(USBD2_STATUS_DISCONNECTED);
// reset endpoint to make transfer get terminated
_reset_all_endpoints();
}
}
static void _usbd2_init_register_isr(void)
{
SCU_EXTREG_USB_OTG_CTRL_SET_EXTCTRL_SUSPENDM(1);
SCU_EXTREG_USB_OTG_CTRL_SET_u_iddig(1);
SCU_EXTREG_USB_OTG_CTRL_SET_wakeup(0);
SCU_EXTREG_USB_OTG_CTRL_SET_l1_wakeup(0);
SCU_EXTREG_USB_OTG_CTRL_SET_OSCOUTEN(0);
SCU_EXTREG_USB_OTG_CTRL_SET_PLLALIV(0);
SCU_EXTREG_USB_OTG_CTRL_SET_XTLSEL(0);
SCU_EXTREG_USB_OTG_CTRL_SET_OUTCLKSEL(0);
_set_link_status_detection(false);
// enable Device and OTG interrupt
UsbRegWrite(REG_GLB_INT, ~(INT_POLARITY | DEV_INT | OTG_INT) & 0xF);
#ifdef USBD2_DBG
{
uint32_t reg_value = UsbRegRead(REG_GLB_INT);
usbd2_dbg("\nsetting global interrupt register [0x%03X]:\n", REG_GLB_INT);
usbd2_dbg("- interrupt active %s\n", (reg_value & 0x8) ? "high" : "low");
usbd2_dbg("- host interrupt : %s\n", (reg_value & 0x4) ? "X" : "O");
usbd2_dbg("- OTG interrupt : %s\n", (reg_value & 0x2) ? "X" : "O");
usbd2_dbg("- device interrupt : %s\n", (reg_value & 0x1) ? "X" : "O");
usbd2_dbg("\n");
}
#endif
// listen all 4 groups for 0x140
//UsbRegWrite(REG_DEV_MIGR, GX_INT_G1_RO | GX_INT_G2_RO);
//UsbRegWrite(REG_DEV_MIGR, GX_INT_G1_RO);
UsbRegWrite(REG_DEV_MIGR, 0);
#ifdef USBD2_DBG
{
uint32_t reg_value = UsbRegRead(REG_DEV_MIGR);
usbd2_dbg("\nsetting groups interrupt enable register [0x%03X]:\n", REG_DEV_MIGR);
for (int i = 0; i < 4; i++)
usbd2_dbg("- group %d interrupt : %s\n", i, (reg_value & (0x1 << i)) ? "X" : "O");
usbd2_dbg("\n");
}
#endif
UsbRegWrite(REG_DEV_MISG0, 0);
#ifdef USBD2_DBG
{
uint32_t reg_value = UsbRegRead(REG_DEV_MISG0);
usbd2_dbg("\nsetting group 0 interrupt register [0x%03X]:\n", REG_DEV_MISG0);
usbd2_dbg("- CX abort interrupt : %s\n", (reg_value & 0x20) ? "X" : "O");
usbd2_dbg("- CX Failed interrupt : %s\n", (reg_value & 0x10) ? "X" : "O");
usbd2_dbg("- CX Status Packet interrupt : %s\n", (reg_value & 0x8) ? "X" : "O");
usbd2_dbg("- CX OUT interrupt : %s\n", (reg_value & 0x4) ? "X" : "O");
usbd2_dbg("- CX IN interrupt : %s\n", (reg_value & 0x2) ? "X" : "O");
usbd2_dbg("- CX SETUP interrupt : %s\n", (reg_value & 0x1) ? "X" : "O");
usbd2_dbg("\n");
}
#endif
// grop 1 interrupt mask
// at initialization turn off all fifo interrupts
// for FIFO IN, it triggers interrupt while FIFO is empty !
UsbRegWrite(REG_DEV_MISG1, 0xFFFFFFFF);
#ifdef USBD2_DBG
{
uint32_t reg_value = UsbRegRead(REG_DEV_MISG1);
usbd2_dbg("\nsetting group 1 interrupt register [0x%03X]:\n", REG_DEV_MISG1);
for (int i = 0; i < 4; i++)
{
uint32_t temp1 = reg_value >> (i * 2);
uint32_t temp2 = reg_value >> (16 + i);
usbd2_dbg("- FIFO %d interrupt >> OUT: %s, ShortPkt: %s, IN: %s\n",
i, (temp1 & 0x1) ? "off" : "on", (temp1 & 0x2) ? "off" : "on",
(temp2 & 0x1) ? "off" : "on");
}
usbd2_dbg("\n");
}
#endif
// grop 2 interrupt mask
UsbRegWrite(REG_DEV_MISG2,
~(G2_TX0BYTE_INT_RW1C | G2_USBRST_INT_RW1C));
#ifdef USBD2_DBG
{
uint32_t reg_value = UsbRegRead(REG_DEV_MISG2);
usbd2_dbg("\nsetting group 2 interrupt register [0x%03X]:\n", REG_DEV_MISG2);
usbd2_dbg("- wake up by vbus interrupt : %s\n", (reg_value & 0x40) ? "X" : "O");
usbd2_dbg("- device idle interrupt : %s\n", (reg_value & 0x200) ? "X" : "O");
usbd2_dbg("- DMA error interrupt : %s\n", (reg_value & 0x100) ? "X" : "O");
usbd2_dbg("- DMA complete interrupt : %s\n", (reg_value & 0x80) ? "X" : "O");
usbd2_dbg("- received ZLP interrupt : %s\n", (reg_value & 0x40) ? "X" : "O");
usbd2_dbg("- sent ZLP interrupt : %s\n", (reg_value & 0x20) ? "X" : "O");
usbd2_dbg("- ISOC seq abort interrupt : %s\n", (reg_value & 0x10) ? "X" : "O");
usbd2_dbg("- ISOC seq error interrupt : %s\n", (reg_value & 0x8) ? "X" : "O");
usbd2_dbg("- resume interrupt : %s\n", (reg_value & 0x4) ? "X" : "O");
usbd2_dbg("- suspend interrupt : %s\n", (reg_value & 0x2) ? "X" : "O");
usbd2_dbg("- bus reset interrupt : %s\n", (reg_value & 0x1) ? "X" : "O");
usbd2_dbg("\n");
}
#endif
// set device idle counter = 7ms
UsbRegWrite(REG_DEV_ICR, 0x7);
// device soft reset
UsbRegMaskedSet(REG_DEV_CTL, BIT4);
// clear all FIFO counter
UsbRegMaskedSet(REG_DEV_TST, BIT0);
// enable chip
UsbRegMaskedSet(REG_DEV_CTL, BIT5);
// clear all interrupts status for RW1C bits
UsbRegWrite(REG_OTG_ISR, 0xFFFFFFFF);
UsbRegWrite(REG_DEV_ISG0, 0xFFFFFFFF);
UsbRegWrite(REG_DEV_ISG2, 0xFFFFFFFF);
// global interrupt enable
UsbRegMaskedSet(REG_DEV_CTL, BIT2);
UsbRegWrite(0x330, 1); // init VDMA
NVIC_SetVector(OTG_SBS_3_IRQ, (uint32_t)_usbd2_isr);
// Clear and Enable SAI IRQ
//NVIC_ClearPendingIRQ(OTG_SBS_3_IRQ);
NVIC_EnableIRQ(OTG_SBS_3_IRQ);
}
static void _default_status_isr_callback(kdrv_usbd2v_link_status_t event)
{
}
static bool _default_usr_cx_isr_callback(kdrv_usbd2v_setup_packet_t *setup)
{
return false; // RESP_STALL
}
////////////////// below are API ////////////////////
kdrv_status_t kdrv_usbd2v_initialize(
kdrv_usbd2v_device_descriptor_t *dev_desc,
kdrv_usbd2v_string_descriptor_t *dev_str_desc,
kdrv_usbd2v_link_status_callback_t status_isr_cb,
kdrv_usbd2v_user_control_callback_t usr_cx_isr_cb)
{
_dev_desc = dev_desc;
_dev_str_desc = dev_str_desc;
_evt_cb = (status_isr_cb == NULL) ? _default_status_isr_callback : status_isr_cb;
_usr_cx_cb = (usr_cx_isr_cb == NULL) ? _default_usr_cx_isr_callback : usr_cx_isr_cb;
_evt_id = osEventFlagsNew(NULL);
_usbd2_init_register_isr();
for (int enp_no = 1; enp_no <= MAX_ENP_NUM; enp_no++)
_enp_semaphore[enp_no] = osSemaphoreNew(1, 0, NULL); // at first it is locked
return KDRV_STATUS_OK;
}
kdrv_status_t kdrv_usbd2v_uninitialize(void)
{
osEventFlagsDelete(_evt_id);
for (int enp_no = 1; enp_no <= MAX_ENP_NUM; enp_no++)
{
osSemaphoreDelete(_enp_semaphore[enp_no]);
_enp_semaphore[enp_no] = NULL;
}
return KDRV_STATUS_OK;
}
kdrv_status_t kdrv_usbd2v_set_enable(bool enable)
{
if ((UsbRegRead(REG_PHY_TST) & TST_JSTA) == 0)
{
UsbRegMaskedSet(REG_PHY_TST, TST_JSTA);
osDelay(5);
}
if (enable)
// Make PHY work properly, FIXME ?
UsbRegMaskedClr(REG_PHY_TST, TST_JSTA);
else
// Make PHY not work, FIXME ?
UsbRegMaskedSet(REG_PHY_TST, TST_JSTA);
return KDRV_STATUS_OK;
}
kdrv_usbd2v_link_status_t kdrv_usbd2v_get_link_status(void)
{
return _link_status;
}
kdrv_status_t kdrv_usbd2v_reset_device()
{
usbd2_dbg("%s()\n", __FUNCTION__);
// disable bus
kdrv_usbd2v_set_enable(false);
// reset all endpoints and terminate all in-progress transfer
_reset_all_endpoints();
// some delay for USB bus, FIXME ?
osDelay(100);
_config_state = CONFIG_DEFAULT_STATE;
// re-init registers and isr
_usbd2_init_register_isr();
// re-enable bus
kdrv_usbd2v_set_enable(true);
return KDRV_STATUS_OK;
}
kdrv_status_t kdrv_usbd2v_terminate_all_endpoint(void)
{
usbd2_dbg("%s()\n", __FUNCTION__);
UsbRegWrite(0x330, 0); // stop VDMA
for (int enp_no = 1; enp_no <= MAX_ENP_NUM; enp_no++)
{
int fifo_no = _enp_to_fifo[enp_no];
if (fifo_no >= 0)
{
UsbRegWrite(0x1B0 + 4 * fifo_no, FFRST);
if (_fifo_ctx[fifo_no].isTransferring)
{
// notify transfer done to user
_fifo_ctx[fifo_no].status = KDRV_STATUS_USBD_TRANSFER_TERMINATED;
osEventFlagsSet(_evt_id, (0x1 << fifo_no));
}
}
}
UsbRegWrite(0x330, 1); // start VDMA
return KDRV_STATUS_OK;
}
kdrv_status_t kdrv_usbd2v_bulk_send(uint32_t endpoint, uint32_t *buf, uint32_t txLen, uint32_t timeout_ms)
{
kdrv_status_t status;
uint8_t enp_no = endpoint & 0xF;
uint8_t fifo_no = _enp_to_fifo[enp_no];
osSemaphoreAcquire(_enp_semaphore[enp_no], osWaitForever);
usbd2_dbg("[bulk_send] enp 0x%x fifo_no %d buf 0x%x txLen %d\n", endpoint, fifo_no, buf, txLen);
_fifo_ctx[fifo_no].send_ZLP = ((txLen & 0x1FF) == 0) ? true : false;
uint32_t vdma_txfer_size = MIN(txLen, VDMA_ONE_TXFER_SIZE);
txLen -= vdma_txfer_size;
_fifo_ctx[fifo_no].isTransferring = true;
_fifo_ctx[fifo_no].user_buf_len = txLen;
_fifo_ctx[fifo_no].dir = 1;
osEventFlagsClear(_evt_id, (0x1 << fifo_no));
UsbRegWrite(0x30C + 8 * fifo_no, _dma_remap_addr((uint32_t)buf));
UsbRegWrite(0x308 + 8 * fifo_no, (vdma_txfer_size << 8) | BIT1 | BIT0);
uint32_t flags = osEventFlagsWait(_evt_id, (0x1 << fifo_no), osFlagsWaitAny, timeout_ms);
if (flags == osFlagsErrorTimeout)
status = KDRV_STATUS_USBD_TRANSFER_TIMEOUT;
else
status = _fifo_ctx[fifo_no].status;
_fifo_ctx[fifo_no].isTransferring = false;
usbd2_dbg("[bulk_send] enp 0x%x status %d\n", endpoint, status);
if (status != KDRV_STATUS_USBD_TRANSFER_DISCONNECTED)
osSemaphoreRelease(_enp_semaphore[enp_no]);
return status;
}
#define FAKE_ZLP 0x11223344
kdrv_status_t kdrv_usbd2v_bulk_receive(uint32_t endpoint, uint32_t *buf, uint32_t *blen, uint32_t timeout_ms)
{
kdrv_status_t status;
uint8_t enp_no = endpoint & 0xF;
uint8_t fifo_no = _enp_to_fifo[enp_no];
osSemaphoreAcquire(_enp_semaphore[enp_no], osWaitForever);
usbd2_dbg("[bulk_recv] enp 0x%x fifo_no %d buf 0x%x buf_len %d\n", endpoint, fifo_no, buf, *blen);
uint32_t left_buf_size = *blen;
uint32_t vdma_txfer_size = MIN(left_buf_size, VDMA_ONE_TXFER_SIZE);
left_buf_size -= vdma_txfer_size;
_fifo_ctx[fifo_no].isTransferring = true;
_fifo_ctx[fifo_no].user_buf_len = left_buf_size;
_fifo_ctx[fifo_no].received_length = vdma_txfer_size; // pre value
_fifo_ctx[fifo_no].dir = 0;
osEventFlagsClear(_evt_id, (0x1 << fifo_no));
UsbRegWrite(0x30C + 8 * fifo_no, _dma_remap_addr((uint32_t)buf));
UsbRegWrite(0x308 + 8 * fifo_no, (vdma_txfer_size << 8) | BIT0);
uint32_t flags = osEventFlagsWait(_evt_id, (0x1 << fifo_no), osFlagsWaitAny, timeout_ms);
if (flags == osFlagsErrorTimeout)
status = KDRV_STATUS_USBD_TRANSFER_TIMEOUT;
else
{
*blen = _fifo_ctx[fifo_no].received_length;
status = _fifo_ctx[fifo_no].status;
if (((*blen - 4) & 0x1FF) == 0 && *(uint32_t *)((uint32_t)buf + *blen - 4) == FAKE_ZLP)
{
usbd2_dbg("[bulk_recv] enp 0x%x recv fake ZLP\n", endpoint);
*blen -= 4;
}
}
_fifo_ctx[fifo_no].isTransferring = false;
usbd2_dbg("[bulk_recv] enp 0x%x recv %d status %d\n", endpoint, *blen, status);
if (status != KDRV_STATUS_USBD_TRANSFER_DISCONNECTED)
osSemaphoreRelease(_enp_semaphore[enp_no]);
return status;
}
bool kdrv_usbd2v_interrupt_send_check_buffer_empty(uint32_t endpoint)
{
uint8_t enp_no = endpoint & 0xF;
uint8_t fifo_no = _enp_to_fifo[enp_no];
return !!(UsbRegRead(REG_CXCFE) & (F_EMP_0 << fifo_no));
}
kdrv_status_t kdrv_usbd2v_interrupt_send(uint32_t endpoint, uint32_t *buf, uint32_t txLen, uint32_t timeout_ms)
{
kdrv_status_t status;
uint8_t enp_no = endpoint & 0xF;
uint8_t fifo_no = _enp_to_fifo[enp_no];
osSemaphoreAcquire(_enp_semaphore[enp_no], osWaitForever);
// clear FIFO content before transmission
UsbRegWrite(0x1B0 + 4 * fifo_no, FFRST);
uint32_t vdma_txfer_size = MIN(txLen, 1024);
_fifo_ctx[fifo_no].isTransferring = true;
_fifo_ctx[fifo_no].user_buf_len = 0;
_fifo_ctx[fifo_no].dir = 1;
osEventFlagsClear(_evt_id, (0x1 << fifo_no));
UsbRegWrite(0x30C + 8 * fifo_no, _dma_remap_addr((uint32_t)buf));
UsbRegWrite(0x308 + 8 * fifo_no, (vdma_txfer_size << 8) | BIT1 | BIT0);
uint32_t flags = osEventFlagsWait(_evt_id, (0x1 << fifo_no), osFlagsWaitAny, timeout_ms);
if (flags == osFlagsErrorTimeout)
status = KDRV_STATUS_USBD_TRANSFER_TIMEOUT;
else
status = _fifo_ctx[fifo_no].status;
_fifo_ctx[fifo_no].isTransferring = false;
if (status != KDRV_STATUS_USBD_TRANSFER_DISCONNECTED)
osSemaphoreRelease(_enp_semaphore[enp_no]);
return status;
}