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 (desc_nd->bDescriptorType == USB_DESC_BOS) {
97 				return desc_nd;
98 			}
99 		}
100 	}
101 
102 	return NULL;
103 }
104 
usbd_desc_remove_all(struct usbd_context * const uds_ctx)105 int usbd_desc_remove_all(struct usbd_context *const uds_ctx)
106 {
107 	struct usbd_desc_node *tmp;
108 	sys_dnode_t *node;
109 
110 	while ((node = sys_dlist_get(&uds_ctx->descriptors))) {
111 		tmp = CONTAINER_OF(node, struct usbd_desc_node, node);
112 		LOG_DBG("Remove descriptor node %p type %u",
113 			(void *)tmp, tmp->str.utype);
114 	}
115 
116 	return 0;
117 }
118 
usbd_add_descriptor(struct usbd_context * const uds_ctx,struct usbd_desc_node * const desc_nd)119 int usbd_add_descriptor(struct usbd_context *const uds_ctx,
120 			struct usbd_desc_node *const desc_nd)
121 {
122 	struct usb_device_descriptor *hs_desc, *fs_desc;
123 	int ret = 0;
124 
125 	usbd_device_lock(uds_ctx);
126 
127 	hs_desc = uds_ctx->hs_desc;
128 	fs_desc = uds_ctx->fs_desc;
129 	if (!fs_desc || !hs_desc || usbd_is_initialized(uds_ctx)) {
130 		ret = -EPERM;
131 		goto add_descriptor_error;
132 	}
133 
134 	/* Check if descriptor list is initialized */
135 	if (!sys_dnode_is_linked(&uds_ctx->descriptors)) {
136 		LOG_DBG("Initialize descriptors list");
137 		sys_dlist_init(&uds_ctx->descriptors);
138 	}
139 
140 	if (sys_dnode_is_linked(&desc_nd->node)) {
141 		ret = -EALREADY;
142 		goto add_descriptor_error;
143 	}
144 
145 	if (desc_nd->bDescriptorType == USB_DESC_BOS) {
146 		if (desc_nd->bos.utype == USBD_DUT_BOS_VREQ) {
147 			ret =  usbd_device_register_vreq(uds_ctx, desc_nd->bos.vreq_nd);
148 			if (ret) {
149 				goto add_descriptor_error;
150 			}
151 		}
152 
153 		sys_dlist_append(&uds_ctx->descriptors, &desc_nd->node);
154 	}
155 
156 	if (desc_nd->bDescriptorType == USB_DESC_STRING) {
157 		ret = desc_add_and_update_idx(uds_ctx, desc_nd);
158 		if (ret) {
159 			ret = -EINVAL;
160 			goto add_descriptor_error;
161 		}
162 
163 		switch (desc_nd->str.utype) {
164 		case USBD_DUT_STRING_LANG:
165 			break;
166 		case USBD_DUT_STRING_MANUFACTURER:
167 			hs_desc->iManufacturer = desc_nd->str.idx;
168 			fs_desc->iManufacturer = desc_nd->str.idx;
169 			break;
170 		case USBD_DUT_STRING_PRODUCT:
171 			hs_desc->iProduct = desc_nd->str.idx;
172 			fs_desc->iProduct = desc_nd->str.idx;
173 			break;
174 		case USBD_DUT_STRING_SERIAL_NUMBER:
175 			hs_desc->iSerialNumber = desc_nd->str.idx;
176 			fs_desc->iSerialNumber = desc_nd->str.idx;
177 			break;
178 		default:
179 			break;
180 		}
181 	}
182 
183 add_descriptor_error:
184 	usbd_device_unlock(uds_ctx);
185 	return ret;
186 }
187 
usbd_str_desc_get_idx(const struct usbd_desc_node * const desc_nd)188 uint8_t usbd_str_desc_get_idx(const struct usbd_desc_node *const desc_nd)
189 {
190 	if (sys_dnode_is_linked(&desc_nd->node)) {
191 		return desc_nd->str.idx;
192 	}
193 
194 	return 0;
195 }
196 
usbd_remove_descriptor(struct usbd_desc_node * const desc_nd)197 void usbd_remove_descriptor(struct usbd_desc_node *const desc_nd)
198 {
199 	if (sys_dnode_is_linked(&desc_nd->node)) {
200 		sys_dlist_remove(&desc_nd->node);
201 
202 		if (desc_nd->bDescriptorType == USB_DESC_STRING) {
203 			desc_nd->str.idx = 0U;
204 		}
205 	}
206 }
207