[sane-devel] Re: Brother MFC 6800

Henning Meier-Geinitz henning@meier-geinitz.de
Thu, 20 Feb 2003 12:01:01 +0100


--ew6BAiZeqk4r7MaW
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline

Hi,

On Wed, Feb 19, 2003 at 07:38:35PM +0100, Henning Meier-Geinitz wrote:
> On Wed, Feb 19, 2003 at 09:27:04AM -0500, Fernando Trias wrote:
> > The patch to detect multiple interfaces on a USB device seems to work.
> > Thanks! Now, the same problem exists in sane-find-scanner.
> 
> Ok. I'll attach a new version of sane-find-scanner. Please test.

Someone steals the attachments every time :-)

Bye,
  Henning

--ew6BAiZeqk4r7MaW
Content-Type: text/x-csrc; charset=us-ascii
Content-Disposition: attachment; filename="sane-find-scanner.c"

/* sane-find-scanner.c

   Copyright (C) 1997-2002 Oliver Rauch, Henning Meier-Geinitz, and others.

   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.

 */

#include "../include/sane/config.h"

#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <errno.h>

#ifdef HAVE_LIBUSB
#include "usb.h"
#endif

#include "../include/sane/sanei.h"
#include "../include/sane/sanei_scsi.h"
#include "../include/sane/sanei_usb.h"

#ifndef PATH_MAX
# define PATH_MAX 1024
#endif

static const char *prog_name;
static int verbose = 1;
static SANE_Bool force = SANE_FALSE;
static SANE_Bool device_found = SANE_FALSE;
static SANE_Bool libusb_device_found = SANE_FALSE;
static SANE_Bool unknown_found = SANE_FALSE;

typedef struct
{
  unsigned char *cmd;
  size_t size;
}
scsiblk;

#define INQUIRY					0x12
#define set_inquiry_return_size(icb,val)	icb[0x04]=val
#define IN_periph_devtype_cpu			0x03
#define IN_periph_devtype_scanner		0x06
#define get_scsi_inquiry_vendor(in, buf)	strncpy(buf, in + 0x08, 0x08)
#define get_scsi_inquiry_product(in, buf)	strncpy(buf, in + 0x10, 0x010)
#define get_scsi_inquiry_version(in, buf)	strncpy(buf, in + 0x20, 0x04)
#define get_scsi_inquiry_periph_devtype(in)	(in[0] & 0x1f)
#define get_scsi_inquiry_additional_length(in)	in[0x04]
#define set_scsi_inquiry_length(out,n)		out[0x04]=n-5

static unsigned char inquiryC[] = {
  INQUIRY, 0x00, 0x00, 0x00, 0xff, 0x00
};
static scsiblk inquiry = {
  inquiryC, sizeof (inquiryC)
};

static void
usage (char *msg)
{
  fprintf (stderr, "Usage: %s [-hvqf] [devname ...]\n", prog_name);
  fprintf (stderr, "\t-h: print this help message\n");
  fprintf (stderr, "\t-v: be more verbose (can be used multiple times)\n");
  fprintf (stderr, "\t-q: be quiet (print only devices, no comments)\n");
  fprintf (stderr, "\t-f: force opening devname as SCSI even if it looks "
	   "like USB\n");
  if (msg)
    fprintf (stderr, "\t%s\n", msg);
}

/* if SCSI generic is optional on your OS, and there is a software test
   which will determine if it is included, add it here. If you are sure
   that SCSI generic was found, return 1. If SCSI generic is always
   available in your OS, return 1 */

static int
check_sg (void)
{
#if defined(__linux__)
  /* Assumption: /proc/devices lines are shorter than 256 characters */
  char line[256], driver[256] = "";
  FILE *stream;

  stream = fopen ("/proc/devices", "r");
  /* Likely reason for failure, no /proc => probably no SG either */
  if (stream == NULL)
    return 0;

  while (fgets (line, sizeof (line) - 1, stream))
    {
      if (sscanf (line, " %*d %s\n", driver) > 0 && !strcmp (driver, "sg"))
	break;
    }
  fclose (stream);

  if (strcmp (driver, "sg"))
    {
      return 0;
    }
  else
    {
      return 1;
    }
#endif
  return 1;			/* Give up, and assume yes to avoid false negatives */
}

/* Display a buffer in the log. Display by lines of 16 bytes. */
static void
hexdump (const char *comment, unsigned char *buf, const int length)
{
  int i;
  char line[128];
  char *ptr;
  char asc_buf[17];
  char *asc_ptr;

  printf ("  %s\n", comment);

  i = 0;
  goto start;

  do
    {
      if (i < length)
	{
	  ptr += sprintf (ptr, " %2.2x", *buf);

	  if (*buf >= 32 && *buf <= 127)
	    {
	      asc_ptr += sprintf (asc_ptr, "%c", *buf);
	    }
	  else
	    {
	      asc_ptr += sprintf (asc_ptr, ".");
	    }
	}
      else
	{
	  /* After the length; do nothing. */
	  ptr += sprintf (ptr, "   ");
	}

      i++;
      buf++;

      if ((i % 16) == 0)
	{
	  /* It's a new line */
	  printf ("  %s    %s\n", line, asc_buf);

	start:
	  ptr = line;
	  *ptr = '\0';
	  asc_ptr = asc_buf;
	  *asc_ptr = '\0';

	  ptr += sprintf (ptr, "  %3.3d:", i);
	}

    }
  while (i < ((length + 15) & ~15));
}

