1 /*
2 * Copyright 2016 Google Inc. All Rights Reserved.
3 * Author: [email protected] (Gurjant Kalsi)
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining
6 * a copy of this software and associated documentation files
7 * (the "Software"), to deal in the Software without restriction,
8 * including without limitation the rights to use, copy, modify, merge,
9 * publish, distribute, sublicense, and/or sell copies of the Software,
10 * and to permit persons to whom the Software is furnished to do so,
11 * subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be
14 * included in all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
21 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
22 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 */
24
25 #include <dev/usb/class/cdcserial.h>
26
27 #include <assert.h>
28 #include <dev/udc.h>
29 #include <dev/usb.h>
30 #include <dev/usbc.h>
31 #include <dev/usbc.h>
32 #include <err.h>
33 #include <kernel/event.h>
34 #include <sys/types.h>
35 #include <trace.h>
36
37 #define LOCAL_TRACE 0
38
39 #define W(w) (w & 0xff), (w >> 8)
40 #define W3(w) (w & 0xff), ((w >> 8) & 0xff), ((w >> 16) & 0xff)
41
42 #define CDC_REQ_SEND_CMD 0x00
43 #define CDC_REQ_GET_RESP 0x01
44 #define CDC_REQ_SET_COMM_FEATURE 0x02
45 #define CDC_REQ_GET_COMM_FEATURE 0x03
46 #define CDC_REQ_CLEAR_COMM_FEATURE 0x04
47 #define CDC_REQ_SET_LINE_CODING 0x20
48 #define CDC_REQ_GET_LINE_CODING 0x21
49 #define CDC_REQ_SET_CONTROL_LINE_STATE 0x22
50 #define CDC_REQ_SEND_BREAK 0x23
51
52 #define EP0_MTU (64)
53 #define MAX_USB_ENDPOINT_PAIRS (16)
54
55 // NB: These must be kept in sync with the if_descriptors below.
56 #define CTRL_IN_EP_ADDR_OFFSET (0x0B)
57 #define DATA_IN_EP_ADDR_OFFSET (0x0B)
58 #define DATA_OUT_EP_ADDR_OFFSET (0x12)
59 #define LEADER_EP_NUMBER_OFFSET (0x1C)
60 #define FOLLOWER_EP_NUMBER_OFFSET (0x1D)
61
62 static event_t txevt = EVENT_INITIAL_VALUE(txevt, 0, EVENT_FLAG_AUTOUNSIGNAL);
63 static event_t rxevt = EVENT_INITIAL_VALUE(rxevt, 0, EVENT_FLAG_AUTOUNSIGNAL);
64
65 static volatile bool usb_online = false;
66
67 // A bitfield corresponding to the registered endpoints. When we get a
68 // USB_ONLINE event, these are the endpoints that we need to setup.
69 static volatile uint16_t registered_bulk_eps_in;
70 static volatile uint16_t registered_bulk_eps_out;
71 static volatile uint16_t registered_intr_eps_in;
72 static volatile uint16_t registered_intr_eps_out;
73
74 static uint8_t ctrl_if_descriptor[] = {
75 0x09, /* length */
76 INTERFACE, /* type */
77 0x00, /* interface num */
78 0x00, /* alternates */
79 0x01, /* endpoint count */
80 0x02, /* interface class */
81 0x02, /* interface subclass */
82 0x01, /* interface protocol */
83 0x00, /* string index */
84
85 /* endpoint 1 IN */
86 0x07, /* length */
87 ENDPOINT, /* type */
88 0x80, /* address: 1 IN */
89 0x03, /* type: bulk */
90 W(8), /* max packet size: 8 */
91 0xFF, /* interval */
92
93 /*Call Management Functional Descriptor*/
94 0x05, /* bFunctionLength */
95 0x24, /* bDescriptorType: CS_INTERFACE */
96 0x01, /* bDescriptorSubtype: Call Management Func Desc */
97 0x00, /* bmCapabilities: D0+D1 */
98 0x00, /* bDataInterface: 1 */
99
100 /*ACM Functional Descriptor*/
101 0x04, /* bFunctionLength */
102 0x24, /* bDescriptorType: CS_INTERFACE */
103 0x02, /* bDescriptorSubtype: Abstract Control Management desc */
104 0x02, /* bmCapabilities */
105
106 /* Union Functional Descriptor */
107 0x05, /* bFunctionLength */
108 0x24, /* bDescriptorType: CS_INTERFACE */
109 0x06, /* bDescriptorSubtype: Union func desc */
110 0x00, /* bLeaderIngerface0: Communication class interface */
111 0x00, /* bFollowerInterface0: Data Class Interface */
112 };
113
114 static uint8_t data_if_descriptor[] = {
115 /*Data class interface descriptor*/
116 0x09, /* bLength: Endpoint Descriptor size */
117 INTERFACE, /* bDescriptorType: */
118 0x01, /* bInterfaceNumber: Number of Interface */
119 0x00, /* bAlternateSetting: Alternate setting */
120 0x02, /* bNumEndpoints: Two endpoints used */
121 0x0A, /* bInterfaceClass: CDC */
122 0x00, /* bInterfaceSubClass: */
123 0x00, /* bInterfaceProtocol: */
124 0x00, /* iInterface: */
125
126 /*Endpoint OUT Descriptor*/
127 0x07, /* bLength: Endpoint Descriptor size */
128 ENDPOINT, /* bDescriptorType: Endpoint */
129 0x00, /* bEndpointAddress */
130 0x02, /* bmAttributes: Bulk */
131 0x40, /* wMaxPacketSize: */
132 0x00,
133 0x00, /* bInterval: ignore for Bulk transfer */
134
135 /*Endpoint IN Descriptor*/
136 0x07, /* bLength: Endpoint Descriptor size */
137 ENDPOINT, /* bDescriptorType: Endpoint */
138 0x80, /* bEndpointAddress */
139 0x02, /* bmAttributes: Bulk */
140 0x40, /* wMaxPacketSize: */
141 0x00,
142 0x00 /* bInterval */
143 };
144
145
usb_register_cb(void * cookie,usb_callback_op_t op,const union usb_callback_args * args)146 static status_t usb_register_cb(
147 void *cookie,
148 usb_callback_op_t op,
149 const union usb_callback_args *args
150 )
151 {
152 if (op == USB_CB_ONLINE) {
153 for (int i = 0; i < MAX_USB_ENDPOINT_PAIRS; i++) {
154 if ((0x1 << i) & registered_bulk_eps_in) {
155 usbc_setup_endpoint(i, USB_IN, 0x40, USB_BULK);
156 }
157 if ((0x1 << i) & registered_bulk_eps_out) {
158 usbc_setup_endpoint(i, USB_OUT, 0x40, USB_BULK);
159 }
160 if ((0x1 << i) & registered_intr_eps_in) {
161 usbc_setup_endpoint(i, USB_IN, 0x40, USB_INTR);
162 }
163 if ((0x1 << i) & registered_intr_eps_out) {
164 usbc_setup_endpoint(i, USB_OUT, 0x40, USB_INTR);
165 }
166 }
167
168 usb_online = true;
169 } else if (op == USB_CB_SETUP_MSG) {
170 static uint8_t buf[EP0_MTU];
171 const struct usb_setup *setup = args->setup;
172 // NB: The host might send us some modem commands here. Since we're not a
173 // real modem, we're probably okay to drop them on the floor and reply with
174 // an acknowledgement.
175 if (setup->length) { // Has data phase?
176 if (setup->request_type & 0x80) {
177 // We have to send data?
178 usbc_ep0_send(buf, setup->length, 64);
179 } else {
180 usbc_ep0_recv(buf, setup->length, NULL);
181 usbc_ep0_ack();
182 }
183 } else {
184 switch (setup->request) {
185 case CDC_REQ_SEND_CMD:
186 case CDC_REQ_GET_RESP:
187 case CDC_REQ_SET_COMM_FEATURE:
188 case CDC_REQ_GET_COMM_FEATURE:
189 case CDC_REQ_CLEAR_COMM_FEATURE:
190 case CDC_REQ_SET_LINE_CODING:
191 case CDC_REQ_GET_LINE_CODING:
192 case CDC_REQ_SET_CONTROL_LINE_STATE:
193 case CDC_REQ_SEND_BREAK:
194 // Ack any command that we understand.
195 usbc_ep0_ack();
196 break;
197 }
198 }
199 }
200
201 return NO_ERROR;
202 }
203
cdcserial_init(void)204 status_t cdcserial_init(void)
205 {
206 usb_register_callback(&usb_register_cb, NULL);
207 return NO_ERROR;
208 }
209
cdcserial_create_channel(int data_ep_addr,int ctrl_ep_addr)210 void cdcserial_create_channel(int data_ep_addr, int ctrl_ep_addr)
211 {
212 ctrl_if_descriptor[CTRL_IN_EP_ADDR_OFFSET] = ctrl_ep_addr | 0x80;
213 data_if_descriptor[DATA_IN_EP_ADDR_OFFSET] = data_ep_addr | 0x80;
214 data_if_descriptor[DATA_OUT_EP_ADDR_OFFSET] = data_ep_addr;
215
216 ctrl_if_descriptor[LEADER_EP_NUMBER_OFFSET] =
217 usb_get_current_iface_num_lowspeed();
218 ctrl_if_descriptor[FOLLOWER_EP_NUMBER_OFFSET] =
219 usb_get_current_iface_num_lowspeed() + 1;
220
221 usb_append_interface_lowspeed(ctrl_if_descriptor, sizeof(ctrl_if_descriptor));
222 usb_append_interface_lowspeed(data_if_descriptor, sizeof(data_if_descriptor));
223
224 ctrl_if_descriptor[LEADER_EP_NUMBER_OFFSET] =
225 usb_get_current_iface_num_highspeed();
226 ctrl_if_descriptor[FOLLOWER_EP_NUMBER_OFFSET] =
227 usb_get_current_iface_num_highspeed() + 1;
228
229 usb_append_interface_highspeed(ctrl_if_descriptor, sizeof(ctrl_if_descriptor));
230 usb_append_interface_highspeed(data_if_descriptor, sizeof(data_if_descriptor));
231
232 registered_bulk_eps_in |= (0x1 << data_ep_addr);
233 registered_bulk_eps_out |= (0x1 << data_ep_addr);
234 registered_intr_eps_in |= (0x1 << ctrl_ep_addr);
235 }
236
usb_xmit_cplt_cb(ep_t endpoint,usbc_transfer_t * t)237 static status_t usb_xmit_cplt_cb(ep_t endpoint, usbc_transfer_t *t)
238 {
239 event_signal(&txevt, false);
240 return 0;
241 }
242
usb_recv_cplt_cb(ep_t endpoint,usbc_transfer_t * t)243 static status_t usb_recv_cplt_cb(ep_t endpoint, usbc_transfer_t *t)
244 {
245 event_signal(&rxevt, false);
246 return 0;
247 }
248
249 // Write len bytes to the CDC Serial Virtual Com Port.
cdcserial_write(size_t len,uint8_t * buf)250 status_t cdcserial_write(size_t len, uint8_t *buf)
251 {
252 LTRACEF("len = %d, buf = %p\n", len, buf);
253
254 DEBUG_ASSERT(buf);
255
256 if (!usb_online) {
257 return ERR_NOT_READY;
258 }
259
260 usbc_transfer_t transfer = {
261 .callback = &usb_xmit_cplt_cb,
262 .result = 0,
263 .buf = buf,
264 .buflen = len,
265 .bufpos = 0,
266 .extra = 0,
267 };
268
269 usbc_queue_tx(1, &transfer);
270 event_wait(&txevt);
271
272 return NO_ERROR;
273 }
274
275 // Read at most len bytes from the CDC Serial virtual Com Port. Returns the
276 // actual number of bytes read.
cdcserial_read(size_t len,uint8_t * buf)277 ssize_t cdcserial_read(size_t len, uint8_t *buf)
278 {
279 LTRACEF("len = %d, buf = %p\n", len, buf);
280
281 DEBUG_ASSERT(buf);
282
283 if (!usb_online) {
284 return ERR_NOT_READY;
285 }
286
287 usbc_transfer_t transfer = {
288 .callback = &usb_recv_cplt_cb,
289 .result = 0,
290 .buf = buf,
291 .buflen = len,
292 .bufpos = 0,
293 .extra = 0,
294 };
295
296 usbc_queue_rx(1, &transfer);
297 event_wait(&rxevt);
298
299 return transfer.bufpos;
300 }