> Send bulk and interrupt packets to the device and return the result to > the host. Each one is a request and an answer, so they reuse the packet > tracking added for control transfers. > > A bulk request carries a 32 bit length, so the host can ask us to > allocate up to 4 GB. Refuse a request that big. Do not answer it with > fewer bytes instead: the host counts the bytes it gets back, and it > resets the device when some are missing. > > Signed-off-by: Jamin Lin <[email protected]> > Reviewed-by: Marc-André Lureau <[email protected]>
Can you drop my reviewed-by, we are undergoing still significant changes. Thanks > Message-ID: <[email protected]> > > diff --git a/hw/usb/redirect-server.c b/hw/usb/redirect-server.c > index 29a9fbc94178..f81dbae2d0b2 100644 > --- a/hw/usb/redirect-server.c > +++ b/hw/usb/redirect-server.c > @@ -82,8 +82,16 @@ OBJECT_DECLARE_SIMPLE_TYPE(USBRedirServer, > USB_REDIR_SERVER) > #define USBREDIR_SERVER_MAX_EP 32 > #define USBREDIR_SERVER_EP_IN_BASE 16 > > +/* > + * The bulk length field is 32 bits, so the host can ask for up to 4 GB. > + * This is the largest transfer accepted. > + */ > +#define USBREDIR_SERVER_MAX_BULK (1 * MiB) > + > #define USBREDIR_SERVER_CTRL_SETUP 0 > #define USBREDIR_SERVER_CTRL_STATUS 1 > +#define USBREDIR_SERVER_BULK 2 > +#define USBREDIR_SERVER_INTR 3 > > /* Which message answers the host when a control transfer ends. */ > typedef enum { > @@ -96,7 +104,9 @@ typedef struct USBRedirServerPkt { > USBPacket pkt; > > /* Saved headers for the usbredir response */ > + struct usb_redir_interrupt_packet_header intr_hdr; > struct usb_redir_control_packet_header ctrl_hdr; > + struct usb_redir_bulk_packet_header bulk_hdr; > > /* > * The IOV points here. IN data lands in it, OUT data is copied in. > @@ -524,6 +534,52 @@ static void > usbredir_server_ctrl_status_complete(USBRedirServer *s, > usbredir_server_send_ep_info(s); > } > > +static void usbredir_server_bulk_complete(USBRedirServer *s, > + USBRedirServerPkt *rp) > +{ > + struct usb_redir_bulk_packet_header resp = rp->bulk_hdr; > + bool is_in = !!(rp->bulk_hdr.endpoint & USB_DIR_IN); > + int actual = rp->pkt.actual_length; > + int len; > + > + resp.status = usbredir_server_status(rp->pkt.status); A bulk IN that the device answers with USB_RET_NAK ends up here, since submit_to_device() completes everything that is not USB_RET_ASYNC, and usbredir_server_status() turns it into usb_redir_ioerror. usbredirhost, the libusb backend, keeps the transfer pending until data arrives. The server should do the same here: on NAK keep the packet on the inflight list and submit it again from wakeup_endpoint, like the interrupt stream in patch 7 does. > + > + /* Only an IN transfer carries data back. */ > + len = is_in ? actual : 0; > + resp.length = len; > + resp.length_high = len >> 16; > + > + trace_usbredir_server_bulk_complete(rp->redir_id, resp.endpoint, > + resp.status, actual); > + > + usbredirparser_send_bulk_packet(s->parser, rp->redir_id, &resp, > + len > 0 ? rp->data : NULL, len); > + usbredirparser_do_write(s->parser); > +} > + > +static void usbredir_server_intr_complete(USBRedirServer *s, > + USBRedirServerPkt *rp) > +{ > + struct usb_redir_interrupt_packet_header resp = rp->intr_hdr; > + bool is_in = !!(rp->intr_hdr.endpoint & USB_DIR_IN); > + int actual = rp->pkt.actual_length; > + int len; > + > + resp.status = usbredir_server_status(rp->pkt.status); > + > + /* Only an IN transfer carries data back. */ > + len = is_in ? actual : 0; > + resp.length = len; > + > + trace_usbredir_server_intr_complete(rp->redir_id, resp.endpoint, > + resp.status, actual); > + > + usbredirparser_send_interrupt_packet(s->parser, rp->redir_id, &resp, > + len > 0 ? rp->data : NULL, > + len); > + usbredirparser_do_write(s->parser); > +} > + > /* > * USB port ops > */ > @@ -600,6 +656,12 @@ static void usbredir_server_packet_complete(USBPort > *port, USBPacket *p) > case USBREDIR_SERVER_CTRL_STATUS: > usbredir_server_ctrl_status_complete(s, rp); > break; > + case USBREDIR_SERVER_BULK: > + usbredir_server_bulk_complete(s, rp); > + break; > + case USBREDIR_SERVER_INTR: > + usbredir_server_intr_complete(s, rp); > + break; > } > > usbredir_server_pkt_free(rp); > @@ -979,6 +1041,110 @@ static void usbredir_server_get_alt_setting(void > *priv, uint64_t id, > USBREDIR_SERVER_REPLY_ALT); > } > > +static void usbredir_server_bulk_packet(void *priv, uint64_t id, > + struct usb_redir_bulk_packet_header *hdr, > + uint8_t *data, int data_len) > +{ > + USBRedirServer *s = priv; > + USBDevice *device = usbredir_server_device(s); > + bool is_in = !!(hdr->endpoint & USB_DIR_IN); > + int pid = is_in ? USB_TOKEN_IN : USB_TOKEN_OUT; > + struct usb_redir_bulk_packet_header resp; > + int ep_nr = hdr->endpoint & 0x0f; > + USBRedirServerPkt *rp; > + USBEndpoint *ep; > + uint32_t len; > + > + if (!s->host_connected || !device || !device->attached) { > + usbredirparser_free_packet_data(s->parser, data); > + return; > + } > + > + /* IN: what the host asked for. OUT: what the host sent. */ > + len = is_in ? (((uint32_t)hdr->length_high << 16) | hdr->length) > + : (uint32_t)data_len; > + > + /* > + * Too big. Tell the host the transfer failed. > + * Do not send back less data instead. The host would see the > + * missing bytes as an error and reset the device. > + */ > + if (len > USBREDIR_SERVER_MAX_BULK) { > + resp = *hdr; > + resp.status = usb_redir_inval; > + resp.length = 0; > + resp.length_high = 0; > + > + trace_usbredir_server_bulk_too_big(id, hdr->endpoint, len); > + usbredirparser_send_bulk_packet(s->parser, id, &resp, NULL, 0); > + usbredirparser_do_write(s->parser); > + usbredirparser_free_packet_data(s->parser, data); > + return; > + } > + > + ep = usb_ep_get(device, pid, ep_nr); Nothing rejects ep_nr == 0. usb_ep_get() then returns the control endpoint and usb_process_one() runs do_token_in()/do_token_out() on it, mixing a "bulk" packet into ep0's setup state machine. Same for usbredir_server_interrupt_packet() below. Answer usb_redir_inval for endpoint 0, as start_interrupt_receiving does in patch 7? -- Marc-André Lureau <[email protected]>
