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 }