sane-project-backends/sanei/sanei_scsi.c

2753 wiersze
74 KiB
C

/* sane - Scanner Access Now Easy.
Copyright (C) 1996, 1997 David Mosberger-Tang
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 <lalloca.h> /* MUST come first for AIX! */
#endif
#include <sane/config.h>
#include <lalloca.h>
#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/param.h>
#include <sys/types.h>
#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
#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 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
#elif defined (HAVE_OS2_H)
# define USE OS2_INTERFACE
# define INCL_DOSFILEMGR
# define INCL_DOS
# define INCL_DOSDEVICES
# define INCL_DOSDEVIOCTL
# define INCL_DOSSEMAPHORES
# define INCL_DOSMEMMGR
# include <os2.h>
# include "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_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"
#endif
#ifndef USE
# define USE STUBBED_INTERFACE
#endif
#include <sane/sanei.h>
#include <sane/sanei_config.h>
#include <sane/sanei_scsi.h>
#define BACKEND_NAME sanei_scsi
#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
static int unit_ready (int fd);
#endif
#ifdef SG_BIG_BUFF
# define MAX_DATA SG_BIG_BUFF
#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
#ifndef MAX_DATA
# define MAX_DATA (32*1024)
#endif
int sanei_scsi_max_request_size = MAX_DATA;
#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 HEV postSema = 0; /* Event Semaphore for posting SRB completion */
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 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 openSemaReturn;
USHORT lockSegmentReturn;
unsigned long cbreturn;
unsigned long cbParam;
SRB SRBlock; /* SCSI Request Block */
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. */
}
rc = DosAllocMem (&aspi_buf, sanei_scsi_max_request_size,
OBJ_TILE | PAG_READ | PAG_WRITE | PAG_COMMIT);
if (rc)
{
DBG (1, "open_aspi: can't allocate memory\n");
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;
}
rc = DosCreateEventSem (NULL, &postSema, /* create event semaphore */
DC_SEM_SHARED, 0);
if (rc)
{
/* DosCreateEventSem failed */
DBG (1, "open_aspi: couldn't create semaphore.\n");
return 0;
}
rc = DosDevIOCtl (driver_handle, 0x92, 0x03, /* pass semaphore handle */
(void *) &postSema, sizeof (HEV), /* to driver */
&cbParam, (void *) &openSemaReturn,
sizeof (USHORT), &cbreturn);
if (rc || openSemaReturn)
{
DBG (1, "open_aspi: couldn't set semaphore.\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, "open_aspi: Can't lock buffer.\n");
return 0;
}
/* query number of installed adapters */
memset (&SRBlock, 0, sizeof (SRBlock));
SRBlock.cmd = SRB_Inquiry; /* host adapter inquiry */
SRBlock.ha_num = 0; /* host adapter number */
SRBlock.flags = 0; /* no flags set */
rc = DosDevIOCtl (driver_handle, 0x92, 0x02,
(void *) &SRBlock, sizeof (SRB), &cbParam,
(void *) &SRBlock, sizeof (SRB), &cbreturn);
num_adapters = SRBlock.u.inq.num_ha;
DBG (1, "OS/2: installed adapters %d\n", num_adapters);
DBG (1, "OS/2: ASPI manager is '%s'\n", SRBlock.u.inq.aspimgr_id);
DBG (1, "OS/2: host adapter is '%s'\n", SRBlock.u.inq.host_id);
DBG (1, "OS/2: unique id is '%s'\n", SRBlock.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 (&SRBlock, 0, sizeof (SRBlock));
SRBlock.cmd = SRB_Inquiry; /* host adapter inquiry */
SRBlock.ha_num = i; /* host adapter number */
SRBlock.flags = 0; /* no flags set */
rc = DosDevIOCtl (driver_handle, 0x92, 0x02,
(void *) &SRBlock, sizeof (SRB), &cbParam,
(void *) &SRBlock, sizeof (SRB), &cbreturn);
DBG (1, "OS/2: adapter#%02d '%s'\n", i, SRBlock.u.inq.host_id);
/* scan scsi chain (need 15 for wide?) */
for (id = 0; id < 7; id++)
{
unsigned char len;
unsigned char vendor[9];
unsigned char product[17];
unsigned char version[5];
unsigned char *pp;
memset (&SRBlock, 0, sizeof (SRBlock));
SRBlock.cmd = SRB_Device; /* get device type */
SRBlock.ha_num = i; /* host adapter number */
SRBlock.flags = 0; /* no flags set */
SRBlock.u.dev.target = id; /* target id */
SRBlock.u.dev.lun = 0; /* target LUN */
rc = DosDevIOCtl (driver_handle, 0x92, 0x02,
(void *) &SRBlock, sizeof (SRB), &cbParam,
(void *) &SRBlock, sizeof (SRB), &cbreturn);
DBG (1, "OS/2: id#%02d status=%02xh\n",
id, SRBlock.status);
/* skip if device not connected */
if (SRBlock.status == SRB_BadDevice)
continue;
DBG (1, "OS/2: type is '%s'\n",
SRBlock.u.dev.devtype < sizeof (devtypes) / sizeof (char *)?
devtypes[SRBlock.u.dev.devtype] : "unknown device");
/* query adapter string id */
memset (&SRBlock, 0, sizeof (SRBlock));
SRBlock.cmd = SRB_Command; /* execute SCSI command */
SRBlock.ha_num = i; /* host adapter number */
SRBlock.flags = SRB_Read | SRB_Post; /* data transfer, posting */
SRBlock.u.cmd.target = id; /* Target SCSI ID */
SRBlock.u.cmd.lun = 0; /* Target SCSI LUN */
SRBlock.u.cmd.data_len = 5; /* # of bytes transferred */
SRBlock.u.cmd.sense_len = 16; /* length of sense buffer */
SRBlock.u.cmd.data_ptr = NULL; /* pointer to data buffer */
SRBlock.u.cmd.link_ptr = NULL; /* pointer to next SRB */
SRBlock.u.cmd.cdb_len = 6; /* SCSI command length */
SRBlock.u.cmd.cdb_st[0] = INQUIRY; /* inquiry command */
SRBlock.u.cmd.cdb_st[1] = 0; /* ?? length */
SRBlock.u.cmd.cdb_st[2] = 0; /* transfer length MSB */
SRBlock.u.cmd.cdb_st[3] = 0; /* transfer length */
SRBlock.u.cmd.cdb_st[4] = 5; /* transfer length LSB */
SRBlock.u.cmd.cdb_st[5] = 0;
rc = DosDevIOCtl (driver_handle, 0x92, 0x02,
(void *) &SRBlock, sizeof (SRB), &cbParam,
(void *) &SRBlock, sizeof (SRB), &cbreturn);
DosWaitEventSem (postSema, -1);
DosResetEventSem (postSema, &rc);
len = ((char *) aspi_buf)[4]; /* additional length */
/* query id string */
memset (&SRBlock, 0, sizeof (SRBlock));
SRBlock.cmd = SRB_Command; /* execute SCSI command */
SRBlock.ha_num = i; /* host adapter number */
SRBlock.flags = SRB_Read | SRB_Post; /* data transfer, posting */
SRBlock.u.cmd.target = id; /* Target SCSI ID */
SRBlock.u.cmd.lun = 0; /* Target SCSI LUN */
SRBlock.u.cmd.data_len = 5 + len; /* # of bytes transferred */
SRBlock.u.cmd.sense_len = 16; /* length of sense buffer */
SRBlock.u.cmd.data_ptr = NULL; /* pointer to data buffer */
SRBlock.u.cmd.link_ptr = NULL; /* pointer to next SRB */
SRBlock.u.cmd.cdb_len = 6; /* SCSI command length */
SRBlock.u.cmd.cdb_st[0] = 0x12; /* inquiry command */
SRBlock.u.cmd.cdb_st[1] = 0; /* ?? length */
SRBlock.u.cmd.cdb_st[2] = 0; /* transfer length MSB */
SRBlock.u.cmd.cdb_st[3] = 0; /* transfer length */
SRBlock.u.cmd.cdb_st[4] = 5 + len; /* transfer length LSB */
SRBlock.u.cmd.cdb_st[5] = 0;
rc = DosDevIOCtl (driver_handle, 0x92, 0x02,
(void *) &SRBlock, sizeof (SRB), &cbParam,
(void *) &SRBlock, sizeof (SRB), &cbreturn);
DosWaitEventSem (postSema, -1);
DosResetEventSem (postSema, &rc);
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 (*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 (postSema)
DosCloseEventSem (postSema); /* Close event semaphore. */
postSema = 0;
if (driver_handle) /* Close driver. */
DosClose (driver_handle);
driver_handle = 0;
if (aspi_buf) /* Free buffer. */
DosFreeMem (aspi_buf);
aspi_buf = 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 */
SANE_Status
sanei_scsi_open (const char *dev, int *fdp,
SANEI_SCSI_Sense_Handler handler, void *handler_arg)
{
u_int bus = 0, target = 0, lun = 0, fake_fd = 0;
static int num_alloced = 0;
char *real_dev = 0;
void *pdata = 0;
int fd;
#ifdef __linux__
static int first_time = 1;
#endif
DBG_INIT ();
#if USE == LINUX_INTERFACE
if (first_time)
{
char buf[32];
size_t len;
int fd;
first_time = 0;
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);
DBG (1, "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%dt%dl%d", &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 a valid\n",
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;
}
#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: device %s not found\n",
strerror(errno));
return SANE_STATUS_INVAL;
}
}
#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 = open (dev, O_RDWR | O_EXCL);
if (fd < 0)
{
SANE_Status status = SANE_STATUS_INVAL;
if (errno == EACCES)
status = SANE_STATUS_ACCESS_DENIED;
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 = 10 * 60 * HZ; /* how about 10 minutes? ;-) */
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 */
#endif /* !DECUNIX_INTERFACE */
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
/* verify that the device really exists: */
if (!unit_ready (fd))
{
sanei_scsi_close (fd);
return SANE_STATUS_INVAL;
}
#endif
if (fdp)
*fdp = fd;
return SANE_STATUS_GOOD;
}
void
sanei_scsi_close (int fd)
{
fd_info[fd].in_use = 0;
fd_info[fd].sense_handler = 0;
fd_info[fd].sense_handler_arg = 0;
if (!fd_info[fd].fake_fd)
close (fd);
#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 == 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_enter (int fd, 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_enter: (fd=%x, src=%p, src_size=%x, "
"dst=%p, dst_size=%x, *idp=%p)\n",
fd, src, src_size, dst, *dst_size, idp);
else
DBG (1, "sanei_scsi_req_enter: (fd=%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 = CDB_SIZE (*(u_char *) src);
if (dst_size)
com->dst_size = *dst_size;
memcpy (&com->cdb, src, 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->cdb_size;
com->buf_size = src_size - com->cdb_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_cmd (int fd, const void *src, size_t src_size,
void *dst, size_t * dst_size)
{
SANE_Status status;
void *id;
DBG (1, "sanei_scsi_cmd: (fd=%d)\n", fd);
status = sanei_scsi_req_enter (fd, 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 struct req
{
struct req *next;
int fd;
u_int running:1, done:1;
SANE_Status status;
size_t *dst_len;
void *dst;
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;
}
*qhead, *qtail, *free_list;
static void
issue (struct req *req)
{
ssize_t nwritten;
if (!req || req->running)
return;
DBG (4, "sanei_scsi.issue: %p\n", req);
ATOMIC (req->running = 1;
nwritten = write (req->fd, &req->cdb, req->cdb.hdr.pack_len));
if (nwritten != req->cdb.hdr.pack_len)
{
DBG (1, "sanei_scsi.issue: bad write (errno=%s)\n",
strerror (errno));
req->done = 1;
if (errno == ENOMEM)
{
DBG (1, "sanei_scsi.issue: SG_BIG_BUF inconsistency? "
"Check file PROBLEMS.\n");
req->status = SANE_STATUS_NO_MEM;
}
else
req->status = SANE_STATUS_IO_ERROR;
}
}
void
sanei_scsi_req_flush_all (void)
{
struct req *req, *next_req;
for (req = qhead; req; req = next_req)
{
if (req->running && !req->done)
read (req->fd, &req->cdb, req->cdb.hdr.reply_len);
next_req = req->next;
req->next = free_list;
free_list = req;
}
qhead = qtail = 0;
}
SANE_Status
sanei_scsi_req_enter (int fd, const void *src, size_t src_size,
void *dst, size_t * dst_size, void **idp)
{
struct req *req;
size_t size;
if (free_list)
{
req = free_list;
free_list = req->next;
req->next = 0;
}
else
{
size = (sizeof (*req) - sizeof (req->cdb.data)
+ sanei_scsi_max_request_size);
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;
memset (&req->cdb.hdr, 0, sizeof (req->cdb.hdr));
req->cdb.hdr.pack_id = pack_id++;
req->cdb.hdr.pack_len = src_size + sizeof (req->cdb.hdr);
req->cdb.hdr.reply_len = (dst_size ? *dst_size : 0) + sizeof (req->cdb.hdr);
memcpy (&req->cdb.data, src, src_size);
req->next = 0;
ATOMIC (if (qtail)
{
qtail->next = req;
qtail = req;
}
else
qhead = qtail = req);
DBG (4, "scsi_req_enter: entered %p\n", req);
*idp = req;
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;
assert (req == qhead); /* we don't support out-of-order completion */
DBG (4, "sanei_scsi_req_wait: waiting for %p\n", req);
issue (req); /* ensure the command is running */
if (req->done)
{
issue (req->next); /* issue next command, if any */
status = req->status;
}
else
{
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->cdb, req->cdb.hdr.reply_len);
req->done = 1);
/* 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
{
nread -= sizeof (req->cdb.hdr);
/* check for errors, but let the sense_handler decide.... */
if ((req->cdb.hdr.result != 0) ||
((req->cdb.hdr.sense_buffer[0] & 0x7f) != 0))
{
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->cdb.hdr.result));
if (req->cdb.hdr.result == EBUSY)
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->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->cdb.data, nread);
if (req->dst_len)
*req->dst_len = nread;
}
}
}
/* dequeue and release processed request: */
ATOMIC (qhead = qhead->next;
if (!qhead)
qtail = 0;
req->next = free_list;
free_list = req);
return status;
}
SANE_Status
sanei_scsi_cmd (int fd, const void *src, size_t src_size,
void *dst, size_t * dst_size)
{
SANE_Status status;
void *id;
status = sanei_scsi_req_enter (fd, src, src_size, dst, dst_size, &id);
if (status != SANE_STATUS_GOOD)
return status;
return sanei_scsi_req_wait (id);
}
#define PROCFILE "/proc/scsi/scsi"
#define CSTRLEN(string_const) (sizeof (string_const) - 1)
static int
get_devicename (int devnum, char *name, size_t name_len)
{
int dev_fd, i;
static const struct
{
const char *prefix;
char base;
}
device_name_list[] =
{
{"/dev/sg", 'a'},
{"/dev/sg", '0'},
{"/dev/uk", '0'},
{"/dev/gsc", '0'},
};
for (i = 0; i < NELEMS (device_name_list); ++i)
{
snprintf (name, name_len, "%s%c", device_name_list[i].prefix,
device_name_list[i].base + devnum);
dev_fd = open (name, O_RDWR);
if (dev_fd >= 0)
{
close (dev_fd);
DBG (1, "searched device is %s\n", name);
return 0;
}
}
return -1;
}
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;
const 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, { vendor }},
{"Model:", 6, 0, { model }},
{"Type:", 5, 0, { type }},
{"Rev:", 4, 0, { revision }},
{"scsi", 4, 1, { &bus }},
{"Channel:", 8, 1, { &channel }},
{"Id:", 3, 1, { &id }},
{"Lun:", 4, 1, { &lun }}
};
DBG_INIT ();
proc_fp = fopen (PROCFILE, "r");
if (!proc_fp)
{
DBG (1, "could not open %s for reading\n", 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);
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;
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 (*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 (number, 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;
}
fclose (proc_fp);
}
#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_cmd (int fd, const void *src, size_t src_size,
void *dst, size_t * dst_size)
{
size_t cdb_size;
scsireq_t hdr;
int result;
cdb_size = CDB_SIZE (*(u_char *) src);
memset (&hdr, 0, sizeof (hdr));
memcpy (hdr.cmd, src, cdb_size);
if (dst_size && *dst_size)
{
assert (cdb_size == src_size);
hdr.flags = SCCMD_READ;
hdr.databuf = dst;
hdr.datalen = *dst_size;
}
else
{
assert (cdb_size <= src_size);
hdr.flags = SCCMD_WRITE;
hdr.databuf = (char *) src + cdb_size;
hdr.datalen = src_size;
}
hdr.timeout = 60000; /* 1 minute timeout */
hdr.cmdlen = cdb_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_cmd(int fd, const void *src, size_t src_size,
void *dst, size_t * dst_size) {
size_t cdb_size;
struct cam_device *dev;
union ccb *ccb;
int rv;
u_int32_t ccb_flags;
char* data_buf;
size_t data_len;
if (fd < 0 || fd > CAM_MAXDEVS || cam_devices[fd] == NULL) {
fprintf(stderr, "attempt to reference invalid unit %d\n", fd);
return SANE_STATUS_INVAL;
}
cdb_size = CDB_SIZE (*(u_char *) src);
dev = cam_devices[fd];
ccb = cam_getccb(dev);
/* Build the CCB */
bzero(&(&ccb->ccb_h)[1], sizeof(struct ccb_scsiio));
bcopy(src, &ccb->csio.cdb_io.cdb_bytes, cdb_size);
/*
* Set the data direction flags.
*/
if(dst_size && *dst_size) {
assert (cdb_size == src_size);
ccb_flags = CAM_DIR_IN;
data_buf = ((char*)(dst));
data_len = *dst_size;
}
else if(src_size > cdb_size) {
ccb_flags = CAM_DIR_OUT;
data_buf = ((char*)(src + cdb_size));
data_len = src_size - cdb_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 */ cdb_size,
/* timeout */ 60 * 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));
if((ccb->ccb_h.status & CAM_STATUS_MASK)
== SANE_STATUS_DEVICE_BUSY)
return SANE_STATUS_DEVICE_BUSY;
handler = fd_info[fd].sense_handler;
if (handler) {
SANE_Status st = (*handler) (fd, ((u_char*)(&ccb->csio.sense_data)),
fd_info[fd].sense_handler_arg);
cam_freeccb(ccb);
return st;
}
else
return SANE_STATUS_IO_ERROR;
}
cam_freeccb(ccb);
return SANE_STATUS_GOOD;
}
#endif
#if USE == HPUX_INTERFACE
/* XXX untested code! */
SANE_Status
sanei_scsi_cmd (int fd, const void *src, size_t src_size,
void *dst, size_t * dst_size)
{
struct sctl_io hdr;
size_t cdb_size;
cdb_size = CDB_SIZE (*(u_char *) src);
memset (&hdr, 0, sizeof (hdr));
memcpy (hdr.cdb, src, src_size);
if (dst_size && *dst_size)
{
assert (cdb_size == src_size);
hdr.flags = SCTL_READ;
hdr.data = dst;
hdr.data_length = *dst_size;
}
else
{
assert (cdb_size <= src_size);
hdr.data = (char *) src + cdb_size;
hdr.data_length = src_size - cdb_size;
}
hdr.cdb_length = cdb_size;
hdr.max_msecs = 60000; /* 1 minute timeout */
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_cmd (int fd, const void *src, size_t src_size,
void *dst, size_t * dst_size)
{
struct scsi_req hdr;
size_t cdb_size;
cdb_size = CDB_SIZE (*(u_char *) src);
memset (&hdr, 0, sizeof (hdr));
memcpy (&hdr.sr_cdb, src, cdb_size);
if (dst_size && *dst_size)
{
assert (cdb_size == src_size);
hdr.sr_dma_dir = SR_DMA_RD;
hdr.sr_addr = dst;
hdr.sr_dma_max = *dst_size;
}
else
{
assert (cdb_size <= src_size);
hdr.sr_dma_dir = SR_DMA_WR;
hdr.sr_addr = (char *) src + cdb_size;
hdr.sr_dma_max = src_size - cdb_size;
}
hdr.sr_ioto = 60; /* I/O timeout in seconds */
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_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_cmd (int fd, const void *src, size_t src_size,
void *dst, size_t * dst_size)
{
u_char sense[64];
UAGT_CAM_CCB hdr;
CCB_SCSIIO ccb;
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)
{
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
{
assert (cdb_size <= src_size);
if (cdb_size == 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 + cdb_size;
ccb.cam_dxfer_len = src_size - cdb_size;
}
ccb.cam_timeout = 60; /* set timeout in seconds */
ccb.cam_cdb_len = cdb_size;
memcpy (&ccb.cam_cdb_io.cam_cdb_bytes[0], src, cdb_size);
memset (&hdr, 0, sizeof (hdr));
hdr.uagt_ccb = (CCB_HEADER *) & 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_cmd (int fd, 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;
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;
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 - cdb_size;
sc->data_ptr = (char *) src + cdb_size;
sc->is_write = 1;
}
memcpy (sc->cdb, src, cdb_size);
sc->cdb_len = cdb_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 == 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;
const 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, { vendor }},
{"Model:", 6, 0, { model }},
{"Type:", 5, 0, { type }},
{"Rev:", 4, 0, { revision }},
{"scsi", 4, 1, { &bus }},
{"Channel:", 8, 1, { &channel }},
{"Id:", 3, 1, { &id }},
{"Lun:", 4, 1, { &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 (*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_cmd (int fd, const void *src, size_t src_size,
void *dst, size_t * dst_size)
{
ULONG rc; /* Returns. */
unsigned long cbreturn;
unsigned long cbParam;
SRB srb; /* SCSI Request Block */
ULONG count = 0; /* For semaphore. */
size_t cdb_size;
memset ((char *) &srb, 0, sizeof (srb)); /* Okay, I'm paranoid. */
cdb_size = CDB_SIZE (*(u_char *) src); /* Size of command block. */
srb.cmd = SRB_Command; /* execute SCSI cmd */
srb.ha_num = fd_info[fd].bus; /* host adapter number */
srb.u.cmd.target = fd_info[fd].target; /* Target SCSI ID */
srb.u.cmd.lun = fd_info[fd].lun; /* Target SCSI LUN */
srb.flags = SRB_Post; /* posting enabled */
if (dst_size && *dst_size)
{
/* Reading. */
assert (*dst_size <= sanei_scsi_max_request_size);
assert (cdb_size == src_size);
srb.u.cmd.data_len = *dst_size;
srb.flags |= SRB_Read;
}
else
{
/* Writing. */
srb.u.cmd.data_len = src_size - cdb_size;
assert (cdb_size <= src_size);
assert (srb.u.cmd.data_len <= sanei_scsi_max_request_size);
if (srb.u.cmd.data_len)
srb.flags |= SRB_Write;
else
srb.flags |= SRB_NoTransfer;
memcpy (aspi_buf, (char *) src + cdb_size, srb.u.cmd.data_len);
}
srb.u.cmd.sense_len = 16; /* length of sense buffer */
srb.u.cmd.data_ptr = NULL; /* pointer to data buffer already registered */
srb.u.cmd.link_ptr = NULL; /* pointer to next SRB */
srb.u.cmd.cdb_len = cdb_size; /* SCSI command length */
memcpy (&srb.u.cmd.cdb_st[0], (char *) src, cdb_size);
/* Do the command. */
rc = DosDevIOCtl (driver_handle, 0x92, 0x02, (void *) &srb,
sizeof (SRB), &cbParam,
(void *) &srb, sizeof (SRB), &cbreturn);
if (rc)
{
DBG (1, "sanei_scsi_cmd: DosDevIOCtl failed.\n");
return SANE_STATUS_IO_ERROR;
}
if (DosWaitEventSem (postSema, -1) || /* wait forever for sema. */
DosResetEventSem (postSema, &count)) /* reset semaphore. */
{
DBG (1, "sanei_scsi_cmd: semaphore failure.\n");
return SANE_STATUS_IO_ERROR;
}
/* Get sense data if available. */
if ((srb.status == SRB_Aborted || srb.status == SRB_Error) &&
srb.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, &srb.u.cmd.cdb_st[cdb_size],
fd_info[fd].sense_handler_arg);
}
if (srb.status != SRB_Done ||
srb.u.cmd.ha_status != SRB_NoError ||
srb.u.cmd.target_status != SRB_NoStatus)
DBG (1, "sanei_scsi_cmd: command 0x%02x failed.\n", srb.u.cmd.cdb_st[0]);
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_cmd (int fd, 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_cmd (int fd, const void *src, size_t src_size,
void *dst, size_t * dst_size)
{
dsreq_t scsi_req; /* SCSI request */
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);
databuf = malloc(MAX_DATA);
if (cmdbuf == NULL || sensebuf == NULL || databuf == NULL)
return SANE_STATUS_NO_MEM;
}
/*
* Build the SCSI request...
*/
cdb_size = CDB_SIZE (*(u_char *) src);
DBG(1, "sanei_scsi_cmd: cdb_size = %d\n", cdb_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 = cdb_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 = sizeof (sensebuf);
/*
* Copy command to cmdbuf to assure 32-bit alignment.
*/
memcpy(cmdbuf, src, cdb_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 = cdb_size;
scsi_req.ds_databuf = (caddr_t) databuf;
scsi_req.ds_datalen = src_size - cdb_size;
scsi_req.ds_sensebuf = (caddr_t) sensebuf;
scsi_req.ds_senselen = sizeof (sensebuf);
/*
* Copy command and data to local buffers to ensure 32-bit alignment...
*/
memcpy (cmdbuf, (u_char *) src, cdb_size);
memcpy (databuf, (u_char *) src + cdb_size, src_size - cdb_size);
}
/*
* 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 ((sensebuf[0] & 0x80) && 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_cmd (int fd, const void *src, size_t src_size,
void *dst, size_t * dst_size)
{
scmd_t scmd;
size_t cdb_size;
char sense_buf[32];
char status;
cdb_size = CDB_SIZE (*(u_char *) src);
memset (&scmd, 0, sizeof (scmd));
if (dst_size && *dst_size)
{
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 + cdb_size;
scmd.datalen = src_size - cdb_size;
}
scmd.cdb = (char *) src;
scmd.cdblen = cdb_size;
scmd.timeval = 60; /* 1 minute 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_cmd (int fd, const void *src, size_t src_size,
void *dst, size_t * dst_size)
{
struct user_scsi us;
size_t cdb_size;
char sensebf[CCS_SENSE_LEN];
cdb_size = CDB_SIZE (*(u_char *) src);
/* first put the user scsi structure together. */
memset (&us, 0, sizeof (us));
us.us_cdbp = (caddr_t) src;
us.us_cdblen = cdb_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 + cdb_size;
us.us_buflen = src_size - cdb_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 60; /* 1 minute */
/* 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 *src, size_t src_size,
void *dst, size_t * dst_size, int probing)
{
struct scg_cmd scmd;
size_t cdb_size;
SANEI_SCSI_Sense_Handler handler;
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)
{
assert (cdb_size == src_size);
scmd.flags |= SCG_RECV_DATA;
scmd.addr = dst;
scmd.size = *dst_size;
}
else
{
assert (cdb_size <= src_size);
scmd.addr = (caddr_t) src + cdb_size;
scmd.size = src_size - cdb_size;
}
scmd.cdb_len = cdb_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, src, cdb_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, 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_cmd (int fd, const void *src, size_t src_size,
void *dst, size_t * dst_size)
{
return scsi_cmd (fd, 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, 1);
return (status == SANE_STATUS_GOOD);
}
#endif /* USE == SOLARIS_INTERFACE */
#ifndef 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)
{
return sanei_scsi_cmd (fd, 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)
{
}
#endif /* WE_HAVE_ASYNC_SCSI */
#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 (1, "sanei_scsi_find_devices: not implemented for this platform\n");
}
#endif /* WE_HAVE_FIND_DEVICES */