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