Path: blob/master/drivers/media/rc/ir-lirc-codec.c
15109 views
/* ir-lirc-codec.c - rc-core to classic lirc interface bridge1*2* Copyright (C) 2010 by Jarod Wilson <[email protected]>3*4* This program is free software; you can redistribute it and/or modify5* it under the terms of the GNU General Public License as published by6* the Free Software Foundation version 2 of the License.7*8* This program is distributed in the hope that it will be useful,9* but WITHOUT ANY WARRANTY; without even the implied warranty of10* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the11* GNU General Public License for more details.12*/1314#include <linux/sched.h>15#include <linux/wait.h>16#include <media/lirc.h>17#include <media/lirc_dev.h>18#include <media/rc-core.h>19#include "rc-core-priv.h"2021#define LIRCBUF_SIZE 2562223/**24* ir_lirc_decode() - Send raw IR data to lirc_dev to be relayed to the25* lircd userspace daemon for decoding.26* @input_dev: the struct rc_dev descriptor of the device27* @duration: the struct ir_raw_event descriptor of the pulse/space28*29* This function returns -EINVAL if the lirc interfaces aren't wired up.30*/31static int ir_lirc_decode(struct rc_dev *dev, struct ir_raw_event ev)32{33struct lirc_codec *lirc = &dev->raw->lirc;34int sample;3536if (!(dev->raw->enabled_protocols & RC_TYPE_LIRC))37return 0;3839if (!dev->raw->lirc.drv || !dev->raw->lirc.drv->rbuf)40return -EINVAL;4142/* Packet start */43if (ev.reset)44return 0;4546/* Carrier reports */47if (ev.carrier_report) {48sample = LIRC_FREQUENCY(ev.carrier);49IR_dprintk(2, "carrier report (freq: %d)\n", sample);5051/* Packet end */52} else if (ev.timeout) {5354if (lirc->gap)55return 0;5657lirc->gap_start = ktime_get();58lirc->gap = true;59lirc->gap_duration = ev.duration;6061if (!lirc->send_timeout_reports)62return 0;6364sample = LIRC_TIMEOUT(ev.duration / 1000);65IR_dprintk(2, "timeout report (duration: %d)\n", sample);6667/* Normal sample */68} else {6970if (lirc->gap) {71int gap_sample;7273lirc->gap_duration += ktime_to_ns(ktime_sub(ktime_get(),74lirc->gap_start));7576/* Convert to ms and cap by LIRC_VALUE_MASK */77do_div(lirc->gap_duration, 1000);78lirc->gap_duration = min(lirc->gap_duration,79(u64)LIRC_VALUE_MASK);8081gap_sample = LIRC_SPACE(lirc->gap_duration);82lirc_buffer_write(dev->raw->lirc.drv->rbuf,83(unsigned char *) &gap_sample);84lirc->gap = false;85}8687sample = ev.pulse ? LIRC_PULSE(ev.duration / 1000) :88LIRC_SPACE(ev.duration / 1000);89IR_dprintk(2, "delivering %uus %s to lirc_dev\n",90TO_US(ev.duration), TO_STR(ev.pulse));91}9293lirc_buffer_write(dev->raw->lirc.drv->rbuf,94(unsigned char *) &sample);95wake_up(&dev->raw->lirc.drv->rbuf->wait_poll);9697return 0;98}99100static ssize_t ir_lirc_transmit_ir(struct file *file, const char *buf,101size_t n, loff_t *ppos)102{103struct lirc_codec *lirc;104struct rc_dev *dev;105int *txbuf; /* buffer with values to transmit */106int ret = 0;107size_t count;108109lirc = lirc_get_pdata(file);110if (!lirc)111return -EFAULT;112113if (n % sizeof(int))114return -EINVAL;115116count = n / sizeof(int);117if (count > LIRCBUF_SIZE || count % 2 == 0 || n % sizeof(int) != 0)118return -EINVAL;119120txbuf = memdup_user(buf, n);121if (IS_ERR(txbuf))122return PTR_ERR(txbuf);123124dev = lirc->dev;125if (!dev) {126ret = -EFAULT;127goto out;128}129130if (dev->tx_ir)131ret = dev->tx_ir(dev, txbuf, (u32)n);132133out:134kfree(txbuf);135return ret;136}137138static long ir_lirc_ioctl(struct file *filep, unsigned int cmd,139unsigned long __user arg)140{141struct lirc_codec *lirc;142struct rc_dev *dev;143int ret = 0;144__u32 val = 0, tmp;145146lirc = lirc_get_pdata(filep);147if (!lirc)148return -EFAULT;149150dev = lirc->dev;151if (!dev)152return -EFAULT;153154if (_IOC_DIR(cmd) & _IOC_WRITE) {155ret = get_user(val, (__u32 *)arg);156if (ret)157return ret;158}159160switch (cmd) {161162/* legacy support */163case LIRC_GET_SEND_MODE:164val = LIRC_CAN_SEND_PULSE & LIRC_CAN_SEND_MASK;165break;166167case LIRC_SET_SEND_MODE:168if (val != (LIRC_MODE_PULSE & LIRC_CAN_SEND_MASK))169return -EINVAL;170return 0;171172/* TX settings */173case LIRC_SET_TRANSMITTER_MASK:174if (!dev->s_tx_mask)175return -EINVAL;176177return dev->s_tx_mask(dev, val);178179case LIRC_SET_SEND_CARRIER:180if (!dev->s_tx_carrier)181return -EINVAL;182183return dev->s_tx_carrier(dev, val);184185case LIRC_SET_SEND_DUTY_CYCLE:186if (!dev->s_tx_duty_cycle)187return -ENOSYS;188189if (val <= 0 || val >= 100)190return -EINVAL;191192return dev->s_tx_duty_cycle(dev, val);193194/* RX settings */195case LIRC_SET_REC_CARRIER:196if (!dev->s_rx_carrier_range)197return -ENOSYS;198199if (val <= 0)200return -EINVAL;201202return dev->s_rx_carrier_range(dev,203dev->raw->lirc.carrier_low,204val);205206case LIRC_SET_REC_CARRIER_RANGE:207if (val <= 0)208return -EINVAL;209210dev->raw->lirc.carrier_low = val;211return 0;212213case LIRC_GET_REC_RESOLUTION:214val = dev->rx_resolution;215break;216217case LIRC_SET_WIDEBAND_RECEIVER:218if (!dev->s_learning_mode)219return -ENOSYS;220221return dev->s_learning_mode(dev, !!val);222223case LIRC_SET_MEASURE_CARRIER_MODE:224if (!dev->s_carrier_report)225return -ENOSYS;226227return dev->s_carrier_report(dev, !!val);228229/* Generic timeout support */230case LIRC_GET_MIN_TIMEOUT:231if (!dev->max_timeout)232return -ENOSYS;233val = dev->min_timeout / 1000;234break;235236case LIRC_GET_MAX_TIMEOUT:237if (!dev->max_timeout)238return -ENOSYS;239val = dev->max_timeout / 1000;240break;241242case LIRC_SET_REC_TIMEOUT:243if (!dev->max_timeout)244return -ENOSYS;245246tmp = val * 1000;247248if (tmp < dev->min_timeout ||249tmp > dev->max_timeout)250return -EINVAL;251252dev->timeout = tmp;253break;254255case LIRC_SET_REC_TIMEOUT_REPORTS:256lirc->send_timeout_reports = !!val;257break;258259default:260return lirc_dev_fop_ioctl(filep, cmd, arg);261}262263if (_IOC_DIR(cmd) & _IOC_READ)264ret = put_user(val, (__u32 *)arg);265266return ret;267}268269static int ir_lirc_open(void *data)270{271return 0;272}273274static void ir_lirc_close(void *data)275{276return;277}278279static struct file_operations lirc_fops = {280.owner = THIS_MODULE,281.write = ir_lirc_transmit_ir,282.unlocked_ioctl = ir_lirc_ioctl,283#ifdef CONFIG_COMPAT284.compat_ioctl = ir_lirc_ioctl,285#endif286.read = lirc_dev_fop_read,287.poll = lirc_dev_fop_poll,288.open = lirc_dev_fop_open,289.release = lirc_dev_fop_close,290.llseek = no_llseek,291};292293static int ir_lirc_register(struct rc_dev *dev)294{295struct lirc_driver *drv;296struct lirc_buffer *rbuf;297int rc = -ENOMEM;298unsigned long features;299300drv = kzalloc(sizeof(struct lirc_driver), GFP_KERNEL);301if (!drv)302return rc;303304rbuf = kzalloc(sizeof(struct lirc_buffer), GFP_KERNEL);305if (!rbuf)306goto rbuf_alloc_failed;307308rc = lirc_buffer_init(rbuf, sizeof(int), LIRCBUF_SIZE);309if (rc)310goto rbuf_init_failed;311312features = LIRC_CAN_REC_MODE2;313if (dev->tx_ir) {314features |= LIRC_CAN_SEND_PULSE;315if (dev->s_tx_mask)316features |= LIRC_CAN_SET_TRANSMITTER_MASK;317if (dev->s_tx_carrier)318features |= LIRC_CAN_SET_SEND_CARRIER;319if (dev->s_tx_duty_cycle)320features |= LIRC_CAN_SET_SEND_DUTY_CYCLE;321}322323if (dev->s_rx_carrier_range)324features |= LIRC_CAN_SET_REC_CARRIER |325LIRC_CAN_SET_REC_CARRIER_RANGE;326327if (dev->s_learning_mode)328features |= LIRC_CAN_USE_WIDEBAND_RECEIVER;329330if (dev->s_carrier_report)331features |= LIRC_CAN_MEASURE_CARRIER;332333if (dev->max_timeout)334features |= LIRC_CAN_SET_REC_TIMEOUT;335336snprintf(drv->name, sizeof(drv->name), "ir-lirc-codec (%s)",337dev->driver_name);338drv->minor = -1;339drv->features = features;340drv->data = &dev->raw->lirc;341drv->rbuf = rbuf;342drv->set_use_inc = &ir_lirc_open;343drv->set_use_dec = &ir_lirc_close;344drv->code_length = sizeof(struct ir_raw_event) * 8;345drv->fops = &lirc_fops;346drv->dev = &dev->dev;347drv->owner = THIS_MODULE;348349drv->minor = lirc_register_driver(drv);350if (drv->minor < 0) {351rc = -ENODEV;352goto lirc_register_failed;353}354355dev->raw->lirc.drv = drv;356dev->raw->lirc.dev = dev;357return 0;358359lirc_register_failed:360rbuf_init_failed:361kfree(rbuf);362rbuf_alloc_failed:363kfree(drv);364365return rc;366}367368static int ir_lirc_unregister(struct rc_dev *dev)369{370struct lirc_codec *lirc = &dev->raw->lirc;371372lirc_unregister_driver(lirc->drv->minor);373lirc_buffer_free(lirc->drv->rbuf);374kfree(lirc->drv);375376return 0;377}378379static struct ir_raw_handler lirc_handler = {380.protocols = RC_TYPE_LIRC,381.decode = ir_lirc_decode,382.raw_register = ir_lirc_register,383.raw_unregister = ir_lirc_unregister,384};385386static int __init ir_lirc_codec_init(void)387{388ir_raw_handler_register(&lirc_handler);389390printk(KERN_INFO "IR LIRC bridge handler initialized\n");391return 0;392}393394static void __exit ir_lirc_codec_exit(void)395{396ir_raw_handler_unregister(&lirc_handler);397}398399module_init(ir_lirc_codec_init);400module_exit(ir_lirc_codec_exit);401402MODULE_LICENSE("GPL");403MODULE_AUTHOR("Jarod Wilson <[email protected]>");404MODULE_AUTHOR("Red Hat Inc. (http://www.redhat.com)");405MODULE_DESCRIPTION("LIRC IR handler bridge");406407408