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