root/drivers/char/ppdev.c
// 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
 */

#include <linux/module.h>
#include <linux/init.h>
#include <linux/sched/signal.h>
#include <linux/device.h>
#include <linux/ioctl.h>
#include <linux/parport.h>
#include <linux/ctype.h>
#include <linux/poll.h>
#include <linux/slab.h>
#include <linux/major.h>
#include <linux/ppdev.h>
#include <linux/mutex.h>
#include <linux/uaccess.h>
#include <linux/compat.h>

#define PP_VERSION "ppdev: user-space parallel port driver"
#define CHRDEV "ppdev"

struct pp_struct {
        struct pardevice *pdev;
        wait_queue_head_t irq_wait;
        atomic_t irqc;
        unsigned int flags;
        int irqresponse;
        unsigned char irqctl;
        struct ieee1284_info state;
        struct ieee1284_info saved_state;
        long default_inactivity;
        int index;
};

/* should we use PARDEVICE_MAX here? */
static struct device *devices[PARPORT_MAX];

static DEFINE_IDA(ida_index);

/* pp_struct.flags bitfields */
#define PP_CLAIMED    (1<<0)
#define PP_EXCL       (1<<1)

/* Other constants */
#define PP_INTERRUPT_TIMEOUT (10 * HZ) /* 10s */
#define PP_BUFFER_SIZE 1024
#define PARDEVICE_MAX 8

static DEFINE_MUTEX(pp_do_mutex);

/* define fixed sized ioctl cmd for y2038 migration */
#define PPGETTIME32     _IOR(PP_IOCTL, 0x95, s32[2])
#define PPSETTIME32     _IOW(PP_IOCTL, 0x96, s32[2])
#define PPGETTIME64     _IOR(PP_IOCTL, 0x95, s64[2])
#define PPSETTIME64     _IOW(PP_IOCTL, 0x96, s64[2])

static inline void pp_enable_irq(struct pp_struct *pp)
{
        struct parport *port = pp->pdev->port;

        port->ops->enable_irq(port);
}

static ssize_t pp_read(struct file *file, char __user *buf, size_t count,
                       loff_t *ppos)
{
        unsigned int minor = iminor(file_inode(file));
        struct pp_struct *pp = file->private_data;
        char *kbuffer;
        ssize_t bytes_read = 0;
        struct parport *pport;
        int mode;

        if (!(pp->flags & PP_CLAIMED)) {
                /* Don't have the port claimed */
                pr_debug(CHRDEV "%x: claim the port first\n", minor);
                return -EINVAL;
        }

        /* Trivial case. */
        if (count == 0)
                return 0;

        kbuffer = kmalloc(min_t(size_t, count, PP_BUFFER_SIZE), GFP_KERNEL);
        if (!kbuffer)
                return -ENOMEM;
        pport = pp->pdev->port;
        mode = pport->ieee1284.mode & ~(IEEE1284_DEVICEID | IEEE1284_ADDR);

        parport_set_timeout(pp->pdev,
                            (file->f_flags & O_NONBLOCK) ?
                            PARPORT_INACTIVITY_O_NONBLOCK :
                            pp->default_inactivity);

        while (bytes_read == 0) {
                ssize_t need = min_t(unsigned long, count, PP_BUFFER_SIZE);

                if (mode == IEEE1284_MODE_EPP) {
                        /* various specials for EPP mode */
                        int flags = 0;
                        size_t (*fn)(struct parport *, void *, size_t, int);

                        if (pp->flags & PP_W91284PIC)
                                flags |= PARPORT_W91284PIC;
                        if (pp->flags & PP_FASTREAD)
                                flags |= PARPORT_EPP_FAST;
                        if (pport->ieee1284.mode & IEEE1284_ADDR)
                                fn = pport->ops->epp_read_addr;
                        else
                                fn = pport->ops->epp_read_data;
                        bytes_read = (*fn)(pport, kbuffer, need, flags);
                } else {
                        bytes_read = parport_read(pport, kbuffer, need);
                }

                if (bytes_read != 0)
                        break;

                if (file->f_flags & O_NONBLOCK) {
                        bytes_read = -EAGAIN;
                        break;
                }

                if (signal_pending(current)) {
                        bytes_read = -ERESTARTSYS;
                        break;
                }

                cond_resched();
        }

        parport_set_timeout(pp->pdev, pp->default_inactivity);

        if (bytes_read > 0 && copy_to_user(buf, kbuffer, bytes_read))
                bytes_read = -EFAULT;

        kfree(kbuffer);
        pp_enable_irq(pp);
        return bytes_read;
}

