mirror of https://github.com/commaai/panda.git
refactor SPI and make flasher reliable
This commit is contained in:
parent
a2523f2d3a
commit
0a5a8ab5ec
|
@ -12,9 +12,6 @@
|
|||
#include "libc.h"
|
||||
#include "gpio.h"
|
||||
|
||||
|
||||
void spi_cb_rx(uint8_t *data, int len) {};
|
||||
|
||||
#include "drivers/drivers.h"
|
||||
#include "drivers/spi.h"
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
//#define DEBUG
|
||||
//#define DEBUG_USB
|
||||
//#define DEBUG_SPI
|
||||
|
||||
#ifdef STM32F4
|
||||
#define PANDA
|
||||
|
|
|
@ -70,8 +70,7 @@ void timer_init(TIM_TypeDef *TIM, int psc);
|
|||
// IRQs: DMA2_Stream2, DMA2_Stream3, EXTI4
|
||||
|
||||
void spi_init();
|
||||
void spi_cb_rx(uint8_t *data, int len);
|
||||
void spi_tx_dma(void *addr, int len);
|
||||
int spi_cb_rx(uint8_t *data, int len, uint8_t *data_out);
|
||||
|
||||
|
||||
// ********************* CAN *********************
|
||||
|
|
|
@ -17,6 +17,10 @@ void spi_init() {
|
|||
NVIC_EnableIRQ(DMA2_Stream3_IRQn);
|
||||
//NVIC_EnableIRQ(SPI1_IRQn);
|
||||
|
||||
// reset handshake back to pull up
|
||||
GPIOB->MODER &= ~(GPIO_MODER_MODER0);
|
||||
GPIOB->PUPDR |= GPIO_PUPDR_PUPDR0_0;
|
||||
|
||||
// setup interrupt on falling edge of SPI enable (on PA4)
|
||||
SYSCFG->EXTICR[2] = SYSCFG_EXTICR2_EXTI4_PA;
|
||||
EXTI->IMR = (1 << 4);
|
||||
|
@ -39,6 +43,12 @@ void spi_tx_dma(void *addr, int len) {
|
|||
DMA2_Stream3->CR |= DMA_SxCR_TCIE;
|
||||
|
||||
SPI1->CR2 |= SPI_CR2_TXDMAEN;
|
||||
|
||||
// signal data is ready by driving low
|
||||
// esp must be configured as input by this point
|
||||
GPIOB->MODER &= ~(GPIO_MODER_MODER0);
|
||||
GPIOB->MODER |= GPIO_MODER_MODER0_0;
|
||||
GPIOB->ODR &= ~(GPIO_ODR_ODR_0);
|
||||
}
|
||||
|
||||
void spi_rx_dma(void *addr, int len) {
|
||||
|
@ -64,11 +74,22 @@ void spi_rx_dma(void *addr, int len) {
|
|||
|
||||
// ***************************** SPI IRQs *****************************
|
||||
|
||||
// can't go on the stack cause it's DMAed
|
||||
uint8_t spi_tx_buf[0x44];
|
||||
|
||||
// SPI RX
|
||||
void DMA2_Stream2_IRQHandler(void) {
|
||||
// ack
|
||||
DMA2->LIFCR = DMA_LIFCR_CTCIF2;
|
||||
spi_cb_rx(spi_buf, 0x13);
|
||||
int *resp_len = (int*)spi_tx_buf;
|
||||
memset(spi_tx_buf, 0xaa, 0x44);
|
||||
*resp_len = spi_cb_rx(spi_buf, 0x13, spi_tx_buf+4);
|
||||
#ifdef DEBUG_SPI
|
||||
puts("SPI write: ");
|
||||
puth(*resp_len);
|
||||
puts("\n");
|
||||
#endif
|
||||
spi_tx_dma(spi_tx_buf, *resp_len + 4);
|
||||
}
|
||||
|
||||
// SPI TX
|
||||
|
@ -76,6 +97,10 @@ void DMA2_Stream3_IRQHandler(void) {
|
|||
// ack
|
||||
DMA2->LIFCR = DMA_LIFCR_CTCIF3;
|
||||
|
||||
#ifdef DEBUG_SPI
|
||||
puts("SPI handshake\n");
|
||||
#endif
|
||||
|
||||
// reset handshake back to pull up
|
||||
GPIOB->MODER &= ~(GPIO_MODER_MODER0);
|
||||
GPIOB->PUPDR |= GPIO_PUPDR_PUPDR0_0;
|
||||
|
|
27
board/main.c
27
board/main.c
|
@ -358,25 +358,20 @@ int usb_cb_control_msg(USB_Setup_TypeDef *setup, uint8_t *resp, int hardwired) {
|
|||
}
|
||||
|
||||
#ifdef PANDA
|
||||
|
||||
// can't go on the stack cause it's DMAed
|
||||
uint8_t spi_tx_buf[0x44];
|
||||
|
||||
void spi_cb_rx(uint8_t *data, int len) {
|
||||
memset(spi_tx_buf, 0xaa, 0x44);
|
||||
int spi_cb_rx(uint8_t *data, int len, uint8_t *data_out) {
|
||||
// data[0] = endpoint
|
||||
// data[2] = length
|
||||
// data[4:] = data
|
||||
int *resp_len = (int*)spi_tx_buf;
|
||||
*resp_len = 0;
|
||||
|
||||
int resp_len = 0;
|
||||
switch (data[0]) {
|
||||
case 0:
|
||||
// control transfer
|
||||
*resp_len = usb_cb_control_msg((USB_Setup_TypeDef *)(data+4), spi_tx_buf+4, 0);
|
||||
resp_len = usb_cb_control_msg((USB_Setup_TypeDef *)(data+4), data_out, 0);
|
||||
break;
|
||||
case 1:
|
||||
// ep 1, read
|
||||
*resp_len = usb_cb_ep1_in(spi_tx_buf+4, 0x40, 0);
|
||||
resp_len = usb_cb_ep1_in(data_out, 0x40, 0);
|
||||
break;
|
||||
case 2:
|
||||
// ep 2, send serial
|
||||
|
@ -387,18 +382,12 @@ void spi_cb_rx(uint8_t *data, int len) {
|
|||
usb_cb_ep3_out(data+4, data[2], 0);
|
||||
break;
|
||||
}
|
||||
spi_tx_dma(spi_tx_buf, 0x44);
|
||||
|
||||
// signal data is ready by driving low
|
||||
// esp must be configured as input by this point
|
||||
GPIOB->MODER &= ~(GPIO_MODER_MODER0);
|
||||
GPIOB->MODER |= GPIO_MODER_MODER0_0;
|
||||
GPIOB->ODR &= ~(GPIO_ODR_ODR_0);
|
||||
return resp_len;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
void spi_cb_rx(uint8_t *data, int len) {};
|
||||
int spi_cb_rx(uint8_t *data, int len) { return 0; };
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -460,7 +449,7 @@ int main() {
|
|||
|
||||
// set PWM
|
||||
fan_init();
|
||||
fan_set_speed(65535);
|
||||
fan_set_speed(0);
|
||||
|
||||
puts("**** INTERRUPTS ON ****\n");
|
||||
|
||||
|
|
|
@ -1,27 +1,83 @@
|
|||
/*void lock_bootloader() {
|
||||
if (FLASH->OPTCR & FLASH_OPTCR_nWRP_0) {
|
||||
FLASH->OPTKEYR = 0x08192A3B;
|
||||
FLASH->OPTKEYR = 0x4C5D6E7F;
|
||||
// can't go on the stack cause it's DMAed
|
||||
uint8_t spi_tx_buf[0x44];
|
||||
|
||||
// write protect the bootloader
|
||||
FLASH->OPTCR &= ~FLASH_OPTCR_nWRP_0;
|
||||
uint32_t *prog_ptr;
|
||||
int unlocked = 0;
|
||||
|
||||
// OPT program
|
||||
FLASH->OPTCR |= FLASH_OPTCR_OPTSTRT;
|
||||
int spi_cb_rx(uint8_t *data, int len, uint8_t *data_out) {
|
||||
// get serial number even in bootstub mode
|
||||
if (memcmp("\x00\x00\x00\x00\x40\xD0\x00\x00\x00\x00\x20\x00", data, 0xC) == 0) {
|
||||
memcpy(data_out, (void *)0x1fff79e0, 0x20);
|
||||
return 0x20;
|
||||
}
|
||||
|
||||
// flasher mode
|
||||
if (data[0] == (0xff^data[1]) &&
|
||||
data[2] == (0xff^data[3])) {
|
||||
int sec;
|
||||
memset(data_out, 0, 4);
|
||||
memcpy(data_out+4, "\xde\xad\xd0\x0d", 4);
|
||||
data_out[0] = 0xff;
|
||||
data_out[2] = data[0];
|
||||
data_out[3] = data[1];
|
||||
switch (data[0]) {
|
||||
case 0xf:
|
||||
// echo
|
||||
data_out[1] = 0xff;
|
||||
break;
|
||||
case 0x10:
|
||||
// unlock flash
|
||||
if (FLASH->CR & FLASH_CR_LOCK) {
|
||||
FLASH->KEYR = 0x45670123;
|
||||
FLASH->KEYR = 0xCDEF89AB;
|
||||
data_out[1] = 0xff;
|
||||
}
|
||||
set_led(LED_GREEN, 1);
|
||||
unlocked = 1;
|
||||
prog_ptr = (uint32_t *)0x8004000;
|
||||
break;
|
||||
case 0x11:
|
||||
// erase
|
||||
sec = data[2] & 0xF;
|
||||
// don't erase the bootloader
|
||||
if (sec != 0 && sec < 12 && unlocked) {
|
||||
FLASH->CR = (sec << 3) | FLASH_CR_SER;
|
||||
FLASH->CR |= FLASH_CR_STRT;
|
||||
while (FLASH->SR & FLASH_SR_BSY);
|
||||
data_out[1] = 0xff;
|
||||
}
|
||||
break;
|
||||
case 0x12:
|
||||
if (data[2] <= 4 && unlocked) {
|
||||
set_led(LED_RED, 0);
|
||||
for (int i = 0; i < data[2]; i++) {
|
||||
// program byte 1
|
||||
FLASH->CR = FLASH_CR_PSIZE_1 | FLASH_CR_PG;
|
||||
|
||||
*prog_ptr = *(uint32_t*)(data+4+(i*4));
|
||||
while (FLASH->SR & FLASH_SR_BSY);
|
||||
|
||||
// relock it
|
||||
FLASH->OPTCR |= FLASH_OPTCR_OPTLOCK;
|
||||
|
||||
//*(uint64_t*)(&spi_tx_buf[0x30+(i*4)]) = *prog_ptr;
|
||||
prog_ptr++;
|
||||
}
|
||||
set_led(LED_RED, 1);
|
||||
data_out[1] = 0xff;
|
||||
}
|
||||
break;
|
||||
case 0x13:
|
||||
// reset
|
||||
NVIC_SystemReset();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 8;
|
||||
}
|
||||
}*/
|
||||
|
||||
void spi_flasher() {
|
||||
// green LED on for flashing
|
||||
GPIOC->MODER |= GPIO_MODER_MODER6_0;
|
||||
GPIOC->ODR &= ~(1 << 6);
|
||||
__disable_irq();
|
||||
|
||||
RCC->AHB1ENR |= RCC_AHB1ENR_DMA2EN;
|
||||
RCC->APB2ENR |= RCC_APB2ENR_SPI1EN;
|
||||
|
@ -32,112 +88,13 @@ void spi_flasher() {
|
|||
GPIOA->AFR[0] = GPIO_AF5_SPI1 << (4*4) | GPIO_AF5_SPI1 << (5*4) |
|
||||
GPIO_AF5_SPI1 << (6*4) | GPIO_AF5_SPI1 << (7*4);
|
||||
|
||||
// blue LED on for flashing
|
||||
set_led(LED_BLUE, 1);
|
||||
|
||||
// flasher
|
||||
spi_init();
|
||||
__enable_irq();
|
||||
|
||||
unsigned char spi_rx_buf[0x14];
|
||||
unsigned char spi_tx_buf[0x44];
|
||||
|
||||
int i;
|
||||
int sec;
|
||||
int rcv = 0;
|
||||
int lastval = (GPIOA->IDR & (1 << 4));
|
||||
uint32_t *prog_ptr = (uint32_t *)0x8004000;
|
||||
|
||||
while (1) {
|
||||
int val = (GPIOA->IDR & (1 << 4));
|
||||
if (!val && lastval) {
|
||||
spi_rx_dma(spi_rx_buf, 0x14);
|
||||
rcv = 1;
|
||||
}
|
||||
lastval = val;
|
||||
|
||||
if (rcv && (DMA2->LISR & DMA_LISR_TCIF2)) {
|
||||
DMA2->LIFCR = DMA_LIFCR_CTCIF2;
|
||||
|
||||
rcv = 0;
|
||||
memset(spi_tx_buf, 0, 0x44);
|
||||
spi_tx_buf[0x40] = 0xde;
|
||||
spi_tx_buf[0x41] = 0xad;
|
||||
spi_tx_buf[0x42] = 0xd0;
|
||||
spi_tx_buf[0x43] = 0x0d;
|
||||
|
||||
if (memcmp("\x00\x00\x00\x00\x40\xD0\x00\x00\x00\x00\x20\x00", spi_rx_buf, 0xC) == 0) {
|
||||
*(uint32_t*)(&spi_tx_buf[0]) = 0x20;
|
||||
memcpy(spi_tx_buf+4, (void *)0x1fff79e0, 0x20);
|
||||
} else if (spi_rx_buf[0] == (0xff^spi_rx_buf[1]) &&
|
||||
spi_rx_buf[2] == (0xff^spi_rx_buf[3])) {
|
||||
spi_tx_buf[0] = 0xff;
|
||||
*(uint32_t*)(&spi_tx_buf[4]) = FLASH->CR;
|
||||
*(uint32_t*)(&spi_tx_buf[8]) = (uint32_t)prog_ptr;
|
||||
// valid
|
||||
switch (spi_rx_buf[0]) {
|
||||
case 0x10:
|
||||
// unlock flash
|
||||
if (FLASH->CR & FLASH_CR_LOCK) {
|
||||
FLASH->KEYR = 0x45670123;
|
||||
FLASH->KEYR = 0xCDEF89AB;
|
||||
spi_tx_buf[1] = 0xff;
|
||||
}
|
||||
break;
|
||||
case 0x11:
|
||||
// erase
|
||||
sec = spi_rx_buf[2] & 0xF;
|
||||
// don't erase the bootloader
|
||||
if (sec != 0 && sec < 12) {
|
||||
FLASH->CR = (sec << 3) | FLASH_CR_SER;
|
||||
FLASH->CR |= FLASH_CR_STRT;
|
||||
while (FLASH->SR & FLASH_SR_BSY);
|
||||
spi_tx_buf[1] = 0xff;
|
||||
}
|
||||
break;
|
||||
case 0x12:
|
||||
if (spi_rx_buf[2] <= 4) {
|
||||
set_led(LED_RED, 0);
|
||||
for (i = 0; i < spi_rx_buf[2]; i++) {
|
||||
// program byte 1
|
||||
FLASH->CR = FLASH_CR_PSIZE_1 | FLASH_CR_PG;
|
||||
|
||||
*prog_ptr = *(uint32_t*)(spi_rx_buf+4+(i*4));
|
||||
while (FLASH->SR & FLASH_SR_BSY);
|
||||
|
||||
*(uint64_t*)(&spi_tx_buf[0x30+(i*4)]) = *prog_ptr;
|
||||
prog_ptr++;
|
||||
}
|
||||
set_led(LED_RED, 1);
|
||||
spi_tx_buf[1] = 0xff;
|
||||
}
|
||||
break;
|
||||
case 0x13:
|
||||
// reset
|
||||
NVIC_SystemReset();
|
||||
break;
|
||||
case 0x14:
|
||||
// bootloader
|
||||
/*enter_bootloader_mode = ENTER_BOOTLOADER_MAGIC;
|
||||
NVIC_SystemReset();*/
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
memcpy(spi_tx_buf+0x10, spi_rx_buf, 0x14);
|
||||
while (1) { }
|
||||
}
|
||||
|
||||
spi_tx_dma(spi_tx_buf, 0x44);
|
||||
|
||||
// signal data is ready by driving low
|
||||
// esp must be configured as input by this point
|
||||
GPIOB->MODER &= ~(GPIO_MODER_MODER0);
|
||||
GPIOB->MODER |= GPIO_MODER_MODER0_0;
|
||||
GPIOB->ODR &= ~(GPIO_ODR_ODR_0);
|
||||
}
|
||||
|
||||
if (DMA2->LISR & DMA_LISR_TCIF3) {
|
||||
DMA2->LIFCR = DMA_LIFCR_CTCIF3;
|
||||
|
||||
// reset handshake back to pull up
|
||||
GPIOB->MODER &= ~(GPIO_MODER_MODER0);
|
||||
GPIOB->PUPDR |= GPIO_PUPDR_PUPDR0_0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -56,7 +56,9 @@ static int ICACHE_FLASH_ATTR __spi_comm(char *dat, int len, uint32_t *recvData,
|
|||
SPIMasterSendData(SpiNum_HSPI, &spiData);
|
||||
|
||||
// give the ST time to be ready, up to 1s
|
||||
for (int i = 0;(gpio_input_get() & (1 << 4)) && i < 100000; i++) os_delay_us(10);
|
||||
for (int i = 0;(gpio_input_get() & (1 << 4)) && i < 100000; i++) {
|
||||
os_delay_us(10);
|
||||
}
|
||||
|
||||
// TODO: handle this better
|
||||
if (gpio_input_get() & (1 << 4)) os_printf("ERROR: SPI receive failed\n");
|
||||
|
@ -70,6 +72,10 @@ static int ICACHE_FLASH_ATTR __spi_comm(char *dat, int len, uint32_t *recvData,
|
|||
SPIMasterRecvData(SpiNum_HSPI, &spiData);
|
||||
int length = recvData[0];
|
||||
|
||||
if (length > 0x40) {
|
||||
os_printf("SPI: BAD LENGTH RECEIVED\n");
|
||||
}
|
||||
|
||||
// got response, 0x40 works, 0x44 does not
|
||||
spiData.data = recvData+1;
|
||||
spiData.dataLen = recvDataLen;
|
||||
|
|
|
@ -35,44 +35,61 @@ LOCAL os_timer_t ota_reboot_timer;
|
|||
|
||||
#define FIRMWARE_SIZE 503808
|
||||
|
||||
void st_flash() {
|
||||
void ICACHE_FLASH_ATTR st_flash() {
|
||||
int i;
|
||||
|
||||
if (st_firmware != NULL) {
|
||||
// stupid watchdog test
|
||||
//system_soft_wdt_stop();
|
||||
|
||||
// boot mode
|
||||
os_printf("st_flash: enter boot mode\n");
|
||||
st_set_boot_mode(1);
|
||||
|
||||
// echo
|
||||
os_printf("st_flash: wait for echo\n");
|
||||
int i;
|
||||
for (i = 0; i < 10; i++) {
|
||||
if (st_cmd(0xf, 0, NULL) == 1) break;
|
||||
os_printf(" miss: %d\n", i);
|
||||
}
|
||||
|
||||
// unlock flash
|
||||
os_printf("st_flash: unlock flash\n");
|
||||
st_cmd(0x10, 0, NULL);
|
||||
st_cmd(0xf, 0, NULL);
|
||||
|
||||
// erase sector 1
|
||||
os_printf("st_flash: erase sector 1\n");
|
||||
st_cmd(0x11, 1, NULL);
|
||||
st_cmd(0xf, 0, NULL);
|
||||
|
||||
if (real_content_length >= 16384) {
|
||||
// erase sector 2
|
||||
os_printf("st_flash: erase sector 2\n");
|
||||
st_cmd(0x11, 2, NULL);
|
||||
st_cmd(0xf, 0, NULL);
|
||||
}
|
||||
|
||||
// real content length will always be 0x10 aligned
|
||||
os_printf("st_flash: flashing\n");
|
||||
for (i = 0; i < real_content_length; i += 0x10) {
|
||||
st_cmd(0x12, 4, &st_firmware[i]);
|
||||
if (!st_cmd(0x12, 4, &st_firmware[i])) {
|
||||
os_printf("st_flash: FAILED, BAILING\n");
|
||||
break;
|
||||
}
|
||||
system_soft_wdt_feed();
|
||||
}
|
||||
|
||||
// reboot into normal mode
|
||||
os_printf("st_flash: rebooting\n");
|
||||
st_set_boot_mode(0);
|
||||
|
||||
// done with this
|
||||
os_free(st_firmware);
|
||||
st_firmware = NULL;
|
||||
|
||||
// watchdog done
|
||||
//system_soft_wdt_restart();
|
||||
}
|
||||
}
|
||||
|
||||
typedef enum {
|
||||
NOT_STARTED,
|
||||
|
@ -87,7 +104,7 @@ typedef enum {
|
|||
web_state_t state = NOT_STARTED;
|
||||
int esp_address, esp_address_erase_limit, start_address;
|
||||
|
||||
void hexdump(char *data, int len) {
|
||||
void ICACHE_FLASH_ATTR hexdump(char *data, int len) {
|
||||
int i;
|
||||
for (i=0;i<len;i++) {
|
||||
if (i!=0 && (i%0x10)==0) os_printf("\n");
|
||||
|
@ -96,7 +113,7 @@ void hexdump(char *data, int len) {
|
|||
os_printf("\n");
|
||||
}
|
||||
|
||||
void st_reset() {
|
||||
void ICACHE_FLASH_ATTR st_reset() {
|
||||
// reset the ST
|
||||
gpio16_output_conf();
|
||||
gpio16_output_set(0);
|
||||
|
@ -105,7 +122,7 @@ void st_reset() {
|
|||
os_delay_us(10000);
|
||||
}
|
||||
|
||||
void st_set_boot_mode(int boot_mode) {
|
||||
void ICACHE_FLASH_ATTR st_set_boot_mode(int boot_mode) {
|
||||
if (boot_mode) {
|
||||
// boot mode (pull low)
|
||||
gpio_output_set(0, (1 << 4), (1 << 4), 0);
|
||||
|
@ -115,9 +132,12 @@ void st_set_boot_mode(int boot_mode) {
|
|||
gpio_output_set((1 << 4), 0, (1 << 4), 0);
|
||||
st_reset();
|
||||
}
|
||||
|
||||
// float boot pin
|
||||
gpio_output_set(0, 0, 0, (1 << 4));
|
||||
}
|
||||
|
||||
int st_cmd(int d1, int d2, char *data) {
|
||||
int ICACHE_FLASH_ATTR st_cmd(int d1, int d2, char *data) {
|
||||
uint32_t __dat[0x14];
|
||||
char *dat = (char *)__dat;
|
||||
memset(dat, 0, 0x14);
|
||||
|
@ -127,10 +147,31 @@ int st_cmd(int d1, int d2, char *data) {
|
|||
dat[2] = d2;
|
||||
dat[3] = 0xFF^d2;
|
||||
if (data != NULL) memcpy(dat+4, data, 0x10);
|
||||
hexdump(dat, 0x14);
|
||||
//hexdump(dat, 0x14);
|
||||
|
||||
spi_comm(dat, 0x14, recv, 0x40);
|
||||
return memcmp(recv+0x10, "\xde\xad\xd0\x0d", 4)==0;
|
||||
//hexdump(recv, 0x44);
|
||||
int good = memcmp(recv+2, "\xde\xad\xd0\x0d", 4)==0;
|
||||
|
||||
if (!good) {
|
||||
os_printf("ST command failed!\n");
|
||||
hexdump(recv, 0x44);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (((uint8_t*)recv)[6] != d1) {
|
||||
os_printf("ST command WRONG\n");
|
||||
hexdump(recv, 0x44);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (((uint8_t*)recv)[5] != 0xFF) {
|
||||
os_printf("ST command logical fail\n");
|
||||
hexdump(recv, 0x44);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void ICACHE_FLASH_ATTR web_rx_cb(void *arg, char *data, uint16_t len) {
|
||||
|
@ -182,7 +223,7 @@ static void ICACHE_FLASH_ATTR web_rx_cb(void *arg, char *data, uint16_t len) {
|
|||
// 0x1000 = user1.bin
|
||||
// 0x81000 = user2.bin
|
||||
// 0x3FE000 = blank.bin
|
||||
os_printf("init st firmware\n");
|
||||
os_printf("init esp firmware\n");
|
||||
char *cl = strstr(data, "Content-Length: ");
|
||||
if (cl != NULL) {
|
||||
// get content length
|
||||
|
|
Loading…
Reference in New Issue