1 /*
2  * Copyright (c) 2017 PHYTEC Messtechnik GmbH
3  * Copyright (c) 2017, 2018 Intel Corporation
4  * Copyright (c) 2022 Nordic Semiconductor ASA
5  *
6  * SPDX-License-Identifier: Apache-2.0
7  */
8 
9 #include <string.h>
10 #include <zephyr/sys/byteorder.h>
11 #include <zephyr/usb/usbd.h>
12 
13 #include "usbd_desc.h"
14 #include "usbd_device.h"
15 
16 #include <zephyr/logging/log.h>
17 LOG_MODULE_REGISTER(usbd_desc, CONFIG_USBD_LOG_LEVEL);
18 
desc_type_equal(const struct usbd_desc_node * const a,const struct usbd_desc_node * const b)19 static inline bool desc_type_equal(const struct usbd_desc_node *const a,
20 				   const struct usbd_desc_node *const b)
21 {
22 	return a->bDescriptorType == b->bDescriptorType;
23 }
24 
25 /*
26  * Add descriptor node to the descriptor list in ascending order by index
27  * and sorted by bDescriptorType. For the string descriptors, the function
28  * does not care about index zero for the language string descriptor,
29  * so if it is not added first, the device will be non-compliant.
30  */
desc_add_and_update_idx(struct usbd_context * const uds_ctx,struct usbd_desc_node * const new_nd)31 static int desc_add_and_update_idx(struct usbd_context *const uds_ctx,
32 				   struct usbd_desc_node *const new_nd)
33 {
34 	struct usbd_desc_node *tmp_nd;
35 
36 	SYS_DLIST_FOR_EACH_CONTAINER(&uds_ctx->descriptors, tmp_nd, node) {
37 		struct usbd_desc_node *next_nd;
38 
39 		if (!desc_type_equal(tmp_nd, new_nd)) {
40 			continue;
41 		}
42 
43 		next_nd = SYS_DLIST_PEEK_NEXT_CONTAINER(&uds_ctx->descriptors,
44 							tmp_nd,
45 							node);
46 
47 		if (next_nd == NULL) {
48 			/* Last node of the same bDescriptorType or tail */
49 			new_nd->str.idx = tmp_nd->str.idx + 1;
50 			sys_dlist_append(&uds_ctx->descriptors, &new_nd->node);
51 			LOG_DBG("Add %u behind %u", new_nd->str.idx, tmp_nd->str.idx);
52 
53 			return 0;
54 		}
55 
56 		if (!desc_type_equal(next_nd, new_nd)) {
57 			/* Last node of the same bDescriptorType */
58 			new_nd->str.idx = tmp_nd->str.idx + 1;
59 			sys_dlist_insert(&next_nd->node, &new_nd->node);
60 			LOG_DBG("Add %u before %u", new_nd->str.idx, next_nd->str.idx);
61 
62 			return 0;
63 		}
64 
65 		if (tmp_nd->str.idx != (next_nd->str.idx - 1)) {
66 			/* Add between nodes of the same bDescriptorType */
67 			new_nd->str.idx = tmp_nd->str.idx + 1;
68 			sys_dlist_insert(&next_nd->node, &new_nd->node);
69 			LOG_DBG("Add %u between %u and %u",
70 				tmp_nd->str.idx, next_nd->str.idx, new_nd->str.idx);
71 			return 0;
72 		}
73 	}
74 
75 	/* If there are none of same bDescriptorType, node idx is set to 0. */
76 	new_nd->str.idx = 0;
77 	sys_dlist_append(&uds_ctx->descriptors, &new_nd->node);
78 	LOG_DBG("Added first descriptor node (usage type %u)", new_nd->str.utype);
79 
80 	return 0;
81 }
82 
usbd_get_descriptor(struct usbd_context * const uds_ctx,const uint8_t type,const uint8_t idx)83 struct usbd_desc_node *usbd_get_descriptor(struct usbd_context *const uds_ctx,
84 					   const uint8_t type, const uint8_t idx)
85 {
86 	struct usbd_desc_node *desc_nd;
87 
88 	SYS_DLIST_FOR_EACH_CONTAINER(&uds_ctx->descriptors, desc_nd, node) {
89 		if (desc_nd->bDescriptorType == type) {
90 			if (desc_nd->bDescriptorType == USB_DESC_STRING) {
91 				if (desc_nd->str.idx == idx) {
92 					return desc_nd;
93 				}
94 			}
95 
96 			if (IS_ENABLED(CONFIG_USBD_BOS_SUPPORT) &&
97 			    desc_nd->bDescriptorType == USB_DESC_BOS) {
98 				return desc_nd;
99 			}
100 		}
101 	}
102 
103 	return NULL;
104 }
105 
usbd_desc_remove_all(struct usbd_context * const uds_ctx)106 int usbd_desc_remove_all(struct usbd_context *const uds_ctx)
107 {
108 	struct usbd_desc_node *tmp;
109 	sys_dnode_t *node;
110 
111 	while ((node = sys_dlist_get(&uds_ctx->descriptors))) {
112 		tmp = CONTAINER_OF(node, struct usbd_desc_node, node);
113 		LOG_DBG("Remove descriptor node %p type %u",
114 			(void *)tmp, tmp->str.utype);
115 	}
116 
117 	return 0;
118 }
119 
usbd_add_descriptor(struct usbd_context * const uds_ctx,struct usbd_desc_node * const desc_nd)120 int usbd_add_descriptor(struct usbd_context *const uds_ctx,
121 			struct usbd_desc_node *const desc_nd)
122 {
123 	struct usb_device_descriptor *hs_desc, *fs_desc;
124 	int ret = 0;
125 
126 	usbd_device_lock(uds_ctx);
127 
128 	hs_desc = uds_ctx->hs_desc;
129 	if (USBD_SUPPORTS_HIGH_SPEED && hs_desc == NULL) {
130 		ret = -EPERM;
131 		goto add_descriptor_error;
132 	}
133 
134 	fs_desc = uds_ctx->fs_desc;
135 	if (!fs_desc || usbd_is_initialized(uds_ctx)) {
136 		ret = -EPERM;
137 		goto add_descriptor_error;
138 	}
139 
140 	/* Check if descriptor list is initialized */
141 	if (!sys_dnode_is_linked(&uds_ctx->descriptors)) {
142 		LOG_DBG("Initialize descriptors list");
143 		sys_dlist_init(&uds_ctx->descriptors);
144 	}
145 
146 	if (sys_dnode_is_linked(&desc_nd->node)) {
147 		ret = -EALREADY;
148 		goto add_descriptor_error;
149 	}
150 
151 	if (IS_ENABLED(CONFIG_USBD_BOS_SUPPORT) &&
152 	    desc_nd->bDescriptorType == USB_DESC_BOS) {
153 		if (IS_ENABLED(CONFIG_USBD_VREQ_SUPPORT) &&
154 		    desc_nd->bos.utype == USBD_DUT_BOS_VREQ) {
155 			ret =  usbd_device_register_vreq(uds_ctx, desc_nd->bos.vreq_nd);
156 			if (ret) {
157 				goto add_descriptor_error;
158 			}
159 		}
160 
161 		sys_dlist_append(&uds_ctx->descriptors, &desc_nd->node);
162 	}
163 
164 	if (desc_nd->bDescriptorType == USB_DESC_STRING) {
165 		ret = desc_add_and_update_idx(uds_ctx, desc_nd);
166 		if (ret) {
167 			ret = -EINVAL;
168 			goto add_descriptor_error;
169 		}
170 
171 		switch (desc_nd->str.utype) {
172 		case USBD_DUT_STRING_LANG:
173 			break;
174 		case USBD_DUT_STRING_MANUFACTURER:
175 			if (USBD_SUPPORTS_HIGH_SPEED) {
176 				hs_desc->iManufacturer = desc_nd->str.idx;
177 			}
178 
179 			fs_desc->iManufacturer = desc_nd->str.idx;
180 			break;
181 		case USBD_DUT_STRING_PRODUCT:
182 			if (USBD_SUPPORTS_HIGH_SPEED) {
183 				hs_desc->iProduct = desc_nd->str.idx;
184 			}
185 
186 			fs_desc->iProduct = desc_nd->str.idx;
187 			break;
188 		case USBD_DUT_STRING_SERIAL_NUMBER:
189 			if (USBD_SUPPORTS_HIGH_SPEED) {
190 				hs_desc->iSerialNumber = desc_nd->str.idx;
191 			}
192 
193 			fs_desc->iSerialNumber = desc_nd->str.idx;
194 			break;
195 		default:
196 			break;
197 		}
198 	}
199 
200 add_descriptor_error:
201 	usbd_device_unlock(uds_ctx);
202 	return ret;
203 }
204 
usbd_str_desc_get_idx(const struct usbd_desc_node * const desc_nd)205 uint8_t usbd_str_desc_get_idx(const struct usbd_desc_node *const desc_nd)
206 {
207 	if (sys_dnode_is_linked(&desc_nd->node)) {
208 		return desc_nd->str.idx;
209 	}
210 
211 	return 0;
212 }
213 
usbd_remove_descriptor(struct usbd_desc_node * const desc_nd)214 void usbd_remove_descriptor(struct usbd_desc_node *const desc_nd)
215 {
216 	if (sys_dnode_is_linked(&desc_nd->node)) {
217 		sys_dlist_remove(&desc_nd->node);
218 
219 		if (desc_nd->bDescriptorType == USB_DESC_STRING) {
220 			desc_nd->str.idx = 0U;
221 		}
222 	}
223 }
224