1
/************************************************************************************\
3
soapht.c - HP SANE backend support for soap based multi-function peripherals
5
(c) 2006,2008 Copyright Hewlett-Packard Development Company, LP
7
Permission is hereby granted, free of charge, to any person obtaining a copy
8
of this software and associated documentation files (the "Software"), to deal
9
in the Software without restriction, including without limitation the rights
10
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
11
of the Software, and to permit persons to whom the Software is furnished to do
12
so, subject to the following conditions:
14
The above copyright notice and this permission notice shall be included in all
15
copies or substantial portions of the Software.
17
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
19
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
20
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
21
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24
Note when the LJM1522 input source is ADF, all pages loaded in the ADF must be scanned
25
as one complete scan job, otherwise the ADF will jam. This mean if you try to scan
26
one page only when multiple pages are loaded, the second page will jam. This is how the
27
hardware works. The Windows driver has the same limitation.
29
Author: David Suffield
30
Contributor: Sarbeswar Meher
32
\************************************************************************************/
54
#define DEBUG_DECLARE_ONLY
55
#include "sanei_debug.h"
57
static struct soap_session *session = NULL; /* assume one sane_open per process */
59
static int bb_load(struct soap_session *ps, const char *so)
65
/* Load hpmud manually with symbols exported. Otherwise the plugin will not find it. */
66
if ((ps->hpmud_handle = dlopen("libhpmud.so.0", RTLD_LAZY|RTLD_GLOBAL)) == NULL)
68
BUG("unable to load restricted library: %s\n", dlerror());
72
/* Load math library manually with symbols exported (Ubuntu 8.04). Otherwise the plugin will not find it. */
73
if ((ps->math_handle = dlopen("libm.so", RTLD_LAZY|RTLD_GLOBAL)) == NULL)
75
if ((ps->math_handle = dlopen("libm.so.6", RTLD_LAZY|RTLD_GLOBAL)) == NULL)
77
BUG("unable to load restricted library: %s\n", dlerror());
82
if (hpmud_get_conf("[dirs]", "home", home, sizeof(home)) != HPMUD_R_OK)
84
snprintf(sz, sizeof(sz), "%s/scan/plugins/%s", home, so);
85
if ((ps->bb_handle = dlopen(sz, RTLD_NOW|RTLD_GLOBAL)) == NULL)
87
BUG("unable to load restricted library %s: %s\n", sz, dlerror());
88
SendScanEvent(ps->uri, EVENT_PLUGIN_FAIL);
92
if ((ps->bb_open = dlsym(ps->bb_handle, "bb_open")) == NULL)
94
BUG("unable to load restricted library %s: %s\n", sz, dlerror());
97
if ((ps->bb_close = dlsym(ps->bb_handle, "bb_close")) == NULL)
99
BUG("unable to load restricted library %s: %s\n", sz, dlerror());
102
if ((ps->bb_get_parameters = dlsym(ps->bb_handle, "bb_get_parameters")) == NULL)
104
BUG("unable to load restricted library %s: %s\n", sz, dlerror());
107
if ((ps->bb_is_paper_in_adf = dlsym(ps->bb_handle, "bb_is_paper_in_adf")) == NULL)
109
BUG("unable to load restricted library %s: %s\n", sz, dlerror());
112
if ((ps->bb_start_scan = dlsym(ps->bb_handle, "bb_start_scan")) == NULL)
114
BUG("unable to load restricted library %s: %s\n", sz, dlerror());
117
if ((ps->bb_end_scan = dlsym(ps->bb_handle, "bb_end_scan")) == NULL)
119
BUG("unable to load restricted library %s: %s\n", sz, dlerror());
122
if ((ps->bb_get_image_data = dlsym(ps->bb_handle, "bb_get_image_data")) == NULL)
124
BUG("unable to load restricted library %s: %s\n", sz, dlerror());
127
if ((ps->bb_end_page = dlsym(ps->bb_handle, "bb_end_page")) == NULL)
129
BUG("unable to load restricted library %s: %s\n", sz, dlerror());
139
static int bb_unload(struct soap_session *ps)
143
dlclose(ps->bb_handle);
144
ps->bb_handle = NULL;
146
if (ps->hpmud_handle)
148
dlclose(ps->hpmud_handle);
149
ps->hpmud_handle = NULL;
153
dlclose(ps->math_handle);
154
ps->math_handle = NULL;
159
/* Get raw data (ie: uncompressed data) from image processor. */
160
static int get_ip_data(struct soap_session *ps, SANE_Byte *data, SANE_Int maxLength, SANE_Int *length)
162
int ip_ret=IP_INPUT_ERROR;
163
unsigned int outputAvail=maxLength, outputUsed=0, outputThisPos;
164
unsigned char *input, *output = data;
165
unsigned int inputAvail, inputUsed=0, inputNextPos;
169
BUG("invalid ipconvert state\n");
173
if (ps->bb_get_image_data(ps, outputAvail))
178
inputAvail = ps->cnt;
179
input = &ps->buf[ps->index];
183
input = NULL; /* no more scan data, flush ipconvert pipeline */
187
/* Transform input data to output. Note, output buffer may consume more bytes than input buffer (ie: jpeg to raster). */
188
ip_ret = ipConvert(ps->ip_handle, inputAvail, input, &inputUsed, &inputNextPos, outputAvail, output, &outputUsed, &outputThisPos);
190
DBG6("cnt=%d index=%d input=%p inputAvail=%d inputUsed=%d inputNextPos=%d output=%p outputAvail=%d outputUsed=%d outputThisPos=%d\n", ps->cnt, ps->index, input,
191
inputAvail, inputUsed, inputNextPos, output, outputAvail, outputUsed, outputThisPos);
195
if (inputAvail == inputUsed)
197
ps->index = ps->cnt = 0; /* reset buffer */
201
ps->cnt -= inputUsed; /* save left over buffer for next soap_read */
202
ps->index += inputUsed;
207
*length = outputUsed;
209
/* For sane do not send output data simultaneously with IP_DONE. */
210
if (ip_ret & IP_DONE && outputUsed)
217
static int set_scan_mode_side_effects(struct soap_session *ps, enum COLOR_ENTRY scanMode)
221
memset(ps->compressionList, 0, sizeof(ps->compressionList));
222
memset(ps->compressionMap, 0, sizeof(ps->compressionMap));
226
case CE_BLACK_AND_WHITE1: /* same as GRAY8 */
230
ps->compressionList[j] = STR_COMPRESSION_NONE;
231
ps->compressionMap[j++] = SF_HPRAW;
232
ps->compressionList[j] = STR_COMPRESSION_JPEG;
233
ps->compressionMap[j++] = SF_JFIF;
234
ps->currentCompression = SF_JFIF;
235
ps->option[SOAP_OPTION_JPEG_QUALITY].cap |= SANE_CAP_SOFT_SELECT; /* enable jpeg quality */
240
} /* set_scan_mode_side_effects */
242
static int set_input_source_side_effects(struct soap_session *ps, enum INPUT_SOURCE source)
247
ps->min_width = ps->platen_min_width;
248
ps->min_height = ps->platen_min_height;
249
ps->tlxRange.max = ps->platen_tlxRange.max;
250
ps->brxRange.max = ps->platen_brxRange.max;
251
ps->tlyRange.max = ps->platen_tlyRange.max;
252
ps->bryRange.max = ps->platen_bryRange.max;
257
ps->min_width = ps->adf_min_width;
258
ps->min_height = ps->adf_min_height;
259
ps->tlxRange.max = ps->adf_tlxRange.max;
260
ps->brxRange.max = ps->adf_brxRange.max;
261
ps->tlyRange.max = ps->adf_tlyRange.max;
262
ps->bryRange.max = ps->adf_bryRange.max;
267
} /* set_input_source_side_effects */
269
static int init_options(struct soap_session *ps)
271
ps->option[SOAP_OPTION_COUNT].name = "option-cnt";
272
ps->option[SOAP_OPTION_COUNT].title = SANE_TITLE_NUM_OPTIONS;
273
ps->option[SOAP_OPTION_COUNT].desc = SANE_DESC_NUM_OPTIONS;
274
ps->option[SOAP_OPTION_COUNT].type = SANE_TYPE_INT;
275
ps->option[SOAP_OPTION_COUNT].unit = SANE_UNIT_NONE;
276
ps->option[SOAP_OPTION_COUNT].size = sizeof(SANE_Int);
277
ps->option[SOAP_OPTION_COUNT].cap = SANE_CAP_SOFT_DETECT;
278
ps->option[SOAP_OPTION_COUNT].constraint_type = SANE_CONSTRAINT_NONE;
280
ps->option[SOAP_OPTION_GROUP_SCAN_MODE].name = "mode-group";
281
ps->option[SOAP_OPTION_GROUP_SCAN_MODE].title = SANE_TITLE_SCAN_MODE;
282
ps->option[SOAP_OPTION_GROUP_SCAN_MODE].type = SANE_TYPE_GROUP;
284
ps->option[SOAP_OPTION_SCAN_MODE].name = SANE_NAME_SCAN_MODE;
285
ps->option[SOAP_OPTION_SCAN_MODE].title = SANE_TITLE_SCAN_MODE;
286
ps->option[SOAP_OPTION_SCAN_MODE].desc = SANE_DESC_SCAN_MODE;
287
ps->option[SOAP_OPTION_SCAN_MODE].type = SANE_TYPE_STRING;
288
ps->option[SOAP_OPTION_SCAN_MODE].unit = SANE_UNIT_NONE;
289
ps->option[SOAP_OPTION_SCAN_MODE].size = MAX_STRING_SIZE;
290
ps->option[SOAP_OPTION_SCAN_MODE].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
291
ps->option[SOAP_OPTION_SCAN_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
292
ps->option[SOAP_OPTION_SCAN_MODE].constraint.string_list = ps->scanModeList;
294
ps->option[SOAP_OPTION_INPUT_SOURCE].name = SANE_NAME_SCAN_SOURCE;
295
ps->option[SOAP_OPTION_INPUT_SOURCE].title = SANE_TITLE_SCAN_SOURCE;
296
ps->option[SOAP_OPTION_INPUT_SOURCE].desc = SANE_DESC_SCAN_SOURCE;
297
ps->option[SOAP_OPTION_INPUT_SOURCE].type = SANE_TYPE_STRING;
298
ps->option[SOAP_OPTION_INPUT_SOURCE].unit = SANE_UNIT_NONE;
299
ps->option[SOAP_OPTION_INPUT_SOURCE].size = MAX_STRING_SIZE;
300
ps->option[SOAP_OPTION_INPUT_SOURCE].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
301
ps->option[SOAP_OPTION_INPUT_SOURCE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
302
ps->option[SOAP_OPTION_INPUT_SOURCE].constraint.string_list = ps->inputSourceList;
304
ps->option[SOAP_OPTION_SCAN_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION;
305
ps->option[SOAP_OPTION_SCAN_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION;
306
ps->option[SOAP_OPTION_SCAN_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION;
307
ps->option[SOAP_OPTION_SCAN_RESOLUTION].type = SANE_TYPE_INT;
308
ps->option[SOAP_OPTION_SCAN_RESOLUTION].unit = SANE_UNIT_DPI;
309
ps->option[SOAP_OPTION_SCAN_RESOLUTION].size = sizeof(SANE_Int);
310
ps->option[SOAP_OPTION_SCAN_RESOLUTION].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
311
ps->option[SOAP_OPTION_SCAN_RESOLUTION].constraint_type = SANE_CONSTRAINT_WORD_LIST;
312
ps->option[SOAP_OPTION_SCAN_RESOLUTION].constraint.word_list = ps->resolutionList;
314
ps->option[SOAP_OPTION_GROUP_ADVANCED].name = "advanced-group";
315
ps->option[SOAP_OPTION_GROUP_ADVANCED].title = STR_TITLE_ADVANCED;
316
ps->option[SOAP_OPTION_GROUP_ADVANCED].type = SANE_TYPE_GROUP;
317
ps->option[SOAP_OPTION_GROUP_ADVANCED].cap = SANE_CAP_ADVANCED;
319
ps->option[SOAP_OPTION_CONTRAST].name = SANE_NAME_CONTRAST;
320
ps->option[SOAP_OPTION_CONTRAST].title = SANE_TITLE_CONTRAST;
321
ps->option[SOAP_OPTION_CONTRAST].desc = SANE_DESC_CONTRAST;
322
ps->option[SOAP_OPTION_CONTRAST].type = SANE_TYPE_INT;
323
ps->option[SOAP_OPTION_CONTRAST].unit = SANE_UNIT_NONE;
324
ps->option[SOAP_OPTION_CONTRAST].size = sizeof(SANE_Int);
325
ps->option[SOAP_OPTION_CONTRAST].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED;
326
ps->option[SOAP_OPTION_CONTRAST].constraint_type = SANE_CONSTRAINT_RANGE;
327
ps->option[SOAP_OPTION_CONTRAST].constraint.range = &ps->contrastRange;
328
ps->contrastRange.min = SOAP_CONTRAST_MIN;
329
ps->contrastRange.max = SOAP_CONTRAST_MAX;
330
ps->contrastRange.quant = 0;
332
ps->option[SOAP_OPTION_COMPRESSION].name = STR_NAME_COMPRESSION;
333
ps->option[SOAP_OPTION_COMPRESSION].title = STR_TITLE_COMPRESSION;
334
ps->option[SOAP_OPTION_COMPRESSION].desc = STR_DESC_COMPRESSION;
335
ps->option[SOAP_OPTION_COMPRESSION].type = SANE_TYPE_STRING;
336
ps->option[SOAP_OPTION_COMPRESSION].unit = SANE_UNIT_NONE;
337
ps->option[SOAP_OPTION_COMPRESSION].size = MAX_STRING_SIZE;
338
ps->option[SOAP_OPTION_COMPRESSION].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED;
339
ps->option[SOAP_OPTION_COMPRESSION].constraint_type = SANE_CONSTRAINT_STRING_LIST;
340
ps->option[SOAP_OPTION_COMPRESSION].constraint.string_list = ps->compressionList;
342
ps->option[SOAP_OPTION_JPEG_QUALITY].name = STR_NAME_JPEG_QUALITY;
343
ps->option[SOAP_OPTION_JPEG_QUALITY].title = STR_TITLE_JPEG_QUALITY;
344
ps->option[SOAP_OPTION_JPEG_QUALITY].desc = STR_DESC_JPEG_QUALITY;
345
ps->option[SOAP_OPTION_JPEG_QUALITY].type = SANE_TYPE_INT;
346
ps->option[SOAP_OPTION_JPEG_QUALITY].unit = SANE_UNIT_NONE;
347
ps->option[SOAP_OPTION_JPEG_QUALITY].size = sizeof(SANE_Int);
348
ps->option[SOAP_OPTION_JPEG_QUALITY].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED;
349
ps->option[SOAP_OPTION_JPEG_QUALITY].constraint_type = SANE_CONSTRAINT_RANGE;
350
ps->option[SOAP_OPTION_JPEG_QUALITY].constraint.range = &ps->jpegQualityRange;
351
ps->jpegQualityRange.min = MIN_JPEG_COMPRESSION_FACTOR;
352
ps->jpegQualityRange.max = MAX_JPEG_COMPRESSION_FACTOR;
353
ps->jpegQualityRange.quant = 0;
355
ps->option[SOAP_OPTION_GROUP_GEOMETRY].name = "geometry-group";
356
ps->option[SOAP_OPTION_GROUP_GEOMETRY].title = STR_TITLE_GEOMETRY;
357
ps->option[SOAP_OPTION_GROUP_GEOMETRY].type = SANE_TYPE_GROUP;
358
ps->option[SOAP_OPTION_GROUP_GEOMETRY].cap = SANE_CAP_ADVANCED;
360
ps->option[SOAP_OPTION_TL_X].name = SANE_NAME_SCAN_TL_X;
361
ps->option[SOAP_OPTION_TL_X].title = SANE_TITLE_SCAN_TL_X;
362
ps->option[SOAP_OPTION_TL_X].desc = SANE_DESC_SCAN_TL_X;
363
ps->option[SOAP_OPTION_TL_X].type = SANE_TYPE_FIXED;
364
ps->option[SOAP_OPTION_TL_X].unit = SANE_UNIT_MM;
365
ps->option[SOAP_OPTION_TL_X].size = sizeof(SANE_Int);
366
ps->option[SOAP_OPTION_TL_X].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
367
ps->option[SOAP_OPTION_TL_X].constraint_type = SANE_CONSTRAINT_RANGE;
368
ps->option[SOAP_OPTION_TL_X].constraint.range = &ps->tlxRange;
369
ps->tlxRange.min = 0;
370
ps->tlxRange.quant = 0;
372
ps->option[SOAP_OPTION_TL_Y].name = SANE_NAME_SCAN_TL_Y;
373
ps->option[SOAP_OPTION_TL_Y].title = SANE_TITLE_SCAN_TL_Y;
374
ps->option[SOAP_OPTION_TL_Y].desc = SANE_DESC_SCAN_TL_Y;
375
ps->option[SOAP_OPTION_TL_Y].type = SANE_TYPE_FIXED;
376
ps->option[SOAP_OPTION_TL_Y].unit = SANE_UNIT_MM;
377
ps->option[SOAP_OPTION_TL_Y].size = sizeof(SANE_Int);
378
ps->option[SOAP_OPTION_TL_Y].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
379
ps->option[SOAP_OPTION_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE;
380
ps->option[SOAP_OPTION_TL_Y].constraint.range = &ps->tlyRange;
381
ps->tlyRange.min = 0;
382
ps->tlyRange.quant = 0;
384
ps->option[SOAP_OPTION_BR_X].name = SANE_NAME_SCAN_BR_X;
385
ps->option[SOAP_OPTION_BR_X].title = SANE_TITLE_SCAN_BR_X;
386
ps->option[SOAP_OPTION_BR_X].desc = SANE_DESC_SCAN_BR_X;
387
ps->option[SOAP_OPTION_BR_X].type = SANE_TYPE_FIXED;
388
ps->option[SOAP_OPTION_BR_X].unit = SANE_UNIT_MM;
389
ps->option[SOAP_OPTION_BR_X].size = sizeof(SANE_Int);
390
ps->option[SOAP_OPTION_BR_X].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
391
ps->option[SOAP_OPTION_BR_X].constraint_type = SANE_CONSTRAINT_RANGE;
392
ps->option[SOAP_OPTION_BR_X].constraint.range = &ps->brxRange;
393
ps->brxRange.min = 0;
394
ps->brxRange.quant = 0;
396
ps->option[SOAP_OPTION_BR_Y].name = SANE_NAME_SCAN_BR_Y;
397
ps->option[SOAP_OPTION_BR_Y].title = SANE_TITLE_SCAN_BR_Y;
398
ps->option[SOAP_OPTION_BR_Y].desc = SANE_DESC_SCAN_BR_Y;
399
ps->option[SOAP_OPTION_BR_Y].type = SANE_TYPE_FIXED;
400
ps->option[SOAP_OPTION_BR_Y].unit = SANE_UNIT_MM;
401
ps->option[SOAP_OPTION_BR_Y].size = sizeof(SANE_Int);
402
ps->option[SOAP_OPTION_BR_Y].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
403
ps->option[SOAP_OPTION_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE;
404
ps->option[SOAP_OPTION_BR_Y].constraint.range = &ps->bryRange;
405
ps->bryRange.min = 0;
406
ps->bryRange.quant = 0;
411
/* Verify current x/y extents and set effective extents. */
412
static int set_extents(struct soap_session *ps)
416
if ((ps->currentBrx > ps->currentTlx) && (ps->currentBrx - ps->currentTlx >= ps->min_width) && (ps->currentBrx - ps->currentTlx <= ps->tlxRange.max))
418
ps->effectiveTlx = ps->currentTlx;
419
ps->effectiveBrx = ps->currentBrx;
423
ps->effectiveTlx = 0; /* current setting is not valid, zero it */
424
ps->effectiveBrx = 0;
427
if ((ps->currentBry > ps->currentTly) && (ps->currentBry - ps->currentTly > ps->min_height) && (ps->currentBry - ps->currentTly <= ps->tlyRange.max))
429
ps->effectiveTly = ps->currentTly;
430
ps->effectiveBry = ps->currentBry;
434
ps->effectiveTly = 0; /* current setting is not valid, zero it */
435
ps->effectiveBry = 0;
441
static struct soap_session *create_session()
443
struct soap_session *ps;
445
if ((ps = malloc(sizeof(struct soap_session))) == NULL)
447
BUG("malloc failed: %m\n");
450
memset(ps, 0, sizeof(struct soap_session));
456
} /* create_session */
462
SANE_Status soapht_open(SANE_String_Const device, SANE_Handle *handle)
464
struct hpmud_model_attributes ma;
465
int i, stat = SANE_STATUS_IO_ERROR;
467
DBG8("sane_hpaio_open(%s)\n", device);
471
BUG("session in use\n");
472
return SANE_STATUS_DEVICE_BUSY;
475
if ((session = create_session()) == NULL)
476
return SANE_STATUS_NO_MEM;
478
/* Set session to specified device. */
479
snprintf(session->uri, sizeof(session->uri)-1, "hp:%s", device); /* prepend "hp:" */
481
/* Get actual model attributes from models.dat. */
482
hpmud_query_model(session->uri, &ma);
483
session->scan_type = ma.scantype;
485
if (hpmud_open_device(session->uri, ma.mfp_mode, &session->dd) != HPMUD_R_OK)
487
BUG("unable to open device %s\n", session->uri);
492
return SANE_STATUS_IO_ERROR;
495
if (bb_load(session, "bb_soapht.so"))
497
stat = SANE_STATUS_IO_ERROR;
501
/* Init sane option descriptors. */
502
init_options(session);
504
if (session->bb_open(session))
506
stat = SANE_STATUS_IO_ERROR;
510
/* Set supported Scan Modes as determined by bb_open. */
511
soapht_control_option(session, SOAP_OPTION_SCAN_MODE, SANE_ACTION_SET_AUTO, NULL, NULL); /* set default option */
513
/* Set scan input sources as determined by bb_open. */
514
soapht_control_option(session, SOAP_OPTION_INPUT_SOURCE, SANE_ACTION_SET_AUTO, NULL, NULL); /* set default option */
516
/* Set supported resolutions. */
517
soapht_control_option(session, SOAP_OPTION_SCAN_RESOLUTION, SANE_ACTION_SET_AUTO, NULL, NULL); /* set default option */
519
/* Set supported contrast. */
520
soapht_control_option(session, SOAP_OPTION_CONTRAST, SANE_ACTION_SET_AUTO, NULL, NULL); /* set default option */
522
/* Set supported compression. (Note, cm1017 may say it supports MMR, but it doesn't) */
523
soapht_control_option(session, SOAP_OPTION_COMPRESSION, SANE_ACTION_SET_AUTO, NULL, NULL); /* set default option */
525
/* Determine supported jpeg quality factor as determined by bb_open. */
526
soapht_control_option(session, SOAP_OPTION_JPEG_QUALITY, SANE_ACTION_SET_AUTO, NULL, NULL); /* set default option */
528
/* Set x,y extents. See bb_open */
529
soapht_control_option(session, SOAP_OPTION_TL_X, SANE_ACTION_SET_AUTO, NULL, NULL); /* set default option */
530
soapht_control_option(session, SOAP_OPTION_TL_Y, SANE_ACTION_SET_AUTO, NULL, NULL); /* set default option */
531
soapht_control_option(session, SOAP_OPTION_BR_X, SANE_ACTION_SET_AUTO, NULL, NULL); /* set default option */
532
soapht_control_option(session, SOAP_OPTION_BR_Y, SANE_ACTION_SET_AUTO, NULL, NULL); /* set default option */
534
*handle = (SANE_Handle *)session;
536
stat = SANE_STATUS_GOOD;
540
if (stat != SANE_STATUS_GOOD)
546
hpmud_close_device(session->dd);
555
void soapht_close(SANE_Handle handle)
557
struct soap_session *ps = (struct soap_session *)handle;
559
DBG8("sane_hpaio_close()\n");
561
if (ps == NULL || ps != session)
563
BUG("invalid sane_close\n");
571
hpmud_close_device(ps->dd);
577
const SANE_Option_Descriptor *soapht_get_option_descriptor(SANE_Handle handle, SANE_Int option)
579
struct soap_session *ps = (struct soap_session *)handle;
581
DBG8("sane_hpaio_get_option_descriptor(option=%s)\n", ps->option[option].name);
583
if (option < 0 || option >= SOAP_OPTION_MAX)
586
return &ps->option[option];
587
} /* soapht_get_option_descriptor */
589
SANE_Status soapht_control_option(SANE_Handle handle, SANE_Int option, SANE_Action action, void *value, SANE_Int *set_result)
591
struct soap_session *ps = (struct soap_session *)handle;
592
SANE_Int *int_value = value, mset_result=0;
593
int i, stat=SANE_STATUS_INVAL;
598
case SOAP_OPTION_COUNT:
599
if (action == SANE_ACTION_GET_VALUE)
601
*int_value = SOAP_OPTION_MAX;
602
stat = SANE_STATUS_GOOD;
605
case SOAP_OPTION_SCAN_MODE:
606
if (action == SANE_ACTION_GET_VALUE)
608
for (i=0; ps->scanModeList[i]; i++)
610
if (ps->currentScanMode == ps->scanModeMap[i])
612
strcpy(value, ps->scanModeList[i]);
613
stat = SANE_STATUS_GOOD;
618
else if (action == SANE_ACTION_SET_VALUE)
620
for (i=0; ps->scanModeList[i]; i++)
622
if (strcasecmp(ps->scanModeList[i], value) == 0)
624
ps->currentScanMode = ps->scanModeMap[i];
625
set_scan_mode_side_effects(ps, ps->currentScanMode);
626
mset_result |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
627
stat = SANE_STATUS_GOOD;
634
ps->currentScanMode = CE_RGB24;
635
set_scan_mode_side_effects(ps, ps->currentScanMode);
636
stat = SANE_STATUS_GOOD;
639
case SOAP_OPTION_INPUT_SOURCE:
640
if (action == SANE_ACTION_GET_VALUE)
642
for (i=0; ps->inputSourceList[i]; i++)
644
if (ps->currentInputSource == ps->inputSourceMap[i])
646
strcpy(value, ps->inputSourceList[i]);
647
stat = SANE_STATUS_GOOD;
652
else if (action == SANE_ACTION_SET_VALUE)
654
for (i=0; ps->inputSourceList[i]; i++)
656
if (strcasecmp(ps->inputSourceList[i], value) == 0)
658
ps->currentInputSource = ps->inputSourceMap[i];
659
set_input_source_side_effects(ps, ps->currentInputSource);
660
if(ps->currentInputSource == IS_ADF || ps->currentInputSource == IS_ADF_DUPLEX)
662
i = ps->adf_resolutionList[0] + 1;
663
while(i--) ps->resolutionList[i] = ps->adf_resolutionList[i];
665
else //if(ps->currentInputSource == IS_PLATEN)
667
i = ps->platen_resolutionList[0] + 1;
668
while(i--) ps->resolutionList[i] = ps->platen_resolutionList[i];
670
mset_result |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
671
stat = SANE_STATUS_GOOD;
678
ps->currentInputSource = IS_PLATEN;
679
set_input_source_side_effects(ps, ps->currentInputSource);
680
mset_result |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
681
stat = SANE_STATUS_GOOD;
684
case SOAP_OPTION_SCAN_RESOLUTION:
685
if (action == SANE_ACTION_GET_VALUE)
687
*int_value = ps->currentResolution;
688
stat = SANE_STATUS_GOOD;
690
else if (action == SANE_ACTION_SET_VALUE)
692
for (i=1; i <= ps->resolutionList[0]; i++)
694
if (ps->resolutionList[i] == *int_value)
696
ps->currentResolution = *int_value;
697
mset_result |= SANE_INFO_RELOAD_PARAMS;
698
stat = SANE_STATUS_GOOD;
705
ps->currentResolution = 75;
706
stat = SANE_STATUS_GOOD;
709
case SOAP_OPTION_CONTRAST:
710
if (action == SANE_ACTION_GET_VALUE)
712
*int_value = ps->currentContrast;
713
stat = SANE_STATUS_GOOD;
715
else if (action == SANE_ACTION_SET_VALUE)
717
if (*int_value >= SOAP_CONTRAST_MIN && *int_value <= SOAP_CONTRAST_MAX)
719
ps->currentContrast = *int_value;
720
stat = SANE_STATUS_GOOD;
726
ps->currentContrast = SOAP_CONTRAST_DEFAULT;
727
stat = SANE_STATUS_GOOD;
730
case SOAP_OPTION_COMPRESSION:
731
if (action == SANE_ACTION_GET_VALUE)
733
for (i=0; ps->compressionList[i]; i++)
735
if (ps->currentCompression == ps->compressionMap[i])
737
strcpy(value, ps->compressionList[i]);
738
stat = SANE_STATUS_GOOD;
743
else if (action == SANE_ACTION_SET_VALUE)
745
for (i=0; ps->compressionList[i]; i++)
747
if (strcasecmp(ps->compressionList[i], value) == 0)
749
ps->currentCompression = ps->compressionMap[i];
750
stat = SANE_STATUS_GOOD;
757
ps->currentCompression = SF_JFIF;
758
stat = SANE_STATUS_GOOD;
761
case SOAP_OPTION_JPEG_QUALITY:
762
if (action == SANE_ACTION_GET_VALUE)
764
*int_value = ps->currentJpegQuality;
765
stat = SANE_STATUS_GOOD;
767
else if (action == SANE_ACTION_SET_VALUE)
769
if (*int_value >= MIN_JPEG_COMPRESSION_FACTOR && *int_value <= MAX_JPEG_COMPRESSION_FACTOR)
771
ps->currentJpegQuality = *int_value;
772
stat = SANE_STATUS_GOOD;
778
ps->currentJpegQuality = SAFER_JPEG_COMPRESSION_FACTOR;
779
stat = SANE_STATUS_GOOD;
782
case SOAP_OPTION_TL_X:
783
if (action == SANE_ACTION_GET_VALUE)
785
*int_value = ps->currentTlx;
786
stat = SANE_STATUS_GOOD;
788
else if (action == SANE_ACTION_SET_VALUE)
790
if (*int_value >= ps->tlxRange.min && *int_value <= ps->tlxRange.max)
792
ps->currentTlx = *int_value;
793
mset_result |= SANE_INFO_RELOAD_PARAMS;
794
stat = SANE_STATUS_GOOD;
800
ps->currentTlx = ps->tlxRange.min;
801
stat = SANE_STATUS_GOOD;
804
case SOAP_OPTION_TL_Y:
805
if (action == SANE_ACTION_GET_VALUE)
807
*int_value = ps->currentTly;
808
stat = SANE_STATUS_GOOD;
810
else if (action == SANE_ACTION_SET_VALUE)
812
if (*int_value >= ps->tlyRange.min && *int_value <= ps->tlyRange.max)
815
ps->currentTly = *int_value;
816
mset_result |= SANE_INFO_RELOAD_PARAMS;
817
stat = SANE_STATUS_GOOD;
823
ps->currentTly = ps->tlyRange.min;
824
stat = SANE_STATUS_GOOD;
827
case SOAP_OPTION_BR_X:
828
if (action == SANE_ACTION_GET_VALUE)
830
*int_value = ps->currentBrx;
831
stat = SANE_STATUS_GOOD;
833
else if (action == SANE_ACTION_SET_VALUE)
835
if (*int_value >= ps->brxRange.min && *int_value <= ps->brxRange.max)
837
ps->currentBrx = *int_value;
838
mset_result |= SANE_INFO_RELOAD_PARAMS;
839
stat = SANE_STATUS_GOOD;
845
ps->currentBrx = ps->brxRange.max;
846
stat = SANE_STATUS_GOOD;
849
case SOAP_OPTION_BR_Y:
850
if (action == SANE_ACTION_GET_VALUE)
852
*int_value = ps->currentBry;
853
stat = SANE_STATUS_GOOD;
855
else if (action == SANE_ACTION_SET_VALUE)
857
if (*int_value >= ps->bryRange.min && *int_value <= ps->bryRange.max)
859
ps->currentBry = *int_value;
860
mset_result |= SANE_INFO_RELOAD_PARAMS;
861
stat = SANE_STATUS_GOOD;
867
ps->currentBry = ps->bryRange.max;
868
stat = SANE_STATUS_GOOD;
876
*set_result = mset_result;
878
if (stat != SANE_STATUS_GOOD)
880
BUG("control_option failed: option=%s action=%s\n", ps->option[option].name,
881
action==SANE_ACTION_GET_VALUE ? "get" : action==SANE_ACTION_SET_VALUE ? "set" : "auto");
884
DBG8("sane_hpaio_control_option (option=%s action=%s value=%s)\n", ps->option[option].name,
885
action==SANE_ACTION_GET_VALUE ? "get" : action==SANE_ACTION_SET_VALUE ? "set" : "auto",
886
value ? ps->option[option].type == SANE_TYPE_STRING ? (char *)value : psnprintf(sz, sizeof(sz), "%d", *(int *)value) : "na");
889
} /* soapht_control_option */
891
SANE_Status soapht_get_parameters(SANE_Handle handle, SANE_Parameters *params)
893
struct soap_session *ps = (struct soap_session *)handle;
897
/* Get scan parameters for sane client. */
898
ps->bb_get_parameters(ps, params, ps->ip_handle ? SPO_STARTED : SPO_BEST_GUESS);
900
DBG8("sane_hpaio_get_parameters(): format=%d, last_frame=%d, lines=%d, depth=%d, pixels_per_line=%d, bytes_per_line=%d\n",
901
params->format, params->last_frame, params->lines, params->depth, params->pixels_per_line, params->bytes_per_line);
903
return SANE_STATUS_GOOD;
904
} /* soapht_get_parameters */
906
SANE_Status soapht_start(SANE_Handle handle)
908
struct soap_session *ps = (struct soap_session *)handle;
910
IP_IMAGE_TRAITS traits;
911
IP_XFORM_SPEC xforms[IP_MAX_XFORMS], *pXform=xforms;
914
DBG8("sane_hpaio_start()\n");
916
ps -> user_cancel = 0;
922
BUG("invalid extents: tlx=%d brx=%d tly=%d bry=%d minwidth=%d minheight%d maxwidth=%d maxheight=%d\n",
923
ps->currentTlx, ps->currentTly, ps->currentBrx, ps->currentBry, ps->min_width, ps->min_height, ps->tlxRange.max, ps->tlyRange.max);
924
stat = SANE_STATUS_INVAL;
928
/* If input is ADF and ADF is empty, return SANE_STATUS_NO_DOCS. */
929
if (ps->currentInputSource==IS_ADF || ps->currentInputSource==IS_ADF_DUPLEX)
931
ret = ps->bb_is_paper_in_adf(ps); /* 0 = no paper in adf, 1 = paper in adf, -1 = error */
934
stat = SANE_STATUS_NO_DOCS; /* done scanning */
935
SendScanEvent (ps->uri, EVENT_SCAN_ADF_NO_DOCS);
940
stat = SANE_STATUS_IO_ERROR;
945
/* Start scan and get actual image traits. */
946
if (ps->bb_start_scan(ps))
948
stat = SANE_STATUS_IO_ERROR;
951
SendScanEvent(ps->uri, EVENT_START_SCAN_JOB);
952
memset(xforms, 0, sizeof(xforms));
954
/* Setup image-processing pipeline for xform. */
955
if (ps->currentScanMode == CE_RGB24 || ps->currentScanMode == CE_GRAY8)
957
switch(ps->currentCompression)
960
pXform->aXformInfo[IP_JPG_DECODE_FROM_DENALI].dword = 0; /* 0=no */
961
ADD_XFORM(X_JPG_DECODE);
962
pXform->aXformInfo[IP_CNV_COLOR_SPACE_WHICH_CNV].dword = IP_CNV_YCC_TO_SRGB;
963
pXform->aXformInfo[IP_CNV_COLOR_SPACE_GAMMA].dword = 0x00010000;
964
ADD_XFORM(X_CNV_COLOR_SPACE);
972
{ /* Must be BLACK_AND_WHITE1 (Lineart). */
973
switch(ps->currentCompression)
976
pXform->aXformInfo[IP_JPG_DECODE_FROM_DENALI].dword = 0; /* 0=no */
977
ADD_XFORM(X_JPG_DECODE);
978
pXform->aXformInfo[IP_GRAY_2_BI_THRESHOLD].dword = 127;
979
ADD_XFORM(X_GRAY_2_BI);
982
pXform->aXformInfo[IP_GRAY_2_BI_THRESHOLD].dword = 127;
983
ADD_XFORM(X_GRAY_2_BI);
989
/* Setup x/y cropping for xform. (Actually we let cm1017 do it's own cropping) */
990
pXform->aXformInfo[IP_CROP_LEFT].dword = 0;
991
pXform->aXformInfo[IP_CROP_RIGHT].dword = 0;
992
pXform->aXformInfo[IP_CROP_TOP].dword = 0;
993
pXform->aXformInfo[IP_CROP_MAXOUTROWS].dword = 0;
996
/* Setup x/y padding for xform. (Actually we let cm1017 do it's own padding) */
997
pXform->aXformInfo[IP_PAD_LEFT].dword = 0; /* # of pixels to add to left side */
998
pXform->aXformInfo[IP_PAD_RIGHT].dword = 0; /* # of pixels to add to right side */
999
pXform->aXformInfo[IP_PAD_TOP].dword = 0; /* # of rows to add to top */
1000
pXform->aXformInfo[IP_PAD_BOTTOM].dword = 0; /* # of rows to add to bottom */
1001
pXform->aXformInfo[IP_PAD_VALUE].dword = ps->currentScanMode == CE_BLACK_AND_WHITE1 ? 0 : -1; /* lineart white = 0, rgb white = -1 */
1002
pXform->aXformInfo[IP_PAD_MIN_HEIGHT].dword = 0;
1005
/* Open image processor. */
1006
if ((ret = ipOpen(pXform-xforms, xforms, 0, &ps->ip_handle)) != IP_DONE)
1008
BUG("unable open image processor: err=%d\n", ret);
1009
stat = SANE_STATUS_INVAL;
1013
/* Get scan parameters for image processor. */
1014
if (ps->currentCompression == SF_HPRAW)
1015
ps->bb_get_parameters(ps, &pp, SPO_STARTED_JR); /* hpraw, use actual parameters */
1017
ps->bb_get_parameters(ps, &pp, SPO_BEST_GUESS); /* jpeg, use best guess */
1018
traits.iPixelsPerRow = pp.pixels_per_line;
1019
switch(ps->currentScanMode)
1021
case CE_BLACK_AND_WHITE1: /* lineart (let IP create Mono from Gray8) */
1023
traits.iBitsPerPixel = 8; /* grayscale */
1027
traits.iBitsPerPixel = 24; /* color */
1030
traits.lHorizDPI = ps->currentResolution << 16;
1031
traits.lVertDPI = ps->currentResolution << 16;
1032
traits.lNumRows = pp.lines;
1033
traits.iNumPages = 1;
1034
traits.iPageNum = 1;
1035
traits.iComponentsPerPixel = ((traits.iBitsPerPixel % 3) ? 1 : 3);
1036
ipSetDefaultInputTraits(ps->ip_handle, &traits);
1038
/* If jpeg get output image attributes from the image processor. */
1039
if (ps->currentCompression == SF_JFIF)
1041
/* Enable parsed header flag. */
1042
ipResultMask(ps->ip_handle, IP_PARSED_HEADER);
1044
/* Wait for image processor to process header so we know the exact size of the image for sane_get_params. */
1047
ret = get_ip_data(ps, NULL, 0, NULL);
1049
if (ret & (IP_INPUT_ERROR | IP_FATAL_ERROR | IP_DONE))
1051
BUG("ipConvert error=%x\n", ret);
1052
stat = SANE_STATUS_IO_ERROR;
1056
if (ret & IP_PARSED_HEADER)
1058
ipGetImageTraits(ps->ip_handle, NULL, &ps->image_traits); /* get valid image traits */
1059
ipResultMask(ps->ip_handle, 0); /* disable parsed header flag */
1065
ipGetImageTraits(ps->ip_handle, NULL, &ps->image_traits); /* get valid image traits */
1067
stat = SANE_STATUS_GOOD;
1070
if (stat != SANE_STATUS_GOOD)
1074
ipClose(ps->ip_handle);
1077
ps->bb_end_scan(ps, stat == SANE_STATUS_IO_ERROR ? 1: 0);
1081
} /* soapht_start */
1083
SANE_Status soapht_read(SANE_Handle handle, SANE_Byte *data, SANE_Int maxLength, SANE_Int *length)
1085
struct soap_session *ps = (struct soap_session *)handle;
1086
int ret, stat=SANE_STATUS_IO_ERROR;
1088
DBG8("sane_hpaio_read() handle=%p data=%p maxLength=%d\n", (void *)handle, data, maxLength);
1091
DBG8("soapht_read() EVENT_SCAN_CANCEL****uri=%s\n", ps->uri);
1092
SendScanEvent(ps->uri, EVENT_SCAN_CANCEL);
1093
return SANE_STATUS_CANCELLED;
1096
ret = get_ip_data(ps, data, maxLength, length);
1098
if(ret & (IP_INPUT_ERROR | IP_FATAL_ERROR))
1100
BUG("ipConvert error=%x\n", ret);
1106
stat = SANE_STATUS_EOF;
1107
SendScanEvent(ps->uri, EVENT_END_SCAN_JOB);
1110
stat = SANE_STATUS_GOOD;
1113
if (stat != SANE_STATUS_GOOD)
1117
/* Note always call ipClose when SANE_STATUS_EOF, do not depend on sane_cancel because sane_cancel is only called at the end of a batch job. */
1118
ipClose(ps->ip_handle);
1121
ps->bb_end_page(ps, 0);
1124
DBG8("-sane_hpaio_read() output=%p bytes_read=%d maxLength=%d status=%d\n", data, *length, maxLength, stat);
1129
void soapht_cancel(SANE_Handle handle)
1131
struct soap_session *ps = (struct soap_session *)handle;
1133
DBG8("sane_hpaio_cancel()\n");
1136
* Sane_cancel is always called at the end of the scan job. Note that on a multiple page scan job
1137
* sane_cancel is called only once.
1139
ps -> user_cancel = 1;
1142
ipClose(ps->ip_handle);
1145
ps->bb_end_scan(ps, 0);
1146
} /* soapht_cancel */