usb_gamepad_input: add minimal USB gamepad driver

This minimal USB gamepad driver uses the Usb session to access the USB
device and provides Genode's Input service to its client. There is no
support for any fancy features like rumble support or, if available,
battery state checking. Furthermore there is currently no way to
calibrate the analog input sources, which leads to unexpected motion
events due to input jitter.

For a list of supported devices and more information please look at the
README.

Fixes #58.
This commit is contained in:
Josef Söntgen
2016-12-29 12:59:15 +01:00
committed by Norman Feske
parent 08b0e5df02
commit c96a5d2016
13 changed files with 2579 additions and 0 deletions

View File

@@ -0,0 +1,187 @@
This directory contains an minimal USB gamepad driver. It uses the Usb session
interface to access the USB device and provides Genode's Input service to its
client. There is no support for any fancy features like rumble support or, if
available, battery state checking. Furthermore there is currently no way to
calibrate the analog input sources, which leads to unexpected motion events
due to input jitter.
Usage
-----
Please take a look at the run script _repos/world/run/usb_gamepad_input.run_.
Support gamepads and mappings
-----------------------------
* iBuffalo SNES replica (0583:2060) fully supported:
dpad top: BTN_FORWARD
dpad bottom: BTN_BACK
dpad left: BTN_LEFT
dpad right: BTN_RIGHT
start: BTN_START
select: BTN_SELECT
A: BTN_A
B: BTN_B
X: BTN_X
Y: BTN_Y
L: BTN_TL
R: BTN_RL
* Logitech Precision Gamepad (046d:c21a) fully supported:
dpad top: BTN_FORWARD
dpad bottom: BTN_BACK
dpad left: BTN_LEFT
dpad right: BTN_RIGHT
start: BTN_START
select: BTN_SELECT
1: BTN_A
2: BTN_B
3: BTN_X
4: BTN_Y
L1: BTN_TL
R1: BTN_RL
L2: BTN_TL2
R2: BTN_RL2
* Microsoft XBox 360 (045e:028e) / One (045e:02d1) partially supported:
LED support and battery information checking is missing as well as
is rumble support.
analog X axis: MOTION 0 ax
analog Y axis: MOTION 0 ay
analog Z axis: MOTION 1 ax
analog Rz axis: MOTION 1 ay
analog L2 axis: MOTION 2 ax
analog R2 axis: MOTION 3 ax
dpad up: BTN_FORWARD
dpad right: BTN_RIGHT
dpad down: BTN_BACK
dpad left: BTN_LEFT
start: BTN_START
select: BTN_SELECT
guide: BTN_MODE
A: BTN_A
B: BTN_B
X: BTN_X
Y: BTN_Y
L1: BTN_TL
R1: BTN_RL
L3: BTN_THUMBL
R3: BTN_THUMBR
* Retrolink N64 replica (0079:0006) fully supported:
The dpad is actually a 8-way hat that is mapped to 4-way dpad.
analog X axis: MOTION ax
analog Y axis: MOTION ay
dpad up: BTN_FORWARD
dpad right: BTN_RIGHT
dpad down: BTN_BACK
dpad left: BTN_LEFT
start: BTN_START
select: BTN_SELECT
A: BTN_A
B: BTN_B
CUP: BTN_0are
CRIGHT: BTN_1
CDOWN: BTN_2
CLEFT: BTN_3
L: BTN_TL
R: BTN_RL
Z: BTN_Z
* Sony DualShock3 Sixaxis (054c:0268) partially supported:
Analog support for all buttons as well as sixaxis support and battery
information checking and rumble support is missing. The PS button is
not usable for now.
analog X axis: MOTION 0 ax
analog Y axis: MOTION 0 ay
analog Z axis: MOTION 1 ax
analog Rz axis: MOTION 1 ay
analog L2 axis: MOTION 2 ax
analog R2 axis: MOTION 3 ax
R2: BTN_RL2
dpad up: BTN_FORWARD
dpad right: BTN_RIGHT
dpad down: BTN_BACK
dpad left: BTN_LEFT
start: BTN_START
select: BTN_SELECT
X: BTN_A
O: BTN_B
SQUARE: BTN_X
CIRCLE: BTN_Y
L1: BTN_TL
R1: BTN_RL
L2: BTN_TL2
R2: BTN_RL2
L3: BTN_THUMB
R3: BTN_THUMB2
* Sony DualShock4 Sixaxis (054c:05c4) partially supported:
Gyro/touchpad support, battery information checking and rumble support
as well as LED support is missing. The dpad is actually a 8-way hat that
is mapped to 4-way dpad.
analog X axis: MOTION 0 ax
analog Y axis: MOTION 0 ay
analog Z axis: MOTION 1 ax
analog Rz axis: MOTION 1 ay
analog L2 axis: MOTION 2 ax
analog R2 axis: MOTION 3 ax
R2: BTN_RL2
dpad up: BTN_FORWARD
dpad right: BTN_RIGHT
dpad down: BTN_BACK
dpad left: BTN_LEFT
start: BTN_START
select: BTN_SELECT
X: BTN_A
O: BTN_B
SQUARE: BTN_X
CIRCLE: BTN_Y
L1: BTN_TL
R1: BTN_RL
L2: BTN_TL2
R2: BTN_RL2
L3: BTN_THUMB
R3: BTN_THUMB2
Adding support for additional devices
-------------------------------------
There is a variety of 3rd party devices that are compatible to the
Microsoft and Sony gamepads. In their case it should be enough to
add their vendor and product id to the proper driver for them to be
recognized.
Adding support for other completely different devices is mostly just
a matter of picking a fitting existing driver as blueprint and implementing
the 'parse' method. If the device needs a quirk to work, like the DS3,
it may be hacked in the like it was done for the DS4 and XBone controller.
Todo
----
* add proper configuration handling, e.g. enable_left_analog_stick='yes'
and calibration knobs
* generate device Report, i.e., how many buttons, axis and so on
* rework quirk mechanism and thereby turn the drivers inside out and make
use of a proper state-machine
* support for fancy features

View File

@@ -0,0 +1,177 @@
/*
* \brief USB HID to Input translator
* \author Josef Soentgen
* \date 2016-10-12
*/
/*
* Copyright (C) 2016 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _BUFFALO_SNES_H_
#define _BUFFALO_SNES_H_
/* Genode includes */
#include <util/string.h>
/* local includes */
#include <hid_device.h>
struct Buffalo_snes : Hid_device
{
/*
* Supported devices
*/
struct {
uint16_t vendor_id;
uint16_t product_id;
} devices[1] = {
{0x0583, 0x2060}
};
enum {
IFACE_NUM = 0,
ALT_NUM = 0,
EP_NUM = 0,
DATA_LENGTH = 8,
X = 0,
Y = 1,
B = 2,
ORIGIN = 0x80,
LEFT_PRESSED = 0x00,
RIGHT_PRESSED = 0xff,
UP_PRESSED = 0x00,
DOWN_PRESSED = 0xff,
};
Input::Keycode button_mapping[8] = {
Input::Keycode::BTN_A, /* 0x01 */
Input::Keycode::BTN_B, /* 0x02 */
Input::Keycode::BTN_X, /* 0x03 */
Input::Keycode::BTN_Y, /* 0x04 */
Input::Keycode::BTN_TL, /* 0x05 */
Input::Keycode::BTN_TR, /* 0x06 */
Input::Keycode::BTN_SELECT, /* 0x07 */
Input::Keycode::BTN_START, /* 0x08 */
/* clear / turbo buttons omitted */
};
uint8_t last[DATA_LENGTH] = {};
/* XXX instead checking for false positives check absolute values */
bool false_positive(uint8_t o, uint8_t n)
{
return (o == 0x7f && n == 0x80) || (o == 0x80 && n == 0x7f);
}
Buffalo_snes(Input::Session_component &input_session)
: Hid_device(input_session, "iBuffalo classic USB gamepad (SNES)")
{
/* initial values */
last[X] = ORIGIN;
last[Y] = ORIGIN;
}
/**************************
** HID device interface **
**************************/
bool probe(uint16_t vendor_id, uint16_t product_id) const override
{
for (size_t i = 0; i < sizeof(devices)/sizeof(devices[0]); i++) {
if ( vendor_id == devices[i].vendor_id
&& product_id == devices[i].product_id) {
return true;
}
}
return false;
}
void parse(uint8_t const *new_data, size_t len) override
{
using namespace Genode;
if (len != DATA_LENGTH) {
error("new data invalid");
throw -1;
}
bool const changed = memcmp(last, new_data, len) != 0;
if (!changed) { return; }
/* x-axis */
if (last[X] != new_data[X]) {
if (false_positive(last[X], new_data[X])) { return; }
/* now pressed if last was origin */
bool const press = last[X] == ORIGIN;
bool left = true;
if (press && !(new_data[X] < last[X])) {
left = false;
}
if (last[X] == RIGHT_PRESSED) { left = false; }
Input::Event ev(press ? Input::Event::PRESS : Input::Event::RELEASE,
left ? Input::Keycode::BTN_LEFT : Input::Keycode::BTN_RIGHT,
0, 0, 0, 0);
input_session.submit(ev);
}
/* y-axis*/
if (last[Y] != new_data[Y]) {
if (false_positive(last[Y], new_data[Y])) { return; }
/* now pressed if last was origin */
bool const press = last[Y] == ORIGIN;
bool up = true;
if (press && !(new_data[Y] < last[Y])) {
up = false;
}
if (last[Y] == DOWN_PRESSED) { up = false; }
Input::Event ev(press ? Input::Event::PRESS : Input::Event::RELEASE,
up ? Input::Keycode::BTN_FORWARD : Input::Keycode::BTN_BACK,
0, 0, 0, 0);
input_session.submit(ev);
}
if (last[B] != new_data[B]) {
uint8_t const prev = last[B];
uint8_t const curr = new_data[B];
for (int i = 0; i < 8; i++) {
uint8_t const idx = 1u << i;
if ((prev & idx) == (curr & idx)) { continue; }
bool const press = !(prev & idx) && (curr & idx);
Input::Event ev(press ? Input::Event::PRESS : Input::Event::RELEASE,
button_mapping[i], 0, 0, 0, 0);
input_session.submit(ev);
}
}
/* save for next poll */
Genode::memcpy(last, new_data, len);
}
uint8_t iface() const { return IFACE_NUM; }
uint8_t ep() const { return EP_NUM; }
uint8_t alt() const { return ALT_NUM; }
};
#endif /* _BUFFALO_SNES_H_ */

