18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * linux/drivers/char/ppdev.c
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * This is the code behind /dev/parport* -- it allows a user-space
68c2ecf20Sopenharmony_ci * application to use the parport subsystem.
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * Copyright (C) 1998-2000, 2002 Tim Waugh <tim@cyberelk.net>
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci * A /dev/parportx device node represents an arbitrary device
118c2ecf20Sopenharmony_ci * on port 'x'.  The following operations are possible:
128c2ecf20Sopenharmony_ci *
138c2ecf20Sopenharmony_ci * open		do nothing, set up default IEEE 1284 protocol to be COMPAT
148c2ecf20Sopenharmony_ci * close	release port and unregister device (if necessary)
158c2ecf20Sopenharmony_ci * ioctl
168c2ecf20Sopenharmony_ci *   EXCL	register device exclusively (may fail)
178c2ecf20Sopenharmony_ci *   CLAIM	(register device first time) parport_claim_or_block
188c2ecf20Sopenharmony_ci *   RELEASE	parport_release
198c2ecf20Sopenharmony_ci *   SETMODE	set the IEEE 1284 protocol to use for read/write
208c2ecf20Sopenharmony_ci *   SETPHASE	set the IEEE 1284 phase of a particular mode.  Not to be
218c2ecf20Sopenharmony_ci *              confused with ioctl(fd, SETPHASER, &stun). ;-)
228c2ecf20Sopenharmony_ci *   DATADIR	data_forward / data_reverse
238c2ecf20Sopenharmony_ci *   WDATA	write_data
248c2ecf20Sopenharmony_ci *   RDATA	read_data
258c2ecf20Sopenharmony_ci *   WCONTROL	write_control
268c2ecf20Sopenharmony_ci *   RCONTROL	read_control
278c2ecf20Sopenharmony_ci *   FCONTROL	frob_control
288c2ecf20Sopenharmony_ci *   RSTATUS	read_status
298c2ecf20Sopenharmony_ci *   NEGOT	parport_negotiate
308c2ecf20Sopenharmony_ci *   YIELD	parport_yield_blocking
318c2ecf20Sopenharmony_ci *   WCTLONIRQ	on interrupt, set control lines
328c2ecf20Sopenharmony_ci *   CLRIRQ	clear (and return) interrupt count
338c2ecf20Sopenharmony_ci *   SETTIME	sets device timeout (struct timeval)
348c2ecf20Sopenharmony_ci *   GETTIME	gets device timeout (struct timeval)
358c2ecf20Sopenharmony_ci *   GETMODES	gets hardware supported modes (unsigned int)
368c2ecf20Sopenharmony_ci *   GETMODE	gets the current IEEE1284 mode
378c2ecf20Sopenharmony_ci *   GETPHASE   gets the current IEEE1284 phase
388c2ecf20Sopenharmony_ci *   GETFLAGS   gets current (user-visible) flags
398c2ecf20Sopenharmony_ci *   SETFLAGS   sets current (user-visible) flags
408c2ecf20Sopenharmony_ci * read/write	read or write in current IEEE 1284 protocol
418c2ecf20Sopenharmony_ci * select	wait for interrupt (in readfds)
428c2ecf20Sopenharmony_ci *
438c2ecf20Sopenharmony_ci * Changes:
448c2ecf20Sopenharmony_ci * Added SETTIME/GETTIME ioctl, Fred Barnes, 1999.
458c2ecf20Sopenharmony_ci *
468c2ecf20Sopenharmony_ci * Arnaldo Carvalho de Melo <acme@conectiva.com.br> 2000/08/25
478c2ecf20Sopenharmony_ci * - On error, copy_from_user and copy_to_user do not return -EFAULT,
488c2ecf20Sopenharmony_ci *   They return the positive number of bytes *not* copied due to address
498c2ecf20Sopenharmony_ci *   space errors.
508c2ecf20Sopenharmony_ci *
518c2ecf20Sopenharmony_ci * Added GETMODES/GETMODE/GETPHASE ioctls, Fred Barnes <frmb2@ukc.ac.uk>, 03/01/2001.
528c2ecf20Sopenharmony_ci * Added GETFLAGS/SETFLAGS ioctls, Fred Barnes, 04/2001
538c2ecf20Sopenharmony_ci */
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci#include <linux/module.h>
568c2ecf20Sopenharmony_ci#include <linux/init.h>
578c2ecf20Sopenharmony_ci#include <linux/sched/signal.h>
588c2ecf20Sopenharmony_ci#include <linux/device.h>
598c2ecf20Sopenharmony_ci#include <linux/ioctl.h>
608c2ecf20Sopenharmony_ci#include <linux/parport.h>
618c2ecf20Sopenharmony_ci#include <linux/ctype.h>
628c2ecf20Sopenharmony_ci#include <linux/poll.h>
638c2ecf20Sopenharmony_ci#include <linux/slab.h>
648c2ecf20Sopenharmony_ci#include <linux/major.h>
658c2ecf20Sopenharmony_ci#include <linux/ppdev.h>
668c2ecf20Sopenharmony_ci#include <linux/mutex.h>
678c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
688c2ecf20Sopenharmony_ci#include <linux/compat.h>
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci#define PP_VERSION "ppdev: user-space parallel port driver"
718c2ecf20Sopenharmony_ci#define CHRDEV "ppdev"
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_cistruct pp_struct {
748c2ecf20Sopenharmony_ci	struct pardevice *pdev;
758c2ecf20Sopenharmony_ci	wait_queue_head_t irq_wait;
768c2ecf20Sopenharmony_ci	atomic_t irqc;
778c2ecf20Sopenharmony_ci	unsigned int flags;
788c2ecf20Sopenharmony_ci	int irqresponse;
798c2ecf20Sopenharmony_ci	unsigned char irqctl;
808c2ecf20Sopenharmony_ci	struct ieee1284_info state;
818c2ecf20Sopenharmony_ci	struct ieee1284_info saved_state;
828c2ecf20Sopenharmony_ci	long default_inactivity;
838c2ecf20Sopenharmony_ci	int index;
848c2ecf20Sopenharmony_ci};
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci/* should we use PARDEVICE_MAX here? */
878c2ecf20Sopenharmony_cistatic struct device *devices[PARPORT_MAX];
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_cistatic DEFINE_IDA(ida_index);
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci/* pp_struct.flags bitfields */
928c2ecf20Sopenharmony_ci#define PP_CLAIMED    (1<<0)
938c2ecf20Sopenharmony_ci#define PP_EXCL       (1<<1)
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci/* Other constants */
968c2ecf20Sopenharmony_ci#define PP_INTERRUPT_TIMEOUT (10 * HZ) /* 10s */
978c2ecf20Sopenharmony_ci#define PP_BUFFER_SIZE 1024
988c2ecf20Sopenharmony_ci#define PARDEVICE_MAX 8
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(pp_do_mutex);
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci/* define fixed sized ioctl cmd for y2038 migration */
1038c2ecf20Sopenharmony_ci#define PPGETTIME32	_IOR(PP_IOCTL, 0x95, s32[2])
1048c2ecf20Sopenharmony_ci#define PPSETTIME32	_IOW(PP_IOCTL, 0x96, s32[2])
1058c2ecf20Sopenharmony_ci#define PPGETTIME64	_IOR(PP_IOCTL, 0x95, s64[2])
1068c2ecf20Sopenharmony_ci#define PPSETTIME64	_IOW(PP_IOCTL, 0x96, s64[2])
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_cistatic inline void pp_enable_irq(struct pp_struct *pp)
1098c2ecf20Sopenharmony_ci{
1108c2ecf20Sopenharmony_ci	struct parport *port = pp->pdev->port;
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	port->ops->enable_irq(port);
1138c2ecf20Sopenharmony_ci}
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_cistatic ssize_t pp_read(struct file *file, char __user *buf, size_t count,
1168c2ecf20Sopenharmony_ci		       loff_t *ppos)
1178c2ecf20Sopenharmony_ci{
1188c2ecf20Sopenharmony_ci	unsigned int minor = iminor(file_inode(file));
1198c2ecf20Sopenharmony_ci	struct pp_struct *pp = file->private_data;
1208c2ecf20Sopenharmony_ci	char *kbuffer;
1218c2ecf20Sopenharmony_ci	ssize_t bytes_read = 0;
1228c2ecf20Sopenharmony_ci	struct parport *pport;
1238c2ecf20Sopenharmony_ci	int mode;
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	if (!(pp->flags & PP_CLAIMED)) {
1268c2ecf20Sopenharmony_ci		/* Don't have the port claimed */
1278c2ecf20Sopenharmony_ci		pr_debug(CHRDEV "%x: claim the port first\n", minor);
1288c2ecf20Sopenharmony_ci		return -EINVAL;
1298c2ecf20Sopenharmony_ci	}
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	/* Trivial case. */
1328c2ecf20Sopenharmony_ci	if (count == 0)
1338c2ecf20Sopenharmony_ci		return 0;
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	kbuffer = kmalloc(min_t(size_t, count, PP_BUFFER_SIZE), GFP_KERNEL);
1368c2ecf20Sopenharmony_ci	if (!kbuffer)
1378c2ecf20Sopenharmony_ci		return -ENOMEM;
1388c2ecf20Sopenharmony_ci	pport = pp->pdev->port;
1398c2ecf20Sopenharmony_ci	mode = pport->ieee1284.mode & ~(IEEE1284_DEVICEID | IEEE1284_ADDR);
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	parport_set_timeout(pp->pdev,
1428c2ecf20Sopenharmony_ci			    (file->f_flags & O_NONBLOCK) ?
1438c2ecf20Sopenharmony_ci			    PARPORT_INACTIVITY_O_NONBLOCK :
1448c2ecf20Sopenharmony_ci			    pp->default_inactivity);
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	while (bytes_read == 0) {
1478c2ecf20Sopenharmony_ci		ssize_t need = min_t(unsigned long, count, PP_BUFFER_SIZE);
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci		if (mode == IEEE1284_MODE_EPP) {
1508c2ecf20Sopenharmony_ci			/* various specials for EPP mode */
1518c2ecf20Sopenharmony_ci			int flags = 0;
1528c2ecf20Sopenharmony_ci			size_t (*fn)(struct parport *, void *, size_t, int);
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci			if (pp->flags & PP_W91284PIC)
1558c2ecf20Sopenharmony_ci				flags |= PARPORT_W91284PIC;
1568c2ecf20Sopenharmony_ci			if (pp->flags & PP_FASTREAD)
1578c2ecf20Sopenharmony_ci				flags |= PARPORT_EPP_FAST;
1588c2ecf20Sopenharmony_ci			if (pport->ieee1284.mode & IEEE1284_ADDR)
1598c2ecf20Sopenharmony_ci				fn = pport->ops->epp_read_addr;
1608c2ecf20Sopenharmony_ci			else
1618c2ecf20Sopenharmony_ci				fn = pport->ops->epp_read_data;
1628c2ecf20Sopenharmony_ci			bytes_read = (*fn)(pport, kbuffer, need, flags);
1638c2ecf20Sopenharmony_ci		} else {
1648c2ecf20Sopenharmony_ci			bytes_read = parport_read(pport, kbuffer, need);
1658c2ecf20Sopenharmony_ci		}
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci		if (bytes_read != 0)
1688c2ecf20Sopenharmony_ci			break;
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci		if (file->f_flags & O_NONBLOCK) {
1718c2ecf20Sopenharmony_ci			bytes_read = -EAGAIN;
1728c2ecf20Sopenharmony_ci			break;
1738c2ecf20Sopenharmony_ci		}
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci		if (signal_pending(current)) {
1768c2ecf20Sopenharmony_ci			bytes_read = -ERESTARTSYS;
1778c2ecf20Sopenharmony_ci			break;
1788c2ecf20Sopenharmony_ci		}
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci		cond_resched();
1818c2ecf20Sopenharmony_ci	}
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	parport_set_timeout(pp->pdev, pp->default_inactivity);
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	if (bytes_read > 0 && copy_to_user(buf, kbuffer, bytes_read))
1868c2ecf20Sopenharmony_ci		bytes_read = -EFAULT;
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	kfree(kbuffer);
1898c2ecf20Sopenharmony_ci	pp_enable_irq(pp);
1908c2ecf20Sopenharmony_ci	return bytes_read;
1918c2ecf20Sopenharmony_ci}
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_cistatic ssize_t pp_write(struct file *file, const char __user *buf,
1948c2ecf20Sopenharmony_ci			size_t count, loff_t *ppos)
1958c2ecf20Sopenharmony_ci{
1968c2ecf20Sopenharmony_ci	unsigned int minor = iminor(file_inode(file));
1978c2ecf20Sopenharmony_ci	struct pp_struct *pp = file->private_data;
1988c2ecf20Sopenharmony_ci	char *kbuffer;
1998c2ecf20Sopenharmony_ci	ssize_t bytes_written = 0;
2008c2ecf20Sopenharmony_ci	ssize_t wrote;
2018c2ecf20Sopenharmony_ci	int mode;
2028c2ecf20Sopenharmony_ci	struct parport *pport;
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	if (!(pp->flags & PP_CLAIMED)) {
2058c2ecf20Sopenharmony_ci		/* Don't have the port claimed */
2068c2ecf20Sopenharmony_ci		pr_debug(CHRDEV "%x: claim the port first\n", minor);
2078c2ecf20Sopenharmony_ci		return -EINVAL;
2088c2ecf20Sopenharmony_ci	}
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	kbuffer = kmalloc(min_t(size_t, count, PP_BUFFER_SIZE), GFP_KERNEL);
2118c2ecf20Sopenharmony_ci	if (!kbuffer)
2128c2ecf20Sopenharmony_ci		return -ENOMEM;
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	pport = pp->pdev->port;
2158c2ecf20Sopenharmony_ci	mode = pport->ieee1284.mode & ~(IEEE1284_DEVICEID | IEEE1284_ADDR);
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	parport_set_timeout(pp->pdev,
2188c2ecf20Sopenharmony_ci			    (file->f_flags & O_NONBLOCK) ?
2198c2ecf20Sopenharmony_ci			    PARPORT_INACTIVITY_O_NONBLOCK :
2208c2ecf20Sopenharmony_ci			    pp->default_inactivity);
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	while (bytes_written < count) {
2238c2ecf20Sopenharmony_ci		ssize_t n = min_t(unsigned long, count - bytes_written, PP_BUFFER_SIZE);
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci		if (copy_from_user(kbuffer, buf + bytes_written, n)) {
2268c2ecf20Sopenharmony_ci			bytes_written = -EFAULT;
2278c2ecf20Sopenharmony_ci			break;
2288c2ecf20Sopenharmony_ci		}
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci		if ((pp->flags & PP_FASTWRITE) && (mode == IEEE1284_MODE_EPP)) {
2318c2ecf20Sopenharmony_ci			/* do a fast EPP write */
2328c2ecf20Sopenharmony_ci			if (pport->ieee1284.mode & IEEE1284_ADDR) {
2338c2ecf20Sopenharmony_ci				wrote = pport->ops->epp_write_addr(pport,
2348c2ecf20Sopenharmony_ci					kbuffer, n, PARPORT_EPP_FAST);
2358c2ecf20Sopenharmony_ci			} else {
2368c2ecf20Sopenharmony_ci				wrote = pport->ops->epp_write_data(pport,
2378c2ecf20Sopenharmony_ci					kbuffer, n, PARPORT_EPP_FAST);
2388c2ecf20Sopenharmony_ci			}
2398c2ecf20Sopenharmony_ci		} else {
2408c2ecf20Sopenharmony_ci			wrote = parport_write(pp->pdev->port, kbuffer, n);
2418c2ecf20Sopenharmony_ci		}
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci		if (wrote <= 0) {
2448c2ecf20Sopenharmony_ci			if (!bytes_written)
2458c2ecf20Sopenharmony_ci				bytes_written = wrote;
2468c2ecf20Sopenharmony_ci			break;
2478c2ecf20Sopenharmony_ci		}
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci		bytes_written += wrote;
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci		if (file->f_flags & O_NONBLOCK) {
2528c2ecf20Sopenharmony_ci			if (!bytes_written)
2538c2ecf20Sopenharmony_ci				bytes_written = -EAGAIN;
2548c2ecf20Sopenharmony_ci			break;
2558c2ecf20Sopenharmony_ci		}
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci		if (signal_pending(current))
2588c2ecf20Sopenharmony_ci			break;
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci		cond_resched();
2618c2ecf20Sopenharmony_ci	}
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci	parport_set_timeout(pp->pdev, pp->default_inactivity);
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci	kfree(kbuffer);
2668c2ecf20Sopenharmony_ci	pp_enable_irq(pp);
2678c2ecf20Sopenharmony_ci	return bytes_written;
2688c2ecf20Sopenharmony_ci}
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_cistatic void pp_irq(void *private)
2718c2ecf20Sopenharmony_ci{
2728c2ecf20Sopenharmony_ci	struct pp_struct *pp = private;
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	if (pp->irqresponse) {
2758c2ecf20Sopenharmony_ci		parport_write_control(pp->pdev->port, pp->irqctl);
2768c2ecf20Sopenharmony_ci		pp->irqresponse = 0;
2778c2ecf20Sopenharmony_ci	}
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci	atomic_inc(&pp->irqc);
2808c2ecf20Sopenharmony_ci	wake_up_interruptible(&pp->irq_wait);
2818c2ecf20Sopenharmony_ci}
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_cistatic int register_device(int minor, struct pp_struct *pp)
2848c2ecf20Sopenharmony_ci{
2858c2ecf20Sopenharmony_ci	struct parport *port;
2868c2ecf20Sopenharmony_ci	struct pardevice *pdev = NULL;
2878c2ecf20Sopenharmony_ci	char *name;
2888c2ecf20Sopenharmony_ci	struct pardev_cb ppdev_cb;
2898c2ecf20Sopenharmony_ci	int rc = 0, index;
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci	name = kasprintf(GFP_KERNEL, CHRDEV "%x", minor);
2928c2ecf20Sopenharmony_ci	if (name == NULL)
2938c2ecf20Sopenharmony_ci		return -ENOMEM;
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci	port = parport_find_number(minor);
2968c2ecf20Sopenharmony_ci	if (!port) {
2978c2ecf20Sopenharmony_ci		pr_warn("%s: no associated port!\n", name);
2988c2ecf20Sopenharmony_ci		rc = -ENXIO;
2998c2ecf20Sopenharmony_ci		goto err;
3008c2ecf20Sopenharmony_ci	}
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci	index = ida_simple_get(&ida_index, 0, 0, GFP_KERNEL);
3038c2ecf20Sopenharmony_ci	memset(&ppdev_cb, 0, sizeof(ppdev_cb));
3048c2ecf20Sopenharmony_ci	ppdev_cb.irq_func = pp_irq;
3058c2ecf20Sopenharmony_ci	ppdev_cb.flags = (pp->flags & PP_EXCL) ? PARPORT_FLAG_EXCL : 0;
3068c2ecf20Sopenharmony_ci	ppdev_cb.private = pp;
3078c2ecf20Sopenharmony_ci	pdev = parport_register_dev_model(port, name, &ppdev_cb, index);
3088c2ecf20Sopenharmony_ci	parport_put_port(port);
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	if (!pdev) {
3118c2ecf20Sopenharmony_ci		pr_warn("%s: failed to register device!\n", name);
3128c2ecf20Sopenharmony_ci		rc = -ENXIO;
3138c2ecf20Sopenharmony_ci		ida_simple_remove(&ida_index, index);
3148c2ecf20Sopenharmony_ci		goto err;
3158c2ecf20Sopenharmony_ci	}
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci	pp->pdev = pdev;
3188c2ecf20Sopenharmony_ci	pp->index = index;
3198c2ecf20Sopenharmony_ci	dev_dbg(&pdev->dev, "registered pardevice\n");
3208c2ecf20Sopenharmony_cierr:
3218c2ecf20Sopenharmony_ci	kfree(name);
3228c2ecf20Sopenharmony_ci	return rc;
3238c2ecf20Sopenharmony_ci}
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_cistatic enum ieee1284_phase init_phase(int mode)
3268c2ecf20Sopenharmony_ci{
3278c2ecf20Sopenharmony_ci	switch (mode & ~(IEEE1284_DEVICEID
3288c2ecf20Sopenharmony_ci			 | IEEE1284_ADDR)) {
3298c2ecf20Sopenharmony_ci	case IEEE1284_MODE_NIBBLE:
3308c2ecf20Sopenharmony_ci	case IEEE1284_MODE_BYTE:
3318c2ecf20Sopenharmony_ci		return IEEE1284_PH_REV_IDLE;
3328c2ecf20Sopenharmony_ci	}
3338c2ecf20Sopenharmony_ci	return IEEE1284_PH_FWD_IDLE;
3348c2ecf20Sopenharmony_ci}
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_cistatic int pp_set_timeout(struct pardevice *pdev, long tv_sec, int tv_usec)
3378c2ecf20Sopenharmony_ci{
3388c2ecf20Sopenharmony_ci	long to_jiffies;
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci	if ((tv_sec < 0) || (tv_usec < 0))
3418c2ecf20Sopenharmony_ci		return -EINVAL;
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci	to_jiffies = usecs_to_jiffies(tv_usec);
3448c2ecf20Sopenharmony_ci	to_jiffies += tv_sec * HZ;
3458c2ecf20Sopenharmony_ci	if (to_jiffies <= 0)
3468c2ecf20Sopenharmony_ci		return -EINVAL;
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci	pdev->timeout = to_jiffies;
3498c2ecf20Sopenharmony_ci	return 0;
3508c2ecf20Sopenharmony_ci}
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_cistatic int pp_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
3538c2ecf20Sopenharmony_ci{
3548c2ecf20Sopenharmony_ci	unsigned int minor = iminor(file_inode(file));
3558c2ecf20Sopenharmony_ci	struct pp_struct *pp = file->private_data;
3568c2ecf20Sopenharmony_ci	struct parport *port;
3578c2ecf20Sopenharmony_ci	void __user *argp = (void __user *)arg;
3588c2ecf20Sopenharmony_ci	struct ieee1284_info *info;
3598c2ecf20Sopenharmony_ci	unsigned char reg;
3608c2ecf20Sopenharmony_ci	unsigned char mask;
3618c2ecf20Sopenharmony_ci	int mode;
3628c2ecf20Sopenharmony_ci	s32 time32[2];
3638c2ecf20Sopenharmony_ci	s64 time64[2];
3648c2ecf20Sopenharmony_ci	struct timespec64 ts;
3658c2ecf20Sopenharmony_ci	int ret;
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci	/* First handle the cases that don't take arguments. */
3688c2ecf20Sopenharmony_ci	switch (cmd) {
3698c2ecf20Sopenharmony_ci	case PPCLAIM:
3708c2ecf20Sopenharmony_ci	    {
3718c2ecf20Sopenharmony_ci		if (pp->flags & PP_CLAIMED) {
3728c2ecf20Sopenharmony_ci			dev_dbg(&pp->pdev->dev, "you've already got it!\n");
3738c2ecf20Sopenharmony_ci			return -EINVAL;
3748c2ecf20Sopenharmony_ci		}
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci		/* Deferred device registration. */
3778c2ecf20Sopenharmony_ci		if (!pp->pdev) {
3788c2ecf20Sopenharmony_ci			int err = register_device(minor, pp);
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci			if (err)
3818c2ecf20Sopenharmony_ci				return err;
3828c2ecf20Sopenharmony_ci		}
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci		ret = parport_claim_or_block(pp->pdev);
3858c2ecf20Sopenharmony_ci		if (ret < 0)
3868c2ecf20Sopenharmony_ci			return ret;
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci		pp->flags |= PP_CLAIMED;
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci		/* For interrupt-reporting to work, we need to be
3918c2ecf20Sopenharmony_ci		 * informed of each interrupt. */
3928c2ecf20Sopenharmony_ci		pp_enable_irq(pp);
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci		/* We may need to fix up the state machine. */
3958c2ecf20Sopenharmony_ci		info = &pp->pdev->port->ieee1284;
3968c2ecf20Sopenharmony_ci		pp->saved_state.mode = info->mode;
3978c2ecf20Sopenharmony_ci		pp->saved_state.phase = info->phase;
3988c2ecf20Sopenharmony_ci		info->mode = pp->state.mode;
3998c2ecf20Sopenharmony_ci		info->phase = pp->state.phase;
4008c2ecf20Sopenharmony_ci		pp->default_inactivity = parport_set_timeout(pp->pdev, 0);
4018c2ecf20Sopenharmony_ci		parport_set_timeout(pp->pdev, pp->default_inactivity);
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci		return 0;
4048c2ecf20Sopenharmony_ci	    }
4058c2ecf20Sopenharmony_ci	case PPEXCL:
4068c2ecf20Sopenharmony_ci		if (pp->pdev) {
4078c2ecf20Sopenharmony_ci			dev_dbg(&pp->pdev->dev,
4088c2ecf20Sopenharmony_ci				"too late for PPEXCL; already registered\n");
4098c2ecf20Sopenharmony_ci			if (pp->flags & PP_EXCL)
4108c2ecf20Sopenharmony_ci				/* But it's not really an error. */
4118c2ecf20Sopenharmony_ci				return 0;
4128c2ecf20Sopenharmony_ci			/* There's no chance of making the driver happy. */
4138c2ecf20Sopenharmony_ci			return -EINVAL;
4148c2ecf20Sopenharmony_ci		}
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci		/* Just remember to register the device exclusively
4178c2ecf20Sopenharmony_ci		 * when we finally do the registration. */
4188c2ecf20Sopenharmony_ci		pp->flags |= PP_EXCL;
4198c2ecf20Sopenharmony_ci		return 0;
4208c2ecf20Sopenharmony_ci	case PPSETMODE:
4218c2ecf20Sopenharmony_ci	    {
4228c2ecf20Sopenharmony_ci		int mode;
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ci		if (copy_from_user(&mode, argp, sizeof(mode)))
4258c2ecf20Sopenharmony_ci			return -EFAULT;
4268c2ecf20Sopenharmony_ci		/* FIXME: validate mode */
4278c2ecf20Sopenharmony_ci		pp->state.mode = mode;
4288c2ecf20Sopenharmony_ci		pp->state.phase = init_phase(mode);
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_ci		if (pp->flags & PP_CLAIMED) {
4318c2ecf20Sopenharmony_ci			pp->pdev->port->ieee1284.mode = mode;
4328c2ecf20Sopenharmony_ci			pp->pdev->port->ieee1284.phase = pp->state.phase;
4338c2ecf20Sopenharmony_ci		}
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci		return 0;
4368c2ecf20Sopenharmony_ci	    }
4378c2ecf20Sopenharmony_ci	case PPGETMODE:
4388c2ecf20Sopenharmony_ci	    {
4398c2ecf20Sopenharmony_ci		int mode;
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci		if (pp->flags & PP_CLAIMED)
4428c2ecf20Sopenharmony_ci			mode = pp->pdev->port->ieee1284.mode;
4438c2ecf20Sopenharmony_ci		else
4448c2ecf20Sopenharmony_ci			mode = pp->state.mode;
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ci		if (copy_to_user(argp, &mode, sizeof(mode)))
4478c2ecf20Sopenharmony_ci			return -EFAULT;
4488c2ecf20Sopenharmony_ci		return 0;
4498c2ecf20Sopenharmony_ci	    }
4508c2ecf20Sopenharmony_ci	case PPSETPHASE:
4518c2ecf20Sopenharmony_ci	    {
4528c2ecf20Sopenharmony_ci		int phase;
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_ci		if (copy_from_user(&phase, argp, sizeof(phase)))
4558c2ecf20Sopenharmony_ci			return -EFAULT;
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_ci		/* FIXME: validate phase */
4588c2ecf20Sopenharmony_ci		pp->state.phase = phase;
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci		if (pp->flags & PP_CLAIMED)
4618c2ecf20Sopenharmony_ci			pp->pdev->port->ieee1284.phase = phase;
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_ci		return 0;
4648c2ecf20Sopenharmony_ci	    }
4658c2ecf20Sopenharmony_ci	case PPGETPHASE:
4668c2ecf20Sopenharmony_ci	    {
4678c2ecf20Sopenharmony_ci		int phase;
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_ci		if (pp->flags & PP_CLAIMED)
4708c2ecf20Sopenharmony_ci			phase = pp->pdev->port->ieee1284.phase;
4718c2ecf20Sopenharmony_ci		else
4728c2ecf20Sopenharmony_ci			phase = pp->state.phase;
4738c2ecf20Sopenharmony_ci		if (copy_to_user(argp, &phase, sizeof(phase)))
4748c2ecf20Sopenharmony_ci			return -EFAULT;
4758c2ecf20Sopenharmony_ci		return 0;
4768c2ecf20Sopenharmony_ci	    }
4778c2ecf20Sopenharmony_ci	case PPGETMODES:
4788c2ecf20Sopenharmony_ci	    {
4798c2ecf20Sopenharmony_ci		unsigned int modes;
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_ci		port = parport_find_number(minor);
4828c2ecf20Sopenharmony_ci		if (!port)
4838c2ecf20Sopenharmony_ci			return -ENODEV;
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_ci		modes = port->modes;
4868c2ecf20Sopenharmony_ci		parport_put_port(port);
4878c2ecf20Sopenharmony_ci		if (copy_to_user(argp, &modes, sizeof(modes)))
4888c2ecf20Sopenharmony_ci			return -EFAULT;
4898c2ecf20Sopenharmony_ci		return 0;
4908c2ecf20Sopenharmony_ci	    }
4918c2ecf20Sopenharmony_ci	case PPSETFLAGS:
4928c2ecf20Sopenharmony_ci	    {
4938c2ecf20Sopenharmony_ci		int uflags;
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_ci		if (copy_from_user(&uflags, argp, sizeof(uflags)))
4968c2ecf20Sopenharmony_ci			return -EFAULT;
4978c2ecf20Sopenharmony_ci		pp->flags &= ~PP_FLAGMASK;
4988c2ecf20Sopenharmony_ci		pp->flags |= (uflags & PP_FLAGMASK);
4998c2ecf20Sopenharmony_ci		return 0;
5008c2ecf20Sopenharmony_ci	    }
5018c2ecf20Sopenharmony_ci	case PPGETFLAGS:
5028c2ecf20Sopenharmony_ci	    {
5038c2ecf20Sopenharmony_ci		int uflags;
5048c2ecf20Sopenharmony_ci
5058c2ecf20Sopenharmony_ci		uflags = pp->flags & PP_FLAGMASK;
5068c2ecf20Sopenharmony_ci		if (copy_to_user(argp, &uflags, sizeof(uflags)))
5078c2ecf20Sopenharmony_ci			return -EFAULT;
5088c2ecf20Sopenharmony_ci		return 0;
5098c2ecf20Sopenharmony_ci	    }
5108c2ecf20Sopenharmony_ci	}	/* end switch() */
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_ci	/* Everything else requires the port to be claimed, so check
5138c2ecf20Sopenharmony_ci	 * that now. */
5148c2ecf20Sopenharmony_ci	if ((pp->flags & PP_CLAIMED) == 0) {
5158c2ecf20Sopenharmony_ci		pr_debug(CHRDEV "%x: claim the port first\n", minor);
5168c2ecf20Sopenharmony_ci		return -EINVAL;
5178c2ecf20Sopenharmony_ci	}
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_ci	port = pp->pdev->port;
5208c2ecf20Sopenharmony_ci	switch (cmd) {
5218c2ecf20Sopenharmony_ci	case PPRSTATUS:
5228c2ecf20Sopenharmony_ci		reg = parport_read_status(port);
5238c2ecf20Sopenharmony_ci		if (copy_to_user(argp, &reg, sizeof(reg)))
5248c2ecf20Sopenharmony_ci			return -EFAULT;
5258c2ecf20Sopenharmony_ci		return 0;
5268c2ecf20Sopenharmony_ci	case PPRDATA:
5278c2ecf20Sopenharmony_ci		reg = parport_read_data(port);
5288c2ecf20Sopenharmony_ci		if (copy_to_user(argp, &reg, sizeof(reg)))
5298c2ecf20Sopenharmony_ci			return -EFAULT;
5308c2ecf20Sopenharmony_ci		return 0;
5318c2ecf20Sopenharmony_ci	case PPRCONTROL:
5328c2ecf20Sopenharmony_ci		reg = parport_read_control(port);
5338c2ecf20Sopenharmony_ci		if (copy_to_user(argp, &reg, sizeof(reg)))
5348c2ecf20Sopenharmony_ci			return -EFAULT;
5358c2ecf20Sopenharmony_ci		return 0;
5368c2ecf20Sopenharmony_ci	case PPYIELD:
5378c2ecf20Sopenharmony_ci		parport_yield_blocking(pp->pdev);
5388c2ecf20Sopenharmony_ci		return 0;
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_ci	case PPRELEASE:
5418c2ecf20Sopenharmony_ci		/* Save the state machine's state. */
5428c2ecf20Sopenharmony_ci		info = &pp->pdev->port->ieee1284;
5438c2ecf20Sopenharmony_ci		pp->state.mode = info->mode;
5448c2ecf20Sopenharmony_ci		pp->state.phase = info->phase;
5458c2ecf20Sopenharmony_ci		info->mode = pp->saved_state.mode;
5468c2ecf20Sopenharmony_ci		info->phase = pp->saved_state.phase;
5478c2ecf20Sopenharmony_ci		parport_release(pp->pdev);
5488c2ecf20Sopenharmony_ci		pp->flags &= ~PP_CLAIMED;
5498c2ecf20Sopenharmony_ci		return 0;
5508c2ecf20Sopenharmony_ci
5518c2ecf20Sopenharmony_ci	case PPWCONTROL:
5528c2ecf20Sopenharmony_ci		if (copy_from_user(&reg, argp, sizeof(reg)))
5538c2ecf20Sopenharmony_ci			return -EFAULT;
5548c2ecf20Sopenharmony_ci		parport_write_control(port, reg);
5558c2ecf20Sopenharmony_ci		return 0;
5568c2ecf20Sopenharmony_ci
5578c2ecf20Sopenharmony_ci	case PPWDATA:
5588c2ecf20Sopenharmony_ci		if (copy_from_user(&reg, argp, sizeof(reg)))
5598c2ecf20Sopenharmony_ci			return -EFAULT;
5608c2ecf20Sopenharmony_ci		parport_write_data(port, reg);
5618c2ecf20Sopenharmony_ci		return 0;
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_ci	case PPFCONTROL:
5648c2ecf20Sopenharmony_ci		if (copy_from_user(&mask, argp,
5658c2ecf20Sopenharmony_ci				   sizeof(mask)))
5668c2ecf20Sopenharmony_ci			return -EFAULT;
5678c2ecf20Sopenharmony_ci		if (copy_from_user(&reg, 1 + (unsigned char __user *) arg,
5688c2ecf20Sopenharmony_ci				   sizeof(reg)))
5698c2ecf20Sopenharmony_ci			return -EFAULT;
5708c2ecf20Sopenharmony_ci		parport_frob_control(port, mask, reg);
5718c2ecf20Sopenharmony_ci		return 0;
5728c2ecf20Sopenharmony_ci
5738c2ecf20Sopenharmony_ci	case PPDATADIR:
5748c2ecf20Sopenharmony_ci		if (copy_from_user(&mode, argp, sizeof(mode)))
5758c2ecf20Sopenharmony_ci			return -EFAULT;
5768c2ecf20Sopenharmony_ci		if (mode)
5778c2ecf20Sopenharmony_ci			port->ops->data_reverse(port);
5788c2ecf20Sopenharmony_ci		else
5798c2ecf20Sopenharmony_ci			port->ops->data_forward(port);
5808c2ecf20Sopenharmony_ci		return 0;
5818c2ecf20Sopenharmony_ci
5828c2ecf20Sopenharmony_ci	case PPNEGOT:
5838c2ecf20Sopenharmony_ci		if (copy_from_user(&mode, argp, sizeof(mode)))
5848c2ecf20Sopenharmony_ci			return -EFAULT;
5858c2ecf20Sopenharmony_ci		switch ((ret = parport_negotiate(port, mode))) {
5868c2ecf20Sopenharmony_ci		case 0: break;
5878c2ecf20Sopenharmony_ci		case -1: /* handshake failed, peripheral not IEEE 1284 */
5888c2ecf20Sopenharmony_ci			ret = -EIO;
5898c2ecf20Sopenharmony_ci			break;
5908c2ecf20Sopenharmony_ci		case 1:  /* handshake succeeded, peripheral rejected mode */
5918c2ecf20Sopenharmony_ci			ret = -ENXIO;
5928c2ecf20Sopenharmony_ci			break;
5938c2ecf20Sopenharmony_ci		}
5948c2ecf20Sopenharmony_ci		pp_enable_irq(pp);
5958c2ecf20Sopenharmony_ci		return ret;
5968c2ecf20Sopenharmony_ci
5978c2ecf20Sopenharmony_ci	case PPWCTLONIRQ:
5988c2ecf20Sopenharmony_ci		if (copy_from_user(&reg, argp, sizeof(reg)))
5998c2ecf20Sopenharmony_ci			return -EFAULT;
6008c2ecf20Sopenharmony_ci
6018c2ecf20Sopenharmony_ci		/* Remember what to set the control lines to, for next
6028c2ecf20Sopenharmony_ci		 * time we get an interrupt. */
6038c2ecf20Sopenharmony_ci		pp->irqctl = reg;
6048c2ecf20Sopenharmony_ci		pp->irqresponse = 1;
6058c2ecf20Sopenharmony_ci		return 0;
6068c2ecf20Sopenharmony_ci
6078c2ecf20Sopenharmony_ci	case PPCLRIRQ:
6088c2ecf20Sopenharmony_ci		ret = atomic_read(&pp->irqc);
6098c2ecf20Sopenharmony_ci		if (copy_to_user(argp, &ret, sizeof(ret)))
6108c2ecf20Sopenharmony_ci			return -EFAULT;
6118c2ecf20Sopenharmony_ci		atomic_sub(ret, &pp->irqc);
6128c2ecf20Sopenharmony_ci		return 0;
6138c2ecf20Sopenharmony_ci
6148c2ecf20Sopenharmony_ci	case PPSETTIME32:
6158c2ecf20Sopenharmony_ci		if (copy_from_user(time32, argp, sizeof(time32)))
6168c2ecf20Sopenharmony_ci			return -EFAULT;
6178c2ecf20Sopenharmony_ci
6188c2ecf20Sopenharmony_ci		if ((time32[0] < 0) || (time32[1] < 0))
6198c2ecf20Sopenharmony_ci			return -EINVAL;
6208c2ecf20Sopenharmony_ci
6218c2ecf20Sopenharmony_ci		return pp_set_timeout(pp->pdev, time32[0], time32[1]);
6228c2ecf20Sopenharmony_ci
6238c2ecf20Sopenharmony_ci	case PPSETTIME64:
6248c2ecf20Sopenharmony_ci		if (copy_from_user(time64, argp, sizeof(time64)))
6258c2ecf20Sopenharmony_ci			return -EFAULT;
6268c2ecf20Sopenharmony_ci
6278c2ecf20Sopenharmony_ci		if ((time64[0] < 0) || (time64[1] < 0))
6288c2ecf20Sopenharmony_ci			return -EINVAL;
6298c2ecf20Sopenharmony_ci
6308c2ecf20Sopenharmony_ci		if (IS_ENABLED(CONFIG_SPARC64) && !in_compat_syscall())
6318c2ecf20Sopenharmony_ci			time64[1] >>= 32;
6328c2ecf20Sopenharmony_ci
6338c2ecf20Sopenharmony_ci		return pp_set_timeout(pp->pdev, time64[0], time64[1]);
6348c2ecf20Sopenharmony_ci
6358c2ecf20Sopenharmony_ci	case PPGETTIME32:
6368c2ecf20Sopenharmony_ci		jiffies_to_timespec64(pp->pdev->timeout, &ts);
6378c2ecf20Sopenharmony_ci		time32[0] = ts.tv_sec;
6388c2ecf20Sopenharmony_ci		time32[1] = ts.tv_nsec / NSEC_PER_USEC;
6398c2ecf20Sopenharmony_ci
6408c2ecf20Sopenharmony_ci		if (copy_to_user(argp, time32, sizeof(time32)))
6418c2ecf20Sopenharmony_ci			return -EFAULT;
6428c2ecf20Sopenharmony_ci
6438c2ecf20Sopenharmony_ci		return 0;
6448c2ecf20Sopenharmony_ci
6458c2ecf20Sopenharmony_ci	case PPGETTIME64:
6468c2ecf20Sopenharmony_ci		jiffies_to_timespec64(pp->pdev->timeout, &ts);
6478c2ecf20Sopenharmony_ci		time64[0] = ts.tv_sec;
6488c2ecf20Sopenharmony_ci		time64[1] = ts.tv_nsec / NSEC_PER_USEC;
6498c2ecf20Sopenharmony_ci
6508c2ecf20Sopenharmony_ci		if (IS_ENABLED(CONFIG_SPARC64) && !in_compat_syscall())
6518c2ecf20Sopenharmony_ci			time64[1] <<= 32;
6528c2ecf20Sopenharmony_ci
6538c2ecf20Sopenharmony_ci		if (copy_to_user(argp, time64, sizeof(time64)))
6548c2ecf20Sopenharmony_ci			return -EFAULT;
6558c2ecf20Sopenharmony_ci
6568c2ecf20Sopenharmony_ci		return 0;
6578c2ecf20Sopenharmony_ci
6588c2ecf20Sopenharmony_ci	default:
6598c2ecf20Sopenharmony_ci		dev_dbg(&pp->pdev->dev, "What? (cmd=0x%x)\n", cmd);
6608c2ecf20Sopenharmony_ci		return -EINVAL;
6618c2ecf20Sopenharmony_ci	}
6628c2ecf20Sopenharmony_ci
6638c2ecf20Sopenharmony_ci	/* Keep the compiler happy */
6648c2ecf20Sopenharmony_ci	return 0;
6658c2ecf20Sopenharmony_ci}
6668c2ecf20Sopenharmony_ci
6678c2ecf20Sopenharmony_cistatic long pp_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
6688c2ecf20Sopenharmony_ci{
6698c2ecf20Sopenharmony_ci	long ret;
6708c2ecf20Sopenharmony_ci
6718c2ecf20Sopenharmony_ci	mutex_lock(&pp_do_mutex);
6728c2ecf20Sopenharmony_ci	ret = pp_do_ioctl(file, cmd, arg);
6738c2ecf20Sopenharmony_ci	mutex_unlock(&pp_do_mutex);
6748c2ecf20Sopenharmony_ci	return ret;
6758c2ecf20Sopenharmony_ci}
6768c2ecf20Sopenharmony_ci
6778c2ecf20Sopenharmony_cistatic int pp_open(struct inode *inode, struct file *file)
6788c2ecf20Sopenharmony_ci{
6798c2ecf20Sopenharmony_ci	unsigned int minor = iminor(inode);
6808c2ecf20Sopenharmony_ci	struct pp_struct *pp;
6818c2ecf20Sopenharmony_ci
6828c2ecf20Sopenharmony_ci	if (minor >= PARPORT_MAX)
6838c2ecf20Sopenharmony_ci		return -ENXIO;
6848c2ecf20Sopenharmony_ci
6858c2ecf20Sopenharmony_ci	pp = kmalloc(sizeof(struct pp_struct), GFP_KERNEL);
6868c2ecf20Sopenharmony_ci	if (!pp)
6878c2ecf20Sopenharmony_ci		return -ENOMEM;
6888c2ecf20Sopenharmony_ci
6898c2ecf20Sopenharmony_ci	pp->state.mode = IEEE1284_MODE_COMPAT;
6908c2ecf20Sopenharmony_ci	pp->state.phase = init_phase(pp->state.mode);
6918c2ecf20Sopenharmony_ci	pp->flags = 0;
6928c2ecf20Sopenharmony_ci	pp->irqresponse = 0;
6938c2ecf20Sopenharmony_ci	atomic_set(&pp->irqc, 0);
6948c2ecf20Sopenharmony_ci	init_waitqueue_head(&pp->irq_wait);
6958c2ecf20Sopenharmony_ci
6968c2ecf20Sopenharmony_ci	/* Defer the actual device registration until the first claim.
6978c2ecf20Sopenharmony_ci	 * That way, we know whether or not the driver wants to have
6988c2ecf20Sopenharmony_ci	 * exclusive access to the port (PPEXCL).
6998c2ecf20Sopenharmony_ci	 */
7008c2ecf20Sopenharmony_ci	pp->pdev = NULL;
7018c2ecf20Sopenharmony_ci	file->private_data = pp;
7028c2ecf20Sopenharmony_ci
7038c2ecf20Sopenharmony_ci	return 0;
7048c2ecf20Sopenharmony_ci}
7058c2ecf20Sopenharmony_ci
7068c2ecf20Sopenharmony_cistatic int pp_release(struct inode *inode, struct file *file)
7078c2ecf20Sopenharmony_ci{
7088c2ecf20Sopenharmony_ci	unsigned int minor = iminor(inode);
7098c2ecf20Sopenharmony_ci	struct pp_struct *pp = file->private_data;
7108c2ecf20Sopenharmony_ci	int compat_negot;
7118c2ecf20Sopenharmony_ci
7128c2ecf20Sopenharmony_ci	compat_negot = 0;
7138c2ecf20Sopenharmony_ci	if (!(pp->flags & PP_CLAIMED) && pp->pdev &&
7148c2ecf20Sopenharmony_ci	    (pp->state.mode != IEEE1284_MODE_COMPAT)) {
7158c2ecf20Sopenharmony_ci		struct ieee1284_info *info;
7168c2ecf20Sopenharmony_ci
7178c2ecf20Sopenharmony_ci		/* parport released, but not in compatibility mode */
7188c2ecf20Sopenharmony_ci		parport_claim_or_block(pp->pdev);
7198c2ecf20Sopenharmony_ci		pp->flags |= PP_CLAIMED;
7208c2ecf20Sopenharmony_ci		info = &pp->pdev->port->ieee1284;
7218c2ecf20Sopenharmony_ci		pp->saved_state.mode = info->mode;
7228c2ecf20Sopenharmony_ci		pp->saved_state.phase = info->phase;
7238c2ecf20Sopenharmony_ci		info->mode = pp->state.mode;
7248c2ecf20Sopenharmony_ci		info->phase = pp->state.phase;
7258c2ecf20Sopenharmony_ci		compat_negot = 1;
7268c2ecf20Sopenharmony_ci	} else if ((pp->flags & PP_CLAIMED) && pp->pdev &&
7278c2ecf20Sopenharmony_ci	    (pp->pdev->port->ieee1284.mode != IEEE1284_MODE_COMPAT)) {
7288c2ecf20Sopenharmony_ci		compat_negot = 2;
7298c2ecf20Sopenharmony_ci	}
7308c2ecf20Sopenharmony_ci	if (compat_negot) {
7318c2ecf20Sopenharmony_ci		parport_negotiate(pp->pdev->port, IEEE1284_MODE_COMPAT);
7328c2ecf20Sopenharmony_ci		dev_dbg(&pp->pdev->dev,
7338c2ecf20Sopenharmony_ci			"negotiated back to compatibility mode because user-space forgot\n");
7348c2ecf20Sopenharmony_ci	}
7358c2ecf20Sopenharmony_ci
7368c2ecf20Sopenharmony_ci	if ((pp->flags & PP_CLAIMED) && pp->pdev) {
7378c2ecf20Sopenharmony_ci		struct ieee1284_info *info;
7388c2ecf20Sopenharmony_ci
7398c2ecf20Sopenharmony_ci		info = &pp->pdev->port->ieee1284;
7408c2ecf20Sopenharmony_ci		pp->state.mode = info->mode;
7418c2ecf20Sopenharmony_ci		pp->state.phase = info->phase;
7428c2ecf20Sopenharmony_ci		info->mode = pp->saved_state.mode;
7438c2ecf20Sopenharmony_ci		info->phase = pp->saved_state.phase;
7448c2ecf20Sopenharmony_ci		parport_release(pp->pdev);
7458c2ecf20Sopenharmony_ci		if (compat_negot != 1) {
7468c2ecf20Sopenharmony_ci			pr_debug(CHRDEV "%x: released pardevice "
7478c2ecf20Sopenharmony_ci				"because user-space forgot\n", minor);
7488c2ecf20Sopenharmony_ci		}
7498c2ecf20Sopenharmony_ci	}
7508c2ecf20Sopenharmony_ci
7518c2ecf20Sopenharmony_ci	if (pp->pdev) {
7528c2ecf20Sopenharmony_ci		parport_unregister_device(pp->pdev);
7538c2ecf20Sopenharmony_ci		ida_simple_remove(&ida_index, pp->index);
7548c2ecf20Sopenharmony_ci		pp->pdev = NULL;
7558c2ecf20Sopenharmony_ci		pr_debug(CHRDEV "%x: unregistered pardevice\n", minor);
7568c2ecf20Sopenharmony_ci	}
7578c2ecf20Sopenharmony_ci
7588c2ecf20Sopenharmony_ci	kfree(pp);
7598c2ecf20Sopenharmony_ci
7608c2ecf20Sopenharmony_ci	return 0;
7618c2ecf20Sopenharmony_ci}
7628c2ecf20Sopenharmony_ci
7638c2ecf20Sopenharmony_ci/* No kernel lock held - fine */
7648c2ecf20Sopenharmony_cistatic __poll_t pp_poll(struct file *file, poll_table *wait)
7658c2ecf20Sopenharmony_ci{
7668c2ecf20Sopenharmony_ci	struct pp_struct *pp = file->private_data;
7678c2ecf20Sopenharmony_ci	__poll_t mask = 0;
7688c2ecf20Sopenharmony_ci
7698c2ecf20Sopenharmony_ci	poll_wait(file, &pp->irq_wait, wait);
7708c2ecf20Sopenharmony_ci	if (atomic_read(&pp->irqc))
7718c2ecf20Sopenharmony_ci		mask |= EPOLLIN | EPOLLRDNORM;
7728c2ecf20Sopenharmony_ci
7738c2ecf20Sopenharmony_ci	return mask;
7748c2ecf20Sopenharmony_ci}
7758c2ecf20Sopenharmony_ci
7768c2ecf20Sopenharmony_cistatic struct class *ppdev_class;
7778c2ecf20Sopenharmony_ci
7788c2ecf20Sopenharmony_cistatic const struct file_operations pp_fops = {
7798c2ecf20Sopenharmony_ci	.owner		= THIS_MODULE,
7808c2ecf20Sopenharmony_ci	.llseek		= no_llseek,
7818c2ecf20Sopenharmony_ci	.read		= pp_read,
7828c2ecf20Sopenharmony_ci	.write		= pp_write,
7838c2ecf20Sopenharmony_ci	.poll		= pp_poll,
7848c2ecf20Sopenharmony_ci	.unlocked_ioctl	= pp_ioctl,
7858c2ecf20Sopenharmony_ci	.compat_ioctl   = compat_ptr_ioctl,
7868c2ecf20Sopenharmony_ci	.open		= pp_open,
7878c2ecf20Sopenharmony_ci	.release	= pp_release,
7888c2ecf20Sopenharmony_ci};
7898c2ecf20Sopenharmony_ci
7908c2ecf20Sopenharmony_cistatic void pp_attach(struct parport *port)
7918c2ecf20Sopenharmony_ci{
7928c2ecf20Sopenharmony_ci	struct device *ret;
7938c2ecf20Sopenharmony_ci
7948c2ecf20Sopenharmony_ci	if (devices[port->number])
7958c2ecf20Sopenharmony_ci		return;
7968c2ecf20Sopenharmony_ci
7978c2ecf20Sopenharmony_ci	ret = device_create(ppdev_class, port->dev,
7988c2ecf20Sopenharmony_ci			    MKDEV(PP_MAJOR, port->number), NULL,
7998c2ecf20Sopenharmony_ci			    "parport%d", port->number);
8008c2ecf20Sopenharmony_ci	if (IS_ERR(ret)) {
8018c2ecf20Sopenharmony_ci		pr_err("Failed to create device parport%d\n",
8028c2ecf20Sopenharmony_ci		       port->number);
8038c2ecf20Sopenharmony_ci		return;
8048c2ecf20Sopenharmony_ci	}
8058c2ecf20Sopenharmony_ci	devices[port->number] = ret;
8068c2ecf20Sopenharmony_ci}
8078c2ecf20Sopenharmony_ci
8088c2ecf20Sopenharmony_cistatic void pp_detach(struct parport *port)
8098c2ecf20Sopenharmony_ci{
8108c2ecf20Sopenharmony_ci	if (!devices[port->number])
8118c2ecf20Sopenharmony_ci		return;
8128c2ecf20Sopenharmony_ci
8138c2ecf20Sopenharmony_ci	device_destroy(ppdev_class, MKDEV(PP_MAJOR, port->number));
8148c2ecf20Sopenharmony_ci	devices[port->number] = NULL;
8158c2ecf20Sopenharmony_ci}
8168c2ecf20Sopenharmony_ci
8178c2ecf20Sopenharmony_cistatic int pp_probe(struct pardevice *par_dev)
8188c2ecf20Sopenharmony_ci{
8198c2ecf20Sopenharmony_ci	struct device_driver *drv = par_dev->dev.driver;
8208c2ecf20Sopenharmony_ci	int len = strlen(drv->name);
8218c2ecf20Sopenharmony_ci
8228c2ecf20Sopenharmony_ci	if (strncmp(par_dev->name, drv->name, len))
8238c2ecf20Sopenharmony_ci		return -ENODEV;
8248c2ecf20Sopenharmony_ci
8258c2ecf20Sopenharmony_ci	return 0;
8268c2ecf20Sopenharmony_ci}
8278c2ecf20Sopenharmony_ci
8288c2ecf20Sopenharmony_cistatic struct parport_driver pp_driver = {
8298c2ecf20Sopenharmony_ci	.name		= CHRDEV,
8308c2ecf20Sopenharmony_ci	.probe		= pp_probe,
8318c2ecf20Sopenharmony_ci	.match_port	= pp_attach,
8328c2ecf20Sopenharmony_ci	.detach		= pp_detach,
8338c2ecf20Sopenharmony_ci	.devmodel	= true,
8348c2ecf20Sopenharmony_ci};
8358c2ecf20Sopenharmony_ci
8368c2ecf20Sopenharmony_cistatic int __init ppdev_init(void)
8378c2ecf20Sopenharmony_ci{
8388c2ecf20Sopenharmony_ci	int err = 0;
8398c2ecf20Sopenharmony_ci
8408c2ecf20Sopenharmony_ci	if (register_chrdev(PP_MAJOR, CHRDEV, &pp_fops)) {
8418c2ecf20Sopenharmony_ci		pr_warn(CHRDEV ": unable to get major %d\n", PP_MAJOR);
8428c2ecf20Sopenharmony_ci		return -EIO;
8438c2ecf20Sopenharmony_ci	}
8448c2ecf20Sopenharmony_ci	ppdev_class = class_create(THIS_MODULE, CHRDEV);
8458c2ecf20Sopenharmony_ci	if (IS_ERR(ppdev_class)) {
8468c2ecf20Sopenharmony_ci		err = PTR_ERR(ppdev_class);
8478c2ecf20Sopenharmony_ci		goto out_chrdev;
8488c2ecf20Sopenharmony_ci	}
8498c2ecf20Sopenharmony_ci	err = parport_register_driver(&pp_driver);
8508c2ecf20Sopenharmony_ci	if (err < 0) {
8518c2ecf20Sopenharmony_ci		pr_warn(CHRDEV ": unable to register with parport\n");
8528c2ecf20Sopenharmony_ci		goto out_class;
8538c2ecf20Sopenharmony_ci	}
8548c2ecf20Sopenharmony_ci
8558c2ecf20Sopenharmony_ci	pr_info(PP_VERSION "\n");
8568c2ecf20Sopenharmony_ci	goto out;
8578c2ecf20Sopenharmony_ci
8588c2ecf20Sopenharmony_ciout_class:
8598c2ecf20Sopenharmony_ci	class_destroy(ppdev_class);
8608c2ecf20Sopenharmony_ciout_chrdev:
8618c2ecf20Sopenharmony_ci	unregister_chrdev(PP_MAJOR, CHRDEV);
8628c2ecf20Sopenharmony_ciout:
8638c2ecf20Sopenharmony_ci	return err;
8648c2ecf20Sopenharmony_ci}
8658c2ecf20Sopenharmony_ci
8668c2ecf20Sopenharmony_cistatic void __exit ppdev_cleanup(void)
8678c2ecf20Sopenharmony_ci{
8688c2ecf20Sopenharmony_ci	/* Clean up all parport stuff */
8698c2ecf20Sopenharmony_ci	parport_unregister_driver(&pp_driver);
8708c2ecf20Sopenharmony_ci	class_destroy(ppdev_class);
8718c2ecf20Sopenharmony_ci	unregister_chrdev(PP_MAJOR, CHRDEV);
8728c2ecf20Sopenharmony_ci}
8738c2ecf20Sopenharmony_ci
8748c2ecf20Sopenharmony_cimodule_init(ppdev_init);
8758c2ecf20Sopenharmony_cimodule_exit(ppdev_cleanup);
8768c2ecf20Sopenharmony_ci
8778c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
8788c2ecf20Sopenharmony_ciMODULE_ALIAS_CHARDEV_MAJOR(PP_MAJOR);
879