kopia lustrzana https://gitlab.com/sane-project/backends
1532 wiersze
34 KiB
C
1532 wiersze
34 KiB
C
/***************************************************************************
|
|
* SANE - Scanner Access Now Easy.
|
|
|
|
dc210.c
|
|
|
|
11/11/98
|
|
|
|
This file (C) 1998 Brian J. Murrell
|
|
|
|
This file is part of the SANE package.
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU General Public License as
|
|
published by the Free Software Foundation; either version 2 of the
|
|
License, or (at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful, but
|
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place - Suite 330, Boston,
|
|
MA 02111-1307, USA.
|
|
|
|
As a special exception, the authors of SANE give permission for
|
|
additional uses of the libraries contained in this release of SANE.
|
|
|
|
The exception is that, if you link a SANE library with other files
|
|
to produce an executable, this does not by itself cause the
|
|
resulting executable to be covered by the GNU General Public
|
|
License. Your use of that executable is in no way restricted on
|
|
account of linking the SANE library code into it.
|
|
|
|
This exception does not, however, invalidate any other reasons why
|
|
the executable file might be covered by the GNU General Public
|
|
License.
|
|
|
|
If you submit changes to SANE to the maintainers to be included in
|
|
a subsequent release, you agree by submitting the changes that
|
|
those changes may be distributed with this exception intact.
|
|
|
|
If you write modifications of your own for SANE, it is your choice
|
|
whether to permit this exception to apply to your modifications.
|
|
If you do not wish that, delete this exception notice.
|
|
|
|
***************************************************************************
|
|
|
|
This file implements a SANE backend for the Kodak DC-210
|
|
digital camera. THIS IS EXTREMELY ALPHA CODE! USE AT YOUR OWN RISK!!
|
|
|
|
(feedback to: sane-dc210@interlinx.bc.ca
|
|
|
|
This backend is based somewhat on the dc25 backend included in this
|
|
package by Peter Fales
|
|
|
|
***************************************************************************/
|
|
|
|
#include "sane/config.h"
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <limits.h>
|
|
#include "cdjpeg.h"
|
|
#include <sys/ioctl.h>
|
|
|
|
#include "sane/sane.h"
|
|
#include "sane/sanei.h"
|
|
#include "sane/saneopts.h"
|
|
|
|
#define BACKEND_NAME dc210
|
|
#include "sane/sanei_backend.h"
|
|
|
|
#include "dc210.h"
|
|
|
|
#ifndef PATH_MAX
|
|
# define PATH_MAX 1024
|
|
#endif
|
|
|
|
#define MAGIC (void *)0xab730324
|
|
#define DC210_CONFIG_FILE "dc210.conf"
|
|
#define THUMBSIZE 20736
|
|
|
|
#ifdef B115200
|
|
# define DEFAULT_BAUD_RATE B115200
|
|
#else
|
|
# define DEFAULT_BAUD_RATE B38400
|
|
#endif
|
|
|
|
#if defined (__sgi)
|
|
# define DEFAULT_TTY "/dev/ttyd1" /* Irix */
|
|
#elif defined (__sun)
|
|
# define DEFAULT_TTY "/dev/term/a" /* Solaris */
|
|
#elif defined (hpux)
|
|
# define DEFAULT_TTY "/dev/tty1d0" /* HP-UX */
|
|
#elif defined (__osf__)
|
|
# define DEFAULT_TTY "/dev/tty00" /* Digital UNIX */
|
|
#else
|
|
# define DEFAULT_TTY "/dev/ttyS0" /* Linux */
|
|
#endif
|
|
|
|
static SANE_Bool is_open = 0;
|
|
|
|
static SANE_Bool dc210_opt_thumbnails;
|
|
static SANE_Bool dc210_opt_snap;
|
|
static SANE_Bool dc210_opt_lowres;
|
|
static SANE_Bool dc210_opt_erase;
|
|
static SANE_Bool dumpinquiry;
|
|
|
|
static struct jpeg_decompress_struct cinfo;
|
|
static djpeg_dest_ptr dest_mgr = NULL;
|
|
|
|
static unsigned long cmdrespause = 250000UL; /* pause after sending cmd */
|
|
static unsigned long breakpause = 1000000UL; /* pause after sending break */
|
|
|
|
static int bytes_in_buffer;
|
|
static int bytes_read_from_buffer;
|
|
static int total_bytes_read;
|
|
|
|
static DC210 Camera;
|
|
|
|
static SANE_Range image_range = {
|
|
0,
|
|
14,
|
|
0
|
|
};
|
|
|
|
static SANE_Option_Descriptor sod[] = {
|
|
{
|
|
SANE_NAME_NUM_OPTIONS,
|
|
SANE_TITLE_NUM_OPTIONS,
|
|
SANE_DESC_NUM_OPTIONS,
|
|
SANE_TYPE_INT,
|
|
SANE_UNIT_NONE,
|
|
sizeof (SANE_Word),
|
|
SANE_CAP_SOFT_DETECT,
|
|
SANE_CONSTRAINT_NONE,
|
|
{NULL}
|
|
}
|
|
,
|
|
|
|
#define D25_OPT_IMAGE_SELECTION 1
|
|
{
|
|
"",
|
|
"Image Selection",
|
|
"Selection of the image to load.",
|
|
SANE_TYPE_GROUP,
|
|
SANE_UNIT_NONE,
|
|
0,
|
|
0,
|
|
SANE_CONSTRAINT_NONE,
|
|
{NULL}
|
|
}
|
|
,
|
|
|
|
#define DC210_OPT_IMAGE_NUMBER 2
|
|
{
|
|
"image",
|
|
"Image Number",
|
|
"Select Image Number to load from camera",
|
|
SANE_TYPE_INT,
|
|
SANE_UNIT_NONE,
|
|
4,
|
|
SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT,
|
|
SANE_CONSTRAINT_RANGE,
|
|
{(SANE_String_Const *) & image_range} /* this is ANSI conformant! */
|
|
}
|
|
,
|
|
|
|
#define DC210_OPT_THUMBS 3
|
|
{
|
|
"thumbs",
|
|
"Load Thumbnail",
|
|
"Load the image as thumbnail.",
|
|
SANE_TYPE_BOOL,
|
|
SANE_UNIT_NONE,
|
|
sizeof (SANE_Word),
|
|
SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT,
|
|
SANE_CONSTRAINT_NONE,
|
|
{NULL}
|
|
}
|
|
,
|
|
#define DC210_OPT_SNAP 4
|
|
{
|
|
"snap",
|
|
"Snap new picture",
|
|
"Take new picture and download it",
|
|
SANE_TYPE_BOOL,
|
|
SANE_UNIT_NONE,
|
|
sizeof (SANE_Word),
|
|
SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT /* | SANE_CAP_ADVANCED */ ,
|
|
SANE_CONSTRAINT_NONE,
|
|
{NULL}
|
|
}
|
|
,
|
|
#define DC210_OPT_LOWRES 5
|
|
{
|
|
"lowres",
|
|
"Low Resolution",
|
|
"Resolution of new pictures",
|
|
SANE_TYPE_BOOL,
|
|
SANE_UNIT_NONE,
|
|
sizeof (SANE_Word),
|
|
SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_INACTIVE
|
|
/* | SANE_CAP_ADVANCED */ ,
|
|
SANE_CONSTRAINT_NONE,
|
|
{NULL}
|
|
}
|
|
,
|
|
|
|
#define DC210_OPT_ERASE 6
|
|
{
|
|
"erase",
|
|
"Erase",
|
|
"Erase the picture after downloading",
|
|
SANE_TYPE_BOOL,
|
|
SANE_UNIT_NONE,
|
|
sizeof (SANE_Word),
|
|
SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT,
|
|
SANE_CONSTRAINT_NONE,
|
|
{NULL}
|
|
}
|
|
,
|
|
|
|
#define DC210_OPT_DEFAULT 7
|
|
{
|
|
"default-enhancements",
|
|
"Defaults",
|
|
"Set default values for enhancement controls.",
|
|
SANE_TYPE_BUTTON,
|
|
SANE_UNIT_NONE,
|
|
0,
|
|
SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT,
|
|
SANE_CONSTRAINT_NONE,
|
|
{NULL}
|
|
}
|
|
,
|
|
#define DC210_OPT_INIT_DC210 8
|
|
{
|
|
"camera-init",
|
|
"Re-establish Communications",
|
|
"Re-establish communications with camera (in case of timeout, etc.)",
|
|
SANE_TYPE_BUTTON,
|
|
SANE_UNIT_NONE,
|
|
0,
|
|
SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT,
|
|
SANE_CONSTRAINT_NONE,
|
|
{NULL}
|
|
}
|
|
};
|
|
|
|
static SANE_Parameters parms = {
|
|
SANE_FRAME_RGB,
|
|
0,
|
|
0, /* Number of bytes returned per scan line: */
|
|
0, /* Number of pixels per scan line. */
|
|
0, /* Number of lines for the current scan. */
|
|
8, /* Number of bits per sample. */
|
|
};
|
|
|
|
|
|
|
|
|
|
static unsigned char shoot_pck[] = SHOOT_PCK;
|
|
static unsigned char init_pck[] = INIT_PCK;
|
|
static unsigned char thumb_pck[] = THUMBS_PCK;
|
|
static unsigned char pic_pck[] = PICS_PCK;
|
|
static unsigned char pic_info_pck[] = PICS_INFO_PCK;
|
|
static unsigned char info_pck[] = INFO_PCK;
|
|
static unsigned char erase_pck[] = ERASE_PCK;
|
|
static unsigned char res_pck[] = RES_PCK;
|
|
|
|
static struct pkt_speed speeds[] = SPEEDS;
|
|
static struct termios tty_orig;
|
|
|
|
#include <sys/time.h>
|
|
#include <unistd.h>
|
|
|
|
static int
|
|
send_pck (int fd, unsigned char *pck)
|
|
{
|
|
int n;
|
|
unsigned char r = 0xf0; /* prime the loop with a "camera busy" */
|
|
|
|
/* keep trying if camera says it's busy */
|
|
while (r == 0xf0)
|
|
{
|
|
/*
|
|
* Not quite sure why we need this, but the program works a whole
|
|
* lot better (at least on the DC210) with this short delay.
|
|
*/
|
|
|
|
if (write (fd, (char *) pck, 8) != 8)
|
|
{
|
|
DBG (2, "send_pck: error: write returned -1\n");
|
|
return -1;
|
|
}
|
|
/* need to wait before we read command result */
|
|
usleep (cmdrespause);
|
|
|
|
if ((n = read (fd, (char *) &r, 1)) != 1)
|
|
{
|
|
DBG (2, "send_pck: error: read returned -1\n");
|
|
return -1;
|
|
}
|
|
}
|
|
return (r == 0xd1) ? 0 : -1;
|
|
}
|
|
|
|
static int
|
|
init_dc210 (DC210 * camera)
|
|
{
|
|
struct termios tty_new;
|
|
int speed_index;
|
|
|
|
for (speed_index = 0; speed_index < NELEMS (speeds); speed_index++)
|
|
{
|
|
if (speeds[speed_index].baud == camera->baud)
|
|
{
|
|
init_pck[2] = speeds[speed_index].pkt_code[0];
|
|
init_pck[3] = speeds[speed_index].pkt_code[1];
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (init_pck[2] == 0)
|
|
{
|
|
DBG (2, "unsupported baud rate.\n");
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
Open device file.
|
|
*/
|
|
if ((camera->fd = open (camera->tty_name, O_RDWR)) == -1)
|
|
{
|
|
DBG (2, "init_dc210: error: could not open %s for read/write\n",
|
|
camera->tty_name);
|
|
return -1;
|
|
}
|
|
/*
|
|
Save old device information to restore when we are done.
|
|
*/
|
|
if (tcgetattr (camera->fd, &tty_orig) == -1)
|
|
{
|
|
DBG (2, "init_dc210: error: could not get attributes\n");
|
|
return -1;
|
|
}
|
|
|
|
memcpy ((char *) &tty_new, (char *) &tty_orig, sizeof (struct termios));
|
|
/*
|
|
We need the device to be raw. 8 bits even parity on 9600 baud to start.
|
|
*/
|
|
#ifdef HAVE_CFMAKERAW
|
|
cfmakeraw (&tty_new);
|
|
#else
|
|
/* Modified to set the port REALLY as required. Code inspired by
|
|
the gPhoto2 serial port setup */
|
|
|
|
/* input control settings */
|
|
tty_new.c_iflag &= ~(IGNBRK | IGNCR | INLCR | ICRNL | IUCLC |
|
|
IXANY | IXON | IXOFF | INPCK | ISTRIP);
|
|
tty_new.c_iflag |= (BRKINT | IGNPAR);
|
|
/* output control settings */
|
|
tty_new.c_oflag &= ~OPOST;
|
|
/* hardware control settings */
|
|
tty_new.c_cflag = (tty_new.c_cflag & ~CSIZE) | CS8;
|
|
tty_new.c_cflag &= ~(PARENB | PARODD | CSTOPB);
|
|
# if defined(__sgi)
|
|
tty_new.c_cflag &= ~CNEW_RTSCTS;
|
|
# else
|
|
/* OS/2 doesn't have CRTSCTS - will this work for them? */
|
|
# ifdef CRTSCTS
|
|
tty_new.c_cflag &= ~CRTSCTS;
|
|
# endif
|
|
# endif
|
|
tty_new.c_cflag |= CLOCAL | CREAD;
|
|
#endif
|
|
/* line discipline settings */
|
|
tty_new.c_lflag &= ~(ICANON | ISIG | ECHO | ECHONL | ECHOE |
|
|
ECHOK | IEXTEN);
|
|
tty_new.c_cc[VMIN] = 0;
|
|
tty_new.c_cc[VTIME] = 5;
|
|
cfsetospeed (&tty_new, B9600);
|
|
cfsetispeed (&tty_new, B9600);
|
|
|
|
if (tcsetattr (camera->fd, TCSANOW, &tty_new) == -1)
|
|
{
|
|
DBG (2, "init_dc210: error: could not set attributes\n");
|
|
return -1;
|
|
}
|
|
|
|
/* send a break to get it back to a known state */
|
|
/* Used to supply a non-zero argument to tcsendbreak(), TCSBRK,
|
|
* and TCSBRKP, but that is system dependent. e.g. on irix a non-zero
|
|
* value does a drain instead of a break. A zero value is universally
|
|
* used to send a break.
|
|
*/
|
|
|
|
#ifdef HAVE_TCSENDBREAK
|
|
tcsendbreak (camera->fd, 0);
|
|
# if defined(__sgi)
|
|
tcdrain (camera->fd);
|
|
# endif
|
|
# elif defined(TCSBRKP)
|
|
ioctl (camera->fd, TCSBRKP, 0);
|
|
# elif defined(TCSBRK)
|
|
ioctl (camera->fd, TCSBRK, 0);
|
|
#endif
|
|
|
|
/* and wait for it to recover from the break */
|
|
|
|
#ifdef HAVE_USLEEP
|
|
usleep (breakpause);
|
|
#else
|
|
sleep (1);
|
|
#endif
|
|
|
|
if (send_pck (camera->fd, init_pck) == -1)
|
|
{
|
|
/*
|
|
* The camera always powers up at 9600, so we try
|
|
* that first. However, it may be already set to
|
|
* a different speed. Try the entries in the table:
|
|
*/
|
|
|
|
for (speed_index = NELEMS (speeds) - 1; speed_index > 0; speed_index--)
|
|
{
|
|
int x;
|
|
DBG (3, "init_dc210: changing speed to %d\n",
|
|
(int) speeds[speed_index].baud);
|
|
|
|
cfsetospeed (&tty_new, speeds[speed_index].baud);
|
|
cfsetispeed (&tty_new, speeds[speed_index].baud);
|
|
|
|
if (tcsetattr (camera->fd, TCSANOW, &tty_new) == -1)
|
|
{
|
|
DBG (2, "init_dc210: error: could not set attributes\n");
|
|
return -1;
|
|
}
|
|
for (x = 0; x < 3; x++)
|
|
if (send_pck (camera->fd, init_pck) != -1)
|
|
break;
|
|
}
|
|
|
|
if (speed_index == 0)
|
|
{
|
|
tcsetattr (camera->fd, TCSANOW, &tty_orig);
|
|
DBG (2, "init_dc210: error: no suitable baud rate\n");
|
|
return -1;
|
|
}
|
|
}
|
|
/*
|
|
Set speed to requested speed.
|
|
*/
|
|
cfsetospeed (&tty_new, Camera.baud);
|
|
cfsetispeed (&tty_new, Camera.baud);
|
|
|
|
if (tcsetattr (camera->fd, TCSANOW, &tty_new) == -1)
|
|
{
|
|
DBG (2, "init_dc210: error: could not set attributes\n");
|
|
return -1;
|
|
}
|
|
|
|
return camera->fd;
|
|
}
|
|
|
|
static void
|
|
close_dc210 (int fd)
|
|
{
|
|
/*
|
|
* Put the camera back to 9600 baud
|
|
*/
|
|
|
|
if (close (fd) == -1)
|
|
{
|
|
DBG (4, "close_dc210: error: could not close device\n");
|
|
}
|
|
}
|
|
|
|
int
|
|
get_info (DC210 * camera)
|
|
{
|
|
|
|
char f[] = "get_info";
|
|
unsigned char buf[256];
|
|
|
|
if (send_pck (camera->fd, info_pck) == -1)
|
|
{
|
|
DBG (2, "%s: error: send_pck returned -1\n", f);
|
|
return -1;
|
|
}
|
|
|
|
DBG (9, "%s: read info packet\n", f);
|
|
|
|
if (read_data (camera->fd, buf, 256) == -1)
|
|
{
|
|
DBG (2, "%s: error: read_data returned -1\n", f);
|
|
return -1;
|
|
}
|
|
|
|
if (end_of_data (camera->fd) == -1)
|
|
{
|
|
DBG (2, "%s: error: end_of_data returned -1\n", f);
|
|
return -1;
|
|
}
|
|
|
|
camera->model = buf[1];
|
|
camera->ver_major = buf[2];
|
|
camera->ver_minor = buf[3];
|
|
camera->pic_taken = buf[56] << 8 | buf[57];
|
|
camera->pic_left = buf[72] << 8 | buf[73];
|
|
camera->flags.low_res = buf[22];
|
|
camera->flags.low_batt = buf[8];
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
read_data (int fd, unsigned char *buf, int sz)
|
|
{
|
|
unsigned char ccsum;
|
|
unsigned char rcsum;
|
|
unsigned char c;
|
|
int n;
|
|
int r = 0;
|
|
int i;
|
|
|
|
/* read the control byte */
|
|
if (read (fd, &c, 1) != 1)
|
|
{
|
|
DBG (2,
|
|
"read_data: error: read for packet control byte returned bad status\n");
|
|
return -1;
|
|
}
|
|
if (c != 1)
|
|
{
|
|
DBG (2, "read_data: error: incorrect packet control byte: %02x\n", c);
|
|
return -1;
|
|
}
|
|
for (n = 0; n < sz && (r = read (fd, (char *) &buf[n], sz - n)) > 0; n += r)
|
|
;
|
|
|
|
if (r <= 0)
|
|
{
|
|
DBG (2, "read_data: error: read returned -1\n");
|
|
return -1;
|
|
}
|
|
|
|
if (n < sz || read (fd, &rcsum, 1) != 1)
|
|
{
|
|
DBG (2, "read_data: error: buffer underrun or no checksum\n");
|
|
return -1;
|
|
}
|
|
|
|
for (i = 0, ccsum = 0; i < n; i++)
|
|
ccsum ^= buf[i];
|
|
|
|
if (ccsum != rcsum)
|
|
{
|
|
DBG (2, "read_data: error: bad checksum (%02x !=%02x)\n", rcsum, ccsum);
|
|
return -1;
|
|
}
|
|
|
|
c = 0xd2;
|
|
|
|
if (write (fd, (char *) &c, 1) != 1)
|
|
{
|
|
DBG (2, "read_data: error: write ack\n");
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
end_of_data (int fd)
|
|
{
|
|
unsigned char c;
|
|
|
|
do
|
|
{ /* loop until the camera isn't busy */
|
|
if (read (fd, &c, 1) != 1)
|
|
{
|
|
DBG (2, "end_of_data: error: read returned -1\n");
|
|
return -1;
|
|
}
|
|
if (c == 0) /* got successful end of data */
|
|
return 0; /* return success */
|
|
sleep (1); /* not too fast */
|
|
}
|
|
while (c == 0xf0);
|
|
|
|
/* Accck! Not busy, but not a good end of data either */
|
|
if (c != 0)
|
|
{
|
|
DBG (2, "end_of_data: error: bad EOD from camera (%02x)\n",
|
|
(unsigned) c);
|
|
return -1;
|
|
}
|
|
return 0; /* should never get here but shut gcc -Wall up */
|
|
}
|
|
|
|
static int
|
|
erase (int fd)
|
|
{
|
|
if (send_pck (fd, erase_pck) == -1)
|
|
{
|
|
DBG (3, "erase: error: send_pck returned -1\n");
|
|
return -1;
|
|
}
|
|
|
|
if (end_of_data (fd) == -1)
|
|
{
|
|
DBG (3, "erase: error: end_of_data returned -1\n");
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
change_res (int fd, unsigned char res)
|
|
{
|
|
char f[] = "change_res";
|
|
|
|
DBG (127, "%s called\n", f);
|
|
if (res != 0 && res != 1)
|
|
{
|
|
DBG (3, "%s: error: unsupported resolution\n", f);
|
|
return -1;
|
|
}
|
|
|
|
/* cameras resolution semantics are opposite of ours */
|
|
res = !res;
|
|
DBG (127, "%s: setting res to %d\n", f, res);
|
|
res_pck[2] = res;
|
|
|
|
if (send_pck (fd, res_pck) == -1)
|
|
{
|
|
DBG (4, "%s: error: send_pck returned -1\n", f);
|
|
}
|
|
|
|
if (end_of_data (fd) == -1)
|
|
{
|
|
DBG (4, "%s: error: end_of_data returned -1\n", f);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
SANE_Status
|
|
sane_init (SANE_Int * version_code, SANE_Auth_Callback UNUSEDARG authorize)
|
|
{
|
|
|
|
char f[] = "sane_init";
|
|
char dev_name[PATH_MAX], *p;
|
|
size_t len;
|
|
FILE *fp;
|
|
int baud;
|
|
|
|
DBG_INIT ();
|
|
|
|
if (version_code)
|
|
*version_code = SANE_VERSION_CODE (V_MAJOR, V_MINOR, 0);
|
|
|
|
fp = sanei_config_open (DC210_CONFIG_FILE);
|
|
|
|
/* defaults */
|
|
Camera.baud = DEFAULT_BAUD_RATE;
|
|
Camera.tty_name = DEFAULT_TTY;
|
|
|
|
if (!fp)
|
|
{
|
|
/* default to /dev/whatever instead of insisting on config file */
|
|
DBG (1, "%s: missing config file '%s'\n", f, DC210_CONFIG_FILE);
|
|
}
|
|
else
|
|
{
|
|
while (sanei_config_read (dev_name, sizeof (dev_name), fp))
|
|
{
|
|
dev_name[sizeof (dev_name) - 1] = '\0';
|
|
DBG (20, "%s: config- %s\n", f, dev_name);
|
|
|
|
if (dev_name[0] == '#')
|
|
continue; /* ignore line comments */
|
|
len = strlen (dev_name);
|
|
if (!len)
|
|
continue; /* ignore empty lines */
|
|
if (strncmp (dev_name, "port=", 5) == 0)
|
|
{
|
|
p = strchr (dev_name, '/');
|
|
if (p)
|
|
Camera.tty_name = strdup (p);
|
|
DBG (20, "Config file port=%s\n", Camera.tty_name);
|
|
}
|
|
else if (strncmp (dev_name, "baud=", 5) == 0)
|
|
{
|
|
baud = atoi (&dev_name[5]);
|
|
switch (baud)
|
|
{
|
|
case 9600:
|
|
Camera.baud = B9600;
|
|
break;
|
|
case 19200:
|
|
Camera.baud = B19200;
|
|
break;
|
|
case 38400:
|
|
Camera.baud = B38400;
|
|
break;
|
|
#ifdef B57600
|
|
case 57600:
|
|
Camera.baud = B57600;
|
|
break;
|
|
#endif
|
|
#ifdef B115200
|
|
case 115200:
|
|
Camera.baud = B115200;
|
|
break;
|
|
#endif
|
|
}
|
|
DBG (20, "Config file baud=%d\n", Camera.baud);
|
|
}
|
|
else if (strcmp (dev_name, "dumpinquiry") == 0)
|
|
{
|
|
dumpinquiry = SANE_TRUE;
|
|
}
|
|
else if (strncmp (dev_name, "cmdrespause=", 12) == 0)
|
|
{
|
|
cmdrespause = atoi (&dev_name[12]);
|
|
DBG (20, "Config file cmdrespause=%lu\n", cmdrespause);
|
|
}
|
|
else if (strncmp (dev_name, "breakpause=", 11) == 0)
|
|
{
|
|
breakpause = atoi (&dev_name[11]);
|
|
DBG (20, "Config file breakpause=%lu\n", breakpause);
|
|
}
|
|
}
|
|
fclose (fp);
|
|
}
|
|
|
|
if (init_dc210 (&Camera) == -1)
|
|
return SANE_STATUS_INVAL;
|
|
|
|
if (get_info (&Camera) == -1)
|
|
{
|
|
DBG (2, "error: could not get info\n");
|
|
close_dc210 (Camera.fd);
|
|
return SANE_STATUS_INVAL;
|
|
}
|
|
if (Camera.pic_taken == 0)
|
|
{
|
|
sod[DC210_OPT_IMAGE_NUMBER].cap |= SANE_CAP_INACTIVE;
|
|
image_range.min = 0;
|
|
image_range.max = 0;
|
|
}
|
|
else
|
|
{
|
|
sod[DC210_OPT_IMAGE_NUMBER].cap &= ~SANE_CAP_INACTIVE;
|
|
image_range.min = 1;
|
|
image_range.max = Camera.pic_taken;
|
|
}
|
|
|
|
|
|
/* load the current images array */
|
|
Camera.Pictures = get_pictures_info ();
|
|
|
|
if (Camera.pic_taken == 0)
|
|
{
|
|
Camera.current_picture_number = 0;
|
|
parms.bytes_per_line = 0;
|
|
parms.pixels_per_line = 0;
|
|
parms.lines = 0;
|
|
}
|
|
else
|
|
{
|
|
Camera.current_picture_number = 1;
|
|
if (Camera.Pictures[Camera.current_picture_number - 1].low_res)
|
|
{
|
|
parms.bytes_per_line = 640 * 3;
|
|
parms.pixels_per_line = 640;
|
|
parms.lines = 480;
|
|
}
|
|
else
|
|
{
|
|
parms.bytes_per_line = 1152 * 3;
|
|
parms.pixels_per_line = 1152;
|
|
parms.lines = 864;
|
|
}
|
|
}
|
|
|
|
if (dumpinquiry)
|
|
{
|
|
DBG (0, "\nCamera information:\n~~~~~~~~~~~~~~~~~\n\n");
|
|
DBG (0, "Model...........: DC%x\n", Camera.model);
|
|
DBG (0, "Firmware version: %d.%d\n", Camera.ver_major,
|
|
Camera.ver_minor);
|
|
DBG (0, "Pictures........: %d/%d\n", Camera.pic_taken,
|
|
Camera.pic_taken + Camera.pic_left);
|
|
DBG (0, "Resolution......: %s\n",
|
|
Camera.flags.low_res ? "low" : "high");
|
|
DBG (0, "Battery state...: %s\n",
|
|
Camera.flags.low_batt ? "low" : "good");
|
|
}
|
|
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
|
|
void
|
|
sane_exit (void)
|
|
{
|
|
}
|
|
|
|
/* Device select/open/close */
|
|
|
|
static const SANE_Device dev[] = {
|
|
{
|
|
"0",
|
|
"Kodak",
|
|
"DC-210",
|
|
"still camera"},
|
|
};
|
|
|
|
SANE_Status
|
|
sane_get_devices (const SANE_Device *** device_list,
|
|
SANE_Bool UNUSEDARG local_only)
|
|
{
|
|
static const SANE_Device *devlist[] = {
|
|
dev + 0, 0
|
|
};
|
|
|
|
DBG (127, "sane_get_devices called\n");
|
|
|
|
*device_list = devlist;
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
|
|
SANE_Status
|
|
sane_open (SANE_String_Const devicename, SANE_Handle * handle)
|
|
{
|
|
int i;
|
|
|
|
DBG (127, "sane_open for device %s\n", devicename);
|
|
if (!devicename[0])
|
|
{
|
|
i = 0;
|
|
}
|
|
else
|
|
{
|
|
for (i = 0; i < NELEMS (dev); ++i)
|
|
{
|
|
if (strcmp (devicename, dev[i].name) == 0)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (i >= NELEMS (dev))
|
|
{
|
|
return SANE_STATUS_INVAL;
|
|
}
|
|
|
|
if (is_open)
|
|
{
|
|
return SANE_STATUS_DEVICE_BUSY;
|
|
}
|
|
|
|
is_open = 1;
|
|
*handle = MAGIC;
|
|
|
|
DBG (3, "sane_open: pictures taken=%d\n", Camera.pic_taken);
|
|
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
|
|
void
|
|
sane_close (SANE_Handle handle)
|
|
{
|
|
DBG (127, "sane_close called\n");
|
|
if (handle == MAGIC)
|
|
is_open = 0;
|
|
|
|
DBG (127, "sane_close returning\n");
|
|
}
|
|
|
|
const SANE_Option_Descriptor *
|
|
sane_get_option_descriptor (SANE_Handle handle, SANE_Int option)
|
|
{
|
|
if (handle != MAGIC || !is_open)
|
|
return NULL; /* wrong device */
|
|
if (option < 0 || option >= NELEMS (sod))
|
|
return NULL;
|
|
return &sod[option];
|
|
}
|
|
|
|
SANE_Status
|
|
sane_control_option (SANE_Handle handle, SANE_Int option,
|
|
SANE_Action action, void *value, SANE_Int * info)
|
|
{
|
|
SANE_Int myinfo = 0;
|
|
SANE_Status status;
|
|
|
|
DBG (127, "control_option(handle=%p,opt=%s,act=%s,val=%p,info=%p)\n",
|
|
handle, sod[option].title,
|
|
(action ==
|
|
SANE_ACTION_SET_VALUE ? "SET" : (action ==
|
|
SANE_ACTION_GET_VALUE ? "GET" :
|
|
"SETAUTO")), value, (void *)info);
|
|
|
|
if (handle != MAGIC || !is_open)
|
|
return SANE_STATUS_INVAL; /* Unknown handle ... */
|
|
|
|
if (option < 0 || option >= NELEMS (sod))
|
|
return SANE_STATUS_INVAL; /* Unknown option ... */
|
|
|
|
switch (action)
|
|
{
|
|
case SANE_ACTION_SET_VALUE:
|
|
status = sanei_constrain_value (sod + option, value, &myinfo);
|
|
if (status != SANE_STATUS_GOOD)
|
|
{
|
|
DBG (1, "Constraint error in control_option\n");
|
|
return status;
|
|
}
|
|
|
|
switch (option)
|
|
{
|
|
case DC210_OPT_IMAGE_NUMBER:
|
|
Camera.current_picture_number = *(SANE_Word *) value;
|
|
myinfo |= SANE_INFO_RELOAD_PARAMS;
|
|
/* get the image's resolution */
|
|
|
|
if (Camera.Pictures[Camera.current_picture_number - 1].low_res)
|
|
{
|
|
parms.bytes_per_line = 640 * 3;
|
|
parms.pixels_per_line = 640;
|
|
parms.lines = 480;
|
|
}
|
|
else
|
|
{
|
|
parms.bytes_per_line = 1152 * 3;
|
|
parms.pixels_per_line = 1152;
|
|
parms.lines = 864;
|
|
}
|
|
break;
|
|
|
|
case DC210_OPT_THUMBS:
|
|
dc210_opt_thumbnails = !!*(SANE_Word *) value;
|
|
myinfo |= SANE_INFO_RELOAD_PARAMS;
|
|
|
|
if (dc210_opt_thumbnails)
|
|
{
|
|
/*
|
|
* DC210 thumbnail are 96x72x8x3
|
|
*/
|
|
parms.bytes_per_line = 96 * 3;
|
|
parms.pixels_per_line = 96;
|
|
parms.lines = 72;
|
|
}
|
|
else
|
|
{
|
|
if (Camera.Pictures[Camera.current_picture_number - 1].low_res)
|
|
{
|
|
parms.bytes_per_line = 640 * 3;
|
|
parms.pixels_per_line = 640;
|
|
parms.lines = 480;
|
|
}
|
|
else
|
|
{
|
|
parms.bytes_per_line = 1152 * 3;
|
|
parms.pixels_per_line = 1152;
|
|
parms.lines = 864;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case DC210_OPT_SNAP:
|
|
dc210_opt_snap = !!*(SANE_Word *) value;
|
|
myinfo |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
|
|
/* if we are snapping a new one */
|
|
if (dc210_opt_snap)
|
|
{
|
|
/* activate the resolution setting */
|
|
sod[DC210_OPT_LOWRES].cap &= ~SANE_CAP_INACTIVE;
|
|
/* and de-activate the image number selector */
|
|
sod[DC210_OPT_IMAGE_NUMBER].cap |= SANE_CAP_INACTIVE;
|
|
}
|
|
else
|
|
{
|
|
/* deactivate the resolution setting */
|
|
sod[DC210_OPT_LOWRES].cap |= SANE_CAP_INACTIVE;
|
|
/* and activate the image number selector */
|
|
sod[DC210_OPT_IMAGE_NUMBER].cap &= ~SANE_CAP_INACTIVE;
|
|
}
|
|
/* set params according to resolution settings */
|
|
if (dc210_opt_lowres)
|
|
{
|
|
parms.bytes_per_line = 640 * 3;
|
|
parms.pixels_per_line = 640;
|
|
parms.lines = 480;
|
|
}
|
|
else
|
|
{
|
|
parms.bytes_per_line = 1152 * 3;
|
|
parms.pixels_per_line = 1152;
|
|
parms.lines = 864;
|
|
}
|
|
break;
|
|
|
|
case DC210_OPT_LOWRES:
|
|
dc210_opt_lowres = !!*(SANE_Word *) value;
|
|
myinfo |= SANE_INFO_RELOAD_PARAMS;
|
|
|
|
if (!dc210_opt_thumbnails)
|
|
{
|
|
|
|
/* XXX - change the number of pictures left depending on resolution
|
|
perhaps just call get_info again?
|
|
*/
|
|
if (dc210_opt_lowres)
|
|
{
|
|
parms.bytes_per_line = 640 * 3;
|
|
parms.pixels_per_line = 640;
|
|
parms.lines = 480;
|
|
}
|
|
else
|
|
{
|
|
parms.bytes_per_line = 1152 * 3;
|
|
parms.pixels_per_line = 1152;
|
|
parms.lines = 864;
|
|
}
|
|
|
|
}
|
|
break;
|
|
|
|
case DC210_OPT_ERASE:
|
|
dc210_opt_erase = !!*(SANE_Word *) value;
|
|
break;
|
|
|
|
case DC210_OPT_DEFAULT:
|
|
DBG (1, "Fixme: Set all defaults here!\n");
|
|
break;
|
|
case DC210_OPT_INIT_DC210:
|
|
if ((Camera.fd = init_dc210 (&Camera)) == -1)
|
|
{
|
|
return SANE_STATUS_INVAL;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
return SANE_STATUS_INVAL;
|
|
}
|
|
break;
|
|
|
|
case SANE_ACTION_GET_VALUE:
|
|
switch (option)
|
|
{
|
|
case 0:
|
|
*(SANE_Word *) value = NELEMS (sod);
|
|
break;
|
|
|
|
case DC210_OPT_IMAGE_NUMBER:
|
|
*(SANE_Word *) value = Camera.current_picture_number;
|
|
break;
|
|
|
|
case DC210_OPT_THUMBS:
|
|
*(SANE_Word *) value = dc210_opt_thumbnails;
|
|
break;
|
|
|
|
case DC210_OPT_SNAP:
|
|
*(SANE_Word *) value = dc210_opt_snap;
|
|
break;
|
|
|
|
case DC210_OPT_LOWRES:
|
|
*(SANE_Word *) value = dc210_opt_lowres;
|
|
break;
|
|
|
|
case DC210_OPT_ERASE:
|
|
*(SANE_Word *) value = dc210_opt_erase;
|
|
break;
|
|
|
|
default:
|
|
return SANE_STATUS_INVAL;
|
|
}
|
|
break;
|
|
|
|
case SANE_ACTION_SET_AUTO:
|
|
switch (option)
|
|
{
|
|
default:
|
|
return SANE_STATUS_UNSUPPORTED; /* We are DUMB */
|
|
}
|
|
}
|
|
|
|
if (info)
|
|
*info = myinfo;
|
|
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
|
|
SANE_Status
|
|
sane_get_parameters (SANE_Handle handle, SANE_Parameters * params)
|
|
{
|
|
int rc = SANE_STATUS_GOOD;
|
|
|
|
DBG (127, "sane_get_params called\n");
|
|
|
|
if (handle != MAGIC || !is_open)
|
|
rc = SANE_STATUS_INVAL; /* Unknown handle ... */
|
|
|
|
parms.last_frame = SANE_TRUE; /* Have no idea what this does */
|
|
*params = parms;
|
|
DBG (127, "sane_get_params return %d\n", rc);
|
|
return rc;
|
|
}
|
|
|
|
typedef struct
|
|
{
|
|
struct jpeg_source_mgr pub;
|
|
JOCTET *buffer;
|
|
}
|
|
my_source_mgr;
|
|
typedef my_source_mgr *my_src_ptr;
|
|
|
|
METHODDEF (void)
|
|
sanei_jpeg_init_source (j_decompress_ptr UNUSEDARG cinfo)
|
|
{
|
|
/* nothing to do */
|
|
}
|
|
|
|
METHODDEF (boolean) sanei_jpeg_fill_input_buffer (j_decompress_ptr cinfo)
|
|
{
|
|
|
|
my_src_ptr src = (my_src_ptr) cinfo->src;
|
|
|
|
if (read_data (Camera.fd, src->buffer, 1024) == -1)
|
|
{
|
|
DBG (5, "sane_start: read_data failed\n");
|
|
src->buffer[0] = (JOCTET) 0xFF;
|
|
src->buffer[1] = (JOCTET) JPEG_EOI;
|
|
return FALSE;
|
|
}
|
|
src->pub.next_input_byte = src->buffer;
|
|
src->pub.bytes_in_buffer = 1024;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
METHODDEF (void)
|
|
sanei_jpeg_skip_input_data (j_decompress_ptr cinfo, long num_bytes)
|
|
{
|
|
|
|
my_src_ptr src = (my_src_ptr) cinfo->src;
|
|
|
|
if (num_bytes > 0)
|
|
{
|
|
while (num_bytes > (long) src->pub.bytes_in_buffer)
|
|
{
|
|
num_bytes -= (long) src->pub.bytes_in_buffer;
|
|
(void) sanei_jpeg_fill_input_buffer (cinfo);
|
|
}
|
|
}
|
|
src->pub.next_input_byte += (size_t) num_bytes;
|
|
src->pub.bytes_in_buffer -= (size_t) num_bytes;
|
|
}
|
|
|
|
METHODDEF (void)
|
|
sanei_jpeg_term_source (j_decompress_ptr UNUSEDARG cinfo)
|
|
{
|
|
/* no work necessary here */
|
|
}
|
|
|
|
SANE_Status
|
|
sane_start (SANE_Handle handle)
|
|
{
|
|
|
|
DBG (127, "sane_start called\n");
|
|
if (handle != MAGIC || !is_open ||
|
|
(Camera.current_picture_number == 0 && dc210_opt_snap == SANE_FALSE))
|
|
return SANE_STATUS_INVAL; /* Unknown handle ... */
|
|
|
|
if (Camera.scanning)
|
|
return SANE_STATUS_EOF;
|
|
|
|
if (dc210_opt_snap)
|
|
{
|
|
|
|
/*
|
|
* Don't allow picture unless there is room in the
|
|
* camera.
|
|
*/
|
|
if (Camera.pic_left == 0)
|
|
{
|
|
DBG (3, "No room to store new picture\n");
|
|
return SANE_STATUS_INVAL;
|
|
}
|
|
|
|
|
|
if (snap_pic (Camera.fd) != SANE_STATUS_GOOD)
|
|
{
|
|
DBG (1, "Failed to snap new picture\n");
|
|
return SANE_STATUS_INVAL;
|
|
}
|
|
}
|
|
|
|
if (dc210_opt_thumbnails)
|
|
{
|
|
|
|
thumb_pck[3] = (unsigned char) Camera.current_picture_number - 1;
|
|
thumb_pck[4] = 1;
|
|
|
|
if (send_pck (Camera.fd, thumb_pck) == -1)
|
|
{
|
|
DBG (4, "sane_start: error: send_pck returned -1\n");
|
|
return SANE_STATUS_INVAL;
|
|
}
|
|
|
|
parms.bytes_per_line = 96 * 3;
|
|
parms.pixels_per_line = 96;
|
|
parms.lines = 72;
|
|
|
|
bytes_in_buffer = 0;
|
|
bytes_read_from_buffer = 0;
|
|
|
|
}
|
|
else
|
|
{
|
|
my_src_ptr src;
|
|
|
|
struct jpeg_error_mgr jerr;
|
|
int row_stride;
|
|
|
|
pic_pck[3] = (unsigned char) Camera.current_picture_number - 1;
|
|
|
|
if (send_pck (Camera.fd, pic_pck) == -1)
|
|
{
|
|
DBG (4, "sane_start: error: send_pck returned -1\n");
|
|
return SANE_STATUS_INVAL;
|
|
}
|
|
cinfo.err = jpeg_std_error (&jerr);
|
|
jpeg_create_decompress (&cinfo);
|
|
|
|
cinfo.src = (struct jpeg_source_mgr *) (*cinfo.mem->alloc_small) ((j_common_ptr) & cinfo, JPOOL_PERMANENT, sizeof (my_source_mgr));
|
|
src = (my_src_ptr) cinfo.src;
|
|
|
|
src->buffer = (JOCTET *) (*cinfo.mem->alloc_small) ((j_common_ptr) &
|
|
cinfo,
|
|
JPOOL_PERMANENT,
|
|
1024 *
|
|
sizeof (JOCTET));
|
|
src->pub.init_source = sanei_jpeg_init_source;
|
|
src->pub.fill_input_buffer = sanei_jpeg_fill_input_buffer;
|
|
src->pub.skip_input_data = sanei_jpeg_skip_input_data;
|
|
src->pub.resync_to_restart = jpeg_resync_to_restart; /* default */
|
|
src->pub.term_source = sanei_jpeg_term_source;
|
|
src->pub.bytes_in_buffer = 0;
|
|
src->pub.next_input_byte = NULL;
|
|
|
|
(void) jpeg_read_header (&cinfo, TRUE);
|
|
dest_mgr = sanei_jpeg_jinit_write_ppm (&cinfo);
|
|
(void) jpeg_start_decompress (&cinfo);
|
|
row_stride = cinfo.output_width * cinfo.output_components;
|
|
|
|
}
|
|
|
|
Camera.scanning = SANE_TRUE; /* don't overlap scan requests */
|
|
total_bytes_read = 0;
|
|
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
|
|
SANE_Status
|
|
sane_read (SANE_Handle UNUSEDARG handle, SANE_Byte * data,
|
|
SANE_Int max_length, SANE_Int * length)
|
|
{
|
|
|
|
static char buffer[1024];
|
|
|
|
if (dc210_opt_thumbnails)
|
|
{
|
|
if (total_bytes_read == THUMBSIZE)
|
|
{
|
|
if (dc210_opt_erase)
|
|
{
|
|
if (erase (Camera.fd) == -1)
|
|
{
|
|
DBG (1, "Failed to erase memory\n");
|
|
return SANE_STATUS_INVAL;
|
|
}
|
|
Camera.pic_taken--;
|
|
Camera.pic_left++;
|
|
Camera.current_picture_number = Camera.pic_taken;
|
|
image_range.max--;
|
|
}
|
|
return SANE_STATUS_EOF;
|
|
}
|
|
|
|
*length = 0;
|
|
if (!(bytes_in_buffer - bytes_read_from_buffer))
|
|
{
|
|
if (read_data (Camera.fd, (unsigned char *) buffer, 1024) == -1)
|
|
{
|
|
DBG (5, "sane_read: read_data failed\n");
|
|
return SANE_STATUS_INVAL;
|
|
}
|
|
bytes_in_buffer = 1024;
|
|
bytes_read_from_buffer = 0;
|
|
}
|
|
|
|
while (bytes_read_from_buffer < bytes_in_buffer &&
|
|
max_length && total_bytes_read < THUMBSIZE)
|
|
{
|
|
*data++ = buffer[bytes_read_from_buffer++];
|
|
(*length)++;
|
|
max_length--;
|
|
total_bytes_read++;
|
|
}
|
|
|
|
if (total_bytes_read == THUMBSIZE)
|
|
{
|
|
if (end_of_data (Camera.fd) == -1)
|
|
{
|
|
DBG (4, "sane_read: end_of_data error\n");
|
|
return SANE_STATUS_INVAL;
|
|
}
|
|
else
|
|
{
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int lines = 0;
|
|
|
|
if (cinfo.output_scanline >= cinfo.output_height)
|
|
{
|
|
/* clean up comms with the camera */
|
|
if (end_of_data (Camera.fd) == -1)
|
|
{
|
|
DBG (2, "sane_read: error: end_of_data returned -1\n");
|
|
return SANE_STATUS_INVAL;
|
|
}
|
|
if (dc210_opt_erase)
|
|
{
|
|
DBG (127, "sane_read bp%d, erase image\n", __LINE__);
|
|
if (erase (Camera.fd) == -1)
|
|
{
|
|
DBG (1, "Failed to erase memory\n");
|
|
return SANE_STATUS_INVAL;
|
|
}
|
|
Camera.pic_taken--;
|
|
Camera.pic_left++;
|
|
Camera.current_picture_number = Camera.pic_taken;
|
|
image_range.max--;
|
|
}
|
|
return SANE_STATUS_EOF;
|
|
}
|
|
|
|
/* XXX - we should read more than 1 line at a time here */
|
|
lines = 1;
|
|
(void) jpeg_read_scanlines (&cinfo, dest_mgr->buffer, lines);
|
|
(*dest_mgr->put_pixel_rows) (&cinfo, dest_mgr, lines, (char *) data);
|
|
*length = cinfo.output_width * cinfo.output_components * lines;
|
|
|
|
return SANE_STATUS_GOOD;
|
|
|
|
}
|
|
}
|
|
|
|
void
|
|
sane_cancel (SANE_Handle UNUSEDARG handle)
|
|
{
|
|
DBG (127, "sane_cancel() called\n");
|
|
if (Camera.scanning)
|
|
Camera.scanning = SANE_FALSE; /* done with scan */
|
|
else
|
|
DBG (127, "sane_cancel() aborted, scanner not scanning\n");
|
|
}
|
|
|
|
SANE_Status
|
|
sane_set_io_mode (SANE_Handle UNUSEDARG handle,
|
|
SANE_Bool UNUSEDARG non_blocking)
|
|
{
|
|
return SANE_STATUS_UNSUPPORTED;
|
|
}
|
|
|
|
SANE_Status
|
|
sane_get_select_fd (SANE_Handle UNUSEDARG handle, SANE_Int * UNUSEDARG fd)
|
|
{
|
|
return SANE_STATUS_UNSUPPORTED;
|
|
}
|
|
|
|
static PictureInfo *
|
|
get_pictures_info (void)
|
|
{
|
|
|
|
char f[] = "get_pictures_info";
|
|
unsigned int p;
|
|
PictureInfo *pics;
|
|
|
|
if ((pics = (PictureInfo *) malloc (Camera.pic_taken *
|
|
sizeof (PictureInfo))) == NULL)
|
|
{
|
|
DBG (4, "%s: error: allocate memory for pictures array\n", f);
|
|
return NULL;
|
|
}
|
|
|
|
for (p = 0; p < (unsigned int) Camera.pic_taken; p++)
|
|
{
|
|
if (get_picture_info (pics + p, p) == -1)
|
|
{
|
|
free (pics);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
return pics;
|
|
}
|
|
|
|
static int
|
|
get_picture_info (PictureInfo * pic, int p)
|
|
{
|
|
|
|
char f[] = "get_picture_info";
|
|
static char buffer[256];
|
|
|
|
DBG (4, "%s: info for pic #%d\n", f, p);
|
|
|
|
pic_info_pck[3] = (unsigned char) p;
|
|
|
|
if (send_pck (Camera.fd, pic_info_pck) == -1)
|
|
{
|
|
DBG (4, "%s: error: send_pck returned -1\n", f);
|
|
return -1;
|
|
}
|
|
|
|
if (read_data (Camera.fd, (unsigned char *) buffer, 256) == -1)
|
|
{
|
|
DBG (2, "%s: error: read_data returned -1\n", f);
|
|
return -1;
|
|
}
|
|
|
|
if (end_of_data (Camera.fd) == -1)
|
|
{
|
|
DBG (2, "%s: error: end_of_data returned -1\n", f);
|
|
return -1;
|
|
}
|
|
|
|
if (buffer[3] == 0)
|
|
{
|
|
pic->low_res = SANE_TRUE;
|
|
}
|
|
else if (buffer[3] == 1)
|
|
{
|
|
pic->low_res = SANE_FALSE;
|
|
}
|
|
else
|
|
{
|
|
DBG (2, "%s: error: unknown resolution code %u\n", f, buffer[3]);
|
|
return -1;
|
|
}
|
|
pic->size = (buffer[8] & 0xFF) << 24;
|
|
pic->size |= (buffer[9] & 0xFF) << 16;
|
|
pic->size |= (buffer[10] & 0xFF) << 8;
|
|
pic->size |= (buffer[11] & 0xFF);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static SANE_Status
|
|
snap_pic (int fd)
|
|
{
|
|
|
|
char f[] = "snap_pic";
|
|
|
|
/* make sure camera is set to our settings state */
|
|
if (change_res (Camera.fd, dc210_opt_lowres) == -1)
|
|
{
|
|
DBG (1, "%s: Failed to set resolution\n", f);
|
|
return SANE_STATUS_INVAL;
|
|
}
|
|
|
|
/* take the picture */
|
|
if (send_pck (fd, shoot_pck) == -1)
|
|
{
|
|
DBG (4, "%s: error: send_pck returned -1\n", f);
|
|
return SANE_STATUS_INVAL;
|
|
}
|
|
else
|
|
{
|
|
if (end_of_data (Camera.fd) == -1)
|
|
{
|
|
DBG (2, "%s: error: end_of_data returned -1\n", f);
|
|
return SANE_STATUS_INVAL;
|
|
}
|
|
}
|
|
Camera.pic_taken++;
|
|
Camera.pic_left--;
|
|
Camera.current_picture_number = Camera.pic_taken;
|
|
image_range.max++;
|
|
sod[DC210_OPT_IMAGE_NUMBER].cap &= ~SANE_CAP_INACTIVE;
|
|
|
|
/* add this one to the Pictures array */
|
|
if ((Camera.Pictures =
|
|
(PictureInfo *) realloc (Camera.Pictures,
|
|
Camera.pic_taken * sizeof (PictureInfo))) ==
|
|
NULL)
|
|
{
|
|
DBG (4, "%s: error: allocate memory for pictures array\n", f);
|
|
return SANE_STATUS_INVAL;
|
|
}
|
|
|
|
if (get_picture_info (Camera.Pictures + Camera.pic_taken,
|
|
Camera.pic_taken) == -1)
|
|
{
|
|
DBG (1, "%s: Failed to get new picture info\n", f);
|
|
/* XXX - I guess we should try to erase the image here */
|
|
return SANE_STATUS_INVAL;
|
|
}
|
|
|
|
return SANE_STATUS_GOOD;
|
|
}
|