Changeset 28553 in vbox for trunk/src/VBox/Main
- Timestamp:
- Apr 21, 2010 10:16:57 AM (15 years ago)
- svn:sync-xref-src-repo-rev:
- 60363
- Location:
- trunk/src/VBox/Main
- Files:
-
- 3 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Main/linux/HostHardwareLinux.cpp
r28349 r28553 41 41 #include <iprt/param.h> 42 42 #include <iprt/path.h> 43 #include <iprt/pipe.h> 44 #include <iprt/poll.h> 45 #include <iprt/socket.h> 46 #include <iprt/string.h> 43 47 #include <iprt/thread.h> /* for RTThreadSleep() */ 44 #include <iprt/string.h>45 48 46 49 #ifdef RT_OS_LINUX … … 62 65 # include <iprt/linux/sysfs.h> 63 66 #endif /* RT_OS_LINUX */ 67 68 #include <fam.h> 69 64 70 #include <vector> 65 71 … … 1176 1182 }; 1177 1183 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 1225 static 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 1242 static 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 1250 static 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 1259 class 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 } 1330 public: 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 1353 hotplugSysfsFAMImpl::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 1389 void 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 1401 int 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 1434 void 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 1178 1445 VBoxMainHotplugWaiter::VBoxMainHotplugWaiter(void) 1179 1446 { … … 1182 1449 { 1183 1450 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; 1184 1458 return; 1185 1459 } -
trunk/src/VBox/Main/testcase/Makefile.kmk
r27885 r28553 134 134 $(if $(VBOX_WITH_DBUS),VBOX_WITH_DBUS,) \ 135 135 $(if $(VBOX_USB_WITH_SYSFS),VBOX_USB_WITH_SYSFS,) 136 tstHostHardwareLinux_LIBS += $(PATH_OUT)/lib/USBLib.a 136 tstHostHardwareLinux_LIBS += \ 137 $(PATH_OUT)/lib/USBLib.a \ 138 $(if $(VBOX_USB_WITH_SYSFS),fam,) 137 139 138 140 -
trunk/src/VBox/Main/testcase/tstHostHardwareLinux.cpp
r26163 r28553 29 29 #include <iprt/param.h> 30 30 #include <iprt/stream.h> 31 #include <iprt/thread.h> 31 32 #include <iprt/linux/sysfs.h> 32 33 … … 37 38 #include <string.h> 38 39 #include <stdlib.h> 40 41 void 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 } 39 62 40 63 int main() … … 132 155 RTPrintf ("Waiting for hotplug events. Note that DBus often seems to deliver duplicate events in close succession.\n"); 133 156 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); 139 158 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); 145 160 #endif /* VBOX_USB_WITH_SYSFS */ 146 161 return 0;
Note:
See TracChangeset
for help on using the changeset viewer.