static SANE_Status
scanner_do_scsi_inquiry (unsigned char *buffer, int sfd)
{
  size_t size;
  SANE_Status status;

  memset (buffer, '\0', 256);	/* clear buffer */

  size = 5;			/* first get only 5 bytes to get size of 
				   inquiry_return_block */
  set_inquiry_return_size (inquiry.cmd, size);
  status = sanei_scsi_cmd (sfd, inquiry.cmd, inquiry.size, buffer, &size);

  if (status != SANE_STATUS_GOOD)
    return (status);

  size = get_scsi_inquiry_additional_length (buffer) + 5;

  /* then get inquiry with actual size */
  set_inquiry_return_size (inquiry.cmd, size);
  status = sanei_scsi_cmd (sfd, inquiry.cmd, inquiry.size, buffer, &size);

  return (status);
}

static void
scanner_identify_scsi_scanner (unsigned char *buffer, int sfd,
			       char *devicename)
{
  unsigned char vendor[9];
  unsigned char product[17];
  unsigned char version[5];
  unsigned char *pp;
  unsigned int devtype;
  SANE_Status status;
  static char *devtypes[] = {
    "disk", "tape", "printer", "processor", "CD-writer",
    "CD-drive", "scanner", "optical-drive", "jukebox",
    "communicator"
  };
  status = scanner_do_scsi_inquiry (buffer, sfd);
  if (status != SANE_STATUS_GOOD)
    {
      if (verbose > 1)
	printf ("inquiry for device %s failed (%s)\n",
		devicename, sane_strstatus (status));
      return;
    }

  if (verbose > 2)
    hexdump ("Inquiry for device:", buffer,
	     get_scsi_inquiry_additional_length (buffer) + 5);

  devtype = get_scsi_inquiry_periph_devtype (buffer);
  if (verbose <= 1 && devtype != IN_periph_devtype_scanner
      /* old HP scanners use the CPU id ... */
      && devtype != IN_periph_devtype_cpu)
    return;			/* no, continue searching */

  get_scsi_inquiry_vendor ((char *) buffer, (char *) vendor);
  get_scsi_inquiry_product ((char *) buffer, (char *) product);
  get_scsi_inquiry_version ((char *) buffer, (char *) version);

  pp = &vendor[7];
  vendor[8] = '\0';
  while (pp >= vendor && (*pp == ' ' || *pp >= 127))
    *pp-- = '\0';

  pp = &product[15];
  product[16] = '\0';
  while (pp >= product && (*pp == ' ' || *pp >= 127))
    *pp-- = '\0';

  pp = &version[3];
  version[4] = '\0';
  while (pp >= version && (*pp == ' ' || *(pp - 1) >= 127))
    *pp-- = '\0';

  device_found = SANE_TRUE;
  printf ("found SCSI %s \"%s %s %s\" at %s\n",
	  devtype < NELEMS (devtypes) ? devtypes[devtype] : "unknown device",
	  vendor, product, version, devicename);
  return;
}

static void
check_scsi_file (char *file_name)
{
  int result;
  int sfd;
  unsigned char buffer[16384];

  if (strstr (file_name, "usb")
      || strstr (file_name, "uscanner") || strstr (file_name, "ugen"))
    {
      if (force)
	{
	  if (verbose > 1)
	    printf ("checking %s even though it looks like a USB device...",
		    file_name);
	}
      else
	{
	  if (verbose > 1)
	    printf ("ignored %s (not a SCSI device)\n", file_name);
	  return;
	}
    }
  else if (verbose > 1)
    printf ("checking %s...", file_name);

  result = sanei_scsi_open (file_name, &sfd, NULL, NULL);

  if (verbose > 1)
    {
      if (result != 0)
	printf (" failed to open (%s)\n", sane_strstatus (result));
      else
	printf (" open ok\n");
    }

  if (result == SANE_STATUS_GOOD)
    {
      scanner_identify_scsi_scanner (buffer, sfd, file_name);
      sanei_scsi_close (sfd);
    }
  return;
}

static void
check_usb_file (char *file_name)
{
  SANE_Status result;
  SANE_Word vendor, product;
  SANE_Int fd;

  if (!strstr (file_name, "usb")
      && !strstr (file_name, "uscanner") && !strstr (file_name, "ugen"))
    {
      if (force)
	{
	  if (verbose > 1)
	    printf ("checking %s even though doesn't look like a "
		    "USB device...", file_name);
	}
      else
	{
	  if (verbose > 1)
	    printf ("ignored %s (not a USB device)\n", file_name);
	  return;
	}
    }
  else if (verbose > 1)
    printf ("checking %s...", file_name);

  result = sanei_usb_open (file_name, &fd);

  if (result != SANE_STATUS_GOOD)
    {
      if (verbose > 1)
	printf (" failed to open (%s)\n", sane_strstatus (result));
    }
  else
    {
      result = sanei_usb_get_vendor_product (fd, &vendor, &product);
      if (result == SANE_STATUS_GOOD)
	{
	  if (verbose > 1)
	    printf (" open ok, vendor and product ids were identified\n");
	  printf ("found USB scanner (vendor=0x%04x, "
		  "product=0x%04x) at %s\n", vendor, product, file_name);
	}
      else
	{
	  if (verbose > 1)
	    printf (" open ok, but vendor and product could NOT be "
		    "identified\n");
	  printf ("found USB scanner (UNKNOWN vendor and product) "
		  "at device %s\n", file_name);
	  unknown_found = SANE_TRUE;
	}
      device_found = SANE_TRUE;
      sanei_usb_close (fd);
    }
}

#ifdef HAVE_LIBUSB

