2010-02-13 03:23:14 +00:00
|
|
|
/*
|
|
|
|
Copyright (C) 2008, Panasonic Russia Ltd.
|
2011-06-07 00:52:25 +00:00
|
|
|
Copyright (C) 2010-2011, m. allan noah
|
2010-02-13 03:23:14 +00:00
|
|
|
*/
|
|
|
|
/* sane - Scanner Access Now Easy.
|
|
|
|
Panasonic KV-S1020C / KV-S1025C USB scanners.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#define DEBUG_NOT_STATIC
|
|
|
|
|
|
|
|
#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 "kvs1025.h"
|
|
|
|
#include "kvs1025_low.h"
|
|
|
|
|
|
|
|
#include "../include/sane/sanei_debug.h"
|
|
|
|
|
|
|
|
/* SANE backend operations, see Sane standard 1.04 documents (sane_dev.pdf)
|
|
|
|
for details */
|
|
|
|
|
|
|
|
/* Init the KV-S1025 SANE backend. This function must be called before any other
|
|
|
|
SANE function can be called. */
|
|
|
|
SANE_Status
|
|
|
|
sane_init (SANE_Int * version_code,
|
|
|
|
SANE_Auth_Callback __sane_unused__ authorize)
|
|
|
|
{
|
|
|
|
SANE_Status status;
|
|
|
|
|
|
|
|
DBG_INIT ();
|
|
|
|
|
|
|
|
DBG (DBG_sane_init, "sane_init\n");
|
|
|
|
|
|
|
|
DBG (DBG_error,
|
2010-03-26 04:01:16 +00:00
|
|
|
"This is panasonic KV-S1020C / KV-S1025C version %d.%d build %d\n",
|
2010-02-13 03:23:14 +00:00
|
|
|
V_MAJOR, V_MINOR, V_BUILD);
|
|
|
|
|
|
|
|
if (version_code)
|
|
|
|
{
|
|
|
|
*version_code = SANE_VERSION_CODE (V_MAJOR, V_MINOR, V_BUILD);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Initialize USB */
|
2010-04-01 19:33:50 +00:00
|
|
|
sanei_usb_init ();
|
2010-02-13 03:23:14 +00:00
|
|
|
|
|
|
|
status = kv_enum_devices ();
|
|
|
|
if (status)
|
|
|
|
return status;
|
|
|
|
|
|
|
|
DBG (DBG_proc, "sane_init: leave\n");
|
|
|
|
return SANE_STATUS_GOOD;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Terminate the KV-S1025 SANE backend */
|
|
|
|
void
|
2010-04-02 21:02:21 +00:00
|
|
|
sane_exit (void)
|
2010-02-13 03:23:14 +00:00
|
|
|
{
|
|
|
|
DBG (DBG_proc, "sane_exit: enter\n");
|
|
|
|
|
|
|
|
kv_exit ();
|
|
|
|
|
|
|
|
DBG (DBG_proc, "sane_exit: exit\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Get device list */
|
|
|
|
SANE_Status
|
|
|
|
sane_get_devices (const SANE_Device *** device_list,
|
|
|
|
SANE_Bool __sane_unused__ local_only)
|
|
|
|
{
|
|
|
|
DBG (DBG_proc, "sane_get_devices: enter\n");
|
|
|
|
kv_get_devices_list (device_list);
|
|
|
|
DBG (DBG_proc, "sane_get_devices: leave\n");
|
|
|
|
return SANE_STATUS_GOOD;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Open device, return the device handle */
|
|
|
|
SANE_Status
|
|
|
|
sane_open (SANE_String_Const devicename, SANE_Handle * handle)
|
|
|
|
{
|
|
|
|
return kv_open_by_name (devicename, handle);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Close device */
|
|
|
|
void
|
|
|
|
sane_close (SANE_Handle handle)
|
|
|
|
{
|
|
|
|
DBG (DBG_proc, "sane_close: enter\n");
|
|
|
|
kv_close ((PKV_DEV) handle);
|
|
|
|
DBG (DBG_proc, "sane_close: leave\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Get option descriptor */
|
|
|
|
const SANE_Option_Descriptor *
|
|
|
|
sane_get_option_descriptor (SANE_Handle handle, SANE_Int option)
|
|
|
|
{
|
|
|
|
return kv_get_option_descriptor ((PKV_DEV) handle, option);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Control option */
|
|
|
|
SANE_Status
|
|
|
|
sane_control_option (SANE_Handle handle, SANE_Int option,
|
|
|
|
SANE_Action action, void *val, SANE_Int * info)
|
|
|
|
{
|
|
|
|
return kv_control_option ((PKV_DEV) handle, option, action, val, info);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Get scan parameters */
|
|
|
|
SANE_Status
|
|
|
|
sane_get_parameters (SANE_Handle handle, SANE_Parameters * params)
|
|
|
|
{
|
|
|
|
PKV_DEV dev = (PKV_DEV) handle;
|
|
|
|
|
|
|
|
int side = dev->current_side == SIDE_FRONT ? 0 : 1;
|
|
|
|
|
|
|
|
DBG (DBG_proc, "sane_get_parameters: enter\n");
|
|
|
|
|
|
|
|
if (!(dev->scanning))
|
|
|
|
{
|
|
|
|
/* Setup the parameters for the scan. (guessed value) */
|
|
|
|
int resolution = dev->val[OPT_RESOLUTION].w;
|
|
|
|
int width, length, depth = kv_get_depth (kv_get_mode (dev));;
|
|
|
|
|
|
|
|
DBG (DBG_proc, "sane_get_parameters: initial settings\n");
|
|
|
|
kv_calc_paper_size (dev, &width, &length);
|
|
|
|
|
|
|
|
DBG (DBG_error, "Resolution = %d\n", resolution);
|
|
|
|
DBG (DBG_error, "Paper width = %d, height = %d\n", width, length);
|
|
|
|
|
|
|
|
/* Prepare the parameters for the caller. */
|
|
|
|
dev->params[0].format = kv_get_mode (dev) == SM_COLOR ?
|
|
|
|
SANE_FRAME_RGB : SANE_FRAME_GRAY;
|
|
|
|
|
|
|
|
dev->params[0].last_frame = SANE_TRUE;
|
|
|
|
dev->params[0].pixels_per_line = ((width * resolution) / 1200) & (~0xf);
|
|
|
|
|
|
|
|
dev->params[0].depth = depth > 8 ? 8 : depth;
|
|
|
|
|
|
|
|
dev->params[0].bytes_per_line =
|
|
|
|
(dev->params[0].pixels_per_line / 8) * depth;
|
|
|
|
dev->params[0].lines = (length * resolution) / 1200;
|
|
|
|
|
|
|
|
memcpy (&dev->params[1], &dev->params[0], sizeof (SANE_Parameters));
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Return the current values. */
|
|
|
|
if (params)
|
|
|
|
*params = (dev->params[side]);
|
|
|
|
|
|
|
|
DBG (DBG_proc, "sane_get_parameters: exit\n");
|
|
|
|
return SANE_STATUS_GOOD;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Start scanning */
|
|
|
|
SANE_Status
|
|
|
|
sane_start (SANE_Handle handle)
|
|
|
|
{
|
|
|
|
SANE_Status status;
|
|
|
|
PKV_DEV dev = (PKV_DEV) handle;
|
|
|
|
SANE_Bool dev_ready;
|
|
|
|
KV_CMD_RESPONSE rs;
|
|
|
|
|
|
|
|
DBG (DBG_proc, "sane_start: enter\n");
|
|
|
|
if (!dev->scanning)
|
|
|
|
{
|
|
|
|
/* open device */
|
|
|
|
if (!kv_already_open (dev))
|
|
|
|
{
|
|
|
|
DBG (DBG_proc, "sane_start: need to open device\n");
|
|
|
|
status = kv_open (dev);
|
|
|
|
if (status)
|
|
|
|
{
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* Begin scan */
|
|
|
|
DBG (DBG_proc, "sane_start: begin scan\n");
|
|
|
|
|
|
|
|
/* Get necessary parameters */
|
|
|
|
sane_get_parameters (dev, NULL);
|
|
|
|
|
|
|
|
dev->current_page = 0;
|
|
|
|
dev->current_side = SIDE_FRONT;
|
|
|
|
|
|
|
|
/* The scanner must be ready. */
|
|
|
|
status = CMD_test_unit_ready (dev, &dev_ready);
|
|
|
|
if (status || !dev_ready)
|
|
|
|
{
|
|
|
|
return SANE_STATUS_DEVICE_BUSY;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!strcmp (dev->val[OPT_MANUALFEED].s, "off"))
|
|
|
|
{
|
|
|
|
status = CMD_get_document_existanse (dev);
|
|
|
|
if (status)
|
|
|
|
{
|
|
|
|
DBG (DBG_proc, "sane_start: exit with no more docs\n");
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Set window */
|
|
|
|
status = CMD_reset_window (dev);
|
|
|
|
if (status)
|
|
|
|
{
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
status = CMD_set_window (dev, SIDE_FRONT, &rs);
|
|
|
|
if (status)
|
|
|
|
{
|
|
|
|
DBG (DBG_proc, "sane_start: error setting window\n");
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (rs.status)
|
|
|
|
{
|
|
|
|
DBG (DBG_proc, "sane_start: error setting window\n");
|
|
|
|
DBG (DBG_proc,
|
|
|
|
"sane_start: sense_key=0x%x, ASC=0x%x, ASCQ=0x%x\n",
|
|
|
|
get_RS_sense_key (rs.sense),
|
|
|
|
get_RS_ASC (rs.sense), get_RS_ASCQ (rs.sense));
|
|
|
|
return SANE_STATUS_DEVICE_BUSY;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (IS_DUPLEX (dev))
|
|
|
|
{
|
|
|
|
status = CMD_set_window (dev, SIDE_BACK, &rs);
|
|
|
|
|
|
|
|
if (status)
|
|
|
|
{
|
|
|
|
DBG (DBG_proc, "sane_start: error setting window\n");
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
if (rs.status)
|
|
|
|
{
|
|
|
|
DBG (DBG_proc, "sane_start: error setting window\n");
|
|
|
|
DBG (DBG_proc,
|
|
|
|
"sane_start: sense_key=0x%x, "
|
|
|
|
"ASC=0x%x, ASCQ=0x%x\n",
|
|
|
|
get_RS_sense_key (rs.sense),
|
|
|
|
get_RS_ASC (rs.sense), get_RS_ASCQ (rs.sense));
|
|
|
|
return SANE_STATUS_INVAL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Scan */
|
|
|
|
status = CMD_scan (dev);
|
|
|
|
if (status)
|
|
|
|
{
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
status = AllocateImageBuffer (dev);
|
|
|
|
if (status)
|
|
|
|
{
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
dev->scanning = 1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* renew page */
|
|
|
|
if (IS_DUPLEX (dev))
|
|
|
|
{
|
|
|
|
if (dev->current_side == SIDE_FRONT)
|
|
|
|
{
|
2011-06-07 00:52:25 +00:00
|
|
|
/* back image data already read, so just return */
|
2010-02-13 03:23:14 +00:00
|
|
|
dev->current_side = SIDE_BACK;
|
2011-06-07 00:52:25 +00:00
|
|
|
DBG (DBG_proc, "sane_start: duplex back\n");
|
|
|
|
status = SANE_STATUS_GOOD;
|
|
|
|
goto cleanup;
|
2010-02-13 03:23:14 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
dev->current_side = SIDE_FRONT;
|
|
|
|
dev->current_page++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
dev->current_page++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
DBG (DBG_proc, "sane_start: NOW SCANNING page\n");
|
|
|
|
|
|
|
|
/* Read image data */
|
|
|
|
status = ReadImageData (dev, dev->current_page);
|
|
|
|
if (status)
|
|
|
|
{
|
|
|
|
dev->scanning = 0;
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Get picture element size */
|
|
|
|
{
|
|
|
|
int width, height;
|
|
|
|
status = CMD_read_pic_elements (dev, dev->current_page,
|
|
|
|
SIDE_FRONT, &width, &height);
|
|
|
|
if (status)
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (IS_DUPLEX (dev))
|
|
|
|
{
|
|
|
|
int width, height;
|
|
|
|
status = CMD_read_pic_elements (dev, dev->current_page,
|
|
|
|
SIDE_BACK, &width, &height);
|
|
|
|
if (status)
|
|
|
|
return status;
|
|
|
|
}
|
2011-06-07 00:52:25 +00:00
|
|
|
|
|
|
|
/* software based enhancement functions from sanei_magic */
|
|
|
|
/* these will modify the image, and adjust the params */
|
|
|
|
/* at this point, we are only looking at the front image */
|
|
|
|
/* of simplex or duplex data, back side has already exited */
|
|
|
|
/* so, we do both sides now, if required */
|
|
|
|
if (dev->val[OPT_SWDESKEW].w){
|
|
|
|
buffer_deskew(dev,SIDE_FRONT);
|
|
|
|
}
|
|
|
|
if (dev->val[OPT_SWCROP].w){
|
|
|
|
buffer_crop(dev,SIDE_FRONT);
|
|
|
|
}
|
|
|
|
if (dev->val[OPT_SWDESPECK].w){
|
|
|
|
buffer_despeck(dev,SIDE_FRONT);
|
|
|
|
}
|
|
|
|
if (dev->val[OPT_SWDEROTATE].w || dev->val[OPT_ROTATE].w){
|
|
|
|
buffer_rotate(dev,SIDE_FRONT);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (IS_DUPLEX (dev)){
|
|
|
|
if (dev->val[OPT_SWDESKEW].w){
|
|
|
|
buffer_deskew(dev,SIDE_BACK);
|
|
|
|
}
|
|
|
|
if (dev->val[OPT_SWCROP].w){
|
|
|
|
buffer_crop(dev,SIDE_BACK);
|
|
|
|
}
|
|
|
|
if (dev->val[OPT_SWDESPECK].w){
|
|
|
|
buffer_despeck(dev,SIDE_BACK);
|
|
|
|
}
|
|
|
|
if (dev->val[OPT_SWDEROTATE].w || dev->val[OPT_ROTATE].w){
|
|
|
|
buffer_rotate(dev,SIDE_BACK);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
|
|
|
|
/* check if we need to skip this page */
|
|
|
|
if (dev->val[OPT_SWSKIP].w && buffer_isblank(dev,dev->current_side)){
|
|
|
|
DBG (DBG_proc, "sane_start: blank page, recurse\n");
|
|
|
|
return sane_start(handle);
|
|
|
|
}
|
|
|
|
|
2010-02-13 03:23:14 +00:00
|
|
|
DBG (DBG_proc, "sane_start: exit\n");
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
SANE_Status
|
|
|
|
sane_read (SANE_Handle handle, SANE_Byte * buf,
|
|
|
|
SANE_Int max_len, SANE_Int * len)
|
|
|
|
{
|
|
|
|
PKV_DEV dev = (PKV_DEV) handle;
|
|
|
|
int side = dev->current_side == SIDE_FRONT ? 0 : 1;
|
|
|
|
|
|
|
|
int size = max_len;
|
|
|
|
if (!dev->scanning)
|
|
|
|
return SANE_STATUS_EOF;
|
|
|
|
|
|
|
|
if (size > dev->img_size[side])
|
|
|
|
size = dev->img_size[side];
|
|
|
|
|
|
|
|
if (size == 0)
|
|
|
|
{
|
|
|
|
*len = size;
|
|
|
|
return SANE_STATUS_EOF;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dev->val[OPT_INVERSE].w &&
|
|
|
|
(kv_get_mode (dev) == SM_BINARY || kv_get_mode (dev) == SM_DITHER))
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
unsigned char *p = dev->img_pt[side];
|
|
|
|
for (i = 0; i < size; i++)
|
|
|
|
{
|
|
|
|
buf[i] = ~p[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
memcpy (buf, dev->img_pt[side], size);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*hexdump(DBG_error, "img data", buf, 128); */
|
|
|
|
|
|
|
|
dev->img_pt[side] += size;
|
|
|
|
dev->img_size[side] -= size;
|
|
|
|
|
|
|
|
DBG (DBG_proc, "sane_read: %d bytes to read, "
|
|
|
|
"%d bytes read, EOF=%s %d\n",
|
|
|
|
max_len, size, dev->img_size[side] == 0 ? "True" : "False", side);
|
|
|
|
|
|
|
|
if (len)
|
|
|
|
{
|
|
|
|
*len = size;
|
|
|
|
}
|
|
|
|
if (dev->img_size[side] == 0)
|
|
|
|
{
|
|
|
|
if (!strcmp (dev->val[OPT_FEEDER_MODE].s, "single"))
|
|
|
|
if ((IS_DUPLEX (dev) && side) || !IS_DUPLEX (dev))
|
|
|
|
dev->scanning = 0;
|
|
|
|
}
|
|
|
|
return SANE_STATUS_GOOD;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
sane_cancel (SANE_Handle handle)
|
|
|
|
{
|
|
|
|
PKV_DEV dev = (PKV_DEV) handle;
|
|
|
|
DBG (DBG_proc, "sane_cancel: scan canceled.\n");
|
|
|
|
dev->scanning = 0;
|
|
|
|
|
|
|
|
kv_close (dev);
|
|
|
|
}
|
|
|
|
|
|
|
|
SANE_Status
|
|
|
|
sane_set_io_mode (SANE_Handle h, SANE_Bool m)
|
|
|
|
{
|
2010-03-26 04:01:16 +00:00
|
|
|
h=h;
|
|
|
|
m=m;
|
2010-02-13 03:23:14 +00:00
|
|
|
return SANE_STATUS_UNSUPPORTED;
|
|
|
|
}
|
|
|
|
|
|
|
|
SANE_Status
|
|
|
|
sane_get_select_fd (SANE_Handle h, SANE_Int * fd)
|
|
|
|
{
|
2010-03-26 04:01:16 +00:00
|
|
|
h=h;
|
|
|
|
fd=fd;
|
2010-02-13 03:23:14 +00:00
|
|
|
return SANE_STATUS_UNSUPPORTED;
|
|
|
|
}
|