1 /************************************************************************************
2 
3   ledm.c - HP SANE backend support for LEDM based multi-function peripherals
4   (c) 2010 Copyright HP 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. */
set_extents(struct ledm_session * ps)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 
create_session()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. */
get_ip_data(struct ledm_session * ps,SANE_Byte * data,SANE_Int maxLength,SANE_Int * length)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 
set_scan_mode_side_effects(struct ledm_session * ps,enum COLOR_ENTRY scanMode)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 
set_input_source_side_effects(struct ledm_session * ps,enum INPUT_SOURCE source)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     if ((ps->adf_bryRange.max != ps->platen_bryRange.max) || (ps->adf_brxRange.max !=  ps->platen_brxRange.max))
210     {
211         ps->currentTly = ps->tlyRange.min;
212         ps->currentBrx = ps->brxRange.max;
213         ps->currentTlx = ps->tlxRange.min;
214         ps->currentBry = ps->bryRange.max;
215     }
216 
217    return 0;
218 } /* set_input_source_side_effects */
219 
init_options(struct ledm_session * ps)220 static int init_options(struct ledm_session *ps)
221 {
222   ps->option[LEDM_OPTION_COUNT].name = "option-cnt";
223   ps->option[LEDM_OPTION_COUNT].title = SANE_TITLE_NUM_OPTIONS;
224   ps->option[LEDM_OPTION_COUNT].desc = SANE_DESC_NUM_OPTIONS;
225   ps->option[LEDM_OPTION_COUNT].type = SANE_TYPE_INT;
226   ps->option[LEDM_OPTION_COUNT].unit = SANE_UNIT_NONE;
227   ps->option[LEDM_OPTION_COUNT].size = sizeof(SANE_Int);
228   ps->option[LEDM_OPTION_COUNT].cap = SANE_CAP_SOFT_DETECT;
229   ps->option[LEDM_OPTION_COUNT].constraint_type = SANE_CONSTRAINT_NONE;
230 
231   ps->option[LEDM_OPTION_GROUP_SCAN_MODE].name = "mode-group";
232   ps->option[LEDM_OPTION_GROUP_SCAN_MODE].title = SANE_TITLE_SCAN_MODE;
233   ps->option[LEDM_OPTION_GROUP_SCAN_MODE].type = SANE_TYPE_GROUP;
234 
235   ps->option[LEDM_OPTION_SCAN_MODE].name = SANE_NAME_SCAN_MODE;
236   ps->option[LEDM_OPTION_SCAN_MODE].title = SANE_TITLE_SCAN_MODE;
237   ps->option[LEDM_OPTION_SCAN_MODE].desc = SANE_DESC_SCAN_MODE;
238   ps->option[LEDM_OPTION_SCAN_MODE].type = SANE_TYPE_STRING;
239   ps->option[LEDM_OPTION_SCAN_MODE].unit = SANE_UNIT_NONE;
240   ps->option[LEDM_OPTION_SCAN_MODE].size = MAX_STRING_SIZE;
241   ps->option[LEDM_OPTION_SCAN_MODE].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
242   ps->option[LEDM_OPTION_SCAN_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
243   ps->option[LEDM_OPTION_SCAN_MODE].constraint.string_list = ps->scanModeList;
244 
245   ps->option[LEDM_OPTION_INPUT_SOURCE].name = SANE_NAME_SCAN_SOURCE;
246   ps->option[LEDM_OPTION_INPUT_SOURCE].title = SANE_TITLE_SCAN_SOURCE;
247   ps->option[LEDM_OPTION_INPUT_SOURCE].desc = SANE_DESC_SCAN_SOURCE;
248   ps->option[LEDM_OPTION_INPUT_SOURCE].type = SANE_TYPE_STRING;
249   ps->option[LEDM_OPTION_INPUT_SOURCE].unit = SANE_UNIT_NONE;
250   ps->option[LEDM_OPTION_INPUT_SOURCE].size = MAX_STRING_SIZE;
251   ps->option[LEDM_OPTION_INPUT_SOURCE].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
252   ps->option[LEDM_OPTION_INPUT_SOURCE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
253   ps->option[LEDM_OPTION_INPUT_SOURCE].constraint.string_list = ps->inputSourceList;
254 
255   ps->option[LEDM_OPTION_SCAN_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION;
256   ps->option[LEDM_OPTION_SCAN_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION;
257   ps->option[LEDM_OPTION_SCAN_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION;
258   ps->option[LEDM_OPTION_SCAN_RESOLUTION].type = SANE_TYPE_INT;
259   ps->option[LEDM_OPTION_SCAN_RESOLUTION].unit = SANE_UNIT_DPI;
260   ps->option[LEDM_OPTION_SCAN_RESOLUTION].size = sizeof(SANE_Int);
261   ps->option[LEDM_OPTION_SCAN_RESOLUTION].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
262   ps->option[LEDM_OPTION_SCAN_RESOLUTION].constraint_type = SANE_CONSTRAINT_WORD_LIST;
263   ps->option[LEDM_OPTION_SCAN_RESOLUTION].constraint.word_list = ps->resolutionList;
264 
265   ps->option[LEDM_OPTION_GROUP_ADVANCED].name = "advanced-group";
266   ps->option[LEDM_OPTION_GROUP_ADVANCED].title = STR_TITLE_ADVANCED;
267   ps->option[LEDM_OPTION_GROUP_ADVANCED].type = SANE_TYPE_GROUP;
268   ps->option[LEDM_OPTION_GROUP_ADVANCED].cap = SANE_CAP_ADVANCED;
269 
270   ps->option[LEDM_OPTION_BRIGHTNESS].name = SANE_NAME_BRIGHTNESS;
271   ps->option[LEDM_OPTION_BRIGHTNESS].title = SANE_TITLE_BRIGHTNESS;
272   ps->option[LEDM_OPTION_BRIGHTNESS].desc = SANE_DESC_BRIGHTNESS;
273   ps->option[LEDM_OPTION_BRIGHTNESS].type = SANE_TYPE_INT;
274   ps->option[LEDM_OPTION_BRIGHTNESS].unit = SANE_UNIT_NONE;
275   ps->option[LEDM_OPTION_BRIGHTNESS].size = sizeof(SANE_Int);
276   ps->option[LEDM_OPTION_BRIGHTNESS].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED;
277   ps->option[LEDM_OPTION_BRIGHTNESS].constraint_type = SANE_CONSTRAINT_RANGE;
278   ps->option[LEDM_OPTION_BRIGHTNESS].constraint.range = &ps->brightnessRange;
279   ps->brightnessRange.min = LEDM_BRIGHTNESS_MIN;
280   ps->brightnessRange.max = LEDM_BRIGHTNESS_MAX;
281   ps->brightnessRange.quant = 0;
282 
283   ps->option[LEDM_OPTION_CONTRAST].name = SANE_NAME_CONTRAST;
284   ps->option[LEDM_OPTION_CONTRAST].title = SANE_TITLE_CONTRAST;
285   ps->option[LEDM_OPTION_CONTRAST].desc = SANE_DESC_CONTRAST;
286   ps->option[LEDM_OPTION_CONTRAST].type = SANE_TYPE_INT;
287   ps->option[LEDM_OPTION_CONTRAST].unit = SANE_UNIT_NONE;
288   ps->option[LEDM_OPTION_CONTRAST].size = sizeof(SANE_Int);
289   ps->option[LEDM_OPTION_CONTRAST].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED;
290   ps->option[LEDM_OPTION_CONTRAST].constraint_type = SANE_CONSTRAINT_RANGE;
291   ps->option[LEDM_OPTION_CONTRAST].constraint.range = &ps->contrastRange;
292   ps->contrastRange.min = LEDM_CONTRAST_MIN;
293   ps->contrastRange.max = LEDM_CONTRAST_MAX;
294   ps->contrastRange.quant = 0;
295 
296   ps->option[LEDM_OPTION_COMPRESSION].name = STR_NAME_COMPRESSION;
297   ps->option[LEDM_OPTION_COMPRESSION].title = STR_TITLE_COMPRESSION;
298   ps->option[LEDM_OPTION_COMPRESSION].desc = STR_DESC_COMPRESSION;
299   ps->option[LEDM_OPTION_COMPRESSION].type = SANE_TYPE_STRING;
300   ps->option[LEDM_OPTION_COMPRESSION].unit = SANE_UNIT_NONE;
301   ps->option[LEDM_OPTION_COMPRESSION].size = MAX_STRING_SIZE;
302   ps->option[LEDM_OPTION_COMPRESSION].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED;
303   ps->option[LEDM_OPTION_COMPRESSION].constraint_type = SANE_CONSTRAINT_STRING_LIST;
304   ps->option[LEDM_OPTION_COMPRESSION].constraint.string_list = ps->compressionList;
305 
306   ps->option[LEDM_OPTION_JPEG_QUALITY].name = STR_NAME_JPEG_QUALITY;
307   ps->option[LEDM_OPTION_JPEG_QUALITY].title = STR_TITLE_JPEG_QUALITY;
308   ps->option[LEDM_OPTION_JPEG_QUALITY].desc = STR_DESC_JPEG_QUALITY;
309   ps->option[LEDM_OPTION_JPEG_QUALITY].type = SANE_TYPE_INT;
310   ps->option[LEDM_OPTION_JPEG_QUALITY].unit = SANE_UNIT_NONE;
311   ps->option[LEDM_OPTION_JPEG_QUALITY].size = sizeof(SANE_Int);
312   ps->option[LEDM_OPTION_JPEG_QUALITY].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED;
313   ps->option[LEDM_OPTION_JPEG_QUALITY].constraint_type = SANE_CONSTRAINT_RANGE;
314   ps->option[LEDM_OPTION_JPEG_QUALITY].constraint.range = &ps->jpegQualityRange;
315   ps->jpegQualityRange.min = MIN_JPEG_COMPRESSION_FACTOR;
316   ps->jpegQualityRange.max = MAX_JPEG_COMPRESSION_FACTOR;
317   ps->jpegQualityRange.quant = 0;
318 
319   ps->option[LEDM_OPTION_GROUP_GEOMETRY].name = "geometry-group";
320   ps->option[LEDM_OPTION_GROUP_GEOMETRY].title = STR_TITLE_GEOMETRY;
321   ps->option[LEDM_OPTION_GROUP_GEOMETRY].type = SANE_TYPE_GROUP;
322   ps->option[LEDM_OPTION_GROUP_GEOMETRY].cap = SANE_CAP_ADVANCED;
323 
324   ps->option[LEDM_OPTION_TL_X].name = SANE_NAME_SCAN_TL_X;
325   ps->option[LEDM_OPTION_TL_X].title = SANE_TITLE_SCAN_TL_X;
326   ps->option[LEDM_OPTION_TL_X].desc = SANE_DESC_SCAN_TL_X;
327   ps->option[LEDM_OPTION_TL_X].type = SANE_TYPE_FIXED;
328   ps->option[LEDM_OPTION_TL_X].unit = SANE_UNIT_MM;
329   ps->option[LEDM_OPTION_TL_X].size = sizeof(SANE_Int);
330   ps->option[LEDM_OPTION_TL_X].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
331   ps->option[LEDM_OPTION_TL_X].constraint_type = SANE_CONSTRAINT_RANGE;
332   ps->option[LEDM_OPTION_TL_X].constraint.range = &ps->tlxRange;
333   ps->tlxRange.min = 0;
334   ps->tlxRange.quant = 0;
335 
336   ps->option[LEDM_OPTION_TL_Y].name = SANE_NAME_SCAN_TL_Y;
337   ps->option[LEDM_OPTION_TL_Y].title = SANE_TITLE_SCAN_TL_Y;
338   ps->option[LEDM_OPTION_TL_Y].desc = SANE_DESC_SCAN_TL_Y;
339   ps->option[LEDM_OPTION_TL_Y].type = SANE_TYPE_FIXED;
340   ps->option[LEDM_OPTION_TL_Y].unit = SANE_UNIT_MM;
341   ps->option[LEDM_OPTION_TL_Y].size = sizeof(SANE_Int);
342   ps->option[LEDM_OPTION_TL_Y].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
343   ps->option[LEDM_OPTION_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE;
344   ps->option[LEDM_OPTION_TL_Y].constraint.range = &ps->tlyRange;
345   ps->tlyRange.min = 0;
346   ps->tlyRange.quant = 0;
347 
348   ps->option[LEDM_OPTION_BR_X].name = SANE_NAME_SCAN_BR_X;
349   ps->option[LEDM_OPTION_BR_X].title = SANE_TITLE_SCAN_BR_X;
350   ps->option[LEDM_OPTION_BR_X].desc = SANE_DESC_SCAN_BR_X;
351   ps->option[LEDM_OPTION_BR_X].type = SANE_TYPE_FIXED;
352   ps->option[LEDM_OPTION_BR_X].unit = SANE_UNIT_MM;
353   ps->option[LEDM_OPTION_BR_X].size = sizeof(SANE_Int);
354   ps->option[LEDM_OPTION_BR_X].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
355   ps->option[LEDM_OPTION_BR_X].constraint_type = SANE_CONSTRAINT_RANGE;
356   ps->option[LEDM_OPTION_BR_X].constraint.range = &ps->brxRange;
357   ps->brxRange.min = 0;
358   ps->brxRange.quant = 0;
359 
360   ps->option[LEDM_OPTION_BR_Y].name = SANE_NAME_SCAN_BR_Y;
361   ps->option[LEDM_OPTION_BR_Y].title = SANE_TITLE_SCAN_BR_Y;
362   ps->option[LEDM_OPTION_BR_Y].desc = SANE_DESC_SCAN_BR_Y;
363   ps->option[LEDM_OPTION_BR_Y].type = SANE_TYPE_FIXED;
364   ps->option[LEDM_OPTION_BR_Y].unit = SANE_UNIT_MM;
365   ps->option[LEDM_OPTION_BR_Y].size = sizeof(SANE_Int);
366   ps->option[LEDM_OPTION_BR_Y].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
367   ps->option[LEDM_OPTION_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE;
368   ps->option[LEDM_OPTION_BR_Y].constraint.range = &ps->bryRange;
369   ps->bryRange.min = 0;
370   ps->bryRange.quant = 0;
371 
372 return 0;
373 }
374 
375 /*----------------------------------- LEDM API Calls ------------------------------------*/
376 
ledm_open(SANE_String_Const device,SANE_Handle * handle)377 SANE_Status __attribute__ ((visibility ("hidden"))) ledm_open(SANE_String_Const device, SANE_Handle *handle)
378 {
379   struct hpmud_model_attributes ma;
380   int stat = SANE_STATUS_IO_ERROR;
381 
382   if(session)
383   {
384     return SANE_STATUS_DEVICE_BUSY;
385   }
386   if((session = create_session()) == NULL)
387     return SANE_STATUS_NO_MEM;
388 
389   /* Set session to specified device. */
390   snprintf(session->uri, sizeof(session->uri)-1, "hp:%s", device);   /* prepend "hp:" */
391 
392   /* Get actual model attributes from models.dat. */
393   hpmud_query_model(session->uri, &ma);
394   session->scan_type = ma.scantype;
395 
396   if (hpmud_open_device(session->uri, ma.mfp_mode, &session->dd) != HPMUD_R_OK)
397   {
398     stat = SANE_STATUS_IO_ERROR;
399     goto bugout;
400   }
401 
402   init_options(session);
403 
404   if (bb_open(session))
405   {
406     stat = SANE_STATUS_IO_ERROR;
407     goto bugout;
408   }
409 
410   /* Set supported Scan Modes as determined by bb_open. */
411    ledm_control_option(session, LEDM_OPTION_SCAN_MODE, SANE_ACTION_SET_AUTO, NULL, NULL); /* set default option */
412 
413   /* Set scan input sources as determined by bb_open. */
414    ledm_control_option(session, LEDM_OPTION_INPUT_SOURCE, SANE_ACTION_SET_AUTO, NULL, NULL); /* set default option */
415 
416   /* Set supported resolutions. */
417   ledm_control_option(session, LEDM_OPTION_SCAN_RESOLUTION, SANE_ACTION_SET_AUTO, NULL, NULL); /* set default option */
418 
419   /* Set supported contrast. */
420   ledm_control_option(session, LEDM_OPTION_CONTRAST, SANE_ACTION_SET_AUTO, NULL, NULL); /* set default option */
421 
422   /* Set supported brightness. */
423   ledm_control_option(session, LEDM_OPTION_BRIGHTNESS, SANE_ACTION_SET_AUTO, NULL, NULL); /* set default option */
424 
425   /* Set supported compression. (Note, cm1017 may say it supports MMR, but it doesn't) */
426   ledm_control_option(session, LEDM_OPTION_COMPRESSION, SANE_ACTION_SET_AUTO, NULL, NULL); /* set default option */
427 
428   /* Determine supported jpeg quality factor as determined by bb_open. */
429   ledm_control_option(session, LEDM_OPTION_JPEG_QUALITY, SANE_ACTION_SET_AUTO, NULL, NULL); /* set default option */
430 
431   /* Set x,y extents. See bb_open */
432   ledm_control_option(session, LEDM_OPTION_TL_X, SANE_ACTION_SET_AUTO, NULL, NULL); /* set default option */
433   ledm_control_option(session, LEDM_OPTION_TL_Y, SANE_ACTION_SET_AUTO, NULL, NULL); /* set default option */
434   ledm_control_option(session, LEDM_OPTION_BR_X, SANE_ACTION_SET_AUTO, NULL, NULL); /* set default option */
435   ledm_control_option(session, LEDM_OPTION_BR_Y, SANE_ACTION_SET_AUTO, NULL, NULL); /* set default option */
436 
437   *handle = (SANE_Handle *)session;
438 
439   stat = SANE_STATUS_GOOD;
440 
441 bugout:
442 
443    if (stat != SANE_STATUS_GOOD)
444    {
445       if (session)
446       {
447          bb_close(session);
448          if (session->cd > 0)
449             hpmud_close_channel(session->dd, session->cd);
450          if (session->dd > 0)
451             hpmud_close_device(session->dd);
452          free(session);
453          session = NULL;
454       }
455    }
456 
457    return stat;
458 }
459 
ledm_get_option_descriptor(SANE_Handle handle,SANE_Int option)460 const SANE_Option_Descriptor *ledm_get_option_descriptor(SANE_Handle handle, SANE_Int option)
461 {
462   struct ledm_session *ps = (struct ledm_session *)handle;
463 
464   DBG8("sane_hpaio_get_option_descriptor(option=%s)\n", ps->option[option].name);
465 
466   if (option < 0 || option >= LEDM_OPTION_MAX)
467     return NULL;
468 
469   return &ps->option[option];
470 } /* ledm_get_option_descriptor */
471 
ledm_control_option(SANE_Handle handle,SANE_Int option,SANE_Action action,void * value,SANE_Int * set_result)472 SANE_Status ledm_control_option(SANE_Handle handle, SANE_Int option, SANE_Action action, void *value, SANE_Int *set_result)
473 {
474   struct ledm_session *ps = (struct ledm_session *)handle;
475   SANE_Int *int_value = value, mset_result=0;
476   int i, stat=SANE_STATUS_INVAL;
477 
478   switch(option)
479   {
480     case LEDM_OPTION_COUNT:
481       if (action == SANE_ACTION_GET_VALUE)
482       {
483         *int_value = LEDM_OPTION_MAX;
484         stat = SANE_STATUS_GOOD;
485       }
486       break;
487     case LEDM_OPTION_SCAN_MODE:
488       if(action == SANE_ACTION_GET_VALUE)
489       {
490         for(i=0; ps->scanModeList[i]; i++)
491         {
492           if(ps->currentScanMode == ps->scanModeMap[i])
493           {
494             strcpy(value, ps->scanModeList[i]);
495             stat = SANE_STATUS_GOOD;
496             break;
497           }
498         }
499       }
500       else if (action == SANE_ACTION_SET_VALUE)
501       {
502         for (i=0; ps->scanModeList[i]; i++)
503         {
504           if (strcasecmp(ps->scanModeList[i], value) == 0)
505           {
506             ps->currentScanMode = ps->scanModeMap[i];
507             set_scan_mode_side_effects(ps, ps->currentScanMode);
508             mset_result |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
509             stat = SANE_STATUS_GOOD;
510             break;
511           }
512         }
513       }
514       else
515       {  /* Set default. */
516         ps->currentScanMode = ps->scanModeMap[0];
517         set_scan_mode_side_effects(ps, ps->currentScanMode);
518         stat = SANE_STATUS_GOOD;
519       }
520       break;
521       case LEDM_OPTION_INPUT_SOURCE:
522          if (action == SANE_ACTION_GET_VALUE)
523          {
524             for (i=0; ps->inputSourceList[i]; i++)
525             {
526                if (ps->currentInputSource == ps->inputSourceMap[i])
527                {
528                   strcpy(value, ps->inputSourceList[i]);
529                   stat = SANE_STATUS_GOOD;
530                   break;
531                }
532             }
533          }
534          else if (action == SANE_ACTION_SET_VALUE)
535          {
536            for (i=0; ps->inputSourceList[i]; i++)
537            {
538              if (strcasecmp(ps->inputSourceList[i], value) == 0)
539              {
540                ps->currentInputSource = ps->inputSourceMap[i];
541                set_input_source_side_effects(ps, ps->currentInputSource);
542                if(ps->currentInputSource == IS_PLATEN)
543                {
544                  i = session->platen_resolutionList[0] + 1;
545                  while(i--) session->resolutionList[i] = session->platen_resolutionList[i];
546                }
547                else
548                {
549                  i = session->adf_resolutionList[0] + 1;
550                  while(i--) session->resolutionList[i] = session->adf_resolutionList[i];
551                }
552                ps->currentResolution = session->resolutionList[1];
553                break;
554              }
555            }
556            /*For some devices resolution varies, when we change 'source' in Xsane.
557            Hence need to update the resolution  */
558            if (i>1) /*Number of sources > 1*/
559            {
560              if(session->platen_resolutionList[1] != session->adf_resolutionList[1])
561                  ps->currentResolution = session->resolutionList[1];
562            }
563            mset_result |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
564            stat = SANE_STATUS_GOOD;
565            break;
566          }
567          else
568          {  /* Set default. */
569            ps->currentInputSource = ps->inputSourceMap[0];
570            set_input_source_side_effects(ps, ps->currentInputSource);
571            mset_result |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
572            stat = SANE_STATUS_GOOD;
573          }
574          break;
575       case LEDM_OPTION_SCAN_RESOLUTION:
576          if (action == SANE_ACTION_GET_VALUE)
577          {
578             *int_value = ps->currentResolution;
579             stat = SANE_STATUS_GOOD;
580          }
581          else if (action == SANE_ACTION_SET_VALUE)
582          {
583             for (i=1; i <= ps->resolutionList[0]; i++)
584             {
585                if (ps->resolutionList[i] == *int_value)
586                {
587                   ps->currentResolution = *int_value;
588                   if(ps->currentResolution == 4800) SendScanEvent(ps->uri, EVENT_SIZE_WARNING);
589                   mset_result |= SANE_INFO_RELOAD_PARAMS;
590                   stat = SANE_STATUS_GOOD;
591                   break;
592                }
593             }
594             if (stat != SANE_STATUS_GOOD)
595             {
596                 ps->currentResolution = ps->resolutionList[1];
597                 stat = SANE_STATUS_GOOD;
598             }
599          }
600          else
601          {  /* Set default. */
602             ps->currentResolution = 75;
603             stat = SANE_STATUS_GOOD;
604          }
605          break;
606       case LEDM_OPTION_CONTRAST:
607          if (action == SANE_ACTION_GET_VALUE)
608          {
609             *int_value = ps->currentContrast;
610             stat = SANE_STATUS_GOOD;
611          }
612          else if (action == SANE_ACTION_SET_VALUE)
613          {
614             if (*int_value >= LEDM_CONTRAST_MIN && *int_value <= LEDM_CONTRAST_MAX)
615             {
616                ps->currentContrast = *int_value;
617             }
618             else
619             {
620               ps->currentContrast = LEDM_CONTRAST_DEFAULT;
621             }
622             mset_result |= SANE_INFO_RELOAD_PARAMS;
623             stat = SANE_STATUS_GOOD;
624          }
625          else
626          {  /* Set default. */
627             ps->currentContrast = LEDM_CONTRAST_DEFAULT;
628             stat = SANE_STATUS_GOOD;
629          }
630          break;
631       case LEDM_OPTION_BRIGHTNESS:
632          if (action == SANE_ACTION_GET_VALUE)
633          {
634             *int_value = ps->currentBrightness;
635             stat = SANE_STATUS_GOOD;
636          }
637          else if (action == SANE_ACTION_SET_VALUE)
638          {
639             if (*int_value >= LEDM_BRIGHTNESS_MIN && *int_value <= LEDM_BRIGHTNESS_MAX)
640             {
641                ps->currentBrightness = *int_value;
642             }
643             else
644             {
645               ps->currentBrightness = LEDM_BRIGHTNESS_DEFAULT;
646             }
647             stat = SANE_STATUS_GOOD;
648          }
649          else
650          {  /* Set default. */
651             ps->currentBrightness = LEDM_BRIGHTNESS_DEFAULT;
652             stat = SANE_STATUS_GOOD;
653          }
654          break;
655       case LEDM_OPTION_COMPRESSION:
656          if (action == SANE_ACTION_GET_VALUE)
657          {
658             for (i=0; ps->compressionList[i]; i++)
659             {
660                if (ps->currentCompression == ps->compressionMap[i])
661                {
662                   strcpy(value, ps->compressionList[i]);
663                   stat = SANE_STATUS_GOOD;
664                   break;
665                }
666             }
667          }
668          else if (action == SANE_ACTION_SET_VALUE)
669          {
670             for (i=0; ps->compressionList[i]; i++)
671             {
672                if (strcasecmp(ps->compressionList[i], value) == 0)
673                {
674                   ps->currentCompression = ps->compressionMap[i];
675                   stat = SANE_STATUS_GOOD;
676                   break;
677                }
678             }
679          }
680          else
681          {  /* Set default. */
682             ps->currentCompression = SF_JPEG;
683             stat = SANE_STATUS_GOOD;
684          }
685          break;
686       case LEDM_OPTION_JPEG_QUALITY:
687          if (action == SANE_ACTION_GET_VALUE)
688          {
689             *int_value = ps->currentJpegQuality;
690             stat = SANE_STATUS_GOOD;
691          }
692          else if (action == SANE_ACTION_SET_VALUE)
693          {
694             if (*int_value >= MIN_JPEG_COMPRESSION_FACTOR && *int_value <= MAX_JPEG_COMPRESSION_FACTOR)
695             {
696                ps->currentJpegQuality = *int_value;
697                stat = SANE_STATUS_GOOD;
698                break;
699             }
700          }
701          else
702          {  /* Set default. */
703             ps->currentJpegQuality = SAFER_JPEG_COMPRESSION_FACTOR;
704             stat = SANE_STATUS_GOOD;
705          }
706          break;
707       case LEDM_OPTION_TL_X:
708          if (action == SANE_ACTION_GET_VALUE)
709          {
710             *int_value = ps->currentTlx;
711             stat = SANE_STATUS_GOOD;
712          }
713          else if (action == SANE_ACTION_SET_VALUE)
714          {
715             if (*int_value >= ps->tlxRange.min && *int_value <= ps->tlxRange.max)
716             {
717                ps->currentTlx = *int_value;
718                mset_result |= SANE_INFO_RELOAD_PARAMS;
719                stat = SANE_STATUS_GOOD;
720                break;
721             }
722          }
723          else
724          {  /* Set default. */
725             ps->currentTlx = ps->tlxRange.min;
726             stat = SANE_STATUS_GOOD;
727          }
728          break;
729       case LEDM_OPTION_TL_Y:
730          if (action == SANE_ACTION_GET_VALUE)
731          {
732             *int_value = ps->currentTly;
733             stat = SANE_STATUS_GOOD;
734          }
735          else if (action == SANE_ACTION_SET_VALUE)
736          {
737             if (*int_value >= ps->tlyRange.min && *int_value <= ps->tlyRange.max)
738             {
739 
740                ps->currentTly = *int_value;
741                mset_result |= SANE_INFO_RELOAD_PARAMS;
742                stat = SANE_STATUS_GOOD;
743                break;
744             }
745          }
746          else
747          {  /* Set default. */
748             ps->currentTly = ps->tlyRange.min;
749             stat = SANE_STATUS_GOOD;
750          }
751          break;
752       case LEDM_OPTION_BR_X:
753          if (action == SANE_ACTION_GET_VALUE)
754          {
755             *int_value = ps->currentBrx;
756             stat = SANE_STATUS_GOOD;
757          }
758          else if (action == SANE_ACTION_SET_VALUE)
759          {
760             if (*int_value >= ps->brxRange.min && *int_value <= ps->brxRange.max)
761             {
762                ps->currentBrx = *int_value;
763                mset_result |= SANE_INFO_RELOAD_PARAMS;
764                stat = SANE_STATUS_GOOD;
765                break;
766             }
767          }
768          else
769          {  /* Set default. */
770             ps->currentBrx = ps->brxRange.max;
771             stat = SANE_STATUS_GOOD;
772          }
773          break;
774       case LEDM_OPTION_BR_Y:
775          if (action == SANE_ACTION_GET_VALUE)
776          {
777             *int_value = ps->currentBry;
778             stat = SANE_STATUS_GOOD;
779          }
780          else if (action == SANE_ACTION_SET_VALUE)
781          {
782             if (*int_value >= ps->bryRange.min && *int_value <= ps->bryRange.max)
783             {
784                ps->currentBry = *int_value;
785                mset_result |= SANE_INFO_RELOAD_PARAMS;
786                stat = SANE_STATUS_GOOD;
787                break;
788             }
789          }
790          else
791          {  /* Set default. */
792             ps->currentBry = ps->bryRange.max;
793             stat = SANE_STATUS_GOOD;
794          }
795          break;
796       default:
797          break;
798    }
799 
800    if (set_result)
801       *set_result = mset_result;
802 
803   if (stat != SANE_STATUS_GOOD)
804   {
805      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");
806   }
807 
808    return stat;
809 } /* ledm_control_option */
810 
ledm_get_parameters(SANE_Handle handle,SANE_Parameters * params)811 SANE_Status ledm_get_parameters(SANE_Handle handle, SANE_Parameters *params)
812 {
813   struct ledm_session *ps = (struct ledm_session *)handle;
814 
815   set_extents(ps);
816 
817   /* Get scan parameters for sane client. */
818   bb_get_parameters(ps, params, ps->ip_handle ? SPO_STARTED : SPO_BEST_GUESS);
819 
820   DBG8("sane_hpaio_get_parameters(): format=%d, last_frame=%d, lines=%d, depth=%d, pixels_per_line=%d, bytes_per_line=%d\n",
821     params->format, params->last_frame, params->lines, params->depth, params->pixels_per_line, params->bytes_per_line);
822 
823   return SANE_STATUS_GOOD;
824 } /* ledm_get_parameters */
825 
ledm_start(SANE_Handle handle)826 SANE_Status ledm_start(SANE_Handle handle)
827 {
828   struct ledm_session *ps = (struct ledm_session *)handle;
829   SANE_Parameters pp;
830   IP_IMAGE_TRAITS traits;
831   IP_XFORM_SPEC xforms[IP_MAX_XFORMS], *pXform=xforms;
832   int stat, ret;
833 
834   DBG8("sane_hpaio_start()\n");
835 
836   ps -> user_cancel = 0;
837   ps -> cnt = 0;
838   ps -> index = 0;
839 
840   if (set_extents(ps))
841   {
842     stat = SANE_STATUS_INVAL;
843     goto bugout;
844   }
845 
846   /* If input is ADF and ADF is empty, return SANE_STATUS_NO_DOCS. */
847   if (ps->currentInputSource==IS_ADF || ps->currentInputSource ==IS_ADF_DUPLEX)
848   {
849     ret = bb_is_paper_in_adf(ps);   /* 0 = no paper in adf, 1 = paper in adf, -1 = error */
850     if (ret == 0)
851     {
852       stat = SANE_STATUS_NO_DOCS;   /* done scanning */
853       SendScanEvent (ps->uri, EVENT_SCAN_ADF_NO_DOCS);
854       goto bugout;
855     }
856     else if (ret < 0)
857     {
858       stat = SANE_STATUS_IO_ERROR;
859       goto bugout;
860     }
861   }
862 
863    /* Start scan and get actual image traits. */
864   stat = bb_start_scan(ps);
865   if (stat != SANE_STATUS_GOOD)
866     goto bugout;
867 
868   if(ps->user_cancel)
869   {
870     stat = SANE_STATUS_GOOD ;
871     goto bugout;
872   }
873 
874   SendScanEvent(ps->uri, EVENT_START_SCAN_JOB);
875 
876   memset(xforms, 0, sizeof(xforms));
877 
878   /* Setup image-processing pipeline for xform. */
879   if (ps->currentScanMode == CE_COLOR8 || ps->currentScanMode == CE_GRAY8)
880   {
881     switch(ps->currentCompression)
882     {
883       case SF_JPEG:
884         pXform->aXformInfo[IP_JPG_DECODE_FROM_DENALI].dword = 0;    /* 0=no */
885         ADD_XFORM(X_JPG_DECODE);
886         pXform->aXformInfo[IP_CNV_COLOR_SPACE_WHICH_CNV].dword = IP_CNV_YCC_TO_SRGB;
887         pXform->aXformInfo[IP_CNV_COLOR_SPACE_GAMMA].dword = 0x00010000;
888         ADD_XFORM(X_CNV_COLOR_SPACE);
889         break;
890       case SF_RAW:
891       default:
892         break;
893     }
894   }
895   else
896   {  /* Must be BLACK_AND_WHITE1 (Lineart). */
897     switch(ps->currentCompression)
898     {
899       case SF_JPEG:
900         pXform->aXformInfo[IP_JPG_DECODE_FROM_DENALI].dword = 0;    /* 0=no */
901         ADD_XFORM(X_JPG_DECODE);
902         pXform->aXformInfo[IP_GRAY_2_BI_THRESHOLD].dword = 127;
903         ADD_XFORM(X_GRAY_2_BI);
904         break;
905       case SF_RAW:
906         pXform->aXformInfo[IP_GRAY_2_BI_THRESHOLD].dword = 127;
907         ADD_XFORM(X_GRAY_2_BI);
908       default:
909         break;
910     }
911   }
912 
913   /* Setup x/y cropping for xform. (Actually we let cm1017 do it's own cropping) */
914   pXform->aXformInfo[IP_CROP_LEFT].dword = 0;
915   pXform->aXformInfo[IP_CROP_RIGHT].dword = 0;
916   pXform->aXformInfo[IP_CROP_TOP].dword = 0;
917   pXform->aXformInfo[IP_CROP_MAXOUTROWS].dword = 0;
918   ADD_XFORM(X_CROP);
919 
920   /* Setup x/y padding for xform. (Actually we let cm1017 do it's own padding) */
921   pXform->aXformInfo[IP_PAD_LEFT].dword = 0; /* # of pixels to add to left side */
922   pXform->aXformInfo[IP_PAD_RIGHT].dword = 0; /* # of pixels to add to right side */
923   pXform->aXformInfo[IP_PAD_TOP].dword = 0; /* # of rows to add to top */
924   pXform->aXformInfo[IP_PAD_BOTTOM].dword = 0;  /* # of rows to add to bottom */
925   pXform->aXformInfo[IP_PAD_VALUE].dword = ps->currentScanMode == CE_K1 ? 0 : -1;   /* lineart white = 0, rgb white = -1 */
926   pXform->aXformInfo[IP_PAD_MIN_HEIGHT].dword = 0;
927   ADD_XFORM(X_PAD);
928 
929   /* Open image processor. */
930   if ((ret = ipOpen(pXform-xforms, xforms, 0, &ps->ip_handle)) != IP_DONE)
931   {
932     stat = SANE_STATUS_INVAL;
933     goto bugout;
934   }
935 
936   /* Get scan parameters for image processor. */
937   if (ps->currentCompression == SF_RAW)
938     bb_get_parameters(ps, &pp, SPO_STARTED_JR);     /* hpraw, use actual parameters */
939   else
940     bb_get_parameters(ps, &pp, SPO_BEST_GUESS);     /* jpeg, use best guess */
941   traits.iPixelsPerRow = pp.pixels_per_line;
942   switch(ps->currentScanMode)
943   {
944     case CE_K1:                      /* lineart (let IP create Mono from Gray8) */
945     case CE_GRAY8:
946       traits.iBitsPerPixel = 8;     /* grayscale */
947       break;
948     case CE_COLOR8:
949     default:
950       traits.iBitsPerPixel = 24;    /* color */
951       break;
952   }
953   traits.lHorizDPI = ps->currentResolution << 16;
954   traits.lVertDPI = ps->currentResolution << 16;
955   traits.lNumRows =  pp.lines;
956   traits.iNumPages = 1;
957   traits.iPageNum = 1;
958   traits.iComponentsPerPixel = ((traits.iBitsPerPixel % 3) ? 1 : 3);
959   ipSetDefaultInputTraits(ps->ip_handle, &traits);
960 
961   /* If jpeg get output image attributes from the image processor. */
962   if (ps->currentCompression == SF_JPEG)
963   {
964     /* Enable parsed header flag. */
965     ipResultMask(ps->ip_handle, IP_PARSED_HEADER);
966 
967     /* Wait for image processor to process header so we know the exact size of the image for sane_get_params. */
968     while (1)
969     {
970       ret = get_ip_data(ps, NULL, 0, NULL);
971 
972       if (ret & (IP_INPUT_ERROR | IP_FATAL_ERROR | IP_DONE))
973       {
974         stat = SANE_STATUS_IO_ERROR;
975         goto bugout;
976       }
977 
978       if (ret & IP_PARSED_HEADER)
979       {
980         ipGetImageTraits(ps->ip_handle, NULL, &ps->image_traits);  /* get valid image traits */
981         ipResultMask(ps->ip_handle, 0);                          /* disable parsed header flag */
982         break;
983       }
984     }
985   }
986   else
987   ipGetImageTraits(ps->ip_handle, NULL, &ps->image_traits);  /* get valid image traits */
988 
989   stat = SANE_STATUS_GOOD;
990 
991 bugout:
992   if (stat != SANE_STATUS_GOOD)
993   {
994     if (ps->ip_handle)
995     {
996       ipClose(ps->ip_handle);
997       ps->ip_handle = 0;
998     }
999     bb_end_scan(ps, stat == SANE_STATUS_IO_ERROR ? 1: 0);
1000   }
1001   return stat;
1002 } /* ledm_start */
1003 
ledm_read(SANE_Handle handle,SANE_Byte * data,SANE_Int maxLength,SANE_Int * length)1004 SANE_Status ledm_read(SANE_Handle handle, SANE_Byte *data, SANE_Int maxLength, SANE_Int *length)
1005 {
1006   struct ledm_session *ps = (struct ledm_session *)handle;
1007   int ret, stat=SANE_STATUS_IO_ERROR;
1008 
1009   if(ps->user_cancel)
1010   {
1011     SendScanEvent(ps->uri, EVENT_SCAN_CANCEL);
1012     return SANE_STATUS_CANCELLED;
1013   }
1014 
1015   ret = get_ip_data(ps, data, maxLength, length);
1016 
1017   if(ret & (IP_INPUT_ERROR | IP_FATAL_ERROR))
1018   {
1019      goto bugout;
1020    }
1021 
1022   if(ret==IP_DONE)
1023   {
1024     stat = SANE_STATUS_EOF;
1025     SendScanEvent(ps->uri, EVENT_END_SCAN_JOB);
1026   }
1027   else stat= SANE_STATUS_GOOD;
1028 
1029 bugout:
1030   if (stat != SANE_STATUS_GOOD)
1031   {
1032     if (ps->ip_handle)
1033     {
1034       /* 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. */
1035       ipClose(ps->ip_handle);
1036       ps->ip_handle = 0;
1037     }
1038     bb_end_page(ps, 0);
1039   }
1040 
1041   DBG8("-sane_hpaio_read() output=%p bytes_read=%d maxLength=%d status=%d\n", data, *length, maxLength, stat);
1042 
1043   return stat;
1044 } /* ledm_read */
1045 
ledm_cancel(SANE_Handle handle)1046 void ledm_cancel(SANE_Handle handle)
1047 {
1048   struct ledm_session *ps = (struct ledm_session *)handle;
1049 
1050   DBG8("sane_hpaio_cancel()\n");
1051 
1052   ps -> user_cancel = 1;
1053   /* Sane_cancel is always called at the end of the scan job.
1054   Note that on a multiple page scan job sane_cancel is called only once */
1055 
1056   if (ps->ip_handle)
1057   {
1058     ipClose(ps->ip_handle);
1059     ps->ip_handle = 0;
1060   }
1061   bb_end_scan(ps, 0);
1062 } /* ledm_cancel */
1063 
ledm_close(SANE_Handle handle)1064 void ledm_close(SANE_Handle handle)
1065 {
1066   struct ledm_session *ps = (struct ledm_session *)handle;
1067 
1068   if (ps == NULL || ps != session)
1069   {
1070     BUG("invalid sane_close\n");
1071     return;
1072   }
1073 
1074   bb_close(ps);
1075 
1076   if (ps->dd > 0)
1077     hpmud_close_device(ps->dd);
1078 
1079   free(ps);
1080   session = NULL;
1081 }
1082