Some framework for integrating libusb into a glib main loop and async support for Controller, test app seems to be working, full xboxdrv will not

This commit is contained in:
Ingo Ruhnke 2011-03-18 21:15:12 +01:00
parent a5ccae7373
commit 3c85ad5765
15 changed files with 599 additions and 14 deletions

22
.gitignore vendored
View file

@ -1,18 +1,20 @@
*~
*.o
.sconsign.dblite
xboxdrv
old
*~
.sconf_temp/
config.log
doc/xboxdrv-daemon.html/
doc/xboxdrv.html/
doc/xboxdrv.ent
custom.py
.sconsign.dblite
cache/
config.log
custom.py
doc/xboxdrv-daemon.html/
doc/xboxdrv.ent
doc/xboxdrv.html/
libxboxdrv.a
test/ini_parser_test
old
src/xboxdrv_controller_glue.hpp
src/xboxdrv_daemon_glue.hpp
src/xboxdrv_dbus_glue.hpp
src/xboxdrv_vfs.hpp
test/ini_parser_test
test/usb_read_thread_test
test/usb_system_test
xboxdrv

6
TODO
View file

@ -55,11 +55,13 @@ $ sudo pbuilder --build --basetgz /var/cache/pbuilder/base-lucid.tgz ../xboxdrv-
Stuff to do before 0.7.4 release:
=================================
* deadzone:MIN:MAX:SMOOTH is broken
* export build-in config files, both as text and as directory, so that users can browse them
* deadzone:MIN:MAX:SMOOTH is broken (fixed)
* deadzone filter documentation is wrong
* d-bus requires X11?!
* d-bus requires X11?! -> try system bus when running as root
Autolaunch error: X11 initialization failed." failed to open connection to bus: /bin/dbus-launch terminated abnormally with the following error: Autolaunch error: X11 initialization failed.

36
src/controller.cpp Normal file
View file

@ -0,0 +1,36 @@
/*
** Xbox360 USB Gamepad Userspace Driver
** Copyright (C) 2011 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 3 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, see <http://www.gnu.org/licenses/>.
*/
#include "controller.hpp"
void
Controller::set_msg_callback(const boost::function<void(const XboxGenericMsg&)>& callback)
{
m_callback = callback;
}
void
Controller::submit_msg(const XboxGenericMsg& msg)
{
if (m_callback)
{
m_callback(msg);
}
}
/* EOF */

View file

@ -27,10 +27,16 @@ struct XboxGenericMsg;
class Controller
{
private:
boost::function<void(const XboxGenericMsg& msg)> m_callback;
public:
Controller() {}
Controller() : m_callback() {}
virtual ~Controller() {}
virtual void start() {}
virtual void stop() {}
virtual void set_rumble(uint8_t left, uint8_t right) =0;
virtual void set_led(uint8_t status) =0;
@ -47,6 +53,9 @@ public:
virtual std::string get_usbid() const { return "-1:-1"; }
virtual std::string get_name() const { return "<not implemented>"; }
void set_msg_callback(const boost::function<void(const XboxGenericMsg&)>& callback);
void submit_msg(const XboxGenericMsg& msg);
private:
Controller (const Controller&);
Controller& operator= (const Controller&);

View file

@ -129,6 +129,30 @@ EvdevController::EvdevController(const std::string& filename,
}
}
void
EvdevController::start()
{
GIOChannel* channel = g_io_channel_unix_new(m_fd);
// set encoding to binary
GError* error;
if (g_io_channel_set_encoding(channel, NULL, &error) != G_IO_STATUS_NORMAL)
{
log_error(error->message);
g_error_free(error);
}
guint source_id;
source_id = g_io_add_watch(channel, static_cast<GIOCondition>(G_IO_IN | G_IO_ERR),
&EvdevController::on_read_data_wrap, this);
}
void
EvdevController::stop()
{
}
void
EvdevController::set_rumble(uint8_t left, uint8_t right)
{
@ -247,4 +271,13 @@ EvdevController::read(XboxGenericMsg& msg, int timeout)
return false;
}
gboolean
EvdevController::on_read_data(GIOChannel* source, GIOCondition condition)
{
//int fd = g_io_channel_unix_get_fd(source);
// process data
return TRUE;
}
/* EOF */

View file

