18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Generic parallel printer driver
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 1992 by Jim Weigand and Linus Torvalds
68c2ecf20Sopenharmony_ci * Copyright (C) 1992,1993 by Michael K. Johnson
78c2ecf20Sopenharmony_ci * - Thanks much to Gunter Windau for pointing out to me where the error
88c2ecf20Sopenharmony_ci *   checking ought to be.
98c2ecf20Sopenharmony_ci * Copyright (C) 1993 by Nigel Gamble (added interrupt code)
108c2ecf20Sopenharmony_ci * Copyright (C) 1994 by Alan Cox (Modularised it)
118c2ecf20Sopenharmony_ci * LPCAREFUL, LPABORT, LPGETSTATUS added by Chris Metcalf, metcalf@lcs.mit.edu
128c2ecf20Sopenharmony_ci * Statistics and support for slow printers by Rob Janssen, rob@knoware.nl
138c2ecf20Sopenharmony_ci * "lp=" command line parameters added by Grant Guenther, grant@torque.net
148c2ecf20Sopenharmony_ci * lp_read (Status readback) support added by Carsten Gross,
158c2ecf20Sopenharmony_ci *                                             carsten@sol.wohnheim.uni-ulm.de
168c2ecf20Sopenharmony_ci * Support for parport by Philip Blundell <philb@gnu.org>
178c2ecf20Sopenharmony_ci * Parport sharing hacking by Andrea Arcangeli
188c2ecf20Sopenharmony_ci * Fixed kernel_(to/from)_user memory copy to check for errors
198c2ecf20Sopenharmony_ci * 				by Riccardo Facchetti <fizban@tin.it>
208c2ecf20Sopenharmony_ci * 22-JAN-1998  Added support for devfs  Richard Gooch <rgooch@atnf.csiro.au>
218c2ecf20Sopenharmony_ci * Redesigned interrupt handling for handle printers with buggy handshake
228c2ecf20Sopenharmony_ci *				by Andrea Arcangeli, 11 May 1998
238c2ecf20Sopenharmony_ci * Full efficient handling of printer with buggy irq handshake (now I have
248c2ecf20Sopenharmony_ci * understood the meaning of the strange handshake). This is done sending new
258c2ecf20Sopenharmony_ci * characters if the interrupt is just happened, even if the printer say to
268c2ecf20Sopenharmony_ci * be still BUSY. This is needed at least with Epson Stylus Color. To enable
278c2ecf20Sopenharmony_ci * the new TRUST_IRQ mode read the `LP OPTIMIZATION' section below...
288c2ecf20Sopenharmony_ci * Fixed the irq on the rising edge of the strobe case.
298c2ecf20Sopenharmony_ci * Obsoleted the CAREFUL flag since a printer that doesn' t work with
308c2ecf20Sopenharmony_ci * CAREFUL will block a bit after in lp_check_status().
318c2ecf20Sopenharmony_ci *				Andrea Arcangeli, 15 Oct 1998
328c2ecf20Sopenharmony_ci * Obsoleted and removed all the lowlevel stuff implemented in the last
338c2ecf20Sopenharmony_ci * month to use the IEEE1284 functions (that handle the _new_ compatibilty
348c2ecf20Sopenharmony_ci * mode fine).
358c2ecf20Sopenharmony_ci */
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci/* This driver should, in theory, work with any parallel port that has an
388c2ecf20Sopenharmony_ci * appropriate low-level driver; all I/O is done through the parport
398c2ecf20Sopenharmony_ci * abstraction layer.
408c2ecf20Sopenharmony_ci *
418c2ecf20Sopenharmony_ci * If this driver is built into the kernel, you can configure it using the
428c2ecf20Sopenharmony_ci * kernel command-line.  For example:
438c2ecf20Sopenharmony_ci *
448c2ecf20Sopenharmony_ci *	lp=parport1,none,parport2	(bind lp0 to parport1, disable lp1 and
458c2ecf20Sopenharmony_ci *					 bind lp2 to parport2)
468c2ecf20Sopenharmony_ci *
478c2ecf20Sopenharmony_ci *	lp=auto				(assign lp devices to all ports that
488c2ecf20Sopenharmony_ci *				         have printers attached, as determined
498c2ecf20Sopenharmony_ci *					 by the IEEE-1284 autoprobe)
508c2ecf20Sopenharmony_ci *
518c2ecf20Sopenharmony_ci *	lp=reset			(reset the printer during
528c2ecf20Sopenharmony_ci *					 initialisation)
538c2ecf20Sopenharmony_ci *
548c2ecf20Sopenharmony_ci *	lp=off				(disable the printer driver entirely)
558c2ecf20Sopenharmony_ci *
568c2ecf20Sopenharmony_ci * If the driver is loaded as a module, similar functionality is available
578c2ecf20Sopenharmony_ci * using module parameters.  The equivalent of the above commands would be:
588c2ecf20Sopenharmony_ci *
598c2ecf20Sopenharmony_ci *	# insmod lp.o parport=1,none,2
608c2ecf20Sopenharmony_ci *
618c2ecf20Sopenharmony_ci *	# insmod lp.o parport=auto
628c2ecf20Sopenharmony_ci *
638c2ecf20Sopenharmony_ci *	# insmod lp.o reset=1
648c2ecf20Sopenharmony_ci */
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci/* COMPATIBILITY WITH OLD KERNELS
678c2ecf20Sopenharmony_ci *
688c2ecf20Sopenharmony_ci * Under Linux 2.0 and previous versions, lp devices were bound to ports at
698c2ecf20Sopenharmony_ci * particular I/O addresses, as follows:
708c2ecf20Sopenharmony_ci *
718c2ecf20Sopenharmony_ci *	lp0		0x3bc
728c2ecf20Sopenharmony_ci *	lp1		0x378
738c2ecf20Sopenharmony_ci *	lp2		0x278
748c2ecf20Sopenharmony_ci *
758c2ecf20Sopenharmony_ci * The new driver, by default, binds lp devices to parport devices as it
768c2ecf20Sopenharmony_ci * finds them.  This means that if you only have one port, it will be bound
778c2ecf20Sopenharmony_ci * to lp0 regardless of its I/O address.  If you need the old behaviour, you
788c2ecf20Sopenharmony_ci * can force it using the parameters described above.
798c2ecf20Sopenharmony_ci */
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci/*
828c2ecf20Sopenharmony_ci * The new interrupt handling code take care of the buggy handshake
838c2ecf20Sopenharmony_ci * of some HP and Epson printer:
848c2ecf20Sopenharmony_ci * ___
858c2ecf20Sopenharmony_ci * ACK    _______________    ___________
868c2ecf20Sopenharmony_ci *                       |__|
878c2ecf20Sopenharmony_ci * ____
888c2ecf20Sopenharmony_ci * BUSY   _________              _______
898c2ecf20Sopenharmony_ci *                 |____________|
908c2ecf20Sopenharmony_ci *
918c2ecf20Sopenharmony_ci * I discovered this using the printer scanner that you can find at:
928c2ecf20Sopenharmony_ci *
938c2ecf20Sopenharmony_ci *	ftp://e-mind.com/pub/linux/pscan/
948c2ecf20Sopenharmony_ci *
958c2ecf20Sopenharmony_ci *					11 May 98, Andrea Arcangeli
968c2ecf20Sopenharmony_ci *
978c2ecf20Sopenharmony_ci * My printer scanner run on an Epson Stylus Color show that such printer
988c2ecf20Sopenharmony_ci * generates the irq on the _rising_ edge of the STROBE. Now lp handle
998c2ecf20Sopenharmony_ci * this case fine too.
1008c2ecf20Sopenharmony_ci *
1018c2ecf20Sopenharmony_ci *					15 Oct 1998, Andrea Arcangeli
1028c2ecf20Sopenharmony_ci *
1038c2ecf20Sopenharmony_ci * The so called `buggy' handshake is really the well documented
1048c2ecf20Sopenharmony_ci * compatibility mode IEEE1284 handshake. They changed the well known
1058c2ecf20Sopenharmony_ci * Centronics handshake acking in the middle of busy expecting to not
1068c2ecf20Sopenharmony_ci * break drivers or legacy application, while they broken linux lp
1078c2ecf20Sopenharmony_ci * until I fixed it reverse engineering the protocol by hand some
1088c2ecf20Sopenharmony_ci * month ago...
1098c2ecf20Sopenharmony_ci *
1108c2ecf20Sopenharmony_ci *                                     14 Dec 1998, Andrea Arcangeli
1118c2ecf20Sopenharmony_ci *
1128c2ecf20Sopenharmony_ci * Copyright (C) 2000 by Tim Waugh (added LPSETTIMEOUT ioctl)
1138c2ecf20Sopenharmony_ci */
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci#include <linux/module.h>
1168c2ecf20Sopenharmony_ci#include <linux/init.h>
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci#include <linux/errno.h>
1198c2ecf20Sopenharmony_ci#include <linux/kernel.h>
1208c2ecf20Sopenharmony_ci#include <linux/major.h>
1218c2ecf20Sopenharmony_ci#include <linux/sched/signal.h>
1228c2ecf20Sopenharmony_ci#include <linux/slab.h>
1238c2ecf20Sopenharmony_ci#include <linux/fcntl.h>
1248c2ecf20Sopenharmony_ci#include <linux/delay.h>
1258c2ecf20Sopenharmony_ci#include <linux/poll.h>
1268c2ecf20Sopenharmony_ci#include <linux/console.h>
1278c2ecf20Sopenharmony_ci#include <linux/device.h>
1288c2ecf20Sopenharmony_ci#include <linux/wait.h>
1298c2ecf20Sopenharmony_ci#include <linux/jiffies.h>
1308c2ecf20Sopenharmony_ci#include <linux/mutex.h>
1318c2ecf20Sopenharmony_ci#include <linux/compat.h>
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci#include <linux/parport.h>
1348c2ecf20Sopenharmony_ci#undef LP_STATS
1358c2ecf20Sopenharmony_ci#include <linux/lp.h>
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci#include <asm/irq.h>
1388c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci/* if you have more than 8 printers, remember to increase LP_NO */
1418c2ecf20Sopenharmony_ci#define LP_NO 8
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(lp_mutex);
1448c2ecf20Sopenharmony_cistatic struct lp_struct lp_table[LP_NO];
1458c2ecf20Sopenharmony_cistatic int port_num[LP_NO];
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_cistatic unsigned int lp_count = 0;
1488c2ecf20Sopenharmony_cistatic struct class *lp_class;
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci#ifdef CONFIG_LP_CONSOLE
1518c2ecf20Sopenharmony_cistatic struct parport *console_registered;
1528c2ecf20Sopenharmony_ci#endif /* CONFIG_LP_CONSOLE */
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci#undef LP_DEBUG
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci/* Bits used to manage claiming the parport device */
1578c2ecf20Sopenharmony_ci#define LP_PREEMPT_REQUEST 1
1588c2ecf20Sopenharmony_ci#define LP_PARPORT_CLAIMED 2
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci/* --- low-level port access ----------------------------------- */
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci#define r_dtr(x)	(parport_read_data(lp_table[(x)].dev->port))
1638c2ecf20Sopenharmony_ci#define r_str(x)	(parport_read_status(lp_table[(x)].dev->port))
1648c2ecf20Sopenharmony_ci#define w_ctr(x,y)	do { parport_write_control(lp_table[(x)].dev->port, (y)); } while (0)
1658c2ecf20Sopenharmony_ci#define w_dtr(x,y)	do { parport_write_data(lp_table[(x)].dev->port, (y)); } while (0)
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci/* Claim the parport or block trying unless we've already claimed it */
1688c2ecf20Sopenharmony_cistatic void lp_claim_parport_or_block(struct lp_struct *this_lp)
1698c2ecf20Sopenharmony_ci{
1708c2ecf20Sopenharmony_ci	if (!test_and_set_bit(LP_PARPORT_CLAIMED, &this_lp->bits)) {
1718c2ecf20Sopenharmony_ci		parport_claim_or_block(this_lp->dev);
1728c2ecf20Sopenharmony_ci	}
1738c2ecf20Sopenharmony_ci}
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci/* Claim the parport or block trying unless we've already claimed it */
1768c2ecf20Sopenharmony_cistatic void lp_release_parport(struct lp_struct *this_lp)
1778c2ecf20Sopenharmony_ci{
1788c2ecf20Sopenharmony_ci	if (test_and_clear_bit(LP_PARPORT_CLAIMED, &this_lp->bits)) {
1798c2ecf20Sopenharmony_ci		parport_release(this_lp->dev);
1808c2ecf20Sopenharmony_ci	}
1818c2ecf20Sopenharmony_ci}
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_cistatic int lp_preempt(void *handle)
1868c2ecf20Sopenharmony_ci{
1878c2ecf20Sopenharmony_ci	struct lp_struct *this_lp = (struct lp_struct *)handle;
1888c2ecf20Sopenharmony_ci	set_bit(LP_PREEMPT_REQUEST, &this_lp->bits);
1898c2ecf20Sopenharmony_ci	return 1;
1908c2ecf20Sopenharmony_ci}
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci/*
1948c2ecf20Sopenharmony_ci * Try to negotiate to a new mode; if unsuccessful negotiate to
1958c2ecf20Sopenharmony_ci * compatibility mode.  Return the mode we ended up in.
1968c2ecf20Sopenharmony_ci */
1978c2ecf20Sopenharmony_cistatic int lp_negotiate(struct parport *port, int mode)
1988c2ecf20Sopenharmony_ci{
1998c2ecf20Sopenharmony_ci	if (parport_negotiate(port, mode) != 0) {
2008c2ecf20Sopenharmony_ci		mode = IEEE1284_MODE_COMPAT;
2018c2ecf20Sopenharmony_ci		parport_negotiate(port, mode);
2028c2ecf20Sopenharmony_ci	}
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	return mode;
2058c2ecf20Sopenharmony_ci}
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_cistatic int lp_reset(int minor)
2088c2ecf20Sopenharmony_ci{
2098c2ecf20Sopenharmony_ci	int retval;
2108c2ecf20Sopenharmony_ci	lp_claim_parport_or_block(&lp_table[minor]);
2118c2ecf20Sopenharmony_ci	w_ctr(minor, LP_PSELECP);
2128c2ecf20Sopenharmony_ci	udelay(LP_DELAY);
2138c2ecf20Sopenharmony_ci	w_ctr(minor, LP_PSELECP | LP_PINITP);
2148c2ecf20Sopenharmony_ci	retval = r_str(minor);
2158c2ecf20Sopenharmony_ci	lp_release_parport(&lp_table[minor]);
2168c2ecf20Sopenharmony_ci	return retval;
2178c2ecf20Sopenharmony_ci}
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_cistatic void lp_error(int minor)
2208c2ecf20Sopenharmony_ci{
2218c2ecf20Sopenharmony_ci	DEFINE_WAIT(wait);
2228c2ecf20Sopenharmony_ci	int polling;
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	if (LP_F(minor) & LP_ABORT)
2258c2ecf20Sopenharmony_ci		return;
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	polling = lp_table[minor].dev->port->irq == PARPORT_IRQ_NONE;
2288c2ecf20Sopenharmony_ci	if (polling)
2298c2ecf20Sopenharmony_ci		lp_release_parport(&lp_table[minor]);
2308c2ecf20Sopenharmony_ci	prepare_to_wait(&lp_table[minor].waitq, &wait, TASK_INTERRUPTIBLE);
2318c2ecf20Sopenharmony_ci	schedule_timeout(LP_TIMEOUT_POLLED);
2328c2ecf20Sopenharmony_ci	finish_wait(&lp_table[minor].waitq, &wait);
2338c2ecf20Sopenharmony_ci	if (polling)
2348c2ecf20Sopenharmony_ci		lp_claim_parport_or_block(&lp_table[minor]);
2358c2ecf20Sopenharmony_ci	else
2368c2ecf20Sopenharmony_ci		parport_yield_blocking(lp_table[minor].dev);
2378c2ecf20Sopenharmony_ci}
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_cistatic int lp_check_status(int minor)
2408c2ecf20Sopenharmony_ci{
2418c2ecf20Sopenharmony_ci	int error = 0;
2428c2ecf20Sopenharmony_ci	unsigned int last = lp_table[minor].last_error;
2438c2ecf20Sopenharmony_ci	unsigned char status = r_str(minor);
2448c2ecf20Sopenharmony_ci	if ((status & LP_PERRORP) && !(LP_F(minor) & LP_CAREFUL))
2458c2ecf20Sopenharmony_ci		/* No error. */
2468c2ecf20Sopenharmony_ci		last = 0;
2478c2ecf20Sopenharmony_ci	else if ((status & LP_POUTPA)) {
2488c2ecf20Sopenharmony_ci		if (last != LP_POUTPA) {
2498c2ecf20Sopenharmony_ci			last = LP_POUTPA;
2508c2ecf20Sopenharmony_ci			printk(KERN_INFO "lp%d out of paper\n", minor);
2518c2ecf20Sopenharmony_ci		}
2528c2ecf20Sopenharmony_ci		error = -ENOSPC;
2538c2ecf20Sopenharmony_ci	} else if (!(status & LP_PSELECD)) {
2548c2ecf20Sopenharmony_ci		if (last != LP_PSELECD) {
2558c2ecf20Sopenharmony_ci			last = LP_PSELECD;
2568c2ecf20Sopenharmony_ci			printk(KERN_INFO "lp%d off-line\n", minor);
2578c2ecf20Sopenharmony_ci		}
2588c2ecf20Sopenharmony_ci		error = -EIO;
2598c2ecf20Sopenharmony_ci	} else if (!(status & LP_PERRORP)) {
2608c2ecf20Sopenharmony_ci		if (last != LP_PERRORP) {
2618c2ecf20Sopenharmony_ci			last = LP_PERRORP;
2628c2ecf20Sopenharmony_ci			printk(KERN_INFO "lp%d on fire\n", minor);
2638c2ecf20Sopenharmony_ci		}
2648c2ecf20Sopenharmony_ci		error = -EIO;
2658c2ecf20Sopenharmony_ci	} else {
2668c2ecf20Sopenharmony_ci		last = 0; /* Come here if LP_CAREFUL is set and no
2678c2ecf20Sopenharmony_ci			     errors are reported. */
2688c2ecf20Sopenharmony_ci	}
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci	lp_table[minor].last_error = last;
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci	if (last != 0)
2738c2ecf20Sopenharmony_ci		lp_error(minor);
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci	return error;
2768c2ecf20Sopenharmony_ci}
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_cistatic int lp_wait_ready(int minor, int nonblock)
2798c2ecf20Sopenharmony_ci{
2808c2ecf20Sopenharmony_ci	int error = 0;
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci	/* If we're not in compatibility mode, we're ready now! */
2838c2ecf20Sopenharmony_ci	if (lp_table[minor].current_mode != IEEE1284_MODE_COMPAT) {
2848c2ecf20Sopenharmony_ci		return 0;
2858c2ecf20Sopenharmony_ci	}
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci	do {
2888c2ecf20Sopenharmony_ci		error = lp_check_status(minor);
2898c2ecf20Sopenharmony_ci		if (error && (nonblock || (LP_F(minor) & LP_ABORT)))
2908c2ecf20Sopenharmony_ci			break;
2918c2ecf20Sopenharmony_ci		if (signal_pending(current)) {
2928c2ecf20Sopenharmony_ci			error = -EINTR;
2938c2ecf20Sopenharmony_ci			break;
2948c2ecf20Sopenharmony_ci		}
2958c2ecf20Sopenharmony_ci	} while (error);
2968c2ecf20Sopenharmony_ci	return error;
2978c2ecf20Sopenharmony_ci}
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_cistatic ssize_t lp_write(struct file *file, const char __user *buf,
3008c2ecf20Sopenharmony_ci			size_t count, loff_t *ppos)
3018c2ecf20Sopenharmony_ci{
3028c2ecf20Sopenharmony_ci	unsigned int minor = iminor(file_inode(file));
3038c2ecf20Sopenharmony_ci	struct parport *port = lp_table[minor].dev->port;
3048c2ecf20Sopenharmony_ci	char *kbuf = lp_table[minor].lp_buffer;
3058c2ecf20Sopenharmony_ci	ssize_t retv = 0;
3068c2ecf20Sopenharmony_ci	ssize_t written;
3078c2ecf20Sopenharmony_ci	size_t copy_size = count;
3088c2ecf20Sopenharmony_ci	int nonblock = ((file->f_flags & O_NONBLOCK) ||
3098c2ecf20Sopenharmony_ci			(LP_F(minor) & LP_ABORT));
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci#ifdef LP_STATS
3128c2ecf20Sopenharmony_ci	if (time_after(jiffies, lp_table[minor].lastcall + LP_TIME(minor)))
3138c2ecf20Sopenharmony_ci		lp_table[minor].runchars = 0;
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci	lp_table[minor].lastcall = jiffies;
3168c2ecf20Sopenharmony_ci#endif
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	/* Need to copy the data from user-space. */
3198c2ecf20Sopenharmony_ci	if (copy_size > LP_BUFFER_SIZE)
3208c2ecf20Sopenharmony_ci		copy_size = LP_BUFFER_SIZE;
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci	if (mutex_lock_interruptible(&lp_table[minor].port_mutex))
3238c2ecf20Sopenharmony_ci		return -EINTR;
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci	if (copy_from_user(kbuf, buf, copy_size)) {
3268c2ecf20Sopenharmony_ci		retv = -EFAULT;
3278c2ecf20Sopenharmony_ci		goto out_unlock;
3288c2ecf20Sopenharmony_ci	}
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci	/* Claim Parport or sleep until it becomes available
3318c2ecf20Sopenharmony_ci	 */
3328c2ecf20Sopenharmony_ci	lp_claim_parport_or_block(&lp_table[minor]);
3338c2ecf20Sopenharmony_ci	/* Go to the proper mode. */
3348c2ecf20Sopenharmony_ci	lp_table[minor].current_mode = lp_negotiate(port,
3358c2ecf20Sopenharmony_ci						    lp_table[minor].best_mode);
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci	parport_set_timeout(lp_table[minor].dev,
3388c2ecf20Sopenharmony_ci			    (nonblock ? PARPORT_INACTIVITY_O_NONBLOCK
3398c2ecf20Sopenharmony_ci			     : lp_table[minor].timeout));
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci	if ((retv = lp_wait_ready(minor, nonblock)) == 0)
3428c2ecf20Sopenharmony_ci	do {
3438c2ecf20Sopenharmony_ci		/* Write the data. */
3448c2ecf20Sopenharmony_ci		written = parport_write(port, kbuf, copy_size);
3458c2ecf20Sopenharmony_ci		if (written > 0) {
3468c2ecf20Sopenharmony_ci			copy_size -= written;
3478c2ecf20Sopenharmony_ci			count -= written;
3488c2ecf20Sopenharmony_ci			buf  += written;
3498c2ecf20Sopenharmony_ci			retv += written;
3508c2ecf20Sopenharmony_ci		}
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci		if (signal_pending(current)) {
3538c2ecf20Sopenharmony_ci			if (retv == 0)
3548c2ecf20Sopenharmony_ci				retv = -EINTR;
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci			break;
3578c2ecf20Sopenharmony_ci		}
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci		if (copy_size > 0) {
3608c2ecf20Sopenharmony_ci			/* incomplete write -> check error ! */
3618c2ecf20Sopenharmony_ci			int error;
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_ci			parport_negotiate(lp_table[minor].dev->port,
3648c2ecf20Sopenharmony_ci					  IEEE1284_MODE_COMPAT);
3658c2ecf20Sopenharmony_ci			lp_table[minor].current_mode = IEEE1284_MODE_COMPAT;
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci			error = lp_wait_ready(minor, nonblock);
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_ci			if (error) {
3708c2ecf20Sopenharmony_ci				if (retv == 0)
3718c2ecf20Sopenharmony_ci					retv = error;
3728c2ecf20Sopenharmony_ci				break;
3738c2ecf20Sopenharmony_ci			} else if (nonblock) {
3748c2ecf20Sopenharmony_ci				if (retv == 0)
3758c2ecf20Sopenharmony_ci					retv = -EAGAIN;
3768c2ecf20Sopenharmony_ci				break;
3778c2ecf20Sopenharmony_ci			}
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci			parport_yield_blocking(lp_table[minor].dev);
3808c2ecf20Sopenharmony_ci			lp_table[minor].current_mode
3818c2ecf20Sopenharmony_ci			  = lp_negotiate(port,
3828c2ecf20Sopenharmony_ci					 lp_table[minor].best_mode);
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci		} else if (need_resched())
3858c2ecf20Sopenharmony_ci			schedule();
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_ci		if (count) {
3888c2ecf20Sopenharmony_ci			copy_size = count;
3898c2ecf20Sopenharmony_ci			if (copy_size > LP_BUFFER_SIZE)
3908c2ecf20Sopenharmony_ci				copy_size = LP_BUFFER_SIZE;
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ci			if (copy_from_user(kbuf, buf, copy_size)) {
3938c2ecf20Sopenharmony_ci				if (retv == 0)
3948c2ecf20Sopenharmony_ci					retv = -EFAULT;
3958c2ecf20Sopenharmony_ci				break;
3968c2ecf20Sopenharmony_ci			}
3978c2ecf20Sopenharmony_ci		}
3988c2ecf20Sopenharmony_ci	} while (count > 0);
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci	if (test_and_clear_bit(LP_PREEMPT_REQUEST,
4018c2ecf20Sopenharmony_ci			       &lp_table[minor].bits)) {
4028c2ecf20Sopenharmony_ci		printk(KERN_INFO "lp%d releasing parport\n", minor);
4038c2ecf20Sopenharmony_ci		parport_negotiate(lp_table[minor].dev->port,
4048c2ecf20Sopenharmony_ci				  IEEE1284_MODE_COMPAT);
4058c2ecf20Sopenharmony_ci		lp_table[minor].current_mode = IEEE1284_MODE_COMPAT;
4068c2ecf20Sopenharmony_ci		lp_release_parport(&lp_table[minor]);
4078c2ecf20Sopenharmony_ci	}
4088c2ecf20Sopenharmony_ciout_unlock:
4098c2ecf20Sopenharmony_ci	mutex_unlock(&lp_table[minor].port_mutex);
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci	return retv;
4128c2ecf20Sopenharmony_ci}
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci#ifdef CONFIG_PARPORT_1284
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci/* Status readback conforming to ieee1284 */
4178c2ecf20Sopenharmony_cistatic ssize_t lp_read(struct file *file, char __user *buf,
4188c2ecf20Sopenharmony_ci		       size_t count, loff_t *ppos)
4198c2ecf20Sopenharmony_ci{
4208c2ecf20Sopenharmony_ci	DEFINE_WAIT(wait);
4218c2ecf20Sopenharmony_ci	unsigned int minor=iminor(file_inode(file));
4228c2ecf20Sopenharmony_ci	struct parport *port = lp_table[minor].dev->port;
4238c2ecf20Sopenharmony_ci	ssize_t retval = 0;
4248c2ecf20Sopenharmony_ci	char *kbuf = lp_table[minor].lp_buffer;
4258c2ecf20Sopenharmony_ci	int nonblock = ((file->f_flags & O_NONBLOCK) ||
4268c2ecf20Sopenharmony_ci			(LP_F(minor) & LP_ABORT));
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ci	if (count > LP_BUFFER_SIZE)
4298c2ecf20Sopenharmony_ci		count = LP_BUFFER_SIZE;
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci	if (mutex_lock_interruptible(&lp_table[minor].port_mutex))
4328c2ecf20Sopenharmony_ci		return -EINTR;
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_ci	lp_claim_parport_or_block(&lp_table[minor]);
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ci	parport_set_timeout(lp_table[minor].dev,
4378c2ecf20Sopenharmony_ci			    (nonblock ? PARPORT_INACTIVITY_O_NONBLOCK
4388c2ecf20Sopenharmony_ci			     : lp_table[minor].timeout));
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_ci	parport_negotiate(lp_table[minor].dev->port, IEEE1284_MODE_COMPAT);
4418c2ecf20Sopenharmony_ci	if (parport_negotiate(lp_table[minor].dev->port,
4428c2ecf20Sopenharmony_ci			      IEEE1284_MODE_NIBBLE)) {
4438c2ecf20Sopenharmony_ci		retval = -EIO;
4448c2ecf20Sopenharmony_ci		goto out;
4458c2ecf20Sopenharmony_ci	}
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_ci	while (retval == 0) {
4488c2ecf20Sopenharmony_ci		retval = parport_read(port, kbuf, count);
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci		if (retval > 0)
4518c2ecf20Sopenharmony_ci			break;
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci		if (nonblock) {
4548c2ecf20Sopenharmony_ci			retval = -EAGAIN;
4558c2ecf20Sopenharmony_ci			break;
4568c2ecf20Sopenharmony_ci		}
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_ci		/* Wait for data. */
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci		if (lp_table[minor].dev->port->irq == PARPORT_IRQ_NONE) {
4618c2ecf20Sopenharmony_ci			parport_negotiate(lp_table[minor].dev->port,
4628c2ecf20Sopenharmony_ci					  IEEE1284_MODE_COMPAT);
4638c2ecf20Sopenharmony_ci			lp_error(minor);
4648c2ecf20Sopenharmony_ci			if (parport_negotiate(lp_table[minor].dev->port,
4658c2ecf20Sopenharmony_ci					      IEEE1284_MODE_NIBBLE)) {
4668c2ecf20Sopenharmony_ci				retval = -EIO;
4678c2ecf20Sopenharmony_ci				goto out;
4688c2ecf20Sopenharmony_ci			}
4698c2ecf20Sopenharmony_ci		} else {
4708c2ecf20Sopenharmony_ci			prepare_to_wait(&lp_table[minor].waitq, &wait, TASK_INTERRUPTIBLE);
4718c2ecf20Sopenharmony_ci			schedule_timeout(LP_TIMEOUT_POLLED);
4728c2ecf20Sopenharmony_ci			finish_wait(&lp_table[minor].waitq, &wait);
4738c2ecf20Sopenharmony_ci		}
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ci		if (signal_pending(current)) {
4768c2ecf20Sopenharmony_ci			retval = -ERESTARTSYS;
4778c2ecf20Sopenharmony_ci			break;
4788c2ecf20Sopenharmony_ci		}
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_ci		cond_resched();
4818c2ecf20Sopenharmony_ci	}
4828c2ecf20Sopenharmony_ci	parport_negotiate(lp_table[minor].dev->port, IEEE1284_MODE_COMPAT);
4838c2ecf20Sopenharmony_ci out:
4848c2ecf20Sopenharmony_ci	lp_release_parport(&lp_table[minor]);
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ci	if (retval > 0 && copy_to_user(buf, kbuf, retval))
4878c2ecf20Sopenharmony_ci		retval = -EFAULT;
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_ci	mutex_unlock(&lp_table[minor].port_mutex);
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci	return retval;
4928c2ecf20Sopenharmony_ci}
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_ci#endif /* IEEE 1284 support */
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_cistatic int lp_open(struct inode *inode, struct file *file)
4978c2ecf20Sopenharmony_ci{
4988c2ecf20Sopenharmony_ci	unsigned int minor = iminor(inode);
4998c2ecf20Sopenharmony_ci	int ret = 0;
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_ci	mutex_lock(&lp_mutex);
5028c2ecf20Sopenharmony_ci	if (minor >= LP_NO) {
5038c2ecf20Sopenharmony_ci		ret = -ENXIO;
5048c2ecf20Sopenharmony_ci		goto out;
5058c2ecf20Sopenharmony_ci	}
5068c2ecf20Sopenharmony_ci	if ((LP_F(minor) & LP_EXIST) == 0) {
5078c2ecf20Sopenharmony_ci		ret = -ENXIO;
5088c2ecf20Sopenharmony_ci		goto out;
5098c2ecf20Sopenharmony_ci	}
5108c2ecf20Sopenharmony_ci	if (test_and_set_bit(LP_BUSY_BIT_POS, &LP_F(minor))) {
5118c2ecf20Sopenharmony_ci		ret = -EBUSY;
5128c2ecf20Sopenharmony_ci		goto out;
5138c2ecf20Sopenharmony_ci	}
5148c2ecf20Sopenharmony_ci	/* If ABORTOPEN is set and the printer is offline or out of paper,
5158c2ecf20Sopenharmony_ci	   we may still want to open it to perform ioctl()s.  Therefore we
5168c2ecf20Sopenharmony_ci	   have commandeered O_NONBLOCK, even though it is being used in
5178c2ecf20Sopenharmony_ci	   a non-standard manner.  This is strictly a Linux hack, and
5188c2ecf20Sopenharmony_ci	   should most likely only ever be used by the tunelp application. */
5198c2ecf20Sopenharmony_ci	if ((LP_F(minor) & LP_ABORTOPEN) && !(file->f_flags & O_NONBLOCK)) {
5208c2ecf20Sopenharmony_ci		int status;
5218c2ecf20Sopenharmony_ci		lp_claim_parport_or_block(&lp_table[minor]);
5228c2ecf20Sopenharmony_ci		status = r_str(minor);
5238c2ecf20Sopenharmony_ci		lp_release_parport(&lp_table[minor]);
5248c2ecf20Sopenharmony_ci		if (status & LP_POUTPA) {
5258c2ecf20Sopenharmony_ci			printk(KERN_INFO "lp%d out of paper\n", minor);
5268c2ecf20Sopenharmony_ci			LP_F(minor) &= ~LP_BUSY;
5278c2ecf20Sopenharmony_ci			ret = -ENOSPC;
5288c2ecf20Sopenharmony_ci			goto out;
5298c2ecf20Sopenharmony_ci		} else if (!(status & LP_PSELECD)) {
5308c2ecf20Sopenharmony_ci			printk(KERN_INFO "lp%d off-line\n", minor);
5318c2ecf20Sopenharmony_ci			LP_F(minor) &= ~LP_BUSY;
5328c2ecf20Sopenharmony_ci			ret = -EIO;
5338c2ecf20Sopenharmony_ci			goto out;
5348c2ecf20Sopenharmony_ci		} else if (!(status & LP_PERRORP)) {
5358c2ecf20Sopenharmony_ci			printk(KERN_ERR "lp%d printer error\n", minor);
5368c2ecf20Sopenharmony_ci			LP_F(minor) &= ~LP_BUSY;
5378c2ecf20Sopenharmony_ci			ret = -EIO;
5388c2ecf20Sopenharmony_ci			goto out;
5398c2ecf20Sopenharmony_ci		}
5408c2ecf20Sopenharmony_ci	}
5418c2ecf20Sopenharmony_ci	lp_table[minor].lp_buffer = kmalloc(LP_BUFFER_SIZE, GFP_KERNEL);
5428c2ecf20Sopenharmony_ci	if (!lp_table[minor].lp_buffer) {
5438c2ecf20Sopenharmony_ci		LP_F(minor) &= ~LP_BUSY;
5448c2ecf20Sopenharmony_ci		ret = -ENOMEM;
5458c2ecf20Sopenharmony_ci		goto out;
5468c2ecf20Sopenharmony_ci	}
5478c2ecf20Sopenharmony_ci	/* Determine if the peripheral supports ECP mode */
5488c2ecf20Sopenharmony_ci	lp_claim_parport_or_block(&lp_table[minor]);
5498c2ecf20Sopenharmony_ci	if ( (lp_table[minor].dev->port->modes & PARPORT_MODE_ECP) &&
5508c2ecf20Sopenharmony_ci	     !parport_negotiate(lp_table[minor].dev->port,
5518c2ecf20Sopenharmony_ci				 IEEE1284_MODE_ECP)) {
5528c2ecf20Sopenharmony_ci		printk(KERN_INFO "lp%d: ECP mode\n", minor);
5538c2ecf20Sopenharmony_ci		lp_table[minor].best_mode = IEEE1284_MODE_ECP;
5548c2ecf20Sopenharmony_ci	} else {
5558c2ecf20Sopenharmony_ci		lp_table[minor].best_mode = IEEE1284_MODE_COMPAT;
5568c2ecf20Sopenharmony_ci	}
5578c2ecf20Sopenharmony_ci	/* Leave peripheral in compatibility mode */
5588c2ecf20Sopenharmony_ci	parport_negotiate(lp_table[minor].dev->port, IEEE1284_MODE_COMPAT);
5598c2ecf20Sopenharmony_ci	lp_release_parport(&lp_table[minor]);
5608c2ecf20Sopenharmony_ci	lp_table[minor].current_mode = IEEE1284_MODE_COMPAT;
5618c2ecf20Sopenharmony_ciout:
5628c2ecf20Sopenharmony_ci	mutex_unlock(&lp_mutex);
5638c2ecf20Sopenharmony_ci	return ret;
5648c2ecf20Sopenharmony_ci}
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_cistatic int lp_release(struct inode *inode, struct file *file)
5678c2ecf20Sopenharmony_ci{
5688c2ecf20Sopenharmony_ci	unsigned int minor = iminor(inode);
5698c2ecf20Sopenharmony_ci
5708c2ecf20Sopenharmony_ci	lp_claim_parport_or_block(&lp_table[minor]);
5718c2ecf20Sopenharmony_ci	parport_negotiate(lp_table[minor].dev->port, IEEE1284_MODE_COMPAT);
5728c2ecf20Sopenharmony_ci	lp_table[minor].current_mode = IEEE1284_MODE_COMPAT;
5738c2ecf20Sopenharmony_ci	lp_release_parport(&lp_table[minor]);
5748c2ecf20Sopenharmony_ci	kfree(lp_table[minor].lp_buffer);
5758c2ecf20Sopenharmony_ci	lp_table[minor].lp_buffer = NULL;
5768c2ecf20Sopenharmony_ci	LP_F(minor) &= ~LP_BUSY;
5778c2ecf20Sopenharmony_ci	return 0;
5788c2ecf20Sopenharmony_ci}
5798c2ecf20Sopenharmony_ci
5808c2ecf20Sopenharmony_cistatic int lp_do_ioctl(unsigned int minor, unsigned int cmd,
5818c2ecf20Sopenharmony_ci	unsigned long arg, void __user *argp)
5828c2ecf20Sopenharmony_ci{
5838c2ecf20Sopenharmony_ci	int status;
5848c2ecf20Sopenharmony_ci	int retval = 0;
5858c2ecf20Sopenharmony_ci
5868c2ecf20Sopenharmony_ci#ifdef LP_DEBUG
5878c2ecf20Sopenharmony_ci	printk(KERN_DEBUG "lp%d ioctl, cmd: 0x%x, arg: 0x%lx\n", minor, cmd, arg);
5888c2ecf20Sopenharmony_ci#endif
5898c2ecf20Sopenharmony_ci	if (minor >= LP_NO)
5908c2ecf20Sopenharmony_ci		return -ENODEV;
5918c2ecf20Sopenharmony_ci	if ((LP_F(minor) & LP_EXIST) == 0)
5928c2ecf20Sopenharmony_ci		return -ENODEV;
5938c2ecf20Sopenharmony_ci	switch ( cmd ) {
5948c2ecf20Sopenharmony_ci		case LPTIME:
5958c2ecf20Sopenharmony_ci			if (arg > UINT_MAX / HZ)
5968c2ecf20Sopenharmony_ci				return -EINVAL;
5978c2ecf20Sopenharmony_ci			LP_TIME(minor) = arg * HZ/100;
5988c2ecf20Sopenharmony_ci			break;
5998c2ecf20Sopenharmony_ci		case LPCHAR:
6008c2ecf20Sopenharmony_ci			LP_CHAR(minor) = arg;
6018c2ecf20Sopenharmony_ci			break;
6028c2ecf20Sopenharmony_ci		case LPABORT:
6038c2ecf20Sopenharmony_ci			if (arg)
6048c2ecf20Sopenharmony_ci				LP_F(minor) |= LP_ABORT;
6058c2ecf20Sopenharmony_ci			else
6068c2ecf20Sopenharmony_ci				LP_F(minor) &= ~LP_ABORT;
6078c2ecf20Sopenharmony_ci			break;
6088c2ecf20Sopenharmony_ci		case LPABORTOPEN:
6098c2ecf20Sopenharmony_ci			if (arg)
6108c2ecf20Sopenharmony_ci				LP_F(minor) |= LP_ABORTOPEN;
6118c2ecf20Sopenharmony_ci			else
6128c2ecf20Sopenharmony_ci				LP_F(minor) &= ~LP_ABORTOPEN;
6138c2ecf20Sopenharmony_ci			break;
6148c2ecf20Sopenharmony_ci		case LPCAREFUL:
6158c2ecf20Sopenharmony_ci			if (arg)
6168c2ecf20Sopenharmony_ci				LP_F(minor) |= LP_CAREFUL;
6178c2ecf20Sopenharmony_ci			else
6188c2ecf20Sopenharmony_ci				LP_F(minor) &= ~LP_CAREFUL;
6198c2ecf20Sopenharmony_ci			break;
6208c2ecf20Sopenharmony_ci		case LPWAIT:
6218c2ecf20Sopenharmony_ci			LP_WAIT(minor) = arg;
6228c2ecf20Sopenharmony_ci			break;
6238c2ecf20Sopenharmony_ci		case LPSETIRQ:
6248c2ecf20Sopenharmony_ci			return -EINVAL;
6258c2ecf20Sopenharmony_ci			break;
6268c2ecf20Sopenharmony_ci		case LPGETIRQ:
6278c2ecf20Sopenharmony_ci			if (copy_to_user(argp, &LP_IRQ(minor),
6288c2ecf20Sopenharmony_ci					sizeof(int)))
6298c2ecf20Sopenharmony_ci				return -EFAULT;
6308c2ecf20Sopenharmony_ci			break;
6318c2ecf20Sopenharmony_ci		case LPGETSTATUS:
6328c2ecf20Sopenharmony_ci			if (mutex_lock_interruptible(&lp_table[minor].port_mutex))
6338c2ecf20Sopenharmony_ci				return -EINTR;
6348c2ecf20Sopenharmony_ci			lp_claim_parport_or_block(&lp_table[minor]);
6358c2ecf20Sopenharmony_ci			status = r_str(minor);
6368c2ecf20Sopenharmony_ci			lp_release_parport(&lp_table[minor]);
6378c2ecf20Sopenharmony_ci			mutex_unlock(&lp_table[minor].port_mutex);
6388c2ecf20Sopenharmony_ci
6398c2ecf20Sopenharmony_ci			if (copy_to_user(argp, &status, sizeof(int)))
6408c2ecf20Sopenharmony_ci				return -EFAULT;
6418c2ecf20Sopenharmony_ci			break;
6428c2ecf20Sopenharmony_ci		case LPRESET:
6438c2ecf20Sopenharmony_ci			lp_reset(minor);
6448c2ecf20Sopenharmony_ci			break;
6458c2ecf20Sopenharmony_ci#ifdef LP_STATS
6468c2ecf20Sopenharmony_ci		case LPGETSTATS:
6478c2ecf20Sopenharmony_ci			if (copy_to_user(argp, &LP_STAT(minor),
6488c2ecf20Sopenharmony_ci					sizeof(struct lp_stats)))
6498c2ecf20Sopenharmony_ci				return -EFAULT;
6508c2ecf20Sopenharmony_ci			if (capable(CAP_SYS_ADMIN))
6518c2ecf20Sopenharmony_ci				memset(&LP_STAT(minor), 0,
6528c2ecf20Sopenharmony_ci						sizeof(struct lp_stats));
6538c2ecf20Sopenharmony_ci			break;
6548c2ecf20Sopenharmony_ci#endif
6558c2ecf20Sopenharmony_ci		case LPGETFLAGS:
6568c2ecf20Sopenharmony_ci			status = LP_F(minor);
6578c2ecf20Sopenharmony_ci			if (copy_to_user(argp, &status, sizeof(int)))
6588c2ecf20Sopenharmony_ci				return -EFAULT;
6598c2ecf20Sopenharmony_ci			break;
6608c2ecf20Sopenharmony_ci
6618c2ecf20Sopenharmony_ci		default:
6628c2ecf20Sopenharmony_ci			retval = -EINVAL;
6638c2ecf20Sopenharmony_ci	}
6648c2ecf20Sopenharmony_ci	return retval;
6658c2ecf20Sopenharmony_ci}
6668c2ecf20Sopenharmony_ci
6678c2ecf20Sopenharmony_cistatic int lp_set_timeout(unsigned int minor, s64 tv_sec, long tv_usec)
6688c2ecf20Sopenharmony_ci{
6698c2ecf20Sopenharmony_ci	long to_jiffies;
6708c2ecf20Sopenharmony_ci
6718c2ecf20Sopenharmony_ci	/* Convert to jiffies, place in lp_table */
6728c2ecf20Sopenharmony_ci	if (tv_sec < 0 || tv_usec < 0)
6738c2ecf20Sopenharmony_ci		return -EINVAL;
6748c2ecf20Sopenharmony_ci
6758c2ecf20Sopenharmony_ci	/*
6768c2ecf20Sopenharmony_ci	 * we used to not check, so let's not make this fatal,
6778c2ecf20Sopenharmony_ci	 * but deal with user space passing a 32-bit tv_nsec in
6788c2ecf20Sopenharmony_ci	 * a 64-bit field, capping the timeout to 1 second
6798c2ecf20Sopenharmony_ci	 * worth of microseconds, and capping the total at
6808c2ecf20Sopenharmony_ci	 * MAX_JIFFY_OFFSET.
6818c2ecf20Sopenharmony_ci	 */
6828c2ecf20Sopenharmony_ci	if (tv_usec > 999999)
6838c2ecf20Sopenharmony_ci		tv_usec = 999999;
6848c2ecf20Sopenharmony_ci
6858c2ecf20Sopenharmony_ci	if (tv_sec >= MAX_SEC_IN_JIFFIES - 1) {
6868c2ecf20Sopenharmony_ci		to_jiffies = MAX_JIFFY_OFFSET;
6878c2ecf20Sopenharmony_ci	} else {
6888c2ecf20Sopenharmony_ci		to_jiffies = DIV_ROUND_UP(tv_usec, 1000000/HZ);
6898c2ecf20Sopenharmony_ci		to_jiffies += tv_sec * (long) HZ;
6908c2ecf20Sopenharmony_ci	}
6918c2ecf20Sopenharmony_ci
6928c2ecf20Sopenharmony_ci	if (to_jiffies <= 0) {
6938c2ecf20Sopenharmony_ci		return -EINVAL;
6948c2ecf20Sopenharmony_ci	}
6958c2ecf20Sopenharmony_ci	lp_table[minor].timeout = to_jiffies;
6968c2ecf20Sopenharmony_ci	return 0;
6978c2ecf20Sopenharmony_ci}
6988c2ecf20Sopenharmony_ci
6998c2ecf20Sopenharmony_cistatic int lp_set_timeout32(unsigned int minor, void __user *arg)
7008c2ecf20Sopenharmony_ci{
7018c2ecf20Sopenharmony_ci	s32 karg[2];
7028c2ecf20Sopenharmony_ci
7038c2ecf20Sopenharmony_ci	if (copy_from_user(karg, arg, sizeof(karg)))
7048c2ecf20Sopenharmony_ci		return -EFAULT;
7058c2ecf20Sopenharmony_ci
7068c2ecf20Sopenharmony_ci	return lp_set_timeout(minor, karg[0], karg[1]);
7078c2ecf20Sopenharmony_ci}
7088c2ecf20Sopenharmony_ci
7098c2ecf20Sopenharmony_cistatic int lp_set_timeout64(unsigned int minor, void __user *arg)
7108c2ecf20Sopenharmony_ci{
7118c2ecf20Sopenharmony_ci	s64 karg[2];
7128c2ecf20Sopenharmony_ci
7138c2ecf20Sopenharmony_ci	if (copy_from_user(karg, arg, sizeof(karg)))
7148c2ecf20Sopenharmony_ci		return -EFAULT;
7158c2ecf20Sopenharmony_ci
7168c2ecf20Sopenharmony_ci	/* sparc64 suseconds_t is 32-bit only */
7178c2ecf20Sopenharmony_ci	if (IS_ENABLED(CONFIG_SPARC64) && !in_compat_syscall())
7188c2ecf20Sopenharmony_ci		karg[1] >>= 32;
7198c2ecf20Sopenharmony_ci
7208c2ecf20Sopenharmony_ci	return lp_set_timeout(minor, karg[0], karg[1]);
7218c2ecf20Sopenharmony_ci}
7228c2ecf20Sopenharmony_ci
7238c2ecf20Sopenharmony_cistatic long lp_ioctl(struct file *file, unsigned int cmd,
7248c2ecf20Sopenharmony_ci			unsigned long arg)
7258c2ecf20Sopenharmony_ci{
7268c2ecf20Sopenharmony_ci	unsigned int minor;
7278c2ecf20Sopenharmony_ci	int ret;
7288c2ecf20Sopenharmony_ci
7298c2ecf20Sopenharmony_ci	minor = iminor(file_inode(file));
7308c2ecf20Sopenharmony_ci	mutex_lock(&lp_mutex);
7318c2ecf20Sopenharmony_ci	switch (cmd) {
7328c2ecf20Sopenharmony_ci	case LPSETTIMEOUT_OLD:
7338c2ecf20Sopenharmony_ci		if (BITS_PER_LONG == 32) {
7348c2ecf20Sopenharmony_ci			ret = lp_set_timeout32(minor, (void __user *)arg);
7358c2ecf20Sopenharmony_ci			break;
7368c2ecf20Sopenharmony_ci		}
7378c2ecf20Sopenharmony_ci		fallthrough;	/* for 64-bit */
7388c2ecf20Sopenharmony_ci	case LPSETTIMEOUT_NEW:
7398c2ecf20Sopenharmony_ci		ret = lp_set_timeout64(minor, (void __user *)arg);
7408c2ecf20Sopenharmony_ci		break;
7418c2ecf20Sopenharmony_ci	default:
7428c2ecf20Sopenharmony_ci		ret = lp_do_ioctl(minor, cmd, arg, (void __user *)arg);
7438c2ecf20Sopenharmony_ci		break;
7448c2ecf20Sopenharmony_ci	}
7458c2ecf20Sopenharmony_ci	mutex_unlock(&lp_mutex);
7468c2ecf20Sopenharmony_ci
7478c2ecf20Sopenharmony_ci	return ret;
7488c2ecf20Sopenharmony_ci}
7498c2ecf20Sopenharmony_ci
7508c2ecf20Sopenharmony_ci#ifdef CONFIG_COMPAT
7518c2ecf20Sopenharmony_cistatic long lp_compat_ioctl(struct file *file, unsigned int cmd,
7528c2ecf20Sopenharmony_ci			unsigned long arg)
7538c2ecf20Sopenharmony_ci{
7548c2ecf20Sopenharmony_ci	unsigned int minor;
7558c2ecf20Sopenharmony_ci	int ret;
7568c2ecf20Sopenharmony_ci
7578c2ecf20Sopenharmony_ci	minor = iminor(file_inode(file));
7588c2ecf20Sopenharmony_ci	mutex_lock(&lp_mutex);
7598c2ecf20Sopenharmony_ci	switch (cmd) {
7608c2ecf20Sopenharmony_ci	case LPSETTIMEOUT_OLD:
7618c2ecf20Sopenharmony_ci		if (!COMPAT_USE_64BIT_TIME) {
7628c2ecf20Sopenharmony_ci			ret = lp_set_timeout32(minor, (void __user *)arg);
7638c2ecf20Sopenharmony_ci			break;
7648c2ecf20Sopenharmony_ci		}
7658c2ecf20Sopenharmony_ci		fallthrough;	/* for x32 mode */
7668c2ecf20Sopenharmony_ci	case LPSETTIMEOUT_NEW:
7678c2ecf20Sopenharmony_ci		ret = lp_set_timeout64(minor, (void __user *)arg);
7688c2ecf20Sopenharmony_ci		break;
7698c2ecf20Sopenharmony_ci#ifdef LP_STATS
7708c2ecf20Sopenharmony_ci	case LPGETSTATS:
7718c2ecf20Sopenharmony_ci		/* FIXME: add an implementation if you set LP_STATS */
7728c2ecf20Sopenharmony_ci		ret = -EINVAL;
7738c2ecf20Sopenharmony_ci		break;
7748c2ecf20Sopenharmony_ci#endif
7758c2ecf20Sopenharmony_ci	default:
7768c2ecf20Sopenharmony_ci		ret = lp_do_ioctl(minor, cmd, arg, compat_ptr(arg));
7778c2ecf20Sopenharmony_ci		break;
7788c2ecf20Sopenharmony_ci	}
7798c2ecf20Sopenharmony_ci	mutex_unlock(&lp_mutex);
7808c2ecf20Sopenharmony_ci
7818c2ecf20Sopenharmony_ci	return ret;
7828c2ecf20Sopenharmony_ci}
7838c2ecf20Sopenharmony_ci#endif
7848c2ecf20Sopenharmony_ci
7858c2ecf20Sopenharmony_cistatic const struct file_operations lp_fops = {
7868c2ecf20Sopenharmony_ci	.owner		= THIS_MODULE,
7878c2ecf20Sopenharmony_ci	.write		= lp_write,
7888c2ecf20Sopenharmony_ci	.unlocked_ioctl	= lp_ioctl,
7898c2ecf20Sopenharmony_ci#ifdef CONFIG_COMPAT
7908c2ecf20Sopenharmony_ci	.compat_ioctl	= lp_compat_ioctl,
7918c2ecf20Sopenharmony_ci#endif
7928c2ecf20Sopenharmony_ci	.open		= lp_open,
7938c2ecf20Sopenharmony_ci	.release	= lp_release,
7948c2ecf20Sopenharmony_ci#ifdef CONFIG_PARPORT_1284
7958c2ecf20Sopenharmony_ci	.read		= lp_read,
7968c2ecf20Sopenharmony_ci#endif
7978c2ecf20Sopenharmony_ci	.llseek		= noop_llseek,
7988c2ecf20Sopenharmony_ci};
7998c2ecf20Sopenharmony_ci
8008c2ecf20Sopenharmony_ci/* --- support for console on the line printer ----------------- */
8018c2ecf20Sopenharmony_ci
8028c2ecf20Sopenharmony_ci#ifdef CONFIG_LP_CONSOLE
8038c2ecf20Sopenharmony_ci
8048c2ecf20Sopenharmony_ci#define CONSOLE_LP 0
8058c2ecf20Sopenharmony_ci
8068c2ecf20Sopenharmony_ci/* If the printer is out of paper, we can either lose the messages or
8078c2ecf20Sopenharmony_ci * stall until the printer is happy again.  Define CONSOLE_LP_STRICT
8088c2ecf20Sopenharmony_ci * non-zero to get the latter behaviour. */
8098c2ecf20Sopenharmony_ci#define CONSOLE_LP_STRICT 1
8108c2ecf20Sopenharmony_ci
8118c2ecf20Sopenharmony_ci/* The console must be locked when we get here. */
8128c2ecf20Sopenharmony_ci
8138c2ecf20Sopenharmony_cistatic void lp_console_write(struct console *co, const char *s,
8148c2ecf20Sopenharmony_ci			     unsigned count)
8158c2ecf20Sopenharmony_ci{
8168c2ecf20Sopenharmony_ci	struct pardevice *dev = lp_table[CONSOLE_LP].dev;
8178c2ecf20Sopenharmony_ci	struct parport *port = dev->port;
8188c2ecf20Sopenharmony_ci	ssize_t written;
8198c2ecf20Sopenharmony_ci
8208c2ecf20Sopenharmony_ci	if (parport_claim(dev))
8218c2ecf20Sopenharmony_ci		/* Nothing we can do. */
8228c2ecf20Sopenharmony_ci		return;
8238c2ecf20Sopenharmony_ci
8248c2ecf20Sopenharmony_ci	parport_set_timeout(dev, 0);
8258c2ecf20Sopenharmony_ci
8268c2ecf20Sopenharmony_ci	/* Go to compatibility mode. */
8278c2ecf20Sopenharmony_ci	parport_negotiate(port, IEEE1284_MODE_COMPAT);
8288c2ecf20Sopenharmony_ci
8298c2ecf20Sopenharmony_ci	do {
8308c2ecf20Sopenharmony_ci		/* Write the data, converting LF->CRLF as we go. */
8318c2ecf20Sopenharmony_ci		ssize_t canwrite = count;
8328c2ecf20Sopenharmony_ci		char *lf = memchr(s, '\n', count);
8338c2ecf20Sopenharmony_ci		if (lf)
8348c2ecf20Sopenharmony_ci			canwrite = lf - s;
8358c2ecf20Sopenharmony_ci
8368c2ecf20Sopenharmony_ci		if (canwrite > 0) {
8378c2ecf20Sopenharmony_ci			written = parport_write(port, s, canwrite);
8388c2ecf20Sopenharmony_ci
8398c2ecf20Sopenharmony_ci			if (written <= 0)
8408c2ecf20Sopenharmony_ci				continue;
8418c2ecf20Sopenharmony_ci
8428c2ecf20Sopenharmony_ci			s += written;
8438c2ecf20Sopenharmony_ci			count -= written;
8448c2ecf20Sopenharmony_ci			canwrite -= written;
8458c2ecf20Sopenharmony_ci		}
8468c2ecf20Sopenharmony_ci
8478c2ecf20Sopenharmony_ci		if (lf && canwrite <= 0) {
8488c2ecf20Sopenharmony_ci			const char *crlf = "\r\n";
8498c2ecf20Sopenharmony_ci			int i = 2;
8508c2ecf20Sopenharmony_ci
8518c2ecf20Sopenharmony_ci			/* Dodge the original '\n', and put '\r\n' instead. */
8528c2ecf20Sopenharmony_ci			s++;
8538c2ecf20Sopenharmony_ci			count--;
8548c2ecf20Sopenharmony_ci			do {
8558c2ecf20Sopenharmony_ci				written = parport_write(port, crlf, i);
8568c2ecf20Sopenharmony_ci				if (written > 0) {
8578c2ecf20Sopenharmony_ci					i -= written;
8588c2ecf20Sopenharmony_ci					crlf += written;
8598c2ecf20Sopenharmony_ci				}
8608c2ecf20Sopenharmony_ci			} while (i > 0 && (CONSOLE_LP_STRICT || written > 0));
8618c2ecf20Sopenharmony_ci		}
8628c2ecf20Sopenharmony_ci	} while (count > 0 && (CONSOLE_LP_STRICT || written > 0));
8638c2ecf20Sopenharmony_ci
8648c2ecf20Sopenharmony_ci	parport_release(dev);
8658c2ecf20Sopenharmony_ci}
8668c2ecf20Sopenharmony_ci
8678c2ecf20Sopenharmony_cistatic struct console lpcons = {
8688c2ecf20Sopenharmony_ci	.name		= "lp",
8698c2ecf20Sopenharmony_ci	.write		= lp_console_write,
8708c2ecf20Sopenharmony_ci	.flags		= CON_PRINTBUFFER,
8718c2ecf20Sopenharmony_ci};
8728c2ecf20Sopenharmony_ci
8738c2ecf20Sopenharmony_ci#endif /* console on line printer */
8748c2ecf20Sopenharmony_ci
8758c2ecf20Sopenharmony_ci/* --- initialisation code ------------------------------------- */
8768c2ecf20Sopenharmony_ci
8778c2ecf20Sopenharmony_cistatic int parport_nr[LP_NO] = { [0 ... LP_NO-1] = LP_PARPORT_UNSPEC };
8788c2ecf20Sopenharmony_cistatic char *parport[LP_NO];
8798c2ecf20Sopenharmony_cistatic bool reset;
8808c2ecf20Sopenharmony_ci
8818c2ecf20Sopenharmony_cimodule_param_array(parport, charp, NULL, 0);
8828c2ecf20Sopenharmony_cimodule_param(reset, bool, 0);
8838c2ecf20Sopenharmony_ci
8848c2ecf20Sopenharmony_ci#ifndef MODULE
8858c2ecf20Sopenharmony_cistatic int __init lp_setup(char *str)
8868c2ecf20Sopenharmony_ci{
8878c2ecf20Sopenharmony_ci	static int parport_ptr;
8888c2ecf20Sopenharmony_ci	int x;
8898c2ecf20Sopenharmony_ci
8908c2ecf20Sopenharmony_ci	if (get_option(&str, &x)) {
8918c2ecf20Sopenharmony_ci		if (x == 0) {
8928c2ecf20Sopenharmony_ci			/* disable driver on "lp=" or "lp=0" */
8938c2ecf20Sopenharmony_ci			parport_nr[0] = LP_PARPORT_OFF;
8948c2ecf20Sopenharmony_ci		} else {
8958c2ecf20Sopenharmony_ci			printk(KERN_WARNING "warning: 'lp=0x%x' is deprecated, ignored\n", x);
8968c2ecf20Sopenharmony_ci			return 0;
8978c2ecf20Sopenharmony_ci		}
8988c2ecf20Sopenharmony_ci	} else if (!strncmp(str, "parport", 7)) {
8998c2ecf20Sopenharmony_ci		int n = simple_strtoul(str+7, NULL, 10);
9008c2ecf20Sopenharmony_ci		if (parport_ptr < LP_NO)
9018c2ecf20Sopenharmony_ci			parport_nr[parport_ptr++] = n;
9028c2ecf20Sopenharmony_ci		else
9038c2ecf20Sopenharmony_ci			printk(KERN_INFO "lp: too many ports, %s ignored.\n",
9048c2ecf20Sopenharmony_ci			       str);
9058c2ecf20Sopenharmony_ci	} else if (!strcmp(str, "auto")) {
9068c2ecf20Sopenharmony_ci		parport_nr[0] = LP_PARPORT_AUTO;
9078c2ecf20Sopenharmony_ci	} else if (!strcmp(str, "none")) {
9088c2ecf20Sopenharmony_ci		if (parport_ptr < LP_NO)
9098c2ecf20Sopenharmony_ci			parport_nr[parport_ptr++] = LP_PARPORT_NONE;
9108c2ecf20Sopenharmony_ci		else
9118c2ecf20Sopenharmony_ci			printk(KERN_INFO "lp: too many ports, %s ignored.\n",
9128c2ecf20Sopenharmony_ci			       str);
9138c2ecf20Sopenharmony_ci	} else if (!strcmp(str, "reset")) {
9148c2ecf20Sopenharmony_ci		reset = true;
9158c2ecf20Sopenharmony_ci	}
9168c2ecf20Sopenharmony_ci	return 1;
9178c2ecf20Sopenharmony_ci}
9188c2ecf20Sopenharmony_ci#endif
9198c2ecf20Sopenharmony_ci
9208c2ecf20Sopenharmony_cistatic int lp_register(int nr, struct parport *port)
9218c2ecf20Sopenharmony_ci{
9228c2ecf20Sopenharmony_ci	struct pardev_cb ppdev_cb;
9238c2ecf20Sopenharmony_ci
9248c2ecf20Sopenharmony_ci	memset(&ppdev_cb, 0, sizeof(ppdev_cb));
9258c2ecf20Sopenharmony_ci	ppdev_cb.preempt = lp_preempt;
9268c2ecf20Sopenharmony_ci	ppdev_cb.private = &lp_table[nr];
9278c2ecf20Sopenharmony_ci	lp_table[nr].dev = parport_register_dev_model(port, "lp",
9288c2ecf20Sopenharmony_ci						      &ppdev_cb, nr);
9298c2ecf20Sopenharmony_ci	if (lp_table[nr].dev == NULL)
9308c2ecf20Sopenharmony_ci		return 1;
9318c2ecf20Sopenharmony_ci	lp_table[nr].flags |= LP_EXIST;
9328c2ecf20Sopenharmony_ci
9338c2ecf20Sopenharmony_ci	if (reset)
9348c2ecf20Sopenharmony_ci		lp_reset(nr);
9358c2ecf20Sopenharmony_ci
9368c2ecf20Sopenharmony_ci	device_create(lp_class, port->dev, MKDEV(LP_MAJOR, nr), NULL,
9378c2ecf20Sopenharmony_ci		      "lp%d", nr);
9388c2ecf20Sopenharmony_ci
9398c2ecf20Sopenharmony_ci	printk(KERN_INFO "lp%d: using %s (%s).\n", nr, port->name,
9408c2ecf20Sopenharmony_ci	       (port->irq == PARPORT_IRQ_NONE)?"polling":"interrupt-driven");
9418c2ecf20Sopenharmony_ci
9428c2ecf20Sopenharmony_ci#ifdef CONFIG_LP_CONSOLE
9438c2ecf20Sopenharmony_ci	if (!nr) {
9448c2ecf20Sopenharmony_ci		if (port->modes & PARPORT_MODE_SAFEININT) {
9458c2ecf20Sopenharmony_ci			register_console(&lpcons);
9468c2ecf20Sopenharmony_ci			console_registered = port;
9478c2ecf20Sopenharmony_ci			printk(KERN_INFO "lp%d: console ready\n", CONSOLE_LP);
9488c2ecf20Sopenharmony_ci		} else
9498c2ecf20Sopenharmony_ci			printk(KERN_ERR "lp%d: cannot run console on %s\n",
9508c2ecf20Sopenharmony_ci			       CONSOLE_LP, port->name);
9518c2ecf20Sopenharmony_ci	}
9528c2ecf20Sopenharmony_ci#endif
9538c2ecf20Sopenharmony_ci	port_num[nr] = port->number;
9548c2ecf20Sopenharmony_ci
9558c2ecf20Sopenharmony_ci	return 0;
9568c2ecf20Sopenharmony_ci}
9578c2ecf20Sopenharmony_ci
9588c2ecf20Sopenharmony_cistatic void lp_attach(struct parport *port)
9598c2ecf20Sopenharmony_ci{
9608c2ecf20Sopenharmony_ci	unsigned int i;
9618c2ecf20Sopenharmony_ci
9628c2ecf20Sopenharmony_ci	switch (parport_nr[0]) {
9638c2ecf20Sopenharmony_ci	case LP_PARPORT_UNSPEC:
9648c2ecf20Sopenharmony_ci	case LP_PARPORT_AUTO:
9658c2ecf20Sopenharmony_ci		if (parport_nr[0] == LP_PARPORT_AUTO &&
9668c2ecf20Sopenharmony_ci		    port->probe_info[0].class != PARPORT_CLASS_PRINTER)
9678c2ecf20Sopenharmony_ci			return;
9688c2ecf20Sopenharmony_ci		if (lp_count == LP_NO) {
9698c2ecf20Sopenharmony_ci			printk(KERN_INFO "lp: ignoring parallel port (max. %d)\n",LP_NO);
9708c2ecf20Sopenharmony_ci			return;
9718c2ecf20Sopenharmony_ci		}
9728c2ecf20Sopenharmony_ci		for (i = 0; i < LP_NO; i++)
9738c2ecf20Sopenharmony_ci			if (port_num[i] == -1)
9748c2ecf20Sopenharmony_ci				break;
9758c2ecf20Sopenharmony_ci
9768c2ecf20Sopenharmony_ci		if (!lp_register(i, port))
9778c2ecf20Sopenharmony_ci			lp_count++;
9788c2ecf20Sopenharmony_ci		break;
9798c2ecf20Sopenharmony_ci
9808c2ecf20Sopenharmony_ci	default:
9818c2ecf20Sopenharmony_ci		for (i = 0; i < LP_NO; i++) {
9828c2ecf20Sopenharmony_ci			if (port->number == parport_nr[i]) {
9838c2ecf20Sopenharmony_ci				if (!lp_register(i, port))
9848c2ecf20Sopenharmony_ci					lp_count++;
9858c2ecf20Sopenharmony_ci				break;
9868c2ecf20Sopenharmony_ci			}
9878c2ecf20Sopenharmony_ci		}
9888c2ecf20Sopenharmony_ci		break;
9898c2ecf20Sopenharmony_ci	}
9908c2ecf20Sopenharmony_ci}
9918c2ecf20Sopenharmony_ci
9928c2ecf20Sopenharmony_cistatic void lp_detach(struct parport *port)
9938c2ecf20Sopenharmony_ci{
9948c2ecf20Sopenharmony_ci	int n;
9958c2ecf20Sopenharmony_ci
9968c2ecf20Sopenharmony_ci	/* Write this some day. */
9978c2ecf20Sopenharmony_ci#ifdef CONFIG_LP_CONSOLE
9988c2ecf20Sopenharmony_ci	if (console_registered == port) {
9998c2ecf20Sopenharmony_ci		unregister_console(&lpcons);
10008c2ecf20Sopenharmony_ci		console_registered = NULL;
10018c2ecf20Sopenharmony_ci	}
10028c2ecf20Sopenharmony_ci#endif /* CONFIG_LP_CONSOLE */
10038c2ecf20Sopenharmony_ci
10048c2ecf20Sopenharmony_ci	for (n = 0; n < LP_NO; n++) {
10058c2ecf20Sopenharmony_ci		if (port_num[n] == port->number) {
10068c2ecf20Sopenharmony_ci			port_num[n] = -1;
10078c2ecf20Sopenharmony_ci			lp_count--;
10088c2ecf20Sopenharmony_ci			device_destroy(lp_class, MKDEV(LP_MAJOR, n));
10098c2ecf20Sopenharmony_ci			parport_unregister_device(lp_table[n].dev);
10108c2ecf20Sopenharmony_ci		}
10118c2ecf20Sopenharmony_ci	}
10128c2ecf20Sopenharmony_ci}
10138c2ecf20Sopenharmony_ci
10148c2ecf20Sopenharmony_cistatic struct parport_driver lp_driver = {
10158c2ecf20Sopenharmony_ci	.name = "lp",
10168c2ecf20Sopenharmony_ci	.match_port = lp_attach,
10178c2ecf20Sopenharmony_ci	.detach = lp_detach,
10188c2ecf20Sopenharmony_ci	.devmodel = true,
10198c2ecf20Sopenharmony_ci};
10208c2ecf20Sopenharmony_ci
10218c2ecf20Sopenharmony_cistatic int __init lp_init(void)
10228c2ecf20Sopenharmony_ci{
10238c2ecf20Sopenharmony_ci	int i, err = 0;
10248c2ecf20Sopenharmony_ci
10258c2ecf20Sopenharmony_ci	if (parport_nr[0] == LP_PARPORT_OFF)
10268c2ecf20Sopenharmony_ci		return 0;
10278c2ecf20Sopenharmony_ci
10288c2ecf20Sopenharmony_ci	for (i = 0; i < LP_NO; i++) {
10298c2ecf20Sopenharmony_ci		lp_table[i].dev = NULL;
10308c2ecf20Sopenharmony_ci		lp_table[i].flags = 0;
10318c2ecf20Sopenharmony_ci		lp_table[i].chars = LP_INIT_CHAR;
10328c2ecf20Sopenharmony_ci		lp_table[i].time = LP_INIT_TIME;
10338c2ecf20Sopenharmony_ci		lp_table[i].wait = LP_INIT_WAIT;
10348c2ecf20Sopenharmony_ci		lp_table[i].lp_buffer = NULL;
10358c2ecf20Sopenharmony_ci#ifdef LP_STATS
10368c2ecf20Sopenharmony_ci		lp_table[i].lastcall = 0;
10378c2ecf20Sopenharmony_ci		lp_table[i].runchars = 0;
10388c2ecf20Sopenharmony_ci		memset(&lp_table[i].stats, 0, sizeof(struct lp_stats));
10398c2ecf20Sopenharmony_ci#endif
10408c2ecf20Sopenharmony_ci		lp_table[i].last_error = 0;
10418c2ecf20Sopenharmony_ci		init_waitqueue_head(&lp_table[i].waitq);
10428c2ecf20Sopenharmony_ci		init_waitqueue_head(&lp_table[i].dataq);
10438c2ecf20Sopenharmony_ci		mutex_init(&lp_table[i].port_mutex);
10448c2ecf20Sopenharmony_ci		lp_table[i].timeout = 10 * HZ;
10458c2ecf20Sopenharmony_ci		port_num[i] = -1;
10468c2ecf20Sopenharmony_ci	}
10478c2ecf20Sopenharmony_ci
10488c2ecf20Sopenharmony_ci	if (register_chrdev(LP_MAJOR, "lp", &lp_fops)) {
10498c2ecf20Sopenharmony_ci		printk(KERN_ERR "lp: unable to get major %d\n", LP_MAJOR);
10508c2ecf20Sopenharmony_ci		return -EIO;
10518c2ecf20Sopenharmony_ci	}
10528c2ecf20Sopenharmony_ci
10538c2ecf20Sopenharmony_ci	lp_class = class_create(THIS_MODULE, "printer");
10548c2ecf20Sopenharmony_ci	if (IS_ERR(lp_class)) {
10558c2ecf20Sopenharmony_ci		err = PTR_ERR(lp_class);
10568c2ecf20Sopenharmony_ci		goto out_reg;
10578c2ecf20Sopenharmony_ci	}
10588c2ecf20Sopenharmony_ci
10598c2ecf20Sopenharmony_ci	if (parport_register_driver(&lp_driver)) {
10608c2ecf20Sopenharmony_ci		printk(KERN_ERR "lp: unable to register with parport\n");
10618c2ecf20Sopenharmony_ci		err = -EIO;
10628c2ecf20Sopenharmony_ci		goto out_class;
10638c2ecf20Sopenharmony_ci	}
10648c2ecf20Sopenharmony_ci
10658c2ecf20Sopenharmony_ci	if (!lp_count) {
10668c2ecf20Sopenharmony_ci		printk(KERN_INFO "lp: driver loaded but no devices found\n");
10678c2ecf20Sopenharmony_ci#ifndef CONFIG_PARPORT_1284
10688c2ecf20Sopenharmony_ci		if (parport_nr[0] == LP_PARPORT_AUTO)
10698c2ecf20Sopenharmony_ci			printk(KERN_INFO "lp: (is IEEE 1284 support enabled?)\n");
10708c2ecf20Sopenharmony_ci#endif
10718c2ecf20Sopenharmony_ci	}
10728c2ecf20Sopenharmony_ci
10738c2ecf20Sopenharmony_ci	return 0;
10748c2ecf20Sopenharmony_ci
10758c2ecf20Sopenharmony_ciout_class:
10768c2ecf20Sopenharmony_ci	class_destroy(lp_class);
10778c2ecf20Sopenharmony_ciout_reg:
10788c2ecf20Sopenharmony_ci	unregister_chrdev(LP_MAJOR, "lp");
10798c2ecf20Sopenharmony_ci	return err;
10808c2ecf20Sopenharmony_ci}
10818c2ecf20Sopenharmony_ci
10828c2ecf20Sopenharmony_cistatic int __init lp_init_module(void)
10838c2ecf20Sopenharmony_ci{
10848c2ecf20Sopenharmony_ci	if (parport[0]) {
10858c2ecf20Sopenharmony_ci		/* The user gave some parameters.  Let's see what they were.  */
10868c2ecf20Sopenharmony_ci		if (!strncmp(parport[0], "auto", 4))
10878c2ecf20Sopenharmony_ci			parport_nr[0] = LP_PARPORT_AUTO;
10888c2ecf20Sopenharmony_ci		else {
10898c2ecf20Sopenharmony_ci			int n;
10908c2ecf20Sopenharmony_ci			for (n = 0; n < LP_NO && parport[n]; n++) {
10918c2ecf20Sopenharmony_ci				if (!strncmp(parport[n], "none", 4))
10928c2ecf20Sopenharmony_ci					parport_nr[n] = LP_PARPORT_NONE;
10938c2ecf20Sopenharmony_ci				else {
10948c2ecf20Sopenharmony_ci					char *ep;
10958c2ecf20Sopenharmony_ci					unsigned long r = simple_strtoul(parport[n], &ep, 0);
10968c2ecf20Sopenharmony_ci					if (ep != parport[n])
10978c2ecf20Sopenharmony_ci						parport_nr[n] = r;
10988c2ecf20Sopenharmony_ci					else {
10998c2ecf20Sopenharmony_ci						printk(KERN_ERR "lp: bad port specifier `%s'\n", parport[n]);
11008c2ecf20Sopenharmony_ci						return -ENODEV;
11018c2ecf20Sopenharmony_ci					}
11028c2ecf20Sopenharmony_ci				}
11038c2ecf20Sopenharmony_ci			}
11048c2ecf20Sopenharmony_ci		}
11058c2ecf20Sopenharmony_ci	}
11068c2ecf20Sopenharmony_ci
11078c2ecf20Sopenharmony_ci	return lp_init();
11088c2ecf20Sopenharmony_ci}
11098c2ecf20Sopenharmony_ci
11108c2ecf20Sopenharmony_cistatic void lp_cleanup_module(void)
11118c2ecf20Sopenharmony_ci{
11128c2ecf20Sopenharmony_ci	parport_unregister_driver(&lp_driver);
11138c2ecf20Sopenharmony_ci
11148c2ecf20Sopenharmony_ci#ifdef CONFIG_LP_CONSOLE
11158c2ecf20Sopenharmony_ci	unregister_console(&lpcons);
11168c2ecf20Sopenharmony_ci#endif
11178c2ecf20Sopenharmony_ci
11188c2ecf20Sopenharmony_ci	unregister_chrdev(LP_MAJOR, "lp");
11198c2ecf20Sopenharmony_ci	class_destroy(lp_class);
11208c2ecf20Sopenharmony_ci}
11218c2ecf20Sopenharmony_ci
11228c2ecf20Sopenharmony_ci__setup("lp=", lp_setup);
11238c2ecf20Sopenharmony_cimodule_init(lp_init_module);
11248c2ecf20Sopenharmony_cimodule_exit(lp_cleanup_module);
11258c2ecf20Sopenharmony_ci
11268c2ecf20Sopenharmony_ciMODULE_ALIAS_CHARDEV_MAJOR(LP_MAJOR);
11278c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
1128