Moved usb handling into seperate thread

Added mouse emulation via abs_to_rel
Added abs_to_btn converter
This commit is contained in:
Ingo Ruhnke 2008-04-26 06:36:38 +02:00
parent 5008924201
commit 1d01b66573
13 changed files with 428 additions and 133 deletions

View file

@ -5,10 +5,12 @@ env.Program("xboxdrv", ["xboxdrv.cpp", "uinput.cpp"])
env.Program("inputdrv",
["inputdrv.cpp",
"xbox360_driver.cpp",
"xbox360_usb_thread.cpp",
"control.cpp",
"abs_to_rel.cpp",
"abs_to_btn.cpp",
"uinput_driver.cpp",
"toggle_button.cpp"],
LIBS=['boost_signals', 'usb'])
LIBS=['boost_signals', 'usb', 'pthread'])
# EOF #

34
TODO
View file

@ -1,3 +1,37 @@
sending rumble is slow and delayed, gets buffered up
d-feet
dbus-send --system --type=method_call --print-reply --dest=org.x.config.display0 /org/x/config/0 org.x.config.input.remove uint32:5
Error org.freedesktop.DBus.Error.ServiceUnknown: The name org.x.config.display0 was not provi
Doing polling seems to lose some events
dbus-send --dest=org.gnome.ScreenSaver
/
dbus-send --dest=org.freedesktop.ExampleName \
/org/freedesktop/sample/object/name
org.freedesktop.ExampleInterface.ExampleMethod \
int32:47 string:'hello world' double:65.32 \
array:string:"1st item","next item","last item" \
dict:string:int32:"one",1,"two",2,"three",3 \
variant:int32:-8 \
objpath:/org/freedesktop/sample/object/name
dbus-send --dest=org.x.config.display0 \
/org/x/config/0
org.x.config.input.add
dbus-send --dest=org.x.config.display0 /org/x/config/0 org.x.config.input.remove int32:5
dbus-send \
--dest=org.x.config.display0 \
org.freedesktop.DBus.Introspectable \
org.freedesktop.DBus.Introspectable.Introspect \
Controls:
=========
* invert button

67
abs_to_btn.cpp Normal file
View file

@ -0,0 +1,67 @@
/* $Id$
** __ __ __ ___ __ __ __ __
** / \ / \__| ____ __| _/_______/ |_|__| | | | ____
** \ \/\/ / |/ \ / __ |/ ___/\ __\ | | | | _/ __ \
** \ /| | | \/ /_/ |\___ \ | | | | |_| |_\ ___/
** \__/\ / |__|___| /\____ /____ > |__| |__|____/____/\___ >
** \/ \/ \/ \/ \/
** Copyright (C) 2007 Ingo Ruhnke <grumbel@gmx.de>
**
** This program is free software; you can redistribute it and/or
** modify it under the terms of the GNU General Public License
** as published by the Free Software Foundation; either version 2
** of the License, or (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software
** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
** 02111-1307, USA.
*/
#include <boost/bind.hpp>
#include "abs_to_btn.hpp"
AbsToBtn::AbsToBtn(int threshold)
: threshold(threshold)
{
abs_port_in.push_back(new AbsPortIn("AbsToBtn-In", 0, 0,
boost::bind(&AbsToBtn::on_abs, this, _1)));
btn_port_out.push_back(new BtnPortOut("AbsToBtn-Out-0"));
btn_port_out.push_back(new BtnPortOut("AbsToBtn-Out-1"));
}
void
AbsToBtn::on_abs(AbsPortOut* port)
{
if (abs(port->get_state() > threshold))
{
if (port->get_state() > 0)
{
btn_port_out[0]->set_state(true);
btn_port_out[1]->set_state(false);
}
else
{
btn_port_out[0]->set_state(false);
btn_port_out[1]->set_state(true);
}
}
else
{
btn_port_out[0]->set_state(false);
btn_port_out[1]->set_state(false);
}
}
void
AbsToBtn::update(float delta)
{
}
/* EOF */

50
abs_to_btn.hpp Normal file
View file

