lkml.org 
[lkml]   [1997]   [Feb]   [15]   [last100]   RSS Feed
Views: [more markup]  [less markup]  [headers]  [forward] 
 
Messages in this thread
Patch in this message
/
FromCarsten Gross <>
Subjectbidirectional printer port patch...
DateSun, 16 Feb 1997 03:01:24 +0100 (MET)
Hi everyone....
A lot of printers from HP and many other manufactors are able to report
their status and a lot more useful information over a channel readable by
the computer. This "status readback" is done via a special protocol defined
in IEEE1284. I've written a small patch to do a status read over the lp
device and I hope this is the right place to make it available for everyone.

In the case of a HP Printer all status reads are prepared with so called PJL
(Printer job language) commands.  In the following example we send a "Tell
me your Device ID" command to the printer. After that we just "cat" the
answer from the printer (yes - this is the work done by the patch). This
example should work for 5L, 5P, 5MP, 4PJ, 4LJ Pro, 4si, 6P, 6MP and so on.

# echo -e "\33%-12345X@PJL\n@PJL INFO ID\n\33%-12345X"| cat > /dev/lp0
# cat /dev/lp0
@PJL INFO ID
"LASERJET 6P"
The patch is against Kernel 2.1.25 or 2.1.26, for the file
drivers/char/lp.c and I hope everything is okay (in fact this is my first
"real" patch for the linux kernel - comments are welcome)

Ciao

Carsten 

Carsten Gross			Internet: carsten@sol.wohnheim.uni-ulm.de
Sebastian-Kneipp-Weg 6          89075 Ulm, Tel: 0731-552943
My public pgp key: `finger carsten@sol.wohnheim.uni-ulm.de'


--- lp.c.OLD	Wed Feb  5 11:43:00 1997
+++ lp.c	Sat Feb 15 16:21:03 1997
@@ -8,6 +8,7 @@
  * LPCAREFUL, LPABORT, LPGETSTATUS added by Chris Metcalf, metcalf@lcs.mit.edu
  * Statistics and support for slow printers by Rob Janssen, rob@knoware.nl
  * "lp=" command line parameters added by Grant Guenther, grant@torque.net
+ * lp_read (Status readback) support added by Carsten Gross, carsten@sol.wohnheim.uni-ulm.de
  */
 
 #include <linux/module.h>
@@ -316,6 +317,116 @@
 	return -ESPIPE;
 }
 
+/* Test if nibble mode for status readback is okay. 
+ * returns false if not, otherwise true
+ */
+static int lp_nibble_mode_ok(int minor) 
+{
+	int reply=1;
+
+	outb_p(0, LP_B(minor)); 	/* Request "nibble mode" */
+	udelay(LP_DELAY);		
+	outb((inb(LP_C(minor)) & ~8), LP_C(minor)); /* SelectIN low */
+	outb((inb(LP_C(minor)) | 2), LP_C(minor)); /* AutoFeed high */
+	udelay(LP_DELAY);
+	outb((inb(LP_C(minor)) | 1), LP_C(minor)); /* Strobe high */
+	if (( LP_S(minor) & ~0x80) != 0x38) reply=0; /* expected answer? */
+	outb((inb(LP_C(minor)) & ~1 ), LP_C(minor)); /* Strobe low */
+	outb((inb(LP_C(minor)) & ~2 ), LP_C(minor)); /* Autofeed low */
+	udelay(LP_DELAY);
+	return(reply);
+}
+
+
+static int lp_read_nibble(int minor) 
+{
+	unsigned char i;
+	i=LP_S(minor)>>3;
+	i&=~8;
+	if ( ( i & 0x10) == 0) i|=8;
+	return(i & 0x0f);
+}
+
+static void lp_select_in_high(int minor) {
+	outb((inb(LP_C(minor)) | 8), LP_C(minor));
+}
+
+/* Status readback confirming to ieee1284 */
+static long lp_read(struct inode * inode, struct file * file, 
+		   char * buf, unsigned long count)
+{
+	unsigned char z=0, Byte=0, status;
+	char *temp;
+	int retval;
+	unsigned int counter=0;
+	unsigned int i;
+	unsigned int minor=MINOR(inode->i_rdev);
+	
+	temp=buf;	
+#ifdef LP_READ_DEBUG 
+	printk(KERN_INFO "lp%d: read mode\n", minor);
+#endif
+
+	retval = verify_area(VERIFY_WRITE, buf, count);
+	if (retval)
+		return retval;
+	if (lp_nibble_mode_ok(minor)==0) {
+		lp_select_in_high(minor);
+		return temp-buf;          /*  End of file */
+	}
+	for (i=0; i<=(count*2); i++) {
+		outb((inb(LP_C(minor)) | 2), LP_C(minor)); /* AutoFeed high */
+		do {
+			status=(LP_S(minor) & 0x40);
+			udelay(50);
+			counter++;
+			if (need_resched)
+				schedule();
+		} while ( (status == 0x40) && (counter < 20) );
+		if ( counter == 20 ) { /* Timeout */
+#ifdef LP_READ_DEBUG
+			printk(KERN_DEBUG "lp_read: (Autofeed high) timeout\n");
+#endif		
+			outb((inb(LP_C(minor)) & ~2), LP_C(minor));
+			lp_select_in_high(minor);
+			return temp-buf; /* end the read at timeout */
+		}
+		counter=0;
+		z=lp_read_nibble(minor);
+		outb((inb(LP_C(minor)) & ~2), LP_C(minor)); /* AutoFeed low */
+		do {
+			status=(LP_S(minor) & 0x40);
+			udelay(20);
+			counter++;
+			if (need_resched)
+				schedule();
+		} while ( (status == 0) && (counter < 20) );
+		if (counter == 20) { /* Timeout */
+#ifdef LP_READ_DEBUG
+			printk(KERN_DEBUG "lp_read: (Autofeed low) timeout\n");
+#endif
+			if (current->signal & ~current->blocked) {
+				lp_select_in_high(minor);
+				if (temp !=buf)
+					return temp-buf;
+				else 
+					return -EINTR;
+			}
+			current->state=TASK_INTERRUPTIBLE;
+			current->timeout=jiffies + LP_TIME(minor);
+			schedule();
+		}
+		counter=0;
+		if (( i & 1) != 0) {
+			Byte= (Byte | z<<4);
+			put_user(Byte, temp);
+			temp++;
+		} else Byte=z;
+	}
+	lp_select_in_high(minor);
+	return temp-buf;	
+}
+
 static int lp_open(struct inode * inode, struct file * file)
 {
 	unsigned int minor = MINOR(inode->i_rdev);
@@ -524,7 +635,7 @@
 
 static struct file_operations lp_fops = {
 	lp_lseek,
-	NULL,		/* lp_read */
+	lp_read,
 	lp_write,
 	NULL,		/* lp_readdir */
 	NULL,		/* lp_poll */
\
 
 \ /
  Last update: 2005-03-22 13:38    [W:3.660 / U:0.120 seconds]
©2003-2008 Jasper Spaans