From d2fe8672137e801a5cee19177d2003595e5d88d8 Mon Sep 17 00:00:00 2001
From: Ingo Ruhnke <grumbel@gmx.de>
Date: Mon, 24 Jan 2011 19:09:26 +0100
Subject: [PATCH] Improvement error messages on exceptions, added
 DummyMessageProcessor, fixed crash on controller unplug

---
 TODO                                |  8 +++-
 src/chatpad.cpp                     |  5 +-
 src/dummy_message_processor.cpp     | 34 ++++++++++++++
 src/dummy_message_processor.hpp     | 38 +++++++++++++++
 src/raise_exception.hpp             | 32 +++++++++++++
 src/ui_event.cpp                    |  2 +
 src/xbox360_controller.cpp          | 24 +++++-----
 src/xbox360_wireless_controller.cpp |  5 +-
 src/xbox_controller.cpp             |  5 +-
 src/xboxdrv.cpp                     | 14 +++---
 src/xboxdrv_daemon.cpp              | 72 ++++++++++++++++++++++-------
 src/xboxdrv_daemon.hpp              |  2 +
 12 files changed, 198 insertions(+), 43 deletions(-)
 create mode 100644 src/dummy_message_processor.cpp
 create mode 100644 src/dummy_message_processor.hpp
 create mode 100644 src/raise_exception.hpp

diff --git a/TODO b/TODO
index ad12488..6c50cce 100644
--- a/TODO
+++ b/TODO
@@ -42,6 +42,9 @@ $ dput my-ppa ../xboxdrv_0.7.0-1~lucid1_source.changes
 Stuff to do before 0.7.0 release:
 =================================
 
+* figure out a good place to set the LEDs off when Xboxdrv is quit,
+  just doing it in the destructor causes trouble
+
 * AxisSensitivity filter has overflow issue 
 
 * fix the FIXME's
@@ -57,8 +60,7 @@ Stuff to do before 0.7.0 release:
 Daemon Related Stuff
 ====================
 
-* currently device creation fails as all configurations are assigned
-  to the same virtual device
+* unplugging a controller should reset it to neutral position
 
 * improve output on which uinput devices are created
 
@@ -122,6 +124,8 @@ List Output
 Stuff to do before 0.7.x release:
 =================================
 
+* add ABS support to macros
+
 * Playstation 3 controller support
   - http://www.pabr.org/sixlinux/sixlinux.en.html
   - http://www.motioninjoy.com/
diff --git a/src/chatpad.cpp b/src/chatpad.cpp
index 9161f42..5425463 100644
--- a/src/chatpad.cpp
+++ b/src/chatpad.cpp
@@ -24,6 +24,7 @@
 #include "linux_uinput.hpp"
 #include "log.hpp"
 #include "usb_helper.hpp"
+#include "raise_exception.hpp"
 
 Chatpad::Chatpad(libusb_device_handle* handle, uint16_t bcdDevice,
                  bool no_init, bool debug) :
@@ -132,7 +133,7 @@ Chatpad::send_ctrl(uint8_t request_type, uint8_t request, uint16_t value, uint16
   int ret = libusb_control_transfer(m_handle, request_type, request, value, index, data, length, 0);
   if (ret != LIBUSB_SUCCESS)
   {
-    throw std::runtime_error("-- failure --"); // FIXME
+    raise_exception(std::runtime_error, "libusb_control_transfer() failed: " << usb_strerror(ret));
   }
 }
 
@@ -208,7 +209,7 @@ Chatpad::read_thread()
                                           data, sizeof(data), &len, 0);
       if (ret != LIBUSB_SUCCESS)
       {
-        throw std::runtime_error("-- failure --"); // FIXME
+        raise_exception(std::runtime_error, "libusb_interrupt_transfer() failed: " << usb_strerror(ret));
       }
 
       if (len < 0)
