2010-07-03 08:06:57 +04:00
/*
* LIRC base driver
*
* by Artur Lipowski < alipowski @ interia . pl >
*
* 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 ; either version 2 of the License , or
* ( at your option ) any later version .
*
* 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 .
*
*/
2016-07-06 12:01:16 +03:00
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
2010-07-03 08:06:57 +04:00
# include <linux/module.h>
# include <linux/mutex.h>
# include <linux/device.h>
2017-06-25 15:32:05 +03:00
# include <linux/idr.h>
2017-09-26 16:34:47 +03:00
# include <linux/poll.h>
2017-11-02 23:39:16 +03:00
# include <linux/sched.h>
# include <linux/wait.h>
2010-07-03 08:06:57 +04:00
2017-09-23 17:41:13 +03:00
# include "rc-core-priv.h"
2017-11-02 23:44:21 +03:00
# include <uapi/linux/lirc.h>
2010-07-03 08:06:57 +04:00
2017-11-02 23:39:16 +03:00
# define LIRCBUF_SIZE 256
2010-07-03 08:06:57 +04:00
static dev_t lirc_base_dev ;
2017-06-25 15:32:05 +03:00
/* Used to keep track of allocated lirc devices */
static DEFINE_IDA ( lirc_ida ) ;
2010-07-03 08:06:57 +04:00
/* Only used for sysfs but defined to void otherwise */
static struct class * lirc_class ;
2017-11-02 23:39:16 +03:00
/**
* ir_lirc_raw_event ( ) - Send raw IR data to lirc to be relayed to userspace
*
* @ dev : the struct rc_dev descriptor of the device
* @ ev : the struct ir_raw_event descriptor of the pulse / space
*/
void ir_lirc_raw_event ( struct rc_dev * dev , struct ir_raw_event ev )
{
2017-11-03 00:21:13 +03:00
unsigned long flags ;
struct lirc_fh * fh ;
2017-11-02 23:39:16 +03:00
int sample ;
/* Packet start */
if ( ev . reset ) {
/*
* Userspace expects a long space event before the start of
* the signal to use as a sync . This may be done with repeat
* packets and normal samples . But if a reset has been sent
* then we assume that a long time has passed , so we send a
* space with the maximum time value .
*/
sample = LIRC_SPACE ( LIRC_VALUE_MASK ) ;
IR_dprintk ( 2 , " delivering reset sync space to lirc_dev \n " ) ;
/* Carrier reports */
} else if ( ev . carrier_report ) {
sample = LIRC_FREQUENCY ( ev . carrier ) ;
IR_dprintk ( 2 , " carrier report (freq: %d) \n " , sample ) ;
/* Packet end */
} else if ( ev . timeout ) {
if ( dev - > gap )
return ;
dev - > gap_start = ktime_get ( ) ;
dev - > gap = true ;
dev - > gap_duration = ev . duration ;
sample = LIRC_TIMEOUT ( ev . duration / 1000 ) ;
IR_dprintk ( 2 , " timeout report (duration: %d) \n " , sample ) ;
/* Normal sample */
} else {
if ( dev - > gap ) {
dev - > gap_duration + = ktime_to_ns ( ktime_sub ( ktime_get ( ) ,
dev - > gap_start ) ) ;
/* Convert to ms and cap by LIRC_VALUE_MASK */
do_div ( dev - > gap_duration , 1000 ) ;
dev - > gap_duration = min_t ( u64 , dev - > gap_duration ,
LIRC_VALUE_MASK ) ;
2017-11-03 00:21:13 +03:00
spin_lock_irqsave ( & dev - > lirc_fh_lock , flags ) ;
list_for_each_entry ( fh , & dev - > lirc_fh , list )
kfifo_put ( & fh - > rawir ,
LIRC_SPACE ( dev - > gap_duration ) ) ;
spin_unlock_irqrestore ( & dev - > lirc_fh_lock , flags ) ;
2017-11-02 23:39:16 +03:00
dev - > gap = false ;
}
sample = ev . pulse ? LIRC_PULSE ( ev . duration / 1000 ) :
LIRC_SPACE ( ev . duration / 1000 ) ;
IR_dprintk ( 2 , " delivering %uus %s to lirc_dev \n " ,
TO_US ( ev . duration ) , TO_STR ( ev . pulse ) ) ;
}
2017-11-03 00:21:13 +03:00
spin_lock_irqsave ( & dev - > lirc_fh_lock , flags ) ;
list_for_each_entry ( fh , & dev - > lirc_fh , list ) {
if ( LIRC_IS_TIMEOUT ( sample ) & & ! fh - > send_timeout_reports )
continue ;
if ( kfifo_put ( & fh - > rawir , sample ) )
wake_up_poll ( & fh - > wait_poll , POLLIN | POLLRDNORM ) ;
}
spin_unlock_irqrestore ( & dev - > lirc_fh_lock , flags ) ;
2017-11-02 23:39:16 +03:00
}
/**
* ir_lirc_scancode_event ( ) - Send scancode data to lirc to be relayed to
2017-11-03 00:21:13 +03:00
* userspace . This can be called in atomic context .
2017-11-02 23:39:16 +03:00
* @ dev : the struct rc_dev descriptor of the device
* @ lsc : the struct lirc_scancode describing the decoded scancode
*/
void ir_lirc_scancode_event ( struct rc_dev * dev , struct lirc_scancode * lsc )
{
2017-11-03 00:21:13 +03:00
unsigned long flags ;
struct lirc_fh * fh ;
2017-11-02 23:39:16 +03:00
lsc - > timestamp = ktime_get_ns ( ) ;
2017-11-03 00:21:13 +03:00
spin_lock_irqsave ( & dev - > lirc_fh_lock , flags ) ;
list_for_each_entry ( fh , & dev - > lirc_fh , list ) {
if ( kfifo_put ( & fh - > scancodes , * lsc ) )
wake_up_poll ( & fh - > wait_poll , POLLIN | POLLRDNORM ) ;
}
spin_unlock_irqrestore ( & dev - > lirc_fh_lock , flags ) ;
2017-11-02 23:39:16 +03:00
}
EXPORT_SYMBOL_GPL ( ir_lirc_scancode_event ) ;
static int ir_lirc_open ( struct inode * inode , struct file * file )
{
struct rc_dev * dev = container_of ( inode - > i_cdev , struct rc_dev ,
lirc_cdev ) ;
2017-11-03 00:21:13 +03:00
struct lirc_fh * fh = kzalloc ( sizeof ( * fh ) , GFP_KERNEL ) ;
unsigned long flags ;
2017-11-02 23:39:16 +03:00
int retval ;
2017-11-03 00:21:13 +03:00
if ( ! fh )
return - ENOMEM ;
2017-11-02 23:39:16 +03:00
2017-11-03 00:21:13 +03:00
get_device ( & dev - > dev ) ;
2017-11-02 23:39:16 +03:00
if ( ! dev - > registered ) {
retval = - ENODEV ;
2017-11-03 00:21:13 +03:00
goto out_fh ;
2017-11-02 23:39:16 +03:00
}
2017-11-03 00:21:13 +03:00
if ( dev - > driver_type = = RC_DRIVER_IR_RAW ) {
if ( kfifo_alloc ( & fh - > rawir , MAX_IR_EVENT_SIZE , GFP_KERNEL ) ) {
retval = - ENOMEM ;
goto out_fh ;
}
2017-11-02 23:39:16 +03:00
}
2017-11-03 00:21:13 +03:00
if ( dev - > driver_type ! = RC_DRIVER_IR_RAW_TX ) {
if ( kfifo_alloc ( & fh - > scancodes , 32 , GFP_KERNEL ) ) {
retval = - ENOMEM ;
goto out_rawir ;
}
}
fh - > send_mode = LIRC_MODE_PULSE ;
fh - > rc = dev ;
fh - > send_timeout_reports = true ;
if ( dev - > driver_type = = RC_DRIVER_SCANCODE )
fh - > rec_mode = LIRC_MODE_SCANCODE ;
else
fh - > rec_mode = LIRC_MODE_MODE2 ;
retval = rc_open ( dev ) ;
if ( retval )
goto out_kfifo ;
init_waitqueue_head ( & fh - > wait_poll ) ;
2017-11-02 23:39:16 +03:00
2017-11-03 00:21:13 +03:00
file - > private_data = fh ;
spin_lock_irqsave ( & dev - > lirc_fh_lock , flags ) ;
list_add ( & fh - > list , & dev - > lirc_fh ) ;
spin_unlock_irqrestore ( & dev - > lirc_fh_lock , flags ) ;
2017-11-02 23:39:16 +03:00
nonseekable_open ( inode , file ) ;
return 0 ;
2017-11-03 00:21:13 +03:00
out_kfifo :
if ( dev - > driver_type ! = RC_DRIVER_IR_RAW_TX )
kfifo_free ( & fh - > scancodes ) ;
out_rawir :
if ( dev - > driver_type = = RC_DRIVER_IR_RAW )
kfifo_free ( & fh - > rawir ) ;
out_fh :
kfree ( fh ) ;
put_device ( & dev - > dev ) ;
2017-11-02 23:39:16 +03:00
return retval ;
}
static int ir_lirc_close ( struct inode * inode , struct file * file )
{
2017-11-03 00:21:13 +03:00
struct lirc_fh * fh = file - > private_data ;
struct rc_dev * dev = fh - > rc ;
unsigned long flags ;
2017-11-02 23:39:16 +03:00
2017-11-03 00:21:13 +03:00
spin_lock_irqsave ( & dev - > lirc_fh_lock , flags ) ;
list_del ( & fh - > list ) ;
spin_unlock_irqrestore ( & dev - > lirc_fh_lock , flags ) ;
if ( dev - > driver_type = = RC_DRIVER_IR_RAW )
kfifo_free ( & fh - > rawir ) ;
if ( dev - > driver_type ! = RC_DRIVER_IR_RAW_TX )
kfifo_free ( & fh - > scancodes ) ;
kfree ( fh ) ;
2017-11-02 23:39:16 +03:00
rc_close ( dev ) ;
2017-11-03 00:21:13 +03:00
put_device ( & dev - > dev ) ;
2017-11-02 23:39:16 +03:00
return 0 ;
}
static ssize_t ir_lirc_transmit_ir ( struct file * file , const char __user * buf ,
size_t n , loff_t * ppos )
{
2017-11-03 00:21:13 +03:00
struct lirc_fh * fh = file - > private_data ;
struct rc_dev * dev = fh - > rc ;
2017-11-02 23:39:16 +03:00
unsigned int * txbuf = NULL ;
struct ir_raw_event * raw = NULL ;
2017-11-04 15:30:45 +03:00
ssize_t ret ;
2017-11-02 23:39:16 +03:00
size_t count ;
ktime_t start ;
s64 towait ;
unsigned int duration = 0 ; /* signal duration in us */
int i ;
2017-11-04 15:30:45 +03:00
ret = mutex_lock_interruptible ( & dev - > lock ) ;
if ( ret )
return ret ;
if ( ! dev - > registered ) {
ret = - ENODEV ;
goto out ;
}
2017-11-02 23:39:16 +03:00
start = ktime_get ( ) ;
if ( ! dev - > tx_ir ) {
ret = - EINVAL ;
goto out ;
}
2017-11-03 00:21:13 +03:00
if ( fh - > send_mode = = LIRC_MODE_SCANCODE ) {
2017-11-02 23:39:16 +03:00
struct lirc_scancode scan ;
2017-11-04 15:30:45 +03:00
if ( n ! = sizeof ( scan ) ) {
ret = - EINVAL ;
goto out ;
}
2017-11-02 23:39:16 +03:00
2017-11-04 15:30:45 +03:00
if ( copy_from_user ( & scan , buf , sizeof ( scan ) ) ) {
ret = - EFAULT ;
goto out ;
}
2017-11-02 23:39:16 +03:00
2017-11-04 15:30:45 +03:00
if ( scan . flags | | scan . keycode | | scan . timestamp ) {
ret = - EINVAL ;
goto out ;
}
2017-11-02 23:39:16 +03:00
/*
* The scancode field in lirc_scancode is 64 - bit simply
* to future - proof it , since there are IR protocols encode
* use more than 32 bits . For now only 32 - bit protocols
* are supported .
*/
if ( scan . scancode > U32_MAX | |
2017-11-04 15:30:45 +03:00
! rc_validate_scancode ( scan . rc_proto , scan . scancode ) ) {
ret = - EINVAL ;
goto out ;
}
2017-11-02 23:39:16 +03:00
raw = kmalloc_array ( LIRCBUF_SIZE , sizeof ( * raw ) , GFP_KERNEL ) ;
2017-11-04 15:30:45 +03:00
if ( ! raw ) {
ret = - ENOMEM ;
goto out ;
}
2017-11-02 23:39:16 +03:00
ret = ir_raw_encode_scancode ( scan . rc_proto , scan . scancode ,
raw , LIRCBUF_SIZE ) ;
if ( ret < 0 )
goto out ;
count = ret ;
txbuf = kmalloc_array ( count , sizeof ( unsigned int ) , GFP_KERNEL ) ;
if ( ! txbuf ) {
ret = - ENOMEM ;
goto out ;
}
for ( i = 0 ; i < count ; i + + )
/* Convert from NS to US */
txbuf [ i ] = DIV_ROUND_UP ( raw [ i ] . duration , 1000 ) ;
if ( dev - > s_tx_carrier ) {
int carrier = ir_raw_encode_carrier ( scan . rc_proto ) ;
if ( carrier > 0 )
dev - > s_tx_carrier ( dev , carrier ) ;
}
} else {
2017-11-04 15:30:45 +03:00
if ( n < sizeof ( unsigned int ) | | n % sizeof ( unsigned int ) ) {
ret = - EINVAL ;
goto out ;
}
2017-11-02 23:39:16 +03:00
count = n / sizeof ( unsigned int ) ;
2017-11-04 15:30:45 +03:00
if ( count > LIRCBUF_SIZE | | count % 2 = = 0 ) {
ret = - EINVAL ;
goto out ;
}
2017-11-02 23:39:16 +03:00
txbuf = memdup_user ( buf , n ) ;
2017-11-04 15:30:45 +03:00
if ( IS_ERR ( txbuf ) ) {
ret = PTR_ERR ( txbuf ) ;
goto out ;
}
2017-11-02 23:39:16 +03:00
}
for ( i = 0 ; i < count ; i + + ) {
if ( txbuf [ i ] > IR_MAX_DURATION / 1000 - duration | | ! txbuf [ i ] ) {
ret = - EINVAL ;
goto out ;
}
duration + = txbuf [ i ] ;
}
ret = dev - > tx_ir ( dev , txbuf , count ) ;
if ( ret < 0 )
goto out ;
2017-11-03 00:21:13 +03:00
if ( fh - > send_mode = = LIRC_MODE_SCANCODE ) {
2017-11-02 23:39:16 +03:00
ret = n ;
} else {
for ( duration = i = 0 ; i < ret ; i + + )
duration + = txbuf [ i ] ;
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 ) ) ;
}
}
out :
2017-11-04 15:30:45 +03:00
mutex_unlock ( & dev - > lock ) ;
2017-11-02 23:39:16 +03:00
kfree ( txbuf ) ;
kfree ( raw ) ;
return ret ;
}
2017-11-03 00:21:13 +03:00
static long ir_lirc_ioctl ( struct file * file , unsigned int cmd ,
2017-11-02 23:39:16 +03:00
unsigned long arg )
{
2017-11-03 00:21:13 +03:00
struct lirc_fh * fh = file - > private_data ;
struct rc_dev * dev = fh - > rc ;
2017-11-02 23:39:16 +03:00
u32 __user * argp = ( u32 __user * ) ( arg ) ;
2017-11-04 15:30:45 +03:00
u32 val = 0 ;
int ret ;
2017-11-02 23:39:16 +03:00
if ( _IOC_DIR ( cmd ) & _IOC_WRITE ) {
ret = get_user ( val , argp ) ;
if ( ret )
return ret ;
}
2017-11-04 15:30:45 +03:00
ret = mutex_lock_interruptible ( & dev - > lock ) ;
if ( ret )
return ret ;
if ( ! dev - > registered ) {
ret = - ENODEV ;
goto out ;
}
2017-11-02 23:39:16 +03:00
switch ( cmd ) {
case LIRC_GET_FEATURES :
if ( dev - > driver_type = = RC_DRIVER_SCANCODE )
val | = LIRC_CAN_REC_SCANCODE ;
if ( dev - > driver_type = = RC_DRIVER_IR_RAW ) {
val | = LIRC_CAN_REC_MODE2 | LIRC_CAN_REC_SCANCODE ;
if ( dev - > rx_resolution )
val | = LIRC_CAN_GET_REC_RESOLUTION ;
}
if ( dev - > tx_ir ) {
val | = LIRC_CAN_SEND_PULSE | LIRC_CAN_SEND_SCANCODE ;
if ( dev - > s_tx_mask )
val | = LIRC_CAN_SET_TRANSMITTER_MASK ;
if ( dev - > s_tx_carrier )
val | = LIRC_CAN_SET_SEND_CARRIER ;
if ( dev - > s_tx_duty_cycle )
val | = LIRC_CAN_SET_SEND_DUTY_CYCLE ;
}
if ( dev - > s_rx_carrier_range )
val | = LIRC_CAN_SET_REC_CARRIER |
LIRC_CAN_SET_REC_CARRIER_RANGE ;
if ( dev - > s_learning_mode )
val | = LIRC_CAN_USE_WIDEBAND_RECEIVER ;
if ( dev - > s_carrier_report )
val | = LIRC_CAN_MEASURE_CARRIER ;
if ( dev - > max_timeout )
val | = LIRC_CAN_SET_REC_TIMEOUT ;
break ;
/* mode support */
case LIRC_GET_REC_MODE :
if ( dev - > driver_type = = RC_DRIVER_IR_RAW_TX )
2017-11-04 15:30:45 +03:00
ret = - ENOTTY ;
else
val = fh - > rec_mode ;
2017-11-02 23:39:16 +03:00
break ;
case LIRC_SET_REC_MODE :
switch ( dev - > driver_type ) {
case RC_DRIVER_IR_RAW_TX :
2017-11-04 15:30:45 +03:00
ret = - ENOTTY ;
break ;
2017-11-02 23:39:16 +03:00
case RC_DRIVER_SCANCODE :
if ( val ! = LIRC_MODE_SCANCODE )
2017-11-04 15:30:45 +03:00
ret = - EINVAL ;
2017-11-02 23:39:16 +03:00
break ;
case RC_DRIVER_IR_RAW :
if ( ! ( val = = LIRC_MODE_MODE2 | |
val = = LIRC_MODE_SCANCODE ) )
2017-11-04 15:30:45 +03:00
ret = - EINVAL ;
2017-11-02 23:39:16 +03:00
break ;
}
2017-11-04 15:30:45 +03:00
if ( ! ret )
fh - > rec_mode = val ;
break ;
2017-11-02 23:39:16 +03:00
case LIRC_GET_SEND_MODE :
if ( ! dev - > tx_ir )
2017-11-04 15:30:45 +03:00
ret = - ENOTTY ;
else
val = fh - > send_mode ;
2017-11-02 23:39:16 +03:00
break ;
case LIRC_SET_SEND_MODE :
if ( ! dev - > tx_ir )
2017-11-04 15:30:45 +03:00
ret = - ENOTTY ;
else if ( ! ( val = = LIRC_MODE_PULSE | | val = = LIRC_MODE_SCANCODE ) )
ret = - EINVAL ;
else
fh - > send_mode = val ;
break ;
2017-11-02 23:39:16 +03:00
/* TX settings */
case LIRC_SET_TRANSMITTER_MASK :
if ( ! dev - > s_tx_mask )
2017-11-04 15:30:45 +03:00
ret = - ENOTTY ;
else
ret = dev - > s_tx_mask ( dev , val ) ;
break ;
2017-11-02 23:39:16 +03:00
case LIRC_SET_SEND_CARRIER :
if ( ! dev - > s_tx_carrier )
2017-11-04 15:30:45 +03:00
ret = - ENOTTY ;
else
ret = dev - > s_tx_carrier ( dev , val ) ;
break ;
2017-11-02 23:39:16 +03:00
case LIRC_SET_SEND_DUTY_CYCLE :
if ( ! dev - > s_tx_duty_cycle )
2017-11-04 15:30:45 +03:00
ret = - ENOTTY ;
else if ( val < = 0 | | val > = 100 )
ret = - EINVAL ;
else
ret = dev - > s_tx_duty_cycle ( dev , val ) ;
break ;
2017-11-02 23:39:16 +03:00
/* RX settings */
case LIRC_SET_REC_CARRIER :
if ( ! dev - > s_rx_carrier_range )
2017-11-04 15:30:45 +03:00
ret = - ENOTTY ;
else if ( val < = 0 )
ret = - EINVAL ;
else
ret = dev - > s_rx_carrier_range ( dev , fh - > carrier_low ,
val ) ;
break ;
2017-11-02 23:39:16 +03:00
case LIRC_SET_REC_CARRIER_RANGE :
if ( ! dev - > s_rx_carrier_range )
2017-11-04 15:30:45 +03:00
ret = - ENOTTY ;
else if ( val < = 0 )
ret = - EINVAL ;
else
fh - > carrier_low = val ;
break ;
2017-11-02 23:39:16 +03:00
case LIRC_GET_REC_RESOLUTION :
if ( ! dev - > rx_resolution )
2017-11-04 15:30:45 +03:00
ret = - ENOTTY ;
else
val = dev - > rx_resolution / 1000 ;
2017-11-02 23:39:16 +03:00
break ;
case LIRC_SET_WIDEBAND_RECEIVER :
if ( ! dev - > s_learning_mode )
2017-11-04 15:30:45 +03:00
ret = - ENOTTY ;
else
ret = dev - > s_learning_mode ( dev , ! ! val ) ;
break ;
2017-11-02 23:39:16 +03:00
case LIRC_SET_MEASURE_CARRIER_MODE :
if ( ! dev - > s_carrier_report )
2017-11-04 15:30:45 +03:00
ret = - ENOTTY ;
else
ret = dev - > s_carrier_report ( dev , ! ! val ) ;
break ;
2017-11-02 23:39:16 +03:00
/* Generic timeout support */
case LIRC_GET_MIN_TIMEOUT :
if ( ! dev - > max_timeout )
2017-11-04 15:30:45 +03:00
ret = - ENOTTY ;
else
val = DIV_ROUND_UP ( dev - > min_timeout , 1000 ) ;
2017-11-02 23:39:16 +03:00
break ;
case LIRC_GET_MAX_TIMEOUT :
if ( ! dev - > max_timeout )
2017-11-04 15:30:45 +03:00
ret = - ENOTTY ;
else
val = dev - > max_timeout / 1000 ;
2017-11-02 23:39:16 +03:00
break ;
case LIRC_SET_REC_TIMEOUT :
2017-11-04 15:30:45 +03:00
if ( ! dev - > max_timeout ) {
ret = - ENOTTY ;
} else if ( val > U32_MAX / 1000 ) {
/* Check for multiply overflow */
ret = - EINVAL ;
} else {
u32 tmp = val * 1000 ;
if ( tmp < dev - > min_timeout | | tmp > dev - > max_timeout )
ret = - EINVAL ;
else if ( dev - > s_timeout )
ret = dev - > s_timeout ( dev , tmp ) ;
else if ( ! ret )
dev - > timeout = tmp ;
}
2017-11-02 23:39:16 +03:00
break ;
case LIRC_SET_REC_TIMEOUT_REPORTS :
if ( ! dev - > timeout )
2017-11-04 15:30:45 +03:00
ret = - ENOTTY ;
else
fh - > send_timeout_reports = ! ! val ;
2017-11-02 23:39:16 +03:00
break ;
default :
2017-11-04 15:30:45 +03:00
ret = - ENOTTY ;
2017-11-02 23:39:16 +03:00
}
2017-11-04 15:30:45 +03:00
if ( ! ret & & _IOC_DIR ( cmd ) & _IOC_READ )
2017-11-02 23:39:16 +03:00
ret = put_user ( val , argp ) ;
2017-11-04 15:30:45 +03:00
out :
mutex_unlock ( & dev - > lock ) ;
2017-11-02 23:39:16 +03:00
return ret ;
}
static unsigned int ir_lirc_poll ( struct file * file ,
struct poll_table_struct * wait )
{
2017-11-03 00:21:13 +03:00
struct lirc_fh * fh = file - > private_data ;
struct rc_dev * rcdev = fh - > rc ;
2017-11-02 23:39:16 +03:00
unsigned int events = 0 ;
2017-11-03 00:21:13 +03:00
poll_wait ( file , & fh - > wait_poll , wait ) ;
2017-11-02 23:39:16 +03:00
if ( ! rcdev - > registered ) {
events = POLLHUP | POLLERR ;
} else if ( rcdev - > driver_type ! = RC_DRIVER_IR_RAW_TX ) {
2017-11-03 00:21:13 +03:00
if ( fh - > rec_mode = = LIRC_MODE_SCANCODE & &
! kfifo_is_empty ( & fh - > scancodes ) )
2017-11-02 23:39:16 +03:00
events = POLLIN | POLLRDNORM ;
2017-11-03 00:21:13 +03:00
if ( fh - > rec_mode = = LIRC_MODE_MODE2 & &
! kfifo_is_empty ( & fh - > rawir ) )
2017-11-02 23:39:16 +03:00
events = POLLIN | POLLRDNORM ;
}
return events ;
}
static ssize_t ir_lirc_read_mode2 ( struct file * file , char __user * buffer ,
size_t length )
{
2017-11-03 00:21:13 +03:00
struct lirc_fh * fh = file - > private_data ;
struct rc_dev * rcdev = fh - > rc ;
2017-11-02 23:39:16 +03:00
unsigned int copied ;
int ret ;
if ( length < sizeof ( unsigned int ) | | length % sizeof ( unsigned int ) )
return - EINVAL ;
do {
2017-11-03 00:21:13 +03:00
if ( kfifo_is_empty ( & fh - > rawir ) ) {
2017-11-02 23:39:16 +03:00
if ( file - > f_flags & O_NONBLOCK )
return - EAGAIN ;
2017-11-03 00:21:13 +03:00
ret = wait_event_interruptible ( fh - > wait_poll ,
! kfifo_is_empty ( & fh - > rawir ) | |
2017-11-02 23:39:16 +03:00
! rcdev - > registered ) ;
if ( ret )
return ret ;
}
if ( ! rcdev - > registered )
return - ENODEV ;
ret = mutex_lock_interruptible ( & rcdev - > lock ) ;
if ( ret )
return ret ;
2017-11-03 00:21:13 +03:00
ret = kfifo_to_user ( & fh - > rawir , buffer , length , & copied ) ;
2017-11-02 23:39:16 +03:00
mutex_unlock ( & rcdev - > lock ) ;
if ( ret )
return ret ;
} while ( copied = = 0 ) ;
return copied ;
}
static ssize_t ir_lirc_read_scancode ( struct file * file , char __user * buffer ,
size_t length )
{
2017-11-03 00:21:13 +03:00
struct lirc_fh * fh = file - > private_data ;
struct rc_dev * rcdev = fh - > rc ;
2017-11-02 23:39:16 +03:00
unsigned int copied ;
int ret ;
if ( length < sizeof ( struct lirc_scancode ) | |
length % sizeof ( struct lirc_scancode ) )
return - EINVAL ;
do {
2017-11-03 00:21:13 +03:00
if ( kfifo_is_empty ( & fh - > scancodes ) ) {
2017-11-02 23:39:16 +03:00
if ( file - > f_flags & O_NONBLOCK )
return - EAGAIN ;
2017-11-03 00:21:13 +03:00
ret = wait_event_interruptible ( fh - > wait_poll ,
! kfifo_is_empty ( & fh - > scancodes ) | |
2017-11-02 23:39:16 +03:00
! rcdev - > registered ) ;
if ( ret )
return ret ;
}
if ( ! rcdev - > registered )
return - ENODEV ;
ret = mutex_lock_interruptible ( & rcdev - > lock ) ;
if ( ret )
return ret ;
2017-11-03 00:21:13 +03:00
ret = kfifo_to_user ( & fh - > scancodes , buffer , length , & copied ) ;
2017-11-02 23:39:16 +03:00
mutex_unlock ( & rcdev - > lock ) ;
if ( ret )
return ret ;
} while ( copied = = 0 ) ;
return copied ;
}
static ssize_t ir_lirc_read ( struct file * file , char __user * buffer ,
size_t length , loff_t * ppos )
{
2017-11-03 00:21:13 +03:00
struct lirc_fh * fh = file - > private_data ;
struct rc_dev * rcdev = fh - > rc ;
2017-11-02 23:39:16 +03:00
if ( rcdev - > driver_type = = RC_DRIVER_IR_RAW_TX )
return - EINVAL ;
if ( ! rcdev - > registered )
return - ENODEV ;
2017-11-03 00:21:13 +03:00
if ( fh - > rec_mode = = LIRC_MODE_MODE2 )
2017-11-02 23:39:16 +03:00
return ir_lirc_read_mode2 ( file , buffer , length ) ;
else /* LIRC_MODE_SCANCODE */
return ir_lirc_read_scancode ( file , buffer , length ) ;
}
static const struct file_operations lirc_fops = {
. owner = THIS_MODULE ,
. write = ir_lirc_transmit_ir ,
. unlocked_ioctl = ir_lirc_ioctl ,
# ifdef CONFIG_COMPAT
. compat_ioctl = ir_lirc_ioctl ,
# endif
. read = ir_lirc_read ,
. poll = ir_lirc_poll ,
. open = ir_lirc_open ,
. release = ir_lirc_close ,
. llseek = no_llseek ,
} ;
2017-06-25 15:32:36 +03:00
static void lirc_release_device ( struct device * ld )
2010-07-03 08:06:57 +04:00
{
2017-09-26 16:34:47 +03:00
struct rc_dev * rcdev = container_of ( ld , struct rc_dev , lirc_dev ) ;
2017-06-30 11:41:57 +03:00
2017-09-26 16:34:47 +03:00
put_device ( & rcdev - > dev ) ;
2017-06-25 15:32:15 +03:00
}
2017-09-26 16:34:47 +03:00
int ir_lirc_register ( struct rc_dev * dev )
2016-07-06 12:01:13 +03:00
{
2017-09-26 16:34:47 +03:00
int err , minor ;
2010-07-03 08:06:57 +04:00
2017-09-26 16:34:47 +03:00
minor = ida_simple_get ( & lirc_ida , 0 , RC_DEV_MAX , GFP_KERNEL ) ;
2017-11-03 00:21:13 +03:00
if ( minor < 0 )
return minor ;
2017-06-30 11:41:57 +03:00
2017-11-03 00:21:13 +03:00
device_initialize ( & dev - > lirc_dev ) ;
dev - > lirc_dev . class = lirc_class ;
2017-09-26 16:34:47 +03:00
dev - > lirc_dev . parent = & dev - > dev ;
2017-11-03 00:21:13 +03:00
dev - > lirc_dev . release = lirc_release_device ;
2017-09-26 16:34:47 +03:00
dev - > lirc_dev . devt = MKDEV ( MAJOR ( lirc_base_dev ) , minor ) ;
dev_set_name ( & dev - > lirc_dev , " lirc%d " , minor ) ;
2017-05-01 19:04:21 +03:00
2017-11-03 00:21:13 +03:00
INIT_LIST_HEAD ( & dev - > lirc_fh ) ;
spin_lock_init ( & dev - > lirc_fh_lock ) ;
2017-09-26 16:34:47 +03:00
cdev_init ( & dev - > lirc_cdev , & lirc_fops ) ;
2010-07-03 08:06:57 +04:00
2017-09-26 16:34:47 +03:00
err = cdev_device_add ( & dev - > lirc_cdev , & dev - > lirc_dev ) ;
if ( err )
goto out_ida ;
2017-01-30 18:49:58 +03:00
2017-09-26 16:34:47 +03:00
get_device ( & dev - > dev ) ;
2010-07-03 08:06:57 +04:00
2017-09-26 16:34:47 +03:00
dev_info ( & dev - > dev , " lirc_dev: driver %s registered at minor = %d " ,
dev - > driver_name , minor ) ;
2017-05-01 19:04:11 +03:00
2017-06-25 15:31:24 +03:00
return 0 ;
2017-09-26 16:34:47 +03:00
out_ida :
ida_simple_remove ( & lirc_ida , minor ) ;
return err ;
2010-07-03 08:06:57 +04:00
}
2017-09-26 16:34:47 +03:00
void ir_lirc_unregister ( struct rc_dev * dev )
2010-07-03 08:06:57 +04:00
{
2017-11-03 00:21:13 +03:00
unsigned long flags ;
struct lirc_fh * fh ;
2017-09-26 16:34:47 +03:00
dev_dbg ( & dev - > dev , " lirc_dev: driver %s unregistered from minor = %d \n " ,
dev - > driver_name , MINOR ( dev - > lirc_dev . devt ) ) ;
2010-07-03 08:06:57 +04:00
2017-11-03 00:21:13 +03:00
spin_lock_irqsave ( & dev - > lirc_fh_lock , flags ) ;
list_for_each_entry ( fh , & dev - > lirc_fh , list )
wake_up_poll ( & fh - > wait_poll , POLLHUP | POLLERR ) ;
spin_unlock_irqrestore ( & dev - > lirc_fh_lock , flags ) ;
2010-07-03 08:06:57 +04:00
2017-09-26 16:34:47 +03:00
cdev_device_del ( & dev - > lirc_cdev , & dev - > lirc_dev ) ;
ida_simple_remove ( & lirc_ida , MINOR ( dev - > lirc_dev . devt ) ) ;
2010-07-03 08:06:57 +04:00
}
2017-09-23 17:41:13 +03:00
int __init lirc_dev_init ( void )
2010-07-03 08:06:57 +04:00
{
int retval ;
lirc_class = class_create ( THIS_MODULE , " lirc " ) ;
if ( IS_ERR ( lirc_class ) ) {
2016-07-06 12:01:16 +03:00
pr_err ( " class_create failed \n " ) ;
2016-07-06 12:01:17 +03:00
return PTR_ERR ( lirc_class ) ;
2010-07-03 08:06:57 +04:00
}
2017-09-26 16:34:47 +03:00
retval = alloc_chrdev_region ( & lirc_base_dev , 0 , RC_DEV_MAX ,
2017-05-01 19:04:47 +03:00
" BaseRemoteCtl " ) ;
2010-07-03 08:06:57 +04:00
if ( retval ) {
class_destroy ( lirc_class ) ;
2016-07-06 12:01:16 +03:00
pr_err ( " alloc_chrdev_region failed \n " ) ;
2016-07-06 12:01:17 +03:00
return retval ;
2010-07-03 08:06:57 +04:00
}
2016-07-06 12:01:16 +03:00
pr_info ( " IR Remote Control driver registered, major %d \n " ,
MAJOR ( lirc_base_dev ) ) ;
2010-07-03 08:06:57 +04:00
2016-07-06 12:01:17 +03:00
return 0 ;
2010-07-03 08:06:57 +04:00
}
2017-09-23 17:41:13 +03:00
void __exit lirc_dev_exit ( void )
2010-07-03 08:06:57 +04:00
{
class_destroy ( lirc_class ) ;
2017-09-26 16:34:47 +03:00
unregister_chrdev_region ( lirc_base_dev , RC_DEV_MAX ) ;
2010-07-03 08:06:57 +04:00
}