kopia lustrzana https://gitlab.com/sane-project/backends
328 wiersze
9.8 KiB
C
328 wiersze
9.8 KiB
C
![]() |
/* ---------------------------------------------------------------------- */
|
||
|
|
||
|
/* sane - Scanner Access Now Easy.
|
||
|
|
||
|
umax-usb.c
|
||
|
|
||
|
(C) 2001-2002 Frank Zago
|
||
|
|
||
|
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, write to the Free Software
|
||
|
Foundation, Inc., 59 Temple Place - Suite 330, Boston,
|
||
|
MA 02111-1307, USA.
|
||
|
|
||
|
As a special exception, the authors of SANE give permission for
|
||
|
additional uses of the libraries contained in this release of SANE.
|
||
|
|
||
|
The exception is that, if you link a SANE library with other files
|
||
|
to produce an executable, this does not by itself cause the
|
||
|
resulting executable to be covered by the GNU General Public
|
||
|
License. Your use of that executable is in no way restricted on
|
||
|
account of linking the SANE library code into it.
|
||
|
|
||
|
This exception does not, however, invalidate any other reasons why
|
||
|
the executable file might be covered by the GNU General Public
|
||
|
License.
|
||
|
|
||
|
If you submit changes to SANE to the maintainers to be included in
|
||
|
a subsequent release, you agree by submitting the changes that
|
||
|
those changes may be distributed with this exception intact.
|
||
|
|
||
|
If you write modifications of your own for SANE, it is your choice
|
||
|
whether to permit this exception to apply to your modifications.
|
||
|
If you do not wish that, delete this exception notice.
|
||
|
|
||
|
This file implements a SANE backend for UMAX USB flatbed scanners. */
|
||
|
|
||
|
|
||
|
/* ---------------------------------------------------------------------- */
|
||
|
|
||
|
#include "../include/sane/sanei_usb.h"
|
||
|
|
||
|
#include "sane/sanei_pv8630.h"
|
||
|
|
||
|
/* USB specific parts */
|
||
|
|
||
|
/* Apparently this will recover from some errors. */
|
||
|
static void pv8630_mini_init_scanner(int fd)
|
||
|
{
|
||
|
DBG(DBG_info, "mini_init_scanner\n");
|
||
|
|
||
|
/* (re-)init the device (?) */
|
||
|
sanei_pv8630_write_byte(fd, PV8630_UNKNOWN, 0x04 );
|
||
|
sanei_pv8630_write_byte(fd, PV8630_RMODE, 0x02 );
|
||
|
sanei_pv8630_write_byte(fd, PV8630_RMODE, 0x02 );
|
||
|
|
||
|
sanei_pv8630_wait_byte(fd, PV8630_RSTATUS, 0xd0, 0xff, 1000);
|
||
|
}
|
||
|
|
||
|
/* Length of the CDB given the SCSI command. The last two are not
|
||
|
correct (vendor reserved). */
|
||
|
static u_char cdb_sizes[8] = {
|
||
|
6, 10, 10, 6, 16, 12, 0, 0
|
||
|
};
|
||
|
#define CDB_SIZE(opcode) cdb_sizes[(((opcode) >> 5) & 7)]
|
||
|
|
||
|
/* Sends a CDB to the scanner. Also sends the parameters and receives
|
||
|
* the data, if necessary. When this function returns with a
|
||
|
* SANE_STATUS_GOOD, the SCSI command has been completed.
|
||
|
*
|
||
|
* Note: I don't know about deferred commands.
|
||
|
*/
|
||
|
static SANE_Status sanei_umaxusb_cmd(int fd, const void *src, size_t src_size, void *dst, size_t * dst_size)
|
||
|
{
|
||
|
unsigned char result;
|
||
|
size_t cmd_size = CDB_SIZE (*(char *) src);
|
||
|
size_t param_size = src_size - cmd_size;
|
||
|
char * param_ptr = ((char *) src) + cmd_size;
|
||
|
size_t tmp_len;
|
||
|
|
||
|
DBG(DBG_info, "Sending SCSI cmd 0x%02x cdb len %d, param len %d, result len %d\n", ((unsigned char *)src)[0], cmd_size, param_size, dst_size? *dst_size:0);
|
||
|
|
||
|
/* This looks like some kinf of pre-initialization. */
|
||
|
sanei_pv8630_write_byte(fd, PV8630_UNKNOWN, 0x0c);
|
||
|
sanei_pv8630_wait_byte(fd, PV8630_RSTATUS, 0xf0, 0xff, 1000);
|
||
|
sanei_pv8630_write_byte(fd, PV8630_UNKNOWN, 0x04);
|
||
|
|
||
|
/* Send the CDB and check it's been received OK. */
|
||
|
sanei_pv8630_write_byte(fd, PV8630_RMODE, 0x16);
|
||
|
sanei_pv8630_flush_buffer(fd);
|
||
|
sanei_pv8630_prep_bulkwrite(fd, cmd_size);
|
||
|
|
||
|
tmp_len = cmd_size;
|
||
|
sanei_pv8630_bulkwrite(fd, src, &tmp_len);
|
||
|
sanei_pv8630_wait_byte(fd, PV8630_RSTATUS, 0xf8, 0xff, 1000);
|
||
|
|
||
|
sanei_pv8630_flush_buffer(fd);
|
||
|
sanei_pv8630_prep_bulkread(fd, 1);
|
||
|
|
||
|
result = 0xA5; /* to be sure */
|
||
|
tmp_len = 1;
|
||
|
sanei_pv8630_bulkread(fd, &result, &tmp_len);
|
||
|
if (result != 0) {
|
||
|
DBG(DBG_info, "error in sanei_pv8630_bulkread (got %02x)\n", result);
|
||
|
if (result == 8) {
|
||
|
pv8630_mini_init_scanner(fd);
|
||
|
}
|
||
|
return(SANE_STATUS_IO_ERROR);
|
||
|
}
|
||
|
|
||
|
/* Send the parameters and check they've been received OK. */
|
||
|
if (param_size) {
|
||
|
sanei_pv8630_flush_buffer(fd);
|
||
|
sanei_pv8630_prep_bulkwrite(fd, param_size);
|
||
|
|
||
|
tmp_len = param_size;
|
||
|
sanei_pv8630_bulkwrite(fd, param_ptr, &tmp_len);
|
||
|
sanei_pv8630_wait_byte(fd, PV8630_RSTATUS, 0xf8, 0xff, 1000);
|
||
|
|
||
|
sanei_pv8630_flush_buffer(fd);
|
||
|
sanei_pv8630_prep_bulkread(fd, 1);
|
||
|
|
||
|
result = 0xA5; /* to be sure */
|
||
|
tmp_len = 1;
|
||
|
sanei_pv8630_bulkread(fd, &result, &tmp_len);
|
||
|
if (result != 0) {
|
||
|
DBG(DBG_info, "error in sanei_pv8630_bulkread (got %02x)\n", result);
|
||
|
if (result == 8) {
|
||
|
pv8630_mini_init_scanner(fd);
|
||
|
}
|
||
|
return(SANE_STATUS_IO_ERROR);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* If the SCSI command expect a return, get it. */
|
||
|
if (dst_size != NULL && *dst_size != 0 && dst != NULL) {
|
||
|
sanei_pv8630_flush_buffer(fd);
|
||
|
sanei_pv8630_prep_bulkread(fd, *dst_size);
|
||
|
sanei_pv8630_bulkread(fd, dst, dst_size);
|
||
|
|
||
|
DBG(DBG_info, " SCSI cmd returned %d bytes\n", *dst_size);
|
||
|
|
||
|
sanei_pv8630_wait_byte(fd, PV8630_RSTATUS, 0xf8, 0xff, 1000);
|
||
|
|
||
|
sanei_pv8630_flush_buffer(fd);
|
||
|
sanei_pv8630_prep_bulkread(fd, 1);
|
||
|
|
||
|
result = 0x5A; /* just to be sure */
|
||
|
tmp_len = 1;
|
||
|
sanei_pv8630_bulkread(fd, &result, &tmp_len);
|
||
|
if (result != 0) {
|
||
|
DBG(DBG_info, "error in sanei_pv8630_bulkread (got %02x)\n", result);
|
||
|
if (result == 8) {
|
||
|
pv8630_mini_init_scanner(fd);
|
||
|
}
|
||
|
return(SANE_STATUS_IO_ERROR);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
sanei_pv8630_write_byte(fd, PV8630_UNKNOWN, 0x04);
|
||
|
sanei_pv8630_write_byte(fd, PV8630_RMODE, 0x02);
|
||
|
sanei_pv8630_write_byte(fd, PV8630_RMODE, 0x02);
|
||
|
sanei_pv8630_wait_byte(fd, PV8630_RSTATUS, 0xd0, 0xff, 1000);
|
||
|
|
||
|
DBG(DBG_info, " SCSI command successfully executed\n");
|
||
|
|
||
|
return(SANE_STATUS_GOOD);
|
||
|
}
|
||
|
|
||
|
/* Initialize the PowerVision 8630. */
|
||
|
static SANE_Status pv8630_init_umaxusb_scanner(int fd)
|
||
|
{
|
||
|
DBG(DBG_info, "Initializing the PV8630\n");
|
||
|
|
||
|
/* Init the device */
|
||
|
sanei_pv8630_write_byte(fd, PV8630_UNKNOWN, 0x04);
|
||
|
sanei_pv8630_write_byte(fd, PV8630_RMODE, 0x02);
|
||
|
sanei_pv8630_write_byte(fd, PV8630_RMODE, 0x02);
|
||
|
|
||
|
sanei_pv8630_wait_byte(fd, PV8630_RSTATUS, 0xd0, 0xff, 1000);
|
||
|
|
||
|
sanei_pv8630_write_byte(fd, PV8630_UNKNOWN, 0x0c);
|
||
|
sanei_pv8630_wait_byte(fd, PV8630_RSTATUS, 0xf0, 0xff, 1000);
|
||
|
|
||
|
sanei_pv8630_write_byte(fd, PV8630_UNKNOWN, 0x04);
|
||
|
sanei_pv8630_wait_byte(fd, PV8630_RSTATUS, 0xf0, 0xff, 1000);
|
||
|
|
||
|
sanei_pv8630_write_byte(fd, PV8630_UNKNOWN, 0x0c);
|
||
|
sanei_pv8630_wait_byte(fd, PV8630_RSTATUS, 0xf0, 0xff, 1000);
|
||
|
sanei_pv8630_wait_byte(fd, PV8630_RSTATUS, 0xf8, 0xff, 1000);
|
||
|
|
||
|
sanei_pv8630_write_byte(fd, PV8630_UNKNOWN, 0x04);
|
||
|
|
||
|
sanei_pv8630_write_byte(fd, PV8630_RMODE, 0x02);
|
||
|
sanei_pv8630_write_byte(fd, PV8630_RMODE, 0x02);
|
||
|
sanei_pv8630_wait_byte(fd, PV8630_RSTATUS, 0xd0, 0xff, 1000);
|
||
|
|
||
|
sanei_pv8630_write_byte(fd, PV8630_UNKNOWN, 0x0c);
|
||
|
sanei_pv8630_wait_byte(fd, PV8630_RSTATUS, 0xf0, 0xff, 1000);
|
||
|
|
||
|
sanei_pv8630_write_byte(fd, PV8630_UNKNOWN, 0x04);
|
||
|
|
||
|
sanei_pv8630_write_byte(fd, PV8630_RMODE, 0x16);
|
||
|
|
||
|
DBG(DBG_info, "PV8630 initialized\n");
|
||
|
|
||
|
return(SANE_STATUS_GOOD);
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* SCSI functions for the emulation.
|
||
|
*
|
||
|
* The following functions emulate their sanei_scsi_* counterpart.
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
|
||
|
/*
|
||
|
* sanei_umaxusb_req_wait() and sanei_umaxusb_req_enter()
|
||
|
*
|
||
|
* I don't know if it is possible to queue the reads to the
|
||
|
* scanner. So The queing is disabled. The performance does not seems
|
||
|
* to be bad anyway.
|
||
|
*/
|
||
|
|
||
|
static void *umaxusb_req_buffer; /* keep the buffer ptr as an ID */
|
||
|
|
||
|
static SANE_Status sanei_umaxusb_req_enter (int fd,
|
||
|
const void *src, size_t src_size,
|
||
|
void *dst, size_t * dst_size, void **idp)
|
||
|
{
|
||
|
umaxusb_req_buffer = *idp = dst;
|
||
|
return(sanei_umaxusb_cmd(fd, src, src_size, dst, dst_size));
|
||
|
}
|
||
|
|
||
|
static SANE_Status
|
||
|
sanei_umaxusb_req_wait (void *id)
|
||
|
{
|
||
|
if (id != umaxusb_req_buffer) {
|
||
|
DBG(DBG_info, "sanei_umaxusb_req_wait: AIE, invalid id\n");
|
||
|
return(SANE_STATUS_IO_ERROR);
|
||
|
}
|
||
|
return(SANE_STATUS_GOOD);
|
||
|
}
|
||
|
|
||
|
/* Open the device.
|
||
|
*/
|
||
|
static SANE_Status
|
||
|
sanei_umaxusb_open (const char *dev, int *fdp,
|
||
|
SANEI_SCSI_Sense_Handler handler, void *handler_arg)
|
||
|
{
|
||
|
handler = handler; /* silence gcc */
|
||
|
handler_arg = handler_arg; /* silence gcc */
|
||
|
|
||
|
*fdp = open (dev, O_RDWR | O_EXCL);
|
||
|
if (*fdp == -1) {
|
||
|
SANE_Status status = SANE_STATUS_INVAL;
|
||
|
|
||
|
if (errno == EACCES)
|
||
|
status = SANE_STATUS_ACCESS_DENIED;
|
||
|
|
||
|
DBG (1, "sanei_umaxusb_open: open of `%s' failed: %s\n",
|
||
|
dev, strerror (errno));
|
||
|
return status;
|
||
|
} else {
|
||
|
SANE_Word vendor;
|
||
|
SANE_Word product;
|
||
|
|
||
|
/* We have openned the device. Check that it is a USB scanner. */
|
||
|
if (sanei_usb_get_vendor_product (*fdp, &vendor, &product) != SANE_STATUS_GOOD) {
|
||
|
/* This is not a USB scanner, or SANE or the OS doesn't support it. */
|
||
|
close(*fdp);
|
||
|
*fdp = -1;
|
||
|
return SANE_STATUS_UNSUPPORTED;
|
||
|
}
|
||
|
|
||
|
/* So it's a scanner. Does this backend support it?
|
||
|
* Only the UMAX 2200 USB is currently supported. */
|
||
|
if ((vendor != 0x1606) || (product != 0x0230)) {
|
||
|
close(*fdp);
|
||
|
*fdp = -1;
|
||
|
return SANE_STATUS_UNSUPPORTED;
|
||
|
}
|
||
|
|
||
|
/* It's a good scanner. Initialize it.
|
||
|
*
|
||
|
* Note: pv8630_init_umaxusb_scanner() is for the UMAX
|
||
|
* 2200. Other UMAX scanner might need a different
|
||
|
* initialization routine. */
|
||
|
|
||
|
pv8630_init_umaxusb_scanner(*fdp);
|
||
|
}
|
||
|
|
||
|
return(SANE_STATUS_GOOD);
|
||
|
}
|
||
|
|
||
|
/* sanei_umaxusb_open_extended() is just a passthrough for sanei_umaxusb_open(). */
|
||
|
static SANE_Status
|
||
|
sanei_umaxusb_open_extended (const char *dev, int *fdp,
|
||
|
SANEI_SCSI_Sense_Handler handler, void *handler_arg, int *buffersize)
|
||
|
{
|
||
|
buffersize = buffersize;
|
||
|
return(sanei_umaxusb_open(dev, fdp, handler, handler_arg));
|
||
|
}
|
||
|
|
||
|
/* Close the scanner. */
|
||
|
static void
|
||
|
sanei_umaxusb_close (int fd)
|
||
|
{
|
||
|
close(fd);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|