summaryrefslogtreecommitdiff
path: root/backend/gt68xx_low.c
diff options
context:
space:
mode:
Diffstat (limited to 'backend/gt68xx_low.c')
-rw-r--r--backend/gt68xx_low.c1034
1 files changed, 1034 insertions, 0 deletions
diff --git a/backend/gt68xx_low.c b/backend/gt68xx_low.c
new file mode 100644
index 0000000..6060c45
--- /dev/null
+++ b/backend/gt68xx_low.c
@@ -0,0 +1,1034 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2002 Sergey Vlasov <vsu@altlinux.ru>
+ Copyright (C) 2002 - 2007 Henning Geinitz <sane@geinitz.org>
+
+ 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.
+*/
+
+/** @file
+ * @brief Implementation of the low-level scanner interface functions.
+ */
+
+#include "gt68xx_low.h"
+
+#include "../include/sane/sane.h"
+#include "../include/sane/sanei_debug.h"
+#include "../include/sane/sanei_usb.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef USE_FORK
+#include <sys/wait.h>
+#include <unistd.h>
+#include "gt68xx_shm_channel.c"
+#endif
+
+/** Check that the device pointer is not NULL.
+ *
+ * @param dev Pointer to the device object (GT68xx_Device).
+ * @param func_name Function name (for use in debug messages).
+ */
+#define CHECK_DEV_NOT_NULL(dev, func_name) \
+ do { \
+ IF_DBG( \
+ if (!(dev)) \
+ { \
+ DBG (0, "BUG: NULL device\n"); \
+ return SANE_STATUS_INVAL; \
+ } \
+ ) \
+ } while (SANE_FALSE)
+
+/** Check that the device is open.
+ *
+ * @param dev Pointer to the device object (GT68xx_Device).
+ * @param func_name Function name (for use in debug messages).
+ */
+#define CHECK_DEV_OPEN(dev, func_name) \
+ do { \
+ IF_DBG( \
+ CHECK_DEV_NOT_NULL ((dev), (func_name)); \
+ if ((dev)->fd == -1) \
+ { \
+ DBG (0, "%s: BUG: device %p not open\n", (func_name), \
+ ((void *) dev)); \
+ return SANE_STATUS_INVAL; \
+ } \
+ ) \
+ } while (SANE_FALSE)
+
+/** Check that the device is open and active.
+ *
+ * @param dev Pointer to the device (GT68xx_Device).
+ * @param func_name Function name (for use in debug messages).
+ */
+#define CHECK_DEV_ACTIVE(dev, func_name) \
+ do { \
+ IF_DBG( \
+ CHECK_DEV_OPEN ((dev), (func_name)); \
+ if (!(dev)->active) \
+ { \
+ DBG (0, "%s: BUG: device %p not active\n", (func_name), \
+ ((void *) dev)); \
+ return SANE_STATUS_INVAL; \
+ } \
+ ) \
+ } while (SANE_FALSE)
+
+
+#ifndef NDEBUG
+
+/** Dump a request packet for debugging purposes.
+ *
+ * @param prefix String printed before the packet contents.
+ * @param req The request packet to be dumped.
+ */
+static void
+dump_req (SANE_String_Const prefix, GT68xx_Packet req)
+{
+ int i;
+ char buf[GT68XX_PACKET_SIZE * 3 + 1];
+
+ for (i = 0; i < GT68XX_PACKET_SIZE; ++i)
+ sprintf (buf + i * 3, " %02x", req[i]);
+ DBG (8, "%s%s\n", prefix, buf);
+}
+
+#endif /* not NDEBUG */
+
+/** Dump a request packet if the debug level is at 8 or above.
+ *
+ * @param prefix String printed before the packet contents.
+ * @param req The request packet to be dumped.
+ */
+#define DUMP_REQ(prefix, req) \
+ do { IF_DBG( if (DBG_LEVEL >= 8) dump_req ((prefix), (req)); ) } while (0)
+
+
+SANE_Status
+gt68xx_device_new (GT68xx_Device ** dev_return)
+{
+ GT68xx_Device *dev;
+
+ DBG (7, "gt68xx_device_new: enter\n");
+ if (!dev_return)
+ return SANE_STATUS_INVAL;
+
+ dev = (GT68xx_Device *) malloc (sizeof (GT68xx_Device));
+
+ if (!dev)
+ {
+ DBG (3, "gt68xx_device_new: couldn't malloc %lu bytes for device\n",
+ (u_long) sizeof (GT68xx_Device));
+ *dev_return = 0;
+ return SANE_STATUS_NO_MEM;
+ }
+ *dev_return = dev;
+
+ memset (dev, 0, sizeof (GT68xx_Device));
+
+ dev->fd = -1;
+ dev->active = SANE_FALSE;
+
+ dev->model = NULL;
+ dev->command_set_private = NULL;
+
+ dev->read_buffer = NULL;
+ dev->read_buffer_size = 32768;
+
+ dev->manual_selection = SANE_FALSE;
+
+#ifdef USE_FORK
+ dev->shm_channel = NULL;
+#endif /* USE_FORK */
+
+ DBG (7, "gt68xx_device_new:: leave: ok\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+gt68xx_device_free (GT68xx_Device * dev)
+{
+ DBG (7, "gt68xx_device_free: enter: dev=%p\n", (void *) dev);
+ if (dev)
+ {
+ if (dev->active)
+ gt68xx_device_deactivate (dev);
+
+ if (dev->fd != -1)
+ gt68xx_device_close (dev);
+
+ if (dev->model && dev->model->allocated)
+ {
+ DBG (7, "gt68xx_device_free: freeing model data %p\n",
+ (void *) dev->model);
+ free (dev->model);
+ }
+
+ DBG (7, "gt68xx_device_free: freeing dev\n");
+ free (dev);
+ }
+ DBG (7, "gt68xx_device_free: leave: ok\n");
+ return SANE_STATUS_GOOD;
+}
+
+static GT68xx_USB_Device_Entry *
+gt68xx_find_usb_device_entry (SANE_Word vendor, SANE_Word product)
+{
+ GT68xx_USB_Device_Entry *entry;
+
+ for (entry = gt68xx_usb_device_list; entry->model; ++entry)
+ {
+ if (vendor == entry->vendor && product == entry->product)
+ return entry;
+ }
+
+ return NULL;
+}
+
+static SANE_Status
+gt68xx_device_identify (GT68xx_Device * dev)
+{
+ SANE_Status status;
+ SANE_Word vendor, product;
+ GT68xx_USB_Device_Entry *entry;
+
+ CHECK_DEV_OPEN (dev, "gt68xx_device_identify");
+
+ status = sanei_usb_get_vendor_product (dev->fd, &vendor, &product);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (3, "gt68xx_device_identify: error getting USB id: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ entry = gt68xx_find_usb_device_entry (vendor, product);
+
+ if (entry)
+ {
+ dev->model = entry->model;
+ }
+ else
+ {
+ dev->model = NULL;
+ DBG (3, "gt68xx_device_identify: unknown USB device (vendor 0x%04x, "
+ "product 0x%04x)\n", vendor, product);
+ return SANE_STATUS_INVAL;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+gt68xx_device_open (GT68xx_Device * dev, const char *dev_name)
+{
+ SANE_Status status;
+ SANE_Int fd;
+
+ DBG (7, "gt68xx_device_open: enter: dev=%p\n", (void *) dev);
+
+ CHECK_DEV_NOT_NULL (dev, "gt68xx_device_open");
+
+ if (dev->fd != -1)
+ {
+ DBG (3, "gt68xx_device_open: device already open\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ status = sanei_usb_open (dev_name, &fd);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (3, "gt68xx_device_open: sanei_usb_open failed: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ dev->fd = fd;
+
+ if (!dev->model)
+ gt68xx_device_identify (dev);
+
+ DBG (7, "gt68xx_device_open: leave: ok\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+gt68xx_device_close (GT68xx_Device * dev)
+{
+ DBG (7, "gt68xx_device_close: enter: dev=%p\n", (void *) dev);
+
+ CHECK_DEV_OPEN (dev, "gt68xx_device_close");
+
+ if (dev->active)
+ gt68xx_device_deactivate (dev);
+
+ sanei_usb_close (dev->fd);
+ dev->fd = -1;
+
+ DBG (7, "gt68xx_device_close: leave: ok\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Bool
+gt68xx_device_is_configured (GT68xx_Device * dev)
+{
+ if (dev && dev->model && dev->model->command_set)
+ return SANE_TRUE;
+ else
+ return SANE_FALSE;
+}
+
+SANE_Status
+gt68xx_device_set_model (GT68xx_Device * dev, GT68xx_Model * model)
+{
+ if (dev->active)
+ {
+ DBG (3, "gt68xx_device_set_model: device already active\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ if (dev->model && dev->model->allocated)
+ free (dev->model);
+
+ dev->model = model;
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Bool
+gt68xx_device_get_model (SANE_String name, GT68xx_Model ** model)
+{
+ GT68xx_USB_Device_Entry *entry;
+
+ for (entry = gt68xx_usb_device_list; entry->model; ++entry)
+ {
+ if (strcmp (name, entry->model->name) == 0)
+ {
+ *model = entry->model;
+ return SANE_TRUE;
+ }
+ }
+ return SANE_FALSE;
+}
+
+
+SANE_Status
+gt68xx_device_activate (GT68xx_Device * dev)
+{
+ SANE_Status status;
+ CHECK_DEV_OPEN (dev, "gt68xx_device_activate");
+ if (dev->active)
+ {
+ DBG (3, "gt68xx_device_activate: device already active\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ if (!gt68xx_device_is_configured (dev))
+ {
+ DBG (3, "gt68xx_device_activate: device is not configured\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ DBG (7, "gt68xx_device_activate: model \"%s\"\n", dev->model->name);
+ if (dev->model->command_set->activate)
+ {
+ status = (*dev->model->command_set->activate) (dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (3, "gt68xx_device_activate: command-set-specific "
+ "activate failed: %s\n", sane_strstatus (status));
+ return status;
+ }
+ }
+ dev->afe = malloc (sizeof (*dev->afe));
+ dev->exposure = malloc (sizeof (*dev->exposure));
+ if (!dev->afe || !dev->exposure)
+ return SANE_STATUS_NO_MEM;
+ memcpy (dev->afe, &dev->model->afe_params, sizeof (*dev->afe));
+ memcpy (dev->exposure, &dev->model->exposure, sizeof (*dev->exposure));
+ dev->gamma_value = dev->model->default_gamma_value;
+ dev->active = SANE_TRUE;
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+gt68xx_device_deactivate (GT68xx_Device * dev)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ CHECK_DEV_ACTIVE (dev, "gt68xx_device_deactivate");
+ if (dev->read_active)
+ gt68xx_device_read_finish (dev);
+ if (dev->model->command_set->deactivate)
+ {
+ status = (*dev->model->command_set->deactivate) (dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (3,
+ "gt68xx_device_deactivate: command set-specific deactivate failed: %s\n",
+ sane_strstatus (status));
+ /* proceed with deactivate anyway */
+ }
+ }
+ if (dev->afe)
+ free (dev->afe);
+ dev->afe = 0;
+ if (dev->exposure)
+ free (dev->exposure);
+ dev->exposure = 0;
+ dev->active = SANE_FALSE;
+ return status;
+}
+
+SANE_Status
+gt68xx_device_memory_write (GT68xx_Device * dev,
+ SANE_Word addr, SANE_Word size, SANE_Byte * data)
+{
+ SANE_Status status;
+ DBG (8,
+ "gt68xx_device_memory_write: dev=%p, addr=0x%x, size=0x%x, data=%p\n",
+ (void *) dev, addr, size, data);
+ CHECK_DEV_ACTIVE (dev, "gt68xx_device_memory_write");
+ status =
+ sanei_usb_control_msg (dev->fd, 0x40,
+ dev->model->command_set->request,
+ dev->model->command_set->memory_write_value,
+ addr, size, data);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (3,
+ "gt68xx_device_memory_write: sanei_usb_control_msg failed: %s\n",
+ sane_strstatus (status));
+ }
+ return status;
+}
+
+SANE_Status
+gt68xx_device_memory_read (GT68xx_Device * dev,
+ SANE_Word addr, SANE_Word size, SANE_Byte * data)
+{
+ SANE_Status status;
+ DBG (8,
+ "gt68xx_device_memory_read: dev=%p, addr=0x%x, size=0x%x, data=%p\n",
+ (void *) dev, addr, size, data);
+ CHECK_DEV_ACTIVE (dev, "gt68xx_device_memory_read");
+ status =
+ sanei_usb_control_msg (dev->fd, 0xc0,
+ dev->model->command_set->request,
+ dev->model->command_set->memory_read_value,
+ addr, size, data);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (3,
+ "gt68xx_device_memory_read: sanei_usb_control_msg failed: %s\n",
+ sane_strstatus (status));
+ }
+
+ return status;
+}
+
+static SANE_Status
+gt68xx_device_generic_req (GT68xx_Device * dev,
+ SANE_Byte request_type, SANE_Word request,
+ SANE_Word cmd_value, SANE_Word cmd_index,
+ SANE_Word res_value, SANE_Word res_index,
+ GT68xx_Packet cmd, GT68xx_Packet res,
+ size_t res_size)
+{
+ SANE_Status status;
+ DBG (7, "gt68xx_device_generic_req: command=0x%02x\n", cmd[0]);
+ DUMP_REQ (">>", cmd);
+ CHECK_DEV_ACTIVE (dev, "gt68xx_device_generic_req");
+ status = sanei_usb_control_msg (dev->fd,
+ request_type, request, cmd_value,
+ cmd_index, GT68XX_PACKET_SIZE, cmd);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (3, "gt68xx_device_generic_req: writing command failed: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ memset (res, 0, sizeof (GT68xx_Packet));
+ status = sanei_usb_control_msg (dev->fd,
+ request_type | 0x80, request,
+ res_value, res_index, res_size, res);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (3, "gt68xx_device_generic_req: reading response failed: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ DUMP_REQ ("<<", res);
+ return status;
+}
+
+SANE_Status
+gt68xx_device_req (GT68xx_Device * dev, GT68xx_Packet cmd, GT68xx_Packet res)
+{
+ GT68xx_Command_Set *command_set = dev->model->command_set;
+ return gt68xx_device_generic_req (dev,
+ command_set->request_type,
+ command_set->request,
+ command_set->send_cmd_value,
+ command_set->send_cmd_index,
+ command_set->recv_res_value,
+ command_set->recv_res_index, cmd,
+ res, GT68XX_PACKET_SIZE);
+}
+
+SANE_Status
+gt68xx_device_small_req (GT68xx_Device * dev, GT68xx_Packet cmd,
+ GT68xx_Packet res)
+{
+ GT68xx_Command_Set *command_set = dev->model->command_set;
+ GT68xx_Packet fixed_cmd;
+ int i;
+ for (i = 0; i < 8; ++i)
+ memcpy (fixed_cmd + i * 8, cmd, 8);
+ return gt68xx_device_generic_req (dev,
+ command_set->request_type,
+ command_set->request,
+ command_set->send_small_cmd_value,
+ command_set->send_small_cmd_index,
+ command_set->recv_small_res_value,
+ command_set->recv_small_res_index,
+ fixed_cmd, res, 0x08);
+}
+
+
+SANE_Status
+gt68xx_device_download_firmware (GT68xx_Device * dev,
+ SANE_Byte * data, SANE_Word size)
+{
+ CHECK_DEV_ACTIVE (dev, "gt68xx_device_download_firmware");
+ if (dev->model->command_set->download_firmware)
+ return (*dev->model->command_set->download_firmware) (dev, data, size);
+ else
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+SANE_Status
+gt68xx_device_get_power_status (GT68xx_Device * dev, SANE_Bool * power_ok)
+{
+ CHECK_DEV_ACTIVE (dev, "gt68xx_device_get_power_status");
+ if (dev->model->command_set->get_power_status)
+ return (*dev->model->command_set->get_power_status) (dev, power_ok);
+ else
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+SANE_Status
+gt68xx_device_get_ta_status (GT68xx_Device * dev, SANE_Bool * ta_attached)
+{
+ CHECK_DEV_ACTIVE (dev, "gt68xx_device_get_ta_status");
+ if (dev->model->command_set->get_ta_status)
+ return (*dev->model->command_set->get_ta_status) (dev, ta_attached);
+ else
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+SANE_Status
+gt68xx_device_lamp_control (GT68xx_Device * dev, SANE_Bool fb_lamp,
+ SANE_Bool ta_lamp)
+{
+ CHECK_DEV_ACTIVE (dev, "gt68xx_device_lamp_control");
+ if (dev->model->command_set->lamp_control)
+ return (*dev->model->command_set->lamp_control) (dev, fb_lamp, ta_lamp);
+ else
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+SANE_Status
+gt68xx_device_is_moving (GT68xx_Device * dev, SANE_Bool * moving)
+{
+ CHECK_DEV_ACTIVE (dev, "gt68xx_device_is_moving");
+ if (dev->model->command_set->is_moving)
+ return (*dev->model->command_set->is_moving) (dev, moving);
+ else
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+/* currently not used */
+#if 0
+static SANE_Status
+gt68xx_device_move_relative (GT68xx_Device * dev, SANE_Int distance)
+{
+ CHECK_DEV_ACTIVE (dev, "gt68xx_device_move_relative");
+ if (dev->model->command_set->move_relative)
+ return (*dev->model->command_set->move_relative) (dev, distance);
+ else
+ return SANE_STATUS_UNSUPPORTED;
+}
+#endif
+
+SANE_Status
+gt68xx_device_carriage_home (GT68xx_Device * dev)
+{
+ CHECK_DEV_ACTIVE (dev, "gt68xx_device_carriage_home");
+ if (dev->model->command_set->carriage_home)
+ return (*dev->model->command_set->carriage_home) (dev);
+ else
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+SANE_Status
+gt68xx_device_paperfeed (GT68xx_Device * dev)
+{
+ CHECK_DEV_ACTIVE (dev, "gt68xx_device_paperfeed");
+ if (dev->model->command_set->paperfeed)
+ return (*dev->model->command_set->paperfeed) (dev);
+ else
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+SANE_Status
+gt68xx_device_start_scan (GT68xx_Device * dev)
+{
+ CHECK_DEV_ACTIVE (dev, "gt68xx_device_start_scan");
+ if (dev->model->command_set->start_scan)
+ return (*dev->model->command_set->start_scan) (dev);
+ else
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+SANE_Status
+gt68xx_device_read_scanned_data (GT68xx_Device * dev, SANE_Bool * ready)
+{
+ CHECK_DEV_ACTIVE (dev, "gt68xx_device_read_scanned_data");
+ if (dev->model->command_set->read_scanned_data)
+ return (*dev->model->command_set->read_scanned_data) (dev, ready);
+ else
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+SANE_Status
+gt68xx_device_setup_scan (GT68xx_Device * dev,
+ GT68xx_Scan_Request * request,
+ GT68xx_Scan_Action action,
+ GT68xx_Scan_Parameters * params)
+{
+ CHECK_DEV_ACTIVE (dev, "gt68xx_device_setup_scan");
+ if (dev->model->command_set->setup_scan)
+ return (*dev->model->command_set->setup_scan) (dev, request, action,
+ params);
+ else
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+SANE_Status
+gt68xx_device_set_afe (GT68xx_Device * dev, GT68xx_AFE_Parameters * params)
+{
+ CHECK_DEV_ACTIVE (dev, "gt68xx_device_set_afe");
+ if (dev->model->command_set->set_afe)
+ return (*dev->model->command_set->set_afe) (dev, params);
+ else
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+SANE_Status
+gt68xx_device_set_exposure_time (GT68xx_Device * dev,
+ GT68xx_Exposure_Parameters * params)
+{
+ CHECK_DEV_ACTIVE (dev, "gt68xx_device_set_exposure_time");
+ if (dev->model->command_set->set_exposure_time)
+ return (*dev->model->command_set->set_exposure_time) (dev, params);
+ else
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+SANE_Status
+gt68xx_device_stop_scan (GT68xx_Device * dev)
+{
+ CHECK_DEV_ACTIVE (dev, "gt68xx_device_stop_scan");
+ if (dev->model->command_set->stop_scan)
+ return (*dev->model->command_set->stop_scan) (dev);
+ else
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+SANE_Status
+gt68xx_device_read_raw (GT68xx_Device * dev, SANE_Byte * buffer,
+ size_t * size)
+{
+ SANE_Status status;
+ CHECK_DEV_ACTIVE (dev, "gt68xx_device_read_raw");
+ DBG (7, "gt68xx_device_read_raw: enter: size=%lu\n", (unsigned long) *size);
+ status = sanei_usb_read_bulk (dev->fd, buffer, size);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (3, "gt68xx_device_read_raw: bulk read failed: %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ DBG (7, "gt68xx_device_read_raw: leave: size=%lu\n", (unsigned long) *size);
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+gt68xx_device_set_read_buffer_size (GT68xx_Device * dev, size_t buffer_size)
+{
+ CHECK_DEV_NOT_NULL (dev, "gt68xx_device_set_read_buffer_size");
+ if (dev->read_active)
+ {
+ DBG (3, "gt68xx_device_set_read_buffer_size: BUG: read already "
+ "active\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ buffer_size = (buffer_size + 63UL) & ~63UL;
+ if (buffer_size > 0)
+ {
+ dev->requested_buffer_size = buffer_size;
+ return SANE_STATUS_GOOD;
+ }
+
+ DBG (3, "gt68xx_device_set_read_buffer_size: bad buffer size\n");
+ return SANE_STATUS_INVAL;
+}
+
+SANE_Status
+gt68xx_device_read_prepare (GT68xx_Device * dev,
+ size_t expected_count, SANE_Bool final_scan)
+{
+ size_t buffer_size;
+ CHECK_DEV_ACTIVE (dev, "gt68xx_device_read_prepare");
+ if (dev->read_active)
+ {
+ DBG (3, "gt68xx_device_read_prepare: read already active\n");
+ return SANE_STATUS_INVAL;
+ }
+ DBG (5, "gt68xx_device_read_prepare: total size: %lu bytes\n",
+ (unsigned long) expected_count);
+ buffer_size = dev->requested_buffer_size;
+ DBG (5, "gt68xx_device_read_prepare: requested buffer size: %lu\n",
+ (unsigned long) buffer_size);
+ if (buffer_size > expected_count)
+ {
+ buffer_size = (expected_count + 63UL) & ~63UL;
+ }
+ DBG (5, "gt68xx_device_read_prepare: real size: %lu\n",
+ (unsigned long) buffer_size);
+ dev->read_buffer_size = buffer_size;
+ dev->read_buffer = (SANE_Byte *) malloc (buffer_size);
+ if (!dev->read_buffer)
+ {
+ DBG (3,
+ "gt68xx_device_read_prepare: not enough memory for the read buffer (%lu bytes)\n",
+ (unsigned long) buffer_size);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ dev->read_active = SANE_TRUE;
+ dev->final_scan = final_scan;
+ dev->read_pos = dev->read_bytes_in_buffer = 0;
+ dev->read_bytes_left = expected_count;
+ return SANE_STATUS_GOOD;
+}
+
+#ifdef USE_FORK
+
+static SANE_Status
+gt68xx_reader_process (GT68xx_Device * dev)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ SANE_Int buffer_id;
+ SANE_Byte *buffer_addr;
+ size_t size;
+ SANE_Int line = 0;
+ size_t read_bytes_left = dev->read_bytes_left;
+ shm_channel_writer_init (dev->shm_channel);
+ while (read_bytes_left > 0)
+ {
+ status = shm_channel_writer_get_buffer (dev->shm_channel,
+ &buffer_id, &buffer_addr);
+ if (status != SANE_STATUS_GOOD)
+ break;
+ DBG (9, "gt68xx_reader_process: buffer %d: get\n", buffer_id);
+ size = dev->read_buffer_size;
+ DBG (9, "gt68xx_reader_process: buffer %d: trying to read %lu bytes "
+ "(%lu bytes left, line %d)\n", buffer_id, (unsigned long) size,
+ (unsigned long) read_bytes_left, line);
+ status = gt68xx_device_read_raw (dev, buffer_addr, &size);
+ if (status != SANE_STATUS_GOOD)
+ break;
+ DBG (9,
+ "gt68xx_reader_process: buffer %d: read %lu bytes (line %d)\n",
+ buffer_id, (unsigned long) size, line);
+ status =
+ shm_channel_writer_put_buffer (dev->shm_channel, buffer_id, size);
+ if (status != SANE_STATUS_GOOD)
+ break;
+ DBG (9, "gt68xx_reader_process: buffer %d: put\n", buffer_id);
+ read_bytes_left -= size;
+ line++;
+ }
+ DBG (9, "gt68xx_reader_process: finished, now sleeping\n");
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ sleep (5 * 60); /* wait until we are killed (or timeout) */
+ shm_channel_writer_close (dev->shm_channel);
+ return status;
+}
+
+static SANE_Status
+gt68xx_device_read_start_fork (GT68xx_Device * dev)
+{
+ SANE_Status status;
+ int pid;
+ if (dev->shm_channel)
+ {
+ DBG (3,
+ "gt68xx_device_read_start_fork: BUG: shm_channel already created\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ status =
+ shm_channel_new (dev->read_buffer_size, SHM_BUFFERS, &dev->shm_channel);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (3,
+ "gt68xx_device_read_start_fork: cannot create shared memory channel: "
+ "%s\n", sane_strstatus (status));
+ dev->shm_channel = NULL;
+ return status;
+ }
+
+ pid = fork ();
+ if (pid == -1)
+ {
+ DBG (3, "gt68xx_device_read_start_fork: cannot fork: %s\n",
+ strerror (errno));
+ shm_channel_free (dev->shm_channel);
+ dev->shm_channel = NULL;
+ return SANE_STATUS_NO_MEM;
+ }
+
+ if (pid == 0)
+ {
+ /* Child process */
+ status = gt68xx_reader_process (dev);
+ _exit (status);
+ }
+ else
+ {
+ /* Parent process */
+ dev->reader_pid = pid;
+ shm_channel_reader_init (dev->shm_channel);
+ shm_channel_reader_start (dev->shm_channel);
+ return SANE_STATUS_GOOD;
+ }
+}
+
+#endif /* USE_FORK */
+
+
+static SANE_Status
+gt68xx_device_read_start (GT68xx_Device * dev)
+{
+ CHECK_DEV_ACTIVE (dev, "gt68xx_device_read_start");
+#ifdef USE_FORK
+ /* Don't fork a separate process for every calibration scan. */
+ if (dev->final_scan)
+ return gt68xx_device_read_start_fork (dev);
+#endif /* USE_FORK */
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+gt68xx_device_read (GT68xx_Device * dev, SANE_Byte * buffer, size_t * size)
+{
+ SANE_Status status;
+ size_t byte_count = 0;
+ size_t left_to_read = *size;
+ size_t transfer_size, block_size, raw_block_size;
+#ifdef USE_FORK
+ SANE_Int buffer_id;
+ SANE_Byte *buffer_addr;
+ SANE_Int buffer_bytes;
+#endif /* USE_FORK */
+ CHECK_DEV_ACTIVE (dev, "gt68xx_device_read");
+ if (!dev->read_active)
+ {
+ DBG (3, "gt68xx_device_read: read not active\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ while (left_to_read > 0)
+ {
+ if (dev->read_bytes_in_buffer == 0)
+ {
+ block_size = dev->read_buffer_size;
+ if (block_size > dev->read_bytes_left)
+ block_size = dev->read_bytes_left;
+ if (block_size == 0)
+ break;
+ raw_block_size = (block_size + 63UL) & ~63UL;
+ DBG (7, "gt68xx_device_read: trying to read %ld bytes\n",
+ (long) raw_block_size);
+#ifdef USE_FORK
+ if (dev->shm_channel)
+ {
+ status = shm_channel_reader_get_buffer (dev->shm_channel,
+ &buffer_id,
+ &buffer_addr,
+ &buffer_bytes);
+ if (status == SANE_STATUS_GOOD && buffer_addr != NULL)
+ {
+ DBG (9, "gt68xx_device_read: buffer %d: get\n", buffer_id);
+ memcpy (dev->read_buffer, buffer_addr, buffer_bytes);
+ shm_channel_reader_put_buffer (dev->shm_channel, buffer_id);
+ DBG (9, "gt68xx_device_read: buffer %d: put\n", buffer_id);
+ }
+ }
+ else
+#endif /* USE_FORK */
+ status = gt68xx_device_read_raw (dev, dev->read_buffer,
+ &raw_block_size);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (3, "gt68xx_device_read: read failed\n");
+ return status;
+ }
+ dev->read_pos = 0;
+ dev->read_bytes_in_buffer = block_size;
+ dev->read_bytes_left -= block_size;
+ }
+
+ transfer_size = left_to_read;
+ if (transfer_size > dev->read_bytes_in_buffer)
+ transfer_size = dev->read_bytes_in_buffer;
+ if (transfer_size > 0)
+ {
+ memcpy (buffer, dev->read_buffer + dev->read_pos, transfer_size);
+ dev->read_pos += transfer_size;
+ dev->read_bytes_in_buffer -= transfer_size;
+ byte_count += transfer_size;
+ left_to_read -= transfer_size;
+ buffer += transfer_size;
+ }
+ }
+
+ *size = byte_count;
+ if (byte_count == 0)
+ return SANE_STATUS_EOF;
+ else
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+gt68xx_device_read_finish (GT68xx_Device * dev)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ CHECK_DEV_ACTIVE (dev, "gt68xx_device_read_finish");
+ if (!dev->read_active)
+ {
+ DBG (3, "gt68xx_device_read_finish: read not active\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ DBG (7, "gt68xx_device_read_finish: read_bytes_left = %ld\n",
+ (long) dev->read_bytes_left);
+#ifdef USE_FORK
+ if (dev->reader_pid != 0)
+ {
+ int pid_status;
+ /* usleep (100000); */
+ DBG (7, "gt68xx_device_read_finish: trying to kill reader process\n");
+ kill (dev->reader_pid, SIGKILL);
+ waitpid (dev->reader_pid, &pid_status, 0);
+ if (WIFEXITED (pid_status))
+ status = WEXITSTATUS (pid_status);
+ DBG (7, "gt68xx_device_read_finish: reader process killed\n");
+ dev->reader_pid = 0;
+ }
+ if (dev->shm_channel)
+ {
+ shm_channel_free (dev->shm_channel);
+ dev->shm_channel = NULL;
+ }
+
+#endif /* USE_FORK */
+
+ free (dev->read_buffer);
+ dev->read_buffer = NULL;
+ dev->read_active = SANE_FALSE;
+ DBG (7, "gt68xx_device_read_finish: exit (%s)\n", sane_strstatus (status));
+ return status;
+}
+
+static SANE_Status
+gt68xx_device_check_result (GT68xx_Packet res, SANE_Byte command)
+{
+ if (res[0] != 0)
+ {
+ DBG (1, "gt68xx_device_check_result: result was %2X %2X "
+ "(expected: %2X %2X)\n", res[0], res[1], 0, command);
+ return SANE_STATUS_IO_ERROR;
+ }
+ /* The Gt681xfw.usb firmware doesn't return the command byte
+ in the second byte, so we can't rely on that test */
+ if (res[1] != command)
+ DBG (5, "gt68xx_device_check_result: warning: result was %2X %2X "
+ "(expected: %2X %2X)\n", res[0], res[1], 0, command);
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+gt68xx_device_get_id (GT68xx_Device * dev)
+{
+ CHECK_DEV_ACTIVE (dev, "gt68xx_device_get_id");
+ if (dev->model->command_set->get_id)
+ return (*dev->model->command_set->get_id) (dev);
+ else
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+static void
+gt68xx_device_fix_descriptor (GT68xx_Device * dev)
+{
+ SANE_Byte data[8];
+ sanei_usb_control_msg (dev->fd, 0x80, 0x06, 0x01 << 8, 0, 8, data);
+}
+
+
+/* vim: set sw=2 cino=>2se-1sn-1s{s^-1st0(0u0 smarttab expandtab: */