162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * linux/drivers/char/ppdev.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * This is the code behind /dev/parport* -- it allows a user-space 662306a36Sopenharmony_ci * application to use the parport subsystem. 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Copyright (C) 1998-2000, 2002 Tim Waugh <tim@cyberelk.net> 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * A /dev/parportx device node represents an arbitrary device 1162306a36Sopenharmony_ci * on port 'x'. The following operations are possible: 1262306a36Sopenharmony_ci * 1362306a36Sopenharmony_ci * open do nothing, set up default IEEE 1284 protocol to be COMPAT 1462306a36Sopenharmony_ci * close release port and unregister device (if necessary) 1562306a36Sopenharmony_ci * ioctl 1662306a36Sopenharmony_ci * EXCL register device exclusively (may fail) 1762306a36Sopenharmony_ci * CLAIM (register device first time) parport_claim_or_block 1862306a36Sopenharmony_ci * RELEASE parport_release 1962306a36Sopenharmony_ci * SETMODE set the IEEE 1284 protocol to use for read/write 2062306a36Sopenharmony_ci * SETPHASE set the IEEE 1284 phase of a particular mode. Not to be 2162306a36Sopenharmony_ci * confused with ioctl(fd, SETPHASER, &stun). ;-) 2262306a36Sopenharmony_ci * DATADIR data_forward / data_reverse 2362306a36Sopenharmony_ci * WDATA write_data 2462306a36Sopenharmony_ci * RDATA read_data 2562306a36Sopenharmony_ci * WCONTROL write_control 2662306a36Sopenharmony_ci * RCONTROL read_control 2762306a36Sopenharmony_ci * FCONTROL frob_control 2862306a36Sopenharmony_ci * RSTATUS read_status 2962306a36Sopenharmony_ci * NEGOT parport_negotiate 3062306a36Sopenharmony_ci * YIELD parport_yield_blocking 3162306a36Sopenharmony_ci * WCTLONIRQ on interrupt, set control lines 3262306a36Sopenharmony_ci * CLRIRQ clear (and return) interrupt count 3362306a36Sopenharmony_ci * SETTIME sets device timeout (struct timeval) 3462306a36Sopenharmony_ci * GETTIME gets device timeout (struct timeval) 3562306a36Sopenharmony_ci * GETMODES gets hardware supported modes (unsigned int) 3662306a36Sopenharmony_ci * GETMODE gets the current IEEE1284 mode 3762306a36Sopenharmony_ci * GETPHASE gets the current IEEE1284 phase 3862306a36Sopenharmony_ci * GETFLAGS gets current (user-visible) flags 3962306a36Sopenharmony_ci * SETFLAGS sets current (user-visible) flags 4062306a36Sopenharmony_ci * read/write read or write in current IEEE 1284 protocol 4162306a36Sopenharmony_ci * select wait for interrupt (in readfds) 4262306a36Sopenharmony_ci * 4362306a36Sopenharmony_ci * Changes: 4462306a36Sopenharmony_ci * Added SETTIME/GETTIME ioctl, Fred Barnes, 1999. 4562306a36Sopenharmony_ci * 4662306a36Sopenharmony_ci * Arnaldo Carvalho de Melo <acme@conectiva.com.br> 2000/08/25 4762306a36Sopenharmony_ci * - On error, copy_from_user and copy_to_user do not return -EFAULT, 4862306a36Sopenharmony_ci * They return the positive number of bytes *not* copied due to address 4962306a36Sopenharmony_ci * space errors. 5062306a36Sopenharmony_ci * 5162306a36Sopenharmony_ci * Added GETMODES/GETMODE/GETPHASE ioctls, Fred Barnes <frmb2@ukc.ac.uk>, 03/01/2001. 5262306a36Sopenharmony_ci * Added GETFLAGS/SETFLAGS ioctls, Fred Barnes, 04/2001 5362306a36Sopenharmony_ci */ 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci#include <linux/module.h> 5662306a36Sopenharmony_ci#include <linux/init.h> 5762306a36Sopenharmony_ci#include <linux/sched/signal.h> 5862306a36Sopenharmony_ci#include <linux/device.h> 5962306a36Sopenharmony_ci#include <linux/ioctl.h> 6062306a36Sopenharmony_ci#include <linux/parport.h> 6162306a36Sopenharmony_ci#include <linux/ctype.h> 6262306a36Sopenharmony_ci#include <linux/poll.h> 6362306a36Sopenharmony_ci#include <linux/slab.h> 6462306a36Sopenharmony_ci#include <linux/major.h> 6562306a36Sopenharmony_ci#include <linux/ppdev.h> 6662306a36Sopenharmony_ci#include <linux/mutex.h> 6762306a36Sopenharmony_ci#include <linux/uaccess.h> 6862306a36Sopenharmony_ci#include <linux/compat.h> 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci#define PP_VERSION "ppdev: user-space parallel port driver" 7162306a36Sopenharmony_ci#define CHRDEV "ppdev" 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_cistruct pp_struct { 7462306a36Sopenharmony_ci struct pardevice *pdev; 7562306a36Sopenharmony_ci wait_queue_head_t irq_wait; 7662306a36Sopenharmony_ci atomic_t irqc; 7762306a36Sopenharmony_ci unsigned int flags; 7862306a36Sopenharmony_ci int irqresponse; 7962306a36Sopenharmony_ci unsigned char irqctl; 8062306a36Sopenharmony_ci struct ieee1284_info state; 8162306a36Sopenharmony_ci struct ieee1284_info saved_state; 8262306a36Sopenharmony_ci long default_inactivity; 8362306a36Sopenharmony_ci int index; 8462306a36Sopenharmony_ci}; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci/* should we use PARDEVICE_MAX here? */ 8762306a36Sopenharmony_cistatic struct device *devices[PARPORT_MAX]; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_cistatic DEFINE_IDA(ida_index); 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci/* pp_struct.flags bitfields */ 9262306a36Sopenharmony_ci#define PP_CLAIMED (1<<0) 9362306a36Sopenharmony_ci#define PP_EXCL (1<<1) 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci/* Other constants */ 9662306a36Sopenharmony_ci#define PP_INTERRUPT_TIMEOUT (10 * HZ) /* 10s */ 9762306a36Sopenharmony_ci#define PP_BUFFER_SIZE 1024 9862306a36Sopenharmony_ci#define PARDEVICE_MAX 8 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_cistatic DEFINE_MUTEX(pp_do_mutex); 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci/* define fixed sized ioctl cmd for y2038 migration */ 10362306a36Sopenharmony_ci#define PPGETTIME32 _IOR(PP_IOCTL, 0x95, s32[2]) 10462306a36Sopenharmony_ci#define PPSETTIME32 _IOW(PP_IOCTL, 0x96, s32[2]) 10562306a36Sopenharmony_ci#define PPGETTIME64 _IOR(PP_IOCTL, 0x95, s64[2]) 10662306a36Sopenharmony_ci#define PPSETTIME64 _IOW(PP_IOCTL, 0x96, s64[2]) 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_cistatic inline void pp_enable_irq(struct pp_struct *pp) 10962306a36Sopenharmony_ci{ 11062306a36Sopenharmony_ci struct parport *port = pp->pdev->port; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci port->ops->enable_irq(port); 11362306a36Sopenharmony_ci} 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_cistatic ssize_t pp_read(struct file *file, char __user *buf, size_t count, 11662306a36Sopenharmony_ci loff_t *ppos) 11762306a36Sopenharmony_ci{ 11862306a36Sopenharmony_ci unsigned int minor = iminor(file_inode(file)); 11962306a36Sopenharmony_ci struct pp_struct *pp = file->private_data; 12062306a36Sopenharmony_ci char *kbuffer; 12162306a36Sopenharmony_ci ssize_t bytes_read = 0; 12262306a36Sopenharmony_ci struct parport *pport; 12362306a36Sopenharmony_ci int mode; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci if (!(pp->flags & PP_CLAIMED)) { 12662306a36Sopenharmony_ci /* Don't have the port claimed */ 12762306a36Sopenharmony_ci pr_debug(CHRDEV "%x: claim the port first\n", minor); 12862306a36Sopenharmony_ci return -EINVAL; 12962306a36Sopenharmony_ci } 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci /* Trivial case. */ 13262306a36Sopenharmony_ci if (count == 0) 13362306a36Sopenharmony_ci return 0; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci kbuffer = kmalloc(min_t(size_t, count, PP_BUFFER_SIZE), GFP_KERNEL); 13662306a36Sopenharmony_ci if (!kbuffer) 13762306a36Sopenharmony_ci return -ENOMEM; 13862306a36Sopenharmony_ci pport = pp->pdev->port; 13962306a36Sopenharmony_ci mode = pport->ieee1284.mode & ~(IEEE1284_DEVICEID | IEEE1284_ADDR); 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci parport_set_timeout(pp->pdev, 14262306a36Sopenharmony_ci (file->f_flags & O_NONBLOCK) ? 14362306a36Sopenharmony_ci PARPORT_INACTIVITY_O_NONBLOCK : 14462306a36Sopenharmony_ci pp->default_inactivity); 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci while (bytes_read == 0) { 14762306a36Sopenharmony_ci ssize_t need = min_t(unsigned long, count, PP_BUFFER_SIZE); 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci if (mode == IEEE1284_MODE_EPP) { 15062306a36Sopenharmony_ci /* various specials for EPP mode */ 15162306a36Sopenharmony_ci int flags = 0; 15262306a36Sopenharmony_ci size_t (*fn)(struct parport *, void *, size_t, int); 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci if (pp->flags & PP_W91284PIC) 15562306a36Sopenharmony_ci flags |= PARPORT_W91284PIC; 15662306a36Sopenharmony_ci if (pp->flags & PP_FASTREAD) 15762306a36Sopenharmony_ci flags |= PARPORT_EPP_FAST; 15862306a36Sopenharmony_ci if (pport->ieee1284.mode & IEEE1284_ADDR) 15962306a36Sopenharmony_ci fn = pport->ops->epp_read_addr; 16062306a36Sopenharmony_ci else 16162306a36Sopenharmony_ci fn = pport->ops->epp_read_data; 16262306a36Sopenharmony_ci bytes_read = (*fn)(pport, kbuffer, need, flags); 16362306a36Sopenharmony_ci } else { 16462306a36Sopenharmony_ci bytes_read = parport_read(pport, kbuffer, need); 16562306a36Sopenharmony_ci } 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci if (bytes_read != 0) 16862306a36Sopenharmony_ci break; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci if (file->f_flags & O_NONBLOCK) { 17162306a36Sopenharmony_ci bytes_read = -EAGAIN; 17262306a36Sopenharmony_ci break; 17362306a36Sopenharmony_ci } 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci if (signal_pending(current)) { 17662306a36Sopenharmony_ci bytes_read = -ERESTARTSYS; 17762306a36Sopenharmony_ci break; 17862306a36Sopenharmony_ci } 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci cond_resched(); 18162306a36Sopenharmony_ci } 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci parport_set_timeout(pp->pdev, pp->default_inactivity); 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci if (bytes_read > 0 && copy_to_user(buf, kbuffer, bytes_read)) 18662306a36Sopenharmony_ci bytes_read = -EFAULT; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci kfree(kbuffer); 18962306a36Sopenharmony_ci pp_enable_irq(pp); 19062306a36Sopenharmony_ci return bytes_read; 19162306a36Sopenharmony_ci} 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_cistatic ssize_t pp_write(struct file *file, const char __user *buf, 19462306a36Sopenharmony_ci size_t count, loff_t *ppos) 19562306a36Sopenharmony_ci{ 19662306a36Sopenharmony_ci unsigned int minor = iminor(file_inode(file)); 19762306a36Sopenharmony_ci struct pp_struct *pp = file->private_data; 19862306a36Sopenharmony_ci char *kbuffer; 19962306a36Sopenharmony_ci ssize_t bytes_written = 0; 20062306a36Sopenharmony_ci ssize_t wrote; 20162306a36Sopenharmony_ci int mode; 20262306a36Sopenharmony_ci struct parport *pport; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci if (!(pp->flags & PP_CLAIMED)) { 20562306a36Sopenharmony_ci /* Don't have the port claimed */ 20662306a36Sopenharmony_ci pr_debug(CHRDEV "%x: claim the port first\n", minor); 20762306a36Sopenharmony_ci return -EINVAL; 20862306a36Sopenharmony_ci } 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci kbuffer = kmalloc(min_t(size_t, count, PP_BUFFER_SIZE), GFP_KERNEL); 21162306a36Sopenharmony_ci if (!kbuffer) 21262306a36Sopenharmony_ci return -ENOMEM; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci pport = pp->pdev->port; 21562306a36Sopenharmony_ci mode = pport->ieee1284.mode & ~(IEEE1284_DEVICEID | IEEE1284_ADDR); 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci parport_set_timeout(pp->pdev, 21862306a36Sopenharmony_ci (file->f_flags & O_NONBLOCK) ? 21962306a36Sopenharmony_ci PARPORT_INACTIVITY_O_NONBLOCK : 22062306a36Sopenharmony_ci pp->default_inactivity); 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci while (bytes_written < count) { 22362306a36Sopenharmony_ci ssize_t n = min_t(unsigned long, count - bytes_written, PP_BUFFER_SIZE); 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci if (copy_from_user(kbuffer, buf + bytes_written, n)) { 22662306a36Sopenharmony_ci bytes_written = -EFAULT; 22762306a36Sopenharmony_ci break; 22862306a36Sopenharmony_ci } 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci if ((pp->flags & PP_FASTWRITE) && (mode == IEEE1284_MODE_EPP)) { 23162306a36Sopenharmony_ci /* do a fast EPP write */ 23262306a36Sopenharmony_ci if (pport->ieee1284.mode & IEEE1284_ADDR) { 23362306a36Sopenharmony_ci wrote = pport->ops->epp_write_addr(pport, 23462306a36Sopenharmony_ci kbuffer, n, PARPORT_EPP_FAST); 23562306a36Sopenharmony_ci } else { 23662306a36Sopenharmony_ci wrote = pport->ops->epp_write_data(pport, 23762306a36Sopenharmony_ci kbuffer, n, PARPORT_EPP_FAST); 23862306a36Sopenharmony_ci } 23962306a36Sopenharmony_ci } else { 24062306a36Sopenharmony_ci wrote = parport_write(pp->pdev->port, kbuffer, n); 24162306a36Sopenharmony_ci } 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci if (wrote <= 0) { 24462306a36Sopenharmony_ci if (!bytes_written) 24562306a36Sopenharmony_ci bytes_written = wrote; 24662306a36Sopenharmony_ci break; 24762306a36Sopenharmony_ci } 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci bytes_written += wrote; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci if (file->f_flags & O_NONBLOCK) { 25262306a36Sopenharmony_ci if (!bytes_written) 25362306a36Sopenharmony_ci bytes_written = -EAGAIN; 25462306a36Sopenharmony_ci break; 25562306a36Sopenharmony_ci } 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci if (signal_pending(current)) 25862306a36Sopenharmony_ci break; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci cond_resched(); 26162306a36Sopenharmony_ci } 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci parport_set_timeout(pp->pdev, pp->default_inactivity); 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci kfree(kbuffer); 26662306a36Sopenharmony_ci pp_enable_irq(pp); 26762306a36Sopenharmony_ci return bytes_written; 26862306a36Sopenharmony_ci} 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_cistatic void pp_irq(void *private) 27162306a36Sopenharmony_ci{ 27262306a36Sopenharmony_ci struct pp_struct *pp = private; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci if (pp->irqresponse) { 27562306a36Sopenharmony_ci parport_write_control(pp->pdev->port, pp->irqctl); 27662306a36Sopenharmony_ci pp->irqresponse = 0; 27762306a36Sopenharmony_ci } 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci atomic_inc(&pp->irqc); 28062306a36Sopenharmony_ci wake_up_interruptible(&pp->irq_wait); 28162306a36Sopenharmony_ci} 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_cistatic int register_device(int minor, struct pp_struct *pp) 28462306a36Sopenharmony_ci{ 28562306a36Sopenharmony_ci struct parport *port; 28662306a36Sopenharmony_ci struct pardevice *pdev = NULL; 28762306a36Sopenharmony_ci char *name; 28862306a36Sopenharmony_ci struct pardev_cb ppdev_cb; 28962306a36Sopenharmony_ci int rc = 0, index; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci name = kasprintf(GFP_KERNEL, CHRDEV "%x", minor); 29262306a36Sopenharmony_ci if (name == NULL) 29362306a36Sopenharmony_ci return -ENOMEM; 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci port = parport_find_number(minor); 29662306a36Sopenharmony_ci if (!port) { 29762306a36Sopenharmony_ci pr_warn("%s: no associated port!\n", name); 29862306a36Sopenharmony_ci rc = -ENXIO; 29962306a36Sopenharmony_ci goto err; 30062306a36Sopenharmony_ci } 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci index = ida_simple_get(&ida_index, 0, 0, GFP_KERNEL); 30362306a36Sopenharmony_ci memset(&ppdev_cb, 0, sizeof(ppdev_cb)); 30462306a36Sopenharmony_ci ppdev_cb.irq_func = pp_irq; 30562306a36Sopenharmony_ci ppdev_cb.flags = (pp->flags & PP_EXCL) ? PARPORT_FLAG_EXCL : 0; 30662306a36Sopenharmony_ci ppdev_cb.private = pp; 30762306a36Sopenharmony_ci pdev = parport_register_dev_model(port, name, &ppdev_cb, index); 30862306a36Sopenharmony_ci parport_put_port(port); 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci if (!pdev) { 31162306a36Sopenharmony_ci pr_warn("%s: failed to register device!\n", name); 31262306a36Sopenharmony_ci rc = -ENXIO; 31362306a36Sopenharmony_ci ida_simple_remove(&ida_index, index); 31462306a36Sopenharmony_ci goto err; 31562306a36Sopenharmony_ci } 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci pp->pdev = pdev; 31862306a36Sopenharmony_ci pp->index = index; 31962306a36Sopenharmony_ci dev_dbg(&pdev->dev, "registered pardevice\n"); 32062306a36Sopenharmony_cierr: 32162306a36Sopenharmony_ci kfree(name); 32262306a36Sopenharmony_ci return rc; 32362306a36Sopenharmony_ci} 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_cistatic enum ieee1284_phase init_phase(int mode) 32662306a36Sopenharmony_ci{ 32762306a36Sopenharmony_ci switch (mode & ~(IEEE1284_DEVICEID 32862306a36Sopenharmony_ci | IEEE1284_ADDR)) { 32962306a36Sopenharmony_ci case IEEE1284_MODE_NIBBLE: 33062306a36Sopenharmony_ci case IEEE1284_MODE_BYTE: 33162306a36Sopenharmony_ci return IEEE1284_PH_REV_IDLE; 33262306a36Sopenharmony_ci } 33362306a36Sopenharmony_ci return IEEE1284_PH_FWD_IDLE; 33462306a36Sopenharmony_ci} 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_cistatic int pp_set_timeout(struct pardevice *pdev, long tv_sec, int tv_usec) 33762306a36Sopenharmony_ci{ 33862306a36Sopenharmony_ci long to_jiffies; 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci if ((tv_sec < 0) || (tv_usec < 0)) 34162306a36Sopenharmony_ci return -EINVAL; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci to_jiffies = usecs_to_jiffies(tv_usec); 34462306a36Sopenharmony_ci to_jiffies += tv_sec * HZ; 34562306a36Sopenharmony_ci if (to_jiffies <= 0) 34662306a36Sopenharmony_ci return -EINVAL; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci pdev->timeout = to_jiffies; 34962306a36Sopenharmony_ci return 0; 35062306a36Sopenharmony_ci} 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_cistatic int pp_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 35362306a36Sopenharmony_ci{ 35462306a36Sopenharmony_ci unsigned int minor = iminor(file_inode(file)); 35562306a36Sopenharmony_ci struct pp_struct *pp = file->private_data; 35662306a36Sopenharmony_ci struct parport *port; 35762306a36Sopenharmony_ci void __user *argp = (void __user *)arg; 35862306a36Sopenharmony_ci struct ieee1284_info *info; 35962306a36Sopenharmony_ci unsigned char reg; 36062306a36Sopenharmony_ci unsigned char mask; 36162306a36Sopenharmony_ci int mode; 36262306a36Sopenharmony_ci s32 time32[2]; 36362306a36Sopenharmony_ci s64 time64[2]; 36462306a36Sopenharmony_ci struct timespec64 ts; 36562306a36Sopenharmony_ci int ret; 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci /* First handle the cases that don't take arguments. */ 36862306a36Sopenharmony_ci switch (cmd) { 36962306a36Sopenharmony_ci case PPCLAIM: 37062306a36Sopenharmony_ci { 37162306a36Sopenharmony_ci if (pp->flags & PP_CLAIMED) { 37262306a36Sopenharmony_ci dev_dbg(&pp->pdev->dev, "you've already got it!\n"); 37362306a36Sopenharmony_ci return -EINVAL; 37462306a36Sopenharmony_ci } 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci /* Deferred device registration. */ 37762306a36Sopenharmony_ci if (!pp->pdev) { 37862306a36Sopenharmony_ci int err = register_device(minor, pp); 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci if (err) 38162306a36Sopenharmony_ci return err; 38262306a36Sopenharmony_ci } 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci ret = parport_claim_or_block(pp->pdev); 38562306a36Sopenharmony_ci if (ret < 0) 38662306a36Sopenharmony_ci return ret; 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci pp->flags |= PP_CLAIMED; 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci /* For interrupt-reporting to work, we need to be 39162306a36Sopenharmony_ci * informed of each interrupt. */ 39262306a36Sopenharmony_ci pp_enable_irq(pp); 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci /* We may need to fix up the state machine. */ 39562306a36Sopenharmony_ci info = &pp->pdev->port->ieee1284; 39662306a36Sopenharmony_ci pp->saved_state.mode = info->mode; 39762306a36Sopenharmony_ci pp->saved_state.phase = info->phase; 39862306a36Sopenharmony_ci info->mode = pp->state.mode; 39962306a36Sopenharmony_ci info->phase = pp->state.phase; 40062306a36Sopenharmony_ci pp->default_inactivity = parport_set_timeout(pp->pdev, 0); 40162306a36Sopenharmony_ci parport_set_timeout(pp->pdev, pp->default_inactivity); 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci return 0; 40462306a36Sopenharmony_ci } 40562306a36Sopenharmony_ci case PPEXCL: 40662306a36Sopenharmony_ci if (pp->pdev) { 40762306a36Sopenharmony_ci dev_dbg(&pp->pdev->dev, 40862306a36Sopenharmony_ci "too late for PPEXCL; already registered\n"); 40962306a36Sopenharmony_ci if (pp->flags & PP_EXCL) 41062306a36Sopenharmony_ci /* But it's not really an error. */ 41162306a36Sopenharmony_ci return 0; 41262306a36Sopenharmony_ci /* There's no chance of making the driver happy. */ 41362306a36Sopenharmony_ci return -EINVAL; 41462306a36Sopenharmony_ci } 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci /* Just remember to register the device exclusively 41762306a36Sopenharmony_ci * when we finally do the registration. */ 41862306a36Sopenharmony_ci pp->flags |= PP_EXCL; 41962306a36Sopenharmony_ci return 0; 42062306a36Sopenharmony_ci case PPSETMODE: 42162306a36Sopenharmony_ci { 42262306a36Sopenharmony_ci int mode; 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci if (copy_from_user(&mode, argp, sizeof(mode))) 42562306a36Sopenharmony_ci return -EFAULT; 42662306a36Sopenharmony_ci /* FIXME: validate mode */ 42762306a36Sopenharmony_ci pp->state.mode = mode; 42862306a36Sopenharmony_ci pp->state.phase = init_phase(mode); 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci if (pp->flags & PP_CLAIMED) { 43162306a36Sopenharmony_ci pp->pdev->port->ieee1284.mode = mode; 43262306a36Sopenharmony_ci pp->pdev->port->ieee1284.phase = pp->state.phase; 43362306a36Sopenharmony_ci } 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci return 0; 43662306a36Sopenharmony_ci } 43762306a36Sopenharmony_ci case PPGETMODE: 43862306a36Sopenharmony_ci { 43962306a36Sopenharmony_ci int mode; 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci if (pp->flags & PP_CLAIMED) 44262306a36Sopenharmony_ci mode = pp->pdev->port->ieee1284.mode; 44362306a36Sopenharmony_ci else 44462306a36Sopenharmony_ci mode = pp->state.mode; 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci if (copy_to_user(argp, &mode, sizeof(mode))) 44762306a36Sopenharmony_ci return -EFAULT; 44862306a36Sopenharmony_ci return 0; 44962306a36Sopenharmony_ci } 45062306a36Sopenharmony_ci case PPSETPHASE: 45162306a36Sopenharmony_ci { 45262306a36Sopenharmony_ci int phase; 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci if (copy_from_user(&phase, argp, sizeof(phase))) 45562306a36Sopenharmony_ci return -EFAULT; 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci /* FIXME: validate phase */ 45862306a36Sopenharmony_ci pp->state.phase = phase; 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci if (pp->flags & PP_CLAIMED) 46162306a36Sopenharmony_ci pp->pdev->port->ieee1284.phase = phase; 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci return 0; 46462306a36Sopenharmony_ci } 46562306a36Sopenharmony_ci case PPGETPHASE: 46662306a36Sopenharmony_ci { 46762306a36Sopenharmony_ci int phase; 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci if (pp->flags & PP_CLAIMED) 47062306a36Sopenharmony_ci phase = pp->pdev->port->ieee1284.phase; 47162306a36Sopenharmony_ci else 47262306a36Sopenharmony_ci phase = pp->state.phase; 47362306a36Sopenharmony_ci if (copy_to_user(argp, &phase, sizeof(phase))) 47462306a36Sopenharmony_ci return -EFAULT; 47562306a36Sopenharmony_ci return 0; 47662306a36Sopenharmony_ci } 47762306a36Sopenharmony_ci case PPGETMODES: 47862306a36Sopenharmony_ci { 47962306a36Sopenharmony_ci unsigned int modes; 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci port = parport_find_number(minor); 48262306a36Sopenharmony_ci if (!port) 48362306a36Sopenharmony_ci return -ENODEV; 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci modes = port->modes; 48662306a36Sopenharmony_ci parport_put_port(port); 48762306a36Sopenharmony_ci if (copy_to_user(argp, &modes, sizeof(modes))) 48862306a36Sopenharmony_ci return -EFAULT; 48962306a36Sopenharmony_ci return 0; 49062306a36Sopenharmony_ci } 49162306a36Sopenharmony_ci case PPSETFLAGS: 49262306a36Sopenharmony_ci { 49362306a36Sopenharmony_ci int uflags; 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci if (copy_from_user(&uflags, argp, sizeof(uflags))) 49662306a36Sopenharmony_ci return -EFAULT; 49762306a36Sopenharmony_ci pp->flags &= ~PP_FLAGMASK; 49862306a36Sopenharmony_ci pp->flags |= (uflags & PP_FLAGMASK); 49962306a36Sopenharmony_ci return 0; 50062306a36Sopenharmony_ci } 50162306a36Sopenharmony_ci case PPGETFLAGS: 50262306a36Sopenharmony_ci { 50362306a36Sopenharmony_ci int uflags; 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci uflags = pp->flags & PP_FLAGMASK; 50662306a36Sopenharmony_ci if (copy_to_user(argp, &uflags, sizeof(uflags))) 50762306a36Sopenharmony_ci return -EFAULT; 50862306a36Sopenharmony_ci return 0; 50962306a36Sopenharmony_ci } 51062306a36Sopenharmony_ci } /* end switch() */ 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci /* Everything else requires the port to be claimed, so check 51362306a36Sopenharmony_ci * that now. */ 51462306a36Sopenharmony_ci if ((pp->flags & PP_CLAIMED) == 0) { 51562306a36Sopenharmony_ci pr_debug(CHRDEV "%x: claim the port first\n", minor); 51662306a36Sopenharmony_ci return -EINVAL; 51762306a36Sopenharmony_ci } 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci port = pp->pdev->port; 52062306a36Sopenharmony_ci switch (cmd) { 52162306a36Sopenharmony_ci case PPRSTATUS: 52262306a36Sopenharmony_ci reg = parport_read_status(port); 52362306a36Sopenharmony_ci if (copy_to_user(argp, ®, sizeof(reg))) 52462306a36Sopenharmony_ci return -EFAULT; 52562306a36Sopenharmony_ci return 0; 52662306a36Sopenharmony_ci case PPRDATA: 52762306a36Sopenharmony_ci reg = parport_read_data(port); 52862306a36Sopenharmony_ci if (copy_to_user(argp, ®, sizeof(reg))) 52962306a36Sopenharmony_ci return -EFAULT; 53062306a36Sopenharmony_ci return 0; 53162306a36Sopenharmony_ci case PPRCONTROL: 53262306a36Sopenharmony_ci reg = parport_read_control(port); 53362306a36Sopenharmony_ci if (copy_to_user(argp, ®, sizeof(reg))) 53462306a36Sopenharmony_ci return -EFAULT; 53562306a36Sopenharmony_ci return 0; 53662306a36Sopenharmony_ci case PPYIELD: 53762306a36Sopenharmony_ci parport_yield_blocking(pp->pdev); 53862306a36Sopenharmony_ci return 0; 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci case PPRELEASE: 54162306a36Sopenharmony_ci /* Save the state machine's state. */ 54262306a36Sopenharmony_ci info = &pp->pdev->port->ieee1284; 54362306a36Sopenharmony_ci pp->state.mode = info->mode; 54462306a36Sopenharmony_ci pp->state.phase = info->phase; 54562306a36Sopenharmony_ci info->mode = pp->saved_state.mode; 54662306a36Sopenharmony_ci info->phase = pp->saved_state.phase; 54762306a36Sopenharmony_ci parport_release(pp->pdev); 54862306a36Sopenharmony_ci pp->flags &= ~PP_CLAIMED; 54962306a36Sopenharmony_ci return 0; 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci case PPWCONTROL: 55262306a36Sopenharmony_ci if (copy_from_user(®, argp, sizeof(reg))) 55362306a36Sopenharmony_ci return -EFAULT; 55462306a36Sopenharmony_ci parport_write_control(port, reg); 55562306a36Sopenharmony_ci return 0; 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci case PPWDATA: 55862306a36Sopenharmony_ci if (copy_from_user(®, argp, sizeof(reg))) 55962306a36Sopenharmony_ci return -EFAULT; 56062306a36Sopenharmony_ci parport_write_data(port, reg); 56162306a36Sopenharmony_ci return 0; 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci case PPFCONTROL: 56462306a36Sopenharmony_ci if (copy_from_user(&mask, argp, 56562306a36Sopenharmony_ci sizeof(mask))) 56662306a36Sopenharmony_ci return -EFAULT; 56762306a36Sopenharmony_ci if (copy_from_user(®, 1 + (unsigned char __user *) arg, 56862306a36Sopenharmony_ci sizeof(reg))) 56962306a36Sopenharmony_ci return -EFAULT; 57062306a36Sopenharmony_ci parport_frob_control(port, mask, reg); 57162306a36Sopenharmony_ci return 0; 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci case PPDATADIR: 57462306a36Sopenharmony_ci if (copy_from_user(&mode, argp, sizeof(mode))) 57562306a36Sopenharmony_ci return -EFAULT; 57662306a36Sopenharmony_ci if (mode) 57762306a36Sopenharmony_ci port->ops->data_reverse(port); 57862306a36Sopenharmony_ci else 57962306a36Sopenharmony_ci port->ops->data_forward(port); 58062306a36Sopenharmony_ci return 0; 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci case PPNEGOT: 58362306a36Sopenharmony_ci if (copy_from_user(&mode, argp, sizeof(mode))) 58462306a36Sopenharmony_ci return -EFAULT; 58562306a36Sopenharmony_ci switch ((ret = parport_negotiate(port, mode))) { 58662306a36Sopenharmony_ci case 0: break; 58762306a36Sopenharmony_ci case -1: /* handshake failed, peripheral not IEEE 1284 */ 58862306a36Sopenharmony_ci ret = -EIO; 58962306a36Sopenharmony_ci break; 59062306a36Sopenharmony_ci case 1: /* handshake succeeded, peripheral rejected mode */ 59162306a36Sopenharmony_ci ret = -ENXIO; 59262306a36Sopenharmony_ci break; 59362306a36Sopenharmony_ci } 59462306a36Sopenharmony_ci pp_enable_irq(pp); 59562306a36Sopenharmony_ci return ret; 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci case PPWCTLONIRQ: 59862306a36Sopenharmony_ci if (copy_from_user(®, argp, sizeof(reg))) 59962306a36Sopenharmony_ci return -EFAULT; 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci /* Remember what to set the control lines to, for next 60262306a36Sopenharmony_ci * time we get an interrupt. */ 60362306a36Sopenharmony_ci pp->irqctl = reg; 60462306a36Sopenharmony_ci pp->irqresponse = 1; 60562306a36Sopenharmony_ci return 0; 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci case PPCLRIRQ: 60862306a36Sopenharmony_ci ret = atomic_read(&pp->irqc); 60962306a36Sopenharmony_ci if (copy_to_user(argp, &ret, sizeof(ret))) 61062306a36Sopenharmony_ci return -EFAULT; 61162306a36Sopenharmony_ci atomic_sub(ret, &pp->irqc); 61262306a36Sopenharmony_ci return 0; 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci case PPSETTIME32: 61562306a36Sopenharmony_ci if (copy_from_user(time32, argp, sizeof(time32))) 61662306a36Sopenharmony_ci return -EFAULT; 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci if ((time32[0] < 0) || (time32[1] < 0)) 61962306a36Sopenharmony_ci return -EINVAL; 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci return pp_set_timeout(pp->pdev, time32[0], time32[1]); 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci case PPSETTIME64: 62462306a36Sopenharmony_ci if (copy_from_user(time64, argp, sizeof(time64))) 62562306a36Sopenharmony_ci return -EFAULT; 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci if ((time64[0] < 0) || (time64[1] < 0)) 62862306a36Sopenharmony_ci return -EINVAL; 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_SPARC64) && !in_compat_syscall()) 63162306a36Sopenharmony_ci time64[1] >>= 32; 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci return pp_set_timeout(pp->pdev, time64[0], time64[1]); 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci case PPGETTIME32: 63662306a36Sopenharmony_ci jiffies_to_timespec64(pp->pdev->timeout, &ts); 63762306a36Sopenharmony_ci time32[0] = ts.tv_sec; 63862306a36Sopenharmony_ci time32[1] = ts.tv_nsec / NSEC_PER_USEC; 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci if (copy_to_user(argp, time32, sizeof(time32))) 64162306a36Sopenharmony_ci return -EFAULT; 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci return 0; 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci case PPGETTIME64: 64662306a36Sopenharmony_ci jiffies_to_timespec64(pp->pdev->timeout, &ts); 64762306a36Sopenharmony_ci time64[0] = ts.tv_sec; 64862306a36Sopenharmony_ci time64[1] = ts.tv_nsec / NSEC_PER_USEC; 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_SPARC64) && !in_compat_syscall()) 65162306a36Sopenharmony_ci time64[1] <<= 32; 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci if (copy_to_user(argp, time64, sizeof(time64))) 65462306a36Sopenharmony_ci return -EFAULT; 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci return 0; 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci default: 65962306a36Sopenharmony_ci dev_dbg(&pp->pdev->dev, "What? (cmd=0x%x)\n", cmd); 66062306a36Sopenharmony_ci return -EINVAL; 66162306a36Sopenharmony_ci } 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci /* Keep the compiler happy */ 66462306a36Sopenharmony_ci return 0; 66562306a36Sopenharmony_ci} 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_cistatic long pp_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 66862306a36Sopenharmony_ci{ 66962306a36Sopenharmony_ci long ret; 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci mutex_lock(&pp_do_mutex); 67262306a36Sopenharmony_ci ret = pp_do_ioctl(file, cmd, arg); 67362306a36Sopenharmony_ci mutex_unlock(&pp_do_mutex); 67462306a36Sopenharmony_ci return ret; 67562306a36Sopenharmony_ci} 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_cistatic int pp_open(struct inode *inode, struct file *file) 67862306a36Sopenharmony_ci{ 67962306a36Sopenharmony_ci unsigned int minor = iminor(inode); 68062306a36Sopenharmony_ci struct pp_struct *pp; 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci if (minor >= PARPORT_MAX) 68362306a36Sopenharmony_ci return -ENXIO; 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci pp = kmalloc(sizeof(struct pp_struct), GFP_KERNEL); 68662306a36Sopenharmony_ci if (!pp) 68762306a36Sopenharmony_ci return -ENOMEM; 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci pp->state.mode = IEEE1284_MODE_COMPAT; 69062306a36Sopenharmony_ci pp->state.phase = init_phase(pp->state.mode); 69162306a36Sopenharmony_ci pp->flags = 0; 69262306a36Sopenharmony_ci pp->irqresponse = 0; 69362306a36Sopenharmony_ci atomic_set(&pp->irqc, 0); 69462306a36Sopenharmony_ci init_waitqueue_head(&pp->irq_wait); 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci /* Defer the actual device registration until the first claim. 69762306a36Sopenharmony_ci * That way, we know whether or not the driver wants to have 69862306a36Sopenharmony_ci * exclusive access to the port (PPEXCL). 69962306a36Sopenharmony_ci */ 70062306a36Sopenharmony_ci pp->pdev = NULL; 70162306a36Sopenharmony_ci file->private_data = pp; 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci return 0; 70462306a36Sopenharmony_ci} 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_cistatic int pp_release(struct inode *inode, struct file *file) 70762306a36Sopenharmony_ci{ 70862306a36Sopenharmony_ci unsigned int minor = iminor(inode); 70962306a36Sopenharmony_ci struct pp_struct *pp = file->private_data; 71062306a36Sopenharmony_ci int compat_negot; 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci compat_negot = 0; 71362306a36Sopenharmony_ci if (!(pp->flags & PP_CLAIMED) && pp->pdev && 71462306a36Sopenharmony_ci (pp->state.mode != IEEE1284_MODE_COMPAT)) { 71562306a36Sopenharmony_ci struct ieee1284_info *info; 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci /* parport released, but not in compatibility mode */ 71862306a36Sopenharmony_ci parport_claim_or_block(pp->pdev); 71962306a36Sopenharmony_ci pp->flags |= PP_CLAIMED; 72062306a36Sopenharmony_ci info = &pp->pdev->port->ieee1284; 72162306a36Sopenharmony_ci pp->saved_state.mode = info->mode; 72262306a36Sopenharmony_ci pp->saved_state.phase = info->phase; 72362306a36Sopenharmony_ci info->mode = pp->state.mode; 72462306a36Sopenharmony_ci info->phase = pp->state.phase; 72562306a36Sopenharmony_ci compat_negot = 1; 72662306a36Sopenharmony_ci } else if ((pp->flags & PP_CLAIMED) && pp->pdev && 72762306a36Sopenharmony_ci (pp->pdev->port->ieee1284.mode != IEEE1284_MODE_COMPAT)) { 72862306a36Sopenharmony_ci compat_negot = 2; 72962306a36Sopenharmony_ci } 73062306a36Sopenharmony_ci if (compat_negot) { 73162306a36Sopenharmony_ci parport_negotiate(pp->pdev->port, IEEE1284_MODE_COMPAT); 73262306a36Sopenharmony_ci dev_dbg(&pp->pdev->dev, 73362306a36Sopenharmony_ci "negotiated back to compatibility mode because user-space forgot\n"); 73462306a36Sopenharmony_ci } 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci if ((pp->flags & PP_CLAIMED) && pp->pdev) { 73762306a36Sopenharmony_ci struct ieee1284_info *info; 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci info = &pp->pdev->port->ieee1284; 74062306a36Sopenharmony_ci pp->state.mode = info->mode; 74162306a36Sopenharmony_ci pp->state.phase = info->phase; 74262306a36Sopenharmony_ci info->mode = pp->saved_state.mode; 74362306a36Sopenharmony_ci info->phase = pp->saved_state.phase; 74462306a36Sopenharmony_ci parport_release(pp->pdev); 74562306a36Sopenharmony_ci if (compat_negot != 1) { 74662306a36Sopenharmony_ci pr_debug(CHRDEV "%x: released pardevice " 74762306a36Sopenharmony_ci "because user-space forgot\n", minor); 74862306a36Sopenharmony_ci } 74962306a36Sopenharmony_ci } 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci if (pp->pdev) { 75262306a36Sopenharmony_ci parport_unregister_device(pp->pdev); 75362306a36Sopenharmony_ci ida_simple_remove(&ida_index, pp->index); 75462306a36Sopenharmony_ci pp->pdev = NULL; 75562306a36Sopenharmony_ci pr_debug(CHRDEV "%x: unregistered pardevice\n", minor); 75662306a36Sopenharmony_ci } 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci kfree(pp); 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_ci return 0; 76162306a36Sopenharmony_ci} 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci/* No kernel lock held - fine */ 76462306a36Sopenharmony_cistatic __poll_t pp_poll(struct file *file, poll_table *wait) 76562306a36Sopenharmony_ci{ 76662306a36Sopenharmony_ci struct pp_struct *pp = file->private_data; 76762306a36Sopenharmony_ci __poll_t mask = 0; 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci poll_wait(file, &pp->irq_wait, wait); 77062306a36Sopenharmony_ci if (atomic_read(&pp->irqc)) 77162306a36Sopenharmony_ci mask |= EPOLLIN | EPOLLRDNORM; 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_ci return mask; 77462306a36Sopenharmony_ci} 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_cistatic const struct class ppdev_class = { 77762306a36Sopenharmony_ci .name = CHRDEV, 77862306a36Sopenharmony_ci}; 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_cistatic const struct file_operations pp_fops = { 78162306a36Sopenharmony_ci .owner = THIS_MODULE, 78262306a36Sopenharmony_ci .llseek = no_llseek, 78362306a36Sopenharmony_ci .read = pp_read, 78462306a36Sopenharmony_ci .write = pp_write, 78562306a36Sopenharmony_ci .poll = pp_poll, 78662306a36Sopenharmony_ci .unlocked_ioctl = pp_ioctl, 78762306a36Sopenharmony_ci .compat_ioctl = compat_ptr_ioctl, 78862306a36Sopenharmony_ci .open = pp_open, 78962306a36Sopenharmony_ci .release = pp_release, 79062306a36Sopenharmony_ci}; 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_cistatic void pp_attach(struct parport *port) 79362306a36Sopenharmony_ci{ 79462306a36Sopenharmony_ci struct device *ret; 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_ci if (devices[port->number]) 79762306a36Sopenharmony_ci return; 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci ret = device_create(&ppdev_class, port->dev, 80062306a36Sopenharmony_ci MKDEV(PP_MAJOR, port->number), NULL, 80162306a36Sopenharmony_ci "parport%d", port->number); 80262306a36Sopenharmony_ci if (IS_ERR(ret)) { 80362306a36Sopenharmony_ci pr_err("Failed to create device parport%d\n", 80462306a36Sopenharmony_ci port->number); 80562306a36Sopenharmony_ci return; 80662306a36Sopenharmony_ci } 80762306a36Sopenharmony_ci devices[port->number] = ret; 80862306a36Sopenharmony_ci} 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_cistatic void pp_detach(struct parport *port) 81162306a36Sopenharmony_ci{ 81262306a36Sopenharmony_ci if (!devices[port->number]) 81362306a36Sopenharmony_ci return; 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_ci device_destroy(&ppdev_class, MKDEV(PP_MAJOR, port->number)); 81662306a36Sopenharmony_ci devices[port->number] = NULL; 81762306a36Sopenharmony_ci} 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_cistatic int pp_probe(struct pardevice *par_dev) 82062306a36Sopenharmony_ci{ 82162306a36Sopenharmony_ci struct device_driver *drv = par_dev->dev.driver; 82262306a36Sopenharmony_ci int len = strlen(drv->name); 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_ci if (strncmp(par_dev->name, drv->name, len)) 82562306a36Sopenharmony_ci return -ENODEV; 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci return 0; 82862306a36Sopenharmony_ci} 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_cistatic struct parport_driver pp_driver = { 83162306a36Sopenharmony_ci .name = CHRDEV, 83262306a36Sopenharmony_ci .probe = pp_probe, 83362306a36Sopenharmony_ci .match_port = pp_attach, 83462306a36Sopenharmony_ci .detach = pp_detach, 83562306a36Sopenharmony_ci .devmodel = true, 83662306a36Sopenharmony_ci}; 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_cistatic int __init ppdev_init(void) 83962306a36Sopenharmony_ci{ 84062306a36Sopenharmony_ci int err = 0; 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_ci if (register_chrdev(PP_MAJOR, CHRDEV, &pp_fops)) { 84362306a36Sopenharmony_ci pr_warn(CHRDEV ": unable to get major %d\n", PP_MAJOR); 84462306a36Sopenharmony_ci return -EIO; 84562306a36Sopenharmony_ci } 84662306a36Sopenharmony_ci err = class_register(&ppdev_class); 84762306a36Sopenharmony_ci if (err) 84862306a36Sopenharmony_ci goto out_chrdev; 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ci err = parport_register_driver(&pp_driver); 85162306a36Sopenharmony_ci if (err < 0) { 85262306a36Sopenharmony_ci pr_warn(CHRDEV ": unable to register with parport\n"); 85362306a36Sopenharmony_ci goto out_class; 85462306a36Sopenharmony_ci } 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_ci pr_info(PP_VERSION "\n"); 85762306a36Sopenharmony_ci goto out; 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_ciout_class: 86062306a36Sopenharmony_ci class_unregister(&ppdev_class); 86162306a36Sopenharmony_ciout_chrdev: 86262306a36Sopenharmony_ci unregister_chrdev(PP_MAJOR, CHRDEV); 86362306a36Sopenharmony_ciout: 86462306a36Sopenharmony_ci return err; 86562306a36Sopenharmony_ci} 86662306a36Sopenharmony_ci 86762306a36Sopenharmony_cistatic void __exit ppdev_cleanup(void) 86862306a36Sopenharmony_ci{ 86962306a36Sopenharmony_ci /* Clean up all parport stuff */ 87062306a36Sopenharmony_ci parport_unregister_driver(&pp_driver); 87162306a36Sopenharmony_ci class_unregister(&ppdev_class); 87262306a36Sopenharmony_ci unregister_chrdev(PP_MAJOR, CHRDEV); 87362306a36Sopenharmony_ci} 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_cimodule_init(ppdev_init); 87662306a36Sopenharmony_cimodule_exit(ppdev_cleanup); 87762306a36Sopenharmony_ci 87862306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 87962306a36Sopenharmony_ciMODULE_ALIAS_CHARDEV_MAJOR(PP_MAJOR); 880