From 58912f68c2489bcee787599837447e0d64dfd61a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Frings-F=C3=BCrst?= Date: Wed, 24 May 2017 21:03:56 +0200 Subject: New upstream version 1.0.27 --- sanei/sanei_usb.c | 262 ++++++++++++++++++++++++++++++------------------------ 1 file changed, 147 insertions(+), 115 deletions(-) (limited to 'sanei/sanei_usb.c') diff --git a/sanei/sanei_usb.c b/sanei/sanei_usb.c index f210d4f..e4b23dc 100644 --- a/sanei/sanei_usb.c +++ b/sanei/sanei_usb.c @@ -62,31 +62,22 @@ #include #include -/* for debug messages */ -#if __STDC_VERSION__ < 199901L -# if __GNUC__ >= 2 -# define __func__ __FUNCTION__ -# else -# define __func__ "" -# endif -#endif - #ifdef HAVE_RESMGR #include #endif -#ifdef HAVE_LIBUSB +#ifdef HAVE_LIBUSB_LEGACY #ifdef HAVE_LUSB0_USB_H #include #else #include #endif -#endif /* HAVE_LIBUSB */ +#endif /* HAVE_LIBUSB_LEGACY */ -#ifdef HAVE_LIBUSB_1_0 +#ifdef HAVE_LIBUSB #include -#endif /* HAVE_LIBUSB_1_0 */ +#endif /* HAVE_LIBUSB */ #ifdef HAVE_USBCALLS #include @@ -156,14 +147,14 @@ typedef struct SANE_Int interface_nr; SANE_Int alt_setting; SANE_Int missing; -#ifdef HAVE_LIBUSB +#ifdef HAVE_LIBUSB_LEGACY usb_dev_handle *libusb_handle; struct usb_device *libusb_device; -#endif /* HAVE_LIBUSB */ -#ifdef HAVE_LIBUSB_1_0 +#endif /* HAVE_LIBUSB_LEGACY */ +#ifdef HAVE_LIBUSB libusb_device *lu_device; libusb_device_handle *lu_handle; -#endif /* HAVE_LIBUSB_1_0 */ +#endif /* HAVE_LIBUSB */ } device_list_type; @@ -183,13 +174,13 @@ static int device_number=0; * count number of time sanei_usb has been initialized */ static int initialized=0; -#if defined(HAVE_LIBUSB) || defined(HAVE_LIBUSB_1_0) +#if defined(HAVE_LIBUSB_LEGACY) || defined(HAVE_LIBUSB) static int libusb_timeout = 30 * 1000; /* 30 seconds */ -#endif /* HAVE_LIBUSB */ +#endif /* HAVE_LIBUSB_LEGACY */ -#ifdef HAVE_LIBUSB_1_0 +#ifdef HAVE_LIBUSB static libusb_context *sanei_usb_ctx; -#endif /* HAVE_LIBUSB_1_0 */ +#endif /* HAVE_LIBUSB */ #if defined (__linux__) /* From /usr/src/linux/driver/usb/scanner.h */ @@ -266,7 +257,7 @@ print_buffer (const SANE_Byte * buffer, SANE_Int size) } } -#if !defined(HAVE_LIBUSB) && !defined(HAVE_LIBUSB_1_0) +#if !defined(HAVE_LIBUSB_LEGACY) && !defined(HAVE_LIBUSB) static void kernel_get_vendor_product (int fd, const char *name, int *vendorID, int *productID) { @@ -340,7 +331,7 @@ kernel_get_vendor_product (int fd, const char *name, int *vendorID, int *product #endif /* defined (__linux__), defined(__BEOS__), ... */ /* put more os-dependant stuff ... */ } -#endif /* !defined(HAVE_LIBUSB) && !defined(HAVE_LIBUSB_1_0) */ +#endif /* !defined(HAVE_LIBUSB_LEGACY) && !defined(HAVE_LIBUSB) */ /** * store the given device in device list if it isn't already @@ -366,10 +357,10 @@ store_device (device_list_type device) * Need to update the LibUSB device pointer, since it might * have changed after the latest USB scan. */ -#ifdef HAVE_LIBUSB +#ifdef HAVE_LIBUSB_LEGACY devices[i].libusb_device = device.libusb_device; #endif -#ifdef HAVE_LIBUSB_1_0 +#ifdef HAVE_LIBUSB devices[i].lu_device = device.lu_device; #endif @@ -408,7 +399,7 @@ store_device (device_list_type device) devices[pos].open = SANE_FALSE; } -#ifdef HAVE_LIBUSB_1_0 +#ifdef HAVE_LIBUSB static char * sanei_libusb_strerror (int errcode) { @@ -462,14 +453,14 @@ sanei_libusb_strerror (int errcode) return "Unknown libusb-1.0 error code"; } } -#endif /* HAVE_LIBUSB_1_0 */ +#endif /* HAVE_LIBUSB */ void sanei_usb_init (void) { -#ifdef HAVE_LIBUSB_1_0 +#ifdef HAVE_LIBUSB int ret; -#endif /* HAVE_LIBUSB_1_0 */ +#endif /* HAVE_LIBUSB */ DBG_INIT (); #ifdef DBG_LEVEL @@ -483,18 +474,18 @@ sanei_usb_init (void) memset (devices, 0, sizeof (devices)); /* initialize USB with old libusb library */ -#ifdef HAVE_LIBUSB +#ifdef HAVE_LIBUSB_LEGACY DBG (4, "%s: Looking for libusb devices\n", __func__); usb_init (); #ifdef DBG_LEVEL if (DBG_LEVEL > 4) usb_set_debug (255); #endif /* DBG_LEVEL */ -#endif /* HAVE_LIBUSB */ +#endif /* HAVE_LIBUSB_LEGACY */ /* initialize USB using libusb-1.0 */ -#ifdef HAVE_LIBUSB_1_0 +#ifdef HAVE_LIBUSB if (!sanei_usb_ctx) { DBG (4, "%s: initializing libusb-1.0\n", __func__); @@ -511,9 +502,9 @@ sanei_usb_init (void) libusb_set_debug (sanei_usb_ctx, 3); #endif /* DBG_LEVEL */ } -#endif /* HAVE_LIBUSB_1_0 */ +#endif /* HAVE_LIBUSB */ -#if !defined(HAVE_LIBUSB) && !defined(HAVE_LIBUSB_1_0) +#if !defined(HAVE_LIBUSB_LEGACY) && !defined(HAVE_LIBUSB) DBG (4, "%s: SANE is built without support for libusb\n", __func__); #endif @@ -553,7 +544,7 @@ int i; devices[i].devname=NULL; } } -#ifdef HAVE_LIBUSB_1_0 +#ifdef HAVE_LIBUSB if (sanei_usb_ctx) { libusb_exit (sanei_usb_ctx); @@ -643,7 +634,7 @@ static void usbcall_scan_devices(void) } #endif /* HAVE_USBCALLS */ -#if !defined(HAVE_LIBUSB) && !defined(HAVE_LIBUSB_1_0) +#if !defined(HAVE_LIBUSB_LEGACY) && !defined(HAVE_LIBUSB) /** scan for devices using kernel device. * Check for devices using kernel device */ @@ -742,9 +733,9 @@ static void kernel_scan_devices(void) closedir (dir); } } -#endif /* !defined(HAVE_LIBUSB) && !defined(HAVE_LIBUSB_1_0) */ +#endif /* !defined(HAVE_LIBUSB_LEGACY) && !defined(HAVE_LIBUSB) */ -#ifdef HAVE_LIBUSB +#ifdef HAVE_LIBUSB_LEGACY /** scan for devices using old libusb * Check for devices using 0.1.x libusb */ @@ -855,9 +846,9 @@ static void libusb_scan_devices(void) } } } -#endif /* HAVE_LIBUSB */ +#endif /* HAVE_LIBUSB_LEGACY */ -#ifdef HAVE_LIBUSB_1_0 +#ifdef HAVE_LIBUSB /** scan for devices using libusb * Check for devices using libusb-1.0 */ @@ -1031,7 +1022,7 @@ static void libusb_scan_devices(void) libusb_free_device_list (devlist, 1); } -#endif /* HAVE_LIBUSB_1_0 */ +#endif /* HAVE_LIBUSB */ void @@ -1057,11 +1048,11 @@ sanei_usb_scan_devices (void) } /* Check for devices using the kernel scanner driver */ -#if !defined(HAVE_LIBUSB) && !defined(HAVE_LIBUSB_1_0) +#if !defined(HAVE_LIBUSB_LEGACY) && !defined(HAVE_LIBUSB) kernel_scan_devices(); #endif -#if defined(HAVE_LIBUSB) || defined(HAVE_LIBUSB_1_0) +#if defined(HAVE_LIBUSB_LEGACY) || defined(HAVE_LIBUSB) /* Check for devices using libusb (old or new)*/ libusb_scan_devices(); #endif @@ -1308,7 +1299,6 @@ sanei_usb_open (SANE_String_Const devname, SANE_Int * dn) { int devcount; SANE_Bool found = SANE_FALSE; - int c, i, a; DBG (5, "sanei_usb_open: trying to open device `%s'\n", devname); if (!dn) @@ -1341,10 +1331,11 @@ sanei_usb_open (SANE_String_Const devname, SANE_Int * dn) if (devices[devcount].method == sanei_usb_method_libusb) { -#ifdef HAVE_LIBUSB +#ifdef HAVE_LIBUSB_LEGACY struct usb_device *dev; struct usb_interface_descriptor *interface; int result, num; + int c, i, a; devices[devcount].libusb_handle = usb_open (devices[devcount].libusb_device); @@ -1600,13 +1591,14 @@ sanei_usb_open (SANE_String_Const devname, SANE_Int * dn) } } -#elif defined(HAVE_LIBUSB_1_0) /* libusb-1.0 */ +#elif defined(HAVE_LIBUSB) /* libusb-1.0 */ int config; libusb_device *dev; struct libusb_device_descriptor desc; struct libusb_config_descriptor *config0; int result, num; + int c, i, a; dev = devices[devcount].lu_device; @@ -1902,11 +1894,11 @@ sanei_usb_open (SANE_String_Const devname, SANE_Int * dn) libusb_free_config_descriptor (config); } -#else /* not HAVE_LIBUSB && not HAVE_LIBUSB_1_0 */ +#else /* not HAVE_LIBUSB_LEGACY && not HAVE_LIBUSB */ DBG (1, "sanei_usb_open: can't open device `%s': " "libusb support missing\n", devname); return SANE_STATUS_UNSUPPORTED; -#endif /* not HAVE_LIBUSB && not HAVE_LIBUSB_1_0 */ +#endif /* not HAVE_LIBUSB_LEGACY && not HAVE_LIBUSB */ } else if (devices[devcount].method == sanei_usb_method_scanner_driver) { @@ -2108,6 +2100,17 @@ sanei_usb_open (SANE_String_Const devname, SANE_Int * dn) void sanei_usb_close (SANE_Int dn) { + char *env; + int workaround = 0; + + DBG (5, "sanei_usb_close: evaluating environment variable SANE_USB_WORKAROUND\n"); + env = getenv ("SANE_USB_WORKAROUND"); + if (env) + { + workaround = atoi(env); + DBG (5, "sanei_usb_close: workaround: %d\n", workaround); + } + DBG (5, "sanei_usb_close: closing device %d\n", dn); if (dn >= device_number || dn < 0) { @@ -2133,31 +2136,37 @@ sanei_usb_close (SANE_Int dn) #endif } else -#ifdef HAVE_LIBUSB +#ifdef HAVE_LIBUSB_LEGACY { /* This call seems to be required by Linux xhci driver * even though it should be a no-op. Without it, the * host or driver does not reset it's data toggle bit. * We intentionally ignore the return val */ - sanei_usb_set_altinterface (dn, devices[dn].alt_setting); + if (workaround) + { + sanei_usb_set_altinterface (dn, devices[dn].alt_setting); + } usb_release_interface (devices[dn].libusb_handle, devices[dn].interface_nr); usb_close (devices[dn].libusb_handle); } -#elif defined(HAVE_LIBUSB_1_0) +#elif defined(HAVE_LIBUSB) { /* This call seems to be required by Linux xhci driver * even though it should be a no-op. Without it, the * host or driver does not reset it's data toggle bit. * We intentionally ignore the return val */ - sanei_usb_set_altinterface (dn, devices[dn].alt_setting); + if (workaround) + { + sanei_usb_set_altinterface (dn, devices[dn].alt_setting); + } libusb_release_interface (devices[dn].lu_handle, devices[dn].interface_nr); libusb_close (devices[dn].lu_handle); } -#else /* not HAVE_LIBUSB && not HAVE_LIBUSB_1_0 */ +#else /* not HAVE_LIBUSB_LEGACY && not HAVE_LIBUSB */ DBG (1, "sanei_usb_close: libusb support missing\n"); #endif devices[dn].open = SANE_FALSE; @@ -2165,19 +2174,28 @@ sanei_usb_close (SANE_Int dn) } void -sanei_usb_set_timeout (SANE_Int timeout) +sanei_usb_set_timeout (SANE_Int __sane_unused__ timeout) { -#if defined(HAVE_LIBUSB) || defined(HAVE_LIBUSB_1_0) +#if defined(HAVE_LIBUSB_LEGACY) || defined(HAVE_LIBUSB) libusb_timeout = timeout; #else DBG (1, "sanei_usb_set_timeout: libusb support missing\n"); -#endif /* HAVE_LIBUSB || HAVE_LIBUSB_1_0 */ +#endif /* HAVE_LIBUSB_LEGACY || HAVE_LIBUSB */ } SANE_Status sanei_usb_clear_halt (SANE_Int dn) { - int ret; + char *env; + int workaround = 0; + + DBG (5, "sanei_usb_clear_halt: evaluating environment variable SANE_USB_WORKAROUND\n"); + env = getenv ("SANE_USB_WORKAROUND"); + if (env) + { + workaround = atoi(env); + DBG (5, "sanei_usb_clear_halt: workaround: %d\n", workaround); + } if (dn >= device_number || dn < 0) { @@ -2185,13 +2203,17 @@ sanei_usb_clear_halt (SANE_Int dn) return SANE_STATUS_INVAL; } -#ifdef HAVE_LIBUSB +#ifdef HAVE_LIBUSB_LEGACY + int ret; /* This call seems to be required by Linux xhci driver * even though it should be a no-op. Without it, the * host or driver does not send the clear to the device. * We intentionally ignore the return val */ - sanei_usb_set_altinterface (dn, devices[dn].alt_setting); + if (workaround) + { + sanei_usb_set_altinterface (dn, devices[dn].alt_setting); + } ret = usb_clear_halt (devices[dn].libusb_handle, devices[dn].bulk_in_ep); if (ret){ @@ -2205,13 +2227,17 @@ sanei_usb_clear_halt (SANE_Int dn) return SANE_STATUS_INVAL; } -#elif defined(HAVE_LIBUSB_1_0) +#elif defined(HAVE_LIBUSB) + int ret; /* This call seems to be required by Linux xhci driver * even though it should be a no-op. Without it, the * host or driver does not send the clear to the device. * We intentionally ignore the return val */ - sanei_usb_set_altinterface (dn, devices[dn].alt_setting); + if (workaround) + { + sanei_usb_set_altinterface (dn, devices[dn].alt_setting); + } ret = libusb_clear_halt (devices[dn].lu_handle, devices[dn].bulk_in_ep); if (ret){ @@ -2224,17 +2250,17 @@ sanei_usb_clear_halt (SANE_Int dn) DBG (1, "sanei_usb_clear_halt: BULK_OUT ret=%d\n", ret); return SANE_STATUS_INVAL; } -#else /* not HAVE_LIBUSB && not HAVE_LIBUSB_1_0 */ +#else /* not HAVE_LIBUSB_LEGACY && not HAVE_LIBUSB */ DBG (1, "sanei_usb_clear_halt: libusb support missing\n"); -#endif /* HAVE_LIBUSB || HAVE_LIBUSB_1_0 */ +#endif /* HAVE_LIBUSB_LEGACY || HAVE_LIBUSB */ return SANE_STATUS_GOOD; } SANE_Status -sanei_usb_reset (SANE_Int dn) +sanei_usb_reset (SANE_Int __sane_unused__ dn) { -#ifdef HAVE_LIBUSB +#ifdef HAVE_LIBUSB_LEGACY int ret; ret = usb_reset (devices[dn].libusb_handle); @@ -2243,7 +2269,7 @@ sanei_usb_reset (SANE_Int dn) return SANE_STATUS_INVAL; } -#elif defined(HAVE_LIBUSB_1_0) +#elif defined(HAVE_LIBUSB) int ret; ret = libusb_reset_device (devices[dn].lu_handle); @@ -2252,9 +2278,9 @@ sanei_usb_reset (SANE_Int dn) return SANE_STATUS_INVAL; } -#else /* not HAVE_LIBUSB && not HAVE_LIBUSB_1_0 */ +#else /* not HAVE_LIBUSB_LEGACY && not HAVE_LIBUSB */ DBG (1, "sanei_usb_reset: libusb support missing\n"); -#endif /* HAVE_LIBUSB || HAVE_LIBUSB_1_0 */ +#endif /* HAVE_LIBUSB_LEGACY || HAVE_LIBUSB */ return SANE_STATUS_GOOD; } @@ -2287,7 +2313,7 @@ sanei_usb_read_bulk (SANE_Int dn, SANE_Byte * buffer, size_t * size) strerror (errno)); } else if (devices[dn].method == sanei_usb_method_libusb) -#ifdef HAVE_LIBUSB +#ifdef HAVE_LIBUSB_LEGACY { if (devices[dn].bulk_in_ep) { @@ -2306,14 +2332,14 @@ sanei_usb_read_bulk (SANE_Int dn, SANE_Byte * buffer, size_t * size) return SANE_STATUS_INVAL; } } -#elif defined(HAVE_LIBUSB_1_0) +#elif defined(HAVE_LIBUSB) { if (devices[dn].bulk_in_ep) { - int ret; + int ret, rsize; ret = libusb_bulk_transfer (devices[dn].lu_handle, devices[dn].bulk_in_ep, buffer, - (int) *size, (int *) &read_size, + (int) *size, &rsize, libusb_timeout); if (ret < 0) @@ -2323,6 +2349,10 @@ sanei_usb_read_bulk (SANE_Int dn, SANE_Byte * buffer, size_t * size) read_size = -1; } + else + { + read_size = rsize; + } } else { @@ -2331,12 +2361,12 @@ sanei_usb_read_bulk (SANE_Int dn, SANE_Byte * buffer, size_t * size) return SANE_STATUS_INVAL; } } -#else /* not HAVE_LIBUSB && not HAVE_LIBUSB_1_0 */ +#else /* not HAVE_LIBUSB_LEGACY && not HAVE_LIBUSB */ { DBG (1, "sanei_usb_read_bulk: libusb support missing\n"); return SANE_STATUS_UNSUPPORTED; } -#endif /* not HAVE_LIBUSB */ +#endif /* not HAVE_LIBUSB_LEGACY */ else if (devices[dn].method == sanei_usb_method_usbcalls) { #ifdef HAVE_USBCALLS @@ -2382,10 +2412,10 @@ sanei_usb_read_bulk (SANE_Int dn, SANE_Byte * buffer, size_t * size) if (read_size < 0) { -#ifdef HAVE_LIBUSB +#ifdef HAVE_LIBUSB_LEGACY if (devices[dn].method == sanei_usb_method_libusb) usb_clear_halt (devices[dn].libusb_handle, devices[dn].bulk_in_ep); -#elif defined(HAVE_LIBUSB_1_0) +#elif defined(HAVE_LIBUSB) if (devices[dn].method == sanei_usb_method_libusb) libusb_clear_halt (devices[dn].lu_handle, devices[dn].bulk_in_ep); #endif @@ -2437,7 +2467,7 @@ sanei_usb_write_bulk (SANE_Int dn, const SANE_Byte * buffer, size_t * size) strerror (errno)); } else if (devices[dn].method == sanei_usb_method_libusb) -#ifdef HAVE_LIBUSB +#ifdef HAVE_LIBUSB_LEGACY { if (devices[dn].bulk_out_ep) { @@ -2456,7 +2486,7 @@ sanei_usb_write_bulk (SANE_Int dn, const SANE_Byte * buffer, size_t * size) return SANE_STATUS_INVAL; } } -#elif defined(HAVE_LIBUSB_1_0) +#elif defined(HAVE_LIBUSB) { if (devices[dn].bulk_out_ep) { @@ -2464,7 +2494,7 @@ sanei_usb_write_bulk (SANE_Int dn, const SANE_Byte * buffer, size_t * size) int trans_bytes; ret = libusb_bulk_transfer (devices[dn].lu_handle, devices[dn].bulk_out_ep, - buffer, + (unsigned char *) buffer, (int) *size, &trans_bytes, libusb_timeout); if (ret < 0) @@ -2484,12 +2514,12 @@ sanei_usb_write_bulk (SANE_Int dn, const SANE_Byte * buffer, size_t * size) return SANE_STATUS_INVAL; } } -#else /* not HAVE_LIBUSB && not HAVE_LIBUSB_1_0 */ +#else /* not HAVE_LIBUSB_LEGACY && not HAVE_LIBUSB */ { DBG (1, "sanei_usb_write_bulk: libusb support missing\n"); return SANE_STATUS_UNSUPPORTED; } -#endif /* not HAVE_LIBUSB && not HAVE_LIBUSB_1_0 */ +#endif /* not HAVE_LIBUSB_LEGACY && not HAVE_LIBUSB */ else if (devices[dn].method == sanei_usb_method_usbcalls) { #ifdef HAVE_USBCALLS @@ -2537,10 +2567,10 @@ sanei_usb_write_bulk (SANE_Int dn, const SANE_Byte * buffer, size_t * size) if (write_size < 0) { *size = 0; -#ifdef HAVE_LIBUSB +#ifdef HAVE_LIBUSB_LEGACY if (devices[dn].method == sanei_usb_method_libusb) usb_clear_halt (devices[dn].libusb_handle, devices[dn].bulk_out_ep); -#elif defined(HAVE_LIBUSB_1_0) +#elif defined(HAVE_LIBUSB) if (devices[dn].method == sanei_usb_method_libusb) libusb_clear_halt (devices[dn].lu_handle, devices[dn].bulk_out_ep); #endif @@ -2616,7 +2646,7 @@ sanei_usb_control_msg (SANE_Int dn, SANE_Int rtype, SANE_Int req, #endif /* not __linux__ */ } else if (devices[dn].method == sanei_usb_method_libusb) -#ifdef HAVE_LIBUSB +#ifdef HAVE_LIBUSB_LEGACY { int result; @@ -2633,7 +2663,7 @@ sanei_usb_control_msg (SANE_Int dn, SANE_Int rtype, SANE_Int req, print_buffer (data, len); return SANE_STATUS_GOOD; } -#elif defined(HAVE_LIBUSB_1_0) +#elif defined(HAVE_LIBUSB) { int result; @@ -2650,12 +2680,12 @@ sanei_usb_control_msg (SANE_Int dn, SANE_Int rtype, SANE_Int req, print_buffer (data, len); return SANE_STATUS_GOOD; } -#else /* not HAVE_LIBUSB && not HAVE_LIBUSB_1_0*/ +#else /* not HAVE_LIBUSB_LEGACY && not HAVE_LIBUSB*/ { DBG (1, "sanei_usb_control_msg: libusb support missing\n"); return SANE_STATUS_UNSUPPORTED; } -#endif /* not HAVE_LIBUSB && not HAVE_LIBUSB_1_0 */ +#endif /* not HAVE_LIBUSB_LEGACY && not HAVE_LIBUSB */ else if (devices[dn].method == sanei_usb_method_usbcalls) { #ifdef HAVE_USBCALLS @@ -2692,7 +2722,7 @@ SANE_Status sanei_usb_read_int (SANE_Int dn, SANE_Byte * buffer, size_t * size) { ssize_t read_size = 0; -#if defined(HAVE_LIBUSB) || defined(HAVE_LIBUSB_1_0) +#if defined(HAVE_LIBUSB_LEGACY) || defined(HAVE_LIBUSB) SANE_Bool stalled = SANE_FALSE; #endif @@ -2717,7 +2747,7 @@ sanei_usb_read_int (SANE_Int dn, SANE_Byte * buffer, size_t * size) return SANE_STATUS_INVAL; } else if (devices[dn].method == sanei_usb_method_libusb) -#ifdef HAVE_LIBUSB +#ifdef HAVE_LIBUSB_LEGACY { if (devices[dn].int_in_ep) { @@ -2739,7 +2769,7 @@ sanei_usb_read_int (SANE_Int dn, SANE_Byte * buffer, size_t * size) return SANE_STATUS_INVAL; } } -#elif defined(HAVE_LIBUSB_1_0) +#elif defined(HAVE_LIBUSB) { if (devices[dn].int_in_ep) { @@ -2764,12 +2794,12 @@ sanei_usb_read_int (SANE_Int dn, SANE_Byte * buffer, size_t * size) return SANE_STATUS_INVAL; } } -#else /* not HAVE_LIBUSB && not HAVE_LIBUSB_1_0 */ +#else /* not HAVE_LIBUSB_LEGACY && not HAVE_LIBUSB */ { DBG (1, "sanei_usb_read_int: libusb support missing\n"); return SANE_STATUS_UNSUPPORTED; } -#endif /* not HAVE_LIBUSB && not HAVE_LIBUSB_1_0 */ +#endif /* not HAVE_LIBUSB_LEGACY && not HAVE_LIBUSB */ else if (devices[dn].method == sanei_usb_method_usbcalls) { #ifdef HAVE_USBCALLS @@ -2808,11 +2838,11 @@ sanei_usb_read_int (SANE_Int dn, SANE_Byte * buffer, size_t * size) if (read_size < 0) { -#ifdef HAVE_LIBUSB +#ifdef HAVE_LIBUSB_LEGACY if (devices[dn].method == sanei_usb_method_libusb) if (stalled) usb_clear_halt (devices[dn].libusb_handle, devices[dn].int_in_ep); -#elif defined(HAVE_LIBUSB_1_0) +#elif defined(HAVE_LIBUSB) if (devices[dn].method == sanei_usb_method_libusb) if (stalled) libusb_clear_halt (devices[dn].lu_handle, devices[dn].int_in_ep); @@ -2858,7 +2888,7 @@ sanei_usb_set_configuration (SANE_Int dn, SANE_Int configuration) #endif /* not __linux__ */ } else if (devices[dn].method == sanei_usb_method_libusb) -#ifdef HAVE_LIBUSB +#ifdef HAVE_LIBUSB_LEGACY { int result; @@ -2872,7 +2902,7 @@ sanei_usb_set_configuration (SANE_Int dn, SANE_Int configuration) } return SANE_STATUS_GOOD; } -#elif defined(HAVE_LIBUSB_1_0) +#elif defined(HAVE_LIBUSB) { int result; @@ -2885,12 +2915,12 @@ sanei_usb_set_configuration (SANE_Int dn, SANE_Int configuration) } return SANE_STATUS_GOOD; } -#else /* not HAVE_LIBUSB && not HAVE_LIBUSB_1_0 */ +#else /* not HAVE_LIBUSB_LEGACY && not HAVE_LIBUSB */ { DBG (1, "sanei_usb_set_configuration: libusb support missing\n"); return SANE_STATUS_UNSUPPORTED; } -#endif /* not HAVE_LIBUSB && not HAVE_LIBUSB_1_0 */ +#endif /* not HAVE_LIBUSB_LEGACY && not HAVE_LIBUSB */ else { DBG (1, @@ -2928,7 +2958,7 @@ sanei_usb_claim_interface (SANE_Int dn, SANE_Int interface_number) #endif /* not __linux__ */ } else if (devices[dn].method == sanei_usb_method_libusb) -#ifdef HAVE_LIBUSB +#ifdef HAVE_LIBUSB_LEGACY { int result; @@ -2941,7 +2971,7 @@ sanei_usb_claim_interface (SANE_Int dn, SANE_Int interface_number) } return SANE_STATUS_GOOD; } -#elif defined(HAVE_LIBUSB_1_0) +#elif defined(HAVE_LIBUSB) { int result; @@ -2954,12 +2984,12 @@ sanei_usb_claim_interface (SANE_Int dn, SANE_Int interface_number) } return SANE_STATUS_GOOD; } -#else /* not HAVE_LIBUSB && not HAVE_LIBUSB_1_0 */ +#else /* not HAVE_LIBUSB_LEGACY && not HAVE_LIBUSB */ { DBG (1, "sanei_usb_claim_interface: libusb support missing\n"); return SANE_STATUS_UNSUPPORTED; } -#endif /* not HAVE_LIBUSB && not HAVE_LIBUSB_1_0 */ +#endif /* not HAVE_LIBUSB_LEGACY && not HAVE_LIBUSB */ else { DBG (1, "sanei_usb_claim_interface: access method %d not implemented\n", @@ -2995,7 +3025,7 @@ sanei_usb_release_interface (SANE_Int dn, SANE_Int interface_number) #endif /* not __linux__ */ } else if (devices[dn].method == sanei_usb_method_libusb) -#ifdef HAVE_LIBUSB +#ifdef HAVE_LIBUSB_LEGACY { int result; @@ -3008,7 +3038,7 @@ sanei_usb_release_interface (SANE_Int dn, SANE_Int interface_number) } return SANE_STATUS_GOOD; } -#elif defined(HAVE_LIBUSB_1_0) +#elif defined(HAVE_LIBUSB) { int result; @@ -3021,12 +3051,12 @@ sanei_usb_release_interface (SANE_Int dn, SANE_Int interface_number) } return SANE_STATUS_GOOD; } -#else /* not HAVE_LIBUSB && not HAVE_LIBUSB_1_0 */ +#else /* not HAVE_LIBUSB_LEGACY && not HAVE_LIBUSB */ { DBG (1, "sanei_usb_release_interface: libusb support missing\n"); return SANE_STATUS_UNSUPPORTED; } -#endif /* not HAVE_LIBUSB && not HAVE_LIBUSB_1_0 */ +#endif /* not HAVE_LIBUSB_LEGACY && not HAVE_LIBUSB */ else { DBG (1, @@ -3061,7 +3091,7 @@ sanei_usb_set_altinterface (SANE_Int dn, SANE_Int alternate) #endif /* not __linux__ */ } else if (devices[dn].method == sanei_usb_method_libusb) -#ifdef HAVE_LIBUSB +#ifdef HAVE_LIBUSB_LEGACY { int result; @@ -3074,7 +3104,7 @@ sanei_usb_set_altinterface (SANE_Int dn, SANE_Int alternate) } return SANE_STATUS_GOOD; } -#elif defined(HAVE_LIBUSB_1_0) +#elif defined(HAVE_LIBUSB) { int result; @@ -3088,12 +3118,12 @@ sanei_usb_set_altinterface (SANE_Int dn, SANE_Int alternate) } return SANE_STATUS_GOOD; } -#else /* not HAVE_LIBUSB && not HAVE_LIBUSB_1_0 */ +#else /* not HAVE_LIBUSB_LEGACY && not HAVE_LIBUSB */ { DBG (1, "sanei_set_altinterface: libusb support missing\n"); return SANE_STATUS_UNSUPPORTED; } -#endif /* not HAVE_LIBUSB && not HAVE_LIBUSB_1_0 */ +#endif /* not HAVE_LIBUSB_LEGACY && not HAVE_LIBUSB */ else { DBG (1, @@ -3104,7 +3134,9 @@ sanei_usb_set_altinterface (SANE_Int dn, SANE_Int alternate) } extern SANE_Status -sanei_usb_get_descriptor( SANE_Int dn, struct sanei_usb_dev_descriptor *desc ) +sanei_usb_get_descriptor( SANE_Int dn, + struct sanei_usb_dev_descriptor __sane_unused__ + *desc ) { if (dn >= device_number || dn < 0) { @@ -3115,7 +3147,7 @@ sanei_usb_get_descriptor( SANE_Int dn, struct sanei_usb_dev_descriptor *desc ) } DBG (5, "sanei_usb_get_descriptor\n"); -#ifdef HAVE_LIBUSB +#ifdef HAVE_LIBUSB_LEGACY { struct usb_device_descriptor *usb_descr; @@ -3130,7 +3162,7 @@ sanei_usb_get_descriptor( SANE_Int dn, struct sanei_usb_dev_descriptor *desc ) desc->max_packet_size = usb_descr->bMaxPacketSize0; return SANE_STATUS_GOOD; } -#elif defined(HAVE_LIBUSB_1_0) +#elif defined(HAVE_LIBUSB) { struct libusb_device_descriptor lu_desc; int ret; @@ -3155,10 +3187,10 @@ sanei_usb_get_descriptor( SANE_Int dn, struct sanei_usb_dev_descriptor *desc ) desc->max_packet_size = lu_desc.bMaxPacketSize0; return SANE_STATUS_GOOD; } -#else /* not HAVE_LIBUSB && not HAVE_LIBUSB_1_0 */ +#else /* not HAVE_LIBUSB_LEGACY && not HAVE_LIBUSB */ { DBG (1, "sanei_usb_get_descriptor: libusb support missing\n"); return SANE_STATUS_UNSUPPORTED; } -#endif /* not HAVE_LIBUSB && not HAVE_LIBUSB_1_0 */ +#endif /* not HAVE_LIBUSB_LEGACY && not HAVE_LIBUSB */ } -- cgit v1.2.3 From 1687222e1b9e74c89cafbb5910e72d8ec7bfd40f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Frings-F=C3=BCrst?= Date: Wed, 31 Jul 2019 16:59:49 +0200 Subject: New upstream version 1.0.28 --- sanei/sanei_usb.c | 427 ++++++++++++++++-------------------------------------- 1 file changed, 124 insertions(+), 303 deletions(-) (limited to 'sanei/sanei_usb.c') diff --git a/sanei/sanei_usb.c b/sanei/sanei_usb.c index e4b23dc..6fd6040 100644 --- a/sanei/sanei_usb.c +++ b/sanei/sanei_usb.c @@ -1262,6 +1262,77 @@ sanei_usb_set_endpoint (SANE_Int dn, SANE_Int ep_type, SANE_Int ep) } } +#if HAVE_LIBUSB_LEGACY || HAVE_LIBUSB || HAVE_USBCALLS +static const char* sanei_usb_transfer_type_desc(SANE_Int transfer_type) +{ + switch (transfer_type) + { + case USB_ENDPOINT_TYPE_INTERRUPT: return "interrupt"; + case USB_ENDPOINT_TYPE_BULK: return "bulk"; + case USB_ENDPOINT_TYPE_ISOCHRONOUS: return "isochronous"; + case USB_ENDPOINT_TYPE_CONTROL: return "control"; + } + return NULL; +} + +// Similar sanei_usb_set_endpoint, but ignors duplicate endpoints +static void sanei_usb_add_endpoint(device_list_type* device, + SANE_Int transfer_type, + SANE_Int ep_address, + SANE_Int ep_direction) +{ + DBG(5, "%s: direction: %d, address: %d, transfer_type: %d\n", + __func__, ep_direction, ep_address, transfer_type); + + SANE_Int* ep_in = NULL; + SANE_Int* ep_out = NULL; + const char* transfer_type_msg = sanei_usb_transfer_type_desc(transfer_type); + + switch (transfer_type) + { + case USB_ENDPOINT_TYPE_INTERRUPT: + ep_in = &device->int_in_ep; + ep_out = &device->int_out_ep; + break; + case USB_ENDPOINT_TYPE_BULK: + ep_in = &device->bulk_in_ep; + ep_out = &device->bulk_out_ep; + break; + case USB_ENDPOINT_TYPE_ISOCHRONOUS: + ep_in = &device->iso_in_ep; + ep_out = &device->iso_out_ep; + break; + case USB_ENDPOINT_TYPE_CONTROL: + ep_in = &device->control_in_ep; + ep_out = &device->control_out_ep; + break; + } + + DBG(5, "%s: found %s-%s endpoint (address 0x%02x)\n", + __func__, transfer_type_msg, ep_direction ? "in" : "out", + ep_address); + + if (ep_direction) // in + { + if (*ep_in) + DBG(3, "%s: we already have a %s-in endpoint " + "(address: 0x%02x), ignoring the new one\n", + __func__, transfer_type_msg, *ep_in); + else + *ep_in = ep_address; + } + else + { + if (*ep_out) + DBG(3, "%s: we already have a %s-out endpoint " + "(address: 0x%02x), ignoring the new one\n", + __func__, transfer_type_msg, *ep_out); + else + *ep_out = ep_address; + } +} +#endif // HAVE_LIBUSB_LEGACY || HAVE_LIBUSB || HAVE_USBCALLS + SANE_Int sanei_usb_get_endpoint (SANE_Int dn, SANE_Int ep_type) { @@ -1460,132 +1531,12 @@ sanei_usb_open (SANE_String_Const devname, SANE_Int * dn) DBG (5, "sanei_usb_open: endpoint nr: %d\n", num); transfer_type = endpoint->bmAttributes & USB_ENDPOINT_TYPE_MASK; - address = - endpoint-> - bEndpointAddress & USB_ENDPOINT_ADDRESS_MASK; direction = endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK; - DBG (5, "sanei_usb_open: direction: %d\n", direction); - - DBG (5, - "sanei_usb_open: address: %d transfertype: %d\n", - address, transfer_type); - - - /* save the endpoints we need later */ - if (transfer_type == USB_ENDPOINT_TYPE_INTERRUPT) - { - DBG (5, - "sanei_usb_open: found interrupt-%s endpoint (address 0x%02x)\n", - direction ? "in" : "out", address); - if (direction) /* in */ - { - if (devices[devcount].int_in_ep) - DBG (3, - "sanei_usb_open: we already have a int-in endpoint " - "(address: 0x%02x), ignoring the new one\n", - devices[devcount].int_in_ep); - else - devices[devcount].int_in_ep = - endpoint->bEndpointAddress; - } - else - { - if (devices[devcount].int_out_ep) - DBG (3, - "sanei_usb_open: we already have a int-out endpoint " - "(address: 0x%02x), ignoring the new one\n", - devices[devcount].int_out_ep); - else - devices[devcount].int_out_ep = - endpoint->bEndpointAddress; - } - } - else if (transfer_type == USB_ENDPOINT_TYPE_BULK) - { - DBG (5, - "sanei_usb_open: found bulk-%s endpoint (address 0x%02x)\n", - direction ? "in" : "out", address); - if (direction) /* in */ - { - if (devices[devcount].bulk_in_ep) - DBG (3, - "sanei_usb_open: we already have a bulk-in endpoint " - "(address: 0x%02x), ignoring the new one\n", - devices[devcount].bulk_in_ep); - else - devices[devcount].bulk_in_ep = - endpoint->bEndpointAddress; - } - else - { - if (devices[devcount].bulk_out_ep) - DBG (3, - "sanei_usb_open: we already have a bulk-out endpoint " - "(address: 0x%02x), ignoring the new one\n", - devices[devcount].bulk_out_ep); - else - devices[devcount].bulk_out_ep = - endpoint->bEndpointAddress; - } - } - else if (transfer_type == USB_ENDPOINT_TYPE_ISOCHRONOUS) - { - DBG (5, - "sanei_usb_open: found isochronous-%s endpoint (address 0x%02x)\n", - direction ? "in" : "out", address); - if (direction) /* in */ - { - if (devices[devcount].iso_in_ep) - DBG (3, - "sanei_usb_open: we already have a isochronous-in endpoint " - "(address: 0x%02x), ignoring the new one\n", - devices[devcount].iso_in_ep); - else - devices[devcount].iso_in_ep = - endpoint->bEndpointAddress; - } - else - { - if (devices[devcount].iso_out_ep) - DBG (3, - "sanei_usb_open: we already have a isochronous-out endpoint " - "(address: 0x%02x), ignoring the new one\n", - devices[devcount].iso_out_ep); - else - devices[devcount].iso_out_ep = - endpoint->bEndpointAddress; - } - } - else if (transfer_type == USB_ENDPOINT_TYPE_CONTROL) - { - DBG (5, - "sanei_usb_open: found control-%s endpoint (address 0x%02x)\n", - direction ? "in" : "out", address); - if (direction) /* in */ - { - if (devices[devcount].control_in_ep) - DBG (3, - "sanei_usb_open: we already have a control-in endpoint " - "(address: 0x%02x), ignoring the new one\n", - devices[devcount].control_in_ep); - else - devices[devcount].control_in_ep = - endpoint->bEndpointAddress; - } - else - { - if (devices[devcount].control_out_ep) - DBG (3, - "sanei_usb_open: we already have a control-out endpoint " - "(address: 0x%02x), ignoring the new one\n", - devices[devcount].control_out_ep); - else - devices[devcount].control_out_ep = - endpoint->bEndpointAddress; - } - } + sanei_usb_add_endpoint(&devices[devcount], transfer_type, + endpoint->bEndpointAddress, + direction); } } } @@ -1769,124 +1720,39 @@ sanei_usb_open (SANE_String_Const devname, SANE_Int * dn) for (num = 0; num < interface->bNumEndpoints; num++) { const struct libusb_endpoint_descriptor *endpoint; - int address, direction, transfer_type; + int direction, transfer_type, transfer_type_libusb; endpoint = &interface->endpoint[num]; DBG (5, "sanei_usb_open: endpoint nr: %d\n", num); - transfer_type = endpoint->bmAttributes & LIBUSB_TRANSFER_TYPE_MASK; - address = endpoint->bEndpointAddress & LIBUSB_ENDPOINT_ADDRESS_MASK; + transfer_type_libusb = + endpoint->bmAttributes & LIBUSB_TRANSFER_TYPE_MASK; direction = endpoint->bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK; - DBG (5, "sanei_usb_open: direction: %d\n", direction); - DBG (5, "sanei_usb_open: address: %d transfertype: %d\n", - address, transfer_type); - - /* save the endpoints we need later */ - if (transfer_type == LIBUSB_TRANSFER_TYPE_INTERRUPT) - { - DBG (5, - "sanei_usb_open: found interrupt-%s endpoint (address 0x%02x)\n", - direction ? "in" : "out", address); - if (direction) /* in */ - { - if (devices[devcount].int_in_ep) - DBG (3, - "sanei_usb_open: we already have a int-in endpoint " - "(address: 0x%02x), ignoring the new one\n", - devices[devcount].int_in_ep); - else - devices[devcount].int_in_ep = endpoint->bEndpointAddress; - } - else - { - if (devices[devcount].int_out_ep) - DBG (3, - "sanei_usb_open: we already have a int-out endpoint " - "(address: 0x%02x), ignoring the new one\n", - devices[devcount].int_out_ep); - else - devices[devcount].int_out_ep = endpoint->bEndpointAddress; - } - } - else if (transfer_type == LIBUSB_TRANSFER_TYPE_BULK) - { - DBG (5, - "sanei_usb_open: found bulk-%s endpoint (address 0x%02x)\n", - direction ? "in" : "out", address); - if (direction) /* in */ - { - if (devices[devcount].bulk_in_ep) - DBG (3, - "sanei_usb_open: we already have a bulk-in endpoint " - "(address: 0x%02x), ignoring the new one\n", - devices[devcount].bulk_in_ep); - else - devices[devcount].bulk_in_ep = endpoint->bEndpointAddress; - } - else - { - if (devices[devcount].bulk_out_ep) - DBG (3, - "sanei_usb_open: we already have a bulk-out endpoint " - "(address: 0x%02x), ignoring the new one\n", - devices[devcount].bulk_out_ep); - else - devices[devcount].bulk_out_ep = endpoint->bEndpointAddress; - } - } - else if (transfer_type == LIBUSB_TRANSFER_TYPE_ISOCHRONOUS) - { - DBG (5, - "sanei_usb_open: found isochronous-%s endpoint (address 0x%02x)\n", - direction ? "in" : "out", address); - if (direction) /* in */ - { - if (devices[devcount].iso_in_ep) - DBG (3, - "sanei_usb_open: we already have a isochronous-in endpoint " - "(address: 0x%02x), ignoring the new one\n", - devices[devcount].iso_in_ep); - else - devices[devcount].iso_in_ep = endpoint->bEndpointAddress; - } - else - { - if (devices[devcount].iso_out_ep) - DBG (3, - "sanei_usb_open: we already have a isochronous-out endpoint " - "(address: 0x%02x), ignoring the new one\n", - devices[devcount].iso_out_ep); - else - devices[devcount].iso_out_ep = endpoint->bEndpointAddress; - } - } - else if (transfer_type == LIBUSB_TRANSFER_TYPE_CONTROL) - { - DBG (5, - "sanei_usb_open: found control-%s endpoint (address 0x%02x)\n", - direction ? "in" : "out", address); - if (direction) /* in */ - { - if (devices[devcount].control_in_ep) - DBG (3, - "sanei_usb_open: we already have a control-in endpoint " - "(address: 0x%02x), ignoring the new one\n", - devices[devcount].control_in_ep); - else - devices[devcount].control_in_ep = endpoint->bEndpointAddress; - } - else - { - if (devices[devcount].control_out_ep) - DBG (3, - "sanei_usb_open: we already have a control-out endpoint " - "(address: 0x%02x), ignoring the new one\n", - devices[devcount].control_out_ep); - else - devices[devcount].control_out_ep = endpoint->bEndpointAddress; - } - } + // don't rely on LIBUSB_TRANSFER_TYPE_* mapping to + // USB_ENDPOINT_TYPE_* even though they'll most likely be + // the same + switch (transfer_type_libusb) + { + case LIBUSB_TRANSFER_TYPE_INTERRUPT: + transfer_type = USB_ENDPOINT_TYPE_INTERRUPT; + break; + case LIBUSB_TRANSFER_TYPE_BULK: + transfer_type = USB_ENDPOINT_TYPE_BULK; + break; + case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS: + transfer_type = LIBUSB_TRANSFER_TYPE_ISOCHRONOUS; + break; + case LIBUSB_TRANSFER_TYPE_CONTROL: + transfer_type = USB_ENDPOINT_TYPE_CONTROL; + break; + + } + + sanei_usb_add_endpoint(&devices[devcount], + transfer_type, + endpoint->bEndpointAddress, + direction); } } } @@ -2016,62 +1882,21 @@ sanei_usb_open (SANE_String_Const devname, SANE_Int * dn) break; case USB_DT_ENDPOINT: endpoint = (struct usb_endpoint_descriptor*)pDescHead; - address = endpoint->bEndpointAddress; direction = endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK; transfer_type = endpoint->bmAttributes & USB_ENDPOINT_TYPE_MASK; - /* save the endpoints we need later */ - if (transfer_type == USB_ENDPOINT_TYPE_INTERRUPT) - { - DBG (5, "sanei_usb_open: found interupt-%s endpoint (address %2x)\n", - direction ? "in" : "out", address); - if (direction) /* in */ - { - if (devices[devcount].int_in_ep) - DBG (3, "sanei_usb_open: we already have a int-in endpoint " - "(address: %d), ignoring the new one\n", - devices[devcount].int_in_ep); - else - devices[devcount].int_in_ep = endpoint->bEndpointAddress; - } - else - if (devices[devcount].int_out_ep) - DBG (3, "sanei_usb_open: we already have a int-out endpoint " - "(address: %d), ignoring the new one\n", - devices[devcount].int_out_ep); - else - devices[devcount].int_out_ep = endpoint->bEndpointAddress; - } - else if (transfer_type == USB_ENDPOINT_TYPE_BULK) - { - DBG (5, "sanei_usb_open: found bulk-%s endpoint (address %2x)\n", - direction ? "in" : "out", address); - if (direction) /* in */ - { - if (devices[devcount].bulk_in_ep) - DBG (3, "sanei_usb_open: we already have a bulk-in endpoint " - "(address: %d), ignoring the new one\n", - devices[devcount].bulk_in_ep); - else - devices[devcount].bulk_in_ep = endpoint->bEndpointAddress; - } - else - { - if (devices[devcount].bulk_out_ep) - DBG (3, "sanei_usb_open: we already have a bulk-out endpoint " - "(address: %d), ignoring the new one\n", - devices[devcount].bulk_out_ep); - else - devices[devcount].bulk_out_ep = endpoint->bEndpointAddress; - } - } + + if (transfer_type == USB_ENDPOINT_TYPE_INTERRUPT || + transfer_type == USB_ENDPOINT_TYPE_BULK) + { + sanei_usb_add_endpoint(&devices[devcount], transfer_type, + endpoint->bEndpointAddress, direction); + } /* ignore currently unsupported endpoints */ else { DBG (5, "sanei_usb_open: ignoring %s-%s endpoint " "(address: %d)\n", - transfer_type == USB_ENDPOINT_TYPE_CONTROL ? "control" : - transfer_type == USB_ENDPOINT_TYPE_ISOCHRONOUS - ? "isochronous" : "interrupt", - direction ? "in" : "out", address); + sanei_usb_transfer_type_desc(transfer_type), + direction ? "in" : "out", address); continue; } break; @@ -2344,8 +2169,8 @@ sanei_usb_read_bulk (SANE_Int dn, SANE_Byte * buffer, size_t * size) if (ret < 0) { - DBG (1, "sanei_usb_read_bulk: read failed: %s\n", - sanei_libusb_strerror (ret)); + DBG (1, "sanei_usb_read_bulk: read failed (still got %d bytes): %s\n", + rsize, sanei_libusb_strerror (ret)); read_size = -1; } @@ -2372,9 +2197,10 @@ sanei_usb_read_bulk (SANE_Int dn, SANE_Byte * buffer, size_t * size) #ifdef HAVE_USBCALLS int rc; char* buffer_ptr = (char*) buffer; - while (*size) + size_t requested_size = *size; + while (requested_size) { - ULONG ulToRead = (*size>MAX_RW)?MAX_RW:*size; + ULONG ulToRead = (requested_size>MAX_RW)?MAX_RW:requested_size; ULONG ulNum = ulToRead; DBG (5, "Entered usbcalls UsbBulkRead with dn = %d\n",dn); DBG (5, "Entered usbcalls UsbBulkRead with dh = %p\n",dh); @@ -2392,7 +2218,7 @@ sanei_usb_read_bulk (SANE_Int dn, SANE_Byte * buffer, size_t * size) return SANE_STATUS_INVAL; } if (rc || (ulNum!=ulToRead)) return SANE_STATUS_INVAL; - *size -=ulToRead; + requested_size -=ulToRead; buffer_ptr += ulToRead; read_size += ulToRead; } @@ -2529,11 +2355,12 @@ sanei_usb_write_bulk (SANE_Int dn, const SANE_Byte * buffer, size_t * size) DBG (5, "Entered usbcalls UsbBulkWrite with bulk_out_ep = 0x%02x\n",devices[dn].bulk_out_ep); DBG (5, "Entered usbcalls UsbBulkWrite with interface_nr = %d\n",devices[dn].interface_nr); DBG (5, "Entered usbcalls UsbBulkWrite with usbcalls_timeout = %d\n",usbcalls_timeout); - while (*size) + size_t requested_size = *size; + while (requested_size) { - ULONG ulToWrite = (*size>MAX_RW)?MAX_RW:*size; + ULONG ulToWrite = (requested_size>MAX_RW)?MAX_RW:requested_size; - DBG (5, "size requested to write = %lu, ulToWrite = %lu\n",(unsigned long) *size,ulToWrite); + DBG (5, "size requested to write = %lu, ulToWrite = %lu\n",(unsigned long) requested_size,ulToWrite); if (devices[dn].bulk_out_ep){ rc = UsbBulkWrite (dh, devices[dn].bulk_out_ep, devices[dn].interface_nr, ulToWrite, (char*) buffer, usbcalls_timeout); @@ -2545,10 +2372,10 @@ sanei_usb_write_bulk (SANE_Int dn, const SANE_Byte * buffer, size_t * size) return SANE_STATUS_INVAL; } if (rc) return SANE_STATUS_INVAL; - *size -=ulToWrite; + requested_size -=ulToWrite; buffer += ulToWrite; write_size += ulToWrite; - DBG (5, "size = %d, write_size = %d\n",*size, write_size); + DBG (5, "size = %d, write_size = %d\n", requested_size, write_size); } #else /* not HAVE_USBCALLS */ { @@ -2619,7 +2446,6 @@ sanei_usb_control_msg (SANE_Int dn, SANE_Int rtype, SANE_Int req, } if ((rtype & 0x80) && debug_level > 10) print_buffer (data, len); - return SANE_STATUS_GOOD; #elif defined(__BEOS__) struct usb_scanner_ioctl_ctrlmsg c; @@ -2638,8 +2464,6 @@ sanei_usb_control_msg (SANE_Int dn, SANE_Int rtype, SANE_Int req, } if ((rtype & 0x80) && debug_level > 10) print_buffer (data, len); - - return SANE_STATUS_GOOD; #else /* not __linux__ */ DBG (5, "sanei_usb_control_msg: not supported on this OS\n"); return SANE_STATUS_UNSUPPORTED; @@ -2661,7 +2485,6 @@ sanei_usb_control_msg (SANE_Int dn, SANE_Int rtype, SANE_Int req, } if ((rtype & 0x80) && debug_level > 10) print_buffer (data, len); - return SANE_STATUS_GOOD; } #elif defined(HAVE_LIBUSB) { @@ -2678,7 +2501,6 @@ sanei_usb_control_msg (SANE_Int dn, SANE_Int rtype, SANE_Int req, } if ((rtype & 0x80) && debug_level > 10) print_buffer (data, len); - return SANE_STATUS_GOOD; } #else /* not HAVE_LIBUSB_LEGACY && not HAVE_LIBUSB*/ { @@ -2702,7 +2524,6 @@ sanei_usb_control_msg (SANE_Int dn, SANE_Int rtype, SANE_Int req, } if ((rtype & 0x80) && debug_level > 10) print_buffer (data, len); - return SANE_STATUS_GOOD; #else /* not HAVE_USBCALLS */ { DBG (1, "sanei_usb_control_msg: usbcalls support missing\n"); @@ -2716,6 +2537,7 @@ sanei_usb_control_msg (SANE_Int dn, SANE_Int rtype, SANE_Int req, devices[dn].method); return SANE_STATUS_UNSUPPORTED; } + return SANE_STATUS_GOOD; } SANE_Status @@ -3160,7 +2982,6 @@ sanei_usb_get_descriptor( SANE_Int dn, desc->dev_sub_class = usb_descr->bDeviceSubClass; desc->dev_protocol = usb_descr->bDeviceProtocol; desc->max_packet_size = usb_descr->bMaxPacketSize0; - return SANE_STATUS_GOOD; } #elif defined(HAVE_LIBUSB) { @@ -3185,7 +3006,6 @@ sanei_usb_get_descriptor( SANE_Int dn, desc->dev_sub_class = lu_desc.bDeviceSubClass; desc->dev_protocol = lu_desc.bDeviceProtocol; desc->max_packet_size = lu_desc.bMaxPacketSize0; - return SANE_STATUS_GOOD; } #else /* not HAVE_LIBUSB_LEGACY && not HAVE_LIBUSB */ { @@ -3193,4 +3013,5 @@ sanei_usb_get_descriptor( SANE_Int dn, return SANE_STATUS_UNSUPPORTED; } #endif /* not HAVE_LIBUSB_LEGACY && not HAVE_LIBUSB */ + return SANE_STATUS_GOOD; } -- cgit v1.2.3 From ffa8801644a7d53cc1c785e3450f794c07a14eb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Frings-F=C3=BCrst?= Date: Sun, 2 Feb 2020 17:13:01 +0100 Subject: New upstream version 1.0.29 --- sanei/sanei_usb.c | 1840 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 1819 insertions(+), 21 deletions(-) (limited to 'sanei/sanei_usb.c') diff --git a/sanei/sanei_usb.c b/sanei/sanei_usb.c index 6fd6040..db0f452 100644 --- a/sanei/sanei_usb.c +++ b/sanei/sanei_usb.c @@ -49,6 +49,7 @@ #include "../include/sane/config.h" #include +#include #include #include #include @@ -62,6 +63,9 @@ #include #include +#if WITH_USB_RECORD_REPLAY +#include +#endif #ifdef HAVE_RESMGR #include @@ -174,6 +178,34 @@ static int device_number=0; * count number of time sanei_usb has been initialized */ static int initialized=0; +typedef enum +{ + sanei_usb_testing_mode_disabled = 0, + + sanei_usb_testing_mode_record, // records the communication with the slave + // but does not change the USB stack in any + // way + sanei_usb_testing_mode_replay, // replays the communication with the scanner + // recorded earlier +} +sanei_usb_testing_mode; + +// Whether testing mode has been enabled +static sanei_usb_testing_mode testing_mode = sanei_usb_testing_mode_disabled; + +#if WITH_USB_RECORD_REPLAY +static int testing_development_mode = 0; +int testing_known_commands_input_failed = 0; +unsigned testing_last_known_seq = 0; +SANE_String testing_record_backend = NULL; +xmlNode* testing_append_commands_node = NULL; + +// XML file from which we read testing data +SANE_String testing_xml_path = NULL; +xmlDoc* testing_xml_doc = NULL; +xmlNode* testing_xml_next_tx_node = NULL; +#endif // WITH_USB_RECORD_REPLAY + #if defined(HAVE_LIBUSB_LEGACY) || defined(HAVE_LIBUSB) static int libusb_timeout = 30 * 1000; /* 30 seconds */ #endif /* HAVE_LIBUSB_LEGACY */ @@ -455,6 +487,859 @@ sanei_libusb_strerror (int errcode) } #endif /* HAVE_LIBUSB */ +#if WITH_USB_RECORD_REPLAY +SANE_Status sanei_usb_testing_enable_replay(SANE_String_Const path, + int development_mode) +{ + testing_mode = sanei_usb_testing_mode_replay; + testing_development_mode = development_mode; + + // TODO: we'll leak if noone ever inits sane_usb properly + testing_xml_path = strdup(path); + testing_xml_doc = xmlReadFile(testing_xml_path, NULL, 0); + if (!testing_xml_doc) + return SANE_STATUS_ACCESS_DENIED; + + return SANE_STATUS_GOOD; +} + +#define FAIL_TEST(func, ...) \ + do { \ + DBG(1, "%s: FAIL: ", func); \ + DBG(1, __VA_ARGS__); \ + fail_test(); \ + } while (0) + +#define FAIL_TEST_TX(func, node, ...) \ + do { \ + sanei_xml_print_seq_if_any(node, func); \ + DBG(1, "%s: FAIL: ", func); \ + DBG(1, __VA_ARGS__); \ + fail_test(); \ + } while (0) + +void fail_test() +{ +} + +SANE_Status sanei_usb_testing_enable_record(SANE_String_Const path, SANE_String_Const be_name) +{ + testing_mode = sanei_usb_testing_mode_record; + testing_record_backend = strdup(be_name); + testing_xml_path = strdup(path); + + return SANE_STATUS_GOOD; +} + +static xmlNode* sanei_xml_find_first_child_with_name(xmlNode* parent, + const char* name) +{ + xmlNode* curr_child = xmlFirstElementChild(parent); + while (curr_child != NULL) + { + if (xmlStrcmp(curr_child->name, (const xmlChar*)name) == 0) + return curr_child; + curr_child = xmlNextElementSibling(curr_child); + } + return NULL; +} + +static xmlNode* sanei_xml_find_next_child_with_name(xmlNode* child, + const char* name) +{ + xmlNode* curr_child = xmlNextElementSibling(child); + while (curr_child != NULL) + { + if (xmlStrcmp(curr_child->name, (const xmlChar*)name) == 0) + return curr_child; + curr_child = xmlNextElementSibling(curr_child); + } + return NULL; +} + +// a wrapper to get rid of -Wpointer-sign warnings in a single place +static char* sanei_xml_get_prop(xmlNode* node, const char* name) +{ + return (char*)xmlGetProp(node, (const xmlChar*)name); +} + +// returns -1 if attribute is not found +static int sanei_xml_get_prop_uint(xmlNode* node, const char* name) +{ + char* attr = sanei_xml_get_prop(node, name); + if (attr == NULL) + { + return -1; + } + + unsigned attr_uint = strtoul(attr, NULL, 0); + xmlFree(attr); + return attr_uint; +} + +static void sanei_xml_print_seq_if_any(xmlNode* node, const char* parent_fun) +{ + char* attr = sanei_xml_get_prop(node, "seq"); + if (attr == NULL) + return; + + DBG(1, "%s: FAIL: in transaction with seq %s:\n", parent_fun, attr); + xmlFree(attr); +} + +// Checks whether transaction should be ignored. We ignore get_descriptor and +// set_configuration transactions. The latter is ignored because +// set_configuration is called in sanei_usb_open outside test path. +static int sanei_xml_is_transaction_ignored(xmlNode* node) +{ + if (xmlStrcmp(node->name, (const xmlChar*)"control_tx") != 0) + return 0; + + if (sanei_xml_get_prop_uint(node, "endpoint_number") != 0) + return 0; + + int is_direction_in = 0; + int is_direction_out = 0; + + char* attr = sanei_xml_get_prop(node, "direction"); + if (attr == NULL) + return 0; + + if (strcmp(attr, "IN") == 0) + is_direction_in = 1; + if (strcmp(attr, "OUT") == 0) + is_direction_out = 1; + xmlFree(attr); + + unsigned bRequest = sanei_xml_get_prop_uint(node, "bRequest"); + if (bRequest == USB_REQ_GET_DESCRIPTOR && is_direction_in) + { + if (sanei_xml_get_prop_uint(node, "bmRequestType") != 0x80) + return 0; + return 1; + } + if (bRequest == USB_REQ_SET_CONFIGURATION && is_direction_out) + return 1; + + return 0; +} + +static xmlNode* sanei_xml_skip_non_tx_nodes(xmlNode* node) +{ + const char* known_node_names[] = { + "control_tx", "bulk_tx", "interrupt_tx", "debug", "known_commands_end" + }; + + while (node != NULL) + { + int found = 0; + for (unsigned i = 0; i < sizeof(known_node_names) / + sizeof(known_node_names[0]); ++i) + { + if (xmlStrcmp(node->name, (const xmlChar*) known_node_names[i]) == 0) + { + found = 1; + break; + } + } + + if (found && sanei_xml_is_transaction_ignored(node) == 0) + { + break; + } + + node = xmlNextElementSibling(node); + } + return node; +} + +static int sanei_xml_is_known_commands_end(xmlNode* node) +{ + if (!testing_development_mode || node == NULL) + return 0; + return xmlStrcmp(node->name, (const xmlChar*)"known_commands_end") == 0; +} + +// returns next transaction node that is not get_descriptor +static xmlNode* sanei_xml_peek_next_tx_node() +{ + return testing_xml_next_tx_node; +} + +// returns next transaction node that is not get_descriptor +static xmlNode* sanei_xml_get_next_tx_node() +{ + xmlNode* next = testing_xml_next_tx_node; + + if (sanei_xml_is_known_commands_end(next)) + { + testing_append_commands_node = xmlPreviousElementSibling(next); + return next; + } + + testing_xml_next_tx_node = + xmlNextElementSibling(testing_xml_next_tx_node); + + testing_xml_next_tx_node = + sanei_xml_skip_non_tx_nodes(testing_xml_next_tx_node); + + return next; +} + +#define CHAR_TYPE_INVALID -1 +#define CHAR_TYPE_SPACE -2 + +static int8_t sanei_xml_char_types[256] = +{ + /* 0x00-0x0f */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -2, -2, -2, -2, -2, -1, -1, + /* 0x10-0x1f */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + /* 0x20-0x2f */ -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + /* 0x30-0x3f */ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, + /* 0x40-0x4f */ -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, + /* 0x50-0x5f */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + /* 0x60-0x6f */ -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, + /* 0x70-0x7f */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + /* 0x80-0x8f */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + /* 0x90-0x9f */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + /* 0xa0-0xaf */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + /* 0xb0-0xbf */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + /* 0xc0-0xcf */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + /* 0xd0-0xdf */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + /* 0xe0-0xef */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + /* 0xf0-0xff */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +}; + +static char* sanei_xml_get_hex_data_slow_path(xmlNode* node, xmlChar* content, xmlChar* cur_content, + char* ret_data, char* cur_ret_data, size_t* size) +{ + int num_nibbles = 0; + unsigned cur_nibble = 0; + + while (*cur_content != 0) + { + while (sanei_xml_char_types[(uint8_t)*cur_content] == CHAR_TYPE_SPACE) + cur_content++; + + if (*cur_content == 0) + break; + + // don't use stroul because it will parse in big-endian and data is in + // little endian + uint8_t c = *cur_content; + int8_t ci = sanei_xml_char_types[c]; + if (ci == CHAR_TYPE_INVALID) + { + FAIL_TEST_TX(__func__, node, "unexpected character %c\n", c); + cur_content++; + continue; + } + + cur_nibble = (cur_nibble << 4) | ci; + num_nibbles++; + + if (num_nibbles == 2) + { + *cur_ret_data++ = cur_nibble; + cur_nibble = 0; + num_nibbles = 0; + } + cur_content++; + } + *size = cur_ret_data - ret_data; + xmlFree(content); + return ret_data; +} + +// Parses hex data in XML text node in the format of '00 11 ab 3f', etc. to +// binary string. The size is returned as *size. The caller is responsible for +// freeing the returned value +static char* sanei_xml_get_hex_data(xmlNode* node, size_t* size) +{ + xmlChar* content = xmlNodeGetContent(node); + + // let's overallocate to simplify the implementation. We expect the string + // to be deallocated soon anyway + char* ret_data = malloc(strlen((const char*)content) / 2 + 2); + char* cur_ret_data = ret_data; + + xmlChar* cur_content = content; + + // the text to binary conversion takes most of the time spent in tests, so we + // take extra care to optimize it. We split the implementation into fast and + // slow path. The fast path utilizes the knowledge that there will be no spaces + // within bytes. When this assumption does not hold, we switch to the slow path. + while (*cur_content != 0) + { + // most of the time there will be 1 or 2 spaces between bytes. Give the CPU + // chance to predict this by partially unrolling the while loop. + if (sanei_xml_char_types[(uint8_t)*cur_content] == CHAR_TYPE_SPACE) + { + cur_content++; + if (sanei_xml_char_types[(uint8_t)*cur_content] == CHAR_TYPE_SPACE) + { + cur_content++; + while (sanei_xml_char_types[(uint8_t)*cur_content] == CHAR_TYPE_SPACE) + cur_content++; + } + } + + if (*cur_content == 0) + break; + + // don't use stroul because it will parse in big-endian and data is in + // little endian + int8_t ci1 = sanei_xml_char_types[(uint8_t)*cur_content]; + int8_t ci2 = sanei_xml_char_types[(uint8_t)*(cur_content + 1)]; + + if (ci1 < 0 || ci2 < 0) + return sanei_xml_get_hex_data_slow_path(node, content, cur_content, ret_data, cur_ret_data, + size); + + *cur_ret_data++ = ci1 << 4 | ci2; + cur_content += 2; + } + *size = cur_ret_data - ret_data; + xmlFree(content); + return ret_data; +} + +// caller is responsible for freeing the returned pointer +static char* sanei_binary_to_hex_data(const char* data, size_t size, + size_t* out_size) +{ + char* hex_data = malloc(size * 4); + size_t hex_size = 0; + + for (size_t i = 0; i < size; ++i) + { + hex_size += snprintf(hex_data + hex_size, 3, "%02hhx", data[i]); + if (i + 1 != size) + { + if ((i + 1) % 32 == 0) + hex_data[hex_size++] = '\n'; + else + hex_data[hex_size++] = ' '; + } + } + hex_data[hex_size] = 0; + if (out_size) + *out_size = hex_size; + return hex_data; +} + + +static void sanei_xml_set_data(xmlNode* node, const char* data) +{ + // FIXME: remove existing children + xmlAddChild(node, xmlNewText((const xmlChar*)data)); +} + +// Writes binary data to XML node as a child text node in the hex format of +// '00 11 ab 3f'. +static void sanei_xml_set_hex_data(xmlNode* node, const char* data, + size_t size) +{ + char* hex_data = sanei_binary_to_hex_data(data, size, NULL); + sanei_xml_set_data(node, hex_data); + free(hex_data); +} + +static void sanei_xml_set_hex_attr(xmlNode* node, const char* attr_name, + unsigned attr_value) +{ + const int buf_size = 128; + char buf[buf_size]; + if (attr_value > 0xffffff) + snprintf(buf, buf_size, "0x%x", attr_value); + else if (attr_value > 0xffff) + snprintf(buf, buf_size, "0x%06x", attr_value); + else if (attr_value > 0xff) + snprintf(buf, buf_size, "0x%04x", attr_value); + else + snprintf(buf, buf_size, "0x%02x", attr_value); + + xmlNewProp(node, (const xmlChar*)attr_name, (const xmlChar*)buf); +} + +static void sanei_xml_set_uint_attr(xmlNode* node, const char* attr_name, + unsigned attr_value) +{ + const int buf_size = 128; + char buf[buf_size]; + snprintf(buf, buf_size, "%d", attr_value); + xmlNewProp(node, (const xmlChar*)attr_name, (const xmlChar*)buf); +} + +static xmlNode* sanei_xml_append_command(xmlNode* sibling, + int indent, xmlNode* e_command) +{ + if (indent) + { + xmlNode* e_indent = xmlNewText((const xmlChar*)"\n "); + sibling = xmlAddNextSibling(sibling, e_indent); + } + return xmlAddNextSibling(sibling, e_command); +} + +static void sanei_xml_command_common_props(xmlNode* node, int endpoint_number, + const char* direction) +{ + xmlNewProp(node, (const xmlChar*)"time_usec", (const xmlChar*)"0"); + sanei_xml_set_uint_attr(node, "seq", ++testing_last_known_seq); + sanei_xml_set_uint_attr(node, "endpoint_number", endpoint_number); + xmlNewProp(node, (const xmlChar*)"direction", (const xmlChar*)direction); +} + +static void sanei_xml_record_seq(xmlNode* node) +{ + int seq = sanei_xml_get_prop_uint(node, "seq"); + if (seq > 0) + testing_last_known_seq = seq; +} + +static void sanei_xml_break() +{ +} + +static void sanei_xml_break_if_needed(xmlNode* node) +{ + char* attr = sanei_xml_get_prop(node, "debug_break"); + if (attr != NULL) + { + sanei_xml_break(); + xmlFree(attr); + } +} + +// returns 1 on success +static int sanei_usb_check_attr(xmlNode* node, const char* attr_name, + const char* expected, const char* parent_fun) +{ + char* attr = sanei_xml_get_prop(node, attr_name); + if (attr == NULL) + { + FAIL_TEST_TX(parent_fun, node, "no %s attribute\n", attr_name); + return 0; + } + + if (strcmp(attr, expected) != 0) + { + FAIL_TEST_TX(parent_fun, node, "unexpected %s attribute: %s, wanted %s\n", + attr_name, attr, expected); + xmlFree(attr); + return 0; + } + xmlFree(attr); + return 1; +} + +// returns 1 on success +static int sanei_usb_attr_is(xmlNode* node, const char* attr_name, + const char* expected) +{ + char* attr = sanei_xml_get_prop(node, attr_name); + if (attr == NULL) + return 0; + + if (strcmp(attr, expected) != 0) + { + xmlFree(attr); + return 0; + } + xmlFree(attr); + return 1; +} + +// returns 0 on success +static int sanei_usb_check_attr_uint(xmlNode* node, const char* attr_name, + unsigned expected, const char* parent_fun) +{ + char* attr = sanei_xml_get_prop(node, attr_name); + if (attr == NULL) + { + FAIL_TEST_TX(parent_fun, node, "no %s attribute\n", attr_name); + return 0; + } + + unsigned attr_int = strtoul(attr, NULL, 0); + if (attr_int != expected) + { + FAIL_TEST_TX(parent_fun, node, + "unexpected %s attribute: %s, wanted 0x%x\n", + attr_name, attr, expected); + xmlFree(attr); + return 0; + } + xmlFree(attr); + return 1; +} + +static int sanei_usb_attr_is_uint(xmlNode* node, const char* attr_name, + unsigned expected) +{ + char* attr = sanei_xml_get_prop(node, attr_name); + if (attr == NULL) + return 0; + + unsigned attr_int = strtoul(attr, NULL, 0); + if (attr_int != expected) + { + xmlFree(attr); + return 0; + } + xmlFree(attr); + return 1; +} + +// returns 1 on data equality +static int sanei_usb_check_data_equal(xmlNode* node, + const char* data, + size_t data_size, + const char* expected_data, + size_t expected_size, + const char* parent_fun) +{ + if ((data_size == expected_size) && + (memcmp(data, expected_data, data_size) == 0)) + return 1; + + char* data_hex = sanei_binary_to_hex_data(data, data_size, NULL); + char* expected_hex = sanei_binary_to_hex_data(expected_data, expected_size, + NULL); + + if (data_size == expected_size) + FAIL_TEST_TX(parent_fun, node, "data differs (size %lu):\n", data_size); + else + FAIL_TEST_TX(parent_fun, node, + "data differs (got size %lu, expected %lu):\n", + data_size, expected_size); + + FAIL_TEST(parent_fun, "got: %s\n", data_hex); + FAIL_TEST(parent_fun, "expected: %s\n", expected_hex); + free(data_hex); + free(expected_hex); + return 0; +} + +SANE_String sanei_usb_testing_get_backend() +{ + if (testing_xml_doc == NULL) + return NULL; + + xmlNode* el_root = xmlDocGetRootElement(testing_xml_doc); + if (xmlStrcmp(el_root->name, (const xmlChar*)"device_capture") != 0) + { + FAIL_TEST(__func__, "the given file is not USB capture\n"); + return NULL; + } + + char* attr = sanei_xml_get_prop(el_root, "backend"); + if (attr == NULL) + { + FAIL_TEST(__func__, "no backend attr in description node\n"); + return NULL; + } + // duplicate using strdup so that the caller can use free() + char* ret = strdup(attr); + xmlFree(attr); + return ret; +} + +SANE_Bool sanei_usb_is_replay_mode_enabled() +{ + if (testing_mode == sanei_usb_testing_mode_replay) + return SANE_TRUE; + + return SANE_FALSE; +} + +static void sanei_usb_record_debug_msg(xmlNode* node, SANE_String_Const message) +{ + int node_was_null = node == NULL; + if (node_was_null) + node = testing_append_commands_node; + + xmlNode* e_tx = xmlNewNode(NULL, (const xmlChar*)"debug"); + sanei_xml_set_uint_attr(e_tx, "seq", ++testing_last_known_seq); + xmlNewProp(e_tx, (const xmlChar*)"message", (const xmlChar*)message); + + node = sanei_xml_append_command(node, node_was_null, e_tx); + + if (node_was_null) + testing_append_commands_node = node; +} + +static void sanei_usb_record_replace_debug_msg(xmlNode* node, SANE_String_Const message) +{ + if (!testing_development_mode) + return; + + testing_last_known_seq--; + sanei_usb_record_debug_msg(node, message); + xmlUnlinkNode(node); + xmlFreeNode(node); +} + +static void sanei_usb_replay_debug_msg(SANE_String_Const message) +{ + if (testing_known_commands_input_failed) + return; + + xmlNode* node = sanei_xml_get_next_tx_node(); + if (node == NULL) + { + FAIL_TEST(__func__, "no more transactions\n"); + return; + } + + if (sanei_xml_is_known_commands_end(node)) + { + sanei_usb_record_debug_msg(NULL, message); + return; + } + + sanei_xml_record_seq(node); + sanei_xml_break_if_needed(node); + + if (xmlStrcmp(node->name, (const xmlChar*)"debug") != 0) + { + FAIL_TEST_TX(__func__, node, "unexpected transaction type %s\n", + (const char*) node->name); + sanei_usb_record_replace_debug_msg(node, message); + } + + if (!sanei_usb_check_attr(node, "message", message, __func__)) + { + sanei_usb_record_replace_debug_msg(node, message); + } +} + +extern void sanei_usb_testing_record_message(SANE_String_Const message) +{ + if (testing_mode == sanei_usb_testing_mode_record) + { + sanei_usb_record_debug_msg(NULL, message); + } + if (testing_mode == sanei_usb_testing_mode_replay) + { + sanei_usb_replay_debug_msg(message); + } +} + +static void sanei_usb_add_endpoint(device_list_type* device, + SANE_Int transfer_type, + SANE_Int ep_address, + SANE_Int ep_direction); + +static SANE_Status sanei_usb_testing_init() +{ + DBG_INIT(); + + if (testing_mode == sanei_usb_testing_mode_record) + { + testing_xml_doc = xmlNewDoc((const xmlChar*)"1.0"); + return SANE_STATUS_GOOD; + } + + if (device_number != 0) + return SANE_STATUS_INVAL; // already opened + + xmlNode* el_root = xmlDocGetRootElement(testing_xml_doc); + if (xmlStrcmp(el_root->name, (const xmlChar*)"device_capture") != 0) + { + DBG(1, "%s: the given file is not USB capture\n", __func__); + return SANE_STATUS_INVAL; + } + + xmlNode* el_description = + sanei_xml_find_first_child_with_name(el_root, "description"); + if (el_description == NULL) + { + DBG(1, "%s: could not find description node\n", __func__); + return SANE_STATUS_INVAL; + } + + int device_id = sanei_xml_get_prop_uint(el_description, "id_vendor"); + if (device_id < 0) + { + DBG(1, "%s: no id_vendor attr in description node\n", __func__); + return SANE_STATUS_INVAL; + } + + int product_id = sanei_xml_get_prop_uint(el_description, "id_product"); + if (product_id < 0) + { + DBG(1, "%s: no id_product attr in description node\n", __func__); + return SANE_STATUS_INVAL; + } + + xmlNode* el_configurations = + sanei_xml_find_first_child_with_name(el_description, "configurations"); + if (el_configurations == NULL) + { + DBG(1, "%s: could not find configurations node\n", __func__); + return SANE_STATUS_INVAL; + } + + xmlNode* el_configuration = + sanei_xml_find_first_child_with_name(el_configurations, "configuration"); + if (el_configuration == NULL) + { + DBG(1, "%s: no configuration nodes\n", __func__); + return SANE_STATUS_INVAL; + } + + while (el_configuration != NULL) + { + xmlNode* el_interface = + sanei_xml_find_first_child_with_name(el_configuration, "interface"); + + while (el_interface != NULL) + { + device_list_type device; + memset(&device, 0, sizeof(device)); + device.devname = strdup(testing_xml_path); + + // other code shouldn't depend on methon because testing_mode is + // sanei_usb_testing_mode_replay + device.method = sanei_usb_method_libusb; + device.vendor = device_id; + device.product = product_id; + + device.interface_nr = sanei_xml_get_prop_uint(el_interface, "number"); + if (device.interface_nr < 0) + { + DBG(1, "%s: no number attr in interface node\n", __func__); + return SANE_STATUS_INVAL; + } + + xmlNode* el_endpoint = + sanei_xml_find_first_child_with_name(el_interface, "endpoint"); + + while (el_endpoint != NULL) + { + char* transfer_attr = sanei_xml_get_prop(el_endpoint, + "transfer_type"); + int address = sanei_xml_get_prop_uint(el_endpoint, "address"); + char* direction_attr = sanei_xml_get_prop(el_endpoint, + "direction"); + + int direction_is_in = strcmp(direction_attr, "IN") == 0 ? 1 : 0; + int transfer_type = -1; + if (strcmp(transfer_attr, "INTERRUPT") == 0) + transfer_type = USB_ENDPOINT_TYPE_INTERRUPT; + else if (strcmp(transfer_attr, "BULK") == 0) + transfer_type = USB_ENDPOINT_TYPE_BULK; + else if (strcmp(transfer_attr, "ISOCHRONOUS") == 0) + transfer_type = USB_ENDPOINT_TYPE_ISOCHRONOUS; + else if (strcmp(transfer_attr, "CONTROL") == 0) + transfer_type = USB_ENDPOINT_TYPE_CONTROL; + else + { + DBG(3, "%s: unknown endpoint type %s\n", + __func__, transfer_attr); + } + + if (transfer_type >= 0) + { + sanei_usb_add_endpoint(&device, transfer_type, address, + direction_is_in); + } + + xmlFree(transfer_attr); + xmlFree(direction_attr); + + el_endpoint = + sanei_xml_find_next_child_with_name(el_endpoint, "endpoint"); + } + device.alt_setting = 0; + device.missing = 0; + + memcpy(&(devices[device_number]), &device, sizeof(device)); + device_number++; + + el_interface = sanei_xml_find_next_child_with_name(el_interface, + "interface"); + } + el_configuration = + sanei_xml_find_next_child_with_name(el_configurations, + "configuration"); + } + + xmlNode* el_transactions = + sanei_xml_find_first_child_with_name(el_root, "transactions"); + + if (el_transactions == NULL) + { + DBG(1, "%s: could not find transactions node\n", __func__); + return SANE_STATUS_INVAL; + } + + xmlNode* el_transaction = xmlFirstElementChild(el_transactions); + el_transaction = sanei_xml_skip_non_tx_nodes(el_transaction); + + if (el_transaction == NULL) + { + DBG(1, "%s: no transactions within capture\n", __func__); + return SANE_STATUS_INVAL; + } + + testing_xml_next_tx_node = el_transaction; + + return SANE_STATUS_GOOD; +} + +static void sanei_usb_testing_exit() +{ + if (testing_development_mode || testing_mode == sanei_usb_testing_mode_record) + { + if (testing_mode == sanei_usb_testing_mode_record) + { + xmlAddNextSibling(testing_append_commands_node, xmlNewText((const xmlChar*)"\n ")); + free(testing_record_backend); + } + xmlSaveFileEnc(testing_xml_path, testing_xml_doc, "UTF-8"); + } + xmlFreeDoc(testing_xml_doc); + free(testing_xml_path); + xmlCleanupParser(); +} +#else // WITH_USB_RECORD_REPLAY +SANE_Status sanei_usb_testing_enable_replay(SANE_String_Const path, + int development_mode) +{ + (void) path; + (void) development_mode; + + DBG(1, "USB record-replay mode support is missing\n"); + return SANE_STATUS_UNSUPPORTED; +} + +SANE_Status sanei_usb_testing_enable_record(SANE_String_Const path, SANE_String_Const be_name) +{ + (void) path; + (void) be_name; + + DBG(1, "USB record-replay mode support is missing\n"); + return SANE_STATUS_UNSUPPORTED; +} + +SANE_String sanei_usb_testing_get_backend() +{ + return NULL; +} + +SANE_Bool sanei_usb_is_replay_mode_enabled() +{ + return SANE_FALSE; +} + +void sanei_usb_testing_record_message(SANE_String_Const message) +{ + (void) message; +} +#endif // WITH_USB_RECORD_REPLAY + void sanei_usb_init (void) { @@ -473,6 +1358,26 @@ sanei_usb_init (void) if(device_number==0) memset (devices, 0, sizeof (devices)); +#if WITH_USB_RECORD_REPLAY + if (testing_mode != sanei_usb_testing_mode_disabled) + { + if (initialized == 0) + { + if (sanei_usb_testing_init() != SANE_STATUS_GOOD) + { + DBG(1, "%s: failed initializing fake USB stack\n", __func__); + return; + } + } + + if (testing_mode == sanei_usb_testing_mode_replay) + { + initialized++; + return; + } + } +#endif + /* initialize USB with old libusb library */ #ifdef HAVE_LIBUSB_LEGACY DBG (4, "%s: Looking for libusb devices\n", __func__); @@ -499,7 +1404,12 @@ sanei_usb_init (void) } #ifdef DBG_LEVEL if (DBG_LEVEL > 4) +#if LIBUSB_API_VERSION >= 0x01000106 + libusb_set_option (sanei_usb_ctx, LIBUSB_OPTION_LOG_LEVEL, + LIBUSB_LOG_LEVEL_INFO); +#else libusb_set_debug (sanei_usb_ctx, 3); +#endif /* LIBUSB_API_VERSION */ #endif /* DBG_LEVEL */ } #endif /* HAVE_LIBUSB */ @@ -533,6 +1443,13 @@ int i; /* if we reach 0, free allocated resources */ if(initialized==0) { +#if WITH_USB_RECORD_REPLAY + if (testing_mode != sanei_usb_testing_mode_disabled) + { + sanei_usb_testing_exit(); + } +#endif // WITH_USB_RECORD_REPLAY + /* free allocated resources */ DBG (4, "%s: freeing resources\n", __func__); for (i = 0; i < device_number; i++) @@ -1038,6 +1955,11 @@ sanei_usb_scan_devices (void) return; } + if (testing_mode == sanei_usb_testing_mode_replay) + { + // device added in sanei_usb_testing_init() + return; + } /* we mark all already detected devices as missing */ /* each scan method will reset this value to 0 (not missing) * when storing the device */ @@ -1262,7 +2184,7 @@ sanei_usb_set_endpoint (SANE_Int dn, SANE_Int ep_type, SANE_Int ep) } } -#if HAVE_LIBUSB_LEGACY || HAVE_LIBUSB || HAVE_USBCALLS +#if HAVE_LIBUSB_LEGACY || HAVE_LIBUSB || HAVE_USBCALLS || WITH_USB_RECORD_REPLAY static const char* sanei_usb_transfer_type_desc(SANE_Int transfer_type) { switch (transfer_type) @@ -1365,6 +2287,64 @@ sanei_usb_get_endpoint (SANE_Int dn, SANE_Int ep_type) } } +#if WITH_USB_RECORD_REPLAY +static void sanei_usb_record_open(SANE_Int dn) +{ + xmlNode* e_root = xmlNewNode(NULL, (const xmlChar*) "device_capture"); + xmlDocSetRootElement(testing_xml_doc, e_root); + xmlNewProp(e_root, (const xmlChar*)"backend", (const xmlChar*) testing_record_backend); + + xmlNode* e_description = xmlNewChild(e_root, NULL, (const xmlChar*) "description", NULL); + sanei_xml_set_hex_attr(e_description, "id_vendor", devices[dn].vendor); + sanei_xml_set_hex_attr(e_description, "id_product", devices[dn].product); + + xmlNode* e_configurations = xmlNewChild(e_description, NULL, + (const xmlChar*) "configurations", NULL); + xmlNode* e_configuration = xmlNewChild(e_configurations, NULL, + (const xmlChar*) "configuration", NULL); + sanei_xml_set_uint_attr(e_configuration, "number", 1); + + xmlNode* e_interface = xmlNewChild(e_configuration, NULL, (const xmlChar*) "interface", NULL); + sanei_xml_set_uint_attr(e_interface, "number", devices[dn].interface_nr); + + struct endpoint_data_desc { + const char* transfer_type; + const char* direction; + SANE_Int ep_address; + }; + + struct endpoint_data_desc endpoints[8] = + { + { "BULK", "IN", devices[dn].bulk_in_ep }, + { "BULK", "OUT", devices[dn].bulk_out_ep }, + { "ISOCHRONOUS", "IN", devices[dn].iso_in_ep }, + { "ISOCHRONOUS", "OUT", devices[dn].iso_out_ep }, + { "INTERRUPT", "IN", devices[dn].int_in_ep }, + { "INTERRUPT", "OUT", devices[dn].int_out_ep }, + { "CONTROL", "IN", devices[dn].control_in_ep }, + { "CONTROL", "OUT", devices[dn].control_out_ep } + }; + + for (int i = 0; i < 8; ++i) + { + if (endpoints[i].ep_address) + { + xmlNode* e_endpoint = xmlNewChild(e_interface, NULL, (const xmlChar*)"endpoint", NULL); + xmlNewProp(e_endpoint, (const xmlChar*)"transfer_type", + (const xmlChar*) endpoints[i].transfer_type); + sanei_xml_set_uint_attr(e_endpoint, "number", endpoints[i].ep_address & 0x0f); + xmlNewProp(e_endpoint, (const xmlChar*)"direction", + (const xmlChar*) endpoints[i].direction); + sanei_xml_set_hex_attr(e_endpoint, "address", endpoints[i].ep_address); + } + } + xmlNode* e_transactions = xmlNewChild(e_root, NULL, (const xmlChar*)"transactions", NULL); + + // add an empty node so that we have something to append to + testing_append_commands_node = xmlAddChild(e_transactions, xmlNewText((const xmlChar*)""));; +} +#endif // WITH_USB_RECORD_REPLAY + SANE_Status sanei_usb_open (SANE_String_Const devname, SANE_Int * dn) { @@ -1400,7 +2380,13 @@ sanei_usb_open (SANE_String_Const devname, SANE_Int * dn) return SANE_STATUS_INVAL; } - if (devices[devcount].method == sanei_usb_method_libusb) + if (testing_mode == sanei_usb_testing_mode_replay) + { + DBG (1, "sanei_usb_open: opening fake USB device\n"); + // the device configuration has been already filled in + // sanei_usb_testing_init() + } + else if (devices[devcount].method == sanei_usb_method_libusb) { #ifdef HAVE_LIBUSB_LEGACY struct usb_device *dev; @@ -1915,6 +2901,16 @@ sanei_usb_open (SANE_String_Const devname, SANE_Int * dn) return SANE_STATUS_INVAL; } + if (testing_mode == sanei_usb_testing_mode_record) + { +#if WITH_USB_RECORD_REPLAY + sanei_usb_record_open(devcount); +#else + DBG (1, "USB record-replay mode support is missing\n"); + return SANE_STATUS_UNSUPPORTED; +#endif + } + devices[devcount].open = SANE_TRUE; *dn = devcount; DBG (3, "sanei_usb_open: opened usb device `%s' (*dn=%d)\n", @@ -1948,7 +2944,11 @@ sanei_usb_close (SANE_Int dn) dn); return; } - if (devices[dn].method == sanei_usb_method_scanner_driver) + if (testing_mode == sanei_usb_testing_mode_replay) + { + DBG (1, "sanei_usb_close: closing fake USB device\n"); + } + else if (devices[dn].method == sanei_usb_method_scanner_driver) close (devices[dn].fd); else if (devices[dn].method == sanei_usb_method_usbcalls) { @@ -2001,6 +3001,9 @@ sanei_usb_close (SANE_Int dn) void sanei_usb_set_timeout (SANE_Int __sane_unused__ timeout) { + if (testing_mode == sanei_usb_testing_mode_replay) + return; + #if defined(HAVE_LIBUSB_LEGACY) || defined(HAVE_LIBUSB) libusb_timeout = timeout; #else @@ -2028,6 +3031,9 @@ sanei_usb_clear_halt (SANE_Int dn) return SANE_STATUS_INVAL; } + if (testing_mode == sanei_usb_testing_mode_replay) + return SANE_STATUS_GOOD; + #ifdef HAVE_LIBUSB_LEGACY int ret; @@ -2085,6 +3091,9 @@ sanei_usb_clear_halt (SANE_Int dn) SANE_Status sanei_usb_reset (SANE_Int __sane_unused__ dn) { + if (testing_mode == sanei_usb_testing_mode_replay) + return SANE_STATUS_GOOD; + #ifdef HAVE_LIBUSB_LEGACY int ret; @@ -2110,6 +3119,160 @@ sanei_usb_reset (SANE_Int __sane_unused__ dn) return SANE_STATUS_GOOD; } +#if WITH_USB_RECORD_REPLAY +// returns non-negative value on success, -1 on failure +static int sanei_usb_replay_next_read_bulk_packet_size(SANE_Int dn) +{ + xmlNode* node = sanei_xml_peek_next_tx_node(); + if (node == NULL) + return -1; + + if (xmlStrcmp(node->name, (const xmlChar*)"bulk_tx") != 0) + { + return -1; + } + + if (!sanei_usb_attr_is(node, "direction", "IN")) + return -1; + if (!sanei_usb_attr_is_uint(node, "endpoint_number", + devices[dn].bulk_in_ep & 0x0f)) + return -1; + + size_t got_size = 0; + char* got_data = sanei_xml_get_hex_data(node, &got_size); + free(got_data); + return got_size; +} + +static void sanei_usb_record_read_bulk(xmlNode* node, SANE_Int dn, + SANE_Byte* buffer, + size_t size, ssize_t read_size) +{ + int node_was_null = node == NULL; + if (node_was_null) + node = testing_append_commands_node; + + xmlNode* e_tx = xmlNewNode(NULL, (const xmlChar*)"bulk_tx"); + sanei_xml_command_common_props(e_tx, devices[dn].bulk_in_ep & 0x0f, "IN"); + + if (buffer == NULL) + { + const int buf_size = 128; + char buf[buf_size]; + snprintf(buf, buf_size, "(unknown read of allowed size %ld)", size); + xmlNode* e_content = xmlNewText((const xmlChar*)buf); + xmlAddChild(e_tx, e_content); + } + else + { + if (read_size >= 0) + { + sanei_xml_set_hex_data(e_tx, (const char*)buffer, read_size); + } + else + { + xmlNewProp(e_tx, (const xmlChar*)"error", (const xmlChar*)"timeout"); + } + } + + node = sanei_xml_append_command(node, node_was_null, e_tx); + + if (node_was_null) + testing_append_commands_node = node; +} + +static void sanei_usb_record_replace_read_bulk(xmlNode* node, SANE_Int dn, + SANE_Byte* buffer, + size_t size, size_t read_size) +{ + if (!testing_development_mode) + return; + testing_known_commands_input_failed = 1; + testing_last_known_seq--; + sanei_usb_record_read_bulk(node, dn, buffer, size, read_size); + xmlUnlinkNode(node); + xmlFreeNode(node); +} + +static int sanei_usb_replay_read_bulk(SANE_Int dn, SANE_Byte* buffer, + size_t size) +{ + // libusb may potentially combine multiple IN packets into a single transfer. + // We recontruct that by looking into the next packet. If it can be + // included into the current transfer without + size_t wanted_size = size; + size_t total_got_size = 0; + while (wanted_size > 0) + { + if (testing_known_commands_input_failed) + return -1; + + xmlNode* node = sanei_xml_get_next_tx_node(); + if (node == NULL) + { + FAIL_TEST(__func__, "no more transactions\n"); + return -1; + } + + if (sanei_xml_is_known_commands_end(node)) + { + sanei_usb_record_read_bulk(NULL, dn, NULL, 0, size); + testing_known_commands_input_failed = 1; + return -1; + } + + sanei_xml_record_seq(node); + sanei_xml_break_if_needed(node); + + if (xmlStrcmp(node->name, (const xmlChar*)"bulk_tx") != 0) + { + FAIL_TEST_TX(__func__, node, "unexpected transaction type %s\n", + (const char*) node->name); + sanei_usb_record_replace_read_bulk(node, dn, NULL, 0, wanted_size); + return -1; + } + + if (!sanei_usb_check_attr(node, "direction", "IN", __func__)) + { + sanei_usb_record_replace_read_bulk(node, dn, NULL, 0, wanted_size); + return -1; + } + if (!sanei_usb_check_attr_uint(node, "endpoint_number", + devices[dn].bulk_in_ep & 0x0f, + __func__)) + { + sanei_usb_record_replace_read_bulk(node, dn, NULL, 0, wanted_size); + return -1; + } + + size_t got_size = 0; + char* got_data = sanei_xml_get_hex_data(node, &got_size); + + if (got_size > wanted_size) + { + FAIL_TEST_TX(__func__, node, + "got more data than wanted (%lu vs %lu)\n", + got_size, wanted_size); + free(got_data); + sanei_usb_record_replace_read_bulk(node, dn, NULL, 0, wanted_size); + return -1; + } + + memcpy(buffer + total_got_size, got_data, got_size); + free(got_data); + total_got_size += got_size; + wanted_size -= got_size; + + int next_size = sanei_usb_replay_next_read_bulk_packet_size(dn); + if (next_size < 0) + return total_got_size; + if ((size_t) next_size > wanted_size) + return total_got_size; + } + return total_got_size; +} +#endif // WITH_USB_RECORD_REPLAY + SANE_Status sanei_usb_read_bulk (SANE_Int dn, SANE_Byte * buffer, size_t * size) { @@ -2129,7 +3292,16 @@ sanei_usb_read_bulk (SANE_Int dn, SANE_Byte * buffer, size_t * size) DBG (5, "sanei_usb_read_bulk: trying to read %lu bytes\n", (unsigned long) *size); - if (devices[dn].method == sanei_usb_method_scanner_driver) + if (testing_mode == sanei_usb_testing_mode_replay) + { +#if WITH_USB_RECORD_REPLAY + read_size = sanei_usb_replay_read_bulk(dn, buffer, *size); +#else + DBG(1, "%s: USB record-replay mode support missing\n", __func__); + return SANE_STATUS_UNSUPPORTED; +#endif + } + else if (devices[dn].method == sanei_usb_method_scanner_driver) { read_size = read (devices[dn].fd, buffer, *size); @@ -2236,8 +3408,22 @@ sanei_usb_read_bulk (SANE_Int dn, SANE_Byte * buffer, size_t * size) return SANE_STATUS_INVAL; } + if (testing_mode == sanei_usb_testing_mode_record) + { +#if WITH_USB_RECORD_REPLAY + sanei_usb_record_read_bulk(NULL, dn, buffer, *size, read_size); +#else + DBG (1, "USB record-replay mode support is missing\n"); + return SANE_STATUS_UNSUPPORTED; +#endif + } + if (read_size < 0) { + *size = 0; + if (testing_mode != sanei_usb_testing_mode_disabled) + return SANE_STATUS_IO_ERROR; + #ifdef HAVE_LIBUSB_LEGACY if (devices[dn].method == sanei_usb_method_libusb) usb_clear_halt (devices[dn].libusb_handle, devices[dn].bulk_in_ep); @@ -2245,23 +3431,181 @@ sanei_usb_read_bulk (SANE_Int dn, SANE_Byte * buffer, size_t * size) if (devices[dn].method == sanei_usb_method_libusb) libusb_clear_halt (devices[dn].lu_handle, devices[dn].bulk_in_ep); #endif - *size = 0; return SANE_STATUS_IO_ERROR; } if (read_size == 0) { - DBG (3, "sanei_usb_read_bulk: read returned EOF\n"); - *size = 0; - return SANE_STATUS_EOF; + DBG (3, "sanei_usb_read_bulk: read returned EOF\n"); + *size = 0; + return SANE_STATUS_EOF; + } + if (debug_level > 10) + print_buffer (buffer, read_size); + DBG (5, "sanei_usb_read_bulk: wanted %lu bytes, got %ld bytes\n", + (unsigned long) *size, (unsigned long) read_size); + *size = read_size; + + return SANE_STATUS_GOOD; +} + +#if WITH_USB_RECORD_REPLAY +static int sanei_usb_record_write_bulk(xmlNode* node, SANE_Int dn, + const SANE_Byte* buffer, + size_t size, size_t write_size) +{ + int node_was_null = node == NULL; + if (node_was_null) + node = testing_append_commands_node; + + xmlNode* e_tx = xmlNewNode(NULL, (const xmlChar*)"bulk_tx"); + sanei_xml_command_common_props(e_tx, devices[dn].bulk_out_ep & 0x0f, "OUT"); + sanei_xml_set_hex_data(e_tx, (const char*)buffer, size); + // FIXME: output write_size + + node = sanei_xml_append_command(node, node_was_null, e_tx); + + if (node_was_null) + testing_append_commands_node = node; + return write_size; +} + +static void sanei_usb_record_replace_write_bulk(xmlNode* node, SANE_Int dn, + const SANE_Byte* buffer, + size_t size, size_t write_size) +{ + if (!testing_development_mode) + return; + testing_last_known_seq--; + sanei_usb_record_write_bulk(node, dn, buffer, size, write_size); + xmlUnlinkNode(node); + xmlFreeNode(node); +} + +// returns non-negative value on success, -1 on failure +static int sanei_usb_replay_next_write_bulk_packet_size(SANE_Int dn) +{ + xmlNode* node = sanei_xml_peek_next_tx_node(); + if (node == NULL) + return -1; + + if (xmlStrcmp(node->name, (const xmlChar*)"bulk_tx") != 0) + { + return -1; } - if (debug_level > 10) - print_buffer (buffer, read_size); - DBG (5, "sanei_usb_read_bulk: wanted %lu bytes, got %ld bytes\n", - (unsigned long) *size, (unsigned long) read_size); - *size = read_size; - return SANE_STATUS_GOOD; + if (!sanei_usb_attr_is(node, "direction", "OUT")) + return -1; + if (!sanei_usb_attr_is_uint(node, "endpoint_number", + devices[dn].bulk_out_ep & 0x0f)) + return -1; + + size_t got_size = 0; + char* got_data = sanei_xml_get_hex_data(node, &got_size); + free(got_data); + return got_size; +} + +static int sanei_usb_replay_write_bulk(SANE_Int dn, const SANE_Byte* buffer, + size_t size) +{ + size_t wanted_size = size; + size_t total_wrote_size = 0; + while (wanted_size > 0) + { + if (testing_known_commands_input_failed) + return -1; + + xmlNode* node = sanei_xml_get_next_tx_node(); + if (node == NULL) + { + FAIL_TEST(__func__, "no more transactions\n"); + return -1; + } + + if (sanei_xml_is_known_commands_end(node)) + { + sanei_usb_record_write_bulk(NULL, dn, buffer, size, size); + return size; + } + + sanei_xml_record_seq(node); + sanei_xml_break_if_needed(node); + + if (xmlStrcmp(node->name, (const xmlChar*)"bulk_tx") != 0) + { + FAIL_TEST_TX(__func__, node, "unexpected transaction type %s\n", + (const char*) node->name); + sanei_usb_record_replace_write_bulk(node, dn, buffer, size, size); + return -1; + } + + if (!sanei_usb_check_attr(node, "direction", "OUT", __func__)) + { + sanei_usb_record_replace_write_bulk(node, dn, buffer, size, size); + return -1; + } + if (!sanei_usb_check_attr_uint(node, "endpoint_number", + devices[dn].bulk_out_ep & 0x0f, + __func__)) + { + sanei_usb_record_replace_write_bulk(node, dn, buffer, size, size); + return -1; + } + + size_t wrote_size = 0; + char* wrote_data = sanei_xml_get_hex_data(node, &wrote_size); + + if (wrote_size > wanted_size) + { + FAIL_TEST_TX(__func__, node, + "wrote more data than wanted (%lu vs %lu)\n", + wrote_size, wanted_size); + if (!testing_development_mode) + { + free(wrote_data); + return -1; + } + sanei_usb_record_replace_write_bulk(node, dn, buffer, size, size); + wrote_size = size; + } + else if (!sanei_usb_check_data_equal(node, + ((const char*) buffer) + + total_wrote_size, + wrote_size, + wrote_data, wrote_size, + __func__)) + { + if (!testing_development_mode) + { + free(wrote_data); + return -1; + } + sanei_usb_record_replace_write_bulk(node, dn, buffer, size, + size); + wrote_size = size; + } + + free(wrote_data); + if (wrote_size < wanted_size && + sanei_usb_replay_next_write_bulk_packet_size(dn) < 0) + { + FAIL_TEST_TX(__func__, node, + "wrote less data than wanted (%lu vs %lu)\n", + wrote_size, wanted_size); + if (!testing_development_mode) + { + return -1; + } + sanei_usb_record_replace_write_bulk(node, dn, buffer, size, + size); + wrote_size = size; + } + total_wrote_size += wrote_size; + wanted_size -= wrote_size; + } + return total_wrote_size; } +#endif SANE_Status sanei_usb_write_bulk (SANE_Int dn, const SANE_Byte * buffer, size_t * size) @@ -2284,7 +3628,16 @@ sanei_usb_write_bulk (SANE_Int dn, const SANE_Byte * buffer, size_t * size) if (debug_level > 10) print_buffer (buffer, *size); - if (devices[dn].method == sanei_usb_method_scanner_driver) + if (testing_mode == sanei_usb_testing_mode_replay) + { +#if WITH_USB_RECORD_REPLAY + write_size = sanei_usb_replay_write_bulk(dn, buffer, *size); +#else + DBG (1, "USB record-replay mode support is missing\n"); + return SANE_STATUS_UNSUPPORTED; +#endif + } + else if (devices[dn].method == sanei_usb_method_scanner_driver) { write_size = write (devices[dn].fd, buffer, *size); @@ -2391,9 +3744,22 @@ sanei_usb_write_bulk (SANE_Int dn, const SANE_Byte * buffer, size_t * size) return SANE_STATUS_INVAL; } + if (testing_mode == sanei_usb_testing_mode_record) + { +#if WITH_USB_RECORD_REPLAY + sanei_usb_record_write_bulk(NULL, dn, buffer, *size, write_size); +#else + DBG (1, "USB record-replay mode support is missing\n"); + return SANE_STATUS_UNSUPPORTED; +#endif + } + if (write_size < 0) { *size = 0; + if (testing_mode != sanei_usb_testing_mode_disabled) + return SANE_STATUS_IO_ERROR; + #ifdef HAVE_LIBUSB_LEGACY if (devices[dn].method == sanei_usb_method_libusb) usb_clear_halt (devices[dn].libusb_handle, devices[dn].bulk_out_ep); @@ -2409,6 +3775,161 @@ sanei_usb_write_bulk (SANE_Int dn, const SANE_Byte * buffer, size_t * size) return SANE_STATUS_GOOD; } +#if WITH_USB_RECORD_REPLAY +static void +sanei_usb_record_control_msg(xmlNode* node, + SANE_Int dn, SANE_Int rtype, SANE_Int req, + SANE_Int value, SANE_Int index, SANE_Int len, + const SANE_Byte* data) +{ + (void) dn; + + int node_was_null = node == NULL; + if (node_was_null) + node = testing_append_commands_node; + + xmlNode* e_tx = xmlNewNode(NULL, (const xmlChar*)"control_tx"); + + int direction_is_in = (rtype & 0x80) == 0x80; + sanei_xml_command_common_props(e_tx, rtype & 0x1f, + direction_is_in ? "IN" : "OUT"); + sanei_xml_set_hex_attr(e_tx, "bmRequestType", rtype); + sanei_xml_set_hex_attr(e_tx, "bRequest", req); + sanei_xml_set_hex_attr(e_tx, "wValue", value); + sanei_xml_set_hex_attr(e_tx, "wIndex", index); + sanei_xml_set_hex_attr(e_tx, "wLength", len); + + if (direction_is_in && data == NULL) + { + const int buf_size = 128; + char buf[buf_size]; + snprintf(buf, buf_size, "(unknown read of size %d)", len); + xmlNode* e_content = xmlNewText((const xmlChar*)buf); + xmlAddChild(e_tx, e_content); + } + else + { + sanei_xml_set_hex_data(e_tx, (const char*)data, len); + } + + node = sanei_xml_append_command(node, node_was_null, e_tx); + + if (node_was_null) + testing_append_commands_node = node; +} + + +static SANE_Status +sanei_usb_record_replace_control_msg(xmlNode* node, + SANE_Int dn, SANE_Int rtype, SANE_Int req, + SANE_Int value, SANE_Int index, SANE_Int len, + const SANE_Byte* data) +{ + if (!testing_development_mode) + return SANE_STATUS_IO_ERROR; + + SANE_Status ret = SANE_STATUS_GOOD; + int direction_is_in = (rtype & 0x80) == 0x80; + if (direction_is_in) + { + testing_known_commands_input_failed = 1; + ret = SANE_STATUS_IO_ERROR; + } + + testing_last_known_seq--; + sanei_usb_record_control_msg(node, dn, rtype, req, value, index, len, data); + xmlUnlinkNode(node); + xmlFreeNode(node); + return ret; +} + +static SANE_Status +sanei_usb_replay_control_msg(SANE_Int dn, SANE_Int rtype, SANE_Int req, + SANE_Int value, SANE_Int index, SANE_Int len, + SANE_Byte* data) +{ + (void) dn; + + if (testing_known_commands_input_failed) + return -1; + + xmlNode* node = sanei_xml_get_next_tx_node(); + if (node == NULL) + { + FAIL_TEST(__func__, "no more transactions\n"); + return SANE_STATUS_IO_ERROR; + } + + int direction_is_in = (rtype & 0x80) == 0x80; + SANE_Byte* rdata = direction_is_in ? NULL : data; + + if (sanei_xml_is_known_commands_end(node)) + { + sanei_usb_record_control_msg(NULL, dn, rtype, req, value, index, len, + rdata); + if (direction_is_in) + { + testing_known_commands_input_failed = 1; + return SANE_STATUS_IO_ERROR; + } + return SANE_STATUS_GOOD; + } + + sanei_xml_record_seq(node); + sanei_xml_break_if_needed(node); + + if (xmlStrcmp(node->name, (const xmlChar*)"control_tx") != 0) + { + FAIL_TEST_TX(__func__, node, "unexpected transaction type %s\n", + (const char*) node->name); + return sanei_usb_record_replace_control_msg(node, dn, rtype, req, value, + index, len, rdata); + } + + if (!sanei_usb_check_attr(node, "direction", direction_is_in ? "IN" : "OUT", + __func__) || + !sanei_usb_check_attr_uint(node, "bmRequestType", rtype, __func__) || + !sanei_usb_check_attr_uint(node, "bRequest", req, __func__) || + !sanei_usb_check_attr_uint(node, "wValue", value, __func__) || + !sanei_usb_check_attr_uint(node, "wIndex", index, __func__) || + !sanei_usb_check_attr_uint(node, "wLength", len, __func__)) + { + return sanei_usb_record_replace_control_msg(node, dn, rtype, req, value, + index, len, rdata); + } + + size_t tx_data_size = 0; + char* tx_data = sanei_xml_get_hex_data(node, &tx_data_size); + + if (direction_is_in) + { + if (tx_data_size != (size_t)len) + { + FAIL_TEST_TX(__func__, node, + "got different amount of data than wanted (%lu vs %lu)\n", + tx_data_size, (size_t)len); + free(tx_data); + return sanei_usb_record_replace_control_msg(node, dn, rtype, req, + value, index, len, rdata); + } + memcpy(data, tx_data, tx_data_size); + } + else + { + if (!sanei_usb_check_data_equal(node, + (const char*)data, len, + tx_data, tx_data_size, __func__)) + { + free(tx_data); + return sanei_usb_record_replace_control_msg(node, dn, rtype, req, + value, index, len, rdata); + } + } + free(tx_data); + return SANE_STATUS_GOOD; +} +#endif + SANE_Status sanei_usb_control_msg (SANE_Int dn, SANE_Int rtype, SANE_Int req, SANE_Int value, SANE_Int index, SANE_Int len, @@ -2426,6 +3947,16 @@ sanei_usb_control_msg (SANE_Int dn, SANE_Int rtype, SANE_Int req, if (!(rtype & 0x80) && debug_level > 10) print_buffer (data, len); + if (testing_mode == sanei_usb_testing_mode_replay) + { +#if WITH_USB_RECORD_REPLAY + return sanei_usb_replay_control_msg(dn, rtype, req, value, index, len, + data); +#else + DBG (1, "USB record-replay mode support is missing\n"); + return SANE_STATUS_UNSUPPORTED; +#endif + } if (devices[dn].method == sanei_usb_method_scanner_driver) { #if defined(__linux__) @@ -2537,9 +4068,141 @@ sanei_usb_control_msg (SANE_Int dn, SANE_Int rtype, SANE_Int req, devices[dn].method); return SANE_STATUS_UNSUPPORTED; } + + if (testing_mode == sanei_usb_testing_mode_record) + { +#if WITH_USB_RECORD_REPLAY + // TODO: record in the error code path too + sanei_usb_record_control_msg(NULL, dn, rtype, req, value, index, len, + data); +#else + DBG (1, "USB record-replay mode support is missing\n"); + return SANE_STATUS_UNSUPPORTED; +#endif + } return SANE_STATUS_GOOD; } +#if WITH_USB_RECORD_REPLAY +static void sanei_usb_record_read_int(xmlNode* node, + SANE_Int dn, SANE_Byte* buffer, + size_t size, ssize_t read_size) +{ + (void) size; + + int node_was_null = node == NULL; + if (node_was_null) + node = testing_append_commands_node; + + xmlNode* e_tx = xmlNewNode(NULL, (const xmlChar*)"interrupt_tx"); + + sanei_xml_command_common_props(e_tx, devices[dn].int_in_ep & 0x0f, "IN"); + + if (buffer == NULL) + { + const int buf_size = 128; + char buf[buf_size]; + snprintf(buf, buf_size, "(unknown read of wanted size %ld)", read_size); + xmlNode* e_content = xmlNewText((const xmlChar*)buf); + xmlAddChild(e_tx, e_content); + } + else + { + if (read_size >= 0) + { + sanei_xml_set_hex_data(e_tx, (const char*)buffer, read_size); + } + else + { + xmlNewProp(e_tx, (const xmlChar*)"error", (const xmlChar*)"timeout"); + } + } + + node = sanei_xml_append_command(node, node_was_null, e_tx); + + if (node_was_null) + testing_append_commands_node = node; +} + +static void sanei_usb_record_replace_read_int(xmlNode* node, + SANE_Int dn, SANE_Byte* buffer, + size_t size, size_t read_size) +{ + if (!testing_development_mode) + return; + testing_known_commands_input_failed = 1; + testing_last_known_seq--; + sanei_usb_record_read_int(node, dn, buffer, size, read_size); + xmlUnlinkNode(node); + xmlFreeNode(node); +} + +static int sanei_usb_replay_read_int(SANE_Int dn, SANE_Byte* buffer, + size_t size) +{ + if (testing_known_commands_input_failed) + return -1; + + size_t wanted_size = size; + + xmlNode* node = sanei_xml_get_next_tx_node(); + if (node == NULL) + { + FAIL_TEST(__func__, "no more transactions\n"); + return -1; + } + + if (sanei_xml_is_known_commands_end(node)) + { + sanei_usb_record_read_int(NULL, dn, NULL, 0, size); + testing_known_commands_input_failed = 1; + return -1; + } + + sanei_xml_record_seq(node); + sanei_xml_break_if_needed(node); + + if (xmlStrcmp(node->name, (const xmlChar*)"interrupt_tx") != 0) + { + FAIL_TEST_TX(__func__, node, "unexpected transaction type %s\n", + (const char*) node->name); + sanei_usb_record_replace_read_int(node, dn, NULL, 0, size); + return -1; + } + + if (!sanei_usb_check_attr(node, "direction", "IN", __func__)) + { + sanei_usb_record_replace_read_int(node, dn, NULL, 0, size); + return -1; + } + + if (!sanei_usb_check_attr_uint(node, "endpoint_number", + devices[dn].int_in_ep & 0x0f, + __func__)) + { + sanei_usb_record_replace_read_int(node, dn, NULL, 0, size); + return -1; + } + + size_t tx_data_size = 0; + char* tx_data = sanei_xml_get_hex_data(node, &tx_data_size); + + if (tx_data_size > wanted_size) + { + FAIL_TEST_TX(__func__, node, + "got more data than wanted (%lu vs %lu)\n", + tx_data_size, wanted_size); + sanei_usb_record_replace_read_int(node, dn, NULL, 0, size); + free(tx_data); + return -1; + } + + memcpy((char*) buffer, tx_data, tx_data_size); + free(tx_data); + return tx_data_size; +} +#endif // WITH_USB_RECORD_REPLAY + SANE_Status sanei_usb_read_int (SANE_Int dn, SANE_Byte * buffer, size_t * size) { @@ -2562,7 +4225,16 @@ sanei_usb_read_int (SANE_Int dn, SANE_Byte * buffer, size_t * size) DBG (5, "sanei_usb_read_int: trying to read %lu bytes\n", (unsigned long) *size); - if (devices[dn].method == sanei_usb_method_scanner_driver) + if (testing_mode == sanei_usb_testing_mode_replay) + { +#if WITH_USB_RECORD_REPLAY + read_size = sanei_usb_replay_read_int(dn, buffer, *size); +#else + DBG (1, "USB record-replay mode support is missing\n"); + return SANE_STATUS_UNSUPPORTED; +#endif + } + else if (devices[dn].method == sanei_usb_method_scanner_driver) { DBG (1, "sanei_usb_read_int: access method %d not implemented\n", devices[dn].method); @@ -2658,8 +4330,22 @@ sanei_usb_read_int (SANE_Int dn, SANE_Byte * buffer, size_t * size) return SANE_STATUS_INVAL; } + if (testing_mode == sanei_usb_testing_mode_record) + { +#if WITH_USB_RECORD_REPLAY + sanei_usb_record_read_int(NULL, dn, buffer, *size, read_size); +#else + DBG (1, "USB record-replay mode support is missing\n"); + return SANE_STATUS_UNSUPPORTED; +#endif + } + if (read_size < 0) { + *size = 0; + if (testing_mode != sanei_usb_testing_mode_disabled) + return SANE_STATUS_IO_ERROR; + #ifdef HAVE_LIBUSB_LEGACY if (devices[dn].method == sanei_usb_method_libusb) if (stalled) @@ -2669,7 +4355,6 @@ sanei_usb_read_int (SANE_Int dn, SANE_Byte * buffer, size_t * size) if (stalled) libusb_clear_halt (devices[dn].lu_handle, devices[dn].int_in_ep); #endif - *size = 0; return SANE_STATUS_IO_ERROR; } if (read_size == 0) @@ -2687,6 +4372,58 @@ sanei_usb_read_int (SANE_Int dn, SANE_Byte * buffer, size_t * size) return SANE_STATUS_GOOD; } +#if WITH_USB_RECORD_REPLAY +static SANE_Status sanei_usb_replay_set_configuration(SANE_Int dn, + SANE_Int configuration) +{ + (void) dn; + + xmlNode* node = sanei_xml_get_next_tx_node(); + if (node == NULL) + { + FAIL_TEST(__func__, "no more transactions\n"); + return SANE_STATUS_IO_ERROR; + } + + sanei_xml_record_seq(node); + sanei_xml_break_if_needed(node); + + if (xmlStrcmp(node->name, (const xmlChar*)"control_tx") != 0) + { + FAIL_TEST_TX(__func__, node, "unexpected transaction type %s\n", + (const char*) node->name); + return SANE_STATUS_IO_ERROR; + } + + if (!sanei_usb_check_attr(node, "direction", "OUT", __func__)) + return SANE_STATUS_IO_ERROR; + + if (!sanei_usb_check_attr_uint(node, "bmRequestType", 0, __func__)) + return SANE_STATUS_IO_ERROR; + + if (!sanei_usb_check_attr_uint(node, "bRequest", 9, __func__)) + return SANE_STATUS_IO_ERROR; + + if (!sanei_usb_check_attr_uint(node, "wValue", configuration, __func__)) + return SANE_STATUS_IO_ERROR; + + if (!sanei_usb_check_attr_uint(node, "wIndex", 0, __func__)) + return SANE_STATUS_IO_ERROR; + + if (!sanei_usb_check_attr_uint(node, "wLength", 0, __func__)) + return SANE_STATUS_IO_ERROR; + + return SANE_STATUS_GOOD; +} + +static void sanei_usb_record_set_configuration(SANE_Int dn, + SANE_Int configuration) +{ + (void) dn; (void) configuration; + // TODO +} +#endif // WITH_USB_RECORD_REPLAY + SANE_Status sanei_usb_set_configuration (SANE_Int dn, SANE_Int configuration) { @@ -2700,7 +4437,26 @@ sanei_usb_set_configuration (SANE_Int dn, SANE_Int configuration) DBG (5, "sanei_usb_set_configuration: configuration = %d\n", configuration); - if (devices[dn].method == sanei_usb_method_scanner_driver) + if (testing_mode == sanei_usb_testing_mode_record) + { +#if WITH_USB_RECORD_REPLAY + sanei_usb_record_set_configuration(dn, configuration); +#else + DBG (1, "USB record-replay mode support is missing\n"); + return SANE_STATUS_UNSUPPORTED; +#endif + } + + if (testing_mode == sanei_usb_testing_mode_replay) + { +#if WITH_USB_RECORD_REPLAY + return sanei_usb_replay_set_configuration(dn, configuration); +#else + DBG (1, "USB record-replay mode support is missing\n"); + return SANE_STATUS_UNSUPPORTED; +#endif + } + else if (devices[dn].method == sanei_usb_method_scanner_driver) { #if defined(__linux__) return SANE_STATUS_GOOD; @@ -2770,7 +4526,11 @@ sanei_usb_claim_interface (SANE_Int dn, SANE_Int interface_number) DBG (5, "sanei_usb_claim_interface: interface_number = %d\n", interface_number); - if (devices[dn].method == sanei_usb_method_scanner_driver) + if (testing_mode == sanei_usb_testing_mode_replay) + { + return SANE_STATUS_GOOD; + } + else if (devices[dn].method == sanei_usb_method_scanner_driver) { #if defined(__linux__) return SANE_STATUS_GOOD; @@ -2837,7 +4597,11 @@ sanei_usb_release_interface (SANE_Int dn, SANE_Int interface_number) } DBG (5, "sanei_usb_release_interface: interface_number = %d\n", interface_number); - if (devices[dn].method == sanei_usb_method_scanner_driver) + if (testing_mode == sanei_usb_testing_mode_replay) + { + return SANE_STATUS_GOOD; + } + else if (devices[dn].method == sanei_usb_method_scanner_driver) { #if defined(__linux__) return SANE_STATUS_GOOD; @@ -2903,7 +4667,11 @@ sanei_usb_set_altinterface (SANE_Int dn, SANE_Int alternate) devices[dn].alt_setting = alternate; - if (devices[dn].method == sanei_usb_method_scanner_driver) + if (testing_mode == sanei_usb_testing_mode_replay) + { + return SANE_STATUS_GOOD; + } + else if (devices[dn].method == sanei_usb_method_scanner_driver) { #if defined(__linux__) return SANE_STATUS_GOOD; @@ -2955,6 +4723,25 @@ sanei_usb_set_altinterface (SANE_Int dn, SANE_Int alternate) } } +static SANE_Status +sanei_usb_replay_get_descriptor(SANE_Int dn, + struct sanei_usb_dev_descriptor *desc) +{ + (void) dn; + (void) desc; + return SANE_STATUS_UNSUPPORTED; + // ZZTODO +} + +static void +sanei_usb_record_get_descriptor(SANE_Int dn, + struct sanei_usb_dev_descriptor *desc) +{ + (void) dn; + (void) desc; + // ZZTODO +} + extern SANE_Status sanei_usb_get_descriptor( SANE_Int dn, struct sanei_usb_dev_descriptor __sane_unused__ @@ -2968,6 +4755,11 @@ sanei_usb_get_descriptor( SANE_Int dn, return SANE_STATUS_INVAL; } + if (testing_mode == sanei_usb_testing_mode_replay) + { + return sanei_usb_replay_get_descriptor(dn, desc); + } + DBG (5, "sanei_usb_get_descriptor\n"); #ifdef HAVE_LIBUSB_LEGACY { @@ -3013,5 +4805,11 @@ sanei_usb_get_descriptor( SANE_Int dn, return SANE_STATUS_UNSUPPORTED; } #endif /* not HAVE_LIBUSB_LEGACY && not HAVE_LIBUSB */ + + if (testing_mode == sanei_usb_testing_mode_record) + { + sanei_usb_record_get_descriptor(dn, desc); + } + return SANE_STATUS_GOOD; } -- cgit v1.2.3