2011-01-18 08:33:08 +03:00
/* ir-lirc-codec.c - rc-core to classic lirc interface bridge
2010-07-03 08:07:53 +04:00
*
* Copyright ( C ) 2010 by Jarod Wilson < jarod @ redhat . com >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation version 2 of the License .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*/
# include <linux/sched.h>
# include <linux/wait.h>
2011-07-03 22:03:12 +04:00
# include <linux/module.h>
2010-07-03 08:07:53 +04:00
# include <media/lirc.h>
2010-07-16 21:25:33 +04:00
# include <media/lirc_dev.h>
2010-11-17 19:28:38 +03:00
# include <media/rc-core.h>
2010-11-10 05:09:57 +03:00
# include "rc-core-priv.h"
2010-07-03 08:07:53 +04:00
# define LIRCBUF_SIZE 256
/**
* ir_lirc_decode ( ) - Send raw IR data to lirc_dev to be relayed to the
* lircd userspace daemon for decoding .
2010-10-29 23:08:23 +04:00
* @ input_dev : the struct rc_dev descriptor of the device
2010-07-03 08:07:53 +04:00
* @ duration : the struct ir_raw_event descriptor of the pulse / space
*
* This function returns - EINVAL if the lirc interfaces aren ' t wired up .
*/
2010-10-29 23:08:23 +04:00
static int ir_lirc_decode ( struct rc_dev * dev , struct ir_raw_event ev )
2010-07-03 08:07:53 +04:00
{
2010-10-29 23:08:23 +04:00
struct lirc_codec * lirc = & dev - > raw - > lirc ;
2010-07-31 18:59:15 +04:00
int sample ;
2010-07-03 08:07:53 +04:00
2010-11-17 20:20:52 +03:00
if ( ! ( dev - > raw - > enabled_protocols & RC_TYPE_LIRC ) )
2010-07-03 08:07:53 +04:00
return 0 ;
2010-10-29 23:08:23 +04:00
if ( ! dev - > raw - > lirc . drv | | ! dev - > raw - > lirc . drv - > rbuf )
2010-07-03 08:07:53 +04:00
return - EINVAL ;
2010-10-17 02:56:28 +04:00
/* Packet start */
if ( ev . reset )
2010-07-31 18:59:15 +04:00
return 0 ;
2010-10-17 02:56:28 +04:00
/* Carrier reports */
if ( ev . carrier_report ) {
sample = LIRC_FREQUENCY ( ev . carrier ) ;
2011-01-18 08:33:08 +03:00
IR_dprintk ( 2 , " carrier report (freq: %d) \n " , sample ) ;
2010-07-03 08:07:53 +04:00
2010-10-17 02:56:28 +04:00
/* Packet end */
} else if ( ev . timeout ) {
if ( lirc - > gap )
return 0 ;
lirc - > gap_start = ktime_get ( ) ;
lirc - > gap = true ;
lirc - > gap_duration = ev . duration ;
if ( ! lirc - > send_timeout_reports )
return 0 ;
sample = LIRC_TIMEOUT ( ev . duration / 1000 ) ;
2011-01-18 08:33:08 +03:00
IR_dprintk ( 2 , " timeout report (duration: %d) \n " , sample ) ;
2010-10-17 02:56:28 +04:00
/* Normal sample */
} else {
if ( lirc - > gap ) {
int gap_sample ;
lirc - > gap_duration + = ktime_to_ns ( ktime_sub ( ktime_get ( ) ,
lirc - > gap_start ) ) ;
/* Convert to ms and cap by LIRC_VALUE_MASK */
do_div ( lirc - > gap_duration , 1000 ) ;
lirc - > gap_duration = min ( lirc - > gap_duration ,
( u64 ) LIRC_VALUE_MASK ) ;
gap_sample = LIRC_SPACE ( lirc - > gap_duration ) ;
2010-10-29 23:08:23 +04:00
lirc_buffer_write ( dev - > raw - > lirc . drv - > rbuf ,
2010-10-17 02:56:28 +04:00
( unsigned char * ) & gap_sample ) ;
lirc - > gap = false ;
}
sample = ev . pulse ? LIRC_PULSE ( ev . duration / 1000 ) :
LIRC_SPACE ( ev . duration / 1000 ) ;
2011-01-18 08:33:08 +03:00
IR_dprintk ( 2 , " delivering %uus %s to lirc_dev \n " ,
TO_US ( ev . duration ) , TO_STR ( ev . pulse ) ) ;
2010-10-17 02:56:28 +04:00
}
2010-07-03 08:07:53 +04:00
2010-10-29 23:08:23 +04:00
lirc_buffer_write ( dev - > raw - > lirc . drv - > rbuf ,
2010-07-31 18:59:15 +04:00
( unsigned char * ) & sample ) ;
2010-10-29 23:08:23 +04:00
wake_up ( & dev - > raw - > lirc . drv - > rbuf - > wait_poll ) ;
2010-07-03 08:07:53 +04:00
return 0 ;
}
2011-10-06 09:41:06 +04:00
static ssize_t ir_lirc_transmit_ir ( struct file * file , const char __user * buf ,
2010-07-03 08:07:53 +04:00
size_t n , loff_t * ppos )
{
struct lirc_codec * lirc ;
2010-10-29 23:08:23 +04:00
struct rc_dev * dev ;
2011-04-28 19:13:58 +04:00
unsigned int * txbuf ; /* buffer with values to transmit */
2012-08-13 15:59:49 +04:00
ssize_t ret = - EINVAL ;
2010-11-26 20:06:35 +03:00
size_t count ;
2011-04-28 18:14:03 +04:00
ktime_t start ;
s64 towait ;
unsigned int duration = 0 ; /* signal duration in us */
int i ;
start = ktime_get ( ) ;
2010-07-03 08:07:53 +04:00
lirc = lirc_get_pdata ( file ) ;
if ( ! lirc )
return - EFAULT ;
2011-04-28 19:13:58 +04:00
if ( n < sizeof ( unsigned ) | | n % sizeof ( unsigned ) )
2010-07-03 08:07:53 +04:00
return - EINVAL ;
2011-04-28 19:13:58 +04:00
count = n / sizeof ( unsigned ) ;
if ( count > LIRCBUF_SIZE | | count % 2 = = 0 )
2010-07-03 08:07:53 +04:00
return - EINVAL ;
2010-07-17 01:29:54 +04:00
txbuf = memdup_user ( buf , n ) ;
if ( IS_ERR ( txbuf ) )
return PTR_ERR ( txbuf ) ;
2010-07-03 08:07:53 +04:00
2010-10-29 23:08:23 +04:00
dev = lirc - > dev ;
if ( ! dev ) {
2010-07-03 08:07:53 +04:00
ret = - EFAULT ;
goto out ;
}
2011-04-28 18:14:03 +04:00
if ( ! dev - > tx_ir ) {
ret = - ENOSYS ;
goto out ;
}
ret = dev - > tx_ir ( dev , txbuf , ( u32 ) n ) ;
if ( ret < 0 )
goto out ;
for ( i = 0 ; i < ret ; i + + )
duration + = txbuf [ i ] ;
2011-04-28 19:13:58 +04:00
2011-04-28 18:14:03 +04:00
ret * = sizeof ( unsigned int ) ;
/*
* The lircd gap calculation expects the write function to
* wait for the actual IR signal to be transmitted before
* returning .
*/
towait = ktime_us_delta ( ktime_add_us ( start , duration ) , ktime_get ( ) ) ;
if ( towait > 0 ) {
set_current_state ( TASK_INTERRUPTIBLE ) ;
schedule_timeout ( usecs_to_jiffies ( towait ) ) ;
}
2010-07-03 08:07:53 +04:00
out :
kfree ( txbuf ) ;
return ret ;
}
2010-07-31 18:59:23 +04:00
static long ir_lirc_ioctl ( struct file * filep , unsigned int cmd ,
2011-10-06 09:41:06 +04:00
unsigned long arg )
2010-07-03 08:07:53 +04:00
{
struct lirc_codec * lirc ;
2010-10-29 23:08:23 +04:00
struct rc_dev * dev ;
2011-10-06 09:41:06 +04:00
u32 __user * argp = ( u32 __user * ) ( arg ) ;
2010-07-03 08:07:53 +04:00
int ret = 0 ;
2010-10-17 02:56:28 +04:00
__u32 val = 0 , tmp ;
2010-07-03 08:07:53 +04:00
lirc = lirc_get_pdata ( filep ) ;
if ( ! lirc )
return - EFAULT ;
2010-10-29 23:08:23 +04:00
dev = lirc - > dev ;
if ( ! dev )
2010-07-03 08:07:53 +04:00
return - EFAULT ;
2010-07-31 18:59:23 +04:00
if ( _IOC_DIR ( cmd ) & _IOC_WRITE ) {
2011-10-06 09:41:06 +04:00
ret = get_user ( val , argp ) ;
2010-07-03 08:07:53 +04:00
if ( ret )
return ret ;
2010-07-31 18:59:23 +04:00
}
switch ( cmd ) {
/* legacy support */
case LIRC_GET_SEND_MODE :
val = LIRC_CAN_SEND_PULSE & LIRC_CAN_SEND_MASK ;
break ;
case LIRC_SET_SEND_MODE :
if ( val ! = ( LIRC_MODE_PULSE & LIRC_CAN_SEND_MASK ) )
return - EINVAL ;
2010-10-17 02:56:28 +04:00
return 0 ;
2010-07-03 08:07:53 +04:00
2010-07-31 18:59:23 +04:00
/* TX settings */
case LIRC_SET_TRANSMITTER_MASK :
2010-10-29 23:08:23 +04:00
if ( ! dev - > s_tx_mask )
2010-07-03 08:07:53 +04:00
return - EINVAL ;
2010-10-17 02:56:28 +04:00
2010-10-29 23:08:23 +04:00
return dev - > s_tx_mask ( dev , val ) ;
2010-07-03 08:07:53 +04:00
case LIRC_SET_SEND_CARRIER :
2010-10-29 23:08:23 +04:00
if ( ! dev - > s_tx_carrier )
2010-07-03 08:07:53 +04:00
return - EINVAL ;
2010-10-17 02:56:28 +04:00
2010-10-29 23:08:23 +04:00
return dev - > s_tx_carrier ( dev , val ) ;
2010-07-03 08:07:53 +04:00
2010-07-31 18:59:23 +04:00
case LIRC_SET_SEND_DUTY_CYCLE :
2010-10-29 23:08:23 +04:00
if ( ! dev - > s_tx_duty_cycle )
2010-07-31 18:59:23 +04:00
return - ENOSYS ;
if ( val < = 0 | | val > = 100 )
return - EINVAL ;
2010-10-29 23:08:23 +04:00
return dev - > s_tx_duty_cycle ( dev , val ) ;
2010-07-03 08:07:53 +04:00
2010-07-31 18:59:23 +04:00
/* RX settings */
case LIRC_SET_REC_CARRIER :
2010-10-29 23:08:23 +04:00
if ( ! dev - > s_rx_carrier_range )
2010-07-31 18:59:23 +04:00
return - ENOSYS ;
2010-07-03 08:07:53 +04:00
2010-10-17 02:56:28 +04:00
if ( val < = 0 )
return - EINVAL ;
2010-10-29 23:08:23 +04:00
return dev - > s_rx_carrier_range ( dev ,
dev - > raw - > lirc . carrier_low ,
val ) ;
2010-07-31 18:59:23 +04:00
case LIRC_SET_REC_CARRIER_RANGE :
2010-10-17 02:56:28 +04:00
if ( val < = 0 )
return - EINVAL ;
2010-07-31 18:59:23 +04:00
2010-10-29 23:08:23 +04:00
dev - > raw - > lirc . carrier_low = val ;
2010-10-17 02:56:28 +04:00
return 0 ;
2010-07-31 18:59:23 +04:00
case LIRC_GET_REC_RESOLUTION :
2010-10-29 23:08:23 +04:00
val = dev - > rx_resolution ;
2010-07-31 18:59:23 +04:00
break ;
case LIRC_SET_WIDEBAND_RECEIVER :
2010-10-29 23:08:23 +04:00
if ( ! dev - > s_learning_mode )
2010-07-31 18:59:23 +04:00
return - ENOSYS ;
2010-10-29 23:08:23 +04:00
return dev - > s_learning_mode ( dev , ! ! val ) ;
2010-10-17 02:56:28 +04:00
case LIRC_SET_MEASURE_CARRIER_MODE :
2010-10-29 23:08:23 +04:00
if ( ! dev - > s_carrier_report )
2010-10-17 02:56:28 +04:00
return - ENOSYS ;
2010-10-29 23:08:23 +04:00
return dev - > s_carrier_report ( dev , ! ! val ) ;
2010-10-17 02:56:28 +04:00
2010-07-31 18:59:23 +04:00
/* Generic timeout support */
case LIRC_GET_MIN_TIMEOUT :
2010-10-29 23:08:23 +04:00
if ( ! dev - > max_timeout )
2010-07-31 18:59:23 +04:00
return - ENOSYS ;
2010-10-29 23:08:23 +04:00
val = dev - > min_timeout / 1000 ;
2010-07-31 18:59:23 +04:00
break ;
case LIRC_GET_MAX_TIMEOUT :
2010-10-29 23:08:23 +04:00
if ( ! dev - > max_timeout )
2010-07-31 18:59:23 +04:00
return - ENOSYS ;
2010-10-29 23:08:23 +04:00
val = dev - > max_timeout / 1000 ;
2010-07-31 18:59:23 +04:00
break ;
case LIRC_SET_REC_TIMEOUT :
2010-10-29 23:08:23 +04:00
if ( ! dev - > max_timeout )
2010-10-17 02:56:28 +04:00
return - ENOSYS ;
tmp = val * 1000 ;
2010-10-29 23:08:23 +04:00
if ( tmp < dev - > min_timeout | |
tmp > dev - > max_timeout )
2010-10-17 02:56:28 +04:00
return - EINVAL ;
2010-10-29 23:08:23 +04:00
dev - > timeout = tmp ;
2010-10-17 02:56:28 +04:00
break ;
case LIRC_SET_REC_TIMEOUT_REPORTS :
lirc - > send_timeout_reports = ! ! val ;
2010-07-03 08:07:53 +04:00
break ;
default :
2010-08-02 22:43:35 +04:00
return lirc_dev_fop_ioctl ( filep , cmd , arg ) ;
2010-07-03 08:07:53 +04:00
}
2010-07-31 18:59:23 +04:00
if ( _IOC_DIR ( cmd ) & _IOC_READ )
2011-10-06 09:41:06 +04:00
ret = put_user ( val , argp ) ;
2010-07-31 18:59:23 +04:00
2010-07-03 08:07:53 +04:00
return ret ;
}
static int ir_lirc_open ( void * data )
{
return 0 ;
}
static void ir_lirc_close ( void * data )
{
return ;
}
static struct file_operations lirc_fops = {
. owner = THIS_MODULE ,
. write = ir_lirc_transmit_ir ,
2010-08-02 22:43:35 +04:00
. unlocked_ioctl = ir_lirc_ioctl ,
2010-10-09 22:07:06 +04:00
# ifdef CONFIG_COMPAT
. compat_ioctl = ir_lirc_ioctl ,
# endif
2010-07-03 08:07:53 +04:00
. read = lirc_dev_fop_read ,
. poll = lirc_dev_fop_poll ,
. open = lirc_dev_fop_open ,
. release = lirc_dev_fop_close ,
2010-08-15 20:51:56 +04:00
. llseek = no_llseek ,
2010-07-03 08:07:53 +04:00
} ;
2010-10-29 23:08:23 +04:00
static int ir_lirc_register ( struct rc_dev * dev )
2010-07-03 08:07:53 +04:00
{
struct lirc_driver * drv ;
struct lirc_buffer * rbuf ;
int rc = - ENOMEM ;
unsigned long features ;
drv = kzalloc ( sizeof ( struct lirc_driver ) , GFP_KERNEL ) ;
if ( ! drv )
return rc ;
rbuf = kzalloc ( sizeof ( struct lirc_buffer ) , GFP_KERNEL ) ;
2010-07-23 14:08:26 +04:00
if ( ! rbuf )
2010-07-03 08:07:53 +04:00
goto rbuf_alloc_failed ;
rc = lirc_buffer_init ( rbuf , sizeof ( int ) , LIRCBUF_SIZE ) ;
if ( rc )
goto rbuf_init_failed ;
features = LIRC_CAN_REC_MODE2 ;
2010-10-29 23:08:23 +04:00
if ( dev - > tx_ir ) {
2010-07-03 08:07:53 +04:00
features | = LIRC_CAN_SEND_PULSE ;
2010-10-29 23:08:23 +04:00
if ( dev - > s_tx_mask )
2010-07-03 08:07:53 +04:00
features | = LIRC_CAN_SET_TRANSMITTER_MASK ;
2010-10-29 23:08:23 +04:00
if ( dev - > s_tx_carrier )
2010-07-03 08:07:53 +04:00
features | = LIRC_CAN_SET_SEND_CARRIER ;
2010-10-29 23:08:23 +04:00
if ( dev - > s_tx_duty_cycle )
2010-09-07 01:26:08 +04:00
features | = LIRC_CAN_SET_SEND_DUTY_CYCLE ;
2010-07-03 08:07:53 +04:00
}
2010-10-29 23:08:23 +04:00
if ( dev - > s_rx_carrier_range )
2010-07-31 18:59:23 +04:00
features | = LIRC_CAN_SET_REC_CARRIER |
LIRC_CAN_SET_REC_CARRIER_RANGE ;
2010-10-29 23:08:23 +04:00
if ( dev - > s_learning_mode )
2010-07-31 18:59:23 +04:00
features | = LIRC_CAN_USE_WIDEBAND_RECEIVER ;
2010-10-29 23:08:23 +04:00
if ( dev - > s_carrier_report )
2010-10-17 02:56:28 +04:00
features | = LIRC_CAN_MEASURE_CARRIER ;
2010-10-29 23:08:23 +04:00
if ( dev - > max_timeout )
2010-07-31 18:59:23 +04:00
features | = LIRC_CAN_SET_REC_TIMEOUT ;
2010-07-03 08:07:53 +04:00
snprintf ( drv - > name , sizeof ( drv - > name ) , " ir-lirc-codec (%s) " ,
2010-10-29 23:08:23 +04:00
dev - > driver_name ) ;
2010-07-03 08:07:53 +04:00
drv - > minor = - 1 ;
drv - > features = features ;
2010-10-29 23:08:23 +04:00
drv - > data = & dev - > raw - > lirc ;
2010-07-03 08:07:53 +04:00
drv - > rbuf = rbuf ;
drv - > set_use_inc = & ir_lirc_open ;
drv - > set_use_dec = & ir_lirc_close ;
drv - > code_length = sizeof ( struct ir_raw_event ) * 8 ;
drv - > fops = & lirc_fops ;
2010-10-29 23:08:23 +04:00
drv - > dev = & dev - > dev ;
2010-07-03 08:07:53 +04:00
drv - > owner = THIS_MODULE ;
drv - > minor = lirc_register_driver ( drv ) ;
if ( drv - > minor < 0 ) {
rc = - ENODEV ;
goto lirc_register_failed ;
}
2010-10-29 23:08:23 +04:00
dev - > raw - > lirc . drv = drv ;
dev - > raw - > lirc . dev = dev ;
2010-07-03 08:07:53 +04:00
return 0 ;
lirc_register_failed :
rbuf_init_failed :
kfree ( rbuf ) ;
rbuf_alloc_failed :
kfree ( drv ) ;
return rc ;
}
2010-10-29 23:08:23 +04:00
static int ir_lirc_unregister ( struct rc_dev * dev )
2010-07-03 08:07:53 +04:00
{
2010-10-29 23:08:23 +04:00
struct lirc_codec * lirc = & dev - > raw - > lirc ;
2010-07-03 08:07:53 +04:00
lirc_unregister_driver ( lirc - > drv - > minor ) ;
lirc_buffer_free ( lirc - > drv - > rbuf ) ;
kfree ( lirc - > drv ) ;
return 0 ;
}
static struct ir_raw_handler lirc_handler = {
2010-11-17 20:20:52 +03:00
. protocols = RC_TYPE_LIRC ,
2010-07-03 08:07:53 +04:00
. decode = ir_lirc_decode ,
. raw_register = ir_lirc_register ,
. raw_unregister = ir_lirc_unregister ,
} ;
static int __init ir_lirc_codec_init ( void )
{
ir_raw_handler_register ( & lirc_handler ) ;
printk ( KERN_INFO " IR LIRC bridge handler initialized \n " ) ;
return 0 ;
}
static void __exit ir_lirc_codec_exit ( void )
{
ir_raw_handler_unregister ( & lirc_handler ) ;
}
module_init ( ir_lirc_codec_init ) ;
module_exit ( ir_lirc_codec_exit ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_AUTHOR ( " Jarod Wilson <jarod@redhat.com> " ) ;
MODULE_AUTHOR ( " Red Hat Inc. (http://www.redhat.com) " ) ;
MODULE_DESCRIPTION ( " LIRC IR handler bridge " ) ;