View File

@@ -0,0 +1,92 @@
/*
* \brief USB HID to Input translator
* \author Josef Soentgen
* \date 2016-10-12
*/
/*
* Copyright (C) 2016 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _HID_DEVICE_H_
#define _HID_DEVICE_H_
/* Genode includes */
#include <base/fixed_stdint.h>
#include <util/string.h>
struct Hid_device
{
typedef Genode::uint8_t uint8_t;
typedef Genode::uint16_t uint16_t;
typedef Genode::size_t size_t;
typedef signed short int16_t;
enum { MAX_DATA = 256, };
uint8_t data[MAX_DATA] = {};
typedef Genode::String<64> Name;
Name name { "<generic USB HID gamepad>" };
Input::Session_component &input_session;
Hid_device(Input::Session_component &input_session, Name const &name)
: name(name), input_session(input_session) { }
Hid_device(Input::Session_component &input_session)
: input_session(input_session) { }
virtual ~Hid_device() { }
/**************************
** HID device interface **
**************************/
virtual bool probe(uint16_t vendor_id, uint16_t product_id) const
{
Genode::warning(__func__, "(): not implemented");
return false;
}
virtual void parse(uint8_t const *new_data, size_t len)
{
using namespace Genode;
if (MAX_DATA < len) {
warning("limit data len: ", len, " to: ", (int)MAX_DATA);
len = MAX_DATA;
}
log("generic USB HID dump data:");
for (size_t i = 0; i < len; i++) {
log(Hex(i), ": ", Hex(new_data[i]), " (", Hex(data[i]), ")");
}
/* save for next poll */
Genode::memcpy(data, new_data, len);
}
virtual uint8_t iface() const
{
Genode::warning(__func__, "(): not implemented, returning 0");
return 0;
}
virtual uint8_t ep() const
{
Genode::warning(__func__, "(): not implemented, returning 0");
return 0;
}
virtual uint8_t alt() const
{
Genode::warning(__func__, "(): not implemented, returning 0");
return 0;
}
};
#endif /* _HID_DEVICE_H_ */

View File

@@ -0,0 +1,175 @@
/*
* \brief USB HID to Input translator
* \author Josef Soentgen
* \date 2016-10-12
*/
/*
* Copyright (C) 2016 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _LOGITECH_PRECISION_H_
#define _LOGITECH_PRECISION_H_
/* Genode includes */
#include <util/string.h>
/* local includes */
#include <hid_device.h>
struct Logitech_precision : Hid_device
{
/*
* Supported devices
*/
struct {
uint16_t vendor_id;
uint16_t product_id;
} devices[1] = {
{0x046d, 0xc21a}
};
struct Report
{
uint8_t x; /* x-axis [1,255] */
uint8_t y; /* y-axis [1,255] */
uint16_t b; /* 8 buttons */
}; __attribute__((packed));
enum {
IFACE_NUM = 0,
ALT_NUM = 0,
EP_NUM = 0,
DATA_LENGTH = 4,
X = 0,
Y = 1,
ORIGIN = 0x80,
BUTTONS = 10,
RIGHT_PRESSED = 0xff,
DOWN_PRESSED = 0xff,
};
Input::Keycode button_mapping[BUTTONS] = {
Input::Keycode::BTN_X, /* 0x01 */
Input::Keycode::BTN_A, /* 0x02 */
Input::Keycode::BTN_B, /* 0x04 */
Input::Keycode::BTN_Y, /* 0x08 */
Input::Keycode::BTN_TL, /* 0x10 */
Input::Keycode::BTN_TR, /* 0x20 */
Input::Keycode::BTN_TL2, /* 0x40 */
Input::Keycode::BTN_TR2, /* 0x80 */
Input::Keycode::BTN_SELECT, /* 0x100 */
Input::Keycode::BTN_START, /* 0x200 */
};
uint8_t last[DATA_LENGTH] = {};
Logitech_precision(Input::Session_component &input_session)
: Hid_device(input_session, "Logitech, Inc. Precision Gamepad")
{
/* initial values */
last[X] = ORIGIN;
last[Y] = ORIGIN;
}
/**************************
** HID device interface **
**************************/
bool probe(uint16_t vendor_id, uint16_t product_id) const override
{
for (size_t i = 0; i < sizeof(devices)/sizeof(devices[0]); i++) {
if ( vendor_id == devices[i].vendor_id
&& product_id == devices[i].product_id) {
return true;
}
}
return false;
}
void parse(uint8_t const *data, size_t len) override
{
using namespace Genode;
if (len != DATA_LENGTH) {
error("new data invalid");
throw -1;
}
bool const changed = memcpy(last, data, len);
if (!changed) { return; }
Report const * const o = reinterpret_cast<Report const*>(last);
Report const * const n = reinterpret_cast<Report const*>(data);
/* x-axis */
uint8_t const ox = last[X];
uint8_t const nx = data[X];
bool const x = (ox != nx);
if (x) {
/* now pressed if last was origin */
bool const press = (ox == ORIGIN);
bool left = true;
if (press && !(nx < ox)) {
left = false;
}
if (ox == RIGHT_PRESSED) { left = false; }
Input::Event ev(press ? Input::Event::PRESS : Input::Event::RELEASE,
left ? Input::Keycode::BTN_LEFT : Input::Keycode::BTN_RIGHT,
0, 0, 0, 0);
input_session.submit(ev);
}
/* y-axis*/
uint8_t const oy = last[Y];
uint8_t const ny = data[Y];
bool const y = (oy != ny);
if (y) {
/* now pressed if last was origin */
bool const press = oy == ORIGIN;
bool up = true;
if (press && !(ny < oy)) {
up = false;
}
if (oy == DOWN_PRESSED) { up = false; }
Input::Event ev(press ? Input::Event::PRESS : Input::Event::RELEASE,
up ? Input::Keycode::BTN_FORWARD : Input::Keycode::BTN_BACK,
0, 0, 0, 0);
input_session.submit(ev);
}
/* check buttons */
uint16_t const ob = o->b;
uint16_t const nb = n->b;
bool const b = (ob != nb);
if (b) {
Utils::check_buttons(input_session, ob, nb, BUTTONS, button_mapping);
}
/* save for next poll */
Genode::memcpy(last, data, len);
}
uint8_t iface() const { return IFACE_NUM; }
uint8_t ep() const { return EP_NUM; }
uint8_t alt() const { return ALT_NUM; }
};
#endif /* _LOGITECH_PRECISION_H_ */

View File

