1 /************************************************************************************\
3 marvell.c - HP SANE backend support for Marvell based multi-function peripherals
5 (c) 2008 Copyright Hewlett-Packard Development Company, LP
7 Permission is hereby granted, free of charge, to any person obtaining a copy
8 of this software and associated documentation files (the "Software"), to deal
9 in the Software without restriction, including without limitation the rights
10 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
11 of the Software, and to permit persons to whom the Software is furnished to do
12 so, subject to the following conditions:
14 The above copyright notice and this permission notice shall be included in all
15 copies or substantial portions of the Software.
17 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
19 FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
20 COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
21 IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 Author: David Suffield, Yashwant Sahu
26 \************************************************************************************/
32 #include <sys/socket.h>
38 #include <sys/types.h>
52 #define DEBUG_DECLARE_ONLY
53 #include "sanei_debug.h"
55 static struct marvell_session *session = NULL; /* assume one sane_open per process */
57 static int bb_load(struct marvell_session *ps, const char *so)
63 /* Load hpmud manually with symbols exported. Otherwise the plugin will not find it. */
64 if ((ps->hpmud_handle = dlopen("libhpmud.so", RTLD_LAZY|RTLD_GLOBAL)) == NULL)
66 BUG("unable to load restricted library: %s\n", dlerror());
70 /* Load math library manually with symbols exported (Ubuntu 8.04). Otherwise the plugin will not find it. */
71 if ((ps->math_handle = dlopen("libm.so", RTLD_LAZY|RTLD_GLOBAL)) == NULL)
73 if ((ps->math_handle = dlopen("libm.so.6", RTLD_LAZY|RTLD_GLOBAL)) == NULL)
75 BUG("unable to load restricted library: %s\n", dlerror());
80 if (hpmud_get_conf("[dirs]", "home", home, sizeof(home)) != HPMUD_R_OK)
82 snprintf(sz, sizeof(sz), "%s/scan/plugins/%s", home, so);
83 if ((ps->bb_handle = dlopen(sz, RTLD_NOW|RTLD_GLOBAL)) == NULL)
85 BUG("unable to load restricted library %s: %s\n", sz, dlerror());
86 SendScanEvent(ps->uri, EVENT_PLUGIN_FAIL);
90 if ((ps->bb_open = dlsym(ps->bb_handle, "bb_open")) == NULL)
92 BUG("unable to load restricted library %s: %s\n", sz, dlerror());
95 if ((ps->bb_close = dlsym(ps->bb_handle, "bb_close")) == NULL)
97 BUG("unable to load restricted library %s: %s\n", sz, dlerror());
100 if ((ps->bb_get_parameters = dlsym(ps->bb_handle, "bb_get_parameters")) == NULL)
102 BUG("unable to load restricted library %s: %s\n", sz, dlerror());
105 if ((ps->bb_is_paper_in_adf = dlsym(ps->bb_handle, "bb_is_paper_in_adf")) == NULL)
107 BUG("unable to load restricted library %s: %s\n", sz, dlerror());
110 if ((ps->bb_start_scan = dlsym(ps->bb_handle, "bb_start_scan")) == NULL)
112 BUG("unable to load restricted library %s: %s\n", sz, dlerror());
115 if ((ps->bb_end_scan = dlsym(ps->bb_handle, "bb_end_scan")) == NULL)
117 BUG("unable to load restricted library %s: %s\n", sz, dlerror());
120 if ((ps->bb_get_image_data = dlsym(ps->bb_handle, "bb_get_image_data")) == NULL)
122 BUG("unable to load restricted library %s: %s\n", sz, dlerror());
125 if ((ps->bb_end_page = dlsym(ps->bb_handle, "bb_end_page")) == NULL)
127 BUG("unable to load restricted library %s: %s\n", sz, dlerror());
137 static int bb_unload(struct marvell_session *ps)
141 dlclose(ps->bb_handle);
142 ps->bb_handle = NULL;
144 if (ps->hpmud_handle)
146 dlclose(ps->hpmud_handle);
147 ps->hpmud_handle = NULL;
151 dlclose(ps->math_handle);
152 ps->math_handle = NULL;
157 /* Get raw data (ie: uncompressed data) from image processor. */
158 static int get_ip_data(struct marvell_session *ps, SANE_Byte *data, SANE_Int maxLength, SANE_Int *length)
160 int ip_ret=IP_INPUT_ERROR;
161 unsigned int outputAvail=maxLength, outputUsed=0, outputThisPos;
162 unsigned char *input, *output = data;
163 unsigned int inputAvail, inputUsed=0, inputNextPos;
167 BUG("invalid ipconvert state\n");
171 if (ps->bb_get_image_data(ps, outputAvail))
176 inputAvail = ps->cnt;
181 input = NULL; /* no more scan data, flush ipconvert pipeline */
185 /* Transform input data to output. Note, output buffer may consume more bytes than input buffer (ie: jpeg to raster). */
186 ip_ret = ipConvert(ps->ip_handle, inputAvail, input, &inputUsed, &inputNextPos, outputAvail, output, &outputUsed, &outputThisPos);
188 DBG6("input=%p inputAvail=%d inputUsed=%d inputNextPos=%d output=%p outputAvail=%d outputUsed=%d outputThisPos=%d ret=%x\n", input,
189 inputAvail, inputUsed, inputNextPos, output, outputAvail, outputUsed, outputThisPos, ip_ret);
192 *length = outputUsed;
194 /* For sane do not send output data simultaneously with IP_DONE. */
195 if (ip_ret & IP_DONE && outputUsed)
202 static int init_options(struct marvell_session *ps)
204 ps->option[MARVELL_OPTION_COUNT].name = "option-cnt";
205 ps->option[MARVELL_OPTION_COUNT].title = SANE_TITLE_NUM_OPTIONS;
206 ps->option[MARVELL_OPTION_COUNT].desc = SANE_DESC_NUM_OPTIONS;
207 ps->option[MARVELL_OPTION_COUNT].type = SANE_TYPE_INT;
208 ps->option[MARVELL_OPTION_COUNT].unit = SANE_UNIT_NONE;
209 ps->option[MARVELL_OPTION_COUNT].size = sizeof(SANE_Int);
210 ps->option[MARVELL_OPTION_COUNT].cap = SANE_CAP_SOFT_DETECT;
211 ps->option[MARVELL_OPTION_COUNT].constraint_type = SANE_CONSTRAINT_NONE;
213 ps->option[MARVELL_OPTION_GROUP_SCAN_MODE].name = "mode-group";
214 ps->option[MARVELL_OPTION_GROUP_SCAN_MODE].title = SANE_TITLE_SCAN_MODE;
215 ps->option[MARVELL_OPTION_GROUP_SCAN_MODE].type = SANE_TYPE_GROUP;
217 ps->option[MARVELL_OPTION_SCAN_MODE].name = SANE_NAME_SCAN_MODE;
218 ps->option[MARVELL_OPTION_SCAN_MODE].title = SANE_TITLE_SCAN_MODE;
219 ps->option[MARVELL_OPTION_SCAN_MODE].desc = SANE_DESC_SCAN_MODE;
220 ps->option[MARVELL_OPTION_SCAN_MODE].type = SANE_TYPE_STRING;
221 ps->option[MARVELL_OPTION_SCAN_MODE].unit = SANE_UNIT_NONE;
222 ps->option[MARVELL_OPTION_SCAN_MODE].size = MAX_STRING_SIZE;
223 ps->option[MARVELL_OPTION_SCAN_MODE].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
224 ps->option[MARVELL_OPTION_SCAN_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
225 ps->option[MARVELL_OPTION_SCAN_MODE].constraint.string_list = ps->scan_mode_list;
227 ps->option[MARVELL_OPTION_INPUT_SOURCE].name = SANE_NAME_SCAN_SOURCE;
228 ps->option[MARVELL_OPTION_INPUT_SOURCE].title = SANE_TITLE_SCAN_SOURCE;
229 ps->option[MARVELL_OPTION_INPUT_SOURCE].desc = SANE_DESC_SCAN_SOURCE;
230 ps->option[MARVELL_OPTION_INPUT_SOURCE].type = SANE_TYPE_STRING;
231 ps->option[MARVELL_OPTION_INPUT_SOURCE].unit = SANE_UNIT_NONE;
232 ps->option[MARVELL_OPTION_INPUT_SOURCE].size = MAX_STRING_SIZE;
233 ps->option[MARVELL_OPTION_INPUT_SOURCE].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
234 ps->option[MARVELL_OPTION_INPUT_SOURCE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
235 ps->option[MARVELL_OPTION_INPUT_SOURCE].constraint.string_list = ps->input_source_list;
237 ps->option[MARVELL_OPTION_SCAN_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION;
238 ps->option[MARVELL_OPTION_SCAN_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION;
239 ps->option[MARVELL_OPTION_SCAN_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION;
240 ps->option[MARVELL_OPTION_SCAN_RESOLUTION].type = SANE_TYPE_INT;
241 ps->option[MARVELL_OPTION_SCAN_RESOLUTION].unit = SANE_UNIT_DPI;
242 ps->option[MARVELL_OPTION_SCAN_RESOLUTION].size = sizeof(SANE_Int);
243 ps->option[MARVELL_OPTION_SCAN_RESOLUTION].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
244 ps->option[MARVELL_OPTION_SCAN_RESOLUTION].constraint_type = SANE_CONSTRAINT_WORD_LIST;
245 ps->option[MARVELL_OPTION_SCAN_RESOLUTION].constraint.word_list = ps->resolution_list;
247 ps->option[MARVELL_OPTION_GROUP_ADVANCED].name = "advanced-group";
248 ps->option[MARVELL_OPTION_GROUP_ADVANCED].title = STR_TITLE_ADVANCED;
249 ps->option[MARVELL_OPTION_GROUP_ADVANCED].type = SANE_TYPE_GROUP;
250 ps->option[MARVELL_OPTION_GROUP_ADVANCED].cap = SANE_CAP_ADVANCED;
252 ps->option[MARVELL_OPTION_CONTRAST].name = SANE_NAME_CONTRAST;
253 ps->option[MARVELL_OPTION_CONTRAST].title = SANE_TITLE_CONTRAST;
254 ps->option[MARVELL_OPTION_CONTRAST].desc = SANE_DESC_CONTRAST;
255 ps->option[MARVELL_OPTION_CONTRAST].type = SANE_TYPE_INT;
256 ps->option[MARVELL_OPTION_CONTRAST].unit = SANE_UNIT_NONE;
257 ps->option[MARVELL_OPTION_CONTRAST].size = sizeof(SANE_Int);
258 ps->option[MARVELL_OPTION_CONTRAST].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED;
259 ps->option[MARVELL_OPTION_CONTRAST].constraint_type = SANE_CONSTRAINT_RANGE;
260 ps->option[MARVELL_OPTION_CONTRAST].constraint.range = &ps->contrast_range;
261 ps->contrast_range.min = MARVELL_CONTRAST_MIN;
262 ps->contrast_range.max = MARVELL_CONTRAST_MAX;
263 ps->contrast_range.quant = 0;
265 ps->option[MARVELL_OPTION_GROUP_GEOMETRY].name = "geometry-group";
266 ps->option[MARVELL_OPTION_GROUP_GEOMETRY].title = STR_TITLE_GEOMETRY;
267 ps->option[MARVELL_OPTION_GROUP_GEOMETRY].type = SANE_TYPE_GROUP;
268 ps->option[MARVELL_OPTION_GROUP_GEOMETRY].cap = SANE_CAP_ADVANCED;
270 ps->option[MARVELL_OPTION_TL_X].name = SANE_NAME_SCAN_TL_X;
271 ps->option[MARVELL_OPTION_TL_X].title = SANE_TITLE_SCAN_TL_X;
272 ps->option[MARVELL_OPTION_TL_X].desc = SANE_DESC_SCAN_TL_X;
273 ps->option[MARVELL_OPTION_TL_X].type = SANE_TYPE_FIXED;
274 ps->option[MARVELL_OPTION_TL_X].unit = SANE_UNIT_MM;
275 ps->option[MARVELL_OPTION_TL_X].size = sizeof(SANE_Int);
276 ps->option[MARVELL_OPTION_TL_X].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
277 ps->option[MARVELL_OPTION_TL_X].constraint_type = SANE_CONSTRAINT_RANGE;
278 ps->option[MARVELL_OPTION_TL_X].constraint.range = &ps->tlxRange;
279 ps->tlxRange.min = 0;
280 ps->tlxRange.quant = 0;
282 ps->option[MARVELL_OPTION_TL_Y].name = SANE_NAME_SCAN_TL_Y;
283 ps->option[MARVELL_OPTION_TL_Y].title = SANE_TITLE_SCAN_TL_Y;
284 ps->option[MARVELL_OPTION_TL_Y].desc = SANE_DESC_SCAN_TL_Y;
285 ps->option[MARVELL_OPTION_TL_Y].type = SANE_TYPE_FIXED;
286 ps->option[MARVELL_OPTION_TL_Y].unit = SANE_UNIT_MM;
287 ps->option[MARVELL_OPTION_TL_Y].size = sizeof(SANE_Int);
288 ps->option[MARVELL_OPTION_TL_Y].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
289 ps->option[MARVELL_OPTION_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE;
290 ps->option[MARVELL_OPTION_TL_Y].constraint.range = &ps->tlyRange;
291 ps->tlyRange.min = 0;
292 ps->tlyRange.quant = 0;
294 ps->option[MARVELL_OPTION_BR_X].name = SANE_NAME_SCAN_BR_X;
295 ps->option[MARVELL_OPTION_BR_X].title = SANE_TITLE_SCAN_BR_X;
296 ps->option[MARVELL_OPTION_BR_X].desc = SANE_DESC_SCAN_BR_X;
297 ps->option[MARVELL_OPTION_BR_X].type = SANE_TYPE_FIXED;
298 ps->option[MARVELL_OPTION_BR_X].unit = SANE_UNIT_MM;
299 ps->option[MARVELL_OPTION_BR_X].size = sizeof(SANE_Int);
300 ps->option[MARVELL_OPTION_BR_X].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
301 ps->option[MARVELL_OPTION_BR_X].constraint_type = SANE_CONSTRAINT_RANGE;
302 ps->option[MARVELL_OPTION_BR_X].constraint.range = &ps->brxRange;
303 ps->brxRange.min = 0;
304 ps->brxRange.quant = 0;
306 ps->option[MARVELL_OPTION_BR_Y].name = SANE_NAME_SCAN_BR_Y;
307 ps->option[MARVELL_OPTION_BR_Y].title = SANE_TITLE_SCAN_BR_Y;
308 ps->option[MARVELL_OPTION_BR_Y].desc = SANE_DESC_SCAN_BR_Y;
309 ps->option[MARVELL_OPTION_BR_Y].type = SANE_TYPE_FIXED;
310 ps->option[MARVELL_OPTION_BR_Y].unit = SANE_UNIT_MM;
311 ps->option[MARVELL_OPTION_BR_Y].size = sizeof(SANE_Int);
312 ps->option[MARVELL_OPTION_BR_Y].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
313 ps->option[MARVELL_OPTION_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE;
314 ps->option[MARVELL_OPTION_BR_Y].constraint.range = &ps->bryRange;
315 ps->bryRange.min = 0;
316 ps->bryRange.quant = 0;
321 /* Verify current x/y extents and set effective extents. */
322 static int set_extents(struct marvell_session *ps)
326 if ((ps->currentBrx > ps->currentTlx) && (ps->currentBrx - ps->currentTlx >= ps->min_width) && (ps->currentBrx - ps->currentTlx <= ps->tlxRange.max))
328 ps->effectiveTlx = ps->currentTlx;
329 ps->effectiveBrx = ps->currentBrx;
333 ps->effectiveTlx = 0; /* current setting is not valid, zero it */
334 ps->effectiveBrx = 0;
337 if ((ps->currentBry > ps->currentTly) && (ps->currentBry - ps->currentTly > ps->min_height) && (ps->currentBry - ps->currentTly <= ps->tlyRange.max))
339 ps->effectiveTly = ps->currentTly;
340 ps->effectiveBry = ps->currentBry;
344 ps->effectiveTly = 0; /* current setting is not valid, zero it */
345 ps->effectiveBry = 0;
351 static struct marvell_session *create_session()
353 struct marvell_session *ps;
355 if ((ps = malloc(sizeof(struct marvell_session))) == NULL)
357 BUG("malloc failed: %m\n");
360 memset(ps, 0, sizeof(struct marvell_session));
372 SANE_Status marvell_open(SANE_String_Const device, SANE_Handle *handle)
374 struct hpmud_model_attributes ma;
375 int stat = SANE_STATUS_IO_ERROR;
378 DBG8("sane_hpaio_open(%s)\n", device);
382 BUG("session in use\n");
383 return SANE_STATUS_DEVICE_BUSY;
386 if ((session = create_session()) == NULL)
387 return SANE_STATUS_NO_MEM;
389 /* Set session to specified device. */
390 snprintf(session->uri, sizeof(session->uri)-1, "hp:%s", device); /* prepend "hp:" */
392 /* Get actual model attributes from models.dat. */
393 hpmud_query_model(session->uri, &ma);
394 session->scan_type = ma.scantype;
395 session->scansrc = ma.scansrc;
399 case HPMUD_SCANTYPE_MARVELL:
400 session->version = MARVELL_1;
402 case HPMUD_SCANTYPE_MARVELL2:
403 session->version = MARVELL_2;
406 session->version = MARVELL_1;
409 if (hpmud_open_device(session->uri, ma.mfp_mode, &session->dd) != HPMUD_R_OK)
411 BUG("unable to open device %s\n", session->uri);
416 return SANE_STATUS_IO_ERROR;
419 if (hpmud_open_channel(session->dd, HPMUD_S_MARVELL_SCAN_CHANNEL, &session->cd) != HPMUD_R_OK)
421 BUG("unable to open %s channel %s\n", HPMUD_S_MARVELL_SCAN_CHANNEL, session->uri);
422 stat = SANE_STATUS_DEVICE_BUSY;
426 if (bb_load(session, "bb_marvell.so"))
428 stat = SANE_STATUS_IO_ERROR;
432 /* Init sane option descriptors. */
433 init_options(session);
435 if (session->bb_open(session))
437 stat = SANE_STATUS_IO_ERROR;
441 /* Set supported Scan Modes and set sane option. */
443 session->scan_mode_list[i] = SANE_VALUE_SCAN_MODE_LINEART;
444 session->scan_mode_map[i++] = CE_BLACK_AND_WHITE1;
445 session->scan_mode_list[i] = SANE_VALUE_SCAN_MODE_GRAY;
446 session->scan_mode_map[i++] = CE_GRAY8;
447 session->scan_mode_list[i] = SANE_VALUE_SCAN_MODE_COLOR;
448 session->scan_mode_map[i++] = CE_RGB24;
449 marvell_control_option(session, MARVELL_OPTION_SCAN_MODE, SANE_ACTION_SET_AUTO, NULL, NULL); /* set default option */
452 /* Determine scan input source. */
454 /* Some of the marvell devices supports both flatbed and ADF, No command to get the src types supported */
455 /* Getting from the model file */
456 if ( session->scansrc & HPMUD_SCANSRC_ADF)
458 session->input_source_list[i] = STR_ADF_MODE_ADF;
459 session->input_source_map[i++] = IS_ADF;
460 DBG8("scan src HPMUD_SCANSRC_ADF \n");
462 if ( session->scansrc & HPMUD_SCANSRC_FLATBED)
464 session->input_source_list[i] = STR_ADF_MODE_FLATBED;
465 session->input_source_map[i++] = IS_PLATEN;
466 DBG8("scan src HPMUD_SCANSRC_FLATBED \n");
468 /* Values if un specified in the, value is 0, get ADF state from the printer */
469 if (session->scansrc == HPMUD_SCANSRC_NA)
471 if (session->bb_is_paper_in_adf(session) == 2)
473 session->input_source_list[i] = STR_ADF_MODE_FLATBED;
474 session->input_source_map[i++] = IS_PLATEN;
475 DBG8("scan src b_is_paper_in_adf value 2 \n");
479 session->input_source_list[i] = STR_ADF_MODE_ADF;
480 session->input_source_map[i++] = IS_ADF;
481 DBG8("scan src b_is_paper_in_adf value not 2 \n");
485 marvell_control_option(session, MARVELL_OPTION_INPUT_SOURCE, SANE_ACTION_SET_AUTO, NULL, NULL); /* set default option */
487 /* Set supported resolutions. */
489 session->resolution_list[i++] = 75;
490 session->resolution_list[i++] = 100;
491 session->resolution_list[i++] = 150;
492 session->resolution_list[i++] = 200;
493 session->resolution_list[i++] = 300;
494 session->resolution_list[i++] = 600;
495 session->resolution_list[i++] = 1200;
496 session->resolution_list[0] = i-1; /* length of word_list */
497 marvell_control_option(session, MARVELL_OPTION_SCAN_RESOLUTION, SANE_ACTION_SET_AUTO, NULL, NULL); /* set default option */
499 /* Set supported contrast. */
500 marvell_control_option(session, MARVELL_OPTION_CONTRAST, SANE_ACTION_SET_AUTO, NULL, NULL); /* set default option */
502 /* Set x,y extents. See bb_open(). */
503 marvell_control_option(session, MARVELL_OPTION_TL_X, SANE_ACTION_SET_AUTO, NULL, NULL); /* set default option */
504 marvell_control_option(session, MARVELL_OPTION_TL_Y, SANE_ACTION_SET_AUTO, NULL, NULL); /* set default option */
505 marvell_control_option(session, MARVELL_OPTION_BR_X, SANE_ACTION_SET_AUTO, NULL, NULL); /* set default option */
506 marvell_control_option(session, MARVELL_OPTION_BR_Y, SANE_ACTION_SET_AUTO, NULL, NULL); /* set default option */
508 *handle = (SANE_Handle *)session;
510 stat = SANE_STATUS_GOOD;
514 if (stat != SANE_STATUS_GOOD)
520 hpmud_close_channel(session->dd, session->cd);
522 hpmud_close_device(session->dd);
531 void marvell_close(SANE_Handle handle)
533 struct marvell_session *ps = (struct marvell_session *)handle;
535 DBG8("sane_hpaio_close()\n");
537 if (ps == NULL || ps != session)
539 BUG("invalid sane_close\n");
549 hpmud_close_channel(ps->dd, ps->cd);
550 hpmud_close_device(ps->dd);
557 const SANE_Option_Descriptor *marvell_get_option_descriptor(SANE_Handle handle, SANE_Int option)
559 struct marvell_session *ps = (struct marvell_session *)handle;
561 DBG8("sane_hpaio_get_option_descriptor(option=%s)\n", ps->option[option].name);
563 if (option < 0 || option >= MARVELL_OPTION_MAX)
566 return &ps->option[option];
569 SANE_Status marvell_control_option(SANE_Handle handle, SANE_Int option, SANE_Action action, void *value, SANE_Int *set_result)
571 struct marvell_session *ps = (struct marvell_session *)handle;
572 SANE_Int *int_value = value, mset_result=0;
573 int i, stat=SANE_STATUS_INVAL;
578 case MARVELL_OPTION_COUNT:
579 if (action == SANE_ACTION_GET_VALUE)
581 *int_value = MARVELL_OPTION_MAX;
582 stat = SANE_STATUS_GOOD;
585 case MARVELL_OPTION_SCAN_MODE:
586 if (action == SANE_ACTION_GET_VALUE)
588 for (i=0; ps->scan_mode_list[i]; i++)
590 if (ps->current_scan_mode == ps->scan_mode_map[i])
592 strcpy(value, ps->scan_mode_list[i]);
593 stat = SANE_STATUS_GOOD;
598 else if (action == SANE_ACTION_SET_VALUE)
600 for (i=0; ps->scan_mode_list[i]; i++)
602 if (strcasecmp(ps->scan_mode_list[i], value) == 0)
604 ps->current_scan_mode = ps->scan_mode_map[i];
605 mset_result |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
606 stat = SANE_STATUS_GOOD;
613 ps->current_scan_mode = ps->scan_mode_map[0];
614 stat = SANE_STATUS_GOOD;
617 case MARVELL_OPTION_INPUT_SOURCE:
618 if (action == SANE_ACTION_GET_VALUE)
620 for (i=0; ps->input_source_list[i]; i++)
622 if (ps->current_input_source == ps->input_source_map[i])
624 strcpy(value, ps->input_source_list[i]);
625 stat = SANE_STATUS_GOOD;
630 else if (action == SANE_ACTION_SET_VALUE)
632 for (i=0; ps->input_source_list[i]; i++)
634 if (strcasecmp(ps->input_source_list[i], value) == 0)
636 ps->current_input_source = ps->input_source_map[i];
637 stat = SANE_STATUS_GOOD;
644 ps->current_input_source = ps->input_source_map[0];
645 stat = SANE_STATUS_GOOD;
648 case MARVELL_OPTION_SCAN_RESOLUTION:
649 if (action == SANE_ACTION_GET_VALUE)
651 *int_value = ps->current_resolution;
652 stat = SANE_STATUS_GOOD;
654 else if (action == SANE_ACTION_SET_VALUE)
656 for (i=1; i <= ps->resolution_list[0]; i++)
658 if (ps->resolution_list[i] == *int_value)
660 ps->current_resolution = *int_value;
661 mset_result |= SANE_INFO_RELOAD_PARAMS;
662 stat = SANE_STATUS_GOOD;
669 ps->current_resolution = 75;
670 stat = SANE_STATUS_GOOD;
673 case MARVELL_OPTION_CONTRAST:
674 if (action == SANE_ACTION_GET_VALUE)
676 *int_value = ps->current_contrast;
677 stat = SANE_STATUS_GOOD;
679 else if (action == SANE_ACTION_SET_VALUE)
681 if (*int_value >= MARVELL_CONTRAST_MIN && *int_value <= MARVELL_CONTRAST_MAX)
683 ps->current_contrast = *int_value;
684 stat = SANE_STATUS_GOOD;
690 ps->current_contrast = MARVELL_CONTRAST_DEFAULT;
691 stat = SANE_STATUS_GOOD;
694 case MARVELL_OPTION_TL_X:
695 if (action == SANE_ACTION_GET_VALUE)
697 *int_value = ps->currentTlx;
698 stat = SANE_STATUS_GOOD;
700 else if (action == SANE_ACTION_SET_VALUE)
702 if (*int_value >= ps->tlxRange.min && *int_value <= ps->tlxRange.max)
704 ps->currentTlx = *int_value;
705 mset_result |= SANE_INFO_RELOAD_PARAMS;
706 stat = SANE_STATUS_GOOD;
712 ps->currentTlx = ps->tlxRange.min;
713 stat = SANE_STATUS_GOOD;
716 case MARVELL_OPTION_TL_Y:
717 if (action == SANE_ACTION_GET_VALUE)
719 *int_value = ps->currentTly;
720 stat = SANE_STATUS_GOOD;
722 else if (action == SANE_ACTION_SET_VALUE)
724 if (*int_value >= ps->tlyRange.min && *int_value <= ps->tlyRange.max)
727 ps->currentTly = *int_value;
728 mset_result |= SANE_INFO_RELOAD_PARAMS;
729 stat = SANE_STATUS_GOOD;
735 ps->currentTly = ps->tlyRange.min;
736 stat = SANE_STATUS_GOOD;
739 case MARVELL_OPTION_BR_X:
740 if (action == SANE_ACTION_GET_VALUE)
742 *int_value = ps->currentBrx;
743 stat = SANE_STATUS_GOOD;
745 else if (action == SANE_ACTION_SET_VALUE)
747 if (*int_value >= ps->brxRange.min && *int_value <= ps->brxRange.max)
749 ps->currentBrx = *int_value;
750 mset_result |= SANE_INFO_RELOAD_PARAMS;
751 stat = SANE_STATUS_GOOD;
757 ps->currentBrx = ps->brxRange.max;
758 stat = SANE_STATUS_GOOD;
761 case MARVELL_OPTION_BR_Y:
762 if (action == SANE_ACTION_GET_VALUE)
764 *int_value = ps->currentBry;
765 stat = SANE_STATUS_GOOD;
767 else if (action == SANE_ACTION_SET_VALUE)
769 if (*int_value >= ps->bryRange.min && *int_value <= ps->bryRange.max)
771 ps->currentBry = *int_value;
772 mset_result |= SANE_INFO_RELOAD_PARAMS;
773 stat = SANE_STATUS_GOOD;
776 BUG("value=%d brymin=%d brymax=%d\n", *int_value, ps->bryRange.min, ps->bryRange.max);
780 ps->currentBry = ps->bryRange.max;
781 stat = SANE_STATUS_GOOD;
789 *set_result = mset_result;
791 if (stat != SANE_STATUS_GOOD)
793 BUG("control_option failed: option=%s action=%s\n", ps->option[option].name,
794 action==SANE_ACTION_GET_VALUE ? "get" : action==SANE_ACTION_SET_VALUE ? "set" : "auto");
797 DBG8("sane_hpaio_control_option (option=%s action=%s value=%s)\n", ps->option[option].name,
798 action==SANE_ACTION_GET_VALUE ? "get" : action==SANE_ACTION_SET_VALUE ? "set" : "auto",
799 value ? ps->option[option].type == SANE_TYPE_STRING ? (char *)value : psnprintf(sz, sizeof(sz), "%d", *(int *)value) : "na");
804 SANE_Status marvell_get_parameters(SANE_Handle handle, SANE_Parameters *params)
806 struct marvell_session *ps = (struct marvell_session *)handle;
810 ps->bb_get_parameters(ps, params, ps->ip_handle ? 1 : 0);
812 DBG8("sane_hpaio_get_parameters(): format=%d, last_frame=%d, lines=%d, depth=%d, pixels_per_line=%d, bytes_per_line=%d\n",
813 params->format, params->last_frame, params->lines, params->depth, params->pixels_per_line, params->bytes_per_line);
815 return SANE_STATUS_GOOD;
818 SANE_Status marvell_start(SANE_Handle handle)
820 struct marvell_session *ps = (struct marvell_session *)handle;
822 IP_IMAGE_TRAITS traits;
823 IP_XFORM_SPEC xforms[IP_MAX_XFORMS], *pXform=xforms;
825 // int tmo=EXCEPTION_TIMEOUT*2;
827 DBG8("sane_hpaio_start()\n");
828 ps->is_user_cancel = 0;
832 BUG("invalid extents: tlx=%d brx=%d tly=%d bry=%d minwidth=%d minheight%d maxwidth=%d maxheight=%d\n",
833 ps->currentTlx, ps->currentTly, ps->currentBrx, ps->currentBry, ps->min_width, ps->min_height, ps->tlxRange.max, ps->tlyRange.max);
834 stat = SANE_STATUS_INVAL;
838 /* If input is ADF and ADF is empty, return SANE_STATUS_NO_DOCS. */
839 if (ps->current_input_source == IS_ADF)
841 ret = ps->bb_is_paper_in_adf(ps); /* 0 = no paper in adf, 1 = paper in adf, 2 = no adf, -1 = error */
844 stat = SANE_STATUS_NO_DOCS; /* done scanning */
845 SendScanEvent(ps->uri, EVENT_SCAN_ADF_NO_DOCS);
850 stat = SANE_STATUS_IO_ERROR;
854 /* Start scan and get actual image traits. */
855 if (ps->bb_start_scan(ps))
857 stat = SANE_STATUS_IO_ERROR;
861 SendScanEvent(ps->uri, EVENT_START_SCAN_JOB);
862 memset(xforms, 0, sizeof(xforms));
864 /* Setup image-processing pipeline for xform. */
865 if (ps->current_scan_mode == CE_BLACK_AND_WHITE1)
867 pXform->aXformInfo[IP_GRAY_2_BI_THRESHOLD].dword = 127;
868 ADD_XFORM(X_GRAY_2_BI);
871 /* Setup x/y cropping for xform. (Actually we let cm1017 do it's own cropping) */
872 pXform->aXformInfo[IP_CROP_LEFT].dword = 0;
873 pXform->aXformInfo[IP_CROP_RIGHT].dword = 0;
874 pXform->aXformInfo[IP_CROP_TOP].dword = 0;
875 pXform->aXformInfo[IP_CROP_MAXOUTROWS].dword = 0;
878 /* Setup x/y padding for xform. (Actually we let cm1017 do it's own padding) */
879 pXform->aXformInfo[IP_PAD_LEFT].dword = 0; /* # of pixels to add to left side */
880 pXform->aXformInfo[IP_PAD_RIGHT].dword = 0; /* # of pixels to add to right side */
881 pXform->aXformInfo[IP_PAD_TOP].dword = 0; /* # of rows to add to top */
882 pXform->aXformInfo[IP_PAD_BOTTOM].dword = 0; /* # of rows to add to bottom */
883 pXform->aXformInfo[IP_PAD_VALUE].dword = ps->current_scan_mode == CE_BLACK_AND_WHITE1 ? 0 : -1; /* lineart white = 0, rgb white = -1 */
884 pXform->aXformInfo[IP_PAD_MIN_HEIGHT].dword = 0;
887 /* Open image processor. */
888 if ((ret = ipOpen(pXform-xforms, xforms, 0, &ps->ip_handle)) != IP_DONE)
890 BUG("unable open image processor: err=%d\n", ret);
891 stat = SANE_STATUS_INVAL;
895 /* Get actual input image attributes. See bb_start_scan(). */
896 ps->bb_get_parameters(ps, &pp, 1);
898 /* Now set known input image attributes. */
899 traits.iPixelsPerRow = pp.pixels_per_line;
900 switch(ps->current_scan_mode)
902 case CE_BLACK_AND_WHITE1: /* lineart */
904 traits.iBitsPerPixel = 8; /* grayscale */
908 traits.iBitsPerPixel = 24; /* color */
911 traits.lHorizDPI = ps->current_resolution << 16;
912 traits.lVertDPI = ps->current_resolution << 16;
913 traits.lNumRows = pp.lines;
914 traits.iNumPages = 1;
916 traits.iComponentsPerPixel = ((traits.iBitsPerPixel % 3) ? 1 : 3);
917 ipSetDefaultInputTraits(ps->ip_handle, &traits);
919 /* Get output image attributes from the image processor. */
920 ipGetImageTraits(ps->ip_handle, NULL, &ps->image_traits); /* get valid image traits */
922 stat = SANE_STATUS_GOOD;
925 if (stat != SANE_STATUS_GOOD)
929 ipClose(ps->ip_handle);
932 ps->bb_end_scan(ps, stat == SANE_STATUS_IO_ERROR ? 1: 0);
938 SANE_Status marvell_read(SANE_Handle handle, SANE_Byte *data, SANE_Int maxLength, SANE_Int *length)
940 struct marvell_session *ps = (struct marvell_session *)handle;
941 int ret, stat=SANE_STATUS_IO_ERROR;
942 // int tmo=EXCEPTION_TIMEOUT;
944 DBG8("sane_hpaio_read() handle=%p data=%p maxLength=%d\n", (void *)handle, data, maxLength);
946 ret = get_ip_data(ps, data, maxLength, length);
948 if(ret & (IP_INPUT_ERROR | IP_FATAL_ERROR))
950 BUG("ipConvert error=%x\n", ret);
956 stat = SANE_STATUS_EOF;
957 SendScanEvent(ps->uri, EVENT_END_SCAN_JOB);
960 stat = SANE_STATUS_GOOD;
963 if (stat != SANE_STATUS_GOOD)
967 /* 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. */
968 ipClose(ps->ip_handle);
971 //If user has cancelled scan from device
972 if (ps->is_user_cancel)
974 //Don't do anything. sane_hpaio_cancel() will be invoked automatically
975 SendScanEvent(ps->uri, EVENT_SCAN_CANCEL);
976 return SANE_STATUS_CANCELLED;
981 ps->bb_end_page(ps, stat == SANE_STATUS_IO_ERROR ? 1: 0);
985 DBG8("-sane_hpaio_read() output=%p bytes_read=%d maxLength=%d status=%d\n", data, *length, maxLength, stat);
990 void marvell_cancel(SANE_Handle handle)
992 struct marvell_session *ps = (struct marvell_session *)handle;
994 DBG8("sane_hpaio_cancel()\n");
997 * Sane_cancel is always called at the end of the scan job. Note that on a multiple page scan job
998 * sane_cancel is called only once.
1000 ps->is_user_cancel = 1 ;
1004 ipClose(ps->ip_handle);
1007 ps->bb_end_scan(ps, 0);