kopia lustrzana https://gitlab.com/sane-project/backends
				
				
				
			
		
			
				
	
	
		
			527 wiersze
		
	
	
		
			23 KiB
		
	
	
	
		
			C
		
	
	
			
		
		
	
	
			527 wiersze
		
	
	
		
			23 KiB
		
	
	
	
		
			C
		
	
	
/* sane - Scanner Access Now Easy.
 | 
						|
   Copyright (C) 1996, 1997 David Mosberger-Tang
 | 
						|
   This file is part of the SANE package.
 | 
						|
 | 
						|
   This program is free software; you can redistribute it and/or
 | 
						|
   modify it under the terms of the GNU General Public License as
 | 
						|
   published by the Free Software Foundation; either version 2 of the
 | 
						|
   License, or (at your option) any later version.
 | 
						|
 | 
						|
   This program is distributed in the hope that it will be useful, but
 | 
						|
   WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
						|
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | 
						|
   General Public License for more details.
 | 
						|
 | 
						|
   You should have received a copy of the GNU General Public License
 | 
						|
   along with this program.  If not, see <https://www.gnu.org/licenses/>.
 | 
						|
 | 
						|
   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 defines a server for Apollo Domain/OS systems.  It does all
 | 
						|
of the scsi_$ calls that are needed for SANE.  This is necessary because
 | 
						|
Domain/OS will not allow a child process to access a parent's SCSI
 | 
						|
device.  The interface is through a common, mapped area.  Mutex locks
 | 
						|
are used to prevent concurrency problems, and eventcounts are used to
 | 
						|
notify a waiting process that its request has completed.
 | 
						|
 | 
						|
    This program is intended to support only one device at a time,
 | 
						|
although multiple instances of this program may run concurrently.  It is
 | 
						|
intended that this program be forked/execd by a SANE application, and
 | 
						|
that it will exit when the application exits.
 | 
						|
 | 
						|
    Upon startup, the program is invoked with the path to an object that
 | 
						|
needs to be mapped for communication.  The parent process will have
 | 
						|
already initialized the 'public' eventcounts and locks, and will be
 | 
						|
waiting for the ResultReady eventcount to be incremented.  After
 | 
						|
initialization, the server will increment this eventcount, and wait for
 | 
						|
an incoming request, which is signified by the CommandAvailable
 | 
						|
eventcount.  This EC will be incremented after another process has
 | 
						|
filled in the parameter area.
 | 
						|
 | 
						|
DBG levels:
 | 
						|
    0   Error - always printed.
 | 
						|
    1   Basic monitor - print entry to main functions, or errors that are
 | 
						|
         normally suppressed because they are reported at a higher level.
 | 
						|
    2   Medium monitor - show intermediate steps in functions
 | 
						|
    3   Detailed monitor - if its there, print it
 | 
						|
 | 
						|
*/
 | 
						|
 | 
						|
#include <assert.h>
 | 
						|
#include <ctype.h>
 | 
						|
#include <stdio.h>
 | 
						|
#include <stdlib.h>
 | 
						|
#include <string.h>
 | 
						|
 | 
						|
#include <apollo/base.h>
 | 
						|
#include <apollo/ec2.h>
 | 
						|
#include <apollo/error.h>
 | 
						|
#include <apollo/fault.h>
 | 
						|
#include <apollo/ms.h>
 | 
						|
#include <apollo/mutex.h>
 | 
						|
#include <apollo/pfm.h>
 | 
						|
#include <apollo/scsi.h>
 | 
						|
 | 
						|
#include "../include/sane/config.h"
 | 
						|
 | 
						|
#include "../include/sane/sanei_scsi.h"
 | 
						|
 | 
						|
#include "../include/sane/sanei_debug.h"
 | 
						|
 | 
						|
#include "sanei_DomainOS.h"
 | 
						|
 | 
						|
/* Timeout period for SCSI wait, in milliseconds.
 | 
						|
We are using 100 seconds here. */
 | 
						|
#define DomainScsiTimeout 100000
 | 
						|
 | 
						|
