2005-04-17 02:20:36 +04:00
/*
* Generic parallel printer driver
*
* Copyright ( C ) 1992 by Jim Weigand and Linus Torvalds
* Copyright ( C ) 1992 , 1993 by Michael K . Johnson
* - Thanks much to Gunter Windau for pointing out to me where the error
* checking ought to be .
* Copyright ( C ) 1993 by Nigel Gamble ( added interrupt code )
* Copyright ( C ) 1994 by Alan Cox ( Modularised it )
* LPCAREFUL , LPABORT , LPGETSTATUS added by Chris Metcalf , metcalf @ lcs . mit . edu
* Statistics and support for slow printers by Rob Janssen , rob @ knoware . nl
* " lp= " command line parameters added by Grant Guenther , grant @ torque . net
* lp_read ( Status readback ) support added by Carsten Gross ,
* carsten @ sol . wohnheim . uni - ulm . de
* Support for parport by Philip Blundell < philb @ gnu . org >
* Parport sharing hacking by Andrea Arcangeli
* Fixed kernel_ ( to / from ) _user memory copy to check for errors
* by Riccardo Facchetti < fizban @ tin . it >
* 22 - JAN - 1998 Added support for devfs Richard Gooch < rgooch @ atnf . csiro . au >
* Redesigned interrupt handling for handle printers with buggy handshake
* by Andrea Arcangeli , 11 May 1998
* Full efficient handling of printer with buggy irq handshake ( now I have
* understood the meaning of the strange handshake ) . This is done sending new
* characters if the interrupt is just happened , even if the printer say to
* be still BUSY . This is needed at least with Epson Stylus Color . To enable
* the new TRUST_IRQ mode read the ` LP OPTIMIZATION ' section below . . .
* Fixed the irq on the rising edge of the strobe case .
* Obsoleted the CAREFUL flag since a printer that doesn ' t work with
* CAREFUL will block a bit after in lp_check_status ( ) .
* Andrea Arcangeli , 15 Oct 1998
* Obsoleted and removed all the lowlevel stuff implemented in the last
* month to use the IEEE1284 functions ( that handle the _new_ compatibilty
* mode fine ) .
*/
/* This driver should, in theory, work with any parallel port that has an
* appropriate low - level driver ; all I / O is done through the parport
* abstraction layer .
*
* If this driver is built into the kernel , you can configure it using the
* kernel command - line . For example :
*
* lp = parport1 , none , parport2 ( bind lp0 to parport1 , disable lp1 and
* bind lp2 to parport2 )
*
* lp = auto ( assign lp devices to all ports that
* have printers attached , as determined
* by the IEEE - 1284 autoprobe )
*
* lp = reset ( reset the printer during
* initialisation )
*
* lp = off ( disable the printer driver entirely )
*
* If the driver is loaded as a module , similar functionality is available
* using module parameters . The equivalent of the above commands would be :
*
* # insmod lp . o parport = 1 , none , 2
*
* # insmod lp . o parport = auto
*
* # insmod lp . o reset = 1
*/
/* COMPATIBILITY WITH OLD KERNELS
*
* Under Linux 2.0 and previous versions , lp devices were bound to ports at
* particular I / O addresses , as follows :
*
* lp0 0x3bc
* lp1 0x378
* lp2 0x278
*
* The new driver , by default , binds lp devices to parport devices as it
* finds them . This means that if you only have one port , it will be bound
* to lp0 regardless of its I / O address . If you need the old behaviour , you
* can force it using the parameters described above .
*/
/*
* The new interrupt handling code take care of the buggy handshake
* of some HP and Epson printer :
* ___
* ACK _______________ ___________
* | __ |
* ____
* BUSY _________ _______
* | ____________ |
*
* I discovered this using the printer scanner that you can find at :
*
* ftp : //e-mind.com/pub/linux/pscan/
*
* 11 May 98 , Andrea Arcangeli
*
* My printer scanner run on an Epson Stylus Color show that such printer
* generates the irq on the _rising_ edge of the STROBE . Now lp handle
* this case fine too .
*
* 15 Oct 1998 , Andrea Arcangeli
*
* The so called ` buggy ' handshake is really the well documented
* compatibility mode IEEE1284 handshake . They changed the well known
* Centronics handshake acking in the middle of busy expecting to not
* break drivers or legacy application , while they broken linux lp
* until I fixed it reverse engineering the protocol by hand some
* month ago . . .
*
* 14 Dec 1998 , Andrea Arcangeli
*
* Copyright ( C ) 2000 by Tim Waugh ( added LPSETTIMEOUT ioctl )
*/
# include <linux/module.h>
# include <linux/init.h>
# include <linux/errno.h>
# include <linux/kernel.h>
# include <linux/major.h>
# include <linux/sched.h>
# include <linux/slab.h>
# include <linux/fcntl.h>
# include <linux/delay.h>
# include <linux/poll.h>
# include <linux/console.h>
# include <linux/device.h>
# include <linux/wait.h>
2005-09-10 11:26:57 +04:00
# include <linux/jiffies.h>
2005-04-17 02:20:36 +04:00
# include <linux/parport.h>
# undef LP_STATS
# include <linux/lp.h>
# include <asm/irq.h>
# include <asm/uaccess.h>
# include <asm/system.h>
/* if you have more than 8 printers, remember to increase LP_NO */
# define LP_NO 8
static struct lp_struct lp_table [ LP_NO ] ;
static unsigned int lp_count = 0 ;
2005-03-23 20:53:09 +03:00
static struct class * lp_class ;
2005-04-17 02:20:36 +04:00
# ifdef CONFIG_LP_CONSOLE
2007-10-17 10:30:29 +04:00
static struct parport * console_registered ;
2005-04-17 02:20:36 +04:00
# endif /* CONFIG_LP_CONSOLE */
# undef LP_DEBUG
/* Bits used to manage claiming the parport device */
# define LP_PREEMPT_REQUEST 1
# define LP_PARPORT_CLAIMED 2
/* --- low-level port access ----------------------------------- */
# define r_dtr(x) (parport_read_data(lp_table[(x)].dev->port))
# define r_str(x) (parport_read_status(lp_table[(x)].dev->port))
# define w_ctr(x,y) do { parport_write_control(lp_table[(x)].dev->port, (y)); } while (0)
# define w_dtr(x,y) do { parport_write_data(lp_table[(x)].dev->port, (y)); } while (0)
/* Claim the parport or block trying unless we've already claimed it */
static void lp_claim_parport_or_block ( struct lp_struct * this_lp )
{
if ( ! test_and_set_bit ( LP_PARPORT_CLAIMED , & this_lp - > bits ) ) {
parport_claim_or_block ( this_lp - > dev ) ;
}
}
/* Claim the parport or block trying unless we've already claimed it */
static void lp_release_parport ( struct lp_struct * this_lp )
{
if ( test_and_clear_bit ( LP_PARPORT_CLAIMED , & this_lp - > bits ) ) {
parport_release ( this_lp - > dev ) ;
}
}
static int lp_preempt ( void * handle )
{
struct lp_struct * this_lp = ( struct lp_struct * ) handle ;
set_bit ( LP_PREEMPT_REQUEST , & this_lp - > bits ) ;
return ( 1 ) ;
}
/*
* Try to negotiate to a new mode ; if unsuccessful negotiate to
* compatibility mode . Return the mode we ended up in .
*/
static int lp_negotiate ( struct parport * port , int mode )
{
if ( parport_negotiate ( port , mode ) ! = 0 ) {
mode = IEEE1284_MODE_COMPAT ;
parport_negotiate ( port , mode ) ;
}
return ( mode ) ;
}
static int lp_reset ( int minor )
{
int retval ;
lp_claim_parport_or_block ( & lp_table [ minor ] ) ;
w_ctr ( minor , LP_PSELECP ) ;
udelay ( LP_DELAY ) ;
w_ctr ( minor , LP_PSELECP | LP_PINITP ) ;
retval = r_str ( minor ) ;
lp_release_parport ( & lp_table [ minor ] ) ;
return retval ;
}
static void lp_error ( int minor )
{
DEFINE_WAIT ( wait ) ;
int polling ;
if ( LP_F ( minor ) & LP_ABORT )
return ;
polling = lp_table [ minor ] . dev - > port - > irq = = PARPORT_IRQ_NONE ;
if ( polling ) lp_release_parport ( & lp_table [ minor ] ) ;
prepare_to_wait ( & lp_table [ minor ] . waitq , & wait , TASK_INTERRUPTIBLE ) ;
schedule_timeout ( LP_TIMEOUT_POLLED ) ;
finish_wait ( & lp_table [ minor ] . waitq , & wait ) ;
if ( polling ) lp_claim_parport_or_block ( & lp_table [ minor ] ) ;
else parport_yield_blocking ( lp_table [ minor ] . dev ) ;
}
static int lp_check_status ( int minor )
{
int error = 0 ;
unsigned int last = lp_table [ minor ] . last_error ;
unsigned char status = r_str ( minor ) ;
if ( ( status & LP_PERRORP ) & & ! ( LP_F ( minor ) & LP_CAREFUL ) )
/* No error. */
last = 0 ;
else if ( ( status & LP_POUTPA ) ) {
if ( last ! = LP_POUTPA ) {
last = LP_POUTPA ;
printk ( KERN_INFO " lp%d out of paper \n " , minor ) ;
}
error = - ENOSPC ;
} else if ( ! ( status & LP_PSELECD ) ) {
if ( last ! = LP_PSELECD ) {
last = LP_PSELECD ;
printk ( KERN_INFO " lp%d off-line \n " , minor ) ;
}
error = - EIO ;
} else if ( ! ( status & LP_PERRORP ) ) {
if ( last ! = LP_PERRORP ) {
last = LP_PERRORP ;
printk ( KERN_INFO " lp%d on fire \n " , minor ) ;
}
error = - EIO ;
} else {
last = 0 ; /* Come here if LP_CAREFUL is set and no
errors are reported . */
}
lp_table [ minor ] . last_error = last ;
if ( last ! = 0 )
lp_error ( minor ) ;
return error ;
}
static int lp_wait_ready ( int minor , int nonblock )
{
int error = 0 ;
/* If we're not in compatibility mode, we're ready now! */
if ( lp_table [ minor ] . current_mode ! = IEEE1284_MODE_COMPAT ) {
return ( 0 ) ;
}
do {
error = lp_check_status ( minor ) ;
if ( error & & ( nonblock | | ( LP_F ( minor ) & LP_ABORT ) ) )
break ;
if ( signal_pending ( current ) ) {
error = - EINTR ;
break ;
}
} while ( error ) ;
return error ;
}
static ssize_t lp_write ( struct file * file , const char __user * buf ,
size_t count , loff_t * ppos )
{
2006-12-08 13:36:55 +03:00
unsigned int minor = iminor ( file - > f_path . dentry - > d_inode ) ;
2005-04-17 02:20:36 +04:00
struct parport * port = lp_table [ minor ] . dev - > port ;
char * kbuf = lp_table [ minor ] . lp_buffer ;
ssize_t retv = 0 ;
ssize_t written ;
size_t copy_size = count ;
int nonblock = ( ( file - > f_flags & O_NONBLOCK ) | |
( LP_F ( minor ) & LP_ABORT ) ) ;
# ifdef LP_STATS
2005-09-10 11:26:57 +04:00
if ( time_after ( jiffies , lp_table [ minor ] . lastcall + LP_TIME ( minor ) ) )
2005-04-17 02:20:36 +04:00
lp_table [ minor ] . runchars = 0 ;
lp_table [ minor ] . lastcall = jiffies ;
# endif
/* Need to copy the data from user-space. */
if ( copy_size > LP_BUFFER_SIZE )
copy_size = LP_BUFFER_SIZE ;
2008-02-06 12:36:25 +03:00
if ( mutex_lock_interruptible ( & lp_table [ minor ] . port_mutex ) )
2005-04-17 02:20:36 +04:00
return - EINTR ;
if ( copy_from_user ( kbuf , buf , copy_size ) ) {
retv = - EFAULT ;
goto out_unlock ;
}
/* Claim Parport or sleep until it becomes available
*/
lp_claim_parport_or_block ( & lp_table [ minor ] ) ;
/* Go to the proper mode. */
lp_table [ minor ] . current_mode = lp_negotiate ( port ,
lp_table [ minor ] . best_mode ) ;
parport_set_timeout ( lp_table [ minor ] . dev ,
( nonblock ? PARPORT_INACTIVITY_O_NONBLOCK
: lp_table [ minor ] . timeout ) ) ;
if ( ( retv = lp_wait_ready ( minor , nonblock ) ) = = 0 )
do {
/* Write the data. */
written = parport_write ( port , kbuf , copy_size ) ;
if ( written > 0 ) {
copy_size - = written ;
count - = written ;
buf + = written ;
retv + = written ;
}
if ( signal_pending ( current ) ) {
if ( retv = = 0 )
retv = - EINTR ;
break ;
}
if ( copy_size > 0 ) {
/* incomplete write -> check error ! */
int error ;
parport_negotiate ( lp_table [ minor ] . dev - > port ,
IEEE1284_MODE_COMPAT ) ;
lp_table [ minor ] . current_mode = IEEE1284_MODE_COMPAT ;
error = lp_wait_ready ( minor , nonblock ) ;
if ( error ) {
if ( retv = = 0 )
retv = error ;
break ;
} else if ( nonblock ) {
if ( retv = = 0 )
retv = - EAGAIN ;
break ;
}
parport_yield_blocking ( lp_table [ minor ] . dev ) ;
lp_table [ minor ] . current_mode
= lp_negotiate ( port ,
lp_table [ minor ] . best_mode ) ;
} else if ( need_resched ( ) )
schedule ( ) ;
if ( count ) {
copy_size = count ;
if ( copy_size > LP_BUFFER_SIZE )
copy_size = LP_BUFFER_SIZE ;
if ( copy_from_user ( kbuf , buf , copy_size ) ) {
if ( retv = = 0 )
retv = - EFAULT ;
break ;
}
}
} while ( count > 0 ) ;
if ( test_and_clear_bit ( LP_PREEMPT_REQUEST ,
& lp_table [ minor ] . bits ) ) {
printk ( KERN_INFO " lp%d releasing parport \n " , minor ) ;
parport_negotiate ( lp_table [ minor ] . dev - > port ,
IEEE1284_MODE_COMPAT ) ;
lp_table [ minor ] . current_mode = IEEE1284_MODE_COMPAT ;
lp_release_parport ( & lp_table [ minor ] ) ;
}
out_unlock :
2008-02-06 12:36:25 +03:00
mutex_unlock ( & lp_table [ minor ] . port_mutex ) ;
2005-04-17 02:20:36 +04:00
return retv ;
}
# ifdef CONFIG_PARPORT_1284
/* Status readback conforming to ieee1284 */
static ssize_t lp_read ( struct file * file , char __user * buf ,
size_t count , loff_t * ppos )
{
DEFINE_WAIT ( wait ) ;
2006-12-08 13:36:55 +03:00
unsigned int minor = iminor ( file - > f_path . dentry - > d_inode ) ;
2005-04-17 02:20:36 +04:00
struct parport * port = lp_table [ minor ] . dev - > port ;
ssize_t retval = 0 ;
char * kbuf = lp_table [ minor ] . lp_buffer ;
int nonblock = ( ( file - > f_flags & O_NONBLOCK ) | |
( LP_F ( minor ) & LP_ABORT ) ) ;
if ( count > LP_BUFFER_SIZE )
count = LP_BUFFER_SIZE ;
2008-02-06 12:36:25 +03:00
if ( mutex_lock_interruptible ( & lp_table [ minor ] . port_mutex ) )
2005-04-17 02:20:36 +04:00
return - EINTR ;
lp_claim_parport_or_block ( & lp_table [ minor ] ) ;
parport_set_timeout ( lp_table [ minor ] . dev ,
( nonblock ? PARPORT_INACTIVITY_O_NONBLOCK
: lp_table [ minor ] . timeout ) ) ;
parport_negotiate ( lp_table [ minor ] . dev - > port , IEEE1284_MODE_COMPAT ) ;
if ( parport_negotiate ( lp_table [ minor ] . dev - > port ,
IEEE1284_MODE_NIBBLE ) ) {
retval = - EIO ;
goto out ;
}
while ( retval = = 0 ) {
retval = parport_read ( port , kbuf , count ) ;
if ( retval > 0 )
break ;
if ( nonblock ) {
retval = - EAGAIN ;
break ;
}
/* Wait for data. */
if ( lp_table [ minor ] . dev - > port - > irq = = PARPORT_IRQ_NONE ) {
parport_negotiate ( lp_table [ minor ] . dev - > port ,
IEEE1284_MODE_COMPAT ) ;
lp_error ( minor ) ;
if ( parport_negotiate ( lp_table [ minor ] . dev - > port ,
IEEE1284_MODE_NIBBLE ) ) {
retval = - EIO ;
goto out ;
}
} else {
prepare_to_wait ( & lp_table [ minor ] . waitq , & wait , TASK_INTERRUPTIBLE ) ;
schedule_timeout ( LP_TIMEOUT_POLLED ) ;
finish_wait ( & lp_table [ minor ] . waitq , & wait ) ;
}
if ( signal_pending ( current ) ) {
retval = - ERESTARTSYS ;
break ;
}
cond_resched ( ) ;
}
parport_negotiate ( lp_table [ minor ] . dev - > port , IEEE1284_MODE_COMPAT ) ;
out :
lp_release_parport ( & lp_table [ minor ] ) ;
if ( retval > 0 & & copy_to_user ( buf , kbuf , retval ) )
retval = - EFAULT ;
2008-02-06 12:36:25 +03:00
mutex_unlock ( & lp_table [ minor ] . port_mutex ) ;
2005-04-17 02:20:36 +04:00
return retval ;
}
# endif /* IEEE 1284 support */
static int lp_open ( struct inode * inode , struct file * file )
{
unsigned int minor = iminor ( inode ) ;
if ( minor > = LP_NO )
return - ENXIO ;
if ( ( LP_F ( minor ) & LP_EXIST ) = = 0 )
return - ENXIO ;
if ( test_and_set_bit ( LP_BUSY_BIT_POS , & LP_F ( minor ) ) )
return - EBUSY ;
/* If ABORTOPEN is set and the printer is offline or out of paper,
we may still want to open it to perform ioctl ( ) s . Therefore we
have commandeered O_NONBLOCK , even though it is being used in
a non - standard manner . This is strictly a Linux hack , and
should most likely only ever be used by the tunelp application . */
if ( ( LP_F ( minor ) & LP_ABORTOPEN ) & & ! ( file - > f_flags & O_NONBLOCK ) ) {
int status ;
lp_claim_parport_or_block ( & lp_table [ minor ] ) ;
status = r_str ( minor ) ;
lp_release_parport ( & lp_table [ minor ] ) ;
if ( status & LP_POUTPA ) {
printk ( KERN_INFO " lp%d out of paper \n " , minor ) ;
LP_F ( minor ) & = ~ LP_BUSY ;
return - ENOSPC ;
} else if ( ! ( status & LP_PSELECD ) ) {
printk ( KERN_INFO " lp%d off-line \n " , minor ) ;
LP_F ( minor ) & = ~ LP_BUSY ;
return - EIO ;
} else if ( ! ( status & LP_PERRORP ) ) {
printk ( KERN_ERR " lp%d printer error \n " , minor ) ;
LP_F ( minor ) & = ~ LP_BUSY ;
return - EIO ;
}
}
2006-12-13 11:35:56 +03:00
lp_table [ minor ] . lp_buffer = kmalloc ( LP_BUFFER_SIZE , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! lp_table [ minor ] . lp_buffer ) {
LP_F ( minor ) & = ~ LP_BUSY ;
return - ENOMEM ;
}
/* Determine if the peripheral supports ECP mode */
lp_claim_parport_or_block ( & lp_table [ minor ] ) ;
if ( ( lp_table [ minor ] . dev - > port - > modes & PARPORT_MODE_ECP ) & &
! parport_negotiate ( lp_table [ minor ] . dev - > port ,
IEEE1284_MODE_ECP ) ) {
printk ( KERN_INFO " lp%d: ECP mode \n " , minor ) ;
lp_table [ minor ] . best_mode = IEEE1284_MODE_ECP ;
} else {
lp_table [ minor ] . best_mode = IEEE1284_MODE_COMPAT ;
}
/* Leave peripheral in compatibility mode */
parport_negotiate ( lp_table [ minor ] . dev - > port , IEEE1284_MODE_COMPAT ) ;
lp_release_parport ( & lp_table [ minor ] ) ;
lp_table [ minor ] . current_mode = IEEE1284_MODE_COMPAT ;
return 0 ;
}
static int lp_release ( struct inode * inode , struct file * file )
{
unsigned int minor = iminor ( inode ) ;
lp_claim_parport_or_block ( & lp_table [ minor ] ) ;
parport_negotiate ( lp_table [ minor ] . dev - > port , IEEE1284_MODE_COMPAT ) ;
lp_table [ minor ] . current_mode = IEEE1284_MODE_COMPAT ;
lp_release_parport ( & lp_table [ minor ] ) ;
kfree ( lp_table [ minor ] . lp_buffer ) ;
lp_table [ minor ] . lp_buffer = NULL ;
LP_F ( minor ) & = ~ LP_BUSY ;
return 0 ;
}
static int lp_ioctl ( struct inode * inode , struct file * file ,
unsigned int cmd , unsigned long arg )
{
unsigned int minor = iminor ( inode ) ;
int status ;
int retval = 0 ;
void __user * argp = ( void __user * ) arg ;
# ifdef LP_DEBUG
printk ( KERN_DEBUG " lp%d ioctl, cmd: 0x%x, arg: 0x%lx \n " , minor , cmd , arg ) ;
# endif
if ( minor > = LP_NO )
return - ENODEV ;
if ( ( LP_F ( minor ) & LP_EXIST ) = = 0 )
return - ENODEV ;
switch ( cmd ) {
struct timeval par_timeout ;
long to_jiffies ;
case LPTIME :
LP_TIME ( minor ) = arg * HZ / 100 ;
break ;
case LPCHAR :
LP_CHAR ( minor ) = arg ;
break ;
case LPABORT :
if ( arg )
LP_F ( minor ) | = LP_ABORT ;
else
LP_F ( minor ) & = ~ LP_ABORT ;
break ;
case LPABORTOPEN :
if ( arg )
LP_F ( minor ) | = LP_ABORTOPEN ;
else
LP_F ( minor ) & = ~ LP_ABORTOPEN ;
break ;
case LPCAREFUL :
if ( arg )
LP_F ( minor ) | = LP_CAREFUL ;
else
LP_F ( minor ) & = ~ LP_CAREFUL ;
break ;
case LPWAIT :
LP_WAIT ( minor ) = arg ;
break ;
case LPSETIRQ :
return - EINVAL ;
break ;
case LPGETIRQ :
if ( copy_to_user ( argp , & LP_IRQ ( minor ) ,
sizeof ( int ) ) )
return - EFAULT ;
break ;
case LPGETSTATUS :
lp_claim_parport_or_block ( & lp_table [ minor ] ) ;
status = r_str ( minor ) ;
lp_release_parport ( & lp_table [ minor ] ) ;
if ( copy_to_user ( argp , & status , sizeof ( int ) ) )
return - EFAULT ;
break ;
case LPRESET :
lp_reset ( minor ) ;
break ;
# ifdef LP_STATS
case LPGETSTATS :
if ( copy_to_user ( argp , & LP_STAT ( minor ) ,
sizeof ( struct lp_stats ) ) )
return - EFAULT ;
if ( capable ( CAP_SYS_ADMIN ) )
memset ( & LP_STAT ( minor ) , 0 ,
sizeof ( struct lp_stats ) ) ;
break ;
# endif
case LPGETFLAGS :
status = LP_F ( minor ) ;
if ( copy_to_user ( argp , & status , sizeof ( int ) ) )
return - EFAULT ;
break ;
case LPSETTIMEOUT :
if ( copy_from_user ( & par_timeout , argp ,
sizeof ( struct timeval ) ) ) {
return - EFAULT ;
}
/* Convert to jiffies, place in lp_table */
if ( ( par_timeout . tv_sec < 0 ) | |
( par_timeout . tv_usec < 0 ) ) {
return - EINVAL ;
}
2007-05-08 11:28:57 +04:00
to_jiffies = DIV_ROUND_UP ( par_timeout . tv_usec , 1000000 / HZ ) ;
2005-04-17 02:20:36 +04:00
to_jiffies + = par_timeout . tv_sec * ( long ) HZ ;
if ( to_jiffies < = 0 ) {
return - EINVAL ;
}
lp_table [ minor ] . timeout = to_jiffies ;
break ;
default :
retval = - EINVAL ;
}
return retval ;
}
2006-07-03 11:24:21 +04:00
static const struct file_operations lp_fops = {
2005-04-17 02:20:36 +04:00
. owner = THIS_MODULE ,
. write = lp_write ,
. ioctl = lp_ioctl ,
. open = lp_open ,
. release = lp_release ,
# ifdef CONFIG_PARPORT_1284
. read = lp_read ,
# endif
} ;
/* --- support for console on the line printer ----------------- */
# ifdef CONFIG_LP_CONSOLE
# define CONSOLE_LP 0
/* If the printer is out of paper, we can either lose the messages or
* stall until the printer is happy again . Define CONSOLE_LP_STRICT
* non - zero to get the latter behaviour . */
# define CONSOLE_LP_STRICT 1
/* The console must be locked when we get here. */
static void lp_console_write ( struct console * co , const char * s ,
unsigned count )
{
struct pardevice * dev = lp_table [ CONSOLE_LP ] . dev ;
struct parport * port = dev - > port ;
ssize_t written ;
if ( parport_claim ( dev ) )
/* Nothing we can do. */
return ;
parport_set_timeout ( dev , 0 ) ;
/* Go to compatibility mode. */
parport_negotiate ( port , IEEE1284_MODE_COMPAT ) ;
do {
/* Write the data, converting LF->CRLF as we go. */
ssize_t canwrite = count ;
char * lf = memchr ( s , ' \n ' , count ) ;
if ( lf )
canwrite = lf - s ;
if ( canwrite > 0 ) {
written = parport_write ( port , s , canwrite ) ;
if ( written < = 0 )
continue ;
s + = written ;
count - = written ;
canwrite - = written ;
}
if ( lf & & canwrite < = 0 ) {
const char * crlf = " \r \n " ;
int i = 2 ;
/* Dodge the original '\n', and put '\r\n' instead. */
s + + ;
count - - ;
do {
written = parport_write ( port , crlf , i ) ;
if ( written > 0 )
i - = written , crlf + = written ;
} while ( i > 0 & & ( CONSOLE_LP_STRICT | | written > 0 ) ) ;
}
} while ( count > 0 & & ( CONSOLE_LP_STRICT | | written > 0 ) ) ;
parport_release ( dev ) ;
}
static struct console lpcons = {
. name = " lp " ,
. write = lp_console_write ,
. flags = CON_PRINTBUFFER ,
} ;
# endif /* console on line printer */
/* --- initialisation code ------------------------------------- */
static int parport_nr [ LP_NO ] = { [ 0 . . . LP_NO - 1 ] = LP_PARPORT_UNSPEC } ;
2007-10-17 10:30:29 +04:00
static char * parport [ LP_NO ] ;
static int reset ;
2005-04-17 02:20:36 +04:00
module_param_array ( parport , charp , NULL , 0 ) ;
module_param ( reset , bool , 0 ) ;
# ifndef MODULE
static int __init lp_setup ( char * str )
{
2007-10-17 10:30:29 +04:00
static int parport_ptr ;
2005-04-17 02:20:36 +04:00
int x ;
2007-10-17 10:30:29 +04:00
if ( get_option ( & str , & x ) ) {
2005-04-17 02:20:36 +04:00
if ( x = = 0 ) {
/* disable driver on "lp=" or "lp=0" */
parport_nr [ 0 ] = LP_PARPORT_OFF ;
} else {
printk ( KERN_WARNING " warning: 'lp=0x%x' is deprecated, ignored \n " , x ) ;
return 0 ;
}
} else if ( ! strncmp ( str , " parport " , 7 ) ) {
int n = simple_strtoul ( str + 7 , NULL , 10 ) ;
if ( parport_ptr < LP_NO )
parport_nr [ parport_ptr + + ] = n ;
else
printk ( KERN_INFO " lp: too many ports, %s ignored. \n " ,
str ) ;
} else if ( ! strcmp ( str , " auto " ) ) {
parport_nr [ 0 ] = LP_PARPORT_AUTO ;
} else if ( ! strcmp ( str , " none " ) ) {
parport_nr [ parport_ptr + + ] = LP_PARPORT_NONE ;
} else if ( ! strcmp ( str , " reset " ) ) {
reset = 1 ;
}
return 1 ;
}
# endif
static int lp_register ( int nr , struct parport * port )
{
lp_table [ nr ] . dev = parport_register_device ( port , " lp " ,
lp_preempt , NULL , NULL , 0 ,
( void * ) & lp_table [ nr ] ) ;
if ( lp_table [ nr ] . dev = = NULL )
return 1 ;
lp_table [ nr ] . flags | = LP_EXIST ;
if ( reset )
lp_reset ( nr ) ;
2007-08-08 09:28:44 +04:00
device_create ( lp_class , port - > dev , MKDEV ( LP_MAJOR , nr ) , " lp%d " , nr ) ;
2005-04-17 02:20:36 +04:00
printk ( KERN_INFO " lp%d: using %s (%s). \n " , nr , port - > name ,
( port - > irq = = PARPORT_IRQ_NONE ) ? " polling " : " interrupt-driven " ) ;
# ifdef CONFIG_LP_CONSOLE
if ( ! nr ) {
if ( port - > modes & PARPORT_MODE_SAFEININT ) {
2007-10-17 10:30:29 +04:00
register_console ( & lpcons ) ;
2005-04-17 02:20:36 +04:00
console_registered = port ;
printk ( KERN_INFO " lp%d: console ready \n " , CONSOLE_LP ) ;
} else
printk ( KERN_ERR " lp%d: cannot run console on %s \n " ,
CONSOLE_LP , port - > name ) ;
}
# endif
return 0 ;
}
static void lp_attach ( struct parport * port )
{
unsigned int i ;
2007-10-17 10:30:29 +04:00
switch ( parport_nr [ 0 ] ) {
2005-04-17 02:20:36 +04:00
case LP_PARPORT_UNSPEC :
case LP_PARPORT_AUTO :
if ( parport_nr [ 0 ] = = LP_PARPORT_AUTO & &
port - > probe_info [ 0 ] . class ! = PARPORT_CLASS_PRINTER )
return ;
if ( lp_count = = LP_NO ) {
printk ( KERN_INFO " lp: ignoring parallel port (max. %d) \n " , LP_NO ) ;
return ;
}
if ( ! lp_register ( lp_count , port ) )
lp_count + + ;
break ;
default :
for ( i = 0 ; i < LP_NO ; i + + ) {
if ( port - > number = = parport_nr [ i ] ) {
if ( ! lp_register ( i , port ) )
lp_count + + ;
break ;
}
}
break ;
}
}
static void lp_detach ( struct parport * port )
{
/* Write this some day. */
# ifdef CONFIG_LP_CONSOLE
if ( console_registered = = port ) {
2007-10-17 10:30:29 +04:00
unregister_console ( & lpcons ) ;
2005-04-17 02:20:36 +04:00
console_registered = NULL ;
}
# endif /* CONFIG_LP_CONSOLE */
}
static struct parport_driver lp_driver = {
. name = " lp " ,
. attach = lp_attach ,
. detach = lp_detach ,
} ;
static int __init lp_init ( void )
{
int i , err = 0 ;
if ( parport_nr [ 0 ] = = LP_PARPORT_OFF )
return 0 ;
for ( i = 0 ; i < LP_NO ; i + + ) {
lp_table [ i ] . dev = NULL ;
lp_table [ i ] . flags = 0 ;
lp_table [ i ] . chars = LP_INIT_CHAR ;
lp_table [ i ] . time = LP_INIT_TIME ;
lp_table [ i ] . wait = LP_INIT_WAIT ;
lp_table [ i ] . lp_buffer = NULL ;
# ifdef LP_STATS
lp_table [ i ] . lastcall = 0 ;
lp_table [ i ] . runchars = 0 ;
memset ( & lp_table [ i ] . stats , 0 , sizeof ( struct lp_stats ) ) ;
# endif
lp_table [ i ] . last_error = 0 ;
init_waitqueue_head ( & lp_table [ i ] . waitq ) ;
init_waitqueue_head ( & lp_table [ i ] . dataq ) ;
2008-02-06 12:36:25 +03:00
mutex_init ( & lp_table [ i ] . port_mutex ) ;
2005-04-17 02:20:36 +04:00
lp_table [ i ] . timeout = 10 * HZ ;
}
if ( register_chrdev ( LP_MAJOR , " lp " , & lp_fops ) ) {
printk ( KERN_ERR " lp: unable to get major %d \n " , LP_MAJOR ) ;
return - EIO ;
}
2005-03-23 20:53:09 +03:00
lp_class = class_create ( THIS_MODULE , " printer " ) ;
2005-04-17 02:20:36 +04:00
if ( IS_ERR ( lp_class ) ) {
err = PTR_ERR ( lp_class ) ;
2006-09-29 12:59:47 +04:00
goto out_reg ;
2005-04-17 02:20:36 +04:00
}
if ( parport_register_driver ( & lp_driver ) ) {
printk ( KERN_ERR " lp: unable to register with parport \n " ) ;
err = - EIO ;
goto out_class ;
}
if ( ! lp_count ) {
printk ( KERN_INFO " lp: driver loaded but no devices found \n " ) ;
# ifndef CONFIG_PARPORT_1284
if ( parport_nr [ 0 ] = = LP_PARPORT_AUTO )
printk ( KERN_INFO " lp: (is IEEE 1284 support enabled?) \n " ) ;
# endif
}
return 0 ;
out_class :
2005-03-23 20:53:09 +03:00
class_destroy ( lp_class ) ;
2006-09-29 12:59:47 +04:00
out_reg :
2005-04-17 02:20:36 +04:00
unregister_chrdev ( LP_MAJOR , " lp " ) ;
return err ;
}
static int __init lp_init_module ( void )
{
if ( parport [ 0 ] ) {
/* The user gave some parameters. Let's see what they were. */
if ( ! strncmp ( parport [ 0 ] , " auto " , 4 ) )
parport_nr [ 0 ] = LP_PARPORT_AUTO ;
else {
int n ;
for ( n = 0 ; n < LP_NO & & parport [ n ] ; n + + ) {
if ( ! strncmp ( parport [ n ] , " none " , 4 ) )
parport_nr [ n ] = LP_PARPORT_NONE ;
else {
char * ep ;
unsigned long r = simple_strtoul ( parport [ n ] , & ep , 0 ) ;
if ( ep ! = parport [ n ] )
parport_nr [ n ] = r ;
else {
printk ( KERN_ERR " lp: bad port specifier `%s' \n " , parport [ n ] ) ;
return - ENODEV ;
}
}
}
}
}
return lp_init ( ) ;
}
static void lp_cleanup_module ( void )
{
unsigned int offset ;
parport_unregister_driver ( & lp_driver ) ;
# ifdef CONFIG_LP_CONSOLE
unregister_console ( & lpcons ) ;
# endif
unregister_chrdev ( LP_MAJOR , " lp " ) ;
for ( offset = 0 ; offset < LP_NO ; offset + + ) {
if ( lp_table [ offset ] . dev = = NULL )
continue ;
parport_unregister_device ( lp_table [ offset ] . dev ) ;
2007-08-08 09:28:44 +04:00
device_destroy ( lp_class , MKDEV ( LP_MAJOR , offset ) ) ;
2005-04-17 02:20:36 +04:00
}
2005-03-23 20:53:09 +03:00
class_destroy ( lp_class ) ;
2005-04-17 02:20:36 +04:00
}
__setup ( " lp= " , lp_setup ) ;
module_init ( lp_init_module ) ;
module_exit ( lp_cleanup_module ) ;
MODULE_ALIAS_CHARDEV_MAJOR ( LP_MAJOR ) ;
MODULE_LICENSE ( " GPL " ) ;