/* sane - Scanner Access Now Easy. ScanMaker 3840 Backend Copyright (C) 2005-7 Earle F. Philhower, III earle@ziplabel.com - http://www.ziplabel.com This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . As a special exception, the authors of SANE give permission for additional uses of the libraries contained in this release of SANE. The exception is that, if you link a SANE library with other files to produce an executable, this does not by itself cause the resulting executable to be covered by the GNU General Public License. Your use of that executable is in no way restricted on account of linking the SANE library code into it. This exception does not, however, invalidate any other reasons why the executable file might be covered by the GNU General Public License. If you submit changes to SANE to the maintainers to be included in a subsequent release, you agree by submitting the changes that those changes may be distributed with this exception intact. If you write modifications of your own for SANE, it is your choice whether to permit this exception to apply to your modifications. If you do not wish that, delete this exception notice. */ #include "../include/sane/config.h" #include #include #include #include #include #include #include #include #include #include #include "../include/sane/sane.h" #include "../include/sane/saneopts.h" #define BACKENDNAME sm3840 #include "../include/sane/sanei_backend.h" #include "../include/sane/sanei_usb.h" #include "../include/sane/sanei_config.h" #include "sm3840.h" #include "sm3840_scan.c" #include "sm3840_lib.c" static double sm3840_unit_convert (SANE_Int val); static int num_devices; static SM3840_Device *first_dev; static SM3840_Scan *first_handle; static const SANE_Device **devlist = 0; static const SANE_String_Const mode_list[] = { SANE_VALUE_SCAN_MODE_GRAY, SANE_VALUE_SCAN_MODE_COLOR, SANE_VALUE_SCAN_MODE_LINEART, SANE_VALUE_SCAN_MODE_HALFTONE, 0 }; static const SANE_Word resolution_list[] = { 4, 1200, 600, 300, 150 }; static const SANE_Word bpp_list[] = { 2, 8, 16 }; static const SANE_Range x_range = { SANE_FIX (0), SANE_FIX (215.91), /* 8.5 inches */ SANE_FIX (0) }; static const SANE_Range y_range = { SANE_FIX (0), SANE_FIX (297.19), /* 11.7 inches */ SANE_FIX (0) }; static const SANE_Range brightness_range = { 1, 4096, 1.0 }; static const SANE_Range contrast_range = { SANE_FIX (0.1), SANE_FIX (9.9), SANE_FIX (0.1) }; static const SANE_Range lamp_range = { 1, 15, 1 }; static const SANE_Range threshold_range = { 0, 255, 1 }; /*--------------------------------------------------------------------------*/ static int min (int a, int b) { if (a < b) return a; else return b; } SANE_Status sane_read (SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len, SANE_Int * len) { SM3840_Scan *s = handle; unsigned char c, d; int i; DBG (2, "+sane-read:%p %p %d %p\n", (void *) s, (void *) buf, max_len, (void *) len); DBG (2, "+sane-read:remain:%lu offset:%lu linesleft:%d linebuff:%p linesread:%d\n", (u_long) s->remaining, (u_long) s->offset, s->linesleft, (void *) s->line_buffer, s->linesread); if (!s->scanning) return SANE_STATUS_INVAL; if (!s->remaining) { if (!s->linesleft) { *len = 0; s->scanning = 0; /* Move to home position */ reset_scanner ((p_usb_dev_handle)s->udev); /* Send lamp timeout */ set_lamp_timer ((p_usb_dev_handle)s->udev, s->sm3840_params.lamp); /* Free memory */ if (s->save_scan_line) free (s->save_scan_line); s->save_scan_line = NULL; if (s->save_dpi1200_remap) free (s->save_dpi1200_remap); s->save_dpi1200_remap = NULL; if (s->save_color_remap) free (s->save_color_remap); s->save_color_remap = NULL; return SANE_STATUS_EOF; } record_line ((s->linesread == 0) ? 1 : 0, (p_usb_dev_handle) s->udev, s->line_buffer, s->sm3840_params.dpi, s->sm3840_params.scanpix, s->sm3840_params.gray, (s->sm3840_params.bpp == 16) ? 1 : 0, &s->save_i, &s->save_scan_line, &s->save_dpi1200_remap, &s->save_color_remap); s->remaining = s->sm3840_params.linelen; s->offset = 0; s->linesread++; s->linesleft--; } /* Need to software emulate 1-bpp modes, simple threshold and error */ /* diffusion dither implemented. */ if (s->sm3840_params.lineart || s->sm3840_params.halftone) { d = 0; for (i = 0; i < min (max_len * 8, s->remaining); i++) { d = d << 1; if (s->sm3840_params.halftone) { c = (*(unsigned char *) (s->offset + s->line_buffer + i)); if (c + s->save_dither_err < 128) { d |= 1; s->save_dither_err += c; } else { s->save_dither_err += c - 255; } } else { if ((*(unsigned char *) (s->offset + s->line_buffer + i)) < s->threshold ) d |= 1; } if (i % 8 == 7) *(buf++) = d; } *len = i / 8; s->offset += i; s->remaining -= i; } else { memcpy (buf, s->offset + s->line_buffer, min (max_len, s->remaining)); *len = min (max_len, s->remaining); s->offset += min (max_len, s->remaining); s->remaining -= min (max_len, s->remaining); } DBG (2, "-sane_read\n"); return SANE_STATUS_GOOD; } /*--------------------------------------------------------------------------*/ void sane_cancel (SANE_Handle h) { SM3840_Scan *s = h; DBG (2, "trying to cancel...\n"); if (s->scanning) { if (!s->cancelled) { /* Move to home position */ reset_scanner ((p_usb_dev_handle) s->udev); /* Send lamp timeout */ set_lamp_timer ((p_usb_dev_handle) s->udev, s->sm3840_params.lamp); /* Free memory */ if (s->save_scan_line) free (s->save_scan_line); s->save_scan_line = NULL; if (s->save_dpi1200_remap) free (s->save_dpi1200_remap); s->save_dpi1200_remap = NULL; if (s->save_color_remap) free (s->save_color_remap); s->save_color_remap = NULL; s->scanning = 0; s->cancelled = SANE_TRUE; } } } /*--------------------------------------------------------------------------*/ SANE_Status sane_start (SANE_Handle handle) { SM3840_Scan *s = handle; SANE_Status status; /* First make sure we have a current parameter set. Some of the * parameters will be overwritten below, but that's OK. */ DBG (2, "sane_start\n"); status = sane_get_parameters (s, 0); if (status != SANE_STATUS_GOOD) return status; DBG (1, "Got params again...\n"); s->scanning = SANE_TRUE; s->cancelled = 0; s->line_buffer = malloc (s->sm3840_params.linelen); s->remaining = 0; s->offset = 0; s->linesleft = s->sm3840_params.scanlines; s->linesread = 0; s->save_i = 0; s->save_scan_line = NULL; s->save_dpi1200_remap = NULL; s->save_color_remap = NULL; s->save_dither_err = 0; s->threshold = s->sm3840_params.threshold; setup_scan ((p_usb_dev_handle) s->udev, &(s->sm3840_params)); return (SANE_STATUS_GOOD); } static double sm3840_unit_convert (SANE_Int val) { double d; d = SANE_UNFIX (val); d /= MM_PER_INCH; return d; } /*--------------------------------------------------------------------------*/ SANE_Status sane_get_parameters (SANE_Handle handle, SANE_Parameters * params) { SM3840_Scan *s = handle; DBG (2, "sane_get_parameters\n"); if (!s->scanning) { memset (&s->sane_params, 0, sizeof (s->sane_params)); /* Copy from options to sm3840_params */ s->sm3840_params.gray = (!strcasecmp (s->value[OPT_MODE].s, SANE_VALUE_SCAN_MODE_GRAY)) ? 1 : 0; s->sm3840_params.halftone = (!strcasecmp (s->value[OPT_MODE].s, SANE_VALUE_SCAN_MODE_HALFTONE)) ? 1 : 0; s->sm3840_params.lineart = (!strcasecmp (s->value[OPT_MODE].s, SANE_VALUE_SCAN_MODE_LINEART)) ? 1 : 0; s->sm3840_params.dpi = s->value[OPT_RESOLUTION].w; s->sm3840_params.bpp = s->value[OPT_BIT_DEPTH].w; s->sm3840_params.gain = SANE_UNFIX (s->value[OPT_CONTRAST].w); s->sm3840_params.offset = s->value[OPT_BRIGHTNESS].w; s->sm3840_params.lamp = s->value[OPT_LAMP_TIMEOUT].w; s->sm3840_params.threshold = s->value[OPT_THRESHOLD].w; if (s->sm3840_params.lineart || s->sm3840_params.halftone) { s->sm3840_params.gray = 1; s->sm3840_params.bpp = 8; } s->sm3840_params.top = sm3840_unit_convert (s->value[OPT_TL_Y].w); s->sm3840_params.left = sm3840_unit_convert (s->value[OPT_TL_X].w); s->sm3840_params.width = sm3840_unit_convert (s->value[OPT_BR_X].w) - s->sm3840_params.left; s->sm3840_params.height = sm3840_unit_convert (s->value[OPT_BR_Y].w) - s->sm3840_params.top; /* Legalize and calculate pixel sizes */ prepare_params (&(s->sm3840_params)); /* Copy into sane_params */ s->sane_params.pixels_per_line = s->sm3840_params.scanpix; s->sane_params.lines = s->sm3840_params.scanlines; s->sane_params.format = s->sm3840_params.gray ? SANE_FRAME_GRAY : SANE_FRAME_RGB; s->sane_params.bytes_per_line = s->sm3840_params.linelen; s->sane_params.depth = s->sm3840_params.bpp; if (s->sm3840_params.lineart || s->sm3840_params.halftone) { s->sane_params.bytes_per_line += 7; s->sane_params.bytes_per_line /= 8; s->sane_params.depth = 1; s->sane_params.pixels_per_line = s->sane_params.bytes_per_line * 8; } s->sane_params.last_frame = SANE_TRUE; } /*!scanning */ if (params) *params = s->sane_params; return (SANE_STATUS_GOOD); } /*--------------------------------------------------------------------------*/ SANE_Status sane_control_option (SANE_Handle handle, SANE_Int option, SANE_Action action, void *val, SANE_Int * info) { SM3840_Scan *s = handle; SANE_Status status = 0; SANE_Word cap; DBG (2, "sane_control_option\n"); if (info) *info = 0; if (s->scanning) return SANE_STATUS_DEVICE_BUSY; if (option >= NUM_OPTIONS) return SANE_STATUS_INVAL; cap = s->options_list[option].cap; if (!SANE_OPTION_IS_ACTIVE (cap)) return SANE_STATUS_INVAL; if (action == SANE_ACTION_GET_VALUE) { DBG (1, "sane_control_option %d, get value\n", option); switch (option) { /* word options: */ case OPT_RESOLUTION: case OPT_BIT_DEPTH: case OPT_TL_X: case OPT_TL_Y: case OPT_BR_X: case OPT_BR_Y: case OPT_NUM_OPTS: case OPT_CONTRAST: case OPT_BRIGHTNESS: case OPT_LAMP_TIMEOUT: case OPT_THRESHOLD: *(SANE_Word *) val = s->value[option].w; return (SANE_STATUS_GOOD); /* string options: */ case OPT_MODE: strcpy (val, s->value[option].s); return (SANE_STATUS_GOOD); } } else if (action == SANE_ACTION_SET_VALUE) { DBG (1, "sane_control_option %d, set value\n", option); if (!SANE_OPTION_IS_SETTABLE (cap)) return (SANE_STATUS_INVAL); if (status != SANE_STATUS_GOOD) return (status); status = sanei_constrain_value (s->options_list + option, val, info); switch (option) { /* (mostly) side-effect-free word options: */ case OPT_RESOLUTION: case OPT_BIT_DEPTH: case OPT_BR_X: case OPT_BR_Y: case OPT_TL_X: case OPT_TL_Y: if (info) *info |= SANE_INFO_RELOAD_PARAMS; /* fall through */ case OPT_NUM_OPTS: case OPT_CONTRAST: case OPT_BRIGHTNESS: case OPT_LAMP_TIMEOUT: case OPT_THRESHOLD: s->value[option].w = *(SANE_Word *) val; return (SANE_STATUS_GOOD); case OPT_MODE: if (s->value[option].s) free (s->value[option].s); s->value[option].s = strdup (val); if (info) *info |= SANE_INFO_RELOAD_PARAMS; return (SANE_STATUS_GOOD); } } return (SANE_STATUS_INVAL); } /*--------------------------------------------------------------------------*/ const SANE_Option_Descriptor * sane_get_option_descriptor (SANE_Handle handle, SANE_Int option) { SM3840_Scan *s = handle; DBG (2, "sane_get_option_descriptor\n"); if ((unsigned) option >= NUM_OPTIONS) return (0); return (&s->options_list[option]); } /*--------------------------------------------------------------------------*/ void sane_close (SANE_Handle handle) { SM3840_Scan *prev, *s; DBG (2, "sane_close\n"); /* remove handle from list of open handles: */ prev = 0; for (s = first_handle; s; s = s->next) { if (s == handle) break; prev = s; } if (!s) { DBG (1, "close: invalid handle %p\n", handle); return; /* oops, not a handle we know about */ } if (s->scanning) { sane_cancel (handle); } sanei_usb_close (s->udev); if (s->line_buffer) free (s->line_buffer); if (s->save_scan_line) free (s->save_scan_line); if (s->save_dpi1200_remap) free (s->save_dpi1200_remap); if (s->save_color_remap) free (s->save_color_remap); if (prev) prev->next = s->next; else first_handle = s; free (handle); } /*--------------------------------------------------------------------------*/ void sane_exit (void) { SM3840_Device *next; DBG (2, "sane_exit\n"); while (first_dev != NULL) { next = first_dev->next; free (first_dev); first_dev = next; } if (devlist) free (devlist); } /*--------------------------------------------------------------------------*/ SANE_Status sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize) { DBG_INIT (); if (version_code) *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, 0); if (authorize) DBG (2, "Unused authorize\n"); sanei_usb_init (); return (SANE_STATUS_GOOD); } /*--------------------------------------------------------------------------*/ static SANE_Status add_sm_device (SANE_String_Const devname, SANE_String_Const modname) { SM3840_Device *dev; dev = calloc (sizeof (*dev), 1); if (!dev) return (SANE_STATUS_NO_MEM); memset (dev, 0, sizeof (*dev)); dev->sane.name = strdup (devname); dev->sane.model = modname; dev->sane.vendor = "Microtek"; dev->sane.type = "flatbed scanner"; ++num_devices; dev->next = first_dev; first_dev = dev; return (SANE_STATUS_GOOD); } static SANE_Status add_sm3840_device (SANE_String_Const devname) { return add_sm_device (devname, "ScanMaker 3840"); } static SANE_Status add_sm4800_device (SANE_String_Const devname) { return add_sm_device (devname, "ScanMaker 4800"); } /*--------------------------------------------------------------------------*/ SANE_Status sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only) { static const SANE_Device **devlist = 0; SM3840_Device *dev; int i; DBG (3, "sane_get_devices (local_only = %d)\n", local_only); while (first_dev) { dev = first_dev->next; free (first_dev); first_dev = dev; } first_dev = NULL; num_devices = 0; /* If we get enough scanners should use an array, but for now */ /* do it one-by-one... */ sanei_usb_find_devices (0x05da, 0x30d4, add_sm3840_device); sanei_usb_find_devices (0x05da, 0x30cf, add_sm4800_device); if (devlist) free (devlist); devlist = calloc ((num_devices + 1) * sizeof (devlist[0]), 1); if (!devlist) return SANE_STATUS_NO_MEM; i = 0; for (dev = first_dev; i < num_devices; dev = dev->next) devlist[i++] = &dev->sane; devlist[i++] = 0; if (device_list) *device_list = devlist; return (SANE_STATUS_GOOD); } /*--------------------------------------------------------------------------*/ static size_t max_string_size (const SANE_String_Const strings[]) { size_t size, max_size = 0; int i; for (i = 0; strings[i]; ++i) { size = strlen (strings[i]) + 1; if (size > max_size) max_size = size; } return (max_size); } /*--------------------------------------------------------------------------*/ static void initialize_options_list (SM3840_Scan * s) { SANE_Int option; DBG (2, "initialize_options_list\n"); for (option = 0; option < NUM_OPTIONS; ++option) { s->options_list[option].size = sizeof (SANE_Word); s->options_list[option].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; } s->options_list[OPT_NUM_OPTS].name = SANE_NAME_NUM_OPTIONS; s->options_list[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS; s->options_list[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS; s->options_list[OPT_NUM_OPTS].type = SANE_TYPE_INT; s->options_list[OPT_NUM_OPTS].unit = SANE_UNIT_NONE; s->options_list[OPT_NUM_OPTS].size = sizeof (SANE_Word); s->options_list[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT; s->options_list[OPT_NUM_OPTS].constraint_type = SANE_CONSTRAINT_NONE; s->value[OPT_NUM_OPTS].w = NUM_OPTIONS; s->options_list[OPT_MODE].name = SANE_NAME_SCAN_MODE; s->options_list[OPT_MODE].title = SANE_TITLE_SCAN_MODE; s->options_list[OPT_MODE].desc = SANE_DESC_SCAN_MODE; s->options_list[OPT_MODE].type = SANE_TYPE_STRING; s->options_list[OPT_MODE].size = max_string_size (mode_list); s->options_list[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST; s->options_list[OPT_MODE].constraint.string_list = mode_list; s->value[OPT_MODE].s = strdup (mode_list[1]); s->options_list[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION; s->options_list[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION; s->options_list[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION; s->options_list[OPT_RESOLUTION].type = SANE_TYPE_INT; s->options_list[OPT_RESOLUTION].unit = SANE_UNIT_DPI; s->options_list[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_WORD_LIST; s->options_list[OPT_RESOLUTION].constraint.word_list = resolution_list; s->value[OPT_RESOLUTION].w = 300; s->options_list[OPT_BIT_DEPTH].name = SANE_NAME_BIT_DEPTH; s->options_list[OPT_BIT_DEPTH].title = SANE_TITLE_BIT_DEPTH; s->options_list[OPT_BIT_DEPTH].desc = SANE_DESC_BIT_DEPTH; s->options_list[OPT_BIT_DEPTH].type = SANE_TYPE_INT; s->options_list[OPT_BIT_DEPTH].unit = SANE_UNIT_NONE; s->options_list[OPT_BIT_DEPTH].constraint_type = SANE_CONSTRAINT_WORD_LIST; s->options_list[OPT_BIT_DEPTH].constraint.word_list = bpp_list; s->value[OPT_BIT_DEPTH].w = 8; s->options_list[OPT_TL_X].name = SANE_NAME_SCAN_TL_X; s->options_list[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X; s->options_list[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X; s->options_list[OPT_TL_X].type = SANE_TYPE_FIXED; s->options_list[OPT_TL_X].unit = SANE_UNIT_MM; s->options_list[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE; s->options_list[OPT_TL_X].constraint.range = &x_range; s->value[OPT_TL_X].w = s->options_list[OPT_TL_X].constraint.range->min; s->options_list[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y; s->options_list[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y; s->options_list[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y; s->options_list[OPT_TL_Y].type = SANE_TYPE_FIXED; s->options_list[OPT_TL_Y].unit = SANE_UNIT_MM; s->options_list[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE; s->options_list[OPT_TL_Y].constraint.range = &y_range; s->value[OPT_TL_Y].w = s->options_list[OPT_TL_Y].constraint.range->min; s->options_list[OPT_BR_X].name = SANE_NAME_SCAN_BR_X; s->options_list[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X; s->options_list[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X; s->options_list[OPT_BR_X].type = SANE_TYPE_FIXED; s->options_list[OPT_BR_X].unit = SANE_UNIT_MM; s->options_list[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE; s->options_list[OPT_BR_X].constraint.range = &x_range; s->value[OPT_BR_X].w = s->options_list[OPT_BR_X].constraint.range->max; s->options_list[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y; s->options_list[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y; s->options_list[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y; s->options_list[OPT_BR_Y].type = SANE_TYPE_FIXED; s->options_list[OPT_BR_Y].unit = SANE_UNIT_MM; s->options_list[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE; s->options_list[OPT_BR_Y].constraint.range = &y_range; s->value[OPT_BR_Y].w = s->options_list[OPT_BR_Y].constraint.range->max; s->options_list[OPT_CONTRAST].name = SANE_NAME_CONTRAST; s->options_list[OPT_CONTRAST].title = SANE_TITLE_CONTRAST; s->options_list[OPT_CONTRAST].desc = SANE_DESC_CONTRAST; s->options_list[OPT_CONTRAST].type = SANE_TYPE_FIXED; s->options_list[OPT_CONTRAST].unit = SANE_UNIT_NONE; s->options_list[OPT_CONTRAST].constraint_type = SANE_CONSTRAINT_RANGE; s->options_list[OPT_CONTRAST].constraint.range = &contrast_range; s->value[OPT_CONTRAST].w = SANE_FIX (3.5); s->options_list[OPT_BRIGHTNESS].name = SANE_NAME_BRIGHTNESS; s->options_list[OPT_BRIGHTNESS].title = SANE_TITLE_BRIGHTNESS; s->options_list[OPT_BRIGHTNESS].desc = SANE_DESC_BRIGHTNESS; s->options_list[OPT_BRIGHTNESS].type = SANE_TYPE_INT; s->options_list[OPT_BRIGHTNESS].unit = SANE_UNIT_NONE; s->options_list[OPT_BRIGHTNESS].constraint_type = SANE_CONSTRAINT_RANGE; s->options_list[OPT_BRIGHTNESS].constraint.range = &brightness_range; s->value[OPT_BRIGHTNESS].w = 1800; s->options_list[OPT_LAMP_TIMEOUT].name = "lamp-timeout"; s->options_list[OPT_LAMP_TIMEOUT].title = SANE_I18N ("Lamp timeout"); s->options_list[OPT_LAMP_TIMEOUT].desc = SANE_I18N ("Minutes until lamp is turned off after scan"); s->options_list[OPT_LAMP_TIMEOUT].type = SANE_TYPE_INT; s->options_list[OPT_LAMP_TIMEOUT].unit = SANE_UNIT_NONE; s->options_list[OPT_LAMP_TIMEOUT].constraint_type = SANE_CONSTRAINT_RANGE; s->options_list[OPT_LAMP_TIMEOUT].constraint.range = &lamp_range; s->value[OPT_LAMP_TIMEOUT].w = 15; s->options_list[OPT_THRESHOLD].name = "threshold"; s->options_list[OPT_THRESHOLD].title = SANE_I18N ("Threshold"); s->options_list[OPT_THRESHOLD].desc = SANE_I18N ("Threshold value for lineart mode"); s->options_list[OPT_THRESHOLD].type = SANE_TYPE_INT; s->options_list[OPT_THRESHOLD].unit = SANE_UNIT_NONE; s->options_list[OPT_THRESHOLD].constraint_type = SANE_CONSTRAINT_RANGE; s->options_list[OPT_THRESHOLD].constraint.range = &threshold_range; s->value[OPT_THRESHOLD].w = 128; } /*--------------------------------------------------------------------------*/ SANE_Status sane_open (SANE_String_Const devicename, SANE_Handle * handle) { SANE_Status status; SM3840_Device *dev; SM3840_Scan *s; DBG (2, "sane_open\n"); /* Make sure we have first_dev */ sane_get_devices (NULL, 0); if (devicename[0]) { for (dev = first_dev; dev; dev = dev->next) if (strcmp (dev->sane.name, devicename) == 0) break; } else { /* empty devicename -> use first device */ dev = first_dev; } DBG (2, "using device: %s %p\n", dev->sane.name, (void *) dev); if (!dev) return SANE_STATUS_INVAL; s = calloc (sizeof (*s), 1); if (!s) return SANE_STATUS_NO_MEM; status = sanei_usb_open (dev->sane.name, &(s->udev)); if (status != SANE_STATUS_GOOD) return status; initialize_options_list (s); s->scanning = 0; /* insert newly opened handle into list of open handles: */ s->next = first_handle; first_handle = s; *handle = s; return (SANE_STATUS_GOOD); } /*--------------------------------------------------------------------------*/ SANE_Status sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking) { SM3840_Scan *s = handle; DBG (2, "sane_set_io_mode( %p, %d )\n", handle, non_blocking); if (s->scanning) { if (non_blocking == SANE_FALSE) return SANE_STATUS_GOOD; else return (SANE_STATUS_UNSUPPORTED); } else return SANE_STATUS_INVAL; } /*---------------------------------------------------------------------------*/ SANE_Status sane_get_select_fd (SANE_Handle handle, SANE_Int * fd) { DBG (2, "sane_get_select_fd( %p, %p )\n", (void *) handle, (void *) fd); return SANE_STATUS_UNSUPPORTED; }