/* 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 .
*
*
* 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
#endif
#include
#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
/*! 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; isize; 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);
}