diff --git a/src/dummy_message_processor.cpp b/src/dummy_message_processor.cpp
new file mode 100644
index 0000000..0072777
--- /dev/null
+++ b/src/dummy_message_processor.cpp
@@ -0,0 +1,34 @@
+/*
+**  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 "dummy_message_processor.hpp"
+
+#include "log.hpp"
+#include "xboxmsg.hpp"
+
+DummyMessageProcessor::DummyMessageProcessor()
+{
+}
+
+void
+DummyMessageProcessor::send(const XboxGenericMsg& msg, int msec_delta)
+{
+  //log_info << msg << std::endl;
+}
+
+/* EOF */
diff --git a/src/dummy_message_processor.hpp b/src/dummy_message_processor.hpp
new file mode 100644
index 0000000..b5ca8a5
--- /dev/null
+++ b/src/dummy_message_processor.hpp
@@ -0,0 +1,38 @@
+/*
+**  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_DUMMY_MESSAGE_PROCESSOR_HPP
+#define HEADER_XBOXDRV_DUMMY_MESSAGE_PROCESSOR_HPP
+
+#include "message_processor.hpp"
+
+class DummyMessageProcessor : public MessageProcessor
+{
+private:
+public:
+  DummyMessageProcessor();
+  void send(const XboxGenericMsg& msg, int msec_delta);
+
+private:
+  DummyMessageProcessor(const DummyMessageProcessor&);
+  DummyMessageProcessor& operator=(const DummyMessageProcessor&);
+};
+
+#endif
+
+/* EOF */
diff --git a/src/raise_exception.hpp b/src/raise_exception.hpp
new file mode 100644
index 0000000..93dc4e3
--- /dev/null
+++ b/src/raise_exception.hpp
@@ -0,0 +1,32 @@
+/*
+**  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_RAISE_EXCEPTION_HPP
+#define HEADER_XBOXDRV_RAISE_EXCEPTION_HPP
+
+#include "log.hpp"
+
+#define raise_exception(type, expr) do {  \
+  std::ostringstream b42465a70169; \
+  b42465a70169 << log_pretty_print(__PRETTY_FUNCTION__) << ": " << expr; \
+  throw type(b42465a70169.str()); \
+} while(false)
+
+#endif
+
+/* EOF */
diff --git a/src/ui_event.cpp b/src/ui_event.cpp
index d99176a..0bff185 100644
--- a/src/ui_event.cpp
+++ b/src/ui_event.cpp
@@ -84,6 +84,8 @@ UIEvent::operator<(const UIEvent& rhs)  const
 void
 UIEvent::resolve_device_id(int slot, bool extra_devices)
 {
+  assert(!m_device_id_resolved);
+
   if (m_slot_id == SLOTID_AUTO)
   {
     m_slot_id = slot;
diff --git a/src/xbox360_controller.cpp b/src/xbox360_controller.cpp
index d2f9deb..00cf1d7 100644
--- a/src/xbox360_controller.cpp
+++ b/src/xbox360_controller.cpp
@@ -24,6 +24,7 @@
 #include "headset.hpp"
 #include "helper.hpp"
 #include "options.hpp"
+#include "raise_exception.hpp"
 #include "usb_helper.hpp"
 
 Xbox360Controller::Xbox360Controller(libusb_device* dev_, 
@@ -55,10 +56,9 @@ Xbox360Controller::Xbox360Controller(libusb_device* dev_,
     int err;
     if ((err = libusb_set_configuration(handle, 0)) < 0)
     {
-      std::ostringstream out;
-      out << "Error set USB configuration: " << usb_strerror(err) << std::endl
-          << "Try to run 'rmmod xpad' and then xboxdrv again or start xboxdrv with the option --detach-kernel-driver.";
-      throw std::runtime_error(out.str());
+      raise_exception(std::runtime_error,
+                      "Error set USB configuration: " << usb_strerror(err) <<
+                      "\nTry to run 'rmmod xpad' and then xboxdrv again or start xboxdrv with the option --detach-kernel-driver.");
     }
   }
 
@@ -83,7 +83,8 @@ Xbox360Controller::Xbox360Controller(libusb_device* dev_,
   {
     libusb_device_descriptor desc;
 
-    if (libusb_get_device_descriptor(dev, &desc) == LIBUSB_SUCCESS)
+    ret = libusb_get_device_descriptor(dev, &desc);
+    if (ret == LIBUSB_SUCCESS)
     {
       m_chatpad.reset(new Chatpad(handle, desc.bcdDevice, chatpad_no_init, chatpad_debug));
       m_chatpad->send_init();
@@ -91,7 +92,7 @@ Xbox360Controller::Xbox360Controller(libusb_device* dev_,
     }
     else
     {
-      throw std::runtime_error("-- failure --"); // FIXME
+      raise_exception(std::runtime_error, "libusb_get_config_descriptor() failed: " << usb_strerror(ret));
     }
   }
 
@@ -103,7 +104,6 @@ Xbox360Controller::Xbox360Controller(libusb_device* dev_,
 
 Xbox360Controller::~Xbox360Controller()
 {
-  set_led(0);
   libusb_release_interface(handle, 0); 
   libusb_close(handle);
 }
@@ -115,7 +115,7 @@ Xbox360Controller::find_endpoints()
   int ret = libusb_get_config_descriptor(dev, 0 /* config_index */, &config);
   if (ret != LIBUSB_SUCCESS)
   {
-    throw std::runtime_error("-- failure --"); // FIXME
+    raise_exception(std::runtime_error, "libusb_get_config_descriptor() failed: " << usb_strerror(ret));
   }
 
   bool debug_print = false;
@@ -171,7 +171,7 @@ Xbox360Controller::set_rumble(uint8_t left, uint8_t right)
                                   &transferred, 0);
   if (ret != LIBUSB_SUCCESS)
   {
-    throw std::runtime_error("-- failure --"); // FIXME
+    raise_exception(std::runtime_error, "libusb_interrupt_transfer() failed: " << usb_strerror(ret));
   }
 }
 
@@ -186,7 +186,7 @@ Xbox360Controller::set_led(uint8_t status)
                                   &transferred, 0);
   if (ret != LIBUSB_SUCCESS)
   {
-    throw std::runtime_error("-- failure --"); // FIXME
+    raise_exception(std::runtime_error, "libusb_interrupt_transfer() failed: " << usb_strerror(ret));
   }
 }
 
@@ -206,9 +206,7 @@ Xbox360Controller::read(XboxGenericMsg& msg, bool verbose, int timeout)
   }
   else if (ret != LIBUSB_SUCCESS)
   { // Error
-    std::ostringstream str;
-    str << "Xbox360Controller: libusb_interrupt_transfer(): " << usb_strerror(ret);
-    throw std::runtime_error(str.str());
+    raise_exception(std::runtime_error, "libusb_interrupt_transfer(): " << usb_strerror(ret));
   }
   else if (len == 0)
   {
diff --git a/src/xbox360_wireless_controller.cpp b/src/xbox360_wireless_controller.cpp
index c11a48f..cb00f9b 100644
--- a/src/xbox360_wireless_controller.cpp
+++ b/src/xbox360_wireless_controller.cpp
@@ -23,6 +23,7 @@
 #include <boost/format.hpp>
 
 #include "helper.hpp"
+#include "raise_exception.hpp"
 #include "usb_helper.hpp"
 #include "xboxmsg.hpp"
 
@@ -77,7 +78,7 @@ Xbox360WirelessController::set_rumble(uint8_t left, uint8_t right)
                                       rumblecmd, sizeof(rumblecmd), &transferred, 0);
   if (ret != LIBUSB_SUCCESS)
   {
-    throw std::runtime_error("-- failure --"); // FIXME
+    raise_exception(std::runtime_error, "libusb_interrupt_transfer() failed: " << usb_strerror(ret));
   }
 }
 
