summaryrefslogtreecommitdiff
path: root/firmware/usbstack/usb_serial.c
diff options
context:
space:
mode:
authorAidan MacDonald <amachronic@protonmail.com>2021-09-24 22:41:07 +0100
committerAidan MacDonald <amachronic@protonmail.com>2021-10-16 21:14:42 +0100
commit24294bda15fc1c8c5e838e21f0bac5b5419e5cd2 (patch)
tree633f03121a640f7645a2de6966adc6336e95aeee /firmware/usbstack/usb_serial.c
parenta665c1faada95e95a51072bfb78d3157ccb5fe85 (diff)
downloadrockbox-24294bda15fc1c8c5e838e21f0bac5b5419e5cd2.tar.gz
rockbox-24294bda15fc1c8c5e838e21f0bac5b5419e5cd2.zip
usb: ensure RX buffers are a multiple of the packet size
When performing an OUT transfer which is not a multiple of the max packet size, the last packet of the OUT transfer should be a short packet. However, there's no guarantee the host sends the expected amount of data in the final packet. The DWC2 USB controller handles this case by accepting any size packet and copying it out to memory. So if the packet is bigger than expected, it'll overrun the caller's buffer and Bad Things will happen. The USB 2.0 spec seems to endorse this behavior. Section 8.5.1 says "an ACK handshake indicates the endpoint has space for a wMaxPacketSize data payload." So it is possible that other USB controllers share the DWC2's behavior. The simplest solution is to force all USB RX buffers to be big enough to hold the transfer size, rounded up to a multiple of the max packet size. For example, a transfer of 700 bytes would require a 1024-byte buffer if the MPS = 512 bytes. Change-Id: Ibb84d2b2d53aec8800a3a7c2449f7a17480acbcf
Diffstat (limited to 'firmware/usbstack/usb_serial.c')
-rw-r--r--firmware/usbstack/usb_serial.c32
1 files changed, 23 insertions, 9 deletions
diff --git a/firmware/usbstack/usb_serial.c b/firmware/usbstack/usb_serial.c
index b49a5ca013..ae90b57078 100644
--- a/firmware/usbstack/usb_serial.c
+++ b/firmware/usbstack/usb_serial.c
@@ -174,7 +174,13 @@ static struct usb_endpoint_descriptor
174 .bInterval = 0 174 .bInterval = 0
175}; 175};
176 176
177static struct cdc_line_coding line_coding; 177union line_coding_buffer
178{
179 struct cdc_line_coding data;
180 unsigned char raw[64];
181};
182
183static union line_coding_buffer line_coding USB_DEVBSS_ATTR;
178 184
179/* send_buffer: local ring buffer. 185/* send_buffer: local ring buffer.
180 * transit_buffer: used to store aligned data that will be sent by the USB 186 * transit_buffer: used to store aligned data that will be sent by the USB
@@ -184,10 +190,11 @@ static struct cdc_line_coding line_coding;
184 */ 190 */
185#define BUFFER_SIZE 512 191#define BUFFER_SIZE 512
186#define TRANSIT_BUFFER_SIZE 32 192#define TRANSIT_BUFFER_SIZE 32
193#define RECV_BUFFER_SIZE 32
187static unsigned char send_buffer[BUFFER_SIZE]; 194static unsigned char send_buffer[BUFFER_SIZE];
188static unsigned char transit_buffer[TRANSIT_BUFFER_SIZE] 195static unsigned char transit_buffer[TRANSIT_BUFFER_SIZE]
189 USB_DEVBSS_ATTR __attribute__((aligned(4))); 196 USB_DEVBSS_ATTR __attribute__((aligned(4)));
190static unsigned char receive_buffer[32] 197static unsigned char receive_buffer[512]
191 USB_DEVBSS_ATTR __attribute__((aligned(32))); 198 USB_DEVBSS_ATTR __attribute__((aligned(32)));
192 199
193static void sendout(void); 200static void sendout(void);
@@ -293,13 +300,19 @@ bool usb_serial_control_request(struct usb_ctrlrequest* req, void* reqdata, unsi
293 { 300 {
294 if (req->bRequest == SET_LINE_CODING) 301 if (req->bRequest == SET_LINE_CODING)
295 { 302 {
296 if (req->wLength == sizeof(line_coding)) 303 if (req->wLength == sizeof(struct cdc_line_coding))
297 { 304 {
298 /* Receive line coding into local copy */ 305 /* Receive line coding into local copy */
299 if(!reqdata) 306 if (!reqdata)
300 usb_drv_control_response(USB_CONTROL_RECEIVE, &line_coding, sizeof(line_coding)); 307 {
308 usb_drv_control_response(USB_CONTROL_RECEIVE, line_coding.raw,
309 sizeof(struct cdc_line_coding));
310 }
301 else 311 else
312 {
302 usb_drv_control_response(USB_CONTROL_ACK, NULL, 0); 313 usb_drv_control_response(USB_CONTROL_ACK, NULL, 0);
314 }
315
303 handled = true; 316 handled = true;
304 } 317 }
305 } 318 }
@@ -317,10 +330,11 @@ bool usb_serial_control_request(struct usb_ctrlrequest* req, void* reqdata, unsi
317 { 330 {
318 if (req->bRequest == GET_LINE_CODING) 331 if (req->bRequest == GET_LINE_CODING)
319 { 332 {
320 if (req->wLength == sizeof(line_coding)) 333 if (req->wLength == sizeof(struct cdc_line_coding))
321 { 334 {
322 /* Send back line coding so host is happy */ 335 /* Send back line coding so host is happy */
323 usb_drv_control_response(USB_CONTROL_ACK, &line_coding, sizeof(line_coding)); 336 usb_drv_control_response(USB_CONTROL_ACK, line_coding.raw,
337 sizeof(struct cdc_line_coding));
324 handled = true; 338 handled = true;
325 } 339 }
326 } 340 }
@@ -332,7 +346,7 @@ bool usb_serial_control_request(struct usb_ctrlrequest* req, void* reqdata, unsi
332void usb_serial_init_connection(void) 346void usb_serial_init_connection(void)
333{ 347{
334 /* prime rx endpoint */ 348 /* prime rx endpoint */
335 usb_drv_recv_nonblocking(ep_out, receive_buffer, sizeof receive_buffer); 349 usb_drv_recv_nonblocking(ep_out, receive_buffer, RECV_BUFFER_SIZE);
336 350
337 /* we come here too after a bus reset, so reset some data */ 351 /* we come here too after a bus reset, so reset some data */
338 buffer_transitlength = 0; 352 buffer_transitlength = 0;
@@ -423,7 +437,7 @@ void usb_serial_transfer_complete(int ep,int dir, int status, int length)
423 /* Data received. TODO : Do something with it ? */ 437 /* Data received. TODO : Do something with it ? */
424 438
425 /* Get the next bit */ 439 /* Get the next bit */
426 usb_drv_recv_nonblocking(ep_out, receive_buffer, sizeof receive_buffer); 440 usb_drv_recv_nonblocking(ep_out, receive_buffer, RECV_BUFFER_SIZE);
427 break; 441 break;
428 442
429 case USB_DIR_IN: 443 case USB_DIR_IN: