linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [RFC PATCH 0/4] V3 - Implementation of IR support using the input subsystem
@ 2008-10-06 19:43 Jon Smirl
  2008-10-06 19:43 ` [RFC PATCH 1/4] Changes to core input subsystem to allow send and receive of IR messages. Encode and decode state machines are provided for common IR porotocols such as Sony, JVC, NEC, Philips, etc Jon Smirl
                   ` (6 more replies)
  0 siblings, 7 replies; 16+ messages in thread
From: Jon Smirl @ 2008-10-06 19:43 UTC (permalink / raw)
  To: linux-kernel, lirc-list

Third pass at implementing evdev support for IR. The goal of in-kernel IR is to integrate IR events into the evdev input event queue and maintain ordering of events from all input devices.

New feature, raw mode. There are three sysfs attributes - ir_raw, ir_carrier, ir_xmitter. Read from ir_raw to get the raw timing data from the IR device. Set carrier and active xmitters and then copy raw data to ir_raw to send. These attributes may be better on a debug switch. You would use raw mode when decoding a new protocol. After you figure out the new protocol, write an in-kernel encoder/decoder for it.

What should the IR API look like? 

How are IR events mapped into keyboard events? Should they be mapped? Map them in the kernel or in user space? The maps are tiny, less than 1K per remote.  Sysfs can be used to load maps into the kernel driver. Make maps only for the common buttons and don't map unusual ones?

How should multiple remotes be handled? Split them out into individual input devices, or group them onto a single IR device? I can implement either.

The in-kernel code is tiny, about 20K including a driver.

>From last post...
Note that user space IR device drivers can use the existing support in evdev to inject events into the input queue.

Send and receive are implemented. Received IR messages are decoded and sent to user space as input messages. Send is done via an IOCTL on the input device.

Two drivers are supplied. mceusb2 implements send and receive support for the Microsoft USB IR dongle.

The GPT driver implements receive only support for a GPT pin - GPT is a GPIO with a timer attached.

Code is only lightly tested. Encoders and decoders have not been written for all protocols. 
Repeat is not handled for any protocol.
I'm looking for help. There are 15 more existing LIRC drivers.

---

Jon Smirl (4):
      Microsoft mceusb2 driver for in-kernel IR subsystem
      Example of PowerPC device tree support for GPT based IR
      GPT driver for in-kernel IR support.
      Changes to core input subsystem to allow send and receive of IR messages. Encode and decode state machines are provided for common IR porotocols such as Sony, JVC, NEC, Philips, etc.


 arch/powerpc/boot/dts/dspeak01.dts |   19 -
 drivers/input/Kconfig              |    2 
 drivers/input/Makefile             |    3 
 drivers/input/evdev.c              |   55 +++
 drivers/input/input.c              |   21 +
 drivers/input/ir-core.c            |  659 ++++++++++++++++++++++++++++++++
 drivers/input/ir/Kconfig           |   26 +
 drivers/input/ir/Makefile          |    7 
 drivers/input/ir/ir-gpt.c          |  221 +++++++++++
 drivers/input/ir/ir-mceusb2.c      |  741 ++++++++++++++++++++++++++++++++++++
 include/linux/input.h              |  101 +++++
 include/linux/mod_devicetable.h    |    3 
 12 files changed, 1846 insertions(+), 12 deletions(-)
 create mode 100644 drivers/input/ir-core.c
 create mode 100644 drivers/input/ir/Kconfig
 create mode 100644 drivers/input/ir/Makefile
 create mode 100644 drivers/input/ir/ir-gpt.c
 create mode 100644 drivers/input/ir/ir-mceusb2.c

-- 
Jon Smirl
jonsmirl@gmail.com

^ permalink raw reply	[flat|nested] 16+ messages in thread

* [RFC PATCH 1/4] Changes to core input subsystem to allow send and receive of IR messages. Encode and decode state machines are provided for common IR porotocols such as Sony, JVC, NEC, Philips, etc.
  2008-10-06 19:43 [RFC PATCH 0/4] V3 - Implementation of IR support using the input subsystem Jon Smirl
@ 2008-10-06 19:43 ` Jon Smirl
  2009-05-28 23:23   ` Maxim Levitsky
  2008-10-06 19:43 ` [RFC PATCH 2/4] GPT driver for in-kernel IR support Jon Smirl
                   ` (5 subsequent siblings)
  6 siblings, 1 reply; 16+ messages in thread
From: Jon Smirl @ 2008-10-06 19:43 UTC (permalink / raw)
  To: linux-kernel, lirc-list

Received IR messages generate event in the input queue.
IR messages are sent using an input IOCTL.

Jon Smirl
<jonsmirl@gmail.com>
---
 drivers/input/Kconfig           |    2 
 drivers/input/Makefile          |    3 
 drivers/input/evdev.c           |   55 +++
 drivers/input/input.c           |   21 +
 drivers/input/ir-core.c         |  659 +++++++++++++++++++++++++++++++++++++++
 drivers/input/ir/Kconfig        |   14 +
 drivers/input/ir/Makefile       |    5 
 include/linux/input.h           |  101 ++++++
 include/linux/mod_devicetable.h |    3 
 9 files changed, 862 insertions(+), 1 deletions(-)
 create mode 100644 drivers/input/ir-core.c
 create mode 100644 drivers/input/ir/Kconfig
 create mode 100644 drivers/input/ir/Makefile

diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig
index 747633c..780d321 100644
--- a/drivers/input/Kconfig
+++ b/drivers/input/Kconfig
@@ -172,6 +172,8 @@ source "drivers/input/touchscreen/Kconfig"
 
 source "drivers/input/misc/Kconfig"
 
+source "drivers/input/ir/Kconfig"
+
 endif
 
 menu "Hardware I/O ports"
diff --git a/drivers/input/Makefile b/drivers/input/Makefile
index 6a1049b..da47340 100644
--- a/drivers/input/Makefile
+++ b/drivers/input/Makefile
@@ -5,7 +5,7 @@
 # Each configuration option enables a list of files.
 
 obj-$(CONFIG_INPUT)		+= input-core.o
-input-core-objs := input.o ff-core.o
+input-core-objs := input.o ff-core.o ir-core.o
 
 obj-$(CONFIG_INPUT_FF_MEMLESS)	+= ff-memless.o
 obj-$(CONFIG_INPUT_POLLDEV)	+= input-polldev.o
@@ -21,6 +21,7 @@ obj-$(CONFIG_INPUT_JOYSTICK)	+= joystick/
 obj-$(CONFIG_INPUT_TABLET)	+= tablet/
 obj-$(CONFIG_INPUT_TOUCHSCREEN)	+= touchscreen/
 obj-$(CONFIG_INPUT_MISC)	+= misc/
+obj-$(CONFIG_INPUT_IR)		+= ir/
 
 obj-$(CONFIG_INPUT_APMPOWER)	+= apm-power.o
 
diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c
index 3524bef..7a3f935 100644
--- a/drivers/input/evdev.c
+++ b/drivers/input/evdev.c
@@ -329,6 +329,14 @@ struct ff_effect_compat {
 	} u;
 };
 
+struct ir_command_compat {
+	__u32 protocol;
+	__u32 device;
+	__u32 command;
+	__u32 transmitters;
+};
+
+
 /* Note to the author of this code: did it ever occur to
    you why the ifdefs are needed? Think about it again. -AK */
 #ifdef CONFIG_X86_64
@@ -433,6 +441,32 @@ static int evdev_ff_effect_from_user(const char __user *buffer, size_t size,
 	return 0;
 }
 
+static int evdev_ir_send_from_user(const char __user *buffer, size_t size,
+				     struct ir_command *ir_command)
+{
+	if (COMPAT_TEST) {
+		struct ir_command_compat *compat_ir_command;
+
+		if (size != sizeof(struct ir_command_compat))
+			return -EINVAL;
+
+		compat_ir_command = (struct ir_command_compat *)ir_command;
+
+		if (copy_from_user(compat_ir_command, buffer,
+				   sizeof(struct ir_command_compat)))
+			return -EFAULT;
+
+	} else {
+		if (size != sizeof(struct ir_command))
+			return -EINVAL;
+
+		if (copy_from_user(ir_command, buffer, sizeof(struct ir_command)))
+			return -EFAULT;
+	}
+
+	return 0;
+}
+
 #else
 
 static inline size_t evdev_event_size(void)
@@ -470,6 +504,18 @@ static int evdev_ff_effect_from_user(const char __user *buffer, size_t size,
 	return 0;
 }
 
+static int evdev_ir_send_from_user(const char __user *buffer, size_t size,
+				     struct ir_command *ir_command)
+{
+	if (size != sizeof(struct ir_command))
+		return -EINVAL;
+
+	if (copy_from_user(ir_command, buffer, sizeof(struct ir_command)))
+		return -EFAULT;
+
+	return 0;
+}
+
 #endif /* CONFIG_COMPAT */
 
 static ssize_t evdev_write(struct file *file, const char __user *buffer,
@@ -696,6 +742,7 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd,
 	struct input_dev *dev = evdev->handle.dev;
 	struct input_absinfo abs;
 	struct ff_effect effect;
+	struct ir_command ir_command;
 	int __user *ip = (int __user *)p;
 	int i, t, u, v;
 	int error;
@@ -860,6 +907,14 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd,
 
 				return 0;
 			}
+
+			if (_IOC_NR(cmd) == _IOC_NR(EVIOIRSEND)) {
+
+				if (evdev_ir_send_from_user(p, _IOC_SIZE(cmd), &ir_command))
+					return -EFAULT;
+
+				return input_ir_send(dev, &ir_command, file);
+			}
 		}
 	}
 	return -EINVAL;
diff --git a/drivers/input/input.c b/drivers/input/input.c
index c13ced3..18f36d7 100644
--- a/drivers/input/input.c
+++ b/drivers/input/input.c
@@ -240,6 +240,10 @@ static void input_handle_event(struct input_dev *dev,
 	case EV_PWR:
 		disposition = INPUT_PASS_TO_ALL;
 		break;
+
+	case EV_IR:
+		disposition = INPUT_PASS_TO_ALL;
+		break;
 	}
 
 	if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN)
@@ -693,6 +697,7 @@ static const struct input_device_id *input_match_device(const struct input_devic
 		MATCH_BIT(sndbit, SND_MAX);
 		MATCH_BIT(ffbit,  FF_MAX);
 		MATCH_BIT(swbit,  SW_MAX);
+		MATCH_BIT(irbit,  IR_MAX);
 
 		return id;
 	}
@@ -815,6 +820,8 @@ static int input_devices_seq_show(struct seq_file *seq, void *v)
 		input_seq_print_bitmap(seq, "FF", dev->ffbit, FF_MAX);
 	if (test_bit(EV_SW, dev->evbit))
 		input_seq_print_bitmap(seq, "SW", dev->swbit, SW_MAX);
+	if (test_bit(EV_IR, dev->evbit))
+		input_seq_print_bitmap(seq, "IR", dev->irbit, IR_MAX);
 
 	seq_putc(seq, '\n');
 
@@ -992,6 +999,8 @@ static int input_print_modalias(char *buf, int size, struct input_dev *id,
 				'f', id->ffbit, 0, FF_MAX);
 	len += input_print_modalias_bits(buf + len, size - len,
 				'w', id->swbit, 0, SW_MAX);
+	len += input_print_modalias_bits(buf + len, size - len,
+				'i', id->irbit, 0, IR_MAX);
 
 	if (add_cr)
 		len += snprintf(buf + len, max(size - len, 0), "\n");
@@ -1093,6 +1102,7 @@ INPUT_DEV_CAP_ATTR(LED, led);
 INPUT_DEV_CAP_ATTR(SND, snd);
 INPUT_DEV_CAP_ATTR(FF, ff);
 INPUT_DEV_CAP_ATTR(SW, sw);
