summaryrefslogtreecommitdiff
path: root/backend/lexmark_x2600.c
diff options
context:
space:
mode:
authorJörg Frings-Fürst <debian@jff.email>2024-03-03 09:54:51 +0100
committerJörg Frings-Fürst <debian@jff.email>2024-03-03 09:54:51 +0100
commit44916ca6d75e0b5f258a098a50d659f31c6625fd (patch)
tree2e51a12ae43b3def9e55d3f2c9ca60d2032ad45c /backend/lexmark_x2600.c
parent84357741a6a6e6430f199b2c3f7498e0e97da9ad (diff)
New upstream version 1.3.0upstream/1.3.0upstream
Diffstat (limited to 'backend/lexmark_x2600.c')
-rw-r--r--backend/lexmark_x2600.c1287
1 files changed, 1287 insertions, 0 deletions
diff --git a/backend/lexmark_x2600.c b/backend/lexmark_x2600.c
new file mode 100644
index 0000000..610064e
--- /dev/null
+++ b/backend/lexmark_x2600.c
@@ -0,0 +1,1287 @@
+/* lexmark_x2600.c: SANE backend for Lexmark x2600 scanners.
+
+ (C) 2023 "Benoit Juin" <benoit.juin@gmail.com>
+
+ This file is part of the SANE package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+ As a special exception, the authors of SANE give permission for
+ additional uses of the libraries contained in this release of SANE.
+
+ The exception is that, if you link a SANE library with other files
+ to produce an executable, this does not by itself cause the
+ resulting executable to be covered by the GNU General Public
+ License. Your use of that executable is in no way restricted on
+ account of linking the SANE library code into it.
+
+ This exception does not, however, invalidate any other reasons why
+ the executable file might be covered by the GNU General Public
+ License.
+
+ If you submit changes to SANE to the maintainers to be included in
+ a subsequent release, you agree by submitting the changes that
+ those changes may be distributed with this exception intact.
+
+ If you write modifications of your own for SANE, it is your choice
+ whether to permit this exception to apply to your modifications.
+ If you do not wish that, delete this exception notice.
+
+ **************************************************************************/
+
+
+#include "lexmark_x2600.h"
+
+#define BUILD 1
+#define LEXMARK_X2600_CONFIG_FILE "lexmark_x2600.conf"
+#define MAX_OPTION_STRING_SIZE 255
+static SANE_Int transfer_buffer_size = 32768;
+static Lexmark_Device *first_device = 0;
+static SANE_Int num_devices = 0;
+static const SANE_Device **devlist = 0;
+
+static SANE_Bool initialized = SANE_FALSE;
+
+// first value is the size of the wordlist!
+static SANE_Int dpi_list[] = {
+ 4, 100, 200, 300, 600
+};
+static SANE_Int dpi_list_size = sizeof(dpi_list) / sizeof(dpi_list[0]);
+
+static SANE_String_Const mode_list[] = {
+ SANE_VALUE_SCAN_MODE_COLOR,
+ SANE_VALUE_SCAN_MODE_GRAY,
+ NULL
+};
+
+static SANE_Range x_range = {
+ 0, /* minimum */
+ 5078, /* maximum */
+ 1 /* quantization */
+};
+
+static SANE_Range y_range = {
+ 0, /* minimum */
+ 7015, /* maximum */
+ 1 /* quantization */
+};
+
+static SANE_Byte command1_block[] = {
+ 0xA5, 0x00, 0x19, 0x10, 0x01, 0x83, 0xAA, 0xBB,
+ 0xCC, 0xDD, 0x02, 0x00, 0x1B, 0x53, 0x03, 0x00,
+ 0x00, 0x00, 0x80, 0x00, 0xAA, 0xBB, 0xCC, 0xDD,
+ 0xAA, 0xBB, 0xCC, 0xDD};
+static SANE_Int command1_block_size = sizeof(command1_block);
+
+static SANE_Byte command2_block[] = {
+ 0xA5, 0x00, 0x19, 0x10, 0x01, 0x83, 0xAA, 0xBB,
+ 0xCC, 0xDD, 0x02, 0x00, 0x1B, 0x53, 0x04, 0x00,
+ 0x00, 0x00, 0x80, 0x00, 0xAA, 0xBB, 0xCC, 0xDD,
+ 0xAA, 0xBB, 0xCC, 0xDD};
+static SANE_Int command2_block_size = sizeof(command2_block);
+
+static SANE_Byte command_with_params_block[] = {
+ 0xA5, 0x00, 0x31, 0x10, 0x01, 0x83, 0xAA, 0xBB,
+ 0xCC, 0xDD, 0x02, 0x00, 0x1B, 0x53, 0x05, 0x00,
+ 0x18, 0x00, 0x80, 0x00, 0xFF, 0x00, 0x00, 0x02,
+ 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xAA, 0xBB, 0xCC, 0xDD,
+ 0xAA, 0xBB, 0xCC, 0xDD};
+static SANE_Int command_with_params_block_size = sizeof(command_with_params_block);
+
+static SANE_Byte command_cancel1_block[] = {
+ 0xa5, 0x00, 0x19, 0x10, 0x01, 0x83, 0xaa, 0xbb,
+ 0xcc, 0xdd, 0x02, 0x00, 0x1b, 0x53, 0x0f, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xaa, 0xbb, 0xcc, 0xdd,
+ 0xaa, 0xbb, 0xcc, 0xdd};
+static SANE_Byte command_cancel2_block[] = {
+ 0xa5, 0x00, 0x19, 0x10, 0x01, 0x83, 0xaa, 0xbb,
+ 0xcc, 0xdd, 0x02, 0x00, 0x1b, 0x53, 0x06, 0x00,
+ 0x00, 0x00, 0x80, 0x00, 0xaa, 0xbb, 0xcc, 0xdd,
+ 0xaa, 0xbb, 0xcc, 0xdd};
+static SANE_Int command_cancel_size = sizeof(command_cancel1_block);
+
+static SANE_Byte empty_line_data_packet[] = {
+ 0x1b, 0x53, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00};
+static SANE_Int empty_line_data_packet_size = sizeof(empty_line_data_packet);
+
+static SANE_Byte last_data_packet[] = {
+ 0x1b, 0x53, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x01};
+static SANE_Int last_data_packet_size = sizeof(last_data_packet);
+
+static SANE_Byte cancel_packet[] = {
+ 0x1b, 0x53, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x03};
+static SANE_Int cancel_packet_size = sizeof(cancel_packet);
+
+static SANE_Byte linebegin_data_packet[] = {
+ 0x1b, 0x53, 0x02, 0x00};
+static SANE_Int linebegin_data_packet_size = sizeof(linebegin_data_packet);
+
+static SANE_Byte unknown_a_data_packet[] = {
+ 0x1b, 0x53, 0x01, 0x00, 0x01, 0x00, 0x80, 0x00};
+static SANE_Int unknown_a_data_packet_size = sizeof(unknown_a_data_packet);
+
+static SANE_Byte unknown_b_data_packet[] = {
+ 0x1b, 0x53, 0x04, 0x00, 0x00, 0x00, 0x80, 0x00};
+static SANE_Int unknown_b_data_packet_size = sizeof(unknown_b_data_packet);
+
+static SANE_Byte unknown_c_data_packet[] = {
+ 0x1b, 0x53, 0x04, 0x00, 0x00, 0x00, 0x84, 0x00};
+static SANE_Int unknown_c_data_packet_size = sizeof(unknown_c_data_packet);
+
+static SANE_Byte unknown_d_data_packet[] = {
+ 0x1b, 0x53, 0x05, 0x00, 0x00, 0x00};
+static SANE_Int unknown_d_data_packet_size = sizeof(unknown_d_data_packet);
+
+static SANE_Byte unknown_e_data_packet[] = {
+ 0xa5, 0x00, 0x06, 0x10, 0x01, 0xaa, 0xbb, 0xcc,
+ 0xdd};
+static SANE_Int unknown_e_data_packet_size = sizeof(unknown_e_data_packet);
+
+/* static SANE_Byte not_ready_data_packet[] = { */
+/* 0x1b, 0x53, 0x01, 0x00, 0x01, 0x00, 0x84, 0x00}; */
+/* static SANE_Int not_ready_data_packet_size = sizeof(not_ready_data_packet); */
+
+
+static SANE_Int line_header_length = 9;
+
+
+//static SANE_Byte empty_data_packet[8] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
+
+SANE_Status
+clean_and_copy_data(const SANE_Byte * source, SANE_Int source_size,
+ SANE_Byte * destination, SANE_Int * destination_length,
+ SANE_Int mode, SANE_Int max_length, SANE_Handle dev)
+{
+ DBG (10, "clean_and_copy_data\n");
+ // if source doesnt start with 1b 53 02, then it is a continuation packet
+ // SANE_Int k = 0;
+ // SANE_Int bytes_written = 0;
+ // BW 1b 53 02 00 21 00 00 00 00 | 32 | 21 -> 33 (segmentlng= 32)
+ // BW 1b 53 02 00 41 00 00 00 00 | 64 | 41 -> 65 (segmentlng= 64)
+ // COLOR 1b 53 02 00 c1 00 00 00 00 | 64 | c1 -> 193 (segmentlng= 192)
+ // COLOR 1b 53 02 00 01 06 00 00 00 | 512 | 601 -> 1537 (segmentlng= 1536)
+ // COLOR 1b 53 02 00 99 3a 00 00 00 | 5000 | 3a99 -> 15001 (segmentlng=15000)
+ // COLOR 1b 53 02 00 f7 0f 00 | 1362 | 0ff7 -> 4087 <- limit where sane_read can a read a line at e time, more that 1362 and then the rest
+ // of the line will be available in the next sane_read call
+ // COLOR 1b 53 02 00 fa 0f 00 | | 0ffa -> 4090 <- in that case the line doesnt fit, clean_and_copy_data will be called again with the rest of the data
+
+
+ // edge case segment doesn(t feet in the packet size
+ /* if(segment_length > source_size - 9) */
+ /* segment_length = source_size - 9; */
+
+ // the scanner sends series of 8 lines function param source
+ // every lines has prefix see linebegin_data_packet
+ // the source parameter as a limited length :function param source_size
+ // so the serie og 8 lines can be splited
+ // in such case, in the next call of this function, source contain the end of the
+ // broken segment.
+ // Here is the way data is read:
+ // 1 - check that source begin with a linebegin_data_packet signature
+ // if this is the case the source[4] & source[5] contains how much data
+ // can be read before onother header is reach (linebegin_data_packet)
+
+ Lexmark_Device * ldev = (Lexmark_Device * ) dev;
+ SANE_Int i = 0;
+ SANE_Int bytes_read = 0;
+ SANE_Byte tmp = 0;
+ SANE_Int source_read_cursor = 0;
+ SANE_Int block_pixel_data_length = 0;
+ SANE_Int size_to_realloc = 0;
+
+
+ if(!ldev->eof){
+
+ // does source start with linebegin_data_packet?
+ if (memcmp(linebegin_data_packet, source, linebegin_data_packet_size) == 0){
+ // extract the number of bytes we can read befor new header is reached
+ // store it in the device in case of continuation packet
+ ldev->read_buffer->linesize = (source[4] + ((source[5] << 8) & 0xFF00)) - 1;
+ ldev->read_buffer->last_line_bytes_read = ldev->read_buffer->linesize;
+ DBG (10, " this is the begining of a line linesize=%ld\n",
+ ldev->read_buffer->linesize);
+ } else {
+ DBG (10, " this is not a new line packet, continue to fill the read buffer\n");
+ //return;
+ }
+
+ if(ldev->read_buffer->linesize == 0){
+ DBG (10, " linesize=0 something went wrong, lets ignore that USB packet\n");
+ return SANE_STATUS_CANCELLED;
+ }
+
+
+ // loop over source buffer
+ while(i < source_size){
+ // last line was full
+ if(ldev->read_buffer->last_line_bytes_read == ldev->read_buffer->linesize){
+ // if next block fit in the source
+ if(i + line_header_length + (SANE_Int) ldev->read_buffer->linesize <= source_size){
+ ldev->read_buffer->image_line_no += 1;
+ source_read_cursor = i + line_header_length;
+ block_pixel_data_length = ldev->read_buffer->linesize;
+ ldev->read_buffer->last_line_bytes_read = block_pixel_data_length;
+ size_to_realloc = ldev->read_buffer->image_line_no *
+ ldev->read_buffer->linesize * sizeof(SANE_Byte);
+ bytes_read = block_pixel_data_length + line_header_length;
+ }
+ // next block cannot be read fully because source_size is too small
+ // (USB packet fragmentation)
+ else{
+ ldev->read_buffer->image_line_no += 1;
+ source_read_cursor = i + line_header_length;
+ block_pixel_data_length = source_size - i - line_header_length;
+ ldev->read_buffer->last_line_bytes_read = block_pixel_data_length;
+ size_to_realloc = ((ldev->read_buffer->image_line_no-1) *
+ ldev->read_buffer->linesize + block_pixel_data_length) * sizeof(SANE_Byte);
+ bytes_read = block_pixel_data_length + line_header_length;
+ }
+ }
+ // last line was not full lets extract what is left
+ // this is du to USB packet fragmentation
+ else{
+ // the last line was not full so no increment
+ ldev->read_buffer->image_line_no += 0;
+ source_read_cursor = i;
+ block_pixel_data_length = ldev->read_buffer->linesize -
+ ldev->read_buffer->last_line_bytes_read;
+ // we completed the last line with missing bytes so new the line is full
+ ldev->read_buffer->last_line_bytes_read = ldev->read_buffer->linesize;
+ size_to_realloc = ldev->read_buffer->image_line_no *
+ ldev->read_buffer->linesize * sizeof(SANE_Byte);
+ bytes_read = block_pixel_data_length;
+ }
+
+ DBG (20, " size_to_realloc=%d i=%d image_line_no=%d\n",
+ size_to_realloc, i, ldev->read_buffer->image_line_no);
+ // do realoc memory space for our buffer
+ SANE_Byte* alloc_result = realloc(ldev->read_buffer->data, size_to_realloc);
+ if(alloc_result == NULL){
+ // TODO allocation was not possible
+ DBG (20, " REALLOC failed\n");
+ return SANE_STATUS_NO_MEM;
+ }
+ // point data to our new memary space
+ ldev->read_buffer->data = alloc_result;
+ // reposition writeptr and readptr to the correct memory adress
+ // to do that use write_byte_counter and read_byte_counter
+ ldev->read_buffer->writeptr =
+ ldev->read_buffer->data + ldev->read_buffer->write_byte_counter;
+ // copy new data
+ memcpy(
+ ldev->read_buffer->writeptr,
+ source + source_read_cursor,
+ block_pixel_data_length
+ );
+
+ // store how long is the buffer
+ ldev->read_buffer->write_byte_counter += block_pixel_data_length;
+
+ i += bytes_read;
+ }
+ }
+
+ // reposition our readptr
+ ldev->read_buffer->readptr =
+ ldev->read_buffer->data + ldev->read_buffer->read_byte_counter;
+
+
+ // read our buffer to fill the destination buffer
+ // mulitple call so read may has been already started
+ // length already read is stored in ldev->read_buffer->read_byte_counter
+
+ SANE_Int available_bytes_to_read =
+ ldev->read_buffer->write_byte_counter - ldev->read_buffer->read_byte_counter;
+
+ DBG (20, " source read done now sending to destination \n");
+
+ // we will copy image data 3 bytes by 3 bytes if color mod to allow color swap
+ // this avoid error on color channels swapping
+ if (mode == SANE_FRAME_RGB){
+
+ // get max chunk
+ SANE_Int data_chunk_size = max_length;
+ if(data_chunk_size > available_bytes_to_read){
+ data_chunk_size = available_bytes_to_read;
+ }
+ data_chunk_size = data_chunk_size / 3;
+ data_chunk_size = data_chunk_size * 3;
+
+ // we have to invert color channels
+ SANE_Byte * color_swarp_ptr = ldev->read_buffer->readptr;
+ for(SANE_Int j=0; j < data_chunk_size;j += 3){
+ // DBG (20, " swapping RGB <- BGR j=%d\n", j);
+ tmp = *(color_swarp_ptr + j);
+ *(color_swarp_ptr + j) = *(color_swarp_ptr + j + 2);
+ *(color_swarp_ptr + j + 2) = tmp;
+ }
+
+ memcpy (destination,
+ ldev->read_buffer->readptr,
+ data_chunk_size);
+
+ ldev->read_buffer->read_byte_counter += data_chunk_size;
+ *destination_length = data_chunk_size;
+
+ }
+ // gray mode copy until max_length
+ else{
+
+ SANE_Int data_chunk_size = max_length;
+ if(data_chunk_size > available_bytes_to_read){
+ data_chunk_size = available_bytes_to_read;
+ }
+ memcpy (
+ destination,
+ ldev->read_buffer->readptr,
+ data_chunk_size
+ );
+ ldev->read_buffer->read_byte_counter += data_chunk_size;;
+ *destination_length = data_chunk_size;
+
+ }
+
+ DBG (20, " done destination_length=%d available_bytes_to_read=%d\n",
+ *destination_length, available_bytes_to_read);
+
+ if(available_bytes_to_read > 0){
+ return SANE_STATUS_GOOD;
+ }else{
+ ldev->eof = 0;
+ return SANE_STATUS_EOF;
+ }
+
+}
+
+SANE_Status
+usb_write_then_read (Lexmark_Device * dev, SANE_Byte * cmd, size_t cmd_size)
+{
+ size_t buf_size = 256;
+ SANE_Byte buf[buf_size];
+ SANE_Status status;
+
+ DBG (10, "usb_write_then_read: %d\n", dev->devnum);
+ sanei_usb_set_endpoint(dev->devnum, USB_DIR_OUT|USB_ENDPOINT_TYPE_BULK, 0x02);
+ DBG (10, " endpoint set: %d\n", dev->devnum);
+
+ /* status = sanei_usb_read_bulk (dev->devnum, buf, &buf_size); */
+ /* DBG (10, " readdone: %d\n", dev->devnum); */
+ /* if (status != SANE_STATUS_GOOD && status != SANE_STATUS_EOF) */
+ /* { */
+ /* DBG (1, "USB READ IO Error in usb_write_then_read, fail devnum=%d\n", */
+ /* dev->devnum); */
+ /* return status; */
+ /* } */
+
+ DBG (10, " attempting to write...: %d\n", dev->devnum);
+ status = sanei_usb_write_bulk (dev->devnum, cmd, &cmd_size);
+ DBG (10, " writedone: %d\n", dev->devnum);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, "USB WRITE IO Error in usb_write_then_read, launch fail: %d\n",
+ status);
+ return status;
+ }
+
+ debug_packet(cmd, cmd_size, WRITE);
+
+ DBG (10, " attempting to read...: %d\n", dev->devnum);
+ status = sanei_usb_read_bulk (dev->devnum, buf, &buf_size);
+ DBG (10, " readdone: %d\n", dev->devnum);
+ if (status != SANE_STATUS_GOOD && status != SANE_STATUS_EOF)
+ {
+ DBG (1, "USB READ IO Error in usb_write_then_read, fail devnum=%d\n",
+ dev->devnum);
+ return status;
+ }
+ debug_packet(buf, buf_size, READ);
+ return SANE_STATUS_GOOD;
+}
+
+void
+build_packet(Lexmark_Device * dev, SANE_Byte packet_id, SANE_Byte * buffer){
+ memcpy(buffer, command_with_params_block, command_with_params_block_size);
+ // protocole related... "ID?"
+ buffer[14] = packet_id;
+
+ // mode
+ if (memcmp(dev->val[OPT_MODE].s, "Color", 5) == 0 )
+ buffer[20] = 0x03;
+ else
+ buffer[20] = 0x02;
+
+ // pixel width (swap lower byte -> higher byte)
+ buffer[24] = dev->val[OPT_BR_X].w & 0xFF;
+ buffer[25] = (dev->val[OPT_BR_X].w >> 8) & 0xFF;
+
+ // pixel height (swap lower byte -> higher byte)
+ buffer[28] = dev->val[OPT_BR_Y].w & 0xFF;
+ buffer[29] = (dev->val[OPT_BR_Y].w >> 8) & 0xFF;
+
+ // dpi x (swap lower byte -> higher byte)
+ buffer[40] = dev->val[OPT_RESOLUTION].w & 0xFF;
+ buffer[41] = (dev->val[OPT_RESOLUTION].w >> 8) & 0xFF;
+
+ // dpi y (swap lower byte -> higher byte)
+ buffer[42] = dev->val[OPT_RESOLUTION].w & 0xFF;
+ buffer[43] = (dev->val[OPT_RESOLUTION].w >> 8) & 0xFF;
+}
+
+SANE_Status
+init_options (Lexmark_Device * dev)
+{
+
+ SANE_Option_Descriptor *od;
+
+ DBG (2, "init_options: dev = %p\n", (void *) dev);
+
+ /* number of options */
+ od = &(dev->opt[OPT_NUM_OPTS]);
+ od->name = SANE_NAME_NUM_OPTIONS;
+ od->title = SANE_TITLE_NUM_OPTIONS;
+ od->desc = SANE_DESC_NUM_OPTIONS;
+ od->type = SANE_TYPE_INT;
+ od->unit = SANE_UNIT_NONE;
+ od->size = sizeof (SANE_Word);
+ od->cap = SANE_CAP_SOFT_DETECT;
+ od->constraint_type = SANE_CONSTRAINT_NONE;
+ od->constraint.range = 0;
+ dev->val[OPT_NUM_OPTS].w = NUM_OPTIONS;
+
+ /* mode - sets the scan mode: Color / Gray */
+ od = &(dev->opt[OPT_MODE]);
+ od->name = SANE_NAME_SCAN_MODE;
+ od->title = SANE_TITLE_SCAN_MODE;
+ od->desc = SANE_DESC_SCAN_MODE;;
+ od->type = SANE_TYPE_STRING;
+ od->unit = SANE_UNIT_NONE;
+ od->size = MAX_OPTION_STRING_SIZE;
+ od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT;
+ od->constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ od->constraint.string_list = mode_list;
+ dev->val[OPT_MODE].s = malloc (od->size);
+ if (!dev->val[OPT_MODE].s)
+ return SANE_STATUS_NO_MEM;
+ strcpy (dev->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_COLOR);
+
+ /* resolution */
+ od = &(dev->opt[OPT_RESOLUTION]);
+ od->name = SANE_NAME_SCAN_RESOLUTION;
+ od->title = SANE_TITLE_SCAN_RESOLUTION;
+ od->desc = SANE_DESC_SCAN_RESOLUTION;
+ od->type = SANE_TYPE_INT;
+ od->unit = SANE_UNIT_DPI;
+ od->size = sizeof (SANE_Int);
+ od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT;
+ od->constraint_type = SANE_CONSTRAINT_WORD_LIST;
+ od->constraint.word_list = dpi_list;
+ dev->val[OPT_RESOLUTION].w = 200;
+
+ /* preview mode */
+ od = &(dev->opt[OPT_PREVIEW]);
+ od->name = SANE_NAME_PREVIEW;
+ od->title = SANE_TITLE_PREVIEW;
+ od->desc = SANE_DESC_PREVIEW;
+ od->size = sizeof (SANE_Word);
+ od->cap = SANE_CAP_INACTIVE;
+ od->type = SANE_TYPE_BOOL;
+ od->constraint_type = SANE_CONSTRAINT_NONE;
+ dev->val[OPT_PREVIEW].w = SANE_FALSE;
+
+ /* "Geometry" group: */
+ od = &(dev->opt[OPT_GEOMETRY_GROUP]);
+ od->name = "";
+ od->title = SANE_I18N ("Geometry");
+ od->desc = "";
+ od->type = SANE_TYPE_GROUP;
+ od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT;
+ od->size = 0;
+ od->constraint_type = SANE_CONSTRAINT_NONE;
+ //
+
+ /* top-left x */
+ od = &(dev->opt[OPT_TL_X]);
+ od->name = SANE_NAME_SCAN_TL_X;
+ od->title = SANE_TITLE_SCAN_TL_X;
+ od->desc = SANE_DESC_SCAN_TL_X;
+ od->type = SANE_TYPE_INT;
+ od->cap = SANE_CAP_INACTIVE;
+ od->size = sizeof (SANE_Word);
+ od->unit = SANE_UNIT_PIXEL;
+ od->constraint_type = SANE_CONSTRAINT_RANGE;
+ od->constraint.range = &x_range;
+ dev->val[OPT_TL_X].w = 0;
+
+ /* top-left y */
+ od = &(dev->opt[OPT_TL_Y]);
+ od->name = SANE_NAME_SCAN_TL_Y;
+ od->title = SANE_TITLE_SCAN_TL_Y;
+ od->desc = SANE_DESC_SCAN_TL_Y;
+ od->type = SANE_TYPE_INT;
+ od->cap = SANE_CAP_INACTIVE;
+ od->size = sizeof (SANE_Word);
+ od->unit = SANE_UNIT_PIXEL;
+ od->constraint_type = SANE_CONSTRAINT_RANGE;
+ od->constraint.range = &y_range;
+ dev->val[OPT_TL_Y].w = 0;
+
+ /* bottom-right x */
+ od = &(dev->opt[OPT_BR_X]);
+ od->name = SANE_NAME_SCAN_BR_X;
+ od->title = SANE_TITLE_SCAN_BR_X;
+ od->desc = SANE_DESC_SCAN_BR_X;
+ od->type = SANE_TYPE_INT;
+ od->size = sizeof (SANE_Word);
+ od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT;
+ od->unit = SANE_UNIT_PIXEL;
+ od->constraint_type = SANE_CONSTRAINT_RANGE;
+ od->constraint.range = &x_range;
+ dev->val[OPT_BR_X].w = 1654;
+
+ /* bottom-right y */
+ od = &(dev->opt[OPT_BR_Y]);
+ od->name = SANE_NAME_SCAN_BR_Y;
+ od->title = SANE_TITLE_SCAN_BR_Y;
+ od->desc = SANE_DESC_SCAN_BR_Y;
+ od->type = SANE_TYPE_INT;
+ od->size = sizeof (SANE_Word);
+ od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT;
+ od->unit = SANE_UNIT_PIXEL;
+ od->constraint_type = SANE_CONSTRAINT_RANGE;
+ od->constraint.range = &y_range;
+ dev->val[OPT_BR_Y].w = 2339;
+
+ return SANE_STATUS_GOOD;
+}
+
+/* callback function for sanei_usb_attach_matching_devices
+*/
+static SANE_Status
+attach_one (SANE_String_Const devname)
+{
+ Lexmark_Device *lexmark_device;
+
+ DBG (2, "attach_one: attachLexmark: devname=%s first_device=%p\n",
+ devname, (void *)first_device);
+
+ for (lexmark_device = first_device; lexmark_device;
+ lexmark_device = lexmark_device->next){
+ /* already attached devices */
+
+ if (strcmp (lexmark_device->sane.name, devname) == 0){
+ lexmark_device->missing = SANE_FALSE;
+ return SANE_STATUS_GOOD;
+ }
+ }
+
+ lexmark_device = (Lexmark_Device *) malloc (sizeof (Lexmark_Device));
+ if (lexmark_device == NULL)
+ return SANE_STATUS_NO_MEM;
+
+ lexmark_device->sane.name = strdup (devname);
+ if (lexmark_device->sane.name == NULL)
+ return SANE_STATUS_NO_MEM;
+ lexmark_device->sane.vendor = "Lexmark";
+ lexmark_device->sane.model = "X2600 series";
+ lexmark_device->sane.type = "flat bed";
+
+ /* init transfer_buffer */
+ lexmark_device->transfer_buffer = malloc (transfer_buffer_size);
+ if (lexmark_device->transfer_buffer == NULL)
+ return SANE_STATUS_NO_MEM;
+
+ /* Make the pointer to the read buffer null here */
+ lexmark_device->read_buffer = malloc (sizeof (Read_Buffer));
+ if (lexmark_device->read_buffer == NULL)
+ return SANE_STATUS_NO_MEM;
+
+ /* mark device as present */
+ lexmark_device->missing = SANE_FALSE;
+ lexmark_device->device_cancelled = SANE_FALSE;
+ /* insert it a the start of the chained list */
+ lexmark_device->next = first_device;
+ first_device = lexmark_device;
+ num_devices++;
+ DBG (2, " first_device=%p\n", (void *)first_device);
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+scan_devices(){
+ DBG (2, "scan_devices\n");
+ SANE_Char config_line[PATH_MAX];
+ FILE *fp;
+ const char *lp;
+ num_devices = 0;
+
+ // -- free existing device we are doning a full re-scan
+ while (first_device){
+ Lexmark_Device *this_device = first_device;
+ first_device = first_device->next;
+ DBG (2, " free first_device\n");
+ free(this_device);
+ }
+
+ fp = sanei_config_open (LEXMARK_X2600_CONFIG_FILE);
+ if (!fp)
+ {
+ DBG (2, " No config no prob...(%s)\n", LEXMARK_X2600_CONFIG_FILE);
+ return SANE_STATUS_GOOD;
+ }
+ while (sanei_config_read (config_line, sizeof (config_line), fp))
+ {
+ if (config_line[0] == '#')
+ continue; /* ignore line comments */
+
+ lp = sanei_config_skip_whitespace (config_line);
+ /* skip empty lines */
+ if (*lp == 0)
+ continue;
+
+ DBG (4, " attach_matching_devices(%s)\n", config_line);
+ sanei_usb_init();
+ sanei_usb_attach_matching_devices (config_line, attach_one);
+ }
+
+ fclose (fp);
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_init (SANE_Int *version_code, SANE_Auth_Callback authorize)
+{
+ DBG_INIT ();
+ DBG (2, "sane_init: version_code %s 0, authorize %s 0\n",
+ version_code == 0 ? "=" : "!=", authorize == 0 ? "=" : "!=");
+ DBG (1, " SANE lexmark_x2600 backend version %d.%d.%d from %s\n",
+ SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, BUILD, PACKAGE_STRING);
+
+ if (version_code)
+ *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, BUILD);
+
+
+ SANE_Status status = scan_devices();
+ initialized = SANE_TRUE;
+ return status;
+}
+
+SANE_Status
+sane_get_devices (const SANE_Device ***device_list, SANE_Bool local_only)
+{
+ SANE_Int index;
+ Lexmark_Device *lexmark_device;
+
+ DBG (2, "sane_get_devices: device_list=%p, local_only=%d num_devices=%d\n",
+ (void *) device_list, local_only, num_devices);
+
+ //sanei_usb_scan_devices ();
+ SANE_Status status = scan_devices();
+
+ if (devlist)
+ free (devlist);
+
+ devlist = malloc ((num_devices + 1) * sizeof (devlist[0]));
+ if (!devlist)
+ return (SANE_STATUS_NO_MEM);
+
+ index = 0;
+ lexmark_device = first_device;
+ while (lexmark_device != NULL)
+ {
+ DBG (2, " lexmark_device->missing:%d\n",
+ lexmark_device->missing);
+ if (lexmark_device->missing == SANE_FALSE)
+ {
+
+ devlist[index] = &(lexmark_device->sane);
+ index++;
+ }
+ lexmark_device = lexmark_device->next;
+ }
+ devlist[index] = 0;
+
+ *device_list = devlist;
+
+ return status;
+}
+
+SANE_Status
+sane_open (SANE_String_Const devicename, SANE_Handle * handle)
+{
+ Lexmark_Device *lexmark_device;
+ SANE_Status status;
+
+ DBG (2, "sane_open: devicename=\"%s\", handle=%p\n", devicename,
+ (void *) handle);
+
+ /* walk the linked list of scanner device until there is a match
+ * with the device name */
+ for (lexmark_device = first_device; lexmark_device;
+ lexmark_device = lexmark_device->next)
+ {
+ DBG (10, " devname from list: %s\n",
+ lexmark_device->sane.name);
+ if (strcmp (devicename, "") == 0
+ || strcmp (devicename, "lexmark") == 0
+ || strcmp (devicename, lexmark_device->sane.name) == 0)
+ break;
+ }
+
+ *handle = lexmark_device;
+
+ status = init_options (lexmark_device);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ DBG(2, " device `%s' opening devnum: '%d'\n",
+ lexmark_device->sane.name, lexmark_device->devnum);
+ status = sanei_usb_open (lexmark_device->sane.name, &(lexmark_device->devnum));
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (1, " couldn't open device `%s': %s\n",
+ lexmark_device->sane.name,
+ sane_strstatus (status));
+ return status;
+ }
+ else
+ {
+ DBG (2, " device `%s' successfully opened devnum: '%d'\n",
+ lexmark_device->sane.name, lexmark_device->devnum);
+ }
+
+ return status;
+}
+
+const SANE_Option_Descriptor *
+sane_get_option_descriptor (SANE_Handle handle, SANE_Int option)
+{
+ Lexmark_Device *lexmark_device;
+
+ //DBG (2, "sane_get_option_descriptor: handle=%p, option = %d\n",
+ // (void *) handle, option);
+
+ /* Check for valid option number */
+ if ((option < 0) || (option >= NUM_OPTIONS))
+ return NULL;
+
+ for (lexmark_device = first_device; lexmark_device;
+ lexmark_device = lexmark_device->next)
+ {
+ if (lexmark_device == handle)
+ break;
+ }
+
+ if (!lexmark_device)
+ return NULL;
+
+ if (lexmark_device->opt[option].name)
+ {
+ //DBG (2, " name=%s\n",
+ // lexmark_device->opt[option].name);
+ }
+
+ return &(lexmark_device->opt[option]);
+}
+
+SANE_Status
+sane_control_option (SANE_Handle handle, SANE_Int option, SANE_Action action,
+ void * value, SANE_Word * info)
+{
+ Lexmark_Device *lexmark_device;
+ SANE_Status status;
+ SANE_Word w;
+ SANE_Int res_selected;
+
+ DBG (2, "sane_control_option: handle=%p, opt=%d, act=%d, val=%p, info=%p\n",
+ (void *) handle, option, action, (void *) value, (void *) info);
+
+ for (lexmark_device = first_device; lexmark_device;
+ lexmark_device = lexmark_device->next){
+ if (lexmark_device == handle)
+ break;
+ }
+
+
+ if (value == NULL)
+ return SANE_STATUS_INVAL;
+
+ switch (action){
+ case SANE_ACTION_SET_VALUE:
+ if (!SANE_OPTION_IS_SETTABLE (lexmark_device->opt[option].cap)){
+ return SANE_STATUS_INVAL;
+ }
+ /* Make sure boolean values are only TRUE or FALSE */
+ if (lexmark_device->opt[option].type == SANE_TYPE_BOOL){
+ if (!
+ ((*(SANE_Bool *) value == SANE_FALSE)
+ || (*(SANE_Bool *) value == SANE_TRUE)))
+ return SANE_STATUS_INVAL;
+ }
+
+ /* Check range constraints */
+ if (lexmark_device->opt[option].constraint_type ==
+ SANE_CONSTRAINT_RANGE){
+ status =
+ sanei_constrain_value (&(lexmark_device->opt[option]), value,
+ info);
+ if (status != SANE_STATUS_GOOD){
+ DBG (2, " SANE_CONTROL_OPTION: Bad value for range\n");
+ return SANE_STATUS_INVAL;
+ }
+ }
+ switch (option){
+ case OPT_NUM_OPTS:
+ case OPT_RESOLUTION:
+ res_selected = *(SANE_Int *) value;
+ // first value is the size of the wordlist!
+ for(int i=1; i<dpi_list_size; i++){
+ DBG (10, " posible res=%d selected=%d\n", dpi_list[i], res_selected);
+ if(res_selected == dpi_list[i]){
+ lexmark_device->val[option].w = *(SANE_Word *) value;
+ }
+ }
+ break;
+ case OPT_TL_X:
+ case OPT_TL_Y:
+ case OPT_BR_X:
+ case OPT_BR_Y:
+ DBG (2, " Option value set to %d (%s)\n", *(SANE_Word *) value,
+ lexmark_device->opt[option].name);
+ lexmark_device->val[option].w = *(SANE_Word *) value;
+ if (lexmark_device->val[OPT_TL_X].w >
+ lexmark_device->val[OPT_BR_X].w){
+ w = lexmark_device->val[OPT_TL_X].w;
+ lexmark_device->val[OPT_TL_X].w =
+ lexmark_device->val[OPT_BR_X].w;
+ lexmark_device->val[OPT_BR_X].w = w;
+ if (info)
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ }
+ if (lexmark_device->val[OPT_TL_Y].w >
+ lexmark_device->val[OPT_BR_Y].w){
+ w = lexmark_device->val[OPT_TL_Y].w;
+ lexmark_device->val[OPT_TL_Y].w =
+ lexmark_device->val[OPT_BR_Y].w;
+ lexmark_device->val[OPT_BR_Y].w = w;
+ if (info)
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ }
+ break;
+ case OPT_MODE:
+ strcpy (lexmark_device->val[option].s, value);
+ if (info)
+ *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
+ return SANE_STATUS_GOOD;
+ }
+
+
+ if (info != NULL)
+ *info |= SANE_INFO_RELOAD_PARAMS;
+
+ break;
+ case SANE_ACTION_GET_VALUE:
+ switch (option){
+ case OPT_NUM_OPTS:
+ 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 = lexmark_device->val[option].w;
+ //DBG (2, " Option value = %d (%s)\n", *(SANE_Word *) value,
+ // lexmark_device->opt[option].name);
+ break;
+ case OPT_MODE:
+ strcpy (value, lexmark_device->val[option].s);
+ break;
+ }
+ break;
+
+ default:
+ return SANE_STATUS_INVAL;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_get_parameters (SANE_Handle handle, SANE_Parameters * params)
+{
+ Lexmark_Device *lexmark_device;
+ SANE_Parameters *device_params;
+ SANE_Int width_px;
+
+ DBG (2, "sane_get_parameters: handle=%p, params=%p\n", (void *) handle,
+ (void *) params);
+
+ for (lexmark_device = first_device; lexmark_device;
+ lexmark_device = lexmark_device->next)
+ {
+ if (lexmark_device == handle)
+ break;
+ }
+
+ if (!lexmark_device)
+ return SANE_STATUS_INVAL;
+
+ // res = lexmark_device->val[OPT_RESOLUTION].w;
+ device_params = &(lexmark_device->params);
+
+ width_px =
+ lexmark_device->val[OPT_BR_X].w - lexmark_device->val[OPT_TL_X].w;
+
+ /* 24 bit colour = 8 bits/channel for each of the RGB channels */
+ device_params->pixels_per_line = width_px;
+ device_params->format = SANE_FRAME_RGB; // SANE_FRAME_GRAY
+ device_params->depth = 8;
+ device_params->bytes_per_line =
+ (SANE_Int) (3 * device_params->pixels_per_line);
+
+ if (strcmp (lexmark_device->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_COLOR)
+ != 0)
+ {
+ device_params->format = SANE_FRAME_GRAY;
+ device_params->bytes_per_line =
+ (SANE_Int) (device_params->pixels_per_line);
+ }
+
+ /* geometry in pixels */
+ device_params->last_frame = SANE_TRUE;
+ device_params->lines = -1;//lexmark_device->val[OPT_BR_Y].w;
+
+ DBG (2, " device_params->pixels_per_line=%d\n",
+ device_params->pixels_per_line);
+ DBG (2, " device_params->bytes_per_line=%d\n",
+ device_params->bytes_per_line);
+ DBG (2, " device_params->depth=%d\n",
+ device_params->depth);
+ DBG (2, " device_params->format=%d\n",
+ device_params->format);
+ DBG (2, " SANE_FRAME_GRAY: %d\n",
+ SANE_FRAME_GRAY);
+ DBG (2, " SANE_FRAME_RGB: %d\n",
+ SANE_FRAME_RGB);
+
+ if (params != 0)
+ {
+ params->format = device_params->format;
+ params->last_frame = device_params->last_frame;
+ params->lines = device_params->lines;
+ params->depth = device_params->depth;
+ params->pixels_per_line = device_params->pixels_per_line;
+ params->bytes_per_line = device_params->bytes_per_line;
+ }
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_start (SANE_Handle handle)
+{
+ Lexmark_Device * lexmark_device;
+ SANE_Status status;
+ SANE_Byte * cmd = (SANE_Byte *) malloc
+ (command_with_params_block_size * sizeof (SANE_Byte));
+ if (cmd == NULL)
+ return SANE_STATUS_NO_MEM;
+
+ DBG (2, "sane_start: handle=%p initialized=%d\n", (void *) handle, initialized);
+
+ if (!initialized)
+ return SANE_STATUS_INVAL;
+
+ for (lexmark_device = first_device; lexmark_device;
+ lexmark_device = lexmark_device->next)
+ {
+ if (lexmark_device == handle)
+ break;
+ }
+
+ if(lexmark_device == NULL){
+ DBG (2, " Cannot find device\n");
+ free(cmd);
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ lexmark_device->read_buffer->data = NULL;
+ lexmark_device->read_buffer->size = 0;
+ lexmark_device->read_buffer->last_line_bytes_read = 0;
+ lexmark_device->read_buffer->image_line_no = 0;
+ lexmark_device->read_buffer->write_byte_counter = 0;
+ lexmark_device->read_buffer->read_byte_counter = 0;
+ lexmark_device->eof = SANE_FALSE;
+ lexmark_device->device_cancelled = SANE_FALSE;
+
+ //launch scan commands
+ status = usb_write_then_read(lexmark_device, command1_block,
+ command1_block_size);
+ if (status != SANE_STATUS_GOOD){
+ free(cmd);
+ return status;
+ }
+ status = usb_write_then_read(lexmark_device, command2_block,
+ command2_block_size);
+ if (status != SANE_STATUS_GOOD){
+ free(cmd);
+ return status;
+ }
+ build_packet(lexmark_device, 0x05, cmd);
+ status = usb_write_then_read(lexmark_device, cmd,
+ command_with_params_block_size);
+ if (status != SANE_STATUS_GOOD){
+ free(cmd);
+ return status;
+ }
+ build_packet(lexmark_device, 0x01, cmd);;
+ status = usb_write_then_read(lexmark_device, cmd,
+ command_with_params_block_size);
+ if (status != SANE_STATUS_GOOD){
+ free(cmd);
+ return status;
+ }
+
+ free(cmd);
+ return SANE_STATUS_GOOD;
+}
+
+
+void debug_packet(const SANE_Byte * source, SANE_Int source_size, Debug_Packet dp){
+ if(dp == READ){
+ DBG (10, "source READ <<< size=%d\n", source_size);
+ }else{
+ DBG (10, "source WRITE >>> size=%d\n", source_size);
+ }
+
+ DBG (10, " %02hhx %02hhx %02hhx %02hhx | %02hhx %02hhx %02hhx %02hhx \n",
+ source[0], source[1], source[2], source[3], source[4], source[5], source[6], source[7]);
+ DBG (10, " %02hhx %02hhx %02hhx %02hhx | %02hhx %02hhx %02hhx %02hhx \n",
+ source[8], source[9], source[10], source[11], source[12], source[13], source[14], source[15]);
+ int debug_offset = 4092;
+ if(source_size > debug_offset){
+ DBG (10, " %02hhx %02hhx %02hhx %02hhx | %02hhx %02hhx %02hhx %02hhx \n",
+ source[source_size-16-debug_offset],
+ source[source_size-15-debug_offset],
+ source[source_size-14-debug_offset],
+ source[source_size-13-debug_offset],
+ source[source_size-12-debug_offset],
+ source[source_size-11-debug_offset],
+ source[source_size-10-debug_offset],
+ source[source_size-9-debug_offset]);
+ DBG (10, " %02hhx %02hhx %02hhx %02hhx | %02hhx %02hhx %02hhx %02hhx \n",
+ source[source_size-8-debug_offset],
+ source[source_size-7-debug_offset],
+ source[source_size-6-debug_offset],
+ source[source_size-5-debug_offset],
+ source[source_size-4-debug_offset],
+ source[source_size-3-debug_offset],
+ source[source_size-2-debug_offset],
+ source[source_size-1-debug_offset]);
+ }
+ return;
+}
+
+SANE_Status
+sane_read (SANE_Handle handle, SANE_Byte * data,
+ SANE_Int max_length, SANE_Int * length)
+{
+ Lexmark_Device * lexmark_device;
+ SANE_Status status;
+ size_t size = transfer_buffer_size;
+ //SANE_Byte buf[size];
+ DBG (1, "\n");
+ DBG (1, "sane_read max_length=%d:\n", max_length);
+
+ for (lexmark_device = first_device; lexmark_device;
+ lexmark_device = lexmark_device->next)
+ {
+ if (lexmark_device == handle)
+ break;
+ }
+
+ if (lexmark_device->device_cancelled == SANE_TRUE) {
+ DBG (10, "device_cancelled=True \n");
+ usb_write_then_read(lexmark_device, command_cancel1_block,
+ command_cancel_size);
+ usb_write_then_read(lexmark_device, command_cancel2_block,
+ command_cancel_size);
+ usb_write_then_read(lexmark_device, command_cancel1_block,
+ command_cancel_size);
+ usb_write_then_read(lexmark_device, command_cancel2_block,
+ command_cancel_size);
+ // to empty buffers
+ status = sanei_usb_read_bulk (
+ lexmark_device->devnum, lexmark_device->transfer_buffer, &size);
+ if(status == SANE_STATUS_GOOD){
+ status = sanei_usb_read_bulk (
+ lexmark_device->devnum, lexmark_device->transfer_buffer, &size);
+ }
+ if(status == SANE_STATUS_GOOD){
+ status = sanei_usb_read_bulk (
+ lexmark_device->devnum, lexmark_device->transfer_buffer, &size);
+ }
+
+ return status;
+ }
+
+ //status = sanei_usb_read_bulk (lexmark_device->devnum, buf, &size);
+ if(!lexmark_device->eof){
+ DBG (1, " usb_read\n");
+ status = sanei_usb_read_bulk (
+ lexmark_device->devnum, lexmark_device->transfer_buffer, &size);
+ if (status != SANE_STATUS_GOOD && status != SANE_STATUS_EOF)
+ {
+ DBG (1, " USB READ Error in sanei_usb_read_bulk, cannot read devnum=%d status=%d size=%ld\n",
+ lexmark_device->devnum, status, size);
+ return status;
+ }
+ DBG (1, " usb_read done size=%ld\n", size);
+ debug_packet(lexmark_device->transfer_buffer, size, READ);
+ }else{
+ DBG (1, " no usb_read eof reached\n");
+ }
+
+ // is last data packet ?
+ if (!lexmark_device->eof && memcmp(last_data_packet, lexmark_device->transfer_buffer, last_data_packet_size) == 0){
+
+ // we may still have data left to send in our buffer device->read_buffer->data
+ //length = 0;
+ //return SANE_STATUS_EOF;
+ lexmark_device->eof = SANE_TRUE;
+ DBG (1, " EOF PACKET no more data from scanner\n");
+
+ return SANE_STATUS_GOOD;
+ }
+ // cancel packet received?
+ if (memcmp(cancel_packet, lexmark_device->transfer_buffer, cancel_packet_size) == 0){
+ length = 0;
+ return SANE_STATUS_CANCELLED;
+ }
+ if (memcmp(empty_line_data_packet, lexmark_device->transfer_buffer, empty_line_data_packet_size) == 0){
+ return SANE_STATUS_GOOD;
+ }
+ if (memcmp(unknown_a_data_packet, lexmark_device->transfer_buffer, unknown_a_data_packet_size) == 0){
+ return SANE_STATUS_GOOD;
+ }
+ if (memcmp(unknown_b_data_packet, lexmark_device->transfer_buffer, unknown_b_data_packet_size) == 0){
+ return SANE_STATUS_GOOD;
+ }
+ if (memcmp(unknown_c_data_packet, lexmark_device->transfer_buffer, unknown_c_data_packet_size) == 0){
+ return SANE_STATUS_GOOD;
+ }
+ if (memcmp(unknown_d_data_packet, lexmark_device->transfer_buffer, unknown_d_data_packet_size) == 0){
+ return SANE_STATUS_GOOD;
+ }
+ if (memcmp(unknown_e_data_packet, lexmark_device->transfer_buffer, unknown_e_data_packet_size) == 0){
+ return SANE_STATUS_GOOD;
+ }
+
+ status = clean_and_copy_data(
+ lexmark_device->transfer_buffer,
+ size,
+ data,
+ length,
+ lexmark_device->params.format,
+ max_length,
+ handle);
+
+ return status;
+}
+
+SANE_Status
+sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking)
+{
+ DBG (2, "sane_set_io_mode: handle = %p, non_blocking = %d\n",
+ (void *) handle, non_blocking);
+
+ if (non_blocking)
+ return SANE_STATUS_UNSUPPORTED;
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_get_select_fd (SANE_Handle handle, SANE_Int * fd)
+{
+ DBG (2, "sane_get_select_fd: handle = %p, fd %s 0\n", (void *) handle,
+ fd ? "!=" : "=");
+
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+void
+sane_cancel (SANE_Handle handle)
+{
+ Lexmark_Device * lexmark_device;
+
+ DBG (2, "sane_cancel: handle = %p\n", (void *) handle);
+
+ for (lexmark_device = first_device; lexmark_device;
+ lexmark_device = lexmark_device->next)
+ {
+ if (lexmark_device == handle)
+ break;
+ }
+ sanei_usb_reset (lexmark_device->devnum);
+ lexmark_device->device_cancelled = SANE_TRUE;
+}
+
+void
+sane_close (SANE_Handle handle)
+{
+ Lexmark_Device * lexmark_device;
+
+ DBG (2, "sane_close: handle=%p\n", (void *) handle);
+
+ for (lexmark_device = first_device; lexmark_device;
+ lexmark_device = lexmark_device->next)
+ {
+ if (lexmark_device == handle)
+ break;
+ }
+
+ sanei_usb_close (lexmark_device->devnum);
+}
+
+void
+sane_exit (void)
+{
+ Lexmark_Device *lexmark_device, *next_lexmark_device;
+
+ DBG (2, "sane_exit\n");
+
+ if (!initialized)
+ return;
+
+ for (lexmark_device = first_device; lexmark_device;
+ lexmark_device = next_lexmark_device)
+ {
+ next_lexmark_device = lexmark_device->next;
+ free (lexmark_device->transfer_buffer);
+ free (lexmark_device->read_buffer);
+ free (lexmark_device);
+ }
+
+ if (devlist)
+ free (devlist);
+
+ sanei_usb_exit();
+ initialized = SANE_FALSE;
+
+}