/* $Id: tstUsbMouse.cpp 47571 2013-08-07 09:49:33Z vboxsync $ */ /** @file * tstUsbMouse.cpp - testcase USB mouse and tablet devices. */ /* * Copyright (C) 2013 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 * *******************************************************************************/ #include "VBoxDD.h" #include #include #include #include #include /** Test mouse driver structure. */ typedef struct DRVTSTMOUSE { /** The USBHID structure. */ struct USBHID *pUsbHid; /** The base interface for the mouse driver. */ PDMIBASE IBase; /** Our mouse connector interface. */ PDMIMOUSECONNECTOR IConnector; /** The base interface of the attached mouse port. */ PPDMIBASE pDrvBase; /** The mouse port interface of the attached mouse port. */ PPDMIMOUSEPORT pDrv; /** Is relative mode currently supported? */ bool fRel; /** Is absolute mode currently supported? */ bool fAbs; /** Is multi-touch mode currently supported? */ bool fMT; } DRVTSTMOUSE, *PDRVTSTMOUSE; /** Global mouse driver variable. * @todo To be improved some time. */ static DRVTSTMOUSE s_drvTstMouse; /** @interface_method_impl{PDMUSBHLPR3,pfnVMSetErrorV} */ static DECLCALLBACK(int) tstVMSetErrorV(PPDMUSBINS pUsbIns, int rc, RT_SRC_POS_DECL, const char *pszFormat, va_list va) { NOREF(pUsbIns); RTPrintf("Error: %s:%u:%s:", RT_SRC_POS_ARGS); RTPrintfV(pszFormat, va); return rc; } /** @interface_method_impl{PDMUSBHLPR3,pfnDriverAttach} */ /** @todo We currently just take the driver interface from the global * variable. This is sufficient for a unit test but still a bit sad. */ static DECLCALLBACK(int) tstDriverAttach(PPDMUSBINS pUsbIns, RTUINT iLun, PPDMIBASE pBaseInterface, PPDMIBASE *ppBaseInterface, const char *pszDesc) { NOREF(iLun); NOREF(pszDesc); s_drvTstMouse.pDrvBase = pBaseInterface; s_drvTstMouse.pDrv = PDMIBASE_QUERY_INTERFACE(pBaseInterface, PDMIMOUSEPORT); *ppBaseInterface = &s_drvTstMouse.IBase; return VINF_SUCCESS; } static PDMUSBHLP s_tstUsbHlp; /** * @interface_method_impl{PDMIBASE,pfnQueryInterface} */ static DECLCALLBACK(void *) tstMouseQueryInterface(PPDMIBASE pInterface, const char *pszIID) { PDRVTSTMOUSE pThis = RT_FROM_MEMBER(pInterface, DRVTSTMOUSE, IBase); PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThis->IBase); PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMOUSECONNECTOR, &pThis->IConnector); return NULL; } /** * @interface_method_impl{PDMIMOUSECONNECTOR,pfnReportModes} */ static DECLCALLBACK(void) tstMouseReportModes(PPDMIMOUSECONNECTOR pInterface, bool fRel, bool fAbs, bool fMT) { PDRVTSTMOUSE pDrv = RT_FROM_MEMBER(pInterface, DRVTSTMOUSE, IConnector); pDrv->fRel = fRel; pDrv->fAbs = fAbs; pDrv->fMT = fMT; } static int tstMouseConstruct(int iInstance, const char *pcszMode, uint8_t u8CoordShift, PPDMUSBINS *ppThis) { int rc = VERR_NO_MEMORY; PPDMUSBINS pThis = (PPDMUSBINS)RTMemAllocZ( sizeof(*pThis) + g_UsbHidMou.cbInstance); PCFGMNODE pCfg = NULL; if (pThis) pCfg = CFGMR3CreateTree(NULL); if (pCfg) rc = CFGMR3InsertString(pCfg, "Mode", pcszMode); if (RT_SUCCESS(rc)) rc = CFGMR3InsertInteger(pCfg, "CoordShift", u8CoordShift); if (RT_SUCCESS(rc)) { s_drvTstMouse.pDrv = NULL; s_drvTstMouse.pDrvBase = NULL; pThis->iInstance = iInstance; pThis->pHlpR3 = &s_tstUsbHlp; rc = g_UsbHidMou.pfnConstruct(pThis, iInstance, pCfg, NULL); if (RT_SUCCESS(rc)) { *ppThis = pThis; return rc; } } /* Failure */ if (pCfg) CFGMR3DestroyTree(pCfg); if (pThis) RTMemFree(pThis); return rc; } static void testConstructAndDestruct(RTTEST hTest) { PPDMUSBINS pThis; RTTestSub(hTest, "simple construction and destruction"); int rc = tstMouseConstruct(0, "relative", 1, &pThis); RTTEST_CHECK_RC_OK(hTest, rc); if (pThis) g_UsbHidMou.pfnDestruct(pThis); } static void testSendPositionRel(RTTEST hTest) { PPDMUSBINS pThis = NULL; VUSBURB Urb; RTTestSub(hTest, "sending a relative position event"); int rc = tstMouseConstruct(0, "relative", 1, &pThis); RT_ZERO(Urb); if (RT_SUCCESS(rc)) rc = g_UsbHidMou.pfnUsbReset(pThis, false); if (RT_SUCCESS(rc) && !s_drvTstMouse.pDrv) rc = VERR_PDM_MISSING_INTERFACE; RTTEST_CHECK_RC_OK(hTest, rc); if (RT_SUCCESS(rc)) { s_drvTstMouse.pDrv->pfnPutEvent(s_drvTstMouse.pDrv, 123, -16, 1, -1, 3); Urb.EndPt = 0x01; rc = g_UsbHidMou.pfnUrbQueue(pThis, &Urb); } if (RT_SUCCESS(rc)) { PVUSBURB pUrb = g_UsbHidMou.pfnUrbReap(pThis, 0); if (pUrb) { if (pUrb == &Urb) { if ( Urb.abData[0] != 3 /* Buttons */ || Urb.abData[1] != 123 /* x */ || Urb.abData[2] != 240 /* 256 - y */ || Urb.abData[3] != 255 /* z */) rc = VERR_GENERAL_FAILURE; } else rc = VERR_GENERAL_FAILURE; } else rc = VERR_GENERAL_FAILURE; } RTTEST_CHECK_RC_OK(hTest, rc); if (pThis) g_UsbHidMou.pfnDestruct(pThis); } static void testSendPositionAbs(RTTEST hTest) { PPDMUSBINS pThis = NULL; VUSBURB Urb; RTTestSub(hTest, "sending an absolute position event"); int rc = tstMouseConstruct(0, "absolute", 1, &pThis); RT_ZERO(Urb); if (RT_SUCCESS(rc)) { rc = g_UsbHidMou.pfnUsbReset(pThis, false); } if (RT_SUCCESS(rc)) { if (s_drvTstMouse.pDrv) s_drvTstMouse.pDrv->pfnPutEventAbs(s_drvTstMouse.pDrv, 300, 200, 1, 0, 3); else rc = VERR_PDM_MISSING_INTERFACE; } if (RT_SUCCESS(rc)) { Urb.EndPt = 0x01; rc = g_UsbHidMou.pfnUrbQueue(pThis, &Urb); } if (RT_SUCCESS(rc)) { PVUSBURB pUrb = g_UsbHidMou.pfnUrbReap(pThis, 0); if (pUrb) { if (pUrb == &Urb) { if ( *(uint16_t *)&Urb.abData[0] != 150 /* x >> 1 */ || *(uint16_t *)&Urb.abData[2] != 100 /* y >> 1 */ || Urb.abData[4] != 3 /* Buttons */) rc = VERR_GENERAL_FAILURE; } else rc = VERR_GENERAL_FAILURE; } else rc = VERR_GENERAL_FAILURE; } RTTEST_CHECK_RC_OK(hTest, rc); if (pThis) g_UsbHidMou.pfnDestruct(pThis); } #if 0 /** @todo PDM interface was updated. This is not working anymore. */ static void testSendPositionMT(RTTEST hTest) { PPDMUSBINS pThis = NULL; VUSBURB Urb; RTTestSub(hTest, "sending a multi-touch position event"); int rc = tstMouseConstruct(0, "multitouch", 1, &pThis); RT_ZERO(Urb); if (RT_SUCCESS(rc)) { rc = g_UsbHidMou.pfnUsbReset(pThis, false); } if (RT_SUCCESS(rc)) { if (s_drvTstMouse.pDrv) s_drvTstMouse.pDrv->pfnPutEventMT(s_drvTstMouse.pDrv, 300, 200, 2, 3); else rc = VERR_PDM_MISSING_INTERFACE; } if (RT_SUCCESS(rc)) { Urb.EndPt = 0x01; rc = g_UsbHidMou.pfnUrbQueue(pThis, &Urb); } if (RT_SUCCESS(rc)) { PVUSBURB pUrb = g_UsbHidMou.pfnUrbReap(pThis, 0); if (pUrb) { if (pUrb == &Urb) { if ( Urb.abData[0] != 1 /* Report ID */ || Urb.abData[1] != 3 /* Contact flags */ || *(uint16_t *)&Urb.abData[2] != 150 /* x >> 1 */ || *(uint16_t *)&Urb.abData[4] != 100 /* y >> 1 */ || Urb.abData[6] != 2 /* Contact number */) rc = VERR_GENERAL_FAILURE; } else rc = VERR_GENERAL_FAILURE; } else rc = VERR_GENERAL_FAILURE; } RTTEST_CHECK_RC_OK(hTest, rc); if (pThis) g_UsbHidMou.pfnDestruct(pThis); } #endif int main() { /* * Init the runtime, test and say hello. */ RTTEST hTest; PDRVTSTMOUSE pThis; int rc = RTTestInitAndCreate("tstUsbMouse", &hTest); if (rc) return rc; RTTestBanner(hTest); /* Set up our faked PDMUSBHLP interface. */ s_tstUsbHlp.pfnVMSetErrorV = tstVMSetErrorV; s_tstUsbHlp.pfnDriverAttach = tstDriverAttach; /* Set up our global mouse driver */ s_drvTstMouse.IBase.pfnQueryInterface = tstMouseQueryInterface; s_drvTstMouse.IConnector.pfnReportModes = tstMouseReportModes; /* * Run the tests. */ testConstructAndDestruct(hTest); testSendPositionRel(hTest); testSendPositionAbs(hTest); /* testSendPositionMT(hTest); */ return RTTestSummaryAndDestroy(hTest); }