@@ -0,0 +1,557 @@
/*
* \brief USB HID gamepad to Input session translator
* \author Josef Soentgen
* \date 2016-10-12
*/
/*
* Copyright (C) 2016 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
/* Genode includes */
#include <base/allocator_avl.h>
#include <base/attached_rom_dataspace.h>
#include <base/component.h>
#include <base/log.h>
#include <base/heap.h>
#include <input/component.h>
#include <input/keycodes.h>
#include <input_session/connection.h>
#include <os/reporter.h>
#include <os/static_root.h>
#include <timer_session/connection.h>
#include <usb/types.h>
#include <usb_session/connection.h>
/* local includes */
#include <utils.h>
#include <hid_device.h>
/* include known gamepads */
#include <buffalo_snes.h>
#include <logitech_precision.h>
#include <microsoft_xbox360.h>
#include <microsoft_xboxone.h>
#include <retrolink_n64.h>
#include <sony_ds3.h>
#include <sony_ds4.h>
static bool const verbose_intr = false;
static bool const verbose = false;
static bool const debug = false;
static bool const dump_dt = false;
namespace Usb {
using namespace Genode;
struct Hid;
struct Main;
}
/**********************************
** USB HID Input implementation **
**********************************/
/**
* USB HID
*/
struct Usb::Hid
{
Env &env;
Input::Session_component &input_session;
/*
* Supported USB HID gamepads
*/
Hid_device generic { input_session };
Buffalo_snes buffalo_snes { input_session };
Logitech_precision logitech_precision { input_session };
Microsoft_xbox360 xbox360 { input_session };
Microsoft_xboxone xboxone { input_session };
Retrolink_n64 retrolink_n64 { input_session };
Sony_ds3 sony_ds3 { input_session };
Sony_ds4 sony_ds4 { input_session };
enum { MAX_DEVICES = 8, };
Hid_device *devices[MAX_DEVICES] {
&generic,
&buffalo_snes,
&logitech_precision,
&retrolink_n64,
&xbox360,
&xboxone,
&sony_ds3,
&sony_ds4,
};
Hid_device *device = &generic;
Timer::Connection timer { env };
unsigned polling_us = 0;
void state_change()
{
if (usb.plugged()) {
log("Gamepad plugged in");
probe_device();
return;
}
log("Gamepad unplugged");
}
/* construct before Usb::Connection so the dispatcher is valid */
Signal_handler<Hid> state_dispatcher { env.ep(), *this, &Hid::state_change };
Allocator_avl usb_alloc;
Usb::Connection usb { env, &usb_alloc, "usb_gamepad", 32*1024, state_dispatcher };
Usb::Config_descriptor config_descr;
Usb::Device_descriptor device_descr;
Usb::Interface_descriptor iface_descr;
Usb::Endpoint_descriptor ep_descr;
void handle_alt_setting(Packet_descriptor &p) { warning(__func__, ": not implemented"); }
void handle_config_packet(Packet_descriptor &p) { _claim_device(); }
struct Hid_report
{
struct Invalid_report : Genode::Exception { };
};
Hid_report hid_report;
Hid_report parse_hid_report(uint8_t const *r, size_t len)
{
if (dump_dt) {
log("HID report dump:");
/* using i+=4 is save since we use a 256 byte buffer */
for (size_t i = 0; i < len; i+=4) {
log(Hex(r[i], Hex::PREFIX, Hex::PAD),
" ", Hex(r[i+1], Hex::PREFIX, Hex::PAD),
" ", Hex(r[i+2], Hex::PREFIX, Hex::PAD),
" ", Hex(r[i+3], Hex::PREFIX, Hex::PAD));
}
}
return Hid_report();
}
struct Hid_report_descriptor
{
Usb::Hid &hid;
Hid_report_descriptor(Usb::Hid &hid) : hid(hid) { }
void request()
{
enum {
USB_REQUEST_TO_HOST = 0x80,
USB_REQUEST_RCPT_IFACE = 0x01,
USB_REQUEST_GET_DESCRIPTOR = 0x06,
USB_REQUEST_DT = 0x22,
REQUEST = USB_REQUEST_TO_HOST | USB_REQUEST_RCPT_IFACE,
};
/* XXX read size from interface */
Usb::Packet_descriptor p = hid.alloc_packet(256);
p.type = Usb::Packet_descriptor::CTRL;
p.control.request = USB_REQUEST_GET_DESCRIPTOR;
p.control.request_type = REQUEST;
p.control.value = USB_REQUEST_DT<<8;
p.control.index = 0;
p.control.timeout = 1000;
/* alloc_packet checks readiness */
hid.usb.source()->submit_packet(p);
}
};
Hid_report_descriptor hid_report_descr { *this };
void handle_ctrl(Packet_descriptor &p)
{
uint8_t const * const data = (uint8_t*)usb.source()->packet_content(p);
size_t const len = p.control.actual_size > 0
? p.control.actual_size : 0;
try {
/* XXX only parse HID report if not already known */
hid_report = parse_hid_report(data, len);
} catch (Hid_report::Invalid_report) {
error("HID report is invalid");
return;
}
/* kick-off polling */
timer.trigger_once(polling_us);
}
void handle_irq_packet(Packet_descriptor &p)
{
if (!p.read_transfer()) { return; }
uint8_t const * const data = (uint8_t*)usb.source()->packet_content(p);
size_t const len = p.transfer.actual_size > 0
? p.transfer.actual_size : 0;
try {
device->parse(data, len);
}
catch (...) {
error("input data is invalid, reconnect device");
return;
}
/* keep on r^Wpolling */
if (polling_us) { timer.trigger_once(polling_us); }
}
struct String_descr
{
Usb::Hid &hid;
char const * const name;
enum { MAX_STRING_LENGTH = 128, };
char string[MAX_STRING_LENGTH] {};
uint8_t index = 0xff; /* hopefully invalid */
String_descr(Usb::Hid &hid, char const *name) : hid(hid), name(name) { }
void request(uint8_t i)
{
index = i;
Usb::Packet_descriptor p = hid.alloc_packet(MAX_STRING_LENGTH);
p.type = Usb::Packet_descriptor::STRING;
p.string.index = index;
p.string.length = MAX_STRING_LENGTH;
hid.usb.source()->submit_packet(p);
}
};
void handle_string_packet(Usb::Packet_descriptor &p)
{
String_descr *s = nullptr;
if (p.string.index == manufactorer_string.index) {
s = &manufactorer_string;
} else if (p.string.index == product_string.index) {
s = &product_string;
} else if (p.string.index == serial_number_string.index) {
s = &serial_number_string;
}
if (!s) { return; }
uint16_t const * const u = (uint16_t*)usb.source()->packet_content(p);
if (p.string.length < 0) { p.string.length = 0; }
int const len = min((unsigned)p.string.length,
(unsigned)String_descr::MAX_STRING_LENGTH - 1);
for (int i = 0; i < len; i++) { s->string[i] = u[i] & 0xff; }
s->string[len] = 0;
log(s->name, ": ", (char const*)s->string);
}
String_descr manufactorer_string { *this, "Manufactorer" };
String_descr product_string { *this, "Product" };
String_descr serial_number_string { *this, "Serial_number" };
struct Completion : Usb::Completion
{
enum State { VALID, FREE, CANCELED };
State state = FREE;
void complete(Usb::Packet_descriptor &p) override { }
void complete(Usb::Hid &hid, Usb::Packet_descriptor &p)
{
if (state != VALID)
return;
if (!p.succeded) {
/*
* We might end up here b/c the generic driver was used and a vendor
* did not fill the queried string index. It is, however, more likely
* that a IRQ or CTRL packet failed. For better or worse that is quite
* often the case when a gamepad is unplugged. Therefore we never print
* any error in case of a failure. If something goes wrong, one has to
* debug anyway...
*/
return;
}
switch (p.type) {
case Usb::Packet_descriptor::IRQ: hid.handle_irq_packet(p); break;
case Usb::Packet_descriptor::CTRL: hid.handle_ctrl(p); break;
case Usb::Packet_descriptor::STRING: hid.handle_string_packet(p); break;
case Usb::Packet_descriptor::CONFIG: hid.handle_config_packet(p); break;
case Usb::Packet_descriptor::ALT_SETTING: hid.handle_alt_setting(p); break;
/* ignore other packets */
case Usb::Packet_descriptor::BULK:
default: break;
}
}
} completions[Usb::Session::TX_QUEUE_SIZE];
void ack_avail()
{
while (usb.source()->ack_avail()) {
Usb::Packet_descriptor p = usb.source()->get_acked_packet();
dynamic_cast<Completion *>(p.completion)->complete(*this, p);
free_packet(p);
}
}
Signal_handler<Hid> ack_avail_dispatcher { env.ep(), *this, &Hid::ack_avail };
void probe_device()
{
try {
usb.config_descriptor(&device_descr, &config_descr);
} catch (Usb::Session::Device_not_found) {
error("cound not read config descriptor");
throw -1;
}
for (int i = 1; i < MAX_DEVICES; i++) {
bool const found = devices[i]->probe(device_descr.vendor_id,
device_descr.product_id);
if (found) {
device = devices[i];
log("Driver found for device: ", device->name);
break;
}
}
if (device == &generic) {
warning("no matching driver found, falling back to generic driver");
}
Usb::Packet_descriptor p = alloc_packet(0);
p.type = Packet_descriptor::CONFIG;
p.number = 1; /* XXX read from device */
usb.source()->submit_packet(p);
}
bool _claim_device()
{
try {
usb.config_descriptor(&device_descr, &config_descr);
if (verbose) { Utils::Dump::device(device_descr); }
} catch (Usb::Session::Device_not_found) {
error("cound not read config descriptor");
return false;
}
uint8_t const iface = device->iface();
uint8_t const alt = device->alt();
uint8_t const ep = device->ep();
try { usb.claim_interface(iface); }
catch (Usb::Session::Interface_already_claimed) {
error("could not claim device");
return false;
}
try {
usb.interface_descriptor(iface, alt, &iface_descr);
if (verbose) { Utils::Dump::iface(iface_descr); }
} catch (Usb::Session::Interface_not_found) {
error("could not read interface descriptor");
return false;
}
try {
usb.endpoint_descriptor(iface, alt, ep, &ep_descr);
if (verbose) { Utils::Dump::ep(ep_descr); }
} catch (Usb::Session::Interface_not_found) {
error("could not read endpoint descriptor");
return false;
}
polling_us = 1000 * ep_descr.polling_interval;
if (debug) { polling_us = 1000 * 1000; }
/*
* Request HID report descriptor here because certain devices,
* e.g., XBox 360 controller, will not respond otherwise.
*/
hid_report_descr.request();
/*
* Execute device specific quirk method which in most cases simply
* sends an USB packet to get the device in working order.
*/
if (device == &xboxone) {
log("Enable XBox One quirk");
Usb::Endpoint_descriptor ep_out_descr;
usb.endpoint_descriptor(0, 0, 0, &ep_out_descr);
Usb::Packet_descriptor p = alloc_packet(5);
p.type = Usb::Packet_descriptor::IRQ;
p.transfer.ep = ep_out_descr.address;
p.transfer.polling_interval = 100;
uint8_t *data = reinterpret_cast<uint8_t*>(usb.source()->packet_content(p));
data[0] = 0x05;
data[1] = 0x20;
data[2] = 0x00; /* serial */
data[3] = 0x00;
data[4] = 0x00;
usb.source()->submit_packet(p);
} else if (device == &sony_ds3) {
log("Enable DS3 quirk");
enum {
USB_REQUEST_TO_DEVICE = 0x00,
USB_REQUEST_TYPE_CLASS = 0x20,
USB_REQUEST_RCPT_IFACE = 0x01,
USB_REQUEST_GET_DESCRIPTOR = 0x06,
USB_HID_REQUEST_GET_REPORT = 0x01,
USB_HID_REQUEST_SET_REPORT = 0x09,
USB_HID_FEATURE_REPORT = 0x02,
DS3_REPORT = 0xf4,
REQUEST = USB_REQUEST_TO_DEVICE | USB_REQUEST_TYPE_CLASS | USB_REQUEST_RCPT_IFACE,
};
uint8_t const cmd[4] = { 0x42, 0x0C, 0x00, 0x00 };
Usb::Packet_descriptor p = alloc_packet(sizeof(cmd));
uint8_t *data = reinterpret_cast<uint8_t*>(usb.source()->packet_content(p));
Genode::memcpy(data, cmd, sizeof(cmd));
p.type = Usb::Packet_descriptor::CTRL;
p.control.request = USB_HID_REQUEST_SET_REPORT;
p.control.request_type = REQUEST;
p.control.value = ((USB_HID_FEATURE_REPORT + 1) << 8) | DS3_REPORT;
p.control.index = 0;
p.control.timeout = 1000;
usb.source()->submit_packet(p);
}
/* if we do not know the device, at least query vendor information */
if (device == &generic) {
manufactorer_string.request(device_descr.manufactorer_index);
product_string.request(device_descr.product_index);
serial_number_string.request(device_descr.serial_number_index);
}
return true;
}
void handle_polling()
{
Usb::Packet_descriptor p = alloc_packet(ep_descr.max_packet_size);
p.type = Usb::Packet_descriptor::IRQ;
p.succeded = false;
p.transfer.ep = ep_descr.address;
p.transfer.polling_interval = 10;
usb.source()->submit_packet(p);
}
Signal_handler<Hid> polling_dispatcher = {
env.ep(), *this, &Hid::handle_polling };
Completion *_alloc_completion()
{
for (unsigned i = 0; i < Usb::Session::TX_QUEUE_SIZE; i++)
if (completions[i].state == Completion::FREE) {
completions[i].state = Completion::VALID;
return &completions[i];
}
return nullptr;
}
struct Queue_full : Genode::Exception { };
struct No_completion_free : Genode::Exception { };
Usb::Packet_descriptor alloc_packet(int length)
{
if (!usb.source()->ready_to_submit()) { throw Queue_full(); }
Usb::Packet_descriptor p = usb.source()->alloc_packet(length);
p.completion = _alloc_completion();
if (!p.completion) {
usb.source()->release_packet(p);
throw No_completion_free();
}
return p;
}
void free_packet(Usb::Packet_descriptor &packet)
{
dynamic_cast<Completion *>(packet.completion)->state = Completion::FREE;
usb.source()->release_packet(packet);
}
/**
* Constructor
*
* \param env environment
* \param alloc allocator used by Usb::Connection
* \param input Input session
*/
Hid(Env &env, Genode::Allocator &alloc, Input::Session_component &input)
: env(env), input_session(input), usb_alloc(&alloc)
{
usb.tx_channel()->sigh_ack_avail(ack_avail_dispatcher);
timer.sigh(polling_dispatcher);
/* HID gets initialized by state_change() */
}
};
struct Usb::Main
{
Env &env;
Heap heap { env.ram(), env.rm() };
Input::Session_component input_session;
Static_root<Input::Session> input_root { env.ep().manage(input_session) };
Usb::Hid hid { env, heap, input_session };
Main(Env &env) : env(env)
{
input_session.event_queue().enabled(true);
env.parent().announce(env.ep().manage(input_root));
}
};
void Component::construct(Genode::Env &env) { static Usb::Main main(env); }

