14a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson/* 24a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson * LIRC base driver 34a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson * 44a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson * by Artur Lipowski <alipowski@interia.pl> 54a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson * 64a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson * This program is free software; you can redistribute it and/or modify 74a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson * it under the terms of the GNU General Public License as published by 84a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson * the Free Software Foundation; either version 2 of the License, or 94a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson * (at your option) any later version. 104a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson * 114a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson * This program is distributed in the hope that it will be useful, 124a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson * but WITHOUT ANY WARRANTY; without even the implied warranty of 134a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 144a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson * GNU General Public License for more details. 154a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson * 164a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson * You should have received a copy of the GNU General Public License 174a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson * along with this program; if not, write to the Free Software 184a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 194a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson * 204a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson */ 214a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 224a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson#include <linux/module.h> 234a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson#include <linux/kernel.h> 244a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson#include <linux/sched.h> 254a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson#include <linux/errno.h> 264a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson#include <linux/ioctl.h> 274a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson#include <linux/fs.h> 284a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson#include <linux/poll.h> 294a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson#include <linux/completion.h> 304a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson#include <linux/mutex.h> 314a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson#include <linux/wait.h> 324a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson#include <linux/unistd.h> 334a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson#include <linux/kthread.h> 344a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson#include <linux/bitops.h> 354a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson#include <linux/device.h> 364a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson#include <linux/cdev.h> 374a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 38ca7a722db1c90dfe0dba165ecef01d6ac8cfee0dSrinivas Kandagatla#include <media/rc-core.h> 394a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson#include <media/lirc.h> 405690085e7ba7f3081c6ab6db3a3b543444ad8a21Jarod Wilson#include <media/lirc_dev.h> 414a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 4290ab5ee94171b3e28de6bb42ee30b527014e0be7Rusty Russellstatic bool debug; 434a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 444a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson#define IRCTL_DEV_NAME "BaseRemoteCtl" 454a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson#define NOPLUG -1 464a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson#define LOGHEAD "lirc_dev (%s[%d]): " 474a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 484a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilsonstatic dev_t lirc_base_dev; 494a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 504a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilsonstruct irctl { 514a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson struct lirc_driver d; 524a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson int attached; 534a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson int open; 544a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 554a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson struct mutex irctl_lock; 564a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson struct lirc_buffer *buf; 574a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson unsigned int chunk_size; 584a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 598de111e27688798623b9e9062235bb0cac29f599Jarod Wilson struct cdev *cdev; 608de111e27688798623b9e9062235bb0cac29f599Jarod Wilson 614a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson struct task_struct *task; 624a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson long jiffies_to_wait; 634a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson}; 644a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 654a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilsonstatic DEFINE_MUTEX(lirc_dev_lock); 664a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 674a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilsonstatic struct irctl *irctls[MAX_IRCTL_DEVICES]; 684a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 694a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson/* Only used for sysfs but defined to void otherwise */ 704a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilsonstatic struct class *lirc_class; 714a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 724a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson/* helper function 734a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson * initializes the irctl structure 744a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson */ 75578fcb8e5f3046493932105c404792a2fe0e066fJarod Wilsonstatic void lirc_irctl_init(struct irctl *ir) 764a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson{ 774a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson mutex_init(&ir->irctl_lock); 784a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson ir->d.minor = NOPLUG; 794a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson} 804a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 81578fcb8e5f3046493932105c404792a2fe0e066fJarod Wilsonstatic void lirc_irctl_cleanup(struct irctl *ir) 824a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson{ 834a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson dev_dbg(ir->d.dev, LOGHEAD "cleaning up\n", ir->d.name, ir->d.minor); 844a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 854a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson device_destroy(lirc_class, MKDEV(MAJOR(lirc_base_dev), ir->d.minor)); 864a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 874a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson if (ir->buf != ir->d.rbuf) { 884a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson lirc_buffer_free(ir->buf); 894a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson kfree(ir->buf); 904a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson } 914a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson ir->buf = NULL; 924a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson} 934a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 944a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson/* helper function 954a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson * reads key codes from driver and puts them into buffer 964a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson * returns 0 on success 974a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson */ 98578fcb8e5f3046493932105c404792a2fe0e066fJarod Wilsonstatic int lirc_add_to_buf(struct irctl *ir) 994a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson{ 1004a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson if (ir->d.add_to_buf) { 1014a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson int res = -ENODATA; 1024a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson int got_data = 0; 1034a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 1044a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson /* 1054a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson * service the device as long as it is returning 1064a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson * data and we have space 1074a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson */ 1084a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilsonget_data: 1094a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson res = ir->d.add_to_buf(ir->d.data, ir->buf); 1104a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson if (res == 0) { 1114a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson got_data++; 1124a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson goto get_data; 1134a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson } 1144a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 1154a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson if (res == -ENODEV) 1164a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson kthread_stop(ir->task); 1174a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 1184a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson return got_data ? 0 : res; 1194a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson } 1204a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 1214a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson return 0; 1224a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson} 1234a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 1244a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson/* main function of the polling thread 1254a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson */ 1264a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilsonstatic int lirc_thread(void *irctl) 1274a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson{ 1284a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson struct irctl *ir = irctl; 1294a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 1304a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson dev_dbg(ir->d.dev, LOGHEAD "poll thread started\n", 1314a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson ir->d.name, ir->d.minor); 1324a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 1334a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson do { 1344a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson if (ir->open) { 1354a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson if (ir->jiffies_to_wait) { 1364a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson set_current_state(TASK_INTERRUPTIBLE); 1374a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson schedule_timeout(ir->jiffies_to_wait); 1384a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson } 1394a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson if (kthread_should_stop()) 1404a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson break; 141578fcb8e5f3046493932105c404792a2fe0e066fJarod Wilson if (!lirc_add_to_buf(ir)) 1424a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson wake_up_interruptible(&ir->buf->wait_poll); 1434a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson } else { 1444a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson set_current_state(TASK_INTERRUPTIBLE); 1454a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson schedule(); 1464a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson } 1474a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson } while (!kthread_should_stop()); 1484a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 1494a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson dev_dbg(ir->d.dev, LOGHEAD "poll thread ended\n", 1504a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson ir->d.name, ir->d.minor); 1514a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 1524a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson return 0; 1534a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson} 1544a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 1554a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 15675ef9de1267ba171ecefafca35758e2be0db10dcAl Virostatic const struct file_operations lirc_dev_fops = { 1574a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson .owner = THIS_MODULE, 1584a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson .read = lirc_dev_fop_read, 1594a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson .write = lirc_dev_fop_write, 1604a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson .poll = lirc_dev_fop_poll, 161044e5878c2158d701e6f47a9604910589a384ee2Arnd Bergmann .unlocked_ioctl = lirc_dev_fop_ioctl, 1628be292cc035ebc3422f08e84682626dd8ed8334bJarod Wilson#ifdef CONFIG_COMPAT 1638be292cc035ebc3422f08e84682626dd8ed8334bJarod Wilson .compat_ioctl = lirc_dev_fop_ioctl, 1648be292cc035ebc3422f08e84682626dd8ed8334bJarod Wilson#endif 1654a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson .open = lirc_dev_fop_open, 1664a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson .release = lirc_dev_fop_close, 1676038f373a3dc1f1c26496e60b6c40b164716f07eArnd Bergmann .llseek = noop_llseek, 1684a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson}; 1694a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 1704a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilsonstatic int lirc_cdev_add(struct irctl *ir) 1714a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson{ 1728de111e27688798623b9e9062235bb0cac29f599Jarod Wilson int retval = -ENOMEM; 1734a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson struct lirc_driver *d = &ir->d; 1748de111e27688798623b9e9062235bb0cac29f599Jarod Wilson struct cdev *cdev; 1758de111e27688798623b9e9062235bb0cac29f599Jarod Wilson 1768de111e27688798623b9e9062235bb0cac29f599Jarod Wilson cdev = kzalloc(sizeof(*cdev), GFP_KERNEL); 1778de111e27688798623b9e9062235bb0cac29f599Jarod Wilson if (!cdev) 1788de111e27688798623b9e9062235bb0cac29f599Jarod Wilson goto err_out; 1794a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 1804a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson if (d->fops) { 181c1cbb7029e81894c056680d61c64741bd2ff246fJarod Wilson cdev_init(cdev, d->fops); 182c1cbb7029e81894c056680d61c64741bd2ff246fJarod Wilson cdev->owner = d->owner; 1834a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson } else { 184c1cbb7029e81894c056680d61c64741bd2ff246fJarod Wilson cdev_init(cdev, &lirc_dev_fops); 185c1cbb7029e81894c056680d61c64741bd2ff246fJarod Wilson cdev->owner = THIS_MODULE; 1864a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson } 187b395cbac36e58a55729fe7e6262a3f0b1691bcedVasiliy Kulikov retval = kobject_set_name(&cdev->kobj, "lirc%d", d->minor); 188b395cbac36e58a55729fe7e6262a3f0b1691bcedVasiliy Kulikov if (retval) 1898de111e27688798623b9e9062235bb0cac29f599Jarod Wilson goto err_out; 1904a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 191c1cbb7029e81894c056680d61c64741bd2ff246fJarod Wilson retval = cdev_add(cdev, MKDEV(MAJOR(lirc_base_dev), d->minor), 1); 1928de111e27688798623b9e9062235bb0cac29f599Jarod Wilson if (retval) { 193c1cbb7029e81894c056680d61c64741bd2ff246fJarod Wilson kobject_put(&cdev->kobj); 1948de111e27688798623b9e9062235bb0cac29f599Jarod Wilson goto err_out; 1958de111e27688798623b9e9062235bb0cac29f599Jarod Wilson } 1964a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 1978de111e27688798623b9e9062235bb0cac29f599Jarod Wilson ir->cdev = cdev; 1988de111e27688798623b9e9062235bb0cac29f599Jarod Wilson 1998de111e27688798623b9e9062235bb0cac29f599Jarod Wilson return 0; 2008de111e27688798623b9e9062235bb0cac29f599Jarod Wilson 2018de111e27688798623b9e9062235bb0cac29f599Jarod Wilsonerr_out: 2028de111e27688798623b9e9062235bb0cac29f599Jarod Wilson kfree(cdev); 2034a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson return retval; 2044a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson} 2054a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 2064a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilsonint lirc_register_driver(struct lirc_driver *d) 2074a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson{ 2084a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson struct irctl *ir; 2094a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson int minor; 2104a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson int bytes_in_key; 2114a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson unsigned int chunk_size; 2124a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson unsigned int buffer_size; 2134a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson int err; 2144a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 2154a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson if (!d) { 2164a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson printk(KERN_ERR "lirc_dev: lirc_register_driver: " 2174a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson "driver pointer must be not NULL!\n"); 2184a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson err = -EBADRQC; 2194a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson goto out; 2204a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson } 2214a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 222715d29a74450696696dc064f85ba4ff0eaadb1d2Jarod Wilson if (!d->dev) { 223715d29a74450696696dc064f85ba4ff0eaadb1d2Jarod Wilson printk(KERN_ERR "%s: dev pointer not filled in!\n", __func__); 224715d29a74450696696dc064f85ba4ff0eaadb1d2Jarod Wilson err = -EINVAL; 225715d29a74450696696dc064f85ba4ff0eaadb1d2Jarod Wilson goto out; 226715d29a74450696696dc064f85ba4ff0eaadb1d2Jarod Wilson } 227715d29a74450696696dc064f85ba4ff0eaadb1d2Jarod Wilson 2284a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson if (MAX_IRCTL_DEVICES <= d->minor) { 2294a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson dev_err(d->dev, "lirc_dev: lirc_register_driver: " 2304a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson "\"minor\" must be between 0 and %d (%d)!\n", 2318de111e27688798623b9e9062235bb0cac29f599Jarod Wilson MAX_IRCTL_DEVICES - 1, d->minor); 2324a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson err = -EBADRQC; 2334a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson goto out; 2344a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson } 2354a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 2364a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson if (1 > d->code_length || (BUFLEN * 8) < d->code_length) { 2374a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson dev_err(d->dev, "lirc_dev: lirc_register_driver: " 2384a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson "code length in bits for minor (%d) " 2394a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson "must be less than %d!\n", 2404a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson d->minor, BUFLEN * 8); 2414a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson err = -EBADRQC; 2424a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson goto out; 2434a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson } 2444a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 2454a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson dev_dbg(d->dev, "lirc_dev: lirc_register_driver: sample_rate: %d\n", 2464a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson d->sample_rate); 2474a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson if (d->sample_rate) { 2484a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson if (2 > d->sample_rate || HZ < d->sample_rate) { 2494a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson dev_err(d->dev, "lirc_dev: lirc_register_driver: " 2504a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson "sample_rate must be between 2 and %d!\n", HZ); 2514a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson err = -EBADRQC; 2524a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson goto out; 2534a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson } 2544a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson if (!d->add_to_buf) { 2554a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson dev_err(d->dev, "lirc_dev: lirc_register_driver: " 2564a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson "add_to_buf cannot be NULL when " 2574a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson "sample_rate is set\n"); 2584a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson err = -EBADRQC; 2594a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson goto out; 2604a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson } 2614a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson } else if (!(d->fops && d->fops->read) && !d->rbuf) { 2624a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson dev_err(d->dev, "lirc_dev: lirc_register_driver: " 2634a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson "fops->read and rbuf cannot all be NULL!\n"); 2644a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson err = -EBADRQC; 2654a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson goto out; 2664a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson } else if (!d->rbuf) { 2674a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson if (!(d->fops && d->fops->read && d->fops->poll && 268044e5878c2158d701e6f47a9604910589a384ee2Arnd Bergmann d->fops->unlocked_ioctl)) { 2694a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson dev_err(d->dev, "lirc_dev: lirc_register_driver: " 270044e5878c2158d701e6f47a9604910589a384ee2Arnd Bergmann "neither read, poll nor unlocked_ioctl can be NULL!\n"); 2714a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson err = -EBADRQC; 2724a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson goto out; 2734a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson } 2744a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson } 2754a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 2764a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson mutex_lock(&lirc_dev_lock); 2774a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 2784a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson minor = d->minor; 2794a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 2804a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson if (minor < 0) { 2814a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson /* find first free slot for driver */ 2824a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson for (minor = 0; minor < MAX_IRCTL_DEVICES; minor++) 2834a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson if (!irctls[minor]) 2844a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson break; 2854a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson if (MAX_IRCTL_DEVICES == minor) { 2864a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson dev_err(d->dev, "lirc_dev: lirc_register_driver: " 2874a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson "no free slots for drivers!\n"); 2884a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson err = -ENOMEM; 2894a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson goto out_lock; 2904a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson } 2914a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson } else if (irctls[minor]) { 2924a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson dev_err(d->dev, "lirc_dev: lirc_register_driver: " 2934a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson "minor (%d) just registered!\n", minor); 2944a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson err = -EBUSY; 2954a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson goto out_lock; 2964a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson } 2974a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 2984a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson ir = kzalloc(sizeof(struct irctl), GFP_KERNEL); 2994a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson if (!ir) { 3004a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson err = -ENOMEM; 3014a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson goto out_lock; 3024a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson } 303578fcb8e5f3046493932105c404792a2fe0e066fJarod Wilson lirc_irctl_init(ir); 3044a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson irctls[minor] = ir; 3054a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson d->minor = minor; 3064a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 3074a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson if (d->sample_rate) { 3084a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson ir->jiffies_to_wait = HZ / d->sample_rate; 3094a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson } else { 3104a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson /* it means - wait for external event in task queue */ 3114a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson ir->jiffies_to_wait = 0; 3124a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson } 3134a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 3144a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson /* some safety check 8-) */ 3154a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson d->name[sizeof(d->name)-1] = '\0'; 3164a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 3174a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson bytes_in_key = BITS_TO_LONGS(d->code_length) + 3184a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson (d->code_length % 8 ? 1 : 0); 3194a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson buffer_size = d->buffer_size ? d->buffer_size : BUFLEN / bytes_in_key; 3204a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson chunk_size = d->chunk_size ? d->chunk_size : bytes_in_key; 3214a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 3224a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson if (d->rbuf) { 3234a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson ir->buf = d->rbuf; 3244a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson } else { 3254a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson ir->buf = kmalloc(sizeof(struct lirc_buffer), GFP_KERNEL); 3264a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson if (!ir->buf) { 3274a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson err = -ENOMEM; 3284a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson goto out_lock; 3294a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson } 3304a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson err = lirc_buffer_init(ir->buf, chunk_size, buffer_size); 3314a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson if (err) { 3324a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson kfree(ir->buf); 3334a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson goto out_lock; 3344a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson } 3354a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson } 3364a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson ir->chunk_size = ir->buf->chunk_size; 3374a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 3384a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson if (d->features == 0) 3394a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson d->features = LIRC_CAN_REC_LIRCCODE; 3404a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 3414a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson ir->d = *d; 3424a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 3434a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson device_create(lirc_class, ir->d.dev, 3444a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson MKDEV(MAJOR(lirc_base_dev), ir->d.minor), NULL, 3454a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson "lirc%u", ir->d.minor); 3464a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 3474a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson if (d->sample_rate) { 3484a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson /* try to fire up polling thread */ 3494a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson ir->task = kthread_run(lirc_thread, (void *)ir, "lirc_dev"); 3504a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson if (IS_ERR(ir->task)) { 3514a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson dev_err(d->dev, "lirc_dev: lirc_register_driver: " 3524a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson "cannot run poll thread for minor = %d\n", 3534a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson d->minor); 3544a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson err = -ECHILD; 3554a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson goto out_sysfs; 3564a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson } 3574a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson } 3584a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 3594a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson err = lirc_cdev_add(ir); 3604a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson if (err) 3614a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson goto out_sysfs; 3624a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 3634a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson ir->attached = 1; 3644a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson mutex_unlock(&lirc_dev_lock); 3654a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 3664a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson dev_info(ir->d.dev, "lirc_dev: driver %s registered at minor = %d\n", 3674a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson ir->d.name, ir->d.minor); 3684a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson return minor; 3694a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 3704a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilsonout_sysfs: 3714a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson device_destroy(lirc_class, MKDEV(MAJOR(lirc_base_dev), ir->d.minor)); 3724a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilsonout_lock: 3734a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson mutex_unlock(&lirc_dev_lock); 3744a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilsonout: 3754a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson return err; 3764a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson} 3774a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod WilsonEXPORT_SYMBOL(lirc_register_driver); 3784a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 3794a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilsonint lirc_unregister_driver(int minor) 3804a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson{ 3814a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson struct irctl *ir; 382c1cbb7029e81894c056680d61c64741bd2ff246fJarod Wilson struct cdev *cdev; 3834a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 3844a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson if (minor < 0 || minor >= MAX_IRCTL_DEVICES) { 3854fc215430f6764f3e30bdf86bc2a46ab2e0c4404Jarod Wilson printk(KERN_ERR "lirc_dev: %s: minor (%d) must be between " 3868de111e27688798623b9e9062235bb0cac29f599Jarod Wilson "0 and %d!\n", __func__, minor, MAX_IRCTL_DEVICES - 1); 3874a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson return -EBADRQC; 3884a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson } 3894a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 3904a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson ir = irctls[minor]; 391df1868e4ee605444bbb98505126bdfb3519749afJarod Wilson if (!ir) { 3924fc215430f6764f3e30bdf86bc2a46ab2e0c4404Jarod Wilson printk(KERN_ERR "lirc_dev: %s: failed to get irctl struct " 3934fc215430f6764f3e30bdf86bc2a46ab2e0c4404Jarod Wilson "for minor %d!\n", __func__, minor); 394df1868e4ee605444bbb98505126bdfb3519749afJarod Wilson return -ENOENT; 395df1868e4ee605444bbb98505126bdfb3519749afJarod Wilson } 3964a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 3978de111e27688798623b9e9062235bb0cac29f599Jarod Wilson cdev = ir->cdev; 3984a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 3994a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson mutex_lock(&lirc_dev_lock); 4004a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 4014a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson if (ir->d.minor != minor) { 4024fc215430f6764f3e30bdf86bc2a46ab2e0c4404Jarod Wilson printk(KERN_ERR "lirc_dev: %s: minor (%d) device not " 4034fc215430f6764f3e30bdf86bc2a46ab2e0c4404Jarod Wilson "registered!\n", __func__, minor); 4044a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson mutex_unlock(&lirc_dev_lock); 4054a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson return -ENOENT; 4064a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson } 4074a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 4084a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson /* end up polling thread */ 4094a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson if (ir->task) 4104a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson kthread_stop(ir->task); 4114a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 4124a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson dev_dbg(ir->d.dev, "lirc_dev: driver %s unregistered from minor = %d\n", 4134a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson ir->d.name, ir->d.minor); 4144a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 4154a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson ir->attached = 0; 4164a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson if (ir->open) { 4174a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson dev_dbg(ir->d.dev, LOGHEAD "releasing opened driver\n", 4184a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson ir->d.name, ir->d.minor); 4194a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson wake_up_interruptible(&ir->buf->wait_poll); 4204a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson mutex_lock(&ir->irctl_lock); 4214a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson ir->d.set_use_dec(ir->d.data); 422c1cbb7029e81894c056680d61c64741bd2ff246fJarod Wilson module_put(cdev->owner); 4234a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson mutex_unlock(&ir->irctl_lock); 4244a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson } else { 425578fcb8e5f3046493932105c404792a2fe0e066fJarod Wilson lirc_irctl_cleanup(ir); 426c1cbb7029e81894c056680d61c64741bd2ff246fJarod Wilson cdev_del(cdev); 4278de111e27688798623b9e9062235bb0cac29f599Jarod Wilson kfree(cdev); 4284a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson kfree(ir); 4294a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson irctls[minor] = NULL; 4304a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson } 4314a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 4324a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson mutex_unlock(&lirc_dev_lock); 4334a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 4344a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson return 0; 4354a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson} 4364a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod WilsonEXPORT_SYMBOL(lirc_unregister_driver); 4374a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 4384a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilsonint lirc_dev_fop_open(struct inode *inode, struct file *file) 4394a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson{ 4404a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson struct irctl *ir; 441c1cbb7029e81894c056680d61c64741bd2ff246fJarod Wilson struct cdev *cdev; 4424a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson int retval = 0; 4434a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 4444a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson if (iminor(inode) >= MAX_IRCTL_DEVICES) { 4454a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson printk(KERN_WARNING "lirc_dev [%d]: open result = -ENODEV\n", 4464a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson iminor(inode)); 4474a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson return -ENODEV; 4484a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson } 4494a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 4504a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson if (mutex_lock_interruptible(&lirc_dev_lock)) 4514a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson return -ERESTARTSYS; 4524a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 4534a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson ir = irctls[iminor(inode)]; 4544a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson if (!ir) { 4554a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson retval = -ENODEV; 4564a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson goto error; 4574a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson } 4584a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 4594a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson dev_dbg(ir->d.dev, LOGHEAD "open called\n", ir->d.name, ir->d.minor); 4604a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 4614a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson if (ir->d.minor == NOPLUG) { 4624a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson retval = -ENODEV; 4634a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson goto error; 4644a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson } 4654a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 4664a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson if (ir->open) { 4674a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson retval = -EBUSY; 4684a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson goto error; 4694a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson } 4704a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 471ca7a722db1c90dfe0dba165ecef01d6ac8cfee0dSrinivas Kandagatla if (ir->d.rdev) { 472ca7a722db1c90dfe0dba165ecef01d6ac8cfee0dSrinivas Kandagatla retval = rc_open(ir->d.rdev); 473ca7a722db1c90dfe0dba165ecef01d6ac8cfee0dSrinivas Kandagatla if (retval) 474ca7a722db1c90dfe0dba165ecef01d6ac8cfee0dSrinivas Kandagatla goto error; 475ca7a722db1c90dfe0dba165ecef01d6ac8cfee0dSrinivas Kandagatla } 476ca7a722db1c90dfe0dba165ecef01d6ac8cfee0dSrinivas Kandagatla 4778de111e27688798623b9e9062235bb0cac29f599Jarod Wilson cdev = ir->cdev; 478c1cbb7029e81894c056680d61c64741bd2ff246fJarod Wilson if (try_module_get(cdev->owner)) { 479c1cbb7029e81894c056680d61c64741bd2ff246fJarod Wilson ir->open++; 4804a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson retval = ir->d.set_use_inc(ir->d.data); 4814a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 4824a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson if (retval) { 483c1cbb7029e81894c056680d61c64741bd2ff246fJarod Wilson module_put(cdev->owner); 484c1cbb7029e81894c056680d61c64741bd2ff246fJarod Wilson ir->open--; 4854a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson } else { 4864a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson lirc_buffer_clear(ir->buf); 4874a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson } 4884a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson if (ir->task) 4894a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson wake_up_process(ir->task); 4904a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson } 4914a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 4924a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilsonerror: 4934a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson if (ir) 4944a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson dev_dbg(ir->d.dev, LOGHEAD "open result = %d\n", 4954a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson ir->d.name, ir->d.minor, retval); 4964a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 4974a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson mutex_unlock(&lirc_dev_lock); 4984a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 499d9d2e9d5c9eead1f73f92f5fc03424dab90b6c95Arnd Bergmann nonseekable_open(inode, file); 500d9d2e9d5c9eead1f73f92f5fc03424dab90b6c95Arnd Bergmann 5014a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson return retval; 5024a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson} 5034a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod WilsonEXPORT_SYMBOL(lirc_dev_fop_open); 5044a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 5054a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilsonint lirc_dev_fop_close(struct inode *inode, struct file *file) 5064a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson{ 5074a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson struct irctl *ir = irctls[iminor(inode)]; 5088de111e27688798623b9e9062235bb0cac29f599Jarod Wilson struct cdev *cdev; 5094a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 510715d29a74450696696dc064f85ba4ff0eaadb1d2Jarod Wilson if (!ir) { 511715d29a74450696696dc064f85ba4ff0eaadb1d2Jarod Wilson printk(KERN_ERR "%s: called with invalid irctl\n", __func__); 512715d29a74450696696dc064f85ba4ff0eaadb1d2Jarod Wilson return -EINVAL; 513715d29a74450696696dc064f85ba4ff0eaadb1d2Jarod Wilson } 5144a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 5158de111e27688798623b9e9062235bb0cac29f599Jarod Wilson cdev = ir->cdev; 5168de111e27688798623b9e9062235bb0cac29f599Jarod Wilson 5174a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson dev_dbg(ir->d.dev, LOGHEAD "close called\n", ir->d.name, ir->d.minor); 5184a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 5194a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson WARN_ON(mutex_lock_killable(&lirc_dev_lock)); 5204a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 521ca7a722db1c90dfe0dba165ecef01d6ac8cfee0dSrinivas Kandagatla if (ir->d.rdev) 522ca7a722db1c90dfe0dba165ecef01d6ac8cfee0dSrinivas Kandagatla rc_close(ir->d.rdev); 523ca7a722db1c90dfe0dba165ecef01d6ac8cfee0dSrinivas Kandagatla 524715d29a74450696696dc064f85ba4ff0eaadb1d2Jarod Wilson ir->open--; 5254a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson if (ir->attached) { 5264a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson ir->d.set_use_dec(ir->d.data); 527c1cbb7029e81894c056680d61c64741bd2ff246fJarod Wilson module_put(cdev->owner); 5284a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson } else { 529578fcb8e5f3046493932105c404792a2fe0e066fJarod Wilson lirc_irctl_cleanup(ir); 530c1cbb7029e81894c056680d61c64741bd2ff246fJarod Wilson cdev_del(cdev); 5314a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson irctls[ir->d.minor] = NULL; 5328de111e27688798623b9e9062235bb0cac29f599Jarod Wilson kfree(cdev); 5334a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson kfree(ir); 5344a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson } 5354a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 5364a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson mutex_unlock(&lirc_dev_lock); 5374a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 5384a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson return 0; 5394a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson} 5404a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod WilsonEXPORT_SYMBOL(lirc_dev_fop_close); 5414a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 5424a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilsonunsigned int lirc_dev_fop_poll(struct file *file, poll_table *wait) 5434a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson{ 544496ad9aa8ef448058e36ca7a787c61f2e63f0f54Al Viro struct irctl *ir = irctls[iminor(file_inode(file))]; 5454a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson unsigned int ret; 5464a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 547715d29a74450696696dc064f85ba4ff0eaadb1d2Jarod Wilson if (!ir) { 548715d29a74450696696dc064f85ba4ff0eaadb1d2Jarod Wilson printk(KERN_ERR "%s: called with invalid irctl\n", __func__); 549715d29a74450696696dc064f85ba4ff0eaadb1d2Jarod Wilson return POLLERR; 550715d29a74450696696dc064f85ba4ff0eaadb1d2Jarod Wilson } 551715d29a74450696696dc064f85ba4ff0eaadb1d2Jarod Wilson 5524a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson dev_dbg(ir->d.dev, LOGHEAD "poll called\n", ir->d.name, ir->d.minor); 5534a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 5545c769a68beaee924e1dc90bf06e1b087b1d46237Dan Carpenter if (!ir->attached) 5554a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson return POLLERR; 5564a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 5574a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson poll_wait(file, &ir->buf->wait_poll, wait); 5584a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 5594a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson if (ir->buf) 5604a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson if (lirc_buffer_empty(ir->buf)) 5614a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson ret = 0; 5624a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson else 5634a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson ret = POLLIN | POLLRDNORM; 5644a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson else 5654a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson ret = POLLERR; 5664a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 5674a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson dev_dbg(ir->d.dev, LOGHEAD "poll result = %d\n", 5684a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson ir->d.name, ir->d.minor, ret); 5694a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 5704a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson return ret; 5714a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson} 5724a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod WilsonEXPORT_SYMBOL(lirc_dev_fop_poll); 5734a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 574044e5878c2158d701e6f47a9604910589a384ee2Arnd Bergmannlong lirc_dev_fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 5754a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson{ 576be1f985ffa49467f604318182616678b3e5184fdJarod Wilson __u32 mode; 5774a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson int result = 0; 578496ad9aa8ef448058e36ca7a787c61f2e63f0f54Al Viro struct irctl *ir = irctls[iminor(file_inode(file))]; 5794a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 5808be292cc035ebc3422f08e84682626dd8ed8334bJarod Wilson if (!ir) { 5818be292cc035ebc3422f08e84682626dd8ed8334bJarod Wilson printk(KERN_ERR "lirc_dev: %s: no irctl found!\n", __func__); 5828be292cc035ebc3422f08e84682626dd8ed8334bJarod Wilson return -ENODEV; 5838be292cc035ebc3422f08e84682626dd8ed8334bJarod Wilson } 5844a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 5854a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson dev_dbg(ir->d.dev, LOGHEAD "ioctl called (0x%x)\n", 5864a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson ir->d.name, ir->d.minor, cmd); 5874a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 5884a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson if (ir->d.minor == NOPLUG || !ir->attached) { 5894a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson dev_dbg(ir->d.dev, LOGHEAD "ioctl result = -ENODEV\n", 5904a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson ir->d.name, ir->d.minor); 5914a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson return -ENODEV; 5924a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson } 5934a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 5944a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson mutex_lock(&ir->irctl_lock); 5954a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 5964a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson switch (cmd) { 5974a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson case LIRC_GET_FEATURES: 59860519af3fd0e75a97036075fc657f1ebe87e0f0dHans Verkuil result = put_user(ir->d.features, (__u32 __user *)arg); 5994a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson break; 6004a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson case LIRC_GET_REC_MODE: 6014a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson if (!(ir->d.features & LIRC_CAN_REC_MASK)) { 6024a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson result = -ENOSYS; 6034a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson break; 6044a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson } 6054a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 6064a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson result = put_user(LIRC_REC2MODE 6074a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson (ir->d.features & LIRC_CAN_REC_MASK), 60860519af3fd0e75a97036075fc657f1ebe87e0f0dHans Verkuil (__u32 __user *)arg); 6094a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson break; 6104a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson case LIRC_SET_REC_MODE: 6114a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson if (!(ir->d.features & LIRC_CAN_REC_MASK)) { 6124a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson result = -ENOSYS; 6134a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson break; 6144a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson } 6154a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 61660519af3fd0e75a97036075fc657f1ebe87e0f0dHans Verkuil result = get_user(mode, (__u32 __user *)arg); 6174a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson if (!result && !(LIRC_MODE2REC(mode) & ir->d.features)) 6184a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson result = -EINVAL; 6194a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson /* 6204a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson * FIXME: We should actually set the mode somehow but 6214a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson * for now, lirc_serial doesn't support mode changing either 6224a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson */ 6234a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson break; 6244a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson case LIRC_GET_LENGTH: 62560519af3fd0e75a97036075fc657f1ebe87e0f0dHans Verkuil result = put_user(ir->d.code_length, (__u32 __user *)arg); 6264a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson break; 6274a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson case LIRC_GET_MIN_TIMEOUT: 6284a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson if (!(ir->d.features & LIRC_CAN_SET_REC_TIMEOUT) || 6294a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson ir->d.min_timeout == 0) { 6304a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson result = -ENOSYS; 6314a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson break; 6324a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson } 6334a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 63460519af3fd0e75a97036075fc657f1ebe87e0f0dHans Verkuil result = put_user(ir->d.min_timeout, (__u32 __user *)arg); 6354a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson break; 6364a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson case LIRC_GET_MAX_TIMEOUT: 6374a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson if (!(ir->d.features & LIRC_CAN_SET_REC_TIMEOUT) || 6384a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson ir->d.max_timeout == 0) { 6394a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson result = -ENOSYS; 6404a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson break; 6414a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson } 6424a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 64360519af3fd0e75a97036075fc657f1ebe87e0f0dHans Verkuil result = put_user(ir->d.max_timeout, (__u32 __user *)arg); 6444a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson break; 6454a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson default: 6464a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson result = -EINVAL; 6474a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson } 6484a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 6494a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson dev_dbg(ir->d.dev, LOGHEAD "ioctl result = %d\n", 6504a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson ir->d.name, ir->d.minor, result); 6514a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 6524a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson mutex_unlock(&ir->irctl_lock); 6534a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 6544a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson return result; 6554a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson} 6564a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod WilsonEXPORT_SYMBOL(lirc_dev_fop_ioctl); 6574a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 6584a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilsonssize_t lirc_dev_fop_read(struct file *file, 6590e835087dfe7db19f1f072046f5e116d4ec6662bDan Carpenter char __user *buffer, 6604a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson size_t length, 6614a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson loff_t *ppos) 6624a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson{ 663496ad9aa8ef448058e36ca7a787c61f2e63f0f54Al Viro struct irctl *ir = irctls[iminor(file_inode(file))]; 664715d29a74450696696dc064f85ba4ff0eaadb1d2Jarod Wilson unsigned char *buf; 6654a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson int ret = 0, written = 0; 6664a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson DECLARE_WAITQUEUE(wait, current); 6674a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 668715d29a74450696696dc064f85ba4ff0eaadb1d2Jarod Wilson if (!ir) { 669715d29a74450696696dc064f85ba4ff0eaadb1d2Jarod Wilson printk(KERN_ERR "%s: called with invalid irctl\n", __func__); 670715d29a74450696696dc064f85ba4ff0eaadb1d2Jarod Wilson return -ENODEV; 671715d29a74450696696dc064f85ba4ff0eaadb1d2Jarod Wilson } 672715d29a74450696696dc064f85ba4ff0eaadb1d2Jarod Wilson 6734a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson dev_dbg(ir->d.dev, LOGHEAD "read called\n", ir->d.name, ir->d.minor); 6744a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 675715d29a74450696696dc064f85ba4ff0eaadb1d2Jarod Wilson buf = kzalloc(ir->chunk_size, GFP_KERNEL); 676715d29a74450696696dc064f85ba4ff0eaadb1d2Jarod Wilson if (!buf) 677715d29a74450696696dc064f85ba4ff0eaadb1d2Jarod Wilson return -ENOMEM; 678715d29a74450696696dc064f85ba4ff0eaadb1d2Jarod Wilson 679250f7a5f62a08985af5cf7728ae7ba9edbfdc0a9Dan Carpenter if (mutex_lock_interruptible(&ir->irctl_lock)) { 680250f7a5f62a08985af5cf7728ae7ba9edbfdc0a9Dan Carpenter ret = -ERESTARTSYS; 681250f7a5f62a08985af5cf7728ae7ba9edbfdc0a9Dan Carpenter goto out_unlocked; 682250f7a5f62a08985af5cf7728ae7ba9edbfdc0a9Dan Carpenter } 6834a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson if (!ir->attached) { 684250f7a5f62a08985af5cf7728ae7ba9edbfdc0a9Dan Carpenter ret = -ENODEV; 685250f7a5f62a08985af5cf7728ae7ba9edbfdc0a9Dan Carpenter goto out_locked; 6864a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson } 6874a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 6884a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson if (length % ir->chunk_size) { 689250f7a5f62a08985af5cf7728ae7ba9edbfdc0a9Dan Carpenter ret = -EINVAL; 690250f7a5f62a08985af5cf7728ae7ba9edbfdc0a9Dan Carpenter goto out_locked; 6914a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson } 6924a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 6934a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson /* 6944a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson * we add ourselves to the task queue before buffer check 6954a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson * to avoid losing scan code (in case when queue is awaken somewhere 6964a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson * between while condition checking and scheduling) 6974a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson */ 6984a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson add_wait_queue(&ir->buf->wait_poll, &wait); 6994a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson set_current_state(TASK_INTERRUPTIBLE); 7004a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 7014a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson /* 7024a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson * while we didn't provide 'length' bytes, device is opened in blocking 7034a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson * mode and 'copy_to_user' is happy, wait for data. 7044a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson */ 7054a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson while (written < length && ret == 0) { 7064a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson if (lirc_buffer_empty(ir->buf)) { 7074a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson /* According to the read(2) man page, 'written' can be 7084a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson * returned as less than 'length', instead of blocking 7094a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson * again, returning -EWOULDBLOCK, or returning 7104a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson * -ERESTARTSYS */ 7114a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson if (written) 7124a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson break; 7134a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson if (file->f_flags & O_NONBLOCK) { 7144a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson ret = -EWOULDBLOCK; 7154a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson break; 7164a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson } 7174a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson if (signal_pending(current)) { 7184a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson ret = -ERESTARTSYS; 7194a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson break; 7204a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson } 7214a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 7224a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson mutex_unlock(&ir->irctl_lock); 7234a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson schedule(); 7244a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson set_current_state(TASK_INTERRUPTIBLE); 7254a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 7264a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson if (mutex_lock_interruptible(&ir->irctl_lock)) { 7274a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson ret = -ERESTARTSYS; 72869c271f33b949a7b1cbe6f7f39ce3db9e80997a2Jarod Wilson remove_wait_queue(&ir->buf->wait_poll, &wait); 72969c271f33b949a7b1cbe6f7f39ce3db9e80997a2Jarod Wilson set_current_state(TASK_RUNNING); 73069c271f33b949a7b1cbe6f7f39ce3db9e80997a2Jarod Wilson goto out_unlocked; 7314a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson } 7324a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 7334a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson if (!ir->attached) { 7344a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson ret = -ENODEV; 7354a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson break; 7364a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson } 7374a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson } else { 7384a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson lirc_buffer_read(ir->buf, buf); 73960519af3fd0e75a97036075fc657f1ebe87e0f0dHans Verkuil ret = copy_to_user((void __user *)buffer+written, buf, 7404a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson ir->buf->chunk_size); 741250f7a5f62a08985af5cf7728ae7ba9edbfdc0a9Dan Carpenter if (!ret) 742250f7a5f62a08985af5cf7728ae7ba9edbfdc0a9Dan Carpenter written += ir->buf->chunk_size; 743250f7a5f62a08985af5cf7728ae7ba9edbfdc0a9Dan Carpenter else 744250f7a5f62a08985af5cf7728ae7ba9edbfdc0a9Dan Carpenter ret = -EFAULT; 7454a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson } 7464a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson } 7474a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 7484a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson remove_wait_queue(&ir->buf->wait_poll, &wait); 7494a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson set_current_state(TASK_RUNNING); 750250f7a5f62a08985af5cf7728ae7ba9edbfdc0a9Dan Carpenter 751250f7a5f62a08985af5cf7728ae7ba9edbfdc0a9Dan Carpenterout_locked: 7524a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson mutex_unlock(&ir->irctl_lock); 7534a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 75469c271f33b949a7b1cbe6f7f39ce3db9e80997a2Jarod Wilsonout_unlocked: 755715d29a74450696696dc064f85ba4ff0eaadb1d2Jarod Wilson kfree(buf); 7564a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson dev_dbg(ir->d.dev, LOGHEAD "read result = %s (%d)\n", 757250f7a5f62a08985af5cf7728ae7ba9edbfdc0a9Dan Carpenter ir->d.name, ir->d.minor, ret ? "<fail>" : "<ok>", ret); 7584a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 7594a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson return ret ? ret : written; 7604a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson} 7614a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod WilsonEXPORT_SYMBOL(lirc_dev_fop_read); 7624a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 7634a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilsonvoid *lirc_get_pdata(struct file *file) 7644a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson{ 7650990a97a1fc649bf9a7e6057c326a835d1520847Al Viro return irctls[iminor(file_inode(file))]->d.data; 7664a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson} 7674a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod WilsonEXPORT_SYMBOL(lirc_get_pdata); 7684a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 7694a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 7700e835087dfe7db19f1f072046f5e116d4ec6662bDan Carpenterssize_t lirc_dev_fop_write(struct file *file, const char __user *buffer, 7714a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson size_t length, loff_t *ppos) 7724a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson{ 773496ad9aa8ef448058e36ca7a787c61f2e63f0f54Al Viro struct irctl *ir = irctls[iminor(file_inode(file))]; 7744a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 775715d29a74450696696dc064f85ba4ff0eaadb1d2Jarod Wilson if (!ir) { 776715d29a74450696696dc064f85ba4ff0eaadb1d2Jarod Wilson printk(KERN_ERR "%s: called with invalid irctl\n", __func__); 777715d29a74450696696dc064f85ba4ff0eaadb1d2Jarod Wilson return -ENODEV; 778715d29a74450696696dc064f85ba4ff0eaadb1d2Jarod Wilson } 779715d29a74450696696dc064f85ba4ff0eaadb1d2Jarod Wilson 7804a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson dev_dbg(ir->d.dev, LOGHEAD "write called\n", ir->d.name, ir->d.minor); 7814a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 7824a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson if (!ir->attached) 7834a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson return -ENODEV; 7844a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 7854a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson return -EINVAL; 7864a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson} 7874a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod WilsonEXPORT_SYMBOL(lirc_dev_fop_write); 7884a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 7894a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 7904a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilsonstatic int __init lirc_dev_init(void) 7914a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson{ 7924a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson int retval; 7934a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 7944a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson lirc_class = class_create(THIS_MODULE, "lirc"); 7954a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson if (IS_ERR(lirc_class)) { 7964a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson retval = PTR_ERR(lirc_class); 7974a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson printk(KERN_ERR "lirc_dev: class_create failed\n"); 7984a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson goto error; 7994a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson } 8004a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 8014a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson retval = alloc_chrdev_region(&lirc_base_dev, 0, MAX_IRCTL_DEVICES, 8024a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson IRCTL_DEV_NAME); 8034a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson if (retval) { 8044a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson class_destroy(lirc_class); 8054a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson printk(KERN_ERR "lirc_dev: alloc_chrdev_region failed\n"); 8064a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson goto error; 8074a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson } 8084a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 8094a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 8104a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson printk(KERN_INFO "lirc_dev: IR Remote Control driver registered, " 8114a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson "major %d \n", MAJOR(lirc_base_dev)); 8124a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 8134a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilsonerror: 8144a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson return retval; 8154a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson} 8164a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 8174a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 8184a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 8194a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilsonstatic void __exit lirc_dev_exit(void) 8204a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson{ 8214a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson class_destroy(lirc_class); 8224a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson unregister_chrdev_region(lirc_base_dev, MAX_IRCTL_DEVICES); 8234a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson printk(KERN_INFO "lirc_dev: module unloaded\n"); 8244a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson} 8254a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 8264a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilsonmodule_init(lirc_dev_init); 8274a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilsonmodule_exit(lirc_dev_exit); 8284a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 8294a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod WilsonMODULE_DESCRIPTION("LIRC base driver module"); 8304a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod WilsonMODULE_AUTHOR("Artur Lipowski"); 8314a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod WilsonMODULE_LICENSE("GPL"); 8324a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson 8334a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilsonmodule_param(debug, bool, S_IRUGO | S_IWUSR); 8344a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod WilsonMODULE_PARM_DESC(debug, "Enable debugging messages"); 835