1 /************************************************************************************\
2
3 soapht.c - HP SANE backend support for soap based multi-function peripherals
4
5 (c) 2006,2008 Copyright HP Development Company, LP
6
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:
13
14 The above copyright notice and this permission notice shall be included in all
15 copies or substantial portions of the Software.
16
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.
23
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.
28
29 Author: David Suffield
30 Contributor: Sarbeswar Meher
31
32 \************************************************************************************/
33
34 #ifndef _GNU_SOURCE
35 #define _GNU_SOURCE
36 #endif
37
38 #include <stdarg.h>
39 #include <syslog.h>
40 #include <stdio.h>
41 #include <string.h>
42 #include <fcntl.h>
43 #include <math.h>
44 #include <dlfcn.h>
45 #include "sane.h"
46 #include "saneopts.h"
47 #include "hpmud.h"
48 #include "hpip.h"
49 #include "common.h"
50 #include "soapht.h"
51 #include "soaphti.h"
52 #include "io.h"
53 #include "utils.h"
54
55 #define DEBUG_DECLARE_ONLY
56 #include "sanei_debug.h"
57
58 static struct soap_session *session = NULL; /* assume one sane_open per process */
59
bb_load(struct soap_session * ps,const char * so)60 static int bb_load(struct soap_session *ps, const char *so)
61 {
62 int stat=1;
63
64 /* Load hpmud manually with symbols exported. Otherwise the plugin will not find it. */
65 if ((ps->hpmud_handle = load_library("libhpmud.so.0")) == NULL)
66 {
67 if ((ps->hpmud_handle = load_library("libhpmud.so.0")) == NULL)
68 goto bugout;
69 }
70
71 /* Load math library manually with symbols exported (Ubuntu 8.04). Otherwise the plugin will not find it. */
72 if ((ps->math_handle = load_library("libm.so")) == NULL)
73 {
74 if ((ps->math_handle = load_library("libm.so.6")) == NULL)
75 goto bugout;
76 }
77
78 if ((ps->bb_handle = load_plugin_library(UTILS_SCAN_PLUGIN_LIBRARY, so)) == NULL)
79 {
80 SendScanEvent(ps->uri, EVENT_PLUGIN_FAIL);
81 goto bugout;
82 }
83
84 if ((ps->bb_open = get_library_symbol(ps->bb_handle, "bb_open")) == NULL)
85 goto bugout;
86
87 if ((ps->bb_close = get_library_symbol(ps->bb_handle, "bb_close")) == NULL)
88 goto bugout;
89
90 if ((ps->bb_get_parameters = get_library_symbol(ps->bb_handle, "bb_get_parameters")) == NULL)
91 goto bugout;
92
93 if ((ps->bb_is_paper_in_adf = get_library_symbol(ps->bb_handle, "bb_is_paper_in_adf")) == NULL)
94 goto bugout;
95
96 if ((ps->bb_start_scan = get_library_symbol(ps->bb_handle, "bb_start_scan")) == NULL)
97 goto bugout;
98
99 if ((ps->bb_end_scan = get_library_symbol(ps->bb_handle, "bb_end_scan")) == NULL)
100 goto bugout;
101
102 if ((ps->bb_get_image_data = get_library_symbol(ps->bb_handle, "bb_get_image_data")) == NULL)
103 goto bugout;
104
105 if ((ps->bb_end_page = get_library_symbol(ps->bb_handle, "bb_end_page")) == NULL)
106 goto bugout;
107
108
109 stat=0;
110
111 bugout:
112 return stat;
113 } /* bb_load */
114
bb_unload(struct soap_session * ps)115 static int bb_unload(struct soap_session *ps)
116 {
117 unload_library(ps->bb_handle);
118 ps->bb_handle = NULL;
119
120 unload_library(ps->hpmud_handle);
121 ps->hpmud_handle = NULL;
122
123 unload_library(ps->math_handle);
124 ps->math_handle = NULL;
125
126 return 0;
127 } /* bb_unload */
128
129 /* Get raw data (ie: uncompressed data) from image processor. */
get_ip_data(struct soap_session * ps,SANE_Byte * data,SANE_Int maxLength,SANE_Int * length)130 static int get_ip_data(struct soap_session *ps, SANE_Byte *data, SANE_Int maxLength, SANE_Int *length)
131 {
132 int ip_ret=IP_INPUT_ERROR;
133 unsigned int outputAvail=maxLength, outputUsed=0, outputThisPos;
134 unsigned char *input, *output = data;
135 unsigned int inputAvail, inputUsed=0, inputNextPos;
136
137 if (!ps->ip_handle)
138 {
139 BUG("invalid ipconvert state\n");
140 goto bugout;
141 }
142
143 if (ps->bb_get_image_data(ps, outputAvail))
144 goto bugout;
145
146 if (ps->cnt > 0)
147 {
148 inputAvail = ps->cnt;
149 input = &ps->buf[ps->index];
150 }
151 else
152 {
153 input = NULL; /* no more scan data, flush ipconvert pipeline */
154 inputAvail = 0;
155 }
156
157 /* Transform input data to output. Note, output buffer may consume more bytes than input buffer (ie: jpeg to raster). */
158 ip_ret = ipConvert(ps->ip_handle, inputAvail, input, &inputUsed, &inputNextPos, outputAvail, output, &outputUsed, &outputThisPos);
159
160 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,
161 inputAvail, inputUsed, inputNextPos, output, outputAvail, outputUsed, outputThisPos);
162
163 if (input != NULL)
164 {
165 if (inputAvail == inputUsed)
166 {
167 ps->index = ps->cnt = 0; /* reset buffer */
168 }
169 else
170 {
171 ps->cnt -= inputUsed; /* save left over buffer for next soap_read */
172 ps->index += inputUsed;
173 }
174 }
175
176 if (data)
177 *length = outputUsed;
178
179 /* For sane do not send output data simultaneously with IP_DONE. */
180 if (ip_ret & IP_DONE && outputUsed)
181 ip_ret &= ~IP_DONE;
182
183 bugout:
184 return ip_ret;
185 } /* get_ip_data */
186
set_scan_mode_side_effects(struct soap_session * ps,enum COLOR_ENTRY scanMode)187 static int set_scan_mode_side_effects(struct soap_session *ps, enum COLOR_ENTRY scanMode)
188 {
189 int j=0;
190
191 memset(ps->compressionList, 0, sizeof(ps->compressionList));
192 memset(ps->compressionMap, 0, sizeof(ps->compressionMap));
193
194 switch (scanMode)
195 {
196 case CE_BLACK_AND_WHITE1: /* same as GRAY8 */
197 case CE_GRAY8:
198 case CE_RGB24:
199 default:
200 ps->compressionList[j] = STR_COMPRESSION_NONE;
201 ps->compressionMap[j++] = SF_HPRAW;
202 ps->compressionList[j] = STR_COMPRESSION_JPEG;
203 ps->compressionMap[j++] = SF_JFIF;
204 ps->currentCompression = SF_JFIF;
205 ps->option[SOAP_OPTION_JPEG_QUALITY].cap |= SANE_CAP_SOFT_SELECT; /* enable jpeg quality */
206 break;
207 }
208
209 return 0;
210 } /* set_scan_mode_side_effects */
211
set_input_source_side_effects(struct soap_session * ps,enum INPUT_SOURCE source)212 static int set_input_source_side_effects(struct soap_session *ps, enum INPUT_SOURCE source)
213 {
214 switch (source)
215 {
216 case IS_PLATEN:
217 ps->min_width = ps->platen_min_width;
218 ps->min_height = ps->platen_min_height;
219 ps->tlxRange.max = ps->platen_tlxRange.max;
220 ps->brxRange.max = ps->platen_brxRange.max;
221 ps->tlyRange.max = ps->platen_tlyRange.max;
222 ps->bryRange.max = ps->platen_bryRange.max;
223 break;
224 case IS_ADF:
225 case IS_ADF_DUPLEX:
226 default:
227 ps->min_width = ps->adf_min_width;
228 ps->min_height = ps->adf_min_height;
229 ps->tlxRange.max = ps->adf_tlxRange.max;
230 ps->brxRange.max = ps->adf_brxRange.max;
231 ps->tlyRange.max = ps->adf_tlyRange.max;
232 ps->bryRange.max = ps->adf_bryRange.max;
233 break;
234 }
235
236 if ((ps->adf_bryRange.max != ps->platen_bryRange.max) || (ps->adf_brxRange.max != ps->platen_brxRange.max))
237 {
238 ps->currentTly = ps->tlyRange.min;
239 ps->currentBrx = ps->brxRange.max;
240 ps->currentTlx = ps->tlxRange.min;
241 ps->currentBry = ps->bryRange.max;
242 }
243
244 return 0;
245 } /* set_input_source_side_effects */
246
init_options(struct soap_session * ps)247 static int init_options(struct soap_session *ps)
248 {
249 ps->option[SOAP_OPTION_COUNT].name = "option-cnt";
250 ps->option[SOAP_OPTION_COUNT].title = SANE_TITLE_NUM_OPTIONS;
251 ps->option[SOAP_OPTION_COUNT].desc = SANE_DESC_NUM_OPTIONS;
252 ps->option[SOAP_OPTION_COUNT].type = SANE_TYPE_INT;
253 ps->option[SOAP_OPTION_COUNT].unit = SANE_UNIT_NONE;
254 ps->option[SOAP_OPTION_COUNT].size = sizeof(SANE_Int);
255 ps->option[SOAP_OPTION_COUNT].cap = SANE_CAP_SOFT_DETECT;
256 ps->option[SOAP_OPTION_COUNT].constraint_type = SANE_CONSTRAINT_NONE;
257
258 ps->option[SOAP_OPTION_GROUP_SCAN_MODE].name = "mode-group";
259 ps->option[SOAP_OPTION_GROUP_SCAN_MODE].title = SANE_TITLE_SCAN_MODE;
260 ps->option[SOAP_OPTION_GROUP_SCAN_MODE].type = SANE_TYPE_GROUP;
261
262 ps->option[SOAP_OPTION_SCAN_MODE].name = SANE_NAME_SCAN_MODE;
263 ps->option[SOAP_OPTION_SCAN_MODE].title = SANE_TITLE_SCAN_MODE;
264 ps->option[SOAP_OPTION_SCAN_MODE].desc = SANE_DESC_SCAN_MODE;
265 ps->option[SOAP_OPTION_SCAN_MODE].type = SANE_TYPE_STRING;
266 ps->option[SOAP_OPTION_SCAN_MODE].unit = SANE_UNIT_NONE;
267 ps->option[SOAP_OPTION_SCAN_MODE].size = MAX_STRING_SIZE;
268 ps->option[SOAP_OPTION_SCAN_MODE].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
269 ps->option[SOAP_OPTION_SCAN_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
270 ps->option[SOAP_OPTION_SCAN_MODE].constraint.string_list = ps->scanModeList;
271
272 ps->option[SOAP_OPTION_INPUT_SOURCE].name = SANE_NAME_SCAN_SOURCE;
273 ps->option[SOAP_OPTION_INPUT_SOURCE].title = SANE_TITLE_SCAN_SOURCE;
274 ps->option[SOAP_OPTION_INPUT_SOURCE].desc = SANE_DESC_SCAN_SOURCE;
275 ps->option[SOAP_OPTION_INPUT_SOURCE].type = SANE_TYPE_STRING;
276 ps->option[SOAP_OPTION_INPUT_SOURCE].unit = SANE_UNIT_NONE;
277 ps->option[SOAP_OPTION_INPUT_SOURCE].size = MAX_STRING_SIZE;
278 ps->option[SOAP_OPTION_INPUT_SOURCE].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
279 ps->option[SOAP_OPTION_INPUT_SOURCE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
280 ps->option[SOAP_OPTION_INPUT_SOURCE].constraint.string_list = ps->inputSourceList;
281
282 ps->option[SOAP_OPTION_SCAN_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION;
283 ps->option[SOAP_OPTION_SCAN_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION;
284 ps->option[SOAP_OPTION_SCAN_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION;
285 ps->option[SOAP_OPTION_SCAN_RESOLUTION].type = SANE_TYPE_INT;
286 ps->option[SOAP_OPTION_SCAN_RESOLUTION].unit = SANE_UNIT_DPI;
287 ps->option[SOAP_OPTION_SCAN_RESOLUTION].size = sizeof(SANE_Int);
288 ps->option[SOAP_OPTION_SCAN_RESOLUTION].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
289 ps->option[SOAP_OPTION_SCAN_RESOLUTION].constraint_type = SANE_CONSTRAINT_WORD_LIST;
290 ps->option[SOAP_OPTION_SCAN_RESOLUTION].constraint.word_list = ps->resolutionList;
291
292 ps->option[SOAP_OPTION_GROUP_ADVANCED].name = "advanced-group";
293 ps->option[SOAP_OPTION_GROUP_ADVANCED].title = STR_TITLE_ADVANCED;
294 ps->option[SOAP_OPTION_GROUP_ADVANCED].type = SANE_TYPE_GROUP;
295 ps->option[SOAP_OPTION_GROUP_ADVANCED].cap = SANE_CAP_ADVANCED;
296
297 ps->option[SOAP_OPTION_BRIGHTNESS].name = SANE_NAME_BRIGHTNESS;
298 ps->option[SOAP_OPTION_BRIGHTNESS].title = SANE_TITLE_BRIGHTNESS;
299 ps->option[SOAP_OPTION_BRIGHTNESS].desc = SANE_DESC_BRIGHTNESS;
300 ps->option[SOAP_OPTION_BRIGHTNESS].type = SANE_TYPE_INT;
301 ps->option[SOAP_OPTION_BRIGHTNESS].unit = SANE_UNIT_NONE;
302 ps->option[SOAP_OPTION_BRIGHTNESS].size = sizeof(SANE_Int);
303 ps->option[SOAP_OPTION_BRIGHTNESS].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED;
304 ps->option[SOAP_OPTION_BRIGHTNESS].constraint_type = SANE_CONSTRAINT_RANGE;
305 ps->option[SOAP_OPTION_BRIGHTNESS].constraint.range = &ps->brightnessRange;
306 ps->brightnessRange.min = SOAP_BRIGHTNESS_MIN;
307 ps->brightnessRange.max = SOAP_BRIGHTNESS_MAX;
308 ps->brightnessRange.quant = 0;
309
310 ps->option[SOAP_OPTION_CONTRAST].name = SANE_NAME_CONTRAST;
311 ps->option[SOAP_OPTION_CONTRAST].title = SANE_TITLE_CONTRAST;
312 ps->option[SOAP_OPTION_CONTRAST].desc = SANE_DESC_CONTRAST;
313 ps->option[SOAP_OPTION_CONTRAST].type = SANE_TYPE_INT;
314 ps->option[SOAP_OPTION_CONTRAST].unit = SANE_UNIT_NONE;
315 ps->option[SOAP_OPTION_CONTRAST].size = sizeof(SANE_Int);
316 ps->option[SOAP_OPTION_CONTRAST].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED;
317 ps->option[SOAP_OPTION_CONTRAST].constraint_type = SANE_CONSTRAINT_RANGE;
318 ps->option[SOAP_OPTION_CONTRAST].constraint.range = &ps->contrastRange;
319 ps->contrastRange.min = SOAP_CONTRAST_MIN;
320 ps->contrastRange.max = SOAP_CONTRAST_MAX;
321 ps->contrastRange.quant = 0;
322
323 ps->option[SOAP_OPTION_COMPRESSION].name = STR_NAME_COMPRESSION;
324 ps->option[SOAP_OPTION_COMPRESSION].title = STR_TITLE_COMPRESSION;
325 ps->option[SOAP_OPTION_COMPRESSION].desc = STR_DESC_COMPRESSION;
326 ps->option[SOAP_OPTION_COMPRESSION].type = SANE_TYPE_STRING;
327 ps->option[SOAP_OPTION_COMPRESSION].unit = SANE_UNIT_NONE;
328 ps->option[SOAP_OPTION_COMPRESSION].size = MAX_STRING_SIZE;
329 ps->option[SOAP_OPTION_COMPRESSION].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED;
330 ps->option[SOAP_OPTION_COMPRESSION].constraint_type = SANE_CONSTRAINT_STRING_LIST;
331 ps->option[SOAP_OPTION_COMPRESSION].constraint.string_list = ps->compressionList;
332
333 ps->option[SOAP_OPTION_JPEG_QUALITY].name = STR_NAME_JPEG_QUALITY;
334 ps->option[SOAP_OPTION_JPEG_QUALITY].title = STR_TITLE_JPEG_QUALITY;
335 ps->option[SOAP_OPTION_JPEG_QUALITY].desc = STR_DESC_JPEG_QUALITY;
336 ps->option[SOAP_OPTION_JPEG_QUALITY].type = SANE_TYPE_INT;
337 ps->option[SOAP_OPTION_JPEG_QUALITY].unit = SANE_UNIT_NONE;
338 ps->option[SOAP_OPTION_JPEG_QUALITY].size = sizeof(SANE_Int);
339 ps->option[SOAP_OPTION_JPEG_QUALITY].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED;
340 ps->option[SOAP_OPTION_JPEG_QUALITY].constraint_type = SANE_CONSTRAINT_RANGE;
341 ps->option[SOAP_OPTION_JPEG_QUALITY].constraint.range = &ps->jpegQualityRange;
342 ps->jpegQualityRange.min = MIN_JPEG_COMPRESSION_FACTOR;
343 ps->jpegQualityRange.max = MAX_JPEG_COMPRESSION_FACTOR;
344 ps->jpegQualityRange.quant = 0;
345
346 ps->option[SOAP_OPTION_GROUP_GEOMETRY].name = "geometry-group";
347 ps->option[SOAP_OPTION_GROUP_GEOMETRY].title = STR_TITLE_GEOMETRY;
348 ps->option[SOAP_OPTION_GROUP_GEOMETRY].type = SANE_TYPE_GROUP;
349 ps->option[SOAP_OPTION_GROUP_GEOMETRY].cap = SANE_CAP_ADVANCED;
350
351 ps->option[SOAP_OPTION_TL_X].name = SANE_NAME_SCAN_TL_X;
352 ps->option[SOAP_OPTION_TL_X].title = SANE_TITLE_SCAN_TL_X;
353 ps->option[SOAP_OPTION_TL_X].desc = SANE_DESC_SCAN_TL_X;
354 ps->option[SOAP_OPTION_TL_X].type = SANE_TYPE_FIXED;
355 ps->option[SOAP_OPTION_TL_X].unit = SANE_UNIT_MM;
356 ps->option[SOAP_OPTION_TL_X].size = sizeof(SANE_Int);
357 ps->option[SOAP_OPTION_TL_X].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
358 ps->option[SOAP_OPTION_TL_X].constraint_type = SANE_CONSTRAINT_RANGE;
359 ps->option[SOAP_OPTION_TL_X].constraint.range = &ps->tlxRange;
360 ps->tlxRange.min = 0;
361 ps->tlxRange.quant = 0;
362
363 ps->option[SOAP_OPTION_TL_Y].name = SANE_NAME_SCAN_TL_Y;
364 ps->option[SOAP_OPTION_TL_Y].title = SANE_TITLE_SCAN_TL_Y;
365 ps->option[SOAP_OPTION_TL_Y].desc = SANE_DESC_SCAN_TL_Y;
366 ps->option[SOAP_OPTION_TL_Y].type = SANE_TYPE_FIXED;
367 ps->option[SOAP_OPTION_TL_Y].unit = SANE_UNIT_MM;
368 ps->option[SOAP_OPTION_TL_Y].size = sizeof(SANE_Int);
369 ps->option[SOAP_OPTION_TL_Y].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
370 ps->option[SOAP_OPTION_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE;
371 ps->option[SOAP_OPTION_TL_Y].constraint.range = &ps->tlyRange;
372 ps->tlyRange.min = 0;
373 ps->tlyRange.quant = 0;
374
375 ps->option[SOAP_OPTION_BR_X].name = SANE_NAME_SCAN_BR_X;
376 ps->option[SOAP_OPTION_BR_X].title = SANE_TITLE_SCAN_BR_X;
377 ps->option[SOAP_OPTION_BR_X].desc = SANE_DESC_SCAN_BR_X;
378 ps->option[SOAP_OPTION_BR_X].type = SANE_TYPE_FIXED;
379 ps->option[SOAP_OPTION_BR_X].unit = SANE_UNIT_MM;
380 ps->option[SOAP_OPTION_BR_X].size = sizeof(SANE_Int);
381 ps->option[SOAP_OPTION_BR_X].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
382 ps->option[SOAP_OPTION_BR_X].constraint_type = SANE_CONSTRAINT_RANGE;
383 ps->option[SOAP_OPTION_BR_X].constraint.range = &ps->brxRange;
384 ps->brxRange.min = 0;
385 ps->brxRange.quant = 0;
386
387 ps->option[SOAP_OPTION_BR_Y].name = SANE_NAME_SCAN_BR_Y;
388 ps->option[SOAP_OPTION_BR_Y].title = SANE_TITLE_SCAN_BR_Y;
389 ps->option[SOAP_OPTION_BR_Y].desc = SANE_DESC_SCAN_BR_Y;
390 ps->option[SOAP_OPTION_BR_Y].type = SANE_TYPE_FIXED;
391 ps->option[SOAP_OPTION_BR_Y].unit = SANE_UNIT_MM;
392 ps->option[SOAP_OPTION_BR_Y].size = sizeof(SANE_Int);
393 ps->option[SOAP_OPTION_BR_Y].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
394 ps->option[SOAP_OPTION_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE;
395 ps->option[SOAP_OPTION_BR_Y].constraint.range = &ps->bryRange;
396 ps->bryRange.min = 0;
397 ps->bryRange.quant = 0;
398
399 return 0;
400 } /* init_options */
401
402 /* Verify current x/y extents and set effective extents. */
set_extents(struct soap_session * ps)403 static int set_extents(struct soap_session *ps)
404 {
405 int stat = 0;
406
407 if ((ps->currentBrx > ps->currentTlx) && (ps->currentBrx - ps->currentTlx >= ps->min_width) && (ps->currentBrx - ps->currentTlx <= ps->tlxRange.max))
408 {
409 ps->effectiveTlx = ps->currentTlx;
410 ps->effectiveBrx = ps->currentBrx;
411 }
412 else
413 {
414 ps->effectiveTlx = 0; /* current setting is not valid, zero it */
415 ps->effectiveBrx = 0;
416 stat = 1;
417 }
418 if ((ps->currentBry > ps->currentTly) && (ps->currentBry - ps->currentTly > ps->min_height) && (ps->currentBry - ps->currentTly <= ps->tlyRange.max))
419 {
420 ps->effectiveTly = ps->currentTly;
421 ps->effectiveBry = ps->currentBry;
422 }
423 else
424 {
425 ps->effectiveTly = 0; /* current setting is not valid, zero it */
426 ps->effectiveBry = 0;
427 stat = 1;
428 }
429 return stat;
430 } /* set_extents */
431
create_session()432 static struct soap_session *create_session()
433 {
434 struct soap_session *ps;
435
436 if ((ps = malloc(sizeof(struct soap_session))) == NULL)
437 {
438 BUG("malloc failed: %m\n");
439 return NULL;
440 }
441 memset(ps, 0, sizeof(struct soap_session));
442 ps->tag = "SOAPHT";
443 ps->dd = -1;
444 ps->cd = -1;
445
446 return ps;
447 } /* create_session */
448
449 /*
450 * SANE APIs.
451 */
452
soapht_open(SANE_String_Const device,SANE_Handle * handle)453 SANE_Status soapht_open(SANE_String_Const device, SANE_Handle *handle)
454 {
455 struct hpmud_model_attributes ma;
456 int i, stat = SANE_STATUS_IO_ERROR;
457
458 DBG8("sane_hpaio_open(%s)\n", device);
459
460 if (session)
461 {
462 BUG("session in use\n");
463 return SANE_STATUS_DEVICE_BUSY;
464 }
465
466 if ((session = create_session()) == NULL)
467 return SANE_STATUS_NO_MEM;
468
469 /* Set session to specified device. */
470 snprintf(session->uri, sizeof(session->uri)-1, "hp:%s", device); /* prepend "hp:" */
471
472 /* Get actual model attributes from models.dat. */
473 hpmud_query_model(session->uri, &ma);
474 session->scan_type = ma.scantype;
475
476 if (hpmud_open_device(session->uri, ma.mfp_mode, &session->dd) != HPMUD_R_OK)
477 {
478 BUG("unable to open device %s\n", session->uri);
479 goto bugout;
480
481 free(session);
482 session = NULL;
483 return SANE_STATUS_IO_ERROR;
484 }
485
486 if (bb_load(session, SCAN_PLUGIN_SOAPHT))
487 {
488 stat = SANE_STATUS_IO_ERROR;
489 goto bugout;
490 }
491
492 /* Init sane option descriptors. */
493 init_options(session);
494
495 if (session->bb_open(session))
496 {
497 stat = SANE_STATUS_IO_ERROR;
498 goto bugout;
499 }
500
501 /* Set supported Scan Modes as determined by bb_open. */
502 soapht_control_option(session, SOAP_OPTION_SCAN_MODE, SANE_ACTION_SET_AUTO, NULL, NULL); /* set default option */
503
504 /* Set scan input sources as determined by bb_open. */
505 soapht_control_option(session, SOAP_OPTION_INPUT_SOURCE, SANE_ACTION_SET_AUTO, NULL, NULL); /* set default option */
506
507 /* Set supported resolutions. */
508 soapht_control_option(session, SOAP_OPTION_SCAN_RESOLUTION, SANE_ACTION_SET_AUTO, NULL, NULL); /* set default option */
509
510 /* Set supported brightness. */
511 soapht_control_option(session, SOAP_OPTION_BRIGHTNESS, SANE_ACTION_SET_AUTO, NULL, NULL); /* set default option */
512
513 /* Set supported contrast. */
514 soapht_control_option(session, SOAP_OPTION_CONTRAST, SANE_ACTION_SET_AUTO, NULL, NULL); /* set default option */
515
516 /* Set supported compression. (Note, cm1017 may say it supports MMR, but it doesn't) */
517 soapht_control_option(session, SOAP_OPTION_COMPRESSION, SANE_ACTION_SET_AUTO, NULL, NULL); /* set default option */
518
519 /* Determine supported jpeg quality factor as determined by bb_open. */
520 soapht_control_option(session, SOAP_OPTION_JPEG_QUALITY, SANE_ACTION_SET_AUTO, NULL, NULL); /* set default option */
521
522 /* Set x,y extents. See bb_open */
523 soapht_control_option(session, SOAP_OPTION_TL_X, SANE_ACTION_SET_AUTO, NULL, NULL); /* set default option */
524 soapht_control_option(session, SOAP_OPTION_TL_Y, SANE_ACTION_SET_AUTO, NULL, NULL); /* set default option */
525 soapht_control_option(session, SOAP_OPTION_BR_X, SANE_ACTION_SET_AUTO, NULL, NULL); /* set default option */
526 soapht_control_option(session, SOAP_OPTION_BR_Y, SANE_ACTION_SET_AUTO, NULL, NULL); /* set default option */
527
528 *handle = (SANE_Handle *)session;
529
530 stat = SANE_STATUS_GOOD;
531
532 bugout:
533
534 if (stat != SANE_STATUS_GOOD)
535 {
536 if (session)
537 {
538 bb_unload(session);
539 if (session->dd > 0)
540 hpmud_close_device(session->dd);
541 free(session);
542 session = NULL;
543 }
544 }
545
546 return stat;
547 } /* saneht_open */
548
soapht_close(SANE_Handle handle)549 void soapht_close(SANE_Handle handle)
550 {
551 struct soap_session *ps = (struct soap_session *)handle;
552
553 DBG8("sane_hpaio_close()\n");
554
555 if (ps == NULL || ps != session)
556 {
557 BUG("invalid sane_close\n");
558 return;
559 }
560
561 ps->bb_close(ps);
562 bb_unload(ps);
563
564 if (ps->dd > 0)
565 hpmud_close_device(ps->dd);
566
567 free(ps);
568 session = NULL;
569 } /* saneht_close */
570
soapht_get_option_descriptor(SANE_Handle handle,SANE_Int option)571 const SANE_Option_Descriptor *soapht_get_option_descriptor(SANE_Handle handle, SANE_Int option)
572 {
573 struct soap_session *ps = (struct soap_session *)handle;
574
575 DBG8("sane_hpaio_get_option_descriptor(option=%s)\n", ps->option[option].name);
576
577 if (option < 0 || option >= SOAP_OPTION_MAX)
578 return NULL;
579
580 return &ps->option[option];
581 } /* soapht_get_option_descriptor */
582
soapht_control_option(SANE_Handle handle,SANE_Int option,SANE_Action action,void * value,SANE_Int * set_result)583 SANE_Status soapht_control_option(SANE_Handle handle, SANE_Int option, SANE_Action action, void *value, SANE_Int *set_result)
584 {
585 struct soap_session *ps = (struct soap_session *)handle;
586 SANE_Int *int_value = value, mset_result=0;
587 int i, stat=SANE_STATUS_INVAL;
588 char sz[64];
589
590 switch(option)
591 {
592 case SOAP_OPTION_COUNT:
593 if (action == SANE_ACTION_GET_VALUE)
594 {
595 *int_value = SOAP_OPTION_MAX;
596 stat = SANE_STATUS_GOOD;
597 }
598 break;
599 case SOAP_OPTION_SCAN_MODE:
600 if (action == SANE_ACTION_GET_VALUE)
601 {
602 for (i=0; ps->scanModeList[i]; i++)
603 {
604 if (ps->currentScanMode == ps->scanModeMap[i])
605 {
606 strcpy(value, ps->scanModeList[i]);
607 stat = SANE_STATUS_GOOD;
608 break;
609 }
610 }
611 }
612 else if (action == SANE_ACTION_SET_VALUE)
613 {
614 for (i=0; ps->scanModeList[i]; i++)
615 {
616 if (strcasecmp(ps->scanModeList[i], value) == 0)
617 {
618 ps->currentScanMode = ps->scanModeMap[i];
619 set_scan_mode_side_effects(ps, ps->currentScanMode);
620 mset_result |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
621 stat = SANE_STATUS_GOOD;
622 break;
623 }
624 }
625 }
626 else
627 { /* Set default. */
628 ps->currentScanMode = ps->scanModeMap[0];
629 set_scan_mode_side_effects(ps, ps->currentScanMode);
630 stat = SANE_STATUS_GOOD;
631 }
632 break;
633 case SOAP_OPTION_INPUT_SOURCE:
634 if (action == SANE_ACTION_GET_VALUE)
635 {
636 for (i=0; ps->inputSourceList[i]; i++)
637 {
638 if (ps->currentInputSource == ps->inputSourceMap[i])
639 {
640 strcpy(value, ps->inputSourceList[i]);
641 stat = SANE_STATUS_GOOD;
642 break;
643 }
644 }
645 }
646 else if (action == SANE_ACTION_SET_VALUE)
647 {
648 for (i=0; ps->inputSourceList[i]; i++)
649 {
650 if (strcasecmp(ps->inputSourceList[i], value) == 0)
651 {
652 ps->currentInputSource = ps->inputSourceMap[i];
653 set_input_source_side_effects(ps, ps->currentInputSource);
654 if(ps->currentInputSource == IS_ADF || ps->currentInputSource == IS_ADF_DUPLEX)
655 {
656 i = ps->adf_resolutionList[0] + 1;
657 while(i--) ps->resolutionList[i] = ps->adf_resolutionList[i];
658 }
659 else //if(ps->currentInputSource == IS_PLATEN)
660 {
661 i = ps->platen_resolutionList[0] + 1;
662 while(i--) ps->resolutionList[i] = ps->platen_resolutionList[i];
663 }
664 mset_result |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
665 stat = SANE_STATUS_GOOD;
666 break;
667 }
668 }
669 }
670 else
671 { /* Set default. */
672 ps->currentInputSource = ps->inputSourceMap[0];
673 set_input_source_side_effects(ps, ps->currentInputSource);
674 mset_result |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
675 stat = SANE_STATUS_GOOD;
676 }
677 break;
678 case SOAP_OPTION_SCAN_RESOLUTION:
679 if (action == SANE_ACTION_GET_VALUE)
680 {
681 *int_value = ps->currentResolution;
682 stat = SANE_STATUS_GOOD;
683 }
684 else if (action == SANE_ACTION_SET_VALUE)
685 {
686 for (i=1; i <= ps->resolutionList[0]; i++)
687 {
688 if (ps->resolutionList[i] == *int_value)
689 {
690 ps->currentResolution = *int_value;
691 mset_result |= SANE_INFO_RELOAD_PARAMS;
692 stat = SANE_STATUS_GOOD;
693 break;
694 }
695 }
696 if (stat != SANE_STATUS_GOOD)
697 {
698 ps->currentResolution = ps->resolutionList[1];
699 stat = SANE_STATUS_GOOD;
700 }
701 }
702 else
703 { /* Set default. */
704 ps->currentResolution = 75;
705 stat = SANE_STATUS_GOOD;
706 }
707 break;
708 case SOAP_OPTION_CONTRAST:
709 if (action == SANE_ACTION_GET_VALUE)
710 {
711 *int_value = ps->currentContrast;
712 stat = SANE_STATUS_GOOD;
713 }
714 else if (action == SANE_ACTION_SET_VALUE)
715 {
716 if (*int_value >= SOAP_CONTRAST_MIN && *int_value <= SOAP_CONTRAST_MAX)
717 {
718 ps->currentContrast = *int_value;
719 }
720 else
721 {
722 ps->currentContrast = SOAP_CONTRAST_DEFAULT;
723 }
724 mset_result |= SANE_INFO_RELOAD_PARAMS;
725 stat = SANE_STATUS_GOOD;
726 }
727 else
728 { /* Set default. */
729 ps->currentContrast = SOAP_CONTRAST_DEFAULT;
730 stat = SANE_STATUS_GOOD;
731 }
732 break;
733 case SOAP_OPTION_BRIGHTNESS:
734 if (action == SANE_ACTION_GET_VALUE)
735 {
736 *int_value = ps->currentBrightness;
737 stat = SANE_STATUS_GOOD;
738 }
739 else if (action == SANE_ACTION_SET_VALUE)
740 {
741 if (*int_value >= SOAP_BRIGHTNESS_MIN && *int_value <= SOAP_BRIGHTNESS_MAX)
742 {
743 ps->currentBrightness = *int_value;
744 }
745 else
746 {
747 ps->currentBrightness = SOAP_BRIGHTNESS_DEFAULT;
748 }
749 stat = SANE_STATUS_GOOD;
750 }
751 else
752 { /* Set default. */
753 ps->currentBrightness = SOAP_BRIGHTNESS_DEFAULT;
754 stat = SANE_STATUS_GOOD;
755 }
756 break;
757 case SOAP_OPTION_COMPRESSION:
758 if (action == SANE_ACTION_GET_VALUE)
759 {
760 for (i=0; ps->compressionList[i]; i++)
761 {
762 if (ps->currentCompression == ps->compressionMap[i])
763 {
764 strcpy(value, ps->compressionList[i]);
765 stat = SANE_STATUS_GOOD;
766 break;
767 }
768 }
769 }
770 else if (action == SANE_ACTION_SET_VALUE)
771 {
772 for (i=0; ps->compressionList[i]; i++)
773 {
774 if (strcasecmp(ps->compressionList[i], value) == 0)
775 {
776 ps->currentCompression = ps->compressionMap[i];
777 stat = SANE_STATUS_GOOD;
778 break;
779 }
780 }
781 }
782 else
783 { /* Set default. */
784 ps->currentCompression = SF_JFIF;
785 stat = SANE_STATUS_GOOD;
786 }
787 break;
788 case SOAP_OPTION_JPEG_QUALITY:
789 if (action == SANE_ACTION_GET_VALUE)
790 {
791 *int_value = ps->currentJpegQuality;
792 stat = SANE_STATUS_GOOD;
793 }
794 else if (action == SANE_ACTION_SET_VALUE)
795 {
796 if (*int_value >= MIN_JPEG_COMPRESSION_FACTOR && *int_value <= MAX_JPEG_COMPRESSION_FACTOR)
797 {
798 ps->currentJpegQuality = *int_value;
799 stat = SANE_STATUS_GOOD;
800 break;
801 }
802 }
803 else
804 { /* Set default. */
805 ps->currentJpegQuality = SAFER_JPEG_COMPRESSION_FACTOR;
806 stat = SANE_STATUS_GOOD;
807 }
808 break;
809 case SOAP_OPTION_TL_X:
810 if (action == SANE_ACTION_GET_VALUE)
811 {
812 *int_value = ps->currentTlx;
813 stat = SANE_STATUS_GOOD;
814 }
815 else if (action == SANE_ACTION_SET_VALUE)
816 {
817 if (*int_value >= ps->tlxRange.min && *int_value <= ps->tlxRange.max)
818 {
819 ps->currentTlx = *int_value;
820 mset_result |= SANE_INFO_RELOAD_PARAMS;
821 stat = SANE_STATUS_GOOD;
822 break;
823 }
824 }
825 else
826 { /* Set default. */
827 ps->currentTlx = ps->tlxRange.min;
828 stat = SANE_STATUS_GOOD;
829 }
830 break;
831 case SOAP_OPTION_TL_Y:
832 if (action == SANE_ACTION_GET_VALUE)
833 {
834 *int_value = ps->currentTly;
835 stat = SANE_STATUS_GOOD;
836 }
837 else if (action == SANE_ACTION_SET_VALUE)
838 {
839 if (*int_value >= ps->tlyRange.min && *int_value <= ps->tlyRange.max)
840 {
841
842 ps->currentTly = *int_value;
843 mset_result |= SANE_INFO_RELOAD_PARAMS;
844 stat = SANE_STATUS_GOOD;
845 break;
846 }
847 }
848 else
849 { /* Set default. */
850 ps->currentTly = ps->tlyRange.min;
851 stat = SANE_STATUS_GOOD;
852 }
853 break;
854 case SOAP_OPTION_BR_X:
855 if (action == SANE_ACTION_GET_VALUE)
856 {
857 *int_value = ps->currentBrx;
858 stat = SANE_STATUS_GOOD;
859 }
860 else if (action == SANE_ACTION_SET_VALUE)
861 {
862 if (*int_value >= ps->brxRange.min && *int_value <= ps->brxRange.max)
863 {
864 ps->currentBrx = *int_value;
865 mset_result |= SANE_INFO_RELOAD_PARAMS;
866 stat = SANE_STATUS_GOOD;
867 break;
868 }
869 }
870 else
871 { /* Set default. */
872 ps->currentBrx = ps->brxRange.max;
873 stat = SANE_STATUS_GOOD;
874 }
875 break;
876 case SOAP_OPTION_BR_Y:
877 if (action == SANE_ACTION_GET_VALUE)
878 {
879 *int_value = ps->currentBry;
880 stat = SANE_STATUS_GOOD;
881 }
882 else if (action == SANE_ACTION_SET_VALUE)
883 {
884 if (*int_value >= ps->bryRange.min && *int_value <= ps->bryRange.max)
885 {
886 ps->currentBry = *int_value;
887 mset_result |= SANE_INFO_RELOAD_PARAMS;
888 stat = SANE_STATUS_GOOD;
889 break;
890 }
891 }
892 else
893 { /* Set default. */
894 ps->currentBry = ps->bryRange.max;
895 stat = SANE_STATUS_GOOD;
896 }
897 break;
898 default:
899 break;
900 }
901
902 if (set_result)
903 *set_result = mset_result;
904
905 if (stat != SANE_STATUS_GOOD)
906 {
907 BUG("control_option failed: option=%s action=%s\n", ps->option[option].name,
908 action==SANE_ACTION_GET_VALUE ? "get" : action==SANE_ACTION_SET_VALUE ? "set" : "auto");
909 }
910
911 DBG8("sane_hpaio_control_option (option=%s action=%s value=%s)\n", ps->option[option].name,
912 action==SANE_ACTION_GET_VALUE ? "get" : action==SANE_ACTION_SET_VALUE ? "set" : "auto",
913 value ? ps->option[option].type == SANE_TYPE_STRING ? (char *)value : psnprintf(sz, sizeof(sz), "%d", *(int *)value) : "na");
914
915 return stat;
916 } /* soapht_control_option */
917
soapht_get_parameters(SANE_Handle handle,SANE_Parameters * params)918 SANE_Status soapht_get_parameters(SANE_Handle handle, SANE_Parameters *params)
919 {
920 struct soap_session *ps = (struct soap_session *)handle;
921
922 set_extents(ps);
923
924 /* Get scan parameters for sane client. */
925 ps->bb_get_parameters(ps, params, ps->ip_handle ? SPO_STARTED : SPO_BEST_GUESS);
926
927 DBG8("sane_hpaio_get_parameters(): format=%d, last_frame=%d, lines=%d, depth=%d, pixels_per_line=%d, bytes_per_line=%d\n",
928 params->format, params->last_frame, params->lines, params->depth, params->pixels_per_line, params->bytes_per_line);
929
930 return SANE_STATUS_GOOD;
931 } /* soapht_get_parameters */
932
soapht_start(SANE_Handle handle)933 SANE_Status soapht_start(SANE_Handle handle)
934 {
935 struct soap_session *ps = (struct soap_session *)handle;
936 SANE_Parameters pp;
937 IP_IMAGE_TRAITS traits;
938 IP_XFORM_SPEC xforms[IP_MAX_XFORMS], *pXform=xforms;
939 int stat, ret;
940
941 DBG8("sane_hpaio_start()\n");
942
943 ps -> user_cancel = 0;
944 ps -> cnt = 0;
945 ps -> index = 0;
946
947 if (set_extents(ps))
948 {
949 BUG("invalid extents: tlx=%d brx=%d tly=%d bry=%d minwidth=%d minheight%d maxwidth=%d maxheight=%d\n",
950 ps->currentTlx, ps->currentTly, ps->currentBrx, ps->currentBry, ps->min_width, ps->min_height, ps->tlxRange.max, ps->tlyRange.max);
951 stat = SANE_STATUS_INVAL;
952 goto bugout;
953 }
954
955 /* If input is ADF and ADF is empty, return SANE_STATUS_NO_DOCS. */
956 if (ps->currentInputSource==IS_ADF || ps->currentInputSource==IS_ADF_DUPLEX)
957 {
958 ret = ps->bb_is_paper_in_adf(ps); /* 0 = no paper in adf, 1 = paper in adf, -1 = error */
959 if (ret == 0)
960 {
961 stat = SANE_STATUS_NO_DOCS; /* done scanning */
962 SendScanEvent (ps->uri, EVENT_SCAN_ADF_NO_DOCS);
963 goto bugout;
964 }
965 else if (ret < 0)
966 {
967 stat = SANE_STATUS_IO_ERROR;
968 goto bugout;
969 }
970 }
971
972 /* Start scan and get actual image traits. */
973 if (ps->bb_start_scan(ps))
974 {
975 stat = SANE_STATUS_IO_ERROR;
976 goto bugout;
977 }
978 SendScanEvent(ps->uri, EVENT_START_SCAN_JOB);
979 memset(xforms, 0, sizeof(xforms));
980
981 /* Setup image-processing pipeline for xform. */
982 if (ps->currentScanMode == CE_RGB24 || ps->currentScanMode == CE_GRAY8)
983 {
984 switch(ps->currentCompression)
985 {
986 case SF_JFIF:
987 pXform->aXformInfo[IP_JPG_DECODE_FROM_DENALI].dword = 0; /* 0=no */
988 ADD_XFORM(X_JPG_DECODE);
989 pXform->aXformInfo[IP_CNV_COLOR_SPACE_WHICH_CNV].dword = IP_CNV_YCC_TO_SRGB;
990 pXform->aXformInfo[IP_CNV_COLOR_SPACE_GAMMA].dword = 0x00010000;
991 ADD_XFORM(X_CNV_COLOR_SPACE);
992 break;
993 case SF_HPRAW:
994 default:
995 break;
996 }
997 }
998 else
999 { /* Must be BLACK_AND_WHITE1 (Lineart). */
1000 switch(ps->currentCompression)
1001 {
1002 case SF_JFIF:
1003 pXform->aXformInfo[IP_JPG_DECODE_FROM_DENALI].dword = 0; /* 0=no */
1004 ADD_XFORM(X_JPG_DECODE);
1005 pXform->aXformInfo[IP_GRAY_2_BI_THRESHOLD].dword = 127;
1006 ADD_XFORM(X_GRAY_2_BI);
1007 break;
1008 case SF_HPRAW:
1009 pXform->aXformInfo[IP_GRAY_2_BI_THRESHOLD].dword = 127;
1010 ADD_XFORM(X_GRAY_2_BI);
1011 default:
1012 break;
1013 }
1014 }
1015
1016 /* Setup x/y cropping for xform. (Actually we let cm1017 do it's own cropping) */
1017 pXform->aXformInfo[IP_CROP_LEFT].dword = 0;
1018 pXform->aXformInfo[IP_CROP_RIGHT].dword = 0;
1019 pXform->aXformInfo[IP_CROP_TOP].dword = 0;
1020 pXform->aXformInfo[IP_CROP_MAXOUTROWS].dword = 0;
1021 ADD_XFORM(X_CROP);
1022
1023 /* Setup x/y padding for xform. (Actually we let cm1017 do it's own padding) */
1024 pXform->aXformInfo[IP_PAD_LEFT].dword = 0; /* # of pixels to add to left side */
1025 pXform->aXformInfo[IP_PAD_RIGHT].dword = 0; /* # of pixels to add to right side */
1026 pXform->aXformInfo[IP_PAD_TOP].dword = 0; /* # of rows to add to top */
1027 pXform->aXformInfo[IP_PAD_BOTTOM].dword = 0; /* # of rows to add to bottom */
1028 pXform->aXformInfo[IP_PAD_VALUE].dword = ps->currentScanMode == CE_BLACK_AND_WHITE1 ? 0 : -1; /* lineart white = 0, rgb white = -1 */
1029 pXform->aXformInfo[IP_PAD_MIN_HEIGHT].dword = 0;
1030 ADD_XFORM(X_PAD);
1031
1032 /* Open image processor. */
1033 if ((ret = ipOpen(pXform-xforms, xforms, 0, &ps->ip_handle)) != IP_DONE)
1034 {
1035 BUG("unable open image processor: err=%d\n", ret);
1036 stat = SANE_STATUS_INVAL;
1037 goto bugout;
1038 }
1039
1040 /* Get scan parameters for image processor. */
1041 if (ps->currentCompression == SF_HPRAW)
1042 ps->bb_get_parameters(ps, &pp, SPO_STARTED_JR); /* hpraw, use actual parameters */
1043 else
1044 ps->bb_get_parameters(ps, &pp, SPO_BEST_GUESS); /* jpeg, use best guess */
1045 traits.iPixelsPerRow = pp.pixels_per_line;
1046 switch(ps->currentScanMode)
1047 {
1048 case CE_BLACK_AND_WHITE1: /* lineart (let IP create Mono from Gray8) */
1049 case CE_GRAY8:
1050 traits.iBitsPerPixel = 8; /* grayscale */
1051 break;
1052 case CE_RGB24:
1053 default:
1054 traits.iBitsPerPixel = 24; /* color */
1055 break;
1056 }
1057 traits.lHorizDPI = ps->currentResolution << 16;
1058 traits.lVertDPI = ps->currentResolution << 16;
1059 traits.lNumRows = pp.lines;
1060 traits.iNumPages = 1;
1061 traits.iPageNum = 1;
1062 traits.iComponentsPerPixel = ((traits.iBitsPerPixel % 3) ? 1 : 3);
1063 ipSetDefaultInputTraits(ps->ip_handle, &traits);
1064
1065 /* If jpeg get output image attributes from the image processor. */
1066 if (ps->currentCompression == SF_JFIF)
1067 {
1068 /* Enable parsed header flag. */
1069 ipResultMask(ps->ip_handle, IP_PARSED_HEADER);
1070
1071 /* Wait for image processor to process header so we know the exact size of the image for sane_get_params. */
1072 while (1)
1073 {
1074 ret = get_ip_data(ps, NULL, 0, NULL);
1075
1076 if (ret & (IP_INPUT_ERROR | IP_FATAL_ERROR | IP_DONE))
1077 {
1078 BUG("ipConvert error=%x\n", ret);
1079 stat = SANE_STATUS_IO_ERROR;
1080 goto bugout;
1081 }
1082
1083 if (ret & IP_PARSED_HEADER)
1084 {
1085 ipGetImageTraits(ps->ip_handle, NULL, &ps->image_traits); /* get valid image traits */
1086 ipResultMask(ps->ip_handle, 0); /* disable parsed header flag */
1087 break;
1088 }
1089 }
1090 }
1091 else
1092 ipGetImageTraits(ps->ip_handle, NULL, &ps->image_traits); /* get valid image traits */
1093
1094 stat = SANE_STATUS_GOOD;
1095
1096 bugout:
1097 if (stat != SANE_STATUS_GOOD)
1098 {
1099 if (ps->ip_handle)
1100 {
1101 ipClose(ps->ip_handle);
1102 ps->ip_handle = 0;
1103 }
1104 ps->bb_end_scan(ps, stat == SANE_STATUS_IO_ERROR ? 1: 0);
1105 }
1106
1107 return stat;
1108 } /* soapht_start */
1109
soapht_read(SANE_Handle handle,SANE_Byte * data,SANE_Int maxLength,SANE_Int * length)1110 SANE_Status soapht_read(SANE_Handle handle, SANE_Byte *data, SANE_Int maxLength, SANE_Int *length)
1111 {
1112 struct soap_session *ps = (struct soap_session *)handle;
1113 int ret, stat=SANE_STATUS_IO_ERROR;
1114
1115 DBG8("sane_hpaio_read() handle=%p data=%p maxLength=%d\n", (void *)handle, data, maxLength);
1116 if(ps->user_cancel)
1117 {
1118 DBG8("soapht_read() EVENT_SCAN_CANCEL****uri=%s\n", ps->uri);
1119 SendScanEvent(ps->uri, EVENT_SCAN_CANCEL);
1120 return SANE_STATUS_CANCELLED;
1121 }
1122
1123 ret = get_ip_data(ps, data, maxLength, length);
1124
1125 if(ret & (IP_INPUT_ERROR | IP_FATAL_ERROR))
1126 {
1127 BUG("ipConvert error=%x\n", ret);
1128 goto bugout;
1129 }
1130
1131 if (ret & IP_DONE)
1132 {
1133 stat = SANE_STATUS_EOF;
1134 SendScanEvent(ps->uri, EVENT_END_SCAN_JOB);
1135 }
1136 else
1137 stat = SANE_STATUS_GOOD;
1138
1139 bugout:
1140 if (stat != SANE_STATUS_GOOD)
1141 {
1142 if (ps->ip_handle)
1143 {
1144 /* 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. */
1145 ipClose(ps->ip_handle);
1146 ps->ip_handle = 0;
1147 }
1148 ps->bb_end_page(ps, 0);
1149 }
1150
1151 DBG8("-sane_hpaio_read() output=%p bytes_read=%d maxLength=%d status=%d\n", data, *length, maxLength, stat);
1152
1153 return stat;
1154 } /* soapht_read */
1155
soapht_cancel(SANE_Handle handle)1156 void soapht_cancel(SANE_Handle handle)
1157 {
1158 struct soap_session *ps = (struct soap_session *)handle;
1159
1160 DBG8("sane_hpaio_cancel()\n");
1161
1162 /*
1163 * Sane_cancel is always called at the end of the scan job. Note that on a multiple page scan job
1164 * sane_cancel is called only once.
1165 */
1166 ps -> user_cancel = 1;
1167 if (ps->ip_handle)
1168 {
1169 ipClose(ps->ip_handle);
1170 ps->ip_handle = 0;
1171 }
1172 ps->bb_end_scan(ps, 0);
1173 } /* soapht_cancel */
1174
1175