View File

@@ -0,0 +1,186 @@
/*
* \brief USB HID to Input translator
* \author Josef Soentgen
* \date 2016-10-12
*/
/*
* Copyright (C) 2016 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _MICROSOFT_XBOX_360_H_
#define _MICROSOFT_XBOX_360_H_
/* Genode includes */
#include <util/string.h>
/* local includes */
#include <utils.h>
#include <hid_device.h>
struct Microsoft_xbox360 : Hid_device
{
/*
* Supported devices
*/
struct {
uint16_t vendor_id;
uint16_t product_id;
} devices[1] = {
{0x045e, 0x028e} /* orignal Microsoft XBox 360 wired controller */
};
struct Report
{
uint8_t cmd;
uint8_t size;
uint16_t buttons;
uint8_t lt;
uint8_t rt;
int16_t x;
int16_t y;
int16_t z;
int16_t rz;
uint8_t reserved[6];
}; __attribute__((packed));
enum {
IFACE_NUM = 0,
ALT_NUM = 0,
EP_NUM = 0,
DATA_LENGTH = 20,
B_NUM = 16,
DATA_CMD = 0x00,
AXIS_XY = 0,
AXIS_ZRZ = 1,
AXIS_LT = 2,
AXIS_RT = 3,
};
Input::Keycode b_mapping[B_NUM] = {
Input::Keycode::BTN_FORWARD, /* 0x0001 */
Input::Keycode::BTN_BACK, /* 0x0002 */
Input::Keycode::BTN_LEFT, /* 0x0004 */
Input::Keycode::BTN_RIGHT, /* 0x0008 */
Input::Keycode::BTN_START, /* 0x0010 */
Input::Keycode::BTN_SELECT, /* 0x0020 */
Input::Keycode::BTN_THUMBL, /* 0x0040 */
Input::Keycode::BTN_THUMBR, /* 0x0080 */
Input::Keycode::BTN_TL, /* 0x0100 */ /* LB */
Input::Keycode::BTN_TR, /* 0x0200 */ /* RB */
Input::Keycode::BTN_MODE, /* 0x0400 */ /* Xbox/guide button */
Input::Keycode::KEY_UNKNOWN, /* 0x0800 */ /* unused */
Input::Keycode::BTN_A, /* 0x1000 */
Input::Keycode::BTN_B, /* 0x2000 */
Input::Keycode::BTN_X, /* 0x4000 */
Input::Keycode::BTN_Y, /* 0x8000 */
};
typedef signed short int16_t;
uint8_t last[DATA_LENGTH] = {};
bool left_stick_enabled = true;
bool right_stick_enabled = true;
bool verbose = false;
void dump_state(Report const * const o, Report const * const n)
{
using namespace Genode;
log("dump state:");
log("cmd: ", Hex(n->cmd), " (", Hex(o->cmd), ")");
log("size: ", Hex(n->size), " (", Hex(o->size), ")");
log("buttons: ", Hex(n->buttons), " (", Hex(o->buttons), ")");
log("tl: ", Hex(n->lt), " (", Hex(o->lt), ")");
log("tr: ", Hex(n->rt), " (", Hex(o->rt), ")");
log("x: ", Hex(n->x), " (", Hex(o->x), ")");
log("y: ", Hex(n->y), " (", Hex(o->y), ")");
log("z: ", Hex(n->z), " (", Hex(o->z), ")");
log("rz: ", Hex(n->rz), " (", Hex(o->rz), ")");
}
Microsoft_xbox360(Input::Session_component &input_session)
: Hid_device(input_session, "Microsoft Corp. Xbox360 Controller") { }
/**************************
** HID device interface **
**************************/
bool probe(uint16_t vendor_id, uint16_t product_id) const override
{
for (size_t i = 0; i < sizeof(devices)/sizeof(devices[0]); i++) {
if ( vendor_id == devices[i].vendor_id
&& product_id == devices[i].product_id) {
return true;
}
}
return false;
}
void parse(uint8_t const *data, size_t len) override
{
using namespace Genode;
Report const * const n = reinterpret_cast<Report const*>(data);
/* for now ignore the rest */
if (n->cmd != DATA_CMD) { return; }
Report const * const o = reinterpret_cast<Report const*>(last);
bool const changed = memcmp(data, last, len);
if (!changed) { return; }
if (verbose) { dump_state(o, n); }
/* check analog sticks */
if (left_stick_enabled) {
Utils::check_axis(input_session, o->x, n->x, o->y, n->y, AXIS_XY);
}
if (right_stick_enabled) {
Utils::check_axis(input_session, o->z, n->z, o->rz, n->rz, AXIS_ZRZ);
}
/* check analog triggers */
uint8_t const olt = o->lt;
uint8_t const nlt = n->lt;
if (olt != nlt) {
int16_t const oltv = Utils::convert_u8_to_s16(olt);
int16_t const nltv = Utils::convert_u8_to_s16(nlt);
Utils::check_axis(input_session, oltv, nltv, 0, 0, AXIS_LT);
}
uint8_t const ort = o->rt;
uint8_t const nrt = n->rt;
if (ort != nrt) {
int16_t const ortv = Utils::convert_u8_to_s16(ort);
int16_t const nrtv = Utils::convert_u8_to_s16(nrt);
Utils::check_axis(input_session, ortv, nrtv, 0, 0, AXIS_RT);
}
/* check buttons */
uint16_t const ob = o->buttons;
uint16_t const nb = n->buttons;
if (ob != nb) { Utils::check_buttons(input_session, ob, nb, B_NUM, b_mapping); }
/* save for next poll */
Genode::memcpy(last, data, len);
}
uint8_t iface() const { return IFACE_NUM; }
uint8_t ep() const { return EP_NUM; }
uint8_t alt() const { return ALT_NUM; }
};
#endif /* _MICROSOFT_XBOX_360_H_ */

