Mailing List archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[linux-dvb] Re: Remote control for Hauppauge Nova-T



Hi,

I've been playing with the budget-ci module with a view to adding loadable 
keymap support as per the av7110 module.

Thus far, I have separated out the "generic keymap support" functions into a 
new module called dvb_ir - the hope being that the facilities can be shared 
and even extended.

I have also started to separate out the keymap support from the av7110 module 
- but since I do not have the appropriate hardware or experience, I do not 
know whether this will work... or if this is a good idea.

The code below works for me with my Nova-T card on a 2.4 kernel build.

I can use the av7110_loadkeys utility to output a keymap to /proc/dvb_ir0 (I 
would expect to choose a more sensible naming convention for the proc entry 
at a later date).

./av7110_loadkeys nova-t.rc5 > /proc/dvb_ir0

I have never written a linux module before and am really just "copying other 
people's code". I'm posting this here, in the early stages of development, in 
the hope that people might provide some feedback/comments, etc.

Is this of interest to anyone? Is it a good idea? Am I wasting my time and/or 
am I making a complete hash of things!?

As I said, I've *never* written a kernel module so please feel free to tell me 
that I don't have a clue and that my code sucks. ;)

budget-ci.c (latest cvs, 23rd Nov 2003)
30a31
> #include "dvb_ir.h"
36,42d36
< #include <linux/input.h>
<
< #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0))
< #include "input_fake.h"
< #endif
< 
<
46d39
< 	struct input_dev input_dev;
47a41
> 	struct dvb_ir dvb_ir;
74,145d67
< /* from reading the following remotes:
<    Zenith Universal 7 / TV Mode 807 / VCR Mode 837
<    Hauppauge (from NOVA-CI-s box product)
<    i've taken a "middle of the road" approach and note the differences
< */
< static  u16 key_map[64] = {
< 	/* 0x0X */
< 	KEY_0, KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, KEY_7, KEY_8,
< 	KEY_9,
< 	KEY_ENTER,
< 	KEY_RED,
< 	KEY_POWER,              /* RADIO on Hauppauge */
< 	KEY_MUTE,
< 	0,
< 	KEY_A,                  /* TV on Hauppauge */
< 	/* 0x1X */
< 	KEY_VOLUMEUP, KEY_VOLUMEDOWN,
< 	0, 0,
< 	KEY_B,
< 	0, 0, 0, 0, 0, 0, 0,
< 	KEY_UP, KEY_DOWN,
< 	KEY_OPTION,             /* RESERVED on Hauppauge */
< 	KEY_BREAK,
< 	/* 0x2X */
< 	KEY_CHANNELUP, KEY_CHANNELDOWN,
< 	KEY_PREVIOUS,           /* Prev. Ch on Zenith, SOURCE on Hauppauge */
< 	0, KEY_RESTART, KEY_OK,
< 	KEY_CYCLEWINDOWS,       /* MINIMIZE on Hauppauge */
< 	0,
< 	KEY_ENTER,              /* VCR mode on Zenith */
< 	KEY_PAUSE,
< 	0,
< 	KEY_RIGHT, KEY_LEFT,
< 	0,
< 	KEY_MENU,               /* FULL SCREEN on Hauppauge */
< 	0,
< 	/* 0x3X */
< 	KEY_SLOW,
< 	KEY_PREVIOUS,           /* VCR mode on Zenith */
< 	KEY_REWIND,
< 	0,
< 	KEY_FASTFORWARD,
< 	KEY_PLAY, KEY_STOP,
< 	KEY_RECORD,
< 	KEY_TUNER,              /* TV/VCR on Zenith */
< 	0,
< 	KEY_C,
< 	0,
< 	KEY_EXIT,
< 	KEY_POWER2,
< 	KEY_TUNER,              /* VCR mode on Zenith */
< 	0,
< };
<
< 
< static void msp430_ir_debounce (unsigned long data)
< {
< 	struct input_dev *dev = (struct input_dev *) data;
<
< 	if (dev->rep[0] == 0 || dev->rep[0] == ~0) {
< 		input_event(dev, EV_KEY, key_map[dev->repeat_key], !!0);
< 		return;
< 	}
<
< 	dev->rep[0] = 0;
< 	dev->timer.expires = jiffies + HZ * 350 / 1000;
< 	add_timer(&dev->timer);
< 	input_event(dev, EV_KEY, key_map[dev->repeat_key], 2);  /* REPEAT */
< }
< 
<
< 
150,151d71
< 	struct input_dev *dev = &budget_ci->input_dev;
< 	unsigned int code = budget_debiread4(saa, DEBINOSWAP, 0x1234, 2) >> 8;
152a73
> 	unsigned int code = budget_debiread4(saa, DEBINOSWAP, 0x1234, 2) >> 8;
155,179c76
<
<         	if (timer_pending(&dev->timer)) {
<                 	if (code == dev->repeat_key) {
<                         	++dev->rep[0];
< 	                        return;
<         	        }
<                 	del_timer(&dev->timer);
< 		        input_event(dev, EV_KEY, key_map[dev->repeat_key], !!0);
< 		}
< 
< 		if (!key_map[code]) {
<         	        printk ("DVB (%s): no key for %02x!\n",
< 				__FUNCTION__, code);
< 		        return;
<        		}
< 
< 		/* initialize debounce and repeat */
< 		dev->repeat_key = code;
< 		/* Zenith remote _always_ sends 2 sequences */
< 		dev->rep[0] = ~0;
< 		/* 350 milliseconds */
< 		dev->timer.expires = jiffies + HZ * 350 / 1000;
< 		/* MAKE */
<         	input_event(dev, EV_KEY, key_map[code], !0);
< 		add_timer(&dev->timer);
---
> 		dvb_ir_event(&budget_ci->dvb_ir, code, 0);
187,189c84
< 	int i;
< 
< 	memset(&budget_ci->input_dev, 0, sizeof(struct input_dev));
---
> 	struct input_dev *input_dev = &budget_ci->dvb_ir.input_dev;
191,201c86,88
< 	budget_ci->input_dev.name = saa->name;
< 
< 	set_bit(EV_KEY, budget_ci->input_dev.evbit);
< 
< 	for (i=0; i<sizeof(key_map)/sizeof(*key_map); i++)
< 		if (key_map[i])
< 			set_bit(key_map[i], budget_ci->input_dev.keybit);
< 
< 	input_register_device(&budget_ci->input_dev);
< 
< 	budget_ci->input_dev.timer.function = msp430_ir_debounce;
---
> 	memset(&budget_ci->dvb_ir, 0, sizeof(struct dvb_ir));
> 	dvb_ir_register(&budget_ci->dvb_ir, budget_ci->budget.dvb_adapter);
> 	input_dev->name = saa->name;
214d100
< 	struct input_dev *dev = &budget_ci->input_dev;
220,223c106
< 	if (del_timer(&dev->timer))
< 		input_event(dev, EV_KEY, key_map[dev->repeat_key], !!0);
<
< 	input_unregister_device(dev);
---
> 	dvb_ir_unregister(&budget_ci->dvb_ir);

