* Add a JoystickProtocolHandler to usb_hid that supports the BJoystick protocol.

This includes input scaling, so the resulting input ranges should always match
  the ones of the BJoystick data (hence no calibration should be required).
* It supports joysticks, gamepads and multi-axis controllers. I've only tested
  it with a Microsoft SideWinder Gamepad Pro so far, which now works as expected
  with stickit.
* Fixes #7429.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@41851 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Michael Lotz
2011-05-31 02:21:05 +00:00
parent 10a1a81d02
commit f46bc7f19e
4 changed files with 307 additions and 3 deletions
@@ -3,7 +3,7 @@ SubDir HAIKU_TOP src add-ons kernel drivers input usb_hid ;
SubDirC++Flags -fno-rtti ;
SubDirSysHdrs $(HAIKU_TOP) headers os drivers ;
UsePrivateHeaders [ FDirName kernel util ] input drivers ;
UsePrivateHeaders [ FDirName kernel util ] input drivers device ;
UsePrivateKernelHeaders ;
KernelAddon usb_hid :
@@ -17,6 +17,8 @@ KernelAddon usb_hid :
HIDReportItem.cpp
ProtocolHandler.cpp
JoystickProtocolHandler.cpp
KeyboardProtocolHandler.cpp
MouseProtocolHandler.cpp
;
@@ -0,0 +1,254 @@
/*
* Copyright 2011 Michael Lotz <[email protected]>
* Distributed under the terms of the MIT license.
*/
//! Driver for USB Human Interface Devices.
#include "Driver.h"
#include "JoystickProtocolHandler.h"
#include "HIDCollection.h"
#include "HIDDevice.h"
#include "HIDReport.h"
#include "HIDReportItem.h"
#include <new>
#include <string.h>
#include <usb/USB_hid.h>
JoystickProtocolHandler::JoystickProtocolHandler(HIDReport &report)
:
ProtocolHandler(report.Device(), "joystick/usb/", 512),
fReport(report)
{
for (uint32 i = 0; i < MAX_AXES; i++)
fAxis[i] = NULL;
uint32 buttonCount = 0;
for (uint32 i = 0; i < report.CountItems(); i++) {
HIDReportItem *item = report.ItemAt(i);
if (!item->HasData())
continue;
switch (item->UsagePage()) {
case B_HID_USAGE_PAGE_BUTTON:
{
if (item->UsageID() - 1 < MAX_BUTTONS)
fButtons[buttonCount++] = item;
break;
}
case B_HID_USAGE_PAGE_GENERIC_DESKTOP:
{
switch (item->UsageID()) {
case B_HID_UID_GD_X:
case B_HID_UID_GD_Y:
case B_HID_UID_GD_Z:
case B_HID_UID_GD_RX:
case B_HID_UID_GD_RY:
case B_HID_UID_GD_RZ:
uint16 axis = item->UsageID() - B_HID_UID_GD_X;
if (axis >= MAX_AXES)
break;
fAxis[axis] = item;
break;
}
break;
}
}
}
fButtons[buttonCount] = NULL;
TRACE("joystick device with %lu buttons\n", buttonCount);
TRACE("report id: %u\n", report.ID());
}
void
JoystickProtocolHandler::AddHandlers(HIDDevice &device,
HIDCollection &collection, ProtocolHandler *&handlerList)
{
if (collection.UsagePage() != B_HID_USAGE_PAGE_GENERIC_DESKTOP
|| (collection.UsageID() != B_HID_UID_GD_JOYSTICK
&& collection.UsageID() != B_HID_UID_GD_GAMEPAD
&& collection.UsageID() != B_HID_UID_GD_MULTIAXIS)) {
TRACE("collection not a joystick or gamepad\n");
return;
}
HIDParser &parser = device.Parser();
uint32 maxReportCount = parser.CountReports(HID_REPORT_TYPE_INPUT);
if (maxReportCount == 0)
return;
uint32 inputReportCount = 0;
HIDReport *inputReports[maxReportCount];
collection.BuildReportList(HID_REPORT_TYPE_INPUT, inputReports,
inputReportCount);
for (uint32 i = 0; i < inputReportCount; i++) {
HIDReport *inputReport = inputReports[i];
// try to find at least one axis
bool foundAxis = false;
for (uint32 j = 0; j < inputReport->CountItems(); j++) {
HIDReportItem *item = inputReport->ItemAt(j);
if (item == NULL || !item->HasData())
continue;
if (item->UsagePage() != B_HID_USAGE_PAGE_GENERIC_DESKTOP)
continue;
if (item->UsageID() >= B_HID_UID_GD_X
&& item->UsageID() <= B_HID_UID_GD_RZ) {
foundAxis = true;
break;
}
}
if (!foundAxis)
continue;
ProtocolHandler *newHandler
= new(std::nothrow) JoystickProtocolHandler(*inputReport);
if (newHandler == NULL) {
TRACE("failed to allocated joystick protocol handler\n");
continue;
}
newHandler->SetNextHandler(handlerList);
handlerList = newHandler;
}
}
status_t
JoystickProtocolHandler::Read(uint32 *cookie, off_t position, void *buffer,
size_t *numBytes)
{
if (*numBytes < sizeof(extended_joystick))
return B_BUFFER_OVERFLOW;
while (RingBufferReadable() == 0) {
status_t result = _ReadReport();
if (result != B_OK)
return result;
}
status_t result = RingBufferRead(buffer, sizeof(extended_joystick));
if (result != B_OK)
return result;
*numBytes = sizeof(extended_joystick);
return B_OK;
}
status_t
JoystickProtocolHandler::Write(uint32 *cookie, off_t position,
const void *buffer, size_t *numBytes)
{
*numBytes = 0;
return B_NOT_SUPPORTED;
}
status_t
JoystickProtocolHandler::Control(uint32 *cookie, uint32 op, void *buffer,
size_t length)
{
switch (op) {
case B_JOYSTICK_SET_DEVICE_MODULE:
{
if (length < sizeof(joystick_module_info))
return B_BAD_VALUE;
fJoystickModuleInfo = *(joystick_module_info *)buffer;
fJoystickModuleInfo.num_axes = 0;
for (uint32 i = 0; i < MAX_AXES; i++) {
if (fAxis[i] != NULL)
fJoystickModuleInfo.num_axes = i + 1;
}
fJoystickModuleInfo.num_buttons = 0;
for (uint32 i = 0; i < MAX_BUTTONS; i++) {
if (fButtons[i] == NULL)
break;
uint8 button = fButtons[i]->UsageID();
if (button > fJoystickModuleInfo.num_buttons)
fJoystickModuleInfo.num_buttons = button;
}
fJoystickModuleInfo.num_hats = 0;
fJoystickModuleInfo.num_sticks = 1;
fJoystickModuleInfo.config_size = 0;
break;
}
case B_JOYSTICK_GET_DEVICE_MODULE:
if (length < sizeof(joystick_module_info))
return B_BAD_VALUE;
*(joystick_module_info *)buffer = fJoystickModuleInfo;
break;
}
return B_ERROR;
}
status_t
JoystickProtocolHandler::_ReadReport()
{
status_t result = fReport.WaitForReport(B_INFINITE_TIMEOUT);
if (result != B_OK) {
if (fReport.Device()->IsRemoved()) {
TRACE("device has been removed\n");
return B_DEV_NOT_READY;
}
if (result != B_INTERRUPTED) {
// interrupts happen when other reports come in on the same
// input as ours
TRACE_ALWAYS("error waiting for report: %s\n", strerror(result));
}
// signal that we simply want to try again
return B_OK;
}
extended_joystick info;
memset(&info, 0, sizeof(info));
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);
}
for (uint32 i = 0; i < MAX_BUTTONS; i++) {
HIDReportItem *button = fButtons[i];
if (button == NULL)
break;
if (button->Extract() == B_OK && button->Valid())
info.buttons |= (button->Data() & 1) << (button->UsageID() - 1);
}
fReport.DoneProcessing();
TRACE("got joystick report\n");
info.timestamp = system_time();
return RingBufferWrite(&info, sizeof(info));
}
@@ -0,0 +1,48 @@
/*
* Copyright 2008-2011 Michael Lotz <[email protected]>
* Distributed under the terms of the MIT license.
*/
#ifndef USB_JOYSTICK_PROTOCOL_HANDLER_H
#define USB_JOYSTICK_PROTOCOL_HANDLER_H
#include <InterfaceDefs.h>
#include "ProtocolHandler.h"
#include <joystick_driver.h>
class HIDCollection;
class HIDReportItem;
class JoystickProtocolHandler : public ProtocolHandler {
public:
JoystickProtocolHandler(HIDReport &report);
static void AddHandlers(HIDDevice &device,
HIDCollection &collection,
ProtocolHandler *&handlerList);
virtual status_t Read(uint32 *cookie, off_t position,
void *buffer, size_t *numBytes);
virtual status_t Write(uint32 *cookie, off_t position,
const void *buffer, size_t *numBytes);
virtual status_t Control(uint32 *cookie, uint32 op,
void *buffer, size_t length);
private:
status_t _ReadReport();
private:
HIDReport & fReport;
HIDReportItem * fAxis[MAX_AXES];
HIDReportItem * fButtons[MAX_BUTTONS];
joystick_module_info fJoystickModuleInfo;
};
#endif // USB_JOYSTICK_PROTOCOL_HANDLER_H
@@ -16,7 +16,7 @@
// includes for the different protocol handlers
#include "KeyboardProtocolHandler.h"
#include "MouseProtocolHandler.h"
//#include "JoystickProtocolHandler.h"
#include "JoystickProtocolHandler.h"
ProtocolHandler::ProtocolHandler(HIDDevice *device, const char *basePath,
@@ -88,7 +88,7 @@ ProtocolHandler::AddHandlers(HIDDevice &device, ProtocolHandler *&handlerList,
KeyboardProtocolHandler::AddHandlers(device, *collection, handlerList);
MouseProtocolHandler::AddHandlers(device, *collection, handlerList);
//JoystickProtocolHandler::AddHandlers(device, *collection, handlerList);
JoystickProtocolHandler::AddHandlers(device, *collection, handlerList);
}
ProtocolHandler *handler = handlerList;