diff --git a/include/sane/sanei_usb.h b/include/sane/sanei_usb.h
index 3115b3daf..d43013856 100644
--- a/include/sane/sanei_usb.h
+++ b/include/sane/sanei_usb.h
@@ -1,5 +1,5 @@
 /* sane - Scanner Access Now Easy.
-   Copyright (C) 2001 Henning Meier-Geinitz
+   Copyright (C) 2001, 2002 Henning Meier-Geinitz
    This file is part of the SANE package.
 
    SANE is free software; you can redistribute it and/or modify it
@@ -20,10 +20,12 @@
 /** @file sanei_usb.h
  * This file provides a generic USB interface.  
  *
- * Currently, only access to device files as provided by the Linux USB
- * scanner driver is supported. FreeBSD and OpenBSD with their uscanner 
- * drivers also work. However, detection and control messages aren't
- * supported on these platforms.
+ * Currently, two access methods to USB devices are provided:
+ * - Access to device
+ * files as used by the Linux kernel USB scanner driver is supported. FreeBSD
+ * and OpenBSD with their uscanner drivers also work this way. However, 
+ * detection and control messages aren't supported on these platforms.
+ * - Access using libusb (where available). 
  *
  * @sa sanei_lm983x.h, sanei_pa4s2.h, sanei_pio.h, sanei_scsi.h, and man sane-usb(5)
@@ -36,12 +38,90 @@
 #include "../include/sane/config.h"
 #include "../include/sane/sane.h"
 
+/* USB spec defines */
+#ifndef USB_CLASS_PER_INTERFACE
+/* Also defined in libusb */
+/* Device and/or interface class codes */
+#define USB_CLASS_PER_INTERFACE         0x00
+#define USB_CLASS_AUDIO                 0x01
+#define USB_CLASS_COMM                  0x02
+#define USB_CLASS_HID                   0x03
+#define USB_CLASS_PRINTER               0x07
+#define USB_CLASS_MASS_STORAGE          0x08
+#define USB_CLASS_HUB                   0x09
+#define USB_CLASS_DATA                  0x0a
+#define USB_CLASS_VENDOR_SPEC           0xff
+
+/* USB descriptor types */
+#define USB_DT_DEVICE                   0x01
+#define USB_DT_CONFIG                   0x02
+#define USB_DT_STRING                   0x03
+#define USB_DT_INTERFACE                0x04
+#define USB_DT_ENDPOINT                 0x05
+
+#define USB_DT_HID                      0x21
+#define USB_DT_REPORT                   0x22
+#define USB_DT_PHYSICAL                 0x23
+#define USB_DT_HUB                      0x29
+
+/* Descriptor sizes per descriptor type */
+#define USB_DT_DEVICE_SIZE              18
+#define USB_DT_CONFIG_SIZE              9
+#define USB_DT_INTERFACE_SIZE           9
+#define USB_DT_ENDPOINT_SIZE            7
+#define USB_DT_ENDPOINT_AUDIO_SIZE      9
+#define USB_DT_HUB_NONVAR_SIZE          7
+
+/* Endpoint descriptor values */
+#define USB_ENDPOINT_ADDRESS_MASK       0x0f
+#define USB_ENDPOINT_DIR_MASK           0x80
+
+#define USB_ENDPOINT_TYPE_MASK          0x03
+#define USB_ENDPOINT_TYPE_CONTROL       0
+#define USB_ENDPOINT_TYPE_ISOCHRONOUS   1
+#define USB_ENDPOINT_TYPE_BULK          2
+#define USB_ENDPOINT_TYPE_INTERRUPT     3
+
+/* Standard requests */
+#define USB_REQ_GET_STATUS              0x00
+#define USB_REQ_CLEAR_FEATURE           0x01
+#define USB_REQ_SET_FEATURE             0x03
+#define USB_REQ_SET_ADDRESS             0x05
+#define USB_REQ_GET_DESCRIPTOR          0x06
+#define USB_REQ_SET_DESCRIPTOR          0x07
+#define USB_REQ_GET_CONFIGURATION       0x08
+#define USB_REQ_SET_CONFIGURATION       0x09
+#define USB_REQ_GET_INTERFACE           0x0A
+#define USB_REQ_SET_INTERFACE           0x0B
+#define USB_REQ_SYNCH_FRAME             0x0C
+
+/* USB types */
+#define USB_TYPE_STANDARD               (0x00 << 5)
+#define USB_TYPE_CLASS                  (0x01 << 5)
+#define USB_TYPE_VENDOR                 (0x02 << 5)
+#define USB_TYPE_RESERVED               (0x03 << 5)
+
+/* USB recipients */
+#define USB_RECIP_DEVICE                0x00
+#define USB_RECIP_INTERFACE             0x01
+#define USB_RECIP_ENDPOINT              0x02
+#define USB_RECIP_OTHER                 0x03
+
+#endif /* not USB_CLASS_PER_INTERFACE */
+
+/* Not defined in libsub */
+#define USB_TYPE_MASK                   (0x03 << 5)
+#define USB_RECIP_MASK                  0x1f
+
+/* USB directions */
+#define USB_DIR_OUT                     0
+#define USB_DIR_IN                      0x80
+
 /** Initialize sanei_usb.
  *
  * Call this before any other sanei_usb function.
  */
-extern void
-sanei_usb_init (void);
+extern void sanei_usb_init (void);
 
 /** Get the vendor and product ids.
  *
@@ -89,15 +169,13 @@ sanei_usb_find_devices (SANE_Int vendor, SANE_Int product,
  *   permissions
  * - SANE_STATUS_INVAL - on every other error
  */
-extern SANE_Status
-sanei_usb_open (SANE_String_Const devname, SANE_Int *fd);
+extern SANE_Status sanei_usb_open (SANE_String_Const devname, SANE_Int * fd);
 
 /** Close a USB device.
  * 
  * @param fd file descriptor of the device
  */
-extern void
-sanei_usb_close (SANE_Int fd);
+extern void sanei_usb_close (SANE_Int fd);
 
 /** Initiate a bulk transfer read.
  *
@@ -114,9 +192,9 @@ sanei_usb_close (SANE_Int fd);
  * - SANE_STATUS_IO_ERROR - if an error occured during the read
  * - SANE_STATUS_INVAL - on every other error
  *
- */ 
+ */
 extern SANE_Status
-sanei_usb_read_bulk (SANE_Int fd, SANE_Byte * buffer, size_t *size);
+sanei_usb_read_bulk (SANE_Int fd, SANE_Byte * buffer, size_t * size);
 
 /** Initiate a bulk transfer write.
  *
@@ -133,7 +211,7 @@ sanei_usb_read_bulk (SANE_Int fd, SANE_Byte * buffer, size_t *size);
  * - SANE_STATUS_INVAL - on every other error
  */
 extern SANE_Status
-sanei_usb_write_bulk (SANE_Int fd, SANE_Byte * buffer, size_t *size);
+sanei_usb_write_bulk (SANE_Int fd, SANE_Byte * buffer, size_t * size);
 
 /** Send/receive a control message to/from a USB device.
  *
@@ -162,7 +240,7 @@ sanei_usb_write_bulk (SANE_Int fd, SANE_Byte * buffer, size_t *size);
 extern SANE_Status
 sanei_usb_control_msg (SANE_Int fd, SANE_Int rtype, SANE_Int req,
 		       SANE_Int value, SANE_Int index, SANE_Int len,
-		       SANE_Byte *data);
+		       SANE_Byte * data);
 
 /** Expand device name patterns into a list of devices.
  *
@@ -179,7 +257,7 @@ sanei_usb_control_msg (SANE_Int fd, SANE_Int rtype, SANE_Int req,
  * @param attach attach function
  *
  */
-extern void 
+extern void
 sanei_usb_attach_matching_devices (const char *name,
 				   SANE_Status (*attach) (const char *dev));
 
diff --git a/sanei/sanei_usb.c b/sanei/sanei_usb.c
index 2d61bbd66..3502b61f7 100644
--- a/sanei/sanei_usb.c
+++ b/sanei/sanei_usb.c
@@ -1,5 +1,5 @@
 /* sane - Scanner Access Now Easy.
-   Copyright (C) 2001 Henning Meier-Geinitz
+   Copyright (C) 2001, 2002 Henning Meier-Geinitz
    Copyright (C) 2001 Frank Zago (sanei_usb_control_msg)
    This file is part of the SANE package.
 
@@ -39,7 +39,7 @@
    whether to permit this exception to apply to your modifications.
    If you do not wish that, delete this exception notice.
 
-   This file provides a generic (?) USB interface.  */
+   This file provides a generic USB interface.  */
 
 #include "../include/sane/config.h"
 
@@ -53,14 +53,47 @@
 #include 
 #include 
 
+#ifdef HAVE_LIBUSB
+#include 
+#endif /* HAVE_LIBUSB */
+
 #define BACKEND_NAME	sanei_usb
 #include "../include/sane/sane.h"
 #include "../include/sane/sanei_debug.h"
 #include "../include/sane/sanei_usb.h"
 #include "../include/sane/sanei_config.h"
 
-#if defined (__linux__)
+typedef enum
+{
+  sanei_usb_method_scanner_driver = 0,	/* kernel scanner driver 
+					   (Linux, BSD) */
+  sanei_usb_method_libusb
+}
+sanei_usb_access_method_type;
 
+typedef struct
+{
+  SANE_Bool open;
+  sanei_usb_access_method_type method;
+  int fd;
+  SANE_Int bulk_in_ep;
+  SANE_Int bulk_out_ep;
+#ifdef HAVE_LIBUSB
+  usb_dev_handle *libusb_handle;
+#endif				/* HAVE_LIBUSB */
+}
+device_list_type;
+
+#define MAX_DEVICES 100
+
+/* per-device information, using the functions fd as index */
+static device_list_type devices[MAX_DEVICES];
+
+#ifdef HAVE_LIBUSB
+int libusb_timeout = 30 * 1000;	/* 30 seconds */
+#endif /* HAVE_LIBUSB */
+
+#if defined (__linux__)
 /* From /usr/src/linux/driver/usb/scanner.h */
 #define SCANNER_IOCTL_VENDOR _IOR('U', 0x20, int)
 #define SCANNER_IOCTL_PRODUCT _IOR('U', 0x21, int)
@@ -69,30 +102,46 @@
 #define SCANNER_IOCTL_VENDOR_OLD _IOR('u', 0xa0, int)
 #define SCANNER_IOCTL_PRODUCT_OLD _IOR('u', 0xa1, int)
 
-/* From /usr/src/linux/include/linux/usb.h */ 
-typedef struct {
+/* From /usr/src/linux/include/linux/usb.h */
+typedef struct
+{
   unsigned char requesttype;
   unsigned char request;
   unsigned short value;
   unsigned short index;
   unsigned short length;
-} devrequest __attribute__ ((packed));
+}
+devrequest __attribute__ ((packed));
 
 /* From /usr/src/linux/driver/usb/scanner.h */
-struct ctrlmsg_ioctl {
-  devrequest  req;
-  void        *data;
-} cmsg;
-
+struct ctrlmsg_ioctl
+{
+  devrequest req;
+  void *data;
+}
+cmsg;
 #endif /* __linux__ */
 
 
 void
 sanei_usb_init (void)
 {
-  DBG_INIT();
+  DBG_INIT ();
+
+  memset (devices, 0, sizeof (devices));
+
+#ifdef HAVE_LIBUSB
+  usb_init ();
+#ifdef DBG_LEVEL
+  if (DBG_LEVEL > 4)
+    usb_set_debug (255);
+#endif /* DBG_LEVEL */
+  usb_find_busses ();
+  usb_find_devices ();
+#endif /* HAVE_LIBUSB */
 }
 
+
 /* This logically belongs to sanei_config.c but not every backend that
    uses sanei_config() wants to depend on sanei_usb.  */
 void
@@ -131,7 +180,7 @@ sanei_usb_attach_matching_devices (const char *name,
 	}
       sanei_usb_find_devices (vendorID, productID, attach);
     }
-  else 
+  else
     (*attach) (name);
 }
 
