1 /*
2 * Copyright (c) 2024 Nordic Semiconductor ASA
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/logging/log.h>
8 LOG_MODULE_REGISTER(sfunc, LOG_LEVEL_INF);
9
10 #include <zephyr/usb/usbd.h>
11 #include <zephyr/drivers/usb/udc.h>
12 #include <zephyr/sys/byteorder.h>
13
14 /*
15 * This file implements a simple USB function that echoes received data back to
16 * the host using bulk endpoints.
17 */
18
19 NET_BUF_POOL_FIXED_DEFINE(sfunc_pool,
20 1, 0, sizeof(struct udc_buf_info), NULL);
21
22 UDC_STATIC_BUF_DEFINE(sfunc_buf, 512);
23
24 struct sfunc_desc {
25 struct usb_if_descriptor if0;
26 struct usb_ep_descriptor if0_out_ep;
27 struct usb_ep_descriptor if0_in_ep;
28 struct usb_ep_descriptor if0_hs_out_ep;
29 struct usb_ep_descriptor if0_hs_in_ep;
30 struct usb_desc_header nil_desc;
31 };
32
33 #define SAMPLE_FUNCTION_ENABLED 0
34
35 struct sfunc_data {
36 struct sfunc_desc *const desc;
37 const struct usb_desc_header **const fs_desc;
38 const struct usb_desc_header **const hs_desc;
39 atomic_t state;
40 };
41
sfunc_get_bulk_out(struct usbd_class_data * const c_data)42 static uint8_t sfunc_get_bulk_out(struct usbd_class_data *const c_data)
43 {
44 struct sfunc_data *data = usbd_class_get_private(c_data);
45 struct usbd_context *uds_ctx = usbd_class_get_ctx(c_data);
46 struct sfunc_desc *desc = data->desc;
47
48 if (usbd_bus_speed(uds_ctx) == USBD_SPEED_HS) {
49 return desc->if0_hs_out_ep.bEndpointAddress;
50 }
51
52 return desc->if0_out_ep.bEndpointAddress;
53 }
54
sfunc_get_bulk_in(struct usbd_class_data * const c_data)55 static uint8_t sfunc_get_bulk_in(struct usbd_class_data *const c_data)
56 {
57 struct sfunc_data *data = usbd_class_get_private(c_data);
58 struct usbd_context *uds_ctx = usbd_class_get_ctx(c_data);
59 struct sfunc_desc *desc = data->desc;
60
61 if (usbd_bus_speed(uds_ctx) == USBD_SPEED_HS) {
62 return desc->if0_hs_in_ep.bEndpointAddress;
63 }
64
65 return desc->if0_in_ep.bEndpointAddress;
66 }
67
sfunc_request_handler(struct usbd_class_data * c_data,struct net_buf * buf,int err)68 static int sfunc_request_handler(struct usbd_class_data *c_data,
69 struct net_buf *buf, int err)
70 {
71 struct usbd_context *uds_ctx = usbd_class_get_ctx(c_data);
72 struct sfunc_data *data = usbd_class_get_private(c_data);
73 struct udc_buf_info *bi = NULL;
74
75 bi = (struct udc_buf_info *)net_buf_user_data(buf);
76 LOG_INF("Transfer finished %p -> ep 0x%02x, len %u, err %d",
77 (void *)c_data, bi->ep, buf->len, err);
78
79 if (atomic_test_bit(&data->state, SAMPLE_FUNCTION_ENABLED) && err == 0) {
80 uint8_t ep = bi->ep;
81
82 memset(bi, 0, sizeof(struct udc_buf_info));
83
84 if (ep == sfunc_get_bulk_in(c_data)) {
85 bi->ep = sfunc_get_bulk_out(c_data);
86 net_buf_reset(buf);
87 } else {
88 bi->ep = sfunc_get_bulk_in(c_data);
89 }
90
91 if (usbd_ep_enqueue(c_data, buf)) {
92 LOG_ERR("Failed to enqueue buffer");
93 usbd_ep_buf_free(uds_ctx, buf);
94 }
95 } else {
96 LOG_ERR("Function is disabled or transfer failed");
97 usbd_ep_buf_free(uds_ctx, buf);
98 }
99
100 return 0;
101 }
102
sfunc_get_desc(struct usbd_class_data * const c_data,const enum usbd_speed speed)103 static void *sfunc_get_desc(struct usbd_class_data *const c_data,
104 const enum usbd_speed speed)
105 {
106 struct sfunc_data *data = usbd_class_get_private(c_data);
107
108 if (speed == USBD_SPEED_HS) {
109 return data->hs_desc;
110 }
111
112 return data->fs_desc;
113 }
114
sfunc_buf_alloc(struct usbd_class_data * const c_data,const uint8_t ep)115 struct net_buf *sfunc_buf_alloc(struct usbd_class_data *const c_data,
116 const uint8_t ep)
117 {
118 struct usbd_context *uds_ctx = usbd_class_get_ctx(c_data);
119 struct net_buf *buf = NULL;
120 struct udc_buf_info *bi;
121 size_t size;
122
123 if (usbd_bus_speed(uds_ctx) == USBD_SPEED_HS) {
124 size = 512U;
125 } else {
126 size = 64U;
127 }
128
129 buf = net_buf_alloc_with_data(&sfunc_pool, sfunc_buf, size, K_NO_WAIT);
130 net_buf_reset(buf);
131 if (!buf) {
132 return NULL;
133 }
134
135 bi = udc_get_buf_info(buf);
136 bi->ep = ep;
137
138 return buf;
139 }
140
sfunc_enable(struct usbd_class_data * const c_data)141 static void sfunc_enable(struct usbd_class_data *const c_data)
142 {
143 struct sfunc_data *data = usbd_class_get_private(c_data);
144 struct usbd_context *uds_ctx = usbd_class_get_ctx(c_data);
145 struct net_buf *buf;
146
147 LOG_INF("Configuration enabled");
148
149 if (!atomic_test_and_set_bit(&data->state, SAMPLE_FUNCTION_ENABLED)) {
150 buf = sfunc_buf_alloc(c_data, sfunc_get_bulk_out(c_data));
151 if (buf == NULL) {
152 LOG_ERR("Failed to allocate buffer");
153 return;
154 }
155
156 if (usbd_ep_enqueue(c_data, buf)) {
157 LOG_ERR("Failed to enqueue buffer");
158 usbd_ep_buf_free(uds_ctx, buf);
159 }
160 }
161 }
162
sfunc_disable(struct usbd_class_data * const c_data)163 static void sfunc_disable(struct usbd_class_data *const c_data)
164 {
165 struct sfunc_data *data = usbd_class_get_private(c_data);
166
167 atomic_clear_bit(&data->state, SAMPLE_FUNCTION_ENABLED);
168 LOG_INF("Configuration disabled");
169 }
170
sfunc_init(struct usbd_class_data * c_data)171 static int sfunc_init(struct usbd_class_data *c_data)
172 {
173 LOG_DBG("Init class instance %p", (void *)c_data);
174
175 return 0;
176 }
177
178 struct usbd_class_api sfunc_api = {
179 .request = sfunc_request_handler,
180 .get_desc = sfunc_get_desc,
181 .enable = sfunc_enable,
182 .disable = sfunc_disable,
183 .init = sfunc_init,
184 };
185
186 #define SFUNC_DESCRIPTOR_DEFINE(n, _) \
187 static struct sfunc_desc sfunc_desc_##n = { \
188 /* Interface descriptor 0 */ \
189 .if0 = { \
190 .bLength = sizeof(struct usb_if_descriptor), \
191 .bDescriptorType = USB_DESC_INTERFACE, \
192 .bInterfaceNumber = 0, \
193 .bAlternateSetting = 0, \
194 .bNumEndpoints = 2, \
195 .bInterfaceClass = USB_BCC_VENDOR, \
196 .bInterfaceSubClass = 0, \
197 .bInterfaceProtocol = 0, \
198 .iInterface = 0, \
199 }, \
200 \
201 /* Endpoint OUT */ \
202 .if0_out_ep = { \
203 .bLength = sizeof(struct usb_ep_descriptor), \
204 .bDescriptorType = USB_DESC_ENDPOINT, \
205 .bEndpointAddress = 0x01, \
206 .bmAttributes = USB_EP_TYPE_BULK, \
207 .wMaxPacketSize = sys_cpu_to_le16(64U), \
208 .bInterval = 0x00, \
209 }, \
210 \
211 /* Endpoint IN */ \
212 .if0_in_ep = { \
213 .bLength = sizeof(struct usb_ep_descriptor), \
214 .bDescriptorType = USB_DESC_ENDPOINT, \
215 .bEndpointAddress = 0x81, \
216 .bmAttributes = USB_EP_TYPE_BULK, \
217 .wMaxPacketSize = sys_cpu_to_le16(64U), \
218 .bInterval = 0x00, \
219 }, \
220 \
221 /* High-speed Endpoint OUT */ \
222 .if0_hs_out_ep = { \
223 .bLength = sizeof(struct usb_ep_descriptor), \
224 .bDescriptorType = USB_DESC_ENDPOINT, \
225 .bEndpointAddress = 0x01, \
226 .bmAttributes = USB_EP_TYPE_BULK, \
227 .wMaxPacketSize = sys_cpu_to_le16(512), \
228 .bInterval = 0x00, \
229 }, \
230 \
231 /* High-speed Endpoint IN */ \
232 .if0_hs_in_ep = { \
233 .bLength = sizeof(struct usb_ep_descriptor), \
234 .bDescriptorType = USB_DESC_ENDPOINT, \
235 .bEndpointAddress = 0x81, \
236 .bmAttributes = USB_EP_TYPE_BULK, \
237 .wMaxPacketSize = sys_cpu_to_le16(512), \
238 .bInterval = 0x00, \
239 }, \
240 \
241 /* Termination descriptor */ \
242 .nil_desc = { \
243 .bLength = 0, \
244 .bDescriptorType = 0, \
245 }, \
246 }; \
247 \
248 const static struct usb_desc_header *sfunc_fs_desc_##n[] = { \
249 (struct usb_desc_header *) &sfunc_desc_##n.if0, \
250 (struct usb_desc_header *) &sfunc_desc_##n.if0_in_ep, \
251 (struct usb_desc_header *) &sfunc_desc_##n.if0_out_ep, \
252 (struct usb_desc_header *) &sfunc_desc_##n.nil_desc, \
253 }; \
254 \
255 const static struct usb_desc_header *sfunc_hs_desc_##n[] = { \
256 (struct usb_desc_header *) &sfunc_desc_##n.if0, \
257 (struct usb_desc_header *) &sfunc_desc_##n.if0_hs_in_ep, \
258 (struct usb_desc_header *) &sfunc_desc_##n.if0_hs_out_ep, \
259 (struct usb_desc_header *) &sfunc_desc_##n.nil_desc, \
260 };
261
262
263 #define SFUNC_FUNCTION_DATA_DEFINE(n, _) \
264 static struct sfunc_data sfunc_data_##n = { \
265 .desc = &sfunc_desc_##n, \
266 .fs_desc = sfunc_fs_desc_##n, \
267 .hs_desc = sfunc_hs_desc_##n, \
268 }; \
269 \
270 USBD_DEFINE_CLASS(sfunc_##n, &sfunc_api, &sfunc_data_##n, NULL);
271
272 LISTIFY(1, SFUNC_DESCRIPTOR_DEFINE, ())
273 LISTIFY(1, SFUNC_FUNCTION_DATA_DEFINE, ())
274