VirtualBox

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

Last change on this file was 106061, checked in by vboxsync, 3 months ago

Copyright year updates by scm.

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