kopia lustrzana https://gitlab.com/sane-project/backends
1179 wiersze
27 KiB
C
1179 wiersze
27 KiB
C
/*
|
|
Copyright (C) 2008, Panasonic Russia Ltd.
|
|
*/
|
|
/* sane - Scanner Access Now Easy.
|
|
Panasonic KV-S1020C / KV-S1025C USB scanners.
|
|
*/
|
|
|
|
#define DEBUG_DECLARE_ONLY
|
|
|
|
#include "../include/sane/config.h"
|
|
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <limits.h>
|
|
#include <signal.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/types.h>
|
|
#include <sys/wait.h>
|
|
#include <unistd.h>
|
|
|
|
#include "../include/sane/sane.h"
|
|
#include "../include/sane/saneopts.h"
|
|
#include "../include/sane/sanei.h"
|
|
#include "../include/sane/sanei_usb.h"
|
|
#include "../include/sane/sanei_backend.h"
|
|
#include "../include/sane/sanei_config.h"
|
|
#include "../include/lassert.h"
|
|
#include "../include/sane/sanei_magic.h"
|
|
|
|
#include "kvs1025.h"
|
|
#include "kvs1025_low.h"
|
|
#include "kvs1025_usb.h"
|
|
|
|
#include "../include/sane/sanei_debug.h"
|
|
|
|
/* Global storage */
|
|
|
|
PKV_DEV g_devices = NULL; /* Chain of devices */
|
|
const SANE_Device **g_devlist = NULL;
|
|
|
|
/* Static functions */
|
|
|
|
/* Free one device */
|
|
static void
|
|
kv_free (KV_DEV ** pdev)
|
|
{
|
|
KV_DEV *dev;
|
|
|
|
dev = *pdev;
|
|
|
|
if (dev == NULL)
|
|
return;
|
|
|
|
DBG (DBG_proc, "kv_free : enter\n");
|
|
|
|
kv_close (dev);
|
|
|
|
DBG (DBG_proc, "kv_free : free image buffer 0 \n");
|
|
if (dev->img_buffers[0])
|
|
free (dev->img_buffers[0]);
|
|
DBG (DBG_proc, "kv_free : free image buffer 1 \n");
|
|
if (dev->img_buffers[1])
|
|
free (dev->img_buffers[1]);
|
|
DBG (DBG_proc, "kv_free : free scsi device name\n");
|
|
if (dev->scsi_device_name)
|
|
free (dev->scsi_device_name);
|
|
|
|
DBG (DBG_proc, "kv_free : free SCSI buffer\n");
|
|
if (dev->buffer0)
|
|
free (dev->buffer0);
|
|
|
|
DBG (DBG_proc, "kv_free : free dev \n");
|
|
free (dev);
|
|
|
|
*pdev = NULL;
|
|
|
|
DBG (DBG_proc, "kv_free : exit\n");
|
|
}
|
|
|
|
/* Free all devices */
|
|
static void
|
|
kv_free_devices (void)
|
|
{
|
|
PKV_DEV dev;
|
|
while (g_devices)
|
|
{
|
|
dev = g_devices;
|
|
g_devices = dev->next;
|
|
kv_free (&dev);
|
|
}
|
|
if (g_devlist)
|
|
{
|
|
free (g_devlist);
|
|
g_devlist = NULL;
|
|
}
|
|
}
|
|
|
|
/* Get all supported scanners, and store into g_scanners_supported */
|
|
SANE_Status
|
|
kv_enum_devices (void)
|
|
{
|
|
SANE_Status status;
|
|
kv_free_devices ();
|
|
status = kv_usb_enum_devices ();
|
|
if (status)
|
|
{
|
|
kv_free_devices ();
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
/* Return devices list to the front end */
|
|
void
|
|
kv_get_devices_list (const SANE_Device *** devices_list)
|
|
{
|
|
*devices_list = g_devlist;
|
|
}
|
|
|
|
/* Close all open handles and clean up global storage */
|
|
void
|
|
kv_exit (void)
|
|
{
|
|
kv_free_devices (); /* Free all devices */
|
|
kv_usb_cleanup (); /* Clean USB bus */
|
|
}
|
|
|
|
/* Open device by name */
|
|
SANE_Status
|
|
kv_open_by_name (SANE_String_Const devicename, SANE_Handle * handle)
|
|
{
|
|
|
|
PKV_DEV pd = g_devices;
|
|
DBG (DBG_proc, "sane_open: enter (dev_name=%s)\n", devicename);
|
|
while (pd)
|
|
{
|
|
if (strcmp (pd->sane.name, devicename) == 0)
|
|
{
|
|
if (kv_open (pd) == 0)
|
|
{
|
|
*handle = (SANE_Handle) pd;
|
|
DBG (DBG_proc, "sane_open: leave\n");
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
}
|
|
pd = pd->next;
|
|
}
|
|
DBG (DBG_proc, "sane_open: leave -- no device found\n");
|
|
return SANE_STATUS_UNSUPPORTED;
|
|
}
|
|
|
|
/* Open a device */
|
|
SANE_Status
|
|
kv_open (PKV_DEV dev)
|
|
{
|
|
SANE_Status status = SANE_STATUS_UNSUPPORTED;
|
|
int i;
|
|
#define RETRAY_NUM 3
|
|
|
|
|
|
if (dev->bus_mode == KV_USB_BUS)
|
|
{
|
|
status = kv_usb_open (dev);
|
|
}
|
|
if (status)
|
|
return status;
|
|
for (i = 0; i < RETRAY_NUM; i++)
|
|
{
|
|
SANE_Bool dev_ready;
|
|
status = CMD_test_unit_ready (dev, &dev_ready);
|
|
if (!status && dev_ready)
|
|
break;
|
|
}
|
|
|
|
if (status == 0)
|
|
{
|
|
/* Read device support info */
|
|
status = CMD_read_support_info (dev);
|
|
|
|
if (status == 0)
|
|
{
|
|
/* Init options */
|
|
kv_init_options (dev);
|
|
status = CMD_set_timeout (dev, dev->val[OPT_FEED_TIMEOUT].w);
|
|
}
|
|
}
|
|
dev->scanning = 0;
|
|
return status;
|
|
}
|
|
|
|
/* Check if device is already open */
|
|
|
|
SANE_Bool
|
|
kv_already_open (PKV_DEV dev)
|
|
{
|
|
SANE_Bool status = 0;
|
|
|
|
if (dev->bus_mode == KV_USB_BUS)
|
|
{
|
|
status = kv_usb_already_open (dev);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
/* Close a device */
|
|
void
|
|
kv_close (PKV_DEV dev)
|
|
{
|
|
if (dev->bus_mode == KV_USB_BUS)
|
|
{
|
|
kv_usb_close (dev);
|
|
}
|
|
dev->scanning = 0;
|
|
}
|
|
|
|
/* Send command to a device */
|
|
SANE_Status
|
|
kv_send_command (PKV_DEV dev,
|
|
PKV_CMD_HEADER header, PKV_CMD_RESPONSE response)
|
|
{
|
|
SANE_Status status = SANE_STATUS_UNSUPPORTED;
|
|
if (dev->bus_mode == KV_USB_BUS)
|
|
{
|
|
if (!kv_usb_already_open(dev))
|
|
{
|
|
DBG (DBG_error, "kv_send_command error: device not open.\n");
|
|
return SANE_STATUS_IO_ERROR;
|
|
}
|
|
|
|
status = kv_usb_send_command (dev, header, response);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
/* Commands */
|
|
|
|
SANE_Status
|
|
CMD_test_unit_ready (PKV_DEV dev, SANE_Bool * ready)
|
|
{
|
|
SANE_Status status;
|
|
KV_CMD_HEADER hdr;
|
|
KV_CMD_RESPONSE rs;
|
|
|
|
DBG (DBG_proc, "CMD_test_unit_ready\n");
|
|
|
|
memset (&hdr, 0, sizeof (hdr));
|
|
|
|
hdr.direction = KV_CMD_NONE;
|
|
hdr.cdb[0] = SCSI_TEST_UNIT_READY;
|
|
hdr.cdb_size = 6;
|
|
|
|
status = kv_send_command (dev, &hdr, &rs);
|
|
|
|
if (status == 0)
|
|
{
|
|
*ready = (rs.status == KV_SUCCESS ? 1 : 0);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
SANE_Status
|
|
CMD_set_timeout (PKV_DEV dev, SANE_Word timeout)
|
|
{
|
|
SANE_Status status;
|
|
KV_CMD_HEADER hdr;
|
|
KV_CMD_RESPONSE rs;
|
|
|
|
DBG (DBG_proc, "CMD_set_timeout\n");
|
|
|
|
memset (&hdr, 0, sizeof (hdr));
|
|
|
|
hdr.direction = KV_CMD_OUT;
|
|
hdr.cdb[0] = SCSI_SET_TIMEOUT;
|
|
hdr.cdb[2] = 0x8D;
|
|
hdr.cdb[8] = 0x2;
|
|
hdr.cdb_size = 10;
|
|
hdr.data = dev->buffer;
|
|
dev->buffer[0] = 0;
|
|
dev->buffer[1] = (SANE_Byte) timeout;
|
|
hdr.data_size = 2;
|
|
|
|
status = kv_send_command (dev, &hdr, &rs);
|
|
|
|
return status;
|
|
}
|
|
|
|
SANE_Status
|
|
CMD_read_support_info (PKV_DEV dev)
|
|
{
|
|
SANE_Status status;
|
|
KV_CMD_HEADER hdr;
|
|
KV_CMD_RESPONSE rs;
|
|
|
|
DBG (DBG_proc, "CMD_read_support_info\n");
|
|
|
|
memset (&hdr, 0, sizeof (hdr));
|
|
|
|
hdr.direction = KV_CMD_IN;
|
|
hdr.cdb_size = 10;
|
|
hdr.cdb[0] = SCSI_READ_10;
|
|
hdr.cdb[2] = 0x93;
|
|
Ito24 (32, &hdr.cdb[6]);
|
|
hdr.data = dev->buffer;
|
|
hdr.data_size = 32;
|
|
|
|
status = kv_send_command (dev, &hdr, &rs);
|
|
|
|
DBG (DBG_error, "test.\n");
|
|
|
|
if (status == 0)
|
|
{
|
|
if (rs.status == 0)
|
|
{
|
|
int min_x_res, min_y_res, max_x_res, max_y_res;
|
|
int step_x_res, step_y_res;
|
|
|
|
dev->support_info.memory_size
|
|
= (dev->buffer[2] << 8 | dev->buffer[3]);
|
|
min_x_res = (dev->buffer[4] << 8) | dev->buffer[5];
|
|
min_y_res = (dev->buffer[6] << 8) | dev->buffer[7];
|
|
max_x_res = (dev->buffer[8] << 8) | dev->buffer[9];
|
|
max_y_res = (dev->buffer[10] << 8) | dev->buffer[11];
|
|
step_x_res = (dev->buffer[12] << 8) | dev->buffer[13];
|
|
step_y_res = (dev->buffer[14] << 8) | dev->buffer[15];
|
|
|
|
dev->support_info.min_resolution =
|
|
min_x_res > min_y_res ? min_x_res : min_y_res;
|
|
dev->support_info.max_resolution =
|
|
max_x_res < max_y_res ? max_x_res : max_y_res;
|
|
dev->support_info.step_resolution =
|
|
step_x_res > step_y_res ? step_x_res : step_y_res;
|
|
dev->support_info.support_duplex =
|
|
((dev->buffer[0] & 0x08) == 0) ? 1 : 0;
|
|
dev->support_info.support_lamp =
|
|
((dev->buffer[23] & 0x80) != 0) ? 1 : 0;
|
|
|
|
dev->support_info.max_x_range = KV_MAX_X_RANGE;
|
|
dev->support_info.max_y_range = KV_MAX_Y_RANGE;
|
|
|
|
dev->x_range.min = dev->y_range.min = 0;
|
|
dev->x_range.max = SANE_FIX (dev->support_info.max_x_range);
|
|
dev->y_range.max = SANE_FIX (dev->support_info.max_y_range);
|
|
dev->x_range.quant = dev->y_range.quant = 0;
|
|
|
|
DBG (DBG_error,
|
|
"support_info.memory_size = %d (MB)\n",
|
|
dev->support_info.memory_size);
|
|
DBG (DBG_error,
|
|
"support_info.min_resolution = %d (DPI)\n",
|
|
dev->support_info.min_resolution);
|
|
DBG (DBG_error,
|
|
"support_info.max_resolution = %d (DPI)\n",
|
|
dev->support_info.max_resolution);
|
|
DBG (DBG_error,
|
|
"support_info.step_resolution = %d (DPI)\n",
|
|
dev->support_info.step_resolution);
|
|
DBG (DBG_error,
|
|
"support_info.support_duplex = %s\n",
|
|
dev->support_info.support_duplex ? "TRUE" : "FALSE");
|
|
DBG (DBG_error, "support_info.support_lamp = %s\n",
|
|
dev->support_info.support_lamp ? "TRUE" : "FALSE");
|
|
}
|
|
else
|
|
{
|
|
DBG (DBG_error, "Error in CMD_get_support_info, "
|
|
"sense_key=%d, ASC=%d, ASCQ=%d\n",
|
|
get_RS_sense_key (rs.sense),
|
|
get_RS_ASC (rs.sense), get_RS_ASCQ (rs.sense));
|
|
|
|
}
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
SANE_Status
|
|
CMD_scan (PKV_DEV dev)
|
|
{
|
|
SANE_Status status;
|
|
KV_CMD_HEADER hdr;
|
|
KV_CMD_RESPONSE rs;
|
|
|
|
DBG (DBG_proc, "CMD_scan\n");
|
|
|
|
memset (&hdr, 0, sizeof (hdr));
|
|
|
|
hdr.direction = KV_CMD_NONE;
|
|
hdr.cdb[0] = SCSI_SCAN;
|
|
hdr.cdb_size = 6;
|
|
|
|
status = kv_send_command (dev, &hdr, &rs);
|
|
|
|
if (status == 0 && rs.status != 0)
|
|
{
|
|
DBG (DBG_error,
|
|
"Error in CMD_scan, sense_key=%d, ASC=%d, ASCQ=%d\n",
|
|
get_RS_sense_key (rs.sense), get_RS_ASC (rs.sense),
|
|
get_RS_ASCQ (rs.sense));
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
SANE_Status
|
|
CMD_set_window (PKV_DEV dev, int side, PKV_CMD_RESPONSE rs)
|
|
{
|
|
unsigned char *window;
|
|
unsigned char *windowdata;
|
|
int size = 74;
|
|
KV_SCAN_MODE scan_mode;
|
|
KV_CMD_HEADER hdr;
|
|
|
|
DBG (DBG_proc, "CMD_set_window\n");
|
|
|
|
window = (unsigned char *) dev->buffer;
|
|
windowdata = window + 8;
|
|
|
|
memset (&hdr, 0, sizeof (hdr));
|
|
memset (window, 0, size);
|
|
|
|
Ito16 (66, &window[6]); /* Window descriptor block length */
|
|
|
|
/* Set window data */
|
|
|
|
scan_mode = kv_get_mode (dev);
|
|
|
|
kv_set_window_data (dev, scan_mode, side, windowdata);
|
|
|
|
hdr.direction = KV_CMD_OUT;
|
|
hdr.cdb_size = 10;
|
|
hdr.cdb[0] = SCSI_SET_WINDOW;
|
|
Ito24 (size, &hdr.cdb[6]);
|
|
hdr.data = window;
|
|
hdr.data_size = size;
|
|
|
|
hexdump (DBG_error, "window", window, size);
|
|
|
|
return kv_send_command (dev, &hdr, rs);
|
|
}
|
|
|
|
SANE_Status
|
|
CMD_reset_window (PKV_DEV dev)
|
|
{
|
|
KV_CMD_HEADER hdr;
|
|
KV_CMD_RESPONSE rs;
|
|
SANE_Status status;
|
|
|
|
DBG (DBG_proc, "CMD_reset_window\n");
|
|
|
|
memset (&hdr, 0, sizeof (hdr));
|
|
|
|
hdr.direction = KV_CMD_NONE;
|
|
hdr.cdb_size = 10;
|
|
hdr.cdb[0] = SCSI_SET_WINDOW;
|
|
|
|
status = kv_send_command (dev, &hdr, &rs);
|
|
if (rs.status != 0)
|
|
status = SANE_STATUS_INVAL;
|
|
|
|
return status;
|
|
}
|
|
|
|
SANE_Status
|
|
CMD_get_buff_status (PKV_DEV dev, int *front_size, int *back_size)
|
|
{
|
|
KV_CMD_HEADER hdr;
|
|
KV_CMD_RESPONSE rs;
|
|
SANE_Status status;
|
|
unsigned char *data = (unsigned char *) dev->buffer;
|
|
int size = 12;
|
|
memset (&hdr, 0, sizeof (hdr));
|
|
memset (data, 0, size);
|
|
|
|
hdr.direction = KV_CMD_IN;
|
|
hdr.cdb_size = 10;
|
|
hdr.cdb[0] = SCSI_GET_BUFFER_STATUS;
|
|
hdr.cdb[8] = size;
|
|
hdr.data = data;
|
|
hdr.data_size = size;
|
|
|
|
status = kv_send_command (dev, &hdr, &rs);
|
|
if (status == 0)
|
|
{
|
|
if (rs.status == KV_CHK_CONDITION)
|
|
return SANE_STATUS_NO_DOCS;
|
|
else
|
|
{
|
|
unsigned char *p = data + 4;
|
|
if (p[0] == SIDE_FRONT)
|
|
{
|
|
*front_size = (p[5] << 16) | (p[6] << 8) | p[7];
|
|
}
|
|
else
|
|
{
|
|
*back_size = (p[5] << 16) | (p[6] << 8) | p[7];
|
|
}
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
}
|
|
return status;
|
|
}
|
|
|
|
SANE_Status
|
|
CMD_wait_buff_status (PKV_DEV dev, int *front_size, int *back_size)
|
|
{
|
|
SANE_Status status = SANE_STATUS_GOOD;
|
|
int cnt = 0;
|
|
*front_size = 0;
|
|
*back_size = 0;
|
|
|
|
DBG (DBG_proc, "CMD_wait_buff_status: enter feed %s\n",
|
|
dev->val[OPT_MANUALFEED].s);
|
|
|
|
do
|
|
{
|
|
DBG (DBG_proc, "CMD_wait_buff_status: tray #%d of %d\n", cnt,
|
|
dev->val[OPT_FEED_TIMEOUT].w);
|
|
status = CMD_get_buff_status (dev, front_size, back_size);
|
|
sleep (1);
|
|
}
|
|
while (status == SANE_STATUS_GOOD && (*front_size == 0)
|
|
&& (*back_size == 0) && cnt++ < dev->val[OPT_FEED_TIMEOUT].w);
|
|
|
|
if (cnt > dev->val[OPT_FEED_TIMEOUT].w)
|
|
status = SANE_STATUS_NO_DOCS;
|
|
|
|
if (status == 0)
|
|
DBG (DBG_proc, "CMD_wait_buff_status: exit "
|
|
"front_size %d, back_size %d\n", *front_size, *back_size);
|
|
else
|
|
DBG (DBG_proc, "CMD_wait_buff_status: exit with no docs\n");
|
|
return status;
|
|
}
|
|
|
|
|
|
SANE_Status
|
|
CMD_read_pic_elements (PKV_DEV dev, int page, int side,
|
|
int *width, int *height)
|
|
{
|
|
SANE_Status status;
|
|
KV_CMD_HEADER hdr;
|
|
KV_CMD_RESPONSE rs;
|
|
|
|
DBG (DBG_proc, "CMD_read_pic_elements\n");
|
|
|
|
memset (&hdr, 0, sizeof (hdr));
|
|
|
|
hdr.direction = KV_CMD_IN;
|
|
hdr.cdb_size = 10;
|
|
hdr.cdb[0] = SCSI_READ_10;
|
|
hdr.cdb[2] = 0x80;
|
|
hdr.cdb[4] = page;
|
|
hdr.cdb[5] = side;
|
|
Ito24 (16, &hdr.cdb[6]);
|
|
hdr.data = dev->buffer;
|
|
hdr.data_size = 16;
|
|
|
|
status = kv_send_command (dev, &hdr, &rs);
|
|
if (status == 0)
|
|
{
|
|
if (rs.status == 0)
|
|
{
|
|
int s = side == SIDE_FRONT ? 0 : 1;
|
|
int depth = kv_get_depth (kv_get_mode (dev));
|
|
*width = B32TOI (dev->buffer);
|
|
*height = B32TOI (&dev->buffer[4]);
|
|
|
|
assert ((*width) % 8 == 0);
|
|
|
|
DBG (DBG_proc, "CMD_read_pic_elements: "
|
|
"Page %d, Side %s, W=%d, H=%d\n",
|
|
page, side == SIDE_FRONT ? "F" : "B", *width, *height);
|
|
|
|
dev->params[s].format = kv_get_mode (dev) == SM_COLOR ?
|
|
SANE_FRAME_RGB : SANE_FRAME_GRAY;
|
|
dev->params[s].last_frame = SANE_TRUE;
|
|
dev->params[s].depth = depth > 8 ? 8 : depth;
|
|
dev->params[s].lines = *height ? *height
|
|
: dev->val[OPT_LANDSCAPE].w ? (*width * 3) / 4 : (*width * 4) / 3;
|
|
dev->params[s].pixels_per_line = *width;
|
|
dev->params[s].bytes_per_line =
|
|
(dev->params[s].pixels_per_line / 8) * depth;
|
|
}
|
|
else
|
|
{
|
|
DBG (DBG_proc, "CMD_read_pic_elements: failed\n");
|
|
status = SANE_STATUS_INVAL;
|
|
}
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
SANE_Status
|
|
CMD_read_image (PKV_DEV dev, int page, int side,
|
|
unsigned char *buffer, int *psize, KV_CMD_RESPONSE * rs)
|
|
{
|
|
SANE_Status status;
|
|
KV_CMD_HEADER hdr;
|
|
int size = *psize;
|
|
|
|
DBG (DBG_proc, "CMD_read_image\n");
|
|
|
|
memset (&hdr, 0, sizeof (hdr));
|
|
|
|
hdr.direction = KV_CMD_IN;
|
|
hdr.cdb_size = 10;
|
|
hdr.cdb[0] = SCSI_READ_10;
|
|
hdr.cdb[4] = page;
|
|
hdr.cdb[5] = side;
|
|
Ito24 (size, &hdr.cdb[6]);
|
|
hdr.data = buffer;
|
|
hdr.data_size = size;
|
|
|
|
*psize = 0;
|
|
|
|
status = kv_send_command (dev, &hdr, rs);
|
|
|
|
if (status)
|
|
return status;
|
|
|
|
*psize = size;
|
|
|
|
if (rs->status == KV_CHK_CONDITION && get_RS_ILI (rs->sense))
|
|
{
|
|
int delta = B32TOI (&rs->sense[3]);
|
|
DBG (DBG_error, "size=%d, delta=0x%x (%d)\n", size, delta, delta);
|
|
*psize = size - delta;
|
|
}
|
|
|
|
DBG (DBG_error, "CMD_read_image: bytes requested=%d, read=%d\n",
|
|
size, *psize);
|
|
DBG (DBG_error, "CMD_read_image: ILI=%d, EOM=%d\n",
|
|
get_RS_ILI (rs->sense), get_RS_EOM (rs->sense));
|
|
|
|
return status;
|
|
}
|
|
|
|
SANE_Status
|
|
CMD_get_document_existanse (PKV_DEV dev)
|
|
{
|
|
SANE_Status status;
|
|
KV_CMD_HEADER hdr;
|
|
KV_CMD_RESPONSE rs;
|
|
|
|
DBG (DBG_proc, "CMD_get_document_existanse\n");
|
|
|
|
memset (&hdr, 0, sizeof (hdr));
|
|
|
|
hdr.direction = KV_CMD_IN;
|
|
hdr.cdb_size = 10;
|
|
hdr.cdb[0] = SCSI_READ_10;
|
|
hdr.cdb[2] = 0x81;
|
|
Ito24 (6, &hdr.cdb[6]);
|
|
hdr.data = dev->buffer;
|
|
hdr.data_size = 6;
|
|
|
|
status = kv_send_command (dev, &hdr, &rs);
|
|
if (status)
|
|
return status;
|
|
if (rs.status)
|
|
return SANE_STATUS_NO_DOCS;
|
|
if ((dev->buffer[0] & 0x20) != 0)
|
|
{
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
|
|
return SANE_STATUS_NO_DOCS;
|
|
}
|
|
|
|
SANE_Status
|
|
CMD_wait_document_existanse (PKV_DEV dev)
|
|
{
|
|
SANE_Status status;
|
|
KV_CMD_HEADER hdr;
|
|
KV_CMD_RESPONSE rs;
|
|
int cnt;
|
|
|
|
DBG (DBG_proc, "CMD_wait_document_existanse\n");
|
|
|
|
memset (&hdr, 0, sizeof (hdr));
|
|
|
|
hdr.direction = KV_CMD_IN;
|
|
hdr.cdb_size = 10;
|
|
hdr.cdb[0] = SCSI_READ_10;
|
|
hdr.cdb[2] = 0x81;
|
|
Ito24 (6, &hdr.cdb[6]);
|
|
hdr.data = dev->buffer;
|
|
hdr.data_size = 6;
|
|
|
|
for (cnt = 0; cnt < dev->val[OPT_FEED_TIMEOUT].w; cnt++)
|
|
{
|
|
DBG (DBG_proc, "CMD_wait_document_existanse: tray #%d of %d\n", cnt,
|
|
dev->val[OPT_FEED_TIMEOUT].w);
|
|
status = kv_send_command (dev, &hdr, &rs);
|
|
if (status)
|
|
return status;
|
|
if (rs.status)
|
|
return SANE_STATUS_NO_DOCS;
|
|
if ((dev->buffer[0] & 0x20) != 0)
|
|
{
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
else if (strcmp (dev->val[OPT_MANUALFEED].s, "off") == 0)
|
|
{
|
|
return SANE_STATUS_NO_DOCS;
|
|
}
|
|
sleep (1);
|
|
}
|
|
|
|
return SANE_STATUS_NO_DOCS;
|
|
}
|
|
|
|
SANE_Status
|
|
CMD_request_sense (PKV_DEV dev)
|
|
{
|
|
KV_CMD_HEADER hdr;
|
|
KV_CMD_RESPONSE rs;
|
|
|
|
DBG (DBG_proc, "CMD_request_sense\n");
|
|
memset (&hdr, 0, sizeof (hdr));
|
|
hdr.direction = KV_CMD_IN;
|
|
hdr.cdb[0] = SCSI_REQUEST_SENSE;
|
|
hdr.cdb[4] = 0x12;
|
|
hdr.cdb_size = 6;
|
|
hdr.data_size = 0x12;
|
|
hdr.data = dev->buffer;
|
|
|
|
return kv_send_command (dev, &hdr, &rs);
|
|
}
|
|
|
|
/* Scan routines */
|
|
|
|
/* Allocate image buffer for one page (1 or 2 sides) */
|
|
|
|
SANE_Status
|
|
AllocateImageBuffer (PKV_DEV dev)
|
|
{
|
|
int *size = dev->bytes_to_read;
|
|
int sides = IS_DUPLEX (dev) ? 2 : 1;
|
|
int i;
|
|
size[0] = dev->params[0].bytes_per_line * dev->params[0].lines;
|
|
size[1] = dev->params[1].bytes_per_line * dev->params[1].lines;
|
|
|
|
DBG (DBG_proc, "AllocateImageBuffer: enter\n");
|
|
|
|
for (i = 0; i < sides; i++)
|
|
{
|
|
SANE_Byte *p;
|
|
DBG (DBG_proc, "AllocateImageBuffer: size(%c)=%d\n",
|
|
i ? 'B' : 'F', size[i]);
|
|
|
|
if (dev->img_buffers[i] == NULL)
|
|
{
|
|
p = (SANE_Byte *) malloc (size[i]);
|
|
if (p == NULL)
|
|
{
|
|
return SANE_STATUS_NO_MEM;
|
|
}
|
|
dev->img_buffers[i] = p;
|
|
}
|
|
else
|
|
{
|
|
p = (SANE_Byte *) realloc (dev->img_buffers[i], size[i]);
|
|
if (p == NULL)
|
|
{
|
|
return SANE_STATUS_NO_MEM;
|
|
}
|
|
else
|
|
{
|
|
dev->img_buffers[i] = p;
|
|
}
|
|
}
|
|
}
|
|
DBG (DBG_proc, "AllocateImageBuffer: exit\n");
|
|
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
|
|
/* Read image data from scanner dev->img_buffers[0],
|
|
for the simplex page */
|
|
SANE_Status
|
|
ReadImageDataSimplex (PKV_DEV dev, int page)
|
|
{
|
|
int bytes_to_read = dev->bytes_to_read[0];
|
|
SANE_Byte *buffer = (SANE_Byte *) dev->buffer;
|
|
int buff_size = SCSI_BUFFER_SIZE;
|
|
SANE_Byte *pt = dev->img_buffers[0];
|
|
KV_CMD_RESPONSE rs;
|
|
dev->img_size[0] = 0;
|
|
dev->img_size[1] = 0;
|
|
|
|
/* read loop */
|
|
do
|
|
{
|
|
int size = buff_size;
|
|
SANE_Status status;
|
|
DBG (DBG_error, "Bytes left = %d\n", bytes_to_read);
|
|
status = CMD_read_image (dev, page, SIDE_FRONT, buffer, &size, &rs);
|
|
if (status)
|
|
{
|
|
return status;
|
|
}
|
|
if (rs.status)
|
|
{
|
|
if (get_RS_sense_key (rs.sense))
|
|
{
|
|
DBG (DBG_error, "Error reading image data, "
|
|
"sense_key=%d, ASC=%d, ASCQ=%d",
|
|
get_RS_sense_key (rs.sense),
|
|
get_RS_ASC (rs.sense), get_RS_ASCQ (rs.sense));
|
|
|
|
if (get_RS_sense_key (rs.sense) == 3)
|
|
{
|
|
if (!get_RS_ASCQ (rs.sense))
|
|
return SANE_STATUS_NO_DOCS;
|
|
return SANE_STATUS_JAMMED;
|
|
}
|
|
return SANE_STATUS_IO_ERROR;
|
|
}
|
|
|
|
}
|
|
/* copy data to image buffer */
|
|
if (size > bytes_to_read)
|
|
{
|
|
size = bytes_to_read;
|
|
}
|
|
if (size > 0)
|
|
{
|
|
memcpy (pt, buffer, size);
|
|
bytes_to_read -= size;
|
|
pt += size;
|
|
dev->img_size[0] += size;
|
|
}
|
|
}
|
|
while (!get_RS_EOM (rs.sense));
|
|
|
|
assert (pt == dev->img_buffers[0] + dev->img_size[0]);
|
|
DBG (DBG_error, "Image size = %d\n", dev->img_size[0]);
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
|
|
/* Read image data from scanner dev->img_buffers[0],
|
|
for the duplex page */
|
|
SANE_Status
|
|
ReadImageDataDuplex (PKV_DEV dev, int page)
|
|
{
|
|
int bytes_to_read[2];
|
|
SANE_Byte *buffer = (SANE_Byte *) dev->buffer;
|
|
int buff_size[2];
|
|
SANE_Byte *pt[2];
|
|
KV_CMD_RESPONSE rs;
|
|
int sides[2];
|
|
SANE_Bool eoms[2];
|
|
int current_side = 1;
|
|
|
|
bytes_to_read[0] = dev->bytes_to_read[0];
|
|
bytes_to_read[1] = dev->bytes_to_read[1];
|
|
|
|
pt[0] = dev->img_buffers[0];
|
|
pt[1] = dev->img_buffers[1];
|
|
|
|
sides[0] = SIDE_FRONT;
|
|
sides[1] = SIDE_BACK;
|
|
eoms[0] = eoms[1] = 0;
|
|
|
|
buff_size[0] = SCSI_BUFFER_SIZE;
|
|
buff_size[1] = SCSI_BUFFER_SIZE;
|
|
dev->img_size[0] = 0;
|
|
dev->img_size[1] = 0;
|
|
|
|
/* read loop */
|
|
do
|
|
{
|
|
int size = buff_size[current_side];
|
|
SANE_Status status;
|
|
DBG (DBG_error, "Bytes left (F) = %d\n", bytes_to_read[0]);
|
|
DBG (DBG_error, "Bytes left (B) = %d\n", bytes_to_read[1]);
|
|
|
|
status = CMD_read_image (dev, page, sides[current_side],
|
|
buffer, &size, &rs);
|
|
if (status)
|
|
{
|
|
return status;
|
|
}
|
|
if (rs.status)
|
|
{
|
|
if (get_RS_sense_key (rs.sense))
|
|
{
|
|
DBG (DBG_error, "Error reading image data, "
|
|
"sense_key=%d, ASC=%d, ASCQ=%d",
|
|
get_RS_sense_key (rs.sense),
|
|
get_RS_ASC (rs.sense), get_RS_ASCQ (rs.sense));
|
|
|
|
if (get_RS_sense_key (rs.sense) == 3)
|
|
{
|
|
if (!get_RS_ASCQ (rs.sense))
|
|
return SANE_STATUS_NO_DOCS;
|
|
return SANE_STATUS_JAMMED;
|
|
}
|
|
return SANE_STATUS_IO_ERROR;
|
|
}
|
|
}
|
|
|
|
/* copy data to image buffer */
|
|
if (size > bytes_to_read[current_side])
|
|
{
|
|
size = bytes_to_read[current_side];
|
|
}
|
|
if (size > 0)
|
|
{
|
|
memcpy (pt[current_side], buffer, size);
|
|
bytes_to_read[current_side] -= size;
|
|
pt[current_side] += size;
|
|
dev->img_size[current_side] += size;
|
|
}
|
|
if (rs.status)
|
|
{
|
|
if (get_RS_EOM (rs.sense))
|
|
{
|
|
eoms[current_side] = 1;
|
|
}
|
|
if (get_RS_ILI (rs.sense))
|
|
{
|
|
current_side++;
|
|
current_side &= 1;
|
|
}
|
|
}
|
|
}
|
|
while (eoms[0] == 0 || eoms[1] == 0);
|
|
|
|
DBG (DBG_error, "Image size (F) = %d\n", dev->img_size[0]);
|
|
DBG (DBG_error, "Image size (B) = %d\n", dev->img_size[1]);
|
|
|
|
assert (pt[0] == dev->img_buffers[0] + dev->img_size[0]);
|
|
assert (pt[1] == dev->img_buffers[1] + dev->img_size[1]);
|
|
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
|
|
/* Read image data for one page */
|
|
SANE_Status
|
|
ReadImageData (PKV_DEV dev, int page)
|
|
{
|
|
SANE_Status status;
|
|
DBG (DBG_proc, "Reading image data for page %d\n", page);
|
|
|
|
if (IS_DUPLEX (dev))
|
|
{
|
|
DBG (DBG_proc, "ReadImageData: Duplex %d\n", page);
|
|
status = ReadImageDataDuplex (dev, page);
|
|
}
|
|
else
|
|
{
|
|
DBG (DBG_proc, "ReadImageData: Simplex %d\n", page);
|
|
status = ReadImageDataSimplex (dev, page);
|
|
}
|
|
dev->img_pt[0] = dev->img_buffers[0];
|
|
dev->img_pt[1] = dev->img_buffers[1];
|
|
|
|
DBG (DBG_proc, "Reading image data for page %d, finished\n", page);
|
|
|
|
return status;
|
|
}
|
|
|
|
/* Look in image for likely upper and left paper edges, then rotate
|
|
* image so that upper left corner of paper is upper left of image.
|
|
* FIXME: should we do this before we binarize instead of after? */
|
|
SANE_Status
|
|
buffer_deskew(PKV_DEV s, int side)
|
|
{
|
|
SANE_Status ret = SANE_STATUS_GOOD;
|
|
int bg_color = 0xd6;
|
|
int side_index = (side == SIDE_FRONT)?0:1;
|
|
int resolution = s->val[OPT_RESOLUTION].w;
|
|
|
|
DBG (10, "buffer_deskew: start\n");
|
|
|
|
/*only find skew on first image from a page, or if first image had error */
|
|
if(side == SIDE_FRONT || s->deskew_stat){
|
|
|
|
s->deskew_stat = sanei_magic_findSkew(
|
|
&s->params[side_index],s->img_buffers[side_index],
|
|
resolution,resolution,
|
|
&s->deskew_vals[0],&s->deskew_vals[1],&s->deskew_slope);
|
|
|
|
if(s->deskew_stat){
|
|
DBG (5, "buffer_despeck: bad findSkew, bailing\n");
|
|
goto cleanup;
|
|
}
|
|
}
|
|
/* backside images can use a 'flipped' version of frontside data */
|
|
else{
|
|
s->deskew_slope *= -1;
|
|
s->deskew_vals[0]
|
|
= s->params[side_index].pixels_per_line - s->deskew_vals[0];
|
|
}
|
|
|
|
ret = sanei_magic_rotate(&s->params[side_index],s->img_buffers[side_index],
|
|
s->deskew_vals[0],s->deskew_vals[1],s->deskew_slope,bg_color);
|
|
|
|
if(ret){
|
|
DBG(5,"buffer_deskew: rotate error: %d",ret);
|
|
ret = SANE_STATUS_GOOD;
|
|
goto cleanup;
|
|
}
|
|
|
|
cleanup:
|
|
DBG (10, "buffer_deskew: finish\n");
|
|
return ret;
|
|
}
|
|
|
|
/* Look in image for likely left/right/bottom paper edges, then crop image.
|
|
* Does not attempt to rotate the image, that should be done first.
|
|
* FIXME: should we do this before we binarize instead of after? */
|
|
SANE_Status
|
|
buffer_crop(PKV_DEV s, int side)
|
|
{
|
|
SANE_Status ret = SANE_STATUS_GOOD;
|
|
int side_index = (side == SIDE_FRONT)?0:1;
|
|
int resolution = s->val[OPT_RESOLUTION].w;
|
|
|
|
DBG (10, "buffer_crop: start\n");
|
|
|
|
/*only find edges on first image from a page, or if first image had error */
|
|
if(side == SIDE_FRONT || s->crop_stat){
|
|
|
|
s->crop_stat = sanei_magic_findEdges(
|
|
&s->params[side_index],s->img_buffers[side_index],
|
|
resolution,resolution,
|
|
&s->crop_vals[0],&s->crop_vals[1],&s->crop_vals[2],&s->crop_vals[3]);
|
|
|
|
if(s->crop_stat){
|
|
DBG (5, "buffer_crop: bad edges, bailing\n");
|
|
goto cleanup;
|
|
}
|
|
|
|
DBG (15, "buffer_crop: t:%d b:%d l:%d r:%d\n",
|
|
s->crop_vals[0],s->crop_vals[1],s->crop_vals[2],s->crop_vals[3]);
|
|
|
|
/* we dont listen to the 'top' value, since the top is not padded */
|
|
/*s->crop_vals[0] = 0;*/
|
|
}
|
|
/* backside images can use a 'flipped' version of frontside data */
|
|
else{
|
|
int left = s->crop_vals[2];
|
|
int right = s->crop_vals[3];
|
|
|
|
s->crop_vals[2] = s->params[side_index].pixels_per_line - right;
|
|
s->crop_vals[3] = s->params[side_index].pixels_per_line - left;
|
|
}
|
|
|
|
/* now crop the image */
|
|
ret = sanei_magic_crop(&s->params[side_index],s->img_buffers[side_index],
|
|
s->crop_vals[0],s->crop_vals[1],s->crop_vals[2],s->crop_vals[3]);
|
|
|
|
if(ret){
|
|
DBG (5, "buffer_crop: bad crop, bailing\n");
|
|
ret = SANE_STATUS_GOOD;
|
|
goto cleanup;
|
|
}
|
|
|
|
/* update image size counter to new, smaller size */
|
|
s->img_size[side_index]
|
|
= s->params[side_index].lines * s->params[side_index].bytes_per_line;
|
|
|
|
cleanup:
|
|
DBG (10, "buffer_crop: finish\n");
|
|
return ret;
|
|
}
|
|
|
|
/* Look in image for disconnected 'spots' of the requested size.
|
|
* Replace the spots with the average color of the surrounding pixels.
|
|
* FIXME: should we do this before we binarize instead of after? */
|
|
SANE_Status
|
|
buffer_despeck(PKV_DEV s, int side)
|
|
{
|
|
SANE_Status ret = SANE_STATUS_GOOD;
|
|
int side_index = (side == SIDE_FRONT)?0:1;
|
|
|
|
DBG (10, "buffer_despeck: start\n");
|
|
|
|
ret = sanei_magic_despeck(
|
|
&s->params[side_index],s->img_buffers[side_index],s->val[OPT_SWDESPECK].w
|
|
);
|
|
if(ret){
|
|
DBG (5, "buffer_despeck: bad despeck, bailing\n");
|
|
ret = SANE_STATUS_GOOD;
|
|
goto cleanup;
|
|
}
|
|
|
|
cleanup:
|
|
DBG (10, "buffer_despeck: finish\n");
|
|
return ret;
|
|
}
|
|
|
|
/* Look if image has too few dark pixels.
|
|
* FIXME: should we do this before we binarize instead of after? */
|
|
int
|
|
buffer_isblank(PKV_DEV s, int side)
|
|
{
|
|
SANE_Status ret = SANE_STATUS_GOOD;
|
|
int side_index = (side == SIDE_FRONT)?0:1;
|
|
int status = 0;
|
|
|
|
DBG (10, "buffer_isblank: start\n");
|
|
|
|
ret = sanei_magic_isBlank(
|
|
&s->params[side_index],s->img_buffers[side_index],
|
|
SANE_UNFIX(s->val[OPT_SWSKIP].w)
|
|
);
|
|
|
|
if(ret == SANE_STATUS_NO_DOCS){
|
|
DBG (5, "buffer_isblank: blank!\n");
|
|
status = 1;
|
|
}
|
|
else if(ret){
|
|
DBG (5, "buffer_isblank: error %d\n",ret);
|
|
}
|
|
|
|
DBG (10, "buffer_isblank: finished\n");
|
|
return status;
|
|
}
|
|
|
|
/* Look if image needs rotation
|
|
* FIXME: should we do this before we binarize instead of after? */
|
|
SANE_Status
|
|
buffer_rotate(PKV_DEV s, int side)
|
|
{
|
|
SANE_Status ret = SANE_STATUS_GOOD;
|
|
int angle = 0;
|
|
int side_index = (side == SIDE_FRONT)?0:1;
|
|
int resolution = s->val[OPT_RESOLUTION].w;
|
|
|
|
DBG (10, "buffer_rotate: start\n");
|
|
|
|
if(s->val[OPT_SWDEROTATE].w){
|
|
ret = sanei_magic_findTurn(
|
|
&s->params[side_index],s->img_buffers[side_index],
|
|
resolution,resolution,&angle);
|
|
|
|
if(ret){
|
|
DBG (5, "buffer_rotate: error %d\n",ret);
|
|
ret = SANE_STATUS_GOOD;
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
angle += s->val[OPT_ROTATE].w;
|
|
|
|
/*90 or 270 degree rotations are reversed on back side*/
|
|
if(side == SIDE_BACK && s->val[OPT_ROTATE].w % 180){
|
|
angle += 180;
|
|
}
|
|
|
|
ret = sanei_magic_turn(
|
|
&s->params[side_index],s->img_buffers[side_index],
|
|
angle);
|
|
|
|
if(ret){
|
|
DBG (5, "buffer_rotate: error %d\n",ret);
|
|
ret = SANE_STATUS_GOOD;
|
|
goto cleanup;
|
|
}
|
|
|
|
/* update image size counter to new, smaller size */
|
|
s->img_size[side_index]
|
|
= s->params[side_index].lines * s->params[side_index].bytes_per_line;
|
|
|
|
cleanup:
|
|
DBG (10, "buffer_rotate: finished\n");
|
|
return ret;
|
|
}
|