aboutsummaryrefslogtreecommitdiff
path: root/sanei/sanei_usb.c
diff options
context:
space:
mode:
Diffstat (limited to 'sanei/sanei_usb.c')
-rw-r--r--sanei/sanei_usb.c3381
1 files changed, 3381 insertions, 0 deletions
diff --git a/sanei/sanei_usb.c b/sanei/sanei_usb.c
new file mode 100644
index 0000000..f50a56c
--- /dev/null
+++ b/sanei/sanei_usb.c
@@ -0,0 +1,3381 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) 2001 - 2005 Henning Meier-Geinitz
+ Copyright (C) 2001 Frank Zago (sanei_usb_control_msg)
+ Copyright (C) 2003 Rene Rebe (sanei_read_int,sanei_set_timeout)
+ Copyright (C) 2005 Paul Smedley <paul@smedley.info> (OS/2 usbcalls)
+ Copyright (C) 2007 EPSON AVASYS CORPORATION (usblp detach/attach)
+ Copyright (C) 2008 m. allan noah (bus rescan support, sanei_usb_clear_halt)
+ Copyright (C) 2009 Julien BLACHE <jb@jblache.org> (libusb-1.0)
+ Copyright (C) 2011 Reinhold Kainhofer <reinhold@kainhofer.com> (sanei_usb_set_endpoint)
+ This file is part of the SANE package.
+
+ 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 2 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, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ 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. */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <stdio.h>
+#include <dirent.h>
+#include <time.h>
+
+#ifdef HAVE_RESMGR
+#include <resmgr.h>
+#endif
+
+#ifdef HAVE_LIBUSB
+#include <usb.h>
+#endif /* HAVE_LIBUSB */
+
+#ifdef HAVE_LIBUSB_1_0
+#include <libusb.h>
+#endif /* HAVE_LIBUSB_1_0 */
+
+#ifdef HAVE_USBCALLS
+#include <usb.h>
+#include <os2.h>
+#include <usbcalls.h>
+#define MAX_RW 64000
+static int usbcalls_timeout = 30 * 1000; /* 30 seconds */
+USBHANDLE dh;
+PHEV pUsbIrqStartHev=NULL;
+
+static
+struct usb_descriptor_header *
+GetNextDescriptor( struct usb_descriptor_header *currHead, UCHAR *lastBytePtr)
+{
+ UCHAR *currBytePtr, *nextBytePtr;
+
+ if (!currHead->bLength)
+ return (NULL);
+ currBytePtr=(UCHAR *)currHead;
+ nextBytePtr=currBytePtr+currHead->bLength;
+ if (nextBytePtr>=lastBytePtr)
+ return (NULL);
+ return ((struct usb_descriptor_header*)nextBytePtr);
+}
+#endif /* HAVE_USBCALLS */
+
+#if defined (__FreeBSD__)
+#include <dev/usb/usb.h>
+#endif /* __FreeBSD__ */
+#if defined (__DragonFly__)
+#include <bus/usb/usb.h>
+#endif
+
+#ifdef BACKEND_NAME
+#undef BACKEND_NAME
+#endif
+
+#define BACKEND_NAME sanei_usb
+#include <sane/sane.h>
+#include "sane/sanei_debug.h"
+#include "sane/sanei_usb.h"
+#include "sane/sanei_config.h"
+
+typedef enum
+{
+ sanei_usb_method_scanner_driver = 0, /* kernel scanner driver
+ (Linux, BSD) */
+ sanei_usb_method_libusb,
+
+ sanei_usb_method_usbcalls
+}
+sanei_usb_access_method_type;
+
+typedef struct
+{
+ SANE_Bool open;
+ sanei_usb_access_method_type method;
+ int fd;
+ SANE_String devname;
+ SANE_Int vendor;
+ SANE_Int product;
+ SANE_Int bulk_in_ep;
+ SANE_Int bulk_out_ep;
+ SANE_Int iso_in_ep;
+ SANE_Int iso_out_ep;
+ SANE_Int int_in_ep;
+ SANE_Int int_out_ep;
+ SANE_Int control_in_ep;
+ SANE_Int control_out_ep;
+ SANE_Int interface_nr;
+ SANE_Int missing;
+#ifdef HAVE_LIBUSB
+ usb_dev_handle *libusb_handle;
+ struct usb_device *libusb_device;
+#endif /* HAVE_LIBUSB */
+#ifdef HAVE_LIBUSB_1_0
+ libusb_device *lu_device;
+ libusb_device_handle *lu_handle;
+#endif /* HAVE_LIBUSB_1_0 */
+#if defined (__linux__) && (defined(HAVE_LIBUSB) || defined(HAVE_LIBUSB_1_0))
+#if defined (USB_MAXINTERFACES)
+#define MAX_INTERFACE_FLAGS ((USB_MAXINTERFACES + 7) / 8)
+#else
+#define MAX_INTERFACE_FLAGS (256 / 8)
+#endif
+ char driver_detached[MAX_INTERFACE_FLAGS];
+#endif /* __linux__ && (HAVE_LIBUSB || HAVE_LIBUSB_1_0) */
+}
+device_list_type;
+
+/**
+ * total number of devices that can be found at the same time */
+#define MAX_DEVICES 100
+
+/**
+ * per-device information, using the functions' parameters dn as index */
+static device_list_type devices[MAX_DEVICES];
+
+/**
+ * total number of detected devices in devices array */
+static int device_number=0;
+
+#if defined(HAVE_LIBUSB) || defined(HAVE_LIBUSB_1_0)
+static int libusb_timeout = 30 * 1000; /* 30 seconds */
+#endif /* HAVE_LIBUSB || HAVE_LIBUSB_1_0 */
+
+#ifdef HAVE_LIBUSB
+#if defined (__linux__)
+#ifndef USB_ERROR_STR
+#define USB_ERROR_STR(x, format, args...) \
+ do {\
+ DBG (2, format, ## args);\
+ return x;\
+ } while (0)
+#endif
+
+/* From libusb-0.1.12/linux.[ch].
+ Used to detach and (re)attach kernel drivers. */
+struct usb_ioctl {
+ int ifno; /* interface 0..N ; negative numbers reserved */
+ int ioctl_code; /* MUST encode size + direction of data so the
+ * macros in <asm/ioctl.h> give correct values */
+ void *data; /* param buffer (in, or out) */
+};
+
+#define USB_MAXDRIVERNAME 255
+
+struct usb_getdriver {
+ unsigned int interface;
+ char driver[USB_MAXDRIVERNAME + 1];
+};
+
+#define IOCTL_USB_IOCTL _IOWR('U', 18, struct usb_ioctl)
+#define IOCTL_USB_GETDRIVER _IOW('U', 8, struct usb_getdriver)
+#define IOCTL_USB_DISCONNECT _IO('U', 22)
+#define IOCTL_USB_CONNECT _IO('U', 23)
+
+struct usb_dev_handle { int fd; };
+
+#ifndef LIBUSB_HAS_USB_GET_DRIVER_NP
+static int
+_usb_get_driver_np (usb_dev_handle *dev, int interface, char *name,
+ unsigned int namelen)
+{
+ struct usb_getdriver getdrv;
+ int ret;
+
+ getdrv.interface = interface;
+ ret = ioctl (dev->fd, IOCTL_USB_GETDRIVER, &getdrv);
+ if (ret)
+ USB_ERROR_STR (-errno, "could not get bound driver: %s", strerror(errno));
+
+ strncpy (name, getdrv.driver, namelen - 1);
+ name[namelen - 1] = 0;
+
+ return 0;
+}
+#define sanei_usb_get_driver_np _usb_get_driver_np
+#else
+#define sanei_usb_get_driver_np usb_get_driver_np
+#endif /* LIBUSB_HAS_USB_GET_DRIVER_NP */
+
+#ifndef LIBUSB_HAS_USB_DETACH_KERNEL_DRIVER_NP
+static int
+_usb_detach_kernel_driver_np (usb_dev_handle *dev, int interface)
+{
+ struct usb_ioctl command;
+ int ret;
+
+ command.ifno = interface;
+ command.ioctl_code = IOCTL_USB_DISCONNECT;
+ command.data = NULL;
+
+ ret = ioctl (dev->fd, IOCTL_USB_IOCTL, &command);
+ if (ret)
+ USB_ERROR_STR (-errno,
+ "could not detach kernel driver from interface %d: %s",
+ interface, strerror(errno));
+
+ return 0;
+}
+#define sanei_usb_detach_kernel_driver_np _usb_detach_kernel_driver_np
+#else
+#define sanei_usb_detach_kernel_driver_np usb_detach_kernel_driver_np
+#endif /* LIBUSB_HAS_USB_DETACH_KERNEL_DRIVER_NP */
+
+#ifndef LIBUSB_HAS_USB_ATTACH_KERNEL_DRIVER_NP
+static int
+_usb_attach_kernel_driver_np (struct usb_dev_handle *dev, int interface)
+{
+ struct usb_ioctl command;
+ int ret;
+
+ command.ifno = interface;
+ command.ioctl_code = IOCTL_USB_CONNECT;
+ command.data = NULL;
+
+ ret = ioctl(dev->fd, IOCTL_USB_IOCTL, &command);
+ if (ret)
+ USB_ERROR_STR(-errno,
+ "could not attach kernel driver to interface %d: %s",
+ interface, strerror(errno));
+ return 0;
+}
+#define sanei_usb_attach_kernel_driver_np _usb_attach_kernel_driver_np
+#else
+#define sanei_usb_attach_kernel_driver_np usb_attach_kernel_driver_np
+#endif /* LIBUSB_HAS_USB_ATTACH_KERNEL_DRIVER_NP */
+
+static void
+detach_usblp_driver (usb_dev_handle *h, char *detached)
+{
+ const int c = 0; /* only config ever tried */
+
+ int i;
+ struct usb_device *d = usb_device (h);
+
+ for (i = 0; i < d->config[c].bNumInterfaces; ++i)
+ {
+ char driver[32];
+
+ driver[0] = 0;
+ sanei_usb_get_driver_np (h, i, driver, sizeof (driver));
+
+ if ((0 != driver[0]) && (0 == strcasecmp ("usblp", driver)))
+ {
+ detached[i/8] |= (1 << (i%8));
+ DBG (4, "Detaching '%s' from config/interface %03i/%03i\n",
+ driver, c, i);
+ if (0 > sanei_usb_detach_kernel_driver_np (h, i))
+ {
+ DBG (4, "Failed to detach '%s'\n", driver);
+ }
+ }
+ }
+}
+
+static void
+attach_usblp_driver (usb_dev_handle *h, char *detached)
+{
+ const int c = 0; /* only config ever tried */
+
+ int i;
+ struct usb_device *d = usb_device (h);
+
+ for (i = 0; i < d->config[c].bNumInterfaces; ++i)
+ {
+ if ((1 << (i%8)) & detached[i/8])
+ {
+ DBG (4, "Attaching drivers on config/interface %03i/%03i\n",
+ c, i);
+ if (0 > sanei_usb_attach_kernel_driver_np (h, i))
+ {
+ DBG (4, "Failed to attach drivers\n");
+ }
+ break;
+ }
+ }
+}
+#endif /* __linux__ */
+#endif /* HAVE_LIBUSB */
+
+#ifdef HAVE_LIBUSB_1_0
+static char *sanei_libusb_strerror (int errcode);
+#endif /* HAVE_LIBUSB_1_0 */
+
+#if defined (HAVE_LIBUSB_1_0) && defined (__linux__)
+static void
+detach_usblp_driver (libusb_device_handle *h, char *detached)
+{
+ const int c = 0; /* only config ever tried */
+
+ int i;
+ libusb_device *d = libusb_get_device (h);
+ struct libusb_config_descriptor *cfg = NULL;
+ int ret = libusb_get_config_descriptor (d, c, &cfg);
+ if (LIBUSB_SUCCESS != ret)
+ {
+ DBG (4, "Failed to get config descriptor; '%s'\n", sanei_libusb_strerror (ret));
+ return;
+ }
+
+ for (i = 0; i < cfg->bNumInterfaces; ++i)
+ {
+ const int is_active = 1;
+ ret = libusb_kernel_driver_active (h, i);
+ if (is_active == ret)
+ {
+ detached[i/8] |= (1 << (i%8));
+ DBG (4, "Detaching kernel driver from config/interface %03i/%03i\n",
+ c, i);
+ ret = libusb_detach_kernel_driver (h, i);
+ if (LIBUSB_SUCCESS != ret)
+ {
+ DBG (4, "Failed to detach kernel driver; '%s'\n", sanei_libusb_strerror (ret));
+ }
+ }
+ else if (0 != ret)
+ {
+ DBG (4, "Failed to find kernel driver; '%s'\n", sanei_libusb_strerror (ret));
+ }
+ }
+}
+
+static void
+attach_usblp_driver (libusb_device_handle *h, char *detached)
+{
+ const int c = 0; /* only config ever tried */
+
+ int i;
+ libusb_device *d = libusb_get_device (h);
+ struct libusb_config_descriptor *cfg = NULL;
+ int ret = libusb_get_config_descriptor (d, c, &cfg);
+ if (LIBUSB_SUCCESS != ret)
+ {
+ DBG (4, "Failed to get config descriptor; '%s'\n", sanei_libusb_strerror (ret));
+ return;
+ }
+
+ for (i = 0; i < cfg->bNumInterfaces; ++i)
+ {
+ if ((1 << (i%8)) & detached[i/8])
+ {
+ DBG (4, "Attaching drivers on config/interface %03i/%03i\n",
+ c, i);
+ ret = libusb_attach_kernel_driver (h, i);
+ if (LIBUSB_SUCCESS != ret)
+ {
+ DBG (4, "Failed to attach drivers; '%s'\n", sanei_libusb_strerror (ret));
+ }
+ break;
+ }
+ }
+}
+#endif /* HAVE_LIBUSB_1_0 && __linux__ */
+
+#ifdef HAVE_LIBUSB_1_0
+static libusb_context *sanei_usb_ctx;
+#endif /* HAVE_LIBUSB_1_0 */
+
+#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)
+#define SCANNER_IOCTL_CTRLMSG _IOWR('U', 0x22, devrequest)
+/* Older (unofficial) IOCTL numbers for Linux < v2.4.13 */
+#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
+{
+ unsigned char requesttype;
+ unsigned char request;
+ unsigned short value;
+ unsigned short index;
+ unsigned short length;
+}
+devrequest;
+
+/* From /usr/src/linux/driver/usb/scanner.h */
+struct ctrlmsg_ioctl
+{
+ devrequest req;
+ void *data;
+}
+cmsg;
+#elif defined(__BEOS__)
+#include <drivers/USB_scanner.h>
+#include <kernel/OS.h>
+#endif /* __linux__ */
+
+/* Debug level from sanei_init_debug */
+static SANE_Int debug_level;
+
+static void
+print_buffer (const SANE_Byte * buffer, SANE_Int size)
+{
+#define NUM_COLUMNS 16
+#define PRINT_BUFFER_SIZE (4 + NUM_COLUMNS * (3 + 1) + 1 + 1)
+ char line_str[PRINT_BUFFER_SIZE];
+ char *pp;
+ int column;
+ int line;
+
+ memset (line_str, 0, PRINT_BUFFER_SIZE);
+
+ for (line = 0; line < ((size + NUM_COLUMNS - 1) / NUM_COLUMNS); line++)
+ {
+ pp = line_str;
+ sprintf (pp, "%03X ", line * NUM_COLUMNS);
+ pp += 4;
+ for (column = 0; column < NUM_COLUMNS; column++)
+ {
+ if ((line * NUM_COLUMNS + column) < size)
+ sprintf (pp, "%02X ", buffer[line * NUM_COLUMNS + column]);
+ else
+ sprintf (pp, " ");
+ pp += 3;
+ }
+ for (column = 0; column < NUM_COLUMNS; column++)
+ {
+ if ((line * NUM_COLUMNS + column) < size)
+ sprintf (pp, "%c",
+ (buffer[line * NUM_COLUMNS + column] < 127) &&
+ (buffer[line * NUM_COLUMNS + column] > 31) ?
+ buffer[line * NUM_COLUMNS + column] : '.');
+ else
+ sprintf (pp, " ");
+ pp += 1;
+ }
+ DBG (11, "%s\n", line_str);
+ }
+}
+
+static void
+kernel_get_vendor_product (int fd, const char *name, int *vendorID, int *productID)
+{
+#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, "kernel_get_vendor_product: ioctl (vendor) "
+ "of device %s failed: %s\n", name, strerror (errno));
+ }
+ if (ioctl (fd, SCANNER_IOCTL_PRODUCT, productID) == -1)
+ {
+ if (ioctl (fd, SCANNER_IOCTL_PRODUCT_OLD, productID) == -1)
+ DBG (3, "sanei_usb_get_vendor_product: ioctl (product) "
+ "of device %s failed: %s\n", name, strerror (errno));
+ }
+#elif defined(__BEOS__)
+ {
+ uint16 vendor, product;
+ if (ioctl (fd, B_SCANNER_IOCTL_VENDOR, &vendor) != B_OK)
+ DBG (3, "kernel_get_vendor_product: ioctl (vendor) "
+ "of device %d failed: %s\n", fd, strerror (errno));
+ if (ioctl (fd, B_SCANNER_IOCTL_PRODUCT, &product) != B_OK)
+ DBG (3, "sanei_usb_get_vendor_product: ioctl (product) "
+ "of device %d failed: %s\n", fd, strerror (errno));
+ /* copy from 16 to 32 bit value */
+ *vendorID = vendor;
+ *productID = product;
+ }
+#elif defined (__FreeBSD__) || defined (__DragonFly__)
+ {
+ int controller;
+ int ctrl_fd;
+ char buf[40];
+ int dev;
+
+ for (controller = 0; ; controller++ )
+ {
+ snprintf (buf, sizeof (buf) - 1, "/dev/usb%d", controller);
+ ctrl_fd = open (buf, O_RDWR);
+
+ /* If we can not open the usb controller device, treat it
+ as the end of controller devices */
+ if (ctrl_fd < 0)
+ break;
+
+ /* Search for the scanner device on this bus */
+ for (dev = 1; dev < USB_MAX_DEVICES; dev++)
+ {
+ struct usb_device_info devInfo;
+ devInfo.udi_addr = dev;
+
+ if (ioctl (ctrl_fd, USB_DEVICEINFO, &devInfo) == -1)
+ break; /* Treat this as the end of devices for this controller */
+
+ snprintf (buf, sizeof (buf), "/dev/%s", devInfo.udi_devnames[0]);
+ if (strncmp (buf, name, sizeof (buf)) == 0)
+ {
+ *vendorID = (int) devInfo.udi_vendorNo;
+ *productID = (int) devInfo.udi_productNo;
+ close (ctrl_fd);
+ return;
+ }
+ }
+ close (ctrl_fd);
+ }
+ DBG (3, "kernel_get_vendor_product: Could not retrieve "
+ "vendor/product ID from device %s\n", name);
+ }
+#endif /* defined (__linux__), defined(__BEOS__), ... */
+ /* put more os-dependant stuff ... */
+}
+
+/**
+ * store the given device in device list if it isn't already
+ * in it
+ * @param device device to store if new
+ */
+static void
+store_device (device_list_type device)
+{
+ int i = 0;
+ int pos = -1;
+
+ /* if there are already some devices present, check against
+ * them and leave if an equal one is found */
+ for (i = 0; i < device_number; i++)
+ {
+ if (devices[i].method == device.method
+ && !strcmp (devices[i].devname, device.devname)
+ && devices[i].vendor == device.vendor
+ && devices[i].product == device.product)
+ {
+ /*
+ * Need to update the LibUSB device pointer, since it might
+ * have changed after the latest USB scan.
+ */
+#ifdef HAVE_LIBUSB
+ devices[i].libusb_device = device.libusb_device;
+#endif
+#ifdef HAVE_LIBUSB_1_0
+ devices[i].lu_device = device.lu_device;
+#endif
+
+ devices[i].missing=0;
+ DBG (3, "store_device: not storing device %s\n", device.devname);
+ return;
+ }
+ if (devices[i].missing >= 2)
+ pos = i;
+ }
+
+ if(pos > -1){
+ DBG (3, "store_device: overwrite dn %d with %s\n", pos, device.devname);
+ }
+ else{
+ if(device_number >= MAX_DEVICES){
+ DBG (3, "store_device: no room for %s\n", device.devname);
+ return;
+ }
+ pos = device_number;
+ device_number++;
+ DBG (3, "store_device: add dn %d with %s\n", pos, device.devname);
+ }
+ memcpy (&(devices[pos]), &device, sizeof (device));
+ devices[pos].open = SANE_FALSE;
+}
+
+#ifdef HAVE_LIBUSB_1_0
+static char *
+sanei_libusb_strerror (int errcode)
+{
+ /* Error codes & descriptions from the libusb-1.0 documentation */
+
+ switch (errcode)
+ {
+ case LIBUSB_SUCCESS:
+ return "Success (no error)";
+
+ case LIBUSB_ERROR_IO:
+ return "Input/output error";
+
+ case LIBUSB_ERROR_INVALID_PARAM:
+ return "Invalid parameter";
+
+ case LIBUSB_ERROR_ACCESS:
+ return "Access denied (insufficient permissions)";
+
+ case LIBUSB_ERROR_NO_DEVICE:
+ return "No such device (it may have been disconnected)";
+
+ case LIBUSB_ERROR_NOT_FOUND:
+ return "Entity not found";
+
+ case LIBUSB_ERROR_BUSY:
+ return "Resource busy";
+
+ case LIBUSB_ERROR_TIMEOUT:
+ return "Operation timed out";
+
+ case LIBUSB_ERROR_OVERFLOW:
+ return "Overflow";
+
+ case LIBUSB_ERROR_PIPE:
+ return "Pipe error";
+
+ case LIBUSB_ERROR_INTERRUPTED:
+ return "System call interrupted (perhaps due to signal)";
+
+ case LIBUSB_ERROR_NO_MEM:
+ return "Insufficient memory";
+
+ case LIBUSB_ERROR_NOT_SUPPORTED:
+ return "Operation not supported or unimplemented on this platform";
+
+ case LIBUSB_ERROR_OTHER:
+ return "Other error";
+
+ default:
+ return "Unknown libusb-1.0 error code";
+ }
+
+ return "Unknown libusb-1.0 error code";
+}
+#endif /* HAVE_LIBUSB_1_0 */
+
+void
+sanei_usb_init (void)
+{
+ SANE_String *prefix;
+ SANE_String prefixlist[] = {
+#if defined(__linux__)
+ "/dev/", "usbscanner",
+ "/dev/usb/", "scanner",
+#elif defined(__FreeBSD__) || defined(__NetBSD__) || defined (__OpenBSD__) || defined (__DragonFly__)
+ "/dev/", "uscanner",
+#elif defined(__BEOS__)
+ "/dev/scanner/usb/", "",
+#endif
+ 0, 0
+ };
+ SANE_Int vendor, product;
+ SANE_Char devname[1024];
+ int fd;
+ int i;
+ device_list_type device;
+#ifdef HAVE_LIBUSB
+ struct usb_bus *bus;
+ struct usb_device *dev;
+#endif /* HAVE_LIBUSB */
+#ifdef HAVE_LIBUSB_1_0
+ libusb_device **devlist;
+ ssize_t ndev;
+
+ libusb_device *dev;
+ libusb_device_handle *hdl;
+ struct libusb_device_descriptor desc;
+ struct libusb_config_descriptor *config0;
+ unsigned short vid, pid;
+ unsigned char busno, address;
+ int config;
+ int interface;
+ int ret;
+#endif /* HAVE_LIBUSB_1_0 */
+
+ DBG_INIT ();
+#ifdef DBG_LEVEL
+ debug_level = DBG_LEVEL;
+#else
+ debug_level = 0;
+#endif
+
+ /* if no device yet, clean up memory */
+ if(device_number==0)
+ memset (devices, 0, sizeof (devices));
+
+ DBG (4, "sanei_usb_init: marking existing devices\n");
+ for (i = 0; i < device_number; i++) {
+ devices[i].missing++;
+ }
+
+ DBG (4, "sanei_usb_init: Looking for kernel scanner devices\n");
+ /* Check for devices using the kernel scanner driver */
+
+ for (prefix = prefixlist; *prefix; prefix += 2)
+ {
+ SANE_String dir_name = *prefix;
+ SANE_String base_name = *(prefix + 1);
+ struct stat stat_buf;
+ DIR *dir;
+ struct dirent *dir_entry;
+
+ if (stat (dir_name, &stat_buf) < 0)
+ {
+ DBG (5, "sanei_usb_init: can't stat %s: %s\n", dir_name,
+ strerror (errno));
+ continue;
+ }
+ if (!S_ISDIR (stat_buf.st_mode))
+ {
+ DBG (5, "sanei_usb_init: %s is not a directory\n", dir_name);
+ continue;
+ }
+ if ((dir = opendir (dir_name)) == 0)
+ {
+ DBG (5, "sanei_usb_init: cannot read directory %s: %s\n", dir_name,
+ strerror (errno));
+ continue;
+ }
+
+ while ((dir_entry = readdir (dir)) != 0)
+ {
+ /* skip standard dir entries */
+ if (strcmp (dir_entry->d_name, ".") == 0 || strcmp (dir_entry->d_name, "..") == 0)
+ continue;
+
+ if (strncmp (base_name, dir_entry->d_name, strlen (base_name)) == 0)
+ {
+ if (strlen (dir_name) + strlen (dir_entry->d_name) + 1 >
+ sizeof (devname))
+ continue;
+ sprintf (devname, "%s%s", dir_name, dir_entry->d_name);
+ fd = -1;
+#ifdef HAVE_RESMGR
+ fd = rsm_open_device (devname, O_RDWR);
+#endif
+ if (fd == -1)
+ fd = open (devname, O_RDWR);
+ if (fd < 0)
+ {
+ DBG (5, "sanei_usb_init: couldn't open %s: %s\n", devname,
+ strerror (errno));
+ continue;
+ }
+ vendor = -1;
+ product = -1;
+ kernel_get_vendor_product (fd, devname, &vendor, &product);
+ close (fd);
+ memset (&device, 0, sizeof (device));
+ device.devname = strdup (devname);
+ if (!device.devname)
+ {
+ closedir (dir);
+ return;
+ }
+ device.vendor = vendor;
+ device.product = product;
+ device.method = sanei_usb_method_scanner_driver;
+ DBG (4,
+ "sanei_usb_init: found kernel scanner device (0x%04x/0x%04x) at %s\n",
+ vendor, product, devname);
+ store_device(device);
+ }
+ }
+ closedir (dir);
+ }
+
+ /* Check for devices using (old) libusb */
+#ifdef HAVE_LIBUSB
+ DBG (4, "sanei_usb_init: Looking for libusb devices\n");
+ usb_init ();
+#ifdef DBG_LEVEL
+ if (DBG_LEVEL > 4)
+ usb_set_debug (255);
+#endif /* DBG_LEVEL */
+
+ usb_find_busses ();
+ usb_find_devices ();
+
+ /* Check for the matching device */
+ for (bus = usb_get_busses (); bus; bus = bus->next)
+ {
+ for (dev = bus->devices; dev; dev = dev->next)
+ {
+ int interface;
+ SANE_Bool found = SANE_FALSE;
+
+ if (!dev->config)
+ {
+ DBG (1,
+ "sanei_usb_init: device 0x%04x/0x%04x is not configured\n",
+ dev->descriptor.idVendor, dev->descriptor.idProduct);
+ continue;
+ }
+ if (dev->descriptor.idVendor == 0 || dev->descriptor.idProduct == 0)
+ {
+ DBG (5,
+ "sanei_usb_init: device 0x%04x/0x%04x looks like a root hub\n",
+ dev->descriptor.idVendor, dev->descriptor.idProduct);
+ continue;
+ }
+
+ for (interface = 0;
+ interface < dev->config[0].bNumInterfaces && !found;
+ interface++)
+ {
+ switch (dev->descriptor.bDeviceClass)
+ {
+ case USB_CLASS_VENDOR_SPEC:
+ found = SANE_TRUE;
+ break;
+ case USB_CLASS_PER_INTERFACE:
+ if (dev->config[0].interface[interface].num_altsetting == 0 ||
+ !dev->config[0].interface[interface].altsetting)
+ {
+ DBG (1, "sanei_usb_init: device 0x%04x/0x%04x doesn't "
+ "have an altsetting for interface %d\n",
+ dev->descriptor.idVendor, dev->descriptor.idProduct,
+ interface);
+ continue;
+ }
+ switch (dev->config[0].interface[interface].altsetting[0].
+ bInterfaceClass)
+ {
+ case USB_CLASS_VENDOR_SPEC:
+ case USB_CLASS_PER_INTERFACE:
+ case 6: /* imaging? */
+ case 16: /* data? */
+ found = SANE_TRUE;
+ break;
+ }
+ break;
+ }
+ if (!found)
+ DBG (5,
+ "sanei_usb_init: device 0x%04x/0x%04x, interface %d "
+ "doesn't look like a "
+ "scanner (%d/%d)\n", dev->descriptor.idVendor,
+ dev->descriptor.idProduct, interface,
+ dev->descriptor.bDeviceClass,
+ dev->config[0].interface[interface].altsetting != 0
+ ? dev->config[0].interface[interface].altsetting[0].
+ bInterfaceClass : -1);
+ }
+ interface--;
+ if (!found)
+ {
+ DBG (5,
+ "sanei_usb_init: device 0x%04x/0x%04x: no suitable interfaces\n",
+ dev->descriptor.idVendor, dev->descriptor.idProduct);
+ continue;
+ }
+
+ memset (&device, 0, sizeof (device));
+ device.libusb_device = dev;
+ snprintf (devname, sizeof (devname), "libusb:%s:%s",
+ dev->bus->dirname, dev->filename);
+ device.devname = strdup (devname);
+ if (!device.devname)
+ return;
+ device.vendor = dev->descriptor.idVendor;
+ device.product = dev->descriptor.idProduct;
+ device.method = sanei_usb_method_libusb;
+ device.interface_nr = interface;
+ DBG (4,
+ "sanei_usb_init: found libusb device (0x%04x/0x%04x) interface "
+ "%d at %s\n",
+ dev->descriptor.idVendor, dev->descriptor.idProduct, interface,
+ devname);
+ store_device(device);
+ }
+ }
+#endif /* HAVE_LIBUSB */
+
+
+ /* Check for devices using libusb-1.0 */
+#ifdef HAVE_LIBUSB_1_0
+ if (!sanei_usb_ctx)
+ {
+ DBG (4, "sanei_usb_init: initializing libusb-1.0\n");
+ ret = libusb_init (&sanei_usb_ctx);
+ if (ret < 0)
+ {
+ DBG (1,
+ "sanei_usb_init: failed to initialize libusb-1.0, error %d\n",
+ ret);
+
+ goto failed_libusb_1_0;
+ }
+#ifdef DBG_LEVEL
+ if (DBG_LEVEL > 4)
+ libusb_set_debug (sanei_usb_ctx, 3);
+#endif /* DBG_LEVEL */
+ }
+
+ DBG (4, "sanei_usb_init: Looking for libusb-1.0 devices\n");
+
+ ndev = libusb_get_device_list (sanei_usb_ctx, &devlist);
+ if (ndev < 0)
+ {
+ DBG (1,
+ "sanei_usb_init: failed to get libusb-1.0 device list, error %d\n",
+ (int) ndev);
+
+ goto failed_libusb_1_0;
+ }
+
+ for (i = 0; i < ndev; i++)
+ {
+ SANE_Bool found = SANE_FALSE;
+
+ dev = devlist[i];
+
+ busno = libusb_get_bus_number (dev);
+ address = libusb_get_device_address (dev);
+
+ ret = libusb_get_device_descriptor (dev, &desc);
+ if (ret < 0)
+ {
+ DBG (1,
+ "sanei_usb_init: could not get device descriptor for device at %03d:%03d (err %d)\n",
+ busno, address, ret);
+ continue;
+ }
+
+ vid = desc.idVendor;
+ pid = desc.idProduct;
+
+ if ((vid == 0) || (pid == 0))
+ {
+ DBG (5,
+ "sanei_usb_init: device 0x%04x/0x%04x at %03d:%03d looks like a root hub\n",
+ vid, pid, busno, address);
+ continue;
+ }
+
+ ret = libusb_open (dev, &hdl);
+ if (ret < 0)
+ {
+ DBG (1,
+ "sanei_usb_init: skipping device 0x%04x/0x%04x at %03d:%03d: cannot open: %s\n",
+ vid, pid, busno, address, sanei_libusb_strerror (ret));
+
+ continue;
+ }
+
+ ret = libusb_get_configuration (hdl, &config);
+
+ libusb_close (hdl);
+
+ if (ret < 0)
+ {
+ DBG (1,
+ "sanei_usb_init: could not get configuration for device 0x%04x/0x%04x at %03d:%03d (err %d)\n",
+ vid, pid, busno, address, ret);
+ continue;
+ }
+
+ if (config == 0)
+ {
+ DBG (1,
+ "sanei_usb_init: device 0x%04x/0x%04x at %03d:%03d is not configured\n",
+ vid, pid, busno, address);
+ continue;
+ }
+
+ ret = libusb_get_config_descriptor (dev, 0, &config0);
+ if (ret < 0)
+ {
+ DBG (1,
+ "sanei_usb_init: could not get config[0] descriptor for device 0x%04x/0x%04x at %03d:%03d (err %d)\n",
+ vid, pid, busno, address, ret);
+ continue;
+ }
+
+ for (interface = 0; (interface < config0->bNumInterfaces) && !found; interface++)
+ {
+ switch (desc.bDeviceClass)
+ {
+ case LIBUSB_CLASS_VENDOR_SPEC:
+ found = SANE_TRUE;
+ break;
+
+ case LIBUSB_CLASS_PER_INTERFACE:
+ if ((config0->interface[interface].num_altsetting == 0)
+ || !config0->interface[interface].altsetting)
+ {
+ DBG (1, "sanei_usb_init: device 0x%04x/0x%04x doesn't "
+ "have an altsetting for interface %d\n",
+ vid, pid, interface);
+ continue;
+ }
+
+ switch (config0->interface[interface].altsetting[0].bInterfaceClass)
+ {
+ case LIBUSB_CLASS_VENDOR_SPEC:
+ case LIBUSB_CLASS_PER_INTERFACE:
+ case LIBUSB_CLASS_PTP:
+ case 16: /* data? */
+ found = SANE_TRUE;
+ break;
+ }
+ break;
+ }
+
+ if (!found)
+ DBG (5,
+ "sanei_usb_init: device 0x%04x/0x%04x, interface %d "
+ "doesn't look like a scanner (%d/%d)\n",
+ vid, pid, interface, desc.bDeviceClass,
+ (config0->interface[interface].altsetting != 0)
+ ? config0->interface[interface].altsetting[0].bInterfaceClass : -1);
+ }
+
+ libusb_free_config_descriptor (config0);
+
+ interface--;
+
+ if (!found)
+ {
+ DBG (5,
+ "sanei_usb_init: device 0x%04x/0x%04x at %03d:%03d: no suitable interfaces\n",
+ vid, pid, busno, address);
+ continue;
+ }
+
+ memset (&device, 0, sizeof (device));
+ device.lu_device = libusb_ref_device(dev);
+ snprintf (devname, sizeof (devname), "libusb:%03d:%03d",
+ busno, address);
+ device.devname = strdup (devname);
+ if (!device.devname)
+ return;
+ device.vendor = vid;
+ device.product = pid;
+ device.method = sanei_usb_method_libusb;
+ device.interface_nr = interface;
+ DBG (4,
+ "sanei_usb_init: found libusb-1.0 device (0x%04x/0x%04x) interface "
+ "%d at %s\n",
+ vid, pid, interface, devname);
+
+ store_device (device);
+ }
+
+ libusb_free_device_list (devlist, 1);
+
+ failed_libusb_1_0:
+ /* libusb 1.0 failed to initialize */
+
+#endif /* HAVE_LIBUSB_1_0 */
+
+#if !defined(HAVE_LIBUSB) && !defined(HAVE_LIBUSB_1_0)
+ DBG (4, "sanei_usb_init: SANE is built without support for libusb\n");
+#endif
+
+
+#ifdef HAVE_USBCALLS
+ /* Check for devices using OS/2 USBCALLS Interface */
+
+ CHAR ucData[2048];
+ struct usb_device_descriptor *pDevDesc;
+ struct usb_config_descriptor *pCfgDesc;
+ struct usb_interface_descriptor *intf;
+ struct usb_endpoint_descriptor *ep;
+ struct usb_descriptor_header *pDescHead;
+
+ APIRET rc;
+ ULONG ulNumDev, ulDev, ulBufLen;
+
+ ulBufLen = sizeof(ucData);
+ memset(&ucData,0,sizeof(ucData));
+ rc = UsbQueryNumberDevices( &ulNumDev);
+
+ if(rc==0 && ulNumDev)
+ {
+ for (ulDev=1; ulDev<=ulNumDev; ulDev++)
+ {
+ rc = UsbQueryDeviceReport( ulDev,
+ &ulBufLen,
+ ucData);
+
+ pDevDesc = (struct usb_device_descriptor*)ucData;
+ pCfgDesc = (struct usb_config_descriptor*) (ucData+sizeof(struct usb_device_descriptor));
+ int interface=0;
+ SANE_Bool found;
+ if (!pCfgDesc->bConfigurationValue)
+ {
+ DBG (1, "sanei_usb_init: device 0x%04x/0x%04x is not configured\n",
+ pDevDesc->idVendor, pDevDesc->idProduct);
+ continue;
+ }
+ if (pDevDesc->idVendor == 0 || pDevDesc->idProduct == 0)
+ {
+ DBG (5, "sanei_usb_init: device 0x%04x/0x%04x looks like a root hub\n",
+ pDevDesc->idVendor, pDevDesc->idProduct);
+ continue;
+ }
+ found = SANE_FALSE;
+
+ if (pDevDesc->bDeviceClass == USB_CLASS_VENDOR_SPEC)
+ {
+ found = SANE_TRUE;
+ }
+
+ if (!found)
+ {
+ DBG (5, "sanei_usb_init: device 0x%04x/0x%04x: no suitable interfaces\n",
+ pDevDesc->idVendor, pDevDesc->idProduct);
+ continue;
+ }
+
+ snprintf (devname, sizeof (devname), "usbcalls:%d",
+ ulDev);
+ memset (&device, 0, sizeof (device));
+ device.devname = strdup (devname);
+ device.fd = ulDev; /* store usbcalls device number */
+ device.vendor = pDevDesc->idVendor;
+ device.product = pDevDesc->idProduct;
+ device.method = sanei_usb_method_usbcalls;
+ device.interface_nr = interface;
+ DBG (4, "sanei_usb_init: found usbcalls device (0x%04x/0x%04x) as device number %s\n",
+ pDevDesc->idVendor, pDevDesc->idProduct,device.devname);
+ store_device(device);
+ }
+ }
+
+#endif /* HAVE_USBCALLS */
+
+ DBG (5, "sanei_usb_init: found %d devices\n", device_number);
+ if (debug_level > 5)
+ {
+ for (i = 0; i < device_number; i++)
+ {
+ if(devices[i].missing)
+ continue;
+ DBG (6, "sanei_usb_init: device %02d is %s\n", i, devices[i].devname);
+ }
+ }
+}
+
+
+/* This logically belongs to sanei_config.c but not every backend that
+ uses sanei_config() wants to depend on sanei_usb. */
+void
+sanei_usb_attach_matching_devices (const char *name,
+ SANE_Status (*attach) (const char *dev))
+{
+ char *vendor, *product;
+
+ if (strncmp (name, "usb", 3) == 0)
+ {
+ SANE_Word vendorID = 0, productID = 0;
+
+ name += 3;
+
+ name = sanei_config_skip_whitespace (name);
+ if (*name)
+ {
+ name = sanei_config_get_string (name, &vendor);
+ if (vendor)
+ {
+ vendorID = strtol (vendor, 0, 0);
+ free (vendor);
+ }
+ name = sanei_config_skip_whitespace (name);
+ }
+
+ name = sanei_config_skip_whitespace (name);
+ if (*name)
+ {
+ name = sanei_config_get_string (name, &product);
+ if (product)
+ {
+ productID = strtol (product, 0, 0);
+ free (product);
+ }
+ }
+ sanei_usb_find_devices (vendorID, productID, attach);
+ }
+ else
+ (*attach) (name);
+}
+
+SANE_Status
+sanei_usb_get_vendor_product_byname (SANE_String_Const devname,
+ SANE_Word * vendor, SANE_Word * product)
+{
+ int i;
+ SANE_Bool found = SANE_FALSE;
+
+ for (i = 0; i < device_number && devices[i].devname; i++)
+ {
+ if(devices[i].missing)
+ continue;
+
+ if (strcmp (devices[i].devname, devname) == 0)
+ {
+ found = SANE_TRUE;
+ break;
+ }
+ }
+
+ if (!found)
+ {
+ DBG (1, "sanei_usb_get_vendor_product_byname: can't find device `%s' in list\n", devname);
+ return SANE_STATUS_INVAL;
+ }
+
+ if ((devices[i].vendor == 0) && (devices[i].product == 0))
+ {
+ DBG (1, "sanei_usb_get_vendor_product_byname: not support for this method\n");
+ return SANE_STATUS_UNSUPPORTED;
+ }
+
+ if (vendor)
+ *vendor = devices[i].vendor;
+
+ if (product)
+ *product = devices[i].product;
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sanei_usb_get_vendor_product (SANE_Int dn, SANE_Word * vendor,
+ SANE_Word * product)
+{
+ SANE_Word vendorID = 0;
+ SANE_Word productID = 0;
+
+ if (dn >= device_number || dn < 0)
+ {
+ DBG (1, "sanei_usb_get_vendor_product: dn >= device number || dn < 0\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ if (devices[dn].method == sanei_usb_method_scanner_driver)
+ kernel_get_vendor_product (devices[dn].fd, devices[dn].devname, &vendorID, &productID);
+ else if (devices[dn].method == sanei_usb_method_libusb)
+ {
+#ifdef HAVE_LIBUSB
+ vendorID = usb_device (devices[dn].libusb_handle)->descriptor.idVendor;
+ productID =
+ usb_device (devices[dn].libusb_handle)->descriptor.idProduct;
+#elif defined(HAVE_LIBUSB_1_0)
+ vendorID = devices[dn].vendor;
+ productID = devices[dn].product;
+#else
+ DBG (1, "sanei_usb_get_vendor_product: libusb support missing\n");
+ return SANE_STATUS_UNSUPPORTED;
+#endif /* HAVE_LIBUSB */
+ }
+ else if (devices[dn].method == sanei_usb_method_usbcalls)
+ {
+#ifdef HAVE_USBCALLS
+ vendorID = devices[dn].vendor;
+ productID = devices[dn].product;
+#else
+ DBG (1, "sanei_usb_get_vendor_product: usbcalls support missing\n");
+ return SANE_STATUS_UNSUPPORTED;
+#endif /* HAVE_USBCALLS */
+ }
+ else
+ {
+ DBG (1, "sanei_usb_get_vendor_product: access method %d not "
+ "implemented\n", devices[dn].method);
+ return SANE_STATUS_INVAL;
+ }
+ if (vendor)
+ *vendor = vendorID;
+ if (product)
+ *product = productID;
+
+ if (!vendorID || !productID)
+ {
+ DBG (3, "sanei_usb_get_vendor_product: device %d: Your OS doesn't "
+ "seem to support detection of vendor+product ids\n", dn);
+ return SANE_STATUS_UNSUPPORTED;
+ }
+ else
+ {
+ DBG (3, "sanei_usb_get_vendor_product: device %d: vendorID: 0x%04x, "
+ "productID: 0x%04x\n", dn, vendorID, productID);
+ return SANE_STATUS_GOOD;
+ }
+}
+
+SANE_Status
+sanei_usb_find_devices (SANE_Int vendor, SANE_Int product,
+ SANE_Status (*attach) (SANE_String_Const dev))
+{
+ SANE_Int dn = 0;
+
+ DBG (3,
+ "sanei_usb_find_devices: vendor=0x%04x, product=0x%04x\n",
+ vendor, product);
+
+ while (devices[dn].devname && dn < device_number)
+ {
+ if (devices[dn].vendor == vendor
+ && devices[dn].product == product
+ && !devices[dn].missing
+ && attach)
+ attach (devices[dn].devname);
+ dn++;
+ }
+ return SANE_STATUS_GOOD;
+}
+
+void
+sanei_usb_set_endpoint (SANE_Int dn, SANE_Int ep_type, SANE_Int ep)
+{
+ if (dn >= device_number || dn < 0)
+ {
+ DBG (1, "sanei_usb_set_endpoint: dn >= device number || dn < 0\n");
+ return;
+ }
+
+ DBG (5, "sanei_usb_set_endpoint: Setting endpoint of type 0x%02x to 0x%02x\n", ep_type, ep);
+ switch (ep_type)
+ {
+ case USB_DIR_IN|USB_ENDPOINT_TYPE_BULK:
+ devices[dn].bulk_in_ep = ep;
+ break;
+ case USB_DIR_OUT|USB_ENDPOINT_TYPE_BULK:
+ devices[dn].bulk_out_ep = ep;
+ break;
+ case USB_DIR_IN|USB_ENDPOINT_TYPE_ISOCHRONOUS:
+ devices[dn].iso_in_ep = ep;
+ break;
+ case USB_DIR_OUT|USB_ENDPOINT_TYPE_ISOCHRONOUS:
+ devices[dn].iso_out_ep = ep;
+ break;
+ case USB_DIR_IN|USB_ENDPOINT_TYPE_INTERRUPT:
+ devices[dn].int_in_ep = ep;
+ break;
+ case USB_DIR_OUT|USB_ENDPOINT_TYPE_INTERRUPT:
+ devices[dn].int_out_ep = ep;
+ break;
+ case USB_DIR_IN|USB_ENDPOINT_TYPE_CONTROL:
+ devices[dn].control_in_ep = ep;
+ break;
+ case USB_DIR_OUT|USB_ENDPOINT_TYPE_CONTROL:
+ devices[dn].control_out_ep = ep;
+ break;
+ }
+}
+
+SANE_Int
+sanei_usb_get_endpoint (SANE_Int dn, SANE_Int ep_type)
+{
+ if (dn >= device_number || dn < 0)
+ {
+ DBG (1, "sanei_usb_get_endpoint: dn >= device number || dn < 0\n");
+ return 0;
+ }
+
+ switch (ep_type)
+ {
+ case USB_DIR_IN|USB_ENDPOINT_TYPE_BULK:
+ return devices[dn].bulk_in_ep;
+ case USB_DIR_OUT|USB_ENDPOINT_TYPE_BULK:
+ return devices[dn].bulk_out_ep;
+ case USB_DIR_IN|USB_ENDPOINT_TYPE_ISOCHRONOUS:
+ return devices[dn].iso_in_ep;
+ case USB_DIR_OUT|USB_ENDPOINT_TYPE_ISOCHRONOUS:
+ return devices[dn].iso_out_ep;
+ case USB_DIR_IN|USB_ENDPOINT_TYPE_INTERRUPT:
+ return devices[dn].int_in_ep;
+ case USB_DIR_OUT|USB_ENDPOINT_TYPE_INTERRUPT:
+ return devices[dn].int_out_ep;
+ case USB_DIR_IN|USB_ENDPOINT_TYPE_CONTROL:
+ return devices[dn].control_in_ep;
+ case USB_DIR_OUT|USB_ENDPOINT_TYPE_CONTROL:
+ return devices[dn].control_out_ep;
+ default:
+ return 0;
+ }
+}
+
+SANE_Status
+sanei_usb_open (SANE_String_Const devname, SANE_Int * dn)
+{
+ int devcount;
+ SANE_Bool found = SANE_FALSE;
+ int c, i, a;
+
+ DBG (5, "sanei_usb_open: trying to open device `%s'\n", devname);
+ if (!dn)
+ {
+ DBG (1, "sanei_usb_open: can't open `%s': dn == NULL\n", devname);
+ return SANE_STATUS_INVAL;
+ }
+
+ for (devcount = 0;
+ devcount < device_number && devices[devcount].devname != 0;
+ devcount++)
+ {
+ if(devices[devcount].missing)
+ continue;
+
+ if (strcmp (devices[devcount].devname, devname) == 0)
+ {
+ if (devices[devcount].open)
+ {
+ DBG (1, "sanei_usb_open: device `%s' already open\n", devname);
+ return SANE_STATUS_INVAL;
+ }
+ found = SANE_TRUE;
+ break;
+ }
+ }
+
+ if (!found)
+ {
+ DBG (1, "sanei_usb_open: can't find device `%s' in list\n", devname);
+ return SANE_STATUS_INVAL;
+ }
+
+ if (devices[devcount].method == sanei_usb_method_libusb)
+ {
+#ifdef HAVE_LIBUSB
+ struct usb_device *dev;
+ struct usb_interface_descriptor *interface;
+ int result, num;
+
+ devices[devcount].libusb_handle =
+ usb_open (devices[devcount].libusb_device);
+ if (!devices[devcount].libusb_handle)
+ {
+ SANE_Status status = SANE_STATUS_INVAL;
+
+ DBG (1, "sanei_usb_open: can't open device `%s': %s\n",
+ devname, strerror (errno));
+ 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;
+ }
+ return status;
+ }
+
+ dev = usb_device (devices[devcount].libusb_handle);
+
+#if defined (__linux__)
+ if (!getenv ("SANEI_USB_KEEP_USBLP"))
+ {
+ memset (devices[devcount].driver_detached, 0, MAX_INTERFACE_FLAGS);
+ detach_usblp_driver (devices[devcount].libusb_handle,
+ devices[devcount].driver_detached);
+ }
+#endif /* __linux__ */
+
+ /* Set the configuration */
+ if (!dev->config)
+ {
+ DBG (1, "sanei_usb_open: device `%s' not configured?\n", devname);
+ return SANE_STATUS_INVAL;
+ }
+ if (dev->descriptor.bNumConfigurations > 1)
+ {
+ DBG (3, "sanei_usb_open: more than one "
+ "configuration (%d), choosing first config (%d)\n",
+ dev->descriptor.bNumConfigurations,
+ dev->config[0].bConfigurationValue);
+
+ result = usb_set_configuration (devices[devcount].libusb_handle,
+ dev->config[0].bConfigurationValue);
+ 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 (3, "Maybe the kernel scanner driver or usblp claims "
+ "the interface? Ignoring this error...\n");
+ status = SANE_STATUS_GOOD;
+ }
+ if (status != SANE_STATUS_GOOD)
+ {
+ usb_close (devices[devcount].libusb_handle);
+ return status;
+ }
+ }
+ }
+
+ /* Claim the interface */
+ result = usb_claim_interface (devices[devcount].libusb_handle,
+ devices[devcount].interface_nr);
+ 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;
+ }
+
+ /* Loop through all of the configurations */
+ for (c = 0; c < dev->descriptor.bNumConfigurations; c++)
+ {
+ /* Loop through all of the interfaces */
+ for (i = 0; i < dev->config[c].bNumInterfaces; i++)
+ {
+ /* Loop through all of the alternate settings */
+ for (a = 0; a < dev->config[c].interface[i].num_altsetting; a++)
+ {
+ DBG (5, "sanei_usb_open: configuration nr: %d\n", c);
+ DBG (5, "sanei_usb_open: interface nr: %d\n", i);
+ DBG (5, "sanei_usb_open: alt_setting nr: %d\n", a);
+
+ /* Start by interfaces found in sanei_usb_init */
+ if (c == 0 && i != devices[devcount].interface_nr)
+ {
+ DBG (5, "sanei_usb_open: interface %d not detected as "
+ "a scanner by sanei_usb_init, ignoring.\n", i);
+ continue;
+ }
+
+ interface = &dev->config[c].interface[i].altsetting[a];
+
+ /* 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];
+ DBG (5, "sanei_usb_open: endpoint nr: %d\n", num);
+ transfer_type =
+ endpoint->bmAttributes & USB_ENDPOINT_TYPE_MASK;
+ address =
+ endpoint->
+ bEndpointAddress & USB_ENDPOINT_ADDRESS_MASK;
+ direction =
+ endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK;
+
+ DBG (5, "sanei_usb_open: direction: %d\n", direction);
+ transfer_type =
+ endpoint->bmAttributes & USB_ENDPOINT_TYPE_MASK;
+
+ DBG (5,
+ "sanei_usb_open: address: %d transfertype: %d\n",
+ address, transfer_type);
+
+
+ /* save the endpoints we need later */
+ if (transfer_type == USB_ENDPOINT_TYPE_INTERRUPT)
+ {
+ DBG (5,
+ "sanei_usb_open: found interrupt-%s endpoint (address 0x%02x)\n",
+ direction ? "in" : "out", address);
+ if (direction) /* in */
+ {
+ if (devices[devcount].int_in_ep)
+ DBG (3,
+ "sanei_usb_open: we already have a int-in endpoint "
+ "(address: 0x%02x), ignoring the new one\n",
+ devices[devcount].int_in_ep);
+ else
+ devices[devcount].int_in_ep =
+ endpoint->bEndpointAddress;
+ }
+ else
+ {
+ if (devices[devcount].int_out_ep)
+ DBG (3,
+ "sanei_usb_open: we already have a int-out endpoint "
+ "(address: 0x%02x), ignoring the new one\n",
+ devices[devcount].int_out_ep);
+ else
+ devices[devcount].int_out_ep =
+ endpoint->bEndpointAddress;
+ }
+ }
+ else if (transfer_type == USB_ENDPOINT_TYPE_BULK)
+ {
+ DBG (5,
+ "sanei_usb_open: found bulk-%s endpoint (address 0x%02x)\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: 0x%02x), ignoring the new one\n",
+ devices[devcount].bulk_in_ep);
+ else
+ devices[devcount].bulk_in_ep =
+ endpoint->bEndpointAddress;
+ }
+ else
+ {
+ if (devices[devcount].bulk_out_ep)
+ DBG (3,
+ "sanei_usb_open: we already have a bulk-out endpoint "
+ "(address: 0x%02x), ignoring the new one\n",
+ devices[devcount].bulk_out_ep);
+ else
+ devices[devcount].bulk_out_ep =
+ endpoint->bEndpointAddress;
+ }
+ }
+ else if (transfer_type == USB_ENDPOINT_TYPE_ISOCHRONOUS)
+ {
+ DBG (5,
+ "sanei_usb_open: found isochronous-%s endpoint (address 0x%02x)\n",
+ direction ? "in" : "out", address);
+ if (direction) /* in */
+ {
+ if (devices[devcount].iso_in_ep)
+ DBG (3,
+ "sanei_usb_open: we already have a isochronous-in endpoint "
+ "(address: 0x%02x), ignoring the new one\n",
+ devices[devcount].iso_in_ep);
+ else
+ devices[devcount].iso_in_ep =
+ endpoint->bEndpointAddress;
+ }
+ else
+ {
+ if (devices[devcount].iso_out_ep)
+ DBG (3,
+ "sanei_usb_open: we already have a isochronous-out endpoint "
+ "(address: 0x%02x), ignoring the new one\n",
+ devices[devcount].iso_out_ep);
+ else
+ devices[devcount].iso_out_ep =
+ endpoint->bEndpointAddress;
+ }
+ }
+ else if (transfer_type == USB_ENDPOINT_TYPE_CONTROL)
+ {
+ DBG (5,
+ "sanei_usb_open: found control-%s endpoint (address 0x%02x)\n",
+ direction ? "in" : "out", address);
+ if (direction) /* in */
+ {
+ if (devices[devcount].control_in_ep)
+ DBG (3,
+ "sanei_usb_open: we already have a control-in endpoint "
+ "(address: 0x%02x), ignoring the new one\n",
+ devices[devcount].control_in_ep);
+ else
+ devices[devcount].control_in_ep =
+ endpoint->bEndpointAddress;
+ }
+ else
+ {
+ if (devices[devcount].control_out_ep)
+ DBG (3,
+ "sanei_usb_open: we already have a control-out endpoint "
+ "(address: 0x%02x), ignoring the new one\n",
+ devices[devcount].control_out_ep);
+ else
+ devices[devcount].control_out_ep =
+ endpoint->bEndpointAddress;
+ }
+ }
+ }
+ }
+ }
+ }
+
+#elif defined(HAVE_LIBUSB_1_0) /* libusb-1.0 */
+
+ int config;
+ libusb_device *dev;
+ struct libusb_device_descriptor desc;
+ struct libusb_config_descriptor *config0;
+ int result, num;
+
+ dev = devices[devcount].lu_device;
+
+ result = libusb_open (dev, &devices[devcount].lu_handle);
+ if (result < 0)
+ {
+ SANE_Status status = SANE_STATUS_INVAL;
+
+ DBG (1, "sanei_usb_open: can't open device `%s': %s\n",
+ devname, sanei_libusb_strerror (result));
+ if (result == LIBUSB_ERROR_ACCESS)
+ {
+ DBG (1, "Make sure you run as root or set appropriate "
+ "permissions\n");
+ status = SANE_STATUS_ACCESS_DENIED;
+ }
+ else if (result == LIBUSB_ERROR_BUSY)
+ {
+ DBG (1, "Maybe the kernel scanner driver claims the "
+ "scanner's interface?\n");
+ status = SANE_STATUS_DEVICE_BUSY;
+ }
+ else if (result == LIBUSB_ERROR_NO_MEM)
+ {
+ status = SANE_STATUS_NO_MEM;
+ }
+ return status;
+ }
+
+#if defined (__linux__)
+ if (!getenv ("SANEI_USB_KEEP_USBLP"))
+ {
+ memset (devices[devcount].driver_detached, 0, MAX_INTERFACE_FLAGS);
+ detach_usblp_driver (devices[devcount].lu_handle,
+ devices[devcount].driver_detached);
+ }
+#endif /* __linux__ */
+
+ result = libusb_get_configuration (devices[devcount].lu_handle, &config);
+ if (result < 0)
+ {
+ DBG (1,
+ "sanei_usb_open: could not get configuration for device `%s' (err %d)\n",
+ devname, result);
+ return SANE_STATUS_INVAL;
+ }
+
+ if (config == 0)
+ {
+ DBG (1, "sanei_usb_open: device `%s' not configured?\n", devname);
+ return SANE_STATUS_INVAL;
+ }
+
+ result = libusb_get_device_descriptor (dev, &desc);
+ if (result < 0)
+ {
+ DBG (1,
+ "sanei_usb_open: could not get device descriptor for device `%s' (err %d)\n",
+ devname, result);
+ return SANE_STATUS_INVAL;
+ }
+
+ result = libusb_get_config_descriptor (dev, 0, &config0);
+ if (result < 0)
+ {
+ DBG (1,
+ "sanei_usb_open: could not get config[0] descriptor for device `%s' (err %d)\n",
+ devname, result);
+ return SANE_STATUS_INVAL;
+ }
+
+ /* Set the configuration */
+ if (desc.bNumConfigurations > 1)
+ {
+ DBG (3, "sanei_usb_open: more than one "
+ "configuration (%d), choosing first config (%d)\n",
+ desc.bNumConfigurations,
+ config0->bConfigurationValue);
+
+ result = 0;
+ if (config != config0->bConfigurationValue)
+ result = libusb_set_configuration (devices[devcount].lu_handle,
+ config0->bConfigurationValue);
+
+ if (result < 0)
+ {
+ SANE_Status status = SANE_STATUS_INVAL;
+
+ DBG (1, "sanei_usb_open: libusb complained: %s\n",
+ sanei_libusb_strerror (result));
+ if (result == LIBUSB_ERROR_ACCESS)
+ {
+ DBG (1, "Make sure you run as root or set appropriate "
+ "permissions\n");
+ status = SANE_STATUS_ACCESS_DENIED;
+ }
+ else if (result == LIBUSB_ERROR_BUSY)
+ {
+ DBG (3, "Maybe the kernel scanner driver or usblp claims "
+ "the interface? Ignoring this error...\n");
+ status = SANE_STATUS_GOOD;
+ }
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ libusb_close (devices[devcount].lu_handle);
+ libusb_free_config_descriptor (config0);
+ return status;
+ }
+ }
+ }
+ libusb_free_config_descriptor (config0);
+
+ /* Claim the interface */
+ result = libusb_claim_interface (devices[devcount].lu_handle,
+ devices[devcount].interface_nr);
+ if (result < 0)
+ {
+ SANE_Status status = SANE_STATUS_INVAL;
+
+ DBG (1, "sanei_usb_open: libusb complained: %s\n",
+ sanei_libusb_strerror (result));
+ if (result == LIBUSB_ERROR_ACCESS)
+ {
+ DBG (1, "Make sure you run as root or set appropriate "
+ "permissions\n");
+ status = SANE_STATUS_ACCESS_DENIED;
+ }
+ else if (result == LIBUSB_ERROR_BUSY)
+ {
+ DBG (1, "Maybe the kernel scanner driver claims the "
+ "scanner's interface?\n");
+ status = SANE_STATUS_DEVICE_BUSY;
+ }
+
+ libusb_close (devices[devcount].lu_handle);
+ return status;
+ }
+
+ /* Loop through all of the configurations */
+ for (c = 0; c < desc.bNumConfigurations; c++)
+ {
+ struct libusb_config_descriptor *config;
+
+ result = libusb_get_config_descriptor (dev, c, &config);
+ if (result < 0)
+ {
+ DBG (1,
+ "sanei_usb_open: could not get config[%d] descriptor for device `%s' (err %d)\n",
+ c, devname, result);
+ continue;
+ }
+
+ /* Loop through all of the interfaces */
+ for (i = 0; i < config->bNumInterfaces; i++)
+ {
+ /* Loop through all of the alternate settings */
+ for (a = 0; a < config->interface[i].num_altsetting; a++)
+ {
+ const struct libusb_interface_descriptor *interface;
+
+ DBG (5, "sanei_usb_open: configuration nr: %d\n", c);
+ DBG (5, "sanei_usb_open: interface nr: %d\n", i);
+ DBG (5, "sanei_usb_open: alt_setting nr: %d\n", a);
+
+ /* Start by interfaces found in sanei_usb_init */
+ if (c == 0 && i != devices[devcount].interface_nr)
+ {
+ DBG (5, "sanei_usb_open: interface %d not detected as "
+ "a scanner by sanei_usb_init, ignoring.\n", i);
+ continue;
+ }
+
+ interface = &config->interface[i].altsetting[a];
+
+ /* Now we look for usable endpoints */
+ for (num = 0; num < interface->bNumEndpoints; num++)
+ {
+ const struct libusb_endpoint_descriptor *endpoint;
+ int address, direction, transfer_type;
+
+ endpoint = &interface->endpoint[num];
+ DBG (5, "sanei_usb_open: endpoint nr: %d\n", num);
+
+ transfer_type = endpoint->bmAttributes & LIBUSB_TRANSFER_TYPE_MASK;
+ address = endpoint->bEndpointAddress & LIBUSB_ENDPOINT_ADDRESS_MASK;
+ direction = endpoint->bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK;
+
+ DBG (5, "sanei_usb_open: direction: %d\n", direction);
+ DBG (5, "sanei_usb_open: address: %d transfertype: %d\n",
+ address, transfer_type);
+
+ /* save the endpoints we need later */
+ if (transfer_type == LIBUSB_TRANSFER_TYPE_INTERRUPT)
+ {
+ DBG (5,
+ "sanei_usb_open: found interrupt-%s endpoint (address 0x%02x)\n",
+ direction ? "in" : "out", address);
+ if (direction) /* in */
+ {
+ if (devices[devcount].int_in_ep)
+ DBG (3,
+ "sanei_usb_open: we already have a int-in endpoint "
+ "(address: 0x%02x), ignoring the new one\n",
+ devices[devcount].int_in_ep);
+ else
+ devices[devcount].int_in_ep = endpoint->bEndpointAddress;
+ }
+ else
+ {
+ if (devices[devcount].int_out_ep)
+ DBG (3,
+ "sanei_usb_open: we already have a int-out endpoint "
+ "(address: 0x%02x), ignoring the new one\n",
+ devices[devcount].int_out_ep);
+ else
+ devices[devcount].int_out_ep = endpoint->bEndpointAddress;
+ }
+ }
+ else if (transfer_type == LIBUSB_TRANSFER_TYPE_BULK)
+ {
+ DBG (5,
+ "sanei_usb_open: found bulk-%s endpoint (address 0x%02x)\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: 0x%02x), ignoring the new one\n",
+ devices[devcount].bulk_in_ep);
+ else
+ devices[devcount].bulk_in_ep = endpoint->bEndpointAddress;
+ }
+ else
+ {
+ if (devices[devcount].bulk_out_ep)
+ DBG (3,
+ "sanei_usb_open: we already have a bulk-out endpoint "
+ "(address: 0x%02x), ignoring the new one\n",
+ devices[devcount].bulk_out_ep);
+ else
+ devices[devcount].bulk_out_ep = endpoint->bEndpointAddress;
+ }
+ }
+ else if (transfer_type == LIBUSB_TRANSFER_TYPE_ISOCHRONOUS)
+ {
+ DBG (5,
+ "sanei_usb_open: found isochronous-%s endpoint (address 0x%02x)\n",
+ direction ? "in" : "out", address);
+ if (direction) /* in */
+ {
+ if (devices[devcount].iso_in_ep)
+ DBG (3,
+ "sanei_usb_open: we already have a isochronous-in endpoint "
+ "(address: 0x%02x), ignoring the new one\n",
+ devices[devcount].iso_in_ep);
+ else
+ devices[devcount].iso_in_ep = endpoint->bEndpointAddress;
+ }
+ else
+ {
+ if (devices[devcount].iso_out_ep)
+ DBG (3,
+ "sanei_usb_open: we already have a isochronous-out endpoint "
+ "(address: 0x%02x), ignoring the new one\n",
+ devices[devcount].iso_out_ep);
+ else
+ devices[devcount].iso_out_ep = endpoint->bEndpointAddress;
+ }
+ }
+ else if (transfer_type == LIBUSB_TRANSFER_TYPE_CONTROL)
+ {
+ DBG (5,
+ "sanei_usb_open: found control-%s endpoint (address 0x%02x)\n",
+ direction ? "in" : "out", address);
+ if (direction) /* in */
+ {
+ if (devices[devcount].control_in_ep)
+ DBG (3,
+ "sanei_usb_open: we already have a control-in endpoint "
+ "(address: 0x%02x), ignoring the new one\n",
+ devices[devcount].control_in_ep);
+ else
+ devices[devcount].control_in_ep = endpoint->bEndpointAddress;
+ }
+ else
+ {
+ if (devices[devcount].control_out_ep)
+ DBG (3,
+ "sanei_usb_open: we already have a control-out endpoint "
+ "(address: 0x%02x), ignoring the new one\n",
+ devices[devcount].control_out_ep);
+ else
+ devices[devcount].control_out_ep = endpoint->bEndpointAddress;
+ }
+ }
+ }
+ }
+ }
+
+ libusb_free_config_descriptor (config);
+ }
+
+#else /* not HAVE_LIBUSB && not HAVE_LIBUSB_1_0 */
+ DBG (1, "sanei_usb_open: can't open device `%s': "
+ "libusb support missing\n", devname);
+ return SANE_STATUS_UNSUPPORTED;
+#endif /* not HAVE_LIBUSB && not HAVE_LIBUSB_1_0 */
+ }
+ else if (devices[devcount].method == sanei_usb_method_scanner_driver)
+ {
+ long int flag;
+ /* Using kernel scanner driver */
+ devices[devcount].fd = -1;
+#ifdef HAVE_RESMGR
+ devices[devcount].fd = rsm_open_device (devname, O_RDWR);
+#endif
+ if (devices[devcount].fd == -1)
+ 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;
+ }
+ flag = fcntl (devices[devcount].fd, F_GETFD);
+ if (flag >= 0)
+ {
+ if (fcntl (devices[devcount].fd, F_SETFD, flag | FD_CLOEXEC) < 0)
+ DBG (1, "sanei_usb_open: fcntl of `%s' failed: %s\n",
+ devname, strerror (errno));
+ }
+ }
+ else if (devices[devcount].method == sanei_usb_method_usbcalls)
+ {
+#ifdef HAVE_USBCALLS
+ CHAR ucData[2048];
+ struct usb_device_descriptor *pDevDesc;
+ struct usb_config_descriptor *pCfgDesc;
+ struct usb_interface_descriptor *interface;
+ struct usb_endpoint_descriptor *endpoint;
+ struct usb_descriptor_header *pDescHead;
+
+ ULONG ulBufLen;
+ ulBufLen = sizeof(ucData);
+ memset(&ucData,0,sizeof(ucData));
+
+ int result, num,rc;
+ int address, direction, transfer_type;
+
+ DBG (5, "devname = %s, devcount = %d\n",devices[devcount].devname,devcount);
+ DBG (5, "USBCalls device number to open = %d\n",devices[devcount].fd);
+ DBG (5, "USBCalls Vendor/Product to open = 0x%04x/0x%04x\n",
+ devices[devcount].vendor,devices[devcount].product);
+
+ rc = UsbOpen (&dh,
+ devices[devcount].vendor,
+ devices[devcount].product,
+ USB_ANY_PRODUCTVERSION,
+ USB_OPEN_FIRST_UNUSED);
+ DBG (1, "sanei_usb_open: UsbOpen rc = %d\n",rc);
+ if (rc!=0)
+ {
+ SANE_Status status = SANE_STATUS_INVAL;
+ DBG (1, "sanei_usb_open: can't open device `%s': %s\n",
+ devname, strerror (rc));
+ return status;
+ }
+ rc = UsbQueryDeviceReport( devices[devcount].fd,
+ &ulBufLen,
+ ucData);
+ DBG (1, "sanei_usb_open: UsbQueryDeviceReport rc = %d\n",rc);
+ pDevDesc = (struct usb_device_descriptor*)ucData;
+ pCfgDesc = (struct usb_config_descriptor*) (ucData+sizeof(struct usb_device_descriptor));
+ UCHAR *pCurPtr = (UCHAR*) pCfgDesc;
+ UCHAR *pEndPtr = pCurPtr+ pCfgDesc->wTotalLength;
+ pDescHead = (struct usb_descriptor_header *) (pCurPtr+pCfgDesc->bLength);
+ /* Set the configuration */
+ if (pDevDesc->bNumConfigurations > 1)
+ {
+ DBG (3, "sanei_usb_open: more than one "
+ "configuration (%d), choosing first config (%d)\n",
+ pDevDesc->bNumConfigurations,
+ pCfgDesc->bConfigurationValue);
+ }
+ DBG (5, "UsbDeviceSetConfiguration parameters: dh = %p, bConfigurationValue = %d\n",
+ dh,pCfgDesc->bConfigurationValue);
+ result = UsbDeviceSetConfiguration (dh,
+ pCfgDesc->bConfigurationValue);
+ DBG (1, "sanei_usb_open: UsbDeviceSetConfiguration rc = %d\n",result);
+ if (result)
+ {
+ SANE_Status status = SANE_STATUS_INVAL;
+ DBG (1, "sanei_usb_open: usbcalls complained on UsbDeviceSetConfiguration, rc= %d\n", result);
+ status = SANE_STATUS_ACCESS_DENIED;
+ UsbClose (dh);
+ return status;
+ }
+
+ /* Now we look for usable endpoints */
+
+ for (pDescHead = (struct usb_descriptor_header *) (pCurPtr+pCfgDesc->bLength);
+ pDescHead;pDescHead = GetNextDescriptor(pDescHead,pEndPtr) )
+ {
+ switch(pDescHead->bDescriptorType)
+ {
+ case USB_DT_INTERFACE:
+ interface = (struct usb_interface_descriptor *) pDescHead;
+ DBG (5, "Found %d endpoints\n",interface->bNumEndpoints);
+ DBG (5, "bAlternateSetting = %d\n",interface->bAlternateSetting);
+ break;
+ case USB_DT_ENDPOINT:
+ endpoint = (struct usb_endpoint_descriptor*)pDescHead;
+ address = endpoint->bEndpointAddress;
+ direction = endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK;
+ transfer_type = endpoint->bmAttributes & USB_ENDPOINT_TYPE_MASK;
+ /* save the endpoints we need later */
+ if (transfer_type == USB_ENDPOINT_TYPE_INTERRUPT)
+ {
+ DBG (5, "sanei_usb_open: found interupt-%s endpoint (address %2x)\n",
+ direction ? "in" : "out", address);
+ if (direction) /* in */
+ {
+ if (devices[devcount].int_in_ep)
+ DBG (3, "sanei_usb_open: we already have a int-in endpoint "
+ "(address: %d), ignoring the new one\n",
+ devices[devcount].int_in_ep);
+ else
+ devices[devcount].int_in_ep = endpoint->bEndpointAddress;
+ }
+ else
+ if (devices[devcount].int_out_ep)
+ DBG (3, "sanei_usb_open: we already have a int-out endpoint "
+ "(address: %d), ignoring the new one\n",
+ devices[devcount].int_out_ep);
+ else
+ devices[devcount].int_out_ep = endpoint->bEndpointAddress;
+ }
+ else if (transfer_type == USB_ENDPOINT_TYPE_BULK)
+ {
+ DBG (5, "sanei_usb_open: found bulk-%s endpoint (address %2x)\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 = endpoint->bEndpointAddress;
+ }
+ 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 = endpoint->bEndpointAddress;
+ }
+ }
+ /* ignore currently unsupported endpoints */
+ else {
+ 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;
+ }
+ break;
+ }
+ }
+#else
+ DBG (1, "sanei_usb_open: can't open device `%s': "
+ "usbcalls support missing\n", devname);
+ return SANE_STATUS_UNSUPPORTED;
+#endif /* HAVE_USBCALLS */
+ }
+ else
+ {
+ DBG (1, "sanei_usb_open: access method %d not implemented\n",
+ devices[devcount].method);
+ return SANE_STATUS_INVAL;
+ }
+
+ devices[devcount].open = SANE_TRUE;
+ *dn = devcount;
+ DBG (3, "sanei_usb_open: opened usb device `%s' (*dn=%d)\n",
+ devname, devcount);
+ return SANE_STATUS_GOOD;
+}
+
+void
+sanei_usb_close (SANE_Int dn)
+{
+ DBG (5, "sanei_usb_close: closing device %d\n", dn);
+ if (dn >= device_number || dn < 0)
+ {
+ DBG (1, "sanei_usb_close: dn >= device number || dn < 0\n");
+ return;
+ }
+ if (!devices[dn].open)
+ {
+ DBG (1, "sanei_usb_close: device %d already closed or never opened\n",
+ dn);
+ return;
+ }
+ if (devices[dn].method == sanei_usb_method_scanner_driver)
+ close (devices[dn].fd);
+ else if (devices[dn].method == sanei_usb_method_usbcalls)
+ {
+#ifdef HAVE_USBCALLS
+ int rc;
+ rc=UsbClose (dh);
+ DBG (5,"rc of UsbClose = %d\n",rc);
+#else
+ DBG (1, "sanei_usb_close: usbcalls support missing\n");
+#endif
+ }
+ else
+#ifdef HAVE_LIBUSB
+ {
+#if 0
+ /* Should only be done in case of a stall */
+ usb_clear_halt (devices[dn].libusb_handle, devices[dn].bulk_in_ep);
+ usb_clear_halt (devices[dn].libusb_handle, devices[dn].bulk_out_ep);
+ usb_clear_halt (devices[dn].libusb_handle, devices[dn].iso_in_ep);
+ /* be careful, we don't know if we are in DATA0 stage now */
+ usb_resetep (devices[dn].libusb_handle, devices[dn].bulk_in_ep);
+ usb_resetep (devices[dn].libusb_handle, devices[dn].bulk_out_ep);
+ usb_resetep (devices[dn].libusb_handle, devices[dn].iso_in_ep);
+#endif /* ! __macos_x__ */
+ usb_release_interface (devices[dn].libusb_handle,
+ devices[dn].interface_nr);
+
+#if defined (__linux__)
+ if (!getenv ("SANEI_USB_KEEP_USBLP"))
+ {
+ attach_usblp_driver (devices[dn].libusb_handle,
+ devices[dn].driver_detached);
+ }
+#endif /* __linux__*/
+
+ usb_close (devices[dn].libusb_handle);
+ }
+#elif defined(HAVE_LIBUSB_1_0)
+ {
+#if 0
+ /* Should only be done in case of a stall */
+ libusb_clear_halt (devices[dn].lu_handle, devices[dn].bulk_in_ep);
+ libusb_clear_halt (devices[dn].lu_handle, devices[dn].bulk_out_ep);
+ libusb_clear_halt (devices[dn].lu_handle, devices[dn].iso_in_ep);
+#endif /* ! __macos_x__ */
+ libusb_release_interface (devices[dn].lu_handle,
+ devices[dn].interface_nr);
+
+#if defined (__linux__)
+ if (!getenv ("SANEI_USB_KEEP_USBLP"))
+ {
+ attach_usblp_driver (devices[dn].lu_handle,
+ devices[dn].driver_detached);
+ }
+#endif /* __linux__*/
+
+ libusb_close (devices[dn].lu_handle);
+ }
+#else /* not HAVE_LIBUSB && not HAVE_LIBUSB_1_0 */
+ DBG (1, "sanei_usb_close: libusb support missing\n");
+#endif
+ devices[dn].open = SANE_FALSE;
+ return;
+}
+
+void
+sanei_usb_set_timeout (SANE_Int timeout)
+{
+#if defined(HAVE_LIBUSB) || defined(HAVE_LIBUSB_1_0)
+ libusb_timeout = timeout;
+#else
+ DBG (1, "sanei_usb_set_timeout: libusb support missing\n");
+#endif /* HAVE_LIBUSB || HAVE_LIBUSB_1_0 */
+}
+
+SANE_Status
+sanei_usb_clear_halt (SANE_Int dn)
+{
+#ifdef HAVE_LIBUSB
+ int ret;
+
+ if (dn >= device_number || dn < 0)
+ {
+ DBG (1, "sanei_usb_clear_halt: dn >= device number || dn < 0\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ ret = usb_clear_halt (devices[dn].libusb_handle, devices[dn].bulk_in_ep);
+ if (ret){
+ DBG (1, "sanei_usb_clear_halt: BULK_IN ret=%d\n", ret);
+ return SANE_STATUS_INVAL;
+ }
+
+ ret = usb_clear_halt (devices[dn].libusb_handle, devices[dn].bulk_out_ep);
+ if (ret){
+ DBG (1, "sanei_usb_clear_halt: BULK_OUT ret=%d\n", ret);
+ return SANE_STATUS_INVAL;
+ }
+
+ /* be careful, we don't know if we are in DATA0 stage now
+ ret = usb_resetep (devices[dn].libusb_handle, devices[dn].bulk_in_ep);
+ ret = usb_resetep (devices[dn].libusb_handle, devices[dn].bulk_out_ep);
+ */
+#elif defined(HAVE_LIBUSB_1_0)
+ int ret;
+
+ if (dn >= device_number || dn < 0)
+ {
+ DBG (1, "sanei_usb_clear_halt: dn >= device number || dn < 0\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ ret = libusb_clear_halt (devices[dn].lu_handle, devices[dn].bulk_in_ep);
+ if (ret){
+ DBG (1, "sanei_usb_clear_halt: BULK_IN ret=%d\n", ret);
+ return SANE_STATUS_INVAL;
+ }
+
+ ret = libusb_clear_halt (devices[dn].lu_handle, devices[dn].bulk_out_ep);
+ if (ret){
+ DBG (1, "sanei_usb_clear_halt: BULK_OUT ret=%d\n", ret);
+ return SANE_STATUS_INVAL;
+ }
+#else /* not HAVE_LIBUSB && not HAVE_LIBUSB_1_0 */
+ DBG (1, "sanei_usb_clear_halt: libusb support missing\n");
+#endif /* HAVE_LIBUSB || HAVE_LIBUSB_1_0 */
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sanei_usb_reset (SANE_Int dn)
+{
+#ifdef HAVE_LIBUSB
+ int ret;
+
+ ret = usb_reset (devices[dn].libusb_handle);
+ if (ret){
+ DBG (1, "sanei_usb_reset: ret=%d\n", ret);
+ return SANE_STATUS_INVAL;
+ }
+
+#elif defined(HAVE_LIBUSB_1_0)
+ int ret;
+
+ ret = libusb_reset_device (devices[dn].lu_handle);
+ if (ret){
+ DBG (1, "sanei_usb_reset: ret=%d\n", ret);
+ return SANE_STATUS_INVAL;
+ }
+
+#else /* not HAVE_LIBUSB && not HAVE_LIBUSB_1_0 */
+ DBG (1, "sanei_usb_reset: libusb support missing\n");
+#endif /* HAVE_LIBUSB || HAVE_LIBUSB_1_0 */
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sanei_usb_read_bulk (SANE_Int dn, SANE_Byte * buffer, size_t * size)
+{
+ ssize_t read_size = 0;
+ int tries = 0;
+ const int max_tries = 3;
+ int need_retry = 0;
+
+ if (!size)
+ {
+ DBG (1, "sanei_usb_read_bulk: size == NULL\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ if (dn >= device_number || dn < 0)
+ {
+ DBG (1, "sanei_usb_read_bulk: dn >= device number || dn < 0\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ do
+ {
+ ++tries;
+ DBG (5, "sanei_usb_read_bulk: trying to read %lu bytes\n",
+ (unsigned long) *size);
+
+ if (devices[dn].method == sanei_usb_method_scanner_driver)
+ {
+ read_size = read (devices[dn].fd, buffer, *size);
+
+ if (read_size < 0)
+ DBG (1, "sanei_usb_read_bulk: read failed: %s\n",
+ strerror (errno));
+ }
+ else if (devices[dn].method == sanei_usb_method_libusb)
+#ifdef HAVE_LIBUSB
+ {
+ if (devices[dn].bulk_in_ep)
+ {
+ read_size = usb_bulk_read (devices[dn].libusb_handle,
+ devices[dn].bulk_in_ep, (char *) buffer,
+ (int) *size, libusb_timeout);
+
+ need_retry = errno;
+ if (read_size < 0)
+ {
+ DBG (1, "sanei_usb_read_bulk: read failed: %s\n",
+ strerror (errno));
+ if (-EPIPE == read_size)
+ {
+ usb_clear_halt (devices[dn].libusb_handle,
+ devices[dn].bulk_in_ep);
+ }
+ }
+ }
+ else
+ {
+ DBG (1, "sanei_usb_read_bulk: can't read without a bulk-in "
+ "endpoint\n");
+ return SANE_STATUS_INVAL;
+ }
+ }
+#elif defined(HAVE_LIBUSB_1_0)
+ {
+ if (devices[dn].bulk_in_ep)
+ {
+ int ret;
+ ret = libusb_bulk_transfer (devices[dn].lu_handle,
+ devices[dn].bulk_in_ep, buffer,
+ (int) *size, (int *) &read_size,
+ libusb_timeout);
+
+ if (LIBUSB_ERROR_PIPE == ret)
+ {
+ ret = libusb_clear_halt (devices[dn].lu_handle,
+ devices[dn].bulk_in_ep);
+ }
+ need_retry = ret;
+ if (ret < 0)
+ {
+ DBG (1, "sanei_usb_read_bulk: read failed: %s\n",
+ sanei_libusb_strerror (ret));
+
+ read_size = -1;
+ }
+ }
+ else
+ {
+ DBG (1, "sanei_usb_read_bulk: can't read without a bulk-in "
+ "endpoint\n");
+ return SANE_STATUS_INVAL;
+ }
+ }
+#else /* not HAVE_LIBUSB && not HAVE_LIBUSB_1_0 */
+ {
+ DBG (1, "sanei_usb_read_bulk: libusb support missing\n");
+ return SANE_STATUS_UNSUPPORTED;
+ }
+#endif /* not HAVE_LIBUSB */
+ else if (devices[dn].method == sanei_usb_method_usbcalls)
+ {
+#ifdef HAVE_USBCALLS
+ int rc;
+ char* buffer_ptr = (char*) buffer;
+ while (*size)
+ {
+ ULONG ulToRead = (*size>MAX_RW)?MAX_RW:*size;
+ ULONG ulNum = ulToRead;
+ DBG (5, "Entered usbcalls UsbBulkRead with dn = %d\n",dn);
+ DBG (5, "Entered usbcalls UsbBulkRead with dh = %p\n",dh);
+ DBG (5, "Entered usbcalls UsbBulkRead with bulk_in_ep = 0x%02x\n",devices[dn].bulk_in_ep);
+ DBG (5, "Entered usbcalls UsbBulkRead with interface_nr = %d\n",devices[dn].interface_nr);
+ DBG (5, "Entered usbcalls UsbBulkRead with usbcalls_timeout = %d\n",usbcalls_timeout);
+
+ if (devices[dn].bulk_in_ep){
+ rc = UsbBulkRead (dh, devices[dn].bulk_in_ep, devices[dn].interface_nr,
+ &ulToRead, buffer_ptr, usbcalls_timeout);
+ DBG (1, "sanei_usb_read_bulk: rc = %d\n",rc);}
+ else
+ {
+ DBG (1, "sanei_usb_read_bulk: can't read without a bulk-in endpoint\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (rc || (ulNum!=ulToRead)) return SANE_STATUS_INVAL;
+ *size -=ulToRead;
+ buffer_ptr += ulToRead;
+ read_size += ulToRead;
+ }
+#else /* not HAVE_USBCALLS */
+ {
+ DBG (1, "sanei_usb_read_bulk: usbcalls support missing\n");
+ return SANE_STATUS_UNSUPPORTED;
+ }
+#endif /* not HAVE_USBCALLS */
+ }
+ else
+ {
+ DBG (1, "sanei_usb_read_bulk: access method %d not implemented\n",
+ devices[dn].method);
+ return SANE_STATUS_INVAL;
+ }
+ } while (read_size < 0
+#if defined(HAVE_LIBUSB)
+ && (EAGAIN == need_retry || EWOULDBLOCK == need_retry)
+#elif defined(HAVE_LIBUSB_1_0)
+ && LIBUSB_ERROR_BUSY == need_retry
+#endif /* HAVE_LIBUSB_1_0 */
+ && max_tries > tries);
+
+ if (read_size < 0)
+ {
+#ifdef HAVE_LIBUSB
+ if (devices[dn].method == sanei_usb_method_libusb)
+ usb_clear_halt (devices[dn].libusb_handle, devices[dn].bulk_in_ep);
+#elif defined(HAVE_LIBUSB_1_0)
+ if (devices[dn].method == sanei_usb_method_libusb)
+ libusb_clear_halt (devices[dn].lu_handle, devices[dn].bulk_in_ep);
+#endif
+ *size = 0;
+ return SANE_STATUS_IO_ERROR;
+ }
+ if (read_size == 0)
+ {
+ DBG (3, "sanei_usb_read_bulk: read returned EOF\n");
+ *size = 0;
+ return SANE_STATUS_EOF;
+ }
+ if (debug_level > 10)
+ print_buffer (buffer, read_size);
+ DBG (5, "sanei_usb_read_bulk: wanted %lu bytes, got %ld bytes\n",
+ (unsigned long) *size, (unsigned long) read_size);
+ *size = read_size;
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sanei_usb_write_bulk (SANE_Int dn, const SANE_Byte * buffer, size_t * size)
+{
+ ssize_t write_size = 0;
+ int tries = 0;
+ const int max_tries = 3;
+ int need_retry = 0;
+
+ if (!size)
+ {
+ DBG (1, "sanei_usb_write_bulk: size == NULL\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ if (dn >= device_number || dn < 0)
+ {
+ DBG (1, "sanei_usb_write_bulk: dn >= device number || dn < 0\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ do
+ {
+ ++tries;
+ DBG (5, "sanei_usb_write_bulk: trying to write %lu bytes\n",
+ (unsigned long) *size);
+ if (debug_level > 10)
+ print_buffer (buffer, *size);
+
+ if (devices[dn].method == sanei_usb_method_scanner_driver)
+ {
+ write_size = write (devices[dn].fd, buffer, *size);
+
+ if (write_size < 0)
+ DBG (1, "sanei_usb_write_bulk: write failed: %s\n",
+ strerror (errno));
+ }
+ else if (devices[dn].method == sanei_usb_method_libusb)
+#ifdef HAVE_LIBUSB
+ {
+ if (devices[dn].bulk_out_ep)
+ {
+ write_size = usb_bulk_write (devices[dn].libusb_handle,
+ devices[dn].bulk_out_ep,
+ (const char *) buffer,
+ (int) *size, libusb_timeout);
+ need_retry = errno;
+ if (write_size < 0)
+ {
+ DBG (1, "sanei_usb_write_bulk: write failed: %s\n",
+ strerror (errno));
+ if (-EPIPE == write_size)
+ {
+ usb_clear_halt (devices[dn].libusb_handle,
+ devices[dn].bulk_out_ep);
+ }
+ }
+ }
+ else
+ {
+ DBG (1, "sanei_usb_write_bulk: can't write without a bulk-out "
+ "endpoint\n");
+ return SANE_STATUS_INVAL;
+ }
+ }
+#elif defined(HAVE_LIBUSB_1_0)
+ {
+ if (devices[dn].bulk_out_ep)
+ {
+ int ret;
+ int trans_bytes;
+ ret = libusb_bulk_transfer (devices[dn].lu_handle,
+ devices[dn].bulk_out_ep,
+ (SANE_Byte *) buffer,
+ (int) *size, &trans_bytes,
+ libusb_timeout);
+ if (LIBUSB_ERROR_PIPE == ret)
+ {
+ ret = libusb_clear_halt (devices[dn].lu_handle,
+ devices[dn].bulk_out_ep);
+ }
+ need_retry = ret;
+ if (ret < 0)
+ {
+ DBG (1, "sanei_usb_write_bulk: write failed: %s\n",
+ sanei_libusb_strerror (ret));
+
+ write_size = -1;
+ }
+ else
+ write_size = trans_bytes;
+ }
+ else
+ {
+ DBG (1, "sanei_usb_write_bulk: can't write without a bulk-out "
+ "endpoint\n");
+ return SANE_STATUS_INVAL;
+ }
+ }
+#else /* not HAVE_LIBUSB && not HAVE_LIBUSB_1_0 */
+ {
+ DBG (1, "sanei_usb_write_bulk: libusb support missing\n");
+ return SANE_STATUS_UNSUPPORTED;
+ }
+#endif /* not HAVE_LIBUSB && not HAVE_LIBUSB_1_0 */
+ else if (devices[dn].method == sanei_usb_method_usbcalls)
+ {
+#ifdef HAVE_USBCALLS
+ int rc;
+ DBG (5, "Entered usbcalls UsbBulkWrite with dn = %d\n",dn);
+ DBG (5, "Entered usbcalls UsbBulkWrite with dh = %p\n",dh);
+ DBG (5, "Entered usbcalls UsbBulkWrite with bulk_out_ep = 0x%02x\n",devices[dn].bulk_out_ep);
+ DBG (5, "Entered usbcalls UsbBulkWrite with interface_nr = %d\n",devices[dn].interface_nr);
+ DBG (5, "Entered usbcalls UsbBulkWrite with usbcalls_timeout = %d\n",usbcalls_timeout);
+ while (*size)
+ {
+ ULONG ulToWrite = (*size>MAX_RW)?MAX_RW:*size;
+
+ DBG (5, "size requested to write = %lu, ulToWrite = %lu\n",(unsigned long) *size,ulToWrite);
+ if (devices[dn].bulk_out_ep){
+ rc = UsbBulkWrite (dh, devices[dn].bulk_out_ep, devices[dn].interface_nr,
+ ulToWrite, (char*) buffer, usbcalls_timeout);
+ DBG (1, "sanei_usb_write_bulk: rc = %d\n",rc);
+ }
+ else
+ {
+ DBG (1, "sanei_usb_write_bulk: can't read without a bulk-out endpoint\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (rc) return SANE_STATUS_INVAL;
+ *size -=ulToWrite;
+ buffer += ulToWrite;
+ write_size += ulToWrite;
+ DBG (5, "size = %d, write_size = %d\n",*size, write_size);
+ }
+#else /* not HAVE_USBCALLS */
+ {
+ DBG (1, "sanei_usb_write_bulk: usbcalls support missing\n");
+ return SANE_STATUS_UNSUPPORTED;
+ }
+#endif /* not HAVE_USBCALLS */
+ }
+ else
+ {
+ DBG (1, "sanei_usb_write_bulk: access method %d not implemented\n",
+ devices[dn].method);
+ return SANE_STATUS_INVAL;
+ }
+ } while (write_size < 0
+#if defined(HAVE_LIBUSB)
+ && (EAGAIN == need_retry || EWOULDBLOCK == need_retry)
+#elif defined(HAVE_LIBUSB_1_0)
+ && LIBUSB_ERROR_BUSY == need_retry
+#endif /* HAVE_LIBUSB_1_0 */
+ && max_tries > tries);
+
+ if (write_size < 0)
+ {
+ *size = 0;
+#ifdef HAVE_LIBUSB
+ if (devices[dn].method == sanei_usb_method_libusb)
+ usb_clear_halt (devices[dn].libusb_handle, devices[dn].bulk_out_ep);
+#elif defined(HAVE_LIBUSB_1_0)
+ if (devices[dn].method == sanei_usb_method_libusb)
+ libusb_clear_halt (devices[dn].lu_handle, devices[dn].bulk_out_ep);
+#endif
+ 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);
+ *size = write_size;
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sanei_usb_control_msg (SANE_Int dn, SANE_Int rtype, SANE_Int req,
+ SANE_Int value, SANE_Int index, size_t * size,
+ SANE_Byte * data)
+{
+ SANE_Int len = *size;
+
+ if (dn >= device_number || dn < 0)
+ {
+ DBG (1, "sanei_usb_control_msg: dn >= device number || dn < 0, dn=%d\n",
+ dn);
+ return SANE_STATUS_INVAL;
+ }
+
+ DBG (5, "sanei_usb_control_msg: rtype = 0x%02x, req = %d, value = %d, "
+ "index = %d, len = %d\n", rtype, req, value, index, len);
+ if (!(rtype & 0x80) && debug_level > 10)
+ print_buffer (data, len);
+
+ if (devices[dn].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;
+
+ if (ioctl (devices[dn].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;
+ }
+ /* Assuming ioctl() updates c.req.length to hold bytes read. Unverified. */
+ len = c.req.length;
+ *size = len;
+ if ((rtype & 0x80) && debug_level > 10)
+ print_buffer (data, len);
+ return SANE_STATUS_GOOD;
+#elif defined(__BEOS__)
+ struct usb_scanner_ioctl_ctrlmsg c;
+
+ c.req.request_type = rtype;
+ c.req.request = req;
+ c.req.value = value;
+ c.req.index = index;
+ c.req.length = len;
+ c.data = data;
+
+ if (ioctl (devices[dn].fd, B_SCANNER_IOCTL_CTRLMSG, &c) < 0)
+ {
+ DBG (5, "sanei_usb_control_msg: SCANNER_IOCTL_CTRLMSG error - %s\n",
+ strerror (errno));
+ return SANE_STATUS_IO_ERROR;
+ }
+ /* Assuming ioctl() updates c.req.length to hold bytes read. Unverified. */
+ len = c.req.length;
+ *size = len;
+ if ((rtype & 0x80) && debug_level > 10)
+ print_buffer (data, len);
+
+ 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[dn].method == sanei_usb_method_libusb)
+#ifdef HAVE_LIBUSB
+ {
+ int result;
+
+ result = usb_control_msg (devices[dn].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;
+ }
+ *size = len = result;
+ if ((rtype & 0x80) && debug_level > 10)
+ print_buffer (data, len);
+ return SANE_STATUS_GOOD;
+ }
+#elif defined(HAVE_LIBUSB_1_0)
+ {
+ int result;
+
+ result = libusb_control_transfer (devices[dn].lu_handle, rtype, req,
+ value, index, data, len,
+ libusb_timeout);
+ if (result < 0)
+ {
+ DBG (1, "sanei_usb_control_msg: libusb complained: %s\n",
+ sanei_libusb_strerror (result));
+ return SANE_STATUS_INVAL;
+ }
+ *size = len = result;
+ if ((rtype & 0x80) && debug_level > 10)
+ print_buffer (data, len);
+ return SANE_STATUS_GOOD;
+ }
+#else /* not HAVE_LIBUSB && not HAVE_LIBUSB_1_0*/
+ {
+ DBG (1, "sanei_usb_control_msg: libusb support missing\n");
+ return SANE_STATUS_UNSUPPORTED;
+ }
+#endif /* not HAVE_LIBUSB && not HAVE_LIBUSB_1_0 */
+ else if (devices[dn].method == sanei_usb_method_usbcalls)
+ {
+#ifdef HAVE_USBCALLS
+ int result;
+
+ result = UsbCtrlMessage (dh, rtype, req,
+ value, index, len, (char *) data,
+ usbcalls_timeout);
+ DBG (5, "rc of usb_control_msg = %d\n",result);
+ if (result < 0)
+ {
+ DBG (1, "sanei_usb_control_msg: usbcalls complained: %d\n",result);
+ return SANE_STATUS_INVAL;
+ }
+ /* Assuming UsbCtrlMessage() returns result to hold bytes read. Unverified. */
+ len = result;
+ *size = len;
+ if ((rtype & 0x80) && debug_level > 10)
+ print_buffer (data, len);
+ return SANE_STATUS_GOOD;
+#else /* not HAVE_USBCALLS */
+ {
+ DBG (1, "sanei_usb_control_msg: usbcalls support missing\n");
+ return SANE_STATUS_UNSUPPORTED;
+ }
+#endif /* not HAVE_USBCALLS */
+ }
+ else
+ {
+ DBG (1, "sanei_usb_control_msg: access method %d not implemented\n",
+ devices[dn].method);
+ return SANE_STATUS_UNSUPPORTED;
+ }
+}
+
+SANE_Status
+sanei_usb_read_int (SANE_Int dn, SANE_Byte * buffer, size_t * size)
+{
+ ssize_t read_size = 0;
+#if defined(HAVE_LIBUSB) || defined(HAVE_LIBUSB_1_0)
+ SANE_Bool stalled = SANE_FALSE;
+#endif
+
+ if (!size)
+ {
+ DBG (1, "sanei_usb_read_int: size == NULL\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ if (dn >= device_number || dn < 0)
+ {
+ DBG (1, "sanei_usb_read_int: dn >= device number || dn < 0\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ DBG (5, "sanei_usb_read_int: trying to read %lu bytes\n",
+ (unsigned long) *size);
+ if (devices[dn].method == sanei_usb_method_scanner_driver)
+ {
+ DBG (1, "sanei_usb_read_int: access method %d not implemented\n",
+ devices[dn].method);
+ return SANE_STATUS_INVAL;
+ }
+ else if (devices[dn].method == sanei_usb_method_libusb)
+#ifdef HAVE_LIBUSB
+ {
+ if (devices[dn].int_in_ep)
+ {
+ read_size = usb_interrupt_read (devices[dn].libusb_handle,
+ devices[dn].int_in_ep,
+ (char *) buffer, (int) *size,
+ libusb_timeout);
+
+ if (read_size < 0)
+ DBG (1, "sanei_usb_read_int: read failed: %s\n",
+ strerror (errno));
+
+ stalled = (read_size == -EPIPE);
+ }
+ else
+ {
+ DBG (1, "sanei_usb_read_int: can't read without an int "
+ "endpoint\n");
+ return SANE_STATUS_INVAL;
+ }
+ }
+#elif defined(HAVE_LIBUSB_1_0)
+ {
+ if (devices[dn].int_in_ep)
+ {
+ int ret;
+ int trans_bytes;
+ ret = libusb_interrupt_transfer (devices[dn].lu_handle,
+ devices[dn].int_in_ep,
+ buffer, (int) *size,
+ &trans_bytes, libusb_timeout);
+
+ if (ret < 0)
+ read_size = -1;
+ else
+ read_size = trans_bytes;
+
+ stalled = (ret == LIBUSB_ERROR_PIPE);
+ }
+ else
+ {
+ DBG (1, "sanei_usb_read_int: can't read without an int "
+ "endpoint\n");
+ return SANE_STATUS_INVAL;
+ }
+ }
+#else /* not HAVE_LIBUSB && not HAVE_LIBUSB_1_0 */
+ {
+ DBG (1, "sanei_usb_read_int: libusb support missing\n");
+ return SANE_STATUS_UNSUPPORTED;
+ }
+#endif /* not HAVE_LIBUSB && not HAVE_LIBUSB_1_0 */
+ else if (devices[dn].method == sanei_usb_method_usbcalls)
+ {
+#ifdef HAVE_USBCALLS
+ int rc;
+ USHORT usNumBytes=*size;
+ DBG (5, "Entered usbcalls UsbIrqStart with dn = %d\n",dn);
+ DBG (5, "Entered usbcalls UsbIrqStart with dh = %p\n",dh);
+ DBG (5, "Entered usbcalls UsbIrqStart with int_in_ep = 0x%02x\n",devices[dn].int_in_ep);
+ DBG (5, "Entered usbcalls UsbIrqStart with interface_nr = %d\n",devices[dn].interface_nr);
+ DBG (5, "Entered usbcalls UsbIrqStart with bytes to read = %u\n",usNumBytes);
+
+ if (devices[dn].int_in_ep){
+ rc = UsbIrqStart (dh,devices[dn].int_in_ep,devices[dn].interface_nr,
+ usNumBytes, (char *) buffer, pUsbIrqStartHev);
+ DBG (5, "rc of UsbIrqStart = %d\n",rc);
+ }
+ else
+ {
+ DBG (1, "sanei_usb_read_int: can't read without an int "
+ "endpoint\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (rc) return SANE_STATUS_INVAL;
+ read_size += usNumBytes;
+#else
+ DBG (1, "sanei_usb_read_int: usbcalls support missing\n");
+ return SANE_STATUS_UNSUPPORTED;
+#endif /* HAVE_USBCALLS */
+ }
+ else
+ {
+ DBG (1, "sanei_usb_read_int: access method %d not implemented\n",
+ devices[dn].method);
+ return SANE_STATUS_INVAL;
+ }
+
+ if (read_size < 0)
+ {
+#ifdef HAVE_LIBUSB
+ if (devices[dn].method == sanei_usb_method_libusb)
+ if (stalled)
+ usb_clear_halt (devices[dn].libusb_handle, devices[dn].int_in_ep);
+#elif defined(HAVE_LIBUSB_1_0)
+ if (devices[dn].method == sanei_usb_method_libusb)
+ if (stalled)
+ libusb_clear_halt (devices[dn].lu_handle, devices[dn].int_in_ep);
+#endif
+ *size = 0;
+ return SANE_STATUS_IO_ERROR;
+ }
+ if (read_size == 0)
+ {
+ DBG (3, "sanei_usb_read_int: read returned EOF\n");
+ *size = 0;
+ return SANE_STATUS_EOF;
+ }
+ DBG (5, "sanei_usb_read_int: wanted %lu bytes, got %ld bytes\n",
+ (unsigned long) *size, (unsigned long) read_size);
+ *size = read_size;
+ if (debug_level > 10)
+ print_buffer (buffer, read_size);
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sanei_usb_set_configuration (SANE_Int dn, SANE_Int configuration)
+{
+ if (dn >= device_number || dn < 0)
+ {
+ DBG (1,
+ "sanei_usb_set_configuration: dn >= device number || dn < 0, dn=%d\n",
+ dn);
+ return SANE_STATUS_INVAL;
+ }
+
+ DBG (5, "sanei_usb_set_configuration: configuration = %d\n", configuration);
+
+ if (devices[dn].method == sanei_usb_method_scanner_driver)
+ {
+#if defined(__linux__)
+ return SANE_STATUS_GOOD;
+#else /* not __linux__ */
+ DBG (5, "sanei_usb_set_configuration: not supported on this OS\n");
+ return SANE_STATUS_UNSUPPORTED;
+#endif /* not __linux__ */
+ }
+ else if (devices[dn].method == sanei_usb_method_libusb)
+#ifdef HAVE_LIBUSB
+ {
+ int result;
+
+ result =
+ usb_set_configuration (devices[dn].libusb_handle, configuration);
+ if (result < 0)
+ {
+ DBG (1, "sanei_usb_set_configuration: libusb complained: %s\n",
+ usb_strerror ());
+ return SANE_STATUS_INVAL;
+ }
+ return SANE_STATUS_GOOD;
+ }
+#elif defined(HAVE_LIBUSB_1_0)
+ {
+ int result;
+
+ result = libusb_set_configuration (devices[dn].lu_handle, configuration);
+ if (result < 0)
+ {
+ DBG (1, "sanei_usb_set_configuration: libusb complained: %s\n",
+ sanei_libusb_strerror (result));
+ return SANE_STATUS_INVAL;
+ }
+ return SANE_STATUS_GOOD;
+ }
+#else /* not HAVE_LIBUSB && not HAVE_LIBUSB_1_0 */
+ {
+ DBG (1, "sanei_usb_set_configuration: libusb support missing\n");
+ return SANE_STATUS_UNSUPPORTED;
+ }
+#endif /* not HAVE_LIBUSB && not HAVE_LIBUSB_1_0 */
+ else
+ {
+ DBG (1,
+ "sanei_usb_set_configuration: access method %d not implemented\n",
+ devices[dn].method);
+ return SANE_STATUS_UNSUPPORTED;
+ }
+}
+
+SANE_Status
+sanei_usb_claim_interface (SANE_Int dn, SANE_Int interface_number)
+{
+ if (dn >= device_number || dn < 0)
+ {
+ DBG (1,
+ "sanei_usb_claim_interface: dn >= device number || dn < 0, dn=%d\n",
+ dn);
+ return SANE_STATUS_INVAL;
+ }
+
+ DBG (5, "sanei_usb_claim_interface: interface_number = %d\n", interface_number);
+
+ if (devices[dn].method == sanei_usb_method_scanner_driver)
+ {
+#if defined(__linux__)
+ return SANE_STATUS_GOOD;
+#else /* not __linux__ */
+ DBG (5, "sanei_usb_claim_interface: not supported on this OS\n");
+ return SANE_STATUS_UNSUPPORTED;
+#endif /* not __linux__ */
+ }
+ else if (devices[dn].method == sanei_usb_method_libusb)
+#ifdef HAVE_LIBUSB
+ {
+ int result;
+
+ result = usb_claim_interface (devices[dn].libusb_handle, interface_number);
+ if (result < 0)
+ {
+ DBG (1, "sanei_usb_claim_interface: libusb complained: %s\n",
+ usb_strerror ());
+ return SANE_STATUS_INVAL;
+ }
+ return SANE_STATUS_GOOD;
+ }
+#elif defined(HAVE_LIBUSB_1_0)
+ {
+ int result;
+
+ result = libusb_claim_interface (devices[dn].lu_handle, interface_number);
+ if (result < 0)
+ {
+ DBG (1, "sanei_usb_claim_interface: libusb complained: %s\n",
+ sanei_libusb_strerror (result));
+ return SANE_STATUS_INVAL;
+ }
+ return SANE_STATUS_GOOD;
+ }
+#else /* not HAVE_LIBUSB && not HAVE_LIBUSB_1_0 */
+ {
+ DBG (1, "sanei_usb_claim_interface: libusb support missing\n");
+ return SANE_STATUS_UNSUPPORTED;
+ }
+#endif /* not HAVE_LIBUSB && not HAVE_LIBUSB_1_0 */
+ else
+ {
+ DBG (1, "sanei_usb_claim_interface: access method %d not implemented\n",
+ devices[dn].method);
+ return SANE_STATUS_UNSUPPORTED;
+ }
+}
+
+SANE_Status
+sanei_usb_release_interface (SANE_Int dn, SANE_Int interface_number)
+{
+ if (dn >= device_number || dn < 0)
+ {
+ DBG (1,
+ "sanei_usb_release_interface: dn >= device number || dn < 0, dn=%d\n",
+ dn);
+ return SANE_STATUS_INVAL;
+ }
+
+ DBG (5, "sanei_usb_release_interface: interface_number = %d\n", interface_number);
+
+ if (devices[dn].method == sanei_usb_method_scanner_driver)
+ {
+#if defined(__linux__)
+ return SANE_STATUS_GOOD;
+#else /* not __linux__ */
+ DBG (5, "sanei_usb_release_interface: not supported on this OS\n");
+ return SANE_STATUS_UNSUPPORTED;
+#endif /* not __linux__ */
+ }
+ else if (devices[dn].method == sanei_usb_method_libusb)
+#ifdef HAVE_LIBUSB
+ {
+ int result;
+
+ result = usb_release_interface (devices[dn].libusb_handle, interface_number);
+ if (result < 0)
+ {
+ DBG (1, "sanei_usb_release_interface: libusb complained: %s\n",
+ usb_strerror ());
+ return SANE_STATUS_INVAL;
+ }
+ return SANE_STATUS_GOOD;
+ }
+#elif defined(HAVE_LIBUSB_1_0)
+ {
+ int result;
+
+ result = libusb_release_interface (devices[dn].lu_handle, interface_number);
+ if (result < 0)
+ {
+ DBG (1, "sanei_usb_release_interface: libusb complained: %s\n",
+ sanei_libusb_strerror (result));
+ return SANE_STATUS_INVAL;
+ }
+ return SANE_STATUS_GOOD;
+ }
+#else /* not HAVE_LIBUSB && not HAVE_LIBUSB_1_0 */
+ {
+ DBG (1, "sanei_usb_release_interface: libusb support missing\n");
+ return SANE_STATUS_UNSUPPORTED;
+ }
+#endif /* not HAVE_LIBUSB && not HAVE_LIBUSB_1_0 */
+ else
+ {
+ DBG (1,
+ "sanei_usb_release_interface: access method %d not implemented\n",
+ devices[dn].method);
+ return SANE_STATUS_UNSUPPORTED;
+ }
+}
+
+SANE_Status
+sanei_usb_set_altinterface (SANE_Int dn, SANE_Int alternate)
+{
+ if (dn >= device_number || dn < 0)
+ {
+ DBG (1,
+ "sanei_usb_set_altinterface: dn >= device number || dn < 0, dn=%d\n",
+ dn);
+ return SANE_STATUS_INVAL;
+ }
+
+ DBG (5, "sanei_usb_set_altinterface: alternate = %d\n", alternate);
+
+ if (devices[dn].method == sanei_usb_method_scanner_driver)
+ {
+#if defined(__linux__)
+ return SANE_STATUS_GOOD;
+#else /* not __linux__ */
+ DBG (5, "sanei_usb_set_altinterface: not supported on this OS\n");
+ return SANE_STATUS_UNSUPPORTED;
+#endif /* not __linux__ */
+ }
+ else if (devices[dn].method == sanei_usb_method_libusb)
+#ifdef HAVE_LIBUSB
+ {
+ int result;
+
+ result = usb_set_altinterface (devices[dn].libusb_handle, alternate);
+ if (result < 0)
+ {
+ DBG (1, "sanei_usb_set_altinterface: libusb complained: %s\n",
+ usb_strerror ());
+ return SANE_STATUS_INVAL;
+ }
+ return SANE_STATUS_GOOD;
+ }
+#elif defined(HAVE_LIBUSB_1_0)
+ {
+ int result;
+
+ result = libusb_set_interface_alt_setting (devices[dn].lu_handle,
+ devices[dn].interface_nr, alternate);
+ if (result < 0)
+ {
+ DBG (1, "sanei_usb_set_altinterface: libusb complained: %s\n",
+ sanei_libusb_strerror (result));
+ return SANE_STATUS_INVAL;
+ }
+ return SANE_STATUS_GOOD;
+ }
+#else /* not HAVE_LIBUSB && not HAVE_LIBUSB_1_0 */
+ {
+ DBG (1, "sanei_set_altinterface: libusb support missing\n");
+ return SANE_STATUS_UNSUPPORTED;
+ }
+#endif /* not HAVE_LIBUSB && not HAVE_LIBUSB_1_0 */
+ else
+ {
+ DBG (1,
+ "sanei_usb_set_altinterface: access method %d not implemented\n",
+ devices[dn].method);
+ return SANE_STATUS_UNSUPPORTED;
+ }
+}
+
+extern SANE_Status
+sanei_usb_get_descriptor( SANE_Int dn, struct sanei_usb_dev_descriptor *desc )
+{
+ if (dn >= device_number || dn < 0)
+ {
+ DBG (1,
+ "sanei_usb_get_descriptor: dn >= device number || dn < 0, dn=%d\n",
+ dn);
+ return SANE_STATUS_INVAL;
+ }
+
+ DBG (5, "sanei_usb_get_descriptor\n");
+#ifdef HAVE_LIBUSB
+ {
+ struct usb_device_descriptor *usb_descr;
+
+ usb_descr = &(devices[dn].libusb_device->descriptor);
+ desc->desc_type = usb_descr->bDescriptorType;
+ desc->bcd_usb = usb_descr->bcdUSB;
+ desc->bcd_dev = usb_descr->bcdDevice;
+ desc->dev_class = usb_descr->bDeviceClass;
+
+ desc->dev_sub_class = usb_descr->bDeviceSubClass;
+ desc->dev_protocol = usb_descr->bDeviceProtocol;
+ desc->max_packet_size = usb_descr->bMaxPacketSize0;
+ return SANE_STATUS_GOOD;
+ }
+#elif defined(HAVE_LIBUSB_1_0)
+ {
+ struct libusb_device_descriptor lu_desc;
+ int ret;
+
+ ret = libusb_get_device_descriptor (devices[dn].lu_device, &lu_desc);
+ if (ret < 0)
+ {
+ DBG (1,
+ "sanei_usb_get_descriptor: libusb error: %s\n",
+ sanei_libusb_strerror (ret));
+
+ return SANE_STATUS_INVAL;
+ }
+
+ desc->desc_type = lu_desc.bDescriptorType;
+ desc->bcd_usb = lu_desc.bcdUSB;
+ desc->bcd_dev = lu_desc.bcdDevice;
+ desc->dev_class = lu_desc.bDeviceClass;
+
+ desc->dev_sub_class = lu_desc.bDeviceSubClass;
+ desc->dev_protocol = lu_desc.bDeviceProtocol;
+ desc->max_packet_size = lu_desc.bMaxPacketSize0;
+ return SANE_STATUS_GOOD;
+ }
+#else /* not HAVE_LIBUSB && not HAVE_LIBUSB_1_0 */
+ {
+ DBG (1, "sanei_usb_get_descriptor: libusb support missing\n");
+ return SANE_STATUS_UNSUPPORTED;
+ }
+#endif /* not HAVE_LIBUSB && not HAVE_LIBUSB_1_0 */
+}