/* $Id: VUSBSnifferUsbMon.cpp 98103 2023-01-17 14:15:46Z vboxsync $ */ /** @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 . * * SPDX-License-Identifier: GPL-3.0-only */ /********************************************************************************************************************************* * Header Files * *********************************************************************************************************************************/ #define LOG_GROUP LOG_GROUP_DRV_VUSB #include #include #include #include #include #include #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 };