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