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 USBD_DESC_SERIAL_NUMBER_DEFINE(sample_sn);
40 /* doc string instantiation end */
41 
42 USBD_DESC_CONFIG_DEFINE(fs_cfg_desc, "FS Configuration");
43 USBD_DESC_CONFIG_DEFINE(hs_cfg_desc, "HS Configuration");
44 
45 /* doc configuration instantiation start */
46 static const uint8_t attributes = (IS_ENABLED(CONFIG_SAMPLE_USBD_SELF_POWERED) ?
47 				   USB_SCD_SELF_POWERED : 0) |
48 				  (IS_ENABLED(CONFIG_SAMPLE_USBD_REMOTE_WAKEUP) ?
49 				   USB_SCD_REMOTE_WAKEUP : 0);
50 
51 /* Full speed configuration */
52 USBD_CONFIGURATION_DEFINE(sample_fs_config,
53 			  attributes,
54 			  CONFIG_SAMPLE_USBD_MAX_POWER, &fs_cfg_desc);
55 
56 /* High speed configuration */
57 USBD_CONFIGURATION_DEFINE(sample_hs_config,
58 			  attributes,
59 			  CONFIG_SAMPLE_USBD_MAX_POWER, &hs_cfg_desc);
60 /* doc configuration instantiation end */
61 
62 /*
63  * This does not yet provide valuable information, but rather serves as an
64  * example, and will be improved in the future.
65  */
66 static const struct usb_bos_capability_lpm bos_cap_lpm = {
67 	.bLength = sizeof(struct usb_bos_capability_lpm),
68 	.bDescriptorType = USB_DESC_DEVICE_CAPABILITY,
69 	.bDevCapabilityType = USB_BOS_CAPABILITY_EXTENSION,
70 	.bmAttributes = 0UL,
71 };
72 
73 USBD_DESC_BOS_DEFINE(sample_usbext, sizeof(bos_cap_lpm), &bos_cap_lpm);
74 
sample_fix_code_triple(struct usbd_context * uds_ctx,const enum usbd_speed speed)75 static void sample_fix_code_triple(struct usbd_context *uds_ctx,
76 				   const enum usbd_speed speed)
77 {
78 	/* Always use class code information from Interface Descriptors */
79 	if (IS_ENABLED(CONFIG_USBD_CDC_ACM_CLASS) ||
80 	    IS_ENABLED(CONFIG_USBD_CDC_ECM_CLASS) ||
81 	    IS_ENABLED(CONFIG_USBD_CDC_NCM_CLASS) ||
82 	    IS_ENABLED(CONFIG_USBD_MIDI2_CLASS) ||
83 	    IS_ENABLED(CONFIG_USBD_AUDIO2_CLASS)) {
84 		/*
85 		 * Class with multiple interfaces have an Interface
86 		 * Association Descriptor available, use an appropriate triple
87 		 * to indicate it.
88 		 */
89 		usbd_device_set_code_triple(uds_ctx, speed,
90 					    USB_BCC_MISCELLANEOUS, 0x02, 0x01);
91 	} else {
92 		usbd_device_set_code_triple(uds_ctx, speed, 0, 0, 0);
93 	}
94 }
95 
sample_usbd_setup_device(usbd_msg_cb_t msg_cb)96 struct usbd_context *sample_usbd_setup_device(usbd_msg_cb_t msg_cb)
97 {
98 	int err;
99 
100 	/* doc add string descriptor start */
101 	err = usbd_add_descriptor(&sample_usbd, &sample_lang);
102 	if (err) {
103 		LOG_ERR("Failed to initialize language descriptor (%d)", err);
104 		return NULL;
105 	}
106 
107 	err = usbd_add_descriptor(&sample_usbd, &sample_mfr);
108 	if (err) {
109 		LOG_ERR("Failed to initialize manufacturer descriptor (%d)", err);
110 		return NULL;
111 	}
112 
113 	err = usbd_add_descriptor(&sample_usbd, &sample_product);
114 	if (err) {
115 		LOG_ERR("Failed to initialize product descriptor (%d)", err);
116 		return NULL;
117 	}
118 
119 	err = usbd_add_descriptor(&sample_usbd, &sample_sn);
120 	if (err) {
121 		LOG_ERR("Failed to initialize SN descriptor (%d)", err);
122 		return NULL;
123 	}
124 	/* doc add string descriptor end */
125 
126 	if (usbd_caps_speed(&sample_usbd) == USBD_SPEED_HS) {
127 		err = usbd_add_configuration(&sample_usbd, USBD_SPEED_HS,
128 					     &sample_hs_config);
129 		if (err) {
130 			LOG_ERR("Failed to add High-Speed configuration");
131 			return NULL;
132 		}
133 
134 		err = usbd_register_all_classes(&sample_usbd, USBD_SPEED_HS, 1,
135 						blocklist);
136 		if (err) {
137 			LOG_ERR("Failed to add register classes");
138 			return NULL;
139 		}
140 
141 		sample_fix_code_triple(&sample_usbd, USBD_SPEED_HS);
142 	}
143 
144 	/* doc configuration register start */
145 	err = usbd_add_configuration(&sample_usbd, USBD_SPEED_FS,
146 				     &sample_fs_config);
147 	if (err) {
148 		LOG_ERR("Failed to add Full-Speed configuration");
149 		return NULL;
150 	}
151 	/* doc configuration register end */
152 
153 	/* doc functions register start */
154 	err = usbd_register_all_classes(&sample_usbd, USBD_SPEED_FS, 1, blocklist);
155 	if (err) {
156 		LOG_ERR("Failed to add register classes");
157 		return NULL;
158 	}
159 	/* doc functions register end */
160 
161 	sample_fix_code_triple(&sample_usbd, USBD_SPEED_FS);
162 	usbd_self_powered(&sample_usbd, attributes & USB_SCD_SELF_POWERED);
163 
164 	if (msg_cb != NULL) {
165 		/* doc device init-and-msg start */
166 		err = usbd_msg_register_cb(&sample_usbd, msg_cb);
167 		if (err) {
168 			LOG_ERR("Failed to register message callback");
169 			return NULL;
170 		}
171 		/* doc device init-and-msg end */
172 	}
173 
174 	if (IS_ENABLED(CONFIG_SAMPLE_USBD_20_EXTENSION_DESC)) {
175 		(void)usbd_device_set_bcd_usb(&sample_usbd, USBD_SPEED_FS, 0x0201);
176 		(void)usbd_device_set_bcd_usb(&sample_usbd, USBD_SPEED_HS, 0x0201);
177 
178 		err = usbd_add_descriptor(&sample_usbd, &sample_usbext);
179 		if (err) {
180 			LOG_ERR("Failed to add USB 2.0 Extension Descriptor");
181 			return NULL;
182 		}
183 	}
184 
185 	return &sample_usbd;
186 }
187 
sample_usbd_init_device(usbd_msg_cb_t msg_cb)188 struct usbd_context *sample_usbd_init_device(usbd_msg_cb_t msg_cb)
189 {
190 	int err;
191 
192 	if (sample_usbd_setup_device(msg_cb) == NULL) {
193 		return NULL;
194 	}
195 
196 	/* doc device init start */
197 	err = usbd_init(&sample_usbd);
198 	if (err) {
199 		LOG_ERR("Failed to initialize device support");
200 		return NULL;
201 	}
202 	/* doc device init end */
203 
204 	return &sample_usbd;
205 }
206