kopia lustrzana https://gitlab.com/sane-project/backends
				
				
				
			
		
			
				
	
	
		
			1271 wiersze
		
	
	
		
			40 KiB
		
	
	
	
		
			C
		
	
	
			
		
		
	
	
			1271 wiersze
		
	
	
		
			40 KiB
		
	
	
	
		
			C
		
	
	
| /* 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\n", devname);
 | |
| 
 | |
|   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++;
 | |
| 
 | |
|   return SANE_STATUS_GOOD;
 | |
| }
 | |
| 
 | |
| SANE_Status
 | |
| sane_init (SANE_Int *version_code, SANE_Auth_Callback authorize)
 | |
| {
 | |
|   FILE *fp;
 | |
|   SANE_Char config_line[PATH_MAX];
 | |
|   const char *lp;
 | |
| 
 | |
|   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);
 | |
| 
 | |
|   sanei_usb_init ();
 | |
| 
 | |
|   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_attach_matching_devices (config_line, attach_one);
 | |
|     }
 | |
| 
 | |
|   fclose (fp);
 | |
|   initialized = SANE_TRUE;
 | |
|   return SANE_STATUS_GOOD;
 | |
| }
 | |
| 
 | |
| 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 ();
 | |
| 
 | |
|   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 SANE_STATUS_GOOD;
 | |
| }
 | |
| 
 | |
| 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;
 | |
| 
 | |
| }
 |