kopia lustrzana https://gitlab.com/sane-project/backends
953 wiersze
28 KiB
C
953 wiersze
28 KiB
C
/* sane - Scanner Access Now Easy.
|
|
Copyright (C) 2007 Ilia Sotnikov <hostcc@gmail.com>
|
|
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 is part of a SANE backend for HP 5550/5590/7650 Scanners
|
|
*/
|
|
|
|
#include "sane/config.h"
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#ifdef HAVE_NETINET_IN_H
|
|
# include <netinet/in.h>
|
|
#endif /* HAVE_NETINET_IN_H */
|
|
|
|
#include "sane/sanei_debug.h"
|
|
#include "sane/sanei_usb.h"
|
|
#include "hp5590_low.h"
|
|
|
|
/* Debug levels */
|
|
#define DBG_err 0
|
|
#define DBG_proc 10
|
|
#define DBG_usb 50
|
|
|
|
/* Custom assert() macro */
|
|
#define hp5590_low_assert(exp) if(!(exp)) { \
|
|
DBG (DBG_err, "Assertion '%s' failed at %s:%u\n", #exp, __FILE__, __LINE__);\
|
|
return SANE_STATUS_INVAL; \
|
|
}
|
|
|
|
/* Structure describing bulk transfer size */
|
|
struct bulk_size
|
|
{
|
|
u_int16_t size;
|
|
u_int8_t unused;
|
|
} __attribute__ ((packed));
|
|
|
|
/* Structure describing bulk URB */
|
|
/* FIXME: Verify according to USB standard */
|
|
struct usb_in_usb_bulk_setup
|
|
{
|
|
u_int8_t bRequestType;
|
|
u_int8_t bRequest;
|
|
u_int8_t bEndpoint;
|
|
u_int16_t unknown;
|
|
u_int16_t wLength;
|
|
u_int8_t pad;
|
|
} __attribute__ ((packed));
|
|
|
|
/* Structure describing control URB */
|
|
struct usb_in_usb_ctrl_setup {
|
|
u_int8_t bRequestType;
|
|
u_int8_t bRequest;
|
|
u_int16_t wValue;
|
|
u_int16_t wIndex;
|
|
u_int16_t wLength;
|
|
} __attribute__ ((packed));
|
|
|
|
/* CORE status flag - ready or not */
|
|
#define CORE_FLAG_NOT_READY 1 << 1
|
|
|
|
/* Bulk transfers are done in pages, below their respective sizes */
|
|
#define BULK_WRITE_PAGE_SIZE 0x0f000
|
|
#define BULK_READ_PAGE_SIZE 0x10000
|
|
#define ALLOCATE_BULK_READ_PAGES 16 /* 16 * 65536 = 1Mb */
|
|
|
|
/* Structure describing bulk read state, because bulk reads will be done in
|
|
* pages, but function caller uses its own buffer, whose size is certainly
|
|
* different. Also, each bulk read page is ACK'ed by special command
|
|
* so total pages received should be tracked as well
|
|
*/
|
|
struct bulk_read_state
|
|
{
|
|
unsigned char *buffer;
|
|
unsigned int buffer_size;
|
|
unsigned int bytes_available;
|
|
unsigned char *buffer_out_ptr;
|
|
unsigned char *buffer_in_ptr;
|
|
unsigned int total_pages;
|
|
unsigned char *buffer_end_ptr;
|
|
unsigned int initialized;
|
|
};
|
|
|
|
/*******************************************************************************
|
|
* USB-in-USB: get acknowledge for last USB-in-USB operation
|
|
*
|
|
* Parameters
|
|
* dn - sanei_usb device descriptor
|
|
*
|
|
* Returns
|
|
* SANE_STATUS_GOOD - if correct acknowledge was received
|
|
* SANE_STATUS_DEVICE_BUSY - otherwise
|
|
*/
|
|
static SANE_Status
|
|
hp5590_get_ack (SANE_Int dn)
|
|
{
|
|
u_int8_t status;
|
|
SANE_Status ret;
|
|
|
|
DBG (DBG_proc, "%s\n", __FUNCTION__);
|
|
|
|
/* Check if USB-in-USB operation was accepted */
|
|
ret = sanei_usb_control_msg (dn, USB_DIR_IN | USB_TYPE_VENDOR,
|
|
0x0c, 0x8e, 0x20,
|
|
sizeof (status), &status);
|
|
if (ret != SANE_STATUS_GOOD)
|
|
{
|
|
DBG (DBG_err, "%s: USB-in-USB: error getting acknowledge\n",
|
|
__FUNCTION__);
|
|
return ret;
|
|
}
|
|
|
|
DBG (DBG_usb, "%s: USB-in-USB: accepted\n", __FUNCTION__);
|
|
|
|
/* Check if we received correct acknowledgement */
|
|
if (status != 0x01)
|
|
{
|
|
DBG (DBG_err, "%s: USB-in-USB: not accepted (status %u)\n",
|
|
__FUNCTION__, status);
|
|
return SANE_STATUS_DEVICE_BUSY;
|
|
}
|
|
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
* USB-in-USB: get device status
|
|
*
|
|
* Parameters
|
|
* dn - sanei_usb device descriptor
|
|
*
|
|
* Returns
|
|
* SANE_STATUS_GOOD - if correct status was received
|
|
* SANE_STATUS_DEVICE_BUSY - otherwise
|
|
*/
|
|
static SANE_Status
|
|
hp5590_get_status (SANE_Int dn)
|
|
{
|
|
u_int8_t status;
|
|
SANE_Status ret;
|
|
|
|
DBG (DBG_proc, "%s\n", __FUNCTION__);
|
|
|
|
ret = sanei_usb_control_msg (dn, USB_DIR_IN | USB_TYPE_VENDOR,
|
|
0x0c, 0x8e, 0x20,
|
|
sizeof (status), &status);
|
|
if (ret != SANE_STATUS_GOOD)
|
|
{
|
|
DBG (DBG_err, "%s: USB-in-USB: error getting device status\n",
|
|
__FUNCTION__);
|
|
return ret;
|
|
}
|
|
|
|
/* Check if we received correct status */
|
|
if (status != 0x00)
|
|
{
|
|
DBG (DBG_err, "%s: USB-in-USB: got non-zero device status (status %u)\n",
|
|
__FUNCTION__, status);
|
|
return SANE_STATUS_DEVICE_BUSY;
|
|
}
|
|
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
* USB-in-USB: sends control message for IN or OUT operation
|
|
*
|
|
* Parameters
|
|
* dn - sanei_usb device descriptor
|
|
* requesttype, request, value, index - their meaninings are similar to
|
|
* sanei_control_msg()
|
|
* bytes - pointer to data buffer
|
|
* size - size of data
|
|
* core_flags -
|
|
* CORE_NONE - no CORE operation will be performed
|
|
* CORE_DATA - operation on CORE data will be performed
|
|
* CORE_BULK_IN - preparation for bulk IN transfer (not used yet)
|
|
* CORE_BULK_OUT - preparation for bulk OUT trasfer
|
|
*
|
|
* Returns
|
|
* SANE_STATUS_GOOD - control message was sent w/o any errors
|
|
* all other SANE_Status values - otherwise
|
|
*/
|
|
static SANE_Status
|
|
hp5590_control_msg (SANE_Int dn,
|
|
int requesttype, int request,
|
|
int value, int index, unsigned char *bytes,
|
|
int size, int core_flags)
|
|
{
|
|
struct usb_in_usb_ctrl_setup ctrl;
|
|
SANE_Status ret;
|
|
unsigned int len;
|
|
unsigned char *ptr;
|
|
u_int8_t ack;
|
|
u_int8_t response;
|
|
unsigned int needed_response;
|
|
|
|
DBG (DBG_proc, "%s: USB-in-USB: core data: %s\n",
|
|
__FUNCTION__, core_flags & CORE_DATA ? "yes" : "no");
|
|
|
|
hp5590_low_assert (bytes != NULL);
|
|
|
|
/* IN (read) operation will be performed */
|
|
if (requesttype & USB_DIR_IN)
|
|
{
|
|
/* Prepare USB-in-USB control message */
|
|
memset (&ctrl, 0, sizeof (ctrl));
|
|
ctrl.bRequestType = 0xc0;
|
|
ctrl.bRequest = request;
|
|
ctrl.wValue = htons (value);
|
|
ctrl.wIndex = htons (index);
|
|
ctrl.wLength = size;
|
|
|
|
DBG (DBG_usb, "%s: USB-in-USB: sending control msg\n", __FUNCTION__);
|
|
/* Send USB-in-USB control message */
|
|
ret = sanei_usb_control_msg (dn, USB_DIR_OUT | USB_TYPE_VENDOR,
|
|
0x04, 0x8f, 0x00,
|
|
sizeof (ctrl), (unsigned char *) &ctrl);
|
|
if (ret != SANE_STATUS_GOOD)
|
|
{
|
|
DBG (DBG_err, "%s: USB-in-USB: error sending control message\n",
|
|
__FUNCTION__);
|
|
return ret;
|
|
}
|
|
|
|
DBG (DBG_usb, "%s: USB-in-USB: checking acknowledge for control message\n",
|
|
__FUNCTION__);
|
|
ret = hp5590_get_ack (dn);
|
|
if (ret != SANE_STATUS_GOOD)
|
|
return ret;
|
|
|
|
len = size;
|
|
ptr = bytes;
|
|
/* Data is read in 8 byte portions */
|
|
while (len)
|
|
{
|
|
unsigned int next_packet_size;
|
|
next_packet_size = 8;
|
|
if (len < 8)
|
|
next_packet_size = len;
|
|
|
|
/* Read USB-in-USB data */
|
|
ret = sanei_usb_control_msg (dn, USB_DIR_IN | USB_TYPE_VENDOR,
|
|
core_flags & CORE_DATA ? 0x0c : 0x04,
|
|
0x90, 0x00, next_packet_size, ptr);
|
|
if (ret != SANE_STATUS_GOOD)
|
|
{
|
|
DBG (DBG_err, "%s: USB-in-USB: error reading data\n", __FUNCTION__);
|
|
return ret;
|
|
}
|
|
|
|
ptr += next_packet_size;
|
|
len -= next_packet_size;
|
|
}
|
|
|
|
/* Confirm data reception */
|
|
ack = 0;
|
|
ret = sanei_usb_control_msg (dn, USB_DIR_OUT | USB_TYPE_VENDOR,
|
|
0x0c, 0x8f, 0x00,
|
|
sizeof (ack), &ack);
|
|
if (ret != SANE_STATUS_GOOD)
|
|
{
|
|
DBG (DBG_err, "%s: USB-in-USB: error confirming data reception\n",
|
|
__FUNCTION__);
|
|
return -1;
|
|
}
|
|
|
|
DBG (DBG_usb, "%s: USB-in-USB: checking if confirmation was acknowledged\n",
|
|
__FUNCTION__);
|
|
ret = hp5590_get_ack (dn);
|
|
if (ret != SANE_STATUS_GOOD)
|
|
return ret;
|
|
}
|
|
|
|
/* OUT (write) operation will be performed */
|
|
if (!(requesttype & USB_DIR_IN))
|
|
{
|
|
/* Prepare USB-in-USB control message */
|
|
memset (&ctrl, 0, sizeof (ctrl));
|
|
ctrl.bRequestType = 0x40;
|
|
ctrl.bRequest = request;
|
|
ctrl.wValue = htons (value);
|
|
ctrl.wIndex = htons (index);
|
|
ctrl.wLength = size;
|
|
|
|
DBG (DBG_usb, "%s: USB-in-USB: sending control msg\n", __FUNCTION__);
|
|
/* Send USB-in-USB control message */
|
|
ret = sanei_usb_control_msg (dn, USB_DIR_OUT | USB_TYPE_VENDOR,
|
|
0x04, 0x8f, 0x00,
|
|
sizeof (ctrl), (unsigned char *) &ctrl);
|
|
if (ret != SANE_STATUS_GOOD)
|
|
{
|
|
DBG (DBG_err, "%s: USB-in-USB: error sending control message\n",
|
|
__FUNCTION__);
|
|
return ret;
|
|
}
|
|
|
|
DBG (DBG_usb, "%s: USB-in-USB: checking acknowledge for control message\n",
|
|
__FUNCTION__);
|
|
ret = hp5590_get_ack (dn);
|
|
if (ret != SANE_STATUS_GOOD)
|
|
return ret;
|
|
|
|
len = size;
|
|
ptr = bytes;
|
|
/* Data is sent in 8 byte portions */
|
|
while (len)
|
|
{
|
|
unsigned int next_packet_size;
|
|
next_packet_size = 8;
|
|
if (len < 8)
|
|
next_packet_size = len;
|
|
|
|
/* Send USB-in-USB data */
|
|
ret = sanei_usb_control_msg (dn, USB_DIR_OUT | USB_TYPE_VENDOR,
|
|
core_flags & CORE_DATA ? 0x04 : 0x0c,
|
|
0x8f, 0x00, next_packet_size, ptr);
|
|
if (ret != SANE_STATUS_GOOD)
|
|
{
|
|
DBG (DBG_err, "%s: USB-in-USB: error sending data\n", __FUNCTION__);
|
|
return ret;
|
|
}
|
|
|
|
/* CORE data is acknowledged packet by packet */
|
|
if (core_flags & CORE_DATA)
|
|
{
|
|
DBG (DBG_usb, "%s: USB-in-USB: checking if data was accepted\n",
|
|
__FUNCTION__);
|
|
ret = hp5590_get_ack (dn);
|
|
if (ret != SANE_STATUS_GOOD)
|
|
return ret;
|
|
}
|
|
|
|
ptr += next_packet_size;
|
|
len -= next_packet_size;
|
|
}
|
|
|
|
/* Normal (non-CORE) data is acknowledged after its full transmission */
|
|
if (!(core_flags & CORE_DATA))
|
|
{
|
|
DBG (3, "%s: USB-in-USB: checking if data was accepted\n",
|
|
__FUNCTION__);
|
|
ret = hp5590_get_ack (dn);
|
|
if (ret != SANE_STATUS_GOOD)
|
|
return ret;
|
|
}
|
|
|
|
/* Getting response after data transmission */
|
|
DBG (DBG_usb, "%s: USB-in-USB: getting response\n", __FUNCTION__);
|
|
ret = sanei_usb_control_msg (dn, USB_DIR_IN | USB_TYPE_VENDOR,
|
|
0x0c, 0x90, 0x00,
|
|
sizeof (response), &response);
|
|
if (ret != SANE_STATUS_GOOD)
|
|
{
|
|
DBG (DBG_err, "%s: USB-in-USB: error getting response\n", __FUNCTION__);
|
|
return ret;
|
|
}
|
|
|
|
/* Necessary response after normal (non-CORE) data is 0x00,
|
|
* after bulk OUT preparation - 0x24
|
|
*/
|
|
needed_response = core_flags & CORE_BULK_OUT ? 0x24 : 0x00;
|
|
if (response == needed_response)
|
|
DBG (DBG_usb, "%s: USB-in-USB: got correct response\n",
|
|
__FUNCTION__);
|
|
|
|
if (response != needed_response)
|
|
{
|
|
DBG (DBG_err,
|
|
"%s: USB-in-USB: invalid response received "
|
|
"(needed %04x, got %04x)\n",
|
|
__FUNCTION__, needed_response, response);
|
|
return SANE_STATUS_IO_ERROR;
|
|
}
|
|
|
|
/* Send bulk OUT flags is bulk OUT preparation is performed */
|
|
if (core_flags & CORE_BULK_OUT)
|
|
{
|
|
u_int8_t bulk_flags = 0x24;
|
|
DBG (DBG_usb, "%s: USB-in-USB: sending bulk flags\n",
|
|
__FUNCTION__);
|
|
|
|
ret = sanei_usb_control_msg (dn, USB_DIR_OUT | USB_TYPE_VENDOR,
|
|
0x0c, 0x83, 0x00,
|
|
sizeof (bulk_flags), &bulk_flags);
|
|
if (ret != SANE_STATUS_GOOD)
|
|
{
|
|
DBG (DBG_err, "%s: USB-in-USB: error sending bulk flags\n",
|
|
__FUNCTION__);
|
|
return ret;
|
|
}
|
|
|
|
/* Check acknowledge for bulk flags transmission */
|
|
DBG (DBG_usb, "%s: USB-in-USB: checking confirmation for bulk flags\n",
|
|
__FUNCTION__);
|
|
ret = hp5590_get_ack (dn);
|
|
if (ret != SANE_STATUS_GOOD)
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
* USB-in-USB: verifies last command
|
|
*
|
|
* Parameters
|
|
* dn - sanei_usb device descriptor
|
|
* cmd - command to verify
|
|
*
|
|
* Returns
|
|
* SANE_STATUS_GOOD - command verified successfully and CORE is ready
|
|
* SANE_STATUS_IO_ERROR - command verification failed
|
|
* SANE_STATUS_DEVICE_BUSY - command verified successfully but CORE isn't ready
|
|
* all other SANE_Status values - otherwise
|
|
*/
|
|
static SANE_Status
|
|
hp5590_verify_last_cmd (SANE_Int dn, unsigned int cmd)
|
|
{
|
|
u_int16_t verify_cmd;
|
|
unsigned int last_cmd;
|
|
unsigned int core_status;
|
|
SANE_Status ret;
|
|
|
|
DBG (3, "%s: USB-in-USB: command verification requested\n",
|
|
__FUNCTION__);
|
|
|
|
/* Read last command along with CORE status */
|
|
ret = hp5590_control_msg (dn, USB_DIR_IN,
|
|
0x04, 0xc5, 0x00,
|
|
(unsigned char *) &verify_cmd,
|
|
sizeof (verify_cmd), CORE_NONE);
|
|
if (ret != SANE_STATUS_GOOD)
|
|
return ret;
|
|
|
|
/* Last command - minor byte */
|
|
last_cmd = verify_cmd & 0xff;
|
|
/* CORE status - major byte */
|
|
core_status = (verify_cmd & 0xff00) >> 8;
|
|
|
|
/* Verify last command */
|
|
DBG (DBG_usb, "%s: USB-in-USB: command verification %04x, "
|
|
"last command: %04x, core status: %04x\n",
|
|
__FUNCTION__, verify_cmd, last_cmd, core_status);
|
|
if ((cmd & 0x00ff) != last_cmd)
|
|
{
|
|
DBG (DBG_err, "%s: USB-in-USB: command verification failed: "
|
|
"expected 0x%04x, got 0x%04x\n",
|
|
__FUNCTION__, cmd, last_cmd);
|
|
return SANE_STATUS_IO_ERROR;
|
|
}
|
|
|
|
DBG (DBG_usb, "%s: USB-in-USB: command verified successfully\n",
|
|
__FUNCTION__);
|
|
|
|
/* Return value depends on CORE status */
|
|
return core_status & CORE_FLAG_NOT_READY ?
|
|
SANE_STATUS_DEVICE_BUSY : SANE_STATUS_GOOD;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
* USB-in-USB: send command (convenience wrapper around hp5590_control_msg())
|
|
*
|
|
* Parameters
|
|
* dn - sanei_usb device descriptor
|
|
* requesttype, request, value, index - their meaninings are similar to
|
|
* sanei_control_msg()
|
|
* bytes - pointer to data buffer
|
|
* size - size of data
|
|
* core_flags -
|
|
* CORE_NONE - no CORE operation will be performed
|
|
* CORE_DATA - operation on CORE data will be performed
|
|
* CORE_BULK_IN - preparation for bulk IN transfer (not used yet)
|
|
* CORE_BULK_OUT - preparation for bulk OUT trasfer
|
|
*
|
|
* Returns
|
|
* SANE_STATUS_GOOD - command was sent (and possible verified) w/o any errors
|
|
* all other SANE_Status values - otherwise
|
|
*/
|
|
static SANE_Status
|
|
hp5590_cmd (SANE_Int dn, unsigned int flags,
|
|
unsigned int cmd, unsigned char *data, unsigned int size,
|
|
unsigned int core_flags)
|
|
{
|
|
SANE_Status ret;
|
|
|
|
DBG (3, "%s: USB-in-USB: command : %04x\n", __FUNCTION__, cmd);
|
|
|
|
ret = hp5590_control_msg (dn,
|
|
flags & CMD_IN ? USB_DIR_IN : USB_DIR_OUT,
|
|
0x04, cmd, 0x00, data, size, core_flags);
|
|
if (ret != SANE_STATUS_GOOD)
|
|
return ret;
|
|
|
|
ret = SANE_STATUS_GOOD;
|
|
/* Verify last command if requested */
|
|
if (flags & CMD_VERIFY)
|
|
{
|
|
ret = hp5590_verify_last_cmd (dn, cmd);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
* USB-in-USB: initialized bulk read state
|
|
*
|
|
* Parameters
|
|
* state - pointer to a pointer for initialized state
|
|
*
|
|
* Returns
|
|
* SANE_STATUS_GOOD - if state was initialized successfully
|
|
* SANE_STATUS_NO_MEM - memory allocation failed
|
|
*/
|
|
static SANE_Status
|
|
hp5590_low_init_bulk_read_state (void **state)
|
|
{
|
|
struct bulk_read_state *bulk_read_state;
|
|
|
|
DBG (3, "%s: USB-in-USB: initializing bulk read state\n", __FUNCTION__);
|
|
|
|
hp5590_low_assert (state != NULL);
|
|
|
|
bulk_read_state = malloc (sizeof (struct bulk_read_state));
|
|
if (!bulk_read_state)
|
|
return SANE_STATUS_NO_MEM;
|
|
memset (bulk_read_state, 0, sizeof (struct bulk_read_state));
|
|
|
|
bulk_read_state->buffer = malloc (ALLOCATE_BULK_READ_PAGES
|
|
* BULK_READ_PAGE_SIZE);
|
|
if (!bulk_read_state->buffer)
|
|
{
|
|
DBG (DBG_err, "%s: Memory allocation failed for %u bytes\n",
|
|
__FUNCTION__, ALLOCATE_BULK_READ_PAGES * BULK_READ_PAGE_SIZE);
|
|
return SANE_STATUS_NO_MEM;
|
|
}
|
|
bulk_read_state->buffer_size = ALLOCATE_BULK_READ_PAGES
|
|
* BULK_READ_PAGE_SIZE;
|
|
bulk_read_state->bytes_available = 0;
|
|
bulk_read_state->buffer_out_ptr = bulk_read_state->buffer;
|
|
bulk_read_state->buffer_in_ptr = bulk_read_state->buffer;
|
|
bulk_read_state->total_pages = 0;
|
|
bulk_read_state->buffer_end_ptr = bulk_read_state->buffer
|
|
+ bulk_read_state->buffer_size;
|
|
bulk_read_state->initialized = 1;
|
|
|
|
*state = bulk_read_state;
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
* USB-in-USB: free bulk read state
|
|
*
|
|
* Parameters
|
|
* state - pointer to a pointer to bulk read state
|
|
*
|
|
* Returns
|
|
* SANE_STATUS_GOOD - bulk read state freed successfully
|
|
*/
|
|
static SANE_Status
|
|
hp5590_low_free_bulk_read_state (void **state)
|
|
{
|
|
struct bulk_read_state *bulk_read_state;
|
|
|
|
DBG (3, "%s\n", __FUNCTION__);
|
|
|
|
hp5590_low_assert (state != NULL);
|
|
/* Just return if NULL bulk read state was given */
|
|
if (*state == NULL)
|
|
return SANE_STATUS_GOOD;
|
|
|
|
bulk_read_state = *state;
|
|
|
|
DBG (3, "%s: USB-in-USB: freeing bulk read state\n", __FUNCTION__);
|
|
|
|
free (bulk_read_state->buffer);
|
|
bulk_read_state->buffer = NULL;
|
|
free (bulk_read_state);
|
|
*state = NULL;
|
|
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
|
|
/* FIXME: perhaps needs to be converted to use hp5590_control_msg() */
|
|
/*******************************************************************************
|
|
* USB-in-USB: bulk read
|
|
*
|
|
* Parameters
|
|
* dn - sanei_usb device descriptor
|
|
* bytes - pointer to data buffer
|
|
* size - size of data to read
|
|
* state - pointer to initialized bulk read state structure
|
|
*/
|
|
static SANE_Status
|
|
hp5590_bulk_read (SANE_Int dn, unsigned char *bytes, unsigned int size,
|
|
void *state)
|
|
{
|
|
struct usb_in_usb_bulk_setup ctrl;
|
|
SANE_Status ret;
|
|
unsigned int next_pages;
|
|
u_int8_t bulk_flags;
|
|
size_t next_portion;
|
|
struct bulk_read_state *bulk_read_state;
|
|
unsigned int bytes_until_buffer_end;
|
|
|
|
DBG (3, "%s\n", __FUNCTION__);
|
|
|
|
hp5590_low_assert (state != NULL);
|
|
hp5590_low_assert (bytes != NULL);
|
|
|
|
bulk_read_state = state;
|
|
if (bulk_read_state->initialized == 0)
|
|
{
|
|
DBG (DBG_err, "%s: USB-in-USB: bulk read state not initialized\n",
|
|
__FUNCTION__);
|
|
return SANE_STATUS_INVAL;
|
|
}
|
|
|
|
memset (bytes, 0, size);
|
|
|
|
/* Check if requested data would fit into the buffer */
|
|
if (size > bulk_read_state->buffer_size)
|
|
{
|
|
DBG (DBG_err, "Data requested won't fit in the bulk read buffer "
|
|
"(requested: %u, buffer size: %u\n", size,
|
|
bulk_read_state->buffer_size);
|
|
return SANE_STATUS_NO_MEM;
|
|
}
|
|
|
|
/* Read data until requested size of data will be received */
|
|
while (bulk_read_state->bytes_available < size)
|
|
{
|
|
DBG (DBG_usb, "%s: USB-in-USB: not enough data in buffer available "
|
|
"(available: %u, requested: %u)\n",
|
|
__FUNCTION__, bulk_read_state->bytes_available, size);
|
|
|
|
/* IMPORTANT! 'next_pages' means 'request and receive next_pages pages in
|
|
* one bulk transfer request '. Windows driver uses 4 pages between each
|
|
* request. The more pages are received between requests the less the
|
|
* scanner does scan head re-positioning thus improving scanning speed.
|
|
* On the other hand, scanner expects that all of the requested pages
|
|
* will be received immediately after the request. In case when a
|
|
* frontend will have a delay between reads we will get bulk transfer
|
|
* timeout sooner or later.
|
|
* Having next_pages = 1 is the most safe case.
|
|
*/
|
|
next_pages = 1;
|
|
/* Count all received pages to calculate when we will need to send
|
|
* another bulk request
|
|
*/
|
|
bulk_read_state->total_pages++;
|
|
DBG (DBG_usb, "%s: USB-in-USB: total pages done: %u\n",
|
|
__FUNCTION__, bulk_read_state->total_pages);
|
|
|
|
/* Send another bulk request for 'next_pages' before first
|
|
* page or next necessary one
|
|
*/
|
|
if ( bulk_read_state->total_pages == 1
|
|
|| bulk_read_state->total_pages % next_pages == 0)
|
|
{
|
|
/* Send bulk flags */
|
|
DBG (DBG_usb, "%s: USB-in-USB: sending USB-in-USB bulk flags\n",
|
|
__FUNCTION__);
|
|
bulk_flags = 0x24;
|
|
ret = sanei_usb_control_msg (dn, USB_DIR_OUT | USB_TYPE_VENDOR,
|
|
0x0c, 0x83, 0x00,
|
|
sizeof (bulk_flags), &bulk_flags);
|
|
if (ret != SANE_STATUS_GOOD)
|
|
{
|
|
DBG (DBG_err, "%s: USB-in-USB: error sending bulk flags\n",
|
|
__FUNCTION__);
|
|
return ret;
|
|
}
|
|
|
|
/* Check acknowledge for bulk flags just sent */
|
|
DBG (DBG_usb, "%s: USB-in-USB: checking confirmation for bulk flags\n",
|
|
__FUNCTION__);
|
|
ret = hp5590_get_ack (dn);
|
|
if (ret != SANE_STATUS_GOOD)
|
|
return ret;
|
|
|
|
/* Prepare bulk read request */
|
|
memset (&ctrl, 0, sizeof (ctrl));
|
|
ctrl.bRequestType = 0x00;
|
|
ctrl.bEndpoint = 0x82;
|
|
ctrl.wLength = htons (next_pages);
|
|
|
|
/* Send bulk read request */
|
|
DBG (DBG_usb, "%s: USB-in-USB: sending control msg for bulk\n",
|
|
__FUNCTION__);
|
|
ret = sanei_usb_control_msg (dn, USB_DIR_OUT | USB_TYPE_VENDOR,
|
|
0x04, 0x82, 0x00,
|
|
sizeof (ctrl),
|
|
(unsigned char *) &ctrl);
|
|
if (ret != SANE_STATUS_GOOD)
|
|
{
|
|
DBG (DBG_err, "%s: USB-in-USB: error sending control msg\n",
|
|
__FUNCTION__);
|
|
return ret;
|
|
}
|
|
|
|
/* Check acknowledge for bulk read request */
|
|
DBG (DBG_usb, "%s: USB-in-USB: checking if control msg was accepted\n",
|
|
__FUNCTION__);
|
|
ret = hp5590_get_ack (dn);
|
|
if (ret != SANE_STATUS_GOOD)
|
|
return ret;
|
|
}
|
|
|
|
next_portion = BULK_READ_PAGE_SIZE;
|
|
/* Check if next page will fit into the buffer */
|
|
if (bulk_read_state->buffer_size
|
|
- bulk_read_state->bytes_available < next_portion)
|
|
{
|
|
DBG (DBG_err, "%s: USB-in-USB: buffer too small\n", __FUNCTION__);
|
|
return SANE_STATUS_NO_MEM;
|
|
}
|
|
|
|
/* Bulk read next page */
|
|
DBG (DBG_usb, "%s: USB-in-USB: bulk reading %lu bytes\n",
|
|
__FUNCTION__, (u_long) next_portion);
|
|
ret = sanei_usb_read_bulk (dn,
|
|
bulk_read_state->buffer_in_ptr,
|
|
&next_portion);
|
|
if (ret != SANE_STATUS_GOOD)
|
|
{
|
|
if (ret == SANE_STATUS_EOF)
|
|
return ret;
|
|
DBG (DBG_err, "%s: USB-in-USB: error during bulk read: %s\n",
|
|
__FUNCTION__, sane_strstatus (ret));
|
|
return ret;
|
|
}
|
|
|
|
/* Check if we received the same amount of data as requsted */
|
|
if (next_portion != BULK_READ_PAGE_SIZE)
|
|
{
|
|
DBG (DBG_err, "%s: USB-in-USB: incomplete bulk read "
|
|
"(requested %u bytes, got %lu bytes)\n",
|
|
__FUNCTION__, BULK_READ_PAGE_SIZE, (u_long) next_portion);
|
|
return SANE_STATUS_IO_ERROR;
|
|
}
|
|
|
|
/* Move pointers to the next position */
|
|
bulk_read_state->buffer_in_ptr += next_portion;
|
|
|
|
/* Check for the end of the buffer */
|
|
if (bulk_read_state->buffer_in_ptr > bulk_read_state->buffer_end_ptr)
|
|
{
|
|
DBG (DBG_err,
|
|
"%s: USB-in-USB: attempted to access over the end of buffer "
|
|
"(in_ptr: %p, end_ptr: %p, ptr: %p, buffer size: %u\n",
|
|
__FUNCTION__, bulk_read_state->buffer_in_ptr,
|
|
bulk_read_state->buffer_end_ptr, bulk_read_state->buffer,
|
|
bulk_read_state->buffer_size);
|
|
return SANE_STATUS_NO_MEM;
|
|
}
|
|
|
|
/* Check for buffer pointer wrapping */
|
|
if (bulk_read_state->buffer_in_ptr == bulk_read_state->buffer_end_ptr)
|
|
{
|
|
DBG (DBG_usb, "%s: USB-in-USB: buffer wrapped while writing\n",
|
|
__FUNCTION__);
|
|
bulk_read_state->buffer_in_ptr = bulk_read_state->buffer;
|
|
}
|
|
|
|
/* Count the amount of data we read */
|
|
bulk_read_state->bytes_available += next_portion;
|
|
}
|
|
|
|
/* Transfer requested amount of data to the caller */
|
|
DBG (DBG_usb, "%s: USB-in-USB: data in bulk buffer is available "
|
|
"(requested %u bytes, available %u bytes)\n",
|
|
__FUNCTION__, size, bulk_read_state->bytes_available);
|
|
|
|
/* Check for buffer pointer wrapping */
|
|
bytes_until_buffer_end = bulk_read_state->buffer_end_ptr
|
|
- bulk_read_state->buffer_out_ptr;
|
|
if (bytes_until_buffer_end <= size)
|
|
{
|
|
/* First buffer part */
|
|
DBG (DBG_usb, "%s: USB-in-USB: reached bulk read buffer end\n", __FUNCTION__);
|
|
memcpy (bytes, bulk_read_state->buffer_out_ptr, bytes_until_buffer_end);
|
|
bulk_read_state->buffer_out_ptr = bulk_read_state->buffer;
|
|
/* And second part (if any) */
|
|
if (bytes_until_buffer_end < size)
|
|
{
|
|
DBG (DBG_usb, "%s: USB-in-USB: giving 2nd buffer part\n", __FUNCTION__);
|
|
memcpy (bytes + bytes_until_buffer_end,
|
|
bulk_read_state->buffer_out_ptr,
|
|
size - bytes_until_buffer_end);
|
|
bulk_read_state->buffer_out_ptr += size - bytes_until_buffer_end;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* The data is in one buffer part (w/o wrapping) */
|
|
memcpy (bytes, bulk_read_state->buffer_out_ptr, size);
|
|
bulk_read_state->buffer_out_ptr += size;
|
|
if (bulk_read_state->buffer_out_ptr == bulk_read_state->buffer_end_ptr)
|
|
{
|
|
DBG (DBG_usb, "%s: USB-in-USB: buffer wrapped while reading\n",
|
|
__FUNCTION__);
|
|
bulk_read_state->buffer_out_ptr = bulk_read_state->buffer;
|
|
}
|
|
}
|
|
|
|
/* Count the amount of data transferred to the caller */
|
|
bulk_read_state->bytes_available -= size;
|
|
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
* USB-in-USB: bulk write
|
|
*
|
|
* Parameters
|
|
* dn - sanei_usb device descriptor
|
|
* cmd - command for bulk write operation
|
|
* bytes - pointer to data buffer
|
|
* size - size of data
|
|
*
|
|
* Returns
|
|
* SANE_STATUS_GOOD - all data transferred successfully
|
|
* all other SANE_Status value - otherwise
|
|
*/
|
|
static SANE_Status
|
|
hp5590_bulk_write (SANE_Int dn, int cmd, unsigned char *bytes,
|
|
unsigned int size)
|
|
{
|
|
struct usb_in_usb_bulk_setup ctrl;
|
|
SANE_Status ret;
|
|
struct bulk_size bulk_size;
|
|
|
|
unsigned int len;
|
|
unsigned char *ptr;
|
|
size_t next_portion;
|
|
|
|
DBG (3, "%s: USB-in-USB: command: %04x, size %u\n", __FUNCTION__, cmd,
|
|
size);
|
|
|
|
hp5590_low_assert (bytes != NULL);
|
|
|
|
/* Prepare bulk write request */
|
|
memset (&bulk_size, 0, sizeof (bulk_size));
|
|
/* Counted in page size */
|
|
bulk_size.size = size / BULK_WRITE_PAGE_SIZE;
|
|
|
|
/* Send bulk write request */
|
|
DBG (3, "%s: USB-in-USB: total %u pages (each of %u bytes)\n",
|
|
__FUNCTION__, bulk_size.size, BULK_WRITE_PAGE_SIZE);
|
|
ret = hp5590_control_msg (dn, USB_DIR_OUT,
|
|
0x04, cmd, 0,
|
|
(unsigned char *) &bulk_size, sizeof (bulk_size),
|
|
CORE_DATA | CORE_BULK_OUT);
|
|
if (ret != SANE_STATUS_GOOD)
|
|
return ret;
|
|
|
|
len = size;
|
|
ptr = bytes;
|
|
|
|
/* Send all data in pages */
|
|
while (len)
|
|
{
|
|
next_portion = BULK_WRITE_PAGE_SIZE;
|
|
if (len < next_portion)
|
|
next_portion = len;
|
|
|
|
DBG (3, "%s: USB-in-USB: next portion %lu bytes\n",
|
|
__FUNCTION__, (u_long) next_portion);
|
|
|
|
/* Prepare bulk write request */
|
|
memset (&ctrl, 0, sizeof (ctrl));
|
|
ctrl.bRequestType = 0x01;
|
|
ctrl.bEndpoint = 0x82;
|
|
ctrl.wLength = htons (next_portion);
|
|
|
|
/* Send bulk write request */
|
|
ret = sanei_usb_control_msg (dn, USB_DIR_OUT | USB_TYPE_VENDOR,
|
|
0x04, 0x82, 0,
|
|
sizeof (ctrl), (unsigned char *) &ctrl);
|
|
if (ret != SANE_STATUS_GOOD)
|
|
return ret;
|
|
|
|
/* Check acknowledge for bulk write request */
|
|
DBG (DBG_usb, "%s: USB-in-USB: checking if command was accepted\n",
|
|
__FUNCTION__);
|
|
ret = hp5590_get_ack (dn);
|
|
if (ret != SANE_STATUS_GOOD)
|
|
return ret;
|
|
|
|
/* Write bulk data */
|
|
DBG (3, "%s: USB-in-USB: bulk writing %lu bytes\n",
|
|
__FUNCTION__, (u_long) next_portion);
|
|
ret = sanei_usb_write_bulk (dn, ptr, &next_portion);
|
|
if (ret != SANE_STATUS_GOOD)
|
|
{
|
|
/* Treast EOF as successful result */
|
|
if (ret == SANE_STATUS_EOF)
|
|
break;
|
|
DBG (DBG_err, "%s: USB-in-USB: error during bulk write: %s\n",
|
|
__FUNCTION__, sane_strstatus (ret));
|
|
return ret;
|
|
}
|
|
|
|
/* Move to the next page */
|
|
len -= next_portion;
|
|
ptr += next_portion;
|
|
}
|
|
|
|
/* Verify bulk command */
|
|
return hp5590_verify_last_cmd (dn, cmd);
|
|
}
|
|
/* vim: sw=2 ts=8
|
|
*/
|