diff --git a/TODO b/TODO
index 100285a..ad12488 100644
--- a/TODO
+++ b/TODO
@@ -1,4 +1,4 @@
-Pre Release Testing:
+slPre Release Testing:
 =====================
 
 * check the version number
@@ -44,54 +44,10 @@ Stuff to do before 0.7.0 release:
 
 * AxisSensitivity filter has overflow issue 
 
-* add support for pairing the controller to the PC (or zero)
-
-  --ps3-pair 00:00:00:00:00:00
-  --ps3-pair-with-bluetooth
-
-* Playstation 3 controller support
-  - http://www.pabr.org/sixlinux/sixlinux.en.html
-  - http://www.motioninjoy.com/
-
-  - figure out what the remaining unknown bits mean:
-
-     data from just pluging the controller in and out a few times
-     without much pause inbetween:
-
-     Dualshock3:
-     // leaving controller plugged in for a longer time settles to this:
-      00 00 03 ef 16 00 00 00 00 33 fc 77 01 de
-
-     00 00 03 ef 16 00 00 00 00 33 fc 77 01 de 
-     00 00 03 ef 16 00 00 00 00 33 fc 77 01 c0
-     00 00 02 ee 12 00 00 00 00 12 fc 77 01 de
-           ^^^^^                         ^^^^^
-     00 00 01 ee 12 00 00 00 00 12 fc 77 01 de 
-     00 00 03 ef 16 00 00 00 00 11 fc 77 01 de 
-     00 00 03 ef 16 00 00 00 00 33 fc 77 01 de 
-     00 00 02 ee 12 00 00 00 00 12 fc 77 01 de 
-     wrong ideas: bluetooth master id
-
-     00 00 01 ef 16 00 00 00 00 11 fc 77 01 c0
-     00 00 03 ef 16 00 00 00 00 11 fc 77 01 c0
-
-     SIXAXIS:
-     00 00 06 ee 10 00 00 00 00 06 83 77 01 81 
-     00 00 06 ee 10 00 00 00 00 06 83 77 01
-     00 00 06 ee 10 00 00 00 00 06 83 77 
-
-     // taken from: http://www.pabr.org/sixlinux/sixlinux.en.html
-     00 00 02 ee 10 00 00 00 00 02 b2 77 01 81
-     
-     random guesses: bluetooth id, serial number, calibration data,
-     battery status
-
 * fix the FIXME's
 
 * fix event output (have it pre-modifier or post-modifier?)
 
-* fix --no-uinput
-
 * need to hide/disable the toggle button from the UIButtonmap
 
 * move XBOX_BTN_UNKNOWN behind XBOX_BTN_MAX, so iteration can start
@@ -101,13 +57,20 @@ 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
+
+* improve output on which uinput devices are created
+
+* fix --no-uinput
+
+* uinput must be thread safe
+
 * implement --on-connect and --on-disconnect for the daemon
   - maybe have a more general event interface that allows to run stuff
     on configuration changes, controller plug-ins, etc. (notifiy area as example)
   - also supply useful information as argument
 
