[PATCH 2/2] firedtv: fix remote control input

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

 



and update the scancode-to-keycode mapping to a current model.  Per
default, various media key keycodes are emitted which closely match what
is printed on the remote.  Userland can modify the mapping by means of
evdev ioctls.

The old scancode-to-keycode mapping is left in the driver but cannot be
modified by ioctls.  This preserves status quo for old remotes.

Signed-off-by: Stefan Richter <stefanr at s5r6.in-berlin.de>
---

Dimitry, it would be nice if you could have a quick look if this is
sane.  This was written after I scrolled through input-programming.text
and peeked into some other input drivers without a deeper understanding.

Runtime-tested, except for evdev ioctls.

 drivers/media/dvb/firesat/avc_api.c      |   31 ++---
 drivers/media/dvb/firesat/firesat-rc.c   |  137 ++++++++++++++++++-----
 drivers/media/dvb/firesat/firesat-rc.h   |    6 -
 drivers/media/dvb/firesat/firesat_1394.c |   25 ++--
 4 files changed, 139 insertions(+), 60 deletions(-)

Index: linux/drivers/media/dvb/firesat/avc_api.c
===================================================================
--- linux.orig/drivers/media/dvb/firesat/avc_api.c
+++ linux/drivers/media/dvb/firesat/avc_api.c
@@ -252,27 +252,24 @@ int AVCWrite(struct firesat*firesat, con
 
 int AVCRecv(struct firesat *firesat, u8 *data, size_t length)
 {
-//	printk(KERN_INFO "%s\n",__func__);
-
-	// remote control handling
-
-#if 0
-	AVCRspFrm *RspFrm = (AVCRspFrm*)data;
+	AVCRspFrm *RspFrm = (AVCRspFrm *)data;
 
-	if(/*RspFrm->length >= 8 && ###*/
-			((RspFrm->operand[0] == SFE_VENDOR_DE_COMPANYID_0 &&
-			RspFrm->operand[1] == SFE_VENDOR_DE_COMPANYID_1 &&
-			RspFrm->operand[2] == SFE_VENDOR_DE_COMPANYID_2)) &&
-			RspFrm->operand[3] == SFE_VENDOR_OPCODE_REGISTER_REMOTE_CONTROL) {
-		if(RspFrm->resp == CHANGED) {
-//			printk(KERN_INFO "%s: code = %02x %02x\n",__func__,RspFrm->operand[4],RspFrm->operand[5]);
-			firesat_got_remotecontrolcode((((u16)RspFrm->operand[4]) << 8) | ((u16)RspFrm->operand[5]));
+	if (length >= 8 &&
+	    RspFrm->operand[0] == SFE_VENDOR_DE_COMPANYID_0 &&
+	    RspFrm->operand[1] == SFE_VENDOR_DE_COMPANYID_1 &&
+	    RspFrm->operand[2] == SFE_VENDOR_DE_COMPANYID_2 &&
+	    RspFrm->operand[3] == SFE_VENDOR_OPCODE_REGISTER_REMOTE_CONTROL) {
+		if (RspFrm->resp == CHANGED) {
+			firesat_handle_rc(RspFrm->operand[4] << 8 |
+					  RspFrm->operand[5]);
 			schedule_work(&firesat->remote_ctrl_work);
-		} else if(RspFrm->resp != INTERIM)
-			printk(KERN_INFO "%s: remote control result = %d\n",__func__, RspFrm->resp);
+		} else if (RspFrm->resp != INTERIM) {
+			printk(KERN_INFO "FireDTV: remote control result = "
+			       "%d\n", RspFrm->resp);
+		}
 		return 0;
 	}
-#endif
+
 	if(atomic_read(&firesat->avc_reply_received) == 1) {
 		printk(KERN_ERR "%s: received out-of-order AVC response, "
 		       "ignored\n",__func__);
Index: linux/drivers/media/dvb/firesat/firesat-rc.c
===================================================================
--- linux.orig/drivers/media/dvb/firesat/firesat-rc.c
+++ linux/drivers/media/dvb/firesat/firesat-rc.c
@@ -9,11 +9,18 @@
  *	the License, or (at your option) any later version.
  */
 
+#include <linux/bitops.h>
 #include <linux/input.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
 
 #include "firesat-rc.h"
 
-static u16 firesat_irtable[] = {
+/* fixed table with older keycodes, geared towards MythTV */
+const static u16 oldtable[] = {
+
+	/* code from device: 0x4501...0x451f */
+
 	KEY_ESC,
 	KEY_F9,
 	KEY_1,
@@ -45,50 +52,124 @@ static u16 firesat_irtable[] = {
 	KEY_RIGHT,
 	KEY_P,
 	KEY_M,
+
+	/* code from device: 0x4540...0x4542 */
+
 	KEY_R,
 	KEY_V,
 	KEY_C,
-	0
 };
 
-static struct input_dev firesat_idev;
+/* user-modifiable table for a remote as sold in 2008 */
+static u16 keytable[] = {
 
-int firesat_register_rc(void)
-{
-	int index;
+	/* code from device: 0x0300...0x031f */
 
-	memset(&firesat_idev, 0, sizeof(firesat_idev));
+	[0x00] = KEY_POWER,
+	[0x01] = KEY_SLEEP,
+	[0x02] = KEY_STOP,
+	[0x03] = KEY_OK,
+	[0x04] = KEY_RIGHT,
+	[0x05] = KEY_1,
+	[0x06] = KEY_2,
+	[0x07] = KEY_3,
+	[0x08] = KEY_LEFT,
+	[0x09] = KEY_4,
+	[0x0a] = KEY_5,
+	[0x0b] = KEY_6,
+	[0x0c] = KEY_UP,
+	[0x0d] = KEY_7,
+	[0x0e] = KEY_8,
+	[0x0f] = KEY_9,
+	[0x10] = KEY_DOWN,
+	[0x11] = KEY_TITLE,	/* "OSD" - fixme */
+	[0x12] = KEY_0,
+	[0x13] = KEY_F20,	/* "16:9" - fixme */
+	[0x14] = KEY_SCREEN,	/* "FULL" - fixme */
+	[0x15] = KEY_MUTE,
+	[0x16] = KEY_SUBTITLE,
+	[0x17] = KEY_RECORD,
+	[0x18] = KEY_TEXT,
+	[0x19] = KEY_AUDIO,
+	[0x1a] = KEY_RED,
+	[0x1b] = KEY_PREVIOUS,
+	[0x1c] = KEY_REWIND,
+	[0x1d] = KEY_PLAYPAUSE,
+	[0x1e] = KEY_NEXT,
+	[0x1f] = KEY_VOLUMEUP,
+
+	/* code from device: 0x0340...0x0354 */
+
+	[0x20] = KEY_CHANNELUP,
+	[0x21] = KEY_F21,	/* "4:3" - fixme */
+	[0x22] = KEY_TV,
+	[0x23] = KEY_DVD,
+	[0x24] = KEY_VCR,
+	[0x25] = KEY_AUX,
+	[0x26] = KEY_GREEN,
+	[0x27] = KEY_YELLOW,
+	[0x28] = KEY_BLUE,
+	[0x29] = KEY_CHANNEL,	/* "CH.LIST" */
+	[0x2a] = KEY_VENDOR,	/* "CI" - fixme */
+	[0x2b] = KEY_VOLUMEDOWN,
+	[0x2c] = KEY_CHANNELDOWN,
+	[0x2d] = KEY_LAST,
+	[0x2e] = KEY_INFO,
+	[0x2f] = KEY_FORWARD,
+	[0x30] = KEY_LIST,
+	[0x31] = KEY_FAVORITES,
+	[0x32] = KEY_MENU,
+	[0x33] = KEY_EPG,
+	[0x34] = KEY_EXIT,
+};
+
+static struct input_dev *idev;
 
-	firesat_idev.evbit[0] = BIT(EV_KEY);
+int firesat_register_rc(void)
+{
+	int i, err;
 
-	for (index = 0; firesat_irtable[index] != 0; index++)
-		set_bit(firesat_irtable[index], firesat_idev.keybit);
+	idev = input_allocate_device();
+	if (!idev)
+		return -ENOMEM;
+
+	idev->name = "FireDTV remote control";
+	idev->evbit[0] = BIT_MASK(EV_KEY);
+	idev->keycode = keytable;
+	idev->keycodesize = sizeof(keytable[0]);
+	idev->keycodemax = ARRAY_SIZE(keytable);
+
+	for (i = 0; i < ARRAY_SIZE(keytable); i++)
+		set_bit(keytable[i], idev->keybit);
+
+	err = input_register_device(idev);
+	if (err)
+		input_free_device(idev);
 
-	return input_register_device(&firesat_idev);
+	return err;
 }
 
-int firesat_unregister_rc(void)
+void firesat_unregister_rc(void)
 {
-	input_unregister_device(&firesat_idev);
-	return 0;
+	input_unregister_device(idev);
 }
 
-int firesat_got_remotecontrolcode(u16 code)
+void firesat_handle_rc(unsigned int code)
 {
-	u16 keycode;
-
-	if (code > 0x4500 && code < 0x4520)
-		keycode = firesat_irtable[code - 0x4501];
-	else if (code > 0x453f && code < 0x4543)
-		keycode = firesat_irtable[code - 0x4521];
+	if (code >= 0x0300 && code <= 0x031f)
+		code = keytable[code - 0x0300];
+	else if (code >= 0x0340 && code <= 0x0354)
+		code = keytable[code - 0x0320];
+	else if (code >= 0x4501 && code <= 0x451f)
+		code = oldtable[code - 0x4501];
+	else if (code >= 0x4540 && code <= 0x4542)
+		code = oldtable[code - 0x4521];
 	else {
-		printk(KERN_DEBUG "%s: invalid key code 0x%04x\n", __func__,
-		       code);
-		return -EINVAL;
+		printk(KERN_DEBUG "FireDTV: invalid key code 0x%04x "
+		       "from remote control\n", code);
+		return;
 	}
 
-	input_report_key(&firesat_idev, keycode, 1);
-	input_report_key(&firesat_idev, keycode, 0);
-
-	return 0;
+	input_report_key(idev, code, 1);
+	input_report_key(idev, code, 0);
 }
Index: linux/drivers/media/dvb/firesat/firesat-rc.h
===================================================================
--- linux.orig/drivers/media/dvb/firesat/firesat-rc.h
+++ linux/drivers/media/dvb/firesat/firesat-rc.h
@@ -1,10 +1,8 @@
 #ifndef _FIREDTV_RC_H
 #define _FIREDTV_RC_H
 
-#include <linux/types.h>
-
 int firesat_register_rc(void);
-int firesat_unregister_rc(void);
-int firesat_got_remotecontrolcode(u16 code);
+void firesat_unregister_rc(void);
+void firesat_handle_rc(unsigned int code);
 
 #endif /* _FIREDTV_RC_H */
Index: linux/drivers/media/dvb/firesat/firesat_1394.c
===================================================================
--- linux.orig/drivers/media/dvb/firesat/firesat_1394.c
+++ linux/drivers/media/dvb/firesat/firesat_1394.c
@@ -237,9 +237,8 @@ static int firesat_probe(struct device *
 		firesats[subunit] = firesat;
 	} // loop for all tuners
 
-	//beta ;-) Disable remote control stuff to avoid crashing
-	//if(firesats[0])
-	//	AVCRegisterRemoteControl(firesats[0]);
+	if (firesats[0])
+		AVCRegisterRemoteControl(firesats[0]);
 
     return 0;
 }
@@ -325,28 +324,32 @@ static int __init firesat_init(void)
 {
 	int ret;
 
-	printk(KERN_INFO "FireDTV loaded\n");
 	hpsb_register_highlevel(&firesat_highlevel);
 	ret = hpsb_register_protocol(&firesat_driver);
 	if (ret) {
 		printk(KERN_ERR "FireDTV: failed to register protocol\n");
-		hpsb_unregister_highlevel(&firesat_highlevel);
-		return ret;
+		goto fail;
 	}
 
-	//Crash in this function, just disable RC for the time being...
-	//Don't forget to uncomment in firesat_exit and firesat_probe when you enable this.
-	/*if((ret=firesat_register_rc()))
-		printk("%s: firesat_register_rc return error code %d (ignored)\n", __func__, ret);*/
+	ret = firesat_register_rc();
+	if (ret) {
+		printk(KERN_ERR "FireDTV: failed to register input device\n");
+		goto fail_rc;
+	}
 
 	return 0;
+fail_rc:
+	hpsb_unregister_protocol(&firesat_driver);
+fail:
+	hpsb_unregister_highlevel(&firesat_highlevel);
+	return ret;
 }
 
 static void __exit firesat_exit(void)
 {
+	firesat_unregister_rc();
 	hpsb_unregister_protocol(&firesat_driver);
 	hpsb_unregister_highlevel(&firesat_highlevel);
-	printk(KERN_INFO "FireDTV quit\n");
 }
 
 module_init(firesat_init);

-- 
Stefan Richter
-=====-==--- =--- ==-=-
http://arcgraph.de/sr/



[Index of Archives]     [Linux Driver Backports]     [DMA Engine]     [Linux GPIO]     [Linux SPI]     [Video for Linux]     [Linux USB Devel]     [Linux Coverity]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]
  Powered by Linux