kopia lustrzana https://gitlab.com/sane-project/backends
				
				
				
			
		
			
				
	
	
		
			2112 wiersze
		
	
	
		
			52 KiB
		
	
	
	
		
			C
		
	
	
			
		
		
	
	
			2112 wiersze
		
	
	
		
			52 KiB
		
	
	
	
		
			C
		
	
	
| /* sane - Scanner Access Now Easy.
 | |
|    Copyright (C) 1997 Geoffrey T. Dairiki
 | |
|    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 is part of a SANE backend for HP Scanners supporting
 | |
|    HP Scanner Control Language (SCL).
 | |
| */
 | |
| 
 | |
| /*
 | |
|    $Log$
 | |
|    Revision 1.13  2004/03/27 13:52:39  kig-guest
 | |
|    Keep USB-connection open (was problem with Linux 2.6.x)
 | |
| 
 | |
|    Revision 1.12  2003/10/09 19:34:57  kig-guest
 | |
|    Redo when TEST UNIT READY failed
 | |
|    Redo when read returns with 0 bytes (non-SCSI only)
 | |
| 
 | |
| 
 | |
| */
 | |
| 
 | |
| /*
 | |
| #define STUBS
 | |
| extern int sanei_debug_hp;*/
 | |
| #define DEBUG_DECLARE_ONLY
 | |
| #include "sane/config.h"
 | |
| #include "../include/lalloca.h"		/* Must be first */
 | |
| 
 | |
| #ifdef HAVE_UNISTD_H
 | |
| # include <unistd.h>
 | |
| #endif
 | |
| #include <stdlib.h>
 | |
| #include <ctype.h>
 | |
| #include <stdio.h>
 | |
| #include <string.h>
 | |
| #include <errno.h>
 | |
| #include "../include/lassert.h"
 | |
| #include <signal.h>
 | |
| #include <sys/types.h>
 | |
| #include <sys/stat.h>
 | |
| #include <fcntl.h>
 | |
| #include "sane/sanei_scsi.h"
 | |
| #include "sane/sanei_usb.h"
 | |
| #include "sane/sanei_pio.h"
 | |
| 
 | |
| #include "hp.h"
 | |
| 
 | |
| #include "sane/sanei_backend.h"
 | |
| 
 | |
| #include "hp-option.h"
 | |
| #include "hp-scsi.h"
 | |
| #include "hp-scl.h"
 | |
| #include "hp-device.h"
 | |
| 
 | |
| #define HP_SCSI_INQ_LEN		(36)
 | |
| #define HP_SCSI_CMD_LEN		(6)
 | |
| #define HP_SCSI_BUFSIZ	(HP_SCSI_MAX_WRITE + HP_SCSI_CMD_LEN)
 | |
| 
 | |
| #define HP_MAX_OPEN_FD 16
 | |
| static struct hp_open_fd_s  /* structure to save info about open file descriptor */
 | |
| {
 | |
|     char *devname;
 | |
|     HpConnect connect;
 | |
|     int fd;
 | |
| } asHpOpenFd[HP_MAX_OPEN_FD];
 | |
| 
 | |
| 
 | |
| /*
 | |
|  *
 | |
|  */
 | |
| struct hp_scsi_s
 | |
| {
 | |
|     int		fd;
 | |
|     char      * devname;
 | |
| 
 | |
|     /* Output buffering */
 | |
|     hp_byte_t	buf[HP_SCSI_BUFSIZ];
 | |
|     hp_byte_t *	bufp;
 | |
| 
 | |
|     hp_byte_t	inq_data[HP_SCSI_INQ_LEN];
 | |
| };
 | |
| 
 | |
| #define HP_TMP_BUF_SIZE (1024*4)
 | |
| #define HP_WR_BUF_SIZE (1024*4)
 | |
| 
 | |
| typedef struct
 | |
| {
 | |
|   HpProcessData procdata;
 | |
| 
 | |
|   int outfd;
 | |
|   const unsigned char *map;
 | |
| 
 | |
|   unsigned char *image_buf; /* Buffer to store complete image (if req.) */
 | |
|   unsigned char *image_ptr;
 | |
|   int image_buf_size;
 | |
| 
 | |
|   unsigned char *tmp_buf; /* Buffer for scan data to get even number of bytes */
 | |
|   int tmp_buf_size;
 | |
|   int tmp_buf_len;
 | |
| 
 | |
|   unsigned char wr_buf[HP_WR_BUF_SIZE];
 | |
|   unsigned char *wr_ptr;
 | |
|   int wr_buf_size;
 | |
|   int wr_left;
 | |
| } PROCDATA_HANDLE;
 | |
| 
 | |
| 
 | |
| /* Initialize structure where we remember out open file descriptors */
 | |
| void
 | |
| hp_init_openfd ()
 | |
| {int iCount;
 | |
|  memset (asHpOpenFd, 0, sizeof (asHpOpenFd));
 | |
| 
 | |
|  for (iCount = 0; iCount < HP_MAX_OPEN_FD; iCount++)
 | |
|      asHpOpenFd[iCount].fd = -1;
 | |
| }
 | |
| 
 | |
| 
 | |
| /* Look if the device is still open */
 | |
| static SANE_Status
 | |
| hp_GetOpenDevice (const char *devname, HpConnect connect, int *pfd)
 | |
| 
 | |
| {int iCount;
 | |
| 
 | |
|  for (iCount = 0; iCount < HP_MAX_OPEN_FD; iCount++)
 | |
|      {
 | |
|      if (!asHpOpenFd[iCount].devname) continue;
 | |
|      if (   (strcmp (asHpOpenFd[iCount].devname, devname) == 0)
 | |
|          && (asHpOpenFd[iCount].connect == connect) )
 | |
|          {
 | |
|          if (pfd) *pfd = asHpOpenFd[iCount].fd;
 | |
|          DBG(3, "hp_GetOpenDevice: device %s is open with fd=%d\n", devname,
 | |
|              asHpOpenFd[iCount].fd);
 | |
|          return SANE_STATUS_GOOD;
 | |
|          }
 | |
|      }
 | |
|  DBG(3, "hp_GetOpenDevice: device %s not open\n", devname);
 | |
|  return SANE_STATUS_INVAL;
 | |
| }
 | |
| 
 | |
| /* Add an open file descriptor. This also decides */
 | |
| /* if we keep a connection open or not. */
 | |
| static SANE_Status
 | |
| hp_AddOpenDevice (const char *devname, HpConnect connect, int fd)
 | |
| 
 | |
| {int iCount, iKeepOpen;
 | |
|  static int iInitKeepFlags = 1;
 | |
| 
 | |
|  /* The default values which connections to keep open or not */
 | |
|  static int iKeepOpenSCSI = 0;
 | |
|  static int iKeepOpenUSB = 1;
 | |
|  static int iKeepOpenDevice = 0;
 | |
|  static int iKeepOpenPIO = 0;
 | |
| 
 | |
|  if (iInitKeepFlags) /* Change the defaults by environment */
 | |
|      {char *eptr;
 | |
| 
 | |
|      iInitKeepFlags = 0;
 | |
| 
 | |
|      eptr = getenv ("SANE_HP_KEEPOPEN_SCSI");
 | |
|      if ( (eptr != NULL) && ((*eptr == '0') || (*eptr == '1')) )
 | |
|          iKeepOpenSCSI = (*eptr == '1');
 | |
| 
 | |
|      eptr = getenv ("SANE_HP_KEEPOPEN_USB");
 | |
|      if ( (eptr != NULL) && ((*eptr == '0') || (*eptr == '1')) )
 | |
|          iKeepOpenUSB = (*eptr == '1');
 | |
| 
 | |
|      eptr = getenv ("SANE_HP_KEEPOPEN_DEVICE");
 | |
|      if ( (eptr != NULL) && ((*eptr == '0') || (*eptr == '1')) )
 | |
|          iKeepOpenDevice = (*eptr == '1');
 | |
| 
 | |
|      eptr = getenv ("SANE_HP_KEEPOPEN_PIO");
 | |
|      if ( (eptr != NULL) && ((*eptr == '0') || (*eptr == '1')) )
 | |
|          iKeepOpenPIO = (*eptr == '1');
 | |
|      }
 | |
| 
 | |
|  /* Look if we should keep it open or not */
 | |
|  iKeepOpen = 0;
 | |
|  switch (connect)
 | |
|      {
 | |
|      case HP_CONNECT_SCSI: iKeepOpen = iKeepOpenSCSI;
 | |
|                            break;
 | |
|      case HP_CONNECT_PIO : iKeepOpen = iKeepOpenPIO;
 | |
|                            break;
 | |
|      case HP_CONNECT_USB : iKeepOpen = iKeepOpenUSB;
 | |
|                            break;
 | |
|      case HP_CONNECT_DEVICE : iKeepOpen = iKeepOpenDevice;
 | |
|                            break;
 | |
|      case HP_CONNECT_RESERVE:
 | |
|                            break;
 | |
|      }
 | |
|  if (!iKeepOpen)
 | |
|      {
 | |
|      DBG(3, "hp_AddOpenDevice: %s should not be kept open\n", devname);
 | |
|      return SANE_STATUS_INVAL;
 | |
|      }
 | |
| 
 | |
|  for (iCount = 0; iCount < HP_MAX_OPEN_FD; iCount++)
 | |
|      {
 | |
|      if (!asHpOpenFd[iCount].devname)  /* Is this entry free ? */
 | |
|          {
 | |
|          asHpOpenFd[iCount].devname = sanei_hp_strdup (devname);
 | |
|          if (!asHpOpenFd[iCount].devname) return SANE_STATUS_NO_MEM;
 | |
|          DBG(3, "hp_AddOpenDevice: added device %s with fd=%d\n", devname, fd);
 | |
|          asHpOpenFd[iCount].connect = connect;
 | |
|          asHpOpenFd[iCount].fd = fd;
 | |
|          return SANE_STATUS_GOOD;
 | |
|          }
 | |
|      }
 | |
|  DBG(3, "hp_AddOpenDevice: %s not added\n", devname);
 | |
|  return SANE_STATUS_NO_MEM;
 | |
| }
 | |
| 
 | |
| 
 | |
| /* Check if we have remembered an open file descriptor */
 | |
| static SANE_Status
 | |
| hp_IsOpenFd (int fd, HpConnect connect)
 | |
| 
 | |
| {int iCount;
 | |
| 
 | |
|  for (iCount = 0; iCount < HP_MAX_OPEN_FD; iCount++)
 | |
|      {
 | |
|      if (   (asHpOpenFd[iCount].devname != NULL)
 | |
|          && (asHpOpenFd[iCount].fd == fd)
 | |
|          && (asHpOpenFd[iCount].connect == connect) )
 | |
|          {
 | |
|          DBG(3, "hp_IsOpenFd: %d is open\n", fd);
 | |
|          return SANE_STATUS_GOOD;
 | |
|          }
 | |
|      }
 | |
|  DBG(3, "hp_IsOpenFd: %d not open\n", fd);
 | |
|  return SANE_STATUS_INVAL;
 | |
| }
 | |
| 
 | |
| 
 | |
| static SANE_Status
 | |
| hp_RemoveOpenFd (int fd, HpConnect connect)
 | |
| 
 | |
| {int iCount;
 | |
| 
 | |
|  for (iCount = 0; iCount < HP_MAX_OPEN_FD; iCount++)
 | |
|      {
 | |
|      if (   (asHpOpenFd[iCount].devname != NULL)
 | |
|          && (asHpOpenFd[iCount].fd == fd)
 | |
|          && (asHpOpenFd[iCount].connect == connect) )
 | |
|          {
 | |
|          sanei_hp_free (asHpOpenFd[iCount].devname);
 | |
|          asHpOpenFd[iCount].devname = NULL;
 | |
|          DBG(3, "hp_RemoveOpenFd: removed %d\n", asHpOpenFd[iCount].fd);
 | |
|          asHpOpenFd[iCount].fd = -1;
 | |
|          return SANE_STATUS_GOOD;
 | |
|          }
 | |
|      }
 | |
|  DBG(3, "hp_RemoveOpenFd: %d not removed\n", fd);
 | |
|  return SANE_STATUS_INVAL;
 | |
| }
 | |