static char *
get_libusb_string_descriptor (struct usb_device *dev, int index)
{
  usb_dev_handle *handle;
  char *buffer;
  struct usb_string_descriptor *sd;
  int size = 100;
  int i;

  if (!index)
    return 0;
  buffer = calloc (1, size + 1);
  if (!buffer)
    return 0;
  sd = (struct usb_string_descriptor *) buffer;
  handle = usb_open (dev);
  if (!handle)
    return 0;
  if (usb_control_msg (handle,
		       USB_ENDPOINT_IN + USB_TYPE_STANDARD + USB_RECIP_DEVICE,
		       USB_REQ_GET_DESCRIPTOR,
		       (USB_DT_STRING << 8) + index, 0, buffer,
		       size, 1000) < 0)
    {
      usb_close (handle);
      return 0;
    }
  if (sd->bLength < 2 || sd->bLength > size
      || sd->bDescriptorType != USB_DT_STRING)
    {
      usb_close (handle);
      return 0;
    }
  size = sd->bLength - 2;
  for (i = 0; i < (size / 2); i++)
    buffer[i] = buffer[2 + 2 * i];
  buffer[i] = 0;

  usb_close (handle);
  return buffer;
}

static char *
get_libusb_vendor (struct usb_device *dev)
{
  return get_libusb_string_descriptor (dev, dev->descriptor.iManufacturer);
}

static char *
get_libusb_product (struct usb_device *dev)
{
  return get_libusb_string_descriptor (dev, dev->descriptor.iProduct);
}

static void
check_libusb_device (struct usb_device *dev)
{
  int is_scanner = 0;
  char *vendor = get_libusb_vendor (dev);
  char *product = get_libusb_product (dev);
  int interface_nr;

  if (!dev->config)
    {
      if (verbose > 1)
	printf ("device 0x%04x/0x%04x is not configured\n",
		dev->descriptor.idVendor, dev->descriptor.idProduct);
      return;
    }

  if (verbose > 2)
    {
      /* print everything we know about the device */
      int config_nr;
      struct usb_device_descriptor *d = &dev->descriptor;


      printf ("\n");
      printf ("<device descriptor of 0x%04x/0x%04x at %s:%s",
	      d->idVendor, d->idProduct, dev->bus->dirname, dev->filename);
      if (vendor || product)
	{
	  printf (" (%s%s%s)", vendor ? vendor : "",
		  (vendor && product) ? " " : "", product ? product : "");
	}
      printf (">\n");
      printf ("bLength               %d\n", d->bLength);
      printf ("bDescriptorType       %d\n", d->bDescriptorType);
      printf ("bcdUSB                %d.%d%d\n", d->bcdUSB >> 8,
	      (d->bcdUSB >> 4) & 15, d->bcdUSB & 15);
      printf ("bDeviceClass          %d\n", d->bDeviceClass);
      printf ("bDeviceSubClass       %d\n", d->bDeviceSubClass);
      printf ("bDeviceProtocol       %d\n", d->bDeviceProtocol);
      printf ("bMaxPacketSize0       %d\n", d->bMaxPacketSize0);
      printf ("idVendor              0x%04X\n", d->idVendor);
      printf ("idProduct             0x%04X\n", d->idProduct);
      printf ("bcdDevice             %d.%d%d\n", d->bcdDevice >> 8,
	      (d->bcdDevice >> 4) & 15, d->bcdDevice & 15);
      printf ("iManufacturer         %d (%s)\n", d->iManufacturer,
	      d->iManufacturer
	      ? get_libusb_string_descriptor (dev, d->iManufacturer) : "");
      printf ("iProduct              %d (%s)\n", d->iProduct,
	      d->iProduct
	      ? get_libusb_string_descriptor (dev, d->iProduct) : "");
      printf ("iSerialNumber         %d (%s)\n", d->iSerialNumber,
	      d->iSerialNumber
	      ? get_libusb_string_descriptor (dev, d->iSerialNumber) : "");
      printf ("bNumConfigurations    %d\n", d->bNumConfigurations);

      for (config_nr = 0; config_nr < d->bNumConfigurations; config_nr++)
	{
	  struct usb_config_descriptor *c = &dev->config[config_nr];
	  int interface_nr;

	  printf (" <configuration %d>\n", config_nr);
	  printf (" bLength              %d\n", c->bLength);
	  printf (" bDescriptorType      %d\n", c->bDescriptorType);
	  printf (" wTotalLength         %d\n", c->wTotalLength);
	  printf (" bNumInterfaces       %d\n", c->bNumInterfaces);
	  printf (" bConfigurationValue  %d\n", c->bConfigurationValue);
	  printf (" iConfiguration       %d (%s)\n", c->iConfiguration,
		  c->iConfiguration ?
		  get_libusb_string_descriptor (dev, c->iConfiguration) : "");
	  printf (" bmAttributes         %d (%s%s)\n", c->bmAttributes,
		  c->bmAttributes & 64 ? "Self-powered" : "",
		  c->bmAttributes & 32 ? "Remote Wakeup" : "");
	  printf (" MaxPower             %d mA\n", c->MaxPower * 2);

	  for (interface_nr = 0; interface_nr < c->bNumInterfaces;
	       interface_nr++)
	    {
	      struct usb_interface *interface = &c->interface[interface_nr];
	      int alt_setting_nr;

	      printf ("  <interface %d>\n", interface_nr);
	      for (alt_setting_nr = 0;
		   alt_setting_nr < interface->num_altsetting;
		   alt_setting_nr++)
		{
		  struct usb_interface_descriptor *i
		    = &interface->altsetting[0];
		  int ep_nr;
		  printf ("   <altsetting %d>\n", alt_setting_nr);
		  printf ("   bLength            %d\n", i->bLength);
		  printf ("   bDescriptorType    %d\n", i->bDescriptorType);
		  printf ("   bInterfaceNumber   %d\n", i->bInterfaceNumber);
		  printf ("   bAlternateSetting  %d\n", i->bAlternateSetting);
		  printf ("   bNumEndpoints      %d\n", i->bNumEndpoints);
		  printf ("   bInterfaceClass    %d\n", i->bInterfaceClass);
		  printf ("   bInterfaceSubClass %d\n",
			  i->bInterfaceSubClass);
		  printf ("   bInterfaceProtocol %d\n",
			  i->bInterfaceProtocol);
		  printf ("   iInterface         %d (%s)\n", i->iInterface,
			  i->iInterface
			  ? get_libusb_string_descriptor (dev,
							  i->
							  iInterface) : "");
		  for (ep_nr = 0; ep_nr < i->bNumEndpoints; ep_nr++)
		    {
		      struct usb_endpoint_descriptor *e = &i->endpoint[ep_nr];
		      char *ep_type;

		      switch (e->bmAttributes & USB_ENDPOINT_TYPE_MASK)
			{
			case USB_ENDPOINT_TYPE_CONTROL:
			  ep_type = "control";
			  break;
			case USB_ENDPOINT_TYPE_ISOCHRONOUS:
			  ep_type = "isochronous";
			  break;
			case USB_ENDPOINT_TYPE_BULK:
			  ep_type = "bulk";
			  break;
			case USB_ENDPOINT_TYPE_INTERRUPT:
			  ep_type = "interrupt";
			  break;
			default:
			  ep_type = "unknown";
			  break;
			}
		      printf ("    <endpoint %d>\n", ep_nr);
		      printf ("    bLength           %d\n", e->bLength);
		      printf ("    bDescriptorType   %d\n",
			      e->bDescriptorType);
		      printf ("    bEndpointAddress  0x%02X (%s 0x%02X)\n",
			      e->bEndpointAddress,
			      e->bEndpointAddress & USB_ENDPOINT_DIR_MASK ?
			      "in" : "out",
			      e->
			      bEndpointAddress & USB_ENDPOINT_ADDRESS_MASK);
		      printf ("    bmAttributes      %d (%s)\n",
			      e->bmAttributes, ep_type);
		      printf ("    wMaxPacketSize    %d\n",
			      e->wMaxPacketSize);
		      printf ("    bInterval         %d ms\n", e->bInterval);
		      printf ("    bRefresh          %d\n", e->bRefresh);
		      printf ("    bSynchAddress     %d\n", e->bSynchAddress);
		    }
		}
	    }
	}

    }

  /* Some heuristics, which device may be a scanner */
  if (dev->descriptor.idVendor == 0)	/* hub */
    --is_scanner;
  if (dev->descriptor.idProduct == 0)	/* hub */
    --is_scanner;

  for (interface_nr = 0; interface_nr < dev->config[0].bNumInterfaces && is_scanner <= 0; interface_nr++)
    {
      switch (dev->descriptor.bDeviceClass)
	{
	case USB_CLASS_VENDOR_SPEC:
	  ++is_scanner;
	  break;
	case USB_CLASS_PER_INTERFACE:
	  switch (dev->config[0].interface[interface_nr].altsetting[0].bInterfaceClass)
	    {
	    case USB_CLASS_VENDOR_SPEC:
	    case USB_CLASS_PER_INTERFACE:
	    case 16:                /* data? */
	      ++is_scanner;
	      break;
	    }
	  break;
	}
    }

  if (is_scanner > 0)
    {
      printf ("found USB scanner (vendor=0x%04x", dev->descriptor.idVendor);
      if (vendor)
	printf (" [%s]", vendor);
      printf (", product=0x%04x", dev->descriptor.idProduct);
      if (product)
	printf (" [%s]", product);
      printf (") at libusb:%s:%s\n", dev->bus->dirname, dev->filename);
      libusb_device_found = SANE_TRUE;
      device_found = SANE_TRUE;
    }
}
#endif /* HAVE_LIBUSB */

