📄 lp.c
字号:
/* * Copyright (C) 1992 by Jim Weigand and Linus Torvalds * Copyright (C) 1992,1993 by Michael K. Johnson * - Thanks much to Gunter Windau for pointing out to me where the error * checking ought to be. * 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 * "lp=" command line parameters added by Grant Guenther, grant@torque.net */#include <linux/module.h>#include <linux/errno.h>#include <linux/kernel.h>#include <linux/major.h>#include <linux/sched.h>#include <linux/lp.h>#include <linux/malloc.h>#include <linux/ioport.h>#include <linux/fcntl.h>#include <linux/delay.h>#include <asm/io.h>#include <asm/segment.h>#include <asm/system.h>#define NO_IRQ (-1)/* the BIOS manuals say there can be up to 4 lpt devices * but I have not seen a board where the 4th address is listed * if you have different hardware change the table below * please let me know if you have different equipment * if you have more than 3 printers, remember to increase LP_NO */struct lp_struct lp_table[] = { { 0x3bc, NO_IRQ, 0, LP_INIT_CHAR, LP_INIT_TIME, LP_INIT_WAIT, NULL, NULL, 0, 0, 0, {0} }, { 0x378, NO_IRQ, 0, LP_INIT_CHAR, LP_INIT_TIME, LP_INIT_WAIT, NULL, NULL, 0, 0, 0, {0} }, { 0x278, NO_IRQ, 0, LP_INIT_CHAR, LP_INIT_TIME, LP_INIT_WAIT, NULL, NULL, 0, 0, 0, {0} },};#define LP_NO 3/* Test if printer is ready (and optionally has no error conditions) */#define LP_READY(minor, status) \ ((LP_F(minor) & LP_CAREFUL) ? _LP_CAREFUL_READY(status) : (status & LP_PBUSY))#define LP_CAREFUL_READY(minor, status) \ ((LP_F(minor) & LP_CAREFUL) ? _LP_CAREFUL_READY(status) : 1)#define _LP_CAREFUL_READY(status) \ (status & (LP_PBUSY|LP_POUTPA|LP_PSELECD|LP_PERRORP)) == \ (LP_PBUSY|LP_PSELECD|LP_PERRORP)/* * 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_DEBUGstatic int lp_reset(int minor){ outb_p(LP_PSELECP, LP_C(minor)); udelay(LP_DELAY); outb_p(LP_PSELECP | LP_PINITP, LP_C(minor)); return LP_S(minor);}static inline int lp_char_polled(char lpchar, int minor){ int status, wait = 0; unsigned long count = 0; struct lp_stats *stats; do { status = LP_S(minor); count ++; if(need_resched) schedule(); } while(!LP_READY(minor,status) && count < LP_CHAR(minor)); if (count == LP_CHAR(minor)) { return 0; /* we timed out, and the character was /not/ printed */ } 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++; /* control port takes strobe high */ outb_p(( LP_PSELECP | LP_PINITP | LP_PSTROBE ), ( LP_C( minor ))); /* Wait until NBUSY line goes high */ count = 0; do { status = LP_S(minor); count++; if (need_resched) schedule(); } while (LP_READY(minor, status) && (count<LP_CHAR(minor))); /* 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 inline int lp_char_interrupt(char lpchar, int minor){ int wait; unsigned long count = 0; unsigned char status; struct lp_stats *stats; do { if(need_resched) schedule(); 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;}static void lp_interrupt(int irq, void *dev_id, struct pt_regs *regs){ struct lp_struct *lp = &lp_table[0]; while (irq != lp->irq) { if (++lp >= &lp_table[LP_NO]) return; } wake_up(&lp->lp_wait_q);}static inline int lp_write_interrupt(unsigned int minor, const char * buf, int count){ unsigned long copy_size; unsigned long total_bytes_written = 0; unsigned long bytes_written; struct lp_struct *lp = &lp_table[minor]; unsigned char status; do { bytes_written = 0; copy_size = (count <= LP_BUFFER_SIZE ? count : LP_BUFFER_SIZE); memcpy_fromfs(lp->lp_buffer, buf, copy_size); while (copy_size) { 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); if (LP_F(minor) & LP_ABORT) return rc?rc:-ENOSPC; } else if (!(status & LP_PSELECD)) { printk(KERN_INFO "lp%d off-line\n", minor); if (LP_F(minor) & LP_ABORT) return rc?rc:-EIO; } else if (!(status & LP_PERRORP)) { printk(KERN_ERR "lp%d printer error\n", minor); 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); if ((!(status & LP_PACK) || (status & LP_PBUSY)) && LP_CAREFUL_READY(minor, status)) { outb_p((LP_PSELECP|LP_PINITP), (LP_C(minor))); 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))); sti(); if (current->signal & ~current->blocked) { if (total_bytes_written + bytes_written) return total_bytes_written + bytes_written; else return -EINTR; } } } total_bytes_written += bytes_written; buf += bytes_written; count -= bytes_written; } while (count > 0); return total_bytes_written;}static inline int lp_write_polled(unsigned int minor, const char * buf, int count){ int retval,status; char c; 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++; 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); if(LP_F(minor) & LP_ABORT) return temp-buf?temp-buf:-ENOSPC; current->state = TASK_INTERRUPTIBLE; current->timeout = jiffies + LP_TIMEOUT_POLLED; schedule(); } else if (!(status & LP_PSELECD)) { printk(KERN_INFO "lp%d off-line\n", minor); if(LP_F(minor) & LP_ABORT) return temp-buf?temp-buf:-EIO; current->state = TASK_INTERRUPTIBLE; current->timeout = jiffies + LP_TIMEOUT_POLLED; schedule(); } else /* not offline or out of paper. on fire? */ if (!(status & LP_PERRORP)) { printk(KERN_ERR "lp%d reported invalid error status (on fire, eh?)\n", minor); if(LP_F(minor) & LP_ABORT) return temp-buf?temp-buf:-EIO; current->state = TASK_INTERRUPTIBLE; current->timeout = jiffies + LP_TIMEOUT_POLLED; schedule(); } /* check for signals before going to sleep */ if (current->signal & ~current->blocked) { if (temp != buf) return temp-buf; else return -EINTR; } LP_STAT(minor).sleeps++;#ifdef LP_DEBUG 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(); } } return temp-buf;}static int lp_write(struct inode * inode, struct file * file, const char * buf, int 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) != NO_IRQ) return lp_write_interrupt(minor, buf, count); else return lp_write_polled(minor, buf, count);}static int lp_lseek(struct inode * inode, struct file * file, off_t offset, int origin){ return -ESPIPE;}static int lp_open(struct inode * inode, struct file * file){ unsigned int minor = MINOR(inode->i_rdev); int ret; unsigned int irq; if (minor >= LP_NO) return -ENXIO; if ((LP_F(minor) & LP_EXIST) == 0) return -ENXIO; if (LP_F(minor) & LP_BUSY) return -EBUSY; MOD_INC_USE_COUNT; /* If ABORTOPEN is set and the printer is offline or out of paper,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -