kopia lustrzana https://gitlab.com/sane-project/backends
1098 wiersze
29 KiB
C
1098 wiersze
29 KiB
C
/* 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 0
|
|
|
|
#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[] = {
|
|
"Gray",
|
|
"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;
|
|
|
|
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 = round ((dx / ((double) MAX_X_S)) * 640);
|
|
dev->height = round ((dy / ((double) MAX_Y_S)) * 880);
|
|
break;
|
|
case 100:
|
|
dev->width = round ((dx / ((double) MAX_X_S)) * 848);
|
|
dev->height = round ((dy / ((double) MAX_Y_S)) * 1180);
|
|
break;
|
|
case 150:
|
|
dev->width = round ((dx / ((double) MAX_X_S)) * 1264);
|
|
dev->height = round ((dy / ((double) MAX_Y_S)) * 1775);
|
|
break;
|
|
case 200:
|
|
dev->width = round ((dx / ((double) MAX_X_S)) * 1696);
|
|
dev->height = round ((dy / ((double) MAX_Y_S)) * 2351);
|
|
break;
|
|
case 300:
|
|
dev->width = round ((dx / ((double) MAX_X_S)) * 2528);
|
|
dev->height = round ((dy / ((double) MAX_Y_S)) * 3510);
|
|
break;
|
|
case 600:
|
|
dev->width = round ((dx / ((double) MAX_X_S)) * 5088);
|
|
dev->height = round ((dy / ((double) MAX_Y_S)) * 7020);
|
|
break;
|
|
case 1200:
|
|
dev->width = round ((dx / ((double) MAX_X_S)) * 10208);
|
|
dev->height = round ((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 (V_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) round ((dev->optionw[option] / ((double) MAX_X_S))
|
|
* MAX_X_H);
|
|
s_unit_2 = (int) round ((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] = round ((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) round ((dev->optionw[option] / ((double) MAX_X_S))
|
|
* MAX_X_H);
|
|
s_unit_2 = (int) round ((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] = round ((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) round ((dev->optionw[option] / ((double) MAX_Y_S))
|
|
* MAX_Y_H);
|
|
|
|
s_unit_2 = (int) round ((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] = round ((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) round ((dev->optionw[option] / ((double) MAX_Y_S))
|
|
* MAX_Y_H);
|
|
|
|
s_unit_2 = (int) round ((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] = round ((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) round ((dev->optionw[Y1_OFFSET] / ((double) MAX_Y_S)) * MAX_Y_H);
|
|
y2 = (int) round ((dev->optionw[Y2_OFFSET] / ((double) MAX_Y_S)) * MAX_Y_H);
|
|
x1 = (int) round ((dev->optionw[X1_OFFSET] / ((double) MAX_X_S)) * MAX_X_H);
|
|
x2 = (int) round ((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 + 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;
|
|
}
|
|
|