Init repository

This commit is contained in:
Loïc Guégan 2025-03-24 09:33:49 +01:00
parent d3ecfe3498
commit 7741f01445
35 changed files with 2067 additions and 90 deletions

38
src/boot/boot2.S Normal file
View file

@ -0,0 +1,38 @@
.thumb
.section .boot2, "ax"
// Disable SSI
ldr r0, =SSI_SSIENR
ldr r1, =0
str r1, [r0]
// Set baud rate
ldr r0, =SSI_BAUDR
ldr r1, =4
str r1, [r0]
// Enter XIP
ldr r0, =SSI_CTRLR0
ldr r1, =(3 << 8) | (31 << 16)
str r1, [r0]
// CTRLR0
ldr r0, =SSI_SPI_CTRLR0
ldr r1, =(6 << 2) | (2 << 8) | (0x03 << 24)
str r1, [r0]
// Enable back SSI
ldr r0, =SSI_SSIENR
ldr r1, =1
str r1, [r0]
// Jump to crt0.S
ldr r0, =0x10000101
bx r0
.set SSI_BASE, 0x18000000
.set SSI_CTRLR0, SSI_BASE + 0x00
.set SSI_SSIENR, SSI_BASE + 0x08
.set SSI_BAUDR, SSI_BASE + 0x14
.set SSI_SPI_CTRLR0, SSI_BASE + 0xF4

40
src/boot/crt0.S Normal file
View file

@ -0,0 +1,40 @@
.section .crt0, "ax"
// Load data segment to SRAM
ldr r0, =__data_src__
ldr r1, =__data_dst__
ldr r2, =__data_size__
mov r3, #0
data_seg_start:
cmp r2, #0
beq data_seg_end
ldrb r3, [r0]
strb r3, [r1]
add r0, #1
add r1, #1
sub r2, #1
b data_seg_start
data_seg_end:
// Init bss in SRAM
ldr r0, =__bss_start__
ldr r1, =__bss_size__
mov r2, #0
bss_init_start:
cmp r1, #0
beq bss_init_end
strb r2, [r0]
add r0, #1
sub r1, #1
b bss_init_start
bss_init_end:
// Setup stack
ldr r0, =SRAM_END
mov sp, r0
// Start kernel
ldr r0, =main
blx r0
.set SRAM_END, 0x20042000

87
src/libs/addrmap.h Normal file
View file

@ -0,0 +1,87 @@
#ifndef __ADDRMAP__
#define __ADDRMAP__
// ----- SIO -----
#define SIO_BASE 0xd0000000
#define SIO_CPUID SIO_BASE + 0x000
#define SIO_GPIO_OE SIO_BASE + 0x020
#define SIO_GPIO_OUT_XOR SIO_BASE + 0x01c
#define SIO_SPINLOCK0 SIO_BASE + 0x100
#define SIO_SPINLOCK31 SIO_BASE + 0x17c
// ----- RESET -----
#define RESETS_BASE 0x4000c000
#define RESETS_RESET RESETS_BASE + 0x0
#define RESETS_DONE RESETS_BASE + 0x8
// ----- Cortex M0+ -----
#define PPB_BASE 0xe0000000 // M0PLUS CPU registers
#define PPB_SYST_CSR PPB_BASE + 0xe010
#define PPB_SYST_RVR PPB_BASE + 0xe014
#define PPB_SYST_CVR PPB_BASE + 0xe018
#define PPB_SYST_CALIB PPB_BASE + 0xe01c
#define PPB_NVIC_ISER PPB_BASE + 0xe100
#define PPB_NVIC_ISPR PPB_BASE + 0xe200
#define PPB_NVIC_ICPR PPB_BASE + 0xe280
#define PPB_VTOR PPB_BASE + 0xed08
// ----- APB Peripherals -----
// IO_BANK0
#define IO_BANK0_BASE 0x40014000
#define IO_BANK0_GPIO25_STATUS IO_BANK0_BASE + 0x0C8
#define IO_BANK0_GPIO25_CTRL IO_BANK0_BASE + 0x0CC
// CLOCKS
#define CLOCKS_BASE 0x40008000
#define CLOCKS_SYS_CTRL CLOCKS_BASE + 0x03c
#define CLOCKS_SYS_DIV CLOCKS_BASE + 0x040
#define CLOCKS_SYS_SELECTED CLOCKS_BASE + 0x044
#define CLOCKS_REF_CTRL CLOCKS_BASE + 0x030
#define CLOCKS_PERI_CTRL CLOCKS_BASE + 0x048
#define CLOCKS_USB_CTRL CLOCKS_BASE + 0x054
// XOSC
#define XOSC_BASE 0x40024000
#define XOSC_CTRL XOSC_BASE + 0x000
#define XOSC_STATUS XOSC_BASE + 0x004
#define XOSC_DORMANT XOSC_BASE + 0x008
#define XOSC_STARTUP XOSC_BASE + 0x00c
#define XOSC_COUNT XOSC_BASE + 0x01c
// PLL_SYS
#define PLL_SYS_BASE 0x40028000
#define PLL_SYS_CS PLL_SYS_BASE + 0x000
#define PLL_SYS_PWR PLL_SYS_BASE + 0x004
#define PLL_SYS_FBDIV_INT PLL_SYS_BASE + 0x008
#define PLL_SYS_PRIM PLL_SYS_BASE + 0x00c
// PLL_USB
#define PLL_USB_BASE 0x4002c000
#define PLL_USB_CS PLL_USB_BASE + 0x000
#define PLL_USB_PWR PLL_USB_BASE + 0x004
#define PLL_USB_FBDIV_INT PLL_USB_BASE + 0x008
#define PLL_USB_PRIM PLL_USB_BASE + 0x00c
// SRAM
#define SRAM_BASE 0x20000000
#define SRAM_BANK4_BASE 0x20040000
#define SRAM_BANK5_BASE 0x20041000
#define SRAM_END SRAM_BASE + 0x42000
// USB Controller
#define USBCTRL_BASE 0x50100000
#define USBCTRL_DPSRAM_BASE USBCTRL_BASE + 0x000
#define USBCTRL_DPSRAM_SETUP_PACKET USBCTRL_DPSRAM_BASE + 0x000
#define USBCTRL_REGS_BASE 0x50110000
#define USBCTRL_EP0_BUFFER0 USBCTRL_DPSRAM_BASE + 0x100
#define USBCTRL_EP0_BUFFER_CTRL_IN USBCTRL_DPSRAM_BASE + 0x080
#define USBCTRL_EP0_BUFFER_CTRL_OUT USBCTRL_DPSRAM_BASE + 0x084
#define USBCTRL_EP1_ENDP_CTRL_IN USBCTRL_DPSRAM_BASE + 0x08
#define USBCTRL_EP1_BUFFER_CTRL_IN USBCTRL_DPSRAM_BASE + 0x88
#define USBCTRL_DATA_BUFFER_START USBCTRL_DPSRAM_BASE + 0x180
#define USBCTRL_ADDR_ENDP USBCTRL_REGS_BASE + 0x000
#define USBCTRL_MAINCTRL USBCTRL_REGS_BASE + 0x040
#define USBCTRL_SIE_CTRL USBCTRL_REGS_BASE + 0x04c
#define USBCTRL_SIE_STATUS USBCTRL_REGS_BASE + 0x050
#define USBCTRL_BUFF_STATUS USBCTRL_REGS_BASE + 0x058
#define USBCTRL_INTR USBCTRL_REGS_BASE + 0x08c
#define USBCTRL_INTE USBCTRL_REGS_BASE + 0x090
#define USBCTRL_INTS USBCTRL_REGS_BASE + 0x098
#define USBCTRL_MUXING USBCTRL_REGS_BASE + 0x074
#define USBCTRL_PWR USBCTRL_REGS_BASE + 0x078
#endif

42
src/libs/bitmap.h Normal file
View file