static DIR *
scan_directory (char *dir_name)
{
  struct stat stat_buf;
  DIR *dir;

  if (verbose > 2)
    printf ("scanning directory %s\n", dir_name);

  if (stat (dir_name, &stat_buf) < 0)
    {
      if (verbose > 1)
	printf ("cannot stat `%s' (%s)\n", dir_name, strerror (errno));
      return 0;
    }
  if (!S_ISDIR (stat_buf.st_mode))
    {
      if (verbose > 1)
	printf ("`%s' is not a directory\n", dir_name);
      return 0;
    }
  if ((dir = opendir (dir_name)) == 0)
    {
      if (verbose > 1)
	printf ("cannot read directory `%s' (%s)\n", dir_name,
		strerror (errno));
      return 0;
    }
  return dir;
}

static char *
get_next_file (char *dir_name, DIR * dir)
{
  struct dirent *dir_entry;
  static char file_name[PATH_MAX];

  do
    {
      dir_entry = readdir (dir);
      if (!dir_entry)
	return 0;
    }
  while (strcmp (dir_entry->d_name, ".") == 0
	 || strcmp (dir_entry->d_name, "..") == 0);

  if (strlen (dir_name) + strlen (dir_entry->d_name) + 1 > PATH_MAX)
    {
      if (verbose > 1)
	printf ("filename too long\n");
      return 0;
    }
  sprintf (file_name, "%s%s", dir_name, dir_entry->d_name);
  return file_name;
}

