diff options
author | Jörg Frings-Fürst <debian@jff-webhosting.net> | 2015-09-27 08:20:20 +0200 |
---|---|---|
committer | Jörg Frings-Fürst <debian@jff-webhosting.net> | 2015-09-27 08:20:20 +0200 |
commit | e94622c319e4307617b202f19d3ec05b21a9bb49 (patch) | |
tree | c5ddf44352b9bd47aeb3eb432ba79638079efe25 /backend/pieusb_usb.c | |
parent | 6f2255c9648b175d51e2c8e5e9670557f9dc4ee1 (diff) |
merge to 1.0.25+git20150927
Diffstat (limited to 'backend/pieusb_usb.c')
-rw-r--r-- | backend/pieusb_usb.c | 679 |
1 files changed, 679 insertions, 0 deletions
diff --git a/backend/pieusb_usb.c b/backend/pieusb_usb.c new file mode 100644 index 0000000..71b6bcf --- /dev/null +++ b/backend/pieusb_usb.c @@ -0,0 +1,679 @@ +/* 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 + 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. */ + +#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; + } + while (size-- > 0) + { + if ((count % 16) == 0) + fprintf (stderr, "%s\t%08lx:", msg?msg:"", start); + msg = NULL; + 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) + fprintf (stderr, "\t%08lx bytes clipped\n", clipped); + + 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; +} |