@ -0,0 +1,42 @@
#ifndef __BITMAP_H__
#define __BITMAP_H__
#define _BIT(BIT) (1u << (BIT))
// ----- USB Controller -----
// INTS
#define BIT_USBCTRL_INTS_SETUP_REQ _BIT(16)
#define BIT_USBCTRL_INTS_BUFFER_STATUS _BIT(4)
#define BIT_USBCTRL_INTS_TRANS_COMPLETE _BIT(3)
#define BIT_USBCTRL_INTS_BUS_RESET _BIT(12)
// SIE_STATUS
#define BIT_USBCTRL_SIE_STATUS_SETUP_REC _BIT(17)
#define BIT_USBCTRL_SIE_STATUS_BUS_RESET _BIT(19)
// Buffer control
#define BIT_USBCTRL_BUFFER0_PID _BIT(13)
#define BIT_USBCTRL_BUFFER0_FULL _BIT(15)
#define BIT_USBCTRL_BUFFER0_AVAILABLE _BIT(10)
#define MASK_USBCTRL_BUFFER0_LENGTH (0x3ff)
// Muxing
#define BIT_USBCTRL_MUXING_TO_PHY _BIT(0)
#define BIT_USBCTRL_MUXING_SOFTCON _BIT(3)
// Power
#define BIT_USBCTRL_PWR_VBUS_DETECT _BIT(2)
#define BIT_USBCTRL_PWR_VBUS_DETECT_OVERRIDE_EN _BIT(3)
// SIE_CTRL
#define BIT_USBCTRL_SIE_CTRL_PULLUP_EN _BIT(16)
#define BIT_USBCTRL_SIE_CTRL_PULLDOWN_EN _BIT(15)
#define BIT_USBCTRL_SIE_CTRL_EP0_INT_1BUF _BIT(29)
// MAIN_CTRL
#define BIT_USBCTRL_MAIN_CTRL_CONTROLLER_EN _BIT(0)
#define BIT_USBCTRL_MAIN_CTRL_HOST_NDEVICE _BIT(1)
// INTE
#define BIT_USBCTRL_INTE_SETUP_REQ _BIT(16)
#define BIT_USBCTRL_INTE_BUS_RESET _BIT(12)
#define BIT_USBCTRL_INTE_BUFF_STATUS _BIT(4)
// Endpoint Control
#define BIT_USBCTRL_ENDP_CTRL_ENABLE _BIT(31)
#define BIT_USBCTRL_ENDP_CTRL_INT_PER_BUFF1 _BIT(29)
#define BIT_USBCTRL_ENDP_CTRL_TYPE_LSB (26)
#endif

57
src/libs/clock.c Normal file
View file

@ -0,0 +1,57 @@
#include "clock.h"
#include "addrmap.h"
#include "types.h"
#include "utils.h"
// Updated at each systick interrupts, one for each core
__attribute__((aligned(4))) u32 cores_systick[2];
void wait(u32 ms){
u32 cpuid=REG_READ(SIO_CPUID);
cores_systick[cpuid]=0; // Init core counter
while(cores_systick[cpuid]<ms){} // Wait for correct amount of ticks
}
void xosc_init() {
// Starting xosc
REG_WRITE(XOSC_CTRL, 0xAA0); // Straight up from doc (to set it up to max (15MHz) => 12MHz in reality because of the xosc crystal)
REG_WRITE(XOSC_STARTUP, 0x00C4); // Startup delay (default value)
REG_WRITE_BITMAP_SET(XOSC_CTRL, 0xFAB << 12); // Enable clock
while(!(REG_READ(XOSC_STATUS) & 1<<31)){} // Wait for xosc to be stable
// Reset System PLL
REG_WRITE_BITMAP_CLEAR(RESETS_RESET, 1<<12); // Reset System PLL
while((REG_READ(RESETS_DONE) & 1<<12) == 0){} // Wait for System PLL reset to be done
// Setup and starts System PLL
// We use default config from (P232) which generate a 125MHz clock signal
REG_WRITE(PLL_SYS_FBDIV_INT, 125); // Multiply factor (we multply input frequency by 125 (so Fxosc * 10 = 125*12MHz = 1500MHz))
REG_WRITE(PLL_SYS_PRIM, 6<<16|2<<12); // Post divider divide output frequency by 6 then 2 (totally 12) thus Ffinal=1500MHz/12 = 125MHz
REG_WRITE_BITMAP_CLEAR(PLL_SYS_PWR, 1<<5|1<<3|1<<0); // Turn on VCO + PLL + fbdiv
while(!(REG_READ(PLL_SYS_CS) & 1<<31)){} // Wait for System PLL to be locked (Fin == Fout * Fact)
// By default System PLL is connected to sys_clk auxsrc
// So, just permute sys_clck to auxsrc (remember it is glitchless)
REG_WRITE(CLOCKS_SYS_CTRL, 0x1); // Now cores are using System PLL output
// Setup clock interrupt (1 every ms)
REG_WRITE(PPB_SYST_RVR, 125*1000); // We want 1tick per ms = 125MHz/125e3 = 1000Hz == 1 tick per ms, we can argue on accuracy here...
REG_WRITE(PPB_SYST_CSR, 0b111); // Clock source for Systick is core clock (PLL)/ Interrupt enable / Counter enable
// Now, setup USB PLL, reset USB PLL
REG_WRITE_BITMAP_CLEAR(RESETS_RESET, 1<<13); // Reset USB PLL
while((REG_READ(RESETS_DONE) & 1<<13) == 0){} // Wait for USB PLL reset to be done
// We use default config from (P232) which generate a 48MHz clock signal for USB
REG_WRITE(PLL_USB_FBDIV_INT, 100); // Multiply factor (we multply input frequency by 100 (so Fxosc * 100 = 100*12MHz = 1200MHz))
REG_WRITE(PLL_USB_PRIM, 5<<16|5<<12); // Post divider divide output frequency by 5 then 5 (totally 25) thus Ffinal=1200MHz/25 = 48MHz
REG_WRITE_BITMAP_CLEAR(PLL_USB_PWR, 1<<5|1<<3|1<<0); // Turn on VCO + PLL + fbdiv
while(!(REG_READ(PLL_USB_CS) & 1<<31)){} // Wait for USB PLL to be locked
// Enable USB clock generator
REG_WRITE(CLOCKS_USB_CTRL,1<<11);
// Last step, use XOSC for clock ref
REG_WRITE(CLOCKS_REF_CTRL, 2); // This is glitchless
}

9
src/libs/clock.h Normal file
View file

@ -0,0 +1,9 @@
#ifndef __CLOCK_H__
#define __CLOCK_H__
#include "types.h"
void xosc_init();
void wait(u32 ms); // Did not test much that one
#endif

39
src/libs/gpio.c Normal file
View file

@ -0,0 +1,39 @@
#include "addrmap.h"
#include "utils.h"
#include "gpio.h"
void gpio_init() { REG_WRITE_BITMAP_CLEAR(RESETS_BASE, 32);} // Reset bank0 (all gpios)
void gpio_set_function(u8 gpio, u8 fn){
REG_WRITE(IO_BANK0_GPIO25_CTRL, fn); // Set function as SIO
}
void gpio_output_enable(u8 gpio){
u32 r=REG_READ(SIO_GPIO_OE)|1<<gpio;
REG_WRITE(SIO_GPIO_OE, r);
}
void gpio_toggle_state(u8 gpio){
REG_WRITE(SIO_GPIO_OUT_XOR,1<<gpio);
}
void gpio_toggle_led(){
// Ensure led pin is configured
u32 fn=REG_READ(IO_BANK0_GPIO25_CTRL);
u32 oe=REG_READ(SIO_GPIO_OE)&1<<25;
if(fn!=GPIO_FN_SIO || !oe){
gpio_set_function(25, GPIO_FN_SIO);
gpio_output_enable(25);
}
gpio_toggle_state(25); // Switch between ON/OFF
}
void gpio_blink_led(int n){
for(int i=0;i<n;i++){
gpio_toggle_led();
for(int j=0;j<5000000;j++){} // These are disgusting but I am lazy..
gpio_toggle_led();
for(int j=0;j<5000000;j++){}
}
}

