summaryrefslogtreecommitdiff
path: root/firmware/usbstack
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
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')
-rw-r--r--firmware/usbstack/usb_hid.c3
-rw-r--r--firmware/usbstack/usb_serial.c32
-rw-r--r--firmware/usbstack/usb_storage.c2
3 files changed, 25 insertions, 12 deletions
diff --git a/firmware/usbstack/usb_hid.c b/firmware/usbstack/usb_hid.c
index 121736d2dd..64aa123ced 100644
--- a/firmware/usbstack/usb_hid.c
+++ b/firmware/usbstack/usb_hid.c
@@ -666,8 +666,7 @@ void usb_hid_transfer_complete(int ep, int dir, int status, int length)
666 * to the DAP using the host's custom driver */ 666 * to the DAP using the host's custom driver */
667static int usb_hid_set_report(struct usb_ctrlrequest *req, void *reqdata) 667static int usb_hid_set_report(struct usb_ctrlrequest *req, void *reqdata)
668{ 668{
669 static unsigned char buf[SET_REPORT_BUF_LEN] USB_DEVBSS_ATTR 669 static unsigned char buf[64] USB_DEVBSS_ATTR __attribute__((aligned(32)));
670 __attribute__((aligned(32)));
671 int length; 670 int length;
672 671
673 if ((req->wValue >> 8) != REPORT_TYPE_OUTPUT) 672 if ((req->wValue >> 8) != REPORT_TYPE_OUTPUT)
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:
diff --git a/firmware/usbstack/usb_storage.c b/firmware/usbstack/usb_storage.c
index 6d79be06ca..a32cf185e7 100644
--- a/firmware/usbstack/usb_storage.c
+++ b/firmware/usbstack/usb_storage.c
@@ -71,7 +71,7 @@
71#endif /* USB_READ_BUFFER_SIZE */ 71#endif /* USB_READ_BUFFER_SIZE */
72 72
73/* We don't use sizeof() here, because we *need* a multiple of 32 */ 73/* We don't use sizeof() here, because we *need* a multiple of 32 */
74#define MAX_CBW_SIZE 32 74#define MAX_CBW_SIZE 512
75 75
76#ifdef USB_WRITE_BUFFER_SIZE 76#ifdef USB_WRITE_BUFFER_SIZE
77#define WRITE_BUFFER_SIZE USB_WRITE_BUFFER_SIZE 77#define WRITE_BUFFER_SIZE USB_WRITE_BUFFER_SIZE