1 /*
2  * Copyright (c) 2023 Nordic Semiconductor ASA
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <stdint.h>
8 
9 #include <zephyr/device.h>
10 #include <zephyr/usb/usbd.h>
11 #include <zephyr/usb/bos.h>
12 
13 #include <zephyr/logging/log.h>
14 LOG_MODULE_REGISTER(usbd_sample_config);
15 
16 #define ZEPHYR_PROJECT_USB_VID		0x2fe3
17 
18 /* By default, do not register the USB DFU class DFU mode instance. */
19 static const char *const blocklist[] = {
20 	"dfu_dfu",
21 	NULL,
22 };
23 
24 /* doc device instantiation start */
25 /*
26  * Instantiate a context named sample_usbd using the default USB device
27  * controller, the Zephyr project vendor ID, and the sample product ID.
28  * Zephyr project vendor ID must not be used outside of Zephyr samples.
29  */
30 USBD_DEVICE_DEFINE(sample_usbd,
31 		   DEVICE_DT_GET(DT_NODELABEL(zephyr_udc0)),
32 		   ZEPHYR_PROJECT_USB_VID, CONFIG_SAMPLE_USBD_PID);
33 /* doc device instantiation end */
34 
35 /* doc string instantiation start */
36 USBD_DESC_LANG_DEFINE(sample_lang);
37 USBD_DESC_MANUFACTURER_DEFINE(sample_mfr, CONFIG_SAMPLE_USBD_MANUFACTURER);
38 USBD_DESC_PRODUCT_DEFINE(sample_product, CONFIG_SAMPLE_USBD_PRODUCT);
39 IF_ENABLED(CONFIG_HWINFO, (USBD_DESC_SERIAL_NUMBER_DEFINE(sample_sn)));
40 
41 /* doc string instantiation end */
42 
43 USBD_DESC_CONFIG_DEFINE(fs_cfg_desc, "FS Configuration");
44 USBD_DESC_CONFIG_DEFINE(hs_cfg_desc, "HS Configuration");
45 
46 /* doc configuration instantiation start */
47 static const uint8_t attributes = (IS_ENABLED(CONFIG_SAMPLE_USBD_SELF_POWERED) ?
48 				   USB_SCD_SELF_POWERED : 0) |
49 				  (IS_ENABLED(CONFIG_SAMPLE_USBD_REMOTE_WAKEUP) ?
50 				   USB_SCD_REMOTE_WAKEUP : 0);
51 
52 /* Full speed configuration */
53 USBD_CONFIGURATION_DEFINE(sample_fs_config,
54 			  attributes,
55 			  CONFIG_SAMPLE_USBD_MAX_POWER, &fs_cfg_desc);
56 
57 /* High speed configuration */
58 USBD_CONFIGURATION_DEFINE(sample_hs_config,
59 			  attributes,
60 			  CONFIG_SAMPLE_USBD_MAX_POWER, &hs_cfg_desc);
61 /* doc configuration instantiation end */
62 
63 #if CONFIG_SAMPLE_USBD_20_EXTENSION_DESC
64 /*
65  * This does not yet provide valuable information, but rather serves as an
66  * example, and will be improved in the future.
67  */
68 static const struct usb_bos_capability_lpm bos_cap_lpm = {
69 	.bLength = sizeof(struct usb_bos_capability_lpm),
70 	.bDescriptorType = USB_DESC_DEVICE_CAPABILITY,
71 	.bDevCapabilityType = USB_BOS_CAPABILITY_EXTENSION,
72 	.bmAttributes = 0UL,
73 };
74 
75 USBD_DESC_BOS_DEFINE(sample_usbext, sizeof(bos_cap_lpm), &bos_cap_lpm);
76 #endif
77 
sample_fix_code_triple(struct usbd_context * uds_ctx,const enum usbd_speed speed)78 static void sample_fix_code_triple(struct usbd_context *uds_ctx,
79 				   const enum usbd_speed speed)
80 {
81 	/* Always use class code information from Interface Descriptors */
82 	if (IS_ENABLED(CONFIG_USBD_CDC_ACM_CLASS) ||
83 	    IS_ENABLED(CONFIG_USBD_CDC_ECM_CLASS) ||
84 	    IS_ENABLED(CONFIG_USBD_CDC_NCM_CLASS) ||
85 	    IS_ENABLED(CONFIG_USBD_MIDI2_CLASS) ||
86 	    IS_ENABLED(CONFIG_USBD_AUDIO2_CLASS) ||
87 	    IS_ENABLED(CONFIG_USBD_VIDEO_CLASS)) {
88 		/*
89 		 * Class with multiple interfaces have an Interface
90 		 * Association Descriptor available, use an appropriate triple
91 		 * to indicate it.
92 		 */
93 		usbd_device_set_code_triple(uds_ctx, speed,
94 					    USB_BCC_MISCELLANEOUS, 0x02, 0x01);
95 	} else {
96 		usbd_device_set_code_triple(uds_ctx, speed, 0, 0, 0);
97 	}
98 }
99 
sample_usbd_setup_device(usbd_msg_cb_t msg_cb)100 struct usbd_context *sample_usbd_setup_device(usbd_msg_cb_t msg_cb)
101 {
102 	int err;
103 
104 	/* doc add string descriptor start */
105 	err = usbd_add_descriptor(&sample_usbd, &sample_lang);
106 	if (err) {
107 		LOG_ERR("Failed to initialize language descriptor (%d)", err);
108 		return NULL;
109 	}
110 
111 	err = usbd_add_descriptor(&sample_usbd, &sample_mfr);
112 	if (err) {
113 		LOG_ERR("Failed to initialize manufacturer descriptor (%d)", err);
114 		return NULL;
115 	}
116 
117 	err = usbd_add_descriptor(&sample_usbd, &sample_product);
118 	if (err) {
119 		LOG_ERR("Failed to initialize product descriptor (%d)", err);
120 		return NULL;
121 	}
122 
123 	IF_ENABLED(CONFIG_HWINFO, (
124 		err = usbd_add_descriptor(&sample_usbd, &sample_sn);
125 	))
126 	if (err) {
127 		LOG_ERR("Failed to initialize SN descriptor (%d)", err);
128 		return NULL;
129 	}
130 	/* doc add string descriptor end */
131 
132 	if (USBD_SUPPORTS_HIGH_SPEED &&
133 	    usbd_caps_speed(&sample_usbd) == USBD_SPEED_HS) {
134 		err = usbd_add_configuration(&sample_usbd, USBD_SPEED_HS,
135 					     &sample_hs_config);
136 		if (err) {
137 			LOG_ERR("Failed to add High-Speed configuration");
138 			return NULL;
139 		}
140 
141 		err = usbd_register_all_classes(&sample_usbd, USBD_SPEED_HS, 1,
142 						blocklist);
143 		if (err) {
144 			LOG_ERR("Failed to add register classes");
145 			return NULL;
146 		}
147 
148 		sample_fix_code_triple(&sample_usbd, USBD_SPEED_HS);
149 	}
150 
151 	/* doc configuration register start */
152 	err = usbd_add_configuration(&sample_usbd, USBD_SPEED_FS,
153 				     &sample_fs_config);
154 	if (err) {
155 		LOG_ERR("Failed to add Full-Speed configuration");
156 		return NULL;
157 	}
158 	/* doc configuration register end */
159 
160 	/* doc functions register start */
161 	err = usbd_register_all_classes(&sample_usbd, USBD_SPEED_FS, 1, blocklist);
162 	if (err) {
163 		LOG_ERR("Failed to add register classes");
164 		return NULL;
165 	}
166 	/* doc functions register end */
167 
168 	sample_fix_code_triple(&sample_usbd, USBD_SPEED_FS);
169 	usbd_self_powered(&sample_usbd, attributes & USB_SCD_SELF_POWERED);
170 
171 	if (msg_cb != NULL) {
172 		/* doc device init-and-msg start */
173 		err = usbd_msg_register_cb(&sample_usbd, msg_cb);
174 		if (err) {
175 			LOG_ERR("Failed to register message callback");
176 			return NULL;
177 		}
178 		/* doc device init-and-msg end */
179 	}
180 
181 #if CONFIG_SAMPLE_USBD_20_EXTENSION_DESC
182 	(void)usbd_device_set_bcd_usb(&sample_usbd, USBD_SPEED_FS, 0x0201);
183 	(void)usbd_device_set_bcd_usb(&sample_usbd, USBD_SPEED_HS, 0x0201);
184 
185 	err = usbd_add_descriptor(&sample_usbd, &sample_usbext);
186 	if (err) {
187 		LOG_ERR("Failed to add USB 2.0 Extension Descriptor");
188 		return NULL;
189 	}
190 #endif
191 
192 	return &sample_usbd;
193 }
194 
sample_usbd_init_device(usbd_msg_cb_t msg_cb)195 struct usbd_context *sample_usbd_init_device(usbd_msg_cb_t msg_cb)
196 {
197 	int err;
198 
199 	if (sample_usbd_setup_device(msg_cb) == NULL) {
200 		return NULL;
201 	}
202 
203 	/* doc device init start */
204 	err = usbd_init(&sample_usbd);
205 	if (err) {
206 		LOG_ERR("Failed to initialize device support");
207 		return NULL;
208 	}
209 	/* doc device init end */
210 
211 	return &sample_usbd;
212 }
213