static ssize_t pp_write(struct file *file, const char __user *buf,
                        size_t count, loff_t *ppos)
{
        unsigned int minor = iminor(file_inode(file));
        struct pp_struct *pp = file->private_data;
        char *kbuffer;
        ssize_t bytes_written = 0;
        ssize_t wrote;
        int mode;
        struct parport *pport;

        if (!(pp->flags & PP_CLAIMED)) {
                /* Don't have the port claimed */
                pr_debug(CHRDEV "%x: claim the port first\n", minor);
                return -EINVAL;
        }

        kbuffer = kmalloc(min_t(size_t, count, PP_BUFFER_SIZE), GFP_KERNEL);
        if (!kbuffer)
                return -ENOMEM;

        pport = pp->pdev->port;
        mode = pport->ieee1284.mode & ~(IEEE1284_DEVICEID | IEEE1284_ADDR);

        parport_set_timeout(pp->pdev,
                            (file->f_flags & O_NONBLOCK) ?
                            PARPORT_INACTIVITY_O_NONBLOCK :
                            pp->default_inactivity);

        while (bytes_written < count) {
                ssize_t n = min_t(unsigned long, count - bytes_written, PP_BUFFER_SIZE);

                if (copy_from_user(kbuffer, buf + bytes_written, n)) {
                        bytes_written = -EFAULT;
                        break;
                }

                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;
                }

                if (signal_pending(current))
                        break;

                cond_resched();
        }

        parport_set_timeout(pp->pdev, pp->default_inactivity);

        kfree(kbuffer);
        pp_enable_irq(pp);
        return bytes_written;
}

static void pp_irq(void *private)
{
        struct pp_struct *pp = 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, struct pp_struct *pp)
{
        struct parport *port;
        struct pardevice *pdev = NULL;
        char *name;
        struct pardev_cb ppdev_cb;
        int rc = 0, index;

        name = kasprintf(GFP_KERNEL, CHRDEV "%x", minor);
        if (name == NULL)
                return -ENOMEM;

        port = parport_find_number(minor);
        if (!port) {
                pr_warn("%s: no associated port!\n", name);
                rc = -ENXIO;
                goto err_free_name;
        }

        index = ida_alloc(&ida_index, GFP_KERNEL);
        if (index < 0) {
                pr_warn("%s: failed to get index!\n", name);
                rc = index;
                goto err_put_port;
        }

        memset(&ppdev_cb, 0, sizeof(ppdev_cb));
        ppdev_cb.irq_func = pp_irq;
        ppdev_cb.flags = (pp->flags & PP_EXCL) ? PARPORT_FLAG_EXCL : 0;
        ppdev_cb.private = pp;
        pdev = parport_register_dev_model(port, name, &ppdev_cb, index);

        if (!pdev) {
                pr_warn("%s: failed to register device!\n", name);
                rc = -ENXIO;
                ida_free(&ida_index, index);
                goto err_put_port;
        }

        pp->pdev = pdev;
        pp->index = index;
        dev_dbg(&pdev->dev, "registered pardevice\n");
err_put_port:
        parport_put_port(port);
err_free_name:
        kfree(name);
        return rc;
}

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_set_timeout(struct pardevice *pdev, long tv_sec, int tv_usec)
{
        long to_jiffies;

        if ((tv_sec < 0) || (tv_usec < 0))
                return -EINVAL;

        to_jiffies = usecs_to_jiffies(tv_usec);
        to_jiffies += tv_sec * HZ;
        if (to_jiffies <= 0)
                return -EINVAL;

        pdev->timeout = to_jiffies;
        return 0;
}

