VirtualBox

source: vbox/trunk/src/VBox/ValidationKit/utils/usb/UsbTestServicePlatform-linux.cpp@ 60801

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

ValidationKit/UsbTestService: Fixes to support testing super-speed devices. The gadget functions hard code the maximum supported USB speed. When the dummy HCD is enabled for super

speed devices we can't control the desired speed of the device so it is always super speed now breaking testing of lower speeds. As a workaround
the USB test server has a modified kernel with a new dummy HCD driver using a slightly different naming for super-speed devices only. The original
driver will be used for the remaining speeds. Add support for this configuration in the UsbTestService.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 15.2 KB
Line 
1/* $Id: UsbTestServicePlatform-linux.cpp 60801 2016-05-03 08:53:41Z vboxsync $ */
2/** @file
3 * UsbTestServ - Remote USB test configuration and execution server, Platform
4 * specific helpers - Linux version.
5 */
6
7/*
8 * Copyright (C) 2016 Oracle Corporation
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.215389.xyz. 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
19
20/*********************************************************************************************************************************
21* Header Files *
22*********************************************************************************************************************************/
23
24#include <iprt/asm.h>
25#include <iprt/cdefs.h>
26#include <iprt/ctype.h>
27#include <iprt/dir.h>
28#include <iprt/env.h>
29#include <iprt/mem.h>
30#include <iprt/path.h>
31#include <iprt/process.h>
32#include <iprt/string.h>
33#include <iprt/types.h>
34
35#include <iprt/linux/sysfs.h>
36
37#include "UsbTestServicePlatform.h"
38
39/*********************************************************************************************************************************
40* Constants And Macros, Structures and Typedefs *
41*********************************************************************************************************************************/
42
43/** Where the dummy_hcd.* and dummy_udc.* entries are stored. */
44#define UTS_PLATFORM_LNX_DUMMY_HCD_PATH "/sys/devices/platform"
45
46/**
47 * A USB bus provided by the dummy HCD.
48 */
49typedef struct UTSPLATFORMLNXDUMMYHCDBUS
50{
51 /** The bus ID on the host the dummy HCD is serving. */
52 uint32_t uBusId;
53 /** Flag whether this is a super speed bus. */
54 bool fSuperSpeed;
55} UTSPLATFORMLNXDUMMYHCDBUS;
56/** Pointer to a Dummy HCD bus. */
57typedef UTSPLATFORMLNXDUMMYHCDBUS *PUTSPLATFORMLNXDUMMYHCDBUS;
58
59/**
60 * A dummy UDC descriptor.
61 */
62typedef struct UTSPLATFORMLNXDUMMYHCD
63{
64 /** Index of the dummy hcd entry. */
65 uint32_t idxDummyHcd;
66 /** Name for the dummy HCD. */
67 const char *pszHcdName;
68 /** Name for the accompanying dummy HCD. */
69 const char *pszUdcName;
70 /** Flag whether this HCD is free for use. */
71 bool fAvailable;
72 /** Flag whether this HCD contains a super speed capable bus. */
73 bool fSuperSpeed;
74 /** Number of busses this HCD instance serves. */
75 unsigned cBusses;
76 /** Bus structures the HCD serves.*/
77 PUTSPLATFORMLNXDUMMYHCDBUS paBusses;
78} UTSPLATFORMLNXDUMMYHCD;
79/** Pointer to a dummy HCD entry. */
80typedef UTSPLATFORMLNXDUMMYHCD *PUTSPLATFORMLNXDUMMYHCD;
81
82/*********************************************************************************************************************************
83* Global Variables *
84*********************************************************************************************************************************/
85
86/** Array of dummy HCD entries. */
87static PUTSPLATFORMLNXDUMMYHCD g_paDummyHcd = NULL;
88/** Number of Dummy hCD entries in the array. */
89static unsigned g_cDummyHcd = 0;
90
91
92/*********************************************************************************************************************************
93* Internal Functions *
94*********************************************************************************************************************************/
95
96
97/**
98 * Queries the assigned busses for the given dummy HCD instance.
99 *
100 * @returns IPRT status code.
101 * @param pHcd The dummy HCD bus instance.
102 * @param pszHcdName The base HCD name.
103 */
104static int utsPlatformLnxDummyHcdQueryBusses(PUTSPLATFORMLNXDUMMYHCD pHcd, const char *pszHcdName)
105{
106 int rc = VINF_SUCCESS;
107 char aszPath[RTPATH_MAX + 1];
108 unsigned idxBusCur = 0;
109 unsigned idxBusMax = 0;
110
111 size_t cchPath = RTStrPrintf(&aszPath[0], RT_ELEMENTS(aszPath), UTS_PLATFORM_LNX_DUMMY_HCD_PATH "/%s.%u/usb*",
112 pszHcdName, pHcd->idxDummyHcd);
113 if (cchPath == RT_ELEMENTS(aszPath))
114 return VERR_BUFFER_OVERFLOW;
115
116 PRTDIR pDir = NULL;
117 rc = RTDirOpenFiltered(&pDir, aszPath, RTDIRFILTER_WINNT, 0);
118 if (RT_SUCCESS(rc))
119 {
120 do
121 {
122 RTDIRENTRY DirFolderContent;
123 rc = RTDirRead(pDir, &DirFolderContent, NULL);
124 if (RT_SUCCESS(rc))
125 {
126 uint32_t uBusId = 0;
127
128 /* Extract the bus number - it is after "usb", i.e. "usb9" indicates a bus ID of 9. */
129 rc = RTStrToUInt32Ex(&DirFolderContent.szName[3], NULL, 10, &uBusId);
130 if (RT_SUCCESS(rc))
131 {
132 /* Check whether this is a super speed bus. */
133 int64_t iSpeed = 0;
134 bool fSuperSpeed = false;
135 rc = RTLinuxSysFsReadIntFile(10, &iSpeed, UTS_PLATFORM_LNX_DUMMY_HCD_PATH "/%s.%u/%s/speed",
136 pszHcdName, pHcd->idxDummyHcd, DirFolderContent.szName);
137 if ( RT_SUCCESS(rc)
138 && (iSpeed == 5000 || iSpeed == 10000))
139 {
140 fSuperSpeed = true;
141 pHcd->fSuperSpeed = true;
142 }
143
144 /* Add to array of available busses for this HCD. */
145 if (idxBusCur == idxBusMax)
146 {
147 size_t cbNew = (idxBusMax + 10) * sizeof(UTSPLATFORMLNXDUMMYHCDBUS);
148 PUTSPLATFORMLNXDUMMYHCDBUS pNew = (PUTSPLATFORMLNXDUMMYHCDBUS)RTMemRealloc(pHcd->paBusses, cbNew);
149 if (pNew)
150 {
151 idxBusMax += 10;
152 pHcd->paBusses = pNew;
153 }
154 }
155
156 if (idxBusCur < idxBusMax)
157 {
158 pHcd->paBusses[idxBusCur].uBusId = uBusId;
159 pHcd->paBusses[idxBusCur].fSuperSpeed = fSuperSpeed;
160 idxBusCur++;
161 }
162 else
163 rc = VERR_NO_MEMORY;
164 }
165 }
166 } while (RT_SUCCESS(rc));
167
168 pHcd->cBusses = idxBusCur;
169
170 if (rc == VERR_NO_MORE_FILES)
171 rc = VINF_SUCCESS;
172
173 RTDirClose(pDir);
174 }
175
176 return rc;
177}
178
179
180/**
181 * Scans all available HCDs with the given name.
182 *
183 * @returns IPRT status code.
184 * @param pszHcdName The base HCD name.
185 * @param pszUdcName The base UDC name.
186 */
187static int utsPlatformLnxHcdScanByName(const char *pszHcdName, const char *pszUdcName)
188{
189 char aszPath[RTPATH_MAX + 1];
190 size_t cchPath = RTStrPrintf(&aszPath[0], RT_ELEMENTS(aszPath),
191 UTS_PLATFORM_LNX_DUMMY_HCD_PATH "/%s.*", pszHcdName);
192 if (cchPath == RT_ELEMENTS(aszPath))
193 return VERR_BUFFER_OVERFLOW;
194
195 /* Enumerate the available HCD and their bus numbers. */
196 PRTDIR pDir = NULL;
197 int rc = RTDirOpenFiltered(&pDir, aszPath, RTDIRFILTER_WINNT, 0);
198 if (RT_SUCCESS(rc))
199 {
200 unsigned idxHcdCur = g_cDummyHcd;
201 unsigned idxHcdMax = g_cDummyHcd;
202
203 do
204 {
205 RTDIRENTRY DirFolderContent;
206 rc = RTDirRead(pDir, &DirFolderContent, NULL);
207 if (RT_SUCCESS(rc))
208 {
209 /*
210 * Get the HCD index and assigned bus number form the sysfs entries,
211 * Any error here is silently ignored and results in the HCD not being
212 * added to the list of available controllers.
213 */
214 const char *pszIdx = RTStrStr(DirFolderContent.szName, ".");
215 if (pszIdx)
216 {
217 /* Skip the separator and convert number to index. */
218 pszIdx++;
219
220 uint32_t idxHcd = 0;
221 rc = RTStrToUInt32Ex(pszIdx, NULL, 10, &idxHcd);
222 if (RT_SUCCESS(rc))
223 {
224 /* Add to array of available HCDs. */
225 if (idxHcdCur == idxHcdMax)
226 {
227 size_t cbNew = (idxHcdMax + 10) * sizeof(UTSPLATFORMLNXDUMMYHCD);
228 PUTSPLATFORMLNXDUMMYHCD pNew = (PUTSPLATFORMLNXDUMMYHCD)RTMemRealloc(g_paDummyHcd, cbNew);
229 if (pNew)
230 {
231 idxHcdMax += 10;
232 g_paDummyHcd = pNew;
233 }
234 }
235
236 if (idxHcdCur < idxHcdMax)
237 {
238 g_paDummyHcd[idxHcdCur].idxDummyHcd = idxHcd;
239 g_paDummyHcd[idxHcdCur].pszHcdName = pszHcdName;
240 g_paDummyHcd[idxHcdCur].pszUdcName = pszUdcName;
241 g_paDummyHcd[idxHcdCur].fAvailable = true;
242 g_paDummyHcd[idxHcdCur].fSuperSpeed = false;
243 g_paDummyHcd[idxHcdCur].cBusses = 0;
244 g_paDummyHcd[idxHcdCur].paBusses = NULL;
245 rc = utsPlatformLnxDummyHcdQueryBusses(&g_paDummyHcd[idxHcdCur], pszHcdName);
246 if (RT_SUCCESS(rc))
247 idxHcdCur++;
248 }
249 else
250 rc = VERR_NO_MEMORY;
251 }
252 }
253 }
254 } while (RT_SUCCESS(rc));
255
256 g_cDummyHcd = idxHcdCur;
257
258 if (rc == VERR_NO_MORE_FILES)
259 rc = VINF_SUCCESS;
260
261 RTDirClose(pDir);
262 }
263
264 return rc;
265}
266
267DECLHIDDEN(int) utsPlatformInit(void)
268{
269 /* Load the modules required for setting up USB/IP testing. */
270 int rc = utsPlatformModuleLoad("libcomposite", NULL, 0);
271 if (RT_SUCCESS(rc))
272 {
273 const char *apszArg[] = { "num=20" }; /** @todo: Make configurable from config. */
274 rc = utsPlatformModuleLoad("dummy_hcd", &apszArg[0], RT_ELEMENTS(apszArg));
275 if (RT_SUCCESS(rc))
276 rc = utsPlatformModuleLoad("dummy_hcd_ss", &apszArg[0], RT_ELEMENTS(apszArg));
277 if (RT_SUCCESS(rc))
278 rc = utsPlatformLnxHcdScanByName("dummy_hcd", "dummy_udc");
279 if (RT_SUCCESS(rc))
280 rc = utsPlatformLnxHcdScanByName("dummy_hcd_ss", "dummy_udc_ss");
281 }
282
283 return rc;
284}
285
286
287DECLHIDDEN(void) utsPlatformTerm(void)
288{
289 /* Unload dummy HCD. */
290 utsPlatformModuleUnload("dummy_hcd");
291 utsPlatformModuleUnload("dummy_hcd_ss");
292
293 RTMemFree(g_paDummyHcd);
294}
295
296
297DECLHIDDEN(int) utsPlatformModuleLoad(const char *pszModule, const char **papszArgv,
298 unsigned cArgv)
299{
300 RTPROCESS hProcModprobe = NIL_RTPROCESS;
301 const char **papszArgs = (const char **)RTMemAllocZ((3 + cArgv) * sizeof(const char *));
302 if (RT_UNLIKELY(!papszArgs))
303 return VERR_NO_MEMORY;
304
305 papszArgs[0] = "modprobe";
306 papszArgs[1] = pszModule;
307
308 unsigned idx;
309 for (idx = 0; idx < cArgv; idx++)
310 papszArgs[2+idx] = papszArgv[idx];
311 papszArgs[2+idx] = NULL;
312
313 int rc = RTProcCreate("modprobe", papszArgs, RTENV_DEFAULT, RTPROC_FLAGS_SEARCH_PATH, &hProcModprobe);
314 if (RT_SUCCESS(rc))
315 {
316 RTPROCSTATUS ProcSts;
317 rc = RTProcWait(hProcModprobe, RTPROCWAIT_FLAGS_BLOCK, &ProcSts);
318 if (RT_SUCCESS(rc))
319 {
320 /* Evaluate the process status. */
321 if ( ProcSts.enmReason != RTPROCEXITREASON_NORMAL
322 || ProcSts.iStatus != 0)
323 rc = VERR_UNRESOLVED_ERROR; /** @todo: Log and give finer grained status code. */
324 }
325 }
326
327 RTMemFree(papszArgs);
328 return rc;
329}
330
331
332DECLHIDDEN(int) utsPlatformModuleUnload(const char *pszModule)
333{
334 RTPROCESS hProcModprobe = NIL_RTPROCESS;
335 const char *apszArgv[3];
336
337 apszArgv[0] = "rmmod";
338 apszArgv[1] = pszModule;
339 apszArgv[2] = NULL;
340
341 int rc = RTProcCreate("rmmod", apszArgv, RTENV_DEFAULT, RTPROC_FLAGS_SEARCH_PATH, &hProcModprobe);
342 if (RT_SUCCESS(rc))
343 {
344 RTPROCSTATUS ProcSts;
345 rc = RTProcWait(hProcModprobe, RTPROCWAIT_FLAGS_BLOCK, &ProcSts);
346 if (RT_SUCCESS(rc))
347 {
348 /* Evaluate the process status. */
349 if ( ProcSts.enmReason != RTPROCEXITREASON_NORMAL
350 || ProcSts.iStatus != 0)
351 rc = VERR_UNRESOLVED_ERROR; /** @todo: Log and give finer grained status code. */
352 }
353 }
354
355 return rc;
356}
357
358
359DECLHIDDEN(int) utsPlatformLnxAcquireUDC(bool fSuperSpeed, char **ppszUdc, uint32_t *puBusId)
360{
361 int rc = VERR_NOT_FOUND;
362
363 for (unsigned i = 0; i < g_cDummyHcd; i++)
364 {
365 PUTSPLATFORMLNXDUMMYHCD pHcd = &g_paDummyHcd[i];
366
367 /*
368 * We can't use a super speed capable UDC for gadgets with lower speeds
369 * because they hardcode the maximum speed to SuperSpeed most of the time
370 * which will make it unusable for lower speeds.
371 */
372 if ( pHcd->fAvailable
373 && pHcd->fSuperSpeed == fSuperSpeed)
374 {
375 /* Check all assigned busses for a speed match. */
376 for (unsigned idxBus = 0; idxBus < pHcd->cBusses; idxBus++)
377 {
378 if (pHcd->paBusses[idxBus].fSuperSpeed == fSuperSpeed)
379 {
380 rc = VINF_SUCCESS;
381 int cbRet = RTStrAPrintf(ppszUdc, "%s.%u", pHcd->pszUdcName, pHcd->idxDummyHcd);
382 if (cbRet == -1)
383 rc = VERR_NO_STR_MEMORY;
384 *puBusId = pHcd->paBusses[idxBus].uBusId;
385 pHcd->fAvailable = false;
386 break;
387 }
388 }
389
390 if (rc != VERR_NOT_FOUND)
391 break;
392 }
393 }
394
395 return rc;
396}
397
398
399DECLHIDDEN(int) utsPlatformLnxReleaseUDC(const char *pszUdc)
400{
401 int rc = VERR_INVALID_PARAMETER;
402 const char *pszIdx = RTStrStr(pszUdc, ".");
403 if (pszIdx)
404 {
405 size_t cchUdcName = pszIdx - pszUdc;
406 pszIdx++;
407 uint32_t idxHcd = 0;
408 rc = RTStrToUInt32Ex(pszIdx, NULL, 10, &idxHcd);
409 if (RT_SUCCESS(rc))
410 {
411 rc = VERR_NOT_FOUND;
412
413 for (unsigned i = 0; i < g_cDummyHcd; i++)
414 {
415 if ( g_paDummyHcd[i].idxDummyHcd == idxHcd
416 && !RTStrNCmp(g_paDummyHcd[i].pszUdcName, pszUdc, cchUdcName))
417 {
418 AssertReturn(!g_paDummyHcd[i].fAvailable, VERR_INVALID_PARAMETER);
419 g_paDummyHcd[i].fAvailable = true;
420 rc = VINF_SUCCESS;
421 break;
422 }
423 }
424 }
425 }
426
427 return rc;
428}
429
Note: See TracBrowser for help on using the repository browser.

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