Subject: kern/2329: Fix for HP ScanJet code
To: None <gnats-bugs@NetBSD.ORG>
From: Kenneth Stailey <kstailey@dol-esa.gov>
List: netbsd-bugs
Date: 04/14/1996 17:18:04
>Number:         2329
>Category:       kern
>Synopsis:       HP ScanJet image data corrupt for many size/resolutions
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    kern-bug-people (Kernel Bug People)
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Sun Apr 14 17:35:00 1996
>Last-Modified:
>Originator:     Kenneth Stailey
>Organization:
US Dept of Labor/Employee Standards Administration
>Release:        NetBSD 1.1B 2-Apr-1996
>Environment:

System: NetBSD owl 1.1A NetBSD 1.1A (OWL) #1: Mon Apr 8 14:38:10 EDT 1996 kstailey@owl:/usr/src/sys/arch/i386/compile/OWL i386

>Description:

Certain (read "most") combinations of sizes and resolutions cause the
HP ScanJet driver to return incorrect image data.  It is because the
scaling algorithm in the ROMs is too hard to second-guess (and may even
vary from model to model.)

>How-To-Repeat:

Use:

set_scanner -w 8.5 -h 1 -i g -r 100
grabscan > foo ; xv foo
set_scanner -w 8.5 -h 1 -i g -r 101
grabscan > foo ; xv foo
set_scanner -w 8.5 -h 1 -i g -r 102
and so on.

The image data returned will be incorrect for almost all of the resolutions
except 100, 150, 200, etc.

>Fix:

Interrogate scanner for image data size:

--- /sys/scsi/ss_scanjet.c	Wed Apr 10 23:55:08 1996
+++ ./ss_scanjet.c	Sun Apr 14 16:14:42 1996
@@ -61,8 +61,10 @@
 
 /* only used internally */
 int scanjet_write __P((struct ss_softc *ss, char *buf, u_int size, int flags));
+int scanjet_ctl_read __P((struct ss_softc *ss, char *buf, u_int size,
+			  int flags));
 int scanjet_set_window __P((struct ss_softc *ss));
-void scanjet_compute_sizes __P((struct ss_softc *));
+int scanjet_compute_sizes __P((struct ss_softc *));
 
 /*
  * structure for the special handlers
@@ -89,6 +91,7 @@
 #ifdef SCSIDEBUG
 	struct scsi_link *sc_link = sa->sa_sc_link;
 #endif
+	int error;
 
 	SC_DEBUG(sc_link, SDEV_DB1, ("scanjet_attach: start\n"));
 	ss->sio.scan_scanner_type = 0;
@@ -99,11 +102,11 @@
 
 	if (!bcmp(sa->sa_inqbuf->product, "C1750A", 6)) {
 		ss->sio.scan_scanner_type = HP_SCANJET_IIC;
-		printf("HP ScanJet IIc\n");
+		printf("HP ScanJet IIc");
 	}
 	if (!bcmp(sa->sa_inqbuf->product, "C2500A", 6)) {
 		ss->sio.scan_scanner_type = HP_SCANJET_IIC;
-		printf("HP ScanJet IIcx\n");
+		printf("HP ScanJet IIcx");
 	}
 
 	SC_DEBUG(sc_link, SDEV_DB1, ("scanjet_attach: scanner_type = %d\n",
@@ -126,7 +129,15 @@
 	ss->sio.scan_quality		= 100;
 	ss->sio.scan_image_mode		= SIM_GRAYSCALE;
 
-	scanjet_compute_sizes(ss);
+	error = scanjet_set_window(ss);
+	if (error) {
+		printf(" set window failed\n");
+		return;
+	}
+	error = scanjet_compute_sizes(ss);
+	if (error)
+		printf(" compute sizes failed ");
+	printf("\n");
 }
 
 int
@@ -147,9 +158,9 @@
 	struct ss_softc *ss;
 	struct scan_io *sio;
 {
-#if 0
 	int error;
 
+#if 0
 	/*
 	 * if the scanner is triggered, then rewind it
 	 */
@@ -188,7 +199,16 @@
 	sio->scan_scanner_type = ss->sio.scan_scanner_type;
 	bcopy(sio, &ss->sio, sizeof(struct scan_io));
 
-	scanjet_compute_sizes(ss);
+	error = scanjet_set_window(ss);
+	if (error) {
+		printf("set window in set params failed\n");
+		return (error);
+	}
+	error = scanjet_compute_sizes(ss);
+	if (error) {
+		printf("compute sizes in set params failed\n");
+		return (error);
+	}
 
 	return (0);
 }
@@ -208,12 +228,16 @@
 #endif
 	int error;
 
-	scanjet_compute_sizes(ss);
+	error = scanjet_compute_sizes(ss);
+	if (error) {
+		uprintf("compute sizes in trigger failed\n");
+		return (error);
+	}
 
 	/* send parameters */
 	error = scanjet_set_window(ss);
 	if (error) {
-		uprintf("set window failed\n");
+		uprintf("set window in trigger failed\n");
 		return (error);
 	}
 
