From 6f5404c6fea051ebd85009ff38fc4646501208d5 Mon Sep 17 00:00:00 2001 From: Chris Danford Date: Tue, 13 Apr 2004 08:05:50 +0000 Subject: [PATCH] add move Linux mem card write test to seprarate thread --- .../src/arch/ArchHooks/ArchHooks_Unix.cpp | 1 + .../src/arch/MemoryCard/MemoryCardDriver.h | 4 +- .../MemoryCardDriverThreaded_Linux.cpp | 408 ++++++++++++++++++ .../MemoryCardDriverThreaded_Linux.h | 24 ++ .../arch/MemoryCard/MemoryCardDriver_Null.h | 13 +- stepmania/src/arch/arch.cpp | 5 +- stepmania/src/arch/arch_linux.h | 2 +- 7 files changed, 445 insertions(+), 12 deletions(-) create mode 100644 stepmania/src/arch/MemoryCard/MemoryCardDriverThreaded_Linux.cpp create mode 100644 stepmania/src/arch/MemoryCard/MemoryCardDriverThreaded_Linux.h diff --git a/stepmania/src/arch/ArchHooks/ArchHooks_Unix.cpp b/stepmania/src/arch/ArchHooks/ArchHooks_Unix.cpp index fca40193be..0d210359d2 100644 --- a/stepmania/src/arch/ArchHooks/ArchHooks_Unix.cpp +++ b/stepmania/src/arch/ArchHooks/ArchHooks_Unix.cpp @@ -6,6 +6,7 @@ #include "archutils/Unix/SignalHandler.h" #include "archutils/Unix/GetSysInfo.h" #include "archutils/Unix/LinuxThreadHelpers.h" +#include #if defined(CRASH_HANDLER) #include "archutils/Unix/CrashHandler.h" diff --git a/stepmania/src/arch/MemoryCard/MemoryCardDriver.h b/stepmania/src/arch/MemoryCard/MemoryCardDriver.h index 8be79b8a2e..c57f113d62 100644 --- a/stepmania/src/arch/MemoryCard/MemoryCardDriver.h +++ b/stepmania/src/arch/MemoryCard/MemoryCardDriver.h @@ -47,8 +47,8 @@ public: virtual void GetStorageDevices( vector& vStorageDevicesOut ) = 0; virtual bool MountAndTestWrite( UsbStorageDevice* pDevice, CString sMountPoint ) = 0; // return false if mount or write fails virtual void Unmount( UsbStorageDevice* pDevice, CString sMountPoint ) = 0; - virtual void Flush( UsbStorageDevice* pDevice ) {} - virtual void ResetUsbStorage() {}; + virtual void Flush( UsbStorageDevice* pDevice ) = 0; + virtual void ResetUsbStorage() = 0; }; #endif diff --git a/stepmania/src/arch/MemoryCard/MemoryCardDriverThreaded_Linux.cpp b/stepmania/src/arch/MemoryCard/MemoryCardDriverThreaded_Linux.cpp new file mode 100644 index 0000000000..b9ecae74c4 --- /dev/null +++ b/stepmania/src/arch/MemoryCard/MemoryCardDriverThreaded_Linux.cpp @@ -0,0 +1,408 @@ +#include "global.h" +#include "MemoryCardDriverThreaded_Linux.h" +#include "RageLog.h" +#include "RageUtil.h" +#include "RageFileManager.h" + +#include +#include +#include +#include +#include +#include + +#include + +static const char *USB_DEVICE_LIST_FILE = "/proc/bus/usb/devices"; +static const char *ETC_MTAB = "/etc/mtab"; + +void GetNewStorageDevices( vector& vDevicesOut ); + +template +bool VectorsAreEqual( const T &a, const T &b ) +{ + if( a.size() != b.size() ) + return false; + + for( unsigned i=0; iWarn( "Failed to open \"%s\": %s", USB_DEVICE_LIST_FILE, strerror(errno) ); + return; + } + + time_t lastModTime = 0; + + vector 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; + + if( bChanged ) + { + // 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 vDevicesNow; + GetNewStorageDevices( vDevicesNow ); + + vector &vNew = vDevicesNow; + vector &vOld = vDevicesLastSeen; + + // check for disconnects + vector vDisconnects; + for( unsigned i=0; iTrace( ssprintf("Disconnected bus %d port %d device %d path %s", old.iBus, old.iPortOnHub, old.iDeviceOnBus, old.sOsMountDir.c_str()) ); + vDisconnects.push_back( &old ); + } + } + + // check for connects + vector vConnects; + for( unsigned i=0; iTrace( ssprintf("Connected bus %d port %d device %d path %s", newd.iBus, newd.iPortOnHub, newd.iDeviceOnBus, newd.sOsMountDir.c_str()) ); + vConnects.push_back( &newd ); + } + } + + // unmount all disconnects + for( unsigned i=0; iTrace( "executing '%s'", sCommand.c_str() ); + if( system(sCommand) == -1 ) + LOG->Warn( "failed to execute '%s'", sCommand.c_str() ); + } + + // mount all connects + for( unsigned i=0; iTrace( "executing '%s'", sCommand.c_str() ); + if( system(sCommand) == -1 ) + LOG->Warn( "failed to execute '%s'", sCommand.c_str() ); + + sCommand = "mount " + d.sOsMountDir; + LOG->Trace( "executing '%s'", sCommand.c_str() ); + if( system(sCommand) == -1 ) + LOG->Warn( "failed to execute '%s'", sCommand.c_str() ); + + d.bWriteTestSucceeded = TestWrite( d.sOsMountDir ); + 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; iTrace( "index %d, bWriteTestSucceeded %d", i, d.bWriteTestSucceeded ); + } + } + + vDevicesLastSeen = vDevicesNow; + } + usleep( 1000*100 ); // 100 ms + } + CHECKPOINT; +} + +MemoryCardDriverThreaded_Linux::MemoryCardDriverThreaded_Linux() +{ + this->StartThread(); +} + +void GetNewStorageDevices( vector& vDevicesOut ) +{ + vDevicesOut.clear(); + + { + // Find all attached USB devices. Output looks like: + + // T: Bus=02 Lev=00 Prnt=00 Port=00 Cnt=00 Dev#= 1 Spd=12 MxCh= 2 + // B: Alloc= 0/900 us ( 0%), #Int= 0, #Iso= 0 + // D: Ver= 1.00 Cls=09(hub ) Sub=00 Prot=00 MxPS= 8 #Cfgs= 1 + // P: Vendor=0000 ProdID=0000 Rev= 0.00 + // S: Product=USB UHCI Root Hub + // S: SerialNumber=ff80 + // C:* #Ifs= 1 Cfg#= 1 Atr=40 MxPwr= 0mA + // I: If#= 0 Alt= 0 #EPs= 1 Cls=09(hub ) Sub=00 Prot=00 Driver=hub + // E: Ad=81(I) Atr=03(Int.) MxPS= 8 Ivl=255ms + // T: Bus=02 Lev=01 Prnt=01 Port=00 Cnt=01 Dev#= 2 Spd=12 MxCh= 0 + // D: Ver= 1.10 Cls=00(>ifc ) Sub=00 Prot=00 MxPS= 8 #Cfgs= 1 + // P: Vendor=04e8 ProdID=0100 Rev= 0.01 + // S: Manufacturer=KINGSTON + // S: Product=USB DRIVE + // S: SerialNumber=1125198948886 + // C:* #Ifs= 1 Cfg#= 1 Atr=80 MxPwr= 90mA + // I: If#= 0 Alt= 0 #EPs= 2 Cls=08(stor.) Sub=06 Prot=50 Driver=usb-storage + // E: Ad=82(I) Atr=02(Bulk) MxPS= 64 Ivl=0ms + // E: Ad=03(O) Atr=02(Bulk) MxPS= 64 Ivl=0ms + + ifstream f; + CString fn = "/proc/bus/usb/devices"; + f.open(fn); + if( !f.is_open() ) + { + LOG->Warn( "can't open '%s'", fn.c_str() ); + return; + } + + UsbStorageDeviceEx usbd; + CString sLine; + while( getline(f, sLine) ) + { + int iRet, iThrowAway; + + // T: Bus=02 Lev=00 Prnt=00 Port=00 Cnt=00 Dev#= 1 Spd=12 MxCh= 2 + int iBus, iPort, iDevice; + iRet = sscanf( sLine.c_str(), "T: Bus=%d Lev=%d Prnt=%d Port=%d Cnt=%d Dev#=%d Spd=%d MxCh=%d", &iBus, &iThrowAway, &iThrowAway, &iPort, &iThrowAway, &iDevice, &iThrowAway, &iThrowAway ); + if( iRet == 8 ) + { + usbd.iBus = iBus; + usbd.iDeviceOnBus = iDevice; + usbd.iPortOnHub = iPort; + continue; // stop processing this line + } + + // S: SerialNumber=ff80 + char szSerial[1024]; + iRet = sscanf( sLine.c_str(), "S: SerialNumber=%[^\n]", szSerial ); + if( iRet == 1 ) + { + usbd.sSerial = szSerial; + continue; // stop processing this line + } + + // I: If#= 0 Alt= 0 #EPs= 2 Cls=08(stor.) Sub=06 Prot=50 Driver=usb-storage + int iClass; + iRet = sscanf( sLine.c_str(), "I: If#=%d Alt=%d #EPs=%d Cls=%d", &iThrowAway, &iThrowAway, &iThrowAway, &iClass ); + if( iRet == 4 ) + { + if( iClass == 8 ) // storage class + { + vDevicesOut.push_back( usbd ); + LOG->Trace( "iUsbStorageIndex: %d, iBus: %d, iDeviceOnBus: %d, iPortOnHub: %d", + usbd.iUsbStorageIndex, usbd.iBus, usbd.iDeviceOnBus, usbd.iPortOnHub ); + } + continue; // stop processing this line + } + } + } + + { + // Find the usb-storage device index for all storage class devices. + for( unsigned i=0; true; i++ ) + { + // Read the usb-storage descriptor. It looks like: + + // Host scsi0: usb-storage + // Vendor: KINGSTON + // Product: USB DRIVE + // Serial Number: 1125198948886 + // Protocol: Transparent SCSI + // Transport: Bulk + // GUID: 04e801000001125198948886 + // Attached: Yes + + CString fn = ssprintf( "/proc/scsi/usb-storage-%d/%d", i, i ); + ifstream f; + f.open(fn); + if( !f.is_open() ) + break; + + CString sLine; + while( getline(f, sLine) ) + { + // Serial Number: 1125198948886 + char szSerial[1024]; + int iRet = sscanf( sLine.c_str(), "Serial Number: %[^\n]", szSerial ); + if( iRet == 1 ) // we found our line + { + // Search for the device corresponding to this serial number. + for( unsigned j=0; jTrace( "iUsbStorageIndex: %d, iBus: %d, iDeviceOnBus: %d, iPortOnHub: %d, sSerial: %s", + usbd.iUsbStorageIndex, usbd.iBus, usbd.iDeviceOnBus, usbd.iPortOnHub, usbd.sSerial.c_str() ); + break; // done looking for the corresponding device. + } + } + break; // we already found the line we care about + } + } + } + } + + { + // Find where each device is mounted. Output looks like: + + // /dev/sda1 /mnt/flash1 auto noauto,owner 0 0 + // /dev/sdb1 /mnt/flash2 auto noauto,owner 0 0 + // /dev/sdc1 /mnt/flash3 auto noauto,owner 0 0 + + CString fn = "/etc/fstab"; + RageFile f; + if( !f.Open(fn) ) + { + LOG->Warn( "can't open '%s': %s", fn.c_str(), f.GetError().c_str() ); + return; + } + + CString sLine; + while( f.GetLine(sLine) ) + { + // /dev/sda1 /mnt/flash1 auto noauto,owner 0 0 + char cScsiDev; + char szMountPoint[1024]; + int iRet = sscanf( sLine.c_str(), "/dev/sd%c1 %s", &cScsiDev, szMountPoint ); + if( iRet != 2 ) + continue; // don't process this line + + int iUsbStorageIndex = cScsiDev - 'a'; + CString sMountPoint = szMountPoint; + TrimLeft( sMountPoint ); + TrimRight( sMountPoint ); + + // search for the usb-storage device corresponding to the SCSI device + for( unsigned i=0; iTrace( "iUsbStorageIndex: %d, iBus: %d, iDeviceOnBus: %d, iPortOnHub: %d, sOsMountDir: %s", + usbd.iUsbStorageIndex, usbd.iBus, usbd.iDeviceOnBus, usbd.iPortOnHub, usbd.sOsMountDir.c_str() ); + + break; // stop looking for a match + } + } + } + } +} + +void MemoryCardDriverThreaded_Linux::Mount( UsbStorageDevice* pDevice, CString sMountPoint ) +{ + ASSERT( !pDevice->sOsMountDir.empty() ); + + /* Unmount any previous mounts for this mountpoint. */ + vector Mounts; + FILEMAN->GetLoadedDrivers( Mounts ); + for( unsigned i = 0; i < Mounts.size(); ++i ) + { + if( Mounts[i].Type.CompareNoCase( "dir" ) ) + continue; // wrong type + if( Mounts[i].Root.CompareNoCase( pDevice->sOsMountDir ) ) + continue; // wrong root + FILEMAN->Unmount( Mounts[i].Type, Mounts[i].Root, Mounts[i].MountPoint ); + } + + FILEMAN->Mount( "dir", pDevice->sOsMountDir, sMountPoint.c_str() ); + LOG->Trace( "FILEMAN->Mount %s %s", pDevice->sOsMountDir.c_str(), sMountPoint.c_str() ); +} + +void MemoryCardDriverThreaded_Linux::Unmount( UsbStorageDevice* pDevice, CString sMountPoint ) +{ + if( pDevice->sOsMountDir.empty() ) + return; + + // already unmounted by the mounting thread + //CString sCommand = "umount " + pDevice->sOsMountDir; + //LOG->Trace( "executing '%s'", sCommand.c_str() ); + //system( sCommand ); +} + +void MemoryCardDriverThreaded_Linux::Flush( UsbStorageDevice* pDevice ) +{ + if( pDevice->sOsMountDir.empty() ) + return; + + // "sync" will only flush all file systems at the same time. -Chris + // I don't think so. Also, sync() merely queues a flush; it doesn't guarantee + // that the flush is completed on return. However, we can mount the filesystem + // with the flag "-o sync", which forces synchronous access (but that's probably + // very slow.) -glenn + CString sCommand = "mount -o remount " + pDevice->sOsMountDir; + LOG->Trace( "executing '%s'", sCommand.c_str() ); + if( system(sCommand) == -1 ) + LOG->Warn( "failed to execute '%s'", sCommand.c_str() ); + +} + +void MemoryCardDriverThreaded_Linux::ResetUsbStorage() +{ + CString sCommand; + sCommand = "rmmod usb-storage"; + if( system(sCommand) == -1 ) + LOG->Warn( "failed to execute '%s'", sCommand.c_str() ); + sCommand = "modprobe usb-storage"; + if( system(sCommand) == -1 ) + LOG->Warn( "failed to execute '%s'", sCommand.c_str() ); +} + + +/* + * Copyright (c) 2003 by the person(s) listed below. All rights reserved. + * Chris Danford + */ diff --git a/stepmania/src/arch/MemoryCard/MemoryCardDriverThreaded_Linux.h b/stepmania/src/arch/MemoryCard/MemoryCardDriverThreaded_Linux.h new file mode 100644 index 0000000000..d7344b4368 --- /dev/null +++ b/stepmania/src/arch/MemoryCard/MemoryCardDriverThreaded_Linux.h @@ -0,0 +1,24 @@ +#ifndef MemoryCardDriverThreaded_Linux_H +#define MemoryCardDriverThreaded_Linux_H 1 + +#include "MemoryCardDriverThreaded.h" + +class MemoryCardDriverThreaded_Linux : public MemoryCardDriverThreaded +{ +public: + MemoryCardDriverThreaded_Linux(); + + virtual void Unmount( UsbStorageDevice* pDevice, CString sMountPoint ); + virtual void Flush( UsbStorageDevice* pDevice ); + virtual void ResetUsbStorage(); +protected: + virtual void MountThreadMain(); + virtual void Mount( UsbStorageDevice* pDevice, CString sMountPoint ); +}; + +#endif + +/* + * Copyright (c) 2003 by the person(s) listed below. All rights reserved. + * Chris Danford + */ diff --git a/stepmania/src/arch/MemoryCard/MemoryCardDriver_Null.h b/stepmania/src/arch/MemoryCard/MemoryCardDriver_Null.h index 7211436f4f..64686aa63a 100644 --- a/stepmania/src/arch/MemoryCard/MemoryCardDriver_Null.h +++ b/stepmania/src/arch/MemoryCard/MemoryCardDriver_Null.h @@ -6,11 +6,14 @@ class MemoryCardDriver_Null : public MemoryCardDriver { public: - MemoryCardDriver_Null() {}; - virtual bool StorageDevicesChanged() { return false; }; - virtual void GetStorageDevices( vector& vStorageDevicesOut ) {}; - virtual bool MountAndTestWrite( UsbStorageDevice* pDevice, CString sMountPoint ) { return false; }; - virtual void Unmount( UsbStorageDevice* pDevice, CString sMountPoint ) {}; + MemoryCardDriver_Null() {} + virtual bool StorageDevicesChanged() { return false; } + virtual void GetStorageDevices( vector& vStorageDevicesOut ) {} + virtual bool MountAndTestWrite( UsbStorageDevice* pDevice, CString sMountPoint ) { return false; } + virtual void Unmount( UsbStorageDevice* pDevice, CString sMountPoint ) {} + virtual void Flush( UsbStorageDevice* pDevice ) {} + virtual void ResetUsbStorage() {} + }; #endif diff --git a/stepmania/src/arch/arch.cpp b/stepmania/src/arch/arch.cpp index 2aac17c8ce..30f6068cae 100644 --- a/stepmania/src/arch/arch.cpp +++ b/stepmania/src/arch/arch.cpp @@ -138,9 +138,6 @@ LightsDriver *MakeLightsDriver(CString driver) #ifdef _WINDOWS if(!driver.CompareNoCase("Parallel")) ret = new LightsDriver_Win32Parallel; -#endif -#ifdef LINUX - #endif if(!driver.CompareNoCase("SystemMessage")) ret = new LightsDriver_SystemMessage; if(!driver.CompareNoCase("Null") || !ret ) @@ -159,7 +156,7 @@ MemoryCardDriver *MakeMemoryCardDriver() MemoryCardDriver *ret = NULL; #ifdef LINUX - ret = new MemoryCardDriver_Linux; + ret = new MemoryCardDriverThreaded_Linux; #elif _WINDOWS ret = new MemoryCardDriverThreaded_Windows; #endif diff --git a/stepmania/src/arch/arch_linux.h b/stepmania/src/arch/arch_linux.h index 9bc3e7b749..2d01f81b8c 100644 --- a/stepmania/src/arch/arch_linux.h +++ b/stepmania/src/arch/arch_linux.h @@ -19,7 +19,7 @@ #include "ArchHooks/ArchHooks_Unix.h" -#include "MemoryCard/MemoryCardDriver_Linux.h" +#include "MemoryCard/MemoryCardDriverThreaded_Linux.h" #endif /*