@@ -93,7 +94,7 @@ Xbox360WirelessController::set_led(uint8_t status)
                                 ledcmd, sizeof(ledcmd), &transferred, 0);
   if (ret != LIBUSB_SUCCESS)
   {
-    throw std::runtime_error("-- failure --"); // FIXME:
+    raise_exception(std::runtime_error, "libusb_interrupt_transfer() failed: " << usb_strerror(ret));
   }
 }
 
diff --git a/src/xbox_controller.cpp b/src/xbox_controller.cpp
index 856e0bd..7e0497c 100644
--- a/src/xbox_controller.cpp
+++ b/src/xbox_controller.cpp
@@ -24,6 +24,7 @@
 #include <string.h>
 
 #include "usb_helper.hpp"
+#include "raise_exception.hpp"
 #include "xboxmsg.hpp"
 
 XboxController::XboxController(libusb_device* dev_, bool try_detach) :
@@ -65,7 +66,7 @@ XboxController::find_endpoints()
   int ret = libusb_get_config_descriptor(dev, 0 /* config_index */, &config);
   if (ret != LIBUSB_SUCCESS)
   {
-    throw std::runtime_error("-- failure --"); // FIXME
+    raise_exception(std::runtime_error, "libusb_get_config_descriptor() failed: " << usb_strerror(ret));
   }
 
   bool debug_print = false;
