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