View File

@@ -0,0 +1,197 @@
/*
* \brief USB HID to Input translator
* \author Josef Soentgen
* \date 2016-10-12
*/
/*
* Copyright (C) 2016 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _MICROSOFT_XBOX_ONE_H_
#define _MICROSOFT_XBOX_ONE_H_
/* Genode includes */
#include <util/string.h>
/* local includes */
#include <utils.h>
#include <hid_device.h>
struct Microsoft_xboxone : Hid_device
{
/*
* Supported devices
*/
struct {
uint16_t vendor_id;
uint16_t product_id;
} devices[2] = {
{0x045e, 0x02d1}, /* Microsoft XBox One wired controller */
{0x045e, 0x02dd} /* Microsoft XBox One wired controller (new FW) */
};
struct Report
{
uint8_t cmd;
uint8_t reserved1; /* always 0x00 */
uint8_t packet_nr;
uint8_t reserved2; /* always 0x0e */
uint16_t buttons;
uint16_t lt;
uint16_t rt;
int16_t x;
int16_t y;
int16_t z;
int16_t rz;
}; __attribute__((packed));
enum {
IFACE_NUM = 0,
ALT_NUM = 0,
EP_NUM = 1,
DATA_LENGTH = 18,
DATA_OFFSET = 4, /* ignore cmd + packet_nr */
B_NUM = 16,
DATA_CMD = 0x20,
AXIS_XY = 0,
AXIS_ZRZ = 1,
AXIS_LT = 2,
AXIS_RT = 3,
};
Input::Keycode b_mapping[B_NUM] = {
Input::Keycode::KEY_UNKNOWN, /* 0x0001 */ /* unused */
Input::Keycode::KEY_UNKNOWN, /* 0x0002 */ /* unused */
Input::Keycode::BTN_START, /* 0x0004 */
Input::Keycode::BTN_SELECT, /* 0x0008 */
Input::Keycode::BTN_A, /* 0x0010 */
Input::Keycode::BTN_B, /* 0x0020 */
Input::Keycode::BTN_X, /* 0x0040 */
Input::Keycode::BTN_Y, /* 0x0080 */
Input::Keycode::BTN_FORWARD, /* 0x0100 */
Input::Keycode::BTN_BACK, /* 0x0200 */
Input::Keycode::BTN_LEFT, /* 0x0400 */
Input::Keycode::BTN_RIGHT, /* 0x0800 */
Input::Keycode::BTN_TL, /* 0x1000 */ /* LB */
Input::Keycode::BTN_TR, /* 0x2000 */ /* RB */
Input::Keycode::BTN_THUMBL, /* 0x4000 */
Input::Keycode::BTN_THUMBR, /* 0x8000 */
};
typedef signed short int16_t;
uint8_t last[DATA_LENGTH] = {};
bool left_stick_enabled = true;
bool right_stick_enabled = true;
bool verbose = false;
void dump_state(Report const * const o, Report const * const n)
{
using namespace Genode;
log("dump state:");
log("cmd: ", Hex(n->cmd), " (", Hex(o->cmd), ")");
log("packet_nr: ", Hex(n->packet_nr), " (", Hex(o->packet_nr), ")");
log("buttons: ", Hex(n->buttons), " (", Hex(o->buttons), ")");
log("lt: ", Hex(n->lt), " (", Hex(o->lt), ")");
log("rt: ", Hex(n->rt), " (", Hex(o->rt), ")");
log("x: ", Hex(n->x), " (", Hex(o->x), ")");
log("y: ", Hex(n->y), " (", Hex(o->y), ")");
log("z: ", Hex(n->z), " (", Hex(o->z), ")");
log("rz: ", Hex(n->rz), " (", Hex(o->rz), ")");
}
Microsoft_xboxone(Input::Session_component &input_session)
: Hid_device(input_session, "Microsoft Corp. Xbox One Controller") { }
/**************************
** HID device interface **
**************************/
bool probe(uint16_t vendor_id, uint16_t product_id) const override
{
for (size_t i = 0; i < sizeof(devices)/sizeof(devices[0]); i++) {
if ( vendor_id == devices[i].vendor_id
&& product_id == devices[i].product_id) {
return true;
}
}
return false;
}
void parse(uint8_t const *data, size_t len) override
{
using namespace Genode;
Report const * const n = reinterpret_cast<Report const*>(data);
/* ignore the rest */
if (n->cmd != DATA_CMD) { return; }
if (len != DATA_LENGTH) {
warning("drop invalid packet ", n->packet_nr);
return;
}
Report const * const o = reinterpret_cast<Report const*>(last);
bool const changed = memcmp(data+DATA_OFFSET, last+DATA_OFFSET, len-DATA_OFFSET);
if (!changed) { return; }
if (verbose) { dump_state(o, n); }
/* check analog sticks */
if (left_stick_enabled) {
Utils::check_axis(input_session, o->x, n->x, o->y, n->y, AXIS_XY);
}
if (right_stick_enabled) {
Utils::check_axis(input_session, o->z, n->z, o->rz, n->rz, AXIS_ZRZ);
}
/*
* check analog triggers
*
* XXX not sure if 1024 -> [-2^15,2^15] is a good idea
*/
uint8_t const olt = o->lt;
uint8_t const nlt = n->lt;
if (olt != nlt) {
int16_t const oltv = Utils::convert_u8_to_s16(olt);
int16_t const nltv = Utils::convert_u8_to_s16(nlt);
Utils::check_axis(input_session, oltv, nltv, 0, 0, AXIS_LT);
}
uint16_t const ort = o->rt;
uint16_t const nrt = n->rt;
if (ort != nrt) {
int16_t const ortv = Utils::convert_u8_to_s16(ort);
int16_t const nrtv = Utils::convert_u8_to_s16(nrt);
Utils::check_axis(input_session, ortv, nrtv, 0, 0, AXIS_RT);
}
/* check buttons */
uint16_t const ob = o->buttons;
uint16_t const nb = n->buttons;
if (ob != nb) { Utils::check_buttons(input_session, ob, nb, B_NUM, b_mapping); }
/* save for next poll */
Genode::memcpy(last, data, len);
}
uint8_t iface() const { return IFACE_NUM; }
uint8_t ep() const { return EP_NUM; }
uint8_t alt() const { return ALT_NUM; }
};
#endif /* _MICROSOFT_XBOX_ONE_H_ */

