diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1999-10-09 00:00:47 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 1999-10-09 00:00:47 +0000 |
commit | d6434e1042f3b0a6dfe1b1f615af369486f9b1fa (patch) | |
tree | e2be02f33984c48ec019c654051d27964e42c441 /drivers/char/ppdev.c | |
parent | 609d1e803baf519487233b765eb487f9ec227a18 (diff) |
Merge with 2.3.19.
Diffstat (limited to 'drivers/char/ppdev.c')
-rw-r--r-- | drivers/char/ppdev.c | 349 |
1 files changed, 183 insertions, 166 deletions
diff --git a/drivers/char/ppdev.c b/drivers/char/ppdev.c index 881e521ad..c210b0785 100644 --- a/drivers/char/ppdev.c +++ b/drivers/char/ppdev.c @@ -11,7 +11,7 @@ * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * - * A /dev/parportxy device node represents an arbitrary device ('y') + * A /dev/parportx device node represents an arbitrary device * on port 'x'. The following operations are possible: * * open do nothing, set up default IEEE 1284 protocol to be COMPAT @@ -21,6 +21,8 @@ * CLAIM (register device first time) parport_claim_or_block * RELEASE parport_release * SETMODE set the IEEE 1284 protocol to use for read/write + * SETPHASE set the IEEE 1284 phase of a particular mode. Not to be + * confused with ioctl(fd, SETPHASER, &stun). ;-) * DATADIR data_forward / data_reverse * WDATA write_data * RDATA read_data @@ -30,6 +32,8 @@ * RSTATUS read_status * NEGOT parport_negotiate * YIELD parport_yield_blocking + * WCTLONIRQ on interrupt, set control lines + * CLRIRQ clear (and return) interrupt count * read/write read or write in current IEEE 1284 protocol * select wait for interrupt (in readfds) */ @@ -50,16 +54,15 @@ #define min(a,b) ((a) < (b) ? (a) : (b)) #endif -/* The device minor encodes the parport number and (arbitrary) - * pardevice number as (port << 4) | dev. */ -#define PP_PORT(minor) ((minor >> 4) & 0xf) -#define PP_DEV(minor) ((minor) & 0xf) - struct pp_struct { struct pardevice * pdev; wait_queue_head_t irq_wait; - int mode; + atomic_t irqc; unsigned int flags; + int irqresponse; + unsigned char irqctl; + struct ieee1284_info state; + struct ieee1284_info saved_state; }; /* pp_struct.flags bitfields */ @@ -71,111 +74,31 @@ struct pp_struct { #define PP_BUFFER_SIZE 256 #define PARDEVICE_MAX 8 -static struct pp_struct pp_table[PARPORT_MAX][PARDEVICE_MAX]; - -static loff_t pp_lseek (struct file * file, long long offset, int origin) -{ - return -ESPIPE; -} - -/* This looks a bit like parport_read. The difference is that we don't - * determine the mode to use from the port data, but rather from the - * mode the driver told us to use. */ -static ssize_t do_read (struct pp_struct *pp, void *buf, size_t len) +static inline void enable_irq (struct pp_struct *pp) { - size_t (*fn) (struct parport *, void *, size_t, int); struct parport *port = pp->pdev->port; - - switch (pp->mode) { - case IEEE1284_MODE_COMPAT: - /* This is a write-only mode. */ - return -EIO; - - case IEEE1284_MODE_NIBBLE: - fn = port->ops->nibble_read_data; - break; - - case IEEE1284_MODE_BYTE: - fn = port->ops->byte_read_data; - break; - - case IEEE1284_MODE_EPP: - fn = port->ops->epp_read_data; - break; - - case IEEE1284_MODE_ECP: - case IEEE1284_MODE_ECPRLE: - fn = port->ops->ecp_read_data; - break; - - case IEEE1284_MODE_ECPSWE: - fn = parport_ieee1284_ecp_read_data; - break; - - default: - printk (KERN_DEBUG "%s: unknown mode 0x%02x\n", - pp->pdev->name, pp->mode); - return -EINVAL; - } - - return (*fn) (port, buf, len, 0); + port->ops->enable_irq (port); } -/* This looks a bit like parport_write. The difference is that we don't - * determine the mode to use from the port data, but rather from the - * mode the driver told us to use. */ -static ssize_t do_write (struct pp_struct *pp, const void *buf, size_t len) +static loff_t pp_lseek (struct file * file, long long offset, int origin) { - size_t (*fn) (struct parport *, const void *, size_t, int); - struct parport *port = pp->pdev->port; - - switch (pp->mode) { - case IEEE1284_MODE_NIBBLE: - case IEEE1284_MODE_BYTE: - /* Read-only modes. */ - return -EIO; - - case IEEE1284_MODE_COMPAT: - fn = port->ops->compat_write_data; - break; - - case IEEE1284_MODE_EPP: - fn = port->ops->epp_write_data; - break; - - case IEEE1284_MODE_ECP: - case IEEE1284_MODE_ECPRLE: - fn = port->ops->ecp_write_data; - break; - - case IEEE1284_MODE_ECPSWE: - fn = parport_ieee1284_ecp_write_data; - break; - - default: - printk (KERN_DEBUG "%s: unknown mode 0x%02x\n", - pp->pdev->name, pp->mode); - return -EINVAL; - } - - return (*fn) (port, buf, len, 0); + return -ESPIPE; } static ssize_t pp_read (struct file * file, char * buf, size_t count, loff_t * ppos) { unsigned int minor = MINOR (file->f_dentry->d_inode->i_rdev); - unsigned int portnum = PP_PORT (minor); - unsigned int dev = PP_DEV (minor); + struct pp_struct *pp = file->private_data; char * kbuffer; ssize_t bytes_read = 0; ssize_t got = 0; - if (!(pp_table[portnum][dev].flags & PP_CLAIMED)) { + if (!(pp->flags & PP_CLAIMED)) { /* Don't have the port claimed */ - printk (KERN_DEBUG CHRDEV "%02x: claim the port first\n", + printk (KERN_DEBUG CHRDEV "%x: claim the port first\n", minor); - return -EPERM; + return -EINVAL; } kbuffer = kmalloc (min (count, PP_BUFFER_SIZE), GFP_KERNEL); @@ -185,16 +108,16 @@ static ssize_t pp_read (struct file * file, char * buf, size_t count, while (bytes_read < count) { ssize_t need = min(count - bytes_read, PP_BUFFER_SIZE); - got = do_read (&pp_table[portnum][dev], kbuffer, need); + got = parport_read (pp->pdev->port, kbuffer, need); - if (got < 0) { + if (got <= 0) { if (!bytes_read) bytes_read = got; break; } - if (copy_to_user (kbuffer, buf + bytes_read, got)) { + if (copy_to_user (buf + bytes_read, kbuffer, got)) { bytes_read = -EFAULT; break; } @@ -212,6 +135,7 @@ static ssize_t pp_read (struct file * file, char * buf, size_t count, } kfree (kbuffer); + enable_irq (pp); return bytes_read; } @@ -219,17 +143,16 @@ static ssize_t pp_write (struct file * file, const char * buf, size_t count, loff_t * ppos) { unsigned int minor = MINOR (file->f_dentry->d_inode->i_rdev); - unsigned int portnum = PP_PORT (minor); - unsigned int dev = PP_DEV (minor); + struct pp_struct *pp = file->private_data; char * kbuffer; ssize_t bytes_written = 0; ssize_t wrote; - if (!(pp_table[portnum][dev].flags & PP_CLAIMED)) { + if (!(pp->flags & PP_CLAIMED)) { /* Don't have the port claimed */ - printk (KERN_DEBUG CHRDEV "%02x: claim the port first\n", + printk (KERN_DEBUG CHRDEV "%x: claim the port first\n", minor); - return -EPERM; + return -EINVAL; } kbuffer = kmalloc (min (count, PP_BUFFER_SIZE), GFP_KERNEL); @@ -244,7 +167,7 @@ static ssize_t pp_write (struct file * file, const char * buf, size_t count, break; } - wrote = do_write (&pp_table[portnum][dev], kbuffer, n); + wrote = parport_write (pp->pdev->port, kbuffer, n); if (wrote < 0) { if (!bytes_written) @@ -265,19 +188,25 @@ static ssize_t pp_write (struct file * file, const char * buf, size_t count, } kfree (kbuffer); + enable_irq (pp); return bytes_written; } static void pp_irq (int irq, void * private, struct pt_regs * unused) { struct pp_struct * pp = (struct pp_struct *) private; + + if (pp->irqresponse) { + parport_write_control (pp->pdev->port, pp->irqctl); + pp->irqresponse = 0; + } + + atomic_inc (&pp->irqc); wake_up_interruptible (&pp->irq_wait); } -static int register_device (int minor) +static int register_device (int minor, struct pp_struct *pp) { - unsigned int portnum = PP_PORT (minor); - unsigned int dev = PP_DEV (minor); struct parport * port; struct pardevice * pdev = NULL; char *name; @@ -287,10 +216,10 @@ static int register_device (int minor) if (name == NULL) return -ENOMEM; - sprintf (name, CHRDEV "%02x", minor); + sprintf (name, CHRDEV "%x", minor); port = parport_enumerate (); /* FIXME: use attach/detach */ - while (port && port->number != portnum) + while (port && port->number != minor) port = port->next; if (!port) { @@ -299,9 +228,9 @@ static int register_device (int minor) return -ENXIO; } - fl = (pp_table[portnum][dev].flags & PP_EXCL) ? PARPORT_FLAG_EXCL : 0; + fl = (pp->flags & PP_EXCL) ? PARPORT_FLAG_EXCL : 0; pdev = parport_register_device (port, name, NULL, NULL, pp_irq, fl, - &pp_table[portnum][dev]); + pp); if (!pdev) { printk (KERN_WARNING "%s: failed to register device!\n", name); @@ -309,45 +238,68 @@ static int register_device (int minor) return -ENXIO; } - pp_table[portnum][dev].pdev = pdev; + pp->pdev = pdev; printk (KERN_DEBUG "%s: registered pardevice\n", name); return 0; } +static enum ieee1284_phase init_phase (int mode) +{ + switch (mode & ~(IEEE1284_DEVICEID + | IEEE1284_ADDR)) { + case IEEE1284_MODE_NIBBLE: + case IEEE1284_MODE_BYTE: + return IEEE1284_PH_REV_IDLE; + } + return IEEE1284_PH_FWD_IDLE; +} + static int pp_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { unsigned int minor = MINOR(inode->i_rdev); - unsigned int portnum = PP_PORT (minor); - unsigned int dev = PP_DEV (minor); + struct pp_struct *pp = file->private_data; struct parport * port; /* First handle the cases that don't take arguments. */ if (cmd == PPCLAIM) { - if (pp_table[portnum][dev].flags & PP_CLAIMED) { + struct ieee1284_info *info; + + if (pp->flags & PP_CLAIMED) { printk (KERN_DEBUG CHRDEV - "%02x: you've already got it!\n", minor); + "%x: you've already got it!\n", minor); return -EINVAL; } /* Deferred device registration. */ - if (!pp_table[portnum][dev].pdev) { - int err = register_device (minor); + if (!pp->pdev) { + int err = register_device (minor, pp); if (err) return err; } - parport_claim_or_block (pp_table[portnum][dev].pdev); - pp_table[portnum][dev].flags |= PP_CLAIMED; + parport_claim_or_block (pp->pdev); + pp->flags |= PP_CLAIMED; + + /* For interrupt-reporting to work, we need to be + * informed of each interrupt. */ + enable_irq (pp); + + /* We may need to fix up the state machine. */ + info = &pp->pdev->port->ieee1284; + pp->saved_state.mode = info->mode; + pp->saved_state.phase = info->phase; + info->mode = pp->state.mode; + info->phase = pp->state.phase; + return 0; } - port = pp_table[portnum][dev].pdev->port; if (cmd == PPEXCL) { - if (pp_table[portnum][dev].pdev) { - printk (KERN_DEBUG CHRDEV "%02x: too late for PPEXCL; " + if (pp->pdev) { + printk (KERN_DEBUG CHRDEV "%x: too late for PPEXCL; " "already registered\n", minor); - if (pp_table[portnum][dev].flags & PP_EXCL) + if (pp->flags & PP_EXCL) /* But it's not really an error. */ return 0; /* There's no chance of making the driver happy. */ @@ -356,22 +308,54 @@ static int pp_ioctl(struct inode *inode, struct file *file, /* Just remember to register the device exclusively * when we finally do the registration. */ - pp_table[portnum][dev].flags |= PP_EXCL; + pp->flags |= PP_EXCL; + return 0; + } + + if (cmd == PPSETMODE) { + int mode; + if (copy_from_user (&mode, (int *) arg, sizeof (mode))) + return -EFAULT; + /* FIXME: validate mode */ + pp->state.mode = mode; + pp->state.phase = init_phase (mode); + + if (pp->flags & PP_CLAIMED) { + pp->pdev->port->ieee1284.mode = mode; + pp->pdev->port->ieee1284.phase = pp->state.phase; + } + + return 0; + } + + if (cmd == PPSETPHASE) { + int phase; + if (copy_from_user (&phase, (int *) arg, sizeof (phase))) + return -EFAULT; + /* FIXME: validate phase */ + pp->state.phase = phase; + + if (pp->flags & PP_CLAIMED) + pp->pdev->port->ieee1284.phase = phase; + return 0; } /* Everything else requires the port to be claimed, so check * that now. */ - if ((pp_table[portnum][dev].flags & PP_CLAIMED) == 0) { - printk (KERN_DEBUG CHRDEV "%02x: claim the port first\n", + if ((pp->flags & PP_CLAIMED) == 0) { + printk (KERN_DEBUG CHRDEV "%x: claim the port first\n", minor); - return -EPERM; + return -EINVAL; } + port = pp->pdev->port; switch (cmd) { + struct ieee1284_info *info; unsigned char reg; unsigned char mask; int mode; + int ret; case PPRSTATUS: reg = parport_read_status (port); @@ -389,19 +373,18 @@ static int pp_ioctl(struct inode *inode, struct file *file, sizeof (reg)); case PPYIELD: - parport_yield_blocking (pp_table[portnum][dev].pdev); + parport_yield_blocking (pp->pdev); return 0; case PPRELEASE: - parport_release (pp_table[portnum][dev].pdev); - pp_table[portnum][dev].flags &= ~PP_CLAIMED; - return 0; - - case PPSETMODE: - if (copy_from_user (&mode, (int *) arg, sizeof (mode))) - return -EFAULT; - /* FIXME: validate mode */ - pp_table[portnum][dev].mode = mode; + /* Save the state machine's state. */ + info = &pp->pdev->port->ieee1284; + pp->state.mode = info->mode; + pp->state.phase = info->phase; + info->mode = pp->saved_state.mode; + info->phase = pp->saved_state.phase; + parport_release (pp->pdev); + pp->flags &= ~PP_CLAIMED; return 0; case PPWCONTROL: @@ -438,11 +421,38 @@ static int pp_ioctl(struct inode *inode, struct file *file, case PPNEGOT: if (copy_from_user (&mode, (int *) arg, sizeof (mode))) return -EFAULT; - /* FIXME: validate mode */ - return parport_negotiate (port, mode); + switch ((ret = parport_negotiate (port, mode))) { + case 0: break; + case -1: /* handshake failed, peripheral not IEEE 1284 */ + ret = -EIO; + break; + case 1: /* handshake succeeded, peripheral rejected mode */ + ret = -ENXIO; + break; + } + enable_irq (pp); + return ret; + + case PPWCTLONIRQ: + if (copy_from_user (®, (unsigned char *) arg, + sizeof (reg))) + return -EFAULT; + + /* Remember what to set the control lines to, for next + * time we get an interrupt. */ + pp->irqctl = reg; + pp->irqresponse = 1; + return 0; + + case PPCLRIRQ: + ret = atomic_read (&pp->irqc); + if (copy_to_user ((int *) arg, &ret, sizeof (ret))) + return -EFAULT; + atomic_sub (ret, &pp->irqc); + return 0; default: - printk (KERN_DEBUG CHRDEV "%02x: What? (cmd=0x%x\n", minor, + printk (KERN_DEBUG CHRDEV "%x: What? (cmd=0x%x)\n", minor, cmd); return -EINVAL; } @@ -454,24 +464,27 @@ static int pp_ioctl(struct inode *inode, struct file *file, static int pp_open (struct inode * inode, struct file * file) { unsigned int minor = MINOR (inode->i_rdev); - unsigned int portnum = PP_PORT (minor); - unsigned int dev = PP_DEV (minor); + struct pp_struct *pp; - if (portnum >= PARPORT_MAX) + if (minor >= PARPORT_MAX) return -ENXIO; - if (pp_table[portnum][dev].pdev) - return -EBUSY; + pp = kmalloc (GFP_KERNEL, sizeof (struct pp_struct)); + if (!pp) + return -ENOMEM; - pp_table[portnum][dev].mode = IEEE1284_MODE_COMPAT; - pp_table[portnum][dev].flags = 0; - init_waitqueue_head (&pp_table[portnum][dev].irq_wait); + pp->state.mode = IEEE1284_MODE_COMPAT; + pp->state.phase = init_phase (pp->state.mode); + pp->flags = 0; + atomic_set (&pp->irqc, 0); + init_waitqueue_head (&pp->irq_wait); /* Defer the actual device registration until the first claim. * That way, we know whether or not the driver wants to have * exclusive access to the port (PPEXCL). */ - pp_table[portnum][dev].pdev = NULL; + pp->pdev = NULL; + file->private_data = pp; MOD_INC_USE_COUNT; return 0; @@ -480,42 +493,46 @@ static int pp_open (struct inode * inode, struct file * file) static int pp_release (struct inode * inode, struct file * file) { unsigned int minor = MINOR (inode->i_rdev); - unsigned int portnum = PP_PORT (minor); - unsigned int dev = PP_DEV (minor); + struct pp_struct *pp = file->private_data; - if (pp_table[portnum][dev].flags & PP_CLAIMED) { - parport_release (pp_table[portnum][dev].pdev); - printk (KERN_DEBUG CHRDEV "%02x: released pardevice because " + if (pp->flags & PP_CLAIMED) { + parport_release (pp->pdev); + printk (KERN_DEBUG CHRDEV "%x: released pardevice because " "user-space forgot\n", minor); } - if (pp_table[portnum][dev].pdev) { - kfree (pp_table[portnum][dev].pdev->name); - parport_unregister_device (pp_table[portnum][dev].pdev); - pp_table[portnum][dev].pdev = NULL; - printk (KERN_DEBUG CHRDEV "%02x: unregistered pardevice\n", + if (pp->pdev) { + parport_unregister_device (pp->pdev); + kfree (pp->pdev->name); + pp->pdev = NULL; + printk (KERN_DEBUG CHRDEV "%x: unregistered pardevice\n", minor); } + kfree (pp); + MOD_DEC_USE_COUNT; return 0; } -#if 0 static unsigned int pp_poll (struct file * file, poll_table * wait) { - unsigned int minor = MINOR (file->f_dentry->d_inode->i_rdev); - poll_wait (file, &pp_table[minor].irq_wait, wait); - return 0; /* FIXME! Return value is wrong here */ + struct pp_struct *pp = file->private_data; + unsigned int mask = 0; + + if (atomic_read (&pp->irqc)) + mask |= POLLIN | POLLRDNORM; + + poll_wait (file, &pp->irq_wait, wait); + return mask; } -#endif static struct file_operations pp_fops = { pp_lseek, pp_read, pp_write, NULL, /* pp_readdir */ - NULL, /* pp_poll */ + pp_poll, pp_ioctl, NULL, /* pp_mmap */ pp_open, |