// SPDX-License-Identifier: GPL-2.0-only /* * 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 <philb@gnu.org> * Parport sharing hacking by Andrea Arcangeli * Fixed kernel_(to/from)_user memory copy to check for errors * by Riccardo Facchetti <fizban@tin.it> * 22-JAN-1998 Added support for devfs Richard Gooch <rgooch@atnf.csiro.au> * 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 * Obsoleted and removed all the lowlevel stuff implemented in the last * month to use the IEEE1284 functions (that handle the _new_ compatibilty * mode fine).
*/
/* 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
*/
/* 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 * * The so called `buggy' handshake is really the well documented * compatibility mode IEEE1284 handshake. They changed the well known * Centronics handshake acking in the middle of busy expecting to not * break drivers or legacy application, while they broken linux lp * until I fixed it reverse engineering the protocol by hand some * month ago... * * 14 Dec 1998, Andrea Arcangeli * * Copyright (C) 2000 by Tim Waugh (added LPSETTIMEOUT ioctl)
*/
/* * Try to negotiate to a new mode; if unsuccessful negotiate to * compatibility mode. Return the mode we ended up in.
*/ staticint lp_negotiate(struct parport *port, int mode)
{ if (parport_negotiate(port, mode) != 0) {
mode = IEEE1284_MODE_COMPAT;
parport_negotiate(port, mode);
}
staticvoid lp_error(int minor)
{
DEFINE_WAIT(wait); int polling;
if (LP_F(minor) & LP_ABORT) return;
polling = lp_table[minor].dev->port->irq == PARPORT_IRQ_NONE; if (polling)
lp_release_parport(&lp_table[minor]);
prepare_to_wait(&lp_table[minor].waitq, &wait, TASK_INTERRUPTIBLE);
schedule_timeout(LP_TIMEOUT_POLLED);
finish_wait(&lp_table[minor].waitq, &wait); if (polling)
lp_claim_parport_or_block(&lp_table[minor]); else
parport_yield_blocking(lp_table[minor].dev);
}
staticint lp_check_status(int minor)
{ int error = 0; unsignedint last = lp_table[minor].last_error; unsignedchar status = r_str(minor); if ((status & LP_PERRORP) && !(LP_F(minor) & LP_CAREFUL)) /* No error. */
last = 0; elseif ((status & LP_POUTPA)) { if (last != LP_POUTPA) {
last = LP_POUTPA;
printk(KERN_INFO "lp%d out of paper\n", minor);
}
error = -ENOSPC;
} elseif (!(status & LP_PSELECD)) { if (last != LP_PSELECD) {
last = LP_PSELECD;
printk(KERN_INFO "lp%d off-line\n", minor);
}
error = -EIO;
} elseif (!(status & LP_PERRORP)) { if (last != LP_PERRORP) {
last = LP_PERRORP;
printk(KERN_INFO "lp%d on fire\n", minor);
}
error = -EIO;
} else {
last = 0; /* Come here if LP_CAREFUL is set and no
errors are reported. */
}
lp_table[minor].last_error = last;
if (last != 0)
lp_error(minor);
return error;
}
staticint lp_wait_ready(int minor, int nonblock)
{ int error = 0;
/* If we're not in compatibility mode, we're ready now! */ if (lp_table[minor].current_mode != IEEE1284_MODE_COMPAT) { return 0;
}
do {
error = lp_check_status(minor); if (error && (nonblock || (LP_F(minor) & LP_ABORT))) break; if (signal_pending(current)) {
error = -EINTR; break;
}
} while (error); return error;
}
/* Claim Parport or sleep until it becomes available
*/
lp_claim_parport_or_block(&lp_table[minor]); /* Go to the proper mode. */
lp_table[minor].current_mode = lp_negotiate(port,
lp_table[minor].best_mode);
staticint lp_open(struct inode *inode, struct file *file)
{ unsignedint minor = iminor(inode); int ret = 0;
mutex_lock(&lp_mutex); if (minor >= LP_NO) {
ret = -ENXIO; goto out;
} if ((LP_F(minor) & LP_EXIST) == 0) {
ret = -ENXIO; goto out;
} if (test_and_set_bit(LP_BUSY_BIT_POS, &LP_F(minor))) {
ret = -EBUSY; goto out;
} /* If ABORTOPEN is set and the printer is offline or out of paper, we may still want to open it to perform ioctl()s. Therefore we have commandeered O_NONBLOCK, even though it is being used in a non-standard manner. This is strictly a Linux hack, and
should most likely only ever be used by the tunelp application. */ if ((LP_F(minor) & LP_ABORTOPEN) && !(file->f_flags & O_NONBLOCK)) { int status;
lp_claim_parport_or_block(&lp_table[minor]);
status = r_str(minor);
lp_release_parport(&lp_table[minor]); if (status & LP_POUTPA) {
printk(KERN_INFO "lp%d out of paper\n", minor);
LP_F(minor) &= ~LP_BUSY;
ret = -ENOSPC; goto out;
} elseif (!(status & LP_PSELECD)) {
printk(KERN_INFO "lp%d off-line\n", minor);
LP_F(minor) &= ~LP_BUSY;
ret = -EIO; goto out;
} elseif (!(status & LP_PERRORP)) {
printk(KERN_ERR "lp%d printer error\n", minor);
LP_F(minor) &= ~LP_BUSY;
ret = -EIO; goto out;
}
}
lp_table[minor].lp_buffer = kmalloc(LP_BUFFER_SIZE, GFP_KERNEL); if (!lp_table[minor].lp_buffer) {
LP_F(minor) &= ~LP_BUSY;
ret = -ENOMEM; goto out;
} /* Determine if the peripheral supports ECP mode */
lp_claim_parport_or_block(&lp_table[minor]); if ((lp_table[minor].dev->port->modes & PARPORT_MODE_ECP) &&
!parport_negotiate(lp_table[minor].dev->port,
IEEE1284_MODE_ECP)) {
printk(KERN_INFO "lp%d: ECP mode\n", minor);
lp_table[minor].best_mode = IEEE1284_MODE_ECP;
} else {
lp_table[minor].best_mode = IEEE1284_MODE_COMPAT;
} /* Leave peripheral in compatibility mode */
parport_negotiate(lp_table[minor].dev->port, IEEE1284_MODE_COMPAT);
lp_release_parport(&lp_table[minor]);
lp_table[minor].current_mode = IEEE1284_MODE_COMPAT;
out:
mutex_unlock(&lp_mutex); return ret;
}
staticint lp_do_ioctl(unsignedint minor, unsignedint cmd, unsignedlong arg, void __user *argp)
{ int status; int retval = 0;
#ifdef LP_DEBUG
printk(KERN_DEBUG "lp%d ioctl, cmd: 0x%x, arg: 0x%lx\n", minor, cmd, arg); #endif if (minor >= LP_NO) return -ENODEV; if ((LP_F(minor) & LP_EXIST) == 0) return -ENODEV; switch (cmd) { case LPTIME: if (arg > UINT_MAX / HZ) return -EINVAL;
LP_TIME(minor) = arg * HZ/100; break; case LPCHAR:
LP_CHAR(minor) = arg; break; case LPABORT: if (arg)
LP_F(minor) |= LP_ABORT; else
LP_F(minor) &= ~LP_ABORT; break; case LPABORTOPEN: if (arg)
LP_F(minor) |= LP_ABORTOPEN; else
LP_F(minor) &= ~LP_ABORTOPEN; break; case LPCAREFUL: if (arg)
LP_F(minor) |= LP_CAREFUL; else
LP_F(minor) &= ~LP_CAREFUL; break; case LPWAIT:
LP_WAIT(minor) = arg; break; case LPSETIRQ: return -EINVAL; case LPGETIRQ: if (copy_to_user(argp, &LP_IRQ(minor), sizeof(int))) return -EFAULT; break; case LPGETSTATUS: if (mutex_lock_interruptible(&lp_table[minor].port_mutex)) return -EINTR;
lp_claim_parport_or_block(&lp_table[minor]);
status = r_str(minor);
lp_release_parport(&lp_table[minor]);
mutex_unlock(&lp_table[minor].port_mutex);
if (copy_to_user(argp, &status, sizeof(int))) return -EFAULT; break; case LPRESET:
lp_reset(minor); break; #ifdef LP_STATS case LPGETSTATS: if (copy_to_user(argp, &LP_STAT(minor), sizeof(struct lp_stats))) return -EFAULT; if (capable(CAP_SYS_ADMIN))
memset(&LP_STAT(minor), 0, sizeof(struct lp_stats)); break; #endif case LPGETFLAGS:
status = LP_F(minor); if (copy_to_user(argp, &status, sizeof(int))) return -EFAULT; break;
default:
retval = -EINVAL;
} return retval;
}
staticint lp_set_timeout(unsignedint minor, s64 tv_sec, long tv_usec)
{ long to_jiffies;
/* Convert to jiffies, place in lp_table */ if (tv_sec < 0 || tv_usec < 0) return -EINVAL;
/* * we used to not check, so let's not make this fatal, * but deal with user space passing a 32-bit tv_nsec in * a 64-bit field, capping the timeout to 1 second * worth of microseconds, and capping the total at * MAX_JIFFY_OFFSET.
*/ if (tv_usec > 999999)
tv_usec = 999999;
minor = iminor(file_inode(file));
mutex_lock(&lp_mutex); switch (cmd) { case LPSETTIMEOUT_OLD: if (!COMPAT_USE_64BIT_TIME) {
ret = lp_set_timeout32(minor, (void __user *)arg); break;
}
fallthrough; /* for x32 mode */ case LPSETTIMEOUT_NEW:
ret = lp_set_timeout64(minor, (void __user *)arg); break; #ifdef LP_STATS case LPGETSTATS: /* FIXME: add an implementation if you set LP_STATS */
ret = -EINVAL; break; #endif default:
ret = lp_do_ioctl(minor, cmd, arg, compat_ptr(arg)); break;
}
mutex_unlock(&lp_mutex);
/* --- support for console on the line printer ----------------- */
#ifdef CONFIG_LP_CONSOLE
#define CONSOLE_LP 0
/* If the printer is out of paper, we can either lose the messages or * stall until the printer is happy again. Define CONSOLE_LP_STRICT
* non-zero to get the latter behaviour. */ #define CONSOLE_LP_STRICT 1
/* The console must be locked when we get here. */
if (parport_claim(dev)) /* Nothing we can do. */ return;
parport_set_timeout(dev, 0);
/* Go to compatibility mode. */
parport_negotiate(port, IEEE1284_MODE_COMPAT);
do { /* Write the data, converting LF->CRLF as we go. */
ssize_t canwrite = count; char *lf = memchr(s, '\n', count); if (lf)
canwrite = lf - s;
if (canwrite > 0) {
written = parport_write(port, s, canwrite);
if (written <= 0) continue;
s += written;
count -= written;
canwrite -= written;
}
if (lf && canwrite <= 0) { constchar *crlf = "\r\n"; int i = 2;
/* Dodge the original '\n', and put '\r\n' instead. */
s++;
count--; do {
written = parport_write(port, crlf, i); if (written > 0) {
i -= written;
crlf += written;
}
} while (i > 0 && (CONSOLE_LP_STRICT || written > 0));
}
} while (count > 0 && (CONSOLE_LP_STRICT || written > 0));
switch (parport_nr[0]) { case LP_PARPORT_UNSPEC: case LP_PARPORT_AUTO: if (parport_nr[0] == LP_PARPORT_AUTO &&
port->probe_info[0].class != PARPORT_CLASS_PRINTER) return; if (lp_count == LP_NO) {
printk(KERN_INFO "lp: ignoring parallel port (max. %d)\n",LP_NO); return;
} for (i = 0; i < LP_NO; i++) if (port_num[i] == -1) break;
if (!lp_register(i, port))
lp_count++; break;
default: for (i = 0; i < LP_NO; i++) { if (port->number == parport_nr[i]) { if (!lp_register(i, port))
lp_count++; break;
}
} break;
}
}
staticvoid lp_detach(struct parport *port)
{ int n;
/* Write this some day. */ #ifdef CONFIG_LP_CONSOLE if (console_registered == port) {
unregister_console(&lpcons);
console_registered = NULL;
} #endif/* CONFIG_LP_CONSOLE */
for (n = 0; n < LP_NO; n++) { if (port_num[n] == port->number) {
port_num[n] = -1;
lp_count--;
device_destroy(&lp_class, MKDEV(LP_MAJOR, n));
parport_unregister_device(lp_table[n].dev);
}
}
}
staticint __init lp_init_module(void)
{ if (parport[0]) { /* The user gave some parameters. Let's see what they were. */ if (!strncmp(parport[0], "auto", 4))
parport_nr[0] = LP_PARPORT_AUTO; else { int n; for (n = 0; n < LP_NO && parport[n]; n++) { if (!strncmp(parport[n], "none", 4))
parport_nr[n] = LP_PARPORT_NONE; else { char *ep; unsignedlong r = simple_strtoul(parport[n], &ep, 0); if (ep != parport[n])
parport_nr[n] = r; else {
printk(KERN_ERR "lp: bad port specifier `%s'\n", parport[n]); return -ENODEV;
}
}
}
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.