/* sane - Scanner Access Now Easy. This file is part of the SANE package. 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. -------------------------------------------------------------------------- This file implements a SANE backend for HP ScanJet 3500 series scanners. Currently supported: - HP ScanJet 3500C - HP ScanJet 3530C - HP ScanJet 3570C SANE FLOW DIAGRAM - sane_init() : initialize backend, attach scanners . - sane_get_devices() : query list of scanner devices . - sane_open() : open a particular scanner device . . - sane_set_io_mode : set blocking mode . . - sane_get_select_fd : get scanner fd . . - sane_get_option_descriptor() : get option information . . - sane_control_option() : change option values . . . . - sane_start() : start image acquisition . . - sane_get_parameters() : returns actual scan parameters . . - sane_read() : read image data (from pipe) . . . . - sane_cancel() : cancel operation . - sane_close() : close opened scanner device - sane_exit() : terminate use of backend There are some device specific routines in this file that are in "#if 0" sections - these are left in place for documentation purposes in case somebody wants to implement features that use those routines. */ /* ------------------------------------------------------------------------- */ #include "../include/sane/config.h" #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_LIBC_H # include /* NeXTStep/OpenStep */ #endif #include "../include/sane/sane.h" #include "../include/sane/sanei_usb.h" #include "../include/sane/saneopts.h" #include "../include/sane/sanei_config.h" #include "../include/sane/sanei_thread.h" #include "../include/sane/sanei_backend.h" #define RTCMD_GETREG 0x80 #define RTCMD_READSRAM 0x81 #define RTCMD_SETREG 0x88 #define RTCMD_WRITESRAM 0x89 #define RTCMD_NVRAMCONTROL 0x8a #define RTCMD_BYTESAVAIL 0x90 #define RTCMD_READBYTES 0x91 #define RT_CHANNEL_ALL 0 #define RT_CHANNEL_RED 1 #define RT_CHANNEL_GREEN 2 #define RT_CHANNEL_BLUE 3 typedef int (*rts8801_callback) (void *param, unsigned bytes, void *data); #define DEBUG 1 #define SCANNER_UNIT_TO_FIXED_MM(number) SANE_FIX(number * MM_PER_INCH / 1200) #define FIXED_MM_TO_SCANNER_UNIT(number) SANE_UNFIX(number) * 1200 / MM_PER_INCH #define MSG_ERR 1 #define MSG_USER 5 #define MSG_INFO 6 #define FLOW_CONTROL 10 #define MSG_IO 15 #define MSG_IO_READ 17 #define IO_CMD 20 #define IO_CMD_RES 20 #define MSG_GET 25 /* ------------------------------------------------------------------------- */ enum hp3500_option { OPT_NUM_OPTS = 0, OPT_RESOLUTION, OPT_GEOMETRY_GROUP, OPT_TL_X, OPT_TL_Y, OPT_BR_X, OPT_BR_Y, OPT_MODE_GROUP, OPT_MODE, OPT_BRIGHTNESS, OPT_CONTRAST, OPT_GAMMA, NUM_OPTIONS }; typedef struct { int left; int top; int right; int bottom; } hp3500_rect; struct hp3500_data { struct hp3500_data *next; char *devicename; int sfd; int pipe_r; int pipe_w; SANE_Pid reader_pid; int resolution; int mode; time_t last_scan; hp3500_rect request_mm; hp3500_rect actual_mm; hp3500_rect fullres_pixels; hp3500_rect actres_pixels; int rounded_left; int rounded_top; int rounded_right; int rounded_bottom; int bytes_per_scan_line; int scan_width_pixels; int scan_height_pixels; int brightness; int contrast; double gamma; SANE_Option_Descriptor opt[NUM_OPTIONS]; SANE_Device sane; }; struct hp3500_write_info { struct hp3500_data *scanner; int bytesleft; }; typedef struct detailed_calibration_data { unsigned char const *channeldata[3]; unsigned resolution_divisor; } detailed_calibration_data; static struct hp3500_data *first_dev = 0; static struct hp3500_data **new_dev = &first_dev; static int num_devices = 0; static SANE_Int res_list[] = { 9, 50, 75, 100, 150, 200, 300, 400, 600, 1200 }; static const SANE_Range range_x = { 0, SANE_FIX (215.9), SANE_FIX (MM_PER_INCH / 1200) }; static const SANE_Range range_y = { 0, SANE_FIX (298.7), SANE_FIX (MM_PER_INCH / 1200) }; static const SANE_Range range_brightness = { 0, 255, 0 }; static const SANE_Range range_contrast = { 0, 255, 0 }; static const SANE_Range range_gamma = { SANE_FIX (0.2), SANE_FIX(4.0), SANE_FIX(0.01) }; #define HP3500_COLOR_SCAN 0 #define HP3500_GRAY_SCAN 1 #define HP3500_LINEART_SCAN 2 #define HP3500_TOTAL_SCANS 3 static char const *scan_mode_list[HP3500_TOTAL_SCANS + 1] = { 0 }; static SANE_Status attachScanner (const char *name); static SANE_Status init_options (struct hp3500_data *scanner); static int reader_process (void *); static void calculateDerivedValues (struct hp3500_data *scanner); static void do_reset (struct hp3500_data *scanner); static void do_cancel (struct hp3500_data *scanner); static size_t max_string_size(char const **); /* * used by sane_get_devices */ static const SANE_Device **devlist = 0; /* * SANE Interface */ /** * Called by SANE initially. * * From the SANE spec: * This function must be called before any other SANE function can be * called. The behavior of a SANE backend is undefined if this * function is not called first. The version code of the backend is * returned in the value pointed to by version_code. If that pointer * is NULL, no version code is returned. Argument authorize is either * a pointer to a function that is invoked when the backend requires * authentication for a specific resource or NULL if the frontend does * not support authentication. */ SANE_Status sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize) { (void) authorize; /* get rid of compiler warning */ DBG_INIT (); DBG (10, "sane_init\n"); sanei_usb_init (); sanei_thread_init (); if (version_code) *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, 0); sanei_usb_find_devices (0x03f0, 0x2205, attachScanner); sanei_usb_find_devices (0x03f0, 0x2005, attachScanner); return SANE_STATUS_GOOD; } /** * Called by SANE to find out about supported devices. * * From the SANE spec: * This function can be used to query the list of devices that are * available. If the function executes successfully, it stores a * pointer to a NULL terminated array of pointers to SANE_Device * structures in *device_list. The returned list is guaranteed to * remain unchanged and valid until (a) another call to this function * is performed or (b) a call to sane_exit() is performed. This * function can be called repeatedly to detect when new devices become * available. If argument local_only is true, only local devices are * returned (devices directly attached to the machine that SANE is * running on). If it is false, the device list includes all remote * devices that are accessible to the SANE library. * * SANE does not require that this function is called before a * sane_open() call is performed. A device name may be specified * explicitly by a user which would make it unnecessary and * undesirable to call this function first. */ SANE_Status sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only) { int i; struct hp3500_data *dev; DBG (10, "sane_get_devices %d\n", local_only); if (devlist) free (devlist); devlist = calloc (num_devices + 1, sizeof (SANE_Device *)); if (!devlist) return SANE_STATUS_NO_MEM; for (dev = first_dev, i = 0; i < num_devices; dev = dev->next) devlist[i++] = &dev->sane; devlist[i++] = 0; *device_list = devlist; return SANE_STATUS_GOOD; } /** * Called to establish connection with the scanner. This function will * also establish meaningful defaults and initialize the options. * * From the SANE spec: * This function is used to establish a connection to a particular * device. The name of the device to be opened is passed in argument * name. If the call completes successfully, a handle for the device * is returned in *h. As a special case, specifying a zero-length * string as the device requests opening the first available device * (if there is such a device). */ SANE_Status sane_open (SANE_String_Const name, SANE_Handle * handle) { struct hp3500_data *dev = NULL; struct hp3500_data *scanner = NULL; if (name[0] == 0) { DBG (10, "sane_open: no device requested, using default\n"); if (first_dev) { scanner = (struct hp3500_data *) first_dev; DBG (10, "sane_open: device %s found\n", first_dev->sane.name); } } else { DBG (10, "sane_open: device %s requested\n", name); for (dev = first_dev; dev; dev = dev->next) { if (strcmp (dev->sane.name, name) == 0) { DBG (10, "sane_open: device %s found\n", name); scanner = (struct hp3500_data *) dev; } } } if (!scanner) { DBG (10, "sane_open: no device found\n"); return SANE_STATUS_INVAL; } *handle = scanner; init_options (scanner); scanner->resolution = 200; scanner->request_mm.left = 0; scanner->request_mm.top = 0; scanner->request_mm.right = SCANNER_UNIT_TO_FIXED_MM (10200); scanner->request_mm.bottom = SCANNER_UNIT_TO_FIXED_MM (14100); scanner->mode = 0; scanner->brightness = 128; scanner->contrast = 64; scanner->gamma = 2.2; calculateDerivedValues (scanner); return SANE_STATUS_GOOD; } /** * An advanced method we don't support but have to define. */ SANE_Status sane_set_io_mode (SANE_Handle h, SANE_Bool non_blocking) { DBG (10, "sane_set_io_mode\n"); DBG (99, "%d %p\n", non_blocking, h); return SANE_STATUS_UNSUPPORTED; } /** * An advanced method we don't support but have to define. */ SANE_Status sane_get_select_fd (SANE_Handle h, SANE_Int * fdp) { struct hp3500_data *scanner = (struct hp3500_data *) h; DBG (10, "sane_get_select_fd\n"); *fdp = scanner->pipe_r; DBG (99, "%p %d\n", h, *fdp); return SANE_STATUS_GOOD; } /** * Returns the options we know. * * From the SANE spec: * This function is used to access option descriptors. The function * returns the option descriptor for option number n of the device * represented by handle h. Option number 0 is guaranteed to be a * valid option. Its value is an integer that specifies the number of * options that are available for device handle h (the count includes * option 0). If n is not a valid option index, the function returns * NULL. The returned option descriptor is guaranteed to remain valid * (and at the returned address) until the device is closed. */ const SANE_Option_Descriptor * sane_get_option_descriptor (SANE_Handle handle, SANE_Int option) { struct hp3500_data *scanner = handle; DBG (MSG_GET, "sane_get_option_descriptor: \"%s\"\n", scanner->opt[option].name); if ((unsigned) option >= NUM_OPTIONS) return NULL; return &scanner->opt[option]; } /** * Gets or sets an option value. * * From the SANE spec: * This function is used to set or inquire the current value of option * number n of the device represented by handle h. The manner in which * the option is controlled is specified by parameter action. The * possible values of this parameter are described in more detail * below. The value of the option is passed through argument val. It * is a pointer to the memory that holds the option value. The memory * area pointed to by v must be big enough to hold the entire option * value (determined by member size in the corresponding option * descriptor). * * The only exception to this rule is that when setting the value of a * string option, the string pointed to by argument v may be shorter * since the backend will stop reading the option value upon * encountering the first NUL terminator in the string. If argument i * is not NULL, the value of *i will be set to provide details on how * well the request has been met. */ SANE_Status sane_control_option (SANE_Handle handle, SANE_Int option, SANE_Action action, void *val, SANE_Int * info) { struct hp3500_data *scanner = (struct hp3500_data *) handle; SANE_Status status; SANE_Word cap; SANE_Int dummy; int i; /* Make sure that all those statements involving *info cannot break (better * than having to do "if (info) ..." everywhere!) */ if (info == 0) info = &dummy; *info = 0; if (option >= NUM_OPTIONS) return SANE_STATUS_INVAL; cap = scanner->opt[option].cap; /* * SANE_ACTION_GET_VALUE: We have to find out the current setting and * return it in a human-readable form (often, text). */ if (action == SANE_ACTION_GET_VALUE) { DBG (MSG_GET, "sane_control_option: get value \"%s\"\n", scanner->opt[option].name); DBG (11, "\tcap = %d\n", cap); if (!SANE_OPTION_IS_ACTIVE (cap)) { DBG (10, "\tinactive\n"); return SANE_STATUS_INVAL; } switch (option) { case OPT_NUM_OPTS: *(SANE_Word *) val = NUM_OPTIONS; return SANE_STATUS_GOOD; case OPT_RESOLUTION: *(SANE_Word *) val = scanner->resolution; return SANE_STATUS_GOOD; case OPT_TL_X: *(SANE_Word *) val = scanner->request_mm.left; return SANE_STATUS_GOOD; case OPT_TL_Y: *(SANE_Word *) val = scanner->request_mm.top; return SANE_STATUS_GOOD; case OPT_BR_X: *(SANE_Word *) val = scanner->request_mm.right; return SANE_STATUS_GOOD; case OPT_BR_Y: *(SANE_Word *) val = scanner->request_mm.bottom; return SANE_STATUS_GOOD; case OPT_MODE: strcpy ((SANE_Char *) val, scan_mode_list[scanner->mode]); return SANE_STATUS_GOOD; case OPT_CONTRAST: *(SANE_Word *) val = scanner->contrast; return SANE_STATUS_GOOD; case OPT_GAMMA: *(SANE_Word *) val = SANE_FIX(scanner->gamma); return SANE_STATUS_GOOD; case OPT_BRIGHTNESS: *(SANE_Word *) val = scanner->brightness; return SANE_STATUS_GOOD; } } else if (action == SANE_ACTION_SET_VALUE) { DBG (10, "sane_control_option: set value \"%s\"\n", scanner->opt[option].name); if (!SANE_OPTION_IS_ACTIVE (cap)) { DBG (10, "\tinactive\n"); return SANE_STATUS_INVAL; } if (!SANE_OPTION_IS_SETTABLE (cap)) { DBG (10, "\tnot settable\n"); return SANE_STATUS_INVAL; } status = sanei_constrain_value (scanner->opt + option, val, info); if (status != SANE_STATUS_GOOD) { DBG (10, "\tbad value\n"); return status; } /* * Note - for those options which can assume one of a list of * valid values, we can safely assume that they will have * exactly one of those values because that's what * sanei_constrain_value does. Hence no "else: invalid" branches * below. */ switch (option) { case OPT_RESOLUTION: if (scanner->resolution == *(SANE_Word *) val) { return SANE_STATUS_GOOD; } scanner->resolution = (*(SANE_Word *) val); calculateDerivedValues (scanner); *info |= SANE_INFO_RELOAD_PARAMS; return SANE_STATUS_GOOD; case OPT_TL_X: if (scanner->request_mm.left == *(SANE_Word *) val) return SANE_STATUS_GOOD; scanner->request_mm.left = *(SANE_Word *) val; calculateDerivedValues (scanner); if (scanner->actual_mm.left != scanner->request_mm.left) *info |= SANE_INFO_INEXACT; *info |= SANE_INFO_RELOAD_PARAMS; return SANE_STATUS_GOOD; case OPT_TL_Y: if (scanner->request_mm.top == *(SANE_Word *) val) return SANE_STATUS_GOOD; scanner->request_mm.top = *(SANE_Word *) val; calculateDerivedValues (scanner); if (scanner->actual_mm.top != scanner->request_mm.top) *info |= SANE_INFO_INEXACT; *info |= SANE_INFO_RELOAD_PARAMS; return SANE_STATUS_GOOD; case OPT_BR_X: if (scanner->request_mm.right == *(SANE_Word *) val) { return SANE_STATUS_GOOD; } scanner->request_mm.right = *(SANE_Word *) val; calculateDerivedValues (scanner); if (scanner->actual_mm.right != scanner->request_mm.right) *info |= SANE_INFO_INEXACT; *info |= SANE_INFO_RELOAD_PARAMS; return SANE_STATUS_GOOD; case OPT_BR_Y: if (scanner->request_mm.bottom == *(SANE_Word *) val) { return SANE_STATUS_GOOD; } scanner->request_mm.bottom = *(SANE_Word *) val; calculateDerivedValues (scanner); if (scanner->actual_mm.bottom != scanner->request_mm.bottom) *info |= SANE_INFO_INEXACT; *info |= SANE_INFO_RELOAD_PARAMS; return SANE_STATUS_GOOD; case OPT_MODE: for (i = 0; scan_mode_list[i]; ++i) { if (!strcmp ((SANE_Char const *) val, scan_mode_list[i])) { DBG (10, "Setting scan mode to %s (request: %s)\n", scan_mode_list[i], (SANE_Char const *) val); scanner->mode = i; return SANE_STATUS_GOOD; } } /* Impossible */ return SANE_STATUS_INVAL; case OPT_BRIGHTNESS: scanner->brightness = *(SANE_Word *) val; return SANE_STATUS_GOOD; case OPT_CONTRAST: scanner->contrast = *(SANE_Word *) val; return SANE_STATUS_GOOD; case OPT_GAMMA: scanner->gamma = SANE_UNFIX(*(SANE_Word *) val); return SANE_STATUS_GOOD; } /* switch */ } /* else */ return SANE_STATUS_INVAL; } /** * Called by SANE when a page acquisition operation is to be started. * */ SANE_Status sane_start (SANE_Handle handle) { struct hp3500_data *scanner = handle; int defaultFds[2]; int ret; DBG (10, "sane_start\n"); if (scanner->sfd < 0) { /* first call */ DBG (10, "sane_start opening USB device\n"); if (sanei_usb_open (scanner->sane.name, &(scanner->sfd)) != SANE_STATUS_GOOD) { DBG (MSG_ERR, "sane_start: open of %s failed:\n", scanner->sane.name); return SANE_STATUS_INVAL; } } calculateDerivedValues (scanner); DBG (10, "\tbytes per line = %d\n", scanner->bytes_per_scan_line); DBG (10, "\tpixels_per_line = %d\n", scanner->scan_width_pixels); DBG (10, "\tlines = %d\n", scanner->scan_height_pixels); /* create a pipe, fds[0]=read-fd, fds[1]=write-fd */ if (pipe (defaultFds) < 0) { DBG (MSG_ERR, "ERROR: could not create pipe\n"); do_cancel (scanner); return SANE_STATUS_IO_ERROR; } scanner->pipe_r = defaultFds[0]; scanner->pipe_w = defaultFds[1]; ret = SANE_STATUS_GOOD; scanner->reader_pid = sanei_thread_begin (reader_process, scanner); time (&scanner->last_scan); if (!sanei_thread_is_valid (scanner->reader_pid)) { DBG (MSG_ERR, "cannot fork reader process.\n"); DBG (MSG_ERR, "%s", strerror (errno)); ret = SANE_STATUS_IO_ERROR; } if (sanei_thread_is_forked ()) { close (scanner->pipe_w); } if (ret == SANE_STATUS_GOOD) { DBG (10, "sane_start: ok\n"); } return ret; } /** * Called by SANE to retrieve information about the type of data * that the current scan will return. * * From the SANE spec: * This function is used to obtain the current scan parameters. The * returned parameters are guaranteed to be accurate between the time * a scan has been started (sane_start() has been called) and the * completion of that request. Outside of that window, the returned * values are best-effort estimates of what the parameters will be * when sane_start() gets invoked. * * Calling this function before a scan has actually started allows, * for example, to get an estimate of how big the scanned image will * be. The parameters passed to this function are the handle h of the * device for which the parameters should be obtained and a pointer p * to a parameter structure. */ SANE_Status sane_get_parameters (SANE_Handle handle, SANE_Parameters * params) { struct hp3500_data *scanner = (struct hp3500_data *) handle; DBG (10, "sane_get_parameters\n"); calculateDerivedValues (scanner); params->format = (scanner->mode == HP3500_COLOR_SCAN) ? SANE_FRAME_RGB : SANE_FRAME_GRAY; params->depth = (scanner->mode == HP3500_LINEART_SCAN) ? 1 : 8; params->pixels_per_line = scanner->scan_width_pixels; params->lines = scanner->scan_height_pixels; params->bytes_per_line = scanner->bytes_per_scan_line; params->last_frame = 1; DBG (10, "\tdepth %d\n", params->depth); DBG (10, "\tlines %d\n", params->lines); DBG (10, "\tpixels_per_line %d\n", params->pixels_per_line); DBG (10, "\tbytes_per_line %d\n", params->bytes_per_line); return SANE_STATUS_GOOD; } /** * Called by SANE to read data. * * In this implementation, sane_read does nothing much besides reading * data from a pipe and handing it back. On the other end of the pipe * there's the reader process which gets data from the scanner and * stuffs it into the pipe. * * From the SANE spec: * This function is used to read image data from the device * represented by handle h. Argument buf is a pointer to a memory * area that is at least maxlen bytes long. The number of bytes * returned is stored in *len. A backend must set this to zero when * the call fails (i.e., when a status other than SANE_STATUS_GOOD is * returned). * * When the call succeeds, the number of bytes returned can be * anywhere in the range from 0 to maxlen bytes. */ SANE_Status sane_read (SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len, SANE_Int * len) { struct hp3500_data *scanner = (struct hp3500_data *) handle; ssize_t nread; int source = scanner->pipe_r; *len = 0; nread = read (source, buf, max_len); DBG (30, "sane_read: read %ld bytes of %ld\n", (long) nread, (long) max_len); if (nread < 0) { if (errno == EAGAIN) { return SANE_STATUS_GOOD; } else { do_cancel (scanner); return SANE_STATUS_IO_ERROR; } } *len = nread; if (nread == 0) { close (source); DBG (10, "sane_read: pipe closed\n"); return SANE_STATUS_EOF; } return SANE_STATUS_GOOD; } /* sane_read */ /** * Cancels a scan. * * It has been said on the mailing list that sane_cancel is a bit of a * misnomer because it is routinely called to signal the end of a * batch - quoting David Mosberger-Tang: * * > In other words, the idea is to have sane_start() be called, and * > collect as many images as the frontend wants (which could in turn * > consist of multiple frames each as indicated by frame-type) and * > when the frontend is done, it should call sane_cancel(). * > Sometimes it's better to think of sane_cancel() as "sane_stop()" * > but that name would have had some misleading connotations as * > well, that's why we stuck with "cancel". * * The current consensus regarding duplex and ADF scans seems to be * the following call sequence: sane_start; sane_read (repeat until * EOF); sane_start; sane_read... and then call sane_cancel if the * batch is at an end. I.e. do not call sane_cancel during the run but * as soon as you get a SANE_STATUS_NO_DOCS. * * From the SANE spec: * This function is used to immediately or as quickly as possible * cancel the currently pending operation of the device represented by * handle h. This function can be called at any time (as long as * handle h is a valid handle) but usually affects long-running * operations only (such as image is acquisition). It is safe to call * this function asynchronously (e.g., from within a signal handler). * It is important to note that completion of this operation does not * imply that the currently pending operation has been cancelled. It * only guarantees that cancellation has been initiated. Cancellation * completes only when the cancelled call returns (typically with a * status value of SANE_STATUS_CANCELLED). Since the SANE API does * not require any other operations to be re-entrant, this implies * that a frontend must not call any other operation until the * cancelled operation has returned. */ void sane_cancel (SANE_Handle h) { DBG (10, "sane_cancel\n"); do_cancel ((struct hp3500_data *) h); } /** * Ends use of the scanner. * * From the SANE spec: * This function terminates the association between the device handle * passed in argument h and the device it represents. If the device is * presently active, a call to sane_cancel() is performed first. After * this function returns, handle h must not be used anymore. */ void sane_close (SANE_Handle handle) { DBG (10, "sane_close\n"); do_reset (handle); do_cancel (handle); } /** * Terminates the backend. * * From the SANE spec: * This function must be called to terminate use of a backend. The * function will first close all device handles that still might be * open (it is recommended to close device handles explicitly through * a call to sane_clo-se(), but backends are required to release all * resources upon a call to this function). After this function * returns, no function other than sane_init() may be called * (regardless of the status value returned by sane_exit(). Neglecting * to call this function may result in some resources not being * released properly. */ void sane_exit (void) { struct hp3500_data *dev, *next; DBG (10, "sane_exit\n"); for (dev = first_dev; dev; dev = next) { next = dev->next; free (dev->devicename); free (dev); } if (devlist) free (devlist); } /* * The scanning code */ static SANE_Status attachScanner (const char *devicename) { struct hp3500_data *dev; DBG (15, "attach_scanner: %s\n", devicename); for (dev = first_dev; dev; dev = dev->next) { if (strcmp (dev->sane.name, devicename) == 0) { DBG (5, "attach_scanner: scanner already attached (is ok)!\n"); return SANE_STATUS_GOOD; } } if (NULL == (dev = malloc (sizeof (*dev)))) return SANE_STATUS_NO_MEM; memset (dev, 0, sizeof (*dev)); dev->devicename = strdup (devicename); dev->sfd = -1; dev->last_scan = 0; dev->reader_pid = (SANE_Pid) -1; dev->pipe_r = dev->pipe_w = -1; dev->sane.name = dev->devicename; dev->sane.vendor = "Hewlett-Packard"; dev->sane.model = "ScanJet 3500"; dev->sane.type = "scanner"; ++num_devices; *new_dev = dev; DBG (15, "attach_scanner: done\n"); return SANE_STATUS_GOOD; } static SANE_Status init_options (struct hp3500_data *scanner) { int i; SANE_Option_Descriptor *opt; memset (scanner->opt, 0, sizeof (scanner->opt)); for (i = 0; i < NUM_OPTIONS; ++i) { scanner->opt[i].name = "filler"; scanner->opt[i].size = sizeof (SANE_Word); scanner->opt[i].cap = SANE_CAP_INACTIVE; } opt = scanner->opt + OPT_NUM_OPTS; opt->title = SANE_TITLE_NUM_OPTIONS; opt->desc = SANE_DESC_NUM_OPTIONS; opt->type = SANE_TYPE_INT; opt->cap = SANE_CAP_SOFT_DETECT; opt = scanner->opt + OPT_RESOLUTION; opt->name = SANE_NAME_SCAN_RESOLUTION; opt->title = SANE_TITLE_SCAN_RESOLUTION; opt->desc = SANE_DESC_SCAN_RESOLUTION; opt->type = SANE_TYPE_INT; opt->constraint_type = SANE_CONSTRAINT_WORD_LIST; opt->constraint.word_list = res_list; opt->unit = SANE_UNIT_DPI; opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; opt = scanner->opt + OPT_GEOMETRY_GROUP; opt->title = SANE_I18N ("Geometry"); opt->desc = SANE_I18N ("Geometry Group"); opt->type = SANE_TYPE_GROUP; opt->constraint_type = SANE_CONSTRAINT_NONE; opt = scanner->opt + OPT_TL_X; opt->name = SANE_NAME_SCAN_TL_X; opt->title = SANE_TITLE_SCAN_TL_X; opt->desc = SANE_DESC_SCAN_TL_X; opt->type = SANE_TYPE_FIXED; opt->unit = SANE_UNIT_MM; opt->constraint_type = SANE_CONSTRAINT_RANGE; opt->constraint.range = &range_x; opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; opt = scanner->opt + OPT_TL_Y; opt->name = SANE_NAME_SCAN_TL_Y; opt->title = SANE_TITLE_SCAN_TL_Y; opt->desc = SANE_DESC_SCAN_TL_Y; opt->type = SANE_TYPE_FIXED; opt->unit = SANE_UNIT_MM; opt->constraint_type = SANE_CONSTRAINT_RANGE; opt->constraint.range = &range_y; opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; opt = scanner->opt + OPT_BR_X; opt->name = SANE_NAME_SCAN_BR_X; opt->title = SANE_TITLE_SCAN_BR_X; opt->desc = SANE_DESC_SCAN_BR_X; opt->type = SANE_TYPE_FIXED; opt->unit = SANE_UNIT_MM; opt->constraint_type = SANE_CONSTRAINT_RANGE; opt->constraint.range = &range_x; opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; opt = scanner->opt + OPT_BR_Y; opt->name = SANE_NAME_SCAN_BR_Y; opt->title = SANE_TITLE_SCAN_BR_Y; opt->desc = SANE_DESC_SCAN_BR_Y; opt->type = SANE_TYPE_FIXED; opt->unit = SANE_UNIT_MM; opt->constraint_type = SANE_CONSTRAINT_RANGE; opt->constraint.range = &range_y; opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; if (!scan_mode_list[0]) { scan_mode_list[HP3500_COLOR_SCAN] = SANE_VALUE_SCAN_MODE_COLOR; scan_mode_list[HP3500_GRAY_SCAN] = SANE_VALUE_SCAN_MODE_GRAY; scan_mode_list[HP3500_LINEART_SCAN] = SANE_VALUE_SCAN_MODE_LINEART; scan_mode_list[HP3500_TOTAL_SCANS] = 0; } opt = scanner->opt + OPT_MODE_GROUP; opt->title = SANE_I18N ("Scan Mode Group"); opt->desc = SANE_I18N ("Scan Mode Group"); opt->type = SANE_TYPE_GROUP; opt->constraint_type = SANE_CONSTRAINT_NONE; opt = scanner->opt + OPT_MODE; opt->name = SANE_NAME_SCAN_MODE; opt->title = SANE_TITLE_SCAN_MODE; opt->desc = SANE_DESC_SCAN_MODE; opt->type = SANE_TYPE_STRING; opt->size = max_string_size(scan_mode_list); opt->constraint_type = SANE_CONSTRAINT_STRING_LIST; opt->constraint.string_list = (SANE_String_Const *) scan_mode_list; opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; opt = scanner->opt + OPT_BRIGHTNESS; opt->name = SANE_NAME_BRIGHTNESS; opt->title = SANE_TITLE_BRIGHTNESS; opt->desc = SANE_DESC_BRIGHTNESS; opt->type = SANE_TYPE_INT; opt->constraint_type = SANE_CONSTRAINT_RANGE; opt->constraint.range = &range_brightness; opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; opt = scanner->opt + OPT_CONTRAST; opt->name = SANE_NAME_CONTRAST; opt->title = SANE_TITLE_CONTRAST; opt->desc = SANE_DESC_CONTRAST; opt->type = SANE_TYPE_INT; opt->constraint_type = SANE_CONSTRAINT_RANGE; opt->constraint.range = &range_contrast; opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; opt = scanner->opt + OPT_GAMMA; opt->name = SANE_NAME_ANALOG_GAMMA; opt->title = SANE_TITLE_ANALOG_GAMMA; opt->desc = SANE_DESC_ANALOG_GAMMA; opt->type = SANE_TYPE_FIXED; opt->unit = SANE_UNIT_NONE; opt->constraint_type = SANE_CONSTRAINT_RANGE; opt->constraint.range = &range_gamma; opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; return SANE_STATUS_GOOD; } static void do_reset (struct hp3500_data *scanner) { (void) scanner; /* kill warning */ } static void do_cancel (struct hp3500_data *scanner) { if (sanei_thread_is_valid (scanner->reader_pid)) { if (sanei_thread_kill (scanner->reader_pid) == 0) { int exit_status; sanei_thread_waitpid (scanner->reader_pid, &exit_status); } sanei_thread_invalidate (scanner->reader_pid); } if (scanner->pipe_r >= 0) { close (scanner->pipe_r); scanner->pipe_r = -1; } } static void calculateDerivedValues (struct hp3500_data *scanner) { DBG (12, "calculateDerivedValues\n"); /* Convert the SANE_FIXED values for the scan area into 1/1200 inch * scanner units */ scanner->fullres_pixels.left = FIXED_MM_TO_SCANNER_UNIT (scanner->request_mm.left); scanner->fullres_pixels.top = FIXED_MM_TO_SCANNER_UNIT (scanner->request_mm.top); scanner->fullres_pixels.right = FIXED_MM_TO_SCANNER_UNIT (scanner->request_mm.right); scanner->fullres_pixels.bottom = FIXED_MM_TO_SCANNER_UNIT (scanner->request_mm.bottom); DBG (12, "\tleft margin: %u\n", scanner->fullres_pixels.left); DBG (12, "\ttop margin: %u\n", scanner->fullres_pixels.top); DBG (12, "\tright margin: %u\n", scanner->fullres_pixels.right); DBG (12, "\tbottom margin: %u\n", scanner->fullres_pixels.bottom); scanner->scan_width_pixels = scanner->resolution * (scanner->fullres_pixels.right - scanner->fullres_pixels.left) / 1200; scanner->scan_height_pixels = scanner->resolution * (scanner->fullres_pixels.bottom - scanner->fullres_pixels.top) / 1200; if (scanner->mode == HP3500_LINEART_SCAN) scanner->bytes_per_scan_line = (scanner->scan_width_pixels + 7) / 8; else if (scanner->mode == HP3500_GRAY_SCAN) scanner->bytes_per_scan_line = scanner->scan_width_pixels; else scanner->bytes_per_scan_line = scanner->scan_width_pixels * 3; if (scanner->scan_width_pixels < 1) scanner->scan_width_pixels = 1; if (scanner->scan_height_pixels < 1) scanner->scan_height_pixels = 1; scanner->actres_pixels.left = scanner->fullres_pixels.left * scanner->resolution / 1200; scanner->actres_pixels.top = scanner->fullres_pixels.top * scanner->resolution / 1200; scanner->actres_pixels.right = scanner->actres_pixels.left + scanner->scan_width_pixels; scanner->actres_pixels.bottom = scanner->actres_pixels.top + scanner->scan_height_pixels; scanner->actual_mm.left = SCANNER_UNIT_TO_FIXED_MM (scanner->fullres_pixels.left); scanner->actual_mm.top = SCANNER_UNIT_TO_FIXED_MM (scanner->fullres_pixels.top); scanner->actual_mm.bottom = SCANNER_UNIT_TO_FIXED_MM (scanner->scan_width_pixels * 1200 / scanner->resolution); scanner->actual_mm.right = SCANNER_UNIT_TO_FIXED_MM (scanner->scan_height_pixels * 1200 / scanner->resolution); DBG (12, "calculateDerivedValues: ok\n"); } /* From here on in we have the original code written for the scanner demo */ #define MAX_COMMANDS_BYTES 131072 #define MAX_READ_COMMANDS 1 /* Issuing more than one register * read command in a single request * seems to put the device in an * unpredictable state. */ #define MAX_READ_BYTES 0xffc0 #define REG_DESTINATION_POSITION 0x60 #define REG_MOVE_CONTROL_TEST 0xb3 static int command_reads_outstanding = 0; static int command_bytes_outstanding = 0; static unsigned char command_buffer[MAX_COMMANDS_BYTES]; static int receive_bytes_outstanding = 0; static char *command_readmem_outstanding[MAX_READ_COMMANDS]; static int command_readbytes_outstanding[MAX_READ_COMMANDS]; static unsigned char sram_access_method = 0; static unsigned sram_size = 0; static int udh; static int rt_execute_commands (void) { SANE_Status result; size_t bytes; if (!command_bytes_outstanding) return 0; bytes = command_bytes_outstanding; result = sanei_usb_write_bulk (udh, /* 0x02, */ command_buffer, &bytes); if (result == SANE_STATUS_GOOD && receive_bytes_outstanding) { unsigned char readbuf[MAX_READ_BYTES]; int total_read = 0; do { bytes = receive_bytes_outstanding - total_read; result = sanei_usb_read_bulk (udh, /* 0x81, */ readbuf + total_read, &bytes); if (result == SANE_STATUS_GOOD) total_read += bytes; else break; } while (total_read < receive_bytes_outstanding); if (result == SANE_STATUS_GOOD) { unsigned char *readptr; int i; for (i = 0, readptr = readbuf; i < command_reads_outstanding; readptr += command_readbytes_outstanding[i++]) { memcpy (command_readmem_outstanding[i], readptr, command_readbytes_outstanding[i]); } } } receive_bytes_outstanding = command_reads_outstanding = command_bytes_outstanding = 0; return (result == SANE_STATUS_GOOD) ? 0 : -1; } static int rt_queue_command (int command, int reg, int count, int bytes, void const *data_, int readbytes, void *readdata) { int len = 4 + bytes; unsigned char *buffer; unsigned char const *data = data_; /* We add "bytes" here to account for the possibility that all of the * data bytes are 0xaa and hence require a following 0x00 byte. */ if (command_bytes_outstanding + len + bytes > MAX_COMMANDS_BYTES || (readbytes && ((command_reads_outstanding >= MAX_READ_COMMANDS) || (receive_bytes_outstanding >= MAX_READ_BYTES)))) { if (rt_execute_commands () < 0) return -1; } buffer = command_buffer + command_bytes_outstanding; *buffer++ = command; *buffer++ = reg; *buffer++ = count >> 8; *buffer++ = count; while (bytes--) { *buffer++ = *data; if (*data++ == 0xaa) { *buffer++ = 0; ++len; } } command_bytes_outstanding += len; if (readbytes) { command_readbytes_outstanding[command_reads_outstanding] = readbytes; command_readmem_outstanding[command_reads_outstanding] = readdata; receive_bytes_outstanding += readbytes; ++command_reads_outstanding; } return 0; } static int rt_send_command_immediate (int command, int reg, int count, int bytes, void *data, int readbytes, void *readdata) { rt_queue_command (command, reg, count, bytes, data, readbytes, readdata); return rt_execute_commands (); } static int rt_queue_read_register (int reg, int bytes, void *data) { return rt_queue_command (RTCMD_GETREG, reg, bytes, 0, 0, bytes, data); } static int rt_read_register_immediate (int reg, int bytes, void *data) { if (rt_queue_read_register (reg, bytes, data) < 0) return -1; return rt_execute_commands (); } static int rt_queue_set_register (int reg, int bytes, void *data) { return rt_queue_command (RTCMD_SETREG, reg, bytes, bytes, data, 0, 0); } static int rt_set_register_immediate (int reg, int bytes, void *data) { if (reg < 0xb3 && reg + bytes > 0xb3) { int bytes_in_first_block = 0xb3 - reg; if (rt_set_register_immediate (reg, bytes_in_first_block, data) < 0 || rt_set_register_immediate (0xb4, bytes - bytes_in_first_block - 1, (char *) data + bytes_in_first_block + 1) < 0) return -1; return 0; } if (rt_queue_set_register (reg, bytes, data) < 0) return -1; return rt_execute_commands (); } static int rt_set_one_register (int reg, int val) { char r = val; return rt_set_register_immediate (reg, 1, &r); } static int rt_write_sram (int bytes, void *data_) { unsigned char *data = (unsigned char *) data_; /* The number of bytes passed in could be much larger than we can transmit * (0xffc0) bytes. With 0xaa escapes it could be even larger. Accordingly * we need to count the 0xaa escapes and write in chunks if the number of * bytes would otherwise exceed a limit (I have used 0xf000 as the limit). */ while (bytes > 0) { int now = 0; int bufsize = 0; while (now < bytes && bufsize < 0xf000) { int i; /* Try to avoid writing part pages */ for (i = 0; i < 32 && now < bytes; ++i) { ++bufsize; if (data[now++] == 0xaa) ++bufsize; } } if (rt_send_command_immediate (RTCMD_WRITESRAM, 0, now, now, data, 0, 0) < 0) return -1; bytes -= now; data += now; } return 0; } static int rt_read_sram (int bytes, void *data_) { unsigned char *data = (unsigned char *) data_; while (bytes > 0) { int now = (bytes > 0xf000) ? 0xf000 : bytes; if (rt_send_command_immediate (RTCMD_READSRAM, 0, bytes, 0, 0, bytes, data) < 0) return -1; bytes -= now; data += now; } return 0; } static int rt_set_sram_page (int page) { unsigned char regs[2]; regs[0] = page; regs[1] = page >> 8; return rt_set_register_immediate (0x91, 2, regs); } static int rt_detect_sram (unsigned *totalbytes, unsigned char *r93setting) { char data[0x818]; char testbuf[0x818]; unsigned i; int test_values[] = { 6, 2, 1, -1 }; for (i = 0; i < sizeof (data); ++i) data[i] = i % 0x61; for (i = 0; test_values[i] != -1; ++i) { if (rt_set_one_register (0x93, test_values[i]) || rt_set_sram_page (0x81) || rt_write_sram (0x818, data) || rt_set_sram_page (0x81) || rt_read_sram (0x818, testbuf)) return -1; if (!memcmp (testbuf, data, 0x818)) { sram_access_method = test_values[i]; if (r93setting) *r93setting = sram_access_method; break; } } if (!sram_access_method) return -1; for (i = 0; i < 16; ++i) { int j; char write_data[32]; char read_data[32]; int pagesetting; for (j = 0; j < 16; j++) { write_data[j * 2] = j * 2; write_data[j * 2 + 1] = i; } pagesetting = i * 4096; if (rt_set_sram_page (pagesetting) < 0 || rt_write_sram (32, write_data) < 0) return -1; if (i) { if (rt_set_sram_page (0) < 0 || rt_read_sram (32, read_data) < 0) return -1; if (!memcmp (read_data, write_data, 32)) { sram_size = i * 0x20000; if (totalbytes) *totalbytes = sram_size; return 0; } } } return -1; } static int rt_get_available_bytes (void) { unsigned char data[3]; if (rt_queue_command (RTCMD_BYTESAVAIL, 0, 3, 0, 0, 3, data) < 0 || rt_execute_commands () < 0) return -1; return ((unsigned) data[0]) | ((unsigned) data[1] << 8) | ((unsigned) data[2] << 16); } static int rt_get_data (int bytes, void *data) { while (bytes) { int bytesnow = bytes; if (bytesnow > 0xffc0) bytesnow = 0xffc0; if (rt_queue_command (RTCMD_READBYTES, 0, bytesnow, 0, 0, bytesnow, data) < 0 || rt_execute_commands () < 0) return -1; bytes -= bytesnow; data = (char *) data + bytesnow; } return 0; } static int rt_is_moving (void) { char r; if (rt_read_register_immediate (REG_MOVE_CONTROL_TEST, 1, &r) < 0) return -1; if (r == 0x08) return 1; return 0; } static int rt_is_rewound (void) { char r; if (rt_read_register_immediate (0x1d, 1, &r) < 0) return -1; if (r & 0x02) return 1; return 0; } static int rt_set_direction_forwards (unsigned char *regs) { regs[0xc6] |= 0x08; return 0; } static int rt_set_direction_rewind (unsigned char *regs) { regs[0xc6] &= 0xf7; return 0; } static int rt_set_stop_when_rewound (unsigned char *regs, int stop) { if (stop) regs[0xb2] |= 0x10; else regs[0xb2] &= 0xef; return 0; } static int rt_start_moving (void) { if (rt_set_one_register (REG_MOVE_CONTROL_TEST, 2) < 0 || rt_set_one_register (REG_MOVE_CONTROL_TEST, 2) < 0 || rt_set_one_register (REG_MOVE_CONTROL_TEST, 0) < 0 || rt_set_one_register (REG_MOVE_CONTROL_TEST, 0) < 0 || rt_set_one_register (REG_MOVE_CONTROL_TEST, 8) < 0 || rt_set_one_register (REG_MOVE_CONTROL_TEST, 8) < 0) return -1; return 0; } static int rt_stop_moving (void) { if (rt_set_one_register (REG_MOVE_CONTROL_TEST, 2) < 0 || rt_set_one_register (REG_MOVE_CONTROL_TEST, 2) < 0 || rt_set_one_register (REG_MOVE_CONTROL_TEST, 0) < 0 || rt_set_one_register (REG_MOVE_CONTROL_TEST, 0) < 0) return -1; return 0; } static int rt_set_powersave_mode (int enable) { unsigned char r; if (rt_read_register_immediate (REG_MOVE_CONTROL_TEST, 1, &r) < 0) return -1; if (r & 0x04) { if (enable == 1) return 0; r &= ~0x04; } else { if (enable == 0) return 0; r |= 0x04; } if (rt_set_one_register (REG_MOVE_CONTROL_TEST, r) < 0 || rt_set_one_register (REG_MOVE_CONTROL_TEST, r) < 0) return -1; return 0; } static int rt_turn_off_lamp (void) { return rt_set_one_register (0x3a, 0); } static int rt_turn_on_lamp (void) { char r3ab[2]; char r10; char r58; if (rt_read_register_immediate (0x3a, 1, r3ab) < 0 || rt_read_register_immediate (0x10, 1, &r10) < 0 || rt_read_register_immediate (0x58, 1, &r58) < 0) return -1; r3ab[0] |= 0x80; r3ab[1] = 0x40; r10 |= 0x01; r58 &= 0x0f; if (rt_set_register_immediate (0x3a, 2, r3ab) < 0 || rt_set_one_register (0x10, r10) < 0 || rt_set_one_register (0x58, r58) < 0) return -1; return 0; } static int rt_set_value_lsbfirst (unsigned char *regs, int firstreg, int totalregs, unsigned value) { while (totalregs--) { regs[firstreg++] = value & 0xff; value >>= 8; } return 0; } #if 0 static int rt_set_value_msbfirst (unsigned char *regs, int firstreg, int totalregs, unsigned value) { while (totalregs--) { regs[firstreg + totalregs] = value & 0xff; value >>= 8; } return 0; } #endif static int rt_set_ccd_shift_clock_multiplier (unsigned char *regs, unsigned value) { return rt_set_value_lsbfirst (regs, 0xf0, 3, value); } static int rt_set_ccd_clock_reset_interval (unsigned char *regs, unsigned value) { return rt_set_value_lsbfirst (regs, 0xf9, 3, value); } static int rt_set_ccd_clamp_clock_multiplier (unsigned char *regs, unsigned value) { return rt_set_value_lsbfirst (regs, 0xfc, 3, value); } static int rt_set_movement_pattern (unsigned char *regs, unsigned value) { return rt_set_value_lsbfirst (regs, 0xc0, 3, value); } static int rt_set_motor_movement_clock_multiplier (unsigned char *regs, unsigned value) { regs[0x40] = (regs[0x40] & ~0xc0) | (value << 6); return 0; } static int rt_set_motor_type (unsigned char *regs, unsigned value) { regs[0xc9] = (regs[0xc9] & 0xf8) | (value & 0x7); return 0; } static int rt_set_noscan_distance (unsigned char *regs, unsigned value) { DBG (10, "Setting distance without scanning to %d\n", value); return rt_set_value_lsbfirst (regs, 0x60, 2, value); } static int rt_set_total_distance (unsigned char *regs, unsigned value) { DBG (10, "Setting total distance to %d\n", value); return rt_set_value_lsbfirst (regs, 0x62, 2, value); } static int rt_set_scanline_start (unsigned char *regs, unsigned value) { return rt_set_value_lsbfirst (regs, 0x66, 2, value); } static int rt_set_scanline_end (unsigned char *regs, unsigned value) { return rt_set_value_lsbfirst (regs, 0x6c, 2, value); } static int rt_set_basic_calibration (unsigned char *regs, int redoffset1, int redoffset2, int redgain, int greenoffset1, int greenoffset2, int greengain, int blueoffset1, int blueoffset2, int bluegain) { regs[0x02] = redoffset1; regs[0x05] = redoffset2; regs[0x08] = redgain; regs[0x03] = greenoffset1; regs[0x06] = greenoffset2; regs[0x09] = greengain; regs[0x04] = blueoffset1; regs[0x07] = blueoffset2; regs[0x0a] = bluegain; return 0; } static int rt_set_calibration_addresses (unsigned char *regs, unsigned redaddr, unsigned greenaddr, unsigned blueaddr, unsigned endaddr, unsigned width) { unsigned endpage = (endaddr + 31) / 32; unsigned scanline_pages = ((width + 1) * 3 + 31) / 32; /* Red, green and blue detailed calibration addresses */ regs[0x84] = redaddr; regs[0x8e] = (regs[0x8e] & 0x0f) | ((redaddr >> 4) & 0xf0); rt_set_value_lsbfirst (regs, 0x85, 2, greenaddr); rt_set_value_lsbfirst (regs, 0x87, 2, blueaddr); /* I don't know what the next three are used for, but each buffer commencing * at 0x80 and 0x82 needs to hold a full scan line. */ rt_set_value_lsbfirst (regs, 0x80, 2, endpage); rt_set_value_lsbfirst (regs, 0x82, 2, endpage + scanline_pages); rt_set_value_lsbfirst (regs, 0x89, 2, endpage + scanline_pages * 2); /* I don't know what this is, but it seems to be a number of pages that can hold * 16 complete scan lines, but not calculated as an offset from any other page */ rt_set_value_lsbfirst (regs, 0x51, 2, (48 * (width + 1) + 31) / 32); /* I don't know what this is either, but this is what the Windows driver does */ rt_set_value_lsbfirst (regs, 0x8f, 2, 0x1c00); return 0; } static int rt_set_lamp_duty_cycle (unsigned char *regs, int enable, int frequency, int offduty) { if (enable) regs[0x3b] |= 0x80; else regs[0x3b] &= 0x7f; regs[0x3b] = (regs[0x3b] & 0x80) | ((frequency & 0x7) << 4) | (offduty & 0x0f); regs[0x3d] = (regs[0x3d] & 0x7f) | ((frequency & 0x8) << 4); return 0; } static int rt_set_data_feed_on (unsigned char *regs) { regs[0xb2] &= ~0x04; return 0; } static int rt_set_data_feed_off (unsigned char *regs) { regs[0xb2] |= 0x04; return 0; } static int rt_enable_ccd (unsigned char *regs, int enable) { if (enable) regs[0x00] &= ~0x10; else regs[0x00] |= 0x10; return 0; } static int rt_set_cdss (unsigned char *regs, int val1, int val2) { regs[0x28] = (regs[0x28] & 0xe0) | (val1 & 0x1f); regs[0x2a] = (regs[0x2a] & 0xe0) | (val2 & 0x1f); return 0; } static int rt_set_cdsc (unsigned char *regs, int val1, int val2) { regs[0x29] = (regs[0x29] & 0xe0) | (val1 & 0x1f); regs[0x2b] = (regs[0x2b] & 0xe0) | (val2 & 0x1f); return 0; } static int rt_update_after_setting_cdss2 (unsigned char *regs) { int fullcolour = (!(regs[0x2f] & 0xc0) && (regs[0x2f] & 0x04)); int value = regs[0x2a] & 0x1f; regs[0x2a] = (regs[0x2a] & 0xe0) | (value & 0x1f); if (fullcolour) value *= 3; if ((regs[0x40] & 0xc0) == 0x40) value += 17; else value += 16; regs[0x2c] = (regs[0x2c] & 0xe0) | (value % 24); regs[0x2d] = (regs[0x2d] & 0xe0) | ((value + 2) % 24); return 0; } static int rt_set_cph0s (unsigned char *regs, int on) { if (on) regs[0x2d] |= 0x20; /* 1200dpi horizontal coordinate space */ else regs[0x2d] &= ~0x20; /* 600dpi horizontal coordinate space */ return 0; } static int rt_set_cvtr_lm (unsigned char *regs, int val1, int val2, int val3) { regs[0x28] = (regs[0x28] & ~0xe0) | (val1 << 5); regs[0x29] = (regs[0x29] & ~0xe0) | (val2 << 5); regs[0x2a] = (regs[0x2a] & ~0xe0) | (val3 << 5); return 0; } static int rt_set_cvtr_mpt (unsigned char *regs, int val1, int val2, int val3) { regs[0x3c] = (val1 & 0x0f) | (val2 << 4); regs[0x3d] = (regs[0x3d] & 0xf0) | (val3 & 0x0f); return 0; } static int rt_set_cvtr_wparams (unsigned char *regs, unsigned fpw, unsigned bpw, unsigned w) { regs[0x31] = (w & 0x0f) | ((bpw << 4) & 0x30) | (fpw << 6); return 0; } static int rt_enable_movement (unsigned char *regs, int enable) { if (enable) regs[0xc3] |= 0x80; else regs[0xc3] &= ~0x80; return 0; } static int rt_set_scan_frequency (unsigned char *regs, int frequency) { regs[0x64] = (regs[0x64] & 0xf0) | (frequency & 0x0f); return 0; } static int rt_set_merge_channels (unsigned char *regs, int on) { /* RGBRGB instead of RRRRR...GGGGG...BBBB */ regs[0x2f] &= ~0x14; regs[0x2f] |= on ? 0x04 : 0x10; return 0; } static int rt_set_channel (unsigned char *regs, int channel) { regs[0x2f] = (regs[0x2f] & ~0xc0) | (channel << 6); return 0; } static int rt_set_single_channel_scanning (unsigned char *regs, int on) { if (on) regs[0x2f] |= 0x20; else regs[0x2f] &= ~0x20; return 0; } static int rt_set_colour_mode (unsigned char *regs, int on) { if (on) regs[0x2f] |= 0x02; else regs[0x2f] &= ~0x02; return 0; } static int rt_set_horizontal_resolution (unsigned char *regs, int resolution) { int base_resolution = 300; if (regs[0x2d] & 0x20) base_resolution *= 2; if (regs[0xd3] & 0x08) base_resolution *= 2; regs[0x7a] = base_resolution / resolution; return 0; } static int rt_set_last_sram_page (unsigned char *regs, int pagenum) { rt_set_value_lsbfirst (regs, 0x8b, 2, pagenum); return 0; } static int rt_set_step_size (unsigned char *regs, int stepsize) { rt_set_value_lsbfirst (regs, 0xe2, 2, stepsize); rt_set_value_lsbfirst (regs, 0xe0, 2, 0); return 0; } static int rt_set_all_registers (void const *regs_) { char regs[255]; memcpy (regs, regs_, 255); regs[0x32] &= ~0x40; if (rt_set_one_register (0x32, regs[0x32]) < 0 || rt_set_register_immediate (0, 255, regs) < 0 || rt_set_one_register (0x32, regs[0x32] | 0x40) < 0) return -1; return 0; } static int rt_adjust_misc_registers (unsigned char *regs) { /* Mostly unknown purposes - probably no need to adjust */ regs[0xc6] = (regs[0xc6] & 0x0f) | 0x20; /* Purpose unknown - appears to do nothing */ regs[0x2e] = 0x86; /* ???? - Always has this value */ regs[0x30] = 2; /* CCPL = 1 */ regs[0xc9] |= 0x38; /* Doesn't have any obvious effect, but the Windows driver does this */ return 0; } #define NVR_MAX_ADDRESS_SIZE 11 #define NVR_MAX_OPCODE_SIZE 3 #define NVR_DATA_SIZE 8 #define NVR_MAX_COMMAND_SIZE ((NVR_MAX_ADDRESS_SIZE + \ NVR_MAX_OPCODE_SIZE + \ NVR_DATA_SIZE) * 2 + 1) static int rt_nvram_enable_controller (int enable) { unsigned char r; if (rt_read_register_immediate (0x1d, 1, &r) < 0) return -1; if (enable) r |= 1; else r &= ~1; return rt_set_one_register (0x1d, r); } static int rt_nvram_init_command (void) { unsigned char regs[13]; if (rt_read_register_immediate (0x10, 13, regs) < 0) return -1; regs[2] |= 0xf0; regs[4] = (regs[4] & 0x1f) | 0x60; return rt_set_register_immediate (0x10, 13, regs); } static int rt_nvram_init_stdvars (int block, int *addrbits, unsigned char *basereg) { int bitsneeded; int capacity; switch (block) { case 0: bitsneeded = 7; break; case 1: bitsneeded = 9; break; case 2: bitsneeded = 11; break; default: bitsneeded = 0; capacity = 1; while (capacity < block) capacity <<= 1, ++bitsneeded; break; } *addrbits = bitsneeded; if (rt_read_register_immediate (0x10, 1, basereg) < 0) return -1; *basereg &= ~0x60; return 0; } static void rt_nvram_set_half_bit (unsigned char *buffer, int value, unsigned char stdbits, int whichhalf) { *buffer = stdbits | (value ? 0x40 : 0) | (whichhalf ? 0x20 : 0); } static void rt_nvram_set_command_bit (unsigned char *buffer, int value, unsigned char stdbits) { rt_nvram_set_half_bit (buffer, value, stdbits, 0); rt_nvram_set_half_bit (buffer + 1, value, stdbits, 1); } static void rt_nvram_set_addressing_bits (unsigned char *buffer, int location, int addressingbits, unsigned char stdbits) { int currentbit = 1 << (addressingbits - 1); while (addressingbits--) { rt_nvram_set_command_bit (buffer, (location & currentbit) ? 1 : 0, stdbits); buffer += 2; currentbit >>= 1; } } #if 0 static int rt_nvram_enable_write (int addressingbits, int enable, unsigned char stdbits) { unsigned char cmdbuffer[NVR_MAX_COMMAND_SIZE]; int cmdsize = 6 + addressingbits * 2; rt_nvram_set_command_bit (cmdbuffer, 1, stdbits); rt_nvram_set_command_bit (cmdbuffer + 2, 0, stdbits); rt_nvram_set_command_bit (cmdbuffer + 4, 0, stdbits); rt_nvram_set_command_bit (cmdbuffer + 6, enable, stdbits); if (addressingbits > 1) rt_nvram_set_addressing_bits (cmdbuffer + 8, 0, addressingbits - 1, stdbits); if (rt_nvram_enable_controller (1) < 0 || rt_send_command_immediate (RTCMD_NVRAMCONTROL, 0, cmdsize, cmdsize, cmdbuffer, 0, 0) < 0 || rt_nvram_enable_controller (0) < 0) { return -1; } return 0; } static int rt_nvram_write (int block, int location, char const *data, int bytes) { int addressingbits; unsigned char stdbits; unsigned char cmdbuffer[NVR_MAX_COMMAND_SIZE]; unsigned char *address_bits; unsigned char *data_bits; int cmdsize; /* This routine doesn't appear to work, but I can't see anything wrong with it */ if (rt_nvram_init_stdvars (block, &addressingbits, &stdbits) < 0) return -1; cmdsize = (addressingbits + 8) * 2 + 6; address_bits = cmdbuffer + 6; data_bits = address_bits + (addressingbits * 2); rt_nvram_set_command_bit (cmdbuffer, 1, stdbits); rt_nvram_set_command_bit (cmdbuffer + 2, 0, stdbits); rt_nvram_set_command_bit (cmdbuffer + 4, 1, stdbits); if (rt_nvram_init_command () < 0 || rt_nvram_enable_write (addressingbits, 1, stdbits) < 0) return -1; while (bytes--) { int i; rt_nvram_set_addressing_bits (address_bits, location, addressingbits, stdbits); rt_nvram_set_addressing_bits (data_bits, *data++, 8, stdbits); if (rt_nvram_enable_controller (1) < 0 || rt_send_command_immediate (RTCMD_NVRAMCONTROL, 0, cmdsize, cmdsize, cmdbuffer, 0, 0) < 0 || rt_nvram_enable_controller (0) < 0) return -1; if (rt_nvram_enable_controller (1) < 0) return -1; for (i = 0; i < cmdsize; ++i) { unsigned char r; unsigned char cmd; rt_nvram_set_half_bit (&cmd, 0, stdbits, i & 1); if (rt_send_command_immediate (RTCMD_NVRAMCONTROL, 0, 1, 1, &cmd, 0, 0) < 0 || rt_read_register_immediate (0x10, 1, &r) < 0) { return -1; } else if (r & 0x80) { break; } } if (rt_nvram_enable_controller (0) < 0) return -1; ++location; } if (rt_nvram_enable_write (addressingbits, 0, stdbits) < 0) return -1; return 0; } #endif static int rt_nvram_read (int block, int location, unsigned char *data, int bytes) { int addressingbits; unsigned char stdbits; unsigned char cmdbuffer[NVR_MAX_COMMAND_SIZE]; unsigned char *address_bits; unsigned char readbit_command[2]; int cmdsize; if (rt_nvram_init_stdvars (block, &addressingbits, &stdbits) < 0) return -1; cmdsize = addressingbits * 2 + 7; address_bits = cmdbuffer + 6; rt_nvram_set_command_bit (cmdbuffer, 1, stdbits); rt_nvram_set_command_bit (cmdbuffer + 2, 1, stdbits); rt_nvram_set_command_bit (cmdbuffer + 4, 0, stdbits); rt_nvram_set_half_bit (cmdbuffer + cmdsize - 1, 0, stdbits, 0); rt_nvram_set_half_bit (readbit_command, 0, stdbits, 1); rt_nvram_set_half_bit (readbit_command + 1, 0, stdbits, 0); if (rt_nvram_init_command () < 0) return -1; while (bytes--) { char c = 0; unsigned char r; int i; rt_nvram_set_addressing_bits (address_bits, location, addressingbits, stdbits); if (rt_nvram_enable_controller (1) < 0 || rt_send_command_immediate (RTCMD_NVRAMCONTROL, 0x1d, cmdsize, cmdsize, cmdbuffer, 0, 0) < 0) return -1; for (i = 0; i < 8; ++i) { c <<= 1; if (rt_send_command_immediate (RTCMD_NVRAMCONTROL, 0x1d, 2, 2, readbit_command, 0, 0) < 0 || rt_read_register_immediate (0x10, 1, &r) < 0) return -1; if (r & 0x80) c |= 1; } if (rt_nvram_enable_controller (0) < 0) return -1; *data++ = c; ++location; } return 0; } /* This is what we want as the initial registers, not what they * are at power on time. In particular 13 bytes at 0x10 are * different, and the byte at 0x94 is different. */ static unsigned char initial_regs[] = { /* 0x00 */ 0xf5, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08 */ 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, /* 0x10 */ 0x81, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, /* 0x18 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, /* 0x20 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x06, 0x19, /* 0x30 */ 0xd0, 0x7a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38 */ 0x00, 0x00, 0xa0, 0x37, 0xff, 0x0f, 0x00, 0x00, /* 0x40 */ 0x80, 0x00, 0x00, 0x00, 0x8c, 0x76, 0x00, 0x00, /* 0x48 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50 */ 0x20, 0xbc, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58 */ 0x1d, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, /* 0x60 */ 0x5e, 0xea, 0x5f, 0xea, 0x00, 0x80, 0x64, 0x00, /* 0x68 */ 0x00, 0x00, 0x00, 0x00, 0x84, 0x04, 0x00, 0x00, /* 0x70 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78 */ 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80 */ 0x0f, 0x02, 0x4b, 0x02, 0x00, 0xec, 0x19, 0xd8, /* 0x88 */ 0x2d, 0x87, 0x02, 0xff, 0x3f, 0x78, 0x60, 0x00, /* 0x90 */ 0x1c, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, /* 0x98 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0 */ 0x00, 0x00, 0x00, 0x0c, 0x27, 0x64, 0x00, 0x00, /* 0xa8 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0 */ 0x12, 0x08, 0x06, 0x04, 0x00, 0x00, 0x00, 0x00, /* 0xb8 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0 */ 0x00, 0x00, 0x80, 0x00, 0x10, 0x00, 0x00, 0x00, /* 0xc8 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0 */ 0xff, 0xbf, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, /* 0xd8 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe0 */ 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf8 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; #define RT_NORMAL_TG 0 #define RT_DOUBLE_TG 1 #define RT_TRIPLE_TG 2 #define RT_DDOUBLE_TG 3 #define RT_300_TG 4 #define RT_150_TG 5 #define RT_TEST_TG 6 static struct tg_info__ { int tg_cph0p; int tg_crsp; int tg_cclpp; int tg_cph0s; int tg_cdss1; int tg_cdsc1; int tg_cdss2; int tg_cdsc2; } tg_info[] = { /* CPH CCD Shifting Clock * 0P ??? Perhaps CCD rising edge position * 0S ??? * CRS Reset CCD Clock * P ??? Perhaps CCD falling edge position * CCLP CCD Clamp Clock * P ??? * CDS ??? * S1 ??? * S2 ??? * C1 ??? * C2 ??? */ /*CPH0P CRSP CCLPP CPH0S CDSS1 CDSC1 CDSS2 CDSC2 */ { 0x01FFE0, 0x3c0000, 0x003000, 1, 0xb, 0xd, 0x00, 0x01}, /* NORMAL */ { 0x7ff800, 0xf00000, 0x01c000, 0, 0xb, 0xc, 0x14, 0x15}, /* DOUBLE */ { 0x033fcc, 0x300000, 0x060000, 1, 0x8, 0xa, 0x00, 0x01}, /* TRIPLE */ { 0x028028, 0x300000, 0x060000, 1, 0x8, 0xa, 0x00, 0x01}, /* DDOUBLE */ { 0x7ff800, 0x030000, 0x060000, 0, 0xa, 0xc, 0x17, 0x01}, /* 300 */ { 0x7fc700, 0x030000, 0x060000, 0, 0x7, 0x9, 0x17, 0x01}, /* 150 */ { 0x7ff800, 0x300000, 0x060000, 0, 0xa, 0xc, 0x17, 0x01}, /* TEST */ }; struct resolution_parameters { unsigned resolution; int reg_39_value; int reg_c3_value; int reg_c6_value; int scan_frequency; int cph0s; int red_green_offset; int green_blue_offset; int intra_channel_offset; int motor_movement_clock_multiplier; int d3_bit_3_value; int tg; int step_size; }; /* The TG value sets seem to affect the exposure time: * At 200dpi: * NORMAL gets higher values than DOUBLE * DDOUBLE gives a crazy spike in the data * TRIPLE gives a black result * TEST gives a black result * 300 gives a black result * 150 gives a black result */ static struct resolution_parameters resparms[] = { /* Acceptable values for stepsz are: * 0x157b 0xabd, 0x55e, 0x2af, 0x157, 0xab, 0x55 */ /* My values - all work */ /*res r39 rC3 rC6 freq cph0s rgo gbo intra mmcm d3 tg stepsz */ {1200, 3, 6, 4, 2, 1, 22, 22, 4, 2, 1, RT_NORMAL_TG, 0x157b}, {600, 15, 6, 4, 1, 1, 9, 10, 0, 2, 1, RT_NORMAL_TG, 0x055e}, {400, 3, 1, 4, 1, 1, 6, 6, 1, 2, 1, RT_NORMAL_TG, 0x157b}, {300, 15, 3, 4, 1, 1, 5, 4, 0, 2, 1, RT_NORMAL_TG, 0x02af}, {200, 7, 1, 4, 1, 1, 3, 3, 0, 2, 1, RT_NORMAL_TG, 0x055e}, {150, 15, 3, 1, 1, 1, 2, 2, 0, 2, 1, RT_NORMAL_TG, 0x02af}, {100, 3, 1, 3, 1, 1, 1, 1, 0, 2, 1, RT_NORMAL_TG, 0x0abd}, {75, 15, 3, 3, 1, 1, 1, 1, 0, 2, 1, RT_NORMAL_TG, 0x02af}, {50, 15, 1, 1, 1, 1, 0, 0, 0, 2, 1, RT_NORMAL_TG, 0x055e}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }; struct dcalibdata { unsigned char *buffers[3]; int pixelsperrow; int pixelnow; int channelnow; int firstrowdone; }; static void dump_registers (unsigned char const *); static int rts8801_rewind (void) { unsigned char regs[255]; int n; int tg_setting = RT_DOUBLE_TG; rt_read_register_immediate (0, 255, regs); rt_set_noscan_distance (regs, 59998); rt_set_total_distance (regs, 59999); rt_set_stop_when_rewound (regs, 0); rt_set_one_register (0xc6, 0); rt_set_one_register (0xc6, 0); rt_set_direction_rewind (regs); rt_set_step_size (regs, 0x55); regs[0x39] = 3; regs[0xc3] = (regs[0xc3] & 0xf8) | 0x86; regs[0xc6] = (regs[0xc6] & 0xf8) | 4; rt_set_horizontal_resolution (regs, 25); rt_set_ccd_shift_clock_multiplier (regs, tg_info[tg_setting].tg_cph0p); rt_set_ccd_clock_reset_interval (regs, tg_info[tg_setting].tg_crsp); rt_set_ccd_clamp_clock_multiplier (regs, tg_info[tg_setting].tg_cclpp); rt_set_cdss (regs, tg_info[tg_setting].tg_cdss1, tg_info[tg_setting].tg_cdss2); rt_set_cdsc (regs, tg_info[tg_setting].tg_cdsc1, tg_info[tg_setting].tg_cdsc2); rt_update_after_setting_cdss2 (regs); rt_set_cvtr_wparams (regs, 3, 0, 6); rt_set_cvtr_mpt (regs, 15, 15, 15); rt_set_cvtr_lm (regs, 7, 7, 7); rt_set_motor_type (regs, 2); if (DBG_LEVEL >= 5) dump_registers (regs); rt_set_all_registers (regs); rt_set_one_register (0x2c, regs[0x2c]); rt_start_moving (); while (!rt_is_rewound () && ((n = rt_get_available_bytes ()) > 0 || rt_is_moving () > 0)) { if (n) { char buffer[0xffc0]; if (n > (int) sizeof (buffer)) n = sizeof (buffer); rt_get_data (n, buffer); } else { usleep (10000); } } rt_stop_moving (); return 0; } static int cancelled_scan = 0; static unsigned get_lsbfirst_int (unsigned char const *p, int n) { unsigned value = *p++; int shift = 8; while (--n) { unsigned now = *p++; value |= now << shift; shift += 8; } return value; } static int convert_c6 (int i) { switch (i) { case 3: return 1; case 1: return 2; case 4: return 4; } return -1; } static void dump_registers (unsigned char const *regs) { int i = 0; long pixels; DBG (5, "Scan commencing with registers:\n"); while (i < 255) { int j = 0; char buffer[80]; buffer[0] = 0; sprintf (buffer + strlen (buffer), "%02x:", i); while (j < 8) { sprintf (buffer + strlen (buffer), " %02x", regs[i++]); j++; } sprintf (buffer + strlen (buffer), " -"); while (j++ < 16 && i < 255) sprintf (buffer + strlen (buffer), " %02x", regs[i++]); DBG (5, " %s\n", buffer); } DBG (5, " Position:\n"); DBG (5, " Distance without scanning: %u\n", get_lsbfirst_int (regs + 0x60, 2)); DBG (5, " Total distance: %u\n", get_lsbfirst_int (regs + 0x62, 2)); DBG (5, " Scanning distance: %u\n", get_lsbfirst_int (regs + 0x62, 2) - get_lsbfirst_int (regs + 0x60, 2)); DBG (5, " Direction: %s\n", (regs[0xc6] & 0x08) ? "forward" : "rewind"); DBG (5, " Motor: %s\n", (regs[0xc3] & 0x80) ? "enabled" : "disabled"); if (regs[0x7a]) DBG (5, " X range: %u-%u\n", get_lsbfirst_int (regs + 0x66, 2) / regs[0x7a], get_lsbfirst_int (regs + 0x6c, 2) / regs[0x7a]); DBG (5, " TG Info:\n"); DBG (5, " CPH0P: %06x\n", get_lsbfirst_int (regs + 0xf0, 3)); DBG (5, " CRSP: %06x\n", get_lsbfirst_int (regs + 0xf9, 3)); DBG (5, " CCLPP: %06x\n", get_lsbfirst_int (regs + 0xfc, 3)); DBG (5, " CPH0S: %d\n", (regs[0x2d] & 0x20) ? 1 : 0); DBG (5, " CDSS1: %02x\n", regs[0x28] & 0x1f); DBG (5, " CDSC1: %02x\n", regs[0x29] & 0x1f); DBG (5, " CDSS2: %02x\n", regs[0x2a] & 0x1f); DBG (5, " CDSC2: %02x\n", regs[0x2b] & 0x1f); DBG (5, " Resolution specific:\n"); if (!regs[0x7a]) DBG (5, " Horizontal resolution: Denominator is zero!\n"); else DBG (5, " Horizontal resolution: %u\n", 300 * ((regs[0x2d] & 0x20) ? 2 : 1) * ((regs[0xd3] & 0x08) ? 2 : 1) / regs[0x7a]); DBG (5, " Derived vertical resolution: %u\n", 400 * (regs[0xc3] & 0x1f) * convert_c6 (regs[0xc6] & 0x7) / (regs[0x39] + 1)); DBG (5, " Register D3:3 %u\n", (regs[0xd3] & 0x08) ? 1 : 0); DBG (5, " Register 39: %u\n", regs[0x39]); DBG (5, " Register C3:0-5: %u\n", regs[0xc3] & 0x1f); DBG (5, " Register C6:0-2: %u\n", regs[0xc6] & 0x7); DBG (5, " Motor movement clock multiplier: %u\n", regs[0x40] >> 6); DBG (5, " Step Size: %04x\n", get_lsbfirst_int (regs + 0xe2, 2)); DBG (5, " Frequency: %u\n", regs[0x64] & 0xf); DBG (5, " Colour registers\n"); DBG (5, " Register 2F: %02x\n", regs[0x2f]); DBG (5, " Register 2C: %02x\n", regs[0x2c]); if (regs[0x7a]) { DBG (5, " Scan data estimates:\n"); pixels = (long) (get_lsbfirst_int (regs + 0x62, 2) - get_lsbfirst_int (regs + 0x60, 2)) * (long) (get_lsbfirst_int (regs + 0x6c, 2) - get_lsbfirst_int (regs + 0x66, 2)) / regs[0x7a]; DBG (5, " Pixels: %ld\n", pixels); DBG (5, " Bytes at 24BPP: %ld\n", pixels * 3); DBG (5, " Bytes at 1BPP: %ld\n", pixels / 8); } DBG (5, "\n"); } static int constrain (int val, int min, int max) { if (val < min) { DBG (10, "Clipped %d to %d\n", val, min); val = min; } else if (val > max) { DBG (10, "Clipped %d to %d\n", val, max); val = max; } return val; } #if 0 static void sram_dump_byte(FILE *fp, unsigned char const *left, unsigned leftstart, unsigned leftlimit, unsigned char const *right, unsigned rightstart, unsigned rightlimit, unsigned idx) { unsigned ridx = rightstart + idx; unsigned lidx = leftstart + idx; putc(' ', fp); if (rightstart < rightlimit && leftstart < leftlimit && left[lidx] != right[ridx]) fputs("", fp); if (leftstart < leftlimit) fprintf(fp, "%02x", left[lidx]); else fputs(" ", fp); if (rightstart < rightlimit && leftstart < leftlimit && left[lidx] != right[ridx]) fputs("", fp); } static void dump_sram_to_file(char const *fname, unsigned char const *expected, unsigned end_calibration_offset) { FILE *fp = fopen(fname, "w"); rt_set_sram_page(0); if (fp) { unsigned char buf[1024]; unsigned loc = 0; fprintf(fp, "
\n");
      while (loc < end_calibration_offset)
        {
          unsigned byte = 0;

          rt_read_sram(1024, buf);

          while (byte < 1024)
            {
              unsigned idx = 0;

              fprintf(fp, "%06x:", loc);
              do
                {
		  sram_dump_byte(fp, buf, byte, 1024, expected, loc, end_calibration_offset, idx);
                } while (++idx & 0x7);
              fprintf(fp, " -");
              do
                {
		  sram_dump_byte(fp, buf, byte, 1024, expected, loc, end_calibration_offset, idx);
                } while (++idx & 0x7);

              idx = 0;
              fputs("     ", fp);

              do
                {
                  sram_dump_byte(fp, expected, loc, end_calibration_offset, buf, byte, 1024, idx);
                } while (++idx & 0x7);
              fprintf(fp, " -");
              do
                {
                  sram_dump_byte(fp, expected, loc, end_calibration_offset, buf, byte, 1024, idx);
                } while (++idx & 0x7);


              fputs("\n", fp);
              byte += 16;
              loc += 16;
            }
        }
      fprintf(fp, "
"); fclose(fp); } } #endif static int rts8801_doscan (unsigned width, unsigned height, unsigned colour, unsigned red_green_offset, unsigned green_blue_offset, unsigned intra_channel_offset, rts8801_callback cbfunc, void *params, int oddfirst, unsigned char const *calib_info, int merged_channels, double *postprocess_offsets, double *postprocess_gains) { unsigned rowbytes = 0; unsigned output_rowbytes = 0; unsigned channels = 0; unsigned total_rows = 0; unsigned char *row_buffer; unsigned char *output_buffer; unsigned buffered_rows; int rows_to_begin; int rowbuffer_bytes; int n; unsigned rownow = 0; unsigned bytenow = 0; unsigned char *channel_data[3][2]; unsigned i; unsigned j; int result = 0; unsigned rows_supplied = 0; (void) calib_info; /* Kill warning */ if (cancelled_scan) return -1; rt_start_moving (); channels = 3; rowbytes = width * 3; switch (colour) { case HP3500_GRAY_SCAN: output_rowbytes = width; break; case HP3500_COLOR_SCAN: output_rowbytes = rowbytes; break; case HP3500_LINEART_SCAN: output_rowbytes = (width + 7) / 8; break; } buffered_rows = red_green_offset + green_blue_offset + intra_channel_offset + 1; rows_to_begin = buffered_rows; rowbuffer_bytes = buffered_rows * rowbytes; row_buffer = (unsigned char *) malloc (rowbuffer_bytes); output_buffer = (unsigned char *) malloc (rowbytes); for (i = j = 0; i < channels; ++i) { if (i == 1) j += red_green_offset; else if (i == 2) j += green_blue_offset; if (merged_channels) channel_data[i][1 - oddfirst] = row_buffer + rowbytes * j + i; else channel_data[i][1 - oddfirst] = row_buffer + rowbytes * j + width * i; channel_data[i][oddfirst] = channel_data[i][1 - oddfirst] + rowbytes * intra_channel_offset; } while (((n = rt_get_available_bytes ()) > 0 || rt_is_moving () > 0) && !cancelled_scan) { if (n == 1 && (rt_is_moving () || rt_get_available_bytes () != 1)) n = 0; if (n > 0) { unsigned char buffer[0xffc0]; if (n > 0xffc0) n = 0xffc0; else if ((n > 1) && (n & 1)) --n; if (rt_get_data (n, buffer) >= 0) { unsigned char *bufnow = buffer; while (n) { int numcopy = rowbytes - bytenow; if (numcopy > n) numcopy = n; memcpy (row_buffer + rownow * rowbytes + bytenow, bufnow, numcopy); bytenow += numcopy; bufnow += numcopy; n -= numcopy; if (bytenow == rowbytes) { if (!rows_to_begin || !--rows_to_begin) { unsigned char *outnow = output_buffer; unsigned x; for (i = x = 0; x < width; ++x, i += merged_channels ? channels : 1) { for (j = 0; j < channels; ++j) { unsigned pix = (unsigned char) channel_data[j][i & 1][i]; if (postprocess_gains && postprocess_offsets) { int ppidx = j * width + x; pix = constrain ( pix * postprocess_gains[ppidx] - postprocess_offsets[ppidx], 0, 255); } *outnow++ = pix; } } if (colour == HP3500_GRAY_SCAN || colour == HP3500_LINEART_SCAN) { unsigned char const *in_now = output_buffer; int bit = 7; outnow = output_buffer; for (i = 0; i < width; ++i) { if (colour == HP3500_GRAY_SCAN) { *outnow++ = ((unsigned) in_now[0] * 2989 + (unsigned) in_now[1] * 5870 + (unsigned) in_now[2] * 1140) / 10000; } else { if (bit == 7) *outnow = ((in_now[1] < 0x80) ? 0x80 : 0); else if (in_now[1] < 0x80) *outnow |= (1 << bit); if (bit == 0) { ++outnow; bit = 7; } else { --bit; } } in_now += 3; } } if (rows_supplied++ < height && !((*cbfunc) (params, output_rowbytes, output_buffer))) break; for (i = 0; i < channels; ++i) { for (j = 0; j < 2; ++j) { channel_data[i][j] += rowbytes; if (channel_data[i][j] - row_buffer >= rowbuffer_bytes) channel_data[i][j] -= rowbuffer_bytes; } } } ++total_rows; if (++rownow == buffered_rows) rownow = 0; bytenow = 0; } } } DBG (30, "total_rows = %d\r", total_rows); } else { usleep (10000); } } DBG (10, "\n"); if (n < 0) result = -1; free (output_buffer); free (row_buffer); rt_stop_moving (); return result; } static unsigned local_sram_size; static unsigned char r93setting; #define RTS8801_F_SUPPRESS_MOVEMENT 1 #define RTS8801_F_LAMP_OFF 2 #define RTS8801_F_NO_DISPLACEMENTS 4 #define RTS8801_F_ODDX 8 static int find_resolution_index (unsigned resolution) { int res = 0; for (res = 0; resparms[res].resolution != resolution; ++res) { if (!resparms[res].resolution) return -1; } return res; } static int rts8801_fullscan (unsigned x, unsigned y, unsigned w, unsigned h, unsigned xresolution, unsigned yresolution, unsigned colour, rts8801_callback cbfunc, void *param, unsigned char *calib_info, int flags, int red_calib_offset, int green_calib_offset, int blue_calib_offset, int end_calib_offset, double *postprocess_offsets, double *postprocess_gains) { int ires, jres; int tg_setting; unsigned char regs[256]; unsigned char offdutytime; int result; int scan_frequency; unsigned intra_channel_offset; unsigned red_green_offset; unsigned green_blue_offset; unsigned total_offsets; ires = find_resolution_index (xresolution); jres = find_resolution_index (yresolution); if (ires < 0 || jres < 0) return -1; /* Set scan parameters */ rt_read_register_immediate (0, 255, regs); regs[255] = 0; rt_enable_ccd (regs, 1); rt_enable_movement (regs, 1); rt_set_scan_frequency (regs, 1); rt_adjust_misc_registers (regs); rt_set_cvtr_wparams (regs, 3, 0, 6); rt_set_cvtr_mpt (regs, 15, 15, 15); rt_set_cvtr_lm (regs, 7, 7, 7); rt_set_motor_type (regs, 2); if (rt_nvram_read (0, 0x7b, &offdutytime, 1) < 0 || offdutytime >= 15) { offdutytime = 6; } rt_set_lamp_duty_cycle (regs, 1, /* On */ 10, /* Frequency */ offdutytime); /* Off duty time */ rt_set_movement_pattern (regs, 0x800000); rt_set_direction_forwards (regs); rt_set_stop_when_rewound (regs, 0); rt_set_calibration_addresses (regs, 0, 0, 0, 0, 0); rt_set_basic_calibration (regs, calib_info[0], calib_info[1], calib_info[2], calib_info[3], calib_info[4], calib_info[5], calib_info[6], calib_info[7], calib_info[8]); regs[0x0b] = 0x70; /* If set to 0x71, the alternative, all values are low */ regs[0x40] &= 0xc0; if (red_calib_offset >= 0 && green_calib_offset >= 0 && blue_calib_offset >= 0) { rt_set_calibration_addresses (regs, red_calib_offset, green_calib_offset, blue_calib_offset, end_calib_offset, w); regs[0x40] |= 0x2f; } else if (end_calib_offset >= 0) { rt_set_calibration_addresses (regs, 0x600, 0x600, 0x600, end_calib_offset, w); } rt_set_channel (regs, RT_CHANNEL_ALL); rt_set_single_channel_scanning (regs, 0); rt_set_merge_channels (regs, 1); rt_set_colour_mode (regs, 1); rt_set_last_sram_page (regs, (local_sram_size - 1) >> 5); scan_frequency = resparms[jres].scan_frequency; rt_set_cph0s (regs, resparms[ires].cph0s); if (resparms[ires].d3_bit_3_value) regs[0xd3] |= 0x08; else regs[0xd3] &= 0xf7; if (flags & RTS8801_F_SUPPRESS_MOVEMENT) regs[0xc3] &= 0x7f; regs[0xb2] &= 0xf7; rt_set_horizontal_resolution (regs, xresolution); rt_set_scanline_start (regs, x * (1200 / xresolution) / (resparms[ires].cph0s ? 1 : 2) / (resparms[ires].d3_bit_3_value ? 1 : 2)); rt_set_scanline_end (regs, (x + w) * (1200 / xresolution) / (resparms[ires].cph0s ? 1 : 2) / (resparms[ires].d3_bit_3_value ? 1 : 2)); if (flags & RTS8801_F_NO_DISPLACEMENTS) { red_green_offset = green_blue_offset = intra_channel_offset = 0; } else { red_green_offset = resparms[jres].red_green_offset; green_blue_offset = resparms[jres].green_blue_offset; intra_channel_offset = resparms[jres].intra_channel_offset; } total_offsets = red_green_offset + green_blue_offset + intra_channel_offset; if (y > total_offsets + 2) y -= total_offsets; h += total_offsets; if (yresolution > 75 && !(flags & RTS8801_F_SUPPRESS_MOVEMENT)) { int rmres = find_resolution_index (50); if (rmres >= 0) { int factor = yresolution / 50; int fastres = y / factor; int remainder = y % factor; while (remainder < 2) { --fastres; remainder += factor; } if (fastres >= 3) { y = remainder; rt_set_noscan_distance(regs, fastres * resparms[rmres].scan_frequency - 2); rt_set_total_distance(regs, fastres * resparms[rmres].scan_frequency - 1); rt_set_scan_frequency(regs, 1); tg_setting = resparms[rmres].tg; rt_set_ccd_shift_clock_multiplier (regs, tg_info[tg_setting].tg_cph0p); rt_set_ccd_clock_reset_interval (regs, tg_info[tg_setting].tg_crsp); rt_set_ccd_clamp_clock_multiplier (regs, tg_info[tg_setting].tg_cclpp); rt_set_one_register (0xc6, 0); rt_set_one_register (0xc6, 0); rt_set_step_size (regs, resparms[rmres].step_size); rt_set_motor_movement_clock_multiplier (regs, resparms[rmres]. motor_movement_clock_multiplier); rt_set_cdss (regs, tg_info[tg_setting].tg_cdss1, tg_info[tg_setting].tg_cdss2); rt_set_cdsc (regs, tg_info[tg_setting].tg_cdsc1, tg_info[tg_setting].tg_cdsc2); rt_update_after_setting_cdss2 (regs); regs[0x39] = resparms[rmres].reg_39_value; regs[0xc3] = (regs[0xc3] & 0xf8) | resparms[rmres].reg_c3_value; regs[0xc6] = (regs[0xc6] & 0xf8) | resparms[rmres].reg_c6_value; rt_set_data_feed_off (regs); rt_set_all_registers (regs); rt_set_one_register (0x2c, regs[0x2c]); if (DBG_LEVEL >= 5) dump_registers (regs); rt_start_moving (); while (rt_is_moving ()); } } } rt_set_noscan_distance (regs, y * scan_frequency - 1); rt_set_total_distance (regs, scan_frequency * (y + h) - 1); rt_set_scan_frequency (regs, scan_frequency); tg_setting = resparms[jres].tg; rt_set_ccd_shift_clock_multiplier (regs, tg_info[tg_setting].tg_cph0p); rt_set_ccd_clock_reset_interval (regs, tg_info[tg_setting].tg_crsp); rt_set_ccd_clamp_clock_multiplier (regs, tg_info[tg_setting].tg_cclpp); rt_set_one_register (0xc6, 0); rt_set_one_register (0xc6, 0); rt_set_step_size (regs, resparms[jres].step_size); rt_set_motor_movement_clock_multiplier (regs, resparms[jres]. motor_movement_clock_multiplier); rt_set_cdss (regs, tg_info[tg_setting].tg_cdss1, tg_info[tg_setting].tg_cdss2); rt_set_cdsc (regs, tg_info[tg_setting].tg_cdsc1, tg_info[tg_setting].tg_cdsc2); rt_update_after_setting_cdss2 (regs); regs[0x39] = resparms[jres].reg_39_value; regs[0xc3] = (regs[0xc3] & 0xf8) | resparms[jres].reg_c3_value; regs[0xc6] = (regs[0xc6] & 0xf8) | resparms[jres].reg_c6_value; rt_set_data_feed_on (regs); rt_set_all_registers (regs); rt_set_one_register (0x2c, regs[0x2c]); if (DBG_LEVEL >= 5) dump_registers (regs); result = rts8801_doscan (w, h, colour, red_green_offset, green_blue_offset, intra_channel_offset, cbfunc, param, (x & 1), calib_info, (regs[0x2f] & 0x04) != 0, postprocess_offsets, postprocess_gains); return result; } static int accumfunc (struct dcalibdata *dcd, int bytes, char *data) { unsigned char *c = (unsigned char *) data; while (bytes > 0) { if (dcd->firstrowdone) dcd->buffers[dcd->channelnow][dcd->pixelnow - dcd->pixelsperrow] = *c; if (++dcd->channelnow >= 3) { dcd->channelnow = 0; if (++dcd->pixelnow == dcd->pixelsperrow) ++dcd->firstrowdone; } c++; bytes--; } return 1; } static int calcmedian (unsigned char const *data, int pixel, int pixels_per_row, int elements) { int tallies[256]; int i; int elemstogo = elements / 2; memset (tallies, 0, sizeof (tallies)); data += pixel; for (i = 0; i < elements; ++i) { ++tallies[*data]; data += pixels_per_row; } i = 0; while (elemstogo - tallies[i] > 0) elemstogo -= tallies[i++]; return i; } struct calibdata { unsigned char *buffer; int space; }; static int storefunc (struct calibdata *cd, int bytes, char *data) { if (cd->space > 0) { if (bytes > cd->space) bytes = cd->space; memcpy (cd->buffer, data, bytes); cd->buffer += bytes; cd->space -= bytes; } return 1; } static unsigned sum_channel (unsigned char *p, int n, int bytwo) { unsigned v = 0; while (n-- > 0) { v += *p; p += 3; if (bytwo) p += 3; } return v; } static int do_warmup = 1; #define DETAILED_PASS_COUNT 3 #define DETAILED_PASS_OFFSETS 0 #define DETAILED_PASS_GAINS_FIRSTPASS 1 #define DETAILED_PASS_GAINS_SECONDPASS 2 static int rts8801_scan (unsigned x, unsigned y, unsigned w, unsigned h, unsigned resolution, unsigned colour, unsigned brightness, unsigned contrast, rts8801_callback cbfunc, void *param, double gamma) { unsigned char calib_info[9]; unsigned char calibbuf[2400]; struct dcalibdata dcd; struct calibdata cd; unsigned char *detail_buffer = 0; int iCalibY; int iCalibTarget; int iMoveFlags = 0; unsigned aiBestOffset[6]; int aiPassed[6]; int i; unsigned j; int k; int calibration_size; unsigned char *pDetailedCalib; int red_calibration_offset; int green_calibration_offset; int blue_calibration_offset; int end_calibration_offset; int base_resolution; int resolution_divisor; int resolution_index; int detailed_calibration_rows = 50; unsigned char *tdetail_buffer; int pass; int onechanged; double *postprocess_gains; double *postprocess_offsets; int needs_postprocessed_calibration = 0; double contrast_adjust = (double) contrast / 64; int brightness_adjust = brightness - 0x80; /* Initialise and power up */ rt_set_all_registers (initial_regs); rt_set_powersave_mode (0); /* Initial rewind in case scanner is stuck away from home position */ rts8801_rewind (); /* Detect SRAM */ rt_detect_sram (&local_sram_size, &r93setting); /* Warm up the lamp */ DBG (10, "Warming up the lamp\n"); rt_turn_on_lamp (); if (do_warmup) sleep (25); /* Basic calibration */ DBG (10, "Calibrating (stage 1)\n"); calib_info[2] = calib_info[5] = calib_info[8] = 1; iCalibY = (resolution == 25) ? 1 : 2; iCalibTarget = 550; rt_turn_off_lamp(); for (i = 0; i < 6; ++i) { aiBestOffset[i] = 0xbf; aiPassed[i] = 0; } do { DBG (30, "Initial calibration pass commences\n"); onechanged = 0; for (i = 0; i < 3; ++i) { calib_info[i * 3] = aiBestOffset[i]; calib_info[i * 3 + 1] = aiBestOffset[i + 3]; } cd.buffer = calibbuf; cd.space = sizeof (calibbuf); DBG (30, "Commencing scan for initial calibration pass\n"); rts8801_fullscan (1401, iCalibY, 100, 2, 400, resolution, HP3500_COLOR_SCAN, (rts8801_callback) storefunc, &cd, calib_info, iMoveFlags, -1, -1, -1, -1, 0, 0); DBG (30, "Completed scan for initial calibration pass\n"); iMoveFlags = RTS8801_F_SUPPRESS_MOVEMENT | RTS8801_F_NO_DISPLACEMENTS; iCalibY = 2; for (i = 0; i < 6; ++i) { int sum; if (aiBestOffset[i] >= 255 || aiPassed[i] > 2) continue; sum = sum_channel (calibbuf + i, 50, 1); DBG (20, "channel[%d] sum = %d (target %d)\n", i, sum, iCalibTarget); if (sum < iCalibTarget) { onechanged = 1; ++aiBestOffset[i]; } else { ++aiPassed[i]; } } DBG (30, "Initial calibration pass completed\n"); } while (onechanged); DBG (20, "Offsets calculated\n"); rt_turn_on_lamp(); usleep(500000); tdetail_buffer = (unsigned char *) malloc (w * 3 * detailed_calibration_rows); for (i = 0; i < 3; ++i) { calib_info[i * 3 + 2] = 1; aiPassed[i] = 0; } do { struct dcalibdata dcdt; dcdt.buffers[0] = tdetail_buffer; dcdt.buffers[1] = (tdetail_buffer + w * detailed_calibration_rows); dcdt.buffers[2] = (dcdt.buffers[1] + w * detailed_calibration_rows); dcdt.pixelsperrow = w; dcdt.pixelnow = dcdt.channelnow = dcdt.firstrowdone = 0; DBG (20, "Scanning for part 2 of initial calibration\n"); rts8801_fullscan (x, 4, w, detailed_calibration_rows + 1, resolution, resolution, HP3500_COLOR_SCAN, (rts8801_callback) accumfunc, &dcdt, calib_info, RTS8801_F_SUPPRESS_MOVEMENT | RTS8801_F_NO_DISPLACEMENTS, -1, -1, -1, -1, 0, 0); DBG (20, "Scan for part 2 of initial calibration completed\n"); onechanged = 0; for (i = 0; i < 3; ++i) { int largest = 1; if (aiPassed[i] > 2 || calib_info[i * 3 + 2] >= 63) continue; for (j = 0; j < w; ++j) { int val = calcmedian (dcdt.buffers[i], j, w, detailed_calibration_rows); if (val > largest) largest = val; } if (largest < 0xe0) { ++calib_info[i * 3 + 2]; onechanged = 1; } else { ++aiPassed[i]; } } } while (onechanged); for (i = 0; i < 3; ++i) { DBG (10, "Channel [%d] gain=%02x offset=%02x\n", i, calib_info[i * 3] + 2, calib_info[i * 3]); } DBG (20, "Gain factors calculated\n"); /* Stage 2 calibration */ DBG (10, "Calibrating (stage 2)\n"); detail_buffer = (unsigned char *) malloc (w * 3 * detailed_calibration_rows); dcd.buffers[0] = detail_buffer; dcd.buffers[1] = (detail_buffer + w * detailed_calibration_rows); dcd.buffers[2] = (dcd.buffers[1] + w * detailed_calibration_rows); dcd.pixelsperrow = w; /* And now for the detailed calibration */ resolution_index = find_resolution_index (resolution); base_resolution = 300; if (resparms[resolution_index].cph0s) base_resolution *= 2; if (resparms[resolution_index].d3_bit_3_value) base_resolution *= 2; resolution_divisor = base_resolution / resolution; calibration_size = w * resolution_divisor * 6 + 1568 + 96; red_calibration_offset = 0x600; green_calibration_offset = red_calibration_offset + w * resolution_divisor * 2; blue_calibration_offset = green_calibration_offset + w * resolution_divisor * 2; end_calibration_offset = blue_calibration_offset + w * resolution_divisor * 2; pDetailedCalib = (unsigned char *) malloc (calibration_size); memset (pDetailedCalib, 0, calibration_size); for (i = 0; i < 3; ++i) { int idx = (i == 0) ? red_calibration_offset : (i == 1) ? green_calibration_offset : blue_calibration_offset; for (j = 0; j < 256; j++) { /* Gamma table - appears to be 256 byte pairs for each input * range (so the first entry cover inputs in the range 0 to 1, * the second 1 to 2, and so on), mapping that input range * (including the fractional parts within it) to an output * range. */ pDetailedCalib[i * 512 + j * 2] = j; pDetailedCalib[i * 512 + j * 2 + 1] = j; } for (j = 0; j < w; ++j) { for (k = 0; k < resolution_divisor; ++k) { pDetailedCalib[idx++] = 0; pDetailedCalib[idx++] = 0x80; } } } rt_set_sram_page (0); rt_set_one_register (0x93, r93setting); rt_write_sram (calibration_size, pDetailedCalib); postprocess_gains = (double *) malloc(sizeof(double) * 3 * w); postprocess_offsets = (double *) malloc(sizeof(double) * 3 * w); for (pass = 0; pass < DETAILED_PASS_COUNT; ++pass) { int ppidx = 0; DBG (10, "Performing detailed calibration scan %d\n", pass); switch (pass) { case DETAILED_PASS_OFFSETS: rt_turn_off_lamp(); usleep(500000); /* To be sure it has gone off */ break; case DETAILED_PASS_GAINS_FIRSTPASS: rt_turn_on_lamp(); usleep(500000); /* Give the lamp time to settle */ break; } dcd.pixelnow = dcd.channelnow = dcd.firstrowdone = 0; rts8801_fullscan (x, iCalibY, w, detailed_calibration_rows + 1, resolution, resolution, HP3500_COLOR_SCAN, (rts8801_callback) accumfunc, &dcd, calib_info, RTS8801_F_SUPPRESS_MOVEMENT | RTS8801_F_NO_DISPLACEMENTS, red_calibration_offset, green_calibration_offset, blue_calibration_offset, end_calibration_offset, 0, 0); DBG (10, " Detailed calibration scan %d completed\n", pass); for (i = 0; i < 3; ++i) { int idx = (i == 0) ? red_calibration_offset : (i == 1) ? green_calibration_offset : blue_calibration_offset; for (j = 0; j < w; ++j) { double multnow = 0x80; int offnow = 0; /* This seems to be the approach for reg 0x40 & 0x3f == 0x27, which allows detailed * calibration to return either higher or lower values. */ { double denom1 = calcmedian (dcd.buffers[i], j, w, detailed_calibration_rows); switch (pass) { case DETAILED_PASS_OFFSETS: /* The offset is the number needed to be subtracted from "black" at detailed gain = 0x80, * which is the value we started with. For the next round, pull the gain down to 0x20. Our * next scan is a test scan to confirm the offset works. */ multnow = 0x20; offnow = denom1; break; case DETAILED_PASS_GAINS_FIRSTPASS: multnow = 128.0 / denom1 * 0x20; /* Then bring it up to whatever we need to hit 192 */ if (multnow > 255) multnow = 255; offnow = pDetailedCalib[idx]; break; case DETAILED_PASS_GAINS_SECONDPASS: multnow = 255.0 / denom1 * contrast_adjust * pDetailedCalib[idx+1]; /* And finally to 255 */ offnow = pDetailedCalib[idx] - brightness_adjust * 0x80 / multnow; if (offnow < 0) { postprocess_offsets[ppidx] = multnow * offnow / 0x80; offnow = 0; needs_postprocessed_calibration = 1; } else if (offnow > 255) { postprocess_offsets[ppidx] = multnow * (offnow - 255) / 0x80; offnow = 255; needs_postprocessed_calibration = 1; } else { postprocess_offsets[ppidx] = 0; } if (multnow > 255) { postprocess_gains[ppidx] = multnow / 255; multnow = 255; needs_postprocessed_calibration = 1; } else { postprocess_gains[ppidx] = 1.0; } break; } } if (offnow > 255) offnow = 255; for (k = 0; k < resolution_divisor; ++k) { pDetailedCalib[idx++] = offnow; /* Subtract this value from the result at gains = 0x80*/ pDetailedCalib[idx++] = multnow; /* Then multiply by this value divided by 0x80 */ } ++ppidx; } } if (pass == DETAILED_PASS_GAINS_SECONDPASS) { /* Build gamma table */ unsigned char *redgamma = pDetailedCalib; unsigned char *greengamma = redgamma + 512; unsigned char *bluegamma = greengamma + 512; double val; double invgamma = 1.0l / gamma; *redgamma++ = *bluegamma++ = *greengamma++ = 0; /* The windows driver does a linear interpolation for the next 19 boundaries */ val = pow (20.0l / 255, invgamma) * 255; for (j = 1; j <= 20; ++j) { *redgamma++ = *bluegamma++ = *greengamma++ = val * j / 20 + 0.5; *redgamma++ = *bluegamma++ = *greengamma++ = val * j / 20 + 0.5; } for (; j <= 255; ++j) { val = pow((double) j / 255, invgamma) * 255; *redgamma++ = *bluegamma++ = *greengamma++ = val + 0.5; *redgamma++ = *bluegamma++ = *greengamma++ = val + 0.5; } *redgamma++ = *bluegamma++ = *greengamma++ = 255; } DBG (10, "\n"); rt_set_sram_page (0); rt_set_one_register (0x93, r93setting); rt_write_sram (calibration_size, pDetailedCalib); } /* And finally, perform the scan */ DBG (10, "Scanning\n"); rts8801_rewind (); rts8801_fullscan (x, y, w, h, resolution, resolution, colour, cbfunc, param, calib_info, 0, red_calibration_offset, green_calibration_offset, blue_calibration_offset, end_calibration_offset, needs_postprocessed_calibration ? postprocess_offsets : 0, needs_postprocessed_calibration ? postprocess_gains : 0); rt_turn_off_lamp (); rts8801_rewind (); rt_set_powersave_mode (1); if (pDetailedCalib) free (pDetailedCalib); if (detail_buffer) free (detail_buffer); if (tdetail_buffer) free(tdetail_buffer); if (postprocess_gains) free(postprocess_gains); if (postprocess_offsets) free(postprocess_offsets); return 0; } static int writefunc (struct hp3500_write_info *winfo, int bytes, char *data) { static int warned = 0; if (bytes > winfo->bytesleft) { if (!warned) { warned = 1; DBG (1, "Overflow protection triggered\n"); rt_stop_moving (); } bytes = winfo->bytesleft; if (!bytes) return 0; } winfo->bytesleft -= bytes; return write (winfo->scanner->pipe_w, data, bytes) == bytes; } #ifdef _POSIX_SOURCE static void sigtermHandler (int signal) { (void) signal; /* get rid of compiler warning */ cancelled_scan = 1; } #endif static int reader_process (void *pv) { struct hp3500_data *scanner = pv; time_t t; sigset_t ignore_set; sigset_t sigterm_set; struct SIGACTION act; struct hp3500_write_info winfo; int status; if (sanei_thread_is_forked ()) { close (scanner->pipe_r); sigfillset (&ignore_set); sigdelset (&ignore_set, SIGTERM); #if defined (__APPLE__) && defined (__MACH__) sigdelset (&ignore_set, SIGUSR2); #endif sigprocmask (SIG_SETMASK, &ignore_set, 0); sigemptyset (&sigterm_set); sigaddset (&sigterm_set, SIGTERM); memset (&act, 0, sizeof (act)); #ifdef _POSIX_SOURCE act.sa_handler = sigtermHandler; #endif sigaction (SIGTERM, &act, 0); } /* Warm up the lamp again if our last scan ended more than 5 minutes ago. */ time (&t); do_warmup = (t - scanner->last_scan) > 300; if (getenv ("HP3500_NOWARMUP") && atoi (getenv ("HP3500_NOWARMUP")) > 0) do_warmup = 0; udh = scanner->sfd; cancelled_scan = 0; winfo.scanner = scanner; winfo.bytesleft = scanner->bytes_per_scan_line * scanner->scan_height_pixels; if (getenv ("HP3500_SLEEP")) { int seconds = atoi (getenv ("HP3500_SLEEP")); DBG (1, "Backend process %d sleeping for %d seconds\n", getpid (), seconds); sleep (seconds); } DBG (10, "Scanning at %ddpi, mode=%s\n", scanner->resolution, scan_mode_list[scanner->mode]); if (rts8801_scan (scanner->actres_pixels.left + 250 * scanner->resolution / 1200, scanner->actres_pixels.top + 599 * scanner->resolution / 1200, scanner->actres_pixels.right - scanner->actres_pixels.left, scanner->actres_pixels.bottom - scanner->actres_pixels.top, scanner->resolution, scanner->mode, scanner->brightness, scanner->contrast, (rts8801_callback) writefunc, &winfo, scanner->gamma) >= 0) status = SANE_STATUS_GOOD; status = SANE_STATUS_IO_ERROR; close (scanner->pipe_w); return status; } static size_t max_string_size (char const **strings) { size_t size, max_size = 0; SANE_Int i; for (i = 0; strings[i]; ++i) { size = strlen (strings[i]) + 1; if (size > max_size) max_size = size; } return max_size; }