22
src/libs/gpio.h Normal file
View file

@ -0,0 +1,22 @@
#ifndef __GPIO_H__
#define __GPIO_H__
#include "types.h"
#define GPIO_FN_SPI 1
#define GPIO_FN_UART 2
#define GPIO_FN_I2C 3
#define GPIO_FN_PWM 4
#define GPIO_FN_SIO 5
#define GPIO_FN_PIO0 6
#define GPIO_FN_PIO1 7
#define GPIO_FN_USB 8
void gpio_init();
void gpio_set_function(u8 gpio, u8 fn);
void gpio_output_enable(u8 gpio);
void gpio_toggle_state(u8 gpio);
void gpio_toggle_led();
void gpio_blink_led(int n);
#endif

67
src/libs/interrupts.c Normal file
View file

@ -0,0 +1,67 @@
#include "interrupts.h"
#include "addrmap.h"
#include "bitmap.h"
#include "utils.h"
#include "gpio.h"
#include "usb/cusb.h"
extern u32 cores_systick[];
void isr_unbind(void){}
void isr_systick(void){
cores_systick[0]++; // Core 0
cores_systick[1]++; // Core 1
}
void isr_usb(){
if(cusb_check_interrupt(BIT_USBCTRL_INTS_SETUP_REQ)){
REG_WRITE_BITMAP_CLEAR(USBCTRL_SIE_STATUS, BIT_USBCTRL_SIE_STATUS_SETUP_REC);
cusb_handle_setup();
}
if(cusb_check_interrupt(BIT_USBCTRL_INTS_BUS_RESET)){
REG_WRITE_BITMAP_CLEAR(USBCTRL_SIE_STATUS, BIT_USBCTRL_SIE_STATUS_BUS_RESET);
cusb_handle_bus_reset();
}
if(cusb_check_interrupt(BIT_USBCTRL_INTS_BUFFER_STATUS)){
cusb_handle_buffer_status();
}
cusb_eoi();
}
__attribute__((used,aligned(4),section(".vector_table"))) void (*vtable[])(void) = {
(void (*)(void))SRAM_END,
// ----- Start of internal (to core) interrupts
isr_unbind, // 01-NMI
isr_unbind, // 02-HardFault
isr_unbind, // 03-Unused
isr_unbind, // 04-Unused
isr_unbind, // 05-Unused
isr_unbind, // 06-Unused
isr_unbind, // 07-Unused
isr_unbind, // 08-Unused
isr_unbind, // 09-Unused
isr_unbind, // 10-Unused
isr_unbind, // 11-SVCall
isr_unbind, // 12-Unused
isr_unbind, // 13-Unused
isr_unbind, // 14-PendSV
isr_systick, // 15-SysTick
// ----- Start of external (RP2040 ones) interrupts
isr_unbind, // 00-Unused
isr_unbind, // 01-Unused
isr_unbind, // 02-Unused
isr_unbind, // 03-Unused
isr_unbind, // 04-Unused
isr_usb // 05-Usb Controller
};
void interrupts_init(){
memcpy((void*)SRAM_BASE, (void*)vtable, 4*30);
REG_WRITE(PPB_VTOR, SRAM_BASE);
}
void interrupts_enable(int num){
REG_WRITE(PPB_NVIC_ICPR, 1<<num); // Clear pending interrupts
REG_WRITE(PPB_NVIC_ISER, 1<<num); // Enable num interrupts
}

7
src/libs/interrupts.h Normal file
View file

@ -0,0 +1,7 @@
#ifndef __INTERRUPT_H__
#define __INTERRUPT_H__
void interrupts_init();
void interrupts_enable(int num);
#endif

25
src/libs/lock.c Normal file
View file

@ -0,0 +1,25 @@
#include "lock.h"
#include "utils.h"
#include "addrmap.h"
#include "time.h"
char locks_pool[LOCKS_POOL_SIZE]; // Automatically zeroed (.bss)
void lock_acquire(int lockid) {
while(1){
while(locks_pool[lockid]) {} // Lurking stage
while(!REG_READ(HARDWARE_LOCK)){} // Acquire HW lock
if(!locks_pool[lockid]){
locks_pool[lockid]=1; // Acquire the lock
REG_WRITE(HARDWARE_LOCK,0); // Release HW lock
break;
}
REG_WRITE(HARDWARE_LOCK,0);
}
}
void lock_release(int lockid) {
while(!REG_READ(HARDWARE_LOCK)){}
locks_pool[lockid]=0;
REG_WRITE(HARDWARE_LOCK,0);
}

10
src/libs/lock.h Normal file
View file

@ -0,0 +1,10 @@
#ifndef __LOCK_H__
#define __LOCK_H__
#define LOCKS_POOL_SIZE 10
#define HARDWARE_LOCK SIO_SPINLOCK31
void lock_acquire(int lockid);
void lock_release(int lockid);
#endif

27
src/libs/tty.c Normal file
View file

@ -0,0 +1,27 @@
#include "tty.h"
#include "gpio.h"
#include "usb/cdc-acm.h"
#include "usb/cusb.h"
#include "utils.h"
#include "addrmap.h"
void tty_init(void){
usb_cdc_acm_init();
}
char tty_getchar(void){
char c;
usb_cdc_acm_recv(&c, 1);
return c;
}
void tty_putchar(char c){
char str[2]="";
str[0]=c;
usb_cdc_acm_send(str, 2);
}
void tty_putstr(char *str){
usb_cdc_acm_send(str, strlen(str)+1);
}

9
src/libs/tty.h Normal file
View file

@ -0,0 +1,9 @@
#ifndef __TTY_H__
#define __TTY_H__
void tty_init(void);
char tty_getchar(void);
void tty_putchar(char c);
void tty_putstr(char *str);
#endif

8
src/libs/types.h Normal file
View file

@ -0,0 +1,8 @@
#ifndef __TYPES_H__
#define __TYPES_H__
typedef unsigned char u8;
typedef unsigned short u16;
typedef unsigned int u32;
#endif

184
src/libs/usb/cdc-acm.c Normal file
View file