@@ -142,38 +191,66 @@ sanei_usb_get_vendor_product (SANE_Int fd, SANE_Word * vendor,
   SANE_Word vendorID = 0;
   SANE_Word productID = 0;
 
+  if (fd >= MAX_DEVICES || fd < 0)
+    {
+      DBG (1, "sanei_usb_get_vendor_product: fd >= MAX_DEVICES || fd < 0\n");
+      return SANE_STATUS_INVAL;
+    }
+
+  if (devices[fd].method == sanei_usb_method_scanner_driver)
+    {
 #if defined (__linux__)
-  /* read the vendor and product IDs via the IOCTLs */
-  if (ioctl (fd, SCANNER_IOCTL_VENDOR , &vendorID) == -1)
-    {
-      if (ioctl (fd, SCANNER_IOCTL_VENDOR_OLD , &vendorID) == -1)
-	DBG (3, "sanei_usb_get_vendor_product: ioctl (vendor) of fd %d "
-	     "failed: %s\n", fd, strerror (errno));
+      /* read the vendor and product IDs via the IOCTLs */
+      if (ioctl (devices[fd].fd, SCANNER_IOCTL_VENDOR, &vendorID) == -1)
+	{
+	  if (ioctl (devices[fd].fd, SCANNER_IOCTL_VENDOR_OLD, &vendorID)
+	      == -1)
+	    DBG (3, "sanei_usb_get_vendor_product: ioctl (vendor) "
+		 "of device %d failed: %s\n", fd, strerror (errno));
+	}
+      if (ioctl (devices[fd].fd, SCANNER_IOCTL_PRODUCT, &productID) == -1)
+	{
+	  if (ioctl (devices[fd].fd, SCANNER_IOCTL_PRODUCT_OLD,
+		     &productID) == -1)
+	    DBG (3, "sanei_usb_get_vendor_product: ioctl (product) "
+		 "of device %d failed: %s\n", fd, strerror (errno));
+	}
+#endif /* defined (__linux__) */
+      /* put more os-dependant stuff ... */
     }
-  if (ioctl (fd, SCANNER_IOCTL_PRODUCT , &productID) == -1)
+  else if (devices[fd].method == sanei_usb_method_libusb)
     {
-      if (ioctl (fd, SCANNER_IOCTL_PRODUCT_OLD , &productID) == -1)
-	DBG (3, "sanei_usb_get_vendor_product: ioctl (product) of fd %d "
-	     "failed: %s\n", fd, strerror (errno));
+#ifdef HAVE_LIBUSB
+      vendorID = usb_device (devices[fd].libusb_handle)->descriptor.idVendor;
+      productID =
+	usb_device (devices[fd].libusb_handle)->descriptor.idProduct;
+#else
+      DBG (1, "sanei_usb_get_vendor_product: libusb support missing\n");
+      return SANE_STATUS_UNSUPPORTED;
+#endif /* HAVE_LIBUSB */
     }
+  else
+    {
+      DBG (1, "sanei_usb_get_vendor_product: access method %d not "
+	   "implemented\n", devices[fd].method);
+      return SANE_STATUS_INVAL;
+    }
+
   if (vendor)
     *vendor = vendorID;
   if (product)
     *product = productID;
-#endif /* not defined (__linux__) */
 
   if (!vendorID || !productID)
     {
-      DBG (3, "sanei_usb_get_vendor_product: fd %d: Your OS doesn't seem to "
-	   "support detection of vendor+product ids\n",
-	   fd);
-
+      DBG (3, "sanei_usb_get_vendor_product: device %d: Your OS doesn't "
+	   "seem to support detection of vendor+product ids\n", fd);
       return SANE_STATUS_UNSUPPORTED;
     }
   else
     {
-      DBG (3, "sanei_usb_get_vendor_product: fd %d: vendorID: 0x%x, "
-	   "productID: 0x%x\n", fd, vendorID, productID);
+      DBG (3, "sanei_usb_get_vendor_product: device %d: vendorID: 0x%04x, "
+	   "productID: 0x%04x\n", fd, vendorID, productID);
       return SANE_STATUS_GOOD;
     }
 }
@@ -217,68 +294,285 @@ sanei_usb_find_devices (SANE_Int vendor, SANE_Int product,
 #elif defined(__FreeBSD__)
     "/dev/uscanner",
 #endif
-    0};
-  SANE_Char devname[30];
+    0
+  };
+  SANE_Char devname[1024];
   int devcount;
   SANE_Status status;
 
-#define SANEI_USB_MAX_DEVICES 16
+#define SANEI_USB_MAX_DEVICE_FILES 16
 
-  DBG (3, "sanei_usb_find_devices: vendor=0x%x, product=0x%x, attach=%p\n",
+  DBG (3,
+       "sanei_usb_find_devices: vendor=0x%04x, product=0x%04x, attach=%p\n",
        vendor, product, attach);
 
+  /* First we check the device files for access over the kernel scanner
+     drivers */
   for (prefix = prefixlist; *prefix; prefix++)
     {
       status = check_vendor_product (*prefix, vendor, product, attach);
       if (status == SANE_STATUS_UNSUPPORTED)
-	return status; /* give up if we can't detect vendor/product */
-      for (devcount = 0; devcount < SANEI_USB_MAX_DEVICES; 
-	   devcount++)
+	break;			/* give up if we can't detect vendor/product */
+      for (devcount = 0; devcount < SANEI_USB_MAX_DEVICE_FILES; devcount++)
 	{
-	  snprintf (devname, sizeof (devname), "%s%d", *prefix,
-		    devcount);
+	  snprintf (devname, sizeof (devname), "%s%d", *prefix, devcount);
 	  status = check_vendor_product (devname, vendor, product, attach);
 	  if (status == SANE_STATUS_UNSUPPORTED)
-	    return status; /* give up if we can't detect vendor/product */
+	    break;		/* give up if we can't detect vendor/product */
 	}
     }
+  /* Now we will check all the devices libusb finds (if available) */
+#ifdef HAVE_LIBUSB
+  {
+    struct usb_bus *bus;
+    struct usb_device *dev;
+
+    for (bus = usb_get_busses (); bus; bus = bus->next)
+      {
+	for (dev = bus->devices; dev; dev = dev->next)
+	  {
+	    if (dev->descriptor.idVendor == vendor
+		&& dev->descriptor.idProduct == product)
+	      {
+		DBG (3,
+		     "sanei_usb_find_devices: found matching libusb device "
+		     "(bus=%s, filename=%s, vendor=0x%04x, product=0x%04x)\n",
+		     bus->dirname, dev->filename, dev->descriptor.idVendor,
+		     dev->descriptor.idProduct);
+		snprintf (devname, sizeof (devname), "libusb:%s:%s",
+			  bus->dirname, dev->filename);
+		attach (devname);
+	      }
+	    else
+	      DBG (5, "sanei_usb_find_devices: found libusb device "
+		   "(bus=%s, filename=%s, vendor=0x%04x, product=0x%04x)\n",
+		   bus->dirname, dev->filename, dev->descriptor.idVendor,
+		   dev->descriptor.idProduct);
+	  }
+      }
+  }
+#endif /* HAVE_LIBUSB */
   return SANE_STATUS_GOOD;
 }
 
 SANE_Status
