> 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]>


Reply via email to