@ -0,0 +1,184 @@
#include "cdc-acm.h"
#include "cusb.h"
#include "proto.h"
#include "../gpio.h"
#include "../addrmap.h"
void cdc_acm_ep1_in_handler(u8 *buffer, int len);
void cdc_acm_ep2_in_handler(u8 *buffer, int len);
void cdc_acm_ep3_out_handler(u8 *buffer, int len);
void cdc_acm_setup_handler(UD_SETUP *pkt);
void cdc_acm_setup_ack_handler(void);
void cdc_acm_eoi(void);
u8 configuration_descriptor[128];
u8 *send_buffer;
u8 send_buffer_size=0;
u8 *recv_buffer;
u8 recv_buffer_size=0;
DeviceConfiguration CDC_ACM_CONFIG ={
.device_descriptor = {
.bDeviceClass = USB_DEVICE_CLASS_CDC,
.idProduct = 1,
.iManufacturer = 1, // String id 1
.iProduct = 2 // String id 2
},
.configuration_descriptor = {
.bNumInterfaces = 2
},
.interface_descriptors = {
{
.bInterfaceClass = USB_INTERFACE_CLASS_COMMUNICATION,
.bInterfaceSubClass = USB_INTERFACE_SUBCLASS_ACM,
.bInterfaceProtocol = USB_INTERFACE_COMMUNICATION_PROTOCOL_NOPROTO,
.bInterfaceNumber = 0,
.bNumEndpoints = 1 // SHOULD BE 1 (IT IS 0 BECAUSE GET_CONFIG DESCRIPTOR MUST BE UPDATED)
},
{
.bInterfaceClass = USB_INTERFACE_CLASS_DATA,
.bInterfaceProtocol = USB_INTERFACE_COMMUNICATION_PROTOCOL_NOPROTO,
.bInterfaceNumber = 1,
.bNumEndpoints = 2
}
},
.endpoints = {
{
.endpoint_descriptor = {
.bEndpointAddress = B_ENDPOINT_ADDRESS(1, USB_DIRECTION_IN),
.wMaxPacketSize=64,
.bmAttributes = USB_TRANSFERT_TYPE_INTERRUPT,
.bInterval = 0
},
.handler = cdc_acm_ep1_in_handler
},
{
.endpoint_descriptor = {
.bEndpointAddress = B_ENDPOINT_ADDRESS(2, USB_DIRECTION_IN),
.wMaxPacketSize=64,
.bmAttributes = USB_TRANSFERT_TYPE_BULK,
.bInterval = 0
},
.handler = cdc_acm_ep2_in_handler
},
{
.endpoint_descriptor = {
.bEndpointAddress = B_ENDPOINT_ADDRESS(3, USB_DIRECTION_OUT),
.wMaxPacketSize=64,
.bmAttributes = USB_TRANSFERT_TYPE_BULK,
.bInterval = 0
},
.handler = cdc_acm_ep3_out_handler
},
},
.descriptor_strings = {
"Raspberry Pi", // Vendor
"Pico RP2040" // Product
},
.supported_languages = {
.wLANGID0 = USB_LANG_ENGLISH_US
},
.setup_handler = cdc_acm_setup_handler,
.setup_ack_handler = cdc_acm_setup_ack_handler,
.eoi=cdc_acm_eoi
};
void usb_cdc_acm_init(void) {
cusb_init(&CDC_ACM_CONFIG);
USB_CDC_ACM_FUNCTIONAL_DESCRIPTOR f;
f.header_descriptor.bFunctionLength=sizeof(USB_CDC_HEADER_FUNCTIONAL_DESCRIPTOR);
f.header_descriptor.bDescriptorType=0x24;
f.header_descriptor.bcdCDC=0b0110;
f.acm_descriptor.bFunctionLength=sizeof(USB_ACM_FUNCTIONAL_DESCRIPTOR);
f.acm_descriptor.bDescriptorType=0x24;
f.acm_descriptor.bDescriptorSubtype=0x02;
f.acm_descriptor.bmCapabilities=0xf;
f.union_descriptor.bFunctionLength=sizeof(USB_UNION_INTERFACE_DESCRIPTOR);
f.union_descriptor.bDescriptorType=0x24;
f.union_descriptor.bDescriptorSubtype=0x06;
f.union_descriptor.bMasterInterface=0;
f.union_descriptor.bSlaveInterface0=1;
// ----- Setting-up full configuration descriptor -----
u8 *ptr=configuration_descriptor;
// Configuration descriptor
memcpy(ptr,(void*) &(CDC_ACM_CONFIG.configuration_descriptor), sizeof(USB_CONFIGURATION_DESCRIPTOR));
ptr+=sizeof(USB_CONFIGURATION_DESCRIPTOR);
// Communication interface descriptor
memcpy(ptr,(void*) &(CDC_ACM_CONFIG.interface_descriptors[0]), sizeof(USB_INTERFACE_DESCRIPTOR));
ptr+=sizeof(USB_INTERFACE_DESCRIPTOR);
memcpy(ptr,(void*) &f, sizeof(USB_CDC_ACM_FUNCTIONAL_DESCRIPTOR)); // Functional descriptor
ptr+=sizeof(USB_CDC_ACM_FUNCTIONAL_DESCRIPTOR);
memcpy(ptr,(void*) &(CDC_ACM_CONFIG.endpoints[0].endpoint_descriptor), sizeof(USB_ENDPOINT_DESCRIPTOR)); // Endpoint descriptor
ptr+=sizeof(USB_ENDPOINT_DESCRIPTOR);
// Data interface descriptor
memcpy(ptr,(void*) &(CDC_ACM_CONFIG.interface_descriptors[1]), sizeof(USB_INTERFACE_DESCRIPTOR));
ptr+=sizeof(USB_INTERFACE_DESCRIPTOR);
memcpy(ptr,(void*) &(CDC_ACM_CONFIG.endpoints[1].endpoint_descriptor), sizeof(USB_ENDPOINT_DESCRIPTOR));
ptr+=sizeof(USB_ENDPOINT_DESCRIPTOR);
memcpy(ptr,(void*) &(CDC_ACM_CONFIG.endpoints[2].endpoint_descriptor), sizeof(USB_ENDPOINT_DESCRIPTOR));
ptr+=sizeof(USB_ENDPOINT_DESCRIPTOR);
// Setup full descriptor length
USB_CONFIGURATION_DESCRIPTOR *conf=(USB_CONFIGURATION_DESCRIPTOR*)configuration_descriptor;
conf->wTotalLength=ptr-configuration_descriptor;
// Finalize
CDC_ACM_CONFIG.full_configuration_descriptor=configuration_descriptor;
// Start receiving from host
cusb_start_xfer(0,64,&(CDC_ACM_CONFIG.endpoints[2]));
}
void cdc_acm_ep1_in_handler(u8 *buffer, int len) {}
void cdc_acm_ep2_in_handler(u8 *buffer, int len) {
send_buffer_size=0;
}
void cdc_acm_ep3_out_handler(u8 *buffer, int len) {
if(recv_buffer_size){
memcpy(recv_buffer, buffer, len);
recv_buffer_size=0; // Prevent further receive until usb_cdc_acm_recv() is called again
cusb_start_xfer(0,64,&(CDC_ACM_CONFIG.endpoints[2]));
}
}
void cdc_acm_setup_handler(UD_SETUP *pkt){
u8 direction=pkt->bmRequestType & BM_REQUEST_TYPE_DIR_BIT;
if(pkt->bRequest == USB_REQUEST_CODE_SET_LINE_CODING){
// TODO
}
else if(pkt->bRequest == USB_REQUEST_CODE_SET_CONTROL_LINE_STATE){
// TODO
}
else if(pkt->bRequest == USB_REQUEST_CODE_SEND_BREAK){
// TODO
}
else {
// TODO
}
}
void cdc_acm_setup_ack_handler(void){
}
void cdc_acm_eoi(void){
if(send_buffer_size)
cusb_start_xfer(send_buffer,send_buffer_size,&(CDC_ACM_CONFIG.endpoints[1]));
}
void usb_cdc_acm_send(char *data, int size){
send_buffer=data;
send_buffer_size=size;
cusb_isr_trigger();
while(send_buffer_size){};
}
void usb_cdc_acm_recv(char *data, int size){
recv_buffer=data;
recv_buffer_size=size;
while(recv_buffer_size){}
}

10
src/libs/usb/cdc-acm.h Normal file
View file

@ -0,0 +1,10 @@
#ifndef __CDC_ACM_H__
#define __CDC_ACM_H__
#include "proto.h"
void usb_cdc_acm_init(void);
void usb_cdc_acm_send(char *data, int size);
void usb_cdc_acm_recv(char *data, int size);
#endif

309
src/libs/usb/cusb.c Normal file
View file

