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