void bootloader(unsigned timeout) { int c; int arg = 0; unsigned i; unsigned address = board_info.fw_size; /* force erase before upload will work */ uint32_t first_word = 0xffffffff; static union { uint8_t c[256]; uint32_t w[64]; } flash_buffer; /* (re)start the timer system */ systick_set_clocksource(STK_CTRL_CLKSOURCE_AHB); systick_set_reload(board_info.systick_mhz * 1000); /* 1ms tick, magic number */ systick_interrupt_enable(); systick_counter_enable(); /* if we are working with a timeout, start it running */ if (timeout) timer[TIMER_BL_WAIT] = timeout; while (true) { // Wait for a command byte led_off(LED_ACTIVITY); do { /* if we have a timeout and the timer has expired, return now */ if (timeout && !timer[TIMER_BL_WAIT]) return; /* try to get a byte from the host */ c = cin_wait(0); } while (c < 0); led_on(LED_ACTIVITY); // common argument handling for commands switch (c) { case PROTO_GET_SYNC: case PROTO_CHIP_ERASE: case PROTO_CHIP_VERIFY: case PROTO_DEBUG: /* expect EOC */ if (cin_wait(1000) != PROTO_EOC) goto cmd_bad; break; case PROTO_PROG_MULTI: /* expect count */ arg = cin_wait(1000); if (arg < 0) goto cmd_bad; break; case PROTO_GET_DEVICE: case PROTO_READ_MULTI: /* expect arg/count then EOC */ arg = cin_wait(1000); if (arg < 0) goto cmd_bad; if (cin_wait(1000) != PROTO_EOC) goto cmd_bad; break; } // handle the command byte switch (c) { case PROTO_GET_SYNC: // sync break; case PROTO_GET_DEVICE: // report board info switch (arg) { case PROTO_DEVICE_BL_REV: cout((uint8_t *)&bl_proto_rev, sizeof(bl_proto_rev)); break; case PROTO_DEVICE_BOARD_ID: cout((uint8_t *)&board_info.board_type, sizeof(board_info.board_type)); break; case PROTO_DEVICE_BOARD_REV: cout((uint8_t *)&board_info.board_rev, sizeof(board_info.board_rev)); break; case PROTO_DEVICE_FW_SIZE: cout((uint8_t *)&board_info.fw_size, sizeof(board_info.fw_size)); break; default: goto cmd_bad; } break; case PROTO_CHIP_ERASE: // erase the program area + read for programming flash_unlock(); for (i = 0; flash_func_sector_size(i) != 0; i++) flash_func_erase_sector(i); address = 0; break; case PROTO_CHIP_VERIFY: // reset for verification of the program area address = 0; break; case PROTO_PROG_MULTI: // program bytes if (arg % 4) goto cmd_bad; if ((address + arg) > board_info.fw_size) goto cmd_bad; if (arg > sizeof(flash_buffer.c)) goto cmd_bad; for (i = 0; i < arg; i++) { c = cin_wait(1000); if (c < 0) goto cmd_bad; flash_buffer.c[i] = c; } if (cin_wait(1000) != PROTO_EOC) goto cmd_bad; if (address == 0) { // save the first word and don't program it until // everything else is done first_word = flash_buffer.w[0]; // replace first word with bits we can overwrite later flash_buffer.w[0] = 0xffffffff; } arg /= 4; for (i = 0; i < arg; i++) { flash_func_write_word(address, flash_buffer.w[i]); address += 4; } break; case PROTO_READ_MULTI: // readback bytes if (arg % 4) goto cmd_bad; if ((address + arg) > board_info.fw_size) goto cmd_bad; arg /= 4; /* handle readback of the not-yet-programmed first word */ if ((address == 0) && (first_word != 0xffffffff)) { cout((uint8_t *)&first_word, 4); address += 4; arg--; } while (arg-- > 0) { cout_word(flash_func_read_word(address)); address += 4; } break; case PROTO_BOOT: // program the deferred first word if (first_word != 0xffffffff) { flash_func_write_word(0, first_word); // revert in case the flash was bad... first_word = 0xffffffff; } // quiesce and jump to the app return; case PROTO_DEBUG: // XXX reserved for ad-hoc debugging as required break; default: continue; } // we got a command worth syncing, so kill the timeout because // we are probably talking to the uploader timeout = 0; // send the sync response for this command sync_response(); continue; cmd_bad: // Currently we do nothing & let the programming tool time out // if that's what it wants to do. // Let the initial delay keep counting down so that we ignore // random chatter from a device. while(true); continue; } }
void vTaskVCP(void *pvParameters) { static u32 address = 0; static uint8_t usb_inited = 0; uint8_t c; int arg; uint8_t tmpbuf[EERROM_SIZE]; for(;;) { //Give the change to the OS scheduling. It is really a fool idea. Change me! //TODO - should use semaphore vTaskDelay( 1 / portTICK_RATE_MS ); // xSemaphoreTake(onVCPSemaphore, portMAX_DELAY); if(usb_inited == 0) { GPIO_InitTypeDef gpio; //This is a trick to perform a USB re-enumerate gpio.GPIO_Pin = GPIO_Pin_12; gpio.GPIO_Speed = GPIO_Speed_100MHz; gpio.GPIO_Mode = GPIO_Mode_OUT; gpio.GPIO_OType = GPIO_OType_PP; gpio.GPIO_PuPd = GPIO_PuPd_NOPULL; GPIO_Init(GPIOB, &gpio); GPIO_SetBits(GPIOB, GPIO_Pin_12 ); vTaskDelay(500 / portTICK_RATE_MS); GPIO_ResetBits(GPIOB, GPIO_Pin_12 ); // Initialize USB VCP. Do it ASAP USBD_Init(&USB_OTG_dev, USB_OTG_FS_CORE_ID, &USR_desc, &USBD_CDC_cb, &USR_cb); usb_inited = 1; } if (VCP_get_char(&c)) { switch(c) { case PROTO_GET_SYNC: if (cin_wait(usb_vcp_timeout) != PROTO_EOC) goto cmd_bad; break; case PROTO_GET_DEVICE: arg = cin_wait(usb_vcp_timeout); if (arg < 0) goto cmd_bad; if (cin_wait(usb_vcp_timeout) != PROTO_EOC) goto cmd_bad; switch (arg) { case PROTO_DEVICE_BL_REV: VCP_send_str((uint8_t*)&bl_proto_rev); break; default: goto cmd_bad; } break; case PROTO_START_TRANS: if (cin_wait(usb_vcp_timeout) != PROTO_EOC) goto cmd_bad; address = 0; memset(tmpbuf,0,EERROM_SIZE); break; case PROTO_SET_PARAMS: arg = cin_wait(usb_vcp_timeout); if (arg < 0) goto cmd_bad; // sanity-check arguments if (arg % 4) goto cmd_bad; if ((address + arg) > EERROM_SIZE) goto cmd_bad; for (int i = 0; i < arg; i++) { c = cin_wait(usb_vcp_timeout); if (c < 0) goto cmd_bad; tmpbuf[address++] = c; } break; case PROTO_END_TRANS: if (cin_wait(usb_vcp_timeout) != PROTO_EOC) goto cmd_bad; //ensure we receive right size if(address == EERROM_SIZE) { memcpy(eeprom_buffer.c,tmpbuf,EERROM_SIZE); // bool ret = StoreParams(); // if(!ret) // { // //TODO - handle flash write error here // } } break; case PROTO_SAVE_TO_FLASH: if (cin_wait(usb_vcp_timeout) != PROTO_EOC) goto cmd_bad; //ensure we receive right size if(address == EERROM_SIZE) { bool ret = StoreParams(); if(!ret) { //TODO - handle flash write error here } } break; case PROTO_GET_PARAMS: if (cin_wait(usb_vcp_timeout) != PROTO_EOC) goto cmd_bad; for (int i = 0; i < EERROM_SIZE; i++) { VCP_put_char(eeprom_buffer.c[i]); //TM_USB_VCP_Putc(testbuf[i]); //vTaskDelay( 1 / portTICK_RATE_MS ); } break; case PROTO_BL_UPLOAD: if (cin_wait(usb_vcp_timeout) != PROTO_EOC) goto cmd_bad; sync_response(); vTaskDelay( 200 / portTICK_RATE_MS ); GPIO_SetBits(GPIOB,GPIO_Pin_12); vTaskDelay( 500 / portTICK_RATE_MS ); GPIO_ResetBits(GPIOB,GPIO_Pin_12); __DSB(); /* Ensure all outstanding memory accesses included buffered write are completed before reset */ SCB->AIRCR = ((0x5FA << SCB_AIRCR_VECTKEY_Pos) | (SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) | SCB_AIRCR_SYSRESETREQ_Msk); /* Keep priority group unchanged */ __DSB(); /* Ensure completion of memory access */ while(1); break; default: continue; } // send the sync response for this command sync_response(); continue; } vTaskDelay( 10 / portTICK_RATE_MS ); continue; cmd_bad: // send an 'invalid' response but don't kill the timeout - could be garbage invalid_response(); continue; cmd_fail: // send a 'command failed' response but don't kill the timeout - could be garbage failure_response(); continue; } }
// Bootloader protocol logic // static void bootloader(void) { uint8_t c; uint8_t count, i; static uint16_t address; // Wait for a command byte LED_BOOTLOADER = LED_ON; c = cin(); LED_BOOTLOADER = LED_OFF; // common tests for EOC switch (c) { case PROTO_GET_SYNC: case PROTO_GET_DEVICE: case PROTO_CHIP_ERASE: case PROTO_PARAM_ERASE: case PROTO_READ_FLASH: case PROTO_DEBUG: if (cin() != PROTO_EOC) goto cmd_bad; } switch (c) { case PROTO_GET_SYNC: // sync break; case PROTO_GET_DEVICE: cout(BOARD_ID); cout(board_frequency); break; case PROTO_CHIP_ERASE: // erase the program area flash_erase_app(); break; case PROTO_PARAM_ERASE: flash_erase_scratch(); break; case PROTO_LOAD_ADDRESS: // set address address = cin(); address |= (uint16_t)cin() << 8; if (cin() != PROTO_EOC) goto cmd_bad; break; case PROTO_PROG_FLASH: // program byte c = cin(); if (cin() != PROTO_EOC) goto cmd_bad; flash_write_byte(address++, c); break; case PROTO_READ_FLASH: // readback byte c = flash_read_byte(address++); cout(c); break; case PROTO_PROG_MULTI: count = cin(); if (count > sizeof(buf)) goto cmd_bad; for (i = 0; i < count; i++) buf[i] = cin(); if (cin() != PROTO_EOC) goto cmd_bad; for (i = 0; i < count; i++) flash_write_byte(address++, buf[i]); break; case PROTO_READ_MULTI: count = cin(); if (cin() != PROTO_EOC) goto cmd_bad; for (i = 0; i < count; i++) { c = flash_read_byte(address++); cout(c); } break; case PROTO_REBOOT: // generate a software reset, which should boot to the application RSTSRC |= (1 << 4); case PROTO_DEBUG: // XXX reserved for ad-hoc debugging as required break; default: goto cmd_bad; } sync_response(); cmd_bad: return; }