diff options
author | Jörg Frings-Fürst <debian@jff-webhosting.net> | 2014-10-06 14:00:40 +0200 |
---|---|---|
committer | Jörg Frings-Fürst <debian@jff-webhosting.net> | 2014-10-06 14:00:40 +0200 |
commit | 6e9c41a892ed0e0da326e0278b3221ce3f5713b8 (patch) | |
tree | 2e301d871bbeeb44aa57ff9cc070fcf3be484487 /sanei/sanei_scsi.c |
Initial import of sane-backends version 1.0.24-1.2
Diffstat (limited to 'sanei/sanei_scsi.c')
-rw-r--r-- | sanei/sanei_scsi.c | 6187 |
1 files changed, 6187 insertions, 0 deletions
diff --git a/sanei/sanei_scsi.c b/sanei/sanei_scsi.c new file mode 100644 index 0000000..6d171d3 --- /dev/null +++ b/sanei/sanei_scsi.c @@ -0,0 +1,6187 @@ +/* sane - Scanner Access Now Easy. + Copyright (C) 1996, 1997 David Mosberger-Tang + Copyright (C) 2003 Frank Zago + 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 SCSI interface. */ + +#ifdef _AIX +# include "../include/lalloca.h" /* MUST come first for AIX! */ +#endif + +#include "../include/sane/config.h" +#include "../include/lalloca.h" +#include "../include/lassert.h" + +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#ifdef HAVE_SYS_IOCTL_H +#include <sys/ioctl.h> +#endif +#include <sys/param.h> +#include <sys/types.h> + +#if defined (HAVE_WINDOWS_H) +# include <windows.h> +#endif + +#define STUBBED_INTERFACE 0 +#define LINUX_INTERFACE 1 +#define BSD_INTERFACE 2 +#define HPUX_INTERFACE 3 +#define OPENSTEP_INTERFACE 4 +#define DECUNIX_INTERFACE 5 +#define SCO_OS5_INTERFACE 6 +#define IRIX_INTERFACE 7 +#define SOLARIS_INTERFACE 8 +#define SOLARIS_SG_INTERFACE 9 +#define OS2_INTERFACE 10 +#define AIX_GSC_INTERFACE 11 +#define DOMAINOS_INTERFACE 12 +#define FREEBSD_CAM_INTERFACE 13 +#define SYSVR4_INTERFACE 14 +#define SCO_UW71_INTERFACE 15 +#define SOLARIS_USCSI_INTERFACE 16 +#define MACOSX_INTERFACE 17 +#define WIN32_INTERFACE 18 + +#ifdef HAVE_RESMGR +# include <resmgr.h> +#endif + +#if defined (HAVE_SCSI_SG_H) +# define USE LINUX_INTERFACE +# include <scsi/sg.h> +#elif defined (HAVE__USR_SRC_LINUX_INCLUDE_SCSI_SG_H) +# define USE LINUX_INTERFACE +# include "/usr/src/linux/include/scsi/sg.h" +#elif defined (HAVE_SYS_SCSICMD) +# define USE SCSO_OS5_INTERFACE +# include <sys/scsi.h> +# include <sys/scsicmd.h> +#elif defined (HAVE_CAMLIB_H) +# define USE FREEBSD_CAM_INTERFACE +# include <stdio.h> /* there is a bug in scsi_all.h */ +# include <cam/cam.h> +# include <cam/cam_ccb.h> +# include <cam/scsi/scsi_message.h> +# include <cam/scsi/scsi_pass.h> +# include <camlib.h> +#elif defined (HAVE_SYS_SCSIIO_H) +# define USE BSD_INTERFACE +# include <sys/scsiio.h> +# ifdef HAVE_SCSI_H +# include <scsi.h> +# endif +#elif defined (HAVE_BSD_DEV_SCSIREG_H) +# define USE OPENSTEP_INTERFACE +# include <bsd/dev/scsireg.h> +#elif defined (HAVE_IO_CAM_CAM_H) +# define USE DECUNIX_INTERFACE +# include <io/common/iotypes.h> +# include <io/cam/cam.h> +# include <io/cam/dec_cam.h> +# include <io/cam/uagt.h> +# include <io/cam/scsi_all.h> +#elif defined (HAVE_SYS_DSREQ_H) +# define USE IRIX_INTERFACE +# include <sys/dsreq.h> +# include <invent.h> +#elif defined (HAVE_SYS_SCSI_H) +# include <sys/scsi.h> +# ifdef HAVE_SYS_SDI_COMM_H +# ifdef HAVE_SYS_PASSTHRUDEF_H +# define USE SCO_UW71_INTERFACE +# include <sys/scsi.h> +# include <sys/sdi_edt.h> +# include <sys/sdi.h> +# include <sys/passthrudef.h> +# else +# define USE SYSVR4_INTERFACE /* Unixware 2.x tested */ +# define HAVE_SYSV_DRIVER +# include <sys/sdi_edt.h> +# include <sys/sdi_comm.h> +# endif +# else +# ifdef SCTL_READ +# define USE HPUX_INTERFACE +# else +# ifdef HAVE_GSCDDS_H +# define USE AIX_GSC_INTERFACE +# include <gscdds.h> +# else + /* This happens for AIX without gsc and possibly other platforms... */ +# endif +# endif +# endif +#elif defined (HAVE_OS2_H) +# define USE OS2_INTERFACE +# define INCL_DOSFILEMGR +# define INCL_DOS +# define INCL_DOSDEVICES +# define INCL_DOSDEVIOCTL +# define INCL_DOSMEMMGR +# include <os2.h> +# include "os2_srb.h" +#elif defined (HAVE_SYS_SCSI_SGDEFS_H) +# define USE SOLARIS_SG_INTERFACE +# include <sys/scsi/sgdefs.h> +#elif defined (HAVE_SYS_SCSI_TARGETS_SCGIO_H) +# define USE SOLARIS_INTERFACE +# define SOL2 +# include <sys/scsi/targets/scgio.h> +#elif defined (HAVE_SYS_SCSI_SCSI_H) + /* + * the "offical" solaris uscsi(7I) interface; comes last, so that users + * installing the SCG/SG driver can still use these generic scsi interfaces + */ +# define USE SOLARIS_USCSI_INTERFACE +# define SOL2 +# include <sys/scsi/scsi.h> +#elif defined (HAVE_APOLLO_SCSI_H) +# define USE DOMAINOS_INTERFACE +# include <signal.h> /* Only used for signal name for KillDomainServer */ +# include <apollo/base.h> +# include <apollo/ec2.h> +# include <apollo/error.h> +# include <apollo/ms.h> +# include <apollo/mutex.h> +# include <apollo/scsi.h> +# include <apollo/time.h> +# include "sanei_DomainOS.h" +#elif defined (HAVE_IOKIT_CDB_IOSCSILIB_H) || \ + defined (HAVE_IOKIT_SCSI_SCSICOMMANDOPERATIONCODES_H) || \ + defined (HAVE_IOKIT_SCSI_COMMANDS_SCSICOMMANDOPERATIONCODES_H) +# define USE MACOSX_INTERFACE +# include <CoreFoundation/CoreFoundation.h> +# include <IOKit/IOKitLib.h> +# ifdef HAVE_IOKIT_CDB_IOSCSILIB_H +# include <IOKit/IOCFPlugIn.h> +# include <IOKit/cdb/IOSCSILib.h> +# endif +# ifdef HAVE_IOKIT_SCSI_SCSICOMMANDOPERATIONCODES_H +/* The def of VERSION causes problems in the following include files */ +# undef VERSION +# include <IOKit/scsi/SCSICmds_INQUIRY_Definitions.h> +# include <IOKit/scsi/SCSICommandOperationCodes.h> +# include <IOKit/scsi/SCSITaskLib.h> +# else +# ifdef HAVE_IOKIT_SCSI_COMMANDS_SCSICOMMANDOPERATIONCODES_H +/* The def of VERSION causes problems in the following include files */ +# undef VERSION +# include <IOKit/scsi-commands/SCSICmds_INQUIRY_Definitions.h> +# include <IOKit/scsi-commands/SCSICommandOperationCodes.h> +# include <IOKit/scsi-commands/SCSITaskLib.h> +# endif +# endif +#elif defined (HAVE_DDK_NTDDSCSI_H) +# define USE WIN32_INTERFACE +# include <ddk/scsi.h> +# include <ddk/ntddscsi.h> +#elif defined (HAVE_NTDDSCSI_H) +# define USE WIN32_INTERFACE +# include <ntddscsi.h> +#endif + +#ifndef USE +# define USE STUBBED_INTERFACE +#endif + +#if USE == LINUX_INTERFACE +# include <dirent.h> +#endif + +#include "../include/sane/sanei.h" +#include "../include/sane/sanei_config.h" +#include "../include/sane/sanei_scsi.h" + +#define BACKEND_NAME sanei_scsi +#include "../include/sane/sanei_debug.h" + +#if USE == DECUNIX_INTERFACE +static int cam_fd = -1; /* used for SCSI CAM based interfaces */ +#endif + +#if USE == SOLARIS_INTERFACE || USE == SOLARIS_USCSI_INTERFACE +static int unit_ready (int fd); +#endif + +#ifdef SG_BIG_BUFF +# define MAX_DATA SG_BIG_BUFF +#endif + +#if USE == SYSVR4_INTERFACE +# define MAX_DATA 56*1024 /* don't increase or kernel will dump + * tested with adsl, adsa and umax backend + * it depends on the lowend scsi + * drivers . But the most restriction + * is in the UNIXWARE KERNEL witch do + * not allow more then 64kB DMA transfers */ +static char lastrcmd[16]; /* hold command block of last read command */ +#endif + +#if USE == OPENSTEP_INTERFACE +# define MAX_DATA (120*1024) +#endif + +#if USE == IRIX_INTERFACE +# define MAX_DATA (256*1024) +#endif + +#if USE == FREEBSD_CAM_INTERFACE +# define MAX_DATA (DFLTPHYS - PAGE_SIZE) +#endif + +#if USE == SOLARIS_INTERFACE +# define MAX_DATA (128*1024) +#endif + +#if USE == SOLARIS_SG_INTERFACE +# define MAX_DATA (128*1024) +#endif + +#if USE == SOLARIS_USCSI_INTERFACE +# define MAX_DATA (64*1024) +#endif + +#if USE == OS2_INTERFACE +# define MAX_DATA (64*1024) +#endif + +#if USE == MACOSX_INTERFACE +# define MAX_DATA (128*1024) +#endif + + +#ifndef MAX_DATA +# define MAX_DATA (32*1024) +#endif + +#ifdef SG_SET_TIMEOUT +# ifdef _SC_CLK_TCK +# define GNU_HZ sysconf(_SC_CLK_TCK) +# else +# ifdef HZ +# define GNU_HZ HZ +# else +# ifdef CLOCKS_PER_SEC +# define GNU_HZ CLOCKS_PER_SEC +# endif +# endif +# endif +#endif + +/* default timeout value: 120 seconds */ +static int sane_scsicmd_timeout = 120; +int sanei_scsi_max_request_size = MAX_DATA; +#if USE == LINUX_INTERFACE +/* the following #defines follow Douglas Gilbert's sample code + to maintain run time compatibility with the old and the + new SG driver for Linux +*/ +#include "linux_sg3_err.h" /* contains several definitions of error codes */ +#ifndef SG_SET_COMMAND_Q +#define SG_SET_COMMAND_Q 0x2271 +#endif +#ifndef SG_SET_RESERVED_SIZE +#define SG_SET_RESERVED_SIZE 0x2275 +#endif +#ifndef SG_GET_RESERVED_SIZE +#define SG_GET_RESERVED_SIZE 0x2272 +#endif +#ifndef SG_GET_SCSI_ID +#define SG_GET_SCSI_ID 0x2276 +#else +#define SG_GET_SCSI_ID_FOUND +#endif +#ifndef SG_GET_VERSION_NUM +#define SG_GET_VERSION_NUM 0x2282 +#endif +#ifndef SG_NEXT_CMD_LEN +#define SG_NEXT_CMD_LEN 0x2283 +#endif + +#ifndef SCSIBUFFERSIZE +#define SCSIBUFFERSIZE (128 * 1024) +#endif + +/* the struct returned by the SG ioctl call SG_GET_SCSI_ID changed + from version 2.1.34 to 2.1.35, and we need the informations from + the field s_queue_depth, which was introduced in 2.1.35. + To get this file compiling also with older versions of sg.h, the + struct is re-defined here. +*/ +typedef struct xsg_scsi_id +{ + int host_no; /* as in "scsi<n>" where 'n' is one of 0, 1, 2 etc */ + int channel; + int scsi_id; /* scsi id of target device */ + int lun; + int scsi_type; /* TYPE_... defined in scsi/scsi.h */ + short h_cmd_per_lun; /* host (adapter) maximum commands per lun */ + short d_queue_depth; /* device (or adapter) maximum queue length */ + int unused1; /* probably find a good use, set 0 for now */ + int unused2; /* ditto */ +} +SG_scsi_id; + +typedef struct req +{ + struct req *next; + int fd; + u_int running:1, done:1; + SANE_Status status; + size_t *dst_len; + void *dst; +/* take the definition of the ioctl parameter SG_IO as a + compiler flag if the new SG driver is available +*/ + union + { + struct + { + struct sg_header hdr; + /* Make sure this is the last element, the real size is + SG_BIG_BUFF and machine dependant */ + u_int8_t data[1]; + } + cdb; +#ifdef SG_IO +/* at present, Linux's SCSI system limits the sense buffer to 16 bytes + which is definitely too small. Hoping that this will change at some time, + let's set the sense buffer size to 64. +*/ +#define SENSE_MAX 64 +#define MAX_CDB 12 + struct + { + struct sg_io_hdr hdr; + u_char sense_buffer[SENSE_MAX]; + u_int8_t data[1]; + } + sg3; +#endif + } + sgdata; +} +req; + +typedef struct Fdparms +{ + int sg_queue_used, sg_queue_max; + size_t buffersize; + req *sane_qhead, *sane_qtail, *sane_free_list; +} +fdparms; + +#endif + +#if USE == FREEBSD_CAM_INTERFACE +# define CAM_MAXDEVS 128 +struct cam_device *cam_devices[CAM_MAXDEVS] = { NULL }; +#endif + +static struct +{ + u_int in_use:1; /* is this fd_info in use? */ + u_int fake_fd:1; /* is this a fake file descriptor? */ + u_int bus, target, lun; /* nexus info; used for some interfaces only */ + SANEI_SCSI_Sense_Handler sense_handler; + void *sense_handler_arg; + void *pdata; /* platform-specific data */ +} + *fd_info; + +static u_char cdb_sizes[8] = { + 6, 10, 10, 12, 12, 12, 10, 10 +}; +#define CDB_SIZE(opcode) cdb_sizes[(((opcode) >> 5) & 7)] + + +#if USE == DOMAINOS_INTERFACE + +/* + This includes the server code. Most of these routines are private to the + actual server. The only public ones are: + sanei_DomainOS_init Used to initialize the server + DomainErrorCheck A common error handling routine + */ + +#include "sanei_DomainOS.c" + +int ServerInitialized = 0; +pid_t ServerPID; +struct DomainServerCommon *com; +long CommandTriggerValue[2]; +ec2_$ptr_t CommandAcceptedPtr[2]; +long ResultTriggerValue[2]; +ec2_$ptr_t ResultReadyPtr[2]; +time_$clock_t Wait16S = { 64, 0 }; /* Delay of about 16 Seconds */ + + +/* This function is registered as an exit function. It's purpose is + to make sure that the Domain SANE Server is stopped. It tries to + send an Exit command, and if that fails, it will send SIGQUIT to + the server. It will also unmap the common area before it + returns. */ +static void +KillDomainServer (void) +{ + static boolean GotTheLock; + static status_$t status; + static pinteger index; + + DBG (1, "Asking Domain SANE Server to exit\n"); + /* First, try to send a command to exit */ + if (GotTheLock = mutex_$lock (&com->CommandLock, Wait16S)) + { + /* Set the wait time to 16 Seconds (units are 4uS) */ + com->opcode = Exit; + CommandTriggerValue[0] = ec2_$read (com->CommandAccepted) + 1; + ec2_$advance (&com->CommandAvailable, &status); + DomainErrorCheck (status, "Can't advance CommandAvailable EC"); + /* For this wait, we want to allow a timeout as well */ + CommandTriggerValue[1] = (ec2_$read (*CommandAcceptedPtr[1]) + + DomainECWaitConstant); + index = ec2_$wait_svc (CommandAcceptedPtr, CommandTriggerValue, 2, + &status); + DomainErrorCheck (status, + "Error waiting on Exit command acceptance EC"); + /* Release the lock */ + mutex_$unlock (&com->CommandLock); + if (index == 1) + DBG (1, "Domain SANE Server responded to exit request\n"); + else + DBG (1, "Domain SANE Server did not respond to exit request\n"); + } + else + DBG (0, "Could not get mutex lock for killing server\n"); + if ((!GotTheLock) || (index != 1)) + { + /* If we get here, then we never got the mutex lock, or we timed out + waiting for an Exit command ack. */ + /* It's now time to be brutal with the server */ + DBG (1, "Sending QUIT signal to Domain SANE Server\n"); + kill (ServerPID, SIGQUIT); + } + /* unmap the common area */ + ms_$unmap (com, sizeof (struct DomainServerCommon), &status); + DomainErrorCheck (status, "Error unmapping common area"); +} +#endif /* USE == DOMAINOS_INTERFACE */ + + +#if USE == OS2_INTERFACE + +/* Driver info: */ +static HFILE driver_handle = 0; /* file handle for device driver */ +static PVOID aspi_buf = 0; /* Big data buffer locked by driver. */ +static int aspi_ref_count = 0; /* # of fds using ASPI */ +static SRB *PSRBlock = 0; /* SCSI Request Block */ +static char tmpAspi[MAXPATHLEN]; /* scsi chain scan */ +#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_inquiry_vendor(in, buf) strncpy(buf, in + 0x08, 0x08) +#define get_inquiry_product(in, buf) strncpy(buf, in + 0x10, 0x010) +#define get_inquiry_version(in, buf) strncpy(buf, in + 0x20, 0x04) +#define get_inquiry_periph_devtype(in) (in[0] & 0x1f) +#define get_inquiry_additional_length(in) in[0x04] +#define set_inquiry_length(out,n) out[0x04]=n-5 + +/* Open OS2 ASPI driver. + + Output: 0 if error, which is reported. */ +static int +open_aspi (void) +{ + ULONG rc; + ULONG ActionTaken; + USHORT lockSegmentReturn; + unsigned long cbreturn = 0; + unsigned long cbParam = 0; + int i, num_adapters; /* no. of scsi adapters installed */ + + char *devtypes[] = { + "disk", "tape", "printer", "processor", "CD-writer", + "CD-drive", "scanner", "optical-drive", "jukebox", + "communicator" + }; + FILE *tmp; + + if (driver_handle) + { + aspi_ref_count++; /* increment internal usage counter */ + return 1; /* Already open. */ + } + aspi_buf = _tcalloc (sanei_scsi_max_request_size, 1); + if (aspi_buf == NULL) + { + DBG (1, "sanei_scsi_open_aspi: _tcalloc aspi_buf failed"); + return 0; + } + + PSRBlock = _tcalloc (sizeof (SRB), 1); + if (PSRBlock == NULL) + { + DBG (1, "sanei_scsi_open_aspi: _tcalloc PSRBlock failed"); + return 0; + } + + rc = DosOpen ((PSZ) "aspirou$", /* open driver */ + &driver_handle, + &ActionTaken, + 0, + 0, + FILE_OPEN, + OPEN_SHARE_DENYREADWRITE | OPEN_ACCESS_READWRITE, NULL); + if (rc) + { + /* opening failed -> return false */ + DBG (1, "open_aspi: opening failed.\n"); + return 0; + } + + /* Lock aspi_buf. */ + rc = DosDevIOCtl (driver_handle, 0x92, 0x04, /* pass aspi_buf pointer */ + (void *) aspi_buf, sizeof (PVOID), /* to driver */ + &cbParam, (void *) &lockSegmentReturn, + sizeof (USHORT), &cbreturn); + if (rc || lockSegmentReturn) + { + /* DosDevIOCtl failed */ + DBG (1, "sanei_scsi_open_aspi: Can't lock buffer. rc= %lu \n", rc); + return 0; + } + + /* query number of installed adapters */ + memset (PSRBlock, 0, sizeof (SRB)); + PSRBlock->cmd = SRB_Inquiry; /* host adapter inquiry */ + + PSRBlock->ha_num = 0; /* host adapter number */ + + PSRBlock->flags = 0; /* no flags set */ + + rc = DosDevIOCtl (driver_handle, 0x92, 0x02, + (void *) PSRBlock, sizeof (SRB), &cbParam, + (void *) PSRBlock, sizeof (SRB), &cbreturn); + num_adapters = PSRBlock->u.inq.num_ha; + + DBG (1, "OS/2: installed adapters %d\n", num_adapters); + DBG (1, "OS/2: ASPI manager is '%s'\n", PSRBlock->u.inq.aspimgr_id); + DBG (1, "OS/2: host adapter is '%s'\n", PSRBlock->u.inq.host_id); + DBG (1, "OS/2: unique id is '%s'\n", PSRBlock->u.inq.unique_id); + + strcpy (tmpAspi, "asXXXXXX"); + mktemp (tmpAspi); + DBG (2, "open_aspi: open temporary file '%s'\n", tmpAspi); + tmp = fopen (tmpAspi, "w"); + if (!tmp) + { /* can't open tmp file */ + + DBG (1, "open_aspi: Can't open temporary file.\n"); + return 0; + } + + /* scan all installed adapters */ + for (i = 0; i < num_adapters; i++) + { + int id; + /* query adapter name */ + memset (PSRBlock, 0, sizeof (SRB)); + PSRBlock->cmd = SRB_Inquiry; /* host adapter inquiry */ + + PSRBlock->ha_num = i; /* host adapter number */ + + PSRBlock->flags = 0; /* no flags set */ + + rc = DosDevIOCtl (driver_handle, 0x92, 0x02, + (void *) PSRBlock, sizeof (SRB), &cbParam, + (void *) PSRBlock, sizeof (SRB), &cbreturn); + DBG (1, "OS/2: adapter#%02d '%s'\n", i, PSRBlock->u.inq.host_id); + + /* scan scsi chain (need 15 for wide?) */ + for (id = 0; id < 7; id++) + { + unsigned char len; + char vendor[9]; + char product[17]; + char version[5]; + char *pp; + + memset (PSRBlock, 0, sizeof (SRB)); + PSRBlock->cmd = SRB_Device; /* get device type */ + + PSRBlock->ha_num = i; /* host adapter number */ + + PSRBlock->flags = 0; /* no flags set */ + + PSRBlock->u.dev.target = id; /* target id */ + + PSRBlock->u.dev.lun = 0; /* target LUN */ + + rc = DosDevIOCtl (driver_handle, 0x92, 0x02, + (void *) PSRBlock, sizeof (SRB), &cbParam, + (void *) PSRBlock, sizeof (SRB), &cbreturn); + DBG (1, "OS/2: id#%02d status=%02xh\n", + id, PSRBlock->status); + + /* skip if device not connected */ + if (PSRBlock->status == SRB_BadDevice) + continue; + + DBG (1, "OS/2: type is '%s'\n", + PSRBlock->u.dev.devtype < sizeof (devtypes) / sizeof (char *)? + devtypes[PSRBlock->u.dev.devtype] : "unknown device"); + + /* query adapter string id */ + memset (PSRBlock, 0, sizeof (SRB)); + PSRBlock->cmd = SRB_Command; /* execute SCSI command */ + + PSRBlock->ha_num = i; /* host adapter number */ + PSRBlock->flags = SRB_Read | SRB_Post; /* data transfer, posting */ + PSRBlock->u.cmd.target = id; /* Target SCSI ID */ + PSRBlock->u.cmd.lun = 0; /* Target SCSI LUN */ + PSRBlock->u.cmd.data_len = 5; /* # of bytes transferred */ + PSRBlock->u.cmd.sense_len = 32; /* length of sense buffer */ + PSRBlock->u.cmd.data_ptr = NULL; /* pointer to data buffer */ + PSRBlock->u.cmd.link_ptr = NULL; /* pointer to next SRB */ + PSRBlock->u.cmd.cdb_len = 6; /* SCSI command length */ + PSRBlock->u.cmd.cdb_st[0] = INQUIRY; /* inquiry command */ + PSRBlock->u.cmd.cdb_st[1] = 0; /* ?? length */ + PSRBlock->u.cmd.cdb_st[2] = 0; /* transfer length MSB */ + PSRBlock->u.cmd.cdb_st[3] = 0; /* transfer length */ + PSRBlock->u.cmd.cdb_st[4] = 5; /* transfer length LSB */ + PSRBlock->u.cmd.cdb_st[5] = 0; + rc = DosDevIOCtl (driver_handle, 0x92, 0x02, + (void *) PSRBlock, sizeof (SRB), &cbParam, + (void *) PSRBlock, sizeof (SRB), &cbreturn); + len = ((char *) aspi_buf)[4]; /* additional length */ + + /* query id string */ + memset (PSRBlock, 0, sizeof (SRB)); + PSRBlock->cmd = SRB_Command; /* execute SCSI command */ + PSRBlock->ha_num = i; /* host adapter number */ + PSRBlock->flags = SRB_Read | SRB_Post; /* data transfer, posting */ + PSRBlock->u.cmd.target = id; /* Target SCSI ID */ + PSRBlock->u.cmd.lun = 0; /* Target SCSI LUN */ + PSRBlock->u.cmd.data_len = 5 + len; /* # of bytes transferred */ + PSRBlock->u.cmd.sense_len = 32; /* length of sense buffer */ + PSRBlock->u.cmd.data_ptr = NULL; /* pointer to data buffer */ + PSRBlock->u.cmd.link_ptr = NULL; /* pointer to next SRB */ + PSRBlock->u.cmd.cdb_len = 6; /* SCSI command length */ + PSRBlock->u.cmd.cdb_st[0] = 0x12; /* inquiry command */ + PSRBlock->u.cmd.cdb_st[1] = 0; /* ?? length */ + PSRBlock->u.cmd.cdb_st[2] = 0; /* transfer length MSB */ + PSRBlock->u.cmd.cdb_st[3] = 0; /* transfer length */ + PSRBlock->u.cmd.cdb_st[4] = 5 + len; /* transfer length LSB */ + PSRBlock->u.cmd.cdb_st[5] = 0; + rc = DosDevIOCtl (driver_handle, 0x92, 0x02, + (void *) PSRBlock, sizeof (SRB), &cbParam, + (void *) PSRBlock, sizeof (SRB), &cbreturn); + DBG (1, "OS/2 '%s'\n", (char *) aspi_buf + 8); + /* write data */ + get_inquiry_vendor ((char *) aspi_buf, vendor); + get_inquiry_product ((char *) aspi_buf, product); + get_inquiry_version ((char *) aspi_buf, 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 = product; + do + { + if (isspace ((int) *pp)) + *pp = '_'; + } + while (*++pp); + + pp = &version[3]; + version[4] = '\0'; + while (pp >= version && (*pp == ' ' || *(pp - 1) >= 127)) + *pp-- = '\0'; + fprintf (tmp, "Vendor: %s ", vendor); + fprintf (tmp, "Model: %s ", product); + fprintf (tmp, "Rev: %s ", version); + fprintf (tmp, "scsi %d Channel: 0 Id: %d Lun: 0\n", i, id); + } + } + DBG (2, "open_aspi: close temporary file '%s'\n", tmpAspi); + fclose (tmp); + + aspi_ref_count++; /* increment internal usage counter */ + + return 1; +} + +/* Close driver and free everything. */ + +static void +close_aspi (void) +{ + aspi_ref_count--; /* decrement internal usage counter */ + + if (aspi_ref_count) + return; /* wait for usage==0 */ + + if (driver_handle) /* Close driver. */ + DosClose (driver_handle); + driver_handle = 0; + if (aspi_buf) /* Free buffer. */ + _tfree (aspi_buf); + aspi_buf = 0; + + if (PSRBlock) + _tfree (PSRBlock); + PSRBlock = 0; + + errno = 0; + if (unlink (tmpAspi)) /* remove scsi descriptions */ + DBG (2, "OS/2: error#%d while removing temporary '%s'\n", errno, tmpAspi); + strcpy (tmpAspi, ""); + + DBG (1, "OS/2: ASPI closed\n"); +} + +#endif /* USE_OS2_INTERFACE */ + +static int num_alloced = 0; + +#if USE == LINUX_INTERFACE + +static int sg_version = 0; + +static SANE_Status +get_max_buffer_size (const char *file) +{ + int fd = -1; + int buffersize = SCSIBUFFERSIZE, i; + size_t len; + char *cc, *cc1, buf[32]; + +#ifdef HAVE_RESMGR + fd = rsm_open_device(file, O_RDWR); +#endif + + if (fd == -1) + fd = open (file, O_RDWR); + + if (fd > 0) + { + cc = getenv ("SANE_SG_BUFFERSIZE"); + if (cc) + { + i = strtol (cc, &cc1, 10); + if (cc != cc1 && i >= 32768) + buffersize = i; + } + + ioctl (fd, SG_SET_RESERVED_SIZE, &buffersize); + if (0 == ioctl (fd, SG_GET_RESERVED_SIZE, &buffersize)) + { + if (buffersize < sanei_scsi_max_request_size) + sanei_scsi_max_request_size = buffersize; + close (fd); + DBG (4, "get_max_buffer_size for %s: %i\n", file, + sanei_scsi_max_request_size); + return SANE_STATUS_GOOD; + } + else + { + close (fd); + /* ioctl not available: we have the old SG driver */ + fd = open ("/proc/sys/kernel/sg-big-buff", O_RDONLY); + if (fd > 0 && (len = read (fd, buf, sizeof (buf) - 1)) > 0) + { + buf[len] = '\0'; + sanei_scsi_max_request_size = atoi (buf); + close (fd); + } + else + sanei_scsi_max_request_size = buffersize < SG_BIG_BUFF ? + buffersize : SG_BIG_BUFF; + return SANE_STATUS_IO_ERROR; + } + } + else + return SANE_STATUS_GOOD; +} + + +SANE_Status +sanei_scsi_open_extended (const char *dev, int *fdp, + SANEI_SCSI_Sense_Handler handler, + void *handler_arg, int *buffersize) +#else + +SANE_Status +sanei_scsi_open (const char *dev, int *fdp, + SANEI_SCSI_Sense_Handler handler, void *handler_arg) +#endif +{ + u_int bus = 0, target = 0, lun = 0, fake_fd = 0; + char *real_dev = 0; + void *pdata = 0; + char *cc, *cc1; + int fd, i; +#if USE == LINUX_INTERFACE + static int first_time = 1; +#elif USE == MACOSX_INTERFACE + UInt8 *guid; + int len; + u_int d; +#endif + + cc = getenv ("SANE_SCSICMD_TIMEOUT"); + if (cc) + { + i = strtol (cc, &cc1, 10); + /* 20 minutes are hopefully enough as a timeout value ;) */ + if (cc != cc1 && i > 0 && i <= 1200) + { + sane_scsicmd_timeout = i; + } + else + { + DBG (1, + "sanei_scsi_open: timeout value must be between 1 and 1200 seconds\n"); + } + } + + DBG_INIT (); + +#if USE == LINUX_INTERFACE + if (first_time) + { + first_time = 0; + + /* Try to determine a reliable value for sanei_scsi_max_request_size: + + With newer versions of the SG driver, check the available buffer + size by opening all SG device files belonging to a scanner, + issue the ioctl calls for setting and reading the reserved + buffer size, and take the smallest value. + + For older version of the SG driver, which don't support variable + buffer size, try to read /proc/sys/kernel/sg-big-biff ; if + this fails (SG driver too old, or loaded as a module), use + SG_BIG_BUFF + */ + + sanei_scsi_max_request_size = SCSIBUFFERSIZE; + cc = getenv ("SANE_SG_BUFFERSIZE"); + if (cc) + { + i = strtol (cc, &cc1, 10); + if (cc != cc1 && i >= 32768) + sanei_scsi_max_request_size = i; + } + sanei_scsi_find_devices (0, 0, "Scanner", -1, -1, -1, -1, + get_max_buffer_size); + sanei_scsi_find_devices (0, 0, "Processor", -1, -1, -1, -1, + get_max_buffer_size); + DBG (4, "sanei_scsi_open: sanei_scsi_max_request_size=%d bytes\n", + sanei_scsi_max_request_size); + } +#endif + +#if USE == OS2_INTERFACE + if (sscanf (dev, "b%ut%ul%u", &bus, &target, &lun) != 3) + { + DBG (1, "sanei_scsi_open: device name %s is not valid\n", dev); + return SANE_STATUS_INVAL; + } + if (!open_aspi ()) + { + /* Open driver if necessary. */ + close_aspi (); + return SANE_STATUS_INVAL; + } + + /* Find fake fd. */ + for (fd = 0; fd < num_alloced; ++fd) + if (!fd_info[fd].in_use) + break; + fake_fd = 1; +#elif USE == DECUNIX_INTERFACE + { + UAGT_CAM_SCAN cam_scan; + + if (sscanf (dev, "b%dt%dl%d", &bus, &target, &lun) != 3) + { + DBG (1, "sanei_scsi_open: device name `%s´ is not valid: %s\n", + dev, strerror (errno)); + return SANE_STATUS_INVAL; + } + + if (cam_fd < 0) + { + cam_fd = open ("/dev/cam", O_RDWR); + if (cam_fd < 0) + { + DBG (1, "sanei_scsi_open: open(/dev/cam) failed: %s\n", + strerror (errno)); + return SANE_STATUS_INVAL; + } + } + cam_scan.ucs_bus = bus; + cam_scan.ucs_target = target; + cam_scan.ucs_lun = lun; + if (ioctl (cam_fd, UAGT_CAM_SINGLE_SCAN, &cam_scan) < 0) + { + DBG (1, "sanei_scsi_open: ioctl(UAGT_CAM_SINGLE_SCAN) failed: %s\n", + strerror (errno)); + return SANE_STATUS_INVAL; + } + + for (fd = 0; fd < num_alloced; ++fd) + if (!fd_info[fd].in_use) + break; + fake_fd = 1; + } +#elif USE == DOMAINOS_INTERFACE + { + static int index; + static status_$t status; + static unsigned long length_mapped; + + DBG (1, "sanei_scsi_open: (dev='%s', int * fdp=%p, " + "SANEI_SCSI_Sense_Handler handler=%p)\n", dev, fdp, handler); + + /* See if the server process has started yet */ + if (!ServerInitialized) + { + static char *CommonAreaPath; + + /* Initialize the server */ + DBG (2, "Initializing Domain Server\n"); + + /* Map the area */ + CommonAreaPath = tmpnam (NULL); + DBG (2, "Domain Server Common area name is '%s'\n", CommonAreaPath); + com = ms_$crmapl (CommonAreaPath, strlen (CommonAreaPath), 0, + sizeof (struct DomainServerCommon), ms_$cowriters, + &status); + DomainErrorCheck (status, "Can't open common area"); + DBG (2, "Domain Server common area mapped\n"); + + /* Initialize the eventcounts */ + ec2_$init (&com->CommandAvailable); + ec2_$init (&com->CommandAccepted); + ec2_$init (&com->ResultReady); + ec2_$init (&com->ResultAccepted); + DBG (2, "Domain Server EC's initialized\n"); + /* Initialize the mutex locks */ + mutex_$init (&com->CommandLock); + mutex_$init (&com->ResultLock); + DBG (2, "Domain Server MutexLock's initialized\n"); + + /* Initialize pointers to ECs */ + CommandAcceptedPtr[0] = &com->CommandAccepted; + ResultReadyPtr[0] = &com->ResultReady; + time_$get_ec (time_$clockh_key, &CommandAcceptedPtr[1], &status); + DomainErrorCheck (status, "Can't get time EC"); + ResultReadyPtr[1] = CommandAcceptedPtr[1]; + + /* Read the ResultReady EC value, to avoid race with the server */ + ResultTriggerValue[0] = ec2_$read (com->ResultReady) + 1; + + /* Now invoke the server */ + ServerPID = fork (); + if (!ServerPID) + { + /* I am the child, call the initialization routine */ + sanei_DomainOS_init (CommonAreaPath); + /* We get here when the server is done, so we just exit. */ + exit (EXIT_SUCCESS); + } + + /* The communication area is open, wait for the initial response */ + ResultTriggerValue[1] = (ec2_$read (*ResultReadyPtr[1]) + + DomainECWaitConstant); + index = + ec2_$wait_svc (ResultReadyPtr, ResultTriggerValue, 2, &status); + DomainErrorCheck (status, "Error waiting on initial open EC"); + if (index != 1) + { + DBG (0, "Domain SANE Server never responded on startup\n"); + /* Send a quit signal to the server */ + kill (ServerPID, SIGQUIT); + return SANE_STATUS_INVAL; + } + /* Register a function to kill the server when we are done */ + assert (!atexit (KillDomainServer)); + ServerInitialized = 1; + } + + /* Find fake fd. */ + for (fd = 0; fd < num_alloced; ++fd) + if (!fd_info[fd].in_use) + break; + fake_fd = 1; + + /* Send the command open to the server */ + if (!mutex_$lock (&com->CommandLock, Wait16S)) + { + DBG (0, "Could not obtain mutex lock for Open\n"); + return SANE_STATUS_INVAL; + } + com->opcode = Open; + strcpy (com->open_path, dev); + CommandTriggerValue[0] = ec2_$read (com->CommandAccepted) + 1; + ec2_$advance (&com->CommandAvailable, &status); + DomainErrorCheck (status, "Can't advance CommandAvailable EC"); + CommandTriggerValue[1] = (ec2_$read (*CommandAcceptedPtr[1]) + + DomainECWaitConstant); + index = ec2_$wait_svc (CommandAcceptedPtr, CommandTriggerValue, 2, + &status); + DomainErrorCheck (status, "Error waiting on Open command acceptance EC"); + if (index != 1) + { + DBG (0, "Domain SANE Server never accepted Open Command\n"); + return SANE_STATUS_INVAL; + } + + /* Read the result */ + status = com->CommandStatus; + DomainErrorCheck (status, "Opening device in server"); + + /* Now map the data area, and make it temporary */ + DBG (2, "Mapping server's data block, name is '%s'\n", com->open_path); + pdata = ms_$mapl (com->open_path, strlen (com->open_path), 0, + DomainMaxDataSize + DomainSenseSize, ms_$cowriters, + ms_$wr, true, &length_mapped, &status); + DomainErrorCheck (status, "Mapping Server Data block"); + assert (length_mapped >= DomainMaxDataSize + DomainSenseSize); + ms_$mk_temporary (pdata, &status); + DomainErrorCheck (status, "Can't make data block temporary"); + + /* Release the lock */ + mutex_$unlock (&com->CommandLock); + + if (status.all != status_$ok) + { + /* we have a failure, return an error code, and generate debug + output */ + DBG (1, "sanei_scsi_open: acquire failed, Domain/OS status is %08x\n", + status.all); + error_$print (status); + return SANE_STATUS_INVAL; + } + else + { + /* device acquired, what else to do? */ + fd = com->fd; + } + } +#elif USE == FREEBSD_CAM_INTERFACE + if (1) + { /* 'if(1) {' makes my emacs c-mode indent better than + just '{' unfortunately, this only works if all of + the '{' are that way. */ + + struct cam_device *curdev; + + fake_fd = 1; + fd = -1; + + if ((curdev = cam_open_pass (dev, O_RDWR, NULL)) != NULL) + { + for (fd = 0; fd < CAM_MAXDEVS && cam_devices[fd] != NULL; fd++) + ; + + if (fd == CAM_MAXDEVS) + { + DBG (1, "sanei_scsi_open: too many CAM devices (%d)\n", fd); + cam_close_device (curdev); + return SANE_STATUS_INVAL; + } + cam_devices[fd] = curdev; + } + else + { + DBG (1, "sanei_scsi_open: can't open device `%s´: %s\n", dev, + strerror (errno)); + return SANE_STATUS_INVAL; + } + } +#elif USE == SCO_UW71_INTERFACE + { + pt_scsi_address_t dev_addr; + pt_handle_t pt_handle; + int bus, cnt, id, lun; + + if (4 != + sscanf (dev, "/dev/passthru0:%d,%d,%d,%d", &bus, &cnt, &id, &lun)) + { + DBG (1, "sanei_scsi_open: device name `%s´ is not valid: %s\n", + dev, strerror (errno)); + return SANE_STATUS_INVAL; + } + dev_addr.psa_bus = bus; + dev_addr.psa_controller = cnt; + dev_addr.psa_target = id; + dev_addr.psa_lun = lun; + + if (0 != pt_open (PASSTHRU_SCSI_ADDRESS, &dev_addr, PT_EXCLUSIVE, + &pt_handle)) + { + DBG (1, "sanei_scsi_open: pt_open failed: %s!\n", strerror (errno)); + return SANE_STATUS_INVAL; + } + else + fd = (int) pt_handle; + } +#elif USE == MACOSX_INTERFACE + { +# if defined (HAVE_IOKIT_SCSI_SCSICOMMANDOPERATIONCODES_H) || \ + defined (HAVE_IOKIT_SCSI_COMMANDS_SCSICOMMANDOPERATIONCODES_H) + len = strlen (dev); + if (len > 2 && len % 2 == 0 && dev [0] == '<' && dev [len - 1] == '>') + { + len = (len - 2) / 2; + guid = (UInt8 *) malloc (len); + for (i = 0; i < len; i++) + { + if (sscanf (&dev [2 * i + 1], "%02x", &d) != 1) + break; + guid [i] = d; + } + if (i == len) + pdata = (void *) CFDataCreate (kCFAllocatorDefault, guid, len); + free (guid); + } +# endif +# ifdef HAVE_IOKIT_CDB_IOSCSILIB_H + if ((pdata == NULL) && + (sscanf (dev, "u%ut%ul%u", &bus, &target, &lun) != 3)) +# else + if (pdata == NULL) +# endif + { + DBG (1, "sanei_scsi_open: device name %s is not valid\n", dev); + return SANE_STATUS_INVAL; + } + + /* Find fake fd. */ + for (fd = 0; fd < num_alloced; ++fd) + if (!fd_info[fd].in_use) + break; + fake_fd = 1; + } +#elif USE == WIN32_INTERFACE + { + char scsi_hca_name[20]; + u_int hca = 0; + + if (sscanf (dev, "h%ub%ut%ul%u", &hca, &bus, &target, &lun) != 4) + { + DBG (1, "sanei_scsi_open: device name %s is not valid\n", dev); + return SANE_STATUS_INVAL; + } + + snprintf(scsi_hca_name, 19, "\\\\.\\Scsi%d:", hca); + scsi_hca_name[19] = 0; + + fd = CreateFile(scsi_hca_name, GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, OPEN_EXISTING, + FILE_FLAG_RANDOM_ACCESS, NULL ); + + if (fd == INVALID_HANDLE_VALUE) fd = -1; + } +#else +#if defined(SGIOCSTL) || (USE == SOLARIS_INTERFACE) + { + size_t len; + + /* OpenStep and the Solaris SCG driver are a bit broken in that + the device name refers to a scsi _bus_, not an individual scsi + device. Hence, SANE has to fudge with the device name so we + know which target to connect to. For this purpose, we use the + last character in the device name as the target index. 'a' is + target 0, 'b', target 1, and so on... */ + + len = strlen (dev); + if (len <= 1) + { + DBG (1, "sanei_scsi_open: devicename `%s' too short\n", dev); + return SANE_STATUS_INVAL; + } + + real_dev = strdup (dev); + real_dev[len - 1] = '\0'; + + target = dev[len - 1] - 'a'; + if (target > 7) + { + DBG (1, "sanei_scsi_open: `%c' is not a valid target id\n", + dev[len - 1]); + return SANE_STATUS_INVAL; + } + dev = real_dev; + } +#endif /* defined(SGIOCSTL) || (USE == SOLARIS_INTERFACE) */ + + fd = -1; +#ifdef HAVE_RESMGR + fd = rsm_open_device(dev, O_RDWR | O_EXCL | O_NONBLOCK); +#endif + + if (fd == -1) + fd = open (dev, O_RDWR | O_EXCL +#if USE == LINUX_INTERFACE + | O_NONBLOCK +#endif + ); + if (fd < 0) + { + SANE_Status status = SANE_STATUS_INVAL; + + if (errno == EACCES) + status = SANE_STATUS_ACCESS_DENIED; + else if (errno == EBUSY) + status = SANE_STATUS_DEVICE_BUSY; + + DBG (1, "sanei_scsi_open: open of `%s' failed: %s\n", + dev, strerror (errno)); + return status; + } + + if (real_dev) + free (real_dev); + +#ifdef SG_SET_TIMEOUT + /* Set large timeout since some scanners are slow but do not + disconnect... ;-( */ + { + int timeout; + timeout = sane_scsicmd_timeout * GNU_HZ; + ioctl (fd, SG_SET_TIMEOUT, &timeout); + } +#endif + +#ifdef SGIOCSTL + { + struct scsi_adr sa; + + sa.sa_target = target; + sa.sa_lun = 0; + if (ioctl (fd, SGIOCSTL, &sa) == -1) + { + DBG (1, "sanei_scsi_open: failed to attach to target: %u (%s)\n", + sa.sa_target, strerror (errno)); + return SANE_STATUS_INVAL; + } + } +#endif /* SGIOCSTL */ +#if USE == LINUX_INTERFACE + { + SG_scsi_id sid; + int ioctl_val; + int real_buffersize; + fdparms *fdpa = 0; + SG_scsi_id devinfo; + + pdata = fdpa = malloc (sizeof (fdparms)); + if (!pdata) + { + close (fd); + return SANE_STATUS_NO_MEM; + } + memset (fdpa, 0, sizeof (fdparms)); + /* default: allow only one command to be sent to the SG driver + */ + fdpa->sg_queue_max = 1; + + /* Try to read the SG version. If the ioctl call is successful, + we have the new SG driver, and we can increase the buffer size + using another ioctl call. + If we have SG version 2.1.35 or above, we can additionally enable + command queueing. + */ + if (0 == ioctl (fd, SG_GET_VERSION_NUM, &sg_version)) + { + DBG (1, "sanei_scsi_open: SG driver version: %i\n", sg_version); + + ioctl_val = ioctl (fd, SG_GET_SCSI_ID, &devinfo); + if (ioctl_val == EINVAL || ioctl_val == ENOTTY) + { + DBG (1, "sanei_scsi_open: The file %s is not an SG device file\n", + dev); + close (fd); + return SANE_STATUS_INVAL; + } + + if (devinfo.scsi_type != 6 && devinfo.scsi_type != 3) + { + DBG (1, + "sanei_scsi_open: The device found for %s does not look like a scanner\n", + dev); + close (fd); + return SANE_STATUS_INVAL; + } + + /* try to reserve a SG buffer of the size specified by *buffersize + */ + ioctl (fd, SG_SET_RESERVED_SIZE, buffersize); + + /* the set call may not be able to allocate as much memory + as requested, thus we read the actual buffer size. + */ + if (0 == ioctl (fd, SG_GET_RESERVED_SIZE, &real_buffersize)) + { + /* if we got more memory than requested, we stick with + with the requested value, in order to allow + sanei_scsi_open to check the buffer size exactly. + */ + if (real_buffersize < *buffersize) + *buffersize = real_buffersize; + fdpa->buffersize = *buffersize; + } + else + { + DBG (1, "sanei_scsi_open: cannot read SG buffer size - %s\n", + strerror (errno)); + close (fd); + return SANE_STATUS_NO_MEM; + } + DBG (1, "sanei_scsi_open_extended: using %i bytes as SCSI buffer\n", + *buffersize); + + if (sg_version >= 20135) + { + DBG (1, "trying to enable low level command queueing\n"); + + if (0 == ioctl (fd, SG_GET_SCSI_ID, &sid)) + { + DBG (1, "sanei_scsi_open: Host adapter queue depth: %i\n", + sid.d_queue_depth); + + ioctl_val = 1; + if (0 == ioctl (fd, SG_SET_COMMAND_Q, &ioctl_val)) + { + fdpa->sg_queue_max = sid.d_queue_depth; + if (fdpa->sg_queue_max <= 0) + fdpa->sg_queue_max = 1; + } + } + } + } + else + { + /* we have a really old SG driver version, or we're not opening + an SG device file + */ + if (ioctl (fd, SG_GET_TIMEOUT, &ioctl_val) < 0) + { + DBG (1, "sanei_scsi_open: The file %s is not an SG device file\n", + dev); + close (fd); + return SANE_STATUS_INVAL; + } + if (sanei_scsi_max_request_size < *buffersize) + *buffersize = sanei_scsi_max_request_size; + fdpa->buffersize = *buffersize; + } + if (sg_version == 0) + { + DBG (1, "sanei_scsi_open: using old SG driver logic\n"); + } + else + { + DBG (1, + "sanei_scsi_open: SG driver can change buffer size at run time\n"); + if (fdpa->sg_queue_max > 1) + DBG (1, "sanei_scsi_open: low level command queueing enabled\n"); +#ifdef SG_IO + if (sg_version >= 30000) + { + DBG (1, "sanei_scsi_open: using new SG header structure\n"); + } +#endif + } + } +#endif /* LINUX_INTERFACE */ +#endif /* !DECUNIX_INTERFACE */ + +/* Note: this really relies on fd to start small. Windows starts a little higher than 3. */ + + if (fd >= num_alloced) + { + size_t new_size, old_size; + + old_size = num_alloced * sizeof (fd_info[0]); + num_alloced = fd + 8; + new_size = num_alloced * sizeof (fd_info[0]); + if (fd_info) + fd_info = realloc (fd_info, new_size); + else + fd_info = malloc (new_size); + memset ((char *) fd_info + old_size, 0, new_size - old_size); + if (!fd_info) + { + if (!fake_fd) + close (fd); + return SANE_STATUS_NO_MEM; + } + } + fd_info[fd].in_use = 1; + fd_info[fd].sense_handler = handler; + fd_info[fd].sense_handler_arg = handler_arg; + fd_info[fd].fake_fd = fake_fd; + fd_info[fd].bus = bus; + fd_info[fd].target = target; + fd_info[fd].lun = lun; + fd_info[fd].pdata = pdata; + +#if USE == SOLARIS_INTERFACE || USE == SOLARIS_USCSI_INTERFACE + /* verify that the device really exists: */ + if (!unit_ready (fd)) + { + sanei_scsi_close (fd); + return SANE_STATUS_INVAL; + } +#endif +#if USE == SYSVR4_INTERFACE + memset (lastrcmd, 0, 16); /* reinitialize last read command block */ +#endif + + if (fdp) + *fdp = fd; + + return SANE_STATUS_GOOD; +} + +#if USE == LINUX_INTERFACE +/* The "wrapper" for the old open call */ +SANE_Status +sanei_scsi_open (const char *dev, int *fdp, + SANEI_SCSI_Sense_Handler handler, void *handler_arg) +{ + int i = 0; + int wanted_buffersize = SCSIBUFFERSIZE, real_buffersize; + SANE_Status res; + char *cc, *cc1; + static int first_time = 1; + + if (first_time) + { + cc = getenv ("SANE_SG_BUFFERSIZE"); + if (cc) + { + i = strtol (cc, &cc1, 10); + if (cc != cc1 && i >= 32768) + wanted_buffersize = i; + } + } + else + wanted_buffersize = sanei_scsi_max_request_size; + + real_buffersize = wanted_buffersize; + res = sanei_scsi_open_extended (dev, fdp, handler, handler_arg, + &real_buffersize); + + /* make sure that we got as much memory as we wanted, otherwise + the backend might be confused + */ + if (!first_time && real_buffersize != wanted_buffersize) + { + DBG (1, "sanei_scsi_open: could not allocate SG buffer memory " + "wanted: %i got: %i\n", wanted_buffersize, real_buffersize); + sanei_scsi_close (*fdp); + return SANE_STATUS_NO_MEM; + } + + first_time = 0; + return res; +} +#else +/* dummy for the proposed new open call */ +SANE_Status +sanei_scsi_open_extended (const char *dev, int *fdp, + SANEI_SCSI_Sense_Handler handler, + void *handler_arg, int *buffersize) +{ + SANE_Status res; + res = sanei_scsi_open (dev, fdp, handler, handler_arg); + if (sanei_scsi_max_request_size < *buffersize) + *buffersize = sanei_scsi_max_request_size; + return res; +} +#endif + +void +sanei_scsi_close (int fd) +{ +#if USE == LINUX_INTERFACE + if (fd_info[fd].pdata) + { + req *req, *next_req; + + /* make sure that there are no pending SCSI calls */ + sanei_scsi_req_flush_all_extended (fd); + + req = ((fdparms *) fd_info[fd].pdata)->sane_free_list; + while (req) + { + next_req = req->next; + free (req); + req = next_req; + } + free (fd_info[fd].pdata); + } +#endif + + fd_info[fd].in_use = 0; + fd_info[fd].sense_handler = 0; + fd_info[fd].sense_handler_arg = 0; + +#ifdef WIN32 + CloseHandle(fd); +#else + if (!fd_info[fd].fake_fd) + close (fd); +#endif + +#if USE == FREEBSD_CAM_INTERFACE + cam_close_device (cam_devices[fd]); + cam_devices[fd] = NULL; +#elif USE == DOMAINOS_INTERFACE + { + static int index; + static status_$t status; + + DBG (1, "sanei_scsi_close: fd=%d\n", fd); + + /* Send the command to the server */ + if (!mutex_$lock (&com->CommandLock, Wait16S)) + { + DBG (0, "Could not obtain mutex lock for Close command\n"); + } + else + { + com->opcode = Close; + com->fd = fd; + CommandTriggerValue[0] = ec2_$read (com->CommandAccepted) + 1; + ec2_$advance (&com->CommandAvailable, &status); + DomainErrorCheck (status, "Can't advance CommandAvailable EC"); + CommandTriggerValue[1] = (ec2_$read (*CommandAcceptedPtr[1]) + + DomainECWaitConstant); + index = ec2_$wait_svc (CommandAcceptedPtr, CommandTriggerValue, 2, + &status); + DomainErrorCheck (status, + "Error waiting on Close command acceptance EC"); + if (index != 1) + { + DBG (0, "Domain SANE Server never accepted Close Command\n"); + } + + /* Read the result */ + status = com->CommandStatus; + /* Release the lock */ + mutex_$unlock (&com->CommandLock); + } + + /* Unmap the data area */ + ms_$unmap (fd_info[com->fd].pdata, DomainMaxDataSize + DomainSenseSize, + &status); + DomainErrorCheck (status, "Error unmapping device data area"); + } +#endif /* USE == DOMAINOS_INTERFACE */ + +#if USE == OS2_INTERFACE + close_aspi (); +#endif /* USE == OS2_INTERFACE */ + +#if USE == MACOSX_INTERFACE + if (fd_info[fd].pdata) + CFRelease (fd_info[fd].pdata); +#endif /* USE == MACOSX_INTERFACE */ +} + + +#if USE == DOMAINOS_INTERFACE +# define WE_HAVE_ASYNC_SCSI + +void +sanei_scsi_req_flush_all (void) +{ + status_$t status; + + DBG (1, "sanei_scsi_req_flush_all: ()\n"); + /* I have never seen this called, and I'm not sure what to do with it, + so I guarantee that it will generate a fault, and I can add support + for it. */ + assert (1 == 0); +} + + +SANE_Status +sanei_scsi_req_enter2 (int fd, + const void *cmd, size_t cmd_size, + const void *src, size_t src_size, + void *dst, size_t * dst_size, void **idp) +{ + SANEI_SCSI_Sense_Handler handler; + static int index; + static SANE_Status sane_status; + static status_$t status; + static scsi_$status_t SCSIStatus; + static void *buf_ptr; + + if (dst_size) + DBG (1, "sanei_scsi_req_enter2: (fd=%x, cmd=%p, cmd_size=%x, " + "src=%p, src_size=%x, dst=%p, dst_size=%x, *idp=%p)\n", + fd, cmd, cmd_size, src, src_size, dst, *dst_size, idp); + else + DBG (1, "sanei_scsi_req_enter2: (fd=%x, cmd=%p, cmd_size=%x, " + "src=%p, src_size=%x, dst=%p, dst_size=NULL, *idp=%p)\n", + fd, src, src_size, dst, idp); + + /* Lock the command structure */ + if (!mutex_$lock (&com->CommandLock, mutex_$wait_forever)) + { + DBG (0, "Could not obtain mutex lock for Enter Command\n"); + return SANE_STATUS_INVAL; + } + + /* Fill in the command structure */ + com->opcode = Enter; + com->fd = fd; + com->cdb_size = cmd_size; + if (dst_size) + com->dst_size = *dst_size; + memcpy (&com->cdb, cmd, com->cdb_size); + + /* figure out if this is a read or a write */ + if (dst_size && *dst_size) + { + /* dest buffer specified, must be a read */ + /* assert (com->cdb_size == src_size); */ + com->direction = scsi_read; + buf_ptr = dst; + com->buf_size = *dst_size; + } + else + { + /* no dest buffer, must be a write */ + /* assert (com->cdb_size <= src_size); */ + com->direction = scsi_write; + buf_ptr = (char *) src; + com->buf_size = src_size; + if (com->buf_size) + memcpy (fd_info[fd].pdata, buf_ptr, com->buf_size); + } + + CommandTriggerValue[0] = ec2_$read (com->CommandAccepted) + 1; + ec2_$advance (&com->CommandAvailable, &status); + DomainErrorCheck (status, "Can't advance CommandAvailable EC"); + CommandTriggerValue[1] = (ec2_$read (*CommandAcceptedPtr[1]) + + DomainECWaitConstant); + index = ec2_$wait_svc (CommandAcceptedPtr, CommandTriggerValue, 2, &status); + DomainErrorCheck (status, "Error waiting on Enter command acceptance EC"); + if (index != 1) + { + DBG (0, "Domain SANE Server never accepted Enter Command\n"); + return SANE_STATUS_INVAL; + } + + /* Read the result */ + status = com->CommandStatus; + SCSIStatus = com->SCSIStatus; + + /* Release the lock */ + mutex_$unlock (&com->CommandLock); + + /* Now decode the return status */ + if (status.all) + DBG (1, "Server returned status %08x from Enter command\n", status.all); + switch (status.all) + { + case status_$ok: + sane_status = SANE_STATUS_GOOD; + break; + case scsi_$dma_underrun: + sane_status = SANE_STATUS_IO_ERROR; + /* This error is generated by the HP and UMAX backends. They + ask for too much data. For now, the error is ignored :-( */ + sane_status = SANE_STATUS_GOOD; + break; + case scsi_$operation_timeout: + sane_status = SANE_STATUS_DEVICE_BUSY; + break; + case scsi_$hdwr_failure: /* received when both scanners were active */ + sane_status = SANE_STATUS_IO_ERROR; + break; + case (status_$ok | 0x80000000): + /* Special - no Domain/OS error, but fail bit set means to check + SCSI operation status. */ + DBG (1, "Server returned SCSI status of %08x\n", SCSIStatus); + switch (SCSIStatus) + { + case scsi_check_condition: + /* Call the sense handler, if defined */ + handler = fd_info[com->fd].sense_handler; + if (handler) + (*handler) (fd, ((u_char *) fd_info[fd].pdata + + DomainMaxDataSize), + fd_info[com->fd].sense_handler_arg); + sane_status = SANE_STATUS_IO_ERROR; + break; + case scsi_busy: + sane_status = SANE_STATUS_DEVICE_BUSY; + break; + default: + DBG (0, "Error - Unrecognized SCSI status %08x returned from " + "Enter command\n", SCSIStatus); + sane_status = SANE_STATUS_IO_ERROR; + exit (EXIT_FAILURE); + } + break; + default: + DBG (0, "Unmapped status (%08x) returned from Domain SANE Server\n", + status.all); + sane_status = SANE_STATUS_IO_ERROR; + } + + /* If a read, copy the data into the destination buffer */ + if ((com->direction == scsi_read) && com->dst_size) + memcpy (buf_ptr, fd_info[fd].pdata, com->dst_size); + + return sane_status; +} + + +SANE_Status +sanei_scsi_req_wait (void *id) +{ + SANE_Status status; + DBG (1, "sanei_scsi_req_wait: (id=%p)\n", id); + status = SANE_STATUS_GOOD; + return status; +} + + +SANE_Status +sanei_scsi_cmd2 (int fd, + const void *cmd, size_t cmd_size, + const void *src, size_t src_size, + void *dst, size_t * dst_size) +{ + SANE_Status status; + void *id; + + DBG (1, "sanei_scsi_cmd2: (fd=%d)\n", fd); + status = + sanei_scsi_req_enter2 (fd, cmd, cmd_size, src, src_size, dst, dst_size, + &id); + if (status != SANE_STATUS_GOOD) + return status; + return sanei_scsi_req_wait (id); +} + +#endif /* USE == DOMAINOS_INTERFACE */ + + +#if USE == LINUX_INTERFACE + +#include <ctype.h> +#include <signal.h> + +#include <sys/time.h> + +#define WE_HAVE_ASYNC_SCSI +#define WE_HAVE_FIND_DEVICES + +static int pack_id = 0; +static int need_init = 1; +static sigset_t all_signals; + +#define ATOMIC(s) \ +do \ + { \ + sigset_t old_mask; \ + \ + if (need_init) \ + { \ + need_init = 0; \ + sigfillset (&all_signals); \ + } \ + sigprocmask (SIG_BLOCK, &all_signals, &old_mask); \ + {s;} \ + sigprocmask (SIG_SETMASK, &old_mask, 0); \ + } \ +while (0) + +static void +issue (struct req *req) +{ + ssize_t nwritten; + fdparms *fdp; + struct req *rp; + int retries; + int ret; + + if (!req) + return; + + fdp = (fdparms *) fd_info[req->fd].pdata; + DBG (4, "sanei_scsi.issue: %p\n", (void *) req); + + rp = fdp->sane_qhead; + while (rp && rp->running) + rp = rp->next; + + while (rp && fdp->sg_queue_used < fdp->sg_queue_max) + { + retries = 20; + while (retries) + { + errno = 0; +#ifdef SG_IO + if (sg_version < 30000) + { +#endif + ATOMIC (rp->running = 1; + nwritten = write (rp->fd, &rp->sgdata.cdb, + rp->sgdata.cdb.hdr.pack_len); + ret = 0; + if (nwritten != rp->sgdata.cdb.hdr.pack_len) + { + /* ENOMEM can easily happen, if both command queueing + inside the SG driver and large buffers are used. + Therefore, if ENOMEM does not occur for the first + command in the queue, we simply try to issue + it later again. + */ + if (errno == EAGAIN + || (errno == ENOMEM && rp != fdp->sane_qhead)) + { + /* don't try to send the data again, but + wait for the next call to issue() + */ + rp->running = 0;} + } + ); +#ifdef SG_IO + } + else + { + ATOMIC (rp->running = 1; + ret = ioctl(rp->fd, SG_IO, &rp->sgdata.sg3.hdr); + nwritten = 0; + if (ret < 0) + { + /* ENOMEM can easily happen, if both command queuein + inside the SG driver and large buffers are used. + Therefore, if ENOMEM does not occur for the first + command in the queue, we simply try to issue + it later again. + */ + if (errno == EAGAIN + || (errno == ENOMEM && rp != fdp->sane_qhead)) + { + /* don't try to send the data again, but + wait for the next call to issue() + */ + rp->running = 0; + } + else /* game over */ + { + rp->running = 0; + rp->done = 1; + rp->status = SANE_STATUS_IO_ERROR; + } + } + ); + IF_DBG (if (DBG_LEVEL >= 255) + system ("cat /proc/scsi/sg/debug 1>&2");) + } +#endif + if (rp == fdp->sane_qhead && errno == EAGAIN) + { + retries--; + usleep (10000); + } + else + retries = 0; + } + +#ifndef SG_IO + if (nwritten != rp->sgdata.cdb.hdr.pack_len) +#else + if ((sg_version < 30000 && nwritten != rp->sgdata.cdb.hdr.pack_len) + || (sg_version >= 30000 && ret < 0)) +#endif + { + if (rp->running) + { +#ifdef SG_IO + if (sg_version < 30000) +#endif + DBG (1, "sanei_scsi.issue: bad write (errno=%i) %s %li\n", + errno, strerror (errno), (long)nwritten); +#ifdef SG_IO + else if (sg_version > 30000) + DBG (1, "sanei_scsi.issue: SG_IO ioctl error (errno=%i, ret=%d) %s\n", + errno, ret, strerror (errno)); +#endif + rp->done = 1; + if (errno == ENOMEM) + { + DBG (1, "sanei_scsi.issue: SG_BIG_BUF inconsistency? " + "Check file PROBLEMS.\n"); + rp->status = SANE_STATUS_NO_MEM; + } + else + rp->status = SANE_STATUS_IO_ERROR; + } + else + { + if (errno == ENOMEM) + DBG (1, "issue: ENOMEM - cannot queue SCSI command. " + "Trying again later.\n"); + else + DBG (1, "issue: EAGAIN - cannot queue SCSI command. " + "Trying again later.\n"); + } + break; /* in case of an error don't try to queue more commands */ + } + else + { +#ifdef SG_IO + if (sg_version < 30000) +#endif + req->status = SANE_STATUS_IO_ERROR; +#ifdef SG_IO + else if (sg_version > 30000) /* SG_IO is synchronous, we're all set */ + req->status = SANE_STATUS_GOOD; +#endif + } + fdp->sg_queue_used++; + rp = rp->next; + } +} + + void sanei_scsi_req_flush_all_extended (int fd) + { + fdparms *fdp; + struct req *req, *next_req; + int len, count; + + fdp = (fdparms *) fd_info[fd].pdata; + for (req = fdp->sane_qhead; req; req = next_req) + { + if (req->running && !req->done) + { + count = sane_scsicmd_timeout * 10; + while (count) + { + errno = 0; +#ifdef SG_IO + if (sg_version < 30000) +#endif + len = + read (fd, &req->sgdata.cdb, + req->sgdata.cdb.hdr.reply_len); +#ifdef SG_IO + else + len = read (fd, &req->sgdata.sg3.hdr, sizeof (Sg_io_hdr)); +#endif + if (len >= 0 || (len < 0 && errno != EAGAIN)) + break; + usleep (100000); + count--; + } + ((fdparms *) fd_info[req->fd].pdata)->sg_queue_used--; + } + next_req = req->next; + + req->next = fdp->sane_free_list; + fdp->sane_free_list = req; + } + + fdp->sane_qhead = fdp->sane_qtail = 0; + } + + void sanei_scsi_req_flush_all () + { + int fd, i, j = 0; + + /* sanei_scsi_open allows only one open file handle, so we + can simply look for the first entry where in_use is set + */ + + fd = num_alloced; + for (i = 0; i < num_alloced; i++) + if (fd_info[i].in_use) + { + j++; + fd = i; + } + + assert (j < 2); + + if (fd < num_alloced) + sanei_scsi_req_flush_all_extended (fd); + } + + SANE_Status + sanei_scsi_req_enter2 (int fd, + const void *cmd, size_t cmd_size, + const void *src, size_t src_size, + void *dst, size_t * dst_size, void **idp) + { + struct req *req; + size_t size; + fdparms *fdp; + + fdp = (fdparms *) fd_info[fd].pdata; + + if (fdp->sane_free_list) + { + req = fdp->sane_free_list; + fdp->sane_free_list = req->next; + req->next = 0; + } + else + { +#ifdef SG_IO + if (sg_version < 30000) +#endif + size = (sizeof (*req) - sizeof (req->sgdata.cdb.data) + + fdp->buffersize); +#ifdef SG_IO + else + size = sizeof (*req) + MAX_CDB + fdp->buffersize + - sizeof (req->sgdata.sg3.data); +#endif + req = malloc (size); + if (!req) + { + DBG (1, "sanei_scsi_req_enter: failed to malloc %lu bytes\n", + (u_long) size); + return SANE_STATUS_NO_MEM; + } + } + req->fd = fd; + req->running = 0; + req->done = 0; + req->status = SANE_STATUS_GOOD; + req->dst = dst; + req->dst_len = dst_size; +#ifdef SG_IO + if (sg_version < 30000) + { +#endif + memset (&req->sgdata.cdb.hdr, 0, sizeof (req->sgdata.cdb.hdr)); + req->sgdata.cdb.hdr.pack_id = pack_id++; + req->sgdata.cdb.hdr.pack_len = cmd_size + src_size + + sizeof (req->sgdata.cdb.hdr); + req->sgdata.cdb.hdr.reply_len = (dst_size ? *dst_size : 0) + + sizeof (req->sgdata.cdb.hdr); + memcpy (&req->sgdata.cdb.data, cmd, cmd_size); + memcpy (&req->sgdata.cdb.data[cmd_size], src, src_size); + if (CDB_SIZE (*(const u_char *) cmd) != cmd_size) + { + if (ioctl (fd, SG_NEXT_CMD_LEN, &cmd_size)) + { + DBG (1, + "sanei_scsi_req_enter2: ioctl to set command length failed\n"); + } + } +#ifdef SG_IO + } + else + { + memset (&req->sgdata.sg3.hdr, 0, sizeof (req->sgdata.sg3.hdr)); + req->sgdata.sg3.hdr.interface_id = 'S'; + req->sgdata.sg3.hdr.cmd_len = cmd_size; + req->sgdata.sg3.hdr.iovec_count = 0; + req->sgdata.sg3.hdr.mx_sb_len = SENSE_MAX; + /* read or write? */ + if (dst_size && *dst_size) + { + req->sgdata.sg3.hdr.dxfer_direction = SG_DXFER_FROM_DEV; + req->sgdata.sg3.hdr.dxfer_len = *dst_size; + req->sgdata.sg3.hdr.dxferp = dst; + } + else if (src_size) + { + req->sgdata.sg3.hdr.dxfer_direction = SG_DXFER_TO_DEV; + if (src_size > fdp->buffersize) + { + DBG (1, + "sanei_scsi_req_enter2 warning: truncating write data " + "from requested %li bytes to allowed %li bytes\n", + (long)src_size, (long)fdp->buffersize); + src_size = fdp->buffersize; + } + req->sgdata.sg3.hdr.dxfer_len = src_size; + memcpy (&req->sgdata.sg3.data[MAX_CDB], src, src_size); + req->sgdata.sg3.hdr.dxferp = &req->sgdata.sg3.data[MAX_CDB]; + } + else + { + req->sgdata.sg3.hdr.dxfer_direction = SG_DXFER_NONE; + } + if (cmd_size > MAX_CDB) + { + DBG (1, "sanei_scsi_req_enter2 warning: truncating write data " + "from requested %li bytes to allowed %i bytes\n", + (long)cmd_size, MAX_CDB); + cmd_size = MAX_CDB; + } + memcpy (req->sgdata.sg3.data, cmd, cmd_size); + req->sgdata.sg3.hdr.cmdp = req->sgdata.sg3.data; + req->sgdata.sg3.hdr.sbp = &(req->sgdata.sg3.sense_buffer[0]); + req->sgdata.sg3.hdr.timeout = 1000 * sane_scsicmd_timeout; +#ifdef ENABLE_SCSI_DIRECTIO + /* for the adventurous: If direct IO is used, + the kernel locks the buffer. This can lead to conflicts, + if a backend uses shared memory. + OTOH, direct IO may be faster, and it reduces memory usage + */ + req->sgdata.sg3.hdr.flags = SG_FLAG_DIRECT_IO; +#else + req->sgdata.sg3.hdr.flags = 0; +#endif + req->sgdata.sg3.hdr.pack_id = pack_id++; + req->sgdata.sg3.hdr.usr_ptr = 0; + } +#endif + + req->next = 0; + ATOMIC (if (fdp->sane_qtail) + { + fdp->sane_qtail->next = req; fdp->sane_qtail = req;} + else + fdp->sane_qhead = fdp->sane_qtail = req); + + DBG (4, "scsi_req_enter: entered %p\n", (void *) req); + + *idp = req; + issue (req); + + DBG (10, "scsi_req_enter: queue_used: %i, queue_max: %i\n", + ((fdparms *) fd_info[fd].pdata)->sg_queue_used, + ((fdparms *) fd_info[fd].pdata)->sg_queue_max); + + return SANE_STATUS_GOOD; + } + + SANE_Status sanei_scsi_req_wait (void *id) + { + SANE_Status status = SANE_STATUS_GOOD; + struct req *req = id; + ssize_t nread = 0; + + /* we don't support out-of-order completion */ + assert (req == ((fdparms *) fd_info[req->fd].pdata)->sane_qhead); + + DBG (4, "sanei_scsi_req_wait: waiting for %p\n", (void *) req); + + issue (req); /* ensure the command is running */ + if (req->done) + { + issue (req->next); /* issue next command, if any */ + status = req->status; + } + else + { +#ifdef SG_IO + if (sg_version < 30000) + { +#endif + fd_set readable; + + /* wait for command completion: */ + FD_ZERO (&readable); + FD_SET (req->fd, &readable); + select (req->fd + 1, &readable, 0, 0, 0); + + /* now atomically read result and set DONE: */ + ATOMIC (nread = read (req->fd, &req->sgdata.cdb, + req->sgdata.cdb.hdr.reply_len); + req->done = 1); +#ifdef SG_IO + } + else + { + IF_DBG (if (DBG_LEVEL >= 255) + system ("cat /proc/scsi/sg/debug 1>&2");) + + /* set DONE: */ + nread = 0; /* unused in this code path */ + req->done = 1; + } +#endif + + if (fd_info[req->fd].pdata) + ((fdparms *) fd_info[req->fd].pdata)->sg_queue_used--; + + /* Now issue next command asap, if any. We can't do this + earlier since the Linux kernel has space for just one big + buffer. */ + issue (req->next); + + DBG (4, "sanei_scsi_req_wait: read %ld bytes\n", (long) nread); + + if (nread < 0) + { + DBG (1, "sanei_scsi_req_wait: read returned %ld (errno=%d)\n", + (long) nread, errno); + status = SANE_STATUS_IO_ERROR; + } + else + { +#ifdef SG_IO + if (sg_version < 30000) + { +#endif + nread -= sizeof (req->sgdata.cdb.hdr); + + /* check for errors, but let the sense_handler decide.... */ + if ((req->sgdata.cdb.hdr.result != 0) || + (((req->sgdata.cdb.hdr.sense_buffer[0] & 0x7f) != 0) +#ifdef HAVE_SG_TARGET_STATUS + /* this is messy... Sometimes it happens that we have + a valid looking sense buffer, but the DRIVER_SENSE + bit is not set. Moreover, we can check this only for + not tooo old SG drivers + */ + && (req->sgdata.cdb.hdr.driver_status & DRIVER_SENSE) +#endif + )) + { + SANEI_SCSI_Sense_Handler handler + = fd_info[req->fd].sense_handler; + void *arg = fd_info[req->fd].sense_handler_arg; + + DBG (1, + "sanei_scsi_req_wait: SCSI command complained: %s\n", + strerror (req->sgdata.cdb.hdr.result)); + DBG (10, + "sense buffer: %02x %02x %02x %02x %02x %02x %02x %02x" + " %02x %02x %02x %02x %02x %02x %02x %02x\n", + req->sgdata.cdb.hdr.sense_buffer[0], + req->sgdata.cdb.hdr.sense_buffer[1], + req->sgdata.cdb.hdr.sense_buffer[2], + req->sgdata.cdb.hdr.sense_buffer[3], + req->sgdata.cdb.hdr.sense_buffer[4], + req->sgdata.cdb.hdr.sense_buffer[5], + req->sgdata.cdb.hdr.sense_buffer[6], + req->sgdata.cdb.hdr.sense_buffer[7], + req->sgdata.cdb.hdr.sense_buffer[8], + req->sgdata.cdb.hdr.sense_buffer[9], + req->sgdata.cdb.hdr.sense_buffer[10], + req->sgdata.cdb.hdr.sense_buffer[11], + req->sgdata.cdb.hdr.sense_buffer[12], + req->sgdata.cdb.hdr.sense_buffer[13], + req->sgdata.cdb.hdr.sense_buffer[14], + req->sgdata.cdb.hdr.sense_buffer[15]); +#ifdef HAVE_SG_TARGET_STATUS + /* really old SG header do not define target_status, + host_status and driver_status + */ + DBG (10, "target status: %02x host status: %02x" + " driver status: %02x\n", + req->sgdata.cdb.hdr.target_status, + req->sgdata.cdb.hdr.host_status, + req->sgdata.cdb.hdr.driver_status); + + if (req->sgdata.cdb.hdr.host_status == DID_NO_CONNECT || req->sgdata.cdb.hdr.host_status == DID_BUS_BUSY || req->sgdata.cdb.hdr.host_status == DID_TIME_OUT || req->sgdata.cdb.hdr.driver_status == DRIVER_BUSY || req->sgdata.cdb.hdr.target_status == 0x04) /* BUSY */ +#else + if (req->sgdata.cdb.hdr.result == EBUSY) +#endif + status = SANE_STATUS_DEVICE_BUSY; + else if (handler) + /* sense handler should return SANE_STATUS_GOOD if it + decided all was ok afterall */ + status = + (*handler) (req->fd, req->sgdata.cdb.hdr.sense_buffer, + arg); + else + status = SANE_STATUS_IO_ERROR; + } + + /* if we are ok so far, copy over the return data */ + if (status == SANE_STATUS_GOOD) + { + if (req->dst) + memcpy (req->dst, req->sgdata.cdb.data, nread); + + if (req->dst_len) + *req->dst_len = nread; + } +#ifdef SG_IO + } + else + { + /* check for errors, but let the sense_handler decide.... */ + if (((req->sgdata.sg3.hdr.info & SG_INFO_CHECK) != 0) + || ((req->sgdata.sg3.hdr.sb_len_wr > 0) + && ((req->sgdata.sg3.sense_buffer[0] & 0x7f) != 0) + && (req->sgdata.sg3.hdr. + driver_status & DRIVER_SENSE))) + { + SANEI_SCSI_Sense_Handler handler + = fd_info[req->fd].sense_handler; + void *arg = fd_info[req->fd].sense_handler_arg; + + DBG (1, + "sanei_scsi_req_wait: SCSI command complained: %s\n", + strerror (errno)); + DBG (10, + "sense buffer: %02x %02x %02x %02x %02x %02x %02x %02x" + " %02x %02x %02x %02x %02x %02x %02x %02x\n", + req->sgdata.sg3.sense_buffer[0], + req->sgdata.sg3.sense_buffer[1], + req->sgdata.sg3.sense_buffer[2], + req->sgdata.sg3.sense_buffer[3], + req->sgdata.sg3.sense_buffer[4], + req->sgdata.sg3.sense_buffer[5], + req->sgdata.sg3.sense_buffer[6], + req->sgdata.sg3.sense_buffer[7], + req->sgdata.sg3.sense_buffer[8], + req->sgdata.sg3.sense_buffer[9], + req->sgdata.sg3.sense_buffer[10], + req->sgdata.sg3.sense_buffer[11], + req->sgdata.sg3.sense_buffer[12], + req->sgdata.sg3.sense_buffer[13], + req->sgdata.sg3.sense_buffer[14], + req->sgdata.sg3.sense_buffer[15]); + DBG (10, + "target status: %02x host status: %04x" + " driver status: %04x\n", req->sgdata.sg3.hdr.status, + req->sgdata.sg3.hdr.host_status, + req->sgdata.sg3.hdr.driver_status); + + /* the first three tests below are an replacement of the + error "classification" as it was with the old SG driver, + the fourth test is new. + */ + if (req->sgdata.sg3.hdr.host_status == SG_ERR_DID_NO_CONNECT || req->sgdata.sg3.hdr.host_status == SG_ERR_DID_BUS_BUSY || req->sgdata.sg3.hdr.host_status == SG_ERR_DID_TIME_OUT || req->sgdata.sg3.hdr.driver_status == DRIVER_BUSY || req->sgdata.sg3.hdr.masked_status == 0x04) /* BUSY */ + status = SANE_STATUS_DEVICE_BUSY; + else if (handler && req->sgdata.sg3.hdr.sb_len_wr) + /* sense handler should return SANE_STATUS_GOOD if it + decided all was ok afterall */ + status = + (*handler) (req->fd, req->sgdata.sg3.sense_buffer, + arg); + + /* status bits INTERMEDIATE and CONDITION MET should not + result in an error; neither should reserved bits + */ + else if (((req->sgdata.sg3.hdr.status & 0x2a) == 0) + && (req->sgdata.sg3.hdr.host_status == + SG_ERR_DID_OK) + && + ((req->sgdata.sg3.hdr. + driver_status & ~SG_ERR_DRIVER_SENSE) == + SG_ERR_DRIVER_OK)) + status = SANE_STATUS_GOOD; + else + status = SANE_STATUS_IO_ERROR; + } + +#if 0 + /* Sometimes the Linux SCSI system reports bogus resid values. + Observed with lk 2.4.5, 2.4.13, aic7xxx and sym53c8xx drivers, + if command queueing is used. So we better issue only a warning + */ + if (status == SANE_STATUS_GOOD) + { + if (req->dst_len) + { + *req->dst_len -= req->sgdata.sg3.hdr.resid; + } + } +#endif + if (req->sgdata.sg3.hdr.resid) + { + DBG (1, + "sanei_scsi_req_wait: SG driver returned resid %i\n", + req->sgdata.sg3.hdr.resid); + DBG (1, + " NOTE: This value may be bogus\n"); + } + } +#endif + } + } + + /* dequeue and release processed request: */ + ATOMIC (((fdparms *) fd_info[req->fd].pdata)->sane_qhead + = ((fdparms *) fd_info[req->fd].pdata)->sane_qhead->next; + if (!((fdparms *) fd_info[req->fd].pdata)->sane_qhead) + ((fdparms *) fd_info[req->fd].pdata)->sane_qtail = 0; + req->next = ((fdparms *) fd_info[req->fd].pdata)->sane_free_list; + ((fdparms *) fd_info[req->fd].pdata)->sane_free_list = req); + return status; + } + + SANE_Status + sanei_scsi_cmd2 (int fd, + const void *cmd, size_t cmd_size, + const void *src, size_t src_size, + void *dst, size_t * dst_size) + { + SANE_Status status; + void *id; + + status = + sanei_scsi_req_enter2 (fd, cmd, cmd_size, src, src_size, dst, dst_size, + &id); + if (status != SANE_STATUS_GOOD) + return status; + return sanei_scsi_req_wait (id); + } + +/* The following code (up to and including sanei_scsi_find_devices() ) + is trying to match device/manufacturer names and/or SCSI addressing + numbers (i.e. <host,bus,id,lun>) with a sg device file name + (e.g. /dev/sg3). +*/ +#define PROCFILE "/proc/scsi/scsi" +#define DEVFS_MSK "/dev/scsi/host%d/bus%d/target%d/lun%d/generic" +#define SCAN_MISSES 5 + +/* Some <scsi/scsi.h> headers don't have the following define */ +#ifndef SCSI_IOCTL_GET_IDLUN +#define SCSI_IOCTL_GET_IDLUN 0x5382 +#endif + + static int lx_sg_dev_base = -1; + static int lx_devfs = -1; + + static const struct lx_device_name_list_tag + { + const char *prefix; + char base; + } + lx_dnl[] = + { + { + "/dev/sg", 0} + , + { + "/dev/sg", 'a'} + , + { + "/dev/uk", 0} + , + { + "/dev/gsc", 0} + }; + + static int /* Returns open sg file descriptor, or -1 for no access, + or -2 for not found (or other error) */ + lx_mk_devicename (int guess_devnum, char *name, size_t name_len) + { + int dev_fd, k, dnl_len; + const struct lx_device_name_list_tag *dnp; + + dnl_len = NELEMS (lx_dnl); + k = ((-1 == lx_sg_dev_base) ? 0 : lx_sg_dev_base); + for (; k < dnl_len; ++k) + { + dnp = &lx_dnl[k]; + if (dnp->base) + snprintf (name, name_len, "%s%c", dnp->prefix, + dnp->base + guess_devnum); + else + snprintf (name, name_len, "%s%d", dnp->prefix, guess_devnum); + dev_fd = -1; +#ifdef HAVE_RESMGR + dev_fd = rsm_open_device (name, O_RDWR | O_NONBLOCK); +#endif + if (dev_fd == -1) + dev_fd = open (name, O_RDWR | O_NONBLOCK); + if (dev_fd >= 0) + { + lx_sg_dev_base = k; + return dev_fd; + } + else if ((EACCES == errno) || (EBUSY == errno)) + { + lx_sg_dev_base = k; + return -1; + } + if (-1 != lx_sg_dev_base) + return -2; + } + return -2; + } + + static int /* Returns 1 for match, else 0 */ + lx_chk_id (int dev_fd, int host, int channel, int id, int lun) + { +#ifdef SG_GET_SCSI_ID_FOUND + struct sg_scsi_id ssid; + + if ((ioctl (dev_fd, SG_GET_SCSI_ID, &ssid) >= 0)) + { + DBG (2, "lx_chk_id: %d,%d %d,%d %d,%d %d,%d\n", host, ssid.host_no, + channel, ssid.channel, id, ssid.scsi_id, lun, ssid.lun); + if ((host == ssid.host_no) && + (channel == ssid.channel) && + (id == ssid.scsi_id) && (lun == ssid.lun)) + return 1; + else + return 0; + } +#endif + { + struct my_scsi_idlun + { + int dev_id; + int host_unique_id; + } + my_idlun; + if (ioctl (dev_fd, SCSI_IOCTL_GET_IDLUN, &my_idlun) >= 0) + { + if (((my_idlun.dev_id & 0xff) == id) && + (((my_idlun.dev_id >> 8) & 0xff) == lun) && + (((my_idlun.dev_id >> 16) & 0xff) == channel)) + return 1; /* cheating, assume 'host' number matches */ + } + } + return 0; + } + + static int /* Returns 1 if match with 'name' set, else 0 */ + + lx_scan_sg (int exclude_devnum, char *name, size_t name_len, + int host, int channel, int id, int lun) + { + int dev_fd, k, missed; + + if (-1 == lx_sg_dev_base) + return 0; + for (k = 0, missed = 0; (missed < SCAN_MISSES) && (k < 255); + ++k, ++missed) + { + DBG (2, "lx_scan_sg: k=%d, exclude=%d, missed=%d\n", k, + exclude_devnum, missed); + if (k == exclude_devnum) + { + missed = 0; + continue; /* assumed this one has been checked already */ + } + if ((dev_fd = lx_mk_devicename (k, name, name_len)) >= 0) + { + missed = 0; + if (lx_chk_id (dev_fd, host, channel, id, lun)) + { + close (dev_fd); + return 1; + } + close (dev_fd); + } + else if (-1 == dev_fd) + missed = 0; /* no permissions but something found */ + } + return 0; + } + + static int /* Returns 1 if match, else 0 */ + + lx_chk_devicename (int guess_devnum, char *name, size_t name_len, + int host, int channel, int id, int lun) + { + int dev_fd; + + if (host < 0) + return 0; + if (0 != lx_devfs) + { /* simple mapping if we have devfs */ + if (-1 == lx_devfs) + { + if ((dev_fd = + lx_mk_devicename (guess_devnum, name, name_len)) >= 0) + close (dev_fd); /* hack to load sg driver module */ + } + snprintf (name, name_len, DEVFS_MSK, host, channel, id, lun); + dev_fd = open (name, O_RDWR | O_NONBLOCK); + if (dev_fd >= 0) + { + close (dev_fd); + lx_devfs = 1; + DBG (1, "lx_chk_devicename: matched device(devfs): %s\n", name); + return 1; + } + else if (ENOENT == errno) + lx_devfs = 0; + } + + if ((dev_fd = lx_mk_devicename (guess_devnum, name, name_len)) < -1) + { /* no candidate sg device file name found, try /dev/sg0,1 */ + if ((dev_fd = lx_mk_devicename (0, name, name_len)) < -1) + { + if ((dev_fd = lx_mk_devicename (1, name, name_len)) < -1) + return 0; /* no luck finding sg fd to open */ + } + } + if (dev_fd >= 0) + { +/* now check this fd for match on <host, channel, id, lun> */ + if (lx_chk_id (dev_fd, host, channel, id, lun)) + { + close (dev_fd); + DBG (1, "lx_chk_devicename: matched device(direct): %s\n", name); + return 1; + } + close (dev_fd); + } +/* if mismatch then call scan algorithm */ + if (lx_scan_sg (guess_devnum, name, name_len, host, channel, id, lun)) + { + DBG (1, "lx_chk_devicename: matched device(scan): %s\n", name); + return 1; + } + return 0; + } + +/* Legacy /proc/scsi/scsi */ +static void /* calls 'attach' function pointer with sg device file name iff match */ +sanei_proc_scsi_find_devices (const char *findvendor, const char *findmodel, + const char *findtype, + int findbus, int findchannel, int findid, + int findlun, + SANE_Status (*attach) (const char *dev)) + { +#define FOUND_VENDOR 1 +#define FOUND_MODEL 2 +#define FOUND_TYPE 4 +#define FOUND_REV 8 +#define FOUND_HOST 16 +#define FOUND_CHANNEL 32 +#define FOUND_ID 64 +#define FOUND_LUN 128 +#define FOUND_ALL 255 + + char *me = "sanei_proc_scsi_find_devices"; + + size_t findvendor_len = 0, findmodel_len = 0, findtype_len = 0; + char vendor[32], model[32], type[32], revision[32]; + int bus, channel, id, lun; + + int number, i, j, definedd; + char line[256], dev_name[128], *c1, *c2, ctmp; + const char *string; + FILE *proc_fp; + char *end; + struct + { + const char *name; + size_t name_len; + int is_int; /* integer valued? (not a string) */ + union + { + void *v; /* avoids compiler warnings... */ + char *str; + int *i; + } + u; + } + param[] = + { + { + "Vendor:", 7, 0, + { + 0} + } + , + { + "Model:", 6, 0, + { + 0} + } + , + { + "Type:", 5, 0, + { + 0} + } + , + { + "Rev:", 4, 0, + { + 0} + } + , + { + "scsi", 4, 1, + { + 0} + } + , + { + "Channel:", 8, 1, + { + 0} + } + , + { + "Id:", 3, 1, + { + 0} + } + , + { + "Lun:", 4, 1, + { + 0} + } + }; + + param[0].u.str = vendor; + param[1].u.str = model; + param[2].u.str = type; + param[3].u.str = revision; + param[4].u.i = &bus; + param[5].u.i = &channel; + param[6].u.i = &id; + param[7].u.i = &lun; + + DBG_INIT (); + + proc_fp = fopen (PROCFILE, "r"); + if (!proc_fp) + { + DBG (1, "%s: could not open %s for reading\n", me, PROCFILE); + return; + } + + number = bus = channel = id = lun = -1; + + vendor[0] = model[0] = type[0] = '\0'; + if (findvendor) + findvendor_len = strlen (findvendor); + if (findmodel) + findmodel_len = strlen (findmodel); + if (findtype) + findtype_len = strlen (findtype); + + definedd = 0; + while (!feof (proc_fp)) + { + fgets (line, sizeof (line), proc_fp); + string = sanei_config_skip_whitespace (line); + + while (*string) + { + for (i = 0; i < NELEMS (param); ++i) + { + if (strncmp (string, param[i].name, param[i].name_len) == 0) + { + string += param[i].name_len; + /* Make sure that we don't read the next parameter name + as a value, if the real value consists only of spaces + */ + c2 = string + strlen (string); + for (j = 0; j < NELEMS (param); ++j) + { + c1 = strstr (string, param[j].name); + if ((j != i) && c1 && (c1 < c2)) + c2 = c1; + } + ctmp = *c2; + *c2 = 0; + string = sanei_config_skip_whitespace (string); + + if (param[i].is_int) + { + if (*string) + { + *param[i].u.i = strtol (string, &end, 10); + string = (char *) end; + } + else + *param[i].u.i = 0; + } + else + { + strncpy (param[i].u.str, string, 32); + param[i].u.str[31] = '\0'; + /* while (*string && !isspace (*string)) + ++string; + */ + } + /* string = sanei_config_skip_whitespace (string); */ + *c2 = ctmp; + string = c2; + definedd |= 1 << i; + + if (param[i].u.v == &bus) + { + ++number; + definedd = FOUND_HOST; + } + break; + } + } + if (i >= NELEMS (param)) + ++string; /* no match */ + } + + if (FOUND_ALL != definedd) + /* some info is still missing */ + continue; + + definedd = 0; + if ((!findvendor || strncmp (vendor, findvendor, findvendor_len) == 0) + && (!findmodel || strncmp (model, findmodel, findmodel_len) == 0) + && (!findtype || strncmp (type, findtype, findtype_len) == 0) + && (findbus == -1 || bus == findbus) + && (findchannel == -1 || channel == findchannel) + && (findid == -1 || id == findid) + && (findlun == -1 || lun == findlun)) + { + DBG (2, "%s: found: vendor=%s model=%s type=%s\n\t" + "bus=%d chan=%d id=%d lun=%d num=%d\n", + me, findvendor, findmodel, findtype, + bus, channel, id, lun, number); + if (lx_chk_devicename (number, dev_name, sizeof (dev_name), bus, + channel, id, lun) + && ((*attach) (dev_name) != SANE_STATUS_GOOD)) + { + DBG(1,"sanei_scsi_find_devices: bad attach\n"); + } + } + else + { + DBG (2, "%s: no match\n", me); + } + vendor[0] = model[0] = type[0] = 0; + bus = channel = id = lun = -1; + } + fclose (proc_fp); + } + +#define SYSFS_SCSI_DEVICES "/sys/bus/scsi/devices" + +/* From linux/drivers/scsi/scsi.c */ +static char *lnxscsi_device_types[] = { + "Direct-Access ", + "Sequential-Access", + "Printer ", + "Processor ", + "WORM ", + "CD-ROM ", + "Scanner ", + "Optical Device ", + "Medium Changer ", + "Communications ", + "ASC IT8 ", + "ASC IT8 ", + "RAID ", + "Enclosure ", + "Direct-Access-RBC", + "Optical card ", + "Bridge controller", + "Object storage ", + "Automation/Drive " +}; + +void /* calls 'attach' function pointer with sg device file name iff match */ +sanei_scsi_find_devices (const char *findvendor, const char *findmodel, + const char *findtype, + int findbus, int findchannel, int findid, + int findlun, + SANE_Status (*attach) (const char *dev)) + { + char *me = "sanei_scsi_find_devices"; + char path[PATH_MAX]; + char dev_name[128]; + struct dirent buf; + struct dirent *de; + DIR *scsidevs; + FILE *fp; + char *ptr; + char *end; + int bcil[4]; /* bus, channel, id, lun */ + char vmt[3][33]; /* vendor, model, type */ + int vmt_len[3]; + char *vmtfiles[3] = { "vendor", "model", "type" }; + int lastbus; + int number; + int i; + long val; + int ret; + + DBG_INIT (); + + DBG (2, "%s: looking for: v=%s m=%s t=%s b=%d c=%d i=%d l=%d\n", + me, findvendor, findmodel, findtype, + findbus, findchannel, findid, findlun); + + scsidevs = opendir (SYSFS_SCSI_DEVICES); + if (!scsidevs) + { + DBG (1, "%s: could not open %s; falling back to /proc\n", + me, SYSFS_SCSI_DEVICES); + + sanei_proc_scsi_find_devices (findvendor, findmodel, findtype, + findbus, findchannel, findid, findlun, + attach); + return; + } + + vmt_len[0] = (findvendor) ? strlen(findvendor) : 0; + vmt_len[1] = (findmodel) ? strlen(findmodel) : 0; + vmt_len[2] = (findtype) ? strlen(findtype) : 0; + + lastbus = -1; + number = -1; + for (;;) + { + ret = readdir_r(scsidevs, &buf, &de); + if (ret != 0) + { + DBG (1, "%s: could not read directory %s: %s\n", + me, SYSFS_SCSI_DEVICES, strerror(errno)); + + break; + } + + if (de == NULL) + break; + + if (buf.d_name[0] == '.') + continue; + + /* Extract bus, channel, id, lun from directory name b:c:i:l */ + ptr = buf.d_name; + for (i = 0; i < 4; i++) + { + errno = 0; + val = strtol (ptr, &end, 10); + if (((errno == ERANGE) && ((val == LONG_MAX) || (val == LONG_MIN))) + || ((errno != 0) && (val == 0))) + { + DBG (1, "%s: invalid integer in string (%s): %s\n", + me, ptr, strerror(errno)); + + i = 12; /* Skip */ + break; + } + + if (end == ptr) + { + DBG (1, "%s: no integer found in string: %s (%d)\n", me, ptr, i); + + i = 12; /* Skip */ + break; + } + + if (*end && (*end != ':')) + { + DBG (1, "%s: parse error on string %s (%d)\n", me, buf.d_name, i); + + i = 12; /* Skip */ + break; + } + + if (val > INT_MAX) + { + DBG (1, "%s: integer value too large (%s)\n", me, buf.d_name); + + i = 12; /* Skip */ + break; + } + + bcil[i] = (int) val; + ptr = end + 1; + } + + /* Skip this one */ + if (i == 12) + continue; + + if (bcil[0] != lastbus) + { + lastbus = bcil[0]; + number++; + } + + for (i = 0; i < 3; i++) + { + ret = snprintf (path, PATH_MAX, "%s/%s/%s", + SYSFS_SCSI_DEVICES, buf.d_name, vmtfiles[i]); + if ((ret < 0) || (ret >= PATH_MAX)) + { + DBG (1, "%s: skipping %s/%s, PATH_MAX exceeded on %s\n", + me, SYSFS_SCSI_DEVICES, buf.d_name, vmtfiles[i]); + + i = 12; /* Skip */ + break; + } + + memset (vmt[i], 0, sizeof(vmt[i])); + + fp = fopen(path, "r"); + if (!fp) + { + DBG (1, "%s: could not open %s: %s\n", me, path, strerror(errno)); + + i = 12; /* Skip */ + break; + } + + ret = fread (vmt[i], 1, sizeof(vmt[i]) - 1, fp); + if (ret <= 0) + { + if (ferror(fp)) + { + DBG (1, "%s: error reading %s\n", me, path); + + i = 12; /* Skip */ + break; + } + } + + if (vmt[i][ret - 1] == '\n') + vmt[i][ret - 1] = '\0'; + + fclose (fp); + } + + /* Skip this one */ + if (i == 12) + continue; + + /* Type is a numeric string and must be converted back to a well-known string */ + errno = 0; + val = strtol (vmt[2], &end, 10); + if (((errno == ERANGE) && ((val == LONG_MAX) || (val == LONG_MIN))) + || ((errno != 0) && (val == 0))) + { + DBG (1, "%s: invalid integer in type string (%s): %s\n", + me, vmt[2], strerror(errno)); + continue; + } + + if (end == vmt[2]) + { + DBG (1, "%s: no integer found in type string: %s\n", me, vmt[2]); + continue; + } + + if ((val < 0) || (val >= (int)(sizeof(lnxscsi_device_types) / sizeof(lnxscsi_device_types[0])))) + { + DBG (1, "%s: invalid type %ld\n", me, val); + continue; + } + + strncpy(vmt[2], lnxscsi_device_types[val], sizeof(vmt[2]) - 1); + + if ((!findvendor || strncmp (vmt[0], findvendor, vmt_len[0]) == 0) + && (!findmodel || strncmp (vmt[1], findmodel, vmt_len[1]) == 0) + && (!findtype || strncmp (vmt[2], findtype, vmt_len[2]) == 0) + && (findbus == -1 || bcil[0] == findbus) + && (findchannel == -1 || bcil[1] == findchannel) + && (findid == -1 || bcil[2] == findid) + && (findlun == -1 || bcil[3] == findlun)) + { + DBG (2, "%s: found: vendor=%s model=%s type=%s\n\t" + "bus=%d chan=%d id=%d lun=%d num=%d\n", + me, vmt[0], vmt[1], vmt[2], + bcil[0], bcil[1], bcil[2], bcil[3], number); + + if (lx_chk_devicename (number, dev_name, sizeof (dev_name), + bcil[0], bcil[1], bcil[2], bcil[3]) + && ((*attach) (dev_name) != SANE_STATUS_GOOD)) + { + DBG (1, "%s: bad attach\n", me); + } + } + else + { + DBG (2, "%s: no match\n", me); + } + } + + closedir(scsidevs); + } + +#endif /* USE == LINUX_INTERFACE */ + + +#if USE == BSD_INTERFACE + +#ifndef HAVE_SCSIREQ_ENTER + static int scsireq_enter (int fd, scsireq_t * hdr) + { + return ioctl (fd, SCIOCCOMMAND, hdr); + } +#endif /* !HAVE_SCSIREQ_ENTER */ + + SANE_Status + sanei_scsi_cmd2 (int fd, + const void *cmd, size_t cmd_size, + const void *src, size_t src_size, + void *dst, size_t * dst_size) + { + /* xxx obsolete: size_t cdb_size; + */ + scsireq_t hdr; + int result; + +/* xxx obsolete: + cdb_size = CDB_SIZE (*(u_char *) src); +*/ + + memset (&hdr, 0, sizeof (hdr)); + memcpy (hdr.cmd, cmd, cmd_size); + if (dst_size && *dst_size) + { + /* xxx obsolete: assert (cdb_size == src_size); + */ + hdr.flags = SCCMD_READ; + hdr.databuf = dst; + hdr.datalen = *dst_size; + } + else + { + /* xxx obsolete: assert (cdb_size <= src_size); + */ + hdr.flags = SCCMD_WRITE; + /* The old variant: + hdr.databuf = (char *) src + cdb_size; + hdr.datalen = src_size; + xxxxxx huh? Shouldn´t the above line have been src_size - cdb_size) + */ + hdr.databuf = (char *) src; + hdr.datalen = src_size; + } + hdr.timeout = sane_scsicmd_timeout * 1000; + hdr.cmdlen = cmd_size; + hdr.senselen = sizeof (hdr.sense); + + result = scsireq_enter (fd, &hdr); + if (result < 0) + { + DBG (1, "sanei_scsi_cmd: scsi_reqenter() failed: %s\n", + strerror (errno)); + return SANE_STATUS_IO_ERROR; + } + if (hdr.retsts != SCCMD_OK) + { + SANEI_SCSI_Sense_Handler handler; + + DBG (1, "sanei_scsi_cmd: scsi returned with status %d\n", hdr.retsts); + switch (hdr.retsts) + { + case SCCMD_TIMEOUT: + case SCCMD_BUSY: + return SANE_STATUS_DEVICE_BUSY; + + case SCCMD_SENSE: + handler = fd_info[fd].sense_handler; + if (handler) + return (*handler) (fd, &hdr.sense[0], + fd_info[fd].sense_handler_arg); + /* fall through */ + default: + return SANE_STATUS_IO_ERROR; + } + } + + if (dst_size) + *dst_size = hdr.datalen_used; + + return SANE_STATUS_GOOD; + } +#endif /* USE == BSD_INTERFACE */ + +#if USE == FREEBSD_CAM_INTERFACE + SANE_Status sanei_scsi_cmd2 (int fd, + const void *cmd, size_t cmd_size, + const void *src, size_t src_size, + void *dst, size_t * dst_size) + { + + struct cam_device *dev; + union ccb *ccb; + int rv; + u_int32_t ccb_flags; + char *data_buf; + size_t data_len; + SANE_Status status; + + if (fd < 0 || fd > CAM_MAXDEVS || cam_devices[fd] == NULL) + { + fprintf (stderr, "attempt to reference invalid unit %d\n", fd); + return SANE_STATUS_INVAL; + } + + dev = cam_devices[fd]; + ccb = cam_getccb (dev); + + /* Build the CCB */ + bzero (&(&ccb->ccb_h)[1], sizeof (struct ccb_scsiio)); + bcopy (cmd, &ccb->csio.cdb_io.cdb_bytes, cmd_size); + + /* + * Set the data direction flags. + */ + if (dst_size && *dst_size) + { + /* xxx obsolete: assert (cdb_size == src_size); + */ + ccb_flags = CAM_DIR_IN; + data_buf = ((char *) (dst)); + data_len = *dst_size; + } + else if (src_size > 0) + { + ccb_flags = CAM_DIR_OUT; + data_buf = ((char *) (src)); + data_len = src_size; + } + else + { + ccb_flags = CAM_DIR_NONE; + data_buf = NULL; + data_len = 0; + } + + cam_fill_csio (&ccb->csio, + /* retries */ 1, + /* cbfncp */ NULL, + /* flags */ ccb_flags, + /* tag_action */ MSG_SIMPLE_Q_TAG, + /* data_ptr */ (u_int8_t *) data_buf, + /* dxfer_len */ data_len, + /* sense_len */ SSD_FULL_SIZE, + /* cdb_len */ cmd_size, + /* timeout */ sane_scsicmd_timeout * 1000); + + /* Run the command */ + errno = 0; + if ((rv = cam_send_ccb (dev, ccb)) == -1) + { + cam_freeccb (ccb); + return (SANE_STATUS_IO_ERROR); + } + + if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) + { + SANEI_SCSI_Sense_Handler handler; + + DBG (1, "sanei_scsi_cmd: scsi returned with status %d\n", + (ccb->ccb_h.status & CAM_STATUS_MASK)); + + switch (ccb->ccb_h.status & CAM_STATUS_MASK) + { + case CAM_BUSY: + case CAM_SEL_TIMEOUT: + case CAM_SCSI_BUSY: + status = SANE_STATUS_DEVICE_BUSY; + break; + default: + status = SANE_STATUS_IO_ERROR; + } + + handler = fd_info[fd].sense_handler; + if (handler && (ccb->ccb_h.status & CAM_AUTOSNS_VALID)) + { + SANE_Status st = (*handler) + (fd, ((u_char *) (&ccb->csio.sense_data)), + fd_info[fd].sense_handler_arg); + cam_freeccb (ccb); + return st; + } + else + { + cam_freeccb (ccb); + return status; + } + } + cam_freeccb (ccb); + return SANE_STATUS_GOOD; + } + +#define WE_HAVE_FIND_DEVICES + + int + cam_compare_inquiry (int fd, path_id_t path_id, + target_id_t target_id, lun_id_t target_lun, + const char *vendor, const char *product, + const char *type) + { + struct ccb_dev_match cdm; + struct device_match_pattern *pattern; + struct scsi_inquiry_data *inq; + int retval = 0; + + /* build ccb for device match */ + bzero (&cdm, sizeof (cdm)); + cdm.ccb_h.func_code = XPT_DEV_MATCH; + + /* result buffer */ + cdm.match_buf_len = sizeof (struct dev_match_result); + cdm.matches = (struct dev_match_result *) malloc (cdm.match_buf_len); + cdm.num_matches = 0; + + /* pattern buffer */ + cdm.num_patterns = 1; + cdm.pattern_buf_len = sizeof (struct dev_match_pattern); + cdm.patterns = (struct dev_match_pattern *) malloc (cdm.pattern_buf_len); + + /* assemble conditions */ + cdm.patterns[0].type = DEV_MATCH_DEVICE; + pattern = &cdm.patterns[0].pattern.device_pattern; + pattern->flags = DEV_MATCH_PATH | DEV_MATCH_TARGET | DEV_MATCH_LUN; + pattern->path_id = path_id; + pattern->target_id = target_id; + pattern->target_lun = target_lun; + + if (ioctl (fd, CAMIOCOMMAND, &cdm) == -1) + { + DBG (1, "error sending CAMIOCOMMAND ioctl"); + retval = -1; + goto ret; + } + + if ((cdm.ccb_h.status != CAM_REQ_CMP) + || ((cdm.status != CAM_DEV_MATCH_LAST) + && (cdm.status != CAM_DEV_MATCH_MORE))) + { + DBG (1, "got CAM error %#x, CDM error %d\n", + cdm.ccb_h.status, cdm.status); + retval = -1; + goto ret; + } + + if (cdm.num_matches == 0) + { + DBG (1, "not found\n"); + retval = -1; + goto ret; + } + + if (cdm.matches[0].type != DEV_MATCH_DEVICE) + { + DBG (1, "no device match\n"); + retval = -1; + goto ret; + } + + inq = &cdm.matches[0].result.device_result.inq_data; + if ((vendor && cam_strmatch (inq->vendor, vendor, SID_VENDOR_SIZE)) || + (product && cam_strmatch (inq->product, product, SID_PRODUCT_SIZE))) + retval = 1; + + ret: + free (cdm.patterns); + free (cdm.matches); + return (retval); + } + + void + sanei_scsi_find_devices (const char *findvendor, const char *findmodel, + const char *findtype, + int findbus, int findchannel, int findid, + int findlun, + SANE_Status (*attach) (const char *dev)) + { + int fd; + struct ccb_dev_match cdm; + struct periph_match_pattern *pattern; + struct periph_match_result *result; + int i; + char devname[16]; + + DBG_INIT (); + + if ((fd = open (XPT_DEVICE, O_RDWR)) == -1) + { + DBG (1, "could not open %s\n", XPT_DEVICE); + return; + } + + /* build ccb for device match */ + bzero (&cdm, sizeof (cdm)); + cdm.ccb_h.func_code = XPT_DEV_MATCH; + + /* result buffer */ + cdm.match_buf_len = sizeof (struct dev_match_result) * 100; + cdm.matches = (struct dev_match_result *) malloc (cdm.match_buf_len); + cdm.num_matches = 0; + + /* pattern buffer */ + cdm.num_patterns = 1; + cdm.pattern_buf_len = sizeof (struct dev_match_pattern); + cdm.patterns = (struct dev_match_pattern *) malloc (cdm.pattern_buf_len); + + /* assemble conditions ... findchannel is ignored */ + cdm.patterns[0].type = DEV_MATCH_PERIPH; + pattern = &cdm.patterns[0].pattern.periph_pattern; + pattern->flags = PERIPH_MATCH_NAME; + strcpy (pattern->periph_name, "pass"); + if (findbus != -1) + { + pattern->path_id = findbus; + pattern->flags |= PERIPH_MATCH_PATH; + } + if (findid != -1) + { + pattern->target_id = findid; + pattern->flags |= PERIPH_MATCH_TARGET; + } + if (findlun != -1) + { + pattern->target_lun = findlun; + pattern->flags |= PERIPH_MATCH_LUN; + } + + /* result loop */ + do + { + if (ioctl (fd, CAMIOCOMMAND, &cdm) == -1) + { + DBG (1, "error sending CAMIOCOMMAND ioctl"); + break; + } + + if ((cdm.ccb_h.status != CAM_REQ_CMP) + || ((cdm.status != CAM_DEV_MATCH_LAST) + && (cdm.status != CAM_DEV_MATCH_MORE))) + { + DBG (1, "got CAM error %#x, CDM error %d\n", + cdm.ccb_h.status, cdm.status); + break; + } + + for (i = 0; i < cdm.num_matches; i++) + { + if (cdm.matches[i].type != DEV_MATCH_PERIPH) + continue; + result = &cdm.matches[i].result.periph_result; + DBG (4, "%s%d on scbus%d %d:%d\n", + result->periph_name, result->unit_number, + result->path_id, result->target_id, result->target_lun); + if (cam_compare_inquiry (fd, result->path_id, + result->target_id, result->target_lun, + findvendor, findmodel, findtype) == 0) + { + sprintf (devname, "/dev/%s%d", result->periph_name, + result->unit_number); + (*attach) (devname); + } + } + } + while ((cdm.ccb_h.status == CAM_REQ_CMP) + && (cdm.status == CAM_DEV_MATCH_MORE)); + + free (cdm.patterns); + free (cdm.matches); + close (fd); + return; + } + +#endif + + + +#if USE == HPUX_INTERFACE +/* XXX untested code! */ + SANE_Status + sanei_scsi_cmd2 (int fd, + const void *cmd, size_t cmd_size, + const void *src, size_t src_size, + void *dst, size_t * dst_size) + { + struct sctl_io hdr; + /* xxx obsolete size_t cdb_size; + + cdb_size = CDB_SIZE (*(u_char *) src); + */ + + memset (&hdr, 0, sizeof (hdr)); + memcpy (hdr.cdb, cmd, cmd_size); + if (dst_size && *dst_size) + { + /* xxx obsolete assert (cdb_size == src_size); + */ + hdr.flags = SCTL_READ; + hdr.data = dst; + hdr.data_length = *dst_size; + } + else + { + /* xxx obsolete assert (cdb_size <= src_size); + */ + hdr.data = (char *) src; + hdr.data_length = src_size; + } + hdr.cdb_length = cmd_size; + hdr.max_msecs = sane_scsicmd_timeout * 1000; + if (ioctl (fd, SIOC_IO, &hdr) < 0) + { + DBG (1, "sanei_scsi_cmd: ioctl(SIOC_IO) failed: %s\n", + strerror (errno)); + return SANE_STATUS_IO_ERROR; + } + if (hdr.cdb_status) + DBG (1, "sanei_scsi_cmd: SCSI completed with cdb_status=%d\n", + hdr.cdb_status); + if (dst_size) + *dst_size = hdr.data_xfer; + + if (hdr.sense_xfer > 0 && (hdr.sense[0] & 0x80) + && fd_info[fd].sense_handler) + return (*fd_info[fd].sense_handler) (fd, hdr.sense, + fd_info[fd].sense_handler_arg); + return SANE_STATUS_GOOD; + } +#endif /* USE == HPUX_INTERFACE */ + + +#if USE == OPENSTEP_INTERFACE + SANE_Status + sanei_scsi_cmd2 (int fd, + const void *cmd, size_t cmd_size, + const void *src, size_t src_size, + void *dst, size_t * dst_size) + { + struct scsi_req hdr; + /* xxx obsolete size_t cdb_size; + + cdb_size = CDB_SIZE (*(u_char *) src); + */ + + memset (&hdr, 0, sizeof (hdr)); + memcpy (&hdr.sr_cdb, cmd, cmd_size); + hdr.sr_cdb_length = cmd_size; + + if (dst_size && *dst_size) + { + /* xxx obsolete assert (cdb_size == src_size); + */ + hdr.sr_dma_dir = SR_DMA_RD; + hdr.sr_addr = dst; + hdr.sr_dma_max = *dst_size; + } + else + { + /* xxx obsolete assert (cdb_size <= src_size); + */ + hdr.sr_dma_dir = SR_DMA_WR; + hdr.sr_addr = (char *) src; + hdr.sr_dma_max = src_size; + } + hdr.sr_ioto = sane_scsicmd_timeout; + + if (ioctl (fd, SGIOCREQ, &hdr) == -1) + { + DBG (1, "sanei_scsi_cmd: ioctl(SGIOCREQ) failed: %s\n", + strerror (errno)); + return SANE_STATUS_IO_ERROR; + } + if (hdr.sr_io_status != 1) + DBG (1, "sanei_scsi_cmd: SGIOCREQ completed with sr_io_status=%d\n", + hdr.sr_io_status); + + if (hdr.sr_io_status == SR_IOST_CHKSNV) + { + struct scsi_req sr; + struct cdb_6 *cdbp = &sr.sr_cdb.cdb_c6; + struct esense_reply sense_reply; + int i; + char *p; + + /* clear struct */ + p = (char *) cdbp; + for (i = 0; i < sizeof (union cdb); i++) + *p++ = 0; + memset (&sr, 0, sizeof (struct scsi_req)); + + cdbp->c6_opcode = C6OP_REQSENSE; + cdbp->c6_lun = 0; /* where do I get the lun from? */ + cdbp->c6_len = 0x20; + cdbp->c6_ctrl = 0; + + sr.sr_dma_dir = SR_DMA_RD; + sr.sr_addr = (char *) &sense_reply; + sr.sr_dma_max = sizeof (struct esense_reply); + sr.sr_ioto = sane_scsicmd_timeout; + sr.sr_cdb_length = 6; + + ioctl (fd, SGIOCREQ, &sr); + if (sense_reply.er_ibvalid) + { + sr.sr_esense = sense_reply; + if (fd_info[fd].sense_handler) + return (*fd_info[fd].sense_handler) + (fd, (u_char *) & sr.sr_esense, + fd_info[fd].sense_handler_arg); + } + + /* sense reply is invalid */ + return SANE_STATUS_INVAL; + } + + if (hdr.sr_scsi_status == SR_IOST_CHKSV && fd_info[fd].sense_handler) + return (*fd_info[fd].sense_handler) (fd, (u_char *) & hdr.sr_esense, + fd_info[fd].sense_handler_arg); + if (dst_size) + *dst_size = hdr.sr_dma_xfr; + return SANE_STATUS_GOOD; + } +#endif /* USE == OPENSTEP_INTERFACE */ + + +#if USE == DECUNIX_INTERFACE + SANE_Status + sanei_scsi_cmd2 (int fd, + const void *cmd, size_t cmd_size, + const void *src, size_t src_size, + void *dst, size_t * dst_size) + { + u_char sense[64]; + UAGT_CAM_CCB hdr; + CCB_SCSIIO ccb; + /* xxx obsolete size_t cdb_size; + + cdb_size = CDB_SIZE (*(u_char *) src); + */ + + memset (&ccb, 0, sizeof (ccb)); + ccb.cam_ch.my_addr = (CCB_HEADER *) & ccb; + ccb.cam_ch.cam_ccb_len = sizeof (ccb); + ccb.cam_ch.cam_func_code = XPT_SCSI_IO; + ccb.cam_ch.cam_path_id = fd_info[fd].bus; + ccb.cam_ch.cam_target_id = fd_info[fd].target; + ccb.cam_ch.cam_target_lun = fd_info[fd].lun; + ccb.cam_ch.cam_flags = 0; + + if (dst_size && *dst_size) + { + /* xxx obsolete assert (cdb_size == src_size); + */ + ccb.cam_ch.cam_flags |= CAM_DIR_IN; + ccb.cam_data_ptr = (u_char *) dst; + ccb.cam_dxfer_len = *dst_size; + } + else + { + /* xxx obsolete assert (cdb_size <= src_size); + */ + if (0 == src_size) + ccb.cam_ch.cam_flags |= CAM_DIR_NONE; + else + ccb.cam_ch.cam_flags |= CAM_DIR_OUT; + ccb.cam_data_ptr = (u_char *) src; + ccb.cam_dxfer_len = src_size; + } + ccb.cam_timeout = sane_scsicmd_timeout; + ccb.cam_cdb_len = cmd_size; + memcpy (&ccb.cam_cdb_io.cam_cdb_bytes[0], cmd, cmd_size); + + memset (&hdr, 0, sizeof (hdr)); + hdr.uagt_ccb = (CCB_HEADER *) & ccb; + hdr.uagt_ccblen = sizeof (ccb); + hdr.uagt_buffer = ccb.cam_data_ptr; + hdr.uagt_buflen = ccb.cam_dxfer_len; + hdr.uagt_snsbuf = sense; + hdr.uagt_snslen = sizeof (sense); + hdr.uagt_cdb = 0; /* indicate that CDB is in CCB */ + hdr.uagt_cdblen = 0; + + if (ioctl (cam_fd, UAGT_CAM_IO, &hdr) < 0) + { + DBG (1, "sanei_scsi_cmd: ioctl(UAGT_CAM_IO) failed: %s\n", + strerror (errno)); + return SANE_STATUS_IO_ERROR; + } + if (ccb.cam_ch.cam_status != CAM_REQ_CMP) + { + DBG (1, "sanei_scsi_cmd: UAGT_CAM_IO completed with cam_status=%d\n", + ccb.cam_ch.cam_status); + + if (ccb.cam_ch.cam_status == CAM_AUTOSNS_VALID + && fd_info[fd].sense_handler) + return (*fd_info[fd].sense_handler) (fd, sense, + fd_info[fd].sense_handler_arg); + else + return SANE_STATUS_INVAL; + } + if (dst_size) + *dst_size = ccb.cam_dxfer_len; + return SANE_STATUS_GOOD; + } +#endif /* USE == DECUNIX_INTERFACE */ + + +#if USE == SCO_OS5_INTERFACE + SANE_Status + sanei_scsi_cmd2 (int fd, + const void *cmd, size_t cmd_size, + const void *src, size_t src_size, + void *dst, size_t * dst_size) + { + static u_char sense_buffer[256]; + struct scsicmd2 sc2; + struct scsicmd *sc; + /* xxx obsolete int cdb_size; + */ + int opcode; + int i; + + if (fd < 0) + return SANE_STATUS_IO_ERROR; + + memset (&sc2, 0, sizeof (sc2)); + sc = &sc2.cmd; + sc2.sense_len = sizeof (sense_buffer); + sc2.sense_ptr = sense_buffer; + + /* xxx obsolete cdb_size = CDB_SIZE (*(u_char *) src); + */ + if (dst_size && *dst_size) + { + sc->is_write = 0; + sc->data_ptr = dst; + sc->data_len = *dst_size; + } + else + { + sc->data_len = src_size; + sc->data_ptr = (char *) src; + sc->is_write = 1; + } + memcpy (sc->cdb, cmd, cmd_size); + sc->cdb_len = cmd_size; + + /* Send the command down via the "pass-through" interface */ + if (ioctl (fd, SCSIUSERCMD2, &sc2) < 0) + { + DBG (1, "sanei_scsi_cmd: ioctl(SCSIUSERCMD2) failed: %s\n", + strerror (errno)); + return SANE_STATUS_IO_ERROR; + } + if (sc->host_sts || sc->target_sts) + { + DBG (1, "sanei_scsi_cmd: SCSIUSERCMD2 completed with " + "host_sts=%x, target_sts=%x\n", sc->host_sts, sc->target_sts); + if (fd_info[fd].sense_handler) + return (*fd_info[fd].sense_handler) (fd, sense_buffer, + fd_info[fd].sense_handler_arg); + return SANE_STATUS_IO_ERROR; + } + return SANE_STATUS_GOOD; + } +#endif /* USE == SCO_OS5_INTERFACE */ +#if USE == SYSVR4_INTERFACE + +/* + * UNIXWARE 2.x interface + * (c) R=I+S Rapp Informatik System Germany + * Email: wolfgang@rapp-informatik.de + * + * The driver version should run with other scsi componets like disk + * attached to the same controller at the same time. + * + * Attention : This port needs a sane kernel driver for Unixware 2.x + * The driver is available in binary pkgadd format + * Plese mail me. + * + */ + SANE_Status + sanei_scsi_cmd2 (int fd, + const void *cmd, size_t cmd_size, + const void *src, size_t src_size, + void *dst, size_t * dst_size) + { + struct sb sb, *sb_ptr; /* Command block and pointer */ + struct scs *scs; /* group 6 command pointer */ + struct scm *scm; /* group 10 command pointer */ + struct scv *scv; /* group 12 command pointer */ + char sense[32]; /* for call of sens req */ + char cmd[16]; /* global for right alignment */ + char *cp; + + /* xxx obsolete size_t cdb_size; + + cdb_size = CDB_SIZE (*(u_char *) src); + */ + memset (&cmd, 0, 16); + sb_ptr = &sb; + sb_ptr->sb_type = ISCB_TYPE; + cp = (char *) cmd; + DBG (1, + "cdb_size = %d src = {0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x ...}\n", + cmd_size, cp[0], cp[1], cp[2], cp[3], cp[4], cp[5], cp[6], cp[7], + cp[8], cp[9]); + switch (cmd_size) + { + default: + return SANE_STATUS_IO_ERROR; + case 6: + scs = (struct scs *) cmd; + memcpy (SCS_AD (scs), cmd, SCS_SZ); + scs->ss_lun = 0; + sb_ptr->SCB.sc_cmdpt = SCS_AD (scs); + sb_ptr->SCB.sc_cmdsz = SCS_SZ; + break; + case 10: + scm = (struct scm *) cmd; + memcpy (SCM_AD (scm), cmd, SCM_SZ); + scm->sm_lun = 0; + sb_ptr->SCB.sc_cmdpt = SCM_AD (scm); + sb_ptr->SCB.sc_cmdsz = SCM_SZ; + break; + case 12: + scv = (struct scv *) cmd; + memcpy (SCV_AD (scv), cmd, SCV_SZ); + scv->sv_lun = 0; + sb_ptr->SCB.sc_cmdpt = SCV_AD (scv); + sb_ptr->SCB.sc_cmdsz = SCV_SZ; + break; + } + if (dst_size && *dst_size) + { + assert (0 == src_size); + sb_ptr->SCB.sc_mode = SCB_READ; + sb_ptr->SCB.sc_datapt = dst; + sb_ptr->SCB.sc_datasz = *dst_size; + } + else + { + assert (0 <= src_size); + sb_ptr->SCB.sc_mode = SCB_WRITE; + sb_ptr->SCB.sc_datapt = (char *) src; + if ((sb_ptr->SCB.sc_datasz = src_size) > 0) + { + sb_ptr->SCB.sc_mode = SCB_WRITE; + } + else + { + /* also use READ mode if the backends have write with length 0 */ + sb_ptr->SCB.sc_mode = SCB_READ; + } + } + sb_ptr->SCB.sc_time = sane_scsicmd_timeout * 1000; + DBG (1, "sanei_scsi_cmd: sc_mode = %d, sc_cmdsz = %d, sc_datasz = %d\n", + sb_ptr->SCB.sc_mode, sb_ptr->SCB.sc_cmdsz, sb_ptr->SCB.sc_datasz); + { + /* do read write by normal read or write system calls */ + /* the driver will lock process in momory and do optimized transfer */ + cp = (char *) cmd; + switch (*cp) + { + case 0x0: /* test unit ready */ + if (ioctl (fd, SS_TEST, NULL) < 0) + { + return SANE_STATUS_DEVICE_BUSY; + } + break; + case SS_READ: + case SM_READ: + if (*dst_size > 0x2048) + { + sb_ptr->SCB.sc_datapt = NULL; + sb_ptr->SCB.sc_datasz = 0; + if (memcmp + (sb_ptr->SCB.sc_cmdpt, lastrcmd, sb_ptr->SCB.sc_cmdsz)) + { + /* set the command block for the next read or write */ + memcpy (lastrcmd, sb_ptr->SCB.sc_cmdpt, + sb_ptr->SCB.sc_cmdsz); + if (!ioctl (fd, SDI_SEND, sb_ptr)) + { + *dst_size = read (fd, dst, *dst_size); + if (*dst_size == -1) + { + perror ("sanei-scsi:UW-driver read "); + return SANE_STATUS_IO_ERROR; + } + break; + } + } + else + { + *dst_size = read (fd, dst, *dst_size); + if (*dst_size == -1) + { + perror ("sanei-scsi:UW-driver read "); + return SANE_STATUS_IO_ERROR; + } + break; + } + return SANE_STATUS_IO_ERROR; + } + /* fall through for small read */ + default: + if (ioctl (fd, SDI_SEND, sb_ptr) < 0) + { + DBG (1, "sanei_scsi_cmd: ioctl(SDI_SEND) FAILED: %s\n", + strerror (errno)); + return SANE_STATUS_IO_ERROR; + } + if (dst_size) + *dst_size = sb_ptr->SCB.sc_datasz; +#ifdef UWSUPPORTED /* at this time not supported by driver */ + if (sb_ptr->SCB.sc_comp_code != SDI_ASW) + { + DBG (1, "sanei_scsi_cmd: scsi_cmd failture %x\n", + sb_ptr->SCB.sc_comp_code); + if (sb_ptr->SCB.sc_comp_code == SDI_CKSTAT + && sb_ptr->SCB.sc_status == S_CKCON) + if (fd_info[fd].sense_handler) + { + void *arg = fd_info[fd].sense_handler_arg; + return (*fd_info[fd].sense_handler) (fd, + (u_char *) & sb_ptr-> + SCB.sc_link, arg); + } + return SANE_STATUS_IO_ERROR; + } +#endif + break; + } + return SANE_STATUS_GOOD; + } + } +#endif /* USE == SYSVR4_INTERFACE */ +#if USE == SCO_UW71_INTERFACE + SANE_Status + sanei_scsi_cmd2 (int fd, + const void *cmd, size_t cmd_size, + const void *src, size_t src_size, + void *dst, size_t * dst_size) + { + static u_char sense_buffer[24]; + struct scb cmdblk; + time_t elapsed; + uint_t compcode, status; + /* xxx obsolete int cdb_size, mode; + */ + int mode; + int i; + + if (fd < 0) + return SANE_STATUS_IO_ERROR; + + cmdblk.sc_cmdpt = (caddr_t) cmd; + /* xxx obsolete cdb_size = CDB_SIZE (*(u_char *) src); + */ + cmdblk.sc_cmdsz = cmd_size; + cmdblk.sc_time = 60000; /* 60 secs */ + + if (dst_size && *dst_size) + { + /* xxx obsolete assert (cdb_size == src_size); + */ + cmdblk.sc_datapt = (caddr_t) dst; + cmdblk.sc_datasz = *dst_size; + mode = SCB_READ; + } + else + { + /* xxx obsolete assert (cdb_size <= src_size); + */ + cmdblk.sc_datapt = (char *) src; + cmdblk.sc_datasz = src_size; + mode = SCB_WRITE; + } + + if (pt_send (fd, cmdblk.sc_cmdpt, cmdblk.sc_cmdsz, cmdblk.sc_datapt, + cmdblk.sc_datasz, mode, cmdblk.sc_time, &elapsed, &compcode, + &status, sense_buffer, sizeof (sense_buffer)) != 0) + { + DBG (1, "sanei_scsi_cmd: pt_send failed: %s!\n", strerror (errno)); + } + else + { + DBG (2, "sanei_scsi_cmd completed with: compcode = %x, status = %x\n", + compcode, status); + + switch (compcode) + { + case SDI_ASW: /* All seems well */ + return SANE_STATUS_GOOD; + case SDI_CKSTAT: + DBG (2, "Sense Data:\n"); + for (i = 0; i < sizeof (sense_buffer); i++) + DBG (2, "%.2X ", sense_buffer[i]); + DBG (2, "\n"); + if (fd_info[fd].sense_handler) + return (*fd_info[fd].sense_handler) (fd, sense_buffer, + fd_info[fd]. + sense_handler_arg); + /* fall through */ + default: + return SANE_STATUS_IO_ERROR; + } + } + } +#endif /* USE == SCO_UW71_INTERFACE */ + +#if USE == OS2_INTERFACE + +#define WE_HAVE_FIND_DEVICES + + static int + get_devicename (int bus, int target, int lun, char *name, size_t name_len) + { + snprintf (name, name_len, "b%dt%dl%d", bus, target, lun); + DBG (1, "OS/2 searched device is %s\n", name); + return 0; + } + + void + sanei_scsi_find_devices (const char *findvendor, const char *findmodel, + const char *findtype, + int findbus, int findchannel, int findid, + int findlun, + SANE_Status (*attach) (const char *dev)) + { + size_t findvendor_len = 0, findmodel_len = 0, findtype_len = 0; + char vendor[32], model[32], type[32], revision[32]; + int bus, channel, id, lun, number, i; + char line[256], dev_name[128]; + const char *string; + FILE *proc_fp; + char *end; + struct + { + const char *name; + size_t name_len; + int is_int; /* integer valued? (not a string) */ + union + { + void *v; /* avoids compiler warnings... */ + char *str; + int *i; + } + u; + } + param[] = + { + { + "Vendor:", 7, 0, + { + 0} + } + , + { + "Model:", 6, 0, + { + 0} + } + , + { + "Type:", 5, 0, + { + 0} + } + , + { + "Rev:", 4, 0, + { + 0} + } + , + { + "scsi", 4, 1, + { + 0} + } + , + { + "Channel:", 8, 1, + { + 0} + } + , + { + "Id:", 3, 1, + { + 0} + } + , + { + "Lun:", 4, 1, + { + 0} + } + }; + + param[0].u.str = vendor; + param[1].u.str = model; + param[2].u.str = type; + param[3].u.str = revision; + param[4].u.i = &bus; + param[5].u.i = &channel; + param[6].u.i = &id; + param[7].u.i = &lun; + + DBG_INIT (); + + open_aspi (); /* open aspi manager if not already done */ + + DBG (2, "find_devices: open temporary file '%s'\n", tmpAspi); + proc_fp = fopen (tmpAspi, "r"); + if (!proc_fp) + { + DBG (1, "could not open %s for reading\n", tmpAspi); + return; + } + + number = bus = channel = id = lun = -1; + + vendor[0] = model[0] = type[0] = '\0'; + if (findvendor) + findvendor_len = strlen (findvendor); + if (findmodel) + findmodel_len = strlen (findmodel); + if (findtype) + findtype_len = strlen (findtype); + + while (!feof (proc_fp)) + { + if (!fgets (line, sizeof (line), proc_fp)) + break; /* at eof exit */ + + string = sanei_config_skip_whitespace (line); + + while (*string) + { + for (i = 0; i < NELEMS (param); ++i) + { + if (strncmp (string, param[i].name, param[i].name_len) == 0) + { + string += param[i].name_len; + string = sanei_config_skip_whitespace (string); + if (param[i].is_int) + { + *param[i].u.i = strtol (string, &end, 10); + string = (char *) end; + } + else + { + strncpy (param[i].u.str, string, 32); + param[i].u.str[31] = '\0'; + while (*string && !isspace ((int) *string)) + ++string; + } + string = sanei_config_skip_whitespace (string); + + if (param[i].u.v == &bus) + ++number; + break; + } + } + if (i >= NELEMS (param)) + ++string; /* no match */ + } + + if ((findvendor && !vendor[0]) || (findmodel && !model[0]) + || (findtype && !type[0]) + || (findbus >= 0 && bus == -1) || (findchannel >= 0 + && channel == -1) + || (findlun >= 0 && lun == -1)) + /* some info is still missing */ + continue; + + if ((!findvendor || strncmp (vendor, findvendor, findvendor_len) == 0) + && (!findmodel || strncmp (model, findmodel, findmodel_len) == 0) + && (!findtype || strncmp (type, findtype, findtype_len) == 0) + && (findbus == -1 || bus == findbus) + && (findchannel == -1 || channel == findchannel) + && (findid == -1 || id == findid) + && (findlun == -1 || lun == findlun) + && get_devicename (bus, id, lun, dev_name, sizeof (dev_name)) >= 0 + && (*attach) (dev_name) != SANE_STATUS_GOOD) + return; + + vendor[0] = model[0] = type[0] = 0; + bus = channel = id = lun = -1; + } + + DBG (2, "find_devices: close temporary file '%s'\n", tmpAspi); + fclose (proc_fp); + + close_aspi (); /* close aspi manager */ + } + +/* XXX untested code! */ + SANE_Status + sanei_scsi_cmd2 (int fd, + const void *cmd, size_t cmd_size, + const void *src, size_t src_size, + void *dst, size_t * dst_size) + { + ULONG rc; /* Returns. */ + unsigned long cbreturn; + unsigned long cbParam; + if (aspi_buf == NULL) /* avoid SIGSEGV in memcpy() when calling + sanei_scsi_cmd2() while aspi-driver is closed */ + { + DBG (1, "sanei_scsi_cmd: Error no device (aspi_buf == NULL)\n"); + return SANE_STATUS_INVAL; + } + + if (PSRBlock == NULL) /* avoid SIGSEGV in memcpy() when calling + sanei_scsi_cmd2() while aspi-driver is closed */ + { + DBG (1, "sanei_scsi_cmd: Error no device (PSRBlock == NULL)\n"); + return SANE_STATUS_INVAL; + } + + memset (PSRBlock, 0, sizeof (SRB)); /* Okay, I'm paranoid. */ + PSRBlock->cmd = SRB_Command; /* execute SCSI cmd */ + PSRBlock->ha_num = fd_info[fd].bus; /* host adapter number */ + PSRBlock->u.cmd.target = fd_info[fd].target; /* Target SCSI ID */ + PSRBlock->u.cmd.lun = fd_info[fd].lun; /* Target SCSI LUN */ + PSRBlock->flags = SRB_Post; /* posting enabled */ + if (dst_size && *dst_size) + { + /* Reading. */ + assert (*dst_size <= (size_t) sanei_scsi_max_request_size); + PSRBlock->u.cmd.data_len = *dst_size; + DBG (1, "sanei_scsi_cmd: Reading PSRBlock->u.cmd.data_len= %lu\n", + PSRBlock->u.cmd.data_len); + PSRBlock->flags |= SRB_Read; + } + else + { + /* Writing. */ + PSRBlock->u.cmd.data_len = src_size; + DBG (1, "sanei_scsi_cmd: Writing PSRBlock->u.cmd.data_len= %lu\n", + PSRBlock->u.cmd.data_len); + assert (PSRBlock->u.cmd.data_len <= + (unsigned long) sanei_scsi_max_request_size); + if (PSRBlock->u.cmd.data_len) + PSRBlock->flags |= SRB_Write; + else + PSRBlock->flags |= SRB_NoTransfer; + memcpy (aspi_buf, src, PSRBlock->u.cmd.data_len); + } + PSRBlock->u.cmd.sense_len = 32; /* length of sense buffer */ + PSRBlock->u.cmd.data_ptr = NULL; /* pointer to data buffer already registered */ + PSRBlock->u.cmd.link_ptr = NULL; /* pointer to next SRB */ + PSRBlock->u.cmd.cdb_len = cmd_size; /* SCSI command length */ + memcpy (PSRBlock->u.cmd.cdb_st, cmd, cmd_size); + + /* Do the command. */ + rc = DosDevIOCtl (driver_handle, 0x92, 0x02, + (void *) PSRBlock, sizeof (SRB), &cbParam, + (void *) PSRBlock, sizeof (SRB), &cbreturn); + + if (rc) + { + DBG (1, "sanei_scsi_cmd: DosDevIOCtl failed. rc= %lu \n", rc); + return SANE_STATUS_IO_ERROR; + } + + /* Get sense data if available. */ + if ((PSRBlock->status == SRB_Aborted || PSRBlock->status == SRB_Error) && + PSRBlock->u.cmd.target_status == SRB_CheckStatus + && fd_info[fd].sense_handler != 0) + { + SANEI_SCSI_Sense_Handler s_handler = fd_info[fd].sense_handler; + return (*s_handler) (fd, &PSRBlock->u.cmd.cdb_st[cmd_size], + fd_info[fd].sense_handler_arg); + } + if (PSRBlock->status != SRB_Done || + PSRBlock->u.cmd.ha_status != SRB_NoError || + PSRBlock->u.cmd.target_status != SRB_NoStatus) + { + DBG (1, "sanei_scsi_cmd: command 0x%02x failed.\n" + "PSRBlock->status= 0x%02x\n" + "PSRBlock->u.chm.ha_status= 0x%02x\n" + "PSRBlock->u.cmd.target_status= 0x%02x\n", + PSRBlock->u.cmd.cdb_st[0], + PSRBlock->status, + PSRBlock->u.cmd.ha_status, PSRBlock->u.cmd.target_status); + return SANE_STATUS_IO_ERROR; + } + + if (dst_size && *dst_size) /* Reading? */ + memcpy ((char *) dst, aspi_buf, *dst_size); + return SANE_STATUS_GOOD; + } +#endif /* USE == OS2_INTERFACE */ + +#if USE == STUBBED_INTERFACE + SANE_Status + sanei_scsi_cmd2 (int fd, + const void *cmd, size_t cmd_size, + const void *src, size_t src_size, + void *dst, size_t * dst_size) + { + return SANE_STATUS_UNSUPPORTED; + } +#endif /* USE == STUBBED_INTERFACE */ + +#if USE == IRIX_INTERFACE + +#define WE_HAVE_FIND_DEVICES + + SANE_Status + sanei_scsi_cmd2 (int fd, + const void *cmd, size_t cmd_size, + const void *src, size_t src_size, + void *dst, size_t * dst_size) + { + dsreq_t scsi_req; /* SCSI request */ +/* xxx obsolete size_t cdb_size; *//* Size of SCSI command */ + static u_char *cmdbuf = NULL, /* Command buffer */ + *sensebuf = NULL, /* Request sense buffer */ + *databuf = NULL; /* Data buffer */ + + /* + * Allocate the sense and command data buffers as necessary; we have + * to do this to avoid buffer alignment problems, since some + * hardware requires these buffers to be 32-bit aligned. + */ + if (cmdbuf == NULL) + { + cmdbuf = malloc (64); + sensebuf = malloc (1024); /* may be can reduced to 128 */ + databuf = malloc (MAX_DATA); + + if (cmdbuf == NULL || sensebuf == NULL || databuf == NULL) + return SANE_STATUS_NO_MEM; + } + + /* + * Build the SCSI request... + */ + /* xxx obsolete cdb_size = CDB_SIZE (*(u_char *) src); + */ + + DBG (1, "sanei_scsi_cmd: cmd_size = %d\n", cmd_size); + + if (dst != NULL) + { + /* + * SCSI command returning/reading data... + */ + scsi_req.ds_flags = DSRQ_READ | DSRQ_SENSE; + scsi_req.ds_time = 120 * 1000; + scsi_req.ds_cmdbuf = (caddr_t) cmdbuf; + scsi_req.ds_cmdlen = cmd_size; + scsi_req.ds_databuf = (caddr_t) databuf; + scsi_req.ds_datalen = *dst_size; + scsi_req.ds_sensebuf = (caddr_t) sensebuf; + scsi_req.ds_senselen = 128; /* 1024 does not work, 128 is tested (O.Rauch) */ + + /* + * Copy command to cmdbuf to assure 32-bit alignment. + */ + memcpy (cmdbuf, cmd, cmd_size); + } + else + { + /* + * SCSI command sending/writing data... + */ + scsi_req.ds_flags = DSRQ_WRITE | DSRQ_SENSE; + scsi_req.ds_time = 120 * 1000; + scsi_req.ds_cmdbuf = (caddr_t) cmdbuf; + scsi_req.ds_cmdlen = cmd_size; + scsi_req.ds_databuf = (caddr_t) databuf; + scsi_req.ds_datalen = src_size; + scsi_req.ds_sensebuf = (caddr_t) sensebuf; + scsi_req.ds_senselen = 128; + + /* + * Copy command and data to local buffers to ensure 32-bit alignment... + */ + memcpy (cmdbuf, (u_char *) cmd, cmd_size); + memcpy (databuf, (u_char *) src, src_size); + } + + bzero (sensebuf, 128); + + /* + * Do SCSI request... + */ + if (ioctl (fd, DS_ENTER, &scsi_req) < 0) + { + DBG (1, "sanei_scsi_cmd: ioctl failed - %s\n", strerror (errno)); + return SANE_STATUS_IO_ERROR; + } + + DBG (1, "sanei_scsi_cmd: status = %d\n", scsi_req.ds_status); + + /* + * Set the incoming data size and copy the destination data as needed... + */ + if (dst != NULL) + { + *dst_size = scsi_req.ds_datasent; + + DBG (1, "sanei_scsi_cmd: read %d bytes\n", scsi_req.ds_datasent); + + if (scsi_req.ds_datasent > 0) + memcpy (dst, databuf, scsi_req.ds_datasent); + } + + /* + * Return the appropriate status code... + */ + if (scsi_req.ds_status != 0) + { + if (scsi_req.ds_status == STA_BUSY) + return SANE_STATUS_DEVICE_BUSY; + else if (fd_info[fd].sense_handler) + return (*fd_info[fd].sense_handler) (fd, sensebuf, + fd_info[fd].sense_handler_arg); + else + return SANE_STATUS_IO_ERROR; + } + return SANE_STATUS_GOOD; + } + + void + sanei_scsi_find_devices (const char *findvendor, const char *findmodel, + const char *findtype, + int findbus, int findchannel, int findid, + int findlun, + SANE_Status (*attach) (const char *dev)) + { + size_t findvendor_len = 0, findmodel_len = 0; + /* Lengths of search strings */ + inventory_t *inv; /* Current hardware inventory entry */ + int bus, id, lun; /* Current Bus, ID, and LUN */ + char dev_name[128]; /* SCSI device name */ + int fd; /* SCSI file */ + size_t inqsize; /* Size of returned inquiry data */ + char vendor[9], /* Vendor name */ + model[17]; /* Model/product name */ + u_char inqdata[128], /* Inquiry data buffer */ + inqcommand[6]; /* Inquiry command (0x12) buffer */ + + DBG_INIT (); + + vendor[0] = model[0] = '\0'; + if (findvendor) + findvendor_len = strlen (findvendor); + if (findmodel) + findmodel_len = strlen (findmodel); + + if (findvendor != NULL) + DBG (1, "sanei_scsi_find_devices: looking for vendors starting " + "with \"%s\".\n", findvendor); + + if (findmodel != NULL) + DBG (1, "sanei_scsi_find_devices: looking for models starting " + "with \"%s\".\n", findmodel); + + setinvent (); + + while ((inv = getinvent ()) != NULL) + { + if (inv->inv_class != INV_SCSI || + (inv->inv_type != INV_SCANNER && inv->inv_type != INV_CPU)) + continue; + + bus = inv->inv_controller; + id = inv->inv_unit; + lun = inv->inv_state >> 8; + + DBG (1, "sanei_scsi_find_devices: found %s on controller %d, " + "ID %d, LUN %d.\n", + inv->inv_type == INV_SCANNER ? "scanner" : "processor", + bus, id, lun); + + if ((findbus >= 0 && bus != findbus) || + (findid >= 0 && id != findid) || (findlun >= 0 && lun != findlun)) + { + DBG (1, "sanei_scsi_find_devices: ignoring this device.\n"); + continue; + } + + sprintf (dev_name, "/dev/scsi/sc%dd%dl%d", bus, id, lun); + DBG (1, "sanei_scsi_find_devices: device name is \"%s\".\n", + dev_name); + + /* + * Open the SCSI device and get the inquiry data... + */ + + if (sanei_scsi_open (dev_name, &fd, NULL, NULL) != SANE_STATUS_GOOD) + { + DBG (1, + "sanei_scsi_find_devices: unable to open device file - %s.\n", + strerror (errno)); + continue; + } + + DBG (1, "sanei_scsi_find_devices: device fd = %d.\n", fd); + + inqsize = sizeof (inqdata); + + inqcommand[0] = 0x12; + inqcommand[1] = 0; + inqcommand[2] = 0; + inqcommand[3] = sizeof (inqdata) >> 8; + inqcommand[4] = sizeof (inqdata); + inqcommand[5] = 0; + + if (sanei_scsi_cmd (fd, inqcommand, sizeof (inqcommand), inqdata, + &inqsize) != SANE_STATUS_GOOD) + { + DBG (1, + "sanei_scsi_find_devices: unable to get inquiry data - %s.\n", + strerror (errno)); + continue; + } + + sanei_scsi_close (fd); + + strncpy (vendor, (char *) inqdata + 8, 8); + vendor[8] = '\0'; + strncpy (model, (char *) inqdata + 16, 16); + model[16] = '\0'; + + DBG (1, "sanei_scsi_find_devices: vendor = \'%s\', model = \'%s'.\n", + vendor, model); + + /* + * Compare as necessary... + */ + + if ((findvendor != NULL + && strncmp (findvendor, vendor, findvendor_len)) + || (findmodel != NULL + && strncmp (findmodel, model, findmodel_len))) + { + DBG (1, "sanei_scsi_find_devices: ignoring this device.\n"); + continue; + } + + /* + * OK, this one matches, so use it! + */ + + DBG (1, "sanei_scsi_find_devices: attaching this device.\n"); + + (*attach) (dev_name); + } + } +#endif /* USE == IRIX_INTERFACE */ + +#if USE == AIX_GSC_INTERFACE + SANE_Status + sanei_scsi_cmd2 (int fd, + const void *cmd, size_t cmd_size, + const void *src, size_t src_size, + void *dst, size_t * dst_size) + { + scmd_t scmd; + /* xxx obsolete size_t cdb_size; + */ + char sense_buf[32]; + char status; + + /* xxx obsolete cdb_size = CDB_SIZE (*(u_char *) src); + */ + + memset (&scmd, 0, sizeof (scmd)); + if (dst_size && *dst_size) + { + /* xxx obsolete assert (cdb_size == src_size); + */ + scmd.rw = 1; + scmd.data_buf = dst; + scmd.datalen = *dst_size; + } + else + { + /* assert (cdb_size <= src_size); + */ + scmd.data_buf = (char *) src; + scmd.datalen = src_size; + } + scmd.cdb = (char *) cmd; + scmd.cdblen = cmd_size; + scmd.timeval = sane_scsicmd_timeout; + scmd.sense_buf = sense_buf; + scmd.senselen = sizeof (sense_buf); + scmd.statusp = &status; + DBG (1, "sanei_scsi_cmd: scmd.rw = %d, scmd.cdblen = %d, ", + scmd.rw, scmd.cdblen); + DBG (1, "scmd.cdb = {0x%x,0x%x,0x%x,0x%x,0x%x,0x%x, ...}\n", + scmd.cdb[0], scmd.cdb[1], scmd.cdb[2], + scmd.cdb[3], scmd.cdb[4], scmd.cdb[5]); + if (ioctl (fd, GSC_CMD, &scmd) < 0) + { + DBG (1, "sanei_scsi_cmd: ioctl(SIOC_IO) failed: %s\n", + strerror (errno)); + return SANE_STATUS_IO_ERROR; + } + if (*scmd.statusp) + DBG (1, "sanei_scsi_cmd: SCSI completed with status=%d\n", + *scmd.statusp); + + DBG (1, "sanei_scsi_cmd: dst = {0x%x,0x%x,0x%x,0x%x,0x%x,0x%x, ...}\n", + *((char *) dst + 0), *((char *) dst + 1), *((char *) dst + 2), + *((char *) dst + 3), *((char *) dst + 4), *((char *) dst + 5)); + + if (dst_size) + *dst_size = scmd.datalen; + + if (scmd.senselen > 0 + && (scmd.sense_buf[0] & 0x80) && fd_info[fd].sense_handler) + return (*fd_info[fd].sense_handler) (fd, (u_char *) scmd.sense_buf, + fd_info[fd].sense_handler_arg); + return SANE_STATUS_GOOD; + } +#endif /* USE == AIX_GSC_INTERFACE */ + +#if USE == SOLARIS_SG_INTERFACE + +#ifndef CCS_SENSE_LEN +# define CCS_SENSE_LEN 18 +#endif + + SANE_Status + sanei_scsi_cmd2 (int fd, + const void *cmd, size_t cmd_size, + const void *src, size_t src_size, + void *dst, size_t * dst_size) + { + struct user_scsi us; + /* xxx obsolete size_t cdb_size; + */ + char sensebf[CCS_SENSE_LEN]; + + /* xxx obsolete cdb_size = CDB_SIZE (*(u_char *) src); + */ + + /* first put the user scsi structure together. */ + memset (&us, 0, sizeof (us)); + us.us_cdbp = (caddr_t) cmd; + us.us_cdblen = cmd_size; + us.us_sensep = sensebf; + us.us_senselen = CCS_SENSE_LEN; + if (dst && dst_size && *dst_size) + { + us.us_bufp = (caddr_t) dst; + us.us_buflen = *dst_size; + us.us_flags = USER_SCSI_READ; + } + else + { + us.us_bufp = (caddr_t) src; + us.us_buflen = src_size; + us.us_flags = USER_SCSI_WRITE; + } + /* now run it */ + if (ioctl (fd, USER_SCSI, &us) < 0) + return SANE_STATUS_IO_ERROR; + if (dst_size) + *dst_size -= us.us_resid; + + return SANE_STATUS_GOOD; + } +#endif /* USE == SOLARIS_SG_INTERFACE */ + +#if USE == SOLARIS_INTERFACE + +#ifndef SC_NOT_READ +# define SC_NOT_READY 0x02 +#endif + +#ifndef SC_BUSY +# define SC_BUSY 0x08 +#endif +#define DEF_TIMEOUT sane_scsicmd_timeout; + +/* Choosing one of the following DEF_SCG_FLG's SCG_DISRE_ENA allows + the SCSI driver to disconnect/reconnect. SCG_CMD_RETRY allows a + retry if a retryable error occurs. + + Disallowing SCG_DISRE_ENA slows down the operation of the SCSI bus + while the scanner is working. If you have severe problems try to + set it to 0. + + SCG_CMD_RETRY allows the driver to retry some commands. It should + normally be set. For some kinds of odd problems, it may cause the + machine to hang for some time. */ + +#define DEF_SCG_FLG SCG_DISRE_ENA +/* #define DEF_SCG_FLG 0 */ +/* #define DEF_SCG_FLG SCG_DISRE_ENA | SCG_CMD_RETRY */ +/* #define DEF_SCG_FLG SCG_CMD_RETRY */ + + static int d_errs = 100; + + static SANE_Status + scsi_cmd (int fd, + const void *cmd, size_t cmd_size, + const void *src, size_t src_size, + void *dst, size_t * dst_size, int probing) + { + struct scg_cmd scmd; + /* xxx obsolete size_t cdb_size; + */ + SANEI_SCSI_Sense_Handler handler; + + /* xxx obsolete cdb_size = CDB_SIZE (*(u_char *) src); + */ + + memset (&scmd, 0, sizeof (scmd)); + scmd.flags = DEF_SCG_FLG | (probing ? SCG_SILENT : 0); + if (dst && dst_size && *dst_size) + { + /* xxx obsolete assert (cdb_size == src_size); + */ + scmd.flags |= SCG_RECV_DATA; + scmd.addr = dst; + scmd.size = *dst_size; + } + else + { + /* xxx obsolete assert (cdb_size <= src_size); + */ + scmd.addr = (caddr_t) src; + scmd.size = src_size; + } + scmd.cdb_len = cmd_size; + scmd.sense_len = CCS_SENSE_LEN; + scmd.target = fd_info[fd].target; + /* use 2 second timeout when probing, 60 seconds otherwise: */ + scmd.timeout = probing ? 2 : DEF_TIMEOUT; + memcpy (&scmd.cdb.g0_cdb.cmd, cmd, cmd_size); + scmd.cdb.cmd_cdb[1] |= fd_info[fd].lun << 5; + if (ioctl (fd, SCGIO_CMD, &scmd) < 0) + return SANE_STATUS_IO_ERROR; + if (dst_size) + *dst_size = scmd.size - scmd.resid; + if (scmd.error == 0 && scmd.errno == 0 && *(u_char *) & scmd.scb == 0) + return SANE_STATUS_GOOD; + + if (scmd.error == SCG_TIMEOUT) + DBG (0, "sanei_scsi_cmd %x: timeout\n", scmd.cdb.g0_cdb.cmd); + else if (probing) + { + struct scsi_ext_sense *ext_sense = + (struct scsi_ext_sense *) &scmd.sense; + + if (scmd.error < SCG_FATAL + && ((scmd.sense.code < 0x70 && scmd.sense.code != 0x04) + || (scmd.sense.code >= 0x70 + && ext_sense->key != SC_NOT_READY))) + return SANE_STATUS_GOOD; + } + else + { + char errbf[128]; + int i, rv, lifes; + + handler = fd_info[fd].sense_handler; + DBG (3, "cmd=%x, error=%d:%s, bsiz=%d, stat=%x,%x,%x, slen=%d\n", + scmd.cdb.g0_cdb.cmd, scmd.error, strerror (scmd.errno), + ((dst_size != NULL) ? (*dst_size) : 0), scmd.u_scb.cmd_scb[0], + scmd.u_scb.cmd_scb[1], scmd.u_scb.cmd_scb[2], scmd.sense_count); + *errbf = '\0'; + for (i = 0; i < scmd.sense_count; i++) + sprintf (errbf + strlen (errbf), "%x,", scmd.u_sense.cmd_sense[i]); + DBG (3, "sense=%s\n", errbf); + + /* test_unit_ready on a busy unit returns error = 0 or 2 with + errno=EIO. I've seen 0 on a CDrom without a CD, and 2 on a + scanner just busy. + + If (SANE_DEBUG_SANEI_SCSI > 100) lifes = + SANE_DEBUG_SANEI_SCSI - 100 use up one life for every + scmd.error abort and dump core when no lifes left + test_unit_ready commands are not counted. */ + if (scmd.error) + { + if (sanei_debug_sanei_scsi > 100 && + scmd.cdb.g0_cdb.cmd != SC_TEST_UNIT_READY) + { + lifes = sanei_debug_sanei_scsi - ++d_errs; + DBG (1, "sanei_scsi_cmd: %d lifes left\n", lifes); + assert (lifes > 0); + } + return SANE_STATUS_IO_ERROR; + } + if (scmd.u_scb.cmd_scb[0] == SC_BUSY) + return SANE_STATUS_DEVICE_BUSY; + if (*(u_char *) & scmd.sense && handler) + { + rv = (*handler) (fd, scmd.u_sense.cmd_sense, + fd_info[fd].sense_handler_arg); + DBG (2, "sanei_scsi_cmd: sense-handler returns %d\n", rv); + return rv; + } + } + return SANE_STATUS_IO_ERROR; + } + + SANE_Status + sanei_scsi_cmd2 (int fd, + const void *cmd, size_t cmd_size, + const void *src, size_t src_size, + void *dst, size_t * dst_size) + { + return scsi_cmd (fd, cmd, cmd_size, src, src_size, dst, dst_size, 0); + } + + static int unit_ready (int fd) + { + static const u_char test_unit_ready[] = { 0, 0, 0, 0, 0, 0 }; + int status; + + status = scsi_cmd (fd, test_unit_ready, sizeof (test_unit_ready), + 0, 0, 0, 0, 1); + return (status == SANE_STATUS_GOOD); + } + +#endif /* USE == SOLARIS_INTERFACE */ + + +#if USE == SOLARIS_USCSI_INTERFACE + +#define DEF_TIMEOUT sane_scsicmd_timeout; + + static int d_errs = 100; + typedef struct scsi_extended_sense extended_sense_t; + typedef struct scsi_inquiry scsi_inquiry_t; + + static SANE_Status + scsi_cmd (int fd, + const void *cmd, size_t cmd_size, + const void *src, size_t src_size, + void *dst, size_t * dst_size, int probing) + { + struct uscsi_cmd us; + scsi_inquiry_t inquiry, *iq = &inquiry; + extended_sense_t sense, *sp = &sense; + SANEI_SCSI_Sense_Handler handler; + + memset (&us, 0, sizeof (us)); + memset (sp, 0, sizeof (*sp)); + + us.uscsi_flags = USCSI_SILENT | USCSI_RQENABLE | USCSI_DIAGNOSE; + us.uscsi_timeout = probing ? 2 : DEF_TIMEOUT; + us.uscsi_rqbuf = (caddr_t) sp; /* sense data address */ + us.uscsi_rqlen = sizeof (extended_sense_t); /* length of sense data */ + + if (dst && dst_size && *dst_size) + { + us.uscsi_flags |= USCSI_READ; + us.uscsi_bufaddr = (caddr_t) dst; + us.uscsi_buflen = *dst_size; + } + else + { + us.uscsi_flags |= USCSI_WRITE; + us.uscsi_bufaddr = (caddr_t) src; + us.uscsi_buflen = src_size; + } + + us.uscsi_cdblen = cmd_size; + us.uscsi_cdb = (caddr_t) cmd; + + if (ioctl (fd, USCSICMD, &us) < 0) + return SANE_STATUS_IO_ERROR; + + if (dst_size) + *dst_size = us.uscsi_buflen - us.uscsi_resid; + + if ((us.uscsi_status & STATUS_MASK) == STATUS_GOOD) + return SANE_STATUS_GOOD; + + if (sp->es_key == SUN_KEY_TIMEOUT) + DBG (0, "sanei_scsi_cmd %x: timeout\n", *(char *) cmd); + else + { + char errbf[128]; + int i, rv, lifes; + + handler = fd_info[fd].sense_handler; + DBG (3, "cmd=%x, scsi_status=%x\n", *(char *) cmd, us.uscsi_status); + *errbf = '\0'; + + for (i = 0; i < us.uscsi_rqlen; i++) + sprintf (errbf + strlen (errbf), "%x,", *(sp + i)); + + DBG (3, "sense=%s\n", errbf); + +#if 0 + if (us.error) + { + if (sanei_debug_sanei_scsi > 100 && + scmd.cdb.g0_cdb.cmd != SC_TEST_UNIT_READY) + { + lifes = sanei_debug_sanei_scsi - ++d_errs; + DBG (1, "sanei_scsi_cmd: %d lifes left\n", lifes); + assert (lifes > 0); + } + return SANE_STATUS_IO_ERROR; + } + + if (scmd.u_scb.cmd_scb[0] == SC_BUSY) + return SANE_STATUS_DEVICE_BUSY; +#endif + + if (handler) + { + rv = (*handler) (fd, (unsigned char *) sp, + fd_info[fd].sense_handler_arg); + DBG (2, "sanei_scsi_cmd: sense-handler returns %d\n", rv); + return rv; + } + } + + return SANE_STATUS_IO_ERROR; + } + + SANE_Status + sanei_scsi_cmd2 (int fd, + const void *cmd, size_t cmd_size, + const void *src, size_t src_size, + void *dst, size_t * dst_size) + { + return scsi_cmd (fd, cmd, cmd_size, src, src_size, dst, dst_size, 0); + } + + static int unit_ready (int fd) + { + static const u_char test_unit_ready[] = { 0, 0, 0, 0, 0, 0 }; + int status; + + status = scsi_cmd (fd, test_unit_ready, sizeof (test_unit_ready), + 0, 0, 0, 0, 1); + return (status == SANE_STATUS_GOOD); + } +#endif /* USE == SOLARIS_USCSI_INTERFACE */ + +#if USE == WIN32_INTERFACE + +SANE_Status +sanei_scsi_cmd2 (int fd, + const void *cmd, size_t cmd_size, + const void *src, size_t src_size, + void *dst, size_t * dst_size) +{ + struct pkt { + SCSI_PASS_THROUGH_DIRECT sptd; + unsigned char sense[255]; + } pkt; + DWORD BytesReturned; + BOOL ret; + + memset(&pkt, 0, sizeof( pkt )); + pkt.sptd.Length = sizeof( SCSI_PASS_THROUGH_DIRECT ); + + pkt.sptd.PathId = fd_info[fd].bus; + pkt.sptd.TargetId = fd_info[fd].target; + pkt.sptd.Lun = fd_info[fd].lun; + + assert(cmd_size == 6 || cmd_size == 10 || cmd_size == 12 || cmd_size == 16); + memcpy(pkt.sptd.Cdb, cmd, cmd_size); + pkt.sptd.CdbLength = cmd_size; + + if (dst_size && *dst_size) + { + pkt.sptd.DataIn = SCSI_IOCTL_DATA_IN; + pkt.sptd.DataTransferLength = *dst_size; + pkt.sptd.DataBuffer = dst; + } + else if (src_size) + { + pkt.sptd.DataIn = SCSI_IOCTL_DATA_OUT; + pkt.sptd.DataTransferLength = src_size; + pkt.sptd.DataBuffer = src; + } + else { + pkt.sptd.DataIn = SCSI_IOCTL_DATA_UNSPECIFIED; + } + + pkt.sptd.TimeOutValue = sane_scsicmd_timeout; + + pkt.sptd.SenseInfoOffset = (void *)pkt.sense - (void *)&pkt; + pkt.sptd.SenseInfoLength = sizeof(pkt.sense); + + ret = DeviceIoControl(fd, + IOCTL_SCSI_PASS_THROUGH_DIRECT, + &pkt.sptd, sizeof( pkt ), + &pkt.sptd, sizeof( pkt ), + &BytesReturned, NULL ); + + if (ret == 0) + { + DBG (1, "sanei_scsi_cmd2: DeviceIoControl() failed: %ld\n", + GetLastError()); + return SANE_STATUS_IO_ERROR; + } + + if (pkt.sptd.ScsiStatus == 2){ + /* Check condition. */ + SANEI_SCSI_Sense_Handler handler; + + handler = fd_info[fd].sense_handler; + if (handler) { + return handler(fd, pkt.sense, fd_info[fd].sense_handler_arg); + } + else { + return SANE_STATUS_IO_ERROR; + } + } + else if (pkt.sptd.ScsiStatus != 0) { + DBG (1, "sanei_scsi_cmd2: ScsiStatus is %d\n", + pkt.sptd.ScsiStatus); + return SANE_STATUS_IO_ERROR; + } + + if (dst_size) { + *dst_size = pkt.sptd.DataTransferLength; + } + + return SANE_STATUS_GOOD; +} + +#define WE_HAVE_FIND_DEVICES + +/* This is almost the same algorithm used in sane-find-scanner. */ +void +sanei_scsi_find_devices (const char *findvendor, const char *findmodel, + const char *findtype, + int findbus, int findchannel, int findid, int findlun, + SANE_Status (*attach) (const char *dev)) +{ + int hca; + HANDLE fd; + char scsi_hca_name[20]; + char buffer[4096]; + DWORD BytesReturned; + BOOL ret; + PSCSI_ADAPTER_BUS_INFO adapter; + PSCSI_INQUIRY_DATA inquiry; + int i; + + DBG_INIT(); + + hca = 0; + + for(hca = 0; ; hca++) { + + /* Open the adapter */ + snprintf(scsi_hca_name, 20, "\\\\.\\Scsi%d:", hca); + fd = CreateFile(scsi_hca_name, GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, OPEN_EXISTING, + FILE_FLAG_RANDOM_ACCESS, NULL ); + + if (fd == INVALID_HANDLE_VALUE) { + /* Assume there is no more adapter. This is wrong in the case + * of hot-plug stuff, but I have yet to see it on a user + * machine. */ + break; + } + + /* Get the inquiry info for the devices on that hca. */ + ret = DeviceIoControl(fd, + IOCTL_SCSI_GET_INQUIRY_DATA, + NULL, + 0, + buffer, + sizeof(buffer), + &BytesReturned, + FALSE); + + if(ret == 0) + { + CloseHandle(fd); + continue; + } + + adapter = (PSCSI_ADAPTER_BUS_INFO)buffer; + + for(i = 0; i < adapter->NumberOfBuses; i++) { + + if (adapter->BusData[i].InquiryDataOffset == 0) { + /* No device here */ + continue; + } + + inquiry = (PSCSI_INQUIRY_DATA) (buffer + + adapter->BusData[i].InquiryDataOffset); + + while(1) { + + if ((findvendor == NULL || strncmp(findvendor, (char *)&inquiry->InquiryData[8], 8) == 0)) { + DBG(1, "OK1\n"); + } else { + DBG(1, "failed for [%s] and [%s]\n",findvendor, (char *)&inquiry->InquiryData[8] ); + } + + + /* Check if this device fits the criteria. */ + if ((findvendor == NULL || strncmp(findvendor, (char *)&inquiry->InquiryData[8], strlen(findvendor)) == 0) && + (findmodel == NULL || strncmp(findmodel, (char *)&inquiry->InquiryData[16], strlen(findmodel)) == 0) && + (findbus == -1 || findbus == hca) && + (findchannel == -1 || findchannel == inquiry->PathId) && + (findid == -1 || findid == inquiry->TargetId) && + (findlun == -1 || findlun == inquiry->Lun)) { + + char device_name[20]; + sprintf(device_name, "h%db%dt%dl%d", hca, inquiry->PathId, inquiry->TargetId, inquiry->Lun); + attach(device_name); + } + if (inquiry->NextInquiryDataOffset == 0) { + /* No device here */ + break; + } else { + inquiry = (PSCSI_INQUIRY_DATA) (buffer + + inquiry->NextInquiryDataOffset); + } + } + } + CloseHandle(fd); + + } +} +#endif /* USE == WIN32_INTERFACE */ + +#if USE == MACOSX_INTERFACE + +# ifdef HAVE_IOKIT_CDB_IOSCSILIB_H + + static SANE_Status + sanei_scsi_cmd2_old_api (int fd, + const void *cmd, size_t cmd_size, + const void *src, size_t src_size, + void *dst, size_t * dst_size) + { + mach_port_t masterPort; + IOReturn ioReturnValue; + io_object_t scsiDevice; + int i; + CFMutableDictionaryRef scsiMatchDictionary; + int deviceTypeNumber; + CFNumberRef deviceTypeRef; + io_iterator_t scsiObjectIterator; + io_object_t device; + CFNumberRef IOUnitRef; + int iounit; + CFNumberRef scsiTargetRef; + int scsitarget; + CFNumberRef scsiLunRef; + int scsilun; + IOCFPlugInInterface **plugInInterface; + SInt32 score; + HRESULT plugInResult; + IOSCSIDeviceInterface **scsiDeviceInterface; + IOCDBCommandInterface **cdbCommandInterface; + CDBInfo cdb; + IOVirtualRange range; + UInt32 transferCount; + Boolean isWrite; + SCSIResults results; + UInt32 seqNumber; + + masterPort = 0; + ioReturnValue = IOMasterPort (MACH_PORT_NULL, &masterPort); + if (ioReturnValue != kIOReturnSuccess || masterPort == 0) + { + DBG (5, "Could not get I/O master port (0x%08x)\n", ioReturnValue); + return SANE_STATUS_IO_ERROR; + } + + scsiDevice = 0; + for (i = 0; !scsiDevice && i < 2; i++) + { + scsiMatchDictionary = IOServiceMatching (kIOSCSIDeviceClassName); + if (scsiMatchDictionary == NULL) + { + DBG (5, "Could not create SCSI matching dictionary\n"); + return SANE_STATUS_NO_MEM; + } + + deviceTypeNumber = + (i == 0 ? kSCSIDevTypeScanner : kSCSIDevTypeProcessor); + deviceTypeRef = CFNumberCreate (NULL, kCFNumberIntType, + &deviceTypeNumber); + CFDictionarySetValue (scsiMatchDictionary, + CFSTR (kSCSIPropertyDeviceTypeID), + deviceTypeRef); + CFRelease (deviceTypeRef); + + scsiObjectIterator = 0; + ioReturnValue = IOServiceGetMatchingServices (masterPort, + scsiMatchDictionary, + &scsiObjectIterator); + if (ioReturnValue != kIOReturnSuccess) + { + DBG (5, "Could not match services (0x%08x)\n", ioReturnValue); + return SANE_STATUS_NO_MEM; + } + + while ((device = IOIteratorNext (scsiObjectIterator))) + { + IOUnitRef = + IORegistryEntryCreateCFProperty (device, + CFSTR (kSCSIPropertyIOUnit), + NULL, 0); + CFNumberGetValue (IOUnitRef, kCFNumberIntType, &iounit); + CFRelease (IOUnitRef); + scsiTargetRef = + IORegistryEntryCreateCFProperty (device, + CFSTR (kSCSIPropertyTarget), + NULL, 0); + CFNumberGetValue (scsiTargetRef, kCFNumberIntType, &scsitarget); + CFRelease (scsiTargetRef); + scsiLunRef = + IORegistryEntryCreateCFProperty (device, + CFSTR (kSCSIPropertyLun), + NULL, 0); + CFNumberGetValue (scsiLunRef, kCFNumberIntType, &scsilun); + CFRelease (scsiLunRef); + + if (fd_info[fd].bus == iounit && + fd_info[fd].target == scsitarget && + fd_info[fd].lun == scsilun) + scsiDevice = device; + else + IOObjectRelease (device); + } + IOObjectRelease (scsiObjectIterator); + } + if (!scsiDevice) + { + DBG (5, "Device not found (unit %i, target %i, lun %i)\n", + fd_info[fd].bus, fd_info[fd].target, fd_info[fd].lun); + return SANE_STATUS_INVAL; + } + + plugInInterface = NULL; + score = 0; + ioReturnValue = IOCreatePlugInInterfaceForService (scsiDevice, + kIOSCSIUserClientTypeID, + kIOCFPlugInInterfaceID, + &plugInInterface, + &score); + if (ioReturnValue != kIOReturnSuccess || plugInInterface == NULL) + { + DBG (5, "Error creating plugin interface (0x%08x)\n", ioReturnValue); + return SANE_STATUS_NO_MEM; + } + + scsiDeviceInterface = NULL; + plugInResult = (*plugInInterface)-> + QueryInterface (plugInInterface, + CFUUIDGetUUIDBytes (kIOSCSIDeviceInterfaceID), + (LPVOID) & scsiDeviceInterface); + if (plugInResult != S_OK || scsiDeviceInterface == NULL) + { + DBG (5, "Couldn't create SCSI device interface (%ld)\n", plugInResult); + return SANE_STATUS_NO_MEM; + } + + (*plugInInterface)->Release (plugInInterface); + IOObjectRelease (scsiDevice); + + ioReturnValue = (*scsiDeviceInterface)->open (scsiDeviceInterface); + if (ioReturnValue != kIOReturnSuccess) + { + DBG (5, "Error opening SCSI interface (0x%08x)\n", ioReturnValue); + return SANE_STATUS_IO_ERROR; + } + + cdbCommandInterface = NULL; + plugInResult = (*scsiDeviceInterface)-> + QueryInterface (scsiDeviceInterface, + CFUUIDGetUUIDBytes (kIOCDBCommandInterfaceID), + (LPVOID) & cdbCommandInterface); + if (plugInResult != S_OK || cdbCommandInterface == NULL) + { + DBG (5, "Error creating CDB interface (%ld)\n", plugInResult); + return SANE_STATUS_NO_MEM; + } + + cdb.cdbLength = cmd_size; + memcpy (&cdb.cdb, cmd, cmd_size); + if (dst && dst_size) + { + bzero (dst, *dst_size); + range.address = (IOVirtualAddress) dst; + range.length = *dst_size; + transferCount = *dst_size; + isWrite = false; + } + else + { + range.address = (IOVirtualAddress) src; + range.length = src_size; + transferCount = src_size; + isWrite = true; + } + + seqNumber = 0; + ioReturnValue = (*cdbCommandInterface)-> + setAndExecuteCommand (cdbCommandInterface, &cdb, transferCount, + &range, 1, isWrite, sane_scsicmd_timeout * 1000, + 0, 0, 0, &seqNumber); + if (ioReturnValue != kIOReturnSuccess && + ioReturnValue != kIOReturnUnderrun) + { + DBG (5, "Error executing CDB command (0x%08x)\n", ioReturnValue); + return SANE_STATUS_IO_ERROR; + } + + ioReturnValue = (*cdbCommandInterface)->getResults (cdbCommandInterface, + &results); + if (ioReturnValue != kIOReturnSuccess && + ioReturnValue != kIOReturnUnderrun) + { + DBG (5, "Error getting results from CDB Interface (0x%08x)\n", + ioReturnValue); + return SANE_STATUS_IO_ERROR; + } + + if (dst && dst_size) + *dst_size = results.bytesTransferred; + + (*cdbCommandInterface)->Release (cdbCommandInterface); + (*scsiDeviceInterface)->close (scsiDeviceInterface); + (*scsiDeviceInterface)->Release (scsiDeviceInterface); + + return SANE_STATUS_GOOD; + } + + + static void + sanei_scsi_find_devices_old_api (const char *findvendor, + const char *findmodel, + const char *findtype, int findbus, + int findchannel, int findid, int findlun, + SANE_Status (*attach) (const char *dev)) + { + mach_port_t masterPort; + IOReturn ioReturnValue; + int i; + CFMutableDictionaryRef scsiMatchDictionary; + int deviceTypeNumber; + CFNumberRef deviceTypeRef; + io_iterator_t scsiObjectIterator; + io_object_t scsiDevice; + CFNumberRef IOUnitRef; + int iounit; + CFNumberRef scsiTargetRef; + int scsitarget; + CFNumberRef scsiLunRef; + int scsilun; + IOCFPlugInInterface **plugInInterface; + SInt32 score; + HRESULT plugInResult; + IOSCSIDeviceInterface **scsiDeviceInterface; + SCSIInquiry inquiry; + UInt32 inquirySize; + char devname[16]; + + masterPort = 0; + ioReturnValue = IOMasterPort (MACH_PORT_NULL, &masterPort); + if (ioReturnValue != kIOReturnSuccess || masterPort == 0) + { + DBG (5, "Could not get I/O master port (0x%08x)\n", ioReturnValue); + return; + } + + for (i = 0; i < 2; i++) + { + scsiMatchDictionary = IOServiceMatching (kIOSCSIDeviceClassName); + if (scsiMatchDictionary == NULL) + { + DBG (5, "Could not create SCSI matching dictionary\n"); + return; + } + deviceTypeNumber = + (i == 0 ? kSCSIDevTypeScanner : kSCSIDevTypeProcessor); + deviceTypeRef = CFNumberCreate (NULL, kCFNumberIntType, + &deviceTypeNumber); + CFDictionarySetValue (scsiMatchDictionary, + CFSTR (kSCSIPropertyDeviceTypeID), + deviceTypeRef); + CFRelease (deviceTypeRef); + + scsiObjectIterator = 0; + ioReturnValue = IOServiceGetMatchingServices (masterPort, + scsiMatchDictionary, + &scsiObjectIterator); + if (ioReturnValue != kIOReturnSuccess) + { + DBG (5, "Could not match services (0x%08x)\n", ioReturnValue); + return; + } + + while ((scsiDevice = IOIteratorNext (scsiObjectIterator))) + { + IOUnitRef = + IORegistryEntryCreateCFProperty (scsiDevice, + CFSTR (kSCSIPropertyIOUnit), + NULL, 0); + CFNumberGetValue (IOUnitRef, kCFNumberIntType, &iounit); + CFRelease (IOUnitRef); + scsiTargetRef = + IORegistryEntryCreateCFProperty (scsiDevice, + CFSTR (kSCSIPropertyTarget), + NULL, 0); + CFNumberGetValue (scsiTargetRef, kCFNumberIntType, &scsitarget); + CFRelease (scsiTargetRef); + scsiLunRef = + IORegistryEntryCreateCFProperty (scsiDevice, + CFSTR (kSCSIPropertyLun), + NULL, 0); + CFNumberGetValue (scsiLunRef, kCFNumberIntType, &scsilun); + CFRelease (scsiLunRef); + + plugInInterface = NULL; + score = 0; + ioReturnValue = + IOCreatePlugInInterfaceForService (scsiDevice, + kIOSCSIUserClientTypeID, + kIOCFPlugInInterfaceID, + &plugInInterface, &score); + if (ioReturnValue != kIOReturnSuccess || plugInInterface == NULL) + { + DBG (5, "Error creating plugin interface (0x%08x)\n", + ioReturnValue); + return; + } + + scsiDeviceInterface = NULL; + plugInResult = (*plugInInterface)-> + QueryInterface (plugInInterface, + CFUUIDGetUUIDBytes (kIOSCSIDeviceInterfaceID), + (LPVOID) & scsiDeviceInterface); + if (plugInResult != S_OK || scsiDeviceInterface == NULL) + { + DBG (5, "Couldn't create SCSI device interface (%ld)\n", + plugInResult); + return; + } + + (*plugInInterface)->Release (plugInInterface); + IOObjectRelease (scsiDevice); + + ioReturnValue = (*scsiDeviceInterface)-> + getInquiryData (scsiDeviceInterface, &inquiry, + sizeof (SCSIInquiry), &inquirySize); + + (*scsiDeviceInterface)->Release (scsiDeviceInterface); + + if ((findlun < 0 || findlun == scsilun) && + (findvendor == NULL || strncmp (findvendor, + inquiry.vendorName, + strlen (findvendor)) == 0) && + (findmodel == NULL || strncmp (findmodel, + inquiry.productName, + strlen (findmodel)) == 0)) + { + sprintf (devname, "u%dt%dl%d", iounit, scsitarget, scsilun); + (*attach) (devname); + } + } + IOObjectRelease (scsiObjectIterator); + } + } + +# endif /* ifdef HAVE_IOKIT_CDB_IOSCSILIB_H */ + +# if defined (HAVE_IOKIT_SCSI_SCSICOMMANDOPERATIONCODES_H) || \ + defined (HAVE_IOKIT_SCSI_COMMANDS_SCSICOMMANDOPERATIONCODES_H) + + static + void CreateMatchingDictionaryForSTUC (SInt32 peripheralDeviceType, + const char *findvendor, + const char *findmodel, + const CFDataRef scsiguid, + CFMutableDictionaryRef * matchingDict) + { + CFMutableDictionaryRef subDict; + CFNumberRef deviceTypeRef; + CFStringRef str; + + /* Create the dictionaries */ + *matchingDict = + CFDictionaryCreateMutable (kCFAllocatorDefault, 0, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + if (*matchingDict == NULL) + { + return; + } + + subDict = + CFDictionaryCreateMutable (kCFAllocatorDefault, 0, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + if (subDict == NULL) + { + CFRelease (*matchingDict); + *matchingDict = NULL; + return; + } + + /* Create a dictionary with the "SCSITaskDeviceCategory" key with the + appropriate value for the device type we're interested in.*/ + + CFDictionarySetValue (subDict, + CFSTR (kIOPropertySCSITaskDeviceCategory), + CFSTR (kIOPropertySCSITaskUserClientDevice)); + + deviceTypeRef = CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType, + &peripheralDeviceType); + CFDictionarySetValue (subDict, + CFSTR (kIOPropertySCSIPeripheralDeviceType), + deviceTypeRef); + CFRelease (deviceTypeRef); + + /* Add search for a vendor or model */ + + if (findvendor) + { + str = CFStringCreateWithCString (kCFAllocatorDefault, findvendor, + kCFStringEncodingUTF8); + CFDictionarySetValue (subDict, + CFSTR (kIOPropertySCSIVendorIdentification), + str); + CFRelease (str); + } + if (findmodel) + { + str = CFStringCreateWithCString (kCFAllocatorDefault, findmodel, + kCFStringEncodingUTF8); + CFDictionarySetValue (subDict, + CFSTR (kIOPropertySCSIProductIdentification), + str); + CFRelease (str); + } + if (scsiguid) + { + CFDictionarySetValue (subDict, + CFSTR + (kIOPropertySCSITaskUserClientInstanceGUID), + scsiguid); + } + + /* Add the dictionary to the main dictionary with the key "IOPropertyMatch" + to narrow the search to the above dictionary. */ + + CFDictionarySetValue (*matchingDict, CFSTR (kIOPropertyMatchKey), subDict); + CFRelease (subDict); + } + + static + void CreateDeviceInterfaceUsingSTUC (io_object_t scsiDevice, + IOCFPlugInInterface *** + thePlugInInterface, + SCSITaskDeviceInterface *** + theInterface) + { + IOReturn ioReturnValue; + IOCFPlugInInterface **plugInInterface = NULL; + SInt32 score = 0; + HRESULT plugInResult; + SCSITaskDeviceInterface **interface = NULL; + + /* Create the base interface of type IOCFPlugInInterface. + This object will be used to create the SCSI device interface object. */ + + ioReturnValue = + IOCreatePlugInInterfaceForService (scsiDevice, + kIOSCSITaskDeviceUserClientTypeID, + kIOCFPlugInInterfaceID, + &plugInInterface, &score); + if (ioReturnValue != kIOReturnSuccess) + { + DBG (5, "Error creating plugin interface (0x%08x)\n", ioReturnValue); + return; + } + + /* Query the base plugin interface for an instance of the specific + SCSI device interface object. */ + + plugInResult = + (*plugInInterface)->QueryInterface (plugInInterface, + CFUUIDGetUUIDBytes + (kIOSCSITaskDeviceInterfaceID), + (LPVOID) & interface); + if (plugInResult != S_OK) + { + DBG (5, "Couldn't create SCSI device interface (%ld)\n", + (long) plugInResult); + return; + } + + /* Set the return values. */ + + *thePlugInInterface = plugInInterface; + *theInterface = interface; + } + + static SANE_Status + ExecuteSCSITask (SCSITaskInterface ** task, + const void *cmd, size_t cmd_size, + const void *src, size_t src_size, + void *dst, size_t * dst_size) + { + SCSITaskStatus taskStatus; + SCSI_Sense_Data senseData; + SCSICommandDescriptorBlock cdb; + IOReturn ioReturnValue; +#ifdef HAVE_SCSITASKSGELEMENT + SCSITaskSGElement range; +#else + IOVirtualRange range; +#endif + UInt64 transferCount = 0; + UInt64 data_length = 0; + UInt8 transferType = 0; + + if (dst && dst_size) /* isRead */ + { + DBG (6, "isRead dst_size:%ld\n", *dst_size); + + /* Zero the buffer. */ + bzero (dst, *dst_size); + + /* Configure the virtual range for the buffer. */ + range.address = (long) dst; + range.length = *dst_size; + + data_length = *dst_size; + transferType = kSCSIDataTransfer_FromTargetToInitiator; + } + else + { + DBG (6, "isWrite src_size:%ld\n", src_size); + + /* Configure the virtual range for the buffer. */ + range.address = (long) src; + range.length = src_size; + + data_length = src_size; + transferType = kSCSIDataTransfer_FromInitiatorToTarget; + } + + + /* zero the senseData and CDB */ + bzero (&senseData, sizeof (senseData)); + bzero (cdb, sizeof (cdb)); + + /* copy the command data */ + memcpy (cdb, cmd, cmd_size); + + /* Set the actual cdb in the task */ + ioReturnValue = (*task)->SetCommandDescriptorBlock (task, cdb, cmd_size); + if (ioReturnValue != kIOReturnSuccess) + { + DBG (5, "Error setting CDB (0x%08x)\n", ioReturnValue); + return SANE_STATUS_IO_ERROR; + } + + /* Set the scatter-gather entry in the task */ + ioReturnValue = (*task)->SetScatterGatherEntries (task, &range, 1, + data_length, + transferType); + if (ioReturnValue != kIOReturnSuccess) + { + DBG (5, "Error setting scatter-gather entries (0x%08x)\n", + ioReturnValue); + return SANE_STATUS_IO_ERROR; + } + + /* Set the timeout in the task */ + ioReturnValue = (*task)->SetTimeoutDuration (task, + sane_scsicmd_timeout * 1000); + if (ioReturnValue != kIOReturnSuccess) + { + DBG (5, "Error setting timeout (0x%08x)\n", ioReturnValue); + return SANE_STATUS_IO_ERROR; + } + + DBG (5, "Executing command\n"); + + /* Send it! */ + ioReturnValue = (*task)->ExecuteTaskSync (task, &senseData, &taskStatus, + &transferCount); + if (ioReturnValue != kIOReturnSuccess) + { + DBG (5, "Error executing task (0x%08x)\n", ioReturnValue); + return SANE_STATUS_IO_ERROR; + } + + DBG (5, "ExecuteTaskSync OK Transferred %lld bytes\n", transferCount); + + if (taskStatus != kSCSITaskStatus_GOOD) + { + DBG (5, "taskStatus = 0x%08x\n", taskStatus); + return SANE_STATUS_IO_ERROR; + } + + /* Task worked correctly */ + if (dst && dst_size) + *dst_size = transferCount; + + return SANE_STATUS_GOOD; + } + + static SANE_Status + ExecuteCommandUsingSTUC (SCSITaskDeviceInterface ** interface, + const void *cmd, size_t cmd_size, + const void *src, size_t src_size, + void *dst, size_t * dst_size) + { + SCSITaskInterface **task; + IOReturn ioReturnValue; + SANE_Status returnValue; + + /* Get exclusive access for the device if we can. This must be done + before any SCSITasks can be created and sent to the device. */ + ioReturnValue = (*interface)->ObtainExclusiveAccess (interface); + + if (ioReturnValue != kIOReturnSuccess) + { + DBG (5, "ObtainExclusiveAccess failed (0x%08x)\n", ioReturnValue); + return SANE_STATUS_NO_MEM; + } + + /* Create a task now that we have exclusive access */ + task = (*interface)->CreateSCSITask (interface); + + if (task == NULL) + { + DBG (5, "CreateSCSITask returned NULL\n"); + (*interface)->ReleaseExclusiveAccess (interface); + return SANE_STATUS_NO_MEM; + } + + returnValue = ExecuteSCSITask (task, cmd, cmd_size, + src, src_size, dst, dst_size); + + /* Release the task interface */ + (*task)->Release (task); + + /* Release exclusive access */ + (*interface)->ReleaseExclusiveAccess (interface); + + return returnValue; + } + + static SANE_Status + sanei_scsi_cmd2_stuc_api (int fd, + const void *cmd, size_t cmd_size, + const void *src, size_t src_size, + void *dst, size_t * dst_size) + { + CFDataRef guid; + mach_port_t masterPort; + int i; + io_object_t scsiDevice; + SInt32 peripheralDeviceType; + CFMutableDictionaryRef matchingDict; + io_iterator_t iokIterator; + IOReturn ioReturnValue; + IOCFPlugInInterface **plugInInterface = NULL; + SCSITaskDeviceInterface **interface = NULL; + io_object_t nextDevice; + SANE_Status returnValue; + + guid = fd_info[fd].pdata; + if (!guid) + { + DBG (5, "No GUID\n"); + return SANE_STATUS_INVAL; + } + + DBG (2, "cmd2: cmd_size:%ld src_size:%ld dst_size:%ld isWrite:%d\n", + cmd_size, src_size, (!dst_size) ? 0 : *dst_size, (!dst_size) ? 1 : 0); + + /* Use default master port */ + masterPort = 0; + ioReturnValue = IOMasterPort (MACH_PORT_NULL, &masterPort); + if (ioReturnValue != kIOReturnSuccess || masterPort == 0) + { + DBG (5, "Could not get I/O master port (0x%08x)\n", ioReturnValue); + return SANE_STATUS_IO_ERROR; + } + + /* Search for both Scanner type and Processor type devices */ + /* GB TDB This should only be needed for find */ + scsiDevice = 0; + for (i = 0; !scsiDevice && i < 2; i++) + { + peripheralDeviceType = + (i == 0 ? kINQUIRY_PERIPHERAL_TYPE_ScannerSCSI2Device : + kINQUIRY_PERIPHERAL_TYPE_ProcessorSPCDevice); + + /* Set up a matching dictionary to search the I/O Registry for + the SCSI device */ + /* we are interested in, specifying the SCSITaskUserClient GUID. */ + matchingDict = NULL; + CreateMatchingDictionaryForSTUC (peripheralDeviceType, NULL, NULL, + guid, &matchingDict); + if (matchingDict == NULL) + { + DBG (5, "CreateMatchingDictionaryForSTUC Failed\n"); + return SANE_STATUS_NO_MEM; + } + + /* Now search I/O Registry for the matching device */ + iokIterator = 0; + ioReturnValue = + IOServiceGetMatchingServices (masterPort, matchingDict, + &iokIterator); + if (ioReturnValue != kIOReturnSuccess) + { + DBG (5, "IOServiceGetMatchingServices Failed\n"); + return SANE_STATUS_NO_MEM; + } + + scsiDevice = IOIteratorNext (iokIterator); + + while ((nextDevice = IOIteratorNext (iokIterator))) + { + IOObjectRelease (nextDevice); + } + + IOObjectRelease (iokIterator); + } + + if (!scsiDevice) + { + DBG (5, "Device not found\n"); + return SANE_STATUS_INVAL; + } + + /* Found Device */ + /* Create interface */ + + CreateDeviceInterfaceUsingSTUC (scsiDevice, &plugInInterface, &interface); + + /* Done with SCSI object from I/O Registry. */ + ioReturnValue = IOObjectRelease (scsiDevice); + + returnValue = SANE_STATUS_IO_ERROR; + + if (ioReturnValue != kIOReturnSuccess) + { + DBG (5, "Error releasing SCSI device. (0x%08x)\n", ioReturnValue); + } + else if (interface != NULL) + { + /* Execute the command */ + returnValue = + ExecuteCommandUsingSTUC (interface, cmd, cmd_size, src, src_size, + dst, dst_size); + } + + if (interface != NULL) + { + (*interface)->Release (interface); + } + + if (plugInInterface != NULL) + { + IODestroyPlugInInterface (plugInInterface); + } + + return returnValue; + } + + static void + sanei_scsi_find_devices_stuc_api (const char *findvendor, + const char *findmodel, + const char *findtype, int findbus, + int findchannel, int findid, int findlun, + SANE_Status (*attach) (const char *dev)) + { + mach_port_t masterPort; + IOReturn ioReturnValue; + int i; + SInt32 peripheralDeviceType; + CFMutableDictionaryRef matchingDict; + io_iterator_t iokIterator; + io_object_t scsiDevice; + CFDataRef GUIDRef; + char *devname; + int len; + const unsigned char *p; + CFDictionaryRef protocolCharacteristics; + CFNumberRef scsiLunRef; + int scsilun; + + masterPort = 0; + ioReturnValue = IOMasterPort (MACH_PORT_NULL, &masterPort); + if (ioReturnValue != kIOReturnSuccess || masterPort == 0) + return; + + DBG (5, "Search for Vendor: %s Model: %s\n", + (findvendor) ? findvendor : "(none)", + (findmodel) ? findmodel : "(none)"); + + /* Search for both Scanner type and Processor type devices */ + + for (i = 0; i < 2; i++) + { + peripheralDeviceType = + (i == 0 ? kINQUIRY_PERIPHERAL_TYPE_ScannerSCSI2Device : + kINQUIRY_PERIPHERAL_TYPE_ProcessorSPCDevice); + + /* Set up a matching dictionary to search the I/O Registry for SCSI + devices we are interested in. */ + + matchingDict = NULL; + CreateMatchingDictionaryForSTUC (peripheralDeviceType, findvendor, + findmodel, NULL, &matchingDict); + if (matchingDict == NULL) + { + DBG (5, "CreateMatchingDictionaryForSTUC Failed\n"); + return; + } + + /* Now search I/O Registry for matching devices. */ + + iokIterator = 0; + ioReturnValue = + IOServiceGetMatchingServices (masterPort, matchingDict, + &iokIterator); + if (ioReturnValue != kIOReturnSuccess) + { + DBG (5, "IOServiceGetMatchingServices Failed\n"); + return; + } + + /* Check devices */ + + while ((scsiDevice = IOIteratorNext (iokIterator))) + { + scsilun = 0; + protocolCharacteristics = IORegistryEntryCreateCFProperty + (scsiDevice, CFSTR ("Protocol Characteristics"), NULL, 0); + if (protocolCharacteristics) + { + scsiLunRef = CFDictionaryGetValue + (protocolCharacteristics, + CFSTR ("SCSI Logical Unit Number")); + if (scsiLunRef) + CFNumberGetValue (scsiLunRef, kCFNumberIntType, &scsilun); + CFRelease (protocolCharacteristics); + } + + if (findlun < 0 || findlun == scsilun) + { + /* Create device name from the SCSITaskUserClient GUID */ + + GUIDRef = IORegistryEntryCreateCFProperty + (scsiDevice, + CFSTR (kIOPropertySCSITaskUserClientInstanceGUID), + NULL, 0); + + if (GUIDRef) + { + len = CFDataGetLength (GUIDRef); + p = CFDataGetBytePtr (GUIDRef); + + devname = (char *) malloc (2 * len + 3); + devname [0] = '<'; + for (i = 0; i < len; i++) + sprintf (&devname [2 * i + 1], "%02x", p [i]); + devname [2 * len + 1] = '>'; + devname [2 * len + 2] = '\0'; + + CFRelease (GUIDRef); + + DBG (1, "Found: %s\n", devname); + + /* Attach to the device */ + (*attach) (devname); + free (devname); + } + else + DBG (1, "Can't find SCSITaskUserClient GUID\n"); + } + } + IOObjectRelease (iokIterator); + } + } + +# endif /* HAVE_IOKIT_SCSI_COMMANDS_SCSICOMMANDOPERATIONCODES_H */ + + SANE_Status + sanei_scsi_cmd2 (int fd, + const void *cmd, size_t cmd_size, + const void *src, size_t src_size, + void *dst, size_t * dst_size) + { + if (fd_info[fd].pdata) +# if defined (HAVE_IOKIT_SCSI_SCSICOMMANDOPERATIONCODES_H) || \ + defined (HAVE_IOKIT_SCSI_COMMANDS_SCSICOMMANDOPERATIONCODES_H) + return sanei_scsi_cmd2_stuc_api (fd, cmd, cmd_size, src, src_size, + dst, dst_size); +# else + return SANE_STATUS_INVAL; +# endif + else +# ifdef HAVE_IOKIT_CDB_IOSCSILIB_H + return sanei_scsi_cmd2_old_api (fd, cmd, cmd_size, src, src_size, + dst, dst_size); +# else + return SANE_STATUS_INVAL; +# endif + } + + void + sanei_scsi_find_devices (const char *findvendor, const char *findmodel, + const char *findtype, + int findbus, int findchannel, int findid, + int findlun, + SANE_Status (*attach) (const char *dev)) + { +# if defined (HAVE_IOKIT_SCSI_SCSICOMMANDOPERATIONCODES_H) || \ + defined (HAVE_IOKIT_SCSI_COMMANDS_SCSICOMMANDOPERATIONCODES_H) + sanei_scsi_find_devices_stuc_api (findvendor, findmodel, findtype, + findbus, findchannel, findid, + findlun, attach); +# endif +# ifdef HAVE_IOKIT_CDB_IOSCSILIB_H + sanei_scsi_find_devices_old_api (findvendor, findmodel, findtype, + findbus, findchannel, findid, + findlun, attach); +# endif + } + +#define WE_HAVE_FIND_DEVICES + +#endif /* USE == MACOSX_INTERFACE */ + + +#ifndef WE_HAVE_ASYNC_SCSI + + SANE_Status + sanei_scsi_req_enter2 (int fd, const void *cmd, size_t cmd_size, + const void *src, size_t src_size, + void *dst, size_t * dst_size, void **idp) + { + return sanei_scsi_cmd2 (fd, cmd, cmd_size, src, src_size, dst, dst_size); + } + + SANE_Status sanei_scsi_req_wait (void *id) + { + return SANE_STATUS_GOOD; + } + + void sanei_scsi_req_flush_all (void) + { + } + + void sanei_scsi_req_flush_all_extended (int fd) + { + } + +#endif /* WE_HAVE_ASYNC_SCSI */ + + SANE_Status sanei_scsi_req_enter (int fd, + const void *src, size_t src_size, + void *dst, size_t * dst_size, void **idp) + { + size_t cmd_size = CDB_SIZE (*(const char *) src); + + if (dst_size && *dst_size) + assert (src_size == cmd_size); + else + assert (src_size >= cmd_size); + + return sanei_scsi_req_enter2 (fd, src, cmd_size, + (const char *) src + cmd_size, + src_size - cmd_size, dst, dst_size, idp); + } + + SANE_Status + sanei_scsi_cmd (int fd, const void *src, size_t src_size, + void *dst, size_t * dst_size) + { + size_t cmd_size = CDB_SIZE (*(const char *) src); + + if (dst_size && *dst_size) + assert (src_size == cmd_size); + else + assert (src_size >= cmd_size); + + return sanei_scsi_cmd2 (fd, src, cmd_size, + (const char *) src + cmd_size, + src_size - cmd_size, dst, dst_size); + } + + + +#ifndef WE_HAVE_FIND_DEVICES + + void + sanei_scsi_find_devices (const char *findvendor, const char *findmodel, + const char *findtype, + int findbus, int findchannel, int findid, + int findlun, + SANE_Status (*attach) (const char *dev)) + { + DBG_INIT (); + DBG (1, "sanei_scsi_find_devices: not implemented for this platform\n"); + } + +#endif /* WE_HAVE_FIND_DEVICES */ |