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, ®, 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, ®, 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, ®, 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(®, 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(®, 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(®, 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(®, 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