summaryrefslogtreecommitdiff
path: root/backend/stv680.c
diff options
context:
space:
mode:
Diffstat (limited to 'backend/stv680.c')
-rw-r--r--backend/stv680.c2180
1 files changed, 2180 insertions, 0 deletions
diff --git a/backend/stv680.c b/backend/stv680.c
new file mode 100644
index 0000000..15a8472
--- /dev/null
+++ b/backend/stv680.c
@@ -0,0 +1,2180 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2004 - 2006 Gerard Klaver <gerard at gkall dot hobby dot nl>
+
+ The teco2 and gl646 backend (Frank Zago) are used as a template for
+ this backend.
+
+ For the usb commands and bayer decoding parts of the following
+ program are used:
+ The pencam2 program (GNU GPL license 2)
+
+ For the usb commands parts of the following programs are used:
+ The libgphoto2 (camlib stv0680) (GNU GPL license 2)
+ The stv680.c/.h kernel module (GNU GPL license 2)
+
+ For the stv680_add_text routine the add_text routine and font_6x11.h file
+ are taken from the webcam.c file, part of xawtv program,
+ (c) 1998-2002 Gerd Knorr (GNU GPL license 2).
+
+ 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.
+ ------------------------------------------------------------------
+*/
+
+/* $Id$
+
+ stv680 vidcam driver Gerard Klaver
+*/
+
+/*SANE FLOW DIAGRAM
+
+ - sane_init() : initialize backend, attach vidcams
+ . - sane_get_devices() : query list of vidcam devices
+ . - sane_open() : open a particular vidcam device
+ . . - sane_set_io_mode : set blocking mode
+ . . - sane_get_select_fd : get vidcam fd
+ . . - sane_get_option_descriptor() : get option information
+ . . - sane_control_option() : change option values
+ . .
+ . . - sane_start() : start image acquisition
+ . . - sane_get_parameters() : returns actual scan parameters
+ . . - sane_read() : read image data (from pipe)
+ . . (sane_read called multiple times;
+ . . after sane_read returns EOF)
+ . . go back to sane_start() if more frames desired
+ . . - sane_cancel() : cancel operation
+ . - sane_close() : close opened vidcam device
+ - sane_exit() : terminate use of backend
+*/
+/*--------------------------------------------------------------------------*/
+
+#define BUILD 1 /* 2004/09/09 update 20-04-2006 */
+#define BACKEND_NAME stv680
+#define STV680_CONFIG_FILE "stv680.conf"
+
+/* --------------------- SANE INTERNATIONALISATION ------------------------ */
+
+/* must be first include */
+#include "../include/sane/config.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "../include/sane/sane.h"
+#include "../include/sane/sanei.h"
+#include "../include/sane/saneopts.h"
+#include "../include/sane/sanei_usb.h"
+#include "../include/sane/sanei_debug.h"
+#include "../include/sane/sanei_backend.h"
+#include "../include/sane/sanei_config.h"
+#include "../include/lassert.h"
+
+/* for add-text routine */
+#include <time.h>
+#include "../include/font_6x11.h"
+/*-----------------------*/
+
+#include "stv680.h"
+
+#define TIMEOUT 1000
+
+/*--------------------------------------------------------------------------*/
+/* Lists of possible scan modes. */
+static SANE_String_Const scan_mode_list[] = {
+ COLOR_RGB_STR,
+ COLOR_RGB_TEXT_STR,
+ SANE_VALUE_SCAN_MODE_COLOR,
+ COLOR_RAW_STR,
+
+ NULL
+};
+
+/*-----------------------------------------minium, maximum, quantization----*/
+static const SANE_Range brightness_range = { -128, 128, 1 };
+
+static const SANE_Range red_level_range = { -32, 32, 1 };
+
+static const SANE_Range green_level_range = { -32, 32, 1 };
+
+static const SANE_Range blue_level_range = { -32, 32, 1 };
+
+/*--------------------------------------------------------------------------*/
+
+static const struct dpi_color_adjust stv680_dpi_color_adjust[] = {
+
+ /*dpi, y, x, color sequence R G or B */
+ {176, 144, 0, 1, 2}, /* QCIF selected by dev->CIF */
+ {352, 288, 0, 1, 2}, /* CIF ,, */
+ {160, 120, 0, 1, 2}, /* QSIF ,, dev->VGA */
+ {320, 240, 0, 1, 2}, /* QVGA (SIF) ,, */
+ {640, 480, 0, 1, 2}, /* VGA ,, */
+ /* must be the last entry */
+ {0, 0, 0, 0, 0}
+};
+
+static const struct vidcam_hardware vidcams[] = {
+
+ {0x0553, 0x0202, USB_CLASS_VENDOR_SPEC,
+ "AIPTEK", "PENCAM STV0680",
+ stv680_dpi_color_adjust},
+
+ {0x04c8, 0x0722, USB_CLASS_VENDOR_SPEC,
+ "Konica", "e-mini",
+ stv680_dpi_color_adjust},
+
+ {0x1183, 0x0001, USB_CLASS_VENDOR_SPEC,
+ "DigitalDream", "l'espion XS",
+ stv680_dpi_color_adjust},
+
+ {0x041e, 0x4007, USB_CLASS_VENDOR_SPEC,
+ "Creative", "WebCam Go mini",
+ stv680_dpi_color_adjust}
+};
+
+/* List of vidcams attached. */
+static Stv680_Vidcam *first_dev = NULL;
+static int num_devices = 0;
+/* used by sane_get_devices */
+static const SANE_Device **devlist = NULL;
+
+/*----------------------------------------------------------- */
+
+/* Local functions. */
+
+/* Display a buffer in the log. Display by lines of 16 bytes. */
+static void
+hexdump (int level, const char *comment, unsigned char *buf, const int length)
+{
+ int i;
+ char line[128];
+ char *ptr;
+ char asc_buf[17];
+ char *asc_ptr;
+
+ DBG (level, " %s\n", comment);
+
+ i = 0;
+ goto start;
+
+ do
+ {
+ if (i < length)
+ {
+ ptr += sprintf (ptr, " %2.2x", *buf);
+
+ if (*buf >= 32 && *buf <= 127)
+ {
+ asc_ptr += sprintf (asc_ptr, "%c", *buf);
+ }
+ else
+ {
+ asc_ptr += sprintf (asc_ptr, ".");
+ }
+ }
+ else
+ {
+ /* After the length; do nothing. */
+ ptr += sprintf (ptr, " ");
+ }
+
+ i++;
+ buf++;
+
+ if ((i % 16) == 0)
+ {
+ /* It's a new line */
+ DBG (level, " %s %s\n", line, asc_buf);
+
+ start:
+ ptr = line;
+ *ptr = '\0';
+ asc_ptr = asc_buf;
+ *asc_ptr = '\0';
+
+ ptr += sprintf (ptr, " %3.3d:", i);
+ }
+ }
+ while (i < ((length + 15) & ~15));
+}
+
+/* Returns the length of the longest string, including the terminating
+ * character. */
+static size_t
+max_string_size (SANE_String_Const strings[])
+{
+ size_t size, max_size = 0;
+ int i;
+
+ for (i = 0; strings[i]; ++i)
+ {
+ size = strlen (strings[i]) + 1;
+ if (size > max_size)
+ {
+ max_size = size;
+ }
+ }
+ return max_size;
+}
+
+/* Initialize a vidcam entry. Return an allocated vidcam with some
+ * */
+static Stv680_Vidcam *
+stv680_init (void)
+{
+ Stv680_Vidcam *dev;
+
+ DBG (DBG_proc, "stv680_init: enter\n");
+
+ /* Allocate a new vidcam entry. */
+ dev = malloc (sizeof (Stv680_Vidcam));
+ if (dev == NULL)
+ {
+ return NULL;
+ }
+ memset (dev, 0, sizeof (Stv680_Vidcam));
+
+/* Allocate the windoww buffer*/
+ dev->windoww_size = 0x20;
+ dev->windoww = malloc (dev->windoww_size);
+ if (dev->windoww == NULL)
+ {
+ free (dev);
+ return NULL;
+ }
+
+/* Allocate the windowr buffer*/
+ dev->windowr_size = 0x20;
+ dev->windowr = malloc (dev->windowr_size);
+ if (dev->windowr == NULL)
+ {
+ free (dev->windoww);
+ free (dev);
+ return NULL;
+ }
+
+ dev->fd = -1;
+
+ DBG (DBG_proc, "stv680_init: exit\n");
+
+ return (dev);
+}
+
+static SANE_Status
+stv680_init_2 (Stv680_Vidcam * dev)
+{
+ SANE_Status status;
+
+ DBG (DBG_proc, "stv680_init_2: enter\n");
+
+ /* Allocate the buffer used to transfer the USB data */
+ /* Check for max. format image size so buffer size can
+ * be adjusted, format from camera is bayer 422 */
+ if (dev->CIF)
+ dev->buffer_size = 356 * 292;
+ if (dev->VGA)
+ dev->buffer_size = 644 * 484;
+ DBG (DBG_proc, "stv680_init_2: dev->bufffer = 0x%lx\n", (unsigned long) (size_t) dev->buffer_size);
+
+ dev->buffer = malloc (dev->buffer_size);
+
+ if (dev->buffer == NULL)
+ {
+ free (dev->windowr);
+ free (dev->windoww);
+ free (dev);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ /* Allocate the output buffer used for bayer conversion */
+ dev->output_size = dev->buffer_size * 3;
+
+ dev->output = malloc (dev->output_size);
+ if (dev->output == NULL)
+ {
+ free (dev->windowr);
+ free (dev->windoww);
+ free (dev->buffer);
+ free (dev);
+
+ return SANE_STATUS_NO_MEM;
+ }
+ dev->image_size = dev->buffer_size;
+
+ dev->image = malloc (dev->image_size);
+ if (dev->image == NULL)
+ {
+ free (dev->windowr);
+ free (dev->windoww);
+ free (dev->buffer);
+ free (dev->output);
+ free (dev);
+
+ return SANE_STATUS_NO_MEM;
+ }
+
+ DBG (DBG_proc, "stv680_init_2: exit\n");
+ status = SANE_STATUS_GOOD;
+ return status;
+}
+
+/* Closes an open vidcams. */
+static void
+stv680_close (Stv680_Vidcam * dev)
+{
+ DBG (DBG_proc, "stv680_close: enter \n");
+
+ if (dev->fd != -1)
+ {
+
+ DBG (DBG_proc, "stv680_close: fd !=-1 \n");
+ sanei_usb_close (dev->fd);
+ dev->fd = -1;
+ }
+
+ DBG (DBG_proc, "stv680_close: exit\n");
+}
+
+/* Frees the memory used by a vidcam. */
+static void
+stv680_free (Stv680_Vidcam * dev)
+{
+ int i;
+
+ DBG (DBG_proc, "stv680_free: enter\n");
+
+ if (dev == NULL)
+ return;
+
+ stv680_close (dev);
+ if (dev->devicename)
+ {
+ free (dev->devicename);
+ }
+ if (dev->buffer)
+ {
+ free (dev->buffer);
+ }
+ if (dev->output)
+ {
+ free (dev->output);
+ }
+ if (dev->image)
+ {
+ free (dev->image);
+ }
+ if (dev->windoww)
+ {
+ free (dev->windoww);
+ }
+ if (dev->windowr)
+ {
+ free (dev->windowr);
+ }
+ for (i = 1; i < OPT_NUM_OPTIONS; i++)
+ {
+ if (dev->opt[i].type == SANE_TYPE_STRING && dev->val[i].s)
+ {
+ free (dev->val[i].s);
+ }
+ }
+ if (dev->resolutions_list)
+ free (dev->resolutions_list);
+
+ free (dev);
+
+ DBG (DBG_proc, "stv680_free: exit\n");
+}
+
+static SANE_Status
+stv680_set_config (Stv680_Vidcam * dev, int configuration, int interface,
+ int alternate)
+{
+ SANE_Status status;
+ DBG (DBG_proc, "stv680_set_config: open\n");
+/* seems a problem on some systems (Debian amd64 unstable 19042006)
+ * not calling usb_set_configuration seems to help reason ?
+ status = sanei_usb_set_configuration (dev->fd, configuration);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "stv680_set_config: STV680 FAILED to set configuration %d\n",
+ configuration);
+ return status;
+ }
+*/
+ status = sanei_usb_claim_interface (dev->fd, interface);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "stv680_set_config: STV0680 FAILED to claim interface\n");
+ return status;
+ }
+
+ status = sanei_usb_set_altinterface (dev->fd, alternate);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "stv680_set_config: STV0680 FAILED to set alternate interface %d\n",
+ alternate);
+ return status;
+ }
+ DBG (DBG_proc,
+ "stv680_set_config: configuration=%d, interface=%d, alternate=%d\n",
+ configuration, interface, alternate);
+
+ DBG (DBG_proc, "stv680_set_config: exit\n");
+ return status;
+}
+
+/* Reset vidcam */
+static SANE_Status
+stv680_reset_vidcam (Stv680_Vidcam * dev)
+{
+ SANE_Status status;
+ size_t sizew; /* significant size of window */
+ size_t sizer;
+
+ DBG (DBG_proc, "stv680_reset_vidcam: enter\n");
+
+ sizew = dev->windoww_size;
+ sizer = dev->windowr_size;
+
+ memset (dev->windoww, 0, sizew);
+ memset (dev->windowr, 0, sizer);
+
+ sizew = 0x00; /* was 0 ? */
+ status =
+ sanei_usb_control_msg (dev->fd, 0x41, 0x0a, 0x0000, 0, sizew,
+ dev->windoww);
+ if (status != SANE_STATUS_GOOD)
+ {
+ return status;
+ }
+ DBG (DBG_proc, "stv680_reset_vidcam: CMDID_STOP_VIDEO end\n");
+
+ /* this is a high priority command; it stops all lower order commands */
+
+ sizew = 0x00; /* was 0 */
+ status =
+ sanei_usb_control_msg (dev->fd, 0x41, 0x04, 0x0000, 0, sizew,
+ dev->windoww);
+ if (status != SANE_STATUS_GOOD)
+ {
+ return status;
+ }
+ DBG (DBG_proc, "stv680_reset_vidcam: CMDID_CANCEL_TRANSACTION end\n");
+ sizer = 0x02;
+ DBG (DBG_proc, "stv680_reset_vidcam: CMDID_GET_LAST_ERROR begin\n");
+ status =
+ sanei_usb_control_msg (dev->fd, 0xc1, 0x80, 0x0000, 0, sizer,
+ dev->windowr);
+ if (status != SANE_STATUS_GOOD)
+ {
+ /* Get Last Error; 2 = busy */
+ DBG (DBG_proc,
+ "stv680_reset_vidcam: last error: %i, command = 0x%x\n",
+ dev->windowr[0], dev->windowr[1]);
+ return status;
+ }
+ else
+ {
+ DBG (DBG_proc, "stv680_reset_vidcam: Camera reset to idle mode.\n");
+ }
+ hexdump (DBG_info2, "stv680_reset_vidcam: CMDID_GET_LAST_ERROR",
+ dev->windowr, sizer);
+
+ /* configuration = 1, interface = 0, alternate = 0 */
+ /*
+ status = stv680_set_config (dev, 1, 0, 0);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "stv680_reset_vidcam: STV680 FAILED to set configure\n");
+ return status;
+ }
+ */
+ status = SANE_STATUS_GOOD;
+ DBG (DBG_proc, "stv680_reset_vidcam: exit\n");
+
+ return status;
+}
+
+/* Inquiry a device and returns TRUE if is supported. */
+static int
+stv680_identify_vidcam (Stv680_Vidcam * dev)
+{
+ SANE_Status status;
+ SANE_Word vendor;
+ SANE_Word product;
+ int i;
+ size_t sizer;
+
+ DBG (DBG_info, "stv680_identify_vidcam: open\n");
+
+ status = sanei_usb_get_vendor_product (dev->fd, &vendor, &product);
+
+ /* Loop through our list to make sure this scanner is supported. */
+ for (i = 0; i < NELEMS (vidcams); i++)
+ {
+ if (vidcams[i].vendor == vendor && vidcams[i].product == product)
+ {
+
+ DBG (DBG_info, "stv680_identify_vidcam: vidcam %x:%x is in list\n",
+ vendor, product);
+
+ dev->hw = &(vidcams[i]);
+
+ sizer = dev->windowr_size;
+ memset (dev->windowr, 0, sizer);
+
+ /* configuration = 1, interface = 0, alternate = 0 */
+ /*
+ status = stv680_set_config (dev, 1, 0, 0);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "stv680_vidcam_init: STV680 FAILED to set configure\n");
+ return status;
+ }
+ */
+ sizer = 0x02;
+ status =
+ sanei_usb_control_msg (dev->fd, 0xc1, 0x88, 0x5678, 0, sizer,
+ dev->windowr);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "stv680_identify_vidcam: this is not a STV680 (idVendor = %d, bProduct = %d) writing register failed with %s\n",
+ vendor, product, sane_strstatus (status));
+ return SANE_FALSE;
+ }
+ if ((dev->windowr[0] != 0x56) || (dev->windowr[1] != 0x78))
+ {
+ DBG (DBG_proc,
+ "STV(e): camera ping failed!!, checkvalue !=0x5678\n");
+ sizer = 0x02;
+ hexdump (DBG_info2, "urb12 window", dev->windowr, sizer);
+ return SANE_FALSE;
+ }
+ sizer = 0x02;
+ hexdump (DBG_info2, "urb12 ping data", dev->windowr, sizer);
+
+ sizer = 0x10;
+ status =
+ sanei_usb_control_msg (dev->fd, 0xc1, 0x85, 0x0000, 0, sizer,
+ dev->windowr);
+ if (status != SANE_STATUS_GOOD)
+ return SANE_FALSE;
+
+ hexdump (DBG_info2, "urbxx CMDID_GET_CAMERA_INFO", dev->windowr,
+ sizer);
+
+ dev->SupportedModes = dev->windowr[7];
+ i = dev->SupportedModes;
+ dev->QSIF = 0;
+ dev->CIF = 0;
+ dev->QCIF = 0;
+ dev->VGA = 0;
+ dev->QVGA = 0;
+ if (i & 1)
+ dev->CIF = 1;
+ if (i & 2)
+ dev->VGA = 1;
+ if (i & 8)
+ dev->QVGA = 1;
+ if (i & 4)
+ dev->QCIF = 1;
+ dev->QSIF = dev->QVGA; /* for software subsample */
+ if (dev->SupportedModes == 0)
+ {
+ DBG (DBG_proc,
+ "STV(e): There are NO supported STV680 modes!!\n");
+ i = -1;
+ return SANE_FALSE;
+ }
+ else
+ {
+ if (dev->VGA)
+ DBG (DBG_proc, "STV(i): VGA is supported\n");
+ if (dev->CIF)
+ DBG (DBG_proc, "STV(i): CIF is supported\n");
+ if (dev->QVGA)
+ DBG (DBG_proc, "STV(i): QVGA is supported\n");
+ if (dev->QCIF)
+ DBG (DBG_proc, "STV(i): QCIF is supported\n");
+ }
+
+ /* FW rev, ASIC rev, sensor ID */
+ DBG (DBG_proc, "STV(i): Firmware rev is %i.%i\n", dev->windowr[0],
+ dev->windowr[1]);
+ DBG (DBG_proc, "STV(i): ASIC rev is %i.%i\n", dev->windowr[2],
+ dev->windowr[3]);
+ DBG (DBG_proc, "STV(i): Sensor ID is %i.%i\n", (dev->windowr[4]),
+ (dev->windowr[5]));
+ /* Hardware config */
+ dev->HardwareConfig = dev->windowr[6];
+ i = dev->HardwareConfig;
+ /* Comms link, Flicker freq, Mem size */
+ if (i & 1)
+ DBG (DBG_proc, "STV(i): Comms link is serial\n");
+ else
+ DBG (DBG_proc, "STV(i): Comms link is USB\n");
+ if (i & 2)
+ DBG (DBG_proc, "STV(i): Flicker freq = 60 Hz\n");
+ else
+ DBG (DBG_proc, "STV(i): Flicker freq = 50 Hz\n");
+ if (i & 4)
+ DBG (DBG_proc, "STV(i): Mem size = 16Mbit\n");
+ else
+ DBG (DBG_proc, "STV(i): Mem size = 64Mbit\n");
+ if (i & 8)
+ DBG (DBG_proc, "STV(i): Thumbnails supported\n");
+ else
+ DBG (DBG_proc, "STV(i): Thumbnails N/A\n");
+ if (i & 16)
+ DBG (DBG_proc, "STV(i): Video supported\n");
+ else
+ DBG (DBG_proc, "STV(i): Video N/A\n");
+ if (i & 32)
+ DBG (DBG_proc, "STV(i): Startup Complete\n");
+ else
+ DBG (DBG_proc, "STV(i): Startup Not Complete\n");
+ if (i & 64)
+ DBG (DBG_proc, "STV(i): Monochrome\n");
+ else
+ DBG (DBG_proc, "STV(i): Color\n");
+ if (i & 128)
+ DBG (DBG_proc, "STV(i): Mem fitted\n");
+ else
+ DBG (DBG_proc, "STV(i): Mem not fitted\n");
+
+ DBG (DBG_proc, "urb 25 CMDID_GET_IMAGE_INFO\n");
+ sizer = 0x10;
+ status =
+ sanei_usb_control_msg (dev->fd, 0xc1, 0x86, 0x0000, 0, sizer,
+ dev->windowr);
+ if (status != SANE_STATUS_GOOD)
+ {
+ return SANE_FALSE;
+ }
+ hexdump (DBG_info2, "urb25 CMDID_GET_IMAGE_INFO", dev->windowr,
+ sizer);
+ DBG (DBG_proc, "STV(i): Current image index %d\n",
+ ((dev->windowr[0] << 8) + (dev->windowr[1])));
+ DBG (DBG_proc,
+ "If images are stored in camera, they will be lost when captering images is started!!!!!\n");
+ DBG (DBG_proc, "STV(i): Max images %d\n",
+ ((dev->windowr[2] << 8) + (dev->windowr[3])));
+ DBG (DBG_proc, "STV(i): Image width (pix) %d\n",
+ ((dev->windowr[4] << 8) + (dev->windowr[5])));
+ DBG (DBG_proc, "STV(i): Image height (pix) %d\n",
+ ((dev->windowr[6] << 8) + (dev->windowr[7])));
+ DBG (DBG_proc, "STV(i): Image size camera %d bytes\n",
+ ((dev->windowr[8] << 24) + (dev->windowr[9] << 16) +
+ (dev->windowr[10] << 8) + (dev->windowr[11])));
+
+ /* configuration = 1, interface = 0, alternate = 1 */
+ status = stv680_set_config (dev, 1, 0, 1);
+ /*
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "stv680_vidcam_init: STV680 FAILED to set configure\n");
+ return status;
+ }
+
+ DBG (DBG_info, "stv680_identify_vidcam: exit vidcam supported\n");
+ */
+ return SANE_TRUE;
+ }
+ }
+ DBG (DBG_error, "stv680_identify_vidcam: exit this is not a STV680 exit\n");
+ return SANE_FALSE;
+}
+
+static SANE_Status
+stv680_vidcam_init (Stv680_Vidcam * dev)
+{
+ SANE_Status status;
+ SANE_Byte i = 0;
+ SANE_Byte val = 0;
+ size_t sizer;
+ size_t sizew;
+ DBG (DBG_proc, "stv680_vidcam_init: open\n");
+
+ sizew = dev->windoww_size;
+ sizer = dev->windowr_size;
+
+ memset (dev->windoww, 0, sizew);
+ memset (dev->windowr, 0, sizer);
+
+ DBG (DBG_proc, "stv680_vidcam_init: urb 13 CMDID_GET_USER_INFO\n");
+ dev->video_status = 0x04; /* dummy value busy */
+
+ while (dev->video_status == 0x04)
+ {
+ sizer = 0x08;
+ status =
+ sanei_usb_control_msg (dev->fd, 0xc1, 0x8d, 0x0000, 0, sizer,
+ dev->windowr);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ hexdump (DBG_info2, "stv680_vidcam_init: urb13 CMDID_GET_USER_INFO",
+ dev->windowr, sizer);
+
+ dev->video_status = dev->windowr[1];
+ if (dev->video_status == 0x02)
+ {
+ DBG (DBG_proc, "stv680_vidcam_init: status = video\n");
+ }
+ else if ((dev->video_status == 0x01) || (dev->video_status == 0x08))
+ {
+ DBG (DBG_proc, "stv680_vidcam_init: status=%d\n",
+ dev->video_status);
+ }
+ else if (dev->video_status != 0x04)
+ {
+ DBG (DBG_proc, "stv680_vidcam_init: status = busy\n");
+ /* CMDID_CANCEL_TRANSACTION */
+ status =
+ sanei_usb_control_msg (dev->fd, 0x41, 0x04, 0x0000, 0, 0, 0);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_info,
+ "stv680_vidcam_init: urb13 CMDID_CANCEL_TRANSACTION NOK\n");
+ return status;
+ }
+ }
+ }
+
+ if (dev->video_status == 0x01 || dev->video_status == 0x08)
+ {
+ DBG (DBG_proc, "stv680_vidcam_init: urb 21 CMDID_GET_COLDATA_SIZE\n");
+ sizer = 0x02;
+ status =
+ sanei_usb_control_msg (dev->fd, 0xc1, 0x8a, 0x0000, 0, sizer,
+ dev->windowr);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+ val = dev->windowr[0];
+ hexdump (DBG_info2, "stv680_vidcam_init: urb21 CMDID_GET_COLDATA_SIZE",
+ dev->windowr, sizer);
+ if (dev->windowr[0] &= 0x00)
+ DBG (DBG_info,
+ "stv680_vidcam_init: no camera defaults, must be downloaded?\n");
+
+ sizer = 0x10;
+ for (i = 0; i < val; i += 0x10)
+ {
+ DBG (DBG_proc,
+ "stv680_vidcam_init: urb 22, 23, 24 CMDID_GET_COLDATA i=0x%x, val=0x%x\n",
+ i, val);
+ status =
+ sanei_usb_control_msg (dev->fd, 0xc1, 0x8b, (i << 8), 0, sizer,
+ dev->windowr);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ hexdump (DBG_info2,
+ "stv680_vidcam_init: urb22, 23, 24 CMDID_GET_COLDATA",
+ dev->windowr, sizer);
+ }
+
+ sizer = 0x12;
+ status =
+ sanei_usb_control_msg (dev->fd, 0x80, 0x06, 0x0100, 0, sizer,
+ dev->windowr);
+ if (status != SANE_STATUS_GOOD)
+ return status;
+ /* if (!(i > 0) && (dev->windowr[8] == 0x53) && (dev->windowr[9] == 0x05))
+ {
+ DBG (DBG_proc, "STV(e): Could not get descriptor 0100.");
+ *//* return status; *//*
+ } */
+ sizer = 0x12;
+ hexdump (DBG_info2, "stv680_vidcam_init: CMDID_SET_IMAGE_INDEX",
+ dev->windowr, sizer);
+
+ /* configuration = 1, interface = 0, alternate = 1 */
+ status = stv680_set_config (dev, 1, 0, 1);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error,
+ "stv680_vidcam_init: STV680 FAILED to set configure\n");
+ return status;
+ }
+ }
+ /* Switch to Video mode: 0x0000 = CIF (352x288), 0x0200 = QCIF (176x144) */
+ /* Switch to Video mode: 0x0100 = VGA (640x480), 0x0300 = QVGA (320x240) */
+ sizew = 0x0;
+ status =
+ sanei_usb_control_msg (dev->fd, 0x41, 0x09, dev->video_mode, 0, sizew,
+ dev->windoww);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_proc, "stv680_vidcam_init: video_mode = 0x%x\n",
+ dev->video_mode);
+ return status;
+ }
+ DBG (DBG_proc,
+ "stv680_vidcam_init: CMDID_START_VIDEO: video_mode=0x%x\n",
+ dev->video_mode);
+
+ if (dev->x_resolution == 176)
+ {
+ usleep (1000); /* delay time needed */
+ }
+ status = SANE_STATUS_GOOD;
+
+ if (status)
+ {
+ DBG (DBG_error, "stv680_vidcam_init failed : %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+ DBG (DBG_proc, "stv680_vidcam_init: exit\n");
+ return status;
+}
+
+/* Attach a vidcam to this backend. */
+static SANE_Status
+attach_vidcam (SANE_String_Const devicename, Stv680_Vidcam ** devp)
+{
+ Stv680_Vidcam *dev;
+ int fd;
+ SANE_Status status;
+
+ DBG (DBG_proc, "attach_vidcam: %s\n", devicename);
+
+ if (devp)
+ *devp = NULL;
+
+ /* Check if we know this device name. */
+ for (dev = first_dev; dev; dev = dev->next)
+ {
+ if (strcmp (dev->sane.name, devicename) == 0)
+ {
+ if (devp)
+ {
+ *devp = dev;
+ }
+ DBG (DBG_info, "device is already known\n");
+ return SANE_STATUS_GOOD;
+ }
+ }
+
+ /* Allocate a new vidcam entry. */
+ dev = stv680_init ();
+ if (dev == NULL)
+ {
+ DBG (DBG_error, "stv680_init ERROR: not enough memory\n");
+ return SANE_STATUS_NO_MEM;
+ }
+
+ DBG (DBG_info, "attach_vidcam: opening USB device %s\n", devicename);
+
+ if (sanei_usb_open (devicename, &fd) != 0)
+ {
+ DBG (DBG_error, "ERROR: attach_vidcam: open failed\n");
+ stv680_free (dev);
+ return SANE_STATUS_INVAL;
+ }
+ /* Fill some scanner specific values. */
+ dev->devicename = strdup (devicename);
+ dev->fd = fd;
+
+ /* Now, check that it is a vidcam we support. */
+
+ if (stv680_identify_vidcam (dev) == SANE_FALSE)
+ {
+ DBG (DBG_error, "ERROR: attach_vidcam: vidcam-identification failed\n");
+ stv680_free (dev);
+ return SANE_STATUS_INVAL;
+ }
+
+ /* Allocate a buffer memory. */
+ status = stv680_init_2 (dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "stv680_initi_2, ERROR: not enough memory\n");
+ return SANE_STATUS_NO_MEM;
+ }
+
+ stv680_close (dev);
+
+ DBG (DBG_info, "attach_vidcam: opening USB device %s\n", devicename);
+
+ /* Build list of vidcam supported resolutions. */
+ DBG (DBG_proc, "attach_vidcam: build resolution list\n");
+
+ if (dev->hw->color_adjust[0].resolution_x != 0)
+ {
+ int num_entries;
+ int i;
+ num_entries = 0;
+
+ while (dev->hw->color_adjust[num_entries].resolution_x != 0)
+ num_entries++;
+
+ dev->resolutions_list = malloc (sizeof (SANE_Word) * (num_entries + 1));
+
+ if (dev->resolutions_list == NULL)
+ {
+ DBG (DBG_error,
+ "ERROR: attach_vidcam: vidcam resolution list failed\n");
+ stv680_free (dev);
+ return SANE_STATUS_NO_MEM;
+ }
+ /* for CIF or VGA sensor different resolutions */
+ if (dev->CIF)
+ num_entries = 2;
+ if (dev->VGA)
+ num_entries = 3;
+ dev->resolutions_list[0] = num_entries;
+ DBG (DBG_proc, "attach_vidcam: make color resolution table \n");
+ for (i = 0; i < num_entries; i++)
+ {
+ dev->resolutions_list[i + 1 + dev->VGA + dev->QVGA] =
+ dev->hw->color_adjust[i].resolution_x;
+ }
+ }
+ else
+ {
+ dev->resolutions_list = NULL;
+ }
+
+ /* Set the default options for that vidcam. */
+ dev->sane.name = dev->devicename;
+ dev->sane.vendor = dev->hw->vendor_name;
+ dev->sane.model = dev->hw->product_name;
+ dev->sane.type = SANE_I18N ("webcam");
+
+ /* Link the vidcam with the others. */
+ dev->next = first_dev;
+ first_dev = dev;
+
+ if (devp)
+ {
+ *devp = dev;
+ }
+
+ num_devices++;
+
+ DBG (DBG_proc, "attach_vidcam: exit\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+attach_one (const char *dev)
+{
+ DBG (DBG_proc, "attach_one: open \n");
+ attach_vidcam (dev, NULL);
+ DBG (DBG_proc, "attach_one: exit \n");
+ return SANE_STATUS_GOOD;
+}
+
+/* Reset the options for that vidcam. */
+static void
+stv680_init_options (Stv680_Vidcam * dev)
+{
+ int i;
+
+ DBG (DBG_proc, "stv680_init_options: open\n");
+
+ /* Pre-initialize the options. */
+ memset (dev->opt, 0, sizeof (dev->opt));
+ memset (dev->val, 0, sizeof (dev->val));
+
+ for (i = 0; i < OPT_NUM_OPTIONS; ++i)
+ {
+ dev->opt[i].size = sizeof (SANE_Word);
+ dev->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ }
+ DBG (DBG_proc,
+ "stv680_init_options: done loop opt_num_options=%d, i=%d \n",
+ OPT_NUM_OPTIONS, i);
+ /* Number of options. */
+ dev->opt[OPT_NUM_OPTS].name = "";
+ dev->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS;
+ dev->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS;
+ dev->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT;
+ dev->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT;
+ dev->val[OPT_NUM_OPTS].w = OPT_NUM_OPTIONS;
+
+ /* Mode group */
+ dev->opt[OPT_MODE_GROUP].title = SANE_I18N ("Scan Mode");
+ dev->opt[OPT_MODE_GROUP].desc = ""; /* not valid for a group */
+ dev->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP;
+ dev->opt[OPT_MODE_GROUP].cap = 0;
+ dev->opt[OPT_MODE_GROUP].size = 0;
+ dev->opt[OPT_MODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* Vidcam supported modes */
+ dev->opt[OPT_MODE].name = SANE_NAME_SCAN_MODE;
+ dev->opt[OPT_MODE].title = SANE_TITLE_SCAN_MODE;
+ dev->opt[OPT_MODE].desc = SANE_DESC_SCAN_MODE;
+ dev->opt[OPT_MODE].type = SANE_TYPE_STRING;
+ dev->opt[OPT_MODE].size = max_string_size (scan_mode_list);
+ dev->opt[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ dev->opt[OPT_MODE].constraint.string_list = scan_mode_list;
+ dev->val[OPT_MODE].s = (SANE_Char *) strdup (""); /* will be set later */
+
+ /* X and Y resolution */
+ dev->opt[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION;
+ dev->opt[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION;
+ dev->opt[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION;
+ dev->opt[OPT_RESOLUTION].type = SANE_TYPE_INT;
+ dev->opt[OPT_RESOLUTION].unit = SANE_UNIT_DPI;
+ dev->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->val[OPT_RESOLUTION].w = dev->resolutions_list[dev->CIF + dev->QCIF + dev->VGA + dev->QVGA + dev->QSIF]; /* value will be 2 or 3 */
+
+ /* brightness */
+ dev->opt[OPT_BRIGHTNESS].name = SANE_NAME_BRIGHTNESS;
+ dev->opt[OPT_BRIGHTNESS].title = SANE_TITLE_BRIGHTNESS;
+ dev->opt[OPT_BRIGHTNESS].desc = SANE_DESC_BRIGHTNESS;
+ dev->opt[OPT_BRIGHTNESS].type = SANE_TYPE_INT;
+ dev->opt[OPT_BRIGHTNESS].unit = SANE_UNIT_NONE;
+ dev->opt[OPT_BRIGHTNESS].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->opt[OPT_BRIGHTNESS].constraint.range = &brightness_range;
+ dev->val[OPT_BRIGHTNESS].w = 0; /* to get middle value */
+
+ /* Enhancement group */
+ dev->opt[OPT_ENHANCEMENT_GROUP].title = SANE_I18N ("Enhancement");
+ dev->opt[OPT_ENHANCEMENT_GROUP].desc = ""; /* not valid for a group */
+ dev->opt[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP;
+ dev->opt[OPT_ENHANCEMENT_GROUP].cap = SANE_CAP_ADVANCED;
+ dev->opt[OPT_ENHANCEMENT_GROUP].size = 0;
+ dev->opt[OPT_ENHANCEMENT_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* red level calibration manual correction */
+ dev->opt[OPT_WHITE_LEVEL_R].name = SANE_NAME_WHITE_LEVEL_R;
+ dev->opt[OPT_WHITE_LEVEL_R].title = SANE_TITLE_WHITE_LEVEL_R;
+ dev->opt[OPT_WHITE_LEVEL_R].desc = SANE_DESC_WHITE_LEVEL_R;
+ dev->opt[OPT_WHITE_LEVEL_R].type = SANE_TYPE_INT;
+ dev->opt[OPT_WHITE_LEVEL_R].unit = SANE_UNIT_NONE;
+ dev->opt[OPT_WHITE_LEVEL_R].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->opt[OPT_WHITE_LEVEL_R].constraint.range = &red_level_range;
+ dev->val[OPT_WHITE_LEVEL_R].w = 00; /* to get middle value */
+
+ /* green level calibration manual correction */
+ dev->opt[OPT_WHITE_LEVEL_G].name = SANE_NAME_WHITE_LEVEL_G;
+ dev->opt[OPT_WHITE_LEVEL_G].title = SANE_TITLE_WHITE_LEVEL_G;
+ dev->opt[OPT_WHITE_LEVEL_G].desc = SANE_DESC_WHITE_LEVEL_G;
+ dev->opt[OPT_WHITE_LEVEL_G].type = SANE_TYPE_INT;
+ dev->opt[OPT_WHITE_LEVEL_G].unit = SANE_UNIT_NONE;
+ dev->opt[OPT_WHITE_LEVEL_G].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->opt[OPT_WHITE_LEVEL_G].constraint.range = &green_level_range;
+ dev->val[OPT_WHITE_LEVEL_G].w = 00; /* to get middle value */
+
+ /* blue level calibration manual correction */
+ dev->opt[OPT_WHITE_LEVEL_B].name = SANE_NAME_WHITE_LEVEL_B;
+ dev->opt[OPT_WHITE_LEVEL_B].title = SANE_TITLE_WHITE_LEVEL_B;
+ dev->opt[OPT_WHITE_LEVEL_B].desc = SANE_DESC_WHITE_LEVEL_B;
+ dev->opt[OPT_WHITE_LEVEL_B].type = SANE_TYPE_INT;
+ dev->opt[OPT_WHITE_LEVEL_B].unit = SANE_UNIT_NONE;
+ dev->opt[OPT_WHITE_LEVEL_B].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->opt[OPT_WHITE_LEVEL_B].constraint.range = &blue_level_range;
+ dev->val[OPT_WHITE_LEVEL_B].w = 00; /* to get middle value */
+
+ DBG (DBG_proc, "stv680_init_options: after blue level\n");
+
+ /* Lastly, set the default scan mode. This might change some
+ * values previously set here. */
+
+ sane_control_option (dev, OPT_MODE, SANE_ACTION_SET_VALUE,
+ (SANE_String_Const *) scan_mode_list[0], NULL);
+ DBG (DBG_proc, "stv680_init_options: exit\n");
+}
+
+/* Read the image from the vidcam and fill the temporary buffer with it. */
+static SANE_Status
+stv680_fill_image (Stv680_Vidcam * dev)
+{
+ SANE_Status status;
+ size_t size;
+ size_t bulk_size_read;
+
+ assert (dev->image_begin == dev->image_end);
+ assert (dev->real_bytes_left > 0);
+
+ DBG (DBG_proc, "stv680_fill_image: enter\n");
+
+ DBG (DBG_proc, "stv680_fill_image: real dev bytes left=0x%lx \n",
+ (unsigned long) (size_t) dev->real_bytes_left);
+ bulk_size_read = dev->real_bytes_left;
+
+ while (dev->real_bytes_left)
+ {
+ /* Try to read the maximum number of bytes. */
+ DBG (DBG_proc,
+ "stv680_fill_image: real dev bytes left, while loop=0x%lx \n",
+ (unsigned long) (size_t) dev->real_bytes_left);
+
+ size = dev->real_bytes_left;
+ if (size < bulk_size_read)
+ {
+ size = bulk_size_read; /* it seems size can not be smaller then read by bulk */
+ }
+ if (size == 0)
+ {
+ /* Probably reached the end of the buffer. Check, just in case. */
+ assert (dev->image_end != 0);
+ return (SANE_STATUS_GOOD);
+ }
+
+ /* Do the transfer */
+
+ DBG (DBG_proc,
+ "stv680_fill_image: dev->real_bytes_left: 0x%lx size: 0x%lx\n",
+ (unsigned long) (size_t) dev->real_bytes_left, (unsigned long) (size_t) size);
+ usleep (3000);
+ /* urb 44 first read bulk */
+
+ status = sanei_usb_read_bulk (dev->fd, dev->buffer, &size);
+
+ if (status != SANE_STATUS_GOOD)
+ {
+ return status;
+ }
+
+ DBG (DBG_info,
+ "stv680_fill_image: size (read) = 0x%lx bytes (bpl=0x%lx)\n",
+ (unsigned long) (size_t) size, (unsigned long) (size_t) dev->params.bytes_per_line);
+
+ memcpy (dev->image + dev->image_end, dev->buffer, size);
+
+ dev->image_end += size;
+ bulk_size_read = size;
+ if (dev->real_bytes_left > size)
+ dev->real_bytes_left -= size;
+ else if (dev->real_bytes_left <= size) /* last loop */
+ dev->real_bytes_left = 0;
+ DBG (DBG_info, "stv680_fill_image: real bytes left = 0x%lx\n",
+ (unsigned long) (size_t) dev->real_bytes_left);
+ }
+ /* i = stv_sndctrl (0, dev, 0x80, 0, &window, 0x02); *//* Get Last Error */
+/* DBG (DBG_proc, "STV(i): last error: %i, command = 0x%x", window[0], window[1]);
+ return -1; */
+/*
+ }
+ return 0; */
+
+ DBG (DBG_proc, "stv680_fill_image: exit\n");
+ return (SANE_STATUS_GOOD); /* unreachable */
+}
+
+#define MSG_MAXLEN 45
+#define CHAR_HEIGHT 11
+#define CHAR_WIDTH 6
+#define CHAR_START 4
+
+static SANE_Status
+stv680_add_text (SANE_Byte * image, int width, int height, char *txt)
+{
+ SANE_Status status;
+ time_t t;
+ struct tm *tm;
+ char line[MSG_MAXLEN + 1];
+ SANE_Byte *ptr;
+ int i, x, y, f, len;
+ char fmtstring[25] = " %Y-%m-%d %H:%M:%S";
+ char fmttxt[46];
+
+ DBG (DBG_proc, "stv680_add_text: enter\n");
+ time (&t);
+ tm = localtime (&t);
+ if (strlen (txt) > (MSG_MAXLEN - 23))
+ strncpy (fmttxt, txt, (MSG_MAXLEN - 23));
+ else
+ strcpy (fmttxt, txt);
+ strcat (fmttxt, fmtstring);
+
+ len = strftime (line, MSG_MAXLEN, fmttxt, tm);
+
+ for (y = 0; y < CHAR_HEIGHT; y++)
+ {
+ ptr = image + 3 * width * (height - CHAR_HEIGHT - 2 + y) + 12;
+
+ for (x = 0; x < len; x++)
+ {
+ f = fontdata[line[x] * CHAR_HEIGHT + y];
+ for (i = CHAR_WIDTH - 1; i >= 0; i--)
+ {
+ if (f & (CHAR_START << i))
+ {
+ ptr[0] = 255;
+ ptr[1] = 255;
+ ptr[2] = 255;
+ }
+ ptr += 3;
+ } /* for i */
+ } /* for x */
+ } /* for y */
+
+ DBG (DBG_proc, "stv680_add_text: exit vw=%d, vh=%d\n", width, height);
+ status = (SANE_STATUS_GOOD);
+ return status;
+
+}
+
+/* ************************** Video Decoding ********************* */
+
+static SANE_Status
+stv680_bayer_unshuffle (Stv680_Vidcam * dev, SANE_Byte * buf, size_t * size)
+{
+ SANE_Status status;
+ int x, y;
+ int i = 0;
+ int RED, GREEN, BLUE;
+ int w = dev->cwidth;
+ int vw = dev->x_resolution;
+ int vh = dev->y_resolution;
+ SANE_Byte p = 0;
+ int colour = 0, bayer = 0;
+ int bright_red;
+ int bright_green;
+ int bright_blue;
+ int count;
+
+ RED = dev->red_s;
+ GREEN = dev->green_s;
+ BLUE = dev->blue_s;
+
+ DBG (DBG_proc, "stv680_bayer_unshuffle: enter\n");
+
+#define AD(x, y, w) (((y)*(w)+(x))*3)
+
+ DBG (DBG_proc,
+ "stv680_bayer_unshuffle: color read RED=%d, GREEN=%d, BLUE=%d\n",
+ RED, GREEN, BLUE);
+
+ DBG (DBG_proc, "stv680_bayer_unshuffle: w=%d, vw=%d, vh=%d, len=0x%lx\n",
+ w, vw, vh, (unsigned long) (size_t) size);
+
+ for (y = 0; y < vh; y++)
+ {
+ for (x = 0; x < vw; x++)
+ {
+ if (x & 1)
+ {
+ p = dev->image[y * w + (x >> 1)];
+ }
+ else
+ {
+ p = dev->image[y * w + (x >> 1) + (w >> 1)];
+ }
+ if (y & 1)
+ bayer = 2;
+ else
+ bayer = 0;
+ if (x & 1)
+ bayer++;
+
+ switch (bayer)
+ {
+ case 0:
+ case 3:
+ colour = 1;
+ break;
+ case 1:
+ colour = 0;
+ break;
+ case 2:
+ colour = 2;
+ break;
+ }
+ i = (y * vw + x) * 3;
+ *(dev->output + i + colour) = (SANE_Byte) p;
+ } /* for x */
+
+ } /* for y */
+
+ /****** gamma correction plus hardcoded white balance */
+ /* Correction values red[], green[], blue[], are generated by
+ (pow(i/256.0, GAMMA)*255.0)*white balanceRGB where GAMMA=0.55, 1<i<255.
+ White balance (RGB)= 1.0, 1.17, 1.48. Values are calculated as double float and
+ converted to unsigned char. Values are in stv680.h */
+ if (dev->scan_mode == STV680_COLOR_RGB
+ || dev->scan_mode == STV680_COLOR_RGB_TEXT)
+ {
+ for (y = 0; y < vh; y++)
+ {
+ for (x = 0; x < vw; x++)
+ {
+ i = (y * vw + x) * 3;
+ *(dev->output + i) = red_g[*(dev->output + i)];
+ *(dev->output + i + 1) = green_g[*(dev->output + i + 1)];
+ *(dev->output + i + 2) = blue_g[*(dev->output + i + 2)];
+ }
+ }
+ }
+ DBG (DBG_proc, "stv680_bayer_unshuffle: gamma correction done\n");
+
+ if (dev->scan_mode != STV680_COLOR_RAW)
+ {
+
+ /****** bayer demosaic ******/
+ for (y = 1; y < (vh - 1); y++)
+ {
+ for (x = 1; x < (vw - 1); x++)
+ { /* work out pixel type */
+ if (y & 1)
+ bayer = 0;
+ else
+ bayer = 2;
+ if (!(x & 1))
+ bayer++;
+ switch (bayer)
+ {
+ case 0: /* green. blue lr, red tb */
+ *(dev->output + AD (x, y, vw) + BLUE) =
+ ((int) *(dev->output + AD (x - 1, y, vw) + BLUE) +
+ (int) *(dev->output + AD (x + 1, y, vw) + BLUE)) >> 1;
+ *(dev->output + AD (x, y, vw) + RED) =
+ ((int) *(dev->output + AD (x, y - 1, vw) + RED) +
+ (int) *(dev->output + AD (x, y + 1, vw) + RED)) >> 1;
+ break;
+
+ case 1: /* blue. green lrtb, red diagonals */
+ *(dev->output + AD (x, y, vw) + GREEN) =
+ ((int) *(dev->output + AD (x - 1, y, vw) + GREEN) +
+ (int) *(dev->output + AD (x + 1, y, vw) + GREEN) +
+ (int) *(dev->output + AD (x, y - 1, vw) + GREEN) +
+ (int) *(dev->output + AD (x, y + 1, vw) + GREEN)) >> 2;
+ *(dev->output + AD (x, y, vw) + RED) =
+ ((int) *(dev->output + AD (x - 1, y - 1, vw) + RED) +
+ (int) *(dev->output + AD (x - 1, y + 1, vw) + RED) +
+ (int) *(dev->output + AD (x + 1, y - 1, vw) + RED) +
+ (int) *(dev->output + AD (x + 1, y + 1, vw) + RED)) >> 2;
+ break;
+
+ case 2: /* red. green lrtb, blue diagonals */
+ *(dev->output + AD (x, y, vw) + GREEN) =
+ ((int) *(dev->output + AD (x - 1, y, vw) + GREEN) +
+ (int) *(dev->output + AD (x + 1, y, vw) + GREEN) +
+ (int) *(dev->output + AD (x, y - 1, vw) + GREEN) +
+ (int) *(dev->output + AD (x, y + 1, vw) + GREEN)) >> 2;
+ *(dev->output + AD (x, y, vw) + BLUE) =
+ ((int) *(dev->output + AD (x - 1, y - 1, vw) + BLUE) +
+ (int) *(dev->output + AD (x + 1, y - 1, vw) + BLUE) +
+ (int) *(dev->output + AD (x - 1, y + 1, vw) + BLUE) +
+ (int) *(dev->output + AD (x + 1, y + 1, vw) +
+ BLUE)) >> 2;
+ break;
+
+ case 3: /* green. red lr, blue tb */
+ *(dev->output + AD (x, y, vw) + RED) =
+ ((int) *(dev->output + AD (x - 1, y, vw) + RED) +
+ (int) *(dev->output + AD (x + 1, y, vw) + RED)) >> 1;
+ *(dev->output + AD (x, y, vw) + BLUE) =
+ ((int) *(dev->output + AD (x, y - 1, vw) + BLUE) +
+ (int) *(dev->output + AD (x, y + 1, vw) + BLUE)) >> 1;
+ break;
+ } /* switch */
+ } /* for x */
+ } /* for y - end demosaic */
+ } /* no bayer demosaic */
+ DBG (DBG_proc, "stv680_bayer_unshuffle: bayer demosaic done\n");
+
+ /* fix top and bottom row, left and right side */
+ i = vw * 3;
+ memcpy (dev->output, (dev->output + i), i);
+
+ memcpy ((dev->output + (vh * i)), (dev->output + ((vh - 1) * i)), i);
+
+
+ for (y = 0; y < vh; y++)
+ {
+ i = y * vw * 3;
+ memcpy ((dev->output + i), (dev->output + i + 3), 3);
+ memcpy ((dev->output + i + (vw * 3)),
+ (dev->output + i + (vw - 1) * 3), 3);
+ }
+
+ /* process all raw data, then trim to size if necessary */
+ if (dev->subsample == 160)
+ {
+ i = 0;
+ for (y = 0; y < vh; y++)
+ {
+ if (!(y & 1))
+ {
+ for (x = 0; x < vw; x++)
+ {
+ p = (y * vw + x) * 3;
+ if (!(x & 1))
+ {
+ *(dev->output + i) = *(dev->output + p);
+ *(dev->output + i + 1) = *(dev->output + p + 1);
+ *(dev->output + i + 2) = *(dev->output + p + 2);
+ i += 3;
+ }
+ } /* for x */
+ }
+ } /* for y */
+
+ DBG (DBG_proc,
+ "stv680_bayer_unshuffle: if needed, trim to size 160 done\n");
+ }
+ /* reset to proper width */
+ if ((dev->subsample == 160))
+ {
+ vw = 160;
+ vh = 120;
+ }
+
+ /* brightness adjustment */
+
+ count = vw * vh * 3;
+
+ bright_red = (dev->val[OPT_BRIGHTNESS].w) + (dev->val[OPT_WHITE_LEVEL_R].w);
+ bright_green =
+ (dev->val[OPT_BRIGHTNESS].w) + (dev->val[OPT_WHITE_LEVEL_G].w);
+ bright_blue =
+ (dev->val[OPT_BRIGHTNESS].w) + (dev->val[OPT_WHITE_LEVEL_B].w);
+
+ for (x = 0; x < count; x++)
+ {
+ y = x + 1;
+ i = x + 2;
+ if ((*(dev->output + x) + bright_red) >= 255)
+ *(buf + x) = 255;
+
+ else if ((*(dev->output + x) + bright_red) <= 0)
+ *(buf + x) = 0;
+ else
+ *(buf + x) = (*(dev->output + x) + bright_red);
+
+ if ((*(dev->output + y) + bright_green) >= 255)
+ *(buf + y) = 255;
+
+ else if ((*(dev->output + y) + bright_green) <= 0)
+ *(buf + y) = 0;
+ else
+ *(buf + y) = (*(dev->output + y) + bright_green);
+
+ if ((*(dev->output + i) + bright_blue) >= 255)
+ *(buf + i) = 255;
+
+ else if ((*(dev->output + i) + bright_blue) <= 0)
+ *(buf + i) = 0;
+ else
+ *(buf + i) = (*(dev->output + i) + bright_blue);
+
+ x += 2;
+ }
+
+ if (dev->scan_mode == STV680_COLOR_RGB_TEXT)
+ {
+ strcpy (dev->picmsg_ps, "STVcam ");
+
+ status = stv680_add_text (buf, vw, vh, dev->picmsg_ps);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_info, "stv680_bayer_unshuffle status NOK\n");
+ return (status);
+ }
+ }
+
+ DBG (DBG_proc, "stv680_bayer_unshuffle: exit vw=%d, vh=%d\n", vw, vh);
+ status = (SANE_STATUS_GOOD);
+ return status;
+}
+
+/* Sane entry points */
+
+SANE_Status
+sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize)
+{
+ FILE *fp;
+ char line[PATH_MAX];
+ size_t len;
+
+ num_devices = 0;
+ devlist = NULL;
+ first_dev = NULL;
+
+ DBG_INIT ();
+
+ DBG (DBG_sane_init, "sane_init\n");
+
+ authorize = authorize; /* silence gcc */
+
+ DBG (DBG_error, "This is sane-stv680 version %d.%d-%d\n", SANE_CURRENT_MAJOR,
+ V_MINOR, BUILD);
+ DBG (DBG_error, "(C) 2004-2006 by Gerard Klaver\n");
+
+ if (version_code)
+ {
+ *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, V_MINOR, BUILD);
+ }
+
+ DBG (DBG_proc, "sane_init: authorize %s null\n", authorize ? "!=" : "==");
+
+ sanei_usb_init ();
+
+ fp = sanei_config_open (STV680_CONFIG_FILE);
+ if (!fp)
+ {
+ /* No default vidcam? */
+ DBG (DBG_warning, "configuration file not found (%s)\n",
+ STV680_CONFIG_FILE);
+
+ return SANE_STATUS_GOOD;
+ }
+
+ while (sanei_config_read (line, sizeof (line), fp))
+ {
+ SANE_Word vendor;
+ SANE_Word product;
+
+ if (line[0] == '#') /* ignore line comments */
+ continue;
+ len = strlen (line);
+
+ if (!len)
+ continue; /* ignore empty lines */
+ if (sscanf (line, "usb %i %i", &vendor, &product) == 2)
+ {
+
+ sanei_usb_attach_matching_devices (line, attach_one);
+ }
+ else
+ {
+ /* Garbage. Ignore. */
+ DBG (DBG_warning, "bad configuration line: \"%s\" - ignoring.\n",
+ line);
+ }
+ }
+
+ fclose (fp);
+
+ DBG (DBG_proc, "sane_init: leave\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only)
+{
+ Stv680_Vidcam *dev;
+ int i;
+
+ DBG (DBG_proc, "sane_get_devices: enter\n");
+
+ local_only = local_only; /* silence gcc */
+
+ if (devlist)
+ free (devlist);
+
+ devlist = malloc ((num_devices + 1) * sizeof (devlist[0]));
+ if (!devlist)
+ return SANE_STATUS_NO_MEM;
+
+ i = 0;
+ for (dev = first_dev; i < num_devices; dev = dev->next)
+ devlist[i++] = &dev->sane;
+ devlist[i++] = 0;
+
+ *device_list = devlist;
+
+ DBG (DBG_proc, "sane_get_devices: exit\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_open (SANE_String_Const devicename, SANE_Handle * handle)
+{
+ Stv680_Vidcam *dev;
+ SANE_Status status;
+
+ DBG (DBG_proc, "sane_open: enter\n");
+
+ /* search for devicename */
+ if (devicename[0])
+ {
+ DBG (DBG_info, "sane_open: devicename=%s\n", devicename);
+
+ for (dev = first_dev; dev; dev = dev->next)
+ {
+ if (strcmp (dev->sane.name, devicename) == 0)
+ {
+ break;
+ }
+ }
+
+ if (!dev)
+ {
+ status = attach_vidcam (devicename, &dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ return status;
+ }
+ }
+ }
+ else
+ {
+ DBG (DBG_sane_info, "sane_open: no devicename, opening first device\n");
+ dev = first_dev; /* empty devicename -> use first device */
+ }
+
+ if (!dev)
+ {
+ DBG (DBG_error, "No vidcam found\n");
+
+ return SANE_STATUS_INVAL;
+ }
+
+ stv680_init_options (dev);
+
+ *handle = dev;
+
+ DBG (DBG_proc, "sane_open: exit\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+const SANE_Option_Descriptor *
+sane_get_option_descriptor (SANE_Handle handle, SANE_Int option)
+{
+ Stv680_Vidcam *dev = handle;
+
+ DBG (DBG_proc, "sane_get_option_descriptor: enter, option %d\n", option);
+
+ if ((unsigned) option >= OPT_NUM_OPTIONS)
+ {
+ return NULL;
+ }
+
+ DBG (DBG_proc, "sane_get_option_descriptor: exit\n");
+
+ return dev->opt + option;
+}
+
+SANE_Status
+sane_control_option (SANE_Handle handle, SANE_Int option,
+ SANE_Action action, void *val, SANE_Int * info)
+{
+ Stv680_Vidcam *dev = handle;
+ SANE_Status status;
+ SANE_Word cap;
+
+ DBG (DBG_proc, "sane_control_option: enter, option %d, action %d\n",
+ option, action);
+
+ if (info)
+ {
+ *info = 0;
+ }
+
+ if (dev->scanning)
+ {
+ return SANE_STATUS_DEVICE_BUSY;
+ }
+
+ if (option < 0 || option >= OPT_NUM_OPTIONS)
+ {
+ return SANE_STATUS_INVAL;
+ }
+
+ cap = dev->opt[option].cap;
+ if (!SANE_OPTION_IS_ACTIVE (cap))
+ {
+ return SANE_STATUS_INVAL;
+ }
+
+ if (action == SANE_ACTION_GET_VALUE)
+ {
+
+ switch (option)
+ {
+ /* word options */
+ case OPT_NUM_OPTS:
+ case OPT_RESOLUTION:
+ case OPT_BRIGHTNESS:
+ case OPT_WHITE_LEVEL_R:
+ case OPT_WHITE_LEVEL_G:
+ case OPT_WHITE_LEVEL_B:
+ *(SANE_Word *) val = dev->val[option].w;
+ return SANE_STATUS_GOOD;
+ case OPT_MODE:
+ strcpy (val, dev->val[option].s);
+ return SANE_STATUS_GOOD;
+ default:
+ return SANE_STATUS_INVAL;
+ }
+ }
+ else if (action == SANE_ACTION_SET_VALUE)
+ {
+
+ if (!SANE_OPTION_IS_SETTABLE (cap))
+ {
+ DBG (DBG_error, "could not set option, not settable\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ status = sanei_constrain_value (dev->opt + option, val, info);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_error, "could not set option, invalid value\n");
+ return status;
+ }
+
+ switch (option)
+ {
+
+ /* Numeric side-effect options */
+ case OPT_RESOLUTION:
+ case OPT_BRIGHTNESS:
+ case OPT_WHITE_LEVEL_R:
+ case OPT_WHITE_LEVEL_G:
+ case OPT_WHITE_LEVEL_B:
+ if (info)
+ {
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ }
+ dev->val[option].w = *(SANE_Word *) val;
+ return SANE_STATUS_GOOD;
+
+ /* String side-effect options */
+ case OPT_MODE:
+ if (strcmp (dev->val[option].s, val) == 0)
+ return SANE_STATUS_GOOD;
+
+ free (dev->val[OPT_MODE].s);
+ dev->val[OPT_MODE].s = (SANE_Char *) strdup (val);
+
+ dev->opt[OPT_WHITE_LEVEL_R].cap &= ~SANE_CAP_INACTIVE;
+ dev->opt[OPT_WHITE_LEVEL_G].cap &= ~SANE_CAP_INACTIVE;
+ dev->opt[OPT_WHITE_LEVEL_B].cap &= ~SANE_CAP_INACTIVE;
+
+ if (strcmp (dev->val[OPT_MODE].s, COLOR_RAW_STR) == 0)
+ {
+ dev->scan_mode = STV680_COLOR_RAW;
+ }
+ else if (strcmp (dev->val[OPT_MODE].s, COLOR_RGB_STR) == 0)
+ {
+ dev->scan_mode = STV680_COLOR_RGB;
+ }
+ else if (strcmp (dev->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_COLOR)
+ == 0)
+ {
+ dev->scan_mode = STV680_COLOR;
+
+ }
+ else if (strcmp (dev->val[OPT_MODE].s, COLOR_RGB_TEXT_STR) == 0)
+ {
+ dev->scan_mode = STV680_COLOR_RGB_TEXT;
+
+ }
+
+ /* The STV680 supports only a handful of resolution. */
+ /* This the default resolution range for the STV680 */
+
+ dev->depth = 8;
+ if (dev->resolutions_list != NULL)
+ {
+ int i;
+
+ dev->opt[OPT_RESOLUTION].constraint_type =
+ SANE_CONSTRAINT_WORD_LIST;
+ dev->opt[OPT_RESOLUTION].constraint.word_list =
+ dev->resolutions_list;
+
+ /* If the resolution isn't in the list, set a default. */
+ for (i = 1; i <= dev->resolutions_list[0]; i++)
+ {
+ if (dev->resolutions_list[i] >= dev->val[OPT_RESOLUTION].w)
+ break;
+ }
+ if (i > dev->resolutions_list[0])
+ {
+ /* Too big. Take lowest. */
+ dev->val[OPT_RESOLUTION].w = dev->resolutions_list[1];
+ }
+ else
+ {
+ /* Take immediate superioir value. */
+ dev->val[OPT_RESOLUTION].w = dev->resolutions_list[i];
+ }
+ }
+
+ /* String side-effect options */
+
+ if (info)
+ {
+ *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS;
+ }
+ return SANE_STATUS_GOOD;
+ default:
+ return SANE_STATUS_INVAL;
+ }
+ }
+
+ DBG (DBG_proc, "sane_control_option: exit, bad\n");
+
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+SANE_Status
+sane_get_parameters (SANE_Handle handle, SANE_Parameters * params)
+{
+ Stv680_Vidcam *dev = handle;
+ int i;
+
+ DBG (DBG_proc, "sane_get_parameters: enter\n");
+
+ if (!(dev->scanning))
+ {
+ dev->x_resolution = dev->val[OPT_RESOLUTION].w;
+ /* Prepare the parameters for the caller. */
+ memset (&dev->params, 0, sizeof (SANE_Parameters));
+
+ dev->params.last_frame = SANE_TRUE;
+
+ switch (dev->scan_mode)
+ {
+ case STV680_COLOR_RAW:
+ dev->bytes_pixel = 1; /* raw image is 422 code, 1 byte/pixel */
+ break;
+ case STV680_COLOR_RGB:
+ case STV680_COLOR_RGB_TEXT:
+ case STV680_COLOR:
+ dev->bytes_pixel = 3;
+ break;
+ }
+ dev->params.format = SANE_FRAME_RGB;
+ dev->params.pixels_per_line = dev->x_resolution;
+ dev->params.bytes_per_line =
+ dev->params.pixels_per_line * dev->bytes_pixel;
+ dev->params.depth = 8;
+ if (dev->resolutions_list != NULL)
+ {
+ /* This vidcam has a fixed number of supported
+ * resolutions. Find the color sequence for that
+ * resolution. */
+
+ for (i = 0;
+ dev->hw->color_adjust[i].resolution_x != dev->x_resolution;
+ i++);
+
+ dev->red_s = dev->hw->color_adjust[i].z1_color_0;
+ dev->green_s = dev->hw->color_adjust[i].z1_color_1;
+ dev->blue_s = dev->hw->color_adjust[i].z1_color_2;
+ dev->y_resolution = dev->hw->color_adjust[i].resolution_y;
+ }
+ dev->subsample = 0;
+ switch (dev->val[OPT_RESOLUTION].w)
+ {
+ case 176:
+ dev->video_mode = 0x0200;
+ dev->cwidth = dev->x_resolution + 2;
+ dev->cheight = dev->y_resolution + 2;
+ break;
+ case 160: /* 160x120 subsampled */
+ dev->x_resolution = 320;
+ dev->y_resolution = 240;
+ dev->video_mode = 0x0300;
+ dev->cwidth = dev->x_resolution + 2;
+ dev->cheight = dev->y_resolution + 2;
+ dev->subsample = 160;
+ break;
+ case 320:
+ dev->video_mode = 0x0300;
+ dev->cwidth = dev->x_resolution + 2;
+ dev->cheight = dev->y_resolution + 2;
+ break;
+ case 352:
+ dev->video_mode = 0x0000;
+ dev->cwidth = dev->x_resolution + 4;
+ dev->cheight = dev->y_resolution + 4;
+ break;
+ case 640:
+ dev->video_mode = 0x0100;
+ dev->cwidth = dev->x_resolution + 4;
+ dev->cheight = dev->y_resolution + 4;
+ break;
+ }
+ dev->params.pixels_per_line = dev->x_resolution;
+ dev->params.lines = dev->y_resolution;
+ DBG (DBG_info, "sane_get_parameters: x=%d, y=%d\n", dev->x_resolution,
+ dev->y_resolution);
+ }
+
+ /* Return the current values. */
+ if (params)
+ {
+ *params = (dev->params);
+ }
+
+ DBG (DBG_proc, "sane_get_parameters: exit\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_start (SANE_Handle handle)
+{
+ Stv680_Vidcam *dev = handle;
+ SANE_Status status;
+
+ DBG (DBG_proc, "sane_start: enter\n");
+
+ if (!(dev->scanning))
+ {
+ sane_get_parameters (dev, NULL);
+
+ /* Open again the vidcam */
+ if (sanei_usb_open (dev->devicename, &(dev->fd)) != 0)
+ {
+ DBG (DBG_error, "ERROR: sane_start: open failed\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ /* Initialize the vidcam. */
+ status = stv680_vidcam_init (dev);
+ if (status)
+ {
+ DBG (DBG_error, "ERROR: failed to init the vidcam\n");
+ stv680_close (dev);
+ return status;
+ }
+
+ }
+
+ dev->image_end = 0;
+ dev->image_begin = 0;
+ /* real_byte_left is bulk read bytes, bytes_left is frontend buffer bytes */
+ dev->real_bytes_left = dev->cwidth * dev->cheight;
+ dev->bytes_left = dev->params.bytes_per_line * dev->params.lines;
+
+ dev->scanning = SANE_TRUE;
+
+ DBG (DBG_proc, "sane_start: exit\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_read (SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len,
+ SANE_Int * len)
+{
+ SANE_Status status;
+ Stv680_Vidcam *dev = handle;
+ size_t size;
+
+ DBG (DBG_proc, "sane_read: enter\n");
+
+ *len = 0;
+ if (dev->deliver_eof)
+ {
+ dev->deliver_eof = 0;
+ return SANE_STATUS_EOF;
+ }
+
+ if (!(dev->scanning))
+ {
+ /* OOPS, not scanning, stop a scan. */
+ stv680_reset_vidcam (dev);
+ stv680_close (dev);
+ dev->scanning = SANE_FALSE;
+ return SANE_STATUS_CANCELLED;
+ }
+
+ if (dev->bytes_left <= 0)
+ {
+ return (SANE_STATUS_EOF);
+ }
+
+ if (dev->image_begin == dev->image_end)
+ {
+ /* Fill image */
+ status = stv680_fill_image (dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_info, "sane_read: stv680_fill_image status NOK\n");
+ return (status);
+ }
+ }
+
+ /* Something must have been read */
+ if (dev->image_begin == dev->image_end)
+ {
+ DBG (DBG_info, "sane_read: nothing read\n");
+ return SANE_STATUS_IO_ERROR;
+ }
+
+ size = dev->bytes_left;
+ if (((unsigned int) max_len) < size)
+ {
+ DBG (DBG_error, "sane_read: max_len < size\n");
+ return (SANE_FALSE);
+ }
+ if ((dev->image_end - dev->image_begin) > size)
+ {
+ size = dev->image_end - dev->image_begin;
+ DBG (DBG_proc, "sane_read: size < dev->image_end - dev->image_begin\n");
+ }
+ /* diff between size an dev->bytes_left because of 356/352 and 292/288 */
+ DBG (DBG_info, "sane_read: size =0x%lx bytes, max_len=0x%lx bytes\n",
+ (unsigned long) (size_t) size, (unsigned long) (size_t) max_len);
+
+ *len = dev->bytes_left; /* needed */
+ size = dev->bytes_left;
+ dev->bytes_left = 0; /* needed for frontend or ? */
+
+ if (dev->scan_mode != STV680_COLOR_RAW)
+ {
+ /* do bayer unshuffle after complete frame is read */
+ status = stv680_bayer_unshuffle (dev, buf, &size);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_info, "sane_read: stv680_bayer_unshuffle status NOK\n");
+ return (status);
+ }
+ }
+ else
+ {
+ /* Copy the raw data to the frontend buffer. */
+ memcpy (buf, dev->image, size);
+ DBG (DBG_info, "sane_read: raw mode\n");
+ }
+ DBG (DBG_info, "sane_read: exit\n");
+ status = SANE_STATUS_GOOD;
+ return status;
+}
+
+SANE_Status
+sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking)
+{
+
+ DBG (DBG_proc, "sane_set_io_mode: enter\n");
+
+ handle = handle; /* silence gcc */
+ non_blocking = non_blocking; /* silence gcc */
+
+
+ DBG (DBG_proc, "sane_set_io_mode: exit\n");
+
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+SANE_Status
+sane_get_select_fd (SANE_Handle handle, SANE_Int * fd)
+{
+ DBG (DBG_proc, "sane_get_select_fd: enter\n");
+
+ handle = handle; /* silence gcc */
+ fd = fd; /* silence gcc */
+
+ DBG (DBG_proc, "sane_get_select_fd: exit\n");
+
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+void
+sane_cancel (SANE_Handle handle)
+{
+ Stv680_Vidcam *dev = handle;
+
+ DBG (DBG_proc, "sane_cancel: enter\n");
+
+ /* Stop a scan. */
+ if (dev->scanning == SANE_TRUE)
+ {
+ /* Reset the vidcam */
+ stv680_reset_vidcam (dev);
+ stv680_close (dev);
+ }
+ dev->scanning = SANE_FALSE;
+ dev->deliver_eof = 0;
+
+ /* return SANE_STATUS_CANCELLED; */
+ DBG (DBG_proc, "sane_cancel: exit\n");
+}
+
+void
+sane_close (SANE_Handle handle)
+{
+ Stv680_Vidcam *dev = handle;
+ Stv680_Vidcam *dev_tmp;
+
+ DBG (DBG_proc, "sane_close: enter\n");
+
+/* Stop a scan. */
+
+ if (dev->scanning == SANE_TRUE)
+ {
+ stv680_reset_vidcam (dev);
+ stv680_close (dev);
+ }
+ dev->scanning = SANE_FALSE;
+
+ /* Unlink dev. */
+ if (first_dev == dev)
+ {
+ first_dev = dev->next;
+ }
+ else
+ {
+ dev_tmp = first_dev;
+ while (dev_tmp->next && dev_tmp->next != dev)
+ {
+ dev_tmp = dev_tmp->next;
+ }
+ if (dev_tmp->next != NULL)
+ {
+ dev_tmp->next = dev_tmp->next->next;
+ }
+ }
+
+ stv680_free (dev);
+ num_devices--;
+
+ DBG (DBG_proc, "sane_close: exit\n");
+}
+
+void
+sane_exit (void)
+{
+ DBG (DBG_proc, "sane_exit: enter\n");
+
+ while (first_dev)
+ {
+ sane_close (first_dev);
+ }
+
+ if (devlist)
+ {
+ free (devlist);
+ devlist = NULL;
+ }
+
+ DBG (DBG_proc, "sane_exit: exit\n");
+}