| 
 | |
| 
 | |
| static SANE_Status
 | |
| hp_nonscsi_write (HpScsi this, hp_byte_t *data, size_t len, HpConnect connect)
 | |
| 
 | |
| {int n = -1;
 | |
|  size_t loc_len;
 | |
|  SANE_Status status = SANE_STATUS_GOOD;
 | |
| 
 | |
|  if (len <= 0) return SANE_STATUS_GOOD;
 | |
| 
 | |
|  switch (connect)
 | |
|  {
 | |
|    case HP_CONNECT_DEVICE:   /* direct device-io */
 | |
|      n = write (this->fd, data, len);
 | |
|      break;
 | |
| 
 | |
|    case HP_CONNECT_PIO:      /* Use sanepio interface */
 | |
|      n = sanei_pio_write (this->fd, data, len);
 | |
|      break;
 | |
| 
 | |
|    case HP_CONNECT_USB:      /* Not supported */
 | |
|      loc_len = len;
 | |
|      status = sanei_usb_write_bulk ((SANE_Int)this->fd, data, &loc_len);
 | |
|      n = loc_len;
 | |
|      break;
 | |
| 
 | |
|    case HP_CONNECT_RESERVE:
 | |
|      n = -1;
 | |
|      break;
 | |
| 
 | |
|    default:
 | |
|      n = -1;
 | |
|      break;
 | |
|  }
 | |
| 
 | |
|  if (n == 0) return SANE_STATUS_EOF;
 | |
|  else if (n < 0) return SANE_STATUS_IO_ERROR;
 | |
| 
 | |
|  return status;
 | |
| }
 | |
| 
 | |
| static SANE_Status
 | |
| hp_nonscsi_read (HpScsi this, hp_byte_t *data, size_t *len, HpConnect connect,
 | |
|   int UNUSEDARG isResponse)
 | |
| 
 | |
| {int n = -1;
 | |
|  static int retries = -1;
 | |
|  size_t save_len = *len;
 | |
|  SANE_Status status = SANE_STATUS_GOOD;
 | |
| 
 | |
|  if (*len <= 0) return SANE_STATUS_GOOD;
 | |
| 
 | |
|  if (retries < 0)  /* Read environment */
 | |
|  {char *eptr = getenv ("SANE_HP_RDREDO");
 | |
| 
 | |
|    retries = 1;       /* Set default value */
 | |
|    if (eptr != NULL)
 | |
|    {
 | |
|      if (sscanf (eptr, "%d", &retries) != 1) retries = 1; /* Restore default */
 | |
|      else if (retries < 0) retries = 0; /* Allow no retries here */
 | |
|    }
 | |
|  }
 | |
| 
 | |
|  for (;;) /* Retry on EOF */
 | |
|  {
 | |
|    switch (connect)
 | |
|    {
 | |
|      case HP_CONNECT_DEVICE:
 | |
|        n = read (this->fd, data, *len);
 | |
|        break;
 | |
| 
 | |
|      case HP_CONNECT_PIO:
 | |
|        n = sanei_pio_read (this->fd, data, *len);
 | |
|        break;
 | |
| 
 | |
|      case HP_CONNECT_USB:
 | |
|        status = sanei_usb_read_bulk((SANE_Int)this->fd, (SANE_Byte *)data, len);
 | |
|        n = *len;
 | |
|        break;
 | |
| 
 | |
|      case HP_CONNECT_RESERVE:
 | |
|        n = -1;
 | |
|        break;
 | |
| 
 | |
|      default:
 | |
|        n = -1;
 | |
|        break;
 | |
|    }
 | |
|    if ((n != 0) || (retries <= 0)) break;
 | |
|    retries--;
 | |
|    usleep (100*1000);  /* sleep 0.1 seconds */
 | |
|    *len = save_len;    /* Restore value */
 | |
|  }
 | |
| 
 | |
|  if (n == 0) return SANE_STATUS_EOF;
 | |
|  else if (n < 0) return SANE_STATUS_IO_ERROR;
 | |
| 
 | |
|  *len = n;
 | |
|  return status;
 | |
| }
 | |
| 
 | |
| static SANE_Status
 | |
| hp_nonscsi_open (const char *devname, int *fd, HpConnect connect)
 | |
| 
 | |
| {int lfd, flags;
 | |
|  SANE_Int dn;
 | |
|  SANE_Status status = SANE_STATUS_INVAL;
 | |
| 
 | |
| #ifdef _O_RDWR
 | |
|  flags = _O_RDWR;
 | |
| #else
 | |
|  flags = O_RDWR;
 | |
| #endif
 | |
| #ifdef _O_EXCL
 | |
|  flags |= _O_EXCL;
 | |
| #else
 | |
|  flags |= O_EXCL;
 | |
| #endif
 | |
| #ifdef _O_BINARY
 | |
|  flags |= _O_BINARY;
 | |
| #endif
 | |
| #ifdef O_BINARY
 | |
|  flags |= O_BINARY;
 | |
| #endif
 | |
| 
 | |
|  switch (connect)
 | |
|  {
 | |
|    case HP_CONNECT_DEVICE:
 | |
|      lfd = open (devname, flags);
 | |
|      if (lfd < 0)
 | |
|      {
 | |
|         DBG(1, "hp_nonscsi_open: open device %s failed (%s)\n", devname,
 | |
|             strerror (errno) );
 | |
|        status = (errno == EACCES) ? SANE_STATUS_ACCESS_DENIED : SANE_STATUS_INVAL;
 | |
|      }
 | |
|      else
 | |
|        status = SANE_STATUS_GOOD;
 | |
|      break;
 | |
| 
 | |
|    case HP_CONNECT_PIO:
 | |
|      status = sanei_pio_open (devname, &lfd);
 | |
|      break;
 | |
| 
 | |
|    case HP_CONNECT_USB:
 | |
|      DBG(17, "hp_nonscsi_open: open usb with \"%s\"\n", devname);
 | |
|      status = sanei_usb_open (devname, &dn);
 | |
|      lfd = (int)dn;
 | |
|      break;
 | |
| 
 | |
|    case HP_CONNECT_RESERVE:
 | |
|      status = SANE_STATUS_INVAL;
 | |
|      break;
 | |
| 
 | |
|    default:
 | |
|      status = SANE_STATUS_INVAL;
 | |
|      break;
 | |
|  }
 | |
| 
 | |
|  if (status != SANE_STATUS_GOOD)
 | |
|  {
 | |
|     DBG(1, "hp_nonscsi_open: open device %s failed\n", devname);
 | |
|  }
 | |
|  else
 | |
|  {
 | |
|     DBG(17,"hp_nonscsi_open: device %s opened, fd=%d\n", devname, lfd);
 | |
|  }
 | |
| 
 | |
|  if (fd) *fd = lfd;
 | |
| 
 | |
|  return status;
 | |
| }
 | |
| 
 | |
| static void
 | |
| hp_nonscsi_close (int fd, HpConnect connect)
 | |
| 
 | |
| {
 | |
|  switch (connect)
 | |
|  {
 | |
|    case HP_CONNECT_DEVICE:
 | |
|      close (fd);
 | |
|      break;
 | |
| 
 | |
|    case HP_CONNECT_PIO:
 | |
|      sanei_pio_close (fd);
 | |
|      break;
 | |
| 
 | |
|    case HP_CONNECT_USB:
 | |
|      sanei_usb_close (fd);
 | |
|      break;
 | |
| 
 | |
|    case HP_CONNECT_RESERVE:
 | |
|      break;
 | |
| 
 | |
|    default:
 | |
|      break;
 | |
|  }
 | |
|  DBG(17,"hp_nonscsi_close: closed fd=%d\n", fd);
 | |
| }
 | |
| 
 | |
| SANE_Status
 | |
| sanei_hp_nonscsi_new (HpScsi * newp, const char * devname, HpConnect connect)
 | |
