/* sane - Scanner Access Now Easy. Copyright (C) 2001, Marcio Luis Teixeira Parts copyright (C) 1996, 1997 Andreas Beck Parts copyright (C) 2000, 2001 Michael Herder Parts copyright (C) 2001 Henning Meier-Geinitz 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, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 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. */ #define BUILD 1 #define MM_IN_INCH 25.4 #include "../include/sane/config.h" #include #include #include #include #include #include #include "../include/sane/sane.h" #include "../include/sane/sanei.h" #include "../include/sane/saneopts.h" #include "../include/sane/sanei_config.h" #include "../include/sane/sanei_usb.h" #include "../include/sane/sanei_pv8630.h" #define BACKEND_NAME umax1220u #define UMAX_CONFIG_FILE "umax1220u.conf" #include "../include/sane/sanei_backend.h" #include "umax1220u-common.c" typedef struct Umax_Device { struct Umax_Device *next; SANE_String name; SANE_Device sane; } Umax_Device; typedef struct Umax_Scanner { struct Umax_Scanner *next; Umax_Device *device; UMAX_Handle scan; } Umax_Scanner; static int num_devices = 0; static const SANE_Device **devlist = NULL; static Umax_Device *first_dev = NULL; static Umax_Scanner *first_handle = NULL; static SANE_Parameters parms = { SANE_FRAME_RGB, 0, 0, /* Number of bytes returned per scan line: */ 0, /* Number of pixels per scan line. */ 0, /* Number of lines for the current scan. */ 8 /* Number of bits per sample. */ }; struct _SANE_Option { SANE_Option_Descriptor *descriptor; SANE_Status (*callback) (struct _SANE_Option * option, SANE_Handle handle, SANE_Action action, void *value, SANE_Int * info); }; typedef struct _SANE_Option SANE_Option; static SANE_Word getNumberOfOptions (void); /* Forward declaration */ /* This read-only option returns the number of options available for the device. It should be the first option in the options array declared below. */ static SANE_Option_Descriptor optionNumOptionsDescriptor = { SANE_NAME_NUM_OPTIONS, SANE_TITLE_NUM_OPTIONS, SANE_DESC_NUM_OPTIONS, SANE_TYPE_INT, SANE_UNIT_NONE, sizeof (SANE_Word), SANE_CAP_SOFT_DETECT, SANE_CONSTRAINT_NONE, {NULL} }; static SANE_Status optionNumOptionsCallback (SANE_Option * option, SANE_Handle handle, SANE_Action action, void *value, SANE_Int * info) { option = option; handle = handle; info = info; /* Eliminate warning about unused parameters */ if (action != SANE_ACTION_GET_VALUE) return SANE_STATUS_INVAL; *(SANE_Word *) value = getNumberOfOptions (); return SANE_STATUS_GOOD; } /* This option lets the user select the scan resolution. The UMAX scanner only supports the following resolutions: 75, 150, 300 and 600 */ static const SANE_Word optionResolutionList[] = { 4, /* Number of elements */ 75, 150, 300, 600 /* Resolution list */ }; static SANE_Option_Descriptor optionResolutionDescriptor = { SANE_NAME_SCAN_RESOLUTION, SANE_TITLE_SCAN_RESOLUTION, SANE_DESC_SCAN_RESOLUTION, SANE_TYPE_INT, SANE_UNIT_DPI, sizeof (SANE_Word), SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_AUTOMATIC, SANE_CONSTRAINT_WORD_LIST, {(const SANE_String_Const *) optionResolutionList} }; static SANE_Word optionResolutionValue = 75; static SANE_Status optionResolutionCallback (SANE_Option * option, SANE_Handle handle, SANE_Action action, void *value, SANE_Int * info) { SANE_Status status; SANE_Word autoValue = 75; handle = handle; /* Eliminate warning about unused parameters */ switch (action) { case SANE_ACTION_SET_AUTO: status = sanei_constrain_value (option->descriptor, (void *) &autoValue, info); if (status != SANE_STATUS_GOOD) return status; optionResolutionValue = autoValue; *info |= SANE_INFO_RELOAD_PARAMS; break; case SANE_ACTION_SET_VALUE: *info |= SANE_INFO_RELOAD_PARAMS; optionResolutionValue = *(SANE_Word *) value; break; case SANE_ACTION_GET_VALUE: *(SANE_Word *) value = optionResolutionValue; break; } return SANE_STATUS_GOOD; } /* This option lets the user select a gray scale scan */ static SANE_Word optionGrayscaleValue = SANE_FALSE; static SANE_Option_Descriptor optionGrayscaleDescriptor = { "gray", SANE_I18N ("Grayscale scan"), SANE_I18N ("Do a grayscale rather than color scan"), SANE_TYPE_BOOL, SANE_UNIT_NONE, sizeof (SANE_Word), SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT, SANE_CONSTRAINT_NONE, {NULL} }; static SANE_Status optionGrayscaleCallback (SANE_Option * option, SANE_Handle handle, SANE_Action action, void *value, SANE_Int * info) { handle = handle; option = option; /* Eliminate warning about unused parameters */ switch (action) { case SANE_ACTION_SET_AUTO: return SANE_STATUS_INVAL; break; case SANE_ACTION_SET_VALUE: *info |= SANE_INFO_RELOAD_PARAMS; optionGrayscaleValue = *(SANE_Bool *) value; break; case SANE_ACTION_GET_VALUE: *(SANE_Word *) value = optionGrayscaleValue; break; } return SANE_STATUS_GOOD; } /* This option is a button that allows the user to turn off the lamp in the UMAX scanner */ static SANE_Option_Descriptor optionLampOffDescriptor = { "lamp-off", SANE_I18N ("Lamp off"), SANE_I18N ("Turn off scanner lamp"), SANE_TYPE_BUTTON, SANE_UNIT_NONE, 0, SANE_CAP_SOFT_SELECT, SANE_CONSTRAINT_NONE, {NULL} }; static SANE_Status optionLampOffCallback (SANE_Option * option, SANE_Handle handle, SANE_Action action, void *value, SANE_Int * info) { Umax_Scanner *scanner = handle; SANE_Status res = SANE_STATUS_GOOD; /* Eliminate warnings about unused parameters */ option = option; handle = handle; info = info; value = value; if (action != SANE_ACTION_SET_VALUE) return SANE_STATUS_INVAL; res = UMAX_set_lamp_state (&scanner->scan, UMAX_LAMP_OFF); return res; } static const SANE_Range widthRange = { 0, /* minimum */ SANE_FIX (UMAX_MAX_WIDTH * MM_IN_INCH / 600), /* maximum */ 0 /* quantization */ }; static const SANE_Range heightRange = { 0, /* minimum */ SANE_FIX (UMAX_MAX_HEIGHT * MM_IN_INCH / 600), /* maximum */ 0 /* quantization */ }; /* This option controls the top-left-x corner of the scan */ static SANE_Fixed optionTopLeftXValue = 0; static SANE_Option_Descriptor optionTopLeftXDescriptor = { SANE_NAME_SCAN_TL_X, SANE_TITLE_SCAN_TL_X, SANE_DESC_SCAN_TL_X, SANE_TYPE_FIXED, SANE_UNIT_MM, sizeof (SANE_Fixed), SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT, SANE_CONSTRAINT_RANGE, {(const SANE_String_Const *) & widthRange} }; static SANE_Status optionTopLeftXCallback (SANE_Option * option, SANE_Handle handle, SANE_Action action, void *value, SANE_Int * info) { option = option; handle = handle; value = value; /* Eliminate warning about unused parameters */ switch (action) { case SANE_ACTION_SET_AUTO: return SANE_STATUS_INVAL; break; case SANE_ACTION_SET_VALUE: optionTopLeftXValue = *(SANE_Fixed *) value; *info |= SANE_INFO_RELOAD_PARAMS; break; case SANE_ACTION_GET_VALUE: *(SANE_Fixed *) value = optionTopLeftXValue; break; } return SANE_STATUS_GOOD; } /* This option controls the top-left-y corner of the scan */ static SANE_Fixed optionTopLeftYValue = 0; static SANE_Option_Descriptor optionTopLeftYDescriptor = { SANE_NAME_SCAN_TL_Y, SANE_TITLE_SCAN_TL_Y, SANE_DESC_SCAN_TL_Y, SANE_TYPE_FIXED, SANE_UNIT_MM, sizeof (SANE_Fixed), SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT, SANE_CONSTRAINT_RANGE, {(const SANE_String_Const *) & heightRange} }; static SANE_Status optionTopLeftYCallback (SANE_Option * option, SANE_Handle handle, SANE_Action action, void *value, SANE_Int * info) { /* Eliminate warnings about unused parameters */ option = option; handle = handle; switch (action) { case SANE_ACTION_SET_AUTO: return SANE_STATUS_INVAL; break; case SANE_ACTION_SET_VALUE: optionTopLeftYValue = *(SANE_Fixed *) value; *info |= SANE_INFO_RELOAD_PARAMS; break; case SANE_ACTION_GET_VALUE: *(SANE_Fixed *) value = optionTopLeftYValue; break; } return SANE_STATUS_GOOD; } /* This option controls the bot-right-x corner of the scan */ static SANE_Fixed optionBotRightXValue = 0; static SANE_Option_Descriptor optionBotRightXDescriptor = { SANE_NAME_SCAN_BR_X, SANE_TITLE_SCAN_BR_X, SANE_DESC_SCAN_BR_X, SANE_TYPE_FIXED, SANE_UNIT_MM, sizeof (SANE_Fixed), SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT, SANE_CONSTRAINT_RANGE, {(const SANE_String_Const *) & widthRange} }; static SANE_Status optionBotRightXCallback (SANE_Option * option, SANE_Handle handle, SANE_Action action, void *value, SANE_Int * info) { /* Eliminate warnings about unused parameters */ option = option; handle = handle; switch (action) { case SANE_ACTION_SET_AUTO: return SANE_STATUS_INVAL; break; case SANE_ACTION_SET_VALUE: optionBotRightXValue = *(SANE_Fixed *) value; *info |= SANE_INFO_RELOAD_PARAMS; break; case SANE_ACTION_GET_VALUE: *(SANE_Fixed *) value = optionBotRightXValue; break; } return SANE_STATUS_GOOD; } /* This option controls the bot-right-y corner of the scan */ static SANE_Fixed optionBotRightYValue = 0; static SANE_Option_Descriptor optionBotRightYDescriptor = { SANE_NAME_SCAN_BR_Y, SANE_TITLE_SCAN_BR_Y, SANE_DESC_SCAN_BR_Y, SANE_TYPE_FIXED, SANE_UNIT_MM, sizeof (SANE_Fixed), SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT, SANE_CONSTRAINT_RANGE, {(const SANE_String_Const *) & heightRange} }; static SANE_Status optionBotRightYCallback (SANE_Option * option, SANE_Handle handle, SANE_Action action, void *value, SANE_Int * info) { /* Eliminate warnings about unused parameters */ option = option; handle = handle; switch (action) { case SANE_ACTION_SET_AUTO: return SANE_STATUS_INVAL; break; case SANE_ACTION_SET_VALUE: optionBotRightYValue = *(SANE_Fixed *) value; *info |= SANE_INFO_RELOAD_PARAMS; break; case SANE_ACTION_GET_VALUE: *(SANE_Fixed *) value = optionBotRightYValue; break; } return SANE_STATUS_GOOD; } /* The following array binds the option descriptors to their respective callback routines */ static SANE_Option so[] = { {&optionNumOptionsDescriptor, optionNumOptionsCallback}, {&optionResolutionDescriptor, optionResolutionCallback}, {&optionGrayscaleDescriptor, optionGrayscaleCallback}, {&optionTopLeftXDescriptor, optionTopLeftXCallback}, {&optionTopLeftYDescriptor, optionTopLeftYCallback}, {&optionBotRightXDescriptor, optionBotRightXCallback}, {&optionBotRightYDescriptor, optionBotRightYCallback}, {&optionLampOffDescriptor, optionLampOffCallback} }; static SANE_Word getNumberOfOptions (void) { return NELEMS (so); } /* This routine dispatches the control message to the appropriate callback routine, it outght to be called by sane_control_option after any driver specific validation. */ static SANE_Status dispatch_control_option (SANE_Handle handle, SANE_Int option, SANE_Action action, void *value, SANE_Int * info) { SANE_Option *op = so + option; SANE_Int myinfo = 0; SANE_Status status = SANE_STATUS_GOOD; if (option < 0 || option >= NELEMS (so)) return SANE_STATUS_INVAL; /* Unknown option ... */ if ((action == SANE_ACTION_SET_VALUE) && ((op->descriptor->cap & SANE_CAP_SOFT_SELECT) == 0)) return SANE_STATUS_INVAL; if ((action == SANE_ACTION_GET_VALUE) && ((op->descriptor->cap & SANE_CAP_SOFT_DETECT) == 0)) return SANE_STATUS_INVAL; if ((action == SANE_ACTION_SET_AUTO) && ((op->descriptor->cap & SANE_CAP_AUTOMATIC) == 0)) return SANE_STATUS_INVAL; if (action == SANE_ACTION_SET_VALUE) { status = sanei_constrain_value (op->descriptor, value, &myinfo); if (status != SANE_STATUS_GOOD) return status; } status = (op->callback) (op, handle, action, value, &myinfo); if (info) *info = myinfo; return status; } static SANE_Status attach_scanner (const char *devicename, Umax_Device ** devp) { UMAX_Handle scan; Umax_Device *dev; SANE_Status status; DBG (3, "attach_scanner: %s\n", devicename); for (dev = first_dev; dev; dev = dev->next) { if (strcmp (dev->sane.name, devicename) == 0) { if (devp) *devp = dev; return SANE_STATUS_GOOD; } } dev = malloc (sizeof (*dev)); if (!dev) return SANE_STATUS_NO_MEM; memset (dev, '\0', sizeof (Umax_Device)); /* clear structure */ DBG (4, "attach_scanner: opening %s\n", devicename); status = UMAX_open_device (&scan, devicename); if (status != SANE_STATUS_GOOD) { DBG (1, "ERROR: attach_scanner: opening %s failed\n", devicename); free (dev); return status; } dev->name = strdup (devicename); dev->sane.name = dev->name; dev->sane.vendor = "UMAX"; dev->sane.model = UMAX_get_device_name (&scan); dev->sane.type = "flatbed scanner"; UMAX_close_device (&scan); ++num_devices; dev->next = first_dev; first_dev = dev; if (devp) *devp = dev; return SANE_STATUS_GOOD; } /* callback function for sanei_usb_attach_matching_devices */ static SANE_Status attach_one (const char *name) { attach_scanner (name, 0); return SANE_STATUS_GOOD; } /* This file implements a SANE backend for the UMAX Astra 1220U scanner. */ SANE_Status sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize) { char config_line[PATH_MAX]; size_t len; FILE *fp; DBG_INIT (); DBG (2, "sane_init: version_code %s 0, authorize %s 0\n", version_code == 0 ? "=" : "!=", authorize == 0 ? "=" : "!="); DBG (1, "sane_init: SANE umax1220u backend version %d.%d.%d from %s\n", V_MAJOR, V_MINOR, BUILD, PACKAGE_STRING); if (version_code) *version_code = SANE_VERSION_CODE (V_MAJOR, V_MINOR, BUILD); sanei_usb_init (); sanei_pv8630_init (); fp = sanei_config_open (UMAX_CONFIG_FILE); if (!fp) { /* no config-file: try /dev/scanner and /dev/usbscanner. */ attach_scanner ("/dev/scanner", 0); attach_scanner ("/dev/usbscanner", 0); return SANE_STATUS_GOOD; } DBG (3, "reading configure file %s\n", UMAX_CONFIG_FILE); while (sanei_config_read (config_line, sizeof (config_line), fp)) { if (config_line[0] == '#') continue; /* ignore line comments */ len = strlen (config_line); if (!len) continue; /* ignore empty lines */ DBG (4, "attach_matching_devices(%s)\n", config_line); sanei_usb_attach_matching_devices (config_line, attach_one); } DBG (4, "finished reading configure file\n"); fclose (fp); return SANE_STATUS_GOOD; } void sane_exit (void) { Umax_Device *dev, *next; DBG (3, "sane_exit\n"); for (dev = first_dev; dev; dev = next) { next = dev->next; free (dev->name); free (dev); } if (devlist) free (devlist); return; } SANE_Status sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only) { Umax_Device *dev; int i; DBG (3, "sane_get_devices(local_only = %d)\n", local_only); if (devlist) free (devlist); devlist = malloc ((num_devices + 1) * sizeof (devlist[0])); 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; *device_list = devlist; return SANE_STATUS_GOOD; } SANE_Status sane_open (SANE_String_Const devicename, SANE_Handle * handle) { Umax_Device *dev; SANE_Status status; Umax_Scanner *scanner; DBG (3, "sane_open\n"); if (devicename[0]) /* search for devicename */ { DBG (4, "sane_open: devicename=%s\n", devicename); for (dev = first_dev; dev; dev = dev->next) if (strcmp (dev->sane.name, devicename) == 0) break; if (!dev) { status = attach_scanner (devicename, &dev); if (status != SANE_STATUS_GOOD) return status; } } else { DBG (2, "sane_open: no devicename, opening first device\n"); dev = first_dev; } if (!dev) return SANE_STATUS_INVAL; scanner = malloc (sizeof (*scanner)); if (!scanner) return SANE_STATUS_NO_MEM; memset (scanner, 0, sizeof (*scanner)); scanner->device = dev; status = UMAX_open_device (&scanner->scan, dev->sane.name); if (status != SANE_STATUS_GOOD) { free (scanner); return status; } *handle = scanner; /* insert newly opened handle into list of open handles: */ scanner->next = first_handle; first_handle = scanner; return SANE_STATUS_GOOD; } void sane_close (SANE_Handle handle) { Umax_Scanner *prev, *scanner; SANE_Status res; DBG (3, "sane_close\n"); if (!first_handle) { DBG (1, "ERROR: sane_close: no handles opened\n"); return; } /* remove handle from list of open handles: */ prev = NULL; for (scanner = first_handle; scanner; scanner = scanner->next) { if (scanner == handle) break; prev = scanner; } if (!scanner) { DBG (1, "ERROR: sane_close: invalid handle %p\n", handle); return; /* oops, not a handle we know about */ } if (prev) prev->next = scanner->next; else first_handle = scanner->next; res = UMAX_set_lamp_state (&scanner->scan, UMAX_LAMP_OFF); res = UMAX_close_device (&scanner->scan); free (scanner); } const SANE_Option_Descriptor * sane_get_option_descriptor (SANE_Handle handle, SANE_Int option) { handle = handle; /* Eliminate compiler warning */ DBG (3, "sane_get_option_descriptor: option = %d\n", option); if (option < 0 || option >= NELEMS (so)) return NULL; return so[option].descriptor; } SANE_Status sane_control_option (SANE_Handle handle, SANE_Int option, SANE_Action action, void *value, SANE_Int * info) { handle = handle; /* Eliminate compiler warning */ DBG (3, "sane_control_option: handle=%p, opt=%d, act=%d, val=%p, info=%p\n", handle, option, action, value, info); return dispatch_control_option (handle, option, action, value, info); } SANE_Status sane_get_parameters (SANE_Handle handle, SANE_Parameters * params) { int rc = SANE_STATUS_GOOD; int w = SANE_UNFIX (optionBotRightXValue - optionTopLeftXValue) / MM_IN_INCH * optionResolutionValue; int h = SANE_UNFIX (optionBotRightYValue - optionTopLeftYValue) / MM_IN_INCH * optionResolutionValue; handle = handle; /* Eliminate compiler warning */ DBG (3, "sane_get_parameters\n"); parms.depth = 8; parms.last_frame = SANE_TRUE; parms.pixels_per_line = w; parms.lines = h; if (optionGrayscaleValue == SANE_TRUE) { parms.format = SANE_FRAME_GRAY; parms.bytes_per_line = w; } else { parms.format = SANE_FRAME_RGB; parms.bytes_per_line = w * 3; } *params = parms; return rc; } SANE_Status sane_start (SANE_Handle handle) { Umax_Scanner *scanner = handle; SANE_Status res; DBG (3, "sane_start\n"); res = UMAX_set_scan_parameters (&scanner->scan, optionGrayscaleValue == SANE_FALSE, SANE_UNFIX (optionTopLeftXValue) / MM_IN_INCH * 600, SANE_UNFIX (optionTopLeftYValue) / MM_IN_INCH * 600, SANE_UNFIX (optionBotRightXValue - optionTopLeftXValue) / MM_IN_INCH * optionResolutionValue, SANE_UNFIX (optionBotRightYValue - optionTopLeftYValue) / MM_IN_INCH * optionResolutionValue, optionResolutionValue, optionResolutionValue); if (res != SANE_STATUS_GOOD) return res; return UMAX_start_scan (&scanner->scan); } SANE_Status sane_read (SANE_Handle handle, SANE_Byte * data, SANE_Int max_length, SANE_Int * length) { Umax_Scanner *scanner = handle; SANE_Status res; int len; unsigned char rgb[3]; len = *length = 0; DBG (3, "sane_read: max_length = %d\n", max_length); if (!data || !length) return SANE_STATUS_INVAL; if (scanner->scan.done) { res = UMAX_finish_scan (&scanner->scan); res = UMAX_park_head (&scanner->scan); return SANE_STATUS_EOF; } if (optionGrayscaleValue == SANE_FALSE) { while (!scanner->scan.done && (max_length >= 3)) { res = UMAX_get_rgb (&scanner->scan, rgb); if (res != SANE_STATUS_GOOD) { *length = 0; return res; } *data++ = rgb[0]; *data++ = rgb[1]; *data++ = rgb[2]; max_length -= 3; len += 3; } } else { while (!scanner->scan.done && max_length) { res = UMAX_get_rgb (&scanner->scan, rgb); if (res != SANE_STATUS_GOOD) { *length = 0; return res; } *data++ = rgb[0]; max_length--; len++; } } *length = len; return SANE_STATUS_GOOD; } void sane_cancel (SANE_Handle handle) { DBG (3, "sane_cancel: handle = %p\n", handle); DBG (3, "sane_cancel: canceling is unsupported in this backend\n"); } SANE_Status sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking) { DBG (3, "sane_set_io_mode: handle = %p, non_blocking = %d\n", handle, non_blocking); if (non_blocking != SANE_FALSE) return SANE_STATUS_UNSUPPORTED; return SANE_STATUS_GOOD; } SANE_Status sane_get_select_fd (SANE_Handle handle, SANE_Int * fd) { DBG (3, "sane_get_select_fd: handle = %p, fd %s 0\n", handle, fd ? "!=" : "="); return SANE_STATUS_UNSUPPORTED; }