/* Communication Area pointer */
 | 
						|
struct DomainServerCommon *com;
 | 
						|
 | 
						|
/* Handle for fault handler */
 | 
						|
pfm_$fh_handle_t FaultHandle;
 | 
						|
 | 
						|
 | 
						|
static struct
 | 
						|
   {
 | 
						|
   void *DomainSCSIPtr;         /* Pointer to the data block for this device */
 | 
						|
   void *DomainSensePtr;        /* Pointer to the sense area for this device */
 | 
						|
   u_int in_use  : 1;           /* is this DomainFdInfo in use? */
 | 
						|
   u_int fake_fd : 1;           /* is this a fake file descriptor? */
 | 
						|
   scsi_$handle_t scsi_handle;  /* SCSI handle */
 | 
						|
   scsi_$operation_id_t op_id;  /* op_id of current request */
 | 
						|
   } *DomainFdInfo;
 | 
						|
 | 
						|
/* This function is called error might have occurred, but it would be one that I
 | 
						|
don't know how to handle, or never expect to happen.  */
 | 
						|
static void DomainErrorCheck(status_$t status, const char *message)
 | 
						|
   {
 | 
						|
   char *subsystem, *module, *code;
 | 
						|
   short subsystem_length, module_length, code_length;
 | 
						|
 | 
						|
   if (status.all)
 | 
						|
      {
 | 
						|
      DBG(0, "Unrecoverable Domain/OS Error 0x%08x:  %s\n", status.all, message);
 | 
						|
      error_$find_text(status, &subsystem, &subsystem_length, &module, &module_length, &code, &code_length);
 | 
						|
      if (subsystem_length && module_length && code_length)
 | 
						|
         DBG(0, "%.*s (%.*s/%.*s)\n", code_length, code, subsystem_length, subsystem, module_length, module);
 | 
						|
      exit(EXIT_FAILURE);
 | 
						|
      }
 | 
						|
   }
 | 
						|
 | 
						|
 | 
						|
/* This function is the fault handler for the server.  Currently, it only
 | 
						|
handles asynchronous faults.  It always returns to the faulting code, but
 | 
						|
it disables the handler, so that the server can be killed if the parent is
 | 
						|
unable to do so. */
 | 
						|
pfm_$fh_func_val_t FaultHandler(pfm_$fault_rec_t *FaultStatusPtr)
 | 
						|
   {
 | 
						|
   status_$t status;
 | 
						|
 | 
						|
   DBG(1, "In fault handler, status is %08x\n", FaultStatusPtr->status.all);
 | 
						|
   switch (FaultStatusPtr->status.all)
 | 
						|
      {
 | 
						|
      case fault_$quit:
 | 
						|
         pfm_$release_fault_handler(FaultHandle, &status);
 | 
						|
         DomainErrorCheck(status, "Can't release fault handler");
 | 
						|
         return pfm_$return_to_faulting_code;
 | 
						|
      default:
 | 
						|
         DBG(0, "Unrecognized fault type %08x, exiting\n", FaultStatusPtr->status.all);
 | 
						|
         exit(EXIT_FAILURE);
 | 
						|
      }
 | 
						|
   }
 | 
						|
 | 
						|
 | 
						|
static void DomainSCSIOpen(void)
 | 
						|
   {
 | 
						|
   static int num_alloced = 0;
 | 
						|
   int fd;
 | 
						|
   scsi_$handle_t scsi_handle;
 | 
						|
   pinteger len;
 | 
						|
   void *DataBasePtr;
 | 
						|
 | 
						|
   /* Find fake fd. */
 | 
						|
   for (fd = 0; fd < num_alloced; ++fd)
 | 
						|
      if (!DomainFdInfo[fd].in_use)
 | 
						|
         break;
 | 
						|
 | 
						|
   /* Acquire the device */
 | 
						|
   DBG(1, "DomainSCSIOpen: dev='%s', fd=%d\n", com->open_path, fd);
 | 
						|
   len = strlen(com->open_path);
 | 
						|
   scsi_$acquire((char *)com->open_path, len, &scsi_handle, &com->CommandStatus);
 | 
						|
   if (com->CommandStatus.all != status_$ok)
 | 
						|
      {
 | 
						|
      /* we have a failure, return an error code, and generate debug output */
 | 
						|
      DBG(1, "DomainSCSIOpen: acquire failed, Domain/OS status is %08x\n", com->CommandStatus.all);
 | 
						|
      error_$print(com->CommandStatus);
 | 
						|
      return;
 | 
						|
      }
 | 
						|
   else
 | 
						|
      {
 | 
						|
      /* device acquired, setup buffers and buffer pointers */
 | 
						|
      DBG(2, "DomainSCSIOpen: acquire OK, handle is %x\n", scsi_handle);
 | 
						|
      /* Create/map the data area */
 | 
						|
      tmpnam(com->open_path);
 | 
						|
      DBG(2, "DomainSCSIOpen: Data block name will be '%s'\n", com->open_path);
 | 
						|
      DataBasePtr = ms_$crmapl(com->open_path, strlen(com->open_path), 0, DomainMaxDataSize + DomainSenseSize, ms_$cowriters, &com->CommandStatus);
 | 
						|
      DomainErrorCheck(com->CommandStatus, "Creating Data Area");
 | 
						|
      assert((((int)DataBasePtr) & 0x3ff) == 0);  /* Relies on Domain/OS mapping new objects on page boundary */
 | 
						|
      DBG(2, "Data Buffer block created at %p, length = 0x%lx\n", DataBasePtr, DomainMaxDataSize + DomainSenseSize);
 | 
						|
      /* Wire the buffer */
 | 
						|
      scsi_$wire(scsi_handle, (void *)DataBasePtr, DomainMaxDataSize + DomainSenseSize, &com->CommandStatus);
 | 
						|
      if (com->CommandStatus.all == status_$ok)
 | 
						|
         {
 | 
						|
         /* success, indicate status */
 | 
						|
         DBG(2, "Buffer wire was successful\n");
 | 
						|
         }
 | 
						|
      else
 | 
						|
         {
 | 
						|
         /* failure, print detail and return code */
 | 
						|
         DBG(1, "Buffer wire failed, Domain/OS status is %08x\n", com->CommandStatus.all);
 | 
						|
         error_$print(com->CommandStatus);
 | 
						|
         return;
 | 
						|
         }
 | 
						|
      }
 | 
						|
 | 
						|
   if (fd >= num_alloced)
 | 
						|
      {
 | 
						|
      size_t new_size, old_size;
 | 
						|
 | 
						|
      old_size = num_alloced * sizeof (DomainFdInfo[0]);
 | 
						|
      num_alloced = fd + 8;
 | 
						|
      new_size = num_alloced * sizeof (DomainFdInfo[0]);
 | 
						|
      if (DomainFdInfo)
 | 
						|
         DomainFdInfo = realloc (DomainFdInfo, new_size);
 | 
						|
      else
 | 
						|
         DomainFdInfo = malloc (new_size);
 | 
						|
      memset ((char *) DomainFdInfo + old_size, 0, new_size - old_size);
 | 
						|
      assert(DomainFdInfo);
 | 
						|
      }
 | 
						|
   DomainFdInfo[fd].in_use = 1;
 | 
						|
   DomainFdInfo[fd].scsi_handle = scsi_handle;
 | 
						|
   DomainFdInfo[fd].DomainSCSIPtr = DataBasePtr;
 | 
						|
   DomainFdInfo[fd].DomainSensePtr = ((char *)DataBasePtr) + DomainMaxDataSize;
 | 
						|
   com->fd = fd;
 | 
						|
   }
 | 
						|
 | 
						|
 | 
						|
static void DomainSCSIClose(void)
 | 
						|
   {
 | 
						|
   DomainFdInfo[com->fd].in_use = 0;
 | 
						|
   DBG(1, "sanei_scsi_close:  fd=%d\n", com->fd);
 | 
						|
   /* Unwire the buffer */
 | 
						|
   scsi_$unwire(DomainFdInfo[com->fd].scsi_handle, DomainFdInfo[com->fd].DomainSCSIPtr, DomainMaxDataSize + DomainSenseSize, true, &com->CommandStatus);
 | 
						|
   DomainErrorCheck(com->CommandStatus, "Unwiring SCSI buffers");
 | 
						|
   /* Release the device */
 | 
						|
   scsi_$release(DomainFdInfo[com->fd].scsi_handle, &com->CommandStatus);
 | 
						|
   DomainErrorCheck(com->CommandStatus, "Releasing device");
 | 
						|
   /* Unmap the buffer area */
 | 
						|
   ms_$unmap(DomainFdInfo[com->fd].DomainSCSIPtr, DomainMaxDataSize + DomainSenseSize, &com->CommandStatus);
 | 
						|
   DomainErrorCheck(com->CommandStatus, "Unmapping device data area");
 | 
						|
   }
 | 
						|
 | 
						|
 | 
						|
/* 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.  */
 | 
						|
static void DomainSCSIFlushAll(void)
 | 
						|
   {
 | 
						|
   status_$t status;
 | 
						|
 | 
						|
   DBG(1, "DomainSCSIFlushAll: ()\n");
 | 
						|
   DBG(0, "Error - unimplemented feature in module" "BACKEND_NAME");
 | 
						|
   assert(1==0);
 | 
						|
   }
 | 
						|
 | 
						|
 | 
						|
/* This function must only be called from DomainSCSIEnter.  The current
 | 
						|
server architecture requires that the Wait immediately follow the Enter
 | 
						|
command.  */
 | 
						|