View File

@@ -0,0 +1,193 @@
/*
* \brief USB HID to Input translator
* \author Josef Soentgen
* \date 2016-10-12
*/
/*
* Copyright (C) 2016 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _RETROLINK_N64_H_
#define _RETROLINK_N64_H_
/* Genode includes */
#include <util/string.h>
/* local includes */
#include <utils.h>
#include <hid_device.h>
struct Retrolink_n64 : Hid_device
{
/*
* Supported devices
*/
struct {
uint16_t vendor_id;
uint16_t product_id;
} devices[1] = {
{0x0079, 0x0006}
};
/*
* The actual report data coming from the device does not correspond
* to the HID report, unless I missed something. The digipad and
* the camera buttons are in fact reported in byte 6, 4Bit each.
*/
struct Report
{
uint8_t x; /* analog x-axis [0,255] */
uint8_t y; /* analog y-axis [0,255] */
uint8_t z; /* - */
uint8_t w; /* - */
uint8_t v; /* - */
uint8_t h; /* high nibble pad, low nibble camera */
uint8_t b; /* 6 buttons (l, r, z, a, b, start) */
uint8_t r; /* - */
}; __attribute__((packed));
enum {
IFACE_NUM = 0,
ALT_NUM = 0,
EP_NUM = 0,
DATA_LENGTH = 8,
X = 0,
Y = 1,
H = 5,
B = 6,
AXIS_XY = 0,
ORIGIN = 0x80,
HAT_ORIGIN = 0x0f,
B_NUM = 6,
C_NUM = 4,
H_NUM = 8,
};
Input::Keycode b_mapping[B_NUM] = {
Input::Keycode::BTN_TL, /* 0x01 */
Input::Keycode::BTN_TR, /* 0x02 */
Input::Keycode::BTN_A, /* 0x04 */
Input::Keycode::BTN_Z, /* 0x08 */
Input::Keycode::BTN_B, /* 0x10 */
Input::Keycode::BTN_START, /* 0x20 */
};
Input::Keycode c_mapping[C_NUM] = {
Input::Keycode::BTN_0, /* 0x01 */
Input::Keycode::BTN_1, /* 0x02 */
Input::Keycode::BTN_2, /* 0x04 */
Input::Keycode::BTN_3, /* 0x08 */
};
char const *h_name(uint8_t v)
{
switch (v) {
case 0: return "BTN_FORWARD";
case 2: return "BTN_RIGHT";
case 4: return "BTN_BACK";
case 6: return "BTN_LEFT";
}
return "<unknown button>";
}
uint8_t last[DATA_LENGTH] = {};
Retrolink_n64(Input::Session_component &input_session)
: Hid_device(input_session, "Retrolink N64 gamepad")
{
/* initial values */
last[X] = ORIGIN;
last[Y] = ORIGIN;
last[H] = HAT_ORIGIN;
}
/**************************
** HID device interface **
**************************/
bool probe(uint16_t vendor_id, uint16_t product_id) const override
{
for (size_t i = 0; i < sizeof(devices)/sizeof(devices[0]); i++) {
if ( vendor_id == devices[i].vendor_id
&& product_id == devices[i].product_id) {
return true;
}
}
return false;
}
void parse(uint8_t const *data, size_t len) override
{
using namespace Genode;
if (len != DATA_LENGTH) {
error("new data invalid");
throw -1;
}
Report const * const o = reinterpret_cast<Report const*>(last);
Report const * const n = reinterpret_cast<Report const*>(data);
bool const changed = (n->x != o->x)
||(n->y != o->y)
||(n->h != o->h)
||(n->b != o->b);
if (!changed) { return; }
// log("dump state:");
// log("x: ", Hex(n->x), " (", Hex(o->x), ")");
// log("y: ", Hex(n->y), " (", Hex(o->y), ")");
// log("h: ", Hex(n->h), " (", Hex(o->h), ")");
// log("b: ", Hex(n->b), " (", Hex(o->b), ")");
/* check analog */
uint8_t const ox = o->x;
uint8_t const nx = n->x;
uint8_t const oy = o->y;
uint8_t const ny = n->y;
int16_t const oxv = Utils::convert_u8_to_s16(ox);
int16_t const nxv = Utils::convert_u8_to_s16(nx);
int16_t const oyv = Utils::convert_u8_to_s16(oy);
int16_t const nyv = Utils::convert_u8_to_s16(ny);
Utils::check_axis(input_session, oxv, nxv, oyv, nyv, AXIS_XY);
/* check digipad */
uint8_t const od = o->h & 0x0f;
uint8_t const nd = n->h & 0x0f;
if (od != nd) { Utils::check_hat(input_session, od, nd); }
/* check camera buttons */
uint8_t const oc = (o->h >> 4) & 0x0f;
uint8_t const nc = (n->h >> 4) & 0x0f;
if (oc != nc) { Utils::check_buttons(input_session, oc, nc, C_NUM, c_mapping); }
/* check buttons */
uint8_t const ob = o->b;
uint8_t const nb = n->b;
if (o->b != n->b) { Utils::check_buttons(input_session, ob, nb, B_NUM, b_mapping); }
/* save for next poll */
Genode::memcpy(last, data, len);
}
uint8_t iface() const { return IFACE_NUM; }
uint8_t ep() const { return EP_NUM; }
uint8_t alt() const { return ALT_NUM; }
};
#endif /* _RETROLINK_N64_H_ */

View File

