Skip to content

Commit 5e47305

Browse files
committed
usb: device_next: implement USB DFU class for the new device support
This new implementation is written from scratch and is not tied to the image manager and MCUboot. It allows the user to define their own backend and use a simple macro to instantiate an image. On the USB side this is represented by an interface. The number of possible images is configurable using the Kconfig option, and is a fairly inexpensive approach since it only changes the size of the pointer array. The number of images is only limited by the number of possible interfaces in a configuration. The class implementation does not support multiple instances, as there is no real use for it. However, it does provide two class instances, one for runtime mode and one for DFU mode. The switch from runtime to DFU mode can only be performed by the user application, i.e. the application receives a notification when the host wants to switch to DFU mode, and then the application can disable the runtime configuration and enable the DFU configuration. This implementation does not support switching to the DFU mode by bus reset issued by the host. Signed-off-by: Johann Fischer <johann.fischer@nordicsemi.no>
1 parent 5fd4e56 commit 5e47305

File tree

9 files changed

+1047
-0
lines changed

9 files changed

+1047
-0
lines changed

doc/connectivity/usb/device_next/api/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,4 @@ New USB device support APIs
1212
uac2_device.rst
1313
usbd_msc_device.rst
1414
usb_midi.rst
15+
usbd_dfu.rst
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
.. _usbd_dfu:
2+
3+
USB DFU device API
4+
##################
5+
6+
USB DFU device specific API defined in :zephyr_file:`include/zephyr/usb/class/usbd_dfu.h`.
7+
8+
API Reference
9+
*************
10+
11+
.. doxygengroup:: usbd_dfu