-* uinput must be thread safe
-
 * handle multiple controllers in a sane manner (requires cloning of
   modifier maps, also auto increment of "auto" device id's)
 
@@ -155,12 +118,52 @@ List Output
       1     hal daemon from Thomas Debouverie <debouverie_thomas@yahoo.fr>
       1     Implemented --ui-buttonmap A=BTN_A@{device_id} ??!?!?
 
-* improve output on which uinput devices are created
-
 
 Stuff to do before 0.7.x release:
 =================================
 
+* Playstation 3 controller support
+  - http://www.pabr.org/sixlinux/sixlinux.en.html
+  - http://www.motioninjoy.com/
+
+  - figure out what the remaining unknown bits mean:
+
+     data from just pluging the controller in and out a few times
+     without much pause inbetween:
+
+     Dualshock3:
+     // leaving controller plugged in for a longer time settles to this:
+      00 00 03 ef 16 00 00 00 00 33 fc 77 01 de
+
+     00 00 03 ef 16 00 00 00 00 33 fc 77 01 de 
+     00 00 03 ef 16 00 00 00 00 33 fc 77 01 c0
+     00 00 02 ee 12 00 00 00 00 12 fc 77 01 de
+           ^^^^^                         ^^^^^
+     00 00 01 ee 12 00 00 00 00 12 fc 77 01 de 
+     00 00 03 ef 16 00 00 00 00 11 fc 77 01 de 
+     00 00 03 ef 16 00 00 00 00 33 fc 77 01 de 
+     00 00 02 ee 12 00 00 00 00 12 fc 77 01 de 
+     wrong ideas: bluetooth master id
+
+     00 00 01 ef 16 00 00 00 00 11 fc 77 01 c0
+     00 00 03 ef 16 00 00 00 00 11 fc 77 01 c0
+
+     SIXAXIS:
+     00 00 06 ee 10 00 00 00 00 06 83 77 01 81 
+     00 00 06 ee 10 00 00 00 00 06 83 77 01
+     00 00 06 ee 10 00 00 00 00 06 83 77 
+
+     // taken from: http://www.pabr.org/sixlinux/sixlinux.en.html
+     00 00 02 ee 10 00 00 00 00 02 b2 77 01 81
+     
+     random guesses: bluetooth id, serial number, calibration data,
+     battery status
+
+* add support for pairing the controller to the PC (or zero)
+
+  --ps3-pair 00:00:00:00:00:00
+  --ps3-pair-with-bluetooth
+
 * allow named sections in INI files:
 
   [controller1/modifier]
diff --git a/src/controller_config_set.cpp b/src/controller_config_set.cpp
index 2b36ce1..9733cf0 100644
--- a/src/controller_config_set.cpp
+++ b/src/controller_config_set.cpp
@@ -18,6 +18,175 @@
 
 #include "controller_config_set.hpp"
 
+#include "uinput.hpp"
+#include "options.hpp"
+#include "log.hpp"
+
+#include "modifier/dpad_rotation_modifier.hpp"
+#include "modifier/four_way_restrictor_modifier.hpp"
+#include "modifier/square_axis_modifier.hpp"
+
+ControllerConfigSetPtr
+ControllerConfigSet::create(uInput& uinput, const Options::ControllerConfigs& opts)
+{  
+  ControllerConfigSetPtr m_config(new ControllerConfigSet);
+
+  for(Options::ControllerConfigs::const_iterator i = opts.begin();
+      i != opts.end(); ++i)
+  {
+    const ControllerOptions& ctrl_opt = i->second;
+
+    ControllerConfigPtr config(new ControllerConfig(uinput, ctrl_opt));
+    create_modifier(ctrl_opt, &config->get_modifier());
+    m_config->add_config(config);
+
+#ifdef FIXME
+    // introspection of the config
+    std::cout << "==[[ Active Modifier ]]==" << std::endl;
+    for(std::vector<ModifierPtr>::iterator mod = config->get_modifier().begin(); 
+        mod != config->get_modifier().end(); 
+        ++mod)
+    {
+      std::cout << (*mod)->str() << std::endl;
+    }
+#endif
+  }
+  
+  log_info << "UInput finish" << std::endl;
+
+  // After all the ControllerConfig registered their events, finish up
+  // the device creation
+  uinput.finish();
+
+  return m_config;
+}
+
+void
+ControllerConfigSet::create_modifier(const ControllerOptions& opts, std::vector<ModifierPtr>* modifier)
+{
+  if (!opts.calibration_map.empty())
+  {
+    boost::shared_ptr<AxismapModifier> axismap(new AxismapModifier);
+
+    for(std::map<XboxAxis, AxisFilterPtr>::const_iterator i = opts.calibration_map.begin();
+        i != opts.calibration_map.end();
+        ++i)
+    {
+      axismap->add_filter(i->first, i->second); 
+    }
+
+    modifier->push_back(axismap);
+  }
+
+  if (opts.deadzone)
+  {
+    boost::shared_ptr<AxismapModifier> axismap(new AxismapModifier);
+
+    XboxAxis axes[] = { XBOX_AXIS_X1,
+                        XBOX_AXIS_Y1,
+                      
+                        XBOX_AXIS_X2,
+                        XBOX_AXIS_Y2 };
+
+    for(size_t i = 0; i < sizeof(axes)/sizeof(XboxAxis); ++i)
+    {
+      axismap->add_filter(axes[i],
+                          AxisFilterPtr(new DeadzoneAxisFilter(-opts.deadzone,
+                                                               opts.deadzone,
+                                                               true)));
+    }
+
+    modifier->push_back(axismap);
+  }
+
+  if (opts.deadzone_trigger)
+  {
+    boost::shared_ptr<AxismapModifier> axismap(new AxismapModifier);
+
+    XboxAxis axes[] = { XBOX_AXIS_LT,
+                        XBOX_AXIS_RT };
+
+    for(size_t i = 0; i < sizeof(axes)/sizeof(XboxAxis); ++i)
+    {
+      axismap->add_filter(axes[i],
+                          AxisFilterPtr(new DeadzoneAxisFilter(-opts.deadzone_trigger,
+                                                               opts.deadzone_trigger,
+                                                               true)));
+    }
+
+    modifier->push_back(axismap);
+  }
+
+  if (opts.square_axis)
+  {
+    modifier->push_back(ModifierPtr(new SquareAxisModifier(XBOX_AXIS_X1, XBOX_AXIS_Y1)));
+    modifier->push_back(ModifierPtr(new SquareAxisModifier(XBOX_AXIS_X2, XBOX_AXIS_Y2)));
+  }
+
+  if (!opts.sensitivity_map.empty())
+  {
+    boost::shared_ptr<AxismapModifier> axismap(new AxismapModifier);
+
+    for(std::map<XboxAxis, AxisFilterPtr>::const_iterator i = opts.sensitivity_map.begin();
+        i != opts.sensitivity_map.end(); ++i)
+    {
+      axismap->add_filter(i->first, i->second); 
+    }
+
+    modifier->push_back(axismap);
+  }
+
+  if (opts.four_way_restrictor)
+  {
+    modifier->push_back(ModifierPtr(new FourWayRestrictorModifier(XBOX_AXIS_X1, XBOX_AXIS_Y1)));
+    modifier->push_back(ModifierPtr(new FourWayRestrictorModifier(XBOX_AXIS_X2, XBOX_AXIS_Y2)));
+  }
+
+  if (!opts.relative_axis_map.empty())
+  {
+    boost::shared_ptr<AxismapModifier> axismap(new AxismapModifier);
+
+    for(std::map<XboxAxis, AxisFilterPtr>::const_iterator i = opts.relative_axis_map.begin();
+        i != opts.relative_axis_map.end(); ++i)
+    {
+      axismap->add_filter(i->first, i->second); 
+    }
+
+    modifier->push_back(axismap);
+  }
+
+  if (opts.dpad_rotation)
+  {
+    modifier->push_back(ModifierPtr(new DpadRotationModifier(opts.dpad_rotation)));
+  }
+
+  if (!opts.autofire_map.empty())
+  {
+    boost::shared_ptr<ButtonmapModifier> buttonmap(new ButtonmapModifier);
+
+    for(std::map<XboxButton, ButtonFilterPtr>::const_iterator i = opts.autofire_map.begin();
+        i != opts.autofire_map.end(); ++i)
+    {
+      buttonmap->add_filter(i->first, i->second); 
+    }
+
+    modifier->push_back(buttonmap);
+  }
+
+  // axismap, buttonmap comes last, as otherwise they would mess up the button and axis names
+  if (!opts.buttonmap->empty())
+  {
+    modifier->push_back(opts.buttonmap);
+  }
+
+  if (!opts.axismap->empty())
+  {
+    modifier->push_back(opts.axismap);
+  }
+
+  modifier->insert(modifier->end(), opts.modifier.begin(), opts.modifier.end());
+}
+
 ControllerConfigSet::ControllerConfigSet() :
   m_config(),
   m_current_config(0)
diff --git a/src/controller_config_set.hpp b/src/controller_config_set.hpp
index c58a406..96cc11f 100644
--- a/src/controller_config_set.hpp
+++ b/src/controller_config_set.hpp
@@ -19,10 +19,26 @@
 #ifndef HEADER_XBOXDRV_CONTROLLER_CONFIG_SET_HPP
 #define HEADER_XBOXDRV_CONTROLLER_CONFIG_SET_HPP
 
+#include <boost/shared_ptr.hpp>
+
 #include "controller_config.hpp"
+#include "options.hpp"
+
+class Options;
+class uInput;
+class ControllerConfigSet;
+
+typedef boost::shared_ptr<ControllerConfigSet> ControllerConfigSetPtr;
 
 class ControllerConfigSet
 {
+public:
+  /** Creates a ControllerConfigSet from the Options object and connects it to uInput */
+  static ControllerConfigSetPtr create(uInput& uinput, const Options::ControllerConfigs& opts);
+
+private:
+  static void create_modifier(const ControllerOptions& options, std::vector<ModifierPtr>* modifier);
+  
 private:
   std::vector<ControllerConfigPtr> m_config;
   int m_current_config;
diff --git a/src/default_message_processor.cpp b/src/default_message_processor.cpp
index 423b526..2b2e100 100644
--- a/src/default_message_processor.cpp
+++ b/src/default_message_processor.cpp
@@ -22,186 +22,24 @@
 #include "options.hpp"
 #include "uinput.hpp"
 
-#include "modifier/dpad_rotation_modifier.hpp"
-#include "modifier/four_way_restrictor_modifier.hpp"
-#include "modifier/square_axis_modifier.hpp"
-
-DefaultMessageProcessor::DefaultMessageProcessor(uInput& uinput, const Options& opts) :
+DefaultMessageProcessor::DefaultMessageProcessor(uInput& uinput, ControllerConfigSetPtr config, 
+                                                 const Options& opts) :
   m_uinput(uinput),
-  m_config(),
+  m_config(config),
   m_oldmsg(),
   m_config_toggle_button(opts.config_toggle_button)
 {
   memset(&m_oldmsg, 0, sizeof(m_oldmsg));
-
-  // create ControllerConfigs
-  for(Options::ControllerSlots::const_iterator controller = opts.controller_slots.begin(); 
-      controller != opts.controller_slots.end(); ++controller)
-  {
-    for(Options::ControllerConfigs::const_iterator i = controller->second.begin();
-        i != controller->second.end(); ++i)
-    {
-      const ControllerOptions& ctrl_opt = i->second;
-
-      ControllerConfigPtr config(new ControllerConfig(uinput, ctrl_opt));
-      create_modifier(ctrl_opt, &config->get_modifier());
-      m_config.add_config(config);
-
-#ifdef FIXME
-      // introspection of the config
-      std::cout << "==[[ Active Modifier ]]==" << std::endl;
-      for(std::vector<ModifierPtr>::iterator mod = config->get_modifier().begin(); 
-          mod != config->get_modifier().end(); 
-          ++mod)
-      {
-        std::cout << (*mod)->str() << std::endl;
-      }
-#endif
-    }
-  }
-
-
-  log_info << "UInput finish" << std::endl;
-
-  // After all the ControllerConfig registered their events, finish up
-  // the device creation
-  uinput.finish();
 }
 
 DefaultMessageProcessor::~DefaultMessageProcessor()
 {
 }
 
-void
-DefaultMessageProcessor::create_modifier(const ControllerOptions& opts, std::vector<ModifierPtr>* modifier)
-{
-  if (!opts.calibration_map.empty())
-  {
-    boost::shared_ptr<AxismapModifier> axismap(new AxismapModifier);
-
-    for(std::map<XboxAxis, AxisFilterPtr>::const_iterator i = opts.calibration_map.begin();
-        i != opts.calibration_map.end();
-        ++i)
-    {
-      axismap->add_filter(i->first, i->second); 
-    }
-
-    modifier->push_back(axismap);
-  }
-
-  if (opts.deadzone)
-  {
-    boost::shared_ptr<AxismapModifier> axismap(new AxismapModifier);
-
-    XboxAxis axes[] = { XBOX_AXIS_X1,
-                        XBOX_AXIS_Y1,
-                      
-                        XBOX_AXIS_X2,
-                        XBOX_AXIS_Y2 };
-
-    for(size_t i = 0; i < sizeof(axes)/sizeof(XboxAxis); ++i)
-    {
-      axismap->add_filter(axes[i],
-                          AxisFilterPtr(new DeadzoneAxisFilter(-opts.deadzone,
-                                                               opts.deadzone,
-                                                               true)));
-    }
-
-    modifier->push_back(axismap);
-  }
-
-  if (opts.deadzone_trigger)
-  {
-    boost::shared_ptr<AxismapModifier> axismap(new AxismapModifier);
-
-    XboxAxis axes[] = { XBOX_AXIS_LT,
-                        XBOX_AXIS_RT };
-
-    for(size_t i = 0; i < sizeof(axes)/sizeof(XboxAxis); ++i)
-    {
-      axismap->add_filter(axes[i],
-                          AxisFilterPtr(new DeadzoneAxisFilter(-opts.deadzone_trigger,
-                                                               opts.deadzone_trigger,
-                                                               true)));
-    }
-
-    modifier->push_back(axismap);
-  }
-
-  if (opts.square_axis)
-  {
-    modifier->push_back(ModifierPtr(new SquareAxisModifier(XBOX_AXIS_X1, XBOX_AXIS_Y1)));
-    modifier->push_back(ModifierPtr(new SquareAxisModifier(XBOX_AXIS_X2, XBOX_AXIS_Y2)));
-  }
-
-  if (!opts.sensitivity_map.empty())
-  {
-    boost::shared_ptr<AxismapModifier> axismap(new AxismapModifier);
-
-    for(std::map<XboxAxis, AxisFilterPtr>::const_iterator i = opts.sensitivity_map.begin();
-        i != opts.sensitivity_map.end(); ++i)
-    {
-      axismap->add_filter(i->first, i->second); 
-    }
-
-    modifier->push_back(axismap);
-  }
-
-  if (opts.four_way_restrictor)
-  {
-    modifier->push_back(ModifierPtr(new FourWayRestrictorModifier(XBOX_AXIS_X1, XBOX_AXIS_Y1)));
-    modifier->push_back(ModifierPtr(new FourWayRestrictorModifier(XBOX_AXIS_X2, XBOX_AXIS_Y2)));
-  }
-
-  if (!opts.relative_axis_map.empty())
-  {
-    boost::shared_ptr<AxismapModifier> axismap(new AxismapModifier);
-
-    for(std::map<XboxAxis, AxisFilterPtr>::const_iterator i = opts.relative_axis_map.begin();
-        i != opts.relative_axis_map.end(); ++i)
-    {
-      axismap->add_filter(i->first, i->second); 
-    }
-
-    modifier->push_back(axismap);
-  }
-
-  if (opts.dpad_rotation)
-  {
-    modifier->push_back(ModifierPtr(new DpadRotationModifier(opts.dpad_rotation)));
-  }
-
-  if (!opts.autofire_map.empty())
-  {
-    boost::shared_ptr<ButtonmapModifier> buttonmap(new ButtonmapModifier);
-
-    for(std::map<XboxButton, ButtonFilterPtr>::const_iterator i = opts.autofire_map.begin();
-        i != opts.autofire_map.end(); ++i)
-    {
-      buttonmap->add_filter(i->first, i->second); 
-    }
-
-    modifier->push_back(buttonmap);
-  }
-
-  // axismap, buttonmap comes last, as otherwise they would mess up the button and axis names
-  if (!opts.buttonmap->empty())
-  {
-    modifier->push_back(opts.buttonmap);
-  }
-
-  if (!opts.axismap->empty())
-  {
-    modifier->push_back(opts.axismap);
-  }
-
-  modifier->insert(modifier->end(), opts.modifier.begin(), opts.modifier.end());
-}
-
 void
 DefaultMessageProcessor::send(const XboxGenericMsg& msg_in, int msec_delta)
 {
-  if (!m_config.empty())
+  if (!m_config->empty())
   {
     XboxGenericMsg msg = msg_in; 
 
@@ -214,24 +52,24 @@ DefaultMessageProcessor::send(const XboxGenericMsg& msg_in, int msec_delta)
       if (cur && cur != last)
       {
         // reset old mapping to zero to not get stuck keys/axis
-        m_config.get_config()->get_uinput().reset_all_outputs();
+        m_config->get_config()->get_uinput().reset_all_outputs();
 
         // switch to the next input mapping
-        m_config.next_config();
+        m_config->next_config();
 
         log_info << "Next Config" << std::endl;
       }
     }
 
     // run the controller message through all modifier
-    for(std::vector<ModifierPtr>::iterator i = m_config.get_config()->get_modifier().begin();
-        i != m_config.get_config()->get_modifier().end(); 
+    for(std::vector<ModifierPtr>::iterator i = m_config->get_config()->get_modifier().begin();
+        i != m_config->get_config()->get_modifier().end(); 
         ++i)
     {
       (*i)->update(msec_delta, msg);
     }
 
-    m_config.get_config()->get_uinput().update(msec_delta);
+    m_config->get_config()->get_uinput().update(msec_delta);
 
     // send current Xbox state to uinput
     if (memcmp(&msg, &m_oldmsg, sizeof(XboxGenericMsg)) != 0)
@@ -242,7 +80,7 @@ DefaultMessageProcessor::send(const XboxGenericMsg& msg_in, int msec_delta)
       // too
       m_oldmsg = msg;
 
-      m_config.get_config()->get_uinput().send(msg);
+      m_config->get_config()->get_uinput().send(msg);
     }
   }
 }
diff --git a/src/default_message_processor.hpp b/src/default_message_processor.hpp
index 7418b35..2decadb 100644
--- a/src/default_message_processor.hpp
+++ b/src/default_message_processor.hpp
@@ -32,20 +32,18 @@ class DefaultMessageProcessor : public MessageProcessor
 {
 private:
   uInput& m_uinput;
-  ControllerConfigSet m_config;
+  ControllerConfigSetPtr m_config;
 
   XboxGenericMsg m_oldmsg; /// last data send to uinput
   XboxButton m_config_toggle_button;
 
 public:
-  DefaultMessageProcessor(uInput& uinput, const Options& opts);
+  DefaultMessageProcessor(uInput& uinput, ControllerConfigSetPtr config,
+                          const Options& opts);
   ~DefaultMessageProcessor();
 
   void send(const XboxGenericMsg& msg, int msec_delta);
 
-private:
-  void create_modifier(const ControllerOptions& options, std::vector<ModifierPtr>* modifier);
-
 private:
   DefaultMessageProcessor(const DefaultMessageProcessor&);
   DefaultMessageProcessor& operator=(const DefaultMessageProcessor&);
diff --git a/src/linux_uinput.cpp b/src/linux_uinput.cpp
index acce93f..0a4f342 100644
--- a/src/linux_uinput.cpp
+++ b/src/linux_uinput.cpp
@@ -24,6 +24,8 @@
 #include <errno.h>
 #include <fcntl.h>
 
+#include "evdev_helper.hpp"
+#include "log.hpp"
 #include "force_feedback_handler.hpp"
 
 LinuxUinput::LinuxUinput(DeviceType device_type, const std::string& name_, uint16_t vendor_, uint16_t product_) :
@@ -42,6 +44,8 @@ LinuxUinput::LinuxUinput(DeviceType device_type, const std::string& name_, uint1
   ff_callback(),
   needs_sync(true)
 {
+  log_info << name << " " << vendor << ":" << product << std::endl;
+
   std::fill_n(abs_lst, ABS_CNT, false);
   std::fill_n(rel_lst, REL_CNT, false);
   std::fill_n(key_lst, KEY_CNT, false);
@@ -93,7 +97,7 @@ LinuxUinput::~LinuxUinput()
 void
 LinuxUinput::add_abs(uint16_t code, int min, int max, int fuzz, int flat)
 {
-  // std::cout << "add_abs: " << abs2str(code) << " (" << min << ", " << max << ") " << name << std::endl;
+  log_info << "add_abs: " << abs2str(code) << " (" << min << ", " << max << ") " << name << std::endl;
 
   if (!abs_lst[code])
   {
@@ -117,7 +121,7 @@ LinuxUinput::add_abs(uint16_t code, int min, int max, int fuzz, int flat)
 void
 LinuxUinput::add_rel(uint16_t code)
 {
-  // std::cout << "add_rel: " << rel2str(code) << " " << name << std::endl;
+  log_info << "add_rel: " << rel2str(code) << " " << name << std::endl;
 
   if (!rel_lst[code])
   {
@@ -136,7 +140,7 @@ LinuxUinput::add_rel(uint16_t code)
 void
 LinuxUinput::add_key(uint16_t code)
 {
-  // std::cout << "add_key: " << btn2str(code) << " " << name << std::endl;
+  log_info << "add_key: " << key2str(code) << " " << name << std::endl;
 
   if (!key_lst[code])
   {
@@ -220,14 +224,26 @@ LinuxUinput::finish()
   user_dev.id.vendor  = vendor;
   user_dev.id.product = product;
 
+  log_info << "'" << user_dev.name << "' " << user_dev.id.vendor << ":" << user_dev.id.product << std::endl;
+
   if (ff_bit)
     user_dev.ff_effects_max = ff_handler->get_max_effects();
 
   //std::cout << "Finalizing uinput: '" << user_dev.name << "'" << std::endl;
 
-  if (write(fd, &user_dev, sizeof(user_dev)) < 0)
-    throw std::runtime_error("uinput:finish: " + name + ": " + strerror(errno));
+  {
+    int write_ret = write(fd, &user_dev, sizeof(user_dev));
+    if (write_ret < 0)
+    {
+      throw std::runtime_error("uinput:finish: " + name + ": " + strerror(errno));
+    }
+    else
+    {
+      log_info << "write return value: " << write_ret << std::endl; 
+    }
+  }
 
+  log_info << "finish" << std::endl;
   if (ioctl(fd, UI_DEV_CREATE))
   {
     std::ostringstream out;
diff --git a/src/options.cpp b/src/options.cpp
index a755acc..1655820 100644
--- a/src/options.cpp
+++ b/src/options.cpp
@@ -92,6 +92,20 @@ Options::get_controller_slot()
   return controller_slots[controller_slot];
 }
 
+const Options::ControllerConfigs& 
+Options::get_controller_slot() const
+{
+  ControllerSlots::const_iterator it = controller_slots.find(controller_slot);
+  if (it == controller_slots.end())
+  {
+    assert(!"shouldn't happen");
+  }
+  else
+  {
+    return it->second;
+  }  
+}
+
 ControllerOptions&
 Options::get_controller_options()
 {
diff --git a/src/options.hpp b/src/options.hpp
index 7f40c03..f6221b8 100644
--- a/src/options.hpp
+++ b/src/options.hpp
@@ -146,6 +146,7 @@ public:
   Options();
 
   ControllerConfigs& get_controller_slot();
+  const ControllerConfigs& get_controller_slot() const;
   
   /** Returns the currently active configuration */
   ControllerOptions& get_controller_options();
diff --git a/src/xbox360_controller.cpp b/src/xbox360_controller.cpp
index a96a3bf..d2f9deb 100644
--- a/src/xbox360_controller.cpp
+++ b/src/xbox360_controller.cpp
@@ -42,7 +42,7 @@ Xbox360Controller::Xbox360Controller(libusb_device* dev_,
   m_headset()
 {
   find_endpoints();
-  if (true) // FIXME
+  if (false)
   {
     std::cout << "EP(IN):  " << endpoint_in << std::endl;
     std::cout << "EP(OUT): " << endpoint_out << std::endl;
@@ -207,7 +207,7 @@ Xbox360Controller::read(XboxGenericMsg& msg, bool verbose, int timeout)
   else if (ret != LIBUSB_SUCCESS)
   { // Error
     std::ostringstream str;
-    str << "Xbox360Controller: USBError: " << ret << "\n" << usb_strerror(ret);
+    str << "Xbox360Controller: libusb_interrupt_transfer(): " << usb_strerror(ret);
     throw std::runtime_error(str.str());
   }
   else if (len == 0)
diff --git a/src/xboxdrv.cpp b/src/xboxdrv.cpp
index b550221..81d83b3 100644
--- a/src/xboxdrv.cpp
+++ b/src/xboxdrv.cpp
@@ -463,7 +463,8 @@ Xboxdrv::run_main(const Options& opts)
 
     global_exit_xboxdrv = false;
 
-    std::auto_ptr<MessageProcessor> message_proc(new DefaultMessageProcessor(*uinput, opts));
+    ControllerConfigSetPtr config_set = ControllerConfigSet::create(*uinput, opts.get_controller_slot());
+    std::auto_ptr<MessageProcessor> message_proc(new DefaultMessageProcessor(*uinput, config_set, opts));
     XboxdrvThread loop(message_proc, controller, opts);
     loop.controller_loop(opts);
           
diff --git a/src/xboxdrv_daemon.cpp b/src/xboxdrv_daemon.cpp
index 341587a..052ee5c 100644
--- a/src/xboxdrv_daemon.cpp
+++ b/src/xboxdrv_daemon.cpp
@@ -33,7 +33,7 @@ extern bool global_exit_xboxdrv;
 XboxdrvDaemon::XboxdrvDaemon() :
   m_udev(0),
   m_monitor(0),
-  m_threads()
+  m_controller_slots()
 {
   m_udev = udev_new();
     
@@ -49,9 +49,10 @@ XboxdrvDaemon::XboxdrvDaemon() :
 
 XboxdrvDaemon::~XboxdrvDaemon()
 {
-  for(Threads::iterator i = m_threads.begin(); i != m_threads.end(); ++i)
+  for(ControllerSlots::iterator i = m_controller_slots.begin(); i != m_controller_slots.end(); ++i)
   {
-    delete *i;
+    delete i->thread;
+    i->thread = 0;
   }
 
   udev_monitor_unref(m_monitor);
@@ -61,13 +62,20 @@ XboxdrvDaemon::~XboxdrvDaemon()
 void
 XboxdrvDaemon::cleanup_threads()
 {
-  size_t num_threads = m_threads.size();
-  m_threads.erase(std::remove_if(m_threads.begin(), m_threads.end(), 
-                                 boost::bind(&XboxdrvThread::try_join_thread, _1)),
-                  m_threads.end());
-  if (num_threads != m_threads.size())
+  int count = 0;
+
+  for(ControllerSlots::iterator i = m_controller_slots.begin(); i != m_controller_slots.end(); ++i)
   {
-    log_info << "cleaned up " << (num_threads - m_threads.size()) << " thread(s)" << std::endl;
+    if (i->thread)
+    {
+      i->thread->try_join_thread();
+      count += 1;
+    }
+  }
+
+  if (count > 0)
+  {
+    log_info << "cleaned up " << count << " thread(s)" << std::endl;
   }
 }
 
@@ -173,6 +181,20 @@ XboxdrvDaemon::run(const Options& opts)
       std::cout << "Starting without uinput" << std::endl;
   }
 
+  { // create controller slots
+    int slot_count = 0;
+
+    for(Options::ControllerSlots::const_iterator controller = opts.controller_slots.begin(); 
+        controller != opts.controller_slots.end(); ++controller)
+    {
+      log_info << "creating slot: " << slot_count << std::endl;
+      m_controller_slots.push_back(ControllerSlot(ControllerConfigSet::create(*uinput, controller->second)));
+      slot_count += 1;
+    }
+
+    log_info << "created " << m_controller_slots.size() << " controller slots" << std::endl;
+  }
+
   // Setup udev monitor and enumerate
   m_monitor = udev_monitor_new_from_netlink(m_udev, "udev");
   udev_monitor_filter_add_match_subsystem_devtype(m_monitor, "usb", "usb_device");
@@ -312,7 +334,8 @@ XboxdrvDaemon::print_info(struct udev_device* device)
 }
 
 void
-XboxdrvDaemon::launch_xboxdrv(uInput* uinput, const XPadDevice& dev_type, const Options& opts, uint8_t busnum, uint8_t devnum)
+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) 
@@ -328,11 +351,20 @@ XboxdrvDaemon::launch_xboxdrv(uInput* uinput, const XPadDevice& dev_type, const
   }
   else
   {
-    std::auto_ptr<XboxGenericController> controller = XboxControllerFactory::create(dev_type, dev, opts);
-    std::auto_ptr<MessageProcessor> message_proc(new DefaultMessageProcessor(*uinput, opts));
-    std::auto_ptr<XboxdrvThread> thread(new XboxdrvThread(message_proc, controller, opts));
-    thread->start_thread(opts);
-    m_threads.push_back(thread.release());
+    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();
+        break;
+      }
+    }
   }
 }
 
diff --git a/src/xboxdrv_daemon.hpp b/src/xboxdrv_daemon.hpp
index 04a3c5f..d08e0af 100644
--- a/src/xboxdrv_daemon.hpp
+++ b/src/xboxdrv_daemon.hpp
@@ -23,6 +23,8 @@
 #include <stdint.h>
 #include <vector>
 
+#include "controller_config_set.hpp"
+
 class Options;
 class uInput;
 struct XPadDevice;
@@ -33,8 +35,41 @@ class XboxdrvDaemon
 private:
   struct udev* m_udev;
   struct udev_monitor* m_monitor;
-  typedef std::vector<XboxdrvThread*> Threads;
-  Threads m_threads;
+
+  struct ControllerSlot
+  {
+    ControllerConfigSetPtr config;
+    XboxdrvThread* thread;
+    
+    ControllerSlot() :
+      config(),
+      thread(0)
+    {}
+
+    ControllerSlot(ControllerConfigSetPtr config_,
+                   XboxdrvThread* thread_ = 0) :
+      config(config_),
+      thread(thread_)
+    {}
+
+    ControllerSlot(const ControllerSlot& rhs) :
+      config(rhs.config),
+      thread(rhs.thread)
+    {}
+
+    ControllerSlot& operator=(const ControllerSlot& rhs)
+    {
+      if (&rhs != this)
+      {
+        config = rhs.config;
+        thread = rhs.thread;
+      }
+      return *this;
+    }
+  };
+  
+  typedef std::vector<ControllerSlot> ControllerSlots;
+  ControllerSlots m_controller_slots;
 
 public:
   XboxdrvDaemon();
diff --git a/src/xboxdrv_thread.hpp b/src/xboxdrv_thread.hpp
index 2cae2f1..34d450e 100644
--- a/src/xboxdrv_thread.hpp
+++ b/src/xboxdrv_thread.hpp
@@ -30,6 +30,9 @@ class Options;
 class XboxGenericController;
 class MessageProcessor;
 
+/** XboxdrvThread handles a single XboxGenericController controller
+    (optionally in a separate thread), reads it messages and passes it
+    to the MessageProcessor */
 class XboxdrvThread // FIXME: find a better name, XboxdrvControllerLoop?!
 {
 private: