summaryrefslogtreecommitdiff
path: root/backend/dc210.c
diff options
context:
space:
mode:
Diffstat (limited to 'backend/dc210.c')
-rw-r--r--backend/dc210.c1531
1 files changed, 1531 insertions, 0 deletions
diff --git a/backend/dc210.c b/backend/dc210.c
new file mode 100644
index 0000000..acfe99a
--- /dev/null
+++ b/backend/dc210.c
@@ -0,0 +1,1531 @@
+/***************************************************************************
+ * 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 "../include/sane/config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <limits.h>
+#include "../include/sane/sanei_jpeg.h"
+#include <sys/ioctl.h>
+
+#include "../include/sane/sane.h"
+#include "../include/sane/sanei.h"
+#include "../include/sane/saneopts.h"
+
+#define BACKEND_NAME dc210
+#include "../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,
+ {(void *) & image_range}
+ }
+ ,
+
+#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 __sane_unused__ 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 (SANE_CURRENT_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 __sane_unused__ 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 __sane_unused__ 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 __sane_unused__ 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 __sane_unused__ 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 __sane_unused__ 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 __sane_unused__ handle,
+ SANE_Bool __sane_unused__ non_blocking)
+{
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+SANE_Status
+sane_get_select_fd (SANE_Handle __sane_unused__ handle, SANE_Int __sane_unused__ * 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;
+}