kopia lustrzana https://gitlab.com/sane-project/backends
6188 wiersze
163 KiB
C
6188 wiersze
163 KiB
C
/* 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<64>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 */
|