VirtualBox

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

Last change on this file since 60680 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.4 KB
Line 
1/* $Id: UsbTestServiceGadgetClassTest.cpp 60522 2016-04-15 14:34:35Z 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.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
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
294 /* Get basic device config. */
295 rc = utsGadgetCfgQueryU16Def(paCfg, "Gadget/idVendor", &idVendor, UTS_GADGET_TEST_VENDOR_ID_DEF);
296 if (RT_SUCCESS(rc))
297 rc = utsGadgetCfgQueryU16Def(paCfg, "Gadget/idProduct", &idProduct, UTS_GADGET_TEST_PRODUCT_ID_DEF);
298 if (RT_SUCCESS(rc))
299 rc = utsGadgetCfgQueryU8Def(paCfg, "Gadget/bDeviceClass", &bDeviceClass, UTS_GADGET_TEST_DEVICE_CLASS_DEF);
300 if (RT_SUCCESS(rc))
301 rc = utsGadgetCfgQueryStringDef(paCfg, "Gadget/SerialNumber", &pszSerial, UTS_GADGET_TEST_SERIALNUMBER_DEF);
302 if (RT_SUCCESS(rc))
303 rc = utsGadgetCfgQueryStringDef(paCfg, "Gadget/Manufacturer", &pszManufacturer, UTS_GADGET_TEST_MANUFACTURER_DEF);
304 if (RT_SUCCESS(rc))
305 rc = utsGadgetCfgQueryStringDef(paCfg, "Gadget/Product", &pszProduct, UTS_GADGET_TEST_PRODUCT_DEF);
306
307 if (RT_SUCCESS(rc))
308 {
309 /* Write basic attributes. */
310 rc = RTLinuxSysFsWriteU16File(16, idVendor, "%s/idVendor", pClass->pszGadgetPath);
311 if (RT_SUCCESS(rc))
312 rc = RTLinuxSysFsWriteU16File(16, idProduct, "%s/idProduct", pClass->pszGadgetPath);
313 if (RT_SUCCESS(rc))
314 rc = RTLinuxSysFsWriteU16File(16, bDeviceClass, "%s/bDeviceClass", pClass->pszGadgetPath);
315
316 /* Create english language strings. */
317 if (RT_SUCCESS(rc))
318 rc = utsGadgetClassTestDirCreate("%s/strings/0x409", pClass->pszGadgetPath);
319 if (RT_SUCCESS(rc))
320 rc = RTLinuxSysFsWriteStrFile(pszSerial, 0, NULL, "%s/strings/0x409/serialnumber", pClass->pszGadgetPath);
321 if (RT_SUCCESS(rc))
322 rc = RTLinuxSysFsWriteStrFile(pszManufacturer, 0, NULL, "%s/strings/0x409/manufacturer", pClass->pszGadgetPath);
323 if (RT_SUCCESS(rc))
324 rc = RTLinuxSysFsWriteStrFile(pszProduct, 0, NULL, "%s/strings/0x409/product", pClass->pszGadgetPath);
325
326 /* Create the gadget functions. */
327 if (RT_SUCCESS(rc))
328 rc = utsGadgetClassTestDirCreate("%s/functions/SourceSink.0", pClass->pszGadgetPath);
329 if (RT_SUCCESS(rc))
330 rc = utsGadgetClassTestDirCreate("%s/functions/Loopback.0", pClass->pszGadgetPath);
331
332 /* Create the device configs. */
333 if (RT_SUCCESS(rc))
334 rc = utsGadgetClassTestDirCreate("%s/configs/c.1", pClass->pszGadgetPath);
335 if (RT_SUCCESS(rc))
336 rc = utsGadgetClassTestDirCreate("%s/configs/c.2", pClass->pszGadgetPath);
337
338 /* Write configuration strings. */
339 if (RT_SUCCESS(rc))
340 rc = utsGadgetClassTestDirCreate("%s/configs/c.1/strings/0x409", pClass->pszGadgetPath);
341 if (RT_SUCCESS(rc))
342 rc = utsGadgetClassTestDirCreate("%s/configs/c.2/strings/0x409", pClass->pszGadgetPath);
343 if (RT_SUCCESS(rc))
344 rc = RTLinuxSysFsWriteStrFile("source and sink data", 0, NULL, "%s/configs/c.1/strings/0x409/configuration", pClass->pszGadgetPath);
345 if (RT_SUCCESS(rc))
346 rc = RTLinuxSysFsWriteStrFile("loop input to output", 0, NULL, "%s/configs/c.2/strings/0x409/configuration", pClass->pszGadgetPath);
347
348 /* Link the functions into the configurations. */
349 if (RT_SUCCESS(rc))
350 rc = utsGadgetClassTestLinkFuncToCfg(pClass, "SourceSink.0", "c.1");
351 if (RT_SUCCESS(rc))
352 rc = utsGadgetClassTestLinkFuncToCfg(pClass, "Loopback.0", "c.2");
353
354 /* Finally enable the gadget by attaching it to a UDC. */
355 if (RT_SUCCESS(rc))
356 {
357 pClass->pszUdc = NULL;
358
359 rc = utsPlatformLnxAcquireUDC(&pClass->pszUdc, &pClass->uBusId);
360 if (RT_SUCCESS(rc))
361 rc = RTLinuxSysFsWriteStrFile(pClass->pszUdc, 0, NULL, "%s/UDC", pClass->pszGadgetPath);
362 if (RT_SUCCESS(rc))
363 RTThreadSleep(500); /* Fudge: Sleep a bit to give the device a chance to appear on the host so binding succeeds. */
364 }
365 }
366
367 if (pszSerial)
368 RTStrFree(pszSerial);
369 if (pszManufacturer)
370 RTStrFree(pszManufacturer);
371 if (pszProduct)
372 RTStrFree(pszProduct);
373 }
374 }
375 else
376 rc = VERR_NOT_FOUND;
377
378 if (RT_FAILURE(rc))
379 utsGadgetClassTestCleanup(pClass);
380
381 return rc;
382}
383
384
385/**
386 * @interface_method_impl{UTSGADGETCLASS,pfnTerm}
387 */
388static DECLCALLBACK(void) utsGadgetClassTestTerm(PUTSGADGETCLASSINT pClass)
389{
390 utsGadgetClassTestCleanup(pClass);
391
392 if (pClass->pszGadgetPath)
393 RTStrFree(pClass->pszGadgetPath);
394}
395
396
397/**
398 * @interface_method_impl{UTSGADGETCLASS,pfnGetBusId}
399 */
400static DECLCALLBACK(uint32_t) utsGadgetClassTestGetBusId(PUTSGADGETCLASSINT pClass)
401{
402 return pClass->uBusId;
403}
404
405
406/**
407 * @interface_method_impl{UTSGADGETCLASS,pfnConnect}
408 */
409static DECLCALLBACK(int) utsGadgetClassTestConnect(PUTSGADGETCLASSINT pClass)
410{
411 int rc = RTLinuxSysFsWriteStrFile("connect", 0, NULL, "/sys/class/udc/%s/soft_connect", pClass->pszUdc);
412 if (RT_SUCCESS(rc))
413 RTThreadSleep(500); /* Fudge: Sleep a bit to give the device a chance to appear on the host so binding succeeds. */
414
415 return rc;
416}
417
418
419/**
420 * @interface_method_impl{UTSGADGETCLASS,pfnDisconnect}
421 */
422static DECLCALLBACK(int) utsGadgetClassTestDisconnect(PUTSGADGETCLASSINT pClass)
423{
424 return RTLinuxSysFsWriteStrFile("disconnect", 0, NULL, "/sys/class/udc/%s/soft_connect", pClass->pszUdc);}
425
426
427
428/**
429 * The gadget host interface callback table.
430 */
431const UTSGADGETCLASSIF g_UtsGadgetClassTest =
432{
433 /** enmType */
434 UTSGADGETCLASS_TEST,
435 /** pszDesc */
436 "UTS test device gadget class",
437 /** cbIf */
438 sizeof(UTSGADGETCLASSINT),
439 /** pfnInit */
440 utsGadgetClassTestInit,
441 /** pfnTerm */
442 utsGadgetClassTestTerm,
443 /** pfnGetBusId */
444 utsGadgetClassTestGetBusId,
445 /** pfnConnect */
446 utsGadgetClassTestConnect,
447 /** pfnDisconnect. */
448 utsGadgetClassTestDisconnect
449};
450
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