poll for changes in usb/devices

This commit is contained in:
Chris Danford
2004-05-02 23:27:14 +00:00
parent 9d321a5bb7
commit 6bf3ffc4ce
@@ -16,6 +16,7 @@
#include <dirent.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/poll.h>
const CString TEMP_MOUNT_POINT = "@mctemp/";
@@ -132,119 +133,113 @@ void MemoryCardDriverThreaded_Linux::MountThreadMain()
LOG->Warn( "Failed to open \"%s\": %s", USB_DEVICE_LIST_FILE, strerror(errno) );
return;
}
time_t lastModTime = 0;
vector<UsbStorageDeviceEx> vDevicesLastSeen;
while( !m_bShutdown )
{
struct stat st;
if( fstat(fd, &st) == -1 )
{
LOG->Warn( "stat '%s' failed: %s", USB_DEVICE_LIST_FILE, strerror(errno) );
close( fd );
return;
}
bool bChanged = st.st_mtime != lastModTime;
lastModTime = st.st_mtime;
// TRICKY: The file time only has a resolution of one second. So, if the file
// changes more than one time in one second, we'll only pick up the first change.
// Hack around this by continually scanning for changes in any second that we detect
// a change.
if( bChanged || time(NULL)+1 <= lastModTime )
pollfd pfd = { fd, POLLIN, 0 };
int ret = poll( &pfd, 1, 100 );
switch( ret )
{
// TRICKY: We're waiting for a change in the USB device list, but
// the usb-storage descriptors take a bit longer to update. It's more convenient to wait
// on the USB device list because the usb-storage descriptors are separate files per
// device. So, sleep for a little bit of time after we detect a new USB device and give
// usb-storage a chance to initialize.
usleep(1000*300);
vector<UsbStorageDeviceEx> vDevicesNow;
GetNewStorageDevices( vDevicesNow );
vector<UsbStorageDeviceEx> &vNew = vDevicesNow;
vector<UsbStorageDeviceEx> &vOld = vDevicesLastSeen;
// check for disconnects
vector<UsbStorageDeviceEx*> vDisconnects;
for( unsigned i=0; i<vOld.size(); i++ )
{
UsbStorageDeviceEx &old = vOld[i];
if( find(vNew.begin(),vNew.end(),old) == vNew.end() )// didn't find
{
LOG->Trace( ssprintf("Disconnected bus %d port %d level %d path %s", old.iBus, old.iPort, old.iLevel, old.sOsMountDir.c_str()) );
vDisconnects.push_back( &old );
}
}
// check for connects
vector<UsbStorageDeviceEx*> vConnects;
for( unsigned i=0; i<vNew.size(); i++ )
{
UsbStorageDeviceEx &newd = vNew[i];
if( find(vOld.begin(),vOld.end(),newd) == vOld.end() )// didn't find
{
LOG->Trace( ssprintf("Connected bus %d port %d level %d path %s", newd.iBus, newd.iPort, newd.iLevel, newd.sOsMountDir.c_str()) );
vConnects.push_back( &newd );
}
}
// unmount all disconnects
for( unsigned i=0; i<vDisconnects.size(); i++ )
{
UsbStorageDeviceEx &d = *vDisconnects[i];
CString sCommand = "umount " + d.sOsMountDir;
ExecuteCommand( sCommand );
}
// mount all connects
for( unsigned i=0; i<vConnects.size(); i++ )
{
UsbStorageDeviceEx &d = *vConnects[i];
CString sCommand;
// unmount this device before trying to mount it. If this device
// wasn't unmounted before, then our mount call will fail and the
// mount may contain an out-of-date view of the files on the device.
sCommand = "umount " + d.sOsMountDir;
ExecuteCommand( sCommand ); // don't care if this fails
sCommand = "mount " + d.sOsMountDir;
bool bMountedSuccessfully = ExecuteCommand( sCommand );
d.bWriteTestSucceeded = bMountedSuccessfully && TestWrite( d.sOsMountDir );
// read name
this->Mount( &d, TEMP_MOUNT_POINT );
FILEMAN->FlushDirCache( TEMP_MOUNT_POINT );
Profile profile;
CString sProfileDir = TEMP_MOUNT_POINT + PREFSMAN->m_sMemoryCardProfileSubdir + '/';
profile.LoadEditableDataFromDir( sProfileDir );
d.sName = profile.GetDisplayName();
LOG->Trace( "write test %s", d.bWriteTestSucceeded ? "succeeded" : "failed" );
}
if( !vDisconnects.empty() || !vConnects.empty() )
{
LockMut( m_mutexStorageDevices );
m_bStorageDevicesChanged = true;
m_vStorageDevices = vDevicesNow;
for( unsigned i=0; i<m_vStorageDevices.size(); i++ )
{
UsbStorageDeviceEx &d = m_vStorageDevices[i];
LOG->Trace( "index %d, bWriteTestSucceeded %d", i, d.bWriteTestSucceeded );
}
}
vDevicesLastSeen = vDevicesNow;
case 1:
// file changed. Fall through.
break;
case 0: // no change. Poll again.
continue;
case -1:
LOG->Warn( "Error polling" );
continue;
}
usleep( 1000*100 ); // 100 ms
// TRICKY: We're waiting for a change in the USB device list, but
// the usb-storage descriptors take a bit longer to update. It's more convenient to wait
// on the USB device list because the usb-storage descriptors are separate files per
// device. So, sleep for a little bit of time after we detect a new USB device and give
// usb-storage a chance to initialize.
usleep(1000*300);
vector<UsbStorageDeviceEx> vDevicesNow;
GetNewStorageDevices( vDevicesNow );
vector<UsbStorageDeviceEx> &vNew = vDevicesNow;
vector<UsbStorageDeviceEx> &vOld = vDevicesLastSeen;
// check for disconnects
vector<UsbStorageDeviceEx*> vDisconnects;
for( unsigned i=0; i<vOld.size(); i++ )
{
UsbStorageDeviceEx &old = vOld[i];
if( find(vNew.begin(),vNew.end(),old) == vNew.end() )// didn't find
{
LOG->Trace( ssprintf("Disconnected bus %d port %d level %d path %s", old.iBus, old.iPort, old.iLevel, old.sOsMountDir.c_str()) );
vDisconnects.push_back( &old );
}
}
// check for connects
vector<UsbStorageDeviceEx*> vConnects;
for( unsigned i=0; i<vNew.size(); i++ )
{
UsbStorageDeviceEx &newd = vNew[i];
if( find(vOld.begin(),vOld.end(),newd) == vOld.end() )// didn't find
{
LOG->Trace( ssprintf("Connected bus %d port %d level %d path %s", newd.iBus, newd.iPort, newd.iLevel, newd.sOsMountDir.c_str()) );
vConnects.push_back( &newd );
}
}
// unmount all disconnects
for( unsigned i=0; i<vDisconnects.size(); i++ )
{
UsbStorageDeviceEx &d = *vDisconnects[i];
CString sCommand = "umount " + d.sOsMountDir;
ExecuteCommand( sCommand );
}
// mount all connects
for( unsigned i=0; i<vConnects.size(); i++ )
{
UsbStorageDeviceEx &d = *vConnects[i];
CString sCommand;
// unmount this device before trying to mount it. If this device
// wasn't unmounted before, then our mount call will fail and the
// mount may contain an out-of-date view of the files on the device.
sCommand = "umount " + d.sOsMountDir;
ExecuteCommand( sCommand ); // don't care if this fails
sCommand = "mount " + d.sOsMountDir;
bool bMountedSuccessfully = ExecuteCommand( sCommand );
d.bWriteTestSucceeded = bMountedSuccessfully && TestWrite( d.sOsMountDir );
// read name
this->Mount( &d, TEMP_MOUNT_POINT );
FILEMAN->FlushDirCache( TEMP_MOUNT_POINT );
Profile profile;
CString sProfileDir = TEMP_MOUNT_POINT + PREFSMAN->m_sMemoryCardProfileSubdir + '/';
profile.LoadEditableDataFromDir( sProfileDir );
d.sName = profile.GetDisplayName();
LOG->Trace( "write test %s", d.bWriteTestSucceeded ? "succeeded" : "failed" );
}
if( !vDisconnects.empty() || !vConnects.empty() )
{
LockMut( m_mutexStorageDevices );
m_bStorageDevicesChanged = true;
m_vStorageDevices = vDevicesNow;
for( unsigned i=0; i<m_vStorageDevices.size(); i++ )
{
UsbStorageDeviceEx &d = m_vStorageDevices[i];
LOG->Trace( "index %d, bWriteTestSucceeded %d", i, d.bWriteTestSucceeded );
}
}
vDevicesLastSeen = vDevicesNow;
}
CHECKPOINT;
}