/*
 * dvb_ir.h
 */

#ifndef _DVB_IR_H_
#define _DVB_IR_H_

#include "dvbdev.h"

#include <linux/input.h>

#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0))
#include "input_fake.h"
#endif

struct dvb_ir {
 	struct dvb_adapter *dvb_adapter;
 	struct input_dev input_dev;
	u16 key_map[256];
	u32 key_debounce;
	u32 key_repeat;
	u32 ir_config;
	void (*setup_ir_config) (void *data, u32 ir_config);
	void *setup_ir_config_data;
};

extern int dvb_ir_register(struct dvb_ir *dvb_ir, struct dvb_adapter* 
dvb_adapter);
extern void dvb_ir_unregister(struct dvb_ir *dvb_ir);

extern void dvb_ir_event(struct dvb_ir *dvb_ir, u16 keycode, u16 toggle);

#endif /* #ifndef _DVB_IR_H_ */


/*
 * dvb_ir.c: DVB IR Receiver Support (including loadable keymaps)
 */

#include "dvb_ir.h"

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/proc_fs.h>
#include <linux/vmalloc.h>

static int dvb_ir_debug = 0;
#define dprintk(x...)  do { if (dvb_ir_debug) printk (x); } while (0)

#define KEY_DEBOUNCE_TIMEOUT (HZ * 350 / 1000)
#define KEY_REPEAT_TIMEOUT (HZ * 350 / 1000)

