diff options
Diffstat (limited to 'backend/kodakaio.c')
-rw-r--r-- | backend/kodakaio.c | 3346 |
1 files changed, 3346 insertions, 0 deletions
diff --git a/backend/kodakaio.c b/backend/kodakaio.c new file mode 100644 index 0000000..8c4583a --- /dev/null +++ b/backend/kodakaio.c @@ -0,0 +1,3346 @@ +/* + * kodakaio.c - SANE library for Kodak ESP Aio scanners. + * + * Copyright (C) 2011-2013 Paul Newall + * + * Based on the Magicolor sane backend: + * Based on the epson2 sane backend: + * Based on Kazuhiro Sasayama previous + * work on epson.[ch] file from the SANE package. + * Please see those files for additional copyrights. + * Author: Paul Newall + * + * 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. + + * Using avahi now 25/11/12 for net autodiscovery. Use configure option --enable-avahi + * 01/01/13 Now with adf, the scan can be padded to make up the full page length, + * or the page can terminate at the end of the paper. This is a selectable option. + */ + +/* convenient lines to paste +export SANE_DEBUG_KODAKAIO=10 + +for ubuntu prior to 12.10 +./configure --prefix=/usr --sysconfdir=/etc --localstatedir=/var --enable-avahi --disable-latex BACKENDS=kodakaio + +for ubuntu 12.10 +./configure --prefix=/usr --libdir=/usr/lib/i386-linux-gnu --sysconfdir=/etc --localstatedir=/var --enable-avahi --disable-latex BACKENDS=kodakaio + +*/ + +/* SANE-FLOW-DIAGRAM Kodakaio commands in [] brackets + + - sane_init() : initialize backend, attach scanners(devicename,0) + . - sane_get_devices() : query list of scanner-devices + . - sane_open() : open a particular scanner-device and attach_scanner(devicename,&dev) + . . - sane_set_io_mode : set blocking-mode + . . - sane_get_select_fd : get scanner-fd + . . - sane_get_option_descriptor() : get option informations + . . - sane_control_option() : change option values + . . + . . - sane_start() : start image aquisition [V,L,F,S,C,D,O,Z] first time or after cancel. [(F),E,G] every time + . . - sane_get_parameters() : returns actual scan-parameters + . . - sane_read() : read image-data (from pipe) + . . - sane_cancel() : cancel operation, kill reader_process [(F), U] + + . - sane_close() : close opened scanner-device, do_cancel, free buffer and handle + - sane_exit() : terminate use of backend, free devicename and device-struture +*/ +/* FUNCTION-TREE + sane_init + sane_start + k_init_parametersta + k_lock_scanner + k_set_scanning_parameters + print_params + k_start_scan + cmd_start_scan + print_status + k_send + kodakaio_txrxack + sane_open + device_detect + sane_get_devices + init_options + sane_control_option + getvalue + setvalue + search_string_list + change_source + activateOption + deactivateOption + sane_get_parameters + print_params + sane_read + k_read + cmd_read_data (reads one block) + k_recv + cmp_array + sane_exit + free_devices + k_recv + kodakaio_net_read + dump_hex_buffer_dense + k_send + sanei_kodakaio_net_write_raw + dump_hex_buffer_dense + open_scanner + sanei_kodakaio_net_open + close_scanner + sanei_kodakaio_net_close + detect_usb + kodakaio_getNumberOfUSBProductIds + attach_one_config - (Passed to sanei_configure_attach) + kodakaio_getNumberOfUSBProductIds + kodak_network_discovery + client_callback + browse_callback + resolve_callback + ProcessAvahiDevice + attach_one_net + attach_one_net + attach + device_detect + attach_one_usb - (passed to sanei_usb_find_devices) + attach + device_detect + k_lock_scanner + kodakaio_txrx + k_send + k_recv + kodakaio_txrxack + k_send + k_recv + cmd_set_color_curve + kodakaio_expect_ack + k_recv + cmd_cancel_scan + kodakaio_txrxack + cmd_set_scanning_parameters + kodakaio_txrxack + device_detect + k_dev_init +*/ + + +#define KODAKAIO_VERSION 02 +#define KODAKAIO_REVISION 4 +#define KODAKAIO_BUILD 6 + +/* for usb (but also used for net though it's not required). */ +#define MAX_BLOCK_SIZE 32768 + +#define SCANNER_READ_TIMEOUT 15 +/* POLL_ITN_MS sets the individual poll timeout for network discovery */ +#define POLL_ITN_MS 20 + + +/* debugging levels: +In terminal use: export SANE_DEBUG_KODAKAIO=40 to set the level to 40 or whatever +level you want. +Then you can scan with scanimage or simple-scan from terminal and see debug info + +use these defines to promote certain functions that you are interested in +define low values to make detail of a section appear when DBG level is low +define a high value eg 99 to get normal behaviour. */ +#define DBG_READ 99 +#define DBG_AUTO 99 /* for autodiscovery */ + +/* +normal levels. This system is a plan rather than a reality + * + * 127 recv buffer + * 125 send buffer + * 35 fine-grained status and progress + * 30 sane_read + * 25 setvalue, getvalue, control_option + * 20 low-level (I/O) functions + * 15 mid-level functions + * 10 high-level functions + * 7 open/close/attach + * 6 print_params + * 5 basic functions + * 3 status info and progress + * 2 sane api + * 1 errors & warnings + */ + +#include "sane/config.h" + +#include <limits.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <ctype.h> +#include <fcntl.h> +#include <unistd.h> +#include <errno.h> +#include <sys/time.h> +#include <math.h> +#include <poll.h> +#include <time.h> + +#if WITH_AVAHI +/* used for auto detecting network printers */ +#include <assert.h> +#include <avahi-client/client.h> +#include <avahi-client/lookup.h> +#include <avahi-common/simple-watch.h> +#include <avahi-common/malloc.h> +#include <avahi-common/error.h> + +static AvahiSimplePoll *simple_poll = NULL; /* global because called by several functions */ + +#endif + +#include "../include/sane/saneopts.h" +#include "../include/sane/sanei_usb.h" +#include "../include/sane/sanei_tcp.h" +#include "../include/sane/sanei_udp.h" +#include "../include/sane/sanei_config.h" +#include "../include/sane/sanei_backend.h" + +#include "kodakaio.h" + +/* vendor and product ids that are allowed */ +#define SANE_KODAKAIO_VENDOR_ID (0x040a) + +#define min(x,y) (((x)<(y))?(x):(y)) + +/* I think these timeouts (ms) are defaults, overridden by any timeouts in the kodakaio.conf file */ +static int K_SNMP_Timeout = 3000; /* used for any auto detection method */ +static int K_Scan_Data_Timeout = 10000; +static int K_Request_Timeout = 5000; + +/* This file is used to store directly the raster returned by the scanner for debugging +If RawScanPath has no length it will not be created */ +FILE *RawScan = NULL; +/* example: unsigned char RawScanPath[] = "TestRawScan.pgm"; */ +char RawScanPath[] = ""; /* empty path means no raw scan file is made */ + +/* + * Devices supported by this backend +*/ + +/* kodak command strings */ +static unsigned char KodakEsp_V[] = {0x1b,'S','V',0,0,0,0,0}; /* version?? */ +static unsigned char KodakEsp_v[] = {0x1b,'s','v',0,0,0,0,0}; /* reply to version?? */ +static unsigned char KodakEsp_Lock[] = {0x1b,'S','L',0,0,0,0,0}; /* Locks scanner */ +static unsigned char KodakEsp_UnLock[] = {0x1b,'S','U',0,0,0,0,0}; /* Unlocks scanner */ +static unsigned char KodakEsp_Ack[] = {0x1b,'S','S',0,0,0,0,0}; /* Acknowledge for all commands */ +/* the bytes after esc S S 0 may indicate status: S S 0 1 = docs in adf */ +static unsigned char KodakEsp_F[] = {0x1b,'S','F',0,0,0,0,0}; /* Purpose not known? colour balance?*/ +static unsigned char KodakEsp_Comp[] = {0x1b,'S','C',3,8,3,0,0}; /* 3,8,3,1,0 does compression. */ +/* The compression method is unknown */ +/* static unsigned char KodakEsp_E[] = {0x1b,'S','E',1,0,0,0,0}; NET Purpose not known */ +/* the extra 1 below could be despeckle option? maybe only for Hero 9.1 but no errors with ESP5250 */ +static unsigned char KodakEsp_E[] = {0x1b,'S','E',1,1,0,0,0}; +static unsigned char KodakEsp_Go[] = {0x1b,'S','G',0,0,0,0,0}; /* Starts the scan */ +/* Other commands are: D (resolution), O (top left), Z (bottom right), R, G, B (curves) */ + +/* What is the relationship between these and the ranges in cap? */ +static SANE_Int kodakaio_resolution_list[] = {75, 150, 300, 600, 1200}; +static SANE_Int kodakaio_depth_list[] = {1,8}; /* The first value is the number of following entries */ + +/* strings to try and match the model ';' separator +static unsigned char SupportedMatchString[] = "KODAK ESP;KODAK HERO;KODAK OFFICE HERO;ADVENT WiFi AIO;"; */ + +static struct KodakaioCap kodakaio_cap[] = { +/* usbid,commandtype, modelname, USBoutEP, USBinEP, + opticalres, {dpi range}, pointer to res list, res list size + max depth, pointer to depth list, + flatbed x range, flatbed y range, + adf present, adf duplex, + adf x range, adf y range (y range should be set a little shorter than the paper being scanned) + +The following are not used but may be in future +commandtype, max depth, pointer to depth list +*/ + +/* list of cap data the first scanner is the default +*/ + /* KODAK AIO DEFAULT, */ + { + 0x9999, "esp", "KODAK AIO DEFAULT", + -1, 0x82, /* USBoutEP, USBinEP (-1 means find one) */ + 600, {75, 600, 0}, kodakaio_resolution_list, 4, /* 600 dpi max, 4 resolutions */ + 8, kodakaio_depth_list, /* color depth max 8, list above */ + {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(11.7 * MM_PER_INCH), 0}, /* FBF x/y ranges */ + SANE_FALSE, SANE_FALSE, /* ADF, duplex */ + {0, SANE_FIX(100), 0}, {0, SANE_FIX(100), 0} /* ADF x/y ranges (TODO!) */ + }, + /* KODAK ESP 5100, */ + { + 0x4025, "esp", "KODAK ESP 5100 AiO", + -1, 0x82, /* USBoutEP, USBinEP (-1 means find one) */ + 600, {75, 600, 0}, kodakaio_resolution_list, 4, /* 600 dpi max, 4 resolutions */ + 8, kodakaio_depth_list, /* color depth max 8, list above */ + {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(11.7 * MM_PER_INCH), 0}, /* FBF x/y ranges */ + SANE_FALSE, SANE_FALSE, /* ADF, duplex */ + {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(14 * MM_PER_INCH), 0} /* 14 ADF x/y ranges */ + }, + /* KODAK ESP 5300, */ + { + 0x4026, "esp", "KODAK ESP 5300 AiO", + -1, 0x82, /* USBoutEP, USBinEP (-1 means find one) */ + 600, {75, 600, 0}, kodakaio_resolution_list, 4, /* 600 dpi max, 4 resolutions */ + 8, kodakaio_depth_list, /* color depth max 8, list above */ + {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(11.7 * MM_PER_INCH), 0}, /* FBF x/y ranges */ + SANE_FALSE, SANE_FALSE, /* ADF, duplex */ + {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(14 * MM_PER_INCH), 0} /* 14 ADF x/y ranges */ + }, + /* KODAK ESP 5500, */ + { + 0x4027, "esp", "KODAK ESP 5500 AiO", + -1, 0x82, /* USBoutEP, USBinEP (-1 means find one) */ + 600, {75, 600, 0}, kodakaio_resolution_list, 4, /* 600 dpi max, 4 resolutions */ + 8, kodakaio_depth_list, /* color depth max 8, list above */ + {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(11.7 * MM_PER_INCH), 0}, /* FBF x/y ranges */ + SANE_FALSE, SANE_FALSE, /* ADF, duplex */ + {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(14 * MM_PER_INCH), 0} /* 14 ADF x/y ranges */ + }, + /* KODAK ESP 5000, */ + { + 0x4028, "esp", "KODAK ESP 5000 Series AiO", + -1, 0x82, /* USBoutEP, USBinEP (-1 means find one) */ + 600, {75, 600, 0}, kodakaio_resolution_list, 4, /* 600 dpi max, 4 resolutions */ + 8, kodakaio_depth_list, /* color depth max 8, list above */ + {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(11.7 * MM_PER_INCH), 0}, /* FBF x/y ranges */ + SANE_FALSE, SANE_FALSE, /* ADF, duplex */ + {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(14 * MM_PER_INCH), 0} /* 14 ADF x/y ranges */ + }, + /* KODAK ESP 3300, */ + { + 0x4031, "esp", "KODAK ESP 3300 Series AiO", + -1, 0x82, /* USBoutEP, USBinEP (-1 means find one) */ + 600, {75, 600, 0}, kodakaio_resolution_list, 4, /* 600 dpi max, 4 resolutions */ + 8, kodakaio_depth_list, /* color depth max 8, list above */ + {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(11.7 * MM_PER_INCH), 0}, /* FBF x/y ranges */ + SANE_FALSE, SANE_FALSE, /* ADF, duplex */ + {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(14 * MM_PER_INCH), 0} /* 14 ADF x/y ranges */ + }, + /* KODAK ESP5, */ + { + 0x4032, "esp", "KODAK ESP 5 AiO", + -1, 0x82, /* USBoutEP, USBinEP (-1 means find one) */ + 600, {75, 600, 0}, kodakaio_resolution_list, 4, /* 600 dpi max, 4 resolutions */ + 8, kodakaio_depth_list, /* color depth max 8, list above */ + {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(11.7 * MM_PER_INCH), 0}, /* FBF x/y ranges */ + SANE_FALSE, SANE_FALSE, /* ADF, duplex */ + {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(14 * MM_PER_INCH), 0} /* 14 ADF x/y ranges */ + }, + /* KODAK ESP7, */ + { + 0x403E, "esp", "KODAK ESP 7 AiO", + -1, 0x82, /* USBoutEP, USBinEP (-1 means find one) */ + 600, {75, 600, 0}, kodakaio_resolution_list, 4, /* 600 dpi max, 4 resolutions */ + 8, kodakaio_depth_list, /* color depth max 8, list above */ + {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(11.7 * MM_PER_INCH), 0}, /* FBF x/y ranges */ + SANE_FALSE, SANE_FALSE, /* ADF, duplex */ + {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(14 * MM_PER_INCH), 0} /* 14 ADF x/y ranges */ + }, + /* KODAK ESP9, */ + { + 0x403F, "esp", "KODAK ESP 9 AiO", + -1, 0x82, /* USBoutEP, USBinEP (-1 means find one) */ + 600, {75, 600, 0}, kodakaio_resolution_list, 4, /* 600 dpi max, 4 resolutions */ + 8, kodakaio_depth_list, /* color depth max 8, list above */ + {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(11.7 * MM_PER_INCH), 0}, /* FBF x/y ranges */ + SANE_FALSE, SANE_FALSE, /* ADF, duplex */ + {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(14 * MM_PER_INCH), 0} /* 14 ADF x/y ranges */ + }, + /* KODAK ESP5210 or 5250, */ + { + 0x4041, "esp", "KODAK ESP 5200 Series AiO", + -1, 0x82, /* USBoutEP, USBinEP (-1 means find one) */ + 600, {75, 600, 0}, kodakaio_resolution_list, 4, /* 600 dpi max, 4 resolutions */ + 8, kodakaio_depth_list, /* color depth max 8, list above */ + {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(11.7 * MM_PER_INCH), 0}, /* FBF x/y ranges */ + SANE_FALSE, SANE_FALSE, /* ADF, duplex */ + {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(14 * MM_PER_INCH), 0} /* 14 ADF x/y ranges */ + }, + /* KODAK ESP3200 , */ + { + 0x4043, "esp", "KODAK ESP 3200 Series AiO", + -1, 0x82, + 600, {75, 600, 0}, kodakaio_resolution_list, 4, /* 600 dpi max, 4 resolutions */ + 8, kodakaio_depth_list, /* color depth max 8, list above */ + {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(11.7 * MM_PER_INCH), 0}, /* FBF x/y ranges */ + SANE_FALSE, SANE_FALSE, /* ADF, duplex */ + {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(14 * MM_PER_INCH), 0} /* 14 ADF x/y ranges */ + }, + /* KODAK ESP4100 , */ + { + 0x4053, "esp", "KODAK ESP Office 4100 Series AiO", + -1, 0x82, + 600, {75, 600, 0}, kodakaio_resolution_list, 4, /* 600 dpi max, 4 resolutions */ + 8, kodakaio_depth_list, /* color depth max 8, list above */ + {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(11.7 * MM_PER_INCH), 0}, /* FBF x/y ranges */ + SANE_FALSE, SANE_FALSE, /* ADF, duplex */ + {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(14 * MM_PER_INCH), 0} /* 14 ADF x/y ranges */ + }, + /* KODAK ESP6100 , */ + { + 0x4054, "esp", "KODAK ESP Office 6100 Series AiO", + -1, 0x82, + 600, {75, 600, 0}, kodakaio_resolution_list, 4, /* 600 dpi max, 4 resolutions */ + 8, kodakaio_depth_list, /* color depth max 8, list above */ + {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(11.7 * MM_PER_INCH), 0}, /* FBF x/y ranges */ + SANE_TRUE, SANE_FALSE, /* ADF, duplex */ + {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(14 * MM_PER_INCH), 0} /* 14 ADF x/y ranges */ + }, + /* KODAK ESP7200 , */ + { + 0x4056, "esp", "KODAK ESP 7200 Series AiO", + -1, 0x82, + 600, {75, 600, 0}, kodakaio_resolution_list, 4, /* 600 dpi max, 4 resolutions */ + 8, kodakaio_depth_list, /* color depth max 8, list above */ + {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(11.7 * MM_PER_INCH), 0}, /* FBF x/y ranges */ + SANE_FALSE, SANE_FALSE, /* ADF, duplex */ + {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(14 * MM_PER_INCH), 0} /* 14 ADF x/y ranges */ + }, + /* KODAK ESP C110 , */ + { + 0x4057, "esp", "KODAK ESP C110 AiO", + -1, 0x82, + 600, {75, 600, 0}, kodakaio_resolution_list, 4, /* 600 dpi max, 4 resolutions */ + 8, kodakaio_depth_list, /* color depth max 8, list above */ + {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(11.7 * MM_PER_INCH), 0}, /* FBF x/y ranges */ + SANE_FALSE, SANE_FALSE, /* ADF, duplex */ + {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(14 * MM_PER_INCH), 0} /* 14 ADF x/y ranges */ + }, + /* KODAK ESP C115 , */ + { + 0x4058, "esp", "KODAK ESP C115 AiO", + -1, 0x82, + 600, {75, 600, 0}, kodakaio_resolution_list, 4, /* 600 dpi max, 4 resolutions */ + 8, kodakaio_depth_list, /* color depth max 8, list above */ + {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(11.7 * MM_PER_INCH), 0}, /* FBF x/y ranges */ + SANE_FALSE, SANE_FALSE, /* ADF, duplex */ + {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(14 * MM_PER_INCH), 0} /* 14 ADF x/y ranges */ + }, + /* KODAK ESP 2150 , */ + { + 0x4059, "esp", "KODAK ESP Office 2150 Series", + -1, 0x82, + 600, {75, 600, 0}, kodakaio_resolution_list, 4, /* 600 dpi max, 4 resolutions */ + 8, kodakaio_depth_list, /* color depth max 8, list above */ + {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(11.7 * MM_PER_INCH), 0}, /* FBF x/y ranges */ + SANE_TRUE, SANE_FALSE, /* ADF, duplex */ + {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(14 * MM_PER_INCH), 0} /* 14 ADF x/y ranges */ + }, + /* KODAK ESP C310 , */ + { + 0x405D, "esp", "KODAK ESP C310 AiO", + -1, 0x82, + 600, {75, 600, 0}, kodakaio_resolution_list, 4, /* 600 dpi max, 4 resolutions */ + 8, kodakaio_depth_list, /* color depth max 8, list above */ + {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(11.7 * MM_PER_INCH), 0}, /* FBF x/y ranges */ + SANE_FALSE, SANE_FALSE, /* ADF, duplex */ + {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(14 * MM_PER_INCH), 0} /* 14 ADF x/y ranges */ + }, + /* KODAK ESP C315 , */ + { + 0x405E, "esp", "KODAK ESP C315 AiO", + -1, 0x82, + 600, {75, 600, 0}, kodakaio_resolution_list, 4, /* 600 dpi max, 4 resolutions */ + 8, kodakaio_depth_list, /* color depth max 8, list above */ + {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(11.7 * MM_PER_INCH), 0}, /* FBF x/y ranges */ + SANE_FALSE, SANE_FALSE, /* ADF, duplex */ + {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(14 * MM_PER_INCH), 0} /* 14 ADF x/y ranges */ + }, + /* ADVENT AW10, */ + { + 0x4060, "esp", "ADVENT WiFi AIO AW10", + -1, 0x82, + 600, {75, 600, 0}, kodakaio_resolution_list, 4, /* 600 dpi max, {from, to, 0} 4 resolutions */ + 8, kodakaio_depth_list, /* color depth max 8, list above */ + {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(11.7 * MM_PER_INCH), 0}, /* FBF x/y ranges */ + SANE_TRUE, SANE_FALSE, /* ADF, duplex. */ + {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(14 * MM_PER_INCH), 0} /* 14 ADF x/y ranges */ + }, + /* KODAK HERO 6.1, */ + { + 0x4062, "esp", "KODAK OFFICE HERO 6.1 AiO", + -1, 0x82, + 600, {75, 600, 0}, kodakaio_resolution_list, 4, /* 600 dpi max, {from, to, 0} 4 resolutions */ + 8, kodakaio_depth_list, /* color depth max 8, list above */ + {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(11.7 * MM_PER_INCH), 0}, /* FBF x/y ranges */ + SANE_TRUE, SANE_FALSE, /* ADF, duplex. */ + {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(14 * MM_PER_INCH), 0} /* 14 ADF x/y ranges */ + }, + /* KODAK HERO 7.1, */ + { + 0x4063, "esp", "KODAK HERO 7.1 AiO", + -1, 0x82, + 600, {75, 600, 0}, kodakaio_resolution_list, 4, /* 600 dpi max, {from, to, 0} 4 resolutions */ + 8, kodakaio_depth_list, /* color depth max 8, list above */ + {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(11.7 * MM_PER_INCH), 0}, /* FBF x/y ranges */ + SANE_FALSE, SANE_TRUE, /* ADF, duplex. */ + {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(14 * MM_PER_INCH), 0} /* 14 ADF x/y ranges */ + }, + /* KODAK HERO 5.1, */ + { + 0x4064, "esp", "KODAK HERO 5.1 AiO", + -1, 0x82, + 600, {75, 600, 0}, kodakaio_resolution_list, 4, /* 600 dpi max, {from, to, 0} 4 resolutions */ + 8, kodakaio_depth_list, /* color depth max 8, list above */ + {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(11.7 * MM_PER_INCH), 0}, /* FBF x/y ranges */ + SANE_FALSE, SANE_TRUE, /* ADF, duplex.*/ + {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(14 * MM_PER_INCH), 0} /* 14 ADF x/y ranges */ + }, + /* KODAK ESP9200 , */ + { + 0x4065, "esp", "KODAK ESP 9200 Series AiO", + -1, 0x82, + 600, {75, 600, 0}, kodakaio_resolution_list, 4, /* 600 dpi max, 4 resolutions */ + 8, kodakaio_depth_list, /* color depth max 8, list above */ + {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(11.7 * MM_PER_INCH), 0}, /* FBF x/y ranges */ + SANE_TRUE, SANE_FALSE, /* ADF, duplex */ + {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(14 * MM_PER_INCH), 0} /* 14 ADF x/y ranges */ + }, + /* KODAK ESP2170 , */ + { + 0x4066, "esp", "KODAK ESP Office 2170 Series", + -1, 0x82, + 1200, {75, 1200, 0}, kodakaio_resolution_list, 5, /* 1200 dpi optical, {from, to, 0} 5 resolutions */ + 8, kodakaio_depth_list, /* color depth max 8, list above */ + {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(11.7 * MM_PER_INCH), 0}, /* FBF x/y ranges */ + SANE_TRUE, SANE_FALSE, /* ADF, duplex */ + {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(14 * MM_PER_INCH), 0} /* 14 ADF x/y ranges */ + }, + /* KODAK HERO 9.1, */ + { + 0x4067, "esp", "KODAK HERO 9.1 AiO", + -1, 0x82, + 1200, {75, 1200, 0}, kodakaio_resolution_list, 5, /* 1200 dpi optical, {from, to, 0} 5 resolutions */ + 8, kodakaio_depth_list, /* color depth max 8, list above */ + {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(11.7 * MM_PER_INCH), 0}, /* FBF x/y ranges */ + SANE_TRUE, SANE_FALSE, /* ADF, duplex. */ + {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(14 * MM_PER_INCH), 0} /* 14 ADF x/y ranges */ + }, + /* KODAK HERO 4.1, */ + { + 0x4069, "esp", "KODAK HERO 4.1 AiO", + -1, 0x82, + 600, {75, 600, 0}, kodakaio_resolution_list, 4, /* 600 dpi max, {from, to, 0} 4 resolutions */ + 8, kodakaio_depth_list, /* color depth max 8, list above */ + {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(11.7 * MM_PER_INCH), 0}, /* FBF x/y ranges */ + SANE_TRUE, SANE_FALSE, /* ADF, duplex.*/ + + {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(14 * MM_PER_INCH), 0} /* 14 ADF x/y ranges */ + }, + + /* KODAK HERO 3.1, */ + { + 0x406D, "esp", "KODAK HERO 3.1 AiO", + -1, 0x82, + 600, {75, 600, 0}, kodakaio_resolution_list, 4, /* 600 dpi max, {from, to, 0} 4 resolutions */ + 8, kodakaio_depth_list, /* color depth max 8, list above */ + {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(11.7 * MM_PER_INCH), 0}, /* FBF x/y ranges */ + SANE_FALSE, SANE_TRUE, /* ADF, duplex. */ + {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(14 * MM_PER_INCH), 0} /* 14 ADF x/y ranges */ + }, + /* spare use for specified usbid */ + { + 0, "esp", "specified", + -1, 0x82, + 600, {75, 600, 0}, kodakaio_resolution_list, 4, /* 600 dpi max, {from, to, 0} 4 resolutions */ + 8, kodakaio_depth_list, /* color depth max 8, list above */ + {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(11.7 * MM_PER_INCH), 0}, /* FBF x/y ranges */ + SANE_FALSE, SANE_TRUE, /* ADF, duplex. */ + {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(14 * MM_PER_INCH), 0} /* 14 ADF x/y ranges */ + } +}; + +/**************************************************************************** + * General configuration parameter definitions ****************************************************************************/ + + +/* + * Definition of the mode_param struct, that is used to + * specify the valid parameters for the different scan modes. + * + * The depth variable gets updated when the bit depth is modified. + */ + +static struct mode_param mode_params[] = { + /* {0x00, 1, 1}, // Lineart, 1 color, 1 bit */ + {0x02, 1, 8}, /* Grayscale, 1 color, 8 bit */ + {0x03, 3, 24} /* Color, 3 colors, 24 bit */ +}; + +static SANE_String_Const mode_list[] = { + /* SANE_VALUE_SCAN_MODE_LINEART, */ + SANE_VALUE_SCAN_MODE_GRAY, + SANE_VALUE_SCAN_MODE_COLOR, + NULL +}; + +static const SANE_String_Const adf_mode_list[] = { + SANE_I18N("Simplex"), + SANE_I18N("Duplex"), + NULL +}; + +/* Define the different scan sources */ +#define FBF_STR SANE_I18N("Flatbed") +#define ADF_STR SANE_I18N("Automatic Document Feeder") + +/* + * source list need one dummy entry (save device settings is crashing). + * NOTE: no const - this list gets created while exploring the capabilities + * of the scanner. Here space is reserved for 3 entries + NULL ? + */ + +static SANE_String_Const source_list[] = { + FBF_STR, + NULL, + NULL, + NULL +}; + +/* prototypes */ +static SANE_Status attach_one_usb(SANE_String_Const devname); +static SANE_Status attach_one_net(SANE_String_Const devname, unsigned int device); +void kodakaio_com_str(unsigned char *buf, char *fmt_buf); +int cmparray (unsigned char *array1, unsigned char *array2, size_t len); +static struct KodakaioCap *get_device_from_identification (const char *ident, const char *vid, const char *pid); +void ProcessAvahiDevice(const char *device_id, const char *vid, const char *pid, const char *ip_addr); + + +/* Some utility functions */ + +static size_t +max_string_size(const SANE_String_Const strings[]) +{ +/* returns the length of the longest string in an array of strings */ + size_t size, max_size = 0; + int i; + + for (i = 0; strings[i]; i++) { + size = strlen(strings[i]) + 1; + if (size > max_size) + max_size = size; + } + return max_size; +} + + + +static void +print_params(const SANE_Parameters params, int level) +{ + DBG(level, "formats: binary=?, grey=%d, colour=%d\n",SANE_FRAME_GRAY, SANE_FRAME_RGB ); + DBG(level, "params.format = %d\n", params.format); + DBG(level, "params.last_frame = %d\n", params.last_frame); + DBG(level, "params.bytes_per_line = %d\n", params.bytes_per_line); + DBG(level, "params.pixels_per_line = %d\n", params.pixels_per_line); + DBG(level, "params.lines = %d\n", params.lines); + DBG(level, "params.depth = %d\n", params.depth); +} + +static void +print_status(KodakAio_Scanner *s,int level) +{ + DBG(level, "print_status with level %d\n", level); + DBG(level, "s->bytes_unread = %d\n", s->bytes_unread); +/* + DBG(level, "params.last_frame = %d\n", params.last_frame); + DBG(level, "params.bytes_per_line = %d\n", params.bytes_per_line); + DBG(level, "params.pixels_per_line = %d\n", params.pixels_per_line); + DBG(level, "params.lines = %d\n", params.lines); + DBG(level, "params.depth = %d\n", params.depth); +*/ +} + +/**************************************************************************** + * Low-level Network communication functions ****************************************************************************/ + +/* We don't have a packet wrapper, which holds packet size etc., so we + don't have to use a *read_raw and a *_read function... */ +static int +kodakaio_net_read(struct KodakAio_Scanner *s, unsigned char *buf, size_t wanted, + SANE_Status * status) +{ + size_t size, read = 0; + struct pollfd fds[1]; + int pollreply; + + *status = SANE_STATUS_GOOD; + + /* poll for data-to-be-read (using K_Request_Timeout) */ + fds[0].fd = s->fd; + fds[0].events = POLLIN; + fds[0].revents = 0; + if ((pollreply = poll (fds, 1, K_Request_Timeout)) <= 0) { + if (pollreply ==0) + DBG(1, "net poll timeout\n"); + else + /* pollreply is -ve */ + DBG(1, "net poll error\n"); + *status = SANE_STATUS_IO_ERROR; + return read; + } + else if(fds[0].revents & POLLIN) { + while (read < wanted) { + size = sanei_tcp_read(s->fd, buf + read, wanted - read); + + if (size == 0) + break; + + read += size; + } + +/* this error removed 28/12/12 because adf scans end with less data than wanted + if (read < wanted) + *status = SANE_STATUS_IO_ERROR; + */ + DBG(32, "net read %d bytes:%x,%x,%x,%x,%x,%x,%x,%x\n",read,buf[0],buf[1],buf[2],buf[3],buf[4],buf[5],buf[6],buf[7]); + return read; + } + else + DBG(1, "Unknown problem with poll\n"); + return read; +} + +/* kodak does not pad commands like magicolor, so there's only a write_raw function */ +static int +sanei_kodakaio_net_write_raw(struct KodakAio_Scanner *s, + const unsigned char *buf, size_t buf_size, + SANE_Status *status) +{ + DBG(32, "net write:%x,%x,%x,%x,%x,%x,%x,%x\n",buf[0],buf[1],buf[2],buf[3],buf[4],buf[5],buf[6],buf[7]); + + sanei_tcp_write(s->fd, buf, buf_size); + /* TODO: Check whether sending failed... */ + + *status = SANE_STATUS_GOOD; + return buf_size; +} + +static SANE_Status +sanei_kodakaio_net_open(struct KodakAio_Scanner *s) +{ + struct timeval tv; + + tv.tv_sec = 5; + tv.tv_usec = 0; + + DBG(5, "%s\n", __func__); + + setsockopt(s->fd, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(tv)); + return SANE_STATUS_GOOD; +} + +static SANE_Status +sanei_kodakaio_net_close(struct KodakAio_Scanner *s) +{ + NOT_USED(s); + /* Does nothing - maybe should close the socket ? */ + return SANE_STATUS_GOOD; +} + + +/**************************************************************************** + * Low-level USB communication functions ****************************************************************************/ + +static int +kodakaio_getNumberOfUSBProductIds (void) +{ + return sizeof (kodakaio_cap) / sizeof (struct KodakaioCap); +} + +/**************************************************************************** + * low-level communication commands ****************************************************************************/ + +static void dump_hex_buffer_dense (int level, const unsigned char *buf, size_t buf_size) +{ + size_t k; + char msg[1024], fmt_buf[1024]; + memset (&msg[0], 0x00, 1024); + memset (&fmt_buf[0], 0x00, 1024); + for (k = 0; k < min(buf_size, 80); k++) { + if (k % 16 == 0) { + if (k>0) { + DBG (level, "%s\n", msg); + memset (&msg[0], 0x00, 1024); + } + sprintf (fmt_buf, " 0x%04lx ", (unsigned long)k); + strcat (msg, fmt_buf); + } + if (k % 8 == 0) { + strcat (msg, " "); + } + sprintf (fmt_buf, " %02x" , buf[k]); + strcat (msg, fmt_buf); + } + if (msg[0] != 0 ) { + DBG (level, "%s\n", msg); + } +} + +/* changing params to char seems to cause a stack problem */ +void kodakaio_com_str(unsigned char *buf, char *fmt_buf) +{ +/* returns a printable string version of the first 8 bytes assuming they are a kodakaio command*/ + if(buf[0] == 0x1b) { + sprintf (fmt_buf, "esc %c %c %02x %02x %02x %02x %02x", + buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]); + } + else { + sprintf (fmt_buf, "%02x %02x %02x %02x %02x %02x %02x %02x", + buf[0],buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]); + } +} + +static int +k_send(KodakAio_Scanner * s, void *buf, size_t buf_size, SANE_Status * status) +{ + char fmt_buf[25]; + + kodakaio_com_str(buf, fmt_buf); + DBG(15, "%s: size = %lu :%s\n", __func__, (u_long) buf_size, fmt_buf); + + if (DBG_LEVEL >= 125) { + const unsigned char *s = buf; + DBG(125, "complete buffer:\n"); + dump_hex_buffer_dense (125, s, buf_size); + } + + if (s->hw->connection == SANE_KODAKAIO_NET) { + return sanei_kodakaio_net_write_raw(s, buf, buf_size, status); + } else if (s->hw->connection == SANE_KODAKAIO_USB) { + size_t n; + n = buf_size; + *status = sanei_usb_write_bulk(s->fd, buf, &n); + DBG(50, "USB: wrote %lu bytes, status: %s\n", (unsigned long)n, sane_strstatus(*status)); + return n; + } + + *status = SANE_STATUS_INVAL; + return 0; +} + +static ssize_t +k_recv(KodakAio_Scanner * s, void *buf, ssize_t buf_size, + SANE_Status * status) +{ +/* requests and receives data this function makes the split between USB and NET +this function called by a number of others + +In USB mode, this function will wait until data is available for a maximum of SCANNER_READ_TIMEOUT seconds. +*/ + ssize_t n = 0; + char fmt_buf[25]; + time_t time_start; + time_t time_now; + struct timespec usb_delay, usb_rem; + usb_delay.tv_sec = 0; + usb_delay.tv_nsec = 300000000; /* 0.3 sec */ + + if (s->hw->connection == SANE_KODAKAIO_NET) { + + time(&time_start); + DBG(min(16,DBG_READ), "[%ld] %s: net req size = %ld ", (long) time_start, __func__, (long) buf_size); + n = kodakaio_net_read(s, buf, buf_size, status); + DBG(min(16,DBG_READ), "returned %d\n", n); + + } else if (s->hw->connection == SANE_KODAKAIO_USB) { + /* Start the clock for USB timeout */ + time(&time_start); + + /* Loop until we have data */ + while (n == 0) { + n = buf_size; +/* but what if the data is an exact number of blocks? */ + DBG(min(16,DBG_READ), "[%ld] %s: usb req size = %ld ", (long) time_start, __func__, (long) n); + *status = sanei_usb_read_bulk(s->fd, (SANE_Byte *) buf, (size_t *) & n); + DBG(min(16,DBG_READ), "returned %ld\n", (long) n); + + if(*status != SANE_STATUS_GOOD) { + + DBG(min(16,DBG_READ), "sanei_usb_read_bulk gave %s\n", sane_strstatus(*status)); + + if (*status == SANE_STATUS_EOF) { + /* If we have EOF status, wait for more data */ + time(&time_now); + if (difftime(time_now, time_start) < SCANNER_READ_TIMEOUT) { + nanosleep(&usb_delay, &usb_rem); + } + else { + /* Timeout */ + return n; + } + } + else { + /* If we've encountered another type of error, return */ + return n; + } + } + } + } + + if (n == 8) { + kodakaio_com_str(buf, fmt_buf); + DBG(min(14,DBG_READ), "%s: size = %ld, got %s\n", __func__, (long int)n, fmt_buf); + } + /* dump buffer if appropriate */ + if (DBG_LEVEL >= 127 && n > 0) { + const unsigned char* b=buf; + dump_hex_buffer_dense (125, b, buf_size); + } + return n; +} + + +static SANE_Status +kodakaio_expect_ack(KodakAio_Scanner *s, unsigned char *rxbuf) +/* gets 8 byte reply, checks reply is an Ack and returns appropriate status */ +{ + SANE_Status status; + + k_recv(s, rxbuf, 8, &status); + if (status != SANE_STATUS_GOOD) { + DBG(1, "%s: rx err, %s\n", __func__, sane_strstatus(status)); + return status; + } + /* strncmp ignores diffent possible responses like escSS00000 and escSS02000 */ + if (strncmp((char *)KodakEsp_Ack,(char *)rxbuf,4)!=0) { + DBG (1, "No Ack received, Expected 0x%2x %2x %2x %2x... got 0x%2x %2x %2x %2x...\n", + KodakEsp_Ack[0], KodakEsp_Ack[1], KodakEsp_Ack[2], KodakEsp_Ack[3],rxbuf[0], rxbuf[1], rxbuf[2], rxbuf[3]); + return SANE_STATUS_IO_ERROR; + } + + return status; +} + +static SANE_Status +kodakaio_txrx(KodakAio_Scanner *s, unsigned char *txbuf, unsigned char *rxbuf) +/* Sends 8 byte data to scanner and returns reply and appropriate status. */ +{ + SANE_Status status; + + k_send(s, txbuf, 8, &status); + if (status != SANE_STATUS_GOOD) { + DBG(1, "%s: tx err, %s\n", __func__, sane_strstatus(status)); + return status; + } + k_recv(s, rxbuf, 8, &status); + if (status != SANE_STATUS_GOOD) { + DBG(1, "%s: %s gave rx err, %s\n", __func__, "txvalue", sane_strstatus(status)); + return status; + } + return status; +} + +static SANE_Status +kodakaio_txrxack(KodakAio_Scanner *s, unsigned char *txbuf, unsigned char *rxbuf) +/* +Sends 8 byte data to scanner, gets 8 byte reply, checks reply is an Ack +and returns appropriate status +*/ +{ + SANE_Status status; + + k_send(s, txbuf, 8, &status); + if (status != SANE_STATUS_GOOD) { + DBG(1, "%s: tx err, %s\n", __func__, sane_strstatus(status)); + return status; + } + + k_recv(s, rxbuf, 8, &status); + if (status != SANE_STATUS_GOOD) { + DBG(1, "%s: %s gave rx err, %s\n", __func__, "txvalue", sane_strstatus(status)); + return status; + } + /* strncmp ignores different possible responses like escSS00000 and escSS02000 */ + if (strncmp((char *)KodakEsp_Ack,(char *)rxbuf,3) == 0) { /* was 4 byte comp */ + if (rxbuf[4] == 0x01 && s->adf_loaded == SANE_FALSE) { + s->adf_loaded = SANE_TRUE; + DBG(5, "%s: News - docs in ADF\n", __func__); + } + else if (rxbuf[4] != 0x01 && s->adf_loaded == SANE_TRUE) { + s->adf_loaded = SANE_FALSE; + DBG(5, "%s: News - ADF is empty\n", __func__); + } + } + else { + DBG (1, "No Ack received, Sent 0x%2x %2x %2x %2x... got 0x%2x %2x %2x %2x...\n", + txbuf[0], txbuf[1], txbuf[2], txbuf[3],rxbuf[0], rxbuf[1], rxbuf[2], rxbuf[3]); + return SANE_STATUS_IO_ERROR; + } + + return status; +} + +/* + * high-level communication commands +*/ + +static SANE_Status +k_hello (KodakAio_Scanner * s) +{ + SANE_Status status; + unsigned char reply[8]; + char fmt_buf[25]; + + DBG(5, "%s\n", __func__); + if((status = kodakaio_txrx(s, KodakEsp_V, reply))!= SANE_STATUS_GOOD) { + DBG(1, "%s: KodakEsp_V failure, %s\n", __func__, sane_strstatus(status)); + return SANE_STATUS_IO_ERROR; + } + + if (strncmp((char *) reply, (char *) KodakEsp_v, 3)!=0) { + kodakaio_com_str(reply, fmt_buf); + DBG(1, "%s: KodakEsp_v err, got %s\n", __func__, fmt_buf); + return SANE_STATUS_IO_ERROR; + } + DBG(5, "%s: OK %s\n", __func__, sane_strstatus(status)); + return status; +} + +/* Start scan command */ +static SANE_Status +cmd_start_scan (SANE_Handle handle, size_t expect_total) +/* expect_total is the expected total no of bytes just for info -ve value not a problem? or is it for size_t? */ +{ + KodakAio_Scanner *s = (KodakAio_Scanner *) handle; + SANE_Status status = SANE_STATUS_GOOD; + unsigned char reply[8]; +/*send the start command here */ + print_status(s, 5); +/*adf added 20/2/12, apparently an extra KodakEsp_F is sent when the adf is used */ + if (strcmp(source_list[s->val[OPT_SOURCE].w], ADF_STR) == 0) { /* adf is in use */ + if (! s->adf_loaded) return SANE_STATUS_CANCELLED; /* was SANE_STATUS_NO_DOCS; */ + if (kodakaio_txrxack(s, KodakEsp_F, reply)!= SANE_STATUS_GOOD) { + DBG(1, "%s: Did not get a good reply to KodakEsp_F\n", __func__); + return SANE_STATUS_IO_ERROR; + } + } + + if (kodakaio_txrxack(s, KodakEsp_E, reply)!= SANE_STATUS_GOOD) { + DBG(1, "%s: Did not get a good reply to KodakEsp_E\n", __func__); + return SANE_STATUS_IO_ERROR; + } + + DBG(20, "starting the scan, expected total bytes %d\n",expect_total); + k_send(s, KodakEsp_Go, 8, &status); + + if (status != SANE_STATUS_GOOD) + DBG(1, "%s: KodakEsp_Go command NOT successfully sent\n", __func__); + else { + DBG(30, "%s: KodakEsp_Go command successfully sent\n", __func__); + s->scanning = SANE_TRUE; + } + return status; +} + +static SANE_Status +cmd_cancel_scan (SANE_Handle handle) +{ + KodakAio_Scanner *s = (KodakAio_Scanner *) handle; + unsigned char reply[8]; +/* adf added 20/2/12 should it be adf? or adf with paper in? */ + if (strcmp(source_list[s->val[OPT_SOURCE].w], ADF_STR) == 0) { /* adf */ + if (kodakaio_txrxack(s, KodakEsp_F, reply)!= SANE_STATUS_GOOD) return SANE_STATUS_IO_ERROR; + if (kodakaio_txrxack(s, KodakEsp_UnLock, reply)!= SANE_STATUS_GOOD) return SANE_STATUS_IO_ERROR; + DBG(5, "%s unlocked the scanner with adf F U\n", __func__); + } + else { /* no adf */ + if (kodakaio_txrxack(s, KodakEsp_UnLock, reply)!= SANE_STATUS_GOOD) return SANE_STATUS_IO_ERROR; + DBG(5, "%s unlocked the scanner U\n", __func__); + } + s->scanning = SANE_FALSE; + return SANE_STATUS_GOOD; +} + +static SANE_Status +cmd_get_scanning_parameters(SANE_Handle handle, + SANE_Frame *format, SANE_Int *depth, + SANE_Int *data_pixels, SANE_Int *pixels_per_line, + SANE_Int *lines) +{ +/* data_pixels is per line. +Old mc cmd read this stuff from the scanner. I don't think kodak can do that easily */ + + KodakAio_Scanner *s = (KodakAio_Scanner *) handle; + SANE_Status status = SANE_STATUS_GOOD; + NOT_USED (format); + NOT_USED (depth); + + DBG(10, "%s\n", __func__); + + /* Calculate returned values */ + *lines = s->params.lines; + *pixels_per_line = s->params.pixels_per_line; + *data_pixels = s->params.pixels_per_line; + + DBG (20, "%s: data_pixels = %u, lines = %u, " + "pixels_per_line = %u)\n", __func__, + *data_pixels, *lines, *pixels_per_line); + return status; +} + +static SANE_Status +cmd_set_color_curve(SANE_Handle handle, unsigned char col) +{ +/* sends the color curve data for one color*/ + KodakAio_Scanner *s = (KodakAio_Scanner *) handle; + SANE_Status status = SANE_STATUS_GOOD; + unsigned char tx_col[8]; + unsigned char rx[8]; + unsigned char tx_curve[256]; + unsigned char i; + DBG(32, "%s: start\n", __func__); + tx_col[0]=0x1b; tx_col[1]='S'; tx_col[2]='K'; tx_col[3]=col; tx_col[4]=0; tx_col[5]=0; tx_col[6]=0; tx_col[7]=0; +/* linear curve now but could send tailor made curves in future */ + for(i=0;i<255;++i) tx_curve[i]=i; + k_send(s, tx_col, 8, &status); + if (status != SANE_STATUS_GOOD) { + DBG(1, "%s: tx err, %s\n", __func__, "curve command"); + return status; + } + k_send(s, tx_curve, 256, &status); + if (status != SANE_STATUS_GOOD) { + DBG(1, "%s: tx err, %s\n", __func__, "curve data"); + return status; + } + if (kodakaio_expect_ack(s, rx) != SANE_STATUS_GOOD) return SANE_STATUS_IO_ERROR; + DBG(10, "%s: sent curve OK, \n", __func__); + return status; +} + +/* Set scanning parameters command low level */ +static SANE_Status +cmd_set_scanning_parameters(SANE_Handle handle, + int resolution, + int tl_x, int tl_y, int width, int height, unsigned char source) + +/* NB. here int tl_x, int tl_y, int width, int height are in DPI units, not optres units! */ +/* sends params to scanner, but should we store them too? */ +{ + KodakAio_Scanner *s = (KodakAio_Scanner *) handle; + SANE_Status status = SANE_STATUS_GOOD; + unsigned char tx_S[8]; + unsigned char tx_dpi[8]; + unsigned char tx_topleft[8]; + unsigned char tx_widthheight[8]; + unsigned char bufread[8]; + int i; + +/*don't know the purpose of F yet. windows USB repeated 4 x why? does it affect the colour balance?*/ + DBG(8, "%s\n", __func__); + for(i=0;i<4;++i) { + kodakaio_txrxack(s, KodakEsp_F, bufread); + sleep(1); + } +/* kodakaio_txrxack(s, KodakEsp_F, bufread); for net, just once */ + +/* Source? bed /ADF */ + tx_S[0]=0x1b; tx_S[1]='S'; tx_S[2]='S'; tx_S[3]=source; tx_S[4]=0; tx_S[5]=0; tx_S[6]=0; tx_S[7]=0; + kodakaio_txrxack(s, tx_S, bufread); + +/* Compression */ + kodakaio_txrxack(s, KodakEsp_Comp, bufread); + +/* DPI resolution */ + tx_dpi[0]=0x1b; + tx_dpi[1]='S'; + tx_dpi[2]='D'; + tx_dpi[3]=resolution & 0xff; + tx_dpi[4]=(resolution >> 8) & 0xff; + tx_dpi[5]=resolution & 0xff; + tx_dpi[6]=(resolution >> 8) & 0xff; + tx_dpi[7]=0; + kodakaio_txrxack(s, tx_dpi, bufread); + +/* colour curves don't seem to be sent for usb preview +but it seems to do no harm to send them */ + cmd_set_color_curve(s, 'R'); + cmd_set_color_curve(s, 'G'); + cmd_set_color_curve(s, 'B'); + + +/* Origin top left s->tl_x and s->tl_y are in optres units +this command needs actual DPI units*/ + DBG(20, "%s: left (DPI)=%d, top (DPI)=%d\n", __func__, tl_x , tl_y); + + tx_topleft[0]=0x1b; + tx_topleft[1]='S'; + tx_topleft[2]='O'; + tx_topleft[3]=(tl_x) & 0xff; + tx_topleft[4]=((tl_x) >> 8) & 0xff; + tx_topleft[5]=(tl_y) & 0xff; + tx_topleft[6]=((tl_y) >> 8) & 0xff; + tx_topleft[7]=0; + kodakaio_txrxack(s, tx_topleft, bufread); + +/* Z width height note the s->width and s->height are in optres units +this command needs actual DPI units*/ + tx_widthheight[0]=0x1b; + tx_widthheight[1]='S'; + tx_widthheight[2]='Z'; + tx_widthheight[3]=(width) & 0xff; + tx_widthheight[4]=((width) >> 8) & 0xff; + tx_widthheight[5]=(height) & 0xff; + tx_widthheight[6]=((height) >> 8) & 0xff; + tx_widthheight[7]=0; + kodakaio_txrxack(s, tx_widthheight, bufread); + + if (status != SANE_STATUS_GOOD) + DBG(1, "%s: Data NOT successfully sent\n", __func__); + else + DBG(20, "%s: Data successfully sent\n", __func__); + return status; +} + +int +cmparray (unsigned char *array1, unsigned char *array2, size_t len) +{ +/* compares len bytes of the arrays returns 0 if they match +returns the first missmatch position if they don't match */ +unsigned int i; + for(i=0; i<len; ++i) + { + if(array1[i] != array2[i]) return -1; + } + return 0; +} + + + +static SANE_Status +cmd_read_data (SANE_Handle handle, unsigned char *buf, size_t *len) +{ +/* +cmd_read_data is only used in k_read. It reads one block of data +read data using k_recv until you get the ackstring +when you get the ackstring return s->ack and do padding if the padding option is selected +if no padding option return EOF +*/ + KodakAio_Scanner *s = (KodakAio_Scanner *) handle; + SANE_Status status; + int oldtimeout = K_Request_Timeout; + size_t bytecount; + unsigned char *Last8; /* will point to the last 8 chars in buf */ + int i, line, lines; + + if (s->ack && s->val[OPT_PADDING].w) { + /* do padding of whole block*/ + /* memset(buf, 0x80, *len); need to work out the background colour for this */ + lines = *len / s->params.bytes_per_line; + for (line=0; line < lines; ++line) { + for (i=0; i< s->params.pixels_per_line; ++i) { + buf[line * s->params.bytes_per_line + i] = s->background[0]; /*red */ + buf[line * s->params.bytes_per_line + s->params.pixels_per_line + i] = s->background[1]; /*green */ + buf[line * s->params.bytes_per_line + 2 * s->params.pixels_per_line + i] = s->background[2]; /*blue */ + } + } + s->bytes_unread -= *len; + if (s->bytes_unread < 0) + s->bytes_unread = 0; + return SANE_STATUS_GOOD; + } + + if (s->ack && !s->val[OPT_PADDING].w) { + s->bytes_unread = 0; + s->eof = SANE_TRUE; + return SANE_STATUS_EOF; + } + + /* Temporarily set the poll timeout long instead of short, + * because a color scan needs >5 seconds to initialize. Is this needed for kodak? maybe */ + K_Request_Timeout = K_Scan_Data_Timeout; + sanei_usb_set_timeout (K_Scan_Data_Timeout); + bytecount = k_recv(s, buf, *len, &status); + K_Request_Timeout = oldtimeout; + sanei_usb_set_timeout (oldtimeout); + +/* We may need to do some special thing with the block request size to cope with usb blocks of any length +in order to keep the ack bytes, when using adf, at the end of one block ie not split between blocks. +But it seems that the scanner takes care of that, and gives you the ack as a separate 8 byte block */ + + if (bytecount >= 8) { + /* it may be the last block from the scanner so look for Ack response in last 8 bytes */ + Last8 = buf + bytecount - 8; + + /* only compare 4 bytes because we sometimes get escSS02.. or escSS00.. + is 4 the right number ? */ + if (cmparray(Last8,KodakEsp_Ack,4) == 0) { + DBG(min(10,DBG_READ), "%s: found KodakEsp_Ack at %d bytes of %d\n", __func__, bytecount, *len); + s->ack = SANE_TRUE; + *len = bytecount - 8; /* discard the Ack response */ + s->bytes_unread -= *len; /* return a short block */ + } + else { + /* a not full buffer is returned usb does this */ + DBG(min(10,DBG_READ), "%s: buffer not full, got %d bytes of %d\n", __func__, bytecount, *len); + *len = bytecount; + s->bytes_unread -= bytecount; + } + } + else { + DBG(min(1,DBG_READ), "%s: tiny read, got %d bytes of %d\n", __func__, bytecount, *len); + return SANE_STATUS_IO_ERROR; + } + if (*len > s->params.bytes_per_line) { + /* store average colour as background. That's not the ideal method but it's easy to implement. */ + lines = *len / s->params.bytes_per_line; + s->background[0] = 0; + s->background[1] = 0; + s->background[2] = 0; + + for (line=0; line < lines; ++line) { + for (i=0; i< s->params.pixels_per_line; ++i) { + s->background[0] += buf[line * s->params.bytes_per_line + i]; /*red */ + s->background[1] += buf[line * s->params.bytes_per_line + s->params.pixels_per_line + i]; /*green */ + s->background[2] += buf[line * s->params.bytes_per_line + 2 * s->params.pixels_per_line + i]; /*blue */ + } + } + s->background[0] = s->background[0] / (lines * s->params.pixels_per_line); + s->background[1] = s->background[1] / (lines * s->params.pixels_per_line); + s->background[2] = s->background[2] / (lines * s->params.pixels_per_line); + + } + + if (status == SANE_STATUS_GOOD) + if (s->bytes_unread <= 0) + DBG(min(2,DBG_READ), "%s: Page fully read %d blocks, %ld bytes unread\n", __func__, s->counter, (long) s->bytes_unread); + else + DBG(min(20,DBG_READ), "%s: Image data successfully read %ld bytes, %ld bytes unread\n", __func__, (long) bytecount, (long) s->bytes_unread); + else if (s->ack) /* was (status == SANE_STATUS_EOF) */ + DBG(min(2,DBG_READ), "%s: scanner data read ended %d blocks %ld bytes, %ld bytes unread\n", __func__, s->counter, (long) bytecount, (long) s->bytes_unread); + else + DBG(min(1,DBG_READ), "%s: Image data read stopped with %s after %d blocks %ld bytes, %ld bytes unread\n", __func__, sane_strstatus(status), s->counter, (long) bytecount, (long) s->bytes_unread); + + return status; +} + + + +/**************************************************************************** + * kodakaio backend high-level operations ****************************************************************************/ + +static void +k_dev_init(Kodak_Device *dev, const char *devname, int conntype) +{ + DBG(5, "%s for %s\n", __func__,devname); + + dev->name = NULL; + dev->model = NULL; + dev->connection = conntype; + dev->sane.name = devname; + dev->sane.model = NULL; + dev->sane.type = "flatbed scanner"; + dev->sane.vendor = "Kodak"; + dev->cap = &kodakaio_cap[CAP_DEFAULT]; +} + + +static SANE_Status +k_set_model(KodakAio_Scanner * s, const char *model, size_t len) +{ + unsigned char *buf; + unsigned char *p; + struct Kodak_Device *dev = s->hw; + + if (len<1) return SANE_STATUS_INVAL; /* to handle missing model */ + + buf = malloc(len + 1); + if (buf == NULL) + return SANE_STATUS_NO_MEM; + + memcpy(buf, model, len); + buf[len] = '\0'; + + p = &buf[len - 1]; + + while (*p == ' ') { + *p = '\0'; + p--; + } + + if (dev->model) + free(dev->model); + + dev->model = strndup((const char *) buf, len); + dev->sane.model = dev->model; + DBG(10, "%s: model is '%s'\n", __func__, dev->model); + + free(buf); + + return SANE_STATUS_GOOD; +} + +static void +k_set_device (SANE_Handle handle, SANE_Word device) +{ + KodakAio_Scanner *s = (KodakAio_Scanner *) handle; + Kodak_Device *dev = s->hw; + int n; + + DBG(10, "%s: 0x%x\n", __func__, device); + + for (n = 0; n < NELEMS (kodakaio_cap); n++) { + if (kodakaio_cap[n].id == device) + break; + } + if (n < NELEMS(kodakaio_cap)) { + dev->cap = &kodakaio_cap[n]; + } else { + dev->cap = &kodakaio_cap[CAP_DEFAULT]; + DBG(1, " unknown device 0x%x, using default %s\n", + device, dev->cap->model); + } + k_set_model (s, dev->cap->model, strlen (dev->cap->model)); + +} + +static SANE_Status +k_discover_capabilities(KodakAio_Scanner *s) +{ + SANE_Status status = SANE_STATUS_GOOD; + Kodak_Device *dev = s->hw; + SANE_String_Const *source_list_add = source_list; + + DBG(10, "%s\n", __func__); + + /* always add flatbed */ + *source_list_add++ = FBF_STR; + /* TODO: How can I check for existence of an ADF??? */ + if (dev->cap->ADF == SANE_TRUE) { + *source_list_add++ = ADF_STR; + DBG(10, "%s: added adf to list\n", __func__); + } + + /* TODO: Is there any capability that we can extract from the + * device by some scanner command? So far, it looks like + * the device does not support any reporting. + */ + + dev->x_range = &dev->cap->fbf_x_range; + dev->y_range = &dev->cap->fbf_y_range; + + DBG(10, " x-range: %f %f\n", SANE_UNFIX(dev->x_range->min), SANE_UNFIX(dev->x_range->max)); + DBG(10, " y-range: %f %f\n", SANE_UNFIX(dev->y_range->min), SANE_UNFIX(dev->y_range->max)); + + DBG(5, "End of %s, status:%s\n", __func__, sane_strstatus(status)); + *source_list_add = NULL; /* add end marker to source list */ + return status; +} + +static SANE_Status +k_setup_block_mode (KodakAio_Scanner *s) +{ +/* works for USB and for net changing to make block size = a number of complete lines 28/12/12 */ + s->block_len = MAX_BLOCK_SIZE / s->scan_bytes_per_line * s->scan_bytes_per_line; + s->bytes_unread = s->data_len; + s->counter = 0; + s->bytes_read_in_line = 0; + if (s->line_buffer) + free(s->line_buffer); + s->line_buffer = malloc(s->scan_bytes_per_line); + if (s->line_buffer == NULL) { + DBG(1, "out of memory (line %d)\n", __LINE__); + return SANE_STATUS_NO_MEM; + } + + DBG (10, " %s: Setup block mode - scan_bytes_per_line=%d, pixels_per_line=%d, depth=%d, data_len=%d, block_len=%d, blocks=%d, last_len=%d\n", + __func__, s->scan_bytes_per_line, s->params.pixels_per_line, s->params.depth, s->data_len, s->block_len, s->blocks, s->last_len); + return SANE_STATUS_GOOD; +} + +/* Call the commands to set scanning parameters +In the Kodak Aio the parameters are: +(x1b,"S","F",0,0,0,0,0) +(x1b,"S","S",1,0,0,0,0) +#It looks like the 4th param byte of the C command is compression 1=compress, 0=no compression +#1st (or 3rd) param could be channels, 2nd could be bits per channel +(x1b,"S","C",3,8,3,0,0) #3,8,3,1,0) was what the kodak software used with compression +(x1b,"S","D",LowByte(Res),HighByte(Res),LowByte(Res),HighByte(Res),0) #resolution in DPI +SendColour(tcpCliSock,"R") +SendColour(tcpCliSock,"G") +SendColour(tcpCliSock,"B") +(x1b,"S","O",LowByte(x0*Res),HighByte(x0*Res),LowByte(y0*Res),HighByte(y0*Res),0) #top left in pixels +(x1b,"S","Z",LowByte(x1*Res),HighByte(x1*Res),LowByte(y1*Res),HighByte(y1*Res),0) #bot right in pixels +(x1b,"S","E",1,0,0,0,0) + +*/ + +static SANE_Status +k_lock_scanner (KodakAio_Scanner * s) +{ + SANE_Status status; + unsigned char reply[8]; + + status = k_hello(s); + if(status != SANE_STATUS_GOOD) { + DBG(1, "%s k_hello failed with %s\n", __func__, sane_strstatus(status)); + return status; + } + + if (kodakaio_txrxack(s, KodakEsp_Lock, reply)!= SANE_STATUS_GOOD) { + DBG(1, "%s Could not lock scanner\n", __func__); + return SANE_STATUS_IO_ERROR; + } + if (s->adf_loaded) DBG(5, "%s scanner locked, with docs in adf\n", __func__); + else DBG(5, "%s scanner locked, with no docs in adf\n", __func__); + return SANE_STATUS_GOOD; +} + +static SANE_Status +k_set_scanning_parameters(KodakAio_Scanner * s) +{ + SANE_Status status; + unsigned char rs, source; + SANE_Int scan_pixels_per_line = 0; + int dpi, optres; + + dpi = s->val[OPT_RESOLUTION].w; + optres = s->hw->cap->optical_res; + + /* Find the resolution in the res list and assign the index (rs) */ + for (rs=0; rs < s->hw->cap->res_list_size; rs++ ) { + if ( dpi == s->hw->cap->res_list[rs] ) + break; + } + + /* ADF used? */ + if (strcmp(source_list[s->val[OPT_SOURCE].w], ADF_STR) == 0) { + source = 0x00; + } else { + source = 0x01; + } + + + /* TODO: Any way to set PREVIEW??? */ + + /* Remaining bytes unused */ + status = cmd_set_scanning_parameters(s, dpi, + s->left * dpi / optres, s->top * dpi / optres, /* top/left start (dpi units)*/ + s->params.pixels_per_line, s->params.lines, /* extent was s->width, s->height*/ + source); /* source */ + + if (status != SANE_STATUS_GOOD) + DBG (1, "%s: Command cmd_set_scanning_parameters failed, %s\n", + __func__, sane_strstatus(status)); + + /* Now query the scanner for the current image parameters */ + status = cmd_get_scanning_parameters (s, + &s->params.format, &s->params.depth, + &scan_pixels_per_line, + &s->params.pixels_per_line, &s->params.lines); + if (status != SANE_STATUS_GOOD) { + DBG (1, "%s: Command cmd_get_scanning_parameters failed, %s\n", + __func__, sane_strstatus(status)); + return status; + } + + /* Calculate how many bytes are really used per line */ + s->params.bytes_per_line = ceil (s->params.pixels_per_line * s->params.depth / 8.0); + if (s->val[OPT_MODE].w == MODE_COLOR) + s->params.bytes_per_line *= 3; + + /* Calculate how many bytes per line will be returned by the scanner. + magicolor needed this because it uses padding. Scan bytes per line != image bytes per line + * The values needed for this are returned by get_scanning_parameters */ + s->scan_bytes_per_line = 3 * ceil (scan_pixels_per_line * s->params.depth / 8.0); + s->data_len = s->scan_bytes_per_line * floor (s->height * dpi / optres + 0.5); /* NB this is the length for a full scan */ + DBG (1, "Check: scan_bytes_per_line = %d s->params.bytes_per_line = %d \n", s->scan_bytes_per_line, s->params.bytes_per_line); + +/* k_setup_block_mode at the start of each page for adf to work */ + status = k_setup_block_mode (s); + if (status != SANE_STATUS_GOOD) + DBG (1, "%s: Command k_setup_block_mode failed, %s\n", + __func__, sane_strstatus(status)); + + DBG (18, "%s: bytes_read_in_line: %d\n", __func__, s->bytes_read_in_line); + return status; +} + +static SANE_Status +k_check_adf(KodakAio_Scanner * s) +{ +/* 20/2/12 detect paper in the adf? acknowledge esc S S 00 01.. ?*/ + + if (! s->adf_loaded) { + DBG(5, "%s: NO DOCS\n", __func__); + return SANE_STATUS_NO_DOCS; + } + else { + + /* TODO: Check for jam in ADF */ + DBG(5, "%s: DOCS IN ADF\n", __func__); + return SANE_STATUS_GOOD; + } +} + +static SANE_Status +k_scan_finish(KodakAio_Scanner * s) +{ + SANE_Status status = SANE_STATUS_GOOD; + DBG(10, "%s called\n", __func__); + + /* If we have not yet read all data, cancel the scan */ + if (s->buf && !s->eof) + status = cmd_cancel_scan (s); + + if (s->line_buffer) + free (s->line_buffer); + s->line_buffer = NULL; + free(s->buf); + s->buf = s->end = s->ptr = NULL; + + return status; +} + +static void +k_copy_image_data(KodakAio_Scanner * s, SANE_Byte * data, SANE_Int max_length, + SANE_Int * length) +/* copies the read data from s->line_buffer to the position in data pointer to by s->ptr +uncompressed data is RRRR...GGGG...BBBB per line */ +{ + SANE_Int bytes_available; + + DBG (min(18,DBG_READ), "%s: bytes_read in line: %d\n", __func__, s->bytes_read_in_line); + *length = 0; + + while ((max_length >= s->params.bytes_per_line) && (s->ptr < s->end)) { + SANE_Int bytes_to_copy = s->scan_bytes_per_line - s->bytes_read_in_line; + /* First, fill the line buffer for the current line: */ + bytes_available = (s->end - s->ptr); + /* Don't copy more than we have buffer and available */ + if (bytes_to_copy > bytes_available) + bytes_to_copy = bytes_available; + + if (bytes_to_copy > 0) { + memcpy (s->line_buffer + s->bytes_read_in_line, s->ptr, bytes_to_copy); + s->ptr += bytes_to_copy; + s->bytes_read_in_line += bytes_to_copy; + } + + /* We have filled as much as possible of the current line + * with data from the scanner. If we have a complete line, + * copy it over. + line points to the current byte in the input s->line_buffer + data points to the output buffer*/ + if ((s->bytes_read_in_line >= s->scan_bytes_per_line) && + (s->params.bytes_per_line <= max_length)) + { + SANE_Int i; + SANE_Byte *line = s->line_buffer; + *length += s->params.bytes_per_line; + + for (i=0; i< s->params.pixels_per_line; ++i) { + + if (s->val[OPT_MODE].w == MODE_COLOR){ + /*interlace */ + *data++ = 255-line[0]; /*red */ + *data++ = 255-line[s->params.pixels_per_line]; /*green */ + *data++ = 255-line[2 * s->params.pixels_per_line]; /*blue */ + + } + + else { /* grey */ + /*Average*/ + *data++ = (255-line[0] + +255-line[s->params.pixels_per_line] + +255-line[2 * s->params.pixels_per_line]) + / 3; + } + line++; + } +/*debug file The same for color or grey because the scan is colour */ + if (RawScan != NULL) { + for (i=0; i< s->scan_bytes_per_line; ++i) fputc(s->line_buffer[i],RawScan); + } + max_length -= s->params.bytes_per_line; + s->bytes_read_in_line -= s->scan_bytes_per_line; + } + } +} + +static SANE_Status +k_init_parametersta(KodakAio_Scanner * s) +{ + int dpi, optres; + /* struct mode_param *mparam; */ + + DBG(10, "%s\n", __func__); + + memset(&s->params, 0, sizeof(SANE_Parameters)); + + dpi = s->val[OPT_RESOLUTION].w; + optres = s->hw->cap->optical_res; + + /* mparam = &mode_params[s->val[OPT_MODE].w];does this get used? */ + + if (SANE_UNFIX(s->val[OPT_BR_Y].w) == 0 || + SANE_UNFIX(s->val[OPT_BR_X].w) == 0) + return SANE_STATUS_INVAL; + + /* TODO: Use OPT_RESOLUTION or fixed 600dpi for left/top/width/height? */ + s->left = ((SANE_UNFIX(s->val[OPT_TL_X].w) / MM_PER_INCH) * optres) + 0.5; + + s->top = ((SANE_UNFIX(s->val[OPT_TL_Y].w) / MM_PER_INCH) * optres) + 0.5; + + /* width in pixels */ + s->width = + ((SANE_UNFIX(s->val[OPT_BR_X].w - + s->val[OPT_TL_X].w) / MM_PER_INCH) * optres) + 0.5; + + s->height = ((SANE_UNFIX(s->val[OPT_BR_Y].w - + s->val[OPT_TL_Y].w) / MM_PER_INCH) * optres) + 0.5; + + DBG(20, "%s: s->width = %d, s->height = %d optres units\n", + __func__, s->width, s->height); + + s->params.pixels_per_line = s->width * dpi / optres + 0.5; + + /* ADF used without padding? added 30/12/12 */ + if (strcmp(source_list[s->val[OPT_SOURCE].w], ADF_STR) == 0 && !s->val[OPT_PADDING].w) + s->params.lines = -1; + else + s->params.lines = s->height * dpi / optres + 0.5; + + + DBG(20, "%s: resolution = %d, preview = %d\n", + __func__, dpi, s->val[OPT_PREVIEW].w); + + DBG(20, "%s: %p %p tlx %f tly %f brx %f bry %f [mm]\n", + __func__, (void *) s, (void *) s->val, + SANE_UNFIX(s->val[OPT_TL_X].w), SANE_UNFIX(s->val[OPT_TL_Y].w), + SANE_UNFIX(s->val[OPT_BR_X].w), SANE_UNFIX(s->val[OPT_BR_Y].w)); + + /* + * The default color depth is stored in mode_params.depth: + */ + if (mode_params[s->val[OPT_MODE].w].depth == 1) + s->params.depth = 1; + else { + DBG(20, "%s: setting depth = s->val[OPT_BIT_DEPTH].w = %d\n", __func__,s->val[OPT_BIT_DEPTH].w); + s->params.depth = s->val[OPT_BIT_DEPTH].w; + } + s->params.last_frame = SANE_TRUE; + s->params.bytes_per_line = 3 * ceil (s->params.depth * s->params.pixels_per_line / 8.0); + +/* kodak only scans in color and conversion to grey is done in the driver + s->params.format = SANE_FRAME_RGB; */ + DBG(20, "%s: s->val[OPT_MODE].w = %d (color is %d)\n", __func__,s->val[OPT_MODE].w, MODE_COLOR); + if (s->val[OPT_MODE].w == MODE_COLOR) s->params.format = SANE_FRAME_RGB; + else s->params.format = SANE_FRAME_GRAY; + + DBG(20, "%s: format=%d, bytes_per_line=%d, lines=%d\n", __func__, s->params.format, s->params.bytes_per_line, s->params.lines); + return (s->params.lines >= -1) ? SANE_STATUS_GOOD : SANE_STATUS_INVAL; +} + +static SANE_Status +k_start_scan(KodakAio_Scanner * s) +{ + SANE_Status status; + + status = cmd_start_scan (s, s->data_len); + if (status != SANE_STATUS_GOOD ) { + DBG (1, "%s: starting the scan failed (%s)\n", __func__, sane_strstatus(status)); + } + return status; +} + + + +static SANE_Status +k_read(struct KodakAio_Scanner *s) +{ + unsigned char rx[8]; + +/* monitors progress of blocks and calls cmd_read_data to get each block +you don't know how many blocks there will be in advance because their size may be determined by the scanner*/ + SANE_Status status = SANE_STATUS_GOOD; + size_t buf_len = 0; + + /* did we passed everything we read to sane? */ + if (s->ptr == s->end) { + + if (s->eof) + return SANE_STATUS_EOF; + + s->counter++; + + if (s->bytes_unread >= s->block_len) + buf_len = s->block_len; + else + buf_len = s->bytes_unread; + + DBG(min(20,DBG_READ), "%s: block %d, size %lu\n", __func__, + s->counter, (unsigned long) buf_len); + + /* receive image data + error code */ + status = cmd_read_data (s, s->buf, &buf_len); + if (status != SANE_STATUS_GOOD && status != SANE_STATUS_EOF) { /* was just GOOD 20/2/12 */ + DBG (1, "%s: Receiving image data failed (%s)\n", + __func__, sane_strstatus(status)); + cmd_cancel_scan(s); + return status; + } + + DBG(min(14,DBG_READ), "%s: success %lu bytes of block %d, %d remain\n", __func__, (unsigned long) buf_len, s->counter, s->bytes_unread); + + if (s->bytes_unread > 0) { + if (s->canceling) { + cmd_cancel_scan(s); + return SANE_STATUS_CANCELLED; + } + if (status == SANE_STATUS_EOF) { + /* page ended prematurely. */ + } + } + else { /* s->bytes_unread <=0 This is the end of a page */ + s->eof = SANE_TRUE; + DBG(min(10,DBG_READ), "%s: set EOF after %d blocks\n=============\n", __func__, s->counter); +/* look for the terminating ack if required */ + if (!s->ack) { + if (kodakaio_expect_ack(s, rx) == SANE_STATUS_GOOD) { + s->ack = SANE_TRUE; + } + else { + DBG(min(1,DBG_READ), "%s: Did not get expected ack at end of page\n", __func__); + return SANE_STATUS_IO_ERROR; + } + } + + } + + s->end = s->buf + buf_len; + s->ptr = s->buf; + } + else { + DBG(min(20,DBG_READ), "%s: data left in buffer\n", __func__); + } + return status; +} + +/* + * SANE API implementation (high-level functions) +*/ + +static struct KodakaioCap * +get_device_from_identification (const char *ident, const char *vid, const char *pid) +{ + int n; + SANE_Word pidnum, vidnum; + + if(sscanf(vid, "%x", &vidnum) == EOF) { + DBG(5, "could not convert hex vid <%s>\n", vid); + return NULL; + } + if(sscanf(pid, "%x", &pidnum) == EOF) { + DBG(5, "could not convert hex pid <%s>\n", pid); + return NULL; + } + for (n = 0; n < NELEMS (kodakaio_cap); n++) { + + if (strcmp (kodakaio_cap[n].model, ident)==0) { + DBG(20, "matched <%s> & <%s>\n", kodakaio_cap[n].model, ident); + return &kodakaio_cap[n]; + } + else + if (kodakaio_cap[n].id == pidnum && 0x040A == vidnum) { + DBG(20, "matched <%s> & <%s:%s>\n", kodakaio_cap[n].model, vid, pid); + return &kodakaio_cap[n]; + } + else { + DBG(20, "not found <%s> & <%s>\n", kodakaio_cap[n].model, pid); + } + } + return NULL; +} + + +/* + * close_scanner() + * + * Close the open scanner. Depending on the connection method, a different + * close function is called. + */ +static void +close_scanner(KodakAio_Scanner *s) +{ + DBG(7, "%s: fd = %d\n", __func__, s->fd); + + if (s->fd == -1) + return; + + k_scan_finish(s); + if (s->hw->connection == SANE_KODAKAIO_NET) { + sanei_kodakaio_net_close(s); + sanei_tcp_close(s->fd); + } else if (s->hw->connection == SANE_KODAKAIO_USB) { + sanei_usb_close(s->fd); + } + + s->fd = -1; +} + +static SANE_Bool +split_scanner_name (const char *name, char * IP, unsigned int *model) +{ + const char *device = name; + const char *qm; + *model = 0; + /* cut off leading net: */ + if (strncmp(device, "net:", 4) == 0) + device = &device[4]; + + qm = strchr(device, '?'); + if (qm != NULL) { + size_t len = qm-device; + strncpy (IP, device, len); + IP[len] = '\0'; + qm++; + if (strncmp(qm, "model=", 6) == 0) { + qm += 6; + if (!sscanf(qm, "0x%x", model)) + sscanf(qm, "%x", model); + } + } else { + strcpy (IP, device); + } + return SANE_TRUE; +} + +/* + * open_scanner() + * + * Open the scanner device. Depending on the connection method, + * different open functions are called. + */ + +static SANE_Status +open_scanner(KodakAio_Scanner *s) +{ + SANE_Status status = 0; + + DBG(7, "%s: %s\n", __func__, s->hw->sane.name); + + if (s->fd != -1) { + DBG(10, "scanner is already open: fd = %d\n", s->fd); + return SANE_STATUS_GOOD; /* no need to open the scanner */ + } + + if (s->hw->connection == SANE_KODAKAIO_NET) { + /* device name has the form net:ipaddr?model=... */ + char IP[1024]; + unsigned int model = 0; + if (!split_scanner_name (s->hw->sane.name, IP, &model)) + return SANE_STATUS_INVAL; + DBG(10, "split_scanner_name OK model=0x%x\n",model); +/* normal with IP */ + status = sanei_tcp_open(IP, 9101, &s->fd); /* (host,port,file pointer) */ + + if (status != SANE_STATUS_GOOD ) DBG(1, "Is network scanner switched on?\n"); + + if (model>0) + k_set_device (s, model); + if (status == SANE_STATUS_GOOD) { + status = sanei_kodakaio_net_open (s); + } +else DBG(1, "status was not good at net open\n"); + + + } else if (s->hw->connection == SANE_KODAKAIO_USB) { + DBG(7, "trying to open usb\n"); + status = sanei_usb_open(s->hw->sane.name, &s->fd); + if (s->hw->cap->out_ep>0) + sanei_usb_set_endpoint (s->fd, + USB_DIR_OUT | USB_ENDPOINT_TYPE_BULK, s->hw->cap->out_ep); + if (s->hw->cap->in_ep>0) + sanei_usb_set_endpoint (s->fd, + USB_DIR_IN | USB_ENDPOINT_TYPE_BULK, s->hw->cap->in_ep); + } + + if (status == SANE_STATUS_ACCESS_DENIED) { + DBG(1, "please check that you have permissions on the device.\n"); + DBG(1, "if this is a multi-function device with a printer,\n"); + DBG(1, "disable any conflicting driver (like usblp).\n"); + } + + if (status != SANE_STATUS_GOOD) + DBG(1, "%s open failed: %s\n", s->hw->sane.name, + sane_strstatus(status)); + else + DBG(3, "scanner opened\n"); +/* add check here of usb properties? */ +/*sanei_usb_get_descriptor( SANE_Int dn, struct sanei_usb_dev_descriptor *desc );*/ + + + return status; +} + +static SANE_Status +detect_usb(struct KodakAio_Scanner *s) +{ + SANE_Status status; + SANE_Word vendor, product; + int i, numIds; + SANE_Bool is_valid; + + /* if the sanei_usb_get_vendor_product call is not supported, + * then we just ignore this and rely on the user to config + * the correct device. + */ + + status = sanei_usb_get_vendor_product(s->fd, &vendor, &product); + if (status != SANE_STATUS_GOOD) { + DBG(1, "the device cannot be verified - will continue\n"); + return SANE_STATUS_GOOD; + } + + /* check the vendor ID to see if we are dealing with a kodak device */ + if (vendor != SANE_KODAKAIO_VENDOR_ID) { + /* this is not a supported vendor ID */ + DBG(1, "not a Kodak Aio device at %s (vendor id=0x%x)\n", + s->hw->sane.name, vendor); + return SANE_STATUS_INVAL; + } + + numIds = kodakaio_getNumberOfUSBProductIds(); + is_valid = SANE_FALSE; + i = 0; + + /* check all known product IDs to verify that we know + * about the device */ + while (i != numIds && !is_valid) { + /* if (product == kodakaio_usb_product_ids[i]) */ + if (product == kodakaio_cap[i].id) + is_valid = SANE_TRUE; + i++; + } + + if (is_valid == SANE_FALSE) { + DBG(1, "the device at %s is not a supported (product id=0x%x)\n", + s->hw->sane.name, product); + return SANE_STATUS_INVAL; + } + + DBG(2, "found valid usb Kodak Aio scanner: 0x%x/0x%x (vendorID/productID)\n", + vendor, product); + k_set_device(s, product); /* added 21/12/11 to try and get a name for the device */ + + return SANE_STATUS_GOOD; +} + +/* + * used by attach* and sane_get_devices + * a ptr to a single-linked list of Kodak_Device structs + * a ptr to a null term array of ptrs to SANE_Device structs + */ +static int num_devices; /* number of scanners attached to backend */ +static Kodak_Device *first_dev; /* first scanner in list */ +static const SANE_Device **devlist = NULL; + +static struct KodakAio_Scanner * +scanner_create(struct Kodak_Device *dev, SANE_Status *status) +{ + struct KodakAio_Scanner *s; + + s = malloc(sizeof(struct KodakAio_Scanner)); + if (s == NULL) { + *status = SANE_STATUS_NO_MEM; + return NULL; + } + + memset(s, 0x00, sizeof(struct KodakAio_Scanner)); + + s->fd = -1; + s->hw = dev; + + return s; +} + +static struct KodakAio_Scanner * +device_detect(const char *name, int type, SANE_Status *status) +{ + struct KodakAio_Scanner *s; + struct Kodak_Device *dev; + + /* try to find the device in our list */ + for (dev = first_dev; dev; dev = dev->next) { + if (strcmp(dev->sane.name, name) == 0) { + dev->missing = 0; + DBG (10, "%s: Device %s already attached!\n", __func__, + name); + return scanner_create(dev, status); + } + } + + if (type == SANE_KODAKAIO_NODEV) { + *status = SANE_STATUS_INVAL; + return NULL; + } + + /* alloc and clear our device structure */ + dev = malloc(sizeof(*dev)); + if (!dev) { + *status = SANE_STATUS_NO_MEM; + return NULL; + } + memset(dev, 0x00, sizeof(struct Kodak_Device)); + + s = scanner_create(dev, status); + if (s == NULL) + return NULL; + + k_dev_init(dev, name, type); + + *status = open_scanner(s); + if (*status != SANE_STATUS_GOOD) { + free(s); + free(dev); + return NULL; + } + + /* from now on, close_scanner() must be called */ + + /* USB requires special care */ + if (dev->connection == SANE_KODAKAIO_USB) { + *status = detect_usb(s); + } + + if (*status != SANE_STATUS_GOOD) + goto close; + + /* set name and model (if not already set) */ + if (dev->model == NULL) + k_set_model(s, "generic", 7); + + dev->name = strdup(name); + dev->sane.name = dev->name; + + /* do we need to discover capabilities here? */ + *status = k_discover_capabilities(s); + if (*status != SANE_STATUS_GOOD) + goto close; + + if (source_list[0] == NULL || dev->cap->dpi_range.min == 0) { + DBG(1, "something is wrong in the discovery process, aborting.\n"); + *status = SANE_STATUS_IO_ERROR; + goto close; + } + + /* add this scanner to the device list */ + num_devices++; + dev->missing = 0; + dev->next = first_dev; + first_dev = dev; + + return s; + + close: + close_scanner(s); + free(dev); + free(s); + return NULL; +} + + + +#if WITH_AVAHI +/* ProcessAvahiDevice is called to process each discovered device in turn */ +void +ProcessAvahiDevice(const char *device_id, const char *vid, const char *pid, const char *ip_addr) +{ + struct KodakaioCap *cap; + + DBG(min(10,DBG_AUTO),"device_id = <%s> vid:pid = <%s:%s>\n", device_id,vid,pid); + +/* check if it is a model likely to be supported: "KODAK ESP" or "KODAK HERO" + + DBG(min(10,DBG_AUTO),"look up model <%s>\n", device_model); */ + cap = get_device_from_identification("", vid, pid); + if (cap == NULL) { + return; + } + + DBG(min(10,DBG_AUTO), "%s: Found autodiscovered device: %s (type 0x%x)\n", __func__, cap->model, cap->id); + attach_one_net (ip_addr, cap->id); +} + + +static void resolve_callback( + AvahiServiceResolver *r, + AVAHI_GCC_UNUSED AvahiIfIndex interface, + AVAHI_GCC_UNUSED AvahiProtocol protocol, + AvahiResolverEvent event, + const char *name, + const char *type, + const char *domain, + const char *host_name, + const AvahiAddress *address, + uint16_t port, + AvahiStringList *txt, + AvahiLookupResultFlags flags, + AVAHI_GCC_UNUSED void* userdata) { + + char *pidkey, *pidvalue; + char *vidkey, *vidvalue; + size_t valuesize; + NOT_USED (flags); + + assert(r); + + /* Called whenever a service has been resolved successfully or timed out */ + + switch (event) { + case AVAHI_RESOLVER_FAILURE: + DBG(min(1,DBG_AUTO), "(Resolver) Failed to resolve service '%s' of type '%s' in domain '%s': %s\n", name, type, domain, avahi_strerror(avahi_client_errno(avahi_service_resolver_get_client(r)))); + break; + + case AVAHI_RESOLVER_FOUND: { + char a[AVAHI_ADDRESS_STR_MAX]; + + avahi_address_snprint(a, sizeof(a), address); + +/* Output short for Kodak ESP */ + DBG(min(10,DBG_AUTO), "%s:%u %s ", a,port,host_name); + avahi_string_list_get_pair(avahi_string_list_find(txt, "vid"), + &vidkey, &vidvalue, &valuesize); + DBG(min(10,DBG_AUTO), "%s=%s ", vidkey, vidvalue); + avahi_string_list_get_pair(avahi_string_list_find(txt, "pid"), + &pidkey, &pidvalue, &valuesize); + DBG(min(10,DBG_AUTO), "%s=%s\n", pidkey, pidvalue); + + ProcessAvahiDevice(name, vidvalue, pidvalue, a); + avahi_free(vidkey); avahi_free(vidvalue); + avahi_free(pidkey); avahi_free(pidvalue); + } + } + + avahi_service_resolver_free(r); +} + +static void browse_callback( + AvahiServiceBrowser *b, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiBrowserEvent event, + const char *name, + const char *type, + const char *domain, + AVAHI_GCC_UNUSED AvahiLookupResultFlags flags, + void* userdata) { + + AvahiClient *c = userdata; + assert(b); + + /* Called whenever a new services becomes available on the LAN or is removed from the LAN */ + switch (event) { + case AVAHI_BROWSER_FAILURE: + + DBG(min(1,DBG_AUTO), "(Browser) %s\n", avahi_strerror(avahi_client_errno(avahi_service_browser_get_client(b)))); + avahi_simple_poll_quit(simple_poll); + return; + + case AVAHI_BROWSER_NEW: + DBG(min(5,DBG_AUTO), "(Browser) NEW: service '%s' of type '%s' in domain '%s'\n", name, type, domain); + + /* We ignore the returned resolver object. In the callback + function we free it. If the server is terminated before + the callback function is called the server will free + the resolver for us. */ + + if (!(avahi_service_resolver_new(c, interface, protocol, name, type, domain, AVAHI_PROTO_UNSPEC, 0, resolve_callback, c))) + DBG(min(1,DBG_AUTO), "Failed to resolve service '%s': %s\n", name, avahi_strerror(avahi_client_errno(c))); + + break; + + case AVAHI_BROWSER_REMOVE: + DBG(min(1,DBG_AUTO), "(Browser) REMOVE: service '%s' of type '%s' in domain '%s'\n", name, type, domain); + break; + + case AVAHI_BROWSER_ALL_FOR_NOW: + case AVAHI_BROWSER_CACHE_EXHAUSTED: + DBG(min(5,DBG_AUTO), "(Browser) %s\n", event == AVAHI_BROWSER_CACHE_EXHAUSTED ? "CACHE_EXHAUSTED" : "ALL_FOR_NOW"); + break; + } +} + +static void client_callback(AvahiClient *c, AvahiClientState state, AVAHI_GCC_UNUSED void * userdata) { + assert(c); + + /* Called whenever the client or server state changes */ + + if (state == AVAHI_CLIENT_FAILURE) { + DBG(min(1,DBG_AUTO), "Server connection failure: %s\n", avahi_strerror(avahi_client_errno(c))); + avahi_simple_poll_quit(simple_poll); + } +} + + +static int +kodak_network_discovery(const char*host) +/* If host = NULL do autodiscovery. If host != NULL try to verify the model +First version only does autodiscovery */ +{ + AvahiClient *client = NULL; + AvahiServiceBrowser *sb = NULL; + int error; + int i, ret = 1; + NOT_USED(host); + + DBG(2, "%s: called\n", __func__); + + /* Allocate main loop object */ + if (!(simple_poll = avahi_simple_poll_new())) { + DBG(min(1,DBG_AUTO), "Failed to create simple poll object.\n"); + goto fail; + } + + /* Allocate a new client */ + client = avahi_client_new(avahi_simple_poll_get(simple_poll), 0, client_callback, NULL, &error); + + /* Check wether creating the client object succeeded */ + if (!client) { + DBG(min(1,DBG_AUTO), "Failed to create client: %s\n", avahi_strerror(error)); + goto fail; + } + + /* Create the service browser */ + if (!(sb = avahi_service_browser_new(client, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, "_scanner._tcp", NULL, 0, browse_callback, client))) { + DBG(min(1,DBG_AUTO), "Failed to create service browser: %s\n", avahi_strerror(avahi_client_errno(client))); + goto fail; + } + + /* Run the main loop */ + for(i=1;i<K_SNMP_Timeout/POLL_ITN_MS;++i) { + avahi_simple_poll_iterate(simple_poll,POLL_ITN_MS); + } + ret = 0; + +fail: + + + /* Cleanup things */ + DBG(min(10,DBG_AUTO), "Cleaning up avahi.\n"); + if (sb) + avahi_service_browser_free(sb); + + if (client) + avahi_client_free(client); + + if (simple_poll) + avahi_simple_poll_free(simple_poll); + + return ret; +} + +#endif + + +static SANE_Status +attach(const char *name, int type) +{ + SANE_Status status; + KodakAio_Scanner *s; + + DBG(7, "%s: devname = %s, type = %d\n", __func__, name, type); + + s = device_detect(name, type, &status); + if(s == NULL) + return status; + + close_scanner(s); + free(s); + return status; +} + +SANE_Status +attach_one_usb(const char *dev) +{ + DBG(7, "%s: dev = %s\n", __func__, dev); + return attach(dev, SANE_KODAKAIO_USB); +} + +static SANE_Status +attach_one_net(const char *dev, unsigned int model) +{ + char name[1024]; + + DBG(7, "%s: dev = %s\n", __func__, dev); + if (model > 0) { + snprintf(name, 1024, "net:%s?model=0x%x", dev, model); + } else { + snprintf(name, 1024, "net:%s", dev); + } + + return attach(name, SANE_KODAKAIO_NET); +} + +static SANE_Status +attach_one_config(SANEI_Config __sane_unused__ *config, const char *line) +{ + int vendor, product, timeout; + + int len = strlen(line); + + DBG(7, "%s: len = %d, line = %s\n", __func__, len, line); + + if (sscanf(line, "usb %i %i", &vendor, &product) == 2) { + /* add the vendor and product IDs to the list of + * known devices before we call the attach function */ + + int numIds = kodakaio_getNumberOfUSBProductIds(); + + if (vendor != SANE_KODAKAIO_VENDOR_ID) { + DBG(7, "Wrong vendor: numIds = %d, vendor = %d\n", numIds, vendor); + return SANE_STATUS_INVAL; /* this is not a Kodak device */ + } + /* kodakaio_usb_product_ids[numIds - 1] = product; */ + kodakaio_cap[numIds - 1].id = product; + + sanei_usb_attach_matching_devices(line, attach_one_usb); + + } else if (strncmp(line, "usb", 3) == 0 && len == 3) { + int i, numIds; + /* auto detect ? */ + numIds = kodakaio_getNumberOfUSBProductIds(); + + for (i = 0; i < numIds; i++) { +/* sanei_usb_find_devices(SANE_KODAKAIO_VENDOR_ID, + kodakaio_usb_product_ids[i], attach_one_usb); */ + sanei_usb_find_devices(SANE_KODAKAIO_VENDOR_ID, + kodakaio_cap[i].id, attach_one_usb); + } + + } else if (strncmp(line, "net", 3) == 0) { + + /* remove the "net" sub string */ + const char *name = sanei_config_skip_whitespace(line + 3); + char IP[1024]; + unsigned int model = 0; + + if (strncmp(name, "autodiscovery", 13) == 0) { + +#if WITH_AVAHI + DBG (30, "%s: Initiating network autodiscovery via avahi\n", __func__); + kodak_network_discovery(NULL); +#else + DBG (20, "%s: Network autodiscovery not done because not configured with avahi.\n", __func__); +#endif + + } else if (sscanf(name, "%s %x", IP, &model) == 2) { + DBG(30, "%s: Using network device on IP %s, forcing model 0x%x\n", __func__, IP, model); + attach_one_net(IP, model); + } else { + DBG(1, "%s: net entry %s may be a host name?\n", __func__, name); + attach_one_net(name, 0); + } + + } else if (sscanf(line, "snmp-timeout %i\n", &timeout)) { + /* Timeout for auto network discovery */ + DBG(50, "%s: network auto-discovery timeout set to %d\n", __func__, timeout); + K_SNMP_Timeout = timeout; + + } else if (sscanf(line, "scan-data-timeout %i\n", &timeout)) { + /* Timeout for scan data requests */ + DBG(50, "%s: Scan data timeout set to %d\n", __func__, timeout); + K_Scan_Data_Timeout = timeout; + + } else if (sscanf(line, "request-timeout %i\n", &timeout)) { + /* Timeout for all other read requests */ + DBG(50, "%s: Request timeout set to %d\n", __func__, timeout); + K_Request_Timeout = timeout; + + } else { + /* TODO: Warning about unparsable line! */ + } + + return SANE_STATUS_GOOD; +} + +static void +free_devices(void) +{ + Kodak_Device *dev, *next; + + DBG(5, "%s\n", __func__); + + for (dev = first_dev; dev; dev = next) { + next = dev->next; + free(dev->name); + free(dev->model); + free(dev); + } + + if (devlist) + free(devlist); + devlist = NULL; + first_dev = NULL; +} + +SANE_Status +sane_init(SANE_Int *version_code, SANE_Auth_Callback __sane_unused__ authorize) +{ + DBG_INIT(); + DBG(1, "========================================== \n"); + DBG(2, "%s: " PACKAGE " " VERSION "\n", __func__); + + DBG(1, "kodakaio backend, version %i.%i.%i\n", + KODAKAIO_VERSION, KODAKAIO_REVISION, KODAKAIO_BUILD); + DBG(2, "%s: called\n", __func__); + if (version_code != NULL) + *version_code = SANE_VERSION_CODE(SANE_CURRENT_MAJOR, V_MINOR, + KODAKAIO_BUILD); + sanei_usb_init(); + +#if WITH_AVAHI + DBG(min(3,DBG_AUTO), "avahi detected\n"); +#else + DBG(min(3,DBG_AUTO), "avahi not detected\n"); +#endif + return SANE_STATUS_GOOD; +} + +/* Clean up the list of attached scanners. */ +void +sane_exit(void) +{ + DBG(5, "%s\n", __func__); + free_devices(); +} + +SANE_Status +sane_get_devices(const SANE_Device ***device_list, SANE_Bool __sane_unused__ local_only) +{ + Kodak_Device *dev, *s, *prev=0; + int i; + + DBG(2, "%s: called\n", __func__); + + sanei_usb_init(); + + /* mark all existing scanners as missing, attach_one will remove mark */ + for (s = first_dev; s; s = s->next) { + s->missing = 1; + } + + /* Read the config, mark each device as found, possibly add new devs */ + sanei_configure_attach(KODAKAIO_CONFIG_FILE, NULL, + attach_one_config); + + /*delete missing scanners from list*/ + for (s = first_dev; s;) { + if (s->missing) { + DBG (5, "%s: missing scanner %s\n", __func__, s->name); + + /*splice s out of list by changing pointer in prev to next*/ + if (prev) { + prev->next = s->next; + free (s); + s = prev->next; + num_devices--; + } else { + /*remove s from head of list */ + first_dev = s->next; + free(s); + s = first_dev; + prev=NULL; + num_devices--; + } + } else { + prev = s; + s = prev->next; + } + } + + DBG (15, "%s: found %d scanner(s)\n", __func__, num_devices); + for (s = first_dev; s; s=s->next) { + DBG (15, "%s: found scanner %s\n", __func__, s->name); + } + + if (devlist) + free (devlist); + + devlist = malloc((num_devices + 1) * sizeof(devlist[0])); + if (!devlist) { + DBG(1, "out of memory (line %d)\n", __LINE__); + return SANE_STATUS_NO_MEM; + } + + DBG(5, "%s - results:\n", __func__); + + for (i = 0, dev = first_dev; i < num_devices && dev; dev = dev->next, i++) { + DBG(5, " %d (%d): %s\n", i, dev->connection, dev->model); + devlist[i] = &dev->sane; + } + + devlist[i] = NULL; + + if(device_list){ + *device_list = devlist; + } + + return SANE_STATUS_GOOD; +} + +static SANE_Status +init_options(KodakAio_Scanner *s) +{ + int i; + SANE_Word *res_list; + DBG(5, "%s: called\n", __func__); + + for (i = 0; i < NUM_OPTIONS; i++) { + s->opt[i].size = sizeof(SANE_Word); + s->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; + } + + s->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS; + s->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS; + s->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT; + s->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT; + s->val[OPT_NUM_OPTS].w = NUM_OPTIONS; + + /* "Scan Mode" group: */ + + s->opt[OPT_MODE_GROUP].name = SANE_NAME_STANDARD; + s->opt[OPT_MODE_GROUP].title = SANE_TITLE_STANDARD; + s->opt[OPT_MODE_GROUP].desc = SANE_DESC_STANDARD; + s->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP; + s->opt[OPT_MODE_GROUP].cap = 0; + + /* scan mode */ + s->opt[OPT_MODE].name = SANE_NAME_SCAN_MODE; + s->opt[OPT_MODE].title = SANE_TITLE_SCAN_MODE; + s->opt[OPT_MODE].desc = SANE_DESC_SCAN_MODE; + s->opt[OPT_MODE].type = SANE_TYPE_STRING; + s->opt[OPT_MODE].size = max_string_size(mode_list); + s->opt[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST; + s->opt[OPT_MODE].constraint.string_list = mode_list; + s->val[OPT_MODE].w = 0; /* Binary */ + DBG(20, "%s: mode_list has first entry %s\n", __func__, mode_list[0]); + + /* bit depth */ + s->opt[OPT_BIT_DEPTH].name = SANE_NAME_BIT_DEPTH; + s->opt[OPT_BIT_DEPTH].title = SANE_TITLE_BIT_DEPTH; + s->opt[OPT_BIT_DEPTH].desc = SANE_DESC_BIT_DEPTH; + s->opt[OPT_BIT_DEPTH].type = SANE_TYPE_INT; + s->opt[OPT_BIT_DEPTH].unit = SANE_UNIT_NONE; + s->opt[OPT_BIT_DEPTH].constraint_type = SANE_CONSTRAINT_WORD_LIST; + s->opt[OPT_BIT_DEPTH].constraint.word_list = s->hw->cap->depth_list; + s->opt[OPT_BIT_DEPTH].cap |= SANE_CAP_INACTIVE; + s->val[OPT_BIT_DEPTH].w = s->hw->cap->depth_list[1]; /* the first "real" element is the default */ + + DBG(20, "%s: depth list has depth_list[0] = %d entries\n", __func__, s->hw->cap->depth_list[0]); + if (s->hw->cap->depth_list[0] == 1) { /* only one element in the list -> hide the option */ + s->opt[OPT_BIT_DEPTH].cap |= SANE_CAP_INACTIVE; + DBG(20, "%s: Only one depth in list so inactive option\n", __func__); + } + + /* resolution */ + s->opt[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION; + s->opt[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION; + s->opt[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION; + s->opt[OPT_RESOLUTION].type = SANE_TYPE_INT; + s->opt[OPT_RESOLUTION].unit = SANE_UNIT_DPI; + s->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_WORD_LIST; + res_list = malloc((s->hw->cap->res_list_size + 1) * sizeof(SANE_Word)); + if (res_list == NULL) { + return SANE_STATUS_NO_MEM; + } + *(res_list) = s->hw->cap->res_list_size; + memcpy(&(res_list[1]), s->hw->cap->res_list, s->hw->cap->res_list_size * sizeof(SANE_Word)); + s->opt[OPT_RESOLUTION].constraint.word_list = res_list; + s->val[OPT_RESOLUTION].w = s->hw->cap->dpi_range.min; + + + /* preview */ + s->opt[OPT_PREVIEW].name = SANE_NAME_PREVIEW; + s->opt[OPT_PREVIEW].title = SANE_TITLE_PREVIEW; + s->opt[OPT_PREVIEW].desc = SANE_DESC_PREVIEW; + s->opt[OPT_PREVIEW].type = SANE_TYPE_BOOL; + s->val[OPT_PREVIEW].w = SANE_FALSE; + + for(i=0;source_list[i]!=NULL;++i) + DBG(18, "source_list: %s\n",source_list[i]); + + /* source */ + s->opt[OPT_SOURCE].name = SANE_NAME_SCAN_SOURCE; + s->opt[OPT_SOURCE].title = SANE_TITLE_SCAN_SOURCE; + s->opt[OPT_SOURCE].desc = SANE_DESC_SCAN_SOURCE; + s->opt[OPT_SOURCE].type = SANE_TYPE_STRING; + s->opt[OPT_SOURCE].size = max_string_size(source_list); + s->opt[OPT_SOURCE].constraint_type = SANE_CONSTRAINT_STRING_LIST; + s->opt[OPT_SOURCE].constraint.string_list = source_list; + s->val[OPT_SOURCE].w = 0; /* always use Flatbed as default */ + + if ((!s->hw->cap->ADF)) { + DBG(9, "device with no adf detected source option inactive\n"); + s->opt[OPT_SOURCE].cap |= SANE_CAP_INACTIVE; + } + +/* Are there any ESP scanners that are duplex? */ + s->opt[OPT_ADF_MODE].name = "adf-mode"; + s->opt[OPT_ADF_MODE].title = SANE_I18N("ADF Mode"); + s->opt[OPT_ADF_MODE].desc = + SANE_I18N("Selects the ADF mode (simplex/duplex)"); + s->opt[OPT_ADF_MODE].type = SANE_TYPE_STRING; + s->opt[OPT_ADF_MODE].size = max_string_size(adf_mode_list); + s->opt[OPT_ADF_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST; + s->opt[OPT_ADF_MODE].constraint.string_list = adf_mode_list; + s->val[OPT_ADF_MODE].w = 0; /* simplex */ + if ((!s->hw->cap->ADF) || (!s->hw->cap->adf_duplex)) + s->opt[OPT_ADF_MODE].cap |= SANE_CAP_INACTIVE; + + + /* "Geometry" group: */ + s->opt[OPT_GEOMETRY_GROUP].name = SANE_NAME_GEOMETRY; + s->opt[OPT_GEOMETRY_GROUP].title = SANE_TITLE_GEOMETRY; + s->opt[OPT_GEOMETRY_GROUP].desc = SANE_DESC_GEOMETRY; + s->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP; + s->opt[OPT_GEOMETRY_GROUP].cap = SANE_CAP_ADVANCED; + + /* top-left x */ + s->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X; + s->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X; + s->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X; + s->opt[OPT_TL_X].type = SANE_TYPE_FIXED; + s->opt[OPT_TL_X].unit = SANE_UNIT_MM; + s->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE; + s->opt[OPT_TL_X].constraint.range = s->hw->x_range; + s->val[OPT_TL_X].w = 0; + + /* top-left y */ + s->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y; + s->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y; + s->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y; + s->opt[OPT_TL_Y].type = SANE_TYPE_FIXED; + s->opt[OPT_TL_Y].unit = SANE_UNIT_MM; + s->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE; + s->opt[OPT_TL_Y].constraint.range = s->hw->y_range; + s->val[OPT_TL_Y].w = 0; + + /* bottom-right x */ + s->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X; + s->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X; + s->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X; + s->opt[OPT_BR_X].type = SANE_TYPE_FIXED; + s->opt[OPT_BR_X].unit = SANE_UNIT_MM; + s->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE; + s->opt[OPT_BR_X].constraint.range = s->hw->x_range; + s->val[OPT_BR_X].w = s->hw->x_range->max; + + /* bottom-right y */ + s->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y; + s->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y; + s->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y; + s->opt[OPT_BR_Y].type = SANE_TYPE_FIXED; + s->opt[OPT_BR_Y].unit = SANE_UNIT_MM; + s->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE; + s->opt[OPT_BR_Y].constraint.range = s->hw->y_range; + s->val[OPT_BR_Y].w = s->hw->y_range->max; + + /* padding short pages in adf */ + s->opt[OPT_PADDING].name = "adf-padding"; + s->opt[OPT_PADDING].title = "pad short adf pages"; + s->opt[OPT_PADDING].desc = "Selects whether to make short pages up to full length"; + s->opt[OPT_PADDING].type = SANE_TYPE_BOOL; + s->val[OPT_PADDING].w = SANE_FALSE; + if ((!s->hw->cap->ADF) || (strcmp(source_list[s->val[OPT_SOURCE].w], ADF_STR) != 0)) + { + DBG(9, "adf not source so padding option off and inactive\n"); + s->opt[OPT_PADDING].cap |= SANE_CAP_INACTIVE; + } + + return SANE_STATUS_GOOD; +} + +SANE_Status +sane_open(SANE_String_Const name, SANE_Handle *handle) +{ + SANE_Status status; + KodakAio_Scanner *s = NULL; + + int l = strlen(name); + + DBG(2, "%s: name = %s\n", __func__, name); + + /* probe if empty device name provided */ + if (l == 0) { + + status = sane_get_devices(NULL,0); + if (status != SANE_STATUS_GOOD) { + return status; + } + + if (first_dev == NULL) { + DBG(1, "no device detected\n"); + return SANE_STATUS_INVAL; + } + + s = device_detect(first_dev->sane.name, first_dev->connection, + &status); + if (s == NULL) { + DBG(1, "cannot open a perfectly valid device (%s)," + " please report to the authors\n", name); + return SANE_STATUS_INVAL; + } + + } else { + + if (strncmp(name, "net:", 4) == 0) { + s = device_detect(name, SANE_KODAKAIO_NET, &status); + if (s == NULL) + return status; + } else if (strncmp(name, "libusb:", 7) == 0) { + s = device_detect(name, SANE_KODAKAIO_USB, &status); + if (s == NULL) + return status; + } else { + + /* as a last resort, check for a match + * in the device list. This should handle platforms without libusb. + */ + if (first_dev == NULL) { + status = sane_get_devices(NULL,0); + if (status != SANE_STATUS_GOOD) { + return status; + } + } + + s = device_detect(name, SANE_KODAKAIO_NODEV, &status); + if (s == NULL) { + DBG(1, "invalid device name: %s\n", name); + return SANE_STATUS_INVAL; + } + } + } + + + /* s is always valid here */ + + DBG(10, "handle obtained\n"); + status = k_discover_capabilities(s); /* added 27/12/11 to fix source list problem +maybe we should only be rebuilding the source list here? */ + if (status != SANE_STATUS_GOOD) + return status; + + init_options(s); + + *handle = (SANE_Handle) s; + + status = open_scanner(s); + if (status != SANE_STATUS_GOOD) { + free(s); + return status; + } + + return status; +} + +void +sane_close(SANE_Handle handle) +{ + KodakAio_Scanner *s; + + /* + * XXX Test if there is still data pending from + * the scanner. If so, then do a cancel + */ + + s = (KodakAio_Scanner *) handle; + DBG(2, "%s: called\n", __func__); + + if (s->fd != -1) + close_scanner(s); + if(RawScan != NULL) + fclose(RawScan); + RawScan = NULL; + free(s); +} + +const SANE_Option_Descriptor * +sane_get_option_descriptor(SANE_Handle handle, SANE_Int option) +{ +/* this may be a sane call, but it happens way too often to have DBG level 2 */ + KodakAio_Scanner *s = (KodakAio_Scanner *) handle; + + DBG(20, "%s: called for option %d\n", __func__, option); + + if (option < 0 || option >= NUM_OPTIONS) + return NULL; + + return s->opt + option; +} + +static const SANE_String_Const * +search_string_list(const SANE_String_Const *list, SANE_String value) +{ + while (*list != NULL && strcmp(value, *list) != 0) + list++; + + return ((*list == NULL) ? NULL : list); +} + +/* + Activate, deactivate an option. Subroutines so we can add + debugging info if we want. The change flag is set to TRUE + if we changed an option. If we did not change an option, + then the value of the changed flag is not modified. +*/ + +static void +activateOption(KodakAio_Scanner *s, SANE_Int option, SANE_Bool *change) +{ + if (!SANE_OPTION_IS_ACTIVE(s->opt[option].cap)) { + s->opt[option].cap &= ~SANE_CAP_INACTIVE; + *change = SANE_TRUE; + } +} + +static void +deactivateOption(KodakAio_Scanner *s, SANE_Int option, SANE_Bool *change) +{ + if (SANE_OPTION_IS_ACTIVE(s->opt[option].cap)) { + s->opt[option].cap |= SANE_CAP_INACTIVE; + *change = SANE_TRUE; + } +} + +static SANE_Status +getvalue(SANE_Handle handle, SANE_Int option, void *value) +{ + KodakAio_Scanner *s = (KodakAio_Scanner *) handle; + SANE_Option_Descriptor *sopt = &(s->opt[option]); + Option_Value *sval = &(s->val[option]); + + DBG(17, "%s: option = %d\n", __func__, option); + + switch (option) { + + case OPT_NUM_OPTS: + case OPT_BIT_DEPTH: +/* case OPT_BRIGHTNESS: */ + case OPT_RESOLUTION: + case OPT_PREVIEW: + case OPT_TL_X: + case OPT_TL_Y: + case OPT_BR_X: + case OPT_BR_Y: + *((SANE_Word *) value) = sval->w; + break; + + case OPT_MODE: + case OPT_SOURCE: + case OPT_ADF_MODE: + strcpy((char *) value, sopt->constraint.string_list[sval->w]); + break; + case OPT_PADDING: + *((SANE_Bool *) value) = sval->w; + break; + + default: + return SANE_STATUS_INVAL; + } + + return SANE_STATUS_GOOD; +} + + +/* + * Handles setting the source (flatbed, or auto document feeder (ADF)). + * + */ + +static void +change_source(KodakAio_Scanner *s, SANE_Int optindex, char *value) +{ + int force_max = SANE_FALSE; + SANE_Bool dummy; + + DBG(5, "%s: optindex = %d, source = '%s'\n", __func__, optindex, + value); + + if (s->val[OPT_SOURCE].w == optindex) + return; + + s->val[OPT_SOURCE].w = optindex; + + if (s->val[OPT_TL_X].w == s->hw->x_range->min + && s->val[OPT_TL_Y].w == s->hw->y_range->min + && s->val[OPT_BR_X].w == s->hw->x_range->max + && s->val[OPT_BR_Y].w == s->hw->y_range->max) { + force_max = SANE_TRUE; + } + + if (strcmp(ADF_STR, value) == 0) { + s->hw->x_range = &s->hw->cap->adf_x_range; + s->hw->y_range = &s->hw->cap->adf_y_range; + if (s->hw->cap->adf_duplex) { + activateOption(s, OPT_ADF_MODE, &dummy); + } else { + deactivateOption(s, OPT_ADF_MODE, &dummy); + s->val[OPT_ADF_MODE].w = 0; + } + activateOption(s, OPT_PADDING, &dummy); + + DBG(5, "adf activated flag = %d\n",s->hw->cap->adf_duplex); + + } else { + /* ADF not active */ + s->hw->x_range = &s->hw->cap->fbf_x_range; + s->hw->y_range = &s->hw->cap->fbf_y_range; + + deactivateOption(s, OPT_ADF_MODE, &dummy); + deactivateOption(s, OPT_PADDING, &dummy); + } + + s->opt[OPT_BR_X].constraint.range = s->hw->x_range; + s->opt[OPT_BR_Y].constraint.range = s->hw->y_range; + + if (s->val[OPT_TL_X].w < s->hw->x_range->min || force_max) + s->val[OPT_TL_X].w = s->hw->x_range->min; + + if (s->val[OPT_TL_Y].w < s->hw->y_range->min || force_max) + s->val[OPT_TL_Y].w = s->hw->y_range->min; + + if (s->val[OPT_BR_X].w > s->hw->x_range->max || force_max) + s->val[OPT_BR_X].w = s->hw->x_range->max; + + if (s->val[OPT_BR_Y].w > s->hw->y_range->max || force_max) + s->val[OPT_BR_Y].w = s->hw->y_range->max; + +} + +static SANE_Status +setvalue(SANE_Handle handle, SANE_Int option, void *value, SANE_Int *info) +{ + KodakAio_Scanner *s = (KodakAio_Scanner *) handle; + SANE_Option_Descriptor *sopt = &(s->opt[option]); + Option_Value *sval = &(s->val[option]); + + SANE_Status status; + const SANE_String_Const *optval = NULL; + int optindex = 0; + SANE_Bool reload = SANE_FALSE; + + DBG(17, "%s: option = %d, value = %p, as word: %d\n", __func__, option, value, *(SANE_Word *) value); + + status = sanei_constrain_value(sopt, value, info); + if (status != SANE_STATUS_GOOD) + return status; + + if (info && value && (*info & SANE_INFO_INEXACT) + && sopt->type == SANE_TYPE_INT) + DBG(17, "%s: constrained val = %d\n", __func__, + *(SANE_Word *) value); + + if (sopt->constraint_type == SANE_CONSTRAINT_STRING_LIST) { + optval = search_string_list(sopt->constraint.string_list, + (char *) value); + if (optval == NULL) + return SANE_STATUS_INVAL; + optindex = optval - sopt->constraint.string_list; + } + + switch (option) { + + case OPT_MODE: + { + sval->w = optindex; + /* if binary, then disable the bit depth selection */ + if (optindex == 0) { + s->opt[OPT_BIT_DEPTH].cap |= SANE_CAP_INACTIVE; + } else { + if (s->hw->cap->depth_list[0] == 1) + s->opt[OPT_BIT_DEPTH].cap |= + SANE_CAP_INACTIVE; + else { + s->opt[OPT_BIT_DEPTH].cap &= + ~SANE_CAP_INACTIVE; + s->val[OPT_BIT_DEPTH].w = + mode_params[optindex].depth; + } + } + reload = SANE_TRUE; + break; + } + + case OPT_BIT_DEPTH: + sval->w = *((SANE_Word *) value); + mode_params[s->val[OPT_MODE].w].depth = sval->w; + reload = SANE_TRUE; + break; + + case OPT_RESOLUTION: + sval->w = *((SANE_Word *) value); + DBG(17, "setting resolution to %d\n", sval->w); + reload = SANE_TRUE; + break; + + case OPT_BR_X: + case OPT_BR_Y: + sval->w = *((SANE_Word *) value); + if (SANE_UNFIX(sval->w) == 0) { + DBG(17, "invalid br-x or br-y\n"); + return SANE_STATUS_INVAL; + } + /* passthru */ + case OPT_TL_X: + case OPT_TL_Y: + sval->w = *((SANE_Word *) value); + DBG(17, "setting size to %f\n", SANE_UNFIX(sval->w)); + if (NULL != info) + *info |= SANE_INFO_RELOAD_PARAMS; + break; + + case OPT_SOURCE: + change_source(s, optindex, (char *) value); + reload = SANE_TRUE; + break; + + case OPT_ADF_MODE: + sval->w = optindex; /* Simple lists */ + break; + + case OPT_PADDING: + sval->w = *((SANE_Word *) value); + break; + +/* case OPT_BRIGHTNESS: */ + case OPT_PREVIEW: /* needed? */ + sval->w = *((SANE_Word *) value); + break; + + default: + return SANE_STATUS_INVAL; + } + + if (reload && info != NULL) + *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS; + + DBG(17, "%s: end\n", __func__); + + return SANE_STATUS_GOOD; +} + +SANE_Status +sane_control_option(SANE_Handle handle, SANE_Int option, SANE_Action action, + void *value, SANE_Int *info) +{ + KodakAio_Scanner *s = (KodakAio_Scanner *) handle; + DBG(2, "%s: action = %x, option = %d %s\n", __func__, action, option, s->opt[option].name); + + if (option < 0 || option >= NUM_OPTIONS) + { + DBG(1, "%s: option num = %d (%s) out of range\n", __func__, option, s->opt[option].name); + return SANE_STATUS_INVAL; + } + + if (info != NULL) + *info = 0; + + switch (action) { + case SANE_ACTION_GET_VALUE: + return getvalue(handle, option, value); + + case SANE_ACTION_SET_VALUE: + return setvalue(handle, option, value, info); + + default: + return SANE_STATUS_INVAL; + } + + return SANE_STATUS_INVAL; +} + +SANE_Status +sane_get_parameters(SANE_Handle handle, SANE_Parameters *params) +{ + KodakAio_Scanner *s = (KodakAio_Scanner *) handle; + + DBG(2, "%s: called\n", __func__); + + if (params == NULL) + DBG(1, "%s: params is NULL\n", __func__); + + /* + * If sane_start was already called, then just retrieve the parameters + * from the scanner data structure + */ + + if (!s->eof && s->ptr != NULL) { + DBG(5, "scan in progress, returning saved params structure\n"); + } else { + /* otherwise initialize the params structure and gather the data */ + k_init_parametersta(s); + } + + if (params != NULL) + *params = s->params; + + print_params(s->params,20); + + return SANE_STATUS_GOOD; +} + +/* + * This function is part of the SANE API and gets called from the front end to + * start the scan process. + */ + +SANE_Status +sane_start(SANE_Handle handle) +{ + KodakAio_Scanner *s = (KodakAio_Scanner *) handle; + SANE_Status status; + + DBG(2, "%s: called\n", __func__); + if(! s->scanning) { + /* calc scanning parameters */ + status = k_init_parametersta(s); + if (status != SANE_STATUS_GOOD) + return status; + + /* set scanning parameters; also query the current image + * parameters from the sanner and save + * them to s->params +Only set scanning params the first time, or after a cancel +try change 22/2/12 take lock scanner out of k_set_scanning_parameters */ + status = k_lock_scanner(s); + if (status != SANE_STATUS_GOOD) { + DBG(1, "could not lock scanner\n"); + return status; + } + + } + + status = k_set_scanning_parameters(s); + if (status != SANE_STATUS_GOOD) + return status; + + print_params(s->params, 5); + /* if we scan from ADF, check if it is loaded */ + if (strcmp(source_list[s->val[OPT_SOURCE].w], ADF_STR) == 0) { + status = k_check_adf(s); + if (status != SANE_STATUS_GOOD) { +/* returning SANE_STATUS_NO_DOCS seems not to cause simple-scan to end the adf scan, so we cancel */ + status = SANE_STATUS_CANCELLED; + DBG(10, "%s: returning %s\n", __func__, sane_strstatus(status)); + return status; + } + } + + /* prepare buffer here so that a memory allocation failure + * will leave the scanner in a sane state. + */ + s->buf = realloc(s->buf, s->block_len); + if (s->buf == NULL) + return SANE_STATUS_NO_MEM; + + s->eof = SANE_FALSE; /* page not finished */ + s->ack = SANE_FALSE; /* page from scanner not finished */ + s->ptr = s->end = s->buf; + s->canceling = SANE_FALSE; + + if (strlen(RawScanPath) > 0 && s->params.lines > 0) + RawScan = fopen(RawScanPath, "wb");/* open the debug file if it has a name */ + if(RawScan) fprintf(RawScan, "P5\n%d %d\n%d\n",s->scan_bytes_per_line, s->params.lines, 255); + + /* start scanning */ + DBG(2, "%s: scanning...\n", __func__); + status = k_start_scan(s); + + if (status != SANE_STATUS_GOOD) { + DBG(1, "%s: start failed: %s\n", __func__, + sane_strstatus(status)); + + return status; + } + + return status; +} + +/* this moves data from our buffers to SANE */ + +SANE_Status +sane_read(SANE_Handle handle, SANE_Byte *data, SANE_Int max_length, + SANE_Int *length) +{ + SANE_Status status; + KodakAio_Scanner *s = (KodakAio_Scanner *) handle; + + if (s->buf == NULL || s->canceling) + return SANE_STATUS_CANCELLED; + + *length = 0; + DBG(18, "sane-read, bytes unread %d\n",s->bytes_unread); + + status = k_read(s); + + if (status == SANE_STATUS_CANCELLED) { + k_scan_finish(s); + return status; + } + + k_copy_image_data(s, data, max_length, length); + + DBG(18, "%d lines read, status: %s\n", + *length / s->params.bytes_per_line, sane_strstatus(status)); + + /* continue reading if appropriate */ + if (status == SANE_STATUS_GOOD) + return status; + +/* not sure if we want to finish (unlock scanner) here or in sane_close */ + /* k_scan_finish(s); */ + return status; +} + +/* + * void sane_cancel(SANE_Handle handle) + * + * Set the cancel flag to true. The next time the backend requests data + * from the scanner the CAN message will be sent. + */ + +void +sane_cancel(SANE_Handle handle) +{ + KodakAio_Scanner *s = (KodakAio_Scanner *) handle; + DBG(2, "%s: called\n", __func__); + +/* used to set cancelling flag to tell sane_read to cancel +changed 20/2/12 + s->canceling = SANE_TRUE; +*/ + cmd_cancel_scan(s); +} + +/* + * SANE_Status sane_set_io_mode() + * + * not supported - for asynchronous I/O + */ + +SANE_Status +sane_set_io_mode(SANE_Handle __sane_unused__ handle, + SANE_Bool __sane_unused__ non_blocking) +{ + return SANE_STATUS_UNSUPPORTED; +} + +/* + * SANE_Status sane_get_select_fd() + * + * not supported - for asynchronous I/O + */ + +SANE_Status +sane_get_select_fd(SANE_Handle __sane_unused__ handle, + SANE_Int __sane_unused__ *fd) +{ + return SANE_STATUS_UNSUPPORTED; +} |