diff options
Diffstat (limited to 'backend/ricoh2.c')
-rw-r--r-- | backend/ricoh2.c | 972 |
1 files changed, 972 insertions, 0 deletions
diff --git a/backend/ricoh2.c b/backend/ricoh2.c new file mode 100644 index 0000000..f719268 --- /dev/null +++ b/backend/ricoh2.c @@ -0,0 +1,972 @@ +/* sane - Scanner Access Now Easy. + + Copyright (C) 2018, 2019 Stanislav Yuzvinsky + Based on the work done by viruxx + + 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, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, + MA 02111-1307, USA. + + 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 "../include/sane/config.h" + +#include <string.h> + +#include "../include/sane/sane.h" +#include "../include/sane/sanei.h" +#include "../include/sane/sanei_usb.h" +#include "../include/sane/saneopts.h" +#include "../include/sane/sanei_backend.h" +#include "../include/sane/sanei_debug.h" + +#include "ricoh2_buffer.c" + +#define MAX_OPTION_STRING_SIZE 255 +#define MAX_LINE_SIZE 240 * 256 /* = 61440 */ +#define HEIGHT_PIXELS_300DPI 3508 +#define WIDTH_BYTES_300DPI 2560 +#define WIDTH_PIXELS_300DPI 2550 +#define INFO_SIZE (WIDTH_BYTES_300DPI - WIDTH_PIXELS_300DPI) +#define USB_TIMEOUT_MS 20000 +#define MAX_COMMAND_SIZE 64 + +#define CHECK_IF(x) if (!(x)) return SANE_STATUS_INVAL + +typedef enum +{ + OPT_NUM_OPTS = 0, + OPT_MODE, + OPT_RESOLUTION, + + /* must come last: */ + NUM_OPTIONS +} +Ricoh_Options; + +typedef enum +{ + SCAN_MODE_COLOR, + SCAN_MODE_GRAY +} +Scan_Mode; + + +typedef struct Ricoh2_Device { + struct Ricoh2_Device *next; + SANE_Device sane; + SANE_Bool active; + + /* options */ + SANE_Option_Descriptor opt[NUM_OPTIONS]; + Option_Value val[NUM_OPTIONS]; + + /* acquiring session */ + SANE_Int dn; + SANE_Bool cancelled; + Scan_Mode mode; + SANE_Int resolution; + SANE_Bool eof; + size_t bytes_to_read; + ricoh2_buffer *buffer; + +} +Ricoh2_Device; + +typedef struct Ricoh2_device_info { + SANE_Int product_id; + SANE_String_Const device_name; +} +Ricoh2_device_info; + +static Ricoh2_device_info supported_devices[] = { + { 0x042c, "Aficio SP-100SU" }, + { 0x0438, "Aficio SG-3100SNw" }, + { 0x0439, "Aficio SG-3110SFNw" }, + { 0x0448, "Aficio SP-111SU/SP-112SU" } +}; + +static SANE_String_Const mode_list[] = { + SANE_VALUE_SCAN_MODE_COLOR, + SANE_VALUE_SCAN_MODE_GRAY, + NULL +}; +static SANE_String_Const default_mode = SANE_VALUE_SCAN_MODE_COLOR; + +static SANE_Int resolution_list[] = { + 2, 300, 600 +}; +static SANE_Int default_resolution = 300; + +static SANE_Bool initialized = SANE_FALSE; +static Ricoh2_Device *ricoh2_devices = NULL; +static const SANE_Device **sane_devices = NULL; +static SANE_Int num_devices = 0; + +static Ricoh2_Device * +lookup_handle(SANE_Handle handle) +{ + Ricoh2_Device *device; + + for (device = ricoh2_devices; device; device = device->next) + { + if (device == handle) + return device; + } + + return NULL; +} + +static SANE_String_Const get_model_by_productid(SANE_Int id) +{ + size_t i = 0; + for (; i < sizeof (supported_devices) / sizeof (supported_devices[0]); ++i) + { + if (supported_devices[i].product_id == id) + { + return supported_devices[i].device_name; + } + } + + return "Unidentified device"; +} + +static SANE_Status +attach (SANE_String_Const devname) +{ + SANE_Int dn = -1; + SANE_Status status = SANE_STATUS_GOOD; + Ricoh2_Device *device = NULL; + SANE_Int vendor, product; + + for (device = ricoh2_devices; device; device = device->next) + { + if (strcmp (device->sane.name, devname) == 0) + { + device->active = SANE_TRUE; + return SANE_STATUS_GOOD; + } + } + + device = (Ricoh2_Device *) malloc (sizeof (Ricoh2_Device)); + if (!device) + { + return SANE_STATUS_NO_MEM; + } + + DBG (8, "attach %s\n", devname); + status = sanei_usb_open (devname, &dn); + if (status != SANE_STATUS_GOOD) + { + DBG (1, "attach: couldn't open device `%s': %s\n", devname, + sane_strstatus (status)); + return status; + } + + status = sanei_usb_get_vendor_product (dn, &vendor, &product); + if (status != SANE_STATUS_GOOD) + { + DBG (1, + "attach: couldn't get vendor and product ids of device `%s': %s\n", + devname, sane_strstatus (status)); + sanei_usb_close (dn); + return status; + } + + sanei_usb_close (dn); + device->sane.name = strdup (devname); + device->sane.vendor = "Ricoh"; + device->sane.model = get_model_by_productid (product); + device->sane.type = "flatbed scanner"; + device->active = SANE_TRUE; + device->buffer = NULL; + + device->next = ricoh2_devices; + ricoh2_devices = device; + + DBG (2, "Found device %s\n", device->sane.name); + ++num_devices; + + return SANE_STATUS_GOOD; +} + +static SANE_Status +init_options(Ricoh2_Device *dev) +{ + SANE_Option_Descriptor *od; + + DBG (8, "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_Word); + od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT; + od->constraint_type = SANE_CONSTRAINT_WORD_LIST; + od->constraint.word_list = resolution_list; + dev->val[OPT_RESOLUTION].w = 300; + + return SANE_STATUS_GOOD; +} + +SANE_Status +sane_init (SANE_Int *vc, SANE_Auth_Callback __sane_unused__ cb) +{ + size_t i = 0; + + DBG_INIT (); + + DBG(8, ">sane_init\n"); + + sanei_usb_init (); + sanei_usb_set_timeout (USB_TIMEOUT_MS); + + num_devices = 0; + + for (; i < sizeof (supported_devices) / sizeof (supported_devices[0]); ++i) + { + sanei_usb_find_devices (0x5ca, supported_devices[i].product_id, attach); + } + + if (vc) + *vc = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, V_MINOR, 0); + DBG(8, "<sane_init\n"); + + initialized = SANE_TRUE; + + return SANE_STATUS_GOOD; +} + +SANE_Status +sane_get_devices (const SANE_Device ***dl, + SANE_Bool __sane_unused__ local) +{ + Ricoh2_Device *device = NULL; + SANE_Int i = 0; + + DBG(8, ">sane_get_devices\n"); + + num_devices = 0; + sanei_usb_find_devices (0x5ca, 0x042c, attach); + sanei_usb_find_devices (0x5ca, 0x0448, attach); + + if (sane_devices) + free (sane_devices); + + sane_devices = (const SANE_Device **) malloc (sizeof (const SANE_Device *) + * (num_devices + 1)); + if (!sane_devices) + return SANE_STATUS_NO_MEM; + + for (device = ricoh2_devices; device; device = device->next) + if (device->active) + { + sane_devices[i++] = &(device->sane); + } + + sane_devices[i] = NULL; + *dl = sane_devices; + + DBG(2, "found %i devices\n", i); + DBG(8, "<sane_get_devices\n"); + + return SANE_STATUS_GOOD; +} + +SANE_Status +sane_open (SANE_String_Const name, SANE_Handle *handle) +{ + Ricoh2_Device *device; + SANE_Status status; + + DBG (8, ">sane_open: devicename=\"%s\", handle=%p\n", name, + (void *) handle); + + CHECK_IF (initialized); + CHECK_IF (handle); + + /* walk the linked list of scanner device until there is a match + * with the device name */ + for (device = ricoh2_devices; device; device = device->next) + { + DBG (2, "sane_open: devname from list: %s\n", + device->sane.name); + if (strcmp (name, "") == 0 + || strcmp (name, "ricoh") == 0 + || strcmp (name, device->sane.name) == 0) + break; + } + + *handle = device; + + if (!device) + { + DBG (1, "sane_open: Not a Ricoh device\n"); + return SANE_STATUS_INVAL; + } + + status = init_options (device); + if (status != SANE_STATUS_GOOD) + return status; + + DBG (8, "<sane_open\n"); + + return SANE_STATUS_GOOD; +} + +const SANE_Option_Descriptor * +sane_get_option_descriptor (SANE_Handle handle, SANE_Int option) +{ + Ricoh2_Device *device; + + DBG (8, "<sane_get_option_descriptor: handle=%p, option = %d\n", + (void *) handle, option); + + if (!initialized) + return NULL; + + /* Check for valid option number */ + if ((option < 0) || (option >= NUM_OPTIONS)) + return NULL; + + if (!(device = lookup_handle(handle))) + return NULL; + + if (device->opt[option].name) + { + DBG (8, ">sane_get_option_descriptor: name=%s\n", + device->opt[option].name); + } + + return &(device->opt[option]); +} + +SANE_Status +sane_control_option (SANE_Handle handle, + SANE_Int option, + SANE_Action action, + void *value, + SANE_Word *info) +{ + Ricoh2_Device *device; + SANE_Status status; + + DBG (8, + ">sane_control_option: handle=%p, opt=%d, act=%d, val=%p, info=%p\n", + (void *) handle, option, action, (void *) value, (void *) info); + + CHECK_IF (initialized); + device = lookup_handle (handle); + CHECK_IF (device); + CHECK_IF (value); + CHECK_IF (option >= 0 && option < NUM_OPTIONS); + CHECK_IF (device->opt[option].type != SANE_TYPE_GROUP); + + switch (action) + { + case SANE_ACTION_SET_AUTO: + CHECK_IF (SANE_OPTION_IS_SETTABLE (device->opt[option].cap)); + CHECK_IF (device->opt[option].cap & SANE_CAP_AUTOMATIC); + + switch (option) + { + case OPT_RESOLUTION: + DBG (2, + "Setting value to default value of '%d' for option '%s'\n", + default_resolution, + device->opt[option].name); + device->val[option].w = default_resolution; + break; + + case OPT_MODE: + DBG (2, + "Setting value to default value of '%s' for option '%s'\n", + (SANE_String_Const) default_mode, + device->opt[option].name); + strcpy (device->val[option].s, default_mode); + break; + + default: + return SANE_STATUS_INVAL; + } + break; + + case SANE_ACTION_SET_VALUE: + CHECK_IF (SANE_OPTION_IS_SETTABLE (device->opt[option].cap)); + + if (device->opt[option].type == SANE_TYPE_BOOL) + { + SANE_Bool bool_value = *(SANE_Bool *) value; + CHECK_IF (bool_value == SANE_TRUE || bool_value == SANE_FALSE); + } + + if (device->opt[option].constraint_type == SANE_CONSTRAINT_RANGE) + { + status = sanei_constrain_value (&(device->opt[option]), value, info); + CHECK_IF (status == SANE_STATUS_GOOD); + } + + + switch (option) + { + case OPT_RESOLUTION: + DBG (2, + "Setting value to '%d' for option '%s'\n", + *(SANE_Word *) value, + device->opt[option].name); + device->val[option].w = *(SANE_Word *) value; + break; + + case OPT_MODE: + DBG (2, + "Setting value to '%s' for option '%s'\n", + (SANE_String_Const)value, + device->opt[option].name); + strcpy (device->val[option].s, value); + break; + + default: + return SANE_STATUS_INVAL; + } + break; + + case SANE_ACTION_GET_VALUE: + + switch (option) + { + case OPT_NUM_OPTS: + case OPT_RESOLUTION: + *(SANE_Word *) value = device->val[option].w; + DBG (2, "Option value = %d (%s)\n", *(SANE_Word *) value, + device->opt[option].name); + break; + case OPT_MODE: + strcpy (value, device->val[option].s); + break; + default: + return SANE_STATUS_INVAL; + } + break; + + default: + return SANE_STATUS_INVAL; + } + + DBG (8, "<sane_control_option\n"); + return SANE_STATUS_GOOD; +} + +static void +update_scan_params (Ricoh2_Device *device) +{ + /* Scan mode: color or grayscale */ + if (strcmp(device->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_COLOR) == 0) + { + device->mode = SCAN_MODE_COLOR; + } + else + { + device->mode = SCAN_MODE_GRAY; + } + + /* resolution: 300 or 600dpi */ + device->resolution = device->val[OPT_RESOLUTION].w; +} + + +SANE_Status +sane_get_parameters (SANE_Handle handle, SANE_Parameters *params) +{ + Ricoh2_Device *device; + + DBG (8, "sane_get_parameters: handle=%p, params=%p\n", (void *) handle, + (void *) params); + + CHECK_IF (initialized); + device = lookup_handle (handle); + CHECK_IF (device); + CHECK_IF (params); + + update_scan_params (device); + + params->format = + device->mode == SCAN_MODE_COLOR ? SANE_FRAME_RGB : SANE_FRAME_GRAY; + params->last_frame = SANE_TRUE; + + params->pixels_per_line = WIDTH_PIXELS_300DPI; + params->bytes_per_line = params->pixels_per_line; + params->lines = HEIGHT_PIXELS_300DPI; + params->depth = 8; + + if (device->resolution == 600) + { + params->bytes_per_line *= 2; + params->pixels_per_line *= 2; + params->lines *= 2; + } + + if (device->mode == SCAN_MODE_COLOR) + { + params->bytes_per_line *= 3; + } + + DBG (8, ">sane_get_parameters: format = %s bytes_per_line = %d " + "depth = %d " + "pixels_per_line = %d " + "lines = %d\n", + (params->format == SANE_FRAME_RGB ? "rgb" : "gray"), + params->bytes_per_line, + params->depth, + params->pixels_per_line, + params->lines); + + return SANE_STATUS_GOOD; +} + +typedef struct +{ + SANE_Byte *send_buffer; + size_t to_send; + SANE_Byte *receive_buffer; + size_t to_receive; +} +Send_Receive_Pair; + +static SANE_Status +send_receive (SANE_Int dn, Send_Receive_Pair *transfer) +{ + SANE_Status status; + size_t io_size; + SANE_Byte send_buffer[MAX_COMMAND_SIZE]; + + assert(transfer->to_send <= MAX_COMMAND_SIZE); + + memset(send_buffer, 0, MAX_COMMAND_SIZE); + + /* send a command */ + io_size = MAX_COMMAND_SIZE; + DBG (128, "sending a packet of size %lu\n", io_size); + memcpy (send_buffer, transfer->send_buffer, transfer->to_send); + status = sanei_usb_write_bulk (dn, send_buffer, &io_size); + if (status != SANE_STATUS_GOOD) + { + DBG (1, "could not send packet: %s\n", sane_strstatus (status)); + return status; + } + + /* receive a result */ + io_size = transfer->to_receive; + DBG (128, "receiving a packet of size %lu\n", io_size); + if (io_size) + { + status = sanei_usb_read_bulk (dn, transfer->receive_buffer, &io_size); + if (status != SANE_STATUS_GOOD) + { + DBG (1, "could not get a response for packet: %s\n", + sane_strstatus (status)); + return status; + } + if (io_size != transfer->to_receive) + { + DBG (1, "unexpected size of received packet: expected %lu, " + "received %lu\n", transfer->to_receive, io_size); + return SANE_STATUS_IO_ERROR; + } + } + return SANE_STATUS_GOOD; +} + +static SANE_Status +init_scan(SANE_Int dn, Scan_Mode mode, SANE_Int resolution) +{ + SANE_Status status = SANE_STATUS_GOOD; + SANE_Byte dummy_buffer[11]; /* the longest expected reply */ + size_t i; + + SANE_Byte urb_init[] = { 0x03, 0x09, 0x01 }; + SANE_Byte magic0[] = { 0x03, 0x0d, 0x0b }; + SANE_Byte magic1[] = { + 0x03, 0x0c, 0x11, 0x00, 0x00, 0x00, 0x01, 0x02, 0x05, + 0xff, 0x00, 0x00, 0x00, 0x00, 0xec, 0x13, 0x6c, 0x1b }; + SANE_Byte magic2[] = { 0x03, 0x0b, 0x08 }; + SANE_Byte magic3[] = { + 0x03, 0x08, 0x04, 0x00, 0x00, 0x00, 0x00, 0x50, 0x6d, 0x06, 0x01 }; + + Send_Receive_Pair transfer[] = + { + { urb_init, sizeof (urb_init), dummy_buffer, 1 }, + { magic0, sizeof (magic0), dummy_buffer, 11 }, + { magic1, sizeof (magic1), dummy_buffer, 0 }, + { magic2, sizeof (magic2), dummy_buffer, 8 }, + { magic3, sizeof (magic3), dummy_buffer, 0 } + }; + + if (resolution == 600) + magic1[6] = 0x02; + + if (mode == SCAN_MODE_COLOR) + magic1[7] = 0x03; + + for (i = 0; + i < sizeof (transfer) / sizeof (transfer[0]) + && (status == SANE_STATUS_GOOD); + ++i) + { + DBG (128, "sending initialization packet %zi\n", i); + status = send_receive (dn, transfer + i); + } + + return status; +} + +void +teardown_scan(SANE_Int dn) +{ + SANE_Byte cancel_command[] = { 0x03, 0x0a }; + SANE_Byte end_command[] = { 0x03, 0x09, 0x01 }; + SANE_Byte dummy_buffer; + Send_Receive_Pair transfer; + + DBG (128, "Sending cancel command\n"); + transfer.send_buffer = cancel_command; + transfer.to_send = sizeof (cancel_command); + transfer.receive_buffer = &dummy_buffer; + transfer.to_receive = 0; + send_receive (dn, &transfer); + + transfer.send_buffer = end_command; + transfer.to_send = sizeof (end_command); + transfer.receive_buffer = &dummy_buffer; + transfer.to_receive = 1; + send_receive (dn, &transfer); +} + +SANE_Status +sane_start (SANE_Handle handle) +{ + Ricoh2_Device *device; + SANE_Status status; + SANE_Int pixels_per_line; + SANE_Int resolution_factor = 1; + + DBG (8, ">sane_start: handle=%p\n", (void *) handle); + + CHECK_IF (initialized); + device = lookup_handle (handle); + CHECK_IF (device); + + update_scan_params (device); + device->cancelled = SANE_FALSE; + + status = sanei_usb_open (device->sane.name, &(device->dn)); + if (status != SANE_STATUS_GOOD) + { + DBG (1, "could not open device %s: %s\n", + device->sane.name, sane_strstatus (status)); + return status; + } + + DBG (2, "usb device %s opened, device number is %d\n", + device->sane.name, device->dn); + + status = sanei_usb_claim_interface (device->dn, 0); + if (status != SANE_STATUS_GOOD) + { + DBG (1, "could not claim interface 0: %s\n", + sane_strstatus (status)); + sanei_usb_close (device->dn); + return status; + } + + sanei_usb_set_endpoint (device->dn, + USB_DIR_OUT | USB_ENDPOINT_TYPE_BULK, + 0x03); + + sanei_usb_set_endpoint (device->dn, + USB_DIR_IN | USB_ENDPOINT_TYPE_BULK, + 0x85); + + status = sanei_usb_reset (device->dn); + if (status != SANE_STATUS_GOOD) + { + DBG (1, "could not reset device %s: %s\n", + device->sane.name, sane_strstatus (status)); + sanei_usb_close (device->dn); + return status; + } + + + status = init_scan (device->dn, device->mode, device->resolution); + if (status != SANE_STATUS_GOOD) + { + sanei_usb_close (device->dn); + return status; + } + + resolution_factor = device->resolution == 600 ? 2 : 1; + + pixels_per_line = WIDTH_PIXELS_300DPI * resolution_factor; + + device->bytes_to_read = + WIDTH_PIXELS_300DPI * resolution_factor + * HEIGHT_PIXELS_300DPI * resolution_factor + * (device->mode == SCAN_MODE_COLOR ? 3 : 1); + + device->buffer = + ricoh2_buffer_create (MAX_LINE_SIZE, + pixels_per_line, + INFO_SIZE * resolution_factor, + device->mode == SCAN_MODE_COLOR); + + DBG (8, "<sane_start: %lu bytes to read\n", device->bytes_to_read); + + return SANE_STATUS_GOOD; +} + +SANE_Status +sane_read (SANE_Handle handle, + SANE_Byte *data, + SANE_Int maxlen, + SANE_Int *length) +{ + SANE_Byte read_next_command[] = { 0x03, 0x0E, 0x04, 0, 0, 0, 0, 240 }; + + Ricoh2_Device *device; + SANE_Status status; + Send_Receive_Pair transfer; + + DBG (16, ">sane_read: handle=%p, data=%p, maxlen = %d, length=%p\n", + (void *) handle, (void *) data, maxlen, (void *) length); + + CHECK_IF (initialized); + device = lookup_handle (handle); + CHECK_IF (device); + CHECK_IF (length); + CHECK_IF (maxlen); + + /* + EOF has already been reached before or acquisition process hasn't + been initiated at all + */ + if (device->bytes_to_read <= 0) + { + return SANE_STATUS_EOF; + } + + if (!ricoh2_buffer_get_bytes_remain (device->buffer)) + { + transfer.send_buffer = read_next_command; + transfer.to_send = sizeof (read_next_command); + transfer.receive_buffer = + ricoh2_buffer_get_internal_buffer (device->buffer); + transfer.to_receive = MAX_LINE_SIZE; + read_next_command[7] = transfer.to_receive / 256; + + DBG (128, "Receiving data of size %zi\n", transfer.to_receive); + + status = send_receive (device->dn, &transfer); + if (status != SANE_STATUS_GOOD) + { + device->bytes_to_read = 0; + return status; + } + } + + *length = ricoh2_buffer_get_data (device->buffer, + data, + min(maxlen, device->bytes_to_read)); + + device->bytes_to_read -= *length; + + DBG (128, + "Read length %d, left to read %lu\n", + *length, + device->bytes_to_read); + + DBG (128, + "%d bytes remain in the buffer\n", + ricoh2_buffer_get_bytes_remain(device->buffer)); + + /* we've just reached expected data size */ + if (device->bytes_to_read <= 0) + { + ricoh2_buffer_dispose(device->buffer); + device->buffer = NULL; + return SANE_STATUS_EOF; + } + + DBG (16, "<sane_read\n"); + + return SANE_STATUS_GOOD; +} + +SANE_Status +sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking) +{ + Ricoh2_Device *device; + DBG (8, "sane_set_io_mode: handle = %p, non_blocking = %d\n", + (void *) handle, non_blocking); + + CHECK_IF (initialized); + device = lookup_handle (handle); + CHECK_IF (device); + + if (non_blocking) + return SANE_STATUS_UNSUPPORTED; + + return SANE_STATUS_GOOD; +} + +SANE_Status +sane_get_select_fd (SANE_Handle handle, SANE_Int *fd) +{ + Ricoh2_Device *device; + DBG (8, "sane_get_select_fd: handle = %p, fd %s 0\n", (void *) handle, + fd ? "!=" : "="); + + CHECK_IF (initialized); + device = lookup_handle (handle); + CHECK_IF (device); + + return SANE_STATUS_UNSUPPORTED; +} + +void +sane_cancel (SANE_Handle handle) +{ + Ricoh2_Device *device; + + DBG (8, ">sane_cancel: handle = %p\n", (void *) handle); + + if (!initialized) + return; + + if (!(device = lookup_handle (handle))) + return; + + if (device->cancelled) + return; + + device->cancelled = SANE_TRUE; + + teardown_scan (device->dn); + if (device->buffer) + { + ricoh2_buffer_dispose (device->buffer); + device->buffer = NULL; + } + + sanei_usb_close(device->dn); + + DBG (8, "<sane_cancel\n"); +} + +void +sane_close (SANE_Handle handle) +{ + Ricoh2_Device *device; + + DBG (8, ">sane_close\n"); + + if (!initialized) + return; + + device = lookup_handle (handle); + if (!device) + return; + + /* noop */ + + DBG (8, "<sane_close\n"); +} + +void +sane_exit (void) +{ + Ricoh2_Device *device, *next; + + DBG (8, ">sane_exit\n"); + + if (!initialized) + return; + + for (device = ricoh2_devices, next = device; device; device = next) + { + next = device->next; + free (device); + } + + if (sane_devices) + free (sane_devices); + + sanei_usb_exit (); + initialized = SANE_FALSE; + + DBG (8, "<sane_exit\n"); +} |