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