From patchwork Fri Feb 10 11:43:16 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Gerd Hoffmann X-Patchwork-Id: 140578 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [140.186.70.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id 88594B6F98 for ; Fri, 10 Feb 2012 22:45:14 +1100 (EST) Received: from localhost ([::1]:33534 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1RvouX-0000PC-ID for incoming@patchwork.ozlabs.org; Fri, 10 Feb 2012 06:45:05 -0500 Received: from eggs.gnu.org ([140.186.70.92]:46638) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1RvotN-0006Wt-JV for qemu-devel@nongnu.org; Fri, 10 Feb 2012 06:43:55 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1RvotF-0000Lz-T3 for qemu-devel@nongnu.org; Fri, 10 Feb 2012 06:43:53 -0500 Received: from mx1.redhat.com ([209.132.183.28]:42260) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1RvotF-0000Le-Jh for qemu-devel@nongnu.org; Fri, 10 Feb 2012 06:43:45 -0500 Received: from int-mx10.intmail.prod.int.phx2.redhat.com (int-mx10.intmail.prod.int.phx2.redhat.com [10.5.11.23]) by mx1.redhat.com (8.14.4/8.14.4) with ESMTP id q1ABhiRG005556 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK) for ; Fri, 10 Feb 2012 06:43:44 -0500 Received: from rincewind.home.kraxel.org (ovpn-116-66.ams2.redhat.com [10.36.116.66]) by int-mx10.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id q1ABhZ4a032086; Fri, 10 Feb 2012 06:43:39 -0500 Received: by rincewind.home.kraxel.org (Postfix, from userid 500) id 7FB5E415B1; Fri, 10 Feb 2012 12:43:26 +0100 (CET) From: Gerd Hoffmann To: qemu-devel@nongnu.org Date: Fri, 10 Feb 2012 12:43:16 +0100 Message-Id: <1328874204-20920-21-git-send-email-kraxel@redhat.com> In-Reply-To: <1328874204-20920-1-git-send-email-kraxel@redhat.com> References: <1328874204-20920-1-git-send-email-kraxel@redhat.com> X-Scanned-By: MIMEDefang 2.68 on 10.5.11.23 X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 209.132.183.28 Cc: Gerd Hoffmann Subject: [Qemu-devel] [PATCH 20/28] usb: maintain async packet list per endpoint X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Maintain a list of async packets per endpoint. With the current code the list will never receive more than a single item. I think you can guess what the future plan is though ;) Signed-off-by: Gerd Hoffmann --- hw/usb.c | 127 +++++++++++++++++++++++++++++++++++++++++++++++++------------- hw/usb.h | 9 +++- 2 files changed, 108 insertions(+), 28 deletions(-) diff --git a/hw/usb.c b/hw/usb.c index 240f24b..712bdd4 100644 --- a/hw/usb.c +++ b/hw/usb.c @@ -279,6 +279,28 @@ USBDevice *usb_find_device(USBPort *port, uint8_t addr) return usb_device_find_device(dev, addr); } +static int usb_process_one(USBPacket *p) +{ + USBDevice *dev = p->ep->dev; + + if (p->ep->nr == 0) { + /* control pipe */ + switch (p->pid) { + case USB_TOKEN_SETUP: + return do_token_setup(dev, p); + case USB_TOKEN_IN: + return do_token_in(dev, p); + case USB_TOKEN_OUT: + return do_token_out(dev, p); + default: + return USB_RET_STALL; + } + } else { + /* data pipe */ + return usb_device_handle_data(dev, p); + } +} + /* Hand over a packet to a device for processing. Return value USB_RET_ASYNC indicates the processing isn't finished yet, the driver will call usb_packet_complete() when done processing it. */ @@ -292,30 +314,21 @@ int usb_handle_packet(USBDevice *dev, USBPacket *p) assert(dev == p->ep->dev); assert(dev->state == USB_STATE_DEFAULT); assert(p->state == USB_PACKET_SETUP); + assert(p->ep != NULL); - if (p->ep->nr == 0) { - /* control pipe */ - switch (p->pid) { - case USB_TOKEN_SETUP: - ret = do_token_setup(dev, p); - break; - case USB_TOKEN_IN: - ret = do_token_in(dev, p); - break; - case USB_TOKEN_OUT: - ret = do_token_out(dev, p); - break; - default: - ret = USB_RET_STALL; - break; + if (QTAILQ_EMPTY(&p->ep->queue)) { + ret = usb_process_one(p); + if (ret == USB_RET_ASYNC) { + usb_packet_set_state(p, USB_PACKET_ASYNC); + QTAILQ_INSERT_TAIL(&p->ep->queue, p, queue); + } else { + p->result = ret; + usb_packet_set_state(p, USB_PACKET_COMPLETE); } } else { - /* data pipe */ - ret = usb_device_handle_data(dev, p); - } - - if (ret == USB_RET_ASYNC) { - p->state = USB_PACKET_ASYNC; + ret = USB_RET_ASYNC; + usb_packet_set_state(p, USB_PACKET_QUEUED); + QTAILQ_INSERT_TAIL(&p->ep->queue, p, queue); } return ret; } @@ -325,9 +338,28 @@ int usb_handle_packet(USBDevice *dev, USBPacket *p) handle_packet. */ void usb_packet_complete(USBDevice *dev, USBPacket *p) { + USBEndpoint *ep = p->ep; + int ret; + assert(p->state == USB_PACKET_ASYNC); - p->state = USB_PACKET_COMPLETE; + assert(QTAILQ_FIRST(&ep->queue) == p); + usb_packet_set_state(p, USB_PACKET_COMPLETE); + QTAILQ_REMOVE(&ep->queue, p, queue); dev->port->ops->complete(dev->port, p); + + while (!QTAILQ_EMPTY(&ep->queue)) { + p = QTAILQ_FIRST(&ep->queue); + assert(p->state == USB_PACKET_QUEUED); + ret = usb_process_one(p); + if (ret == USB_RET_ASYNC) { + usb_packet_set_state(p, USB_PACKET_ASYNC); + break; + } + p->result = ret; + usb_packet_set_state(p, USB_PACKET_COMPLETE); + QTAILQ_REMOVE(&ep->queue, p, queue); + dev->port->ops->complete(dev->port, p); + } } /* Cancel an active packet. The packed must have been deferred by @@ -335,9 +367,13 @@ void usb_packet_complete(USBDevice *dev, USBPacket *p) completed. */ void usb_cancel_packet(USBPacket * p) { - assert(p->state == USB_PACKET_ASYNC); - p->state = USB_PACKET_CANCELED; - usb_device_cancel_packet(p->ep->dev, p); + bool callback = (p->state == USB_PACKET_ASYNC); + assert(usb_packet_is_inflight(p)); + usb_packet_set_state(p, USB_PACKET_CANCELED); + QTAILQ_REMOVE(&p->ep->queue, p, queue); + if (callback) { + usb_device_cancel_packet(p->ep->dev, p); + } } @@ -346,14 +382,50 @@ void usb_packet_init(USBPacket *p) qemu_iovec_init(&p->iov, 1); } +void usb_packet_set_state(USBPacket *p, USBPacketState state) +{ +#ifdef DEBUG + static const char *name[] = { + [USB_PACKET_UNDEFINED] = "undef", + [USB_PACKET_SETUP] = "setup", + [USB_PACKET_QUEUED] = "queued", + [USB_PACKET_ASYNC] = "async", + [USB_PACKET_COMPLETE] = "complete", + [USB_PACKET_CANCELED] = "canceled", + }; + static const char *rets[] = { + [-USB_RET_NODEV] = "NODEV", + [-USB_RET_NAK] = "NAK", + [-USB_RET_STALL] = "STALL", + [-USB_RET_BABBLE] = "BABBLE", + [-USB_RET_ASYNC] = "ASYNC", + }; + char add[16] = ""; + + if (state == USB_PACKET_COMPLETE) { + if (p->result < 0) { + snprintf(add, sizeof(add), " - %s", rets[-p->result]); + } else { + snprintf(add, sizeof(add), " - %d", p->result); + } + } + fprintf(stderr, "bus %s, port %s, dev %d, ep %d: packet %p: %s -> %s%s\n", + p->ep->dev->qdev.parent_bus->name, + p->ep->dev->port->path, + p->ep->dev->addr, p->ep->nr, + p, name[p->state], name[state], add); +#endif + p->state = state; +} + void usb_packet_setup(USBPacket *p, int pid, USBEndpoint *ep) { assert(!usb_packet_is_inflight(p)); - p->state = USB_PACKET_SETUP; p->pid = pid; p->ep = ep; p->result = 0; qemu_iovec_reset(&p->iov); + usb_packet_set_state(p, USB_PACKET_SETUP); } void usb_packet_addbuf(USBPacket *p, void *ptr, size_t len) @@ -404,6 +476,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; + QTAILQ_INIT(&dev->ep_ctl.queue); for (ep = 0; ep < USB_MAX_ENDPOINTS; ep++) { dev->ep_in[ep].nr = ep + 1; dev->ep_out[ep].nr = ep + 1; @@ -415,6 +488,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; + QTAILQ_INIT(&dev->ep_in[ep].queue); + QTAILQ_INIT(&dev->ep_out[ep].queue); } } diff --git a/hw/usb.h b/hw/usb.h index a80fe8f..6545b69 100644 --- a/hw/usb.h +++ b/hw/usb.h @@ -177,6 +177,7 @@ struct USBEndpoint { uint8_t ifnum; int max_packet_size; USBDevice *dev; + QTAILQ_HEAD(, USBPacket) queue; }; /* definition of a USB device */ @@ -309,15 +310,16 @@ struct USBPort { typedef void USBCallback(USBPacket * packet, void *opaque); -/* Structure used to hold information about an active USB packet. */ typedef enum USBPacketState { USB_PACKET_UNDEFINED = 0, USB_PACKET_SETUP, + USB_PACKET_QUEUED, USB_PACKET_ASYNC, USB_PACKET_COMPLETE, USB_PACKET_CANCELED, } USBPacketState; +/* Structure used to hold information about an active USB packet. */ struct USBPacket { /* Data fields for use by the driver. */ int pid; @@ -326,9 +328,11 @@ struct USBPacket { int result; /* transfer length or USB_RET_* status code */ /* Internal use by the USB layer. */ USBPacketState state; + QTAILQ_ENTRY(USBPacket) queue; }; void usb_packet_init(USBPacket *p); +void usb_packet_set_state(USBPacket *p, USBPacketState state); void usb_packet_setup(USBPacket *p, int pid, USBEndpoint *ep); void usb_packet_addbuf(USBPacket *p, void *ptr, size_t len); int usb_packet_map(USBPacket *p, QEMUSGList *sgl); @@ -339,7 +343,8 @@ void usb_packet_cleanup(USBPacket *p); static inline bool usb_packet_is_inflight(USBPacket *p) { - return p->state == USB_PACKET_ASYNC; + return (p->state == USB_PACKET_QUEUED || + p->state == USB_PACKET_ASYNC); } USBDevice *usb_find_device(USBPort *port, uint8_t addr);