HostHardwareLinux.cpp revision 9b509d9e7070174b3bd5c80ce9ea52d42dd3de98
/* $Id$ */
/** @file
* Classes for handling hardware detection under Linux. Please feel free to
* expand these to work for other systems (Solaris!) or to add new ones for
* other systems.
*/
/*
* Copyright (C) 2008-2010 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*/
#define LOG_GROUP LOG_GROUP_MAIN
/*******************************************************************************
* Header Files *
*******************************************************************************/
#include <HostHardwareLinux.h>
#include <vector.h>
#ifdef VBOX_USB_WITH_SYSFS
# ifdef VBOX_USB_WITH_INOTIFY
# include <dlfcn.h>
# include <fcntl.h>
# include <poll.h>
# include <signal.h>
# include <unistd.h>
# endif
#endif
#include <vector>
#include <errno.h>
#include <dirent.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
/******************************************************************************
* Global Variables *
******************************************************************************/
#ifdef TESTCASE
static bool testing() { return true; }
static bool fNoProbe = false;
#else
static bool testing() { return false; }
static bool noProbe() { return false; }
#endif
/******************************************************************************
* Typedefs and Defines *
******************************************************************************/
bool *pfSuccess);
bool *pfSuccess);
#ifdef VBOX_USB_WITH_SYSFS
# ifdef VBOX_USB_WITH_INOTIFY
bool *pfSuccess);
/** Function object to be invoked on filenames from a directory. */
typedef struct pathHandler
{
/** Called on each element of the sysfs directory. Can e.g. store
* interesting entries in a list. */
} pathHandler;
{
}
int withRealPath);
# endif
#endif /* VBOX_USB_WITH_SYSFS */
/** Find the length of a string, ignoring trailing non-ascii or control
* characters */
{
cch = i;
return cch + 1;
}
/**
* Get the name of a floppy drive according to the Linux floppy driver.
* @returns true on success, false if the name was not available (i.e. the
* device was not readible, or the file name wasn't a PC floppy
* device)
* @param pcszNode the path to the device node for the device
* @param Number the Linux floppy driver number for the drive. Required.
* @param pszName where to store the name retreived
*/
{
AssertPtrReturn(pcszNode, false);
AssertPtrReturn(pszName, false);
int rc = RTFileOpen(&File, pcszNode, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_NON_BLOCK);
if (RT_SUCCESS(rc))
{
int rcIoCtl;
return true;
}
return false;
}
/**
* Create a UDI and a description for a floppy drive based on a number and the
* driver's name for it. We deliberately return an ugly sequence of
* characters as the description rather than an English language string to
* avoid translation issues.
*
* @returns true if we know the device to be valid, false otherwise
* @param pcszName the floppy driver name for the device (optional)
* @param Number the number of the floppy (0 to 3 on FDC 0, 4 to 7 on
* FDC 1)
* @param pszDesc where to store the device description (optional)
* @param cchDesc the size of the buffer in @a pszDesc
* @param pszUdi where to store the device UDI (optional)
* @param cchUdi the size of the buffer in @a pszUdi
*/
{
if (pcszName)
{
const char *pcszSize;
switch(pcszName[0])
{
case 'd': case 'q': case 'h':
pcszSize = "5.25\"";
break;
case 'D': case 'H': case 'E': case 'u':
pcszSize = "3.5\"";
break;
default:
pcszSize = "(unknown)";
}
if (pszDesc)
}
else
{
if (pszDesc)
}
if (pszUdi)
"/org/freedesktop/Hal/devices/platform_floppy_%u_storage",
Number);
}
/**
* Check whether a device number might correspond to a CD-ROM device according
* to Documentation/devices.txt in the Linux kernel source.
* @returns true if it might, false otherwise
* @param Number the device number (major and minor combination)
*/
{
return true;
if (major == SCSI_CDROM_MAJOR)
return true;
if (major == CDU31A_CDROM_MAJOR)
return true;
if (major == GOLDSTAR_CDROM_MAJOR)
return true;
if (major == OPTICS_CDROM_MAJOR)
return true;
if (major == SANYO_CDROM_MAJOR)
return true;
if (major == MITSUMI_X_CDROM_MAJOR)
return true;
return true;
if (major == MITSUMI_CDROM_MAJOR)
return true;
if (major == CDU535_CDROM_MAJOR)
return true;
if (major == MATSUSHITA_CDROM_MAJOR)
return true;
if (major == MATSUSHITA_CDROM2_MAJOR)
return true;
if (major == MATSUSHITA_CDROM3_MAJOR)
return true;
if (major == MATSUSHITA_CDROM4_MAJOR)
return true;
if (major == AZTECH_CDROM_MAJOR)
return true;
return true;
if (major == CM206_CDROM_MAJOR)
return true;
return true;
return true;
return true;
return true;
return true;
return true;
return true;
return true;
return true;
return false;
}
/**
* Send an SCSI INQUIRY command to a device and return selected information.
* @returns iprt status code
* @returns VERR_TRY_AGAIN if the query failed but might succeed next time
* @param pcszNode the full path to the device node
* @param pu8Type where to store the SCSI device type on success (optional)
* @param pchVendor where to store the vendor id string on success (optional)
* @param cchVendor the size of the @a pchVendor buffer
* @param pchModel where to store the product id string on success (optional)
* @param cchModel the size of the @a pchModel buffer
* @note check documentation on the SCSI INQUIRY command and the Linux kernel
* SCSI headers included above if you want to understand what is going
* on in this method.
*/
{
LogRelFlowFunc(("pcszNode=%s, pu8Type=%p, pchVendor=%p, cchVendor=%llu, pchModel=%p, cchModel=%llu\n",
cchModel));
int rc = RTFileOpen(&hFile, pcszNode, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_NON_BLOCK);
if (RT_SUCCESS(rc))
{
int rcIoCtl = 0;
unsigned char u8Response[96] = { 0 };
struct cdrom_generic_command CdromCommandReq;
if (RT_SUCCESS(rc))
{
if (pu8Type)
if (pchVendor)
if (pchModel)
LogRelFlowFunc(("returning success: type=%u, vendor=%.8s, product=%.16s\n",
return VINF_SUCCESS;
}
}
return rc;
}
/**
* Initialise the device strings (description and UDI) for a DVD drive based on
* vendor and model name strings.
* @param pcszVendor the vendor ID string
* @param pcszModel the product ID string
* @param pszDesc where to store the description string (optional)
* @param cchDesc the size of the buffer in @pszDesc
* @param pszUdi where to store the UDI string (optional)
* @param cchUdi the size of the buffer in @pszUdi
*/
/* static */
{
char szCleaned[128];
/* Create a cleaned version of the model string for the UDI string. */
else
szCleaned[i] = '_';
/* Construct the description string as "Vendor Product" */
if (pszDesc)
{
if (cchVendor > 0)
else
}
/* Construct the UDI string */
if (pszUdi)
{
if (cchModel > 0)
"/org/freedesktop/Hal/devices/storage_model_%s",
else
pszUdi[0] = '\0';
}
}
/**
* Check whether a device node points to a valid device and create a UDI and
* a description for it, and store the device number, if it does.
* @returns true if the device is valid, false otherwise
* @param pcszNode the path to the device node
* @param isDVD are we looking for a DVD device (or a floppy device)?
* @param pDevice where to store the device node (optional)
* @param pszDesc where to store the device description (optional)
* @param cchDesc the size of the buffer in @a pszDesc
* @param pszUdi where to store the device UDI (optional)
* @param cchUdi the size of the buffer in @a pszUdi
*/
{
AssertPtrReturn(pcszNode, false);
AssertPtrNullReturn(pDevice, false);
AssertPtrNullReturn(pszDesc, false);
AssertPtrNullReturn(pszUdi, false);
return false;
return false;
if (pDevice)
if (isDVD)
{
return false;
return false;
return false;
}
else
{
/* Floppies on Linux are legacy devices with hardcoded majors and
* minors */
unsigned Number;
return false;
{
case 0: case 1: case 2: case 3:
break;
case 128: case 129: case 130: case 131:
break;
default:
return false;
}
return false;
cchUdi);
}
return true;
}
int VBoxMainDriveInfo::updateDVDs ()
{
LogFlowThisFunc(("entered\n"));
int rc = VINF_SUCCESS;
bool success = false; /* Have we succeeded in finding anything yet? */
try
{
/* Always allow the user to override our auto-detection using an
* environment variable. */
&success);
setNoProbe(false);
{
setNoProbe(true);
}
/* Walk through the /dev subtree if nothing else has helped. */
}
{
rc = VERR_NO_MEMORY;
}
return rc;
}
int VBoxMainDriveInfo::updateFloppies ()
{
LogFlowThisFunc(("entered\n"));
int rc = VINF_SUCCESS;
bool success = false; /* Have we succeeded in finding anything yet? */
try
{
mFloppyList.clear ();
false /* isDVD */, &success);
setNoProbe(false);
&success);
{
setNoProbe(true);
}
/* Walk through the /dev subtree if nothing else has helped. */
&success);
}
{
rc = VERR_NO_MEMORY;
}
return rc;
}
/**
* Extract the names of drives from an environment variable and add them to a
* list if they are valid.
* @returns iprt status code
* @param pcszVar the name of the environment variable. The variable
* value should be a list of device node names, separated
* by ':' characters.
* @param pList the list to append the drives found to
* @param isDVD are we looking for DVD drives or for floppies?
* @param pfSuccess this will be set to true if we found at least one drive
* and to false otherwise. Optional.
*/
/* static */
{
int rc = VINF_SUCCESS;
bool success = false;
try
{
const char *pcszCurrent = pszFreeMe;
{
if (pcszNext)
else
{
success = true;
}
}
}
{
rc = VERR_NO_MEMORY;
}
return rc;
}
class sysfsBlockDev
{
public:
misValid(false)
{
if (findDeviceNode())
{
if (mwantDVD)
else
}
}
private:
const char *mpcszName;
/** Are we looking for a floppy or a DVD device? */
bool mwantDVD;
/** The device node for the device */
char mszNode[RTPATH_MAX];
/** Does the sysfs entry look like we expect it too? This is a canary
* for future sysfs ABI changes. */
bool misConsistent;
/** Is this entry a valid specimen of what we are looking for? */
bool misValid;
/** Human readible drive description string */
char mszDesc[256];
/** Unique identifier for the drive. Should be identical to hal's UDI for
* the device. May not be unique for two identical drives. */
char mszUdi[256];
private:
/* Private methods */
/**
* @returns boolean success value
*/
bool findDeviceNode()
{
if (dev == 0)
{
misConsistent = false;
return false;
}
return false;
return true;
}
/** Check whether the sysfs block entry is valid for a DVD device and
* initialise the string data members for the object. We try to get all
* the information we need from sysfs if possible, to avoid unnecessarily
* poking the device, and if that fails we fall back to an SCSI INQUIRY
* command. */
void validateAndInitForDVD()
{
return;
{
if (cchVendor >= 0)
{
if (cchModel >= 0)
{
misValid = true;
return;
}
}
}
if (!noProbe())
}
/** Try to find out whether a device is a DVD drive by sending it an
* SCSI INQUIRY command. If it is, initialise the string and validity
* data members for the object based on the returned data.
*/
void probeAndInitForDVD()
{
sizeof(szModel));
{
misValid = true;
}
}
/** Check whether the sysfs block entry is valid for a floppy device and
* initialise the string data members for the object. Since we only
* support floppies using the basic "floppy" driver, we check the driver
* using the entry name and a driver-specific ioctl. */
void validateAndInitForFloppy()
{
bool haveName = false;
char szDriver[8];
if ( mpcszName[0] != 'f'
return;
if (!noProbe())
{
return;
}
else if (!haveName)
return;
misValid = true;
}
public:
bool isConsistent()
{
return misConsistent;
}
bool isValid()
{
return misValid;
}
const char *getDesc()
{
return mszDesc;
}
const char *getUdi()
{
return mszUdi;
}
const char *getNode()
{
return mszNode;
}
};
/**
* Helper function to query the sysfs subsystem for information about DVD
* drives attached to the system.
* @returns iprt status code
* @param pList where to add information about the drives detected
* @param isDVD are we looking for DVDs or floppies?
* @param pfSuccess Did we find anything?
*
* @returns IPRT status code
*/
/* static */
{
LogFlowFunc (("pList=%p, isDVD=%u, pfSuccess=%p\n",
int rc;
bool fSuccess = false;
unsigned cFound = 0;
if (!RTPathExists("/sys"))
return VINF_SUCCESS;
/* This might mean that sysfs semantics have changed */
fSuccess = true;
if (RT_SUCCESS(rc))
for (;;)
{
break;
continue;
/* This might mean that sysfs semantics have changed */
continue;
try
{
}
{
rc = VERR_NO_MEMORY;
break;
}
++cFound;
}
if (rc == VERR_NO_MORE_FILES)
rc = VINF_SUCCESS;
if (RT_FAILURE(rc))
/* Clean up again */
for (unsigned i = 0; i < cFound; ++i)
if (pfSuccess)
return rc;
}
/** Structure for holding information about a drive we have found */
struct deviceNodeInfo
{
/** The device number */
/** The device node path */
char szPath[RTPATH_MAX];
/** The device description */
char szDesc[256];
/** The device UDI */
char szUdi[256];
};
/** The maximum number of devices we will search for. */
enum { MAX_DEVICE_NODES = 8 };
/** An array of MAX_DEVICE_NODES devices */
/**
* Recursive worker function to walk the /dev tree looking for DVD or floppy
* devices.
* @returns true if we have already found MAX_DEVICE_NODES devices, false
* otherwise
* @param pszPath the path to start recursing. The function can modify
* this string at and after the terminating zero
* @param cchPath the size of the buffer (not the string!) in @a pszPath
* @param aDevices where to fill in information about devices that we have
* found
* @param wantDVD are we looking for DVD devices (or floppies)?
*/
{
/*
* Check assumptions made by the code below.
*/
return false;
for (;;)
{
if (RT_FAILURE(rc))
break;
{
continue;
continue;
}
continue;
break;
/* Do the matching. */
continue;
unsigned i;
for (i = 0; i < MAX_DEVICE_NODES; ++i)
break;
AssertBreak(i < MAX_DEVICE_NODES);
continue;
"%s", pszPath);
if (i == MAX_DEVICE_NODES - 1)
break;
continue;
/* Recurse into subdirectories. */
continue;
continue;
continue;
break;
}
}
/**
* Recursively walk through the /dev tree and add any DVD or floppy drives we
* find and can access to our list. (If we can't access them we can't check
* whether or not they are really DVD or floppy drives).
* @note this is rather slow (a couple of seconds) for DVD probing on
* systems with a static /dev tree, as the current code tries to open
* a CD-ROM device, and opening a non-existent device can take a non.
* negligeable time on Linux. If it is ever necessary to improve this
* (static /dev trees are no longer very fashionable these days, and
* sysfs looks like it will be with us for a while), we could further
* reduce the number of device nodes we open by checking whether the
* there is a race, but it is probably not important for us).
* @returns iprt status code
* @param pList the list to append the drives found to
* @param isDVD are we looking for DVD drives or for floppies?
* @param pfSuccess this will be set to true if we found at least one drive
* and to false otherwise. Optional.
*/
/* static */
{
pfSuccess));
int rc = VINF_SUCCESS;
bool success = false;
try
{
for (unsigned i = 0; i < MAX_DEVICE_NODES; ++i)
{
{
success = true;
}
}
}
{
rc = VERR_NO_MEMORY;
}
return rc;
}
{
}
const char *aSystemID)
{
{
return 0;
}
return 1;
}
{
LogFlowFunc(("entered\n"));
int rc = VINF_SUCCESS;
bool success = false; /* Have we succeeded in finding anything yet? */
#ifdef VBOX_USB_WITH_SYSFS
# ifdef VBOX_USB_WITH_INOTIFY
if ( RT_SUCCESS(rc)
# endif
#else /* !VBOX_USB_WITH_SYSFS */
#endif /* !VBOX_USB_WITH_SYSFS */
return rc;
}
class hotplugNullImpl : public VBoxMainHotplugWaiterImpl
{
public:
hotplugNullImpl (void) {}
virtual ~hotplugNullImpl (void) {}
/** @copydoc VBoxMainHotplugWaiter::Wait */
virtual int Wait (RTMSINTERVAL)
{
return VERR_NOT_SUPPORTED;
}
/** @copydoc VBoxMainHotplugWaiter::Interrupt */
virtual void Interrupt (void) {}
virtual int getStatus(void)
{
return VERR_NOT_SUPPORTED;
}
};
#ifdef VBOX_USB_WITH_SYSFS
# ifdef VBOX_USB_WITH_INOTIFY
/** Class wrapper around an inotify watch (or a group of them to be precise).
* Inherits from pathHandler so that it can be passed to walkDirectory() to
* easily add all files from a directory. */
typedef struct inotifyWatch
{
/** The pathHandler we inherit from - this must be the first structure
* member */
int (*inotify_add_watch)(int, const char *, uint32_t);
/** The native handle of the inotify fd. */
int mhInotify;
} inotifyWatch;
/** The flags we pass to inotify - modify, create, delete, change permissions
*/
#define IN_FLAGS 0x306
{
AssertPtrReturn(pParent, false);
errno = 0;
return true;
/* Other errors listed in the manpage can be treated as fatal */
return false;
}
/** Object initialisation */
{
int (*inotify_init)(void);
int rc = VINF_SUCCESS;
errno = 0;
if (!inotify_init)
*(void **)(&pSelf->inotify_add_watch)
if (!pSelf->inotify_add_watch)
fd = inotify_init();
if (fd < 0)
{
return RTErrConvertFromErrno(errno);
}
if ( flags < 0
{
}
if (RT_FAILURE(rc))
else
{
}
return rc;
}
{
{
}
}
{
}
# define SYSFS_USB_DEVICE_PATH "/dev/bus/usb"
# define SYSFS_WAKEUP_STRING "Wake up!"
class hotplugInotifyImpl : public VBoxMainHotplugWaiterImpl
{
/** Pipe used to interrupt wait(), the read end. */
int mhWakeupPipeR;
/** Pipe used to interrupt wait(), the write end. */
int mhWakeupPipeW;
/** The inotify watch set */
/** Flag to mark that the Wait() method is currently being called, and to
* ensure that it isn't called multiple times in parallel. */
/** iprt result code from object initialisation. Should be AssertReturn-ed
* on at the start of all methods. I went this way because I didn't want
* to deal with exceptions. */
int mStatus;
/** ID values associates with the wakeup pipe and the FAM socket for polling
*/
enum
{
RPIPE_ID = 0,
};
/** Clean up any resources in use, gracefully skipping over any which have
* not yet been allocated or already cleaned up. Intended to be called
* from the destructor or after a failed initialisation. */
void term(void);
int drainInotify();
/** Read the wakeup string from the wakeup pipe */
int drainWakeupPipe(void);
public:
hotplugInotifyImpl(void);
virtual ~hotplugInotifyImpl(void)
{
term();
#ifdef DEBUG
/** The first call to term should mark all resources as freed, so this
* should be a semantic no-op. */
term();
#endif
}
/** Are sysfs and inotify available on this system? If so we expect that
* this implementation will be usable. */
static bool Available(void)
{
return ( RTDirExists(SYSFS_USB_DEVICE_PATH)
}
virtual int getStatus(void)
{
return mStatus;
}
/** @copydoc VBoxMainHotplugWaiter::Wait */
virtual int Wait(RTMSINTERVAL);
/** @copydoc VBoxMainHotplugWaiter::Interrupt */
virtual void Interrupt(void);
};
/** Simplified version of RTPipeCreate */
{
/*
* Create the pipe and set the close-on-exec flag if requested.
*/
return RTErrConvertFromErrno(errno);
*phPipeRead = aFds[0];
/*
* Before we leave, make sure to shut up SIGPIPE.
*/
return VINF_SUCCESS;
}
hotplugInotifyImpl::hotplugInotifyImpl(void) :
{
# ifdef DEBUG
/* Excercise the code path (term() on a not-fully-initialised object) as
* well as we can. On an uninitialised object this method is a sematic
* no-op. */
term();
/* For now this probing method should only be used if nothing else is
* available */
# endif
int rc;
do {
break;
break;
} while(0);
if (RT_FAILURE(rc))
term();
}
void hotplugInotifyImpl::term(void)
{
/** This would probably be a pending segfault, so die cleanly */
if (mhWakeupPipeR != -1)
{
mhWakeupPipeR = -1;
}
if (mhWakeupPipeW != -1)
{
mhWakeupPipeW = -1;
}
}
int hotplugInotifyImpl::drainInotify()
{
errno = 0;
do {
} while (cchRead > 0);
if (cchRead == 0)
return VINF_SUCCESS;
return VINF_SUCCESS;
return RTErrConvertFromErrno(errno);
}
int hotplugInotifyImpl::drainWakeupPipe(void)
{
char szBuf[sizeof(SYSFS_WAKEUP_STRING)];
return VINF_SUCCESS;
}
{
int rc;
do {
false)))
break;
errno = 0;
if (cPolled < 0)
{
}
{
rc = drainWakeupPipe();
if (RT_SUCCESS(rc))
break;
}
{
rc = VERR_TIMEOUT;
}
if (RT_FAILURE(rc))
break;
break;
} while (false);
mfWaiting = 0;
return rc;
}
void hotplugInotifyImpl::Interrupt(void)
{
sizeof(SYSFS_WAKEUP_STRING));
if (cbWritten > 0)
}
# endif /* VBOX_USB_WITH_INOTIFY */
#endif /* VBOX_USB_WTH_SYSFS */
{
try
{
#ifdef VBOX_USB_WITH_SYSFS
# ifdef VBOX_USB_WITH_INOTIFY
if (hotplugInotifyImpl::Available())
{
mImpl = new hotplugInotifyImpl;
return;
}
# endif /* VBOX_USB_WITH_INOTIFY */
#endif /* VBOX_USB_WITH_SYSFS */
mImpl = new hotplugNullImpl;
}
{ }
}
#ifdef VBOX_USB_WITH_SYSFS
# ifdef VBOX_USB_WITH_INOTIFY
/** Helper for readFilePathsFromDir(). Adds a path to the vector if it is not
* NULL and not a dotfile (".", "..", ".*"). */
VECTOR_PTR(char *) *pvpchDevs)
{
char *pszPath;
if (!pcszPath)
return 0;
if (pcszEntry[0] == '.')
return 0;
if (!pszPath)
return ENOMEM;
return ENOMEM;
return 0;
}
/** Helper for readFilePaths(). Adds the entries from the open directory
* @a pDir to the vector @a pvpchDevs using either the full path or the
* realpath() and skipping hidden files and files on which realpath() fails. */
{
int err;
{
/* We (implicitly) require that PATH_MAX be defined */
return errno;
if (withRealPath)
else
return err;
}
return err;
}
/**
* Helper for walkDirectory to dump the names of a directory's entries into a
* vector of char pointers.
*
* @returns zero on success or (positive) posix error value.
* @param pcszPath the path to dump.
* @param pvpchDevs an empty vector of char pointers - must be cleaned up
* by the caller even on failure.
* @param withRealPath whether to canonicalise the filename with realpath
*/
int withRealPath)
{
int err;
if (!pDir)
return errno;
return err;
}
/**
* Helper for walkDirectory to walk a set of files, calling a function
* object on each.
*
* @returns zero on success or (positive) posix error value.
* @param pcszPath the path of the directory
* @param pvpchDevs vector of char strings containing the directory entry
* names
* @param pHandler Handler object which will be invoked on each file
*/
{
char **ppszEntry;
break;
return 0;
}
/**
* Walk a directory and applying a function object to each entry which doesn't
* start with a dot.
* @returns iprt status code
* @param pcszPath Directory to walk. May not be '/'-terminated.
* @param pHandler Handler object which will be invoked on each
* directoryentry
* @param withRealPath Whether to apply realpath() to each entry before
* invoking the handler
*/
/* static */
{
VECTOR_PTR(char *) vpchDevs;
int rc;
return rc;
if (!rc)
return RTErrConvertFromErrno(rc);
}
#define USBDEVICE_MAJOR 189
/** Deduce the bus that a USB device is plugged into from the device node
{
AssertReturn(devNum, 0);
}
/** Deduce the device number of a USB device on the bus from the device node
{
AssertReturn(devNum, 0);
}
/**
* interface. To be used with getDeviceInfoFromSysfs().
*/
typedef struct matchUSBDevice
{
/** The pathHandler object we inherit from - must come first */
{
AssertPtrReturn(pParent, false);
return true;
/* Sanity test of our static helpers */
if (!devnum)
return true;
char szDevPath[RTPATH_MAX];
if (cchDevPath < 0)
return true;
&info)))
return true;
return false;
}
{
}
/**
* device. To be used with getDeviceInfoFromSysfs().
*/
typedef struct matchUSBInterface
{
/** The pathHandler class we inherit from - must be the first member. */
/** The logic for testing whether a sysfs address corresponds to an
* interface of a device. Both must be referenced by their canonical
* sysfs paths. This is not tested, as the test requires file-system
* interaction. */
{
/* If this passes, pcszIface is at least cchDev long */
return false;
/* If this passes, pcszIface is longer than cchDev */
return false;
/* In sysfs an interface is an immediate subdirectory of the device */
return false;
/* And it always has a colon in its name */
return false;
/* And hopefully we have now elimitated everything else */
return true;
}
{
AssertPtrReturn(pParent, false);
return true;
if (pszDup)
char *, pszDup)))
return true;
return false;
}
/** This constructor is currently used to unit test the class logic in
* debug builds. Since no access is made to anything outside the class,
* this shouldn't cause any slowdown worth mentioning. */
{
}
/**
* Helper function to query the sysfs subsystem for information about USB
* devices attached to the system.
* @returns iprt status code
* @param pList where to add information about the drives detected
* @param pfSuccess Did we find anything?
*
* @returns IPRT status code
*/
/* static */
bool *pfSuccess)
{
LogFlowFunc (("pvecDevInfo=%p, pfSuccess=%p\n",
pvecDevInfo, pfSuccess));
do {
if (RT_FAILURE(rc))
break;
{
true);
if (RT_FAILURE(rc))
break;
}
} while(0);
if (pfSuccess)
return rc;
}
# endif /* VBOX_USB_WITH_INOTIFY */
#endif /* VBOX_USB_WITH_SYSFS */