patch-1.3.18 linux/drivers/char/lp.c
Next file: linux/drivers/char/serial.c
Previous file: linux/drivers/char/keyboard.c
Back to the patch index
Back to the overall index
- Lines: 362
- Date:
Wed Aug 9 19:12:20 1995
- Orig file:
v1.3.17/linux/drivers/char/lp.c
- Orig date:
Wed Aug 2 13:21:00 1995
diff -u --recursive --new-file v1.3.17/linux/drivers/char/lp.c linux/drivers/char/lp.c
@@ -6,6 +6,7 @@
* Copyright (C) 1993 by Nigel Gamble (added interrupt code)
* Copyright (C) 1994 by Alan Cox (Modularised it)
* LPCAREFUL, LPABORT, LPGETSTATUS added by Chris Metcalf, metcalf@lcs.mit.edu
+ * Statistics and support for slow printers by Rob Janssen, rob@knoware.nl
*/
#ifdef MODULE
@@ -24,6 +25,7 @@
#include <linux/malloc.h>
#include <linux/ioport.h>
#include <linux/fcntl.h>
+#include <linux/delay.h>
#include <asm/io.h>
#include <asm/segment.h>
@@ -36,9 +38,9 @@
* if you have more than 3 printers, remember to increase LP_NO
*/
struct lp_struct lp_table[] = {
- { 0x3bc, 0, 0, LP_INIT_CHAR, LP_INIT_TIME, LP_INIT_WAIT, NULL, NULL, },
- { 0x378, 0, 0, LP_INIT_CHAR, LP_INIT_TIME, LP_INIT_WAIT, NULL, NULL, },
- { 0x278, 0, 0, LP_INIT_CHAR, LP_INIT_TIME, LP_INIT_WAIT, NULL, NULL, },
+ { 0x3bc, 0, 0, LP_INIT_CHAR, LP_INIT_TIME, LP_INIT_WAIT, NULL, NULL, 0, 0, 0, {0} },
+ { 0x378, 0, 0, LP_INIT_CHAR, LP_INIT_TIME, LP_INIT_WAIT, NULL, NULL, 0, 0, 0, {0} },
+ { 0x278, 0, 0, LP_INIT_CHAR, LP_INIT_TIME, LP_INIT_WAIT, NULL, NULL, 0, 0, 0, {0} },
};
#define LP_NO 3
@@ -54,33 +56,24 @@
/*
* All my debugging code assumes that you debug with only one printer at
* a time. RWWH
+ * Debug info moved into stats area, so this is no longer true (Rob Janssen)
*/
#undef LP_DEBUG
static int lp_reset(int minor)
{
- int testvalue;
- unsigned char command;
-
- command = LP_PSELECP | LP_PINITP;
-
- /* reset value */
- outb_p(0, LP_C(minor));
- for (testvalue = 0 ; testvalue < LP_DELAY ; testvalue++)
- ;
- outb_p(command, LP_C(minor));
+ outb_p(LP_PSELECP, LP_C(minor));
+ udelay(LP_DELAY);
+ outb_p(LP_PSELECP | LP_PINITP, LP_C(minor));
return LP_S(minor);
}
-#ifdef LP_DEBUG
-static int lp_max_count = 1;
-#endif
-
-static int lp_char_polled(char lpchar, int minor)
+static inline int lp_char_polled(char lpchar, int minor)
{
- int status = 0, wait = 0;
+ int status, wait = 0;
unsigned long count = 0;
+ struct lp_stats *stats;
do {
status = LP_S(minor);
@@ -93,13 +86,9 @@
return 0;
/* we timed out, and the character was /not/ printed */
}
-#ifdef LP_DEBUG
- if (count > lp_max_count) {
- printk("lp success after %d counts.\n",count);
- lp_max_count=count;
- }
-#endif
outb_p(lpchar, LP_B(minor));
+ stats = &LP_STAT(minor);
+ stats->chars++;
/* must wait before taking strobe high, and after taking strobe
low, according spec. Some printers need it, others don't. */
while(wait != LP_WAIT(minor)) wait++;
@@ -108,58 +97,76 @@
while(wait) wait--;
/* take strobe low */
outb_p(( LP_PSELECP | LP_PINITP ), ( LP_C( minor )));
+ /* update waittime statistics */
+ if (count > stats->maxwait) {
+#ifdef LP_DEBUG
+ printk(KERN_DEBUG "lp%d success after %d counts.\n",minor,count);
+#endif
+ stats->maxwait = count;
+ }
+ count *= 256;
+ wait = (count > stats->meanwait)? count - stats->meanwait :
+ stats->meanwait - count;
+ stats->meanwait = (255*stats->meanwait + count + 128) / 256;
+ stats->mdev = ((127 * stats->mdev) + wait + 64) / 128;
return 1;
}
-static int lp_char_interrupt(char lpchar, int minor)
+static inline int lp_char_interrupt(char lpchar, int minor)
{
- int wait = 0;
+ int wait;
+ unsigned long count = 0;
unsigned char status;
+ struct lp_stats *stats;
-
- if (!((status = LP_S(minor)) & LP_PACK) || (status & LP_PBUSY)
- || !((status = LP_S(minor)) & LP_PACK) || (status & LP_PBUSY)
- || !((status = LP_S(minor)) & LP_PACK) || (status & LP_PBUSY)) {
-
+ do {
+ if ((status = LP_S(minor)) & LP_PBUSY) {
if (!LP_CAREFUL_READY(minor, status))
return 0;
outb_p(lpchar, LP_B(minor));
+ stats = &LP_STAT(minor);
+ stats->chars++;
/* must wait before taking strobe high, and after taking strobe
low, according spec. Some printers need it, others don't. */
+ wait = 0;
while(wait != LP_WAIT(minor)) wait++;
/* control port takes strobe high */
outb_p(( LP_PSELECP | LP_PINITP | LP_PSTROBE ), ( LP_C( minor )));
while(wait) wait--;
/* take strobe low */
outb_p(( LP_PSELECP | LP_PINITP ), ( LP_C( minor )));
+ /* update waittime statistics */
+ if (count) {
+ if (count > stats->maxwait)
+ stats->maxwait = count;
+ count *= 256;
+ wait = (count > stats->meanwait)? count - stats->meanwait :
+ stats->meanwait - count;
+ stats->meanwait = (255*stats->meanwait + count + 128) / 256;
+ stats->mdev = ((127 * stats->mdev) + wait + 64) / 128;
+ }
return 1;
- }
+ }
+ } while (count++ < LP_CHAR(minor));
return 0;
}
-#ifdef LP_DEBUG
- unsigned int lp_total_chars = 0;
- unsigned int lp_last_call = 0;
-#endif
-
static void lp_interrupt(int irq, struct pt_regs *regs)
{
struct lp_struct *lp = &lp_table[0];
- struct lp_struct *lp_end = &lp_table[LP_NO];
while (irq != lp->irq) {
- if (++lp >= lp_end)
+ if (++lp >= &lp_table[LP_NO])
return;
}
wake_up(&lp->lp_wait_q);
}
-static int lp_write_interrupt(struct inode * inode, struct file * file, const char * buf, int count)
+static inline int lp_write_interrupt(unsigned int minor, const char * buf, int count)
{
- unsigned int minor = MINOR(inode->i_rdev);
unsigned long copy_size;
unsigned long total_bytes_written = 0;
unsigned long bytes_written;
@@ -175,8 +182,11 @@
if (lp_char_interrupt(lp->lp_buffer[bytes_written], minor)) {
--copy_size;
++bytes_written;
+ lp_table[minor].runchars++;
} else {
int rc = total_bytes_written + bytes_written;
+ if (lp_table[minor].runchars > LP_STAT(minor).maxrun)
+ LP_STAT(minor).maxrun = lp_table[minor].runchars;
status = LP_S(minor);
if ((status & LP_POUTPA)) {
printk(KERN_INFO "lp%d out of paper\n", minor);
@@ -191,6 +201,7 @@
if (LP_F(minor) & LP_ABORT)
return rc?rc:-EIO;
}
+ LP_STAT(minor).sleeps++;
cli();
outb_p((LP_PSELECP|LP_PINITP|LP_PINTEN), (LP_C(minor)));
status = LP_S(minor);
@@ -200,6 +211,7 @@
sti();
continue;
}
+ lp_table[minor].runchars=0;
current->timeout = jiffies + LP_TIMEOUT_INTERRUPT;
interruptible_sleep_on(&lp->lp_wait_q);
outb_p((LP_PSELECP|LP_PINITP), (LP_C(minor)));
@@ -222,34 +234,24 @@
return total_bytes_written;
}
-static int lp_write_polled(struct inode * inode, struct file * file,
- const char * buf, int count)
+static inline int lp_write_polled(unsigned int minor, const char * buf, int count)
{
- int retval;
- unsigned int minor = MINOR(inode->i_rdev);
+ int retval,status;
char c;
- const char *temp = buf;
-
-#ifdef LP_DEBUG
- if (jiffies-lp_last_call > LP_TIME(minor)) {
- lp_total_chars = 0;
- lp_max_count = 1;
- }
- lp_last_call = jiffies;
-#endif
+ const char *temp;
temp = buf;
while (count > 0) {
c = get_user(temp);
retval = lp_char_polled(c, minor);
/* only update counting vars if character was printed */
- if (retval) { count--; temp++;
-#ifdef LP_DEBUG
- lp_total_chars++;
-#endif
- }
- if (!retval) { /* if printer timed out */
- int status = LP_S(minor);
+ if (retval) {
+ count--; temp++;
+ lp_table[minor].runchars++;
+ } else { /* if printer timed out */
+ if (lp_table[minor].runchars > LP_STAT(minor).maxrun)
+ LP_STAT(minor).maxrun = lp_table[minor].runchars;
+ status = LP_S(minor);
if (status & LP_POUTPA) {
printk(KERN_INFO "lp%d out of paper\n", minor);
@@ -284,11 +286,12 @@
else
return -EINTR;
}
+ LP_STAT(minor).sleeps++;
#ifdef LP_DEBUG
- printk("lp sleeping at %d characters for %d jiffies\n",
- lp_total_chars, LP_TIME(minor));
- lp_total_chars=0;
+ printk(KERN_DEBUG "lp%d sleeping at %d characters for %d jiffies\n",
+ minor,lp_table[minor].runchars, LP_TIME(minor));
#endif
+ lp_table[minor].runchars=0;
current->state = TASK_INTERRUPTIBLE;
current->timeout = jiffies + LP_TIME(minor);
schedule();
@@ -299,10 +302,16 @@
static int lp_write(struct inode * inode, struct file * file, const char * buf, int count)
{
- if (LP_IRQ(MINOR(inode->i_rdev)))
- return lp_write_interrupt(inode, file, buf, count);
+ unsigned int minor = MINOR(inode->i_rdev);
+
+ if (jiffies-lp_table[minor].lastcall > LP_TIME(minor))
+ lp_table[minor].runchars = 0;
+ lp_table[minor].lastcall = jiffies;
+
+ if (LP_IRQ(minor))
+ return lp_write_interrupt(minor, buf, count);
else
- return lp_write_polled(inode, file, buf, count);
+ return lp_write_polled(minor, buf, count);
}
static int lp_lseek(struct inode * inode, struct file * file,
@@ -392,7 +401,7 @@
int retval = 0;
#ifdef LP_DEBUG
- printk("lp%d ioctl, cmd: 0x%x, arg: 0x%x\n", minor, cmd, arg);
+ printk(KERN_DEBUG "lp%d ioctl, cmd: 0x%x, arg: 0x%x\n", minor, cmd, arg);
#endif
if (minor >= LP_NO)
return -ENODEV;
@@ -489,6 +498,17 @@
case LPRESET:
lp_reset(minor);
break;
+ case LPGETSTATS:
+ retval = verify_area(VERIFY_WRITE, (void *) arg,
+ sizeof(struct lp_stats));
+ if (retval)
+ return retval;
+ else {
+ memcpy_tofs((int *) arg, &LP_STAT(minor), sizeof(struct lp_stats));
+ if (suser())
+ memset(&LP_STAT(minor), 0, sizeof(struct lp_stats));
+ }
+ break;
default:
retval = -EINVAL;
}
@@ -517,8 +537,9 @@
#endif
{
int offset = 0;
- unsigned int testvalue = 0;
+ unsigned int testvalue;
int count = 0;
+ int base,size;
if (register_chrdev(LP_MAJOR,"lp",&lp_fops)) {
printk("lp: unable to get major %d\n", LP_MAJOR);
@@ -530,18 +551,19 @@
}
/* take on all known port values */
for (offset = 0; offset < LP_NO; offset++) {
- if (check_region(LP_B(offset), 3))
+ base = LP_B(offset);
+ size = (base == 0x3bc)? 3 : 8;
+ if (check_region(base, size))
continue;
/* write to port & read back to check */
- outb_p( LP_DUMMY, LP_B(offset));
- for (testvalue = 0 ; testvalue < LP_DELAY ; testvalue++)
- ;
- testvalue = inb_p(LP_B(offset));
+ outb_p( LP_DUMMY, base);
+ udelay(LP_DELAY);
+ testvalue = inb_p(base);
if (testvalue == LP_DUMMY) {
LP_F(offset) |= LP_EXIST;
lp_reset(offset);
- printk("lp%d at 0x%04x, ", offset,LP_B(offset));
- request_region(LP_B(offset), 3, "lp");
+ printk("lp%d at 0x%04x, ", offset,base);
+ request_region(base, size, "lp");
if (LP_IRQ(offset))
printk("(irq = %d)\n", LP_IRQ(offset));
else
@@ -559,6 +581,7 @@
return kmem_start;
#endif
}
+ int base,size;
#ifdef MODULE
void cleanup_module(void)
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov
with Sam's (original) version of this