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