static void DomainSCSIWait(void)
 | 
						|
   {
 | 
						|
   int count;
 | 
						|
   char *ascii_wait_status, *ascii_op_status;
 | 
						|
   pinteger return_count;
 | 
						|
   scsi_$op_status_t status_list[1];
 | 
						|
   scsi_$wait_index_t wait_index;
 | 
						|
 | 
						|
   /* wait for the command completion */
 | 
						|
   wait_index = scsi_$wait(DomainFdInfo[com->fd].scsi_handle, DomainScsiTimeout, true, DomainFdInfo[com->fd].op_id, 1, status_list, &return_count, &com->CommandStatus);
 | 
						|
   DBG(2, " scsi_$wait returned status = %08x\n", com->CommandStatus.all);
 | 
						|
   if (com->CommandStatus.all == status_$ok)
 | 
						|
      {
 | 
						|
      switch (wait_index)
 | 
						|
         {
 | 
						|
         case scsi_device_advance:  ascii_wait_status = "scsi_device_advance"; break;
 | 
						|
         case scsi_timeout:         ascii_wait_status = "scsi_timeout"; break;
 | 
						|
         case scsi_async_fault:     ascii_wait_status = "scsi_async_fault"; break;
 | 
						|
         default:                   ascii_wait_status = "unknown"; break;
 | 
						|
         }
 | 
						|
      DBG(2, " scsi_$wait status is %s, return_count is %d\n", ascii_wait_status, return_count);
 | 
						|
      if (wait_index != scsi_device_advance)
 | 
						|
         {
 | 
						|
         DBG(1, "Error - SCSI timeout, or async fault\n");
 | 
						|
         com->CommandStatus.all = scsi_$operation_timeout;
 | 
						|
         }
 | 
						|
      else for (count = 0; count < return_count; count++)
 | 
						|
         {
 | 
						|
         switch (status_list[count].op_status)
 | 
						|
            {
 | 
						|
            case scsi_good_status:                ascii_op_status = "scsi_good_status"; break;
 | 
						|
            case scsi_check_condition:            ascii_op_status = "scsi_check_condition"; break;
 | 
						|
            case scsi_condition_met:              ascii_op_status = "scsi_condition_met"; break;
 | 
						|
            case scsi_rsv1:                       ascii_op_status = "scsi_rsv1"; break;
 | 
						|
            case scsi_busy:                       ascii_op_status = "scsi_busy"; break;
 | 
						|
            case scsi_rsv2:                       ascii_op_status = "scsi_rsv2"; break;
 | 
						|
            case scsi_rsv3:                       ascii_op_status = "scsi_rsv3"; break;
 | 
						|
            case scsi_rsv4:                       ascii_op_status = "scsi_rsv4"; break;
 | 
						|
            case scsi_intermediate_good:          ascii_op_status = "scsi_intermediate_good"; break;
 | 
						|
            case scsi_rsv5:                       ascii_op_status = "scsi_rsv5"; break;
 | 
						|
            case scsi_intermediate_condition_met: ascii_op_status = "scsi_intermediate_condition_met"; break;
 | 
						|
            case scsi_rsv6:                       ascii_op_status = "scsi_rsv6"; break;
 | 
						|
            case scsi_reservation_conflict:       ascii_op_status = "scsi_reservation_conflict"; break;
 | 
						|
            case scsi_rsv7:                       ascii_op_status = "scsi_rsv7"; break;
 | 
						|
            case scsi_rsv8:                       ascii_op_status = "scsi_rsv8"; break;
 | 
						|
            case scsi_rsv9:                       ascii_op_status = "scsi_rsv9"; break;
 | 
						|
            case scsi_undefined_status:           ascii_op_status = "scsi_undefined_status"; break;
 | 
						|
            default:                              ascii_op_status = "unknown"; break;
 | 
						|
            }
 | 
						|
         DBG(2, " list[%d]: op=%x  cmd_status=%08x, status=%s\n", count, status_list[count].op, status_list[count].cmd_status.all, ascii_op_status);
 | 
						|
         switch (status_list[count].cmd_status.all)
 | 
						|
            {
 | 
						|
            case status_$ok:
 | 
						|
               switch (status_list[count].op_status)
 | 
						|
                  {
 | 
						|
                  case scsi_good_status:
 | 
						|
                     break;
 | 
						|
                  case scsi_busy:
 | 
						|
                     com->CommandStatus.all = status_$ok | 0x80000000;
 | 
						|
                     com->SCSIStatus = scsi_busy;
 | 
						|
                     break;
 | 
						|
                  case scsi_check_condition:
 | 
						|
                     {
 | 
						|
                     static unsigned char scanner_sense_cdb[] = {3, 0, 0, 0, DomainSenseSize, 0};
 | 
						|
                     static scsi_$cdb_t sense_cdb;
 | 
						|
                     static linteger sense_cdb_size;
 | 
						|
                     static scsi_$operation_id_t sense_op_id;
 | 
						|
                     static status_$t sense_status;
 | 
						|
                     static pinteger sense_return_count;
 | 
						|
                     static int temp;
 | 
						|
 | 
						|
                     /* Issue the sense command (wire, issue, wait, unwire */
 | 
						|
                     sense_cdb_size = sizeof(scanner_sense_cdb);
 | 
						|
                     memcpy(&sense_cdb, scanner_sense_cdb, sense_cdb_size);
 | 
						|
                     scsi_$do_command_2(DomainFdInfo[com->fd].scsi_handle, sense_cdb, sense_cdb_size, DomainFdInfo[com->fd].DomainSensePtr, DomainSenseSize, scsi_read, &sense_op_id, &sense_status);
 | 
						|
                     DomainErrorCheck(sense_status, "Executing sense command");
 | 
						|
                     scsi_$wait(DomainFdInfo[com->fd].scsi_handle, DomainScsiTimeout, true, sense_op_id, 1, status_list, &sense_return_count, &sense_status);
 | 
						|
                     /* The following debug output is scanner specific */
 | 
						|
                     DBG(2, "Sense information:  Error code=%02x, ASC=%02x, ASCQ=%02x\n", ((u_char *)DomainFdInfo[com->fd].DomainSensePtr)[0], ((char *)DomainFdInfo[com->fd].DomainSensePtr)[0xc], ((char *)DomainFdInfo[com->fd].DomainSensePtr)[0xd]);
 | 
						|
                     DBG(2, " Sense dump:\n");
 | 
						|
                     for (temp = 0; temp < DomainSenseSize; temp++)
 | 
						|
                        DBG(2, " %02x", ((u_char *)DomainFdInfo[com->fd].DomainSensePtr)[temp]);
 | 
						|
                     DBG(2, "\n");
 | 
						|
                     /* see if buffer underrun - ILI/Valid are set, and command was a read */
 | 
						|
                     /* Warning - this might be UMAX specific */
 | 
						|
                     if ((((char *)DomainFdInfo[com->fd].DomainSensePtr)[0] == 0xf0) && (((char *)DomainFdInfo[com->fd].DomainSensePtr)[2] & 0x20) && (com->cdb.g0.cmd == 0x28))
 | 
						|
                        {
 | 
						|
                        /* Warning - the following code is specific to endianness and int size */
 | 
						|
                        /*   Its also very ugly */
 | 
						|
                        DBG(2, "Shortening destination length by %x bytes\n", *(int *)(((char *)DomainFdInfo[com->fd].DomainSensePtr)+3));
 | 
						|
                        com->dst_size -= *(int *)(((char *)DomainFdInfo[com->fd].DomainSensePtr)+3);
 | 
						|
                        DBG(2, "Final dest size is %x\n", com->dst_size);
 | 
						|
                        }
 | 
						|
                     else
 | 
						|
                        {
 | 
						|
                        /* Set this status so that the sense handler can be called */
 | 
						|
                        com->CommandStatus.all = status_$ok | 0x80000000;
 | 
						|
                        com->SCSIStatus = scsi_check_condition;
 | 
						|
                        }
 | 
						|
                     }
 | 
						|
                     break;
 | 
						|
                  default:
 | 
						|
                     /* I fault out in this case because I want to know about this error,
 | 
						|
                        and this guarantees that it will get attention. */
 | 
						|
                     DBG(0, "Unrecoverable Domain/OS scsi handler error:  status=%08x\n", status_list[count].op_status);
 | 
						|
                     exit(EXIT_FAILURE);
 | 
						|
                  }
 | 
						|
               break;
 | 
						|
            /* Handle recognized error conditions by copying the error code over */
 | 
						|
            case scsi_$operation_timeout:
 | 
						|
            case scsi_$dma_underrun:  /* received by some backend code */
 | 
						|
            case scsi_$hdwr_failure:  /* received when both scanners were active */
 | 
						|
               com->CommandStatus = status_list[count].cmd_status;
 | 
						|
               break;
 | 
						|
            default:
 | 
						|
               DBG(0, "Unrecoverable DomainOS scsi handler error:  status=%08x\n", status_list[count].cmd_status.all);
 | 
						|
               error_$print(status_list[count].cmd_status);
 | 
						|
               exit(EXIT_FAILURE);
 | 
						|
            }
 | 
						|
         }
 | 
						|
      /* Dump the buffer contents */
 | 
						|
      if (com->direction == scsi_read)
 | 
						|
         {
 | 
						|
         DBG(3, "first words of buffer are:\n");
 | 
						|
         for (return_count = 0; return_count < com->dst_size; return_count++)
 | 
						|
            DBG(3, "%02X%c", ((unsigned char *)DomainFdInfo[com->fd].DomainSCSIPtr)[return_count], (return_count % 16) == 15 ? '\n' : ' ');
 | 
						|
         DBG(3, "\n");
 | 
						|
         }
 | 
						|
      }
 | 
						|
   else
 | 
						|
      {
 | 
						|
      /* scsi_$wait failed */
 | 
						|
      DBG(1, "scsi_$wait failed, status is %08x\n", com->CommandStatus.all);
 | 
						|
      }
 | 
						|
   }
 | 
						|
 | 
						|
 | 
						|
