// SPDX-License-Identifier: GPL-2.0-or-later /* * linux/drivers/char/ppdev.c * * This is the code behind /dev/parport* -- it allows a user-space * application to use the parport subsystem. * * Copyright (C) 1998-2000, 2002 Tim Waugh <tim@cyberelk.net> * * 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 * close release port and unregister device (if necessary) * ioctl * EXCL register device exclusively (may fail) * 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 * WCONTROL write_control * RCONTROL read_control * FCONTROL frob_control * RSTATUS read_status * NEGOT parport_negotiate * YIELD parport_yield_blocking * WCTLONIRQ on interrupt, set control lines * CLRIRQ clear (and return) interrupt count * SETTIME sets device timeout (struct timeval) * GETTIME gets device timeout (struct timeval) * GETMODES gets hardware supported modes (unsigned int) * GETMODE gets the current IEEE1284 mode * GETPHASE gets the current IEEE1284 phase * GETFLAGS gets current (user-visible) flags * SETFLAGS sets current (user-visible) flags * read/write read or write in current IEEE 1284 protocol * select wait for interrupt (in readfds) * * Changes: * Added SETTIME/GETTIME ioctl, Fred Barnes, 1999. * * Arnaldo Carvalho de Melo <acme@conectiva.com.br> 2000/08/25 * - On error, copy_from_user and copy_to_user do not return -EFAULT, * They return the positive number of bytes *not* copied due to address * space errors. * * Added GETMODES/GETMODE/GETPHASE ioctls, Fred Barnes <frmb2@ukc.ac.uk>, 03/01/2001. * Added GETFLAGS/SETFLAGS ioctls, Fred Barnes, 04/2001
*/
if ((pp->flags & PP_FASTWRITE) && (mode == IEEE1284_MODE_EPP)) { /* do a fast EPP write */ if (pport->ieee1284.mode & IEEE1284_ADDR) {
wrote = pport->ops->epp_write_addr(pport,
kbuffer, n, PARPORT_EPP_FAST);
} else {
wrote = pport->ops->epp_write_data(pport,
kbuffer, n, PARPORT_EPP_FAST);
}
} else {
wrote = parport_write(pp->pdev->port, kbuffer, n);
}
if (wrote <= 0) { if (!bytes_written)
bytes_written = wrote; break;
}
bytes_written += wrote;
if (file->f_flags & O_NONBLOCK) { if (!bytes_written)
bytes_written = -EAGAIN; break;
}
/* First handle the cases that don't take arguments. */ switch (cmd) { case PPCLAIM:
{ if (pp->flags & PP_CLAIMED) {
dev_dbg(&pp->pdev->dev, "you've already got it!\n"); return -EINVAL;
}
/* Deferred device registration. */ if (!pp->pdev) { int err = register_device(minor, pp);
if (err) return err;
}
ret = parport_claim_or_block(pp->pdev); if (ret < 0) return ret;
pp->flags |= PP_CLAIMED;
/* For interrupt-reporting to work, we need to be
* informed of each interrupt. */
pp_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;
pp->default_inactivity = parport_set_timeout(pp->pdev, 0);
parport_set_timeout(pp->pdev, pp->default_inactivity);
return 0;
} case PPEXCL: if (pp->pdev) {
dev_dbg(&pp->pdev->dev, "too late for PPEXCL; already registered\n"); if (pp->flags & PP_EXCL) /* But it's not really an error. */ return 0; /* There's no chance of making the driver happy. */ return -EINVAL;
}
/* Just remember to register the device exclusively
* when we finally do the registration. */
pp->flags |= PP_EXCL; return 0; case PPSETMODE:
{ int mode;
if (pp->flags & PP_CLAIMED)
pp->pdev->port->ieee1284.phase = phase;
return 0;
} case PPGETPHASE:
{ int phase;
if (pp->flags & PP_CLAIMED)
phase = pp->pdev->port->ieee1284.phase; else
phase = pp->state.phase; if (copy_to_user(argp, &phase, sizeof(phase))) return -EFAULT; return 0;
} case PPGETMODES:
{ unsignedint modes;
port = parport_find_number(minor); if (!port) return -ENODEV;
modes = port->modes;
parport_put_port(port); if (copy_to_user(argp, &modes, sizeof(modes))) return -EFAULT; return 0;
} case PPSETFLAGS:
{ int uflags;
if (copy_from_user(&uflags, argp, sizeof(uflags))) return -EFAULT;
pp->flags &= ~PP_FLAGMASK;
pp->flags |= (uflags & PP_FLAGMASK); return 0;
} case PPGETFLAGS:
{ int uflags;
uflags = pp->flags & PP_FLAGMASK; if (copy_to_user(argp, &uflags, sizeof(uflags))) return -EFAULT; return 0;
}
} /* end switch() */
/* Everything else requires the port to be claimed, so check
* that now. */ if ((pp->flags & PP_CLAIMED) == 0) {
pr_debug(CHRDEV "%x: claim the port first\n", minor); return -EINVAL;
}
port = pp->pdev->port; switch (cmd) { case PPRSTATUS:
reg = parport_read_status(port); if (copy_to_user(argp, ®, sizeof(reg))) return -EFAULT; return 0; case PPRDATA:
reg = parport_read_data(port); if (copy_to_user(argp, ®, sizeof(reg))) return -EFAULT; return 0; case PPRCONTROL:
reg = parport_read_control(port); if (copy_to_user(argp, ®, sizeof(reg))) return -EFAULT; return 0; case PPYIELD:
parport_yield_blocking(pp->pdev); return 0;
case PPRELEASE: /* 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: if (copy_from_user(®, argp, sizeof(reg))) return -EFAULT;
parport_write_control(port, reg); return 0;
case PPWDATA: if (copy_from_user(®, argp, sizeof(reg))) return -EFAULT;
parport_write_data(port, reg); return 0;
case PPFCONTROL: if (copy_from_user(&mask, argp, sizeof(mask))) return -EFAULT; if (copy_from_user(®, 1 + (unsignedchar __user *) arg, sizeof(reg))) return -EFAULT;
parport_frob_control(port, mask, reg); return 0;
case PPDATADIR: if (copy_from_user(&mode, argp, sizeof(mode))) return -EFAULT; if (mode)
port->ops->data_reverse(port); else
port->ops->data_forward(port); return 0;
case PPNEGOT: if (copy_from_user(&mode, argp, sizeof(mode))) return -EFAULT; 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;
}
pp_enable_irq(pp); return ret;
case PPWCTLONIRQ: if (copy_from_user(®, argp, 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(argp, &ret, sizeof(ret))) return -EFAULT;
atomic_sub(ret, &pp->irqc); return 0;
case PPSETTIME32: if (copy_from_user(time32, argp, sizeof(time32))) return -EFAULT;
if ((time32[0] < 0) || (time32[1] < 0)) return -EINVAL;
/* 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->pdev = NULL;
file->private_data = pp;
return 0;
}
staticint pp_release(struct inode *inode, struct file *file)
{ unsignedint minor = iminor(inode); struct pp_struct *pp = file->private_data; int compat_negot;
if (register_chrdev(PP_MAJOR, CHRDEV, &pp_fops)) {
pr_warn(CHRDEV ": unable to get major %d\n", PP_MAJOR); return -EIO;
}
err = class_register(&ppdev_class); if (err) goto out_chrdev;
err = parport_register_driver(&pp_driver); if (err < 0) {
pr_warn(CHRDEV ": unable to register with parport\n"); goto out_class;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.