summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/boot/boot2.S38
-rw-r--r--src/boot/crt0.S40
-rw-r--r--src/libs/addrmap.h87
-rw-r--r--src/libs/bitmap.h42
-rw-r--r--src/libs/clock.c57
-rw-r--r--src/libs/clock.h9
-rw-r--r--src/libs/gpio.c39
-rw-r--r--src/libs/gpio.h22
-rw-r--r--src/libs/interrupts.c67
-rw-r--r--src/libs/interrupts.h7
-rw-r--r--src/libs/lock.c25
-rw-r--r--src/libs/lock.h10
-rw-r--r--src/libs/tty.c27
-rw-r--r--src/libs/tty.h9
-rw-r--r--src/libs/types.h8
-rw-r--r--src/libs/usb/cdc-acm.c184
-rw-r--r--src/libs/usb/cdc-acm.h10
-rw-r--r--src/libs/usb/cusb.c309
-rw-r--r--src/libs/usb/cusb.h72
-rw-r--r--src/libs/usb/proto.h227
-rw-r--r--src/libs/utils.c53
-rw-r--r--src/libs/utils.h30
-rw-r--r--src/main.c60
23 files changed, 1432 insertions, 0 deletions
diff --git a/src/boot/boot2.S b/src/boot/boot2.S
new file mode 100644
index 0000000..0b61084
--- /dev/null
+++ b/src/boot/boot2.S
@@ -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
diff --git a/src/boot/crt0.S b/src/boot/crt0.S
new file mode 100644
index 0000000..7935a6e
--- /dev/null
+++ b/src/boot/crt0.S
@@ -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
diff --git a/src/libs/addrmap.h b/src/libs/addrmap.h
new file mode 100644
index 0000000..4de7b80
--- /dev/null
+++ b/src/libs/addrmap.h
@@ -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
diff --git a/src/libs/bitmap.h b/src/libs/bitmap.h
new file mode 100644
index 0000000..ab3d975
--- /dev/null
+++ b/src/libs/bitmap.h
@@ -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
diff --git a/src/libs/clock.c b/src/libs/clock.c
new file mode 100644
index 0000000..e5b493b
--- /dev/null
+++ b/src/libs/clock.c
@@ -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
+}
diff --git a/src/libs/clock.h b/src/libs/clock.h
new file mode 100644
index 0000000..657999e
--- /dev/null
+++ b/src/libs/clock.h
@@ -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
diff --git a/src/libs/gpio.c b/src/libs/gpio.c
new file mode 100644
index 0000000..9d61d85
--- /dev/null
+++ b/src/libs/gpio.c
@@ -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++){}
+ }
+}
diff --git a/src/libs/gpio.h b/src/libs/gpio.h
new file mode 100644
index 0000000..b9d2bb6
--- /dev/null
+++ b/src/libs/gpio.h
@@ -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
diff --git a/src/libs/interrupts.c b/src/libs/interrupts.c
new file mode 100644
index 0000000..371a417
--- /dev/null
+++ b/src/libs/interrupts.c
@@ -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
+}
diff --git a/src/libs/interrupts.h b/src/libs/interrupts.h
new file mode 100644
index 0000000..7360c20
--- /dev/null
+++ b/src/libs/interrupts.h
@@ -0,0 +1,7 @@
+#ifndef __INTERRUPT_H__
+#define __INTERRUPT_H__
+
+void interrupts_init();
+void interrupts_enable(int num);
+
+#endif
diff --git a/src/libs/lock.c b/src/libs/lock.c
new file mode 100644
index 0000000..7b65a69
--- /dev/null
+++ b/src/libs/lock.c
@@ -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);
+}
diff --git a/src/libs/lock.h b/src/libs/lock.h
new file mode 100644
index 0000000..979328e
--- /dev/null
+++ b/src/libs/lock.h
@@ -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
diff --git a/src/libs/tty.c b/src/libs/tty.c
new file mode 100644
index 0000000..7eed0a2
--- /dev/null
+++ b/src/libs/tty.c
@@ -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);
+}
diff --git a/src/libs/tty.h b/src/libs/tty.h
new file mode 100644
index 0000000..64970b9
--- /dev/null
+++ b/src/libs/tty.h
@@ -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
diff --git a/src/libs/types.h b/src/libs/types.h
new file mode 100644
index 0000000..1a0b0d2
--- /dev/null
+++ b/src/libs/types.h
@@ -0,0 +1,8 @@
+#ifndef __TYPES_H__
+#define __TYPES_H__
+
+typedef unsigned char u8;
+typedef unsigned short u16;
+typedef unsigned int u32;
+
+#endif
diff --git a/src/libs/usb/cdc-acm.c b/src/libs/usb/cdc-acm.c
new file mode 100644
index 0000000..711bc99
--- /dev/null
+++ b/src/libs/usb/cdc-acm.c
@@ -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){}
+}
diff --git a/src/libs/usb/cdc-acm.h b/src/libs/usb/cdc-acm.h
new file mode 100644
index 0000000..6262ee8
--- /dev/null
+++ b/src/libs/usb/cdc-acm.h
@@ -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
diff --git a/src/libs/usb/cusb.c b/src/libs/usb/cusb.c
new file mode 100644
index 0000000..249945a
--- /dev/null
+++ b/src/libs/usb/cusb.c
@@ -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)
+}
diff --git a/src/libs/usb/cusb.h b/src/libs/usb/cusb.h
new file mode 100644
index 0000000..b751dd5
--- /dev/null
+++ b/src/libs/usb/cusb.h
@@ -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
diff --git a/src/libs/usb/proto.h b/src/libs/usb/proto.h
new file mode 100644
index 0000000..d313cb5
--- /dev/null
+++ b/src/libs/usb/proto.h
@@ -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
diff --git a/src/libs/utils.c b/src/libs/utils.c
new file mode 100644
index 0000000..75b5661
--- /dev/null
+++ b/src/libs/utils.c
@@ -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;
+}
diff --git a/src/libs/utils.h b/src/libs/utils.h
new file mode 100644
index 0000000..140f1fd
--- /dev/null
+++ b/src/libs/utils.h
@@ -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
diff --git a/src/main.c b/src/main.c
new file mode 100644
index 0000000..b26543d
--- /dev/null
+++ b/src/main.c
@@ -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;
+}