Make adjustments to the JoystickProtocolHandler so that it works in accordance

with the BJoystick requirements:

* Make the Read() non-blocking. This is required as BJoystick is a polling
  interface. A single current state is used that is updated by a separate thread
  on report arrival. The thread is spawned as soon as the ProtocolHandler is
  opened for the first time (and quit at the first wait return after the
  ProtocolHandler is closed). With this we can simply return the current state
  on read.
* Remove the ring buffer as it was not needed in the first place. This also
  happens to solve the problem of sharing a JoystickProtocolHandler. Before,
  concurrent reads would queue up the same result multiple times in the ring
  buffer and then return stale data on the next update.

Solves most of #7629.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@41865 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Michael Lotz 2011-06-02 00:56:18 +00:00
parent 23249681ab
commit 5052c5b678
2 changed files with 92 additions and 19 deletions

View File

@ -22,9 +22,13 @@
JoystickProtocolHandler::JoystickProtocolHandler(HIDReport &report)
:
ProtocolHandler(report.Device(), "joystick/usb/", 512),
fReport(report)
ProtocolHandler(report.Device(), "joystick/usb/", 0),
fReport(report),
fUpdateThread(-1)
{
mutex_init(&fUpdateLock, "joystick update lock");
memset(&fCurrentValues, 0, sizeof(extended_joystick));
for (uint32 i = 0; i < MAX_AXES; i++)
fAxis[i] = NULL;
@ -129,6 +133,45 @@ JoystickProtocolHandler::AddHandlers(HIDDevice &device,
}
status_t
JoystickProtocolHandler::Open(uint32 flags, uint32 *cookie)
{
status_t result = mutex_lock(&fUpdateLock);
if (result != B_OK)
return result;
if (fUpdateThread < 0) {
fUpdateThread = spawn_kernel_thread(_UpdateThread, "joystick update",
B_NORMAL_PRIORITY, (void *)this);
if (fUpdateThread < 0)
result = fUpdateThread;
else
resume_thread(fUpdateThread);
}
mutex_unlock(&fUpdateLock);
if (result != B_OK)
return result;
return ProtocolHandler::Open(flags, cookie);
}
status_t
JoystickProtocolHandler::Close(uint32 *cookie)
{
status_t result = mutex_lock(&fUpdateLock);
if (result == B_OK) {
fUpdateThread = -1;
mutex_unlock(&fUpdateLock);
}
return ProtocolHandler::Close(cookie);
}
status_t
JoystickProtocolHandler::Read(uint32 *cookie, off_t position, void *buffer,
size_t *numBytes)
@ -136,15 +179,15 @@ JoystickProtocolHandler::Read(uint32 *cookie, off_t position, void *buffer,
if (*numBytes < sizeof(extended_joystick))
return B_BUFFER_OVERFLOW;
while (RingBufferReadable() == 0) {
status_t result = _ReadReport();
if (result != B_OK)
return result;
// this is a polling interface, we just return the current value
status_t result = mutex_lock(&fUpdateLock);
if (result != B_OK) {
*numBytes = 0;
return result;
}
status_t result = RingBufferRead(buffer, sizeof(extended_joystick));
if (result != B_OK)
return result;
memcpy(buffer, &fCurrentValues, sizeof(extended_joystick));
mutex_unlock(&fUpdateLock);
*numBytes = sizeof(extended_joystick);
return B_OK;
@ -206,8 +249,22 @@ JoystickProtocolHandler::Control(uint32 *cookie, uint32 op, void *buffer,
}
int32
JoystickProtocolHandler::_UpdateThread(void *data)
{
JoystickProtocolHandler *handler = (JoystickProtocolHandler *)data;
while (handler->fUpdateThread == find_thread(NULL)) {
status_t result = handler->_Update();
if (result != B_OK)
return result;
}
return B_OK;
}
status_t
JoystickProtocolHandler::_ReadReport()
JoystickProtocolHandler::_Update()
{
status_t result = fReport.WaitForReport(B_INFINITE_TIMEOUT);
if (result != B_OK) {
@ -226,15 +283,20 @@ JoystickProtocolHandler::_ReadReport()
return B_OK;
}
extended_joystick info;
memset(&info, 0, sizeof(info));
result = mutex_lock(&fUpdateLock);
if (result != B_OK) {
fReport.DoneProcessing();
return result;
}
memset(&fCurrentValues, 0, sizeof(extended_joystick));
for (uint32 i = 0; i < MAX_AXES; i++) {
if (fAxis[i] == NULL)
continue;
if (fAxis[i]->Extract() == B_OK && fAxis[i]->Valid())
info.axes[i] = (int16)fAxis[i]->ScaledData(16, true);
fCurrentValues.axes[i] = (int16)fAxis[i]->ScaledData(16, true);
}
for (uint32 i = 0; i < MAX_BUTTONS; i++) {
@ -242,13 +304,16 @@ JoystickProtocolHandler::_ReadReport()
if (button == NULL)
break;
if (button->Extract() == B_OK && button->Valid())
info.buttons |= (button->Data() & 1) << (button->UsageID() - 1);
if (button->Extract() == B_OK && button->Valid()) {
fCurrentValues.buttons
|= (button->Data() & 1) << (button->UsageID() - 1);
}
}
fReport.DoneProcessing();
TRACE("got joystick report\n");
info.timestamp = system_time();
return RingBufferWrite(&info, sizeof(info));
fCurrentValues.timestamp = system_time();
mutex_unlock(&fUpdateLock);
return B_OK;
}

View File

@ -11,6 +11,7 @@
#include "ProtocolHandler.h"
#include <joystick_driver.h>
#include <lock.h>
class HIDCollection;
@ -25,6 +26,9 @@ public:
HIDCollection &collection,
ProtocolHandler *&handlerList);
virtual status_t Open(uint32 flags, uint32 *cookie);
virtual status_t Close(uint32 *cookie);
virtual status_t Read(uint32 *cookie, off_t position,
void *buffer, size_t *numBytes);
virtual status_t Write(uint32 *cookie, off_t position,
@ -34,15 +38,19 @@ public:
void *buffer, size_t length);
private:
status_t _ReadReport();
static int32 _UpdateThread(void *data);
status_t _Update();
private:
HIDReport & fReport;
HIDReportItem * fAxis[MAX_AXES];
HIDReportItem * fButtons[MAX_BUTTONS];
joystick_module_info fJoystickModuleInfo;
extended_joystick fCurrentValues;
mutex fUpdateLock;
thread_id fUpdateThread;
};
#endif // USB_JOYSTICK_PROTOCOL_HANDLER_H