summaryrefslogtreecommitdiffstats
path: root/src/VBox/Devices/USB/VUSBSnifferUsbMon.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Devices/USB/VUSBSnifferUsbMon.cpp')
-rw-r--r--src/VBox/Devices/USB/VUSBSnifferUsbMon.cpp249
1 files changed, 249 insertions, 0 deletions
diff --git a/src/VBox/Devices/USB/VUSBSnifferUsbMon.cpp b/src/VBox/Devices/USB/VUSBSnifferUsbMon.cpp
new file mode 100644
index 00000000..bfa52f95
--- /dev/null
+++ b/src/VBox/Devices/USB/VUSBSnifferUsbMon.cpp
@@ -0,0 +1,249 @@
+/* $Id: VUSBSnifferUsbMon.cpp $ */
+/** @file
+ * Virtual USB Sniffer facility - Linux usbmon ASCII format.
+ */
+
+/*
+ * Copyright (C) 2016-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_DRV_VUSB
+#include <VBox/log.h>
+#include <iprt/mem.h>
+#include <iprt/buildconfig.h>
+#include <iprt/string.h>
+#include <iprt/system.h>
+#include <iprt/time.h>
+
+#include "VUSBSnifferInternal.h"
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+
+/**
+ * The internal VUSB sniffer state.
+ */
+typedef struct VUSBSNIFFERFMTINT
+{
+ /** Stream handle. */
+ PVUSBSNIFFERSTRM pStrm;
+} VUSBSNIFFERFMTINT;
+
+
+/*********************************************************************************************************************************
+* Static Variables *
+*********************************************************************************************************************************/
+
+/**
+ * Supported file extensions.
+ */
+static const char *s_apszFileExts[] =
+{
+ "mon",
+ "usbmon",
+ NULL
+};
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+
+
+/** @interface_method_impl{VUSBSNIFFERFMT,pfnInit} */
+static DECLCALLBACK(int) vusbSnifferFmtUsbMonInit(PVUSBSNIFFERFMTINT pThis, PVUSBSNIFFERSTRM pStrm)
+{
+ pThis->pStrm = pStrm;
+ return VINF_SUCCESS;
+}
+
+
+/** @interface_method_impl{VUSBSNIFFERFMT,pfnDestroy} */
+static DECLCALLBACK(void) vusbSnifferFmtUsbMonDestroy(PVUSBSNIFFERFMTINT pThis)
+{
+ RT_NOREF(pThis);
+}
+
+
+/** @interface_method_impl{VUSBSNIFFERFMT,pfnRecordEvent} */
+static DECLCALLBACK(int) vusbSnifferFmtUsbMonRecordEvent(PVUSBSNIFFERFMTINT pThis, PVUSBURB pUrb, VUSBSNIFFEREVENT enmEvent)
+{
+ char aszLineBuf[512];
+ char chEvtType = 'X';
+ char chDir = 'X';
+ char chEptType = 'X';
+
+ switch (enmEvent)
+ {
+ case VUSBSNIFFEREVENT_SUBMIT:
+ chEvtType = 'S';
+ break;
+ case VUSBSNIFFEREVENT_COMPLETE:
+ chEvtType = 'C';
+ break;
+ case VUSBSNIFFEREVENT_ERROR_SUBMIT:
+ case VUSBSNIFFEREVENT_ERROR_COMPLETE:
+ chEvtType = 'E';
+ break;
+ default:
+ AssertMsgFailed(("Invalid event type %d\n", enmEvent));
+ }
+
+ switch (pUrb->enmType)
+ {
+ case VUSBXFERTYPE_ISOC:
+ chEptType = 'Z';
+ break;
+ case VUSBXFERTYPE_BULK:
+ chEptType = 'B';
+ break;
+ case VUSBXFERTYPE_INTR:
+ chEptType = 'I';
+ break;
+ case VUSBXFERTYPE_CTRL:
+ case VUSBXFERTYPE_MSG:
+ chEptType = 'C';
+ break;
+ default:
+ AssertMsgFailed(("invalid transfer type %d\n", pUrb->enmType));
+ }
+
+ if (pUrb->enmDir == VUSBDIRECTION_IN)
+ chDir = 'i';
+ else if (pUrb->enmDir == VUSBDIRECTION_OUT)
+ chDir = 'o';
+ else if (pUrb->enmDir == VUSBDIRECTION_SETUP)
+ chDir = 'o';
+
+ RT_ZERO(aszLineBuf);
+
+ /* Assemble the static part. */
+ size_t cch = RTStrPrintf(&aszLineBuf[0], sizeof(aszLineBuf), "%p %llu %c %c%c:%u:%u:%u ",
+ pUrb, RTTimeNanoTS() / RT_NS_1US, chEvtType, chEptType, chDir,
+ 0, pUrb->DstAddress, pUrb->EndPt | (pUrb->enmDir == VUSBDIRECTION_IN ? 0x80 : 0x00));
+ int rc = pThis->pStrm->pfnWrite(pThis->pStrm, &aszLineBuf[0], cch);
+ if (RT_SUCCESS(rc))
+ {
+ /* Log the setup packet for control requests, the status otherwise. */
+ if ( (pUrb->enmType == VUSBXFERTYPE_CTRL || pUrb->enmType == VUSBXFERTYPE_MSG)
+ && enmEvent == VUSBSNIFFEREVENT_SUBMIT)
+ {
+ PVUSBSETUP pSetup = (PVUSBSETUP)pUrb->abData;
+
+ cch = RTStrPrintf(&aszLineBuf[0], sizeof(aszLineBuf), "s %02x %02x %04x %04x %04x ",
+ pSetup->bmRequestType, pSetup->bRequest, pSetup->wValue,
+ pSetup->wIndex, pSetup->wLength);
+ rc = pThis->pStrm->pfnWrite(pThis->pStrm, &aszLineBuf[0], cch);
+ }
+ else
+ {
+ bool fLogAdditionalStatus = pUrb->enmType == VUSBXFERTYPE_ISOC
+ || pUrb->enmType == VUSBXFERTYPE_INTR;
+
+ cch = RTStrPrintf(&aszLineBuf[0], sizeof(aszLineBuf), "%d%s", pUrb->enmStatus,
+ fLogAdditionalStatus ? "" : " ");
+
+ /* There are additional fields to log for isochronous and interrupt URBs. */
+ if (pUrb->enmType == VUSBXFERTYPE_ISOC)
+ {
+ if (enmEvent == VUSBSNIFFEREVENT_COMPLETE)
+ {
+ uint32_t u32ErrorCount = 0;
+
+ for (unsigned i = 0; i < pUrb->cIsocPkts; i++)
+ if ( pUrb->aIsocPkts[i].enmStatus != VUSBSTATUS_OK
+ && pUrb->aIsocPkts[i].enmStatus != VUSBSTATUS_NOT_ACCESSED)
+ u32ErrorCount++;
+
+ cch += RTStrPrintf(&aszLineBuf[cch], sizeof(aszLineBuf) - cch, ":%u:%u:%u ",
+ 1 /* Interval */, 0 /* Frame number */, u32ErrorCount);
+ }
+ else
+ cch += RTStrPrintf(&aszLineBuf[cch], sizeof(aszLineBuf) - cch, ":%u:%u ",
+ 1 /* Interval */, 0 /* Frame number */);
+ }
+ else if (pUrb->enmType == VUSBXFERTYPE_INTR)
+ cch += RTStrPrintf(&aszLineBuf[cch], sizeof(aszLineBuf) - cch, ":%u ",
+ 1 /* Interval */);
+
+ rc = pThis->pStrm->pfnWrite(pThis->pStrm, &aszLineBuf[0], cch);
+ }
+
+ /* Log the packet descriptors for isochronous URBs. */
+ if ( RT_SUCCESS(rc)
+ && pUrb->enmType == VUSBXFERTYPE_ISOC)
+ {
+ cch = RTStrPrintf(&aszLineBuf[0], sizeof(aszLineBuf), "%u ", pUrb->cIsocPkts);
+ rc = pThis->pStrm->pfnWrite(pThis->pStrm, &aszLineBuf[0], cch);
+ for (unsigned i = 0; i < pUrb->cIsocPkts && RT_SUCCESS(rc); i++)
+ {
+ cch = RTStrPrintf(&aszLineBuf[0], sizeof(aszLineBuf), "%d:%u:%u ",
+ pUrb->aIsocPkts[i].enmStatus, pUrb->aIsocPkts[i].off,
+ pUrb->aIsocPkts[i].cb);
+ rc = pThis->pStrm->pfnWrite(pThis->pStrm, &aszLineBuf[0], cch);
+ }
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ /* Print data length */
+ cch = RTStrPrintf(&aszLineBuf[0], sizeof(aszLineBuf), "%d n\n", pUrb->cbData);
+ rc = pThis->pStrm->pfnWrite(pThis->pStrm, &aszLineBuf[0], cch);
+ }
+
+ /** @todo Dump the data */
+ }
+
+ return rc;
+}
+
+/**
+ * VUSB sniffer format writer.
+ */
+const VUSBSNIFFERFMT g_VUsbSnifferFmtUsbMon =
+{
+ /** szName */
+ "USBMON",
+ /** pszDesc */
+ "UsbMon format writer compatible with vusb-analyzer: http://vusb-analyzer.sourceforge.net",
+ /** papszFileExts */
+ &s_apszFileExts[0],
+ /** cbFmt */
+ sizeof(VUSBSNIFFERFMTINT),
+ /** pfnInit */
+ vusbSnifferFmtUsbMonInit,
+ /** pfnDestroy */
+ vusbSnifferFmtUsbMonDestroy,
+ /** pfnRecordEvent */
+ vusbSnifferFmtUsbMonRecordEvent
+};
+