diff options
Diffstat (limited to 'firmware/usbstack')
-rw-r--r-- | firmware/usbstack/usb_serial.c | 222 |
1 files changed, 207 insertions, 15 deletions
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 |
32 | static 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 | |||
50 | struct cdc_header_descriptor { | ||
51 | uint8_t bFunctionLength; | ||
52 | uint8_t bDescriptorType; | ||
53 | uint8_t bDescriptorSubtype; | ||
54 | uint16_t bcdCDC; | ||
55 | } __attribute__((packed)); | ||
56 | |||
57 | struct 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 | |||
65 | struct cdc_acm_descriptor { | ||
66 | uint8_t bFunctionLength; | ||
67 | uint8_t bDescriptorType; | ||
68 | uint8_t bDescriptorSubtype; | ||
69 | uint8_t bmCapabilities; | ||
70 | } __attribute__((packed)); | ||
71 | |||
72 | struct 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 | |||
80 | struct cdc_line_coding { | ||
81 | uint32_t dwDTERate; | ||
82 | uint8_t bCharFormat; | ||
83 | uint8_t bParityType; | ||
84 | uint8_t bDataBits; | ||
85 | } __attribute__((packed)); | ||
86 | |||
87 | static 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 | |||
100 | static 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 | |||
114 | static 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 | |||
123 | static 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 | |||
133 | static 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 | |||
142 | static 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 | |||
152 | static 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 | 166 | static struct usb_endpoint_descriptor | |
47 | static 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 | ||
177 | static 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; | |||
78 | static int buffer_transitlength; | 199 | static int buffer_transitlength; |
79 | static bool active = false; | 200 | static bool active = false; |
80 | 201 | ||
81 | static int ep_in, ep_out; | 202 | static int ep_in, ep_out, ep_int; |
82 | static int usb_interface; | 203 | static int control_interface, data_interface; |
83 | 204 | ||
84 | int usb_serial_request_endpoints(struct usb_class_driver *drv) | 205 | int 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 | ||
100 | int usb_serial_set_first_interface(int interface) | 228 | int 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 | ||
106 | int usb_serial_get_config_descriptor(unsigned char *dest, int max_packet_size) | 235 | int 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 | ||