From a429856889751534647f1dbfc3cfaaaf82acdfce Mon Sep 17 00:00:00 2001
From: Ingo Ruhnke <grumbel@gmx.de>
Date: Sun, 30 May 2010 14:49:32 +0200
Subject: [PATCH] Added -d, --detach-kernel-driver option

---
 NEWS                                |  2 +-
 TODO                                | 27 +++++++++++++++++++++++++--
 VERSION                             |  2 +-
 doc/xboxdrv.1                       |  6 ++++++
 doc/xboxdrv.xml                     | 12 ++++++++++++
 src/command_line_options.cpp        |  6 ++++++
 src/evdev_controller.cpp            |  2 ++
 src/evdev_helper.cpp                | 17 +++++++++++++++++
 src/firestorm_dual_controller.cpp   |  4 ++--
 src/saitek_p2500_controller.cpp     | 10 ++++------
 src/saitek_p2500_controller.hpp     |  2 +-
 src/xbox360_controller.cpp          |  4 ++--
 src/xbox360_wireless_controller.cpp | 13 +++++++------
 src/xbox360_wireless_controller.hpp |  4 ++--
 src/xbox_controller.cpp             |  7 ++++---
 src/xbox_controller.hpp             |  2 +-
 src/xbox_generic_controller.hpp     |  2 ++
 src/xboxdrv.cpp                     |  6 +++---
 18 files changed, 98 insertions(+), 30 deletions(-)

diff --git a/NEWS b/NEWS
index a5f1e86..e92e87b 100644
--- a/NEWS
+++ b/NEWS
@@ -3,7 +3,7 @@ xboxdrv 0.6.0 - (??/Jun/2010)
 
 * support for reading from evdev, this allows the use of regular
   regular PC joysticks with xboxdrv, useful if you need
-  configurability or joy2key like functionality, but don't have a
+  configurability or joy2key-like functionality, but don't have a
   Xbox360 gamepad
 
 
diff --git a/TODO b/TODO
index 9aab300..5925eff 100644
--- a/TODO
+++ b/TODO
@@ -12,8 +12,31 @@ git archive --format=tar --prefix="xboxdrv-linux-${VERSION}/" ${TAG} | bzip2 -c
 
 git push --tags
 
-Stuff to do before 0.5.1 release:
-===============================
+Stuff to do before 0.6.0 release:
+=================================
+
+* need support for numbers, not just names in evdev_helper, need special syntax to get the type:
+
+  KEY_1 is not unique, maybe: KEYNUM_1, ABSNUM_1, ...
+
+* axis-min/max range must be transmitted to AxisEvent (maybe normalize to float)
+
+* match by protocol not, just vendor/product, from xpad.c:
+
+/* Xbox 360 has a vendor-specific class, so we cannot match it with only
+ * USB_INTERFACE_INFO (also specifically refused by USB subsystem), so we
+ * match against vendor id as well. Wired Xbox 360 devices have protocol 1,
+ * wireless controllers have protocol 129. */
+#define XPAD_XBOX360_VENDOR_PROTOCOL(vend,pr) \
+        .match_flags = USB_DEVICE_ID_MATCH_VENDOR | USB_DEVICE_ID_MATCH_INT_INFO, \
+        .idVendor = (vend), \
+        .bInterfaceClass = USB_CLASS_VENDOR_SPEC, \
+        .bInterfaceSubClass = 93, \
+        .bInterfaceProtocol = (pr)
+#define XPAD_XBOX360_VENDOR(vend) \
+        { XPAD_XBOX360_VENDOR_PROTOCOL(vend,1) }, \
+        { XPAD_XBOX360_VENDOR_PROTOCOL(vend,129) }
+
 
 Docu
 ====
