⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 lp.c

📁 unix/linux 编程实践一书的所有源代码
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * Generic parallel printer driver * * 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 * lp_read (Status readback) support added by Carsten Gross, *                                             carsten@sol.wohnheim.uni-ulm.de * Support for parport by Philip Blundell <Philip.Blundell@pobox.com> * Parport sharing hacking by Andrea Arcangeli * Fixed kernel_(to/from)_user memory copy to check for errors * 				by Riccardo Facchetti <fizban@tin.it> * Redesigned interrupt handling for handle printers with buggy handshake *				by Andrea Arcangeli, 11 May 1998 * Full efficient handling of printer with buggy irq handshake (now I have * understood the meaning of the strange handshake). This is done sending new * characters if the interrupt is just happened, even if the printer say to * be still BUSY. This is needed at least with Epson Stylus Color. To enable * the new TRUST_IRQ mode read the `LP OPTIMIZATION' section below... * Fixed the irq on the rising edge of the strobe case. * Obsoleted the CAREFUL flag since a printer that doesn' t work with * CAREFUL will block a bit after in lp_check_status(). *				Andrea Arcangeli, 15 Oct 1998 *//* This driver should, in theory, work with any parallel port that has an * appropriate low-level driver; all I/O is done through the parport * abstraction layer. * * If this driver is built into the kernel, you can configure it using the * kernel command-line.  For example: * *	lp=parport1,none,parport2	(bind lp0 to parport1, disable lp1 and *					 bind lp2 to parport2) * *	lp=auto				(assign lp devices to all ports that *				         have printers attached, as determined *					 by the IEEE-1284 autoprobe) *  *	lp=reset			(reset the printer during  *					 initialisation) * *	lp=off				(disable the printer driver entirely) * * If the driver is loaded as a module, similar functionality is available * using module parameters.  The equivalent of the above commands would be: * *	# insmod lp.o parport=1,none,2 * *	# insmod lp.o parport=auto * *	# insmod lp.o reset=1 *//* * LP OPTIMIZATIONS * * - TRUST_IRQ flag *  * Epson Stylus Color, HP and many other new printers want the TRUST_IRQ flag * set when printing with interrupts. This is a long story. Such printers * use a broken handshake (see the timing graph below) when printing with * interrupts. The lp driver as default is just able to handle such bogus * handshake, but setting such flag cause lp to go faster and probably do * what such printers want (even if not documented). * * NOTE that setting the TRUST_IRQ flag in some printer can cause the irq * printing to fail completly. You must try, to know if your printer * will handle it. I suggest a graphics printing to force a major flow of * characters to the printer for do the test. NOTE also that the TRUST_IRQ * flag _should_ be fine everywhere but there is a lot of buggy hardware out * there, so I am forced to implement it as a not-default thing. * WARNING: before to do the test, be sure to have not played with the * `-w' parameter of tunelp! * * Note also that lp automagically warn you (with a KERN_WARNING) if it * detects that you could _try_ to set the TRUST_IRQ flag to speed up the * printing and decrease the CPU load. * * To set the TRUST_IRQ flag you can use this command: * * tunelp /dev/lp? -T on * * If you have an old tunelp executable you can (hack and) use this simple * C lazy proggy to set the flag in the lp driver:-------------------------- cut here -------------------------------------#include <fcntl.h>#include <sys/ioctl.h>#define	LPTRUSTIRQ  0x060fint main(int argc, char **argv){	int fd = open("/dev/lp0", O_RDONLY);	ioctl(fd, LPTRUSTIRQ, argc - 1);	if (argc - 1)		printf("trusting the irq\n");	else		printf("untrusting the irq\n");	return 0;}-------------------------- cut here ------------------------------------- * - LP_WAIT time * * You can use this setting if your printer is fast enough and/or your * machine is slow enough ;-). * * tunelp /dev/lp? -w 0 * * - LP_CHAR tries * * If you print with irqs probably you can decrease the CPU load a lot using * this setting. This is not the default because the printing is reported to * be jerky somewhere... * * tunelp /dev/lp? -c 1 * *					11 Nov 1998, Andrea Arcangeli *//* COMPATIBILITY WITH OLD KERNELS * * Under Linux 2.0 and previous versions, lp devices were bound to ports at * particular I/O addresses, as follows: * *	lp0		0x3bc *	lp1		0x378 *	lp2		0x278 * * The new driver, by default, binds lp devices to parport devices as it * finds them.  This means that if you only have one port, it will be bound * to lp0 regardless of its I/O address.  If you need the old behaviour, you * can force it using the parameters described above. *//* * The new interrupt handling code take care of the buggy handshake * of some HP and Epson printer: * ___ * ACK    _______________    ___________ *                       |__| * ____ * BUSY   _________              _______ *                 |____________| * * I discovered this using the printer scanner that you can find at: * *	ftp://e-mind.com/pub/linux/pscan/ * *					11 May 98, Andrea Arcangeli * * My printer scanner run on an Epson Stylus Color show that such printer * generates the irq on the _rising_ edge of the STROBE. Now lp handle * this case fine too. * *					15 Oct 1998, Andrea Arcangeli */#include <linux/module.h>#include <linux/init.h>#include <linux/config.h>#include <linux/errno.h>#include <linux/kernel.h>#include <linux/major.h>#include <linux/sched.h>#include <linux/malloc.h>#include <linux/fcntl.h>#include <linux/delay.h>#include <linux/parport.h>#undef LP_STATS#include <linux/lp.h>#include <asm/irq.h>#include <asm/uaccess.h>#include <asm/system.h>/* if you have more than 3 printers, remember to increase LP_NO */#define LP_NO 3struct lp_struct lp_table[LP_NO] ={	[0 ... LP_NO-1] = {NULL, 0, LP_INIT_CHAR, LP_INIT_TIME, LP_INIT_WAIT,			   NULL,#ifdef LP_STATS			   0, 0, {0},#endif			   NULL, 0, 0, 0}};/* Test if printer is ready */#define	LP_READY(status)	((status) & LP_PBUSY)/* Test if the printer is not acking the strobe */#define	LP_NO_ACKING(status)	((status) & LP_PACK)/* Test if the printer has error conditions */#define LP_NO_ERROR(status)	((status) & LP_PERRORP)#undef LP_DEBUG#undef LP_READ_DEBUG/* --- parport support ----------------------------------------- */static int lp_preempt(void *handle){       struct lp_struct *lps = (struct lp_struct *)handle;       if (waitqueue_active (&lps->wait_q))               wake_up_interruptible(&lps->wait_q);       /* Don't actually release the port now */       return 1;}#define lp_parport_release(x)	do { parport_release(lp_table[(x)].dev); } while (0);#define lp_parport_claim(x)	do { parport_claim_or_block(lp_table[(x)].dev); } while (0);/* --- low-level port access ----------------------------------- */#define r_dtr(x)	(parport_read_data(lp_table[(x)].dev->port))#define r_str(x)	(parport_read_status(lp_table[(x)].dev->port))#define w_ctr(x,y)	do { parport_write_control(lp_table[(x)].dev->port, (y)); } while (0)#define w_dtr(x,y)	do { parport_write_data(lp_table[(x)].dev->port, (y)); } while (0)static __inline__ void lp_yield (int minor){	if (!parport_yield_blocking (lp_table[minor].dev))	{		if (current->need_resched)			schedule ();	} else		lp_table[minor].irq_missed = 1;}static __inline__ void lp_schedule(int minor, long timeout){	struct pardevice *dev = lp_table[minor].dev;	register unsigned long int timeslip = (jiffies - dev->time);	if ((timeslip > dev->timeslice) && (dev->port->waithead != NULL)) {		lp_parport_release(minor);		lp_table[minor].irq_missed = 1;		schedule_timeout(timeout);		lp_parport_claim(minor);	} 	else		schedule_timeout(timeout);}static int lp_reset(int minor){	int retval;	lp_parport_claim (minor);	w_ctr(minor, LP_PSELECP);	udelay (LP_DELAY);	w_ctr(minor, LP_PSELECP | LP_PINITP);	retval = r_str(minor);	lp_parport_release (minor);	return retval;}#define	lp_wait(minor)	udelay(LP_WAIT(minor))static inline int lp_char(char lpchar, int minor){	unsigned long count = 0;#ifdef LP_STATS	struct lp_stats *stats;#endif	if (signal_pending(current))		return 0;	for (;;)	{		unsigned char status;		int irq_ok = 0;		/*		 * Give a chance to other pardevice to run in the meantime.		 */		lp_yield(minor);		status = r_str(minor);		if (LP_NO_ERROR(status))		{			if (LP_READY(status))				break;			/*			 * This is a crude hack that should be well known			 * at least by Epson device driver developers. -arca			 */			irq_ok = (!LP_POLLED(minor) &&				  LP_NO_ACKING(status) &&				  lp_table[minor].irq_detected);			if ((LP_F(minor) & LP_TRUST_IRQ) && irq_ok)				break;		}		/*		 * NOTE: if you run with irqs you _must_ use		 * `tunelp /dev/lp? -c 1' to be rasonable efficient!		 */		if (++count == LP_CHAR(minor))		{			if (irq_ok)			{				static int first_time = 1;				/*				 * The printer is using a buggy handshake, so				 * revert to polling to not overload the				 * machine and warn the user that its printer				 * could get optimized trusting the irq. -arca				 */				lp_table[minor].irq_missed = 1;				if (first_time)				{					first_time = 0;					printk(KERN_WARNING "lp%d: the "					       "printing could be optimized "					       "using the TRUST_IRQ flag, "					       "see the top of "					       "linux/drivers/char/lp.c\n",					       minor);				}			}			return 0;		}	}	w_dtr(minor, lpchar);#ifdef LP_STATS	stats = &LP_STAT(minor);	stats->chars++;#endif	/* must wait before taking strobe high, and after taking strobe	   low, according spec.  Some printers need it, others don't. */	lp_wait(minor);	/* control port takes strobe high */	if (LP_POLLED(minor))	{		w_ctr(minor, LP_PSELECP | LP_PINITP | LP_PSTROBE);		lp_wait(minor);		w_ctr(minor, LP_PSELECP | LP_PINITP);	} else {		/*		 * Epson Stylus Color generate the IRQ on the rising edge of		 * strobe so clean the irq's information before playing with		 * the strobe. -arca		 */		lp_table[minor].irq_detected = 0;		lp_table[minor].irq_missed = 0;		/*		 * Be sure that the CPU doesn' t reorder instructions. -arca		 */		mb();		w_ctr(minor, LP_PSELECP | LP_PINITP | LP_PSTROBE | LP_PINTEN);		lp_wait(minor);		w_ctr(minor, LP_PSELECP | LP_PINITP | LP_PINTEN);	}	/*	 * Give to the printer a chance to put BUSY low. Really we could	 * remove this because we could _guess_ that we are slower to reach	 * again lp_char() than the printer to put BUSY low, but I' d like	 * to remove this variable from the function I go solve	 * when I read bug reports ;-). -arca	 */	lp_wait(minor);#ifdef LP_STATS	/* 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;#endif	return 1;}static void lp_interrupt(int irq, void *dev_id, struct pt_regs *regs){	struct lp_struct *lp_dev = (struct lp_struct *) dev_id;	if (waitqueue_active (&lp_dev->wait_q))		wake_up_interruptible(&lp_dev->wait_q);	lp_dev->irq_detected = 1;	lp_dev->irq_missed = 0;}static void lp_error(int minor){	if (LP_POLLED(minor) || LP_PREEMPTED(minor)) {		current->state = TASK_INTERRUPTIBLE;		lp_parport_release(minor);		current->state = TASK_INTERRUPTIBLE;		schedule_timeout(LP_TIMEOUT_POLLED);		lp_parport_claim(minor);		lp_table[minor].irq_missed = 1;	}}static int lp_check_status(int minor){	unsigned int last = lp_table[minor].last_error;	unsigned char status = r_str(minor);	if ((status & LP_PERRORP) && !(LP_F(minor) & LP_CAREFUL))		/* No error. */		last = 0;	else if ((status & LP_POUTPA)) {		if (last != LP_POUTPA) {			last = LP_POUTPA;			printk(KERN_INFO "lp%d out of paper\n", minor);		}	} else if (!(status & LP_PSELECD)) {		if (last != LP_PSELECD) {			last = LP_PSELECD;			printk(KERN_INFO "lp%d off-line\n", minor);		}	} else if (!(status & LP_PERRORP)) {		if (last != LP_PERRORP) {			last = LP_PERRORP;			printk(KERN_INFO "lp%d on fire\n", minor);		}	} else {		last = 0; /* Come here if LP_CAREFUL is set and no                             errors are reported. */	}	lp_table[minor].last_error = last;	if (last != 0) {		if (LP_F(minor) & LP_ABORT)			return 1;		lp_error(minor);	}	return 0;}static int lp_write_buf(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];	if (minor >= LP_NO)		return -ENXIO;	if (lp->dev == NULL)		return -ENXIO;	lp_table[minor].last_error = 0;	lp_table[minor].irq_detected = 0;	lp_table[minor].irq_missed = 1;	if (LP_POLLED(minor))		w_ctr(minor, LP_PSELECP | LP_PINITP);	else		w_ctr(minor, LP_PSELECP | LP_PINITP | LP_PINTEN);	do {		bytes_written = 0;		copy_size = (count <= LP_BUFFER_SIZE ? count : LP_BUFFER_SIZE);		if (copy_from_user(lp->lp_buffer, buf, copy_size))		{			w_ctr(minor, LP_PSELECP | LP_PINITP);			return -EFAULT;		}		while (copy_size) {			if (lp_char(lp->lp_buffer[bytes_written], minor)) {				--copy_size;				++bytes_written;#ifdef LP_STATS				lp->runchars++;#endif			} else {				int rc = total_bytes_written + bytes_written;#ifdef LP_STATS				if (lp->runchars > LP_STAT(minor).maxrun)					LP_STAT(minor).maxrun = lp->runchars;				LP_STAT(minor).sleeps++;#endif				if (signal_pending(current))				{					w_ctr(minor, LP_PSELECP | LP_PINITP);					if (total_bytes_written + bytes_written)						return total_bytes_written + bytes_written;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -