diff options
Diffstat (limited to 'backend/stv680.c')
-rw-r--r-- | backend/stv680.c | 2180 |
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"); +} |