1 /*
2 * Copyright (c) 2022 Nordic Semiconductor ASA
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/drivers/usb/udc.h>
8 #include <zephyr/usb/usbd.h>
9
10 #include "usbd_device.h"
11 #include "usbd_config.h"
12 #include "usbd_interface.h"
13 #include "usbd_ch9.h"
14 #include "usbd_class_api.h"
15
16 #include <zephyr/logging/log.h>
17 LOG_MODULE_REGISTER(usbd_cfg, CONFIG_USBD_LOG_LEVEL);
18
usbd_config_get(struct usbd_contex * const uds_ctx,const uint8_t cfg)19 struct usbd_config_node *usbd_config_get(struct usbd_contex *const uds_ctx,
20 const uint8_t cfg)
21 {
22 struct usbd_config_node *cfg_nd;
23
24 SYS_SLIST_FOR_EACH_CONTAINER(&uds_ctx->configs, cfg_nd, node) {
25 if (usbd_config_get_value(cfg_nd) == cfg) {
26 return cfg_nd;
27 }
28 }
29
30 return NULL;
31 }
32
33 struct usbd_config_node *
usbd_config_get_current(struct usbd_contex * const uds_ctx)34 usbd_config_get_current(struct usbd_contex *const uds_ctx)
35 {
36 if (!usbd_state_is_configured(uds_ctx)) {
37 LOG_INF("No configuration set (Address state?)");
38 return NULL;
39 }
40
41 return usbd_config_get(uds_ctx, usbd_get_config_value(uds_ctx));
42 }
43
usbd_config_classes_enable(struct usbd_config_node * const cfg_nd,const bool enable)44 static void usbd_config_classes_enable(struct usbd_config_node *const cfg_nd,
45 const bool enable)
46 {
47 struct usbd_class_node *c_nd;
48
49 SYS_SLIST_FOR_EACH_CONTAINER(&cfg_nd->class_list, c_nd, node) {
50 if (enable) {
51 usbd_class_enable(c_nd);
52 } else {
53 usbd_class_disable(c_nd);
54 }
55 }
56 }
57
58 /* Reset configuration to addressed state, shutdown all endpoints */
usbd_config_reset(struct usbd_contex * const uds_ctx)59 static int usbd_config_reset(struct usbd_contex *const uds_ctx)
60 {
61 struct usbd_config_node *cfg_nd;
62 int ret = 0;
63
64 cfg_nd = usbd_config_get_current(uds_ctx);
65 if (cfg_nd == NULL) {
66 return -ENODATA;
67 }
68
69 ret = usbd_interface_shutdown(uds_ctx, cfg_nd);
70
71 memset(&uds_ctx->ch9_data.alternate, 0,
72 USBD_NUMOF_INTERFACES_MAX);
73
74 usbd_set_config_value(uds_ctx, 0);
75 usbd_config_classes_enable(cfg_nd, false);
76
77 return ret;
78 }
79
usbd_config_exist(struct usbd_contex * const uds_ctx,const uint8_t cfg)80 bool usbd_config_exist(struct usbd_contex *const uds_ctx,
81 const uint8_t cfg)
82 {
83 struct usbd_config_node *config;
84
85 config = usbd_config_get(uds_ctx, cfg);
86
87 return (config != NULL) ? true : false;
88 }
89
usbd_config_set(struct usbd_contex * const uds_ctx,const uint8_t new_cfg)90 int usbd_config_set(struct usbd_contex *const uds_ctx,
91 const uint8_t new_cfg)
92 {
93 struct usbd_config_node *cfg_nd;
94 int ret;
95
96 if (usbd_get_config_value(uds_ctx) != 0) {
97 ret = usbd_config_reset(uds_ctx);
98 if (ret) {
99 LOG_ERR("Failed to reset configuration");
100 return ret;
101 }
102 }
103
104 if (new_cfg == 0) {
105 usbd_set_config_value(uds_ctx, new_cfg);
106 return 0;
107 }
108
109 cfg_nd = usbd_config_get(uds_ctx, new_cfg);
110 if (cfg_nd == NULL) {
111 return -ENODATA;
112 }
113
114 ret = usbd_interface_default(uds_ctx, cfg_nd);
115 if (ret) {
116 return ret;
117 }
118
119 usbd_set_config_value(uds_ctx, new_cfg);
120 usbd_config_classes_enable(cfg_nd, true);
121
122 return 0;
123 }
124
125 /*
126 * All the functions below are part of public USB device support API.
127 */
128
usbd_config_attrib_rwup(struct usbd_contex * const uds_ctx,const uint8_t cfg,const bool enable)129 int usbd_config_attrib_rwup(struct usbd_contex *const uds_ctx,
130 const uint8_t cfg, const bool enable)
131 {
132 struct usbd_config_node *cfg_nd;
133 struct usb_cfg_descriptor *desc;
134 struct udc_device_caps caps;
135 int ret = 0;
136
137 usbd_device_lock(uds_ctx);
138
139 if (usbd_is_enabled(uds_ctx)) {
140 ret = -EALREADY;
141 goto attrib_rwup_exit;
142 }
143
144 caps = udc_caps(uds_ctx->dev);
145 if (!caps.rwup) {
146 LOG_ERR("Feature not supported by controller");
147 ret = -ENOTSUP;
148 goto attrib_rwup_exit;
149 }
150
151 cfg_nd = usbd_config_get(uds_ctx, cfg);
152 if (cfg_nd == NULL) {
153 LOG_INF("Configuration %u not found", cfg);
154 ret = -ENODATA;
155 goto attrib_rwup_exit;
156 }
157
158 desc = cfg_nd->desc;
159 if (enable) {
160 desc->bmAttributes |= USB_SCD_REMOTE_WAKEUP;
161 } else {
162 desc->bmAttributes &= ~USB_SCD_REMOTE_WAKEUP;
163 }
164
165 attrib_rwup_exit:
166 usbd_device_unlock(uds_ctx);
167 return ret;
168 }
169
usbd_config_attrib_self(struct usbd_contex * const uds_ctx,const uint8_t cfg,const bool enable)170 int usbd_config_attrib_self(struct usbd_contex *const uds_ctx,
171 const uint8_t cfg, const bool enable)
172 {
173 struct usbd_config_node *cfg_nd;
174 struct usb_cfg_descriptor *desc;
175 int ret = 0;
176
177 usbd_device_lock(uds_ctx);
178
179 if (usbd_is_enabled(uds_ctx)) {
180 ret = -EALREADY;
181 goto attrib_self_exit;
182 }
183
184 cfg_nd = usbd_config_get(uds_ctx, cfg);
185 if (cfg_nd == NULL) {
186 LOG_INF("Configuration %u not found", cfg);
187 ret = -ENODATA;
188 goto attrib_self_exit;
189 }
190
191 desc = cfg_nd->desc;
192 if (enable) {
193 desc->bmAttributes |= USB_SCD_SELF_POWERED;
194 } else {
195 desc->bmAttributes &= ~USB_SCD_SELF_POWERED;
196 }
197
198 attrib_self_exit:
199 usbd_device_unlock(uds_ctx);
200 return ret;
201 }
202
usbd_config_maxpower(struct usbd_contex * const uds_ctx,const uint8_t cfg,const uint8_t power)203 int usbd_config_maxpower(struct usbd_contex *const uds_ctx,
204 const uint8_t cfg, const uint8_t power)
205 {
206 struct usbd_config_node *cfg_nd;
207 struct usb_cfg_descriptor *desc;
208 int ret = 0;
209
210 usbd_device_lock(uds_ctx);
211
212 if (usbd_is_enabled(uds_ctx)) {
213 ret = -EALREADY;
214 goto maxpower_exit;
215 }
216
217 cfg_nd = usbd_config_get(uds_ctx, cfg);
218 if (cfg_nd == NULL) {
219 LOG_INF("Configuration %u not found", cfg);
220 ret = -ENODATA;
221 goto maxpower_exit;
222 }
223
224 desc = cfg_nd->desc;
225 desc->bMaxPower = power;
226
227 maxpower_exit:
228 usbd_device_unlock(uds_ctx);
229 return ret;
230 }
231
usbd_add_configuration(struct usbd_contex * const uds_ctx,struct usbd_config_node * const cfg_nd)232 int usbd_add_configuration(struct usbd_contex *const uds_ctx,
233 struct usbd_config_node *const cfg_nd)
234 {
235 struct usb_cfg_descriptor *desc = cfg_nd->desc;
236 int ret = 0;
237
238 usbd_device_lock(uds_ctx);
239
240 if (usbd_is_initialized(uds_ctx)) {
241 LOG_ERR("USB device support is initialized");
242 ret = -EBUSY;
243 goto add_configuration_exit;
244 }
245
246 if (desc->bmAttributes & USB_SCD_REMOTE_WAKEUP) {
247 struct udc_device_caps caps = udc_caps(uds_ctx->dev);
248
249 if (!caps.rwup) {
250 LOG_ERR("Feature not supported by controller");
251 ret = -ENOTSUP;
252 goto add_configuration_exit;
253 }
254 }
255
256 if (sys_slist_find_and_remove(&uds_ctx->configs, &cfg_nd->node)) {
257 LOG_WRN("Configuration %u re-inserted",
258 usbd_config_get_value(cfg_nd));
259 } else {
260 usbd_config_set_value(cfg_nd, usbd_get_num_configs(uds_ctx) + 1);
261 usbd_set_num_configs(uds_ctx, usbd_get_num_configs(uds_ctx) + 1);
262 }
263
264 sys_slist_append(&uds_ctx->configs, &cfg_nd->node);
265
266 usbd_device_unlock(uds_ctx);
267
268 add_configuration_exit:
269 usbd_device_unlock(uds_ctx);
270 return ret;
271 }
272