2015-02-20 19:52:08 +00:00
|
|
|
/* sane - Scanner Access Now Easy.
|
|
|
|
|
|
|
|
pieusb_usb.c
|
|
|
|
|
|
|
|
Copyright (C) 2012-2015 Jan Vleeshouwers, Michael Rickmann, Klaus Kaempf
|
|
|
|
|
|
|
|
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
|
2021-02-12 08:41:38 +00:00
|
|
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
2015-02-20 19:52:08 +00:00
|
|
|
|
|
|
|
As a special exception, the authors of SANE give permission for
|
|
|
|
additional uses of the libraries contained in this release of SANE.
|
|
|
|
|
|
|
|
The exception is that, if you link a SANE library with other files
|
|
|
|
to produce an executable, this does not by itself cause the
|
|
|
|
resulting executable to be covered by the GNU General Public
|
|
|
|
License. Your use of that executable is in no way restricted on
|
|
|
|
account of linking the SANE library code into it.
|
|
|
|
|
|
|
|
This exception does not, however, invalidate any other reasons why
|
|
|
|
the executable file might be covered by the GNU General Public
|
|
|
|
License.
|
|
|
|
|
|
|
|
If you submit changes to SANE to the maintainers to be included in
|
|
|
|
a subsequent release, you agree by submitting the changes that
|
|
|
|
those changes may be distributed with this exception intact.
|
|
|
|
|
|
|
|
If you write modifications of your own for SANE, it is your choice
|
|
|
|
whether to permit this exception to apply to your modifications.
|
|
|
|
If you do not wish that, delete this exception notice. */
|
|
|
|
|
|
|
|
#define DEBUG_DECLARE_ONLY
|
|
|
|
#include "pieusb.h"
|
|
|
|
#include "pieusb_scancmd.h"
|
|
|
|
#include "pieusb_usb.h"
|
|
|
|
|
|
|
|
#include "../include/sane/sanei_usb.h"
|
|
|
|
#include <unistd.h> /* usleep */
|
|
|
|
#include <time.h> /* time */
|
|
|
|
|
|
|
|
/* USB functions */
|
|
|
|
|
|
|
|
static SANE_Status _ctrl_out_byte(SANE_Int device_number, SANE_Int port, SANE_Byte b);
|
|
|
|
static SANE_Status _bulk_size(SANE_Int device_number, unsigned int size);
|
|
|
|
static SANE_Status _ctrl_in_byte(SANE_Int device_number, SANE_Byte* b);
|
|
|
|
static SANE_Status _bulk_in(SANE_Int device_number, SANE_Byte* data, size_t *size);
|
|
|
|
static SANE_Status _ieee_command(SANE_Int device_number, SANE_Byte command);
|
|
|
|
|
|
|
|
/* Defines for use in USB functions */
|
|
|
|
|
|
|
|
#define REQUEST_TYPE_IN (USB_TYPE_VENDOR | USB_DIR_IN)
|
|
|
|
#define REQUEST_TYPE_OUT (USB_TYPE_VENDOR | USB_DIR_OUT)
|
|
|
|
#define REQUEST_REGISTER 0x0c
|
|
|
|
#define REQUEST_BUFFER 0x04
|
|
|
|
#define ANYINDEX 0x00 /* wIndex value for USB control transfer - value is irrelevant */
|
|
|
|
|
|
|
|
/* from libieee1284 */
|
|
|
|
#define C1284_NSTROBE 0x01
|
|
|
|
#define C1284_NINIT 0x04
|
|
|
|
|
|
|
|
/* usb via ieee1284 */
|
|
|
|
#define IEEE1284_ADDR 0x00
|
|
|
|
#define IEEE1284_RESET 0x30
|
|
|
|
#define IEEE1284_SCSI 0xe0
|
|
|
|
|
|
|
|
#define PORT_SCSI_SIZE 0x0082
|
|
|
|
#define PORT_SCSI_STATUS 0x0084
|
|
|
|
#define PORT_SCSI_CMD 0x0085
|
|
|
|
#define PORT_PAR_CTRL 0x0087 /* IEEE1284 parallel control */
|
|
|
|
#define PORT_PAR_DATA 0x0088 /* IEEE1284 parallel data */
|
|
|
|
|
|
|
|
/* see also: SCSI Status Codes http://www.t10.org/lists/2status.htm */
|
|
|
|
typedef enum {
|
|
|
|
USB_STATUS_OK = 0x00, /* ok */
|
|
|
|
USB_STATUS_READ = 0x01, /* read: send expected length, then read data */
|
|
|
|
USB_STATUS_CHECK = 0x02, /* check condition */
|
|
|
|
USB_STATUS_BUSY = 0x03, /* wait on usb */
|
|
|
|
USB_STATUS_AGAIN = 0x08, /* re-send scsi cmd */
|
|
|
|
USB_STATUS_FAIL = 0x88, /* ??? */
|
|
|
|
USB_STATUS_ERROR = 0xff /* usb i/o error */
|
|
|
|
} PIEUSB_USB_Status;
|
|
|
|
|
|
|
|
static PIEUSB_USB_Status _pieusb_scsi_command(SANE_Int device_number, SANE_Byte command[], SANE_Byte data[], SANE_Int size);
|
|
|
|
|
|
|
|
#define SENSE_CODE_WARMING_UP 4
|
|
|
|
|
|
|
|
/* Standard SCSI Sense codes*/
|
|
|
|
#define SCSI_NO_ADDITIONAL_SENSE_INFORMATION 0x00
|
|
|
|
|
|
|
|
struct code_text_t { int code; char *text; };
|
|
|
|
static struct code_text_t usb_code_text[] = {
|
|
|
|
{ 0x00, "Ok" },
|
|
|
|
{ 0x01, "Read" },
|
|
|
|
{ 0x02, "Check" },
|
|
|
|
{ 0x03, "Busy" },
|
|
|
|
{ 0x08, "Again" },
|
|
|
|
{ 0xff, "Error" },
|
|
|
|
{ -1, NULL }
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct code_text_t scsi_code_text[] = {
|
|
|
|
{ 0x00, "Test Unit Ready" }
|
|
|
|
,{ 0x01, "Calibrate" }
|
|
|
|
,{ 0x03, "Request Sense" }
|
|
|
|
,{ 0x04, "Format" }
|
|
|
|
,{ 0x08, "Read" }
|
|
|
|
,{ 0x0a, "Write" }
|
|
|
|
,{ 0x0f, "Get Param" }
|
|
|
|
,{ 0x10, "Mark" }
|
|
|
|
,{ 0x11, "Space" }
|
|
|
|
,{ 0x12, "Inquiry" }
|
|
|
|
,{ 0x15, "Mode Select" }
|
|
|
|
,{ 0x16, "Reserve Unit" }
|
|
|
|
,{ 0x18, "Copy" }
|
|
|
|
,{ 0x1a, "Mode Sense" }
|
|
|
|
,{ 0x1b, "Scan" }
|
|
|
|
,{ 0x1d, "Diagnose" }
|
|
|
|
,{ 0xa8, "Read Extended" }
|
|
|
|
,{ 0xd1, "Slide" }
|
|
|
|
,{ 0xd2, "Set Scan Head" }
|
|
|
|
,{ 0xd7, "Read Gain Offset" }
|
|
|
|
,{ 0xdc, "Write Gain Offset" }
|
|
|
|
,{ 0xdd, "Read State" }
|
|
|
|
,{ -1, NULL }
|
|
|
|
};
|
|
|
|
|
|
|
|
static char *
|
|
|
|
code_to_text(struct code_text_t *list, int code)
|
|
|
|
{
|
|
|
|
while (list && list->text) {
|
|
|
|
if (list->code == code)
|
|
|
|
return list->text;
|
|
|
|
list++;
|
|
|
|
}
|
|
|
|
return "**unknown**";
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Convert PIEUSB_Status to SANE_Status
|
|
|
|
*/
|
|
|
|
SANE_Status
|
|
|
|
sanei_pieusb_convert_status(PIEUSB_Status status)
|
|
|
|
{
|
|
|
|
return (SANE_Status)status;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* hex dump 'size' bytes starting at 'ptr'
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
_hexdump(char *msg, unsigned char *ptr, int size)
|
|
|
|
{
|
|
|
|
unsigned char *lptr = ptr;
|
|
|
|
int count = 0;
|
|
|
|
long start = 0;
|
|
|
|
long clipped = 0;
|
|
|
|
|
|
|
|
if (DBG_info_proc > DBG_LEVEL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (size > 127) {
|
|
|
|
clipped = size;
|
|
|
|
size = 128;
|
|
|
|
}
|
2017-06-26 09:01:34 +00:00
|
|
|
while (size-- > 0)
|
|
|
|
{
|
|
|
|
if ((count % 16) == 0)
|
|
|
|
{
|
|
|
|
fprintf (stderr, "%s\t%08lx:", msg?msg:"", start);
|
|
|
|
msg = NULL;
|
|
|
|
}
|
2015-02-20 19:52:08 +00:00
|
|
|
fprintf (stderr, " %02x", *ptr++);
|
|
|
|
count++;
|
|
|
|
start++;
|
|
|
|
if (size == 0)
|
|
|
|
{
|
|
|
|
while ((count % 16) != 0)
|
|
|
|
{
|
|
|
|
fprintf (stderr, " ");
|
|
|
|
count++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if ((count % 16) == 0)
|
|
|
|
{
|
|
|
|
fprintf (stderr, " ");
|
|
|
|
while (lptr < ptr)
|
|
|
|
{
|
|
|
|
unsigned char c = *lptr & 0x7f;
|
|
|
|
fprintf (stderr, "%c", ((c < 0x20)||(c == 0x7f)) ? '.' : c);
|
|
|
|
lptr++;
|
|
|
|
}
|
|
|
|
fprintf (stderr, "\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if ((count % 16) != 0)
|
|
|
|
fprintf (stderr, "\n");
|
|
|
|
if (clipped > 0)
|
2017-06-26 09:01:34 +00:00
|
|
|
fprintf (stderr, "\t%08lx bytes clipped\n", clipped);
|
2015-02-20 19:52:08 +00:00
|
|
|
|
|
|
|
fflush(stderr);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* =========================================================================
|
|
|
|
*
|
|
|
|
* USB functions
|
|
|
|
*
|
|
|
|
* ========================================================================= */
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Send a command to the device, retry 10 times if device is busy
|
|
|
|
* and return SENSE data in the sense fields of status if there is a CHECK
|
|
|
|
* CONDITION response from the command.
|
|
|
|
* If the REQUEST SENSE command fails, the SANE status code is unequal to
|
|
|
|
* PIEUSB_STATUS_GOOD and the sense fields are empty.
|
|
|
|
*
|
|
|
|
* @param device_number Device number
|
|
|
|
* @param command Command array
|
|
|
|
* @param data Input or output data buffer
|
|
|
|
* @param size Size of the data buffer
|
|
|
|
* @param status Pieusb_Command_Status
|
|
|
|
*/
|
|
|
|
|
|
|
|
PIEUSB_Status
|
|
|
|
sanei_pieusb_command(SANE_Int device_number, SANE_Byte command[], SANE_Byte data[], SANE_Int size)
|
|
|
|
{
|
|
|
|
#define MAXTIME 60 /* max 60 seconds */
|
|
|
|
time_t start;
|
|
|
|
SANE_Status sane_status;
|
|
|
|
PIEUSB_Status ret = PIEUSB_STATUS_DEVICE_BUSY;
|
|
|
|
SANE_Byte usbstat;
|
|
|
|
PIEUSB_USB_Status usb_status = USB_STATUS_AGAIN;
|
|
|
|
|
|
|
|
DBG (DBG_info_usb, "*** sanei_pieusb_command(%02x:%s): size 0x%02x\n", command[0], code_to_text (scsi_code_text, command[0]), size);
|
|
|
|
|
|
|
|
start = time(NULL);
|
|
|
|
while ((time(NULL)-start) < MAXTIME) {
|
|
|
|
DBG (DBG_info_usb, "\tsanei_pieusb_command loop, status %d:%s\n", usb_status, code_to_text (usb_code_text, usb_status));
|
|
|
|
if (usb_status == USB_STATUS_AGAIN) {
|
|
|
|
usb_status = _pieusb_scsi_command (device_number, command, data, size);
|
|
|
|
DBG (DBG_info_usb, "\t_pieusb_scsi_command returned %d:%s\n", usb_status, code_to_text (usb_code_text, usb_status));
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (usb_status == USB_STATUS_OK) {
|
|
|
|
ret = PIEUSB_STATUS_GOOD;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (usb_status == USB_STATUS_READ) {
|
|
|
|
DBG (DBG_error, "\tsanei_pieusb_command() 2nd STATUS_READ ?!\n");
|
|
|
|
ret = PIEUSB_STATUS_IO_ERROR;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (usb_status == USB_STATUS_CHECK) {
|
|
|
|
/* check condition */
|
|
|
|
struct Pieusb_Sense sense;
|
|
|
|
struct Pieusb_Command_Status senseStatus;
|
|
|
|
|
|
|
|
#define SCSI_REQUEST_SENSE 0x03
|
|
|
|
|
|
|
|
if (command[0] == SCSI_REQUEST_SENSE) {
|
|
|
|
DBG (DBG_error, "\tsanei_pieusb_command() recursive SCSI_REQUEST_SENSE\n");
|
|
|
|
ret = PIEUSB_STATUS_INVAL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* A check sense may be a busy state in disguise
|
|
|
|
* It is also practical to execute a request sense command by
|
|
|
|
* default. The calling function should interpret
|
|
|
|
* PIEUSB_STATUS_CHECK_SENSE as 'sense data available'. */
|
|
|
|
|
|
|
|
sanei_pieusb_cmd_get_sense (device_number, &sense, &senseStatus, &ret);
|
|
|
|
if (senseStatus.pieusb_status != PIEUSB_STATUS_GOOD) {
|
|
|
|
DBG (DBG_error, "\tsanei_pieusb_command(): CHECK CONDITION, but REQUEST SENSE fails\n");
|
|
|
|
ret = senseStatus.pieusb_status;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (usb_status == USB_STATUS_BUSY) {
|
|
|
|
/* wait on usb */
|
|
|
|
sane_status = _ctrl_in_byte (device_number, &usbstat);
|
|
|
|
if (sane_status != SANE_STATUS_GOOD) {
|
|
|
|
DBG (DBG_error, "\tpieusb_scsi_command() fails status in: %d\n", sane_status);
|
|
|
|
ret = PIEUSB_STATUS_IO_ERROR;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
usb_status = usbstat;
|
|
|
|
if (usb_status == USB_STATUS_AGAIN) {
|
|
|
|
sleep(1);
|
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (usb_status == USB_STATUS_AGAIN) {
|
|
|
|
/* re-send scsi cmd */
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (usb_status == USB_STATUS_FAIL) {
|
|
|
|
DBG (DBG_error, "\tsanei_pieusb_command() usb status again2\n");
|
|
|
|
usb_status = USB_STATUS_ERROR;
|
|
|
|
sanei_pieusb_usb_reset(device_number);
|
|
|
|
ret = PIEUSB_STATUS_IO_ERROR;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (usb_status == USB_STATUS_ERROR) {
|
|
|
|
sanei_pieusb_usb_reset(device_number);
|
|
|
|
ret = PIEUSB_STATUS_IO_ERROR;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
DBG (DBG_error, "\tsanei_pieusb_command() unhandled usb status 0x%02x\n", usb_status);
|
|
|
|
ret = PIEUSB_STATUS_IO_ERROR;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if ((time(NULL)-start) > MAXTIME) {
|
|
|
|
DBG (DBG_info_usb, "\tsanei_pieusb_command() timeout !\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
DBG (DBG_info_usb, "\tsanei_pieusb_command() finished with state %d\n", ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Reset IEEE1284 interface
|
|
|
|
*
|
|
|
|
* @param device_number Device number
|
|
|
|
* @returns SANE_Status
|
|
|
|
*/
|
|
|
|
|
|
|
|
SANE_Status
|
|
|
|
sanei_pieusb_usb_reset(SANE_Int device_number)
|
|
|
|
{
|
|
|
|
DBG (DBG_info_sane, "\tsanei_pieusb_usb_reset()\n");
|
|
|
|
return _ieee_command (device_number, IEEE1284_RESET);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* http://www.t10.org/lists/2sensekey.htm */
|
|
|
|
static struct code_text_t sense_code_text[] = {
|
|
|
|
{ SCSI_SENSE_NO_SENSE, "No Sense" },
|
|
|
|
{ SCSI_SENSE_RECOVERED_ERROR, "Recovered Error" },
|
|
|
|
{ SCSI_SENSE_NOT_READY, "Not Ready" },
|
|
|
|
{ SCSI_SENSE_MEDIUM_ERROR, "Medium Error" },
|
|
|
|
{ SCSI_SENSE_HARDWARE_ERROR, "Hardware Error" },
|
|
|
|
{ SCSI_SENSE_ILLEGAL_REQUEST, "Illegal Request" },
|
|
|
|
{ SCSI_SENSE_UNIT_ATTENTION, "Unit Attention" },
|
|
|
|
{ SCSI_SENSE_DATA_PROTECT, "Data Protect" },
|
|
|
|
{ SCSI_SENSE_BLANK_CHECK, "Blank Check" },
|
|
|
|
{ SCSI_SENSE_VENDOR_SPECIFIC, "Vendor Specific" },
|
|
|
|
{ SCSI_SENSE_COPY_ABORTED, "Copy Aborted" },
|
|
|
|
{ SCSI_SENSE_ABORTED_COMMAND, "Aborted Command" },
|
|
|
|
{ SCSI_SENSE_EQUAL, "Equal" },
|
|
|
|
{ SCSI_SENSE_VOLUME_OVERFLOW, "Volume Overflow" },
|
|
|
|
{ SCSI_SENSE_MISCOMPARE, "Miscompare" },
|
|
|
|
{ SCSI_SENSE_COMPLETED, "Completed" },
|
|
|
|
{ -1, NULL }
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return a textual description of the given sense code.
|
|
|
|
*
|
|
|
|
* See http://www.t10.org/lists/asc-num.txt
|
|
|
|
*
|
|
|
|
* @param sense
|
|
|
|
* @return description
|
|
|
|
*/
|
|
|
|
|
|
|
|
SANE_String
|
|
|
|
sanei_pieusb_decode_sense(struct Pieusb_Sense* sense, PIEUSB_Status *status)
|
|
|
|
{
|
|
|
|
SANE_Char* desc = malloc(200);
|
|
|
|
SANE_Char* ptr;
|
|
|
|
strcpy (desc, code_to_text (sense_code_text, sense->senseKey));
|
|
|
|
ptr = desc + strlen(desc);
|
|
|
|
|
|
|
|
switch (sense->senseKey) {
|
|
|
|
case SCSI_SENSE_NOT_READY:
|
|
|
|
if (sense->senseCode == SENSE_CODE_WARMING_UP && sense->senseQualifier == 1) {
|
|
|
|
strcpy (ptr, ": Logical unit is in the process of becoming ready");
|
|
|
|
*status = PIEUSB_STATUS_WARMING_UP;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
sprintf (ptr, ": senseCode 0x%02x, senseQualifier 0x%02x", sense->senseCode, sense->senseQualifier);
|
|
|
|
*status = PIEUSB_STATUS_INVAL;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case SCSI_SENSE_UNIT_ATTENTION:
|
|
|
|
if (sense->senseCode == 0x1a && sense->senseQualifier == 0) {
|
|
|
|
strcpy (ptr, ": Invalid field in parameter list");
|
|
|
|
*status = PIEUSB_STATUS_INVAL;
|
|
|
|
break;
|
|
|
|
} else if (sense->senseCode == 0x20 && sense->senseQualifier == 0) {
|
|
|
|
strcpy (ptr, ": Invalid command operation code");
|
|
|
|
*status = PIEUSB_STATUS_INVAL;
|
|
|
|
break;
|
|
|
|
} else if (sense->senseCode == 0x82 && sense->senseQualifier == 0) {
|
|
|
|
strcpy (ptr, ": Calibration disable not granted");
|
|
|
|
*status = PIEUSB_STATUS_MUST_CALIBRATE;
|
|
|
|
break;
|
|
|
|
} else if (sense->senseCode == 0x00 && sense->senseQualifier == 6) {
|
|
|
|
strcpy (ptr, ": I/O process terminated");
|
|
|
|
*status = PIEUSB_STATUS_IO_ERROR;
|
|
|
|
break;
|
|
|
|
} else if (sense->senseCode == 0x26 && sense->senseQualifier == 0x82) {
|
|
|
|
strcpy (ptr, ": MODE SELECT value invalid: resolution too high (vs)");
|
|
|
|
*status = PIEUSB_STATUS_INVAL;
|
|
|
|
break;
|
|
|
|
} else if (sense->senseCode == 0x26 && sense->senseQualifier == 0x83) {
|
|
|
|
strcpy (ptr, ": MODE SELECT value invalid: select only one color (vs)");
|
|
|
|
*status = PIEUSB_STATUS_INVAL;
|
|
|
|
break;
|
|
|
|
} else if (sense->senseCode == 0x26 && sense->senseQualifier == 0x83) {
|
|
|
|
strcpy (ptr, ": MODE SELECT value invalid: unsupported bit depth (vs)");
|
|
|
|
*status = PIEUSB_STATUS_INVAL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
/*fallthru*/
|
|
|
|
case SCSI_SENSE_NO_SENSE:
|
|
|
|
case SCSI_SENSE_RECOVERED_ERROR:
|
|
|
|
case SCSI_SENSE_MEDIUM_ERROR:
|
|
|
|
case SCSI_SENSE_HARDWARE_ERROR:
|
|
|
|
case SCSI_SENSE_ILLEGAL_REQUEST:
|
|
|
|
case SCSI_SENSE_DATA_PROTECT:
|
|
|
|
case SCSI_SENSE_BLANK_CHECK:
|
|
|
|
case SCSI_SENSE_VENDOR_SPECIFIC:
|
|
|
|
case SCSI_SENSE_COPY_ABORTED:
|
|
|
|
case SCSI_SENSE_ABORTED_COMMAND:
|
|
|
|
case SCSI_SENSE_EQUAL:
|
|
|
|
case SCSI_SENSE_VOLUME_OVERFLOW:
|
|
|
|
case SCSI_SENSE_MISCOMPARE:
|
|
|
|
case SCSI_SENSE_COMPLETED:
|
|
|
|
default:
|
|
|
|
sprintf (ptr, ": senseCode 0x%02x, senseQualifier 0x%02x", sense->senseCode, sense->senseQualifier);
|
|
|
|
*status = PIEUSB_STATUS_INVAL;
|
|
|
|
}
|
|
|
|
return desc;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Prepare IEEE1284 interface
|
|
|
|
* Issue one of IEEE1284_ADDR, IEEE1284_RESET, or IEEE1284_SCSI
|
|
|
|
*
|
|
|
|
* @param device_number Device number
|
|
|
|
* @param command - IEEE1284 command
|
|
|
|
* @returns SANE_Status
|
|
|
|
*/
|
|
|
|
|
|
|
|
static SANE_Status
|
|
|
|
_ieee_command(SANE_Int device_number, SANE_Byte command)
|
|
|
|
{
|
|
|
|
SANE_Status st;
|
|
|
|
static int sequence[] = { 0xff, 0xaa, 0x55, 0x00, 0xff, 0x87, 0x78 };
|
|
|
|
#define SEQUENCE_LEN 7
|
|
|
|
unsigned int i;
|
|
|
|
/* 2 x 4 + 3 bytes preceding command, then SCSI_COMMAND_LEN bytes command */
|
|
|
|
/* IEEE1284 command, see hpsj5s.c:cpp_daisy() */
|
|
|
|
for (i = 0; i < SEQUENCE_LEN; ++i) {
|
|
|
|
st = _ctrl_out_byte (device_number, PORT_PAR_DATA, sequence[i]);
|
|
|
|
if (st != SANE_STATUS_GOOD) {
|
|
|
|
DBG (DBG_error, "\t\t_ieee_command fails after %d bytes\n", i);
|
|
|
|
return st;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
st = _ctrl_out_byte (device_number, PORT_PAR_DATA, command);
|
|
|
|
if (st == SANE_STATUS_GOOD) {
|
|
|
|
usleep(3000); /* 3.000 usec -> 3 msec */
|
|
|
|
st = _ctrl_out_byte (device_number, PORT_PAR_CTRL, C1284_NINIT|C1284_NSTROBE); /* CTRL_VAL_FINAL */
|
|
|
|
if (st == SANE_STATUS_GOOD) {
|
|
|
|
st = _ctrl_out_byte (device_number, PORT_PAR_CTRL, C1284_NINIT);
|
|
|
|
if (st == SANE_STATUS_GOOD) {
|
|
|
|
st = _ctrl_out_byte (device_number, PORT_PAR_DATA, 0xff);
|
|
|
|
if (st != SANE_STATUS_GOOD) {
|
|
|
|
DBG (DBG_error, "\t\t_ieee_command fails to write final data\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
DBG (DBG_error, "\t\t_ieee_command fails to reset strobe\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
DBG (DBG_error, "\t\t_ieee_command fails to set strobe\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return st;
|
|
|
|
#undef SEQUENCE_LEN
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Send a command to the device.
|
|
|
|
* The command is a SCSI_COMMAND_LEN-byte array. The data-array is used for input and output.
|
|
|
|
* The sense-fields of Pieusb_Command_Status are cleared.
|
|
|
|
*
|
|
|
|
* @param device_number Device number
|
|
|
|
* @param command Command array
|
|
|
|
* @param data Input or output data buffer
|
|
|
|
* @param size Size of the data buffer
|
|
|
|
* @returns PIEUSB_SCSI_Status
|
|
|
|
*/
|
|
|
|
static PIEUSB_USB_Status
|
|
|
|
_pieusb_scsi_command(SANE_Int device_number, SANE_Byte command[], SANE_Byte data[], SANE_Int size)
|
|
|
|
{
|
|
|
|
SANE_Status st;
|
|
|
|
SANE_Byte usbstat;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
DBG (DBG_info_usb, "\t\t_pieusb_scsi_command(): %02x:%s\n", command[0], code_to_text (scsi_code_text, command[0]));
|
|
|
|
|
|
|
|
st = _ieee_command (device_number, IEEE1284_SCSI);
|
|
|
|
if (st != SANE_STATUS_GOOD) {
|
|
|
|
DBG (DBG_error, "\t\t_pieusb_scsi_command can't prep scsi cmd: %d\n", st);
|
|
|
|
return USB_STATUS_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* output command */
|
|
|
|
for (i = 0; i < SCSI_COMMAND_LEN; ++i) {
|
|
|
|
SANE_Status st;
|
|
|
|
st = _ctrl_out_byte (device_number, PORT_SCSI_CMD, command[i]);
|
|
|
|
if (st != SANE_STATUS_GOOD) {
|
|
|
|
DBG (DBG_error, "\t\t_pieusb_scsi_command fails command out, after %d bytes: %d\n", i, st);
|
|
|
|
return USB_STATUS_ERROR;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_hexdump ("Cmd", command, SCSI_COMMAND_LEN);
|
|
|
|
|
|
|
|
/* Verify this sequence */
|
|
|
|
st = _ctrl_in_byte (device_number, &usbstat);
|
|
|
|
if (st != SANE_STATUS_GOOD) {
|
|
|
|
DBG (DBG_error, "\t\t_pieusb_scsi_command fails status after command out: %d\n", st);
|
|
|
|
return USB_STATUS_ERROR;
|
|
|
|
}
|
|
|
|
/* Process rest of the data, if present; either input or output, possibly bulk */
|
|
|
|
DBG (DBG_info_usb, "\t\t_pieusb_scsi_command usbstat 0x%02x\n", usbstat);
|
|
|
|
if (usbstat == USB_STATUS_OK && size > 0) {
|
|
|
|
/*
|
|
|
|
* send additional data to usb
|
|
|
|
*/
|
|
|
|
_hexdump ("Out", data, size);
|
|
|
|
for (i = 0; i < size; ++i) {
|
|
|
|
st = _ctrl_out_byte (device_number, PORT_SCSI_CMD, data[i]);
|
|
|
|
if (st != SANE_STATUS_GOOD) {
|
|
|
|
DBG (DBG_error, "\t\t_pieusb_scsi_command fails data out after %d bytes: %d\n", i, st);
|
|
|
|
return USB_STATUS_ERROR;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* Verify data out */
|
|
|
|
st = _ctrl_in_byte (device_number, &usbstat);
|
|
|
|
if (st != SANE_STATUS_GOOD) {
|
|
|
|
DBG (DBG_error, "\t\t_pieusb_scsi_command fails status after data out: %d\n", st);
|
|
|
|
return USB_STATUS_ERROR;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (usbstat == USB_STATUS_READ) {
|
|
|
|
/* Intermediate status OK, device has made data available for reading */
|
|
|
|
/* Read data */
|
|
|
|
size_t remsize;
|
|
|
|
size_t partsize;
|
|
|
|
|
|
|
|
remsize = (size_t)size;
|
|
|
|
|
|
|
|
DBG (DBG_info_usb, "\t\t_pieusb_scsi_command data in\n");
|
|
|
|
while (remsize > 0) {
|
|
|
|
partsize = remsize > 0x1000000 ? 0x1000000 : remsize; /* 0xc000 must be multiples of 0x4000, see _bulk_in() */
|
|
|
|
/* send expected length */
|
|
|
|
st = _bulk_size (device_number, partsize);
|
|
|
|
if (st != SANE_STATUS_GOOD) {
|
|
|
|
DBG (DBG_error, "\t\t_pieusb_scsi_command prepare read data failed for size %u: %d\n", (unsigned int)partsize, st);
|
|
|
|
return USB_STATUS_ERROR;
|
|
|
|
}
|
|
|
|
/* read expected length bytes */
|
|
|
|
st = _bulk_in (device_number, data + size - remsize, &partsize);
|
|
|
|
if (st != SANE_STATUS_GOOD) {
|
|
|
|
DBG (DBG_error, "\t\t_pieusb_scsi_command read data failed for size %u: %d\n", (unsigned int)partsize, st);
|
|
|
|
return USB_STATUS_ERROR;
|
|
|
|
}
|
|
|
|
remsize -= partsize;
|
|
|
|
/* DBG (DBG_info, "\t\t_pieusb_scsi_command partsize %08x, remsize %08x\n", (unsigned int)partsize, (unsigned int)remsize); */
|
|
|
|
}
|
|
|
|
/* Verify data in */
|
|
|
|
st = _ctrl_in_byte (device_number, &usbstat);
|
|
|
|
if (st != SANE_STATUS_GOOD) {
|
|
|
|
DBG (DBG_error, "\t\t_pieusb_scsi_command fails status after data in: %d\n", st);
|
|
|
|
return USB_STATUS_ERROR;
|
|
|
|
}
|
|
|
|
_hexdump ("In", data, size);
|
|
|
|
}
|
|
|
|
|
|
|
|
return usbstat;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Simplified control transfer: one byte to given port
|
|
|
|
*
|
|
|
|
* @param device_number device number
|
|
|
|
* @param b byte to send to device
|
|
|
|
* @return SANE status
|
|
|
|
*/
|
|
|
|
static SANE_Status _ctrl_out_byte(SANE_Int device_number, SANE_Int port, SANE_Byte b) {
|
|
|
|
/* int r = libusb_control_transfer(scannerHandle, CTRL_OUT, 0x0C, 0x0088, ANYINDEX, &b, 1, TIMEOUT); */
|
|
|
|
return sanei_usb_control_msg(device_number, REQUEST_TYPE_OUT, REQUEST_REGISTER, port, ANYINDEX, 1, &b);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Simplified control transfer for port/wValue = 0x82 - prepare bulk
|
|
|
|
*
|
|
|
|
* @param device_number device number
|
|
|
|
* @param size Size of bulk transfer which follows (number of bytes)
|
|
|
|
* @return SANE status
|
|
|
|
*/
|
|
|
|
static SANE_Status _bulk_size(SANE_Int device_number, unsigned int size) {
|
|
|
|
SANE_Byte bulksize[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
|
|
|
bulksize[4] = size & 0xff;
|
|
|
|
bulksize[5] = (size >> 8) & 0xff;
|
|
|
|
bulksize[6] = (size >> 16) & 0xff;
|
|
|
|
bulksize[7] = (size >> 24) & 0xff;
|
|
|
|
return sanei_usb_control_msg(device_number, REQUEST_TYPE_OUT, REQUEST_BUFFER, PORT_SCSI_SIZE, ANYINDEX, 8, bulksize);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Ctrl inbound, single byte
|
|
|
|
*/
|
|
|
|
/**
|
|
|
|
* Inbound control transfer
|
|
|
|
*
|
|
|
|
* @param device_number device number
|
|
|
|
* @param b byte received from device
|
|
|
|
* @return SANE status
|
|
|
|
*/
|
|
|
|
static SANE_Status _ctrl_in_byte(SANE_Int device_number, SANE_Byte* b) {
|
|
|
|
/* int r = libusb_control_transfer(scannerHandle, CTRL_IN, 0x0C, 0x0084, ANYINDEX, &b, 1, TIMEOUT); */
|
|
|
|
/* int r = libusb_control_transfer(scannerHandle, CTRL_IN, 0x0C, 0x0084, ANYINDEX, &b, 1, TIMEOUT); */
|
|
|
|
return sanei_usb_control_msg(device_number, REQUEST_TYPE_IN, REQUEST_REGISTER, PORT_SCSI_STATUS, ANYINDEX, 1, b);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Bulk in transfer for data, in parts of 0x4000 bytes max
|
|
|
|
*
|
|
|
|
* @param device_number device number
|
|
|
|
* @param data array holding or receiving data (must be preallocated)
|
|
|
|
* @param size ptr to size of the data array / actual size on output
|
|
|
|
* @return SANE status
|
|
|
|
*/
|
|
|
|
static SANE_Status
|
|
|
|
_bulk_in(SANE_Int device_number, SANE_Byte *data, size_t *size) {
|
|
|
|
size_t remaining = 0;
|
|
|
|
SANE_Status r = SANE_STATUS_GOOD;
|
|
|
|
size_t part;
|
|
|
|
|
|
|
|
remaining = *size;
|
|
|
|
while (remaining > 0) {
|
|
|
|
/* Determine bulk size */
|
|
|
|
part = (remaining >= 0x4000) ? 0x4000 : remaining; /* max 16k per chunk */
|
|
|
|
/* DBG (DBG_info, "\t\t_bulk_in: %08x @ %p, %08x rem\n", (unsigned int)part, data, (unsigned int)remaining); */
|
|
|
|
r = sanei_usb_read_bulk(device_number, data, &part);
|
|
|
|
if (r != SANE_STATUS_GOOD) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
/* DBG (DBG_info, "\t\t_bulk_in: -> %d : %08x\n", r, (unsigned int)part);*/
|
|
|
|
remaining -= part;
|
|
|
|
data += part;
|
|
|
|
}
|
|
|
|
*size -= remaining;
|
|
|
|
return r;
|
|
|
|
}
|