From 01b3362c1465ffcaa770cfcd64f0f609ee75d1aa Mon Sep 17 00:00:00 2001 From: Peter Wu Date: Thu, 20 Mar 2014 18:05:23 +0100 Subject: usbdump: support xhci The XHCI HCD sends an OUT packet which was unexpected by the usbdump code. That was the case because the core USB code that was normally calling handle_control only called when the full data was received from the host (from the Status stage, with PID == USB_TOKEN_IN). Then there is also the do_parameter function as used by XHCI that was not really handled. Fix this by allowing OUT tokens, also document why this is necessary. Signed-off-by: Peter Wu --- hw/usb/dump.c | 27 +++++++++++++++++++-------- hw/usb/dump.h | 6 ++++-- 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/hw/usb/dump.c b/hw/usb/dump.c index b0209c1f02..3f2fef1b1e 100644 --- a/hw/usb/dump.c +++ b/hw/usb/dump.c @@ -298,11 +298,17 @@ void usb_dump_submit(UsbDumpState *s, const USBPacket *p) } if (ep_type == USB_ENDPOINT_XFER_CONTROL) { - /* PID is IN because this is the status stage for OUT, it cannot get - * called for OUT (that would be the data packet themselves) */ - assert(pid == USB_TOKEN_IN || pid == USB_TOKEN_SETUP); - - if (pid == USB_TOKEN_IN) { + /** + * Two moments where an out transfer happens. + * (1) Via the normal do_token_setup()/do_token_out()/do_token_in() + * sequence. As handle_control() is only called in do_token_in() + * (the status stage), the PID will be IN. + * (2) Via do_parameter (as used by XHCI), all setup/data/status stages + * are done at once, and the PID will be OUT. + * (3) Via xhci_address_slot(), the PID will be OUT. As there is no + * data, this case does not really provide data. + */ + if (pid == USB_TOKEN_IN || (pid == USB_TOKEN_OUT && p->parameter)) { /* ctrl: device should set data in setup stage */ datalen = dev->setup_len; } @@ -332,9 +338,14 @@ void usb_dump_complete(UsbDumpState *s, const USBPacket *p) } if (ep_type == USB_ENDPOINT_XFER_CONTROL) { - assert(pid == USB_TOKEN_IN || pid == USB_TOKEN_SETUP); - - if (pid == USB_TOKEN_SETUP) { + /* Two modes of operation where data is available: + * (1) Via the normal do_token_setup() sequence. Data is provided by the + * device, but since handle_control() is called in do_token_setup(), + * the PID will be SETUP. + * (2) Via do_parameter (as used by XHCI), all setup/data/status stages + * are done at once, and the PID will be IN when data was requested. + */ + if (pid == USB_TOKEN_SETUP || (pid == USB_TOKEN_IN && p->parameter)) { /* this was an IN request, data is now available. */ assert(dev->setup_buf[0] & USB_DIR_IN); datalen = p->actual_length; diff --git a/hw/usb/dump.h b/hw/usb/dump.h index 247016f259..7e81aaa043 100644 --- a/hw/usb/dump.h +++ b/hw/usb/dump.h @@ -36,7 +36,8 @@ typedef struct UsbDumpState UsbDumpState; * - IN: setup_len > 0 (data): callback for OUT * - (same code path: IN: setup_len == 0 (!data): Called iff dir is OUT * (otherwise BROKEN!)) - * - Note: never called in OUT. + * - Note: never called in OUT (via do_token_out), but do_parameter may call + * with OUT as does XHCI. * * Possible combinations (^X,Y = handle_ctrl called, X and Y are Submission or * Callback where X is called before processing the data in handle_ctrl, and Y @@ -52,6 +53,7 @@ typedef struct UsbDumpState UsbDumpState; * - setup(setup_len == 0, IN) / status^SC,(IN) - Same as above (with * status(IN)), but there is no data for both submission and callback. * - setup(setup_len == 0, OUT) / status(OUT) (BROKEN!) + * - NOTE: do_parameter() is not taken into account in the above description. * * handle_data is called for all non-EP0 transfers. PID is either IN or OUT. * Sequence for the PID cases (interrupt): @@ -72,7 +74,7 @@ typedef struct UsbDumpState UsbDumpState; * --------+---+---+ * * Control: DONE - * Int: IN PROGRESS + * Int: DONE * Bulk: TODO * Isoc: TODO */ -- cgit v1.2.1