diff --git a/src/controller_match_rule.cpp b/src/controller_match_rule.cpp
index cea7a0b..b2076a8 100644
--- a/src/controller_match_rule.cpp
+++ b/src/controller_match_rule.cpp
@@ -22,7 +22,8 @@
 
 bool
 ControllerMatchRule::match(int vendor, int product,
-                           int bus, int dev) const
+                           int bus, int dev,
+                           const char* serial) const
 {
   switch(m_type)
   {
@@ -35,6 +36,9 @@ ControllerMatchRule::match(int vendor, int product,
     case kMatchUSBPath:
       return (bus == m_bus && dev == m_dev);
 
+    case kMatchUSBSerial:
+      return serial && (m_serial == serial);
+
     case kMatchEvdevPath:
       assert(!"not implemented");
       return false;
@@ -50,7 +54,7 @@ ControllerMatchRule::create_usb_id(int vendor, int product)
 {
   ControllerMatchRule rule;
   rule.m_type = kMatchUSBId;
-  rule.m_vendor = vendor;
+  rule.m_vendor  = vendor;
   rule.m_product = product;
   return rule;
 }
@@ -65,6 +69,15 @@ ControllerMatchRule::create_usb_path(int bus, int dev)
   return rule;
 }
 
+ControllerMatchRule
+ControllerMatchRule::create_usb_serial(const std::string& serial)
+{
+  ControllerMatchRule rule;
+  rule.m_type = kMatchUSBSerial;
+  rule.m_serial = serial;
+  return rule;
+}
+
 ControllerMatchRule 
 ControllerMatchRule::create_evdev_path(const std::string& path)
 {
diff --git a/src/controller_match_rule.hpp b/src/controller_match_rule.hpp
index 8fe5829..9e3c720 100644
--- a/src/controller_match_rule.hpp
+++ b/src/controller_match_rule.hpp
@@ -28,6 +28,7 @@ public:
     kMatchEverything,
     kMatchUSBId, 
     kMatchUSBPath, 
+    kMatchUSBSerial,
     kMatchEvdevPath
   } m_type;
 
@@ -39,20 +40,25 @@ public:
 
   std::string m_path;
 
+  std::string m_serial;
+
   ControllerMatchRule() :
     m_type(kMatchEverything),
     m_bus(),
     m_dev(),
     m_vendor(),
     m_product(),
-    m_path()
+    m_path(),
+    m_serial()
   {}
 
   bool match(int vendor, int product,
-             int bus, int dev) const;
+             int bus, int dev,
+             const char* serial) const;
 
   static ControllerMatchRule create_usb_id(int vendor, int product);
   static ControllerMatchRule create_usb_path(int bus, int dev);
+  static ControllerMatchRule create_usb_serial(const std::string& serial);
   static ControllerMatchRule create_evdev_path(const std::string& path);
 };
 
diff --git a/src/options.cpp b/src/options.cpp
index a68b4a1..0e01be1 100644
--- a/src/options.cpp
+++ b/src/options.cpp
@@ -269,6 +269,17 @@ Options::add_match(const std::string& lhs, const std::string& rhs)
       get_controller_slot().add_match_rule(ControllerMatchRule::create_usb_path(bus, dev));
     }
   }
+  else if (lhs == "usbserial")
+  {
+    if (args.size() != 1)
+    {
+      raise_exception(std::runtime_error, "usbserial rule requires SERIAL argument");
+    }
+    else
+    {
+      get_controller_slot().add_match_rule(ControllerMatchRule::create_usb_serial(args[0]));
+    }
+  }
   else if (lhs == "evdev")
   {
     if (args.size() != 1)
diff --git a/src/xboxdrv_daemon.cpp b/src/xboxdrv_daemon.cpp
index 2134e59..fb41612 100644
--- a/src/xboxdrv_daemon.cpp
+++ b/src/xboxdrv_daemon.cpp
@@ -166,7 +166,9 @@ XboxdrvDaemon::process_match(const Options& opts, struct udev_device* device)
       }
       else
       {
-        ControllerSlot* slot = find_free_slot(vendor, product, bus, dev);
+        const char* serial = udev_device_get_property_value(device, "ID_SERIAL_SHORT");
+
+        ControllerSlot* slot = find_free_slot(vendor, product, bus, dev, serial);
         if (!slot)
         {
           log_error("no free controller slot found, controller will be ignored");
@@ -386,8 +388,8 @@ XboxdrvDaemon::print_info(struct udev_device* device)
   //udev_device_get_sysattr_value(device, "busnum");
   //udev_device_get_sysattr_value(device, "devnum");
 
-#if FIXME
-  // only works with newer versions of libudev
+#if 0
+  // FIXME: only works with newer versions of libudev
   {
     log_debug("list: ");
     struct udev_list_entry* it = udev_device_get_tags_list_entry(device);
@@ -430,7 +432,8 @@ XboxdrvDaemon::print_info(struct udev_device* device)
 
 XboxdrvDaemon::ControllerSlot*
 XboxdrvDaemon::find_free_slot(uint16_t vendor, uint16_t product,
-                              int bus, int dev) const
+                              int bus, int dev,
+                              const char* serial) 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)
@@ -440,7 +443,7 @@ XboxdrvDaemon::find_free_slot(uint16_t vendor, uint16_t product,
       // 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))
+        if (rule->match(vendor, product, bus, dev, serial))
         {
           // FIXME: ugly const_cast
           return const_cast<ControllerSlot*>(&(*i));
diff --git a/src/xboxdrv_daemon.hpp b/src/xboxdrv_daemon.hpp
index 568f615..b117764 100644
--- a/src/xboxdrv_daemon.hpp
+++ b/src/xboxdrv_daemon.hpp
@@ -98,7 +98,7 @@ private:
   void run_loop(const Options& opts);
 
   ControllerSlot* find_free_slot(uint16_t vendor, uint16_t product,
-                                 int bus, int dev) const;
+                                 int bus, int dev, const char* serial) const;
 
   void cleanup_threads();
   void process_match(const Options& opts, struct udev_device* device);