-sanei_usb_open (SANE_String_Const devname, SANE_Int *fd)
+sanei_usb_open (SANE_String_Const devname, SANE_Int * fd)
 {
+  int devcount;
+
   if (!fd)
     {
-      DBG (1, "sanei_usb_open: fd == NULL\n");
+      DBG (1, "sanei_usb_open: can't open `%s': fd == NULL\n", devname);
       return SANE_STATUS_INVAL;
     }
-  *fd = open (devname, O_RDWR);
-  if (*fd < 0)
-    {
-      SANE_Status status = SANE_STATUS_INVAL;
 
-      DBG (1, "sanei_usb_open: open failed: %s\n", strerror (errno));
-      if (errno == EACCES)
-	status = SANE_STATUS_ACCESS_DENIED;
-      return status;
+  for (devcount = 0;
+       devcount < MAX_DEVICES && devices[devcount].open == SANE_TRUE;
+       devcount++)
+    ;
+
+  if (devices[devcount].open)
+    {
+      DBG (1, "sanei_usb_open: can't open `%s': "
+	   "maximum amount of devices (%d) reached\n", devname, MAX_DEVICES);
+      return SANE_STATUS_NO_MEM;
     }
-  DBG (5, "sanei_usb_open: fd %d opened\n", *fd);
+
+  memset (&devices[devcount], 0, sizeof (devices[devcount]));
+
+  if (strncmp (devname, "libusb:", 7) == 0)
+    {
+      /* Using libusb */
+#ifdef HAVE_LIBUSB
+      SANE_String start, end, bus_string, dev_string;
+      struct usb_bus *bus;
+      struct usb_device *dev;
+      struct usb_interface_descriptor *interface;
+      SANE_Int result;
+      int num;
+
+
+      DBG (5, "sanei_usb_open: trying to open device `%s'\n", devname);
+      start = strchr (devname, ':');
+      if (!start)
+	return SANE_STATUS_INVAL;
+      start++;
+      end = strchr (start, ':');
+      if (!end)
+	return SANE_STATUS_INVAL;
+      bus_string = strndup (start, end - start);
+      if (!bus_string)
+	return SANE_STATUS_NO_MEM;
+
+      start = strchr (start, ':');
+      if (!start)
+	return SANE_STATUS_INVAL;
+      start++;
+      dev_string = strdup (start);
+      if (!dev_string)
+	return SANE_STATUS_NO_MEM;
+
+      for (bus = usb_get_busses (); bus; bus = bus->next)
+	{
+	  if (strcmp (bus->dirname, bus_string) == 0)
+	    {
+	      for (dev = bus->devices; dev; dev = dev->next)
+		{
+		  if (strcmp (dev->filename, dev_string) == 0)
+		    devices[devcount].libusb_handle = usb_open (dev);
+		}
+	    }
+	}
+
+      if (!devices[devcount].libusb_handle)
+	{
+	  DBG (1, "sanei_usb_open: can't open device `%s': "
+	       "not found or libusb error (%s)\n", devname, strerror (errno));
+	  return SANE_STATUS_INVAL;
+	}
+      devices[devcount].method = sanei_usb_method_libusb;
+
+      dev = usb_device (devices[devcount].libusb_handle);
+
+      if (dev->descriptor.bNumConfigurations > 1)
+	{
+	  DBG (3, "sanei_usb_open: more than one "
+	       "configuration (%d), choosing config 0\n",
+	       dev->descriptor.bNumConfigurations);
+	}
+
+      if (dev->config[0].interface->num_altsetting > 1)
+	{
+	  DBG (3, "sanei_usb_open: more than one "
+	       "alternate setting (%d), choosing interface 0\n",
+	       dev->config[0].interface->num_altsetting);
+	}
+
+      result = usb_claim_interface (devices[devcount].libusb_handle, 0);
+      if (result < 0)
+	{
+	  SANE_Status status = SANE_STATUS_INVAL;
+
+	  DBG (1, "sanei_usb_open: libusb complained: %s\n", usb_strerror ());
+	  if (errno == EPERM)
+	    {
+	      DBG (1, "Make sure you run as root or set appropriate "
+		   "permissions\n");
+	      status = SANE_STATUS_ACCESS_DENIED;
+	    }
+	  else if (errno == EBUSY)
+	    {
+	      DBG (1, "Maybe the kernel scanner driver claims the "
+		   "scanner's interface?\n");
+	      status = SANE_STATUS_DEVICE_BUSY;
+	    }
+	  usb_close (devices[devcount].libusb_handle);
+	  return status;
+	}
+      interface = &dev->config[0].interface->altsetting[0];
+
+      /* Now we look for usable endpoints */
+      for (num = 0; num < interface->bNumEndpoints; num++)
+	{
+	  struct usb_endpoint_descriptor *endpoint;
+	  int address, direction, transfer_type;
+
+	  endpoint = &interface->endpoint[num];
+	  address = endpoint->bEndpointAddress & USB_ENDPOINT_ADDRESS_MASK;
+	  direction = endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK;
+	  transfer_type = endpoint->bmAttributes & USB_ENDPOINT_TYPE_MASK;
+
+	  if (transfer_type != USB_ENDPOINT_TYPE_BULK)
+	    {
+	      DBG (5, "sanei_usb_open: ignoring %s-%s endpoint "
+		   "(address: %d)\n",
+		   transfer_type == USB_ENDPOINT_TYPE_CONTROL ? "control" :
+		   transfer_type == USB_ENDPOINT_TYPE_ISOCHRONOUS
+		   ? "isochronous" : "interrupt",
+		   direction ? "in" : "out", address);
+	      continue;
+	    }
+	  DBG (5, "sanei_usb_open: found bulk-%s endpoint (address %d)\n",
+	       direction ? "in" : "out", address);
+	  if (direction)	/* in */
+	    {
+	      if (devices[devcount].bulk_in_ep)
+		DBG (3, "sanei_usb_open: we already have a bulk-in endpoint "
+		     "(address: %d), ignoring the new one\n",
+		     devices[devcount].bulk_in_ep);
+	      else
+		devices[devcount].bulk_in_ep = address;
+	    }
+	  else
+	    {
+	      if (devices[devcount].bulk_out_ep)
+		DBG (3, "sanei_usb_open: we already have a bulk-out endpoint "
+		     "(address: %d), ignoring the new one\n",
+		     devices[devcount].bulk_out_ep);
+	      else
+		devices[devcount].bulk_out_ep = address;
+	    }
+	}
+#else /* not HAVE_LIBUSB */
+      DBG (1, "sanei_usb_open: can't open device `%s': "
+	   "libusb support missing\n", devname);
+      return SANE_STATUS_UNSUPPORTED;
+#endif /* not HAVE_LIBUSB */
+    }
+  else
+    {
+      /* Using kernel scanner driver */
+      devices[devcount].fd = open (devname, O_RDWR);
+      if (devices[devcount].fd < 0)
+	{
+	  SANE_Status status = SANE_STATUS_INVAL;
+
+	  if (errno == EACCES)
+	    status = SANE_STATUS_ACCESS_DENIED;
+	  else if (errno == ENOENT)
+	    {
+	      DBG (5, "sanei_usb_open: open of `%s' failed: %s\n",
+		   devname, strerror (errno));
+	      return status;
+	    }
+	  DBG (1, "sanei_usb_open: open of `%s' failed: %s\n",
+	       devname, strerror (errno));
+	  return status;
+	}
+      devices[devcount].method = sanei_usb_method_scanner_driver;
+    }
+  devices[devcount].open = SANE_TRUE;
+  *fd = devcount;
+  DBG (3, "sanei_usb_open: opened usb device `%s' (*fd=%d)\n",
+       devname, devcount);
   return SANE_STATUS_GOOD;
 }
 
 void
 sanei_usb_close (SANE_Int fd)
 {
-  DBG (5, "sanei_usb_close: closing fd %d\n", fd);
-  close (fd);
+  DBG (5, "sanei_usb_close: closing device %d\n", fd);
+  if (fd >= MAX_DEVICES || fd < 0)
+    {
+      DBG (1, "sanei_usb_close: fd >= MAX_DEVICES || fd < 0\n");
+      return;
+    }
+  if (devices[fd].method == sanei_usb_method_scanner_driver)
+    close (devices[fd].fd);
+  else
+#ifdef HAVE_LIBUSB
+    usb_close (devices[fd].libusb_handle);
+#else
+    DBG (1, "sanei_usb_close: libusb support missing\n");
+#endif
+  devices[fd].open = SANE_FALSE;
   return;
 }
 
 SANE_Status
-sanei_usb_read_bulk (SANE_Int fd, SANE_Byte * buffer, size_t *size)
+sanei_usb_read_bulk (SANE_Int fd, SANE_Byte * buffer, size_t * size)
 {
-  ssize_t read_size;
+  ssize_t read_size = 0;
 
   if (!size)
     {
@@ -286,7 +580,40 @@ sanei_usb_read_bulk (SANE_Int fd, SANE_Byte * buffer, size_t *size)
       return SANE_STATUS_INVAL;
     }
 
-  read_size = read (fd, buffer, *size);
+  if (fd >= MAX_DEVICES || fd < 0)
+    {
+      DBG (1, "sanei_usb_read_bulk: fd >= MAX_DEVICES || fd < 0\n");
+      return SANE_STATUS_INVAL;
+    }
+  if (devices[fd].method == sanei_usb_method_scanner_driver)
+    read_size = read (devices[fd].fd, buffer, *size);
+  else if (devices[fd].method == sanei_usb_method_libusb)
+#ifdef HAVE_LIBUSB
+    {
+      if (devices[fd].bulk_in_ep)
+	read_size = usb_bulk_read (devices[fd].libusb_handle,
+				   devices[fd].bulk_in_ep, (char *) buffer,
+				   (int) *size, libusb_timeout);
+      else
+	{
+	  DBG (1, "sanei_usb_read_bulk: can't read without a bulk-in "
+	       "endpoint\n");
+	  return SANE_STATUS_INVAL;
+	}
+    }
+#else /* not HAVE_LIBUSB */
+    {
+      DBG (1, "sanei_usb_read_bulk: libusb support missing\n");
+      return SANE_STATUS_UNSUPPORTED;
+    }
+#endif /* not HAVE_LIBUSB */
+  else
+    {
+      DBG (1, "sanei_usb_read_bulk: access method %d not implemented\n",
+	   devices[fd].method);
+      return SANE_STATUS_INVAL;
+    }
+
   if (read_size < 0)
     {
       DBG (1, "sanei_usb_read_bulk: read failed: %s\n", strerror (errno));
@@ -300,15 +627,15 @@ sanei_usb_read_bulk (SANE_Int fd, SANE_Byte * buffer, size_t *size)
       return SANE_STATUS_EOF;
     }
   DBG (5, "sanei_usb_read_bulk: wanted %lu bytes, got %ld bytes\n",
-	 (unsigned long) *size, (unsigned long) read_size);
+       (unsigned long) *size, (unsigned long) read_size);
   *size = read_size;
   return SANE_STATUS_GOOD;
 }
 
 SANE_Status