static void DomainSCSIEnter(void)
 | 
						|
   {
 | 
						|
   static int count;
 | 
						|
 | 
						|
   /* Give some debug info */
 | 
						|
   DBG(1, "Entering DomainSCSIEnter, fd=%d, opcode=%02X\n", com->fd, com->cdb.all[0]);
 | 
						|
   DBG(2, " CDB Contents: ");
 | 
						|
   for (count = 0; count < com->cdb_size; count++)
 | 
						|
      DBG(2, " %02X", com->cdb.all[count]);
 | 
						|
   DBG(2, "\n");
 | 
						|
   DBG(2, "Buffer address is 0x%08x\n", DomainFdInfo[com->fd].DomainSCSIPtr);
 | 
						|
   DBG(2, "Buffer size is %x\n", com->buf_size);
 | 
						|
   DBG(2, "Direction is %s\n", (com->direction == scsi_read) ? "READ" : "WRITE");
 | 
						|
   /* now queue the command */
 | 
						|
   scsi_$do_command_2(DomainFdInfo[com->fd].scsi_handle, com->cdb, com->cdb_size, DomainFdInfo[com->fd].DomainSCSIPtr, com->buf_size, com->direction, &DomainFdInfo[com->fd].op_id, &com->CommandStatus);
 | 
						|
   if (com->CommandStatus.all == status_$ok)
 | 
						|
      {
 | 
						|
      /* success, indicate status */
 | 
						|
      DBG(2, " scsi_$do_command_2 was successful, op_id is %x\n", DomainFdInfo[com->fd].op_id);
 | 
						|
 | 
						|
      /* If we supported multiple outstanding requests for one device, this would be
 | 
						|
         a good breakpoint.  We would store the op_id in a private place, and construct
 | 
						|
         a queue for each device.  This complicates things, and SANE doesn't seem to need
 | 
						|
         it, so it won't be implemented.  The current server architecture does the wait
 | 
						|
         automatically, and status for the entire operation is returned.  This means that
 | 
						|
         the sanei_scsi_req_enter and sanei_scsi_req_wait calls don't make sense, and
 | 
						|
         should generate an error. */
 | 
						|
      DomainSCSIWait();
 | 
						|
      }
 | 
						|
   else
 | 
						|
      {
 | 
						|
      /* failure, print detail and return code */
 | 
						|
      DBG(1, " scsi_$do_command_2 failed, status is %08x\n", com->CommandStatus.all);
 | 
						|
      }
 | 
						|
   }
 | 
						|
 | 
						|
 | 
						|
