Commit 1b4b29a1 authored by Gerd Hoffmann's avatar Gerd Hoffmann
Browse files

usb: add shortcut for control transfers



Add a more direct code path to submit control transfers.  Instead of
feeding three usb packets (setup, data, ack) to usb_handle_packet and
have the do_token_* functions in usb.c poke the control transfer
parameters out of it just submit a single packet carrying the actual
data with the control xfer parameters filled into USBPacket->parameters.

Signed-off-by: default avatarGerd Hoffmann <kraxel@redhat.com>
parent 9424d4e7
Loading
Loading
Loading
Loading
+59 −0
Original line number Diff line number Diff line
@@ -95,6 +95,7 @@ void usb_wakeup(USBEndpoint *ep)
#define SETUP_STATE_SETUP 1
#define SETUP_STATE_DATA  2
#define SETUP_STATE_ACK   3
#define SETUP_STATE_PARAM 4

static int do_token_setup(USBDevice *s, USBPacket *p)
{
@@ -226,6 +227,50 @@ static int do_token_out(USBDevice *s, USBPacket *p)
    }
}

static int do_parameter(USBDevice *s, USBPacket *p)
{
    int request, value, index;
    int i, ret = 0;

    for (i = 0; i < 8; i++) {
        s->setup_buf[i] = p->parameter >> (i*8);
    }

    s->setup_state = SETUP_STATE_PARAM;
    s->setup_len   = (s->setup_buf[7] << 8) | s->setup_buf[6];
    s->setup_index = 0;

    request = (s->setup_buf[0] << 8) | s->setup_buf[1];
    value   = (s->setup_buf[3] << 8) | s->setup_buf[2];
    index   = (s->setup_buf[5] << 8) | s->setup_buf[4];

    if (s->setup_len > sizeof(s->data_buf)) {
        fprintf(stderr,
                "usb_generic_handle_packet: ctrl buffer too small (%d > %zu)\n",
                s->setup_len, sizeof(s->data_buf));
        return USB_RET_STALL;
    }

    if (p->pid == USB_TOKEN_OUT) {
        usb_packet_copy(p, s->data_buf, s->setup_len);
    }

    ret = usb_device_handle_control(s, p, request, value, index,
                                    s->setup_len, s->data_buf);
    if (ret < 0) {
        return ret;
    }

    if (ret < s->setup_len) {
        s->setup_len = ret;
    }
    if (p->pid == USB_TOKEN_IN) {
        usb_packet_copy(p, s->data_buf, s->setup_len);
    }

    return ret;
}

/* ctrl complete function for devices which use usb_generic_handle_packet and
   may return USB_RET_ASYNC from their handle_control callback. Device code
   which does this *must* call this function instead of the normal
@@ -250,6 +295,16 @@ void usb_generic_async_ctrl_complete(USBDevice *s, USBPacket *p)
        p->result = 0;
        break;

    case SETUP_STATE_PARAM:
        if (p->result < s->setup_len) {
            s->setup_len = p->result;
        }
        if (p->pid == USB_TOKEN_IN) {
            p->result = 0;
            usb_packet_copy(p, s->data_buf, s->setup_len);
        }
        break;

    default:
        break;
    }
@@ -292,6 +347,9 @@ static int usb_process_one(USBPacket *p)

    if (p->ep->nr == 0) {
        /* control pipe */
        if (p->parameter) {
            return do_parameter(dev, p);
        }
        switch (p->pid) {
        case USB_TOKEN_SETUP:
            return do_token_setup(dev, p);
@@ -416,6 +474,7 @@ void usb_packet_setup(USBPacket *p, int pid, USBEndpoint *ep)
    p->pid = pid;
    p->ep = ep;
    p->result = 0;
    p->parameter = 0;
    qemu_iovec_reset(&p->iov);
    usb_packet_set_state(p, USB_PACKET_SETUP);
}
+1 −0
Original line number Diff line number Diff line
@@ -327,6 +327,7 @@ struct USBPacket {
    int pid;
    USBEndpoint *ep;
    QEMUIOVector iov;
    uint64_t parameter; /* control transfers */
    int result; /* transfer length or USB_RET_* status code */
    /* Internal use by the USB layer.  */
    USBPacketState state;