diff options
Diffstat (limited to 'backend/coolscan3.c')
-rw-r--r-- | backend/coolscan3.c | 3188 |
1 files changed, 3188 insertions, 0 deletions
diff --git a/backend/coolscan3.c b/backend/coolscan3.c new file mode 100644 index 0000000..a1d6fe6 --- /dev/null +++ b/backend/coolscan3.c @@ -0,0 +1,3188 @@ +/* + * SANE - Scanner Access Now Easy. + * coolscan3.c + * + * This file implements a SANE backend for Nikon Coolscan film scanners. + * + * coolscan3.c is based on coolscan2.c, a work of András Major, Ariel Garcia + * and Giuseppe Sacco. + * + * Copyright (C) 2007-08 Tower Technologies + * Author: Alessandro Zummo <a.zummo@towertech.it> + * + * 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, version 2. + * + */ + +/* ========================================================================= */ + +#include "../include/sane/config.h" + +#include <math.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <unistd.h> +#include <time.h> + +#include "../include/_stdint.h" + +#include "../include/sane/sane.h" +#include "../include/sane/sanei.h" +#include "../include/sane/saneopts.h" +#include "../include/sane/sanei_scsi.h" +#include "../include/sane/sanei_usb.h" +#include "../include/sane/sanei_debug.h" +#include "../include/sane/sanei_config.h" + +#define BACKEND_NAME coolscan3 +#include "../include/sane/sanei_backend.h" /* must be last */ + +#define CS3_VERSION_MAJOR 1 +#define CS3_VERSION_MINOR 0 +#define CS3_REVISION 0 +#define CS3_CONFIG_FILE "coolscan3.conf" + +#define WSIZE (sizeof (SANE_Word)) + + +/* ========================================================================= */ +/* typedefs */ + +typedef enum +{ + CS3_TYPE_UNKOWN, + CS3_TYPE_LS30, + CS3_TYPE_LS40, + CS3_TYPE_LS50, + CS3_TYPE_LS2000, + CS3_TYPE_LS4000, + CS3_TYPE_LS5000, + CS3_TYPE_LS8000 +} +cs3_type_t; + +typedef enum +{ + CS3_INTERFACE_UNKNOWN, + CS3_INTERFACE_SCSI, /* includes IEEE1394 via SBP2 */ + CS3_INTERFACE_USB +} +cs3_interface_t; + +typedef enum +{ + CS3_PHASE_NONE = 0x00, + CS3_PHASE_STATUS = 0x01, + CS3_PHASE_OUT = 0x02, + CS3_PHASE_IN = 0x03, + CS3_PHASE_BUSY = 0x04 +} +cs3_phase_t; + +typedef enum +{ + CS3_SCAN_NORMAL, + CS3_SCAN_AE, + CS3_SCAN_AE_WB +} +cs3_scan_t; + +typedef enum +{ + CS3_STATUS_READY = 0, + CS3_STATUS_BUSY = 1, + CS3_STATUS_NO_DOCS = 2, + CS3_STATUS_PROCESSING = 4, + CS3_STATUS_ERROR = 8, + CS3_STATUS_REISSUE = 16, + CS3_STATUS_ALL = 31 /* sum of all others */ +} +cs3_status_t; + +typedef enum +{ + CS3_OPTION_NUM = 0, + + CS3_OPTION_PREVIEW, + + CS3_OPTION_NEGATIVE, + + CS3_OPTION_INFRARED, + + CS3_OPTION_SAMPLES_PER_SCAN, + + CS3_OPTION_DEPTH, + + CS3_OPTION_EXPOSURE, + CS3_OPTION_EXPOSURE_R, + CS3_OPTION_EXPOSURE_G, + CS3_OPTION_EXPOSURE_B, + CS3_OPTION_SCAN_AE, + CS3_OPTION_SCAN_AE_WB, + + CS3_OPTION_LUT_R, + CS3_OPTION_LUT_G, + CS3_OPTION_LUT_B, + + CS3_OPTION_RES, + CS3_OPTION_RESX, + CS3_OPTION_RESY, + CS3_OPTION_RES_INDEPENDENT, + + CS3_OPTION_PREVIEW_RESOLUTION, + + CS3_OPTION_FRAME, + CS3_OPTION_FRAME_COUNT, + CS3_OPTION_SUBFRAME, + CS3_OPTION_XMIN, + CS3_OPTION_XMAX, + CS3_OPTION_YMIN, + CS3_OPTION_YMAX, + + CS3_OPTION_LOAD, + CS3_OPTION_AUTOLOAD, + CS3_OPTION_EJECT, + CS3_OPTION_RESET, + + CS3_OPTION_FOCUS_ON_CENTRE, + CS3_OPTION_FOCUS, + CS3_OPTION_AUTOFOCUS, + CS3_OPTION_FOCUSX, + CS3_OPTION_FOCUSY, + + CS3_N_OPTIONS /* must be last -- counts number of enum items */ +} +cs3_option_t; + +typedef unsigned int cs3_pixel_t; + +#define CS3_COLOR_MAX 10 /* 9 + 1, see cs3_colors */ + +/* Given that there is no way to give scanner vendor + * and model to the calling software, I have to use + * an ugly hack here. :( That's very sad. Suggestions + * that can provide the same features are appreciated. + */ + +#ifndef SANE_COOKIE +#define SANE_COOKIE 0x0BADCAFE + +struct SANE_Cookie +{ + uint16_t version; + const char *vendor; + const char *model; + const char *revision; +}; +#endif + +typedef struct +{ + /* magic bits :( */ + uint32_t magic; + struct SANE_Cookie *cookie_ptr; + struct SANE_Cookie cookie; + + /* interface */ + cs3_interface_t interface; + int fd; + SANE_Byte *send_buf, *recv_buf; + size_t send_buf_size, recv_buf_size; + size_t n_cmd, n_send, n_recv; + + /* device characteristics */ + char vendor_string[9], product_string[17], revision_string[5]; + cs3_type_t type; + int maxbits; + unsigned int resx_optical, resx_min, resx_max, *resx_list, + resx_n_list; + unsigned int resy_optical, resy_min, resy_max, *resy_list, + resy_n_list; + unsigned long boundaryx, boundaryy; + unsigned long frame_offset; + unsigned int unit_dpi; + double unit_mm; + int n_frames; + + int focus_min, focus_max; + + /* settings */ + SANE_Bool preview, negative, infrared, autoload, autofocus, ae, aewb; + int samples_per_scan, depth, real_depth, bytes_per_pixel, shift_bits, + n_colors; + cs3_pixel_t n_lut; + cs3_pixel_t *lut_r, *lut_g, *lut_b, *lut_neutral; + unsigned long resx, resy, res, res_independent, res_preview; + unsigned long xmin, xmax, ymin, ymax; + int i_frame, frame_count; + double subframe; + + unsigned int real_resx, real_resy, real_pitchx, real_pitchy; + unsigned long real_xoffset, real_yoffset, real_width, real_height, + logical_width, logical_height; + int odd_padding; + int block_padding; + + double exposure, exposure_r, exposure_g, exposure_b; + unsigned long real_exposure[CS3_COLOR_MAX]; + + + SANE_Bool focus_on_centre; + unsigned long focusx, focusy, real_focusx, real_focusy; + int focus; + + /* status */ + SANE_Bool scanning; + SANE_Byte *line_buf; + ssize_t n_line_buf, i_line_buf; + unsigned long sense_key, sense_asc, sense_ascq, sense_info; + unsigned long sense_code; + cs3_status_t status; + size_t xfer_position, xfer_bytes_total; + + /* SANE stuff */ + SANE_Option_Descriptor option_list[CS3_N_OPTIONS]; +} +cs3_t; + + +/* ========================================================================= */ +/* prototypes */ + +static SANE_Status cs3_open(const char *device, cs3_interface_t interface, + cs3_t ** sp); +static void cs3_close(cs3_t * s); +static SANE_Status cs3_attach(const char *dev); +static SANE_Status cs3_scsi_sense_handler(int fd, u_char * sense_buffer, + void *arg); +static SANE_Status cs3_parse_sense_data(cs3_t * s); +static void cs3_init_buffer(cs3_t * s); +static SANE_Status cs3_pack_byte(cs3_t * s, SANE_Byte byte); +static void cs3_pack_long(cs3_t * s, unsigned long val); +static void cs3_pack_word(cs3_t * s, unsigned long val); +static SANE_Status cs3_parse_cmd(cs3_t * s, char *text); +static SANE_Status cs3_grow_send_buffer(cs3_t * s); +static SANE_Status cs3_issue_cmd(cs3_t * s); +static cs3_phase_t cs3_phase_check(cs3_t * s); +static SANE_Status cs3_set_boundary(cs3_t * s); +static SANE_Status cs3_scanner_ready(cs3_t * s, int flags); +static SANE_Status cs3_page_inquiry(cs3_t * s, int page); +static SANE_Status cs3_full_inquiry(cs3_t * s); +static SANE_Status cs3_mode_select(cs3_t * s); +static SANE_Status cs3_reserve_unit(cs3_t * s); +static SANE_Status cs3_release_unit(cs3_t * s); +static SANE_Status cs3_execute(cs3_t * s); +static SANE_Status cs3_load(cs3_t * s); +static SANE_Status cs3_eject(cs3_t * s); +static SANE_Status cs3_reset(cs3_t * s); +static SANE_Status cs3_set_focus(cs3_t * s); +static SANE_Status cs3_autofocus(cs3_t * s); +static SANE_Status cs3_autoexposure(cs3_t * s, int wb); +static SANE_Status cs3_get_exposure(cs3_t * s); +static SANE_Status cs3_set_window(cs3_t * s, cs3_scan_t type); +static SANE_Status cs3_convert_options(cs3_t * s); +static SANE_Status cs3_scan(cs3_t * s, cs3_scan_t type); +static void *cs3_xmalloc(size_t size); +static void *cs3_xrealloc(void *p, size_t size); +static void cs3_xfree(const void *p); + + +/* ========================================================================= */ +/* global variables */ + +static int cs3_colors[] = { 1, 2, 3, 9 }; + +static SANE_Device **device_list = NULL; +static int n_device_list = 0; +static cs3_interface_t try_interface = CS3_INTERFACE_UNKNOWN; +static int open_devices = 0; + + +/* ========================================================================= */ +/* SANE entry points */ + +SANE_Status +sane_init(SANE_Int * version_code, SANE_Auth_Callback authorize) +{ + DBG_INIT(); + DBG(1, "coolscan3 backend, version %i.%i.%i initializing.\n", + CS3_VERSION_MAJOR, CS3_VERSION_MINOR, CS3_REVISION); + + authorize = authorize; /* to shut up compiler */ + + if (version_code) + *version_code = SANE_VERSION_CODE(SANE_CURRENT_MAJOR, V_MINOR, 0); + + sanei_usb_init(); + + return SANE_STATUS_GOOD; +} + +void +sane_exit(void) +{ + int i; + + DBG(10, "%s\n", __func__); + + for (i = 0; i < n_device_list; i++) { + cs3_xfree(device_list[i]->name); + cs3_xfree(device_list[i]->vendor); + cs3_xfree(device_list[i]->model); + cs3_xfree(device_list[i]); + } + cs3_xfree(device_list); +} + +SANE_Status +sane_get_devices(const SANE_Device *** list, SANE_Bool local_only) +{ + char line[PATH_MAX], *p; + FILE *config; + + local_only = local_only; /* to shut up compiler */ + + DBG(10, "%s\n", __func__); + + if (device_list) + DBG(6, + "sane_get_devices(): Device list already populated, not probing again.\n"); + else { + if (open_devices) { + DBG(4, + "sane_get_devices(): Devices open, not scanning for scanners.\n"); + return SANE_STATUS_IO_ERROR; + } + + config = sanei_config_open(CS3_CONFIG_FILE); + if (config) { + DBG(4, "sane_get_devices(): Reading config file.\n"); + while (sanei_config_read(line, sizeof(line), config)) { + p = line; + p += strspn(line, " \t"); + if (strlen(p) && (p[0] != '\n') + && (p[0] != '#')) + cs3_open(line, CS3_INTERFACE_UNKNOWN, + NULL); + } + fclose(config); + } else { + DBG(4, "sane_get_devices(): No config file found.\n"); + cs3_open("auto", CS3_INTERFACE_UNKNOWN, NULL); + } + + DBG(6, "%s: %i device(s) detected.\n", + __func__, n_device_list); + } + + *list = (const SANE_Device **) device_list; + + return SANE_STATUS_GOOD; +} + +SANE_Status +sane_open(SANE_String_Const name, SANE_Handle * h) +{ + SANE_Status status; + cs3_t *s; + int i_option; + unsigned int i_list; + SANE_Option_Descriptor o; + SANE_Word *word_list; + SANE_Range *range = NULL; + int alloc_failed = 0; + + DBG(10, "%s\n", __func__); + + status = cs3_open(name, CS3_INTERFACE_UNKNOWN, &s); + if (status != SANE_STATUS_GOOD) + return status; + + *h = (SANE_Handle) s; + + /* get device properties */ + + s->lut_r = s->lut_g = s->lut_b = s->lut_neutral = NULL; + s->resx_list = s->resy_list = NULL; + s->resx_n_list = s->resy_n_list = 0; + + status = cs3_full_inquiry(s); + if (status != SANE_STATUS_GOOD) + return status; + + status = cs3_mode_select(s); + if (status != SANE_STATUS_GOOD) + return status; + + /* option descriptors */ + + for (i_option = 0; i_option < CS3_N_OPTIONS; i_option++) { + o.name = o.title = o.desc = NULL; + o.type = o.unit = o.cap = o.constraint_type = o.size = 0; + o.constraint.range = NULL; /* only one union member needs to be NULLed */ + switch (i_option) { + case CS3_OPTION_NUM: + o.name = ""; + o.title = SANE_TITLE_NUM_OPTIONS; + o.desc = SANE_DESC_NUM_OPTIONS; + o.type = SANE_TYPE_INT; + o.size = WSIZE; + o.cap = SANE_CAP_SOFT_DETECT; + break; + case CS3_OPTION_PREVIEW: + o.name = "preview"; + o.title = "Preview mode"; + o.desc = "Preview mode"; + o.type = SANE_TYPE_BOOL; + o.size = WSIZE; + o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | + SANE_CAP_ADVANCED; + break; + case CS3_OPTION_NEGATIVE: + o.name = "negative"; + o.title = "Negative"; + o.desc = "Negative film: make scanner invert colors"; + o.type = SANE_TYPE_BOOL; + o.size = WSIZE; + o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; + /*o.cap |= SANE_CAP_INACTIVE; */ + break; + + case CS3_OPTION_INFRARED: + o.name = "infrared"; + o.title = "Read infrared channel"; + o.desc = "Read infrared channel in addition to scan colors"; + o.type = SANE_TYPE_BOOL; + o.size = WSIZE; + o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; +#ifndef SANE_FRAME_RGBI + o.cap |= SANE_CAP_INACTIVE; +#endif + break; + + case CS3_OPTION_SAMPLES_PER_SCAN: + o.name = "samples-per-scan"; + o.title = "Samples per Scan"; + o.desc = "Number of samples per scan"; + o.type = SANE_TYPE_INT; + o.unit = SANE_UNIT_NONE; + o.size = WSIZE; + o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; + if (s->type != CS3_TYPE_LS2000 && s->type != CS3_TYPE_LS4000 + && s->type != CS3_TYPE_LS5000 && s->type != CS3_TYPE_LS8000) + o.cap |= SANE_CAP_INACTIVE; + o.constraint_type = SANE_CONSTRAINT_RANGE; + range = (SANE_Range *) cs3_xmalloc (sizeof (SANE_Range)); + if (! range) + alloc_failed = 1; + else + { + range->min = 1; + range->max = 16; + range->quant = 1; + o.constraint.range = range; + } + break; + + case CS3_OPTION_DEPTH: + o.name = "depth"; + o.title = "Bit depth per channel"; + o.desc = "Number of bits output by scanner for each channel"; + o.type = SANE_TYPE_INT; + o.unit = SANE_UNIT_NONE; + o.size = WSIZE; + o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; + o.constraint_type = SANE_CONSTRAINT_WORD_LIST; + word_list = + (SANE_Word *) cs3_xmalloc(2 * + sizeof(SANE_Word)); + if (!word_list) + alloc_failed = 1; + else { + word_list[1] = 8; + word_list[2] = s->maxbits; + word_list[0] = 2; + o.constraint.word_list = word_list; + } + break; + case CS3_OPTION_EXPOSURE: + o.name = "exposure"; + o.title = "Exposure multiplier"; + o.desc = "Exposure multiplier for all channels"; + o.type = SANE_TYPE_FIXED; + o.unit = SANE_UNIT_NONE; + o.size = WSIZE; + o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; + o.constraint_type = SANE_CONSTRAINT_RANGE; + range = (SANE_Range *) + cs3_xmalloc(sizeof(SANE_Range)); + if (!range) + alloc_failed = 1; + else { + range->min = SANE_FIX(0.); + range->max = SANE_FIX(10.); + range->quant = SANE_FIX(0.1); + o.constraint.range = range; + } + break; + case CS3_OPTION_EXPOSURE_R: + o.name = "red-exposure"; + o.title = "Red exposure time"; + o.desc = "Exposure time for red channel"; + o.type = SANE_TYPE_FIXED; + o.unit = SANE_UNIT_MICROSECOND; + o.size = WSIZE; + o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; + o.constraint_type = SANE_CONSTRAINT_RANGE; + range = (SANE_Range *) + cs3_xmalloc(sizeof(SANE_Range)); + if (!range) + alloc_failed = 1; + else { + range->min = SANE_FIX(50.); + range->max = SANE_FIX(20000.); + range->quant = SANE_FIX(10.); + o.constraint.range = range; + } + break; + case CS3_OPTION_EXPOSURE_G: + o.name = "green-exposure"; + o.title = "Green exposure time"; + o.desc = "Exposure time for green channel"; + o.type = SANE_TYPE_FIXED; + o.unit = SANE_UNIT_MICROSECOND; + o.size = WSIZE; + o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; + o.constraint_type = SANE_CONSTRAINT_RANGE; + range = (SANE_Range *) + cs3_xmalloc(sizeof(SANE_Range)); + if (!range) + alloc_failed = 1; + else { + range->min = SANE_FIX(50.); + range->max = SANE_FIX(20000.); + range->quant = SANE_FIX(10.); + o.constraint.range = range; + } + break; + case CS3_OPTION_EXPOSURE_B: + o.name = "blue-exposure"; + o.title = "Blue exposure time"; + o.desc = "Exposure time for blue channel"; + o.type = SANE_TYPE_FIXED; + o.unit = SANE_UNIT_MICROSECOND; + o.size = WSIZE; + o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; + o.constraint_type = SANE_CONSTRAINT_RANGE; + range = (SANE_Range *) + cs3_xmalloc(sizeof(SANE_Range)); + if (!range) + alloc_failed = 1; + else { + range->min = SANE_FIX(50.); + range->max = SANE_FIX(20000.); + range->quant = SANE_FIX(10.); + o.constraint.range = range; + } + break; + case CS3_OPTION_LUT_R: + o.name = "red-gamma-table"; + o.title = "LUT for red channel"; + o.desc = "LUT for red channel"; + o.type = SANE_TYPE_INT; + o.size = s->n_lut * WSIZE; + o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; + o.constraint_type = SANE_CONSTRAINT_RANGE; + range = (SANE_Range *) + cs3_xmalloc(sizeof(SANE_Range)); + if (!range) + alloc_failed = 1; + else { + range->min = 0; + range->max = s->n_lut - 1; + range->quant = 1; + o.constraint.range = range; + } + break; + case CS3_OPTION_LUT_G: + o.name = "green-gamma-table"; + o.title = "LUT for green channel"; + o.desc = "LUT for green channel"; + o.type = SANE_TYPE_INT; + o.size = s->n_lut * WSIZE; + o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; + o.constraint_type = SANE_CONSTRAINT_RANGE; + range = (SANE_Range *) + cs3_xmalloc(sizeof(SANE_Range)); + if (!range) + alloc_failed = 1; + else { + range->min = 0; + range->max = s->n_lut - 1; + range->quant = 1; + o.constraint.range = range; + } + break; + case CS3_OPTION_LUT_B: + o.name = "blue-gamma-table"; + o.title = "LUT for blue channel"; + o.desc = "LUT for blue channel"; + o.type = SANE_TYPE_INT; + o.size = s->n_lut * WSIZE; + o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; + o.constraint_type = SANE_CONSTRAINT_RANGE; + range = (SANE_Range *) + cs3_xmalloc(sizeof(SANE_Range)); + if (!range) + alloc_failed = 1; + else { + range->min = 0; + range->max = s->n_lut - 1; + range->quant = 1; + o.constraint.range = range; + } + break; + case CS3_OPTION_LOAD: + o.name = "load"; + o.title = "Load"; + o.desc = "Load next slide"; + o.type = SANE_TYPE_BUTTON; + o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; + if (s->n_frames > 1) + o.cap |= SANE_CAP_INACTIVE; + break; + case CS3_OPTION_AUTOLOAD: + o.name = "autoload"; + o.title = "Autoload"; + o.desc = "Autoload slide before each scan"; + o.type = SANE_TYPE_BOOL; + o.size = WSIZE; + o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; + if (s->n_frames > 1) + o.cap |= SANE_CAP_INACTIVE; + break; + case CS3_OPTION_EJECT: + o.name = "eject"; + o.title = "Eject"; + o.desc = "Eject loaded medium"; + o.type = SANE_TYPE_BUTTON; + o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; + break; + case CS3_OPTION_RESET: + o.name = "reset"; + o.title = "Reset scanner"; + o.desc = "Initialize scanner"; + o.type = SANE_TYPE_BUTTON; + o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; + break; + case CS3_OPTION_RESX: + case CS3_OPTION_RES: + case CS3_OPTION_PREVIEW_RESOLUTION: + if (i_option == CS3_OPTION_PREVIEW_RESOLUTION) { + o.name = "preview-resolution"; + o.title = "Preview resolution"; + o.desc = "Scanning resolution for preview mode in dpi, affecting both x and y directions"; + } else if (i_option == CS3_OPTION_RES) { + o.name = "resolution"; + o.title = "Resolution"; + o.desc = "Scanning resolution in dpi, affecting both x and y directions"; + } else { + o.name = "x-resolution"; + o.title = "X resolution"; + o.desc = "Scanning resolution in dpi, affecting x direction only"; + } + o.type = SANE_TYPE_INT; + o.unit = SANE_UNIT_DPI; + o.size = WSIZE; + o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; + if (i_option == CS3_OPTION_RESX) + o.cap |= SANE_CAP_INACTIVE | + SANE_CAP_ADVANCED; + if (i_option == CS3_OPTION_PREVIEW_RESOLUTION) + o.cap |= SANE_CAP_ADVANCED; + o.constraint_type = SANE_CONSTRAINT_WORD_LIST; + word_list = + (SANE_Word *) cs3_xmalloc((s->resx_n_list + 1) + * + sizeof(SANE_Word)); + if (!word_list) + alloc_failed = 1; + else { + for (i_list = 0; i_list < s->resx_n_list; + i_list++) + word_list[i_list + 1] = + s->resx_list[i_list]; + word_list[0] = s->resx_n_list; + o.constraint.word_list = word_list; + } + break; + case CS3_OPTION_RESY: + o.name = "y-resolution"; + o.title = "Y resolution"; + o.desc = "Scanning resolution in dpi, affecting y direction only"; + o.type = SANE_TYPE_INT; + o.unit = SANE_UNIT_DPI; + o.size = WSIZE; + o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | + SANE_CAP_INACTIVE | SANE_CAP_ADVANCED; + o.constraint_type = SANE_CONSTRAINT_WORD_LIST; + word_list = + (SANE_Word *) cs3_xmalloc((s->resy_n_list + 1) + * + sizeof(SANE_Word)); + if (!word_list) + alloc_failed = 1; + else { + for (i_list = 0; i_list < s->resy_n_list; + i_list++) + word_list[i_list + 1] = + s->resy_list[i_list]; + word_list[0] = s->resy_n_list; + o.constraint.word_list = word_list; + } + break; + case CS3_OPTION_RES_INDEPENDENT: + o.name = "independent-res"; + o.title = "Independent x/y resolutions"; + o.desc = "Enable independent controls for scanning resolution in x and y direction"; + o.type = SANE_TYPE_BOOL; + o.size = WSIZE; + o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | + SANE_CAP_INACTIVE | SANE_CAP_ADVANCED; + break; + case CS3_OPTION_FRAME: + o.name = "frame"; + o.title = "Frame number"; + o.desc = "Number of frame to be scanned, starting with 1"; + o.type = SANE_TYPE_INT; + o.unit = SANE_UNIT_NONE; + o.size = WSIZE; + o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; + if (s->n_frames <= 1) + o.cap |= SANE_CAP_INACTIVE; + o.constraint_type = SANE_CONSTRAINT_RANGE; + range = (SANE_Range *) + cs3_xmalloc(sizeof(SANE_Range)); + if (!range) + alloc_failed = 1; + else { + range->min = 1; + range->max = s->n_frames; + range->quant = 1; + o.constraint.range = range; + } + break; + case CS3_OPTION_FRAME_COUNT: + o.name = "frame-count"; + o.title = "Frame count"; + o.desc = "Amount of frames to scan"; + o.type = SANE_TYPE_INT; + o.unit = SANE_UNIT_NONE; + o.size = WSIZE; + o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; + if (s->n_frames <= 1) + o.cap |= SANE_CAP_INACTIVE; + o.constraint_type = SANE_CONSTRAINT_RANGE; + range = (SANE_Range *) + cs3_xmalloc(sizeof(SANE_Range)); + if (!range) + alloc_failed = 1; + else { + range->min = 1; + range->max = s->n_frames - s->i_frame + 1; + range->quant = 1; + o.constraint.range = range; + } + break; + case CS3_OPTION_SUBFRAME: + o.name = "subframe"; + o.title = "Frame shift"; + o.desc = "Fine position within the selected frame"; + o.type = SANE_TYPE_FIXED; + o.unit = SANE_UNIT_MM; + o.size = WSIZE; + o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; + o.constraint_type = SANE_CONSTRAINT_RANGE; + range = (SANE_Range *) + cs3_xmalloc(sizeof(SANE_Range)); + if (!range) + alloc_failed = 1; + else { + range->min = SANE_FIX(0.); + range->max = + SANE_FIX((s->boundaryy - + 1) * s->unit_mm); + range->quant = SANE_FIX(0.); + o.constraint.range = range; + } + break; + case CS3_OPTION_XMIN: + o.name = "tl-x"; + o.title = "Left x value of scan area"; + o.desc = "Left x value of scan area"; + o.type = SANE_TYPE_INT; + o.unit = SANE_UNIT_PIXEL; + o.size = WSIZE; + o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; + o.constraint_type = SANE_CONSTRAINT_RANGE; + if (!range) + alloc_failed = 1; + else { + range = (SANE_Range *) + cs3_xmalloc(sizeof(SANE_Range)); + range->min = 0; + range->max = s->boundaryx - 1; + range->quant = 1; + o.constraint.range = range; + } + break; + case CS3_OPTION_XMAX: + o.name = "br-x"; + o.title = "Right x value of scan area"; + o.desc = "Right x value of scan area"; + o.type = SANE_TYPE_INT; + o.unit = SANE_UNIT_PIXEL; + o.size = WSIZE; + o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; + o.constraint_type = SANE_CONSTRAINT_RANGE; + range = (SANE_Range *) + cs3_xmalloc(sizeof(SANE_Range)); + if (!range) + alloc_failed = 1; + else { + range->min = 0; + range->max = s->boundaryx - 1; + range->quant = 1; + o.constraint.range = range; + } + break; + case CS3_OPTION_YMIN: + o.name = "tl-y"; + o.title = "Top y value of scan area"; + o.desc = "Top y value of scan area"; + o.type = SANE_TYPE_INT; + o.unit = SANE_UNIT_PIXEL; + o.size = WSIZE; + o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; + o.constraint_type = SANE_CONSTRAINT_RANGE; + range = (SANE_Range *) + cs3_xmalloc(sizeof(SANE_Range)); + if (!range) + alloc_failed = 1; + else { + range->min = 0; + range->max = s->boundaryy - 1; + range->quant = 1; + o.constraint.range = range; + } + break; + case CS3_OPTION_YMAX: + o.name = "br-y"; + o.title = "Bottom y value of scan area"; + o.desc = "Bottom y value of scan area"; + o.type = SANE_TYPE_INT; + o.unit = SANE_UNIT_PIXEL; + o.size = WSIZE; + o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; + o.constraint_type = SANE_CONSTRAINT_RANGE; + range = (SANE_Range *) + cs3_xmalloc(sizeof(SANE_Range)); + if (!range) + alloc_failed = 1; + else { + range->min = 0; + range->max = s->boundaryy - 1; + range->quant = 1; + o.constraint.range = range; + } + break; + case CS3_OPTION_FOCUS_ON_CENTRE: + o.name = "focus-on-centre"; + o.title = "Use centre of scan area as AF point"; + o.desc = "Use centre of scan area as AF point instead of manual AF point selection"; + o.type = SANE_TYPE_BOOL; + o.size = WSIZE; + o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; + break; + case CS3_OPTION_FOCUS: + o.name = "focus"; + o.title = "Focus position"; + o.desc = "Focus position for manual focus"; + o.type = SANE_TYPE_INT; + o.unit = SANE_UNIT_NONE; + o.size = WSIZE; + o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; + o.constraint_type = SANE_CONSTRAINT_RANGE; + range = (SANE_Range *) + cs3_xmalloc(sizeof(SANE_Range)); + if (!range) + alloc_failed = 1; + else { + range->min = s->focus_min; + range->max = s->focus_max; + range->quant = 1; + o.constraint.range = range; + } + break; + case CS3_OPTION_AUTOFOCUS: + o.name = "autofocus"; + o.title = "Autofocus"; + o.desc = "Perform autofocus before scan"; + o.type = SANE_TYPE_BOOL; + o.size = WSIZE; + o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; + break; + case CS3_OPTION_FOCUSX: + o.name = "focusx"; + o.title = "X coordinate of AF point"; + o.desc = "X coordinate of AF point"; + o.type = SANE_TYPE_INT; + o.unit = SANE_UNIT_PIXEL; + o.size = WSIZE; + o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | + SANE_CAP_INACTIVE; + o.constraint_type = SANE_CONSTRAINT_RANGE; + range = (SANE_Range *) + cs3_xmalloc(sizeof(SANE_Range)); + if (!range) + alloc_failed = 1; + else { + range->min = 0; + range->max = s->boundaryx - 1; + range->quant = 1; + o.constraint.range = range; + } + break; + case CS3_OPTION_FOCUSY: + o.name = "focusy"; + o.title = "Y coordinate of AF point"; + o.desc = "Y coordinate of AF point"; + o.type = SANE_TYPE_INT; + o.unit = SANE_UNIT_PIXEL; + o.size = WSIZE; + o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | + SANE_CAP_INACTIVE; + o.constraint_type = SANE_CONSTRAINT_RANGE; + range = (SANE_Range *) + cs3_xmalloc(sizeof(SANE_Range)); + if (!range) + alloc_failed = 1; + else { + range->min = 0; + range->max = s->boundaryy - 1; + range->quant = 1; + o.constraint.range = range; + } + break; + case CS3_OPTION_SCAN_AE: + o.name = "ae"; + o.title = "Auto-exposure"; + o.desc = "Perform auto-exposure before scan"; + o.type = SANE_TYPE_BOOL; + o.size = WSIZE; + o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; + break; + case CS3_OPTION_SCAN_AE_WB: + o.name = "ae-wb"; + o.title = "Auto-exposure with white balance"; + o.desc = "Perform auto-exposure with white balance before scan"; + o.type = SANE_TYPE_BOOL; + o.size = WSIZE; + o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; + break; + default: + DBG(1, "BUG: sane_open(): Unknown option number: %d\n", i_option); + break; + } + s->option_list[i_option] = o; + } + + s->scanning = SANE_FALSE; + s->preview = SANE_FALSE; + s->negative = SANE_FALSE; + s->autoload = SANE_FALSE; + s->infrared = SANE_FALSE; + s->ae = SANE_FALSE; + s->aewb = SANE_FALSE; + s->samples_per_scan = 1; + s->depth = 8; + s->i_frame = 1; + s->frame_count = 1; + s->subframe = 0.; + s->res = s->resx = s->resx_max; + s->resy = s->resy_max; + s->res_independent = SANE_FALSE; + s->res_preview = s->resx_max / 10; + if (s->res_preview < s->resx_min) + s->res_preview = s->resx_min; + s->xmin = 0; + s->xmax = s->boundaryx - 1; + s->ymin = 0; + s->ymax = s->boundaryy - 1; + s->focus_on_centre = SANE_TRUE; + s->focus = 0; + s->focusx = 0; + s->focusy = 0; + s->exposure = 1.; + s->exposure_r = 1200.; + s->exposure_g = 1200.; + s->exposure_b = 1000.; + s->line_buf = NULL; + s->n_line_buf = 0; + + if (alloc_failed) { + cs3_close(s); + return SANE_STATUS_NO_MEM; + } + + return cs3_reserve_unit(s); +} + +void +sane_close(SANE_Handle h) +{ + cs3_t *s = (cs3_t *) h; + + DBG(10, "%s\n", __func__); + + cs3_release_unit(s); + cs3_close(s); +} + +const SANE_Option_Descriptor * +sane_get_option_descriptor(SANE_Handle h, SANE_Int n) +{ + cs3_t *s = (cs3_t *) h; + + DBG(24, "%s, option %i\n", __func__, n); + + if ((n >= 0) && (n < CS3_N_OPTIONS)) + return &s->option_list[n]; + else + return NULL; +} + +SANE_Status +sane_control_option(SANE_Handle h, SANE_Int n, SANE_Action a, void *v, + SANE_Int * i) +{ + cs3_t *s = (cs3_t *) h; + SANE_Int flags = 0; + cs3_pixel_t pixel; + SANE_Option_Descriptor o = s->option_list[n]; + + DBG(24, "%s, option %i, action %i.\n", __func__, n, a); + + switch (a) { + case SANE_ACTION_GET_VALUE: + + switch (n) { + case CS3_OPTION_NUM: + *(SANE_Word *) v = CS3_N_OPTIONS; + break; + case CS3_OPTION_NEGATIVE: + *(SANE_Word *) v = s->negative; + break; + case CS3_OPTION_INFRARED: + *(SANE_Word *) v = s->infrared; + break; + case CS3_OPTION_SAMPLES_PER_SCAN: + *(SANE_Word *) v = s->samples_per_scan; + break; + case CS3_OPTION_DEPTH: + *(SANE_Word *) v = s->depth; + break; + case CS3_OPTION_PREVIEW: + *(SANE_Word *) v = s->preview; + break; + case CS3_OPTION_AUTOLOAD: + *(SANE_Word *) v = s->autoload; + break; + case CS3_OPTION_EXPOSURE: + *(SANE_Word *) v = SANE_FIX(s->exposure); + break; + case CS3_OPTION_EXPOSURE_R: + *(SANE_Word *) v = SANE_FIX(s->exposure_r); + break; + case CS3_OPTION_EXPOSURE_G: + *(SANE_Word *) v = SANE_FIX(s->exposure_g); + break; + case CS3_OPTION_EXPOSURE_B: + *(SANE_Word *) v = SANE_FIX(s->exposure_b); + break; + case CS3_OPTION_LUT_R: + if (!(s->lut_r)) + return SANE_STATUS_INVAL; + for (pixel = 0; pixel < s->n_lut; pixel++) + ((SANE_Word *) v)[pixel] = s->lut_r[pixel]; + break; + case CS3_OPTION_LUT_G: + if (!(s->lut_g)) + return SANE_STATUS_INVAL; + for (pixel = 0; pixel < s->n_lut; pixel++) + ((SANE_Word *) v)[pixel] = s->lut_g[pixel]; + break; + case CS3_OPTION_LUT_B: + if (!(s->lut_b)) + return SANE_STATUS_INVAL; + for (pixel = 0; pixel < s->n_lut; pixel++) + ((SANE_Word *) v)[pixel] = s->lut_b[pixel]; + break; + case CS3_OPTION_EJECT: + break; + case CS3_OPTION_LOAD: + break; + case CS3_OPTION_RESET: + break; + case CS3_OPTION_FRAME: + *(SANE_Word *) v = s->i_frame; + break; + case CS3_OPTION_FRAME_COUNT: + *(SANE_Word *) v = s->frame_count; + break; + case CS3_OPTION_SUBFRAME: + *(SANE_Word *) v = SANE_FIX(s->subframe); + break; + case CS3_OPTION_RES: + *(SANE_Word *) v = s->res; + break; + case CS3_OPTION_RESX: + *(SANE_Word *) v = s->resx; + break; + case CS3_OPTION_RESY: + *(SANE_Word *) v = s->resy; + break; + case CS3_OPTION_RES_INDEPENDENT: + *(SANE_Word *) v = s->res_independent; + break; + case CS3_OPTION_PREVIEW_RESOLUTION: + *(SANE_Word *) v = s->res_preview; + break; + case CS3_OPTION_XMIN: + *(SANE_Word *) v = s->xmin; + break; + case CS3_OPTION_XMAX: + *(SANE_Word *) v = s->xmax; + break; + case CS3_OPTION_YMIN: + *(SANE_Word *) v = s->ymin; + break; + case CS3_OPTION_YMAX: + *(SANE_Word *) v = s->ymax; + break; + case CS3_OPTION_FOCUS_ON_CENTRE: + *(SANE_Word *) v = s->focus_on_centre; + break; + case CS3_OPTION_FOCUS: + *(SANE_Word *) v = s->focus; + break; + case CS3_OPTION_AUTOFOCUS: + *(SANE_Word *) v = s->autofocus; + break; + case CS3_OPTION_FOCUSX: + *(SANE_Word *) v = s->focusx; + break; + case CS3_OPTION_FOCUSY: + *(SANE_Word *) v = s->focusy; + break; + case CS3_OPTION_SCAN_AE: + *(SANE_Word *) v = s->ae; + break; + case CS3_OPTION_SCAN_AE_WB: + *(SANE_Word *) v = s->aewb; + break; + default: + DBG(4, "%s: Unknown option (bug?).\n", __func__); + return SANE_STATUS_INVAL; + } + break; + + case SANE_ACTION_SET_VALUE: + if (s->scanning) + return SANE_STATUS_INVAL; + /* XXX do this for all elements of arrays */ + switch (o.type) { + case SANE_TYPE_BOOL: + if ((*(SANE_Word *) v != SANE_TRUE) + && (*(SANE_Word *) v != SANE_FALSE)) + return SANE_STATUS_INVAL; + break; + case SANE_TYPE_INT: + case SANE_TYPE_FIXED: + switch (o.constraint_type) { + case SANE_CONSTRAINT_RANGE: + if (*(SANE_Word *) v < + o.constraint.range->min) { + *(SANE_Word *) v = + o.constraint.range->min; + flags |= SANE_INFO_INEXACT; + } else if (*(SANE_Word *) v > + o.constraint.range->max) { + *(SANE_Word *) v = + o.constraint.range->max; + flags |= SANE_INFO_INEXACT; + } + break; + case SANE_CONSTRAINT_WORD_LIST: + break; + default: + break; + } + break; + case SANE_TYPE_STRING: + break; + case SANE_TYPE_BUTTON: + break; + case SANE_TYPE_GROUP: + break; + } + switch (n) { + case CS3_OPTION_NUM: + return SANE_STATUS_INVAL; + break; + case CS3_OPTION_NEGATIVE: + s->negative = *(SANE_Word *) v; + break; + case CS3_OPTION_INFRARED: + s->infrared = *(SANE_Word *) v; + /* flags |= SANE_INFO_RELOAD_PARAMS; XXX */ + break; + case CS3_OPTION_SAMPLES_PER_SCAN: + s->samples_per_scan = *(SANE_Word *) v; + break; + case CS3_OPTION_DEPTH: + if (*(SANE_Word *) v > s->maxbits) + return SANE_STATUS_INVAL; + + s->depth = *(SANE_Word *) v; + flags |= SANE_INFO_RELOAD_PARAMS; + break; + + case CS3_OPTION_PREVIEW: + s->preview = *(SANE_Word *) v; + break; + + case CS3_OPTION_AUTOLOAD: + s->autoload = *(SANE_Word *) v; + break; + + case CS3_OPTION_EXPOSURE: + s->exposure = SANE_UNFIX(*(SANE_Word *) v); + break; + case CS3_OPTION_EXPOSURE_R: + s->exposure_r = SANE_UNFIX(*(SANE_Word *) v); + break; + case CS3_OPTION_EXPOSURE_G: + s->exposure_g = SANE_UNFIX(*(SANE_Word *) v); + break; + case CS3_OPTION_EXPOSURE_B: + s->exposure_b = SANE_UNFIX(*(SANE_Word *) v); + break; + case CS3_OPTION_LUT_R: + if (!(s->lut_r)) + return SANE_STATUS_INVAL; + for (pixel = 0; pixel < s->n_lut; pixel++) + s->lut_r[pixel] = ((SANE_Word *) v)[pixel]; + break; + case CS3_OPTION_LUT_G: + if (!(s->lut_g)) + return SANE_STATUS_INVAL; + for (pixel = 0; pixel < s->n_lut; pixel++) + s->lut_g[pixel] = ((SANE_Word *) v)[pixel]; + break; + case CS3_OPTION_LUT_B: + if (!(s->lut_b)) + return SANE_STATUS_INVAL; + for (pixel = 0; pixel < s->n_lut; pixel++) + s->lut_b[pixel] = ((SANE_Word *) v)[pixel]; + break; + case CS3_OPTION_LOAD: + cs3_load(s); + break; + case CS3_OPTION_EJECT: + cs3_eject(s); + break; + case CS3_OPTION_RESET: + cs3_reset(s); + break; + case CS3_OPTION_FRAME: + s->i_frame = *(SANE_Word *) v; + break; + + case CS3_OPTION_FRAME_COUNT: + if (*(SANE_Word *) v > (s->n_frames - s->i_frame + 1)) + return SANE_STATUS_INVAL; + s->frame_count = *(SANE_Word *) v; + flags |= SANE_INFO_RELOAD_PARAMS; + break; + + case CS3_OPTION_SUBFRAME: + s->subframe = SANE_UNFIX(*(SANE_Word *) v); + break; + case CS3_OPTION_RES: + s->res = *(SANE_Word *) v; + flags |= SANE_INFO_RELOAD_PARAMS; + break; + case CS3_OPTION_RESX: + s->resx = *(SANE_Word *) v; + flags |= SANE_INFO_RELOAD_PARAMS; + break; + case CS3_OPTION_RESY: + s->resy = *(SANE_Word *) v; + flags |= SANE_INFO_RELOAD_PARAMS; + break; + case CS3_OPTION_RES_INDEPENDENT: + s->res_independent = *(SANE_Word *) v; + flags |= SANE_INFO_RELOAD_PARAMS; + break; + case CS3_OPTION_PREVIEW_RESOLUTION: + s->res_preview = *(SANE_Word *) v; + flags |= SANE_INFO_RELOAD_PARAMS; + break; + case CS3_OPTION_XMIN: + s->xmin = *(SANE_Word *) v; + flags |= SANE_INFO_RELOAD_PARAMS; + break; + case CS3_OPTION_XMAX: + s->xmax = *(SANE_Word *) v; + flags |= SANE_INFO_RELOAD_PARAMS; + break; + case CS3_OPTION_YMIN: + s->ymin = *(SANE_Word *) v; + flags |= SANE_INFO_RELOAD_PARAMS; + break; + case CS3_OPTION_YMAX: + s->ymax = *(SANE_Word *) v; + flags |= SANE_INFO_RELOAD_PARAMS; + break; + case CS3_OPTION_FOCUS_ON_CENTRE: + s->focus_on_centre = *(SANE_Word *) v; + if (s->focus_on_centre) { + s->option_list[CS3_OPTION_FOCUSX].cap |= + SANE_CAP_INACTIVE; + s->option_list[CS3_OPTION_FOCUSY].cap |= + SANE_CAP_INACTIVE; + } else { + s->option_list[CS3_OPTION_FOCUSX].cap &= + ~SANE_CAP_INACTIVE; + s->option_list[CS3_OPTION_FOCUSY].cap &= + ~SANE_CAP_INACTIVE; + } + flags |= SANE_INFO_RELOAD_OPTIONS; + break; + case CS3_OPTION_FOCUS: + s->focus = *(SANE_Word *) v; + break; + case CS3_OPTION_AUTOFOCUS: + s->autofocus = *(SANE_Word *) v; + break; + case CS3_OPTION_FOCUSX: + s->focusx = *(SANE_Word *) v; + break; + case CS3_OPTION_FOCUSY: + s->focusy = *(SANE_Word *) v; + break; + case CS3_OPTION_SCAN_AE: + s->ae = *(SANE_Word *) v; + break; + case CS3_OPTION_SCAN_AE_WB: + s->aewb = *(SANE_Word *) v; + break; + default: + DBG(4, + "Error: sane_control_option(): Unknown option number (bug?).\n"); + return SANE_STATUS_INVAL; + break; + } + break; + + default: + DBG(1, + "BUG: sane_control_option(): Unknown action number.\n"); + return SANE_STATUS_INVAL; + break; + } + + if (i) + *i = flags; + + return SANE_STATUS_GOOD; +} + +SANE_Status +sane_get_parameters(SANE_Handle h, SANE_Parameters * p) +{ + cs3_t *s = (cs3_t *) h; + SANE_Status status; + + DBG(10, "%s\n", __func__); + + if (!s->scanning) { /* only recalculate when not scanning */ + status = cs3_convert_options(s); + if (status != SANE_STATUS_GOOD) + return status; + } + + p->bytes_per_line = + s->n_colors * s->logical_width * s->bytes_per_pixel; + +#ifdef SANE_FRAME_RGBI + if (s->infrared) { + p->format = SANE_FRAME_RGBI; + + } else { +#endif + p->format = SANE_FRAME_RGB; /* XXXXXXXX CCCCCCCCCC */ +#ifdef SANE_FRAME_RGBI + } +#endif + + p->last_frame = SANE_TRUE; + p->lines = s->logical_height; + p->depth = 8 * s->bytes_per_pixel; + p->pixels_per_line = s->logical_width; + + return SANE_STATUS_GOOD; +} + +SANE_Status +sane_start(SANE_Handle h) +{ + cs3_t *s = (cs3_t *) h; + SANE_Status status; + + DBG(10, "%s\n", __func__); + + if (s->scanning) + return SANE_STATUS_INVAL; + + if (s->n_frames > 1 && s->frame_count == 0) { + DBG(4, "%s: no more frames\n", __func__); + return SANE_STATUS_NO_DOCS; + } + + if (s->n_frames > 1) { + DBG(4, "%s: scanning frame at position %d, %d to go\n", + __func__, s->i_frame, s->frame_count); + } + + status = cs3_convert_options(s); + if (status != SANE_STATUS_GOOD) + return status; + + s->i_line_buf = 0; + s->xfer_position = 0; + + s->scanning = SANE_TRUE; + + /* load if appropriate */ + if (s->autoload) { + status = cs3_load(s); + if (status != SANE_STATUS_GOOD) + return status; + } + + /* check for documents */ + status = cs3_scanner_ready(s, CS3_STATUS_NO_DOCS); + if (status != SANE_STATUS_GOOD) + return status; + if (s->status & CS3_STATUS_NO_DOCS) + return SANE_STATUS_NO_DOCS; + + if (s->autofocus) { + status = cs3_autofocus(s); + if (status != SANE_STATUS_GOOD) + return status; + } + + if (s->aewb) { + status = cs3_autoexposure(s, 1); + if (status != SANE_STATUS_GOOD) + return status; + } else if (s->ae) { + status = cs3_autoexposure(s, 0); + if (status != SANE_STATUS_GOOD) + return status; + } + + return cs3_scan(s, CS3_SCAN_NORMAL); +} + +SANE_Status +sane_read(SANE_Handle h, SANE_Byte * buf, SANE_Int maxlen, SANE_Int * len) +{ + cs3_t *s = (cs3_t *) h; + SANE_Status status; + ssize_t xfer_len_in, xfer_len_line, xfer_len_out; + unsigned long index; + int color, sample_pass; + uint8_t *s8 = NULL; + uint16_t *s16 = NULL; + double m_avg_sum; + SANE_Byte *line_buf_new; + + DBG(32, "%s, maxlen = %i.\n", __func__, maxlen); + + if (!s->scanning) { + *len = 0; + return SANE_STATUS_CANCELLED; + } + + /* transfer from buffer */ + if (s->i_line_buf > 0) { + xfer_len_out = s->n_line_buf - s->i_line_buf; + if (xfer_len_out > maxlen) + xfer_len_out = maxlen; + + memcpy(buf, &(s->line_buf[s->i_line_buf]), xfer_len_out); + + s->i_line_buf += xfer_len_out; + if (s->i_line_buf >= s->n_line_buf) + s->i_line_buf = 0; + + *len = xfer_len_out; + return SANE_STATUS_GOOD; + } + + xfer_len_line = s->n_colors * s->logical_width * s->bytes_per_pixel; + xfer_len_in = xfer_len_line + (s->n_colors * s->odd_padding); + + if ((xfer_len_in & 0x3f)) { + int d = ((xfer_len_in / 512) * 512) + 512; + s->block_padding = d - xfer_len_in; + } + + DBG(22, "%s: block_padding = %d, odd_padding = %d\n", + __func__, s->block_padding, s->odd_padding); + + DBG(22, + "%s: colors = %d, logical_width = %ld, bytes_per_pixel = %d\n", + __func__, s->n_colors, s->logical_width, s->bytes_per_pixel); + + + /* Do not change the behaviour of older models, pad to 512 */ + if ((s->type == CS3_TYPE_LS50) || (s->type == CS3_TYPE_LS5000)) { + xfer_len_in += s->block_padding; + if (xfer_len_in & 0x3f) + DBG(1, "BUG: %s, not a multiple of 64. (0x%06lx)\n", + __func__, (long) xfer_len_in); + } + + if (s->xfer_position + xfer_len_line > s->xfer_bytes_total) + xfer_len_line = s->xfer_bytes_total - s->xfer_position; /* just in case */ + + if (xfer_len_line == 0) { /* no more data */ + *len = 0; + + /* increment frame number if appropriate */ + if (s->n_frames > 1 && --s->frame_count) { + s->i_frame++; + } + + s->scanning = SANE_FALSE; + return SANE_STATUS_EOF; + } + + if (xfer_len_line != s->n_line_buf) { + line_buf_new = + (SANE_Byte *) cs3_xrealloc(s->line_buf, + xfer_len_line * + sizeof(SANE_Byte)); + if (!line_buf_new) { + *len = 0; + return SANE_STATUS_NO_MEM; + } + s->line_buf = line_buf_new; + s->n_line_buf = xfer_len_line; + } + + /* adapt for multi-sampling */ + xfer_len_in *= s->samples_per_scan; + + cs3_scanner_ready(s, CS3_STATUS_READY); + cs3_init_buffer(s); + cs3_parse_cmd(s, "28 00 00 00 00 00"); + cs3_pack_byte(s, (xfer_len_in >> 16) & 0xff); + cs3_pack_byte(s, (xfer_len_in >> 8) & 0xff); + cs3_pack_byte(s, xfer_len_in & 0xff); + cs3_parse_cmd(s, "00"); + s->n_recv = xfer_len_in; + + status = cs3_issue_cmd(s); + if (status != SANE_STATUS_GOOD) { + *len = 0; + return status; + } + + for (index = 0; index < s->logical_width; index++) { + for (color = 0; color < s->n_colors; color++) { + int where = s->bytes_per_pixel + * (s->n_colors * index + color); + + m_avg_sum = 0.0; + + switch (s->bytes_per_pixel) { + case 1: + { + /* target address */ + s8 = (uint8_t *) & (s->line_buf[where]); + + if (s->samples_per_scan > 1) { + /* calculate average of multi samples */ + for (sample_pass = 0; + sample_pass < s->samples_per_scan; + sample_pass++) { + /* source index */ + int p8 = (sample_pass * s->n_colors + color) + * s->logical_width + + (color + 1) * s->odd_padding + + index; + m_avg_sum += (double) s->recv_buf[p8]; + } + *s8 = (uint8_t) (m_avg_sum / s->samples_per_scan + 0.5); + } else { + /* shortcut for single sample */ + int p8 = s->logical_width * color + + (color + 1) * s->odd_padding + + index; + *s8 = s->recv_buf[p8]; + } + } + break; + case 2: + { + /* target address */ + s16 = (uint16_t *) & (s->line_buf[where]); + + if (s->samples_per_scan > 1) { + /* calculate average of multi samples */ + for (sample_pass = 0; + sample_pass < s->samples_per_scan; + sample_pass++) { + /* source index */ + int p16 = 2 * ((sample_pass * s->n_colors + color) + * s->logical_width + index); + m_avg_sum += (double) ((s->recv_buf[p16] << 8) + + s->recv_buf[p16 + 1]); + } + *s16 = (uint16_t) (m_avg_sum / s->samples_per_scan + 0.5); + } else { + /* shortcut for single sample */ + int p16 = 2 * (color * s->logical_width + index); + + *s16 = (s->recv_buf[p16] << 8) + + s->recv_buf[p16 + 1]; + } + + *s16 <<= s->shift_bits; + } + break; + + default: + DBG(1, + "BUG: sane_read(): Unknown number of bytes per pixel.\n"); + *len = 0; + return SANE_STATUS_INVAL; + break; + } + } + } + + s->xfer_position += xfer_len_line; + + xfer_len_out = xfer_len_line; + if (xfer_len_out > maxlen) + xfer_len_out = maxlen; + + memcpy(buf, s->line_buf, xfer_len_out); + if (xfer_len_out < xfer_len_line) + s->i_line_buf = xfer_len_out; /* data left in the line buffer, read out next time */ + + *len = xfer_len_out; + return SANE_STATUS_GOOD; +} + +void +sane_cancel(SANE_Handle h) +{ + cs3_t *s = (cs3_t *) h; + + DBG(10, "%s, scanning = %d.\n", __func__, s->scanning); + + if (s->scanning) { + cs3_init_buffer(s); + cs3_parse_cmd(s, "c0 00 00 00 00 00"); + cs3_issue_cmd(s); + } + + s->scanning = SANE_FALSE; +} + +SANE_Status +sane_set_io_mode(SANE_Handle h, SANE_Bool m) +{ + cs3_t *s = (cs3_t *) h; + + DBG(10, "%s\n", __func__); + + if (!s->scanning) + return SANE_STATUS_INVAL; + if (m == SANE_FALSE) + return SANE_STATUS_GOOD; + else + return SANE_STATUS_UNSUPPORTED; +} + +SANE_Status +sane_get_select_fd(SANE_Handle h, SANE_Int * fd) +{ + cs3_t *s = (cs3_t *) h; + + DBG(10, "%s\n", __func__); + + fd = fd; /* to shut up compiler */ + s = s; /* to shut up compiler */ + + return SANE_STATUS_UNSUPPORTED; +} + + +/* ========================================================================= */ +/* private functions */ + +static void +cs3_trim(char *s) +{ + int i, l = strlen(s); + + for (i = l - 1; i > 0; i--) { + if (s[i] == ' ') + s[i] = '\0'; + else + break; + } +} + +static SANE_Status +cs3_open(const char *device, cs3_interface_t interface, cs3_t ** sp) +{ + SANE_Status status; + cs3_t *s; + char *prefix = NULL, *line; + int i; + int alloc_failed = 0; + SANE_Device **device_list_new; + + DBG(6, "%s, device = %s, interface = %i\n", + __func__, device, interface); + + if (!strncmp(device, "auto", 5)) { + try_interface = CS3_INTERFACE_SCSI; + sanei_config_attach_matching_devices("scsi Nikon *", + cs3_attach); + try_interface = CS3_INTERFACE_USB; + sanei_usb_attach_matching_devices("usb 0x04b0 0x4000", + cs3_attach); + sanei_usb_attach_matching_devices("usb 0x04b0 0x4001", + cs3_attach); + sanei_usb_attach_matching_devices("usb 0x04b0 0x4002", + cs3_attach); + return SANE_STATUS_GOOD; + } + + if ((s = (cs3_t *) cs3_xmalloc(sizeof(cs3_t))) == NULL) + return SANE_STATUS_NO_MEM; + memset(s, 0, sizeof(cs3_t)); + + /* fill magic bits */ + s->magic = SANE_COOKIE; + s->cookie_ptr = &s->cookie; + + s->cookie.version = 0x01; + s->cookie.vendor = s->vendor_string; + s->cookie.model = s->product_string; + s->cookie.revision = s->revision_string; + + s->send_buf = s->recv_buf = NULL; + s->send_buf_size = s->recv_buf_size = 0; + + switch (interface) { + case CS3_INTERFACE_UNKNOWN: + for (i = 0; i < 2; i++) { + switch (i) { + case 1: + prefix = "usb:"; + try_interface = CS3_INTERFACE_USB; + break; + default: + prefix = "scsi:"; + try_interface = CS3_INTERFACE_SCSI; + break; + } + if (!strncmp(device, prefix, strlen(prefix))) { + const void *p = device + strlen(prefix); + cs3_xfree(s); + return cs3_open(p, try_interface, sp); + } + } + cs3_xfree(s); + return SANE_STATUS_INVAL; + break; + case CS3_INTERFACE_SCSI: + s->interface = CS3_INTERFACE_SCSI; + DBG(6, + "%s, trying to open %s, assuming SCSI or SBP2 interface\n", + __func__, device); + status = sanei_scsi_open(device, &s->fd, + cs3_scsi_sense_handler, s); + if (status != SANE_STATUS_GOOD) { + DBG(6, " ...failed: %s.\n", sane_strstatus(status)); + cs3_xfree(s); + return status; + } + break; + case CS3_INTERFACE_USB: + s->interface = CS3_INTERFACE_USB; + DBG(6, "%s, trying to open %s, assuming USB interface\n", + __func__, device); + status = sanei_usb_open(device, &s->fd); + if (status != SANE_STATUS_GOOD) { + DBG(6, " ...failed: %s.\n", sane_strstatus(status)); + cs3_xfree(s); + return status; + } + break; + } + + open_devices++; + DBG(6, "%s, trying to identify device.\n", __func__); + + /* identify scanner */ + status = cs3_page_inquiry(s, -1); + if (status != SANE_STATUS_GOOD) { + cs3_close(s); + return status; + } + + strncpy(s->vendor_string, (char *) s->recv_buf + 8, 8); + s->vendor_string[8] = '\0'; + strncpy(s->product_string, (char *) s->recv_buf + 16, 16); + s->product_string[16] = '\0'; + strncpy(s->revision_string, (char *) s->recv_buf + 32, 4); + s->revision_string[4] = '\0'; + + DBG(10, + "%s, vendor = '%s', product = '%s', revision = '%s'.\n", + __func__, s->vendor_string, s->product_string, + s->revision_string); + + if (!strncmp(s->product_string, "COOLSCANIII ", 16)) + s->type = CS3_TYPE_LS30; + else if (!strncmp(s->product_string, "LS-40 ED ", 16)) + s->type = CS3_TYPE_LS40; + else if (!strncmp(s->product_string, "LS-50 ED ", 16)) + s->type = CS3_TYPE_LS50; + else if (!strncmp(s->product_string, "LS-2000 ", 16)) + s->type = CS3_TYPE_LS2000; + else if (!strncmp(s->product_string, "LS-4000 ED ", 16)) + s->type = CS3_TYPE_LS4000; + else if (!strncmp(s->product_string, "LS-5000 ED ", 16)) + s->type = CS3_TYPE_LS5000; + else if (!strncmp(s->product_string, "LS-8000 ED ", 16)) + s->type = CS3_TYPE_LS8000; + + if (s->type != CS3_TYPE_UNKOWN) + DBG(10, + "%s, device identified as coolscan3 type #%i.\n", + __func__, s->type); + else { + DBG(10, "%s, device not identified.\n", __func__); + cs3_close(s); + return SANE_STATUS_UNSUPPORTED; + } + + cs3_trim(s->vendor_string); + cs3_trim(s->product_string); + cs3_trim(s->revision_string); + + if (sp) + *sp = s; + else { + device_list_new = + (SANE_Device **) cs3_xrealloc(device_list, + (n_device_list + + 2) * + sizeof(SANE_Device *)); + if (!device_list_new) + return SANE_STATUS_NO_MEM; + device_list = device_list_new; + device_list[n_device_list] = + (SANE_Device *) cs3_xmalloc(sizeof(SANE_Device)); + if (!device_list[n_device_list]) + return SANE_STATUS_NO_MEM; + switch (interface) { + case CS3_INTERFACE_UNKNOWN: + DBG(1, "BUG: cs3_open(): unknown interface.\n"); + cs3_close(s); + return SANE_STATUS_UNSUPPORTED; + break; + case CS3_INTERFACE_SCSI: + prefix = "scsi:"; + break; + case CS3_INTERFACE_USB: + prefix = "usb:"; + break; + } + + line = (char *) cs3_xmalloc(strlen(device) + strlen(prefix) + + 1); + if (!line) + alloc_failed = 1; + else { + strcpy(line, prefix); + strcat(line, device); + device_list[n_device_list]->name = line; + } + + line = (char *) cs3_xmalloc(strlen(s->vendor_string) + 1); + if (!line) + alloc_failed = 1; + else { + strcpy(line, s->vendor_string); + device_list[n_device_list]->vendor = line; + } + + line = (char *) cs3_xmalloc(strlen(s->product_string) + 1); + if (!line) + alloc_failed = 1; + else { + strcpy(line, s->product_string); + device_list[n_device_list]->model = line; + } + + device_list[n_device_list]->type = "film scanner"; + + if (alloc_failed) { + cs3_xfree(device_list[n_device_list]->name); + cs3_xfree(device_list[n_device_list]->vendor); + cs3_xfree(device_list[n_device_list]->model); + cs3_xfree(device_list[n_device_list]); + } else + n_device_list++; + device_list[n_device_list] = NULL; + + cs3_close(s); + } + + return SANE_STATUS_GOOD; +} + +void +cs3_close(cs3_t * s) +{ + cs3_xfree(s->lut_r); + cs3_xfree(s->lut_g); + cs3_xfree(s->lut_b); + cs3_xfree(s->lut_neutral); + cs3_xfree(s->line_buf); + + switch (s->interface) { + case CS3_INTERFACE_UNKNOWN: + DBG(0, "BUG: %s: Unknown interface number.\n", __func__); + break; + case CS3_INTERFACE_SCSI: + sanei_scsi_close(s->fd); + open_devices--; + break; + case CS3_INTERFACE_USB: + sanei_usb_close(s->fd); + open_devices--; + break; + } + + cs3_xfree(s); +} + +static SANE_Status +cs3_attach(const char *dev) +{ + SANE_Status status; + + if (try_interface == CS3_INTERFACE_UNKNOWN) + return SANE_STATUS_UNSUPPORTED; + + status = cs3_open(dev, try_interface, NULL); + return status; +} + +static SANE_Status +cs3_scsi_sense_handler(int fd, u_char * sense_buffer, void *arg) +{ + cs3_t *s = (cs3_t *) arg; + + fd = fd; /* to shut up compiler */ + + /* sort this out ! XXX */ + + s->sense_key = sense_buffer[2] & 0x0f; + s->sense_asc = sense_buffer[12]; + s->sense_ascq = sense_buffer[13]; + s->sense_info = sense_buffer[3]; + + return cs3_parse_sense_data(s); +} + +static SANE_Status +cs3_parse_sense_data(cs3_t * s) +{ + SANE_Status status = SANE_STATUS_GOOD; + + s->sense_code = + (s->sense_key << 24) + (s->sense_asc << 16) + + (s->sense_ascq << 8) + s->sense_info; + + if (s->sense_key) + DBG(14, "sense code: %02lx-%02lx-%02lx-%02lx\n", s->sense_key, + s->sense_asc, s->sense_ascq, s->sense_info); + + switch (s->sense_key) { + case 0x00: + s->status = CS3_STATUS_READY; + break; + + case 0x02: + switch (s->sense_asc) { + case 0x04: + DBG(15, " processing\n"); + s->status = CS3_STATUS_PROCESSING; + break; + case 0x3a: + DBG(15, " no docs\n"); + s->status = CS3_STATUS_NO_DOCS; + break; + default: + DBG(15, " default\n"); + s->status = CS3_STATUS_ERROR; + status = SANE_STATUS_IO_ERROR; + break; + } + break; + + case 0x09: + if ((s->sense_code == 0x09800600) + || (s->sense_code == 0x09800601)) + s->status = CS3_STATUS_REISSUE; + break; + + default: + s->status = CS3_STATUS_ERROR; + status = SANE_STATUS_IO_ERROR; + break; + } + + return status; +} + +static void +cs3_init_buffer(cs3_t * s) +{ + s->n_cmd = 0; + s->n_send = 0; + s->n_recv = 0; +} + +static SANE_Status +cs3_pack_byte(cs3_t * s, SANE_Byte byte) +{ + while (s->send_buf_size <= s->n_send) { + s->send_buf_size += 16; + s->send_buf = + (SANE_Byte *) cs3_xrealloc(s->send_buf, + s->send_buf_size); + if (!s->send_buf) + return SANE_STATUS_NO_MEM; + } + + s->send_buf[s->n_send++] = byte; + + return SANE_STATUS_GOOD; +} + +static void +cs3_pack_long(cs3_t * s, unsigned long val) +{ + cs3_pack_byte(s, (val >> 24) & 0xff); + cs3_pack_byte(s, (val >> 16) & 0xff); + cs3_pack_byte(s, (val >> 8) & 0xff); + cs3_pack_byte(s, val & 0xff); +} + +static void +cs3_pack_word(cs3_t * s, unsigned long val) +{ + cs3_pack_byte(s, (val >> 8) & 0xff); + cs3_pack_byte(s, val & 0xff); +} + +static SANE_Status +cs3_parse_cmd(cs3_t * s, char *text) +{ + size_t i, j; + char c, h; + SANE_Status status; + + for (i = 0; i < strlen(text); i += 2) + if (text[i] == ' ') + i--; /* a bit dirty... advance by -1+2=1 */ + else { + if ((!isxdigit(text[i])) || (!isxdigit(text[i + 1]))) + DBG(1, + "BUG: cs3_parse_cmd(): Parser got invalid character.\n"); + c = 0; + for (j = 0; j < 2; j++) { + h = tolower(text[i + j]); + if ((h >= 'a') && (h <= 'f')) + c += 10 + h - 'a'; + else + c += h - '0'; + if (j == 0) + c <<= 4; + } + status = cs3_pack_byte(s, c); + if (status != SANE_STATUS_GOOD) + return status; + } + + return SANE_STATUS_GOOD; +} + +static SANE_Status +cs3_grow_send_buffer(cs3_t * s) +{ + if (s->n_send > s->send_buf_size) { + s->send_buf_size = s->n_send; + s->send_buf = + (SANE_Byte *) cs3_xrealloc(s->send_buf, + s->send_buf_size); + if (!s->send_buf) + return SANE_STATUS_NO_MEM; + } + + return SANE_STATUS_GOOD; +} + +static SANE_Status +cs3_issue_cmd(cs3_t * s) +{ + SANE_Status status = SANE_STATUS_INVAL; + size_t n_data, n_status; + static SANE_Byte status_buf[8]; + int status_only = 0; + + DBG(20, + "cs3_issue_cmd(): opcode = 0x%02x, n_send = %lu, n_recv = %lu.\n", + s->send_buf[0], (unsigned long) s->n_send, + (unsigned long) s->n_recv); + + s->status = CS3_STATUS_READY; + + if (!s->n_cmd) + switch (s->send_buf[0]) { + case 0x00: + case 0x12: + case 0x15: + case 0x16: + case 0x17: + case 0x1a: + case 0x1b: + case 0x1c: + case 0x1d: + case 0xc0: + case 0xc1: + s->n_cmd = 6; + break; + case 0x24: + case 0x25: + case 0x28: + case 0x2a: + case 0xe0: + case 0xe1: + s->n_cmd = 10; + break; + default: + DBG(1, + "BUG: cs3_issue_cmd(): Unknown command opcode 0x%02x.\n", + s->send_buf[0]); + break; + } + + if (s->n_send < s->n_cmd) { + DBG(1, + "BUG: cs3_issue_cmd(): Negative number of data out bytes requested.\n"); + return SANE_STATUS_INVAL; + } + + n_data = s->n_send - s->n_cmd; + if (s->n_recv > 0) { + if (n_data > 0) { + DBG(1, + "BUG: cs3_issue_cmd(): Both data in and data out requested.\n"); + return SANE_STATUS_INVAL; + } else { + n_data = s->n_recv; + } + } + + s->recv_buf = (SANE_Byte *) cs3_xrealloc(s->recv_buf, s->n_recv); + if (!s->recv_buf) + return SANE_STATUS_NO_MEM; + + switch (s->interface) { + case CS3_INTERFACE_UNKNOWN: + DBG(1, + "BUG: cs3_issue_cmd(): Unknown or uninitialized interface number.\n"); + break; + + case CS3_INTERFACE_SCSI: + sanei_scsi_cmd2(s->fd, s->send_buf, s->n_cmd, + s->send_buf + s->n_cmd, s->n_send - s->n_cmd, + s->recv_buf, &s->n_recv); + status = SANE_STATUS_GOOD; + break; + + case CS3_INTERFACE_USB: + status = sanei_usb_write_bulk(s->fd, s->send_buf, &s->n_cmd); + if (status != SANE_STATUS_GOOD) { + DBG(1, + "Error: cs3_issue_cmd(): Could not write command.\n"); + return SANE_STATUS_IO_ERROR; + } + + switch (cs3_phase_check(s)) { + case CS3_PHASE_OUT: + if (s->n_send - s->n_cmd < n_data || !n_data) { + DBG(4, + "Error: cs3_issue_cmd(): Unexpected data out phase.\n"); + return SANE_STATUS_IO_ERROR; + } + status = sanei_usb_write_bulk(s->fd, + s->send_buf + s->n_cmd, + &n_data); + break; + + case CS3_PHASE_IN: + if (s->n_recv < n_data || !n_data) { + DBG(4, + "Error: cs3_issue_cmd(): Unexpected data in phase.\n"); + return SANE_STATUS_IO_ERROR; + } + status = sanei_usb_read_bulk(s->fd, s->recv_buf, + &n_data); + s->n_recv = n_data; + break; + + case CS3_PHASE_NONE: + DBG(4, "%s: No command received!\n", __func__); + return SANE_STATUS_IO_ERROR; + + default: + if (n_data) { + DBG(4, + "%s: Unexpected non-data phase, but n_data != 0 (%lu).\n", + __func__, (u_long) n_data); + status_only = 1; + } + break; + } + + n_status = 8; + status = sanei_usb_read_bulk(s->fd, status_buf, &n_status); + if (n_status != 8) { + DBG(4, + "Error: cs3_issue_cmd(): Failed to read 8 status bytes from USB.\n"); + return SANE_STATUS_IO_ERROR; + } + + s->sense_key = status_buf[1] & 0x0f; + s->sense_asc = status_buf[2] & 0xff; + s->sense_ascq = status_buf[3] & 0xff; + s->sense_info = status_buf[4] & 0xff; + status = cs3_parse_sense_data(s); + break; + } + + if (status_only) + return SANE_STATUS_IO_ERROR; + else + return status; +} + +static cs3_phase_t +cs3_phase_check(cs3_t * s) +{ + static SANE_Byte phase_send_buf[1] = { 0xd0 }, phase_recv_buf[1]; + SANE_Status status = 0; + size_t n = 1; + + status = sanei_usb_write_bulk(s->fd, phase_send_buf, &n); + status |= sanei_usb_read_bulk(s->fd, phase_recv_buf, &n); + + DBG(40, "%s: returned phase = 0x%02x.\n", __func__, + phase_recv_buf[0]); + + if (status != SANE_STATUS_GOOD) + return -1; + else + return phase_recv_buf[0]; +} + +static SANE_Status +cs3_scanner_ready(cs3_t * s, int flags) +{ + SANE_Status status = SANE_STATUS_GOOD; + int i = -1; + unsigned long count = 0; + int retry = 3; + + do { + if (i >= 0) /* dirty !!! */ + usleep(1000000); + /* test unit ready */ + cs3_init_buffer(s); + for (i = 0; i < 6; i++) + cs3_pack_byte(s, 0x00); + + status = cs3_issue_cmd(s); + if (status != SANE_STATUS_GOOD) + if (--retry < 0) + return status; + + if (++count > 120) { /* 120s timeout */ + DBG(4, "Error: %s: Timeout expired.\n", __func__); + status = SANE_STATUS_IO_ERROR; + break; + } + } + while (s->status & ~flags); /* until all relevant bits are 0 */ + + return status; +} + +static SANE_Status +cs3_page_inquiry(cs3_t * s, int page) +{ + SANE_Status status; + + size_t n; + + if (page >= 0) { + + cs3_scanner_ready(s, CS3_STATUS_NO_DOCS); + cs3_init_buffer(s); + cs3_parse_cmd(s, "12 01"); + cs3_pack_byte(s, page); + cs3_parse_cmd(s, "00 04 00"); + s->n_recv = 4; + status = cs3_issue_cmd(s); + if (status != SANE_STATUS_GOOD) { + DBG(4, + "Error: cs3_page_inquiry(): Inquiry of page size failed: %s.\n", + sane_strstatus(status)); + return status; + } + + n = s->recv_buf[3] + 4; + + } else + n = 36; + + cs3_scanner_ready(s, CS3_STATUS_NO_DOCS); + cs3_init_buffer(s); + if (page >= 0) { + cs3_parse_cmd(s, "12 01"); + cs3_pack_byte(s, page); + cs3_parse_cmd(s, "00"); + } else + cs3_parse_cmd(s, "12 00 00 00"); + cs3_pack_byte(s, n); + cs3_parse_cmd(s, "00"); + s->n_recv = n; + + status = cs3_issue_cmd(s); + if (status != SANE_STATUS_GOOD) { + DBG(4, "Error: %s: inquiry of page failed: %s.\n", + __func__, sane_strstatus(status)); + return status; + } + + return SANE_STATUS_GOOD; +} + +static SANE_Status +cs3_full_inquiry(cs3_t * s) +{ + SANE_Status status; + int pitch, pitch_max; + cs3_pixel_t pixel; + + DBG(4, "%s\n", __func__); + + status = cs3_page_inquiry(s, 0xc1); + if (status != SANE_STATUS_GOOD) + return status; + + s->maxbits = s->recv_buf[82]; + if (s->type == CS3_TYPE_LS30) /* must be overridden, LS-30 claims to have 12 bits */ + s->maxbits = 10; + + s->n_lut = 1; + s->n_lut <<= s->maxbits; + s->lut_r = + (cs3_pixel_t *) cs3_xrealloc(s->lut_r, + s->n_lut * sizeof(cs3_pixel_t)); + s->lut_g = + (cs3_pixel_t *) cs3_xrealloc(s->lut_g, + s->n_lut * sizeof(cs3_pixel_t)); + s->lut_b = + (cs3_pixel_t *) cs3_xrealloc(s->lut_b, + s->n_lut * sizeof(cs3_pixel_t)); + s->lut_neutral = + (cs3_pixel_t *) cs3_xrealloc(s->lut_neutral, + s->n_lut * sizeof(cs3_pixel_t)); + + if (!s->lut_r || !s->lut_g || !s->lut_b || !s->lut_neutral) { + cs3_xfree(s->lut_r); + cs3_xfree(s->lut_g); + cs3_xfree(s->lut_b); + cs3_xfree(s->lut_neutral); + return SANE_STATUS_NO_MEM; + } + + for (pixel = 0; pixel < s->n_lut; pixel++) { + s->lut_r[pixel] = s->lut_g[pixel] = s->lut_b[pixel] = + s->lut_neutral[pixel] = pixel; + } + + s->resx_optical = 256 * s->recv_buf[18] + s->recv_buf[19]; + s->resx_max = 256 * s->recv_buf[20] + s->recv_buf[21]; + s->resx_min = 256 * s->recv_buf[22] + s->recv_buf[23]; + s->boundaryx = + 65536 * (256 * s->recv_buf[36] + s->recv_buf[37]) + + 256 * s->recv_buf[38] + s->recv_buf[39]; + + s->resy_optical = 256 * s->recv_buf[40] + s->recv_buf[41]; + s->resy_max = 256 * s->recv_buf[42] + s->recv_buf[43]; + s->resy_min = 256 * s->recv_buf[44] + s->recv_buf[45]; + s->boundaryy = + 65536 * (256 * s->recv_buf[58] + s->recv_buf[59]) + + 256 * s->recv_buf[60] + s->recv_buf[61]; + + s->focus_min = 256 * s->recv_buf[76] + s->recv_buf[77]; + s->focus_max = 256 * s->recv_buf[78] + s->recv_buf[79]; + + s->n_frames = s->recv_buf[75]; + + s->frame_offset = s->resy_max * 1.5 + 1; /* works for LS-30, maybe not for others */ + + /* generate resolution list for x */ + s->resx_n_list = pitch_max = + floor(s->resx_max / (double) s->resx_min); + s->resx_list = + (unsigned int *) cs3_xrealloc(s->resx_list, + pitch_max * + sizeof(unsigned int)); + for (pitch = 1; pitch <= pitch_max; pitch++) + s->resx_list[pitch - 1] = s->resx_max / pitch; + + /* generate resolution list for y */ + s->resy_n_list = pitch_max = + floor(s->resy_max / (double) s->resy_min); + s->resy_list = + (unsigned int *) cs3_xrealloc(s->resy_list, + pitch_max * + sizeof(unsigned int)); + + for (pitch = 1; pitch <= pitch_max; pitch++) + s->resy_list[pitch - 1] = s->resy_max / pitch; + + s->unit_dpi = s->resx_max; + s->unit_mm = 25.4 / s->unit_dpi; + + DBG(4, " maximum depth: %d\n", s->maxbits); + DBG(4, " focus: %d/%d\n", s->focus_min, s->focus_max); + DBG(4, " resolution (x): %d (%d-%d)\n", s->resx_optical, + s->resx_min, s->resx_max); + DBG(4, " resolution (y): %d (%d-%d)\n", s->resy_optical, + s->resy_min, s->resy_max); + DBG(4, " frames: %d\n", s->n_frames); + DBG(4, " frame offset: %ld\n", s->frame_offset); + + return SANE_STATUS_GOOD; +} + +static SANE_Status +cs3_execute(cs3_t * s) +{ + DBG(16, "%s\n", __func__); + + cs3_scanner_ready(s, CS3_STATUS_NO_DOCS); + cs3_init_buffer(s); + cs3_parse_cmd(s, "c1 00 00 00 00 00"); + return cs3_issue_cmd(s); +} + +static SANE_Status +cs3_issue_and_execute(cs3_t * s) +{ + SANE_Status status; + + DBG(10, "%s, opcode = %02x\n", __func__, s->send_buf[0]); + + status = cs3_issue_cmd(s); + if (status != SANE_STATUS_GOOD) + return status; + + return cs3_execute(s); +} + +static SANE_Status +cs3_mode_select(cs3_t * s) +{ + DBG(4, "%s\n", __func__); + + cs3_scanner_ready(s, CS3_STATUS_NO_DOCS); + cs3_init_buffer(s); + + cs3_parse_cmd(s, + "15 10 00 00 14 00 00 00 00 08 00 00 00 00 00 00 00 01 03 06 00 00"); + cs3_pack_word(s, s->unit_dpi); + cs3_parse_cmd(s, "00 00"); + + return cs3_issue_cmd(s); +} + +static SANE_Status +cs3_load(cs3_t * s) +{ + SANE_Status status; + + DBG(6, "%s\n", __func__); + + cs3_scanner_ready(s, CS3_STATUS_NO_DOCS); + cs3_init_buffer(s); + cs3_parse_cmd(s, "e0 00 d1 00 00 00 00 00 0d 00"); + s->n_send += 13; + + status = cs3_grow_send_buffer(s); + if (status != SANE_STATUS_GOOD) + return status; + + return cs3_issue_and_execute(s); +} + +static SANE_Status +cs3_eject(cs3_t * s) +{ + SANE_Status status; + + cs3_scanner_ready(s, CS3_STATUS_NO_DOCS); + cs3_init_buffer(s); + cs3_parse_cmd(s, "e0 00 d0 00 00 00 00 00 0d 00"); + s->n_send += 13; + + status = cs3_grow_send_buffer(s); + if (status != SANE_STATUS_GOOD) + return status; + + return cs3_issue_and_execute(s); +} + +static SANE_Status +cs3_reset(cs3_t * s) +{ + SANE_Status status; + + cs3_scanner_ready(s, CS3_STATUS_NO_DOCS); + cs3_init_buffer(s); + cs3_parse_cmd(s, "e0 00 80 00 00 00 00 00 0d 00"); + s->n_send += 13; + + status = cs3_grow_send_buffer(s); + if (status != SANE_STATUS_GOOD) + return status; + + return cs3_issue_and_execute(s); +} + + +static SANE_Status +cs3_reserve_unit(cs3_t * s) +{ + DBG(10, "%s\n", __func__); + + cs3_init_buffer(s); + cs3_parse_cmd(s, "16 00 00 00 00 00"); + return cs3_issue_cmd(s); +} + +static SANE_Status +cs3_release_unit(cs3_t * s) +{ + DBG(10, "%s\n", __func__); + + cs3_init_buffer(s); + cs3_parse_cmd(s, "17 00 00 00 00 00"); + return cs3_issue_cmd(s); +} + + +static SANE_Status +cs3_set_focus(cs3_t * s) +{ + DBG(6, "%s: setting focus to %d\n", __func__, s->focus); + + cs3_scanner_ready(s, CS3_STATUS_READY); + cs3_init_buffer(s); + cs3_parse_cmd(s, "e0 00 c1 00 00 00 00 00 09 00 00"); + cs3_pack_long(s, s->focus); + cs3_parse_cmd(s, "00 00 00 00"); + + return cs3_issue_and_execute(s); +} + +static SANE_Status +cs3_read_focus(cs3_t * s) +{ + SANE_Status status; + + cs3_scanner_ready(s, CS3_STATUS_READY); + cs3_init_buffer(s); + cs3_parse_cmd(s, "e1 00 c1 00 00 00 00 00 0d 00"); + s->n_recv = 13; + + status = cs3_issue_cmd(s); + if (status != SANE_STATUS_GOOD) + return status; + + s->focus = + 65536 * (256 * s->recv_buf[1] + s->recv_buf[2]) + + 256 * s->recv_buf[3] + s->recv_buf[4]; + + DBG(4, "%s: focus at %d\n", __func__, s->focus); + + return status; +} + +static SANE_Status +cs3_autofocus(cs3_t * s) +{ + SANE_Status status; + + DBG(6, "%s: focusing at %ld,%ld\n", __func__, + s->real_focusx, s->real_focusy); + + cs3_convert_options(s); + + status = cs3_read_focus(s); + if (status != SANE_STATUS_GOOD) + return status; + + /* set parameter, autofocus */ + cs3_scanner_ready(s, CS3_STATUS_READY); + cs3_init_buffer(s); + cs3_parse_cmd(s, "e0 00 a0 00 00 00 00 00 09 00 00"); + cs3_pack_long(s, s->real_focusx); + cs3_pack_long(s, s->real_focusy); + /*cs3_parse_cmd(s, "00 00 00 00"); */ + + status = cs3_issue_and_execute(s); + if (status != SANE_STATUS_GOOD) + return status; + + return cs3_read_focus(s); +} + +static SANE_Status +cs3_autoexposure(cs3_t * s, int wb) +{ + SANE_Status status; + + DBG(6, "%s, wb = %d\n", __func__, wb); + + cs3_scanner_ready(s, CS3_STATUS_NO_DOCS); + status = cs3_scan(s, wb ? CS3_SCAN_AE_WB : CS3_SCAN_AE); + if (status != SANE_STATUS_GOOD) + return status; + + status = cs3_get_exposure(s); + if (status != SANE_STATUS_GOOD) + return status; + + s->exposure = 1.; + s->exposure_r = s->real_exposure[1] / 100.; + s->exposure_g = s->real_exposure[2] / 100.; + s->exposure_b = s->real_exposure[3] / 100.; + + return status; +} + +static SANE_Status +cs3_get_exposure(cs3_t * s) +{ + SANE_Status status; + int i_color, colors = s->n_colors; + + DBG(6, "%s\n", __func__); + + if ((s->type == CS3_TYPE_LS50) || (s->type == CS3_TYPE_LS5000)) + colors = 3; + + cs3_scanner_ready(s, CS3_STATUS_NO_DOCS); + + /* GET WINDOW */ + for (i_color = 0; i_color < colors; i_color++) { /* XXXXXXXXXXXXX CCCCCCCCCCCCC */ + + cs3_init_buffer(s); + cs3_parse_cmd(s, "25 01 00 00 00"); + cs3_pack_byte(s, cs3_colors[i_color]); + cs3_parse_cmd(s, "00 00 3a 00"); + s->n_recv = 58; + status = cs3_issue_cmd(s); + if (status != SANE_STATUS_GOOD) + return status; + + s->real_exposure[cs3_colors[i_color]] = + 65536 * (256 * s->recv_buf[54] + s->recv_buf[55]) + + 256 * s->recv_buf[56] + s->recv_buf[57]; + + DBG(6, + "%s, exposure for color %i: %li * 10ns\n", + __func__, + cs3_colors[i_color], + s->real_exposure[cs3_colors[i_color]]); + + DBG(6, "%02x %02x %02x %02x\n", s->recv_buf[48], + s->recv_buf[49], s->recv_buf[50], s->recv_buf[51]); + } + + return SANE_STATUS_GOOD; +} + +static SANE_Status +cs3_convert_options(cs3_t * s) +{ + int i_color; + unsigned long xmin, xmax, ymin, ymax; + + DBG(4, "%s\n", __func__); + + s->real_depth = (s->preview ? 8 : s->depth); + s->bytes_per_pixel = (s->real_depth > 8 ? 2 : 1); + s->shift_bits = 8 * s->bytes_per_pixel - s->real_depth; + + DBG(12, " depth = %d, bpp = %d, shift = %d\n", + s->real_depth, s->bytes_per_pixel, s->shift_bits); + + if (s->preview) { + s->real_resx = s->res_preview; + s->real_resy = s->res_preview; + } else if (s->res_independent) { + s->real_resx = s->resx; + s->real_resy = s->resy; + } else { + s->real_resx = s->res; + s->real_resy = s->res; + } + + s->real_pitchx = s->resx_max / s->real_resx; + s->real_pitchy = s->resy_max / s->real_resy; + + s->real_resx = s->resx_max / s->real_pitchx; + s->real_resy = s->resy_max / s->real_pitchy; + + DBG(12, " resx = %d, resy = %d, pitchx = %d, pitchy = %d\n", + s->real_resx, s->real_resy, s->real_pitchx, s->real_pitchy); + + /* The prefix "real_" refers to data in device units (1/maxdpi), + * "logical_" refers to resolution-dependent data. + */ + + if (s->xmin < s->xmax) { + xmin = s->xmin; + xmax = s->xmax; + } else { + xmin = s->xmax; + xmax = s->xmin; + } + + if (s->ymin < s->ymax) { + ymin = s->ymin; + ymax = s->ymax; + } else { + ymin = s->ymax; + ymax = s->ymin; + } + + DBG(12, " xmin = %ld, xmax = %ld\n", xmin, xmax); + DBG(12, " ymin = %ld, ymax = %ld\n", ymin, ymax); + + s->real_xoffset = xmin; + s->real_yoffset = + ymin + (s->i_frame - 1) * s->frame_offset + + s->subframe / s->unit_mm; + + DBG(12, " xoffset = %ld, yoffset = %ld\n", + s->real_xoffset, s->real_yoffset); + + + s->logical_width = (xmax - xmin + 1) / s->real_pitchx; /* XXX use mm units */ + s->logical_height = (ymax - ymin + 1) / s->real_pitchy; + s->real_width = s->logical_width * s->real_pitchx; + s->real_height = s->logical_height * s->real_pitchy; + + DBG(12, " lw = %ld, lh = %ld, rw = %ld, rh = %ld\n", + s->logical_width, s->logical_height, + s->real_width, s->real_height); + + s->odd_padding = 0; + if ((s->bytes_per_pixel == 1) && (s->logical_width & 0x01) + && (s->type != CS3_TYPE_LS30) && (s->type != CS3_TYPE_LS2000)) + s->odd_padding = 1; + + if (s->focus_on_centre) { + s->real_focusx = s->real_xoffset + s->real_width / 2; + s->real_focusy = s->real_yoffset + s->real_height / 2; + } else { + s->real_focusx = s->focusx; + s->real_focusy = + s->focusy + (s->i_frame - 1) * s->frame_offset + + s->subframe / s->unit_mm; + } + + DBG(12, " focusx = %ld, focusy = %ld\n", + s->real_focusx, s->real_focusy); + + s->real_exposure[1] = s->exposure * s->exposure_r * 100.; + s->real_exposure[2] = s->exposure * s->exposure_g * 100.; + s->real_exposure[3] = s->exposure * s->exposure_b * 100.; + + /* XXX IR? */ + for (i_color = 0; i_color < 3; i_color++) + if (s->real_exposure[cs3_colors[i_color]] < 1) + s->real_exposure[cs3_colors[i_color]] = 1; + + s->n_colors = 3; /* XXXXXXXXXXXXXX CCCCCCCCCCCCCC */ + if (s->infrared) + s->n_colors = 4; + + s->xfer_bytes_total = + s->bytes_per_pixel * s->n_colors * s->logical_width * + s->logical_height; + + if (s->preview) + s->infrared = SANE_FALSE; + + return SANE_STATUS_GOOD; +} + +static SANE_Status +cs3_set_boundary(cs3_t * s) +{ + SANE_Status status; + int i_boundary; + + /* Ariel - Check this function */ + cs3_scanner_ready(s, CS3_STATUS_READY); + cs3_init_buffer(s); + cs3_parse_cmd(s, "2a 00 88 00 00 03"); + cs3_pack_byte(s, ((4 + s->n_frames * 16) >> 16) & 0xff); + cs3_pack_byte(s, ((4 + s->n_frames * 16) >> 8) & 0xff); + cs3_pack_byte(s, (4 + s->n_frames * 16) & 0xff); + cs3_parse_cmd(s, "00"); + + cs3_pack_byte(s, ((4 + s->n_frames * 16) >> 8) & 0xff); + cs3_pack_byte(s, (4 + s->n_frames * 16) & 0xff); + cs3_pack_byte(s, s->n_frames); + cs3_pack_byte(s, s->n_frames); + for (i_boundary = 0; i_boundary < s->n_frames; i_boundary++) { + unsigned long lvalue = s->frame_offset * i_boundary + + s->subframe / s->unit_mm; + + cs3_pack_long(s, lvalue); + + cs3_pack_long(s, 0); + + lvalue = s->frame_offset * i_boundary + + s->subframe / s->unit_mm + s->frame_offset - 1; + cs3_pack_long(s, lvalue); + + cs3_pack_long(s, s->boundaryx - 1); + + } + status = cs3_issue_cmd(s); + if (status != SANE_STATUS_GOOD) + return status; + + return SANE_STATUS_GOOD; +} + +static SANE_Status +cs3_send_lut(cs3_t * s) +{ + int color; + SANE_Status status; + cs3_pixel_t *lut, pixel; + + DBG(6, "%s\n", __func__); + + for (color = 0; color < s->n_colors; color++) { + /*cs3_scanner_ready(s, CS3_STATUS_READY); */ + + switch (color) { + case 0: + lut = s->lut_r; + break; + case 1: + lut = s->lut_g; + break; + case 2: + lut = s->lut_b; + break; + case 3: + lut = s->lut_neutral; + break; + default: + DBG(1, + "BUG: %s: Unknown color number for LUT download.\n", + __func__); + return SANE_STATUS_INVAL; + break; + } + + cs3_init_buffer(s); + cs3_parse_cmd(s, "2a 00 03 00"); + cs3_pack_byte(s, cs3_colors[color]); + cs3_pack_byte(s, 2 - 1); /* XXX number of bytes per data point - 1 */ + cs3_pack_byte(s, ((2 * s->n_lut) >> 16) & 0xff); /* XXX 2 bytes per point */ + cs3_pack_byte(s, ((2 * s->n_lut) >> 8) & 0xff); /* XXX 2 bytes per point */ + cs3_pack_byte(s, (2 * s->n_lut) & 0xff); /* XXX 2 bytes per point */ + cs3_pack_byte(s, 0x00); + + for (pixel = 0; pixel < s->n_lut; pixel++) { /* XXX 2 bytes per point */ + cs3_pack_word(s, lut[pixel]); + } + + status = cs3_issue_cmd(s); + if (status != SANE_STATUS_GOOD) + return status; + } + + return status; +} + +static SANE_Status +cs3_set_window(cs3_t * s, cs3_scan_t type) +{ + int color; + SANE_Status status = SANE_STATUS_INVAL; + + /* SET WINDOW */ + for (color = 0; color < s->n_colors; color++) { + + DBG(8, "%s: color %d\n", __func__, cs3_colors[color]); + + cs3_scanner_ready(s, CS3_STATUS_READY); + + cs3_init_buffer(s); + if ((s->type == CS3_TYPE_LS40) + || (s->type == CS3_TYPE_LS4000) + || (s->type == CS3_TYPE_LS50) + || (s->type == CS3_TYPE_LS5000)) + cs3_parse_cmd(s, "24 00 00 00 00 00 00 00 3a 80"); + else + cs3_parse_cmd(s, "24 00 00 00 00 00 00 00 3a 00"); + + cs3_parse_cmd(s, "00 00 00 00 00 00 00 32"); + + cs3_pack_byte(s, cs3_colors[color]); + + cs3_pack_byte(s, 0x00); + + cs3_pack_word(s, s->real_resx); + cs3_pack_word(s, s->real_resy); + cs3_pack_long(s, s->real_xoffset); + cs3_pack_long(s, s->real_yoffset); + cs3_pack_long(s, s->real_width); + cs3_pack_long(s, s->real_height); + cs3_pack_byte(s, 0x00); /* brightness, etc. */ + cs3_pack_byte(s, 0x00); + cs3_pack_byte(s, 0x00); + cs3_pack_byte(s, 0x05); /* image composition CCCCCCC */ + cs3_pack_byte(s, s->real_depth); /* pixel composition */ + cs3_parse_cmd(s, "00 00 00 00 00 00 00 00 00 00 00 00 00"); + cs3_pack_byte(s, ((s->samples_per_scan - 1) << 4) | 0x00); /* multiread, ordering */ + + cs3_pack_byte(s, 0x80 | (s->negative ? 0 : 1)); /* averaging, pos/neg */ + + switch (type) { /* scanning kind */ + case CS3_SCAN_NORMAL: + cs3_pack_byte(s, 0x01); + break; + case CS3_SCAN_AE: + cs3_pack_byte(s, 0x20); + break; + case CS3_SCAN_AE_WB: + cs3_pack_byte(s, 0x40); + break; + default: + DBG(1, "BUG: cs3_scan(): Unknown scanning type.\n"); + return SANE_STATUS_INVAL; + } + if (s->samples_per_scan == 1) + cs3_pack_byte(s, 0x02); /* scanning mode single */ + else + cs3_pack_byte(s, 0x10); /* scanning mode multi */ + cs3_pack_byte(s, 0x02); /* color interleaving */ + cs3_pack_byte(s, 0xff); /* (ae) */ + if (color == 3) /* infrared */ + cs3_parse_cmd(s, "00 00 00 00"); /* automatic */ + else { + DBG(4, "%s: exposure = %ld * 10ns\n", __func__, + s->real_exposure[cs3_colors[color]]); + cs3_pack_long(s, s->real_exposure[cs3_colors[color]]); + } + + status = cs3_issue_cmd(s); + if (status != SANE_STATUS_GOOD) + return status; + } + + return status; +} + + +static SANE_Status +cs3_scan(cs3_t * s, cs3_scan_t type) +{ + SANE_Status status; + + s->block_padding = 0; + + DBG(6, "%s, type = %d, colors = %d\n", __func__, type, s->n_colors); + + switch (type) { + case CS3_SCAN_NORMAL: + DBG(16, "%s: normal scan\n", __func__); + break; + case CS3_SCAN_AE: + DBG(16, "%s: ae scan\n", __func__); + break; + case CS3_SCAN_AE_WB: + DBG(16, "%s: ae wb scan\n", __func__); + break; + } + + /* wait for device to be ready with document, and set device unit */ + status = cs3_scanner_ready(s, CS3_STATUS_NO_DOCS); + if (status != SANE_STATUS_GOOD) + return status; + + if (s->status & CS3_STATUS_NO_DOCS) + return SANE_STATUS_NO_DOCS; + + status = cs3_convert_options(s); + if (status != SANE_STATUS_GOOD) + return status; + + status = cs3_set_boundary(s); + if (status != SANE_STATUS_GOOD) + return status; + + cs3_set_focus(s); + + cs3_scanner_ready(s, CS3_STATUS_READY); + + if (type == CS3_SCAN_NORMAL) + cs3_send_lut(s); + + status = cs3_set_window(s, type); + if (status != SANE_STATUS_GOOD) + return status; + + status = cs3_get_exposure(s); + if (status != SANE_STATUS_GOOD) + return status; + +/* cs3_scanner_ready(s, CS3_STATUS_READY); */ + + cs3_init_buffer(s); + switch (s->n_colors) { + case 3: + cs3_parse_cmd(s, "1b 00 00 00 03 00 01 02 03"); + break; + case 4: + cs3_parse_cmd(s, "1b 00 00 00 04 00 01 02 03 09"); + break; + default: + DBG(0, "BUG: %s: Unknown number of input colors.\n", + __func__); + break; + } + + status = cs3_issue_cmd(s); + if (status != SANE_STATUS_GOOD) { + DBG(6, "scan setup failed\n"); + return status; + } + + if (s->status == CS3_STATUS_REISSUE) { + status = cs3_issue_cmd(s); + if (status != SANE_STATUS_GOOD) + return status; + } + + return SANE_STATUS_GOOD; +} + +static void * +cs3_xmalloc(size_t size) +{ + register void *value = malloc(size); + + if (value == NULL) { + DBG(0, "error: %s: failed to malloc() %lu bytes.\n", + __func__, (unsigned long) size); + } + return value; +} + +static void * +cs3_xrealloc(void *p, size_t size) +{ + register void *value; + + if (!size) + return p; + + value = realloc(p, size); + + if (value == NULL) { + DBG(0, "error: %s: failed to realloc() %lu bytes.\n", + __func__, (unsigned long) size); + } + + return value; +} + +static void +cs3_xfree(const void *p) +{ + if (p) + free(p); +} |