VirtualBox

source: vbox/trunk/src/VBox/ValidationKit/utils/usb/UsbTest.cpp@ 60522

Last change on this file since 60522 was 60522, checked in by vboxsync, 9 years ago

ValidationKit/usb: Fixes, basic compliance testing works finally

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 16.6 KB
Line 
1/* $Id: UsbTest.cpp 60522 2016-04-15 14:34:35Z vboxsync $ */
2/** @file
3 * UsbTest - User frontend for the Linux usbtest USB test and benchmarking module.
4 * Integrates with our test framework for nice outputs.
5 */
6
7/*
8 * Copyright (C) 2014-2015 Oracle Corporation
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 *
18 * The contents of this file may alternatively be used under the terms
19 * of the Common Development and Distribution License Version 1.0
20 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
21 * VirtualBox OSE distribution, in which case the provisions of the
22 * CDDL are applicable instead of those of the GPL.
23 *
24 * You may elect to license modified versions of this file under the
25 * terms and conditions of either the GPL or the CDDL or both.
26 */
27
28
29/*********************************************************************************************************************************
30* Header Files *
31*********************************************************************************************************************************/
32#include <iprt/dir.h>
33#include <iprt/err.h>
34#include <iprt/file.h>
35#include <iprt/getopt.h>
36#include <iprt/path.h>
37#include <iprt/param.h>
38#include <iprt/process.h>
39#include <iprt/stream.h>
40#include <iprt/string.h>
41#include <iprt/test.h>
42
43#include <unistd.h>
44#include <errno.h>
45#include <limits.h>
46
47#include <sys/types.h>
48#include <sys/stat.h>
49#include <fcntl.h>
50
51#include <sys/ioctl.h>
52#include <linux/usbdevice_fs.h>
53
54
55/*********************************************************************************************************************************
56* Defined Constants And Macros *
57*********************************************************************************************************************************/
58
59/*********************************************************************************************************************************
60* Structures and Typedefs *
61*********************************************************************************************************************************/
62
63/**
64 * USB test request data.
65 * There is no public header with this information so we define it ourself here.
66 */
67typedef struct USBTESTPARMS
68{
69 /** Specifies the test to run. */
70 uint32_t idxTest;
71 /** How many iterations the test should be executed. */
72 uint32_t cIterations;
73 /** Size of the data packets. */
74 uint32_t cbData;
75 /** Size of */
76 uint32_t cbVariation;
77 /** Length of the S/G list for the test. */
78 uint32_t cSgLength;
79 /** Returned time data after completing the test. */
80 struct timeval TimeTest;
81} USBTESTPARAMS;
82/** Pointer to a test parameter structure. */
83typedef USBTESTPARAMS *PUSBTESTPARAMS;
84
85/**
86 * USB device descriptor. Used to search for the test device based
87 * on the vendor and product id.
88 */
89#pragma pack(1)
90typedef struct USBDEVDESC
91{
92 uint8_t bLength;
93 uint8_t bDescriptorType;
94 uint16_t bcdUSB;
95 uint8_t bDeviceClass;
96 uint8_t bDeviceSubClass;
97 uint8_t bDeviceProtocol;
98 uint8_t bMaxPacketSize0;
99 uint16_t idVendor;
100 uint16_t idProduct;
101 uint16_t bcdDevice;
102 uint8_t iManufacturer;
103 uint8_t iProduct;
104 uint8_t iSerialNumber;
105 uint8_t bNumConfigurations;
106} USBDEVDESC;
107#pragma pack()
108
109#define USBTEST_REQUEST _IOWR('U', 100, USBTESTPARMS)
110
111/**
112 * Callback to set up the test parameters for a specific test.
113 *
114 * @returns IPRT status code.
115 * @retval VINF_SUCCESS if setting the parameters up succeeded. Any other error code
116 * otherwise indicating the kind of error.
117 * @param idxTest The test index.
118 * @param pszTest Test name.
119 * @param pParams The USB test parameters to set up.
120 */
121typedef DECLCALLBACK(int) FNUSBTESTPARAMSSETUP(unsigned idxTest, const char *pszTest, PUSBTESTPARAMS pParams);
122/** Pointer to a USB test parameters setup callback. */
123typedef FNUSBTESTPARAMSSETUP *PFNUSBTESTPARAMSSETUP;
124
125/**
126 * USB test descriptor.
127 */
128typedef struct USBTESTDESC
129{
130 /** (Sort of) Descriptive test name. */
131 const char *pszName;
132 /** Flag whether the test is excluded. */
133 bool fExcluded;
134 /** The parameter setup callback. */
135 PFNUSBTESTPARAMSSETUP pfnParamsSetup;
136} USBTESTDESC;
137/** Pointer a USB test descriptor. */
138typedef USBTESTDESC *PUSBTESTDESC;
139
140/*********************************************************************************************************************************
141* Global Variables *
142*********************************************************************************************************************************/
143
144/** Some forward method declarations. */
145static DECLCALLBACK(int) usbTestParamsSetupReadWrite(unsigned idxTest, const char *pszTest, PUSBTESTPARAMS pParams);
146static DECLCALLBACK(int) usbTestParamsSetupControlWrites(unsigned idxTest, const char *pszTest, PUSBTESTPARAMS pParams);
147
148/** Command line parameters */
149static const RTGETOPTDEF g_aCmdOptions[] =
150{
151 {"--device", 'd', RTGETOPT_REQ_STRING },
152 {"--help", 'h', RTGETOPT_REQ_NOTHING},
153 {"--exclude", 'e', RTGETOPT_REQ_UINT32}
154};
155
156static USBTESTDESC g_aTests[] =
157{
158 /* pszTest fExcluded pfnParamsSetup */
159 {"NOP", false, usbTestParamsSetupReadWrite},
160 {"Non-queued Bulk write", false, usbTestParamsSetupReadWrite},
161 {"Non-queued Bulk read", false, usbTestParamsSetupReadWrite},
162 {"Non-queued Bulk write variabe size", false, usbTestParamsSetupReadWrite},
163 {"Non-queued Bulk read variabe size", false, usbTestParamsSetupReadWrite},
164 {"Queued Bulk write", false, usbTestParamsSetupReadWrite},
165 {"Queued Bulk read", false, usbTestParamsSetupReadWrite},
166 {"Queued Bulk write variabe size", false, usbTestParamsSetupReadWrite},
167 {"Queued Bulk read variabe size", false, usbTestParamsSetupReadWrite},
168 {"Chapter 9 Control Test", false, usbTestParamsSetupReadWrite},
169 {"Queued control messaging", false, usbTestParamsSetupReadWrite},
170 {"Unlink reads", false, usbTestParamsSetupReadWrite},
171 {"Unlink writes", false, usbTestParamsSetupReadWrite},
172 {"Set/Clear halts", false, usbTestParamsSetupReadWrite},
173 {"Control writes", false, usbTestParamsSetupControlWrites},
174 {"Isochronous write", false, usbTestParamsSetupReadWrite},
175 {"Isochronous read", false, usbTestParamsSetupReadWrite},
176 {"Bulk write unaligned (DMA)", false, usbTestParamsSetupReadWrite},
177 {"Bulk read unaligned (DMA)", false, usbTestParamsSetupReadWrite},
178 {"Bulk write unaligned (no DMA)", false, usbTestParamsSetupReadWrite},
179 {"Bulk read unaligned (no DMA)", false, usbTestParamsSetupReadWrite},
180 {"Control writes unaligned", false, usbTestParamsSetupControlWrites},
181 {"Isochronous write unaligned", false, usbTestParamsSetupReadWrite},
182 {"Isochronous read unaligned", false, usbTestParamsSetupReadWrite},
183 {"Unlink queued Bulk", false, usbTestParamsSetupReadWrite}
184};
185
186/** The test handle. */
187static RTTEST g_hTest;
188
189/**
190 * Setup callback for basic read/write (bulk, isochronous) tests.
191 *
192 * @copydoc FNUSBTESTPARAMSSETUP
193 */
194static DECLCALLBACK(int) usbTestParamsSetupReadWrite(unsigned idxTest, const char *pszTest, PUSBTESTPARAMS pParams)
195{
196 NOREF(idxTest);
197 NOREF(pszTest);
198
199 pParams->cIterations = 1000;
200 pParams->cbData = 512;
201 pParams->cbVariation = 512;
202 pParams->cSgLength = 32;
203
204 return VINF_SUCCESS;
205}
206
207/**
208 * Setup callback for the control writes test.
209 *
210 * @copydoc FNUSBTESTPARAMSSETUP
211 */
212static DECLCALLBACK(int) usbTestParamsSetupControlWrites(unsigned idxTest, const char *pszTest, PUSBTESTPARAMS pParams)
213{
214 NOREF(idxTest);
215 NOREF(pszTest);
216
217 pParams->cIterations = 1000;
218 pParams->cbData = 512;
219 /*
220 * Must be smaller than cbData or the parameter check in the usbtest module fails,
221 * no idea yet why it must be this.
222 */
223 pParams->cbVariation = 256;
224 pParams->cSgLength = 32;
225
226 return VINF_SUCCESS;
227}
228
229/**
230 * Shows tool usage text.
231 */
232static void usbTestUsage(PRTSTREAM pStrm)
233{
234 char szExec[RTPATH_MAX];
235 RTStrmPrintf(pStrm, "usage: %s [options]\n",
236 RTPathFilename(RTProcGetExecutablePath(szExec, sizeof(szExec))));
237 RTStrmPrintf(pStrm, "\n");
238 RTStrmPrintf(pStrm, "options: \n");
239
240
241 for (unsigned i = 0; i < RT_ELEMENTS(g_aCmdOptions); i++)
242 {
243 const char *pszHelp;
244 switch (g_aCmdOptions[i].iShort)
245 {
246 case 'h':
247 pszHelp = "Displays this help and exit";
248 break;
249 case 'd':
250 pszHelp = "Use the specified test device";
251 break;
252 case 'e':
253 pszHelp = "Exclude the given test id from the list";
254 break;
255 default:
256 pszHelp = "Option undocumented";
257 break;
258 }
259 char szOpt[256];
260 RTStrPrintf(szOpt, sizeof(szOpt), "%s, -%c", g_aCmdOptions[i].pszLong, g_aCmdOptions[i].iShort);
261 RTStrmPrintf(pStrm, " %-20s%s\n", szOpt, pszHelp);
262 }
263}
264
265/**
266 * Search for a USB test device and return the device path.
267 *
268 * @returns Path to the USB test device or NULL if none was found.
269 */
270static char *usbTestFindDevice(void)
271{
272 /*
273 * Very crude and quick way to search for the correct test device.
274 * Assumption is that the path looks like /dev/bus/usb/%3d/%3d.
275 */
276 char *pszDevPath = NULL;
277
278 PRTDIR pDirUsb = NULL;
279 int rc = RTDirOpen(&pDirUsb, "/dev/bus/usb");
280 if (RT_SUCCESS(rc))
281 {
282 do
283 {
284 RTDIRENTRY DirUsbBus;
285 rc = RTDirRead(pDirUsb, &DirUsbBus, NULL);
286 if (RT_SUCCESS(rc))
287 {
288 char aszPath[RTPATH_MAX + 1];
289 RTStrPrintf(&aszPath[0], RT_ELEMENTS(aszPath), "/dev/bus/usb/%s", DirUsbBus.szName);
290
291 PRTDIR pDirUsbBus = NULL;
292 rc = RTDirOpen(&pDirUsbBus, &aszPath[0]);
293 if (RT_SUCCESS(rc))
294 {
295 do
296 {
297 RTDIRENTRY DirUsbDev;
298 rc = RTDirRead(pDirUsbBus, &DirUsbDev, NULL);
299 if (RT_SUCCESS(rc))
300 {
301 char aszPathDev[RTPATH_MAX + 1];
302 RTStrPrintf(&aszPathDev[0], RT_ELEMENTS(aszPathDev), "/dev/bus/usb/%s/%s",
303 DirUsbBus.szName, DirUsbDev.szName);
304
305 RTFILE hFileDev;
306 rc = RTFileOpen(&hFileDev, aszPathDev, RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
307 if (RT_SUCCESS(rc))
308 {
309 USBDEVDESC DevDesc;
310
311 rc = RTFileRead(hFileDev, &DevDesc, sizeof(DevDesc), NULL);
312 RTFileClose(hFileDev);
313
314 if ( RT_SUCCESS(rc)
315 && DevDesc.idVendor == 0x0525
316 && DevDesc.idProduct == 0xa4a0)
317 pszDevPath = RTStrDup(aszPathDev);
318 }
319
320 rc = VINF_SUCCESS;
321 }
322 else if (rc != VERR_NO_MORE_FILES)
323 rc = VINF_SUCCESS;
324
325 } while ( RT_SUCCESS(rc)
326 && !pszDevPath);
327
328 rc = VINF_SUCCESS;
329 RTDirClose(pDirUsbBus);
330 }
331 }
332 else if (rc != VERR_NO_MORE_FILES)
333 rc = VINF_SUCCESS;
334 } while ( RT_SUCCESS(rc)
335 && !pszDevPath);
336
337 RTDirClose(pDirUsb);
338 }
339
340 return pszDevPath;
341}
342
343static int usbTestIoctl(int iDevFd, int iInterface, PUSBTESTPARAMS pParams)
344{
345 struct usbdevfs_ioctl IoCtlData;
346
347 IoCtlData.ifno = iInterface;
348 IoCtlData.ioctl_code = (int)USBTEST_REQUEST;
349 IoCtlData.data = pParams;
350 return ioctl(iDevFd, USBDEVFS_IOCTL, &IoCtlData);
351}
352
353/**
354 * Test execution worker.
355 *
356 * @returns nothing.
357 * @param pszDevice The device to use for testing.
358 */
359static void usbTestExec(const char *pszDevice)
360{
361 int iDevFd;
362
363 RTTestSub(g_hTest, "Opening device");
364 iDevFd = open(pszDevice, O_RDWR);
365 if (iDevFd != -1)
366 {
367 USBTESTPARAMS Params;
368
369 RTTestPassed(g_hTest, "Opening device successful\n");
370
371 for (unsigned i = 0; i < RT_ELEMENTS(g_aTests); i++)
372 {
373 RTTestSub(g_hTest, g_aTests[i].pszName);
374
375 if (g_aTests[i].fExcluded)
376 {
377 RTTestSkipped(g_hTest, "Excluded from list");
378 continue;
379 }
380
381 int rc = g_aTests[i].pfnParamsSetup(i, g_aTests[i].pszName, &Params);
382 if (RT_SUCCESS(rc))
383 {
384 Params.idxTest = i;
385
386 /* Assume the test interface has the number 0 for now. */
387 int rcPosix = usbTestIoctl(iDevFd, 0, &Params);
388 if (rcPosix < 0 && errno == EOPNOTSUPP)
389 {
390 RTTestSkipped(g_hTest, "Not supported");
391 continue;
392 }
393
394 if (rcPosix < 0)
395 RTTestFailed(g_hTest, "Test failed with %Rrc\n", RTErrConvertFromErrno(errno));
396 else
397 {
398 uint64_t u64Ns = Params.TimeTest.tv_sec * RT_NS_1SEC + Params.TimeTest.tv_usec * RT_NS_1US;
399 RTTestValue(g_hTest, "Runtime", u64Ns, RTTESTUNIT_NS);
400 }
401 }
402 else
403 RTTestFailed(g_hTest, "Setting up test parameters failed with %Rrc\n", rc);
404 RTTestSubDone(g_hTest);
405 }
406
407 close(iDevFd);
408 }
409 else
410 RTTestFailed(g_hTest, "Opening device failed with %Rrc\n", RTErrConvertFromErrno(errno));
411
412}
413
414int main(int argc, char *argv[])
415{
416 /*
417 * Init IPRT and globals.
418 */
419 int rc = RTTestInitAndCreate("UsbTest", &g_hTest);
420 if (rc)
421 return rc;
422
423 /*
424 * Default values.
425 */
426 const char *pszDevice = NULL;
427
428 RTGETOPTUNION ValueUnion;
429 RTGETOPTSTATE GetState;
430 RTGetOptInit(&GetState, argc, argv, g_aCmdOptions, RT_ELEMENTS(g_aCmdOptions), 1, 0 /* fFlags */);
431 while ((rc = RTGetOpt(&GetState, &ValueUnion)))
432 {
433 switch (rc)
434 {
435 case 'h':
436 usbTestUsage(g_pStdOut);
437 return RTEXITCODE_SUCCESS;
438 case 'd':
439 pszDevice = ValueUnion.psz;
440 break;
441 case 'e':
442 if (ValueUnion.u32 < RT_ELEMENTS(g_aTests))
443 g_aTests[ValueUnion.u32].fExcluded = true;
444 else
445 {
446 RTTestPrintf(g_hTest, RTTESTLVL_FAILURE, "Invalid test number passed to --exclude\n");
447 RTTestErrorInc(g_hTest);
448 return RTGetOptPrintError(VERR_INVALID_PARAMETER, &ValueUnion);
449 }
450 break;
451 default:
452 return RTGetOptPrintError(rc, &ValueUnion);
453 }
454 }
455
456 /*
457 * Start testing.
458 */
459 RTTestBanner(g_hTest);
460
461 /* Find the first test device if none was given. */
462 if (!pszDevice)
463 {
464 RTTestSub(g_hTest, "Detecting device");
465 pszDevice = usbTestFindDevice();
466 if (!pszDevice)
467 RTTestFailed(g_hTest, "Failed to find suitable device\n");
468
469 RTTestSubDone(g_hTest);
470 }
471
472 if (pszDevice)
473 usbTestExec(pszDevice);
474
475 RTEXITCODE rcExit = RTTestSummaryAndDestroy(g_hTest);
476 return rcExit;
477}
478
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette