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