1 /*
2  * Copyright (c) 2021-2022 Nordic Semiconductor ASA
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/sys/slist.h>
8 #include <zephyr/drivers/usb/udc.h>
9 #include <zephyr/usb/usbd.h>
10 
11 #include "usbd_device.h"
12 #include "usbd_config.h"
13 #include "usbd_class.h"
14 #include "usbd_class_api.h"
15 #include "usbd_endpoint.h"
16 
17 #include <zephyr/logging/log.h>
18 LOG_MODULE_REGISTER(usbd_init, CONFIG_USBD_LOG_LEVEL);
19 
20 /* TODO: Allow to disable automatic assignment of endpoint features */
21 
22 /* Assign endpoint address and update wMaxPacketSize */
assign_ep_addr(const struct device * dev,struct usb_ep_descriptor * const ed,uint32_t * const config_ep_bm,uint32_t * const class_ep_bm)23 static int assign_ep_addr(const struct device *dev,
24 			  struct usb_ep_descriptor *const ed,
25 			  uint32_t *const config_ep_bm,
26 			  uint32_t *const class_ep_bm)
27 {
28 	int ret = -ENODEV;
29 
30 	for (unsigned int idx = 1; idx < 16U; idx++) {
31 		uint16_t mps = sys_le16_to_cpu(ed->wMaxPacketSize);
32 		uint8_t ep;
33 
34 		if (USB_EP_DIR_IS_IN(ed->bEndpointAddress)) {
35 			ep = USB_EP_DIR_IN | idx;
36 		} else {
37 			ep = idx;
38 		}
39 
40 		if (usbd_ep_bm_is_set(config_ep_bm, ep) ||
41 		    usbd_ep_bm_is_set(class_ep_bm, ep)) {
42 			continue;
43 		}
44 
45 
46 		ret = udc_ep_try_config(dev, ep,
47 					ed->bmAttributes, &mps,
48 					ed->bInterval);
49 
50 		if (ret == 0) {
51 			LOG_DBG("ep 0x%02x -> 0x%02x", ed->bEndpointAddress, ep);
52 			ed->bEndpointAddress = ep;
53 			ed->wMaxPacketSize = sys_cpu_to_le16(mps);
54 			usbd_ep_bm_set(class_ep_bm, ed->bEndpointAddress);
55 			usbd_ep_bm_set(config_ep_bm, ed->bEndpointAddress);
56 
57 			return 0;
58 		}
59 	}
60 
61 	return ret;
62 }
63 
64 /* Unassign all endpoint of a class instance based on class_ep_bm */
unassign_eps(struct usbd_context * const uds_ctx,uint32_t * const config_ep_bm,uint32_t * const class_ep_bm)65 static int unassign_eps(struct usbd_context *const uds_ctx,
66 			uint32_t *const config_ep_bm,
67 			uint32_t *const class_ep_bm)
68 {
69 	for (unsigned int idx = 1; idx < 16U && *class_ep_bm; idx++) {
70 		uint8_t ep_in = USB_EP_DIR_IN | idx;
71 		uint8_t ep_out = idx;
72 
73 		if (usbd_ep_bm_is_set(class_ep_bm, ep_in)) {
74 			if (!usbd_ep_bm_is_set(config_ep_bm, ep_in)) {
75 				LOG_ERR("Endpoing 0x%02x not assigned", ep_in);
76 				return -EINVAL;
77 			}
78 
79 			usbd_ep_bm_clear(config_ep_bm, ep_in);
80 			usbd_ep_bm_clear(class_ep_bm, ep_in);
81 		}
82 
83 		if (usbd_ep_bm_is_set(class_ep_bm, ep_out)) {
84 			if (!usbd_ep_bm_is_set(config_ep_bm, ep_out)) {
85 				LOG_ERR("Endpoing 0x%02x not assigned", ep_out);
86 				return -EINVAL;
87 			}
88 
89 			usbd_ep_bm_clear(config_ep_bm, ep_out);
90 			usbd_ep_bm_clear(class_ep_bm, ep_out);
91 		}
92 	}
93 
94 	return 0;
95 }
96 
97 /*
98  * Configure all interfaces and endpoints of a class instance
99  *
100  * The total number of interfaces is stored in the configuration descriptor's
101  * value bNumInterfaces. This value is reset at the beginning of configuration
102  * initialization and is increased according to the number of interfaces.
103  * The respective bInterfaceNumber must be assigned to all interfaces
104  * of a class instance.
105  *
106  * Like bInterfaceNumber the endpoint addresses must be assigned
107  * for all registered instances and respective endpoint descriptors.
108  * We use config_ep_bm variable as map for assigned endpoint for an
109  * USB device configuration.
110  */
init_configuration_inst(struct usbd_context * const uds_ctx,const enum usbd_speed speed,struct usbd_class_node * const c_nd,uint32_t * const config_ep_bm,uint8_t * const nif)111 static int init_configuration_inst(struct usbd_context *const uds_ctx,
112 				   const enum usbd_speed speed,
113 				   struct usbd_class_node *const c_nd,
114 				   uint32_t *const config_ep_bm,
115 				   uint8_t *const nif)
116 {
117 	struct usb_desc_header **dhp;
118 	struct usb_association_descriptor *iad = NULL;
119 	struct usb_if_descriptor *ifd = NULL;
120 	struct usb_ep_descriptor *ed;
121 	uint32_t class_ep_bm = 0;
122 	uint8_t tmp_nif;
123 	int ret;
124 
125 	LOG_DBG("Initializing configuration for %u speed", speed);
126 	dhp = usbd_class_get_desc(c_nd->c_data, speed);
127 	if (dhp == NULL) {
128 		return 0;
129 	}
130 
131 	tmp_nif = *nif;
132 	c_nd->iface_bm = 0U;
133 	c_nd->ep_active = 0U;
134 
135 	while (*dhp != NULL && (*dhp)->bLength != 0) {
136 		if ((*dhp)->bDescriptorType == USB_DESC_INTERFACE_ASSOC) {
137 			iad = (struct usb_association_descriptor *)(*dhp);
138 
139 			/* IAD must be before interfaces it associates, so the
140 			 * first interface will be the next interface assigned.
141 			 */
142 			iad->bFirstInterface = tmp_nif;
143 		}
144 
145 		if ((*dhp)->bDescriptorType == USB_DESC_INTERFACE) {
146 			ifd = (struct usb_if_descriptor *)(*dhp);
147 
148 			c_nd->ep_active |= class_ep_bm;
149 
150 			if (ifd->bAlternateSetting == 0) {
151 				ifd->bInterfaceNumber = tmp_nif;
152 				c_nd->iface_bm |= BIT(tmp_nif);
153 				tmp_nif++;
154 			} else {
155 				ifd->bInterfaceNumber = tmp_nif - 1;
156 				/*
157 				 * Unassign endpoints from last alternate,
158 				 * to work properly it requires that the
159 				 * characteristics of endpoints in alternate
160 				 * interfaces are ascending.
161 				 */
162 				unassign_eps(uds_ctx, config_ep_bm, &class_ep_bm);
163 			}
164 
165 			class_ep_bm = 0;
166 			LOG_INF("interface %u alternate %u",
167 				ifd->bInterfaceNumber, ifd->bAlternateSetting);
168 		}
169 
170 		if ((*dhp)->bDescriptorType == USB_DESC_ENDPOINT) {
171 			ed = (struct usb_ep_descriptor *)(*dhp);
172 			ret = assign_ep_addr(uds_ctx->dev, ed,
173 					     config_ep_bm, &class_ep_bm);
174 			if (ret) {
175 				return ret;
176 			}
177 
178 			LOG_INF("\tep 0x%02x mps 0x%04x interface ep-bm 0x%08x",
179 				ed->bEndpointAddress,
180 				sys_le16_to_cpu(ed->wMaxPacketSize),
181 				class_ep_bm);
182 		}
183 
184 		dhp++;
185 	}
186 
187 	if (tmp_nif <= *nif) {
188 		return -EINVAL;
189 	}
190 
191 	*nif = tmp_nif;
192 	c_nd->ep_active |= class_ep_bm;
193 
194 	LOG_INF("Instance iface-bm 0x%08x ep-bm 0x%08x",
195 		c_nd->iface_bm, c_nd->ep_active);
196 
197 	return 0;
198 }
199 
200 /*
201  * Initialize a device configuration
202  *
203  * Iterate on a list of all classes in a configuration
204  */
init_configuration(struct usbd_context * const uds_ctx,const enum usbd_speed speed,struct usbd_config_node * const cfg_nd)205 static int init_configuration(struct usbd_context *const uds_ctx,
206 			      const enum usbd_speed speed,
207 			      struct usbd_config_node *const cfg_nd)
208 {
209 	struct usb_cfg_descriptor *cfg_desc = cfg_nd->desc;
210 	struct usbd_class_node *c_nd;
211 	uint32_t config_ep_bm = 0;
212 	size_t cfg_len = 0;
213 	uint8_t nif = 0;
214 	int ret;
215 
216 	SYS_SLIST_FOR_EACH_CONTAINER(&cfg_nd->class_list, c_nd, node) {
217 
218 		ret = init_configuration_inst(uds_ctx, speed, c_nd,
219 					      &config_ep_bm, &nif);
220 		if (ret != 0) {
221 			LOG_ERR("Failed to assign endpoint addresses");
222 			return ret;
223 		}
224 
225 		ret = usbd_class_init(c_nd->c_data);
226 		if (ret != 0) {
227 			LOG_ERR("Failed to initialize class instance");
228 			return ret;
229 		}
230 
231 		LOG_INF("Init class node %p, descriptor length %zu",
232 			c_nd->c_data, usbd_class_desc_len(c_nd->c_data, speed));
233 		cfg_len += usbd_class_desc_len(c_nd->c_data, speed);
234 	}
235 
236 	/* Update wTotalLength and bNumInterfaces of configuration descriptor */
237 	sys_put_le16(sizeof(struct usb_cfg_descriptor) + cfg_len,
238 		     (uint8_t *)&cfg_desc->wTotalLength);
239 	cfg_desc->bNumInterfaces = nif;
240 
241 	LOG_INF("bNumInterfaces %u wTotalLength %u",
242 		cfg_desc->bNumInterfaces,
243 		cfg_desc->wTotalLength);
244 
245 	/* Finally reset configuration's endpoint assignment */
246 	SYS_SLIST_FOR_EACH_CONTAINER(&cfg_nd->class_list, c_nd, node) {
247 		c_nd->ep_assigned = c_nd->ep_active;
248 		ret = unassign_eps(uds_ctx, &config_ep_bm, &c_nd->ep_active);
249 		if (ret != 0) {
250 			return ret;
251 		}
252 	}
253 
254 	return 0;
255 }
256 
usbd_init_update_fs_mps0(struct usbd_context * const uds_ctx)257 static void usbd_init_update_fs_mps0(struct usbd_context *const uds_ctx)
258 {
259 	struct udc_device_caps caps = udc_caps(uds_ctx->dev);
260 	struct usb_device_descriptor *desc = uds_ctx->fs_desc;
261 
262 	switch (caps.mps0) {
263 	case UDC_MPS0_8:
264 		desc->bMaxPacketSize0 = 8;
265 		break;
266 	case UDC_MPS0_16:
267 		desc->bMaxPacketSize0 = 16;
268 		break;
269 	case UDC_MPS0_32:
270 		desc->bMaxPacketSize0 = 32;
271 		break;
272 	case UDC_MPS0_64:
273 		desc->bMaxPacketSize0 = 64;
274 		break;
275 	}
276 }
277 
usbd_init_configurations(struct usbd_context * const uds_ctx)278 int usbd_init_configurations(struct usbd_context *const uds_ctx)
279 {
280 	struct usbd_config_node *cfg_nd;
281 
282 	usbd_init_update_fs_mps0(uds_ctx);
283 
284 	SYS_SLIST_FOR_EACH_CONTAINER(&uds_ctx->hs_configs, cfg_nd, node) {
285 		int ret;
286 
287 		ret = init_configuration(uds_ctx, USBD_SPEED_HS, cfg_nd);
288 		if (ret) {
289 			LOG_ERR("Failed to init HS configuration %u",
290 				usbd_config_get_value(cfg_nd));
291 			return ret;
292 		}
293 
294 		LOG_INF("HS bNumConfigurations %u",
295 			usbd_get_num_configs(uds_ctx, USBD_SPEED_HS));
296 	}
297 
298 	SYS_SLIST_FOR_EACH_CONTAINER(&uds_ctx->fs_configs, cfg_nd, node) {
299 		int ret;
300 
301 		ret = init_configuration(uds_ctx, USBD_SPEED_FS, cfg_nd);
302 		if (ret) {
303 			LOG_ERR("Failed to init FS configuration %u",
304 				usbd_config_get_value(cfg_nd));
305 			return ret;
306 		}
307 
308 		LOG_INF("FS bNumConfigurations %u",
309 			usbd_get_num_configs(uds_ctx, USBD_SPEED_FS));
310 	}
311 
312 	return 0;
313 }
314