@ -0,0 +1,50 @@
/* $Id$
** __ __ __ ___ __ __ __ __
** / \ / \__| ____ __| _/_______/ |_|__| | | | ____
** \ \/\/ / |/ \ / __ |/ ___/\ __\ | | | | _/ __ \
** \ /| | | \/ /_/ |\___ \ | | | | |_| |_\ ___/
** \__/\ / |__|___| /\____ /____ > |__| |__|____/____/\___ >
** \/ \/ \/ \/ \/
** Copyright (C) 2007 Ingo Ruhnke <grumbel@gmx.de>
**
** This program is free software; you can redistribute it and/or
** modify it under the terms of the GNU General Public License
** as published by the Free Software Foundation; either version 2
** of the License, or (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software
** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
** 02111-1307, USA.
*/
#ifndef HEADER_ABS_TO_BTN_HPP
#define HEADER_ABS_TO_BTN_HPP
#include "control.hpp"
/** */
class AbsToBtn : public Control
{
private:
int threshold;
public:
AbsToBtn(int threshold);
void on_abs(AbsPortOut* port);
void update(float delta);
private:
AbsToBtn (const AbsToBtn&);
AbsToBtn& operator= (const AbsToBtn&);
};
#endif
/* EOF */

View file

@ -27,7 +27,7 @@
#define HEADER_ABS_TO_REL_HPP
#include "control.hpp"
/** */
class AbsToRel : public Control
{
@ -37,11 +37,12 @@ public:
void on_abs(AbsPortOut* port);
void update(float delta);
private:
AbsToRel (const AbsToRel&);
AbsToRel& operator= (const AbsToRel&);
};
#endif
/* EOF */

View file

@ -45,7 +45,12 @@ void abs_change(AbsPortOut* port)
int main()
{
UInputDriver* uinput = new UInputDriver();
// Init USB
usb_init();
usb_find_busses();
usb_find_devices();
UInputDriver* uinput = new UInputDriver("UInputMouseEmulation");
uinput->add_rel(REL_X);
uinput->add_rel(REL_Y);
uinput->add_rel(REL_HWHEEL);
@ -53,13 +58,9 @@ int main()
uinput->add_btn(BTN_LEFT);
uinput->add_btn(BTN_RIGHT);
uinput->add_btn(BTN_MIDDLE);
uinput->add_btn(KEY_Y);
uinput->finish();
// Init USB
usb_init();
usb_find_busses();
usb_find_devices();
std::vector<Control*> controls;
Xbox360Driver* xbox360 = new Xbox360Driver(0);
@ -104,6 +105,9 @@ int main()
xbox360->get_btn_port_out(Xbox360Driver::XBOX360_BTN_X)
->connect(uinput->get_btn_port_in(2));
xbox360->get_btn_port_out(Xbox360Driver::XBOX360_BTN_Y)
->connect(uinput->get_btn_port_in(3));
// ----------------------------
xbox360->get_abs_port_out(Xbox360Driver::XBOX360_AXIS_LT)
@ -122,7 +126,7 @@ int main()
(*i)->update(0.001f);
}
//std::cout << "." << std::flush;
//usleep(1000); // 0.001sec or 1msec
usleep(1000); // 0.001sec or 1msec
}
return 0;

View file

