kopia lustrzana https://gitlab.com/sane-project/backends
1538 wiersze
38 KiB
C
1538 wiersze
38 KiB
C
/* SANE - Scanner Access Now Easy.
|
|
|
|
Copyright (C) 2007-2008 Nicolas Martin, <nicols-guest at alioth dot debian dot org>
|
|
Copyright (C) 2006-2007 Wittawat Yamwong <wittawat@web.de>
|
|
|
|
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.
|
|
*/
|
|
# include "../include/sane/config.h"
|
|
|
|
#include <errno.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#ifdef USE_PTHREAD
|
|
# include <pthread.h>
|
|
#endif
|
|
#include <signal.h> /* sigaction(POSIX) */
|
|
#include <unistd.h> /* POSIX: write read close pipe */
|
|
#ifdef HAVE_FCNTL_H
|
|
# include <fcntl.h>
|
|
#endif
|
|
|
|
#include "pixma_rename.h"
|
|
#include "pixma.h"
|
|
|
|
# define DEBUG_NOT_STATIC
|
|
# include "../include/sane/sane.h"
|
|
# include "../include/sane/sanei.h"
|
|
# include "../include/sane/saneopts.h"
|
|
# include "../include/sane/sanei_thread.h"
|
|
# include "../include/sane/sanei_backend.h"
|
|
# include "../include/sane/sanei_config.h"
|
|
|
|
#ifdef NDEBUG
|
|
# define PDBG(x)
|
|
#else
|
|
# define PDBG(x) IF_DBG(x)
|
|
#endif /* NDEBUG */
|
|
|
|
#ifdef __GNUC__
|
|
# define UNUSED(v) (void) v
|
|
#else
|
|
# define UNUSED(v)
|
|
#endif
|
|
|
|
#define DECL_CTX pixma_sane_t *ss = check_handle(h)
|
|
#define OPT_IN_CTX ss->opt
|
|
#define SOD(opt) OPT_IN_CTX[opt].sod
|
|
#define OVAL(opt) OPT_IN_CTX[opt].val
|
|
#define AUTO_GAMMA 2.2
|
|
|
|
#include "pixma_sane_options.h" /* generated by gen_options.py */
|
|
|
|
|
|
typedef struct pixma_sane_t
|
|
{
|
|
struct pixma_sane_t *next;
|
|
pixma_t *s;
|
|
pixma_scan_param_t sp;
|
|
SANE_Bool cancel;
|
|
|
|
/* valid states: idle, !idle && scanning, !idle && !scanning */
|
|
SANE_Bool idle;
|
|
SANE_Bool scanning;
|
|
SANE_Status last_read_status; /* valid if !idle && !scanning */
|
|
|
|
option_descriptor_t opt[opt_last];
|
|
SANE_Range xrange, yrange;
|
|
SANE_Word dpi_list[9]; /* up to 9600 dpi */
|
|
SANE_String_Const mode_list[3];
|
|
uint8_t gamma_table[4096];
|
|
SANE_String_Const source_list[4];
|
|
pixma_paper_source_t source_map[4];
|
|
|
|
unsigned byte_pos_in_line, output_line_size;
|
|
unsigned image_bytes_read;
|
|
unsigned page_count; /* valid for ADF */
|
|
|
|
SANE_Pid reader_taskid;
|
|
int wpipe, rpipe;
|
|
SANE_Bool reader_stop;
|
|
} pixma_sane_t;
|
|
|
|
|
|
static const char vendor_str[] = "CANON";
|
|
static const char type_str[] = "multi-function peripheral";
|
|
|
|
static pixma_sane_t *first_scanner = NULL;
|
|
static const SANE_Device **dev_list = NULL;
|
|
static const char* conf_devices[MAX_CONF_DEVICES];
|
|
|
|
static SANE_Status config_attach_pixma(SANEI_Config * config, const char *devname)
|
|
{
|
|
int i;
|
|
UNUSED(config);
|
|
for (i=0; i < (MAX_CONF_DEVICES -1); i++)
|
|
{
|
|
if(conf_devices[i] == NULL)
|
|
{
|
|
conf_devices[i] = strdup(devname);
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
}
|
|
return SANE_STATUS_INVAL;
|
|
}
|
|
|
|
static SANE_Status
|
|
map_error (int error)
|
|
{
|
|
if (error >= 0)
|
|
return SANE_STATUS_GOOD;
|
|
|
|
switch (error)
|
|
{
|
|
case PIXMA_ENOMEM:
|
|
return SANE_STATUS_NO_MEM;
|
|
case PIXMA_ECANCELED:
|
|
return SANE_STATUS_CANCELLED;
|
|
case PIXMA_EBUSY:
|
|
return SANE_STATUS_DEVICE_BUSY;
|
|
case PIXMA_EINVAL:
|
|
return SANE_STATUS_INVAL;
|
|
case PIXMA_EACCES:
|
|
return SANE_STATUS_ACCESS_DENIED;
|
|
case PIXMA_EPAPER_JAMMED:
|
|
return SANE_STATUS_JAMMED;
|
|
case PIXMA_ENO_PAPER:
|
|
return SANE_STATUS_NO_DOCS;
|
|
case PIXMA_ECOVER_OPEN:
|
|
return SANE_STATUS_COVER_OPEN;
|
|
case PIXMA_ENOTSUP:
|
|
return SANE_STATUS_UNSUPPORTED;
|
|
case PIXMA_EPROTO:
|
|
case PIXMA_ENODEV:
|
|
case PIXMA_EIO:
|
|
case PIXMA_ETIMEDOUT:
|
|
return SANE_STATUS_IO_ERROR;
|
|
}
|
|
PDBG (pixma_dbg (1, "BUG: unmapped error %d\n", error));
|
|
return SANE_STATUS_IO_ERROR;
|
|
}
|
|
|
|
static int
|
|
getenv_atoi (const char *name, int def)
|
|
{
|
|
const char *str = getenv (name);
|
|
return (str) ? atoi (str) : def;
|
|
}
|
|
|
|
#define CONST_CAST(t,x) (t)(x)
|
|
|
|
static void
|
|
free_block (const void * ptr)
|
|
{
|
|
free (CONST_CAST (void *, ptr));
|
|
}
|
|
|
|
static void
|
|
cleanup_device_list (void)
|
|
{
|
|
if (dev_list)
|
|
{
|
|
int i;
|
|
for (i = 0; dev_list[i]; i++)
|
|
{
|
|
free_block ((const void *) dev_list[i]->name);
|
|
free_block ((const void *) dev_list[i]->model);
|
|
free_block ((const void *) dev_list[i]);
|
|
}
|
|
}
|
|
free (dev_list);
|
|
dev_list = NULL;
|
|
}
|
|
|
|
static void
|
|
find_scanners (void)
|
|
{
|
|
unsigned i, nscanners;
|
|
|
|
cleanup_device_list ();
|
|
nscanners = pixma_find_scanners (conf_devices);
|
|
PDBG (pixma_dbg (3, "pixma_find_scanners() found %u devices\n", nscanners));
|
|
dev_list =
|
|
(const SANE_Device **) calloc (nscanners + 1, sizeof (*dev_list));
|
|
if (!dev_list)
|
|
return;
|
|
for (i = 0; i != nscanners; i++)
|
|
{
|
|
SANE_Device *sdev = (SANE_Device *) calloc (1, sizeof (*sdev));
|
|
char *name, *model;
|
|
if (!sdev)
|
|
goto nomem;
|
|
name = strdup (pixma_get_device_id (i));
|
|
model = strdup (pixma_get_device_model (i));
|
|
if (!name || !model)
|
|
{
|
|
free (name);
|
|
free (model);
|
|
free (sdev);
|
|
goto nomem;
|
|
}
|
|
sdev->name = name;
|
|
sdev->model = model;
|
|
sdev->vendor = vendor_str;
|
|
sdev->type = type_str;
|
|
dev_list[i] = sdev;
|
|
}
|
|
/* dev_list is already NULL terminated by calloc(). */
|
|
return;
|
|
|
|
nomem:
|
|
PDBG (pixma_dbg (1, "WARNING:not enough memory for device list\n"));
|
|
return;
|
|
}
|
|
|
|
static pixma_sane_t *
|
|
check_handle (SANE_Handle h)
|
|
{
|
|
pixma_sane_t *p;
|
|
|
|
for (p = first_scanner; p && (SANE_Handle) p != h; p = p->next)
|
|
{
|
|
}
|
|
return p;
|
|
}
|
|
|
|
static void
|
|
update_button_state (pixma_sane_t * ss, SANE_Int * info)
|
|
{
|
|
SANE_Int b1 = OVAL (opt_button_1).w;
|
|
SANE_Int b2 = OVAL (opt_button_2).w;
|
|
uint32_t ev = pixma_wait_event (ss->s, 300);
|
|
switch (ev & ~PIXMA_EV_ACTION_MASK)
|
|
{
|
|
case PIXMA_EV_BUTTON1:
|
|
b1 = (ev & PIXMA_EV_ACTION_MASK) + 1;
|
|
break;
|
|
case PIXMA_EV_BUTTON2:
|
|
b2 = (ev & PIXMA_EV_ACTION_MASK) + 1;
|
|
break;
|
|
}
|
|
if (b1 != OVAL (opt_button_1).w || b2 != OVAL (opt_button_2).w)
|
|
*info |= SANE_INFO_RELOAD_OPTIONS;
|
|
OVAL (opt_button_1).w = b1;
|
|
OVAL (opt_button_2).w = b2;
|
|
}
|
|
|
|
static SANE_Bool
|
|
enable_option (pixma_sane_t * ss, SANE_Int o, SANE_Bool enable)
|
|
{
|
|
SANE_Word save = SOD (o).cap;
|
|
if (enable)
|
|
SOD (o).cap &= ~SANE_CAP_INACTIVE;
|
|
else
|
|
SOD (o).cap |= SANE_CAP_INACTIVE;
|
|
return (save != SOD (o).cap);
|
|
}
|
|
|
|
static void
|
|
clamp_value (pixma_sane_t * ss, SANE_Int n, void *v, SANE_Int * info)
|
|
{
|
|
SANE_Option_Descriptor *sod = &SOD (n);
|
|
SANE_Word *va = (SANE_Word *) v;
|
|
const SANE_Range *range = sod->constraint.range;
|
|
int i, nmemb;
|
|
|
|
nmemb = sod->size / sizeof (SANE_Word);
|
|
for (i = 0; i < nmemb; i++)
|
|
{
|
|
SANE_Word value = va[i];
|
|
if (value < range->min)
|
|
{
|
|
value = range->min;
|
|
}
|
|
else if (value > range->max)
|
|
{
|
|
value = range->max;
|
|
}
|
|
if (range->quant != 0)
|
|
{
|
|
value = (value - range->min + range->quant / 2) /
|
|
range->quant * range->quant;
|
|
}
|
|
if (value != va[i])
|
|
{
|
|
va[i] = value;
|
|
*info |= SANE_INFO_INEXACT;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
select_value_from_list (pixma_sane_t * ss, SANE_Int n, void *v,
|
|
SANE_Int * info)
|
|
{
|
|
SANE_Option_Descriptor *sod = &SOD (n);
|
|
SANE_Word *va = (SANE_Word *) v;
|
|
const SANE_Word *list = sod->constraint.word_list;
|
|
int i, j, nmemb;
|
|
|
|
nmemb = sod->size / sizeof (SANE_Word);
|
|
for (i = 0; i < nmemb; i++)
|
|
{
|
|
SANE_Word value = va[i];
|
|
SANE_Word mindelta = abs (value - list[1]);
|
|
SANE_Word nearest = list[1];
|
|
for (j = 2; j <= list[0]; j++)
|
|
{
|
|
SANE_Word delta = abs (value - list[j]);
|
|
if (delta < mindelta)
|
|
{
|
|
mindelta = delta;
|
|
nearest = list[j];
|
|
}
|
|
if (mindelta == 0)
|
|
break;
|
|
}
|
|
if (va[i] != nearest)
|
|
{
|
|
va[i] = nearest;
|
|
*info |= SANE_INFO_INEXACT;
|
|
}
|
|
}
|
|
}
|
|
|
|
static SANE_Status
|
|
control_scalar_option (pixma_sane_t * ss, SANE_Int n, SANE_Action a, void *v,
|
|
SANE_Int * info)
|
|
{
|
|
option_descriptor_t *opt = &(OPT_IN_CTX[n]);
|
|
SANE_Word val;
|
|
|
|
switch (a)
|
|
{
|
|
case SANE_ACTION_GET_VALUE:
|
|
switch (opt->sod.type)
|
|
{
|
|
case SANE_TYPE_BOOL:
|
|
case SANE_TYPE_INT:
|
|
case SANE_TYPE_FIXED:
|
|
*(SANE_Word *) v = opt->val.w;
|
|
break;
|
|
default:
|
|
return SANE_STATUS_UNSUPPORTED;
|
|
}
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case SANE_ACTION_SET_VALUE:
|
|
switch (opt->sod.type)
|
|
{
|
|
case SANE_TYPE_BOOL:
|
|
val = *(SANE_Word *) v;
|
|
if (val != SANE_TRUE && val != SANE_FALSE)
|
|
return SANE_STATUS_INVAL;
|
|
opt->val.w = val;
|
|
break;
|
|
case SANE_TYPE_INT:
|
|
case SANE_TYPE_FIXED:
|
|
if (opt->sod.constraint_type == SANE_CONSTRAINT_RANGE)
|
|
clamp_value (ss, n, v, info);
|
|
else if (opt->sod.constraint_type == SANE_CONSTRAINT_WORD_LIST)
|
|
select_value_from_list (ss, n, v, info);
|
|
opt->val.w = *(SANE_Word *) v;
|
|
break;
|
|
default:
|
|
return SANE_STATUS_UNSUPPORTED;
|
|
}
|
|
*info |= opt->info;
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case SANE_ACTION_SET_AUTO:
|
|
switch (opt->sod.type)
|
|
{
|
|
case SANE_TYPE_BOOL:
|
|
case SANE_TYPE_INT:
|
|
case SANE_TYPE_FIXED:
|
|
opt->val.w = opt->def.w;
|
|
break;
|
|
default:
|
|
return SANE_STATUS_UNSUPPORTED;
|
|
}
|
|
*info |= opt->info;
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
return SANE_STATUS_UNSUPPORTED;
|
|
}
|
|
|
|
static SANE_Status
|
|
control_string_option (pixma_sane_t * ss, SANE_Int n, SANE_Action a, void *v,
|
|
SANE_Int * info)
|
|
{
|
|
option_descriptor_t *opt = &(OPT_IN_CTX[n]);
|
|
const SANE_String_Const *slist = opt->sod.constraint.string_list;
|
|
SANE_String str = (SANE_String) v;
|
|
int i;
|
|
|
|
if (opt->sod.constraint_type == SANE_CONSTRAINT_NONE)
|
|
{
|
|
switch (a)
|
|
{
|
|
case SANE_ACTION_GET_VALUE:
|
|
strcpy (str, opt->val.s);
|
|
break;
|
|
case SANE_ACTION_SET_AUTO:
|
|
str = opt->def.s;
|
|
/* fall through */
|
|
case SANE_ACTION_SET_VALUE:
|
|
strncpy (opt->val.s, str, opt->sod.size - 1);
|
|
*info |= opt->info;
|
|
break;
|
|
}
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
else
|
|
{
|
|
switch (a)
|
|
{
|
|
case SANE_ACTION_GET_VALUE:
|
|
strcpy (str, slist[opt->val.w]);
|
|
break;
|
|
case SANE_ACTION_SET_AUTO:
|
|
str = opt->def.ptr;
|
|
/* fall through */
|
|
case SANE_ACTION_SET_VALUE:
|
|
i = 0;
|
|
while (slist[i] && strcasecmp (str, slist[i]) != 0)
|
|
i++;
|
|
if (!slist[i])
|
|
return SANE_STATUS_INVAL;
|
|
if (strcmp (slist[i], str) != 0)
|
|
{
|
|
strcpy (str, slist[i]);
|
|
*info |= SANE_INFO_INEXACT;
|
|
}
|
|
opt->val.w = i;
|
|
*info |= opt->info;
|
|
break;
|
|
}
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
}
|
|
|
|
static SANE_Status
|
|
control_option (pixma_sane_t * ss, SANE_Int n,
|
|
SANE_Action a, void *v, SANE_Int * info)
|
|
{
|
|
int result, i;
|
|
|
|
result = SANE_STATUS_UNSUPPORTED;
|
|
switch (n)
|
|
{
|
|
case opt_gamma_table:
|
|
switch (a)
|
|
{
|
|
case SANE_ACTION_SET_VALUE:
|
|
clamp_value (ss, n, v, info);
|
|
for (i = 0; i != 4096; i++)
|
|
ss->gamma_table[i] = *((SANE_Int *) v + i);
|
|
break;
|
|
case SANE_ACTION_GET_VALUE:
|
|
for (i = 0; i != 4096; i++)
|
|
*((SANE_Int *) v + i) = ss->gamma_table[i];
|
|
break;
|
|
case SANE_ACTION_SET_AUTO:
|
|
pixma_fill_gamma_table (AUTO_GAMMA, ss->gamma_table,
|
|
sizeof (ss->gamma_table));
|
|
break;
|
|
default:
|
|
return SANE_STATUS_UNSUPPORTED;
|
|
}
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case opt_button_update:
|
|
if (a == SANE_ACTION_SET_VALUE)
|
|
{
|
|
update_button_state (ss, info);
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
else
|
|
{
|
|
return SANE_STATUS_INVAL;
|
|
}
|
|
break;
|
|
}
|
|
|
|
switch (SOD (n).type)
|
|
{
|
|
case SANE_TYPE_BOOL:
|
|
case SANE_TYPE_INT:
|
|
case SANE_TYPE_FIXED:
|
|
result = control_scalar_option (ss, n, a, v, info);
|
|
break;
|
|
case SANE_TYPE_STRING:
|
|
result = control_string_option (ss, n, a, v, info);
|
|
break;
|
|
case SANE_TYPE_BUTTON:
|
|
case SANE_TYPE_GROUP:
|
|
PDBG (pixma_dbg (1, "BUG:control_option():Unhandled option\n"));
|
|
result = SANE_STATUS_INVAL;
|
|
break;
|
|
}
|
|
if (result != SANE_STATUS_GOOD)
|
|
return result;
|
|
|
|
switch (n)
|
|
{
|
|
case opt_custom_gamma:
|
|
if (a == SANE_ACTION_SET_VALUE || a == SANE_ACTION_SET_AUTO)
|
|
{
|
|
if (enable_option (ss, opt_gamma_table, OVAL (opt_custom_gamma).b))
|
|
*info |= SANE_INFO_RELOAD_OPTIONS;
|
|
}
|
|
break;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
#ifndef NDEBUG
|
|
static void
|
|
print_scan_param (int level, const pixma_scan_param_t * sp)
|
|
{
|
|
pixma_dbg (level, "Scan parameters\n");
|
|
pixma_dbg (level, " line_size=%u image_size=%u channels=%u depth=%u\n",
|
|
sp->line_size, sp->image_size, sp->channels, sp->depth);
|
|
pixma_dbg (level, " dpi=%ux%u offset=(%u,%u) dimension=%ux%u\n",
|
|
sp->xdpi, sp->ydpi, sp->x, sp->y, sp->w, sp->h);
|
|
pixma_dbg (level, " gamma_table=%p source=%d\n", sp->gamma_table,
|
|
sp->source);
|
|
}
|
|
#endif
|
|
|
|
static int
|
|
calc_scan_param (pixma_sane_t * ss, pixma_scan_param_t * sp)
|
|
{
|
|
int x1, y1, x2, y2;
|
|
int error;
|
|
|
|
memset (sp, 0, sizeof (*sp));
|
|
|
|
sp->channels = (OVAL (opt_mode).w == 0) ? 3 : 1;
|
|
sp->xdpi = sp->ydpi = OVAL (opt_resolution).w;
|
|
|
|
#define PIXEL(x,dpi) (int)((SANE_UNFIX(x) / 25.4 * (dpi)) + 0.5)
|
|
x1 = PIXEL (OVAL (opt_tl_x).w, sp->xdpi);
|
|
x2 = PIXEL (OVAL (opt_br_x).w, sp->xdpi);
|
|
if (x2 < x1)
|
|
{
|
|
int temp = x1;
|
|
x1 = x2;
|
|
x2 = temp;
|
|
}
|
|
y1 = PIXEL (OVAL (opt_tl_y).w, sp->ydpi);
|
|
y2 = PIXEL (OVAL (opt_br_y).w, sp->ydpi);
|
|
if (y2 < y1)
|
|
{
|
|
int temp = y1;
|
|
y1 = y2;
|
|
y2 = temp;
|
|
}
|
|
#undef PIXEL
|
|
sp->x = x1;
|
|
sp->y = y1;
|
|
sp->w = x2 - x1;
|
|
sp->h = y2 - y1;
|
|
if (sp->w == 0)
|
|
sp->w = 1;
|
|
if (sp->h == 0)
|
|
sp->h = 1;
|
|
|
|
sp->gamma_table = (OVAL (opt_custom_gamma).b) ? ss->gamma_table : NULL;
|
|
sp->source = ss->source_map[OVAL (opt_source).w];
|
|
sp->adf_pageid = ss->page_count;
|
|
|
|
error = pixma_check_scan_param (ss->s, sp);
|
|
if (error < 0)
|
|
{
|
|
PDBG (pixma_dbg (1, "BUG:calc_scan_param() failed %d\n", error));
|
|
PDBG (print_scan_param (1, sp));
|
|
}
|
|
return error;
|
|
}
|
|
|
|
static void
|
|
init_option_descriptors (pixma_sane_t * ss)
|
|
{
|
|
const pixma_config_t *cfg;
|
|
int i;
|
|
|
|
cfg = pixma_get_config (ss->s);
|
|
|
|
/* setup range for the scan area. */
|
|
ss->xrange.min = SANE_FIX (0);
|
|
ss->xrange.max = SANE_FIX (cfg->width / 75.0 * 25.4);
|
|
ss->xrange.quant = SANE_FIX (0);
|
|
|
|
ss->yrange.min = SANE_FIX (0);
|
|
ss->yrange.max = SANE_FIX (cfg->height / 75.0 * 25.4);
|
|
ss->yrange.quant = SANE_FIX (0);
|
|
|
|
/* setup dpi up to the value supported by the scanner. */
|
|
i = 0;
|
|
do
|
|
{
|
|
i++;
|
|
ss->dpi_list[i] = 75 * (1 << (i - 1)); /* 75 x 2^(i-1) */
|
|
}
|
|
while ((unsigned) ss->dpi_list[i] != cfg->xdpi);
|
|
ss->dpi_list[0] = i;
|
|
|
|
/* mode_list and source_list were already NULL-terminated,
|
|
* because the whole pixma_sane_t was cleared during allocation. */
|
|
|
|
/* setup available mode. */
|
|
ss->mode_list[0] = SANE_VALUE_SCAN_MODE_COLOR;
|
|
if (cfg->cap & PIXMA_CAP_GRAY)
|
|
{
|
|
ss->mode_list[1] = SANE_VALUE_SCAN_MODE_GRAY;
|
|
}
|
|
|
|
/* setup paper source */
|
|
i = 0;
|
|
ss->source_list[i] = SANE_I18N ("Flatbed");
|
|
ss->source_map[i] = PIXMA_SOURCE_FLATBED;
|
|
i++;
|
|
if (cfg->cap & PIXMA_CAP_ADF)
|
|
{
|
|
ss->source_list[i] = SANE_I18N ("Automatic Document Feeder");
|
|
ss->source_map[i] = PIXMA_SOURCE_ADF;
|
|
i++;
|
|
}
|
|
if ((cfg->cap & PIXMA_CAP_ADFDUP) == PIXMA_CAP_ADFDUP)
|
|
{
|
|
ss->source_list[i] = SANE_I18N ("ADF Duplex");
|
|
ss->source_map[i] = PIXMA_SOURCE_ADFDUP;
|
|
i++;
|
|
}
|
|
if (cfg->cap & PIXMA_CAP_TPU)
|
|
{
|
|
ss->source_list[i] = SANE_I18N ("Transparency Unit");
|
|
ss->source_map[i] = PIXMA_SOURCE_TPU;
|
|
i++;
|
|
}
|
|
|
|
build_option_descriptors (ss);
|
|
|
|
/* Enable options that are available only in some scanners. */
|
|
if (cfg->cap & PIXMA_CAP_GAMMA_TABLE)
|
|
{
|
|
enable_option (ss, opt_custom_gamma, SANE_TRUE);
|
|
sane_control_option (ss, opt_custom_gamma, SANE_ACTION_SET_AUTO,
|
|
NULL, NULL);
|
|
pixma_fill_gamma_table (AUTO_GAMMA, ss->gamma_table, 4096);
|
|
}
|
|
enable_option (ss, opt_button_controlled,
|
|
((cfg->cap & PIXMA_CAP_EVENTS) != 0));
|
|
}
|
|
|
|
/* Writing to reader_ss outside reader_process() is a BUG! */
|
|
static pixma_sane_t *reader_ss = NULL;
|
|
|
|
static RETSIGTYPE
|
|
reader_signal_handler (int sig)
|
|
{
|
|
if (reader_ss)
|
|
{
|
|
reader_ss->reader_stop = SANE_TRUE;
|
|
/* reader process is ended by SIGTERM, so no cancel in this case */
|
|
if (sig != SIGTERM)
|
|
pixma_cancel (reader_ss->s);
|
|
}
|
|
}
|
|
|
|
static int
|
|
write_all (pixma_sane_t * ss, void *buf_, size_t size)
|
|
{
|
|
uint8_t *buf = (uint8_t *) buf_;
|
|
int count;
|
|
|
|
while (size != 0 && !ss->reader_stop)
|
|
{
|
|
count = write (ss->wpipe, buf, size);
|
|
if (count == -1 && errno != EINTR)
|
|
break;
|
|
if (count == -1 && errno == EINTR)
|
|
continue;
|
|
buf += count;
|
|
size -= count;
|
|
}
|
|
return buf - (uint8_t *) buf_;
|
|
}
|
|
|
|
/* NOTE: reader_loop() runs either in a separate thread or process. */
|
|
static SANE_Status
|
|
reader_loop (pixma_sane_t * ss)
|
|
{
|
|
void *buf;
|
|
unsigned bufsize;
|
|
int count = 0;
|
|
|
|
PDBG (pixma_dbg (3, "Reader task started\n"));
|
|
/*bufsize = ss->sp.line_size + 1;*/ /* XXX: "odd" bufsize for testing pixma_read_image() */
|
|
bufsize = ss->sp.line_size; /* bufsize EVEN needed by Xsane for 48 bits depth */
|
|
buf = malloc (bufsize);
|
|
if (!buf)
|
|
{
|
|
count = PIXMA_ENOMEM;
|
|
goto done;
|
|
}
|
|
|
|
count = pixma_activate_connection (ss->s);
|
|
if (count < 0)
|
|
goto done;
|
|
|
|
pixma_enable_background (ss->s, 1);
|
|
if (OVAL (opt_button_controlled).b && ss->page_count == 0)
|
|
{
|
|
int start = 0;
|
|
#ifndef NDEBUG
|
|
pixma_dbg (1, "==== Button-controlled scan mode is enabled.\n");
|
|
pixma_dbg (1, "==== To proceed, press 'SCAN' or 'COLOR' button. "
|
|
"To cancel, press 'GRAY' button.\n");
|
|
#endif
|
|
while (pixma_wait_event (ss->s, 10) != 0)
|
|
{
|
|
}
|
|
while (!start)
|
|
{
|
|
uint32_t events;
|
|
if (ss->reader_stop)
|
|
{
|
|
count = PIXMA_ECANCELED;
|
|
goto done;
|
|
}
|
|
events = pixma_wait_event (ss->s, 1000);
|
|
switch (events & ~PIXMA_EV_ACTION_MASK)
|
|
{
|
|
case PIXMA_EV_BUTTON1:
|
|
start = 1;
|
|
break;
|
|
case PIXMA_EV_BUTTON2:
|
|
count = PIXMA_ECANCELED;
|
|
goto done;
|
|
}
|
|
}
|
|
}
|
|
count = pixma_scan (ss->s, &ss->sp);
|
|
if (count >= 0)
|
|
{
|
|
while ((count = pixma_read_image (ss->s, buf, bufsize)) > 0)
|
|
{
|
|
if (write_all (ss, buf, count) != count)
|
|
pixma_cancel (ss->s);
|
|
}
|
|
}
|
|
|
|
done:
|
|
pixma_enable_background (ss->s, 0);
|
|
pixma_deactivate_connection (ss->s);
|
|
free (buf);
|
|
close (ss->wpipe);
|
|
ss->wpipe = -1;
|
|
if (count >= 0)
|
|
{
|
|
PDBG (pixma_dbg (3, "Reader task terminated\n"));
|
|
}
|
|
else
|
|
{
|
|
PDBG (pixma_dbg
|
|
(2, "Reader task terminated: %s\n", pixma_strerror (count)));
|
|
}
|
|
return map_error (count);
|
|
}
|
|
|
|
static int
|
|
reader_process (void *arg)
|
|
{
|
|
pixma_sane_t *ss = (pixma_sane_t *) arg;
|
|
struct SIGACTION sa;
|
|
|
|
reader_ss = ss;
|
|
memset (&sa, 0, sizeof (sa));
|
|
sigemptyset (&sa.sa_mask);
|
|
sa.sa_handler = reader_signal_handler;
|
|
/* FIXME: which signal else? */
|
|
sigaction (SIGHUP, &sa, NULL);
|
|
sigaction (SIGINT, &sa, NULL);
|
|
sigaction (SIGPIPE, &sa, NULL);
|
|
sigaction (SIGTERM, &sa, NULL);
|
|
close (ss->rpipe);
|
|
ss->rpipe = -1;
|
|
return reader_loop (ss);
|
|
}
|
|
|
|
static int
|
|
reader_thread (void *arg)
|
|
{
|
|
pixma_sane_t *ss = (pixma_sane_t *) arg;
|
|
#ifdef USE_PTHREAD
|
|
/* Block SIGPIPE. We will handle this in reader_loop() by checking
|
|
ss->reader_stop and the return value from write(). */
|
|
sigset_t sigs;
|
|
sigemptyset (&sigs);
|
|
sigaddset (&sigs, SIGPIPE);
|
|
pthread_sigmask (SIG_BLOCK, &sigs, NULL);
|
|
#endif /* USE_PTHREAD */
|
|
return reader_loop (ss);
|
|
}
|
|
|
|
static SANE_Pid
|
|
terminate_reader_task (pixma_sane_t * ss, int *exit_code)
|
|
{
|
|
SANE_Pid result, pid;
|
|
int status = 0;
|
|
|
|
pid = ss->reader_taskid;
|
|
if (pid == -1)
|
|
return -1;
|
|
if (sanei_thread_is_forked ())
|
|
{
|
|
sanei_thread_kill (pid);
|
|
}
|
|
else
|
|
{
|
|
ss->reader_stop = SANE_TRUE;
|
|
/* pixma_cancel (ss->s); What is this for ? Makes end-of-scan buggy => removing */
|
|
}
|
|
result = sanei_thread_waitpid (pid, &status);
|
|
ss->reader_taskid = -1;
|
|
|
|
if (ss->sp.source != PIXMA_SOURCE_ADF && ss->sp.source != PIXMA_SOURCE_ADFDUP)
|
|
ss->idle = SANE_TRUE;
|
|
|
|
if (result == pid)
|
|
{
|
|
if (exit_code)
|
|
*exit_code = status;
|
|
return pid;
|
|
}
|
|
else
|
|
{
|
|
PDBG (pixma_dbg (1, "WARNING:waitpid() failed %s\n", strerror (errno)));
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
static int
|
|
start_reader_task (pixma_sane_t * ss)
|
|
{
|
|
int fds[2];
|
|
SANE_Pid pid;
|
|
int is_forked;
|
|
|
|
if (ss->rpipe != -1 || ss->wpipe != -1)
|
|
{
|
|
PDBG (pixma_dbg
|
|
(1, "BUG:rpipe = %d, wpipe = %d\n", ss->rpipe, ss->wpipe));
|
|
close (ss->rpipe);
|
|
close (ss->wpipe);
|
|
ss->rpipe = -1;
|
|
ss->wpipe = -1;
|
|
}
|
|
if (ss->reader_taskid != -1)
|
|
{
|
|
PDBG (pixma_dbg
|
|
(1, "BUG:reader_taskid(%ld) != -1\n", (long) ss->reader_taskid));
|
|
terminate_reader_task (ss, NULL);
|
|
}
|
|
if (pipe (fds) == -1)
|
|
{
|
|
PDBG (pixma_dbg (1, "ERROR:start_reader_task():pipe() failed %s\n",
|
|
strerror (errno)));
|
|
return PIXMA_ENOMEM;
|
|
}
|
|
ss->rpipe = fds[0];
|
|
ss->wpipe = fds[1];
|
|
ss->reader_stop = SANE_FALSE;
|
|
|
|
is_forked = sanei_thread_is_forked ();
|
|
if (is_forked)
|
|
{
|
|
pid = sanei_thread_begin (reader_process, ss);
|
|
if (pid > 0)
|
|
{
|
|
close (ss->wpipe);
|
|
ss->wpipe = -1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pid = sanei_thread_begin (reader_thread, ss);
|
|
}
|
|
if (pid == -1)
|
|
{
|
|
close (ss->wpipe);
|
|
close (ss->rpipe);
|
|
ss->wpipe = -1;
|
|
ss->rpipe = -1;
|
|
PDBG (pixma_dbg (1, "ERROR:unable to start reader task\n"));
|
|
return PIXMA_ENOMEM;
|
|
}
|
|
PDBG (pixma_dbg (3, "Reader task id=%ld (%s)\n", (long) pid,
|
|
(is_forked) ? "forked" : "threaded"));
|
|
ss->reader_taskid = pid;
|
|
return 0;
|
|
}
|
|
|
|
static SANE_Status
|
|
read_image (pixma_sane_t * ss, void *buf, unsigned size, int *readlen)
|
|
{
|
|
int count, status;
|
|
|
|
if (readlen)
|
|
*readlen = 0;
|
|
if (ss->image_bytes_read >= ss->sp.image_size)
|
|
return SANE_STATUS_EOF;
|
|
|
|
do
|
|
{
|
|
if (ss->cancel)
|
|
/* ss->rpipe has already been closed by sane_cancel(). */
|
|
return SANE_STATUS_CANCELLED;
|
|
count = read (ss->rpipe, buf, size);
|
|
}
|
|
while (count == -1 && errno == EINTR);
|
|
|
|
if (count == -1)
|
|
{
|
|
if (errno == EAGAIN)
|
|
return SANE_STATUS_GOOD;
|
|
if (!ss->cancel)
|
|
{
|
|
PDBG (pixma_dbg (1, "WARNING:read_image():read() failed %s\n",
|
|
strerror (errno)));
|
|
}
|
|
close (ss->rpipe);
|
|
ss->rpipe = -1;
|
|
terminate_reader_task (ss, NULL);
|
|
return SANE_STATUS_IO_ERROR;
|
|
}
|
|
|
|
/* here count >= 0 */
|
|
ss->image_bytes_read += count;
|
|
if (ss->image_bytes_read > ss->sp.image_size)
|
|
{
|
|
PDBG (pixma_dbg (1, "BUG:ss->image_bytes_read > ss->sp.image_size\n"));
|
|
}
|
|
if (ss->image_bytes_read >= ss->sp.image_size)
|
|
{
|
|
close (ss->rpipe);
|
|
ss->rpipe = -1;
|
|
terminate_reader_task (ss, NULL);
|
|
}
|
|
else if (count == 0)
|
|
{
|
|
PDBG (pixma_dbg (3, "read_image():reader task closed the pipe:"
|
|
"%u bytes received, %u bytes expected\n",
|
|
ss->image_bytes_read, ss->sp.image_size));
|
|
close (ss->rpipe);
|
|
ss->rpipe = -1;
|
|
if (terminate_reader_task (ss, &status) != -1
|
|
&& status != SANE_STATUS_GOOD)
|
|
{
|
|
return status;
|
|
}
|
|
else
|
|
{
|
|
/* either terminate_reader_task failed or
|
|
rpipe was closed but we expect more data */
|
|
return SANE_STATUS_IO_ERROR;
|
|
}
|
|
}
|
|
if (readlen)
|
|
*readlen = count;
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
|
|
|
|
/*******************************************************************
|
|
** SANE API
|
|
*******************************************************************/
|
|
SANE_Status
|
|
sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize)
|
|
{
|
|
int status, myversion, i;
|
|
SANEI_Config config;
|
|
|
|
UNUSED (authorize);
|
|
|
|
if (!version_code)
|
|
return SANE_STATUS_INVAL;
|
|
myversion = 100 * PIXMA_VERSION_MAJOR + PIXMA_VERSION_MINOR;
|
|
*version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, V_MINOR, myversion);
|
|
DBG_INIT ();
|
|
sanei_thread_init ();
|
|
pixma_set_debug_level (DBG_LEVEL);
|
|
|
|
PDBG(pixma_dbg(2, "pixma is compiled %s pthread support.\n",
|
|
(sanei_thread_is_forked () ? "without" : "with")));
|
|
|
|
for (i = 0; i < MAX_CONF_DEVICES; i++)
|
|
conf_devices[i] = NULL;
|
|
|
|
config.count = 0;
|
|
config.descriptors = NULL;
|
|
config.values = NULL;
|
|
|
|
if (sanei_configure_attach(PIXMA_CONFIG_FILE, &config, config_attach_pixma) !=
|
|
SANE_STATUS_GOOD)
|
|
PDBG(pixma_dbg(2, "Could not read pixma configuration file: %s\n",
|
|
PIXMA_CONFIG_FILE));
|
|
|
|
status = pixma_init ();
|
|
if (status < 0)
|
|
{
|
|
PDBG (pixma_dbg (2, "pixma_init() failed %s\n", pixma_strerror (status)));
|
|
}
|
|
return map_error (status);
|
|
}
|
|
|
|
void
|
|
sane_exit (void)
|
|
{
|
|
while (first_scanner)
|
|
sane_close (first_scanner);
|
|
cleanup_device_list ();
|
|
pixma_cleanup ();
|
|
}
|
|
|
|
SANE_Status
|
|
sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only)
|
|
{
|
|
UNUSED (local_only);
|
|
|
|
if (!device_list)
|
|
return SANE_STATUS_INVAL;
|
|
find_scanners ();
|
|
*device_list = dev_list;
|
|
return (dev_list) ? SANE_STATUS_GOOD : SANE_STATUS_NO_MEM;
|
|
}
|
|
|
|
SANE_Status
|
|
sane_open (SANE_String_Const name, SANE_Handle * h)
|
|
{
|
|
unsigned i, nscanners;
|
|
int error = 0;
|
|
pixma_sane_t *ss = NULL;
|
|
const pixma_config_t *cfg;
|
|
|
|
if (!name || !h)
|
|
return SANE_STATUS_INVAL;
|
|
|
|
*h = NULL;
|
|
nscanners = pixma_find_scanners (conf_devices);
|
|
if (nscanners == 0)
|
|
return SANE_STATUS_INVAL;
|
|
if (name[0] == '\0')
|
|
name = pixma_get_device_id (0);
|
|
|
|
/* Have we already opened the scanner? */
|
|
for (ss = first_scanner; ss; ss = ss->next)
|
|
{
|
|
if (strcmp (pixma_get_string (ss->s, PIXMA_STRING_ID), name) == 0)
|
|
{
|
|
/* We have already opened it! */
|
|
return SANE_STATUS_DEVICE_BUSY;
|
|
}
|
|
}
|
|
|
|
i = 0;
|
|
while (strcmp (pixma_get_device_id (i), name) != 0)
|
|
{
|
|
if (++i >= nscanners)
|
|
return SANE_STATUS_INVAL;
|
|
}
|
|
cfg = pixma_get_device_config (i);
|
|
if ((cfg->cap & PIXMA_CAP_EXPERIMENT) != 0)
|
|
{
|
|
#ifndef NDEBUG
|
|
pixma_dbg (1, "WARNING:"
|
|
"Experimental backend CAN DAMAGE your hardware!\n");
|
|
if (getenv_atoi ("PIXMA_EXPERIMENT", 0) == 0)
|
|
{
|
|
pixma_dbg (1, "Experimental SANE backend for %s is disabled "
|
|
"by default.\n", pixma_get_device_model (i));
|
|
pixma_dbg (1, "To enable it, set the environment variable "
|
|
"PIXMA_EXPERIMENT to non-zero.\n");
|
|
return SANE_STATUS_UNSUPPORTED;
|
|
}
|
|
#else
|
|
return SANE_STATUS_UNSUPPORTED;
|
|
#endif
|
|
}
|
|
|
|
ss = (pixma_sane_t *) calloc (1, sizeof (*ss));
|
|
if (!ss)
|
|
return SANE_STATUS_NO_MEM;
|
|
ss->next = first_scanner;
|
|
first_scanner = ss;
|
|
ss->reader_taskid = -1;
|
|
ss->wpipe = -1;
|
|
ss->rpipe = -1;
|
|
ss->idle = SANE_TRUE;
|
|
ss->scanning = SANE_FALSE;
|
|
|
|
error = pixma_open (i, &ss->s);
|
|
if (error < 0)
|
|
{
|
|
sane_close (ss);
|
|
return map_error (error);
|
|
}
|
|
pixma_enable_background (ss->s, 0);
|
|
init_option_descriptors (ss);
|
|
*h = ss;
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
|
|
void
|
|
sane_close (SANE_Handle h)
|
|
{
|
|
pixma_sane_t **p, *ss;
|
|
|
|
for (p = &first_scanner; *p && *p != (pixma_sane_t *) h; p = &((*p)->next))
|
|
{
|
|
}
|
|
if (!(*p))
|
|
return;
|
|
ss = *p;
|
|
sane_cancel (ss);
|
|
pixma_close (ss->s);
|
|
*p = ss->next;
|
|
free (ss);
|
|
}
|
|
|
|
const SANE_Option_Descriptor *
|
|
sane_get_option_descriptor (SANE_Handle h, SANE_Int n)
|
|
{
|
|
DECL_CTX;
|
|
|
|
if (ss && 0 <= n && n < opt_last)
|
|
return &SOD (n);
|
|
return NULL;
|
|
}
|
|
|
|
SANE_Status
|
|
sane_control_option (SANE_Handle h, SANE_Int n,
|
|
SANE_Action a, void *v, SANE_Int * i)
|
|
{
|
|
DECL_CTX;
|
|
SANE_Int info = 0;
|
|
int error;
|
|
option_descriptor_t *opt;
|
|
|
|
if (i)
|
|
*i = 0;
|
|
if (!ss)
|
|
return SANE_STATUS_INVAL;
|
|
if (n < 0 || n >= opt_last)
|
|
return SANE_STATUS_UNSUPPORTED;
|
|
if (!ss->idle && a != SANE_ACTION_GET_VALUE)
|
|
{
|
|
PDBG (pixma_dbg (3, "Warning: !idle && !SANE_ACTION_GET_VALUE\n"));
|
|
if (ss->sp.source != PIXMA_SOURCE_ADF && ss->sp.source != PIXMA_SOURCE_ADFDUP)
|
|
return SANE_STATUS_INVAL;
|
|
}
|
|
|
|
opt = &(OPT_IN_CTX[n]);
|
|
if (!SANE_OPTION_IS_ACTIVE (opt->sod.cap))
|
|
return SANE_STATUS_INVAL;
|
|
switch (a)
|
|
{
|
|
case SANE_ACTION_SET_VALUE:
|
|
if ((opt->sod.type != SANE_TYPE_BUTTON && !v) ||
|
|
!SANE_OPTION_IS_SETTABLE (opt->sod.cap))
|
|
return SANE_STATUS_INVAL; /* or _UNSUPPORTED? */
|
|
break;
|
|
case SANE_ACTION_SET_AUTO:
|
|
if (!(opt->sod.cap & SANE_CAP_AUTOMATIC) ||
|
|
!SANE_OPTION_IS_SETTABLE (opt->sod.cap))
|
|
return SANE_STATUS_INVAL; /* or _UNSUPPORTED? */
|
|
break;
|
|
case SANE_ACTION_GET_VALUE:
|
|
if (!v || !(opt->sod.cap & SANE_CAP_SOFT_DETECT))
|
|
return SANE_STATUS_INVAL; /* or _UNSUPPORTED? */
|
|
break;
|
|
default:
|
|
return SANE_STATUS_UNSUPPORTED;
|
|
}
|
|
|
|
error = control_option (ss, n, a, v, &info);
|
|
if (error == SANE_STATUS_GOOD && i)
|
|
*i = info;
|
|
return error;
|
|
}
|
|
|
|
SANE_Status
|
|
sane_get_parameters (SANE_Handle h, SANE_Parameters * p)
|
|
{
|
|
DECL_CTX;
|
|
pixma_scan_param_t temp, *sp;
|
|
|
|
if (!ss || !p)
|
|
return SANE_STATUS_INVAL;
|
|
|
|
if (!ss->idle)
|
|
{
|
|
sp = &ss->sp; /* sp is calculated in sane_start() */
|
|
}
|
|
else
|
|
{
|
|
calc_scan_param (ss, &temp);
|
|
sp = &temp;
|
|
}
|
|
p->format = (sp->channels == 3) ? SANE_FRAME_RGB : SANE_FRAME_GRAY;
|
|
p->last_frame = SANE_TRUE;
|
|
p->lines = sp->h;
|
|
p->depth = sp->depth;
|
|
p->pixels_per_line = sp->w;
|
|
/* p->bytes_per_line = sp->line_size; NOTE: It should work this way, but it doesn't. No SANE frontend can cope with this. */
|
|
p->bytes_per_line = sp->w * sp->channels * (sp->depth / 8);
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
|
|
SANE_Status
|
|
sane_start (SANE_Handle h)
|
|
{
|
|
DECL_CTX;
|
|
int error = 0;
|
|
|
|
if (!ss)
|
|
return SANE_STATUS_INVAL;
|
|
if (!ss->idle && ss->scanning)
|
|
{
|
|
PDBG (pixma_dbg (3, "Warning in Sane_start: !idle && scanning. idle=%d, ss->scanning=%d\n",
|
|
ss->idle, ss->scanning));
|
|
if (ss->sp.source != PIXMA_SOURCE_ADF && ss->sp.source != PIXMA_SOURCE_ADFDUP)
|
|
return SANE_STATUS_INVAL;
|
|
}
|
|
|
|
ss->cancel = SANE_FALSE;
|
|
if (ss->idle ||
|
|
ss->source_map[OVAL (opt_source).w] == PIXMA_SOURCE_FLATBED ||
|
|
ss->source_map[OVAL (opt_source).w] == PIXMA_SOURCE_TPU)
|
|
ss->page_count = 0; /* start from idle state or scan from flatbed or TPU */
|
|
else
|
|
ss->page_count++;
|
|
if (calc_scan_param (ss, &ss->sp) < 0)
|
|
return SANE_STATUS_INVAL;
|
|
ss->image_bytes_read = 0;
|
|
/* TODO: Check paper here in sane_start(). A function like
|
|
pixma_get_status() is needed. */
|
|
error = start_reader_task (ss);
|
|
if (error >= 0)
|
|
{
|
|
ss->output_line_size = ss->sp.w * ss->sp.channels * (ss->sp.depth / 8);
|
|
ss->byte_pos_in_line = 0;
|
|
ss->last_read_status = SANE_STATUS_GOOD;
|
|
ss->scanning = SANE_TRUE;
|
|
ss->idle = SANE_FALSE;
|
|
}
|
|
return map_error (error);
|
|
}
|
|
|
|
SANE_Status
|
|
sane_read (SANE_Handle h, SANE_Byte * buf, SANE_Int maxlen, SANE_Int * len)
|
|
{
|
|
DECL_CTX;
|
|
int sum, n;
|
|
SANE_Byte temp[60];
|
|
SANE_Status status;
|
|
|
|
if (len)
|
|
*len = 0;
|
|
if (!ss || !buf || !len)
|
|
return SANE_STATUS_INVAL;
|
|
if (ss->cancel)
|
|
return SANE_STATUS_CANCELLED;
|
|
if ((ss->idle)
|
|
&& (ss->sp.source == PIXMA_SOURCE_ADF || ss->sp.source == PIXMA_SOURCE_ADFDUP))
|
|
return SANE_STATUS_INVAL;
|
|
if (!ss->scanning)
|
|
return ss->last_read_status;
|
|
|
|
status = SANE_STATUS_GOOD;
|
|
if ((ss->sp.line_size - ss->output_line_size) == 0)
|
|
{
|
|
status = read_image (ss, buf, maxlen, &sum);
|
|
}
|
|
else
|
|
{
|
|
/* FIXME: Because there is no frontend that can cope with padding at
|
|
the end of line, we've to remove it here in the backend! */
|
|
sum = 0;
|
|
while (sum < maxlen)
|
|
{
|
|
if (ss->byte_pos_in_line < ss->output_line_size)
|
|
{
|
|
n = ss->output_line_size - ss->byte_pos_in_line;
|
|
if ((maxlen - sum) < n)
|
|
n = maxlen - sum;
|
|
status = read_image (ss, buf, n, &n);
|
|
if (n == 0)
|
|
break;
|
|
sum += n;
|
|
buf += n;
|
|
ss->byte_pos_in_line += n;
|
|
}
|
|
else
|
|
{
|
|
/* skip padding */
|
|
n = ss->sp.line_size - ss->byte_pos_in_line;
|
|
if (n > (int) sizeof (temp))
|
|
{
|
|
PDBG (pixma_dbg (3, "Inefficient skip buffer. Should be %d\n", n));
|
|
n = sizeof (temp);
|
|
}
|
|
status = read_image (ss, temp, n, &n);
|
|
if (n == 0)
|
|
break;
|
|
ss->byte_pos_in_line += n;
|
|
if (ss->byte_pos_in_line == ss->sp.line_size)
|
|
ss->byte_pos_in_line = 0;
|
|
}
|
|
}
|
|
}
|
|
if (ss->cancel)
|
|
status = SANE_STATUS_CANCELLED;
|
|
else if ((status == SANE_STATUS_GOOD || status == SANE_STATUS_EOF) &&
|
|
sum > 0)
|
|
{
|
|
*len = sum;
|
|
status = SANE_STATUS_GOOD;
|
|
}
|
|
ss->scanning = (status == SANE_STATUS_GOOD);
|
|
ss->last_read_status = status;
|
|
return status;
|
|
}
|
|
|
|
void
|
|
sane_cancel (SANE_Handle h)
|
|
{
|
|
DECL_CTX;
|
|
|
|
if (!ss)
|
|
return;
|
|
ss->cancel = SANE_TRUE;
|
|
if (ss->idle)
|
|
return;
|
|
close (ss->rpipe);
|
|
ss->rpipe = -1;
|
|
terminate_reader_task (ss, NULL);
|
|
ss->idle = SANE_TRUE;
|
|
}
|
|
|
|
SANE_Status
|
|
sane_set_io_mode (SANE_Handle h, SANE_Bool m)
|
|
{
|
|
DECL_CTX;
|
|
|
|
if (!ss || ss->idle || ss->rpipe == -1)
|
|
return SANE_STATUS_INVAL;
|
|
#ifdef HAVE_FCNTL_H
|
|
PDBG (pixma_dbg (2, "Setting %sblocking mode\n", (m) ? "non-" : ""));
|
|
if (fcntl (ss->rpipe, F_SETFL, (m) ? O_NONBLOCK : 0) == -1)
|
|
{
|
|
PDBG (pixma_dbg
|
|
(1, "WARNING:fcntl(F_SETFL) failed %s\n", strerror (errno)));
|
|
return SANE_STATUS_UNSUPPORTED;
|
|
}
|
|
return SANE_STATUS_GOOD;
|
|
#else
|
|
return (m) ? SANE_STATUS_UNSUPPORTED : SANE_STATUS_GOOD;
|
|
#endif
|
|
}
|
|
|
|
SANE_Status
|
|
sane_get_select_fd (SANE_Handle h, SANE_Int * fd)
|
|
{
|
|
DECL_CTX;
|
|
|
|
*fd = -1;
|
|
if (!ss || !fd || ss->idle || ss->rpipe == -1)
|
|
return SANE_STATUS_INVAL;
|
|
*fd = ss->rpipe;
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
|
|
/*
|
|
BEGIN SANE_Option_Descriptor
|
|
|
|
rem -------------------------------------------
|
|
type group
|
|
title Scan mode
|
|
|
|
type int resolution
|
|
unit dpi
|
|
constraint @word_list = ss->dpi_list
|
|
default 75
|
|
title @SANE_TITLE_SCAN_RESOLUTION
|
|
desc @SANE_DESC_SCAN_RESOLUTION
|
|
cap soft_select soft_detect automatic
|
|
info reload_params
|
|
|
|
type string mode[10]
|
|
default Color
|
|
constraint @string_list = ss->mode_list
|
|
title @SANE_TITLE_SCAN_MODE
|
|
desc @SANE_DESC_SCAN_MODE
|
|
cap soft_select soft_detect automatic
|
|
info reload_params
|
|
|
|
type string source[30]
|
|
constraint @string_list = ss->source_list
|
|
title @SANE_TITLE_SCAN_SOURCE
|
|
desc @SANE_DESC_SCAN_SOURCE
|
|
default Flatbed
|
|
cap soft_select soft_detect
|
|
|
|
type bool button-controlled
|
|
title Button-controlled scan
|
|
desc When enabled, scan process will not start immediately. To proceed, press \"SCAN\" button (for MP150) or \"COLOR\" button (for other models). To cancel, press \"GRAY\" button.
|
|
default SANE_FALSE
|
|
cap soft_select soft_detect inactive
|
|
|
|
rem -------------------------------------------
|
|
type group
|
|
title Gamma
|
|
|
|
type bool custom-gamma
|
|
default SANE_TRUE
|
|
title @SANE_TITLE_CUSTOM_GAMMA
|
|
desc @SANE_DESC_CUSTOM_GAMMA
|
|
cap soft_select soft_detect automatic inactive
|
|
|
|
type int gamma-table[4096]
|
|
constraint (0,255,0)
|
|
title @SANE_TITLE_GAMMA_VECTOR
|
|
desc @SANE_DESC_GAMMA_VECTOR
|
|
cap soft_select soft_detect automatic inactive
|
|
|
|
rem -------------------------------------------
|
|
type group
|
|
title Geometry
|
|
|
|
type fixed tl-x
|
|
unit mm
|
|
default 0
|
|
constraint @range = &ss->xrange
|
|
title @SANE_TITLE_SCAN_TL_X
|
|
desc @SANE_DESC_SCAN_TL_X
|
|
cap soft_select soft_detect automatic
|
|
info reload_params
|
|
|
|
type fixed tl-y
|
|
unit mm
|
|
default 0
|
|
constraint @range = &ss->yrange
|
|
title @SANE_TITLE_SCAN_TL_Y
|
|
desc @SANE_DESC_SCAN_TL_Y
|
|
cap soft_select soft_detect automatic
|
|
info reload_params
|
|
|
|
type fixed br-x
|
|
unit mm
|
|
default _MAX
|
|
constraint @range = &ss->xrange
|
|
title @SANE_TITLE_SCAN_BR_X
|
|
desc @SANE_DESC_SCAN_BR_X
|
|
cap soft_select soft_detect automatic
|
|
info reload_params
|
|
|
|
type fixed br-y
|
|
unit mm
|
|
default _MAX
|
|
constraint @range = &ss->yrange
|
|
title @SANE_TITLE_SCAN_BR_Y
|
|
desc @SANE_DESC_SCAN_BR_Y
|
|
cap soft_select soft_detect automatic
|
|
info reload_params
|
|
|
|
rem -------------------------------------------
|
|
type group
|
|
title Buttons
|
|
|
|
type button button-update
|
|
title Update button state
|
|
cap soft_select soft_detect advanced
|
|
|
|
type int button-1
|
|
default 0
|
|
title Button 1
|
|
cap soft_select soft_detect advanced
|
|
|
|
type int button-2
|
|
default 0
|
|
title Button 2
|
|
cap soft_select soft_detect advanced
|
|
|
|
rem -------------------------------------------
|
|
END SANE_Option_Descriptor
|
|
*/
|
|
#include "pixma_sane_options.c" /* generated by gen_options.py */
|