diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/boot/boot2.S | 38 | ||||
| -rw-r--r-- | src/boot/crt0.S | 40 | ||||
| -rw-r--r-- | src/libs/addrmap.h | 87 | ||||
| -rw-r--r-- | src/libs/bitmap.h | 42 | ||||
| -rw-r--r-- | src/libs/clock.c | 57 | ||||
| -rw-r--r-- | src/libs/clock.h | 9 | ||||
| -rw-r--r-- | src/libs/gpio.c | 39 | ||||
| -rw-r--r-- | src/libs/gpio.h | 22 | ||||
| -rw-r--r-- | src/libs/interrupts.c | 67 | ||||
| -rw-r--r-- | src/libs/interrupts.h | 7 | ||||
| -rw-r--r-- | src/libs/lock.c | 25 | ||||
| -rw-r--r-- | src/libs/lock.h | 10 | ||||
| -rw-r--r-- | src/libs/tty.c | 27 | ||||
| -rw-r--r-- | src/libs/tty.h | 9 | ||||
| -rw-r--r-- | src/libs/types.h | 8 | ||||
| -rw-r--r-- | src/libs/usb/cdc-acm.c | 184 | ||||
| -rw-r--r-- | src/libs/usb/cdc-acm.h | 10 | ||||
| -rw-r--r-- | src/libs/usb/cusb.c | 309 | ||||
| -rw-r--r-- | src/libs/usb/cusb.h | 72 | ||||
| -rw-r--r-- | src/libs/usb/proto.h | 227 | ||||
| -rw-r--r-- | src/libs/utils.c | 53 | ||||
| -rw-r--r-- | src/libs/utils.h | 30 | ||||
| -rw-r--r-- | src/main.c | 60 |
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; +} |
