blob: 75e80385994923acf682a1c51fd6af39c39b2830 [file] [log] [blame]
Patrick Delaunayfb929282020-09-14 11:13:34 +02001/*
Yann Gautieref4b7d52022-10-03 09:30:34 +02002 * Copyright (c) 2021-2022, STMicroelectronics - All Rights Reserved
Patrick Delaunayfb929282020-09-14 11:13:34 +02003 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
7#include <assert.h>
8#include <errno.h>
Yann Gautieref4b7d52022-10-03 09:30:34 +02009#include <string.h>
Patrick Delaunayfb929282020-09-14 11:13:34 +020010
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
19struct 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
28static 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
46static 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
55static 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
103static 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) {
Yann Gautierc6233162021-11-02 17:03:46 +0100120 return -EIO;
Patrick Delaunayfb929282020-09-14 11:13:34 +0200121 }
122
123 return 0;
124}
125
126static 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 */
160static 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
166int 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}