Some infrastructure to handle wireless controllers better, not yet working

This commit is contained in:
Ingo Ruhnke 2011-02-02 21:41:33 +01:00
parent b25b055360
commit 14a6fa483c
7 changed files with 272 additions and 144 deletions

View file

@ -20,14 +20,54 @@
#include <boost/format.hpp>
#include "xboxdrv_thread.hpp"
ControllerSlot::ControllerSlot() :
m_id(),
m_config(),
m_rules(),
m_led_status(-1),
m_thread(),
m_busnum(),
m_devnum(),
m_dev_type()
{}
ControllerSlot::ControllerSlot(int id_,
ControllerSlotConfigPtr config_,
std::vector<ControllerMatchRulePtr> rules_,
int led_status_,
XboxdrvThreadPtr thread_) :
m_id(id_),
m_config(config_),
m_rules(rules_),
m_led_status(led_status_),
m_thread(thread_),
m_busnum(),
m_devnum(),
m_dev_type()
{}
void
ControllerSlot::connect(XboxdrvThread* thread,
ControllerSlot::connect(XboxdrvThreadPtr thread)
{
assert(!m_thread);
m_thread = thread;
m_busnum = 0;
m_devnum = 0;
// FIXME: hack this info should be stored in the thread
m_dev_type.type = GAMEPAD_XBOX360;
m_dev_type.idVendor = 0;
m_dev_type.idProduct = 0;
m_dev_type.name = "unset";
}
void
ControllerSlot::connect(XboxdrvThreadPtr thread,
uint8_t busnum, uint8_t devnum,
const XPadDevice& dev_type)
{
assert(m_thread == 0);
assert(!m_thread);
m_thread = thread;
m_busnum = busnum;
@ -35,11 +75,12 @@ ControllerSlot::connect(XboxdrvThread* thread,
m_dev_type = dev_type;
}
void
XboxdrvThreadPtr
ControllerSlot::disconnect()
{
delete m_thread;
m_thread = 0;
XboxdrvThreadPtr thread = m_thread;
m_thread.reset();
return thread;
}
bool

View file

@ -20,10 +20,16 @@
#define HEADER_XBOXDRV_CONTROLLER_SLOT_HPP
#include <vector>
#include <boost/shared_ptr.hpp>
#include <boost/weak_ptr.hpp>
#include "controller_slot_config.hpp"
#include "xboxdrv_thread.hpp"
class XboxdrvThread;
class ControllerSlot;
typedef boost::shared_ptr<ControllerSlot> ControllerSlotPtr;
typedef boost::shared_ptr<ControllerSlot> ControllerSlotWeakPtr;
class ControllerSlot
{
@ -32,71 +38,26 @@ private:
ControllerSlotConfigPtr m_config;
std::vector<ControllerMatchRulePtr> m_rules;
int m_led_status;
XboxdrvThread* m_thread;
XboxdrvThreadPtr m_thread;
uint8_t m_busnum;
uint8_t m_devnum;
XPadDevice m_dev_type;
public:
ControllerSlot() :
m_id(),
m_config(),
m_rules(),
m_led_status(-1),
m_thread(0),
m_busnum(),
m_devnum(),
m_dev_type()
{}
ControllerSlot();
ControllerSlot(int id_,
ControllerSlotConfigPtr config_,
std::vector<ControllerMatchRulePtr> rules_,
int led_status_,
XboxdrvThread* thread_ = 0) :
m_id(id_),
m_config(config_),
m_rules(rules_),
m_led_status(led_status_),
m_thread(thread_),
m_busnum(),
m_devnum(),
m_dev_type()
{}
ControllerSlot(const ControllerSlot& rhs) :
m_id(rhs.m_id),
m_config(rhs.m_config),
m_rules(rhs.m_rules),
m_led_status(rhs.m_led_status),
m_thread(rhs.m_thread),
m_busnum(rhs.m_busnum),
m_devnum(rhs.m_devnum),
m_dev_type(rhs.m_dev_type)
{}
ControllerSlot& operator=(const ControllerSlot& rhs)
{
if (&rhs != this)
{
m_id = rhs.m_id;
m_config = rhs.m_config;
m_rules = rhs.m_rules;
m_led_status = rhs.m_led_status;
m_thread = rhs.m_thread;
m_busnum = rhs.m_busnum;
m_devnum = rhs.m_devnum;
m_dev_type = rhs.m_dev_type;
}
return *this;
}
XboxdrvThreadPtr thread_ = XboxdrvThreadPtr());
bool is_connected() const;
void connect(XboxdrvThread* thread,
void connect(XboxdrvThreadPtr thread,
uint8_t busnum, uint8_t devnum,
const XPadDevice& dev_type);
void disconnect();
void connect(XboxdrvThreadPtr thread);
XboxdrvThreadPtr disconnect();
bool try_disconnect();
const std::vector<ControllerMatchRulePtr>& get_rules() const { return m_rules; }
@ -107,6 +68,8 @@ public:
std::string get_usbpath() const;
std::string get_usbid() const;
std::string get_name() const;
XboxdrvThreadPtr get_thread() const { return m_thread; }
};
#endif

View file

@ -39,6 +39,8 @@ public:
*/
virtual bool read(XboxGenericMsg& msg, int timeout) =0;
virtual bool is_active() const { return true; }
private:
XboxGenericController (const XboxGenericController&);
XboxGenericController& operator= (const XboxGenericController&);

View file

@ -20,11 +20,13 @@
#include <boost/format.hpp>
#include <fstream>
#include <iostream>
#include "uinput_message_processor.hpp"
#include "dummy_message_processor.hpp"
#include "helper.hpp"
#include "raise_exception.hpp"
#include "select.hpp"
#include "uinput.hpp"
#include "usb_helper.hpp"
#include "xbox_controller_factory.hpp"
@ -93,7 +95,9 @@ XboxdrvDaemon::XboxdrvDaemon(const Options& opts) :
m_udev(0),
m_monitor(0),
m_controller_slots(),
m_uinput()
m_inactive_threads(),
m_uinput(),
m_wakeup_pipe()
{
}
@ -101,7 +105,7 @@ XboxdrvDaemon::~XboxdrvDaemon()
{
for(ControllerSlots::iterator i = m_controller_slots.begin(); i != m_controller_slots.end(); ++i)
{
i->disconnect();
(*i)->disconnect();
}
udev_monitor_unref(m_monitor);
@ -115,9 +119,9 @@ XboxdrvDaemon::cleanup_threads()
for(ControllerSlots::iterator i = m_controller_slots.begin(); i != m_controller_slots.end(); ++i)
{
if (i->is_connected())
if ((*i)->is_connected())
{
if (i->try_disconnect())
if ((*i)->try_disconnect())
{
count += 1;
on_disconnect(*i);
@ -165,7 +169,7 @@ XboxdrvDaemon::process_match(const Options& opts, struct udev_device* device)
}
else
{
ControllerSlot* slot = find_free_slot(device);
ControllerSlotPtr slot = find_free_slot(device);
if (!slot)
{
log_error("no free controller slot found, controller will be ignored: "
@ -175,12 +179,14 @@ XboxdrvDaemon::process_match(const Options& opts, struct udev_device* device)
% dev_type.idVendor
% dev_type.idProduct
% dev_type.name);
// m_unused_controllers += 1;
}
else
{
try
{
launch_xboxdrv(dev_type, opts, bus, dev, *slot);
launch_xboxdrv(dev_type, opts, bus, dev, slot);
}
catch(const std::exception& err)
{
@ -217,12 +223,13 @@ XboxdrvDaemon::init_uinput(const Options& opts)
controller != opts.controller_slots.end(); ++controller)
{
log_info("creating slot: " << slot_count);
m_controller_slots.push_back(ControllerSlot(m_controller_slots.size(),
ControllerSlotConfig::create(*m_uinput, slot_count,
opts.extra_devices,
controller->second),
controller->second.get_match_rules(),
controller->second.get_led_status()));
m_controller_slots.push_back(
ControllerSlotPtr(new ControllerSlot(m_controller_slots.size(),
ControllerSlotConfig::create(*m_uinput, slot_count,
opts.extra_devices,
controller->second),
controller->second.get_match_rules(),
controller->second.get_led_status())));
slot_count += 1;
}
@ -257,37 +264,40 @@ XboxdrvDaemon::init_udev_monitor(const Options& opts)
// FIXME: won't we get devices twice that have been plugged in at
// this point? once from the enumeration, once from the monitor
enumerate_udev_devices(opts);
}
void
XboxdrvDaemon::enumerate_udev_devices(const Options& opts)
{
// Enumerate over all devices already connected to the computer
{
struct udev_enumerate* enumerate = udev_enumerate_new(m_udev);
assert(enumerate);
struct udev_enumerate* enumerate = udev_enumerate_new(m_udev);
assert(enumerate);
udev_enumerate_add_match_subsystem(enumerate, "usb");
// not available yet: udev_enumerate_add_match_is_initialized(enumerate);
udev_enumerate_scan_devices(enumerate);
udev_enumerate_add_match_subsystem(enumerate, "usb");
// not available yet: udev_enumerate_add_match_is_initialized(enumerate);
udev_enumerate_scan_devices(enumerate);
struct udev_list_entry* devices;
struct udev_list_entry* dev_list_entry;
struct udev_list_entry* devices;
struct udev_list_entry* dev_list_entry;
devices = udev_enumerate_get_list_entry(enumerate);
udev_list_entry_foreach(dev_list_entry, devices)
devices = udev_enumerate_get_list_entry(enumerate);
udev_list_entry_foreach(dev_list_entry, devices)
{
// name is path, value is NULL
const char* path = udev_list_entry_get_name(dev_list_entry);
struct udev_device* device = udev_device_new_from_syspath(m_udev, path);
// manually filter for devtype, as udev enumerate can't do it by itself
const char* devtype = udev_device_get_devtype(device);
if (devtype && strcmp(devtype, "usb_device") == 0)
{
// name is path, value is NULL
const char* path = udev_list_entry_get_name(dev_list_entry);
struct udev_device* device = udev_device_new_from_syspath(m_udev, path);
// manually filter for devtype, as udev enumerate can't do it by itself
const char* devtype = udev_device_get_devtype(device);
if (devtype && strcmp(devtype, "usb_device") == 0)
{
process_match(opts, device);
}
udev_device_unref(device);
process_match(opts, device);
}
udev_enumerate_unref(enumerate);
}
udev_device_unref(device);
}
udev_enumerate_unref(enumerate);
}
void
@ -327,31 +337,118 @@ XboxdrvDaemon::run(const Options& opts)
}
}
void
XboxdrvDaemon::check_thread_status()
{
// Check for inactive threads and free the slots
for(ControllerSlots::iterator i = m_controller_slots.begin(); i != m_controller_slots.end(); ++i)
{
if (!(*i)->get_thread()->is_active())
{
// FIXME: found a slot with an inactive thread, do something with it:
// 1) disconnect it from slot
// 2) put it somewhere
}
}
// Check for activated threads and connect them to a slot
for(Threads::iterator i = m_inactive_threads.begin(); i != m_inactive_threads.end(); ++i)
{
if ((*i)->is_active())
{
if (!connect_to_slot(*i))
{
log_info("couldn't find a free slot for activated controller");
}
else
{
// successfully connected the thread, so set it to NULL and cleanup later
i->reset();
}
}
}
// Cleanup inactive threads
m_inactive_threads.erase(std::remove(m_inactive_threads.begin(), m_inactive_threads.end(), XboxdrvThreadPtr()),
m_inactive_threads.end());
}
ControllerSlotPtr
XboxdrvDaemon::find_free_slot(XboxdrvThreadPtr thread)
{
log_error("not implemented");
return ControllerSlotPtr();
}
bool
XboxdrvDaemon::connect_to_slot(XboxdrvThreadPtr thread)
{
ControllerSlotPtr slot = find_free_slot(thread);
if (!slot)
{
return false;
}
{
connect(slot, thread);
return true;
}
}
void
XboxdrvDaemon::connect(ControllerSlotPtr slot, XboxdrvThreadPtr thread)
{
slot->connect(thread);
on_connect(slot);
}
XboxdrvThreadPtr
XboxdrvDaemon::disconnect(ControllerSlotPtr slot)
{
XboxdrvThreadPtr thread = slot->disconnect();
on_disconnect(slot);
return thread;
}
void
XboxdrvDaemon::run_loop(const Options& opts)
{
while(!global_exit_xboxdrv)
{
// FIXME: udev_monitor_receive_device() will block, must break out of it somehow
struct udev_device* device = udev_monitor_receive_device(m_monitor);
log_debug("going to wait in select()");
Select sel;
sel.add_fd(udev_monitor_get_fd(m_monitor));
sel.add_fd(m_wakeup_pipe.get_read_fd());
sel.wait();
log_debug("select() is done");
cleanup_threads();
if (!device)
if (sel.is_ready(m_wakeup_pipe.get_read_fd()))
{
// seem to be normal, do we get this when the given device is filtered out?
log_debug("udev device couldn't be read: " << device);
log_debug("got a wakeup call");
check_thread_status();
}
else
if (sel.is_ready(udev_monitor_get_fd(m_monitor)))
{
const char* action = udev_device_get_action(device);
struct udev_device* device = udev_monitor_receive_device(m_monitor);
if (action && strcmp(action, "add") == 0)
cleanup_threads();
if (!device)
{
process_match(opts, device);
// seem to be normal, do we get this when the given device is filtered out?
log_debug("udev device couldn't be read: " << device);
}
else
{
const char* action = udev_device_get_action(device);
udev_device_unref(device);
if (action && strcmp(action, "add") == 0)
{
process_match(opts, device);
}
udev_device_unref(device);
}
}
}
}
@ -435,43 +532,43 @@ XboxdrvDaemon::print_info(struct udev_device* device)
log_debug("\\----------------------------------------------");
}
ControllerSlot*
ControllerSlotPtr
XboxdrvDaemon::find_free_slot(udev_device* dev)
{
// first pass, look for slots where the rules match the given vendor:product, bus:dev
for(ControllerSlots::iterator i = m_controller_slots.begin(); i != m_controller_slots.end(); ++i)
{
if (!i->is_connected())
if (!(*i)->is_connected())
{
// found a free slot, check if the rules match
for(std::vector<ControllerMatchRulePtr>::const_iterator rule = i->get_rules().begin();
rule != i->get_rules().end(); ++rule)
for(std::vector<ControllerMatchRulePtr>::const_iterator rule = (*i)->get_rules().begin();
rule != (*i)->get_rules().end(); ++rule)
{
if ((*rule)->match(dev))
{
return &(*i);
return *i;
}
}
}
}
// second path, look for slots that don't have any rules and thus match everything
// second pass, look for slots that don't have any rules and thus match everything
for(ControllerSlots::iterator i = m_controller_slots.begin(); i != m_controller_slots.end(); ++i)
{
if (!i->is_connected() && i->get_rules().empty())
if (!(*i)->is_connected() && (*i)->get_rules().empty())
{
return &(*i);
return *i;
}
}
// no free slot found
return 0;
return ControllerSlotPtr();
}
void
XboxdrvDaemon::launch_xboxdrv(const XPadDevice& dev_type, const Options& opts,
uint8_t busnum, uint8_t devnum,
ControllerSlot& slot)
ControllerSlotPtr slot)
{
// FIXME: results must be libusb_unref_device()'ed
libusb_device* dev = usb_find_device_by_path(busnum, devnum);
@ -484,35 +581,35 @@ XboxdrvDaemon::launch_xboxdrv(const XPadDevice& dev_type, const Options& opts,
{
std::auto_ptr<XboxGenericController> controller = XboxControllerFactory::create(dev_type, dev, opts);
if (slot.get_led_status() == -1)
if (slot->get_led_status() == -1)
{
controller->set_led(2 + (slot.get_id() % 4));
controller->set_led(2 + (slot->get_id() % 4));
}
else
{
controller->set_led(slot.get_led_status());
controller->set_led(slot->get_led_status());
}
std::auto_ptr<MessageProcessor> message_proc;
if (m_uinput.get())
{
message_proc.reset(new UInputMessageProcessor(*m_uinput, slot.get_config(), opts));
message_proc.reset(new UInputMessageProcessor(*m_uinput, slot->get_config(), opts));
}
else
{
message_proc.reset(new DummyMessageProcessor());
}
std::auto_ptr<XboxdrvThread> thread(new XboxdrvThread(message_proc, controller, opts));
XboxdrvThreadPtr thread(new XboxdrvThread(message_proc, controller, opts));
thread->start_thread(opts);
slot.connect(thread.release(), busnum, devnum, dev_type);
slot->connect(thread, busnum, devnum, dev_type);
on_connect(slot);
log_info("launched XboxdrvThread for " << boost::format("%03d:%03d")
% static_cast<int>(busnum)
% static_cast<int>(devnum)
<< " in slot " << slot.get_id() << ", free slots: "
<< " in slot " << slot->get_id() << ", free slots: "
<< get_free_slot_count() << "/" << m_controller_slots.size());
}
}
@ -524,7 +621,7 @@ XboxdrvDaemon::get_free_slot_count() const
for(ControllerSlots::const_iterator i = m_controller_slots.begin(); i != m_controller_slots.end(); ++i)
{
if (!i->is_connected())
if (!(*i)->is_connected())
{
slot_count += 1;
}
@ -534,12 +631,12 @@ XboxdrvDaemon::get_free_slot_count() const
}
void
XboxdrvDaemon::on_connect(const ControllerSlot& slot)
XboxdrvDaemon::on_connect(ControllerSlotPtr slot)
{
log_info("controller connected: "
<< slot.get_usbpath() << " "
<< slot.get_usbid() << " "
<< "'" << slot.get_name() << "'");
<< slot->get_usbpath() << " "
<< slot->get_usbid() << " "
<< "'" << slot->get_name() << "'");
if (!m_opts.on_connect.empty())
{
@ -547,20 +644,20 @@ XboxdrvDaemon::on_connect(const ControllerSlot& slot)
std::vector<std::string> args;
args.push_back(m_opts.on_connect);
args.push_back(slot.get_usbpath());
args.push_back(slot.get_usbid());
args.push_back(slot.get_name());
args.push_back(slot->get_usbpath());
args.push_back(slot->get_usbid());
args.push_back(slot->get_name());
spawn_exe(args);
}
}
void
XboxdrvDaemon::on_disconnect(const ControllerSlot& slot)
XboxdrvDaemon::on_disconnect(ControllerSlotPtr slot)
{
log_info("controller disconnected: "
<< slot.get_usbpath() << " "
<< slot.get_usbid() << " "
<< "'" << slot.get_name() << "'");
<< slot->get_usbpath() << " "
<< slot->get_usbid() << " "
<< "'" << slot->get_name() << "'");
if (!m_opts.on_disconnect.empty())
{
@ -568,9 +665,9 @@ XboxdrvDaemon::on_disconnect(const ControllerSlot& slot)
std::vector<std::string> args;
args.push_back(m_opts.on_disconnect);
args.push_back(slot.get_usbpath());
args.push_back(slot.get_usbid());
args.push_back(slot.get_name());
args.push_back(slot->get_usbpath());
args.push_back(slot->get_usbid());
args.push_back(slot->get_name());
spawn_exe(args);
}
}

View file

@ -23,6 +23,7 @@
#include "controller_slot_config.hpp"
#include "controller_slot.hpp"
#include "wakeup_pipe.hpp"
class Options;
class UInput;
@ -35,13 +36,17 @@ private:
const Options& m_opts;
struct udev* m_udev;
struct udev_monitor* m_monitor;
typedef std::vector<ControllerSlot> ControllerSlots;
typedef std::vector<ControllerSlotPtr> ControllerSlots;
ControllerSlots m_controller_slots;
typedef std::vector<XboxdrvThreadPtr> Threads;
Threads m_inactive_threads;
std::auto_ptr<UInput> m_uinput;
WakeupPipe m_wakeup_pipe;
public:
XboxdrvDaemon(const Options& opts);
~XboxdrvDaemon();
@ -56,18 +61,26 @@ private:
void run_loop(const Options& opts);
ControllerSlot* find_free_slot(udev_device* dev);
ControllerSlotPtr find_free_slot(udev_device* dev);
ControllerSlotPtr find_free_slot(XboxdrvThreadPtr thread);
void enumerate_udev_devices(const Options& opts);
void cleanup_threads();
void process_match(const Options& opts, struct udev_device* device);
void print_info(struct udev_device* device);
void launch_xboxdrv(const XPadDevice& dev_type, const Options& opts,
uint8_t busnum, uint8_t devnum,
ControllerSlot& slot);
ControllerSlotPtr slot);
int get_free_slot_count() const;
void check_thread_status();
void on_connect(const ControllerSlot& slot);
void on_disconnect(const ControllerSlot& slot);
bool connect_to_slot(XboxdrvThreadPtr thread);
void connect(ControllerSlotPtr slot, XboxdrvThreadPtr thread);
XboxdrvThreadPtr disconnect(ControllerSlotPtr slot);
void on_connect(ControllerSlotPtr slot);
void on_disconnect(ControllerSlotPtr slot);
private:
XboxdrvDaemon(const XboxdrvDaemon&);

View file

@ -178,6 +178,12 @@ XboxdrvThread::controller_loop(const Options& opts)
m_processor->send(msg, 0);
}
}
bool
XboxdrvThread::is_active() const
{
return m_controller->is_active();
}
void
XboxdrvThread::start_thread(const Options& opts)

View file

@ -20,12 +20,16 @@
#define HEADER_XBOXDRV_XBOXDRV_THREAD_HPP
#include <boost/thread.hpp>
#include <boost/shared_ptr.hpp>
#include "controller_slot_config.hpp"
class Options;
class XboxGenericController;
class MessageProcessor;
class XboxdrvThread;
typedef boost::shared_ptr<XboxdrvThread> XboxdrvThreadPtr;
/** XboxdrvThread handles a single XboxGenericController controller
(optionally in a separate thread), reads it messages and passes it
@ -60,6 +64,8 @@ public:
void stop_thread();
bool try_join_thread();
bool is_active() const;
private:
void launch_child_process();
void watch_chid_process();