diff options
Diffstat (limited to 'backend/kvs20xx.c')
-rw-r--r-- | backend/kvs20xx.c | 535 |
1 files changed, 535 insertions, 0 deletions
diff --git a/backend/kvs20xx.c b/backend/kvs20xx.c new file mode 100644 index 0000000..955252a --- /dev/null +++ b/backend/kvs20xx.c @@ -0,0 +1,535 @@ +/* + 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" + +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; + } +} + +static 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, kvs20xx_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; + kvs20xx_init_options (s); + *handle = s; + for (i = 0; i < 3; i++) + { + st = kvs20xx_test_unit_ready (s); + if (st) + { + if (s->bus == SCSI) + { + sanei_scsi_close (s->file); + st = sanei_scsi_open (devname, &h, kvs20xx_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 = kvs20xx_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 kvs20xx_document_exist (s); + + for (i = 0; i < s->val[FEED_TIMEOUT].w; i++) + { + st = kvs20xx_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 = kvs20xx_test_unit_ready (s); + if (st) + return st; + + st = wait_document (s); + if (st) + return st; + + st = kvs20xx_reset_window (s); + if (st) + return st; + st = kvs20xx_set_window (s, SIDE_FRONT); + if (st) + return st; + if (duplex) + { + st = kvs20xx_set_window (s, SIDE_BACK); + if (st) + return st; + } + st = kvs20xx_scan (s); + if (st) + return st; + + st = kvs20xx_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 = kvs20xx_document_exist (s); + if (st) + return st; + for (mx = s->side_size * 2; !st; mx -= read, side ^= SIDE_BACK) + st = kvs20xx_read_image_data (s, s->page, side, + &s->data[s->side_size * 2 - mx], mx, + &read); + } + else + { + unsigned read, mx; + s->read = 0; + st = kvs20xx_document_exist (s); + if (st) + return st; + DBG (DBG_INFO, "start: %d\n", s->page); + + for (mx = s->side_size; !st; mx -= read) + st = kvs20xx_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; +} |