[sane-devel] [PATCH] OT: Scanbuttond: Support for HP4570/5550/5590/7650 scanners

Ilia Sotnikov hostcc at gmail.com
Fri Sep 19 08:41:41 UTC 2008


As scanbuttond author seems unresponsive on his SF mail address, I'm
posting the patch for scanbuttond (applies over CVS version) here.

Hope someone finds it useful.

Best regards,
-- 
 Ilia Sotnikov

diff -Nur --exclude CVS scanbuttond.orig/backends/hp5590.c
scanbuttond/backends/hp5590.c
--- scanbuttond.orig/backends/hp5590.c	1970-01-01 03:00:00.000000000 +0300
+++ scanbuttond/backends/hp5590.c	2008-09-15 18:28:24.000000000 +0300
@@ -0,0 +1,583 @@
+/* hp5590.c: HP4570/5550/5590/7650 backend
+ * This file is part of scanbuttond.
+ * Copyleft )c( 2008 by Ilia Sotnikov <hostcc at gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <string.h>
+#include <syslog.h>
+#include <errno.h>
+#include <netinet/in.h> /* For htons() */
+#include "scanbuttond/scanbuttond.h"
+#include "scanbuttond/libusbi.h"
+#include "hp5590.h"
+
+static char* backend_name = "HP5590 USB";
+
+#define NUM_SUPPORTED_USB_DEVICES 4
+
+static int supported_usb_devices[NUM_SUPPORTED_USB_DEVICES][3] =
+{
+	/* vendor, product, num_buttons */
+	{ 0x03f0, 0x1305, 5 },  /* HP Scanjet 4570 */
+	{ 0x03f0, 0x1305, 5 },	/* HP Scanjet 5550 */
+	{ 0x03f0, 0x1705, 5 },	/* HP Scanjet 5590 */
+	{ 0x03f0, 0x1805, 5 },	/* HP Scanjet 7650 */
+};
+
+static char* usb_device_descriptions[NUM_SUPPORTED_USB_DEVICES][2] =
+{
+	{ "Hewlett-Packard", "ScanJet 4570" },
+	{ "Hewlett-Packard", "ScanJet 5550" },
+	{ "Hewlett-Packard", "Scanjet 5590" },
+	{ "Hewlett-Packard", "Scanjet 7650" },
+};
+
+static libusb_handle_t* libusb_handle;
+static scanner_t* hp5590_scanners = NULL;
+
+/* returns -1 if the scanner is unsupported, or the index of the
+ * corresponding vendor-product pair in the supported_usb_devices array.
+ */
+static int
+hp5590_match_libusb_scanner(libusb_device_t* device)
+{
+	int index;
+
+	for (index = 0; index < NUM_SUPPORTED_USB_DEVICES; index++)
+	{
+		if (supported_usb_devices[index][0] == device->vendorID &&
+			supported_usb_devices[index][1] == device->productID)
+			break;
+	}
+
+	if (index >= NUM_SUPPORTED_USB_DEVICES)
+		return -1;
+
+	return index;
+}
+
+static void
+hp5590_attach_libusb_scanner (libusb_device_t* device)
+{
+	const char* descriptor_prefix = "hp5590:libusb:";
+	int 		index;
+
+	index = hp5590_match_libusb_scanner(device);
+	/* Unsupported */
+	if (index < 0)
+		return;
+
+	scanner_t* scanner = (scanner_t*) malloc (sizeof(scanner_t));
+	scanner->vendor = usb_device_descriptions[index][0];
+	scanner->product = usb_device_descriptions[index][1];
+	scanner->connection = CONNECTION_LIBUSB;
+	scanner->internal_dev_ptr = (void*) device;
+	scanner->lastbutton = 0;
+	scanner->sane_device = (char*) malloc (strlen (device->location) +
+						   strlen (descriptor_prefix) + 1);
+	strcpy (scanner->sane_device, descriptor_prefix);
+	strcat (scanner->sane_device, device->location);
+	scanner->num_buttons = supported_usb_devices[index][2];
+	scanner->is_open = 0;
+	scanner->next = hp5590_scanners;
+	hp5590_scanners = scanner;
+}
+
+static void
+hp5590_detach_scanners (void)
+{
+	scanner_t* next;
+	while (hp5590_scanners != NULL)
+	{
+		next = hp5590_scanners->next;
+		free (hp5590_scanners->sane_device);
+		free (hp5590_scanners);
+		hp5590_scanners = next;
+	}
+}
+
+static void
+hp5590_scan_devices (libusb_device_t* devices)
+{
+	int index;
+	libusb_device_t* device = devices;
+	while (device != NULL)
+	{
+		index = hp5590_match_libusb_scanner (device);
+		if (index >= 0)
+			hp5590_attach_libusb_scanner (device);
+		device = device->next;
+	}
+}
+
+static int
+hp5590_init_libusb (void)
+{
+	libusb_device_t* devices;
+
+	libusb_handle = libusb_init ();
+	devices = libusb_get_devices (libusb_handle);
+	hp5590_scan_devices (devices);
+	return 0;
+}
+
+static void
+hp5590_flush (scanner_t* scanner)
+{
+	switch (scanner->connection)
+	{
+		case CONNECTION_LIBUSB:
+			libusb_flush ((libusb_device_t*) scanner->internal_dev_ptr);
+			break;
+	}
+}
+
+const char*
+scanbtnd_get_backend_name (void)
+{
+	return backend_name;
+}
+
+int
+scanbtnd_init (void)
+{
+	hp5590_scanners = NULL;
+
+	syslog (LOG_INFO, "hp5590-backend: init");
+	return hp5590_init_libusb ();
+}
+
+int
+scanbtnd_rescan (void)
+{
+	libusb_device_t* devices;
+
+	hp5590_detach_scanners ();
+	hp5590_scanners = NULL;
+	libusb_rescan (libusb_handle);
+	devices = libusb_get_devices (libusb_handle);
+	hp5590_scan_devices (devices);
+
+	return 0;
+}
+
+const scanner_t*
+scanbtnd_get_supported_devices (void)
+{
+	return hp5590_scanners;
+}
+
+int
+scanbtnd_open (scanner_t* scanner)
+{
+	int result = -ENOSYS;
+
+	if (scanner->is_open)
+		return -EINVAL;
+
+	switch (scanner->connection)
+	{
+		case CONNECTION_LIBUSB:
+			/* if devices have been added/removed, return -ENODEV to
+			 * make scanbuttond update its device list
+			 */
+			if (libusb_get_changed_device_count () != 0)
+				return -ENODEV;
+			result = libusb_open ((libusb_device_t*) scanner->internal_dev_ptr);
+			break;
+	}
+
+	if (result == 0)
+		scanner->is_open = 1;
+
+	return result;
+}
+
+int
+scanbtnd_close(scanner_t* scanner)
+{
+	int result = -ENOSYS;
+
+	if (!scanner->is_open)
+		return -EINVAL;
+
+	switch (scanner->connection)
+	{
+		case CONNECTION_LIBUSB:
+			result = libusb_close ((libusb_device_t*) scanner->internal_dev_ptr);
+			break;
+	}
+
+	if (result == 0)
+		scanner->is_open = 0;
+
+	return result;
+}
+
+/* Taken from sane-backends/include/sane/sanei_usb.h */
+
+#define USB_DIR_OUT			0x00
+#define USB_DIR_IN			0x80
+
+/* Taken from sane-backends/backends/hp5590_cmds.c */
+
+/* Button flags */
+#define BUTTON_FLAG_EMAIL	1 << 15
+#define BUTTON_FLAG_COPY	1 << 14
+#define BUTTON_FLAG_DOWN	1 << 13
+#define BUTTON_FLAG_MODE	1 << 12
+#define BUTTON_FLAG_UP		1 << 11
+#define BUTTON_FLAG_FILE	1 << 9
+#define BUTTON_FLAG_POWER	1 << 5
+#define BUTTON_FLAG_SCAN	1 << 2
+#define BUTTON_FLAG_COLLECT	1 << 1
+#define BUTTON_FLAG_CANCEL	1 << 0
+
+#define CMD_BUTTON_STATUS	0x0020
+
+/* Taken from sane-backends/backends/hp5590_low.c */
+
+/* Flags for hp5590_cmd() */
+#define CMD_IN				1 << 0	/* Indicates IN direction, otherwise - OUT */
+#define CMD_VERIFY			1 << 1	/* Requests last command verification */
+
+/* Core flags for hp5590_cmd() - they indicate so called CORE commands */
+#define CORE_NONE		    	 0	/* No CORE operation */
+#define CORE_DATA			1 << 0	/* Operate on CORE data */
+
+/* CORE status flag - ready or not */
+#define CORE_FLAG_NOT_READY	1 << 1
+
+/* Structure describing control URB */
+struct usb_in_usb_ctrl_setup
+{
+	u_int8_t  bRequestType;
+	u_int8_t  bRequest;
+	u_int16_t wValue;
+	u_int16_t wIndex;
+	u_int16_t wLength;
+} __attribute__ ((packed));
+
+static int
+hp5590_get_ack (scanner_t *scanner)
+{
+	u_int8_t 	status;
+	int			ret;
+
+	/* Check if USB-in-USB operation was accepted */
+	ret = libusb_control_msg ((libusb_device_t *) scanner->internal_dev_ptr,
+							  USB_DIR_IN | USB_TYPE_VENDOR,
+							  0x0c, 0x8e, 0x20,
+							  &status, sizeof (status));
+ 	if (ret <= 0)
+	{
+      syslog (LOG_ERR, "hp5590-backend: USB-in-USB: error getting
acknowledge");
+      return -1;
+    }
+
+	/* Check if we received correct acknowledgement */
+	if (status != 0x01)
+	{
+		syslog (LOG_ERR, "hp5590-backend: USB-in-USB: not accepted (status
%u)", status);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int
+hp5590_control_msg (scanner_t *scanner,
+		    		int requesttype, int request,
+		   			int value, int index, unsigned char *bytes,
+		    		int size, int core_flags)
+{
+	struct usb_in_usb_ctrl_setup	ctrl;
+	int		 						ret;
+	unsigned int 					len;
+	unsigned char 					*ptr;
+	u_int8_t 						ack;
+	u_int8_t 						response;
+
+	/* IN (read) operation will be performed */
+	if (requesttype & USB_DIR_IN)
+	{
+		/* Prepare USB-in-USB control message */
+		memset (&ctrl, 0, sizeof (ctrl));
+		ctrl.bRequestType = 0xc0;
+		ctrl.bRequest = request;
+		ctrl.wValue = htons (value);
+		ctrl.wIndex = htons (index);
+		ctrl.wLength = size;
+
+		/* Send USB-in-USB control message */
+		ret = libusb_control_msg ((libusb_device_t *) scanner->internal_dev_ptr,
+								  USB_DIR_OUT | USB_TYPE_VENDOR,
+								  0x04, 0x8f, 0x00,
+								  (unsigned char *) &ctrl, sizeof (ctrl));
+		if (ret <= 0)
+		{
+			syslog (LOG_ERR, "hp5590-backend: USB-in-USB: error sending
control message");
+			return -1;
+		}
+
+		ret = hp5590_get_ack (scanner);
+		if (ret != 0)
+			return ret;
+
+		len = size;
+		ptr = bytes;
+		/* Data is read in 8 byte portions */
+		while (len)
+		{
+			unsigned int next_packet_size;
+			next_packet_size = 8;
+			if (len < 8)
+				next_packet_size = len;
+
+			/* Read USB-in-USB data */
+			ret = libusb_control_msg ((libusb_device_t *) scanner->internal_dev_ptr,
+									  USB_DIR_IN | USB_TYPE_VENDOR,
+									  core_flags & CORE_DATA ? 0x0c : 0x04,
+									  0x90, 0x00,
+									  ptr, next_packet_size);
+			if (ret <= 0)
+			{
+				syslog (LOG_ERR, "hp5590-backend: USB-in-USB: error reading data");
+				return -1;
+			}
+
+			ptr += next_packet_size;
+			len -= next_packet_size;
+		}
+
+		/* Confirm data reception */
+		ack = 0;
+		ret = libusb_control_msg ((libusb_device_t *) scanner->internal_dev_ptr,
+								  USB_DIR_OUT | USB_TYPE_VENDOR,
+								  0x0c, 0x8f, 0x00,
+								  &ack, sizeof (ack));
+		if (ret <= 0)
+		{
+			syslog (LOG_ERR, "hp5590-backend: USB-in-USB: error confirming
data reception");
+			return -1;
+		}
+
+		ret = hp5590_get_ack (scanner);
+		if (ret != 0)
+			return ret;
+	}
+
+	/* OUT (write) operation will be performed */
+	if (!(requesttype & USB_DIR_IN))
+	{
+		/* Prepare USB-in-USB control message */
+		memset (&ctrl, 0, sizeof (ctrl));
+		ctrl.bRequestType = 0x40;
+		ctrl.bRequest = request;
+		ctrl.wValue = htons (value);
+		ctrl.wIndex = htons (index);
+		ctrl.wLength = size;
+
+		/* Send USB-in-USB control message */
+		ret = libusb_control_msg ((libusb_device_t *) scanner->internal_dev_ptr,
+								  USB_DIR_OUT | USB_TYPE_VENDOR,
+								  0x04, 0x8f, 0x00,
+								  (unsigned char *) &ctrl, sizeof (ctrl));
+		if (ret <= 0)
+		{
+			syslog (LOG_ERR, "hp5590-backend: USB-in-USB: error sending
control message");
+			return -1;
+		}
+
+		ret = hp5590_get_ack (scanner);
+		if (ret != 0)
+			return ret;
+
+		len = size;
+		ptr = bytes;
+		/* Data is sent in 8 byte portions */
+		while (len)
+		{
+			unsigned int next_packet_size;
+			next_packet_size = 8;
+			if (len < 8)
+				next_packet_size = len;
+
+			/* Send USB-in-USB data */
+			ret = libusb_control_msg ((libusb_device_t *) scanner->internal_dev_ptr,
+									  USB_DIR_OUT | USB_TYPE_VENDOR,
+									  core_flags & CORE_DATA ? 0x04 : 0x0c,
+									  0x8f, 0x00, ptr, next_packet_size);
+			if (ret <= 0)
+			{
+				syslog (LOG_ERR, "hp5590-backend: USB-in-USB: error sending data");
+				return -1;
+			}
+
+			/* CORE data is acknowledged packet by packet */
+			if (core_flags & CORE_DATA)
+			{
+				ret = hp5590_get_ack (scanner);
+				if (ret != 0)
+					return ret;
+			}
+
+			ptr += next_packet_size;
+			len -= next_packet_size;
+		}
+
+		/* Normal (non-CORE) data is acknowledged after its full transmission */
+		if (!(core_flags & CORE_DATA))
+		{
+			ret = hp5590_get_ack (scanner);
+			if (ret != 0)
+				return ret;
+		}
+
+		/* Getting  response after data transmission */
+		ret = libusb_control_msg ((libusb_device_t *) scanner->internal_dev_ptr,
+								  USB_DIR_IN | USB_TYPE_VENDOR,
+								  0x0c, 0x90, 0x00,
+								  &response, sizeof (response));
+		if (ret <= 0)
+		{
+			syslog (LOG_ERR, "hp5590-backend: USB-in-USB: error getting response");
+			return -1;
+		}
+
+		/* Necessary response after normal (non-CORE) data is 0x00 */
+		if (response != 0x00)
+		{
+			syslog (LOG_ERR, "hp5590-backend: USB-in-USB: invalid response received "
+	       			"(needed 0x00, got %04x)", response);
+
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+static int
+hp5590_verify_last_cmd (scanner_t *scanner, unsigned int cmd)
+{
+	u_int16_t		verify_cmd;
+	unsigned int	last_cmd;
+	unsigned int	core_status;
+	int			 	ret;
+
+	/* Read last command along with CORE status */
+	ret = hp5590_control_msg (scanner,
+							  USB_DIR_IN,
+							  0x04, 0xc5, 0x00,
+							  (unsigned char *) &verify_cmd,
+							  sizeof (verify_cmd), CORE_NONE);
+	if (ret != 0)
+		return ret;
+
+	/* Last command - minor byte */
+	last_cmd = verify_cmd & 0xff;
+	/* CORE status - major byte */
+	core_status = (verify_cmd & 0xff00) >> 8;
+
+	/* Verify last command */
+	if ((cmd & 0x00ff) != last_cmd)
+	{
+		syslog (LOG_ERR, "hp5590-backend: USB-in-USB: command verification failed: "
+	   			"expected 0x%04x, got 0x%04x", cmd, last_cmd);
+		return -1;
+	}
+
+	/* Return value depends on CORE status */
+	return core_status & CORE_FLAG_NOT_READY ? -1 : 0;
+}
+
+static int
+hp5590_cmd (scanner_t *scanner, unsigned int flags,
+			unsigned int cmd, unsigned char *data, unsigned int size,
+			unsigned int core_flags)
+{
+	int ret;
+
+	ret = hp5590_control_msg (scanner,
+							  flags & CMD_IN ? USB_DIR_IN : USB_DIR_OUT,
+							  0x04, cmd, 0x00, data, size, core_flags);
+	if (ret != 0)
+		return ret;
+
+	ret = 0;
+	/* Verify last command if requested */
+	if (flags & CMD_VERIFY)
+		ret = hp5590_verify_last_cmd (scanner, cmd);
+
+	return ret;
+}
+
+int
+scanbtnd_get_button(scanner_t* scanner)
+{
+	int 		button = 0;
+	u_int16_t 	button_status;
+	int			ret;
+
+	if (!scanner->is_open)
+		return -EINVAL;
+	
+	ret = hp5590_cmd (scanner, CMD_IN | CMD_VERIFY,
+					  CMD_BUTTON_STATUS,
+					  (unsigned char *) &button_status,
+					  sizeof (button_status), CORE_NONE);
+	if (ret != 0) {
+		hp5590_flush (scanner);
+		return 0;
+	}
+
+	/* Network order */
+	button_status = ntohs (button_status);
+
+	if (button_status & BUTTON_FLAG_SCAN)
+		button = 1;
+
+	if (button_status & BUTTON_FLAG_COLLECT)
+		button = 2;
+
+	if (button_status & BUTTON_FLAG_FILE)
+		button = 3;
+
+	if (button_status & BUTTON_FLAG_EMAIL)
+		button = 4;
+
+	if (button_status & BUTTON_FLAG_COPY)
+		button = 5;
+
+	return button;
+}
+
+const char*
+scanbtnd_get_sane_device_descriptor (scanner_t* scanner)
+{
+	return scanner->sane_device;
+}
+
+int
+scanbtnd_exit (void)
+{
+	syslog (LOG_INFO, "hp5590-backend: exit");
+	hp5590_detach_scanners ();
+	libusb_exit (libusb_handle);
+	return 0;
+}
diff -Nur --exclude CVS scanbuttond.orig/backends/hp5590.h
scanbuttond/backends/hp5590.h
--- scanbuttond.orig/backends/hp5590.h	1970-01-01 03:00:00.000000000 +0300
+++ scanbuttond/backends/hp5590.h	2008-09-13 13:43:02.000000000 +0300
@@ -0,0 +1,25 @@
+/* hp5590.c: HP4570/5550/5590/7650 backend
+ * This file is part of scanbuttond.
+ * Copyleft )c( 2008 by Ilia Sotnikov <hostcc at gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __HP5590_H_INCLUDED
+#define __HP5590_H_INCLUDED
+
+#include "scanbuttond/backend.h"
+
+#endif
diff -Nur --exclude CVS scanbuttond.orig/backends/Makefile.am
scanbuttond/backends/Makefile.am
--- scanbuttond.orig/backends/Makefile.am	2008-08-21 03:54:56.000000000 +0300
+++ scanbuttond/backends/Makefile.am	2008-09-13 11:09:49.000000000 +0300
@@ -8,7 +8,8 @@
 			libscanbtnd-backend_artec_eplus48u.la \
 			libscanbtnd-backend_hp3900.la \
 			libscanbtnd-backend_hp3500.la \
-			libscanbtnd-backend_genesys.la
+			libscanbtnd-backend_genesys.la \
+			libscanbtnd-backend_hp5590.la

 libscanbtnd_backend_epson_la_SOURCES = epson.c epson.h
 libscanbtnd_backend_epson_la_LIBADD = ../interface/libscanbtnd-interface_usb.la
@@ -43,6 +44,9 @@
 libscanbtnd_backend_genesys_la_SOURCES = genesys.c genesys.h
 libscanbtnd_backend_genesys_la_LIBADD =
../interface/libscanbtnd-interface_usb.la
 libscanbtnd_backend_genesys_la_LDFLAGS = -module -version-info 1:0:0
+libscanbtnd_backend_hp5590_la_SOURCES = hp5590.c hp5590.h
+libscanbtnd_backend_hp5590_la_LIBADD =
../interface/libscanbtnd-interface_usb.la
+libscanbtnd_backend_hp5590_la_LDFLAGS = -module -version-info 1:0:0

 pkgsysconfdir = $(sysconfdir)/$(PACKAGE)
 pkgsysconf_DATA = meta.conf
diff -Nur --exclude CVS scanbuttond.orig/backends/meta.conf
scanbuttond/backends/meta.conf
--- scanbuttond.orig/backends/meta.conf	2006-10-02 02:03:04.000000000 +0300
+++ scanbuttond/backends/meta.conf	2008-09-13 11:57:32.000000000 +0300
@@ -7,3 +7,4 @@
 libscanbtnd-backend_artec_eplus48u
 libscanbtnd-backend_hp3900
 libscanbtnd-backend_genesys
+libscanbtnd-backend_hp5590



More information about the sane-devel mailing list