diff --git a/VERSION b/VERSION
index 79a2734..09a3acf 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-0.5.0
\ No newline at end of file
+0.6.0
\ No newline at end of file
diff --git a/doc/xboxdrv.1 b/doc/xboxdrv.1
index f75d14a..253c8a4 100644
--- a/doc/xboxdrv.1
+++ b/doc/xboxdrv.1
@@ -102,6 +102,12 @@ a way to launch \fBxboxdrv\fR
 automatically.
 .SS "DEVICE OPTIONS"
 .TP 
+\*(T<\fB\-d\fR\*(T>, \*(T<\fB\-\-detach\-kernel\-driver\fR\*(T>
+Detaches the kernel driver that is currently associated
+with the given device. This is useful when you have the
+xpad module loaded and want to use xboxdrv without
+unloading it.
+.TP 
 \*(T<\fB\-i\fR\*(T>, \*(T<\fB\-\-id\fR\*(T> \fIN\fR
 Use controller with id N (default: 0),
 use \*(T<\fB\-\-list\-controller\fR\*(T> to obtain a list
diff --git a/doc/xboxdrv.xml b/doc/xboxdrv.xml
index 8aad111..aa1c3b4 100644
--- a/doc/xboxdrv.xml
+++ b/doc/xboxdrv.xml
@@ -214,6 +214,18 @@
     <refsect2>
       <title>Device Options</title>
       <variablelist>
+        <varlistentry>
+          <term><option>-d</option>, <option>--detach-kernel-driver</option></term>
+          <listitem>
+            <para>
+              Detaches the kernel driver that is currently associated
+              with the given device. This is useful when you have the
+              xpad module loaded and want to use xboxdrv without
+              unloading it.
+            </para>
+          </listitem>
+        </varlistentry>
+
         <varlistentry>
           <term><option>-i</option>, <option>--id</option> <replaceable class="parameter">N</replaceable></term>
           <listitem>
diff --git a/src/command_line_options.cpp b/src/command_line_options.cpp
index 077f4fe..a7e6588 100644
--- a/src/command_line_options.cpp
+++ b/src/command_line_options.cpp
@@ -81,6 +81,7 @@ enum {
   OPTION_EVDEV,
   OPTION_EVDEV_ABSMAP,
   OPTION_EVDEV_KEYMAP,
+  OPTION_DETACH_KERNEL_DRIVER,
   OPTION_HELP_DEVICES
 };
 
@@ -144,6 +145,7 @@ CommandLineOptions::CommandLineOptions() :
     .add_newline()
 
     .add_text("Device Options: ")
+    .add_option(OPTION_DETACH_KERNEL_DRIVER, 'd', "detach-kernel-driver", "", "Detaches the kernel driver currently associated with the device")
     .add_option(OPTION_ID,           'i', "id",      "N", "use controller with id N (default: 0)")
     .add_option(OPTION_WID,          'w', "wid",     "N", "use wireless controller with wid N (default: 0)")
     .add_option(OPTION_DEVICE_BY_PATH, 0, "device-by-path", "BUS:DEV", "Use device BUS:DEV, do not do any scanning")
@@ -424,6 +426,10 @@ CommandLineOptions::parse_args(int argc, char** argv)
                   boost::bind(&set_ui_button_map, boost::ref(opts.uinput_config.btn_map), _1));
         break;
 
+      case OPTION_DETACH_KERNEL_DRIVER:
+        opts.detach_kernel_driver = true;
+        break;
+
       case OPTION_EVDEV:
         opts.evdev_device = opt.argument;
         break;
diff --git a/src/evdev_controller.cpp b/src/evdev_controller.cpp
index 3252f88..cfb4d25 100644
--- a/src/evdev_controller.cpp
+++ b/src/evdev_controller.cpp
@@ -209,6 +209,8 @@ EvdevController::read(XboxGenericMsg& msg, bool verbose, int timeout)
     }
   }
 
+  usleep(timeout * 1000);
+
   return false;
 }
 
diff --git a/src/evdev_helper.cpp b/src/evdev_helper.cpp
index 0a95271..5d2c44a 100644
--- a/src/evdev_helper.cpp
+++ b/src/evdev_helper.cpp
@@ -23,6 +23,7 @@
 #include <sstream>
 #include <stdexcept>
 #include <map>
+#include <ctype.h>
 
 #include "enum_box.hpp"
 #include "evdev_helper.hpp"
@@ -674,6 +675,18 @@ int get_event_type(const std::string& name)
   }
 }
 
+bool is_number_(const std::string& name)
+{
+  for(std::string::const_iterator i = name.begin(); i != name.end(); ++i)
+  {
+    if (!isdigit(*i))
+    {
+      return false;
+    }
+  }
+  return true;
+}
+
 int str2abs(const std::string& name)
 {
   return evdev_abs_names[name];
@@ -694,6 +707,10 @@ int str2key(const std::string& name)
   {
     return evdev_key_names[name];
   }
+  else if (is_number_(name))
+  {
+    return boost::lexical_cast<int>(name);
+  }
   else
   {
     throw std::runtime_error("str2key: couldn't convert string: '" + name + "'");
diff --git a/src/firestorm_dual_controller.cpp b/src/firestorm_dual_controller.cpp
index 1a1c951..860f0f7 100644
--- a/src/firestorm_dual_controller.cpp
+++ b/src/firestorm_dual_controller.cpp
@@ -101,8 +101,8 @@ FirestormDualController::FirestormDualController(struct usb_device* dev_, bool i
     if (err != 0) 
     {
       std::ostringstream out;
-      out << "Error couldn't claim the USB interface: " << strerror(-err) << std::endl
-          << "Try to run 'rmmod xpad' and start xboxdrv again.";
+      out << "Error couldn't claim the USB interface: " << usb_strerror() << 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());
     }
   }
diff --git a/src/saitek_p2500_controller.cpp b/src/saitek_p2500_controller.cpp
index c362186..fe9a65a 100644
--- a/src/saitek_p2500_controller.cpp
+++ b/src/saitek_p2500_controller.cpp
@@ -25,6 +25,7 @@
 
 #include "helper.hpp"
 #include "saitek_p2500_controller.hpp"
+#include "usb_helper.hpp"
 
 struct SaitekP2500Msg
 {
@@ -58,7 +59,7 @@ struct SaitekP2500Msg
     
 } __attribute__((__packed__));
 
-SaitekP2500Controller::SaitekP2500Controller(struct usb_device* dev_) :
+SaitekP2500Controller::SaitekP2500Controller(struct usb_device* dev_, bool try_detach) :
   dev(dev_),
   handle(),
   left_rumble(-1),
@@ -71,15 +72,12 @@ SaitekP2500Controller::SaitekP2500Controller(struct usb_device* dev_) :
   }
   else
   {
-    // FIXME: Do not always do that unrequested
-    usb_detach_kernel_driver_np(handle, 0);
-
-    int err = usb_claim_interface(handle, 0);
+    int err = usb_claim_n_detach_interface(handle, 0, try_detach);
     if (err != 0) 
     {
       std::ostringstream out;
       out << "Error couldn't claim the USB interface: " << strerror(-err) << std::endl
-          << "Try to run 'rmmod xpad' and start xboxdrv again.";
+          << "Try to run 'rmmod xpad' and then xboxdrv again or start xboxdrv with the option --detach-kernel-driver.";
       throw std::runtime_error(out.str());
     }
   }
diff --git a/src/saitek_p2500_controller.hpp b/src/saitek_p2500_controller.hpp
index 43c87fd..d421bec 100644
--- a/src/saitek_p2500_controller.hpp
+++ b/src/saitek_p2500_controller.hpp
@@ -33,7 +33,7 @@ private:
   int right_rumble;
 
 public:
-  SaitekP2500Controller(struct usb_device* dev);
+  SaitekP2500Controller(struct usb_device* dev, bool try_detach);
   ~SaitekP2500Controller();
 
   void set_rumble(uint8_t left, uint8_t right);
diff --git a/src/xbox360_controller.cpp b/src/xbox360_controller.cpp
index d0a179c..1aef03c 100644
--- a/src/xbox360_controller.cpp
+++ b/src/xbox360_controller.cpp
@@ -56,7 +56,7 @@ Xbox360Controller::Xbox360Controller(struct usb_device* dev_, bool is_guitar_, b
     {
       std::ostringstream out;
       out << "Error set USB configuration: " << usb_strerror() << std::endl
-          << "Try to run 'rmmod xpad' and start xboxdrv again.";
+          << "Try to run 'rmmod xpad' and then xboxdrv again or start xboxdrv with the option --detach-kernel-driver.";
       throw std::runtime_error(out.str());
     }
   }
@@ -73,7 +73,7 @@ Xbox360Controller::Xbox360Controller(struct usb_device* dev_, bool is_guitar_, b
     {
       std::ostringstream out;
       out << " Error couldn't claim the USB interface: " << usb_strerror() << std::endl
-          << "Try to run 'rmmod xpad' and start xboxdrv again.";
+          << "Try to run 'rmmod xpad' and then xboxdrv again or start xboxdrv with the option --detach-kernel-driver.";
       throw std::runtime_error(out.str());
     }
   }
diff --git a/src/xbox360_wireless_controller.cpp b/src/xbox360_wireless_controller.cpp
index 698fa76..7a6a604 100644
--- a/src/xbox360_wireless_controller.cpp
+++ b/src/xbox360_wireless_controller.cpp
@@ -25,13 +25,14 @@
 #include <boost/format.hpp>
 #include <stdexcept>
 
-#include "usb_read_thread.hpp"
 #include "helper.hpp"
-#include "xboxmsg.hpp"
+#include "usb_helper.hpp"
+#include "usb_read_thread.hpp"
 #include "xbox360_wireless_controller.hpp"
+#include "xboxmsg.hpp"
 
-Xbox360WirelessController::Xbox360WirelessController(struct usb_device* dev_,
-                                                     int controller_id) :
+Xbox360WirelessController::Xbox360WirelessController(struct usb_device* dev_, int controller_id, 
+                                                     bool try_detach) :
   dev(dev_),
   handle(),
   endpoint(),
@@ -54,12 +55,12 @@ Xbox360WirelessController::Xbox360WirelessController(struct usb_device* dev_,
   }
   else
   {
-    int err = usb_claim_interface(handle, interface);
+    int err = usb_claim_n_detach_interface(handle, interface, try_detach);
     if (err != 0) 
     {
       std::ostringstream out;
       out << "Error couldn't claim the USB interface: " << strerror(-err) << std::endl
-          << "Try to run 'rmmod xpad' and start xboxdrv again.";
+          << "Try to run 'rmmod xpad' and then xboxdrv again or start xboxdrv with the option --detach-kernel-driver.";
       throw std::runtime_error(out.str());
     }
   }
diff --git a/src/xbox360_wireless_controller.hpp b/src/xbox360_wireless_controller.hpp
index d64c81f..9e467bf 100644
--- a/src/xbox360_wireless_controller.hpp
+++ b/src/xbox360_wireless_controller.hpp
@@ -22,6 +22,7 @@
 #include "xbox_generic_controller.hpp"
 
 class USBReadThread;
+class XboxGenericMsg;
 struct XPadDevice;
 
 /** */
@@ -39,8 +40,7 @@ private:
   std::auto_ptr<USBReadThread> read_thread;
 
 public:
-  Xbox360WirelessController(struct usb_device* dev,
-                            int controller_id);
+  Xbox360WirelessController(struct usb_device* dev, int controller_id, bool try_detach);
   virtual ~Xbox360WirelessController();
 
   void set_rumble(uint8_t left, uint8_t right);
diff --git a/src/xbox_controller.cpp b/src/xbox_controller.cpp
index 8624939..232e14a 100644
--- a/src/xbox_controller.cpp
+++ b/src/xbox_controller.cpp
@@ -22,10 +22,11 @@
 #include <stdexcept>
 #include <string.h>
 
+#include "usb_helper.hpp"
 #include "xboxmsg.hpp"
 #include "xbox_controller.hpp"
 
-XboxController::XboxController(struct usb_device* dev_) :
+XboxController::XboxController(struct usb_device* dev_, bool try_detach) :
   dev(dev_),
   handle(),
   endpoint_in(1),
@@ -40,12 +41,12 @@ XboxController::XboxController(struct usb_device* dev_) :
   else
   {
     // FIXME: bInterfaceNumber shouldn't be hardcoded
-    int err = usb_claim_interface(handle, 0);
+    int err = usb_claim_n_detach_interface(handle, 0, try_detach);
     if (err != 0) 
     {
       std::ostringstream out;
       out << "Error couldn't claim the USB interface: " << strerror(-err) << std::endl
-          << "Try to run 'rmmod xpad' and start xboxdrv again.";
+          << "Try to run 'rmmod xpad' and then xboxdrv again or start xboxdrv with the option --detach-kernel-driver.";
       throw std::runtime_error(out.str());
     }
   }
diff --git a/src/xbox_controller.hpp b/src/xbox_controller.hpp
index fa2b147..e24904a 100644
--- a/src/xbox_controller.hpp
+++ b/src/xbox_controller.hpp
@@ -37,7 +37,7 @@ private:
   void find_endpoints();
 
 public:
-  XboxController(struct usb_device* dev);
+  XboxController(struct usb_device* dev, bool try_detach);
   virtual ~XboxController();
 
   void set_rumble(uint8_t left, uint8_t right);
diff --git a/src/xbox_generic_controller.hpp b/src/xbox_generic_controller.hpp
index 7d7a77a..777f254 100644
--- a/src/xbox_generic_controller.hpp
+++ b/src/xbox_generic_controller.hpp
@@ -21,6 +21,8 @@
 
 #include <stdint.h>
 
+class XboxGenericMsg;
+
 class XboxGenericController
 {
 private:
diff --git a/src/xboxdrv.cpp b/src/xboxdrv.cpp
index 9ad749c..b9e1eeb 100644
--- a/src/xboxdrv.cpp
+++ b/src/xboxdrv.cpp
@@ -500,7 +500,7 @@ Xboxdrv::run_main(const CommandLineOptions& opts)
       {
         case GAMEPAD_XBOX:
         case GAMEPAD_XBOX_MAT:
-          controller = std::auto_ptr<XboxGenericController>(new XboxController(dev));
+          controller = std::auto_ptr<XboxGenericController>(new XboxController(dev, opts.detach_kernel_driver));
           break;
 
         case GAMEPAD_XBOX360_GUITAR:
@@ -512,7 +512,7 @@ Xboxdrv::run_main(const CommandLineOptions& opts)
           break;
 
         case GAMEPAD_XBOX360_WIRELESS:
-          controller = std::auto_ptr<XboxGenericController>(new Xbox360WirelessController(dev, opts.wireless_id));
+          controller = std::auto_ptr<XboxGenericController>(new Xbox360WirelessController(dev, opts.wireless_id, opts.detach_kernel_driver));
           break;
 
         case GAMEPAD_FIRESTORM:
@@ -524,7 +524,7 @@ Xboxdrv::run_main(const CommandLineOptions& opts)
           break;
 
         case GAMEPAD_SAITEK_P2500:
-          controller = std::auto_ptr<XboxGenericController>(new SaitekP2500Controller(dev));
+          controller = std::auto_ptr<XboxGenericController>(new SaitekP2500Controller(dev, opts.detach_kernel_driver));
           break;
 
         default: