VirtualBox

source: vbox/trunk/src/VBox/ValidationKit/utils/usb/UsbTestServiceGadgetClassTest.cpp@ 60793

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

ValidationKit/UsbTestService: Updates, implement the protocol bits for passing extended configuration from the client to the gadget, detect multiple busses from dummy_hcd which creates a separate bus for high-speed and super-speed gadgets, some smaller fixes

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 16.5 KB
Line 
1/* $Id: UsbTestServiceGadgetClassTest.cpp 60793 2016-05-02 16:12:43Z vboxsync $ */
2/** @file
3 * UsbTestServ - Remote USB test configuration and execution server, USB gadget class
4 * for the test device.
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/symlink.h>
34#include <iprt/thread.h>
35#include <iprt/types.h>
36
37#include <iprt/linux/sysfs.h>
38
39#include "UsbTestServiceGadgetInternal.h"
40#include "UsbTestServicePlatform.h"
41
42/*********************************************************************************************************************************
43* Constants And Macros, Structures and Typedefs *
44*********************************************************************************************************************************/
45
46/** Default configfs mount point. */
47#define UTS_GADGET_CLASS_CONFIGFS_MNT_DEF "/sys/kernel/config/usb_gadget"
48/** Gadget template name */
49#define UTS_GADGET_TEMPLATE_NAME "gadget_test"
50
51/** Default vendor ID which is recognized by the usbtest driver. */
52#define UTS_GADGET_TEST_VENDOR_ID_DEF UINT16_C(0x0525)
53/** Default product ID which is recognized by the usbtest driver. */
54#define UTS_GADGET_TEST_PRODUCT_ID_DEF UINT16_C(0xa4a0)
55/** Default device class. */
56#define UTS_GADGET_TEST_DEVICE_CLASS_DEF UINT8_C(0xff)
57/** Default serial number string. */
58#define UTS_GADGET_TEST_SERIALNUMBER_DEF "0123456789"
59/** Default manufacturer string. */
60#define UTS_GADGET_TEST_MANUFACTURER_DEF "Oracle Inc."
61/** Default product string. */
62#define UTS_GADGET_TEST_PRODUCT_DEF "USB test device"
63
64/**
65 * Internal UTS gadget host instance data.
66 */
67typedef struct UTSGADGETCLASSINT
68{
69 /** Gadget template path. */
70 char *pszGadgetPath;
71 /** The UDC this gadget is connected to. */
72 char *pszUdc;
73 /** Bus identifier for the used UDC. */
74 uint32_t uBusId;
75 /** Device identifier. */
76 uint32_t uDevId;
77} UTSGADGETCLASSINT;
78
79
80/*********************************************************************************************************************************
81* Global Variables *
82*********************************************************************************************************************************/
83
84/** Number of already created gadgets, used for the template name. */
85static volatile uint32_t g_cGadgets = 0;
86
87
88/*********************************************************************************************************************************
89* Internal Functions *
90*********************************************************************************************************************************/
91
92/**
93 * Creates a new directory pointed to by the given format string.
94 *
95 * @returns IPRT status code.
96 * @param pszFormat The format string.
97 * @param va The arguments.
98 */
99static int utsGadgetClassTestDirCreateV(const char *pszFormat, va_list va)
100{
101 int rc = VINF_SUCCESS;
102 char aszPath[RTPATH_MAX + 1];
103
104 size_t cbStr = RTStrPrintfV(&aszPath[0], sizeof(aszPath), pszFormat, va);
105 if (cbStr <= sizeof(aszPath) - 1)
106 rc = RTDirCreateFullPath(aszPath, 0700);
107 else
108 rc = VERR_BUFFER_OVERFLOW;
109
110 return rc;
111}
112
113
114/**
115 * Creates a new directory pointed to by the given format string.
116 *
117 * @returns IPRT status code.
118 * @param pszFormat The format string.
119 * @param ... The arguments.
120 */
121static int utsGadgetClassTestDirCreate(const char *pszFormat, ...)
122{
123 va_list va;
124 va_start(va, pszFormat);
125 int rc = utsGadgetClassTestDirCreateV(pszFormat, va);
126 va_end(va);
127 return rc;
128}
129
130
131/**
132 * Removes a directory pointed to by the given format string.
133 *
134 * @returns IPRT status code.
135 * @param pszFormat The format string.
136 * @param va The arguments.
137 */
138static int utsGadgetClassTestDirRemoveV(const char *pszFormat, va_list va)
139{
140 int rc = VINF_SUCCESS;
141 char aszPath[RTPATH_MAX + 1];
142
143 size_t cbStr = RTStrPrintfV(&aszPath[0], sizeof(aszPath), pszFormat, va);
144 if (cbStr <= sizeof(aszPath) - 1)
145 rc = RTDirRemove(aszPath);
146 else
147 rc = VERR_BUFFER_OVERFLOW;
148
149 return rc;
150}
151
152
153/**
154 * Removes a directory pointed to by the given format string.
155 *
156 * @returns IPRT status code.
157 * @param pszFormat The format string.
158 * @param ... The arguments.
159 */
160static int utsGadgetClassTestDirRemove(const char *pszFormat, ...)
161{
162 va_list va;
163 va_start(va, pszFormat);
164 int rc = utsGadgetClassTestDirRemoveV(pszFormat, va);
165 va_end(va);
166 return rc;
167}
168
169
170/**
171 * Links the given function to the given config.
172 *
173 * @returns IPRT status code.
174 * @param pClass The gadget class instance data.
175 * @param pszFunc The function to link.
176 * @param pszCfg The configuration which the function will be part of.
177 */
178static int utsGadgetClassTestLinkFuncToCfg(PUTSGADGETCLASSINT pClass, const char *pszFunc, const char *pszCfg)
179{
180 int rc = VINF_SUCCESS;
181 char aszPathFunc[RTPATH_MAX + 1];
182 char aszPathCfg[RTPATH_MAX + 1];
183
184 size_t cbStr = RTStrPrintf(&aszPathFunc[0], sizeof(aszPathFunc), "%s/functions/%s",
185 pClass->pszGadgetPath, pszFunc);
186 if (cbStr <= sizeof(aszPathFunc) - 1)
187 {
188 cbStr = RTStrPrintf(&aszPathCfg[0], sizeof(aszPathCfg), "%s/configs/%s/%s",
189 pClass->pszGadgetPath, pszCfg, pszFunc);
190 if (cbStr <= sizeof(aszPathCfg) - 1)
191 rc = RTSymlinkCreate(&aszPathCfg[0], &aszPathFunc[0], RTSYMLINKTYPE_DIR, 0);
192 else
193 rc = VERR_BUFFER_OVERFLOW;
194 }
195 else
196 rc = VERR_BUFFER_OVERFLOW;
197
198 return rc;
199}
200
201
202/**
203 * Unlinks the given function from the given configuration.
204 *
205 * @returns IPRT status code.
206 * @param pClass The gadget class instance data.
207 * @param pszFunc The function to unlink.
208 * @param pszCfg The configuration which the function is currently part of.
209 */
210static int utsGadgetClassTestUnlinkFuncFromCfg(PUTSGADGETCLASSINT pClass, const char *pszFunc, const char *pszCfg)
211{
212 int rc = VINF_SUCCESS;
213 char aszPath[RTPATH_MAX + 1];
214 size_t cbStr = RTStrPrintf(&aszPath[0], sizeof(aszPath), "%s/configs/%s/%s",
215 pClass->pszGadgetPath, pszCfg, pszFunc);
216 if (cbStr <= sizeof(aszPath) - 1)
217 rc = RTSymlinkDelete(&aszPath[0], 0);
218 else
219 rc = VERR_BUFFER_OVERFLOW;
220
221 return rc;
222}
223
224
225/**
226 * Cleans up any leftover configurations from the gadget class.
227 *
228 * @returns nothing.
229 * @param pClass The gadget class instance data.
230 */
231static void utsGadgetClassTestCleanup(PUTSGADGETCLASSINT pClass)
232{
233 /* Unbind the gadget from the currently assigned UDC first. */
234 int rc = RTLinuxSysFsWriteStrFile("", 0, NULL, "%s/UDC", pClass->pszGadgetPath);
235 AssertRC(rc);
236
237 /* Delete the symlinks, ignore any errors. */
238 utsGadgetClassTestUnlinkFuncFromCfg(pClass, "Loopback.0", "c.2");
239 utsGadgetClassTestUnlinkFuncFromCfg(pClass, "SourceSink.0", "c.1");
240
241 /* Delete configuration strings and then the configuration directories. */
242 utsGadgetClassTestDirRemove("%s/configs/c.2/strings/0x409", pClass->pszGadgetPath);
243 utsGadgetClassTestDirRemove("%s/configs/c.1/strings/0x409", pClass->pszGadgetPath);
244
245 utsGadgetClassTestDirRemove("%s/configs/c.2", pClass->pszGadgetPath);
246 utsGadgetClassTestDirRemove("%s/configs/c.1", pClass->pszGadgetPath);
247
248 /* Delete the functions. */
249 utsGadgetClassTestDirRemove("%s/functions/Loopback.0", pClass->pszGadgetPath);
250 utsGadgetClassTestDirRemove("%s/functions/SourceSink.0", pClass->pszGadgetPath);
251
252 /* Delete the english strings. */
253 utsGadgetClassTestDirRemove("%s/strings/0x409", pClass->pszGadgetPath);
254
255 /* Finally delete the gadget template. */
256 utsGadgetClassTestDirRemove(pClass->pszGadgetPath);
257
258 /* Release the UDC. */
259 if (pClass->pszUdc)
260 {
261 rc = utsPlatformLnxReleaseUDC(pClass->pszUdc);
262 AssertRC(rc);
263 RTStrFree(pClass->pszUdc);
264 }
265}
266
267/**
268 * @interface_method_impl{UTSGADGETCLASS,pfnInit}
269 */
270static DECLCALLBACK(int) utsGadgetClassTestInit(PUTSGADGETCLASSINT pClass, PCUTSGADGETCFGITEM paCfg)
271{
272 int rc = VINF_SUCCESS;
273
274 if (RTLinuxSysFsExists(UTS_GADGET_CLASS_CONFIGFS_MNT_DEF))
275 {
276 /* Create the gadget template */
277 unsigned idx = ASMAtomicIncU32(&g_cGadgets);
278
279 int rcStr = RTStrAPrintf(&pClass->pszGadgetPath, "%s/%s%u", UTS_GADGET_CLASS_CONFIGFS_MNT_DEF,
280 UTS_GADGET_TEMPLATE_NAME, idx);
281 if (rcStr == -1)
282 return VERR_NO_STR_MEMORY;
283
284 rc = utsGadgetClassTestDirCreate(pClass->pszGadgetPath);
285 if (RT_SUCCESS(rc))
286 {
287 uint16_t idVendor = 0;
288 uint16_t idProduct = 0;
289 uint8_t bDeviceClass = 0;
290 char *pszSerial = NULL;
291 char *pszManufacturer = NULL;
292 char *pszProduct = NULL;
293 bool fSuperSpeed = false;
294
295 /* Get basic device config. */
296 rc = utsGadgetCfgQueryU16Def(paCfg, "Gadget/idVendor", &idVendor, UTS_GADGET_TEST_VENDOR_ID_DEF);
297 if (RT_SUCCESS(rc))
298 rc = utsGadgetCfgQueryU16Def(paCfg, "Gadget/idProduct", &idProduct, UTS_GADGET_TEST_PRODUCT_ID_DEF);
299 if (RT_SUCCESS(rc))
300 rc = utsGadgetCfgQueryU8Def(paCfg, "Gadget/bDeviceClass", &bDeviceClass, UTS_GADGET_TEST_DEVICE_CLASS_DEF);
301 if (RT_SUCCESS(rc))
302 rc = utsGadgetCfgQueryStringDef(paCfg, "Gadget/SerialNumber", &pszSerial, UTS_GADGET_TEST_SERIALNUMBER_DEF);
303 if (RT_SUCCESS(rc))
304 rc = utsGadgetCfgQueryStringDef(paCfg, "Gadget/Manufacturer", &pszManufacturer, UTS_GADGET_TEST_MANUFACTURER_DEF);
305 if (RT_SUCCESS(rc))
306 rc = utsGadgetCfgQueryStringDef(paCfg, "Gadget/Product", &pszProduct, UTS_GADGET_TEST_PRODUCT_DEF);
307 if (RT_SUCCESS(rc))
308 rc = utsGadgetCfgQueryBoolDef(paCfg, "Gadget/SuperSpeed", &fSuperSpeed, false);
309
310 if (RT_SUCCESS(rc))
311 {
312 /* Write basic attributes. */
313 rc = RTLinuxSysFsWriteU16File(16, idVendor, "%s/idVendor", pClass->pszGadgetPath);
314 if (RT_SUCCESS(rc))
315 rc = RTLinuxSysFsWriteU16File(16, idProduct, "%s/idProduct", pClass->pszGadgetPath);
316 if (RT_SUCCESS(rc))
317 rc = RTLinuxSysFsWriteU16File(16, bDeviceClass, "%s/bDeviceClass", pClass->pszGadgetPath);
318
319 /* Create english language strings. */
320 if (RT_SUCCESS(rc))
321 rc = utsGadgetClassTestDirCreate("%s/strings/0x409", pClass->pszGadgetPath);
322 if (RT_SUCCESS(rc))
323 rc = RTLinuxSysFsWriteStrFile(pszSerial, 0, NULL, "%s/strings/0x409/serialnumber", pClass->pszGadgetPath);
324 if (RT_SUCCESS(rc))
325 rc = RTLinuxSysFsWriteStrFile(pszManufacturer, 0, NULL, "%s/strings/0x409/manufacturer", pClass->pszGadgetPath);
326 if (RT_SUCCESS(rc))
327 rc = RTLinuxSysFsWriteStrFile(pszProduct, 0, NULL, "%s/strings/0x409/product", pClass->pszGadgetPath);
328
329 /* Create the gadget functions. */
330 if (RT_SUCCESS(rc))
331 rc = utsGadgetClassTestDirCreate("%s/functions/SourceSink.0", pClass->pszGadgetPath);
332 if (RT_SUCCESS(rc))
333 rc = utsGadgetClassTestDirCreate("%s/functions/Loopback.0", pClass->pszGadgetPath);
334
335 /* Create the device configs. */
336 if (RT_SUCCESS(rc))
337 rc = utsGadgetClassTestDirCreate("%s/configs/c.1", pClass->pszGadgetPath);
338 if (RT_SUCCESS(rc))
339 rc = utsGadgetClassTestDirCreate("%s/configs/c.2", pClass->pszGadgetPath);
340
341 /* Write configuration strings. */
342 if (RT_SUCCESS(rc))
343 rc = utsGadgetClassTestDirCreate("%s/configs/c.1/strings/0x409", pClass->pszGadgetPath);
344 if (RT_SUCCESS(rc))
345 rc = utsGadgetClassTestDirCreate("%s/configs/c.2/strings/0x409", pClass->pszGadgetPath);
346 if (RT_SUCCESS(rc))
347 rc = RTLinuxSysFsWriteStrFile("source and sink data", 0, NULL, "%s/configs/c.1/strings/0x409/configuration", pClass->pszGadgetPath);
348 if (RT_SUCCESS(rc))
349 rc = RTLinuxSysFsWriteStrFile("loop input to output", 0, NULL, "%s/configs/c.2/strings/0x409/configuration", pClass->pszGadgetPath);
350
351 /* Link the functions into the configurations. */
352 if (RT_SUCCESS(rc))
353 rc = utsGadgetClassTestLinkFuncToCfg(pClass, "SourceSink.0", "c.1");
354 if (RT_SUCCESS(rc))
355 rc = utsGadgetClassTestLinkFuncToCfg(pClass, "Loopback.0", "c.2");
356
357 /* Finally enable the gadget by attaching it to a UDC. */
358 if (RT_SUCCESS(rc))
359 {
360 pClass->pszUdc = NULL;
361
362 rc = utsPlatformLnxAcquireUDC(fSuperSpeed, &pClass->pszUdc, &pClass->uBusId);
363 if (RT_SUCCESS(rc))
364 rc = RTLinuxSysFsWriteStrFile(pClass->pszUdc, 0, NULL, "%s/UDC", pClass->pszGadgetPath);
365 if (RT_SUCCESS(rc))
366 RTThreadSleep(500); /* Fudge: Sleep a bit to give the device a chance to appear on the host so binding succeeds. */
367 }
368 }
369
370 if (pszSerial)
371 RTStrFree(pszSerial);
372 if (pszManufacturer)
373 RTStrFree(pszManufacturer);
374 if (pszProduct)
375 RTStrFree(pszProduct);
376 }
377 }
378 else
379 rc = VERR_NOT_FOUND;
380
381 if (RT_FAILURE(rc))
382 utsGadgetClassTestCleanup(pClass);
383
384 return rc;
385}
386
387
388/**
389 * @interface_method_impl{UTSGADGETCLASS,pfnTerm}
390 */
391static DECLCALLBACK(void) utsGadgetClassTestTerm(PUTSGADGETCLASSINT pClass)
392{
393 utsGadgetClassTestCleanup(pClass);
394
395 if (pClass->pszGadgetPath)
396 RTStrFree(pClass->pszGadgetPath);
397}
398
399
400/**
401 * @interface_method_impl{UTSGADGETCLASS,pfnGetBusId}
402 */
403static DECLCALLBACK(uint32_t) utsGadgetClassTestGetBusId(PUTSGADGETCLASSINT pClass)
404{
405 return pClass->uBusId;
406}
407
408
409/**
410 * @interface_method_impl{UTSGADGETCLASS,pfnConnect}
411 */
412static DECLCALLBACK(int) utsGadgetClassTestConnect(PUTSGADGETCLASSINT pClass)
413{
414 int rc = RTLinuxSysFsWriteStrFile("connect", 0, NULL, "/sys/class/udc/%s/soft_connect", pClass->pszUdc);
415 if (RT_SUCCESS(rc))
416 RTThreadSleep(500); /* Fudge: Sleep a bit to give the device a chance to appear on the host so binding succeeds. */
417
418 return rc;
419}
420
421
422/**
423 * @interface_method_impl{UTSGADGETCLASS,pfnDisconnect}
424 */
425static DECLCALLBACK(int) utsGadgetClassTestDisconnect(PUTSGADGETCLASSINT pClass)
426{
427 return RTLinuxSysFsWriteStrFile("disconnect", 0, NULL, "/sys/class/udc/%s/soft_connect", pClass->pszUdc);}
428
429
430
431/**
432 * The gadget host interface callback table.
433 */
434const UTSGADGETCLASSIF g_UtsGadgetClassTest =
435{
436 /** enmType */
437 UTSGADGETCLASS_TEST,
438 /** pszDesc */
439 "UTS test device gadget class",
440 /** cbIf */
441 sizeof(UTSGADGETCLASSINT),
442 /** pfnInit */
443 utsGadgetClassTestInit,
444 /** pfnTerm */
445 utsGadgetClassTestTerm,
446 /** pfnGetBusId */
447 utsGadgetClassTestGetBusId,
448 /** pfnConnect */
449 utsGadgetClassTestConnect,
450 /** pfnDisconnect. */
451 utsGadgetClassTestDisconnect
452};
453
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