sane-project-backends/backend/hp-scl.c

1277 wiersze
30 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).
*/
#include <sane/config.h>
#include <lalloca.h> /* Must be first */
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
#include <ctype.h>
#include <string.h>
#include <errno.h>
#include <assert.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sane/sanei_scsi.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"
#if (defined(__IBMC__) || defined(__IBMCPP__))
#ifndef _AIX
#define inline /* */
#endif
#endif
#define HP_SCSI_INQ_LEN (36)
#define HP_SCSI_CMD_LEN (6)
#define HP_SCSI_BUFSIZ (HP_SCSI_MAX_WRITE + HP_SCSI_CMD_LEN)
/*
*
*/
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];
};
static SANE_Status
hp_nonscsi_write (HpScsi this, hp_byte_t *data, size_t len, HpConnect connect)
{size_t n = -1;
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 */
n = -1;
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 SANE_STATUS_GOOD;
}
static SANE_Status
hp_nonscsi_read (HpScsi this, hp_byte_t *data, size_t *len, HpConnect connect)
{size_t n = -1;
if (*len <= 0) return SANE_STATUS_GOOD;
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:
n = -1;
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;
*len = n;
return SANE_STATUS_GOOD;
}
static SANE_Status
hp_nonscsi_open (const char *devname, int *fd, HpConnect connect)
{int lfd, flags;
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:
status = SANE_STATUS_INVAL;
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);
}
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:
break;
case HP_CONNECT_RESERVE:
break;
default:
break;
}
}
SANE_Status
sanei_hp_nonscsi_new (HpScsi * newp, const char * devname, HpConnect connect)
{
HpScsi new;
SANE_Status status;
new = sanei_hp_allocz(sizeof(*new));
if (!new)
return SANE_STATUS_NO_MEM;
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 (new->inq_data, "\003zzzzzzzHP MODELx 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;
return SANE_STATUS_GOOD;
}
static void
hp_scsi_close (HpScsi this)
{HpConnect connect;
connect = sanei_hp_scsi_get_connect (this);
if (connect != HP_CONNECT_SCSI)
hp_nonscsi_close (this->fd, connect);
else
sanei_scsi_close (this->fd);
}
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;
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;
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;
}
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));
sanei_scsi_close(new->fd);
sanei_hp_free(new);
}
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;
return SANE_STATUS_GOOD;
}
void
sanei_hp_scsi_destroy (HpScsi this)
{
assert(this->fd >= 0);
DBG(3, "scsi_close: closing fd %d\n", this->fd);
hp_scsi_close (this);
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;
}
HpConnect
sanei_hp_get_connect (const char *devname)
{const HpDeviceInfo *info;
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);
return HP_CONNECT_SCSI;
}
if ( !(info->config_is_up) )
{
DBG(1, "sanei_hp_get_connect: Config not initialized for %s. Assume SCSI\n",
devname);
return HP_CONNECT_SCSI;
}
return info->config.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(10, "scsi_flush: writing %lu bytes:\n", (unsigned long) len);
DBGDUMP(10, 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 inline 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);
}
static SANE_Status
hp_scsi_read (HpScsi this, void * dest, size_t *len)
{
HpConnect connect;
static hp_byte_t read_cmd[6] = { 0x08, 0, 0, 0, 0, 0 };
RETURN_IF_FAIL( hp_scsi_flush(this) );
read_cmd[2] = *len >> 16;
read_cmd[3] = *len >> 8;
read_cmd[4] = *len;
connect = sanei_hp_scsi_get_connect (this);
if (connect == HP_CONNECT_SCSI)
{
RETURN_IF_FAIL( sanei_scsi_cmd (this->fd, read_cmd,
sizeof(read_cmd), dest, len) );
}
else
{
RETURN_IF_FAIL( hp_nonscsi_read (this, dest, len, connect) );
}
DBG(10, "scsi_read: %lu bytes:\n", (unsigned long) *len);
DBGDUMP(10, dest, *len);
return SANE_STATUS_GOOD;
}
static int signal_caught = 0;
static RETSIGTYPE
signal_catcher (int sig)
{
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;
}
SANE_Status
sanei_hp_scsi_pipeout (HpScsi this, int outfd, size_t count,
int mirror, int bytes_per_line,
int bits_per_channel)
{
/* 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;
#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;
int reqs_completed = 0;
int reqs_issued = 0;
int num_lines;
size_t image_len;
char *image_buf = 0, *image_data = 0;
char *read_buf = 0;
const HpDeviceInfo *info;
const char *devname = sanei_hp_scsi_devicename (this);
int enable_requests = 1;
const unsigned char *map = NULL;
HpConnect connect = HP_CONNECT_SCSI;
RETURN_IF_FAIL( hp_scsi_flush(this) );
info = sanei_hp_device_info_get (devname);
assert (info);
if ( info->config_is_up )
{
enable_requests = info->config.use_scsi_request;
connect = info->config.connect;
}
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 < 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);
if ( mirror )
{
image_buf = image_data = sanei_hp_alloc ( count );
if ( !image_buf )
{
mirror = 0;
DBG(1, "do_read: Not enough memory to mirror image\n");
}
}
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;
if ( map )
hp_data_map (map, (int)req->len, (unsigned char *)req->data);
if ( mirror )
{
DBG(3, "do_read: got %lu bytes, save in memory...\n",
(unsigned long) req->len);
memcpy(image_data, req->data, req->len);
image_data += req->len;
}
else
{
DBG(3, "do_read: got %lu bytes, writing...\n",
(unsigned long) req->len);
if (write(outfd, req->data, req->len) != req->len)
{
if (signal_caught)
goto quit;
DBG(1, "do_read: write failed: %s\n", strerror(errno));
status = SANE_STATUS_IO_ERROR;
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);
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;
}
if ( map )
hp_data_map (map, (int)nread, (unsigned char *)read_buf);
if ( mirror )
{
DBG(3, "do_read: got %lu bytes, save in memory...\n",
(unsigned long) nread);
memcpy(image_data, read_buf, nread);
image_data += nread;
}
else
{
DBG(3, "do_read: got %lu bytes, writing...\n",
(unsigned long) nread);
if (write(outfd, read_buf, nread) != nread)
{
if (signal_caught)
goto quit;
DBG(1, "do_read: write failed: %s\n", strerror(errno));
status = SANE_STATUS_IO_ERROR;
goto quit;
}
}
count -= nread;
}
}
if ( mirror )
{
image_len = (size_t) (image_data - image_buf);
num_lines = ((int)(image_len+bytes_per_line-1)) / bytes_per_line;
image_data = image_buf + (num_lines-1) * bytes_per_line;
DBG(3, "do_read: write %d bytes from memory...\n", (int)image_len);
while (num_lines > 0 )
{
if (write(outfd, image_data, bytes_per_line) != bytes_per_line)
{
if (signal_caught)
goto quit;
DBG(1,"do_read: write from memory failed: %s\n", strerror(errno));
status = SANE_STATUS_IO_ERROR;
goto quit;
}
num_lines--;
image_data -= bytes_per_line;
}
}
quit:
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 < 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);
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 '%c' (%d) unsupported\n",
SCL_PARAM_CHAR(scl), 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 > *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);
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 '%c' (%d) unsupported\n",
SCL_PARAM_CHAR(scl), 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);
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, hp_bool_t adf_scan)
{
DBG(1, "sanei_hp_scl_startScan: Start %sscan\n", adf_scan ? "ADF " : "");
if ( adf_scan )
RETURN_IF_FAIL( hp_scsi_scl(scsi, SCL_ADF_SCAN, 0) );
else
RETURN_IF_FAIL( hp_scsi_scl(scsi, SCL_START_SCAN, 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 < 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;
}