Rename MCU
This commit is contained in:
parent
ed2925a961
commit
64a01bef05
75 changed files with 4 additions and 4 deletions
13
stm32f103t8u6-bootloader/src/buildinfo.h
Normal file
13
stm32f103t8u6-bootloader/src/buildinfo.h
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define BUILD_VERSION_MAJOR 0
|
||||
#define BUILD_VERSION_MINOR 2
|
||||
#define BUILD_VERSION_PATCH 0
|
||||
|
||||
#define BUILD_DATE ((uint32_t)&__BUILD_DATE)
|
||||
#define BUILD_NUMBER ((uint32_t)&__BUILD_NUMBER)
|
||||
|
||||
extern char __BUILD_DATE;
|
||||
extern char __BUILD_NUMBER;
|
||||
59
stm32f103t8u6-bootloader/src/commands.h
Normal file
59
stm32f103t8u6-bootloader/src/commands.h
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
#pragma once
|
||||
|
||||
// Legend:
|
||||
// -> is a transmission via endpoint 2 (out) from the host to the MCU
|
||||
// <- is a transmission via endpoint 1 (in) from the MCU to the host
|
||||
|
||||
typedef enum
|
||||
{
|
||||
// Does nothing
|
||||
CMD_NOP = 0x00,
|
||||
|
||||
// Basic bootloader info. Returns 19 to 64 bytes (cf. BootloaderInfo_t):
|
||||
// <- Build date: u32 (YYYYMMDD as an unsigned integer)
|
||||
// <- Build number: u32
|
||||
// <- Flash application base address: u32
|
||||
// <- Maximum application size: u32
|
||||
// <- Major version: u8
|
||||
// <- Minor version: u8
|
||||
// <- Patch version: u8
|
||||
// <- Identifier string (variable length, not zero-terminated)
|
||||
CMD_BOOTLOADER_INFO = 0x01,
|
||||
|
||||
// Calculate a CRC32 of a memory region:
|
||||
// -> Start address: u32
|
||||
// -> Length: u32
|
||||
// <- CRC32: u32
|
||||
CMD_READ_CRC = 0x02,
|
||||
|
||||
// Read memory contents
|
||||
// -> Start address: u32
|
||||
// -> Length n: u32
|
||||
// <- Data: n bytes
|
||||
CMD_READ_MEMORY = 0x03,
|
||||
|
||||
// Erase a single 1 KiB flash page
|
||||
// -> Page number: u8
|
||||
// <- Return code (0 for success): u8
|
||||
CMD_ERASE_PAGE = 0x04,
|
||||
|
||||
// Program flash
|
||||
// -> Start adress: u32
|
||||
// -> Data: n bytes (n must be even)
|
||||
CMD_PROGRAM = 0x05,
|
||||
|
||||
// Exit bootloader and start application software
|
||||
CMD_EXIT = 0xff
|
||||
} Command_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint32_t build_date;
|
||||
uint32_t build_number;
|
||||
uint32_t flash_application_start;
|
||||
uint32_t flash_application_size;
|
||||
uint8_t version_major;
|
||||
uint8_t version_minor;
|
||||
uint8_t version_patch;
|
||||
char identifier[];
|
||||
} __attribute__((packed, aligned(1))) BootloaderInfo_t;
|
||||
66
stm32f103t8u6-bootloader/src/flash.c
Normal file
66
stm32f103t8u6-bootloader/src/flash.c
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
#include "flash.h"
|
||||
#include "usb.h"
|
||||
|
||||
#include "ownership.h"
|
||||
MODULE_OWNS_PERIPHERAL(FLASH);
|
||||
|
||||
static inline void Flash_Unlock(void)
|
||||
{
|
||||
FLASH->KEYR = FLASH_KEY1;
|
||||
FLASH->KEYR = FLASH_KEY2;
|
||||
}
|
||||
|
||||
static inline void Flash_Lock(void)
|
||||
{
|
||||
FLASH->CR = FLASH_CR_LOCK;
|
||||
}
|
||||
|
||||
Flash_Status_t Flash_ErasePage(unsigned int page)
|
||||
{
|
||||
if(page >= FLASH_PAGES)
|
||||
{
|
||||
return FLASH_PROHIBITED;
|
||||
}
|
||||
uint32_t page_start = FLASH_BASE + page * FLASH_PAGE_BYTES;
|
||||
|
||||
Flash_Unlock();
|
||||
|
||||
while(FLASH->SR & FLASH_SR_BSY);
|
||||
FLASH->CR = FLASH_CR_PER;
|
||||
FLASH->AR = page_start;
|
||||
FLASH->CR = FLASH_CR_STRT | FLASH_CR_PER;
|
||||
while(FLASH->SR & FLASH_SR_BSY);
|
||||
|
||||
// Flash_Lock() clears the PER bit, so we don't have to reset that
|
||||
Flash_Lock();
|
||||
|
||||
// Verify
|
||||
for(uint32_t i = page_start; i < page_start + FLASH_PAGE_BYTES; i += 4)
|
||||
{
|
||||
if(*(uint32_t*)i != 0xffffffff)
|
||||
{
|
||||
return FLASH_VERIFY_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
return FLASH_SUCCESS;
|
||||
}
|
||||
|
||||
void Flash_ProgramFromPMA(uint32_t flash_adress, uint16_t pma_offset,
|
||||
uint32_t length)
|
||||
{
|
||||
Flash_Unlock();
|
||||
|
||||
uint16_t *pma = (uint16_t*)(USB_PMA_ADDR + 2 * pma_offset);
|
||||
volatile uint16_t *flash = (uint16_t*)flash_adress;
|
||||
FLASH->CR = FLASH_CR_PG;
|
||||
for(unsigned int i = 0; i < (length + 1) / 2; i++)
|
||||
{
|
||||
*flash++ = *pma++;
|
||||
pma++;
|
||||
while(FLASH->SR & FLASH_SR_BSY);
|
||||
}
|
||||
|
||||
// Flash_Lock() clears the PG bit, so we don't have to reset that
|
||||
Flash_Lock();
|
||||
}
|
||||
22
stm32f103t8u6-bootloader/src/flash.h
Normal file
22
stm32f103t8u6-bootloader/src/flash.h
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
#pragma once
|
||||
|
||||
#include "stm32f103x6.h"
|
||||
|
||||
#define FLASH_PAGE_BYTES 1024U
|
||||
#define FLASH_BOOTLOADER_PAGES 2U
|
||||
#define FLASH_PAGES 64U
|
||||
|
||||
typedef enum
|
||||
{
|
||||
FLASH_SUCCESS = 0,
|
||||
FLASH_PROHIBITED = 1,
|
||||
FLASH_VERIFY_FAILED = 2
|
||||
} Flash_Status_t;
|
||||
|
||||
#define FLASH_APPLICATION_BASE (FLASH_BASE \
|
||||
+ FLASH_BOOTLOADER_PAGES * FLASH_PAGE_BYTES)
|
||||
|
||||
|
||||
Flash_Status_t Flash_ErasePage(unsigned int page);
|
||||
void Flash_ProgramFromPMA(uint32_t flash_adress, uint16_t pma_offset,
|
||||
uint32_t length);
|
||||
119
stm32f103t8u6-bootloader/src/main.c
Normal file
119
stm32f103t8u6-bootloader/src/main.c
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
#include "main.h"
|
||||
|
||||
// Configure clocks based on a 8 MHz external crystal for:
|
||||
// SYSCLK, AHB, APB2 72 Mhz
|
||||
// APB1, ADC 36 MHz
|
||||
static inline void Clock_Init(void)
|
||||
{
|
||||
// Activate HSE and wait for it to be ready
|
||||
RCC->CR = RCC_CR_HSEON | RCC_CR_HSION;
|
||||
while(!(RCC->CR & RCC_CR_HSERDY));
|
||||
|
||||
RCC->CFGR = RCC_CFGR_SW_0;
|
||||
while((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_0);
|
||||
|
||||
FLASH->ACR = FLASH_ACR_PRFTBE | FLASH_ACR_LATENCY_1;
|
||||
|
||||
// Set PLL to x9 (-> 72MHz system clock)
|
||||
RCC->CFGR = RCC_CFGR_PLLMULL9 | RCC_CFGR_PLLSRC | RCC_CFGR_PPRE1_2
|
||||
| RCC_CFGR_SW_0;
|
||||
|
||||
// Activate PLL and wait
|
||||
RCC->CR = RCC_CR_PLLON | RCC_CR_HSEON | RCC_CR_HSION;
|
||||
while(!(RCC->CR & RCC_CR_PLLRDY));
|
||||
|
||||
// Select PLL as clock source
|
||||
RCC->CFGR = RCC_CFGR_PLLMULL9 | RCC_CFGR_PLLSRC | RCC_CFGR_PPRE1_2
|
||||
| RCC_CFGR_SW_1;
|
||||
|
||||
// Disable all interrupts
|
||||
RCC->CIR = 0x00000000;
|
||||
|
||||
// Enable peripheral clocks
|
||||
RCC->APB2ENR = RCC_APB2ENR_IOPAEN | RCC_APB2ENR_AFIOEN;
|
||||
RCC->APB1ENR = RCC_APB1ENR_PWREN | RCC_APB1ENR_BKPEN | RCC_APB1ENR_USBEN;
|
||||
RCC->AHBENR = RCC_AHBENR_CRCEN;
|
||||
}
|
||||
|
||||
static inline bool Bootloader_EntryCondition(void)
|
||||
{
|
||||
// Activate pull-up for test point
|
||||
GPIOA->CRH = (0x44444444
|
||||
& ~(0x0f << (PIN_TEST_POINT * 4 - 32)))
|
||||
| (0x08<< (PIN_TEST_POINT * 4 - 32))
|
||||
;
|
||||
GPIOA->BSRR = (1 << PIN_TEST_POINT);
|
||||
|
||||
// The first word in the application frame is the stack end pointer
|
||||
const uint32_t *application_start = (uint32_t*)FLASH_APPLICATION_BASE;
|
||||
// If it is not a valid RAM address, we can assume there's no application
|
||||
// present in flash and thus enter the bootloader
|
||||
if((*application_start & 0xffff8000) != 0x20000000)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check RTC backup register 1 for magic value
|
||||
PWR->CR = PWR_CR_DBP;
|
||||
if(BKP->DR1 == 0xb007)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check if test point is held low externally
|
||||
if(~GPIOA->IDR & (1 << PIN_TEST_POINT))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline void Bootloader_Exit(void)
|
||||
{
|
||||
// Reset RTC backup register
|
||||
BKP->DR1 = 0x0000;
|
||||
|
||||
// Reset peripherals
|
||||
RCC->APB1RSTR = RCC_APB1RSTR_USBRST;
|
||||
RCC->APB2RSTR = RCC_APB2RSTR_IOPARST | RCC_APB2RSTR_AFIORST;
|
||||
RCC->APB1RSTR = 0x00000000;
|
||||
RCC->APB2RSTR = 0x00000000;
|
||||
|
||||
// Reset peripheral clock enable registers to their reset values
|
||||
RCC->AHBENR = 0x00000014; // Disable CRC
|
||||
RCC->APB2ENR = 0x00000000; // Disable GPIOA, GPIOB, AFIO
|
||||
RCC->APB1ENR = 0x00000000; // Disable USB, PWR, BKP
|
||||
|
||||
// Set vector table location
|
||||
SCB->VTOR = FLASH_APPLICATION_BASE - FLASH_BASE;
|
||||
|
||||
// Set stack pointer and jump into application
|
||||
uint32_t application_stack_pointer = *(uint32_t*)FLASH_APPLICATION_BASE;
|
||||
uint32_t application_reset_handler = *(uint32_t*)(FLASH_APPLICATION_BASE
|
||||
+ 4);
|
||||
__asm__ volatile(".syntax unified \n"
|
||||
"msr msp, %[stack_pointer] \n"
|
||||
"bx %[reset_handler] \n"
|
||||
:
|
||||
: [stack_pointer] "r" (application_stack_pointer),
|
||||
[reset_handler] "r" (application_reset_handler));
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
Clock_Init();
|
||||
|
||||
if(Bootloader_EntryCondition())
|
||||
{
|
||||
USB_Init();
|
||||
|
||||
while(USB_Poll());
|
||||
|
||||
// Delay to allow answer token to be fetched by host
|
||||
Util_Delay(100000);
|
||||
}
|
||||
|
||||
Bootloader_Exit();
|
||||
}
|
||||
|
||||
10
stm32f103t8u6-bootloader/src/main.h
Normal file
10
stm32f103t8u6-bootloader/src/main.h
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "stm32f103x6.h"
|
||||
#include "pinning.h"
|
||||
#include "usb.h"
|
||||
#include "flash.h"
|
||||
#include "util.h"
|
||||
|
||||
int main(void);
|
||||
12
stm32f103t8u6-bootloader/src/ownership.h
Normal file
12
stm32f103t8u6-bootloader/src/ownership.h
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
#pragma once
|
||||
|
||||
#define _PASTE(x, y) x ## y
|
||||
#define PASTE(x, y) _PASTE(x, y)
|
||||
|
||||
#define MODULE_OWNS_PERIPHERAL(peripheral) \
|
||||
void *_PERIPHERAL_OWNERSHIP_ ## peripheral \
|
||||
= (void*)(peripheral)
|
||||
|
||||
#define MODULE_OWNS_PIN(gpio, pin) \
|
||||
void *PASTE(_PIN_OWNERSHIP_ ## gpio ## _, pin) \
|
||||
= (void*)(gpio + pin)
|
||||
6
stm32f103t8u6-bootloader/src/pinning.h
Normal file
6
stm32f103t8u6-bootloader/src/pinning.h
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
// Port A
|
||||
#define PIN_TEST_POINT 10 // PA10
|
||||
#define PIN_USB_DM 11 // PA11 - USB_DM
|
||||
#define PIN_USB_DP 12 // PA12 - USB_DP
|
||||
88
stm32f103t8u6-bootloader/src/startup.S
Normal file
88
stm32f103t8u6-bootloader/src/startup.S
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
.syntax unified
|
||||
.thumb
|
||||
.fpu softvfp
|
||||
|
||||
.global Reset_Handler
|
||||
.type Reset_Handler, %function
|
||||
Reset_Handler:
|
||||
init_data:
|
||||
ldr r2, =_start_init_data
|
||||
ldr r3, =_start_data
|
||||
ldr r1, =_end_data
|
||||
|
||||
init_data_loop:
|
||||
cmp r3, r1
|
||||
bhs zero_bss
|
||||
ldr r0, [r2], #4
|
||||
str r0, [r3], #4
|
||||
b init_data_loop
|
||||
|
||||
zero_bss:
|
||||
ldr r3, =_start_bss
|
||||
ldr r1, =_end_bss
|
||||
movs r0, #0
|
||||
|
||||
zero_bss_loop:
|
||||
cmp r3, r1
|
||||
bhs init_finished
|
||||
str r0, [r3], #4
|
||||
b zero_bss_loop
|
||||
|
||||
init_finished:
|
||||
bl main
|
||||
|
||||
infinite_loop:
|
||||
b infinite_loop
|
||||
|
||||
.size Reset_Handler, .-Reset_Handler
|
||||
|
||||
// Only the core interrupt vectors, since the bootloader code does not use
|
||||
// interrupts. Saves a few bytes to omit the interrupt vector table.
|
||||
.section .cortex_vectors, "a"
|
||||
.word _end_stack
|
||||
.word Reset_Handler
|
||||
.word NMI_Handler
|
||||
.word HardFault_Handler
|
||||
.word MemManage_Handler
|
||||
.word BusFault_Handler
|
||||
.word UsageFault_Handler
|
||||
.word 0x00000000
|
||||
.word 0x00000000
|
||||
.word 0x00000000
|
||||
.word 0x00000000
|
||||
.word SVC_Handler
|
||||
.word DebugMon_Handler
|
||||
.word 0x00000000
|
||||
.word PendSV_Handler
|
||||
.word SysTick_Handler
|
||||
|
||||
.type Dummy_Handler, %function
|
||||
Dummy_Handler:
|
||||
b Dummy_Handler
|
||||
|
||||
.weak NMI_Handler
|
||||
.thumb_set NMI_Handler, Dummy_Handler
|
||||
|
||||
.weak HardFault_Handler
|
||||
.thumb_set HardFault_Handler, Dummy_Handler
|
||||
|
||||
.weak MemManage_Handler
|
||||
.thumb_set MemManage_Handler, Dummy_Handler
|
||||
|
||||
.weak BusFault_Handler
|
||||
.thumb_set BusFault_Handler, Dummy_Handler
|
||||
|
||||
.weak UsageFault_Handler
|
||||
.thumb_set UsageFault_Handler, Dummy_Handler
|
||||
|
||||
.weak SVC_Handler
|
||||
.thumb_set SVC_Handler, Dummy_Handler
|
||||
|
||||
.weak DebugMon_Handler
|
||||
.thumb_set DebugMon_Handler, Dummy_Handler
|
||||
|
||||
.weak PendSV_Handler
|
||||
.thumb_set PendSV_Handler, Dummy_Handler
|
||||
|
||||
.weak SysTick_Handler
|
||||
.thumb_set SysTick_Handler, Dummy_Handler
|
||||
257
stm32f103t8u6-bootloader/src/usb.c
Normal file
257
stm32f103t8u6-bootloader/src/usb.c
Normal file
|
|
@ -0,0 +1,257 @@
|
|||
#include "usb.h"
|
||||
#include "usb_descriptors.h"
|
||||
#include "usb_util.h"
|
||||
#include "usb_com.h"
|
||||
#include "util.h"
|
||||
|
||||
#include "ownership.h"
|
||||
MODULE_OWNS_PERIPHERAL(USB);
|
||||
MODULE_OWNS_PIN(GPIOA, PIN_USB_DM);
|
||||
MODULE_OWNS_PIN(GPIOA, PIN_USB_DP);
|
||||
|
||||
uint8_t USB_DeviceStatus[2] = {0x00, 0x00};
|
||||
volatile unsigned int USB_Address = 0;
|
||||
|
||||
void USB_Init(void)
|
||||
{
|
||||
// Initialise USB GPIOs to AF mode
|
||||
// Note: This will reset the test point pin to an input without pullup
|
||||
GPIOA->CRH = (0x44444444
|
||||
& ~(0x0f << (PIN_USB_DM * 4 - 32))
|
||||
& ~(0x0f << (PIN_USB_DP * 4 - 32)))
|
||||
| (0x0b << (PIN_USB_DM * 4 - 32)) // AF mode, 50 MHz
|
||||
| (0x0b << (PIN_USB_DP * 4 - 32)) // AF mode, 50 MHz
|
||||
;
|
||||
|
||||
Util_Delay(100000);
|
||||
|
||||
// Analog power up
|
||||
USB->CNTR = (uint16_t)USB_CNTR_FRES;
|
||||
// Minimum delay: 1 µs
|
||||
Util_Delay(3000);
|
||||
|
||||
USB->CNTR = (uint16_t)0;
|
||||
USB->ISTR = (uint16_t)0;
|
||||
USB->CNTR = (uint16_t)(USB_CNTR_RESETM | USB_CNTR_CTRM);
|
||||
}
|
||||
|
||||
static inline void USB_HandleReset(void)
|
||||
{
|
||||
// Remove reset flag
|
||||
USB->ISTR = (uint16_t)~(USB_ISTR_RESET);
|
||||
|
||||
// Set buffer table origin
|
||||
USB->BTABLE = USB_BTABLE_OFFSET;
|
||||
|
||||
// Control endpoint 0 (64 bytes size)
|
||||
USB_BTABLE_ENTRIES[0].COUNT_RX = USB_EP_RXCOUNT_BL_SIZE | (1 << 10);
|
||||
USB_BTABLE_ENTRIES[0].ADDR_RX = 0x40;
|
||||
USB_BTABLE_ENTRIES[0].COUNT_TX = 0;
|
||||
USB_BTABLE_ENTRIES[0].ADDR_TX = 0x80;
|
||||
|
||||
USB_SetEPR(&(USB->EP0R), USB_EPR_EP_TYPE_CONTROL
|
||||
| USB_EPR_STAT_TX_NAK | USB_EPR_STAT_RX_VALID);
|
||||
|
||||
// Endpoint 1: In (buffer size 64)
|
||||
USB_BTABLE_ENTRIES[1].COUNT_TX = 0;
|
||||
USB_BTABLE_ENTRIES[1].ADDR_TX = 0xc0;
|
||||
USB_BTABLE_ENTRIES[1].COUNT_RX = 0;
|
||||
USB_BTABLE_ENTRIES[1].ADDR_RX = 0;
|
||||
|
||||
USB_SetEPR(&(USB->EP1R), USB_EPR_EP_TYPE_BULK
|
||||
| USB_EPR_STAT_TX_NAK | USB_EPR_STAT_RX_DISABLED
|
||||
| (1 << USB_EP1R_EA_Pos));
|
||||
|
||||
// Endpoint 2: Out (buffer size 64)
|
||||
USB_BTABLE_ENTRIES[2].COUNT_RX = USB_EP_RXCOUNT_BL_SIZE | (1 << 10);
|
||||
USB_BTABLE_ENTRIES[2].ADDR_RX = 0x100;
|
||||
USB_BTABLE_ENTRIES[2].COUNT_TX = 0;
|
||||
USB_BTABLE_ENTRIES[2].ADDR_TX = 0;
|
||||
|
||||
USB_SetEPR(&(USB->EP2R), USB_EPR_EP_TYPE_BULK
|
||||
| USB_EPR_STAT_TX_DISABLED | USB_EPR_STAT_RX_VALID
|
||||
| (2 << USB_EP2R_EA_Pos));
|
||||
|
||||
// Enable
|
||||
USB->DADDR = USB_DADDR_EF;
|
||||
}
|
||||
|
||||
static inline void USB_HandleIn(void)
|
||||
{
|
||||
if((USB->DADDR & USB_DADDR_ADD) != USB_Address)
|
||||
{
|
||||
USB->DADDR = USB_Address | USB_DADDR_EF;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static inline bool USB_HandleSetup(void)
|
||||
{
|
||||
bool exit_bootloader = false;
|
||||
|
||||
USB_SetupPacket_t sp;
|
||||
USB_PMAToMemory(&sp, USB_BTABLE_ENTRIES[0].ADDR_RX,
|
||||
sizeof(USB_SetupPacket_t));
|
||||
|
||||
const void *reply_data = NULL;
|
||||
int reply_length = 0;
|
||||
uint8_t reply_response = USB_EP_TX_STALL;
|
||||
|
||||
if((sp.bmRequestType & (USB_REQUEST_TYPE | USB_REQUEST_RECIPIENT))
|
||||
== (USB_REQUEST_TYPE_STANDARD | USB_REQUEST_RECIPIENT_DEVICE))
|
||||
{
|
||||
switch(sp.bRequest)
|
||||
{
|
||||
case USB_REQUEST_GET_STATUS:
|
||||
if(sp.wValue == 0 && sp.wIndex == 0 && sp.wLength == 2)
|
||||
{
|
||||
reply_length = 2;
|
||||
reply_data = USB_DeviceStatus;
|
||||
}
|
||||
break;
|
||||
|
||||
case USB_REQUEST_GET_DESCRIPTOR:;
|
||||
USB_DescriptorType_t descriptor_type = sp.wValue >> 8;
|
||||
int descriptor_index = sp.wValue & 0xff;
|
||||
reply_length = sp.wLength;
|
||||
USB_HandleGetDescriptor(descriptor_type, descriptor_index,
|
||||
&reply_data, &reply_length, &reply_response);
|
||||
break;
|
||||
|
||||
case USB_REQUEST_SET_ADDRESS:
|
||||
USB_Address = sp.wValue & USB_DADDR_ADD;
|
||||
reply_response = USB_EP_TX_VALID;
|
||||
break;
|
||||
|
||||
case USB_REQUEST_SET_CONFIGURATION:
|
||||
// There is only one configuration, so this request is ignored
|
||||
// (but still accepted)
|
||||
reply_response = USB_EP_TX_VALID;
|
||||
break;
|
||||
|
||||
default:
|
||||
reply_response = USB_EP_TX_STALL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if((sp.bmRequestType & USB_REQUEST_TYPE) == USB_REQUEST_TYPE_VENDOR)
|
||||
{
|
||||
reply_response = USB_EP_TX_VALID;
|
||||
if(!USB_HandleCommand(&sp))
|
||||
{
|
||||
exit_bootloader = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Unknown request
|
||||
reply_response = USB_EP_TX_STALL;
|
||||
}
|
||||
|
||||
if(reply_data)
|
||||
{
|
||||
// Reply with data
|
||||
USB_MemoryToPMA(USB_BTABLE_ENTRIES[0].ADDR_TX, reply_data,
|
||||
reply_length);
|
||||
USB_BTABLE_ENTRIES[0].COUNT_TX = reply_length;
|
||||
USB->EP0R = (USB_EP_TX_NAK ^ USB_EP_TX_VALID)
|
||||
| USB_EP_CTR_RX | USB_EP_CTR_TX | USB_EPR_EP_TYPE_CONTROL | 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Send response
|
||||
USB_BTABLE_ENTRIES[0].COUNT_TX = 0;
|
||||
USB->EP0R = (USB_EP_TX_NAK ^ reply_response)
|
||||
| USB_EP_CTR_RX | USB_EP_CTR_TX | USB_EPR_EP_TYPE_CONTROL | 0;
|
||||
}
|
||||
|
||||
return !exit_bootloader;
|
||||
}
|
||||
|
||||
bool USB_Poll(void)
|
||||
{
|
||||
if(USB->ISTR & USB_ISTR_RESET)
|
||||
{
|
||||
// Reset happened
|
||||
USB_HandleReset();
|
||||
return true;
|
||||
}
|
||||
uint16_t istr;
|
||||
while((istr = USB->ISTR) & (USB_ISTR_CTR))
|
||||
{
|
||||
if(istr & USB_ISTR_CTR)
|
||||
{
|
||||
// Correct transfer
|
||||
int ep = istr & USB_ISTR_EP_ID;
|
||||
switch(ep)
|
||||
{
|
||||
case 0:
|
||||
// Determine transfer direction
|
||||
if(istr & USB_ISTR_DIR)
|
||||
{
|
||||
// Out transfer
|
||||
if(USB->EP0R & USB_EP0R_SETUP)
|
||||
{
|
||||
// Clear CTR_RX and set RX status to VALID (from
|
||||
// NAK)
|
||||
USB->EP0R = USB_EP_CTR_TX
|
||||
| (USB_EP_RX_NAK ^ USB_EP_RX_VALID)
|
||||
| USB_EPR_EP_TYPE_CONTROL | 0;
|
||||
|
||||
// Setup packed received and check if a command to
|
||||
// exit the bootloader was received
|
||||
if(!USB_HandleSetup())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Only setup packets are supported, so other out
|
||||
// transfers are just ignored
|
||||
|
||||
// Clear CTR_RX and set RX status to VALID (from
|
||||
// NAK)
|
||||
USB->EP0R = USB_EP_CTR_TX
|
||||
| (USB_EP_RX_NAK ^ USB_EP_RX_VALID)
|
||||
| USB_EPR_EP_TYPE_CONTROL | 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// In transfer
|
||||
|
||||
// Clear CTR_TX
|
||||
USB->EP0R = USB_EP_CTR_RX | USB_EPR_EP_TYPE_CONTROL | 0;
|
||||
|
||||
USB_HandleIn();
|
||||
}
|
||||
break;
|
||||
|
||||
case 1:
|
||||
// Data in endpoint
|
||||
|
||||
// In transfer finished. STAT_TX gets set to NAK
|
||||
// automatically.
|
||||
|
||||
// Clear CTR_TX
|
||||
USB->EP1R = USB_EPR_EP_TYPE_BULK | 1;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
// Data out endpoint
|
||||
|
||||
// Clear CTR_RX and set RX status to VALID (which is NAK
|
||||
// after a correct transfer)
|
||||
USB->EP2R = (USB_EP_RX_VALID ^ USB_EP_RX_NAK)
|
||||
| USB_EPR_EP_TYPE_BULK | 2;
|
||||
|
||||
// Out transfer finished
|
||||
USB_HandleEP2Out();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
130
stm32f103t8u6-bootloader/src/usb.h
Normal file
130
stm32f103t8u6-bootloader/src/usb.h
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
#include "stm32f103x6.h"
|
||||
|
||||
#include "pinning.h"
|
||||
|
||||
#define USB_TOKEN_OUT 0b0001
|
||||
#define USB_TOKEN_IN 0x1001
|
||||
#define USB_TOKEN_SOF 0b0101
|
||||
#define USB_TOKEN_SETUP 0b1101
|
||||
#define USB_TOKEN_DATA0 0b0011
|
||||
#define USB_TOKEN_DATA1 0b1011
|
||||
#define USB_TOKEN_DATA2 0b0111
|
||||
#define USB_TOKEN_ACK 0b0010
|
||||
#define USB_TOKEN_NAK 0b1010
|
||||
#define USB_TOKEN_STALL 0b1110
|
||||
#define USB_TOKEN_NYET 0b0110
|
||||
#define USB_TOKEN_PRE 0b1100
|
||||
#define USB_TOKEN_ERR 0b1100
|
||||
#define USB_TOKEN_SPLIT 0b1000
|
||||
#define USB_TOKEN_PING 0b0100
|
||||
|
||||
#define USB_REQUEST_DIRECTION (1 << 7)
|
||||
#define USB_REQUEST_DIRECTION_OUT 0
|
||||
#define USB_REQUEST_DIRECTION_IN (1 << 7)
|
||||
#define USB_REQUEST_TYPE (0x3 << 5)
|
||||
#define USB_REQUEST_TYPE_STANDARD 0
|
||||
#define USB_REQUEST_TYPE_CLASS (1 << 5)
|
||||
#define USB_REQUEST_TYPE_VENDOR (2 << 5)
|
||||
#define USB_REQUEST_TYPE_RESERVED (3 << 5)
|
||||
#define USB_REQUEST_RECIPIENT 0x1f
|
||||
#define USB_REQUEST_RECIPIENT_DEVICE 0
|
||||
#define USB_REQUEST_RECIPIENT_INTERFACE 1
|
||||
#define USB_REQUEST_RECIPIENT_ENDPOINT 2
|
||||
#define USB_REQUEST_RECIPIENT_OTHER 3
|
||||
|
||||
#define USB_REQUEST_CLEAR_FEATURE 1
|
||||
// wValue = <feature>, wIndex = 0|<interface>|<endpoint>, wLength = 0
|
||||
#define USB_REQUEST_GET_CONFIGURATION 8
|
||||
// wValue = 0, wIndex = 0, wLength = 1
|
||||
#define USB_REQUEST_GET_DESCRIPTOR 6
|
||||
// wValue = <descriptor type>:<descriptor index>, wIndex = 0|<language>, wLength = <descriptor length>
|
||||
#define USB_REQUEST_GET_INTERFACE 10
|
||||
// wValue = 0, wIndex = <interface>, wLength = 1
|
||||
#define USB_REQUEST_GET_STATUS 0
|
||||
// wValue = 0, wIndex = 0|<interface>|<endpoint>, wLength = 2
|
||||
#define USB_REQUEST_SET_ADDRESS 5
|
||||
// wValue = <address>, wIndex = 0, wLength = 0
|
||||
#define USB_REQUEST_SET_CONFIGURATION 9
|
||||
// wValue = <configuration value>, wIndex = 0, wLength = 0
|
||||
#define USB_REQUEST_SET_DESCRIPTOR 7
|
||||
// wValue = <descriptor type>:<descriptor index>, wIndex = 0|<language>, wLength = <descriptor length>
|
||||
#define USB_REQUEST_SET_FEATURE 3
|
||||
// wValue = <feature selector>, wIndex = 0|<interface>|<endpoint>, wLength = 0
|
||||
#define USB_REQUEST_SET_INTERFACE 11
|
||||
// wValue = <alternate setting>, wIndex = <interface>, wLength = 0
|
||||
#define USB_REQUEST_SYNCH_FRAME 12
|
||||
// wValue = 0, wIndex = <endpoint>, wLength = 2
|
||||
|
||||
#define USB_EPR_STAT_TX_DISABLED 0x00
|
||||
#define USB_EPR_STAT_TX_STALL USB_EP0R_STAT_TX_0
|
||||
#define USB_EPR_STAT_TX_NAK USB_EP0R_STAT_TX_1
|
||||
#define USB_EPR_STAT_TX_VALID (USB_EP0R_STAT_TX_0 \
|
||||
| USB_EP0R_STAT_TX_1)
|
||||
|
||||
#define USB_EPR_STAT_RX_DISABLED 0x00
|
||||
#define USB_EPR_STAT_RX_STALL USB_EP0R_STAT_RX_0
|
||||
#define USB_EPR_STAT_RX_NAK USB_EP0R_STAT_RX_1
|
||||
#define USB_EPR_STAT_RX_VALID (USB_EP0R_STAT_RX_0 \
|
||||
| USB_EP0R_STAT_RX_1)
|
||||
|
||||
#define USB_EPR_EP_TYPE_BULK 0x00
|
||||
#define USB_EPR_EP_TYPE_CONTROL USB_EP0R_EP_TYPE_0
|
||||
#define USB_EPR_EP_TYPE_ISO USB_EP0R_EP_TYPE_1
|
||||
#define USB_EPR_EP_TYPE_INTERRUPT (USB_EP0R_EP_TYPE_0 \
|
||||
| USB_EP0R_EP_TYPE_1)
|
||||
|
||||
#define USB_PMA_ADDR 0x40006000UL
|
||||
#define USB_BTABLE_OFFSET 0x00
|
||||
#define USB_EP_RXCOUNT_BL_SIZE (1 << 15)
|
||||
|
||||
typedef struct
|
||||
{
|
||||
volatile union
|
||||
{
|
||||
volatile uint16_t ADDR_TX;
|
||||
volatile uint16_t ADDR_RX_0;
|
||||
volatile uint16_t ADDR_TX_0;
|
||||
} __attribute__((aligned(4)));
|
||||
volatile union
|
||||
{
|
||||
volatile uint16_t COUNT_TX;
|
||||
volatile uint16_t COUNT_RX_0;
|
||||
volatile uint16_t COUNT_TX_0;
|
||||
} __attribute__((aligned(4)));
|
||||
volatile union
|
||||
{
|
||||
volatile uint16_t ADDR_RX;
|
||||
volatile uint16_t ADDR_RX_1;
|
||||
volatile uint16_t ADDR_TX_1;
|
||||
} __attribute__((aligned(4)));
|
||||
volatile union
|
||||
{
|
||||
volatile uint16_t COUNT_RX;
|
||||
volatile uint16_t COUNT_RX_1;
|
||||
volatile uint16_t COUNT_TX_1;
|
||||
} __attribute__((aligned(4)));
|
||||
} __attribute__((aligned(8))) USB_BufferTableEntry_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t bmRequestType;
|
||||
uint8_t bRequest;
|
||||
uint16_t wValue;
|
||||
uint16_t wIndex;
|
||||
uint16_t wLength;
|
||||
} __attribute__((packed, aligned(2))) USB_SetupPacket_t;
|
||||
|
||||
#define USB_BTABLE_ENTRIES \
|
||||
((volatile USB_BufferTableEntry_t*)(USB_PMA_ADDR + USB_BTABLE_OFFSET))
|
||||
|
||||
void USB_Init(void);
|
||||
|
||||
// Because no interrupts are used, the interrupt flags have to be polled in a
|
||||
// loop. This functions checks the flags and handles the interrupt requests. It
|
||||
// has thus to be called in a loop. It returns false if the bootloader should
|
||||
// exit and the application be started.
|
||||
bool USB_Poll(void);
|
||||
189
stm32f103t8u6-bootloader/src/usb_com.c
Normal file
189
stm32f103t8u6-bootloader/src/usb_com.c
Normal file
|
|
@ -0,0 +1,189 @@
|
|||
#include "usb_com.h"
|
||||
#include "usb_util.h"
|
||||
#include "commands.h"
|
||||
#include "buildinfo.h"
|
||||
#include "flash.h"
|
||||
|
||||
static const BootloaderInfo_t BootloaderInfo =
|
||||
{
|
||||
.build_date = BUILD_DATE,
|
||||
.build_number = BUILD_NUMBER,
|
||||
.flash_application_start = FLASH_APPLICATION_BASE,
|
||||
.flash_application_size = (FLASH_PAGES - FLASH_BOOTLOADER_PAGES)
|
||||
* FLASH_PAGE_BYTES,
|
||||
.version_major = BUILD_VERSION_MAJOR,
|
||||
.version_minor = BUILD_VERSION_MINOR,
|
||||
.version_patch = BUILD_VERSION_PATCH,
|
||||
.identifier = "STM32F103T8U6"
|
||||
};
|
||||
|
||||
|
||||
static Command_t USB_PendingCommand = CMD_NOP;
|
||||
|
||||
static void USB_EP1Transmit(const void *data, uint16_t length)
|
||||
{
|
||||
USB_MemoryToPMA(USB_BTABLE_ENTRIES[1].ADDR_TX, data, length);
|
||||
USB_BTABLE_ENTRIES[1].COUNT_TX = length;
|
||||
|
||||
// Assume that STAT_TX is currently NAK (which is the case after a reset or
|
||||
// after a correct transfer, just not if the function is called multiple
|
||||
// times without an actual transfer in between)
|
||||
USB->EP1R = (USB_EP_TX_NAK ^ USB_EP_TX_VALID)
|
||||
| USB_EP_CTR_RX | USB_EP_CTR_TX | USB_EPR_EP_TYPE_BULK | 1;
|
||||
}
|
||||
|
||||
bool USB_HandleCommand(const USB_SetupPacket_t *sp)
|
||||
{
|
||||
// The command is stored in the second byte (bRequest field) of the setup
|
||||
// packet
|
||||
Command_t command = sp->bRequest;
|
||||
|
||||
const void *reply_data = NULL;
|
||||
int reply_length = 0;
|
||||
|
||||
switch(command)
|
||||
{
|
||||
case CMD_BOOTLOADER_INFO:
|
||||
reply_data = &BootloaderInfo;
|
||||
reply_length = sizeof(BootloaderInfo)
|
||||
+ strlen(BootloaderInfo.identifier);
|
||||
break;
|
||||
|
||||
case CMD_READ_CRC:
|
||||
// The command will be executed as soon as the start address and
|
||||
// length are transferred via EP2
|
||||
USB_PendingCommand = CMD_READ_CRC;
|
||||
break;
|
||||
|
||||
case CMD_READ_MEMORY:
|
||||
// The command will be executed as soon as the start address and
|
||||
// length are transferred via EP2
|
||||
USB_PendingCommand = CMD_READ_MEMORY;
|
||||
break;
|
||||
|
||||
case CMD_ERASE_PAGE:
|
||||
// The command will be executed as soon as the page number is
|
||||
// transferred via EP2. Since only a single byte is needed for the
|
||||
// page index, this would also be technically be possible with just
|
||||
// one setup packet. Since this is the only command, this minor
|
||||
// USB bandwith saving is not worth the extra special case.
|
||||
USB_PendingCommand = CMD_ERASE_PAGE;
|
||||
break;
|
||||
|
||||
case CMD_PROGRAM:
|
||||
USB_PendingCommand = CMD_PROGRAM;
|
||||
break;
|
||||
|
||||
case CMD_EXIT:
|
||||
return false;
|
||||
|
||||
default:
|
||||
// Invalid commands get ignored
|
||||
break;
|
||||
}
|
||||
|
||||
if(reply_length > 0)
|
||||
{
|
||||
USB_EP1Transmit(reply_data, reply_length);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void USB_HandleEP2Out(void)
|
||||
{
|
||||
// Read how many bytes have been received by EP2
|
||||
int packet_length = USB_BTABLE_ENTRIES[2].COUNT_RX & 0x3ff;
|
||||
|
||||
// The meaning of the received data depends on the command transmitted via
|
||||
// a setup packet before it
|
||||
switch(USB_PendingCommand)
|
||||
{
|
||||
case CMD_READ_CRC:
|
||||
if(packet_length == 8)
|
||||
{
|
||||
uint32_t buff[2];
|
||||
USB_PMAToMemory(buff, USB_BTABLE_ENTRIES[2].ADDR_RX,
|
||||
sizeof(buff));
|
||||
uint32_t *addr = (uint32_t*)(buff[0]);
|
||||
uint32_t length = buff[1];
|
||||
|
||||
CRC->CR = CRC_CR_RESET;
|
||||
|
||||
// TODO: Add basic sanity checks so it isn't possible to crash
|
||||
// the bootloader with this command (or at least not as easy)
|
||||
while(length > 4)
|
||||
{
|
||||
CRC->DR = *addr++;
|
||||
length -= 4;
|
||||
}
|
||||
CRC->DR = *addr & (0xffffffffU >> (32 - 8 * length));
|
||||
|
||||
buff[0] = CRC->DR;
|
||||
USB_EP1Transmit(buff, 4);
|
||||
}
|
||||
break;
|
||||
|
||||
case CMD_READ_MEMORY:
|
||||
if(packet_length == 8)
|
||||
{
|
||||
uint32_t buff[2];
|
||||
USB_PMAToMemory(buff, USB_BTABLE_ENTRIES[2].ADDR_RX,
|
||||
sizeof(buff));
|
||||
uint8_t *start = (uint8_t*)(buff[0]);
|
||||
uint32_t length = buff[1];
|
||||
|
||||
if(length > 64)
|
||||
{
|
||||
length = 64;
|
||||
}
|
||||
|
||||
USB_EP1Transmit(start, length);
|
||||
}
|
||||
break;
|
||||
|
||||
case CMD_ERASE_PAGE:
|
||||
if(packet_length == 1)
|
||||
{
|
||||
// Not that only one byte has been received but since the PMA
|
||||
// can only be accessed word-wise, we'll have to copy two
|
||||
uint8_t buff[2];
|
||||
USB_PMAToMemory(buff, USB_BTABLE_ENTRIES[2].ADDR_RX,
|
||||
sizeof(buff));
|
||||
|
||||
unsigned int page_index = buff[0];
|
||||
if(page_index < FLASH_BOOTLOADER_PAGES)
|
||||
{
|
||||
// Do not allow erasing the bootloader
|
||||
buff[0] = FLASH_PROHIBITED;
|
||||
}
|
||||
else
|
||||
{
|
||||
buff[0] = Flash_ErasePage(page_index);
|
||||
}
|
||||
|
||||
// Reply with status byte
|
||||
USB_EP1Transmit(buff, 1);
|
||||
}
|
||||
break;
|
||||
|
||||
case CMD_PROGRAM:;
|
||||
uint32_t start;
|
||||
USB_PMAToMemory(&start, USB_BTABLE_ENTRIES[2].ADDR_RX, 4);
|
||||
uint32_t length = packet_length - 4;
|
||||
if(start >= FLASH_APPLICATION_BASE && start + length
|
||||
<= FLASH_BASE + FLASH_PAGE_BYTES * FLASH_PAGES)
|
||||
{
|
||||
// Program directly from PMA without an intermediate buffer in
|
||||
// RAM
|
||||
Flash_ProgramFromPMA(start, USB_BTABLE_ENTRIES[2].ADDR_RX + 4,
|
||||
length);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
USB_PendingCommand = CMD_NOP;
|
||||
}
|
||||
14
stm32f103t8u6-bootloader/src/usb_com.h
Normal file
14
stm32f103t8u6-bootloader/src/usb_com.h
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include "stm32f103x6.h"
|
||||
|
||||
#include "usb.h"
|
||||
|
||||
void USB_HandleEP2Out(void);
|
||||
|
||||
// Parses a setup packet with a vendor request type and handle the command it
|
||||
// contains. Returns false if the bootloader should exit and start the
|
||||
// application, true if the bootloader should continue execution.
|
||||
bool USB_HandleCommand(const USB_SetupPacket_t *sp);
|
||||
170
stm32f103t8u6-bootloader/src/usb_descriptors.c
Normal file
170
stm32f103t8u6-bootloader/src/usb_descriptors.c
Normal file
|
|
@ -0,0 +1,170 @@
|
|||
#include "usb_descriptors.h"
|
||||
|
||||
const USB_DeviceDescriptor_t USB_DeviceDescriptor =
|
||||
{
|
||||
.bLength = 18,
|
||||
.bDescriptorType = USB_DEVICE_DESCRIPTOR,
|
||||
.bcdUSB = 0x0200,
|
||||
.bDeviceClass = 0xff,
|
||||
.bDeviceSubClass = 0xff,
|
||||
.bDeviceProtocol = 0x00,
|
||||
.bMaxPacketSize0 = 64,
|
||||
.idVendor = 0x16c0,
|
||||
.idProduct = 0x05dc,
|
||||
.bcdDevice = 0x0200,
|
||||
.iManufacturer = 1,
|
||||
.iProduct = 2,
|
||||
.iSerialNumber = 3,
|
||||
.bNumConfigurations = 1
|
||||
};
|
||||
|
||||
const USB_WholeDescriptor_t USB_ConfigurationInterfaceDescriptor =
|
||||
{
|
||||
.configuration = (USB_ConfigurationDescriptor_t)
|
||||
{
|
||||
.bLength = 9,
|
||||
.bDescriptorType = USB_CONFIGURATION_DESCRIPTOR,
|
||||
.wTotalLength = sizeof(USB_WholeDescriptor_t),
|
||||
.bNumInterfaces = 1,
|
||||
.bConfigurationValue = 1,
|
||||
.iConfiguration = 0,
|
||||
.bmAttributes = 0x80,
|
||||
.bMaxPower = 100
|
||||
},
|
||||
|
||||
.main_interface = (USB_InterfaceDescriptor_t)
|
||||
{
|
||||
.bLength = 9,
|
||||
.bDescriptorType = USB_INTERFACE_DESCRIPTOR,
|
||||
.bInterfaceNumber = 0,
|
||||
.bAlternateSetting = 0,
|
||||
.bNumEndpoints = 2,
|
||||
.bInterfaceClass = 0x00,
|
||||
.bInterfaceSubClass = 0x00,
|
||||
.bInterfaceProtocol = 0x00,
|
||||
.iInterface = 0
|
||||
},
|
||||
|
||||
// Endpoint 1: in
|
||||
.data_in_endpoint = (USB_EndpointDescriptor_t)
|
||||
{
|
||||
.bLength = sizeof(USB_EndpointDescriptor_t),
|
||||
.bDescriptorType = USB_ENDPOINT_DESCRIPTOR,
|
||||
.bEndpointAddress = USB_ENDPOINT_IN | 1,
|
||||
.bmAttributes = USB_ENDPOINT_BULK | USB_ENDPOINT_NO_SYNCHRONIZATION
|
||||
| USB_ENDPOINT_DATA,
|
||||
.wMaxPacketSize = 64,
|
||||
.bInterval = 0x01
|
||||
},
|
||||
|
||||
// Endpoint 2: out
|
||||
.data_out_endpoint = (USB_EndpointDescriptor_t)
|
||||
{
|
||||
.bLength = sizeof(USB_EndpointDescriptor_t),
|
||||
.bDescriptorType = USB_ENDPOINT_DESCRIPTOR,
|
||||
.bEndpointAddress = USB_ENDPOINT_OUT | 2,
|
||||
.bmAttributes = USB_ENDPOINT_BULK | USB_ENDPOINT_NO_SYNCHRONIZATION
|
||||
| USB_ENDPOINT_DATA,
|
||||
.wMaxPacketSize = 64,
|
||||
.bInterval = 0x01
|
||||
},
|
||||
};
|
||||
|
||||
#define USB_STRING_LANGID 0x0409
|
||||
#define USB_STRING_VENDOR '2', '5', '1', '2', '0'
|
||||
#define USB_STRING_PRODUCT 'p', 'u', 'n', 't'
|
||||
|
||||
const uint16_t USB_StringDescriptor_LangID[] =
|
||||
USB_BUILD_STRING_DESCRIPTOR(USB_STRING_LANGID);
|
||||
const uint16_t USB_StringDescriptor_Vendor[] =
|
||||
USB_BUILD_STRING_DESCRIPTOR(USB_STRING_VENDOR);
|
||||
const uint16_t USB_StringDescriptor_Product[] =
|
||||
USB_BUILD_STRING_DESCRIPTOR(USB_STRING_PRODUCT);
|
||||
|
||||
void USB_HandleGetDescriptor(USB_DescriptorType_t descriptor_type,
|
||||
int descriptor_index, const void **reply_data, int *reply_length,
|
||||
uint8_t *reply_response)
|
||||
{
|
||||
switch(descriptor_type)
|
||||
{
|
||||
case USB_DEVICE_DESCRIPTOR:
|
||||
*reply_data = &USB_DeviceDescriptor;
|
||||
*reply_length = USB_DeviceDescriptor.bLength;
|
||||
break;
|
||||
|
||||
case USB_CONFIGURATION_DESCRIPTOR:
|
||||
*reply_data = &USB_ConfigurationInterfaceDescriptor;
|
||||
if(*reply_length < USB_ConfigurationInterfaceDescriptor
|
||||
.configuration.wTotalLength)
|
||||
{
|
||||
*reply_length = USB_ConfigurationInterfaceDescriptor
|
||||
.configuration.bLength;
|
||||
}
|
||||
else
|
||||
{
|
||||
*reply_length = USB_ConfigurationInterfaceDescriptor
|
||||
.configuration.wTotalLength;
|
||||
}
|
||||
break;
|
||||
|
||||
case USB_STRING_DESCRIPTOR:
|
||||
switch(descriptor_index)
|
||||
{
|
||||
case 0:
|
||||
*reply_data = (uint8_t*)USB_StringDescriptor_LangID;
|
||||
*reply_length = (uint8_t)*USB_StringDescriptor_LangID;
|
||||
break;
|
||||
case 1:
|
||||
*reply_data = (uint8_t*)USB_StringDescriptor_Vendor;
|
||||
*reply_length = (uint8_t)*USB_StringDescriptor_Vendor;
|
||||
break;
|
||||
case 2:
|
||||
*reply_data = (uint8_t*)USB_StringDescriptor_Product;
|
||||
*reply_length = (uint8_t)*USB_StringDescriptor_Product;
|
||||
break;
|
||||
case 3:;
|
||||
// String descriptors are 16 bits per char
|
||||
static uint16_t buff[25];
|
||||
// The first byte is the total length in bytes, the second
|
||||
// byte is the descriptor type (3)
|
||||
buff[0] = (0x03 << 8) | sizeof(buff);
|
||||
// The unique device ID is 96 bits = 12 bytes long
|
||||
for(int i = 0; i < 12; i++)
|
||||
{
|
||||
uint8_t uid_byte = *((uint8_t*)UID_BASE + i);
|
||||
// The representation does not matter for the serial, so
|
||||
// we're using one of the first 16 letters of the
|
||||
// alphabet for each nibble
|
||||
buff[1 + 2 * i] = 'A' + (uid_byte & 0x0f);
|
||||
buff[2 + 2 * i] = 'A' + (uid_byte >> 4);
|
||||
}
|
||||
|
||||
*reply_data = (uint8_t*)buff;
|
||||
*reply_length = (uint8_t)*buff;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case USB_INTERFACE_DESCRIPTOR:
|
||||
*reply_data = &USB_ConfigurationInterfaceDescriptor
|
||||
.main_interface;
|
||||
*reply_length = USB_ConfigurationInterfaceDescriptor
|
||||
.main_interface.bLength;
|
||||
break;
|
||||
|
||||
case USB_DEVICE_QUALIFIER_DESCRIPTOR:
|
||||
// Device is full-speed only, so it must return a request error
|
||||
*reply_response = USB_EP_TX_STALL;
|
||||
*reply_data = NULL;
|
||||
break;
|
||||
|
||||
case USB_ENDPOINT_DESCRIPTOR:
|
||||
case USB_OTHER_DESCRIPTOR:
|
||||
case USB_INTERFACE_POWER_DESCRIPTOR:
|
||||
case USB_INTERFACE_ASSOCIATION_DESCRIPTOR:
|
||||
case USB_CLASS_SPECIFIC_INTERFACE_DESCRIPTOR:
|
||||
case USB_CLASS_SPECIFIC_ENDPOINT_DESCRIPTOR:
|
||||
// Not implemented
|
||||
break;
|
||||
}
|
||||
}
|
||||
113
stm32f103t8u6-bootloader/src/usb_descriptors.h
Normal file
113
stm32f103t8u6-bootloader/src/usb_descriptors.h
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include "stm32f103x6.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t bLength;
|
||||
uint8_t bDescriptorType;
|
||||
uint16_t bcdUSB;
|
||||
uint8_t bDeviceClass;
|
||||
uint8_t bDeviceSubClass;
|
||||
uint8_t bDeviceProtocol;
|
||||
uint8_t bMaxPacketSize0;
|
||||
uint16_t idVendor;
|
||||
uint16_t idProduct;
|
||||
uint16_t bcdDevice;
|
||||
uint8_t iManufacturer;
|
||||
uint8_t iProduct;
|
||||
uint8_t iSerialNumber;
|
||||
uint8_t bNumConfigurations;
|
||||
} __attribute__((packed, aligned(1))) USB_DeviceDescriptor_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t bLength;
|
||||
uint8_t bDescriptorType;
|
||||
uint16_t wTotalLength;
|
||||
uint8_t bNumInterfaces;
|
||||
uint8_t bConfigurationValue;
|
||||
uint8_t iConfiguration;
|
||||
uint8_t bmAttributes;
|
||||
uint8_t bMaxPower;
|
||||
} __attribute__((packed, aligned(1))) USB_ConfigurationDescriptor_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t bLength;
|
||||
uint8_t bDescriptorType;
|
||||
uint8_t bInterfaceNumber;
|
||||
uint8_t bAlternateSetting;
|
||||
uint8_t bNumEndpoints;
|
||||
uint8_t bInterfaceClass;
|
||||
uint8_t bInterfaceSubClass;
|
||||
uint8_t bInterfaceProtocol;
|
||||
uint8_t iInterface;
|
||||
} __attribute__((packed, aligned(1))) USB_InterfaceDescriptor_t;
|
||||
|
||||
// Endpoint direction for the bEndpointAddress field
|
||||
#define USB_ENDPOINT_OUT 0x00
|
||||
#define USB_ENDPOINT_IN 0x80
|
||||
|
||||
// Flags in bmAttributes
|
||||
#define USB_ENDPOINT_CONTROL 0x00
|
||||
#define USB_ENDPOINT_ISOCHRONOUS 0x01
|
||||
#define USB_ENDPOINT_BULK 0x02
|
||||
#define USB_ENDPOINT_INTERRUPT 0x03
|
||||
|
||||
#define USB_ENDPOINT_NO_SYNCHRONIZATION 0x00
|
||||
#define USB_ENDPOINT_ASYNCHRONOUS 0x04
|
||||
#define USB_ENDPOINT_ADAPTIVE 0x08
|
||||
#define USB_ENDPOINT_SYNCHRONOUS 0x0c
|
||||
|
||||
#define USB_ENDPOINT_DATA 0x00
|
||||
#define USB_ENDPOINT_FEEDBACK 0x10
|
||||
#define USB_ENDPOINT_IMPLICIT_FEEDBACK_DATA 0x20
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t bLength;
|
||||
uint8_t bDescriptorType;
|
||||
uint8_t bEndpointAddress;
|
||||
uint8_t bmAttributes;
|
||||
uint16_t wMaxPacketSize;
|
||||
uint8_t bInterval;
|
||||
} __attribute__((packed, aligned(1))) USB_EndpointDescriptor_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
USB_ConfigurationDescriptor_t configuration;
|
||||
USB_InterfaceDescriptor_t main_interface;
|
||||
USB_EndpointDescriptor_t data_in_endpoint;
|
||||
USB_EndpointDescriptor_t data_out_endpoint;
|
||||
} __attribute__((packed, aligned(1))) USB_WholeDescriptor_t;
|
||||
|
||||
|
||||
typedef enum
|
||||
{
|
||||
USB_DEVICE_DESCRIPTOR = 0x01,
|
||||
USB_CONFIGURATION_DESCRIPTOR = 0x02,
|
||||
USB_STRING_DESCRIPTOR = 0x03,
|
||||
USB_INTERFACE_DESCRIPTOR = 0x04,
|
||||
USB_ENDPOINT_DESCRIPTOR = 0x05,
|
||||
USB_DEVICE_QUALIFIER_DESCRIPTOR = 0x06,
|
||||
USB_OTHER_DESCRIPTOR = 0x07,
|
||||
USB_INTERFACE_POWER_DESCRIPTOR = 0x08,
|
||||
USB_INTERFACE_ASSOCIATION_DESCRIPTOR = 0x0b,
|
||||
USB_CLASS_SPECIFIC_INTERFACE_DESCRIPTOR = 0x24,
|
||||
USB_CLASS_SPECIFIC_ENDPOINT_DESCRIPTOR = 0x25
|
||||
|
||||
} __attribute__((packed)) USB_DescriptorType_t;
|
||||
|
||||
#define USB_STRING_DESCRIPTOR_LENGTH(...) \
|
||||
(sizeof((uint16_t[]){__VA_ARGS__}) + 2)
|
||||
#define USB_BUILD_STRING_DESCRIPTOR(...) \
|
||||
{USB_STRING_DESCRIPTOR_LENGTH(__VA_ARGS__) \
|
||||
| (USB_STRING_DESCRIPTOR << 8), __VA_ARGS__}
|
||||
|
||||
|
||||
void USB_HandleGetDescriptor(USB_DescriptorType_t descriptor_type,
|
||||
int descriptor_index, const void **reply_data, int *reply_length,
|
||||
uint8_t *reply_response);
|
||||
55
stm32f103t8u6-bootloader/src/usb_util.c
Normal file
55
stm32f103t8u6-bootloader/src/usb_util.c
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
#include "usb_util.h"
|
||||
|
||||
void USB_PMAToMemory(void *mem, uint16_t offset, size_t length)
|
||||
{
|
||||
// Only words can be copied. Thus, if the length is not even, it has to be
|
||||
// incremented to ensure that the last byte is copied. This of course means
|
||||
// that the target memory area must be of even length!
|
||||
if(length & 1)
|
||||
{
|
||||
length++;
|
||||
}
|
||||
|
||||
uint8_t *dst = (uint8_t*)mem;
|
||||
|
||||
uint8_t *pma = (uint8_t*)(USB_PMA_ADDR + 2 * offset);
|
||||
for(unsigned int i = 0; i < length / 2; i++)
|
||||
{
|
||||
dst[2 * i] = *pma++;
|
||||
dst[2 * i + 1] = *pma++;
|
||||
pma += 2;
|
||||
}
|
||||
}
|
||||
|
||||
void USB_MemoryToPMA(uint16_t offset, const void *mem, size_t length)
|
||||
{
|
||||
// Only words can be copied. Thus, if the length is not even, it has to be
|
||||
// incremented to ensure that the last byte is copied. Since the PMA buffer
|
||||
// always has even size, this is not a problem.
|
||||
if(length & 1)
|
||||
{
|
||||
length++;
|
||||
}
|
||||
|
||||
const uint8_t *src = (const uint8_t*)mem;
|
||||
|
||||
uint16_t *pma = (uint16_t*)(USB_PMA_ADDR + 2 * offset);
|
||||
for(unsigned int i = 0; i < length / 2; i++)
|
||||
{
|
||||
uint16_t tmp = src[2 * i] | (src[2 * i + 1] << 8);
|
||||
*pma++ = tmp;
|
||||
pma++;
|
||||
}
|
||||
}
|
||||
|
||||
void USB_SetEPR(volatile uint16_t *EPR, uint16_t status)
|
||||
{
|
||||
// Caution: This function does a read-modify-write and is prone to
|
||||
// unexpected behaviour when there are transactions going one, because the
|
||||
// register contents might change during the function's execution. Thus,
|
||||
// only use this function in initialisation code!
|
||||
volatile uint16_t v = *EPR;
|
||||
status ^= v & (USB_EP0R_DTOG_RX | USB_EP0R_STAT_RX |\
|
||||
USB_EP0R_DTOG_TX | USB_EP0R_STAT_TX);
|
||||
*EPR = status;
|
||||
}
|
||||
7
stm32f103t8u6-bootloader/src/usb_util.h
Normal file
7
stm32f103t8u6-bootloader/src/usb_util.h
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include "usb.h"
|
||||
|
||||
void USB_PMAToMemory(void *mem, uint16_t offset, size_t length);
|
||||
void USB_MemoryToPMA(uint16_t offset, const void *mem, size_t length);
|
||||
void USB_SetEPR(volatile uint16_t *EPR, uint16_t status);
|
||||
10
stm32f103t8u6-bootloader/src/util.c
Normal file
10
stm32f103t8u6-bootloader/src/util.c
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
#include "util.h"
|
||||
|
||||
void Util_Delay(unsigned int delay)
|
||||
{
|
||||
SysTick->LOAD = delay;
|
||||
SysTick->VAL = 0;
|
||||
SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | SysTick_CTRL_ENABLE_Msk;
|
||||
while(!((SysTick->CTRL) & SysTick_CTRL_COUNTFLAG_Msk));
|
||||
SysTick->CTRL = 0;
|
||||
}
|
||||
6
stm32f103t8u6-bootloader/src/util.h
Normal file
6
stm32f103t8u6-bootloader/src/util.h
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include "stm32f103x6.h"
|
||||
|
||||
// Delay in AHB clock cycles
|
||||
void Util_Delay(unsigned int delay);
|
||||
Loading…
Add table
Add a link
Reference in a new issue