static void input_register_keys (struct dvb_ir* dvb_ir)
{
	int i;
	struct input_dev *input_dev = &dvb_ir->input_dev;

	memset (input_dev->keybit, 0, sizeof(input_dev->keybit));

	for (i=0; i<sizeof(dvb_ir->key_map)/sizeof(dvb_ir->key_map[0]); i++) {
		if (dvb_ir->key_map[i] > KEY_MAX)
			dvb_ir->key_map[i] = 0;
		else if (dvb_ir->key_map[i] > KEY_RESERVED)
			set_bit (dvb_ir->key_map[i], input_dev->keybit);
	}
}

static int dvb_ir_write_proc (struct file *file, const char *buffer,
	                      unsigned long count, void *data)
{
	struct dvb_ir *dvb_ir = (struct dvb_ir*) data;

	char *page;
	int size = 4 + 256 * sizeof(u16);

	if (count < size)
		return -EINVAL;

	page = (char *)vmalloc(size);
	if( NULL == page ) {
		return -ENOMEM;
	}

	if (copy_from_user(page, buffer, size)) {
		vfree(page);
		return -EFAULT;
	}

	memcpy (&dvb_ir->ir_config, page, 4);
	memcpy (&dvb_ir->key_map, page + 4, 256 * sizeof(u16));

	vfree(page);

	if (dvb_ir->setup_ir_config) {
		dvb_ir->setup_ir_config(dvb_ir->setup_ir_config_data, dvb_ir->ir_config);
	}

	input_register_keys (dvb_ir);

	return count;
}

static void dvb_ir_debounce (unsigned long data)
{
	struct dvb_ir *dvb_ir = (struct dvb_ir*) data;
	struct input_dev *input_dev = &dvb_ir->input_dev;

	if (input_dev->rep[0] == 0 || input_dev->rep[0] == ~0) {
		input_event(input_dev, EV_KEY, dvb_ir->key_map[input_dev->repeat_key], !!0);
		return;
	}

	input_dev->rep[0] = 0;
	input_dev->timer.expires = jiffies + dvb_ir->key_repeat;
	add_timer(&input_dev->timer);
	input_event(input_dev, EV_KEY, dvb_ir->key_map[input_dev->repeat_key], 2);  
/* REPEAT */
}

void dvb_ir_event(struct dvb_ir *dvb_ir, u16 keycode, u16 toggle)
{
	struct input_dev *input_dev = &dvb_ir->input_dev;
       	if (timer_pending(&input_dev->timer)) {
               	if (keycode == input_dev->repeat_key) {
                       	++input_dev->rep[0];
                        return;
       	        }
               	del_timer(&input_dev->timer);
	        input_event(input_dev, EV_KEY, 
dvb_ir->key_map[input_dev->repeat_key], !!0);
	}

	if (!dvb_ir->key_map[keycode]) {
       	        printk ("DVB (%s): no key for %02x!\n",
			__FUNCTION__, keycode);
	        return;
	}

	/* initialize debounce and repeat */
	input_dev->repeat_key = keycode;
	input_dev->rep[0] = ~0;
	input_dev->timer.expires = jiffies + dvb_ir->key_debounce;

       	input_event(input_dev, EV_KEY, dvb_ir->key_map[keycode], !0);
	add_timer(&input_dev->timer);
}

