diff options
| author | Jörg Frings-Fürst <debian@jff.email> | 2024-03-03 09:54:51 +0100 | 
|---|---|---|
| committer | Jörg Frings-Fürst <debian@jff.email> | 2024-03-03 09:54:51 +0100 | 
| commit | 44916ca6d75e0b5f258a098a50d659f31c6625fd (patch) | |
| tree | 2e51a12ae43b3def9e55d3f2c9ca60d2032ad45c /backend/lexmark_x2600.c | |
| parent | 84357741a6a6e6430f199b2c3f7498e0e97da9ad (diff) | |
New upstream version 1.3.0upstream/1.3.0
Diffstat (limited to 'backend/lexmark_x2600.c')
| -rw-r--r-- | backend/lexmark_x2600.c | 1287 | 
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; + +} | 