-sanei_usb_write_bulk (SANE_Int fd, SANE_Byte * buffer, size_t *size)
+sanei_usb_write_bulk (SANE_Int fd, SANE_Byte * buffer, size_t * size)
 {
-  ssize_t write_size;
+  ssize_t write_size = 0;
 
   if (!size)
     {
@@ -316,7 +643,41 @@ sanei_usb_write_bulk (SANE_Int fd, SANE_Byte * buffer, size_t *size)
       return SANE_STATUS_INVAL;
     }
 
-  write_size = write (fd, buffer, *size);
+  if (fd >= MAX_DEVICES || fd < 0)
+    {
+      DBG (1, "sanei_usb_write_bulk: fd >= MAX_DEVICES || fd < 0\n");
+      return SANE_STATUS_INVAL;
+    }
+
+  if (devices[fd].method == sanei_usb_method_scanner_driver)
+    write_size = write (devices[fd].fd, buffer, *size);
+  else if (devices[fd].method == sanei_usb_method_libusb)
+#ifdef HAVE_LIBUSB
+    {
+      if (devices[fd].bulk_in_ep)
+	write_size = usb_bulk_write (devices[fd].libusb_handle,
+				     devices[fd].bulk_out_ep, (char *) buffer,
+				     (int) *size, libusb_timeout);
+      else
+	{
+	  DBG (1, "sanei_usb_write_bulk: can't write without a bulk-out "
+	       "endpoint\n");
+	  return SANE_STATUS_INVAL;
+	}
+    }
+#else /* not HAVE_LIBUSB */
+    {
+      DBG (1, "sanei_usb_write_bulk: libusb support missing\n");
+      return SANE_STATUS_UNSUPPORTED;
+    }
+#endif /* not HAVE_LIBUSB */
+  else
+    {
+      DBG (1, "sanei_usb_read_bulk: access method %d not implemented\n",
+	   devices[fd].method);
+      return SANE_STATUS_INVAL;
+    }
+
   if (write_size < 0)
     {
       DBG (1, "sanei_usb_write_bulk: write failed: %s\n", strerror (errno));
@@ -324,38 +685,75 @@ sanei_usb_write_bulk (SANE_Int fd, SANE_Byte * buffer, size_t *size)
       return SANE_STATUS_IO_ERROR;
     }
   DBG (5, "sanei_usb_write_bulk: wanted %lu bytes, wrote %ld bytes\n",
-	 (unsigned long) *size, (unsigned long) write_size);
+       (unsigned long) *size, (unsigned long) write_size);
   *size = write_size;
   return SANE_STATUS_GOOD;
 }
 
 SANE_Status
