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