Index: /trunk/src/VBox/Devices/USB/VUSBSniffer.cpp
===================================================================
--- /trunk/src/VBox/Devices/USB/VUSBSniffer.cpp	(revision 53014)
+++ /trunk/src/VBox/Devices/USB/VUSBSniffer.cpp	(revision 53014)
@@ -0,0 +1,664 @@
+/* $Id$ */
+/** @file
+ * Virtual USB - Sniffer facility.
+ */
+
+/*
+ * Copyright (C) 2014 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+/*******************************************************************************
+*   Header Files                                                               *
+*******************************************************************************/
+#define LOG_GROUP LOG_GROUP_DRV_VUSB
+#include <VBox/log.h>
+#include <iprt/file.h>
+#include <iprt/mem.h>
+#include <iprt/buildconfig.h>
+#include <iprt/string.h>
+#include <iprt/system.h>
+
+#include "VUSBSniffer.h"
+
+/*******************************************************************************
+*   Defined Constants And Macros                                               *
+*******************************************************************************/
+
+/** DumpFile Section Header Block type. */
+#define DUMPFILE_SHB_BLOCK_TYPE       UINT32_C(0x0a0d0d0a)
+/** The byte order magic value. */
+#define DUMPFILE_SHB_BYTE_ORDER_MAGIC UINT32_C(0x1a2b3c4d)
+/** Current major version. */
+#define DUMPFILE_SHB_VERSION_MAJOR    UINT16_C(1)
+/** Current minor version. */
+#define DUMPFILE_SHB_VERSION_MINOR    UINT16_C(0)
+
+/** Block type for the interface descriptor block. */
+#define DUMPFILE_IDB_BLOCK_TYPE       UINT32_C(0x00000001)
+/** USB link type. */
+#define DUMPFILE_IDB_LINK_TYPE_USB_LINUX_MMAPED UINT16_C(220)
+
+/** Block type for an enhanced packet block. */
+#define DUMPFILE_EPB_BLOCK_TYPE       UINT32_C(0x00000006)
+
+/** USB packet event types. */
+#define DUMPFILE_USB_EVENT_TYPE_SUBMIT   ('S')
+#define DUMPFILE_USB_EVENT_TYPE_COMPLETE ('C')
+#define DUMPFILE_USB_EVENT_TYPE_ERROR    ('E')
+
+#define DUMPFILE_OPTION_CODE_END      UINT16_C(0)
+#define DUMPFILE_OPTION_CODE_COMMENT  UINT16_C(1)
+
+#define DUMPFILE_OPTION_CODE_HARDWARE UINT16_C(2)
+#define DUMPFILE_OPTION_CODE_OS       UINT16_C(3)
+#define DUMPFILE_OPTION_CODE_USERAPP  UINT16_C(4)
+
+#define DUMPFILE_IDB_OPTION_TS_RESOLUTION UINT16_C(9)
+
+/*******************************************************************************
+*   DumpFile format structures                                                 *
+*******************************************************************************/
+
+/**
+ * DumpFile Block header.
+ */
+typedef struct DumpFileBlockHdr
+{
+    /** Block type. */
+    uint32_t            u32BlockType;
+    /** Block total length. */
+    uint32_t            u32BlockTotalLength;
+} DumpFileBlockHdr;
+/** Pointer to a block header. */
+typedef DumpFileBlockHdr *PDumpFileBlockHdr;
+
+/**
+ * DumpFile Option header.
+ */
+typedef struct DumpFileOptionHdr
+{
+    /** Option code. */
+    uint16_t            u16OptionCode;
+    /** Block total length. */
+    uint16_t            u16OptionLength;
+} DumpFileOptionHdr;
+/** Pointer to a option header. */
+typedef DumpFileOptionHdr *PDumpFileOptionHdr;
+
+/**
+ * DumpFile Section Header Block.
+ */
+typedef struct DumpFileShb
+{
+    /** Block header. */
+    DumpFileBlockHdr    Hdr;
+    /** Byte order magic. */
+    uint32_t            u32ByteOrderMagic;
+    /** Major version. */
+    uint16_t            u16VersionMajor;
+    /** Minor version. */
+    uint16_t            u16VersionMinor;
+    /** Section length. */
+    uint64_t            u64SectionLength;
+} DumpFileShb;
+/** Pointer to a Section Header Block. */
+typedef DumpFileShb *PDumpFileShb;
+
+/**
+ * DumpFile Interface description block.
+ */
+typedef struct DumpFileIdb
+{
+    /** Block header. */
+    DumpFileBlockHdr    Hdr;
+    /** Link type. */
+    uint16_t            u16LinkType;
+    /** Reserved. */
+    uint16_t            u16Reserved;
+    /** Maximum number of bytes dumped from each packet. */
+    uint32_t            u32SnapLen;
+} DumpFileIdb;
+/** Pointer to an Interface description block. */
+typedef DumpFileIdb *PDumpFileIdb;
+
+/**
+ * DumpFile Enhanced packet block.
+ */
+typedef struct DumpFileEpb
+{
+    /** Block header. */
+    DumpFileBlockHdr    Hdr;
+    /** Interface ID. */
+    uint32_t            u32InterfaceId;
+    /** Timestamp (high). */
+    uint32_t            u32TimestampHigh;
+    /** Timestamp (low). */
+    uint32_t            u32TimestampLow;
+    /** Captured packet length. */
+    uint32_t            u32CapturedLen;
+    /** Original packet length. */
+    uint32_t            u32PacketLen;
+} DumpFileEpb;
+/** Pointer to an Enhanced packet block. */
+typedef DumpFileEpb *PDumpFileEpb;
+
+/**
+ * USB setup URB data.
+ */
+typedef struct DumpFileUsbSetup
+{
+    uint8_t    bmRequestType;
+    uint8_t    bRequest;
+    uint16_t   wValue;
+    uint16_t   wIndex;
+    uint16_t   wLength;
+} DumpFileUsbSetup;
+typedef DumpFileUsbSetup *PDumpFileUsbSetup;
+
+/**
+ * USB Isochronous data.
+ */
+typedef struct DumpFileIsoRec
+{
+    int32_t    i32ErrorCount;
+    int32_t    i32NumDesc;
+} DumpFileIsoRec;
+typedef DumpFileIsoRec *PDumpFileIsoRec;
+
+/**
+ * USB packet header (Linux mmapped variant).
+ */
+typedef struct DumpFileUsbHeaderLnxMmapped
+{
+    /** Packet Id. */
+    uint64_t    u64Id;
+    /** Event type. */
+    uint8_t     u8EventType;
+    /** Transfer type. */
+    uint8_t     u8TransferType;
+    /** Endpoint number. */
+    uint8_t     u8EndpointNumber;
+    /** Device address. */
+    uint8_t     u8DeviceAddress;
+    /** Bus id. */
+    uint16_t    u16BusId;
+    /** Setup flag != 0 if the URB setup header is not present. */
+    uint8_t     u8SetupFlag;
+    /** Data present flag != 0 if the URB data is not present. */
+    uint8_t     u8DataFlag;
+    /** Timestamp (second part). */
+    uint64_t    u64TimestampSec;
+    /** Timestamp (us part). */
+    uint32_t    u32TimestampUSec;
+    /** Status. */
+    int32_t     i32Status;
+    /** URB length. */
+    uint32_t    u32UrbLength;
+    /** Recorded data length. */
+    uint32_t    u32DataLength;
+    /** Union of data for different URB types. */
+    union
+    {
+        DumpFileUsbSetup    UsbSetup;
+        DumpFileIsoRec      IsoRec;
+    } u;
+    int32_t     i32Interval;
+    int32_t     i32StartFrame;
+    /** Copy of transfer flags. */
+    uint32_t    u32XferFlags;
+    /** Number of isochronous descriptors. */
+    uint32_t    u32NumDesc;
+} DumpFileUsbHeaderLnxMmapped;
+/** Pointer to a USB packet header. */
+typedef DumpFileUsbHeaderLnxMmapped *PDumpFileUsbHeaderLnxMmapped;
+
+/**
+ * USB packet isochronous descriptor.
+ */
+typedef struct DumpFileUsbIsoDesc
+{
+    int32_t     i32Status;
+    uint32_t    u32Offset;
+    uint32_t    u32Len;
+    uint8_t     au8Padding[4];
+} DumpFileUsbIsoDesc;
+typedef DumpFileUsbIsoDesc *PDumpFileUsbIsoDesc;
+
+/*******************************************************************************
+*   Structures and Typedefs                                                    *
+*******************************************************************************/
+
+/**
+ * The internal VUSB sniffer state.
+ */
+typedef struct VUSBSNIFFERINT
+{
+    /** The file handle to dump to. */
+    RTFILE            hFile;
+    /** Current size of the block being written. */
+    uint32_t          cbBlockCur;
+    /** Maximum size allocated for the block. */
+    uint32_t          cbBlockMax;
+    /** Current block header. */
+    PDumpFileBlockHdr pBlockHdr;
+    /** Pointer to the block data which will be written on commit. */
+    uint8_t          *pbBlockData;
+} VUSBSNIFFERINT;
+/** Pointer to the internal VUSB sniffer state. */
+typedef VUSBSNIFFERINT *PVUSBSNIFFERINT;
+
+/**
+ * Allocates additional space for the block.
+ *
+ * @returns Pointer to the new unused space or NULL if out of memory.
+ * @param   pThis           The VUSB sniffer instance.
+ * @param   cbAdditional    The additional memory requested.
+ */
+static void *vusbSnifferBlockAllocSpace(PVUSBSNIFFERINT pThis, uint32_t cbAdditional)
+{
+    /* Fast path where we have enough memory allocated. */
+    if (pThis->cbBlockCur + cbAdditional <= pThis->cbBlockMax)
+    {
+        void *pv = pThis->pbBlockData + pThis->cbBlockCur;
+        pThis->cbBlockCur += cbAdditional;
+        return pv;
+    }
+
+    /* Allocate additional memory. */
+    uint32_t cbNew = pThis->cbBlockCur + cbAdditional;
+    uint8_t *pbDataNew = (uint8_t *)RTMemRealloc(pThis->pbBlockData, cbNew);
+    if (pbDataNew)
+    {
+        pThis->pbBlockData = pbDataNew;
+        pThis->pBlockHdr   = (PDumpFileBlockHdr)pbDataNew;
+
+        void *pv = pThis->pbBlockData + pThis->cbBlockCur;
+        pThis->cbBlockCur = cbNew;
+        pThis->cbBlockMax = cbNew;
+        return pv;
+    }
+
+    return NULL;
+}
+
+/**
+ * Commits the current block to the capture file.
+ *
+ * @returns VBox status code.
+ * @param   pThis           The VUSB sniffer instance.
+ */
+static int vusbSnifferBlockCommit(PVUSBSNIFFERINT pThis)
+{
+    int rc = VINF_SUCCESS;
+
+    AssertPtr(pThis->pBlockHdr);
+
+    /* Update the block total length field. */
+    uint32_t *pcbTotalLength = (uint32_t *)vusbSnifferBlockAllocSpace(pThis, 4);
+    if (pcbTotalLength)
+    {
+        *pcbTotalLength = pThis->cbBlockCur;
+        pThis->pBlockHdr->u32BlockTotalLength = pThis->cbBlockCur;
+
+        /* Write the data. */
+        rc = RTFileWrite(pThis->hFile, pThis->pbBlockData, pThis->cbBlockCur, NULL);
+        pThis->cbBlockCur = 0;
+        pThis->pBlockHdr  = NULL;
+    }
+    else
+        rc = VERR_NO_MEMORY;
+
+    return rc;
+}
+
+/**
+ * Starts a new block for capturing.
+ *
+ * @returns VBox status code.
+ * @param   pThis           The VUSB sniffer instance.
+ * @param   pBlockHdr       Pointer to the block header for the new block.
+ * @param   cbData          Amount of data added with this block.
+ */
+static int vusbSnifferBlockNew(PVUSBSNIFFERINT pThis, PDumpFileBlockHdr pBlockHdr, uint32_t cbData)
+{
+    int rc = VINF_SUCCESS;
+
+    /* Validate we don't get called while another block is active. */
+    Assert(!pThis->cbBlockCur);
+    Assert(!pThis->pBlockHdr);
+    pThis->pBlockHdr = (PDumpFileBlockHdr)vusbSnifferBlockAllocSpace(pThis, cbData);
+    if (pThis->pBlockHdr)
+        memcpy(pThis->pBlockHdr, pBlockHdr, cbData);
+    else
+        rc = VERR_NO_MEMORY;
+
+    return rc;
+}
+
+/**
+ * Adds new data to the current block.
+ *
+ * @returns VBox status code.
+ * @param   pThis           The VUSB sniffer instance.
+ * @param   pvData          The data to add.
+ * @param   cbData          Amount of data to add.
+ */
+static int vusbSnifferBlockAddData(PVUSBSNIFFERINT pThis, const void *pvData, uint32_t cbData)
+{
+    int rc = VINF_SUCCESS;
+
+    Assert(pThis->cbBlockCur);
+    AssertPtr(pThis->pBlockHdr);
+
+    void *pv = vusbSnifferBlockAllocSpace(pThis, cbData);
+    if (pv)
+        memcpy(pv, pvData, cbData);
+    else
+        rc = VERR_NO_MEMORY;
+
+    return rc;
+}
+
+/**
+ * Add a new option to the current block.
+ *
+ * @returns VBox status code.
+ * @param   pThis           The VUSB sniffer instance.
+ * @param   u16OptionCode   The option code identifying the type of option.
+ * @param   pvOption        Raw data for the option.
+ * @param   cbOption        Size of the optiob data.
+ */
+static int vusbSnifferAddOption(PVUSBSNIFFERINT pThis, uint16_t u16OptionCode, const void *pvOption, uint16_t cbOption)
+{
+    int rc = VINF_SUCCESS;
+    DumpFileOptionHdr OptHdr;
+
+    OptHdr.u16OptionCode   = u16OptionCode;
+    OptHdr.u16OptionLength = cbOption;
+    rc = vusbSnifferBlockAddData(pThis, &OptHdr, sizeof(OptHdr));
+    if (   RT_SUCCESS(rc)
+        && u16OptionCode != DUMPFILE_OPTION_CODE_END
+        && cbOption != 0)
+    {
+        rc = vusbSnifferBlockAddData(pThis, pvOption, cbOption);
+        if (RT_SUCCESS(rc))
+        {
+            /* Pad to 32bits. */
+            uint8_t abPad[3] = { 0 };
+            uint32_t cbPad = RT_ALIGN_32(cbOption, 4) - cbOption;
+
+            Assert(cbPad <= 3);
+            if (cbPad)
+                rc = vusbSnifferBlockAddData(pThis, abPad, cbPad);
+        }
+    }
+
+    return rc;
+}
+
+DECLHIDDEN(int) VUSBSnifferCreate(PVUSBSNIFFER phSniffer, uint32_t fFlags,
+                                  const char *pszCaptureFilename, const char *pszDesc)
+{
+    int rc = VINF_SUCCESS;
+    PVUSBSNIFFERINT pThis = NULL;
+
+    pThis = (PVUSBSNIFFERINT)RTMemAllocZ(sizeof(VUSBSNIFFERINT));
+    if (pThis)
+    {
+        pThis->hFile       = NIL_RTFILE;
+        pThis->cbBlockCur  = 0;
+        pThis->cbBlockMax  = 0;
+        pThis->pbBlockData = NULL;
+
+        rc = RTFileOpen(&pThis->hFile, pszCaptureFilename, RTFILE_O_DENY_NONE | RTFILE_O_CREATE | RTFILE_O_WRITE | RTFILE_O_READ);
+        if (RT_SUCCESS(rc))
+        {
+            /* Write header and link type blocks. */
+            DumpFileShb Shb;
+
+            Shb.Hdr.u32BlockType        = DUMPFILE_SHB_BLOCK_TYPE;
+            Shb.Hdr.u32BlockTotalLength = 0; /* Filled out by lower layer. */
+            Shb.u32ByteOrderMagic       = DUMPFILE_SHB_BYTE_ORDER_MAGIC;
+            Shb.u16VersionMajor         = DUMPFILE_SHB_VERSION_MAJOR;
+            Shb.u16VersionMinor         = DUMPFILE_SHB_VERSION_MINOR;
+            Shb.u64SectionLength        = UINT64_C(0xffffffffffffffff); /* -1 */
+
+            /* Write the blocks. */
+            rc = vusbSnifferBlockNew(pThis, &Shb.Hdr, sizeof(Shb));
+            if (RT_SUCCESS(rc))
+            {
+                const char *pszOpt = RTBldCfgTargetDotArch();
+                rc = vusbSnifferAddOption(pThis, DUMPFILE_OPTION_CODE_HARDWARE, pszOpt, strlen(pszOpt) + 1);
+            }
+
+            if (RT_SUCCESS(rc))
+            {
+                char szTmp[512];
+                size_t cbTmp = sizeof(szTmp);
+
+                RT_ZERO(szTmp);
+
+                /* Build the OS code. */
+                rc = RTSystemQueryOSInfo(RTSYSOSINFO_PRODUCT, szTmp, cbTmp);
+                if (RT_SUCCESS(rc))
+                {
+                    size_t cb = strlen(szTmp);
+
+                    szTmp[cb] = ' ';
+                    rc = RTSystemQueryOSInfo(RTSYSOSINFO_RELEASE, &szTmp[cb + 1], cbTmp - (cb + 1));
+                    if (RT_SUCCESS(rc))
+                    {
+                        cb = strlen(szTmp);
+                        szTmp[cb] = ' ';
+                        rc = RTSystemQueryOSInfo(RTSYSOSINFO_VERSION, &szTmp[cb + 1], cbTmp - (cb + 1));
+                    }
+                }
+
+                if (RT_SUCCESS(rc) || rc == VERR_BUFFER_OVERFLOW)
+                    rc = vusbSnifferAddOption(pThis, DUMPFILE_OPTION_CODE_OS, szTmp, strlen(szTmp) + 1);
+                else
+                    rc = VINF_SUCCESS; /* Skip OS code if building the string failed. */
+            }
+
+            if (RT_SUCCESS(rc))
+            {
+                /** @todo: Add product info. */
+            }
+
+            if (RT_SUCCESS(rc))
+                rc = vusbSnifferAddOption(pThis, DUMPFILE_OPTION_CODE_END, NULL, 0);
+            if (RT_SUCCESS(rc))
+                rc = vusbSnifferBlockCommit(pThis);
+
+            /* Write Interface descriptor block. */
+            if (RT_SUCCESS(rc))
+            {
+                DumpFileIdb Idb;
+
+                Idb.Hdr.u32BlockType        = DUMPFILE_IDB_BLOCK_TYPE;
+                Idb.Hdr.u32BlockTotalLength = 0; /* Filled out by lower layer. */
+                Idb.u16LinkType             = DUMPFILE_IDB_LINK_TYPE_USB_LINUX_MMAPED;
+                Idb.u16Reserved             = 0;
+                Idb.u32SnapLen              = UINT32_C(0xffffffff);
+
+                rc = vusbSnifferBlockNew(pThis, &Idb.Hdr, sizeof(Idb));
+                if (RT_SUCCESS(rc))
+                {
+                    uint8_t u8TsResolution = 9; /* Nano second resolution. */
+                    /* Add timestamp resolution option. */
+                    rc = vusbSnifferAddOption(pThis, DUMPFILE_IDB_OPTION_TS_RESOLUTION,
+                                              &u8TsResolution, sizeof(u8TsResolution));
+                }
+                if (RT_SUCCESS(rc))
+                    rc = vusbSnifferAddOption(pThis, DUMPFILE_OPTION_CODE_END, NULL, 0);
+                if (RT_SUCCESS(rc))
+                    rc = vusbSnifferBlockCommit(pThis);
+            }
+
+            if (RT_SUCCESS(rc))
+            {
+                *phSniffer = pThis;
+                return VINF_SUCCESS;
+            }
+
+            RTFileClose(pThis->hFile);
+            pThis->hFile = NIL_RTFILE;
+            RTFileDelete(pszCaptureFilename);
+        }
+        VUSBSnifferDestroy(pThis);
+    }
+    else
+        rc = VERR_NO_MEMORY;
+
+    return rc;
+}
+
+/**
+ * Destroys the given VUSB sniffer instance.
+ *
+ * @returns nothing.
+ * @param   hSniffer              The sniffer instance to destroy.
+ */
+DECLHIDDEN(void) VUSBSnifferDestroy(VUSBSNIFFER hSniffer)
+{
+    PVUSBSNIFFERINT pThis = hSniffer;
+
+    if (pThis->hFile != NIL_RTFILE)
+        RTFileClose(pThis->hFile);
+    if (pThis->pbBlockData)
+        RTMemFree(pThis->pbBlockData);
+    RTMemFree(pThis);
+}
+
+/**
+ * Records an VUSB event.
+ *
+ * @returns VBox status code.
+ * @param   hSniffer              The sniffer instance.
+ * @param   pUrb                  The URB triggering the event.
+ * @param   enmEvent              The type of event to record.
+ */
+DECLHIDDEN(int) VUSBSnifferRecordEvent(VUSBSNIFFER hSniffer, PVUSBURB pUrb, VUSBSNIFFEREVENT enmEvent)
+{
+    int rc = VINF_SUCCESS;
+    PVUSBSNIFFERINT pThis = hSniffer;
+    DumpFileEpb Epb;
+    DumpFileUsbHeaderLnxMmapped UsbHdr;
+    DumpFileUsbSetup UsbSetup;
+    uint64_t u64TimestampEvent = RTTimeNanoTS();
+
+    /* Start with the enhanced packet block. */
+    Epb.Hdr.u32BlockType        = DUMPFILE_EPB_BLOCK_TYPE;
+    Epb.Hdr.u32BlockTotalLength = 0;
+    Epb.u32InterfaceId          = 0;
+    Epb.u32TimestampHigh        = (u64TimestampEvent >> 32) & UINT32_C(0xffffffff);
+    Epb.u32TimestampLow         = u64TimestampEvent & UINT32_C(0xffffffff);
+    Epb.u32CapturedLen          = sizeof(UsbHdr) + pUrb->cbData;
+    Epb.u32PacketLen            = sizeof(UsbHdr) + pUrb->cbData;
+
+    UsbHdr.u64Id = (uint64_t)pUrb; /** @todo: check whether the pointer is a good ID. */
+    switch (enmEvent)
+    {
+        case VUSBSNIFFEREVENT_SUBMIT:
+            UsbHdr.u8EventType = DUMPFILE_USB_EVENT_TYPE_SUBMIT;
+            break;
+        case VUSBSNIFFEREVENT_COMPLETE:
+            UsbHdr.u8EventType = DUMPFILE_USB_EVENT_TYPE_COMPLETE;
+            break;
+        case VUSBSNIFFEREVENT_ERROR_SUBMIT:
+        case VUSBSNIFFEREVENT_ERROR_COMPLETE:
+            UsbHdr.u8EventType = DUMPFILE_USB_EVENT_TYPE_ERROR;
+            break;
+        default:
+            AssertMsgFailed(("Invalid event type %d\n", enmEvent));
+    }
+    switch (pUrb->enmType)
+    {
+        case VUSBXFERTYPE_ISOC:
+                UsbHdr.u8TransferType = 0;
+                break;
+        case VUSBXFERTYPE_BULK:
+                UsbHdr.u8TransferType = 3;
+                break;
+        case VUSBXFERTYPE_INTR:
+                UsbHdr.u8TransferType = 1;
+                break;
+        case VUSBXFERTYPE_CTRL:
+        case VUSBXFERTYPE_MSG:
+                UsbHdr.u8TransferType = 2;
+                break;
+        default:
+            AssertMsgFailed(("invalid transfer type %d\n", pUrb->enmType));
+    }
+
+    bool fRecordData = false;
+
+    if (   pUrb->cbData
+        && (    (   pUrb->enmDir == VUSBDIRECTION_OUT
+                 && pUrb->enmType != VUSBXFERTYPE_CTRL
+                 && pUrb->enmType != VUSBXFERTYPE_MSG
+                 && enmEvent == VUSBSNIFFEREVENT_SUBMIT)
+             || (   pUrb->enmDir == VUSBDIRECTION_IN
+                 && enmEvent == VUSBSNIFFEREVENT_COMPLETE)))
+        fRecordData = true;
+
+    UsbHdr.u8EndpointNumber = pUrb->EndPt | pUrb->enmDir == VUSBDIRECTION_IN ? 0x80 : 0x00;
+    UsbHdr.u8DeviceAddress  = pUrb->DstAddress;
+    UsbHdr.u16BusId         = 0;
+    UsbHdr.u8SetupFlag      = pUrb->enmType == VUSBXFERTYPE_MSG || pUrb->enmType == VUSBXFERTYPE_CTRL ? 0 : 1;
+    UsbHdr.u8DataFlag       = fRecordData ? 0 : 1;
+    UsbHdr.u64TimestampSec  = u64TimestampEvent / RT_NS_1SEC_64;;
+    UsbHdr.u32TimestampUSec = u64TimestampEvent / RT_NS_1US_64 - UsbHdr.u64TimestampSec * RT_US_1SEC;
+    UsbHdr.i32Status        = pUrb->enmStatus;
+    UsbHdr.u32UrbLength     = pUrb->cbData;
+    UsbHdr.u32DataLength    = fRecordData ? pUrb->cbData : 0;
+    UsbHdr.i32Interval      = 0;
+    UsbHdr.i32StartFrame    = 0;
+    UsbHdr.u32XferFlags     = 0;
+    UsbHdr.u32NumDesc       = pUrb->enmType == VUSBXFERTYPE_ISOC ? : 0;
+
+    if (   pUrb->enmType == VUSBXFERTYPE_CTRL
+        || pUrb->enmType == VUSBXFERTYPE_MSG)
+    {
+        PVUSBSETUP pSetup = (PVUSBSETUP)pUrb->abData;
+
+        UsbHdr.u.UsbSetup.bmRequestType = pSetup->bmRequestType;
+        UsbHdr.u.UsbSetup.bRequest      = pSetup->bRequest;
+        UsbHdr.u.UsbSetup.wValue        = pSetup->wValue;
+        UsbHdr.u.UsbSetup.wIndex        = pSetup->wIndex;
+        UsbHdr.u.UsbSetup.wLength       = pSetup->wLength;
+    }
+
+    rc = vusbSnifferBlockNew(pThis, &Epb.Hdr, sizeof(Epb));
+    if (RT_SUCCESS(rc))
+        rc = vusbSnifferBlockAddData(pThis, &UsbHdr, sizeof(UsbHdr));
+
+    /* Add Isochronous descriptors now. */
+    for (unsigned i = 0; i < pUrb->cIsocPkts && RT_SUCCESS(rc); i++)
+    {
+        DumpFileUsbIsoDesc IsoDesc;
+        IsoDesc.i32Status = pUrb->aIsocPkts[i].enmStatus;
+        IsoDesc.u32Offset = pUrb->aIsocPkts[i].off;
+        IsoDesc.u32Len    = pUrb->aIsocPkts[i].cb;
+        rc = vusbSnifferBlockAddData(pThis, &IsoDesc, sizeof(IsoDesc));
+    }
+
+    /* Record data. */
+    if (   RT_SUCCESS(rc)
+        && fRecordData)
+        rc = vusbSnifferBlockAddData(pThis, pUrb->abData, pUrb->cbData);
+
+    if (RT_SUCCESS(rc))
+        rc = vusbSnifferBlockCommit(pThis);
+
+    return rc;
+}
+
Index: /trunk/src/VBox/Devices/USB/VUSBSniffer.h
===================================================================
--- /trunk/src/VBox/Devices/USB/VUSBSniffer.h	(revision 53014)
+++ /trunk/src/VBox/Devices/USB/VUSBSniffer.h	(revision 53014)
@@ -0,0 +1,87 @@
+/* $Id$ */
+/** @file
+ * Virtual USB - Sniffer facility.
+ */
+
+/*
+ * Copyright (C) 2014 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+#ifndef ___VUSBSniffer_h
+#define ___VUSBSniffer_h
+
+#include <VBox/cdefs.h>
+#include <VBox/types.h>
+#include <VBox/vusb.h>
+
+RT_C_DECLS_BEGIN
+
+/** Opauqe VUSB sniffer handle. */
+typedef struct VUSBSNIFFERINT *VUSBSNIFFER;
+/** Pointer to a VUSB sniffer handle. */
+typedef VUSBSNIFFER *PVUSBSNIFFER;
+
+/** NIL sniffer instance handle. */
+#define VUSBSNIFFER_NIL ((VUSBSNIFFER)0)
+
+/**
+ * VUSB Sniffer event types.
+ */
+typedef enum VUSBSNIFFEREVENT
+{
+    /** Invalid event. */
+    VUSBSNIFFEREVENT_INVALID = 0,
+    /** URB submit event. */
+    VUSBSNIFFEREVENT_SUBMIT,
+    /** URB complete event. */
+    VUSBSNIFFEREVENT_COMPLETE,
+    /** URB submit failed event. */
+    VUSBSNIFFEREVENT_ERROR_SUBMIT,
+    /** URB completed with error event. */
+    VUSBSNIFFEREVENT_ERROR_COMPLETE,
+    /** 32bit hack. */
+    VUSBSNIFFEREVENT_32BIT_HACK = 0x7fffffff
+} VUSBSNIFFEREVENT;
+
+/**
+ * Create a new VUSB sniffer instance dumping to the given capture file.
+ *
+ * @returns VBox status code.
+ * @param   phSniffer             Where to store the handle to the sniffer instance on success.
+ * @param   fFlags                Flags, reserved, must be 0.
+ * @param   pszCaptureFilename    The filename to use for capturing the sniffed data.
+ * @param   pszDesc               Optional description for the dump.
+ */
+DECLHIDDEN(int) VUSBSnifferCreate(PVUSBSNIFFER phSniffer, uint32_t fFlags,
+                                  const char *pszCaptureFilename, const char *pszDesc);
+
+/**
+ * Destroys the given VUSB sniffer instance.
+ *
+ * @returns nothing.
+ * @param   hSniffer              The sniffer instance to destroy.
+ */
+DECLHIDDEN(void) VUSBSnifferDestroy(VUSBSNIFFER hSniffer);
+
+/**
+ * Records an VUSB event.
+ *
+ * @returns VBox status code.
+ * @param   hSniffer              The sniffer instance.
+ * @param   pUrb                  The URB triggering the event.
+ * @param   enmEvent              The type of event to record.
+ */
+DECLHIDDEN(int) VUSBSnifferRecordEvent(VUSBSNIFFER hSniffer, PVUSBURB pUrb, VUSBSNIFFEREVENT enmEvent);
+
+
+RT_C_DECLS_END
+#endif
+
