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