VirtualBox

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

Last change on this file since 76553 was 76553, checked in by vboxsync, 6 years ago

scm --update-copyright-year

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