include/zephyr/usb/class/usbd_dfu.h

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
/*
2+
* Copyright (c) 2024 Nordic Semiconductor ASA
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
/**
8+
* @file
9+
* @brief USB Device Firmware Upgrade (DFU) public header
10+
*
11+
* Header exposes API for registering DFU images.
12+
*/
13+
14+
#ifndef ZEPHYR_INCLUDE_USB_CLASS_USBD_DFU_H
15+
#define ZEPHYR_INCLUDE_USB_CLASS_USBD_DFU_H
16+
17+
#include <stdint.h>
18+
19+
/* DFU Class Subclass */
20+
#define USB_DFU_SUBCLASS 0x01
21+
22+
/* DFU Class runtime Protocol */
23+
#define USB_DFU_PROTOCOL_RUNTIME 0x01
24+
25+
/* DFU Class DFU mode Protocol */
26+
#define USB_DFU_PROTOCOL_DFU 0x02
27+
28+
/* DFU Class Specific Requests */
29+
#define USB_DFU_REQ_DETACH 0x00
30+
#define USB_DFU_REQ_DNLOAD 0x01
31+
#define USB_DFU_REQ_UPLOAD 0x02
32+
#define USB_DFU_REQ_GETSTATUS 0x03
33+
#define USB_DFU_REQ_CLRSTATUS 0x04
34+
#define USB_DFU_REQ_GETSTATE 0x05
35+
#define USB_DFU_REQ_ABORT 0x06
36+
37+
/* Run-Time DFU Functional Descriptor */
38+
struct usb_dfu_descriptor {
39+
uint8_t bLength;
40+
uint8_t bDescriptorType;
41+
uint8_t bmAttributes;
42+
uint16_t wDetachTimeOut;
43+
uint16_t wTransferSize;
44+
uint16_t bcdDFUVersion;
45+
} __packed;
46+
47+
/* DFU Functional Descriptor Type */
48+
#define USB_DESC_DFU_FUNCTIONAL 0x21
49+
50+
/* DFU attributes DFU Functional Descriptor */
51+
#define USB_DFU_ATTR_WILL_DETACH BIT(3)
52+
#define USB_DFU_ATTR_MANIFESTATION_TOLERANT BIT(2)
53+
#define USB_DFU_ATTR_CAN_UPLOAD BIT(1)
54+
#define USB_DFU_ATTR_CAN_DNLOAD BIT(0)
55+
56+
/* DFU Specification release */
57+
#define USB_DFU_VERSION 0x0110
58+
59+
/* DFU device status */
60+
enum usb_dfu_status {
61+
ERR_OK = 0x00,
62+
ERR_TARGET = 0x01,
63+
ERR_FILE = 0x02,
64+
ERR_WRITE = 0x03,
65+
ERR_ERASE = 0x04,
66+
ERR_CHECK_ERASED = 0x05,
67+
ERR_PROG = 0x06,
68+
ERR_VERIFY = 0x07,
69+
ERR_ADDRESS = 0x08,
70+
ERR_NOTDONE = 0x09,
71+
ERR_FIRMWARE = 0x0A,
72+
ERR_VENDOR = 0x0B,
73+
ERR_USBR = 0x0C,
74+
ERR_POR = 0x0D,
75+
ERR_UNKNOWN = 0x0E,
76+
ERR_STALLEDPKT = 0x0F,
77+
};
78+
79+
/* DFU device states */
80+
enum usb_dfu_state {
81+
APP_IDLE = 0,
82+
APP_DETACH = 1,
83+
DFU_IDLE = 2,
84+
DFU_DNLOAD_SYNC = 3,
85+
DFU_DNBUSY = 4,
86+
DFU_DNLOAD_IDLE = 5,
87+
DFU_MANIFEST_SYNC = 6,
88+
DFU_MANIFEST = 7,
89+
DFU_MANIFEST_WAIT_RST = 8,
90+
DFU_UPLOAD_IDLE = 9,
91+
DFU_ERROR = 10,
92+
DFU_STATE_MAX = 11,
93+
};
94+
95+
struct usbd_dfu_image {
96+
const char *name;
97+
struct usb_if_descriptor *const if_desc;
98+
void *const priv;
99+
struct usbd_desc_node *const sd_nd;
100+
bool (*next_cb)(void *const priv,
101+
const enum usb_dfu_state state, const enum usb_dfu_state next);
102+
int (*read_cb)(void *const priv,
103+
const uint32_t block, const uint16_t size,
104+
uint8_t buf[static CONFIG_USBD_DFU_TRANSFER_SIZE]);
105+
int (*write_cb)(void *const priv,
106+
const uint32_t block, const uint16_t size,
107+
const uint8_t buf[static CONFIG_USBD_DFU_TRANSFER_SIZE]);
108+
};
109+
110+
/**
111+
* @brief USB DFU device update API
112+
* @defgroup usbd_dfu USB DFU device update API
113+
* @ingroup usb
114+
* @{
115+
*/
116+
117+
/**
118+
* @brief Define USB DFU image
119+
*
120+
* Use this macro to create USB DFU image
121+
*
122+
* The callbacks must be in form:
123+
*
124+
* @code{.c}
125+
* static int read(void *const priv, const uint32_t block, const uint16_t size,
126+
* uint8_t buf[static CONFIG_USBD_DFU_TRANSFER_SIZE])
127+
* {
128+
* int len;
129+
*
130+
* return len;
131+
* }
132+
*
133+
* static int write(void *const priv, const uint32_t block, const uint16_t size,
134+
* const uint8_t buf[static CONFIG_USBD_DFU_TRANSFER_SIZE])
135+
* {
136+
* return 0;
137+
* }
138+
*
139+
* static bool next(void *const priv,
140+
* const enum usb_dfu_state state, const enum usb_dfu_state next)
141+
* {
142+
* return true;
143+
* }
144+
* @endcode
145+
*
146+
* @param id Identifier by which the linker sorts registered images
147+
* @param iname Image name as used in interface descriptor
148+
* @param iread Image read callback
149+
* @param iwrite Image write callback
150+
* @param inext Notify/confirm next state
151+
*/
152+
#define USBD_DFU_DEFINE_IMG(id, iname, ipriv, iread, iwrite, inext) \
153+
static __noinit struct usb_if_descriptor usbd_dfu_iface_##id; \
154+
\
155+
USBD_DESC_STRING_DEFINE(usbd_dfu_str_##id, iname, USBD_DUT_STRING_INTERFACE); \
156+
\
157+
static const STRUCT_SECTION_ITERABLE(usbd_dfu_image, usbd_dfu_image_##id) = { \
158+
.name = iname, \
159+
.if_desc = &usbd_dfu_iface_##id, \
160+
.priv = ipriv, \
161+
.sd_nd = &usbd_dfu_str_##id, \
162+
.read_cb = iread, \
163+
.write_cb = iwrite, \
164+
.next_cb = inext, \
165+
}
166+
167+
/**
168+
* @}
169+
*/
170+
#endif /* ZEPHYR_INCLUDE_USB_CLASS_USBD_DFU_H */

include/zephyr/usb/usbd_msg.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,10 @@ enum usbd_msg_type {
5252
USBD_MSG_CDC_ACM_LINE_CODING,
5353
/** CDC ACM Line State update */
5454
USBD_MSG_CDC_ACM_CONTROL_LINE_STATE,
55+
/** USB DFU class detach request */
56+
USBD_MSG_DFU_APP_DETACH,
57+
/** USB DFU class download completed */
58+
USBD_MSG_DFU_DOWNLOAD_COMPLETED,
5559
/** Maximum number of message types */
5660
USBD_MSG_MAX_NUMBER,
5761
};
@@ -70,6 +74,8 @@ static const char *const usbd_msg_type_list[] = {
7074
"Stack error",
7175
"CDC ACM line coding",
7276
"CDC ACM control line state",
77+
"DFU detach request",
78+
"DFU download completed",
7379
};
7480

7581
BUILD_ASSERT(ARRAY_SIZE(usbd_msg_type_list) == USBD_MSG_MAX_NUMBER,

subsys/usb/device_next/CMakeLists.txt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,4 +82,14 @@ zephyr_library_sources_ifdef(
8282
class/usbd_hid_api.c
8383
)
8484

85+
zephyr_library_sources_ifdef(
86+
CONFIG_USBD_DFU
87+
class/usbd_dfu.c
88+
)
89+
90+
zephyr_linker_sources_ifdef(
91+
CONFIG_USBD_DFU
92+
SECTIONS class/usbd_dfu.ld
93+
)
94+
8595
zephyr_linker_sources(DATA_SECTIONS usbd_data.ld)

subsys/usb/device_next/class/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,4 @@ rsource "Kconfig.msc"
1111
rsource "Kconfig.uac2"
1212
rsource "Kconfig.hid"
1313
rsource "Kconfig.midi2"
14+
rsource "Kconfig.dfu"
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# Copyright (c) 2024 Nordic Semiconductor ASA
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
menuconfig USBD_DFU
5+
bool "USB DFU Class support"
6+
help
7+
USB DFU Class support
8+
9+
if USBD_DFU
10+
11+
config USBD_DFU_NUMOF_IMAGES
12+
int "Number of possible DFU images"
13+
range 1 256
14+
default 4
15+
help
16+
Number of possible DFU images.
17+
18+
config USBD_DFU_ENABLE_UPLOAD
19+
bool "Allow images to be uploaded to the host"
20+
default y
21+
help
22+
This option sets the bitCanUpload flag in the DFU attributes and
23+
allows images to be uploaded to the host.
24+
25+
config USBD_DFU_MANIFESTATION_TOLERANT
26+
bool "Device is manifestation tolerant"
27+
default y
28+
help
29+
This option sets the bitManifestationTolerant flag in the DFU
30+
attributes and means that the device can communicate over USB after the
31+
manifestation phase.
32+
33+
config USBD_DFU_TRANSFER_SIZE
34+
int "Maximum number of bytes the device can accept per transfer"
35+
default 512
36+
range 64 1024
37+
help
38+
This option sets the wTransferSize in the DFU functional descriptor.
39+
40+
config USBD_DFU_POLLTIMEOUT
41+
int "bwPollTimeout value (in ms)"
42+
default 10
43+
range 0 1000
44+
help
45+
This option sets the bwPollTimeout field in DFU_GETSTATUS response.
46+
47+
module = USBD_DFU
48+
module-str = usbd dfu
49+
source "subsys/logging/Kconfig.template.log_config"
50+
51+
endif

0 commit comments

Comments
 (0)