| {
 | |
|  HpScsi new;
 | |
|  SANE_Status status;
 | |
|  int iAlreadyOpen = 0;
 | |
| 
 | |
|   new = sanei_hp_allocz(sizeof(*new));
 | |
|   if (!new)
 | |
|     return SANE_STATUS_NO_MEM;
 | |
| 
 | |
|   /* Is the device already open ? */
 | |
|   if ( hp_GetOpenDevice (devname, connect, &new->fd) == SANE_STATUS_GOOD )
 | |
|   {
 | |
|     iAlreadyOpen = 1;
 | |
|   }
 | |
|   else
 | |
|   {
 | |
|     status = hp_nonscsi_open(devname, &new->fd, connect);
 | |
|     if (FAILED(status))
 | |
|     {
 | |
|       DBG(1, "nonscsi_new: open failed (%s)\n", sane_strstatus(status));
 | |
|       sanei_hp_free(new);
 | |
|       return SANE_STATUS_IO_ERROR;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   /* For SCSI-devices we would have the inquire command here */
 | |
|   strncpy ((char *)new->inq_data, "\003zzzzzzzHP      ------          R000",
 | |
|            sizeof (new->inq_data));
 | |
| 
 | |
|   new->bufp = new->buf + HP_SCSI_CMD_LEN;
 | |
|   new->devname = sanei_hp_alloc ( strlen ( devname ) + 1 );
 | |
|   if ( new->devname ) strcpy (new->devname, devname);
 | |
| 
 | |
|   *newp = new;
 | |
| 
 | |
|   /* Remember the open device */
 | |
|   if (!iAlreadyOpen) hp_AddOpenDevice (devname, connect, new->fd);
 | |
| 
 | |
|   return SANE_STATUS_GOOD;
 | |
| }
 | |
| 
 | |
| static void
 | |
| hp_scsi_close (HpScsi this, int completely)
 | |
| {HpConnect connect;
 | |
| 
 | |
|  DBG(3, "scsi_close: closing fd %ld\n", (long)this->fd);
 | |
| 
 | |
|  connect = sanei_hp_scsi_get_connect (this);
 | |
| 
 | |
|  if (!completely)  /* May we keep the device open ? */
 | |
|  {
 | |
|    if ( hp_IsOpenFd (this->fd, connect) == SANE_STATUS_GOOD )
 | |
|    {
 | |
|      DBG(3, "scsi_close: not closing. Keep open\n");
 | |
|      return;
 | |
|    }
 | |
|    
 | |
|  }
 | |
|  assert(this->fd >= 0);
 | |
| 
 | |
|  if (connect != HP_CONNECT_SCSI)
 | |
|    hp_nonscsi_close (this->fd, connect);
 | |
|  else
 | |
|    sanei_scsi_close (this->fd);
 | |
| 
 | |
|  DBG(3,"scsi_close: really closed\n");
 | |
| 
 | |
|  /* Remove a remembered open device */
 | |
|  hp_RemoveOpenFd (this->fd, connect);
 | |
| }
 | |
| 
 | |
| 
 | |
| SANE_Status
 | |
| sanei_hp_scsi_new (HpScsi * newp, const char * devname)
 | |
| {
 | |
|   static hp_byte_t inq_cmd[] = { 0x12, 0, 0, 0, HP_SCSI_INQ_LEN, 0};
 | |
|   static hp_byte_t tur_cmd[] = { 0x00, 0, 0, 0, 0, 0};
 | |
|   size_t	inq_len		= HP_SCSI_INQ_LEN;
 | |
|   HpScsi	new;
 | |
|   HpConnect     connect;
 | |
|   SANE_Status	status;
 | |
|   int iAlreadyOpen = 0;
 | |
| 
 | |
|   connect = sanei_hp_get_connect (devname);
 | |
| 
 | |
|   if (connect != HP_CONNECT_SCSI)
 | |
|     return sanei_hp_nonscsi_new (newp, devname, connect);
 | |
| 
 | |
|   new = sanei_hp_allocz(sizeof(*new));
 | |
|   if (!new)
 | |
|       return SANE_STATUS_NO_MEM;
 | |
| 
 | |
|   /* Is the device still open ? */
 | |
|   if ( hp_GetOpenDevice (devname, connect, &new->fd) == SANE_STATUS_GOOD )
 | |
|   {
 | |
|     iAlreadyOpen = 1;
 | |
|   }
 | |
|   else
 | |
|   {
 | |
|     status = sanei_scsi_open(devname, &new->fd, 0, 0);
 | |
|     if (FAILED(status))
 | |
|       {
 | |
|         DBG(1, "scsi_new: open failed (%s)\n", sane_strstatus(status));
 | |
|         sanei_hp_free(new);
 | |
|         return SANE_STATUS_IO_ERROR;
 | |
|       }
 | |
|   }
 | |
| 
 | |
|   DBG(3, "scsi_inquire: sending INQUIRE\n");
 | |
|   status = sanei_scsi_cmd(new->fd, inq_cmd, 6, new->inq_data, &inq_len);
 | |
|   if (FAILED(status))
 | |
|     {
 | |
|       DBG(1, "scsi_inquire: inquiry failed: %s\n", sane_strstatus(status));
 | |
|       sanei_scsi_close(new->fd);
 | |
|       sanei_hp_free(new);
 | |
|       return status;
 | |
|     }
 | |
| 
 | |
|   {char vendor[9], model[17], rev[5];
 | |
|    memset (vendor, 0, sizeof (vendor));
 | |
|    memset (model, 0, sizeof (model));
 | |
|    memset (rev, 0, sizeof (rev));
 | |
|    memcpy (vendor, new->inq_data + 8, 8);
 | |
|    memcpy (model, new->inq_data + 16, 16);
 | |
|    memcpy (rev, new->inq_data + 32, 4);
 | |
| 
 | |
|    DBG(3, "vendor=%s, model=%s, rev=%s\n", vendor, model, rev);
 | |
|   }
 | |
| 
 | |
|   DBG(3, "scsi_new: sending TEST_UNIT_READY\n");
 | |
|   status = sanei_scsi_cmd(new->fd, tur_cmd, 6, 0, 0);
 | |
|   if (FAILED(status))
 | |
|     {
 | |
|       DBG(1, "hp_scsi_open: test unit ready failed (%s)\n",
 | |
| 	  sane_strstatus(status));
 | |
|       usleep (500*1000); /* Wait 0.5 seconds */
 | |
|       DBG(3, "scsi_new: sending TEST_UNIT_READY second time\n");
 | |
|       status = sanei_scsi_cmd(new->fd, tur_cmd, 6, 0, 0);
 | |
|     }
 | |
| 
 | |
|   if (FAILED(status))
 | |
|     {
 | |
|       DBG(1, "hp_scsi_open: test unit ready failed (%s)\n",
 | |
| 	  sane_strstatus(status));
 | |
| 
 | |
|       sanei_scsi_close(new->fd);
 | |
|       sanei_hp_free(new);
 | |
|       return status; /* Fix problem with non-scanner devices */
 | |
|     }
 | |
| 
 | |
|   new->bufp = new->buf + HP_SCSI_CMD_LEN;
 | |
|   new->devname = sanei_hp_alloc ( strlen ( devname ) + 1 );
 | |
|   if ( new->devname ) strcpy (new->devname, devname);
 | |
| 
 | |
|   *newp = new;
 | |
| 
 | |
|   /* Remember the open device */
 | |
|   if (!iAlreadyOpen) hp_AddOpenDevice (devname, connect, new->fd);
 | |
| 
 | |
|   return SANE_STATUS_GOOD;
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| /* The "completely" parameter was added for OfficeJet support.
 | |
|  * For JetDirect connections, closing and re-opening the scan
 | |
|  * channel is very time consuming.  Also, the OfficeJet G85
 | |
|  * unloads a loaded document in the ADF when the scan channel
 | |
|  * gets closed.  The solution is to "completely" destroy the
 | |
|  * connection, including closing and deallocating the PTAL
 | |
|  * channel, when initially probing the device in hp-device.c,
 | |
|  * but leave it open while the frontend is actually using the
 | |
|  * device (from hp-handle.c), and "completely" destroy it when
 | |
|  * the frontend closes its handle. */
 | |
| void
 | |
| sanei_hp_scsi_destroy (HpScsi this,int completely)
 | |
| {
 | |
|   /* Moved to hp_scsi_close():
 | |
|    * assert(this->fd >= 0);
 | |
|    * DBG(3, "scsi_close: closing fd %d\n", this->fd);
 | |
|    */
 | |
| 
 | |
|   hp_scsi_close (this, completely);
 | |
|   if ( this->devname ) sanei_hp_free (this->devname);
 | |
|   sanei_hp_free(this);
 | |
| }
 | |
| 
 | |
| hp_byte_t *
 | |
| sanei_hp_scsi_inq (HpScsi this)
 | |
| {
 | |
|   return this->inq_data;
 | |
| }
 | |
| 
 | |
| const char *
 | |
| sanei_hp_scsi_vendor (HpScsi this)
 | |
| {
 | |
|   static char buf[9];
 | |
|   memcpy(buf, sanei_hp_scsi_inq(this) + 8, 8);
 | |
|   buf[8] = '\0';
 | |
|   return buf;
 | |
| }
 | |
| 
 | |
| const char *
 | |
| sanei_hp_scsi_model (HpScsi this)
 | |
| {
 | |
| 
 | |
|   static char buf[17];
 | |
|   memcpy(buf, sanei_hp_scsi_inq(this) + 16, 16);
 | |
|   buf[16] = '\0';
 | |
|   return buf;
 | |
| }
 | |
| 
 | |
| const char *
 | |
| sanei_hp_scsi_devicename (HpScsi this)
 | |
| {
 | |
|   return this->devname;
 | |
| }
 | |
| 
 | |
| hp_bool_t
 | |
| sanei_hp_is_active_xpa (HpScsi scsi)
 | |
| {HpDeviceInfo *info;
 | |
|  int model_num;
 | |
| 
 | |
|  info = sanei_hp_device_info_get ( sanei_hp_scsi_devicename  (scsi) );
 | |
|  if (info->active_xpa < 0)
 | |
|  {
 | |
|    model_num = sanei_hp_get_max_model (scsi);
 | |
|    info->active_xpa = (model_num >= 17);
 | |
|    DBG(5,"sanei_hp_is_active_xpa: model=%d, active_xpa=%d\n",
 | |
|        model_num, info->active_xpa);
 | |
|  }
 | |
|  return info->active_xpa;
 | |
| }
 | |
| 
 | |
| int
 | |
| sanei_hp_get_max_model (HpScsi scsi)
 | |
| 
 | |
| {HpDeviceInfo *info;
 | |
| 
 | |
|  info = sanei_hp_device_info_get ( sanei_hp_scsi_devicename  (scsi) );
 | |
|  if (info->max_model < 0)
 | |
|  {enum hp_device_compat_e compat;
 | |
|   int model_num;
 | |
| 
 | |
|    if ( sanei_hp_device_probe_model ( &compat, scsi, &model_num, 0)
 | |
|             == SANE_STATUS_GOOD )
 | |
|      info->max_model = model_num;
 | |
|  }
 | |
|  return info->max_model;
 | |
| }
 | |
| 
 | |
| 
 | |
| int
 | |
| sanei_hp_is_flatbed_adf (HpScsi scsi)
 | |
| 
 | |
| {int model = sanei_hp_get_max_model (scsi);
 | |
| 
 | |
|  return ((model == 2) || (model == 4) || (model == 5) || (model == 8));
 | |
| }
 | |
| 
 | |
| 
 | |
| HpConnect
 | |
| sanei_hp_get_connect (const char *devname)
 | |
| 
 | |
| {const HpDeviceInfo *info;
 | |
|  HpConnect connect = HP_CONNECT_SCSI;
 | |
|  int got_connect_type = 0;
 | |
| 
 | |
|  info = sanei_hp_device_info_get (devname);
 | |
|  if (!info)
 | |
|  {
 | |
|    DBG(1, "sanei_hp_get_connect: Could not get info for %s. Assume SCSI\n",
 | |
|        devname);
 | |
|    connect = HP_CONNECT_SCSI;
 | |
|  }
 | |
|  else
 | |
|  if ( !(info->config_is_up) )
 | |
|  {
 | |
|    DBG(1, "sanei_hp_get_connect: Config not initialized for %s. Assume SCSI\n",
 | |
|        devname);
 | |
|    connect = HP_CONNECT_SCSI;
 | |
|  }
 | |
|  else
 | |
|  {
 | |
|    connect = info->config.connect;
 | |
|    got_connect_type = info->config.got_connect_type;
 | |
|  }
 | |
| 
 | |
|  /* Beware of using a USB-device as a SCSI-device (not 100% perfect) */
 | |
|  if ((connect == HP_CONNECT_SCSI) && !got_connect_type)
 | |
|  {int maybe_usb;
 | |
| 
 | |
|    maybe_usb = (   strstr (devname, "usb")
 | |
|                 || strstr (devname, "uscanner")
 | |
|                 || strstr (devname, "ugen"));
 | |
|    if (maybe_usb)
 | |
|    {static int print_warning = 1;
 | |
| 
 | |
|      if (print_warning)
 | |
|      {
 | |
|        print_warning = 0;
 | |
|        DBG(1,"sanei_hp_get_connect: WARNING\n");
 | |
|        DBG(1,"  Device %s assumed to be SCSI, but device name\n",devname);
 | |
|        DBG(1,"  looks like USB. Will continue with USB.\n");
 | |
|        DBG(1,"  If you really want it as SCSI, add the following\n");
 | |
|        DBG(1,"  to your file .../etc/sane.d/hp.conf:\n");
 | |
|        DBG(1,"    %s\n", devname);
 | |
|        DBG(1,"      option connect-scsi\n");
 | |
|        DBG(1,"  The same warning applies to other device names containing\n");
 | |
|        DBG(1,"  \"usb\", \"uscanner\" or \"ugen\".\n");
 | |
|      }
 | |
|      connect = HP_CONNECT_DEVICE;
 | |
|    }
 | |
|  }
 | |
|  return connect;
 | |
| }
 | |
| 
 | |
| HpConnect
 | |
| sanei_hp_scsi_get_connect (HpScsi this)
 | |
| 
 | |
| {
 | |
|  return sanei_hp_get_connect (sanei_hp_scsi_devicename (this));
 | |
| }
 | |
| 
 | |
| 
 | |
| static SANE_Status
 | |
| hp_scsi_flush (HpScsi this)
 | |
| {
 | |
|   hp_byte_t *	data	= this->buf + HP_SCSI_CMD_LEN;
 | |
|   size_t 	len 	= this->bufp - data;
 | |
|   HpConnect     connect;
 | |
| 
 | |
|   assert(len < HP_SCSI_MAX_WRITE);
 | |
|   if (len == 0)
 | |
|       return SANE_STATUS_GOOD;
 | |
| 
 | |
|   this->bufp = this->buf;
 | |
| 
 | |
|   DBG(16, "scsi_flush: writing %lu bytes:\n", (unsigned long) len);
 | |
|   DBGDUMP(16, data, len);
 | |
| 
 | |
|   *this->bufp++ = 0x0A;
 | |
|   *this->bufp++ = 0;
 | |
|   *this->bufp++ = len >> 16;
 | |
|   *this->bufp++ = len >> 8;
 | |
|   *this->bufp++ = len;
 | |
|   *this->bufp++ = 0;
 | |
| 
 | |
|   connect = sanei_hp_scsi_get_connect (this);
 | |
|   if (connect == HP_CONNECT_SCSI)
 | |
|     return sanei_scsi_cmd (this->fd, this->buf, HP_SCSI_CMD_LEN + len, 0, 0);
 | |
|   else
 | |
|     return hp_nonscsi_write (this, this->buf+HP_SCSI_CMD_LEN, len, connect);
 | |
| }
 | |
| 
 | |
| static size_t
 | |
| hp_scsi_room (HpScsi this)
 | |
| {
 | |
|   return this->buf + HP_SCSI_BUFSIZ - this->bufp;
 | |
| }
 | |
| 
 | |
| static SANE_Status
 | |
| hp_scsi_need (HpScsi this, size_t need)
 | |
| {
 | |
|   assert(need < HP_SCSI_MAX_WRITE);
 | |
| 
 | |
|   if (need > hp_scsi_room(this))
 | |
|       RETURN_IF_FAIL( hp_scsi_flush(this) );
 | |
| 
 | |
|   return SANE_STATUS_GOOD;
 | |
| }
 | |
| 
 | |
| static SANE_Status
 | |
| hp_scsi_write (HpScsi this, const void *data, size_t len)
 | |
| {
 | |
|   if ( len < HP_SCSI_MAX_WRITE )
 | |
|     {
 | |
|       RETURN_IF_FAIL( hp_scsi_need(this, len) );
 | |
|       memcpy(this->bufp, data, len);
 | |
|       this->bufp += len;
 | |
|     }
 | |
|   else
 | |
|     {size_t maxwrite = HP_SCSI_MAX_WRITE - 16;
 | |
|      const char *c_data = (const char *)data;
 | |
| 
 | |
|       while ( len > 0 )
 | |
|         {
 | |
|           if ( maxwrite > len ) maxwrite = len;
 | |
|           RETURN_IF_FAIL( hp_scsi_write(this, c_data, maxwrite) );
 | |
|           c_data += maxwrite;
 | |
|           len -= maxwrite;
 | |
|         }
 | |
|     }
 | |
|   return SANE_STATUS_GOOD;
 | |
| }
 | |
| 
 | |
| static SANE_Status
 | |
| hp_scsi_scl(HpScsi this, HpScl scl, int val)
 | |
| {
 | |
|   char	group	= tolower(SCL_GROUP_CHAR(scl));
 | |
|   char	param	= toupper(SCL_PARAM_CHAR(scl));
 | |
|   int	count;
 | |
| 
 | |
|   assert(IS_SCL_CONTROL(scl) || IS_SCL_COMMAND(scl));
 | |
|   assert(isprint(group) && isprint(param));
 | |
| 
 | |
|   RETURN_IF_FAIL( hp_scsi_need(this, 10) );
 | |
| 
 | |
|   /* Dont try to optimize SCL-commands like using <ESC>*a1b0c5T */
 | |
|   /* Some scanners have problems with it (e.g. HP Photosmart Photoscanner */
 | |
|   /* with window position/extent, resolution) */
 | |
|   count = sprintf((char *)this->bufp, "\033*%c%d%c", group, val, param);
 | |
|   this->bufp += count;
 | |
| 
 | |
|   assert(count > 0 && this->bufp < this->buf + HP_SCSI_BUFSIZ);
 | |
| 
 | |
|   return hp_scsi_flush(this);
 | |
| }
 | |
| 
 | |
| /* Read it bytewise */
 | |
| static SANE_Status
 | |
| hp_scsi_read_slow (HpScsi this, void * dest, size_t *len)
 | |
| {static hp_byte_t read_cmd[6] = { 0x08, 0, 0, 0, 0, 0 };
 | |
|  size_t leftover = *len;
 | |
|  SANE_Status status = SANE_STATUS_GOOD;
 | |
|  unsigned char *start_dest = (unsigned char *)dest;
 | |
|  unsigned char *next_dest = start_dest;
 | |
| 
 | |
|  DBG(16, "hp_scsi_read_slow: Start reading %d bytes bytewise\n", (int)*len);
 | |
| 
 | |
|  while (leftover > 0)  /* Until we got all the bytes */
 | |
|  {size_t one = 1;
 | |
| 
 | |
|    read_cmd[2] = 0;
 | |
|    read_cmd[3] = 0;
 | |
|    read_cmd[4] = 1;   /* Read one byte */
 | |
| 
 | |
|    status = sanei_scsi_cmd (this->fd, read_cmd, sizeof(read_cmd),
 | |
|                             next_dest, &one);
 | |
|    if ((status != SANE_STATUS_GOOD) || (one != 1))
 | |
|    {
 | |
|      DBG(250,"hp_scsi_read_slow: Reading byte %d: status=%s, len=%d\n",
 | |
|          (int)(next_dest-start_dest), sane_strstatus(status), (int)one);
 | |
|    }
 | |
| 
 | |
|    if (status != SANE_STATUS_GOOD) break;  /* Finish on error */
 | |
| 
 | |
|    next_dest++;
 | |
|    leftover--;
 | |
|  }
 | |
| 
 | |
|  *len = next_dest-start_dest; /* This is the number of bytes we got */
 | |
| 
 | |
|  DBG(16, "hp_scsi_read_slow: Got %d bytes\n", (int)*len);
 | |
| 
 | |
|  if ((status != SANE_STATUS_GOOD) && (*len > 0))
 | |
|  {
 | |
|    DBG(16, "We got some data. Ignore the error \"%s\"\n",
 | |
|        sane_strstatus(status));
 | |
|    status = SANE_STATUS_GOOD;
 | |
|  }
 | |
|  return status;
 | |
| }
 | |
| 
 | |
| /* The OfficeJets tend to return inquiry responses containing array
 | |
|  * data in two packets.  The added "isResponse" parameter tells
 | |
|  * whether we should keep reading until we get
 | |
|  * a well-formed response.  Naturally, this parameter would be zero
 | |
|  * when reading scan data. */
 | |
| static SANE_Status
 | |
| hp_scsi_read (HpScsi this, void * dest, size_t *len, int isResponse)
 | |
| {
 | |
|   HpConnect connect;
 | |
| 
 | |
|   RETURN_IF_FAIL( hp_scsi_flush(this) );
 | |
| 
 | |
|   connect = sanei_hp_scsi_get_connect (this);
 | |
|   if (connect == HP_CONNECT_SCSI)
 | |
|   {int read_bytewise = 0;
 | |
| 
 | |
|     if (*len <= 32)   /* Is it a candidate for reading bytewise ? */
 | |
|     {const HpDeviceInfo *info;
 | |
| 
 | |
|       info = sanei_hp_device_info_get (sanei_hp_scsi_devicename (this));
 | |
|       if ((info != NULL) && (info->config_is_up) && info->config.dumb_read)
 | |
|         read_bytewise = 1;
 | |
|     }
 | |
| 
 | |
|     if ( ! read_bytewise )
 | |
|     {static hp_byte_t read_cmd[6] = { 0x08, 0, 0, 0, 0, 0 };
 | |
|       read_cmd[2] = *len >> 16;
 | |
|       read_cmd[3] = *len >> 8;
 | |
|       read_cmd[4] = *len;
 | |
| 
 | |
|       RETURN_IF_FAIL( sanei_scsi_cmd (this->fd, read_cmd,
 | |
|                                       sizeof(read_cmd), dest, len) );
 | |
|     }
 | |
|     else
 | |
|     {
 | |
|       RETURN_IF_FAIL (hp_scsi_read_slow (this, dest, len));
 | |
|     }
 | |
|   }
 | |
|   else
 | |
|   {
 | |
|     RETURN_IF_FAIL( hp_nonscsi_read (this, dest, len, connect, isResponse) );
 | |
|   }
 | |
|   DBG(16, "scsi_read:  %lu bytes:\n", (unsigned long) *len);
 | |
|   DBGDUMP(16, dest, *len);
 | |
|   return SANE_STATUS_GOOD;
 | |
| }
 | |
| 
 | |
| 
 | |
| static int signal_caught = 0;
 | |
| 
 | |
| static RETSIGTYPE
 | |
| signal_catcher (int sig)
 | |
| {
 | |
|   DBG(1,"signal_catcher(sig=%d): old signal_caught=%d\n",sig,signal_caught);
 | |
|   if (!signal_caught)
 | |
|       signal_caught = sig;
 | |
| }
 | |
| 
 | |
| static void
 | |
| hp_data_map (register const unsigned char *map, register int count,
 | |
|              register unsigned char *data)
 | |
| {
 | |
|   if (count <= 0) return;
 | |
|   while (count--)
 | |
|   {
 | |
|     *data = map[*data];
 | |
|     data++;
 | |
|   }
 | |
| }
 | |
| 
 | |
| static const unsigned char *
 | |
| hp_get_simulation_map (const char *devname, const HpDeviceInfo *info)
 | |
| {
 | |
|  hp_bool_t     sim_gamma, sim_brightness, sim_contrast;
 | |
|  int           k, ind;
 | |
|  const unsigned char *map = NULL;
 | |
|  static unsigned char map8x8[256];
 | |
| 
 | |
|   sim_gamma = info->simulate.gamma_simulate;
 | |
|   sim_brightness = sanei_hp_device_simulate_get (devname, SCL_BRIGHTNESS);
 | |
|   sim_contrast = sanei_hp_device_simulate_get (devname, SCL_CONTRAST);
 | |
| 
 | |
|   if ( sim_gamma )
 | |
|   {
 | |
|     map = &(info->simulate.gamma_map[0]);
 | |
|   }
 | |
|   else if ( sim_brightness && sim_contrast )
 | |
|   {
 | |
|     for (k = 0; k < 256; k++)
 | |
|     {
 | |
|       ind = info->simulate.contrast_map[k];
 | |
|       map8x8[k] = info->simulate.brightness_map[ind];
 | |
|     }
 | |
|     map = &(map8x8[0]);
 | |
|   }
 | |
|   else if ( sim_brightness )
 | |
|     map = &(info->simulate.brightness_map[0]);
 | |
|   else if ( sim_contrast )
 | |
|     map = &(info->simulate.contrast_map[0]);
 | |
| 
 | |
|   return map;
 | |
| }
 | |
| 
 | |
| 
 | |
| /* Check the native byte order on the local machine */
 | |
| static hp_bool_t
 | |
| is_lowbyte_first_byteorder (void)
 | |
| 
 | |
| {unsigned short testvar = 1;
 | |
|  unsigned char *testptr = (unsigned char *)&testvar;
 | |
| 
 | |
|  if (sizeof (unsigned short) == 2)
 | |
|    return (testptr[0] == 1);
 | |
|  else if (sizeof (unsigned short) == 4)
 | |
|    return ((testptr[0] == 1) || (testptr[2] == 1));
 | |
|  else
 | |
|    return (   (testptr[0] == 1) || (testptr[2] == 1)
 | |
|            || (testptr[4] == 1) || (testptr[6] == 1));
 | |
| }
 | |
| 
 | |
| /* The SANE standard defines that 2-byte data must use the full 16 bit range.
 | |
|  * Byte order returned by the backend must be native byte order.
 | |
|  * Scaling to 16 bit and byte order is achived by hp_scale_to_16bit.
 | |
|  * for >8 bits data, take the two data bytes and scale their content
 | |
|  * to the full 16 bit range, using
 | |
|  *     scaled = unscaled << (newlen - oldlen) +
 | |
|  *              unscaled >> (oldlen - (newlen - oldlen)),
 | |
|  * with newlen=16 and oldlen the original bit depth.
 | |
|  */
 | |
| static void
 | |
| hp_scale_to_16bit(int count, register unsigned char *data, int depth,
 | |
|                   hp_bool_t invert)
 | |
| {
 | |
|     register unsigned int tmp;
 | |
|     register unsigned int mask;
 | |
|     register hp_bool_t lowbyte_first = is_lowbyte_first_byteorder ();
 | |
|     unsigned int shift1 = 16 - depth;
 | |
|     unsigned int shift2 = 2*depth - 16;
 | |
|     int k;
 | |
| 
 | |
|     if (count <= 0) return;
 | |
| 
 | |
|     mask = 1;
 | |
|     for (k = 1; k < depth; k++) mask |= (1 << k);
 | |
| 
 | |
|     if (lowbyte_first)
 | |
|     {
 | |
|       while (count--) {
 | |
|          tmp = ((((unsigned int)data[0])<<8) | ((unsigned int)data[1])) & mask;
 | |
|          tmp = (tmp << shift1) + (tmp >> shift2);
 | |
|          if (invert) tmp = ~tmp;
 | |
|          *data++ = tmp & 255U;
 | |
|          *data++ = (tmp >> 8) & 255U;
 | |
|       }
 | |
|     }
 | |
|     else  /* Highbyte first */
 | |
|     {
 | |
|       while (count--) {
 | |
|          tmp = ((((unsigned int)data[0])<<8) | ((unsigned int)data[1])) & mask;
 | |
|          tmp = (tmp << shift1) + (tmp >> shift2);
 | |
|          if (invert) tmp = ~tmp;
 | |
|          *data++ = (tmp >> 8) & 255U;
 | |
|          *data++ = tmp & 255U;
 | |
|       }
 | |
|     }
 | |
| }
 | |
| 
 | |
| 
 | |
| static void
 | |
| hp_scale_to_8bit(int count, register unsigned char *data, int depth,
 | |
|                  hp_bool_t invert)
 | |
| {
 | |
|     register unsigned int tmp, mask;
 | |
|     register hp_bool_t lowbyte_first = is_lowbyte_first_byteorder ();
 | |
|     unsigned int shift1 = depth-8;
 | |
|     int k;
 | |
|     unsigned char *dataout = data;
 | |
| 
 | |
|     if ((count <= 0) || (shift1 <= 0)) return;
 | |
| 
 | |
|     mask = 1;
 | |
|     for (k = 1; k < depth; k++) mask |= (1 << k);
 | |
| 
 | |
|     if (lowbyte_first)
 | |
|     {
 | |
|       while (count--) {
 | |
|          tmp = ((((unsigned int)data[0])<<8) | ((unsigned int)data[1])) & mask;
 | |
|          tmp >>= shift1;
 | |
|          if (invert) tmp = ~tmp;
 | |
|          *(dataout++) = tmp & 255U;
 | |
|          data += 2;
 | |
|       }
 | |
|     }
 | |
|     else  /* Highbyte first */
 | |
|     {
 | |
|       while (count--) {
 | |
|          tmp = ((((unsigned int)data[0])<<8) | ((unsigned int)data[1])) & mask;
 | |
|          tmp >>= shift1;
 | |
|          if (invert) tmp = ~tmp;
 | |
|          *(dataout++) = tmp & 255U;
 | |
|          data += 2;
 | |
|       }
 | |
|     }
 | |
| }
 | |
| 
 | |
| static void
 | |
| hp_soft_invert(int count, register unsigned char *data) {
 | |
| 	while (count>0) {
 | |
| 		*data = ~(*data);
 | |
| 		data++;
 | |
| 		count--;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static PROCDATA_HANDLE *
 | |
| process_data_init (HpProcessData *procdata, const unsigned char *map,
 | |
|                    int outfd, hp_bool_t use_imgbuf)
 | |
| 
 | |
| {PROCDATA_HANDLE *ph = sanei_hp_alloc (sizeof (PROCDATA_HANDLE));
 | |
|  int tsz;
 | |
| 
 | |
|  if (ph == NULL) return NULL;
 | |
| 
 | |
|  memset (ph, 0, sizeof (*ph));
 | |
|  memcpy (&(ph->procdata), procdata, sizeof (*procdata));
 | |
|  procdata = &(ph->procdata);
 | |
| 
 | |
|  tsz = (HP_TMP_BUF_SIZE <= 0) ? procdata->bytes_per_line : HP_TMP_BUF_SIZE;
 | |
|  ph->tmp_buf = sanei_hp_alloc (tsz);
 | |
|  if (ph->tmp_buf == NULL)
 | |
|  {
 | |
|    sanei_hp_free (ph);
 | |
|    return NULL;
 | |
|  }
 | |
|  ph->tmp_buf_size = tsz;
 | |
|  ph->tmp_buf_len = 0;
 | |
| 
 | |
|  ph->map = map;
 | |
|  ph->outfd = outfd;
 | |
| 
 | |
|  if ( procdata->mirror_vertical || use_imgbuf)
 | |
|  {
 | |
|    tsz = procdata->lines*procdata->bytes_per_line;
 | |
|    if (procdata->out8) tsz /= 2;
 | |
|    ph->image_ptr = ph->image_buf = sanei_hp_alloc (tsz);
 | |
|    if ( !ph->image_buf )
 | |
|    {
 | |
|      procdata->mirror_vertical = 0;
 | |
|      ph->image_buf_size = 0;
 | |
|      DBG(1, "process_scanline_init: Not enough memory to mirror image\n");
 | |
|    }
 | |
|    else
 | |
|      ph->image_buf_size = tsz;
 | |
|  }
 | |
| 
 | |
|  ph->wr_ptr = ph->wr_buf;
 | |
|  ph->wr_buf_size = ph->wr_left = sizeof (ph->wr_buf);
 | |
| 
 | |
|  return ph;
 | |
| }
 | |
| 
 | |
| 
 | |
| static SANE_Status
 | |
| process_data_write (PROCDATA_HANDLE *ph, unsigned char *data, int nbytes)
 | |
| 
 | |
| {int ncopy;
 | |
| 
 | |
|  if (ph == NULL) return SANE_STATUS_INVAL;
 | |
| 
 | |
|  /* Fill up write buffer */
 | |
|  ncopy = ph->wr_left;
 | |
|  if (ncopy > nbytes) ncopy = nbytes;
 | |
| 
 | |
|  memcpy (ph->wr_ptr, data, ncopy);
 | |
|  ph->wr_ptr += ncopy;
 | |
|  ph->wr_left -= ncopy;
 | |
|  data += ncopy;
 | |
|  nbytes -= ncopy;
 | |
| 
 | |
|  if ( ph->wr_left > 0 )  /* Did not fill up the write buffer ? Finished */
 | |
|    return SANE_STATUS_GOOD;
 | |
| 
 | |
|  DBG(12, "process_data_write: write %d bytes\n", ph->wr_buf_size);
 | |
|  /* Don't write data if we got a signal in the meantime */
 | |
|  if (   signal_caught
 | |
|      || (write (ph->outfd, ph->wr_buf, ph->wr_buf_size) != ph->wr_buf_size))
 | |
|  {
 | |
|    DBG(1, "process_data_write: write failed: %s\n",
 | |
|        signal_caught ? "signal caught" : strerror(errno));
 | |
|    return SANE_STATUS_IO_ERROR;
 | |
|  }
 | |
|  ph->wr_ptr = ph->wr_buf;
 | |
|  ph->wr_left = ph->wr_buf_size;
 | |
| 
 | |
|  /* For large amount of data write it from data-buffer */
 | |
|  while ( nbytes > ph->wr_buf_size )
 | |
|  {
 | |
|    if (   signal_caught
 | |
|        || (write (ph->outfd, data, ph->wr_buf_size) != ph->wr_buf_size))
 | |
|    {
 | |
|      DBG(1, "process_data_write: write failed: %s\n",
 | |
|          signal_caught ? "signal caught" : strerror(errno));
 | |
|      return SANE_STATUS_IO_ERROR;
 | |
|    }
 | |
|    nbytes -= ph->wr_buf_size;
 | |
|    data += ph->wr_buf_size;
 | |
|  }
 | |
| 
 | |
|  if ( nbytes > 0 ) /* Something left ? Save it to (empty) write buffer */
 | |
|  {
 | |
|    memcpy (ph->wr_ptr, data, nbytes);
 | |
|    ph->wr_ptr += nbytes;
 | |
|    ph->wr_left -= nbytes;
 | |
|  }
 | |
|  return SANE_STATUS_GOOD;
 | |
| }
 | |
| 
 | |
| static SANE_Status
 | |
| process_scanline (PROCDATA_HANDLE *ph, unsigned char *linebuf,
 | |
|                   int bytes_per_line)
 | |
| 
 | |
| {int out_bytes_per_line = bytes_per_line;
 | |
|  HpProcessData *procdata;
 | |
| 
 | |
|  if (ph == NULL) return SANE_STATUS_INVAL;
 | |
|  procdata = &(ph->procdata);
 | |
| 
 | |
|  if ( ph->map )
 | |
|    hp_data_map (ph->map, bytes_per_line, linebuf);
 | |
| 
 | |
|  if (procdata->bits_per_channel > 8)
 | |
|  {
 | |
|    if (procdata->out8)
 | |
|    {
 | |
|      hp_scale_to_8bit( bytes_per_line/2, linebuf,
 | |
|                        procdata->bits_per_channel,
 | |
|                        procdata->invert);
 | |
|      out_bytes_per_line /= 2;
 | |
|    }
 | |
|    else
 | |
|    {
 | |
|      hp_scale_to_16bit( bytes_per_line/2, linebuf,
 | |
|                         procdata->bits_per_channel,
 | |
|                         procdata->invert);
 | |
|    }
 | |
|  } else if (procdata->invert) {
 | |
|    hp_soft_invert(bytes_per_line,linebuf);
 | |
|  }
 | |
| 
 | |
|  if ( ph->image_buf )
 | |
|  {
 | |
|    DBG(5, "process_scanline: save in memory\n");
 | |
| 
 | |
|    if (    ph->image_ptr+out_bytes_per_line-1
 | |
|         <= ph->image_buf+ph->image_buf_size-1 )
 | |
|    {
 | |
|      memcpy(ph->image_ptr, linebuf, out_bytes_per_line);
 | |
|      ph->image_ptr += out_bytes_per_line;
 | |
|    }
 | |
|    else
 | |
|    {
 | |
|      DBG(1, "process_scanline: would exceed image buffer\n");
 | |
|    }
 | |
|  }
 | |
|  else /* Save scanlines in a bigger buffer. */
 | |
|  {    /* Otherwise we will get performance problems */
 | |
| 
 | |
|    RETURN_IF_FAIL ( process_data_write (ph, linebuf, out_bytes_per_line) );
 | |
|  }
 | |
|  return SANE_STATUS_GOOD;
 | |
| }
 | |
| 
 | |
| 
 | |
| static SANE_Status
 | |
| process_data (PROCDATA_HANDLE *ph, unsigned char *read_ptr, int nread)
 | |
| 
 | |
| {int bytes_left;
 | |
|  HpProcessData *procdata;
 | |
| 
 | |
|  if (nread <= 0) return SANE_STATUS_GOOD;
 | |
| 
 | |
|  if (ph == NULL) return SANE_STATUS_INVAL;
 | |
| 
 | |
|  procdata = &(ph->procdata);
 | |
|  if ( ph->tmp_buf_len > 0 )  /* Something left ? */
 | |
|  {
 | |
|    bytes_left = ph->tmp_buf_size - ph->tmp_buf_len;
 | |
|    if (nread < bytes_left)  /* All to buffer ? */
 | |
|    {
 | |
|      memcpy (ph->tmp_buf+ph->tmp_buf_len, read_ptr, nread);
 | |
|      ph->tmp_buf_len += nread;
 | |
|      return SANE_STATUS_GOOD;
 | |
|    }
 | |
|    memcpy (ph->tmp_buf+ph->tmp_buf_len, read_ptr, bytes_left);
 | |
|    read_ptr += bytes_left;
 | |
|    nread -= bytes_left;
 | |
|    RETURN_IF_FAIL ( process_scanline (ph, ph->tmp_buf, ph->tmp_buf_size) );
 | |
|    ph->tmp_buf_len = 0;
 | |
|  }
 | |
|  while (nread > 0)
 | |
|  {
 | |
|    if (nread >= ph->tmp_buf_size)
 | |
|    {
 | |
|      RETURN_IF_FAIL ( process_scanline (ph, read_ptr, ph->tmp_buf_size) );
 | |
|      read_ptr += ph->tmp_buf_size;
 | |
|      nread -= ph->tmp_buf_size;
 | |
|    }
 | |
|    else
 | |
|    {
 | |
|      memcpy (ph->tmp_buf, read_ptr, nread);
 | |
|      ph->tmp_buf_len = nread;
 | |
|      nread = 0;
 | |
|    }
 | |
|  }
 | |
|  return SANE_STATUS_GOOD;
 | |
| }
 | |
| 
 | |
| 
 | |
| static SANE_Status
 | |
| process_data_flush (PROCDATA_HANDLE *ph)
 | |
| 
 | |
| {SANE_Status status = SANE_STATUS_GOOD;
 | |
|  HpProcessData *procdata;
 | |
|  unsigned char *image_data;
 | |
|  size_t image_len;
 | |
|  int num_lines, bytes_per_line;
 | |
|  int nbytes;
 | |
| 
 | |
|  if (ph == NULL) return SANE_STATUS_INVAL;
 | |
| 
 | |
|  if ( ph->tmp_buf_len > 0 )
 | |
|    process_scanline (ph, ph->tmp_buf, ph->tmp_buf_len);
 | |
| 
 | |
|  if ( ph->wr_left != ph->wr_buf_size ) /* Something in write buffer ? */
 | |
|  {
 | |
|    nbytes = ph->wr_buf_size - ph->wr_left;
 | |
|    if ( signal_caught || (write (ph->outfd, ph->wr_buf, nbytes) != nbytes))
 | |
|    {
 | |
|      DBG(1, "process_data_flush: write failed: %s\n",
 | |
|          signal_caught ? "signal caught" : strerror(errno));
 | |
|      return SANE_STATUS_IO_ERROR;
 | |
|    }
 | |
|    ph->wr_ptr = ph->wr_buf;
 | |
|    ph->wr_left = ph->wr_buf_size;
 | |
|  }
 | |
| 
 | |
|  procdata = &(ph->procdata);
 | |
|  if ( ph->image_buf )
 | |
|  {
 | |
|    bytes_per_line = procdata->bytes_per_line;
 | |
|    if (procdata->out8) bytes_per_line /= 2;
 | |
|    image_len = (size_t) (ph->image_ptr - ph->image_buf);
 | |
|    num_lines = ((int)(image_len + bytes_per_line-1)) / bytes_per_line;
 | |
| 
 | |
|    DBG(3, "process_data_finish: write %d bytes from memory...\n",
 | |
|        (int)image_len);
 | |
| 
 | |
|    if ( procdata->mirror_vertical )
 | |
|    {
 | |
|      image_data = ph->image_buf + (num_lines-1) * bytes_per_line;
 | |
|      while (num_lines > 0 )
 | |
|      {
 | |
|        if (   signal_caught
 | |
|            || (write(ph->outfd, image_data, bytes_per_line) != bytes_per_line))
 | |
|        {
 | |
|          DBG(1,"process_data_finish: write from memory failed: %s\n",
 | |
|              signal_caught ? "signal caught" : strerror(errno));
 | |
|          status = SANE_STATUS_IO_ERROR;
 | |
|          break;
 | |
|        }
 | |
|        num_lines--;
 | |
|        image_data -= bytes_per_line;
 | |
|      }
 | |
|    }
 | |
|    else
 | |
|    {
 | |
|      image_data = ph->image_buf;
 | |
|      while (num_lines > 0 )
 | |
|      {
 | |
|        if (   signal_caught
 | |
|            || (write(ph->outfd, image_data, bytes_per_line) != bytes_per_line))
 | |
|        {
 | |
|          DBG(1,"process_data_finish: write from memory failed: %s\n",
 | |
|              signal_caught ? "signal caught" : strerror(errno));
 | |
|          status = SANE_STATUS_IO_ERROR;
 | |
|          break;
 | |
|        }
 | |
|        num_lines--;
 | |
|        image_data += bytes_per_line;
 | |
|      }
 | |
|    }
 | |
|  }
 | |
|  return status;
 | |
| }
 | |
| 
 | |
| 
 | |
| static void
 | |
| process_data_finish (PROCDATA_HANDLE *ph)
 | |
| 
 | |
| {
 | |
|  DBG(12, "process_data_finish called\n");
 | |
| 
 | |
|  if (ph == NULL) return;
 | |
| 
 | |
|  if (ph->image_buf != NULL) sanei_hp_free (ph->image_buf);
 | |
| 
 | |
|  sanei_hp_free (ph->tmp_buf);
 | |
|  sanei_hp_free (ph);
 | |
| }
 | |
| 
 | |
| 
 | |
| SANE_Status
 | |
| sanei_hp_scsi_pipeout (HpScsi this, int outfd, HpProcessData *procdata)
 | |
| {
 | |
|   /* We will catch these signals, and rethrow them after cleaning up,
 | |
|    * anything not in this list, we will ignore. */
 | |
|   static int kill_sig[] = {
 | |
|       SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGPIPE, SIGALRM, SIGTERM,
 | |
|       SIGUSR1, SIGUSR2, SIGBUS,
 | |
| #ifdef SIGSTKFLT
 | |
|       SIGSTKFLT,
 | |
| #endif
 | |
| #ifdef SIGIO
 | |
|       SIGIO,
 | |
| #else
 | |
| # ifdef SIGPOLL
 | |
|       SIGPOLL,
 | |
| # endif
 | |
| #endif
 | |
| #ifdef SIGXCPU
 | |
|       SIGXCPU,
 | |
| #endif
 | |
| #ifdef SIGXFSZ
 | |
|       SIGXFSZ,
 | |
| #endif
 | |
| #ifdef SIGVTALRM
 | |
|       SIGVTALRM,
 | |
| #endif
 | |
| #ifdef SIGPWR
 | |
|       SIGPWR,
 | |
| #endif
 | |
|   };
 | |
| #define HP_NSIGS (sizeof(kill_sig)/sizeof(kill_sig[0]))
 | |
|   struct SIGACTION old_handler[HP_NSIGS];
 | |
|   struct SIGACTION sa;
 | |
|   sigset_t	old_set, sig_set;
 | |
|   int		i;
 | |
|   int           bits_per_channel = procdata->bits_per_channel;
 | |
| 
 | |
| #define HP_PIPEBUF	32768
 | |
|   SANE_Status	status	= SANE_STATUS_GOOD;
 | |
|   struct {
 | |
|       size_t	len;
 | |
|       void *	id;
 | |
|       hp_byte_t	cmd[6];
 | |
|       hp_byte_t	data[HP_PIPEBUF];
 | |
|   } 	buf[2], *req = NULL;
 | |
| 
 | |
|   int		reqs_completed = 0;
 | |
|   int		reqs_issued = 0;
 | |
|   char          *image_buf = 0;
 | |
|   char          *read_buf = 0;
 | |
|   const HpDeviceInfo *info;
 | |
|   const char    *devname = sanei_hp_scsi_devicename (this);
 | |
|   int           enable_requests = 1;
 | |
|   int           enable_image_buffering = 0;
 | |
|   const unsigned char *map = NULL;
 | |
|   HpConnect     connect;
 | |
|   PROCDATA_HANDLE *ph = NULL;
 | |
|   size_t count = procdata->lines * procdata->bytes_per_line;
 | |
| 
 | |
|   RETURN_IF_FAIL( hp_scsi_flush(this) );
 | |
| 
 | |
|   connect = sanei_hp_get_connect (devname);
 | |
|   info = sanei_hp_device_info_get (devname);
 | |
| 
 | |
|   assert (info);
 | |
| 
 | |
|   if ( info->config_is_up )
 | |
|   {
 | |
|     enable_requests = info->config.use_scsi_request;
 | |
|     enable_image_buffering = info->config.use_image_buffering;
 | |
|   }
 | |
|   else
 | |
|   {
 | |
|     enable_requests = 0;
 | |
|   }
 | |
| 
 | |
|   if (connect != HP_CONNECT_SCSI)
 | |
|     enable_requests = 0;
 | |
| 
 | |
|   /* Currently we can only simulate 8 bits mapping */
 | |
|   if (bits_per_channel == 8)
 | |
|     map = hp_get_simulation_map (devname, info);
 | |
| 
 | |
|   sigfillset(&sig_set);
 | |
|   sigprocmask(SIG_BLOCK, &sig_set, &old_set);
 | |
| 
 | |
|   memset(&sa, 0, sizeof(sa));
 | |
|   sa.sa_handler = signal_catcher;
 | |
|   sigfillset(&sa.sa_mask);
 | |
| 
 | |
|   sigemptyset(&sig_set);
 | |
|   for (i = 0; i < (int)(HP_NSIGS); i++)
 | |
|     {
 | |
|       sigaction(kill_sig[i], &sa, &old_handler[i]);
 | |
|       sigaddset(&sig_set, kill_sig[i]);
 | |
|     }
 | |
|   signal_caught = 0;
 | |
|   sigprocmask(SIG_UNBLOCK, &sig_set, 0);
 | |
| 
 | |
|   /* Wait for front button push ? */
 | |
|   if ( procdata->startscan )
 | |
|   {
 | |
|     for (;;)
 | |
|     {int val = 0;
 | |
| 
 | |
|        if (signal_caught) goto quit;
 | |
|        sanei_hp_scl_inquire (this, SCL_FRONT_BUTTON, &val, 0, 0);
 | |
|        if (val) break;
 | |
|        usleep ((unsigned long)333*1000); /* Wait 1/3 second */
 | |
|     }
 | |
|     status = sanei_hp_scl_startScan (this, procdata->startscan);
 | |
|     if (status != SANE_STATUS_GOOD )
 | |
|     {
 | |
|       DBG(1, "do_read: Error starting scan in reader process\n");
 | |
|       goto quit;
 | |
|     }
 | |
|   }
 | |
|   ph = process_data_init (procdata, map, outfd, enable_image_buffering);
 | |
| 
 | |
|   if ( ph == NULL )
 | |
|   {
 | |
|     DBG(1, "do_read: Error with process_data_init()\n");
 | |
|     goto quit;
 | |
|   }
 | |
| 
 | |
|   DBG(1, "do_read: Start reading data from scanner\n");
 | |
| 
 | |
|   if (enable_requests)   /* Issue SCSI-requests ? */
 | |
|   {
 | |
|     while (count > 0 || reqs_completed < reqs_issued)
 | |
|     {
 | |
|       while (count > 0 && reqs_issued < reqs_completed + 2)
 | |
| 	{
 | |
| 	  req = buf + (reqs_issued++ % 2);
 | |
| 
 | |
| 	  req->len = HP_PIPEBUF;
 | |
| 	  if (count < req->len)
 | |
| 	      req->len = count;
 | |
| 	  count -= req->len;
 | |
| 
 | |
| 	  req->cmd[0] = 0x08;
 | |
| 	  req->cmd[1] = 0;
 | |
| 	  req->cmd[2] = req->len >> 16;
 | |
| 	  req->cmd[3] = req->len >> 8;
 | |
| 	  req->cmd[4] = req->len;
 | |
| 	  req->cmd[5] = 0;
 | |
| 
 | |
| 	  DBG(3, "do_read: entering request to read %lu bytes\n",
 | |
| 	      (unsigned long) req->len);
 | |
| 
 | |
| 	  status = sanei_scsi_req_enter(this->fd, req->cmd, 6,
 | |
| 				      req->data, &req->len, &req->id);
 | |
| 	  if (status != SANE_STATUS_GOOD)
 | |
| 	    {
 | |
| 	      DBG(1, "do_read: Error from scsi_req_enter: %s\n",
 | |
| 		  sane_strstatus(status));
 | |
| 	      goto quit;
 | |
| 	    }
 | |
| 	  if (signal_caught)
 | |
| 	      goto quit;
 | |
| 	}
 | |
| 
 | |
|       if (signal_caught)
 | |
| 	  goto quit;
 | |
| 
 | |
|       assert(reqs_completed < reqs_issued);
 | |
|       req = buf + (reqs_completed++ % 2);
 | |
| 
 | |
|       DBG(3, "do_read: waiting for data\n");
 | |
|       status = sanei_scsi_req_wait(req->id);
 | |
|       if (status != SANE_STATUS_GOOD)
 | |
| 	{
 | |
| 	  DBG(1, "do_read: Error from scsi_req_wait: %s\n",
 | |
| 	      sane_strstatus(status));
 | |
| 	  goto quit;
 | |
| 	}
 | |
|       if (signal_caught)
 | |
| 	  goto quit;
 | |
| 
 | |
|       status = process_data (ph, (unsigned char *)req->data, (int)req->len);
 | |
|       if ( status != SANE_STATUS_GOOD )
 | |
|       {
 | |
|         DBG(1,"do_read: Error in process_data\n");
 | |
|         goto quit;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
|   else  /* Read directly */
 | |
|   {
 | |
|     read_buf = sanei_hp_alloc ( HP_PIPEBUF );
 | |
|     if (!read_buf)
 | |
|     {
 | |
|       DBG(1, "do_read: not enough memory for read buffer\n");
 | |
|       goto quit;
 | |
|     }
 | |
| 
 | |
|     while (count > 0)
 | |
|     {size_t nread;
 | |
| 
 | |
|       if (signal_caught)
 | |
| 	  goto quit;
 | |
| 
 | |
|       DBG(5, "do_read: %lu bytes left to read\n", (unsigned long)count);
 | |
| 
 | |
|       nread = HP_PIPEBUF;
 | |
|       if (nread > count) nread = count;
 | |
| 
 | |
|       DBG(3, "do_read: try to read data (%lu bytes)\n", (unsigned long)nread);
 | |
| 
 | |
|       status = hp_scsi_read (this, read_buf, &nread, 0);
 | |
|       if (status != SANE_STATUS_GOOD)
 | |
|       {
 | |
|         DBG(1, "do_read: Error from scsi_read: %s\n",sane_strstatus(status));
 | |
|         goto quit;
 | |
|       }
 | |
| 
 | |
|       DBG(3, "do_read: got %lu bytes\n", (unsigned long)nread);
 | |
| 
 | |
|       if (nread <= 0)
 | |
|       {
 | |
|         DBG(1, "do_read: Nothing read\n");
 | |
|         continue;
 | |
|       }
 | |
| 
 | |
|       status = process_data (ph, (unsigned char *)read_buf, (int)nread);
 | |
|       if ( status != SANE_STATUS_GOOD )
 | |
|       {
 | |
|         DBG(1,"do_read: Error in process_data\n");
 | |
|         goto quit;
 | |
|       }
 | |
|       count -= nread;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   process_data_flush (ph);
 | |
| 
 | |
| quit:
 | |
| 
 | |
|   process_data_finish (ph);
 | |
| 
 | |
|   if ( image_buf ) sanei_hp_free ( image_buf );
 | |
|   if ( read_buf ) sanei_hp_free ( read_buf );
 | |
| 
 | |
|   if (enable_requests && (reqs_completed < reqs_issued))
 | |
|     {
 | |
|       DBG(1, "do_read: cleaning up leftover requests\n");
 | |
|       while (reqs_completed < reqs_issued)
 | |
| 	{
 | |
| 	  req = buf + (reqs_completed++ % 2);
 | |
| 	  sanei_scsi_req_wait(req->id);
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|   sigfillset(&sig_set);
 | |
|   sigprocmask(SIG_BLOCK, &sig_set, 0);
 | |
|   for (i = 0; i < (int)(HP_NSIGS); i++)
 | |
|       sigaction(kill_sig[i], &old_handler[i], 0);
 | |
|   sigprocmask(SIG_SETMASK, &old_set, 0);
 | |
| 
 | |
|   if (signal_caught)
 | |
|     {
 | |
|       DBG(1, "do_read: caught signal %d\n", signal_caught);
 | |
|       raise(signal_caught);
 | |
|       return SANE_STATUS_CANCELLED;
 | |
|     }
 | |
| 
 | |
|   return status;
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| /*
 | |
|  *
 | |
|  */
 | |
| 
 | |
| static SANE_Status
 | |
| _hp_scl_inq (HpScsi scsi, HpScl scl, HpScl inq_cmnd,
 | |
| 	     void *valp, size_t *lengthp)
 | |
| {
 | |
|   size_t	bufsize	= 16 + (lengthp ? *lengthp: 0);
 | |
|   char *	buf	= alloca(bufsize);
 | |
|   char		expect[16], expect_char;
 | |
|   int		val, count;
 | |
|   SANE_Status	status;
 | |
| 
 | |
|   if (!buf)
 | |
|       return SANE_STATUS_NO_MEM;
 | |
| 
 | |
|   /* Flush data before sending inquiry. */
 | |
|   /* Otherwise scanner might not generate a response. */
 | |
|   RETURN_IF_FAIL( hp_scsi_flush (scsi)) ;
 | |
| 
 | |
|   RETURN_IF_FAIL( hp_scsi_scl(scsi, inq_cmnd, SCL_INQ_ID(scl)) );
 | |
| 
 | |
|   status =  hp_scsi_read(scsi, buf, &bufsize, 1);
 | |
|   if (FAILED(status))
 | |
|     {
 | |
|       DBG(1, "scl_inq: read failed (%s)\n", sane_strstatus(status));
 | |
|       return status;
 | |
|     }
 | |
| 
 | |
|   if (SCL_PARAM_CHAR(inq_cmnd) == 'R')
 | |
|       expect_char = 'p';
 | |
|   else
 | |
|       expect_char = tolower(SCL_PARAM_CHAR(inq_cmnd) - 1);
 | |
| 
 | |
|   count = sprintf(expect, "\033*s%d%c", SCL_INQ_ID(scl), expect_char);
 | |
|   if (memcmp(buf, expect, count) != 0)
 | |
|     {
 | |
|       DBG(1, "scl_inq: malformed response: expected '%s', got '%.*s'\n",
 | |
| 	  expect, count, buf);
 | |
|       return SANE_STATUS_IO_ERROR;
 | |
|     }
 | |
|   buf += count;
 | |
| 
 | |
|   if (buf[0] == 'N')
 | |
|     {				/* null response */
 | |
|       DBG(3, "scl_inq: parameter %d unsupported\n", SCL_INQ_ID(scl));
 | |
|       return SANE_STATUS_UNSUPPORTED;
 | |
|     }
 | |
| 
 | |
|   if (sscanf(buf, "%d%n", &val, &count) != 1)
 | |
|     {
 | |
|       DBG(1, "scl_inq: malformed response: expected int, got '%.8s'\n", buf);
 | |
|       return SANE_STATUS_IO_ERROR;
 | |
|     }
 | |
|   buf += count;
 | |
| 
 | |
|   expect_char = lengthp ? 'W' : 'V';
 | |
|   if (*buf++ != expect_char)
 | |
|     {
 | |
|       DBG(1, "scl_inq: malformed response: expected '%c', got '%.4s'\n",
 | |
| 	  expect_char, buf - 1);
 | |
|       return SANE_STATUS_IO_ERROR;
 | |
|     }
 | |
| 
 | |
|   if (!lengthp)
 | |
|       *(int *)valp = val; /* Get integer value */
 | |
|   else
 | |
|     {
 | |
|       if (val > (int)*lengthp)
 | |
| 	{
 | |
| 	  DBG(1, "scl_inq: inquiry returned %d bytes, expected <= %lu\n",
 | |
| 	      val, (unsigned long) *lengthp);
 | |
| 	  return SANE_STATUS_IO_ERROR;
 | |
| 	}
 | |
|       *lengthp = val;
 | |
|       memcpy(valp, buf , *lengthp); /* Get binary data */
 | |
|     }
 | |
| 
 | |
|   return SANE_STATUS_GOOD;
 | |
| }
 | |
| 
 | |
| 
 | |
| SANE_Status
 | |
| sanei_hp_scl_upload_binary (HpScsi scsi, HpScl scl, size_t *lengthhp,
 | |
|                             char **bufhp)
 | |
| {
 | |
|   size_t	bufsize	= 16, sv;
 | |
|   char *	buf	= alloca(bufsize);
 | |
|   char *        bufstart = buf;
 | |
|   char *        hpdata;
 | |
|   char		expect[16], expect_char;
 | |
|   int		n, val, count;
 | |
|   SANE_Status	status;
 | |
| 
 | |
|   if (!buf)
 | |
|       return SANE_STATUS_NO_MEM;
 | |
| 
 | |
|   assert ( IS_SCL_DATA_TYPE (scl) );
 | |
| 
 | |
|   /* Flush data before sending inquiry. */
 | |
|   /* Otherwise scanner might not generate a response. */
 | |
|   RETURN_IF_FAIL( hp_scsi_flush (scsi)) ;
 | |
| 
 | |
|   RETURN_IF_FAIL( hp_scsi_scl(scsi, SCL_UPLOAD_BINARY_DATA, SCL_INQ_ID(scl)) );
 | |
| 
 | |
|   status =  hp_scsi_read(scsi, buf, &bufsize, 0);
 | |
|   if (FAILED(status))
 | |
|     {
 | |
|       DBG(1, "scl_upload_binary: read failed (%s)\n", sane_strstatus(status));
 | |
|       return status;
 | |
|     }
 | |
| 
 | |
|   expect_char = 't';
 | |
|   count = sprintf(expect, "\033*s%d%c", SCL_INQ_ID(scl), expect_char);
 | |
|   if (memcmp(buf, expect, count) != 0)
 | |
|     {
 | |
|       DBG(1, "scl_upload_binary: malformed response: expected '%s', got '%.*s'\n",
 | |
| 	  expect, count, buf);
 | |
|       return SANE_STATUS_IO_ERROR;
 | |
|     }
 | |
|   buf += count;
 | |
| 
 | |
|   if (buf[0] == 'N')
 | |
|     {				/* null response */
 | |
|       DBG(1, "scl_upload_binary: parameter %d unsupported\n", SCL_INQ_ID(scl));
 | |
|       return SANE_STATUS_UNSUPPORTED;
 | |
|     }
 | |
| 
 | |
|   if (sscanf(buf, "%d%n", &val, &count) != 1)
 | |
|     {
 | |
|       DBG(1, "scl_inq: malformed response: expected int, got '%.8s'\n", buf);
 | |
|       return SANE_STATUS_IO_ERROR;
 | |
|     }
 | |
|   buf += count;
 | |
| 
 | |
|   expect_char = 'W';
 | |
|   if (*buf++ != expect_char)
 | |
|     {
 | |
|       DBG(1, "scl_inq: malformed response: expected '%c', got '%.4s'\n",
 | |
| 	  expect_char, buf - 1);
 | |
|       return SANE_STATUS_IO_ERROR;
 | |
|     }
 | |
| 
 | |
|   *lengthhp = val;
 | |
|   *bufhp = hpdata = sanei_hp_alloc ( val );
 | |
|   if (!hpdata)
 | |
|       return SANE_STATUS_NO_MEM;
 | |
| 
 | |
|   if (buf < bufstart + bufsize)
 | |
|     {
 | |
|        n = bufsize - (buf - bufstart);
 | |
|        if (n > val) n = val;
 | |
|        memcpy (hpdata, buf, n);
 | |
|        hpdata += n;
 | |
|        val -= n;
 | |
|     }
 | |
| 
 | |
|   status = SANE_STATUS_GOOD;
 | |
|   if ( val > 0 )
 | |
|     {
 | |
|       sv = val;
 | |
|       status = hp_scsi_read(scsi, hpdata, &sv, 0);
 | |
|       if (status != SANE_STATUS_GOOD)
 | |
|         sanei_hp_free ( *bufhp );
 | |
|     }
 | |
| 
 | |
|   return status;
 | |
| }
 | |
| 
 | |
| 
 | |
| SANE_Status
 | |
| sanei_hp_scl_set(HpScsi scsi, HpScl scl, int val)
 | |
| {
 | |
|   RETURN_IF_FAIL( hp_scsi_scl(scsi, scl, val) );
 | |
| 
 | |
| 
 | |
| #ifdef PARANOID
 | |
|   RETURN_IF_FAIL( sanei_hp_scl_errcheck(scsi) );
 | |
| #endif
 | |
| 
 | |
|   return SANE_STATUS_GOOD;
 | |
| }
 | |
| 
 | |
| SANE_Status
 | |
| sanei_hp_scl_inquire(HpScsi scsi, HpScl scl, int * valp, int * minp, int * maxp)
 | |
| {
 | |
|   HpScl	inquiry = ( IS_SCL_CONTROL(scl)
 | |
| 		    ? SCL_INQUIRE_PRESENT_VALUE
 | |
| 		    : SCL_INQUIRE_DEVICE_PARAMETER );
 | |
| 
 | |
|   assert(IS_SCL_CONTROL(scl) || IS_SCL_PARAMETER(scl));
 | |
|   assert(IS_SCL_CONTROL(scl) || (!minp && !maxp));
 | |
| 
 | |
|   if (valp)
 | |
|       RETURN_IF_FAIL( _hp_scl_inq(scsi, scl, inquiry, valp, 0) );
 | |
|   if (minp)
 | |
|       RETURN_IF_FAIL( _hp_scl_inq(scsi, scl,
 | |
| 				  SCL_INQUIRE_MINIMUM_VALUE, minp, 0) );
 | |
|   if (maxp)
 | |
|       RETURN_IF_FAIL( _hp_scl_inq(scsi, scl,
 | |
| 				  SCL_INQUIRE_MAXIMUM_VALUE, maxp, 0) );
 | |
|   return SANE_STATUS_GOOD;
 | |
| }
 | |
| 
 | |
| #ifdef _HP_NOT_USED
 | |
| static SANE_Status
 | |
| hp_scl_get_bounds(HpScsi scsi, HpScl scl, int * minp, int * maxp)
 | |
| {
 | |
|   assert(IS_SCL_CONTROL(scl));
 | |
|   RETURN_IF_FAIL( _hp_scl_inq(scsi, scl, SCL_INQUIRE_MINIMUM_VALUE, minp, 0) );
 | |
|   return _hp_scl_inq(scsi, scl, SCL_INQUIRE_MAXIMUM_VALUE, maxp, 0);
 | |
| }
 | |
| #endif
 | |
| 
 | |
| #ifdef _HP_NOT_USED
 | |
| static SANE_Status
 | |
| hp_scl_get_bounds_and_val(HpScsi scsi, HpScl scl,
 | |
| 			  int * minp, int * maxp, int * valp)
 | |
| {
 | |
|   assert(IS_SCL_CONTROL(scl));
 | |
|   RETURN_IF_FAIL( _hp_scl_inq(scsi, scl, SCL_INQUIRE_MINIMUM_VALUE, minp, 0) );
 | |
|   RETURN_IF_FAIL( _hp_scl_inq(scsi, scl, SCL_INQUIRE_MAXIMUM_VALUE, maxp, 0) );
 | |
|   return    _hp_scl_inq(scsi, scl, SCL_INQUIRE_PRESENT_VALUE, valp, 0);
 | |
| }
 | |
| #endif
 | |
| 
 | |
| SANE_Status
 | |
| sanei_hp_scl_download(HpScsi scsi, HpScl scl, const void * valp, size_t len)
 | |
| {
 | |
|   assert(IS_SCL_DATA_TYPE(scl));
 | |
| 
 | |
|   sanei_hp_scl_clearErrors ( scsi );
 | |
|   RETURN_IF_FAIL( hp_scsi_need(scsi, 16) );
 | |
|   RETURN_IF_FAIL( hp_scsi_scl(scsi, SCL_DOWNLOAD_TYPE, SCL_INQ_ID(scl)) );
 | |
|                             /* Download type not supported ? */
 | |
|   RETURN_IF_FAIL( sanei_hp_scl_errcheck(scsi) );
 | |
|   RETURN_IF_FAIL( hp_scsi_scl(scsi, SCL_DOWNLOAD_LENGTH, len) );
 | |
|   RETURN_IF_FAIL( hp_scsi_write(scsi, valp, len) );
 | |
| 
 | |
| #ifdef PARANOID
 | |
|   RETURN_IF_FAIL( sanei_hp_scl_errcheck(scsi) );
 | |
| #endif
 | |
| 
 | |
|   return SANE_STATUS_GOOD;
 | |
| }
 | |
| 
 | |
| SANE_Status
 | |
| sanei_hp_scl_upload(HpScsi scsi, HpScl scl, void * valp, size_t len)
 | |
| {
 | |
|   size_t	nread = len;
 | |
|   HpScl		inquiry = ( IS_SCL_DATA_TYPE(scl)
 | |
| 			    ? SCL_UPLOAD_BINARY_DATA
 | |
| 			    : SCL_INQUIRE_DEVICE_PARAMETER );
 | |
| 
 | |
|   assert(IS_SCL_DATA_TYPE(scl) || IS_SCL_PARAMETER(scl));
 | |
| 
 | |
|   RETURN_IF_FAIL( _hp_scl_inq(scsi, scl, inquiry, valp, &nread) );
 | |
|   if (IS_SCL_PARAMETER(scl) && nread < len)
 | |
|       ((char *)valp)[nread] = '\0';
 | |
|   else if (len != nread)
 | |
|     {
 | |
|       DBG(1, "scl_upload: requested %lu bytes, got %lu\n",
 | |
| 	  (unsigned long) len, (unsigned long) nread);
 | |
|       return SANE_STATUS_IO_ERROR;
 | |
|     }
 | |
|   return SANE_STATUS_GOOD;
 | |
| }
 | |
| 
 | |
| SANE_Status
 | |
| sanei_hp_scl_calibrate(HpScsi scsi)
 | |
| {
 | |
|   RETURN_IF_FAIL( hp_scsi_scl(scsi, SCL_CALIBRATE, 0) );
 | |
|   return hp_scsi_flush(scsi);
 | |
| }
 | |
| 
 | |
| SANE_Status
 | |
| sanei_hp_scl_startScan(HpScsi scsi, HpScl scl)
 | |
| {
 | |
|   char *msg = "";
 | |
| 
 | |
|   if (scl == SCL_ADF_SCAN) msg = " (ADF)";
 | |
|   else if (scl == SCL_XPA_SCAN) msg = " (XPA)";
 | |
|   else scl = SCL_START_SCAN;
 | |
| 
 | |
|   DBG(1, "sanei_hp_scl_startScan: Start scan%s\n", msg);
 | |
| 
 | |
|   /* For active XPA we must not use XPA scan */
 | |
|   if ((scl == SCL_XPA_SCAN) && sanei_hp_is_active_xpa (scsi))
 | |
|   {
 | |
|     DBG(3,"Map XPA scan to scan because of active XPA\n");
 | |
|     scl = SCL_START_SCAN;
 | |
|   }
 | |
| 
 | |
|   RETURN_IF_FAIL( hp_scsi_scl(scsi, scl, 0) );
 | |
|   return hp_scsi_flush(scsi);
 | |
| }
 | |
| 
 | |
| SANE_Status
 | |
| sanei_hp_scl_reset(HpScsi scsi)
 | |
| {
 | |
|   RETURN_IF_FAIL( hp_scsi_write(scsi, "\033E", 2) );
 | |
|   RETURN_IF_FAIL( hp_scsi_flush(scsi) );
 | |
|   return sanei_hp_scl_errcheck(scsi);
 | |
| }
 | |
| 
 | |
| SANE_Status
 | |
| sanei_hp_scl_clearErrors(HpScsi scsi)
 | |
| {
 | |
|   RETURN_IF_FAIL( hp_scsi_flush(scsi) );
 | |
|   RETURN_IF_FAIL( hp_scsi_write(scsi, "\033*oE", 4) );
 | |
|   return hp_scsi_flush(scsi);
 | |
| }
 | |
| 
 | |
| static const char *
 | |
| hp_scl_strerror (int errnum)
 | |
| {
 | |
|   static const char * errlist[] = {
 | |
|       "Command Format Error",
 | |
|       "Unrecognized Command",
 | |
|       "Parameter Error",
 | |
|       "Illegal Window",
 | |
|       "Scaling Error",
 | |
|       "Dither ID Error",
 | |
|       "Tone Map ID Error",
 | |
|       "Lamp Error",
 | |
|       "Matrix ID Error",
 | |
|       "Cal Strip Param Error",
 | |
|       "Gross Calibration Error"
 | |
|   };
 | |
| 
 | |
|   if (errnum >= 0 && errnum < (int)(sizeof(errlist)/sizeof(errlist[0])))
 | |
|       return errlist[errnum];
 | |
|   else
 | |
|       switch(errnum) {
 | |
|       case 1024: return "ADF Paper Jam";
 | |
|       case 1025: return "Home Position Missing";
 | |
|       case 1026: return "Paper Not Loaded";
 | |
|       default: return "??Unkown Error??";
 | |
|       }
 | |
| }
 | |
| 
 | |
| /* Check for SCL errors */
 | |
| SANE_Status
 | |
| sanei_hp_scl_errcheck (HpScsi scsi)
 | |
| {
 | |
|   int		errnum;
 | |
|   int		nerrors;
 | |
|   SANE_Status	status;
 | |
| 
 | |
|   status = sanei_hp_scl_inquire(scsi, SCL_CURRENT_ERROR_STACK, &nerrors,0,0);
 | |
|   if (!FAILED(status) && nerrors)
 | |
|       status = sanei_hp_scl_inquire(scsi, SCL_OLDEST_ERROR, &errnum,0,0);
 | |
|   if (FAILED(status))
 | |
|     {
 | |
|       DBG(1, "scl_errcheck: Can't read SCL error stack: %s\n",
 | |
| 	  sane_strstatus(status));
 | |
|       return SANE_STATUS_IO_ERROR;
 | |
|     }
 | |
| 
 | |
|   if (nerrors)
 | |
|     {
 | |
|       DBG(1, "Scanner issued SCL error: (%d) %s\n",
 | |
| 	  errnum, hp_scl_strerror(errnum));
 | |
| 
 | |
|       sanei_hp_scl_clearErrors (scsi);
 | |
|       return SANE_STATUS_IO_ERROR;
 | |
|     }
 | |
| 
 | |
|   return SANE_STATUS_GOOD;
 | |
| }
 |