summaryrefslogtreecommitdiffstats
path: root/drivers/char/ppdev.c
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>1999-10-09 00:00:47 +0000
committerRalf Baechle <ralf@linux-mips.org>1999-10-09 00:00:47 +0000
commitd6434e1042f3b0a6dfe1b1f615af369486f9b1fa (patch)
treee2be02f33984c48ec019c654051d27964e42c441 /drivers/char/ppdev.c
parent609d1e803baf519487233b765eb487f9ec227a18 (diff)
Merge with 2.3.19.
Diffstat (limited to 'drivers/char/ppdev.c')
-rw-r--r--drivers/char/ppdev.c349
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 (&reg, (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,