sane-project-backends/backend/stv680.c

2178 wiersze
56 KiB
C

/* 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, see <https://www.gnu.org/licenses/>.
------------------------------------------------------------------
*/
/*
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->buffer = 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 TEXT_CHAR_HEIGHT 11
#define TEXT_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 < TEXT_CHAR_HEIGHT; y++)
{
ptr = image + 3 * width * (height - TEXT_CHAR_HEIGHT - 2 + y) + 12;
for (x = 0; x < len; x++)
{
f = fontdata[line[x] * TEXT_CHAR_HEIGHT + y];
for (i = TEXT_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");
(void) authorize; /* silence gcc */
DBG (DBG_error, "This is sane-stv680 version %d.%d-%d\n", SANE_CURRENT_MAJOR,
SANE_CURRENT_MINOR, BUILD);
DBG (DBG_error, "(C) 2004-2006 by Gerard Klaver\n");
if (version_code)
{
*version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, SANE_CURRENT_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");
(void) 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");
(void) handle; /* silence gcc */
(void) 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");
(void) handle; /* silence gcc */
(void) 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");
}