diff --git a/AUTHORS b/AUTHORS index 9ae58cff2..fdeac6fed 100644 --- a/AUTHORS +++ b/AUTHORS @@ -19,6 +19,7 @@ Backends: coolscan: Didier Carlier, Andreas Rick (*) dc25: Peter Fales (*) dc210: Brian J. Murrell + dc240: Peter Fales (*) dll: David Mosberger dmc: David F. Skoll epson: Karl Heinz Kremer(*) @@ -109,7 +110,7 @@ Michel Roelofs Milon Firikis Oliver Rauch Patrick Reynolds -Peter Fales +Peter Fales Peter Kirchgessner Petter Reinholdtsen Randolph Bentson diff --git a/PROJECTS b/PROJECTS index 3b002548c..2a564e4ec 100644 --- a/PROJECTS +++ b/PROJECTS @@ -1,4 +1,4 @@ -PROJECTS (2001-03-31) +PROJECTS (2001-04-07) Here is a list of projects that various people have expressed interest in. Before starting working on one of these projects, it may be a @@ -34,8 +34,8 @@ ftp://www.cs.ubishops.ca/pub/lhii/ HP USB backend for HP 4200 (wip) http://hp4200-backend.sourceforge.net/ -Kodak DC-240 backend (wip) -Peter Fales +Microtek ScanMaker 3600 USB scanner (wip) +http://sm3600.sourceforge.net Mustek BearPaw USB scanner (wip) Henning Meier-Geinitz diff --git a/README b/README index 25a5332ad..d5a082187 100644 --- a/README +++ b/README @@ -146,6 +146,7 @@ below: Connectix QuickCam sane-qcam(5) Kodak DC20/DC25 sane-dc25(5) Kodak dc210 sane-dc210(5) + Kodak dc240 sane-dc240(5) Polaroid sane-dmc(5) Miscellaneous backends: diff --git a/backend/Makefile.in b/backend/Makefile.in index a00811e61..e5d6f833d 100644 --- a/backend/Makefile.in +++ b/backend/Makefile.in @@ -53,9 +53,9 @@ COMPILE = $(CC) -c $(CFLAGS) $(DEFS) $(INCLUDES) $(CPPFLAGS) @SET_MAKE@ PRELOADABLE_BACKENDS = abaton agfafocus apple artec as6e avision bh canon \ - coolscan dc25 @DC210@ dmc epson hp m3096g microtek microtek2 mustek \ - mustek_pp nec @NET@ pie @PINT@ plustek pnm @QCAM@ ricoh s9036 sharp \ - snapscan sp15c st400 tamarack umax @V4L@ + coolscan dc25 @DC210@ @DC240@ dmc epson hp m3096g microtek microtek2 \ + mustek mustek_pp nec @NET@ pie @PINT@ plustek pnm @QCAM@ ricoh s9036 \ + sharp snapscan sp15c st400 tamarack umax @V4L@ ALL_BACKENDS = $(PRELOADABLE_BACKENDS) dll LIBS = $(addprefix libsane-,$(addsuffix .la,$(ALL_BACKENDS))) @@ -156,6 +156,7 @@ libsane.la: dll.lo dll-s.lo $(EXTRA) $(addsuffix .lo,$(DLL_PRELOAD)) $(LIBOBJS) EXTRA_hp = hp-accessor hp-device hp-handle hp-hpmem hp-option hp-scl EXTRA_dc210 = djpeg +EXTRA_dc240 = djpeg # When preloading dll, we need to add in all preloaded objects: libsane-dll.la: $(addsuffix .lo,$(DLL_PRELOAD)) @@ -193,6 +194,7 @@ libsane-coolscan.la: ../sanei/sanei_constrain_value.lo libsane-coolscan.la: ../sanei/sanei_scsi.lo libsane-dc25.la: ../sanei/sanei_constrain_value.lo libsane-dc210.la: ../sanei/sanei_constrain_value.lo djpeg.lo +libsane-dc240.la: ../sanei/sanei_constrain_value.lo djpeg.lo libsane-dmc.la: ../sanei/sanei_config2.lo libsane-dmc.la: ../sanei/sanei_constrain_value.lo libsane-dmc.la: ../sanei/sanei_scsi.lo diff --git a/backend/dc240.c b/backend/dc240.c new file mode 100644 index 000000000..d0c89d1c3 --- /dev/null +++ b/backend/dc240.c @@ -0,0 +1,1867 @@ + +/*************************************************************************** + * _S_A_N_E - Scanner Access Now Easy. + + dc240.c + + 03/12/01 - Peter Fales + + Based on the dc210 driver, (C) 1998 Brian J. Murrell (which is + based on dc25 driver (C) 1998 by Peter Fales) + + This file (C) 2001 by Peter Fales + + 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-240 + digital camera. THIS IS EXTREMELY ALPHA CODE! USE AT YOUR OWN RISK!! + + (feedback to: peter@fales.com + + This backend is based somewhat on the dc25 backend included in this + package by Peter Fales, and the dc210 backend by Brian J. Murrell + + ***************************************************************************/ + +#include "sane/config.h" + +#include +#include +#include +#include +#include +#include +#include "cdjpeg.h" +#include + +#include "sane/sane.h" +#include "sane/sanei.h" +#include "sane/saneopts.h" + +#define BACKEND_NAME dc240 +#include "sane/sanei_backend.h" + +#include "dc240.h" + +#ifndef PATH_MAX +# define PATH_MAX 1024 +#endif + +#define MAGIC (void *)0xab730324 +#define DC240_CONFIG_FILE "dc240.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 dc240_opt_thumbnails; +static SANE_Bool dc240_opt_snap; +static SANE_Bool dc240_opt_lowres; +static SANE_Bool dc240_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 DC240 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 DC240_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 DC240_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 DC240_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 DC240_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 DC240_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 DC240_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 DC240_OPT_INIT_DC240 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 SANE_Byte shoot_pck[] = SHOOT_PCK; +static SANE_Byte init_pck[] = INIT_PCK; +static SANE_Byte thumb_pck[] = THUMBS_PCK; +static SANE_Byte pic_pck[] = PICS_PCK; +static SANE_Byte pic_info_pck[] = PICS_INFO_PCK; +static SANE_Byte info_pck[] = INFO_PCK; +static SANE_Byte erase_pck[] = ERASE_PCK; +static SANE_Byte res_pck[] = RES_PCK; +static SANE_Byte open_card_pck[] = OPEN_CARD_PCK; +static SANE_Byte read_dir_pck[] = READ_DIR_PCK; + +static struct pkt_speed speeds[] = SPEEDS; +static struct termios tty_orig; + +static struct dir_buf dir_buf; + +static struct cam_dirlist *dir_head = NULL; + +static SANE_Byte info_buf[256]; +static SANE_Byte name_buf[60]; + +#include +#include + +static SANE_Int +send_pck (SANE_Int fd, SANE_Byte * pck) +{ + SANE_Int n; + SANE_Byte r = 0xf0; /* prime the loop with a "camera busy" */ + + DBG (127, "send_pck<%x %x %x %x %x %x %x %x>\n", + pck[0], pck[1], pck[2], pck[3], pck[4], pck[5], pck[6], pck[7]); + + /* keep trying if camera says it's busy */ + while (r == 0xf0) + { + if (write (fd, (char *) pck, 8) != 8) + { + DBG (1, "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 (1, "send_pck: error: read returned -1\n"); + return -1; + } + } + DBG (127, "send_pck: read one byte result from camera = %x\n", r); + return (r == 0xd1) ? 0 : -1; +} + +static SANE_Int +init_dc240 (DC240 * camera) +{ + struct termios tty_new; + SANE_Int speed_index; + SANE_Char buf[5], n; + + 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 (1, "unsupported baud rate.\n"); + return -1; + } + + /* + Open device file. + */ + if ((camera->fd = open (camera->tty_name, O_RDWR)) == -1) + { + DBG (1, "init_dc240: 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 (1, "init_dc240: 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 + tty_new.c_lflag &= ~(ICANON | ECHO | ISIG); +#endif + tty_new.c_oflag &= ~CSTOPB; + tty_new.c_lflag = 0; + 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 (1, "init_dc240: error: could not set attributes\n"); + return -1; + } + + /* send a break to get it back to a known state */ + +#ifdef HAVE_TCSENDBREAK + tcsendbreak (camera->fd, 4); +#else +# if defined(TCSBRKP) + ioctl (camera->fd, TCSBRKP, 4); +# elif defined(TCSBRK) + ioctl (camera->fd, TCSBRK, 4); +# endif +#endif + /* and wait for it to recover from the break */ + +#ifdef HAVE_USLEEP + usleep (breakpause); +#else + sleep (1); +#endif + + /* We seem to get some garbage following the break, so + * read anything pending */ + + n = read (camera->fd, buf, 5); + + DBG (127, "init_dc240 flushed %d bytes: %x %x %x %x %x\n", n, buf[0], + buf[1], buf[2], buf[3], buf[4]); + + 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: + */ + + tcsetattr (camera->fd, TCSANOW, &tty_orig); + DBG (1, "init_dc240: error: no response from camera\n"); + return -1; + } + + n = read (camera->fd, buf, 5); + DBG (127, "init_dc240 flushed %d bytes: %x %x %x %x %x\n", n, buf[0], + buf[1], buf[2], buf[3], buf[4]); + + /* + Set speed to requested speed. + */ + cfsetospeed (&tty_new, Camera.baud); + cfsetispeed (&tty_new, Camera.baud); + + if (tcsetattr (camera->fd, TCSANOW, &tty_new) == -1) + { + DBG (1, "init_dc240: error: could not set attributes\n"); + return -1; + } + + + if (send_pck (camera->fd, open_card_pck) == -1) + { + DBG (1, "init_dc240: error: send_pck returned -1\n"); + return -1; + } + + if (end_of_data (camera->fd) == -1) + { + DBG (1, "init_dc240: error: end_of_data returned -1\n"); + return -1; + } + + return camera->fd; + +} + +static void +close_dc240 (SANE_Int fd) +{ + /* + * Put the camera back to 9600 baud + */ + + if (close (fd) == -1) + { + DBG (1, "close_dc240: error: could not close device\n"); + } +} + +int +get_info (DC240 * camera) +{ + + SANE_Char f[] = "get_info"; + SANE_Byte buf[256]; + + if (send_pck (camera->fd, info_pck) == -1) + { + DBG (1, "%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 (1, "%s: error: read_data returned -1\n", f); + return -1; + } + + if (end_of_data (camera->fd) == -1) + { + DBG (1, "%s: error: end_of_data returned -1\n", f); + return -1; + } + + camera->model = buf[1]; + + if (camera->model != 0x5) + { + DBG (0, + "Camera model (%d) is not DC-240 (5). " + "Only the DC-240 is supported by this driver.\n"); + } + + camera->ver_major = buf[2]; + camera->ver_minor = buf[3]; + camera->pic_taken = buf[14] << 8 | buf[15]; + DBG (4, "pic_taken=%d\n", camera->pic_taken); + camera->pic_left = buf[64] << 8 | buf[65]; + DBG (4, "pictures left (at current res)=%d\n", camera->pic_left); + camera->flags.low_batt = buf[8]; + DBG (4, "battery=%d (0=OK, 1=weak, 2=empty)\n", camera->flags.low_batt); + DBG (4, "AC adapter status=%d\n", buf[9]); + dc240_opt_lowres = !buf[79]; + + if (Camera.pic_taken == 0) + { + sod[DC240_OPT_IMAGE_NUMBER].cap |= SANE_CAP_INACTIVE; + image_range.min = 0; + image_range.max = 0; + } + else + { + sod[DC240_OPT_IMAGE_NUMBER].cap &= ~SANE_CAP_INACTIVE; + image_range.min = 1; + image_range.max = Camera.pic_taken; + } + + read_dir ("\\PCCARD\\DCIM\\*.*"); + + return 0; + +} + +/* NEW */ +static SANE_Int +read_data (SANE_Int fd, SANE_Byte * buf, SANE_Int sz) +{ + SANE_Byte ccsum; + SANE_Byte rcsum; + SANE_Byte c; + SANE_Int retries = 0; + SANE_Int n; + SANE_Int r = 0; + SANE_Int i; + + while (retries++ < 5) + { + + /* + * If this is not the first time through, then it must be + * a retry - signal the camera that we didn't like what + * we got. In either case, start filling the packet + */ + if (retries != 1) + { + + DBG (3, "Attempt retry %d\n", retries); + c = 0xe3; + if (write (fd, (char *) &c, 1) != 1) + { + DBG (1, "read_data: error: write ack\n"); + return -1; + } + + } + + /* read the control byte */ + if (read (fd, &c, 1) != 1) + { + DBG (1, + "read_data: error: " + "read for packet control byte returned bad stat!us\n"); + return -1; + } + if (c != 1 && c != 0) + { + DBG (1, "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 (1, "read_data: error: read returned -1\n"); + continue; + } + + if (n < sz || read (fd, &rcsum, 1) != 1) + { + DBG (1, "read_data: error: buffer underrun or no checksum\n"); + continue; + } + + for (i = 0, ccsum = 0; i < n; i++) + ccsum ^= buf[i]; + + if (ccsum != rcsum) + { + DBG (2, + "read_data: warning: " + "bad checksum (got %02x != expected %02x) will re-read block\n", + rcsum, ccsum); + continue; + } + + /* If we got this far, then the packet is OK */ + break; + + + } + + c = 0xd2; + + if (write (fd, (char *) &c, 1) != 1) + { + DBG (1, "read_data: error: write ack\n"); + return -1; + } + + return 0; +} + +static SANE_Int +end_of_data (SANE_Int fd) +{ + SANE_Int n; + SANE_Byte c; + + do + { /* loop until the camera isn't busy */ + if ((n = read (fd, &c, 1)) == -1) + { + DBG (1, "end_of_data: error: read returned -1\n"); + return -1; + } + if (n == 1 && c == 0) /* got successful end of data */ + return 0; /* return success */ + if (n == 1) + { + DBG (127, "end_of_data: got %x while waiting\n", c); + } + else + { + DBG (127, "end_of_data: waiting...\n", c); + } + sleep (1); /* not too fast */ + } +/* It's not documented, but we see a d1 after snapping a picture */ + while (c == 0xf0 || c == 0xd1); + + /* Accck! Not busy, but not a good end of data either */ + if (c != 0) + { + DBG (1, "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 SANE_Int +erase (SANE_Int fd) +{ + if (send_pck (fd, erase_pck) == -1) + { + DBG (1, "erase: error: send_pck returned -1\n"); + return -1; + } + + if (send_data (name_buf) == -1) + { + DBG (1, "erase: error: send_data returned -1\n"); + return SANE_STATUS_INVAL; + } + + if (end_of_data (fd) == -1) + { + DBG (1, "erase: error: end_of_data returned -1\n"); + return -1; + } + + return 0; +} + +static SANE_Int +change_res (SANE_Int fd, SANE_Byte res) +{ + SANE_Char f[] = "change_res"; + + DBG (127, "%s called, low_res=%d\n", f, res); + + if (res != 0 && res != 1) + { + DBG (1, "%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 (1, "%s: error: send_pck returned -1\n", f); + } + + if (end_of_data (fd) == -1) + { + DBG (1, "%s: error: end_of_data returned -1\n", f); + } + return 0; +} + +SANE_Status +sane_init (SANE_Int * version_code, SANE_Auth_Callback UNUSEDARG authorize) +{ + + SANE_Char f[] = "sane_init"; + SANE_Char dev_name[PATH_MAX], *p; + size_t len; + FILE *fp; + SANE_Int baud; + + DBG_INIT (); + + if (version_code) + *version_code = SANE_VERSION_CODE (V_MAJOR, V_MINOR, 0); + + fp = sanei_config_open (DC240_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, DC240_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_dc240 (&Camera) == -1) + return SANE_STATUS_INVAL; + + if (get_info (&Camera) == -1) + { + DBG (1, "error: could not get info\n"); + close_dc240 (Camera.fd); + return SANE_STATUS_INVAL; + } + + /* load the current images array */ + 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; + set_res (Camera.Pictures[Camera.current_picture_number - 1].low_res); + } + + if (dumpinquiry) + { + DBG (0, "\nCamera information:\n~~~~~~~~~~~~~~~~~\n\n"); + DBG (0, "Model...........: DC%s\n", "240"); + 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, "Battery state...: %s\n", + Camera.flags.low_batt == 0 ? "good" : (Camera.flags.low_batt == + 1 ? "weak" : "empty")); + } + + return SANE_STATUS_GOOD; +} + +void +sane_exit (void) +{ +} + +/* Device select/open/close */ + +static const SANE_Device dev[] = { + { + "0", + "Kodak", + "DC-240", + "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) +{ + SANE_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 (4, "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]; +} + +static SANE_Int myinfo = 0; + +SANE_Status +sane_control_option (SANE_Handle handle, SANE_Int option, + SANE_Action action, void *value, SANE_Int * info) +{ + 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, 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 (2, "Constraint error in control_option\n"); + return status; + } + + switch (option) + { + case DC240_OPT_IMAGE_NUMBER: + Camera.current_picture_number = *(SANE_Word *) value; + myinfo |= SANE_INFO_RELOAD_PARAMS; + + /* get the image's resolution */ + set_res (Camera. + Pictures[Camera.current_picture_number - 1].low_res); + + break; + + case DC240_OPT_THUMBS: + dc240_opt_thumbnails = !!*(SANE_Word *) value; + myinfo |= SANE_INFO_RELOAD_PARAMS; + + set_res (Camera. + Pictures[Camera.current_picture_number - 1].low_res); + + break; + + case DC240_OPT_SNAP: + dc240_opt_snap = !!*(SANE_Word *) value; + myinfo |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; + /* if we are snapping a new one */ + if (dc240_opt_snap) + { + /* activate the resolution setting */ + sod[DC240_OPT_LOWRES].cap &= ~SANE_CAP_INACTIVE; + /* and de-activate the image number selector */ + sod[DC240_OPT_IMAGE_NUMBER].cap |= SANE_CAP_INACTIVE; + } + else + { + /* deactivate the resolution setting */ + sod[DC240_OPT_LOWRES].cap |= SANE_CAP_INACTIVE; + /* and activate the image number selector */ + sod[DC240_OPT_IMAGE_NUMBER].cap &= ~SANE_CAP_INACTIVE; + } + /* set params according to resolution settings */ + set_res (dc240_opt_lowres); + + break; + + case DC240_OPT_LOWRES: + dc240_opt_lowres = !!*(SANE_Word *) value; + myinfo |= SANE_INFO_RELOAD_PARAMS; + +/* XXX - change the number of pictures left depending on resolution + perhaps just call get_info again? + */ + set_res (dc240_opt_lowres); + + break; + + case DC240_OPT_ERASE: + dc240_opt_erase = !!*(SANE_Word *) value; + break; + + case DC240_OPT_DEFAULT: + dc240_opt_thumbnails = 0; + dc240_opt_snap = 0; + + /* deactivate the resolution setting */ + sod[DC240_OPT_LOWRES].cap |= SANE_CAP_INACTIVE; + /* and activate the image number selector */ + sod[DC240_OPT_IMAGE_NUMBER].cap &= ~SANE_CAP_INACTIVE; + + myinfo |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; + + DBG (1, "Fixme: Set all defaults here!\n"); + break; + + case DC240_OPT_INIT_DC240: + if ((Camera.fd = init_dc240 (&Camera)) == -1) + { + return SANE_STATUS_INVAL; + } + if (get_info (&Camera) == -1) + { + DBG (1, "error: could not get info\n"); + close_dc240 (Camera.fd); + return SANE_STATUS_INVAL; + } + + /* load the current images array */ + get_pictures_info (); + + myinfo |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS; + break; + + default: + return SANE_STATUS_INVAL; + } + break; + + case SANE_ACTION_GET_VALUE: + switch (option) + { + case 0: + *(SANE_Word *) value = NELEMS (sod); + break; + + case DC240_OPT_IMAGE_NUMBER: + *(SANE_Word *) value = Camera.current_picture_number; + break; + + case DC240_OPT_THUMBS: + *(SANE_Word *) value = dc240_opt_thumbnails; + break; + + case DC240_OPT_SNAP: + *(SANE_Word *) value = dc240_opt_snap; + break; + + case DC240_OPT_LOWRES: + *(SANE_Word *) value = dc240_opt_lowres; + break; + + case DC240_OPT_ERASE: + *(SANE_Word *) value = dc240_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; + myinfo = 0; + } + return SANE_STATUS_GOOD; +} + +SANE_Status +sane_get_parameters (SANE_Handle handle, SANE_Parameters * params) +{ + SANE_Int rc = SANE_STATUS_GOOD; + + DBG (127, "sane_get_params called, wid=%d,height=%d\n", + parms.pixels_per_line, parms.lines); + + 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) +jpeg_init_source (j_decompress_ptr UNUSEDARG cinfo) +{ + /* nothing to do */ +} + +METHODDEF (boolean) jpeg_fill_input_buffer (j_decompress_ptr cinfo) +{ + + my_src_ptr src = (my_src_ptr) cinfo->src; + + if (read_data (Camera.fd, src->buffer, 512) == -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 = 512; + + return TRUE; +} + +METHODDEF (void) 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) 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) +jpeg_term_source (j_decompress_ptr UNUSEDARG cinfo) +{ + /* no work necessary here */ +} + +SANE_Status +sane_start (SANE_Handle handle) +{ + SANE_Int i; + + DBG (127, "sane_start called\n"); + if (handle != MAGIC || !is_open || + (Camera.current_picture_number == 0 && dc240_opt_snap == SANE_FALSE)) + return SANE_STATUS_INVAL; /* Unknown handle ... */ + + if (Camera.scanning) + return SANE_STATUS_EOF; + + if (dc240_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_INVAL) + { + DBG (1, "Failed to snap new picture\n"); + return SANE_STATUS_INVAL; + } + } + + if (dc240_opt_thumbnails) + { + + if (send_pck (Camera.fd, thumb_pck) == -1) + { + DBG (1, "sane_start: error: send_pck returned -1\n"); + return SANE_STATUS_INVAL; + } + + /* Picture size should have been set when thumbnails were + * selected. But, check just in case + */ + + if (parms.pixels_per_line != 160 || + parms.bytes_per_line != 160 * 3 || parms.lines != 120) + { + DBG (1, "sane_start: fixme! thumbnail image size is wrong\n"); + return SANE_STATUS_INVAL; + } + } + else + { + if (send_pck (Camera.fd, pic_pck) == -1) + { + DBG (1, "sane_start: error: send_pck returned -1\n"); + return SANE_STATUS_INVAL; + } + + } + { + my_src_ptr src; + + struct jpeg_error_mgr jerr; + SANE_Int row_stride, n; + SANE_Char f[] = "sane_start"; + struct cam_dirlist *e; + name_buf[0] = 0x80; + + for (n = 1, e = dir_head; e; n++, e = e->next) + { + if (n == Camera.current_picture_number) + break; + } + DBG (9, "%s: pic to read is %d name is %s\n", f, n, e->name); + + strcpy (&name_buf[1], e->name); + for (i = 49; i <= 56; i++) + { + name_buf[i] = 0xff; + } + + if (send_data (name_buf) == -1) + { + DBG (1, "%s: error: send_data returned -1\n", f); + 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 = jpeg_init_source; + src->pub.fill_input_buffer = jpeg_fill_input_buffer; + src->pub.skip_input_data = jpeg_skip_input_data; + src->pub.resync_to_restart = jpeg_resync_to_restart; /* default */ + src->pub.term_source = 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 */ + + return SANE_STATUS_GOOD; +} + +static SANE_Byte linebuffer[HIGHRES_WIDTH * 3]; +static SANE_Int linebuffer_size = 0; +static SANE_Int linebuffer_index = 0; + +SANE_Status +sane_read (SANE_Handle UNUSEDARG handle, SANE_Byte * data, + SANE_Int max_length, SANE_Int * length) +{ + SANE_Int lines = 0; + + /* If there is anything in the buffer, satisfy the read from there */ + if (linebuffer_size && linebuffer_index < linebuffer_size) + { + *length = linebuffer_size - linebuffer_index; + + if (*length > max_length) + { + *length = max_length; + } + memcpy (data, linebuffer + linebuffer_index, *length); + linebuffer_index += *length; + + return SANE_STATUS_GOOD; + } + + if (cinfo.output_scanline >= cinfo.output_height) + { + /* clean up comms with the camera */ + if (end_of_data (Camera.fd) == -1) + { + DBG (1, "sane_read: error: end_of_data returned -1\n"); + return SANE_STATUS_INVAL; + } + if (dc240_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--; + + myinfo |= SANE_INFO_RELOAD_OPTIONS; + dir_delete ((SANE_String) & name_buf[1]); + + } + 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 *) linebuffer); + + *length = cinfo.output_width * cinfo.output_components * lines; + linebuffer_size = *length; + linebuffer_index = 0; + + if (*length > max_length) + { + *length = max_length; + } + memcpy (data, linebuffer + linebuffer_index, *length); + linebuffer_index += *length; + + return SANE_STATUS_GOOD; +} + +void +sane_cancel (SANE_Handle UNUSEDARG handle) +{ + if (Camera.scanning) + Camera.scanning = SANE_FALSE; /* done with scan */ + else + DBG (4, "sane_cancel: not scanning - nothing to do\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; +} + +/* + * get_pictures_info - load information about all pictures currently in + * camera: Mainly the mapping of picture number + * to picture name, and the resolution of each picture. + */ +static PictureInfo * +get_pictures_info (void) +{ + + SANE_Char f[] = "get_pictures_info"; + SANE_Int p; + PictureInfo *pics; + + if (Camera.Pictures) + { + free (Camera.Pictures); + Camera.Pictures = NULL; + } + + /* PSF fixme: Eventually need to read dir names - for now + * just assume 100DC240 + */ + read_dir ("\\PCCARD\\DCIM\\100DC240\\*.*"); + + if ((pics = (PictureInfo *) malloc (Camera.pic_taken * + sizeof (PictureInfo))) == NULL) + { + DBG (1, "%s: error: allocate memory for pictures array\n", f); + return NULL; + } + + for (p = 0; p < Camera.pic_taken; p++) + { + if (get_picture_info (pics + p, p) == -1) + { + free (pics); + return NULL; + } + } + + Camera.Pictures = pics; + return pics; +} + +static SANE_Int +get_picture_info (PictureInfo * pic, SANE_Int p) +{ + + SANE_Char f[] = "get_picture_info"; + SANE_Int n; + struct cam_dirlist *e; + + DBG (4, "%s: info for pic #%d\n", f, p); + + for (n = 0, e = dir_head; e && n < p; n++, e = e->next) + ; + + read_info (e->name); + + /* Validate picture info */ + if (info_buf[0] != 1 || info_buf[1] != 5 || info_buf[2] != 3 + || info_buf[6] != 0) + { + + DBG (1, "%s: error: Image %s does not come from a DC-240.\n", f, + e->name); + return -1; + } + + pic->low_res = info_buf[3] == 0 ? SANE_TRUE : SANE_FALSE; + + return 0; +} + +/* + * snap_pic - take a picture (and call get_pictures_info to re-create + * the directory related data structures) + */ +static SANE_Status +snap_pic (SANE_Int fd) +{ + + SANE_Char f[] = "snap_pic"; + +#if 0 +/* Just checking... It looks like after snapping the picture, we + * get two "d1" ACK responses back, even though the documentation seems + * to say that there should only be one. I thought the first one could + * have been an unread response from an earlier command, so I + * I tried flushing any data here. Still seeing the multiple + * d1 bytes, however... + */ + { + SANE_Int n; + SANE_Char flush[10]; + n = read (Camera.fd, flush, 10); + if (n > 0) + { + DBG (127, "%s: flushed %d bytes\n", f, n); + } + else + { + DBG (127, "%s: nothing to flush\n", f); + } + } +#endif + + /* make sure camera is set to our settings state */ + if (change_res (Camera.fd, dc240_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 (1, "%s: error: send_pck returned -1\n", f); + return SANE_STATUS_INVAL; + } + else + { + if (end_of_data (Camera.fd) == -1) + { + DBG (1, "%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[DC240_OPT_IMAGE_NUMBER].cap &= ~SANE_CAP_INACTIVE; + + /* add this one to the Pictures array */ + + if (get_pictures_info () == NULL) + { + 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; +} + +/* + * read_dir - read a list of file names from the specified directory + * and create a linked list of file name entries in + * alphabetical order. The first entry in the list will + * be "picture #1", etc. + */ +static SANE_Int +read_dir (SANE_String dir) +{ + SANE_Byte buf[256]; + SANE_Byte *next_buf; + SANE_Int i, entries; + SANE_Byte r = 0xf0; /* prime the loop with a "camera busy" */ + SANE_Char f[] = "read_dir"; + struct cam_dirlist *e, *next; + + /* Free up current list */ + for (e = dir_head; e; e = next) + { + DBG (127, "%s: free entry %s\n", f, e->name); + next = e->next; + free (e); + } + dir_head = NULL; + + if (send_pck (Camera.fd, read_dir_pck) == -1) + { + DBG (1, "%s: error: send_pck returned -1\n", f); + return SANE_STATUS_INVAL; + } + + buf[0] = 0x80; + strcpy (&buf[1], dir); + for (i = 49; i <= 56; i++) + { + buf[i] = 0xff; + } + + if (send_data (buf) == -1) + { + DBG (1, "%s: error: send_data returned -1\n", f); + return SANE_STATUS_INVAL; + } + + if (read_data (Camera.fd, (SANE_Byte *) & dir_buf, 256) == -1) + { + DBG (1, "%s: error: read_data returned -1\n", f); + return SANE_STATUS_INVAL; + } + + entries = (dir_buf.entries_msb << 8) + dir_buf.entries_lsb; + DBG (127, "%s: result of dir read is %x, number of entries=%d\n", f, r, + entries); + + if (entries > 1001) + { + DBG (1, "%s: error: more than 999 pictures not supported yet\n", f); + return SANE_STATUS_INVAL; + } + + /* Determine if it's time to read another 256 byte buffer from the camera */ + next_buf = ((SANE_Byte *) & dir_buf) + 256; + while ((SANE_Byte *) (&dir_buf.entry[entries]) >= (SANE_Byte *) next_buf) + { + + DBG (127, "%s: reading additional directory buffer\n", f); + if (read_data (Camera.fd, next_buf, 256) == -1) + { + DBG (1, "%s: error: read_data returned -1\n", f); + return SANE_STATUS_INVAL; + } + next_buf += 256; + } + + for (i = 0; i < entries; i++) + + { + /* Hack: I don't know what attr is used for, so setting it + * to zero is a convenient way to put in the null terminator + */ + dir_buf.entry[i].attr = 0; + DBG (127, "%s: entry=%s\n", f, dir_buf.entry[i].name); + + if (dir_buf.entry[i].name[0] == '.') + { + continue; + } + + if (dir_insert (&dir_buf.entry[i]) != 0) + { + DBG (1, "%s: error: failed to insert dir entry\n"); + return -1; + } + } + + if (end_of_data (Camera.fd) == -1) + { + DBG (1, "%s: error: end_of_data returned -1\n", f); + return -1; + } + + return 0; +} + +/* + * read_info - read the info block from camera for the specified file + */ +static SANE_Int +read_info (SANE_String fname) +{ + SANE_Byte buf[256]; + SANE_Int i; + SANE_Char f[] = "read_info"; + + if (send_pck (Camera.fd, pic_info_pck) == -1) + { + DBG (1, "%s: error: send_pck returned -1\n", f); + return SANE_STATUS_INVAL; + } + + buf[0] = 0x80; + strcpy (&buf[1], fname); + for (i = 49; i <= 56; i++) + { + buf[i] = 0xff; + } + + if (send_data (buf) == -1) + { + DBG (1, "%s: error: send_data returned -1\n", f); + return SANE_STATUS_INVAL; + } + + if (read_data (Camera.fd, info_buf, 256) != 0) + { + DBG (1, "%s: error: Failed in read_data\n", f); + return -1; + } + + DBG (9, "%s: data type=%d, cam type=%d, file type=%d\n", f, + info_buf[0], info_buf[1], info_buf[2]); + + if (end_of_data (Camera.fd) == -1) + { + DBG (1, "%s: error: end_of_data returned -1\n", f); + return -1; + } + + return 0; +} + + +/* + * send_data - Send a data block - assumes all data blocks to camera + * are 60 bytes long + */ + +static SANE_Int +send_data (SANE_Byte * buf) +{ + SANE_Byte r = 0xf0; /* prime the loop with a "camera busy" */ + SANE_Int i, n; + SANE_Byte csum; + SANE_Char f[] = "send_data"; + + for (i = 1, csum = 0; i < 59; i++) + { + csum ^= buf[i]; + } + buf[59] = csum; + DBG (127, "%s: about to send data block\n", f); + + /* keep trying if camera says it's busy */ + while (r == 0xf0) + { + if (write (Camera.fd, (char *) buf, 60) != 60) + { + DBG (1, "%s: error: write returned -1\n", f); + return -1; + } + + /* need to wait before we read command result */ +#ifdef HAVE_USLEEP + usleep (cmdrespause); +#else + sleep (1); +#endif + + if ((n = read (Camera.fd, (char *) &r, 1)) != 1) + { + DBG (1, "%s: error: read returned -1\n", f); + return -1; + } + } + + if (r != 0xd2) + { + DBG (1, "%s: error: bad response to send_data (%d)\n", f, r); + return -1; + } + + return 0; +} + +/* + * dir_insert - Add (in alphabetical order) a directory entry to the + * current list of entries. + */ +static SANE_Int +dir_insert (struct cam_dirent *entry) +{ + struct cam_dirlist *cur, *e; + + cur = (struct cam_dirlist *) malloc (sizeof (struct cam_dirlist)); + if (cur == NULL) + { + DBG (1, "dir_insert: error: could not malloc entry\n"); + return -1; + } + + strcpy (cur->name, "\\PCCARD\\DCIM\\100DC240\\"); + strcat (cur->name, entry->name); + cur->name[strlen (cur->name) - 3] = '\0'; + strcat (cur->name, ".JPG"); + DBG (127, "dir_insert: name is %s\n", cur->name); + + cur->next = NULL; + + if (dir_head == NULL) + { + dir_head = cur; + } + else if (strcmp (cur->name, dir_head->name) < 0) + { + cur->next = dir_head; + dir_head = cur; + return 0; + } + else + { + for (e = dir_head; e->next; e = e->next) + { + if (strcmp (e->next->name, cur->name) > 0) + { + cur->next = e->next; + e->next = cur; + return 0; + } + } + e->next = cur; + } + return 0; +} + +/* + * dir_delete - Delete a directory entry from the linked list of file + * names + */ +static SANE_Int +dir_delete (SANE_String fname) +{ + struct cam_dirlist *cur, *e; + + DBG (127, "dir_delete: %s\n", fname); + + if (strcmp (fname, dir_head->name) == 0) + { + cur = dir_head; + dir_head = dir_head->next; + free (cur); + return 0; + } + + for (e = dir_head; e->next; e = e->next) + { + + if (strcmp (fname, e->next->name) == 0) + { + cur = e->next; + e->next = e->next->next; + free (cur); + return (0); + } + } + DBG (1, "dir_delete: Couldn't find entry %s in dir list\n", fname); + return -1; +} + +/* + * set_res - set picture size depending on resolution settings + */ +static void +set_res (SANE_Int lowres) +{ + if (dc240_opt_thumbnails) + { + parms.bytes_per_line = 160 * 3; + parms.pixels_per_line = 160; + parms.lines = 120; + } + else if (lowres) + { + parms.bytes_per_line = LOWRES_WIDTH * 3; + parms.pixels_per_line = LOWRES_WIDTH; + parms.lines = LOWRES_HEIGHT; + } + else + { + parms.bytes_per_line = HIGHRES_WIDTH * 3; + parms.pixels_per_line = HIGHRES_WIDTH; + parms.lines = HIGHRES_HEIGHT; + } +} diff --git a/backend/dc240.conf b/backend/dc240.conf new file mode 100644 index 000000000..0d3b48a54 --- /dev/null +++ b/backend/dc240.conf @@ -0,0 +1,27 @@ +# Serial port where the camera is connected +## Linux +port=/dev/ttyS0 +## IRIX +#port=/dev/ttyd1 +## Solaris +#port=/dev/term/a +## HP-UX +#port=/dev/tty0p0 +## Digital UNIX +#port=/dev/tty01 +# Max baud rate for download. Camera always starts at 9600 baud, then +# switches to the higher rate +## This works for Linux and some versions of IRIX (6.3 or higher) +baud=115200 +## This works for most UNIX's +#baud=38400 +# Prints some extra information during the init phase. This can be +# handy, but note that printing anything to stderr breaks the saned +# network scanning. +#dumpinquiry +# How many usec (1,000,000ths of a) between writing the command and reading the +# result. 125000 seems to be the lowest I could go reliably. +cmdrespause=125000 +# How many usec (1,000,000ths of a) between sending the "back to default" break +# sending commands. +breakpause=1000000; diff --git a/backend/dc240.desc b/backend/dc240.desc new file mode 100644 index 000000000..e7d569178 --- /dev/null +++ b/backend/dc240.desc @@ -0,0 +1,14 @@ +;Kodak DC240 backend .desc file by Peter S. Fales + +:backend "dc240" ; name of backend +:version "0.0" ; version of backend +:status :alpha ; :alpha, :beta, :stable, :new +:manpage "sane-dc240" ; name of manpage (if it exists) +;:url none ; backend's web page + +:devicetype :stillcam ; start of a list of devices.... + +:mfg "Kodak" +:url "http://www.kodak.com/US/en/digital/cameras/DCSGateway.shtml" +:model "DC240" +:interface "Serial port" diff --git a/backend/dll.conf b/backend/dll.conf index 33af5cd3f..e566fb9db 100644 --- a/backend/dll.conf +++ b/backend/dll.conf @@ -11,6 +11,7 @@ canon coolscan #dc25 #dc210 +#dc240 dmc epson hp diff --git a/doc/Makefile.in b/doc/Makefile.in index 1393e924e..7044e04bf 100644 --- a/doc/Makefile.in +++ b/doc/Makefile.in @@ -38,7 +38,7 @@ SECT5 = sane-abaton.5 sane-agfafocus.5 sane-apple.5 sane-as6e.5 sane-dll.5 \ sane-artec.5 sane-fujitsu.5 sane-sharp.5 sane-s9036.5 \ sane-tamarack.5 sane-ricoh.5 sane-avision.5 sane-plustek.5 \ sane-st400.5 sane-mustek_pp.5 sane-dc210.5 sane-v4l.5 \ - sane-snapscan.5 sane-canon.5 sane-coolscan.5 sane-bh.5 + sane-snapscan.5 sane-canon.5 sane-coolscan.5 sane-bh.5 sane-dc240.5 MANPAGES = $(SECT1) $(SECT5) DOCS = sane.ps sane.dvi LATEX = TEXINPUTS=$(srcdir):$$TEXINPUTS latex diff --git a/doc/sane-dc240.man b/doc/sane-dc240.man new file mode 100644 index 000000000..061c3acff --- /dev/null +++ b/doc/sane-dc240.man @@ -0,0 +1,111 @@ +.TH sane-dc240 5 "23 March 2001" +.IX sane-dc240 +.SH NAME +sane-dc240 \- SANE backend for Kodak DC240 Digital Camera +.SH DESCRIPTION +The +.B sane-dc240 +library implements a SANE (Scanner Access Now Easy) backend that +provides access to the Kodak DC240 camera. THIS IS EXTREMELY ALPHA +CODE! USE AT YOUR OWN RISK!! +.SH "DEVICE NAMES" +The current version of the backend only allows one camera to be +connected. The device name is always "0". +.SH CONFIGURATION +The contents of the +.I dc240.conf +specify the serial port and baud rate to use. The baud rate +specifies the maximum rate to use while downloading pictures. (The +camera is always initialized using 9600 baud, then switches to the +higher rate). On a 450MHz Pentium, I usually have no problems downloading +at 115200 baud, though the camera sometimes has to resend packets due +to lost characters. Results are better when +the "interrupt-unmask flag" is set in the IDE driver (hdparm -u1). +Supported baud rates are: 9600, 19200, 38400, 57600, and 115200. +.PP +The dumpinquiry line causes some information about the camera to +be printed. +.PP +cmdrespause specifies how many usec (1,000,000ths of a) between +writing the command and reading the result should be used. 125000 +seems to be the lowest I could go reliably. +.PP +breakpause is the time in 1,000,000ths of a usec between sending the +"back to default" break sending commands. +.PP +Empty lines and lines starting with a hash mark (#) are +ignored. A sample configuration file is shown below: +.PP +.RS +port=/dev/ttyS0 +.br +# this is a comment +.br +baud=115200 +.br +dumpinquiry +.br +cmdrespause=125000 +.br +breakpause=1000000 +.RE +.PP +.SH FILES +.TP +.I @CONFIGDIR@/dc240.conf +The backend configuration file (see also description of +.B SANE_CONFIG_DIR +below). +.TP +.I @LIBDIR@/libsane-dc240.a +The static library implementing this backend. +.TP +.I @LIBDIR@/libsane-dc240.so +The shared library implementing this backend (present on systems that +support dynamic loading). + +.SH ENVIRONMENT +.TP +.B SANE_CONFIG_DIR +This environment variable specifies the list of directories that may +contain the configuration file. Under UNIX, the directories are +separated by a colon (`:'), under OS/2, they are separated by a +semi-colon (`;'). If this variable is not set, the configuration file +is searched in two default directories: first, the current working +directory (".") and then in /usr/local/etc/sane.d. If the value of the +environment variable ends with the directory separator character, then +the default directories are searched after the explicitly specified +directories. For example, setting +.B SANE_CONFIG_DIR +to "/tmp/config:" would result in directories "tmp/config", ".", and +"/usr/local/etc/sane.d" being searched (in this order). +.TP +.B SANE_DEBUG_DC240 +If the library was compiled with debugging support enabled, this +environment variable controls the debug level for this backend. +A value of 128 requests maximally copious debug output; smaller +levels reduce verbosity. + +.SH "SEE ALSO" + +.SH AUTHOR +Peter S. Fales + +.PP +This backend borrows heavily from the dc210 backend by Brian J. Murrell +which is based somewhat on the dc25 backend by Peter Fales. +.PP +The manpage was largeley copied from the dc210 manpage. + +.SH BUGS +The major limitation that I know of is that the backend assumes +the directory in the camera is 100dc240. Once the camera has +taken more than 9999 pictures, the directory will increment to 101dc240. +Not only should we check for the additional directory, but pictures may +actually be found in multiple directories. +.PP +More general comments, suggestions, and inquiries about frontends +or SANE should go to +.BR sane\-devel@mostang.com , +the SANE Developers mailing list. +