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