@@ -0,0 +1,225 @@
/*
* \brief USB HID to Input translator
* \author Josef Soentgen
* \date 2016-10-12
*/
/*
* Copyright (C) 2016 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _SONY_DS3_H_
#define _SONY_DS3_H_
/* Genode includes */
#include <util/string.h>
/* local includes */
#include <utils.h>
#include <hid_device.h>
struct Sony_ds3 : Hid_device
{
/*
* Supported devices
*/
struct {
uint16_t vendor_id;
uint16_t product_id;
} devices[2] = {
{0x054c, 0x0268}, /* Sony Corp. Batoh Device / PlayStation 3 Controller */
};
struct Report
{
uint8_t rid;
uint8_t unused1;
uint16_t buttons;
uint8_t ps_button; /* currently unused */
uint8_t unused2;
uint8_t x;
uint8_t y;
uint8_t z;
uint8_t rz;
uint8_t unused3[8];
uint8_t lt;
uint8_t rt;
}; __attribute__((packed));
enum {
IFACE_NUM = 0,
ALT_NUM = 0,
EP_NUM = 1,
DATA_LENGTH = 49,
B_NUM = 16,
DATA_CMD = 0x20,
AXIS_XY = 0,
AXIS_ZRZ = 1,
AXIS_LT = 2,
AXIS_RT = 3,
};
#if 0
data [0] rid
data [1] 0x0
data [2] = 0x01 select, 0x08 start buttons, 0x10 DU, 0x20 DR, 0x40 DD, 0x80 DL
data [3] = 0x01 L2, 0x02 R2, 0x04 L1, 0x08 R1, 0x10 t, 0x20 o, 0x40 x, 0x80 s buttons
data [4] = 0x01 PS button
data [6] = x
data [7] = y
data [8] = z
data [9] = rz
data [18] = LT
data [19] = RT
#endif
Input::Keycode b_mapping[B_NUM] = {
Input::Keycode::BTN_SELECT, /* 0x0001 */
Input::Keycode::BTN_THUMBL, /* 0x0002 */
Input::Keycode::BTN_THUMBR, /* 0x0004 */
Input::Keycode::BTN_START, /* 0x0008 */
Input::Keycode::BTN_FORWARD, /* 0x0010 */
Input::Keycode::BTN_RIGHT, /* 0x0020 */
Input::Keycode::BTN_BACK, /* 0x0040 */
Input::Keycode::BTN_LEFT, /* 0x0080 */
Input::Keycode::BTN_TL2, /* 0x0100 */
Input::Keycode::BTN_TR2, /* 0x0200 */
Input::Keycode::BTN_TL, /* 0x0400 */
Input::Keycode::BTN_TR, /* 0x0800 */
Input::Keycode::BTN_Y, /* 0x1000 */
Input::Keycode::BTN_B, /* 0x2000 */
Input::Keycode::BTN_A, /* 0x4000 */
Input::Keycode::BTN_X, /* 0x8000 */
};
typedef signed short int16_t;
uint8_t last[DATA_LENGTH] = {};
bool left_stick_enabled = true;
bool right_stick_enabled = true;
bool verbose = false;
void dump_state(Report const * const o, Report const * const n)
{
using namespace Genode;
log("dump state:");
log("buttons: ", Hex(n->buttons), " (", Hex(o->buttons), ")");
log("ps_button: ", Hex(n->ps_button), " (", Hex(o->ps_button), ")");
log("x: ", Hex(n->x), " (", Hex(o->x), ")");
log("y: ", Hex(n->y), " (", Hex(o->y), ")");
log("z: ", Hex(n->z), " (", Hex(o->z), ")");
log("rz: ", Hex(n->rz), " (", Hex(o->rz), ")");
log("lt: ", Hex(n->lt), " (", Hex(o->lt), ")");
log("rt: ", Hex(n->rt), " (", Hex(o->rt), ")");
}
Sony_ds3(Input::Session_component &input_session)
: Hid_device(input_session, "Sony Corp. PlayStation(R) 3 Controller") { }
/**************************
** HID device interface **
**************************/
bool probe(uint16_t vendor_id, uint16_t product_id) const override
{
for (size_t i = 0; i < sizeof(devices)/sizeof(devices[0]); i++) {
if ( vendor_id == devices[i].vendor_id
&& product_id == devices[i].product_id) {
return true;
}
}
return false;
}
void parse(uint8_t const *data, size_t len) override
{
using namespace Genode;
Report const * const n = reinterpret_cast<Report const*>(data);
if (len != DATA_LENGTH) { return; }
Report const * const o = reinterpret_cast<Report const*>(last);
bool const changed = memcmp(data, last, len);
if (!changed) { return; }
if (verbose) { dump_state(o, n); }
/* check analog sticks */
if (left_stick_enabled) {
uint8_t const ox = o->x;
uint8_t const nx = n->x;
uint8_t const oy = o->y;
uint8_t const ny = n->y;
int16_t const oxv = Utils::convert_u8_to_s16(ox);
int16_t const nxv = Utils::convert_u8_to_s16(nx);
int16_t const oyv = Utils::convert_u8_to_s16(oy);
int16_t const nyv = Utils::convert_u8_to_s16(ny);
Utils::check_axis(input_session, oxv, nxv, oyv, nyv, AXIS_XY);
}
if (right_stick_enabled) {
uint8_t const oz = o->z;
uint8_t const nz = n->z;
uint8_t const orz = o->rz;
uint8_t const nrz = n->rz;
int16_t const ozv = Utils::convert_u8_to_s16(oz);
int16_t const nzv = Utils::convert_u8_to_s16(nz);
int16_t const orzv = Utils::convert_u8_to_s16(orz);
int16_t const nrzv = Utils::convert_u8_to_s16(nrz);
Utils::check_axis(input_session, ozv, nzv, orzv, nrzv, AXIS_ZRZ);
}
/*
* check analog triggers
*
* XXX not sure if 1024 -> [-2^15,2^15] is a good idea
*/
uint8_t const olt = o->lt;
uint8_t const nlt = n->lt;
if (olt != nlt) {
int16_t const oltv = Utils::convert_u8_to_s16(olt);
int16_t const nltv = Utils::convert_u8_to_s16(nlt);
Utils::check_axis(input_session, oltv, nltv, 0, 0, AXIS_LT);
}
uint16_t const ort = o->rt;
uint16_t const nrt = n->rt;
if (ort != nrt) {
int16_t const ortv = Utils::convert_u8_to_s16(ort);
int16_t const nrtv = Utils::convert_u8_to_s16(nrt);
Utils::check_axis(input_session, ortv, nrtv, 0, 0, AXIS_RT);
}
/* check buttons */
uint16_t const ob = o->buttons;
uint16_t const nb = n->buttons;
if (ob != nb) { Utils::check_buttons(input_session, ob, nb, B_NUM, b_mapping); }
/* save for next poll */
Genode::memcpy(last, data, len);
}
uint8_t iface() const { return IFACE_NUM; }
uint8_t ep() const { return EP_NUM; }
uint8_t alt() const { return ALT_NUM; }
};
#endif /* _SONY_DS3_H_ */

View File

@@ -0,0 +1,245 @@
/*
* \brief USB HID to Input translator
* \author Josef Soentgen
* \date 2016-10-12
*/
/*
* Copyright (C) 2016 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _SONY_DS4_H_
#define _SONY_DS4_H_
/* Genode includes */
#include <util/string.h>
/* local includes */
#include <utils.h>
#include <hid_device.h>
struct Sony_ds4 : Hid_device
{
/*
* Supported devices
*/
struct {
uint16_t vendor_id;
uint16_t product_id;
} devices[2] = {
{0x054c, 0x05c4}, /* Sony Corp. */
};
struct Report
{
uint8_t rid;
uint8_t x;
uint8_t y;
uint8_t z;
uint8_t rz;
uint8_t dbuttons;
uint8_t buttons;
uint8_t ps_button;
uint8_t lt;
uint8_t rt;
/*
uint8_t unused2[3];
uint8_t bat_level;
*/
}; __attribute__((packed));
enum {
IFACE_NUM = 0,
ALT_NUM = 0,
EP_NUM = 0, /* first IRQ EP IN */
DATA_LENGTH = 64,
B_NUM = 16,
DATA_CMD = 0x20,
AXIS_XY = 0,
AXIS_ZRZ = 1,
AXIS_LT = 2,
AXIS_RT = 3,
};
#if 0
data [0] rid
data [1] x
data [2] y
data [3] z
data [4] rz
data [5] 0x10 square, 0x20 cross, 0x40 circle, 0x80 triangle
dpad: 7=NW 6=W 5=SW 4=S 3=SE 2=E 1=NE 0=N
data [6] 0x01 L1, 0x02 R1, 0x04 L2, 0x08 R2, 0x10 SH, 0x20 OPT, 0x40 L3, 0x80 R3
data [7] 0x01 PS button
data [8] L2 trigger
data [9] R2 trigger
data [12] BAT level
#endif
/*
* The buttons are ordered after the reshuffling of data:
* v = (dbuttons & 0xf0) | (ps_button & 0x01) | (buttons << 8)
*/
Input::Keycode b_mapping[B_NUM] = {
Input::Keycode::BTN_MODE, /* 0x0001 */
Input::Keycode::KEY_UNKNOWN, /* 0x0002 */
Input::Keycode::KEY_UNKNOWN, /* 0x0004 */
Input::Keycode::KEY_UNKNOWN, /* 0x0008 */
Input::Keycode::BTN_X, /* 0x0010 */
Input::Keycode::BTN_A, /* 0x0020 */
Input::Keycode::BTN_B, /* 0x0040 */
Input::Keycode::BTN_Y, /* 0x0080 */
Input::Keycode::BTN_TL, /* 0x0100 */
Input::Keycode::BTN_TR, /* 0x0200 */
Input::Keycode::BTN_TL2, /* 0x0400 */
Input::Keycode::BTN_TR2, /* 0x0800 */
Input::Keycode::BTN_SELECT, /* 0x1000 */
Input::Keycode::BTN_START, /* 0x2000 */
Input::Keycode::BTN_THUMBL, /* 0x4000 */
Input::Keycode::BTN_THUMBR, /* 0x8000 */
};
typedef signed short int16_t;
uint8_t last[DATA_LENGTH] = {};
bool left_stick_enabled = false;
bool right_stick_enabled = false;
bool verbose = false;
void dump_state(Report const * const o, Report const * const n)
{
using namespace Genode;
log("dump state:");
log("dbuttons: ", Hex(n->dbuttons), " (", Hex(o->dbuttons), ")");
log("buttons: ", Hex(n->buttons), " (", Hex(o->buttons), ")");
log("ps_button: ", Hex(n->ps_button), " (", Hex(o->ps_button), ")");
log("x: ", Hex(n->x), " (", Hex(o->x), ")");
log("y: ", Hex(n->y), " (", Hex(o->y), ")");
log("z: ", Hex(n->z), " (", Hex(o->z), ")");
log("rz: ", Hex(n->rz), " (", Hex(o->rz), ")");
log("lt: ", Hex(n->lt), " (", Hex(o->lt), ")");
log("rt: ", Hex(n->rt), " (", Hex(o->rt), ")");
}
Sony_ds4(Input::Session_component &input_session)
: Hid_device(input_session, "Sony Corp. PlayStation(R) 4 Controller")
{
last[5] = 0x08;
}
/**************************
** HID device interface **
**************************/
bool probe(uint16_t vendor_id, uint16_t product_id) const override
{
for (size_t i = 0; i < sizeof(devices)/sizeof(devices[0]); i++) {
if ( vendor_id == devices[i].vendor_id
&& product_id == devices[i].product_id) {
return true;
}
}
return false;
}
void parse(uint8_t const *data, size_t len) override
{
using namespace Genode;
Report const * const n = reinterpret_cast<Report const*>(data);
if (len != DATA_LENGTH) { return; }
Report const * const o = reinterpret_cast<Report const*>(last);
/* ignore ever changing report counter */
Report * const hack = reinterpret_cast<Report *>(const_cast<uint8_t*>(data));
hack->ps_button &= 0x01;
/* we only care about the actual input data */
bool const changed = memcmp(data, last, sizeof(Report));
if (!changed) { return; }
if (verbose) { dump_state(o, n); }
/* check analog sticks */
if (left_stick_enabled) {
uint8_t const ox = o->x;
uint8_t const nx = n->x;
uint8_t const oy = o->y;
uint8_t const ny = n->y;
int16_t const oxv = Utils::convert_u8_to_s16(ox);
int16_t const nxv = Utils::convert_u8_to_s16(nx);
int16_t const oyv = Utils::convert_u8_to_s16(oy);
int16_t const nyv = Utils::convert_u8_to_s16(ny);
Utils::check_axis(input_session, oxv, nxv, oyv, nyv, AXIS_XY);
}
if (right_stick_enabled) {
uint8_t const oz = o->z;
uint8_t const nz = n->z;
uint8_t const orz = o->rz;
uint8_t const nrz = n->rz;
int16_t const ozv = Utils::convert_u8_to_s16(oz);
int16_t const nzv = Utils::convert_u8_to_s16(nz);
int16_t const orzv = Utils::convert_u8_to_s16(orz);
int16_t const nrzv = Utils::convert_u8_to_s16(nrz);
Utils::check_axis(input_session, ozv, nzv, orzv, nrzv, AXIS_ZRZ);
}
/*
* check analog triggers
*
* XXX not sure if 1024 -> [-2^15,2^15] is a good idea
*/
uint8_t const olt = o->lt;
uint8_t const nlt = n->lt;
if (olt != nlt) {
int16_t const oltv = Utils::convert_u8_to_s16(olt);
int16_t const nltv = Utils::convert_u8_to_s16(nlt);
Utils::check_axis(input_session, oltv, nltv, 0, 0, AXIS_LT);
}
uint16_t const ort = o->rt;
uint16_t const nrt = n->rt;
if (ort != nrt) {
int16_t const ortv = Utils::convert_u8_to_s16(ort);
int16_t const nrtv = Utils::convert_u8_to_s16(nrt);
Utils::check_axis(input_session, ortv, nrtv, 0, 0, AXIS_RT);
}
uint8_t const od = o->dbuttons & 0x0f;
uint8_t const nd = n->dbuttons & 0x0f;
if (od != nd) { Utils::check_hat(input_session, od, nd); }
/* check buttons */
uint16_t const ob = (o->dbuttons & 0xf0) | (o->ps_button & 0x01) | (o->buttons << 8);
uint16_t const nb = (n->dbuttons & 0xf0) | (n->ps_button & 0x01) | (n->buttons << 8);
if (ob != nb) { Utils::check_buttons(input_session, ob, nb, B_NUM, b_mapping); }
/* save for next poll */
Genode::memcpy(last, data, len);
}
uint8_t iface() const { return IFACE_NUM; }
uint8_t ep() const { return EP_NUM; }
uint8_t alt() const { return ALT_NUM; }
};
#endif /* _SONY_DS4_H_ */