-sanei_usb_control_msg(SANE_Int fd, SANE_Int rtype, SANE_Int req,
-		      SANE_Int value, SANE_Int index, SANE_Int len,
-		      SANE_Byte *data )
+sanei_usb_control_msg (SANE_Int fd, SANE_Int rtype, SANE_Int req,
+		       SANE_Int value, SANE_Int index, SANE_Int len,
+		       SANE_Byte * data)
 {
-#if defined(__linux__)
-  struct ctrlmsg_ioctl c;
-
-  c.req.requesttype = rtype;
-  c.req.request = req;
-  c.req.value = value;
-  c.req.index = index;
-  c.req.length = len;
-  c.data = data;
-
-  DBG(5, "sanei_usb_control_msg: rtype = 0x%02x, req = %d, value = %d, "
-      "index = %d, len = %d\n", rtype, req, value, index, len);
-  
-  if (ioctl(fd, SCANNER_IOCTL_CTRLMSG, &c) < 0)
+  if (fd >= MAX_DEVICES || fd < 0)
     {
-      DBG(5, "sanei_usb_control_msg: SCANNER_IOCTL_CTRLMSG error - %s\n",
-	  strerror(errno));
-      return SANE_STATUS_IO_ERROR;
+      DBG (1, "sanei_usb_control_msg: fd >= MAX_DEVICES || fd < 0\n");
+      return SANE_STATUS_INVAL;
+    }
+
+  if (devices[fd].method == sanei_usb_method_scanner_driver)
+    {
+#if defined(__linux__)
+      struct ctrlmsg_ioctl c;
+
+      c.req.requesttype = rtype;
+      c.req.request = req;
+      c.req.value = value;
+      c.req.index = index;
+      c.req.length = len;
+      c.data = data;
+
+      DBG (5, "sanei_usb_control_msg: rtype = 0x%02x, req = %d, value = %d, "
+	   "index = %d, len = %d\n", rtype, req, value, index, len);
+
+      if (ioctl (devices[fd].fd, SCANNER_IOCTL_CTRLMSG, &c) < 0)
+	{
+	  DBG (5, "sanei_usb_control_msg: SCANNER_IOCTL_CTRLMSG error - %s\n",
+	       strerror (errno));
+	  return SANE_STATUS_IO_ERROR;
+	}
+      return SANE_STATUS_GOOD;
+#else /* not __linux__ */
+      DBG (5, "sanei_usb_control_msg: not supported on this OS\n");
+      return SANE_STATUS_UNSUPPORTED;
+#endif /* not __linux__ */
+    }
+  else if (devices[fd].method == sanei_usb_method_libusb)
+#ifdef HAVE_LIBUSB
+    {
+      int result;
+
+      result = usb_control_msg (devices[fd].libusb_handle, rtype, req,
+				value, index, (char *) data, len,
+				libusb_timeout);
+      if (result < 0)
+	{
+	  DBG (1, "sanei_usb_control_msg: libusb complained: %s\n",
+	       usb_strerror ());
+	  return SANE_STATUS_INVAL;
+	}
+      return SANE_STATUS_GOOD;
+    }
+#else /* not HAVE_LIBUSB */
+    {
+      DBG (1, "sanei_usb_control_msg: libusb support missing\n");
+      return SANE_STATUS_UNSUPPORTED;
+    }
+#endif /* not HAVE_LIBUSB */
+  else
+    {
+      DBG (1, "sanei_usb_read_bulk: access method %d not implemented\n",
+	   devices[fd].method);
+      return SANE_STATUS_UNSUPPORTED;
     }
-  return SANE_STATUS_GOOD;
-#else
-  DBG (5, "sanei_usb_control_msg: not supported on this OS\n");
-  return SANE_STATUS_UNSUPPORTED;
-#endif /* __linux__ */
 }