From c7b7342716fd14b57ab224b576d1ffb19eb5d3de Mon Sep 17 00:00:00 2001 From: Ingo Ruhnke <grumbel@gmx.de> Date: Wed, 26 Jan 2011 04:08:49 +0100 Subject: [PATCH] Added --match support to XboxdrvDaemon --- src/controller_match_rule.cpp | 27 ++++ src/controller_match_rule.hpp | 3 + src/xboxdrv_daemon.cpp | 235 +++++++++++++++++++++------------- src/xboxdrv_daemon.hpp | 31 +++-- 4 files changed, 198 insertions(+), 98 deletions(-) diff --git a/src/controller_match_rule.cpp b/src/controller_match_rule.cpp index aceb45d..aab5e9f 100644 --- a/src/controller_match_rule.cpp +++ b/src/controller_match_rule.cpp @@ -18,6 +18,33 @@ #include "controller_match_rule.hpp" +#include <assert.h> + +bool +ControllerMatchRule::match(int vendor, int product, + int bus, int dev) const +{ + switch(m_type) + { + case kMatchEverything: + return true; + + case kMatchUSBId: + return (vendor == m_vendor && product == m_product); + + case kMatchUSBPath: + return (bus == m_bus && dev == m_dev); + + case kMatchEvdevPath: + assert(!"not implemented"); + return false; + + default: + assert(!"never reached"); + return false; + } +} + ControllerMatchRule ControllerMatchRule::match_usb_id(int vendor, int product) { diff --git a/src/controller_match_rule.hpp b/src/controller_match_rule.hpp index c39278b..5594eeb 100644 --- a/src/controller_match_rule.hpp +++ b/src/controller_match_rule.hpp @@ -48,6 +48,9 @@ public: m_path() {} + bool match(int vendor, int product, + int bus, int dev) const; + static ControllerMatchRule match_usb_id(int vendor, int product); static ControllerMatchRule match_usb_path(int bus, int dev); static ControllerMatchRule match_evdev_path(const std::string& path); diff --git a/src/xboxdrv_daemon.cpp b/src/xboxdrv_daemon.cpp index a5c9625..d1ccd7a 100644 --- a/src/xboxdrv_daemon.cpp +++ b/src/xboxdrv_daemon.cpp @@ -24,6 +24,7 @@ #include "default_message_processor.hpp" #include "dummy_message_processor.hpp" +#include "helper.hpp" #include "log.hpp" #include "raise_exception.hpp" #include "uinput.hpp" @@ -33,6 +34,61 @@ extern bool global_exit_xboxdrv; +namespace { + +bool get_usb_id(udev_device* device, uint16_t* vendor_id, uint16_t* product_id) +{ + const char* vendor_id_str = udev_device_get_property_value(device, "ID_VENDOR_ID"); + if (!vendor_id_str) + { + return false; + } + else + { + *vendor_id = hexstr2int(vendor_id_str); + } + + const char* product_id_str = udev_device_get_property_value(device, "ID_MODEL_ID"); + if (!product_id_str) + { + return false; + } + else + { + *product_id = hexstr2int(product_id_str); + } + + return true; +} + +bool get_usb_path(udev_device* device, int* bus, int* dev) +{ + // busnum:devnum are decimal, not hex + const char* busnum_str = udev_device_get_property_value(device, "BUSNUM"); + if (!busnum_str) + { + return false; + } + else + { + *bus = boost::lexical_cast<int>(busnum_str); + } + + const char* devnum_str = udev_device_get_property_value(device, "DEVNUM"); + if (!devnum_str) + { + return false; + } + else + { + *dev = boost::lexical_cast<int>(devnum_str); + } + + return true; +} + +} // namespace + XboxdrvDaemon::XboxdrvDaemon() : m_udev(0), m_monitor(0), @@ -80,72 +136,57 @@ XboxdrvDaemon::cleanup_threads() } void -XboxdrvDaemon::process_match(const Options& opts, UInput* uinput, struct udev_device* device) +XboxdrvDaemon::process_match(const Options& opts, struct udev_device* device) { if (true) { print_info(device); } - // 1) Match vendor/product against the xpad list - // value = udev_device_get_property_value(device, "ID_VENDOR_ID"); // 045e - // value = udev_device_get_property_value(device, "ID_MODEL_ID"); // 028e - // value = udev_device_get_property_value(device, "ID_REVISION"); // 0110 aka bcd - // PRODUCT = "45e/28e/110" + uint16_t vendor; + uint16_t product; - const char* product_str = udev_device_get_property_value(device, "PRODUCT"); - if (product_str) + if (!get_usb_id(device, &vendor, &product)) { - unsigned int vendor = 0; - unsigned int product = 0; - unsigned int bcd = 0; - if (sscanf(product_str, "%x/%x/%x", &vendor, &product, &bcd) != 3) + log_warning << "couldn't get vendor:product" << std::endl; + } + else + { + XPadDevice dev_type; + if (!find_xpad_device(vendor, product, &dev_type)) { - std::cout << "[XboxdrvDaemon] couldn't parse PRODUCT = " << product_str << std::endl; + log_info << "ignoring " << boost::format("%04x:%04x") % vendor % product + << " not a valid Xboxdrv device" << std::endl; } else - { - if (false) - std::cout << "Product parse: " - << boost::format("%03x/%03x/%03x == %s") % vendor % product % bcd % product_str - << std::endl; - - // FIXME: could do this after we know that vendor/product are good - // 2) Get busnum and devnum - // busnum:devnum are decimal, not hex - const char* busnum_str = udev_device_get_property_value(device, "BUSNUM"); - const char* devnum_str = udev_device_get_property_value(device, "DEVNUM"); - - if (busnum_str && devnum_str) + { + int bus; + int dev; + if (!get_usb_path(device, &bus, &dev)) { - try + log_warning << "couldn't get bus:dev" << std::endl; + } + else + { + ControllerSlot* slot = find_free_slot(vendor, product, bus, dev); + if (!slot) { - XPadDevice dev_type; - if (find_xpad_device(vendor, product, &dev_type)) - { - // 3) Launch thread to handle the device - log_info << "controller detected at " << busnum_str << ":" << devnum_str << std::endl; - - try - { - launch_xboxdrv(uinput, - dev_type, opts, - boost::lexical_cast<int>(busnum_str), - boost::lexical_cast<int>(devnum_str)); - } - catch(const std::exception& err) - { - log_error << "failed to launch XboxdrvThread: " << err.what() << std::endl; - } - } + log_error << "no free controller slot found, controller will be ignored" << std::endl; } - catch(const std::exception& err) + else { - log_error << "child thread lauch failure: " << err.what() << std::endl; + try + { + launch_xboxdrv(dev_type, opts, bus, dev, *slot); + } + catch(const std::exception& err) + { + log_error << "failed to launch XboxdrvThread: " << err.what() << std::endl; + } } } } - } + } } void @@ -180,7 +221,8 @@ XboxdrvDaemon::init_uinput(const Options& opts) controller != opts.controller_slots.end(); ++controller) { log_info << "creating slot: " << slot_count << std::endl; - m_controller_slots.push_back(ControllerSlot(ControllerConfigSet::create(*m_uinput, slot_count, + m_controller_slots.push_back(ControllerSlot(m_controller_slots.size(), + ControllerConfigSet::create(*m_uinput, slot_count, opts.extra_devices, controller->second), controller->second.get_match_rules())); @@ -241,7 +283,7 @@ XboxdrvDaemon::init_udev_monitor(const Options& opts) //std::cout << "Enum: " << path << std::endl; struct udev_device* device = udev_device_new_from_syspath(m_udev, path); - process_match(opts, m_uinput.get(), device); + process_match(opts, device); udev_device_unref(device); } udev_enumerate_unref(enumerate); @@ -307,7 +349,7 @@ XboxdrvDaemon::run_loop(const Options& opts) if (action && strcmp(action, "add") == 0) { - process_match(opts, m_uinput.get(), device); + process_match(opts, device); } } udev_device_unref(device); @@ -390,9 +432,45 @@ XboxdrvDaemon::print_info(struct udev_device* device) std::cout << "\\----------------------------------------------" << std::endl; } +XboxdrvDaemon::ControllerSlot* +XboxdrvDaemon::find_free_slot(uint16_t vendor, uint16_t product, + int bus, int dev) const +{ + // first pass, look for slots where the rules match the given vendor:product, bus:dev + for(ControllerSlots::const_iterator i = m_controller_slots.begin(); i != m_controller_slots.end(); ++i) + { + if (i->thread == 0) + { + // found a free slot, check if the rules match + for(std::vector<ControllerMatchRule>::const_iterator rule = i->rules.begin(); rule != i->rules.end(); ++rule) + { + if (rule->match(vendor, product, bus, dev)) + { + // FIXME: ugly const_cast + return const_cast<ControllerSlot*>(&(*i)); + } + } + } + } + + // second path, look for slots that don't have any rules and thus match everything + for(ControllerSlots::const_iterator i = m_controller_slots.begin(); i != m_controller_slots.end(); ++i) + { + if (i->thread == 0 && i->rules.empty()) + { + // FIXME: ugly const_cast + return const_cast<ControllerSlot*>(&(*i)); + } + } + + // no free slot found + return 0; +} + void -XboxdrvDaemon::launch_xboxdrv(UInput* uinput, const XPadDevice& dev_type, const Options& opts, - uint8_t busnum, uint8_t devnum) +XboxdrvDaemon::launch_xboxdrv(const XPadDevice& dev_type, const Options& opts, + uint8_t busnum, uint8_t devnum, + ControllerSlot& slot) { // FIXME: results must be libusb_unref_device()'ed libusb_device* dev = usb_find_device_by_path(busnum, devnum); @@ -403,45 +481,28 @@ XboxdrvDaemon::launch_xboxdrv(UInput* uinput, const XPadDevice& dev_type, const } else { - ControllerSlots::iterator it = m_controller_slots.end(); - for(ControllerSlots::iterator i = m_controller_slots.begin(); i != m_controller_slots.end(); ++i) + std::auto_ptr<XboxGenericController> controller = XboxControllerFactory::create(dev_type, dev, opts); + + std::auto_ptr<MessageProcessor> message_proc; + if (m_uinput.get()) { - if (i->thread == 0) - { - it = i; - break; - } - } - - if (it == m_controller_slots.end()) - { - log_error << "no free controller slot found, controller will be ignored" << std::endl; + message_proc.reset(new DefaultMessageProcessor(*m_uinput, slot.config, opts)); } else { - std::auto_ptr<XboxGenericController> controller = XboxControllerFactory::create(dev_type, dev, opts); - - std::auto_ptr<MessageProcessor> message_proc; - if (uinput) - { - message_proc.reset(new DefaultMessageProcessor(*uinput, it->config, opts)); - } - else - { - message_proc.reset(new DummyMessageProcessor()); - } - std::auto_ptr<XboxdrvThread> thread(new XboxdrvThread(message_proc, controller, opts)); - thread->start_thread(opts); - it->thread = thread.release(); - - log_info << "launched XboxdrvThread for " << boost::format("%03d:%03d") - % static_cast<int>(busnum) - % static_cast<int>(devnum) - << " in slot " << (it - m_controller_slots.begin()) - << ", free slots: " - << get_free_slot_count() << "/" << m_controller_slots.size() - << std::endl; + message_proc.reset(new DummyMessageProcessor()); } + + std::auto_ptr<XboxdrvThread> thread(new XboxdrvThread(message_proc, controller, opts)); + thread->start_thread(opts); + slot.thread = thread.release(); + + log_info << "launched XboxdrvThread for " << boost::format("%03d:%03d") + % static_cast<int>(busnum) + % static_cast<int>(devnum) + << " in slot " << slot.id << ", free slots: " + << get_free_slot_count() << "/" << m_controller_slots.size() + << std::endl; } } diff --git a/src/xboxdrv_daemon.hpp b/src/xboxdrv_daemon.hpp index cb6bc66..bf4fdd5 100644 --- a/src/xboxdrv_daemon.hpp +++ b/src/xboxdrv_daemon.hpp @@ -39,27 +39,32 @@ private: struct ControllerSlot { + int id; ControllerConfigSetPtr config; - std::vector<ControllerMatchRule> match; + std::vector<ControllerMatchRule> rules; XboxdrvThread* thread; ControllerSlot() : + id(), config(), - match(), + rules(), thread(0) {} - ControllerSlot(ControllerConfigSetPtr config_, - std::vector<ControllerMatchRule> match_, + ControllerSlot(int id_, + ControllerConfigSetPtr config_, + std::vector<ControllerMatchRule> rules_, XboxdrvThread* thread_ = 0) : + id(id_), config(config_), - match(match_), + rules(rules_), thread(thread_) {} ControllerSlot(const ControllerSlot& rhs) : + id(rhs.id), config(rhs.config), - match(rhs.match), + rules(rhs.rules), thread(rhs.thread) {} @@ -67,8 +72,9 @@ private: { if (&rhs != this) { + id = rhs.id; config = rhs.config; - match = rhs.match; + rules = rhs.rules; thread = rhs.thread; } return *this; @@ -94,12 +100,15 @@ private: void run_loop(const Options& opts); + ControllerSlot* find_free_slot(uint16_t vendor, uint16_t product, + int bus, int dev) const; + void cleanup_threads(); - void process_match(const Options& opts, UInput* uinput, struct udev_device* device); + void process_match(const Options& opts, struct udev_device* device); void print_info(struct udev_device* device); - void launch_xboxdrv(UInput* uinput, - const XPadDevice& dev_type, const Options& opts, - uint8_t busnum, uint8_t devnum); + void launch_xboxdrv(const XPadDevice& dev_type, const Options& opts, + uint8_t busnum, uint8_t devnum, + ControllerSlot& slot); int get_free_slot_count() const; private: