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);