1 /*
2  * Copyright (c) 2015-2019 Intel Corporation
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 /**
8  * @file
9  * @brief WebUSB enabled custom class driver
10  *
11  * This is a modified version of CDC ACM class driver
12  * to support the WebUSB.
13  */
14 
15 #define LOG_LEVEL CONFIG_USB_DEVICE_LOG_LEVEL
16 #include <zephyr/logging/log.h>
17 LOG_MODULE_REGISTER(webusb);
18 
19 #include <zephyr/sys/byteorder.h>
20 #include <zephyr/usb/usb_device.h>
21 #include <usb_descriptor.h>
22 
23 #include "webusb.h"
24 
25 /* Max packet size for Bulk endpoints */
26 #if defined(CONFIG_USB_DC_HAS_HS_SUPPORT)
27 #define WEBUSB_BULK_EP_MPS		512
28 #else
29 #define WEBUSB_BULK_EP_MPS		64
30 #endif
31 
32 /* Number of interfaces */
33 #define WEBUSB_NUM_ITF			0x01
34 /* Number of Endpoints in the custom interface */
35 #define WEBUSB_NUM_EP			0x02
36 
37 #define WEBUSB_IN_EP_IDX		0
38 #define WEBUSB_OUT_EP_IDX		1
39 
40 static struct webusb_req_handlers *req_handlers;
41 
42 uint8_t rx_buf[WEBUSB_BULK_EP_MPS];
43 
44 #define INITIALIZER_IF(num_ep, iface_class)				\
45 	{								\
46 		.bLength = sizeof(struct usb_if_descriptor),		\
47 		.bDescriptorType = USB_DESC_INTERFACE,			\
48 		.bInterfaceNumber = 0,					\
49 		.bAlternateSetting = 0,					\
50 		.bNumEndpoints = num_ep,				\
51 		.bInterfaceClass = iface_class,				\
52 		.bInterfaceSubClass = 0,				\
53 		.bInterfaceProtocol = 0,				\
54 		.iInterface = 0,					\
55 	}
56 
57 #define INITIALIZER_IF_EP(addr, attr, mps, interval)			\
58 	{								\
59 		.bLength = sizeof(struct usb_ep_descriptor),		\
60 		.bDescriptorType = USB_DESC_ENDPOINT,			\
61 		.bEndpointAddress = addr,				\
62 		.bmAttributes = attr,					\
63 		.wMaxPacketSize = sys_cpu_to_le16(mps),			\
64 		.bInterval = interval,					\
65 	}
66 
67 USBD_CLASS_DESCR_DEFINE(primary, 0) struct {
68 	struct usb_if_descriptor if0;
69 	struct usb_ep_descriptor if0_in_ep;
70 	struct usb_ep_descriptor if0_out_ep;
71 } __packed webusb_desc = {
72 	.if0 = INITIALIZER_IF(WEBUSB_NUM_EP, USB_BCC_VENDOR),
73 	.if0_in_ep = INITIALIZER_IF_EP(AUTO_EP_IN, USB_DC_EP_BULK,
74 				       WEBUSB_BULK_EP_MPS, 0),
75 	.if0_out_ep = INITIALIZER_IF_EP(AUTO_EP_OUT, USB_DC_EP_BULK,
76 					WEBUSB_BULK_EP_MPS, 0),
77 };
78 
79 /**
80  * @brief Custom handler for standard requests in order to
81  *        catch the request and return the WebUSB Platform
82  *        Capability Descriptor.
83  *
84  * @param pSetup    Information about the request to execute.
85  * @param len       Size of the buffer.
86  * @param data      Buffer containing the request result.
87  *
88  * @return  0 on success, negative errno code on fail.
89  */
webusb_custom_handle_req(struct usb_setup_packet * pSetup,int32_t * len,uint8_t ** data)90 int webusb_custom_handle_req(struct usb_setup_packet *pSetup,
91 			     int32_t *len, uint8_t **data)
92 {
93 	LOG_DBG("");
94 
95 	/* Call the callback */
96 	if (req_handlers && req_handlers->custom_handler) {
97 		return req_handlers->custom_handler(pSetup, len, data);
98 	}
99 
100 	return -EINVAL;
101 }
102 
103 /**
104  * @brief Handler called for WebUSB vendor specific commands.
105  *
106  * @param pSetup    Information about the request to execute.
107  * @param len       Size of the buffer.
108  * @param data      Buffer containing the request result.
109  *
110  * @return  0 on success, negative errno code on fail.
111  */
webusb_vendor_handle_req(struct usb_setup_packet * pSetup,int32_t * len,uint8_t ** data)112 int webusb_vendor_handle_req(struct usb_setup_packet *pSetup,
113 			     int32_t *len, uint8_t **data)
114 {
115 	/* Call the callback */
116 	if (req_handlers && req_handlers->vendor_handler) {
117 		return req_handlers->vendor_handler(pSetup, len, data);
118 	}
119 
120 	return -EINVAL;
121 }
122 
123 /**
124  * @brief Register Custom and Vendor request callbacks
125  *
126  * This function registers Custom and Vendor request callbacks
127  * for handling the device requests.
128  *
129  * @param [in] handlers Pointer to WebUSB request handlers structure
130  */
webusb_register_request_handlers(struct webusb_req_handlers * handlers)131 void webusb_register_request_handlers(struct webusb_req_handlers *handlers)
132 {
133 	req_handlers = handlers;
134 }
135 
webusb_write_cb(uint8_t ep,int size,void * priv)136 static void webusb_write_cb(uint8_t ep, int size, void *priv)
137 {
138 	LOG_DBG("ep %x size %u", ep, size);
139 }
140 
webusb_read_cb(uint8_t ep,int size,void * priv)141 static void webusb_read_cb(uint8_t ep, int size, void *priv)
142 {
143 	struct usb_cfg_data *cfg = priv;
144 
145 	LOG_DBG("cfg %p ep %x size %u", cfg, ep, size);
146 
147 	if (size <= 0) {
148 		goto done;
149 	}
150 
151 	usb_transfer(cfg->endpoint[WEBUSB_IN_EP_IDX].ep_addr, rx_buf, size,
152 		     USB_TRANS_WRITE, webusb_write_cb, cfg);
153 done:
154 	usb_transfer(ep, rx_buf, sizeof(rx_buf), USB_TRANS_READ,
155 		     webusb_read_cb, cfg);
156 }
157 
158 /**
159  * @brief Callback used to know the USB connection status
160  *
161  * @param status USB device status code.
162  */
webusb_dev_status_cb(struct usb_cfg_data * cfg,enum usb_dc_status_code status,const uint8_t * param)163 static void webusb_dev_status_cb(struct usb_cfg_data *cfg,
164 				 enum usb_dc_status_code status,
165 				 const uint8_t *param)
166 {
167 	ARG_UNUSED(param);
168 	ARG_UNUSED(cfg);
169 
170 	/* Check the USB status and do needed action if required */
171 	switch (status) {
172 	case USB_DC_ERROR:
173 		LOG_DBG("USB device error");
174 		break;
175 	case USB_DC_RESET:
176 		LOG_DBG("USB device reset detected");
177 		break;
178 	case USB_DC_CONNECTED:
179 		LOG_DBG("USB device connected");
180 		break;
181 	case USB_DC_CONFIGURED:
182 		LOG_DBG("USB device configured");
183 		webusb_read_cb(cfg->endpoint[WEBUSB_OUT_EP_IDX].ep_addr,
184 			       0, cfg);
185 		break;
186 	case USB_DC_DISCONNECTED:
187 		LOG_DBG("USB device disconnected");
188 		break;
189 	case USB_DC_SUSPEND:
190 		LOG_DBG("USB device suspended");
191 		break;
192 	case USB_DC_RESUME:
193 		LOG_DBG("USB device resumed");
194 		break;
195 	case USB_DC_UNKNOWN:
196 	default:
197 		LOG_DBG("USB unknown state");
198 		break;
199 	}
200 }
201 
202 /* Describe EndPoints configuration */
203 static struct usb_ep_cfg_data webusb_ep_data[] = {
204 	{
205 		.ep_cb = usb_transfer_ep_callback,
206 		.ep_addr = AUTO_EP_IN
207 	},
208 	{
209 		.ep_cb	= usb_transfer_ep_callback,
210 		.ep_addr = AUTO_EP_OUT
211 	}
212 };
213 
214 USBD_DEFINE_CFG_DATA(webusb_config) = {
215 	.usb_device_description = NULL,
216 	.interface_descriptor = &webusb_desc.if0,
217 	.cb_usb_status = webusb_dev_status_cb,
218 	.interface = {
219 		.class_handler = NULL,
220 		.custom_handler = webusb_custom_handle_req,
221 		.vendor_handler = webusb_vendor_handle_req,
222 	},
223 	.num_endpoints = ARRAY_SIZE(webusb_ep_data),
224 	.endpoint = webusb_ep_data
225 };
226