kopia lustrzana https://gitlab.com/sane-project/backends
2115 wiersze
52 KiB
C
2115 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.14 2004/10/04 18:09:05 kig-guest
|
|
Rename global function hp_init_openfd to sanei_hp_init_openfd
|
|
|
|
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
|
|
sanei_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;
|
|
}
|