@ -21,6 +21,7 @@
#include <linux/input.h>
#include <string>
#include <glib.h>
#include <queue>
#include "evdev_absmap.hpp"
@ -54,6 +55,9 @@ public:
bool grab,
bool debug);
void start();
void stop();
void set_rumble(uint8_t left, uint8_t right);
void set_led(uint8_t status);
@ -64,6 +68,16 @@ private:
bool apply(XboxGenericMsg& msg, const struct input_event& ev);
void read_data_to_buffer();
gboolean on_read_data(GIOChannel* source,
GIOCondition condition);
static gboolean on_read_data_wrap(GIOChannel* source,
GIOCondition condition,
gpointer userdata)
{
return static_cast<EvdevController*>(userdata)->on_read_data(source, condition);
}
private:
EvdevController(const EvdevController&);
EvdevController& operator=(const EvdevController&);

View file

@ -94,6 +94,17 @@ public:
} \
} while(false)
/** Write an empty debug level message, thus class and function name
are visible */
#define log_trace() do { \
if (g_logger.get_log_level() >= Logger::kDebug) \
{ \
std::ostringstream x6ac1c382; \
x6ac1c382 << log_pretty_print(__PRETTY_FUNCTION__); \
g_logger.append_unchecked(Logger::kDebug, x6ac1c382.str()); \
} \
} while(false)
extern Logger g_logger;
#endif

View file

