1
/************************************************************************************\
3
soap.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 the CLJ CM1017 windows driver supports jpeg, but no hpraw (over the wire).
25
The problem is hpraw does not return bytes_per_line and number_of_lines at the
26
start of the scan job. Even though we perform the same calculation as firmware
27
for hpraw, due to round-off error between different math libraries determining
28
the correct bytes_per_line is not always possible (especially at 600dpi and 1200dpi).
30
Also the CM1017 linart mode only works with hpraw (over the wire).
32
Given the hpraw problem stated above this driver will only support jpeg for all scan
33
modes. Linart will use 8-bit gray then convert to mono. Same as windows.
35
Author: David Suffield
37
\************************************************************************************/
60
#define DEBUG_DECLARE_ONLY
61
#include "sanei_debug.h"
63
static struct soap_session *session = NULL; /* assume one sane_open per process */
65
static int bb_load(struct soap_session *ps, const char *so)
71
/* Load hpmud manually with symbols exported. Otherwise the plugin will not find it. */
72
if ((ps->hpmud_handle = dlopen("libhpmud.so", RTLD_LAZY|RTLD_GLOBAL)) == NULL)
74
BUG("unable to load restricted library: %s\n", dlerror());
78
/* Load math library manually with symbols exported (Ubuntu 8.04). Otherwise the plugin will not find it. */
79
if ((ps->math_handle = dlopen("libm.so", RTLD_LAZY|RTLD_GLOBAL)) == NULL)
81
if ((ps->math_handle = dlopen("libm.so.6", RTLD_LAZY|RTLD_GLOBAL)) == NULL)
83
BUG("unable to load restricted library: %s\n", dlerror());
88
if (hpmud_get_conf("[dirs]", "home", home, sizeof(home)) != HPMUD_R_OK)
90
snprintf(sz, sizeof(sz), "%s/scan/plugins/%s", home, so);
91
if ((ps->bb_handle = dlopen(sz, RTLD_NOW|RTLD_GLOBAL)) == NULL)
93
BUG("unable to load restricted library %s: %s\n", sz, dlerror());
94
SendScanEvent(ps->uri, EVENT_PLUGIN_FAIL);
98
if ((ps->bb_open = dlsym(ps->bb_handle, "bb_open")) == NULL)
100
BUG("unable to load restricted library %s: %s\n", sz, dlerror());
103
if ((ps->bb_close = dlsym(ps->bb_handle, "bb_close")) == NULL)
105
BUG("unable to load restricted library %s: %s\n", sz, dlerror());
108
if ((ps->bb_get_parameters = dlsym(ps->bb_handle, "bb_get_parameters")) == NULL)
110
BUG("unable to load restricted library %s: %s\n", sz, dlerror());
113
if ((ps->bb_is_paper_in_adf = dlsym(ps->bb_handle, "bb_is_paper_in_adf")) == NULL)
115
BUG("unable to load restricted library %s: %s\n", sz, dlerror());
118
if ((ps->bb_start_scan = dlsym(ps->bb_handle, "bb_start_scan")) == NULL)
120
BUG("unable to load restricted library %s: %s\n", sz, dlerror());
123
if ((ps->bb_end_scan = dlsym(ps->bb_handle, "bb_end_scan")) == NULL)
125
BUG("unable to load restricted library %s: %s\n", sz, dlerror());
128
if ((ps->bb_get_image_data = dlsym(ps->bb_handle, "bb_get_image_data")) == NULL)
130
BUG("unable to load restricted library %s: %s\n", sz, dlerror());
133
if ((ps->bb_end_page = dlsym(ps->bb_handle, "bb_end_page")) == NULL)
135
BUG("unable to load restricted library %s: %s\n", sz, dlerror());
145
static int bb_unload(struct soap_session *ps)
149
dlclose(ps->bb_handle);
150
ps->bb_handle = NULL;
152
if (ps->hpmud_handle)
154
dlclose(ps->hpmud_handle);
155
ps->hpmud_handle = NULL;
159
dlclose(ps->math_handle);
160
ps->math_handle = NULL;
165
/* Get raw data (ie: uncompressed data) from image processor. */
166
static int get_ip_data(struct soap_session *ps, SANE_Byte *data, SANE_Int maxLength, SANE_Int *length)
168
int ip_ret=IP_INPUT_ERROR;
169
unsigned int outputAvail=maxLength, outputUsed=0, outputThisPos;
170
unsigned char *input, *output = data;
171
unsigned int inputAvail, inputUsed=0, inputNextPos;
175
BUG("invalid ipconvert state\n");
179
if (ps->bb_get_image_data(ps, outputAvail))
184
inputAvail = ps->cnt;
185
input = &ps->buf[ps->index];
189
input = NULL; /* no more scan data, flush ipconvert pipeline */
193
/* Transform input data to output. Note, output buffer may consume more bytes than input buffer (ie: jpeg to raster). */
194
ip_ret = ipConvert(ps->ip_handle, inputAvail, input, &inputUsed, &inputNextPos, outputAvail, output, &outputUsed, &outputThisPos);
196
DBG6("cnt=%d index=%d input=%p inputAvail=%d inputUsed=%d inputNextPos=%d output=%p outputAvail=%d outputThisPos=%d\n", ps->cnt, ps->index, input,
197
inputAvail, inputUsed, inputNextPos, output, outputAvail, outputThisPos);
201
if (inputAvail == inputUsed)
203
ps->index = ps->cnt = 0; /* reset buffer */
207
ps->cnt -= inputUsed; /* save left over buffer for next soap_read */
208
ps->index += inputUsed;
213
*length = outputUsed;
215
/* For sane do not send output data simultaneously with IP_DONE. */
216
if (ip_ret & IP_DONE && outputUsed)
223
static int set_scan_mode_side_effects(struct soap_session *ps, enum COLOR_ENTRY scanMode)
227
memset(ps->compressionList, 0, sizeof(ps->compressionList));
228
memset(ps->compressionMap, 0, sizeof(ps->compressionMap));
232
case CE_BLACK_AND_WHITE1:
236
ps->compressionList[j] = STR_COMPRESSION_JPEG;
237
ps->compressionMap[j++] = SF_JFIF;
238
ps->currentCompression = SF_JFIF;
239
ps->option[SOAP_OPTION_JPEG_QUALITY].cap |= SANE_CAP_SOFT_SELECT; /* enable jpeg quality */
244
} /* set_scan_mode_side_effects */
246
static struct soap_session *create_session()
248
struct soap_session *ps;
250
if ((ps = malloc(sizeof(struct soap_session))) == NULL)
252
BUG("malloc failed: %m\n");
255
memset(ps, 0, sizeof(struct soap_session));
261
} /* create_session */
263
static int init_options(struct soap_session *ps)
265
ps->option[SOAP_OPTION_COUNT].name = "option-cnt";
266
ps->option[SOAP_OPTION_COUNT].title = SANE_TITLE_NUM_OPTIONS;
267
ps->option[SOAP_OPTION_COUNT].desc = SANE_DESC_NUM_OPTIONS;
268
ps->option[SOAP_OPTION_COUNT].type = SANE_TYPE_INT;
269
ps->option[SOAP_OPTION_COUNT].unit = SANE_UNIT_NONE;
270
ps->option[SOAP_OPTION_COUNT].size = sizeof(SANE_Int);
271
ps->option[SOAP_OPTION_COUNT].cap = SANE_CAP_SOFT_DETECT;
272
ps->option[SOAP_OPTION_COUNT].constraint_type = SANE_CONSTRAINT_NONE;
274
ps->option[SOAP_OPTION_GROUP_SCAN_MODE].name = "mode-group";
275
ps->option[SOAP_OPTION_GROUP_SCAN_MODE].title = SANE_TITLE_SCAN_MODE;
276
ps->option[SOAP_OPTION_GROUP_SCAN_MODE].type = SANE_TYPE_GROUP;
278
ps->option[SOAP_OPTION_SCAN_MODE].name = SANE_NAME_SCAN_MODE;
279
ps->option[SOAP_OPTION_SCAN_MODE].title = SANE_TITLE_SCAN_MODE;
280
ps->option[SOAP_OPTION_SCAN_MODE].desc = SANE_DESC_SCAN_MODE;
281
ps->option[SOAP_OPTION_SCAN_MODE].type = SANE_TYPE_STRING;
282
ps->option[SOAP_OPTION_SCAN_MODE].unit = SANE_UNIT_NONE;
283
ps->option[SOAP_OPTION_SCAN_MODE].size = MAX_STRING_SIZE;
284
ps->option[SOAP_OPTION_SCAN_MODE].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
285
ps->option[SOAP_OPTION_SCAN_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
286
ps->option[SOAP_OPTION_SCAN_MODE].constraint.string_list = ps->scanModeList;
288
ps->option[SOAP_OPTION_SCAN_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION;
289
ps->option[SOAP_OPTION_SCAN_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION;
290
ps->option[SOAP_OPTION_SCAN_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION;
291
ps->option[SOAP_OPTION_SCAN_RESOLUTION].type = SANE_TYPE_INT;
292
ps->option[SOAP_OPTION_SCAN_RESOLUTION].unit = SANE_UNIT_DPI;
293
ps->option[SOAP_OPTION_SCAN_RESOLUTION].size = sizeof(SANE_Int);
294
ps->option[SOAP_OPTION_SCAN_RESOLUTION].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
295
ps->option[SOAP_OPTION_SCAN_RESOLUTION].constraint_type = SANE_CONSTRAINT_WORD_LIST;
296
ps->option[SOAP_OPTION_SCAN_RESOLUTION].constraint.word_list = ps->resolutionList;
298
ps->option[SOAP_OPTION_GROUP_ADVANCED].name = "advanced-group";
299
ps->option[SOAP_OPTION_GROUP_ADVANCED].title = STR_TITLE_ADVANCED;
300
ps->option[SOAP_OPTION_GROUP_ADVANCED].type = SANE_TYPE_GROUP;
301
ps->option[SOAP_OPTION_GROUP_ADVANCED].cap = SANE_CAP_ADVANCED;
303
ps->option[SOAP_OPTION_CONTRAST].name = SANE_NAME_CONTRAST;
304
ps->option[SOAP_OPTION_CONTRAST].title = SANE_TITLE_CONTRAST;
305
ps->option[SOAP_OPTION_CONTRAST].desc = SANE_DESC_CONTRAST;
306
ps->option[SOAP_OPTION_CONTRAST].type = SANE_TYPE_INT;
307
ps->option[SOAP_OPTION_CONTRAST].unit = SANE_UNIT_NONE;
308
ps->option[SOAP_OPTION_CONTRAST].size = sizeof(SANE_Int);
309
ps->option[SOAP_OPTION_CONTRAST].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED;
310
ps->option[SOAP_OPTION_CONTRAST].constraint_type = SANE_CONSTRAINT_RANGE;
311
ps->option[SOAP_OPTION_CONTRAST].constraint.range = &ps->contrastRange;
312
ps->contrastRange.min = SOAP_CONTRAST_MIN;
313
ps->contrastRange.max = SOAP_CONTRAST_MAX;
314
ps->contrastRange.quant = 0;
316
ps->option[SOAP_OPTION_COMPRESSION].name = STR_NAME_COMPRESSION;
317
ps->option[SOAP_OPTION_COMPRESSION].title = STR_TITLE_COMPRESSION;
318
ps->option[SOAP_OPTION_COMPRESSION].desc = STR_DESC_COMPRESSION;
319
ps->option[SOAP_OPTION_COMPRESSION].type = SANE_TYPE_STRING;
320
ps->option[SOAP_OPTION_COMPRESSION].unit = SANE_UNIT_NONE;
321
ps->option[SOAP_OPTION_COMPRESSION].size = MAX_STRING_SIZE;
322
ps->option[SOAP_OPTION_COMPRESSION].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED;
323
ps->option[SOAP_OPTION_COMPRESSION].constraint_type = SANE_CONSTRAINT_STRING_LIST;
324
ps->option[SOAP_OPTION_COMPRESSION].constraint.string_list = ps->compressionList;
326
ps->option[SOAP_OPTION_JPEG_QUALITY].name = STR_NAME_JPEG_QUALITY;
327
ps->option[SOAP_OPTION_JPEG_QUALITY].title = STR_TITLE_JPEG_QUALITY;
328
ps->option[SOAP_OPTION_JPEG_QUALITY].desc = STR_DESC_JPEG_QUALITY;
329
ps->option[SOAP_OPTION_JPEG_QUALITY].type = SANE_TYPE_INT;
330
ps->option[SOAP_OPTION_JPEG_QUALITY].unit = SANE_UNIT_NONE;
331
ps->option[SOAP_OPTION_JPEG_QUALITY].size = sizeof(SANE_Int);
332
ps->option[SOAP_OPTION_JPEG_QUALITY].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED;
333
ps->option[SOAP_OPTION_JPEG_QUALITY].constraint_type = SANE_CONSTRAINT_RANGE;
334
ps->option[SOAP_OPTION_JPEG_QUALITY].constraint.range = &ps->jpegQualityRange;
335
ps->jpegQualityRange.min = MIN_JPEG_COMPRESSION_FACTOR;
336
ps->jpegQualityRange.max = MAX_JPEG_COMPRESSION_FACTOR;
337
ps->jpegQualityRange.quant = 0;
339
ps->option[SOAP_OPTION_GROUP_GEOMETRY].name = "geometry-group";
340
ps->option[SOAP_OPTION_GROUP_GEOMETRY].title = STR_TITLE_GEOMETRY;
341
ps->option[SOAP_OPTION_GROUP_GEOMETRY].type = SANE_TYPE_GROUP;
342
ps->option[SOAP_OPTION_GROUP_GEOMETRY].cap = SANE_CAP_ADVANCED;
344
ps->option[SOAP_OPTION_TL_X].name = SANE_NAME_SCAN_TL_X;
345
ps->option[SOAP_OPTION_TL_X].title = SANE_TITLE_SCAN_TL_X;
346
ps->option[SOAP_OPTION_TL_X].desc = SANE_DESC_SCAN_TL_X;
347
ps->option[SOAP_OPTION_TL_X].type = SANE_TYPE_FIXED;
348
ps->option[SOAP_OPTION_TL_X].unit = SANE_UNIT_MM;
349
ps->option[SOAP_OPTION_TL_X].size = sizeof(SANE_Int);
350
ps->option[SOAP_OPTION_TL_X].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
351
ps->option[SOAP_OPTION_TL_X].constraint_type = SANE_CONSTRAINT_RANGE;
352
ps->option[SOAP_OPTION_TL_X].constraint.range = &ps->tlxRange;
353
ps->tlxRange.min = 0;
354
ps->tlxRange.quant = 0;
356
ps->option[SOAP_OPTION_TL_Y].name = SANE_NAME_SCAN_TL_Y;
357
ps->option[SOAP_OPTION_TL_Y].title = SANE_TITLE_SCAN_TL_Y;
358
ps->option[SOAP_OPTION_TL_Y].desc = SANE_DESC_SCAN_TL_Y;
359
ps->option[SOAP_OPTION_TL_Y].type = SANE_TYPE_FIXED;
360
ps->option[SOAP_OPTION_TL_Y].unit = SANE_UNIT_MM;
361
ps->option[SOAP_OPTION_TL_Y].size = sizeof(SANE_Int);
362
ps->option[SOAP_OPTION_TL_Y].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
363
ps->option[SOAP_OPTION_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE;
364
ps->option[SOAP_OPTION_TL_Y].constraint.range = &ps->tlyRange;
365
ps->tlyRange.min = 0;
366
ps->tlyRange.quant = 0;
368
ps->option[SOAP_OPTION_BR_X].name = SANE_NAME_SCAN_BR_X;
369
ps->option[SOAP_OPTION_BR_X].title = SANE_TITLE_SCAN_BR_X;
370
ps->option[SOAP_OPTION_BR_X].desc = SANE_DESC_SCAN_BR_X;
371
ps->option[SOAP_OPTION_BR_X].type = SANE_TYPE_FIXED;
372
ps->option[SOAP_OPTION_BR_X].unit = SANE_UNIT_MM;
373
ps->option[SOAP_OPTION_BR_X].size = sizeof(SANE_Int);
374
ps->option[SOAP_OPTION_BR_X].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
375
ps->option[SOAP_OPTION_BR_X].constraint_type = SANE_CONSTRAINT_RANGE;
376
ps->option[SOAP_OPTION_BR_X].constraint.range = &ps->brxRange;
377
ps->brxRange.min = 0;
378
ps->brxRange.quant = 0;
380
ps->option[SOAP_OPTION_BR_Y].name = SANE_NAME_SCAN_BR_Y;
381
ps->option[SOAP_OPTION_BR_Y].title = SANE_TITLE_SCAN_BR_Y;
382
ps->option[SOAP_OPTION_BR_Y].desc = SANE_DESC_SCAN_BR_Y;
383
ps->option[SOAP_OPTION_BR_Y].type = SANE_TYPE_FIXED;
384
ps->option[SOAP_OPTION_BR_Y].unit = SANE_UNIT_MM;
385
ps->option[SOAP_OPTION_BR_Y].size = sizeof(SANE_Int);
386
ps->option[SOAP_OPTION_BR_Y].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
387
ps->option[SOAP_OPTION_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE;
388
ps->option[SOAP_OPTION_BR_Y].constraint.range = &ps->bryRange;
389
ps->bryRange.min = 0;
390
ps->bryRange.quant = 0;
395
/* Verify current x/y extents and set effective extents. */
396
static int set_extents(struct soap_session *ps)
400
if ((ps->currentBrx > ps->currentTlx) && (ps->currentBrx - ps->currentTlx >= ps->min_width) && (ps->currentBrx - ps->currentTlx <= ps->tlxRange.max))
402
ps->effectiveTlx = ps->currentTlx;
403
ps->effectiveBrx = ps->currentBrx;
407
ps->effectiveTlx = 0; /* current setting is not valid, zero it */
408
ps->effectiveBrx = 0;
411
if ((ps->currentBry > ps->currentTly) && (ps->currentBry - ps->currentTly > ps->min_height) && (ps->currentBry - ps->currentTly <= ps->tlyRange.max))
413
ps->effectiveTly = ps->currentTly;
414
ps->effectiveBry = ps->currentBry;
418
ps->effectiveTly = 0; /* current setting is not valid, zero it */
419
ps->effectiveBry = 0;
429
SANE_Status soap_open(SANE_String_Const device, SANE_Handle *handle)
431
struct hpmud_model_attributes ma;
432
int stat = SANE_STATUS_IO_ERROR, i;
434
DBG8("sane_hpaio_open(%s)\n", device);
438
BUG("session in use\n");
439
return SANE_STATUS_DEVICE_BUSY;
442
if ((session = create_session()) == NULL)
443
return SANE_STATUS_NO_MEM;
445
/* Set session to specified device. */
446
snprintf(session->uri, sizeof(session->uri)-1, "hp:%s", device); /* prepend "hp:" */
448
/* Get actual model attributes from models.dat. */
449
hpmud_query_model(session->uri, &ma);
450
session->scan_type = ma.scantype;
452
if (hpmud_open_device(session->uri, ma.mfp_mode, &session->dd) != HPMUD_R_OK)
454
BUG("unable to open device %s\n", session->uri);
459
return SANE_STATUS_IO_ERROR;
462
if (bb_load(session, "bb_soap.so"))
464
stat = SANE_STATUS_IO_ERROR;
468
/* Init sane option descriptors. */
469
init_options(session);
471
if (session->bb_open(session))
473
stat = SANE_STATUS_IO_ERROR;
477
/* Set supported Scan Modes as determined by bb_open. */
478
soap_control_option(session, SOAP_OPTION_SCAN_MODE, SANE_ACTION_SET_AUTO, NULL, NULL); /* set default option */
480
/* Set supported resolutions. */
482
session->resolutionList[i++] = 75;
483
session->resolutionList[i++] = 100;
484
session->resolutionList[i++] = 150;
485
session->resolutionList[i++] = 200;
486
session->resolutionList[i++] = 300;
487
session->resolutionList[i++] = 600;
488
session->resolutionList[i++] = 1200;
489
session->resolutionList[0] = i-1; /* length of word_list */
490
soap_control_option(session, SOAP_OPTION_SCAN_RESOLUTION, SANE_ACTION_SET_AUTO, NULL, NULL); /* set default option */
492
/* Set supported contrast. */
493
soap_control_option(session, SOAP_OPTION_CONTRAST, SANE_ACTION_SET_AUTO, NULL, NULL); /* set default option */
495
/* Set supported compression. (Note, cm1017 may say it supports MMR, but it doesn't) */
496
soap_control_option(session, SOAP_OPTION_COMPRESSION, SANE_ACTION_SET_AUTO, NULL, NULL); /* set default option */
498
/* Set jpeg quality factor as determined by bb_open. */
499
soap_control_option(session, SOAP_OPTION_JPEG_QUALITY, SANE_ACTION_SET_AUTO, NULL, NULL); /* set default option */
501
/* Set x,y extents. See bb_open */
502
soap_control_option(session, SOAP_OPTION_TL_X, SANE_ACTION_SET_AUTO, NULL, NULL); /* set default option */
503
soap_control_option(session, SOAP_OPTION_TL_Y, SANE_ACTION_SET_AUTO, NULL, NULL); /* set default option */
504
soap_control_option(session, SOAP_OPTION_BR_X, SANE_ACTION_SET_AUTO, NULL, NULL); /* set default option */
505
soap_control_option(session, SOAP_OPTION_BR_Y, SANE_ACTION_SET_AUTO, NULL, NULL); /* set default option */
507
*handle = (SANE_Handle *)session;
509
stat = SANE_STATUS_GOOD;
513
if (stat != SANE_STATUS_GOOD)
519
hpmud_close_device(session->dd);
528
void soap_close(SANE_Handle handle)
530
struct soap_session *ps = (struct soap_session *)handle;
532
DBG8("sane_hpaio_close()\n");
534
if (ps == NULL || ps != session)
536
BUG("invalid sane_close\n");
544
hpmud_close_device(ps->dd);
550
const SANE_Option_Descriptor *soap_get_option_descriptor(SANE_Handle handle, SANE_Int option)
552
struct soap_session *ps = (struct soap_session *)handle;
554
DBG8("sane_hpaio_get_option_descriptor(option=%s)\n", ps->option[option].name);
556
if (option < 0 || option >= SOAP_OPTION_MAX)
559
return &ps->option[option];
560
} /* soap_get_option_descriptor */
562
SANE_Status soap_control_option(SANE_Handle handle, SANE_Int option, SANE_Action action, void *value, SANE_Int *set_result)
564
struct soap_session *ps = (struct soap_session *)handle;
565
SANE_Int *int_value = value, mset_result=0;
566
int i, stat=SANE_STATUS_INVAL;
571
case SOAP_OPTION_COUNT:
572
if (action == SANE_ACTION_GET_VALUE)
574
*int_value = SOAP_OPTION_MAX;
575
stat = SANE_STATUS_GOOD;
578
case SOAP_OPTION_SCAN_MODE:
579
if (action == SANE_ACTION_GET_VALUE)
581
for (i=0; ps->scanModeList[i]; i++)
583
if (ps->currentScanMode == ps->scanModeMap[i])
585
strcpy(value, ps->scanModeList[i]);
586
stat = SANE_STATUS_GOOD;
591
else if (action == SANE_ACTION_SET_VALUE)
593
for (i=0; ps->scanModeList[i]; i++)
595
if (strcasecmp(ps->scanModeList[i], value) == 0)
597
ps->currentScanMode = ps->scanModeMap[i];
598
set_scan_mode_side_effects(ps, ps->currentScanMode);
599
mset_result |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
600
stat = SANE_STATUS_GOOD;
607
ps->currentScanMode = CE_RGB24;
608
set_scan_mode_side_effects(ps, ps->currentScanMode);
609
stat = SANE_STATUS_GOOD;
612
case SOAP_OPTION_SCAN_RESOLUTION:
613
if (action == SANE_ACTION_GET_VALUE)
615
*int_value = ps->currentResolution;
616
stat = SANE_STATUS_GOOD;
618
else if (action == SANE_ACTION_SET_VALUE)
620
for (i=1; i <= ps->resolutionList[0]; i++)
622
if (ps->resolutionList[i] == *int_value)
624
ps->currentResolution = *int_value;
625
mset_result |= SANE_INFO_RELOAD_PARAMS;
626
stat = SANE_STATUS_GOOD;
633
ps->currentResolution = 75;
634
stat = SANE_STATUS_GOOD;
637
case SOAP_OPTION_CONTRAST:
638
if (action == SANE_ACTION_GET_VALUE)
640
*int_value = ps->currentContrast;
641
stat = SANE_STATUS_GOOD;
643
else if (action == SANE_ACTION_SET_VALUE)
645
if (*int_value >= SOAP_CONTRAST_MIN && *int_value <= SOAP_CONTRAST_MAX)
647
ps->currentContrast = *int_value;
648
stat = SANE_STATUS_GOOD;
654
ps->currentContrast = SOAP_CONTRAST_DEFAULT;
655
stat = SANE_STATUS_GOOD;
658
case SOAP_OPTION_COMPRESSION:
659
if (action == SANE_ACTION_GET_VALUE)
661
for (i=0; ps->compressionList[i]; i++)
663
if (ps->currentCompression == ps->compressionMap[i])
665
strcpy(value, ps->compressionList[i]);
666
stat = SANE_STATUS_GOOD;
671
else if (action == SANE_ACTION_SET_VALUE)
673
for (i=0; ps->compressionList[i]; i++)
675
if (strcasecmp(ps->compressionList[i], value) == 0)
677
ps->currentCompression = ps->compressionMap[i];
678
stat = SANE_STATUS_GOOD;
685
ps->currentCompression = SF_JFIF;
686
stat = SANE_STATUS_GOOD;
689
case SOAP_OPTION_JPEG_QUALITY:
690
if (action == SANE_ACTION_GET_VALUE)
692
*int_value = ps->currentJpegQuality;
693
stat = SANE_STATUS_GOOD;
695
else if (action == SANE_ACTION_SET_VALUE)
697
if (*int_value >= MIN_JPEG_COMPRESSION_FACTOR && *int_value <= MAX_JPEG_COMPRESSION_FACTOR)
699
ps->currentJpegQuality = *int_value;
700
stat = SANE_STATUS_GOOD;
706
ps->currentJpegQuality = SAFER_JPEG_COMPRESSION_FACTOR;
707
stat = SANE_STATUS_GOOD;
710
case SOAP_OPTION_TL_X:
711
if (action == SANE_ACTION_GET_VALUE)
713
*int_value = ps->currentTlx;
714
stat = SANE_STATUS_GOOD;
716
else if (action == SANE_ACTION_SET_VALUE)
718
if (*int_value >= ps->tlxRange.min && *int_value <= ps->tlxRange.max)
720
ps->currentTlx = *int_value;
721
mset_result |= SANE_INFO_RELOAD_PARAMS;
722
stat = SANE_STATUS_GOOD;
728
ps->currentTlx = ps->tlxRange.min;
729
stat = SANE_STATUS_GOOD;
732
case SOAP_OPTION_TL_Y:
733
if (action == SANE_ACTION_GET_VALUE)
735
*int_value = ps->currentTly;
736
stat = SANE_STATUS_GOOD;
738
else if (action == SANE_ACTION_SET_VALUE)
740
if (*int_value >= ps->tlyRange.min && *int_value <= ps->tlyRange.max)
743
ps->currentTly = *int_value;
744
mset_result |= SANE_INFO_RELOAD_PARAMS;
745
stat = SANE_STATUS_GOOD;
751
ps->currentTly = ps->tlyRange.min;
752
stat = SANE_STATUS_GOOD;
755
case SOAP_OPTION_BR_X:
756
if (action == SANE_ACTION_GET_VALUE)
758
*int_value = ps->currentBrx;
759
stat = SANE_STATUS_GOOD;
761
else if (action == SANE_ACTION_SET_VALUE)
763
if (*int_value >= ps->brxRange.min && *int_value <= ps->brxRange.max)
765
ps->currentBrx = *int_value;
766
mset_result |= SANE_INFO_RELOAD_PARAMS;
767
stat = SANE_STATUS_GOOD;
773
ps->currentBrx = ps->brxRange.max;
774
stat = SANE_STATUS_GOOD;
777
case SOAP_OPTION_BR_Y:
778
if (action == SANE_ACTION_GET_VALUE)
780
*int_value = ps->currentBry;
781
stat = SANE_STATUS_GOOD;
783
else if (action == SANE_ACTION_SET_VALUE)
785
if (*int_value >= ps->bryRange.min && *int_value <= ps->bryRange.max)
787
ps->currentBry = *int_value;
788
mset_result |= SANE_INFO_RELOAD_PARAMS;
789
stat = SANE_STATUS_GOOD;
795
ps->currentBry = ps->bryRange.max;
796
stat = SANE_STATUS_GOOD;
804
*set_result = mset_result;
806
if (stat != SANE_STATUS_GOOD)
808
BUG("control_option failed: option=%s action=%s\n", ps->option[option].name,
809
action==SANE_ACTION_GET_VALUE ? "get" : action==SANE_ACTION_SET_VALUE ? "set" : "auto");
812
DBG8("sane_hpaio_control_option (option=%s action=%s value=%s)\n", ps->option[option].name,
813
action==SANE_ACTION_GET_VALUE ? "get" : action==SANE_ACTION_SET_VALUE ? "set" : "auto",
814
value ? ps->option[option].type == SANE_TYPE_STRING ? (char *)value : psnprintf(sz, sizeof(sz), "%d", *(int *)value) : "na");
817
} /* soap_control_option */
819
SANE_Status soap_get_parameters(SANE_Handle handle, SANE_Parameters *params)
821
struct soap_session *ps = (struct soap_session *)handle;
825
/* Get scan parameters for sane client. */
826
ps->bb_get_parameters(ps, params, ps->ip_handle ? SPO_STARTED : SPO_BEST_GUESS);
828
DBG8("sane_hpaio_get_parameters(): format=%d, last_frame=%d, lines=%d, depth=%d, pixels_per_line=%d, bytes_per_line=%d\n",
829
params->format, params->last_frame, params->lines, params->depth, params->pixels_per_line, params->bytes_per_line);
831
return SANE_STATUS_GOOD;
832
} /* soap_get_parameters */
834
SANE_Status soap_start(SANE_Handle handle)
836
struct soap_session *ps = (struct soap_session *)handle;
838
IP_IMAGE_TRAITS traits;
839
IP_XFORM_SPEC xforms[IP_MAX_XFORMS], *pXform=xforms;
842
DBG8("sane_hpaio_start()\n");
846
BUG("invalid extents: tlx=%d brx=%d tly=%d bry=%d minwidth=%d minheight%d maxwidth=%d maxheight=%d\n",
847
ps->currentTlx, ps->currentTly, ps->currentBrx, ps->currentBry, ps->min_width, ps->min_height, ps->tlxRange.max, ps->tlyRange.max);
848
stat = SANE_STATUS_INVAL;
853
if (ps->bb_start_scan(ps))
855
stat = SANE_STATUS_IO_ERROR;
858
SendScanEvent(ps->uri, EVENT_START_SCAN_JOB);
859
memset(xforms, 0, sizeof(xforms));
861
/* Setup image-processing pipeline for xform. */
862
if (ps->currentScanMode == CE_RGB24 || ps->currentScanMode == CE_GRAY8)
864
pXform->aXformInfo[IP_JPG_DECODE_FROM_DENALI].dword = 0; /* 0=no */
865
ADD_XFORM(X_JPG_DECODE);
866
pXform->aXformInfo[IP_CNV_COLOR_SPACE_WHICH_CNV].dword = IP_CNV_YCC_TO_SRGB;
867
pXform->aXformInfo[IP_CNV_COLOR_SPACE_GAMMA].dword = 0x00010000;
868
ADD_XFORM(X_CNV_COLOR_SPACE);
871
{ /* Must be BLACK_AND_WHITE1 (Lineart). */
872
pXform->aXformInfo[IP_JPG_DECODE_FROM_DENALI].dword = 0; /* 0=no */
873
ADD_XFORM(X_JPG_DECODE);
874
pXform->aXformInfo[IP_GRAY_2_BI_THRESHOLD].dword = 127;
875
ADD_XFORM(X_GRAY_2_BI);
878
/* Setup x/y cropping for xform. (Actually we let cm1017 do it's own cropping) */
879
pXform->aXformInfo[IP_CROP_LEFT].dword = 0;
880
pXform->aXformInfo[IP_CROP_RIGHT].dword = 0;
881
pXform->aXformInfo[IP_CROP_TOP].dword = 0;
882
pXform->aXformInfo[IP_CROP_MAXOUTROWS].dword = 0;
885
/* Setup x/y padding for xform. (Actually we let cm1017 do it's own padding) */
886
pXform->aXformInfo[IP_PAD_LEFT].dword = 0; /* # of pixels to add to left side */
887
pXform->aXformInfo[IP_PAD_RIGHT].dword = 0; /* # of pixels to add to right side */
888
pXform->aXformInfo[IP_PAD_TOP].dword = 0; /* # of rows to add to top */
889
pXform->aXformInfo[IP_PAD_BOTTOM].dword = 0; /* # of rows to add to bottom */
890
pXform->aXformInfo[IP_PAD_VALUE].dword = ps->currentScanMode == CE_BLACK_AND_WHITE1 ? 0 : -1; /* lineart white = 0, rgb white = -1 */
891
pXform->aXformInfo[IP_PAD_MIN_HEIGHT].dword = 0;
894
/* Open image processor. */
895
if ((ret = ipOpen(pXform-xforms, xforms, 0, &ps->ip_handle)) != IP_DONE)
897
BUG("unable open image processor: err=%d\n", ret);
898
stat = SANE_STATUS_INVAL;
902
/* Set known input image attributes. */
903
ps->bb_get_parameters(ps, &pp, SPO_BEST_GUESS);
904
traits.iPixelsPerRow = pp.pixels_per_line;
905
switch(ps->currentScanMode)
907
case CE_BLACK_AND_WHITE1: /* linart uses 8-bit gray */
909
traits.iBitsPerPixel = 8; /* grayscale */
913
traits.iBitsPerPixel = 24; /* color */
916
traits.lHorizDPI = ps->currentResolution << 16;
917
traits.lVertDPI = ps->currentResolution << 16;
918
traits.lNumRows = pp.lines;
919
traits.iNumPages = 1;
921
traits.iComponentsPerPixel = ((traits.iBitsPerPixel % 3) ? 1 : 3);
922
DBG6("set traits iPixelsPerRow=%d iBitsPerPixel=%d lNumRows=%d iComponentsPerPixel=%d\n", traits.iPixelsPerRow,
923
traits.iBitsPerPixel, (int)traits.lNumRows, traits.iComponentsPerPixel);
924
ipSetDefaultInputTraits(ps->ip_handle, &traits);
926
/* If jpeg get output image attributes from the image processor. */
927
if (ps->currentCompression == SF_JFIF)
929
/* Enable parsed header flag. */
930
ipResultMask(ps->ip_handle, IP_PARSED_HEADER);
932
/* Wait for image processor to process header so we know the exact size of the image for sane_get_params. */
935
ret = get_ip_data(ps, NULL, 0, NULL);
937
if (ret & (IP_INPUT_ERROR | IP_FATAL_ERROR | IP_DONE))
939
BUG("ipConvert error=%x\n", ret);
940
stat = SANE_STATUS_IO_ERROR;
944
if (ret & IP_PARSED_HEADER)
946
ipGetImageTraits(ps->ip_handle, NULL, &ps->image_traits); /* get valid image traits */
947
ipResultMask(ps->ip_handle, 0); /* disable parsed header flag */
953
ipGetImageTraits(ps->ip_handle, NULL, &ps->image_traits); /* get valid image traits */
955
DBG6("act traits iPixelsPerRow=%d iBitsPerPixel=%d lNumRows=%d iComponentsPerPixel=%d\n", ps->image_traits.iPixelsPerRow,
956
ps->image_traits.iBitsPerPixel, (int)ps->image_traits.lNumRows, ps->image_traits.iComponentsPerPixel);
958
stat = SANE_STATUS_GOOD;
961
if (stat != SANE_STATUS_GOOD)
965
ipClose(ps->ip_handle);
968
ps->bb_end_scan(ps, stat == SANE_STATUS_IO_ERROR ? 1: 0);
974
SANE_Status soap_read(SANE_Handle handle, SANE_Byte *data, SANE_Int maxLength, SANE_Int *length)
976
struct soap_session *ps = (struct soap_session *)handle;
977
int ret, stat=SANE_STATUS_IO_ERROR;
979
DBG8("sane_hpaio_read() handle=%p data=%p maxLength=%d\n", (void *)handle, data, maxLength);
982
DBG8("soap_read() EVENT_SCAN_CANCEL****uri=%s\n", ps->uri);
983
SendScanEvent(ps->uri, EVENT_SCAN_CANCEL);
984
return SANE_STATUS_CANCELLED;
987
ret = get_ip_data(ps, data, maxLength, length);
989
if(ret & (IP_INPUT_ERROR | IP_FATAL_ERROR))
991
BUG("ipConvert error=%x\n", ret);
997
stat = SANE_STATUS_EOF;
998
SendScanEvent (ps->uri, EVENT_END_SCAN_JOB);
1001
stat = SANE_STATUS_GOOD;
1004
if (stat != SANE_STATUS_GOOD)
1008
/* 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. */
1009
ipClose(ps->ip_handle);
1012
ps->bb_end_page(ps, 0);
1015
DBG8("-sane_hpaio_read() output=%p bytes_read=%d maxLength=%d status=%d\n", data, *length, maxLength, stat);
1020
void soap_cancel(SANE_Handle handle)
1022
struct soap_session *ps = (struct soap_session *)handle;
1024
DBG8("sane_hpaio_cancel()\n");
1027
* Sane_cancel is always called at the end of the scan job. Note that on a multiple page scan job
1028
* sane_cancel is called only once.
1030
ps->user_cancel = 1;
1033
ipClose(ps->ip_handle);
1036
ps->bb_end_scan(ps, 0);