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 */