+INPUT_DEV_CAP_ATTR(IR, ir);
 
 static struct attribute *input_dev_caps_attrs[] = {
 	&dev_attr_ev.attr,
@@ -1104,6 +1114,7 @@ static struct attribute *input_dev_caps_attrs[] = {
 	&dev_attr_snd.attr,
 	&dev_attr_ff.attr,
 	&dev_attr_sw.attr,
+	&dev_attr_ir.attr,
 	NULL
 };
 
@@ -1221,6 +1232,8 @@ static int input_dev_uevent(struct device *device, struct kobj_uevent_env *env)
 		INPUT_ADD_HOTPLUG_BM_VAR("FF=", dev->ffbit, FF_MAX);
 	if (test_bit(EV_SW, dev->evbit))
 		INPUT_ADD_HOTPLUG_BM_VAR("SW=", dev->swbit, SW_MAX);
+	if (test_bit(EV_IR, dev->evbit))
+		INPUT_ADD_HOTPLUG_BM_VAR("IR=", dev->irbit, IR_MAX);
 
 	INPUT_ADD_HOTPLUG_MODALIAS_VAR(dev);
 
@@ -1333,6 +1346,10 @@ void input_set_capability(struct input_dev *dev, unsigned int type, unsigned int
 		__set_bit(code, dev->ffbit);
 		break;
 
+	case EV_IR:
+		__set_bit(code, dev->irbit);
+		break;
+
 	case EV_PWR:
 		/* do nothing */
 		break;
@@ -1396,6 +1413,10 @@ int input_register_device(struct input_dev *dev)
 	if (error)
 		return error;
 
+	error = input_ir_register(dev);
+	if (error)
+		return error;
+
 	path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL);
 	printk(KERN_INFO "input: %s as %s\n",
 		dev->name ? dev->name : "Unspecified device", path ? path : "N/A");
diff --git a/drivers/input/ir-core.c b/drivers/input/ir-core.c
new file mode 100644
index 0000000..8efcfa7
--- /dev/null
+++ b/drivers/input/ir-core.c
@@ -0,0 +1,659 @@
+/*
+ * Core routines for IR support
+ *
+ * Copyright (C) 2008 Jon Smirl <jonsmirl@gmail.com>
+ */
+
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/input.h>
+
+#undef IR_PROTOCOL_DEBUG
+#ifdef IR_PROTOCOL_DEBUG
+#define PDEBUG( format, arg... ) \
+	printk(KERN_DEBUG format , ## arg);
+#else
+#define PDEBUG(format, arg...) \
+	({ if (0) printk(KERN_DEBUG format , ## arg); 0; })
+#endif
+
+static int encode_sony(struct ir_device *ir, struct ir_command *command)
+{
+	/* Sony SIRC IR code */
+	/* http://www.sbprojects.com/knowledge/ir/sirc.htm */
+	int i, bit, dev, cmd, total;
+
+	ir->send.count = 0;
+	switch (command->protocol) {
+	case IR_PROTOCOL_SONY_20:
+		dev = 10; cmd = 10; break;
+	case IR_PROTOCOL_SONY_15:
+		dev = 8; cmd = 7; break;
+	default:
+	case IR_PROTOCOL_SONY_12:
+		dev = 5; cmd = 7; break;
+	}
+	ir->send.buffer[ir->send.count++] = 2400;
+	ir->send.buffer[ir->send.count++] = 600;
+
+	for (i = 0; i < cmd; i++) {
+		bit = command->command & 1;
+		command->command >>= 1;
+		ir->send.buffer[ir->send.count++] = (bit ? 1200 : 600);
+		ir->send.buffer[ir->send.count++] = 600;
+	}
+	for (i = 0; i < dev; i++) {
+		bit = command->device & 1;
+		command->device >>= 1;
+		ir->send.buffer[ir->send.count++] = (bit ? 1200 : 600);
+		ir->send.buffer[ir->send.count++] = 600;
+	}
+	total = 0;
+	for (i = 0; i < ir->send.count; i++)
+		total += ir->send.buffer[i];
+	ir->send.buffer[ir->send.count++] = 45000 - total;
+
+	memcpy(&ir->send.buffer[ir->send.count], &ir->send.buffer[0], ir->send.count * sizeof ir->send.buffer[0]);
+	ir->send.count += ir->send.count;
+	memcpy(&ir->send.buffer[ir->send.count], &ir->send.buffer[0], ir->send.count * sizeof ir->send.buffer[0]);
+	ir->send.count += ir->send.count;
+
+	return 0;
+}
+
+static int decode_sony(struct input_dev *dev, struct ir_protocol *sony, unsigned int d, unsigned int bit)
+{
+	/* Sony SIRC IR code */
+	/* http://www.sbprojects.com/knowledge/ir/sirc.htm */
+	/* based on a 600us cadence */
+	int ret = 0, delta = d;
+
+	delta = (delta + 300) / 600;
+
+	if ((bit == 0) && (delta > 22)) {
+		PDEBUG("SIRC state 1\n");
+		if ((sony->state == 26) || (sony->state == 32) || (sony->state == 42)) {
+			if (sony->good && (sony->good == sony->code)) {
+
+				input_report_ir(dev, IR_PROTOCOL, (sony->state == 26) ? IR_PROTOCOL_SONY_12 :
+						(sony->state == 32) ? IR_PROTOCOL_SONY_15 : IR_PROTOCOL_SONY_20);
+
+				if (sony->state == 26) {
+					input_report_ir(dev, IR_DEVICE, sony->code & 0x1F);
+					input_report_ir(dev, IR_COMMAND, sony->code >> 5);
+				} else {
+					input_report_ir(dev, IR_DEVICE, sony->code & 0xFF);
+					input_report_ir(dev, IR_COMMAND, sony->code >> 8);
+				}
+				input_sync(dev);
+
+				sony->good = 0;
+				ret = 1;
+			} else {
+				PDEBUG("SIRC - Saving %d bit %05x\n", (sony->state - 2) / 2, sony->code);
+				sony->good = sony->code;
+			}
+		}
+		sony->state = 1;
+		sony->code = 0;
+		return ret;
+	}
+	if ((sony->state == 1) && (bit == 1) && (delta == 4)) {
+		sony->state = 2;
+		PDEBUG("SIRC state 2\n");
+		return 0;
+	}
+	if ((sony->state == 2) && (bit == 0) && (delta == 1)) {
+		sony->state = 3;
+		PDEBUG("SIRC state 3\n");
+		return 0;
+	}
+	if ((sony->state >= 3) && (sony->state & 1) && (bit == 1) && ((delta == 1) || (delta == 2))) {
+		sony->state++;
+		sony->code |= ((delta - 1) << ((sony->state - 4) / 2));
+		PDEBUG("SIRC state %d bit %d\n", sony->state, delta - 1);
+		return 0;
+	}
+	if ((sony->state >= 3) && !(sony->state & 1) && (bit == 0) && (delta == 1)) {
+		sony->state++;
+		PDEBUG("SIRC state %d\n", sony-> state);
+		return 0;
+	}
+	sony->state = 0;
+	return 0;
+}
+
+
+static int encode_jvc(struct ir_device *ir, struct ir_command *command)
+{
+	/* JVC IR code */
+	/* http://www.sbprojects.com/knowledge/ir/jvc.htm */
+	int i, bit, total;
+
+	ir->send.count = 0;
+
+	ir->send.buffer[ir->send.count++] = 8400;
+	ir->send.buffer[ir->send.count++] = 4200;
+
+	for (i = 0; i < 8; i++) {
+		bit = command->device & 1;
+		command->device >>= 1;
+		ir->send.buffer[ir->send.count++] = 525;
+		ir->send.buffer[ir->send.count++] = (bit ? 1575 : 525);
+	}
+	for (i = 0; i < 8; i++) {
+		bit = command->command & 1;
+		command->command >>= 1;
+		ir->send.buffer[ir->send.count++] = 525;
+		ir->send.buffer[ir->send.count++] = (bit ? 1575 : 525);
+	}
+	ir->send.buffer[ir->send.count++] = 525;
+
+	total = 0;
+	for (i = 0; i < ir->send.count; i++)
+		total += ir->send.buffer[i];
+	ir->send.buffer[ir->send.count] = 55000 - total;
+
+	return 0;
+}
+
+static int decode_jvc(struct input_dev *dev, struct ir_protocol *jvc, unsigned int d, unsigned int bit)
+{
+	/* JVC IR code */
+	/* http://www.sbprojects.com/knowledge/ir/jvc.htm */
+	/* based on a 525us cadence */
+	int ret = 0, delta = d;
+
+	delta = (delta + 263) / 525;
+
+	if ((bit == 0) && (delta > 22)) {
+		PDEBUG("JVC state 1\n");
+		jvc->state = 1;
+		jvc->code = 0;
+		return ret;
+	}
+	if ((jvc->state == 1) && (bit == 1) && (delta == 16)) {
+		jvc->state = 2;
+		PDEBUG("JVC state 2\n");
+		return 0;
+	}
+	if ((jvc->state == 2) && (bit == 0) && (delta == 8)) {
+		jvc->state = 3;
+		PDEBUG("JVC state 3\n");
+		return 0;
+	}
+	if ((jvc->state >= 3) && (jvc->state & 1) && (bit == 1) && (delta == 1)) {
+		jvc->state++;
+		PDEBUG("JVC state %d\n", jvc-> state);
+		return 0;
+	}
+	if ((jvc->state >= 3) && !(jvc->state & 1) && (bit == 0) && ((delta == 1) || (delta == 3))) {
+		if (delta == 3)
+			jvc->code |= 1 << ((jvc->state - 4) / 2);
+		jvc->state++;
+		PDEBUG("JVC state %d bit %d\n", jvc->state, delta - 1);
+		if (jvc->state == 34) {
+			jvc->state = 3;
+			if (jvc->good && (jvc->good == jvc->code)) {
+				input_report_ir(dev, IR_PROTOCOL, IR_PROTOCOL_JVC);
+				input_report_ir(dev, IR_DEVICE, jvc->code >> 8);
+				input_report_ir(dev, IR_COMMAND, jvc->code & 0xFF);
+				input_sync(dev);
+				jvc->good = 0;
+				ret = 1;
+			} else {
+				PDEBUG("JVC - Saving 16 bit %05x\n", jvc->code);
+				jvc->good = jvc->code;
+			}
+			jvc->code = 0;
+		}
+		return 0;
+	}
+	jvc->state = 0;
+	return 0;
+}
+
+
+static int encode_nec(struct ir_device *ir, struct ir_command *command)
+{
+	/* NEC IR code */
+	/* http://www.sbprojects.com/knowledge/ir/nec.htm */
+	int i, bit, total;
+
+	ir->send.count = 0;
+
+	ir->send.buffer[ir->send.count++] = 9000;
+	ir->send.buffer[ir->send.count++] = 4500;
+
+	for (i = 0; i < 8; i++) {
+		bit = command->device & 1;
+		command->device >>= 1;
+		ir->send.buffer[ir->send.count++] = 563;
+		ir->send.buffer[ir->send.count++] = (bit ? 1687 : 562);
+	}
+	for (i = 0; i < 8; i++) {
+		bit = command->command & 1;
+		command->command >>= 1;
+		ir->send.buffer[ir->send.count++] = 563;
+		ir->send.buffer[ir->send.count++] = (bit ? 1687 : 562);
+	}
+	ir->send.buffer[ir->send.count++] = 562;
+
+	total = 0;
+	for (i = 0; i < ir->send.count; i++)
+		total += ir->send.buffer[i];
+	ir->send.buffer[ir->send.count] = 110000 - total;
+
+	return 0;
+}
+
+static int decode_nec(struct input_dev *dev, struct ir_protocol *nec, unsigned int d, unsigned int bit)
+{
+	/* NEC IR code */
+	/* http://www.sbprojects.com/knowledge/ir/nec.htm */
+	/* based on a 560us cadence */
+	int delta = d;
+
+	delta = (delta + 280) / 560;
+
+	if ((bit == 0) && (delta > 22)) {
+		PDEBUG("nec state 1\n");
+		nec->state = 1;
+		nec->code = 0;
+		return 0;
+	}
+	if ((nec->state == 1) && (bit == 1) && (delta == 16)) {
+		nec->state = 2;
+		PDEBUG("nec state 2\n");
+		return 0;
+	}
+	if ((nec->state == 2) && (bit == 0) && (delta == 8)) {
+		nec->state = 3;
+		PDEBUG("nec state 3\n");
+		return 0;
+	}
+	if ((nec->state >= 3) && (nec->state & 1) && (bit == 1) && (delta == 1)) {
+		nec->state++;
+		PDEBUG("nec state %d\n", nec-> state);
+		if (nec->state == 68) {
+			input_report_ir(dev, IR_PROTOCOL, IR_PROTOCOL_NEC);
+			input_report_ir(dev, IR_DEVICE, nec->code >> 16);
+			input_report_ir(dev, IR_COMMAND, nec->code & 0xFFFF);
+			input_sync(dev);
+			return 1;
+		}
+		return 0;
+	}
+	if ((nec->state >= 3) && !(nec->state & 1) && (bit == 0) && ((delta == 1) || (delta == 3))) {
+		if (delta == 3)
+			nec->code |= 1 << ((nec->state - 4) / 2);
+		nec->state++;
+		PDEBUG("nec state %d bit %d\n", nec->state, delta - 1);
+		return 0;
+	}
+	nec->state = 0;
+	nec->code = 0;
+	return 0;
+}
+
+
+static int encode_rc5(struct ir_device *ir, struct ir_command *command)
+{
+	/* Philips RC-5 IR code */
+	/* http://www.sbprojects.com/knowledge/ir/rc5.htm */
+	return 0;
+}
+
+static int decode_rc5(struct input_dev *dev, struct ir_protocol *rc5, unsigned int d, unsigned int bit)
+{
+	/* Philips RC-5 IR code */
+	/* http://www.sbprojects.com/knowledge/ir/rc5.htm */
+	/* based on a 889us cadence */
+	int delta = d;
+
+	delta = (delta + 444) / 889;
+
+	return 0;
+}
+
+
+static int encode_rc6(struct ir_device *ir, struct ir_command *command)
+{
+	/* Philips RC-6 IR code */
+	/* http://www.sbprojects.com/knowledge/ir/rc6.htm */
+	int i, bit, last;
+
+	ir->send.count = 0;
+
+	ir->send.buffer[ir->send.count++] = 2666;
+	ir->send.buffer[ir->send.count++] = 889;
+
+	ir->send.buffer[ir->send.count++] = 444;
+	ir->send.buffer[ir->send.count++] = 444;
+
+	last = 1;
+	for (i = 0; i < 8; i++) {
+		bit = command->device & 1;
+		command->device >>= 1;
+
+		if (last != bit)
+			ir->send.buffer[ir->send.count - 1] += 444;
+		else
+			ir->send.buffer[ir->send.count++] = 444;
+		ir->send.buffer[ir->send.count++] = 444;
+		last = bit;
+	}
+	for (i = 0; i < 8; i++) {
+		bit = command->command & 1;
+		command->command >>= 1;
+
+		if (last != bit)
+			ir->send.buffer[ir->send.count - 1] += 444;
+		else
+			ir->send.buffer[ir->send.count++] = 444;
+		ir->send.buffer[ir->send.count++] = 444;
+		last = bit;
+	}
+	ir->send.buffer[ir->send.count] = 2666;
+
+	return 0;
+}
+
+static void decode_rc6_bit(struct input_dev *dev, struct ir_protocol *rc6, unsigned int bit)
+{
+	/* bits come in one at a time */
+	/* when two are collected look for a symbol */
+	/* rc6->bits == 1 is a zero symbol */
+	/* rc6->bits == 2 is a one symbol */
+	rc6->count++;
+	rc6->bits <<= 1;
+	rc6->bits |= bit;
+	if (rc6->count == 2) {
+		if ((rc6->bits == 0) || (rc6->bits == 3)) {
+			rc6->mode = rc6->code;
+			rc6->code = 0;
+		} else {
+			rc6->code <<= 1;
+			if (rc6->bits == 2)
+				rc6->code |= 1;
+		}
+		rc6->count = 0;
+		if (rc6->state == 23) {
+			input_report_ir(dev, IR_PROTOCOL, IR_PROTOCOL_PHILIPS_RC6);
+			input_report_ir(dev, IR_DEVICE, rc6->code >> 8);
+			input_report_ir(dev, IR_COMMAND, rc6->code & 0xFF);
+			input_sync(dev);
+			rc6->state = 0;
+		} else
+			rc6->state++;
+		PDEBUG("rc6 state %d bit %d\n", rc6->state, rc6->bits == 2);
+		rc6->bits = 0;
+	}
+}
+
+static int decode_rc6(struct input_dev *dev, struct ir_protocol *rc6, unsigned int d, unsigned int bit)
+{
+	/* Philips RC-6 IR code */
+	/* http://www.sbprojects.com/knowledge/ir/rc6.htm */
+	/* based on a 444us cadence */
+
+	int delta = d;
+
+	delta = (delta + 222) / 444;
+
+	if ((bit == 0) && (delta > 19)) {
+		rc6->count = 0;
+		rc6->bits = 0;
+		rc6->state = 1;
+		rc6->code = 0;
+		PDEBUG("rc6 state 1\n");
+		return 0;
+	}
+	if ((rc6->state == 1) && (bit == 1) && (delta == 6)) {
+		rc6->state = 2;
+		PDEBUG("rc6 state 2\n");
+		return 0;
+	}
+	if ((rc6->state == 2) && (bit == 0) && (delta == 2)) {
+		rc6->state = 3;
+		PDEBUG("rc6 state 3\n");
+		return 0;
+	}
+	if (rc6->state >= 3) {
+		if ((delta >= 1) || (delta <= 3)) {
+			while (delta-- >= 1)
+				decode_rc6_bit(dev, rc6, bit);
+			return 0;
+		}
+	}
+	rc6->state = 0;
+	rc6->code = 0;
+	return 0;
+}
+
+static void record_raw(struct input_dev *dev, unsigned int delta, unsigned int bit)
+{
+	int head = dev->ir->raw.head;
+	if (bit)
+		delta = -delta;
+
+	head += 1;
+	if (head > sizeof dev->ir->raw.buffer)
+		head = 0;
+
+	if (head != dev->ir->raw.tail) {
+		dev->ir->raw.buffer[dev->ir->raw.head] = delta;
+		dev->ir->raw.head = head;
+	}
+}
+
+void input_ir_decode(struct input_dev *dev, unsigned int delta, unsigned int bit)
+{
+	PDEBUG("IR bit %d %d\n", delta, bit);
+	record_raw(dev, delta, bit);
+	decode_sony(dev, &dev->ir->sony, delta, bit);
+	decode_jvc(dev, &dev->ir->jvc, delta, bit);
+	decode_nec(dev, &dev->ir->nec, delta, bit);
+	decode_rc5(dev, &dev->ir->rc5, delta, bit);
+	decode_rc6(dev, &dev->ir->rc6, delta, bit);
+}
+EXPORT_SYMBOL_GPL(input_ir_decode);
+
+static ssize_t ir_raw_show(struct device *dev,
+				 struct device_attribute *attr, char *buf)
+{
+	struct input_dev *input_dev = to_input_dev(dev);
+	unsigned int i = input_dev->ir->raw.tail;
+	unsigned int count = 0;
+
+	printk("head %d tail %d\n", input_dev->ir->raw.head, input_dev->ir->raw.tail);
+	while (i != input_dev->ir->raw.head) {
+		count += snprintf(&buf[count], PAGE_SIZE, "%i\n", input_dev->ir->raw.buffer[i++]);
+		if (count >= PAGE_SIZE) {
+			input_dev->ir->raw.tail = i;
+			return PAGE_SIZE;
+		}
+		if (i > sizeof input_dev->ir->raw.buffer)
+			i = 0;
+	}
+	input_dev->ir->raw.tail = i;
+	return count;
+}
+
+static ssize_t ir_raw_store(struct device *dev,
+				  struct device_attribute *attr,
+				  const char *buf,
+				  size_t count)
+{
+	struct ir_device *ir = to_input_dev(dev)->ir;
+	long delta;
+	int i = count;
+	int first = 0;
+
+	if (!ir->xmit)
+		return count;
+	ir->send.count = 0;
+
+	while (i > 0) {
+		i -= strict_strtoul(&buf[i], i, &delta);
+		while ((buf[i] != '\n') && (i > 0))
+			i--;
+		i--;
+		/* skip leading zeros */
+		if ((delta > 0) && !first)
+			continue;
+
+		ir->send.buffer[ir->send.count++] = abs(delta);
+	}
+
+	ir->xmit(ir->private, ir->send.buffer, ir->send.count, ir->raw.carrier, ir->raw.xmitter);
+
+	return count;
+}
+
+static ssize_t ir_carrier_show(struct device *dev,
+				 struct device_attribute *attr, char *buf)
+{
+	struct ir_device *ir = to_input_dev(dev)->ir;
+
+	return sprintf(buf, "%i\n", ir->raw.carrier);
+}
+
+static ssize_t ir_carrier_store(struct device *dev,
+				  struct device_attribute *attr,
+				  const char *buf,
+				  size_t count)
+{
+	struct ir_device *ir = to_input_dev(dev)->ir;
+
+	ir->raw.carrier = simple_strtoul(buf, NULL, 0);
+	return count;
+}
+
+static ssize_t ir_xmitter_show(struct device *dev,
+				 struct device_attribute *attr, char *buf)
+{
+	struct ir_device *ir = to_input_dev(dev)->ir;
+
+	return sprintf(buf, "%i\n", ir->raw.xmitter);
+}
+
+static ssize_t ir_xmitter_store(struct device *dev,
+				  struct device_attribute *attr,
+				  const char *buf,
+				  size_t count)
+{
+	struct ir_device *ir = to_input_dev(dev)->ir;
+
+	ir->raw.xmitter = simple_strtoul(buf, NULL, 0);
+	return count;
+}
+
+static DEVICE_ATTR(ir_raw, 0644, ir_raw_show, ir_raw_store);
+static DEVICE_ATTR(ir_carrier, 0644, ir_carrier_show, ir_carrier_store);
+static DEVICE_ATTR(ir_xmitter, 0644, ir_xmitter_show, ir_xmitter_store);
+
+int input_ir_register(struct input_dev *dev)
+{
+	int rc;
+
+	if (!dev->ir)
+		return 0;
+
+	rc = device_create_file(&dev->dev, &dev_attr_ir_raw);
+	if (rc)
+		return rc;
+
+	rc = device_create_file(&dev->dev, &dev_attr_ir_carrier);
+	if (rc)
+		return rc;
+
+	rc = device_create_file(&dev->dev, &dev_attr_ir_xmitter);
+	return rc;
+}
+
+int input_ir_create(struct input_dev *dev, void *private, send_func xmit)
+{
+	dev->ir = kzalloc(sizeof(struct ir_device), GFP_KERNEL);
+	if (!dev->ir)
+		return -ENOMEM;
+
+	dev->evbit[0] = BIT_MASK(EV_IR);
+	dev->ir->private = private;
+	dev->ir->xmit = xmit;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(input_ir_create);
+
+
+void input_ir_destroy(struct input_dev *dev)
+{
+	if (dev->ir) {
+		kfree(dev->ir);
+		dev->ir = NULL;
+	}
+}
+EXPORT_SYMBOL_GPL(input_ir_destroy);
+
+int input_ir_send(struct input_dev *dev, struct ir_command *ir_command, struct file *file)
+{
+	unsigned freq, xmit = 0;
+	int ret;
+
+	mutex_lock(&dev->ir->lock);
+
+	switch (ir_command->protocol) {
+	case IR_PROTOCOL_PHILIPS_RC5:
+		freq = 36000;
+		encode_rc5(dev->ir, ir_command);
+		break;
+	case IR_PROTOCOL_PHILIPS_RC6:
+		freq = 36000;
+		encode_rc6(dev->ir, ir_command);
+		break;
+	case IR_PROTOCOL_PHILIPS_RCMM:
+		freq = 36000;
+		encode_rc5(dev->ir, ir_command);
+		break;
+	case IR_PROTOCOL_JVC:
+		freq = 38000;
+		encode_jvc(dev->ir, ir_command);
+		break;
+	case IR_PROTOCOL_NEC:
+		freq = 38000;
+		encode_nec(dev->ir, ir_command);
+		break;
+	case IR_PROTOCOL_NOKIA:
+	case IR_PROTOCOL_SHARP:
+	case IR_PROTOCOL_PHILIPS_RECS80:
+		freq = 38000;
+		break;
+	case IR_PROTOCOL_SONY_12:
+	case IR_PROTOCOL_SONY_15:
+	case IR_PROTOCOL_SONY_20:
+		encode_sony(dev->ir, ir_command);
+		freq = 40000;
+		break;
+	case IR_PROTOCOL_RCA:
+		freq = 56000;
+		break;
+	case IR_PROTOCOL_ITT:
+		freq = 0;
+		break;
+	default:
+		ret = -ENODEV;
+		goto exit;
+	}
+
+	if (dev->ir && dev->ir->xmit)
+		ret = dev->ir->xmit(dev->ir->private, dev->ir->send.buffer, dev->ir->send.count, freq, xmit);
+	else
+		ret = -ENODEV;
+
+exit:
+	mutex_unlock(&dev->ir->lock);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(input_ir_send);
+
diff --git a/drivers/input/ir/Kconfig b/drivers/input/ir/Kconfig
new file mode 100644
index 0000000..8afd2d6
--- /dev/null
+++ b/drivers/input/ir/Kconfig
@@ -0,0 +1,14 @@
+#
+# LIRC driver(s) configuration
+#
+menuconfig INPUT_IR
+	bool "Infrared Remote (IR) receiver/transmitter drivers"
+	default n
+	help
+	  Say Y here, and all supported Infrared Remote Control IR
+	  receiver and transmitter drivers will be displayed. The receiver drivers
+	  allow control of your Linux system via remote control.
+
+if INPUT_IR
+
+endif
diff --git a/drivers/input/ir/Makefile b/drivers/input/ir/Makefile
new file mode 100644
index 0000000..08e6954
--- /dev/null
+++ b/drivers/input/ir/Makefile
@@ -0,0 +1,5 @@
+# Makefile for the ir drivers.
+#
+
+# Each configuration option enables a list of files.
+
diff --git a/include/linux/input.h b/include/linux/input.h
index a5802c9..2fbdf5a 100644
--- a/include/linux/input.h
+++ b/include/linux/input.h
@@ -79,6 +79,8 @@ struct input_absinfo {
 #define EVIOCRMFF		_IOW('E', 0x81, int)			/* Erase a force effect */
 #define EVIOCGEFFECTS		_IOR('E', 0x84, int)			/* Report number of effects playable at the same time */
 
+#define EVIOIRSEND		_IOC(_IOC_WRITE, 'E', 0x80, sizeof(struct ir_command))	/* send an IR command */
+
 #define EVIOCGRAB		_IOW('E', 0x90, int)			/* Grab/Release device */
 
 /*
@@ -97,6 +99,7 @@ struct input_absinfo {
 #define EV_FF			0x15
 #define EV_PWR			0x16
 #define EV_FF_STATUS		0x17
+#define EV_IR			0x18
 #define EV_MAX			0x1f
 #define EV_CNT			(EV_MAX+1)
 
@@ -946,6 +949,55 @@ struct ff_effect {
 #define FF_MAX		0x7f
 #define FF_CNT		(FF_MAX+1)
 
+/*
+ * IR Support
+ */
+
+#define IR_PROTOCOL_JVC 1
+#define IR_PROTOCOL_NEC 2
+#define IR_PROTOCOL_NOKIA 3
+#define IR_PROTOCOL_SHARP 4
+#define IR_PROTOCOL_SONY_12 5
+#define IR_PROTOCOL_SONY_15 6
+#define IR_PROTOCOL_SONY_20 7
+#define IR_PROTOCOL_PHILIPS_RC5 8
+#define IR_PROTOCOL_PHILIPS_RC6 9
+#define IR_PROTOCOL_PHILIPS_RCMM 10
+#define IR_PROTOCOL_PHILIPS_RECS80 11
+#define IR_PROTOCOL_RCA 12
+#define IR_PROTOCOL_ITT 13
+
+#define IR_PROTOCOL 1
+#define IR_DEVICE 2
+#define IR_COMMAND 3
+
+#define IR_CAP_RECEIVE_BASEBAND 0
+#define IR_CAP_RECEIVE_36K 1
+#define IR_CAP_RECEIVE_38K 2
+#define IR_CAP_RECEIVE_40K 3
+#define IR_CAP_RECEIVE_56K 4
+#define IR_CAP_SEND_BASEBAND 5
+#define IR_CAP_SEND_36K 6
+#define IR_CAP_SEND_38K 7
+#define IR_CAP_SEND_40K 8
+#define IR_CAP_SEND_56K 9
+#define IR_CAP_XMITTER_1 10
+#define IR_CAP_XMITTER_2 11
+#define IR_CAP_XMITTER_3 12
+#define IR_CAP_XMITTER_4 13
+#define IR_CAP_RECEIVE_RAW 14
+#define IR_CAP_SEND_RAW 15
+#define IR_MAX 0x0f
+#define IR_CNT IR_MAX + 1
+
+struct ir_command {
+	__u32 protocol;
+	__u32 device;
+	__u32 command;
+	__u32 transmitters;
+};
+
+
 #ifdef __KERNEL__
 
 /*
@@ -973,6 +1025,7 @@ struct ff_effect {
  * @sndbit: bitmap of sound effects supported by the device
  * @ffbit: bitmap of force feedback effects supported by the device
  * @swbit: bitmap of switches present on the device
+ * @irbit: bitmap of capabilies of the IR hardware
  * @keycodemax: size of keycode table
  * @keycodesize: size of elements in keycode table
  * @keycode: map of scancodes to keycodes for this device
@@ -1045,6 +1098,7 @@ struct input_dev {
 	unsigned long sndbit[BITS_TO_LONGS(SND_CNT)];
 	unsigned long ffbit[BITS_TO_LONGS(FF_CNT)];
 	unsigned long swbit[BITS_TO_LONGS(SW_CNT)];
+	unsigned long irbit[BITS_TO_LONGS(IR_CNT)];
 
 	unsigned int keycodemax;
 	unsigned int keycodesize;
@@ -1053,6 +1107,7 @@ struct input_dev {
 	int (*getkeycode)(struct input_dev *dev, int scancode, int *keycode);
 
 	struct ff_device *ff;
+	struct ir_device *ir;
 
 	unsigned int repeat_key;
 	struct timer_list timer;
@@ -1288,6 +1343,11 @@ static inline void input_report_switch(struct input_dev *dev, unsigned int code,
 	input_event(dev, EV_SW, code, !!value);
 }
 
+static inline void input_report_ir(struct input_dev *dev, unsigned int code, int value)
+{
+	input_event(dev, EV_IR, code, value);
+}
+
 static inline void input_sync(struct input_dev *dev)
 {
 	input_event(dev, EV_SYN, SYN_REPORT, 0);
@@ -1366,5 +1426,46 @@ int input_ff_erase(struct input_dev *dev, int effect_id, struct file *file);
 int input_ff_create_memless(struct input_dev *dev, void *data,
 		int (*play_effect)(struct input_dev *, void *, struct ff_effect *));
 
+/**
+ * struct ir_device - IR support structures
+ */
+
+struct ir_protocol {
+	unsigned int state, code, good, count, bits, mode;
+};
+
+typedef int (*send_func)(void *private, unsigned int *buffer, unsigned int count,
+		unsigned int frequency, unsigned int xmitters);
+
+struct ir_device {
+	struct ir_protocol sony;
+	struct ir_protocol jvc;
+	struct ir_protocol nec;
+	struct ir_protocol rc5;
+	struct ir_protocol rc6;
+	struct mutex lock;
+	void *private;
+	send_func xmit;
+	struct {
+		unsigned int buffer[200];
+		unsigned int count;
+	} send;
+	struct {
+		int buffer[200];
+		unsigned int head;
+		unsigned int tail;
+		unsigned int carrier;
+		unsigned int xmitter;
+	} raw;
+};
+
+int input_ir_create(struct input_dev *dev, void *private, send_func send);
+void input_ir_destroy(struct input_dev *dev);
+
+void input_ir_decode(struct input_dev *dev, unsigned int delta, unsigned int bit);
+int input_ir_send(struct input_dev *dev, struct ir_command *ir_command, struct file *file);
+
+int input_ir_register(struct input_dev *dev);
+
 #endif
 #endif
diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h
index c4db582..cb088ec 100644
--- a/include/linux/mod_devicetable.h
+++ b/include/linux/mod_devicetable.h
@@ -282,6 +282,7 @@ struct pcmcia_device_id {
 #define INPUT_DEVICE_ID_SND_MAX		0x07
 #define INPUT_DEVICE_ID_FF_MAX		0x7f
 #define INPUT_DEVICE_ID_SW_MAX		0x0f
+#define INPUT_DEVICE_ID_IR_MAX		0x0f
 
 #define INPUT_DEVICE_ID_MATCH_BUS	1
 #define INPUT_DEVICE_ID_MATCH_VENDOR	2
@@ -297,6 +298,7 @@ struct pcmcia_device_id {
 #define INPUT_DEVICE_ID_MATCH_SNDBIT	0x0400
 #define INPUT_DEVICE_ID_MATCH_FFBIT	0x0800
 #define INPUT_DEVICE_ID_MATCH_SWBIT	0x1000
+#define INPUT_DEVICE_ID_MATCH_IRBIT	0x2000
 
 struct input_device_id {
 
@@ -316,6 +318,7 @@ struct input_device_id {
 	kernel_ulong_t sndbit[INPUT_DEVICE_ID_SND_MAX / BITS_PER_LONG + 1];
 	kernel_ulong_t ffbit[INPUT_DEVICE_ID_FF_MAX / BITS_PER_LONG + 1];
 	kernel_ulong_t swbit[INPUT_DEVICE_ID_SW_MAX / BITS_PER_LONG + 1];
+	kernel_ulong_t irbit[INPUT_DEVICE_ID_IR_MAX / BITS_PER_LONG + 1];
 
 	kernel_ulong_t driver_info;
 };


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [RFC PATCH 2/4] GPT driver for in-kernel IR support.
  2008-10-06 19:43 [RFC PATCH 0/4] V3 - Implementation of IR support using the input subsystem Jon Smirl
  2008-10-06 19:43 ` [RFC PATCH 1/4] Changes to core input subsystem to allow send and receive of IR messages. Encode and decode state machines are provided for common IR porotocols such as Sony, JVC, NEC, Philips, etc Jon Smirl
@ 2008-10-06 19:43 ` Jon Smirl
  2008-10-06 19:43 ` [RFC PATCH 3/4] Example of PowerPC device tree support for GPT based IR Jon Smirl
                   ` (4 subsequent siblings)
  6 siblings, 0 replies; 16+ messages in thread
From: Jon Smirl @ 2008-10-06 19:43 UTC (permalink / raw)
  To: linux-kernel, lirc-list

GPT is a GPIO pin that is cable able of measuring the lenght of pulses.
GPTs are common on embedded systems
---
 drivers/input/ir/Kconfig  |    6 +
 drivers/input/ir/Makefile |    1 
 drivers/input/ir/ir-gpt.c |  221 +++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 228 insertions(+), 0 deletions(-)
 create mode 100644 drivers/input/ir/ir-gpt.c

diff --git a/drivers/input/ir/Kconfig b/drivers/input/ir/Kconfig
index 8afd2d6..b80ab31 100644
--- a/drivers/input/ir/Kconfig
+++ b/drivers/input/ir/Kconfig
@@ -11,4 +11,10 @@ menuconfig INPUT_IR
 
 if INPUT_IR
 
+config IR_GPT
+	tristate "GPT Based IR Receiver"
+	default m
+	help
+	  Driver for GPT-based IR receiver found on Digispeaker
+
 endif
diff --git a/drivers/input/ir/Makefile b/drivers/input/ir/Makefile
index 08e6954..7082f1d 100644
--- a/drivers/input/ir/Makefile
+++ b/drivers/input/ir/Makefile
@@ -3,3 +3,4 @@
 
 # Each configuration option enables a list of files.
 
+obj-$(CONFIG_IR_GPT)		+= ir-gpt.o
diff --git a/drivers/input/ir/ir-gpt.c b/drivers/input/ir/ir-gpt.c
new file mode 100644
index 0000000..a26abe9
--- /dev/null
+++ b/drivers/input/ir/ir-gpt.c
@@ -0,0 +1,221 @@
+/*
+ * GPT timer based IR device
+ *
+ * Copyright (C) 2008 Jon Smirl <jonsmirl@gmail.com>
+ */
+
+#define DEBUG
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/of_device.h>
+#include <linux/of_platform.h>
+#include <linux/input.h>
+#include <asm/io.h>
+#include <asm/mpc52xx.h>
+
+#define MAX_SAMPLES 200
+
+struct ir_gpt {
+	int irq;
+	struct mpc52xx_gpt __iomem *regs;
+	spinlock_t lock;
+	struct work_struct queue;
+	int head, tail, previous;
+	unsigned int samples[MAX_SAMPLES];
+	struct input_dev *input;
+};
+
+static void ir_event(struct work_struct *work)
+{
+	unsigned long flags;
+	int delta, count;
+	unsigned int sample, wrap, bit;
+	struct ir_gpt *ir_gpt = container_of(work, struct ir_gpt, queue);
+
+	while (1) {
+		spin_lock_irqsave(ir_gpt->lock, flags);
+		if (ir_gpt->tail == ir_gpt->head) {
+			spin_unlock_irqrestore(ir_gpt->lock, flags);
+			break;
+		}
+		sample = ir_gpt->samples[ir_gpt->tail];
+
+		ir_gpt->tail++;
+		if (ir_gpt->tail >= MAX_SAMPLES)
+			ir_gpt->tail = 0;
+
+		spin_unlock_irqrestore(ir_gpt->lock, flags);
+
+		count = sample >> 16;
+		wrap = (sample >> 12) & 7;
+		bit = (sample >> 8) & 1;
+
+		delta = count - ir_gpt->previous;
+		delta += wrap * 0x10000;
+
+		ir_gpt->previous = count;
+
+		input_ir_decode(ir_gpt->input, delta, bit);
+	}
+}
+
+/*
+ * Interrupt handlers
+ */
+static irqreturn_t dpeak_ir_irq(int irq, void *_ir)
+{
+	unsigned long flags;
+	unsigned int sample, next;
+	struct ir_gpt *ir_gpt = _ir;
+
+	sample = in_be32(&ir_gpt->regs->status);
+	out_be32(&ir_gpt->regs->status, 0xF);
+
+	spin_lock_irqsave(ir_gpt->lock, flags);
+	ir_gpt->samples[ir_gpt->head] = sample;
+	next = ir_gpt->head + 1;
+	ir_gpt->head = (next >= MAX_SAMPLES ? 0 : next);
+	spin_unlock_irqrestore(ir_gpt->lock, flags);
+
+	schedule_work(&ir_gpt->queue);
+
+	return IRQ_HANDLED;
+}
+
+
+/* ---------------------------------------------------------------------
+ * OF platform bus binding code:
+ * - Probe/remove operations
+ * - OF device match table
+ */
+static int __devinit ir_gpt_of_probe(struct of_device *op,
+				      const struct of_device_id *match)
+{
+	struct ir_gpt *ir_gpt;
+	struct resource res;
+	int ret, rc;
+
+	dev_dbg(&op->dev, "ir_gpt_of_probe\n");
+
+	/* Allocate and initialize the driver private data */
+	ir_gpt = kzalloc(sizeof *ir_gpt, GFP_KERNEL);
+	if (!ir_gpt)
+		return -ENOMEM;
+
+	ir_gpt->input = input_allocate_device();
+	if (!ir_gpt->input) {
+		ret = -ENOMEM;
+		goto free_mem;
+	}
+	ret = input_ir_create(ir_gpt->input, ir_gpt, NULL);
+	if (ret)
+		goto free_input;
+
+	ir_gpt->input->id.bustype = BUS_HOST;
+	ir_gpt->input->name = "GPT IR Receiver";
+
+	ir_gpt->input->irbit[0] |= BIT_MASK(IR_CAP_RECEIVE_36K);
+	ir_gpt->input->irbit[0] |= BIT_MASK(IR_CAP_RECEIVE_38K);
+	ir_gpt->input->irbit[0] |= BIT_MASK(IR_CAP_RECEIVE_40K);
+	ir_gpt->input->irbit[0] |= BIT_MASK(IR_CAP_RECEIVE_RAW);
+
+	ret = input_register_device(ir_gpt->input);
+	if (ret)
+		goto free_input;
+
+	spin_lock_init(&ir_gpt->lock);
+	INIT_WORK (&ir_gpt->queue, ir_event);
+
+	/* Fetch the registers and IRQ of the GPT */
+	if (of_address_to_resource(op->node, 0, &res)) {
+		dev_err(&op->dev, "Missing reg property\n");
+		ret = -ENODEV;
+		goto free_input;
+	}
+	ir_gpt->regs = ioremap(res.start, 1 + res.end - res.start);
+	if (!ir_gpt->regs) {
+		dev_err(&op->dev, "Could not map registers\n");
+		ret = -ENODEV;
+		goto free_input;
+	}
+	ir_gpt->irq = irq_of_parse_and_map(op->node, 0);
+	if (ir_gpt->irq == NO_IRQ) {
+		ret = -ENODEV;
+		goto free_input;
+	}
+	dev_dbg(&op->dev, "ir_gpt_of_probe irq=%d\n", ir_gpt->irq);
+
+	rc = request_irq(ir_gpt->irq, &dpeak_ir_irq, IRQF_SHARED,
+			 "gpt-ir", ir_gpt);
+	dev_dbg(&op->dev, "ir_gpt_of_probe request irq rc=%d\n", rc);
+
+	/* set prescale to ? */
+	out_be32(&ir_gpt->regs->count, 0x00870000);
+
+	/* Select input capture, enable the counter, and interrupt */
+	out_be32(&ir_gpt->regs->mode, 0x0);
+	out_be32(&ir_gpt->regs->mode, 0x00000501);
+
+	/* Save what we've done so it can be found again later */
+	dev_set_drvdata(&op->dev, ir_gpt);
+
+	printk("GPT IR Receiver driver\n");
+
+	return 0;
+
+free_input:
+	input_ir_destroy(ir_gpt->input);
+	input_free_device(ir_gpt->input);
+free_mem:
+	kfree(ir_gpt);
+	return ret;
+}
+
+static int __devexit ir_gpt_of_remove(struct of_device *op)
+{
+	struct ir_gpt *ir_gpt = dev_get_drvdata(&op->dev);
+
+	dev_dbg(&op->dev, "ir_gpt_remove()\n");
+
+	input_ir_destroy(ir_gpt->input);
+	input_free_device(ir_gpt->input);
+	kfree(ir_gpt);
+	dev_set_drvdata(&op->dev, NULL);
+
+	return 0;
+}
+
+/* Match table for of_platform binding */
+static struct of_device_id ir_gpt_match[] __devinitdata = {
+	{ .compatible = "gpt-ir", },
+	{}
+};
+MODULE_DEVICE_TABLE(of, ir_gpt_match);
+
+static struct of_platform_driver ir_gpt_driver = {
+	.match_table = ir_gpt_match,
+	.probe = ir_gpt_of_probe,
+	.remove = __devexit_p(ir_gpt_of_remove),
+	.driver = {
+		.name = "ir-gpt",
+		.owner = THIS_MODULE,
+	},
+};
+
+/* ---------------------------------------------------------------------
+ * Module setup and teardown; simply register the of_platform driver
+ */
+static int __init ir_gpt_init(void)
+{
+	return of_register_platform_driver(&ir_gpt_driver);
+}
+module_init(ir_gpt_init);
+
+static void __exit ir_gpt_exit(void)
+{
+	of_unregister_platform_driver(&ir_gpt_driver);
+}
+module_exit(ir_gpt_exit);


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [RFC PATCH 3/4] Example of PowerPC device tree support for GPT based IR
  2008-10-06 19:43 [RFC PATCH 0/4] V3 - Implementation of IR support using the input subsystem Jon Smirl
  2008-10-06 19:43 ` [RFC PATCH 1/4] Changes to core input subsystem to allow send and receive of IR messages. Encode and decode state machines are provided for common IR porotocols such as Sony, JVC, NEC, Philips, etc Jon Smirl
  2008-10-06 19:43 ` [RFC PATCH 2/4] GPT driver for in-kernel IR support Jon Smirl
@ 2008-10-06 19:43 ` Jon Smirl
  2008-10-06 19:43 ` [RFC PATCH 4/4] Microsoft mceusb2 driver for in-kernel IR subsystem Jon Smirl
                   ` (3 subsequent siblings)
  6 siblings, 0 replies; 16+ messages in thread
From: Jon Smirl @ 2008-10-06 19:43 UTC (permalink / raw)
  To: linux-kernel, lirc-list


---
 arch/powerpc/boot/dts/dspeak01.dts |   19 ++++++++-----------
 1 files changed, 8 insertions(+), 11 deletions(-)

diff --git a/arch/powerpc/boot/dts/dspeak01.dts b/arch/powerpc/boot/dts/dspeak01.dts
index 1d10f0a..cbb22a1 100644
--- a/arch/powerpc/boot/dts/dspeak01.dts
+++ b/arch/powerpc/boot/dts/dspeak01.dts
@@ -131,16 +131,6 @@
 			#gpio-cells = <2>;
 		};
 
-		gpt7: timer@670 { /* General Purpose Timer in GPIO mode */
-			compatible = "fsl,mpc5200b-gpt-gpio","fsl,mpc5200-gpt-gpio";
-			cell-index = <7>;
-			reg = <0x670 0x10>;
-			interrupts = <0x1 0x10 0x0>;
-			interrupt-parent = <&mpc5200_pic>;
-			gpio-controller;
-			#gpio-cells = <2>;
-		};
-
 		rtc@800 {	// Real time clock
 			compatible = "fsl,mpc5200b-rtc","fsl,mpc5200-rtc";
 			device_type = "rtc";
@@ -328,6 +318,14 @@
 			reg = <0x8000 0x4000>;
 		};
 
+		ir0: timer@670 { /* General Purpose Timer 6 in Input mode */
+			compatible = "gpt-ir";
+			cell-index = <7>;
+			reg = <0x670 0x10>;
+			interrupts = <0x1 0x10 0x0>;
+			interrupt-parent = <&mpc5200_pic>;
+		};
+
 		/* This is only an example device to show the usage of gpios. It maps all available
 		 * gpios to the "gpio-provider" device.
 		 */
@@ -345,7 +343,6 @@
 				 &gpt4		0 0 /* timer4		61c		x2-16	   */
 				 &gpt5		0 0 /* timer5		44c		x7-11	   */
 				 &gpt6		0 0 /* timer6		60c		x8-15	   */
-				 &gpt7		0 0 /* timer7		36a		x17-9	   */
 				 >;
 		};
 	};


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [RFC PATCH 4/4] Microsoft mceusb2 driver for in-kernel IR subsystem
  2008-10-06 19:43 [RFC PATCH 0/4] V3 - Implementation of IR support using the input subsystem Jon Smirl
                   ` (2 preceding siblings ...)
  2008-10-06 19:43 ` [RFC PATCH 3/4] Example of PowerPC device tree support for GPT based IR Jon Smirl
@ 2008-10-06 19:43 ` Jon Smirl
  2008-10-06 19:46 ` [RFC PATCH 0/4] V3 - Implementation of IR support using the input subsystem Jon Smirl
                   ` (2 subsequent siblings)
  6 siblings, 0 replies; 16+ messages in thread
From: Jon Smirl @ 2008-10-06 19:43 UTC (permalink / raw)
  To: linux-kernel, lirc-list

USB device commonly found on Microsoft Media Center boxes.

Hardware can send and recieve at all common IR frequencies - 36K, 38K, 40K, 56K
---
 drivers/input/ir/Kconfig      |    6 
 drivers/input/ir/Makefile     |    1 
 drivers/input/ir/ir-mceusb2.c |  741 +++++++++++++++++++++++++++++++++++++++++
 3 files changed, 748 insertions(+), 0 deletions(-)
 create mode 100644 drivers/input/ir/ir-mceusb2.c

diff --git a/drivers/input/ir/Kconfig b/drivers/input/ir/Kconfig
index b80ab31..29657d0 100644
--- a/drivers/input/ir/Kconfig
+++ b/drivers/input/ir/Kconfig
@@ -16,5 +16,11 @@ config IR_GPT
 	default m
 	help
 	  Driver for GPT-based IR receiver found on Digispeaker
+	  
+config IR_MCEUSB2
+	tristate "Microsoft Media Center Ed. Receiver, v2"
+	default m
+	help
+	  Driver for the Microsoft Media Center Ed. Receiver, v2
 
 endif
diff --git a/drivers/input/ir/Makefile b/drivers/input/ir/Makefile
index 7082f1d..780de6c 100644
--- a/drivers/input/ir/Makefile
+++ b/drivers/input/ir/Makefile
@@ -4,3 +4,4 @@
 # Each configuration option enables a list of files.
 
 obj-$(CONFIG_IR_GPT)		+= ir-gpt.o
+obj-$(CONFIG_IR_MCEUSB2)		+= ir-mceusb2.o
diff --git a/drivers/input/ir/ir-mceusb2.c b/drivers/input/ir/ir-mceusb2.c
new file mode 100644
index 0000000..a2aaead
--- /dev/null
+++ b/drivers/input/ir/ir-mceusb2.c
@@ -0,0 +1,741 @@
+/*
+ * LIRC driver for Philips eHome USB Infrared Transceiver
+ * and the Microsoft MCE 2005 Remote Control
+ *
+ * (C) by Martin A. Blatter <martin_a_blatter@yahoo.com>
+ *
+ * Transmitter support and reception code cleanup.
+ * (C) by Daniel Melander <lirc@rajidae.se>
+ *
+ * Derived from ATI USB driver by Paul Miller and the original
+ * MCE USB driver by Dan Corti
+ *
+ * This driver will only work reliably with kernel version 2.6.10
+ * or higher, probably because of differences in USB device enumeration
+ * in the kernel code. Device initialization fails most of the time
+ * with earlier kernel versions.
+ *
+ **********************************************************************
+ *
+ * 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
+ *
+ */
+
+#define DEBUG
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/input.h>
+
+#define DRIVER_AUTHOR	"Daniel Melander <lirc@rajidae.se>, " \
+			"Martin Blatter <martin_a_blatter@yahoo.com>"
+#define DRIVER_DESC	"Philips eHome USB IR Transceiver and Microsoft " \
+			"MCE 2005 Remote Control driver"
+#define DRIVER_NAME	"ir_mceusb2"
+
+#define USB_BUFLEN	16	/* USB reception buffer length */
+#define LIRCBUF_SIZE	256	/* LIRC work buffer length */
+
+/* MCE constants */
+#define MCE_CMDBUF_SIZE	384 /* MCE Command buffer length */
+#define MCE_TIME_BASE	50 /* Approx 50us resolution */
+#define MCE_CODE_LENGTH	5 /* Normal length of packet (with header) */
+#define MCE_PACKET_SIZE	4 /* Normal length of packet (without header) */
+#define MCE_PACKET_HEADER 0x84 /* Actual header format is 0x80 + num_bytes */
+#define MCE_CONTROL_HEADER 0x9F /* MCE status header */
+#define MCE_TX_HEADER_LENGTH 3 /* # of bytes in the initializing tx header */
+#define MCE_MAX_CHANNELS 2 /* Two transmitters, hardware dependent? */
+#define MCE_DEFAULT_TX_MASK 0x03 /* Val opts: TX1=0x01, TX2=0x02, ALL=0x03 */
+#define MCE_PULSE_BIT	0x80 /* Pulse bit, MSB set == PULSE else SPACE */
+#define MCE_PULSE_MASK	0x7F /* Pulse mask */
+#define MCE_MAX_PULSE_LENGTH 0x7F /* Longest transmittable pulse symbol */
+#define MCE_PACKET_LENGTH_MASK  0x7 /* Packet length */
+
+
+/* general constants */
+#define SEND_FLAG_IN_PROGRESS	1
+#define SEND_FLAG_COMPLETE	2
+#define RECV_FLAG_IN_PROGRESS	3
+#define RECV_FLAG_COMPLETE	4
+
+#define PHILUSB_RECEIVE		1
+#define PHILUSB_SEND	2
+
+#define VENDOR_PHILIPS		0x0471
+#define VENDOR_SMK		0x0609
+#define VENDOR_TATUNG		0x1460
+#define VENDOR_GATEWAY		0x107b
+#define VENDOR_SHUTTLE		0x1308
+#define VENDOR_SHUTTLE2		0x051c
+#define VENDOR_MITSUMI		0x03ee
+#define VENDOR_TOPSEED		0x1784
+#define VENDOR_RICAVISION	0x179d
+#define VENDOR_ITRON		0x195d
+#define VENDOR_FIC		0x1509
+#define VENDOR_LG		0x043e
+#define VENDOR_MICROSOFT	0x045e
+#define VENDOR_FORMOSA		0x147a
+#define VENDOR_FINTEK		0x1934
+#define VENDOR_PINNACLE		0x2304
+
+static struct usb_device_id usb_remote_table[] = {
+	/* Philips eHome Infrared Transceiver */
+	{ USB_DEVICE(VENDOR_PHILIPS, 0x0815) },
+	/* Philips Infrared Transceiver - HP branded */
+	{ USB_DEVICE(VENDOR_PHILIPS, 0x060c) },
+	/* Philips SRM5100 */
+	{ USB_DEVICE(VENDOR_PHILIPS, 0x060d) },
+	/* Philips Infrared Transceiver - Omaura */
+	{ USB_DEVICE(VENDOR_PHILIPS, 0x060f) },
+	/* SMK/Toshiba G83C0004D410 */
+	{ USB_DEVICE(VENDOR_SMK, 0x031d) },
+	/* SMK eHome Infrared Transceiver (Sony VAIO) */
+	{ USB_DEVICE(VENDOR_SMK, 0x0322) },
+	/* bundled with Hauppauge PVR-150 */
+	{ USB_DEVICE(VENDOR_SMK, 0x0334) },
+	/* Tatung eHome Infrared Transceiver */
+	{ USB_DEVICE(VENDOR_TATUNG, 0x9150) },
+	/* Shuttle eHome Infrared Transceiver */
+	{ USB_DEVICE(VENDOR_SHUTTLE, 0xc001) },
+	/* Shuttle eHome Infrared Transceiver */
+	{ USB_DEVICE(VENDOR_SHUTTLE2, 0xc001) },
+	/* Gateway eHome Infrared Transceiver */
+	{ USB_DEVICE(VENDOR_GATEWAY, 0x3009) },
+	/* Mitsumi */
+	{ USB_DEVICE(VENDOR_MITSUMI, 0x2501) },
+	/* Topseed eHome Infrared Transceiver */
+	{ USB_DEVICE(VENDOR_TOPSEED, 0x0001) },
+	/* Topseed HP eHome Infrared Transceiver */
+	{ USB_DEVICE(VENDOR_TOPSEED, 0x0006) },
+	/* Topseed eHome Infrared Transceiver */
+	{ USB_DEVICE(VENDOR_TOPSEED, 0x0007) },
+	/* Topseed eHome Infrared Transceiver */
+	{ USB_DEVICE(VENDOR_TOPSEED, 0x0008) },
+	/* Ricavision internal Infrared Transceiver */
+	{ USB_DEVICE(VENDOR_RICAVISION, 0x0010) },
+	/* Itron ione Libra Q-11 */
+	{ USB_DEVICE(VENDOR_ITRON, 0x7002) },
+	/* FIC eHome Infrared Transceiver */
+	{ USB_DEVICE(VENDOR_FIC, 0x9242) },
+	/* LG eHome Infrared Transceiver */
+	{ USB_DEVICE(VENDOR_LG, 0x9803) },
+	/* Microsoft MCE Infrared Transceiver */
+	{ USB_DEVICE(VENDOR_MICROSOFT, 0x00a0) },
+	/* Formosa eHome Infrared Transceiver */
+	{ USB_DEVICE(VENDOR_FORMOSA, 0xe015) },
+	/* Formosa21 / eHome Infrared Receiver */
+	{ USB_DEVICE(VENDOR_FORMOSA, 0xe016) },
+	/* Formosa aim / Trust MCE Infrared Receiver */
+	{ USB_DEVICE(VENDOR_FORMOSA, 0xe017) },
+	/* Formosa Industrial Computing / Beanbag Emulation Device */
+	{ USB_DEVICE(VENDOR_FORMOSA, 0xe018) },
+	/* Fintek eHome Infrared Transceiver */
+	{ USB_DEVICE(VENDOR_FINTEK, 0x0602) },
+	/* Pinnacle Remote Kit */
+	{ USB_DEVICE(VENDOR_PINNACLE, 0x0225) },
+	/* Terminating entry */
+	{ }
+};
+
+static struct usb_device_id pinnacle_list[] = {
+	{ USB_DEVICE(VENDOR_PINNACLE, 0x0225) },
+	{}
+};
+
+static struct usb_device_id xmit_inverted[] = {
+	{ USB_DEVICE(VENDOR_SMK, 0x031d) },
+	{ USB_DEVICE(VENDOR_SMK, 0x0322) },
+	{ USB_DEVICE(VENDOR_SMK, 0x0334) },
+	{ USB_DEVICE(VENDOR_TOPSEED, 0x0001) },
+	{ USB_DEVICE(VENDOR_TOPSEED, 0x0007) },
+	{ USB_DEVICE(VENDOR_TOPSEED, 0x0008) },
+	{ USB_DEVICE(VENDOR_PINNACLE, 0x0225) },
+	{}
+};
+
+/* data structure for each usb remote */
+struct irctl {
+
+	/* usb */
+	struct usb_device *usbdev;
+	struct urb *urb_in;
+	struct usb_endpoint_descriptor *usb_ep_in;
+	struct usb_endpoint_descriptor *usb_ep_out;
+
+	/* buffers and dma */
+	unsigned char *buf_in;
+	unsigned int len_in;
+	dma_addr_t dma_in;
+	dma_addr_t dma_out;
+
+	struct {
+		u32 connected:1;
+		u32 pinnacle:1;
+		u32 transmitter_mask_inverted:1;
+		u32 reserved:29;
+	} flags;
+
+	/* handle sending (init strings) */
+	int send_flags;
+	int carrier;
+	char name[128];
+	char phys[128];
+
+	struct {
+		unsigned int command, partial, delta, bit;
+	} last;
+	struct input_dev *input;
+};
+
+/* init strings */
+static char init1[] = {0x00, 0xff, 0xaa, 0xff, 0x0b};
+static char init2[] = {0xff, 0x18};
+
+static char pin_init1[] = { 0x9f, 0x07};
+static char pin_init2[] = { 0x9f, 0x13};
+static char pin_init3[] = { 0x9f, 0x0d};
+
+static void usb_send_callback(struct urb *urb, struct pt_regs *regs)
+{
+	struct irctl *ir = urb->context;
+	int len= urb->actual_length;
+
+	dev_dbg(&ir->usbdev->dev, "usb_send_callback (status=%d len=%d)\n", urb->status, len);
+#ifdef DEBUG
+	print_hex_dump_bytes("MCEUSB2 data: ", DUMP_PREFIX_NONE, urb->transfer_buffer, len);
+#endif
+}
+
+
+static void usb_receive_callback(struct urb *urb, struct pt_regs *regs)
+{
+	struct irctl *ir = urb->context;
+	int len= urb->actual_length;
+
+	dev_dbg(&ir->usbdev->dev, "usb_receive_callback (status=%d len=%d)\n", urb->status, len);
+#ifdef DEBUG
+	print_hex_dump_bytes("MCEUSB2 data: ", DUMP_PREFIX_NONE, urb->transfer_buffer, len);
+#endif
+}
+
+
+/* request incoming or send outgoing usb packet - used to initialize remote */
+static int request_packet_async(struct irctl *ir, struct usb_endpoint_descriptor *ep,
+				 unsigned char *data, int size, int urb_type)
+{
+	int res;
+	struct urb *async_urb;
+	unsigned char *async_buf;
+
+	switch (urb_type) {
+	case PHILUSB_SEND:
+	case PHILUSB_RECEIVE:
+		async_urb = usb_alloc_urb(0, GFP_KERNEL);
+		if (!async_urb) {
+			dev_dbg(&ir->usbdev->dev, "Failed to allocate URB\n");
+			return -ENOMEM;
+		}
+		/* alloc buffer */
+		async_buf = kmalloc(size, GFP_KERNEL);
+		if (!async_buf) {
+			usb_free_urb(async_urb);
+			dev_dbg(&ir->usbdev->dev, "Failed to allocate async_buf\n");
+			return -ENOMEM;
+		}
+
+		if (urb_type == PHILUSB_SEND) {
+			/* outbound data */
+			usb_fill_int_urb(async_urb, ir->usbdev,
+				usb_sndintpipe(ir->usbdev, ep->bEndpointAddress),
+				async_buf, size, (usb_complete_t) usb_send_callback,
+				ir, ep->bInterval);
+
+			memcpy(async_buf, data, size);
+			dev_dbg(&ir->usbdev->dev, "request_packet_async called (size=%#x, send)\n", size);
+		} else {
+			/* inbound data */
+			usb_fill_int_urb(async_urb, ir->usbdev,
+				usb_rcvintpipe(ir->usbdev, ep->bEndpointAddress),
+				async_buf, size, (usb_complete_t) usb_receive_callback,
+				ir, ep->bInterval);
+			dev_dbg(&ir->usbdev->dev, "request_packet_async called (size=%#x, receive)\n", size);
+		}
+		break;
+	default:
+	case 0:
+		/* standard request */
+		async_urb = ir->urb_in;
+		ir->send_flags = RECV_FLAG_IN_PROGRESS;
+		dev_dbg(&ir->usbdev->dev, "request_packet_async called (size=%#x, standard)\n", size);
+		break;
+	}
+
+	async_urb->transfer_buffer_length = size;
+	async_urb->dev = ir->usbdev;
+
+	res = usb_submit_urb(async_urb, GFP_ATOMIC);
+	if (res)
+		dev_dbg(&ir->usbdev->dev, "request_packet_async (res=%d)\n", res);
+	return res;
+}
+
+static void usb_remote_recv(struct urb *urb, struct pt_regs *regs)
+{
+	struct irctl *ir;
+	int buf_len;
+	int i, delta, bit;
+
+	if (!urb)
+		return;
+
+	ir = urb->context;
+	if (!ir) {
+		usb_unlink_urb(urb);
+		return;
+	}
+	buf_len = urb->actual_length;
+
+#ifdef DEBUG
+	print_hex_dump_bytes("MCEUSB2 data: ", DUMP_PREFIX_NONE, urb->transfer_buffer, buf_len);
+#endif
+
+	if (ir->send_flags == RECV_FLAG_IN_PROGRESS) {
+		ir->send_flags = SEND_FLAG_COMPLETE;
+		dev_dbg(&ir->usbdev->dev, "setup answer received %d bytes\n", buf_len);
+	}
+	switch (urb->status) {
+	/* success */
+	case 0:
+		for (i = 0; i < buf_len;) {
+			if (ir->last.partial == 0) {
+				/* decode mce packets of the form (84),AA,BB,CC,DD */
+				/* IR data packets can span USB messages - partial */
+				ir->last.partial = (ir->buf_in[i] & MCE_PACKET_LENGTH_MASK);
+				ir->last.command =  ir->buf_in[i] & ~MCE_PACKET_LENGTH_MASK;
+				i++;
+			}
+			for (; (ir->last.partial > 0) && (i < buf_len); i++) {
+				ir->last.partial--;
+
+				if (ir->last.command == 0x80) {
+					bit = ((ir->buf_in[i] & MCE_PULSE_BIT) != 0);
+					delta = (ir->buf_in[i] & MCE_PULSE_MASK) * MCE_TIME_BASE;
+
+					if ((ir->buf_in[i] & MCE_PULSE_MASK) == 0x7f) {
+						if (ir->last.bit == bit)
+							ir->last.delta += delta;
+						else {
+							ir->last.delta = delta;
+							ir->last.bit = bit;
+						}
+						continue;
+					}
+					delta += ir->last.delta;
+					ir->last.delta = 0;
+					ir->last.bit = bit;
+
+					dev_dbg(&ir->usbdev->dev, "bit %d delta %d\n", bit, delta);
+					input_ir_decode(ir->input, delta, bit);
+				}
+			}
+		}
+		break;
+
+		/* unlink */
+	case -ECONNRESET:
+	case -ENOENT:
+	case -ESHUTDOWN:
+		usb_unlink_urb(urb);
+		return;
+
+	case -EPIPE:
+	default:
+		break;
+	}
+
+	/* resubmit urb */
+	usb_submit_urb(urb, GFP_ATOMIC);
+}
+
+
+/* Sets the send carrier frequency */
+static int set_carrier(struct irctl *ir, int carrier)
+{
+	int clk = 10000000;
+	int prescaler = 0, divisor = 0;
+	unsigned char cmdbuf[] = { 0x9F, 0x06, 0x01, 0x80 };
+
+	/* Carrier is changed */
+	if (ir->carrier != carrier) {
+
+		if (carrier <= 0) {
+			ir->carrier = carrier;
+			dev_dbg(&ir->usbdev->dev, "SET_CARRIER disabling carrier modulation\n");
+			request_packet_async(ir, ir->usb_ep_out,
+					     cmdbuf, sizeof(cmdbuf), PHILUSB_SEND);
+			return carrier;
+		}
+
+		for (prescaler = 0; prescaler < 4; ++prescaler) {
+			divisor = (clk >> (2 * prescaler)) / carrier;
+			if (divisor <= 0xFF) {
+				ir->carrier = carrier;
+				cmdbuf[2] = prescaler;
+				cmdbuf[3] = divisor;
+				dev_dbg(&ir->usbdev->dev, "SET_CARRIER requesting %d Hz\n", carrier);
+
+				/* Transmit the new carrier to the mce
+				   device */
+				request_packet_async(ir, ir->usb_ep_out,
+						     cmdbuf, sizeof(cmdbuf), PHILUSB_SEND);
+				return carrier;
+			}
+		}
+		return -EINVAL;
+	}
+	return carrier;
+}
+
+static int send(void *private, unsigned int *buffer, unsigned int count,
+		unsigned int frequency, unsigned int xmitters)
+{
+	struct irctl *ir = (struct irctl *)private;
+
+	int i, cmdcount = 0;
+	unsigned char cmdbuf[MCE_CMDBUF_SIZE]; /* MCE command buffer */
+	unsigned long signal_duration = 0; /* Signal length in us */
+
+	if (!ir && !ir->usb_ep_out)
+		return -EFAULT;
+
+	set_carrier(ir, frequency);
+
+	/* MCE tx init header */
+	cmdbuf[cmdcount++] = MCE_CONTROL_HEADER;
+	cmdbuf[cmdcount++] = 0x08;
+	cmdbuf[cmdcount++] = (ir->flags.transmitter_mask_inverted ? ~xmitters : xmitters) << 1;
+
+	/* Generate mce packet data */
+	for (i = 0; (i < count) && (cmdcount < MCE_CMDBUF_SIZE); i++) {
+		signal_duration += buffer[i];
+		buffer[i] = buffer[i] / MCE_TIME_BASE;
+
+		do { /* loop to support long pulses/spaces > 127*50us=6.35ms */
+
+			/* Insert mce packet header every 4th entry */
+			if ((cmdcount < MCE_CMDBUF_SIZE) && (cmdcount - MCE_TX_HEADER_LENGTH) % MCE_CODE_LENGTH == 0)
+				cmdbuf[cmdcount++] = MCE_PACKET_HEADER;
+
+			/* Insert mce packet data */
+			if (cmdcount < MCE_CMDBUF_SIZE)
+				cmdbuf[cmdcount++] = (buffer[i] < MCE_PULSE_BIT ? buffer[i] :
+					MCE_MAX_PULSE_LENGTH) | (i & 1 ? 0x00 : MCE_PULSE_BIT);
+			else
+				return -EINVAL;
+		} while ((buffer[i] > MCE_MAX_PULSE_LENGTH) && (buffer[i] -= MCE_MAX_PULSE_LENGTH));
+	}
+
+	/* Fix packet length in last header */
+	cmdbuf[cmdcount - (cmdcount - MCE_TX_HEADER_LENGTH) % MCE_CODE_LENGTH] =
+		0x80 + (cmdcount - MCE_TX_HEADER_LENGTH) % MCE_CODE_LENGTH - 1;
+
+	/* Check if we have room for the empty packet at the end */
+	if (cmdcount >= MCE_CMDBUF_SIZE)
+		return -EINVAL;
+
+	/* All mce commands end with an empty packet (0x80) */
+	cmdbuf[cmdcount++] = 0x80;
+
+	/* Transmit the command to the mce device */
+	request_packet_async(ir, ir->usb_ep_out, cmdbuf, cmdcount, PHILUSB_SEND);
+
+	return 0;
+}
+
+static int usb_remote_probe(struct usb_interface *intf,
+				const struct usb_device_id *id)
+{
+	struct usb_device *dev = interface_to_usbdev(intf);
+	struct usb_host_interface *idesc;
+	struct usb_endpoint_descriptor *ep = NULL;
+	struct usb_endpoint_descriptor *ep_in = NULL;
+	struct usb_endpoint_descriptor *ep_out = NULL;
+	struct usb_host_config *config;
+	struct irctl *ir = NULL;
+	int i, devnum, pipe, maxp, ret, is_pinnacle;
+
+	dev_dbg(&dev->dev, "usb probe called\n");
+
+	usb_reset_device(dev);
+
+	config = dev->actconfig;
+
+	idesc = intf->cur_altsetting;
+
+	is_pinnacle = usb_match_id(intf, pinnacle_list) ? 1 : 0;
+
+	/* step through the endpoints to find first bulk in and out endpoint */
+	for (i = 0; i < idesc->desc.bNumEndpoints; ++i) {
+		ep = &idesc->endpoint[i].desc;
+
+		if ((ep_in == NULL)
+				&& ((ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN)
+				&& (((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_BULK)
+					|| ((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT))) {
+
+			dev_dbg(&dev->dev, "acceptable inbound endpoint found\n");
+			ep_in = ep;
+			ep_in->bmAttributes = USB_ENDPOINT_XFER_INT;
+
+			/*
+			 * setting seems to 1 seem to cause issues with
+			 * Pinnacle timing out on transfer.
+			 */
+			if (is_pinnacle)
+				ep_in->bInterval = ep->bInterval;
+			else
+				ep_in->bInterval = 1;
+		}
+		if ((ep_out == NULL)
+			&& ((ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT)
+			&& (((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_BULK)
+				|| ((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT))) {
+
+			dev_dbg(&dev->dev, "acceptable outbound endpoint found\n");
+			ep_out = ep;
+			ep_out->bmAttributes = USB_ENDPOINT_XFER_INT;
+
+			/*
+			 * setting seems to 1 seem to cause issues with
+			 * Pinnacle timing out on transfer.
+			 */
+			if (is_pinnacle)
+				ep_out->bInterval = ep->bInterval;
+			else
+				ep_out->bInterval = 1;
+		}
+	}
+	if (ep_in == NULL) {
+		dev_dbg(&dev->dev, "inbound and/or endpoint not found\n");
+		return -ENODEV;
+	}
+	devnum = dev->devnum;
+	pipe = usb_rcvintpipe(dev, ep_in->bEndpointAddress);
+	maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
+
+	/* allocate kernel memory */
+	ir = kzalloc(sizeof(struct irctl), GFP_KERNEL);
+	if (!ir)
+		return -ENOMEM;
+
+	ir->buf_in = usb_buffer_alloc(dev, maxp, GFP_ATOMIC, &ir->dma_in);
+	if (!ir->buf_in) {
+		ret = -ENOMEM;
+		goto free_mem;
+	}
+	ir->urb_in = usb_alloc_urb(0, GFP_KERNEL);
+	if (!ir->urb_in) {
+		ret = -ENOMEM;
+		goto free_buffer;
+	}
+
+	ir->usbdev = dev;
+	ir->len_in = maxp;
+	ir->flags.connected = 0;
+	ir->flags.pinnacle = is_pinnacle;
+	ir->flags.transmitter_mask_inverted =
+		usb_match_id(intf, xmit_inverted) ? 0 : 1;
+
+	/* Saving usb interface data for use by the transmitter routine */
+	ir->usb_ep_in = ep_in;
+	ir->usb_ep_out = ep_out;
+
+	/* inbound data */
+	usb_fill_int_urb(ir->urb_in, dev, pipe, ir->buf_in,
+		maxp, (usb_complete_t) usb_remote_recv, ir, ep_in->bInterval);
+
+	/* initialize device */
+	if (ir->flags.pinnacle) {
+		int usbret;
+
+		/*
+		 * I have no idea why but this reset seems to be crucial to
+		 * getting the device to do outbound IO correctly - without
+		 * this the device seems to hang, ignoring all input - although
+		 * IR signals are correctly sent from the device, no input is
+		 * interpreted by the device and the host never does the
+		 * completion routine
+		 */
+
+		usbret = usb_reset_configuration(dev);
+		dev_info(&dev->dev, "usb reset config ret %x\n", usbret);
+
+		/*
+		 * its possible we really should wait for a return
+		 * for each of these...
+		 */
+		request_packet_async(ir, ep_in, NULL, maxp, PHILUSB_RECEIVE);
+		request_packet_async(ir, ep_out, pin_init1, sizeof(pin_init1), PHILUSB_SEND);
+		request_packet_async(ir, ep_in, NULL, maxp, PHILUSB_RECEIVE);
+		request_packet_async(ir, ep_out, pin_init2, sizeof(pin_init2), PHILUSB_SEND);
+		request_packet_async(ir, ep_in, NULL, maxp, PHILUSB_RECEIVE);
+		request_packet_async(ir, ep_out, pin_init3, sizeof(pin_init3), PHILUSB_SEND);
+		/* if we dont issue the correct number of receives
+		 * (PHILUSB_RECEIVE) for each outbound, then the first few ir
+		 * pulses will be interpreted by the usb_async_callback routine
+		 * - we should ensure we have the right amount OR less - as the
+		 * usb_remote_recv routine will handle the control packets OK -
+		 * they start with 0x9f - but the async callback doesnt handle
+		 * ir pulse packets
+		 */
+		request_packet_async(ir, ep_in, NULL, maxp, 0);
+	} else {
+		request_packet_async(ir, ep_in, NULL, maxp, PHILUSB_RECEIVE);
+		request_packet_async(ir, ep_out, init1, sizeof(init1), PHILUSB_SEND);
+		request_packet_async(ir, ep_in, NULL, maxp, PHILUSB_RECEIVE);
+		request_packet_async(ir, ep_out, init2, sizeof(init2), PHILUSB_SEND);
+		request_packet_async(ir, ep_in, NULL, maxp, 0);
+	}
+	usb_set_intfdata(intf, ir);
+
+	ir->input = input_allocate_device();
+	if (!ir->input) {
+		ret = -ENOMEM;
+		goto free_urb;
+	}
+	ret = input_ir_create(ir->input, ir, send);
+	if (ret)
+		goto free_input;
+
+	ir->name[0] = '\0';
+	if (dev->descriptor.iManufacturer)
+		usb_string(dev, dev->descriptor.iManufacturer, ir->name, sizeof(ir->name));
+	i = strlen(ir->name);
+	ir->name[i++] = ' ';
+	if (dev->descriptor.iProduct)
+		usb_string(dev, dev->descriptor.iProduct, &ir->name[i], sizeof(ir->name) - i);
+	dev_info(&dev->dev, "%s\n", ir->name);
+
+	ir->input->id.bustype = BUS_USB;
+	ir->input->id.vendor = le16_to_cpu(dev->descriptor.idVendor);
+	ir->input->id.product = le16_to_cpu(dev->descriptor.idProduct);
+	ir->input->name = ir->name;
+	usb_make_path(dev, ir->phys, sizeof(ir->phys));
+	strlcat(ir->phys, "/input0", sizeof(ir->phys));
+	ir->input->phys = ir->phys;
+
+	ir->input->irbit[0] |= BIT_MASK(IR_CAP_RECEIVE_BASEBAND);
+	ir->input->irbit[0] |= BIT_MASK(IR_CAP_RECEIVE_36K);
+	ir->input->irbit[0] |= BIT_MASK(IR_CAP_RECEIVE_38K);
+	ir->input->irbit[0] |= BIT_MASK(IR_CAP_RECEIVE_40K);
+	ir->input->irbit[0] |= BIT_MASK(IR_CAP_RECEIVE_56K);
+	ir->input->irbit[0] |= BIT_MASK(IR_CAP_SEND_BASEBAND);
+	ir->input->irbit[0] |= BIT_MASK(IR_CAP_SEND_36K);
+	ir->input->irbit[0] |= BIT_MASK(IR_CAP_SEND_38K);
+	ir->input->irbit[0] |= BIT_MASK(IR_CAP_SEND_40K);
+	ir->input->irbit[0] |= BIT_MASK(IR_CAP_SEND_56K);
+	ir->input->irbit[0] |= BIT_MASK(IR_CAP_XMITTER_1);
+	ir->input->irbit[0] |= BIT_MASK(IR_CAP_XMITTER_2);
+	ir->input->irbit[0] |= BIT_MASK(IR_CAP_RECEIVE_RAW);
+	ir->input->irbit[0] |= BIT_MASK(IR_CAP_SEND_RAW);
+
+	ret = input_register_device(ir->input);
+	if (ret)
+		goto free_input;
+
+	return 0;
+
+free_input:
+	input_ir_destroy(ir->input);
+	input_free_device(ir->input);
+free_urb:
+	usb_free_urb(ir->urb_in);
+free_buffer:
+	usb_buffer_free(dev, maxp, ir->buf_in, ir->dma_in);
+free_mem:
+	kfree(ir);
+	dev_err(&dev->dev, "failed to load (code=%d)\n", ret);
+	return ret;
+}
+
+
+static void usb_remote_disconnect(struct usb_interface *intf)
+{
+	struct usb_device *dev = interface_to_usbdev(intf);
+	struct irctl *ir = usb_get_intfdata(intf);
+
+	usb_set_intfdata(intf, NULL);
+
+	if (!ir)
+		return;
+
+	ir->usbdev = NULL;
+
+	usb_kill_urb(ir->urb_in);
+	usb_free_urb(ir->urb_in);
+	usb_buffer_free(dev, ir->len_in, ir->buf_in, ir->dma_in);
+}
+
+static int usb_remote_suspend(struct usb_interface *intf, pm_message_t message)
+{
+	struct irctl *ir = usb_get_intfdata(intf);
+	dev_dbg(&ir->usbdev->dev, "suspend\n");
+	usb_kill_urb(ir->urb_in);
+	return 0;
+}
+
+static int usb_remote_resume(struct usb_interface *intf)
+{
+	struct irctl *ir = usb_get_intfdata(intf);
+	dev_dbg(&ir->usbdev->dev, "resume\n");
+	if (usb_submit_urb(ir->urb_in, GFP_ATOMIC))
+		return -EIO;
+	return 0;
+}
+
+static struct usb_driver usb_remote_driver = {
+	.name =		DRIVER_NAME,
+	.probe =	usb_remote_probe,
+	.disconnect =	usb_remote_disconnect,
+	.suspend =	usb_remote_suspend,
+	.resume =	usb_remote_resume,
+	.id_table =	usb_remote_table
+};
+
+static int __init usb_remote_init(void)
+{
+	int ret;
+
+	ret = usb_register(&usb_remote_driver);
+	if (ret < 0) {
+		printk(DRIVER_NAME ": usb register failed, result = %d\n", ret);
+		return ret;
+	}
+	return 0;
+}
+
+static void __exit usb_remote_exit(void)
+{
+	usb_deregister(&usb_remote_driver);
+}
+
+module_init(usb_remote_init);
+module_exit(usb_remote_exit);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE(usb, usb_remote_table);


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* Re: [RFC PATCH 0/4] V3 - Implementation of IR support using the input subsystem
  2008-10-06 19:43 [RFC PATCH 0/4] V3 - Implementation of IR support using the input subsystem Jon Smirl
                   ` (3 preceding siblings ...)
  2008-10-06 19:43 ` [RFC PATCH 4/4] Microsoft mceusb2 driver for in-kernel IR subsystem Jon Smirl
@ 2008-10-06 19:46 ` Jon Smirl
  2008-10-09 12:03 ` Pavel Machek
  2009-05-28 15:06 ` Maxim Levitsky
  6 siblings, 0 replies; 16+ messages in thread
From: Jon Smirl @ 2008-10-06 19:46 UTC (permalink / raw)
  To: linux-kernel, lirc-list

On Mon, Oct 6, 2008 at 3:43 PM, Jon Smirl <jonsmirl@gmail.com> wrote:
> Third pass at implementing evdev support for IR. The goal of in-kernel IR is to integrate IR events into the evdev input event queue and maintain ordering of events from all input devices.


Mail to LIRC list still bounces because it is slightly too big - over
the 20K limit.

Messages are here:
http://lkml.org/lkml/2008/10/6/202


-- 
Jon Smirl
jonsmirl@gmail.com

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [RFC PATCH 0/4] V3 - Implementation of IR support using the input subsystem
  2008-10-06 19:43 [RFC PATCH 0/4] V3 - Implementation of IR support using the input subsystem Jon Smirl
                   ` (4 preceding siblings ...)
  2008-10-06 19:46 ` [RFC PATCH 0/4] V3 - Implementation of IR support using the input subsystem Jon Smirl
@ 2008-10-09 12:03 ` Pavel Machek
  2008-10-10  4:11   ` Jarod Wilson
  2009-05-28 15:06 ` Maxim Levitsky
  6 siblings, 1 reply; 16+ messages in thread
From: Pavel Machek @ 2008-10-09 12:03 UTC (permalink / raw)
  To: Jon Smirl; +Cc: linux-kernel, lirc-list

Hi!

>  What should the IR API look like?  How are IR events mapped into
> keyboard events? Should they be mapped? Map them in the kernel or in
> user space? The maps are tiny, less than 1K per remote.  Sysfs can
> be used to load maps into the kernel driver. Make maps only for the
> common buttons and don't map unusual ones?

Map them in kernel, in analogy with normal keyboards.  
 
> How should multiple remotes be handled? Split them out into
> individual input devices, or group them onto a single IR device? I
> can implement either.

Individual input devices, I'd say... so that app can only listen for
'its' remote.

-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [RFC PATCH 0/4] V3 - Implementation of IR support using the input subsystem
  2008-10-09 12:03 ` Pavel Machek
@ 2008-10-10  4:11   ` Jarod Wilson
  2008-10-10  5:04     ` Jon Smirl
  0 siblings, 1 reply; 16+ messages in thread
From: Jarod Wilson @ 2008-10-10  4:11 UTC (permalink / raw)
  To: Pavel Machek; +Cc: Jon Smirl, lirc-list, linux-kernel

On Thu, 2008-10-09 at 14:03 +0200, Pavel Machek wrote:
> Hi!
> 
> >  What should the IR API look like?  How are IR events mapped into
> > keyboard events? Should they be mapped? Map them in the kernel or in
> > user space? The maps are tiny, less than 1K per remote.  Sysfs can
> > be used to load maps into the kernel driver. Make maps only for the
> > common buttons and don't map unusual ones?
> 
> Map them in kernel, in analogy with normal keyboards.

My thought was to basically follow what the ati_remote{,2} driver does.

> > How should multiple remotes be handled? Split them out into
> > individual input devices, or group them onto a single IR device? I
> > can implement either.
> 
> Individual input devices, I'd say... so that app can only listen for
> 'its' remote.

I don't quite get it. How can we tell there are multiple remotes to set
up multiple input devices when the system comes up? All we can know
about at driver init time is the receiver(s), no? Or would this be keyed
off a config file? Or ______ ?


-- 
Jarod Wilson
jarod@wilsonet.com


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [RFC PATCH 0/4] V3 - Implementation of IR support using the input subsystem
  2008-10-10  4:11   ` Jarod Wilson
@ 2008-10-10  5:04     ` Jon Smirl
  2008-10-10 13:42       ` Jarod Wilson
  0 siblings, 1 reply; 16+ messages in thread
From: Jon Smirl @ 2008-10-10  5:04 UTC (permalink / raw)
  To: Jarod Wilson; +Cc: Pavel Machek, lirc-list, linux-kernel

On Fri, Oct 10, 2008 at 12:11 AM, Jarod Wilson <jarod@wilsonet.com> wrote:
> On Thu, 2008-10-09 at 14:03 +0200, Pavel Machek wrote:
>> Hi!
>>
>> >  What should the IR API look like?  How are IR events mapped into
>> > keyboard events? Should they be mapped? Map them in the kernel or in
>> > user space? The maps are tiny, less than 1K per remote.  Sysfs can
>> > be used to load maps into the kernel driver. Make maps only for the
>> > common buttons and don't map unusual ones?
>>
>> Map them in kernel, in analogy with normal keyboards.
>
> My thought was to basically follow what the ati_remote{,2} driver does.
>
>> > How should multiple remotes be handled? Split them out into
>> > individual input devices, or group them onto a single IR device? I
>> > can implement either.
>>
>> Individual input devices, I'd say... so that app can only listen for
>> 'its' remote.
>
> I don't quite get it. How can we tell there are multiple remotes to set
> up multiple input devices when the system comes up? All we can know
> about at driver init time is the receiver(s), no? Or would this be keyed
> off a config file? Or ______ ?


We could create a sysfs attribut named ir_config. For each config file
you copy to it it creates a new input device. The config files have
lists of map this protocol, device, command tuple to this key. When a
remote button is pressed the raw codes are fed to the in-kernel
protocol engine. That engine turns the raw codes into tuples. Tuples
are matched against the configs that have been loaded until a hit is
found. If no hit they get sent out the catch-all device.

Most remotes send out unique codes, they have to or they would turn on
unintended devices.


>
>
> --
> Jarod Wilson
> jarod@wilsonet.com
>
>



-- 
Jon Smirl
jonsmirl@gmail.com

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [RFC PATCH 0/4] V3 - Implementation of IR support using the input subsystem
  2008-10-10  5:04     ` Jon Smirl
@ 2008-10-10 13:42       ` Jarod Wilson
  2008-10-10 14:08         ` Jon Smirl
  0 siblings, 1 reply; 16+ messages in thread
From: Jarod Wilson @ 2008-10-10 13:42 UTC (permalink / raw)
  To: Jon Smirl; +Cc: Pavel Machek, lirc-list, linux-kernel

On Fri, 2008-10-10 at 01:04 -0400, Jon Smirl wrote:
> On Fri, Oct 10, 2008 at 12:11 AM, Jarod Wilson <jarod@wilsonet.com> wrote:
> > On Thu, 2008-10-09 at 14:03 +0200, Pavel Machek wrote:
[...]
> >> > How should multiple remotes be handled? Split them out into
> >> > individual input devices, or group them onto a single IR device? I
> >> > can implement either.
> >>
> >> Individual input devices, I'd say... so that app can only listen for
> >> 'its' remote.
> >
> > I don't quite get it. How can we tell there are multiple remotes to set
> > up multiple input devices when the system comes up? All we can know
> > about at driver init time is the receiver(s), no? Or would this be keyed
> > off a config file? Or ______ ?
> 
> 
> We could create a sysfs attribut named ir_config. For each config file
> you copy to it it creates a new input device. The config files have
> lists of map this protocol, device, command tuple to this key. When a
> remote button is pressed the raw codes are fed to the in-kernel
> protocol engine. That engine turns the raw codes into tuples. Tuples
> are matched against the configs that have been loaded until a hit is
> found. If no hit they get sent out the catch-all device.

Okay. I presume the catch-all device would simply note that it saw
something, and not actually take any action beyond that.

Also, a minor clarification: If its config file based, then its not so
much one input device per remote, its one input device per config file,
so one could set up two separate config files that are actually commands
from the same remote, but have them listened to by different apps, or
one could group together commands from multiple remotes in a single
config so that multiple remotes would all control the same thing. Sounds
good to me, provides for a reasonable amount of flexibility.

Of course, this suggests a receiver wouldn't be able to do anything
until the user provided a key mapping table of some sort. Would it make
sense to provide a default IR-to-key mapping for each receiver type? A
bit difficult to provide a sane default for something like a home-brew
serial IR receiver, but you could certainly provide sane defaults for
things like mceusb and imon (i.e., mappings for the remotes typically
bundled with those receivers).

> Most remotes send out unique codes, they have to or they would turn on
> unintended devices.

Yep, that's actually where the root of my concern was. I have at least a
half dozen different remote-controlled devices in my AV cabinet near my
myth box, and I obviously don't want anything but the codes I've set up
for my myth box acted upon on the myth box. :)


-- 
Jarod Wilson
jarod@wilsonet.com


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [RFC PATCH 0/4] V3 - Implementation of IR support using the input subsystem
  2008-10-10 13:42       ` Jarod Wilson
@ 2008-10-10 14:08         ` Jon Smirl
  2008-10-10 21:10           ` Jarod Wilson
  0 siblings, 1 reply; 16+ messages in thread
From: Jon Smirl @ 2008-10-10 14:08 UTC (permalink / raw)
  To: Jarod Wilson, Dmitry Torokhov; +Cc: Pavel Machek, lirc-list, linux-kernel

On Fri, Oct 10, 2008 at 9:42 AM, Jarod Wilson <jarod@wilsonet.com> wrote:
> On Fri, 2008-10-10 at 01:04 -0400, Jon Smirl wrote:
>> On Fri, Oct 10, 2008 at 12:11 AM, Jarod Wilson <jarod@wilsonet.com> wrote:
>> > On Thu, 2008-10-09 at 14:03 +0200, Pavel Machek wrote:
> [...]
>> >> > How should multiple remotes be handled? Split them out into
>> >> > individual input devices, or group them onto a single IR device? I
>> >> > can implement either.
>> >>
>> >> Individual input devices, I'd say... so that app can only listen for
>> >> 'its' remote.
>> >
>> > I don't quite get it. How can we tell there are multiple remotes to set
>> > up multiple input devices when the system comes up? All we can know
>> > about at driver init time is the receiver(s), no? Or would this be keyed
>> > off a config file? Or ______ ?
>>
>>
>> We could create a sysfs attribut named ir_config. For each config file
>> you copy to it it creates a new input device. The config files have
>> lists of map this protocol, device, command tuple to this key. When a
>> remote button is pressed the raw codes are fed to the in-kernel
>> protocol engine. That engine turns the raw codes into tuples. Tuples
>> are matched against the configs that have been loaded until a hit is
>> found. If no hit they get sent out the catch-all device.
>
> Okay. I presume the catch-all device would simply note that it saw
> something, and not actually take any action beyond that.

The catch-all device is an device in /dev/input. It gets the input
tuples in protocol/device/command form sent to it. I should probably
send all IR input to this device, and flag the events that were
translated and sent to another input device.

>
> Also, a minor clarification: If its config file based, then its not so
> much one input device per remote, its one input device per config file,
> so one could set up two separate config files that are actually commands
> from the same remote, but have them listened to by different apps, or
> one could group together commands from multiple remotes in a single
> config so that multiple remotes would all control the same thing. Sounds
> good to me, provides for a reasonable amount of flexibility.

That is how it works.

> Of course, this suggests a receiver wouldn't be able to do anything
> until the user provided a key mapping table of some sort. Would it make
> sense to provide a default IR-to-key mapping for each receiver type? A
> bit difficult to provide a sane default for something like a home-brew
> serial IR receiver, but you could certainly provide sane defaults for
> things like mceusb and imon (i.e., mappings for the remotes typically
> bundled with those receivers).

Receivers can process the raw tuples using the catch-all device.

You could write a little app like udev that listens to the catch-all
device and then search the config files for untranslated tuples. When
you match on an untranslated tuple, load that config file into the
kernel.

I'm taking the kids on their first trip to Disneyworld next week so I
won't be able to work on this for a while.

>
>> Most remotes send out unique codes, they have to or they would turn on
>> unintended devices.
>
> Yep, that's actually where the root of my concern was. I have at least a
> half dozen different remote-controlled devices in my AV cabinet near my
> myth box, and I obviously don't want anything but the codes I've set up
> for my myth box acted upon on the myth box. :)
>
>
> --
> Jarod Wilson
> jarod@wilsonet.com
>
>



-- 
Jon Smirl
jonsmirl@gmail.com

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [RFC PATCH 0/4] V3 - Implementation of IR support using the input subsystem
  2008-10-10 14:08         ` Jon Smirl
@ 2008-10-10 21:10           ` Jarod Wilson
  0 siblings, 0 replies; 16+ messages in thread
From: Jarod Wilson @ 2008-10-10 21:10 UTC (permalink / raw)
  To: Jon Smirl; +Cc: Dmitry Torokhov, Pavel Machek, lirc-list, linux-kernel

On Fri, 2008-10-10 at 10:08 -0400, Jon Smirl wrote:
> On Fri, Oct 10, 2008 at 9:42 AM, Jarod Wilson <jarod@wilsonet.com> wrote:
> > On Fri, 2008-10-10 at 01:04 -0400, Jon Smirl wrote:
> >> On Fri, Oct 10, 2008 at 12:11 AM, Jarod Wilson <jarod@wilsonet.com> wrote:
> >> > On Thu, 2008-10-09 at 14:03 +0200, Pavel Machek wrote:
> > [...]
> >> >> > How should multiple remotes be handled? Split them out into
> >> >> > individual input devices, or group them onto a single IR device? I
> >> >> > can implement either.
> >> >>
> >> >> Individual input devices, I'd say... so that app can only listen for
> >> >> 'its' remote.
> >> >
> >> > I don't quite get it. How can we tell there are multiple remotes to set
> >> > up multiple input devices when the system comes up? All we can know
> >> > about at driver init time is the receiver(s), no? Or would this be keyed
> >> > off a config file? Or ______ ?
> >>
> >>
> >> We could create a sysfs attribut named ir_config. For each config file
> >> you copy to it it creates a new input device. The config files have
> >> lists of map this protocol, device, command tuple to this key. When a
> >> remote button is pressed the raw codes are fed to the in-kernel
> >> protocol engine. That engine turns the raw codes into tuples. Tuples
> >> are matched against the configs that have been loaded until a hit is
> >> found. If no hit they get sent out the catch-all device.
> >
> > Okay. I presume the catch-all device would simply note that it saw
> > something, and not actually take any action beyond that.
> 
> The catch-all device is an device in /dev/input. It gets the input
> tuples in protocol/device/command form sent to it. I should probably
> send all IR input to this device, and flag the events that were
> translated and sent to another input device.

Sounds sane.

> > Also, a minor clarification: If its config file based, then its not so
> > much one input device per remote, its one input device per config file,
> > so one could set up two separate config files that are actually commands
> > from the same remote, but have them listened to by different apps, or
> > one could group together commands from multiple remotes in a single
> > config so that multiple remotes would all control the same thing. Sounds
> > good to me, provides for a reasonable amount of flexibility.
> 
> That is how it works.

Cool. Still getting this all straight in my head... :)

> > Of course, this suggests a receiver wouldn't be able to do anything
> > until the user provided a key mapping table of some sort. Would it make
> > sense to provide a default IR-to-key mapping for each receiver type? A
> > bit difficult to provide a sane default for something like a home-brew
> > serial IR receiver, but you could certainly provide sane defaults for
> > things like mceusb and imon (i.e., mappings for the remotes typically
> > bundled with those receivers).
> 
> Receivers can process the raw tuples using the catch-all device.
> 
> You could write a little app like udev that listens to the catch-all
> device and then search the config files for untranslated tuples. When
> you match on an untranslated tuple, load that config file into the
> kernel.

That's just the sort of thing I *don't* want though -- I can imagine my
receiver picking up all the signals intended for my other AV devices and
loading up a bunch of configs, thinking it should do something with
these signals. Seems you'd want a training/pairing mode you could put
the driver in to set this stuff up.

> I'm taking the kids on their first trip to Disneyworld next week so I
> won't be able to work on this for a while.

Have fun! In the mean time, I've got a bit of porting to try out still,
as well as some remaining review clean-ups...



-- 
Jarod Wilson
jarod@wilsonet.com


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [RFC PATCH 0/4] V3 - Implementation of IR support using the input subsystem
  2008-10-06 19:43 [RFC PATCH 0/4] V3 - Implementation of IR support using the input subsystem Jon Smirl
                   ` (5 preceding siblings ...)
  2008-10-09 12:03 ` Pavel Machek
@ 2009-05-28 15:06 ` Maxim Levitsky
  2009-05-28 15:58   ` Maxim Levitsky
  6 siblings, 1 reply; 16+ messages in thread
From: Maxim Levitsky @ 2009-05-28 15:06 UTC (permalink / raw)
  To: Jon Smirl; +Cc: linux-kernel, lirc-list

On Mon, 2008-10-06 at 15:43 -0400, Jon Smirl wrote:
> Third pass at implementing evdev support for IR. The goal of in-kernel IR is to integrate IR events into the evdev input event queue and maintain ordering of events from all input devices.
> 
> New feature, raw mode. There are three sysfs attributes - ir_raw, ir_carrier, ir_xmitter. Read from ir_raw to get the raw timing data from the IR device. Set carrier and active xmitters and then copy raw data to ir_raw to send. These attributes may be better on a debug switch. You would use raw mode when decoding a new protocol. After you figure out the new protocol, write an in-kernel encoder/decoder for it.
> 
> What should the IR API look like? 
> 
> How are IR events mapped into keyboard events? Should they be mapped? Map them in the kernel or in user space? The maps are tiny, less than 1K per remote.  Sysfs can be used to load maps into the kernel driver. Make maps only for the common buttons and don't map unusual ones?
> 
> How should multiple remotes be handled? Split them out into individual input devices, or group them onto a single IR device? I can implement either.
> 
> The in-kernel code is tiny, about 20K including a driver.
> 
> >From last post...
> Note that user space IR device drivers can use the existing support in evdev to inject events into the input queue.
> 
> Send and receive are implemented. Received IR messages are decoded and sent to user space as input messages. Send is done via an IOCTL on the input device.
> 
> Two drivers are supplied. mceusb2 implements send and receive support for the Microsoft USB IR dongle.
> 
> The GPT driver implements receive only support for a GPT pin - GPT is a GPIO with a timer attached.
> 
> Code is only lightly tested. Encoders and decoders have not been written for all protocols. 
> Repeat is not handled for any protocol.
> I'm looking for help. There are 15 more existing LIRC drivers.

Any update on that?

I have a suggestion:


Instead of decoding protocols in kernel, why not to allow userspace to
upload timing signatures as specified in lirc.conf ?

As a example, what protocol this is:

http://lirc.sourceforge.net/remotes/toshiba/CT-9922

Best regards,
	Maxim Levitsky


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [RFC PATCH 0/4] V3 - Implementation of IR support using the input subsystem
  2009-05-28 15:06 ` Maxim Levitsky
@ 2009-05-28 15:58   ` Maxim Levitsky
  0 siblings, 0 replies; 16+ messages in thread
From: Maxim Levitsky @ 2009-05-28 15:58 UTC (permalink / raw)
  To: Jon Smirl; +Cc: linux-kernel, lirc-list

On Thu, 2009-05-28 at 18:06 +0300, Maxim Levitsky wrote:
> On Mon, 2008-10-06 at 15:43 -0400, Jon Smirl wrote:
> > Third pass at implementing evdev support for IR. The goal of in-kernel IR is to integrate IR events into the evdev input event queue and maintain ordering of events from all input devices.
> > 
> > New feature, raw mode. There are three sysfs attributes - ir_raw, ir_carrier, ir_xmitter. Read from ir_raw to get the raw timing data from the IR device. Set carrier and active xmitters and then copy raw data to ir_raw to send. These attributes may be better on a debug switch. You would use raw mode when decoding a new protocol. After you figure out the new protocol, write an in-kernel encoder/decoder for it.
> > 
> > What should the IR API look like? 
> > 
> > How are IR events mapped into keyboard events? Should they be mapped? Map them in the kernel or in user space? The maps are tiny, less than 1K per remote.  Sysfs can be used to load maps into the kernel driver. Make maps only for the common buttons and don't map unusual ones?
> > 
> > How should multiple remotes be handled? Split them out into individual input devices, or group them onto a single IR device? I can implement either.
> > 
> > The in-kernel code is tiny, about 20K including a driver.
> > 
> > >From last post...
> > Note that user space IR device drivers can use the existing support in evdev to inject events into the input queue.
> > 
> > Send and receive are implemented. Received IR messages are decoded and sent to user space as input messages. Send is done via an IOCTL on the input device.
> > 
> > Two drivers are supplied. mceusb2 implements send and receive support for the Microsoft USB IR dongle.
> > 
> > The GPT driver implements receive only support for a GPT pin - GPT is a GPIO with a timer attached.
> > 
> > Code is only lightly tested. Encoders and decoders have not been written for all protocols. 
> > Repeat is not handled for any protocol.
> > I'm looking for help. There are 15 more existing LIRC drivers.
> 
> Any update on that?
> 
> I have a suggestion:
> 
> 
> Instead of decoding protocols in kernel, why not to allow userspace to
> upload timing signatures as specified in lirc.conf ?
> 
> As a example, what protocol this is:
> 
> http://lirc.sourceforge.net/remotes/toshiba/CT-9922

Replying to myself... this is NEC protocol, and this config file isn't
correct (since it was auto generated)
> 
> Best regards,
> 	Maxim Levitsky
> 


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [RFC PATCH 1/4] Changes to core input subsystem to allow send and receive of IR messages. Encode and decode state machines are provided for common IR porotocols such as Sony, JVC, NEC, Philips, etc.
  2008-10-06 19:43 ` [RFC PATCH 1/4] Changes to core input subsystem to allow send and receive of IR messages. Encode and decode state machines are provided for common IR porotocols such as Sony, JVC, NEC, Philips, etc Jon Smirl
@ 2009-05-28 23:23   ` Maxim Levitsky
  2009-05-29  1:15     ` Jon Smirl
  0 siblings, 1 reply; 16+ messages in thread
From: Maxim Levitsky @ 2009-05-28 23:23 UTC (permalink / raw)
  To: Jon Smirl; +Cc: linux-kernel, lirc-list

On Mon, 2008-10-06 at 15:43 -0400, Jon Smirl wrote:
> Received IR messages generate event in the input queue.
> IR messages are sent using an input IOCTL.

Another question.

So input device sends its events as a special event type?
This would mean that none of current drivers will be able to pick these
events.


I think that input device should have better configuration support, like
generic way to set a keymap (raw->keycode) for a device, and the IR
timing can be set too.
And send normal keypreses.

Regards,
	Maxim Levitsky


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [RFC PATCH 1/4] Changes to core input subsystem to allow send and  receive of IR messages. Encode and decode state machines are provided for  common IR porotocols such as Sony, JVC, NEC, Philips, etc.
  2009-05-28 23:23   ` Maxim Levitsky
@ 2009-05-29  1:15     ` Jon Smirl
  0 siblings, 0 replies; 16+ messages in thread
From: Jon Smirl @ 2009-05-29  1:15 UTC (permalink / raw)
  To: Maxim Levitsky; +Cc: linux-kernel, lirc-list

On Thu, May 28, 2009 at 7:23 PM, Maxim Levitsky <maximlevitsky@gmail.com> wrote:
> On Mon, 2008-10-06 at 15:43 -0400, Jon Smirl wrote:
>> Received IR messages generate event in the input queue.
>> IR messages are sent using an input IOCTL.
>
> Another question.
>
> So input device sends its events as a special event type?
> This would mean that none of current drivers will be able to pick these
> events.

There is a later version of this patch set that uses configfs to map
IR events into keystrokes on the /dev/input device. I have later
additions to the code that haven't been posted. They fix issues with
calling configfs at interrupt time.

-------------------------------------------

I posted V4 of my in-kernel IR system to LKML.  It supports mapping
from IR events to key strokes now.
http://lkml.org/lkml/2008/11/5/233

To do the mapping it uses configfs (part of the kernel).  The main
directory is remotes. You use a shell script to build mappings between
the IR event and key stroke.

mkdir /config/remotes/sony
 -- this creates a new evdev device
mkdir remotes/sony/one
echo 7 >remotes/sony/one/procotol
echo 264 >remotes/sony/one/command
echo 2 >remotes/sony/one/keycode

This transforms a button push of 1 on my remote into a key stroke for KEY_1

 *   configfs root
 *   --remotes
 *   ----specific remote
 *   ------keymap
 *   --------protocol
 *   --------device
 *   --------command
 *   --------keycode
 *   ------repeat keymaps
 *   --------....
 *   ----another remote
 *   ------more keymaps

You can map the 1 button from multiple remote to KEY_1 if you want. Or
you can use a single remote to create multiple virtual keyboards.

I'm always looking for help with this code. I've implemented support
for my embedded GPIO based receiver and Microsoft MCEUSB. I'd really
like to see IR added to existing Linux input family of keyboards,
mice, joystick, tablet, touchscreen and gameport.


>
>
> I think that input device should have better configuration support, like
> generic way to set a keymap (raw->keycode) for a device, and the IR
> timing can be set too.
> And send normal keypreses.
>
> Regards,
>        Maxim Levitsky
>
>



-- 
Jon Smirl
jonsmirl@gmail.com

^ permalink raw reply	[flat|nested] 16+ messages in thread

end of thread, other threads:[~2009-05-29  1:15 UTC | newest]

Thread overview: 16+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2008-10-06 19:43 [RFC PATCH 0/4] V3 - Implementation of IR support using the input subsystem Jon Smirl
2008-10-06 19:43 ` [RFC PATCH 1/4] Changes to core input subsystem to allow send and receive of IR messages. Encode and decode state machines are provided for common IR porotocols such as Sony, JVC, NEC, Philips, etc Jon Smirl
2009-05-28 23:23   ` Maxim Levitsky
2009-05-29  1:15     ` Jon Smirl
2008-10-06 19:43 ` [RFC PATCH 2/4] GPT driver for in-kernel IR support Jon Smirl
2008-10-06 19:43 ` [RFC PATCH 3/4] Example of PowerPC device tree support for GPT based IR Jon Smirl
2008-10-06 19:43 ` [RFC PATCH 4/4] Microsoft mceusb2 driver for in-kernel IR subsystem Jon Smirl
2008-10-06 19:46 ` [RFC PATCH 0/4] V3 - Implementation of IR support using the input subsystem Jon Smirl
2008-10-09 12:03 ` Pavel Machek
2008-10-10  4:11   ` Jarod Wilson
2008-10-10  5:04     ` Jon Smirl
2008-10-10 13:42       ` Jarod Wilson
2008-10-10 14:08         ` Jon Smirl
2008-10-10 21:10           ` Jarod Wilson
2009-05-28 15:06 ` Maxim Levitsky
2009-05-28 15:58   ` Maxim Levitsky

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).