Tizen 2.1 base
[platform/upstream/hplip.git] / scan / sane / ledm.c
1 /************************************************************************************
2
3   ledm.c - HP SANE backend support for LEDM based multi-function peripherals
4   (c) 2010 Copyright Hewlett-Packard Development Company, LP
5
6   Permission is hereby granted, free of charge, to any person obtaining a copy 
7   of this software and associated documentation files (the "Software"), to deal 
8   in the Software without restriction, including without limitation the rights 
9   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
10   of the Software, and to permit persons to whom the Software is furnished to do 
11   so, subject to the following conditions:
12
13   The above copyright notice and this permission notice shall be included in all
14   copies or substantial portions of the Software.
15
16   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
17   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 
18   FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 
19   COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 
20   IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 
21   WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
23   Primary Author: Naga Samrat Chowary, Narla 
24   Contributing Authors: Yashwant Kumar Sahu,Sarbeswar Meher
25
26 *************************************************************************************/
27
28 # ifndef _GNU_SOURCE
29 # define _GNU_SOURCE
30 # endif
31
32 # include <stdio.h>
33 # include <syslog.h>
34 # include <string.h>
35 # include <unistd.h>
36 # include <fcntl.h>
37 # include <dlfcn.h>
38 # include "common.h"
39 # include "hpmud.h"
40 # include "hpip.h"
41 # include "ledm.h"
42 # include "ledmi.h"
43 # include "sane.h"
44 # include "saneopts.h"
45 # include "io.h"
46
47 # define DEBUG_DECLARE_ONLY
48 # include "sanei_debug.h"
49
50 static struct ledm_session *session = NULL;
51
52 /* Verify current x/y extents and set effective extents. */ 
53 static int set_extents(struct ledm_session *ps)
54 {
55   int stat = 0;
56
57   if ((ps->currentBrx > ps->currentTlx) && (ps->currentBrx - ps->currentTlx >= ps->min_width) && (ps->currentBrx - ps->currentTlx <= ps->tlxRange.max))
58   {
59     ps->effectiveTlx = ps->currentTlx;
60     ps->effectiveBrx = ps->currentBrx;
61   }
62   else
63   {
64     ps->effectiveTlx = 0;  /* current setting is not valid, zero it */
65     ps->effectiveBrx = 0;
66     stat = 1;
67   }
68   if ((ps->currentBry > ps->currentTly) && (ps->currentBry - ps->currentTly > ps->min_height) && (ps->currentBry - ps->currentTly <= ps->tlyRange.max))
69   {
70     ps->effectiveTly = ps->currentTly;
71     ps->effectiveBry = ps->currentBry;
72   }
73   else
74   {
75     ps->effectiveTly = 0;  /* current setting is not valid, zero it */
76     ps->effectiveBry = 0;
77     stat = 1;
78   }
79   return stat;
80 } /* set_extents */
81
82 static struct ledm_session *create_session()
83 {
84   struct ledm_session *ps;
85
86   if ((ps = malloc(sizeof(struct ledm_session))) == NULL)
87   {
88     return NULL;
89   }
90   memset(ps, 0, sizeof(struct ledm_session));
91   ps->tag = "LEDM";
92   ps->dd = -1;
93   ps->cd = -1;
94   ps->job_id = 0;
95   ps->page_id = 0;
96   return ps;
97 }
98
99 /* Get raw data (ie: uncompressed data) from image processor. */
100 static int get_ip_data(struct ledm_session *ps, SANE_Byte *data, SANE_Int maxLength, SANE_Int *length)
101 {
102   int ip_ret=IP_INPUT_ERROR;
103   unsigned int outputAvail=maxLength, outputUsed=0, outputThisPos;
104   unsigned char *input, *output = data;
105   unsigned int inputAvail, inputUsed=0, inputNextPos;
106
107   if (!ps->ip_handle)
108   {
109     goto bugout;
110   }      
111
112   int eof;
113
114   eof=bb_get_image_data(ps, outputAvail);
115
116   if (ps->cnt > 0)
117   {
118     inputAvail = ps->cnt;
119     input = &ps->buf[ps->index];
120   }
121   else
122   {
123     input = NULL;
124     inputAvail = 0;
125   }
126
127
128    /* Transform input data to output. Note, output buffer may consume more bytes than input buffer (ie: jpeg to raster). */
129   ip_ret = ipConvert(ps->ip_handle, inputAvail, input, &inputUsed, &inputNextPos, outputAvail, output, &outputUsed, &outputThisPos);
130
131
132   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, 
133     inputAvail, inputUsed, inputNextPos, output, outputAvail, outputUsed, outputThisPos);
134
135   if (input != NULL)
136    {
137       if (inputAvail == inputUsed)
138       {
139          ps->index = ps->cnt = 0;   //
140       }
141       else
142       {
143          ps->cnt -= inputUsed;    // save left over buffer for next soap_read
144          ps->index += inputUsed;
145       }
146    }
147
148    if (data)
149       *length = outputUsed;
150
151   /* For sane do not send output data simultaneously with IP_DONE. */
152    if (ip_ret & IP_DONE && outputUsed)
153       ip_ret &= ~IP_DONE;                               
154
155 bugout:
156    return ip_ret;
157 } /* get_ip_data */
158
159
160 static int set_scan_mode_side_effects(struct ledm_session *ps, enum COLOR_ENTRY scanMode)
161 {
162    int j=0;
163
164    memset(ps->compressionList, 0, sizeof(ps->compressionList));
165    memset(ps->compressionMap, 0, sizeof(ps->compressionMap));
166
167    switch (scanMode)
168    {
169       case CE_K1:         /* same as GRAY8 */
170       case CE_GRAY8:
171       case CE_COLOR8:
172       default:
173 //         ps->compressionList[j] = STR_COMPRESSION_NONE;
174 //         ps->compressionMap[j++] = SF_RAW;
175          ps->compressionList[j] = STR_COMPRESSION_JPEG;
176          ps->compressionMap[j++] = SF_JPEG;
177          ps->currentCompression = SF_JPEG;
178          ps->option[LEDM_OPTION_JPEG_QUALITY].cap |= SANE_CAP_SOFT_SELECT;   /* enable jpeg quality */
179          break;
180    }
181
182    return 0;
183 } /* set_scan_mode_side_effects */
184
185 static int set_input_source_side_effects(struct ledm_session *ps, enum INPUT_SOURCE source)
186 {
187    switch (source)
188    {
189       case IS_PLATEN:
190          ps->min_width = ps->platen_min_width;
191          ps->min_height = ps->platen_min_height;
192          ps->tlxRange.max = ps->platen_tlxRange.max;
193          ps->brxRange.max = ps->platen_brxRange.max;
194          ps->tlyRange.max = ps->platen_tlyRange.max;
195          ps->bryRange.max = ps->platen_bryRange.max;
196          break;
197       case IS_ADF:
198       case IS_ADF_DUPLEX:
199       default:
200          ps->min_width = ps->adf_min_width;
201          ps->min_height = ps->adf_min_height;
202          ps->tlxRange.max = ps->adf_tlxRange.max;
203          ps->brxRange.max = ps->adf_brxRange.max;
204          ps->tlyRange.max = ps->adf_tlyRange.max;
205          ps->bryRange.max = ps->adf_bryRange.max;
206          break;
207    }
208
209    return 0;
210 } /* set_input_source_side_effects */
211
212 static int init_options(struct ledm_session *ps)
213 {
214   ps->option[LEDM_OPTION_COUNT].name = "option-cnt";
215   ps->option[LEDM_OPTION_COUNT].title = SANE_TITLE_NUM_OPTIONS;
216   ps->option[LEDM_OPTION_COUNT].desc = SANE_DESC_NUM_OPTIONS;
217   ps->option[LEDM_OPTION_COUNT].type = SANE_TYPE_INT;
218   ps->option[LEDM_OPTION_COUNT].unit = SANE_UNIT_NONE;
219   ps->option[LEDM_OPTION_COUNT].size = sizeof(SANE_Int);
220   ps->option[LEDM_OPTION_COUNT].cap = SANE_CAP_SOFT_DETECT;
221   ps->option[LEDM_OPTION_COUNT].constraint_type = SANE_CONSTRAINT_NONE;
222
223   ps->option[LEDM_OPTION_GROUP_SCAN_MODE].name = "mode-group";
224   ps->option[LEDM_OPTION_GROUP_SCAN_MODE].title = SANE_TITLE_SCAN_MODE;
225   ps->option[LEDM_OPTION_GROUP_SCAN_MODE].type = SANE_TYPE_GROUP;
226
227   ps->option[LEDM_OPTION_SCAN_MODE].name = SANE_NAME_SCAN_MODE;
228   ps->option[LEDM_OPTION_SCAN_MODE].title = SANE_TITLE_SCAN_MODE;
229   ps->option[LEDM_OPTION_SCAN_MODE].desc = SANE_DESC_SCAN_MODE;
230   ps->option[LEDM_OPTION_SCAN_MODE].type = SANE_TYPE_STRING;
231   ps->option[LEDM_OPTION_SCAN_MODE].unit = SANE_UNIT_NONE;
232   ps->option[LEDM_OPTION_SCAN_MODE].size = MAX_STRING_SIZE;
233   ps->option[LEDM_OPTION_SCAN_MODE].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
234   ps->option[LEDM_OPTION_SCAN_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
235   ps->option[LEDM_OPTION_SCAN_MODE].constraint.string_list = ps->scanModeList;
236
237   ps->option[LEDM_OPTION_INPUT_SOURCE].name = SANE_NAME_SCAN_SOURCE;
238   ps->option[LEDM_OPTION_INPUT_SOURCE].title = SANE_TITLE_SCAN_SOURCE;
239   ps->option[LEDM_OPTION_INPUT_SOURCE].desc = SANE_DESC_SCAN_SOURCE;
240   ps->option[LEDM_OPTION_INPUT_SOURCE].type = SANE_TYPE_STRING;
241   ps->option[LEDM_OPTION_INPUT_SOURCE].unit = SANE_UNIT_NONE;
242   ps->option[LEDM_OPTION_INPUT_SOURCE].size = MAX_STRING_SIZE;
243   ps->option[LEDM_OPTION_INPUT_SOURCE].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
244   ps->option[LEDM_OPTION_INPUT_SOURCE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
245   ps->option[LEDM_OPTION_INPUT_SOURCE].constraint.string_list = ps->inputSourceList;
246
247   ps->option[LEDM_OPTION_SCAN_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION;
248   ps->option[LEDM_OPTION_SCAN_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION;
249   ps->option[LEDM_OPTION_SCAN_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION;
250   ps->option[LEDM_OPTION_SCAN_RESOLUTION].type = SANE_TYPE_INT;
251   ps->option[LEDM_OPTION_SCAN_RESOLUTION].unit = SANE_UNIT_DPI;
252   ps->option[LEDM_OPTION_SCAN_RESOLUTION].size = sizeof(SANE_Int);
253   ps->option[LEDM_OPTION_SCAN_RESOLUTION].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
254   ps->option[LEDM_OPTION_SCAN_RESOLUTION].constraint_type = SANE_CONSTRAINT_WORD_LIST;
255   ps->option[LEDM_OPTION_SCAN_RESOLUTION].constraint.word_list = ps->resolutionList;
256
257   ps->option[LEDM_OPTION_GROUP_ADVANCED].name = "advanced-group";
258   ps->option[LEDM_OPTION_GROUP_ADVANCED].title = STR_TITLE_ADVANCED;
259   ps->option[LEDM_OPTION_GROUP_ADVANCED].type = SANE_TYPE_GROUP;
260   ps->option[LEDM_OPTION_GROUP_ADVANCED].cap = SANE_CAP_ADVANCED;
261
262   ps->option[LEDM_OPTION_CONTRAST].name = SANE_NAME_CONTRAST;
263   ps->option[LEDM_OPTION_CONTRAST].title = SANE_TITLE_CONTRAST;
264   ps->option[LEDM_OPTION_CONTRAST].desc = SANE_DESC_CONTRAST;
265   ps->option[LEDM_OPTION_CONTRAST].type = SANE_TYPE_INT;
266   ps->option[LEDM_OPTION_CONTRAST].unit = SANE_UNIT_NONE;
267   ps->option[LEDM_OPTION_CONTRAST].size = sizeof(SANE_Int);
268   ps->option[LEDM_OPTION_CONTRAST].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED;
269   ps->option[LEDM_OPTION_CONTRAST].constraint_type = SANE_CONSTRAINT_RANGE;
270   ps->option[LEDM_OPTION_CONTRAST].constraint.range = &ps->contrastRange;
271   ps->contrastRange.min = LEDM_CONTRAST_MIN;
272   ps->contrastRange.max = LEDM_CONTRAST_MAX;
273   ps->contrastRange.quant = 0;
274
275   ps->option[LEDM_OPTION_COMPRESSION].name = STR_NAME_COMPRESSION;
276   ps->option[LEDM_OPTION_COMPRESSION].title = STR_TITLE_COMPRESSION;
277   ps->option[LEDM_OPTION_COMPRESSION].desc = STR_DESC_COMPRESSION;
278   ps->option[LEDM_OPTION_COMPRESSION].type = SANE_TYPE_STRING;
279   ps->option[LEDM_OPTION_COMPRESSION].unit = SANE_UNIT_NONE;
280   ps->option[LEDM_OPTION_COMPRESSION].size = MAX_STRING_SIZE;
281   ps->option[LEDM_OPTION_COMPRESSION].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED;
282   ps->option[LEDM_OPTION_COMPRESSION].constraint_type = SANE_CONSTRAINT_STRING_LIST;
283   ps->option[LEDM_OPTION_COMPRESSION].constraint.string_list = ps->compressionList;
284
285   ps->option[LEDM_OPTION_JPEG_QUALITY].name = STR_NAME_JPEG_QUALITY;
286   ps->option[LEDM_OPTION_JPEG_QUALITY].title = STR_TITLE_JPEG_QUALITY;
287   ps->option[LEDM_OPTION_JPEG_QUALITY].desc = STR_DESC_JPEG_QUALITY;
288   ps->option[LEDM_OPTION_JPEG_QUALITY].type = SANE_TYPE_INT;
289   ps->option[LEDM_OPTION_JPEG_QUALITY].unit = SANE_UNIT_NONE;
290   ps->option[LEDM_OPTION_JPEG_QUALITY].size = sizeof(SANE_Int);
291   ps->option[LEDM_OPTION_JPEG_QUALITY].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED;
292   ps->option[LEDM_OPTION_JPEG_QUALITY].constraint_type = SANE_CONSTRAINT_RANGE;
293   ps->option[LEDM_OPTION_JPEG_QUALITY].constraint.range = &ps->jpegQualityRange;
294   ps->jpegQualityRange.min = MIN_JPEG_COMPRESSION_FACTOR;
295   ps->jpegQualityRange.max = MAX_JPEG_COMPRESSION_FACTOR;
296   ps->jpegQualityRange.quant = 0;
297
298   ps->option[LEDM_OPTION_GROUP_GEOMETRY].name = "geometry-group";
299   ps->option[LEDM_OPTION_GROUP_GEOMETRY].title = STR_TITLE_GEOMETRY;
300   ps->option[LEDM_OPTION_GROUP_GEOMETRY].type = SANE_TYPE_GROUP;
301   ps->option[LEDM_OPTION_GROUP_GEOMETRY].cap = SANE_CAP_ADVANCED;
302
303   ps->option[LEDM_OPTION_TL_X].name = SANE_NAME_SCAN_TL_X;
304   ps->option[LEDM_OPTION_TL_X].title = SANE_TITLE_SCAN_TL_X;
305   ps->option[LEDM_OPTION_TL_X].desc = SANE_DESC_SCAN_TL_X;
306   ps->option[LEDM_OPTION_TL_X].type = SANE_TYPE_FIXED;
307   ps->option[LEDM_OPTION_TL_X].unit = SANE_UNIT_MM;
308   ps->option[LEDM_OPTION_TL_X].size = sizeof(SANE_Int);
309   ps->option[LEDM_OPTION_TL_X].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
310   ps->option[LEDM_OPTION_TL_X].constraint_type = SANE_CONSTRAINT_RANGE;
311   ps->option[LEDM_OPTION_TL_X].constraint.range = &ps->tlxRange;
312   ps->tlxRange.min = 0;
313   ps->tlxRange.quant = 0;
314
315   ps->option[LEDM_OPTION_TL_Y].name = SANE_NAME_SCAN_TL_Y;
316   ps->option[LEDM_OPTION_TL_Y].title = SANE_TITLE_SCAN_TL_Y;
317   ps->option[LEDM_OPTION_TL_Y].desc = SANE_DESC_SCAN_TL_Y;
318   ps->option[LEDM_OPTION_TL_Y].type = SANE_TYPE_FIXED;
319   ps->option[LEDM_OPTION_TL_Y].unit = SANE_UNIT_MM;
320   ps->option[LEDM_OPTION_TL_Y].size = sizeof(SANE_Int);
321   ps->option[LEDM_OPTION_TL_Y].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
322   ps->option[LEDM_OPTION_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE;
323   ps->option[LEDM_OPTION_TL_Y].constraint.range = &ps->tlyRange;
324   ps->tlyRange.min = 0;
325   ps->tlyRange.quant = 0;
326
327   ps->option[LEDM_OPTION_BR_X].name = SANE_NAME_SCAN_BR_X;
328   ps->option[LEDM_OPTION_BR_X].title = SANE_TITLE_SCAN_BR_X;
329   ps->option[LEDM_OPTION_BR_X].desc = SANE_DESC_SCAN_BR_X;
330   ps->option[LEDM_OPTION_BR_X].type = SANE_TYPE_FIXED;
331   ps->option[LEDM_OPTION_BR_X].unit = SANE_UNIT_MM;
332   ps->option[LEDM_OPTION_BR_X].size = sizeof(SANE_Int);
333   ps->option[LEDM_OPTION_BR_X].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
334   ps->option[LEDM_OPTION_BR_X].constraint_type = SANE_CONSTRAINT_RANGE;
335   ps->option[LEDM_OPTION_BR_X].constraint.range = &ps->brxRange;
336   ps->brxRange.min = 0;
337   ps->brxRange.quant = 0;
338
339   ps->option[LEDM_OPTION_BR_Y].name = SANE_NAME_SCAN_BR_Y;
340   ps->option[LEDM_OPTION_BR_Y].title = SANE_TITLE_SCAN_BR_Y;
341   ps->option[LEDM_OPTION_BR_Y].desc = SANE_DESC_SCAN_BR_Y;
342   ps->option[LEDM_OPTION_BR_Y].type = SANE_TYPE_FIXED;
343   ps->option[LEDM_OPTION_BR_Y].unit = SANE_UNIT_MM;
344   ps->option[LEDM_OPTION_BR_Y].size = sizeof(SANE_Int);
345   ps->option[LEDM_OPTION_BR_Y].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
346   ps->option[LEDM_OPTION_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE;
347   ps->option[LEDM_OPTION_BR_Y].constraint.range = &ps->bryRange;
348   ps->bryRange.min = 0;
349   ps->bryRange.quant = 0;
350
351 return 0;
352 }
353
354 /*----------------------------------- LEDM API Calls ------------------------------------*/
355
356 SANE_Status __attribute__ ((visibility ("hidden"))) ledm_open(SANE_String_Const device, SANE_Handle *handle)
357 {
358   struct hpmud_model_attributes ma;
359   int stat = SANE_STATUS_IO_ERROR;
360
361   if(session)
362   {
363     return SANE_STATUS_DEVICE_BUSY;
364   }
365   if((session = create_session()) == NULL)
366     return SANE_STATUS_NO_MEM;
367
368   /* Set session to specified device. */
369   snprintf(session->uri, sizeof(session->uri)-1, "hp:%s", device);   /* prepend "hp:" */
370
371   /* Get actual model attributes from models.dat. */
372   hpmud_query_model(session->uri, &ma);
373   session->scan_type = ma.scantype;
374
375   if (hpmud_open_device(session->uri, ma.mfp_mode, &session->dd) != HPMUD_R_OK)
376   {
377     stat = SANE_STATUS_IO_ERROR;
378     goto bugout;
379   }
380
381   init_options(session);
382
383   if (bb_open(session))
384   {
385     stat = SANE_STATUS_IO_ERROR;
386     goto bugout;
387   }
388
389   /* Set supported Scan Modes as determined by bb_open. */
390    ledm_control_option(session, LEDM_OPTION_SCAN_MODE, SANE_ACTION_SET_AUTO, NULL, NULL); /* set default option */
391
392   /* Set scan input sources as determined by bb_open. */
393    ledm_control_option(session, LEDM_OPTION_INPUT_SOURCE, SANE_ACTION_SET_AUTO, NULL, NULL); /* set default option */
394
395   /* Set supported resolutions. */
396   ledm_control_option(session, LEDM_OPTION_SCAN_RESOLUTION, SANE_ACTION_SET_AUTO, NULL, NULL); /* set default option */
397
398   /* Set supported contrast. */
399   ledm_control_option(session, LEDM_OPTION_CONTRAST, SANE_ACTION_SET_AUTO, NULL, NULL); /* set default option */
400
401   /* Set supported compression. (Note, cm1017 may say it supports MMR, but it doesn't) */
402   ledm_control_option(session, LEDM_OPTION_COMPRESSION, SANE_ACTION_SET_AUTO, NULL, NULL); /* set default option */
403
404   /* Determine supported jpeg quality factor as determined by bb_open. */
405   ledm_control_option(session, LEDM_OPTION_JPEG_QUALITY, SANE_ACTION_SET_AUTO, NULL, NULL); /* set default option */
406
407   /* Set x,y extents. See bb_open */
408   ledm_control_option(session, LEDM_OPTION_TL_X, SANE_ACTION_SET_AUTO, NULL, NULL); /* set default option */
409   ledm_control_option(session, LEDM_OPTION_TL_Y, SANE_ACTION_SET_AUTO, NULL, NULL); /* set default option */
410   ledm_control_option(session, LEDM_OPTION_BR_X, SANE_ACTION_SET_AUTO, NULL, NULL); /* set default option */
411   ledm_control_option(session, LEDM_OPTION_BR_Y, SANE_ACTION_SET_AUTO, NULL, NULL); /* set default option */
412
413   *handle = (SANE_Handle *)session;
414
415   stat = SANE_STATUS_GOOD;
416
417 bugout:
418
419    if (stat != SANE_STATUS_GOOD)
420    {
421       if (session)
422       {
423          if (session->cd > 0)
424             hpmud_close_channel(session->dd, session->cd);
425          if (session->dd > 0)
426             hpmud_close_device(session->dd);
427          free(session);
428          session = NULL;
429       }
430    }
431
432    return stat;
433 }
434
435 const SANE_Option_Descriptor *ledm_get_option_descriptor(SANE_Handle handle, SANE_Int option)
436 {
437   struct ledm_session *ps = (struct ledm_session *)handle;
438
439   DBG8("sane_hpaio_get_option_descriptor(option=%s)\n", ps->option[option].name);
440
441   if (option < 0 || option >= LEDM_OPTION_MAX)
442     return NULL;
443
444   return &ps->option[option];
445 } /* ledm_get_option_descriptor */
446
447 SANE_Status ledm_control_option(SANE_Handle handle, SANE_Int option, SANE_Action action, void *value, SANE_Int *set_result)
448 {
449   struct ledm_session *ps = (struct ledm_session *)handle;
450   SANE_Int *int_value = value, mset_result=0;
451   int i, stat=SANE_STATUS_INVAL;
452
453   switch(option)
454   {
455     case LEDM_OPTION_COUNT:
456       if (action == SANE_ACTION_GET_VALUE)
457       {
458         *int_value = LEDM_OPTION_MAX;
459         stat = SANE_STATUS_GOOD;
460       }
461       break;
462     case LEDM_OPTION_SCAN_MODE:
463       if(action == SANE_ACTION_GET_VALUE)
464       {
465         for(i=0; ps->scanModeList[i]; i++)
466         {
467           if(ps->currentScanMode == ps->scanModeMap[i])
468           {
469             strcpy(value, ps->scanModeList[i]);
470             stat = SANE_STATUS_GOOD;
471             break;
472           }
473         }
474       }
475       else if (action == SANE_ACTION_SET_VALUE)
476       {
477         for (i=0; ps->scanModeList[i]; i++)
478         {
479           if (strcasecmp(ps->scanModeList[i], value) == 0)
480           {
481             ps->currentScanMode = ps->scanModeMap[i];
482             set_scan_mode_side_effects(ps, ps->currentScanMode);
483             mset_result |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
484             stat = SANE_STATUS_GOOD;
485             break;
486           }
487         }
488       }
489       else
490       {  /* Set default. */
491         ps->currentScanMode = CE_COLOR8;
492         set_scan_mode_side_effects(ps, ps->currentScanMode);
493         stat = SANE_STATUS_GOOD;
494       }
495       break;
496       case LEDM_OPTION_INPUT_SOURCE:
497          if (action == SANE_ACTION_GET_VALUE)
498          {
499             for (i=0; ps->inputSourceList[i]; i++)
500             {
501                if (ps->currentInputSource == ps->inputSourceMap[i])
502                {
503                   strcpy(value, ps->inputSourceList[i]);
504                   stat = SANE_STATUS_GOOD;
505                   break;
506                }
507             }
508          }
509          else if (action == SANE_ACTION_SET_VALUE)
510          {
511            for (i=0; ps->inputSourceList[i]; i++)
512            {
513              if (strcasecmp(ps->inputSourceList[i], value) == 0)
514              {
515                ps->currentInputSource = ps->inputSourceMap[i];
516                set_input_source_side_effects(ps, ps->currentInputSource);
517                if(ps->currentInputSource == IS_PLATEN) 
518                {
519                  i = session->platen_resolutionList[0] + 1;
520                  while(i--) session->resolutionList[i] = session->platen_resolutionList[i];
521                }
522                else
523                {
524                  i = session->adf_resolutionList[0] + 1;
525                  while(i--) session->resolutionList[i] = session->adf_resolutionList[i];
526                }
527                ps->currentResolution = session->resolutionList[1];
528                mset_result |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
529                stat = SANE_STATUS_GOOD;
530                break;
531              }
532            }
533          }
534          else
535          {  /* Set default. */
536            ps->currentInputSource = IS_PLATEN;
537            set_input_source_side_effects(ps, ps->currentInputSource);
538            mset_result |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
539            stat = SANE_STATUS_GOOD;
540          }
541          break;
542       case LEDM_OPTION_SCAN_RESOLUTION:
543          if (action == SANE_ACTION_GET_VALUE)
544          {
545             *int_value = ps->currentResolution;
546             stat = SANE_STATUS_GOOD;
547          }
548          else if (action == SANE_ACTION_SET_VALUE)
549          {
550             for (i=1; i <= ps->resolutionList[0]; i++)
551             {
552                if (ps->resolutionList[i] == *int_value)
553                {
554                   ps->currentResolution = *int_value;
555                   if(ps->currentResolution == 4800) SendScanEvent(ps->uri, EVENT_SIZE_WARNING);
556                   mset_result |= SANE_INFO_RELOAD_PARAMS;
557                   stat = SANE_STATUS_GOOD;
558                   break;
559                }
560             }
561          }
562          else
563          {  /* Set default. */
564             ps->currentResolution = 75;
565             stat = SANE_STATUS_GOOD;
566          }
567          break;
568       case LEDM_OPTION_CONTRAST:
569          if (action == SANE_ACTION_GET_VALUE)
570          {
571             *int_value = ps->currentContrast;
572             stat = SANE_STATUS_GOOD;
573          }
574          else if (action == SANE_ACTION_SET_VALUE)
575          {
576             if (*int_value >= LEDM_CONTRAST_MIN && *int_value <= LEDM_CONTRAST_MAX)
577             {
578                ps->currentContrast = *int_value;
579                stat = SANE_STATUS_GOOD;
580                break;
581             }
582          }
583          else
584          {  /* Set default. */
585             ps->currentContrast = LEDM_CONTRAST_DEFAULT;
586             stat = SANE_STATUS_GOOD;
587          }
588          break;
589       case LEDM_OPTION_COMPRESSION:
590          if (action == SANE_ACTION_GET_VALUE)
591          {
592             for (i=0; ps->compressionList[i]; i++)
593             {
594                if (ps->currentCompression == ps->compressionMap[i])
595                {
596                   strcpy(value, ps->compressionList[i]);
597                   stat = SANE_STATUS_GOOD;
598                   break;
599                }
600             }
601          }
602          else if (action == SANE_ACTION_SET_VALUE)
603          {
604             for (i=0; ps->compressionList[i]; i++)
605             {
606                if (strcasecmp(ps->compressionList[i], value) == 0)
607                {
608                   ps->currentCompression = ps->compressionMap[i];
609                   stat = SANE_STATUS_GOOD;
610                   break;
611                }
612             }
613          }
614          else
615          {  /* Set default. */
616             ps->currentCompression = SF_JPEG;
617             stat = SANE_STATUS_GOOD;
618          }
619          break;
620       case LEDM_OPTION_JPEG_QUALITY:
621          if (action == SANE_ACTION_GET_VALUE)
622          {
623             *int_value = ps->currentJpegQuality;
624             stat = SANE_STATUS_GOOD;
625          }
626          else if (action == SANE_ACTION_SET_VALUE)
627          {
628             if (*int_value >= MIN_JPEG_COMPRESSION_FACTOR && *int_value <= MAX_JPEG_COMPRESSION_FACTOR)
629             {
630                ps->currentJpegQuality = *int_value;
631                stat = SANE_STATUS_GOOD;
632                break;
633             }
634          }
635          else
636          {  /* Set default. */
637             ps->currentJpegQuality = SAFER_JPEG_COMPRESSION_FACTOR;
638             stat = SANE_STATUS_GOOD;
639          }
640          break;
641       case LEDM_OPTION_TL_X:
642          if (action == SANE_ACTION_GET_VALUE)
643          {
644             *int_value = ps->currentTlx;
645             stat = SANE_STATUS_GOOD;
646          }
647          else if (action == SANE_ACTION_SET_VALUE)
648          {
649             if (*int_value >= ps->tlxRange.min && *int_value <= ps->tlxRange.max)
650             {
651                ps->currentTlx = *int_value;
652                mset_result |= SANE_INFO_RELOAD_PARAMS;
653                stat = SANE_STATUS_GOOD;
654                break;
655             }
656          }
657          else
658          {  /* Set default. */
659             ps->currentTlx = ps->tlxRange.min;
660             stat = SANE_STATUS_GOOD;
661          }
662          break;
663       case LEDM_OPTION_TL_Y:
664          if (action == SANE_ACTION_GET_VALUE)
665          {
666             *int_value = ps->currentTly;
667             stat = SANE_STATUS_GOOD;
668          }
669          else if (action == SANE_ACTION_SET_VALUE)
670          {
671             if (*int_value >= ps->tlyRange.min && *int_value <= ps->tlyRange.max)
672             {
673                
674                ps->currentTly = *int_value;
675                mset_result |= SANE_INFO_RELOAD_PARAMS;
676                stat = SANE_STATUS_GOOD;
677                break;
678             }
679          }
680          else
681          {  /* Set default. */
682             ps->currentTly = ps->tlyRange.min;
683             stat = SANE_STATUS_GOOD;
684          }
685          break;
686       case LEDM_OPTION_BR_X:
687          if (action == SANE_ACTION_GET_VALUE)
688          {
689             *int_value = ps->currentBrx;
690             stat = SANE_STATUS_GOOD;
691          }
692          else if (action == SANE_ACTION_SET_VALUE)
693          {
694             if (*int_value >= ps->brxRange.min && *int_value <= ps->brxRange.max)
695             {
696                ps->currentBrx = *int_value;
697                mset_result |= SANE_INFO_RELOAD_PARAMS;
698                stat = SANE_STATUS_GOOD;
699                break;
700             }
701          }
702          else
703          {  /* Set default. */
704             ps->currentBrx = ps->brxRange.max;
705             stat = SANE_STATUS_GOOD;
706          }
707          break;
708       case LEDM_OPTION_BR_Y:
709          if (action == SANE_ACTION_GET_VALUE)
710          {
711             *int_value = ps->currentBry;
712             stat = SANE_STATUS_GOOD;
713          }
714          else if (action == SANE_ACTION_SET_VALUE)
715          {
716             if (*int_value >= ps->bryRange.min && *int_value <= ps->bryRange.max)
717             {
718                ps->currentBry = *int_value;
719                mset_result |= SANE_INFO_RELOAD_PARAMS;
720                stat = SANE_STATUS_GOOD;
721                break;
722             }
723          }
724          else
725          {  /* Set default. */
726             ps->currentBry = ps->bryRange.max;
727             stat = SANE_STATUS_GOOD;
728          }
729          break;
730       default:
731          break;
732    }
733
734    if (set_result)
735       *set_result = mset_result;
736
737   if (stat != SANE_STATUS_GOOD)
738   {
739      BUG("control_option failed: option=%s action=%s\n", ps->option[option].name, action==SANE_ACTION_GET_VALUE ? "get" : action==SANE_ACTION_SET_VALUE ? "set" : "auto");
740   }
741
742    return stat;
743 } /* ledm_control_option */
744
745 SANE_Status ledm_get_parameters(SANE_Handle handle, SANE_Parameters *params)
746 {
747   struct ledm_session *ps = (struct ledm_session *)handle;
748
749   set_extents(ps);
750
751   /* Get scan parameters for sane client. */
752   bb_get_parameters(ps, params, ps->ip_handle ? SPO_STARTED : SPO_BEST_GUESS);
753
754   DBG8("sane_hpaio_get_parameters(): format=%d, last_frame=%d, lines=%d, depth=%d, pixels_per_line=%d, bytes_per_line=%d\n",
755     params->format, params->last_frame, params->lines, params->depth, params->pixels_per_line, params->bytes_per_line);
756
757   return SANE_STATUS_GOOD;
758 } /* ledm_get_parameters */
759
760 SANE_Status ledm_start(SANE_Handle handle)
761 {
762   struct ledm_session *ps = (struct ledm_session *)handle;
763   SANE_Parameters pp;
764   IP_IMAGE_TRAITS traits;
765   IP_XFORM_SPEC xforms[IP_MAX_XFORMS], *pXform=xforms;
766   int stat, ret;
767
768   DBG8("sane_hpaio_start()\n");
769
770   ps -> user_cancel = 0;
771   ps -> cnt = 0;
772   ps -> index = 0;
773
774   if (set_extents(ps))
775   {
776     stat = SANE_STATUS_INVAL;
777     goto bugout;
778   }   
779
780   /* If input is ADF and ADF is empty, return SANE_STATUS_NO_DOCS. */
781   if (ps->currentInputSource==IS_ADF || ps->currentInputSource ==IS_ADF_DUPLEX)
782   {
783     ret = bb_is_paper_in_adf(ps);   /* 0 = no paper in adf, 1 = paper in adf, -1 = error */
784     if (ret == 0)
785     {
786       stat = SANE_STATUS_NO_DOCS;   /* done scanning */
787       SendScanEvent (ps->uri, EVENT_SCAN_ADF_NO_DOCS);
788       goto bugout;
789     }
790     else if (ret < 0)
791     {
792       stat = SANE_STATUS_IO_ERROR;
793       goto bugout;
794     }
795   }
796
797    /* Start scan and get actual image traits. */
798   stat = bb_start_scan(ps);
799   if (stat != SANE_STATUS_GOOD)
800     goto bugout;
801
802   if(ps->user_cancel)
803   {
804     stat = SANE_STATUS_GOOD ;
805     goto bugout;
806   }
807    
808   SendScanEvent(ps->uri, EVENT_START_SCAN_JOB);
809
810   memset(xforms, 0, sizeof(xforms));
811
812   /* Setup image-processing pipeline for xform. */
813   if (ps->currentScanMode == CE_COLOR8 || ps->currentScanMode == CE_GRAY8)
814   {
815     switch(ps->currentCompression)
816     {
817       case SF_JPEG:
818         pXform->aXformInfo[IP_JPG_DECODE_FROM_DENALI].dword = 0;    /* 0=no */
819         ADD_XFORM(X_JPG_DECODE);
820         pXform->aXformInfo[IP_CNV_COLOR_SPACE_WHICH_CNV].dword = IP_CNV_YCC_TO_SRGB;
821         pXform->aXformInfo[IP_CNV_COLOR_SPACE_GAMMA].dword = 0x00010000;
822         ADD_XFORM(X_CNV_COLOR_SPACE);
823         break;
824       case SF_RAW:
825       default:
826         break;
827     }
828   }
829   else
830   {  /* Must be BLACK_AND_WHITE1 (Lineart). */
831     switch(ps->currentCompression)
832     {
833       case SF_JPEG:
834         pXform->aXformInfo[IP_JPG_DECODE_FROM_DENALI].dword = 0;    /* 0=no */
835         ADD_XFORM(X_JPG_DECODE);
836         pXform->aXformInfo[IP_GRAY_2_BI_THRESHOLD].dword = 127;
837         ADD_XFORM(X_GRAY_2_BI);
838         break;
839       case SF_RAW:
840         pXform->aXformInfo[IP_GRAY_2_BI_THRESHOLD].dword = 127;
841         ADD_XFORM(X_GRAY_2_BI);
842       default:
843         break;
844     }
845   }
846
847   /* Setup x/y cropping for xform. (Actually we let cm1017 do it's own cropping) */
848   pXform->aXformInfo[IP_CROP_LEFT].dword = 0;
849   pXform->aXformInfo[IP_CROP_RIGHT].dword = 0;
850   pXform->aXformInfo[IP_CROP_TOP].dword = 0;
851   pXform->aXformInfo[IP_CROP_MAXOUTROWS].dword = 0;
852   ADD_XFORM(X_CROP);
853
854   /* Setup x/y padding for xform. (Actually we let cm1017 do it's own padding) */
855   pXform->aXformInfo[IP_PAD_LEFT].dword = 0; /* # of pixels to add to left side */
856   pXform->aXformInfo[IP_PAD_RIGHT].dword = 0; /* # of pixels to add to right side */
857   pXform->aXformInfo[IP_PAD_TOP].dword = 0; /* # of rows to add to top */
858   pXform->aXformInfo[IP_PAD_BOTTOM].dword = 0;  /* # of rows to add to bottom */
859   pXform->aXformInfo[IP_PAD_VALUE].dword = ps->currentScanMode == CE_K1 ? 0 : -1;   /* lineart white = 0, rgb white = -1 */ 
860   pXform->aXformInfo[IP_PAD_MIN_HEIGHT].dword = 0;
861   ADD_XFORM(X_PAD);
862
863   /* Open image processor. */
864   if ((ret = ipOpen(pXform-xforms, xforms, 0, &ps->ip_handle)) != IP_DONE)
865   {
866     stat = SANE_STATUS_INVAL;
867     goto bugout;
868   }
869
870   /* Get scan parameters for image processor. */
871   if (ps->currentCompression == SF_RAW)
872     bb_get_parameters(ps, &pp, SPO_STARTED_JR);     /* hpraw, use actual parameters */
873   else
874     bb_get_parameters(ps, &pp, SPO_BEST_GUESS);     /* jpeg, use best guess */
875   traits.iPixelsPerRow = pp.pixels_per_line;
876   switch(ps->currentScanMode)
877   {
878     case CE_K1:                      /* lineart (let IP create Mono from Gray8) */
879     case CE_GRAY8:
880       traits.iBitsPerPixel = 8;     /* grayscale */
881       break;
882     case CE_COLOR8:
883     default:
884       traits.iBitsPerPixel = 24;    /* color */
885       break;
886   }
887   traits.lHorizDPI = ps->currentResolution << 16;
888   traits.lVertDPI = ps->currentResolution << 16;
889   traits.lNumRows =  pp.lines;
890   traits.iNumPages = 1;
891   traits.iPageNum = 1;
892   traits.iComponentsPerPixel = ((traits.iBitsPerPixel % 3) ? 1 : 3);
893   ipSetDefaultInputTraits(ps->ip_handle, &traits);
894
895   /* If jpeg get output image attributes from the image processor. */
896   if (ps->currentCompression == SF_JPEG)
897   {
898     /* Enable parsed header flag. */
899     ipResultMask(ps->ip_handle, IP_PARSED_HEADER);
900
901     /* Wait for image processor to process header so we know the exact size of the image for sane_get_params. */
902     while (1)
903     {
904       ret = get_ip_data(ps, NULL, 0, NULL);
905
906       if (ret & (IP_INPUT_ERROR | IP_FATAL_ERROR | IP_DONE))
907       {
908         stat = SANE_STATUS_IO_ERROR;
909         goto bugout;
910       }
911
912       if (ret & IP_PARSED_HEADER)
913       {
914         ipGetImageTraits(ps->ip_handle, NULL, &ps->image_traits);  /* get valid image traits */
915         ipResultMask(ps->ip_handle, 0);                          /* disable parsed header flag */
916         break;
917       }
918     }
919   }
920   else
921   ipGetImageTraits(ps->ip_handle, NULL, &ps->image_traits);  /* get valid image traits */
922
923   stat = SANE_STATUS_GOOD;
924
925 bugout:
926   if (stat != SANE_STATUS_GOOD)
927   {
928     if (ps->ip_handle)
929     {
930       ipClose(ps->ip_handle); 
931       ps->ip_handle = 0;
932     }   
933     bb_end_scan(ps, stat == SANE_STATUS_IO_ERROR ? 1: 0);
934   }
935   return stat;
936 } /* ledm_start */
937
938 SANE_Status ledm_read(SANE_Handle handle, SANE_Byte *data, SANE_Int maxLength, SANE_Int *length)
939 {
940   struct ledm_session *ps = (struct ledm_session *)handle;
941   int ret, stat=SANE_STATUS_IO_ERROR;
942
943   if(ps->user_cancel)
944   { 
945     SendScanEvent(ps->uri, EVENT_SCAN_CANCEL);
946     return SANE_STATUS_CANCELLED;
947   }
948
949   ret = get_ip_data(ps, data, maxLength, length);
950
951   if(ret & (IP_INPUT_ERROR | IP_FATAL_ERROR))
952   {
953      goto bugout;
954    }
955
956   if(ret==IP_DONE)
957   {
958     stat = SANE_STATUS_EOF;
959     SendScanEvent(ps->uri, EVENT_END_SCAN_JOB);
960   } 
961   else stat= SANE_STATUS_GOOD;
962
963 bugout:
964   if (stat != SANE_STATUS_GOOD)
965   {
966     if (ps->ip_handle)
967     {
968       /* 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. */ 
969       ipClose(ps->ip_handle);  
970       ps->ip_handle = 0;
971     } 
972     bb_end_page(ps, 0);
973   }
974
975   DBG8("-sane_hpaio_read() output=%p bytes_read=%d maxLength=%d status=%d\n", data, *length, maxLength, stat);
976
977   return stat;
978 } /* ledm_read */
979
980 void ledm_cancel(SANE_Handle handle)
981 {
982   struct ledm_session *ps = (struct ledm_session *)handle;
983
984   DBG8("sane_hpaio_cancel()\n"); 
985
986   ps -> user_cancel = 1;
987   /* Sane_cancel is always called at the end of the scan job. 
988   Note that on a multiple page scan job sane_cancel is called only once */
989
990   if (ps->ip_handle)
991   {
992     ipClose(ps->ip_handle); 
993     ps->ip_handle = 0;
994   }
995   bb_end_scan(ps, 0);
996 } /* ledm_cancel */
997
998 void ledm_close(SANE_Handle handle)
999 {
1000   struct ledm_session *ps = (struct ledm_session *)handle;
1001
1002   if (ps == NULL || ps != session)
1003   {
1004     BUG("invalid sane_close\n");
1005     return;
1006   }
1007
1008   bb_close(ps);
1009
1010   if (ps->dd > 0)
1011     hpmud_close_device(ps->dd);
1012     
1013   free(ps);
1014   session = NULL;
1015 }