1 /*
2 * Copyright (c) 2024 Nordic Semiconductor ASA
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <sample_usbd.h>
8
9 #include <zephyr/kernel.h>
10 #include <zephyr/usb/usbd.h>
11 #include <zephyr/usb/class/usbd_dfu.h>
12 #include <zephyr/storage/disk_access.h>
13 #include <zephyr/dfu/mcuboot.h>
14
15 #include <zephyr/logging/log.h>
16 LOG_MODULE_REGISTER(main, LOG_LEVEL_INF);
17
18 USBD_DEVICE_DEFINE(dfu_usbd,
19 DEVICE_DT_GET(DT_NODELABEL(zephyr_udc0)),
20 0x2fe3, 0xffff);
21
22 USBD_DESC_LANG_DEFINE(sample_lang);
23 USBD_DESC_CONFIG_DEFINE(fs_cfg_desc, "DFU FS Configuration");
24 USBD_DESC_CONFIG_DEFINE(hs_cfg_desc, "DFU HS Configuration");
25
26 static const uint8_t attributes = (IS_ENABLED(CONFIG_SAMPLE_USBD_SELF_POWERED) ?
27 USB_SCD_SELF_POWERED : 0) |
28 (IS_ENABLED(CONFIG_SAMPLE_USBD_REMOTE_WAKEUP) ?
29 USB_SCD_REMOTE_WAKEUP : 0);
30 /* Full speed configuration */
31 USBD_CONFIGURATION_DEFINE(sample_fs_config,
32 attributes,
33 CONFIG_SAMPLE_USBD_MAX_POWER, &fs_cfg_desc);
34
35 /* High speed configuration */
36 USBD_CONFIGURATION_DEFINE(sample_hs_config,
37 attributes,
38 CONFIG_SAMPLE_USBD_MAX_POWER, &hs_cfg_desc);
39
40 static void switch_to_dfu_mode(struct usbd_context *const ctx);
41
42 struct dfu_ramdisk_data {
43 const char *name;
44 uint32_t last_block;
45 uint32_t sector_size;
46 uint32_t sector_count;
47 union {
48 uint32_t uploaded;
49 uint32_t downloaded;
50 };
51 };
52
53 static struct dfu_ramdisk_data ramdisk0_data = {
54 .name = "image0",
55 };
56
init_dfu_ramdisk_data(struct dfu_ramdisk_data * const data)57 static int init_dfu_ramdisk_data(struct dfu_ramdisk_data *const data)
58 {
59 int err;
60
61 err = disk_access_init(data->name);
62 if (err) {
63 return err;
64 }
65
66 err = disk_access_status(data->name);
67 if (err) {
68 return err;
69 }
70
71 err = disk_access_ioctl(data->name, DISK_IOCTL_GET_SECTOR_COUNT, &data->sector_count);
72 if (err) {
73 return err;
74 }
75
76 err = disk_access_ioctl(data->name, DISK_IOCTL_GET_SECTOR_SIZE, &data->sector_size);
77 if (err) {
78 return err;
79 }
80
81 LOG_INF("disk %s sector count %u sector size %u",
82 data->name, data->sector_count, data->sector_size);
83
84 return err;
85 }
86
ramdisk_read(void * const priv,const uint32_t block,const uint16_t size,uint8_t buf[static CONFIG_USBD_DFU_TRANSFER_SIZE])87 static int ramdisk_read(void *const priv, const uint32_t block, const uint16_t size,
88 uint8_t buf[static CONFIG_USBD_DFU_TRANSFER_SIZE])
89 {
90 struct dfu_ramdisk_data *const data = priv;
91 int err;
92
93 if (size == 0) {
94 /* There is nothing to upload */
95 return 0;
96 }
97
98 if (block == 0) {
99 if (init_dfu_ramdisk_data(data)) {
100 LOG_ERR("Failed to init ramdisk data");
101 return -EINVAL;
102 }
103
104 data->last_block = 0;
105 data->uploaded = 0;
106 } else {
107 if (data->last_block + 1U != block) {
108 return -EINVAL;
109 }
110
111 }
112
113 if (block >= data->sector_count) {
114 /* Nothing to upload */
115 return 0;
116 }
117
118 err = disk_access_read(data->name, buf, block, 1);
119 if (err) {
120 LOG_ERR("Failed to read from RAMdisk");
121 return err;
122 }
123
124 data->last_block = block;
125 data->uploaded += MIN(size, data->sector_size);
126 LOG_INF("block %u size %u uploaded %u", block, size, data->uploaded);
127
128 return size;
129 }
130
ramdisk_write(void * const priv,const uint32_t block,const uint16_t size,const uint8_t buf[static CONFIG_USBD_DFU_TRANSFER_SIZE])131 static int ramdisk_write(void *const priv, const uint32_t block, const uint16_t size,
132 const uint8_t buf[static CONFIG_USBD_DFU_TRANSFER_SIZE])
133 {
134 struct dfu_ramdisk_data *const data = priv;
135 int err;
136
137 if (block == 0) {
138 if (init_dfu_ramdisk_data(data)) {
139 LOG_ERR("Failed to init ramdisk data");
140 return -EINVAL;
141 }
142
143 data->last_block = 0;
144 data->downloaded = 0;
145 } else {
146 if (data->last_block + 1U != block) {
147 return -EINVAL;
148 }
149
150 }
151
152 if (size == 0) {
153 /* Nothing to write */
154 return 0;
155 }
156
157 err = disk_access_write(data->name, buf, block, 1);
158 if (err) {
159 LOG_ERR("Failed to write to RAMdisk");
160 return err;
161 }
162
163 data->last_block = block;
164 data->downloaded += size;
165 LOG_INF("block %u size %u downloaded %u", block, size, data->downloaded);
166
167 return 0;
168 }
169
170 USBD_DFU_DEFINE_IMG(ramdisk0, "ramdisk0", &ramdisk0_data, ramdisk_read, ramdisk_write, NULL);
171
msg_cb(struct usbd_context * const usbd_ctx,const struct usbd_msg * const msg)172 static void msg_cb(struct usbd_context *const usbd_ctx,
173 const struct usbd_msg *const msg)
174 {
175 LOG_INF("USBD message: %s", usbd_msg_type_string(msg->type));
176
177 if (msg->type == USBD_MSG_CONFIGURATION) {
178 LOG_INF("\tConfiguration value %d", msg->status);
179 }
180
181 if (usbd_can_detect_vbus(usbd_ctx)) {
182 if (msg->type == USBD_MSG_VBUS_READY) {
183 if (usbd_enable(usbd_ctx)) {
184 LOG_ERR("Failed to enable device support");
185 }
186 }
187
188 if (msg->type == USBD_MSG_VBUS_REMOVED) {
189 if (usbd_disable(usbd_ctx)) {
190 LOG_ERR("Failed to disable device support");
191 }
192 }
193 }
194
195 if (msg->type == USBD_MSG_DFU_APP_DETACH) {
196 switch_to_dfu_mode(usbd_ctx);
197 }
198
199 if (msg->type == USBD_MSG_DFU_DOWNLOAD_COMPLETED) {
200 if (IS_ENABLED(CONFIG_BOOTLOADER_MCUBOOT) &&
201 IS_ENABLED(CONFIG_APP_USB_DFU_USE_FLASH_BACKEND)) {
202 boot_request_upgrade(false);
203 }
204 }
205 }
206
switch_to_dfu_mode(struct usbd_context * const ctx)207 static void switch_to_dfu_mode(struct usbd_context *const ctx)
208 {
209 int err;
210
211 LOG_INF("Detach USB device");
212 usbd_disable(ctx);
213 usbd_shutdown(ctx);
214
215 err = usbd_add_descriptor(&dfu_usbd, &sample_lang);
216 if (err) {
217 LOG_ERR("Failed to initialize language descriptor (%d)", err);
218 return;
219 }
220
221 if (usbd_caps_speed(&dfu_usbd) == USBD_SPEED_HS) {
222 err = usbd_add_configuration(&dfu_usbd, USBD_SPEED_HS, &sample_hs_config);
223 if (err) {
224 LOG_ERR("Failed to add High-Speed configuration");
225 return;
226 }
227
228 err = usbd_register_class(&dfu_usbd, "dfu_dfu", USBD_SPEED_HS, 1);
229 if (err) {
230 LOG_ERR("Failed to add register classes");
231 return;
232 }
233
234 usbd_device_set_code_triple(&dfu_usbd, USBD_SPEED_HS, 0, 0, 0);
235 }
236
237 err = usbd_add_configuration(&dfu_usbd, USBD_SPEED_FS, &sample_fs_config);
238 if (err) {
239 LOG_ERR("Failed to add Full-Speed configuration");
240 return;
241 }
242
243 err = usbd_register_class(&dfu_usbd, "dfu_dfu", USBD_SPEED_FS, 1);
244 if (err) {
245 LOG_ERR("Failed to add register classes");
246 return;
247 }
248
249 usbd_device_set_code_triple(&dfu_usbd, USBD_SPEED_FS, 0, 0, 0);
250
251 err = usbd_init(&dfu_usbd);
252 if (err) {
253 LOG_ERR("Failed to initialize USB device support");
254 return;
255 }
256
257 err = usbd_msg_register_cb(&dfu_usbd, msg_cb);
258 if (err) {
259 LOG_ERR("Failed to register message callback");
260 return;
261 }
262
263 err = usbd_enable(&dfu_usbd);
264 if (err) {
265 LOG_ERR("Failed to enable USB device support");
266 }
267 }
268
main(void)269 int main(void)
270 {
271 struct usbd_context *sample_usbd;
272 int ret;
273
274 sample_usbd = sample_usbd_init_device(msg_cb);
275 if (sample_usbd == NULL) {
276 LOG_ERR("Failed to initialize USB device");
277 return -ENODEV;
278 }
279
280 if (!usbd_can_detect_vbus(sample_usbd)) {
281 ret = usbd_enable(sample_usbd);
282 if (ret) {
283 LOG_ERR("Failed to enable device support");
284 return ret;
285 }
286 }
287
288 LOG_INF("USB DFU sample is initialized");
289
290 return 0;
291 }
292