2007-04-27 12:31:21 -03:00
/*
* Driver for the remote control of SAA7146 based AV7110 cards
*
* Copyright ( C ) 1999 - 2003 Holger Waechtler < holger @ convergence . de >
* Copyright ( C ) 2003 - 2007 Oliver Endriss < o . endriss @ gmx . de >
*
* 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 .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 59 Temple Place - Suite 330 , Boston , MA 02111 - 1307 , USA .
* Or , point your browser to http : //www.gnu.org/copyleft/gpl.html
*
*/
2005-04-16 15:20:36 -07:00
# include <linux/types.h>
# include <linux/init.h>
# include <linux/module.h>
# include <linux/moduleparam.h>
# include <linux/proc_fs.h>
2007-02-14 22:57:42 -02:00
# include <linux/kernel.h>
2005-04-16 15:20:36 -07:00
# include <asm/bitops.h>
# include "av7110.h"
2005-09-09 13:03:12 -07:00
# include "av7110_hw.h"
2005-04-16 15:20:36 -07:00
2007-04-27 12:31:21 -03:00
# define AV_CNT 4
# define IR_RC5 0
# define IR_RCMM 1
# define IR_RC5_EXT 2 /* internal only */
# define IR_ALL 0xffffffff
# define UP_TIMEOUT (HZ*7 / 25)
/* Note: enable ir debugging by or'ing debug with 16 */
static int ir_protocol [ AV_CNT ] = { IR_RCMM , IR_RCMM , IR_RCMM , IR_RCMM } ;
module_param_array ( ir_protocol , int , NULL , 0644 ) ;
MODULE_PARM_DESC ( ir_protocol , " Infrared protocol: 0 RC5, 1 RCMM (default) " ) ;
static int ir_inversion [ AV_CNT ] ;
module_param_array ( ir_inversion , int , NULL , 0644 ) ;
MODULE_PARM_DESC ( ir_inversion , " Inversion of infrared signal: 0 not inverted (default), 1 inverted " ) ;
static uint ir_device_mask [ AV_CNT ] = { IR_ALL , IR_ALL , IR_ALL , IR_ALL } ;
module_param_array ( ir_device_mask , uint , NULL , 0644 ) ;
MODULE_PARM_DESC ( ir_device_mask , " Bitmask of infrared devices: bit 0..31 = device 0..31 (default: all) " ) ;
2005-04-16 15:20:36 -07:00
2007-04-27 12:31:21 -03:00
static int av_cnt ;
static struct av7110 * av_list [ AV_CNT ] ;
2006-01-09 15:25:09 -02:00
2007-04-27 12:31:21 -03:00
static u16 default_key_map [ 256 ] = {
2005-04-16 15:20:36 -07:00
KEY_0 , KEY_1 , KEY_2 , KEY_3 , KEY_4 , KEY_5 , KEY_6 , KEY_7 ,
KEY_8 , KEY_9 , KEY_BACK , 0 , KEY_POWER , KEY_MUTE , 0 , KEY_INFO ,
KEY_VOLUMEUP , KEY_VOLUMEDOWN , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
KEY_CHANNELUP , KEY_CHANNELDOWN , 0 , 0 , 0 , 0 , 0 , 0 ,
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
0 , 0 , 0 , 0 , KEY_TEXT , 0 , 0 , KEY_TV , 0 , 0 , 0 , 0 , 0 , KEY_SETUP , 0 , 0 ,
0 , 0 , 0 , KEY_SUBTITLE , 0 , 0 , KEY_LANGUAGE , 0 ,
KEY_RADIO , 0 , 0 , 0 , 0 , KEY_EXIT , 0 , 0 ,
KEY_UP , KEY_DOWN , KEY_LEFT , KEY_RIGHT , KEY_OK , 0 , 0 , 0 ,
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , KEY_RED , KEY_GREEN , KEY_YELLOW ,
KEY_BLUE , 0 , 0 , 0 , 0 , 0 , 0 , 0 , KEY_MENU , KEY_LIST , 0 , 0 , 0 , 0 , 0 , 0 ,
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
0 , 0 , 0 , 0 , KEY_UP , KEY_UP , KEY_DOWN , KEY_DOWN ,
0 , 0 , 0 , 0 , KEY_EPG , 0 , 0 , 0 ,
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , KEY_VCR
} ;
2007-04-27 12:31:21 -03:00
/* key-up timer */
static void av7110_emit_keyup ( unsigned long parm )
2005-04-16 15:20:36 -07:00
{
2007-04-27 12:31:21 -03:00
struct infrared * ir = ( struct infrared * ) parm ;
if ( ! ir | | ! test_bit ( ir - > last_key , ir - > input_dev - > key ) )
2005-04-16 15:20:36 -07:00
return ;
2007-04-27 12:31:21 -03:00
input_report_key ( ir - > input_dev , ir - > last_key , 0 ) ;
input_sync ( ir - > input_dev ) ;
2005-04-16 15:20:36 -07:00
}
2007-04-27 12:31:21 -03:00
/* tasklet */
2005-09-09 13:03:12 -07:00
static void av7110_emit_key ( unsigned long parm )
2005-04-16 15:20:36 -07:00
{
2007-04-27 12:31:21 -03:00
struct infrared * ir = ( struct infrared * ) parm ;
u32 ircom = ir - > ir_command ;
2005-04-16 15:20:36 -07:00
u8 data ;
u8 addr ;
2007-04-27 12:31:21 -03:00
u16 toggle ;
2005-04-16 15:20:36 -07:00
u16 keycode ;
/* extract device address and data */
2007-04-27 12:31:21 -03:00
switch ( ir - > protocol ) {
case IR_RC5 : /* RC5: 5 bits device address, 6 bits data */
2005-09-09 13:03:12 -07:00
data = ircom & 0x3f ;
addr = ( ircom > > 6 ) & 0x1f ;
2007-04-27 12:31:21 -03:00
toggle = ircom & 0x0800 ;
2005-09-09 13:03:12 -07:00
break ;
2007-04-27 12:31:21 -03:00
case IR_RCMM : /* RCMM: ? bits device address, ? bits data */
2005-04-16 15:20:36 -07:00
data = ircom & 0xff ;
2007-04-27 12:31:21 -03:00
addr = ( ircom > > 8 ) & 0x1f ;
toggle = ircom & 0x8000 ;
2005-09-09 13:03:12 -07:00
break ;
2007-04-27 12:31:21 -03:00
case IR_RC5_EXT : /* extended RC5: 5 bits device address, 7 bits data */
2005-04-16 15:20:36 -07:00
data = ircom & 0x3f ;
addr = ( ircom > > 6 ) & 0x1f ;
2005-09-09 13:03:12 -07:00
/* invert 7th data bit for backward compatibility with RC5 keymaps */
if ( ! ( ircom & 0x1000 ) )
data | = 0x40 ;
2007-04-27 12:31:21 -03:00
toggle = ircom & 0x0800 ;
2005-09-09 13:03:12 -07:00
break ;
default :
2007-04-27 12:31:21 -03:00
printk ( " %s invalid protocol %x \n " , __FUNCTION__ , ir - > protocol ) ;
2005-09-09 13:03:12 -07:00
return ;
2005-04-16 15:20:36 -07:00
}
2007-04-27 12:31:24 -03:00
input_event ( ir - > input_dev , EV_MSC , MSC_RAW , ( addr < < 16 ) | data ) ;
input_event ( ir - > input_dev , EV_MSC , MSC_SCAN , data ) ;
2007-04-27 12:31:21 -03:00
keycode = ir - > key_map [ data ] ;
2005-04-16 15:20:36 -07:00
2007-04-27 12:31:21 -03:00
dprintk ( 16 , " %s: code %08x -> addr %i data 0x%02x -> keycode %i \n " ,
__FUNCTION__ , ircom , addr , data , keycode ) ;
2005-04-16 15:20:36 -07:00
2007-04-27 12:31:21 -03:00
/* check device address */
if ( ! ( ir - > device_mask & ( 1 < < addr ) ) )
return ;
2005-04-16 15:20:36 -07:00
if ( ! keycode ) {
2007-04-27 12:31:21 -03:00
printk ( " %s: code %08x -> addr %i data 0x%02x -> unknown key! \n " ,
__FUNCTION__ , ircom , addr , data ) ;
2005-04-16 15:20:36 -07:00
return ;
}
2007-04-27 12:31:21 -03:00
if ( timer_pending ( & ir - > keyup_timer ) ) {
del_timer ( & ir - > keyup_timer ) ;
if ( ir - > last_key ! = keycode | | toggle ! = ir - > last_toggle ) {
ir - > delay_timer_finished = 0 ;
input_event ( ir - > input_dev , EV_KEY , ir - > last_key , 0 ) ;
input_event ( ir - > input_dev , EV_KEY , keycode , 1 ) ;
input_sync ( ir - > input_dev ) ;
} else if ( ir - > delay_timer_finished ) {
input_event ( ir - > input_dev , EV_KEY , keycode , 2 ) ;
input_sync ( ir - > input_dev ) ;
2006-11-20 10:23:04 -03:00
}
2006-01-09 15:25:09 -02:00
} else {
2007-04-27 12:31:21 -03:00
ir - > delay_timer_finished = 0 ;
input_event ( ir - > input_dev , EV_KEY , keycode , 1 ) ;
input_sync ( ir - > input_dev ) ;
2006-01-09 15:25:09 -02:00
}
2005-04-16 15:20:36 -07:00
2007-04-27 12:31:21 -03:00
ir - > last_key = keycode ;
ir - > last_toggle = toggle ;
2005-04-16 15:20:36 -07:00
2007-04-27 12:31:21 -03:00
ir - > keyup_timer . expires = jiffies + UP_TIMEOUT ;
add_timer ( & ir - > keyup_timer ) ;
2005-04-16 15:20:36 -07:00
}
2007-04-27 12:31:21 -03:00
/* register with input layer */
static void input_register_keys ( struct infrared * ir )
2005-04-16 15:20:36 -07:00
{
int i ;
2007-04-27 12:31:21 -03:00
set_bit ( EV_KEY , ir - > input_dev - > evbit ) ;
set_bit ( EV_REP , ir - > input_dev - > evbit ) ;
2007-04-27 12:31:24 -03:00
set_bit ( EV_MSC , ir - > input_dev - > evbit ) ;
set_bit ( MSC_RAW , ir - > input_dev - > mscbit ) ;
set_bit ( MSC_SCAN , ir - > input_dev - > mscbit ) ;
2005-04-16 15:20:36 -07:00
2007-04-27 12:31:21 -03:00
memset ( ir - > input_dev - > keybit , 0 , sizeof ( ir - > input_dev - > keybit ) ) ;
for ( i = 0 ; i < ARRAY_SIZE ( ir - > key_map ) ; i + + ) {
if ( ir - > key_map [ i ] > KEY_MAX )
ir - > key_map [ i ] = 0 ;
else if ( ir - > key_map [ i ] > KEY_RESERVED )
set_bit ( ir - > key_map [ i ] , ir - > input_dev - > keybit ) ;
2005-04-16 15:20:36 -07:00
}
2007-04-27 12:31:21 -03:00
ir - > input_dev - > keycode = ir - > key_map ;
ir - > input_dev - > keycodesize = sizeof ( ir - > key_map [ 0 ] ) ;
ir - > input_dev - > keycodemax = ARRAY_SIZE ( ir - > key_map ) ;
2005-04-16 15:20:36 -07:00
}
2007-04-27 12:31:21 -03:00
/* called by the input driver after rep[REP_DELAY] ms */
static void input_repeat_key ( unsigned long parm )
2005-04-16 15:20:36 -07:00
{
2007-04-27 12:31:21 -03:00
struct infrared * ir = ( struct infrared * ) parm ;
ir - > delay_timer_finished = 1 ;
2005-04-16 15:20:36 -07:00
}
2007-04-27 12:31:21 -03:00
/* check for configuration changes */
int av7110_check_ir_config ( struct av7110 * av7110 , int force )
2006-01-23 09:58:17 -02:00
{
2007-04-27 12:31:21 -03:00
int i ;
int modified = force ;
int ret = - ENODEV ;
2006-01-23 09:58:17 -02:00
2007-04-27 12:31:21 -03:00
for ( i = 0 ; i < av_cnt ; i + + )
if ( av7110 = = av_list [ i ] )
break ;
if ( i < av_cnt & & av7110 ) {
if ( ( av7110 - > ir . protocol & 1 ) ! = ir_protocol [ i ] | |
av7110 - > ir . inversion ! = ir_inversion [ i ] )
modified = true ;
if ( modified ) {
/* protocol */
if ( ir_protocol [ i ] ) {
ir_protocol [ i ] = 1 ;
av7110 - > ir . protocol = IR_RCMM ;
av7110 - > ir . ir_config = 0x0001 ;
} else if ( FW_VERSION ( av7110 - > arm_app ) > = 0x2620 ) {
av7110 - > ir . protocol = IR_RC5_EXT ;
av7110 - > ir . ir_config = 0x0002 ;
} else {
av7110 - > ir . protocol = IR_RC5 ;
av7110 - > ir . ir_config = 0x0000 ;
}
/* inversion */
if ( ir_inversion [ i ] ) {
ir_inversion [ i ] = 1 ;
av7110 - > ir . ir_config | = 0x8000 ;
}
av7110 - > ir . inversion = ir_inversion [ i ] ;
/* update ARM */
ret = av7110_fw_cmd ( av7110 , COMTYPE_PIDFILTER , SetIR , 1 ,
av7110 - > ir . ir_config ) ;
} else
ret = 0 ;
/* address */
if ( av7110 - > ir . device_mask ! = ir_device_mask [ i ] )
av7110 - > ir . device_mask = ir_device_mask [ i ] ;
2006-01-23 09:58:17 -02:00
}
2007-04-27 12:31:21 -03:00
2006-01-23 09:58:17 -02:00
return ret ;
}
2007-04-27 12:31:21 -03:00
/* /proc/av7110_ir interface */
2005-04-16 15:20:36 -07:00
static int av7110_ir_write_proc ( struct file * file , const char __user * buffer ,
unsigned long count , void * data )
{
char * page ;
2005-09-09 13:03:12 -07:00
u32 ir_config ;
2007-04-27 12:31:21 -03:00
int size = sizeof ir_config + sizeof av_list [ 0 ] - > ir . key_map ;
2005-09-09 13:03:12 -07:00
int i ;
2005-04-16 15:20:36 -07:00
if ( count < size )
return - EINVAL ;
page = ( char * ) vmalloc ( size ) ;
if ( ! page )
return - ENOMEM ;
if ( copy_from_user ( page , buffer , size ) ) {
vfree ( page ) ;
return - EFAULT ;
}
2007-04-27 12:31:21 -03:00
memcpy ( & ir_config , page , sizeof ir_config ) ;
for ( i = 0 ; i < av_cnt ; i + + ) {
/* keymap */
memcpy ( av_list [ i ] - > ir . key_map , page + sizeof ir_config ,
sizeof ( av_list [ i ] - > ir . key_map ) ) ;
/* protocol, inversion, address */
ir_protocol [ i ] = ir_config & 0x0001 ;
ir_inversion [ i ] = ir_config & 0x8000 ? 1 : 0 ;
if ( ir_config & 0x4000 )
ir_device_mask [ i ] = 1 < < ( ( ir_config > > 16 ) & 0x1f ) ;
else
ir_device_mask [ i ] = IR_ALL ;
/* update configuration */
av7110_check_ir_config ( av_list [ i ] , false ) ;
input_register_keys ( & av_list [ i ] - > ir ) ;
}
2005-04-16 15:20:36 -07:00
vfree ( page ) ;
return count ;
}
2007-04-27 12:31:21 -03:00
/* interrupt handler */
2005-09-09 13:03:12 -07:00
static void ir_handler ( struct av7110 * av7110 , u32 ircom )
{
2007-04-27 12:31:21 -03:00
dprintk ( 4 , " ir command = %08x \n " , ircom ) ;
av7110 - > ir . ir_command = ircom ;
tasklet_schedule ( & av7110 - > ir . ir_tasklet ) ;
2005-09-09 13:03:12 -07:00
}
2005-04-16 15:20:36 -07:00
2006-02-27 00:09:20 -03:00
int __devinit av7110_ir_init ( struct av7110 * av7110 )
2005-09-09 13:03:12 -07:00
{
2007-04-27 12:31:21 -03:00
struct input_dev * input_dev ;
2005-09-09 13:03:12 -07:00
static struct proc_dir_entry * e ;
2006-11-20 10:23:04 -03:00
int err ;
2005-04-16 15:20:36 -07:00
2007-02-14 22:57:42 -02:00
if ( av_cnt > = ARRAY_SIZE ( av_list ) )
2005-09-09 13:03:12 -07:00
return - ENOSPC ;
2005-04-16 15:20:36 -07:00
2005-09-09 13:03:12 -07:00
av_list [ av_cnt + + ] = av7110 ;
2007-03-12 21:45:46 -03:00
av7110_check_ir_config ( av7110 , true ) ;
2005-04-16 15:20:36 -07:00
2007-04-27 12:31:21 -03:00
init_timer ( & av7110 - > ir . keyup_timer ) ;
av7110 - > ir . keyup_timer . function = av7110_emit_keyup ;
av7110 - > ir . keyup_timer . data = ( unsigned long ) & av7110 - > ir ;
input_dev = input_allocate_device ( ) ;
if ( ! input_dev )
return - ENOMEM ;
av7110 - > ir . input_dev = input_dev ;
snprintf ( av7110 - > ir . input_phys , sizeof ( av7110 - > ir . input_phys ) ,
" pci-%s/ir0 " , pci_name ( av7110 - > dev - > pci ) ) ;
2005-09-09 13:03:12 -07:00
2007-04-27 12:31:21 -03:00
input_dev - > name = " DVB on-card IR receiver " ;
input_dev - > phys = av7110 - > ir . input_phys ;
input_dev - > id . bustype = BUS_PCI ;
input_dev - > id . version = 2 ;
if ( av7110 - > dev - > pci - > subsystem_vendor ) {
input_dev - > id . vendor = av7110 - > dev - > pci - > subsystem_vendor ;
input_dev - > id . product = av7110 - > dev - > pci - > subsystem_device ;
} else {
input_dev - > id . vendor = av7110 - > dev - > pci - > vendor ;
input_dev - > id . product = av7110 - > dev - > pci - > device ;
}
2007-07-16 09:28:15 -03:00
input_dev - > dev . parent = & av7110 - > dev - > pci - > dev ;
2007-04-27 12:31:21 -03:00
/* initial keymap */
memcpy ( av7110 - > ir . key_map , default_key_map , sizeof av7110 - > ir . key_map ) ;
input_register_keys ( & av7110 - > ir ) ;
err = input_register_device ( input_dev ) ;
if ( err ) {
input_free_device ( input_dev ) ;
return err ;
}
input_dev - > timer . function = input_repeat_key ;
input_dev - > timer . data = ( unsigned long ) & av7110 - > ir ;
if ( av_cnt = = 1 ) {
2005-09-09 13:03:12 -07:00
e = create_proc_entry ( " av7110_ir " , S_IFREG | S_IRUGO | S_IWUSR , NULL ) ;
if ( e ) {
e - > write_proc = av7110_ir_write_proc ;
e - > size = 4 + 256 * sizeof ( u16 ) ;
}
2005-04-16 15:20:36 -07:00
}
2007-04-27 12:31:21 -03:00
tasklet_init ( & av7110 - > ir . ir_tasklet , av7110_emit_key , ( unsigned long ) & av7110 - > ir ) ;
av7110 - > ir . ir_handler = ir_handler ;
2005-09-09 13:03:12 -07:00
2005-04-16 15:20:36 -07:00
return 0 ;
}
2006-02-27 00:09:20 -03:00
void __devexit av7110_ir_exit ( struct av7110 * av7110 )
2005-04-16 15:20:36 -07:00
{
2005-09-09 13:03:12 -07:00
int i ;
if ( av_cnt = = 0 )
2005-04-16 15:20:36 -07:00
return ;
2005-09-09 13:03:12 -07:00
2007-04-27 12:31:21 -03:00
del_timer_sync ( & av7110 - > ir . keyup_timer ) ;
av7110 - > ir . ir_handler = NULL ;
tasklet_kill ( & av7110 - > ir . ir_tasklet ) ;
2005-09-09 13:03:12 -07:00
for ( i = 0 ; i < av_cnt ; i + + )
if ( av_list [ i ] = = av7110 ) {
av_list [ i ] = av_list [ av_cnt - 1 ] ;
av_list [ av_cnt - 1 ] = NULL ;
break ;
}
2007-04-27 12:31:21 -03:00
if ( av_cnt = = 1 )
2005-09-09 13:03:12 -07:00
remove_proc_entry ( " av7110_ir " , NULL ) ;
2007-04-27 12:31:21 -03:00
input_unregister_device ( av7110 - > ir . input_dev ) ;
2005-09-09 13:03:12 -07:00
av_cnt - - ;
2005-04-16 15:20:36 -07:00
}
2007-04-27 12:31:21 -03:00
//MODULE_AUTHOR("Holger Waechtler <holger@convergence.de>, Oliver Endriss <o.endriss@gmx.de>");
2005-04-16 15:20:36 -07:00
//MODULE_LICENSE("GPL");