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