1141cc406Sopenharmony_ci/* sane - Scanner Access Now Easy.
2141cc406Sopenharmony_ci   Copyright (C) 2007 Ilia Sotnikov <hostcc@gmail.com>
3141cc406Sopenharmony_ci   This file is part of the SANE package.
4141cc406Sopenharmony_ci
5141cc406Sopenharmony_ci   This program is free software; you can redistribute it and/or
6141cc406Sopenharmony_ci   modify it under the terms of the GNU General Public License as
7141cc406Sopenharmony_ci   published by the Free Software Foundation; either version 2 of the
8141cc406Sopenharmony_ci   License, or (at your option) any later version.
9141cc406Sopenharmony_ci
10141cc406Sopenharmony_ci   This program is distributed in the hope that it will be useful, but
11141cc406Sopenharmony_ci   WITHOUT ANY WARRANTY; without even the implied warranty of
12141cc406Sopenharmony_ci   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13141cc406Sopenharmony_ci   General Public License for more details.
14141cc406Sopenharmony_ci
15141cc406Sopenharmony_ci   You should have received a copy of the GNU General Public License
16141cc406Sopenharmony_ci   along with this program.  If not, see <https://www.gnu.org/licenses/>.
17141cc406Sopenharmony_ci
18141cc406Sopenharmony_ci   As a special exception, the authors of SANE give permission for
19141cc406Sopenharmony_ci   additional uses of the libraries contained in this release of SANE.
20141cc406Sopenharmony_ci
21141cc406Sopenharmony_ci   The exception is that, if you link a SANE library with other files
22141cc406Sopenharmony_ci   to produce an executable, this does not by itself cause the
23141cc406Sopenharmony_ci   resulting executable to be covered by the GNU General Public
24141cc406Sopenharmony_ci   License.  Your use of that executable is in no way restricted on
25141cc406Sopenharmony_ci   account of linking the SANE library code into it.
26141cc406Sopenharmony_ci
27141cc406Sopenharmony_ci   This exception does not, however, invalidate any other reasons why
28141cc406Sopenharmony_ci   the executable file might be covered by the GNU General Public
29141cc406Sopenharmony_ci   License.
30141cc406Sopenharmony_ci
31141cc406Sopenharmony_ci   If you submit changes to SANE to the maintainers to be included in
32141cc406Sopenharmony_ci   a subsequent release, you agree by submitting the changes that
33141cc406Sopenharmony_ci   those changes may be distributed with this exception intact.
34141cc406Sopenharmony_ci
35141cc406Sopenharmony_ci   If you write modifications of your own for SANE, it is your choice
36141cc406Sopenharmony_ci   whether to permit this exception to apply to your modifications.
37141cc406Sopenharmony_ci   If you do not wish that, delete this exception notice.
38141cc406Sopenharmony_ci
39141cc406Sopenharmony_ci   This file is part of a SANE backend for
40141cc406Sopenharmony_ci   HP ScanJet 4500C/4570C/5500C/5550C/5590/7650 Scanners
41141cc406Sopenharmony_ci*/
42141cc406Sopenharmony_ci
43141cc406Sopenharmony_ci#include "../include/sane/config.h"
44141cc406Sopenharmony_ci
45141cc406Sopenharmony_ci#include <stdio.h>
46141cc406Sopenharmony_ci#include <string.h>
47141cc406Sopenharmony_ci#include <errno.h>
48141cc406Sopenharmony_ci#ifdef HAVE_NETINET_IN_H
49141cc406Sopenharmony_ci# include <netinet/in.h>
50141cc406Sopenharmony_ci#endif /* HAVE_NETINET_IN_H */
51141cc406Sopenharmony_ci
52141cc406Sopenharmony_ci#include "byteorder.h"
53141cc406Sopenharmony_ci
54141cc406Sopenharmony_ci#include "../include/sane/sanei_debug.h"
55141cc406Sopenharmony_ci#include "../include/sane/sanei_usb.h"
56141cc406Sopenharmony_ci#include "../include/_stdint.h"
57141cc406Sopenharmony_ci#include "hp5590_low.h"
58141cc406Sopenharmony_ci
59141cc406Sopenharmony_ci/* Debug levels */
60141cc406Sopenharmony_ci#define DBG_err         0
61141cc406Sopenharmony_ci#define DBG_proc        10
62141cc406Sopenharmony_ci#define DBG_usb         50
63141cc406Sopenharmony_ci
64141cc406Sopenharmony_ci/* Custom assert() macro */
65141cc406Sopenharmony_ci#define hp5590_low_assert(exp) if(!(exp)) { \
66141cc406Sopenharmony_ci  DBG (DBG_err, "Assertion '%s' failed at %s:%u\n", #exp, __FILE__, __LINE__);\
67141cc406Sopenharmony_ci  return SANE_STATUS_INVAL; \
68141cc406Sopenharmony_ci}
69141cc406Sopenharmony_ci
70141cc406Sopenharmony_ci/* Structure describing bulk transfer size */
71141cc406Sopenharmony_cistruct bulk_size
72141cc406Sopenharmony_ci{
73141cc406Sopenharmony_ci  uint16_t      size;
74141cc406Sopenharmony_ci  uint8_t       unused;
75141cc406Sopenharmony_ci} __attribute__ ((packed));
76141cc406Sopenharmony_ci
77141cc406Sopenharmony_ci/* Structure describing bulk URB */
78141cc406Sopenharmony_ci/* FIXME: Verify according to USB standard */
79141cc406Sopenharmony_cistruct usb_in_usb_bulk_setup
80141cc406Sopenharmony_ci{
81141cc406Sopenharmony_ci  uint8_t       bRequestType;
82141cc406Sopenharmony_ci  uint8_t       bRequest;
83141cc406Sopenharmony_ci  uint8_t       bEndpoint;
84141cc406Sopenharmony_ci  uint16_t      unknown;
85141cc406Sopenharmony_ci  uint16_t      wLength;        /* MSB first */
86141cc406Sopenharmony_ci  uint8_t       pad;
87141cc406Sopenharmony_ci} __attribute__ ((packed));
88141cc406Sopenharmony_ci
89141cc406Sopenharmony_ci/* Structure describing control URB */
90141cc406Sopenharmony_cistruct usb_in_usb_ctrl_setup {
91141cc406Sopenharmony_ci  uint8_t  bRequestType;
92141cc406Sopenharmony_ci  uint8_t  bRequest;
93141cc406Sopenharmony_ci  uint16_t wValue;              /* MSB first */
94141cc406Sopenharmony_ci  uint16_t wIndex;              /* MSB first */
95141cc406Sopenharmony_ci  uint16_t wLength;             /* LSB first */
96141cc406Sopenharmony_ci} __attribute__ ((packed));
97141cc406Sopenharmony_ci
98141cc406Sopenharmony_ci/* CORE status flag - ready or not */
99141cc406Sopenharmony_ci#define CORE_FLAG_NOT_READY             1 << 1
100141cc406Sopenharmony_ci
101141cc406Sopenharmony_ci/* Bulk transfers are done in pages, below their respective sizes */
102141cc406Sopenharmony_ci#define BULK_WRITE_PAGE_SIZE            0x0f000
103141cc406Sopenharmony_ci#define BULK_READ_PAGE_SIZE             0x10000
104141cc406Sopenharmony_ci#define ALLOCATE_BULK_READ_PAGES        16      /* 16 * 65536 = 1Mb */
105141cc406Sopenharmony_ci
106141cc406Sopenharmony_ci/* Structure describing bulk read state, because bulk reads will be done in
107141cc406Sopenharmony_ci * pages, but function caller uses its own buffer, whose size is certainly
108141cc406Sopenharmony_ci * different. Also, each bulk read page is ACK'ed by special command
109141cc406Sopenharmony_ci * so total pages received should be tracked as well
110141cc406Sopenharmony_ci */
111141cc406Sopenharmony_cistruct bulk_read_state
112141cc406Sopenharmony_ci{
113141cc406Sopenharmony_ci  unsigned char *buffer;
114141cc406Sopenharmony_ci  unsigned int  buffer_size;
115141cc406Sopenharmony_ci  unsigned int  bytes_available;
116141cc406Sopenharmony_ci  unsigned char *buffer_out_ptr;
117141cc406Sopenharmony_ci  unsigned char *buffer_in_ptr;
118141cc406Sopenharmony_ci  unsigned int  total_pages;
119141cc406Sopenharmony_ci  unsigned char *buffer_end_ptr;
120141cc406Sopenharmony_ci  unsigned int  initialized;
121141cc406Sopenharmony_ci};
122141cc406Sopenharmony_ci
123141cc406Sopenharmony_ci/*******************************************************************************
124141cc406Sopenharmony_ci * USB-in-USB: get acknowledge for last USB-in-USB operation
125141cc406Sopenharmony_ci *
126141cc406Sopenharmony_ci * Parameters
127141cc406Sopenharmony_ci * dn - sanei_usb device descriptor
128141cc406Sopenharmony_ci *
129141cc406Sopenharmony_ci * Returns
130141cc406Sopenharmony_ci * SANE_STATUS_GOOD - if correct acknowledge was received
131141cc406Sopenharmony_ci * SANE_STATUS_DEVICE_BUSY - otherwise
132141cc406Sopenharmony_ci */
133141cc406Sopenharmony_cistatic SANE_Status
134141cc406Sopenharmony_cihp5590_get_ack (SANE_Int dn,
135141cc406Sopenharmony_ci                enum proto_flags proto_flags)
136141cc406Sopenharmony_ci{
137141cc406Sopenharmony_ci  uint8_t       status;
138141cc406Sopenharmony_ci  SANE_Status   ret;
139141cc406Sopenharmony_ci
140141cc406Sopenharmony_ci  /* Bypass reading acknowledge if the device doesn't need it */
141141cc406Sopenharmony_ci  if (proto_flags & PF_NO_USB_IN_USB_ACK)
142141cc406Sopenharmony_ci    return SANE_STATUS_GOOD;
143141cc406Sopenharmony_ci
144141cc406Sopenharmony_ci  DBG (DBG_proc, "%s\n", __func__);
145141cc406Sopenharmony_ci
146141cc406Sopenharmony_ci  /* Check if USB-in-USB operation was accepted */
147141cc406Sopenharmony_ci  ret = sanei_usb_control_msg (dn, USB_DIR_IN | USB_TYPE_VENDOR,
148141cc406Sopenharmony_ci                               0x0c, 0x8e, 0x20,
149141cc406Sopenharmony_ci                               sizeof (status), &status);
150141cc406Sopenharmony_ci  if (ret != SANE_STATUS_GOOD)
151141cc406Sopenharmony_ci    {
152141cc406Sopenharmony_ci      DBG (DBG_err, "%s: USB-in-USB: error getting acknowledge\n",
153141cc406Sopenharmony_ci           __func__);
154141cc406Sopenharmony_ci      return ret;
155141cc406Sopenharmony_ci    }
156141cc406Sopenharmony_ci
157141cc406Sopenharmony_ci  DBG (DBG_usb, "%s: USB-in-USB: accepted\n", __func__);
158141cc406Sopenharmony_ci
159141cc406Sopenharmony_ci  /* Check if we received correct acknowledgment */
160141cc406Sopenharmony_ci  if (status != 0x01)
161141cc406Sopenharmony_ci    {
162141cc406Sopenharmony_ci      DBG (DBG_err, "%s: USB-in-USB: not accepted (status %u)\n",
163141cc406Sopenharmony_ci           __func__, status);
164141cc406Sopenharmony_ci      return SANE_STATUS_DEVICE_BUSY;
165141cc406Sopenharmony_ci    }
166141cc406Sopenharmony_ci
167141cc406Sopenharmony_ci  return SANE_STATUS_GOOD;
168141cc406Sopenharmony_ci}
169141cc406Sopenharmony_ci
170141cc406Sopenharmony_ci/*******************************************************************************
171141cc406Sopenharmony_ci * USB-in-USB: get device status
172141cc406Sopenharmony_ci *
173141cc406Sopenharmony_ci * Parameters
174141cc406Sopenharmony_ci * dn - sanei_usb device descriptor
175141cc406Sopenharmony_ci *
176141cc406Sopenharmony_ci * Returns
177141cc406Sopenharmony_ci * SANE_STATUS_GOOD - if correct status was received
178141cc406Sopenharmony_ci * SANE_STATUS_DEVICE_BUSY - otherwise
179141cc406Sopenharmony_ci */
180141cc406Sopenharmony_cistatic SANE_Status
181141cc406Sopenharmony_cihp5590_get_status (SANE_Int dn,
182141cc406Sopenharmony_ci                   __sane_unused__ enum proto_flags proto_flags)
183141cc406Sopenharmony_ci{
184141cc406Sopenharmony_ci  uint8_t status;
185141cc406Sopenharmony_ci  SANE_Status ret;
186141cc406Sopenharmony_ci
187141cc406Sopenharmony_ci  DBG (DBG_proc, "%s\n", __func__);
188141cc406Sopenharmony_ci
189141cc406Sopenharmony_ci  ret = sanei_usb_control_msg (dn, USB_DIR_IN | USB_TYPE_VENDOR,
190141cc406Sopenharmony_ci                               0x0c, 0x8e, 0x00,
191141cc406Sopenharmony_ci                               sizeof (status), &status);
192141cc406Sopenharmony_ci  if (ret != SANE_STATUS_GOOD)
193141cc406Sopenharmony_ci    {
194141cc406Sopenharmony_ci      DBG (DBG_err, "%s: USB-in-USB: error getting device status\n",
195141cc406Sopenharmony_ci           __func__);
196141cc406Sopenharmony_ci      return ret;
197141cc406Sopenharmony_ci    }
198141cc406Sopenharmony_ci
199141cc406Sopenharmony_ci  /* Check if we received correct status */
200141cc406Sopenharmony_ci  if (status != 0x00)
201141cc406Sopenharmony_ci    {
202141cc406Sopenharmony_ci      DBG (DBG_err, "%s: USB-in-USB: got non-zero device status (status %u)\n",
203141cc406Sopenharmony_ci           __func__, status);
204141cc406Sopenharmony_ci      return SANE_STATUS_DEVICE_BUSY;
205141cc406Sopenharmony_ci    }
206141cc406Sopenharmony_ci
207141cc406Sopenharmony_ci  return SANE_STATUS_GOOD;
208141cc406Sopenharmony_ci}
209141cc406Sopenharmony_ci
210141cc406Sopenharmony_ci/*******************************************************************************
211141cc406Sopenharmony_ci * USB-in-USB: sends control message for IN or OUT operation
212141cc406Sopenharmony_ci *
213141cc406Sopenharmony_ci * Parameters
214141cc406Sopenharmony_ci * dn - sanei_usb device descriptor
215141cc406Sopenharmony_ci * requesttype, request, value, index - their meanings are similar to
216141cc406Sopenharmony_ci * sanei_control_msg()
217141cc406Sopenharmony_ci * bytes - pointer to data buffer
218141cc406Sopenharmony_ci * size - size of data
219141cc406Sopenharmony_ci * core_flags -
220141cc406Sopenharmony_ci *  CORE_NONE - no CORE operation will be performed
221141cc406Sopenharmony_ci *  CORE_DATA - operation on CORE data will be performed
222141cc406Sopenharmony_ci *  CORE_BULK_IN - preparation for bulk IN transfer (not used yet)
223141cc406Sopenharmony_ci *  CORE_BULK_OUT - preparation for bulk OUT transfer
224141cc406Sopenharmony_ci *
225141cc406Sopenharmony_ci * Returns
226141cc406Sopenharmony_ci * SANE_STATUS_GOOD - control message was sent w/o any errors
227141cc406Sopenharmony_ci * all other SANE_Status values - otherwise
228141cc406Sopenharmony_ci */
229141cc406Sopenharmony_cistatic SANE_Status
230141cc406Sopenharmony_cihp5590_control_msg (SANE_Int dn,
231141cc406Sopenharmony_ci                    enum proto_flags proto_flags,
232141cc406Sopenharmony_ci                    int requesttype, int request,
233141cc406Sopenharmony_ci                    int value, int index, unsigned char *bytes,
234141cc406Sopenharmony_ci                    int size, int core_flags)
235141cc406Sopenharmony_ci{
236141cc406Sopenharmony_ci  struct usb_in_usb_ctrl_setup  ctrl;
237141cc406Sopenharmony_ci  SANE_Status                   ret;
238141cc406Sopenharmony_ci  unsigned int                  len;
239141cc406Sopenharmony_ci  unsigned char                 *ptr;
240141cc406Sopenharmony_ci  uint8_t                       ack;
241141cc406Sopenharmony_ci  uint8_t                       response;
242141cc406Sopenharmony_ci  unsigned int                  needed_response;
243141cc406Sopenharmony_ci
244141cc406Sopenharmony_ci  DBG (DBG_proc, "%s: USB-in-USB: core data: %s\n",
245141cc406Sopenharmony_ci       __func__, core_flags & CORE_DATA ? "yes" : "no");
246141cc406Sopenharmony_ci
247141cc406Sopenharmony_ci  hp5590_low_assert (bytes != NULL);
248141cc406Sopenharmony_ci
249141cc406Sopenharmony_ci  /* IN (read) operation will be performed */
250141cc406Sopenharmony_ci  if (requesttype & USB_DIR_IN)
251141cc406Sopenharmony_ci    {
252141cc406Sopenharmony_ci      /* Prepare USB-in-USB control message */
253141cc406Sopenharmony_ci      memset (&ctrl, 0, sizeof (ctrl));
254141cc406Sopenharmony_ci      ctrl.bRequestType = 0xc0;
255141cc406Sopenharmony_ci      ctrl.bRequest = request;
256141cc406Sopenharmony_ci      ctrl.wValue = htons (value);
257141cc406Sopenharmony_ci      ctrl.wIndex = htons (index);
258141cc406Sopenharmony_ci      ctrl.wLength = htole16 (size);
259141cc406Sopenharmony_ci
260141cc406Sopenharmony_ci      DBG (DBG_usb, "%s: USB-in-USB: sending control msg\n", __func__);
261141cc406Sopenharmony_ci      /* Send USB-in-USB control message */
262141cc406Sopenharmony_ci      ret = sanei_usb_control_msg (dn, USB_DIR_OUT | USB_TYPE_VENDOR,
263141cc406Sopenharmony_ci                                   0x04, 0x8f, 0x00,
264141cc406Sopenharmony_ci                                   sizeof (ctrl), (unsigned char *) &ctrl);
265141cc406Sopenharmony_ci      if (ret != SANE_STATUS_GOOD)
266141cc406Sopenharmony_ci        {
267141cc406Sopenharmony_ci          DBG (DBG_err, "%s: USB-in-USB: error sending control message\n",
268141cc406Sopenharmony_ci               __func__);
269141cc406Sopenharmony_ci          return ret;
270141cc406Sopenharmony_ci        }
271141cc406Sopenharmony_ci
272141cc406Sopenharmony_ci      /* USB-in-USB: checking acknowledge for control message */
273141cc406Sopenharmony_ci      ret = hp5590_get_ack (dn, proto_flags);
274141cc406Sopenharmony_ci      if (ret != SANE_STATUS_GOOD)
275141cc406Sopenharmony_ci        return ret;
276141cc406Sopenharmony_ci
277141cc406Sopenharmony_ci      len = size;
278141cc406Sopenharmony_ci      ptr = bytes;
279141cc406Sopenharmony_ci      /* Data is read in 8 byte portions */
280141cc406Sopenharmony_ci      while (len)
281141cc406Sopenharmony_ci        {
282141cc406Sopenharmony_ci          unsigned int next_packet_size;
283141cc406Sopenharmony_ci          next_packet_size = 8;
284141cc406Sopenharmony_ci          if (len < 8)
285141cc406Sopenharmony_ci            next_packet_size = len;
286141cc406Sopenharmony_ci
287141cc406Sopenharmony_ci          /* Read USB-in-USB data */
288141cc406Sopenharmony_ci          ret = sanei_usb_control_msg (dn, USB_DIR_IN | USB_TYPE_VENDOR,
289141cc406Sopenharmony_ci                                       core_flags & CORE_DATA ? 0x0c : 0x04,
290141cc406Sopenharmony_ci                                       0x90, 0x00, next_packet_size, ptr);
291141cc406Sopenharmony_ci          if (ret != SANE_STATUS_GOOD)
292141cc406Sopenharmony_ci            {
293141cc406Sopenharmony_ci              DBG (DBG_err, "%s: USB-in-USB: error reading data\n", __func__);
294141cc406Sopenharmony_ci              return ret;
295141cc406Sopenharmony_ci            }
296141cc406Sopenharmony_ci
297141cc406Sopenharmony_ci          ptr += next_packet_size;
298141cc406Sopenharmony_ci          len -= next_packet_size;
299141cc406Sopenharmony_ci        }
300141cc406Sopenharmony_ci
301141cc406Sopenharmony_ci      /* Confirm data reception */
302141cc406Sopenharmony_ci      ack = 0;
303141cc406Sopenharmony_ci      ret = sanei_usb_control_msg (dn, USB_DIR_OUT | USB_TYPE_VENDOR,
304141cc406Sopenharmony_ci                                   0x0c, 0x8f, 0x00,
305141cc406Sopenharmony_ci                                   sizeof (ack), &ack);
306141cc406Sopenharmony_ci      if (ret != SANE_STATUS_GOOD)
307141cc406Sopenharmony_ci        {
308141cc406Sopenharmony_ci          DBG (DBG_err, "%s: USB-in-USB: error confirming data reception\n",
309141cc406Sopenharmony_ci               __func__);
310141cc406Sopenharmony_ci          return -1;
311141cc406Sopenharmony_ci        }
312141cc406Sopenharmony_ci
313141cc406Sopenharmony_ci      /* USB-in-USB: checking if confirmation was acknowledged */
314141cc406Sopenharmony_ci      ret = hp5590_get_ack (dn, proto_flags);
315141cc406Sopenharmony_ci      if (ret != SANE_STATUS_GOOD)
316141cc406Sopenharmony_ci        return ret;
317141cc406Sopenharmony_ci    }
318141cc406Sopenharmony_ci
319141cc406Sopenharmony_ci  /* OUT (write) operation will be performed */
320141cc406Sopenharmony_ci  if (!(requesttype & USB_DIR_IN))
321141cc406Sopenharmony_ci    {
322141cc406Sopenharmony_ci      /* Prepare USB-in-USB control message */
323141cc406Sopenharmony_ci      memset (&ctrl, 0, sizeof (ctrl));
324141cc406Sopenharmony_ci      ctrl.bRequestType = 0x40;
325141cc406Sopenharmony_ci      ctrl.bRequest = request;
326141cc406Sopenharmony_ci      ctrl.wValue = htons (value);
327141cc406Sopenharmony_ci      ctrl.wIndex = htons (index);
328141cc406Sopenharmony_ci      ctrl.wLength = htole16 (size);
329141cc406Sopenharmony_ci
330141cc406Sopenharmony_ci      DBG (DBG_usb, "%s: USB-in-USB: sending control msg\n", __func__);
331141cc406Sopenharmony_ci      /* Send USB-in-USB control message */
332141cc406Sopenharmony_ci      ret = sanei_usb_control_msg (dn, USB_DIR_OUT | USB_TYPE_VENDOR,
333141cc406Sopenharmony_ci                                   0x04, 0x8f, 0x00,
334141cc406Sopenharmony_ci                                   sizeof (ctrl), (unsigned char *) &ctrl);
335141cc406Sopenharmony_ci      if (ret != SANE_STATUS_GOOD)
336141cc406Sopenharmony_ci        {
337141cc406Sopenharmony_ci          DBG (DBG_err, "%s: USB-in-USB: error sending control message\n",
338141cc406Sopenharmony_ci               __func__);
339141cc406Sopenharmony_ci          return ret;
340141cc406Sopenharmony_ci        }
341141cc406Sopenharmony_ci
342141cc406Sopenharmony_ci      /* USB-in-USB: checking acknowledge for control message */
343141cc406Sopenharmony_ci      ret = hp5590_get_ack (dn, proto_flags);
344141cc406Sopenharmony_ci      if (ret != SANE_STATUS_GOOD)
345141cc406Sopenharmony_ci        return ret;
346141cc406Sopenharmony_ci
347141cc406Sopenharmony_ci      len = size;
348141cc406Sopenharmony_ci      ptr = bytes;
349141cc406Sopenharmony_ci      /* Data is sent in 8 byte portions */
350141cc406Sopenharmony_ci      while (len)
351141cc406Sopenharmony_ci        {
352141cc406Sopenharmony_ci          unsigned int next_packet_size;
353141cc406Sopenharmony_ci          next_packet_size = 8;
354141cc406Sopenharmony_ci          if (len < 8)
355141cc406Sopenharmony_ci            next_packet_size = len;
356141cc406Sopenharmony_ci
357141cc406Sopenharmony_ci          /* Send USB-in-USB data */
358141cc406Sopenharmony_ci          ret = sanei_usb_control_msg (dn, USB_DIR_OUT | USB_TYPE_VENDOR,
359141cc406Sopenharmony_ci                                       core_flags & CORE_DATA ? 0x04 : 0x0c,
360141cc406Sopenharmony_ci                                       0x8f, 0x00, next_packet_size, ptr);
361141cc406Sopenharmony_ci          if (ret != SANE_STATUS_GOOD)
362141cc406Sopenharmony_ci            {
363141cc406Sopenharmony_ci              DBG (DBG_err, "%s: USB-in-USB: error sending data\n", __func__);
364141cc406Sopenharmony_ci              return ret;
365141cc406Sopenharmony_ci            }
366141cc406Sopenharmony_ci
367141cc406Sopenharmony_ci          /* CORE data is acknowledged packet by packet */
368141cc406Sopenharmony_ci          if (core_flags & CORE_DATA)
369141cc406Sopenharmony_ci            {
370141cc406Sopenharmony_ci              /* USB-in-USB: checking if data was accepted */
371141cc406Sopenharmony_ci              ret = hp5590_get_ack (dn, proto_flags);
372141cc406Sopenharmony_ci              if (ret != SANE_STATUS_GOOD)
373141cc406Sopenharmony_ci                return ret;
374141cc406Sopenharmony_ci            }
375141cc406Sopenharmony_ci
376141cc406Sopenharmony_ci          ptr += next_packet_size;
377141cc406Sopenharmony_ci          len -= next_packet_size;
378141cc406Sopenharmony_ci        }
379141cc406Sopenharmony_ci
380141cc406Sopenharmony_ci      /* Normal (non-CORE) data is acknowledged after its full transmission */
381141cc406Sopenharmony_ci      if (!(core_flags & CORE_DATA))
382141cc406Sopenharmony_ci        {
383141cc406Sopenharmony_ci          /* USB-in-USB: checking if data was accepted */
384141cc406Sopenharmony_ci          ret = hp5590_get_ack (dn, proto_flags);
385141cc406Sopenharmony_ci          if (ret != SANE_STATUS_GOOD)
386141cc406Sopenharmony_ci            return ret;
387141cc406Sopenharmony_ci        }
388141cc406Sopenharmony_ci
389141cc406Sopenharmony_ci      /* Getting  response after data transmission */
390141cc406Sopenharmony_ci      DBG (DBG_usb, "%s: USB-in-USB: getting response\n", __func__);
391141cc406Sopenharmony_ci      ret = sanei_usb_control_msg (dn, USB_DIR_IN | USB_TYPE_VENDOR,
392141cc406Sopenharmony_ci                                   0x0c, 0x90, 0x00,
393141cc406Sopenharmony_ci                                   sizeof (response), &response);
394141cc406Sopenharmony_ci      if (ret != SANE_STATUS_GOOD)
395141cc406Sopenharmony_ci        {
396141cc406Sopenharmony_ci          DBG (DBG_err, "%s: USB-in-USB: error getting response\n", __func__);
397141cc406Sopenharmony_ci          return ret;
398141cc406Sopenharmony_ci        }
399141cc406Sopenharmony_ci
400141cc406Sopenharmony_ci      /* Necessary response after normal (non-CORE) data is 0x00,
401141cc406Sopenharmony_ci       * after bulk OUT preparation - 0x24
402141cc406Sopenharmony_ci       */
403141cc406Sopenharmony_ci      needed_response = core_flags & CORE_BULK_OUT ? 0x24 : 0x00;
404141cc406Sopenharmony_ci      if (response == needed_response)
405141cc406Sopenharmony_ci        DBG (DBG_usb, "%s: USB-in-USB: got correct response\n",
406141cc406Sopenharmony_ci             __func__);
407141cc406Sopenharmony_ci
408141cc406Sopenharmony_ci      if (response != needed_response)
409141cc406Sopenharmony_ci        {
410141cc406Sopenharmony_ci          DBG (DBG_err,
411141cc406Sopenharmony_ci               "%s: USB-in-USB: invalid response received "
412141cc406Sopenharmony_ci               "(needed %04x, got %04x)\n",
413141cc406Sopenharmony_ci               __func__, needed_response, response);
414141cc406Sopenharmony_ci          return SANE_STATUS_IO_ERROR;
415141cc406Sopenharmony_ci        }
416141cc406Sopenharmony_ci
417141cc406Sopenharmony_ci      /* Send bulk OUT flags is bulk OUT preparation is performed */
418141cc406Sopenharmony_ci      if (core_flags & CORE_BULK_OUT)
419141cc406Sopenharmony_ci        {
420141cc406Sopenharmony_ci          uint8_t bulk_flags = 0x24;
421141cc406Sopenharmony_ci          DBG (DBG_usb, "%s: USB-in-USB: sending bulk flags\n",
422141cc406Sopenharmony_ci               __func__);
423141cc406Sopenharmony_ci
424141cc406Sopenharmony_ci          ret = sanei_usb_control_msg (dn, USB_DIR_OUT | USB_TYPE_VENDOR,
425141cc406Sopenharmony_ci                                       0x0c, 0x83, 0x00,
426141cc406Sopenharmony_ci                                       sizeof (bulk_flags), &bulk_flags);
427141cc406Sopenharmony_ci          if (ret != SANE_STATUS_GOOD)
428141cc406Sopenharmony_ci            {
429141cc406Sopenharmony_ci              DBG (DBG_err, "%s: USB-in-USB: error sending bulk flags\n",
430141cc406Sopenharmony_ci                   __func__);
431141cc406Sopenharmony_ci              return ret;
432141cc406Sopenharmony_ci            }
433141cc406Sopenharmony_ci
434141cc406Sopenharmony_ci          /* USB-in-USB: checking confirmation for bulk flags */
435141cc406Sopenharmony_ci          ret = hp5590_get_ack (dn, proto_flags);
436141cc406Sopenharmony_ci          if (ret != SANE_STATUS_GOOD)
437141cc406Sopenharmony_ci            return ret;
438141cc406Sopenharmony_ci        }
439141cc406Sopenharmony_ci    }
440141cc406Sopenharmony_ci
441141cc406Sopenharmony_ci  return SANE_STATUS_GOOD;
442141cc406Sopenharmony_ci}
443141cc406Sopenharmony_ci
444141cc406Sopenharmony_ci/*******************************************************************************
445141cc406Sopenharmony_ci * USB-in-USB: verifies last command
446141cc406Sopenharmony_ci *
447141cc406Sopenharmony_ci * Parameters
448141cc406Sopenharmony_ci * dn - sanei_usb device descriptor
449141cc406Sopenharmony_ci * cmd - command to verify
450141cc406Sopenharmony_ci *
451141cc406Sopenharmony_ci * Returns
452141cc406Sopenharmony_ci * SANE_STATUS_GOOD - command verified successfully and CORE is ready
453141cc406Sopenharmony_ci * SANE_STATUS_IO_ERROR - command verification failed
454141cc406Sopenharmony_ci * SANE_STATUS_DEVICE_BUSY - command verified successfully but CORE isn't ready
455141cc406Sopenharmony_ci * all other SANE_Status values - otherwise
456141cc406Sopenharmony_ci */
457141cc406Sopenharmony_cistatic SANE_Status
458141cc406Sopenharmony_cihp5590_verify_last_cmd (SANE_Int dn,
459141cc406Sopenharmony_ci                        enum proto_flags proto_flags,
460141cc406Sopenharmony_ci                        unsigned int cmd)
461141cc406Sopenharmony_ci{
462141cc406Sopenharmony_ci  uint16_t      verify_cmd;
463141cc406Sopenharmony_ci  unsigned int  last_cmd;
464141cc406Sopenharmony_ci  unsigned int  core_status;
465141cc406Sopenharmony_ci  SANE_Status   ret;
466141cc406Sopenharmony_ci
467141cc406Sopenharmony_ci  DBG (3, "%s: USB-in-USB: command verification requested\n",
468141cc406Sopenharmony_ci       __func__);
469141cc406Sopenharmony_ci
470141cc406Sopenharmony_ci  /* Read last command along with CORE status */
471141cc406Sopenharmony_ci  ret = hp5590_control_msg (dn,
472141cc406Sopenharmony_ci                            proto_flags,
473141cc406Sopenharmony_ci                            USB_DIR_IN,
474141cc406Sopenharmony_ci                            0x04, 0xc5, 0x00,
475141cc406Sopenharmony_ci                            (unsigned char *) &verify_cmd,
476141cc406Sopenharmony_ci                            sizeof (verify_cmd), CORE_NONE);
477141cc406Sopenharmony_ci  if (ret != SANE_STATUS_GOOD)
478141cc406Sopenharmony_ci    return ret;
479141cc406Sopenharmony_ci
480141cc406Sopenharmony_ci  verify_cmd = le16toh (verify_cmd); /* Response is LSB first */
481141cc406Sopenharmony_ci
482141cc406Sopenharmony_ci  /* Last command - minor byte */
483141cc406Sopenharmony_ci  last_cmd = verify_cmd & 0xff;
484141cc406Sopenharmony_ci  /* CORE status - major byte */
485141cc406Sopenharmony_ci  core_status = (verify_cmd & 0xff00) >> 8;
486141cc406Sopenharmony_ci
487141cc406Sopenharmony_ci  /* Verify last command */
488141cc406Sopenharmony_ci  DBG (DBG_usb, "%s: USB-in-USB: command verification %04x, "
489141cc406Sopenharmony_ci       "last command: %04x, core status: %04x\n",
490141cc406Sopenharmony_ci       __func__, verify_cmd, last_cmd, core_status);
491141cc406Sopenharmony_ci  if ((cmd & 0x00ff) != last_cmd)
492141cc406Sopenharmony_ci    {
493141cc406Sopenharmony_ci      DBG (DBG_err, "%s: USB-in-USB: command verification failed: "
494141cc406Sopenharmony_ci           "expected 0x%04x, got 0x%04x\n",
495141cc406Sopenharmony_ci           __func__, cmd, last_cmd);
496141cc406Sopenharmony_ci      return SANE_STATUS_IO_ERROR;
497141cc406Sopenharmony_ci    }
498141cc406Sopenharmony_ci
499141cc406Sopenharmony_ci  DBG (DBG_usb, "%s: USB-in-USB: command verified successfully\n",
500141cc406Sopenharmony_ci       __func__);
501141cc406Sopenharmony_ci
502141cc406Sopenharmony_ci  /* Return value depends on CORE status */
503141cc406Sopenharmony_ci  return core_status & CORE_FLAG_NOT_READY ?
504141cc406Sopenharmony_ci    SANE_STATUS_DEVICE_BUSY : SANE_STATUS_GOOD;
505141cc406Sopenharmony_ci}
506141cc406Sopenharmony_ci
507141cc406Sopenharmony_ci/*******************************************************************************
508141cc406Sopenharmony_ci * USB-in-USB: send command (convenience wrapper around hp5590_control_msg())
509141cc406Sopenharmony_ci *
510141cc406Sopenharmony_ci * Parameters
511141cc406Sopenharmony_ci * dn - sanei_usb device descriptor
512141cc406Sopenharmony_ci * requesttype, request, value, index - their meanings are similar to
513141cc406Sopenharmony_ci * sanei_control_msg()
514141cc406Sopenharmony_ci * bytes - pointer to data buffer
515141cc406Sopenharmony_ci * size - size of data
516141cc406Sopenharmony_ci * core_flags -
517141cc406Sopenharmony_ci *  CORE_NONE - no CORE operation will be performed
518141cc406Sopenharmony_ci *  CORE_DATA - operation on CORE data will be performed
519141cc406Sopenharmony_ci *  CORE_BULK_IN - preparation for bulk IN transfer (not used yet)
520141cc406Sopenharmony_ci *  CORE_BULK_OUT - preparation for bulk OUT transfer
521141cc406Sopenharmony_ci *
522141cc406Sopenharmony_ci * Returns
523141cc406Sopenharmony_ci * SANE_STATUS_GOOD - command was sent (and possible verified) w/o any errors
524141cc406Sopenharmony_ci * all other SANE_Status values - otherwise
525141cc406Sopenharmony_ci */
526141cc406Sopenharmony_cistatic SANE_Status
527141cc406Sopenharmony_cihp5590_cmd (SANE_Int dn,
528141cc406Sopenharmony_ci            enum proto_flags proto_flags,
529141cc406Sopenharmony_ci            unsigned int flags,
530141cc406Sopenharmony_ci            unsigned int cmd, unsigned char *data, unsigned int size,
531141cc406Sopenharmony_ci            unsigned int core_flags)
532141cc406Sopenharmony_ci{
533141cc406Sopenharmony_ci  SANE_Status ret;
534141cc406Sopenharmony_ci
535141cc406Sopenharmony_ci  DBG (3, "%s: USB-in-USB: command : %04x\n", __func__, cmd);
536141cc406Sopenharmony_ci
537141cc406Sopenharmony_ci  ret = hp5590_control_msg (dn,
538141cc406Sopenharmony_ci                            proto_flags,
539141cc406Sopenharmony_ci                            flags & CMD_IN ? USB_DIR_IN : USB_DIR_OUT,
540141cc406Sopenharmony_ci                            0x04, cmd, 0x00, data, size, core_flags);
541141cc406Sopenharmony_ci  if (ret != SANE_STATUS_GOOD)
542141cc406Sopenharmony_ci    return ret;
543141cc406Sopenharmony_ci
544141cc406Sopenharmony_ci  ret = SANE_STATUS_GOOD;
545141cc406Sopenharmony_ci  /* Verify last command if requested */
546141cc406Sopenharmony_ci  if (flags & CMD_VERIFY)
547141cc406Sopenharmony_ci    {
548141cc406Sopenharmony_ci      ret = hp5590_verify_last_cmd (dn, proto_flags, cmd);
549141cc406Sopenharmony_ci    }
550141cc406Sopenharmony_ci
551141cc406Sopenharmony_ci  return ret;
552141cc406Sopenharmony_ci}
553141cc406Sopenharmony_ci
554141cc406Sopenharmony_ci/*******************************************************************************
555141cc406Sopenharmony_ci * USB-in-USB: initialized bulk read state
556141cc406Sopenharmony_ci *
557141cc406Sopenharmony_ci * Parameters
558141cc406Sopenharmony_ci * state - pointer to a pointer for initialized state
559141cc406Sopenharmony_ci *
560141cc406Sopenharmony_ci * Returns
561141cc406Sopenharmony_ci * SANE_STATUS_GOOD - if state was initialized successfully
562141cc406Sopenharmony_ci * SANE_STATUS_NO_MEM - memory allocation failed
563141cc406Sopenharmony_ci */
564141cc406Sopenharmony_cistatic SANE_Status
565141cc406Sopenharmony_cihp5590_low_init_bulk_read_state (void **state)
566141cc406Sopenharmony_ci{
567141cc406Sopenharmony_ci  struct bulk_read_state *bulk_read_state;
568141cc406Sopenharmony_ci
569141cc406Sopenharmony_ci  DBG (3, "%s: USB-in-USB: initializing bulk read state\n", __func__);
570141cc406Sopenharmony_ci
571141cc406Sopenharmony_ci  hp5590_low_assert (state != NULL);
572141cc406Sopenharmony_ci
573141cc406Sopenharmony_ci  bulk_read_state = malloc (sizeof (struct bulk_read_state));
574141cc406Sopenharmony_ci  if (!bulk_read_state)
575141cc406Sopenharmony_ci    return SANE_STATUS_NO_MEM;
576141cc406Sopenharmony_ci  memset (bulk_read_state, 0, sizeof (struct bulk_read_state));
577141cc406Sopenharmony_ci
578141cc406Sopenharmony_ci  bulk_read_state->buffer = malloc (ALLOCATE_BULK_READ_PAGES
579141cc406Sopenharmony_ci                                    * BULK_READ_PAGE_SIZE);
580141cc406Sopenharmony_ci  if (!bulk_read_state->buffer)
581141cc406Sopenharmony_ci    {
582141cc406Sopenharmony_ci      DBG (DBG_err, "%s: Memory allocation failed for %u bytes\n",
583141cc406Sopenharmony_ci           __func__, ALLOCATE_BULK_READ_PAGES * BULK_READ_PAGE_SIZE);
584141cc406Sopenharmony_ci      return SANE_STATUS_NO_MEM;
585141cc406Sopenharmony_ci    }
586141cc406Sopenharmony_ci  bulk_read_state->buffer_size = ALLOCATE_BULK_READ_PAGES
587141cc406Sopenharmony_ci                                 * BULK_READ_PAGE_SIZE;
588141cc406Sopenharmony_ci  bulk_read_state->bytes_available = 0;
589141cc406Sopenharmony_ci  bulk_read_state->buffer_out_ptr = bulk_read_state->buffer;
590141cc406Sopenharmony_ci  bulk_read_state->buffer_in_ptr = bulk_read_state->buffer;
591141cc406Sopenharmony_ci  bulk_read_state->total_pages = 0;
592141cc406Sopenharmony_ci  bulk_read_state->buffer_end_ptr = bulk_read_state->buffer
593141cc406Sopenharmony_ci                                    + bulk_read_state->buffer_size;
594141cc406Sopenharmony_ci  bulk_read_state->initialized = 1;
595141cc406Sopenharmony_ci
596141cc406Sopenharmony_ci  *state = bulk_read_state;
597141cc406Sopenharmony_ci  return SANE_STATUS_GOOD;
598141cc406Sopenharmony_ci}
599141cc406Sopenharmony_ci
600141cc406Sopenharmony_ci/*******************************************************************************
601141cc406Sopenharmony_ci * USB-in-USB: free bulk read state
602141cc406Sopenharmony_ci *
603141cc406Sopenharmony_ci * Parameters
604141cc406Sopenharmony_ci * state - pointer to a pointer to bulk read state
605141cc406Sopenharmony_ci *
606141cc406Sopenharmony_ci * Returns
607141cc406Sopenharmony_ci * SANE_STATUS_GOOD - bulk read state freed successfully
608141cc406Sopenharmony_ci */
609141cc406Sopenharmony_cistatic SANE_Status
610141cc406Sopenharmony_cihp5590_low_free_bulk_read_state (void **state)
611141cc406Sopenharmony_ci{
612141cc406Sopenharmony_ci  struct bulk_read_state *bulk_read_state;
613141cc406Sopenharmony_ci
614141cc406Sopenharmony_ci  DBG (3, "%s\n", __func__);
615141cc406Sopenharmony_ci
616141cc406Sopenharmony_ci  hp5590_low_assert (state != NULL);
617141cc406Sopenharmony_ci  /* Just return if NULL bulk read state was given */
618141cc406Sopenharmony_ci  if (*state == NULL)
619141cc406Sopenharmony_ci    return SANE_STATUS_GOOD;
620141cc406Sopenharmony_ci
621141cc406Sopenharmony_ci  bulk_read_state = *state;
622141cc406Sopenharmony_ci
623141cc406Sopenharmony_ci  DBG (3, "%s: USB-in-USB: freeing bulk read state\n", __func__);
624141cc406Sopenharmony_ci
625141cc406Sopenharmony_ci  free (bulk_read_state->buffer);
626141cc406Sopenharmony_ci  bulk_read_state->buffer = NULL;
627141cc406Sopenharmony_ci  free (bulk_read_state);
628141cc406Sopenharmony_ci  *state = NULL;
629141cc406Sopenharmony_ci
630141cc406Sopenharmony_ci  return SANE_STATUS_GOOD;
631141cc406Sopenharmony_ci}
632141cc406Sopenharmony_ci
633141cc406Sopenharmony_ci/* FIXME: perhaps needs to be converted to use hp5590_control_msg() */
634141cc406Sopenharmony_ci/*******************************************************************************
635141cc406Sopenharmony_ci * USB-in-USB: bulk read
636141cc406Sopenharmony_ci *
637141cc406Sopenharmony_ci * Parameters
638141cc406Sopenharmony_ci * dn - sanei_usb device descriptor
639141cc406Sopenharmony_ci * bytes - pointer to data buffer
640141cc406Sopenharmony_ci * size - size of data to read
641141cc406Sopenharmony_ci * state - pointer to initialized bulk read state structure
642141cc406Sopenharmony_ci */
643141cc406Sopenharmony_cistatic SANE_Status
644141cc406Sopenharmony_cihp5590_bulk_read (SANE_Int dn,
645141cc406Sopenharmony_ci                  enum proto_flags proto_flags,
646141cc406Sopenharmony_ci                  unsigned char *bytes, unsigned int size,
647141cc406Sopenharmony_ci                  void *state)
648141cc406Sopenharmony_ci{
649141cc406Sopenharmony_ci  struct usb_in_usb_bulk_setup  ctrl;
650141cc406Sopenharmony_ci  SANE_Status                   ret;
651141cc406Sopenharmony_ci  unsigned int                  next_pages;
652141cc406Sopenharmony_ci  uint8_t                       bulk_flags;
653141cc406Sopenharmony_ci  size_t                        next_portion;
654141cc406Sopenharmony_ci  struct bulk_read_state        *bulk_read_state;
655141cc406Sopenharmony_ci  unsigned int                  bytes_until_buffer_end;
656141cc406Sopenharmony_ci
657141cc406Sopenharmony_ci  DBG (3, "%s\n", __func__);
658141cc406Sopenharmony_ci
659141cc406Sopenharmony_ci  hp5590_low_assert (state != NULL);
660141cc406Sopenharmony_ci  hp5590_low_assert (bytes != NULL);
661141cc406Sopenharmony_ci
662141cc406Sopenharmony_ci  bulk_read_state = state;
663141cc406Sopenharmony_ci  if (bulk_read_state->initialized == 0)
664141cc406Sopenharmony_ci    {
665141cc406Sopenharmony_ci      DBG (DBG_err, "%s: USB-in-USB: bulk read state not initialized\n",
666141cc406Sopenharmony_ci           __func__);
667141cc406Sopenharmony_ci      return SANE_STATUS_INVAL;
668141cc406Sopenharmony_ci    }
669141cc406Sopenharmony_ci
670141cc406Sopenharmony_ci  memset (bytes, 0, size);
671141cc406Sopenharmony_ci
672141cc406Sopenharmony_ci  /* Check if requested data would fit into the buffer */
673141cc406Sopenharmony_ci  if (size > bulk_read_state->buffer_size)
674141cc406Sopenharmony_ci    {
675141cc406Sopenharmony_ci      DBG (DBG_err, "Data requested won't fit in the bulk read buffer "
676141cc406Sopenharmony_ci           "(requested: %u, buffer size: %u\n", size,
677141cc406Sopenharmony_ci           bulk_read_state->buffer_size);
678141cc406Sopenharmony_ci      return SANE_STATUS_NO_MEM;
679141cc406Sopenharmony_ci    }
680141cc406Sopenharmony_ci
681141cc406Sopenharmony_ci  /* Read data until requested size of data will be received */
682141cc406Sopenharmony_ci  while (bulk_read_state->bytes_available < size)
683141cc406Sopenharmony_ci    {
684141cc406Sopenharmony_ci      DBG (DBG_usb, "%s: USB-in-USB: not enough data in buffer available "
685141cc406Sopenharmony_ci           "(available: %u, requested: %u)\n",
686141cc406Sopenharmony_ci           __func__, bulk_read_state->bytes_available, size);
687141cc406Sopenharmony_ci
688141cc406Sopenharmony_ci      /* IMPORTANT! 'next_pages' means 'request and receive next_pages pages in
689141cc406Sopenharmony_ci       * one bulk transfer request '. Windows driver uses 4 pages between each
690141cc406Sopenharmony_ci       * request.  The more pages are received between requests the less the
691141cc406Sopenharmony_ci       * scanner does scan head re-positioning thus improving scanning speed.
692141cc406Sopenharmony_ci       * On the other hand, scanner expects that all of the requested pages
693141cc406Sopenharmony_ci       * will be received immediately after the request. In case when a
694141cc406Sopenharmony_ci       * frontend will have a delay between reads we will get bulk transfer
695141cc406Sopenharmony_ci       * timeout sooner or later.
696141cc406Sopenharmony_ci       * Having next_pages = 1 is the most safe case.
697141cc406Sopenharmony_ci       */
698141cc406Sopenharmony_ci      next_pages = 1;
699141cc406Sopenharmony_ci      /* Count all received pages to calculate when we will need to send
700141cc406Sopenharmony_ci       * another bulk request
701141cc406Sopenharmony_ci       */
702141cc406Sopenharmony_ci      bulk_read_state->total_pages++;
703141cc406Sopenharmony_ci      DBG (DBG_usb, "%s: USB-in-USB: total pages done: %u\n",
704141cc406Sopenharmony_ci           __func__, bulk_read_state->total_pages);
705141cc406Sopenharmony_ci
706141cc406Sopenharmony_ci      /* Send another bulk request for 'next_pages' before first
707141cc406Sopenharmony_ci       * page or next necessary one
708141cc406Sopenharmony_ci       */
709141cc406Sopenharmony_ci      if (   bulk_read_state->total_pages == 1
710141cc406Sopenharmony_ci          || bulk_read_state->total_pages % next_pages == 0)
711141cc406Sopenharmony_ci        {
712141cc406Sopenharmony_ci          /* Send bulk flags */
713141cc406Sopenharmony_ci          DBG (DBG_usb, "%s: USB-in-USB: sending USB-in-USB bulk flags\n",
714141cc406Sopenharmony_ci               __func__);
715141cc406Sopenharmony_ci          bulk_flags = 0x24;
716141cc406Sopenharmony_ci          ret = sanei_usb_control_msg (dn, USB_DIR_OUT | USB_TYPE_VENDOR,
717141cc406Sopenharmony_ci                                       0x0c, 0x83, 0x00,
718141cc406Sopenharmony_ci                                       sizeof (bulk_flags), &bulk_flags);
719141cc406Sopenharmony_ci          if (ret != SANE_STATUS_GOOD)
720141cc406Sopenharmony_ci            {
721141cc406Sopenharmony_ci              DBG (DBG_err, "%s: USB-in-USB: error sending bulk flags\n",
722141cc406Sopenharmony_ci                   __func__);
723141cc406Sopenharmony_ci              return ret;
724141cc406Sopenharmony_ci            }
725141cc406Sopenharmony_ci
726141cc406Sopenharmony_ci          /* USB-in-USB: checking confirmation for bulk flags\n" */
727141cc406Sopenharmony_ci          ret = hp5590_get_ack (dn, proto_flags);
728141cc406Sopenharmony_ci          if (ret != SANE_STATUS_GOOD)
729141cc406Sopenharmony_ci            return ret;
730141cc406Sopenharmony_ci
731141cc406Sopenharmony_ci          /* Prepare bulk read request */
732141cc406Sopenharmony_ci          memset (&ctrl, 0, sizeof (ctrl));
733141cc406Sopenharmony_ci          ctrl.bRequestType = 0x00;
734141cc406Sopenharmony_ci          ctrl.bEndpoint = 0x82;
735141cc406Sopenharmony_ci          ctrl.wLength = htons (next_pages);
736141cc406Sopenharmony_ci
737141cc406Sopenharmony_ci          /* Send bulk read request */
738141cc406Sopenharmony_ci          DBG (DBG_usb, "%s: USB-in-USB: sending control msg for bulk\n",
739141cc406Sopenharmony_ci               __func__);
740141cc406Sopenharmony_ci          ret = sanei_usb_control_msg (dn, USB_DIR_OUT | USB_TYPE_VENDOR,
741141cc406Sopenharmony_ci                                       0x04, 0x82, 0x00,
742141cc406Sopenharmony_ci                                       sizeof (ctrl),
743141cc406Sopenharmony_ci                                       (unsigned char *) &ctrl);
744141cc406Sopenharmony_ci          if (ret != SANE_STATUS_GOOD)
745141cc406Sopenharmony_ci            {
746141cc406Sopenharmony_ci              DBG (DBG_err, "%s: USB-in-USB: error sending control msg\n",
747141cc406Sopenharmony_ci                   __func__);
748141cc406Sopenharmony_ci              return ret;
749141cc406Sopenharmony_ci            }
750141cc406Sopenharmony_ci
751141cc406Sopenharmony_ci          /* USB-in-USB: checking if control msg was accepted */
752141cc406Sopenharmony_ci          ret = hp5590_get_ack (dn, proto_flags);
753141cc406Sopenharmony_ci          if (ret != SANE_STATUS_GOOD)
754141cc406Sopenharmony_ci            return ret;
755141cc406Sopenharmony_ci        }
756141cc406Sopenharmony_ci
757141cc406Sopenharmony_ci      next_portion = BULK_READ_PAGE_SIZE;
758141cc406Sopenharmony_ci      /* Check if next page will fit into the buffer */
759141cc406Sopenharmony_ci      if (bulk_read_state->buffer_size
760141cc406Sopenharmony_ci          - bulk_read_state->bytes_available < next_portion)
761141cc406Sopenharmony_ci        {
762141cc406Sopenharmony_ci          DBG (DBG_err, "%s: USB-in-USB: buffer too small\n", __func__);
763141cc406Sopenharmony_ci          return SANE_STATUS_NO_MEM;
764141cc406Sopenharmony_ci        }
765141cc406Sopenharmony_ci
766141cc406Sopenharmony_ci      /* Bulk read next page */
767141cc406Sopenharmony_ci      DBG (DBG_usb, "%s: USB-in-USB: bulk reading %lu bytes\n",
768141cc406Sopenharmony_ci           __func__, (u_long) next_portion);
769141cc406Sopenharmony_ci      ret = sanei_usb_read_bulk (dn,
770141cc406Sopenharmony_ci                                 bulk_read_state->buffer_in_ptr,
771141cc406Sopenharmony_ci                                 &next_portion);
772141cc406Sopenharmony_ci      if (ret != SANE_STATUS_GOOD)
773141cc406Sopenharmony_ci        {
774141cc406Sopenharmony_ci          if (ret == SANE_STATUS_EOF)
775141cc406Sopenharmony_ci            return ret;
776141cc406Sopenharmony_ci          DBG (DBG_err, "%s: USB-in-USB: error during bulk read: %s\n",
777141cc406Sopenharmony_ci               __func__, sane_strstatus (ret));
778141cc406Sopenharmony_ci          return ret;
779141cc406Sopenharmony_ci        }
780141cc406Sopenharmony_ci
781141cc406Sopenharmony_ci      /* Check if we received the same amount of data as requested */
782141cc406Sopenharmony_ci      if (next_portion != BULK_READ_PAGE_SIZE)
783141cc406Sopenharmony_ci        {
784141cc406Sopenharmony_ci          DBG (DBG_err, "%s: USB-in-USB: incomplete bulk read "
785141cc406Sopenharmony_ci               "(requested %u bytes, got %lu bytes)\n",
786141cc406Sopenharmony_ci               __func__, BULK_READ_PAGE_SIZE, (u_long) next_portion);
787141cc406Sopenharmony_ci          return SANE_STATUS_IO_ERROR;
788141cc406Sopenharmony_ci        }
789141cc406Sopenharmony_ci
790141cc406Sopenharmony_ci      /* Move pointers to the next position */
791141cc406Sopenharmony_ci      bulk_read_state->buffer_in_ptr += next_portion;
792141cc406Sopenharmony_ci
793141cc406Sopenharmony_ci      /* Check for the end of the buffer */
794141cc406Sopenharmony_ci      if (bulk_read_state->buffer_in_ptr > bulk_read_state->buffer_end_ptr)
795141cc406Sopenharmony_ci        {
796141cc406Sopenharmony_ci          DBG (DBG_err,
797141cc406Sopenharmony_ci               "%s: USB-in-USB: attempted to access over the end of buffer "
798141cc406Sopenharmony_ci               "(in_ptr: %p, end_ptr: %p, ptr: %p, buffer size: %u\n",
799141cc406Sopenharmony_ci               __func__, (void *) bulk_read_state->buffer_in_ptr,
800141cc406Sopenharmony_ci               (void *) bulk_read_state->buffer_end_ptr,
801141cc406Sopenharmony_ci               (void *) bulk_read_state->buffer,
802141cc406Sopenharmony_ci               bulk_read_state->buffer_size);
803141cc406Sopenharmony_ci          return SANE_STATUS_NO_MEM;
804141cc406Sopenharmony_ci        }
805141cc406Sopenharmony_ci
806141cc406Sopenharmony_ci      /* Check for buffer pointer wrapping */
807141cc406Sopenharmony_ci      if (bulk_read_state->buffer_in_ptr == bulk_read_state->buffer_end_ptr)
808141cc406Sopenharmony_ci        {
809141cc406Sopenharmony_ci          DBG (DBG_usb, "%s: USB-in-USB: buffer wrapped while writing\n",
810141cc406Sopenharmony_ci               __func__);
811141cc406Sopenharmony_ci          bulk_read_state->buffer_in_ptr = bulk_read_state->buffer;
812141cc406Sopenharmony_ci        }
813141cc406Sopenharmony_ci
814141cc406Sopenharmony_ci      /* Count the amount of data we read */
815141cc406Sopenharmony_ci      bulk_read_state->bytes_available += next_portion;
816141cc406Sopenharmony_ci    }
817141cc406Sopenharmony_ci
818141cc406Sopenharmony_ci  /* Transfer requested amount of data to the caller */
819141cc406Sopenharmony_ci  DBG (DBG_usb, "%s: USB-in-USB: data in bulk buffer is available "
820141cc406Sopenharmony_ci       "(requested %u bytes, available %u bytes)\n",
821141cc406Sopenharmony_ci       __func__, size, bulk_read_state->bytes_available);
822141cc406Sopenharmony_ci
823141cc406Sopenharmony_ci  /* Check for buffer pointer wrapping */
824141cc406Sopenharmony_ci  bytes_until_buffer_end = bulk_read_state->buffer_end_ptr
825141cc406Sopenharmony_ci    - bulk_read_state->buffer_out_ptr;
826141cc406Sopenharmony_ci  if (bytes_until_buffer_end <= size)
827141cc406Sopenharmony_ci    {
828141cc406Sopenharmony_ci      /* First buffer part */
829141cc406Sopenharmony_ci      DBG (DBG_usb, "%s: USB-in-USB: reached bulk read buffer end\n", __func__);
830141cc406Sopenharmony_ci      memcpy (bytes, bulk_read_state->buffer_out_ptr, bytes_until_buffer_end);
831141cc406Sopenharmony_ci      bulk_read_state->buffer_out_ptr = bulk_read_state->buffer;
832141cc406Sopenharmony_ci      /* And second part (if any) */
833141cc406Sopenharmony_ci      if (bytes_until_buffer_end < size)
834141cc406Sopenharmony_ci        {
835141cc406Sopenharmony_ci          DBG (DBG_usb, "%s: USB-in-USB: giving 2nd buffer part\n", __func__);
836141cc406Sopenharmony_ci          memcpy (bytes + bytes_until_buffer_end,
837141cc406Sopenharmony_ci                  bulk_read_state->buffer_out_ptr,
838141cc406Sopenharmony_ci                  size - bytes_until_buffer_end);
839141cc406Sopenharmony_ci          bulk_read_state->buffer_out_ptr += size - bytes_until_buffer_end;
840141cc406Sopenharmony_ci        }
841141cc406Sopenharmony_ci    }
842141cc406Sopenharmony_ci  else
843141cc406Sopenharmony_ci    {
844141cc406Sopenharmony_ci      /* The data is in one buffer part (w/o wrapping) */
845141cc406Sopenharmony_ci      memcpy (bytes, bulk_read_state->buffer_out_ptr, size);
846141cc406Sopenharmony_ci      bulk_read_state->buffer_out_ptr += size;
847141cc406Sopenharmony_ci      if (bulk_read_state->buffer_out_ptr == bulk_read_state->buffer_end_ptr)
848141cc406Sopenharmony_ci        {
849141cc406Sopenharmony_ci          DBG (DBG_usb, "%s: USB-in-USB: buffer wrapped while reading\n",
850141cc406Sopenharmony_ci               __func__);
851141cc406Sopenharmony_ci          bulk_read_state->buffer_out_ptr = bulk_read_state->buffer;
852141cc406Sopenharmony_ci        }
853141cc406Sopenharmony_ci    }
854141cc406Sopenharmony_ci
855141cc406Sopenharmony_ci  /* Count the amount of data transferred to the caller */
856141cc406Sopenharmony_ci  bulk_read_state->bytes_available -= size;
857141cc406Sopenharmony_ci
858141cc406Sopenharmony_ci  return SANE_STATUS_GOOD;
859141cc406Sopenharmony_ci}
860141cc406Sopenharmony_ci
861141cc406Sopenharmony_ci/*******************************************************************************
862141cc406Sopenharmony_ci * USB-in-USB: bulk write
863141cc406Sopenharmony_ci *
864141cc406Sopenharmony_ci * Parameters
865141cc406Sopenharmony_ci * dn - sanei_usb device descriptor
866141cc406Sopenharmony_ci * cmd - command for bulk write operation
867141cc406Sopenharmony_ci * bytes - pointer to data buffer
868141cc406Sopenharmony_ci * size - size of data
869141cc406Sopenharmony_ci *
870141cc406Sopenharmony_ci * Returns
871141cc406Sopenharmony_ci * SANE_STATUS_GOOD - all data transferred successfully
872141cc406Sopenharmony_ci * all other SANE_Status value - otherwise
873141cc406Sopenharmony_ci */
874141cc406Sopenharmony_cistatic SANE_Status
875141cc406Sopenharmony_cihp5590_bulk_write (SANE_Int dn,
876141cc406Sopenharmony_ci                   enum proto_flags proto_flags,
877141cc406Sopenharmony_ci                   int cmd, unsigned char *bytes,
878141cc406Sopenharmony_ci                   unsigned int size)
879141cc406Sopenharmony_ci{
880141cc406Sopenharmony_ci  struct usb_in_usb_bulk_setup  ctrl;
881141cc406Sopenharmony_ci  SANE_Status                   ret;
882141cc406Sopenharmony_ci  struct bulk_size              bulk_size;
883141cc406Sopenharmony_ci
884141cc406Sopenharmony_ci  unsigned int len;
885141cc406Sopenharmony_ci  unsigned char *ptr;
886141cc406Sopenharmony_ci  size_t next_portion;
887141cc406Sopenharmony_ci
888141cc406Sopenharmony_ci  DBG (3, "%s: USB-in-USB: command: %04x, size %u\n", __func__, cmd,
889141cc406Sopenharmony_ci       size);
890141cc406Sopenharmony_ci
891141cc406Sopenharmony_ci  hp5590_low_assert (bytes != NULL);
892141cc406Sopenharmony_ci
893141cc406Sopenharmony_ci  /* Prepare bulk write request */
894141cc406Sopenharmony_ci  memset (&bulk_size, 0, sizeof (bulk_size));
895141cc406Sopenharmony_ci  /* Counted in page size */
896141cc406Sopenharmony_ci  bulk_size.size = size / BULK_WRITE_PAGE_SIZE;
897141cc406Sopenharmony_ci
898141cc406Sopenharmony_ci  /* Send bulk write request */
899141cc406Sopenharmony_ci  DBG (3, "%s: USB-in-USB: total %u pages (each of %u bytes)\n",
900141cc406Sopenharmony_ci       __func__, bulk_size.size, BULK_WRITE_PAGE_SIZE);
901141cc406Sopenharmony_ci  ret = hp5590_control_msg (dn,
902141cc406Sopenharmony_ci                            proto_flags,
903141cc406Sopenharmony_ci                            USB_DIR_OUT,
904141cc406Sopenharmony_ci                            0x04, cmd, 0,
905141cc406Sopenharmony_ci                            (unsigned char *) &bulk_size, sizeof (bulk_size),
906141cc406Sopenharmony_ci                            CORE_DATA | CORE_BULK_OUT);
907141cc406Sopenharmony_ci  if (ret != SANE_STATUS_GOOD)
908141cc406Sopenharmony_ci    return ret;
909141cc406Sopenharmony_ci
910141cc406Sopenharmony_ci  len = size;
911141cc406Sopenharmony_ci  ptr = bytes;
912141cc406Sopenharmony_ci
913141cc406Sopenharmony_ci  /* Send all data in pages */
914141cc406Sopenharmony_ci  while (len)
915141cc406Sopenharmony_ci    {
916141cc406Sopenharmony_ci      next_portion = BULK_WRITE_PAGE_SIZE;
917141cc406Sopenharmony_ci      if (len < next_portion)
918141cc406Sopenharmony_ci        next_portion = len;
919141cc406Sopenharmony_ci
920141cc406Sopenharmony_ci      DBG (3, "%s: USB-in-USB: next portion %lu bytes\n",
921141cc406Sopenharmony_ci           __func__, (u_long) next_portion);
922141cc406Sopenharmony_ci
923141cc406Sopenharmony_ci      /* Prepare bulk write request */
924141cc406Sopenharmony_ci      memset (&ctrl, 0, sizeof (ctrl));
925141cc406Sopenharmony_ci      ctrl.bRequestType = 0x01;
926141cc406Sopenharmony_ci      ctrl.bEndpoint = 0x82;
927141cc406Sopenharmony_ci      ctrl.wLength = htons (next_portion);
928141cc406Sopenharmony_ci
929141cc406Sopenharmony_ci      /* Send bulk write request */
930141cc406Sopenharmony_ci      ret = sanei_usb_control_msg (dn, USB_DIR_OUT | USB_TYPE_VENDOR,
931141cc406Sopenharmony_ci                                   0x04, 0x82, 0,
932141cc406Sopenharmony_ci                                   sizeof (ctrl), (unsigned char *) &ctrl);
933141cc406Sopenharmony_ci      if (ret != SANE_STATUS_GOOD)
934141cc406Sopenharmony_ci        return ret;
935141cc406Sopenharmony_ci
936141cc406Sopenharmony_ci      /* USB-in-USB: checking if command was accepted */
937141cc406Sopenharmony_ci      ret = hp5590_get_ack (dn, proto_flags);
938141cc406Sopenharmony_ci      if (ret != SANE_STATUS_GOOD)
939141cc406Sopenharmony_ci        return ret;
940141cc406Sopenharmony_ci
941141cc406Sopenharmony_ci      /* Write bulk data */
942141cc406Sopenharmony_ci      DBG (3, "%s: USB-in-USB: bulk writing %lu bytes\n",
943141cc406Sopenharmony_ci           __func__, (u_long) next_portion);
944141cc406Sopenharmony_ci      ret = sanei_usb_write_bulk (dn, ptr, &next_portion);
945141cc406Sopenharmony_ci      if (ret != SANE_STATUS_GOOD)
946141cc406Sopenharmony_ci        {
947141cc406Sopenharmony_ci          /* Treast EOF as successful result */
948141cc406Sopenharmony_ci          if (ret == SANE_STATUS_EOF)
949141cc406Sopenharmony_ci            break;
950141cc406Sopenharmony_ci          DBG (DBG_err, "%s: USB-in-USB: error during bulk write: %s\n",
951141cc406Sopenharmony_ci               __func__, sane_strstatus (ret));
952141cc406Sopenharmony_ci          return ret;
953141cc406Sopenharmony_ci        }
954141cc406Sopenharmony_ci
955141cc406Sopenharmony_ci      /* Move to the next page */
956141cc406Sopenharmony_ci      len -= next_portion;
957141cc406Sopenharmony_ci      ptr += next_portion;
958141cc406Sopenharmony_ci    }
959141cc406Sopenharmony_ci
960141cc406Sopenharmony_ci  /* Verify bulk command */
961141cc406Sopenharmony_ci  return hp5590_verify_last_cmd (dn, proto_flags, cmd);
962141cc406Sopenharmony_ci}
963141cc406Sopenharmony_ci/* vim: sw=2 ts=8
964141cc406Sopenharmony_ci */
965