@@ -290,6 +314,33 @@
 	    flags | SCSI_DATA_OUT));
 }
 
+
+/*
+ * Do a synchronous read.  Used to read responses to control messages.
+ */
+int
+scanjet_ctl_read(ss, buf, size, flags)
+	struct ss_softc *ss;
+	char *buf;
+	u_int size;
+	int flags;
+{
+	struct scsi_rw_scanner cmd;
+
+	/*
+	 * If it's a null transfer, return immediatly
+	 */
+	if (size == 0)
+		return (0);
+	bzero(&cmd, sizeof(cmd));
+	cmd.opcode = READ;
+	_lto3b(size, cmd.len);
+	return (scsi_scsi_cmd(ss->sc_link, (struct scsi_generic *) &cmd,
+	    sizeof(cmd), (u_char *) buf, size, 0, 100000, NULL,
+	    flags | SCSI_DATA_IN));
+}
+
+
 #ifdef SCANJETDEBUG
 static void show_es(char *es)
 {
@@ -376,11 +427,40 @@
 	return (scanjet_write(ss, escape_codes, p - escape_codes, 0));
 }
 
-void
+/* atoi() and strchr() are from /sys/arch/amiga/dev/ite.c
+   and are only used in scanjet_compute_sizes */
+
+inline static int
+atoi(cp)
+	const char *cp;
+{
+	int n;
+
+	for (n = 0; *cp && *cp >= '0' && *cp <= '9'; cp++)
+		n = n * 10 + *cp - '0';
+
+	return n;
+}
+
+inline static char *
+strchr(cp, ch)
+	const char *cp;
+	char ch;
+{
+	while (*cp && *cp != ch) cp++;
+	return *cp ? (char *) cp : 0;
+}
+
+int
 scanjet_compute_sizes(ss)
 	struct ss_softc *ss;
 {
-	int r = 0;		/* round up by r 1/1200" */
+	int error;
+	char *wfail = "interrogate write failed\n";
+	char *rfail = "interrogate read failed\n";
+	char escape_codes[20];
+	char response[20];
+	char *p;
 
 	/*
 	 * Deal with the fact that the HP ScanJet IIc uses 1/300" not 1/1200"
@@ -394,26 +474,50 @@
 	case SIM_BINARY_MONOCHROME:
 	case SIM_DITHERED_MONOCHROME:
 		ss->sio.scan_bits_per_pixel = 1;
-		break;
+		strcpy(escape_codes, "\033*s1025E"); /* bytes wide */
+		goto ask_it;
 	case SIM_GRAYSCALE:
-		r = 600;
 		ss->sio.scan_bits_per_pixel = 8;
 		break;
 	case SIM_COLOR:
-		r = 600;
 		ss->sio.scan_bits_per_pixel = 24;
 		break;
 	}
+	strcpy(escape_codes, "\033*s1024E"); /* pixels wide */
 
-	ss->sio.scan_pixels_per_line =
-		(ss->sio.scan_width * ss->sio.scan_x_resolution + r) / 1200;
-	if (ss->sio.scan_bits_per_pixel == 1)
-		/* pad to byte boundary: */
-		ss->sio.scan_pixels_per_line =
-		    (ss->sio.scan_pixels_per_line + 7) & 0xfffffff8;
+ask_it:	/* ask scanner for sizes */
+
+	error = scanjet_write(ss, escape_codes, strlen(escape_codes), 0);
+	if (error) {
+		uprintf(wfail);
+		return;
+	}
+	error = scanjet_ctl_read(ss, response, 20, 0);
+	if (error) {
+		uprintf(rfail);
+		return;
+	}
+	p = strchr(response, 'd') + 1;
+	ss->sio.scan_pixels_per_line = atoi(p);
+	if (ss->sio.scan_image_mode < SIM_GRAYSCALE)
+		ss->sio.scan_pixels_per_line *= 8;
+
+	strcpy(escape_codes, "\033*s1026E"); /* pixels high */
+	error = scanjet_write(ss, escape_codes, strlen(escape_codes), 0);
+	if (error) {
+		uprintf(wfail);
+		return;
+	}
+	error = scanjet_ctl_read(ss, response, 20, 0);
+	if (error) {
+		uprintf(rfail);
+		return;
+	}
+	p = strchr(response, 'd') + 1;
+	ss->sio.scan_lines = atoi(p);
 
-	ss->sio.scan_lines =
-	    (ss->sio.scan_height * ss->sio.scan_y_resolution + r) / 1200;
 	ss->sio.scan_window_size = ss->sio.scan_lines *
 	    ((ss->sio.scan_pixels_per_line * ss->sio.scan_bits_per_pixel) / 8);
+
+	return (0);
 }
>Audit-Trail:
>Unformatted: