summaryrefslogtreecommitdiff
path: root/backend/hpljm1005.c
diff options
context:
space:
mode:
Diffstat (limited to 'backend/hpljm1005.c')
-rw-r--r--backend/hpljm1005.c1104
1 files changed, 1104 insertions, 0 deletions
diff --git a/backend/hpljm1005.c b/backend/hpljm1005.c
new file mode 100644
index 0000000..9460c48
--- /dev/null
+++ b/backend/hpljm1005.c
@@ -0,0 +1,1104 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2007-2008 Philippe Rétornaz
+
+ 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 backend is for HP LaserJet M1005 MFP
+
+ Highly inspired from the epson backend
+*/
+
+#define BUILD 1
+
+#include "../include/sane/config.h"
+#include <math.h>
+
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdint.h>
+#include <netinet/in.h>
+#define BACKEND_NAME hpljm1005
+#include "../include/sane/sanei_backend.h"
+#include "../include/sane/sanei_usb.h"
+#include "../include/sane/saneopts.h"
+
+#define MAGIC_NUMBER 0x41535001
+#define PKT_READ_STATUS 0x0
+#define PKT_UNKNOW_1 0x1
+#define PKT_START_SCAN 0x2
+#define PKT_GO_IDLE 0x3
+#define PKT_DATA 0x5
+#define PKT_READCONF 0x6
+#define PKT_SETCONF 0x7
+#define PKT_END_DATA 0xe
+#define PKT_RESET 0x15
+
+#define RED_LAYER 0x3
+#define GREEN_LAYER 0x4
+#define BLUE_LAYER 0x5
+#define GRAY_LAYER 0x6
+
+#define MIN_SCAN_ZONE 101
+
+struct usbdev_s
+{
+ SANE_Int vendor_id;
+ SANE_Int product_id;
+ SANE_String_Const vendor_s;
+ SANE_String_Const model_s;
+ SANE_String_Const type_s;
+};
+
+/* Zero-terminated USB VID/PID array */
+static struct usbdev_s usbid[] = {
+ {0x03f0, 0x3b17, "Hewlett-Packard", "LaserJet M1005",
+ "multi-function peripheral"},
+ {0x03f0, 0x5617, "Hewlett-Packard", "LaserJet M1120",
+ "multi-function peripheral"},
+ {0x03f0, 0x5717, "Hewlett-Packard", "LaserJet M1120n",
+ "multi-function peripheral"},
+ {0, 0, NULL, NULL, NULL},
+ {0, 0, NULL, NULL, NULL}
+};
+
+static int cur_idx;
+
+#define BR_CONT_MIN 0x1
+#define BR_CONT_MAX 0xb
+
+#define RGB 1
+#define GRAY 0
+
+#define MAX_X_H 0x350
+#define MAX_Y_H 0x490
+#define MAX_X_S 220
+#define MAX_Y_S 330
+
+#define OPTION_MAX 9
+
+static SANE_Word resolution_list[] = {
+ 7, 75, 100, 150, 200, 300, 600, 1200
+};
+static SANE_Range range_x = { 0, MAX_X_S, 0 };
+static SANE_Range range_y = { 0, MAX_Y_S, 0 };
+
+static SANE_Range range_br_cont = { BR_CONT_MIN, BR_CONT_MAX, 0 };
+
+static const SANE_String_Const mode_list[] = {
+ SANE_VALUE_SCAN_MODE_GRAY,
+ SANE_VALUE_SCAN_MODE_COLOR,
+ NULL
+};
+
+#define X1_OFFSET 2
+#define X2_OFFSET 4
+#define Y1_OFFSET 3
+#define Y2_OFFSET 5
+#define RES_OFFSET 1
+#define COLOR_OFFSET 8
+#define BRIGH_OFFSET 6
+#define CONTR_OFFSET 7
+
+#define STATUS_IDLE 0
+#define STATUS_SCANNING 1
+#define STATUS_CANCELING 2
+
+struct device_s
+{
+ struct device_s *next;
+ SANE_String_Const devname;
+ int idx; /* Index in the usbid array */
+ int dn; /* Usb "Handle" */
+ SANE_Option_Descriptor optiond[OPTION_MAX];
+ char *buffer;
+ int bufs;
+ int read_offset;
+ int write_offset_r;
+ int write_offset_g;
+ int write_offset_b;
+ int status;
+ int width;
+ int height;
+ SANE_Word optionw[OPTION_MAX];
+ uint32_t conf_data[512];
+ uint32_t packet_data[512];
+};
+
+
+static void
+do_cancel(struct device_s *dev);
+
+
+static struct device_s *devlist_head;
+static int devlist_count; /* Number of element in the list */
+
+/*
+ * List of pointers to devices - will be dynamically allocated depending
+ * on the number of devices found.
+ */
+static SANE_Device **devlist = NULL;
+
+/* round() is c99, so we provide our own, though this version wont return -0 */
+static double
+round2(double x)
+{
+ return (double)(x >= 0.0) ? (int)(x+0.5) : (int)(x-0.5);
+}
+
+static void
+update_img_size (struct device_s *dev)
+{
+ int dx, dy;
+
+ /* Only update the width when not scanning,
+ * otherwise the scanner give us the correct width */
+ if (dev->status == STATUS_SCANNING)
+ {
+ dev->height = -1;
+ return;
+ }
+
+ dx = dev->optionw[X2_OFFSET] - dev->optionw[X1_OFFSET];
+ dy = dev->optionw[Y2_OFFSET] - dev->optionw[Y1_OFFSET];
+
+ switch (dev->optionw[RES_OFFSET])
+ {
+ case 75:
+ dev->width = round2 ((dx / ((double) MAX_X_S)) * 640);
+ dev->height = round2 ((dy / ((double) MAX_Y_S)) * 880);
+ break;
+ case 100:
+ dev->width = round2 ((dx / ((double) MAX_X_S)) * 848);
+ dev->height = round2 ((dy / ((double) MAX_Y_S)) * 1180);
+ break;
+ case 150:
+ dev->width = round2 ((dx / ((double) MAX_X_S)) * 1264);
+ dev->height = round2 ((dy / ((double) MAX_Y_S)) * 1775);
+ break;
+ case 200:
+ dev->width = round2 ((dx / ((double) MAX_X_S)) * 1696);
+ dev->height = round2 ((dy / ((double) MAX_Y_S)) * 2351);
+ break;
+ case 300:
+ dev->width = round2 ((dx / ((double) MAX_X_S)) * 2528);
+ dev->height = round2 ((dy / ((double) MAX_Y_S)) * 3510);
+ break;
+ case 600:
+ dev->width = round2 ((dx / ((double) MAX_X_S)) * 5088);
+ dev->height = round2 ((dy / ((double) MAX_Y_S)) * 7020);
+ break;
+ case 1200:
+ dev->width = round2 ((dx / ((double) MAX_X_S)) * 10208);
+ dev->height = round2 ((dy / ((double) MAX_Y_S)) * 14025);
+ break;
+ }
+
+ DBG(2,"New image size: %dx%d\n",dev->width, dev->height);
+
+}
+
+/* This function is copy/pasted from the Epson backend */
+static size_t
+max_string_size (const SANE_String_Const strings[])
+{
+ size_t size, max_size = 0;
+ int i;
+
+ for (i = 0; strings[i]; i++)
+ {
+ size = strlen (strings[i]) + 1;
+ if (size > max_size)
+ max_size = size;
+ }
+ return max_size;
+}
+
+
+static SANE_Status
+attach (SANE_String_Const devname)
+{
+ struct device_s *dev;
+
+ dev = malloc (sizeof (struct device_s));
+ if (!dev)
+ return SANE_STATUS_NO_MEM;
+ memset (dev, 0, sizeof (struct device_s));
+
+ dev->devname = devname;
+ DBG(1,"New device found: %s\n",dev->devname);
+
+/* Init the whole structure with default values */
+ /* Number of options */
+ dev->optiond[0].name = "";
+ dev->optiond[0].title = NULL;
+ dev->optiond[0].desc = NULL;
+ dev->optiond[0].type = SANE_TYPE_INT;
+ dev->optiond[0].unit = SANE_UNIT_NONE;
+ dev->optiond[0].size = sizeof (SANE_Word);
+ dev->optionw[0] = OPTION_MAX;
+
+ /* resolution */
+ dev->optiond[RES_OFFSET].name = "resolution";
+ dev->optiond[RES_OFFSET].title = "resolution";
+ dev->optiond[RES_OFFSET].desc = "resolution";
+ dev->optiond[RES_OFFSET].type = SANE_TYPE_INT;
+ dev->optiond[RES_OFFSET].unit = SANE_UNIT_DPI;
+ dev->optiond[RES_OFFSET].type = SANE_TYPE_INT;
+ dev->optiond[RES_OFFSET].size = sizeof (SANE_Word);
+ dev->optiond[RES_OFFSET].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ dev->optiond[RES_OFFSET].constraint_type = SANE_CONSTRAINT_WORD_LIST;
+ dev->optiond[RES_OFFSET].constraint.word_list = resolution_list;
+ dev->optionw[RES_OFFSET] = 75;
+
+ /* scan area */
+ dev->optiond[X1_OFFSET].name = "tl-x";
+ dev->optiond[X1_OFFSET].title = "tl-x";
+ dev->optiond[X1_OFFSET].desc = "tl-x";
+ dev->optiond[X1_OFFSET].type = SANE_TYPE_INT;
+ dev->optiond[X1_OFFSET].unit = SANE_UNIT_MM;
+ dev->optiond[X1_OFFSET].size = sizeof (SANE_Word);
+ dev->optiond[X1_OFFSET].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ dev->optiond[X1_OFFSET].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->optiond[X1_OFFSET].constraint.range = &range_x;
+ dev->optionw[X1_OFFSET] = 0;
+
+ dev->optiond[Y1_OFFSET].name = "tl-y";
+ dev->optiond[Y1_OFFSET].title = "tl-y";
+ dev->optiond[Y1_OFFSET].desc = "tl-y";
+ dev->optiond[Y1_OFFSET].type = SANE_TYPE_INT;
+ dev->optiond[Y1_OFFSET].unit = SANE_UNIT_MM;
+ dev->optiond[Y1_OFFSET].size = sizeof (SANE_Word);
+ dev->optiond[Y1_OFFSET].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ dev->optiond[Y1_OFFSET].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->optiond[Y1_OFFSET].constraint.range = &range_y;
+ dev->optionw[Y1_OFFSET] = 0;
+
+ dev->optiond[X2_OFFSET].name = "br-x";
+ dev->optiond[X2_OFFSET].title = "br-x";
+ dev->optiond[X2_OFFSET].desc = "br-x";
+ dev->optiond[X2_OFFSET].type = SANE_TYPE_INT;
+ dev->optiond[X2_OFFSET].unit = SANE_UNIT_MM;
+ dev->optiond[X2_OFFSET].size = sizeof (SANE_Word);
+ dev->optiond[X2_OFFSET].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ dev->optiond[X2_OFFSET].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->optiond[X2_OFFSET].constraint.range = &range_x;
+ dev->optionw[X2_OFFSET] = MAX_X_S;
+
+ dev->optiond[Y2_OFFSET].name = "br-y";
+ dev->optiond[Y2_OFFSET].title = "br-y";
+ dev->optiond[Y2_OFFSET].desc = "br-y";
+ dev->optiond[Y2_OFFSET].type = SANE_TYPE_INT;
+ dev->optiond[Y2_OFFSET].unit = SANE_UNIT_MM;
+ dev->optiond[Y2_OFFSET].size = sizeof (SANE_Word);
+ dev->optiond[Y2_OFFSET].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ dev->optiond[Y2_OFFSET].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->optiond[Y2_OFFSET].constraint.range = &range_y;
+ dev->optionw[Y2_OFFSET] = MAX_Y_S;
+
+ /* brightness */
+ dev->optiond[BRIGH_OFFSET].name = "brightness";
+ dev->optiond[BRIGH_OFFSET].title = "Brightness";
+ dev->optiond[BRIGH_OFFSET].desc = "Set the brightness";
+ dev->optiond[BRIGH_OFFSET].type = SANE_TYPE_INT;
+ dev->optiond[BRIGH_OFFSET].unit = SANE_UNIT_NONE;
+ dev->optiond[BRIGH_OFFSET].size = sizeof (SANE_Word);
+ dev->optiond[BRIGH_OFFSET].cap =
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ dev->optiond[BRIGH_OFFSET].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->optiond[BRIGH_OFFSET].constraint.range = &range_br_cont;
+ dev->optionw[BRIGH_OFFSET] = 0x6;
+
+ /* contrast */
+ dev->optiond[CONTR_OFFSET].name = "contrast";
+ dev->optiond[CONTR_OFFSET].title = "Contrast";
+ dev->optiond[CONTR_OFFSET].desc = "Set the contrast";
+ dev->optiond[CONTR_OFFSET].type = SANE_TYPE_INT;
+ dev->optiond[CONTR_OFFSET].unit = SANE_UNIT_NONE;
+ dev->optiond[CONTR_OFFSET].size = sizeof (SANE_Word);
+ dev->optiond[CONTR_OFFSET].cap =
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ dev->optiond[CONTR_OFFSET].constraint_type = SANE_CONSTRAINT_RANGE;
+ dev->optiond[CONTR_OFFSET].constraint.range = &range_br_cont;
+ dev->optionw[CONTR_OFFSET] = 0x6;
+
+ /* Color */
+ dev->optiond[COLOR_OFFSET].name = SANE_NAME_SCAN_MODE;
+ dev->optiond[COLOR_OFFSET].title = SANE_TITLE_SCAN_MODE;
+ dev->optiond[COLOR_OFFSET].desc = SANE_DESC_SCAN_MODE;
+ dev->optiond[COLOR_OFFSET].type = SANE_TYPE_STRING;
+ dev->optiond[COLOR_OFFSET].size = max_string_size (mode_list);
+ dev->optiond[COLOR_OFFSET].cap =
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ dev->optiond[COLOR_OFFSET].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ dev->optiond[COLOR_OFFSET].constraint.string_list = mode_list;
+ dev->optionw[COLOR_OFFSET] = RGB;
+ dev->dn = 0;
+ dev->idx = cur_idx;
+ dev->status = STATUS_IDLE;
+
+ dev->next = devlist_head;
+ devlist_head = dev;
+ devlist_count++;
+
+
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_init (SANE_Int * version_code,
+ SANE_Auth_Callback __sane_unused__ authorize)
+{
+
+ if (version_code != NULL)
+ *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, V_MINOR, BUILD);
+
+ DBG_INIT();
+
+ sanei_usb_init ();
+
+ return SANE_STATUS_GOOD;
+}
+
+void
+sane_exit (void)
+{
+ /* free everything */
+ struct device_s *iter;
+
+ if (devlist)
+ {
+ int i;
+ for (i = 0; devlist[i]; i++)
+ free (devlist[i]);
+ free (devlist);
+ devlist = NULL;
+ }
+ if (devlist_head)
+ {
+ iter = devlist_head->next;
+ free (devlist_head);
+ devlist_head = NULL;
+ while (iter)
+ {
+ struct device_s *tmp = iter;
+ iter = iter->next;
+ free (tmp);
+ }
+ }
+ devlist_count = 0;
+}
+
+SANE_Status
+sane_get_devices (const SANE_Device * **device_list,
+ SANE_Bool __sane_unused__ local_only)
+{
+ struct device_s *iter;
+ int i;
+
+ devlist_count = 0;
+
+ if (devlist_head)
+ {
+ iter = devlist_head->next;
+ free (devlist_head);
+ devlist_head = NULL;
+ while (iter)
+ {
+ struct device_s *tmp = iter;
+ iter = iter->next;
+ free (tmp);
+ }
+ }
+
+ /* Rebuild our internal scanner list */
+ for (cur_idx = 0; usbid[cur_idx].vendor_id; cur_idx++)
+ sanei_usb_find_devices (usbid[cur_idx].vendor_id,
+ usbid[cur_idx].product_id, attach);
+
+ if (devlist)
+ {
+ for (i = 0; devlist[i]; i++)
+ free (devlist[i]);
+ free (devlist);
+ }
+
+ /* rebuild the sane-API scanner list array */
+ devlist = malloc (sizeof (devlist[0]) * (devlist_count + 1));
+ if (!devlist)
+ return SANE_STATUS_NO_MEM;
+
+ memset (devlist, 0, sizeof (devlist[0]) * (devlist_count + 1));
+
+ for (i = 0, iter = devlist_head; i < devlist_count; i++, iter = iter->next)
+ {
+ devlist[i] = malloc (sizeof (SANE_Device));
+ if (!devlist[i])
+ {
+ int j;
+ for (j = 0; j < i; j++)
+ free (devlist[j]);
+ free (devlist);
+ devlist = NULL;
+ return SANE_STATUS_NO_MEM;
+ }
+ devlist[i]->name = iter->devname;
+ devlist[i]->vendor = usbid[iter->idx].vendor_s;
+ devlist[i]->model = usbid[iter->idx].model_s;
+ devlist[i]->type = usbid[iter->idx].type_s;
+ }
+ if (device_list)
+ *device_list = (const SANE_Device **) devlist;
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_open (SANE_String_Const name, SANE_Handle * h)
+{
+ struct device_s *dev;
+ int ret;
+
+ if(!devlist_head)
+ sane_get_devices(NULL,(SANE_Bool)0);
+
+ dev = devlist_head;
+
+ if (strlen (name))
+ for (; dev; dev = dev->next)
+ if (!strcmp (name, dev->devname))
+ break;
+
+ if (!dev) {
+ DBG(1,"Unable to find device %s\n",name);
+ return SANE_STATUS_INVAL;
+ }
+
+ DBG(1,"Found device %s\n",name);
+
+ /* Now open the usb device */
+ ret = sanei_usb_open (name, &(dev->dn));
+ if (ret != SANE_STATUS_GOOD) {
+ DBG(1,"Unable to open device %s\n",name);
+ return ret;
+ }
+
+ /* Claim the first interface */
+ ret = sanei_usb_claim_interface (dev->dn, 0);
+ if (ret != SANE_STATUS_GOOD)
+ {
+ sanei_usb_close (dev->dn);
+ /* if we cannot claim the interface, this is because
+ someone else is using it */
+ DBG(1,"Unable to claim scanner interface on device %s\n",name);
+ return SANE_STATUS_DEVICE_BUSY;
+ }
+#ifdef HAVE_SANEI_USB_SET_TIMEOUT
+ sanei_usb_set_timeout (30000); /* 30s timeout */
+#endif
+
+ *h = dev;
+
+ return SANE_STATUS_GOOD;
+}
+
+void
+sane_close (SANE_Handle h)
+{
+ struct device_s *dev = (struct device_s *) h;
+
+ /* Just in case if sane_cancel() is called
+ * after starting a scan but not while a sane_read
+ */
+ if (dev->status == STATUS_CANCELING)
+ {
+ do_cancel(dev);
+ }
+
+ sanei_usb_release_interface (dev->dn, 0);
+ sanei_usb_close (dev->dn);
+
+}
+
+const SANE_Option_Descriptor *
+sane_get_option_descriptor (SANE_Handle h, SANE_Int option)
+{
+ struct device_s *dev = (struct device_s *) h;
+
+ if (option >= OPTION_MAX || option < 0)
+ return NULL;
+ return &(dev->optiond[option]);
+}
+
+static SANE_Status
+getvalue (SANE_Handle h, SANE_Int option, void *v)
+{
+ struct device_s *dev = (struct device_s *) h;
+
+ if (option != COLOR_OFFSET)
+ *((SANE_Word *) v) = dev->optionw[option];
+ else
+ {
+ strcpy ((char *) v,
+ dev->optiond[option].constraint.string_list[dev->
+ optionw[option]]);
+ }
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+setvalue (SANE_Handle h, SANE_Int option, void *value, SANE_Int * info)
+{
+ struct device_s *dev = (struct device_s *) h;
+ SANE_Status status = SANE_STATUS_GOOD;
+ int s_unit;
+ int s_unit_2;
+
+ if (option == 0)
+ return SANE_STATUS_UNSUPPORTED;
+
+
+ status = sanei_constrain_value (&(dev->optiond[option]), value, info);
+
+ if (status != SANE_STATUS_GOOD)
+ return status;
+
+
+
+ if (info)
+ *info |= SANE_INFO_RELOAD_PARAMS;
+ switch (option)
+ {
+ case X1_OFFSET:
+ dev->optionw[option] = *((SANE_Word *) value);
+ s_unit = (int) round2 ((dev->optionw[option] / ((double) MAX_X_S))
+ * MAX_X_H);
+ s_unit_2 = (int) round2 ((dev->optionw[X2_OFFSET] / ((double) MAX_X_S))
+ * MAX_X_H);
+ if (abs (s_unit_2 - s_unit) < MIN_SCAN_ZONE)
+ s_unit = s_unit_2 - MIN_SCAN_ZONE;
+ dev->optionw[option] = round2 ((s_unit / ((double) MAX_X_H)) * MAX_X_S);
+ if (info)
+ *info |= SANE_INFO_INEXACT;
+ break;
+
+ case X2_OFFSET:
+ /* X units */
+ /* convert into "scanner" unit, then back into mm */
+ dev->optionw[option] = *((SANE_Word *) value);
+
+ s_unit = (int) round2 ((dev->optionw[option] / ((double) MAX_X_S))
+ * MAX_X_H);
+ s_unit_2 = (int) round2 ((dev->optionw[X1_OFFSET] / ((double) MAX_X_S))
+ * MAX_X_H);
+ if (abs (s_unit_2 - s_unit) < MIN_SCAN_ZONE)
+ s_unit = s_unit_2 + MIN_SCAN_ZONE;
+ dev->optionw[option] = round2 ((s_unit / ((double) MAX_X_H)) * MAX_X_S);
+ if (info)
+ *info |= SANE_INFO_INEXACT;
+ break;
+ case Y1_OFFSET:
+ /* Y units */
+ dev->optionw[option] = *((SANE_Word *) value);
+
+ s_unit = (int) round2 ((dev->optionw[option] / ((double) MAX_Y_S))
+ * MAX_Y_H);
+
+ s_unit_2 = (int) round2 ((dev->optionw[Y2_OFFSET] / ((double) MAX_Y_S))
+ * MAX_Y_H);
+ if (abs (s_unit_2 - s_unit) < MIN_SCAN_ZONE)
+ s_unit = s_unit_2 - MIN_SCAN_ZONE;
+
+ dev->optionw[option] = round2 ((s_unit / ((double) MAX_Y_H)) * MAX_Y_S);
+ if (info)
+ *info |= SANE_INFO_INEXACT;
+ break;
+ case Y2_OFFSET:
+ /* Y units */
+ dev->optionw[option] = *((SANE_Word *) value);
+
+ s_unit = (int) round2 ((dev->optionw[option] / ((double) MAX_Y_S))
+ * MAX_Y_H);
+
+ s_unit_2 = (int) round2 ((dev->optionw[Y1_OFFSET] / ((double) MAX_Y_S))
+ * MAX_Y_H);
+ if (abs (s_unit_2 - s_unit) < MIN_SCAN_ZONE)
+ s_unit = s_unit_2 + MIN_SCAN_ZONE;
+
+ dev->optionw[option] = round2 ((s_unit / ((double) MAX_Y_H)) * MAX_Y_S);
+ if (info)
+ *info |= SANE_INFO_INEXACT;
+ break;
+ case COLOR_OFFSET:
+ if (!strcmp ((char *) value, mode_list[0]))
+ dev->optionw[option] = GRAY; /* Gray */
+ else if (!strcmp ((char *) value, mode_list[1]))
+ dev->optionw[option] = RGB; /* RGB */
+ else
+ return SANE_STATUS_INVAL;
+ break;
+ default:
+ dev->optionw[option] = *((SANE_Word *) value);
+ }
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_control_option (SANE_Handle h, SANE_Int option,
+ SANE_Action a, void *v, SANE_Int * i)
+{
+
+ if (option < 0 || option >= OPTION_MAX)
+ return SANE_STATUS_INVAL;
+
+ if (i)
+ *i = 0;
+
+
+ switch (a)
+ {
+ case SANE_ACTION_GET_VALUE:
+ return getvalue (h, option, v);
+
+ case SANE_ACTION_SET_VALUE:
+ return setvalue (h, option, v, i);
+
+ default:
+ return SANE_STATUS_INVAL;
+ }
+}
+
+SANE_Status
+sane_get_parameters (SANE_Handle h, SANE_Parameters * p)
+{
+ struct device_s *dev = (struct device_s *) h;
+
+ if (!p)
+ return SANE_STATUS_INVAL;
+
+ p->format =
+ dev->optionw[COLOR_OFFSET] == RGB ? SANE_FRAME_RGB : SANE_FRAME_GRAY;
+ p->last_frame = SANE_TRUE;
+ p->depth = 8;
+
+ update_img_size (dev);
+ p->pixels_per_line = dev->width;
+ p->lines = dev->height;
+ p->bytes_per_line = p->pixels_per_line;
+ if (p->format == SANE_FRAME_RGB)
+ p->bytes_per_line *= 3;
+
+ return SANE_STATUS_GOOD;
+}
+
+static void
+send_pkt (int command, int data_size, struct device_s *dev)
+{
+ size_t size = 32;
+
+ DBG(100,"Sending packet %d, next data size %d, device %s\n", command, data_size, dev->devname);
+
+ memset (dev->packet_data, 0, size);
+ dev->packet_data[0] = htonl (MAGIC_NUMBER);
+ dev->packet_data[1] = htonl (command);
+ dev->packet_data[5] = htonl (data_size);
+ sanei_usb_write_bulk (dev->dn, (unsigned char *) dev->packet_data, &size);
+}
+
+
+/* s: printer status */
+/* Return the next packet size */
+static int
+wait_ack (struct device_s *dev, int *s)
+{
+ SANE_Status ret;
+ size_t size;
+ DBG(100, "Waiting scanner answer on device %s\n",dev->devname);
+ do
+ {
+ size = 32;
+ ret =
+ sanei_usb_read_bulk (dev->dn, (unsigned char *) dev->packet_data,
+ &size);
+ }
+ while (SANE_STATUS_EOF == ret || size == 0);
+ if (s)
+ *s = ntohl (dev->packet_data[4]);
+ return ntohl (dev->packet_data[5]);
+}
+
+static void
+send_conf (struct device_s *dev)
+{
+ int y1, y2, x1, x2;
+ size_t size = 100;
+ DBG(100,"Sending configuration packet on device %s\n",dev->devname);
+ y1 = (int) round2 ((dev->optionw[Y1_OFFSET] / ((double) MAX_Y_S)) * MAX_Y_H);
+ y2 = (int) round2 ((dev->optionw[Y2_OFFSET] / ((double) MAX_Y_S)) * MAX_Y_H);
+ x1 = (int) round2 ((dev->optionw[X1_OFFSET] / ((double) MAX_X_S)) * MAX_X_H);
+ x2 = (int) round2 ((dev->optionw[X2_OFFSET] / ((double) MAX_X_S)) * MAX_X_H);
+
+ DBG(100,"\t x1: %d, x2: %d, y1: %d, y2: %d\n",x1, x2, y1, y2);
+ DBG(100,"\t brightness: %d, contrast: %d\n", dev->optionw[BRIGH_OFFSET], dev->optionw[CONTR_OFFSET]);
+ DBG(100,"\t resolution: %d\n",dev->optionw[RES_OFFSET]);
+
+ dev->conf_data[0] = htonl (0x15);
+ dev->conf_data[1] = htonl (dev->optionw[BRIGH_OFFSET]);
+ dev->conf_data[2] = htonl (dev->optionw[CONTR_OFFSET]);
+ dev->conf_data[3] = htonl (dev->optionw[RES_OFFSET]);
+ dev->conf_data[4] = htonl (0x1);
+ dev->conf_data[5] = htonl (0x1);
+ dev->conf_data[6] = htonl (0x1);
+ dev->conf_data[7] = htonl (0x1);
+ dev->conf_data[8] = 0;
+ dev->conf_data[9] = 0;
+ dev->conf_data[10] = htonl (0x8);
+ dev->conf_data[11] = 0;
+ dev->conf_data[12] = 0;
+ dev->conf_data[13] = 0;
+ dev->conf_data[14] = 0;
+ dev->conf_data[16] = htonl (y1);
+ dev->conf_data[17] = htonl (x1);
+ dev->conf_data[18] = htonl (y2);
+ dev->conf_data[19] = htonl (x2);
+ dev->conf_data[20] = 0;
+ dev->conf_data[21] = 0;
+ dev->conf_data[22] = htonl (0x491);
+ dev->conf_data[23] = htonl (0x352);
+
+ if (dev->optionw[COLOR_OFFSET] == RGB)
+ {
+ dev->conf_data[15] = htonl (0x2);
+ dev->conf_data[24] = htonl (0x1);
+ DBG(100,"\t Scanning in RGB format\n");
+ }
+ else
+ {
+ dev->conf_data[15] = htonl (0x6);
+ dev->conf_data[24] = htonl (0x0);
+ DBG(100,"\t Scanning in Grayscale format\n");
+ }
+ sanei_usb_write_bulk (dev->dn, (unsigned char *) dev->conf_data, &size);
+}
+
+static SANE_Status
+get_data (struct device_s *dev)
+{
+ int color;
+ size_t size;
+ int packet_size;
+ unsigned char *buffer = (unsigned char *) dev->packet_data;
+ if (dev->status == STATUS_IDLE)
+ return SANE_STATUS_IO_ERROR;
+ /* first wait a standard data pkt */
+ do
+ {
+ size = 32;
+ sanei_usb_read_bulk (dev->dn, buffer, &size);
+ if (size)
+ {
+ if (ntohl (dev->packet_data[0]) == MAGIC_NUMBER)
+ {
+ if (ntohl (dev->packet_data[1]) == PKT_DATA)
+ break;
+ if (ntohl (dev->packet_data[1]) == PKT_END_DATA)
+ {
+ dev->status = STATUS_IDLE;
+ DBG(100,"End of scan encountered on device %s\n",dev->devname);
+ send_pkt (PKT_GO_IDLE, 0, dev);
+ wait_ack (dev, NULL);
+ wait_ack (dev, NULL);
+ send_pkt (PKT_UNKNOW_1, 0, dev);
+ wait_ack (dev, NULL);
+ send_pkt (PKT_RESET, 0, dev);
+ sleep (2); /* Time for the scanning head to go back home */
+ return SANE_STATUS_EOF;
+ }
+ }
+ }
+ }
+ while (1);
+ packet_size = ntohl (dev->packet_data[5]);
+ if (!dev->buffer)
+ {
+ dev->bufs = packet_size - 24 /* size of header */ ;
+ if (dev->optionw[COLOR_OFFSET] == RGB)
+ dev->bufs *= 3;
+ dev->buffer = malloc (dev->bufs);
+ if (!dev->buffer)
+ return SANE_STATUS_NO_MEM;
+ dev->write_offset_r = 0;
+ dev->write_offset_g = 1;
+ dev->write_offset_b = 2;
+
+ }
+ /* Get the "data header" */
+ do
+ {
+ size = 24;
+ sanei_usb_read_bulk (dev->dn, buffer, &size);
+ }
+ while (!size);
+ color = ntohl (dev->packet_data[0]);
+ packet_size -= size;
+ dev->width = ntohl (dev->packet_data[5]);
+ DBG(100,"Got data size %d on device %s. Scan width: %d\n",packet_size, dev->devname, dev->width);
+ /* Now, read the data */
+ do
+ {
+ int j;
+ int i;
+ int ret;
+ do
+ {
+ size = packet_size > 512 ? 512 : packet_size;
+ ret = sanei_usb_read_bulk (dev->dn, buffer, &size);
+ }
+ while (!size || ret != SANE_STATUS_GOOD);
+ packet_size -= size;
+ switch (color)
+ {
+ case RED_LAYER:
+ DBG(101,"Got red layer data on device %s\n",dev->devname);
+ i = dev->write_offset_r + 3 * size;
+ if (i > dev->bufs)
+ i = dev->bufs;
+ for (j = 0; dev->write_offset_r < i; dev->write_offset_r += 3)
+ dev->buffer[dev->write_offset_r] = buffer[j++];
+ break;
+ case GREEN_LAYER:
+ DBG(101,"Got green layer data on device %s\n",dev->devname);
+ i = dev->write_offset_g + 3 * size;
+ if (i > dev->bufs)
+ i = dev->bufs;
+ for (j = 0; dev->write_offset_g < i; dev->write_offset_g += 3)
+ dev->buffer[dev->write_offset_g] = buffer[j++];
+ break;
+ case BLUE_LAYER:
+ DBG(101,"Got blue layer data on device %s\n",dev->devname);
+ i = dev->write_offset_b + 3 * size;
+ if (i > dev->bufs)
+ i = dev->bufs;
+ for (j = 0; dev->write_offset_b < i; dev->write_offset_b += 3)
+ dev->buffer[dev->write_offset_b] = buffer[j++];
+ break;
+ case GRAY_LAYER:
+ DBG(101,"Got gray layer data on device %s\n",dev->devname);
+ if (dev->write_offset_r + (int)size >= dev->bufs)
+ size = dev->bufs - dev->write_offset_r;
+ memcpy (dev->buffer + dev->write_offset_r, buffer, size);
+ dev->write_offset_r += size;
+ break;
+ }
+ }
+ while (packet_size > 0);
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_start (SANE_Handle h)
+{
+ struct device_s *dev = (struct device_s *) h;
+ int status;
+ size_t size;
+
+ dev->read_offset = 0;
+ dev->write_offset_r = 0;
+ dev->write_offset_g = 1;
+ dev->write_offset_b = 2;
+
+ free (dev->buffer);
+ dev->buffer = NULL;
+
+
+ send_pkt (PKT_RESET, 0, dev);
+ send_pkt (PKT_READ_STATUS, 0, dev);
+ wait_ack (dev, &status);
+ if (status)
+ return SANE_STATUS_IO_ERROR;
+
+ send_pkt (PKT_READCONF, 0, dev);
+
+ if ((size = wait_ack (dev, NULL)))
+ {
+ sanei_usb_read_bulk (dev->dn, (unsigned char *) dev->conf_data, &size);
+ }
+ send_pkt (PKT_SETCONF, 100, dev);
+ send_conf (dev);
+ wait_ack (dev, NULL);
+
+ send_pkt (PKT_START_SCAN, 0, dev);
+ wait_ack (dev, NULL);
+ if ((size = wait_ack (dev, NULL)))
+ {
+ sanei_usb_read_bulk (dev->dn, (unsigned char *) dev->conf_data, &size);
+ }
+ if ((size = wait_ack (dev, NULL)))
+ {
+ sanei_usb_read_bulk (dev->dn, (unsigned char *) dev->conf_data, &size);
+ }
+ if ((size = wait_ack (dev, NULL)))
+ {
+ sanei_usb_read_bulk (dev->dn, (unsigned char *) dev->conf_data, &size);
+ }
+
+ dev->status = STATUS_SCANNING;
+ /* Get the first data */
+ return get_data (dev);
+}
+
+
+static void
+do_cancel(struct device_s *dev)
+{
+ while (get_data (dev) == SANE_STATUS_GOOD);
+ free (dev->buffer);
+ dev->buffer = NULL;
+}
+
+static int
+min3 (int r, int g, int b)
+{
+ /* Optimize me ! */
+ g--;
+ b -= 2;
+ if (r < g && r < b)
+ return r;
+ if (b < r && b < g)
+ return b;
+ return g;
+}
+
+SANE_Status
+sane_read (SANE_Handle h, SANE_Byte * buf, SANE_Int maxlen, SANE_Int * len)
+{
+ struct device_s *dev = (struct device_s *) h;
+ int available;
+ int ret;
+ *len = 0;
+ if (dev->status == STATUS_IDLE)
+ return SANE_STATUS_IO_ERROR;
+ if (dev->optionw[COLOR_OFFSET] == RGB)
+ {
+ while (min3 (dev->write_offset_r, dev->write_offset_g,
+ dev->write_offset_b) <= dev->read_offset)
+ {
+ ret = get_data (dev);
+ if (ret != SANE_STATUS_GOOD)
+ {
+ if (min3 (dev->write_offset_r,
+ dev->write_offset_g,
+ dev->write_offset_b) <= dev->read_offset)
+ return ret;
+ }
+ }
+ available = min3 (dev->write_offset_r, dev->write_offset_g,
+ dev->write_offset_b);
+ }
+ else
+ {
+ while (dev->write_offset_r <= dev->read_offset)
+ {
+ ret = get_data (dev);
+ if (ret != SANE_STATUS_GOOD)
+ if (dev->write_offset_r <= dev->read_offset)
+ return ret;
+ }
+ available = dev->write_offset_r;
+ }
+ *len = available - dev->read_offset;
+ if (*len > maxlen)
+ *len = maxlen;
+ memcpy (buf, dev->buffer + dev->read_offset, *len);
+ dev->read_offset += *len;
+ if (dev->read_offset == dev->bufs)
+ {
+ free (dev->buffer);
+ dev->buffer = NULL;
+ dev->read_offset = 0;
+ dev->write_offset_r = 0;
+ dev->write_offset_g = 1;
+ dev->write_offset_b = 2;
+ }
+
+ /* Special case where sane_cancel is called while scanning */
+ if (dev->status == STATUS_CANCELING)
+ {
+ do_cancel(dev);
+ return SANE_STATUS_CANCELLED;
+ }
+ return SANE_STATUS_GOOD;
+}
+
+void
+sane_cancel (SANE_Handle h)
+{
+ struct device_s *dev = (struct device_s *) h;
+
+
+ if (dev->status == STATUS_SCANNING)
+ {
+ dev->status = STATUS_CANCELING;
+ return;
+ }
+
+ free (dev->buffer);
+ dev->buffer = NULL;
+}
+
+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;
+}
+