From f46bc7f19efb1f6c816a10f2441eafdca37bfc71 Mon Sep 17 00:00:00 2001 From: Michael Lotz Date: Tue, 31 May 2011 02:21:05 +0000 Subject: [PATCH] * 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 --- .../kernel/drivers/input/usb_hid/Jamfile | 4 +- .../input/usb_hid/JoystickProtocolHandler.cpp | 254 ++++++++++++++++++ .../input/usb_hid/JoystickProtocolHandler.h | 48 ++++ .../drivers/input/usb_hid/ProtocolHandler.cpp | 4 +- 4 files changed, 307 insertions(+), 3 deletions(-) create mode 100644 src/add-ons/kernel/drivers/input/usb_hid/JoystickProtocolHandler.cpp create mode 100644 src/add-ons/kernel/drivers/input/usb_hid/JoystickProtocolHandler.h diff --git a/src/add-ons/kernel/drivers/input/usb_hid/Jamfile b/src/add-ons/kernel/drivers/input/usb_hid/Jamfile index 9a42a821c5..bdccce7c81 100644 --- a/src/add-ons/kernel/drivers/input/usb_hid/Jamfile +++ b/src/add-ons/kernel/drivers/input/usb_hid/Jamfile @@ -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 ; diff --git a/src/add-ons/kernel/drivers/input/usb_hid/JoystickProtocolHandler.cpp b/src/add-ons/kernel/drivers/input/usb_hid/JoystickProtocolHandler.cpp new file mode 100644 index 0000000000..4917f1f1ce --- /dev/null +++ b/src/add-ons/kernel/drivers/input/usb_hid/JoystickProtocolHandler.cpp @@ -0,0 +1,254 @@ +/* + * Copyright 2011 Michael Lotz + * 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 +#include +#include + + +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)); +} diff --git a/src/add-ons/kernel/drivers/input/usb_hid/JoystickProtocolHandler.h b/src/add-ons/kernel/drivers/input/usb_hid/JoystickProtocolHandler.h new file mode 100644 index 0000000000..470f221f2e --- /dev/null +++ b/src/add-ons/kernel/drivers/input/usb_hid/JoystickProtocolHandler.h @@ -0,0 +1,48 @@ +/* + * Copyright 2008-2011 Michael Lotz + * Distributed under the terms of the MIT license. + */ +#ifndef USB_JOYSTICK_PROTOCOL_HANDLER_H +#define USB_JOYSTICK_PROTOCOL_HANDLER_H + + +#include + +#include "ProtocolHandler.h" + +#include + + +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 diff --git a/src/add-ons/kernel/drivers/input/usb_hid/ProtocolHandler.cpp b/src/add-ons/kernel/drivers/input/usb_hid/ProtocolHandler.cpp index 18ba6c1c6b..f1c2f55d19 100644 --- a/src/add-ons/kernel/drivers/input/usb_hid/ProtocolHandler.cpp +++ b/src/add-ons/kernel/drivers/input/usb_hid/ProtocolHandler.cpp @@ -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;