diff --git a/stepmania/src/archutils/Win32/USB.cpp b/stepmania/src/archutils/Win32/USB.cpp index 890f1aecad..c96a48d2b5 100644 --- a/stepmania/src/archutils/Win32/USB.cpp +++ b/stepmania/src/archutils/Win32/USB.cpp @@ -15,7 +15,7 @@ extern "C" { #pragma warning( pop ) } -static char *GetUSBDevicePath (int num) +static CString GetUSBDevicePath (int num) { GUID guid; HidD_GetHidGuid(&guid); @@ -26,132 +26,191 @@ static char *GetUSBDevicePath (int num) SP_DEVICE_INTERFACE_DATA DeviceInterface; DeviceInterface.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); - char *ret = NULL; - PSP_INTERFACE_DEVICE_DETAIL_DATA DeviceDetail = NULL; - if (!SetupDiEnumDeviceInterfaces (DeviceInfo, NULL, &guid, num, &DeviceInterface)) - goto err; + { + SetupDiDestroyDeviceInfoList (DeviceInfo); + return ""; + } unsigned long size; SetupDiGetDeviceInterfaceDetail (DeviceInfo, &DeviceInterface, NULL, 0, &size, 0); - DeviceDetail = (PSP_INTERFACE_DEVICE_DETAIL_DATA) malloc(size); + PSP_INTERFACE_DEVICE_DETAIL_DATA DeviceDetail = (PSP_INTERFACE_DEVICE_DETAIL_DATA) malloc(size); DeviceDetail->cbSize = sizeof(SP_INTERFACE_DEVICE_DETAIL_DATA); + CString ret; if (SetupDiGetDeviceInterfaceDetail (DeviceInfo, &DeviceInterface, DeviceDetail, size, &size, NULL)) - { - ret = strdup(DeviceDetail->DevicePath); - } + ret = DeviceDetail->DevicePath; + free(DeviceDetail); -err: - SetupDiDestroyDeviceInfoList (DeviceInfo); - free (DeviceDetail); + SetupDiDestroyDeviceInfoList (DeviceInfo); return ret; } -static HANDLE OpenUSB (int VID, int PID, int num) + +bool USBDevice::Open(int VID, int PID, int blocksize, int num) { DWORD index = 0; - char *path; - HANDLE h = INVALID_HANDLE_VALUE; - - while ((path = GetUSBDevicePath (index++)) != NULL) + CString path; + while ((path = GetUSBDevicePath (index++)) != "") { - if(h != INVALID_HANDLE_VALUE) - CloseHandle (h); - - h = CreateFile (path, GENERIC_READ, - FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); - - free(path); + HANDLE h = CreateFile (path, GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); if(h == INVALID_HANDLE_VALUE) continue; HIDD_ATTRIBUTES attr; if (!HidD_GetAttributes (h, &attr)) + { + CloseHandle(h); continue; + } + CloseHandle(h); if ((VID != -1 && attr.VendorID != VID) && (PID != -1 && attr.ProductID != PID)) continue; /* This isn't it. */ /* The VID and PID match. */ - if(num-- == 0) - return h; + if(num-- > 0) + continue; + + io.Open(path, blocksize); + return true; } - if(h != INVALID_HANDLE_VALUE) - CloseHandle (h); - return INVALID_HANDLE_VALUE; + return false; } -USBDevice::USBDevice() +bool USBDevice::IsOpen() const { - ZeroMemory( &ov, sizeof(ov) ); - pending=false; - h = INVALID_HANDLE_VALUE; + return io.IsOpen(); } -USBDevice::~USBDevice() -{ - if(h != INVALID_HANDLE_VALUE) - CloseHandle(h); -} - -bool USBDevice::Open(int VID, int PID, int num) -{ - h = OpenUSB (VID, PID, num); - return h != INVALID_HANDLE_VALUE; -} - -bool USBDevice::IsOpen() -{ - return h != INVALID_HANDLE_VALUE; -} int USBDevice::GetPadEvent() { - if(h == INVALID_HANDLE_VALUE) + if(!IsOpen()) return -1; - int ret; + long buf; + if( io.read(&buf) <= 0 ) + return -1; - if(!pending) - { - /* Request feedback from the device. */ - unsigned long r; - ret = ReadFile(h, &buf, sizeof(buf), &r, &ov); - pending=true; + return buf; +} + +WindowsFileIO::WindowsFileIO() +{ + ZeroMemory( &ov, sizeof(ov) ); + h = INVALID_HANDLE_VALUE; + buf = NULL; +} + +WindowsFileIO::~WindowsFileIO() +{ + if(h != INVALID_HANDLE_VALUE) + CloseHandle(h); + delete[] buf; +} + +bool WindowsFileIO::Open(CString path, int blocksize_) +{ + LOG->Trace( "WindowsFileIO::open(%s)", path.c_str() ); + blocksize = blocksize_; + + if(buf) + delete[] buf; + buf = new char[blocksize]; + + if(h != INVALID_HANDLE_VALUE) + CloseHandle (h); + + h = CreateFile (path, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); + + if(h == INVALID_HANDLE_VALUE) + return false; + + queue_read(); + + return true; +} + +void WindowsFileIO::queue_read() +{ + /* Request feedback from the device. */ + unsigned long r; + ReadFile(h, buf, blocksize, &r, &ov); +} + +int WindowsFileIO::finish_read(void *p) +{ + LOG->Trace("this %p, %p", this, p); + /* We do; get the result. It'll go into the original buf + * we supplied on the original call; that's why buf is a + * member instead of a local. */ + unsigned long cnt; + int ret = GetOverlappedResult(h, &ov, &cnt, FALSE); + + if(ret == 0 && (GetLastError() == ERROR_IO_PENDING || GetLastError() == ERROR_IO_INCOMPLETE)) + return -1; + + queue_read(); + + if(ret == 0) + { + LOG->Warn(werr_ssprintf(GetLastError(), "Error reading Pump pad")); + return -1; } + memcpy( p, buf, cnt ); + return cnt; +} + +int WindowsFileIO::read(void *p) +{ + LOG->Trace( "WindowsFileIO::read()" ); + /* See if we have a response for our request (which we may * have made on a previous call): */ if(WaitForSingleObjectEx(h, 0, TRUE) == WAIT_TIMEOUT) return -1; - - /* We do; get the result. It'll go into the original &buf - * we supplied on the original call; that's why buf is a - * member instead of a local. */ - unsigned long cnt; - ret = GetOverlappedResult(h, &ov, &cnt, FALSE); - pending=false; - if(ret == 0 && (GetLastError() == ERROR_IO_PENDING || GetLastError() == ERROR_IO_INCOMPLETE)) + return finish_read(p); +} + +int WindowsFileIO::read_several(const vector &sources, void *p, int &actual, float timeout) +{ + HANDLE *Handles = new HANDLE[sources.size()]; + for( unsigned i = 0; i < sources.size(); ++i ) + Handles[i] = sources[i]->h; + + int ret = WaitForMultipleObjectsEx( sources.size(), Handles, false, int(timeout * 1000), true); + delete[] Handles; + + if( ret == -1 ) + { + LOG->Trace( werr_ssprintf(GetLastError(), "WaitForMultipleObjectsEx failed") ); return -1; + } - if(ret == 0) { - // this prints too much info in Win98 - // See if it's fixed--h should be INVALID_HANDLE_VALUE if - // the pad isn't there. -glenn - LOG->Warn(werr_ssprintf(GetLastError(), "Error reading Pump pad")); - return -1; - } + if( ret >= int(WAIT_OBJECT_0) && ret < int(WAIT_OBJECT_0+sources.size()) ) + { + actual = ret - WAIT_OBJECT_0; + return sources[actual]->finish_read(p); + } - return buf; + return 0; +} + +bool WindowsFileIO::IsOpen() const +{ + return h != INVALID_HANDLE_VALUE; } /* diff --git a/stepmania/src/archutils/Win32/USB.h b/stepmania/src/archutils/Win32/USB.h index 5f14e15511..10769dddca 100644 --- a/stepmania/src/archutils/Win32/USB.h +++ b/stepmania/src/archutils/Win32/USB.h @@ -1,20 +1,42 @@ #ifndef WIN32_USB_H #define WIN32_USB_H -class USBDevice +#include + +/* The API for Windows device I/O is obscenely horrible, so encapsulate it. */ +class WindowsFileIO { public: - USBDevice(); - ~USBDevice(); - int GetPadEvent(); - bool Open(int VID, int PID, int num); - bool IsOpen(); + WindowsFileIO(); + ~WindowsFileIO(); + bool Open(CString path, int blocksize); + bool IsOpen() const; + + /* Returns the amount of data read. */ + /* If nonblocking is true, this will */ + /* Nonblocking read. size must always be the same. Returns the number of bytes + * read, or 0. */ + int read(void *p); + static int read_several(const vector &sources, void *p, int &actual, float timeout); private: HANDLE h; OVERLAPPED ov; - long buf; - bool pending; + char *buf; + int blocksize; + + void queue_read(); + int finish_read(void *p); +}; + +class USBDevice +{ +public: + int GetPadEvent(); + bool Open(int VID, int PID, int blocksize, int num); + bool IsOpen() const; + + WindowsFileIO io; }; #endif