![]() | |||||||||||||
Messages in this thread Patch in this message |
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 | |||||||||||||