/* This function is not currently used. */
 | 
						|
static void DomainSCSIReqWait(void)
 | 
						|
   {
 | 
						|
   DBG(1, "sanei_scsi_req_wait: (id=%p)\n", NULL);
 | 
						|
   return;
 | 
						|
   }
 | 
						|
 | 
						|
 | 
						|
/* Startup the server */
 | 
						|
static void sanei_DomainOS_init(char *path)
 | 
						|
   {
 | 
						|
   int done, index;
 | 
						|
   long CommandTriggerValue;
 | 
						|
   ec2_$ptr_t CommandAvailablePtr[1];
 | 
						|
   status_$t status;
 | 
						|
   unsigned long length_mapped;
 | 
						|
 | 
						|
   DBG(1, "Starting Domain SANE Server, common area path = '%s'\n", path);
 | 
						|
   com = ms_$mapl(path, strlen(path), 0, sizeof(struct DomainServerCommon), ms_$cowriters, ms_$wr, true, &length_mapped, &status);
 | 
						|
   DomainErrorCheck(status, "Can't open common area");
 | 
						|
   if (length_mapped < sizeof(struct DomainServerCommon))
 | 
						|
      {
 | 
						|
      DBG(0, "Error - can't open common area '%s' to required length\n", path);
 | 
						|
      DBG(0, " Required length = %lx, returned length = %lx\n", sizeof(struct DomainServerCommon), length_mapped);
 | 
						|
      exit(EXIT_FAILURE);
 | 
						|
      }
 | 
						|
   /* Make the file temporary, so it will disappear when it is closed */
 | 
						|
   ms_$mk_temporary(com, &status);
 | 
						|
   DomainErrorCheck(status, "Can't make common file temporary");
 | 
						|
   DBG(2, "Domain Server common area mapped, length is %lx\n", length_mapped);
 | 
						|
   /* The communication area is open, give the initial response */
 | 
						|
   ec2_$advance(&com->ResultReady, &status);
 | 
						|
   DomainErrorCheck(status, "Can't advance ResultReady EC after startup");
 | 
						|
   /* Enter the command loop */
 | 
						|
   CommandAvailablePtr[0] = &com->CommandAvailable;
 | 
						|
   CommandTriggerValue = ec2_$read(com->CommandAvailable) + 1;
 | 
						|
   /* Inhibit asynchronous faults */
 | 
						|
/*   pfm_$inhibit();*/
 | 
						|
   /* Establish the fault handler */
 | 
						|
   FaultHandle = pfm_$establish_fault_handler(pfm_$all_faults, 0, FaultHandler, &status);
 | 
						|
   DomainErrorCheck(status, "Can't establish fault handler");
 | 
						|
   done = 0;
 | 
						|
   do
 | 
						|
      {
 | 
						|
      /* Wait for the command */
 | 
						|
      DBG(2, "Waiting for incoming command\n");
 | 
						|
      do
 | 
						|
         {
 | 
						|
         index = ec2_$wait_svc(CommandAvailablePtr, &CommandTriggerValue, 1, &status);
 | 
						|
         }
 | 
						|
      while (status.all == ec2_$wait_quit);
 | 
						|
      DomainErrorCheck(status, "Error waiting on CommandAvailable EC");
 | 
						|
      assert (index == 1);
 | 
						|
      /* Get the trigger value for next time - this avoids a race/deadlock */
 | 
						|
      CommandTriggerValue = ec2_$read(com->CommandAvailable) + 1;
 | 
						|
      /* decode/execute the command */
 | 
						|
      DBG(2, "Received a command - opcode is %x\n", com->opcode);
 | 
						|
      switch(com->opcode)
 | 
						|
         {
 | 
						|
         case Open:
 | 
						|
            DomainSCSIOpen();
 | 
						|
            ec2_$advance(&com->CommandAccepted, &status);
 | 
						|
            DomainErrorCheck(status, "Can't advance CommandAccepted EC on open");
 | 
						|
            break;
 | 
						|
         case Close:
 | 
						|
            DomainSCSIClose();
 | 
						|
            ec2_$advance(&com->CommandAccepted, &status);
 | 
						|
            DomainErrorCheck(status, "Can't advance CommandAccepted EC on close");
 | 
						|
            break;
 | 
						|
         case Enter:
 | 
						|
            DomainSCSIEnter();
 | 
						|
            ec2_$advance(&com->CommandAccepted, &status);
 | 
						|
            DomainErrorCheck(status, "Can't advance CommandAccepted EC on enter");
 | 
						|
            break;
 | 
						|
         case Exit:
 | 
						|
            done = 1;
 | 
						|
            /* This lets the parent know that the command was accepted.  It can be
 | 
						|
               used to avoid sending a signal.  */
 | 
						|
            ec2_$advance(&com->CommandAccepted, &status);
 | 
						|
            DomainErrorCheck(status, "Can't advance CommandAccepted EC on exit");
 | 
						|
            break;
 | 
						|
         default:
 | 
						|
            DBG(1, "Invalid command %x received\n", com->opcode);
 | 
						|
         }
 | 
						|
      DBG(2, "Command processing complete\n");
 | 
						|
      }
 | 
						|
   while (!done);
 | 
						|
   /* This would be a good place to close all devices, but for now we'll assume
 | 
						|
      they have already been closed by a well-behaved program */
 | 
						|
   /* Unmap the common area */
 | 
						|
   ms_$unmap(com, sizeof(struct DomainServerCommon), &status);
 | 
						|
   DomainErrorCheck(status, "Error unmapping common area");
 | 
						|
   DBG(1, "Exiting Domain SANE Server\n");
 | 
						|
/*   pfm_$enable();*/
 | 
						|
   exit(EXIT_SUCCESS);
 | 
						|
   }
 |