@@ -119,7 +120,7 @@ XboxController::set_rumble(uint8_t left, uint8_t right)
                                       rumblecmd, sizeof(rumblecmd), &transferred, 0);
   if (ret != LIBUSB_SUCCESS)
   {
-    throw std::runtime_error("-- failure -- "); // FIXME
+    raise_exception(std::runtime_error, "libusb_interrupt_transfer() failed: " << usb_strerror(ret));
   }
 }
 
diff --git a/src/xboxdrv.cpp b/src/xboxdrv.cpp
index 9e28fdb..5a8eb5a 100644
--- a/src/xboxdrv.cpp
+++ b/src/xboxdrv.cpp
@@ -27,7 +27,9 @@
 #include "default_message_processor.hpp"
 #include "evdev_controller.hpp"
 #include "helper.hpp"
+#include "raise_exception.hpp"
 #include "word_wrap.hpp"
+#include "usb_helper.hpp"
 #include "xbox_controller_factory.hpp"
 #include "xboxdrv_daemon.hpp"
 #include "xboxdrv_thread.hpp"
@@ -67,7 +69,7 @@ Xboxdrv::run_list_controller()
   int ret = libusb_init(NULL);
   if (ret != LIBUSB_SUCCESS)
   {
-    throw std::runtime_error("-- failure --"); // FIXME
+    raise_exception(std::runtime_error, "libusb_init() failed: " << usb_strerror(ret));
   }
 
   libusb_device** list;
@@ -383,7 +385,7 @@ Xboxdrv::run_main(const Options& opts)
     int ret = libusb_init(NULL);
     if (ret != LIBUSB_SUCCESS)
     {
-      throw std::runtime_error("-- failure --"); // FIXME
+      raise_exception(std::runtime_error, "libusb_init() failed: " << usb_strerror(ret));
     }
     
     // FIXME: this must be libusb_unref_device()'ed, child code must not keep a copy around
