sane-project-backends/backend/kvs20xx.c

538 wiersze
11 KiB
C
Czysty Zwykły widok Historia

/*
Copyright (C) 2008, Panasonic Russia Ltd.
Copyright (C) 2010, m. allan noah
*/
/*
Panasonic KV-S20xx USB-SCSI scanners.
*/
#define DEBUG_NOT_STATIC
#define BUILD 2
#include "../include/sane/config.h"
#include <string.h>
#include <unistd.h>
#include "../include/sane/sanei_backend.h"
#include "../include/sane/sanei_scsi.h"
#include "../include/sane/sanei_usb.h"
#include "../include/sane/saneopts.h"
#include "../include/sane/sanei_config.h"
#include "../include/lassert.h"
#include "kvs20xx.h"
#include "kvs20xx_cmd.h"
/* private functions */
SANE_Status attach (SANE_String_Const devname);
struct known_device
{
const SANE_Int id;
const SANE_Device scanner;
};
static const struct known_device known_devices[] = {
{
KV_S2025C,
{ "", "MATSHITA", "KV-S2025C", "sheetfed scanner" },
},
{
KV_S2045C,
{ "", "MATSHITA", "KV-S2045C", "sheetfed scanner" },
},
{
KV_S2026C,
{ "", "MATSHITA", "KV-S2026C", "sheetfed scanner" },
},
{
KV_S2046C,
{ "", "MATSHITA", "KV-S2046C", "sheetfed scanner" },
},
{
KV_S2028C,
{ "", "MATSHITA", "KV-S2028C", "sheetfed scanner" },
},
{
KV_S2048C,
{ "", "MATSHITA", "KV-S2048C", "sheetfed scanner" },
},
};
SANE_Status
sane_init (SANE_Int __sane_unused__ * version_code,
SANE_Auth_Callback __sane_unused__ authorize)
{
DBG_INIT ();
DBG (DBG_INFO, "This is panasonic kvs20xx driver\n");
*version_code = SANE_VERSION_CODE (V_MAJOR, V_MINOR, BUILD);
/* Initialize USB */
sanei_usb_init ();
return SANE_STATUS_GOOD;
}
/*
* List of available devices, allocated by sane_get_devices, released
* by sane_exit()
*/
static SANE_Device **devlist = NULL;
static unsigned curr_scan_dev = 0;
void
sane_exit (void)
{
if (devlist)
{
int i;
for (i = 0; devlist[i]; i++)
{
free ((void *) devlist[i]->name);
free ((void *) devlist[i]);
}
free ((void *) devlist);
devlist = NULL;
}
}
SANE_Status
attach (SANE_String_Const devname)
{
int i = 0;
if (devlist)
{
for (; devlist[i]; i++);
devlist = realloc (devlist, sizeof (SANE_Device *) * (i + 1));
if (!devlist)
return SANE_STATUS_NO_MEM;
}
else
{
devlist = malloc (sizeof (SANE_Device *) * 2);
if (!devlist)
return SANE_STATUS_NO_MEM;
}
devlist[i] = malloc (sizeof (SANE_Device));
if (!devlist[i])
return SANE_STATUS_NO_MEM;
memcpy (devlist[i], &known_devices[curr_scan_dev].scanner,
sizeof (SANE_Device));
devlist[i]->name = strdup (devname);
/* terminate device list with NULL entry: */
devlist[i + 1] = 0;
DBG (DBG_INFO, "%s device attached\n", devname);
return SANE_STATUS_GOOD;
}
/* Get device list */
SANE_Status
sane_get_devices (const SANE_Device *** device_list,
SANE_Bool __sane_unused__ local_only)
{
if (devlist)
{
int i;
for (i = 0; devlist[i]; i++)
{
free ((void *) devlist[i]->name);
free ((void *) devlist[i]);
}
free ((void *) devlist);
devlist = NULL;
}
for (curr_scan_dev = 0;
curr_scan_dev < sizeof (known_devices) / sizeof (known_devices[0]);
curr_scan_dev++)
{
sanei_usb_find_devices (PANASONIC_ID,
known_devices[curr_scan_dev].id, attach);
}
for (curr_scan_dev = 0;
curr_scan_dev < sizeof (known_devices) / sizeof (known_devices[0]);
curr_scan_dev++)
{
sanei_scsi_find_devices (known_devices[curr_scan_dev].scanner.vendor,
known_devices[curr_scan_dev].scanner.model,
NULL, -1, -1, -1, -1, attach);
}
*device_list = (const SANE_Device **) devlist;
return SANE_STATUS_GOOD;
}
/* Open device, return the device handle */
SANE_Status
sane_open (SANE_String_Const devname, SANE_Handle * handle)
{
unsigned i, j, id = 0;
struct scanner *s;
SANE_Int h, bus;
SANE_Status st;
for (i = 0; devlist[i]; i++)
{
if (!strcmp (devlist[i]->name, devname))
break;
}
if (!devlist[i])
return SANE_STATUS_INVAL;
for (j = 0; j < sizeof (known_devices) / sizeof (known_devices[0]); j++)
{
if (!strcmp (devlist[i]->model, known_devices[j].scanner.model))
{
id = known_devices[j].id;
break;
}
}
st = sanei_usb_open (devname, &h);
if (st == SANE_STATUS_ACCESS_DENIED)
return st;
if (st)
{
st = sanei_scsi_open (devname, &h, sense_handler, NULL);
if (st)
{
return st;
}
bus = SCSI;
}
else
{
bus = USB;
st = sanei_usb_claim_interface (h, 0);
if (st)
{
sanei_usb_close (h);
return st;
}
}
s = malloc (sizeof (struct scanner));
if (!s)
return SANE_STATUS_NO_MEM;
memset (s, 0, sizeof (struct scanner));
s->buffer = malloc (MAX_READ_DATA_SIZE + BULK_HEADER_SIZE);
if (!s->buffer)
return SANE_STATUS_NO_MEM;
s->file = h;
s->bus = bus;
s->id = id;
init_options (s);
*handle = s;
for (i = 0; i < 3; i++)
{
st = test_unit_ready (s);
if (st)
{
if (s->bus == SCSI)
{
sanei_scsi_close (s->file);
st = sanei_scsi_open (devname, &h, sense_handler, NULL);
if (st)
return st;
}
else
{
sanei_usb_release_interface (s->file, 0);
sanei_usb_close (s->file);
st = sanei_usb_open (devname, &h);
if (st)
return st;
st = sanei_usb_claim_interface (h, 0);
if (st)
{
sanei_usb_close (h);
return st;
}
}
s->file = h;
}
else
break;
}
if (i == 3)
return SANE_STATUS_DEVICE_BUSY;
st = set_timeout (s, s->val[FEED_TIMEOUT].w);
if (st)
{
sane_close (s);
return st;
}
return SANE_STATUS_GOOD;
}
/* Close device */
void
sane_close (SANE_Handle handle)
{
struct scanner *s = (struct scanner *) handle;
int i;
if (s->bus == USB)
{
sanei_usb_release_interface (s->file, 0);
sanei_usb_close (s->file);
}
else
sanei_scsi_close (s->file);
for (i = 1; i < NUM_OPTIONS; i++)
{
if (s->opt[i].type == SANE_TYPE_STRING && s->val[i].s)
free (s->val[i].s);
}
if (s->data)
free (s->data);
free (s->buffer);
free (s);
}
/* Get option descriptor */
const SANE_Option_Descriptor *
sane_get_option_descriptor (SANE_Handle handle, SANE_Int option)
{
struct scanner *s = handle;
if ((unsigned) option >= NUM_OPTIONS || option < 0)
return NULL;
return s->opt + option;
}
static SANE_Status
wait_document (struct scanner *s)
{
SANE_Status st;
int i;
if (!strcmp ("off", s->val[MANUALFEED].s))
return document_exist (s);
for (i = 0; i < s->val[FEED_TIMEOUT].w; i++)
{
st = document_exist (s);
if (st != SANE_STATUS_NO_DOCS)
return st;
sleep (1);
}
return SANE_STATUS_NO_DOCS;
}
/* Start scanning */
SANE_Status
sane_start (SANE_Handle handle)
{
struct scanner *s = (struct scanner *) handle;
SANE_Status st;
int duplex = s->val[DUPLEX].w;
if (!s->scanning)
{
unsigned dummy_length;
st = test_unit_ready (s);
if (st)
return st;
st = wait_document (s);
if (st)
return st;
st = reset_window (s);
if (st)
return st;
st = set_window (s, SIDE_FRONT);
if (st)
return st;
if (duplex)
{
st = set_window (s, SIDE_BACK);
if (st)
return st;
}
st = scan (s);
if (st)
return st;
st = read_picture_element (s, SIDE_FRONT, &s->params);
if (st)
return st;
if (duplex)
{
st = get_adjust_data (s, &dummy_length);
if (st)
return st;
}
else
{
dummy_length = 0;
}
s->scanning = 1;
s->page = 0;
s->read = 0;
s->side = SIDE_FRONT;
sane_get_parameters (s, NULL);
s->saved_dummy_size = s->dummy_size = dummy_length
? (dummy_length * s->val[RESOLUTION].w / 1200 - 1)
* s->params.bytes_per_line : 0;
s->side_size = s->params.lines * s->params.bytes_per_line;
s->data = realloc (s->data, duplex ? s->side_size * 2 : s->side_size);
if (!s->data)
{
s->scanning = 0;
return SANE_STATUS_NO_MEM;
}
}
if (duplex)
{
unsigned side = SIDE_FRONT;
unsigned read, mx;
if (s->side == SIDE_FRONT && s->read == s->side_size - s->dummy_size)
{
s->side = SIDE_BACK;
s->read = s->dummy_size;
s->dummy_size = 0;
return SANE_STATUS_GOOD;
}
s->read = 0;
s->dummy_size = s->saved_dummy_size;
s->side = SIDE_FRONT;
st = document_exist (s);
if (st)
return st;
for (mx = s->side_size * 2; !st; mx -= read, side ^= SIDE_BACK)
st = read_image_data (s, s->page, side,
&s->data[s->side_size * 2 - mx], mx, &read);
}
else
{
unsigned read, mx;
s->read = 0;
st = document_exist (s);
if (st)
return st;
DBG (DBG_INFO, "start: %d\n", s->page);
for (mx = s->side_size; !st; mx -= read)
st = read_image_data (s, s->page, SIDE_FRONT,
&s->data[s->side_size - mx], mx, &read);
}
if (st && st != SANE_STATUS_EOF)
{
s->scanning = 0;
return st;
}
s->page++;
return SANE_STATUS_GOOD;
}
inline static void
memcpy24 (u8 * dest, u8 * src, unsigned size, unsigned ls)
{
unsigned i;
for (i = 0; i < size; i++)
{
dest[i * 3] = src[i];
dest[i * 3 + 1] = src[i + ls];
dest[i * 3 + 2] = src[i + 2 * ls];
}
}
SANE_Status
sane_read (SANE_Handle handle, SANE_Byte * buf,
SANE_Int max_len, SANE_Int * len)
{
struct scanner *s = (struct scanner *) handle;
int duplex = s->val[DUPLEX].w;
int color = !strcmp (s->val[MODE].s, SANE_VALUE_SCAN_MODE_COLOR);
int rest = s->side_size - s->read - s->dummy_size;
*len = 0;
if (!s->scanning || !rest)
{
if (strcmp (s->val[FEEDER_MODE].s, SANE_I18N ("continuous")))
{
if (!duplex || s->side == SIDE_BACK)
s->scanning = 0;
}
return SANE_STATUS_EOF;
}
*len = max_len < rest ? max_len : rest;
if (duplex && (s->id == KV_S2025C
|| s->id == KV_S2026C || s->id == KV_S2028C))
{
if (color)
{
unsigned ls = s->params.bytes_per_line;
unsigned i, a = s->side == SIDE_FRONT ? 0 : ls / 3;
u8 *data;
*len = (*len / ls) * ls;
for (i = 0, data = s->data + s->read * 2 + a;
i < *len / ls; buf += ls, data += 2 * ls, i++)
memcpy24 (buf, data, ls / 3, ls * 2 / 3);
}
else
{
unsigned ls = s->params.bytes_per_line;
unsigned i = s->side == SIDE_FRONT ? 0 : ls;
unsigned head = ls - (s->read % ls);
unsigned tail = (*len - head) % ls;
unsigned lines = (*len - head) / ls;
u8 *data = s->data + (s->read / ls) * ls * 2 + i + s->read % ls;
assert (data <= s->data + s->side_size * 2);
memcpy (buf, data, head);
for (i = 0, buf += head, data += head + (head ? ls : 0);
i < lines; buf += ls, data += ls * 2, i++)
{
assert (data <= s->data + s->side_size * 2);
memcpy (buf, data, ls);
}
assert ((data <= s->data + s->side_size * 2) || !tail);
memcpy (buf, data, tail);
}
s->read += *len;
}
else
{
if (color)
{
unsigned i, ls = s->params.bytes_per_line;
u8 *data = s->data + s->read;
*len = (*len / ls) * ls;
for (i = 0; i < *len / ls; buf += ls, data += ls, i++)
memcpy24 (buf, data, ls / 3, ls / 3);
}
else
{
memcpy (buf, s->data + s->read, *len);
}
s->read += *len;
}
return SANE_STATUS_GOOD;
}
void
sane_cancel (SANE_Handle handle)
{
struct scanner *s = (struct scanner *) handle;
s->scanning = 0;
}
SANE_Status
sane_set_io_mode (SANE_Handle __sane_unused__ h, SANE_Bool __sane_unused__ m)
{
return SANE_STATUS_UNSUPPORTED;
}
SANE_Status
sane_get_select_fd (SANE_Handle __sane_unused__ h,
SANE_Int __sane_unused__ * fd)
{
return SANE_STATUS_UNSUPPORTED;
}