static int pp_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
        unsigned int minor = iminor(file_inode(file));
        struct pp_struct *pp = file->private_data;
        struct parport *port;
        void __user *argp = (void __user *)arg;
        struct ieee1284_info *info;
        unsigned char reg;
        unsigned char mask;
        int mode;
        s32 time32[2];
        s64 time64[2];
        struct timespec64 ts;
        int ret;

        /* 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 (copy_from_user(&mode, argp, 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;
            }
        case PPGETMODE:
            {
                int mode;

                if (pp->flags & PP_CLAIMED)
                        mode = pp->pdev->port->ieee1284.mode;
                else
                        mode = pp->state.mode;

                if (copy_to_user(argp, &mode, sizeof(mode)))
                        return -EFAULT;
                return 0;
            }
        case PPSETPHASE:
            {
                int phase;

                if (copy_from_user(&phase, argp, sizeof(phase)))
                        return -EFAULT;

                /* FIXME: validate phase */
                pp->state.phase = phase;

                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:
            {
                unsigned int 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, &reg, sizeof(reg)))
                        return -EFAULT;
                return 0;
        case PPRDATA:
                reg = parport_read_data(port);
                if (copy_to_user(argp, &reg, sizeof(reg)))
                        return -EFAULT;
                return 0;
        case PPRCONTROL:
                reg = parport_read_control(port);
                if (copy_to_user(argp, &reg, 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(&reg, argp, sizeof(reg)))
                        return -EFAULT;
                parport_write_control(port, reg);
                return 0;

        case PPWDATA:
                if (copy_from_user(&reg, 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(&reg, 1 + (unsigned char __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(&reg, 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;

                return pp_set_timeout(pp->pdev, time32[0], time32[1]);

        case PPSETTIME64:
                if (copy_from_user(time64, argp, sizeof(time64)))
                        return -EFAULT;

                if ((time64[0] < 0) || (time64[1] < 0))
                        return -EINVAL;

                if (IS_ENABLED(CONFIG_SPARC64) && !in_compat_syscall())
                        time64[1] >>= 32;

                return pp_set_timeout(pp->pdev, time64[0], time64[1]);

        case PPGETTIME32:
                jiffies_to_timespec64(pp->pdev->timeout, &ts);
                time32[0] = ts.tv_sec;
                time32[1] = ts.tv_nsec / NSEC_PER_USEC;

                if (copy_to_user(argp, time32, sizeof(time32)))
                        return -EFAULT;

                return 0;

        case PPGETTIME64:
                jiffies_to_timespec64(pp->pdev->timeout, &ts);
                time64[0] = ts.tv_sec;
                time64[1] = ts.tv_nsec / NSEC_PER_USEC;

                if (IS_ENABLED(CONFIG_SPARC64) && !in_compat_syscall())
                        time64[1] <<= 32;

                if (copy_to_user(argp, time64, sizeof(time64)))
                        return -EFAULT;

                return 0;

        default:
                dev_dbg(&pp->pdev->dev, "What? (cmd=0x%x)\n", cmd);
                return -EINVAL;
        }

        /* Keep the compiler happy */
        return 0;
}

static long pp_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
        long ret;

        mutex_lock(&pp_do_mutex);
        ret = pp_do_ioctl(file, cmd, arg);
        mutex_unlock(&pp_do_mutex);
        return ret;
}

static int pp_open(struct inode *inode, struct file *file)
{
        unsigned int minor = iminor(inode);
        struct pp_struct *pp;

        if (minor >= PARPORT_MAX)
                return -ENXIO;

        pp = kmalloc_obj(struct pp_struct);
        if (!pp)
                return -ENOMEM;

        pp->state.mode = IEEE1284_MODE_COMPAT;
        pp->state.phase = init_phase(pp->state.mode);
        pp->flags = 0;
        pp->irqresponse = 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->pdev = NULL;
        file->private_data = pp;

        return 0;
}

static int pp_release(struct inode *inode, struct file *file)
{
        unsigned int minor = iminor(inode);
        struct pp_struct *pp = file->private_data;
        int compat_negot;

        compat_negot = 0;
        if (!(pp->flags & PP_CLAIMED) && pp->pdev &&
            (pp->state.mode != IEEE1284_MODE_COMPAT)) {
                struct ieee1284_info *info;

                /* parport released, but not in compatibility mode */
                parport_claim_or_block(pp->pdev);
                pp->flags |= PP_CLAIMED;
                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;
                compat_negot = 1;
        } else if ((pp->flags & PP_CLAIMED) && pp->pdev &&
            (pp->pdev->port->ieee1284.mode != IEEE1284_MODE_COMPAT)) {
                compat_negot = 2;
        }
        if (compat_negot) {
                parport_negotiate(pp->pdev->port, IEEE1284_MODE_COMPAT);
                dev_dbg(&pp->pdev->dev,
                        "negotiated back to compatibility mode because user-space forgot\n");
        }

        if ((pp->flags & PP_CLAIMED) && pp->pdev) {
                struct ieee1284_info *info;

                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);
                if (compat_negot != 1) {
                        pr_debug(CHRDEV "%x: released pardevice "
                                "because user-space forgot\n", minor);
                }
        }

        if (pp->pdev) {
                parport_unregister_device(pp->pdev);
                ida_free(&ida_index, pp->index);
                pp->pdev = NULL;
                pr_debug(CHRDEV "%x: unregistered pardevice\n", minor);
        }

        kfree(pp);

        return 0;
}

/* No kernel lock held - fine */
static __poll_t pp_poll(struct file *file, poll_table *wait)
{
        struct pp_struct *pp = file->private_data;
        __poll_t mask = 0;

        poll_wait(file, &pp->irq_wait, wait);
        if (atomic_read(&pp->irqc))
                mask |= EPOLLIN | EPOLLRDNORM;

        return mask;
}

static const struct class ppdev_class = {
        .name = CHRDEV,
};

static const struct file_operations pp_fops = {
        .owner          = THIS_MODULE,
        .read           = pp_read,
        .write          = pp_write,
        .poll           = pp_poll,
        .unlocked_ioctl = pp_ioctl,
        .compat_ioctl   = compat_ptr_ioctl,
        .open           = pp_open,
        .release        = pp_release,
};

static void pp_attach(struct parport *port)
{
        struct device *ret;

        if (devices[port->number])
                return;

        ret = device_create(&ppdev_class, port->dev,
                            MKDEV(PP_MAJOR, port->number), NULL,
                            "parport%d", port->number);
        if (IS_ERR(ret)) {
                pr_err("Failed to create device parport%d\n",
                       port->number);
                return;
        }
        devices[port->number] = ret;
}

static void pp_detach(struct parport *port)
{
        if (!devices[port->number])
                return;

        device_destroy(&ppdev_class, MKDEV(PP_MAJOR, port->number));
        devices[port->number] = NULL;
}

static int pp_probe(struct pardevice *par_dev)
{
        struct device_driver *drv = par_dev->dev.driver;
        int len = strlen(drv->name);

        if (strncmp(par_dev->name, drv->name, len))
                return -ENODEV;

        return 0;
}

static struct parport_driver pp_driver = {
        .name           = CHRDEV,
        .probe          = pp_probe,
        .match_port     = pp_attach,
        .detach         = pp_detach,
};

static int __init ppdev_init(void)
{
        int err = 0;

        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;
        }

        pr_info(PP_VERSION "\n");
        goto out;

out_class:
        class_unregister(&ppdev_class);
out_chrdev:
        unregister_chrdev(PP_MAJOR, CHRDEV);
out:
        return err;
}

static void __exit ppdev_cleanup(void)
{
        /* Clean up all parport stuff */
        parport_unregister_driver(&pp_driver);
        class_unregister(&ppdev_class);
        unregister_chrdev(PP_MAJOR, CHRDEV);
}

module_init(ppdev_init);
module_exit(ppdev_cleanup);

MODULE_DESCRIPTION("Support for user-space parallel port device drivers");
MODULE_LICENSE("GPL");
MODULE_ALIAS_CHARDEV_MAJOR(PP_MAJOR);