@ -20,8 +20,10 @@
#include <boost/format.hpp>
#include "usb_helper.hpp"
#include "log.hpp"
#include "raise_exception.hpp"
#include "usb_helper.hpp"
#include "xboxmsg.hpp"
USBController::USBController(libusb_device* dev) :
m_dev(dev),
@ -98,6 +100,57 @@ USBController::get_name() const
return m_name;
}
void
USBController::usb_submit_read(int endpoint, int len)
{
log_debug("ep: " << endpoint << " len: " << len);
libusb_transfer* transfer = libusb_alloc_transfer(0);
uint8_t* data = static_cast<uint8_t*>(malloc(sizeof(uint8_t) * len));
//FIXME: transfer->flags |= LIBUSB_TRANSFER_FREE_BUFFER;
libusb_fill_interrupt_transfer(transfer, m_handle,
endpoint | LIBUSB_ENDPOINT_IN,
data, len,
&USBController::on_read_data_wrap, this,
0); // timeout
int ret;
ret = libusb_submit_transfer(transfer);
log_debug("libusb_submit_transfer: " << usb_strerror(ret));
}
void
USBController::usb_cancel_read()
{
assert(!"implement me");
}
void
USBController::on_read_data(libusb_transfer *transfer)
{
log_trace();
assert(transfer);
// process data
XboxGenericMsg msg;
if (parse(transfer->buffer, transfer->actual_length, &msg))
{
submit_msg(msg);
}
if (false) // cleanup
{
libusb_free_transfer(transfer);
}
else // resubmit
{
int ret;
ret = libusb_submit_transfer(transfer);
assert(ret == LIBUSB_SUCCESS); // FIXME
}
}
void
USBController::usb_claim_interface(int ifnum, bool try_detach)
{

View file

@ -54,6 +54,15 @@ public:
void usb_write(int endpoint, uint8_t* data, int len);
int usb_find_ep(int direction, uint8_t if_class, uint8_t if_subclass, uint8_t if_protocol);
void usb_submit_read(int endpoint, int len);
void usb_cancel_read();
void on_read_data(libusb_transfer *transfer);
static void on_read_data_wrap(libusb_transfer *transfer)
{
static_cast<USBController*>(transfer->user_data)->on_read_data(transfer);
}
private:
USBController(const USBController&);
USBController& operator=(const USBController&);

155
src/usb_system.cpp Normal file
View file

@ -0,0 +1,155 @@
/*
** Xbox360 USB Gamepad Userspace Driver
** Copyright (C) 2011 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 3 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, see <http://www.gnu.org/licenses/>.
*/
#include "usb_system.hpp"
#include <assert.h>
#include <poll.h>
#include <libusb.h>
#include <stdlib.h>
#include "log.hpp"
// GUSBSource
// documentation at: http://library.gnome.org/devel/glib/2.28/
USBSystem::USBSystem() :
m_source_funcs(),
m_source(),
m_source_id()
{
// create the source functions
m_source_funcs.prepare = &USBSystem::on_source_prepare;
m_source_funcs.check = &USBSystem::on_source_check;
m_source_funcs.dispatch = &USBSystem::on_source_dispatch;
m_source_funcs.finalize = NULL;
m_source_funcs.closure_callback = NULL;
m_source_funcs.closure_marshal = NULL;
// create the source itself
m_source = g_source_new(&m_source_funcs, sizeof(GSource));
g_source_set_callback(m_source,
&USBSystem::on_source_wrap, this,
NULL);
// add pollfds to source
const libusb_pollfd** fds = libusb_get_pollfds(NULL);
for(const libusb_pollfd** i = fds; *i != NULL; ++i)
{
log_debug("adding pollfd: " << (*i)->fd);
GPollFD* gfd = new GPollFD;
gfd->fd = (*i)->fd;
gfd->events = (*i)->events;
gfd->revents = 0;
g_source_add_poll(m_source, gfd);
}
free(fds);
// register pollfd callbacks
libusb_set_pollfd_notifiers(NULL,
&USBSystem::on_usb_pollfd_added_wrap,
&USBSystem::on_usb_pollfd_removed_wrap,
this);
}
USBSystem::~USBSystem()
{
}
void
USBSystem::attach(GMainContext* context)
{
// attach source
m_source_id = g_source_attach(m_source, context);
}
void
USBSystem::on_usb_pollfd_added(int fd, short events)
{
assert(POLLIN == G_IO_IN);
assert(POLLOUT == G_IO_OUT);
log_trace();
GPollFD* gfd = new GPollFD;
gfd->fd = fd;
gfd->events = events;
gfd->revents = 0;
g_source_add_poll(m_source, gfd);
// FIXME: put gfd somewhere, like a map or list
}
void
USBSystem::on_usb_pollfd_removed(int fd)
{
log_trace();
// FIXME: how to get the GPollFD
//g_source_remove_poll(m_source, &gfd);
}
gboolean
USBSystem::on_source_prepare(GSource* source, gint* timeout)
{
log_trace();
struct timeval tv;
int ret = libusb_get_next_timeout(NULL, &tv);
log_debug("libusb_get_next_timeout(): " << ret);
if (ret == LIBUSB_SUCCESS)
{
// no timeout
}
else
{
// convert tv
*timeout = -1;
}
return TRUE;
}
gboolean
USBSystem::on_source_check(GSource* source)
{
log_trace();
return TRUE;
}
gboolean
USBSystem::on_source_dispatch(GSource* source, GSourceFunc callback, gpointer userdata)
{
log_trace();
return callback(userdata);
}
gboolean
USBSystem::on_source()
{
log_trace();
libusb_handle_events(NULL);
return TRUE;
}
/* EOF */

68
src/usb_system.hpp Normal file
View file

@ -0,0 +1,68 @@
/*
** Xbox360 USB Gamepad Userspace Driver
** Copyright (C) 2011 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 3 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, see <http://www.gnu.org/licenses/>.
*/
#ifndef HEADER_XBOXDRV_USB_SYSTEM_HPP
#define HEADER_XBOXDRV_USB_SYSTEM_HPP
#include <glib.h>
class USBSystem
{
private:
GSourceFuncs m_source_funcs;
GSource* m_source;
gint m_source_id;
public:
USBSystem();
~USBSystem();
void attach(GMainContext* context);
private:
gboolean on_source();
// libusb callbacks
void on_usb_pollfd_added(int fd, short events);
void on_usb_pollfd_removed(int fd);
static gboolean on_source_wrap(void* userdata) {
return static_cast<USBSystem*>(userdata)->on_source();
}
static void on_usb_pollfd_added_wrap(int fd, short events, void* userdata) {
static_cast<USBSystem*>(userdata)->on_usb_pollfd_added(fd, events);
}
static void on_usb_pollfd_removed_wrap(int fd, void* userdata) {
static_cast<USBSystem*>(userdata)->on_usb_pollfd_removed(fd);
}
// glib callbacks
static gboolean on_source_prepare(GSource* source, gint* timeout_);
static gboolean on_source_check(GSource* source);
static gboolean on_source_dispatch(GSource* source, GSourceFunc callback, gpointer userdata);
private:
USBSystem(const USBSystem&);
USBSystem& operator=(const USBSystem&);
};
#endif
/* EOF */

View file

@ -78,6 +78,20 @@ Xbox360Controller::~Xbox360Controller()
usb_release_interface(0);
}
void
Xbox360Controller::start()
{
log_trace();
usb_submit_read(endpoint_in, 32);
}
void
Xbox360Controller::stop()
{
log_trace();
usb_cancel_read();
}
void
Xbox360Controller::set_rumble(uint8_t left, uint8_t right)
{
@ -103,6 +117,8 @@ Xbox360Controller::read(XboxGenericMsg& msg, int timeout)
bool
Xbox360Controller::parse(uint8_t* data, int len, XboxGenericMsg* msg_out)
{
log_trace();
if (len == 0)
{
// happens with the Xbox360 controller every now and then, just

View file

@ -50,6 +50,9 @@ public:
bool try_detach);
~Xbox360Controller();
void start();
void stop();
void set_rumble(uint8_t left, uint8_t right);
void set_led(uint8_t status);
bool read(XboxGenericMsg& msg, int timeout);

View file

@ -0,0 +1,75 @@
#include <iostream>
#include <libusb.h>
#include <stdexcept>
#include "helper.hpp"
#include "usb_helper.hpp"
#include "usb_read_thread.hpp"
#include "raise_exception.hpp"
int main(int argc, char** argv)
{
g_logger.set_log_level(Logger::kDebug);
int ret = libusb_init(NULL);
if (ret != LIBUSB_SUCCESS)
{
raise_exception(std::runtime_error, "libusb_init() failed: " << usb_strerror(ret));
}
libusb_device_handle* handle = libusb_open_device_with_vid_pid(NULL, 0x045e, 0x028e);
ret = libusb_claim_interface(handle, 1);
if (ret != LIBUSB_SUCCESS)
{
raise_exception(std::runtime_error, "libusb_claim_interface() failed: " << usb_strerror(ret));
}
log_info("handle ok");
if (!handle)
{
raise_exception(std::runtime_error, "couldn't find device");
}
else
{
log_info("starting usb thread");
unsigned char data[32];
USBReadThread thread(handle, 1, 32);
thread.start_thread();
bool shutdown = false;
int count = 0;
while(!shutdown)
{
count += 1;
int transferred;
std::cout << count << std::endl;
if (thread.read(data, sizeof(data), &transferred, 100) != LIBUSB_ERROR_TIMEOUT)
{
std::cout << count << " " << transferred << " - " << raw2str(data, transferred) << std::endl;
}
if (!shutdown && count > 30)
{
std::cout << "shutdown thread" << std::endl;
shutdown = true;
thread.stop_thread();
std::cout << "thread stopped" << std::endl;
}
//usleep(1000 * 10);
}
}
return 0;
}
/* EOF */

99
test/usb_system_test.cpp Normal file
View file

@ -0,0 +1,99 @@
/*
** Xbox360 USB Gamepad Userspace Driver
** Copyright (C) 2011 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 3 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, see <http://www.gnu.org/licenses/>.
*/
#include <libusb.h>
#include <glib.h>
#include <stdexcept>
#include "log.hpp"
#include "raise_exception.hpp"
#include "usb_helper.hpp"
#include "usb_system.hpp"
#include "xbox360_controller.hpp"
#include "xboxmsg.hpp"
libusb_device* get_controller_dev()
{
libusb_device** list;
ssize_t num_devices = libusb_get_device_list(NULL, &list);
for(ssize_t dev_it = 0; dev_it < num_devices; ++dev_it)
{
libusb_device* dev = list[dev_it];
libusb_device_descriptor desc;
int ret = libusb_get_device_descriptor(dev, &desc);
if (ret != LIBUSB_SUCCESS)
{
log_warn("libusb_get_device_descriptor() failed: " << usb_strerror(ret));
}
else
{
if (desc.idVendor == 0x045e && desc.idProduct == 0x028e)
{
libusb_ref_device(dev);
libusb_free_device_list(list, 1 /* unref_devices */);
return dev;
}
}
}
libusb_free_device_list(list, 1 /* unref_devices */);
return 0;
}
void process_msg(const XboxGenericMsg& msg)
{
log_debug(msg);
}
int main()
{
g_logger.set_log_level(Logger::kDebug);
//g_type_init();
int ret = libusb_init(NULL);
if (ret != LIBUSB_SUCCESS)
{
raise_exception(std::runtime_error, "libusb_init() failed: " << usb_strerror(ret));
}
GMainLoop* m_gmain = g_main_loop_new(NULL, FALSE);
USBSystem usb_system;
usb_system.attach(NULL);
libusb_device* dev = get_controller_dev();
assert(dev);
Xbox360Controller* controller = new Xbox360Controller(dev,
false, false, false,
false,
false,
"",
"",
false);
controller->set_msg_callback(boost::bind(&process_msg, _1));
controller->start();
g_main_loop_run(m_gmain);
return 0;
}
/* EOF */