kopia lustrzana https://gitlab.com/sane-project/backends
1019 wiersze
26 KiB
C
1019 wiersze
26 KiB
C
/* sane - Scanner Access Now Easy.
|
|
Copyright (C) 2003 Martijn van Oosterhout <kleptog@svana.org>
|
|
Copyright (C) 2003 Thomas Soumarmon <thomas.soumarmon@cogitae.net>
|
|
|
|
Originally copied from HP3300 testtools. Original notice follows:
|
|
|
|
Copyright (C) 2001 Bertrik Sikken (bertrik@zonnet.nl)
|
|
|
|
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.
|
|
*/
|
|
|
|
|
|
/*
|
|
SANE interface for hp54xx scanners. Prototype.
|
|
Parts of this source were inspired by other backends.
|
|
*/
|
|
|
|
|
|
/* definitions for debug */
|
|
#include "hp5400_debug.h"
|
|
|
|
#include "sane/sane.h"
|
|
#include "sane/sanei.h"
|
|
#include "sane/sanei_backend.h"
|
|
#include "sane/sanei_config.h"
|
|
#include "sane/saneopts.h"
|
|
#include "sane/config.h"
|
|
#include "sane/sanei_usb.h"
|
|
|
|
#include <stdlib.h> /* malloc, free */
|
|
#include <string.h> /* memcpy */
|
|
#include <stdio.h>
|
|
#include <errno.h>
|
|
|
|
#define HP5400_CONFIG_FILE "hp5400.conf"
|
|
|
|
#include "hp5400.h"
|
|
|
|
/* includes for data transfer methods */
|
|
#include "hp5400.h"
|
|
|
|
#ifdef STANDALONE
|
|
#include "hp5400_scanner.h"
|
|
#endif
|
|
|
|
#if defined(LINUX_USB_SUPPORT)
|
|
#include "hp5400_linux.c"
|
|
#endif
|
|
#if defined(USCANNER_SUPPORT)
|
|
#include "hp5400_uscanner.c"
|
|
#endif
|
|
#if defined(LIBUSB_SUPPORT)
|
|
#include "hp5400_libusb.c"
|
|
#endif
|
|
#if defined(LIBIEEE1284_SUPPORT)
|
|
#include "hp5400_ieee1284.c"
|
|
#endif
|
|
|
|
|
|
|
|
/* other definitions */
|
|
#ifndef min
|
|
#define min(A,B) (((A)<(B)) ? (A) : (B))
|
|
#endif
|
|
#ifndef max
|
|
#define max(A,B) (((A)>(B)) ? (A) : (B))
|
|
#endif
|
|
|
|
#define TRUE 1
|
|
#define FALSE 0
|
|
|
|
#define MM_TO_PIXEL(_mm_, _dpi_) ((_mm_) * (_dpi_) / 25.4)
|
|
#define PIXEL_TO_MM(_pixel_, _dpi_) ((_pixel_) * 25.4 / (_dpi_))
|
|
|
|
#define NUM_GAMMA_ENTRIES 65536
|
|
|
|
|
|
/* options enumerator */
|
|
typedef enum
|
|
{
|
|
optCount = 0,
|
|
|
|
optGroupGeometry,
|
|
optTLX, optTLY, optBRX, optBRY,
|
|
optDPI,
|
|
|
|
optGroupImage,
|
|
|
|
optGammaTableRed, /* Gamma Tables */
|
|
optGammaTableGreen,
|
|
optGammaTableBlue,
|
|
|
|
optLast, /* Disable the offset code */
|
|
|
|
optGroupMisc,
|
|
optOffsetX, optOffsetY
|
|
|
|
|
|
/* put temporarily disabled options here after optLast */
|
|
/*
|
|
optLamp,
|
|
*/
|
|
|
|
}
|
|
EOptionIndex;
|
|
|
|
typedef union
|
|
{
|
|
SANE_Word w;
|
|
SANE_Word *wa; /* word array */
|
|
SANE_String s;
|
|
}
|
|
TOptionValue;
|
|
|
|
|
|
typedef struct
|
|
{
|
|
SANE_Option_Descriptor aOptions[optLast];
|
|
TOptionValue aValues[optLast];
|
|
|
|
TScanParams ScanParams;
|
|
THWParams HWParams;
|
|
|
|
TDataPipe DataPipe;
|
|
int iLinesLeft;
|
|
|
|
SANE_Int *aGammaTableR; /* a 16-to-16 bit color lookup table */
|
|
SANE_Int *aGammaTableG; /* a 16-to-16 bit color lookup table */
|
|
SANE_Int *aGammaTableB; /* a 16-to-16 bit color lookup table */
|
|
|
|
int fScanning; /* TRUE if actively scanning */
|
|
int fCanceled;
|
|
}
|
|
TScanner;
|
|
|
|
|
|
/* linked list of SANE_Device structures */
|
|
typedef struct TDevListEntry
|
|
{
|
|
struct TDevListEntry *pNext;
|
|
SANE_Device dev;
|
|
}
|
|
TDevListEntry;
|
|
|
|
|
|
|
|
/* Device filename for USB access */
|
|
char usb_devfile[128];
|
|
|
|
static TDevListEntry *_pFirstSaneDev = 0;
|
|
static int iNumSaneDev = 0;
|
|
|
|
|
|
static const SANE_Device **_pSaneDevList = 0;
|
|
|
|
/* option constraints */
|
|
static const SANE_Range rangeGammaTable = {0, 65535, 1};
|
|
#ifdef SUPPORT_2400_DPI
|
|
static const SANE_Int setResolutions[] = {6, 75, 150, 300, 600, 1200, 2400};
|
|
#else
|
|
static const SANE_Int setResolutions[] = {5, 75, 150, 300, 600, 1200};
|
|
#endif
|
|
static const SANE_Range rangeXmm = {0, 220, 1};
|
|
static const SANE_Range rangeYmm = {0, 300, 1};
|
|
static const SANE_Range rangeXoffset = {0, 20, 1};
|
|
static const SANE_Range rangeYoffset = {0, 70, 1};
|
|
static const SANE_Int offsetX = 5;
|
|
static const SANE_Int offsetY = 52;
|
|
|
|
|
|
static void _InitOptions(TScanner *s)
|
|
{
|
|
int i, j;
|
|
SANE_Option_Descriptor *pDesc;
|
|
TOptionValue *pVal;
|
|
|
|
/* set a neutral gamma */
|
|
if( s->aGammaTableR == NULL ) /* Not yet allocated */
|
|
{
|
|
s->aGammaTableR = malloc( NUM_GAMMA_ENTRIES * sizeof( SANE_Int ) );
|
|
s->aGammaTableG = malloc( NUM_GAMMA_ENTRIES * sizeof( SANE_Int ) );
|
|
s->aGammaTableB = malloc( NUM_GAMMA_ENTRIES * sizeof( SANE_Int ) );
|
|
|
|
for (j = 0; j < NUM_GAMMA_ENTRIES; j++) {
|
|
s->aGammaTableR[j] = j;
|
|
s->aGammaTableG[j] = j;
|
|
s->aGammaTableB[j] = j;
|
|
}
|
|
}
|
|
|
|
for (i = optCount; i < optLast; i++) {
|
|
|
|
pDesc = &s->aOptions[i];
|
|
pVal = &s->aValues[i];
|
|
|
|
/* defaults */
|
|
pDesc->name = "";
|
|
pDesc->title = "";
|
|
pDesc->desc = "";
|
|
pDesc->type = SANE_TYPE_INT;
|
|
pDesc->unit = SANE_UNIT_NONE;
|
|
pDesc->size = sizeof(SANE_Word);
|
|
pDesc->constraint_type = SANE_CONSTRAINT_NONE;
|
|
pDesc->cap = 0;
|
|
|
|
switch (i) {
|
|
|
|
case optCount:
|
|
pDesc->title = SANE_TITLE_NUM_OPTIONS;
|
|
pDesc->desc = SANE_DESC_NUM_OPTIONS;
|
|
pDesc->cap = SANE_CAP_SOFT_DETECT;
|
|
pVal->w = (SANE_Word)optLast;
|
|
break;
|
|
|
|
case optGroupGeometry:
|
|
pDesc->title = "Geometry";
|
|
pDesc->type = SANE_TYPE_GROUP;
|
|
pDesc->size = 0;
|
|
break;
|
|
|
|
case optTLX:
|
|
pDesc->name = SANE_NAME_SCAN_TL_X;
|
|
pDesc->title = SANE_TITLE_SCAN_TL_X;
|
|
pDesc->desc = SANE_DESC_SCAN_TL_X;
|
|
pDesc->unit = SANE_UNIT_MM;
|
|
pDesc->constraint_type = SANE_CONSTRAINT_RANGE;
|
|
pDesc->constraint.range = &rangeXmm;
|
|
pDesc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
|
|
pVal->w = rangeXmm.min + offsetX;
|
|
break;
|
|
|
|
case optTLY:
|
|
pDesc->name = SANE_NAME_SCAN_TL_Y;
|
|
pDesc->title = SANE_TITLE_SCAN_TL_Y;
|
|
pDesc->desc = SANE_DESC_SCAN_TL_Y;
|
|
pDesc->unit = SANE_UNIT_MM;
|
|
pDesc->constraint_type = SANE_CONSTRAINT_RANGE;
|
|
pDesc->constraint.range = &rangeYmm;
|
|
pDesc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
|
|
pVal->w = rangeYmm.min + offsetY;
|
|
break;
|
|
|
|
case optBRX:
|
|
pDesc->name = SANE_NAME_SCAN_BR_X;
|
|
pDesc->title = SANE_TITLE_SCAN_BR_X;
|
|
pDesc->desc = SANE_DESC_SCAN_BR_X;
|
|
pDesc->unit = SANE_UNIT_MM;
|
|
pDesc->constraint_type = SANE_CONSTRAINT_RANGE;
|
|
pDesc->constraint.range = &rangeXmm;
|
|
pDesc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
|
|
pVal->w = rangeXmm.max + offsetX;
|
|
break;
|
|
|
|
case optBRY:
|
|
pDesc->name = SANE_NAME_SCAN_BR_Y;
|
|
pDesc->title = SANE_TITLE_SCAN_BR_Y;
|
|
pDesc->desc = SANE_DESC_SCAN_BR_Y;
|
|
pDesc->unit = SANE_UNIT_MM;
|
|
pDesc->constraint_type = SANE_CONSTRAINT_RANGE;
|
|
pDesc->constraint.range = &rangeYmm;
|
|
pDesc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
|
|
pVal->w = rangeYmm.max + offsetY;
|
|
break;
|
|
|
|
case optDPI:
|
|
pDesc->name = SANE_NAME_SCAN_RESOLUTION;
|
|
pDesc->title = SANE_TITLE_SCAN_RESOLUTION;
|
|
pDesc->desc = SANE_DESC_SCAN_RESOLUTION;
|
|
pDesc->unit = SANE_UNIT_DPI;
|
|
pDesc->constraint_type = SANE_CONSTRAINT_WORD_LIST;
|
|
pDesc->constraint.word_list = setResolutions;
|
|
pDesc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
|
|
pVal->w = setResolutions[1];
|
|
break;
|
|
|
|
case optGroupImage:
|
|
pDesc->title = SANE_I18N("Image");
|
|
pDesc->type = SANE_TYPE_GROUP;
|
|
pDesc->size = 0;
|
|
break;
|
|
|
|
case optGammaTableRed:
|
|
pDesc->name = SANE_NAME_GAMMA_VECTOR_R;
|
|
pDesc->title = SANE_TITLE_GAMMA_VECTOR_R;
|
|
pDesc->desc = SANE_DESC_GAMMA_VECTOR_R;
|
|
pDesc->size = NUM_GAMMA_ENTRIES * sizeof( SANE_Int );
|
|
pDesc->constraint_type = SANE_CONSTRAINT_RANGE;
|
|
pDesc->constraint.range = &rangeGammaTable;
|
|
pDesc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
|
|
pVal->wa = s->aGammaTableR;
|
|
break;
|
|
|
|
case optGammaTableGreen:
|
|
pDesc->name = SANE_NAME_GAMMA_VECTOR_G;
|
|
pDesc->title = SANE_TITLE_GAMMA_VECTOR_G;
|
|
pDesc->desc = SANE_DESC_GAMMA_VECTOR_G;
|
|
pDesc->size = NUM_GAMMA_ENTRIES * sizeof( SANE_Int );
|
|
pDesc->constraint_type = SANE_CONSTRAINT_RANGE;
|
|
pDesc->constraint.range = &rangeGammaTable;
|
|
pDesc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
|
|
pVal->wa = s->aGammaTableG;
|
|
break;
|
|
|
|
case optGammaTableBlue:
|
|
pDesc->name = SANE_NAME_GAMMA_VECTOR_B;
|
|
pDesc->title = SANE_TITLE_GAMMA_VECTOR_B;
|
|
pDesc->desc = SANE_DESC_GAMMA_VECTOR_B;
|
|
pDesc->size = NUM_GAMMA_ENTRIES * sizeof( SANE_Int );
|
|
pDesc->constraint_type = SANE_CONSTRAINT_RANGE;
|
|
pDesc->constraint.range = &rangeGammaTable;
|
|
pDesc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
|
|
pVal->wa = s->aGammaTableB;
|
|
break;
|
|
|
|
case optGroupMisc:
|
|
pDesc->title = SANE_I18N("Miscellaneous");
|
|
pDesc->type = SANE_TYPE_GROUP;
|
|
pDesc->size = 0;
|
|
break;
|
|
|
|
case optOffsetX:
|
|
pDesc->title = SANE_I18N("offset X");
|
|
pDesc->desc = SANE_I18N("Hardware internal X position of the scanning area.");
|
|
pDesc->unit = SANE_UNIT_MM;
|
|
pDesc->constraint_type = SANE_CONSTRAINT_RANGE;
|
|
pDesc->constraint.range = &rangeXoffset;
|
|
pDesc->cap = SANE_CAP_SOFT_SELECT;
|
|
pVal->w = offsetX;
|
|
break;
|
|
|
|
case optOffsetY:
|
|
pDesc->title = SANE_I18N("offset Y");
|
|
pDesc->desc = SANE_I18N("Hardware internal Y position of the scanning area.");
|
|
pDesc->unit = SANE_UNIT_MM;
|
|
pDesc->constraint_type = SANE_CONSTRAINT_RANGE;
|
|
pDesc->constraint.range = &rangeYoffset;
|
|
pDesc->cap = SANE_CAP_SOFT_SELECT;
|
|
pVal->w = offsetY;
|
|
break;
|
|
|
|
|
|
#if 0
|
|
case optLamp:
|
|
pDesc->name = "lamp";
|
|
pDesc->title = SANE_I18N("Lamp status");
|
|
pDesc->desc = SANE_I18N("Switches the lamp on or off.");
|
|
pDesc->type = SANE_TYPE_BOOL;
|
|
pDesc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
|
|
/* switch the lamp on when starting for first the time */
|
|
pVal->w = SANE_TRUE;
|
|
break;
|
|
#endif
|
|
#if 0
|
|
case optCalibrate:
|
|
pDesc->name = "calibrate";
|
|
pDesc->title = SANE_I18N("Calibrate");
|
|
pDesc->desc = SANE_I18N("Calibrates for black and white level.");
|
|
pDesc->type = SANE_TYPE_BUTTON;
|
|
pDesc->cap = SANE_CAP_SOFT_SELECT;
|
|
pDesc->size = 0;
|
|
break;
|
|
#endif
|
|
default:
|
|
HP5400_DBG(DBG_ERR, "Uninitialised option %d\n", i);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
static int _ReportDevice(TScannerModel *pModel, char *pszDeviceName)
|
|
{
|
|
TDevListEntry *pNew, *pDev;
|
|
|
|
HP5400_DBG(DBG_MSG, "hp5400: _ReportDevice '%s'\n", pszDeviceName);
|
|
|
|
pNew = malloc(sizeof(TDevListEntry));
|
|
if (!pNew) {
|
|
HP5400_DBG(DBG_ERR, "no mem\n");
|
|
return -1;
|
|
}
|
|
|
|
/* add new element to the end of the list */
|
|
if (_pFirstSaneDev == NULL) {
|
|
_pFirstSaneDev = pNew;
|
|
}
|
|
else {
|
|
for (pDev = _pFirstSaneDev; pDev->pNext; pDev = pDev->pNext) {
|
|
;
|
|
}
|
|
pDev->pNext = pNew;
|
|
}
|
|
|
|
/* fill in new element */
|
|
pNew->pNext = 0;
|
|
pNew->dev.name = (char*)strdup(pszDeviceName);
|
|
pNew->dev.vendor = pModel->pszVendor;
|
|
pNew->dev.model = pModel->pszName;
|
|
pNew->dev.type = "flatbed scanner";
|
|
|
|
iNumSaneDev++;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static SANE_Status
|
|
attach_one_device (SANE_String_Const devname)
|
|
{
|
|
char * filename = (char*) devname;
|
|
if (HP5400Detect (filename, _ReportDevice) < 0)
|
|
{
|
|
HP5400_DBG (DBG_MSG, "attach_one_device: couldn't attach %s\n", devname);
|
|
return SANE_STATUS_INVAL;
|
|
}
|
|
HP5400_DBG (DBG_MSG, "attach_one_device: attached %s successfully\n", devname);
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
SANE_Status
|
|
sane_init (SANE_Int * piVersion, SANE_Auth_Callback pfnAuth)
|
|
{
|
|
FILE *conf_fp; /* Config file stream */
|
|
SANE_Char line[PATH_MAX];
|
|
SANE_Char *str = NULL;
|
|
SANE_String_Const proper_str;
|
|
int nline = 0;
|
|
|
|
/* prevent compiler from complaing about unused parameters */
|
|
pfnAuth = pfnAuth;
|
|
|
|
strcpy(usb_devfile, "/dev/usb/scanner0");
|
|
_pFirstSaneDev = 0;
|
|
iNumSaneDev = 0;
|
|
|
|
InitHp5400_internal();
|
|
|
|
|
|
DBG_INIT ();
|
|
|
|
HP5400_DBG (DBG_MSG, "sane_init: SANE hp5400 backend version %d.%d-%d (from %s)\n",
|
|
V_MAJOR, V_MINOR, BUILD, PACKAGE_STRING);
|
|
|
|
sanei_usb_init ();
|
|
|
|
conf_fp = sanei_config_open (HP5400_CONFIG_FILE);
|
|
|
|
iNumSaneDev = 0;
|
|
|
|
if (conf_fp)
|
|
{
|
|
HP5400_DBG (DBG_MSG, "Reading config file\n");
|
|
|
|
while (sanei_config_read (line, sizeof (line), conf_fp))
|
|
{
|
|
++nline;
|
|
|
|
if (str)
|
|
{
|
|
free (str);
|
|
}
|
|
|
|
proper_str = sanei_config_get_string (line, &str);
|
|
|
|
/* Discards white lines and comments */
|
|
if (!str || proper_str == line || str[0] == '#')
|
|
{
|
|
HP5400_DBG (DBG_MSG, "Discarding line %d\n", nline);
|
|
}
|
|
else
|
|
{
|
|
/* If line's not blank or a comment, then it's the device
|
|
* filename or a usb directive. */
|
|
HP5400_DBG (DBG_MSG, "Trying to attach %s\n", line);
|
|
sanei_usb_attach_matching_devices (line, attach_one_device);
|
|
}
|
|
} /* while */
|
|
fclose (conf_fp);
|
|
}
|
|
else
|
|
{
|
|
HP5400_DBG (DBG_ERR, "Unable to read config file \"%s\": %s\n",
|
|
HP5400_CONFIG_FILE, strerror (errno));
|
|
HP5400_DBG (DBG_MSG, "Using default built-in values\n");
|
|
attach_one_device (usb_devfile);
|
|
}
|
|
|
|
if (piVersion != NULL)
|
|
{
|
|
*piVersion = SANE_VERSION_CODE (V_MAJOR, V_MINOR, BUILD);
|
|
}
|
|
|
|
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
|
|
|
|
void
|
|
sane_exit (void)
|
|
{
|
|
TDevListEntry *pDev, *pNext;
|
|
|
|
HP5400_DBG (DBG_MSG, "sane_exit\n");
|
|
|
|
/* free device list memory */
|
|
if (_pSaneDevList)
|
|
{
|
|
for (pDev = _pFirstSaneDev; pDev; pDev = pNext)
|
|
{
|
|
pNext = pDev->pNext;
|
|
free ((void *) pDev->dev.name);
|
|
free (pDev);
|
|
}
|
|
_pFirstSaneDev = 0;
|
|
free (_pSaneDevList);
|
|
_pSaneDevList = 0;
|
|
}
|
|
|
|
|
|
FreeHp5400_internal();
|
|
}
|
|
|
|
|
|
SANE_Status
|
|
sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only)
|
|
{
|
|
TDevListEntry *pDev;
|
|
int i;
|
|
|
|
HP5400_DBG (DBG_MSG, "sane_get_devices\n");
|
|
|
|
local_only = local_only;
|
|
|
|
if (_pSaneDevList)
|
|
{
|
|
free (_pSaneDevList);
|
|
}
|
|
|
|
_pSaneDevList = malloc (sizeof (*_pSaneDevList) * (iNumSaneDev + 1));
|
|
if (!_pSaneDevList)
|
|
{
|
|
HP5400_DBG (DBG_MSG, "no mem\n");
|
|
return SANE_STATUS_NO_MEM;
|
|
}
|
|
i = 0;
|
|
for (pDev = _pFirstSaneDev; pDev; pDev = pDev->pNext)
|
|
{
|
|
_pSaneDevList[i++] = &pDev->dev;
|
|
}
|
|
_pSaneDevList[i++] = 0; /* last entry is 0 */
|
|
|
|
*device_list = _pSaneDevList;
|
|
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
|
|
|
|
SANE_Status
|
|
sane_open (SANE_String_Const name, SANE_Handle * h)
|
|
{
|
|
TScanner *s;
|
|
|
|
HP5400_DBG (DBG_MSG, "sane_open: %s\n", name);
|
|
|
|
/* check the name */
|
|
if (strlen (name) == 0)
|
|
{
|
|
/* default to first available device */
|
|
name = _pFirstSaneDev->dev.name;
|
|
}
|
|
|
|
s = malloc (sizeof (TScanner));
|
|
if (!s)
|
|
{
|
|
HP5400_DBG (DBG_MSG, "malloc failed\n");
|
|
return SANE_STATUS_NO_MEM;
|
|
}
|
|
|
|
memset (s, 0, sizeof (TScanner)); /* Clear everything to zero */
|
|
if (HP5400Open (&s->HWParams, (char *) name) < 0)
|
|
{
|
|
/* is this OK ? */
|
|
HP5400_DBG (DBG_ERR, "HP5400Open failed\n");
|
|
free ((void *) s);
|
|
return SANE_STATUS_INVAL; /* is this OK? */
|
|
}
|
|
HP5400_DBG (DBG_MSG, "Handle=%d\n", s->HWParams.iXferHandle);
|
|
_InitOptions (s);
|
|
*h = s;
|
|
|
|
/* Turn on lamp by default at startup */
|
|
/* SetLamp(&s->HWParams, TRUE); */
|
|
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
|
|
|
|
void
|
|
sane_close (SANE_Handle h)
|
|
{
|
|
TScanner *s;
|
|
|
|
HP5400_DBG (DBG_MSG, "sane_close\n");
|
|
|
|
s = (TScanner *) h;
|
|
|
|
/* turn of scanner lamp */
|
|
SetLamp (&s->HWParams, FALSE);
|
|
|
|
/* close scanner */
|
|
HP5400Close (&s->HWParams);
|
|
|
|
/* free scanner object memory */
|
|
free ((void *) s);
|
|
}
|
|
|
|
|
|
const SANE_Option_Descriptor *
|
|
sane_get_option_descriptor (SANE_Handle h, SANE_Int n)
|
|
{
|
|
TScanner *s;
|
|
|
|
HP5400_DBG (DBG_MSG, "sane_get_option_descriptor %d\n", n);
|
|
|
|
if ((n < optCount) || (n >= optLast))
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
s = (TScanner *) h;
|
|
return &s->aOptions[n];
|
|
}
|
|
|
|
|
|
SANE_Status
|
|
sane_control_option (SANE_Handle h, SANE_Int n, SANE_Action Action,
|
|
void *pVal, SANE_Int * pInfo)
|
|
{
|
|
TScanner *s;
|
|
SANE_Int info;
|
|
|
|
HP5400_DBG (DBG_MSG, "sane_control_option: option %d, action %d\n", n, Action);
|
|
|
|
s = (TScanner *) h;
|
|
info = 0;
|
|
|
|
switch (Action)
|
|
{
|
|
case SANE_ACTION_GET_VALUE:
|
|
switch (n)
|
|
{
|
|
|
|
/* Get options of type SANE_Word */
|
|
case optBRX:
|
|
case optTLX:
|
|
*(SANE_Word *) pVal = s->aValues[n].w; /* Not needed anymore - s->aValues[optOffsetX].w; */
|
|
HP5400_DBG (DBG_MSG,
|
|
"sane_control_option: SANE_ACTION_GET_VALUE %d = %d\n", n,
|
|
*(SANE_Word *) pVal);
|
|
break;
|
|
|
|
case optBRY:
|
|
case optTLY:
|
|
*(SANE_Word *) pVal = s->aValues[n].w; /* Not needed anymore - - s->aValues[optOffsetY].w; */
|
|
HP5400_DBG (DBG_MSG,
|
|
"sane_control_option: SANE_ACTION_GET_VALUE %d = %d\n", n,
|
|
*(SANE_Word *) pVal);
|
|
break;
|
|
|
|
case optOffsetX:
|
|
case optOffsetY:
|
|
case optCount:
|
|
case optDPI:
|
|
HP5400_DBG (DBG_MSG,
|
|
"sane_control_option: SANE_ACTION_GET_VALUE %d = %d\n", n,
|
|
(int) s->aValues[n].w);
|
|
*(SANE_Word *) pVal = s->aValues[n].w;
|
|
break;
|
|
|
|
/* Get options of type SANE_Word array */
|
|
case optGammaTableRed:
|
|
case optGammaTableGreen:
|
|
case optGammaTableBlue:
|
|
HP5400_DBG (DBG_MSG, "Reading gamma table\n");
|
|
memcpy (pVal, s->aValues[n].wa, s->aOptions[n].size);
|
|
break;
|
|
|
|
#if 0
|
|
/* Get options of type SANE_Bool */
|
|
case optLamp:
|
|
GetLamp (&s->HWParams, &fLampIsOn);
|
|
*(SANE_Bool *) pVal = fLampIsOn;
|
|
break;
|
|
#endif
|
|
#if 0
|
|
case optCalibrate:
|
|
/* although this option has nothing to read,
|
|
it's added here to avoid a warning when running scanimage --help */
|
|
break;
|
|
#endif
|
|
default:
|
|
HP5400_DBG (DBG_MSG, "SANE_ACTION_GET_VALUE: Invalid option (%d)\n", n);
|
|
}
|
|
break;
|
|
|
|
|
|
case SANE_ACTION_SET_VALUE:
|
|
if (s->fScanning)
|
|
{
|
|
HP5400_DBG (DBG_ERR,
|
|
"sane_control_option: SANE_ACTION_SET_VALUE not allowed during scan\n");
|
|
return SANE_STATUS_INVAL;
|
|
}
|
|
switch (n)
|
|
{
|
|
|
|
case optCount:
|
|
return SANE_STATUS_INVAL;
|
|
break;
|
|
|
|
case optBRX:
|
|
case optTLX:
|
|
info |= SANE_INFO_RELOAD_PARAMS;
|
|
s->ScanParams.iLines = 0; /* Forget actual image settings */
|
|
s->aValues[n].w = *(SANE_Word *) pVal; /* Not needed anymore - + s->aValues[optOffsetX].w; */
|
|
break;
|
|
|
|
case optBRY:
|
|
case optTLY:
|
|
info |= SANE_INFO_RELOAD_PARAMS;
|
|
s->ScanParams.iLines = 0; /* Forget actual image settings */
|
|
s->aValues[n].w = *(SANE_Word *) pVal; /* Not needed anymore - + s->aValues[optOffsetY].w; */
|
|
break;
|
|
case optDPI:
|
|
info |= SANE_INFO_RELOAD_PARAMS;
|
|
s->ScanParams.iLines = 0; /* Forget actual image settings */
|
|
#ifdef SUPPORT_2400_DPI
|
|
(s->aValues[n].w) = *(SANE_Word *) pVal;
|
|
#else
|
|
(s->aValues[n].w) = min (1200, *(SANE_Word *) pVal);
|
|
#endif
|
|
break;
|
|
|
|
case optGammaTableRed:
|
|
case optGammaTableGreen:
|
|
case optGammaTableBlue:
|
|
HP5400_DBG (DBG_MSG, "Writing gamma table\n");
|
|
memcpy (s->aValues[n].wa, pVal, s->aOptions[n].size);
|
|
break;
|
|
/*
|
|
case optLamp:
|
|
fVal = *(SANE_Bool *)pVal;
|
|
HP5400_DBG(DBG_MSG, "lamp %s\n", fVal ? "on" : "off");
|
|
SetLamp(&s->HWParams, fVal);
|
|
break;
|
|
*/
|
|
#if 0
|
|
case optCalibrate:
|
|
/* SimpleCalib(&s->HWParams); */
|
|
break;
|
|
#endif
|
|
default:
|
|
HP5400_DBG (DBG_ERR, "SANE_ACTION_SET_VALUE: Invalid option (%d)\n", n);
|
|
}
|
|
if (pInfo != NULL)
|
|
{
|
|
*pInfo = info;
|
|
}
|
|
break;
|
|
|
|
case SANE_ACTION_SET_AUTO:
|
|
return SANE_STATUS_UNSUPPORTED;
|
|
|
|
|
|
default:
|
|
HP5400_DBG (DBG_ERR, "Invalid action (%d)\n", Action);
|
|
return SANE_STATUS_INVAL;
|
|
}
|
|
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
|
|
|
|
|
|
SANE_Status
|
|
sane_get_parameters (SANE_Handle h, SANE_Parameters * p)
|
|
{
|
|
TScanner *s;
|
|
HP5400_DBG (DBG_MSG, "sane_get_parameters\n");
|
|
|
|
s = (TScanner *) h;
|
|
|
|
/* first do some checks */
|
|
if (s->aValues[optTLX].w >= s->aValues[optBRX].w)
|
|
{
|
|
HP5400_DBG (DBG_ERR, "TLX should be smaller than BRX\n");
|
|
return SANE_STATUS_INVAL; /* proper error code? */
|
|
}
|
|
if (s->aValues[optTLY].w >= s->aValues[optBRY].w)
|
|
{
|
|
HP5400_DBG (DBG_ERR, "TLY should be smaller than BRY\n");
|
|
return SANE_STATUS_INVAL; /* proper error code? */
|
|
}
|
|
|
|
/* return the data */
|
|
p->format = SANE_FRAME_RGB;
|
|
p->last_frame = SANE_TRUE;
|
|
|
|
p->depth = 8;
|
|
if (s->ScanParams.iLines) /* Initialised by doing a scan */
|
|
{
|
|
p->pixels_per_line = s->ScanParams.iBytesPerLine / 3;
|
|
p->lines = s->ScanParams.iLines;
|
|
p->bytes_per_line = s->ScanParams.iBytesPerLine;
|
|
}
|
|
else
|
|
{
|
|
p->lines = MM_TO_PIXEL (s->aValues[optBRY].w - s->aValues[optTLY].w,
|
|
s->aValues[optDPI].w);
|
|
p->pixels_per_line =
|
|
MM_TO_PIXEL (s->aValues[optBRX].w - s->aValues[optTLX].w,
|
|
s->aValues[optDPI].w);
|
|
p->bytes_per_line = p->pixels_per_line * 3;
|
|
}
|
|
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
|
|
#define BUFFER_READ_HEADER_SIZE 32
|
|
|
|
SANE_Status
|
|
sane_start (SANE_Handle h)
|
|
{
|
|
TScanner *s;
|
|
SANE_Parameters par;
|
|
|
|
HP5400_DBG (DBG_MSG, "sane_start\n");
|
|
|
|
s = (TScanner *) h;
|
|
|
|
if (sane_get_parameters (h, &par) != SANE_STATUS_GOOD)
|
|
{
|
|
HP5400_DBG (DBG_MSG, "Invalid scan parameters (sane_get_parameters)\n");
|
|
return SANE_STATUS_INVAL;
|
|
}
|
|
s->iLinesLeft = par.lines;
|
|
|
|
/* fill in the scanparams using the option values */
|
|
s->ScanParams.iDpi = s->aValues[optDPI].w;
|
|
s->ScanParams.iLpi = s->aValues[optDPI].w;
|
|
|
|
/* Guessing here. 75dpi => 1, 2400dpi => 32 */
|
|
/* s->ScanParams.iColourOffset = s->aValues[optDPI].w / 75; */
|
|
/* now we don't need correction => corrected by scan request type ? */
|
|
s->ScanParams.iColourOffset = 0;
|
|
|
|
s->ScanParams.iTop =
|
|
MM_TO_PIXEL (s->aValues[optTLY].w + s->HWParams.iTopLeftY, HW_LPI);
|
|
s->ScanParams.iLeft =
|
|
MM_TO_PIXEL (s->aValues[optTLX].w + s->HWParams.iTopLeftX, HW_DPI);
|
|
|
|
/* Note: All measurements passed to the scanning routines must be in HW_LPI */
|
|
s->ScanParams.iWidth =
|
|
MM_TO_PIXEL (s->aValues[optBRX].w - s->aValues[optTLX].w, HW_LPI);
|
|
s->ScanParams.iHeight =
|
|
MM_TO_PIXEL (s->aValues[optBRY].w - s->aValues[optTLY].w, HW_LPI);
|
|
|
|
/* After the scanning, the iLines and iBytesPerLine will be filled in */
|
|
|
|
/* copy gamma table */
|
|
WriteGammaCalibTable (s->HWParams.iXferHandle, s->aGammaTableR,
|
|
s->aGammaTableG, s->aGammaTableB);
|
|
|
|
/* prepare the actual scan */
|
|
/* We say normal here. In future we should have a preview flag to set preview mode */
|
|
if (InitScan (SCAN_TYPE_NORMAL, &s->ScanParams, &s->HWParams) != 0)
|
|
{
|
|
HP5400_DBG (DBG_MSG, "Invalid scan parameters (InitScan)\n");
|
|
return SANE_STATUS_INVAL;
|
|
}
|
|
|
|
/* for the moment no lines has been read */
|
|
s->ScanParams.iLinesRead = 0;
|
|
|
|
s->fScanning = TRUE;
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
|
|
|
|
SANE_Status
|
|
sane_read (SANE_Handle h, SANE_Byte * buf, SANE_Int maxlen, SANE_Int * len)
|
|
{
|
|
|
|
/* Read actual scan from the circular buffer */
|
|
/* Note: this is already color corrected, though some work still needs to be done
|
|
to deal with the colour offsetting */
|
|
TScanner *s;
|
|
char *buffer = (char*)buf;
|
|
|
|
HP5400_DBG (DBG_MSG, "sane_read: request %d bytes \n", maxlen);
|
|
|
|
s = (TScanner *) h;
|
|
|
|
/* nothing has been read for the moment */
|
|
*len = 0;
|
|
|
|
|
|
/* if we read all the lines return EOF */
|
|
if (s->ScanParams.iLinesRead == s->ScanParams.iLines)
|
|
{
|
|
/* FinishScan( &s->HWParams ); *** FinishScan called in sane_cancel */
|
|
HP5400_DBG (DBG_MSG, "sane_read: EOF\n");
|
|
return SANE_STATUS_EOF;
|
|
}
|
|
|
|
/* read as many lines the buffer may contain and while there are lines to be read */
|
|
while ((*len + s->ScanParams.iBytesPerLine <= maxlen)
|
|
&& (s->ScanParams.iLinesRead < s->ScanParams.iLines))
|
|
{
|
|
|
|
/* get one more line from the circular buffer */
|
|
CircBufferGetLine (s->HWParams.iXferHandle, &s->HWParams.pipe, buffer);
|
|
|
|
/* increment pointer, size and line number */
|
|
buffer += s->ScanParams.iBytesPerLine;
|
|
*len += s->ScanParams.iBytesPerLine;
|
|
s->ScanParams.iLinesRead++;
|
|
}
|
|
|
|
HP5400_DBG (DBG_MSG, "sane_read: %d bytes read\n", *len);
|
|
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
|
|
|
|
void
|
|
sane_cancel (SANE_Handle h)
|
|
{
|
|
TScanner *s;
|
|
|
|
HP5400_DBG (DBG_MSG, "sane_cancel\n");
|
|
|
|
s = (TScanner *) h;
|
|
|
|
/* to be implemented more thoroughly */
|
|
|
|
/* Make sure the scanner head returns home */
|
|
FinishScan (&s->HWParams);
|
|
|
|
s->fCanceled = TRUE;
|
|
s->fScanning = FALSE;
|
|
}
|
|
|
|
|
|
SANE_Status
|
|
sane_set_io_mode (SANE_Handle h, SANE_Bool m)
|
|
{
|
|
HP5400_DBG (DBG_MSG, "sane_set_io_mode %s\n", m ? "non-blocking" : "blocking");
|
|
|
|
/* prevent compiler from complaining about unused parameters */
|
|
h = h;
|
|
|
|
if (m)
|
|
{
|
|
return SANE_STATUS_UNSUPPORTED;
|
|
}
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
|
|
|
|
SANE_Status
|
|
sane_get_select_fd (SANE_Handle h, SANE_Int * fd)
|
|
{
|
|
HP5400_DBG (DBG_MSG, "sane_select_fd\n");
|
|
|
|
/* prevent compiler from complaining about unused parameters */
|
|
h = h;
|
|
fd = fd;
|
|
|
|
return SANE_STATUS_UNSUPPORTED;
|
|
}
|