First upload. Untested software
This commit is contained in:
+150
@@ -0,0 +1,150 @@
|
||||
/* Baremetal Risc-V USB <=> Amiga Joystick/Mouse HID converter.
|
||||
* March 2026 - Anders Holck
|
||||
* This software has no license. If you choose to use, please include my name :)
|
||||
*/
|
||||
|
||||
#include "usb_dwc2.h"
|
||||
#include "usb_descriptors.h"
|
||||
|
||||
// Endpoint 0 State
|
||||
#define EP0_IDLE 0
|
||||
#define EP0_SETUP 1
|
||||
#define EP0_DATA_IN 2
|
||||
#define EP0_DATA_OUT 3
|
||||
#define EP0_STATUS_IN 4
|
||||
#define EP0_STATUS_OUT 5
|
||||
|
||||
static uint8_t ep0_state = EP0_IDLE;
|
||||
static uint8_t usb_address = 0;
|
||||
|
||||
// Setup Packet Structure
|
||||
typedef struct {
|
||||
uint8_t bmRequestType;
|
||||
uint8_t bRequest;
|
||||
uint16_t wValue;
|
||||
uint16_t wIndex;
|
||||
uint16_t wLength;
|
||||
} usb_setup_t;
|
||||
|
||||
void dwc2_setup_fifo() {
|
||||
// Total FIFO size is often 512 or 1024 words.
|
||||
// Configure RX FIFO (Shared)
|
||||
write32(GRXFSIZ, 128); // 128 words
|
||||
|
||||
// Configure TX FIFO 0 (EP0 IN)
|
||||
// Offset = GRXFSIZ
|
||||
write32(GNPTXFSIZ, (128 << 16) | 128); // Size 128, Start 128
|
||||
|
||||
// Configure TX FIFO 1 (EP1 IN - HID)
|
||||
// Offset = GRXFSIZ + GNPTXFSIZ
|
||||
write32(USB_DWC2_BASE + 0x104, (128 << 16) | 256); // Size 128, Start 256
|
||||
}
|
||||
|
||||
void dwc2_send_packet(uint8_t ep_num, const uint8_t *data, uint32_t len) {
|
||||
uint32_t fifo_addr = USB_DWC2_BASE + 0x1000 + (ep_num * 0x1000);
|
||||
|
||||
// 1. Set Transfer Size and Packet Count
|
||||
uint32_t dieptsiz = (1 << 19) | len; // 1 packet, 'len' bytes
|
||||
write32(USB_DWC2_BASE + 0x908 + (ep_num * 0x20), dieptsiz);
|
||||
|
||||
// 2. Enable Endpoint and Clear NAK
|
||||
uint32_t diepctl = read32(USB_DWC2_BASE + 0x900 + (ep_num * 0x20));
|
||||
diepctl |= (1 << 31) | (1 << 26); // EPEna, Cnak
|
||||
write32(USB_DWC2_BASE + 0x900 + (ep_num * 0x20), diepctl);
|
||||
|
||||
// 3. Write data to FIFO (Word-aligned writes)
|
||||
uint32_t words = (len + 3) / 4;
|
||||
uint32_t *data32 = (uint32_t *)data;
|
||||
for (uint32_t i = 0; i < words; i++) {
|
||||
write32(fifo_addr, data32[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void dwc2_handle_setup(usb_setup_t *setup) {
|
||||
if ((setup->bmRequestType & 0x60) == 0) { // Standard Request
|
||||
switch (setup->bRequest) {
|
||||
case USB_REQ_GET_DESCRIPTOR: {
|
||||
uint8_t type = setup->wValue >> 8;
|
||||
const uint8_t *ptr = 0;
|
||||
uint32_t len = 0;
|
||||
|
||||
if (type == USB_DESC_DEVICE) {
|
||||
ptr = device_desc;
|
||||
len = sizeof(device_desc);
|
||||
} else if (type == USB_DESC_CONFIGURATION) {
|
||||
ptr = config_desc;
|
||||
len = sizeof(config_desc);
|
||||
} else if (type == USB_DESC_REPORT) {
|
||||
ptr = hid_report_desc;
|
||||
len = sizeof(hid_report_desc);
|
||||
}
|
||||
|
||||
if (ptr) {
|
||||
if (len > setup->wLength) len = setup->wLength;
|
||||
dwc2_send_packet(0, ptr, len);
|
||||
ep0_state = EP0_DATA_IN;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case USB_REQ_SET_ADDRESS:
|
||||
usb_address = setup->wValue & 0x7F;
|
||||
// Address is applied after the status phase
|
||||
dwc2_send_packet(0, 0, 0); // ZLP Status
|
||||
ep0_state = EP0_STATUS_IN;
|
||||
break;
|
||||
case USB_REQ_SET_CONFIGURATION:
|
||||
dwc2_send_packet(0, 0, 0); // ZLP Status
|
||||
ep0_state = EP0_STATUS_IN;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void dwc2_poll() {
|
||||
uint32_t gintsts = read32(GINTSTS);
|
||||
|
||||
// 1. USB Reset
|
||||
if (gintsts & (1 << 12)) {
|
||||
write32(GINTSTS, (1 << 12));
|
||||
usb_address = 0;
|
||||
ep0_state = EP0_IDLE;
|
||||
dwc2_setup_fifo();
|
||||
// Enable EP0 OUT
|
||||
write32(DOEPCTL0, (1 << 31) | (1 << 26)); // EPEna, Cnak
|
||||
}
|
||||
|
||||
// 2. Enumeration Done
|
||||
if (gintsts & (1 << 13)) {
|
||||
write32(GINTSTS, (1 << 13));
|
||||
// High speed or Full speed detected...
|
||||
}
|
||||
|
||||
// 3. Receive FIFO Level (Data available)
|
||||
if (gintsts & (1 << 4)) {
|
||||
uint32_t grxstsp = read32(GRXSTSP);
|
||||
uint8_t ep_num = grxstsp & 0xF;
|
||||
uint32_t bcnt = (grxstsp >> 4) & 0x7FF;
|
||||
uint8_t pktsts = (grxstsp >> 17) & 0xF;
|
||||
|
||||
if (pktsts == 6) { // SETUP Packet Received
|
||||
usb_setup_t setup;
|
||||
uint32_t *setup_ptr = (uint32_t *)&setup;
|
||||
uint32_t fifo_addr = USB_DWC2_BASE + 0x1000;
|
||||
setup_ptr[0] = read32(fifo_addr);
|
||||
setup_ptr[1] = read32(fifo_addr);
|
||||
dwc2_handle_setup(&setup);
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Update Address after Status Phase
|
||||
if (ep0_state == EP0_STATUS_IN) {
|
||||
uint32_t daint = read32(DAINT);
|
||||
if (daint & (1 << 0)) { // EP0 IN Complete
|
||||
uint32_t dcfg = read32(DCFG);
|
||||
dcfg &= ~(0x7F << 4);
|
||||
dcfg |= (usb_address << 4);
|
||||
write32(DCFG, dcfg);
|
||||
ep0_state = EP0_IDLE;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user