summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--firmware/export/usb_ch9.h17
-rw-r--r--firmware/usbstack/usb_serial.c222
2 files changed, 224 insertions, 15 deletions
diff --git a/firmware/export/usb_ch9.h b/firmware/export/usb_ch9.h
index c11775b893..659bcca101 100644
--- a/firmware/export/usb_ch9.h
+++ b/firmware/export/usb_ch9.h
@@ -391,6 +391,23 @@ struct usb_debug_descriptor {
391} __attribute__((packed)); 391} __attribute__((packed));
392 392
393/*-------------------------------------------------------------------------*/ 393/*-------------------------------------------------------------------------*/
394
395/* USB_DT_INTERFACE_ASSOCIATION: groups interfaces */
396struct usb_interface_assoc_descriptor {
397 uint8_t bLength;
398 uint8_t bDescriptorType;
399
400 uint8_t bFirstInterface;
401 uint8_t bInterfaceCount;
402 uint8_t bFunctionClass;
403 uint8_t bFunctionSubClass;
404 uint8_t bFunctionProtocol;
405 uint8_t iFunction;
406} __attribute__ ((packed));
407
408#define USB_DT_INTERFACE_ASSOCIATION_SIZE 8
409
410/*-------------------------------------------------------------------------*/
394/* USB 2.0 defines three speeds, here's how Linux identifies them */ 411/* USB 2.0 defines three speeds, here's how Linux identifies them */
395 412
396enum usb_device_speed { 413enum usb_device_speed {
diff --git a/firmware/usbstack/usb_serial.c b/firmware/usbstack/usb_serial.c
index d879dc7c99..a08394c0a8 100644
--- a/firmware/usbstack/usb_serial.c
+++ b/firmware/usbstack/usb_serial.c
@@ -8,6 +8,7 @@
8 * $Id$ 8 * $Id$
9 * 9 *
10 * Copyright (C) 2007 by Christian Gmeiner 10 * Copyright (C) 2007 by Christian Gmeiner
11 * Copyright (C) 2021 by Tomasz Moń
11 * 12 *
12 * This program is free software; you can redistribute it and/or 13 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License 14 * modify it under the terms of the GNU General Public License
@@ -28,9 +29,128 @@
28/*#define LOGF_ENABLE*/ 29/*#define LOGF_ENABLE*/
29#include "logf.h" 30#include "logf.h"
30 31
31/* serial interface */ 32#define CDC_SUBCLASS_ACM 0x02
32static struct usb_interface_descriptor __attribute__((aligned(2))) 33#define CDC_PROTOCOL_NONE 0x00
33 interface_descriptor = 34
35/* Class-Specific Request Codes */
36#define SET_LINE_CODING 0x20
37#define GET_LINE_CODING 0x21
38#define SET_CONTROL_LINE_STATE 0x22
39
40#define SUBTYPE_HEADER 0x00
41#define SUBTYPE_CALL_MANAGEMENT 0x01
42#define SUBTYPE_ACM 0x02
43#define SUBTYPE_UNION 0x06
44
45/* Support SET_LINE_CODING, GET_LINE_CODING, SET_CONTROL_LINE_STATE requests
46 * and SERIAL_STATE notification.
47 */
48#define ACM_CAP_LINE_CODING 0x02
49
50struct cdc_header_descriptor {
51 uint8_t bFunctionLength;
52 uint8_t bDescriptorType;
53 uint8_t bDescriptorSubtype;
54 uint16_t bcdCDC;
55} __attribute__((packed));
56
57struct cdc_call_management_descriptor {
58 uint8_t bFunctionLength;
59 uint8_t bDescriptorType;
60 uint8_t bDescriptorSubtype;
61 uint8_t bmCapabilities;
62 uint8_t bDataInterface;
63} __attribute__((packed));
64
65struct cdc_acm_descriptor {
66 uint8_t bFunctionLength;
67 uint8_t bDescriptorType;
68 uint8_t bDescriptorSubtype;
69 uint8_t bmCapabilities;
70} __attribute__((packed));
71
72struct cdc_union_descriptor {
73 uint8_t bFunctionLength;
74 uint8_t bDescriptorType;
75 uint8_t bDescriptorSubtype;
76 uint8_t bControlInterface;
77 uint8_t bSubordinateInterface0;
78} __attribute__((packed));
79
80struct cdc_line_coding {
81 uint32_t dwDTERate;
82 uint8_t bCharFormat;
83 uint8_t bParityType;
84 uint8_t bDataBits;
85} __attribute__((packed));
86
87static struct usb_interface_assoc_descriptor
88 association_descriptor =
89{
90 .bLength = sizeof(struct usb_interface_assoc_descriptor),
91 .bDescriptorType = USB_DT_INTERFACE_ASSOCIATION,
92 .bFirstInterface = 0,
93 .bInterfaceCount = 2,
94 .bFunctionClass = USB_CLASS_COMM,
95 .bFunctionSubClass = CDC_SUBCLASS_ACM,
96 .bFunctionProtocol = CDC_PROTOCOL_NONE,
97 .iFunction = 0
98};
99
100static struct usb_interface_descriptor
101 control_interface_descriptor =
102{
103 .bLength = sizeof(struct usb_interface_descriptor),
104 .bDescriptorType = USB_DT_INTERFACE,
105 .bInterfaceNumber = 0,
106 .bAlternateSetting = 0,
107 .bNumEndpoints = 1,
108 .bInterfaceClass = USB_CLASS_COMM,
109 .bInterfaceSubClass = CDC_SUBCLASS_ACM,
110 .bInterfaceProtocol = CDC_PROTOCOL_NONE,
111 .iInterface = 0
112};
113
114static struct cdc_header_descriptor
115 header_descriptor =
116{
117 .bFunctionLength = sizeof(struct cdc_header_descriptor),
118 .bDescriptorType = USB_DT_CS_INTERFACE,
119 .bDescriptorSubtype = SUBTYPE_HEADER,
120 .bcdCDC = 0x0110
121};
122
123static struct cdc_call_management_descriptor
124 call_management_descriptor =
125{
126 .bFunctionLength = sizeof(struct cdc_call_management_descriptor),
127 .bDescriptorType = USB_DT_CS_INTERFACE,
128 .bDescriptorSubtype = SUBTYPE_CALL_MANAGEMENT,
129 .bmCapabilities = 0,
130 .bDataInterface = 0
131};
132
133static struct cdc_acm_descriptor
134 acm_descriptor =
135{
136 .bFunctionLength = sizeof(struct cdc_acm_descriptor),
137 .bDescriptorType = USB_DT_CS_INTERFACE,
138 .bDescriptorSubtype = SUBTYPE_ACM,
139 .bmCapabilities = ACM_CAP_LINE_CODING
140};
141
142static struct cdc_union_descriptor
143 union_descriptor =
144{
145 .bFunctionLength = sizeof(struct cdc_union_descriptor),
146 .bDescriptorType = USB_DT_CS_INTERFACE,
147 .bDescriptorSubtype = SUBTYPE_UNION,
148 .bControlInterface = 0,
149 .bSubordinateInterface0 = 0
150};
151
152static struct usb_interface_descriptor
153 data_interface_descriptor =
34{ 154{
35 .bLength = sizeof(struct usb_interface_descriptor), 155 .bLength = sizeof(struct usb_interface_descriptor),
36 .bDescriptorType = USB_DT_INTERFACE, 156 .bDescriptorType = USB_DT_INTERFACE,
@@ -43,8 +163,7 @@ static struct usb_interface_descriptor __attribute__((aligned(2)))
43 .iInterface = 0 163 .iInterface = 0
44}; 164};
45 165
46 166static struct usb_endpoint_descriptor
47static struct usb_endpoint_descriptor __attribute__((aligned(2)))
48 endpoint_descriptor = 167 endpoint_descriptor =
49{ 168{
50 .bLength = sizeof(struct usb_endpoint_descriptor), 169 .bLength = sizeof(struct usb_endpoint_descriptor),
@@ -55,6 +174,8 @@ static struct usb_endpoint_descriptor __attribute__((aligned(2)))
55 .bInterval = 0 174 .bInterval = 0
56}; 175};
57 176
177static struct cdc_line_coding line_coding;
178
58/* send_buffer: local ring buffer. 179/* send_buffer: local ring buffer.
59 * transit_buffer: used to store aligned data that will be sent by the USB 180 * transit_buffer: used to store aligned data that will be sent by the USB
60 * driver. PP502x needs boost for high speed USB, but still works up to 181 * driver. PP502x needs boost for high speed USB, but still works up to
@@ -78,8 +199,8 @@ static int buffer_length;
78static int buffer_transitlength; 199static int buffer_transitlength;
79static bool active = false; 200static bool active = false;
80 201
81static int ep_in, ep_out; 202static int ep_in, ep_out, ep_int;
82static int usb_interface; 203static int control_interface, data_interface;
83 204
84int usb_serial_request_endpoints(struct usb_class_driver *drv) 205int usb_serial_request_endpoints(struct usb_class_driver *drv)
85{ 206{
@@ -94,25 +215,59 @@ int usb_serial_request_endpoints(struct usb_class_driver *drv)
94 return -1; 215 return -1;
95 } 216 }
96 217
218 /* Optional interrupt endpoint. While the code does not actively use it,
219 * it is needed to get out-of-the-box serial port experience on Windows
220 * and Linux. If this endpoint is not available, only CDC Data interface
221 * will be exported (can still work on Linux with manual modprobe).
222 */
223 ep_int = usb_core_request_endpoint(USB_ENDPOINT_XFER_INT, USB_DIR_IN, drv);
224
97 return 0; 225 return 0;
98} 226}
99 227
100int usb_serial_set_first_interface(int interface) 228int usb_serial_set_first_interface(int interface)
101{ 229{
102 usb_interface = interface; 230 control_interface = interface;
103 return interface + 1; 231 data_interface = interface + 1;
232 return interface + 2;
104} 233}
105 234
106int usb_serial_get_config_descriptor(unsigned char *dest, int max_packet_size) 235int usb_serial_get_config_descriptor(unsigned char *dest, int max_packet_size)
107{ 236{
108 unsigned char *orig_dest = dest; 237 unsigned char *orig_dest = dest;
109 238
110 interface_descriptor.bInterfaceNumber = usb_interface; 239 association_descriptor.bFirstInterface = control_interface;
111 PACK_DATA(&dest, interface_descriptor); 240 control_interface_descriptor.bInterfaceNumber = control_interface;
241 call_management_descriptor.bDataInterface = data_interface;
242 union_descriptor.bControlInterface = control_interface;
243 union_descriptor.bSubordinateInterface0 = data_interface;
244 data_interface_descriptor.bInterfaceNumber = data_interface;
112 245
113 endpoint_descriptor.wMaxPacketSize = max_packet_size; 246 if (ep_int > 0)
247 {
248 PACK_DATA(&dest, association_descriptor);
249 PACK_DATA(&dest, control_interface_descriptor);
250 PACK_DATA(&dest, header_descriptor);
251 PACK_DATA(&dest, call_management_descriptor);
252 PACK_DATA(&dest, acm_descriptor);
253 PACK_DATA(&dest, union_descriptor);
254
255 /* Notification endpoint. Set wMaxPacketSize to 64 as it is valid
256 * both on Full and High speed. Note that max_packet_size is for bulk.
257 * Maximum bInterval for High Speed is 16 and for Full Speed is 255.
258 */
259 endpoint_descriptor.bEndpointAddress = ep_int;
260 endpoint_descriptor.bmAttributes = USB_ENDPOINT_XFER_INT;
261 endpoint_descriptor.wMaxPacketSize = 64;
262 endpoint_descriptor.bInterval = 16;
263 PACK_DATA(&dest, endpoint_descriptor);
264 }
114 265
266 PACK_DATA(&dest, data_interface_descriptor);
115 endpoint_descriptor.bEndpointAddress = ep_in; 267 endpoint_descriptor.bEndpointAddress = ep_in;
268 endpoint_descriptor.bmAttributes = USB_ENDPOINT_XFER_BULK;
269 endpoint_descriptor.wMaxPacketSize = max_packet_size;
270 endpoint_descriptor.bInterval = 0;
116 PACK_DATA(&dest, endpoint_descriptor); 271 PACK_DATA(&dest, endpoint_descriptor);
117 272
118 endpoint_descriptor.bEndpointAddress = ep_out; 273 endpoint_descriptor.bEndpointAddress = ep_out;
@@ -127,10 +282,47 @@ bool usb_serial_control_request(struct usb_ctrlrequest* req, unsigned char* dest
127 bool handled = false; 282 bool handled = false;
128 283
129 (void)dest; 284 (void)dest;
130 switch (req->bRequest) { 285 if (req->wIndex != control_interface)
131 default: 286 {
132 logf("serial: unhandeld req %d", req->bRequest); 287 return false;
133 } 288 }
289
290 if (req->bRequestType == (USB_DIR_OUT|USB_TYPE_CLASS|USB_RECIP_INTERFACE))
291 {
292 if (req->bRequest == SET_LINE_CODING)
293 {
294 if (req->wLength == sizeof(line_coding))
295 {
296 /* Receive line coding into local copy */
297 usb_drv_recv(EP_CONTROL, &line_coding, sizeof(line_coding));
298 usb_drv_send(EP_CONTROL, NULL, 0); /* ack */
299 handled = true;
300 }
301 }
302 else if (req->bRequest == SET_CONTROL_LINE_STATE)
303 {
304 if (req->wLength == 0)
305 {
306 /* wValue holds Control Signal Bitmap that is simply ignored here */
307 usb_drv_send(EP_CONTROL, NULL, 0); /* ack */
308 handled = true;
309 }
310 }
311 }
312 else if (req->bRequestType == (USB_DIR_IN|USB_TYPE_CLASS|USB_RECIP_INTERFACE))
313 {
314 if (req->bRequest == GET_LINE_CODING)
315 {
316 if (req->wLength == sizeof(line_coding))
317 {
318 /* Send back line coding so host is happy */
319 usb_drv_recv(EP_CONTROL, NULL, 0); /* ack */
320 usb_drv_send(EP_CONTROL, &line_coding, sizeof(line_coding));
321 handled = true;
322 }
323 }
324 }
325
134 return handled; 326 return handled;
135} 327}
136 328