1141cc406Sopenharmony_ci/* sane - Scanner Access Now Easy.
2141cc406Sopenharmony_ci   Copyright (C) 2001 - 2005 Henning Meier-Geinitz
3141cc406Sopenharmony_ci   Copyright (C) 2001 Frank Zago (sanei_usb_control_msg)
4141cc406Sopenharmony_ci   Copyright (C) 2003 Rene Rebe (sanei_read_int,sanei_set_timeout)
5141cc406Sopenharmony_ci   Copyright (C) 2005 Paul Smedley <paul@smedley.info> (OS/2 usbcalls)
6141cc406Sopenharmony_ci   Copyright (C) 2008 m. allan noah (bus rescan support, sanei_usb_clear_halt)
7141cc406Sopenharmony_ci   Copyright (C) 2009 Julien BLACHE <jb@jblache.org> (libusb-1.0)
8141cc406Sopenharmony_ci   Copyright (C) 2011 Reinhold Kainhofer <reinhold@kainhofer.com> (sanei_usb_set_endpoint)
9141cc406Sopenharmony_ci   This file is part of the SANE package.
10141cc406Sopenharmony_ci
11141cc406Sopenharmony_ci   This program is free software; you can redistribute it and/or
12141cc406Sopenharmony_ci   modify it under the terms of the GNU General Public License as
13141cc406Sopenharmony_ci   published by the Free Software Foundation; either version 2 of the
14141cc406Sopenharmony_ci   License, or (at your option) any later version.
15141cc406Sopenharmony_ci
16141cc406Sopenharmony_ci   This program is distributed in the hope that it will be useful, but
17141cc406Sopenharmony_ci   WITHOUT ANY WARRANTY; without even the implied warranty of
18141cc406Sopenharmony_ci   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19141cc406Sopenharmony_ci   General Public License for more details.
20141cc406Sopenharmony_ci
21141cc406Sopenharmony_ci   You should have received a copy of the GNU General Public License
22141cc406Sopenharmony_ci   along with this program.  If not, see <https://www.gnu.org/licenses/>.
23141cc406Sopenharmony_ci
24141cc406Sopenharmony_ci   As a special exception, the authors of SANE give permission for
25141cc406Sopenharmony_ci   additional uses of the libraries contained in this release of SANE.
26141cc406Sopenharmony_ci
27141cc406Sopenharmony_ci   The exception is that, if you link a SANE library with other files
28141cc406Sopenharmony_ci   to produce an executable, this does not by itself cause the
29141cc406Sopenharmony_ci   resulting executable to be covered by the GNU General Public
30141cc406Sopenharmony_ci   License.  Your use of that executable is in no way restricted on
31141cc406Sopenharmony_ci   account of linking the SANE library code into it.
32141cc406Sopenharmony_ci
33141cc406Sopenharmony_ci   This exception does not, however, invalidate any other reasons why
34141cc406Sopenharmony_ci   the executable file might be covered by the GNU General Public
35141cc406Sopenharmony_ci   License.
36141cc406Sopenharmony_ci
37141cc406Sopenharmony_ci   If you submit changes to SANE to the maintainers to be included in
38141cc406Sopenharmony_ci   a subsequent release, you agree by submitting the changes that
39141cc406Sopenharmony_ci   those changes may be distributed with this exception intact.
40141cc406Sopenharmony_ci
41141cc406Sopenharmony_ci   If you write modifications of your own for SANE, it is your choice
42141cc406Sopenharmony_ci   whether to permit this exception to apply to your modifications.
43141cc406Sopenharmony_ci   If you do not wish that, delete this exception notice.
44141cc406Sopenharmony_ci
45141cc406Sopenharmony_ci   This file provides a generic USB interface.  */
46141cc406Sopenharmony_ci
47141cc406Sopenharmony_ci#include "../include/sane/config.h"
48141cc406Sopenharmony_ci
49141cc406Sopenharmony_ci#ifdef HAVE_STDINT_H
50141cc406Sopenharmony_ci# include <stdint.h>
51141cc406Sopenharmony_ci#endif
52141cc406Sopenharmony_ci#include <stdlib.h>
53141cc406Sopenharmony_ci#include <ctype.h>
54141cc406Sopenharmony_ci#include <sys/types.h>
55141cc406Sopenharmony_ci#include <sys/stat.h>
56141cc406Sopenharmony_ci#include <fcntl.h>
57141cc406Sopenharmony_ci#include <errno.h>
58141cc406Sopenharmony_ci#include <string.h>
59141cc406Sopenharmony_ci#include <unistd.h>
60141cc406Sopenharmony_ci#ifdef HAVE_SYS_IOCTL_H
61141cc406Sopenharmony_ci#include <sys/ioctl.h>
62141cc406Sopenharmony_ci#endif
63141cc406Sopenharmony_ci#include <stdio.h>
64141cc406Sopenharmony_ci#include <dirent.h>
65141cc406Sopenharmony_ci#include <time.h>
66141cc406Sopenharmony_ci
67141cc406Sopenharmony_ci#if WITH_USB_RECORD_REPLAY
68141cc406Sopenharmony_ci#include <libxml/tree.h>
69141cc406Sopenharmony_ci#endif
70141cc406Sopenharmony_ci
71141cc406Sopenharmony_ci#ifdef HAVE_RESMGR
72141cc406Sopenharmony_ci#include <resmgr.h>
73141cc406Sopenharmony_ci#endif
74141cc406Sopenharmony_ci
75141cc406Sopenharmony_ci#ifdef HAVE_LIBUSB_LEGACY
76141cc406Sopenharmony_ci#ifdef HAVE_LUSB0_USB_H
77141cc406Sopenharmony_ci#include <lusb0_usb.h>
78141cc406Sopenharmony_ci#else
79141cc406Sopenharmony_ci#include <usb.h>
80141cc406Sopenharmony_ci#endif
81141cc406Sopenharmony_ci#endif /* HAVE_LIBUSB_LEGACY */
82141cc406Sopenharmony_ci
83141cc406Sopenharmony_ci#ifdef HAVE_LIBUSB
84141cc406Sopenharmony_ci#include <libusb.h>
85141cc406Sopenharmony_ci#endif /* HAVE_LIBUSB */
86141cc406Sopenharmony_ci
87141cc406Sopenharmony_ci#ifdef HAVE_USB_MANAGER
88141cc406Sopenharmony_ci#include "usb_manager.h"
89141cc406Sopenharmony_ci#endif /* HAVE_USB_MANAGER */
90141cc406Sopenharmony_ci
91141cc406Sopenharmony_ci#ifdef HAVE_USBCALLS
92141cc406Sopenharmony_ci#include <usb.h>
93141cc406Sopenharmony_ci#include <os2.h>
94141cc406Sopenharmony_ci#include <usbcalls.h>
95141cc406Sopenharmony_ci#define MAX_RW 64000
96141cc406Sopenharmony_cistatic int usbcalls_timeout = 30 * 1000;	/* 30 seconds */
97141cc406Sopenharmony_ciUSBHANDLE dh;
98141cc406Sopenharmony_ciPHEV pUsbIrqStartHev=NULL;
99141cc406Sopenharmony_ci
100141cc406Sopenharmony_cistatic
101141cc406Sopenharmony_cistruct usb_descriptor_header *
102141cc406Sopenharmony_ciGetNextDescriptor( struct usb_descriptor_header *currHead, UCHAR *lastBytePtr)
103141cc406Sopenharmony_ci{
104141cc406Sopenharmony_ci  UCHAR    *currBytePtr, *nextBytePtr;
105141cc406Sopenharmony_ci
106141cc406Sopenharmony_ci  if (!currHead->bLength)
107141cc406Sopenharmony_ci     return (NULL);
108141cc406Sopenharmony_ci  currBytePtr=(UCHAR *)currHead;
109141cc406Sopenharmony_ci  nextBytePtr=currBytePtr+currHead->bLength;
110141cc406Sopenharmony_ci  if (nextBytePtr>=lastBytePtr)
111141cc406Sopenharmony_ci     return (NULL);
112141cc406Sopenharmony_ci  return ((struct usb_descriptor_header*)nextBytePtr);
113141cc406Sopenharmony_ci}
114141cc406Sopenharmony_ci#endif /* HAVE_USBCALLS */
115141cc406Sopenharmony_ci
116141cc406Sopenharmony_ci#if (defined (__FreeBSD__) && (__FreeBSD_version < 800064))
117141cc406Sopenharmony_ci#include <sys/param.h>
118141cc406Sopenharmony_ci#include <dev/usb/usb.h>
119141cc406Sopenharmony_ci#endif /* __FreeBSD__ */
120141cc406Sopenharmony_ci#if defined (__DragonFly__)
121141cc406Sopenharmony_ci#include <bus/usb/usb.h>
122141cc406Sopenharmony_ci#endif
123141cc406Sopenharmony_ci
124141cc406Sopenharmony_ci#define BACKEND_NAME	sanei_usb
125141cc406Sopenharmony_ci#include "../include/sane/sane.h"
126141cc406Sopenharmony_ci#include "../include/sane/sanei_debug.h"
127141cc406Sopenharmony_ci#include "../include/sane/sanei_usb.h"
128141cc406Sopenharmony_ci#include "../include/sane/sanei_config.h"
129141cc406Sopenharmony_ci
130141cc406Sopenharmony_citypedef enum
131141cc406Sopenharmony_ci{
132141cc406Sopenharmony_ci  sanei_usb_method_scanner_driver = 0,	/* kernel scanner driver
133141cc406Sopenharmony_ci					   (Linux, BSD) */
134141cc406Sopenharmony_ci  sanei_usb_method_libusb,
135141cc406Sopenharmony_ci
136141cc406Sopenharmony_ci  sanei_usb_method_usbcalls,
137141cc406Sopenharmony_ci
138141cc406Sopenharmony_ci  sanei_usb_method_usb_manager
139141cc406Sopenharmony_ci}
140141cc406Sopenharmony_cisanei_usb_access_method_type;
141141cc406Sopenharmony_ci
142141cc406Sopenharmony_citypedef struct
143141cc406Sopenharmony_ci{
144141cc406Sopenharmony_ci  SANE_Bool open;
145141cc406Sopenharmony_ci  sanei_usb_access_method_type method;
146141cc406Sopenharmony_ci  int fd;
147141cc406Sopenharmony_ci  SANE_String devname;
148141cc406Sopenharmony_ci  SANE_Int vendor;
149141cc406Sopenharmony_ci  SANE_Int product;
150141cc406Sopenharmony_ci  SANE_Int bulk_in_ep;
151141cc406Sopenharmony_ci  SANE_Int bulk_out_ep;
152141cc406Sopenharmony_ci  SANE_Int iso_in_ep;
153141cc406Sopenharmony_ci  SANE_Int iso_out_ep;
154141cc406Sopenharmony_ci  SANE_Int int_in_ep;
155141cc406Sopenharmony_ci  SANE_Int int_out_ep;
156141cc406Sopenharmony_ci  SANE_Int control_in_ep;
157141cc406Sopenharmony_ci  SANE_Int control_out_ep;
158141cc406Sopenharmony_ci  SANE_Int interface_nr;
159141cc406Sopenharmony_ci  SANE_Int alt_setting;
160141cc406Sopenharmony_ci  SANE_Int missing;
161141cc406Sopenharmony_ci#ifdef HAVE_LIBUSB_LEGACY
162141cc406Sopenharmony_ci  usb_dev_handle *libusb_handle;
163141cc406Sopenharmony_ci  struct usb_device *libusb_device;
164141cc406Sopenharmony_ci#endif /* HAVE_LIBUSB_LEGACY */
165141cc406Sopenharmony_ci#ifdef HAVE_LIBUSB
166141cc406Sopenharmony_ci  libusb_device *lu_device;
167141cc406Sopenharmony_ci  libusb_device_handle *lu_handle;
168141cc406Sopenharmony_ci#endif /* HAVE_LIBUSB */
169141cc406Sopenharmony_ci#ifdef HAVE_USB_MANAGER
170141cc406Sopenharmony_ci  usb_manager_device *usb_manager_device;
171141cc406Sopenharmony_ci  usb_manager_device_handle *usb_manager_handle;
172141cc406Sopenharmony_ci#endif /* HAVE_USB_MANAGER */
173141cc406Sopenharmony_ci
174141cc406Sopenharmony_ci}
175141cc406Sopenharmony_cidevice_list_type;
176141cc406Sopenharmony_ci
177141cc406Sopenharmony_ci/**
178141cc406Sopenharmony_ci * total number of devices that can be found at the same time */
179141cc406Sopenharmony_ci#define MAX_DEVICES 100
180141cc406Sopenharmony_ci
181141cc406Sopenharmony_ci/**
182141cc406Sopenharmony_ci * per-device information, using the functions' parameters dn as index */
183141cc406Sopenharmony_cistatic device_list_type devices[MAX_DEVICES];
184141cc406Sopenharmony_ci
185141cc406Sopenharmony_ci/**
186141cc406Sopenharmony_ci * total number of detected devices in devices array */
187141cc406Sopenharmony_cistatic int device_number=0;
188141cc406Sopenharmony_ci
189141cc406Sopenharmony_ci/**
190141cc406Sopenharmony_ci * count number of time sanei_usb has been initialized */
191141cc406Sopenharmony_cistatic int initialized=0;
192141cc406Sopenharmony_ci
193141cc406Sopenharmony_citypedef enum
194141cc406Sopenharmony_ci{
195141cc406Sopenharmony_ci  sanei_usb_testing_mode_disabled = 0,
196141cc406Sopenharmony_ci
197141cc406Sopenharmony_ci  sanei_usb_testing_mode_record, // records the communication with the slave
198141cc406Sopenharmony_ci                                 // but does not change the USB stack in any
199141cc406Sopenharmony_ci                                 // way
200141cc406Sopenharmony_ci  sanei_usb_testing_mode_replay,  // replays the communication with the scanner
201141cc406Sopenharmony_ci                                  // recorded earlier
202141cc406Sopenharmony_ci}
203141cc406Sopenharmony_cisanei_usb_testing_mode;
204141cc406Sopenharmony_ci
205141cc406Sopenharmony_ci// Whether testing mode has been enabled
206141cc406Sopenharmony_cistatic sanei_usb_testing_mode testing_mode = sanei_usb_testing_mode_disabled;
207141cc406Sopenharmony_ci
208141cc406Sopenharmony_ci#if WITH_USB_RECORD_REPLAY
209141cc406Sopenharmony_cistatic int testing_development_mode = 0;
210141cc406Sopenharmony_cistatic int testing_already_opened = 0;
211141cc406Sopenharmony_cistatic int testing_known_commands_input_failed = 0;
212141cc406Sopenharmony_cistatic unsigned testing_last_known_seq = 0;
213141cc406Sopenharmony_cistatic SANE_String testing_record_backend = NULL;
214141cc406Sopenharmony_cistatic xmlNode* testing_append_commands_node = NULL;
215141cc406Sopenharmony_ci
216141cc406Sopenharmony_ci// XML file from which we read testing data
217141cc406Sopenharmony_cistatic SANE_String testing_xml_path = NULL;
218141cc406Sopenharmony_cistatic xmlDoc* testing_xml_doc = NULL;
219141cc406Sopenharmony_cistatic xmlNode* testing_xml_next_tx_node = NULL;
220141cc406Sopenharmony_ci#endif // WITH_USB_RECORD_REPLAY
221141cc406Sopenharmony_ci
222141cc406Sopenharmony_ci#if defined(HAVE_LIBUSB_LEGACY) || defined(HAVE_LIBUSB)
223141cc406Sopenharmony_cistatic int libusb_timeout = 30 * 1000;	/* 30 seconds */
224141cc406Sopenharmony_ci#endif /* HAVE_LIBUSB_LEGACY */
225141cc406Sopenharmony_ci
226141cc406Sopenharmony_ci#ifdef HAVE_USB_MANAGER
227141cc406Sopenharmony_cistatic int usb_manager_timeout = 30 * 1000;	/* 30 seconds */
228141cc406Sopenharmony_ci#endif /* HAVE_USB_MANAGER */
229141cc406Sopenharmony_ci
230141cc406Sopenharmony_ci
231141cc406Sopenharmony_ci#ifdef HAVE_LIBUSB
232141cc406Sopenharmony_cistatic libusb_context *sanei_usb_ctx;
233141cc406Sopenharmony_ci#endif /* HAVE_LIBUSB */
234141cc406Sopenharmony_ci
235141cc406Sopenharmony_ci#ifdef HAVE_USB_MANAGER
236141cc406Sopenharmony_cistatic usb_manager_context *sanei_usb_ctx;
237141cc406Sopenharmony_ci#endif /* HAVE_USB_MANAGER */
238141cc406Sopenharmony_ci
239141cc406Sopenharmony_ci#if defined (__APPLE__)
240141cc406Sopenharmony_ci/* macOS won't configure several USB scanners (i.e. ScanSnap 300M) because their
241141cc406Sopenharmony_ci * descriptors are vendor specific.  As a result the device will get configured
242141cc406Sopenharmony_ci * later during sanei_usb_open making it safe to ignore the configuration check
243141cc406Sopenharmony_ci * on these platforms. */
244141cc406Sopenharmony_ci#define SANEI_ALLOW_UNCONFIGURED_DEVICES
245141cc406Sopenharmony_ci#endif
246141cc406Sopenharmony_ci
247141cc406Sopenharmony_ci#if defined (__linux__)
248141cc406Sopenharmony_ci/* From /usr/src/linux/driver/usb/scanner.h */
249141cc406Sopenharmony_ci#define SCANNER_IOCTL_VENDOR _IOR('U', 0x20, int)
250141cc406Sopenharmony_ci#define SCANNER_IOCTL_PRODUCT _IOR('U', 0x21, int)
251141cc406Sopenharmony_ci#define SCANNER_IOCTL_CTRLMSG _IOWR('U', 0x22, devrequest)
252141cc406Sopenharmony_ci/* Older (unofficial) IOCTL numbers for Linux < v2.4.13 */
253141cc406Sopenharmony_ci#define SCANNER_IOCTL_VENDOR_OLD _IOR('u', 0xa0, int)
254141cc406Sopenharmony_ci#define SCANNER_IOCTL_PRODUCT_OLD _IOR('u', 0xa1, int)
255141cc406Sopenharmony_ci
256141cc406Sopenharmony_ci/* From /usr/src/linux/include/linux/usb.h */
257141cc406Sopenharmony_citypedef struct
258141cc406Sopenharmony_ci{
259141cc406Sopenharmony_ci  unsigned char requesttype;
260141cc406Sopenharmony_ci  unsigned char request;
261141cc406Sopenharmony_ci  unsigned short value;
262141cc406Sopenharmony_ci  unsigned short index;
263141cc406Sopenharmony_ci  unsigned short length;
264141cc406Sopenharmony_ci}
265141cc406Sopenharmony_cidevrequest;
266141cc406Sopenharmony_ci
267141cc406Sopenharmony_ci/* From /usr/src/linux/driver/usb/scanner.h */
268141cc406Sopenharmony_cistruct ctrlmsg_ioctl
269141cc406Sopenharmony_ci{
270141cc406Sopenharmony_ci  devrequest req;
271141cc406Sopenharmony_ci  void *data;
272141cc406Sopenharmony_ci}
273141cc406Sopenharmony_cicmsg;
274141cc406Sopenharmony_ci#elif defined(__BEOS__)
275141cc406Sopenharmony_ci#include <drivers/USB_scanner.h>
276141cc406Sopenharmony_ci#include <kernel/OS.h>
277141cc406Sopenharmony_ci#endif /* __linux__ */
278141cc406Sopenharmony_ci
279141cc406Sopenharmony_ci/* Debug level from sanei_init_debug */
280141cc406Sopenharmony_cistatic SANE_Int debug_level;
281141cc406Sopenharmony_ci
282141cc406Sopenharmony_cistatic void
283141cc406Sopenharmony_ciprint_buffer (const SANE_Byte * buffer, SANE_Int size)
284141cc406Sopenharmony_ci{
285141cc406Sopenharmony_ci#define NUM_COLUMNS 16
286141cc406Sopenharmony_ci#define PRINT_BUFFER_SIZE (4 + NUM_COLUMNS * (3 + 1) + 1 + 1)
287141cc406Sopenharmony_ci  char line_str[PRINT_BUFFER_SIZE];
288141cc406Sopenharmony_ci  char *pp;
289141cc406Sopenharmony_ci  int column;
290141cc406Sopenharmony_ci  int line;
291141cc406Sopenharmony_ci
292141cc406Sopenharmony_ci  memset (line_str, 0, PRINT_BUFFER_SIZE);
293141cc406Sopenharmony_ci
294141cc406Sopenharmony_ci  for (line = 0; line < ((size + NUM_COLUMNS - 1) / NUM_COLUMNS); line++)
295141cc406Sopenharmony_ci    {
296141cc406Sopenharmony_ci      pp = line_str;
297141cc406Sopenharmony_ci      sprintf (pp, "%03X ", line * NUM_COLUMNS);
298141cc406Sopenharmony_ci      pp += 4;
299141cc406Sopenharmony_ci      for (column = 0; column < NUM_COLUMNS; column++)
300141cc406Sopenharmony_ci	{
301141cc406Sopenharmony_ci	  if ((line * NUM_COLUMNS + column) < size)
302141cc406Sopenharmony_ci	    sprintf (pp, "%02X ", buffer[line * NUM_COLUMNS + column]);
303141cc406Sopenharmony_ci	  else
304141cc406Sopenharmony_ci	    sprintf (pp, "   ");
305141cc406Sopenharmony_ci	  pp += 3;
306141cc406Sopenharmony_ci	}
307141cc406Sopenharmony_ci      for (column = 0; column < NUM_COLUMNS; column++)
308141cc406Sopenharmony_ci	{
309141cc406Sopenharmony_ci	  if ((line * NUM_COLUMNS + column) < size)
310141cc406Sopenharmony_ci	    sprintf (pp, "%c",
311141cc406Sopenharmony_ci		     (buffer[line * NUM_COLUMNS + column] < 127) &&
312141cc406Sopenharmony_ci		     (buffer[line * NUM_COLUMNS + column] > 31) ?
313141cc406Sopenharmony_ci		     buffer[line * NUM_COLUMNS + column] : '.');
314141cc406Sopenharmony_ci	  else
315141cc406Sopenharmony_ci	    sprintf (pp, " ");
316141cc406Sopenharmony_ci	  pp += 1;
317141cc406Sopenharmony_ci	}
318141cc406Sopenharmony_ci      DBG (11, "%s\n", line_str);
319141cc406Sopenharmony_ci    }
320141cc406Sopenharmony_ci}
321141cc406Sopenharmony_ci
322141cc406Sopenharmony_ci#if !defined(HAVE_LIBUSB_LEGACY) && !defined(HAVE_LIBUSB) && !defined(HAVE_USB_MANAGER)
323141cc406Sopenharmony_cistatic void
324141cc406Sopenharmony_cikernel_get_vendor_product (int fd, const char *name, int *vendorID, int *productID)
325141cc406Sopenharmony_ci{
326141cc406Sopenharmony_ci#if defined (__linux__)
327141cc406Sopenharmony_ci  /* read the vendor and product IDs via the IOCTLs */
328141cc406Sopenharmony_ci  if (ioctl (fd, SCANNER_IOCTL_VENDOR, vendorID) == -1)
329141cc406Sopenharmony_ci    {
330141cc406Sopenharmony_ci      if (ioctl (fd, SCANNER_IOCTL_VENDOR_OLD, vendorID) == -1)
331141cc406Sopenharmony_ci	DBG (3, "kernel_get_vendor_product: ioctl (vendor) "
332141cc406Sopenharmony_ci	     "of device %s failed: %s\n", name, strerror (errno));
333141cc406Sopenharmony_ci    }
334141cc406Sopenharmony_ci  if (ioctl (fd, SCANNER_IOCTL_PRODUCT, productID) == -1)
335141cc406Sopenharmony_ci    {
336141cc406Sopenharmony_ci      if (ioctl (fd, SCANNER_IOCTL_PRODUCT_OLD, productID) == -1)
337141cc406Sopenharmony_ci	DBG (3, "sanei_usb_get_vendor_product: ioctl (product) "
338141cc406Sopenharmony_ci	     "of device %s failed: %s\n", name, strerror (errno));
339141cc406Sopenharmony_ci    }
340141cc406Sopenharmony_ci#elif defined(__BEOS__)
341141cc406Sopenharmony_ci  {
342141cc406Sopenharmony_ci    uint16 vendor, product;
343141cc406Sopenharmony_ci    if (ioctl (fd, B_SCANNER_IOCTL_VENDOR, &vendor) != B_OK)
344141cc406Sopenharmony_ci      DBG (3, "kernel_get_vendor_product: ioctl (vendor) "
345141cc406Sopenharmony_ci	   "of device %d failed: %s\n", fd, strerror (errno));
346141cc406Sopenharmony_ci    if (ioctl (fd, B_SCANNER_IOCTL_PRODUCT, &product) != B_OK)
347141cc406Sopenharmony_ci      DBG (3, "sanei_usb_get_vendor_product: ioctl (product) "
348141cc406Sopenharmony_ci	   "of device %d failed: %s\n", fd, strerror (errno));
349141cc406Sopenharmony_ci    /* copy from 16 to 32 bit value */
350141cc406Sopenharmony_ci    *vendorID = vendor;
351141cc406Sopenharmony_ci    *productID = product;
352141cc406Sopenharmony_ci  }
353141cc406Sopenharmony_ci#elif (defined (__FreeBSD__) && __FreeBSD_version < 800064) || defined (__DragonFly__)
354141cc406Sopenharmony_ci  {
355141cc406Sopenharmony_ci    int controller;
356141cc406Sopenharmony_ci    int ctrl_fd;
357141cc406Sopenharmony_ci    char buf[40];
358141cc406Sopenharmony_ci    int dev;
359141cc406Sopenharmony_ci
360141cc406Sopenharmony_ci    for (controller = 0; ; controller++ )
361141cc406Sopenharmony_ci      {
362141cc406Sopenharmony_ci	snprintf (buf, sizeof (buf) - 1, "/dev/usb%d", controller);
363141cc406Sopenharmony_ci	ctrl_fd = open (buf, O_RDWR);
364141cc406Sopenharmony_ci
365141cc406Sopenharmony_ci	/* If we can not open the usb controller device, treat it
366141cc406Sopenharmony_ci	   as the end of controller devices */
367141cc406Sopenharmony_ci	if (ctrl_fd < 0)
368141cc406Sopenharmony_ci	  break;
369141cc406Sopenharmony_ci
370141cc406Sopenharmony_ci	/* Search for the scanner device on this bus */
371141cc406Sopenharmony_ci	for (dev = 1; dev < USB_MAX_DEVICES; dev++)
372141cc406Sopenharmony_ci	  {
373141cc406Sopenharmony_ci	    struct usb_device_info devInfo;
374141cc406Sopenharmony_ci	    devInfo.udi_addr = dev;
375141cc406Sopenharmony_ci
376141cc406Sopenharmony_ci	    if (ioctl (ctrl_fd, USB_DEVICEINFO, &devInfo) == -1)
377141cc406Sopenharmony_ci	      break; /* Treat this as the end of devices for this controller */
378141cc406Sopenharmony_ci
379141cc406Sopenharmony_ci	    snprintf (buf, sizeof (buf), "/dev/%s", devInfo.udi_devnames[0]);
380141cc406Sopenharmony_ci	    if (strncmp (buf, name, sizeof (buf)) == 0)
381141cc406Sopenharmony_ci	      {
382141cc406Sopenharmony_ci		*vendorID = (int) devInfo.udi_vendorNo;
383141cc406Sopenharmony_ci		*productID = (int) devInfo.udi_productNo;
384141cc406Sopenharmony_ci		close (ctrl_fd);
385141cc406Sopenharmony_ci		return;
386141cc406Sopenharmony_ci	      }
387141cc406Sopenharmony_ci	  }
388141cc406Sopenharmony_ci	close (ctrl_fd);
389141cc406Sopenharmony_ci      }
390141cc406Sopenharmony_ci    DBG (3, "kernel_get_vendor_product: Could not retrieve "
391141cc406Sopenharmony_ci	 "vendor/product ID from device %s\n", name);
392141cc406Sopenharmony_ci  }
393141cc406Sopenharmony_ci#endif /* defined (__linux__), defined(__BEOS__), ... */
394141cc406Sopenharmony_ci  /* put more os-dependant stuff ... */
395141cc406Sopenharmony_ci}
396141cc406Sopenharmony_ci#endif /* !defined(HAVE_LIBUSB_LEGACY) && !defined(HAVE_LIBUSB) && !defined(HAVE_USB_MANAGER) */
397141cc406Sopenharmony_ci
398141cc406Sopenharmony_ci/**
399141cc406Sopenharmony_ci * store the given device in device list if it isn't already
400141cc406Sopenharmony_ci * in it
401141cc406Sopenharmony_ci * @param device device to store if new
402141cc406Sopenharmony_ci */
403141cc406Sopenharmony_cistatic void
404141cc406Sopenharmony_cistore_device (device_list_type device)
405141cc406Sopenharmony_ci{
406141cc406Sopenharmony_ci  int i = 0;
407141cc406Sopenharmony_ci  int pos = -1;
408141cc406Sopenharmony_ci
409141cc406Sopenharmony_ci  /* if there are already some devices present, check against
410141cc406Sopenharmony_ci   * them and leave if an equal one is found */
411141cc406Sopenharmony_ci  for (i = 0; i < device_number; i++)
412141cc406Sopenharmony_ci    {
413141cc406Sopenharmony_ci      if (devices[i].method == device.method
414141cc406Sopenharmony_ci       && !strcmp (devices[i].devname, device.devname)
415141cc406Sopenharmony_ci       && devices[i].vendor == device.vendor
416141cc406Sopenharmony_ci       && devices[i].product == device.product)
417141cc406Sopenharmony_ci	{
418141cc406Sopenharmony_ci          /*
419141cc406Sopenharmony_ci          * Need to update the LibUSB device pointer, since it might
420141cc406Sopenharmony_ci          * have changed after the latest USB scan.
421141cc406Sopenharmony_ci          */
422141cc406Sopenharmony_ci#ifdef HAVE_LIBUSB_LEGACY
423141cc406Sopenharmony_ci          devices[i].libusb_device = device.libusb_device;
424141cc406Sopenharmony_ci#endif
425141cc406Sopenharmony_ci#ifdef HAVE_LIBUSB
426141cc406Sopenharmony_ci          devices[i].lu_device = device.lu_device;
427141cc406Sopenharmony_ci#endif
428141cc406Sopenharmony_ci#ifdef HAVE_USB_MANAGER
429141cc406Sopenharmony_ci          devices[i].usb_manager_device = device.usb_manager_device;
430141cc406Sopenharmony_ci#endif
431141cc406Sopenharmony_ci
432141cc406Sopenharmony_ci          devices[i].missing=0;
433141cc406Sopenharmony_ci	  DBG (3, "store_device: not storing device %s\n", device.devname);
434141cc406Sopenharmony_ci
435141cc406Sopenharmony_ci	  /* since devname has been created by strdup()
436141cc406Sopenharmony_ci	   * we have to free it to avoid leaking memory */
437141cc406Sopenharmony_ci	  free(device.devname);
438141cc406Sopenharmony_ci	  return;
439141cc406Sopenharmony_ci	}
440141cc406Sopenharmony_ci      if (devices[i].missing >= 2)
441141cc406Sopenharmony_ci        pos = i;
442141cc406Sopenharmony_ci    }
443141cc406Sopenharmony_ci
444141cc406Sopenharmony_ci  /* reuse slot of a device now missing */
445141cc406Sopenharmony_ci  if(pos > -1){
446141cc406Sopenharmony_ci    DBG (3, "store_device: overwrite dn %d with %s\n", pos, device.devname);
447141cc406Sopenharmony_ci    /* we reuse the slot used by a now missing device
448141cc406Sopenharmony_ci     * so we free the allocated memory for the missing one */
449141cc406Sopenharmony_ci    if (devices[pos].devname) {
450141cc406Sopenharmony_ci      free(devices[pos].devname);
451141cc406Sopenharmony_ci      devices[pos].devname = NULL;
452141cc406Sopenharmony_ci    }
453141cc406Sopenharmony_ci  }
454141cc406Sopenharmony_ci  else{
455141cc406Sopenharmony_ci    if(device_number >= MAX_DEVICES){
456141cc406Sopenharmony_ci      DBG (3, "store_device: no room for %s\n", device.devname);
457141cc406Sopenharmony_ci      return;
458141cc406Sopenharmony_ci    }
459141cc406Sopenharmony_ci    pos = device_number;
460141cc406Sopenharmony_ci    device_number++;
461141cc406Sopenharmony_ci    DBG (3, "store_device: add dn %d with %s\n", pos, device.devname);
462141cc406Sopenharmony_ci  }
463141cc406Sopenharmony_ci  memcpy (&(devices[pos]), &device, sizeof (device));
464141cc406Sopenharmony_ci  devices[pos].open = SANE_FALSE;
465141cc406Sopenharmony_ci}
466141cc406Sopenharmony_ci
467141cc406Sopenharmony_ci#ifdef HAVE_LIBUSB
468141cc406Sopenharmony_cistatic char *
469141cc406Sopenharmony_cisanei_libusb_strerror (int errcode)
470141cc406Sopenharmony_ci{
471141cc406Sopenharmony_ci  /* Error codes & descriptions from the libusb-1.0 documentation */
472141cc406Sopenharmony_ci
473141cc406Sopenharmony_ci  switch (errcode)
474141cc406Sopenharmony_ci    {
475141cc406Sopenharmony_ci      case LIBUSB_SUCCESS:
476141cc406Sopenharmony_ci	return "Success (no error)";
477141cc406Sopenharmony_ci
478141cc406Sopenharmony_ci      case LIBUSB_ERROR_IO:
479141cc406Sopenharmony_ci	return "Input/output error";
480141cc406Sopenharmony_ci
481141cc406Sopenharmony_ci      case LIBUSB_ERROR_INVALID_PARAM:
482141cc406Sopenharmony_ci	return "Invalid parameter";
483141cc406Sopenharmony_ci
484141cc406Sopenharmony_ci      case LIBUSB_ERROR_ACCESS:
485141cc406Sopenharmony_ci	return "Access denied (insufficient permissions)";
486141cc406Sopenharmony_ci
487141cc406Sopenharmony_ci      case LIBUSB_ERROR_NO_DEVICE:
488141cc406Sopenharmony_ci	return "No such device (it may have been disconnected)";
489141cc406Sopenharmony_ci
490141cc406Sopenharmony_ci      case LIBUSB_ERROR_NOT_FOUND:
491141cc406Sopenharmony_ci	return "Entity not found";
492141cc406Sopenharmony_ci
493141cc406Sopenharmony_ci      case LIBUSB_ERROR_BUSY:
494141cc406Sopenharmony_ci	return "Resource busy";
495141cc406Sopenharmony_ci
496141cc406Sopenharmony_ci      case LIBUSB_ERROR_TIMEOUT:
497141cc406Sopenharmony_ci	return "Operation timed out";
498141cc406Sopenharmony_ci
499141cc406Sopenharmony_ci      case LIBUSB_ERROR_OVERFLOW:
500141cc406Sopenharmony_ci	return "Overflow";
501141cc406Sopenharmony_ci
502141cc406Sopenharmony_ci      case LIBUSB_ERROR_PIPE:
503141cc406Sopenharmony_ci	return "Pipe error";
504141cc406Sopenharmony_ci
505141cc406Sopenharmony_ci      case LIBUSB_ERROR_INTERRUPTED:
506141cc406Sopenharmony_ci	return "System call interrupted (perhaps due to signal)";
507141cc406Sopenharmony_ci
508141cc406Sopenharmony_ci      case LIBUSB_ERROR_NO_MEM:
509141cc406Sopenharmony_ci	return "Insufficient memory";
510141cc406Sopenharmony_ci
511141cc406Sopenharmony_ci      case LIBUSB_ERROR_NOT_SUPPORTED:
512141cc406Sopenharmony_ci	return "Operation not supported or unimplemented on this platform";
513141cc406Sopenharmony_ci
514141cc406Sopenharmony_ci      case LIBUSB_ERROR_OTHER:
515141cc406Sopenharmony_ci	return "Other error";
516141cc406Sopenharmony_ci
517141cc406Sopenharmony_ci      default:
518141cc406Sopenharmony_ci	return "Unknown libusb-1.0 error code";
519141cc406Sopenharmony_ci    }
520141cc406Sopenharmony_ci}
521141cc406Sopenharmony_ci#endif /* HAVE_LIBUSB */
522141cc406Sopenharmony_ci
523141cc406Sopenharmony_ci#ifdef HAVE_USB_MANAGER
524141cc406Sopenharmony_cistatic char *
525141cc406Sopenharmony_cisanei_usb_manager_strerror (int errcode)
526141cc406Sopenharmony_ci{
527141cc406Sopenharmony_ci  /* Error codes & descriptions from the usb_manager documentation */
528141cc406Sopenharmony_ci
529141cc406Sopenharmony_ci  switch (errcode)
530141cc406Sopenharmony_ci    {
531141cc406Sopenharmony_ci      case USB_MANAGER_SUCCESS:
532141cc406Sopenharmony_ci	return "Success (no error)";
533141cc406Sopenharmony_ci
534141cc406Sopenharmony_ci      case USB_MANAGER_ERROR_IO:
535141cc406Sopenharmony_ci	return "Input/output error";
536141cc406Sopenharmony_ci
537141cc406Sopenharmony_ci      case USB_MANAGER_ERROR_INVALID_PARAM:
538141cc406Sopenharmony_ci	return "Invalid parameter";
539141cc406Sopenharmony_ci
540141cc406Sopenharmony_ci      case USB_MANAGER_ERROR_ACCESS:
541141cc406Sopenharmony_ci	return "Access denied (insufficient permissions)";
542141cc406Sopenharmony_ci
543141cc406Sopenharmony_ci      case USB_MANAGER_ERROR_NO_DEVICE:
544141cc406Sopenharmony_ci	return "No such device (it may have been disconnected)";
545141cc406Sopenharmony_ci
546141cc406Sopenharmony_ci      case USB_MANAGER_ERROR_NOT_FOUND:
547141cc406Sopenharmony_ci	return "Entity not found";
548141cc406Sopenharmony_ci
549141cc406Sopenharmony_ci      case USB_MANAGER_ERROR_BUSY:
550141cc406Sopenharmony_ci	return "Resource busy";
551141cc406Sopenharmony_ci
552141cc406Sopenharmony_ci      case USB_MANAGER_ERROR_TIMEOUT:
553141cc406Sopenharmony_ci	return "Operation timed out";
554141cc406Sopenharmony_ci
555141cc406Sopenharmony_ci      case USB_MANAGER_ERROR_OVERFLOW:
556141cc406Sopenharmony_ci	return "Overflow";
557141cc406Sopenharmony_ci
558141cc406Sopenharmony_ci      case USB_MANAGER_ERROR_PIPE:
559141cc406Sopenharmony_ci	return "Pipe error";
560141cc406Sopenharmony_ci
561141cc406Sopenharmony_ci      case USB_MANAGER_ERROR_INTERRUPTED:
562141cc406Sopenharmony_ci	return "System call interrupted (perhaps due to signal)";
563141cc406Sopenharmony_ci
564141cc406Sopenharmony_ci      case USB_MANAGER_ERROR_NO_MEM:
565141cc406Sopenharmony_ci	return "Insufficient memory";
566141cc406Sopenharmony_ci
567141cc406Sopenharmony_ci      case USB_MANAGER_ERROR_NOT_SUPPORTED:
568141cc406Sopenharmony_ci	return "Operation not supported or unimplemented on this platform";
569141cc406Sopenharmony_ci
570141cc406Sopenharmony_ci      case USB_MANAGER_ERROR_OTHER:
571141cc406Sopenharmony_ci	return "Other error";
572141cc406Sopenharmony_ci
573141cc406Sopenharmony_ci      default:
574141cc406Sopenharmony_ci	return "Unknown usb_manager error code";
575141cc406Sopenharmony_ci    }
576141cc406Sopenharmony_ci}
577141cc406Sopenharmony_ci#endif /* HAVE_USB_MANAGER */
578141cc406Sopenharmony_ci
579141cc406Sopenharmony_ci#if WITH_USB_RECORD_REPLAY
580141cc406Sopenharmony_ciSANE_Status sanei_usb_testing_enable_replay(SANE_String_Const path,
581141cc406Sopenharmony_ci                                            int development_mode)
582141cc406Sopenharmony_ci{
583141cc406Sopenharmony_ci  testing_mode = sanei_usb_testing_mode_replay;
584141cc406Sopenharmony_ci  testing_development_mode = development_mode;
585141cc406Sopenharmony_ci
586141cc406Sopenharmony_ci  // TODO: we'll leak if no one ever inits sane_usb properly
587141cc406Sopenharmony_ci  testing_xml_path = strdup(path);
588141cc406Sopenharmony_ci  testing_xml_doc = xmlReadFile(testing_xml_path, NULL, 0);
589141cc406Sopenharmony_ci  if (!testing_xml_doc)
590141cc406Sopenharmony_ci    return SANE_STATUS_ACCESS_DENIED;
591141cc406Sopenharmony_ci
592141cc406Sopenharmony_ci  return SANE_STATUS_GOOD;
593141cc406Sopenharmony_ci}
594141cc406Sopenharmony_ci
595141cc406Sopenharmony_ci#define FAIL_TEST(func, ...)                                                   \
596141cc406Sopenharmony_ci  do {                                                                         \
597141cc406Sopenharmony_ci    DBG(1, "%s: FAIL: ", func);                                                \
598141cc406Sopenharmony_ci    DBG(1, __VA_ARGS__);                                                       \
599141cc406Sopenharmony_ci    fail_test();                                                               \
600141cc406Sopenharmony_ci  } while (0)
601141cc406Sopenharmony_ci
602141cc406Sopenharmony_ci#define FAIL_TEST_TX(func, node, ...)                                          \
603141cc406Sopenharmony_ci  do {                                                                         \
604141cc406Sopenharmony_ci    sanei_xml_print_seq_if_any(node, func);                                    \
605141cc406Sopenharmony_ci    DBG(1, "%s: FAIL: ", func);                                                \
606141cc406Sopenharmony_ci    DBG(1, __VA_ARGS__);                                                       \
607141cc406Sopenharmony_ci    fail_test();                                                               \
608141cc406Sopenharmony_ci  } while (0)
609141cc406Sopenharmony_ci
610141cc406Sopenharmony_civoid fail_test()
611141cc406Sopenharmony_ci{
612141cc406Sopenharmony_ci}
613141cc406Sopenharmony_ci
614141cc406Sopenharmony_ciSANE_Status sanei_usb_testing_enable_record(SANE_String_Const path, SANE_String_Const be_name)
615141cc406Sopenharmony_ci{
616141cc406Sopenharmony_ci  testing_mode = sanei_usb_testing_mode_record;
617141cc406Sopenharmony_ci  testing_record_backend = strdup(be_name);
618141cc406Sopenharmony_ci  testing_xml_path = strdup(path);
619141cc406Sopenharmony_ci
620141cc406Sopenharmony_ci  return SANE_STATUS_GOOD;
621141cc406Sopenharmony_ci}
622141cc406Sopenharmony_ci
623141cc406Sopenharmony_cistatic xmlNode* sanei_xml_find_first_child_with_name(xmlNode* parent,
624141cc406Sopenharmony_ci                                                     const char* name)
625141cc406Sopenharmony_ci{
626141cc406Sopenharmony_ci  xmlNode* curr_child = xmlFirstElementChild(parent);
627141cc406Sopenharmony_ci  while (curr_child != NULL)
628141cc406Sopenharmony_ci    {
629141cc406Sopenharmony_ci      if (xmlStrcmp(curr_child->name, (const xmlChar*)name) == 0)
630141cc406Sopenharmony_ci        return curr_child;
631141cc406Sopenharmony_ci      curr_child = xmlNextElementSibling(curr_child);
632141cc406Sopenharmony_ci    }
633141cc406Sopenharmony_ci  return NULL;
634141cc406Sopenharmony_ci}
635141cc406Sopenharmony_ci
636141cc406Sopenharmony_cistatic xmlNode* sanei_xml_find_next_child_with_name(xmlNode* child,
637141cc406Sopenharmony_ci                                                    const char* name)
638141cc406Sopenharmony_ci{
639141cc406Sopenharmony_ci  xmlNode* curr_child = xmlNextElementSibling(child);
640141cc406Sopenharmony_ci  while (curr_child != NULL)
641141cc406Sopenharmony_ci    {
642141cc406Sopenharmony_ci      if (xmlStrcmp(curr_child->name, (const xmlChar*)name) == 0)
643141cc406Sopenharmony_ci        return curr_child;
644141cc406Sopenharmony_ci      curr_child = xmlNextElementSibling(curr_child);
645141cc406Sopenharmony_ci    }
646141cc406Sopenharmony_ci  return NULL;
647141cc406Sopenharmony_ci}
648141cc406Sopenharmony_ci
649141cc406Sopenharmony_ci// a wrapper to get rid of -Wpointer-sign warnings in a single place
650141cc406Sopenharmony_cistatic char* sanei_xml_get_prop(xmlNode* node, const char* name)
651141cc406Sopenharmony_ci{
652141cc406Sopenharmony_ci  return (char*)xmlGetProp(node, (const xmlChar*)name);
653141cc406Sopenharmony_ci}
654141cc406Sopenharmony_ci
655141cc406Sopenharmony_ci// returns -1 if attribute is not found
656141cc406Sopenharmony_cistatic int sanei_xml_get_prop_uint(xmlNode* node, const char* name)
657141cc406Sopenharmony_ci{
658141cc406Sopenharmony_ci  char* attr = sanei_xml_get_prop(node, name);
659141cc406Sopenharmony_ci  if (attr == NULL)
660141cc406Sopenharmony_ci    {
661141cc406Sopenharmony_ci      return -1;
662141cc406Sopenharmony_ci    }
663141cc406Sopenharmony_ci
664141cc406Sopenharmony_ci  unsigned attr_uint = strtoul(attr, NULL, 0);
665141cc406Sopenharmony_ci  xmlFree(attr);
666141cc406Sopenharmony_ci  return attr_uint;
667141cc406Sopenharmony_ci}
668141cc406Sopenharmony_ci
669141cc406Sopenharmony_cistatic void sanei_xml_print_seq_if_any(xmlNode* node, const char* parent_fun)
670141cc406Sopenharmony_ci{
671141cc406Sopenharmony_ci  char* attr = sanei_xml_get_prop(node, "seq");
672141cc406Sopenharmony_ci  if (attr == NULL)
673141cc406Sopenharmony_ci    return;
674141cc406Sopenharmony_ci
675141cc406Sopenharmony_ci  DBG(1, "%s: FAIL: in transaction with seq %s:\n", parent_fun, attr);
676141cc406Sopenharmony_ci  xmlFree(attr);
677141cc406Sopenharmony_ci}
678141cc406Sopenharmony_ci
679141cc406Sopenharmony_ci// Checks whether transaction should be ignored. We ignore set_configuration
680141cc406Sopenharmony_ci// transactions, because set_configuration is called in sanei_usb_open outside test path.
681141cc406Sopenharmony_cistatic int sanei_xml_is_transaction_ignored(xmlNode* node)
682141cc406Sopenharmony_ci{
683141cc406Sopenharmony_ci  if (xmlStrcmp(node->name, (const xmlChar*)"control_tx") != 0)
684141cc406Sopenharmony_ci    return 0;
685141cc406Sopenharmony_ci
686141cc406Sopenharmony_ci  if (sanei_xml_get_prop_uint(node, "endpoint_number") != 0)
687141cc406Sopenharmony_ci    return 0;
688141cc406Sopenharmony_ci
689141cc406Sopenharmony_ci  int is_direction_in = 0;
690141cc406Sopenharmony_ci  int is_direction_out = 0;
691141cc406Sopenharmony_ci
692141cc406Sopenharmony_ci  char* attr = sanei_xml_get_prop(node, "direction");
693141cc406Sopenharmony_ci  if (attr == NULL)
694141cc406Sopenharmony_ci    return 0;
695141cc406Sopenharmony_ci
696141cc406Sopenharmony_ci  if (strcmp(attr, "IN") == 0)
697141cc406Sopenharmony_ci    is_direction_in = 1;
698141cc406Sopenharmony_ci  if (strcmp(attr, "OUT") == 0)
699141cc406Sopenharmony_ci    is_direction_out = 1;
700141cc406Sopenharmony_ci  xmlFree(attr);
701141cc406Sopenharmony_ci
702141cc406Sopenharmony_ci  unsigned bRequest = sanei_xml_get_prop_uint(node, "bRequest");
703141cc406Sopenharmony_ci  if (bRequest == USB_REQ_GET_DESCRIPTOR && is_direction_in)
704141cc406Sopenharmony_ci    {
705141cc406Sopenharmony_ci      if (sanei_xml_get_prop_uint(node, "bmRequestType") != 0x80)
706141cc406Sopenharmony_ci        return 0;
707141cc406Sopenharmony_ci      return 1;
708141cc406Sopenharmony_ci    }
709141cc406Sopenharmony_ci  if (bRequest == USB_REQ_SET_CONFIGURATION && is_direction_out)
710141cc406Sopenharmony_ci    return 1;
711141cc406Sopenharmony_ci
712141cc406Sopenharmony_ci  return 0;
713141cc406Sopenharmony_ci}
714141cc406Sopenharmony_ci
715141cc406Sopenharmony_cistatic xmlNode* sanei_xml_skip_non_tx_nodes(xmlNode* node)
716141cc406Sopenharmony_ci{
717141cc406Sopenharmony_ci  const char* known_node_names[] = {
718141cc406Sopenharmony_ci    "control_tx", "bulk_tx", "interrupt_tx",
719141cc406Sopenharmony_ci    "get_descriptor", "debug", "known_commands_end"
720141cc406Sopenharmony_ci  };
721141cc406Sopenharmony_ci
722141cc406Sopenharmony_ci  while (node != NULL)
723141cc406Sopenharmony_ci    {
724141cc406Sopenharmony_ci      int found = 0;
725141cc406Sopenharmony_ci      for (unsigned i = 0; i < sizeof(known_node_names) /
726141cc406Sopenharmony_ci                               sizeof(known_node_names[0]); ++i)
727141cc406Sopenharmony_ci        {
728141cc406Sopenharmony_ci          if (xmlStrcmp(node->name, (const xmlChar*) known_node_names[i]) == 0)
729141cc406Sopenharmony_ci            {
730141cc406Sopenharmony_ci              found = 1;
731141cc406Sopenharmony_ci              break;
732141cc406Sopenharmony_ci            }
733141cc406Sopenharmony_ci        }
734141cc406Sopenharmony_ci
735141cc406Sopenharmony_ci      if (found && sanei_xml_is_transaction_ignored(node) == 0)
736141cc406Sopenharmony_ci        {
737141cc406Sopenharmony_ci          break;
738141cc406Sopenharmony_ci        }
739141cc406Sopenharmony_ci
740141cc406Sopenharmony_ci      node = xmlNextElementSibling(node);
741141cc406Sopenharmony_ci    }
742141cc406Sopenharmony_ci  return node;
743141cc406Sopenharmony_ci}
744141cc406Sopenharmony_ci
745141cc406Sopenharmony_cistatic int sanei_xml_is_known_commands_end(xmlNode* node)
746141cc406Sopenharmony_ci{
747141cc406Sopenharmony_ci  if (!testing_development_mode || node == NULL)
748141cc406Sopenharmony_ci    return 0;
749141cc406Sopenharmony_ci  return xmlStrcmp(node->name, (const xmlChar*)"known_commands_end") == 0;
750141cc406Sopenharmony_ci}
751141cc406Sopenharmony_ci
752141cc406Sopenharmony_cistatic xmlNode* sanei_xml_peek_next_tx_node()
753141cc406Sopenharmony_ci{
754141cc406Sopenharmony_ci  return testing_xml_next_tx_node;
755141cc406Sopenharmony_ci}
756141cc406Sopenharmony_ci
757141cc406Sopenharmony_cistatic xmlNode* sanei_xml_get_next_tx_node()
758141cc406Sopenharmony_ci{
759141cc406Sopenharmony_ci  xmlNode* next = testing_xml_next_tx_node;
760141cc406Sopenharmony_ci
761141cc406Sopenharmony_ci  if (sanei_xml_is_known_commands_end(next))
762141cc406Sopenharmony_ci    {
763141cc406Sopenharmony_ci      testing_append_commands_node = xmlPreviousElementSibling(next);
764141cc406Sopenharmony_ci      return next;
765141cc406Sopenharmony_ci    }
766141cc406Sopenharmony_ci
767141cc406Sopenharmony_ci  testing_xml_next_tx_node =
768141cc406Sopenharmony_ci      xmlNextElementSibling(testing_xml_next_tx_node);
769141cc406Sopenharmony_ci
770141cc406Sopenharmony_ci  testing_xml_next_tx_node =
771141cc406Sopenharmony_ci      sanei_xml_skip_non_tx_nodes(testing_xml_next_tx_node);
772141cc406Sopenharmony_ci
773141cc406Sopenharmony_ci  return next;
774141cc406Sopenharmony_ci}
775141cc406Sopenharmony_ci
776141cc406Sopenharmony_ci#define CHAR_TYPE_INVALID -1
777141cc406Sopenharmony_ci#define CHAR_TYPE_SPACE -2
778141cc406Sopenharmony_ci
779141cc406Sopenharmony_cistatic int8_t sanei_xml_char_types[256] =
780141cc406Sopenharmony_ci{
781141cc406Sopenharmony_ci  /* 0x00-0x0f */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -2, -2, -2, -2, -2, -1, -1,
782141cc406Sopenharmony_ci  /* 0x10-0x1f */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
783141cc406Sopenharmony_ci  /* 0x20-0x2f */ -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
784141cc406Sopenharmony_ci  /* 0x30-0x3f */  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, -1, -1, -1, -1, -1, -1,
785141cc406Sopenharmony_ci  /* 0x40-0x4f */ -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
786141cc406Sopenharmony_ci  /* 0x50-0x5f */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
787141cc406Sopenharmony_ci  /* 0x60-0x6f */ -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
788141cc406Sopenharmony_ci  /* 0x70-0x7f */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
789141cc406Sopenharmony_ci  /* 0x80-0x8f */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
790141cc406Sopenharmony_ci  /* 0x90-0x9f */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
791141cc406Sopenharmony_ci  /* 0xa0-0xaf */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
792141cc406Sopenharmony_ci  /* 0xb0-0xbf */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
793141cc406Sopenharmony_ci  /* 0xc0-0xcf */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
794141cc406Sopenharmony_ci  /* 0xd0-0xdf */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
795141cc406Sopenharmony_ci  /* 0xe0-0xef */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
796141cc406Sopenharmony_ci  /* 0xf0-0xff */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
797141cc406Sopenharmony_ci};
798141cc406Sopenharmony_ci
799141cc406Sopenharmony_cistatic char* sanei_xml_get_hex_data_slow_path(xmlNode* node, xmlChar* content, xmlChar* cur_content,
800141cc406Sopenharmony_ci                                              char* ret_data, char* cur_ret_data, size_t* size)
801141cc406Sopenharmony_ci{
802141cc406Sopenharmony_ci  int num_nibbles = 0;
803141cc406Sopenharmony_ci  unsigned cur_nibble = 0;
804141cc406Sopenharmony_ci
805141cc406Sopenharmony_ci  while (*cur_content != 0)
806141cc406Sopenharmony_ci    {
807141cc406Sopenharmony_ci      while (sanei_xml_char_types[(uint8_t)*cur_content] == CHAR_TYPE_SPACE)
808141cc406Sopenharmony_ci        cur_content++;
809141cc406Sopenharmony_ci
810141cc406Sopenharmony_ci      if (*cur_content == 0)
811141cc406Sopenharmony_ci        break;
812141cc406Sopenharmony_ci
813141cc406Sopenharmony_ci      // don't use stroul because it will parse in big-endian and data is in
814141cc406Sopenharmony_ci      // little endian
815141cc406Sopenharmony_ci      uint8_t c = *cur_content;
816141cc406Sopenharmony_ci      int8_t ci = sanei_xml_char_types[c];
817141cc406Sopenharmony_ci      if (ci == CHAR_TYPE_INVALID)
818141cc406Sopenharmony_ci        {
819141cc406Sopenharmony_ci          FAIL_TEST_TX(__func__, node, "unexpected character %c\n", c);
820141cc406Sopenharmony_ci          cur_content++;
821141cc406Sopenharmony_ci          continue;
822141cc406Sopenharmony_ci        }
823141cc406Sopenharmony_ci
824141cc406Sopenharmony_ci      cur_nibble = (cur_nibble << 4) | ci;
825141cc406Sopenharmony_ci      num_nibbles++;
826141cc406Sopenharmony_ci
827141cc406Sopenharmony_ci      if (num_nibbles == 2)
828141cc406Sopenharmony_ci        {
829141cc406Sopenharmony_ci          *cur_ret_data++ = cur_nibble;
830141cc406Sopenharmony_ci          cur_nibble = 0;
831141cc406Sopenharmony_ci          num_nibbles = 0;
832141cc406Sopenharmony_ci        }
833141cc406Sopenharmony_ci      cur_content++;
834141cc406Sopenharmony_ci    }
835141cc406Sopenharmony_ci  *size = cur_ret_data - ret_data;
836141cc406Sopenharmony_ci  xmlFree(content);
837141cc406Sopenharmony_ci  return ret_data;
838141cc406Sopenharmony_ci}
839141cc406Sopenharmony_ci
840141cc406Sopenharmony_ci// Parses hex data in XML text node in the format of '00 11 ab 3f', etc. to
841141cc406Sopenharmony_ci// binary string. The size is returned as *size. The caller is responsible for
842141cc406Sopenharmony_ci// freeing the returned value
843141cc406Sopenharmony_cistatic char* sanei_xml_get_hex_data(xmlNode* node, size_t* size)
844141cc406Sopenharmony_ci{
845141cc406Sopenharmony_ci  xmlChar* content = xmlNodeGetContent(node);
846141cc406Sopenharmony_ci
847141cc406Sopenharmony_ci  // let's overallocate to simplify the implementation. We expect the string
848141cc406Sopenharmony_ci  // to be deallocated soon anyway
849141cc406Sopenharmony_ci  char* ret_data = malloc(strlen((const char*)content) / 2 + 2);
850141cc406Sopenharmony_ci  char* cur_ret_data = ret_data;
851141cc406Sopenharmony_ci
852141cc406Sopenharmony_ci  xmlChar* cur_content = content;
853141cc406Sopenharmony_ci
854141cc406Sopenharmony_ci  // the text to binary conversion takes most of the time spent in tests, so we
855141cc406Sopenharmony_ci  // take extra care to optimize it. We split the implementation into fast and
856141cc406Sopenharmony_ci  // slow path. The fast path utilizes the knowledge that there will be no spaces
857141cc406Sopenharmony_ci  // within bytes. When this assumption does not hold, we switch to the slow path.
858141cc406Sopenharmony_ci  while (*cur_content != 0)
859141cc406Sopenharmony_ci    {
860141cc406Sopenharmony_ci      // most of the time there will be 1 or 2 spaces between bytes. Give the CPU
861141cc406Sopenharmony_ci      // chance to predict this by partially unrolling the while loop.
862141cc406Sopenharmony_ci      if (sanei_xml_char_types[(uint8_t)*cur_content] == CHAR_TYPE_SPACE)
863141cc406Sopenharmony_ci        {
864141cc406Sopenharmony_ci          cur_content++;
865141cc406Sopenharmony_ci          if (sanei_xml_char_types[(uint8_t)*cur_content] == CHAR_TYPE_SPACE)
866141cc406Sopenharmony_ci            {
867141cc406Sopenharmony_ci              cur_content++;
868141cc406Sopenharmony_ci              while (sanei_xml_char_types[(uint8_t)*cur_content] == CHAR_TYPE_SPACE)
869141cc406Sopenharmony_ci                cur_content++;
870141cc406Sopenharmony_ci            }
871141cc406Sopenharmony_ci        }
872141cc406Sopenharmony_ci
873141cc406Sopenharmony_ci      if (*cur_content == 0)
874141cc406Sopenharmony_ci        break;
875141cc406Sopenharmony_ci
876141cc406Sopenharmony_ci      // don't use stroul because it will parse in big-endian and data is in
877141cc406Sopenharmony_ci      // little endian
878141cc406Sopenharmony_ci      int8_t ci1 = sanei_xml_char_types[(uint8_t)*cur_content];
879141cc406Sopenharmony_ci      int8_t ci2 = sanei_xml_char_types[(uint8_t)*(cur_content + 1)];
880141cc406Sopenharmony_ci
881141cc406Sopenharmony_ci      if (ci1 < 0 || ci2 < 0)
882141cc406Sopenharmony_ci        return sanei_xml_get_hex_data_slow_path(node, content, cur_content, ret_data, cur_ret_data,
883141cc406Sopenharmony_ci                                                size);
884141cc406Sopenharmony_ci
885141cc406Sopenharmony_ci      *cur_ret_data++ = ci1 << 4 | ci2;
886141cc406Sopenharmony_ci      cur_content += 2;
887141cc406Sopenharmony_ci    }
888141cc406Sopenharmony_ci  *size = cur_ret_data - ret_data;
889141cc406Sopenharmony_ci  xmlFree(content);
890141cc406Sopenharmony_ci  return ret_data;
891141cc406Sopenharmony_ci}
892141cc406Sopenharmony_ci
893141cc406Sopenharmony_ci// caller is responsible for freeing the returned pointer
894141cc406Sopenharmony_cistatic char* sanei_binary_to_hex_data(const char* data, size_t size,
895141cc406Sopenharmony_ci                                      size_t* out_size)
896141cc406Sopenharmony_ci{
897141cc406Sopenharmony_ci  char* hex_data = malloc(size * 4);
898141cc406Sopenharmony_ci  size_t hex_size = 0;
899141cc406Sopenharmony_ci
900141cc406Sopenharmony_ci  for (size_t i = 0; i < size; ++i)
901141cc406Sopenharmony_ci    {
902141cc406Sopenharmony_ci      hex_size += snprintf(hex_data + hex_size, 3, "%02hhx", data[i]);
903141cc406Sopenharmony_ci      if (i + 1 != size)
904141cc406Sopenharmony_ci      {
905141cc406Sopenharmony_ci        if ((i + 1) % 32 == 0)
906141cc406Sopenharmony_ci          hex_data[hex_size++] = '\n';
907141cc406Sopenharmony_ci        else
908141cc406Sopenharmony_ci          hex_data[hex_size++] = ' ';
909141cc406Sopenharmony_ci      }
910141cc406Sopenharmony_ci    }
911141cc406Sopenharmony_ci  hex_data[hex_size] = 0;
912141cc406Sopenharmony_ci  if (out_size)
913141cc406Sopenharmony_ci    *out_size = hex_size;
914141cc406Sopenharmony_ci  return hex_data;
915141cc406Sopenharmony_ci}
916141cc406Sopenharmony_ci
917141cc406Sopenharmony_ci
918141cc406Sopenharmony_cistatic void sanei_xml_set_data(xmlNode* node, const char* data)
919141cc406Sopenharmony_ci{
920141cc406Sopenharmony_ci  // FIXME: remove existing children
921141cc406Sopenharmony_ci  xmlAddChild(node, xmlNewText((const xmlChar*)data));
922141cc406Sopenharmony_ci}
923141cc406Sopenharmony_ci
924141cc406Sopenharmony_ci// Writes binary data to XML node as a child text node in the hex format of
925141cc406Sopenharmony_ci// '00 11 ab 3f'.
926141cc406Sopenharmony_cistatic void sanei_xml_set_hex_data(xmlNode* node, const char* data,
927141cc406Sopenharmony_ci                                   size_t size)
928141cc406Sopenharmony_ci{
929141cc406Sopenharmony_ci  char* hex_data = sanei_binary_to_hex_data(data, size, NULL);
930141cc406Sopenharmony_ci  sanei_xml_set_data(node, hex_data);
931141cc406Sopenharmony_ci  free(hex_data);
932141cc406Sopenharmony_ci}
933141cc406Sopenharmony_ci
934141cc406Sopenharmony_cistatic void sanei_xml_set_hex_attr(xmlNode* node, const char* attr_name,
935141cc406Sopenharmony_ci                                   unsigned attr_value)
936141cc406Sopenharmony_ci{
937141cc406Sopenharmony_ci  const int buf_size = 128;
938141cc406Sopenharmony_ci  char buf[buf_size];
939141cc406Sopenharmony_ci  if (attr_value > 0xffffff)
940141cc406Sopenharmony_ci    snprintf(buf, buf_size, "0x%x", attr_value);
941141cc406Sopenharmony_ci  else if (attr_value > 0xffff)
942141cc406Sopenharmony_ci    snprintf(buf, buf_size, "0x%06x", attr_value);
943141cc406Sopenharmony_ci  else if (attr_value > 0xff)
944141cc406Sopenharmony_ci    snprintf(buf, buf_size, "0x%04x", attr_value);
945141cc406Sopenharmony_ci  else
946141cc406Sopenharmony_ci    snprintf(buf, buf_size, "0x%02x", attr_value);
947141cc406Sopenharmony_ci
948141cc406Sopenharmony_ci  xmlNewProp(node, (const xmlChar*)attr_name, (const xmlChar*)buf);
949141cc406Sopenharmony_ci}
950141cc406Sopenharmony_ci
951141cc406Sopenharmony_cistatic void sanei_xml_set_uint_attr(xmlNode* node, const char* attr_name,
952141cc406Sopenharmony_ci                                    unsigned attr_value)
953141cc406Sopenharmony_ci{
954141cc406Sopenharmony_ci  const int buf_size = 128;
955141cc406Sopenharmony_ci  char buf[buf_size];
956141cc406Sopenharmony_ci  snprintf(buf, buf_size, "%d", attr_value);
957141cc406Sopenharmony_ci  xmlNewProp(node, (const xmlChar*)attr_name, (const xmlChar*)buf);
958141cc406Sopenharmony_ci}
959141cc406Sopenharmony_ci
960141cc406Sopenharmony_cistatic xmlNode* sanei_xml_append_command(xmlNode* sibling,
961141cc406Sopenharmony_ci                                         int indent, xmlNode* e_command)
962141cc406Sopenharmony_ci{
963141cc406Sopenharmony_ci  if (indent)
964141cc406Sopenharmony_ci    {
965141cc406Sopenharmony_ci      xmlNode* e_indent = xmlNewText((const xmlChar*)"\n    ");
966141cc406Sopenharmony_ci      sibling = xmlAddNextSibling(sibling, e_indent);
967141cc406Sopenharmony_ci    }
968141cc406Sopenharmony_ci  return xmlAddNextSibling(sibling, e_command);
969141cc406Sopenharmony_ci}
970141cc406Sopenharmony_ci
971141cc406Sopenharmony_cistatic void sanei_xml_command_common_props(xmlNode* node, int endpoint_number,
972141cc406Sopenharmony_ci                                           const char* direction)
973141cc406Sopenharmony_ci{
974141cc406Sopenharmony_ci  xmlNewProp(node, (const xmlChar*)"time_usec", (const xmlChar*)"0");
975141cc406Sopenharmony_ci  sanei_xml_set_uint_attr(node, "seq", ++testing_last_known_seq);
976141cc406Sopenharmony_ci  sanei_xml_set_uint_attr(node, "endpoint_number", endpoint_number);
977141cc406Sopenharmony_ci  xmlNewProp(node, (const xmlChar*)"direction", (const xmlChar*)direction);
978141cc406Sopenharmony_ci}
979141cc406Sopenharmony_ci
980141cc406Sopenharmony_cistatic void sanei_xml_record_seq(xmlNode* node)
981141cc406Sopenharmony_ci{
982141cc406Sopenharmony_ci  int seq = sanei_xml_get_prop_uint(node, "seq");
983141cc406Sopenharmony_ci  if (seq > 0)
984141cc406Sopenharmony_ci    testing_last_known_seq = seq;
985141cc406Sopenharmony_ci}
986141cc406Sopenharmony_ci
987141cc406Sopenharmony_cistatic void sanei_xml_break()
988141cc406Sopenharmony_ci{
989141cc406Sopenharmony_ci}
990141cc406Sopenharmony_ci
991141cc406Sopenharmony_cistatic void sanei_xml_break_if_needed(xmlNode* node)
992141cc406Sopenharmony_ci{
993141cc406Sopenharmony_ci  char* attr = sanei_xml_get_prop(node, "debug_break");
994141cc406Sopenharmony_ci  if (attr != NULL)
995141cc406Sopenharmony_ci    {
996141cc406Sopenharmony_ci      sanei_xml_break();
997141cc406Sopenharmony_ci      xmlFree(attr);
998141cc406Sopenharmony_ci    }
999141cc406Sopenharmony_ci}
1000141cc406Sopenharmony_ci
1001141cc406Sopenharmony_ci// returns 1 on success
1002141cc406Sopenharmony_cistatic int sanei_usb_check_attr(xmlNode* node, const char* attr_name,
1003141cc406Sopenharmony_ci                                const char* expected, const char* parent_fun)
1004141cc406Sopenharmony_ci{
1005141cc406Sopenharmony_ci  char* attr = sanei_xml_get_prop(node, attr_name);
1006141cc406Sopenharmony_ci  if (attr == NULL)
1007141cc406Sopenharmony_ci    {
1008141cc406Sopenharmony_ci      FAIL_TEST_TX(parent_fun, node, "no %s attribute\n", attr_name);
1009141cc406Sopenharmony_ci      return 0;
1010141cc406Sopenharmony_ci    }
1011141cc406Sopenharmony_ci
1012141cc406Sopenharmony_ci  if (strcmp(attr, expected) != 0)
1013141cc406Sopenharmony_ci    {
1014141cc406Sopenharmony_ci      FAIL_TEST_TX(parent_fun, node, "unexpected %s attribute: %s, wanted %s\n",
1015141cc406Sopenharmony_ci                   attr_name, attr, expected);
1016141cc406Sopenharmony_ci      xmlFree(attr);
1017141cc406Sopenharmony_ci      return 0;
1018141cc406Sopenharmony_ci    }
1019141cc406Sopenharmony_ci  xmlFree(attr);
1020141cc406Sopenharmony_ci  return 1;
1021141cc406Sopenharmony_ci}
1022141cc406Sopenharmony_ci
1023141cc406Sopenharmony_ci// returns 1 on success
1024141cc406Sopenharmony_cistatic int sanei_usb_attr_is(xmlNode* node, const char* attr_name,
1025141cc406Sopenharmony_ci                             const char* expected)
1026141cc406Sopenharmony_ci{
1027141cc406Sopenharmony_ci  char* attr = sanei_xml_get_prop(node, attr_name);
1028141cc406Sopenharmony_ci  if (attr == NULL)
1029141cc406Sopenharmony_ci      return 0;
1030141cc406Sopenharmony_ci
1031141cc406Sopenharmony_ci  if (strcmp(attr, expected) != 0)
1032141cc406Sopenharmony_ci    {
1033141cc406Sopenharmony_ci      xmlFree(attr);
1034141cc406Sopenharmony_ci      return 0;
1035141cc406Sopenharmony_ci    }
1036141cc406Sopenharmony_ci  xmlFree(attr);
1037141cc406Sopenharmony_ci  return 1;
1038141cc406Sopenharmony_ci}
1039141cc406Sopenharmony_ci
1040141cc406Sopenharmony_ci// returns 0 on success
1041141cc406Sopenharmony_cistatic int sanei_usb_check_attr_uint(xmlNode* node, const char* attr_name,
1042141cc406Sopenharmony_ci                                     unsigned expected, const char* parent_fun)
1043141cc406Sopenharmony_ci{
1044141cc406Sopenharmony_ci  char* attr = sanei_xml_get_prop(node, attr_name);
1045141cc406Sopenharmony_ci  if (attr == NULL)
1046141cc406Sopenharmony_ci    {
1047141cc406Sopenharmony_ci      FAIL_TEST_TX(parent_fun, node, "no %s attribute\n", attr_name);
1048141cc406Sopenharmony_ci      return 0;
1049141cc406Sopenharmony_ci    }
1050141cc406Sopenharmony_ci
1051141cc406Sopenharmony_ci  unsigned attr_int = strtoul(attr, NULL, 0);
1052141cc406Sopenharmony_ci  if (attr_int != expected)
1053141cc406Sopenharmony_ci    {
1054141cc406Sopenharmony_ci      FAIL_TEST_TX(parent_fun, node,
1055141cc406Sopenharmony_ci                   "unexpected %s attribute: %s, wanted 0x%x\n",
1056141cc406Sopenharmony_ci                   attr_name, attr, expected);
1057141cc406Sopenharmony_ci      xmlFree(attr);
1058141cc406Sopenharmony_ci      return 0;
1059141cc406Sopenharmony_ci    }
1060141cc406Sopenharmony_ci  xmlFree(attr);
1061141cc406Sopenharmony_ci  return 1;
1062141cc406Sopenharmony_ci}
1063141cc406Sopenharmony_ci
1064141cc406Sopenharmony_cistatic int sanei_usb_attr_is_uint(xmlNode* node, const char* attr_name,
1065141cc406Sopenharmony_ci                                  unsigned expected)
1066141cc406Sopenharmony_ci{
1067141cc406Sopenharmony_ci  char* attr = sanei_xml_get_prop(node, attr_name);
1068141cc406Sopenharmony_ci  if (attr == NULL)
1069141cc406Sopenharmony_ci    return 0;
1070141cc406Sopenharmony_ci
1071141cc406Sopenharmony_ci  unsigned attr_int = strtoul(attr, NULL, 0);
1072141cc406Sopenharmony_ci  if (attr_int != expected)
1073141cc406Sopenharmony_ci    {
1074141cc406Sopenharmony_ci      xmlFree(attr);
1075141cc406Sopenharmony_ci      return 0;
1076141cc406Sopenharmony_ci    }
1077141cc406Sopenharmony_ci  xmlFree(attr);
1078141cc406Sopenharmony_ci  return 1;
1079141cc406Sopenharmony_ci}
1080141cc406Sopenharmony_ci
1081141cc406Sopenharmony_ci// returns 1 on data equality
1082141cc406Sopenharmony_cistatic int sanei_usb_check_data_equal(xmlNode* node,
1083141cc406Sopenharmony_ci                                      const char* data,
1084141cc406Sopenharmony_ci                                      size_t data_size,
1085141cc406Sopenharmony_ci                                      const char* expected_data,
1086141cc406Sopenharmony_ci                                      size_t expected_size,
1087141cc406Sopenharmony_ci                                      const char* parent_fun)
1088141cc406Sopenharmony_ci{
1089141cc406Sopenharmony_ci  if ((data_size == expected_size) &&
1090141cc406Sopenharmony_ci      (memcmp(data, expected_data, data_size) == 0))
1091141cc406Sopenharmony_ci    return 1;
1092141cc406Sopenharmony_ci
1093141cc406Sopenharmony_ci  char* data_hex = sanei_binary_to_hex_data(data, data_size, NULL);
1094141cc406Sopenharmony_ci  char* expected_hex = sanei_binary_to_hex_data(expected_data, expected_size,
1095141cc406Sopenharmony_ci                                                NULL);
1096141cc406Sopenharmony_ci
1097141cc406Sopenharmony_ci  if (data_size == expected_size)
1098141cc406Sopenharmony_ci    FAIL_TEST_TX(parent_fun, node, "data differs (size %lu):\n", data_size);
1099141cc406Sopenharmony_ci  else
1100141cc406Sopenharmony_ci    FAIL_TEST_TX(parent_fun, node,
1101141cc406Sopenharmony_ci                 "data differs (got size %lu, expected %lu):\n",
1102141cc406Sopenharmony_ci                 data_size, expected_size);
1103141cc406Sopenharmony_ci
1104141cc406Sopenharmony_ci  FAIL_TEST(parent_fun, "got: %s\n", data_hex);
1105141cc406Sopenharmony_ci  FAIL_TEST(parent_fun, "expected: %s\n", expected_hex);
1106141cc406Sopenharmony_ci  free(data_hex);
1107141cc406Sopenharmony_ci  free(expected_hex);
1108141cc406Sopenharmony_ci  return 0;
1109141cc406Sopenharmony_ci}
1110141cc406Sopenharmony_ci
1111141cc406Sopenharmony_ciSANE_String sanei_usb_testing_get_backend()
1112141cc406Sopenharmony_ci{
1113141cc406Sopenharmony_ci  if (testing_xml_doc == NULL)
1114141cc406Sopenharmony_ci    return NULL;
1115141cc406Sopenharmony_ci
1116141cc406Sopenharmony_ci  xmlNode* el_root = xmlDocGetRootElement(testing_xml_doc);
1117141cc406Sopenharmony_ci  if (xmlStrcmp(el_root->name, (const xmlChar*)"device_capture") != 0)
1118141cc406Sopenharmony_ci    {
1119141cc406Sopenharmony_ci      FAIL_TEST(__func__, "the given file is not USB capture\n");
1120141cc406Sopenharmony_ci      return NULL;
1121141cc406Sopenharmony_ci    }
1122141cc406Sopenharmony_ci
1123141cc406Sopenharmony_ci  char* attr = sanei_xml_get_prop(el_root, "backend");
1124141cc406Sopenharmony_ci  if (attr == NULL)
1125141cc406Sopenharmony_ci    {
1126141cc406Sopenharmony_ci      FAIL_TEST(__func__, "no backend attr in description node\n");
1127141cc406Sopenharmony_ci      return NULL;
1128141cc406Sopenharmony_ci    }
1129141cc406Sopenharmony_ci  // duplicate using strdup so that the caller can use free()
1130141cc406Sopenharmony_ci  char* ret = strdup(attr);
1131141cc406Sopenharmony_ci  xmlFree(attr);
1132141cc406Sopenharmony_ci  return ret;
1133141cc406Sopenharmony_ci}
1134141cc406Sopenharmony_ci
1135141cc406Sopenharmony_ciSANE_Bool sanei_usb_is_replay_mode_enabled()
1136141cc406Sopenharmony_ci{
1137141cc406Sopenharmony_ci  if (testing_mode == sanei_usb_testing_mode_replay)
1138141cc406Sopenharmony_ci    return SANE_TRUE;
1139141cc406Sopenharmony_ci
1140141cc406Sopenharmony_ci  return SANE_FALSE;
1141141cc406Sopenharmony_ci}
1142141cc406Sopenharmony_ci
1143141cc406Sopenharmony_cistatic void sanei_usb_record_debug_msg(xmlNode* node, SANE_String_Const message)
1144141cc406Sopenharmony_ci{
1145141cc406Sopenharmony_ci  int node_was_null = node == NULL;
1146141cc406Sopenharmony_ci  if (node_was_null)
1147141cc406Sopenharmony_ci    node = testing_append_commands_node;
1148141cc406Sopenharmony_ci
1149141cc406Sopenharmony_ci  xmlNode* e_tx = xmlNewNode(NULL, (const xmlChar*)"debug");
1150141cc406Sopenharmony_ci  sanei_xml_set_uint_attr(e_tx, "seq", ++testing_last_known_seq);
1151141cc406Sopenharmony_ci  xmlNewProp(e_tx, (const xmlChar*)"message", (const xmlChar*)message);
1152141cc406Sopenharmony_ci
1153141cc406Sopenharmony_ci  node = sanei_xml_append_command(node, node_was_null, e_tx);
1154141cc406Sopenharmony_ci
1155141cc406Sopenharmony_ci  if (node_was_null)
1156141cc406Sopenharmony_ci    testing_append_commands_node = node;
1157141cc406Sopenharmony_ci}
1158141cc406Sopenharmony_ci
1159141cc406Sopenharmony_cistatic void sanei_usb_record_replace_debug_msg(xmlNode* node, SANE_String_Const message)
1160141cc406Sopenharmony_ci{
1161141cc406Sopenharmony_ci  if (!testing_development_mode)
1162141cc406Sopenharmony_ci    return;
1163141cc406Sopenharmony_ci
1164141cc406Sopenharmony_ci  testing_last_known_seq--;
1165141cc406Sopenharmony_ci  sanei_usb_record_debug_msg(node, message);
1166141cc406Sopenharmony_ci  xmlUnlinkNode(node);
1167141cc406Sopenharmony_ci  xmlFreeNode(node);
1168141cc406Sopenharmony_ci}
1169141cc406Sopenharmony_ci
1170141cc406Sopenharmony_cistatic void sanei_usb_replay_debug_msg(SANE_String_Const message)
1171141cc406Sopenharmony_ci{
1172141cc406Sopenharmony_ci  if (testing_known_commands_input_failed)
1173141cc406Sopenharmony_ci    return;
1174141cc406Sopenharmony_ci
1175141cc406Sopenharmony_ci  xmlNode* node = sanei_xml_get_next_tx_node();
1176141cc406Sopenharmony_ci  if (node == NULL)
1177141cc406Sopenharmony_ci    {
1178141cc406Sopenharmony_ci      FAIL_TEST(__func__, "no more transactions\n");
1179141cc406Sopenharmony_ci      return;
1180141cc406Sopenharmony_ci    }
1181141cc406Sopenharmony_ci
1182141cc406Sopenharmony_ci  if (sanei_xml_is_known_commands_end(node))
1183141cc406Sopenharmony_ci    {
1184141cc406Sopenharmony_ci      sanei_usb_record_debug_msg(NULL, message);
1185141cc406Sopenharmony_ci      return;
1186141cc406Sopenharmony_ci    }
1187141cc406Sopenharmony_ci
1188141cc406Sopenharmony_ci  sanei_xml_record_seq(node);
1189141cc406Sopenharmony_ci  sanei_xml_break_if_needed(node);
1190141cc406Sopenharmony_ci
1191141cc406Sopenharmony_ci  if (xmlStrcmp(node->name, (const xmlChar*)"debug") != 0)
1192141cc406Sopenharmony_ci    {
1193141cc406Sopenharmony_ci      FAIL_TEST_TX(__func__, node, "unexpected transaction type %s\n",
1194141cc406Sopenharmony_ci                   (const char*) node->name);
1195141cc406Sopenharmony_ci      sanei_usb_record_replace_debug_msg(node, message);
1196141cc406Sopenharmony_ci    }
1197141cc406Sopenharmony_ci
1198141cc406Sopenharmony_ci  if (!sanei_usb_check_attr(node, "message", message, __func__))
1199141cc406Sopenharmony_ci    {
1200141cc406Sopenharmony_ci      sanei_usb_record_replace_debug_msg(node, message);
1201141cc406Sopenharmony_ci    }
1202141cc406Sopenharmony_ci}
1203141cc406Sopenharmony_ci
1204141cc406Sopenharmony_ciextern void sanei_usb_testing_record_clear()
1205141cc406Sopenharmony_ci{
1206141cc406Sopenharmony_ci  if (testing_mode != sanei_usb_testing_mode_record)
1207141cc406Sopenharmony_ci    return;
1208141cc406Sopenharmony_ci
1209141cc406Sopenharmony_ci  // we only need to indicate that we never opened a device and sanei_usb_record_open() will
1210141cc406Sopenharmony_ci  // reinitialize everything for us.
1211141cc406Sopenharmony_ci  testing_already_opened = 0;
1212141cc406Sopenharmony_ci  testing_known_commands_input_failed = 0;
1213141cc406Sopenharmony_ci  testing_last_known_seq = 0;
1214141cc406Sopenharmony_ci  testing_append_commands_node = NULL;
1215141cc406Sopenharmony_ci}
1216141cc406Sopenharmony_ci
1217141cc406Sopenharmony_ciextern void sanei_usb_testing_record_message(SANE_String_Const message)
1218141cc406Sopenharmony_ci{
1219141cc406Sopenharmony_ci  if (testing_mode == sanei_usb_testing_mode_record)
1220141cc406Sopenharmony_ci    {
1221141cc406Sopenharmony_ci      sanei_usb_record_debug_msg(NULL, message);
1222141cc406Sopenharmony_ci    }
1223141cc406Sopenharmony_ci  if (testing_mode == sanei_usb_testing_mode_replay)
1224141cc406Sopenharmony_ci    {
1225141cc406Sopenharmony_ci      sanei_usb_replay_debug_msg(message);
1226141cc406Sopenharmony_ci    }
1227141cc406Sopenharmony_ci}
1228141cc406Sopenharmony_ci
1229141cc406Sopenharmony_cistatic void sanei_usb_add_endpoint(device_list_type* device,
1230141cc406Sopenharmony_ci                                   SANE_Int transfer_type,
1231141cc406Sopenharmony_ci                                   SANE_Int ep_address,
1232141cc406Sopenharmony_ci                                   SANE_Int ep_direction);
1233141cc406Sopenharmony_ci
1234141cc406Sopenharmony_cistatic SANE_Status sanei_usb_testing_init()
1235141cc406Sopenharmony_ci{
1236141cc406Sopenharmony_ci  DBG_INIT();
1237141cc406Sopenharmony_ci
1238141cc406Sopenharmony_ci  if (testing_mode == sanei_usb_testing_mode_record)
1239141cc406Sopenharmony_ci    {
1240141cc406Sopenharmony_ci      testing_xml_doc = xmlNewDoc((const xmlChar*)"1.0");
1241141cc406Sopenharmony_ci      return SANE_STATUS_GOOD;
1242141cc406Sopenharmony_ci    }
1243141cc406Sopenharmony_ci
1244141cc406Sopenharmony_ci  if (device_number != 0)
1245141cc406Sopenharmony_ci    return SANE_STATUS_INVAL; // already opened
1246141cc406Sopenharmony_ci
1247141cc406Sopenharmony_ci  xmlNode* el_root = xmlDocGetRootElement(testing_xml_doc);
1248141cc406Sopenharmony_ci  if (xmlStrcmp(el_root->name, (const xmlChar*)"device_capture") != 0)
1249141cc406Sopenharmony_ci    {
1250141cc406Sopenharmony_ci      DBG(1, "%s: the given file is not USB capture\n", __func__);
1251141cc406Sopenharmony_ci      return SANE_STATUS_INVAL;
1252141cc406Sopenharmony_ci    }
1253141cc406Sopenharmony_ci
1254141cc406Sopenharmony_ci  xmlNode* el_description =
1255141cc406Sopenharmony_ci      sanei_xml_find_first_child_with_name(el_root, "description");
1256141cc406Sopenharmony_ci  if (el_description == NULL)
1257141cc406Sopenharmony_ci    {
1258141cc406Sopenharmony_ci      DBG(1, "%s: could not find description node\n", __func__);
1259141cc406Sopenharmony_ci      return SANE_STATUS_INVAL;
1260141cc406Sopenharmony_ci    }
1261141cc406Sopenharmony_ci
1262141cc406Sopenharmony_ci  int device_id = sanei_xml_get_prop_uint(el_description, "id_vendor");
1263141cc406Sopenharmony_ci  if (device_id < 0)
1264141cc406Sopenharmony_ci    {
1265141cc406Sopenharmony_ci      DBG(1, "%s: no id_vendor attr in description node\n", __func__);
1266141cc406Sopenharmony_ci      return SANE_STATUS_INVAL;
1267141cc406Sopenharmony_ci    }
1268141cc406Sopenharmony_ci
1269141cc406Sopenharmony_ci  int product_id = sanei_xml_get_prop_uint(el_description, "id_product");
1270141cc406Sopenharmony_ci  if (product_id < 0)
1271141cc406Sopenharmony_ci    {
1272141cc406Sopenharmony_ci      DBG(1, "%s: no id_product attr in description node\n", __func__);
1273141cc406Sopenharmony_ci      return SANE_STATUS_INVAL;
1274141cc406Sopenharmony_ci    }
1275141cc406Sopenharmony_ci
1276141cc406Sopenharmony_ci  xmlNode* el_configurations =
1277141cc406Sopenharmony_ci      sanei_xml_find_first_child_with_name(el_description, "configurations");
1278141cc406Sopenharmony_ci  if (el_configurations == NULL)
1279141cc406Sopenharmony_ci    {
1280141cc406Sopenharmony_ci      DBG(1, "%s: could not find configurations node\n", __func__);
1281141cc406Sopenharmony_ci      return SANE_STATUS_INVAL;
1282141cc406Sopenharmony_ci    }
1283141cc406Sopenharmony_ci
1284141cc406Sopenharmony_ci  xmlNode* el_configuration =
1285141cc406Sopenharmony_ci      sanei_xml_find_first_child_with_name(el_configurations, "configuration");
1286141cc406Sopenharmony_ci  if (el_configuration == NULL)
1287141cc406Sopenharmony_ci    {
1288141cc406Sopenharmony_ci      DBG(1, "%s: no configuration nodes\n", __func__);
1289141cc406Sopenharmony_ci      return SANE_STATUS_INVAL;
1290141cc406Sopenharmony_ci    }
1291141cc406Sopenharmony_ci
1292141cc406Sopenharmony_ci  while (el_configuration != NULL)
1293141cc406Sopenharmony_ci    {
1294141cc406Sopenharmony_ci      xmlNode* el_interface =
1295141cc406Sopenharmony_ci          sanei_xml_find_first_child_with_name(el_configuration, "interface");
1296141cc406Sopenharmony_ci
1297141cc406Sopenharmony_ci      while (el_interface != NULL)
1298141cc406Sopenharmony_ci        {
1299141cc406Sopenharmony_ci          device_list_type device;
1300141cc406Sopenharmony_ci          memset(&device, 0, sizeof(device));
1301141cc406Sopenharmony_ci          device.devname = strdup(testing_xml_path);
1302141cc406Sopenharmony_ci
1303141cc406Sopenharmony_ci          // other code shouldn't depend on method because testing_mode is
1304141cc406Sopenharmony_ci          // sanei_usb_testing_mode_replay
1305141cc406Sopenharmony_ci          device.method = sanei_usb_method_libusb;
1306141cc406Sopenharmony_ci          device.vendor = device_id;
1307141cc406Sopenharmony_ci          device.product = product_id;
1308141cc406Sopenharmony_ci
1309141cc406Sopenharmony_ci          device.interface_nr = sanei_xml_get_prop_uint(el_interface, "number");
1310141cc406Sopenharmony_ci          if (device.interface_nr < 0)
1311141cc406Sopenharmony_ci            {
1312141cc406Sopenharmony_ci              DBG(1, "%s: no number attr in interface node\n", __func__);
1313141cc406Sopenharmony_ci              return SANE_STATUS_INVAL;
1314141cc406Sopenharmony_ci            }
1315141cc406Sopenharmony_ci
1316141cc406Sopenharmony_ci          xmlNode* el_endpoint =
1317141cc406Sopenharmony_ci              sanei_xml_find_first_child_with_name(el_interface, "endpoint");
1318141cc406Sopenharmony_ci
1319141cc406Sopenharmony_ci          while (el_endpoint != NULL)
1320141cc406Sopenharmony_ci            {
1321141cc406Sopenharmony_ci              char* transfer_attr = sanei_xml_get_prop(el_endpoint,
1322141cc406Sopenharmony_ci                                                       "transfer_type");
1323141cc406Sopenharmony_ci              int address = sanei_xml_get_prop_uint(el_endpoint, "address");
1324141cc406Sopenharmony_ci              char* direction_attr = sanei_xml_get_prop(el_endpoint,
1325141cc406Sopenharmony_ci                                                        "direction");
1326141cc406Sopenharmony_ci
1327141cc406Sopenharmony_ci              int direction_is_in = strcmp(direction_attr, "IN") == 0 ? 1 : 0;
1328141cc406Sopenharmony_ci              int transfer_type = -1;
1329141cc406Sopenharmony_ci              if (strcmp(transfer_attr, "INTERRUPT") == 0)
1330141cc406Sopenharmony_ci                transfer_type = USB_ENDPOINT_TYPE_INTERRUPT;
1331141cc406Sopenharmony_ci              else if (strcmp(transfer_attr, "BULK") == 0)
1332141cc406Sopenharmony_ci                transfer_type = USB_ENDPOINT_TYPE_BULK;
1333141cc406Sopenharmony_ci              else if (strcmp(transfer_attr, "ISOCHRONOUS") == 0)
1334141cc406Sopenharmony_ci                transfer_type = USB_ENDPOINT_TYPE_ISOCHRONOUS;
1335141cc406Sopenharmony_ci              else if (strcmp(transfer_attr, "CONTROL") == 0)
1336141cc406Sopenharmony_ci                transfer_type = USB_ENDPOINT_TYPE_CONTROL;
1337141cc406Sopenharmony_ci              else
1338141cc406Sopenharmony_ci                {
1339141cc406Sopenharmony_ci                  DBG(3, "%s: unknown endpoint type %s\n",
1340141cc406Sopenharmony_ci                      __func__, transfer_attr);
1341141cc406Sopenharmony_ci                }
1342141cc406Sopenharmony_ci
1343141cc406Sopenharmony_ci              if (transfer_type >= 0)
1344141cc406Sopenharmony_ci                {
1345141cc406Sopenharmony_ci                  sanei_usb_add_endpoint(&device, transfer_type, address,
1346141cc406Sopenharmony_ci                                         direction_is_in);
1347141cc406Sopenharmony_ci                }
1348141cc406Sopenharmony_ci
1349141cc406Sopenharmony_ci              xmlFree(transfer_attr);
1350141cc406Sopenharmony_ci              xmlFree(direction_attr);
1351141cc406Sopenharmony_ci
1352141cc406Sopenharmony_ci              el_endpoint =
1353141cc406Sopenharmony_ci                  sanei_xml_find_next_child_with_name(el_endpoint, "endpoint");
1354141cc406Sopenharmony_ci            }
1355141cc406Sopenharmony_ci          device.alt_setting = 0;
1356141cc406Sopenharmony_ci          device.missing = 0;
1357141cc406Sopenharmony_ci
1358141cc406Sopenharmony_ci          memcpy(&(devices[device_number]), &device, sizeof(device));
1359141cc406Sopenharmony_ci          device_number++;
1360141cc406Sopenharmony_ci
1361141cc406Sopenharmony_ci          el_interface = sanei_xml_find_next_child_with_name(el_interface,
1362141cc406Sopenharmony_ci                                                             "interface");
1363141cc406Sopenharmony_ci        }
1364141cc406Sopenharmony_ci      el_configuration =
1365141cc406Sopenharmony_ci            sanei_xml_find_next_child_with_name(el_configurations,
1366141cc406Sopenharmony_ci                                                "configuration");
1367141cc406Sopenharmony_ci    }
1368141cc406Sopenharmony_ci
1369141cc406Sopenharmony_ci  xmlNode* el_transactions =
1370141cc406Sopenharmony_ci      sanei_xml_find_first_child_with_name(el_root, "transactions");
1371141cc406Sopenharmony_ci
1372141cc406Sopenharmony_ci  if (el_transactions == NULL)
1373141cc406Sopenharmony_ci    {
1374141cc406Sopenharmony_ci      DBG(1, "%s: could not find transactions node\n", __func__);
1375141cc406Sopenharmony_ci      return SANE_STATUS_INVAL;
1376141cc406Sopenharmony_ci    }
1377141cc406Sopenharmony_ci
1378141cc406Sopenharmony_ci  xmlNode* el_transaction = xmlFirstElementChild(el_transactions);
1379141cc406Sopenharmony_ci  el_transaction = sanei_xml_skip_non_tx_nodes(el_transaction);
1380141cc406Sopenharmony_ci
1381141cc406Sopenharmony_ci  if (el_transaction == NULL)
1382141cc406Sopenharmony_ci    {
1383141cc406Sopenharmony_ci      DBG(1, "%s: no transactions within capture\n", __func__);
1384141cc406Sopenharmony_ci      return SANE_STATUS_INVAL;
1385141cc406Sopenharmony_ci    }
1386141cc406Sopenharmony_ci
1387141cc406Sopenharmony_ci  testing_xml_next_tx_node = el_transaction;
1388141cc406Sopenharmony_ci
1389141cc406Sopenharmony_ci  return SANE_STATUS_GOOD;
1390141cc406Sopenharmony_ci}
1391141cc406Sopenharmony_ci
1392141cc406Sopenharmony_cistatic void sanei_usb_testing_exit()
1393141cc406Sopenharmony_ci{
1394141cc406Sopenharmony_ci  if (testing_development_mode || testing_mode == sanei_usb_testing_mode_record)
1395141cc406Sopenharmony_ci    {
1396141cc406Sopenharmony_ci      if (testing_mode == sanei_usb_testing_mode_record)
1397141cc406Sopenharmony_ci        {
1398141cc406Sopenharmony_ci          xmlAddNextSibling(testing_append_commands_node, xmlNewText((const xmlChar*)"\n  "));
1399141cc406Sopenharmony_ci          free(testing_record_backend);
1400141cc406Sopenharmony_ci        }
1401141cc406Sopenharmony_ci      xmlSaveFileEnc(testing_xml_path, testing_xml_doc, "UTF-8");
1402141cc406Sopenharmony_ci    }
1403141cc406Sopenharmony_ci  xmlFreeDoc(testing_xml_doc);
1404141cc406Sopenharmony_ci  free(testing_xml_path);
1405141cc406Sopenharmony_ci  xmlCleanupParser();
1406141cc406Sopenharmony_ci
1407141cc406Sopenharmony_ci  // reset testing-related all data to initial values
1408141cc406Sopenharmony_ci  testing_development_mode = 0;
1409141cc406Sopenharmony_ci  testing_already_opened = 0;
1410141cc406Sopenharmony_ci  testing_known_commands_input_failed = 0;
1411141cc406Sopenharmony_ci  testing_last_known_seq = 0;
1412141cc406Sopenharmony_ci  testing_record_backend = NULL;
1413141cc406Sopenharmony_ci  testing_append_commands_node = NULL;
1414141cc406Sopenharmony_ci
1415141cc406Sopenharmony_ci  testing_xml_path = NULL;
1416141cc406Sopenharmony_ci  testing_xml_doc = NULL;
1417141cc406Sopenharmony_ci  testing_xml_next_tx_node = NULL;
1418141cc406Sopenharmony_ci}
1419141cc406Sopenharmony_ci#else // WITH_USB_RECORD_REPLAY
1420141cc406Sopenharmony_ciSANE_Status sanei_usb_testing_enable_replay(SANE_String_Const path,
1421141cc406Sopenharmony_ci                                            int development_mode)
1422141cc406Sopenharmony_ci{
1423141cc406Sopenharmony_ci  (void) path;
1424141cc406Sopenharmony_ci  (void) development_mode;
1425141cc406Sopenharmony_ci
1426141cc406Sopenharmony_ci  DBG(1, "USB record-replay mode support is missing\n");
1427141cc406Sopenharmony_ci  return SANE_STATUS_UNSUPPORTED;
1428141cc406Sopenharmony_ci}
1429141cc406Sopenharmony_ci
1430141cc406Sopenharmony_ciSANE_Status sanei_usb_testing_enable_record(SANE_String_Const path, SANE_String_Const be_name)
1431141cc406Sopenharmony_ci{
1432141cc406Sopenharmony_ci  (void) path;
1433141cc406Sopenharmony_ci  (void) be_name;
1434141cc406Sopenharmony_ci
1435141cc406Sopenharmony_ci  DBG(1, "USB record-replay mode support is missing\n");
1436141cc406Sopenharmony_ci  return SANE_STATUS_UNSUPPORTED;
1437141cc406Sopenharmony_ci}
1438141cc406Sopenharmony_ci
1439141cc406Sopenharmony_ciSANE_String sanei_usb_testing_get_backend()
1440141cc406Sopenharmony_ci{
1441141cc406Sopenharmony_ci  return NULL;
1442141cc406Sopenharmony_ci}
1443141cc406Sopenharmony_ci
1444141cc406Sopenharmony_ciSANE_Bool sanei_usb_is_replay_mode_enabled()
1445141cc406Sopenharmony_ci{
1446141cc406Sopenharmony_ci  return SANE_FALSE;
1447141cc406Sopenharmony_ci}
1448141cc406Sopenharmony_ci
1449141cc406Sopenharmony_civoid sanei_usb_testing_record_clear()
1450141cc406Sopenharmony_ci{
1451141cc406Sopenharmony_ci}
1452141cc406Sopenharmony_ci
1453141cc406Sopenharmony_civoid sanei_usb_testing_record_message(SANE_String_Const message)
1454141cc406Sopenharmony_ci{
1455141cc406Sopenharmony_ci  (void) message;
1456141cc406Sopenharmony_ci}
1457141cc406Sopenharmony_ci#endif // WITH_USB_RECORD_REPLAY
1458141cc406Sopenharmony_ci
1459141cc406Sopenharmony_civoid
1460141cc406Sopenharmony_cisanei_usb_init (void)
1461141cc406Sopenharmony_ci{
1462141cc406Sopenharmony_ci#if defined(HAVE_LIBUSB) || defined(HAVE_USB_MANAGER)
1463141cc406Sopenharmony_ci  int ret;
1464141cc406Sopenharmony_ci#endif /* HAVE_LIBUSB || HAVE_USB_MANAGER */
1465141cc406Sopenharmony_ci
1466141cc406Sopenharmony_ci  DBG_INIT ();
1467141cc406Sopenharmony_ci#ifdef DBG_LEVEL
1468141cc406Sopenharmony_ci  debug_level = DBG_LEVEL;
1469141cc406Sopenharmony_ci#else
1470141cc406Sopenharmony_ci  debug_level = 0;
1471141cc406Sopenharmony_ci#endif
1472141cc406Sopenharmony_ci
1473141cc406Sopenharmony_ci  /* if no device yet, clean up memory */
1474141cc406Sopenharmony_ci  if(device_number==0)
1475141cc406Sopenharmony_ci    memset (devices, 0, sizeof (devices));
1476141cc406Sopenharmony_ci
1477141cc406Sopenharmony_ci#if WITH_USB_RECORD_REPLAY
1478141cc406Sopenharmony_ci  if (testing_mode != sanei_usb_testing_mode_disabled)
1479141cc406Sopenharmony_ci    {
1480141cc406Sopenharmony_ci      if (initialized == 0)
1481141cc406Sopenharmony_ci        {
1482141cc406Sopenharmony_ci          if (sanei_usb_testing_init() != SANE_STATUS_GOOD)
1483141cc406Sopenharmony_ci            {
1484141cc406Sopenharmony_ci              DBG(1, "%s: failed initializing fake USB stack\n", __func__);
1485141cc406Sopenharmony_ci              return;
1486141cc406Sopenharmony_ci            }
1487141cc406Sopenharmony_ci        }
1488141cc406Sopenharmony_ci
1489141cc406Sopenharmony_ci      if (testing_mode == sanei_usb_testing_mode_replay)
1490141cc406Sopenharmony_ci        {
1491141cc406Sopenharmony_ci          initialized++;
1492141cc406Sopenharmony_ci          return;
1493141cc406Sopenharmony_ci        }
1494141cc406Sopenharmony_ci    }
1495141cc406Sopenharmony_ci#endif
1496141cc406Sopenharmony_ci
1497141cc406Sopenharmony_ci  /* initialize USB with old libusb library */
1498141cc406Sopenharmony_ci#ifdef HAVE_LIBUSB_LEGACY
1499141cc406Sopenharmony_ci  DBG (4, "%s: Looking for libusb devices\n", __func__);
1500141cc406Sopenharmony_ci  usb_init ();
1501141cc406Sopenharmony_ci#ifdef DBG_LEVEL
1502141cc406Sopenharmony_ci  if (DBG_LEVEL > 4)
1503141cc406Sopenharmony_ci    usb_set_debug (255);
1504141cc406Sopenharmony_ci#endif /* DBG_LEVEL */
1505141cc406Sopenharmony_ci#endif /* HAVE_LIBUSB_LEGACY */
1506141cc406Sopenharmony_ci
1507141cc406Sopenharmony_ci
1508141cc406Sopenharmony_ci  /* initialize USB using libusb-1.0 */
1509141cc406Sopenharmony_ci#ifdef HAVE_LIBUSB
1510141cc406Sopenharmony_ci  if (!sanei_usb_ctx)
1511141cc406Sopenharmony_ci    {
1512141cc406Sopenharmony_ci      DBG (4, "%s: initializing libusb-1.0\n", __func__);
1513141cc406Sopenharmony_ci      ret = libusb_init (&sanei_usb_ctx);
1514141cc406Sopenharmony_ci      if (ret < 0)
1515141cc406Sopenharmony_ci	{
1516141cc406Sopenharmony_ci	  DBG (1,
1517141cc406Sopenharmony_ci	       "%s: failed to initialize libusb-1.0, error %d\n", __func__,
1518141cc406Sopenharmony_ci	       ret);
1519141cc406Sopenharmony_ci          return;
1520141cc406Sopenharmony_ci	}
1521141cc406Sopenharmony_ci#ifdef DBG_LEVEL
1522141cc406Sopenharmony_ci      if (DBG_LEVEL > 4)
1523141cc406Sopenharmony_ci#if LIBUSB_API_VERSION >= 0x01000106
1524141cc406Sopenharmony_ci        libusb_set_option (sanei_usb_ctx, LIBUSB_OPTION_LOG_LEVEL,
1525141cc406Sopenharmony_ci                           LIBUSB_LOG_LEVEL_INFO);
1526141cc406Sopenharmony_ci#else
1527141cc406Sopenharmony_ci	libusb_set_debug (sanei_usb_ctx, 3);
1528141cc406Sopenharmony_ci#endif /* LIBUSB_API_VERSION */
1529141cc406Sopenharmony_ci#endif /* DBG_LEVEL */
1530141cc406Sopenharmony_ci    }
1531141cc406Sopenharmony_ci#endif /* HAVE_LIBUSB */
1532141cc406Sopenharmony_ci
1533141cc406Sopenharmony_ci#ifdef HAVE_USB_MANAGER
1534141cc406Sopenharmony_ci  if (!sanei_usb_ctx)
1535141cc406Sopenharmony_ci    {
1536141cc406Sopenharmony_ci      DBG (4, "%s: initializing usb_manager\n", __func__);
1537141cc406Sopenharmony_ci      ret = usb_manager_init (&sanei_usb_ctx);
1538141cc406Sopenharmony_ci      if (ret < 0)
1539141cc406Sopenharmony_ci	{
1540141cc406Sopenharmony_ci	  DBG (1,
1541141cc406Sopenharmony_ci	       "%s: failed to initialize usb_manager, error %d\n", __func__,
1542141cc406Sopenharmony_ci	       ret);
1543141cc406Sopenharmony_ci          return;
1544141cc406Sopenharmony_ci	}
1545141cc406Sopenharmony_ci    }
1546141cc406Sopenharmony_ci#endif /* HAVE_USB_MANAGER */
1547141cc406Sopenharmony_ci
1548141cc406Sopenharmony_ci#if !defined(HAVE_LIBUSB_LEGACY) && !defined(HAVE_LIBUSB) && !defined(HAVE_USB_MANAGER)
1549141cc406Sopenharmony_ci  DBG (4, "%s: SANE is built without support for libusb\n", __func__);
1550141cc406Sopenharmony_ci#endif
1551141cc406Sopenharmony_ci
1552141cc406Sopenharmony_ci  /* sanei_usb is now initialized */
1553141cc406Sopenharmony_ci  initialized++;
1554141cc406Sopenharmony_ci
1555141cc406Sopenharmony_ci  /* do a first scan of USB buses to fill device list */
1556141cc406Sopenharmony_ci  sanei_usb_scan_devices();
1557141cc406Sopenharmony_ci}
1558141cc406Sopenharmony_ci
1559141cc406Sopenharmony_civoid
1560141cc406Sopenharmony_cisanei_usb_exit (void)
1561141cc406Sopenharmony_ci{
1562141cc406Sopenharmony_ciint i;
1563141cc406Sopenharmony_ci
1564141cc406Sopenharmony_ci  /* check we have really some work to do */
1565141cc406Sopenharmony_ci  if(initialized==0)
1566141cc406Sopenharmony_ci    {
1567141cc406Sopenharmony_ci      DBG (1, "%s: sanei_usb in not initialized!\n", __func__);
1568141cc406Sopenharmony_ci      return;
1569141cc406Sopenharmony_ci    }
1570141cc406Sopenharmony_ci
1571141cc406Sopenharmony_ci  /* decrement the use count */
1572141cc406Sopenharmony_ci  initialized--;
1573141cc406Sopenharmony_ci
1574141cc406Sopenharmony_ci  /* if we reach 0, free allocated resources */
1575141cc406Sopenharmony_ci  if(initialized==0)
1576141cc406Sopenharmony_ci    {
1577141cc406Sopenharmony_ci#if WITH_USB_RECORD_REPLAY
1578141cc406Sopenharmony_ci      if (testing_mode != sanei_usb_testing_mode_disabled)
1579141cc406Sopenharmony_ci        {
1580141cc406Sopenharmony_ci          sanei_usb_testing_exit();
1581141cc406Sopenharmony_ci        }
1582141cc406Sopenharmony_ci#endif // WITH_USB_RECORD_REPLAY
1583141cc406Sopenharmony_ci
1584141cc406Sopenharmony_ci      /* free allocated resources */
1585141cc406Sopenharmony_ci      DBG (4, "%s: freeing resources\n", __func__);
1586141cc406Sopenharmony_ci      for (i = 0; i < device_number; i++)
1587141cc406Sopenharmony_ci        {
1588141cc406Sopenharmony_ci          if (devices[i].devname != NULL)
1589141cc406Sopenharmony_ci            {
1590141cc406Sopenharmony_ci              DBG (5, "%s: freeing device %02d\n", __func__, i);
1591141cc406Sopenharmony_ci              free(devices[i].devname);
1592141cc406Sopenharmony_ci              devices[i].devname=NULL;
1593141cc406Sopenharmony_ci            }
1594141cc406Sopenharmony_ci        }
1595141cc406Sopenharmony_ci#if defined(HAVE_LIBUSB)
1596141cc406Sopenharmony_ci      if (sanei_usb_ctx)
1597141cc406Sopenharmony_ci        {
1598141cc406Sopenharmony_ci          libusb_exit (sanei_usb_ctx);
1599141cc406Sopenharmony_ci	  /* reset libusb-1.0 context */
1600141cc406Sopenharmony_ci	  sanei_usb_ctx=NULL;
1601141cc406Sopenharmony_ci        }
1602141cc406Sopenharmony_ci#elif defined(HAVE_USB_MANAGER)
1603141cc406Sopenharmony_ci      if (sanei_usb_ctx)
1604141cc406Sopenharmony_ci        {
1605141cc406Sopenharmony_ci          usb_manager_exit (sanei_usb_ctx);
1606141cc406Sopenharmony_ci	  /* reset usb_manager context */
1607141cc406Sopenharmony_ci	  sanei_usb_ctx=NULL;
1608141cc406Sopenharmony_ci        }
1609141cc406Sopenharmony_ci#endif /* HAVE_LIBUSB || HAVE_USB_MANAGER */
1610141cc406Sopenharmony_ci      /* reset device_number */
1611141cc406Sopenharmony_ci      device_number=0;
1612141cc406Sopenharmony_ci    }
1613141cc406Sopenharmony_ci  else
1614141cc406Sopenharmony_ci    {
1615141cc406Sopenharmony_ci      DBG (4, "%s: not freeing resources since use count is %d\n", __func__, initialized);
1616141cc406Sopenharmony_ci    }
1617141cc406Sopenharmony_ci  return;
1618141cc406Sopenharmony_ci}
1619141cc406Sopenharmony_ci
1620141cc406Sopenharmony_ci#ifdef HAVE_USBCALLS
1621141cc406Sopenharmony_ci/** scan for devices through usbcall method
1622141cc406Sopenharmony_ci * Check for devices using OS/2 USBCALLS Interface
1623141cc406Sopenharmony_ci */
1624141cc406Sopenharmony_cistatic void usbcall_scan_devices(void)
1625141cc406Sopenharmony_ci{
1626141cc406Sopenharmony_ci  SANE_Char devname[1024];
1627141cc406Sopenharmony_ci  device_list_type device;
1628141cc406Sopenharmony_ci  CHAR ucData[2048];
1629141cc406Sopenharmony_ci  struct usb_device_descriptor *pDevDesc;
1630141cc406Sopenharmony_ci  struct usb_config_descriptor   *pCfgDesc;
1631141cc406Sopenharmony_ci
1632141cc406Sopenharmony_ci   APIRET rc;
1633141cc406Sopenharmony_ci   ULONG ulNumDev, ulDev, ulBufLen;
1634141cc406Sopenharmony_ci
1635141cc406Sopenharmony_ci   ulBufLen = sizeof(ucData);
1636141cc406Sopenharmony_ci   memset(&ucData,0,sizeof(ucData));
1637141cc406Sopenharmony_ci   rc = UsbQueryNumberDevices( &ulNumDev);
1638141cc406Sopenharmony_ci
1639141cc406Sopenharmony_ci   if(rc==0 && ulNumDev)
1640141cc406Sopenharmony_ci   {
1641141cc406Sopenharmony_ci       for (ulDev=1; ulDev<=ulNumDev; ulDev++)
1642141cc406Sopenharmony_ci       {
1643141cc406Sopenharmony_ci         UsbQueryDeviceReport(ulDev, &ulBufLen, ucData);
1644141cc406Sopenharmony_ci
1645141cc406Sopenharmony_ci         pDevDesc = (struct usb_device_descriptor*) ucData;
1646141cc406Sopenharmony_ci         pCfgDesc = (struct usb_config_descriptor*) (ucData+sizeof(struct usb_device_descriptor));
1647141cc406Sopenharmony_ci	  int interface=0;
1648141cc406Sopenharmony_ci	  SANE_Bool found;
1649141cc406Sopenharmony_ci	  if (!pCfgDesc->bConfigurationValue)
1650141cc406Sopenharmony_ci	    {
1651141cc406Sopenharmony_ci	      DBG (1, "%s: device 0x%04x/0x%04x is not configured\n", __func__,
1652141cc406Sopenharmony_ci		   pDevDesc->idVendor, pDevDesc->idProduct);
1653141cc406Sopenharmony_ci	      continue;
1654141cc406Sopenharmony_ci	    }
1655141cc406Sopenharmony_ci	  if (pDevDesc->idVendor == 0 || pDevDesc->idProduct == 0)
1656141cc406Sopenharmony_ci	    {
1657141cc406Sopenharmony_ci	      DBG (5, "%s: device 0x%04x/0x%04x looks like a root hub\n", __func__,
1658141cc406Sopenharmony_ci		   pDevDesc->idVendor, pDevDesc->idProduct);
1659141cc406Sopenharmony_ci	      continue;
1660141cc406Sopenharmony_ci	    }
1661141cc406Sopenharmony_ci	  found = SANE_FALSE;
1662141cc406Sopenharmony_ci
1663141cc406Sopenharmony_ci          if (pDevDesc->bDeviceClass == USB_CLASS_VENDOR_SPEC)
1664141cc406Sopenharmony_ci           {
1665141cc406Sopenharmony_ci             found = SANE_TRUE;
1666141cc406Sopenharmony_ci           }
1667141cc406Sopenharmony_ci
1668141cc406Sopenharmony_ci	  if (!found)
1669141cc406Sopenharmony_ci	    {
1670141cc406Sopenharmony_ci	      DBG (5, "%s: device 0x%04x/0x%04x: no suitable interfaces\n", __func__,
1671141cc406Sopenharmony_ci		   pDevDesc->idVendor, pDevDesc->idProduct);
1672141cc406Sopenharmony_ci	      continue;
1673141cc406Sopenharmony_ci	    }
1674141cc406Sopenharmony_ci
1675141cc406Sopenharmony_ci	  snprintf (devname, sizeof (devname), "usbcalls:%d", ulDev);
1676141cc406Sopenharmony_ci          memset (&device, 0, sizeof (device));
1677141cc406Sopenharmony_ci	  device.devname = strdup (devname);
1678141cc406Sopenharmony_ci          device.fd = ulDev; /* store usbcalls device number */
1679141cc406Sopenharmony_ci	  device.vendor = pDevDesc->idVendor;
1680141cc406Sopenharmony_ci	  device.product = pDevDesc->idProduct;
1681141cc406Sopenharmony_ci	  device.method = sanei_usb_method_usbcalls;
1682141cc406Sopenharmony_ci	  device.interface_nr = interface;
1683141cc406Sopenharmony_ci	  device.alt_setting = 0;
1684141cc406Sopenharmony_ci	  DBG (4, "%s: found usbcalls device (0x%04x/0x%04x) as device number %s\n", __func__,
1685141cc406Sopenharmony_ci	       pDevDesc->idVendor, pDevDesc->idProduct,device.devname);
1686141cc406Sopenharmony_ci	  store_device(device);
1687141cc406Sopenharmony_ci       }
1688141cc406Sopenharmony_ci   }
1689141cc406Sopenharmony_ci}
1690141cc406Sopenharmony_ci#endif /* HAVE_USBCALLS */
1691141cc406Sopenharmony_ci
1692141cc406Sopenharmony_ci#if !defined(HAVE_LIBUSB_LEGACY) && !defined(HAVE_LIBUSB) && !defined(HAVE_USB_MANAGER)
1693141cc406Sopenharmony_ci/** scan for devices using kernel device.
1694141cc406Sopenharmony_ci * Check for devices using kernel device
1695141cc406Sopenharmony_ci */
1696141cc406Sopenharmony_cistatic void kernel_scan_devices(void)
1697141cc406Sopenharmony_ci{
1698141cc406Sopenharmony_ci  SANE_String *prefix;
1699141cc406Sopenharmony_ci  SANE_String prefixlist[] = {
1700141cc406Sopenharmony_ci#if defined(__linux__)
1701141cc406Sopenharmony_ci    "/dev/", "usbscanner",
1702141cc406Sopenharmony_ci    "/dev/usb/", "scanner",
1703141cc406Sopenharmony_ci#elif defined(__FreeBSD__) || defined(__NetBSD__) || defined (__OpenBSD__) || defined (__DragonFly__)
1704141cc406Sopenharmony_ci    "/dev/", "uscanner",
1705141cc406Sopenharmony_ci#elif defined(__BEOS__)
1706141cc406Sopenharmony_ci    "/dev/scanner/usb/", "",
1707141cc406Sopenharmony_ci#endif
1708141cc406Sopenharmony_ci    0, 0
1709141cc406Sopenharmony_ci  };
1710141cc406Sopenharmony_ci  SANE_Int vendor, product;
1711141cc406Sopenharmony_ci  SANE_Char devname[1024];
1712141cc406Sopenharmony_ci  int fd;
1713141cc406Sopenharmony_ci  device_list_type device;
1714141cc406Sopenharmony_ci
1715141cc406Sopenharmony_ci  DBG (4, "%s: Looking for kernel scanner devices\n", __func__);
1716141cc406Sopenharmony_ci  /* Check for devices using the kernel scanner driver */
1717141cc406Sopenharmony_ci
1718141cc406Sopenharmony_ci  for (prefix = prefixlist; *prefix; prefix += 2)
1719141cc406Sopenharmony_ci    {
1720141cc406Sopenharmony_ci      SANE_String dir_name = *prefix;
1721141cc406Sopenharmony_ci      SANE_String base_name = *(prefix + 1);
1722141cc406Sopenharmony_ci      struct stat stat_buf;
1723141cc406Sopenharmony_ci      DIR *dir;
1724141cc406Sopenharmony_ci      struct dirent *dir_entry;
1725141cc406Sopenharmony_ci
1726141cc406Sopenharmony_ci      if (stat (dir_name, &stat_buf) < 0)
1727141cc406Sopenharmony_ci	{
1728141cc406Sopenharmony_ci	  DBG (5, "%s: can't stat %s: %s\n", __func__, dir_name,
1729141cc406Sopenharmony_ci	       strerror (errno));
1730141cc406Sopenharmony_ci	  continue;
1731141cc406Sopenharmony_ci	}
1732141cc406Sopenharmony_ci      if (!S_ISDIR (stat_buf.st_mode))
1733141cc406Sopenharmony_ci	{
1734141cc406Sopenharmony_ci	  DBG (5, "%s: %s is not a directory\n", __func__, dir_name);
1735141cc406Sopenharmony_ci	  continue;
1736141cc406Sopenharmony_ci	}
1737141cc406Sopenharmony_ci      if ((dir = opendir (dir_name)) == 0)
1738141cc406Sopenharmony_ci	{
1739141cc406Sopenharmony_ci	  DBG (5, "%s: cannot read directory %s: %s\n", __func__, dir_name,
1740141cc406Sopenharmony_ci	       strerror (errno));
1741141cc406Sopenharmony_ci	  continue;
1742141cc406Sopenharmony_ci	}
1743141cc406Sopenharmony_ci
1744141cc406Sopenharmony_ci      while ((dir_entry = readdir (dir)) != 0)
1745141cc406Sopenharmony_ci	{
1746141cc406Sopenharmony_ci	  /* skip standard dir entries */
1747141cc406Sopenharmony_ci	  if (strcmp (dir_entry->d_name, ".") == 0 || strcmp (dir_entry->d_name, "..") == 0)
1748141cc406Sopenharmony_ci	  	continue;
1749141cc406Sopenharmony_ci
1750141cc406Sopenharmony_ci	  if (strncmp (base_name, dir_entry->d_name, strlen (base_name)) == 0)
1751141cc406Sopenharmony_ci	    {
1752141cc406Sopenharmony_ci	      if (strlen (dir_name) + strlen (dir_entry->d_name) + 1 >
1753141cc406Sopenharmony_ci		  sizeof (devname))
1754141cc406Sopenharmony_ci		continue;
1755141cc406Sopenharmony_ci	      sprintf (devname, "%s%s", dir_name, dir_entry->d_name);
1756141cc406Sopenharmony_ci	      fd = -1;
1757141cc406Sopenharmony_ci#ifdef HAVE_RESMGR
1758141cc406Sopenharmony_ci	      fd = rsm_open_device (devname, O_RDWR);
1759141cc406Sopenharmony_ci#endif
1760141cc406Sopenharmony_ci	      if (fd == -1)
1761141cc406Sopenharmony_ci		fd = open (devname, O_RDWR);
1762141cc406Sopenharmony_ci	      if (fd < 0)
1763141cc406Sopenharmony_ci		{
1764141cc406Sopenharmony_ci		  DBG (5, "%s: couldn't open %s: %s\n", __func__, devname,
1765141cc406Sopenharmony_ci		       strerror (errno));
1766141cc406Sopenharmony_ci		  continue;
1767141cc406Sopenharmony_ci		}
1768141cc406Sopenharmony_ci	      vendor = -1;
1769141cc406Sopenharmony_ci	      product = -1;
1770141cc406Sopenharmony_ci	      kernel_get_vendor_product (fd, devname, &vendor, &product);
1771141cc406Sopenharmony_ci	      close (fd);
1772141cc406Sopenharmony_ci    	      memset (&device, 0, sizeof (device));
1773141cc406Sopenharmony_ci	      device.devname = strdup (devname);
1774141cc406Sopenharmony_ci	      if (!device.devname)
1775141cc406Sopenharmony_ci		{
1776141cc406Sopenharmony_ci		  closedir (dir);
1777141cc406Sopenharmony_ci		  return;
1778141cc406Sopenharmony_ci		}
1779141cc406Sopenharmony_ci	      device.vendor = vendor;
1780141cc406Sopenharmony_ci	      device.product = product;
1781141cc406Sopenharmony_ci	      device.method = sanei_usb_method_scanner_driver;
1782141cc406Sopenharmony_ci	      DBG (4,
1783141cc406Sopenharmony_ci		   "%s: found kernel scanner device (0x%04x/0x%04x) at %s\n", __func__,
1784141cc406Sopenharmony_ci		   vendor, product, devname);
1785141cc406Sopenharmony_ci	      store_device(device);
1786141cc406Sopenharmony_ci	    }
1787141cc406Sopenharmony_ci	}
1788141cc406Sopenharmony_ci      closedir (dir);
1789141cc406Sopenharmony_ci    }
1790141cc406Sopenharmony_ci}
1791141cc406Sopenharmony_ci#endif /* !defined(HAVE_LIBUSB_LEGACY) && !defined(HAVE_LIBUSB) && !defined(HAVE_USB_MANAGER) */
1792141cc406Sopenharmony_ci
1793141cc406Sopenharmony_ci#ifdef HAVE_LIBUSB_LEGACY
1794141cc406Sopenharmony_ci/** scan for devices using old libusb
1795141cc406Sopenharmony_ci * Check for devices using 0.1.x libusb
1796141cc406Sopenharmony_ci */
1797141cc406Sopenharmony_cistatic void libusb_scan_devices(void)
1798141cc406Sopenharmony_ci{
1799141cc406Sopenharmony_ci  struct usb_bus *bus;
1800141cc406Sopenharmony_ci  struct usb_device *dev;
1801141cc406Sopenharmony_ci  SANE_Char devname[1024];
1802141cc406Sopenharmony_ci  device_list_type device;
1803141cc406Sopenharmony_ci
1804141cc406Sopenharmony_ci  DBG (4, "%s: Looking for libusb devices\n", __func__);
1805141cc406Sopenharmony_ci
1806141cc406Sopenharmony_ci  usb_find_busses ();
1807141cc406Sopenharmony_ci  usb_find_devices ();
1808141cc406Sopenharmony_ci
1809141cc406Sopenharmony_ci  /* Check for the matching device */
1810141cc406Sopenharmony_ci  for (bus = usb_get_busses (); bus; bus = bus->next)
1811141cc406Sopenharmony_ci    {
1812141cc406Sopenharmony_ci      for (dev = bus->devices; dev; dev = dev->next)
1813141cc406Sopenharmony_ci	{
1814141cc406Sopenharmony_ci	  int interface;
1815141cc406Sopenharmony_ci	  SANE_Bool found = SANE_FALSE;
1816141cc406Sopenharmony_ci
1817141cc406Sopenharmony_ci	  if (!dev->config)
1818141cc406Sopenharmony_ci	    {
1819141cc406Sopenharmony_ci	      DBG (1,
1820141cc406Sopenharmony_ci		   "%s: device 0x%04x/0x%04x is not configured\n", __func__,
1821141cc406Sopenharmony_ci		   dev->descriptor.idVendor, dev->descriptor.idProduct);
1822141cc406Sopenharmony_ci	      continue;
1823141cc406Sopenharmony_ci	    }
1824141cc406Sopenharmony_ci	  if (dev->descriptor.idVendor == 0 || dev->descriptor.idProduct == 0)
1825141cc406Sopenharmony_ci	    {
1826141cc406Sopenharmony_ci	      DBG (5,
1827141cc406Sopenharmony_ci		 "%s: device 0x%04x/0x%04x looks like a root hub\n", __func__,
1828141cc406Sopenharmony_ci		 dev->descriptor.idVendor, dev->descriptor.idProduct);
1829141cc406Sopenharmony_ci	      continue;
1830141cc406Sopenharmony_ci	    }
1831141cc406Sopenharmony_ci
1832141cc406Sopenharmony_ci	  for (interface = 0;
1833141cc406Sopenharmony_ci	       interface < dev->config[0].bNumInterfaces && !found;
1834141cc406Sopenharmony_ci	       interface++)
1835141cc406Sopenharmony_ci	    {
1836141cc406Sopenharmony_ci	      switch (dev->descriptor.bDeviceClass)
1837141cc406Sopenharmony_ci		{
1838141cc406Sopenharmony_ci		case USB_CLASS_VENDOR_SPEC:
1839141cc406Sopenharmony_ci		  found = SANE_TRUE;
1840141cc406Sopenharmony_ci		  break;
1841141cc406Sopenharmony_ci		case USB_CLASS_PER_INTERFACE:
1842141cc406Sopenharmony_ci		  if (dev->config[0].interface[interface].num_altsetting == 0 ||
1843141cc406Sopenharmony_ci		      !dev->config[0].interface[interface].altsetting)
1844141cc406Sopenharmony_ci		    {
1845141cc406Sopenharmony_ci		      DBG (1, "%s: device 0x%04x/0x%04x doesn't "
1846141cc406Sopenharmony_ci			   "have an altsetting for interface %d\n", __func__,
1847141cc406Sopenharmony_ci			   dev->descriptor.idVendor, dev->descriptor.idProduct,
1848141cc406Sopenharmony_ci			   interface);
1849141cc406Sopenharmony_ci		      continue;
1850141cc406Sopenharmony_ci		    }
1851141cc406Sopenharmony_ci		  switch (dev->config[0].interface[interface].altsetting[0].
1852141cc406Sopenharmony_ci			  bInterfaceClass)
1853141cc406Sopenharmony_ci		    {
1854141cc406Sopenharmony_ci		    case USB_CLASS_VENDOR_SPEC:
1855141cc406Sopenharmony_ci		    case USB_CLASS_PER_INTERFACE:
1856141cc406Sopenharmony_ci		    case 6:	/* imaging? */
1857141cc406Sopenharmony_ci		    case 16:	/* data? */
1858141cc406Sopenharmony_ci		      found = SANE_TRUE;
1859141cc406Sopenharmony_ci		      break;
1860141cc406Sopenharmony_ci		    }
1861141cc406Sopenharmony_ci		  break;
1862141cc406Sopenharmony_ci		}
1863141cc406Sopenharmony_ci	      if (!found)
1864141cc406Sopenharmony_ci		DBG (5,
1865141cc406Sopenharmony_ci		     "%s: device 0x%04x/0x%04x, interface %d "
1866141cc406Sopenharmony_ci                     "doesn't look like a "
1867141cc406Sopenharmony_ci		     "scanner (%d/%d)\n", __func__, dev->descriptor.idVendor,
1868141cc406Sopenharmony_ci		     dev->descriptor.idProduct, interface,
1869141cc406Sopenharmony_ci		     dev->descriptor.bDeviceClass,
1870141cc406Sopenharmony_ci		     dev->config[0].interface[interface].num_altsetting != 0
1871141cc406Sopenharmony_ci                       ? dev->config[0].interface[interface].altsetting[0].
1872141cc406Sopenharmony_ci		       bInterfaceClass : -1);
1873141cc406Sopenharmony_ci	    }
1874141cc406Sopenharmony_ci	  interface--;
1875141cc406Sopenharmony_ci	  if (!found)
1876141cc406Sopenharmony_ci	    {
1877141cc406Sopenharmony_ci	      DBG (5,
1878141cc406Sopenharmony_ci	       "%s: device 0x%04x/0x%04x: no suitable interfaces\n", __func__,
1879141cc406Sopenharmony_ci	        dev->descriptor.idVendor, dev->descriptor.idProduct);
1880141cc406Sopenharmony_ci	      continue;
1881141cc406Sopenharmony_ci	    }
1882141cc406Sopenharmony_ci
1883141cc406Sopenharmony_ci    	  memset (&device, 0, sizeof (device));
1884141cc406Sopenharmony_ci	  device.libusb_device = dev;
1885141cc406Sopenharmony_ci	  snprintf (devname, sizeof (devname), "libusb:%s:%s",
1886141cc406Sopenharmony_ci		    dev->bus->dirname, dev->filename);
1887141cc406Sopenharmony_ci	  device.devname = strdup (devname);
1888141cc406Sopenharmony_ci	  if (!device.devname)
1889141cc406Sopenharmony_ci	    return;
1890141cc406Sopenharmony_ci	  device.vendor = dev->descriptor.idVendor;
1891141cc406Sopenharmony_ci	  device.product = dev->descriptor.idProduct;
1892141cc406Sopenharmony_ci	  device.method = sanei_usb_method_libusb;
1893141cc406Sopenharmony_ci	  device.interface_nr = interface;
1894141cc406Sopenharmony_ci	  device.alt_setting = 0;
1895141cc406Sopenharmony_ci	  DBG (4,
1896141cc406Sopenharmony_ci	       "%s: found libusb device (0x%04x/0x%04x) interface "
1897141cc406Sopenharmony_ci               "%d  at %s\n", __func__,
1898141cc406Sopenharmony_ci	       dev->descriptor.idVendor, dev->descriptor.idProduct, interface,
1899141cc406Sopenharmony_ci	       devname);
1900141cc406Sopenharmony_ci	  store_device(device);
1901141cc406Sopenharmony_ci	}
1902141cc406Sopenharmony_ci    }
1903141cc406Sopenharmony_ci}
1904141cc406Sopenharmony_ci#endif /* HAVE_LIBUSB_LEGACY */
1905141cc406Sopenharmony_ci
1906141cc406Sopenharmony_ci#ifdef HAVE_LIBUSB
1907141cc406Sopenharmony_ci/** scan for devices using libusb
1908141cc406Sopenharmony_ci * Check for devices using libusb-1.0
1909141cc406Sopenharmony_ci */
1910141cc406Sopenharmony_cistatic void libusb_scan_devices(void)
1911141cc406Sopenharmony_ci{
1912141cc406Sopenharmony_ci  device_list_type device;
1913141cc406Sopenharmony_ci  SANE_Char devname[1024];
1914141cc406Sopenharmony_ci  libusb_device **devlist;
1915141cc406Sopenharmony_ci  ssize_t ndev;
1916141cc406Sopenharmony_ci  libusb_device *dev;
1917141cc406Sopenharmony_ci  libusb_device_handle *hdl;
1918141cc406Sopenharmony_ci  struct libusb_device_descriptor desc;
1919141cc406Sopenharmony_ci  struct libusb_config_descriptor *config0;
1920141cc406Sopenharmony_ci  unsigned short vid, pid;
1921141cc406Sopenharmony_ci  unsigned char busno, address;
1922141cc406Sopenharmony_ci  int config;
1923141cc406Sopenharmony_ci  int interface;
1924141cc406Sopenharmony_ci  int ret;
1925141cc406Sopenharmony_ci  int i;
1926141cc406Sopenharmony_ci
1927141cc406Sopenharmony_ci  DBG (4, "%s: Looking for libusb-1.0 devices\n", __func__);
1928141cc406Sopenharmony_ci
1929141cc406Sopenharmony_ci  ndev = libusb_get_device_list (sanei_usb_ctx, &devlist);
1930141cc406Sopenharmony_ci  if (ndev < 0)
1931141cc406Sopenharmony_ci    {
1932141cc406Sopenharmony_ci      DBG (1,
1933141cc406Sopenharmony_ci	   "%s: failed to get libusb-1.0 device list, error %d\n", __func__,
1934141cc406Sopenharmony_ci	   (int) ndev);
1935141cc406Sopenharmony_ci      return;
1936141cc406Sopenharmony_ci    }
1937141cc406Sopenharmony_ci
1938141cc406Sopenharmony_ci  for (i = 0; i < ndev; i++)
1939141cc406Sopenharmony_ci    {
1940141cc406Sopenharmony_ci      SANE_Bool found = SANE_FALSE;
1941141cc406Sopenharmony_ci
1942141cc406Sopenharmony_ci      dev = devlist[i];
1943141cc406Sopenharmony_ci
1944141cc406Sopenharmony_ci      busno = libusb_get_bus_number (dev);
1945141cc406Sopenharmony_ci      address = libusb_get_device_address (dev);
1946141cc406Sopenharmony_ci
1947141cc406Sopenharmony_ci      ret = libusb_get_device_descriptor (dev, &desc);
1948141cc406Sopenharmony_ci      if (ret < 0)
1949141cc406Sopenharmony_ci	{
1950141cc406Sopenharmony_ci	  DBG (1,
1951141cc406Sopenharmony_ci	       "%s: could not get device descriptor for device at %03d:%03d (err %d)\n", __func__,
1952141cc406Sopenharmony_ci	       busno, address, ret);
1953141cc406Sopenharmony_ci	  continue;
1954141cc406Sopenharmony_ci	}
1955141cc406Sopenharmony_ci
1956141cc406Sopenharmony_ci      vid = desc.idVendor;
1957141cc406Sopenharmony_ci      pid = desc.idProduct;
1958141cc406Sopenharmony_ci
1959141cc406Sopenharmony_ci      if ((vid == 0) || (pid == 0))
1960141cc406Sopenharmony_ci	{
1961141cc406Sopenharmony_ci	  DBG (5,
1962141cc406Sopenharmony_ci	       "%s: device 0x%04x/0x%04x at %03d:%03d looks like a root hub\n", __func__,
1963141cc406Sopenharmony_ci	       vid, pid, busno, address);
1964141cc406Sopenharmony_ci	  continue;
1965141cc406Sopenharmony_ci	}
1966141cc406Sopenharmony_ci
1967141cc406Sopenharmony_ci      ret = libusb_open (dev, &hdl);
1968141cc406Sopenharmony_ci      if (ret < 0)
1969141cc406Sopenharmony_ci	{
1970141cc406Sopenharmony_ci	  DBG (1,
1971141cc406Sopenharmony_ci	       "%s: skipping device 0x%04x/0x%04x at %03d:%03d: cannot open: %s\n", __func__,
1972141cc406Sopenharmony_ci	       vid, pid, busno, address, sanei_libusb_strerror (ret));
1973141cc406Sopenharmony_ci
1974141cc406Sopenharmony_ci	  continue;
1975141cc406Sopenharmony_ci	}
1976141cc406Sopenharmony_ci
1977141cc406Sopenharmony_ci      ret = libusb_get_configuration (hdl, &config);
1978141cc406Sopenharmony_ci
1979141cc406Sopenharmony_ci      libusb_close (hdl);
1980141cc406Sopenharmony_ci
1981141cc406Sopenharmony_ci      if (ret < 0)
1982141cc406Sopenharmony_ci	{
1983141cc406Sopenharmony_ci	  DBG (1,
1984141cc406Sopenharmony_ci	       "%s: could not get configuration for device 0x%04x/0x%04x at %03d:%03d (err %d)\n", __func__,
1985141cc406Sopenharmony_ci	       vid, pid, busno, address, ret);
1986141cc406Sopenharmony_ci	  continue;
1987141cc406Sopenharmony_ci	}
1988141cc406Sopenharmony_ci
1989141cc406Sopenharmony_ci#if !defined(SANEI_ALLOW_UNCONFIGURED_DEVICES)
1990141cc406Sopenharmony_ci      if (config == 0)
1991141cc406Sopenharmony_ci	{
1992141cc406Sopenharmony_ci	  DBG (1,
1993141cc406Sopenharmony_ci	       "%s: device 0x%04x/0x%04x at %03d:%03d is not configured\n", __func__,
1994141cc406Sopenharmony_ci	       vid, pid, busno, address);
1995141cc406Sopenharmony_ci	  continue;
1996141cc406Sopenharmony_ci	}
1997141cc406Sopenharmony_ci#endif
1998141cc406Sopenharmony_ci
1999141cc406Sopenharmony_ci      ret = libusb_get_config_descriptor (dev, 0, &config0);
2000141cc406Sopenharmony_ci      if (ret < 0)
2001141cc406Sopenharmony_ci	{
2002141cc406Sopenharmony_ci	  DBG (1,
2003141cc406Sopenharmony_ci	       "%s: could not get config[0] descriptor for device 0x%04x/0x%04x at %03d:%03d (err %d)\n", __func__,
2004141cc406Sopenharmony_ci	       vid, pid, busno, address, ret);
2005141cc406Sopenharmony_ci	  continue;
2006141cc406Sopenharmony_ci	}
2007141cc406Sopenharmony_ci
2008141cc406Sopenharmony_ci      for (interface = 0; (interface < config0->bNumInterfaces) && !found; interface++)
2009141cc406Sopenharmony_ci	{
2010141cc406Sopenharmony_ci	  switch (desc.bDeviceClass)
2011141cc406Sopenharmony_ci	    {
2012141cc406Sopenharmony_ci	      case LIBUSB_CLASS_VENDOR_SPEC:
2013141cc406Sopenharmony_ci		found = SANE_TRUE;
2014141cc406Sopenharmony_ci		break;
2015141cc406Sopenharmony_ci
2016141cc406Sopenharmony_ci	      case LIBUSB_CLASS_PER_INTERFACE:
2017141cc406Sopenharmony_ci		if ((config0->interface[interface].num_altsetting == 0)
2018141cc406Sopenharmony_ci		    || !config0->interface[interface].altsetting)
2019141cc406Sopenharmony_ci		  {
2020141cc406Sopenharmony_ci		    DBG (1, "%s: device 0x%04x/0x%04x doesn't "
2021141cc406Sopenharmony_ci			 "have an altsetting for interface %d\n", __func__,
2022141cc406Sopenharmony_ci			 vid, pid, interface);
2023141cc406Sopenharmony_ci		    continue;
2024141cc406Sopenharmony_ci		  }
2025141cc406Sopenharmony_ci
2026141cc406Sopenharmony_ci		switch (config0->interface[interface].altsetting[0].bInterfaceClass)
2027141cc406Sopenharmony_ci		  {
2028141cc406Sopenharmony_ci		    case LIBUSB_CLASS_VENDOR_SPEC:
2029141cc406Sopenharmony_ci		    case LIBUSB_CLASS_PER_INTERFACE:
2030141cc406Sopenharmony_ci		    case LIBUSB_CLASS_PTP:
2031141cc406Sopenharmony_ci		    case 16:	/* data? */
2032141cc406Sopenharmony_ci		      found = SANE_TRUE;
2033141cc406Sopenharmony_ci		      break;
2034141cc406Sopenharmony_ci		  }
2035141cc406Sopenharmony_ci		break;
2036141cc406Sopenharmony_ci	    }
2037141cc406Sopenharmony_ci
2038141cc406Sopenharmony_ci	  if (!found)
2039141cc406Sopenharmony_ci	    DBG (5,
2040141cc406Sopenharmony_ci		 "%s: device 0x%04x/0x%04x, interface %d "
2041141cc406Sopenharmony_ci		 "doesn't look like a scanner (%d/%d)\n", __func__,
2042141cc406Sopenharmony_ci		 vid, pid, interface, desc.bDeviceClass,
2043141cc406Sopenharmony_ci		 (config0->interface[interface].num_altsetting != 0)
2044141cc406Sopenharmony_ci		 ? config0->interface[interface].altsetting[0].bInterfaceClass : -1);
2045141cc406Sopenharmony_ci	}
2046141cc406Sopenharmony_ci
2047141cc406Sopenharmony_ci      libusb_free_config_descriptor (config0);
2048141cc406Sopenharmony_ci
2049141cc406Sopenharmony_ci      interface--;
2050141cc406Sopenharmony_ci
2051141cc406Sopenharmony_ci      if (!found)
2052141cc406Sopenharmony_ci	{
2053141cc406Sopenharmony_ci	  DBG (5,
2054141cc406Sopenharmony_ci	       "%s: device 0x%04x/0x%04x at %03d:%03d: no suitable interfaces\n", __func__,
2055141cc406Sopenharmony_ci	       vid, pid, busno, address);
2056141cc406Sopenharmony_ci	  continue;
2057141cc406Sopenharmony_ci	}
2058141cc406Sopenharmony_ci
2059141cc406Sopenharmony_ci      memset (&device, 0, sizeof (device));
2060141cc406Sopenharmony_ci      device.lu_device = libusb_ref_device(dev);
2061141cc406Sopenharmony_ci      snprintf (devname, sizeof (devname), "libusb:%03d:%03d",
2062141cc406Sopenharmony_ci		busno, address);
2063141cc406Sopenharmony_ci      device.devname = strdup (devname);
2064141cc406Sopenharmony_ci      if (!device.devname)
2065141cc406Sopenharmony_ci	return;
2066141cc406Sopenharmony_ci      device.vendor = vid;
2067141cc406Sopenharmony_ci      device.product = pid;
2068141cc406Sopenharmony_ci      device.method = sanei_usb_method_libusb;
2069141cc406Sopenharmony_ci      device.interface_nr = interface;
2070141cc406Sopenharmony_ci      device.alt_setting = 0;
2071141cc406Sopenharmony_ci      DBG (4,
2072141cc406Sopenharmony_ci	   "%s: found libusb-1.0 device (0x%04x/0x%04x) interface "
2073141cc406Sopenharmony_ci	   "%d at %s\n", __func__,
2074141cc406Sopenharmony_ci	   vid, pid, interface, devname);
2075141cc406Sopenharmony_ci
2076141cc406Sopenharmony_ci      store_device (device);
2077141cc406Sopenharmony_ci    }
2078141cc406Sopenharmony_ci
2079141cc406Sopenharmony_ci  libusb_free_device_list (devlist, 1);
2080141cc406Sopenharmony_ci
2081141cc406Sopenharmony_ci}
2082141cc406Sopenharmony_ci#endif /* HAVE_LIBUSB */
2083141cc406Sopenharmony_ci
2084141cc406Sopenharmony_ci#ifdef HAVE_USB_MANAGER
2085141cc406Sopenharmony_ci/** scan for devices using usb_manager
2086141cc406Sopenharmony_ci * Check for devices using usb_manager
2087141cc406Sopenharmony_ci */
2088141cc406Sopenharmony_cistatic void libusb_scan_devices(void)
2089141cc406Sopenharmony_ci{
2090141cc406Sopenharmony_ci  device_list_type device;
2091141cc406Sopenharmony_ci  SANE_Char devname[1024];
2092141cc406Sopenharmony_ci  usb_manager_device **devlist;
2093141cc406Sopenharmony_ci  ssize_t ndev;
2094141cc406Sopenharmony_ci  usb_manager_device *dev;
2095141cc406Sopenharmony_ci  usb_manager_device_handle *hdl;
2096141cc406Sopenharmony_ci  struct usb_manager_device_descriptor desc;
2097141cc406Sopenharmony_ci  struct usb_manager_config_descriptor *config0;
2098141cc406Sopenharmony_ci  unsigned short vid, pid;
2099141cc406Sopenharmony_ci  unsigned char busno, address;
2100141cc406Sopenharmony_ci  int config;
2101141cc406Sopenharmony_ci  int interface;
2102141cc406Sopenharmony_ci  int ret;
2103141cc406Sopenharmony_ci  int i;
2104141cc406Sopenharmony_ci
2105141cc406Sopenharmony_ci  DBG (4, "%s: Looking for usb_manager devices\n", __func__);
2106141cc406Sopenharmony_ci
2107141cc406Sopenharmony_ci  ndev = usb_manager_get_device_list (sanei_usb_ctx, &devlist);
2108141cc406Sopenharmony_ci  if (ndev < 0)
2109141cc406Sopenharmony_ci    {
2110141cc406Sopenharmony_ci      DBG (1,
2111141cc406Sopenharmony_ci	   "%s: failed to get usb_manager device list, error %d\n", __func__,
2112141cc406Sopenharmony_ci	   (int) ndev);
2113141cc406Sopenharmony_ci      return;
2114141cc406Sopenharmony_ci    }
2115141cc406Sopenharmony_ci
2116141cc406Sopenharmony_ci  for (i = 0; i < ndev; i++)
2117141cc406Sopenharmony_ci    {
2118141cc406Sopenharmony_ci      SANE_Bool found = SANE_FALSE;
2119141cc406Sopenharmony_ci
2120141cc406Sopenharmony_ci      dev = devlist[i];
2121141cc406Sopenharmony_ci
2122141cc406Sopenharmony_ci      busno = usb_manager_get_bus_number (dev);
2123141cc406Sopenharmony_ci      address = usb_manager_get_device_address (dev);
2124141cc406Sopenharmony_ci
2125141cc406Sopenharmony_ci      ret = usb_manager_get_device_descriptor (dev, &desc);
2126141cc406Sopenharmony_ci      if (ret < 0)
2127141cc406Sopenharmony_ci	{
2128141cc406Sopenharmony_ci	  DBG (1,
2129141cc406Sopenharmony_ci	       "%s: could not get device descriptor for device at %03d:%03d (err %d)\n", __func__,
2130141cc406Sopenharmony_ci	       busno, address, ret);
2131141cc406Sopenharmony_ci	  continue;
2132141cc406Sopenharmony_ci	}
2133141cc406Sopenharmony_ci
2134141cc406Sopenharmony_ci      vid = desc.idVendor;
2135141cc406Sopenharmony_ci      pid = desc.idProduct;
2136141cc406Sopenharmony_ci
2137141cc406Sopenharmony_ci      if ((vid == 0) || (pid == 0))
2138141cc406Sopenharmony_ci	{
2139141cc406Sopenharmony_ci	  DBG (5,
2140141cc406Sopenharmony_ci	       "%s: device 0x%04x/0x%04x at %03d:%03d looks like a root hub\n", __func__,
2141141cc406Sopenharmony_ci	       vid, pid, busno, address);
2142141cc406Sopenharmony_ci	  continue;
2143141cc406Sopenharmony_ci	}
2144141cc406Sopenharmony_ci
2145141cc406Sopenharmony_ci      ret = usb_manager_open (dev, &hdl);
2146141cc406Sopenharmony_ci      if (ret < 0)
2147141cc406Sopenharmony_ci	{
2148141cc406Sopenharmony_ci	  DBG (1,
2149141cc406Sopenharmony_ci	       "%s: skipping device 0x%04x/0x%04x at %03d:%03d: cannot open: %s\n", __func__,
2150141cc406Sopenharmony_ci	       vid, pid, busno, address, sanei_usb_manager_strerror (ret));
2151141cc406Sopenharmony_ci
2152141cc406Sopenharmony_ci	  continue;
2153141cc406Sopenharmony_ci	}
2154141cc406Sopenharmony_ci
2155141cc406Sopenharmony_ci      ret = usb_manager_get_configuration (hdl, &config);
2156141cc406Sopenharmony_ci
2157141cc406Sopenharmony_ci      usb_manager_close (hdl);
2158141cc406Sopenharmony_ci
2159141cc406Sopenharmony_ci      if (ret < 0)
2160141cc406Sopenharmony_ci	{
2161141cc406Sopenharmony_ci	  DBG (1,
2162141cc406Sopenharmony_ci	       "%s: could not get configuration for device 0x%04x/0x%04x at %03d:%03d (err %d)\n", __func__,
2163141cc406Sopenharmony_ci	       vid, pid, busno, address, ret);
2164141cc406Sopenharmony_ci	  continue;
2165141cc406Sopenharmony_ci	}
2166141cc406Sopenharmony_ci
2167141cc406Sopenharmony_ci#if !defined(SANEI_ALLOW_UNCONFIGURED_DEVICES)
2168141cc406Sopenharmony_ci      if (config == 0)
2169141cc406Sopenharmony_ci	{
2170141cc406Sopenharmony_ci	  DBG (1,
2171141cc406Sopenharmony_ci	       "%s: device 0x%04x/0x%04x at %03d:%03d is not configured\n", __func__,
2172141cc406Sopenharmony_ci	       vid, pid, busno, address);
2173141cc406Sopenharmony_ci	  continue;
2174141cc406Sopenharmony_ci	}
2175141cc406Sopenharmony_ci#endif
2176141cc406Sopenharmony_ci
2177141cc406Sopenharmony_ci      ret = usb_manager_get_config_descriptor (dev, 0, &config0);
2178141cc406Sopenharmony_ci      if (ret < 0)
2179141cc406Sopenharmony_ci	{
2180141cc406Sopenharmony_ci	  DBG (1,
2181141cc406Sopenharmony_ci	       "%s: could not get config[0] descriptor for device 0x%04x/0x%04x at %03d:%03d (err %d)\n", __func__,
2182141cc406Sopenharmony_ci	       vid, pid, busno, address, ret);
2183141cc406Sopenharmony_ci	  continue;
2184141cc406Sopenharmony_ci	}
2185141cc406Sopenharmony_ci
2186141cc406Sopenharmony_ci      for (interface = 0; (interface < config0->bNumInterfaces) && !found; interface++)
2187141cc406Sopenharmony_ci	{
2188141cc406Sopenharmony_ci	  switch (desc.bDeviceClass)
2189141cc406Sopenharmony_ci	    {
2190141cc406Sopenharmony_ci	      case USB_MANAGER_CLASS_VENDOR_SPEC:
2191141cc406Sopenharmony_ci		found = SANE_TRUE;
2192141cc406Sopenharmony_ci		break;
2193141cc406Sopenharmony_ci
2194141cc406Sopenharmony_ci	      case USB_MANAGER_CLASS_PER_INTERFACE:
2195141cc406Sopenharmony_ci		if ((config0->interface[interface].num_altsetting == 0)
2196141cc406Sopenharmony_ci		    || !config0->interface[interface].altsetting)
2197141cc406Sopenharmony_ci		  {
2198141cc406Sopenharmony_ci		    DBG (1, "%s: device 0x%04x/0x%04x doesn't "
2199141cc406Sopenharmony_ci			 "have an altsetting for interface %d\n", __func__,
2200141cc406Sopenharmony_ci			 vid, pid, interface);
2201141cc406Sopenharmony_ci		    continue;
2202141cc406Sopenharmony_ci		  }
2203141cc406Sopenharmony_ci
2204141cc406Sopenharmony_ci		switch (config0->interface[interface].altsetting[0].bInterfaceClass)
2205141cc406Sopenharmony_ci		  {
2206141cc406Sopenharmony_ci		    case USB_MANAGER_CLASS_VENDOR_SPEC:
2207141cc406Sopenharmony_ci		    case USB_MANAGER_CLASS_PER_INTERFACE:
2208141cc406Sopenharmony_ci		    case USB_MANAGER_CLASS_PTP:
2209141cc406Sopenharmony_ci		    case 16:	/* data? */
2210141cc406Sopenharmony_ci		      found = SANE_TRUE;
2211141cc406Sopenharmony_ci		      break;
2212141cc406Sopenharmony_ci		  }
2213141cc406Sopenharmony_ci		break;
2214141cc406Sopenharmony_ci	    }
2215141cc406Sopenharmony_ci
2216141cc406Sopenharmony_ci	  if (!found)
2217141cc406Sopenharmony_ci	    DBG (5,
2218141cc406Sopenharmony_ci		 "%s: device 0x%04x/0x%04x, interface %d "
2219141cc406Sopenharmony_ci		 "doesn't look like a scanner (%d/%d)\n", __func__,
2220141cc406Sopenharmony_ci		 vid, pid, interface, desc.bDeviceClass,
2221141cc406Sopenharmony_ci		 (config0->interface[interface].num_altsetting != 0)
2222141cc406Sopenharmony_ci		 ? config0->interface[interface].altsetting[0].bInterfaceClass : -1);
2223141cc406Sopenharmony_ci	}
2224141cc406Sopenharmony_ci
2225141cc406Sopenharmony_ci      usb_manager_free_config_descriptor (config0);
2226141cc406Sopenharmony_ci
2227141cc406Sopenharmony_ci      interface--;
2228141cc406Sopenharmony_ci
2229141cc406Sopenharmony_ci      if (!found)
2230141cc406Sopenharmony_ci	{
2231141cc406Sopenharmony_ci	  DBG (5,
2232141cc406Sopenharmony_ci	       "%s: device 0x%04x/0x%04x at %03d:%03d: no suitable interfaces\n", __func__,
2233141cc406Sopenharmony_ci	       vid, pid, busno, address);
2234141cc406Sopenharmony_ci	  continue;
2235141cc406Sopenharmony_ci	}
2236141cc406Sopenharmony_ci
2237141cc406Sopenharmony_ci      memset (&device, 0, sizeof (device));
2238141cc406Sopenharmony_ci      device.usb_manager_device = usb_manager_ref_device(dev);
2239141cc406Sopenharmony_ci      snprintf (devname, sizeof (devname), "libusb:%03d:%03d",
2240141cc406Sopenharmony_ci		busno, address);
2241141cc406Sopenharmony_ci      device.devname = strdup (devname);
2242141cc406Sopenharmony_ci      if (!device.devname)
2243141cc406Sopenharmony_ci	return;
2244141cc406Sopenharmony_ci      device.vendor = vid;
2245141cc406Sopenharmony_ci      device.product = pid;
2246141cc406Sopenharmony_ci      device.method = sanei_usb_method_libusb;
2247141cc406Sopenharmony_ci      device.interface_nr = interface;
2248141cc406Sopenharmony_ci      device.alt_setting = 0;
2249141cc406Sopenharmony_ci      DBG (4,
2250141cc406Sopenharmony_ci	   "%s: found usb_manager device (0x%04x/0x%04x) interface "
2251141cc406Sopenharmony_ci	   "%d at %s\n", __func__,
2252141cc406Sopenharmony_ci	   vid, pid, interface, devname);
2253141cc406Sopenharmony_ci
2254141cc406Sopenharmony_ci      store_device (device);
2255141cc406Sopenharmony_ci    }
2256141cc406Sopenharmony_ci
2257141cc406Sopenharmony_ci  usb_manager_free_device_list (devlist, 1);
2258141cc406Sopenharmony_ci
2259141cc406Sopenharmony_ci}
2260141cc406Sopenharmony_ci#endif /* HAVE_USB_MANAGER */
2261141cc406Sopenharmony_ci
2262141cc406Sopenharmony_civoid
2263141cc406Sopenharmony_cisanei_usb_scan_devices (void)
2264141cc406Sopenharmony_ci{
2265141cc406Sopenharmony_ci  int count;
2266141cc406Sopenharmony_ci  int i;
2267141cc406Sopenharmony_ci
2268141cc406Sopenharmony_ci  /* check USB has been initialized first */
2269141cc406Sopenharmony_ci  if(initialized==0)
2270141cc406Sopenharmony_ci    {
2271141cc406Sopenharmony_ci      DBG (1, "%s: sanei_usb is not initialized!\n", __func__);
2272141cc406Sopenharmony_ci      return;
2273141cc406Sopenharmony_ci    }
2274141cc406Sopenharmony_ci
2275141cc406Sopenharmony_ci  if (testing_mode == sanei_usb_testing_mode_replay)
2276141cc406Sopenharmony_ci    {
2277141cc406Sopenharmony_ci      // device added in sanei_usb_testing_init()
2278141cc406Sopenharmony_ci      return;
2279141cc406Sopenharmony_ci    }
2280141cc406Sopenharmony_ci  /* we mark all already detected devices as missing */
2281141cc406Sopenharmony_ci  /* each scan method will reset this value to 0 (not missing)
2282141cc406Sopenharmony_ci   * when storing the device */
2283141cc406Sopenharmony_ci  DBG (4, "%s: marking existing devices\n", __func__);
2284141cc406Sopenharmony_ci  for (i = 0; i < device_number; i++)
2285141cc406Sopenharmony_ci    {
2286141cc406Sopenharmony_ci      devices[i].missing++;
2287141cc406Sopenharmony_ci    }
2288141cc406Sopenharmony_ci
2289141cc406Sopenharmony_ci  /* Check for devices using the kernel scanner driver */
2290141cc406Sopenharmony_ci#if !defined(HAVE_LIBUSB_LEGACY) && !defined(HAVE_LIBUSB) && !defined(HAVE_USB_MANAGER)
2291141cc406Sopenharmony_ci  kernel_scan_devices();
2292141cc406Sopenharmony_ci#endif
2293141cc406Sopenharmony_ci
2294141cc406Sopenharmony_ci#if defined(HAVE_LIBUSB_LEGACY) || defined(HAVE_LIBUSB) || defined(HAVE_USB_MANAGER)
2295141cc406Sopenharmony_ci  /* Check for devices using libusb (old or new)*/
2296141cc406Sopenharmony_ci  libusb_scan_devices();
2297141cc406Sopenharmony_ci#endif
2298141cc406Sopenharmony_ci
2299141cc406Sopenharmony_ci#ifdef HAVE_USBCALLS
2300141cc406Sopenharmony_ci  /* Check for devices using OS/2 USBCALLS Interface */
2301141cc406Sopenharmony_ci  usbcall_scan_devices();
2302141cc406Sopenharmony_ci#endif
2303141cc406Sopenharmony_ci
2304141cc406Sopenharmony_ci  /* display found devices */
2305141cc406Sopenharmony_ci  if (debug_level > 5)
2306141cc406Sopenharmony_ci    {
2307141cc406Sopenharmony_ci      count=0;
2308141cc406Sopenharmony_ci      for (i = 0; i < device_number; i++)
2309141cc406Sopenharmony_ci        {
2310141cc406Sopenharmony_ci          if(!devices[i].missing)
2311141cc406Sopenharmony_ci            {
2312141cc406Sopenharmony_ci              count++;
2313141cc406Sopenharmony_ci	      DBG (6, "%s: device %02d is %s\n", __func__, i, devices[i].devname);
2314141cc406Sopenharmony_ci            }
2315141cc406Sopenharmony_ci        }
2316141cc406Sopenharmony_ci      DBG (5, "%s: found %d devices\n", __func__, count);
2317141cc406Sopenharmony_ci    }
2318141cc406Sopenharmony_ci}
2319141cc406Sopenharmony_ci
2320141cc406Sopenharmony_ci
2321141cc406Sopenharmony_ci
2322141cc406Sopenharmony_ci/* This logically belongs to sanei_config.c but not every backend that
2323141cc406Sopenharmony_ci   uses sanei_config() wants to depend on sanei_usb.  */
2324141cc406Sopenharmony_civoid
2325141cc406Sopenharmony_cisanei_usb_attach_matching_devices (const char *name,
2326141cc406Sopenharmony_ci				   SANE_Status (*attach) (const char *dev))
2327141cc406Sopenharmony_ci{
2328141cc406Sopenharmony_ci  char *vendor, *product;
2329141cc406Sopenharmony_ci
2330141cc406Sopenharmony_ci  if (strncmp (name, "usb", 3) == 0)
2331141cc406Sopenharmony_ci    {
2332141cc406Sopenharmony_ci      SANE_Word vendorID = 0, productID = 0;
2333141cc406Sopenharmony_ci
2334141cc406Sopenharmony_ci      name += 3;
2335141cc406Sopenharmony_ci
2336141cc406Sopenharmony_ci      name = sanei_config_skip_whitespace (name);
2337141cc406Sopenharmony_ci      if (*name)
2338141cc406Sopenharmony_ci	{
2339141cc406Sopenharmony_ci	  name = sanei_config_get_string (name, &vendor);
2340141cc406Sopenharmony_ci	  if (vendor)
2341141cc406Sopenharmony_ci	    {
2342141cc406Sopenharmony_ci	      vendorID = strtol (vendor, 0, 0);
2343141cc406Sopenharmony_ci	      free (vendor);
2344141cc406Sopenharmony_ci	    }
2345141cc406Sopenharmony_ci	  name = sanei_config_skip_whitespace (name);
2346141cc406Sopenharmony_ci	}
2347141cc406Sopenharmony_ci
2348141cc406Sopenharmony_ci      name = sanei_config_skip_whitespace (name);
2349141cc406Sopenharmony_ci      if (*name)
2350141cc406Sopenharmony_ci	{
2351141cc406Sopenharmony_ci	  name = sanei_config_get_string (name, &product);
2352141cc406Sopenharmony_ci	  if (product)
2353141cc406Sopenharmony_ci	    {
2354141cc406Sopenharmony_ci	      productID = strtol (product, 0, 0);
2355141cc406Sopenharmony_ci	      free (product);
2356141cc406Sopenharmony_ci	    }
2357141cc406Sopenharmony_ci	}
2358141cc406Sopenharmony_ci      sanei_usb_find_devices (vendorID, productID, attach);
2359141cc406Sopenharmony_ci    }
2360141cc406Sopenharmony_ci  else
2361141cc406Sopenharmony_ci    (*attach) (name);
2362141cc406Sopenharmony_ci}
2363141cc406Sopenharmony_ci
2364141cc406Sopenharmony_ciSANE_Status
2365141cc406Sopenharmony_cisanei_usb_get_vendor_product_byname (SANE_String_Const devname,
2366141cc406Sopenharmony_ci				     SANE_Word * vendor, SANE_Word * product)
2367141cc406Sopenharmony_ci{
2368141cc406Sopenharmony_ci  int i;
2369141cc406Sopenharmony_ci  SANE_Bool found = SANE_FALSE;
2370141cc406Sopenharmony_ci
2371141cc406Sopenharmony_ci  for (i = 0; i < device_number && devices[i].devname; i++)
2372141cc406Sopenharmony_ci    {
2373141cc406Sopenharmony_ci      if (!devices[i].missing && strcmp (devices[i].devname, devname) == 0)
2374141cc406Sopenharmony_ci	{
2375141cc406Sopenharmony_ci	  found = SANE_TRUE;
2376141cc406Sopenharmony_ci	  break;
2377141cc406Sopenharmony_ci	}
2378141cc406Sopenharmony_ci    }
2379141cc406Sopenharmony_ci
2380141cc406Sopenharmony_ci  if (!found)
2381141cc406Sopenharmony_ci    {
2382141cc406Sopenharmony_ci      DBG (1, "sanei_usb_get_vendor_product_byname: can't find device `%s' in list\n", devname);
2383141cc406Sopenharmony_ci      return SANE_STATUS_INVAL;
2384141cc406Sopenharmony_ci    }
2385141cc406Sopenharmony_ci
2386141cc406Sopenharmony_ci  if ((devices[i].vendor == 0) && (devices[i].product == 0))
2387141cc406Sopenharmony_ci    {
2388141cc406Sopenharmony_ci      DBG (1, "sanei_usb_get_vendor_product_byname: not support for this method\n");
2389141cc406Sopenharmony_ci      return SANE_STATUS_UNSUPPORTED;
2390141cc406Sopenharmony_ci    }
2391141cc406Sopenharmony_ci
2392141cc406Sopenharmony_ci  if (vendor)
2393141cc406Sopenharmony_ci    *vendor = devices[i].vendor;
2394141cc406Sopenharmony_ci
2395141cc406Sopenharmony_ci  if (product)
2396141cc406Sopenharmony_ci    *product = devices[i].product;
2397141cc406Sopenharmony_ci
2398141cc406Sopenharmony_ci  return SANE_STATUS_GOOD;
2399141cc406Sopenharmony_ci}
2400141cc406Sopenharmony_ci
2401141cc406Sopenharmony_ciSANE_Status
2402141cc406Sopenharmony_cisanei_usb_get_vendor_product (SANE_Int dn, SANE_Word * vendor,
2403141cc406Sopenharmony_ci			      SANE_Word * product)
2404141cc406Sopenharmony_ci{
2405141cc406Sopenharmony_ci  SANE_Word vendorID = 0;
2406141cc406Sopenharmony_ci  SANE_Word productID = 0;
2407141cc406Sopenharmony_ci
2408141cc406Sopenharmony_ci  if (dn >= device_number || dn < 0)
2409141cc406Sopenharmony_ci    {
2410141cc406Sopenharmony_ci      DBG (1, "sanei_usb_get_vendor_product: dn >= device number || dn < 0\n");
2411141cc406Sopenharmony_ci      return SANE_STATUS_INVAL;
2412141cc406Sopenharmony_ci    }
2413141cc406Sopenharmony_ci  if (devices[dn].missing >= 1)
2414141cc406Sopenharmony_ci    {
2415141cc406Sopenharmony_ci      DBG (1, "sanei_usb_get_vendor_product: dn=%d is missing!\n",dn);
2416141cc406Sopenharmony_ci      return SANE_STATUS_INVAL;
2417141cc406Sopenharmony_ci    }
2418141cc406Sopenharmony_ci
2419141cc406Sopenharmony_ci  /* kernel, usbcal and libusb methods store these when device scanning
2420141cc406Sopenharmony_ci   * is done, so we can use them directly */
2421141cc406Sopenharmony_ci  vendorID = devices[dn].vendor;
2422141cc406Sopenharmony_ci  productID = devices[dn].product;
2423141cc406Sopenharmony_ci
2424141cc406Sopenharmony_ci  if (vendor)
2425141cc406Sopenharmony_ci    *vendor = vendorID;
2426141cc406Sopenharmony_ci  if (product)
2427141cc406Sopenharmony_ci    *product = productID;
2428141cc406Sopenharmony_ci
2429141cc406Sopenharmony_ci  if (!vendorID || !productID)
2430141cc406Sopenharmony_ci    {
2431141cc406Sopenharmony_ci      DBG (3, "sanei_usb_get_vendor_product: device %d: Your OS doesn't "
2432141cc406Sopenharmony_ci	   "seem to support detection of vendor+product ids\n", dn);
2433141cc406Sopenharmony_ci      return SANE_STATUS_UNSUPPORTED;
2434141cc406Sopenharmony_ci    }
2435141cc406Sopenharmony_ci  else
2436141cc406Sopenharmony_ci    {
2437141cc406Sopenharmony_ci      DBG (3, "sanei_usb_get_vendor_product: device %d: vendorID: 0x%04x, "
2438141cc406Sopenharmony_ci	   "productID: 0x%04x\n", dn, vendorID, productID);
2439141cc406Sopenharmony_ci      return SANE_STATUS_GOOD;
2440141cc406Sopenharmony_ci    }
2441141cc406Sopenharmony_ci}
2442141cc406Sopenharmony_ci
2443141cc406Sopenharmony_ciSANE_Status
2444141cc406Sopenharmony_cisanei_usb_find_devices (SANE_Int vendor, SANE_Int product,
2445141cc406Sopenharmony_ci			SANE_Status (*attach) (SANE_String_Const dev))
2446141cc406Sopenharmony_ci{
2447141cc406Sopenharmony_ci  SANE_Int dn = 0;
2448141cc406Sopenharmony_ci
2449141cc406Sopenharmony_ci  DBG (3,
2450141cc406Sopenharmony_ci       "sanei_usb_find_devices: vendor=0x%04x, product=0x%04x\n",
2451141cc406Sopenharmony_ci       vendor, product);
2452141cc406Sopenharmony_ci
2453141cc406Sopenharmony_ci  while (devices[dn].devname && dn < device_number)
2454141cc406Sopenharmony_ci    {
2455141cc406Sopenharmony_ci      if (devices[dn].vendor == vendor
2456141cc406Sopenharmony_ci        && devices[dn].product == product
2457141cc406Sopenharmony_ci        && !devices[dn].missing
2458141cc406Sopenharmony_ci	&& attach)
2459141cc406Sopenharmony_ci	  attach (devices[dn].devname);
2460141cc406Sopenharmony_ci      dn++;
2461141cc406Sopenharmony_ci    }
2462141cc406Sopenharmony_ci  return SANE_STATUS_GOOD;
2463141cc406Sopenharmony_ci}
2464141cc406Sopenharmony_ci
2465141cc406Sopenharmony_civoid
2466141cc406Sopenharmony_cisanei_usb_set_endpoint (SANE_Int dn, SANE_Int ep_type, SANE_Int ep)
2467141cc406Sopenharmony_ci{
2468141cc406Sopenharmony_ci  if (dn >= device_number || dn < 0)
2469141cc406Sopenharmony_ci    {
2470141cc406Sopenharmony_ci      DBG (1, "sanei_usb_set_endpoint: dn >= device number || dn < 0\n");
2471141cc406Sopenharmony_ci      return;
2472141cc406Sopenharmony_ci    }
2473141cc406Sopenharmony_ci
2474141cc406Sopenharmony_ci  DBG (5, "sanei_usb_set_endpoint: Setting endpoint of type 0x%02x to 0x%02x\n", ep_type, ep);
2475141cc406Sopenharmony_ci  switch (ep_type)
2476141cc406Sopenharmony_ci    {
2477141cc406Sopenharmony_ci      case USB_DIR_IN|USB_ENDPOINT_TYPE_BULK:
2478141cc406Sopenharmony_ci	    devices[dn].bulk_in_ep  = ep;
2479141cc406Sopenharmony_ci	    break;
2480141cc406Sopenharmony_ci      case USB_DIR_OUT|USB_ENDPOINT_TYPE_BULK:
2481141cc406Sopenharmony_ci	    devices[dn].bulk_out_ep = ep;
2482141cc406Sopenharmony_ci	    break;
2483141cc406Sopenharmony_ci      case USB_DIR_IN|USB_ENDPOINT_TYPE_ISOCHRONOUS:
2484141cc406Sopenharmony_ci	    devices[dn].iso_in_ep = ep;
2485141cc406Sopenharmony_ci	    break;
2486141cc406Sopenharmony_ci      case USB_DIR_OUT|USB_ENDPOINT_TYPE_ISOCHRONOUS:
2487141cc406Sopenharmony_ci	    devices[dn].iso_out_ep = ep;
2488141cc406Sopenharmony_ci	    break;
2489141cc406Sopenharmony_ci      case USB_DIR_IN|USB_ENDPOINT_TYPE_INTERRUPT:
2490141cc406Sopenharmony_ci	    devices[dn].int_in_ep = ep;
2491141cc406Sopenharmony_ci	    break;
2492141cc406Sopenharmony_ci      case USB_DIR_OUT|USB_ENDPOINT_TYPE_INTERRUPT:
2493141cc406Sopenharmony_ci	    devices[dn].int_out_ep = ep;
2494141cc406Sopenharmony_ci	    break;
2495141cc406Sopenharmony_ci      case USB_DIR_IN|USB_ENDPOINT_TYPE_CONTROL:
2496141cc406Sopenharmony_ci	    devices[dn].control_in_ep = ep;
2497141cc406Sopenharmony_ci	    break;
2498141cc406Sopenharmony_ci      case USB_DIR_OUT|USB_ENDPOINT_TYPE_CONTROL:
2499141cc406Sopenharmony_ci	    devices[dn].control_out_ep = ep;
2500141cc406Sopenharmony_ci	    break;
2501141cc406Sopenharmony_ci    }
2502141cc406Sopenharmony_ci}
2503141cc406Sopenharmony_ci
2504141cc406Sopenharmony_ci#if HAVE_LIBUSB_LEGACY || HAVE_LIBUSB || HAVE_USBCALLS || WITH_USB_RECORD_REPLAY || HAVE_USB_MANAGER
2505141cc406Sopenharmony_cistatic const char* sanei_usb_transfer_type_desc(SANE_Int transfer_type)
2506141cc406Sopenharmony_ci{
2507141cc406Sopenharmony_ci  switch (transfer_type)
2508141cc406Sopenharmony_ci    {
2509141cc406Sopenharmony_ci      case USB_ENDPOINT_TYPE_INTERRUPT: return "interrupt";
2510141cc406Sopenharmony_ci      case USB_ENDPOINT_TYPE_BULK: return "bulk";
2511141cc406Sopenharmony_ci      case USB_ENDPOINT_TYPE_ISOCHRONOUS: return "isochronous";
2512141cc406Sopenharmony_ci      case USB_ENDPOINT_TYPE_CONTROL: return "control";
2513141cc406Sopenharmony_ci    }
2514141cc406Sopenharmony_ci  return NULL;
2515141cc406Sopenharmony_ci}
2516141cc406Sopenharmony_ci
2517141cc406Sopenharmony_ci// Similar sanei_usb_set_endpoint, but ignores duplicate endpoints
2518141cc406Sopenharmony_cistatic void sanei_usb_add_endpoint(device_list_type* device,
2519141cc406Sopenharmony_ci                                   SANE_Int transfer_type,
2520141cc406Sopenharmony_ci                                   SANE_Int ep_address,
2521141cc406Sopenharmony_ci                                   SANE_Int ep_direction)
2522141cc406Sopenharmony_ci{
2523141cc406Sopenharmony_ci  DBG(5, "%s: direction: %d, address: %d, transfer_type: %d\n",
2524141cc406Sopenharmony_ci      __func__, ep_direction, ep_address, transfer_type);
2525141cc406Sopenharmony_ci
2526141cc406Sopenharmony_ci  SANE_Int* ep_in = NULL;
2527141cc406Sopenharmony_ci  SANE_Int* ep_out = NULL;
2528141cc406Sopenharmony_ci  const char* transfer_type_msg = sanei_usb_transfer_type_desc(transfer_type);
2529141cc406Sopenharmony_ci
2530141cc406Sopenharmony_ci  switch (transfer_type)
2531141cc406Sopenharmony_ci    {
2532141cc406Sopenharmony_ci      case USB_ENDPOINT_TYPE_INTERRUPT:
2533141cc406Sopenharmony_ci        ep_in = &device->int_in_ep;
2534141cc406Sopenharmony_ci        ep_out = &device->int_out_ep;
2535141cc406Sopenharmony_ci        break;
2536141cc406Sopenharmony_ci      case USB_ENDPOINT_TYPE_BULK:
2537141cc406Sopenharmony_ci        ep_in = &device->bulk_in_ep;
2538141cc406Sopenharmony_ci        ep_out = &device->bulk_out_ep;
2539141cc406Sopenharmony_ci        break;
2540141cc406Sopenharmony_ci      case USB_ENDPOINT_TYPE_ISOCHRONOUS:
2541141cc406Sopenharmony_ci        ep_in = &device->iso_in_ep;
2542141cc406Sopenharmony_ci        ep_out = &device->iso_out_ep;
2543141cc406Sopenharmony_ci        break;
2544141cc406Sopenharmony_ci      case USB_ENDPOINT_TYPE_CONTROL:
2545141cc406Sopenharmony_ci        ep_in = &device->control_in_ep;
2546141cc406Sopenharmony_ci        ep_out = &device->control_out_ep;
2547141cc406Sopenharmony_ci        break;
2548141cc406Sopenharmony_ci    }
2549141cc406Sopenharmony_ci
2550141cc406Sopenharmony_ci  DBG(5, "%s: found %s-%s endpoint (address 0x%02x)\n",
2551141cc406Sopenharmony_ci      __func__, transfer_type_msg, ep_direction ? "in" : "out",
2552141cc406Sopenharmony_ci      ep_address);
2553141cc406Sopenharmony_ci
2554141cc406Sopenharmony_ci  if (ep_direction) // in
2555141cc406Sopenharmony_ci    {
2556141cc406Sopenharmony_ci      if (*ep_in)
2557141cc406Sopenharmony_ci        DBG(3, "%s: we already have a %s-in endpoint "
2558141cc406Sopenharmony_ci             "(address: 0x%02x), ignoring the new one\n",
2559141cc406Sopenharmony_ci            __func__, transfer_type_msg, *ep_in);
2560141cc406Sopenharmony_ci      else
2561141cc406Sopenharmony_ci        *ep_in = ep_address;
2562141cc406Sopenharmony_ci    }
2563141cc406Sopenharmony_ci  else
2564141cc406Sopenharmony_ci    {
2565141cc406Sopenharmony_ci      if (*ep_out)
2566141cc406Sopenharmony_ci        DBG(3, "%s: we already have a %s-out endpoint "
2567141cc406Sopenharmony_ci             "(address: 0x%02x), ignoring the new one\n",
2568141cc406Sopenharmony_ci            __func__, transfer_type_msg, *ep_out);
2569141cc406Sopenharmony_ci      else
2570141cc406Sopenharmony_ci        *ep_out = ep_address;
2571141cc406Sopenharmony_ci    }
2572141cc406Sopenharmony_ci}
2573141cc406Sopenharmony_ci#endif // HAVE_LIBUSB_LEGACY || HAVE_LIBUSB || HAVE_USBCALLS || HAVE_USB_MANAGER
2574141cc406Sopenharmony_ci
2575141cc406Sopenharmony_ciSANE_Int
2576141cc406Sopenharmony_cisanei_usb_get_endpoint (SANE_Int dn, SANE_Int ep_type)
2577141cc406Sopenharmony_ci{
2578141cc406Sopenharmony_ci  if (dn >= device_number || dn < 0)
2579141cc406Sopenharmony_ci    {
2580141cc406Sopenharmony_ci      DBG (1, "sanei_usb_get_endpoint: dn >= device number || dn < 0\n");
2581141cc406Sopenharmony_ci      return 0;
2582141cc406Sopenharmony_ci    }
2583141cc406Sopenharmony_ci
2584141cc406Sopenharmony_ci  switch (ep_type)
2585141cc406Sopenharmony_ci    {
2586141cc406Sopenharmony_ci      case USB_DIR_IN|USB_ENDPOINT_TYPE_BULK:
2587141cc406Sopenharmony_ci	    return devices[dn].bulk_in_ep;
2588141cc406Sopenharmony_ci      case USB_DIR_OUT|USB_ENDPOINT_TYPE_BULK:
2589141cc406Sopenharmony_ci	    return devices[dn].bulk_out_ep;
2590141cc406Sopenharmony_ci      case USB_DIR_IN|USB_ENDPOINT_TYPE_ISOCHRONOUS:
2591141cc406Sopenharmony_ci	    return devices[dn].iso_in_ep;
2592141cc406Sopenharmony_ci      case USB_DIR_OUT|USB_ENDPOINT_TYPE_ISOCHRONOUS:
2593141cc406Sopenharmony_ci	    return devices[dn].iso_out_ep;
2594141cc406Sopenharmony_ci      case USB_DIR_IN|USB_ENDPOINT_TYPE_INTERRUPT:
2595141cc406Sopenharmony_ci	    return devices[dn].int_in_ep;
2596141cc406Sopenharmony_ci      case USB_DIR_OUT|USB_ENDPOINT_TYPE_INTERRUPT:
2597141cc406Sopenharmony_ci	    return devices[dn].int_out_ep;
2598141cc406Sopenharmony_ci      case USB_DIR_IN|USB_ENDPOINT_TYPE_CONTROL:
2599141cc406Sopenharmony_ci	    return devices[dn].control_in_ep;
2600141cc406Sopenharmony_ci      case USB_DIR_OUT|USB_ENDPOINT_TYPE_CONTROL:
2601141cc406Sopenharmony_ci	    return devices[dn].control_out_ep;
2602141cc406Sopenharmony_ci      default:
2603141cc406Sopenharmony_ci	    return 0;
2604141cc406Sopenharmony_ci    }
2605141cc406Sopenharmony_ci}
2606141cc406Sopenharmony_ci
2607141cc406Sopenharmony_ci#if WITH_USB_RECORD_REPLAY
2608141cc406Sopenharmony_cistatic void sanei_xml_indent_child(xmlNode* parent, unsigned indent_count)
2609141cc406Sopenharmony_ci{
2610141cc406Sopenharmony_ci  indent_count *= 4;
2611141cc406Sopenharmony_ci
2612141cc406Sopenharmony_ci  xmlChar* indent_str = malloc(indent_count + 2);
2613141cc406Sopenharmony_ci  indent_str[0] = '\n';
2614141cc406Sopenharmony_ci  memset(indent_str + 1, ' ', indent_count);
2615141cc406Sopenharmony_ci  indent_str[indent_count + 1] = '\0';
2616141cc406Sopenharmony_ci
2617141cc406Sopenharmony_ci  xmlAddChild(parent, xmlNewText(indent_str));
2618141cc406Sopenharmony_ci  free(indent_str);
2619141cc406Sopenharmony_ci}
2620141cc406Sopenharmony_ci
2621141cc406Sopenharmony_cistatic void sanei_usb_record_open(SANE_Int dn)
2622141cc406Sopenharmony_ci{
2623141cc406Sopenharmony_ci  if (testing_already_opened)
2624141cc406Sopenharmony_ci    return;
2625141cc406Sopenharmony_ci
2626141cc406Sopenharmony_ci  xmlNode* e_root = xmlNewNode(NULL, (const xmlChar*) "device_capture");
2627141cc406Sopenharmony_ci  xmlDocSetRootElement(testing_xml_doc, e_root);
2628141cc406Sopenharmony_ci  xmlNewProp(e_root, (const xmlChar*)"backend", (const xmlChar*) testing_record_backend);
2629141cc406Sopenharmony_ci
2630141cc406Sopenharmony_ci  sanei_xml_indent_child(e_root, 1);
2631141cc406Sopenharmony_ci  xmlNode* e_description = xmlNewChild(e_root, NULL, (const xmlChar*) "description", NULL);
2632141cc406Sopenharmony_ci  sanei_xml_set_hex_attr(e_description, "id_vendor", devices[dn].vendor);
2633141cc406Sopenharmony_ci  sanei_xml_set_hex_attr(e_description, "id_product", devices[dn].product);
2634141cc406Sopenharmony_ci
2635141cc406Sopenharmony_ci  sanei_xml_indent_child(e_description, 2);
2636141cc406Sopenharmony_ci  xmlNode* e_configurations = xmlNewChild(e_description, NULL,
2637141cc406Sopenharmony_ci                                          (const xmlChar*) "configurations", NULL);
2638141cc406Sopenharmony_ci
2639141cc406Sopenharmony_ci  sanei_xml_indent_child(e_configurations, 3);
2640141cc406Sopenharmony_ci  xmlNode* e_configuration = xmlNewChild(e_configurations, NULL,
2641141cc406Sopenharmony_ci                                         (const xmlChar*) "configuration", NULL);
2642141cc406Sopenharmony_ci  sanei_xml_set_uint_attr(e_configuration, "number", 1);
2643141cc406Sopenharmony_ci
2644141cc406Sopenharmony_ci  sanei_xml_indent_child(e_configuration, 4);
2645141cc406Sopenharmony_ci  xmlNode* e_interface = xmlNewChild(e_configuration, NULL, (const xmlChar*) "interface", NULL);
2646141cc406Sopenharmony_ci  sanei_xml_set_uint_attr(e_interface, "number", devices[dn].interface_nr);
2647141cc406Sopenharmony_ci
2648141cc406Sopenharmony_ci  struct endpoint_data_desc {
2649141cc406Sopenharmony_ci    const char* transfer_type;
2650141cc406Sopenharmony_ci    const char* direction;
2651141cc406Sopenharmony_ci    SANE_Int ep_address;
2652141cc406Sopenharmony_ci  };
2653141cc406Sopenharmony_ci
2654141cc406Sopenharmony_ci  struct endpoint_data_desc endpoints[8] =
2655141cc406Sopenharmony_ci  {
2656141cc406Sopenharmony_ci    { "BULK", "IN", devices[dn].bulk_in_ep },
2657141cc406Sopenharmony_ci    { "BULK", "OUT", devices[dn].bulk_out_ep },
2658141cc406Sopenharmony_ci    { "ISOCHRONOUS", "IN", devices[dn].iso_in_ep },
2659141cc406Sopenharmony_ci    { "ISOCHRONOUS", "OUT", devices[dn].iso_out_ep },
2660141cc406Sopenharmony_ci    { "INTERRUPT", "IN", devices[dn].int_in_ep },
2661141cc406Sopenharmony_ci    { "INTERRUPT", "OUT", devices[dn].int_out_ep },
2662141cc406Sopenharmony_ci    { "CONTROL", "IN", devices[dn].control_in_ep },
2663141cc406Sopenharmony_ci    { "CONTROL", "OUT", devices[dn].control_out_ep }
2664141cc406Sopenharmony_ci  };
2665141cc406Sopenharmony_ci
2666141cc406Sopenharmony_ci  for (int i = 0; i < 8; ++i)
2667141cc406Sopenharmony_ci    {
2668141cc406Sopenharmony_ci      if (endpoints[i].ep_address)
2669141cc406Sopenharmony_ci        {
2670141cc406Sopenharmony_ci          sanei_xml_indent_child(e_interface, 5);
2671141cc406Sopenharmony_ci          xmlNode* e_endpoint = xmlNewChild(e_interface, NULL, (const xmlChar*)"endpoint", NULL);
2672141cc406Sopenharmony_ci          xmlNewProp(e_endpoint, (const xmlChar*)"transfer_type",
2673141cc406Sopenharmony_ci                     (const xmlChar*) endpoints[i].transfer_type);
2674141cc406Sopenharmony_ci          sanei_xml_set_uint_attr(e_endpoint, "number", endpoints[i].ep_address & 0x0f);
2675141cc406Sopenharmony_ci          xmlNewProp(e_endpoint, (const xmlChar*)"direction",
2676141cc406Sopenharmony_ci                     (const xmlChar*) endpoints[i].direction);
2677141cc406Sopenharmony_ci          sanei_xml_set_hex_attr(e_endpoint, "address", endpoints[i].ep_address);
2678141cc406Sopenharmony_ci        }
2679141cc406Sopenharmony_ci    }
2680141cc406Sopenharmony_ci  sanei_xml_indent_child(e_interface, 4);
2681141cc406Sopenharmony_ci  sanei_xml_indent_child(e_configuration, 3);
2682141cc406Sopenharmony_ci  sanei_xml_indent_child(e_configurations, 2);
2683141cc406Sopenharmony_ci  sanei_xml_indent_child(e_description, 1);
2684141cc406Sopenharmony_ci
2685141cc406Sopenharmony_ci  sanei_xml_indent_child(e_root, 1);
2686141cc406Sopenharmony_ci  xmlNode* e_transactions = xmlNewChild(e_root, NULL, (const xmlChar*)"transactions", NULL);
2687141cc406Sopenharmony_ci
2688141cc406Sopenharmony_ci  // add an empty node so that we have something to append to
2689141cc406Sopenharmony_ci  testing_append_commands_node = xmlAddChild(e_transactions, xmlNewText((const xmlChar*)""));
2690141cc406Sopenharmony_ci  testing_already_opened = 1;
2691141cc406Sopenharmony_ci}
2692141cc406Sopenharmony_ci#endif // WITH_USB_RECORD_REPLAY
2693141cc406Sopenharmony_ci
2694141cc406Sopenharmony_ciSANE_Status
2695141cc406Sopenharmony_cisanei_usb_open (SANE_String_Const devname, SANE_Int * dn)
2696141cc406Sopenharmony_ci{
2697141cc406Sopenharmony_ci  int devcount;
2698141cc406Sopenharmony_ci  SANE_Bool found = SANE_FALSE;
2699141cc406Sopenharmony_ci
2700141cc406Sopenharmony_ci  DBG (5, "sanei_usb_open: trying to open device `%s'\n", devname);
2701141cc406Sopenharmony_ci  if (!dn)
2702141cc406Sopenharmony_ci    {
2703141cc406Sopenharmony_ci      DBG (1, "sanei_usb_open: can't open `%s': dn == NULL\n", devname);
2704141cc406Sopenharmony_ci      return SANE_STATUS_INVAL;
2705141cc406Sopenharmony_ci    }
2706141cc406Sopenharmony_ci
2707141cc406Sopenharmony_ci  for (devcount = 0;
2708141cc406Sopenharmony_ci       devcount < device_number && devices[devcount].devname != 0;
2709141cc406Sopenharmony_ci       devcount++)
2710141cc406Sopenharmony_ci    {
2711141cc406Sopenharmony_ci      if (!devices[devcount].missing && strcmp (devices[devcount].devname, devname) == 0)
2712141cc406Sopenharmony_ci	{
2713141cc406Sopenharmony_ci	  if (devices[devcount].open)
2714141cc406Sopenharmony_ci	    {
2715141cc406Sopenharmony_ci	      DBG (1, "sanei_usb_open: device `%s' already open\n", devname);
2716141cc406Sopenharmony_ci	      return SANE_STATUS_INVAL;
2717141cc406Sopenharmony_ci	    }
2718141cc406Sopenharmony_ci	  found = SANE_TRUE;
2719141cc406Sopenharmony_ci	  break;
2720141cc406Sopenharmony_ci	}
2721141cc406Sopenharmony_ci    }
2722141cc406Sopenharmony_ci
2723141cc406Sopenharmony_ci  if (!found)
2724141cc406Sopenharmony_ci    {
2725141cc406Sopenharmony_ci      DBG (1, "sanei_usb_open: can't find device `%s' in list\n", devname);
2726141cc406Sopenharmony_ci      return SANE_STATUS_INVAL;
2727141cc406Sopenharmony_ci    }
2728141cc406Sopenharmony_ci
2729141cc406Sopenharmony_ci  if (testing_mode == sanei_usb_testing_mode_replay)
2730141cc406Sopenharmony_ci    {
2731141cc406Sopenharmony_ci      DBG (1, "sanei_usb_open: opening fake USB device\n");
2732141cc406Sopenharmony_ci      // the device configuration has been already filled in
2733141cc406Sopenharmony_ci      // sanei_usb_testing_init()
2734141cc406Sopenharmony_ci    }
2735141cc406Sopenharmony_ci  else if (devices[devcount].method == sanei_usb_method_libusb)
2736141cc406Sopenharmony_ci    {
2737141cc406Sopenharmony_ci#ifdef HAVE_LIBUSB_LEGACY
2738141cc406Sopenharmony_ci      struct usb_device *dev;
2739141cc406Sopenharmony_ci      struct usb_interface_descriptor *interface;
2740141cc406Sopenharmony_ci      int result, num;
2741141cc406Sopenharmony_ci      int c, i, a;
2742141cc406Sopenharmony_ci
2743141cc406Sopenharmony_ci      devices[devcount].libusb_handle =
2744141cc406Sopenharmony_ci	usb_open (devices[devcount].libusb_device);
2745141cc406Sopenharmony_ci      if (!devices[devcount].libusb_handle)
2746141cc406Sopenharmony_ci	{
2747141cc406Sopenharmony_ci	  SANE_Status status = SANE_STATUS_INVAL;
2748141cc406Sopenharmony_ci
2749141cc406Sopenharmony_ci	  DBG (1, "sanei_usb_open: can't open device `%s': %s\n",
2750141cc406Sopenharmony_ci	       devname, strerror (errno));
2751141cc406Sopenharmony_ci	  if (errno == EPERM || errno == EACCES)
2752141cc406Sopenharmony_ci	    {
2753141cc406Sopenharmony_ci	      DBG (1, "Make sure you run as root or set appropriate "
2754141cc406Sopenharmony_ci		   "permissions\n");
2755141cc406Sopenharmony_ci	      status = SANE_STATUS_ACCESS_DENIED;
2756141cc406Sopenharmony_ci	    }
2757141cc406Sopenharmony_ci	  else if (errno == EBUSY)
2758141cc406Sopenharmony_ci	    {
2759141cc406Sopenharmony_ci	      DBG (1, "Maybe the kernel scanner driver claims the "
2760141cc406Sopenharmony_ci		   "scanner's interface?\n");
2761141cc406Sopenharmony_ci	      status = SANE_STATUS_DEVICE_BUSY;
2762141cc406Sopenharmony_ci	    }
2763141cc406Sopenharmony_ci	  return status;
2764141cc406Sopenharmony_ci	}
2765141cc406Sopenharmony_ci
2766141cc406Sopenharmony_ci      dev = usb_device (devices[devcount].libusb_handle);
2767141cc406Sopenharmony_ci
2768141cc406Sopenharmony_ci      /* Set the configuration */
2769141cc406Sopenharmony_ci      if (!dev->config)
2770141cc406Sopenharmony_ci	{
2771141cc406Sopenharmony_ci	  DBG (1, "sanei_usb_open: device `%s' not configured?\n", devname);
2772141cc406Sopenharmony_ci	  return SANE_STATUS_INVAL;
2773141cc406Sopenharmony_ci	}
2774141cc406Sopenharmony_ci      if (dev->descriptor.bNumConfigurations > 1)
2775141cc406Sopenharmony_ci	{
2776141cc406Sopenharmony_ci	  DBG (3, "sanei_usb_open: more than one "
2777141cc406Sopenharmony_ci	       "configuration (%d), choosing first config (%d)\n",
2778141cc406Sopenharmony_ci	       dev->descriptor.bNumConfigurations,
2779141cc406Sopenharmony_ci	       dev->config[0].bConfigurationValue);
2780141cc406Sopenharmony_ci
2781141cc406Sopenharmony_ci	  result = usb_set_configuration (devices[devcount].libusb_handle,
2782141cc406Sopenharmony_ci					  dev->config[0].bConfigurationValue);
2783141cc406Sopenharmony_ci	  if (result < 0)
2784141cc406Sopenharmony_ci	    {
2785141cc406Sopenharmony_ci	      SANE_Status status = SANE_STATUS_INVAL;
2786141cc406Sopenharmony_ci
2787141cc406Sopenharmony_ci	      DBG (1, "sanei_usb_open: libusb complained: %s\n",
2788141cc406Sopenharmony_ci		   usb_strerror ());
2789141cc406Sopenharmony_ci	      if (errno == EPERM || errno == EACCES)
2790141cc406Sopenharmony_ci		{
2791141cc406Sopenharmony_ci		  DBG (1, "Make sure you run as root or set appropriate "
2792141cc406Sopenharmony_ci		       "permissions\n");
2793141cc406Sopenharmony_ci		  status = SANE_STATUS_ACCESS_DENIED;
2794141cc406Sopenharmony_ci		}
2795141cc406Sopenharmony_ci	      else if (errno == EBUSY)
2796141cc406Sopenharmony_ci		{
2797141cc406Sopenharmony_ci		  DBG (3, "Maybe the kernel scanner driver or usblp claims the "
2798141cc406Sopenharmony_ci		       "interface? Ignoring this error...\n");
2799141cc406Sopenharmony_ci		  status = SANE_STATUS_GOOD;
2800141cc406Sopenharmony_ci		}
2801141cc406Sopenharmony_ci	      if (status != SANE_STATUS_GOOD)
2802141cc406Sopenharmony_ci		{
2803141cc406Sopenharmony_ci		  usb_close (devices[devcount].libusb_handle);
2804141cc406Sopenharmony_ci		  return status;
2805141cc406Sopenharmony_ci		}
2806141cc406Sopenharmony_ci	    }
2807141cc406Sopenharmony_ci	}
2808141cc406Sopenharmony_ci
2809141cc406Sopenharmony_ci      /* Claim the interface */
2810141cc406Sopenharmony_ci      result = usb_claim_interface (devices[devcount].libusb_handle,
2811141cc406Sopenharmony_ci				    devices[devcount].interface_nr);
2812141cc406Sopenharmony_ci      if (result < 0)
2813141cc406Sopenharmony_ci	{
2814141cc406Sopenharmony_ci	  SANE_Status status = SANE_STATUS_INVAL;
2815141cc406Sopenharmony_ci
2816141cc406Sopenharmony_ci	  DBG (1, "sanei_usb_open: libusb complained: %s\n", usb_strerror ());
2817141cc406Sopenharmony_ci	  if (errno == EPERM || errno == EACCES)
2818141cc406Sopenharmony_ci	    {
2819141cc406Sopenharmony_ci	      DBG (1, "Make sure you run as root or set appropriate "
2820141cc406Sopenharmony_ci		   "permissions\n");
2821141cc406Sopenharmony_ci	      status = SANE_STATUS_ACCESS_DENIED;
2822141cc406Sopenharmony_ci	    }
2823141cc406Sopenharmony_ci	  else if (errno == EBUSY)
2824141cc406Sopenharmony_ci	    {
2825141cc406Sopenharmony_ci	      DBG (1, "Maybe the kernel scanner driver claims the "
2826141cc406Sopenharmony_ci		   "scanner's interface?\n");
2827141cc406Sopenharmony_ci	      status = SANE_STATUS_DEVICE_BUSY;
2828141cc406Sopenharmony_ci	    }
2829141cc406Sopenharmony_ci	  usb_close (devices[devcount].libusb_handle);
2830141cc406Sopenharmony_ci	  return status;
2831141cc406Sopenharmony_ci	}
2832141cc406Sopenharmony_ci
2833141cc406Sopenharmony_ci      /* Loop through all of the configurations */
2834141cc406Sopenharmony_ci      for (c = 0; c < dev->descriptor.bNumConfigurations; c++)
2835141cc406Sopenharmony_ci	{
2836141cc406Sopenharmony_ci	  /* Loop through all of the interfaces */
2837141cc406Sopenharmony_ci	  for (i = 0; i < dev->config[c].bNumInterfaces; i++)
2838141cc406Sopenharmony_ci	    {
2839141cc406Sopenharmony_ci	      /* Loop through all of the alternate settings */
2840141cc406Sopenharmony_ci	      for (a = 0; a < dev->config[c].interface[i].num_altsetting; a++)
2841141cc406Sopenharmony_ci		{
2842141cc406Sopenharmony_ci		  DBG (5, "sanei_usb_open: configuration nr: %d\n", c);
2843141cc406Sopenharmony_ci		  DBG (5, "sanei_usb_open:     interface nr: %d\n", i);
2844141cc406Sopenharmony_ci		  DBG (5, "sanei_usb_open:   alt_setting nr: %d\n", a);
2845141cc406Sopenharmony_ci
2846141cc406Sopenharmony_ci		  /* Start by interfaces found in sanei_usb_init */
2847141cc406Sopenharmony_ci		  if (c == 0 && i != devices[devcount].interface_nr)
2848141cc406Sopenharmony_ci		    {
2849141cc406Sopenharmony_ci		      DBG (5, "sanei_usb_open: interface %d not detected as "
2850141cc406Sopenharmony_ci			"a scanner by sanei_usb_init, ignoring.\n", i);
2851141cc406Sopenharmony_ci		      continue;
2852141cc406Sopenharmony_ci		     }
2853141cc406Sopenharmony_ci
2854141cc406Sopenharmony_ci		  interface = &dev->config[c].interface[i].altsetting[a];
2855141cc406Sopenharmony_ci
2856141cc406Sopenharmony_ci		  /* Now we look for usable endpoints */
2857141cc406Sopenharmony_ci		  for (num = 0; num < interface->bNumEndpoints; num++)
2858141cc406Sopenharmony_ci		    {
2859141cc406Sopenharmony_ci		      struct usb_endpoint_descriptor *endpoint;
2860141cc406Sopenharmony_ci		      int address, direction, transfer_type;
2861141cc406Sopenharmony_ci
2862141cc406Sopenharmony_ci		      endpoint = &interface->endpoint[num];
2863141cc406Sopenharmony_ci		      DBG (5, "sanei_usb_open: endpoint nr: %d\n", num);
2864141cc406Sopenharmony_ci		      transfer_type =
2865141cc406Sopenharmony_ci			endpoint->bmAttributes & USB_ENDPOINT_TYPE_MASK;
2866141cc406Sopenharmony_ci		      direction =
2867141cc406Sopenharmony_ci			endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK;
2868141cc406Sopenharmony_ci
2869141cc406Sopenharmony_ci                      sanei_usb_add_endpoint(&devices[devcount], transfer_type,
2870141cc406Sopenharmony_ci                                             endpoint->bEndpointAddress,
2871141cc406Sopenharmony_ci                                             direction);
2872141cc406Sopenharmony_ci		    }
2873141cc406Sopenharmony_ci		}
2874141cc406Sopenharmony_ci	    }
2875141cc406Sopenharmony_ci	}
2876141cc406Sopenharmony_ci
2877141cc406Sopenharmony_ci#elif defined(HAVE_LIBUSB) /* libusb-1.0 */
2878141cc406Sopenharmony_ci
2879141cc406Sopenharmony_ci      int config;
2880141cc406Sopenharmony_ci      libusb_device *dev;
2881141cc406Sopenharmony_ci      struct libusb_device_descriptor desc;
2882141cc406Sopenharmony_ci      struct libusb_config_descriptor *config0;
2883141cc406Sopenharmony_ci      int result, num;
2884141cc406Sopenharmony_ci      int c, i, a;
2885141cc406Sopenharmony_ci
2886141cc406Sopenharmony_ci      dev = devices[devcount].lu_device;
2887141cc406Sopenharmony_ci
2888141cc406Sopenharmony_ci      result = libusb_open (dev, &devices[devcount].lu_handle);
2889141cc406Sopenharmony_ci      if (result < 0)
2890141cc406Sopenharmony_ci	{
2891141cc406Sopenharmony_ci	  SANE_Status status = SANE_STATUS_INVAL;
2892141cc406Sopenharmony_ci
2893141cc406Sopenharmony_ci	  DBG (1, "sanei_usb_open: can't open device `%s': %s\n",
2894141cc406Sopenharmony_ci	       devname, sanei_libusb_strerror (result));
2895141cc406Sopenharmony_ci	  if (result == LIBUSB_ERROR_ACCESS)
2896141cc406Sopenharmony_ci	    {
2897141cc406Sopenharmony_ci	      DBG (1, "Make sure you run as root or set appropriate "
2898141cc406Sopenharmony_ci		   "permissions\n");
2899141cc406Sopenharmony_ci	      status = SANE_STATUS_ACCESS_DENIED;
2900141cc406Sopenharmony_ci	    }
2901141cc406Sopenharmony_ci	  else if (result == LIBUSB_ERROR_BUSY)
2902141cc406Sopenharmony_ci	    {
2903141cc406Sopenharmony_ci	      DBG (1, "Maybe the kernel scanner driver claims the "
2904141cc406Sopenharmony_ci		   "scanner's interface?\n");
2905141cc406Sopenharmony_ci	      status = SANE_STATUS_DEVICE_BUSY;
2906141cc406Sopenharmony_ci	    }
2907141cc406Sopenharmony_ci	  else if (result == LIBUSB_ERROR_NO_MEM)
2908141cc406Sopenharmony_ci	    {
2909141cc406Sopenharmony_ci	      status = SANE_STATUS_NO_MEM;
2910141cc406Sopenharmony_ci	    }
2911141cc406Sopenharmony_ci	  return status;
2912141cc406Sopenharmony_ci	}
2913141cc406Sopenharmony_ci
2914141cc406Sopenharmony_ci      result = libusb_get_configuration (devices[devcount].lu_handle, &config);
2915141cc406Sopenharmony_ci      if (result < 0)
2916141cc406Sopenharmony_ci	{
2917141cc406Sopenharmony_ci	  DBG (1,
2918141cc406Sopenharmony_ci	       "sanei_usb_open: could not get configuration for device `%s' (err %d)\n",
2919141cc406Sopenharmony_ci	       devname, result);
2920141cc406Sopenharmony_ci	  return SANE_STATUS_INVAL;
2921141cc406Sopenharmony_ci	}
2922141cc406Sopenharmony_ci
2923141cc406Sopenharmony_ci#if !defined(SANEI_ALLOW_UNCONFIGURED_DEVICES)
2924141cc406Sopenharmony_ci      if (config == 0)
2925141cc406Sopenharmony_ci	{
2926141cc406Sopenharmony_ci	  DBG (1, "sanei_usb_open: device `%s' not configured?\n", devname);
2927141cc406Sopenharmony_ci	  return SANE_STATUS_INVAL;
2928141cc406Sopenharmony_ci	}
2929141cc406Sopenharmony_ci#endif
2930141cc406Sopenharmony_ci
2931141cc406Sopenharmony_ci      result = libusb_get_device_descriptor (dev, &desc);
2932141cc406Sopenharmony_ci      if (result < 0)
2933141cc406Sopenharmony_ci	{
2934141cc406Sopenharmony_ci	  DBG (1,
2935141cc406Sopenharmony_ci	       "sanei_usb_open: could not get device descriptor for device `%s' (err %d)\n",
2936141cc406Sopenharmony_ci	       devname, result);
2937141cc406Sopenharmony_ci	  return SANE_STATUS_INVAL;
2938141cc406Sopenharmony_ci	}
2939141cc406Sopenharmony_ci
2940141cc406Sopenharmony_ci      result = libusb_get_config_descriptor (dev, 0, &config0);
2941141cc406Sopenharmony_ci      if (result < 0)
2942141cc406Sopenharmony_ci	{
2943141cc406Sopenharmony_ci	  DBG (1,
2944141cc406Sopenharmony_ci	       "sanei_usb_open: could not get config[0] descriptor for device `%s' (err %d)\n",
2945141cc406Sopenharmony_ci	       devname, result);
2946141cc406Sopenharmony_ci	  return SANE_STATUS_INVAL;
2947141cc406Sopenharmony_ci	}
2948141cc406Sopenharmony_ci
2949141cc406Sopenharmony_ci      /* Set the configuration */
2950141cc406Sopenharmony_ci      if (desc.bNumConfigurations > 1)
2951141cc406Sopenharmony_ci	{
2952141cc406Sopenharmony_ci	  DBG (3, "sanei_usb_open: more than one "
2953141cc406Sopenharmony_ci	       "configuration (%d), choosing first config (%d)\n",
2954141cc406Sopenharmony_ci	       desc.bNumConfigurations,
2955141cc406Sopenharmony_ci	       config0->bConfigurationValue);
2956141cc406Sopenharmony_ci
2957141cc406Sopenharmony_ci	  result = 0;
2958141cc406Sopenharmony_ci	  if (config != config0->bConfigurationValue)
2959141cc406Sopenharmony_ci	    result = libusb_set_configuration (devices[devcount].lu_handle,
2960141cc406Sopenharmony_ci					       config0->bConfigurationValue);
2961141cc406Sopenharmony_ci
2962141cc406Sopenharmony_ci	  if (result < 0)
2963141cc406Sopenharmony_ci	    {
2964141cc406Sopenharmony_ci	      SANE_Status status = SANE_STATUS_INVAL;
2965141cc406Sopenharmony_ci
2966141cc406Sopenharmony_ci	      DBG (1, "sanei_usb_open: libusb complained: %s\n",
2967141cc406Sopenharmony_ci		   sanei_libusb_strerror (result));
2968141cc406Sopenharmony_ci	      if (result == LIBUSB_ERROR_ACCESS)
2969141cc406Sopenharmony_ci		{
2970141cc406Sopenharmony_ci		  DBG (1, "Make sure you run as root or set appropriate "
2971141cc406Sopenharmony_ci		       "permissions\n");
2972141cc406Sopenharmony_ci		  status = SANE_STATUS_ACCESS_DENIED;
2973141cc406Sopenharmony_ci		}
2974141cc406Sopenharmony_ci	      else if (result == LIBUSB_ERROR_BUSY)
2975141cc406Sopenharmony_ci		{
2976141cc406Sopenharmony_ci		  DBG (3, "Maybe the kernel scanner driver or usblp claims "
2977141cc406Sopenharmony_ci		       "the interface? Ignoring this error...\n");
2978141cc406Sopenharmony_ci		  status = SANE_STATUS_GOOD;
2979141cc406Sopenharmony_ci		}
2980141cc406Sopenharmony_ci
2981141cc406Sopenharmony_ci	      if (status != SANE_STATUS_GOOD)
2982141cc406Sopenharmony_ci		{
2983141cc406Sopenharmony_ci		  libusb_close (devices[devcount].lu_handle);
2984141cc406Sopenharmony_ci		  libusb_free_config_descriptor (config0);
2985141cc406Sopenharmony_ci		  return status;
2986141cc406Sopenharmony_ci		}
2987141cc406Sopenharmony_ci	    }
2988141cc406Sopenharmony_ci	}
2989141cc406Sopenharmony_ci      libusb_free_config_descriptor (config0);
2990141cc406Sopenharmony_ci
2991141cc406Sopenharmony_ci      /* Claim the interface */
2992141cc406Sopenharmony_ci      result = libusb_claim_interface (devices[devcount].lu_handle,
2993141cc406Sopenharmony_ci				       devices[devcount].interface_nr);
2994141cc406Sopenharmony_ci      if (result < 0)
2995141cc406Sopenharmony_ci	{
2996141cc406Sopenharmony_ci	  SANE_Status status = SANE_STATUS_INVAL;
2997141cc406Sopenharmony_ci
2998141cc406Sopenharmony_ci	  DBG (1, "sanei_usb_open: libusb complained: %s\n",
2999141cc406Sopenharmony_ci	       sanei_libusb_strerror (result));
3000141cc406Sopenharmony_ci	  if (result == LIBUSB_ERROR_ACCESS)
3001141cc406Sopenharmony_ci	    {
3002141cc406Sopenharmony_ci	      DBG (1, "Make sure you run as root or set appropriate "
3003141cc406Sopenharmony_ci		   "permissions\n");
3004141cc406Sopenharmony_ci	      status = SANE_STATUS_ACCESS_DENIED;
3005141cc406Sopenharmony_ci	    }
3006141cc406Sopenharmony_ci	  else if (result == LIBUSB_ERROR_BUSY)
3007141cc406Sopenharmony_ci	    {
3008141cc406Sopenharmony_ci	      DBG (1, "Maybe the kernel scanner driver claims the "
3009141cc406Sopenharmony_ci		   "scanner's interface?\n");
3010141cc406Sopenharmony_ci	      status = SANE_STATUS_DEVICE_BUSY;
3011141cc406Sopenharmony_ci	    }
3012141cc406Sopenharmony_ci
3013141cc406Sopenharmony_ci	  libusb_close (devices[devcount].lu_handle);
3014141cc406Sopenharmony_ci	  return status;
3015141cc406Sopenharmony_ci	}
3016141cc406Sopenharmony_ci
3017141cc406Sopenharmony_ci      /* Loop through all of the configurations */
3018141cc406Sopenharmony_ci      for (c = 0; c < desc.bNumConfigurations; c++)
3019141cc406Sopenharmony_ci	{
3020141cc406Sopenharmony_ci	  struct libusb_config_descriptor *config;
3021141cc406Sopenharmony_ci
3022141cc406Sopenharmony_ci	  result = libusb_get_config_descriptor (dev, c, &config);
3023141cc406Sopenharmony_ci	  if (result < 0)
3024141cc406Sopenharmony_ci	    {
3025141cc406Sopenharmony_ci	      DBG (1,
3026141cc406Sopenharmony_ci		   "sanei_usb_open: could not get config[%d] descriptor for device `%s' (err %d)\n",
3027141cc406Sopenharmony_ci		   c, devname, result);
3028141cc406Sopenharmony_ci	      continue;
3029141cc406Sopenharmony_ci	    }
3030141cc406Sopenharmony_ci
3031141cc406Sopenharmony_ci	  /* Loop through all of the interfaces */
3032141cc406Sopenharmony_ci	  for (i = 0; i < config->bNumInterfaces; i++)
3033141cc406Sopenharmony_ci	    {
3034141cc406Sopenharmony_ci	      /* Loop through all of the alternate settings */
3035141cc406Sopenharmony_ci	      for (a = 0; a < config->interface[i].num_altsetting; a++)
3036141cc406Sopenharmony_ci		{
3037141cc406Sopenharmony_ci		  const struct libusb_interface_descriptor *interface;
3038141cc406Sopenharmony_ci
3039141cc406Sopenharmony_ci		  DBG (5, "sanei_usb_open: configuration nr: %d\n", c);
3040141cc406Sopenharmony_ci		  DBG (5, "sanei_usb_open:     interface nr: %d\n", i);
3041141cc406Sopenharmony_ci		  DBG (5, "sanei_usb_open:   alt_setting nr: %d\n", a);
3042141cc406Sopenharmony_ci
3043141cc406Sopenharmony_ci                  /* Start by interfaces found in sanei_usb_init */
3044141cc406Sopenharmony_ci                  if (c == 0 && i != devices[devcount].interface_nr)
3045141cc406Sopenharmony_ci                    {
3046141cc406Sopenharmony_ci                      DBG (5, "sanei_usb_open: interface %d not detected as "
3047141cc406Sopenharmony_ci                        "a scanner by sanei_usb_init, ignoring.\n", i);
3048141cc406Sopenharmony_ci                      continue;
3049141cc406Sopenharmony_ci                     }
3050141cc406Sopenharmony_ci
3051141cc406Sopenharmony_ci		  interface = &config->interface[i].altsetting[a];
3052141cc406Sopenharmony_ci
3053141cc406Sopenharmony_ci		  /* Now we look for usable endpoints */
3054141cc406Sopenharmony_ci		  for (num = 0; num < interface->bNumEndpoints; num++)
3055141cc406Sopenharmony_ci		    {
3056141cc406Sopenharmony_ci		      const struct libusb_endpoint_descriptor *endpoint;
3057141cc406Sopenharmony_ci                      int direction, transfer_type, transfer_type_libusb;
3058141cc406Sopenharmony_ci
3059141cc406Sopenharmony_ci		      endpoint = &interface->endpoint[num];
3060141cc406Sopenharmony_ci		      DBG (5, "sanei_usb_open: endpoint nr: %d\n", num);
3061141cc406Sopenharmony_ci
3062141cc406Sopenharmony_ci                      transfer_type_libusb =
3063141cc406Sopenharmony_ci                          endpoint->bmAttributes & LIBUSB_TRANSFER_TYPE_MASK;
3064141cc406Sopenharmony_ci		      direction = endpoint->bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK;
3065141cc406Sopenharmony_ci
3066141cc406Sopenharmony_ci                      // don't rely on LIBUSB_TRANSFER_TYPE_* mapping to
3067141cc406Sopenharmony_ci                      // USB_ENDPOINT_TYPE_* even though they'll most likely be
3068141cc406Sopenharmony_ci                      // the same
3069141cc406Sopenharmony_ci                      switch (transfer_type_libusb)
3070141cc406Sopenharmony_ci                        {
3071141cc406Sopenharmony_ci                          case LIBUSB_TRANSFER_TYPE_INTERRUPT:
3072141cc406Sopenharmony_ci                            transfer_type = USB_ENDPOINT_TYPE_INTERRUPT;
3073141cc406Sopenharmony_ci                            break;
3074141cc406Sopenharmony_ci                          case LIBUSB_TRANSFER_TYPE_BULK:
3075141cc406Sopenharmony_ci                            transfer_type = USB_ENDPOINT_TYPE_BULK;
3076141cc406Sopenharmony_ci                            break;
3077141cc406Sopenharmony_ci                          case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS:
3078141cc406Sopenharmony_ci                            transfer_type = LIBUSB_TRANSFER_TYPE_ISOCHRONOUS;
3079141cc406Sopenharmony_ci                            break;
3080141cc406Sopenharmony_ci                          case LIBUSB_TRANSFER_TYPE_CONTROL:
3081141cc406Sopenharmony_ci                            transfer_type = USB_ENDPOINT_TYPE_CONTROL;
3082141cc406Sopenharmony_ci                            break;
3083141cc406Sopenharmony_ci
3084141cc406Sopenharmony_ci                        }
3085141cc406Sopenharmony_ci
3086141cc406Sopenharmony_ci                      sanei_usb_add_endpoint(&devices[devcount],
3087141cc406Sopenharmony_ci                                             transfer_type,
3088141cc406Sopenharmony_ci                                             endpoint->bEndpointAddress,
3089141cc406Sopenharmony_ci                                             direction);
3090141cc406Sopenharmony_ci		    }
3091141cc406Sopenharmony_ci		}
3092141cc406Sopenharmony_ci	    }
3093141cc406Sopenharmony_ci
3094141cc406Sopenharmony_ci	  libusb_free_config_descriptor (config);
3095141cc406Sopenharmony_ci	}
3096141cc406Sopenharmony_ci#elif defined(HAVE_USB_MANAGER) /* usb_manager */
3097141cc406Sopenharmony_ci
3098141cc406Sopenharmony_ci      int config;
3099141cc406Sopenharmony_ci      usb_manager_device *dev;
3100141cc406Sopenharmony_ci      struct usb_manager_device_descriptor desc;
3101141cc406Sopenharmony_ci      struct usb_manager_config_descriptor *config0;
3102141cc406Sopenharmony_ci      int result, num;
3103141cc406Sopenharmony_ci      int c, i, a;
3104141cc406Sopenharmony_ci
3105141cc406Sopenharmony_ci      dev = devices[devcount].usb_manager_device;
3106141cc406Sopenharmony_ci
3107141cc406Sopenharmony_ci      result = usb_manager_open (dev, &devices[devcount].usb_manager_handle);
3108141cc406Sopenharmony_ci      if (result < 0)
3109141cc406Sopenharmony_ci	{
3110141cc406Sopenharmony_ci	  SANE_Status status = SANE_STATUS_INVAL;
3111141cc406Sopenharmony_ci
3112141cc406Sopenharmony_ci	  DBG (1, "sanei_usb_open: can't open device `%s': %s\n",
3113141cc406Sopenharmony_ci	       devname, sanei_usb_manager_strerror (result));
3114141cc406Sopenharmony_ci	  if (result == USB_MANAGER_ERROR_ACCESS)
3115141cc406Sopenharmony_ci	    {
3116141cc406Sopenharmony_ci	      DBG (1, "Make sure you run as root or set appropriate "
3117141cc406Sopenharmony_ci		   "permissions\n");
3118141cc406Sopenharmony_ci	      status = SANE_STATUS_ACCESS_DENIED;
3119141cc406Sopenharmony_ci	    }
3120141cc406Sopenharmony_ci	  else if (result == USB_MANAGER_ERROR_BUSY)
3121141cc406Sopenharmony_ci	    {
3122141cc406Sopenharmony_ci	      DBG (1, "Maybe the kernel scanner driver claims the "
3123141cc406Sopenharmony_ci		   "scanner's interface?\n");
3124141cc406Sopenharmony_ci	      status = SANE_STATUS_DEVICE_BUSY;
3125141cc406Sopenharmony_ci	    }
3126141cc406Sopenharmony_ci	  else if (result == USB_MANAGER_ERROR_NO_MEM)
3127141cc406Sopenharmony_ci	    {
3128141cc406Sopenharmony_ci	      status = SANE_STATUS_NO_MEM;
3129141cc406Sopenharmony_ci	    }
3130141cc406Sopenharmony_ci	  return status;
3131141cc406Sopenharmony_ci	}
3132141cc406Sopenharmony_ci
3133141cc406Sopenharmony_ci      result = usb_manager_get_configuration (devices[devcount].usb_manager_handle, &config);
3134141cc406Sopenharmony_ci      if (result < 0)
3135141cc406Sopenharmony_ci	{
3136141cc406Sopenharmony_ci	  DBG (1,
3137141cc406Sopenharmony_ci	       "sanei_usb_open: could not get configuration for device `%s' (err %d)\n",
3138141cc406Sopenharmony_ci	       devname, result);
3139141cc406Sopenharmony_ci	  return SANE_STATUS_INVAL;
3140141cc406Sopenharmony_ci	}
3141141cc406Sopenharmony_ci
3142141cc406Sopenharmony_ci#if !defined(SANEI_ALLOW_UNCONFIGURED_DEVICES)
3143141cc406Sopenharmony_ci      if (config == 0)
3144141cc406Sopenharmony_ci	{
3145141cc406Sopenharmony_ci	  DBG (1, "sanei_usb_open: device `%s' not configured?\n", devname);
3146141cc406Sopenharmony_ci	  return SANE_STATUS_INVAL;
3147141cc406Sopenharmony_ci	}
3148141cc406Sopenharmony_ci#endif
3149141cc406Sopenharmony_ci
3150141cc406Sopenharmony_ci      result = usb_manager_get_device_descriptor (dev, &desc);
3151141cc406Sopenharmony_ci      if (result < 0)
3152141cc406Sopenharmony_ci	{
3153141cc406Sopenharmony_ci	  DBG (1,
3154141cc406Sopenharmony_ci	       "sanei_usb_open: could not get device descriptor for device `%s' (err %d)\n",
3155141cc406Sopenharmony_ci	       devname, result);
3156141cc406Sopenharmony_ci	  return SANE_STATUS_INVAL;
3157141cc406Sopenharmony_ci	}
3158141cc406Sopenharmony_ci
3159141cc406Sopenharmony_ci      result = usb_manager_get_config_descriptor (dev, 0, &config0);
3160141cc406Sopenharmony_ci      if (result < 0)
3161141cc406Sopenharmony_ci	{
3162141cc406Sopenharmony_ci	  DBG (1,
3163141cc406Sopenharmony_ci	       "sanei_usb_open: could not get config[0] descriptor for device `%s' (err %d)\n",
3164141cc406Sopenharmony_ci	       devname, result);
3165141cc406Sopenharmony_ci	  return SANE_STATUS_INVAL;
3166141cc406Sopenharmony_ci	}
3167141cc406Sopenharmony_ci
3168141cc406Sopenharmony_ci      /* Set the configuration */
3169141cc406Sopenharmony_ci      if (desc.bNumConfigurations > 1)
3170141cc406Sopenharmony_ci	{
3171141cc406Sopenharmony_ci	  DBG (3, "sanei_usb_open: more than one "
3172141cc406Sopenharmony_ci	       "configuration (%d), choosing first config (%d)\n",
3173141cc406Sopenharmony_ci	       desc.bNumConfigurations,
3174141cc406Sopenharmony_ci	       config0->bConfigurationValue);
3175141cc406Sopenharmony_ci
3176141cc406Sopenharmony_ci	  result = 0;
3177141cc406Sopenharmony_ci	  if (config != config0->bConfigurationValue)
3178141cc406Sopenharmony_ci	    result = usb_manager_set_configuration (devices[devcount].usb_manager_handle,
3179141cc406Sopenharmony_ci					       config0->bConfigurationValue);
3180141cc406Sopenharmony_ci
3181141cc406Sopenharmony_ci	  if (result < 0)
3182141cc406Sopenharmony_ci	    {
3183141cc406Sopenharmony_ci	      SANE_Status status = SANE_STATUS_INVAL;
3184141cc406Sopenharmony_ci
3185141cc406Sopenharmony_ci	      DBG (1, "sanei_usb_open: usb_manager complained: %s\n",
3186141cc406Sopenharmony_ci		   sanei_usb_manager_strerror (result));
3187141cc406Sopenharmony_ci	      if (result == USB_MANAGER_ERROR_ACCESS)
3188141cc406Sopenharmony_ci		{
3189141cc406Sopenharmony_ci		  DBG (1, "Make sure you run as root or set appropriate "
3190141cc406Sopenharmony_ci		       "permissions\n");
3191141cc406Sopenharmony_ci		  status = SANE_STATUS_ACCESS_DENIED;
3192141cc406Sopenharmony_ci		}
3193141cc406Sopenharmony_ci	      else if (result == USB_MANAGER_ERROR_BUSY)
3194141cc406Sopenharmony_ci		{
3195141cc406Sopenharmony_ci		  DBG (3, "Maybe the kernel scanner driver or usblp claims "
3196141cc406Sopenharmony_ci		       "the interface? Ignoring this error...\n");
3197141cc406Sopenharmony_ci		  status = SANE_STATUS_GOOD;
3198141cc406Sopenharmony_ci		}
3199141cc406Sopenharmony_ci
3200141cc406Sopenharmony_ci	      if (status != SANE_STATUS_GOOD)
3201141cc406Sopenharmony_ci		{
3202141cc406Sopenharmony_ci		  usb_manager_close (devices[devcount].usb_manager_handle);
3203141cc406Sopenharmony_ci		  usb_manager_free_config_descriptor (config0);
3204141cc406Sopenharmony_ci		  return status;
3205141cc406Sopenharmony_ci		}
3206141cc406Sopenharmony_ci	    }
3207141cc406Sopenharmony_ci	}
3208141cc406Sopenharmony_ci      usb_manager_free_config_descriptor (config0);
3209141cc406Sopenharmony_ci
3210141cc406Sopenharmony_ci      /* Claim the interface */
3211141cc406Sopenharmony_ci      result = usb_manager_claim_interface (devices[devcount].usb_manager_handle,
3212141cc406Sopenharmony_ci				       devices[devcount].interface_nr);
3213141cc406Sopenharmony_ci      if (result < 0)
3214141cc406Sopenharmony_ci	{
3215141cc406Sopenharmony_ci	  SANE_Status status = SANE_STATUS_INVAL;
3216141cc406Sopenharmony_ci
3217141cc406Sopenharmony_ci	  DBG (1, "sanei_usb_open: usb_manager complained: %s\n",
3218141cc406Sopenharmony_ci	       sanei_usb_manager_strerror (result));
3219141cc406Sopenharmony_ci	  if (result == USB_MANAGER_ERROR_ACCESS)
3220141cc406Sopenharmony_ci	    {
3221141cc406Sopenharmony_ci	      DBG (1, "Make sure you run as root or set appropriate "
3222141cc406Sopenharmony_ci		   "permissions\n");
3223141cc406Sopenharmony_ci	      status = SANE_STATUS_ACCESS_DENIED;
3224141cc406Sopenharmony_ci	    }
3225141cc406Sopenharmony_ci	  else if (result == USB_MANAGER_ERROR_BUSY)
3226141cc406Sopenharmony_ci	    {
3227141cc406Sopenharmony_ci	      DBG (1, "Maybe the kernel scanner driver claims the "
3228141cc406Sopenharmony_ci		   "scanner's interface?\n");
3229141cc406Sopenharmony_ci	      status = SANE_STATUS_DEVICE_BUSY;
3230141cc406Sopenharmony_ci	    }
3231141cc406Sopenharmony_ci
3232141cc406Sopenharmony_ci	  usb_manager_close (devices[devcount].usb_manager_handle);
3233141cc406Sopenharmony_ci	  return status;
3234141cc406Sopenharmony_ci	}
3235141cc406Sopenharmony_ci
3236141cc406Sopenharmony_ci      /* Loop through all of the configurations */
3237141cc406Sopenharmony_ci      for (c = 0; c < desc.bNumConfigurations; c++)
3238141cc406Sopenharmony_ci	{
3239141cc406Sopenharmony_ci	  struct usb_manager_config_descriptor *config;
3240141cc406Sopenharmony_ci
3241141cc406Sopenharmony_ci	  result = usb_manager_get_config_descriptor (dev, c, &config);
3242141cc406Sopenharmony_ci	  if (result < 0)
3243141cc406Sopenharmony_ci	    {
3244141cc406Sopenharmony_ci	      DBG (1,
3245141cc406Sopenharmony_ci		   "sanei_usb_open: could not get config[%d] descriptor for device `%s' (err %d)\n",
3246141cc406Sopenharmony_ci		   c, devname, result);
3247141cc406Sopenharmony_ci	      continue;
3248141cc406Sopenharmony_ci	    }
3249141cc406Sopenharmony_ci
3250141cc406Sopenharmony_ci	  /* Loop through all of the interfaces */
3251141cc406Sopenharmony_ci	  for (i = 0; i < config->bNumInterfaces; i++)
3252141cc406Sopenharmony_ci	    {
3253141cc406Sopenharmony_ci	      /* Loop through all of the alternate settings */
3254141cc406Sopenharmony_ci	      for (a = 0; a < config->interface[i].num_altsetting; a++)
3255141cc406Sopenharmony_ci		{
3256141cc406Sopenharmony_ci		  const struct usb_manager_interface_descriptor *interface;
3257141cc406Sopenharmony_ci
3258141cc406Sopenharmony_ci		  DBG (5, "sanei_usb_open: configuration nr: %d\n", c);
3259141cc406Sopenharmony_ci		  DBG (5, "sanei_usb_open:     interface nr: %d\n", i);
3260141cc406Sopenharmony_ci		  DBG (5, "sanei_usb_open:   alt_setting nr: %d\n", a);
3261141cc406Sopenharmony_ci
3262141cc406Sopenharmony_ci                  /* Start by interfaces found in sanei_usb_init */
3263141cc406Sopenharmony_ci                  if (c == 0 && i != devices[devcount].interface_nr)
3264141cc406Sopenharmony_ci                    {
3265141cc406Sopenharmony_ci                      DBG (5, "sanei_usb_open: interface %d not detected as "
3266141cc406Sopenharmony_ci                        "a scanner by sanei_usb_init, ignoring.\n", i);
3267141cc406Sopenharmony_ci                      continue;
3268141cc406Sopenharmony_ci                     }
3269141cc406Sopenharmony_ci
3270141cc406Sopenharmony_ci		  interface = &config->interface[i].altsetting[a];
3271141cc406Sopenharmony_ci
3272141cc406Sopenharmony_ci		  /* Now we look for usable endpoints */
3273141cc406Sopenharmony_ci		  for (num = 0; num < interface->bNumEndpoints; num++)
3274141cc406Sopenharmony_ci		    {
3275141cc406Sopenharmony_ci		      const struct usb_manager_endpoint_descriptor *endpoint;
3276141cc406Sopenharmony_ci                      int direction, transfer_type, transfer_type_usb_manager;
3277141cc406Sopenharmony_ci
3278141cc406Sopenharmony_ci		      endpoint = &interface->endpoint[num];
3279141cc406Sopenharmony_ci		      DBG (5, "sanei_usb_open: endpoint nr: %d\n", num);
3280141cc406Sopenharmony_ci
3281141cc406Sopenharmony_ci                      transfer_type_usb_manager =
3282141cc406Sopenharmony_ci                          endpoint->bmAttributes & USB_MANAGER_TRANSFER_TYPE_MASK;
3283141cc406Sopenharmony_ci		      direction = endpoint->bEndpointAddress & USB_MANAGER_ENDPOINT_DIR_MASK;
3284141cc406Sopenharmony_ci
3285141cc406Sopenharmony_ci                      // don't rely on USB_MANAGER_TRANSFER_TYPE_* mapping to
3286141cc406Sopenharmony_ci                      // USB_ENDPOINT_TYPE_* even though they'll most likely be
3287141cc406Sopenharmony_ci                      // the same
3288141cc406Sopenharmony_ci                      switch (transfer_type_usb_manager)
3289141cc406Sopenharmony_ci                        {
3290141cc406Sopenharmony_ci                          case USB_MANAGER_TRANSFER_TYPE_INTERRUPT:
3291141cc406Sopenharmony_ci                            transfer_type = USB_ENDPOINT_TYPE_INTERRUPT;
3292141cc406Sopenharmony_ci                            break;
3293141cc406Sopenharmony_ci                          case USB_MANAGER_TRANSFER_TYPE_BULK:
3294141cc406Sopenharmony_ci                            transfer_type = USB_ENDPOINT_TYPE_BULK;
3295141cc406Sopenharmony_ci                            break;
3296141cc406Sopenharmony_ci                          case USB_MANAGER_TRANSFER_TYPE_ISOCHRONOUS:
3297141cc406Sopenharmony_ci                            transfer_type = USB_MANAGER_TRANSFER_TYPE_ISOCHRONOUS;
3298141cc406Sopenharmony_ci                            break;
3299141cc406Sopenharmony_ci                          case USB_MANAGER_TRANSFER_TYPE_CONTROL:
3300141cc406Sopenharmony_ci                            transfer_type = USB_ENDPOINT_TYPE_CONTROL;
3301141cc406Sopenharmony_ci                            break;
3302141cc406Sopenharmony_ci
3303141cc406Sopenharmony_ci                        }
3304141cc406Sopenharmony_ci
3305141cc406Sopenharmony_ci                      sanei_usb_add_endpoint(&devices[devcount],
3306141cc406Sopenharmony_ci                                             transfer_type,
3307141cc406Sopenharmony_ci                                             endpoint->bEndpointAddress,
3308141cc406Sopenharmony_ci                                             direction);
3309141cc406Sopenharmony_ci		    }
3310141cc406Sopenharmony_ci		}
3311141cc406Sopenharmony_ci	    }
3312141cc406Sopenharmony_ci
3313141cc406Sopenharmony_ci	  usb_manager_free_config_descriptor (config);
3314141cc406Sopenharmony_ci	}
3315141cc406Sopenharmony_ci#else /* not HAVE_LIBUSB_LEGACY && not HAVE_LIBUSB && not HAVE_USB_MANAGER */
3316141cc406Sopenharmony_ci      DBG (1, "sanei_usb_open: can't open device `%s': "
3317141cc406Sopenharmony_ci	   "libusb support missing\n", devname);
3318141cc406Sopenharmony_ci      return SANE_STATUS_UNSUPPORTED;
3319141cc406Sopenharmony_ci#endif /* not HAVE_LIBUSB_LEGACY && not HAVE_LIBUSB && not HAVE_USB_MANAGER */
3320141cc406Sopenharmony_ci    }
3321141cc406Sopenharmony_ci  else if (devices[devcount].method == sanei_usb_method_scanner_driver)
3322141cc406Sopenharmony_ci    {
3323141cc406Sopenharmony_ci#ifdef FD_CLOEXEC
3324141cc406Sopenharmony_ci      long int flag;
3325141cc406Sopenharmony_ci#endif
3326141cc406Sopenharmony_ci      /* Using kernel scanner driver */
3327141cc406Sopenharmony_ci      devices[devcount].fd = -1;
3328141cc406Sopenharmony_ci#ifdef HAVE_RESMGR
3329141cc406Sopenharmony_ci      devices[devcount].fd = rsm_open_device (devname, O_RDWR);
3330141cc406Sopenharmony_ci#endif
3331141cc406Sopenharmony_ci      if (devices[devcount].fd == -1)
3332141cc406Sopenharmony_ci	devices[devcount].fd = open (devname, O_RDWR);
3333141cc406Sopenharmony_ci      if (devices[devcount].fd < 0)
3334141cc406Sopenharmony_ci	{
3335141cc406Sopenharmony_ci	  SANE_Status status = SANE_STATUS_INVAL;
3336141cc406Sopenharmony_ci
3337141cc406Sopenharmony_ci	  if (errno == EACCES)
3338141cc406Sopenharmony_ci	    status = SANE_STATUS_ACCESS_DENIED;
3339141cc406Sopenharmony_ci	  else if (errno == ENOENT)
3340141cc406Sopenharmony_ci	    {
3341141cc406Sopenharmony_ci	      DBG (5, "sanei_usb_open: open of `%s' failed: %s\n",
3342141cc406Sopenharmony_ci		   devname, strerror (errno));
3343141cc406Sopenharmony_ci	      return status;
3344141cc406Sopenharmony_ci	    }
3345141cc406Sopenharmony_ci	  DBG (1, "sanei_usb_open: open of `%s' failed: %s\n",
3346141cc406Sopenharmony_ci	       devname, strerror (errno));
3347141cc406Sopenharmony_ci	  return status;
3348141cc406Sopenharmony_ci	}
3349141cc406Sopenharmony_ci#ifdef FD_CLOEXEC
3350141cc406Sopenharmony_ci      flag = fcntl (devices[devcount].fd, F_GETFD);
3351141cc406Sopenharmony_ci      if (flag >= 0)
3352141cc406Sopenharmony_ci	{
3353141cc406Sopenharmony_ci	  if (fcntl (devices[devcount].fd, F_SETFD, flag | FD_CLOEXEC) < 0)
3354141cc406Sopenharmony_ci	    DBG (1, "sanei_usb_open: fcntl of `%s' failed: %s\n",
3355141cc406Sopenharmony_ci		 devname, strerror (errno));
3356141cc406Sopenharmony_ci	}
3357141cc406Sopenharmony_ci#endif
3358141cc406Sopenharmony_ci    }
3359141cc406Sopenharmony_ci  else if (devices[devcount].method == sanei_usb_method_usbcalls)
3360141cc406Sopenharmony_ci    {
3361141cc406Sopenharmony_ci#ifdef HAVE_USBCALLS
3362141cc406Sopenharmony_ci      CHAR ucData[2048];
3363141cc406Sopenharmony_ci      struct usb_device_descriptor *pDevDesc;
3364141cc406Sopenharmony_ci      struct usb_config_descriptor   *pCfgDesc;
3365141cc406Sopenharmony_ci      struct usb_interface_descriptor *interface;
3366141cc406Sopenharmony_ci      struct usb_endpoint_descriptor  *endpoint;
3367141cc406Sopenharmony_ci      struct usb_descriptor_header    *pDescHead;
3368141cc406Sopenharmony_ci
3369141cc406Sopenharmony_ci      ULONG  ulBufLen;
3370141cc406Sopenharmony_ci      ulBufLen = sizeof(ucData);
3371141cc406Sopenharmony_ci      memset(&ucData,0,sizeof(ucData));
3372141cc406Sopenharmony_ci
3373141cc406Sopenharmony_ci      int result, rc;
3374141cc406Sopenharmony_ci      int address, direction, transfer_type;
3375141cc406Sopenharmony_ci
3376141cc406Sopenharmony_ci      DBG (5, "devname = %s, devcount = %d\n",devices[devcount].devname,devcount);
3377141cc406Sopenharmony_ci      DBG (5, "USBCalls device number to open = %d\n",devices[devcount].fd);
3378141cc406Sopenharmony_ci      DBG (5, "USBCalls Vendor/Product to open = 0x%04x/0x%04x\n",
3379141cc406Sopenharmony_ci               devices[devcount].vendor,devices[devcount].product);
3380141cc406Sopenharmony_ci
3381141cc406Sopenharmony_ci      rc = UsbOpen (&dh,
3382141cc406Sopenharmony_ci			devices[devcount].vendor,
3383141cc406Sopenharmony_ci			devices[devcount].product,
3384141cc406Sopenharmony_ci			USB_ANY_PRODUCTVERSION,
3385141cc406Sopenharmony_ci			USB_OPEN_FIRST_UNUSED);
3386141cc406Sopenharmony_ci      DBG (1, "sanei_usb_open: UsbOpen rc = %d\n",rc);
3387141cc406Sopenharmony_ci      if (rc!=0)
3388141cc406Sopenharmony_ci	{
3389141cc406Sopenharmony_ci	  SANE_Status status = SANE_STATUS_INVAL;
3390141cc406Sopenharmony_ci	  DBG (1, "sanei_usb_open: can't open device `%s': %s\n",
3391141cc406Sopenharmony_ci	       devname, strerror (rc));
3392141cc406Sopenharmony_ci	  return status;
3393141cc406Sopenharmony_ci	}
3394141cc406Sopenharmony_ci      rc = UsbQueryDeviceReport( devices[devcount].fd,
3395141cc406Sopenharmony_ci                                  &ulBufLen,
3396141cc406Sopenharmony_ci                                  ucData);
3397141cc406Sopenharmony_ci      DBG (1, "sanei_usb_open: UsbQueryDeviceReport rc = %d\n",rc);
3398141cc406Sopenharmony_ci      pDevDesc = (struct usb_device_descriptor*)ucData;
3399141cc406Sopenharmony_ci      pCfgDesc = (struct usb_config_descriptor*) (ucData+sizeof(struct usb_device_descriptor));
3400141cc406Sopenharmony_ci      UCHAR *pCurPtr = (UCHAR*) pCfgDesc;
3401141cc406Sopenharmony_ci      UCHAR *pEndPtr = pCurPtr+ pCfgDesc->wTotalLength;
3402141cc406Sopenharmony_ci      pDescHead = (struct usb_descriptor_header *) (pCurPtr+pCfgDesc->bLength);
3403141cc406Sopenharmony_ci      /* Set the configuration */
3404141cc406Sopenharmony_ci      if (pDevDesc->bNumConfigurations > 1)
3405141cc406Sopenharmony_ci	{
3406141cc406Sopenharmony_ci	  DBG (3, "sanei_usb_open: more than one "
3407141cc406Sopenharmony_ci	       "configuration (%d), choosing first config (%d)\n",
3408141cc406Sopenharmony_ci	       pDevDesc->bNumConfigurations,
3409141cc406Sopenharmony_ci	       pCfgDesc->bConfigurationValue);
3410141cc406Sopenharmony_ci	}
3411141cc406Sopenharmony_ci      DBG (5, "UsbDeviceSetConfiguration parameters: dh = %p, bConfigurationValue = %d\n",
3412141cc406Sopenharmony_ci               dh,pCfgDesc->bConfigurationValue);
3413141cc406Sopenharmony_ci      result = UsbDeviceSetConfiguration (dh,
3414141cc406Sopenharmony_ci				      pCfgDesc->bConfigurationValue);
3415141cc406Sopenharmony_ci      DBG (1, "sanei_usb_open: UsbDeviceSetConfiguration rc = %d\n",result);
3416141cc406Sopenharmony_ci      if (result)
3417141cc406Sopenharmony_ci	{
3418141cc406Sopenharmony_ci	  DBG (1, "sanei_usb_open: usbcalls complained on UsbDeviceSetConfiguration, rc= %d\n", result);
3419141cc406Sopenharmony_ci	  UsbClose (dh);
3420141cc406Sopenharmony_ci	  return SANE_STATUS_ACCESS_DENIED;
3421141cc406Sopenharmony_ci	}
3422141cc406Sopenharmony_ci
3423141cc406Sopenharmony_ci      /* Now we look for usable endpoints */
3424141cc406Sopenharmony_ci
3425141cc406Sopenharmony_ci      for (pDescHead = (struct usb_descriptor_header *) (pCurPtr+pCfgDesc->bLength);
3426141cc406Sopenharmony_ci            pDescHead;pDescHead = GetNextDescriptor(pDescHead,pEndPtr) )
3427141cc406Sopenharmony_ci	{
3428141cc406Sopenharmony_ci          switch(pDescHead->bDescriptorType)
3429141cc406Sopenharmony_ci          {
3430141cc406Sopenharmony_ci            case USB_DT_INTERFACE:
3431141cc406Sopenharmony_ci              interface = (struct usb_interface_descriptor *) pDescHead;
3432141cc406Sopenharmony_ci              DBG (5, "Found %d endpoints\n",interface->bNumEndpoints);
3433141cc406Sopenharmony_ci              DBG (5, "bAlternateSetting = %d\n",interface->bAlternateSetting);
3434141cc406Sopenharmony_ci              break;
3435141cc406Sopenharmony_ci            case USB_DT_ENDPOINT:
3436141cc406Sopenharmony_ci	      endpoint = (struct usb_endpoint_descriptor*)pDescHead;
3437141cc406Sopenharmony_ci	      direction = endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK;
3438141cc406Sopenharmony_ci	      transfer_type = endpoint->bmAttributes & USB_ENDPOINT_TYPE_MASK;
3439141cc406Sopenharmony_ci
3440141cc406Sopenharmony_ci              if (transfer_type == USB_ENDPOINT_TYPE_INTERRUPT ||
3441141cc406Sopenharmony_ci                  transfer_type == USB_ENDPOINT_TYPE_BULK)
3442141cc406Sopenharmony_ci                {
3443141cc406Sopenharmony_ci                  sanei_usb_add_endpoint(&devices[devcount], transfer_type,
3444141cc406Sopenharmony_ci                                         endpoint->bEndpointAddress, direction);
3445141cc406Sopenharmony_ci                }
3446141cc406Sopenharmony_ci	     /* ignore currently unsupported endpoints */
3447141cc406Sopenharmony_ci	     else {
3448141cc406Sopenharmony_ci	         DBG (5, "sanei_usb_open: ignoring %s-%s endpoint "
3449141cc406Sopenharmony_ci		      "(address: %d)\n",
3450141cc406Sopenharmony_ci                      sanei_usb_transfer_type_desc(transfer_type),
3451141cc406Sopenharmony_ci                      direction ? "in" : "out", address);
3452141cc406Sopenharmony_ci	         continue;
3453141cc406Sopenharmony_ci	          }
3454141cc406Sopenharmony_ci          break;
3455141cc406Sopenharmony_ci          }
3456141cc406Sopenharmony_ci        }
3457141cc406Sopenharmony_ci#else
3458141cc406Sopenharmony_ci      DBG (1, "sanei_usb_open: can't open device `%s': "
3459141cc406Sopenharmony_ci	   "usbcalls support missing\n", devname);
3460141cc406Sopenharmony_ci      return SANE_STATUS_UNSUPPORTED;
3461141cc406Sopenharmony_ci#endif /* HAVE_USBCALLS */
3462141cc406Sopenharmony_ci    }
3463141cc406Sopenharmony_ci  else
3464141cc406Sopenharmony_ci    {
3465141cc406Sopenharmony_ci      DBG (1, "sanei_usb_open: access method %d not implemented\n",
3466141cc406Sopenharmony_ci	   devices[devcount].method);
3467141cc406Sopenharmony_ci      return SANE_STATUS_INVAL;
3468141cc406Sopenharmony_ci    }
3469141cc406Sopenharmony_ci
3470141cc406Sopenharmony_ci  if (testing_mode == sanei_usb_testing_mode_record)
3471141cc406Sopenharmony_ci    {
3472141cc406Sopenharmony_ci#if WITH_USB_RECORD_REPLAY
3473141cc406Sopenharmony_ci      sanei_usb_record_open(devcount);
3474141cc406Sopenharmony_ci#else
3475141cc406Sopenharmony_ci      DBG (1, "USB record-replay mode support is missing\n");
3476141cc406Sopenharmony_ci      return SANE_STATUS_UNSUPPORTED;
3477141cc406Sopenharmony_ci#endif
3478141cc406Sopenharmony_ci    }
3479141cc406Sopenharmony_ci
3480141cc406Sopenharmony_ci  devices[devcount].open = SANE_TRUE;
3481141cc406Sopenharmony_ci  *dn = devcount;
3482141cc406Sopenharmony_ci  DBG (3, "sanei_usb_open: opened usb device `%s' (*dn=%d)\n",
3483141cc406Sopenharmony_ci       devname, devcount);
3484141cc406Sopenharmony_ci  return SANE_STATUS_GOOD;
3485141cc406Sopenharmony_ci}
3486141cc406Sopenharmony_ci
3487141cc406Sopenharmony_civoid
3488141cc406Sopenharmony_cisanei_usb_close (SANE_Int dn)
3489141cc406Sopenharmony_ci{
3490141cc406Sopenharmony_ci  char *env;
3491141cc406Sopenharmony_ci  int workaround = 0;
3492141cc406Sopenharmony_ci
3493141cc406Sopenharmony_ci  DBG (5, "sanei_usb_close: evaluating environment variable SANE_USB_WORKAROUND\n");
3494141cc406Sopenharmony_ci  env = getenv ("SANE_USB_WORKAROUND");
3495141cc406Sopenharmony_ci  if (env)
3496141cc406Sopenharmony_ci    {
3497141cc406Sopenharmony_ci      workaround = atoi(env);
3498141cc406Sopenharmony_ci      DBG (5, "sanei_usb_close: workaround: %d\n", workaround);
3499141cc406Sopenharmony_ci    }
3500141cc406Sopenharmony_ci
3501141cc406Sopenharmony_ci  DBG (5, "sanei_usb_close: closing device %d\n", dn);
3502141cc406Sopenharmony_ci  if (dn >= device_number || dn < 0)
3503141cc406Sopenharmony_ci    {
3504141cc406Sopenharmony_ci      DBG (1, "sanei_usb_close: dn >= device number || dn < 0\n");
3505141cc406Sopenharmony_ci      return;
3506141cc406Sopenharmony_ci    }
3507141cc406Sopenharmony_ci  if (!devices[dn].open)
3508141cc406Sopenharmony_ci    {
3509141cc406Sopenharmony_ci      DBG (1, "sanei_usb_close: device %d already closed or never opened\n",
3510141cc406Sopenharmony_ci	   dn);
3511141cc406Sopenharmony_ci      return;
3512141cc406Sopenharmony_ci    }
3513141cc406Sopenharmony_ci  if (testing_mode == sanei_usb_testing_mode_replay)
3514141cc406Sopenharmony_ci    {
3515141cc406Sopenharmony_ci      DBG (1, "sanei_usb_close: closing fake USB device\n");
3516141cc406Sopenharmony_ci    }
3517141cc406Sopenharmony_ci  else if (devices[dn].method == sanei_usb_method_scanner_driver)
3518141cc406Sopenharmony_ci    close (devices[dn].fd);
3519141cc406Sopenharmony_ci  else if (devices[dn].method == sanei_usb_method_usbcalls)
3520141cc406Sopenharmony_ci    {
3521141cc406Sopenharmony_ci#ifdef HAVE_USBCALLS
3522141cc406Sopenharmony_ci      int rc;
3523141cc406Sopenharmony_ci      rc=UsbClose (dh);
3524141cc406Sopenharmony_ci      DBG (5,"rc of UsbClose = %d\n",rc);
3525141cc406Sopenharmony_ci#else
3526141cc406Sopenharmony_ci    DBG (1, "sanei_usb_close: usbcalls support missing\n");
3527141cc406Sopenharmony_ci#endif
3528141cc406Sopenharmony_ci    }
3529141cc406Sopenharmony_ci  else
3530141cc406Sopenharmony_ci#ifdef HAVE_LIBUSB_LEGACY
3531141cc406Sopenharmony_ci    {
3532141cc406Sopenharmony_ci      /* This call seems to be required by Linux xhci driver
3533141cc406Sopenharmony_ci       * even though it should be a no-op. Without it, the
3534141cc406Sopenharmony_ci       * host or driver does not reset it's data toggle bit.
3535141cc406Sopenharmony_ci       * We intentionally ignore the return val */
3536141cc406Sopenharmony_ci      if (workaround)
3537141cc406Sopenharmony_ci        {
3538141cc406Sopenharmony_ci          sanei_usb_set_altinterface (dn, devices[dn].alt_setting);
3539141cc406Sopenharmony_ci        }
3540141cc406Sopenharmony_ci
3541141cc406Sopenharmony_ci      usb_release_interface (devices[dn].libusb_handle,
3542141cc406Sopenharmony_ci			     devices[dn].interface_nr);
3543141cc406Sopenharmony_ci      usb_close (devices[dn].libusb_handle);
3544141cc406Sopenharmony_ci    }
3545141cc406Sopenharmony_ci#elif defined(HAVE_LIBUSB)
3546141cc406Sopenharmony_ci    {
3547141cc406Sopenharmony_ci      /* This call seems to be required by Linux xhci driver
3548141cc406Sopenharmony_ci       * even though it should be a no-op. Without it, the
3549141cc406Sopenharmony_ci       * host or driver does not reset it's data toggle bit.
3550141cc406Sopenharmony_ci       * We intentionally ignore the return val */
3551141cc406Sopenharmony_ci      if (workaround)
3552141cc406Sopenharmony_ci        {
3553141cc406Sopenharmony_ci          sanei_usb_set_altinterface (dn, devices[dn].alt_setting);
3554141cc406Sopenharmony_ci        }
3555141cc406Sopenharmony_ci
3556141cc406Sopenharmony_ci      libusb_release_interface (devices[dn].lu_handle,
3557141cc406Sopenharmony_ci				devices[dn].interface_nr);
3558141cc406Sopenharmony_ci      libusb_close (devices[dn].lu_handle);
3559141cc406Sopenharmony_ci    }
3560141cc406Sopenharmony_ci#elif defined(HAVE_USB_MANAGER)
3561141cc406Sopenharmony_ci    {
3562141cc406Sopenharmony_ci      /* This call seems to be required by Linux xhci driver
3563141cc406Sopenharmony_ci       * even though it should be a no-op. Without it, the
3564141cc406Sopenharmony_ci       * host or driver does not reset it's data toggle bit.
3565141cc406Sopenharmony_ci       * We intentionally ignore the return val */
3566141cc406Sopenharmony_ci      if (workaround)
3567141cc406Sopenharmony_ci        {
3568141cc406Sopenharmony_ci          sanei_usb_set_altinterface (dn, devices[dn].alt_setting);
3569141cc406Sopenharmony_ci        }
3570141cc406Sopenharmony_ci
3571141cc406Sopenharmony_ci      usb_manager_release_interface (devices[dn].usb_manager_handle,
3572141cc406Sopenharmony_ci				devices[dn].interface_nr);
3573141cc406Sopenharmony_ci      usb_manager_close (devices[dn].usb_manager_handle);
3574141cc406Sopenharmony_ci    }
3575141cc406Sopenharmony_ci#else /* not HAVE_LIBUSB_LEGACY && not HAVE_LIBUSB && not HAVE_USB_MANAGER */
3576141cc406Sopenharmony_ci    DBG (1, "sanei_usb_close: libusb support missing\n");
3577141cc406Sopenharmony_ci#endif
3578141cc406Sopenharmony_ci  devices[dn].open = SANE_FALSE;
3579141cc406Sopenharmony_ci  return;
3580141cc406Sopenharmony_ci}
3581141cc406Sopenharmony_ci
3582141cc406Sopenharmony_civoid
3583141cc406Sopenharmony_cisanei_usb_set_timeout (SANE_Int __sane_unused__ timeout)
3584141cc406Sopenharmony_ci{
3585141cc406Sopenharmony_ci  if (testing_mode == sanei_usb_testing_mode_replay)
3586141cc406Sopenharmony_ci    return;
3587141cc406Sopenharmony_ci
3588141cc406Sopenharmony_ci#if defined(HAVE_LIBUSB_LEGACY) || defined(HAVE_LIBUSB)
3589141cc406Sopenharmony_ci  libusb_timeout = timeout;
3590141cc406Sopenharmony_ci#elif defined(HAVE_USB_MANAGER)
3591141cc406Sopenharmony_ci  usb_manager_timeout = timeout;
3592141cc406Sopenharmony_ci#else
3593141cc406Sopenharmony_ci  DBG (1, "sanei_usb_set_timeout: libusb support missing\n");
3594141cc406Sopenharmony_ci#endif /* HAVE_LIBUSB_LEGACY || HAVE_LIBUSB || HAVE_USB_MANAGER */
3595141cc406Sopenharmony_ci}
3596141cc406Sopenharmony_ci
3597141cc406Sopenharmony_ciSANE_Status
3598141cc406Sopenharmony_cisanei_usb_clear_halt (SANE_Int dn)
3599141cc406Sopenharmony_ci{
3600141cc406Sopenharmony_ci  char *env;
3601141cc406Sopenharmony_ci  int workaround = 0;
3602141cc406Sopenharmony_ci
3603141cc406Sopenharmony_ci  DBG (5, "sanei_usb_clear_halt: evaluating environment variable SANE_USB_WORKAROUND\n");
3604141cc406Sopenharmony_ci  env = getenv ("SANE_USB_WORKAROUND");
3605141cc406Sopenharmony_ci  if (env)
3606141cc406Sopenharmony_ci    {
3607141cc406Sopenharmony_ci      workaround = atoi(env);
3608141cc406Sopenharmony_ci      DBG (5, "sanei_usb_clear_halt: workaround: %d\n", workaround);
3609141cc406Sopenharmony_ci    }
3610141cc406Sopenharmony_ci
3611141cc406Sopenharmony_ci  if (dn >= device_number || dn < 0)
3612141cc406Sopenharmony_ci    {
3613141cc406Sopenharmony_ci      DBG (1, "sanei_usb_clear_halt: dn >= device number || dn < 0\n");
3614141cc406Sopenharmony_ci      return SANE_STATUS_INVAL;
3615141cc406Sopenharmony_ci    }
3616141cc406Sopenharmony_ci
3617141cc406Sopenharmony_ci  if (testing_mode == sanei_usb_testing_mode_replay)
3618141cc406Sopenharmony_ci    return SANE_STATUS_GOOD;
3619141cc406Sopenharmony_ci
3620141cc406Sopenharmony_ci#ifdef HAVE_LIBUSB_LEGACY
3621141cc406Sopenharmony_ci  int ret;
3622141cc406Sopenharmony_ci
3623141cc406Sopenharmony_ci  /* This call seems to be required by Linux xhci driver
3624141cc406Sopenharmony_ci   * even though it should be a no-op. Without it, the
3625141cc406Sopenharmony_ci   * host or driver does not send the clear to the device.
3626141cc406Sopenharmony_ci   * We intentionally ignore the return val */
3627141cc406Sopenharmony_ci  if (workaround)
3628141cc406Sopenharmony_ci    {
3629141cc406Sopenharmony_ci      sanei_usb_set_altinterface (dn, devices[dn].alt_setting);
3630141cc406Sopenharmony_ci    }
3631141cc406Sopenharmony_ci
3632141cc406Sopenharmony_ci  ret = usb_clear_halt (devices[dn].libusb_handle, devices[dn].bulk_in_ep);
3633141cc406Sopenharmony_ci  if (ret){
3634141cc406Sopenharmony_ci    DBG (1, "sanei_usb_clear_halt: BULK_IN ret=%d\n", ret);
3635141cc406Sopenharmony_ci    return SANE_STATUS_INVAL;
3636141cc406Sopenharmony_ci  }
3637141cc406Sopenharmony_ci
3638141cc406Sopenharmony_ci  ret = usb_clear_halt (devices[dn].libusb_handle, devices[dn].bulk_out_ep);
3639141cc406Sopenharmony_ci  if (ret){
3640141cc406Sopenharmony_ci    DBG (1, "sanei_usb_clear_halt: BULK_OUT ret=%d\n", ret);
3641141cc406Sopenharmony_ci    return SANE_STATUS_INVAL;
3642141cc406Sopenharmony_ci  }
3643141cc406Sopenharmony_ci
3644141cc406Sopenharmony_ci#elif defined(HAVE_LIBUSB)
3645141cc406Sopenharmony_ci  int ret;
3646141cc406Sopenharmony_ci
3647141cc406Sopenharmony_ci  /* This call seems to be required by Linux xhci driver
3648141cc406Sopenharmony_ci   * even though it should be a no-op. Without it, the
3649141cc406Sopenharmony_ci   * host or driver does not send the clear to the device.
3650141cc406Sopenharmony_ci   * We intentionally ignore the return val */
3651141cc406Sopenharmony_ci  if (workaround)
3652141cc406Sopenharmony_ci    {
3653141cc406Sopenharmony_ci      sanei_usb_set_altinterface (dn, devices[dn].alt_setting);
3654141cc406Sopenharmony_ci    }
3655141cc406Sopenharmony_ci
3656141cc406Sopenharmony_ci  ret = libusb_clear_halt (devices[dn].lu_handle, devices[dn].bulk_in_ep);
3657141cc406Sopenharmony_ci  if (ret){
3658141cc406Sopenharmony_ci    DBG (1, "sanei_usb_clear_halt: BULK_IN ret=%d\n", ret);
3659141cc406Sopenharmony_ci    return SANE_STATUS_INVAL;
3660141cc406Sopenharmony_ci  }
3661141cc406Sopenharmony_ci
3662141cc406Sopenharmony_ci  ret = libusb_clear_halt (devices[dn].lu_handle, devices[dn].bulk_out_ep);
3663141cc406Sopenharmony_ci  if (ret){
3664141cc406Sopenharmony_ci    DBG (1, "sanei_usb_clear_halt: BULK_OUT ret=%d\n", ret);
3665141cc406Sopenharmony_ci    return SANE_STATUS_INVAL;
3666141cc406Sopenharmony_ci  }
3667141cc406Sopenharmony_ci#elif defined(HAVE_USB_MANAGER)
3668141cc406Sopenharmony_ci  int ret;
3669141cc406Sopenharmony_ci
3670141cc406Sopenharmony_ci  /* This call seems to be required by Linux xhci driver
3671141cc406Sopenharmony_ci   * even though it should be a no-op. Without it, the
3672141cc406Sopenharmony_ci   * host or driver does not send the clear to the device.
3673141cc406Sopenharmony_ci   * We intentionally ignore the return val */
3674141cc406Sopenharmony_ci  if (workaround)
3675141cc406Sopenharmony_ci    {
3676141cc406Sopenharmony_ci      sanei_usb_set_altinterface (dn, devices[dn].alt_setting);
3677141cc406Sopenharmony_ci    }
3678141cc406Sopenharmony_ci
3679141cc406Sopenharmony_ci  ret = usb_manager_clear_halt (devices[dn].usb_manager_handle, devices[dn].bulk_in_ep);
3680141cc406Sopenharmony_ci  if (ret){
3681141cc406Sopenharmony_ci    DBG (1, "sanei_usb_clear_halt: BULK_IN ret=%d\n", ret);
3682141cc406Sopenharmony_ci    return SANE_STATUS_INVAL;
3683141cc406Sopenharmony_ci  }
3684141cc406Sopenharmony_ci
3685141cc406Sopenharmony_ci  ret = usb_manager_clear_halt (devices[dn].usb_manager_handle, devices[dn].bulk_out_ep);
3686141cc406Sopenharmony_ci  if (ret){
3687141cc406Sopenharmony_ci    DBG (1, "sanei_usb_clear_halt: BULK_OUT ret=%d\n", ret);
3688141cc406Sopenharmony_ci    return SANE_STATUS_INVAL;
3689141cc406Sopenharmony_ci  }
3690141cc406Sopenharmony_ci#else /* not HAVE_LIBUSB_LEGACY && not HAVE_LIBUSB && not HAVE_USB_MANAGER */
3691141cc406Sopenharmony_ci  DBG (1, "sanei_usb_clear_halt: libusb support missing\n");
3692141cc406Sopenharmony_ci#endif /* HAVE_LIBUSB_LEGACY || HAVE_LIBUSB || HAVE_USB_MANAGER */
3693141cc406Sopenharmony_ci
3694141cc406Sopenharmony_ci  return SANE_STATUS_GOOD;
3695141cc406Sopenharmony_ci}
3696141cc406Sopenharmony_ci
3697141cc406Sopenharmony_ciSANE_Status
3698141cc406Sopenharmony_cisanei_usb_reset (SANE_Int __sane_unused__ dn)
3699141cc406Sopenharmony_ci{
3700141cc406Sopenharmony_ci  if (testing_mode == sanei_usb_testing_mode_replay)
3701141cc406Sopenharmony_ci    return SANE_STATUS_GOOD;
3702141cc406Sopenharmony_ci
3703141cc406Sopenharmony_ci#ifdef HAVE_LIBUSB_LEGACY
3704141cc406Sopenharmony_ci  int ret;
3705141cc406Sopenharmony_ci
3706141cc406Sopenharmony_ci  ret = usb_reset (devices[dn].libusb_handle);
3707141cc406Sopenharmony_ci  if (ret){
3708141cc406Sopenharmony_ci    DBG (1, "sanei_usb_reset: ret=%d\n", ret);
3709141cc406Sopenharmony_ci    return SANE_STATUS_INVAL;
3710141cc406Sopenharmony_ci  }
3711141cc406Sopenharmony_ci
3712141cc406Sopenharmony_ci#elif defined(HAVE_LIBUSB)
3713141cc406Sopenharmony_ci  int ret;
3714141cc406Sopenharmony_ci
3715141cc406Sopenharmony_ci  ret = libusb_reset_device (devices[dn].lu_handle);
3716141cc406Sopenharmony_ci  if (ret){
3717141cc406Sopenharmony_ci    DBG (1, "sanei_usb_reset: ret=%d\n", ret);
3718141cc406Sopenharmony_ci    return SANE_STATUS_INVAL;
3719141cc406Sopenharmony_ci  }
3720141cc406Sopenharmony_ci#elif defined(HAVE_USB_MANAGER)
3721141cc406Sopenharmony_ci  int ret;
3722141cc406Sopenharmony_ci
3723141cc406Sopenharmony_ci  ret = usb_manager_reset_device (devices[dn].usb_manager_handle);
3724141cc406Sopenharmony_ci  if (ret){
3725141cc406Sopenharmony_ci    DBG (1, "sanei_usb_reset: ret=%d\n", ret);
3726141cc406Sopenharmony_ci    return SANE_STATUS_INVAL;
3727141cc406Sopenharmony_ci  }
3728141cc406Sopenharmony_ci
3729141cc406Sopenharmony_ci#else /* not HAVE_LIBUSB_LEGACY && not HAVE_LIBUSB && not HAVE_USB_MANAGER */
3730141cc406Sopenharmony_ci  DBG (1, "sanei_usb_reset: libusb support missing\n");
3731141cc406Sopenharmony_ci#endif /* HAVE_LIBUSB_LEGACY || HAVE_LIBUSB || HAVE_USB_MANAGER */
3732141cc406Sopenharmony_ci
3733141cc406Sopenharmony_ci  return SANE_STATUS_GOOD;
3734141cc406Sopenharmony_ci}
3735141cc406Sopenharmony_ci
3736141cc406Sopenharmony_ci#if WITH_USB_RECORD_REPLAY
3737141cc406Sopenharmony_ci// returns non-negative value on success, -1 on failure
3738141cc406Sopenharmony_cistatic int sanei_usb_replay_next_read_bulk_packet_size(SANE_Int dn)
3739141cc406Sopenharmony_ci{
3740141cc406Sopenharmony_ci  xmlNode* node = sanei_xml_peek_next_tx_node();
3741141cc406Sopenharmony_ci  if (node == NULL)
3742141cc406Sopenharmony_ci    return -1;
3743141cc406Sopenharmony_ci
3744141cc406Sopenharmony_ci  if (xmlStrcmp(node->name, (const xmlChar*)"bulk_tx") != 0)
3745141cc406Sopenharmony_ci    {
3746141cc406Sopenharmony_ci      return -1;
3747141cc406Sopenharmony_ci    }
3748141cc406Sopenharmony_ci
3749141cc406Sopenharmony_ci  if (!sanei_usb_attr_is(node, "direction", "IN"))
3750141cc406Sopenharmony_ci    return -1;
3751141cc406Sopenharmony_ci  if (!sanei_usb_attr_is_uint(node, "endpoint_number",
3752141cc406Sopenharmony_ci                              devices[dn].bulk_in_ep & 0x0f))
3753141cc406Sopenharmony_ci    return -1;
3754141cc406Sopenharmony_ci
3755141cc406Sopenharmony_ci  size_t got_size = 0;
3756141cc406Sopenharmony_ci  char* got_data = sanei_xml_get_hex_data(node, &got_size);
3757141cc406Sopenharmony_ci  free(got_data);
3758141cc406Sopenharmony_ci  return got_size;
3759141cc406Sopenharmony_ci}
3760141cc406Sopenharmony_ci
3761141cc406Sopenharmony_cistatic void sanei_usb_record_read_bulk(xmlNode* node, SANE_Int dn,
3762141cc406Sopenharmony_ci                                       SANE_Byte* buffer,
3763141cc406Sopenharmony_ci                                       size_t size, ssize_t read_size)
3764141cc406Sopenharmony_ci{
3765141cc406Sopenharmony_ci  int node_was_null = node == NULL;
3766141cc406Sopenharmony_ci  if (node_was_null)
3767141cc406Sopenharmony_ci    node = testing_append_commands_node;
3768141cc406Sopenharmony_ci
3769141cc406Sopenharmony_ci  xmlNode* e_tx = xmlNewNode(NULL, (const xmlChar*)"bulk_tx");
3770141cc406Sopenharmony_ci  sanei_xml_command_common_props(e_tx, devices[dn].bulk_in_ep & 0x0f, "IN");
3771141cc406Sopenharmony_ci
3772141cc406Sopenharmony_ci  if (buffer == NULL)
3773141cc406Sopenharmony_ci    {
3774141cc406Sopenharmony_ci      const int buf_size = 128;
3775141cc406Sopenharmony_ci      char buf[buf_size];
3776141cc406Sopenharmony_ci      snprintf(buf, buf_size, "(unknown read of allowed size %ld)", size);
3777141cc406Sopenharmony_ci      xmlNode* e_content = xmlNewText((const xmlChar*)buf);
3778141cc406Sopenharmony_ci      xmlAddChild(e_tx, e_content);
3779141cc406Sopenharmony_ci    }
3780141cc406Sopenharmony_ci  else
3781141cc406Sopenharmony_ci    {
3782141cc406Sopenharmony_ci      if (read_size >= 0)
3783141cc406Sopenharmony_ci        {
3784141cc406Sopenharmony_ci          sanei_xml_set_hex_data(e_tx, (const char*)buffer, read_size);
3785141cc406Sopenharmony_ci        }
3786141cc406Sopenharmony_ci      else
3787141cc406Sopenharmony_ci        {
3788141cc406Sopenharmony_ci          xmlNewProp(e_tx, (const xmlChar*)"error", (const xmlChar*)"timeout");
3789141cc406Sopenharmony_ci        }
3790141cc406Sopenharmony_ci    }
3791141cc406Sopenharmony_ci
3792141cc406Sopenharmony_ci  node = sanei_xml_append_command(node, node_was_null, e_tx);
3793141cc406Sopenharmony_ci
3794141cc406Sopenharmony_ci  if (node_was_null)
3795141cc406Sopenharmony_ci    testing_append_commands_node = node;
3796141cc406Sopenharmony_ci}
3797141cc406Sopenharmony_ci
3798141cc406Sopenharmony_cistatic void sanei_usb_record_replace_read_bulk(xmlNode* node, SANE_Int dn,
3799141cc406Sopenharmony_ci                                               SANE_Byte* buffer,
3800141cc406Sopenharmony_ci                                               size_t size, size_t read_size)
3801141cc406Sopenharmony_ci{
3802141cc406Sopenharmony_ci  if (!testing_development_mode)
3803141cc406Sopenharmony_ci    return;
3804141cc406Sopenharmony_ci  testing_known_commands_input_failed = 1;
3805141cc406Sopenharmony_ci  testing_last_known_seq--;
3806141cc406Sopenharmony_ci  sanei_usb_record_read_bulk(node, dn, buffer, size, read_size);
3807141cc406Sopenharmony_ci  xmlUnlinkNode(node);
3808141cc406Sopenharmony_ci  xmlFreeNode(node);
3809141cc406Sopenharmony_ci}
3810141cc406Sopenharmony_ci
3811141cc406Sopenharmony_cistatic int sanei_usb_replay_read_bulk(SANE_Int dn, SANE_Byte* buffer,
3812141cc406Sopenharmony_ci                                      size_t size)
3813141cc406Sopenharmony_ci{
3814141cc406Sopenharmony_ci  // libusb may potentially combine multiple IN packets into a single transfer.
3815141cc406Sopenharmony_ci  // We recontruct that by looking into the next packet. If it can be
3816141cc406Sopenharmony_ci  // included into the current transfer without
3817141cc406Sopenharmony_ci  size_t wanted_size = size;
3818141cc406Sopenharmony_ci  size_t total_got_size = 0;
3819141cc406Sopenharmony_ci  while (wanted_size > 0)
3820141cc406Sopenharmony_ci    {
3821141cc406Sopenharmony_ci      if (testing_known_commands_input_failed)
3822141cc406Sopenharmony_ci        return -1;
3823141cc406Sopenharmony_ci
3824141cc406Sopenharmony_ci      xmlNode* node = sanei_xml_get_next_tx_node();
3825141cc406Sopenharmony_ci      if (node == NULL)
3826141cc406Sopenharmony_ci        {
3827141cc406Sopenharmony_ci          FAIL_TEST(__func__, "no more transactions\n");
3828141cc406Sopenharmony_ci          return -1;
3829141cc406Sopenharmony_ci        }
3830141cc406Sopenharmony_ci
3831141cc406Sopenharmony_ci      if (sanei_xml_is_known_commands_end(node))
3832141cc406Sopenharmony_ci        {
3833141cc406Sopenharmony_ci          sanei_usb_record_read_bulk(NULL, dn, NULL, 0, size);
3834141cc406Sopenharmony_ci          testing_known_commands_input_failed = 1;
3835141cc406Sopenharmony_ci          return -1;
3836141cc406Sopenharmony_ci        }
3837141cc406Sopenharmony_ci
3838141cc406Sopenharmony_ci      sanei_xml_record_seq(node);
3839141cc406Sopenharmony_ci      sanei_xml_break_if_needed(node);
3840141cc406Sopenharmony_ci
3841141cc406Sopenharmony_ci      if (xmlStrcmp(node->name, (const xmlChar*)"bulk_tx") != 0)
3842141cc406Sopenharmony_ci        {
3843141cc406Sopenharmony_ci          FAIL_TEST_TX(__func__, node, "unexpected transaction type %s\n",
3844141cc406Sopenharmony_ci                       (const char*) node->name);
3845141cc406Sopenharmony_ci          sanei_usb_record_replace_read_bulk(node, dn, NULL, 0, wanted_size);
3846141cc406Sopenharmony_ci          return -1;
3847141cc406Sopenharmony_ci        }
3848141cc406Sopenharmony_ci
3849141cc406Sopenharmony_ci      if (!sanei_usb_check_attr(node, "direction", "IN", __func__))
3850141cc406Sopenharmony_ci        {
3851141cc406Sopenharmony_ci          sanei_usb_record_replace_read_bulk(node, dn, NULL, 0, wanted_size);
3852141cc406Sopenharmony_ci          return -1;
3853141cc406Sopenharmony_ci        }
3854141cc406Sopenharmony_ci      if (!sanei_usb_check_attr_uint(node, "endpoint_number",
3855141cc406Sopenharmony_ci                                     devices[dn].bulk_in_ep & 0x0f,
3856141cc406Sopenharmony_ci                                     __func__))
3857141cc406Sopenharmony_ci        {
3858141cc406Sopenharmony_ci          sanei_usb_record_replace_read_bulk(node, dn, NULL, 0, wanted_size);
3859141cc406Sopenharmony_ci          return -1;
3860141cc406Sopenharmony_ci        }
3861141cc406Sopenharmony_ci
3862141cc406Sopenharmony_ci      size_t got_size = 0;
3863141cc406Sopenharmony_ci      char* got_data = sanei_xml_get_hex_data(node, &got_size);
3864141cc406Sopenharmony_ci
3865141cc406Sopenharmony_ci      if (got_size > wanted_size)
3866141cc406Sopenharmony_ci        {
3867141cc406Sopenharmony_ci          FAIL_TEST_TX(__func__, node,
3868141cc406Sopenharmony_ci                       "got more data than wanted (%lu vs %lu)\n",
3869141cc406Sopenharmony_ci                       got_size, wanted_size);
3870141cc406Sopenharmony_ci          free(got_data);
3871141cc406Sopenharmony_ci          sanei_usb_record_replace_read_bulk(node, dn, NULL, 0, wanted_size);
3872141cc406Sopenharmony_ci          return -1;
3873141cc406Sopenharmony_ci        }
3874141cc406Sopenharmony_ci
3875141cc406Sopenharmony_ci      memcpy(buffer + total_got_size, got_data, got_size);
3876141cc406Sopenharmony_ci      free(got_data);
3877141cc406Sopenharmony_ci      total_got_size += got_size;
3878141cc406Sopenharmony_ci      wanted_size -= got_size;
3879141cc406Sopenharmony_ci
3880141cc406Sopenharmony_ci      int next_size = sanei_usb_replay_next_read_bulk_packet_size(dn);
3881141cc406Sopenharmony_ci      if (next_size < 0)
3882141cc406Sopenharmony_ci        return total_got_size;
3883141cc406Sopenharmony_ci      if ((size_t) next_size > wanted_size)
3884141cc406Sopenharmony_ci        return total_got_size;
3885141cc406Sopenharmony_ci    }
3886141cc406Sopenharmony_ci  return total_got_size;
3887141cc406Sopenharmony_ci}
3888141cc406Sopenharmony_ci#endif // WITH_USB_RECORD_REPLAY
3889141cc406Sopenharmony_ci
3890141cc406Sopenharmony_ciSANE_Status
3891141cc406Sopenharmony_cisanei_usb_read_bulk (SANE_Int dn, SANE_Byte * buffer, size_t * size)
3892141cc406Sopenharmony_ci{
3893141cc406Sopenharmony_ci  ssize_t read_size = 0;
3894141cc406Sopenharmony_ci
3895141cc406Sopenharmony_ci  if (!size)
3896141cc406Sopenharmony_ci    {
3897141cc406Sopenharmony_ci      DBG (1, "sanei_usb_read_bulk: size == NULL\n");
3898141cc406Sopenharmony_ci      return SANE_STATUS_INVAL;
3899141cc406Sopenharmony_ci    }
3900141cc406Sopenharmony_ci
3901141cc406Sopenharmony_ci  if (dn >= device_number || dn < 0)
3902141cc406Sopenharmony_ci    {
3903141cc406Sopenharmony_ci      DBG (1, "sanei_usb_read_bulk: dn >= device number || dn < 0\n");
3904141cc406Sopenharmony_ci      return SANE_STATUS_INVAL;
3905141cc406Sopenharmony_ci    }
3906141cc406Sopenharmony_ci  DBG (5, "sanei_usb_read_bulk: trying to read %lu bytes\n",
3907141cc406Sopenharmony_ci       (unsigned long) *size);
3908141cc406Sopenharmony_ci
3909141cc406Sopenharmony_ci  if (testing_mode == sanei_usb_testing_mode_replay)
3910141cc406Sopenharmony_ci    {
3911141cc406Sopenharmony_ci#if WITH_USB_RECORD_REPLAY
3912141cc406Sopenharmony_ci      read_size = sanei_usb_replay_read_bulk(dn, buffer, *size);
3913141cc406Sopenharmony_ci#else
3914141cc406Sopenharmony_ci      DBG(1, "%s: USB record-replay mode support missing\n", __func__);
3915141cc406Sopenharmony_ci      return SANE_STATUS_UNSUPPORTED;
3916141cc406Sopenharmony_ci#endif
3917141cc406Sopenharmony_ci    }
3918141cc406Sopenharmony_ci  else if (devices[dn].method == sanei_usb_method_scanner_driver)
3919141cc406Sopenharmony_ci    {
3920141cc406Sopenharmony_ci      read_size = read (devices[dn].fd, buffer, *size);
3921141cc406Sopenharmony_ci
3922141cc406Sopenharmony_ci      if (read_size < 0)
3923141cc406Sopenharmony_ci	DBG (1, "sanei_usb_read_bulk: read failed: %s\n",
3924141cc406Sopenharmony_ci	     strerror (errno));
3925141cc406Sopenharmony_ci    }
3926141cc406Sopenharmony_ci  else if (devices[dn].method == sanei_usb_method_libusb)
3927141cc406Sopenharmony_ci#ifdef HAVE_LIBUSB_LEGACY
3928141cc406Sopenharmony_ci    {
3929141cc406Sopenharmony_ci      if (devices[dn].bulk_in_ep)
3930141cc406Sopenharmony_ci	{
3931141cc406Sopenharmony_ci	  read_size = usb_bulk_read (devices[dn].libusb_handle,
3932141cc406Sopenharmony_ci				     devices[dn].bulk_in_ep, (char *) buffer,
3933141cc406Sopenharmony_ci				     (int) *size, libusb_timeout);
3934141cc406Sopenharmony_ci
3935141cc406Sopenharmony_ci	  if (read_size < 0)
3936141cc406Sopenharmony_ci	    DBG (1, "sanei_usb_read_bulk: read failed: %s\n",
3937141cc406Sopenharmony_ci		 strerror (errno));
3938141cc406Sopenharmony_ci	}
3939141cc406Sopenharmony_ci      else
3940141cc406Sopenharmony_ci	{
3941141cc406Sopenharmony_ci	  DBG (1, "sanei_usb_read_bulk: can't read without a bulk-in "
3942141cc406Sopenharmony_ci	       "endpoint\n");
3943141cc406Sopenharmony_ci	  return SANE_STATUS_INVAL;
3944141cc406Sopenharmony_ci	}
3945141cc406Sopenharmony_ci    }
3946141cc406Sopenharmony_ci#elif defined(HAVE_LIBUSB)
3947141cc406Sopenharmony_ci    {
3948141cc406Sopenharmony_ci      if (devices[dn].bulk_in_ep)
3949141cc406Sopenharmony_ci	{
3950141cc406Sopenharmony_ci	  int ret, rsize;
3951141cc406Sopenharmony_ci	  ret = libusb_bulk_transfer (devices[dn].lu_handle,
3952141cc406Sopenharmony_ci				      devices[dn].bulk_in_ep, buffer,
3953141cc406Sopenharmony_ci				      (int) *size, &rsize,
3954141cc406Sopenharmony_ci				      libusb_timeout);
3955141cc406Sopenharmony_ci
3956141cc406Sopenharmony_ci	  if (ret < 0)
3957141cc406Sopenharmony_ci	    {
3958141cc406Sopenharmony_ci              DBG (1, "sanei_usb_read_bulk: read failed (still got %d bytes): %s\n",
3959141cc406Sopenharmony_ci                   rsize, sanei_libusb_strerror (ret));
3960141cc406Sopenharmony_ci
3961141cc406Sopenharmony_ci	      read_size = -1;
3962141cc406Sopenharmony_ci	    }
3963141cc406Sopenharmony_ci	  else
3964141cc406Sopenharmony_ci	    {
3965141cc406Sopenharmony_ci	      read_size = rsize;
3966141cc406Sopenharmony_ci	    }
3967141cc406Sopenharmony_ci	}
3968141cc406Sopenharmony_ci      else
3969141cc406Sopenharmony_ci	{
3970141cc406Sopenharmony_ci	  DBG (1, "sanei_usb_read_bulk: can't read without a bulk-in "
3971141cc406Sopenharmony_ci	       "endpoint\n");
3972141cc406Sopenharmony_ci	  return SANE_STATUS_INVAL;
3973141cc406Sopenharmony_ci	}
3974141cc406Sopenharmony_ci    }
3975141cc406Sopenharmony_ci#elif defined(HAVE_USB_MANAGER)
3976141cc406Sopenharmony_ci    {
3977141cc406Sopenharmony_ci      if (devices[dn].bulk_in_ep)
3978141cc406Sopenharmony_ci	{
3979141cc406Sopenharmony_ci	  int ret, rsize;
3980141cc406Sopenharmony_ci	  ret = usb_manager_bulk_transfer (devices[dn].usb_manager_handle,
3981141cc406Sopenharmony_ci				      devices[dn].bulk_in_ep, buffer,
3982141cc406Sopenharmony_ci				      (int) *size, &rsize,
3983141cc406Sopenharmony_ci				      usb_manager_timeout);
3984141cc406Sopenharmony_ci
3985141cc406Sopenharmony_ci	  if (ret < 0)
3986141cc406Sopenharmony_ci	    {
3987141cc406Sopenharmony_ci              DBG (1, "sanei_usb_read_bulk: read failed (still got %d bytes): %s\n",
3988141cc406Sopenharmony_ci                   rsize, sanei_usb_manager_strerror (ret));
3989141cc406Sopenharmony_ci
3990141cc406Sopenharmony_ci	      read_size = -1;
3991141cc406Sopenharmony_ci	    }
3992141cc406Sopenharmony_ci	  else
3993141cc406Sopenharmony_ci	    {
3994141cc406Sopenharmony_ci	      read_size = rsize;
3995141cc406Sopenharmony_ci	    }
3996141cc406Sopenharmony_ci	}
3997141cc406Sopenharmony_ci      else
3998141cc406Sopenharmony_ci	{
3999141cc406Sopenharmony_ci	  DBG (1, "sanei_usb_read_bulk: can't read without a bulk-in "
4000141cc406Sopenharmony_ci	       "endpoint\n");
4001141cc406Sopenharmony_ci	  return SANE_STATUS_INVAL;
4002141cc406Sopenharmony_ci	}
4003141cc406Sopenharmony_ci    }
4004141cc406Sopenharmony_ci#else /* not HAVE_LIBUSB_LEGACY && not HAVE_LIBUSB && not HAVE_USB_MANAGER */
4005141cc406Sopenharmony_ci    {
4006141cc406Sopenharmony_ci      DBG (1, "sanei_usb_read_bulk: libusb support missing\n");
4007141cc406Sopenharmony_ci      return SANE_STATUS_UNSUPPORTED;
4008141cc406Sopenharmony_ci    }
4009141cc406Sopenharmony_ci#endif /* not HAVE_LIBUSB_LEGACY */
4010141cc406Sopenharmony_ci  else if (devices[dn].method == sanei_usb_method_usbcalls)
4011141cc406Sopenharmony_ci  {
4012141cc406Sopenharmony_ci#ifdef HAVE_USBCALLS
4013141cc406Sopenharmony_ci    int rc;
4014141cc406Sopenharmony_ci    char* buffer_ptr = (char*) buffer;
4015141cc406Sopenharmony_ci    size_t requested_size = *size;
4016141cc406Sopenharmony_ci    while (requested_size)
4017141cc406Sopenharmony_ci    {
4018141cc406Sopenharmony_ci      ULONG ulToRead = (requested_size>MAX_RW)?MAX_RW:requested_size;
4019141cc406Sopenharmony_ci      ULONG ulNum = ulToRead;
4020141cc406Sopenharmony_ci      DBG (5, "Entered usbcalls UsbBulkRead with dn = %d\n",dn);
4021141cc406Sopenharmony_ci      DBG (5, "Entered usbcalls UsbBulkRead with dh = %p\n",dh);
4022141cc406Sopenharmony_ci      DBG (5, "Entered usbcalls UsbBulkRead with bulk_in_ep = 0x%02x\n",devices[dn].bulk_in_ep);
4023141cc406Sopenharmony_ci      DBG (5, "Entered usbcalls UsbBulkRead with interface_nr = %d\n",devices[dn].interface_nr);
4024141cc406Sopenharmony_ci      DBG (5, "Entered usbcalls UsbBulkRead with usbcalls_timeout = %d\n",usbcalls_timeout);
4025141cc406Sopenharmony_ci
4026141cc406Sopenharmony_ci      if (devices[dn].bulk_in_ep){
4027141cc406Sopenharmony_ci        rc = UsbBulkRead (dh, devices[dn].bulk_in_ep, devices[dn].interface_nr,
4028141cc406Sopenharmony_ci                               &ulToRead, buffer_ptr, usbcalls_timeout);
4029141cc406Sopenharmony_ci        DBG (1, "sanei_usb_read_bulk: rc = %d\n",rc);}
4030141cc406Sopenharmony_ci      else
4031141cc406Sopenharmony_ci      {
4032141cc406Sopenharmony_ci          DBG (1, "sanei_usb_read_bulk: can't read without a bulk-in endpoint\n");
4033141cc406Sopenharmony_ci          return SANE_STATUS_INVAL;
4034141cc406Sopenharmony_ci      }
4035141cc406Sopenharmony_ci      if (rc || (ulNum!=ulToRead)) return SANE_STATUS_INVAL;
4036141cc406Sopenharmony_ci      requested_size -=ulToRead;
4037141cc406Sopenharmony_ci      buffer_ptr += ulToRead;
4038141cc406Sopenharmony_ci      read_size += ulToRead;
4039141cc406Sopenharmony_ci    }
4040141cc406Sopenharmony_ci#else /* not HAVE_USBCALLS */
4041141cc406Sopenharmony_ci    {
4042141cc406Sopenharmony_ci      DBG (1, "sanei_usb_read_bulk: usbcalls support missing\n");
4043141cc406Sopenharmony_ci      return SANE_STATUS_UNSUPPORTED;
4044141cc406Sopenharmony_ci    }
4045141cc406Sopenharmony_ci#endif /* not HAVE_USBCALLS */
4046141cc406Sopenharmony_ci  }
4047141cc406Sopenharmony_ci  else
4048141cc406Sopenharmony_ci    {
4049141cc406Sopenharmony_ci      DBG (1, "sanei_usb_read_bulk: access method %d not implemented\n",
4050141cc406Sopenharmony_ci	   devices[dn].method);
4051141cc406Sopenharmony_ci      return SANE_STATUS_INVAL;
4052141cc406Sopenharmony_ci    }
4053141cc406Sopenharmony_ci
4054141cc406Sopenharmony_ci  if (testing_mode == sanei_usb_testing_mode_record)
4055141cc406Sopenharmony_ci    {
4056141cc406Sopenharmony_ci#if WITH_USB_RECORD_REPLAY
4057141cc406Sopenharmony_ci      sanei_usb_record_read_bulk(NULL, dn, buffer, *size, read_size);
4058141cc406Sopenharmony_ci#else
4059141cc406Sopenharmony_ci      DBG (1, "USB record-replay mode support is missing\n");
4060141cc406Sopenharmony_ci      return SANE_STATUS_UNSUPPORTED;
4061141cc406Sopenharmony_ci#endif
4062141cc406Sopenharmony_ci    }
4063141cc406Sopenharmony_ci
4064141cc406Sopenharmony_ci  if (read_size < 0)
4065141cc406Sopenharmony_ci    {
4066141cc406Sopenharmony_ci      *size = 0;
4067141cc406Sopenharmony_ci      if (testing_mode != sanei_usb_testing_mode_disabled)
4068141cc406Sopenharmony_ci        return SANE_STATUS_IO_ERROR;
4069141cc406Sopenharmony_ci
4070141cc406Sopenharmony_ci#ifdef HAVE_LIBUSB_LEGACY
4071141cc406Sopenharmony_ci      if (devices[dn].method == sanei_usb_method_libusb)
4072141cc406Sopenharmony_ci	usb_clear_halt (devices[dn].libusb_handle, devices[dn].bulk_in_ep);
4073141cc406Sopenharmony_ci#elif defined(HAVE_LIBUSB)
4074141cc406Sopenharmony_ci      if (devices[dn].method == sanei_usb_method_libusb)
4075141cc406Sopenharmony_ci	libusb_clear_halt (devices[dn].lu_handle, devices[dn].bulk_in_ep);
4076141cc406Sopenharmony_ci#elif defined(HAVE_USB_MANAGER)
4077141cc406Sopenharmony_ci      if (devices[dn].method == sanei_usb_method_usb_manager)
4078141cc406Sopenharmony_ci	usb_manager_clear_halt (devices[dn].usb_manager_handle, devices[dn].bulk_in_ep);
4079141cc406Sopenharmony_ci#endif
4080141cc406Sopenharmony_ci      return SANE_STATUS_IO_ERROR;
4081141cc406Sopenharmony_ci    }
4082141cc406Sopenharmony_ci  if (read_size == 0)
4083141cc406Sopenharmony_ci    {
4084141cc406Sopenharmony_ci      DBG (3, "sanei_usb_read_bulk: read returned EOF\n");
4085141cc406Sopenharmony_ci      *size = 0;
4086141cc406Sopenharmony_ci      return SANE_STATUS_EOF;
4087141cc406Sopenharmony_ci    }
4088141cc406Sopenharmony_ci  if (debug_level > 10)
4089141cc406Sopenharmony_ci    print_buffer (buffer, read_size);
4090141cc406Sopenharmony_ci  DBG (5, "sanei_usb_read_bulk: wanted %lu bytes, got %ld bytes\n",
4091141cc406Sopenharmony_ci       (unsigned long) *size, (unsigned long) read_size);
4092141cc406Sopenharmony_ci  *size = read_size;
4093141cc406Sopenharmony_ci
4094141cc406Sopenharmony_ci  return SANE_STATUS_GOOD;
4095141cc406Sopenharmony_ci}
4096141cc406Sopenharmony_ci
4097141cc406Sopenharmony_ci#if WITH_USB_RECORD_REPLAY
4098141cc406Sopenharmony_cistatic int sanei_usb_record_write_bulk(xmlNode* node, SANE_Int dn,
4099141cc406Sopenharmony_ci                                       const SANE_Byte* buffer,
4100141cc406Sopenharmony_ci                                       size_t size, size_t write_size)
4101141cc406Sopenharmony_ci{
4102141cc406Sopenharmony_ci  int node_was_null = node == NULL;
4103141cc406Sopenharmony_ci  if (node_was_null)
4104141cc406Sopenharmony_ci    node = testing_append_commands_node;
4105141cc406Sopenharmony_ci
4106141cc406Sopenharmony_ci  xmlNode* e_tx = xmlNewNode(NULL, (const xmlChar*)"bulk_tx");
4107141cc406Sopenharmony_ci  sanei_xml_command_common_props(e_tx, devices[dn].bulk_out_ep & 0x0f, "OUT");
4108141cc406Sopenharmony_ci  sanei_xml_set_hex_data(e_tx, (const char*)buffer, size);
4109141cc406Sopenharmony_ci  // FIXME: output write_size
4110141cc406Sopenharmony_ci
4111141cc406Sopenharmony_ci  node = sanei_xml_append_command(node, node_was_null, e_tx);
4112141cc406Sopenharmony_ci
4113141cc406Sopenharmony_ci  if (node_was_null)
4114141cc406Sopenharmony_ci    testing_append_commands_node = node;
4115141cc406Sopenharmony_ci  return write_size;
4116141cc406Sopenharmony_ci}
4117141cc406Sopenharmony_ci
4118141cc406Sopenharmony_cistatic void sanei_usb_record_replace_write_bulk(xmlNode* node, SANE_Int dn,
4119141cc406Sopenharmony_ci                                                const SANE_Byte* buffer,
4120141cc406Sopenharmony_ci                                                size_t size, size_t write_size)
4121141cc406Sopenharmony_ci{
4122141cc406Sopenharmony_ci  if (!testing_development_mode)
4123141cc406Sopenharmony_ci    return;
4124141cc406Sopenharmony_ci  testing_last_known_seq--;
4125141cc406Sopenharmony_ci  sanei_usb_record_write_bulk(node, dn, buffer, size, write_size);
4126141cc406Sopenharmony_ci  xmlUnlinkNode(node);
4127141cc406Sopenharmony_ci  xmlFreeNode(node);
4128141cc406Sopenharmony_ci}
4129141cc406Sopenharmony_ci
4130141cc406Sopenharmony_ci// returns non-negative value on success, -1 on failure
4131141cc406Sopenharmony_cistatic int sanei_usb_replay_next_write_bulk_packet_size(SANE_Int dn)
4132141cc406Sopenharmony_ci{
4133141cc406Sopenharmony_ci  xmlNode* node = sanei_xml_peek_next_tx_node();
4134141cc406Sopenharmony_ci  if (node == NULL)
4135141cc406Sopenharmony_ci    return -1;
4136141cc406Sopenharmony_ci
4137141cc406Sopenharmony_ci  if (xmlStrcmp(node->name, (const xmlChar*)"bulk_tx") != 0)
4138141cc406Sopenharmony_ci    {
4139141cc406Sopenharmony_ci      return -1;
4140141cc406Sopenharmony_ci    }
4141141cc406Sopenharmony_ci
4142141cc406Sopenharmony_ci  if (!sanei_usb_attr_is(node, "direction", "OUT"))
4143141cc406Sopenharmony_ci    return -1;
4144141cc406Sopenharmony_ci  if (!sanei_usb_attr_is_uint(node, "endpoint_number",
4145141cc406Sopenharmony_ci                              devices[dn].bulk_out_ep & 0x0f))
4146141cc406Sopenharmony_ci    return -1;
4147141cc406Sopenharmony_ci
4148141cc406Sopenharmony_ci  size_t got_size = 0;
4149141cc406Sopenharmony_ci  char* got_data = sanei_xml_get_hex_data(node, &got_size);
4150141cc406Sopenharmony_ci  free(got_data);
4151141cc406Sopenharmony_ci  return got_size;
4152141cc406Sopenharmony_ci}
4153141cc406Sopenharmony_ci
4154141cc406Sopenharmony_cistatic int sanei_usb_replay_write_bulk(SANE_Int dn, const SANE_Byte* buffer,
4155141cc406Sopenharmony_ci                                       size_t size)
4156141cc406Sopenharmony_ci{
4157141cc406Sopenharmony_ci  size_t wanted_size = size;
4158141cc406Sopenharmony_ci  size_t total_wrote_size = 0;
4159141cc406Sopenharmony_ci  while (wanted_size > 0)
4160141cc406Sopenharmony_ci    {
4161141cc406Sopenharmony_ci      if (testing_known_commands_input_failed)
4162141cc406Sopenharmony_ci        return -1;
4163141cc406Sopenharmony_ci
4164141cc406Sopenharmony_ci      xmlNode* node = sanei_xml_get_next_tx_node();
4165141cc406Sopenharmony_ci      if (node == NULL)
4166141cc406Sopenharmony_ci        {
4167141cc406Sopenharmony_ci          FAIL_TEST(__func__, "no more transactions\n");
4168141cc406Sopenharmony_ci          return -1;
4169141cc406Sopenharmony_ci        }
4170141cc406Sopenharmony_ci
4171141cc406Sopenharmony_ci      if (sanei_xml_is_known_commands_end(node))
4172141cc406Sopenharmony_ci        {
4173141cc406Sopenharmony_ci          sanei_usb_record_write_bulk(NULL, dn, buffer, size, size);
4174141cc406Sopenharmony_ci          return size;
4175141cc406Sopenharmony_ci        }
4176141cc406Sopenharmony_ci
4177141cc406Sopenharmony_ci      sanei_xml_record_seq(node);
4178141cc406Sopenharmony_ci      sanei_xml_break_if_needed(node);
4179141cc406Sopenharmony_ci
4180141cc406Sopenharmony_ci      if (xmlStrcmp(node->name, (const xmlChar*)"bulk_tx") != 0)
4181141cc406Sopenharmony_ci        {
4182141cc406Sopenharmony_ci          FAIL_TEST_TX(__func__, node, "unexpected transaction type %s\n",
4183141cc406Sopenharmony_ci                       (const char*) node->name);
4184141cc406Sopenharmony_ci          sanei_usb_record_replace_write_bulk(node, dn, buffer, size, size);
4185141cc406Sopenharmony_ci          return -1;
4186141cc406Sopenharmony_ci        }
4187141cc406Sopenharmony_ci
4188141cc406Sopenharmony_ci      if (!sanei_usb_check_attr(node, "direction", "OUT", __func__))
4189141cc406Sopenharmony_ci        {
4190141cc406Sopenharmony_ci          sanei_usb_record_replace_write_bulk(node, dn, buffer, size, size);
4191141cc406Sopenharmony_ci          return -1;
4192141cc406Sopenharmony_ci        }
4193141cc406Sopenharmony_ci      if (!sanei_usb_check_attr_uint(node, "endpoint_number",
4194141cc406Sopenharmony_ci                                     devices[dn].bulk_out_ep & 0x0f,
4195141cc406Sopenharmony_ci                                     __func__))
4196141cc406Sopenharmony_ci        {
4197141cc406Sopenharmony_ci          sanei_usb_record_replace_write_bulk(node, dn, buffer, size, size);
4198141cc406Sopenharmony_ci          return -1;
4199141cc406Sopenharmony_ci        }
4200141cc406Sopenharmony_ci
4201141cc406Sopenharmony_ci      size_t wrote_size = 0;
4202141cc406Sopenharmony_ci      char* wrote_data = sanei_xml_get_hex_data(node, &wrote_size);
4203141cc406Sopenharmony_ci
4204141cc406Sopenharmony_ci      if (wrote_size > wanted_size)
4205141cc406Sopenharmony_ci        {
4206141cc406Sopenharmony_ci          FAIL_TEST_TX(__func__, node,
4207141cc406Sopenharmony_ci                       "wrote more data than wanted (%lu vs %lu)\n",
4208141cc406Sopenharmony_ci                       wrote_size, wanted_size);
4209141cc406Sopenharmony_ci          if (!testing_development_mode)
4210141cc406Sopenharmony_ci            {
4211141cc406Sopenharmony_ci              free(wrote_data);
4212141cc406Sopenharmony_ci              return -1;
4213141cc406Sopenharmony_ci            }
4214141cc406Sopenharmony_ci          sanei_usb_record_replace_write_bulk(node, dn, buffer, size, size);
4215141cc406Sopenharmony_ci          wrote_size = size;
4216141cc406Sopenharmony_ci        }
4217141cc406Sopenharmony_ci      else if (!sanei_usb_check_data_equal(node,
4218141cc406Sopenharmony_ci                                           ((const char*) buffer) +
4219141cc406Sopenharmony_ci                                              total_wrote_size,
4220141cc406Sopenharmony_ci                                           wrote_size,
4221141cc406Sopenharmony_ci                                           wrote_data, wrote_size,
4222141cc406Sopenharmony_ci                                           __func__))
4223141cc406Sopenharmony_ci        {
4224141cc406Sopenharmony_ci          if (!testing_development_mode)
4225141cc406Sopenharmony_ci            {
4226141cc406Sopenharmony_ci              free(wrote_data);
4227141cc406Sopenharmony_ci              return -1;
4228141cc406Sopenharmony_ci            }
4229141cc406Sopenharmony_ci          sanei_usb_record_replace_write_bulk(node, dn, buffer, size,
4230141cc406Sopenharmony_ci                                              size);
4231141cc406Sopenharmony_ci          wrote_size = size;
4232141cc406Sopenharmony_ci        }
4233141cc406Sopenharmony_ci
4234141cc406Sopenharmony_ci      free(wrote_data);
4235141cc406Sopenharmony_ci      if (wrote_size < wanted_size &&
4236141cc406Sopenharmony_ci          sanei_usb_replay_next_write_bulk_packet_size(dn) < 0)
4237141cc406Sopenharmony_ci        {
4238141cc406Sopenharmony_ci          FAIL_TEST_TX(__func__, node,
4239141cc406Sopenharmony_ci                       "wrote less data than wanted (%lu vs %lu)\n",
4240141cc406Sopenharmony_ci                       wrote_size, wanted_size);
4241141cc406Sopenharmony_ci          if (!testing_development_mode)
4242141cc406Sopenharmony_ci            {
4243141cc406Sopenharmony_ci              return -1;
4244141cc406Sopenharmony_ci            }
4245141cc406Sopenharmony_ci          sanei_usb_record_replace_write_bulk(node, dn, buffer, size,
4246141cc406Sopenharmony_ci                                              size);
4247141cc406Sopenharmony_ci          wrote_size = size;
4248141cc406Sopenharmony_ci        }
4249141cc406Sopenharmony_ci      total_wrote_size += wrote_size;
4250141cc406Sopenharmony_ci      wanted_size -= wrote_size;
4251141cc406Sopenharmony_ci    }
4252141cc406Sopenharmony_ci  return total_wrote_size;
4253141cc406Sopenharmony_ci}
4254141cc406Sopenharmony_ci#endif
4255141cc406Sopenharmony_ci
4256141cc406Sopenharmony_ciSANE_Status
4257141cc406Sopenharmony_cisanei_usb_write_bulk (SANE_Int dn, const SANE_Byte * buffer, size_t * size)
4258141cc406Sopenharmony_ci{
4259141cc406Sopenharmony_ci  ssize_t write_size = 0;
4260141cc406Sopenharmony_ci
4261141cc406Sopenharmony_ci  if (!size)
4262141cc406Sopenharmony_ci    {
4263141cc406Sopenharmony_ci      DBG (1, "sanei_usb_write_bulk: size == NULL\n");
4264141cc406Sopenharmony_ci      return SANE_STATUS_INVAL;
4265141cc406Sopenharmony_ci    }
4266141cc406Sopenharmony_ci
4267141cc406Sopenharmony_ci  if (dn >= device_number || dn < 0)
4268141cc406Sopenharmony_ci    {
4269141cc406Sopenharmony_ci      DBG (1, "sanei_usb_write_bulk: dn >= device number || dn < 0\n");
4270141cc406Sopenharmony_ci      return SANE_STATUS_INVAL;
4271141cc406Sopenharmony_ci    }
4272141cc406Sopenharmony_ci  DBG (5, "sanei_usb_write_bulk: trying to write %lu bytes\n",
4273141cc406Sopenharmony_ci       (unsigned long) *size);
4274141cc406Sopenharmony_ci  if (debug_level > 10)
4275141cc406Sopenharmony_ci    print_buffer (buffer, *size);
4276141cc406Sopenharmony_ci
4277141cc406Sopenharmony_ci  if (testing_mode == sanei_usb_testing_mode_replay)
4278141cc406Sopenharmony_ci    {
4279141cc406Sopenharmony_ci#if WITH_USB_RECORD_REPLAY
4280141cc406Sopenharmony_ci      write_size = sanei_usb_replay_write_bulk(dn, buffer, *size);
4281141cc406Sopenharmony_ci#else
4282141cc406Sopenharmony_ci      DBG (1, "USB record-replay mode support is missing\n");
4283141cc406Sopenharmony_ci      return SANE_STATUS_UNSUPPORTED;
4284141cc406Sopenharmony_ci#endif
4285141cc406Sopenharmony_ci    }
4286141cc406Sopenharmony_ci  else if (devices[dn].method == sanei_usb_method_scanner_driver)
4287141cc406Sopenharmony_ci    {
4288141cc406Sopenharmony_ci      write_size = write (devices[dn].fd, buffer, *size);
4289141cc406Sopenharmony_ci
4290141cc406Sopenharmony_ci      if (write_size < 0)
4291141cc406Sopenharmony_ci	DBG (1, "sanei_usb_write_bulk: write failed: %s\n",
4292141cc406Sopenharmony_ci	     strerror (errno));
4293141cc406Sopenharmony_ci    }
4294141cc406Sopenharmony_ci  else if (devices[dn].method == sanei_usb_method_libusb)
4295141cc406Sopenharmony_ci#ifdef HAVE_LIBUSB_LEGACY
4296141cc406Sopenharmony_ci    {
4297141cc406Sopenharmony_ci      if (devices[dn].bulk_out_ep)
4298141cc406Sopenharmony_ci	{
4299141cc406Sopenharmony_ci	  write_size = usb_bulk_write (devices[dn].libusb_handle,
4300141cc406Sopenharmony_ci				       devices[dn].bulk_out_ep,
4301141cc406Sopenharmony_ci				       (const char *) buffer,
4302141cc406Sopenharmony_ci				       (int) *size, libusb_timeout);
4303141cc406Sopenharmony_ci	  if (write_size < 0)
4304141cc406Sopenharmony_ci	    DBG (1, "sanei_usb_write_bulk: write failed: %s\n",
4305141cc406Sopenharmony_ci		 strerror (errno));
4306141cc406Sopenharmony_ci	}
4307141cc406Sopenharmony_ci      else
4308141cc406Sopenharmony_ci	{
4309141cc406Sopenharmony_ci	  DBG (1, "sanei_usb_write_bulk: can't write without a bulk-out "
4310141cc406Sopenharmony_ci	       "endpoint\n");
4311141cc406Sopenharmony_ci	  return SANE_STATUS_INVAL;
4312141cc406Sopenharmony_ci	}
4313141cc406Sopenharmony_ci    }
4314141cc406Sopenharmony_ci#elif defined(HAVE_LIBUSB)
4315141cc406Sopenharmony_ci    {
4316141cc406Sopenharmony_ci      if (devices[dn].bulk_out_ep)
4317141cc406Sopenharmony_ci	{
4318141cc406Sopenharmony_ci	  int ret;
4319141cc406Sopenharmony_ci	  int trans_bytes;
4320141cc406Sopenharmony_ci	  ret = libusb_bulk_transfer (devices[dn].lu_handle,
4321141cc406Sopenharmony_ci				      devices[dn].bulk_out_ep,
4322141cc406Sopenharmony_ci				      (unsigned char *) buffer,
4323141cc406Sopenharmony_ci				      (int) *size, &trans_bytes,
4324141cc406Sopenharmony_ci				      libusb_timeout);
4325141cc406Sopenharmony_ci	  if (ret < 0)
4326141cc406Sopenharmony_ci	    {
4327141cc406Sopenharmony_ci	      DBG (1, "sanei_usb_write_bulk: write failed: %s\n",
4328141cc406Sopenharmony_ci		   sanei_libusb_strerror (ret));
4329141cc406Sopenharmony_ci
4330141cc406Sopenharmony_ci	      write_size = -1;
4331141cc406Sopenharmony_ci	    }
4332141cc406Sopenharmony_ci	  else
4333141cc406Sopenharmony_ci	    write_size = trans_bytes;
4334141cc406Sopenharmony_ci	}
4335141cc406Sopenharmony_ci      else
4336141cc406Sopenharmony_ci	{
4337141cc406Sopenharmony_ci	  DBG (1, "sanei_usb_write_bulk: can't write without a bulk-out "
4338141cc406Sopenharmony_ci	       "endpoint\n");
4339141cc406Sopenharmony_ci	  return SANE_STATUS_INVAL;
4340141cc406Sopenharmony_ci	}
4341141cc406Sopenharmony_ci    }
4342141cc406Sopenharmony_ci#elif defined(HAVE_USB_MANAGER)
4343141cc406Sopenharmony_ci    {
4344141cc406Sopenharmony_ci      if (devices[dn].bulk_out_ep)
4345141cc406Sopenharmony_ci	{
4346141cc406Sopenharmony_ci	  int ret;
4347141cc406Sopenharmony_ci	  int trans_bytes;
4348141cc406Sopenharmony_ci	  ret = usb_manager_bulk_transfer (devices[dn].usb_manager_handle,
4349141cc406Sopenharmony_ci				      devices[dn].bulk_out_ep,
4350141cc406Sopenharmony_ci				      (unsigned char *) buffer,
4351141cc406Sopenharmony_ci				      (int) *size, &trans_bytes,
4352141cc406Sopenharmony_ci				      usb_manager_timeout);
4353141cc406Sopenharmony_ci	  if (ret < 0)
4354141cc406Sopenharmony_ci	    {
4355141cc406Sopenharmony_ci	      DBG (1, "sanei_usb_write_bulk: write failed: %s\n",
4356141cc406Sopenharmony_ci		   sanei_usb_manager_strerror (ret));
4357141cc406Sopenharmony_ci
4358141cc406Sopenharmony_ci	      write_size = -1;
4359141cc406Sopenharmony_ci	    }
4360141cc406Sopenharmony_ci	  else
4361141cc406Sopenharmony_ci	    write_size = trans_bytes;
4362141cc406Sopenharmony_ci	}
4363141cc406Sopenharmony_ci      else
4364141cc406Sopenharmony_ci	{
4365141cc406Sopenharmony_ci	  DBG (1, "sanei_usb_write_bulk: can't write without a bulk-out "
4366141cc406Sopenharmony_ci	       "endpoint\n");
4367141cc406Sopenharmony_ci	  return SANE_STATUS_INVAL;
4368141cc406Sopenharmony_ci	}
4369141cc406Sopenharmony_ci    }
4370141cc406Sopenharmony_ci#else /* not HAVE_LIBUSB_LEGACY && not HAVE_LIBUSB && not HAVE_USB_MANAGER */
4371141cc406Sopenharmony_ci    {
4372141cc406Sopenharmony_ci      DBG (1, "sanei_usb_write_bulk: libusb support missing\n");
4373141cc406Sopenharmony_ci      return SANE_STATUS_UNSUPPORTED;
4374141cc406Sopenharmony_ci    }
4375141cc406Sopenharmony_ci#endif /* not HAVE_LIBUSB_LEGACY && not HAVE_LIBUSB && not HAVE_USB_MANAGER */
4376141cc406Sopenharmony_ci  else if (devices[dn].method == sanei_usb_method_usbcalls)
4377141cc406Sopenharmony_ci  {
4378141cc406Sopenharmony_ci#ifdef HAVE_USBCALLS
4379141cc406Sopenharmony_ci    int rc;
4380141cc406Sopenharmony_ci    DBG (5, "Entered usbcalls UsbBulkWrite with dn = %d\n",dn);
4381141cc406Sopenharmony_ci    DBG (5, "Entered usbcalls UsbBulkWrite with dh = %p\n",dh);
4382141cc406Sopenharmony_ci    DBG (5, "Entered usbcalls UsbBulkWrite with bulk_out_ep = 0x%02x\n",devices[dn].bulk_out_ep);
4383141cc406Sopenharmony_ci    DBG (5, "Entered usbcalls UsbBulkWrite with interface_nr = %d\n",devices[dn].interface_nr);
4384141cc406Sopenharmony_ci    DBG (5, "Entered usbcalls UsbBulkWrite with usbcalls_timeout = %d\n",usbcalls_timeout);
4385141cc406Sopenharmony_ci    size_t requested_size = *size;
4386141cc406Sopenharmony_ci    while (requested_size)
4387141cc406Sopenharmony_ci    {
4388141cc406Sopenharmony_ci      ULONG ulToWrite = (requested_size>MAX_RW)?MAX_RW:requested_size;
4389141cc406Sopenharmony_ci
4390141cc406Sopenharmony_ci      DBG (5, "size requested to write = %lu, ulToWrite = %lu\n",(unsigned long) requested_size,ulToWrite);
4391141cc406Sopenharmony_ci      if (devices[dn].bulk_out_ep){
4392141cc406Sopenharmony_ci        rc = UsbBulkWrite (dh, devices[dn].bulk_out_ep, devices[dn].interface_nr,
4393141cc406Sopenharmony_ci                               ulToWrite, (char*) buffer, usbcalls_timeout);
4394141cc406Sopenharmony_ci        DBG (1, "sanei_usb_write_bulk: rc = %d\n",rc);
4395141cc406Sopenharmony_ci      }
4396141cc406Sopenharmony_ci      else
4397141cc406Sopenharmony_ci      {
4398141cc406Sopenharmony_ci          DBG (1, "sanei_usb_write_bulk: can't read without a bulk-out endpoint\n");
4399141cc406Sopenharmony_ci          return SANE_STATUS_INVAL;
4400141cc406Sopenharmony_ci      }
4401141cc406Sopenharmony_ci      if (rc) return SANE_STATUS_INVAL;
4402141cc406Sopenharmony_ci      requested_size -=ulToWrite;
4403141cc406Sopenharmony_ci      buffer += ulToWrite;
4404141cc406Sopenharmony_ci      write_size += ulToWrite;
4405141cc406Sopenharmony_ci      DBG (5, "size = %d, write_size = %d\n", requested_size, write_size);
4406141cc406Sopenharmony_ci    }
4407141cc406Sopenharmony_ci#else /* not HAVE_USBCALLS */
4408141cc406Sopenharmony_ci    {
4409141cc406Sopenharmony_ci      DBG (1, "sanei_usb_write_bulk: usbcalls support missing\n");
4410141cc406Sopenharmony_ci      return SANE_STATUS_UNSUPPORTED;
4411141cc406Sopenharmony_ci    }
4412141cc406Sopenharmony_ci#endif /* not HAVE_USBCALLS */
4413141cc406Sopenharmony_ci  }
4414141cc406Sopenharmony_ci  else
4415141cc406Sopenharmony_ci    {
4416141cc406Sopenharmony_ci      DBG (1, "sanei_usb_write_bulk: access method %d not implemented\n",
4417141cc406Sopenharmony_ci	   devices[dn].method);
4418141cc406Sopenharmony_ci      return SANE_STATUS_INVAL;
4419141cc406Sopenharmony_ci    }
4420141cc406Sopenharmony_ci
4421141cc406Sopenharmony_ci  if (testing_mode == sanei_usb_testing_mode_record)
4422141cc406Sopenharmony_ci    {
4423141cc406Sopenharmony_ci#if WITH_USB_RECORD_REPLAY
4424141cc406Sopenharmony_ci      sanei_usb_record_write_bulk(NULL, dn, buffer, *size, write_size);
4425141cc406Sopenharmony_ci#else
4426141cc406Sopenharmony_ci      DBG (1, "USB record-replay mode support is missing\n");
4427141cc406Sopenharmony_ci      return SANE_STATUS_UNSUPPORTED;
4428141cc406Sopenharmony_ci#endif
4429141cc406Sopenharmony_ci    }
4430141cc406Sopenharmony_ci
4431141cc406Sopenharmony_ci  if (write_size < 0)
4432141cc406Sopenharmony_ci    {
4433141cc406Sopenharmony_ci      *size = 0;
4434141cc406Sopenharmony_ci      if (testing_mode != sanei_usb_testing_mode_disabled)
4435141cc406Sopenharmony_ci        return SANE_STATUS_IO_ERROR;
4436141cc406Sopenharmony_ci
4437141cc406Sopenharmony_ci#ifdef HAVE_LIBUSB_LEGACY
4438141cc406Sopenharmony_ci      if (devices[dn].method == sanei_usb_method_libusb)
4439141cc406Sopenharmony_ci	usb_clear_halt (devices[dn].libusb_handle, devices[dn].bulk_out_ep);
4440141cc406Sopenharmony_ci#elif defined(HAVE_LIBUSB)
4441141cc406Sopenharmony_ci      if (devices[dn].method == sanei_usb_method_libusb)
4442141cc406Sopenharmony_ci	libusb_clear_halt (devices[dn].lu_handle, devices[dn].bulk_out_ep);
4443141cc406Sopenharmony_ci#elif defined(HAVE_USB_MANAGER)
4444141cc406Sopenharmony_ci      if (devices[dn].method == sanei_usb_method_usb_manager)
4445141cc406Sopenharmony_ci	usb_manager_clear_halt (devices[dn].usb_manager_handle, devices[dn].bulk_out_ep);
4446141cc406Sopenharmony_ci#endif
4447141cc406Sopenharmony_ci      return SANE_STATUS_IO_ERROR;
4448141cc406Sopenharmony_ci    }
4449141cc406Sopenharmony_ci  DBG (5, "sanei_usb_write_bulk: wanted %lu bytes, wrote %ld bytes\n",
4450141cc406Sopenharmony_ci       (unsigned long) *size, (unsigned long) write_size);
4451141cc406Sopenharmony_ci  *size = write_size;
4452141cc406Sopenharmony_ci  return SANE_STATUS_GOOD;
4453141cc406Sopenharmony_ci}
4454141cc406Sopenharmony_ci
4455141cc406Sopenharmony_ci#if WITH_USB_RECORD_REPLAY
4456141cc406Sopenharmony_cistatic void
4457141cc406Sopenharmony_cisanei_usb_record_control_msg(xmlNode* node,
4458141cc406Sopenharmony_ci                             SANE_Int dn, SANE_Int rtype, SANE_Int req,
4459141cc406Sopenharmony_ci                             SANE_Int value, SANE_Int index, SANE_Int len,
4460141cc406Sopenharmony_ci                             const SANE_Byte* data)
4461141cc406Sopenharmony_ci{
4462141cc406Sopenharmony_ci  (void) dn;
4463141cc406Sopenharmony_ci
4464141cc406Sopenharmony_ci  int node_was_null = node == NULL;
4465141cc406Sopenharmony_ci  if (node_was_null)
4466141cc406Sopenharmony_ci    node = testing_append_commands_node;
4467141cc406Sopenharmony_ci
4468141cc406Sopenharmony_ci  xmlNode* e_tx = xmlNewNode(NULL, (const xmlChar*)"control_tx");
4469141cc406Sopenharmony_ci
4470141cc406Sopenharmony_ci  int direction_is_in = (rtype & 0x80) == 0x80;
4471141cc406Sopenharmony_ci  sanei_xml_command_common_props(e_tx, rtype & 0x1f,
4472141cc406Sopenharmony_ci                                 direction_is_in ? "IN" : "OUT");
4473141cc406Sopenharmony_ci  sanei_xml_set_hex_attr(e_tx, "bmRequestType", rtype);
4474141cc406Sopenharmony_ci  sanei_xml_set_hex_attr(e_tx, "bRequest", req);
4475141cc406Sopenharmony_ci  sanei_xml_set_hex_attr(e_tx, "wValue", value);
4476141cc406Sopenharmony_ci  sanei_xml_set_hex_attr(e_tx, "wIndex", index);
4477141cc406Sopenharmony_ci  sanei_xml_set_hex_attr(e_tx, "wLength", len);
4478141cc406Sopenharmony_ci
4479141cc406Sopenharmony_ci  if (direction_is_in && data == NULL)
4480141cc406Sopenharmony_ci    {
4481141cc406Sopenharmony_ci      const int buf_size = 128;
4482141cc406Sopenharmony_ci      char buf[buf_size];
4483141cc406Sopenharmony_ci      snprintf(buf, buf_size, "(unknown read of size %d)", len);
4484141cc406Sopenharmony_ci      xmlNode* e_content = xmlNewText((const xmlChar*)buf);
4485141cc406Sopenharmony_ci      xmlAddChild(e_tx, e_content);
4486141cc406Sopenharmony_ci    }
4487141cc406Sopenharmony_ci  else
4488141cc406Sopenharmony_ci    {
4489141cc406Sopenharmony_ci      sanei_xml_set_hex_data(e_tx, (const char*)data, len);
4490141cc406Sopenharmony_ci    }
4491141cc406Sopenharmony_ci
4492141cc406Sopenharmony_ci  node = sanei_xml_append_command(node, node_was_null, e_tx);
4493141cc406Sopenharmony_ci
4494141cc406Sopenharmony_ci  if (node_was_null)
4495141cc406Sopenharmony_ci    testing_append_commands_node = node;
4496141cc406Sopenharmony_ci}
4497141cc406Sopenharmony_ci
4498141cc406Sopenharmony_ci
4499141cc406Sopenharmony_cistatic SANE_Status
4500141cc406Sopenharmony_cisanei_usb_record_replace_control_msg(xmlNode* node,
4501141cc406Sopenharmony_ci                                     SANE_Int dn, SANE_Int rtype, SANE_Int req,
4502141cc406Sopenharmony_ci                                     SANE_Int value, SANE_Int index, SANE_Int len,
4503141cc406Sopenharmony_ci                                     const SANE_Byte* data)
4504141cc406Sopenharmony_ci{
4505141cc406Sopenharmony_ci  if (!testing_development_mode)
4506141cc406Sopenharmony_ci    return SANE_STATUS_IO_ERROR;
4507141cc406Sopenharmony_ci
4508141cc406Sopenharmony_ci  SANE_Status ret = SANE_STATUS_GOOD;
4509141cc406Sopenharmony_ci  int direction_is_in = (rtype & 0x80) == 0x80;
4510141cc406Sopenharmony_ci  if (direction_is_in)
4511141cc406Sopenharmony_ci    {
4512141cc406Sopenharmony_ci      testing_known_commands_input_failed = 1;
4513141cc406Sopenharmony_ci      ret = SANE_STATUS_IO_ERROR;
4514141cc406Sopenharmony_ci    }
4515141cc406Sopenharmony_ci
4516141cc406Sopenharmony_ci  testing_last_known_seq--;
4517141cc406Sopenharmony_ci  sanei_usb_record_control_msg(node, dn, rtype, req, value, index, len, data);
4518141cc406Sopenharmony_ci  xmlUnlinkNode(node);
4519141cc406Sopenharmony_ci  xmlFreeNode(node);
4520141cc406Sopenharmony_ci  return ret;
4521141cc406Sopenharmony_ci}
4522141cc406Sopenharmony_ci
4523141cc406Sopenharmony_cistatic SANE_Status
4524141cc406Sopenharmony_cisanei_usb_replay_control_msg(SANE_Int dn, SANE_Int rtype, SANE_Int req,
4525141cc406Sopenharmony_ci                             SANE_Int value, SANE_Int index, SANE_Int len,
4526141cc406Sopenharmony_ci                             SANE_Byte* data)
4527141cc406Sopenharmony_ci{
4528141cc406Sopenharmony_ci  (void) dn;
4529141cc406Sopenharmony_ci
4530141cc406Sopenharmony_ci  if (testing_known_commands_input_failed)
4531141cc406Sopenharmony_ci    return SANE_STATUS_IO_ERROR;
4532141cc406Sopenharmony_ci
4533141cc406Sopenharmony_ci  xmlNode* node = sanei_xml_get_next_tx_node();
4534141cc406Sopenharmony_ci  if (node == NULL)
4535141cc406Sopenharmony_ci    {
4536141cc406Sopenharmony_ci      FAIL_TEST(__func__, "no more transactions\n");
4537141cc406Sopenharmony_ci      return SANE_STATUS_IO_ERROR;
4538141cc406Sopenharmony_ci    }
4539141cc406Sopenharmony_ci
4540141cc406Sopenharmony_ci  int direction_is_in = (rtype & 0x80) == 0x80;
4541141cc406Sopenharmony_ci  SANE_Byte* rdata = direction_is_in ? NULL : data;
4542141cc406Sopenharmony_ci
4543141cc406Sopenharmony_ci  if (sanei_xml_is_known_commands_end(node))
4544141cc406Sopenharmony_ci    {
4545141cc406Sopenharmony_ci      sanei_usb_record_control_msg(NULL, dn, rtype, req, value, index, len,
4546141cc406Sopenharmony_ci                                   rdata);
4547141cc406Sopenharmony_ci      if (direction_is_in)
4548141cc406Sopenharmony_ci        {
4549141cc406Sopenharmony_ci          testing_known_commands_input_failed = 1;
4550141cc406Sopenharmony_ci          return SANE_STATUS_IO_ERROR;
4551141cc406Sopenharmony_ci        }
4552141cc406Sopenharmony_ci      return SANE_STATUS_GOOD;
4553141cc406Sopenharmony_ci    }
4554141cc406Sopenharmony_ci
4555141cc406Sopenharmony_ci  sanei_xml_record_seq(node);
4556141cc406Sopenharmony_ci  sanei_xml_break_if_needed(node);
4557141cc406Sopenharmony_ci
4558141cc406Sopenharmony_ci  if (xmlStrcmp(node->name, (const xmlChar*)"control_tx") != 0)
4559141cc406Sopenharmony_ci    {
4560141cc406Sopenharmony_ci      FAIL_TEST_TX(__func__, node, "unexpected transaction type %s\n",
4561141cc406Sopenharmony_ci                   (const char*) node->name);
4562141cc406Sopenharmony_ci      return sanei_usb_record_replace_control_msg(node, dn, rtype, req, value,
4563141cc406Sopenharmony_ci                                                  index, len, rdata);
4564141cc406Sopenharmony_ci    }
4565141cc406Sopenharmony_ci
4566141cc406Sopenharmony_ci  if (!sanei_usb_check_attr(node, "direction", direction_is_in ? "IN" : "OUT",
4567141cc406Sopenharmony_ci                            __func__) ||
4568141cc406Sopenharmony_ci      !sanei_usb_check_attr_uint(node, "bmRequestType", rtype, __func__) ||
4569141cc406Sopenharmony_ci      !sanei_usb_check_attr_uint(node, "bRequest", req, __func__) ||
4570141cc406Sopenharmony_ci      !sanei_usb_check_attr_uint(node, "wValue", value, __func__) ||
4571141cc406Sopenharmony_ci      !sanei_usb_check_attr_uint(node, "wIndex", index, __func__) ||
4572141cc406Sopenharmony_ci      !sanei_usb_check_attr_uint(node, "wLength", len, __func__))
4573141cc406Sopenharmony_ci    {
4574141cc406Sopenharmony_ci      return sanei_usb_record_replace_control_msg(node, dn, rtype, req, value,
4575141cc406Sopenharmony_ci                                                  index, len, rdata);
4576141cc406Sopenharmony_ci    }
4577141cc406Sopenharmony_ci
4578141cc406Sopenharmony_ci  size_t tx_data_size = 0;
4579141cc406Sopenharmony_ci  char* tx_data = sanei_xml_get_hex_data(node, &tx_data_size);
4580141cc406Sopenharmony_ci
4581141cc406Sopenharmony_ci  if (direction_is_in)
4582141cc406Sopenharmony_ci    {
4583141cc406Sopenharmony_ci      if (tx_data_size != (size_t)len)
4584141cc406Sopenharmony_ci        {
4585141cc406Sopenharmony_ci          FAIL_TEST_TX(__func__, node,
4586141cc406Sopenharmony_ci                       "got different amount of data than wanted (%lu vs %lu)\n",
4587141cc406Sopenharmony_ci                       tx_data_size, (size_t)len);
4588141cc406Sopenharmony_ci          free(tx_data);
4589141cc406Sopenharmony_ci          return sanei_usb_record_replace_control_msg(node, dn, rtype, req,
4590141cc406Sopenharmony_ci                                                      value, index, len, rdata);
4591141cc406Sopenharmony_ci        }
4592141cc406Sopenharmony_ci      memcpy(data, tx_data, tx_data_size);
4593141cc406Sopenharmony_ci    }
4594141cc406Sopenharmony_ci  else
4595141cc406Sopenharmony_ci    {
4596141cc406Sopenharmony_ci      if (!sanei_usb_check_data_equal(node,
4597141cc406Sopenharmony_ci                                      (const char*)data, len,
4598141cc406Sopenharmony_ci                                      tx_data, tx_data_size, __func__))
4599141cc406Sopenharmony_ci        {
4600141cc406Sopenharmony_ci          free(tx_data);
4601141cc406Sopenharmony_ci          return sanei_usb_record_replace_control_msg(node, dn, rtype, req,
4602141cc406Sopenharmony_ci                                                      value, index, len, rdata);
4603141cc406Sopenharmony_ci        }
4604141cc406Sopenharmony_ci    }
4605141cc406Sopenharmony_ci  free(tx_data);
4606141cc406Sopenharmony_ci  return SANE_STATUS_GOOD;
4607141cc406Sopenharmony_ci}
4608141cc406Sopenharmony_ci#endif
4609141cc406Sopenharmony_ci
4610141cc406Sopenharmony_ciSANE_Status
4611141cc406Sopenharmony_cisanei_usb_control_msg (SANE_Int dn, SANE_Int rtype, SANE_Int req,
4612141cc406Sopenharmony_ci		       SANE_Int value, SANE_Int index, SANE_Int len,
4613141cc406Sopenharmony_ci		       SANE_Byte * data)
4614141cc406Sopenharmony_ci{
4615141cc406Sopenharmony_ci  if (dn >= device_number || dn < 0)
4616141cc406Sopenharmony_ci    {
4617141cc406Sopenharmony_ci      DBG (1, "sanei_usb_control_msg: dn >= device number || dn < 0, dn=%d\n",
4618141cc406Sopenharmony_ci	   dn);
4619141cc406Sopenharmony_ci      return SANE_STATUS_INVAL;
4620141cc406Sopenharmony_ci    }
4621141cc406Sopenharmony_ci
4622141cc406Sopenharmony_ci  DBG (5, "sanei_usb_control_msg: rtype = 0x%02x, req = %d, value = %d, "
4623141cc406Sopenharmony_ci       "index = %d, len = %d\n", rtype, req, value, index, len);
4624141cc406Sopenharmony_ci  if (!(rtype & 0x80) && debug_level > 10)
4625141cc406Sopenharmony_ci    print_buffer (data, len);
4626141cc406Sopenharmony_ci
4627141cc406Sopenharmony_ci  if (testing_mode == sanei_usb_testing_mode_replay)
4628141cc406Sopenharmony_ci    {
4629141cc406Sopenharmony_ci#if WITH_USB_RECORD_REPLAY
4630141cc406Sopenharmony_ci      return sanei_usb_replay_control_msg(dn, rtype, req, value, index, len,
4631141cc406Sopenharmony_ci                                          data);
4632141cc406Sopenharmony_ci#else
4633141cc406Sopenharmony_ci      DBG (1, "USB record-replay mode support is missing\n");
4634141cc406Sopenharmony_ci      return SANE_STATUS_UNSUPPORTED;
4635141cc406Sopenharmony_ci#endif
4636141cc406Sopenharmony_ci    }
4637141cc406Sopenharmony_ci  if (devices[dn].method == sanei_usb_method_scanner_driver)
4638141cc406Sopenharmony_ci    {
4639141cc406Sopenharmony_ci#if defined(__linux__)
4640141cc406Sopenharmony_ci      struct ctrlmsg_ioctl c;
4641141cc406Sopenharmony_ci
4642141cc406Sopenharmony_ci      c.req.requesttype = rtype;
4643141cc406Sopenharmony_ci      c.req.request = req;
4644141cc406Sopenharmony_ci      c.req.value = value;
4645141cc406Sopenharmony_ci      c.req.index = index;
4646141cc406Sopenharmony_ci      c.req.length = len;
4647141cc406Sopenharmony_ci      c.data = data;
4648141cc406Sopenharmony_ci
4649141cc406Sopenharmony_ci      if (ioctl (devices[dn].fd, SCANNER_IOCTL_CTRLMSG, &c) < 0)
4650141cc406Sopenharmony_ci	{
4651141cc406Sopenharmony_ci	  DBG (5, "sanei_usb_control_msg: SCANNER_IOCTL_CTRLMSG error - %s\n",
4652141cc406Sopenharmony_ci	       strerror (errno));
4653141cc406Sopenharmony_ci	  return SANE_STATUS_IO_ERROR;
4654141cc406Sopenharmony_ci	}
4655141cc406Sopenharmony_ci      if ((rtype & 0x80) && debug_level > 10)
4656141cc406Sopenharmony_ci	print_buffer (data, len);
4657141cc406Sopenharmony_ci#elif defined(__BEOS__)
4658141cc406Sopenharmony_ci      struct usb_scanner_ioctl_ctrlmsg c;
4659141cc406Sopenharmony_ci
4660141cc406Sopenharmony_ci      c.req.request_type = rtype;
4661141cc406Sopenharmony_ci      c.req.request = req;
4662141cc406Sopenharmony_ci      c.req.value = value;
4663141cc406Sopenharmony_ci      c.req.index = index;
4664141cc406Sopenharmony_ci      c.req.length = len;
4665141cc406Sopenharmony_ci      c.data = data;
4666141cc406Sopenharmony_ci
4667141cc406Sopenharmony_ci      if (ioctl (devices[dn].fd, B_SCANNER_IOCTL_CTRLMSG, &c) < 0)
4668141cc406Sopenharmony_ci	{
4669141cc406Sopenharmony_ci	  DBG (5, "sanei_usb_control_msg: SCANNER_IOCTL_CTRLMSG error - %s\n",
4670141cc406Sopenharmony_ci	       strerror (errno));
4671141cc406Sopenharmony_ci	  return SANE_STATUS_IO_ERROR;
4672141cc406Sopenharmony_ci	}
4673141cc406Sopenharmony_ci	if ((rtype & 0x80) && debug_level > 10)
4674141cc406Sopenharmony_ci		print_buffer (data, len);
4675141cc406Sopenharmony_ci#else /* not __linux__ */
4676141cc406Sopenharmony_ci      DBG (5, "sanei_usb_control_msg: not supported on this OS\n");
4677141cc406Sopenharmony_ci      return SANE_STATUS_UNSUPPORTED;
4678141cc406Sopenharmony_ci#endif /* not __linux__ */
4679141cc406Sopenharmony_ci    }
4680141cc406Sopenharmony_ci  else if (devices[dn].method == sanei_usb_method_libusb)
4681141cc406Sopenharmony_ci#ifdef HAVE_LIBUSB_LEGACY
4682141cc406Sopenharmony_ci    {
4683141cc406Sopenharmony_ci      int result;
4684141cc406Sopenharmony_ci
4685141cc406Sopenharmony_ci      result = usb_control_msg (devices[dn].libusb_handle, rtype, req,
4686141cc406Sopenharmony_ci				value, index, (char *) data, len,
4687141cc406Sopenharmony_ci				libusb_timeout);
4688141cc406Sopenharmony_ci      if (result < 0)
4689141cc406Sopenharmony_ci	{
4690141cc406Sopenharmony_ci	  DBG (1, "sanei_usb_control_msg: libusb complained: %s\n",
4691141cc406Sopenharmony_ci	       usb_strerror ());
4692141cc406Sopenharmony_ci	  return SANE_STATUS_INVAL;
4693141cc406Sopenharmony_ci	}
4694141cc406Sopenharmony_ci      if ((rtype & 0x80) && debug_level > 10)
4695141cc406Sopenharmony_ci	print_buffer (data, len);
4696141cc406Sopenharmony_ci    }
4697141cc406Sopenharmony_ci#elif defined(HAVE_LIBUSB)
4698141cc406Sopenharmony_ci    {
4699141cc406Sopenharmony_ci      int result;
4700141cc406Sopenharmony_ci
4701141cc406Sopenharmony_ci      result = libusb_control_transfer (devices[dn].lu_handle, rtype, req,
4702141cc406Sopenharmony_ci					value, index, data, len,
4703141cc406Sopenharmony_ci					libusb_timeout);
4704141cc406Sopenharmony_ci      if (result < 0)
4705141cc406Sopenharmony_ci	{
4706141cc406Sopenharmony_ci	  DBG (1, "sanei_usb_control_msg: libusb complained: %s\n",
4707141cc406Sopenharmony_ci	       sanei_libusb_strerror (result));
4708141cc406Sopenharmony_ci	  return SANE_STATUS_INVAL;
4709141cc406Sopenharmony_ci	}
4710141cc406Sopenharmony_ci      if ((rtype & 0x80) && debug_level > 10)
4711141cc406Sopenharmony_ci	print_buffer (data, len);
4712141cc406Sopenharmony_ci    }
4713141cc406Sopenharmony_ci#elif defined(HAVE_USB_MANAGER)
4714141cc406Sopenharmony_ci    {
4715141cc406Sopenharmony_ci      int result;
4716141cc406Sopenharmony_ci
4717141cc406Sopenharmony_ci      result = usb_manager_control_transfer (devices[dn].usb_manager_handle, rtype, req,
4718141cc406Sopenharmony_ci					value, index, data, len,
4719141cc406Sopenharmony_ci					usb_manager_timeout);
4720141cc406Sopenharmony_ci      if (result < 0)
4721141cc406Sopenharmony_ci	{
4722141cc406Sopenharmony_ci	  DBG (1, "sanei_usb_control_msg: libusb complained: %s\n",
4723141cc406Sopenharmony_ci	       sanei_usb_manager_strerror (result));
4724141cc406Sopenharmony_ci	  return SANE_STATUS_INVAL;
4725141cc406Sopenharmony_ci	}
4726141cc406Sopenharmony_ci      if ((rtype & 0x80) && debug_level > 10)
4727141cc406Sopenharmony_ci	print_buffer (data, len);
4728141cc406Sopenharmony_ci    }
4729141cc406Sopenharmony_ci#else /* not HAVE_LIBUSB_LEGACY && not HAVE_LIBUSB && not HAVE_USB_MANAGER*/
4730141cc406Sopenharmony_ci    {
4731141cc406Sopenharmony_ci      DBG (1, "sanei_usb_control_msg: libusb support missing\n");
4732141cc406Sopenharmony_ci      return SANE_STATUS_UNSUPPORTED;
4733141cc406Sopenharmony_ci    }
4734141cc406Sopenharmony_ci#endif /* not HAVE_LIBUSB_LEGACY && not HAVE_LIBUSB && not HAVE_USB_MANAGER */
4735141cc406Sopenharmony_ci  else if (devices[dn].method == sanei_usb_method_usbcalls)
4736141cc406Sopenharmony_ci     {
4737141cc406Sopenharmony_ci#ifdef HAVE_USBCALLS
4738141cc406Sopenharmony_ci      int result;
4739141cc406Sopenharmony_ci
4740141cc406Sopenharmony_ci      result = UsbCtrlMessage (dh, rtype, req,
4741141cc406Sopenharmony_ci				value, index, len, (char *) data,
4742141cc406Sopenharmony_ci				usbcalls_timeout);
4743141cc406Sopenharmony_ci      DBG (5, "rc of usb_control_msg = %d\n",result);
4744141cc406Sopenharmony_ci      if (result < 0)
4745141cc406Sopenharmony_ci	{
4746141cc406Sopenharmony_ci	  DBG (1, "sanei_usb_control_msg: usbcalls complained: %d\n",result);
4747141cc406Sopenharmony_ci	  return SANE_STATUS_INVAL;
4748141cc406Sopenharmony_ci	}
4749141cc406Sopenharmony_ci      if ((rtype & 0x80) && debug_level > 10)
4750141cc406Sopenharmony_ci	print_buffer (data, len);
4751141cc406Sopenharmony_ci#else /* not HAVE_USBCALLS */
4752141cc406Sopenharmony_ci    {
4753141cc406Sopenharmony_ci      DBG (1, "sanei_usb_control_msg: usbcalls support missing\n");
4754141cc406Sopenharmony_ci      return SANE_STATUS_UNSUPPORTED;
4755141cc406Sopenharmony_ci    }
4756141cc406Sopenharmony_ci#endif /* not HAVE_USBCALLS */
4757141cc406Sopenharmony_ci     }
4758141cc406Sopenharmony_ci  else
4759141cc406Sopenharmony_ci    {
4760141cc406Sopenharmony_ci      DBG (1, "sanei_usb_control_msg: access method %d not implemented\n",
4761141cc406Sopenharmony_ci	   devices[dn].method);
4762141cc406Sopenharmony_ci      return SANE_STATUS_UNSUPPORTED;
4763141cc406Sopenharmony_ci    }
4764141cc406Sopenharmony_ci
4765141cc406Sopenharmony_ci  if (testing_mode == sanei_usb_testing_mode_record)
4766141cc406Sopenharmony_ci    {
4767141cc406Sopenharmony_ci#if WITH_USB_RECORD_REPLAY
4768141cc406Sopenharmony_ci      // TODO: record in the error code path too
4769141cc406Sopenharmony_ci      sanei_usb_record_control_msg(NULL, dn, rtype, req, value, index, len,
4770141cc406Sopenharmony_ci                                   data);
4771141cc406Sopenharmony_ci#else
4772141cc406Sopenharmony_ci      DBG (1, "USB record-replay mode support is missing\n");
4773141cc406Sopenharmony_ci      return SANE_STATUS_UNSUPPORTED;
4774141cc406Sopenharmony_ci#endif
4775141cc406Sopenharmony_ci    }
4776141cc406Sopenharmony_ci  return SANE_STATUS_GOOD;
4777141cc406Sopenharmony_ci}
4778141cc406Sopenharmony_ci
4779141cc406Sopenharmony_ci#if WITH_USB_RECORD_REPLAY
4780141cc406Sopenharmony_cistatic void sanei_usb_record_read_int(xmlNode* node,
4781141cc406Sopenharmony_ci                                      SANE_Int dn, SANE_Byte* buffer,
4782141cc406Sopenharmony_ci                                      size_t size, ssize_t read_size)
4783141cc406Sopenharmony_ci{
4784141cc406Sopenharmony_ci  (void) size;
4785141cc406Sopenharmony_ci
4786141cc406Sopenharmony_ci  int node_was_null = node == NULL;
4787141cc406Sopenharmony_ci  if (node_was_null)
4788141cc406Sopenharmony_ci    node = testing_append_commands_node;
4789141cc406Sopenharmony_ci
4790141cc406Sopenharmony_ci  xmlNode* e_tx = xmlNewNode(NULL, (const xmlChar*)"interrupt_tx");
4791141cc406Sopenharmony_ci
4792141cc406Sopenharmony_ci  sanei_xml_command_common_props(e_tx, devices[dn].int_in_ep & 0x0f, "IN");
4793141cc406Sopenharmony_ci
4794141cc406Sopenharmony_ci  if (buffer == NULL)
4795141cc406Sopenharmony_ci    {
4796141cc406Sopenharmony_ci      const int buf_size = 128;
4797141cc406Sopenharmony_ci      char buf[buf_size];
4798141cc406Sopenharmony_ci      snprintf(buf, buf_size, "(unknown read of wanted size %ld)", read_size);
4799141cc406Sopenharmony_ci      xmlNode* e_content = xmlNewText((const xmlChar*)buf);
4800141cc406Sopenharmony_ci      xmlAddChild(e_tx, e_content);
4801141cc406Sopenharmony_ci    }
4802141cc406Sopenharmony_ci  else
4803141cc406Sopenharmony_ci    {
4804141cc406Sopenharmony_ci      if (read_size >= 0)
4805141cc406Sopenharmony_ci        {
4806141cc406Sopenharmony_ci          sanei_xml_set_hex_data(e_tx, (const char*)buffer, read_size);
4807141cc406Sopenharmony_ci        }
4808141cc406Sopenharmony_ci      else
4809141cc406Sopenharmony_ci        {
4810141cc406Sopenharmony_ci          xmlNewProp(e_tx, (const xmlChar*)"error", (const xmlChar*)"timeout");
4811141cc406Sopenharmony_ci        }
4812141cc406Sopenharmony_ci    }
4813141cc406Sopenharmony_ci
4814141cc406Sopenharmony_ci  node = sanei_xml_append_command(node, node_was_null, e_tx);
4815141cc406Sopenharmony_ci
4816141cc406Sopenharmony_ci  if (node_was_null)
4817141cc406Sopenharmony_ci    testing_append_commands_node = node;
4818141cc406Sopenharmony_ci}
4819141cc406Sopenharmony_ci
4820141cc406Sopenharmony_cistatic void sanei_usb_record_replace_read_int(xmlNode* node,
4821141cc406Sopenharmony_ci                                              SANE_Int dn, SANE_Byte* buffer,
4822141cc406Sopenharmony_ci                                              size_t size, size_t read_size)
4823141cc406Sopenharmony_ci{
4824141cc406Sopenharmony_ci  if (!testing_development_mode)
4825141cc406Sopenharmony_ci    return;
4826141cc406Sopenharmony_ci  testing_known_commands_input_failed = 1;
4827141cc406Sopenharmony_ci  testing_last_known_seq--;
4828141cc406Sopenharmony_ci  sanei_usb_record_read_int(node, dn, buffer, size, read_size);
4829141cc406Sopenharmony_ci  xmlUnlinkNode(node);
4830141cc406Sopenharmony_ci  xmlFreeNode(node);
4831141cc406Sopenharmony_ci}
4832141cc406Sopenharmony_ci
4833141cc406Sopenharmony_cistatic int sanei_usb_replay_read_int(SANE_Int dn, SANE_Byte* buffer,
4834141cc406Sopenharmony_ci                                     size_t size)
4835141cc406Sopenharmony_ci{
4836141cc406Sopenharmony_ci  if (testing_known_commands_input_failed)
4837141cc406Sopenharmony_ci    return -1;
4838141cc406Sopenharmony_ci
4839141cc406Sopenharmony_ci  size_t wanted_size = size;
4840141cc406Sopenharmony_ci
4841141cc406Sopenharmony_ci  xmlNode* node = sanei_xml_get_next_tx_node();
4842141cc406Sopenharmony_ci  if (node == NULL)
4843141cc406Sopenharmony_ci    {
4844141cc406Sopenharmony_ci      FAIL_TEST(__func__, "no more transactions\n");
4845141cc406Sopenharmony_ci      return -1;
4846141cc406Sopenharmony_ci    }
4847141cc406Sopenharmony_ci
4848141cc406Sopenharmony_ci  if (sanei_xml_is_known_commands_end(node))
4849141cc406Sopenharmony_ci    {
4850141cc406Sopenharmony_ci      sanei_usb_record_read_int(NULL, dn, NULL, 0, size);
4851141cc406Sopenharmony_ci      testing_known_commands_input_failed = 1;
4852141cc406Sopenharmony_ci      return -1;
4853141cc406Sopenharmony_ci    }
4854141cc406Sopenharmony_ci
4855141cc406Sopenharmony_ci  sanei_xml_record_seq(node);
4856141cc406Sopenharmony_ci  sanei_xml_break_if_needed(node);
4857141cc406Sopenharmony_ci
4858141cc406Sopenharmony_ci  if (xmlStrcmp(node->name, (const xmlChar*)"interrupt_tx") != 0)
4859141cc406Sopenharmony_ci    {
4860141cc406Sopenharmony_ci      FAIL_TEST_TX(__func__, node, "unexpected transaction type %s\n",
4861141cc406Sopenharmony_ci                   (const char*) node->name);
4862141cc406Sopenharmony_ci      sanei_usb_record_replace_read_int(node, dn, NULL, 0, size);
4863141cc406Sopenharmony_ci      return -1;
4864141cc406Sopenharmony_ci    }
4865141cc406Sopenharmony_ci
4866141cc406Sopenharmony_ci  if (!sanei_usb_check_attr(node, "direction", "IN", __func__))
4867141cc406Sopenharmony_ci    {
4868141cc406Sopenharmony_ci      sanei_usb_record_replace_read_int(node, dn, NULL, 0, size);
4869141cc406Sopenharmony_ci      return -1;
4870141cc406Sopenharmony_ci    }
4871141cc406Sopenharmony_ci
4872141cc406Sopenharmony_ci  if (!sanei_usb_check_attr_uint(node, "endpoint_number",
4873141cc406Sopenharmony_ci                                 devices[dn].int_in_ep & 0x0f,
4874141cc406Sopenharmony_ci                                 __func__))
4875141cc406Sopenharmony_ci    {
4876141cc406Sopenharmony_ci      sanei_usb_record_replace_read_int(node, dn, NULL, 0, size);
4877141cc406Sopenharmony_ci      return -1;
4878141cc406Sopenharmony_ci    }
4879141cc406Sopenharmony_ci
4880141cc406Sopenharmony_ci  if (sanei_usb_check_attr(node, "error", "timeout", __func__))
4881141cc406Sopenharmony_ci    {
4882141cc406Sopenharmony_ci      return -1;
4883141cc406Sopenharmony_ci    }
4884141cc406Sopenharmony_ci
4885141cc406Sopenharmony_ci  size_t tx_data_size = 0;
4886141cc406Sopenharmony_ci  char* tx_data = sanei_xml_get_hex_data(node, &tx_data_size);
4887141cc406Sopenharmony_ci
4888141cc406Sopenharmony_ci  if (tx_data_size > wanted_size)
4889141cc406Sopenharmony_ci    {
4890141cc406Sopenharmony_ci      FAIL_TEST_TX(__func__, node,
4891141cc406Sopenharmony_ci                   "got more data than wanted (%lu vs %lu)\n",
4892141cc406Sopenharmony_ci                   tx_data_size, wanted_size);
4893141cc406Sopenharmony_ci      sanei_usb_record_replace_read_int(node, dn, NULL, 0, size);
4894141cc406Sopenharmony_ci      free(tx_data);
4895141cc406Sopenharmony_ci      return -1;
4896141cc406Sopenharmony_ci    }
4897141cc406Sopenharmony_ci
4898141cc406Sopenharmony_ci  memcpy((char*) buffer, tx_data, tx_data_size);
4899141cc406Sopenharmony_ci  free(tx_data);
4900141cc406Sopenharmony_ci  return tx_data_size;
4901141cc406Sopenharmony_ci}
4902141cc406Sopenharmony_ci#endif // WITH_USB_RECORD_REPLAY
4903141cc406Sopenharmony_ci
4904141cc406Sopenharmony_ciSANE_Status
4905141cc406Sopenharmony_cisanei_usb_read_int (SANE_Int dn, SANE_Byte * buffer, size_t * size)
4906141cc406Sopenharmony_ci{
4907141cc406Sopenharmony_ci  ssize_t read_size = 0;
4908141cc406Sopenharmony_ci#if defined(HAVE_LIBUSB_LEGACY) || defined(HAVE_LIBUSB) || defined(HAVE_USB_MANAGER)
4909141cc406Sopenharmony_ci  SANE_Bool stalled = SANE_FALSE;
4910141cc406Sopenharmony_ci#endif
4911141cc406Sopenharmony_ci
4912141cc406Sopenharmony_ci  if (!size)
4913141cc406Sopenharmony_ci    {
4914141cc406Sopenharmony_ci      DBG (1, "sanei_usb_read_int: size == NULL\n");
4915141cc406Sopenharmony_ci      return SANE_STATUS_INVAL;
4916141cc406Sopenharmony_ci    }
4917141cc406Sopenharmony_ci
4918141cc406Sopenharmony_ci  if (dn >= device_number || dn < 0)
4919141cc406Sopenharmony_ci    {
4920141cc406Sopenharmony_ci      DBG (1, "sanei_usb_read_int: dn >= device number || dn < 0\n");
4921141cc406Sopenharmony_ci      return SANE_STATUS_INVAL;
4922141cc406Sopenharmony_ci    }
4923141cc406Sopenharmony_ci
4924141cc406Sopenharmony_ci  DBG (5, "sanei_usb_read_int: trying to read %lu bytes\n",
4925141cc406Sopenharmony_ci       (unsigned long) *size);
4926141cc406Sopenharmony_ci  if (testing_mode == sanei_usb_testing_mode_replay)
4927141cc406Sopenharmony_ci    {
4928141cc406Sopenharmony_ci#if WITH_USB_RECORD_REPLAY
4929141cc406Sopenharmony_ci      read_size = sanei_usb_replay_read_int(dn, buffer, *size);
4930141cc406Sopenharmony_ci#else
4931141cc406Sopenharmony_ci      DBG (1, "USB record-replay mode support is missing\n");
4932141cc406Sopenharmony_ci      return SANE_STATUS_UNSUPPORTED;
4933141cc406Sopenharmony_ci#endif
4934141cc406Sopenharmony_ci    }
4935141cc406Sopenharmony_ci  else if (devices[dn].method == sanei_usb_method_scanner_driver)
4936141cc406Sopenharmony_ci    {
4937141cc406Sopenharmony_ci      DBG (1, "sanei_usb_read_int: access method %d not implemented\n",
4938141cc406Sopenharmony_ci	   devices[dn].method);
4939141cc406Sopenharmony_ci      return SANE_STATUS_INVAL;
4940141cc406Sopenharmony_ci    }
4941141cc406Sopenharmony_ci  else if (devices[dn].method == sanei_usb_method_libusb)
4942141cc406Sopenharmony_ci#ifdef HAVE_LIBUSB_LEGACY
4943141cc406Sopenharmony_ci    {
4944141cc406Sopenharmony_ci      if (devices[dn].int_in_ep)
4945141cc406Sopenharmony_ci	{
4946141cc406Sopenharmony_ci	  read_size = usb_interrupt_read (devices[dn].libusb_handle,
4947141cc406Sopenharmony_ci					  devices[dn].int_in_ep,
4948141cc406Sopenharmony_ci					  (char *) buffer, (int) *size,
4949141cc406Sopenharmony_ci					  libusb_timeout);
4950141cc406Sopenharmony_ci
4951141cc406Sopenharmony_ci	  if (read_size < 0)
4952141cc406Sopenharmony_ci	    DBG (1, "sanei_usb_read_int: read failed: %s\n",
4953141cc406Sopenharmony_ci		 strerror (errno));
4954141cc406Sopenharmony_ci
4955141cc406Sopenharmony_ci	  stalled = (read_size == -EPIPE);
4956141cc406Sopenharmony_ci	}
4957141cc406Sopenharmony_ci      else
4958141cc406Sopenharmony_ci	{
4959141cc406Sopenharmony_ci	  DBG (1, "sanei_usb_read_int: can't read without an int "
4960141cc406Sopenharmony_ci	       "endpoint\n");
4961141cc406Sopenharmony_ci	  return SANE_STATUS_INVAL;
4962141cc406Sopenharmony_ci	}
4963141cc406Sopenharmony_ci    }
4964141cc406Sopenharmony_ci#elif defined(HAVE_LIBUSB)
4965141cc406Sopenharmony_ci    {
4966141cc406Sopenharmony_ci      if (devices[dn].int_in_ep)
4967141cc406Sopenharmony_ci	{
4968141cc406Sopenharmony_ci	  int ret;
4969141cc406Sopenharmony_ci	  int trans_bytes;
4970141cc406Sopenharmony_ci	  ret = libusb_interrupt_transfer (devices[dn].lu_handle,
4971141cc406Sopenharmony_ci					   devices[dn].int_in_ep,
4972141cc406Sopenharmony_ci					   buffer, (int) *size,
4973141cc406Sopenharmony_ci					   &trans_bytes, libusb_timeout);
4974141cc406Sopenharmony_ci
4975141cc406Sopenharmony_ci	  if (ret < 0)
4976141cc406Sopenharmony_ci	    read_size = -1;
4977141cc406Sopenharmony_ci	  else
4978141cc406Sopenharmony_ci	    read_size = trans_bytes;
4979141cc406Sopenharmony_ci
4980141cc406Sopenharmony_ci	  stalled = (ret == LIBUSB_ERROR_PIPE);
4981141cc406Sopenharmony_ci	}
4982141cc406Sopenharmony_ci      else
4983141cc406Sopenharmony_ci	{
4984141cc406Sopenharmony_ci	  DBG (1, "sanei_usb_read_int: can't read without an int "
4985141cc406Sopenharmony_ci	       "endpoint\n");
4986141cc406Sopenharmony_ci	  return SANE_STATUS_INVAL;
4987141cc406Sopenharmony_ci	}
4988141cc406Sopenharmony_ci    }
4989141cc406Sopenharmony_ci#elif defined(HAVE_USB_MANAGER)
4990141cc406Sopenharmony_ci    {
4991141cc406Sopenharmony_ci      if (devices[dn].int_in_ep)
4992141cc406Sopenharmony_ci	{
4993141cc406Sopenharmony_ci	  int ret;
4994141cc406Sopenharmony_ci	  int trans_bytes;
4995141cc406Sopenharmony_ci	  ret = usb_manager_interrupt_transfer (devices[dn].usb_manager_handle,
4996141cc406Sopenharmony_ci					   devices[dn].int_in_ep,
4997141cc406Sopenharmony_ci					   buffer, (int) *size,
4998141cc406Sopenharmony_ci					   &trans_bytes, usb_manager_timeout);
4999141cc406Sopenharmony_ci
5000141cc406Sopenharmony_ci	  if (ret < 0)
5001141cc406Sopenharmony_ci	    read_size = -1;
5002141cc406Sopenharmony_ci	  else
5003141cc406Sopenharmony_ci	    read_size = trans_bytes;
5004141cc406Sopenharmony_ci
5005141cc406Sopenharmony_ci	  stalled = (ret == USB_MANAGER_ERROR_PIPE);
5006141cc406Sopenharmony_ci	}
5007141cc406Sopenharmony_ci      else
5008141cc406Sopenharmony_ci	{
5009141cc406Sopenharmony_ci	  DBG (1, "sanei_usb_read_int: can't read without an int "
5010141cc406Sopenharmony_ci	       "endpoint\n");
5011141cc406Sopenharmony_ci	  return SANE_STATUS_INVAL;
5012141cc406Sopenharmony_ci	}
5013141cc406Sopenharmony_ci    }
5014141cc406Sopenharmony_ci#else /* not HAVE_LIBUSB_LEGACY && not HAVE_LIBUSB && not HAVE_USB_MANAGER*/
5015141cc406Sopenharmony_ci    {
5016141cc406Sopenharmony_ci      DBG (1, "sanei_usb_read_int: libusb support missing\n");
5017141cc406Sopenharmony_ci      return SANE_STATUS_UNSUPPORTED;
5018141cc406Sopenharmony_ci    }
5019141cc406Sopenharmony_ci#endif /* not HAVE_LIBUSB_LEGACY && not HAVE_LIBUSB && not HAVE_USB_MANAGER */
5020141cc406Sopenharmony_ci  else if (devices[dn].method == sanei_usb_method_usbcalls)
5021141cc406Sopenharmony_ci    {
5022141cc406Sopenharmony_ci#ifdef HAVE_USBCALLS
5023141cc406Sopenharmony_ci      int rc;
5024141cc406Sopenharmony_ci      USHORT usNumBytes=*size;
5025141cc406Sopenharmony_ci      DBG (5, "Entered usbcalls UsbIrqStart with dn = %d\n",dn);
5026141cc406Sopenharmony_ci      DBG (5, "Entered usbcalls UsbIrqStart with dh = %p\n",dh);
5027141cc406Sopenharmony_ci      DBG (5, "Entered usbcalls UsbIrqStart with int_in_ep = 0x%02x\n",devices[dn].int_in_ep);
5028141cc406Sopenharmony_ci      DBG (5, "Entered usbcalls UsbIrqStart with interface_nr = %d\n",devices[dn].interface_nr);
5029141cc406Sopenharmony_ci      DBG (5, "Entered usbcalls UsbIrqStart with bytes to read = %u\n",usNumBytes);
5030141cc406Sopenharmony_ci
5031141cc406Sopenharmony_ci      if (devices[dn].int_in_ep){
5032141cc406Sopenharmony_ci         rc = UsbIrqStart (dh,devices[dn].int_in_ep,devices[dn].interface_nr,
5033141cc406Sopenharmony_ci			usNumBytes, (char *) buffer, pUsbIrqStartHev);
5034141cc406Sopenharmony_ci         DBG (5, "rc of UsbIrqStart = %d\n",rc);
5035141cc406Sopenharmony_ci        }
5036141cc406Sopenharmony_ci      else
5037141cc406Sopenharmony_ci	{
5038141cc406Sopenharmony_ci	  DBG (1, "sanei_usb_read_int: can't read without an int "
5039141cc406Sopenharmony_ci	       "endpoint\n");
5040141cc406Sopenharmony_ci	  return SANE_STATUS_INVAL;
5041141cc406Sopenharmony_ci	}
5042141cc406Sopenharmony_ci      if (rc) return SANE_STATUS_INVAL;
5043141cc406Sopenharmony_ci      read_size += usNumBytes;
5044141cc406Sopenharmony_ci#else
5045141cc406Sopenharmony_ci      DBG (1, "sanei_usb_read_int: usbcalls support missing\n");
5046141cc406Sopenharmony_ci      return SANE_STATUS_UNSUPPORTED;
5047141cc406Sopenharmony_ci#endif /* HAVE_USBCALLS */
5048141cc406Sopenharmony_ci    }
5049141cc406Sopenharmony_ci  else
5050141cc406Sopenharmony_ci    {
5051141cc406Sopenharmony_ci      DBG (1, "sanei_usb_read_int: access method %d not implemented\n",
5052141cc406Sopenharmony_ci	   devices[dn].method);
5053141cc406Sopenharmony_ci      return SANE_STATUS_INVAL;
5054141cc406Sopenharmony_ci    }
5055141cc406Sopenharmony_ci
5056141cc406Sopenharmony_ci  if (testing_mode == sanei_usb_testing_mode_record)
5057141cc406Sopenharmony_ci    {
5058141cc406Sopenharmony_ci#if WITH_USB_RECORD_REPLAY
5059141cc406Sopenharmony_ci      sanei_usb_record_read_int(NULL, dn, buffer, *size, read_size);
5060141cc406Sopenharmony_ci#else
5061141cc406Sopenharmony_ci      DBG (1, "USB record-replay mode support is missing\n");
5062141cc406Sopenharmony_ci      return SANE_STATUS_UNSUPPORTED;
5063141cc406Sopenharmony_ci#endif
5064141cc406Sopenharmony_ci    }
5065141cc406Sopenharmony_ci
5066141cc406Sopenharmony_ci  if (read_size < 0)
5067141cc406Sopenharmony_ci    {
5068141cc406Sopenharmony_ci      *size = 0;
5069141cc406Sopenharmony_ci      if (testing_mode != sanei_usb_testing_mode_disabled)
5070141cc406Sopenharmony_ci        return SANE_STATUS_IO_ERROR;
5071141cc406Sopenharmony_ci
5072141cc406Sopenharmony_ci#ifdef HAVE_LIBUSB_LEGACY
5073141cc406Sopenharmony_ci      if (devices[dn].method == sanei_usb_method_libusb)
5074141cc406Sopenharmony_ci        if (stalled)
5075141cc406Sopenharmony_ci	  usb_clear_halt (devices[dn].libusb_handle, devices[dn].int_in_ep);
5076141cc406Sopenharmony_ci#elif defined(HAVE_LIBUSB)
5077141cc406Sopenharmony_ci      if (devices[dn].method == sanei_usb_method_libusb)
5078141cc406Sopenharmony_ci        if (stalled)
5079141cc406Sopenharmony_ci	  libusb_clear_halt (devices[dn].lu_handle, devices[dn].int_in_ep);
5080141cc406Sopenharmony_ci#elif defined(HAVE_USB_MANAGER)
5081141cc406Sopenharmony_ci      if (devices[dn].method == sanei_usb_method_usb_manager)
5082141cc406Sopenharmony_ci        if (stalled)
5083141cc406Sopenharmony_ci	  usb_manager_clear_halt (devices[dn].usb_manager_handle, devices[dn].int_in_ep);
5084141cc406Sopenharmony_ci#endif
5085141cc406Sopenharmony_ci      return SANE_STATUS_IO_ERROR;
5086141cc406Sopenharmony_ci    }
5087141cc406Sopenharmony_ci  if (read_size == 0)
5088141cc406Sopenharmony_ci    {
5089141cc406Sopenharmony_ci      DBG (3, "sanei_usb_read_int: read returned EOF\n");
5090141cc406Sopenharmony_ci      *size = 0;
5091141cc406Sopenharmony_ci      return SANE_STATUS_EOF;
5092141cc406Sopenharmony_ci    }
5093141cc406Sopenharmony_ci  DBG (5, "sanei_usb_read_int: wanted %lu bytes, got %ld bytes\n",
5094141cc406Sopenharmony_ci       (unsigned long) *size, (unsigned long) read_size);
5095141cc406Sopenharmony_ci  *size = read_size;
5096141cc406Sopenharmony_ci  if (debug_level > 10)
5097141cc406Sopenharmony_ci    print_buffer (buffer, read_size);
5098141cc406Sopenharmony_ci
5099141cc406Sopenharmony_ci  return SANE_STATUS_GOOD;
5100141cc406Sopenharmony_ci}
5101141cc406Sopenharmony_ci
5102141cc406Sopenharmony_ci#if WITH_USB_RECORD_REPLAY
5103141cc406Sopenharmony_cistatic SANE_Status sanei_usb_replay_set_configuration(SANE_Int dn,
5104141cc406Sopenharmony_ci                                                      SANE_Int configuration)
5105141cc406Sopenharmony_ci{
5106141cc406Sopenharmony_ci  (void) dn;
5107141cc406Sopenharmony_ci
5108141cc406Sopenharmony_ci  xmlNode* node = sanei_xml_get_next_tx_node();
5109141cc406Sopenharmony_ci  if (node == NULL)
5110141cc406Sopenharmony_ci    {
5111141cc406Sopenharmony_ci      FAIL_TEST(__func__, "no more transactions\n");
5112141cc406Sopenharmony_ci      return SANE_STATUS_IO_ERROR;
5113141cc406Sopenharmony_ci    }
5114141cc406Sopenharmony_ci
5115141cc406Sopenharmony_ci  sanei_xml_record_seq(node);
5116141cc406Sopenharmony_ci  sanei_xml_break_if_needed(node);
5117141cc406Sopenharmony_ci
5118141cc406Sopenharmony_ci  if (xmlStrcmp(node->name, (const xmlChar*)"control_tx") != 0)
5119141cc406Sopenharmony_ci    {
5120141cc406Sopenharmony_ci      FAIL_TEST_TX(__func__, node, "unexpected transaction type %s\n",
5121141cc406Sopenharmony_ci                   (const char*) node->name);
5122141cc406Sopenharmony_ci      return SANE_STATUS_IO_ERROR;
5123141cc406Sopenharmony_ci    }
5124141cc406Sopenharmony_ci
5125141cc406Sopenharmony_ci  if (!sanei_usb_check_attr(node, "direction", "OUT", __func__))
5126141cc406Sopenharmony_ci    return SANE_STATUS_IO_ERROR;
5127141cc406Sopenharmony_ci
5128141cc406Sopenharmony_ci  if (!sanei_usb_check_attr_uint(node, "bmRequestType", 0, __func__))
5129141cc406Sopenharmony_ci    return SANE_STATUS_IO_ERROR;
5130141cc406Sopenharmony_ci
5131141cc406Sopenharmony_ci  if (!sanei_usb_check_attr_uint(node, "bRequest", 9, __func__))
5132141cc406Sopenharmony_ci    return SANE_STATUS_IO_ERROR;
5133141cc406Sopenharmony_ci
5134141cc406Sopenharmony_ci  if (!sanei_usb_check_attr_uint(node, "wValue", configuration, __func__))
5135141cc406Sopenharmony_ci    return SANE_STATUS_IO_ERROR;
5136141cc406Sopenharmony_ci
5137141cc406Sopenharmony_ci  if (!sanei_usb_check_attr_uint(node, "wIndex", 0, __func__))
5138141cc406Sopenharmony_ci    return SANE_STATUS_IO_ERROR;
5139141cc406Sopenharmony_ci
5140141cc406Sopenharmony_ci  if (!sanei_usb_check_attr_uint(node, "wLength", 0, __func__))
5141141cc406Sopenharmony_ci    return SANE_STATUS_IO_ERROR;
5142141cc406Sopenharmony_ci
5143141cc406Sopenharmony_ci  return SANE_STATUS_GOOD;
5144141cc406Sopenharmony_ci}
5145141cc406Sopenharmony_ci
5146141cc406Sopenharmony_cistatic void sanei_usb_record_set_configuration(SANE_Int dn,
5147141cc406Sopenharmony_ci                                               SANE_Int configuration)
5148141cc406Sopenharmony_ci{
5149141cc406Sopenharmony_ci  (void) dn; (void) configuration;
5150141cc406Sopenharmony_ci  // TODO
5151141cc406Sopenharmony_ci}
5152141cc406Sopenharmony_ci#endif // WITH_USB_RECORD_REPLAY
5153141cc406Sopenharmony_ci
5154141cc406Sopenharmony_ciSANE_Status
5155141cc406Sopenharmony_cisanei_usb_set_configuration (SANE_Int dn, SANE_Int configuration)
5156141cc406Sopenharmony_ci{
5157141cc406Sopenharmony_ci  if (dn >= device_number || dn < 0)
5158141cc406Sopenharmony_ci    {
5159141cc406Sopenharmony_ci      DBG (1,
5160141cc406Sopenharmony_ci	   "sanei_usb_set_configuration: dn >= device number || dn < 0, dn=%d\n",
5161141cc406Sopenharmony_ci	   dn);
5162141cc406Sopenharmony_ci      return SANE_STATUS_INVAL;
5163141cc406Sopenharmony_ci    }
5164141cc406Sopenharmony_ci
5165141cc406Sopenharmony_ci  DBG (5, "sanei_usb_set_configuration: configuration = %d\n", configuration);
5166141cc406Sopenharmony_ci
5167141cc406Sopenharmony_ci  if (testing_mode == sanei_usb_testing_mode_record)
5168141cc406Sopenharmony_ci    {
5169141cc406Sopenharmony_ci#if WITH_USB_RECORD_REPLAY
5170141cc406Sopenharmony_ci      sanei_usb_record_set_configuration(dn, configuration);
5171141cc406Sopenharmony_ci#else
5172141cc406Sopenharmony_ci      DBG (1, "USB record-replay mode support is missing\n");
5173141cc406Sopenharmony_ci      return SANE_STATUS_UNSUPPORTED;
5174141cc406Sopenharmony_ci#endif
5175141cc406Sopenharmony_ci    }
5176141cc406Sopenharmony_ci
5177141cc406Sopenharmony_ci  if (testing_mode == sanei_usb_testing_mode_replay)
5178141cc406Sopenharmony_ci    {
5179141cc406Sopenharmony_ci#if WITH_USB_RECORD_REPLAY
5180141cc406Sopenharmony_ci      return sanei_usb_replay_set_configuration(dn, configuration);
5181141cc406Sopenharmony_ci#else
5182141cc406Sopenharmony_ci      DBG (1, "USB record-replay mode support is missing\n");
5183141cc406Sopenharmony_ci      return SANE_STATUS_UNSUPPORTED;
5184141cc406Sopenharmony_ci#endif
5185141cc406Sopenharmony_ci    }
5186141cc406Sopenharmony_ci  else if (devices[dn].method == sanei_usb_method_scanner_driver)
5187141cc406Sopenharmony_ci    {
5188141cc406Sopenharmony_ci#if defined(__linux__)
5189141cc406Sopenharmony_ci      return SANE_STATUS_GOOD;
5190141cc406Sopenharmony_ci#else /* not __linux__ */
5191141cc406Sopenharmony_ci      DBG (5, "sanei_usb_set_configuration: not supported on this OS\n");
5192141cc406Sopenharmony_ci      return SANE_STATUS_UNSUPPORTED;
5193141cc406Sopenharmony_ci#endif /* not __linux__ */
5194141cc406Sopenharmony_ci    }
5195141cc406Sopenharmony_ci  else if (devices[dn].method == sanei_usb_method_libusb)
5196141cc406Sopenharmony_ci#ifdef HAVE_LIBUSB_LEGACY
5197141cc406Sopenharmony_ci    {
5198141cc406Sopenharmony_ci      int result;
5199141cc406Sopenharmony_ci
5200141cc406Sopenharmony_ci      result =
5201141cc406Sopenharmony_ci	usb_set_configuration (devices[dn].libusb_handle, configuration);
5202141cc406Sopenharmony_ci      if (result < 0)
5203141cc406Sopenharmony_ci	{
5204141cc406Sopenharmony_ci	  DBG (1, "sanei_usb_set_configuration: libusb complained: %s\n",
5205141cc406Sopenharmony_ci	       usb_strerror ());
5206141cc406Sopenharmony_ci	  return SANE_STATUS_INVAL;
5207141cc406Sopenharmony_ci	}
5208141cc406Sopenharmony_ci      return SANE_STATUS_GOOD;
5209141cc406Sopenharmony_ci    }
5210141cc406Sopenharmony_ci#elif defined(HAVE_LIBUSB)
5211141cc406Sopenharmony_ci    {
5212141cc406Sopenharmony_ci      int result;
5213141cc406Sopenharmony_ci
5214141cc406Sopenharmony_ci      result = libusb_set_configuration (devices[dn].lu_handle, configuration);
5215141cc406Sopenharmony_ci      if (result < 0)
5216141cc406Sopenharmony_ci	{
5217141cc406Sopenharmony_ci	  DBG (1, "sanei_usb_set_configuration: libusb complained: %s\n",
5218141cc406Sopenharmony_ci	       sanei_libusb_strerror (result));
5219141cc406Sopenharmony_ci	  return SANE_STATUS_INVAL;
5220141cc406Sopenharmony_ci	}
5221141cc406Sopenharmony_ci      return SANE_STATUS_GOOD;
5222141cc406Sopenharmony_ci    }
5223141cc406Sopenharmony_ci#elif defined(HAVE_USB_MANAGER)
5224141cc406Sopenharmony_ci    {
5225141cc406Sopenharmony_ci      int result;
5226141cc406Sopenharmony_ci
5227141cc406Sopenharmony_ci      result = usb_manager_set_configuration (devices[dn].usb_manager_handle, configuration);
5228141cc406Sopenharmony_ci      if (result < 0)
5229141cc406Sopenharmony_ci	{
5230141cc406Sopenharmony_ci	  DBG (1, "sanei_usb_set_configuration: libusb complained: %s\n",
5231141cc406Sopenharmony_ci	       sanei_usb_manager_strerror (result));
5232141cc406Sopenharmony_ci	  return SANE_STATUS_INVAL;
5233141cc406Sopenharmony_ci	}
5234141cc406Sopenharmony_ci      return SANE_STATUS_GOOD;
5235141cc406Sopenharmony_ci    }
5236141cc406Sopenharmony_ci#else /* not HAVE_LIBUSB_LEGACY && not HAVE_LIBUSB && not HAVE_USB_MANAGER */
5237141cc406Sopenharmony_ci    {
5238141cc406Sopenharmony_ci      DBG (1, "sanei_usb_set_configuration: libusb support missing\n");
5239141cc406Sopenharmony_ci      return SANE_STATUS_UNSUPPORTED;
5240141cc406Sopenharmony_ci    }
5241141cc406Sopenharmony_ci#endif /* not HAVE_LIBUSB_LEGACY && not HAVE_LIBUSB && not HAVE_USB_MANAGER */
5242141cc406Sopenharmony_ci  else
5243141cc406Sopenharmony_ci    {
5244141cc406Sopenharmony_ci      DBG (1,
5245141cc406Sopenharmony_ci	   "sanei_usb_set_configuration: access method %d not implemented\n",
5246141cc406Sopenharmony_ci	   devices[dn].method);
5247141cc406Sopenharmony_ci      return SANE_STATUS_UNSUPPORTED;
5248141cc406Sopenharmony_ci    }
5249141cc406Sopenharmony_ci}
5250141cc406Sopenharmony_ci
5251141cc406Sopenharmony_ciSANE_Status
5252141cc406Sopenharmony_cisanei_usb_claim_interface (SANE_Int dn, SANE_Int interface_number)
5253141cc406Sopenharmony_ci{
5254141cc406Sopenharmony_ci  if (dn >= device_number || dn < 0)
5255141cc406Sopenharmony_ci    {
5256141cc406Sopenharmony_ci      DBG (1,
5257141cc406Sopenharmony_ci	   "sanei_usb_claim_interface: dn >= device number || dn < 0, dn=%d\n",
5258141cc406Sopenharmony_ci	   dn);
5259141cc406Sopenharmony_ci      return SANE_STATUS_INVAL;
5260141cc406Sopenharmony_ci    }
5261141cc406Sopenharmony_ci  if (devices[dn].missing)
5262141cc406Sopenharmony_ci    {
5263141cc406Sopenharmony_ci      DBG (1, "sanei_usb_claim_interface: device dn=%d is missing\n", dn);
5264141cc406Sopenharmony_ci      return SANE_STATUS_INVAL;
5265141cc406Sopenharmony_ci    }
5266141cc406Sopenharmony_ci
5267141cc406Sopenharmony_ci  DBG (5, "sanei_usb_claim_interface: interface_number = %d\n", interface_number);
5268141cc406Sopenharmony_ci
5269141cc406Sopenharmony_ci  if (testing_mode == sanei_usb_testing_mode_replay)
5270141cc406Sopenharmony_ci    {
5271141cc406Sopenharmony_ci      return SANE_STATUS_GOOD;
5272141cc406Sopenharmony_ci    }
5273141cc406Sopenharmony_ci  else if (devices[dn].method == sanei_usb_method_scanner_driver)
5274141cc406Sopenharmony_ci    {
5275141cc406Sopenharmony_ci#if defined(__linux__)
5276141cc406Sopenharmony_ci      return SANE_STATUS_GOOD;
5277141cc406Sopenharmony_ci#else /* not __linux__ */
5278141cc406Sopenharmony_ci      DBG (5, "sanei_usb_claim_interface: not supported on this OS\n");
5279141cc406Sopenharmony_ci      return SANE_STATUS_UNSUPPORTED;
5280141cc406Sopenharmony_ci#endif /* not __linux__ */
5281141cc406Sopenharmony_ci    }
5282141cc406Sopenharmony_ci  else if (devices[dn].method == sanei_usb_method_libusb)
5283141cc406Sopenharmony_ci#ifdef HAVE_LIBUSB_LEGACY
5284141cc406Sopenharmony_ci    {
5285141cc406Sopenharmony_ci      int result;
5286141cc406Sopenharmony_ci
5287141cc406Sopenharmony_ci      result = usb_claim_interface (devices[dn].libusb_handle, interface_number);
5288141cc406Sopenharmony_ci      if (result < 0)
5289141cc406Sopenharmony_ci	{
5290141cc406Sopenharmony_ci	  DBG (1, "sanei_usb_claim_interface: libusb complained: %s\n",
5291141cc406Sopenharmony_ci	       usb_strerror ());
5292141cc406Sopenharmony_ci	  return SANE_STATUS_INVAL;
5293141cc406Sopenharmony_ci	}
5294141cc406Sopenharmony_ci      return SANE_STATUS_GOOD;
5295141cc406Sopenharmony_ci    }
5296141cc406Sopenharmony_ci#elif defined(HAVE_LIBUSB)
5297141cc406Sopenharmony_ci    {
5298141cc406Sopenharmony_ci      int result;
5299141cc406Sopenharmony_ci
5300141cc406Sopenharmony_ci      result = libusb_claim_interface (devices[dn].lu_handle, interface_number);
5301141cc406Sopenharmony_ci      if (result < 0)
5302141cc406Sopenharmony_ci	{
5303141cc406Sopenharmony_ci	  DBG (1, "sanei_usb_claim_interface: libusb complained: %s\n",
5304141cc406Sopenharmony_ci	       sanei_libusb_strerror (result));
5305141cc406Sopenharmony_ci	  return SANE_STATUS_INVAL;
5306141cc406Sopenharmony_ci	}
5307141cc406Sopenharmony_ci      return SANE_STATUS_GOOD;
5308141cc406Sopenharmony_ci    }
5309141cc406Sopenharmony_ci#elif defined(HAVE_USB_MANAGER)
5310141cc406Sopenharmony_ci    {
5311141cc406Sopenharmony_ci      int result;
5312141cc406Sopenharmony_ci
5313141cc406Sopenharmony_ci      result = usb_manager_claim_interface (devices[dn].usb_manager_handle, interface_number);
5314141cc406Sopenharmony_ci      if (result < 0)
5315141cc406Sopenharmony_ci	{
5316141cc406Sopenharmony_ci	  DBG (1, "sanei_usb_claim_interface: libusb complained: %s\n",
5317141cc406Sopenharmony_ci	       sanei_usb_manager_strerror (result));
5318141cc406Sopenharmony_ci	  return SANE_STATUS_INVAL;
5319141cc406Sopenharmony_ci	}
5320141cc406Sopenharmony_ci      return SANE_STATUS_GOOD;
5321141cc406Sopenharmony_ci    }
5322141cc406Sopenharmony_ci#else /* not HAVE_LIBUSB_LEGACY && not HAVE_LIBUSB && not HAVE_USB_MANAGER */
5323141cc406Sopenharmony_ci    {
5324141cc406Sopenharmony_ci      DBG (1, "sanei_usb_claim_interface: libusb support missing\n");
5325141cc406Sopenharmony_ci      return SANE_STATUS_UNSUPPORTED;
5326141cc406Sopenharmony_ci    }
5327141cc406Sopenharmony_ci#endif /* not HAVE_LIBUSB_LEGACY && not HAVE_LIBUSB && not HAVE_USB_MANAGER*/
5328141cc406Sopenharmony_ci  else
5329141cc406Sopenharmony_ci    {
5330141cc406Sopenharmony_ci      DBG (1, "sanei_usb_claim_interface: access method %d not implemented\n",
5331141cc406Sopenharmony_ci	   devices[dn].method);
5332141cc406Sopenharmony_ci      return SANE_STATUS_UNSUPPORTED;
5333141cc406Sopenharmony_ci    }
5334141cc406Sopenharmony_ci}
5335141cc406Sopenharmony_ci
5336141cc406Sopenharmony_ciSANE_Status
5337141cc406Sopenharmony_cisanei_usb_release_interface (SANE_Int dn, SANE_Int interface_number)
5338141cc406Sopenharmony_ci{
5339141cc406Sopenharmony_ci  if (dn >= device_number || dn < 0)
5340141cc406Sopenharmony_ci    {
5341141cc406Sopenharmony_ci      DBG (1,
5342141cc406Sopenharmony_ci	   "sanei_usb_release_interface: dn >= device number || dn < 0, dn=%d\n",
5343141cc406Sopenharmony_ci	   dn);
5344141cc406Sopenharmony_ci      return SANE_STATUS_INVAL;
5345141cc406Sopenharmony_ci    }
5346141cc406Sopenharmony_ci  if (devices[dn].missing)
5347141cc406Sopenharmony_ci    {
5348141cc406Sopenharmony_ci      DBG (1, "sanei_usb_release_interface: device dn=%d is missing\n", dn);
5349141cc406Sopenharmony_ci      return SANE_STATUS_INVAL;
5350141cc406Sopenharmony_ci    }
5351141cc406Sopenharmony_ci  DBG (5, "sanei_usb_release_interface: interface_number = %d\n", interface_number);
5352141cc406Sopenharmony_ci
5353141cc406Sopenharmony_ci  if (testing_mode == sanei_usb_testing_mode_replay)
5354141cc406Sopenharmony_ci    {
5355141cc406Sopenharmony_ci      return SANE_STATUS_GOOD;
5356141cc406Sopenharmony_ci    }
5357141cc406Sopenharmony_ci  else if (devices[dn].method == sanei_usb_method_scanner_driver)
5358141cc406Sopenharmony_ci    {
5359141cc406Sopenharmony_ci#if defined(__linux__)
5360141cc406Sopenharmony_ci      return SANE_STATUS_GOOD;
5361141cc406Sopenharmony_ci#else /* not __linux__ */
5362141cc406Sopenharmony_ci      DBG (5, "sanei_usb_release_interface: not supported on this OS\n");
5363141cc406Sopenharmony_ci      return SANE_STATUS_UNSUPPORTED;
5364141cc406Sopenharmony_ci#endif /* not __linux__ */
5365141cc406Sopenharmony_ci    }
5366141cc406Sopenharmony_ci  else if (devices[dn].method == sanei_usb_method_libusb)
5367141cc406Sopenharmony_ci#ifdef HAVE_LIBUSB_LEGACY
5368141cc406Sopenharmony_ci    {
5369141cc406Sopenharmony_ci      int result;
5370141cc406Sopenharmony_ci
5371141cc406Sopenharmony_ci      result = usb_release_interface (devices[dn].libusb_handle, interface_number);
5372141cc406Sopenharmony_ci      if (result < 0)
5373141cc406Sopenharmony_ci	{
5374141cc406Sopenharmony_ci	  DBG (1, "sanei_usb_release_interface: libusb complained: %s\n",
5375141cc406Sopenharmony_ci	       usb_strerror ());
5376141cc406Sopenharmony_ci	  return SANE_STATUS_INVAL;
5377141cc406Sopenharmony_ci	}
5378141cc406Sopenharmony_ci      return SANE_STATUS_GOOD;
5379141cc406Sopenharmony_ci    }
5380141cc406Sopenharmony_ci#elif defined(HAVE_LIBUSB)
5381141cc406Sopenharmony_ci    {
5382141cc406Sopenharmony_ci      int result;
5383141cc406Sopenharmony_ci
5384141cc406Sopenharmony_ci      result = libusb_release_interface (devices[dn].lu_handle, interface_number);
5385141cc406Sopenharmony_ci      if (result < 0)
5386141cc406Sopenharmony_ci	{
5387141cc406Sopenharmony_ci	  DBG (1, "sanei_usb_release_interface: libusb complained: %s\n",
5388141cc406Sopenharmony_ci	       sanei_libusb_strerror (result));
5389141cc406Sopenharmony_ci	  return SANE_STATUS_INVAL;
5390141cc406Sopenharmony_ci	}
5391141cc406Sopenharmony_ci      return SANE_STATUS_GOOD;
5392141cc406Sopenharmony_ci    }
5393141cc406Sopenharmony_ci#elif defined(HAVE_USB_MANAGER)
5394141cc406Sopenharmony_ci    {
5395141cc406Sopenharmony_ci      int result;
5396141cc406Sopenharmony_ci
5397141cc406Sopenharmony_ci      result = usb_manager_release_interface (devices[dn].usb_manager_handle, interface_number);
5398141cc406Sopenharmony_ci      if (result < 0)
5399141cc406Sopenharmony_ci	{
5400141cc406Sopenharmony_ci	  DBG (1, "sanei_usb_release_interface: libusb complained: %s\n",
5401141cc406Sopenharmony_ci	       sanei_usb_manager_strerror (result));
5402141cc406Sopenharmony_ci	  return SANE_STATUS_INVAL;
5403141cc406Sopenharmony_ci	}
5404141cc406Sopenharmony_ci      return SANE_STATUS_GOOD;
5405141cc406Sopenharmony_ci    }
5406141cc406Sopenharmony_ci#else /* not HAVE_LIBUSB_LEGACY && not HAVE_LIBUSB && not HAVE_USB_MANAGER */
5407141cc406Sopenharmony_ci    {
5408141cc406Sopenharmony_ci      DBG (1, "sanei_usb_release_interface: libusb support missing\n");
5409141cc406Sopenharmony_ci      return SANE_STATUS_UNSUPPORTED;
5410141cc406Sopenharmony_ci    }
5411141cc406Sopenharmony_ci#endif /* not HAVE_LIBUSB_LEGACY && not HAVE_LIBUSB && not HAVE_USB_MANAGER  */
5412141cc406Sopenharmony_ci  else
5413141cc406Sopenharmony_ci    {
5414141cc406Sopenharmony_ci      DBG (1,
5415141cc406Sopenharmony_ci	   "sanei_usb_release_interface: access method %d not implemented\n",
5416141cc406Sopenharmony_ci	   devices[dn].method);
5417141cc406Sopenharmony_ci      return SANE_STATUS_UNSUPPORTED;
5418141cc406Sopenharmony_ci    }
5419141cc406Sopenharmony_ci}
5420141cc406Sopenharmony_ci
5421141cc406Sopenharmony_ciSANE_Status
5422141cc406Sopenharmony_cisanei_usb_set_altinterface (SANE_Int dn, SANE_Int alternate)
5423141cc406Sopenharmony_ci{
5424141cc406Sopenharmony_ci  if (dn >= device_number || dn < 0)
5425141cc406Sopenharmony_ci    {
5426141cc406Sopenharmony_ci      DBG (1,
5427141cc406Sopenharmony_ci	   "sanei_usb_set_altinterface: dn >= device number || dn < 0, dn=%d\n",
5428141cc406Sopenharmony_ci	   dn);
5429141cc406Sopenharmony_ci      return SANE_STATUS_INVAL;
5430141cc406Sopenharmony_ci    }
5431141cc406Sopenharmony_ci
5432141cc406Sopenharmony_ci  DBG (5, "sanei_usb_set_altinterface: alternate = %d\n", alternate);
5433141cc406Sopenharmony_ci
5434141cc406Sopenharmony_ci  devices[dn].alt_setting = alternate;
5435141cc406Sopenharmony_ci
5436141cc406Sopenharmony_ci  if (testing_mode == sanei_usb_testing_mode_replay)
5437141cc406Sopenharmony_ci    {
5438141cc406Sopenharmony_ci      return SANE_STATUS_GOOD;
5439141cc406Sopenharmony_ci    }
5440141cc406Sopenharmony_ci  else if (devices[dn].method == sanei_usb_method_scanner_driver)
5441141cc406Sopenharmony_ci    {
5442141cc406Sopenharmony_ci#if defined(__linux__)
5443141cc406Sopenharmony_ci      return SANE_STATUS_GOOD;
5444141cc406Sopenharmony_ci#else /* not __linux__ */
5445141cc406Sopenharmony_ci      DBG (5, "sanei_usb_set_altinterface: not supported on this OS\n");
5446141cc406Sopenharmony_ci      return SANE_STATUS_UNSUPPORTED;
5447141cc406Sopenharmony_ci#endif /* not __linux__ */
5448141cc406Sopenharmony_ci    }
5449141cc406Sopenharmony_ci  else if (devices[dn].method == sanei_usb_method_libusb)
5450141cc406Sopenharmony_ci#ifdef HAVE_LIBUSB_LEGACY
5451141cc406Sopenharmony_ci    {
5452141cc406Sopenharmony_ci      int result;
5453141cc406Sopenharmony_ci
5454141cc406Sopenharmony_ci      result = usb_set_altinterface (devices[dn].libusb_handle, alternate);
5455141cc406Sopenharmony_ci      if (result < 0)
5456141cc406Sopenharmony_ci	{
5457141cc406Sopenharmony_ci	  DBG (1, "sanei_usb_set_altinterface: libusb complained: %s\n",
5458141cc406Sopenharmony_ci	       usb_strerror ());
5459141cc406Sopenharmony_ci	  return SANE_STATUS_INVAL;
5460141cc406Sopenharmony_ci	}
5461141cc406Sopenharmony_ci      return SANE_STATUS_GOOD;
5462141cc406Sopenharmony_ci    }
5463141cc406Sopenharmony_ci#elif defined(HAVE_LIBUSB)
5464141cc406Sopenharmony_ci    {
5465141cc406Sopenharmony_ci      int result;
5466141cc406Sopenharmony_ci
5467141cc406Sopenharmony_ci      result = libusb_set_interface_alt_setting (devices[dn].lu_handle,
5468141cc406Sopenharmony_ci						 devices[dn].interface_nr, alternate);
5469141cc406Sopenharmony_ci      if (result < 0)
5470141cc406Sopenharmony_ci	{
5471141cc406Sopenharmony_ci	  DBG (1, "sanei_usb_set_altinterface: libusb complained: %s\n",
5472141cc406Sopenharmony_ci	       sanei_libusb_strerror (result));
5473141cc406Sopenharmony_ci	  return SANE_STATUS_INVAL;
5474141cc406Sopenharmony_ci	}
5475141cc406Sopenharmony_ci      return SANE_STATUS_GOOD;
5476141cc406Sopenharmony_ci    }
5477141cc406Sopenharmony_ci#elif defined(HAVE_USB_MANAGER)
5478141cc406Sopenharmony_ci    {
5479141cc406Sopenharmony_ci      int result;
5480141cc406Sopenharmony_ci
5481141cc406Sopenharmony_ci      result = usb_manager_set_interface_alt_setting (devices[dn].usb_manager_handle,
5482141cc406Sopenharmony_ci						 devices[dn].interface_nr, alternate);
5483141cc406Sopenharmony_ci      if (result < 0)
5484141cc406Sopenharmony_ci	{
5485141cc406Sopenharmony_ci	  DBG (1, "sanei_usb_set_altinterface: usb_manager complained: %s\n",
5486141cc406Sopenharmony_ci	       sanei_usb_manager_strerror (result));
5487141cc406Sopenharmony_ci	  return SANE_STATUS_INVAL;
5488141cc406Sopenharmony_ci	}
5489141cc406Sopenharmony_ci      return SANE_STATUS_GOOD;
5490141cc406Sopenharmony_ci    }
5491141cc406Sopenharmony_ci#else /* not HAVE_LIBUSB_LEGACY && not HAVE_LIBUSB && not HAVE_USB_MANAGER*/
5492141cc406Sopenharmony_ci    {
5493141cc406Sopenharmony_ci      DBG (1, "sanei_set_altinterface: libusb support missing\n");
5494141cc406Sopenharmony_ci      return SANE_STATUS_UNSUPPORTED;
5495141cc406Sopenharmony_ci    }
5496141cc406Sopenharmony_ci#endif /* not HAVE_LIBUSB_LEGACY && not HAVE_LIBUSB && not HAVE_USB_MANAGER */
5497141cc406Sopenharmony_ci  else
5498141cc406Sopenharmony_ci    {
5499141cc406Sopenharmony_ci      DBG (1,
5500141cc406Sopenharmony_ci	   "sanei_usb_set_altinterface: access method %d not implemented\n",
5501141cc406Sopenharmony_ci	   devices[dn].method);
5502141cc406Sopenharmony_ci      return SANE_STATUS_UNSUPPORTED;
5503141cc406Sopenharmony_ci    }
5504141cc406Sopenharmony_ci}
5505141cc406Sopenharmony_ci
5506141cc406Sopenharmony_ci#if WITH_USB_RECORD_REPLAY
5507141cc406Sopenharmony_ci
5508141cc406Sopenharmony_cistatic SANE_Status
5509141cc406Sopenharmony_cisanei_usb_replay_get_descriptor(SANE_Int dn,
5510141cc406Sopenharmony_ci                                struct sanei_usb_dev_descriptor *desc)
5511141cc406Sopenharmony_ci{
5512141cc406Sopenharmony_ci  (void) dn;
5513141cc406Sopenharmony_ci
5514141cc406Sopenharmony_ci  if (testing_known_commands_input_failed)
5515141cc406Sopenharmony_ci    return SANE_STATUS_IO_ERROR;
5516141cc406Sopenharmony_ci
5517141cc406Sopenharmony_ci  xmlNode* node = sanei_xml_get_next_tx_node();
5518141cc406Sopenharmony_ci  if (node == NULL)
5519141cc406Sopenharmony_ci    {
5520141cc406Sopenharmony_ci      FAIL_TEST(__func__, "no more transactions\n");
5521141cc406Sopenharmony_ci      return SANE_STATUS_IO_ERROR;
5522141cc406Sopenharmony_ci    }
5523141cc406Sopenharmony_ci
5524141cc406Sopenharmony_ci  if (sanei_xml_is_known_commands_end(node))
5525141cc406Sopenharmony_ci    {
5526141cc406Sopenharmony_ci      testing_known_commands_input_failed = 1;
5527141cc406Sopenharmony_ci      return SANE_STATUS_IO_ERROR;
5528141cc406Sopenharmony_ci    }
5529141cc406Sopenharmony_ci
5530141cc406Sopenharmony_ci  sanei_xml_record_seq(node);
5531141cc406Sopenharmony_ci  sanei_xml_break_if_needed(node);
5532141cc406Sopenharmony_ci
5533141cc406Sopenharmony_ci  if (xmlStrcmp(node->name, (const xmlChar*)"get_descriptor") != 0)
5534141cc406Sopenharmony_ci    {
5535141cc406Sopenharmony_ci      FAIL_TEST_TX(__func__, node, "unexpected transaction type %s\n",
5536141cc406Sopenharmony_ci                   (const char*) node->name);
5537141cc406Sopenharmony_ci      testing_known_commands_input_failed = 1;
5538141cc406Sopenharmony_ci      return SANE_STATUS_IO_ERROR;
5539141cc406Sopenharmony_ci    }
5540141cc406Sopenharmony_ci
5541141cc406Sopenharmony_ci  int desc_type = sanei_xml_get_prop_uint(node, "descriptor_type");
5542141cc406Sopenharmony_ci  int bcd_usb = sanei_xml_get_prop_uint(node, "bcd_usb");
5543141cc406Sopenharmony_ci  int bcd_dev = sanei_xml_get_prop_uint(node, "bcd_device");
5544141cc406Sopenharmony_ci  int dev_class = sanei_xml_get_prop_uint(node, "device_class");
5545141cc406Sopenharmony_ci  int dev_sub_class = sanei_xml_get_prop_uint(node, "device_sub_class");
5546141cc406Sopenharmony_ci  int dev_protocol = sanei_xml_get_prop_uint(node, "device_protocol");
5547141cc406Sopenharmony_ci  int max_packet_size = sanei_xml_get_prop_uint(node, "max_packet_size");
5548141cc406Sopenharmony_ci
5549141cc406Sopenharmony_ci  if (desc_type < 0 || bcd_usb < 0 || bcd_dev < 0 || dev_class < 0 ||
5550141cc406Sopenharmony_ci      dev_sub_class < 0 || dev_protocol < 0 || max_packet_size < 0)
5551141cc406Sopenharmony_ci  {
5552141cc406Sopenharmony_ci      FAIL_TEST_TX(__func__, node, "get_descriptor recorded block is missing attributes\n");
5553141cc406Sopenharmony_ci      testing_known_commands_input_failed = 1;
5554141cc406Sopenharmony_ci      return SANE_STATUS_IO_ERROR;
5555141cc406Sopenharmony_ci  }
5556141cc406Sopenharmony_ci
5557141cc406Sopenharmony_ci  desc->desc_type = desc_type;
5558141cc406Sopenharmony_ci  desc->bcd_usb = bcd_usb;
5559141cc406Sopenharmony_ci  desc->bcd_dev = bcd_dev;
5560141cc406Sopenharmony_ci  desc->dev_class = dev_class;
5561141cc406Sopenharmony_ci  desc->dev_sub_class = dev_sub_class;
5562141cc406Sopenharmony_ci  desc->dev_protocol = dev_protocol;
5563141cc406Sopenharmony_ci  desc->max_packet_size = max_packet_size;
5564141cc406Sopenharmony_ci
5565141cc406Sopenharmony_ci  return SANE_STATUS_GOOD;
5566141cc406Sopenharmony_ci}
5567141cc406Sopenharmony_ci
5568141cc406Sopenharmony_cistatic void
5569141cc406Sopenharmony_cisanei_usb_record_get_descriptor(SANE_Int dn,
5570141cc406Sopenharmony_ci                                struct sanei_usb_dev_descriptor *desc)
5571141cc406Sopenharmony_ci{
5572141cc406Sopenharmony_ci  (void) dn;
5573141cc406Sopenharmony_ci
5574141cc406Sopenharmony_ci  xmlNode* node = testing_append_commands_node;
5575141cc406Sopenharmony_ci
5576141cc406Sopenharmony_ci  xmlNode* e_tx = xmlNewNode(NULL, (const xmlChar*)"get_descriptor");
5577141cc406Sopenharmony_ci
5578141cc406Sopenharmony_ci  xmlNewProp(e_tx, (const xmlChar*)"time_usec", (const xmlChar*)"0");
5579141cc406Sopenharmony_ci  sanei_xml_set_uint_attr(node, "seq", ++testing_last_known_seq);
5580141cc406Sopenharmony_ci
5581141cc406Sopenharmony_ci  sanei_xml_set_hex_attr(e_tx, "descriptor_type", desc->desc_type);
5582141cc406Sopenharmony_ci  sanei_xml_set_hex_attr(e_tx, "bcd_usb", desc->bcd_usb);
5583141cc406Sopenharmony_ci  sanei_xml_set_hex_attr(e_tx, "bcd_device", desc->bcd_dev);
5584141cc406Sopenharmony_ci  sanei_xml_set_hex_attr(e_tx, "device_class", desc->dev_class);
5585141cc406Sopenharmony_ci  sanei_xml_set_hex_attr(e_tx, "device_sub_class", desc->dev_sub_class);
5586141cc406Sopenharmony_ci  sanei_xml_set_hex_attr(e_tx, "device_protocol", desc->dev_protocol);
5587141cc406Sopenharmony_ci  sanei_xml_set_hex_attr(e_tx, "max_packet_size", desc->max_packet_size);
5588141cc406Sopenharmony_ci
5589141cc406Sopenharmony_ci  node = sanei_xml_append_command(node, 1, e_tx);
5590141cc406Sopenharmony_ci  testing_append_commands_node = node;
5591141cc406Sopenharmony_ci}
5592141cc406Sopenharmony_ci
5593141cc406Sopenharmony_ci#endif // WITH_USB_RECORD_REPLAY
5594141cc406Sopenharmony_ci
5595141cc406Sopenharmony_ciextern SANE_Status
5596141cc406Sopenharmony_cisanei_usb_get_descriptor( SANE_Int dn,
5597141cc406Sopenharmony_ci                          struct sanei_usb_dev_descriptor __sane_unused__
5598141cc406Sopenharmony_ci                          *desc )
5599141cc406Sopenharmony_ci{
5600141cc406Sopenharmony_ci  if (dn >= device_number || dn < 0)
5601141cc406Sopenharmony_ci    {
5602141cc406Sopenharmony_ci      DBG (1,
5603141cc406Sopenharmony_ci	   "sanei_usb_get_descriptor: dn >= device number || dn < 0, dn=%d\n",
5604141cc406Sopenharmony_ci	   dn);
5605141cc406Sopenharmony_ci      return SANE_STATUS_INVAL;
5606141cc406Sopenharmony_ci    }
5607141cc406Sopenharmony_ci
5608141cc406Sopenharmony_ci  if (testing_mode == sanei_usb_testing_mode_replay)
5609141cc406Sopenharmony_ci    {
5610141cc406Sopenharmony_ci#if WITH_USB_RECORD_REPLAY
5611141cc406Sopenharmony_ci      return sanei_usb_replay_get_descriptor(dn, desc);
5612141cc406Sopenharmony_ci#else
5613141cc406Sopenharmony_ci      DBG (1, "USB record-replay mode support is missing\n");
5614141cc406Sopenharmony_ci      return SANE_STATUS_UNSUPPORTED;
5615141cc406Sopenharmony_ci#endif
5616141cc406Sopenharmony_ci    }
5617141cc406Sopenharmony_ci
5618141cc406Sopenharmony_ci  DBG (5, "sanei_usb_get_descriptor\n");
5619141cc406Sopenharmony_ci#ifdef HAVE_LIBUSB_LEGACY
5620141cc406Sopenharmony_ci    {
5621141cc406Sopenharmony_ci	  struct usb_device_descriptor *usb_descr;
5622141cc406Sopenharmony_ci
5623141cc406Sopenharmony_ci	  usb_descr = &(devices[dn].libusb_device->descriptor);
5624141cc406Sopenharmony_ci	  desc->desc_type = usb_descr->bDescriptorType;
5625141cc406Sopenharmony_ci	  desc->bcd_usb   = usb_descr->bcdUSB;
5626141cc406Sopenharmony_ci	  desc->bcd_dev   = usb_descr->bcdDevice;
5627141cc406Sopenharmony_ci	  desc->dev_class = usb_descr->bDeviceClass;
5628141cc406Sopenharmony_ci
5629141cc406Sopenharmony_ci	  desc->dev_sub_class   = usb_descr->bDeviceSubClass;
5630141cc406Sopenharmony_ci	  desc->dev_protocol    = usb_descr->bDeviceProtocol;
5631141cc406Sopenharmony_ci	  desc->max_packet_size = usb_descr->bMaxPacketSize0;
5632141cc406Sopenharmony_ci    }
5633141cc406Sopenharmony_ci#elif defined(HAVE_LIBUSB)
5634141cc406Sopenharmony_ci    {
5635141cc406Sopenharmony_ci      struct libusb_device_descriptor lu_desc;
5636141cc406Sopenharmony_ci      int ret;
5637141cc406Sopenharmony_ci
5638141cc406Sopenharmony_ci      ret = libusb_get_device_descriptor (devices[dn].lu_device, &lu_desc);
5639141cc406Sopenharmony_ci      if (ret < 0)
5640141cc406Sopenharmony_ci	{
5641141cc406Sopenharmony_ci	  DBG (1,
5642141cc406Sopenharmony_ci	       "sanei_usb_get_descriptor: libusb error: %s\n",
5643141cc406Sopenharmony_ci	       sanei_libusb_strerror (ret));
5644141cc406Sopenharmony_ci
5645141cc406Sopenharmony_ci	  return SANE_STATUS_INVAL;
5646141cc406Sopenharmony_ci	}
5647141cc406Sopenharmony_ci
5648141cc406Sopenharmony_ci      desc->desc_type = lu_desc.bDescriptorType;
5649141cc406Sopenharmony_ci      desc->bcd_usb   = lu_desc.bcdUSB;
5650141cc406Sopenharmony_ci      desc->bcd_dev   = lu_desc.bcdDevice;
5651141cc406Sopenharmony_ci      desc->dev_class = lu_desc.bDeviceClass;
5652141cc406Sopenharmony_ci
5653141cc406Sopenharmony_ci      desc->dev_sub_class   = lu_desc.bDeviceSubClass;
5654141cc406Sopenharmony_ci      desc->dev_protocol    = lu_desc.bDeviceProtocol;
5655141cc406Sopenharmony_ci      desc->max_packet_size = lu_desc.bMaxPacketSize0;
5656141cc406Sopenharmony_ci    }
5657141cc406Sopenharmony_ci#elif defined(HAVE_USB_MANAGER)
5658141cc406Sopenharmony_ci    {
5659141cc406Sopenharmony_ci      struct usb_manager_device_descriptor usb_manager_desc;
5660141cc406Sopenharmony_ci      int ret;
5661141cc406Sopenharmony_ci
5662141cc406Sopenharmony_ci      ret = usb_manager_get_device_descriptor (devices[dn].usb_manager_device, &usb_manager_desc);
5663141cc406Sopenharmony_ci      if (ret < 0)
5664141cc406Sopenharmony_ci	{
5665141cc406Sopenharmony_ci	  DBG (1,
5666141cc406Sopenharmony_ci	       "sanei_usb_get_descriptor: libusb error: %s\n",
5667141cc406Sopenharmony_ci	       sanei_usb_manager_strerror (ret));
5668141cc406Sopenharmony_ci
5669141cc406Sopenharmony_ci	  return SANE_STATUS_INVAL;
5670141cc406Sopenharmony_ci	}
5671141cc406Sopenharmony_ci
5672141cc406Sopenharmony_ci      desc->desc_type = usb_manager_desc.bDescriptorType;
5673141cc406Sopenharmony_ci      desc->bcd_usb   = usb_manager_desc.bcdUSB;
5674141cc406Sopenharmony_ci      desc->bcd_dev   = usb_manager_desc.bcdDevice;
5675141cc406Sopenharmony_ci      desc->dev_class = usb_manager_desc.bDeviceClass;
5676141cc406Sopenharmony_ci
5677141cc406Sopenharmony_ci      desc->dev_sub_class   = usb_manager_desc.bDeviceSubClass;
5678141cc406Sopenharmony_ci      desc->dev_protocol    = usb_manager_desc.bDeviceProtocol;
5679141cc406Sopenharmony_ci      desc->max_packet_size = usb_manager_desc.bMaxPacketSize0;
5680141cc406Sopenharmony_ci    }
5681141cc406Sopenharmony_ci#else /* not HAVE_LIBUSB_LEGACY && not HAVE_LIBUSB && not HAVE_USB_MANAGER*/
5682141cc406Sopenharmony_ci    {
5683141cc406Sopenharmony_ci      DBG (1, "sanei_usb_get_descriptor: libusb support missing\n");
5684141cc406Sopenharmony_ci      return SANE_STATUS_UNSUPPORTED;
5685141cc406Sopenharmony_ci    }
5686141cc406Sopenharmony_ci#endif /* not HAVE_LIBUSB_LEGACY && not HAVE_LIBUSB && not HAVE_USB_MANAGER */
5687141cc406Sopenharmony_ci
5688141cc406Sopenharmony_ci  if (testing_mode == sanei_usb_testing_mode_record)
5689141cc406Sopenharmony_ci    {
5690141cc406Sopenharmony_ci#if WITH_USB_RECORD_REPLAY
5691141cc406Sopenharmony_ci      sanei_usb_record_get_descriptor(dn, desc);
5692141cc406Sopenharmony_ci#else
5693141cc406Sopenharmony_ci      DBG (1, "USB record-replay mode support is missing\n");
5694141cc406Sopenharmony_ci      return SANE_STATUS_UNSUPPORTED;
5695141cc406Sopenharmony_ci#endif
5696141cc406Sopenharmony_ci    }
5697141cc406Sopenharmony_ci
5698141cc406Sopenharmony_ci  return SANE_STATUS_GOOD;
5699141cc406Sopenharmony_ci}
5700