@ -0,0 +1,309 @@
#include "cusb.h"
#include "../addrmap.h"
#include "../bitmap.h"
#include "../interrupts.h"
#include "../gpio.h"
#include "proto.h"
u8 xferbuff[64]; // Free to use for transfer (IN)
u8 *xferptr;
u8 xferremain=0;
DeviceConfiguration *cusb_dc;
void cusb_init(DeviceConfiguration *dc){
// Reset USB controller
REG_WRITE_BITMAP_CLEAR(RESETS_RESET, 1<<24); // Reset USBCTRL
while((REG_READ(RESETS_DONE) & 1<<24) == 0){} // Wait controller reset
// Flush USB Controller DPSRAM
memset((void*)USBCTRL_DPSRAM_BASE, 0, USBCTRL_DPSRAM_SIZE);
// Enable usb controller interrupt
interrupts_enable(5);
// Setup controller
REG_WRITE(USBCTRL_MUXING, BIT_USBCTRL_MUXING_TO_PHY|BIT_USBCTRL_MUXING_SOFTCON);
REG_WRITE(USBCTRL_PWR, BIT_USBCTRL_PWR_VBUS_DETECT|BIT_USBCTRL_PWR_VBUS_DETECT_OVERRIDE_EN);
REG_WRITE(USBCTRL_MAINCTRL, BIT_USBCTRL_MAIN_CTRL_CONTROLLER_EN); // Set device mode + enable controller
REG_WRITE(USBCTRL_SIE_CTRL, BIT_USBCTRL_SIE_CTRL_EP0_INT_1BUF); // Enable usb interrupts on EP0
REG_WRITE(USBCTRL_INTE, BIT_USBCTRL_INTE_SETUP_REQ|BIT_USBCTRL_INTE_BUS_RESET|BIT_USBCTRL_INTE_BUFF_STATUS); // Setup interrupt granularity
// Initialize device configuration
cusb_dc=dc; // Store controller device config
cusb_init_device_configuration();
// Initialize endpoints
for(int i=0;i<USB_ENDPOINT_COUNT;i++){
EndPointConfiguration *ep=&(cusb_dc->endpoints[i]);
u32 dir=ep->endpoint_descriptor.bEndpointAddress & 0x80;
u32 id=ep->endpoint_descriptor.bEndpointAddress & 0xF;
// Endpoint and buffer control
u32 endpoint_control=USBCTRL_EP1_ENDP_CTRL_IN+0x8*(id-1); // -1 since start at 1 (ep0 already covered in cusb_init_device_configuration())
u32 buffer_control=USBCTRL_EP1_BUFFER_CTRL_IN+0x8*(id-1);
if(dir == USB_DIRECTION_OUT){
endpoint_control+=4;
buffer_control+=4;
}
ep->endpoint_control=(void*)(endpoint_control);
ep->buffer_control=(void*)(buffer_control);
// Init buffers
u32 buffer0=USBCTRL_DATA_BUFFER_START+(64*2)*(id-1); // 64 bits aligned warning see p388
u32 buffer1=buffer0+64;
ep->buffer0=(void*)buffer0;
ep->buffer1=(void*)buffer1;
// Init ep control
u32 epctrl=BIT_USBCTRL_ENDP_CTRL_ENABLE |
BIT_USBCTRL_ENDP_CTRL_INT_PER_BUFF1 |
ep->endpoint_descriptor.bmAttributes << BIT_USBCTRL_ENDP_CTRL_TYPE_LSB|
USB_BUFFER_OFFSET(buffer0);
*ep->endpoint_control=epctrl; // Apply buffer control setting
ep->next_pid=0; // Maybe that is good?
}
// Pull-up usb line! (for the host :)
REG_WRITE_BITMAP_SET(USBCTRL_SIE_CTRL, BIT_USBCTRL_SIE_CTRL_PULLUP_EN);
}
void cusb_init_device_configuration(){
// Init device descriptor
cusb_dc->device_descriptor.bLength=sizeof(USB_DEVICE_DESCRIPTOR);
cusb_dc->device_descriptor.bDescriptorType=USB_DESCRIPTOR_TYPE_DEVICE;
cusb_dc->device_descriptor.bNumConfigurations = 1; // We support only 1
cusb_dc->device_descriptor.bcdUSB=BCD_USB;
cusb_dc->device_descriptor.bMaxPacketSize0=64;
// Init configuration descriptor
cusb_dc->configuration_descriptor.bLength=sizeof(USB_CONFIGURATION_DESCRIPTOR);
cusb_dc->configuration_descriptor.bDescriptorType=USB_DESCRIPTOR_TYPE_CONFIGURATION;
cusb_dc->configuration_descriptor.bConfigurationValue = 1; // Configuration id
cusb_dc->configuration_descriptor.iConfiguration = 0; // No string
cusb_dc->configuration_descriptor.bmAttributes = 0xC0; // attributes: self powered, no remote wakeup
cusb_dc->configuration_descriptor.bMaxPower = 0x32; // 100ma
// Init device state
cusb_dc->devaddr=0; // Just in case
cusb_dc->setdevaddr=0; // Just in case
cusb_dc->setdevaddr=0; // Just in case
// Init string zero descriptor
cusb_dc->supported_languages.bLength = sizeof(USB_STRING_DESCRIPTOR_ZERO);
cusb_dc->supported_languages.bDescriptorType = USB_DESCRIPTOR_TYPE_STRING;
// Init ep0 in
EndPointConfiguration *ep0_in= &(cusb_dc->endpoints[USB_ENDPOINT_COUNT]);
ep0_in->buffer0=(void*)USBCTRL_EP0_BUFFER0;
ep0_in->buffer1=(void*)USBCTRL_EP0_BUFFER0+0x40;
ep0_in->buffer_control=(void*)USBCTRL_EP0_BUFFER_CTRL_IN;
ep0_in->handler=cusb_ep0_in_handler;
ep0_in->endpoint_descriptor.bEndpointAddress = B_ENDPOINT_ADDRESS(0, USB_DIRECTION_IN);
ep0_in->endpoint_descriptor.bmAttributes = USB_TRANSFERT_TYPE_CONTROL;
ep0_in->endpoint_descriptor.wMaxPacketSize=64;
// Init ep0 out
EndPointConfiguration *ep0_out= &(cusb_dc->endpoints[USB_ENDPOINT_COUNT+1]);
ep0_out->buffer0=(void*)USBCTRL_EP0_BUFFER0;
ep0_out->buffer1=(void*)USBCTRL_EP0_BUFFER0+0x40;
ep0_out->buffer_control=(void*)USBCTRL_EP0_BUFFER_CTRL_OUT;
ep0_out->handler=cusb_ep0_out_handler;
ep0_out->endpoint_descriptor.bEndpointAddress = B_ENDPOINT_ADDRESS(0, USB_DIRECTION_OUT);
ep0_out->endpoint_descriptor.bmAttributes = USB_TRANSFERT_TYPE_CONTROL;
ep0_out->endpoint_descriptor.wMaxPacketSize=64;
// Init bLength
for(char i=0;i<USB_ENDPOINT_COUNT+2;i++){
cusb_dc->endpoints[i].endpoint_descriptor.bLength=sizeof(USB_ENDPOINT_DESCRIPTOR);
cusb_dc->endpoints[i].endpoint_descriptor.bDescriptorType=USB_DESCRIPTOR_TYPE_ENDPOINT;
}
// Init interfaces
for(char i=0;i<USB_INTERFACE_COUNT;i++){
cusb_dc->interface_descriptors[i].bLength=sizeof(USB_INTERFACE_DESCRIPTOR);
cusb_dc->interface_descriptors[i].bDescriptorType=USB_DESCRIPTOR_TYPE_INTERFACE;
}
}
int cusb_check_interrupt(int int_bit){
if(REG_READ(USBCTRL_INTS) & int_bit)
return 1;
return 0;
}
void cusb_handle_bus_reset(void){
// https://github.com/raspberrypi/pico-examples/blob/master/usb/device/dev_lowlevel/dev_lowlevel.c#L469
REG_WRITE(USBCTRL_ADDR_ENDP, 0); // Remove device address from controller
cusb_dc->devaddr=0; // No more device address
cusb_dc->setdevaddr=0; // Ensure no device address will be set
cusb_dc->configured=0;
}
EndPointConfiguration* cusb_get_endpoint(char num, u32 direction){
EndPointConfiguration *ep;
for(char i=0;i<(USB_ENDPOINT_COUNT+2);i++){
ep=&(cusb_dc->endpoints[i]);
u32 bEndpointAddress=ep->endpoint_descriptor.bEndpointAddress;
// Bit 7 (mask 0x80) is IN or OUT and first 4 bits is addr (see p269)
if((bEndpointAddress & 0x80) == direction && (bEndpointAddress & 0xF) == num)
return ep;
}
return 0;
}
void cusb_handle_setup(void){
UD_SETUP *pkt=(UD_SETUP*)USBCTRL_DPSRAM_SETUP_PACKET;
// Always responds with DATA1 PID
EndPointConfiguration *ep=cusb_get_endpoint(0, USB_DIRECTION_IN);
ep->next_pid=1;
u8 direction=pkt->bmRequestType & BM_REQUEST_TYPE_DIR_BIT;
if(direction == USB_DIRECTION_OUT){
if (pkt->bRequest == USB_REQUEST_CODE_SET_ADDRESS){
cusb_dc->devaddr= pkt->wValue & 0xff;
cusb_dc->setdevaddr=1;
// Since we should acknowledge (status phase) before setting the address
// we use setdevaddr boolean. When status done, buffer_status interrupt will be triggered
// and ep0_in_handler will set the address
cusb_status_xfer(USB_DIRECTION_IN);
} else if (pkt->bRequest == USB_REQUEST_CODE_SET_CONFIGURATION) {
cusb_status_xfer(USB_DIRECTION_IN);
cusb_dc->configured=1;
} else {
if(cusb_dc->setup_handler){
cusb_dc->setup_handler(pkt);
}
// Acknowledge whatever other requests
cusb_status_xfer(USB_DIRECTION_IN);
}
} else if(direction == USB_DIRECTION_IN){
if(pkt->bRequest == USB_REQUEST_CODE_GET_DESCRIPTOR){
int desc_type=pkt->wValue>>8; // See USB SPecification (wValue contains descriptor type+index)
if(desc_type == USB_DESCRIPTOR_TYPE_DEVICE ){
// Send descriptor
cusb_start_xfer(&cusb_dc->device_descriptor, sizeof(USB_DEVICE_DESCRIPTOR), ep);
} else if(desc_type == USB_DESCRIPTOR_TYPE_CONFIGURATION ){
USB_CONFIGURATION_DESCRIPTOR *conf=cusb_dc->full_configuration_descriptor;
int size=conf->bLength;
if(pkt->wLength > size)
size=conf->wTotalLength;
// Send descriptors!!
cusb_start_xfer(conf, size, ep);
} else if(desc_type == USB_DESCRIPTOR_TYPE_STRING ){
u8 id = pkt->wValue & 0xff; // Get string id
if(id==0){ // This is the special string descriptor for supported language
cusb_start_xfer(&(cusb_dc->supported_languages), sizeof(USB_STRING_DESCRIPTOR_ZERO), ep);
}
else {
char *str=cusb_dc->descriptor_strings[id-1]; // Remember id 0 taken by ZERO DESCriptor
u8 *ptr=xferbuff;
USB_UNICODE_STRING_DESCRIPTOR *u=(USB_UNICODE_STRING_DESCRIPTOR*)ptr;
u->bLength = sizeof(USB_UNICODE_STRING_DESCRIPTOR);
u->bDescriptorType=USB_DESCRIPTOR_TYPE_STRING;
char c;
ptr+=sizeof(USB_UNICODE_STRING_DESCRIPTOR); // String first 2 descriptor entries
do {
c = *str++;
*ptr++ = c;
*ptr++ = 0; // Unicode
u->bLength+=2; // Unicode
} while(c != '\0');
cusb_start_xfer(xferbuff, u->bLength, ep);
}
}
else {
cusb_dc->setup_handler(pkt);
}
}
}
}
void cusb_handle_buffer_status(void){
u32 status=REG_READ(USBCTRL_BUFF_STATUS);
for(u8 num=0;num<16;num++){
for(u8 i=0;i<2;i++){
u32 bit=1u << (num*2+i); // Shift register for IN and OUT for endpoint num
if(status & bit){
u32 dir=i ? USB_DIRECTION_OUT : USB_DIRECTION_IN;
EndPointConfiguration *ep=cusb_get_endpoint(num,dir);
REG_WRITE_BITMAP_CLEAR(USBCTRL_BUFF_STATUS, bit); // Clear buffer status
ep->handler((u8*)ep->buffer0,*(ep->buffer_control)&MASK_USBCTRL_BUFFER0_LENGTH);
}
}
}
}
void cusb_status_xfer(u32 dir){
// If dir == USB_DIRECTION_OUT
// controller will receive the out token from host then send acknowledgement
// otherwise if we do not perform xfer when receiving out token, then controller do not send acknowledgement which do not complete the control transaction
// If dir == USB_DIRECTION_IN
// controller will receive the in token from host then send zlp and receive acknowledgement from host
EndPointConfiguration *ep=cusb_get_endpoint(0, dir);
cusb_start_xfer(0,0,ep);
}
void cusb_ep0_in_handler(u8 *buffer, int len) {
// This function is trigger when buff_status interrupt is handled
// it is called inside cusb_handle_buffer_status
if(cusb_dc->setdevaddr){
REG_WRITE(USBCTRL_ADDR_ENDP, cusb_dc->devaddr);
cusb_dc->setdevaddr=0;
}
else if(xferremain>0){
EndPointConfiguration *ep=cusb_get_endpoint(0, USB_DIRECTION_IN);
cusb_start_xfer(xferptr,xferremain,ep);
}
else {
cusb_status_xfer(USB_DIRECTION_OUT); // Acknowledge with zlp when setup transaction ends
}
}
void cusb_ep0_out_handler(u8 *buffer, int len) {
// This function is trigger when buff_status interrupt is handled
// it is called inside cusb_handle_buffer_status
}
void cusb_start_xfer(void* data, u32 size, EndPointConfiguration *ep){
u32 buffer_ctrl = size; // Set data size
buffer_ctrl|=ep->next_pid ? BIT_USBCTRL_BUFFER0_PID : 0; // Set DATA0 or DATA1
ep->next_pid ^= 1u; // For next transfert
// Move user data to usb buffer if needed
u32 direction=ep->endpoint_descriptor.bEndpointAddress & 0x80;
if(direction == USB_DIRECTION_IN){
memcpy((void *)ep->buffer0, data, size);
buffer_ctrl |= BIT_USBCTRL_BUFFER0_FULL; // Set buffer full for controller
// Support for data > 64 bytes (see also cusb_ep0_in_handler())
if(size>64){
xferremain=size-64;
xferptr=((u8*) data)+64;
}
else {
xferremain=0;
}
}
// Setup buffer (not available bit yet)
*(ep->buffer_control)=buffer_ctrl;
// Now do available bit (because USB and cores have different clock speed (race condition see p389)
asm volatile("nop;nop;nop;"); // ceil(125MHz/48MHz) Should wait 3 cycles see warning p392
*(ep->buffer_control)=buffer_ctrl| BIT_USBCTRL_BUFFER0_AVAILABLE; // Apply available bit
}
void cusb_eoi(){
if(cusb_dc->eoi)
cusb_dc->eoi();
}
void cusb_isr_trigger(){
REG_WRITE(PPB_NVIC_ISPR, 1u<<5); // Trigger interrupt 5 (USB Controller)
}