@@ -486,10 +488,10 @@ Xboxdrv::print_info(libusb_device* dev,
                     const Options& opts) const
 {
   libusb_device_descriptor desc;
-
-  if (libusb_get_device_descriptor(dev, &desc) != LIBUSB_SUCCESS)
+  int ret = libusb_get_device_descriptor(dev, &desc);
+  if (ret != LIBUSB_SUCCESS)
   {
-    throw std::runtime_error("-- failure --"); // FIXME
+    raise_exception(std::runtime_error, "libusb_get_device_descriptor() failed: " << usb_strerror(ret));
   }
 
   std::cout << "USB Device:        " << boost::format("%03d:%03d:") 
@@ -579,7 +581,7 @@ Xboxdrv::run_daemon(const Options& opts)
   int ret = libusb_init(NULL);
   if (ret != LIBUSB_SUCCESS)
   {
-    throw std::runtime_error("-- failure --"); // FIXME
+    raise_exception(std::runtime_error, "libusb_init() failed: " << usb_strerror(ret));
   }
 
   if (!opts.detach)
diff --git a/src/xboxdrv_daemon.cpp b/src/xboxdrv_daemon.cpp
index 4cba008..e58d3e7 100644
--- a/src/xboxdrv_daemon.cpp
+++ b/src/xboxdrv_daemon.cpp
@@ -22,6 +22,7 @@
 #include <fstream>
 
 #include "default_message_processor.hpp"
+#include "dummy_message_processor.hpp"
 #include "log.hpp"
 #include "uinput.hpp"
 #include "usb_helper.hpp"
@@ -129,10 +130,17 @@ XboxdrvDaemon::process_match(const Options& opts, uInput* uinput, struct udev_de
                      << boost::lexical_cast<int>(devnum_str)
                      << std::endl;
            
-            launch_xboxdrv(uinput,
-                           dev_type, opts,
-                           boost::lexical_cast<int>(busnum_str),
-                           boost::lexical_cast<int>(devnum_str));
+            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;
+            }
           }
         }
         catch(const std::exception& err)
@@ -146,6 +154,19 @@ XboxdrvDaemon::process_match(const Options& opts, uInput* uinput, struct udev_de
 
 void
 XboxdrvDaemon::run(const Options& opts)
+{
+  try 
+  {
+    run_real(opts);
+  }
+  catch(const std::exception& err)
+  {
+    log_error << "fatal exception in XboxdrvDaemon::run(): " << err.what() << std::endl;
+  }
+}
+
+void
+XboxdrvDaemon::run_real(const Options& opts)
 {
   if (!opts.pid_file.empty())
   {
@@ -347,11 +368,6 @@ void
 XboxdrvDaemon::launch_xboxdrv(uInput* uinput, const XPadDevice& dev_type, const Options& opts, 
                               uint8_t busnum, uint8_t devnum)
 {
-  std::cout << "[XboxdrvDaemon] launching " << boost::format("%03d:%03d") 
-    % static_cast<int>(busnum) 
-    % static_cast<int>(devnum)
-            << std::endl;
-
   // FIXME: results must be libusb_unref_device()'ed
   libusb_device* dev = usb_find_device_by_path(busnum, devnum);
 
@@ -361,19 +377,43 @@ 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)
     {
       if (i->thread == 0)
       {
-        log_info << "found a free slot: " << (i - m_controller_slots.begin()) << std::endl;
-
-        std::auto_ptr<XboxGenericController> controller = XboxControllerFactory::create(dev_type, dev, opts);
-        std::auto_ptr<MessageProcessor> message_proc(new DefaultMessageProcessor(*uinput, i->config, opts));
-        std::auto_ptr<XboxdrvThread> thread(new XboxdrvThread(message_proc, controller, opts));
-        thread->start_thread(opts);
-        i->thread = thread.release();
+        it = i;
         break;
+      }      
+    }
+    
+    if (it == m_controller_slots.end())
+    {
+      log_error << "no free controller slot found, controller will be ignored" << std::endl;
+    }
+    else
+    {
+      std::cout << "[XboxdrvDaemon] launching " << boost::format("%03d:%03d")
+        % static_cast<int>(busnum) 
+        % static_cast<int>(devnum)
+                << std::endl;
+
+      log_info << "found a free slot: " << (it - m_controller_slots.begin()) << std::endl;
+
+      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();
     }
   }
 }
diff --git a/src/xboxdrv_daemon.hpp b/src/xboxdrv_daemon.hpp
index d08e0af..6483661 100644
--- a/src/xboxdrv_daemon.hpp
+++ b/src/xboxdrv_daemon.hpp
@@ -78,6 +78,8 @@ public:
   void run(const Options& opts);
 
 private:
+  void run_real(const Options& opts);
+
   void cleanup_threads();
   void process_match(const Options& opts, uInput* uinput, struct udev_device* device);
   void print_info(struct udev_device* device);