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