72
src/libs/usb/cusb.h Normal file
View file

@ -0,0 +1,72 @@
#ifndef __USB_H__
#define __USB_H__
#include "../types.h"
#include "../utils.h"
#include "proto.h"
#define USBCTRL_DPSRAM_SIZE (1024*4) // See p122
#define USBCTRL_BUFFER_SIZE 64 // See p385
#define USB_ENDPOINT_COUNT 3 // Number of endpoints for the device driver
#define USB_INTERFACE_COUNT 2 // Number of interfaces for the device driver
#define BCD_USB 0x0110
#define USB_BUFFER_OFFSET(ADDR) (ADDR ^ USBCTRL_DPSRAM_BASE) // See here https://github.com/raspberrypi/pico-examples/blob/master/usb/device/dev_lowlevel/dev_lowlevel.c#L141
typedef struct EndPoint {
void *buffer0;
void *buffer1;
void *rbc; // EP control register
volatile u32 *rbc_in; // EP buffer control in register
volatile u32 *rbc_out; // EP buffer control out register
int next_pid;
int is_in;
} EndPoint;
typedef struct EndPointConfiguration{
USB_ENDPOINT_DESCRIPTOR endpoint_descriptor;
volatile u32* buffer0;
volatile u32* buffer1;
volatile u32* buffer_control;
volatile u32* endpoint_control;
u32 next_pid; // Next usb pkt pid (DATA0/DATA1)
void (*handler)(u8*,int);
} EndPointConfiguration;
typedef struct FunctionalDescriptors {
int size;
void* descriptors;
} FunctionalDescriptors;
typedef struct DeviceConfiguration {
u8 devaddr; // Current device address
u8 setdevaddr; // Set to one when devaddr is available
u8 configured; // Set to true when host triggered SET_CONFIGURATION
USB_DEVICE_DESCRIPTOR device_descriptor;
USB_CONFIGURATION_DESCRIPTOR configuration_descriptor;
USB_INTERFACE_DESCRIPTOR interface_descriptors[USB_INTERFACE_COUNT];
EndPointConfiguration endpoints[2+USB_ENDPOINT_COUNT]; // Endpoint 0 in and out always required so +2
USB_STRING_DESCRIPTOR_ZERO supported_languages;
FunctionalDescriptors functional_descriptors;
void *full_configuration_descriptor;
void (*setup_handler)(UD_SETUP *pkt); // Notify the driver in case of unhandled setup packet
void (*setup_ack_handler)(void); // Notify the driver
void (*eoi)(void); // Notify the driver for End Of Interrupt
char *descriptor_strings[]; // All string like vendor and product id
} DeviceConfiguration;
void cusb_init(DeviceConfiguration *dc);
void cusb_init_device_configuration();
void cusb_ep0_in_handler(u8 *buffer, int len);
void cusb_ep0_out_handler(u8 *buffer, int len);
void cusb_handle_setup(void);
void cusb_handle_bus_reset(void);
void cusb_handle_buffer_status(void);
int cusb_check_interrupt(int int_bit);
void usb_start_xfer(void* data, u32 size, int ep);
void cusb_start_xfer(void *data, u32 size, EndPointConfiguration *ep);
void cusb_status_xfer(u32 dir);
void cusb_eoi(); // Trigger at end of each USB interrupt
void cusb_isr_trigger(); // Manually trigger an usb interrupt
#endif

