diff options
Diffstat (limited to 'drivers/char/lp.c')
| -rw-r--r-- | drivers/char/lp.c | 215 |
1 files changed, 141 insertions, 74 deletions
diff --git a/drivers/char/lp.c b/drivers/char/lp.c index e5726052529..c4094c4e22c 100644 --- a/drivers/char/lp.c +++ b/drivers/char/lp.c @@ -114,13 +114,10 @@ #include <linux/module.h> #include <linux/init.h> -#include <linux/config.h> #include <linux/errno.h> #include <linux/kernel.h> #include <linux/major.h> #include <linux/sched.h> -#include <linux/smp_lock.h> -#include <linux/devfs_fs_kernel.h> #include <linux/slab.h> #include <linux/fcntl.h> #include <linux/delay.h> @@ -129,6 +126,8 @@ #include <linux/device.h> #include <linux/wait.h> #include <linux/jiffies.h> +#include <linux/mutex.h> +#include <linux/compat.h> #include <linux/parport.h> #undef LP_STATS @@ -136,21 +135,18 @@ #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 -/* ROUND_UP macro from fs/select.c */ -#define ROUND_UP(x,y) (((x)+(y)-1)/(y)) - +static DEFINE_MUTEX(lp_mutex); static struct lp_struct lp_table[LP_NO]; static unsigned int lp_count = 0; static struct class *lp_class; #ifdef CONFIG_LP_CONSOLE -static struct parport *console_registered; // initially NULL +static struct parport *console_registered; #endif /* CONFIG_LP_CONSOLE */ #undef LP_DEBUG @@ -298,7 +294,7 @@ static int lp_wait_ready(int minor, int nonblock) static ssize_t lp_write(struct file * file, const char __user * buf, size_t count, loff_t *ppos) { - unsigned int minor = iminor(file->f_dentry->d_inode); + unsigned int minor = iminor(file_inode(file)); struct parport *port = lp_table[minor].dev->port; char *kbuf = lp_table[minor].lp_buffer; ssize_t retv = 0; @@ -318,7 +314,7 @@ static ssize_t lp_write(struct file * file, const char __user * buf, if (copy_size > LP_BUFFER_SIZE) copy_size = LP_BUFFER_SIZE; - if (down_interruptible (&lp_table[minor].port_mutex)) + if (mutex_lock_interruptible(&lp_table[minor].port_mutex)) return -EINTR; if (copy_from_user (kbuf, buf, copy_size)) { @@ -405,7 +401,7 @@ static ssize_t lp_write(struct file * file, const char __user * buf, lp_release_parport (&lp_table[minor]); } out_unlock: - up (&lp_table[minor].port_mutex); + mutex_unlock(&lp_table[minor].port_mutex); return retv; } @@ -417,7 +413,7 @@ static ssize_t lp_read(struct file * file, char __user * buf, size_t count, loff_t *ppos) { DEFINE_WAIT(wait); - unsigned int minor=iminor(file->f_dentry->d_inode); + unsigned int minor=iminor(file_inode(file)); struct parport *port = lp_table[minor].dev->port; ssize_t retval = 0; char *kbuf = lp_table[minor].lp_buffer; @@ -427,7 +423,7 @@ static ssize_t lp_read(struct file * file, char __user * buf, if (count > LP_BUFFER_SIZE) count = LP_BUFFER_SIZE; - if (down_interruptible (&lp_table[minor].port_mutex)) + if (mutex_lock_interruptible(&lp_table[minor].port_mutex)) return -EINTR; lp_claim_parport_or_block (&lp_table[minor]); @@ -485,7 +481,7 @@ static ssize_t lp_read(struct file * file, char __user * buf, if (retval > 0 && copy_to_user (buf, kbuf, retval)) retval = -EFAULT; - up (&lp_table[minor].port_mutex); + mutex_unlock(&lp_table[minor].port_mutex); return retval; } @@ -495,14 +491,21 @@ static ssize_t lp_read(struct file * file, char __user * buf, static int lp_open(struct inode * inode, struct file * file) { unsigned int minor = iminor(inode); + int ret = 0; - 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; - + mutex_lock(&lp_mutex); + if (minor >= LP_NO) { + ret = -ENXIO; + goto out; + } + if ((LP_F(minor) & LP_EXIST) == 0) { + ret = -ENXIO; + goto out; + } + if (test_and_set_bit(LP_BUSY_BIT_POS, &LP_F(minor))) { + ret = -EBUSY; + goto out; + } /* If ABORTOPEN is set and the printer is offline or out of paper, we may still want to open it to perform ioctl()s. Therefore we have commandeered O_NONBLOCK, even though it is being used in @@ -516,21 +519,25 @@ static int lp_open(struct inode * inode, struct file * file) if (status & LP_POUTPA) { printk(KERN_INFO "lp%d out of paper\n", minor); LP_F(minor) &= ~LP_BUSY; - return -ENOSPC; + ret = -ENOSPC; + goto out; } else if (!(status & LP_PSELECD)) { printk(KERN_INFO "lp%d off-line\n", minor); LP_F(minor) &= ~LP_BUSY; - return -EIO; + ret = -EIO; + goto out; } else if (!(status & LP_PERRORP)) { printk(KERN_ERR "lp%d printer error\n", minor); LP_F(minor) &= ~LP_BUSY; - return -EIO; + ret = -EIO; + goto out; } } - lp_table[minor].lp_buffer = (char *) kmalloc(LP_BUFFER_SIZE, GFP_KERNEL); + lp_table[minor].lp_buffer = kmalloc(LP_BUFFER_SIZE, GFP_KERNEL); if (!lp_table[minor].lp_buffer) { LP_F(minor) &= ~LP_BUSY; - return -ENOMEM; + ret = -ENOMEM; + goto out; } /* Determine if the peripheral supports ECP mode */ lp_claim_parport_or_block (&lp_table[minor]); @@ -546,7 +553,9 @@ static int lp_open(struct inode * inode, struct file * file) 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; +out: + mutex_unlock(&lp_mutex); + return ret; } static int lp_release(struct inode * inode, struct file * file) @@ -563,13 +572,11 @@ static int lp_release(struct inode * inode, struct file * file) return 0; } -static int lp_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) +static int lp_do_ioctl(unsigned int minor, unsigned int cmd, + unsigned long arg, void __user *argp) { - 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); @@ -579,10 +586,9 @@ static int lp_ioctl(struct inode *inode, struct file *file, if ((LP_F(minor) & LP_EXIST) == 0) return -ENODEV; switch ( cmd ) { - struct timeval par_timeout; - long to_jiffies; - case LPTIME: + if (arg > UINT_MAX / HZ) + return -EINVAL; LP_TIME(minor) = arg * HZ/100; break; case LPCHAR: @@ -618,9 +624,12 @@ static int lp_ioctl(struct inode *inode, struct file *file, return -EFAULT; break; case LPGETSTATUS: + if (mutex_lock_interruptible(&lp_table[minor].port_mutex)) + return -EINTR; lp_claim_parport_or_block (&lp_table[minor]); status = r_str(minor); lp_release_parport (&lp_table[minor]); + mutex_unlock(&lp_table[minor].port_mutex); if (copy_to_user(argp, &status, sizeof(int))) return -EFAULT; @@ -644,39 +653,104 @@ static int lp_ioctl(struct inode *inode, struct file *file, 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; - } - to_jiffies = ROUND_UP(par_timeout.tv_usec, 1000000/HZ); - 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; } -static struct file_operations lp_fops = { +static int lp_set_timeout(unsigned int minor, struct timeval *par_timeout) +{ + long to_jiffies; + + /* Convert to jiffies, place in lp_table */ + if ((par_timeout->tv_sec < 0) || + (par_timeout->tv_usec < 0)) { + return -EINVAL; + } + to_jiffies = DIV_ROUND_UP(par_timeout->tv_usec, 1000000/HZ); + to_jiffies += par_timeout->tv_sec * (long) HZ; + if (to_jiffies <= 0) { + return -EINVAL; + } + lp_table[minor].timeout = to_jiffies; + return 0; +} + +static long lp_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + unsigned int minor; + struct timeval par_timeout; + int ret; + + minor = iminor(file_inode(file)); + mutex_lock(&lp_mutex); + switch (cmd) { + case LPSETTIMEOUT: + if (copy_from_user(&par_timeout, (void __user *)arg, + sizeof (struct timeval))) { + ret = -EFAULT; + break; + } + ret = lp_set_timeout(minor, &par_timeout); + break; + default: + ret = lp_do_ioctl(minor, cmd, arg, (void __user *)arg); + break; + } + mutex_unlock(&lp_mutex); + + return ret; +} + +#ifdef CONFIG_COMPAT +static long lp_compat_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + unsigned int minor; + struct timeval par_timeout; + int ret; + + minor = iminor(file_inode(file)); + mutex_lock(&lp_mutex); + switch (cmd) { + case LPSETTIMEOUT: + if (compat_get_timeval(&par_timeout, compat_ptr(arg))) { + ret = -EFAULT; + break; + } + ret = lp_set_timeout(minor, &par_timeout); + break; +#ifdef LP_STATS + case LPGETSTATS: + /* FIXME: add an implementation if you set LP_STATS */ + ret = -EINVAL; + break; +#endif + default: + ret = lp_do_ioctl(minor, cmd, arg, compat_ptr(arg)); + break; + } + mutex_unlock(&lp_mutex); + + return ret; +} +#endif + +static const struct file_operations lp_fops = { .owner = THIS_MODULE, .write = lp_write, - .ioctl = lp_ioctl, + .unlocked_ioctl = lp_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = lp_compat_ioctl, +#endif .open = lp_open, .release = lp_release, #ifdef CONFIG_PARPORT_1284 .read = lp_read, #endif + .llseek = noop_llseek, }; /* --- support for console on the line printer ----------------- */ @@ -755,8 +829,8 @@ static struct console lpcons = { /* --- initialisation code ------------------------------------- */ static int parport_nr[LP_NO] = { [0 ... LP_NO-1] = LP_PARPORT_UNSPEC }; -static char *parport[LP_NO] = { NULL, }; -static int reset = 0; +static char *parport[LP_NO]; +static bool reset; module_param_array(parport, charp, NULL, 0); module_param(reset, bool, 0); @@ -764,10 +838,10 @@ module_param(reset, bool, 0); #ifndef MODULE static int __init lp_setup (char *str) { - static int parport_ptr; // initially zero + static int parport_ptr; int x; - if (get_option (&str, &x)) { + if (get_option(&str, &x)) { if (x == 0) { /* disable driver on "lp=" or "lp=0" */ parport_nr[0] = LP_PARPORT_OFF; @@ -805,10 +879,8 @@ static int lp_register(int nr, struct parport *port) if (reset) lp_reset(nr); - class_device_create(lp_class, NULL, MKDEV(LP_MAJOR, nr), NULL, - "lp%d", nr); - devfs_mk_cdev(MKDEV(LP_MAJOR, nr), S_IFCHR | S_IRUGO | S_IWUGO, - "printers/%d", nr); + device_create(lp_class, port->dev, MKDEV(LP_MAJOR, nr), NULL, + "lp%d", nr); printk(KERN_INFO "lp%d: using %s (%s).\n", nr, port->name, (port->irq == PARPORT_IRQ_NONE)?"polling":"interrupt-driven"); @@ -816,7 +888,7 @@ static int lp_register(int nr, struct parport *port) #ifdef CONFIG_LP_CONSOLE if (!nr) { if (port->modes & PARPORT_MODE_SAFEININT) { - register_console (&lpcons); + register_console(&lpcons); console_registered = port; printk (KERN_INFO "lp%d: console ready\n", CONSOLE_LP); } else @@ -832,8 +904,7 @@ static void lp_attach (struct parport *port) { unsigned int i; - switch (parport_nr[0]) - { + switch (parport_nr[0]) { case LP_PARPORT_UNSPEC: case LP_PARPORT_AUTO: if (parport_nr[0] == LP_PARPORT_AUTO && @@ -864,7 +935,7 @@ static void lp_detach (struct parport *port) /* Write this some day. */ #ifdef CONFIG_LP_CONSOLE if (console_registered == port) { - unregister_console (&lpcons); + unregister_console(&lpcons); console_registered = NULL; } #endif /* CONFIG_LP_CONSOLE */ @@ -898,7 +969,7 @@ static int __init lp_init (void) lp_table[i].last_error = 0; init_waitqueue_head (&lp_table[i].waitq); init_waitqueue_head (&lp_table[i].dataq); - init_MUTEX (&lp_table[i].port_mutex); + mutex_init(&lp_table[i].port_mutex); lp_table[i].timeout = 10 * HZ; } @@ -907,11 +978,10 @@ static int __init lp_init (void) return -EIO; } - devfs_mk_dir("printers"); lp_class = class_create(THIS_MODULE, "printer"); if (IS_ERR(lp_class)) { err = PTR_ERR(lp_class); - goto out_devfs; + goto out_reg; } if (parport_register_driver (&lp_driver)) { @@ -932,8 +1002,7 @@ static int __init lp_init (void) out_class: class_destroy(lp_class); -out_devfs: - devfs_remove("printers"); +out_reg: unregister_chrdev(LP_MAJOR, "lp"); return err; } @@ -981,10 +1050,8 @@ static void lp_cleanup_module (void) if (lp_table[offset].dev == NULL) continue; parport_unregister_device(lp_table[offset].dev); - devfs_remove("printers/%d", offset); - class_device_destroy(lp_class, MKDEV(LP_MAJOR, offset)); + device_destroy(lp_class, MKDEV(LP_MAJOR, offset)); } - devfs_remove("printers"); class_destroy(lp_class); } |
