2019-05-27 09:55:06 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
2010-11-26 00:36:27 +03:00
/*
* Loopback driver for rc - core ,
*
* Copyright ( c ) 2010 David Härdeman < david @ hardeman . nu >
*
* This driver receives TX data and passes it back as RX data ,
* which is useful for ( scripted ) debugging of rc - core without
* having to use actual hardware .
*/
# include <linux/device.h>
# include <linux/module.h>
# include <linux/sched.h>
2015-03-31 20:48:11 +03:00
# include <linux/slab.h>
2010-11-26 00:36:27 +03:00
# include <media/rc-core.h>
2021-07-03 12:04:40 +03:00
# define DRIVER_NAME "rc-loopback"
# define RXMASK_NARROWBAND 0x1
# define RXMASK_WIDEBAND 0x2
2010-11-26 00:36:27 +03:00
struct loopback_dev {
struct rc_dev * dev ;
u32 txmask ;
u32 txcarrier ;
u32 txduty ;
bool idle ;
2021-07-03 12:04:40 +03:00
bool wideband ;
2010-11-26 00:36:27 +03:00
bool carrierreport ;
u32 rxcarriermin ;
u32 rxcarriermax ;
} ;
static struct loopback_dev loopdev ;
static int loop_set_tx_mask ( struct rc_dev * dev , u32 mask )
{
struct loopback_dev * lodev = dev - > priv ;
2021-07-03 12:04:40 +03:00
if ( ( mask & ( RXMASK_NARROWBAND | RXMASK_WIDEBAND ) ) ! = mask ) {
2021-07-03 16:36:15 +03:00
dev_dbg ( & dev - > dev , " invalid tx mask: %u \n " , mask ) ;
2021-07-03 16:37:17 +03:00
return 2 ;
2010-11-26 00:36:27 +03:00
}
2021-07-03 16:36:15 +03:00
dev_dbg ( & dev - > dev , " setting tx mask: %u \n " , mask ) ;
2010-11-26 00:36:27 +03:00
lodev - > txmask = mask ;
return 0 ;
}
static int loop_set_tx_carrier ( struct rc_dev * dev , u32 carrier )
{
struct loopback_dev * lodev = dev - > priv ;
2021-07-03 16:36:15 +03:00
dev_dbg ( & dev - > dev , " setting tx carrier: %u \n " , carrier ) ;
2010-11-26 00:36:27 +03:00
lodev - > txcarrier = carrier ;
return 0 ;
}
static int loop_set_tx_duty_cycle ( struct rc_dev * dev , u32 duty_cycle )
{
struct loopback_dev * lodev = dev - > priv ;
if ( duty_cycle < 1 | | duty_cycle > 99 ) {
2021-07-03 16:36:15 +03:00
dev_dbg ( & dev - > dev , " invalid duty cycle: %u \n " , duty_cycle ) ;
2010-11-26 00:36:27 +03:00
return - EINVAL ;
}
2021-07-03 16:36:15 +03:00
dev_dbg ( & dev - > dev , " setting duty cycle: %u \n " , duty_cycle ) ;
2010-11-26 00:36:27 +03:00
lodev - > txduty = duty_cycle ;
return 0 ;
}
static int loop_set_rx_carrier_range ( struct rc_dev * dev , u32 min , u32 max )
{
struct loopback_dev * lodev = dev - > priv ;
if ( min < 1 | | min > max ) {
2021-07-03 16:36:15 +03:00
dev_dbg ( & dev - > dev , " invalid rx carrier range %u to %u \n " , min , max ) ;
2010-11-26 00:36:27 +03:00
return - EINVAL ;
}
2021-07-03 16:36:15 +03:00
dev_dbg ( & dev - > dev , " setting rx carrier range %u to %u \n " , min , max ) ;
2010-11-26 00:36:27 +03:00
lodev - > rxcarriermin = min ;
lodev - > rxcarriermax = max ;
return 0 ;
}
2011-04-28 19:13:58 +04:00
static int loop_tx_ir ( struct rc_dev * dev , unsigned * txbuf , unsigned count )
2010-11-26 00:36:27 +03:00
{
struct loopback_dev * lodev = dev - > priv ;
u32 rxmask ;
unsigned i ;
2018-08-21 22:57:52 +03:00
struct ir_raw_event rawir = { } ;
2010-11-26 00:36:27 +03:00
if ( lodev - > txcarrier < lodev - > rxcarriermin | |
lodev - > txcarrier > lodev - > rxcarriermax ) {
2021-07-03 16:36:15 +03:00
dev_dbg ( & dev - > dev , " ignoring tx, carrier out of range \n " ) ;
2010-11-26 00:36:27 +03:00
goto out ;
}
2021-07-03 12:04:40 +03:00
if ( lodev - > wideband )
rxmask = RXMASK_WIDEBAND ;
2010-11-26 00:36:27 +03:00
else
2021-07-03 12:04:40 +03:00
rxmask = RXMASK_NARROWBAND ;
2010-11-26 00:36:27 +03:00
if ( ! ( rxmask & lodev - > txmask ) ) {
2021-07-03 16:36:15 +03:00
dev_dbg ( & dev - > dev , " ignoring tx, rx mask mismatch \n " ) ;
2010-11-26 00:36:27 +03:00
goto out ;
}
for ( i = 0 ; i < count ; i + + ) {
rawir . pulse = i % 2 ? false : true ;
2020-08-23 20:23:05 +03:00
rawir . duration = txbuf [ i ] ;
2021-07-03 16:51:10 +03:00
2022-01-15 13:19:11 +03:00
/* simulate overflow if ridiculously long pulse was sent */
if ( rawir . pulse & & rawir . duration > MS_TO_US ( 50 ) )
ir_raw_event_overflow ( dev ) ;
else
ir_raw_event_store_with_filter ( dev , & rawir ) ;
2021-07-03 16:51:10 +03:00
}
if ( lodev - > carrierreport ) {
rawir . pulse = false ;
rawir . carrier_report = true ;
rawir . carrier = lodev - > txcarrier ;
ir_raw_event_store ( dev , & rawir ) ;
2010-11-26 00:36:27 +03:00
}
2011-04-28 19:13:32 +04:00
/* Fake a silence long enough to cause us to go idle */
rawir . pulse = false ;
rawir . duration = dev - > timeout ;
ir_raw_event_store_with_filter ( dev , & rawir ) ;
2010-11-26 00:36:27 +03:00
ir_raw_event_handle ( dev ) ;
out :
2011-04-28 19:13:58 +04:00
return count ;
2010-11-26 00:36:27 +03:00
}
static void loop_set_idle ( struct rc_dev * dev , bool enable )
{
struct loopback_dev * lodev = dev - > priv ;
if ( lodev - > idle ! = enable ) {
2021-07-03 16:36:15 +03:00
dev_dbg ( & dev - > dev , " %sing idle mode \n " , enable ? " enter " : " exit " ) ;
2010-11-26 00:36:27 +03:00
lodev - > idle = enable ;
}
}
2021-07-03 12:04:40 +03:00
static int loop_set_wideband_receiver ( struct rc_dev * dev , int enable )
2010-11-26 00:36:27 +03:00
{
struct loopback_dev * lodev = dev - > priv ;
2021-07-03 12:04:40 +03:00
if ( lodev - > wideband ! = enable ) {
dev_dbg ( & dev - > dev , " using %sband receiver \n " , enable ? " wide " : " narrow " ) ;
lodev - > wideband = ! ! enable ;
2010-11-26 00:36:27 +03:00
}
return 0 ;
}
static int loop_set_carrier_report ( struct rc_dev * dev , int enable )
{
struct loopback_dev * lodev = dev - > priv ;
if ( lodev - > carrierreport ! = enable ) {
2021-07-03 16:36:15 +03:00
dev_dbg ( & dev - > dev , " %sabling carrier reports \n " , enable ? " en " : " dis " ) ;
2010-11-26 00:36:27 +03:00
lodev - > carrierreport = ! ! enable ;
}
return 0 ;
}
2015-03-31 20:48:11 +03:00
static int loop_set_wakeup_filter ( struct rc_dev * dev ,
struct rc_scancode_filter * sc )
{
static const unsigned int max = 512 ;
struct ir_raw_event * raw ;
int ret ;
int i ;
/* fine to disable filter */
if ( ! sc - > mask )
return 0 ;
/* encode the specified filter and loop it back */
raw = kmalloc_array ( max , sizeof ( * raw ) , GFP_KERNEL ) ;
if ( ! raw )
return - ENOMEM ;
ret = ir_raw_encode_scancode ( dev - > wakeup_protocol , sc - > data , raw , max ) ;
/* still loop back the partial raw IR even if it's incomplete */
if ( ret = = - ENOBUFS )
ret = max ;
if ( ret > = 0 ) {
/* do the loopback */
for ( i = 0 ; i < ret ; + + i )
ir_raw_event_store ( dev , & raw [ i ] ) ;
ir_raw_event_handle ( dev ) ;
ret = 0 ;
}
kfree ( raw ) ;
return ret ;
}
2010-11-26 00:36:27 +03:00
static int __init loop_init ( void )
{
struct rc_dev * rc ;
int ret ;
2016-12-16 11:50:58 +03:00
rc = rc_allocate_device ( RC_DRIVER_IR_RAW ) ;
2021-07-03 16:36:15 +03:00
if ( ! rc )
2010-11-26 00:36:27 +03:00
return - ENOMEM ;
2017-07-01 19:13:19 +03:00
rc - > device_name = " rc-core loopback device " ;
2010-11-26 00:36:27 +03:00
rc - > input_phys = " rc-core/virtual " ;
rc - > input_id . bustype = BUS_VIRTUAL ;
rc - > input_id . version = 1 ;
rc - > driver_name = DRIVER_NAME ;
rc - > map_name = RC_MAP_EMPTY ;
rc - > priv = & loopdev ;
2017-08-07 23:20:58 +03:00
rc - > allowed_protocols = RC_PROTO_BIT_ALL_IR_DECODER ;
rc - > allowed_wakeup_protocols = RC_PROTO_BIT_ALL_IR_ENCODER ;
2015-03-31 20:48:11 +03:00
rc - > encode_wakeup = true ;
2021-07-05 20:56:01 +03:00
rc - > timeout = IR_DEFAULT_TIMEOUT ;
2010-11-26 00:36:27 +03:00
rc - > min_timeout = 1 ;
2021-07-05 20:56:01 +03:00
rc - > max_timeout = IR_MAX_TIMEOUT ;
2020-08-23 20:23:05 +03:00
rc - > rx_resolution = 1 ;
rc - > tx_resolution = 1 ;
2010-11-26 00:36:27 +03:00
rc - > s_tx_mask = loop_set_tx_mask ;
rc - > s_tx_carrier = loop_set_tx_carrier ;
rc - > s_tx_duty_cycle = loop_set_tx_duty_cycle ;
rc - > s_rx_carrier_range = loop_set_rx_carrier_range ;
rc - > tx_ir = loop_tx_ir ;
rc - > s_idle = loop_set_idle ;
2021-07-03 12:04:40 +03:00
rc - > s_wideband_receiver = loop_set_wideband_receiver ;
2010-11-26 00:36:27 +03:00
rc - > s_carrier_report = loop_set_carrier_report ;
2015-03-31 20:48:11 +03:00
rc - > s_wakeup_filter = loop_set_wakeup_filter ;
2010-11-26 00:36:27 +03:00
2021-07-03 12:04:40 +03:00
loopdev . txmask = RXMASK_NARROWBAND ;
2010-11-26 00:36:27 +03:00
loopdev . txcarrier = 36000 ;
loopdev . txduty = 50 ;
loopdev . rxcarriermin = 1 ;
loopdev . rxcarriermax = ~ 0 ;
loopdev . idle = true ;
2021-07-03 12:04:40 +03:00
loopdev . wideband = false ;
2010-11-26 00:36:27 +03:00
loopdev . carrierreport = false ;
ret = rc_register_device ( rc ) ;
if ( ret < 0 ) {
2021-07-03 16:36:15 +03:00
dev_err ( & rc - > dev , " rc_dev registration failed \n " ) ;
2010-11-26 00:36:27 +03:00
rc_free_device ( rc ) ;
return ret ;
}
loopdev . dev = rc ;
return 0 ;
}
static void __exit loop_exit ( void )
{
rc_unregister_device ( loopdev . dev ) ;
}
module_init ( loop_init ) ;
module_exit ( loop_exit ) ;
MODULE_DESCRIPTION ( " Loopback device for rc-core debugging " ) ;
MODULE_AUTHOR ( " David Härdeman <david@hardeman.nu> " ) ;
MODULE_LICENSE ( " GPL " ) ;