227
src/libs/usb/proto.h Normal file
View file

@ -0,0 +1,227 @@
#ifndef __PROTO_H__
#define __PROTO_H__
#include "../types.h"
// ---------- USB Protocol ----------
#define USB_DATA typedef struct __attribute__((__packed__))
// USB token PIDs
#define USB_PID_OUT 0b0001
#define USB_PID_IN 0b1001
#define USB_PID_SOF 0b0101
#define USB_PID_SETUP 0b1101
// USB data PIDs
#define USB_PID_DATA0 0b0011
#define USB_PID_DATA1 0b1011
#define USB_PID_DATA2 0b0111
#define USB_PID_MDATA 0b1111
// Standard Requests Code (bRequest)
#define USB_REQUEST_CODE_GET_STATUS 0
#define USB_REQUEST_CODE_CLEAR_FEATURE 1
#define USB_REQUEST_CODE_SET_FEATURE 3
#define USB_REQUEST_CODE_SET_ADDRESS 5
#define USB_REQUEST_CODE_GET_DESCRIPTOR 6
#define USB_REQUEST_CODE_SET_DESCRIPTOR 7
#define USB_REQUEST_CODE_GET_CONFIGURATION 8
#define USB_REQUEST_CODE_SET_CONFIGURATION 9
#define USB_REQUEST_CODE_GET_INTERFACE 10
#define USB_REQUEST_CODE_SET_INTERFACE 11
#define USB_REQUEST_CODE_SYNCH_FRAME 12
// Descriptor Types (wValue)
#define USB_DESCRIPTOR_TYPE_DEVICE 1
#define USB_DESCRIPTOR_TYPE_CONFIGURATION 2
#define USB_DESCRIPTOR_TYPE_STRING 3
#define USB_DESCRIPTOR_TYPE_INTERFACE 4
#define USB_DESCRIPTOR_TYPE_ENDPOINT 5
#define USB_DESCRIPTOR_TYPE_DEVICE_QUALIFIER 6
#define USB_DESCRIPTOR_TYPE_OTHER_SPEED_CONFIGURATION 7
#define USB_DESCRIPTOR_TYPE_INTERFACE_POWER 8
// Feature Selector (wvalue)
#define USB_FEATURE_SELECTOR_DEVICE_REMOTE_WAKEUP 1
#define USB_FEATURE_SELECTOR_ENDPOINT_HALT 0
#define USB_FEATURE_SELECTOR_TEST_MODE 2
// Device class code https://developerhelp.microchip.com/xwiki/bin/view/applications/usb/how-it-works/device-classes/
#define USB_DEVICE_CLASS_DEVICE 0x00
#define USB_DEVICE_CLASS_AUDIO 0x01
#define USB_DEVICE_CLASS_CDC 0x02
#define USB_DEVICE_CLASS_HID 0x03
#define USB_DEVICE_CLASS_PHYSICAL 0x05
#define USB_TRANSFERT_TYPE_CONTROL 0b00
#define USB_TRANSFERT_TYPE_ISOCHRONOUS 0b01
#define USB_TRANSFERT_TYPE_BULK 0b10
#define USB_TRANSFERT_TYPE_INTERRUPT 0b11
#define USB_DIRECTION_OUT 0x00
#define USB_DIRECTION_IN 0x80
// Supported languages http://www.baiheee.com/Documents/090518/090518112619/USB_LANGIDs.pdf
#define USB_LANG_ENGLISH_US 0x0409
#define USB_LANG_ENGLISH_UNITED_KINGDOM 0x0809
#define B_ENDPOINT_ADDRESS(NUM, DIR) ((NUM)&0xF | (DIR))
#define BM_REQUEST_TYPE_DIR_BIT (USB_DIRECTION_IN)
#define USB_INTERFACE_CLASS_COMMUNICATION 0x02 // Communication interface class
#define USB_INTERFACE_CLASS_DATA 0x0a // Data interface class
#define USB_INTERFACE_SUBCLASS_ACM 0x02 // Abstract Control Mode
#define USB_INTERFACE_COMMUNICATION_PROTOCOL_NOPROTO 0x00 // No protocol
USB_DATA { // P250
u8 bmRequestType;
u8 bRequest;
u16 wValue;
u16 wIndex;
u16 wLength;
} UD_SETUP;
// ----- Descriptors -----
USB_DATA { // P262
u8 bLength;
u8 bDescriptorType;
u16 bcdUSB;
u8 bDeviceClass;
u8 bDeviceSubClass;
u8 bDeviceProtocol;
u8 bMaxPacketSize0;
u16 idVendor;
u16 idProduct;
u16 bcdDevice;
u8 iManufacturer;
u8 iProduct;
u8 iSerialNumber;
u8 bNumConfigurations;
} USB_DEVICE_DESCRIPTOR;
USB_DATA { // P264 (may not be used in this code)
u8 bLength;
u8 bDescriptorType;
u16 bcdUSB;
u8 bDeviceClass;
u8 bDeviceSubClass;
u8 bDeviceProtocol;
u8 bMaxPacketSize0;
u8 bNumConfigurations;
u8 bReserved;
} USB_DEVICE_QUALIFIER_DESCRIPTOR;
USB_DATA { // P265
u8 bLength;
u8 bDescriptorType;
u16 wTotalLength;
u8 bNumInterfaces;
u8 bConfigurationValue;
u8 iConfiguration;
u8 bmAttributes;
u8 bMaxPower;
} USB_CONFIGURATION_DESCRIPTOR;
USB_DATA {
u8 bLength;
u8 bDescriptorType;
u16 wTotalLength;
u8 bNumInterfaces;
u8 bConfigurationValue;
u8 iConfiguration;
u8 bmAttributes;
u8 bMaxPower;
} USB_OTHER_SPEED_CONFIGURATION_DESCRIPTOR;
USB_DATA {
u8 bLength;
u8 bDescriptorType;
u8 bInterfaceNumber;
u8 bAlternateSetting;
u8 bNumEndpoints;
u8 bInterfaceClass;
u8 bInterfaceSubClass;
u8 bInterfaceProtocol;
u8 iInterface;
} USB_INTERFACE_DESCRIPTOR;
USB_DATA {
u8 bLength;
u8 bDescriptorType;
u8 bEndpointAddress;
u8 bmAttributes;
u16 wMaxPacketSize;
u8 bInterval;
} USB_ENDPOINT_DESCRIPTOR;
USB_DATA {
u8 bLength;
u8 bDescriptorType;
u16 wLANGID0;
// Can be more wLANGIDX but stop here!
} USB_STRING_DESCRIPTOR_ZERO;
USB_DATA {
u8 bLength;
u8 bDescriptorType;
// You add your unicode string there
} USB_UNICODE_STRING_DESCRIPTOR;
// ----- CDC Device Class -----
// p10 PSTN120.pdf in CDC Specification
// p10-.. CDC120-20101103-track.pdf
// p10-.. usbcdc11.pdf
#define USB_INTERFACE_CLASS_COMMUNICATION 0x02 // Communication interface class
#define USB_INTERFACE_CLASS_DATA 0x0a // Data interface class
#define USB_INTERFACE_SUBCLASS_ACM 0x02 // Abstract Control Mode
#define USB_INTERFACE_COMMUNICATION_PROTOCOL_NOPROTO 0x00 // No protocol
#define USB_RS232_BMCAPABILITIES 0b10
#define USB_REQUEST_CODE_SET_LINE_CODING 0x20
#define USB_REQUEST_CODE_SET_CONTROL_LINE_STATE 0x22
#define USB_REQUEST_CODE_SEND_BREAK 0x23
// p34
USB_DATA {
u8 bFunctionLength;
u8 bDescriptorType;
u8 bDescriptorSubtype;
u16 bcdCDC;
} USB_CDC_HEADER_FUNCTIONAL_DESCRIPTOR;
// p35
USB_DATA {
u8 bFunctionLength;
u8 bDescriptorType;
u8 bDescriptorSubtype;
u8 bmCapabilities;
} USB_ACM_FUNCTIONAL_DESCRIPTOR;
// p40
USB_DATA {
u8 bFunctionLength;
u8 bDescriptorType;
u8 bDescriptorSubtype;
u8 bMasterInterface;
u8 bSlaveInterface0;
// Can be more! I guess for RS-232 it is enough
} USB_UNION_INTERFACE_DESCRIPTOR;
// SEE p49!!!! it is all there it seems
// the last descriptor on that page is the "Call management functional
// descriptor" (p34). It seems not required according to chatgpt for rs-232
USB_DATA {
USB_CDC_HEADER_FUNCTIONAL_DESCRIPTOR header_descriptor;
USB_ACM_FUNCTIONAL_DESCRIPTOR acm_descriptor;
USB_UNION_INTERFACE_DESCRIPTOR union_descriptor;
} USB_CDC_ACM_FUNCTIONAL_DESCRIPTOR;
#endif

