[linux-dvb] [PATCH] Add support for remote control to DEC2000-T

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

 



Hi

This patch adds support for recieving signals from an infrared remote 
control with the DEC2000-T box.
This is done by configuring an extra pipe to the usb device and getting 
interrupt events from the box over it.
This is enabled by a specific command which has to be send to the 
device( at least that is *my guessing* of the
command, as i get it only via sniffing the windows driver).
The driver registers itself in the linux input system, so that the 
events from the remote control can be read out
via /dev/input/eventX (using LIRC for example).

I only testet it with the remote control shipped with my box, dont know 
if others will work too.
I also cant say if it will work with all models/frmware versions of the 
DEC2000T(or even DEC3000S?)
Please also take a closer look at the code, as this is actually my first 
coding 'inside the linux kernel', so maybe
there are some errors in it.

There is a point in the code which may still need a bit work, which the 
the proper handling of keyrepeat events.
For now all signals are handled as single key up and down event.
I remember that topic was already discussed in the list(i think for the 
cinergy driver),  i just havent had time yet
to read it again.
As I wont have much time in the next weeks because of my exams just 
startet, I decided to send the patch as it
is now to get some more people the chance to test it.

Thanks
Peter





-------------- next part --------------
Index: ttusb_dec.c
===================================================================
RCS file: /cvs/linuxtv/dvb-kernel/linux/drivers/media/dvb/ttusb-dec/ttusb_dec.c,v
retrieving revision 1.64
diff -p -u -r1.64 ttusb_dec.c
--- ttusb_dec.c	8 Feb 2005 13:00:53 -0000	1.64
+++ ttusb_dec.c	21 Feb 2005 00:14:57 -0000
@@ -32,6 +32,7 @@
 #include <linux/firmware.h>
 #include <linux/crc32.h>
 #include <linux/init.h>
+#include <linux/input.h>
 
 #include "dmxdev.h"
 #include "dvb_demux.h"
@@ -42,11 +43,14 @@
 
 static int debug;
 static int output_pva;
+static int enable_rc;
 
 module_param(debug, int, 0644);
 MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off).");
 module_param(output_pva, int, 0444);
 MODULE_PARM_DESC(output_pva, "Output PVA from dvr device (default:off)");
+module_param(enable_rc, int, 0644);
+MODULE_PARM_DESC(enable_rc, "Turn on/off IR remote control(default: off)");
 
 #define dprintk	if (debug) printk
 