View File

@@ -0,0 +1,4 @@
TARGET = usb_gamepad_input_drv
SRC_CC = main.cc
INC_DIR = $(PRG_DIR)
LIBS = base

View File

@@ -0,0 +1,190 @@
/*
* \brief USB HID to Input translator
* \author Josef Soentgen
* \date 2016-10-12
*/
/*
* Copyright (C) 2016 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _UTILS_H_
#define _UTILS_H_
/* Genode includes */
#include <base/log.h>
#include <usb/types.h>
#include <input/keycodes.h>
#include <input_session/connection.h>
namespace Utils {
using namespace Genode;
using namespace Usb;
namespace Dump {
void device(Device_descriptor &);
void iface(Interface_descriptor &);
void ep(Endpoint_descriptor &);
}
typedef signed short int16_t;
int16_t convert_u8_to_s16(uint8_t);
template <typename T> void check_buttons(Input::Session_component&,
T const, T const, uint8_t const, Input::Keycode[]);
void check_axis(Input::Session_component&,
int16_t const, int16_t const, int16_t const, int16_t const, int const);
void check_hat(Input::Session_component &, uint8_t const, uint8_t const);
}
void Utils::Dump::device(Device_descriptor &descr)
{
log("Device: "
"len: ", Hex(descr.length), " "
"type: " , Hex(descr.type), " "
"class: ", Hex(descr.dclass), " "
"sub-class: ", Hex(descr.dsubclass), " "
"proto: ", Hex(descr.dprotocol), " "
"max_packet_size: ", Hex(descr.max_packet_size));
log(" "
"vendor: ", Hex(descr.vendor_id), " "
"product: ", Hex(descr.product_id), " "
"num_configs: ", Hex(descr.num_configs));
}
void Utils::Dump::iface(Interface_descriptor &descr)
{
log("Interface: ",
"len: ", Hex(descr.length), " "
"type: ", Hex(descr.type), " "
"number: ", Hex(descr.number), " "
"alt_settings: ", Hex(descr.alt_settings), " "
"num_endpoints: ", Hex(descr.num_endpoints), " "
"iclass: ", Hex(descr.iclass), " "
"isubclass: ", Hex(descr.isubclass), " "
"iprotocol: ", Hex(descr.iprotocol), " "
"str_index: ", Hex(descr.interface_index));
}
void Utils::Dump::ep(Endpoint_descriptor &descr)
{
log("Endpoint: ",
"len: ", Hex(descr.length), " "
"type: ", Hex(descr.type), " "
"address: ", Hex(descr.address), " "
"attributes: ", Hex(descr.attributes), " "
"max_packet_size: ", Hex(descr.max_packet_size), " "
"polling_interval: ", descr.polling_interval);
}
Utils::int16_t Utils::convert_u8_to_s16(uint8_t val)
{
if (val == 0) { return -0x7fff; }
return val * 0x0101 - 0x8000;
}
template <typename T>
void Utils::check_buttons(Input::Session_component &input_session,
T const prev, T const curr,
uint8_t const count, Input::Keycode mapping[])
{
for (uint8_t i = 0; i < count; i++) {
uint16_t const idx = 1u << i;
if ((prev & idx) == (curr & idx)) { continue; }
bool const press = !(prev & idx) && (curr & idx);
Input::Event ev(press ? Input::Event::PRESS : Input::Event::RELEASE,
mapping[i], 0, 0, 0, 0);
input_session.submit(ev);
}
}
void Utils::check_axis(Input::Session_component &input_session,
int16_t const ox, int16_t const nx,
int16_t const oy, int16_t const ny,
int const axis)
{
bool const x = (ox != nx);
bool const y = (oy != ny);
if (!x && !y) { return; }
Input::Event ev(Input::Event::MOTION, axis, x ? nx : ox, y ? ny : oy , 0, 0);
input_session.submit(ev);
}
void Utils::check_hat(Input::Session_component &input_session, uint8_t const o, uint8_t const n)
{
static struct Axis_mapping
{
signed char x;
signed char y;
} hat_to_dpad[9] = {
{ 0, -1 }, /* N */
{ 1, -1 }, /* NE */
{ 1, 0 }, /* E */
{ 1, 1 }, /* SE */
{ 0, 1 }, /* S */
{ -1, 1 }, /* SW */
{ -1, 0 }, /* W */
{ -1, -1 }, /* NW */
{ 0, 0 }, /* O */
};
static Input::Keycode axis_keymap_x[3] = {
Input::Keycode::BTN_LEFT,
Input::Keycode::KEY_UNKNOWN,
Input::Keycode::BTN_RIGHT,
};
static Input::Keycode axis_keymap_y[3] = {
Input::Keycode::BTN_FORWARD,
Input::Keycode::KEY_UNKNOWN,
Input::Keycode::BTN_BACK,
};
Axis_mapping ao = hat_to_dpad[o];
Axis_mapping an = hat_to_dpad[n];
if (ao.x != an.x) {
if (an.x != 0) {
Input::Event ev(Input::Event::PRESS, axis_keymap_x[an.x+1], 0, 0, 0, 0);
input_session.submit(ev);
}
if (ao.x != 0) {
Input::Event ev(Input::Event::RELEASE, axis_keymap_x[ao.x+1], 0, 0, 0, 0);
input_session.submit(ev);
}
}
if (ao.y != an.y) {
if (an.y != 0) {
Input::Event ev(Input::Event::PRESS, axis_keymap_y[an.y+1], 0, 0, 0, 0);
input_session.submit(ev);
}
if (ao.y != 0) {
Input::Event ev(Input::Event::RELEASE, axis_keymap_y[ao.y+1], 0, 0, 0, 0);
input_session.submit(ev);
}
}
}
#endif /* _UTILS_H_ */