int dvb_ir_register(struct dvb_ir *dvb_ir, struct dvb_adapter *dvb_adapter)
{
	char procname[64];
	struct proc_dir_entry *e;
	struct input_dev *input_dev = &dvb_ir->input_dev;

	if (dvb_adapter) dvb_ir->dvb_adapter = dvb_adapter;
	else dvb_adapter = dvb_ir->dvb_adapter;

        dvb_ir->key_debounce = KEY_DEBOUNCE_TIMEOUT;
        dvb_ir->key_repeat = KEY_REPEAT_TIMEOUT;

	memset(input_dev, 0, sizeof(struct input_dev));

	memset(&dvb_ir->key_map, 0, sizeof(dvb_ir->key_map));
	input_register_keys(dvb_ir);

	set_bit (EV_KEY, input_dev->evbit);
        set_bit (EV_REP, input_dev->evbit);

	input_register_device(input_dev);

	input_dev->timer.function = dvb_ir_debounce;
	input_dev->timer.data = (unsigned long)dvb_ir;

	if (dvb_adapter) {
		snprintf(procname, sizeof(procname), "dvb_ir%d", dvb_ir->dvb_adapter->num);
		e = create_proc_entry (procname, S_IFREG | S_IRUGO | S_IWUSR, NULL);
		if (e) {
			e->write_proc = dvb_ir_write_proc;
			e->size = 4 + 256 * sizeof(u16);
			e->data = (void*)dvb_ir;
		}
	}

	return 0;
}

void dvb_ir_unregister(struct dvb_ir *dvb_ir)
{
	char procname[64];
	struct input_dev *input_dev = &dvb_ir->input_dev;

	if (dvb_ir->dvb_adapter) {
		snprintf(procname, sizeof(procname), "dvb_ir%d", dvb_ir->dvb_adapter->num);
		remove_proc_entry (procname, NULL);
	}

	if (del_timer(&input_dev->timer))
		input_event(input_dev, EV_KEY, dvb_ir->key_map[input_dev->repeat_key], !!0);

	input_unregister_device(input_dev);
}

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("DVB IR Receiver Support");

MODULE_PARM(dvb_ir_debug,"i");
MODULE_PARM_DESC(dvb_ir_debug, "enable DVB IR receiver debug messages");


example nova-t.rc5 file:

0x00 KEY_0
0x01 KEY_1
0x02 KEY_2
0x03 KEY_3
0x04 KEY_4
0x05 KEY_5
0x06 KEY_6
0x07 KEY_7
0x08 KEY_8
0x09 KEY_9
0x0B KEY_RED
0x0C KEY_OPTION
0x0D KEY_MENU
0x0F KEY_MUTE
0x10 KEY_VOLUMEUP
0x11 KEY_VOLUMEDOWN
0x18 KEY_REWIND
0x1E KEY_FORWARD
0x1F KEY_EXIT
0x20 KEY_CHANNELUP
0x21 KEY_CHANNELDOWN
0x24 KEY_BACK
0x25 KEY_OK
0x29 KEY_BLUE
0x2E KEY_GREEN
0x30 KEY_PLAYPAUSE
0x32 KEY_REWIND
0x34 KEY_FASTFORWARD
0x35 KEY_PLAY
0x36 KEY_STOP
0x37 KEY_RECORD
0x38 KEY_YELLOW
0x3B KEY_GOTO
0x3C KEY_ZOOM
0x3D KEY_POWER


Best wishes,
-- 
Adrian Look



-- 
Info:
To unsubscribe send a mail to ecartis@linuxtv.org with "unsubscribe linux-dvb" as subject.



Home | Main Index | Thread Index