VirtualBox

Changeset 28553 in vbox for trunk/src/VBox/Main


Ignore:
Timestamp:
Apr 21, 2010 10:16:57 AM (15 years ago)
Author:
vboxsync
svn:sync-xref-src-repo-rev:
60363
Message:

Main: add support for polling for USB devices on Linux hosts without usbfs or hal

Location:
trunk/src/VBox/Main
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Main/linux/HostHardwareLinux.cpp

    r28349 r28553  
    4141#include <iprt/param.h>
    4242#include <iprt/path.h>
     43#include <iprt/pipe.h>
     44#include <iprt/poll.h>
     45#include <iprt/socket.h>
     46#include <iprt/string.h>
    4347#include <iprt/thread.h>  /* for RTThreadSleep() */
    44 #include <iprt/string.h>
    4548
    4649#ifdef RT_OS_LINUX
     
    6265# include <iprt/linux/sysfs.h>
    6366#endif /* RT_OS_LINUX */
     67
     68#include <fam.h>
     69
    6470#include <vector>
    6571
     
    11761182};
    11771183
     1184#ifdef VBOX_USB_WITH_SYSFS
     1185
     1186/** @todo move these elsewhere, but wait for words of wisdom from the iprt
     1187 * maintainer first :)
     1188 * The ideas behind the macros are on the one hand to increase code readability
     1189 * by reducing the space taken by the error handling (exceptions using pure C
     1190 * if you like, where a do {} while(0) block replaces the try {} catch() and a
     1191 * variable at a higher scope remembers the success or failure); and on the
     1192 * other to reduce duplication in error handling code, hopefully thereby
     1193 * raising the chances that error paths will actually work.
     1194 */
     1195
     1196/**
     1197 * Store an iprt status code (expected to be a function call with the status
     1198 * code as its return value) in a variable and execute a break statement if
     1199 * the status is unsuccessful.
     1200 * @param  rc    where to store the value
     1201 * @param  expr  the expression returning the status.  @a expr will only be
     1202 *               evaluated once by the macro call
     1203 */
     1204#define SETRCBREAK(rc, expr) \
     1205    if (RT_FAILURE(((rc) = (expr)))) \
     1206        break; \
     1207    else do {} while (0)
     1208
     1209/**
     1210 * Store an iprt status code (expected to be a function call with the status
     1211 * code as its return value) in a variable and execute a return statement if
     1212 * the status is unsuccessful.
     1213 * @param  rc    where to store the value
     1214 * @param  expr  the expression returning the status.  @a expr will only be
     1215 *               evaluated once by the macro call
     1216 */
     1217#define SETRCRETURN(rc, expr) \
     1218    do \
     1219    { \
     1220        (rc) = (expr); \
     1221        if (RT_FAILURE(rc)) \
     1222            return (rc); \
     1223    } while (0)
     1224
     1225static void testSetRCBreak(void)
     1226{
     1227    int rc = VINF_SUCCESS;
     1228    do {
     1229        SETRCBREAK(rc, VERR_WRONG_ORDER);
     1230        rc = VINF_SUCCESS;
     1231    } while(0);
     1232    Assert(rc == VERR_WRONG_ORDER);
     1233    rc = VERR_GENERAL_FAILURE;
     1234    do {
     1235        SETRCBREAK(rc, VINF_BUFFER_OVERFLOW);
     1236        Assert(rc == VINF_BUFFER_OVERFLOW);
     1237        rc = VERR_WRONG_ORDER;
     1238    } while(0);
     1239    Assert(rc == VERR_WRONG_ORDER);
     1240}
     1241
     1242static int testSetRCReturnWorker(int rc)
     1243{
     1244    int rc2 = VERR_WRONG_ORDER;
     1245    SETRCRETURN(rc2, rc);
     1246    Assert(rc == rc2);
     1247    return VINF_SUCCESS;
     1248}
     1249
     1250static void testSetRCReturn(void)
     1251{
     1252    Assert(testSetRCReturnWorker(VERR_GENERAL_FAILURE) == VERR_GENERAL_FAILURE);
     1253    AssertRCSuccess(testSetRCReturnWorker(VINF_BUFFER_OVERFLOW));
     1254}
     1255
     1256#define SYSFS_USB_DEVICE_PATH "/dev/bus/usb"
     1257#define SYSFS_WAKEUP_STRING "Wake up!"
     1258
     1259class hotplugSysfsFAMImpl : public VBoxMainHotplugWaiterImpl
     1260{
     1261    /** Pipe used to interrupt wait(), the read end. */
     1262    RTPIPE mhWakeupPipeR;
     1263    /** Pipe used to interrupt wait(), the write end. */
     1264    RTPIPE mhWakeupPipeW;
     1265    /** Our connection to FAM for polling for changes on sysfs. */
     1266    FAMConnection mFAMConnection;
     1267    /** Has our connection been initialised? */
     1268    bool mfFAMInitialised;
     1269    /** The iprt native handle of the FAM fd socket. */
     1270    RTSOCKET mhFAMFD;
     1271    /** Poll set containing the FAM socket and the termination pipe */
     1272    RTPOLLSET mhPollSet;
     1273    /** iprt result code from object initialisation.  Should be AssertReturn-ed
     1274     * on at the start of all methods.  I went this way because I didn't want
     1275     * to deal with exceptions. */
     1276    int mStatus;
     1277    /** ID values associates with the wakeup pipe and the FAM socket for polling
     1278     */
     1279    enum
     1280    {
     1281        RPIPE_ID = 1,
     1282        FAMFD_ID
     1283    };
     1284
     1285    /** Clean up any resources in use, gracefully skipping over any which have
     1286     * not yet been allocated or already cleaned up.  Intended to be called
     1287     * from the destructor or after a failed initialisation. */
     1288    void term(void);
     1289
     1290    /** Open our connection to FAM and convert the status to iprt.
     1291     * @todo  really convert the status
     1292     */
     1293    int openFAM(void)
     1294    {
     1295        if (FAMOpen(&mFAMConnection) < 0)
     1296            return VERR_UNRESOLVED_ERROR;  /* VERR_FAM_OPEN_FAILED */
     1297        mfFAMInitialised = true;
     1298        return VINF_SUCCESS;
     1299    }
     1300
     1301    /** Monitor a file through the FAM connection. */
     1302    int monitorDirectoryFAM(const char *pszName)
     1303    {
     1304        AssertReturn(mfFAMInitialised, VERR_WRONG_ORDER);
     1305        FAMRequest dummyReq;
     1306        if (FAMMonitorDirectory(&mFAMConnection, pszName, &dummyReq, NULL) < 0)
     1307            return VERR_UNRESOLVED_ERROR;  /* VERR_FAM_MONITOR_FILE_FAILED */
     1308        return VINF_SUCCESS;
     1309    }
     1310
     1311    /** Quick failure test of the monitor function - we temporarily invalidate
     1312     * the connection FD to trigger an error path. */
     1313    void testmonitorDirectoryFAM(void)
     1314    {
     1315        int oldFD = FAMCONNECTION_GETFD(&mFAMConnection);
     1316        FAMCONNECTION_GETFD(&mFAMConnection) = -1;
     1317        Assert(monitorDirectoryFAM(NULL) == VERR_UNRESOLVED_ERROR);
     1318        FAMCONNECTION_GETFD(&mFAMConnection) = oldFD;
     1319    }
     1320
     1321    /** Close our connection to FAM.  We ignore errors as there is no
     1322     * documentation as to what they mean, and the only error which might
     1323     * interest us (EINTR) should be (but isn't) handled inside the library. */
     1324    void closeFAM(void)
     1325    {
     1326        if (mfFAMInitialised)
     1327            FAMClose(&mFAMConnection);
     1328        mfFAMInitialised = false;
     1329    }
     1330public:
     1331    hotplugSysfsFAMImpl(void);
     1332    virtual ~hotplugSysfsFAMImpl(void)
     1333    {
     1334        term();
     1335#ifdef DEBUG
     1336        /** The first call to term should mark all resources as freed, so this
     1337         * should be a semantic no-op. */
     1338        term();
     1339#endif
     1340    }
     1341    /** Is sysfs available on this system?  If so we expect that this
     1342     * implementation will be usable. */
     1343    static bool SysfsAvailable(void)
     1344    {
     1345        return RTDirExists(SYSFS_USB_DEVICE_PATH);
     1346    }
     1347    /** @copydoc VBoxMainHotplugWaiter::Wait */
     1348    virtual int Wait(RTMSINTERVAL);
     1349    /** @copydoc VBoxMainHotplugWaiter::Interrupt */
     1350    virtual void Interrupt(void);
     1351};
     1352
     1353hotplugSysfsFAMImpl::hotplugSysfsFAMImpl(void) :
     1354    mhWakeupPipeR(NIL_RTPIPE), mhWakeupPipeW(NIL_RTPIPE),
     1355    mfFAMInitialised(false), mhFAMFD(NIL_RTSOCKET), mhPollSet(NIL_RTPOLLSET),
     1356    mStatus(VERR_WRONG_ORDER)
     1357{
     1358#ifdef DEBUG
     1359    /* Excercise the code path (term() on a not-fully-initialised object) as
     1360     * well as we can.  On an uninitialised object this method is a sematic
     1361     * no-op. */
     1362    term();
     1363    /** Unit test our macros above */
     1364    testSetRCBreak();
     1365    testSetRCReturn();
     1366#endif
     1367    int rc;
     1368    do {
     1369        SETRCBREAK(rc, RTPipeCreate(&mhWakeupPipeR, &mhWakeupPipeW, 0));
     1370        SETRCBREAK(rc, openFAM());
     1371        SETRCBREAK(rc, RTSocketFromNative
     1372                           (&mhFAMFD, FAMCONNECTION_GETFD(&mFAMConnection)));
     1373        SETRCBREAK(rc, monitorDirectoryFAM(SYSFS_USB_DEVICE_PATH));
     1374        SETRCBREAK(rc, RTPollSetCreate(&mhPollSet));
     1375        SETRCBREAK(rc, RTPollSetAddSocket
     1376                           (mhPollSet, mhFAMFD, RTPOLL_EVT_READ, FAMFD_ID));
     1377        SETRCBREAK(rc, RTPollSetAddPipe
     1378                           (mhPollSet, mhWakeupPipeR, RTPOLL_EVT_READ, RPIPE_ID));
     1379#ifdef DEBUG
     1380        /** Other tests */
     1381        testmonitorDirectoryFAM();
     1382#endif
     1383    } while(0);
     1384    mStatus = rc;
     1385    if (RT_FAILURE(rc))
     1386        term();
     1387}
     1388
     1389void hotplugSysfsFAMImpl::term(void)
     1390{
     1391    RTPipeClose(mhWakeupPipeR);
     1392    mhWakeupPipeR = NIL_RTPIPE;
     1393    RTPipeClose(mhWakeupPipeW);
     1394    mhWakeupPipeW = NIL_RTPIPE;
     1395    closeFAM();
     1396    mhFAMFD = NIL_RTSOCKET;
     1397    RTPollSetDestroy(mhPollSet);
     1398    mhPollSet = NIL_RTPOLLSET;
     1399}
     1400
     1401int hotplugSysfsFAMImpl::Wait(RTMSINTERVAL aMillies)
     1402{
     1403    uint32_t id;
     1404    int rc;
     1405    FAMEvent ev;
     1406
     1407    if (RT_FAILURE(mStatus))
     1408        return VERR_NOT_SUPPORTED;
     1409    /* timeout returns */
     1410    SETRCRETURN(rc, RTPoll(mhPollSet, aMillies, NULL, &id));
     1411    if (id == RPIPE_ID)
     1412    {
     1413        /* drain the pipe */
     1414        char szBuf[sizeof(SYSFS_WAKEUP_STRING)];
     1415        rc = RTPipeRead(mhWakeupPipeR, szBuf, sizeof(szBuf), NULL);
     1416        AssertRC(rc);
     1417        return VINF_SUCCESS;
     1418    }
     1419    AssertReturn(id == FAMFD_ID, VERR_NOT_SUPPORTED);
     1420    /* Samba re-opens the connection to FAM if this happens. */
     1421    AssertReturn(FAMNextEvent(&mFAMConnection, &ev) == 1,
     1422                 VERR_NOT_SUPPORTED);
     1423    switch(ev.code)
     1424    {
     1425        case FAMExists:
     1426        case FAMEndExist:
     1427            return VERR_TRY_AGAIN;
     1428        default:
     1429            break;
     1430    }
     1431    return VINF_SUCCESS;
     1432}
     1433
     1434void hotplugSysfsFAMImpl::Interrupt(void)
     1435{
     1436    size_t cbDummy;
     1437    int rc = RTPipeWrite(mhWakeupPipeW, SYSFS_WAKEUP_STRING,
     1438                         sizeof(SYSFS_WAKEUP_STRING), &cbDummy);
     1439    if (RT_SUCCESS(rc))
     1440        RTPipeFlush(mhWakeupPipeW);
     1441}
     1442
     1443#endif  /* VBOX_USB_WTH_SYSFS */
     1444
    11781445VBoxMainHotplugWaiter::VBoxMainHotplugWaiter(void)
    11791446{
     
    11821449    {
    11831450        mImpl = new hotplugDBusImpl;
     1451        return;
     1452    }
     1453#endif  /* !(defined RT_OS_LINUX && defined VBOX_WITH_DBUS) */
     1454#if defined VBOX_USB_WITH_SYSFS
     1455    if (hotplugSysfsFAMImpl::SysfsAvailable())
     1456    {
     1457        mImpl = new hotplugSysfsFAMImpl;
    11841458        return;
    11851459    }
  • trunk/src/VBox/Main/testcase/Makefile.kmk

    r27885 r28553  
    134134        $(if $(VBOX_WITH_DBUS),VBOX_WITH_DBUS,) \
    135135        $(if $(VBOX_USB_WITH_SYSFS),VBOX_USB_WITH_SYSFS,)
    136 tstHostHardwareLinux_LIBS     += $(PATH_OUT)/lib/USBLib.a
     136tstHostHardwareLinux_LIBS     += \
     137        $(PATH_OUT)/lib/USBLib.a \
     138        $(if $(VBOX_USB_WITH_SYSFS),fam,)
    137139
    138140
  • trunk/src/VBox/Main/testcase/tstHostHardwareLinux.cpp

    r26163 r28553  
    2929#include <iprt/param.h>
    3030#include <iprt/stream.h>
     31#include <iprt/thread.h>
    3132#include <iprt/linux/sysfs.h>
    3233
     
    3738#include <string.h>
    3839#include <stdlib.h>
     40
     41void doHotplugEvent(VBoxMainHotplugWaiter *waiter, RTMSINTERVAL cMillies)
     42{
     43    while (true)
     44    {
     45        int rc = waiter->Wait (cMillies);
     46        if (rc == VERR_TRY_AGAIN)
     47        {
     48            RTThreadYield();
     49            continue;
     50        }
     51        if (rc == VERR_TIMEOUT)
     52            continue;
     53        if (RT_FAILURE(rc))
     54        {
     55            RTPrintf("Failed!\n");
     56            exit(1);
     57        }
     58        if (RT_SUCCESS(rc))
     59            break;
     60    }
     61}
    3962
    4063int main()
     
    132155    RTPrintf ("Waiting for hotplug events.  Note that DBus often seems to deliver duplicate events in close succession.\n");
    133156    RTPrintf ("Waiting for a hotplug event for five seconds...\n");
    134     if (RT_FAILURE(waiter.Wait (5000)))
    135     {
    136         RTPrintf("Failed!\n");
    137         exit(1);
    138     }
     157    doHotplugEvent(&waiter, 5000);
    139158    RTPrintf ("Waiting for a hotplug event, Ctrl-C to abort...\n");
    140     if (RT_FAILURE(waiter.Wait(RT_INDEFINITE_WAIT)))
    141     {
    142         RTPrintf("Failed!\n");
    143         exit(1);
    144     }
     159    doHotplugEvent(&waiter, RT_INDEFINITE_WAIT);
    145160#endif  /* VBOX_USB_WITH_SYSFS */
    146161    return 0;
Note: See TracChangeset for help on using the changeset viewer.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette