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