53
src/libs/utils.c Normal file
View file

@ -0,0 +1,53 @@
#include "utils.h"
void memcpy(u8 *dst, u8 *src, u32 size){
for(u32 i=0;i<size;i++){
((u8*)(dst))[i]=((u8*)(src))[i];
}
}
void memset(u8 *start, u8 value, u32 size){
for(u32 i=0;i<size;i++){
((u8*)(start))[i]=value;
}
}
int modulo(int dividend, int divisor){
while((dividend-divisor)>0){dividend-=divisor;}
return dividend;
}
int strlen(char * cp)
{
int len=0;
while( cp[len++]) ;
return len - 1 ; //because it counted the zero which we don't want.
}
int wordlen(char *s)
{
int len=0;
while( *s!='\n' && *s!='\0' && *s!=' '){ s++; }
return len - 1 ; //because it counted the zero which we don't want.
}
u8 strcmp(char *str1, char*str2){
while( *str1!='\0' && *str2!='\0'){
if(*str1!=*str2)
return 1;
str1++;
str2++;
}
return *str1 == *str2; // Ensure both final char are \0
}
u8 strncmp(char *str1, char *str2, int n){
for(int i=0;i<n;i++){
if(str1[i] == '\0' || str2[i] == '\0')
break;
if(str1[i] != str2[i])
return 1;
}
return *str1 != *str2;
}

30
src/libs/utils.h Normal file
View file

@ -0,0 +1,30 @@
#ifndef __UTILS__
#define __UTILS__
#include "types.h"
// General operations
#define STR32(ADDR,VALUE) *((volatile unsigned int*) (ADDR))=(VALUE)
#define LDR32(ADDR) (*((volatile unsigned int*) (ADDR)))
// Registers operations
#define REG_WRITE(ADDR,VALUE) STR32(ADDR,VALUE)
#define REG_WRITE_XOR(ADDR,VALUE) STR32(ADDR+0x1000,VALUE)
#define REG_WRITE_BITMAP_SET(ADDR,VALUE) STR32(ADDR+0x2000,VALUE)
#define REG_WRITE_BITMAP_CLEAR(ADDR,VALUE) STR32(ADDR+0x3000,VALUE)
#define REG_READ(ADDR) LDR32(ADDR)
// Memory operations
void memcpy(u8 *dst, u8 *src, u32 size); // TODO: Improve perf with 32bits memory transactions
void memset(u8 *start, u8 value, u32 size); // TODO: Improve perf with 32bits memory transactions
// Computations
int modulo(int dividend, int divisor); // Assumes that both argument are positve (TODO improve this)
// Strings (not these functions do not follow libc standards and are buggy)
int strlen(char * cp);
int wordlen(char *s);
u8 strcmp(char *str1, char*str2);
u8 strncmp(char *str1, char*str2, int n);
#endif

60
src/main.c Normal file
View file

@ -0,0 +1,60 @@
#include "libs/tty.h"
#include "libs/utils.h"
#include "libs/gpio.h"
#include "libs/interrupts.h"
#include "libs/clock.h"
#define MOTD "WELCOME TO RP2040 TTY\n\n\r"
#define PROMPT "rp2040> "
#define HELP "\tblink\t\tmake led blink for almost 1s\n\r"
char cmd[64];
char *cmdptr=cmd;
// Execute command from cmd buffer
void exec(){
if(!strncmp(cmd, "blink", strlen("exit")))
gpio_blink_led(1);
else if(!strncmp(cmd, "help", strlen("help")))
tty_putstr(HELP);
else if(cmdptr != cmd)
tty_putstr("Unknown command (see help)\n\r");
}
void main(){
// Finishing boot
interrupts_init();
xosc_init();
gpio_init();
tty_init();
// REPL
char c=tty_getchar();
tty_putstr(MOTD);
tty_putstr(PROMPT);
while(1){
c=tty_getchar();
if(c=='\r'){
tty_putstr("\n\r");
exec();
tty_putstr(PROMPT);
cmdptr=cmd;
}
else if (c ==0x8){
if(cmdptr-cmd){
tty_putstr("\b \b"); // Erase last character
cmdptr--;
}
}
else{
*cmdptr=c;
cmdptr++;
tty_putchar(c);
}
}
return;
}