/* 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, see . 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 #include #include #include #include #include #include #ifdef HAVE_SYS_IOCTL_H #include #endif #include #include #if defined (HAVE_WINDOWS_H) # include #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 #endif #if defined (HAVE_SCSI_SG_H) # define USE LINUX_INTERFACE # include #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_H) # define USE SCSO_OS5_INTERFACE # include # include #elif defined (HAVE_CAMLIB_H) # define USE FREEBSD_CAM_INTERFACE # include /* there is a bug in scsi_all.h */ # include # include # include # include # include #elif defined (HAVE_SYS_SCSIIO_H) # define USE BSD_INTERFACE # include # ifdef HAVE_SCSI_H # include # endif #elif defined (HAVE_BSD_DEV_SCSIREG_H) # define USE OPENSTEP_INTERFACE # include #elif defined (HAVE_IO_CAM_CAM_H) # define USE DECUNIX_INTERFACE # include # include # include # include # include #elif defined (HAVE_SYS_DSREQ_H) # define USE IRIX_INTERFACE # include # include #elif defined (HAVE_SYS_SCSI_H) # include # ifdef HAVE_SYS_SDI_COMM_H # ifdef HAVE_SYS_PASSTHRUDEF_H # define USE SCO_UW71_INTERFACE # include # include # include # include # else # define USE SYSVR4_INTERFACE /* Unixware 2.x tested */ # define HAVE_SYSV_DRIVER # include # include # endif # else # ifdef SCTL_READ # define USE HPUX_INTERFACE # else # ifdef HAVE_GSCDDS_H # define USE AIX_GSC_INTERFACE # include # 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 # include "os2_srb.h" #elif defined (HAVE_SYS_SCSI_SGDEFS_H) # define USE SOLARIS_SG_INTERFACE # include #elif defined (HAVE_SYS_SCSI_TARGETS_SCGIO_H) # define USE SOLARIS_INTERFACE # define SOL2 # include #elif defined (HAVE_SYS_SCSI_SCSI_H) /* * the "official" 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 #elif defined (HAVE_APOLLO_SCSI_H) # define USE DOMAINOS_INTERFACE # include /* Only used for signal name for KillDomainServer */ # include # include # include # include # include # include # include # 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 # include # ifdef HAVE_IOKIT_CDB_IOSCSILIB_H # include # include # endif # ifdef HAVE_IOKIT_SCSI_SCSICOMMANDOPERATIONCODES_H /* The def of VERSION causes problems in the following include files */ # undef VERSION # include # include # include # else # ifdef HAVE_IOKIT_SCSI_COMMANDS_SCSICOMMANDOPERATIONCODES_H /* The def of VERSION causes problems in the following include files */ # undef VERSION # include # include # include # endif # endif #elif defined (HAVE_DDK_NTDDSCSI_H) # define USE WIN32_INTERFACE # include # include #elif defined (HAVE_NTDDSCSI_H) # define USE WIN32_INTERFACE # include #endif #ifndef USE # define USE STUBBED_INTERFACE #endif #if USE == LINUX_INTERFACE # include #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 information 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" 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 dependent */ 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"); mkstemp (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 #include #include #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 too 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 after all */ 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 after all */ 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. ) 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 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 */ 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; 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 = (char *) 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 = (char *) 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; 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 (;;) { errno = 0; buf = readdir (scsidevs); if (errno != 0) { DBG (1, "%s: could not read directory %s: %s\n", me, SYSFS_SCSI_DEVICES, strerror(errno)); break; } if (buf == NULL) break; if (buf->d_name[0] == '.') continue; /* Extract bus, channel, id, lun from directory name b:c:i:l */ ptr = buf->d_name; for (i = 0; i < 4; i++) { errno = 0; val = strtol (ptr, &end, 10); if (((errno == ERANGE) && ((val == LONG_MAX) || (val == LONG_MIN))) || ((errno != 0) && (val == 0))) { DBG (1, "%s: invalid integer in string (%s): %s\n", me, ptr, strerror(errno)); i = 12; /* Skip */ break; } if (end == ptr) { DBG (1, "%s: no integer found in string: %s (%d)\n", me, ptr, i); i = 12; /* Skip */ break; } if (*end && (*end != ':')) { DBG (1, "%s: parse error on string %s (%d)\n", me, buf->d_name, i); i = 12; /* Skip */ break; } if (val > INT_MAX) { DBG (1, "%s: integer value too large (%s)\n", me, buf->d_name); i = 12; /* Skip */ break; } bcil[i] = (int) val; ptr = end + 1; } /* Skip this one */ if (i == 12) continue; if (bcil[0] != lastbus) { lastbus = bcil[0]; number++; } for (i = 0; i < 3; i++) { ret = snprintf (path, PATH_MAX, "%s/%s/%s", SYSFS_SCSI_DEVICES, buf->d_name, vmtfiles[i]); if ((ret < 0) || (ret >= PATH_MAX)) { DBG (1, "%s: skipping %s/%s, PATH_MAX exceeded on %s\n", me, SYSFS_SCSI_DEVICES, buf->d_name, vmtfiles[i]); i = 12; /* Skip */ break; } memset (vmt[i], 0, sizeof(vmt[i])); fp = fopen(path, "r"); if (!fp) { DBG (1, "%s: could not open %s: %s\n", me, path, strerror(errno)); i = 12; /* Skip */ break; } ret = fread (vmt[i], 1, sizeof(vmt[i]) - 1, fp); if (ret <= 0) { if (ferror(fp)) { DBG (1, "%s: error reading %s\n", me, path); i = 12; /* Skip */ break; } } if (vmt[i][ret - 1] == '\n') vmt[i][ret - 1] = '\0'; fclose (fp); } /* Skip this one */ if (i == 12) continue; /* Type is a numeric string and must be converted back to a well-known string */ errno = 0; val = strtol (vmt[2], &end, 10); if (((errno == ERANGE) && ((val == LONG_MAX) || (val == LONG_MIN))) || ((errno != 0) && (val == 0))) { DBG (1, "%s: invalid integer in type string (%s): %s\n", me, vmt[2], strerror(errno)); continue; } if (end == vmt[2]) { DBG (1, "%s: no integer found in type string: %s\n", me, vmt[2]); continue; } if ((val < 0) || (val >= (int)(sizeof(lnxscsi_device_types) / sizeof(lnxscsi_device_types[0])))) { DBG (1, "%s: invalid type %ld\n", me, val); continue; } strncpy(vmt[2], lnxscsi_device_types[val], sizeof(vmt[2]) - 1); if ((!findvendor || strncmp (vmt[0], findvendor, vmt_len[0]) == 0) && (!findmodel || strncmp (vmt[1], findmodel, vmt_len[1]) == 0) && (!findtype || strncmp (vmt[2], findtype, vmt_len[2]) == 0) && (findbus == -1 || bcil[0] == findbus) && (findchannel == -1 || bcil[1] == findchannel) && (findid == -1 || bcil[2] == findid) && (findlun == -1 || bcil[3] == findlun)) { DBG (2, "%s: found: vendor=%s model=%s type=%s\n\t" "bus=%d chan=%d id=%d lun=%d num=%d\n", me, vmt[0], vmt[1], vmt[2], bcil[0], bcil[1], bcil[2], bcil[3], number); if (lx_chk_devicename (number, dev_name, sizeof (dev_name), bcil[0], bcil[1], bcil[2], bcil[3]) && ((*attach) (dev_name) != SANE_STATUS_GOOD)) { DBG (1, "%s: bad attach\n", me); } } else { DBG (2, "%s: no match\n", me); } } closedir(scsidevs); } #endif /* USE == LINUX_INTERFACE */ #if USE == BSD_INTERFACE #ifndef HAVE_SCSIREQ_ENTER static int scsireq_enter (int fd, scsireq_t * hdr) { return ioctl (fd, SCIOCCOMMAND, hdr); } #endif /* !HAVE_SCSIREQ_ENTER */ SANE_Status sanei_scsi_cmd2 (int fd, const void *cmd, size_t cmd_size, const void *src, size_t src_size, void *dst, size_t * dst_size) { /* xxx obsolete: size_t cdb_size; */ scsireq_t hdr; int result; /* xxx obsolete: cdb_size = CDB_SIZE (*(u_char *) src); */ memset (&hdr, 0, sizeof (hdr)); memcpy (hdr.cmd, cmd, cmd_size); if (dst_size && *dst_size) { /* xxx obsolete: assert (cdb_size == src_size); */ hdr.flags = SCCMD_READ; hdr.databuf = dst; hdr.datalen = *dst_size; } else { /* xxx obsolete: assert (cdb_size <= src_size); */ hdr.flags = SCCMD_WRITE; /* The old variant: hdr.databuf = (char *) src + cdb_size; hdr.datalen = src_size; xxxxxx huh? Shouldn´t the above line have been src_size - cdb_size) */ hdr.databuf = (char *) src; hdr.datalen = src_size; } hdr.timeout = sane_scsicmd_timeout * 1000; hdr.cmdlen = cmd_size; hdr.senselen = sizeof (hdr.sense); result = scsireq_enter (fd, &hdr); if (result < 0) { DBG (1, "sanei_scsi_cmd: scsi_reqenter() failed: %s\n", strerror (errno)); return SANE_STATUS_IO_ERROR; } if (hdr.retsts != SCCMD_OK) { SANEI_SCSI_Sense_Handler handler; DBG (1, "sanei_scsi_cmd: scsi returned with status %d\n", hdr.retsts); switch (hdr.retsts) { case SCCMD_TIMEOUT: case SCCMD_BUSY: return SANE_STATUS_DEVICE_BUSY; case SCCMD_SENSE: handler = fd_info[fd].sense_handler; if (handler) return (*handler) (fd, &hdr.sense[0], fd_info[fd].sense_handler_arg); /* fall through */ default: return SANE_STATUS_IO_ERROR; } } if (dst_size) *dst_size = hdr.datalen_used; return SANE_STATUS_GOOD; } #endif /* USE == BSD_INTERFACE */ #if USE == FREEBSD_CAM_INTERFACE SANE_Status sanei_scsi_cmd2 (int fd, const void *cmd, size_t cmd_size, const void *src, size_t src_size, void *dst, size_t * dst_size) { struct cam_device *dev; union ccb *ccb; int rv; u_int32_t ccb_flags; char *data_buf; size_t data_len; SANE_Status status; if (fd < 0 || fd > CAM_MAXDEVS || cam_devices[fd] == NULL) { fprintf (stderr, "attempt to reference invalid unit %d\n", fd); return SANE_STATUS_INVAL; } dev = cam_devices[fd]; ccb = cam_getccb (dev); /* Build the CCB */ memset (&(&ccb->ccb_h)[1], 0, sizeof (struct ccb_scsiio)); memcpy (&ccb->csio.cdb_io.cdb_bytes, cmd, 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 */ memset (&cdm, 0, 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 */ memset (&cdm, 0, 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:" PRIu64 "\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 components 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 * Please 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 failure %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); } memset (sensebuf, 0, 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) { memset (dst, 0, *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. */ memset (dst, 0, *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 */ memset (&senseData, 0, sizeof (senseData)); memset (cdb, 0, 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 */