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