@ -31,7 +31,7 @@
#include <boost/bind.hpp>
#include "uinput_driver.hpp"
UInputDriver::UInputDriver()
UInputDriver::UInputDriver(const std::string& name)
: abs_bit(false),
rel_bit(false),
key_bit(false),
@ -39,7 +39,7 @@ UInputDriver::UInputDriver()
{
std::cout << "UInputDriver" << std::endl;
memset(&user_dev, 0, sizeof(user_dev));
strncpy(user_dev.name, "Custom UInput Driver", UINPUT_MAX_NAME_SIZE);
strncpy(user_dev.name, name.c_str(), UINPUT_MAX_NAME_SIZE);
user_dev.id.version = 0;
user_dev.id.bustype = BUS_USB; // And neither that
user_dev.id.vendor = 0x045e; // FIXME: Don't hardcode this

View file

@ -40,7 +40,7 @@ private:
int fd;
public:
UInputDriver();
UInputDriver(const std::string& name);
~UInputDriver();
void add_abs(uint16_t code, int min, int max);

View file

@ -29,6 +29,8 @@
#include <iostream>
#include <boost/format.hpp>
#include <boost/bind.hpp>
#include "log.hpp"
#include "xbox360_usb_thread.hpp"
#include "xbox360_driver.hpp"
struct usb_device* find_usb_device_by_path(const std::string& busid, const std::string& devid)
@ -88,8 +90,6 @@ const int xbox360_devices_count = sizeof(xbox360_devices)/sizeof(XPadDevice);
void
Xbox360Driver::init()
{
dev = 0;
handle = 0;
rumble_l = 0;
rumble_r = 0;
@ -118,14 +118,23 @@ Xbox360Driver::Xbox360Driver(const std::string& busid, const std::string& devid)
{
init();
dev = find_usb_device_by_path(busid, devid);
open_dev();
struct usb_device* dev = find_usb_device_by_path(busid, devid);
if (!dev)
{
throw std::runtime_error("Xbox360Driver: Couldn't find suitable USB device");
}
else
{
thread = new Xbox360UsbThread(dev);
thread->start();
}
}
Xbox360Driver::Xbox360Driver(int id)
{
init();
struct usb_device* dev = NULL;
// FIXME: This loop can't work
for(int i = 0; i < xbox360_devices_count && !dev; ++i)
{
@ -138,85 +147,29 @@ Xbox360Driver::Xbox360Driver(int id)
}
}
open_dev();
}
Xbox360Driver::~Xbox360Driver()
{
close_dev();
}
void
Xbox360Driver::open_dev()
{
if (!dev)
{
throw std::runtime_error("Xbox360Driver: Couldn't find suitable USB device");
}
else
{
handle = usb_open(dev);
if (usb_claim_interface(handle, 0) != 0) // FIXME: bInterfaceNumber shouldn't be hardcoded
std::cout << "Error claiming the interface: " << usb_strerror() << std::endl;
thread = new Xbox360UsbThread(dev);
thread->start();
}
}
void
Xbox360Driver::close_dev()
Xbox360Driver::~Xbox360Driver()
{
usb_close(handle);
thread->stop();
delete thread;
}
void
Xbox360Driver::update(float delta)
{ // Run this in a seperate Thread
uint8_t data[20];
int ret = usb_interrupt_read(handle, 1 /*EndPoint*/, (char*)data, 20, 5 /*Timeout*/);
if (ret < 0)
{
while(thread->has_msg())
{
if (ret == -ETIMEDOUT)
{ // ok
}
else
{ // Error
std::ostringstream str;
str << "USBError: " << ret << "\n" << usb_strerror() << std::endl;
str << "Shutting down" << std::endl;
throw std::runtime_error(str.str());
}
}
else if (ret == 0) // ignore
{
// happen with the Xbox360 every now and then, just
// ignore, seems harmless
}
else if (ret == 3) // ignore
{
// This data gets send when the controller is accessed the
// first time after being connected to the USB bus, no idea
// what it means, it seems to be identical for different
// controllers, we just ignore it
//
// len: 3 Data: 0x01 0x03 0x0e
// len: 3 Data: 0x02 0x03 0x00
// len: 3 Data: 0x03 0x03 0x03
// len: 3 Data: 0x08 0x03 0x00
// len: 3 Data: 0x01 0x03 0x00
}
else if (ret == 20 && data[0] == 0x00 && data[1] == 0x14)
{
Xbox360Msg& msg = (Xbox360Msg&)data;
process_msg(msg);
}
else
{
std::cout << "Unknown data: bytes: " << ret << " Data: ";
for(int j = 0; j < ret; ++j)
std::cout << boost::format("0x%02x ") % int(data[j]);
std::cout << std::endl;
process_msg(thread->pop_msg());
}
}
@ -253,31 +206,16 @@ Xbox360Driver::process_msg(const Xbox360Msg& msg)
abs_port_out[XBOX360_AXIS_RT]->set_state(msg.rt);
}
void
Xbox360Driver::set_led(uint8_t led_status)
{
char ledcmd[] = { 1, 3, led_status };
usb_interrupt_write(handle, 2, ledcmd, 3, 0);
}
void
Xbox360Driver::set_rumble(uint8_t big, uint8_t small)
{
//std::cout << int(big) << " " << int(small) << std::endl;
char rumblecmd[] = { 0x00, 0x08, 0x00, big, small, 0x00, 0x00, 0x00 };
usb_interrupt_write(handle, 2, rumblecmd, 8, 0);
}
void
Xbox360Driver::on_led_btn(BtnPortOut* btn)
{
if (btn->get_state())
{
set_led(10);
thread->set_led(10);
}
else
{
set_led(0);
thread->set_led(0);
}
}
@ -285,14 +223,14 @@ void
Xbox360Driver::on_rumble_left_abs(AbsPortOut* abs)
{
rumble_l = 255 * (abs->get_state() - abs->min_value) / (abs->max_value - abs->min_value);
set_rumble(rumble_l, rumble_r);
thread->set_rumble(rumble_l, rumble_r);
}
void
Xbox360Driver::on_rumble_right_abs(AbsPortOut* abs)
{
rumble_r = 255 * (abs->get_state() - abs->min_value) / (abs->max_value - abs->min_value);
set_rumble(rumble_l, rumble_r);
thread->set_rumble(rumble_l, rumble_r);
}
/* EOF */

View file

@ -30,6 +30,8 @@
#include "xboxdrv.hpp"
#include "control.hpp"
class Xbox360UsbThread;
/** */
class Xbox360Driver : public Control
{
@ -81,8 +83,7 @@ public:
};
private:
struct usb_device* dev;
struct usb_dev_handle* handle;
Xbox360UsbThread* thread;
uint8_t rumble_l;
uint8_t rumble_r;
@ -92,9 +93,6 @@ public:
Xbox360Driver(const std::string& busid, const std::string& devid);
~Xbox360Driver();
void set_led(uint8_t led_status);
void set_rumble(uint8_t big, uint8_t small);
void on_led_btn(BtnPortOut* btn);
void on_rumble_left_abs(AbsPortOut* abs);
@ -104,8 +102,6 @@ public:
private:
void init();
void open_dev();
void close_dev();
void process_msg(const Xbox360Msg& msg);
Xbox360Driver (const Xbox360Driver&);

157
xbox360_usb_thread.cpp Normal file
View file

@ -0,0 +1,157 @@
/* $Id$
** __ __ __ ___ __ __ __ __
** / \ / \__| ____ __| _/_______/ |_|__| | | | ____
** \ \/\/ / |/ \ / __ |/ ___/\ __\ | | | | _/ __ \
** \ /| | | \/ /_/ |\___ \ | | | | |_| |_\ ___/
** \__/\ / |__|___| /\____ /____ > |__| |__|____/____/\___ >
** \/ \/ \/ \/ \/
** Copyright (C) 2007 Ingo Ruhnke <grumbel@gmx.de>
**
** This program is free software; you can redistribute it and/or
** modify it under the terms of the GNU General Public License
** as published by the Free Software Foundation; either version 2
** of the License, or (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software
** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
** 02111-1307, USA.
*/
#include <usb.h>
#include <iostream>
#include <sstream>
#include <stdexcept>
#include <boost/format.hpp>
#include "log.hpp"
#include "xbox360_usb_thread.hpp"
Xbox360UsbThread::Xbox360UsbThread(struct usb_device* dev)
: thread_quit(false)
{
handle = usb_open(dev); // FIXME: add some error checking
if (usb_claim_interface(handle, 0) != 0) // FIXME: bInterfaceNumber shouldn't be hardcoded
std::cout << "Error claiming the interface: " << usb_strerror() << std::endl;
}
Xbox360UsbThread::~Xbox360UsbThread()
{
usb_close(handle);
}
void
Xbox360UsbThread::start()
{
pthread_t thread;
if (pthread_create(&thread, NULL, Xbox360UsbThread::thread_loop_wrap, this) != 0)
{
LOG("Fatal Error creating the Xbox360UsbThread thread");
}
else
{
LOG("launch_thread_loop");
}
}
void
Xbox360UsbThread::stop()
{ // FIXME: This alone isn't enough, since usb_interrupt_read blocks
thread_quit = true;
}
void
Xbox360UsbThread::set_led(uint8_t led_status)
{ // Should be ok without mutex lock
char ledcmd[] = { 1, 3, led_status };
usb_interrupt_write(handle, 2, ledcmd, 3, 0);
}
void
Xbox360UsbThread::set_rumble(uint8_t big, uint8_t small)
{
// Should be ok without mutex lock
char rumblecmd[] = { 0x00, 0x08, 0x00, big, small, 0x00, 0x00, 0x00 };
usb_interrupt_write(handle, 2, rumblecmd, 8, 0);
}
void*
Xbox360UsbThread::thread_loop()
{
thread_quit = false;
while (!thread_quit)
{
uint8_t data[20];
int ret = usb_interrupt_read(handle, 1 /*EndPoint*/, (char*)data, sizeof(data), 0 /*Timeout*/);
if (ret < 0)
{ // Error
std::ostringstream str;
str << "USBError: " << ret << "\n" << usb_strerror() << std::endl;
str << "Shutting down" << std::endl;
throw std::runtime_error(str.str());
}
else if (ret == 0) // ignore
{
// happen with the Xbox360 every now and then, just
// ignore, seems harmless
}
else if (ret == 3) // ignore
{
// This data gets send when the controller is accessed the
// first time after being connected to the USB bus, no idea
// what it means, it seems to be identical for different
// controllers, we just ignore it
//
// len: 3 Data: 0x01 0x03 0x0e
// len: 3 Data: 0x02 0x03 0x00
// len: 3 Data: 0x03 0x03 0x03
// len: 3 Data: 0x08 0x03 0x00
// len: 3 Data: 0x01 0x03 0x00
}
else if (ret == 20 && data[0] == 0x00 && data[1] == 0x14)
{
//LOG("Data in Mailbox");
Xbox360Msg& msg = (Xbox360Msg&)data;
// mutex this
mailbox.push(msg);
}
else
{
std::cout << "Unknown data: bytes: " << ret << " Data: ";
for(int j = 0; j < ret; ++j)
std::cout << boost::format("0x%02x ") % int(data[j]);
std::cout << std::endl;
}
}
return NULL;
}
void*
Xbox360UsbThread::thread_loop_wrap(void* userdata)
{
Xbox360UsbThread* drv = static_cast<Xbox360UsbThread*>(userdata);
return drv->thread_loop();
}
bool
Xbox360UsbThread::has_msg() const
{
// FIXME: mutex lock this
return !mailbox.empty();
}
Xbox360Msg
Xbox360UsbThread::pop_msg()
{
// FIXME: mutex lock this
Xbox360Msg msg = mailbox.front();
mailbox.pop();
return msg;
}
/* EOF */

66
xbox360_usb_thread.hpp Normal file
View file

@ -0,0 +1,66 @@
/* $Id$
** __ __ __ ___ __ __ __ __
** / \ / \__| ____ __| _/_______/ |_|__| | | | ____
** \ \/\/ / |/ \ / __ |/ ___/\ __\ | | | | _/ __ \
** \ /| | | \/ /_/ |\___ \ | | | | |_| |_\ ___/
** \__/\ / |__|___| /\____ /____ > |__| |__|____/____/\___ >
** \/ \/ \/ \/ \/
** Copyright (C) 2007 Ingo Ruhnke <grumbel@gmx.de>
**
** This program is free software; you can redistribute it and/or
** modify it under the terms of the GNU General Public License
** as published by the Free Software Foundation; either version 2
** of the License, or (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software
** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
** 02111-1307, USA.
*/
#ifndef HEADER_XBOX360_USB_THREAD_HPP
#define HEADER_XBOX360_USB_THREAD_HPP
#include <inttypes.h>
#include <pthread.h>
#include <queue>
#include "xboxdrv.hpp"
/** */
class Xbox360UsbThread
{
private:
struct usb_device* dev;
struct usb_dev_handle* handle;
bool thread_quit;
std::queue<Xbox360Msg> mailbox;
public:
Xbox360UsbThread(struct usb_device* dev);
~Xbox360UsbThread();
void start();
void stop();
void set_led(uint8_t led_status);
void set_rumble(uint8_t big, uint8_t small);
bool has_msg() const;
Xbox360Msg pop_msg();
private:
static void* thread_loop_wrap(void* userdata);
void* thread_loop();
Xbox360UsbThread (const Xbox360UsbThread&);
Xbox360UsbThread& operator= (const Xbox360UsbThread&);
};
#endif
/* EOF */

View file

@ -19,26 +19,6 @@
#ifndef HEADER_XBOX360_HPP
#define HEADER_XBOX360_HPP
// Unused, could be used to allow button remapping
enum Xbox360Buttons {
XBOX360_DPAD_UP = (1<< 0),
XBOX360_DPAD_DOWN = (1<< 1),
XBOX360_DPAD_LEFT = (1<< 2),
XBOX360_DPAD_RIGHT = (1<< 3),
XBOX360_START = (1<< 4),
XBOX360_SELECT = (1<< 5),
XBOX360_THUMB_L = (1<< 6),
XBOX360_THUMB_R = (1<< 7),
XBOX360_LB = (1<< 8),
XBOX360_RB = (1<< 9),
XBOX360_MODE = (1<<10),
// unused = (1<<11),
XBOX360_A = (1<<12),
XBOX360_B = (1<<13),
XBOX360_X = (1<<14),
XBOX360_Y = (1<<15),
};
struct Xbox360Msg
{
// -------------------------