diff options
Diffstat (limited to 'backend/device.c')
-rw-r--r-- | backend/device.c | 563 |
1 files changed, 563 insertions, 0 deletions
diff --git a/backend/device.c b/backend/device.c new file mode 100644 index 0000000..13fdc65 --- /dev/null +++ b/backend/device.c @@ -0,0 +1,563 @@ +/* device.c -- physical device representation + * Copyright (C) 2008, 2009 SEIKO EPSON CORPORATION + * + * License: GPLv2+|iscan + * Authors: AVASYS CORPORATION + * + * This file is part of the SANE backend distributed with Image Scan! + * + * Image Scan!'s SANE backend 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 ought to have received a copy of the GNU General Public License + * along with this package. If not, see <http://www.gnu.org/licenses/>. + * + * + * Linking Image Scan!'s SANE backend statically or dynamically with + * other modules is making a combined work based on this SANE backend. + * Thus, the terms and conditions of the GNU General Public License + * cover the whole combination. + * + * As a special exception, the copyright holders of Image Scan!'s SANE + * backend give you permission to link Image Scan!'s SANE backend with + * SANE frontends that communicate with Image Scan!'s SANE backend + * solely through the SANE Application Programming Interface, + * regardless of the license terms of these SANE frontends, and to + * copy and distribute the resulting combined work under terms of your + * choice, provided that every copy of the combined work is + * accompanied by a complete copy of the source code of Image Scan!'s + * SANE backend (the version of Image Scan!'s SANE backend used to + * produce the combined work), being distributed under the terms of + * the GNU General Public License plus this exception. An independent + * module is a module which is not derived from or based on Image + * Scan!'s SANE backend. + * + * As a special exception, the copyright holders of Image Scan!'s SANE + * backend give you permission to link Image Scan!'s SANE backend with + * independent modules that communicate with Image Scan!'s SANE + * backend solely through the "Interpreter" interface, regardless of + * the license terms of these independent modules, and to copy and + * distribute the resulting combined work under terms of your choice, + * provided that every copy of the combined work is accompanied by a + * complete copy of the source code of Image Scan!'s SANE backend (the + * version of Image Scan!'s SANE backend used to produce the combined + * work), being distributed under the terms of the GNU General Public + * License plus this exception. An independent module is a module + * which is not derived from or based on Image Scan!'s SANE backend. + * + * Note that people who make modified versions of Image Scan!'s SANE + * backend are not obligated to grant special exceptions for their + * modified versions; it is their choice whether to do so. The GNU + * General Public License gives permission to release a modified + * version without this exception; this exception also makes it + * possible to release a modified version which carries forward this + * exception. + */ + + +/*! \file + \brief Implements a hardware device object. + + The hardware device object is built on top of the ESC/I commands. + Whereas the commands are only responsible for the I/O details and + the encoding and decoding of parameters, hardware device objects + handle the protocol logic and keep state. + */ + + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <errno.h> + +#include "command.h" +#include "hw-data.h" +#include "utils.h" + + +/*! Releases all resources associated with a scanner device. + * + * The channel will be closed if open and acquired memory is returned + * to the operating system. + */ +device * +dev_dtor (device *hw) +{ + if (!hw) return hw; + + hw->channel = hw->channel->dtor (hw->channel); + + delete (hw->fbf); + delete (hw->adf); + delete (hw->tpu); + delete (hw->fw_name); + if (hw->res_y.list != hw->res.list + && hw->res_y.list != hw->res_x.list) + { + delete (hw->res_y.list); + } + if (hw->res_x.list != hw->res.list) + { + delete (hw->res_x.list); + } + delete (hw->res.list); + delete (hw->resolution.list); + delete (hw); + + return hw; +} + +/*! Updates the extended status of a hardware device object. + */ +SANE_Status +dev_request_extended_status (device *hw) +{ + SANE_Status status = SANE_STATUS_GOOD; + + if (hw->using_fs) + { + status = cmd_request_scanner_status (hw); + if (SANE_STATUS_GOOD != status) return status; + + status = cmd_request_extended_identity (hw); + if (SANE_STATUS_GOOD != status) return status; + + status = cmd_request_scanner_status (hw); + } + else + status = cmd_request_extended_status (hw); + + return status; +} + +#include <string.h> +/*! Loads a sheet on a page type ADF extension. + */ +SANE_Status +dev_load_paper (device *hw) +{ + SANE_Status status = SANE_STATUS_GOOD; + + if (!hw->adf) return status; /* guard clauses */ + if (!(ADF_STATUS_IST & hw->adf->status)) return status; + if (!(ADF_STATUS_EN & hw->adf->status)) return status; + hw->adf->sheet_count++; + if (!(EXT_STATUS_ADFT & hw->ext_status)) return status; + + log_call (); + + status = cmd_load_paper (hw); + if (SANE_STATUS_GOOD != status) + { + hw->adf->sheet_count--; + return status; + } + log_info ("loaded sheet #%d", hw->adf->sheet_count); + + status = dev_request_extended_status (hw); + + if (ADF_STATUS_PE & hw->adf->status + && adf_early_paper_end_kills_scan (hw)) + { /* so we can scan the last sheet */ + cmd_control_option_unit (hw, 0x00); + hw->adf->status &= ~ ADF_STATUS_EN; + } + + /* Clear the ADF_STATUS_PE bit. We just successfully loaded a + sheet. Also update the ADF_STATUS_ERR bit. + */ + hw->adf->status &= ~ADF_STATUS_PE; + if ( (ADF_STATUS_PE & hw->adf->status) + || (ADF_STATUS_PJ & hw->adf->status) + || (ADF_STATUS_OPN & hw->adf->status)) + hw->adf->status |= ADF_STATUS_ERR; + else + hw->adf->status &= ~ADF_STATUS_ERR; + + return status; +} + +/*! Ejects sheets from the ADF extension. + */ +SANE_Status +dev_eject_paper (device *hw) +{ + SANE_Status status = SANE_STATUS_GOOD; + + if (!hw->adf) return status; /* guard clauses */ + if (!(ADF_STATUS_IST & hw->adf->status)) return status; + if (!(ADF_STATUS_EN & hw->adf->status)) return status; + + log_call (); + + status = cmd_eject_paper (hw); + hw->adf->sheet_count = 0; + + return status; +} + +/*! Open the scanner device. Depending on the connection method, + * different open functions are called. + */ +SANE_Status +dev_open (device *hw) +{ + SANE_Status status = SANE_STATUS_GOOD; + + log_call (); + require (hw->channel); + + if (hw->channel->is_open (hw->channel)) + { + log_info ("scanner is already open: fd = %d", hw->channel->fd); + return SANE_STATUS_GOOD; /* no need to open the scanner */ + } + + hw->channel->open (hw->channel, &status); + + return status; +} + +/*! Log extended (FS W) parameter settings + */ +SANE_Status +dev_log_scanning_parameter (device *hw) +{ + SANE_Status status = SANE_STATUS_GOOD; + + log_call (); + + byte *buf = hw->param_buf; /* for convenience */ + + log_info ("SANE_START: Color: %d", (int) buf[24]); + log_info ("SANE_START: Resolution (x, y): (%u, %u)", + buf_to_uint32 (buf+0), buf_to_uint32 (buf+4)); + log_info ("SANE_START: Scan offset (x, y): (%d, %d)", + buf_to_uint32 (buf+8), buf_to_uint32 (buf+12)); + log_info ("SANE_START: Scan size (w, h): (%d, %d)", + buf_to_uint32 (buf+16), buf_to_uint32 (buf+20)); + log_info ("SANE_START: Data format: %d", (int) buf[25]); + log_info ("SANE_START: Halftone: %d", (int) buf[32]); + log_info ("SANE_START: Brightness: %d", (int) buf[30]); + log_info ("SANE_START: Gamma: %d", (int) buf[29]); + log_info ("SANE_START: Color correction: %d", (int) buf[31]); + log_info ("SANE_START: Sharpness control: %d", (int) buf[35]); + log_info ("SANE_START: Scanning mode: %d", (int) buf[27]); + log_info ("SANE_START: Mirroring: %d", (int) buf[36]); + log_info ("SANE_START: Auto area segmentation: %d", (int) buf[34]); + log_info ("SANE_START: Threshold: %d", (int) buf[33]); + log_info ("SANE_START: Line counter: %d", (int) buf[28]); + log_info ("SANE_START: Option unit control: %d", (int) buf[26]); + log_info ("SANE_START: Film type: %d", (int) buf[37]); + + return status; +} + +/*! Obtain the offset and size of the parameter corresponding + * to the given ESC command in the FS W packet structure. + * Return true if the given command is in the FS W packet + * structure and obtaining the parameter info succeeded, + * otherwise return false. + */ +static bool +get_extended_param_info (byte cmd, short *offset, short *size) +{ + if (NULL == offset) return false; + if (NULL == size) return true; + + *offset = 0; + *size = 1; /* default size of most parameters */ + + if ('R' == cmd) { *offset = 0; *size = 8; } + else if ('A' == cmd) { *offset = 8; *size = 16; } + else if ('C' == cmd) *offset = 24; + else if ('D' == cmd) *offset = 25; + else if ('e' == cmd) *offset = 26; + else if ('g' == cmd) *offset = 27; + else if ('d' == cmd) *offset = 28; + else if ('Z' == cmd) *offset = 29; + else if ('L' == cmd) *offset = 30; + else if ('M' == cmd) *offset = 31; + else if ('B' == cmd) *offset = 32; + else if ('t' == cmd) *offset = 33; + else if ('s' == cmd) *offset = 34; + else if ('Q' == cmd) *offset = 35; + else if ('K' == cmd) *offset = 36; + else if ('N' == cmd) *offset = 37; + else *size = 0; + + if (0 == *size) return false; + return true; +} + +/*! Copies a number of bytes from \a param into the FS W packet buffer. + * The size and offset are looked up based on the \a cmd passed. + */ +SANE_Status +dev_set_scanning_parameter (device *hw, byte cmd, const byte* param) +{ + short offset = 0; + short size = 0; + + log_call (); + require (hw); + + if (NULL == param) return SANE_STATUS_INVAL; + if (!get_extended_param_info (cmd, &offset, &size)) return SANE_STATUS_INVAL; + + memcpy (hw->param_buf + offset, param, size); + + return SANE_STATUS_GOOD; +} + +SANE_Status +dev_set_scanning_resolution (device *hw, SANE_Int x_dpi, SANE_Int y_dpi) +{ + byte buf[8]; + uint32_t x; + uint32_t y; + + log_call (); + require (hw); + + if (x_dpi < 0 || x_dpi > UINT32_MAX) return SANE_STATUS_INVAL; + if (y_dpi < 0 || y_dpi > UINT32_MAX) return SANE_STATUS_INVAL; + + x = x_dpi; + y = y_dpi; + + uint32_to_buf (x, buf + 0); + uint32_to_buf (y, buf + 4); + + return dev_set_scanning_parameter (hw, 'R', buf); +} + +SANE_Status +dev_set_scanning_area (device *hw, SANE_Int left, SANE_Int top, + SANE_Int width, SANE_Int height) +{ + byte buf[16]; + uint32_t nx; + uint32_t ny; + uint32_t nw; + uint32_t nh; + + log_call (); + require (hw); + + if (left > UINT32_MAX) return SANE_STATUS_INVAL; + if (top > UINT32_MAX) return SANE_STATUS_INVAL; + if (width > UINT32_MAX) return SANE_STATUS_INVAL; + if (height > UINT32_MAX) return SANE_STATUS_INVAL; + + nx = left; + ny = top; + nw = width; + nh = height; + + uint32_to_buf (nx, buf + 0); + uint32_to_buf (ny, buf + 4); + uint32_to_buf (nw, buf + 8); + uint32_to_buf (nh, buf + 12); + + return dev_set_scanning_parameter (hw, 'A', buf); +} + +SANE_Status +dev_set_option_unit (device *hw, byte adf_mode) +{ + byte val = 0; + + log_call (); + require (hw); + + val = (using (hw, fbf) ? 0 : 1); + if (hw->adf && 1 == val) + val += adf_mode; /* set simplex/duplex */ + + return dev_set_scanning_parameter (hw, 'e', &val); +} + +static void +limit_res_list (resolution_info *res, int limit) +{ + int i = 0; + int new_size = 0; + + for(i=1; i<res->size; i++) // first entry is the list size, skip it + { + if (res->list[i] > limit) break; + ++new_size; + } + + res->list[0] = new_size; + res->size = new_size; + res->last = 0; + + log_info ("Limit resolution to %ddpi", res->list[res->size]); +} + +void +dev_limit_res (device *self, SANE_Constraint_Type type, int limit) +{ + if (SANE_CONSTRAINT_RANGE == type) + { + self->old_max = self->dpi_range.max; + self->dpi_range.max = limit; + } + else + { + self->old_max = self->res.size; + limit_res_list (&self->res, limit); + limit_res_list (&self->res_x, limit); + limit_res_list (&self->res_y, limit); + } +} + +void +dev_restore_res (device *self, SANE_Constraint_Type type) +{ + if (0 == self->old_max) return; + + if (SANE_CONSTRAINT_RANGE == type) + { + self->dpi_range.max = self->old_max; + } + else + { + // res_x and res_y are recreated, only restore res + self->res.size = self->old_max; + self->res.list[0] = self->old_max; + self->res.last = 0; + } +} + +/*! Sends a cancel to the device when not in the middle of retrieving scan data. + Return true if a cancel command was successfully sent, false, if not. + */ +bool +dev_force_cancel (device *self) +{ + u_char buf[14]; + u_char params[2]; + u_char *dummy = NULL; + uint32_t block_size = 0; + SANE_Status status = SANE_STATUS_GOOD; + + if (!self->using_fs) return false; + + params[0] = FS; + params[1] = self->cmd->start_scanning; + + channel_send (self->channel, params, 2, &status); + if (SANE_STATUS_GOOD != status) return false; + + channel_recv (self->channel, buf, num_of (buf), &status); + if (SANE_STATUS_GOOD != status) return false; + if (STX != buf[0]) return false; + + block_size = buf_to_uint32 (buf + 2); + + dummy = t_malloc (block_size, u_char); + if (dummy == NULL) + { + err_fatal ("%s", strerror (errno)); + return false; + } + + channel_recv_all (self->channel, dummy, + block_size, &status); + delete (dummy); + + if (SANE_STATUS_GOOD != status) return false; + + buf[0] = CAN; + channel_send (self->channel, buf, 1, &status); + if (SANE_STATUS_GOOD != status) return false; + + channel_recv (self->channel, buf, 1, &status); + if (SANE_STATUS_GOOD != status) return false; + if (ACK != buf[0]) return false; + + return true; +} + +SANE_Status +dev_lock (device *hw) +{ + SANE_Status status = SANE_STATUS_GOOD; + + log_call (); + require (hw); + + if (!hw->uses_locking) return status; + if ( hw->is_locked) return status; + + return cmd_lock (hw); +} + +SANE_Status +dev_unlock (device *hw) +{ + SANE_Status status = SANE_STATUS_GOOD; + + log_call (); + require (hw); + + if(!hw->uses_locking) return status; + if(!hw->is_locked) return status; + + return cmd_unlock (hw); +} + +static SANE_Status +dev_maintenance (device *hw, uint16_t mode) +{ + SANE_Status status = SANE_STATUS_GOOD; + + log_call ("(%04x)", mode); + require (hw); + if (!maintenance_is_supported (hw)) + { + return SANE_STATUS_UNSUPPORTED; + } + if ( ESC1_REQ_CLEANING != mode + && ESC1_REQ_CALIBRATION != mode) + { + return SANE_STATUS_INVAL; + } + + status = cmd_request_scanner_maintenance (hw, mode); + if (SANE_STATUS_GOOD == status) + { + do + { + microsleep (hw->polling_time); + status = cmd_request_scanner_maintenance (hw, ESC1_REQ_STATUS); + } while (SANE_STATUS_DEVICE_BUSY == status); + } + + return status; +} + +/* convenience function */ +SANE_Status +dev_clean (device *hw) +{ + return dev_maintenance (hw, ESC1_REQ_CLEANING); +} + +/* convenience function */ +SANE_Status +dev_calibrate (device *hw) +{ + return dev_maintenance (hw, ESC1_REQ_CALIBRATION); +} |