diff options
author | Anthony Liguori <aliguori@us.ibm.com> | 2012-03-09 12:29:40 -0600 |
---|---|---|
committer | Anthony Liguori <aliguori@us.ibm.com> | 2012-03-09 12:29:40 -0600 |
commit | 9f1d43b133d6bac0539f0c99497fe59d561a02ea (patch) | |
tree | 79091812779a75d7080d8801a4a1a4603012ece6 /hw/usb.c | |
parent | 0202181245297a9e847c05f4a18623219d95e93e (diff) | |
parent | cf21a4aef712075d313da4ea924a3376574c16e5 (diff) | |
download | qemu-9f1d43b133d6bac0539f0c99497fe59d561a02ea.zip qemu-9f1d43b133d6bac0539f0c99497fe59d561a02ea.tar.gz qemu-9f1d43b133d6bac0539f0c99497fe59d561a02ea.tar.bz2 |
Merge remote-tracking branch 'kraxel/usb.42' into staging
* kraxel/usb.42:
xhci: fix port status
xhci: fix control xfers
usb: add shortcut for control transfers
usb-host: enable pipelineing for bulk endpoints.
usb: add pipelining option to usb endpoints
usb: queue can have async packets
uhci_fill_queue: zap debug printf
usb: add USB_RET_IOERROR
usb: return BABBLE rather then NAK when we receive too much data
usb-ehci: Cleanup itd error handling
usb-ehci: Fix and simplify nakcnt handling
usb-ehci: Remove dead nakcnt code
usb-ehci: Fix cerr tracking
usb-ehci: Any packet completion except for NAK should set the interrupt
usb-ehci: Rip the queues when the async or period schedule is halted
usb-ehci: Drop cached qhs when the doorbell gets rung
usb-ehci: always call ehci_queues_rip_unused for period queues
usb-ehci: split our qh queue into async and periodic queues
usb-ehci: Never follow table entries with the T-bit set
usb-redir: Set ep type and interface
Diffstat (limited to 'hw/usb.c')
-rw-r--r-- | hw/usb.c | 73 |
1 files changed, 72 insertions, 1 deletions
@@ -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); @@ -323,7 +381,7 @@ int usb_handle_packet(USBDevice *dev, USBPacket *p) assert(p->state == USB_PACKET_SETUP); assert(p->ep != NULL); - if (QTAILQ_EMPTY(&p->ep->queue)) { + if (QTAILQ_EMPTY(&p->ep->queue) || p->ep->pipeline) { ret = usb_process_one(p); if (ret == USB_RET_ASYNC) { usb_packet_set_state(p, USB_PACKET_ASYNC); @@ -356,6 +414,9 @@ void usb_packet_complete(USBDevice *dev, USBPacket *p) while (!QTAILQ_EMPTY(&ep->queue)) { p = QTAILQ_FIRST(&ep->queue); + if (p->state == USB_PACKET_ASYNC) { + break; + } assert(p->state == USB_PACKET_QUEUED); ret = usb_process_one(p); if (ret == USB_RET_ASYNC) { @@ -413,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); } @@ -465,6 +527,7 @@ void usb_ep_init(USBDevice *dev) dev->ep_ctl.type = USB_ENDPOINT_XFER_CONTROL; dev->ep_ctl.ifnum = 0; dev->ep_ctl.dev = dev; + dev->ep_ctl.pipeline = false; QTAILQ_INIT(&dev->ep_ctl.queue); for (ep = 0; ep < USB_MAX_ENDPOINTS; ep++) { dev->ep_in[ep].nr = ep + 1; @@ -477,6 +540,8 @@ void usb_ep_init(USBDevice *dev) dev->ep_out[ep].ifnum = 0; dev->ep_in[ep].dev = dev; dev->ep_out[ep].dev = dev; + dev->ep_in[ep].pipeline = false; + dev->ep_out[ep].pipeline = false; QTAILQ_INIT(&dev->ep_in[ep].queue); QTAILQ_INIT(&dev->ep_out[ep].queue); } @@ -590,3 +655,9 @@ int usb_ep_get_max_packet_size(USBDevice *dev, int pid, int ep) struct USBEndpoint *uep = usb_ep_get(dev, pid, ep); return uep->max_packet_size; } + +void usb_ep_set_pipeline(USBDevice *dev, int pid, int ep, bool enabled) +{ + struct USBEndpoint *uep = usb_ep_get(dev, pid, ep); + uep->pipeline = enabled; +} |