sane-project-backends/backend/qcam.c

2253 wiersze
57 KiB
C

/* sane - Scanner Access Now Easy.
Copyright (C) 1997 David Mosberger-Tang
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/>.
As a special exception, the authors of SANE give permission for
additional uses of the libraries contained in this release of SANE.
The exception is that, if you link a SANE library with other files
to produce an executable, this does not by itself cause the
resulting executable to be covered by the GNU General Public
License. Your use of that executable is in no way restricted on
account of linking the SANE library code into it.
This exception does not, however, invalidate any other reasons why
the executable file might be covered by the GNU General Public
License.
If you submit changes to SANE to the maintainers to be included in
a subsequent release, you agree by submitting the changes that
those changes may be distributed with this exception intact.
If you write modifications of your own for SANE, it is your choice
whether to permit this exception to apply to your modifications.
If you do not wish that, delete this exception notice.
This file implements a SANE backend for the Connectix QuickCam. At
present, only the color camera is supported though the driver
should be able to easily accommodate black and white cameras.
Portions of this code are derived from Scott Laird's qcam driver.
It's copyright notice is reproduced here:
Copyright (C) 1996 by Scott Laird
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use, copy,
modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL SCOTT LAIRD BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
#ifdef _AIX
# include "lalloca.h" /* MUST come first for AIX! */
#endif
#include "../include/sane/config.h"
#include "lalloca.h"
#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <math.h>
#include <setjmp.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"
#define BACKEND_NAME qcam
#include "../include/sane/sanei_backend.h"
#ifndef PATH_MAX
# define PATH_MAX 1024
#endif
#include "../include/sane/sanei_config.h"
#define QCAM_CONFIG_FILE "qcam.conf"
#include "qcam.h"
/* status bits */
#define NeedRamTable (1 << 1)
#define BlackBalanceInProgress (1 << 6)
#define CameraNotReady (1 << 7)
/* lpdata bits: */
#define Cmd0_7 0xff
#define CamRdy2 ( 1 << 0) /* byte mode */
#define Data0_6 (0x7f << 1) /* byte mode */
/* lpstatus bits: */
#define CamRdy1 ( 1 << 3) /* nibble mode */
#define Nibble0_3 (0x0f << 4) /* nibble mode */
#define Data7_11 (0x1f << 3) /* byte mode */
/* lpcontrol bits: */
#define Strobe ( 1 << 0) /* unused */
#define Autofeed ( 1 << 1)
#define Reset_N ( 1 << 2)
#define PCAck ( 1 << 3)
#define BiDir ( 1 << 5)
static int num_devices;
static QC_Device *first_dev;
static QC_Scanner *first_handle;
static const SANE_String_Const resolution_list[] = {
"Low", /* million-mode */
"High", /* billion-mode */
0
};
static const SANE_Int mono_depth_list[] = {
2, /* # of elements */
4, 6
};
static const SANE_Int color_depth_list[] = {
/*2 */ 1,
/* # of elements */
/*16, */ 24
/* "thousand" mode not implemented yet */
};
static const SANE_Int xfer_scale_list[] = {
3, /* # of elements */
1, 2, 4
};
static const SANE_Range u8_range = {
/* min, max, quantization */
0, 255, 0
};
static const SANE_Range brightness_range = {
/* min, max, quantization */
0, 254, 0 /* 255 is bulb mode! */
};
static const SANE_Range x_range[] = {
/* min, max, quantization */
{0, 338, 2}, /* million mode */
{0, 676, 4}, /* billion mode */
};
static const SANE_Range odd_x_range[] = {
/* min, max, quantization */
{1, 339, 2}, /* million mode */
{3, 683, 4}, /* billion mode */
};
static const SANE_Range y_range[] = {
/* min, max, quantization */
{0, 249, 1}, /* million mode */
{0, 498, 2}, /* billion mode */
};
static const SANE_Range odd_y_range[] = {
/* min, max, quantization */
{0, 249, 1}, /* million mode */
{1, 499, 2}, /* billion mode */
};
static const SANE_Range bw_x_range = { 0, 334, 2 };
static const SANE_Range odd_bw_x_range = { 1, 335, 2 };
static const SANE_Range bw_y_range = { 0, 241, 1 };
static const SANE_Range odd_bw_y_range = { 1, 242, 1 };
#include "../include/sane/sanei_directio.h"
#define read_lpdata(d) sanei_inb ((d)->port)
#define read_lpstatus(d) sanei_inb ((d)->port + 1)
#define read_lpcontrol(d) sanei_inb ((d)->port + 2)
#define write_lpdata(d,v) sanei_outb ((d)->port, (v))
#define write_lpcontrol(d,v) sanei_outb ((d)->port + 2, (v))
static SANE_Status
enable_ports (QC_Device * q)
{
/* better safe than sorry */
if (q->port < 0x278 || q->port > 0x3bc)
return SANE_STATUS_INVAL;
if (sanei_ioperm (q->port, 3, 1) < 0)
return SANE_STATUS_INVAL;
return SANE_STATUS_GOOD;
}
static SANE_Status
disable_ports (QC_Device * q)
{
if (sanei_ioperm (q->port, 3, 0) < 0)
return SANE_STATUS_INVAL;
return SANE_STATUS_GOOD;
}
/* We need a short delay loop -- something well under a millisecond.
Unfortunately, adding 2 usleep(1)'s to qc_command slowed it down by
a factor of over 1000 over the same loop with 2 usleep(0)'s, and
that's too slow -- qc_start was taking over a second to run. This
seems to help, but if anyone has a good speed-independent pause
routine, please tell me. -- Scott
If you're worried about hogging the CPU: don't worry, the qcam
interface leaves you no choice, so this doesn't make the situation
any worse... */
static int
qc_wait (QC_Device * q)
{
return read_lpstatus (q);
}
/* This function uses POSIX fcntl-style locking on a file created in
the /tmp directory. Because it uses the Unix record locking
facility, locks are relinquished automatically on process
termination, so "dead locks" are not a problem. (FYI, the lock
file will remain after process termination, but this is actually
desired so that the next process need not re-creat(2)e it... just
lock it.) The wait argument indicates whether or not this function
should "block" waiting for the previous lock to be relinquished.
This is ideal so that multiple processes (eg. qcam) taking
"snapshots" can peacefully coexist.
-- Dave Plonka (plonka@carroll1.cc.edu) */
static SANE_Status
qc_lock_wait (QC_Device * q, int wait)
{
#ifdef F_SETLK
#ifndef HAVE_STRUCT_FLOCK
struct flock
{
off_t l_start;
off_t l_len;
pid_t l_pid;
short l_type;
short l_whence;
};
#endif /* !HAVE_STRUCT_FLOCK */
struct flock sfl;
#endif
DBG (3, "qc_lock_wait: acquiring lock for 0x%x\n", q->port);
#ifdef F_SETLK
memset (&sfl, 0, sizeof (sfl));
#endif
if (q->lock_fd < 0)
{
char lockfile[128];
sprintf (lockfile, "/tmp/LOCK.qcam.0x%x", q->port);
q->lock_fd = open (lockfile, O_WRONLY | O_CREAT | O_EXCL, 0666);
if (q->lock_fd < 0)
{
DBG (1, "qc_lock_wait: failed to open %s (%s)\n",
lockfile, strerror (errno));
return SANE_STATUS_INVAL;
}
}
#ifdef F_SETLK
sfl.l_type = F_WRLCK;
if (fcntl (q->lock_fd, wait ? F_SETLKW : F_SETLK, &sfl) != 0)
{
DBG (1, "qc_lock_wait: failed to acquire lock (%s)\n",
strerror (errno));
return SANE_STATUS_INVAL;
}
#endif
DBG (3, "qc_lock_wait: got lock for 0x%x\n", q->port);
return SANE_STATUS_GOOD;
}
static SANE_Status
qc_unlock (QC_Device * q)
{
SANE_Status status;
char lockfile[128];
#ifdef F_SETLK
#ifndef HAVE_STRUCT_FLOCK
struct flock
{
off_t l_start;
off_t l_len;
pid_t l_pid;
short l_type;
short l_whence;
};
#endif /* !HAVE_STRUCT_FLOCK */
struct flock sfl;
#endif
DBG (3, "qc_unlock: releasing lock for 0x%x\n", q->port);
#ifdef F_SETLK
memset (&sfl, 0, sizeof (sfl));
#endif
if (q->lock_fd < 0)
{
DBG (3, "qc_unlock; port was not locked\n");
return SANE_STATUS_INVAL;
}
/* clear the exclusive lock */
#ifdef F_SETLK
sfl.l_type = F_UNLCK;
if (fcntl (q->lock_fd, F_SETLK, &sfl) != 0)
{
DBG (3, "qc_unlock: failed to release lock (%s)\n", strerror (errno));
return SANE_STATUS_INVAL;
}
#endif
sprintf (lockfile, "/tmp/LOCK.qcam.0x%x", q->port);
DBG (1, "qc_unlock: /tmp/LOCK.qcam.0x%x\n", q->port);
unlink (lockfile);
close (q->lock_fd);
q->lock_fd = -1;
DBG (1, "qc_unlock: exit\n");
status = SANE_STATUS_GOOD;
return status;
}
static SANE_Status
qc_lock (QC_Device * q)
{
return qc_lock_wait (q, 1);
}
/* Busy-waits for a handshake signal from the QuickCam. Almost all
communication with the camera requires handshaking. This is why
qcam is a CPU hog. */
static int
qc_waithand (QC_Device * q, int val)
{
int status;
while (((status = read_lpstatus (q)) & CamRdy1) != val);
return status;
}
/* This is used when the qcam is in bidirectional ("byte") mode, and
the handshaking signal is CamRdy2 (bit 0 of data reg) instead of
CamRdy1 (bit 3 of status register). It also returns the last value
read, since this data is useful. */
static unsigned int
qc_waithand2 (QC_Device * q, int val)
{
unsigned int status;
do
{
status = read_lpdata (q);
}
while ((status & CamRdy2) != (unsigned int) val);
return status;
}
static unsigned int
qc_send (QC_Device * q, unsigned int byte)
{
unsigned int echo;
int n1, n2;
write_lpdata (q, byte);
qc_wait (q);
write_lpcontrol (q, Autofeed | Reset_N);
qc_wait (q);
n1 = qc_waithand (q, CamRdy1);
write_lpcontrol (q, Autofeed | Reset_N | PCAck);
qc_wait (q);
n2 = qc_waithand (q, 0);
echo = (n1 & 0xf0) | ((n2 & 0xf0) >> 4);
#ifndef NDEBUG
if (echo != byte)
{
DBG (1, "qc_send: sent 0x%02x, camera echoed 0x%02x\n", byte, echo);
n2 = read_lpstatus (q);
echo = (n1 & 0xf0) | ((n2 & 0xf0) >> 4);
if (echo != byte)
DBG (1, "qc_send: (re-read does not help)\n");
else
DBG (1, "qc_send: (fixed on re-read)\n");
}
#endif
return echo;
}
static int
qc_readparam (QC_Device * q)
{
int n1, n2;
int cmd;
write_lpcontrol (q, Autofeed | Reset_N); /* clear PCAck */
n1 = qc_waithand (q, CamRdy1);
write_lpcontrol (q, Autofeed | Reset_N | PCAck); /* set PCAck */
n2 = qc_waithand (q, 0);
cmd = (n1 & 0xf0) | ((n2 & 0xf0) >> 4);
return cmd;
}
static unsigned int
qc_getstatus (QC_Device * q)
{
unsigned int status;
qc_send (q, QC_SEND_STATUS);
status = qc_readparam (q);
DBG (3, "qc_getstatus: status=0x%02x\n", status);
return status;
}
static void
qc_setscanmode (QC_Scanner * s, u_int * modep)
{
QC_Device *q = s->hw;
u_int mode = 0;
if (q->version != QC_COLOR)
{
switch (s->val[OPT_XFER_SCALE].w)
{
case 1:
mode = 0;
break;
case 2:
mode = 4;
break;
case 4:
mode = 8;
break;
}
switch (s->val[OPT_DEPTH].w)
{
case 4:
break;
case 6:
mode += 2;
break;
}
}
else
{
switch (s->val[OPT_XFER_SCALE].w)
{
case 1:
mode = 0;
break;
case 2:
mode = 2;
break;
case 4:
mode = 4;
break;
}
if (s->resolution == QC_RES_LOW)
mode |= 0x18; /* millions mode */
else
mode |= 0x10; /* billions mode */
}
if (s->val[OPT_TEST].w)
mode |= 0x40; /* test mode */
if (q->port_mode == QC_BIDIR)
mode |= 1;
DBG (2, "scanmode (before increment): 0x%x\n", mode);
if (q->version == QC_COLOR)
++mode;
*modep = mode;
}
/* Read data bytes from the camera. The number of bytes read is
returned as the function result. Depending on the mode, it may be
either 1, 3 or 6. On failure, 0 is returned. If buffer is 0, the
internal state-machine is reset. */
static size_t
qc_readbytes (QC_Scanner * s, unsigned char buffer[])
{
QC_Device *q = s->hw;
unsigned int hi, lo;
unsigned int hi2, lo2;
size_t bytes = 0;
if (!buffer)
{
s->readbytes_state = 0;
return 0;
}
switch (q->port_mode)
{
case QC_BIDIR:
/* bi-directional port */
/* read off 24 bits: */
write_lpcontrol (q, Autofeed | Reset_N | BiDir);
lo = qc_waithand2 (q, 1) >> 1;
hi = (read_lpstatus (q) >> 3) & 0x1f;
write_lpcontrol (q, Autofeed | Reset_N | PCAck | BiDir);
lo2 = qc_waithand2 (q, 0) >> 1;
hi2 = (read_lpstatus (q) >> 3) & 0x1f;
if (q->version == QC_COLOR)
{
/* is Nibble3 inverted for color quickcams only? */
hi ^= 0x10;
hi2 ^= 0x10;
}
switch (s->val[OPT_DEPTH].w)
{
case 4:
buffer[0] = lo & 0xf;
buffer[1] = ((lo & 0x70) >> 4) | ((hi & 1) << 3);
buffer[2] = (hi & 0x1e) >> 1;
buffer[3] = lo2 & 0xf;
buffer[4] = ((lo2 & 0x70) >> 4) | ((hi2 & 1) << 3);
buffer[5] = (hi2 & 0x1e) >> 1;
bytes = 6;
break;
case 6:
buffer[0] = lo & 0x3f;
buffer[1] = ((lo & 0x40) >> 6) | (hi << 1);
buffer[2] = lo2 & 0x3f;
buffer[3] = ((lo2 & 0x40) >> 6) | (hi2 << 1);
bytes = 4;
break;
case 24:
buffer[0] = lo | ((hi & 0x1) << 7);
buffer[1] = ((hi2 & 0x1e) >> 1) | ((hi & 0x1e) << 3);
buffer[2] = lo2 | ((hi2 & 0x1) << 7);
bytes = 3;
break;
}
break;
case QC_UNIDIR: /* Unidirectional Port */
write_lpcontrol (q, Autofeed | Reset_N);
lo = (qc_waithand (q, CamRdy1) & 0xf0) >> 4;
write_lpcontrol (q, Autofeed | Reset_N | PCAck);
hi = (qc_waithand (q, 0) & 0xf0) >> 4;
if (q->version == QC_COLOR)
{
/* invert Nibble3 */
hi ^= 8;
lo ^= 8;
}
switch (s->val[OPT_DEPTH].w)
{
case 4:
buffer[0] = lo;
buffer[1] = hi;
bytes = 2;
break;
case 6:
switch (s->readbytes_state)
{
case 0:
buffer[0] = (lo << 2) | ((hi & 0xc) >> 2);
s->saved_bits = (hi & 3) << 4;
s->readbytes_state = 1;
bytes = 1;
break;
case 1:
buffer[0] = lo | s->saved_bits;
s->saved_bits = hi << 2;
s->readbytes_state = 2;
bytes = 1;
break;
case 2:
buffer[0] = ((lo & 0xc) >> 2) | s->saved_bits;
buffer[1] = ((lo & 3) << 4) | hi;
s->readbytes_state = 0;
bytes = 2;
break;
default:
DBG (1, "qc_readbytes: bad unidir 6-bit stat %d\n",
s->readbytes_state);
break;
}
break;
case 24:
buffer[0] = (lo << 4) | hi;
bytes = 1;
break;
default:
DBG (1, "qc_readbytes: bad unidir bit depth %d\n",
s->val[OPT_DEPTH].w);
break;
}
break;
default:
DBG (1, "qc_readbytes: bad port_mode %d\n", q->port_mode);
break;
}
return bytes;
}
static void
qc_reset (QC_Device * q)
{
write_lpcontrol (q, Strobe | Autofeed | Reset_N | PCAck);
qc_wait (q);
write_lpcontrol (q, Strobe | Autofeed | PCAck);
qc_wait (q);
write_lpcontrol (q, Strobe | Autofeed | Reset_N | PCAck);
}
/* This function is executed as a child process. The reason this is
executed as a subprocess is because the qcam interface directly reads
off of a I/O port (rather than a filedescriptor). Thus, to have
something to select() on, we transfer the data through a pipe.
WARNING: Since this is executed as a subprocess, it's NOT possible
to update any of the variables in the main process (in particular
the scanner state cannot be updated). */
static jmp_buf env;
static void
sighandler (int signal)
{
DBG (3, "sighandler: got signal %d\n", signal);
longjmp (env, 1);
}
/* Original despeckling code by Patrick Reynolds <patrickr@virginia.edu> */
static void
despeckle (int width, int height, SANE_Byte * in, SANE_Byte * out)
{
long x, i;
/* The light-check threshold. Higher numbers remove more lights but
blur the image more. 30 is good for indoor lighting. */
# define NO_LIGHTS 30
/* macros to make the code a little more readable, p=previous, n=next */
# define R in[i*3]
# define G in[i*3+1]
# define B in[i*3+2]
# define pR in[i*3-3]
# define pG in[i*3-2]
# define pB in[i*3-1]
# define nR in[i*3+3]
# define nG in[i*3+4]
# define nB in[i*3+5]
DBG (1, "despeckle: width=%d, height=%d\n", width, height);
for (x = i = 0; i < width * height; ++i)
{
if (x == 0 || x == width - 1)
memcpy (&out[i * 3], &in[i * 3], 3);
else
{
if (R - (G + B) / 2 >
NO_LIGHTS + ((pR - (pG + pB) / 2) + (nR - (nG + nB) / 2)))
out[i * 3] = (pR + nR) / 2;
else
out[i * 3] = R;
if (G - (R + B) / 2 >
NO_LIGHTS + ((pG - (pR + pB) / 2) + (nG - (nR + nB) / 2)))
out[i * 3 + 1] = (pG + nG) / 2;
else
out[i * 3 + 1] = G;
if (B - (G + R) / 2 >
NO_LIGHTS + ((pB - (pG + pR) / 2) + (nB - (nG + nR) / 2)))
out[i * 3 + 2] = (pB + nB) / 2;
else
out[i * 3 + 2] = B;
}
if (++x >= width)
x = 0;
}
# undef R
# undef G
# undef B
# undef pR
# undef pG
# undef pB
# undef nR
# undef nG
# undef nB
}
static void
despeckle32 (int width, int height, SANE_Byte * in, SANE_Byte * out)
{
long x, i;
/* macros to make the code a little more readable, p=previous, n=next */
# define B in[i*4]
# define Ga in[i*4 + 1]
# define Gb in[i*4 + 1] /* ignore Gb and use Ga instead---Gb is weird */
# define R in[i*4 + 3]
# define pB in[i*4 - 4]
# define pGa in[i*4 - 3]
# define pGb in[i*4 - 1] /* ignore Gb and use Ga instead---Gb is weird */
# define pR in[i*4 - 1]
# define nB in[i*4 + 4]
# define nGa in[i*4 + 5]
# define nGb in[i*4 + 5] /* ignore Gb and use Ga instead---Gb is weird */
# define nR in[i*4 + 7]
DBG (1, "despeckle32: width=%d, height=%d\n", width, height);
for (x = i = 0; i < width * height; ++i)
{
if (x == 0 || x == width - 1)
memcpy (&out[i * 4], &in[i * 4], 4);
else
{
if (x >= width - 2)
/* the last red pixel seems to be black at all times, use
R instead: */
nR = R;
if (R - ((Ga + Gb) / 2 + B) / 2 >
NO_LIGHTS + ((pR - ((pGa + pGb) / 2 + pB) / 2) +
(nR - ((nGa + nGb) / 2 + nB) / 2)))
out[i * 4 + 3] = (pR + nR) / 2;
else
out[i * 4 + 3] = R;
if (Ga - (R + B) / 2 > NO_LIGHTS + ((pGa - (pR + pB) / 2) +
(nGa - (nR + nB) / 2)))
out[i * 4 + 1] = (pGa + nGa) / 2;
else
out[i * 4 + 1] = Ga;
if (Gb - (R + B) / 2 > NO_LIGHTS + ((pGb - (pR + pB) / 2) +
(nGb - (nR + nB) / 2)))
out[i * 4 + 2] = (pGb + nGb) / 2;
else
out[i * 4 + 2] = Gb;
if (B - ((Ga + Gb) / 2 + R) / 2 >
NO_LIGHTS + ((pB - ((pGa + pGb) / 2 + pR) / 2) +
(nB - ((nGa + nGb) / 2 + nR) / 2)))
out[i * 4 + 0] = (pB + nB) / 2;
else
out[i * 4 + 0] = B;
}
if (++x >= width)
x = 0;
}
# undef R
# undef Ga
# undef Gb
# undef B
# undef pR
# undef pGa
# undef pGb
# undef pB
# undef nR
# undef nGa
# undef nGb
# undef nB
}
static int
reader_process (QC_Scanner * s, int in_fd, int out_fd)
{
static SANE_Byte *buffer = 0, *extra = 0;
static size_t buffer_size = 0;
size_t count, len, num_bytes;
QC_Device *q = s->hw;
QC_Scan_Request req;
int width, height;
SANE_Byte *src;
FILE *ofp;
DBG (5, "reader_process: enter\n");
enable_ports (q);
ofp = fdopen (out_fd, "w");
if (!ofp)
return 1;
while (1)
{
if (setjmp (env))
{
char ch;
/* acknowledge the signal: */
DBG (1, "reader_process: sending signal ACK\n");
fwrite (&ch, 1, 1, ofp);
fflush (ofp); /* force everything out the pipe */
continue;
}
signal (SIGINT, sighandler);
/* the main process gets us started by writing a size_t giving
the number of bytes we should expect: */
if (read (in_fd, &req, sizeof (req)) != sizeof (req))
{
perror ("read");
return 1;
}
num_bytes = req.num_bytes;
DBG (3, "reader_process: got request for %lu bytes\n",
(u_long) num_bytes);
/* Don't do this in sane_start() since there may be a long
timespan between it and the first sane_read(), which would
result in poor images. */
qc_send (q, QC_SEND_VIDEO_FRAME);
qc_send (q, req.mode);
if (req.despeckle
&& (!extra || buffer_size < num_bytes
|| buffer_size >= 2 * num_bytes))
{
if (extra)
extra = realloc (extra, num_bytes);
else
extra = malloc (num_bytes);
if (!extra)
{
DBG (1, "reader_process: malloc(%ld) failed\n",
(long) num_bytes);
exit (1);
}
}
if (buffer_size < num_bytes || buffer_size >= 2 * num_bytes)
{
if (buffer)
buffer = realloc (buffer, num_bytes);
else
buffer = malloc (num_bytes);
if (!buffer)
{
DBG (1, "reader_process: malloc(%ld) failed\n",
(long) num_bytes);
exit (1);
}
buffer_size = num_bytes;
}
if (q->port_mode == QC_BIDIR)
{
/* turn port into input port */
write_lpcontrol (q, Autofeed | Reset_N | PCAck | BiDir);
usleep (3);
write_lpcontrol (q, Autofeed | Reset_N | BiDir);
qc_waithand (q, CamRdy1);
write_lpcontrol (q, Autofeed | Reset_N | PCAck | BiDir);
qc_waithand (q, 0);
}
if (q->version == QC_COLOR)
for (len = 0; len < num_bytes; len += count)
count = qc_readbytes (s, buffer + len);
else
{
/* strange -- should be 15:63 below, but 4bpp is odd */
int shift, invert;
unsigned int i;
u_char val;
switch (s->val[OPT_DEPTH].w)
{
case 4:
invert = 16;
shift = 4;
break;
case 6:
invert = 63;
shift = 2;
break;
default:
DBG (1, "reader_process: unexpected depth %d\n",
s->val[OPT_DEPTH].w);
return 1;
}
for (len = 0; len < num_bytes; len += count)
{
count = qc_readbytes (s, buffer + len);
for (i = 0; i < count; ++i)
{
/* 4bpp is odd (again) -- inverter is 16, not 15,
but output must be 0-15 */
val = buffer[len + i];
if (val > 0 || invert != 16)
val = invert - val;
buffer[len + i] = (val << shift) | (val >> (8 - 2 * shift));
}
}
qc_readbytes (s, 0); /* reset state machine */
}
/* we're done reading this frame: */
DBG (2, "reader_process: frame complete\n");
if (q->port_mode == QC_BIDIR)
{
/* return port to output mode */
write_lpcontrol (q, Autofeed);
usleep (3);
write_lpcontrol (q, Autofeed | Reset_N);
usleep (3);
write_lpcontrol (q, Autofeed | Reset_N | PCAck);
}
if (req.resolution == QC_RES_HIGH)
{
SANE_Byte buf[6];
int x, y;
/* in billions mode, we need to oversample the data: */
src = buffer;
width = req.params.pixels_per_line;
height = req.params.lines;
if (req.despeckle)
{
despeckle32 (width / 2, req.params.lines / 2, buffer, extra);
src = extra;
}
assert (!(width & 1)); /* width must be even */
for (y = 0; y < height; ++y)
{
/* even line */
for (x = 0; x < width; x += 2)
{
int red1, green1, blue1, green2, blue2;
blue1 = src[0];
green1 = src[1];
red1 = src[3];
if (x >= width - 2)
{
red1 = src[-1]; /* last red seems to be missing */
blue2 = blue1;
green2 = green1;
}
else
{
blue2 = src[4];
green2 = src[5];
}
src += 4;
buf[0] = red1;
buf[1] = green1;
buf[2] = blue1;
buf[3] = red1;
buf[4] = green2;
buf[5] = blue2;
if (fwrite (buf, 1, 6, ofp) != 6)
{
perror ("fwrite: short write");
return 1;
}
}
if (++y >= height)
break;
src -= 2 * width; /* 4 bytes/pixel -> 2 pixels of 3 bytes each */
/* odd line */
for (x = 0; x < width; x += 2)
{
int red1, green3, blue3, green4, blue4;
int yoff;
if (x >= width - 2)
red1 = src[-1]; /* last red seems to be missing */
else
red1 = src[3];
yoff = 2 * width;
if (y >= height - 1)
yoff = 0;
green3 = src[yoff + 1];
blue3 = src[yoff + 0];
if (x >= width - 2)
{
blue4 = blue3;
green4 = green3;
}
else
{
blue4 = src[yoff + 4];
green4 = src[yoff + 5];
}
src += 4;
buf[0] = red1;
buf[1] = green3;
buf[2] = blue3;
buf[3] = red1;
buf[4] = green4;
buf[5] = blue4;
if (fwrite (buf, 1, 6, ofp) != 6)
{
perror ("fwrite: short write");
return 1;
}
}
}
}
else
{
src = buffer;
if (req.despeckle)
{
despeckle (req.params.pixels_per_line, req.params.lines,
buffer, extra);
src = extra;
}
/* now write the whole thing to the main process: */
if (fwrite (src, 1, num_bytes, ofp) != num_bytes)
{
perror ("fwrite: short write");
return 1;
}
}
fflush (ofp);
}
assert (SANE_FALSE); /* not reached */
DBG (5, "reader_process: exit\n");
return 1;
}
static SANE_Status
attach (const char *devname, QC_Device ** devp)
{
int i, n1, n2, s1, s2, cmd, port, force_unidir;
SANE_Status result, status;
QC_Device *q;
char *endp;
DBG (3, "attach: enter\n");
errno = 0;
force_unidir = 0;
if (devname[0] == 'u')
{
force_unidir = 1;
++devname;
}
port = strtol (devname, &endp, 0);
if (endp == devname || errno == ERANGE)
{
DBG (1, "attach: invalid port address `%s'\n", devname);
return SANE_STATUS_INVAL;
}
for (q = first_dev; q; q = q->next)
if (port == q->port)
{
if (devp)
*devp = q;
return SANE_STATUS_GOOD;
}
q = malloc (sizeof (*q));
if (!q)
return SANE_STATUS_NO_MEM;
memset (q, 0, sizeof (*q));
q->port = port;
q->lock_fd = -1;
result = enable_ports (q);
if (result != SANE_STATUS_GOOD)
{
DBG (1, "attach: cannot enable ports (%s)\n", strerror (errno));
free (q);
return SANE_STATUS_INVAL;
}
/* lock camera while we determine its version: */
qc_lock (q);
qc_reset (q);
write_lpdata (q, QC_SEND_VERSION);
qc_wait (q);
write_lpcontrol (q, Autofeed | Reset_N); /* make PCAck inactive */
qc_wait (q);
for (i = 0; (i < 1000) && !(s1 = (n1 = read_lpstatus (q)) & CamRdy1); i++);
if (!s1)
{
DBG (2, "attach: failed to get CamRdy1 at port 0x%x\n", q->port);
goto unlock_and_fail;
}
write_lpcontrol (q, Autofeed | Reset_N | PCAck);
qc_wait (q);
for (i = 0; (i < 1000) && (s2 = (n2 = read_lpstatus (q)) & CamRdy1); i++);
if (s2)
{
DBG (2, "attach: CamRdy1 failed to clear at port 0x%x\n", q->port);
goto unlock_and_fail;
}
cmd = (n1 & 0xf0) | ((n2 & 0xf0) >> 4);
if (cmd != QC_SEND_VERSION)
{
DBG (2, "attach: got 0x%02x instead of 0x%02x\n", cmd, QC_SEND_VERSION);
goto unlock_and_fail;
}
q->version = qc_readparam (q);
DBG (1, "attach: found QuickCam version 0x%02x\n", q->version);
q->port_mode = QC_UNIDIR;
if (!force_unidir)
{
write_lpcontrol (q, BiDir);
write_lpdata (q, 0x75);
if (read_lpdata (q) != 0x75)
q->port_mode = QC_BIDIR;
}
/* For some reason the color quickcam needs two set-black commands
after a reset. Thus, we now set the black-level to some
reasonable value (0) so that the next set-black level command
will really go through. */
if (q->version == QC_COLOR)
{
qc_send (q, QC_SET_BLACK);
qc_send (q, 0);
DBG (3, "attach: resetting black_level\n");
/* wait for set black level command to finish: */
while (qc_getstatus (q) & (CameraNotReady | BlackBalanceInProgress))
usleep (10000);
}
status = qc_unlock (q);
if (status != SANE_STATUS_GOOD)
{
DBG (1, "attach: status qc_unlock NOK\n");
/* status = SANE_STATUS_GOOD; */
}
q->sane.name = strdup (devname);
q->sane.vendor = "Connectix";
q->sane.model =
(q->version == QC_COLOR) ? "Color QuickCam" : "B&W QuickCam";
q->sane.type = "video camera";
++num_devices;
q->next = first_dev;
first_dev = q;
if (devp)
*devp = q;
DBG (3, "attach: exit status OK\n");
status = SANE_STATUS_GOOD;
return status;
unlock_and_fail:
status = qc_unlock (q);
if (status != SANE_STATUS_GOOD)
{
DBG (1, "attach: unlock_and_fail status qc_unlock NOK\n");
}
free (q);
DBG (3, "attach: exit status NOK\n");
status = SANE_STATUS_INVAL;
return status;
}
static SANE_Status
init_options (QC_Scanner * s)
{
int i;
DBG (3, "init_options: enter\n");
memset (s->opt, 0, sizeof (s->opt));
memset (s->val, 0, sizeof (s->val));
for (i = 0; i < NUM_OPTIONS; ++i)
{
s->opt[i].size = sizeof (SANE_Word);
s->opt[i].cap = (SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT);
}
s->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS;
s->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS;
s->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT;
s->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT;
s->val[OPT_NUM_OPTS].w = NUM_OPTIONS;
/* "Mode" group: */
s->opt[OPT_MODE_GROUP].title = "Scan Mode";
s->opt[OPT_MODE_GROUP].desc = "";
s->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP;
s->opt[OPT_MODE_GROUP].cap = 0;
s->opt[OPT_MODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
/* resolution */
s->opt[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION;
s->opt[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION;
s->opt[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION;
s->opt[OPT_RESOLUTION].type = SANE_TYPE_STRING;
s->opt[OPT_RESOLUTION].size = 5; /* sizeof("High") */
s->opt[OPT_RESOLUTION].unit = SANE_UNIT_NONE;
s->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_STRING_LIST;
s->opt[OPT_RESOLUTION].constraint.string_list = resolution_list;
s->val[OPT_RESOLUTION].s = strdup (resolution_list[QC_RES_LOW]);
/* bit-depth */
s->opt[OPT_DEPTH].name = SANE_NAME_BIT_DEPTH;
s->opt[OPT_DEPTH].title = "Pixel depth";
s->opt[OPT_DEPTH].desc = "Number of bits per pixel.";
s->opt[OPT_DEPTH].type = SANE_TYPE_INT;
s->opt[OPT_DEPTH].unit = SANE_UNIT_BIT;
s->opt[OPT_DEPTH].constraint_type = SANE_CONSTRAINT_WORD_LIST;
s->opt[OPT_DEPTH].constraint.word_list = color_depth_list;
s->val[OPT_DEPTH].w = color_depth_list[NELEMS (color_depth_list) - 1];
/* test */
s->opt[OPT_TEST].name = "test-image";
s->opt[OPT_TEST].title = "Force test image";
s->opt[OPT_TEST].desc =
"Acquire a test-image instead of the image seen by the camera. "
"The test image consists of red, green, and blue squares of "
"32x32 pixels each. Use this to find out whether the "
"camera is connected properly.";
s->opt[OPT_TEST].type = SANE_TYPE_BOOL;
s->val[OPT_TEST].w = SANE_FALSE;
/* "Geometry" group: */
s->opt[OPT_GEOMETRY_GROUP].title = "Geometry";
s->opt[OPT_GEOMETRY_GROUP].desc = "";
s->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP;
s->opt[OPT_GEOMETRY_GROUP].cap = SANE_CAP_ADVANCED;
s->opt[OPT_GEOMETRY_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
/* top-left x */
s->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X;
s->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X;
s->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X;
s->opt[OPT_TL_X].type = SANE_TYPE_INT;
s->opt[OPT_TL_X].unit = SANE_UNIT_PIXEL;
s->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE;
s->opt[OPT_TL_X].constraint.range = &x_range[QC_RES_LOW];
s->val[OPT_TL_X].w = 10;
/* top-left y */
s->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y;
s->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y;
s->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y;
s->opt[OPT_TL_Y].type = SANE_TYPE_INT;
s->opt[OPT_TL_Y].unit = SANE_UNIT_PIXEL;
s->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE;
s->opt[OPT_TL_Y].constraint.range = &y_range[QC_RES_LOW];
s->val[OPT_TL_Y].w = 0;
/* bottom-right x */
s->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X;
s->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X;
s->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X;
s->opt[OPT_BR_X].type = SANE_TYPE_INT;
s->opt[OPT_BR_X].unit = SANE_UNIT_PIXEL;
s->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE;
s->opt[OPT_BR_X].constraint.range = &odd_x_range[QC_RES_LOW];
s->val[OPT_BR_X].w = 339;
/* bottom-right y */
s->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y;
s->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y;
s->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y;
s->opt[OPT_BR_Y].type = SANE_TYPE_INT;
s->opt[OPT_BR_Y].unit = SANE_UNIT_PIXEL;
s->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE;
s->opt[OPT_BR_Y].constraint.range = &odd_y_range[QC_RES_LOW];
s->val[OPT_BR_Y].w = 245;
/* xfer-scale */
s->opt[OPT_XFER_SCALE].name = "transfer-scale";
s->opt[OPT_XFER_SCALE].title = "Transfer scale";
s->opt[OPT_XFER_SCALE].desc =
"The transferscale determines how many of the acquired pixels actually "
"get sent to the computer. For example, a transfer scale of 2 would "
"request that every other acquired pixel would be omitted. That is, "
"when scanning a 200 pixel wide and 100 pixel tall area, the resulting "
"image would be only 100x50 pixels large. Using a large transfer scale "
"improves acquisition speed, but reduces resolution.";
s->opt[OPT_XFER_SCALE].type = SANE_TYPE_INT;
s->opt[OPT_XFER_SCALE].constraint_type = SANE_CONSTRAINT_WORD_LIST;
s->opt[OPT_XFER_SCALE].constraint.word_list = xfer_scale_list;
s->val[OPT_XFER_SCALE].w = xfer_scale_list[1];
/* despeckle */
s->opt[OPT_DESPECKLE].name = "despeckle";
s->opt[OPT_DESPECKLE].title = "Speckle filter";
s->opt[OPT_DESPECKLE].desc = "Turning on this filter will remove the "
"christmas lights that are typically present in dark images.";
s->opt[OPT_DESPECKLE].type = SANE_TYPE_BOOL;
s->opt[OPT_DESPECKLE].constraint_type = SANE_CONSTRAINT_NONE;
s->val[OPT_DESPECKLE].w = 0;
/* "Enhancement" group: */
s->opt[OPT_ENHANCEMENT_GROUP].title = "Enhancement";
s->opt[OPT_ENHANCEMENT_GROUP].desc = "";
s->opt[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP;
s->opt[OPT_ENHANCEMENT_GROUP].cap = 0;
s->opt[OPT_ENHANCEMENT_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
/* brightness */
s->opt[OPT_BRIGHTNESS].name = SANE_NAME_BRIGHTNESS;
s->opt[OPT_BRIGHTNESS].title = SANE_TITLE_BRIGHTNESS;
s->opt[OPT_BRIGHTNESS].desc = SANE_DESC_BRIGHTNESS
" In a conventional camera, this control corresponds to the "
"exposure time.";
s->opt[OPT_BRIGHTNESS].type = SANE_TYPE_INT;
s->opt[OPT_BRIGHTNESS].cap |= SANE_CAP_AUTOMATIC;
s->opt[OPT_BRIGHTNESS].constraint_type = SANE_CONSTRAINT_RANGE;
s->opt[OPT_BRIGHTNESS].constraint.range = &brightness_range;
s->val[OPT_BRIGHTNESS].w = 135;
/* contrast */
s->opt[OPT_CONTRAST].name = SANE_NAME_CONTRAST;
s->opt[OPT_CONTRAST].title = SANE_TITLE_CONTRAST;
s->opt[OPT_CONTRAST].desc = SANE_DESC_CONTRAST;
s->opt[OPT_CONTRAST].type = SANE_TYPE_INT;
s->opt[OPT_CONTRAST].constraint_type = SANE_CONSTRAINT_RANGE;
s->opt[OPT_CONTRAST].constraint.range = &u8_range;
s->val[OPT_CONTRAST].w = 104;
/* black-level */
s->opt[OPT_BLACK_LEVEL].name = SANE_NAME_BLACK_LEVEL;
s->opt[OPT_BLACK_LEVEL].title = SANE_TITLE_BLACK_LEVEL;
s->opt[OPT_BLACK_LEVEL].desc = SANE_DESC_BLACK_LEVEL
" This value should be selected so that black areas just start "
"to look really black (not gray).";
s->opt[OPT_BLACK_LEVEL].type = SANE_TYPE_INT;
s->opt[OPT_BLACK_LEVEL].constraint_type = SANE_CONSTRAINT_RANGE;
s->opt[OPT_BLACK_LEVEL].constraint.range = &u8_range;
s->val[OPT_BLACK_LEVEL].w = 0;
/* white-level */
s->opt[OPT_WHITE_LEVEL].name = SANE_NAME_WHITE_LEVEL;
s->opt[OPT_WHITE_LEVEL].title = SANE_TITLE_WHITE_LEVEL;
s->opt[OPT_WHITE_LEVEL].desc = SANE_DESC_WHITE_LEVEL
" This value should be selected so that white areas just start "
"to look really white (not gray).";
s->opt[OPT_WHITE_LEVEL].type = SANE_TYPE_INT;
s->opt[OPT_WHITE_LEVEL].constraint_type = SANE_CONSTRAINT_RANGE;
s->opt[OPT_WHITE_LEVEL].constraint.range = &u8_range;
s->val[OPT_WHITE_LEVEL].w = 150;
/* hue */
s->opt[OPT_HUE].name = SANE_NAME_HUE;
s->opt[OPT_HUE].title = SANE_TITLE_HUE;
s->opt[OPT_HUE].desc = SANE_DESC_HUE;
s->opt[OPT_HUE].type = SANE_TYPE_INT;
s->opt[OPT_HUE].constraint_type = SANE_CONSTRAINT_RANGE;
s->opt[OPT_HUE].constraint.range = &u8_range;
s->val[OPT_HUE].w = 128;
/* saturation */
s->opt[OPT_SATURATION].name = SANE_NAME_SATURATION;
s->opt[OPT_SATURATION].title = SANE_TITLE_SATURATION;
s->opt[OPT_SATURATION].desc = SANE_DESC_SATURATION;
s->opt[OPT_SATURATION].type = SANE_TYPE_INT;
s->opt[OPT_SATURATION].constraint_type = SANE_CONSTRAINT_RANGE;
s->opt[OPT_SATURATION].constraint.range = &u8_range;
s->val[OPT_SATURATION].w = 100;
DBG (3, "init_options: exit\n");
return SANE_STATUS_GOOD;
}
SANE_Status
sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize)
{
char dev_name[PATH_MAX], *str;
size_t len;
FILE *fp;
(void) authorize; /* silence compilation warnings */
DBG_INIT ();
DBG (1, "sane_init: enter\n");
if (version_code)
*version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, 0);
fp = sanei_config_open (QCAM_CONFIG_FILE);
if (!fp)
{
DBG (1, "sane_init: file `%s' not accessible\n", QCAM_CONFIG_FILE);
return SANE_STATUS_INVAL;
}
while (sanei_config_read (dev_name, sizeof (dev_name), fp))
{
if (dev_name[0] == '#') /* ignore line comments */
continue;
len = strlen (dev_name);
if (!len)
continue; /* ignore empty lines */
for (str = dev_name; *str && !isspace (*str) && *str != '#'; ++str);
*str = '\0';
attach (dev_name, 0);
}
fclose (fp);
DBG (1, "sane_init: exit\n");
return SANE_STATUS_GOOD;
}
void
sane_exit (void)
{
QC_Device *dev, *next;
static const SANE_Device **devlist;
DBG (5, "sane_exit: enter\n");
for (dev = first_dev; dev; dev = next)
{
next = dev->next;
free ((void *) dev->sane.name);
disable_ports (dev);
free (dev);
}
if (devlist) {
free (devlist);
devlist = NULL;
}
DBG (5, "sane_exit: exit\n");
}
SANE_Status
sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only)
{
static const SANE_Device **devlist = 0;
QC_Device *dev;
int i;
DBG (5, "sane_get_devices: enter\n");
(void) local_only; /* silence compilation warnings */
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 (5, "sane_get_devices: exit\n");
return SANE_STATUS_GOOD;
}
SANE_Status
sane_open (SANE_String_Const devicename, SANE_Handle * handle)
{
SANE_Status status;
QC_Device *dev;
QC_Scanner *s;
DBG (5, "sane_open: enter: (devicename = %s)\n", devicename);
if (devicename[0])
{
status = attach (devicename, &dev);
if (status != SANE_STATUS_GOOD)
return status;
}
else
/* empty devicname -> use first device */
dev = first_dev;
if (!dev)
return SANE_STATUS_INVAL;
s = malloc (sizeof (*s));
if (!s)
return SANE_STATUS_NO_MEM;
memset (s, 0, sizeof (*s));
s->user_corner = 0;
s->hw = dev;
s->value_changed = ~0; /* ensure all options get updated */
s->reader_pid = -1;
s->to_child = -1;
s->from_child = -1;
s->read_fd = -1;
init_options (s);
/* The contrast option seems to have an effect for b&w cameras only,
so don't give the user the impression that this is a useful thing
to set... */
if (s->hw->version == QC_COLOR)
s->opt[OPT_CONTRAST].cap |= SANE_CAP_INACTIVE;
else
{
/* Black level, Hue and Saturation are things the b&w cameras
know nothing about. Despeckle might be useful, but this code
seems to work for color cameras only right now. The framesize
seems to work better in these ranges. */
s->opt[OPT_DESPECKLE].cap |= SANE_CAP_INACTIVE;
s->opt[OPT_BLACK_LEVEL].cap |= SANE_CAP_INACTIVE;
s->opt[OPT_HUE].cap |= SANE_CAP_INACTIVE;
s->opt[OPT_SATURATION].cap |= SANE_CAP_INACTIVE;
s->opt[OPT_RESOLUTION].cap |= SANE_CAP_INACTIVE;
s->opt[OPT_TEST].cap |= SANE_CAP_INACTIVE;
s->opt[OPT_DEPTH].constraint.word_list = mono_depth_list;
s->val[OPT_DEPTH].w = mono_depth_list[NELEMS (mono_depth_list) - 1];
s->opt[OPT_TL_X].constraint.range = &bw_x_range;
s->val[OPT_TL_X].w = 14;
s->opt[OPT_TL_Y].constraint.range = &bw_y_range;
s->val[OPT_TL_Y].w = 0;
s->opt[OPT_BR_X].constraint.range = &odd_bw_x_range;
s->val[OPT_BR_X].w = 333;
s->opt[OPT_BR_Y].constraint.range = &odd_bw_y_range;
s->val[OPT_BR_Y].w = 239;
s->val[OPT_BRIGHTNESS].w = 170;
s->val[OPT_CONTRAST].w = 150;
s->val[OPT_WHITE_LEVEL].w = 150;
}
/* insert newly opened handle into list of open handles: */
s->next = first_handle;
first_handle = s;
*handle = s;
DBG (5, "sane_open: exit\n");
return SANE_STATUS_GOOD;
}
void
sane_close (SANE_Handle handle)
{
QC_Scanner *prev, *s;
DBG (5, "sane_close: enter\n");
/* remove handle from list of open handles: */
prev = 0;
for (s = first_handle; s; s = s->next)
{
if (s == handle)
break;
prev = s;
}
if (!s)
{
DBG (1, "sane_close: bad handle %p\n", handle);
return; /* oops, not a handle we know about */
}
if (prev)
prev->next = s->next;
else
first_handle = s->next;
if (s->scanning)
sane_cancel (handle);
if (s->reader_pid >= 0)
{
kill (s->reader_pid, SIGTERM);
waitpid (s->reader_pid, 0, 0);
s->reader_pid = 0;
}
if (s->to_child >= 0)
close (s->to_child);
if (s->from_child >= 0)
close (s->from_child);
if (s->read_fd >= 0)
close (s->read_fd);
free (s);
DBG (5, "sane_close: exit\n");
}
const SANE_Option_Descriptor *
sane_get_option_descriptor (SANE_Handle handle, SANE_Int option)
{
QC_Scanner *s = handle;
DBG (5, "sane_get_option_descriptor: enter\n");
if ((unsigned) option >= NUM_OPTIONS)
return 0;
DBG (5, "sane_get_option_descriptor: exit\n");
return s->opt + option;
}
SANE_Status
sane_control_option (SANE_Handle handle, SANE_Int option,
SANE_Action action, void *val, SANE_Int * info)
{
QC_Scanner *s = handle;
QC_Resolution old_res;
SANE_Status status;
SANE_Word cap;
char *old_val;
int i;
DBG (5, "sane_control_option: enter\n");
if (info)
*info = 0;
if (option >= NUM_OPTIONS)
return SANE_STATUS_INVAL;
cap = s->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_DEPTH:
case OPT_DESPECKLE:
case OPT_TEST:
case OPT_TL_X:
case OPT_TL_Y:
case OPT_BR_X:
case OPT_BR_Y:
case OPT_XFER_SCALE:
case OPT_BRIGHTNESS:
case OPT_CONTRAST:
case OPT_BLACK_LEVEL:
case OPT_WHITE_LEVEL:
case OPT_HUE:
case OPT_SATURATION:
*(SANE_Word *) val = s->val[option].w;
return SANE_STATUS_GOOD;
/* string options: */
case OPT_RESOLUTION:
strcpy (val, s->val[option].s);
return SANE_STATUS_GOOD;
default:
DBG (1, "control_option: option %d unknown\n", option);
}
}
else if (action == SANE_ACTION_SET_VALUE)
{
if (!SANE_OPTION_IS_SETTABLE (cap))
return SANE_STATUS_INVAL;
status = sanei_constrain_value (s->opt + option, val, info);
if (status != SANE_STATUS_GOOD)
return status;
if (option >= OPT_TL_X && option <= OPT_BR_Y)
s->user_corner |= 1 << (option - OPT_TL_X);
assert (option <= 31);
s->value_changed |= 1 << option;
switch (option)
{
/* (mostly) side-effect-free word options: */
case OPT_TL_X:
case OPT_TL_Y:
case OPT_BR_X:
case OPT_BR_Y:
case OPT_XFER_SCALE:
case OPT_DEPTH:
if (!s->scanning && info && s->val[option].w != *(SANE_Word *) val)
/* only signal the reload params if we're not scanning---no point
in creating the frontend useless work */
*info |= SANE_INFO_RELOAD_PARAMS;
/* fall through */
case OPT_NUM_OPTS:
case OPT_TEST:
case OPT_DESPECKLE:
case OPT_BRIGHTNESS:
case OPT_CONTRAST:
case OPT_BLACK_LEVEL:
case OPT_WHITE_LEVEL:
case OPT_HUE:
case OPT_SATURATION:
s->val[option].w = *(SANE_Word *) val;
return SANE_STATUS_GOOD;
/* options with side-effects: */
case OPT_RESOLUTION:
old_val = s->val[OPT_RESOLUTION].s;
if (strcmp (old_val, val) != 0)
return SANE_STATUS_GOOD; /* no change */
if (info)
{
*info |= SANE_INFO_RELOAD_OPTIONS;
if (!s->scanning)
*info |= SANE_INFO_RELOAD_PARAMS;
}
free (old_val);
s->val[OPT_RESOLUTION].s = strdup (val);
/* low-resolution mode: */
old_res = s->resolution;
s->resolution = QC_RES_LOW;
if (strcmp (val, resolution_list[QC_RES_HIGH]) == 0)
/* high-resolution mode: */
s->resolution = QC_RES_HIGH;
s->opt[OPT_TL_X].constraint.range = &x_range[s->resolution];
s->opt[OPT_BR_X].constraint.range = &odd_x_range[s->resolution];
s->opt[OPT_TL_Y].constraint.range = &y_range[s->resolution];
s->opt[OPT_BR_Y].constraint.range = &odd_y_range[s->resolution];
if (old_res == QC_RES_LOW && s->resolution == QC_RES_HIGH)
{
for (i = OPT_TL_X; i <= OPT_BR_Y; ++i)
s->val[i].w *= 2;
s->val[OPT_BR_X].w += 1;
s->val[OPT_BR_Y].w += 1;
s->opt[OPT_TEST].cap |= SANE_CAP_INACTIVE;
}
else if (old_res == QC_RES_HIGH && s->resolution == QC_RES_LOW)
{
for (i = OPT_TL_X; i <= OPT_BR_Y; ++i)
s->val[i].w /= 2;
s->opt[OPT_TEST].cap &= ~SANE_CAP_INACTIVE;
}
if (!(s->user_corner & 0x4))
s->val[OPT_BR_X].w = odd_x_range[s->resolution].max;
if (!(s->user_corner & 0x8))
s->val[OPT_BR_Y].w = odd_y_range[s->resolution].max - 4;
/* make sure the affected options have valid values: */
for (i = OPT_TL_X; i <= OPT_BR_Y; ++i)
if (s->val[i].w > s->opt[i].constraint.range->max)
s->val[i].w = s->opt[i].constraint.range->max;
DBG (5, "sane_control_option: exit\n");
return SANE_STATUS_GOOD;
}
}
else if (action == SANE_ACTION_SET_AUTO)
{
switch (option)
{
case OPT_BRIGHTNESS:
/* not implemented yet */
DBG (5, "sane_control_option: exit\n");
return SANE_STATUS_GOOD;
default:
break;
}
}
DBG (5, "sane_control_option: NOK exit\n");
return SANE_STATUS_INVAL;
}
SANE_Status
sane_get_parameters (SANE_Handle handle, SANE_Parameters * params)
{
QC_Scanner *s = handle;
QC_Device *q = s->hw;
int xfer_scale;
size_t Bpp = 3; /* # of bytes per pixel */
DBG (5, "sane_get_parameters: enter\n");
if (!s->scanning)
{
/* Only compute new parameters when not scanning---allows
changing width/height etc while scan is in progress. */
xfer_scale = s->val[OPT_XFER_SCALE].w;
s->params.format = SANE_FRAME_RGB;
if (q->version != QC_COLOR)
{
s->params.format = SANE_FRAME_GRAY;
Bpp = 1;
}
s->params.last_frame = SANE_TRUE;
s->params.pixels_per_line = s->val[OPT_BR_X].w - s->val[OPT_TL_X].w + 1;
s->params.pixels_per_line /= xfer_scale;
s->params.pixels_per_line &= ~1UL; /* ensure it's even */
if (s->params.pixels_per_line < 2)
s->params.pixels_per_line = 2;
s->params.lines = s->val[OPT_BR_Y].w - s->val[OPT_TL_Y].w + 1;
s->params.lines /= xfer_scale;
if (s->params.lines < 1)
s->params.lines = 1;
s->params.bytes_per_line = Bpp * s->params.pixels_per_line;
s->params.depth = 8;
}
if (params)
*params = s->params;
DBG (5, "sane_get_parameters: exit\n");
return SANE_STATUS_GOOD;
}
SANE_Status
sane_start (SANE_Handle handle)
{
int top, left, width, height, undecimated_width, undecimated_height;
QC_Scanner *s = handle;
QC_Device *q = s->hw;
QC_Scan_Request req;
DBG (5, "sane_start: enter\n");
if (s->scanning)
return SANE_STATUS_DEVICE_BUSY;
if (s->reader_pid < 0)
{
int p2c_pipe[2]; /* parent->child pipe */
int c2p_pipe[2]; /* child->parent pipe */
if (pipe (p2c_pipe) < 0 || pipe (c2p_pipe) < 0)
{
DBG (3, "start: failed to create pipes\n");
return SANE_STATUS_IO_ERROR;
}
s->reader_pid = fork ();
if (s->reader_pid == 0)
{
/* this is the child */
signal (SIGHUP, SIG_DFL);
signal (SIGINT, SIG_DFL);
signal (SIGPIPE, SIG_DFL);
signal (SIGTERM, SIG_DFL);
_exit (reader_process (s, p2c_pipe[0], c2p_pipe[1]));
}
close (p2c_pipe[0]);
close (c2p_pipe[1]);
s->to_child = p2c_pipe[1];
s->from_child = c2p_pipe[0];
}
s->read_fd = dup (s->from_child);
sane_get_parameters (s, 0); /* ensure up-to-date parameters */
qc_lock (q);
s->holding_lock = SANE_TRUE;
if (q->version == QC_COLOR)
{
qc_send (q, QC_SET_SPEED);
qc_send (q, 2);
/* wait for camera to become ready: */
while (qc_getstatus (q) & CameraNotReady)
usleep (10000);
/* Only send black_level if necessary; this optimization may
fail if two applications access the camera in an interleaved
fashion; but the black-level command is slow enough that it
cannot be issued for every image acquisition. */
if (s->value_changed & (1 << OPT_BLACK_LEVEL))
{
s->value_changed &= ~(1 << OPT_BLACK_LEVEL);
qc_send (q, QC_SET_BLACK);
qc_send (q, s->val[OPT_BLACK_LEVEL].w);
DBG (3, "start: black_level=%d\n", s->val[OPT_BLACK_LEVEL].w);
/* wait for set black level command to finish: */
while (qc_getstatus (q) & (CameraNotReady | BlackBalanceInProgress))
usleep (10000);
}
if (s->value_changed & (1 << OPT_HUE))
{
s->value_changed &= ~(1 << OPT_HUE);
qc_send (q, QC_COL_SET_HUE);
qc_send (q, s->val[OPT_HUE].w);
}
if (s->value_changed & (1 << OPT_SATURATION))
{
s->value_changed &= ~(1 << OPT_SATURATION);
qc_send (q, QC_SET_SATURATION);
qc_send (q, s->val[OPT_SATURATION].w);
}
}
if (q->version != QC_COLOR)
qc_reset (q);
if (s->value_changed & (1 << OPT_CONTRAST))
{
s->value_changed &= ~(1 << OPT_CONTRAST);
qc_send (q, ((q->version == QC_COLOR)
? QC_COL_SET_CONTRAST : QC_MONO_SET_CONTRAST));
qc_send (q, s->val[OPT_CONTRAST].w);
}
if (s->value_changed & (1 << OPT_BRIGHTNESS))
{
s->value_changed &= ~(1 << OPT_BRIGHTNESS);
qc_send (q, QC_SET_BRIGHTNESS);
qc_send (q, s->val[OPT_BRIGHTNESS].w);
}
width = s->params.pixels_per_line;
height = s->params.lines;
if (s->resolution == QC_RES_HIGH)
{
width /= 2; /* the expansion occurs through oversampling */
height /= 2; /* we acquire only half the lines that we generate */
}
undecimated_width = width * s->val[OPT_XFER_SCALE].w;
undecimated_height = height * s->val[OPT_XFER_SCALE].w;
s->num_bytes = 0;
s->bytes_per_frame = s->params.lines * s->params.bytes_per_line;
qc_send (q, QC_SET_NUM_V);
qc_send (q, undecimated_height);
if (q->version == QC_COLOR)
{
qc_send (q, QC_SET_NUM_H);
qc_send (q, undecimated_width / 2);
}
else
{
int val, val2;
if (q->port_mode == QC_UNIDIR && s->val[OPT_DEPTH].w == 6)
{
val = undecimated_width;
val2 = s->val[OPT_XFER_SCALE].w * 4;
}
else
{
val = undecimated_width * s->val[OPT_DEPTH].w;
val2 =
((q->port_mode == QC_BIDIR) ? 24 : 8) * s->val[OPT_XFER_SCALE].w;
}
val = (val + val2 - 1) / val2;
qc_send (q, QC_SET_NUM_H);
qc_send (q, val);
}
left = s->val[OPT_TL_X].w / 2;
top = s->val[OPT_TL_Y].w;
if (s->resolution == QC_RES_HIGH)
{
left /= 2;
top /= 2;
}
DBG (3, "sane_start: top=%d, left=%d, white=%d, bright=%d, contr=%d\n",
top, left, s->val[OPT_WHITE_LEVEL].w, s->val[OPT_BRIGHTNESS].w,
s->val[OPT_CONTRAST].w);
qc_send (q, QC_SET_LEFT);
qc_send (q, left);
qc_send (q, QC_SET_TOP);
qc_send (q, top + 1); /* not sure why this is so... ;-( */
if (s->value_changed & (1 << OPT_WHITE_LEVEL))
{
s->value_changed &= ~(1 << OPT_WHITE_LEVEL);
qc_send (q, QC_SET_WHITE);
qc_send (q, s->val[OPT_WHITE_LEVEL].w);
}
DBG (2, "start: %s %d lines of %d pixels each (%ld bytes) => %dx%d\n",
(q->port_mode == QC_BIDIR) ? "bidir" : "unidir",
height, width, (long) s->bytes_per_frame,
s->params.pixels_per_line, s->params.lines);
/* send scan request to reader process: */
qc_setscanmode (s, &req.mode);
req.num_bytes = width * height;
if (q->version == QC_COLOR)
{
if (s->resolution == QC_RES_LOW)
req.num_bytes *= 3;
else
req.num_bytes *= 4;
}
req.resolution = s->resolution;
req.params = s->params;
req.despeckle = s->val[OPT_DESPECKLE].w;
write (s->to_child, &req, sizeof (req));
s->scanning = SANE_TRUE;
s->deliver_eof = 0;
DBG (5, "sane_start: exit\n");
return SANE_STATUS_GOOD;
}
SANE_Status
sane_read (SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len,
SANE_Int * lenp)
{
SANE_Status status;
QC_Scanner *s = handle;
QC_Device *q = s->hw;
ssize_t nread;
size_t len;
DBG (5, "sane_read: enter\n");
*lenp = 0;
if (s->deliver_eof)
{
s->deliver_eof = 0;
return SANE_STATUS_EOF;
}
if (!s->scanning)
return SANE_STATUS_CANCELLED;
len = max_len;
if (s->num_bytes + len > s->bytes_per_frame)
len = s->bytes_per_frame - s->num_bytes;
DBG (8, "read(buf=%p,num_bytes=%ld,max_len=%d,len=%ld)\n",
(void *) buf, (long) s->num_bytes, max_len, (long) len);
nread = read (s->read_fd, buf, len);
if (nread <= 0)
{
if (nread == 0 || errno == EAGAIN)
{
DBG (3, "read: no more data available\n");
return SANE_STATUS_GOOD;
}
DBG (3, "read: short read (%s)\n", strerror (errno));
return SANE_STATUS_IO_ERROR;
}
if (nread > 0 && s->holding_lock)
{
status = qc_unlock (q); /* now we can unlock the camera */
if (status != SANE_STATUS_GOOD)
DBG(3, "sane_read: qc_unlock error\n");
s->holding_lock = SANE_FALSE;
}
s->num_bytes += nread;
if (s->num_bytes >= s->bytes_per_frame)
{
s->scanning = SANE_FALSE;
close (s->read_fd);
s->read_fd = -1;
s->deliver_eof = 1;
}
if (lenp)
*lenp = nread;
DBG (5, "sane_read: exit, read got %d bytes\n", *lenp);
return SANE_STATUS_GOOD;
}
void
sane_cancel (SANE_Handle handle)
{
QC_Scanner *s = handle;
SANE_Bool was_scanning;
SANE_Status status;
DBG (5, "sane_cancel: enter\n");
was_scanning = s->scanning;
s->scanning = SANE_FALSE;
s->deliver_eof = 0;
if (s->read_fd >= 0)
{
close (s->read_fd);
s->read_fd = -1;
}
if (s->reader_pid >= 0 && was_scanning)
{
char buf[1024];
ssize_t nread;
int flags;
DBG (1, "cancel: cancelling read request\n");
kill (s->reader_pid, SIGINT); /* tell reader to stop reading */
/* save non-blocking i/o flags: */
flags = fcntl (s->from_child, F_GETFL, 0);
/* block until we read at least one byte: */
read (s->from_child, buf, 1);
/* put descriptor in non-blocking i/o: */
fcntl (s->from_child, F_SETFL, O_NONBLOCK);
/* read what's left over in the pipe/file buffer: */
do
{
while ((nread = read (s->from_child, buf, sizeof (buf))) > 0);
usleep (100000);
nread = read (s->from_child, buf, sizeof (buf));
}
while (nread > 0);
/* now restore non-blocking i/o flag: */
fcntl (s->from_child, F_SETFL, flags & O_NONBLOCK);
waitpid (s->reader_pid, 0, 0);
s->reader_pid = 0;
DBG (1, "cancel: cancellation completed\n");
}
if (s->holding_lock)
{
status = qc_unlock (s->hw);
if (status != SANE_STATUS_GOOD)
DBG(3, "sane_cancel: qc_unlock error\n");
s->holding_lock = SANE_FALSE;
}
DBG (5, "sane_cancel: exit\n");
}
SANE_Status
sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking)
{
QC_Scanner *s = handle;
DBG (5, "sane_set_io_mode: enter\n");
if (!s->scanning)
return SANE_STATUS_INVAL;
if (fcntl (s->read_fd, F_SETFL, non_blocking ? O_NONBLOCK : 0) < 0)
return SANE_STATUS_IO_ERROR;
DBG (5, "sane_set_io_mode: exit\n");
return SANE_STATUS_GOOD;
}
SANE_Status
sane_get_select_fd (SANE_Handle handle, SANE_Int * fd)
{
QC_Scanner *s = handle;
DBG (5, "sane_get_select_fd: enter\n");
if (!s->scanning)
return SANE_STATUS_INVAL;
*fd = s->read_fd;
DBG (5, "sane_get_select_fd: exit\n");
return SANE_STATUS_GOOD;
}