Added controller disconnect handling

This commit is contained in:
Ingo Ruhnke 2011-05-20 19:55:16 +02:00
parent f1417511e5
commit b5bbf48d80
7 changed files with 83 additions and 86 deletions

View file

@ -25,6 +25,8 @@
Controller::Controller() :
m_msg_cb(),
m_disconnect_cb(),
m_is_disconnected(false),
m_udev_device()
{
}
@ -51,7 +53,7 @@ Controller::set_udev_device(udev_device* udev_dev)
}
void
Controller::set_message_cb(boost::function<void(const XboxGenericMsg&)> msg_cb)
Controller::set_message_cb(const boost::function<void(const XboxGenericMsg&)>& msg_cb)
{
m_msg_cb = msg_cb;
}
@ -62,4 +64,23 @@ Controller::get_udev_device() const
return m_udev_device;
}
bool
Controller::is_disconnected() const
{
return m_is_disconnected;
}
void
Controller::set_disconnect_cb(const boost::function<void ()>& callback)
{
m_disconnect_cb = callback;
}
void
Controller::send_disconnect()
{
m_is_disconnected = true;
m_disconnect_cb();
}
/* EOF */

View file

@ -30,8 +30,10 @@ struct XboxGenericMsg;
class Controller
{
private:
protected:
boost::function<void (const XboxGenericMsg&)> m_msg_cb;
boost::function<void ()> m_disconnect_cb;
bool m_is_disconnected;
udev_device* m_udev_device;
public:
@ -42,13 +44,17 @@ public:
virtual void set_led(uint8_t status) =0;
virtual bool is_active() const { return true; }
virtual void set_activation_cb(const boost::function<void ()> callback) {}
virtual void set_activation_cb(const boost::function<void ()>& callback) {}
virtual bool is_disconnected() const;
virtual void set_disconnect_cb(const boost::function<void ()>& callback);
virtual void send_disconnect();
virtual std::string get_usbpath() const { return "-1:-1"; }
virtual std::string get_usbid() const { return "-1:-1"; }
virtual std::string get_name() const { return "<not implemented>"; }
void set_message_cb(boost::function<void(const XboxGenericMsg&)> msg_cb);
void set_message_cb(const boost::function<void(const XboxGenericMsg&)>& msg_cb);
void set_udev_device(udev_device* udev_dev);
udev_device* get_udev_device() const;

View file

@ -78,6 +78,7 @@ USBController::USBController(libusb_device* dev) :
USBController::~USBController()
{
log_tmp("~USBController");
libusb_close(m_handle);
}
@ -189,17 +190,21 @@ USBController::on_write_data(libusb_transfer* transfer)
void
USBController::usb_cancel_read()
{
assert(m_read_transfer);
libusb_cancel_transfer(m_read_transfer);
libusb_free_transfer(m_read_transfer);
m_read_transfer = 0;
if (m_read_transfer)
{
libusb_cancel_transfer(m_read_transfer);
libusb_free_transfer(m_read_transfer);
m_read_transfer = 0;
}
}
void
USBController::on_read_data(libusb_transfer *transfer)
USBController::on_read_data(libusb_transfer* transfer)
{
assert(transfer);
// FIXME: check for LIBUSB_TRANSFER_COMPLETED
// process data
XboxGenericMsg msg;
if (parse(transfer->buffer, transfer->actual_length, &msg))
@ -215,11 +220,16 @@ USBController::on_read_data(libusb_transfer *transfer)
{
int ret;
ret = libusb_submit_transfer(transfer);
if (ret != LIBUSB_SUCCESS)
if (ret != LIBUSB_SUCCESS) // could also check for LIBUSB_ERROR_NO_DEVICE
{
log_error("failed to resubmit USB transfer: " << usb_strerror(ret));
assert(m_read_transfer == transfer);
libusb_free_transfer(transfer);
// FIXME: must signal somebody that the controller is no longer usable
m_read_transfer = 0;
send_disconnect();
}
}
}

View file

@ -156,30 +156,6 @@ XboxdrvDaemon::run()
}
}
void
XboxdrvDaemon::cleanup_threads()
{
int count = 0;
for(ControllerSlots::iterator i = m_controller_slots.begin(); i != m_controller_slots.end(); ++i)
{
if ((*i)->is_connected())
{
count += 1;
on_disconnect(*i);
// disconnect slot and put the controller back into the inactive group
m_inactive_controllers.push_back((*i)->disconnect());
}
}
if (count > 0)
{
log_info("cleaned up " << count << " thread(s), free slots: " <<
get_free_slot_count() << "/" << m_controller_slots.size());
}
}
void
XboxdrvDaemon::process_match(struct udev_device* device)
{
@ -291,58 +267,12 @@ bool
XboxdrvDaemon::on_wakeup()
{
log_info("got a wakeup call");
cleanup_threads();
check_thread_status();
on_controller_disconnect();
return false; // remove the registered idle callback
}
void
XboxdrvDaemon::check_thread_status()
{
// check for inactive controller and free the slots
for(ControllerSlots::iterator i = m_controller_slots.begin(); i != m_controller_slots.end(); ++i)
{
// if a slot contains an inactive controller, disconnect it and save
// the controller for later when it might be active again
if ((*i)->get_controller() && !(*i)->get_controller()->is_active())
{
ControllerPtr controller = disconnect(*i);
m_inactive_controllers.push_back(controller);
}
}
// check for activated controller and connect them to a slot
for(Controllers::iterator i = m_inactive_controllers.begin(); i != m_inactive_controllers.end(); ++i)
{
if (!*i)
{
log_error("NULL in m_inactive_controllers, shouldn't happen");
}
else
{
if ((*i)->is_active())
{
ControllerSlotPtr slot = find_free_slot((*i)->get_udev_device());
if (!slot)
{
log_info("couldn't find a free slot for activated controller");
}
else
{
connect(slot, *i);
// successfully connected the controller, so set it to NULL and cleanup later
*i = ControllerPtr();
}
}
}
}
// cleanup inactive controller
m_inactive_controllers.erase(std::remove(m_inactive_controllers.begin(), m_inactive_controllers.end(), ControllerPtr()),
m_inactive_controllers.end());
}
ControllerSlotPtr
XboxdrvDaemon::find_free_slot(udev_device* dev)
{
@ -397,6 +327,8 @@ XboxdrvDaemon::launch_controller_thread(udev_device* udev_dev,
{
ControllerPtr& controller = *i;
controller->set_disconnect_cb(boost::bind(&XboxdrvDaemon::wakeup, this));
// FIXME: Little dirty hack
controller->set_udev_device(udev_dev);
@ -529,6 +461,24 @@ XboxdrvDaemon::on_disconnect(ControllerSlotPtr slot)
}
}
void
XboxdrvDaemon::on_controller_disconnect()
{
// cleanup active controllers in slots
for(ControllerSlots::iterator i = m_controller_slots.begin(); i != m_controller_slots.end(); ++i)
{
if ((*i)->get_controller()->is_disconnected())
{
disconnect(*i); // discard the ControllerPtr
}
}
// cleanup inactive controllers
m_inactive_controllers.erase(std::remove_if(m_inactive_controllers.begin(), m_inactive_controllers.end(),
boost::bind(&Controller::is_disconnected, _1)),
m_inactive_controllers.end());
}
void
XboxdrvDaemon::wakeup()
{

View file

@ -68,14 +68,12 @@ private:
ControllerSlotPtr find_free_slot(udev_device* dev);
void cleanup_threads();
void process_match(struct udev_device* device);
void print_info(struct udev_device* device);
void launch_controller_thread(udev_device* dev,
const XPadDevice& dev_type,
uint8_t busnum, uint8_t devnum);
int get_free_slot_count() const;
void check_thread_status();
void connect(ControllerSlotPtr slot, ControllerPtr controller);
ControllerPtr disconnect(ControllerSlotPtr slot);
@ -83,6 +81,8 @@ private:
void on_connect(ControllerSlotPtr slot);
void on_disconnect(ControllerSlotPtr slot);
void on_controller_disconnect();
void wakeup();
private:

View file

@ -24,6 +24,7 @@
#include <stdexcept>
#include <iostream>
#include <boost/format.hpp>
#include <boost/bind.hpp>
#include "controller_factory.hpp"
#include "evdev_controller.hpp"
@ -132,12 +133,19 @@ XboxdrvMain::init_controller(const ControllerPtr& controller)
}
}
void
XboxdrvMain::on_controller_disconnect()
{
shutdown();
}
void
XboxdrvMain::run()
{
USBSubsystem usb_subsystem;
ControllerPtr controller = create_controller();
controller->set_disconnect_cb(boost::bind(&XboxdrvMain::on_controller_disconnect, this));
std::auto_ptr<MessageProcessor> message_proc;
init_controller(controller);

View file

@ -71,6 +71,8 @@ private:
static void on_sigint(int);
void on_controller_disconnect();
void on_child_watch(GPid pid, gint status);
static void on_child_watch_wrap(GPid pid, gint status, gpointer data) {
static_cast<XboxdrvMain*>(data)->on_child_watch(pid, status);