1*afad5214SPatrick Delaunay /* 2*afad5214SPatrick Delaunay * Copyright (c) 2021, STMicroelectronics - All Rights Reserved 3*afad5214SPatrick Delaunay * 4*afad5214SPatrick Delaunay * SPDX-License-Identifier: BSD-3-Clause 5*afad5214SPatrick Delaunay */ 6*afad5214SPatrick Delaunay 7*afad5214SPatrick Delaunay #include <assert.h> 8*afad5214SPatrick Delaunay #include <errno.h> 9*afad5214SPatrick Delaunay 10*afad5214SPatrick Delaunay #include <tools_share/firmware_image_package.h> 11*afad5214SPatrick Delaunay 12*afad5214SPatrick Delaunay #include <stm32cubeprogrammer.h> 13*afad5214SPatrick Delaunay #include <usb_dfu.h> 14*afad5214SPatrick Delaunay 15*afad5214SPatrick Delaunay /* Undefined download address */ 16*afad5214SPatrick Delaunay #define UNDEFINED_DOWN_ADDR 0xFFFFFFFF 17*afad5214SPatrick Delaunay 18*afad5214SPatrick Delaunay struct dfu_state { 19*afad5214SPatrick Delaunay uint8_t phase; 20*afad5214SPatrick Delaunay uintptr_t base; 21*afad5214SPatrick Delaunay size_t len; 22*afad5214SPatrick Delaunay uintptr_t address; 23*afad5214SPatrick Delaunay /* working buffer */ 24*afad5214SPatrick Delaunay uint8_t buffer[UCHAR_MAX]; 25*afad5214SPatrick Delaunay }; 26*afad5214SPatrick Delaunay 27*afad5214SPatrick Delaunay static struct dfu_state dfu_state; 28*afad5214SPatrick Delaunay 29*afad5214SPatrick Delaunay /* minimal size of Get Pḧase = offset for additionnl information */ 30*afad5214SPatrick Delaunay #define GET_PHASE_LEN 9 31*afad5214SPatrick Delaunay 32*afad5214SPatrick Delaunay #define DFU_ERROR(...) \ 33*afad5214SPatrick Delaunay { \ 34*afad5214SPatrick Delaunay ERROR(__VA_ARGS__); \ 35*afad5214SPatrick Delaunay if (dfu->phase != PHASE_RESET) { \ 36*afad5214SPatrick Delaunay snprintf((char *)&dfu->buffer[GET_PHASE_LEN], \ 37*afad5214SPatrick Delaunay sizeof(dfu->buffer) - GET_PHASE_LEN, \ 38*afad5214SPatrick Delaunay __VA_ARGS__); \ 39*afad5214SPatrick Delaunay dfu->phase = PHASE_RESET; \ 40*afad5214SPatrick Delaunay dfu->address = UNDEFINED_DOWN_ADDR; \ 41*afad5214SPatrick Delaunay dfu->len = 0; \ 42*afad5214SPatrick Delaunay } \ 43*afad5214SPatrick Delaunay } 44*afad5214SPatrick Delaunay 45*afad5214SPatrick Delaunay static bool is_valid_header(fip_toc_header_t *header) 46*afad5214SPatrick Delaunay { 47*afad5214SPatrick Delaunay if ((header->name == TOC_HEADER_NAME) && (header->serial_number != 0U)) { 48*afad5214SPatrick Delaunay return true; 49*afad5214SPatrick Delaunay } 50*afad5214SPatrick Delaunay 51*afad5214SPatrick Delaunay return false; 52*afad5214SPatrick Delaunay } 53*afad5214SPatrick Delaunay 54*afad5214SPatrick Delaunay static int dfu_callback_upload(uint8_t alt, uintptr_t *buffer, uint32_t *len, 55*afad5214SPatrick Delaunay void *user_data) 56*afad5214SPatrick Delaunay { 57*afad5214SPatrick Delaunay int result = 0; 58*afad5214SPatrick Delaunay uint32_t length = 0; 59*afad5214SPatrick Delaunay struct dfu_state *dfu = (struct dfu_state *)user_data; 60*afad5214SPatrick Delaunay 61*afad5214SPatrick Delaunay switch (usb_dfu_get_phase(alt)) { 62*afad5214SPatrick Delaunay case PHASE_CMD: 63*afad5214SPatrick Delaunay /* Get Pḧase */ 64*afad5214SPatrick Delaunay dfu->buffer[0] = dfu->phase; 65*afad5214SPatrick Delaunay dfu->buffer[1] = (uint8_t)(dfu->address); 66*afad5214SPatrick Delaunay dfu->buffer[2] = (uint8_t)(dfu->address >> 8); 67*afad5214SPatrick Delaunay dfu->buffer[3] = (uint8_t)(dfu->address >> 16); 68*afad5214SPatrick Delaunay dfu->buffer[4] = (uint8_t)(dfu->address >> 24); 69*afad5214SPatrick Delaunay dfu->buffer[5] = 0x00; 70*afad5214SPatrick Delaunay dfu->buffer[6] = 0x00; 71*afad5214SPatrick Delaunay dfu->buffer[7] = 0x00; 72*afad5214SPatrick Delaunay dfu->buffer[8] = 0x00; 73*afad5214SPatrick Delaunay length = GET_PHASE_LEN; 74*afad5214SPatrick Delaunay if (dfu->phase == PHASE_FLASHLAYOUT && 75*afad5214SPatrick Delaunay dfu->address == UNDEFINED_DOWN_ADDR) { 76*afad5214SPatrick Delaunay INFO("Send detach request\n"); 77*afad5214SPatrick Delaunay dfu->buffer[length++] = 0x01; 78*afad5214SPatrick Delaunay } 79*afad5214SPatrick Delaunay if (dfu->phase == PHASE_RESET) { 80*afad5214SPatrick Delaunay /* error information is added by DFU_ERROR macro */ 81*afad5214SPatrick Delaunay length += strnlen((char *)&dfu->buffer[GET_PHASE_LEN], 82*afad5214SPatrick Delaunay sizeof(dfu->buffer) - GET_PHASE_LEN) 83*afad5214SPatrick Delaunay - 1; 84*afad5214SPatrick Delaunay } 85*afad5214SPatrick Delaunay break; 86*afad5214SPatrick Delaunay 87*afad5214SPatrick Delaunay default: 88*afad5214SPatrick Delaunay DFU_ERROR("phase ID :%i, alternate %i for phase %i\n", 89*afad5214SPatrick Delaunay dfu->phase, alt, usb_dfu_get_phase(alt)); 90*afad5214SPatrick Delaunay result = -EIO; 91*afad5214SPatrick Delaunay break; 92*afad5214SPatrick Delaunay } 93*afad5214SPatrick Delaunay 94*afad5214SPatrick Delaunay if (result == 0) { 95*afad5214SPatrick Delaunay *len = length; 96*afad5214SPatrick Delaunay *buffer = (uintptr_t)dfu->buffer; 97*afad5214SPatrick Delaunay } 98*afad5214SPatrick Delaunay 99*afad5214SPatrick Delaunay return result; 100*afad5214SPatrick Delaunay } 101*afad5214SPatrick Delaunay 102*afad5214SPatrick Delaunay static int dfu_callback_download(uint8_t alt, uintptr_t *buffer, uint32_t *len, 103*afad5214SPatrick Delaunay void *user_data) 104*afad5214SPatrick Delaunay { 105*afad5214SPatrick Delaunay struct dfu_state *dfu = (struct dfu_state *)user_data; 106*afad5214SPatrick Delaunay 107*afad5214SPatrick Delaunay if ((dfu->phase != usb_dfu_get_phase(alt)) || 108*afad5214SPatrick Delaunay (dfu->address == UNDEFINED_DOWN_ADDR)) { 109*afad5214SPatrick Delaunay DFU_ERROR("phase ID :%i, alternate %i, address %x\n", 110*afad5214SPatrick Delaunay dfu->phase, alt, (uint32_t)dfu->address); 111*afad5214SPatrick Delaunay return -EIO; 112*afad5214SPatrick Delaunay } 113*afad5214SPatrick Delaunay 114*afad5214SPatrick Delaunay VERBOSE("Download %d %lx %x\n", alt, dfu->address, *len); 115*afad5214SPatrick Delaunay *buffer = dfu->address; 116*afad5214SPatrick Delaunay dfu->address += *len; 117*afad5214SPatrick Delaunay 118*afad5214SPatrick Delaunay if (dfu->address - dfu->base > dfu->len) { 119*afad5214SPatrick Delaunay return -EIO; 120*afad5214SPatrick Delaunay } 121*afad5214SPatrick Delaunay 122*afad5214SPatrick Delaunay return 0; 123*afad5214SPatrick Delaunay } 124*afad5214SPatrick Delaunay 125*afad5214SPatrick Delaunay static int dfu_callback_manifestation(uint8_t alt, void *user_data) 126*afad5214SPatrick Delaunay { 127*afad5214SPatrick Delaunay struct dfu_state *dfu = (struct dfu_state *)user_data; 128*afad5214SPatrick Delaunay 129*afad5214SPatrick Delaunay if (dfu->phase != usb_dfu_get_phase(alt)) { 130*afad5214SPatrick Delaunay ERROR("Manifestation phase ID :%i, alternate %i, address %lx\n", 131*afad5214SPatrick Delaunay dfu->phase, alt, dfu->address); 132*afad5214SPatrick Delaunay return -EIO; 133*afad5214SPatrick Delaunay } 134*afad5214SPatrick Delaunay 135*afad5214SPatrick Delaunay INFO("phase ID :%i, Manifestation %d at %lx\n", 136*afad5214SPatrick Delaunay dfu->phase, alt, dfu->address); 137*afad5214SPatrick Delaunay 138*afad5214SPatrick Delaunay switch (dfu->phase) { 139*afad5214SPatrick Delaunay case PHASE_SSBL: 140*afad5214SPatrick Delaunay if (!is_valid_header((fip_toc_header_t *)dfu->base)) { 141*afad5214SPatrick Delaunay DFU_ERROR("FIP Header check failed for phase %d\n", alt); 142*afad5214SPatrick Delaunay return -EIO; 143*afad5214SPatrick Delaunay } 144*afad5214SPatrick Delaunay VERBOSE("FIP header looks OK.\n"); 145*afad5214SPatrick Delaunay 146*afad5214SPatrick Delaunay /* Configure End with request detach */ 147*afad5214SPatrick Delaunay dfu->phase = PHASE_FLASHLAYOUT; 148*afad5214SPatrick Delaunay dfu->address = UNDEFINED_DOWN_ADDR; 149*afad5214SPatrick Delaunay dfu->len = 0; 150*afad5214SPatrick Delaunay break; 151*afad5214SPatrick Delaunay default: 152*afad5214SPatrick Delaunay DFU_ERROR("Unknown phase\n"); 153*afad5214SPatrick Delaunay } 154*afad5214SPatrick Delaunay 155*afad5214SPatrick Delaunay return 0; 156*afad5214SPatrick Delaunay } 157*afad5214SPatrick Delaunay 158*afad5214SPatrick Delaunay /* Open a connection to the USB device */ 159*afad5214SPatrick Delaunay static const struct usb_dfu_media usb_dfu_fops = { 160*afad5214SPatrick Delaunay .upload = dfu_callback_upload, 161*afad5214SPatrick Delaunay .download = dfu_callback_download, 162*afad5214SPatrick Delaunay .manifestation = dfu_callback_manifestation, 163*afad5214SPatrick Delaunay }; 164*afad5214SPatrick Delaunay 165*afad5214SPatrick Delaunay int stm32cubeprog_usb_load(struct usb_handle *usb_core_handle, 166*afad5214SPatrick Delaunay uintptr_t base, 167*afad5214SPatrick Delaunay size_t len) 168*afad5214SPatrick Delaunay { 169*afad5214SPatrick Delaunay int ret; 170*afad5214SPatrick Delaunay 171*afad5214SPatrick Delaunay usb_core_handle->user_data = (void *)&dfu_state; 172*afad5214SPatrick Delaunay 173*afad5214SPatrick Delaunay INFO("DFU USB START...\n"); 174*afad5214SPatrick Delaunay ret = usb_core_start(usb_core_handle); 175*afad5214SPatrick Delaunay if (ret != USBD_OK) { 176*afad5214SPatrick Delaunay return -EIO; 177*afad5214SPatrick Delaunay } 178*afad5214SPatrick Delaunay 179*afad5214SPatrick Delaunay dfu_state.phase = PHASE_SSBL; 180*afad5214SPatrick Delaunay dfu_state.address = base; 181*afad5214SPatrick Delaunay dfu_state.base = base; 182*afad5214SPatrick Delaunay dfu_state.len = len; 183*afad5214SPatrick Delaunay 184*afad5214SPatrick Delaunay ret = usb_dfu_loop(usb_core_handle, &usb_dfu_fops); 185*afad5214SPatrick Delaunay if (ret != USBD_OK) { 186*afad5214SPatrick Delaunay return -EIO; 187*afad5214SPatrick Delaunay } 188*afad5214SPatrick Delaunay 189*afad5214SPatrick Delaunay INFO("DFU USB STOP...\n"); 190*afad5214SPatrick Delaunay ret = usb_core_stop(usb_core_handle); 191*afad5214SPatrick Delaunay if (ret != USBD_OK) { 192*afad5214SPatrick Delaunay return -EIO; 193*afad5214SPatrick Delaunay } 194*afad5214SPatrick Delaunay 195*afad5214SPatrick Delaunay return 0; 196*afad5214SPatrick Delaunay } 197