@@ -56,9 +60,11 @@ MODULE_PARM_DESC(output_pva, "Output PVA
 #define RESULT_PIPE		0x84
 #define IN_PIPE			0x88
 #define OUT_PIPE		0x07
+#define IRQ_PIPE		0x8A
 
 #define COMMAND_PACKET_SIZE	0x3c
 #define ARM_PACKET_SIZE		0x1000
+#define IRQ_PACKET_SIZE		0x8
 
 #define ISO_BUF_COUNT		0x04
 #define FRAMES_PER_ISO_BUF	0x04
@@ -107,9 +113,13 @@ struct ttusb_dec {
 	unsigned int			result_pipe;
 	unsigned int			in_pipe;
 	unsigned int			out_pipe;
+	unsigned int			irq_pipe;
 	enum ttusb_dec_interface	interface;
 	struct semaphore		usb_sem;
 
+	void			*irq_buffer;
+	struct urb		*irq_urb;
+	dma_addr_t		irq_dma_handle;
 	void			*iso_buffer;
 	dma_addr_t		iso_dma_handle;
 	struct urb		*iso_urb[ISO_BUF_COUNT];
@@ -141,6 +151,8 @@ struct ttusb_dec {
 	struct dvb_demux_filter	*video_filter;
 	struct list_head	filter_info_list;
 	spinlock_t		filter_info_list_lock;
+	
+	struct input_dev	rc_input_dev;
 
 	int			active; /* Loaded successfully */
 };
@@ -157,9 +169,83 @@ struct filter_info {
 	struct list_head	filter_info_list;
 };
 
+const uint16_t  rc_keys[] = {
+	KEY_POWER,   
+	KEY_MUTE,      
+	KEY_1,        
+	KEY_2,
+	KEY_3,
+	KEY_4,
+	KEY_5,
+	KEY_6,
+	KEY_7,
+	KEY_8,
+	KEY_9,
+	KEY_0,
+	KEY_CHANNELUP,
+	KEY_VOLUMEDOWN,
+	KEY_OK,
+	KEY_VOLUMEUP,
+	KEY_CHANNELDOWN,
+	KEY_PREVIOUS,
+	KEY_ESC,
+	KEY_RED,
+	KEY_GREEN,
+	KEY_YELLOW,
+	KEY_BLUE,
+	KEY_OPTION,
+	KEY_M,
+	KEY_RADIO
+};
+
 static void ttusb_dec_set_model(struct ttusb_dec *dec,
 				enum ttusb_dec_model model);
 
+static void ttusb_dec_handle_irq( struct urb *urb, struct pt_regs *regs)
+{
+	struct ttusb_dec * dec = urb->context;
+	char *buffer = dec->irq_buffer;
+	int retval;
+	
+	switch(urb->status) {
+		case 0: /*success*/
+			break;
+		case -ECONNRESET:
+		case -ENOENT:
+		case -ESHUTDOWN:
+		case -ETIMEDOUT:
+			/* this urb is dead, cleanup */
+			dprintk("%s:urb shutting down with status: %d\n",
+					__FUNCTION__, urb->status);
+			return;
+		default:
+			dprintk("%s:nonzero status received: %d\n",
+					__FUNCTION__,urb->status);			
+			goto exit;
+	}
+
+	if( (buffer[0] == 0x1) && (buffer[2] == 0x15) )  {        
+		/* IR - Event */
+		/* this is an fact a bit too simple implementation;
+		 * the box also reports a keyrepeat signal
+		 * (with buffer[3] == 0x40) in an intervall of ~100ms. 
+		 * But to handle this correctly we had to imlemenent some
+		 * kind of timer which signals a 'key up' event if no 
+		 * keyrepeat signal is recieved for lets say 200ms.
+		 * this should/could be added later ...
+		 * for now lets report each signal as a key down and up*/
+		dprintk("%s:rc signal:%d\n", __FUNCTION__, buffer[4]);
+		input_report_key(&dec->rc_input_dev,rc_keys[buffer[4]-1],1);
+		input_report_key(&dec->rc_input_dev,rc_keys[buffer[4]-1],0);
+		input_sync(&dec->rc_input_dev);
+	}
+
+exit:	retval = usb_submit_urb(urb, GFP_ATOMIC);
+	if(retval)
+		printk("%s - usb_commit_urb failed with result: %d\n",
+			__FUNCTION__, retval);
+}
+
 static u16 crc16(u16 crc, const u8 *buf, size_t len)
 {
 	u16 tmp;
@@ -1095,6 +1181,30 @@ static void ttusb_dec_init_tasklet(struc
 		     (unsigned long)dec);
 }
 
+static void ttusb_init_rc( struct ttusb_dec *dec)
+{
+	u8 b[] = { 0x00, 0x01 };
+	int i;
+	
+	init_input_dev(&dec->rc_input_dev);
+	
+	dec->rc_input_dev.name = "ttusb_dec remote control";	
+	dec->rc_input_dev.evbit[0] = BIT(EV_KEY);
+	dec->rc_input_dev.keycodesize = sizeof(unsigned char);
+	dec->rc_input_dev.keycodemax = KEY_MAX;
+	
+	 for (i = 0; i < sizeof(rc_keys)/sizeof(rc_keys[0]); i++)
+                set_bit(rc_keys[i], dec->rc_input_dev.keybit);
+                
+	input_register_device(&dec->rc_input_dev);
+	
+	if(usb_submit_urb(dec->irq_urb,GFP_KERNEL)) {
+		printk("%s: usb_submit_urb failed\n",__FUNCTION__);
+	}
+	/* enable irq pipe */
+	ttusb_dec_send_command(dec,0xb0,sizeof(b),b,NULL,NULL);
+}
+
 static void ttusb_dec_init_v_pes(struct ttusb_dec *dec)
 {
 	dprintk("%s\n", __FUNCTION__);
@@ -1105,7 +1215,7 @@ static void ttusb_dec_init_v_pes(struct 
 	dec->v_pes[3] = 0xe0;
 }
 
-static void ttusb_dec_init_usb(struct ttusb_dec *dec)
+static int ttusb_dec_init_usb(struct ttusb_dec *dec)
 {
 	dprintk("%s\n", __FUNCTION__);
 
@@ -1116,8 +1226,26 @@ static void ttusb_dec_init_usb(struct tt
 	dec->result_pipe = usb_rcvbulkpipe(dec->udev, RESULT_PIPE);
 	dec->in_pipe = usb_rcvisocpipe(dec->udev, IN_PIPE);
 	dec->out_pipe = usb_sndisocpipe(dec->udev, OUT_PIPE);
+	dec->irq_pipe = usb_rcvintpipe(dec->udev, IRQ_PIPE);
 
-	ttusb_dec_alloc_iso_urbs(dec);
+	if(enable_rc) {
+		dec->irq_urb = usb_alloc_urb(0, GFP_KERNEL);
+		if(!dec->irq_urb) {
+			return -ENOMEM;
+		}	
+        	dec->irq_buffer = usb_buffer_alloc(dec->udev,IRQ_PACKET_SIZE,
+					SLAB_ATOMIC, &dec->irq_dma_handle);
+		if(!dec->irq_buffer) {
+			return -ENOMEM;
+		}
+		usb_fill_int_urb(dec->irq_urb, dec->udev,dec->irq_pipe,
+				 dec->irq_buffer, IRQ_PACKET_SIZE, 
+				 ttusb_dec_handle_irq, dec, 1);
+		dec->irq_urb->transfer_dma = dec->irq_dma_handle;
+		dec->irq_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+	}
+
+	return ttusb_dec_alloc_iso_urbs(dec);
 }
 
 static int ttusb_dec_boot_dsp(struct ttusb_dec *dec)
@@ -1381,6 +1509,20 @@ static void ttusb_dec_exit_usb(struct tt
 		usb_kill_urb(dec->iso_urb[i]);
 
 	ttusb_dec_free_iso_urbs(dec);
+
+	if(enable_rc) {
+		/* we have to check whether the irq URB is already submitted.
+		 * As the irq is submitted after the interface is changed,
+		 * this is the best method i figured out.
+		 * Any other possibilities?*/
+		if(dec->interface == TTUSB_DEC_INTERFACE_IN) 
+			usb_kill_urb(dec->irq_urb);
+		
+		usb_free_urb(dec->irq_urb);
+		
+		usb_buffer_free(dec->udev,IRQ_PACKET_SIZE,
+					dec->irq_buffer, dec->irq_dma_handle);
+	}
 }
 
 static void ttusb_dec_exit_tasklet(struct ttusb_dec *dec)
@@ -1462,7 +1604,8 @@ static int ttusb_dec_probe(struct usb_in
 
 	dec->udev = udev;
 
-	ttusb_dec_init_usb(dec);
+	if (ttusb_dec_init_usb(dec)) 
+		return 0;
 	if (ttusb_dec_init_stb(dec)) {
 		ttusb_dec_exit_usb(dec);
 		return 0;
@@ -1502,6 +1645,9 @@ static int ttusb_dec_probe(struct usb_in
 
 	ttusb_dec_set_interface(dec, TTUSB_DEC_INTERFACE_IN);
 
+	if(enable_rc)
+		ttusb_init_rc(dec);
+
 	return 0;
 }
 

[Index of Archives]     [Linux Media]     [Video 4 Linux]     [Asterisk]     [Samba]     [Xorg]     [Xfree86]     [Linux USB]

  Powered by Linux