kopia lustrzana https://gitlab.com/sane-project/backends
				
				
				
			
		
			
				
	
	
		
			529 wiersze
		
	
	
		
			23 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
			
		
		
	
	
			529 wiersze
		
	
	
		
			23 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
| /* sane - Scanner Access Now Easy.
 | |
|    Copyright (C) 1996, 1997 David Mosberger-Tang
 | |
|    This file is part of the SANE package.
 | |
| 
 | |
|    This program is free software; you can redistribute it and/or
 | |
|    modify it under the terms of the GNU General Public License as
 | |
|    published by the Free Software Foundation; either version 2 of the
 | |
|    License, or (at your option) any later version.
 | |
| 
 | |
|    This program is distributed in the hope that it will be useful, but
 | |
|    WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
|    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | |
|    General Public License for more details.
 | |
| 
 | |
|    You should have received a copy of the GNU General Public License
 | |
|    along with this program; if not, write to the Free Software
 | |
|    Foundation, Inc., 59 Temple Place - Suite 330, Boston,
 | |
|    MA 02111-1307, USA.
 | |
| 
 | |
|    As a special exception, the authors of SANE give permission for
 | |
|    additional uses of the libraries contained in this release of SANE.
 | |
| 
 | |
|    The exception is that, if you link a SANE library with other files
 | |
|    to produce an executable, this does not by itself cause the
 | |
|    resulting executable to be covered by the GNU General Public
 | |
|    License.  Your use of that executable is in no way restricted on
 | |
|    account of linking the SANE library code into it.
 | |
| 
 | |
|    This exception does not, however, invalidate any other reasons why
 | |
|    the executable file might be covered by the GNU General Public
 | |
|    License.
 | |
| 
 | |
|    If you submit changes to SANE to the maintainers to be included in
 | |
|    a subsequent release, you agree by submitting the changes that
 | |
|    those changes may be distributed with this exception intact.
 | |
| 
 | |
|    If you write modifications of your own for SANE, it is your choice
 | |
|    whether to permit this exception to apply to your modifications.
 | |
|    If you do not wish that, delete this exception notice.
 | |
| 
 | |
|    This file 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 "sane/config.h"
 | |
| 
 | |
| #include "sane/sanei_scsi.h"
 | |
| 
 | |
| #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 occured, 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);
 | |
|    }
 |