int
main (int argc, char **argv)
{
  char **dev_list, **usb_dev_list, *dev_name, **ap;

  prog_name = strrchr (argv[0], '/');
  if (prog_name)
    ++prog_name;
  else
    prog_name = argv[0];

  for (ap = argv + 1; ap < argv + argc; ++ap)
    {
      if ((*ap)[0] != '-')
	break;
      switch ((*ap)[1])
	{
	case '?':
	case 'h':
	  usage (0);
	  exit (0);

	case 'v':
	  ++verbose;
	  break;

	case 'q':
	  --verbose;
	  break;

	case 'f':
	  force = SANE_TRUE;
	  break;

	default:
	  printf ("unknown option: -%c, try -h for help\n", (*ap)[1]);
	  exit (0);
	}
    }
  if (ap < argv + argc)
    {
      dev_list = ap;
      usb_dev_list = ap;
    }
  else
    {
      static char *default_dev_list[] = {
#if defined(__sgi)
	"/dev/scsi/sc0d1l0", "/dev/scsi/sc0d2l0",
	"/dev/scsi/sc0d3l0", "/dev/scsi/sc0d4l0",
	"/dev/scsi/sc0d5l0", "/dev/scsi/sc0d6l0",
	"/dev/scsi/sc0d7l0", "/dev/scsi/sc0d8l0",
	"/dev/scsi/sc0d9l0", "/dev/scsi/sc0d10l0",
	"/dev/scsi/sc0d11l0", "/dev/scsi/sc0d12l0",
	"/dev/scsi/sc0d13l0", "/dev/scsi/sc0d14l0",
	"/dev/scsi/sc0d15l0",
	"/dev/scsi/sc1d1l0", "/dev/scsi/sc1d2l0",
	"/dev/scsi/sc1d3l0", "/dev/scsi/sc1d4l0",
	"/dev/scsi/sc1d5l0", "/dev/scsi/sc1d6l0",
	"/dev/scsi/sc1d7l0", "/dev/scsi/sc1d8l0",
	"/dev/scsi/sc1d9l0", "/dev/scsi/sc1d10l0",
	"/dev/scsi/sc1d11l0", "/dev/scsi/sc1d12l0",
	"/dev/scsi/sc1d13l0", "/dev/scsi/sc1d14l0",
	"/dev/scsi/sc1d15l0",
	"/dev/scsi/sc2d1l0", "/dev/scsi/sc2d2l0",
	"/dev/scsi/sc2d3l0", "/dev/scsi/sc2d4l0",
	"/dev/scsi/sc2d5l0", "/dev/scsi/sc2d6l0",
	"/dev/scsi/sc2d7l0", "/dev/scsi/sc2d8l0",
	"/dev/scsi/sc2d9l0", "/dev/scsi/sc2d10l0",
	"/dev/scsi/sc2d11l0", "/dev/scsi/sc2d12l0",
	"/dev/scsi/sc2d13l0", "/dev/scsi/sc2d14l0",
	"/dev/scsi/sc2d15l0",
	"/dev/scsi/sc3d1l0", "/dev/scsi/sc3d2l0",
	"/dev/scsi/sc3d3l0", "/dev/scsi/sc3d4l0",
	"/dev/scsi/sc3d5l0", "/dev/scsi/sc3d6l0",
	"/dev/scsi/sc3d7l0", "/dev/scsi/sc3d8l0",
	"/dev/scsi/sc3d9l0", "/dev/scsi/sc3d10l0",
	"/dev/scsi/sc3d11l0", "/dev/scsi/sc3d12l0",
	"/dev/scsi/sc3d13l0", "/dev/scsi/sc3d14l0",
	"/dev/scsi/sc3d15l0",
	"/dev/scsi/sc4d1l0", "/dev/scsi/sc4d2l0",
	"/dev/scsi/sc4d3l0", "/dev/scsi/sc4d4l0",
	"/dev/scsi/sc4d5l0", "/dev/scsi/sc4d6l0",
	"/dev/scsi/sc4d7l0", "/dev/scsi/sc4d8l0",
	"/dev/scsi/sc4d9l0", "/dev/scsi/sc4d10l0",
	"/dev/scsi/sc4d11l0", "/dev/scsi/sc4d12l0",
	"/dev/scsi/sc4d13l0", "/dev/scsi/sc4d14l0",
	"/dev/scsi/sc4d15l0",
	"/dev/scsi/sc5d1l0", "/dev/scsi/sc5d2l0",
	"/dev/scsi/sc5d3l0", "/dev/scsi/sc5d4l0",
	"/dev/scsi/sc5d5l0", "/dev/scsi/sc5d6l0",
	"/dev/scsi/sc5d7l0", "/dev/scsi/sc5d8l0",
	"/dev/scsi/sc5d9l0", "/dev/scsi/sc5d10l0",
	"/dev/scsi/sc5d11l0", "/dev/scsi/sc5d12l0",
	"/dev/scsi/sc5d13l0", "/dev/scsi/sc5d14l0",
	"/dev/scsi/sc5d15l0",
	"/dev/scsi/sc6d1l0", "/dev/scsi/sc6d2l0",
	"/dev/scsi/sc6d3l0", "/dev/scsi/sc6d4l0",
	"/dev/scsi/sc6d5l0", "/dev/scsi/sc6d6l0",
	"/dev/scsi/sc6d7l0", "/dev/scsi/sc6d8l0",
	"/dev/scsi/sc6d9l0", "/dev/scsi/sc6d10l0",
	"/dev/scsi/sc6d11l0", "/dev/scsi/sc6d12l0",
	"/dev/scsi/sc6d13l0", "/dev/scsi/sc6d14l0",
	"/dev/scsi/sc6d15l0",
	"/dev/scsi/sc7d1l0", "/dev/scsi/sc7d2l0",
	"/dev/scsi/sc7d3l0", "/dev/scsi/sc7d4l0",
	"/dev/scsi/sc7d5l0", "/dev/scsi/sc7d6l0",
	"/dev/scsi/sc7d7l0", "/dev/scsi/sc7d8l0",
	"/dev/scsi/sc7d9l0", "/dev/scsi/sc7d10l0",
	"/dev/scsi/sc7d11l0", "/dev/scsi/sc7d12l0",
	"/dev/scsi/sc7d13l0", "/dev/scsi/sc7d14l0",
	"/dev/scsi/sc7d15l0",
	"/dev/scsi/sc8d1l0", "/dev/scsi/sc8d2l0",
	"/dev/scsi/sc8d3l0", "/dev/scsi/sc8d4l0",
	"/dev/scsi/sc8d5l0", "/dev/scsi/sc8d6l0",
	"/dev/scsi/sc8d7l0", "/dev/scsi/sc8d8l0",
	"/dev/scsi/sc8d9l0", "/dev/scsi/sc8d10l0",
	"/dev/scsi/sc8d11l0", "/dev/scsi/sc8d12l0",
	"/dev/scsi/sc8d13l0", "/dev/scsi/sc8d14l0",
	"/dev/scsi/sc8d15l0",
	"/dev/scsi/sc9d1l0", "/dev/scsi/sc9d2l0",
	"/dev/scsi/sc9d3l0", "/dev/scsi/sc9d4l0",
	"/dev/scsi/sc9d5l0", "/dev/scsi/sc9d6l0",
	"/dev/scsi/sc9d7l0", "/dev/scsi/sc9d8l0",
	"/dev/scsi/sc9d9l0", "/dev/scsi/sc9d10l0",
	"/dev/scsi/sc9d11l0", "/dev/scsi/sc9d12l0",
	"/dev/scsi/sc9d13l0", "/dev/scsi/sc9d14l0",
	"/dev/scsi/sc9d15l0",
	"/dev/scsi/sc10d1l0", "/dev/scsi/sc10d2l0",
	"/dev/scsi/sc10d3l0", "/dev/scsi/sc10d4l0",
	"/dev/scsi/sc10d5l0", "/dev/scsi/sc10d6l0",
	"/dev/scsi/sc10d7l0", "/dev/scsi/sc10d8l0",
	"/dev/scsi/sc10d9l0", "/dev/scsi/sc10d10l0",
	"/dev/scsi/sc10d11l0", "/dev/scsi/sc10d12l0",
	"/dev/scsi/sc10d13l0", "/dev/scsi/sc10d14l0",
	"/dev/scsi/sc10d15l0",
	"/dev/scsi/sc11d1l0", "/dev/scsi/sc11d2l0",
	"/dev/scsi/sc11d3l0", "/dev/scsi/sc11d4l0",
	"/dev/scsi/sc11d5l0", "/dev/scsi/sc11d6l0",
	"/dev/scsi/sc11d7l0", "/dev/scsi/sc11d8l0",
	"/dev/scsi/sc11d9l0", "/dev/scsi/sc11d10l0",
	"/dev/scsi/sc11d11l0", "/dev/scsi/sc11d12l0",
	"/dev/scsi/sc11d13l0", "/dev/scsi/sc11d14l0",
	"/dev/scsi/sc11d15l0",
	"/dev/scsi/sc12d1l0", "/dev/scsi/sc12d2l0",
	"/dev/scsi/sc12d3l0", "/dev/scsi/sc12d4l0",
	"/dev/scsi/sc12d5l0", "/dev/scsi/sc12d6l0",
	"/dev/scsi/sc12d7l0", "/dev/scsi/sc12d8l0",
	"/dev/scsi/sc12d9l0", "/dev/scsi/sc12d10l0",
	"/dev/scsi/sc12d11l0", "/dev/scsi/sc12d12l0",
	"/dev/scsi/sc12d13l0", "/dev/scsi/sc12d14l0",
	"/dev/scsi/sc12d15l0",
	"/dev/scsi/sc13d1l0", "/dev/scsi/sc13d2l0",
	"/dev/scsi/sc13d3l0", "/dev/scsi/sc13d4l0",
	"/dev/scsi/sc13d5l0", "/dev/scsi/sc13d6l0",
	"/dev/scsi/sc13d7l0", "/dev/scsi/sc13d8l0",
	"/dev/scsi/sc13d9l0", "/dev/scsi/sc13d10l0",
	"/dev/scsi/sc13d11l0", "/dev/scsi/sc13d12l0",
	"/dev/scsi/sc13d13l0", "/dev/scsi/sc13d14l0",
	"/dev/scsi/sc13d15l0",
	"/dev/scsi/sc14d1l0", "/dev/scsi/sc14d2l0",
	"/dev/scsi/sc14d3l0", "/dev/scsi/sc14d4l0",
	"/dev/scsi/sc14d5l0", "/dev/scsi/sc14d6l0",
	"/dev/scsi/sc14d7l0", "/dev/scsi/sc14d8l0",
	"/dev/scsi/sc14d9l0", "/dev/scsi/sc14d10l0",
	"/dev/scsi/sc14d11l0", "/dev/scsi/sc14d12l0",
	"/dev/scsi/sc14d13l0", "/dev/scsi/sc14d14l0",
	"/dev/scsi/sc14d15l0",
	"/dev/scsi/sc15d1l0", "/dev/scsi/sc15d2l0",
	"/dev/scsi/sc15d3l0", "/dev/scsi/sc15d4l0",
	"/dev/scsi/sc15d5l0", "/dev/scsi/sc15d6l0",
	"/dev/scsi/sc15d7l0", "/dev/scsi/sc15d8l0",
	"/dev/scsi/sc15d9l0", "/dev/scsi/sc15d10l0",
	"/dev/scsi/sc15d11l0", "/dev/scsi/sc15d12l0",
	"/dev/scsi/sc15d13l0", "/dev/scsi/sc15d14l0",
	"/dev/scsi/sc15d15l0",
#elif defined(__EMX__)
	"b0t0l0", "b0t1l0", "b0t2l0", "b0t3l0",
	"b0t4l0", "b0t5l0", "b0t6l0", "b0t7l0",
	"b1t0l0", "b1t1l0", "b1t2l0", "b1t3l0",
	"b1t4l0", "b1t5l0", "b1t6l0", "b1t7l0",
	"b2t0l0", "b2t1l0", "b2t2l0", "b2t3l0",
	"b2t4l0", "b2t5l0", "b2t6l0", "b2t7l0",
	"b3t0l0", "b3t1l0", "b3t2l0", "b3t3l0",
	"b3t4l0", "b3t5l0", "b3t6l0", "b3t7l0",
#elif defined(__linux__)
	"/dev/scanner",
	"/dev/sg0", "/dev/sg1", "/dev/sg2", "/dev/sg3",
	"/dev/sg4", "/dev/sg5", "/dev/sg6", "/dev/sg7",
	"/dev/sg8", "/dev/sg9",
	"/dev/sga", "/dev/sgb", "/dev/sgc", "/dev/sgd",
	"/dev/sge", "/dev/sgf", "/dev/sgg", "/dev/sgh",
	"/dev/sgi", "/dev/sgj", "/dev/sgk", "/dev/sgl",
	"/dev/sgm", "/dev/sgn", "/dev/sgo", "/dev/sgp",
	"/dev/sgq", "/dev/sgr", "/dev/sgs", "/dev/sgt",
	"/dev/sgu", "/dev/sgv", "/dev/sgw", "/dev/sgx",
	"/dev/sgy", "/dev/sgz",
#elif defined(__NeXT__)
	"/dev/sg0a", "/dev/sg0b", "/dev/sg0c", "/dev/sg0d",
	"/dev/sg0e", "/dev/sg0f", "/dev/sg0g", "/dev/sg0h",
	"/dev/sg1a", "/dev/sg1b", "/dev/sg1c", "/dev/sg1d",
	"/dev/sg1e", "/dev/sg1f", "/dev/sg1g", "/dev/sg1h",
	"/dev/sg2a", "/dev/sg2b", "/dev/sg2c", "/dev/sg2d",
	"/dev/sg2e", "/dev/sg2f", "/dev/sg2g", "/dev/sg2h",
	"/dev/sg3a", "/dev/sg3b", "/dev/sg3c", "/dev/sg3d",
	"/dev/sg3e", "/dev/sg3f", "/dev/sg3g", "/dev/sg3h",
#elif defined(_AIX)
	"/dev/scanner",
	"/dev/gsc0", "/dev/gsc1", "/dev/gsc2", "/dev/gsc3",
	"/dev/gsc4", "/dev/gsc5", "/dev/gsc6", "/dev/gsc7",
	"/dev/gsc8", "/dev/gsc9", "/dev/gsc10", "/dev/gsc11",
	"/dev/gsc12", "/dev/gsc13", "/dev/gsc14", "/dev/gsc15",
#elif defined(__sun)
	"/dev/scg0a", "/dev/scg0b", "/dev/scg0c", "/dev/scg0d",
	"/dev/scg0e", "/dev/scg0f", "/dev/scg0g",
	"/dev/scg1a", "/dev/scg1b", "/dev/scg1c", "/dev/scg1d",
	"/dev/scg1e", "/dev/scg1f", "/dev/scg1g",
	"/dev/scg2a", "/dev/scg2b", "/dev/scg2c", "/dev/scg2d",
	"/dev/scg2e", "/dev/scg2f", "/dev/scg2g",
	"/dev/sg/0", "/dev/sg/1", "/dev/sg/2", "/dev/sg/3",
	"/dev/sg/4", "/dev/sg/5", "/dev/sg/6",
	"/dev/scsi/scanner/", "/dev/scsi/processor/",
#elif defined(HAVE_CAMLIB_H)
	"/dev/scanner", "/dev/scanner0", "/dev/scanner1",
	"/dev/pass0", "/dev/pass1", "/dev/pass2", "/dev/pass3",
	"/dev/pass4", "/dev/pass5", "/dev/pass6", "/dev/pass7",
#elif defined(__FreeBSD__)
	"/dev/uk0", "/dev/uk1", "/dev/uk2", "/dev/uk3", "/dev/uk4",
	"/dev/uk5", "/dev/uk6",
#elif defined(__NetBSD__)
	"/dev/uk0", "/dev/uk1", "/dev/uk2", "/dev/uk3", "/dev/uk4",
	"/dev/uk5", "/dev/uk6",
	"/dev/ss0",
#elif defined(__OpenBSD__)
	"/dev/uk0", "/dev/uk1", "/dev/uk2", "/dev/uk3", "/dev/uk4",
	"/dev/uk5", "/dev/uk6",
#elif defined(__hpux)
	"/dev/rscsi/",
#endif
	0
      };
      static char *usb_default_dev_list[] = {
#if defined(__linux__)
	"/dev/usb/scanner",
	"/dev/usb/scanner0", "/dev/usb/scanner1",
	"/dev/usb/scanner2", "/dev/usb/scanner3",
	"/dev/usb/scanner4", "/dev/usb/scanner5",
	"/dev/usb/scanner5", "/dev/usb/scanner7",
	"/dev/usb/scanner8", "/dev/usb/scanner9",
	"/dev/usb/scanner10", "/dev/usb/scanner11",
	"/dev/usb/scanner12", "/dev/usb/scanner13",
	"/dev/usb/scanner14", "/dev/usb/scanner15",
	"/dev/usbscanner",
	"/dev/usbscanner0", "/dev/usbscanner1",
	"/dev/usbscanner2", "/dev/usbscanner3",
	"/dev/usbscanner4", "/dev/usbscanner5",
	"/dev/usbscanner6", "/dev/usbscanner7",
	"/dev/usbscanner8", "/dev/usbscanner9",
	"/dev/usbscanner10", "/dev/usbscanner11",
	"/dev/usbscanner12", "/dev/usbscanner13",
	"/dev/usbscanner14", "/dev/usbscanner15",
#elif defined(__FreeBSD__)
	"/dev/uscanner",
	"/dev/uscanner0", "/dev/uscanner1",
	"/dev/uscanner2", "/dev/uscanner3",
	"/dev/uscanner4", "/dev/uscanner5",
	"/dev/uscanner6", "/dev/uscanner7",
	"/dev/uscanner8", "/dev/uscanner9",
	"/dev/uscanner10", "/dev/uscanner11",
	"/dev/uscanner12", "/dev/uscanner13",
	"/dev/uscanner14", "/dev/uscanner15",
#elif defined(__OpenBSD__)
	"/dev/uscanner",
	"/dev/uscanner0", "/dev/uscanner1",
	"/dev/uscanner2", "/dev/uscanner3",
	"/dev/uscanner4", "/dev/uscanner5",
	"/dev/uscanner6", "/dev/uscanner7",
	"/dev/uscanner8", "/dev/uscanner9",
	"/dev/uscanner10", "/dev/uscanner11",
	"/dev/uscanner12", "/dev/uscanner13",
	"/dev/uscanner14", "/dev/uscanner15",
#endif
	0
      };

      dev_list = default_dev_list;
      usb_dev_list = usb_default_dev_list;
    }

  if (verbose > 0)
    printf ("\n");
  if (verbose > 1)
    printf ("searching for SCSI scanners:\n");

  while ((dev_name = *dev_list++))
    {
      if (strlen (dev_name) == 0)
	continue;		/* Empty device names ... */

      if (dev_name[strlen (dev_name) - 1] == '/')
	{
	  /* check whole directories */
	  DIR *dir;
	  char *file_name;

	  dir = scan_directory (dev_name);
	  if (!dir)
	    continue;

	  while ((file_name = get_next_file (dev_name, dir)))
	    check_scsi_file (file_name);
	}
      else
	{
	  /* check device files */
	  check_scsi_file (dev_name);
	}
    }
  if (device_found)
    {
      if (verbose > 0)
	printf
	  ("  # Your SCSI scanner was detected. It may or may not be "
	   "supported by SANE. Try\n  # scanimage -L and read the backend's "
	   "manpage.\n");
    }
  else
    {
      if (verbose > 0)
	printf
	  ("  # No SCSI scanners found. If you expected something different, "
	   "make sure that\n  # you have loaded a SCSI driver for your SCSI "
	   "adapter.\n");
      if (!check_sg ())
	{
	  if (verbose > 0)
	    printf
	      ("  # Also you need support for SCSI Generic (sg) in your "
	       "operating system.\n  # If using Linux, try \"modprobe "
	       "sg\".\n");
	}
    }
  if (verbose > 0)
    printf ("\n");
  device_found = SANE_FALSE;
  sanei_usb_init ();
  if (verbose > 1)
    printf ("searching for USB scanners:\n");

  while ((dev_name = *usb_dev_list++))
    {
      if (strlen (dev_name) == 0)
	continue;		/* Empty device names ... */

      if (dev_name[strlen (dev_name) - 1] == '/')
	{
	  /* check whole directories */
	  DIR *dir;
	  char *file_name;

	  dir = scan_directory (dev_name);
	  if (!dir)
	    continue;

	  while ((file_name = get_next_file (dev_name, dir)))
	    check_usb_file (file_name);
	}
      else
	{
	  /* check device files */
	  check_usb_file (dev_name);
	}
    }
#ifdef HAVE_LIBUSB
  /* Now the libusb devices */
  {
    struct usb_bus *bus;
    struct usb_device *dev;

    if (ap < argv + argc)
      {
	/* user-specified devices not useful for libusb */
	if (verbose > 1)
	  printf ("ignoring libusb devices\n");
      }
    else
      {
	if (verbose > 2)
	  printf ("trying libusb:\n");
	for (bus = usb_get_busses (); bus; bus = bus->next)
	  {
	    for (dev = bus->devices; dev; dev = dev->next)
	      {
		check_libusb_device (dev);
	      }			/* for (dev) */
	  }			/* for (bus) */
      }				/* if (usb_dev_list == ap) */
  }
#endif /* HAVE_LIBUSB */

  if (device_found)
    {
      if (libusb_device_found)
	{
	  if (verbose > 0)
	    printf
	      ("  # Your USB scanner was (probably) detected. It "
	       "may or may not be supported by\n  # SANE. Try scanimage "
	       "-L and read the backend's manpage.\n");
	}
      else if (verbose > 0)
	printf
	  ("  # Your USB scanner was detected. It may or may not "
	   "be supported by\n  # SANE. Try scanimage -L and read the "
	   "backend's manpage.\n");
      if (unknown_found && verbose > 0)
	printf
	  ("  # `UNKNOWN vendor and product' means that there seems to be a "
	   "scanner at this\n  # device file but the vendor and product ids "
	   "couldn't be identified.\n  # Currently identification only works "
	   "with Linux versions >= 2.4.8. You may\n  # need to configure your "
	   "backend manually, see the backend's manpage.\n");
    }
  else
    {
      if (verbose > 0)
	printf
	  ("  # No USB scanners found. If you expected something different, "
	   "make sure that\n  # you have loaded a driver for your USB host "
	   "controller and have installed a\n  # kernel scanner module.\n");
    }
  if (verbose > 0)
    printf ("\n  # Scanners connected to the parallel port or other "
	    "proprietary ports can't be\n  # detected by this program.\n");
  if (getuid ())
    if (verbose > 0)
      printf
	("\n  # You may want to run this program as root to find all devices. "
	 "Once you\n  # found the scanner devices, be sure to adjust access "
	 "permissions as\n  # necessary.\n");

  if (verbose > 1)
    printf ("done\n");

  return 0;
}

--ew6BAiZeqk4r7MaW--