2009-11-27 01:34:21

by [email protected]

[permalink] [raw]
Subject: [IR-RFC PATCH v4 0/6] In-kernel IR support using evdev

This is a repost of the LIRC input patch series I wrote a year ago.
Many of the ideas being discussed in the currect "Should we create a
raw input interface for IR's?" thread have already been implemented
in this code.

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

All of this kernel code is tiny, about 20K including a driver.

Basic flow works like this:

patch: Minimal changes to the core input system
Add a new IR data type to the input framework

patch: Core IR module
In-kernel decoding of core IR protocols - RC5, RC6, etc
This only converts from protocol pulse timing to common command format.
Check out: http://www.sbprojects.com/knowledge/ir/sirc.htm

patch: Configfs support for IR
Decoded core protocols pass through a translation map based on configfs
When core protocol matches an entry in configfs it is turned into a
keycode event.

patch: Microsoft mceusb2 driver for in-kernel IR subsystem
Example mceusb IR input driver

patch: the other ones are an embedded driver using a GPT pin.

You make a directory in /configfs/remotes for each remote you have.
Making the directory creates a new evdev device. Under the directory
make an entry for each command generated by the device. These entries
cause the decoded IR data to be mapped into keycodes on the new evdev
device. udev would load these configfs mappings at boot time...

mkdir /config/remotes/sony
-- this creates a new evdev device
mkdir remotes/sony/one
echo 7 >remotes/sony/one/protocol
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 remotes to KEY_1 if you want. Or
you can use a single remote to create multiple virtual keyboards.

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

Raw IR pulse data is available in a FIFO via sysfs. You can use this
to figure out new remote protocols.

Two input events are generated
1) an event for the decoded raw IR protocol
2) a keycode event if thedecoded raw IR protocol matches an entry in
the configfs

You can also send pulses.

------

If you want to script this, you would have a user space task that
watches for either the decoded IR codes or the mapped keycodes.

This system also works with user space device drivers. They can inject
input events into the early event flow and they will get processed as
if the event originated in the kernel.

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

Sure you could push the protocol decoding code (RC5, etc) into user
space. Seems like a lot of hassle to move about 3KB of code out of the
kernel.

--------------------
previos comment on the patches...

Now when a key is pressed on a remote, the configfs directories are
searched for a match on protocol, device, command. If a matches is
found, a key stroke corresponding to keycode is created and sent on
the input device that was created when the directory for the remote
was made.

The configfs directories are pretty flexible. You can use them to map
multiple remotes to the same key stroke, or send a single button push
to multiple apps.

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.

>From last release...

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.

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 (6):
Minimal changes to the core input system
Core IR module
Configfs support for IR
GPT driver for in-kernel IR support.
Example of PowerPC device tree support for GPT based IR
Microsoft mceusb2 driver for in-kernel IR subsystem


arch/powerpc/boot/dts/dspeak01.dts | 19 -
drivers/input/Kconfig | 2
drivers/input/Makefile | 1
drivers/input/input.c | 20 +
drivers/input/ir/Kconfig | 27 +
drivers/input/ir/Makefile | 12 +
drivers/input/ir/ir-configfs.c | 348 +++++++++++++++++
drivers/input/ir/ir-core.c | 730 +++++++++++++++++++++++++++++++++++
drivers/input/ir/ir-gpt.c | 184 +++++++++
drivers/input/ir/ir-mceusb2.c | 745 ++++++++++++++++++++++++++++++++++++
drivers/input/ir/ir.h | 63 +++
include/linux/input.h | 76 ++++
include/linux/mod_devicetable.h | 3
13 files changed, 2219 insertions(+), 11 deletions(-)
create mode 100644 drivers/input/ir/Kconfig
create mode 100644 drivers/input/ir/Makefile
create mode 100644 drivers/input/ir/ir-configfs.c
create mode 100644 drivers/input/ir/ir-core.c
create mode 100644 drivers/input/ir/ir-gpt.c
create mode 100644 drivers/input/ir/ir-mceusb2.c
create mode 100644 drivers/input/ir/ir.h

--
Jon Smirl
[email protected]


2009-11-27 01:34:31

by [email protected]

[permalink] [raw]
Subject: [IR-RFC PATCH v4 1/6] Minimal changes to the core input system

Minimal changes to the core input system. The bulk of IR support loads as a module. These changes are passive if the rest of IR isn't loaded.

Jon Smirl
<[email protected]>
---
drivers/input/input.c | 17 +++++++++
include/linux/input.h | 75 +++++++++++++++++++++++++++++++++++++++
include/linux/mod_devicetable.h | 3 ++
3 files changed, 95 insertions(+), 0 deletions(-)

diff --git a/drivers/input/input.c b/drivers/input/input.c
index 7c237e6..a531cf5 100644
--- a/drivers/input/input.c
+++ b/drivers/input/input.c
@@ -274,6 +274,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)
@@ -727,6 +731,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;
}
@@ -849,6 +854,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');

@@ -1024,6 +1031,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");
@@ -1125,6 +1134,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,
@@ -1136,6 +1146,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
};

@@ -1253,6 +1264,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);

@@ -1371,6 +1384,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;
diff --git a/include/linux/input.h b/include/linux/input.h
index 8b3bc3e..159a99d 100644
--- a/include/linux/input.h
+++ b/include/linux/input.h
@@ -80,6 +80,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 */

/*
@@ -98,6 +100,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)

@@ -985,6 +988,56 @@ struct ff_effect {
#define FF_MAX 0x7f
#define FF_CNT (FF_MAX+1)

+/*
+ * IR Support
+ */
+
+#define IR_PROTOCOL_RESERVED 0
+#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__

/*
@@ -1012,6 +1065,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
@@ -1084,6 +1138,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;
@@ -1092,6 +1147,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;
@@ -1328,6 +1384,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);
@@ -1411,5 +1472,19 @@ 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 *));

+/**
+ * IR support functions
+ */
+
+typedef int (*send_func)(void *private, unsigned int *buffer, unsigned int count,
+ unsigned int frequency, unsigned int xmitters);
+
+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 1bf5900..2bdf253 100644
--- a/include/linux/mod_devicetable.h
+++ b/include/linux/mod_devicetable.h
@@ -293,6 +293,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
@@ -308,6 +309,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 {

@@ -327,6 +329,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;
};

2009-11-27 01:34:45

by [email protected]

[permalink] [raw]
Subject: [IR-RFC PATCH v4 2/6] Core IR module

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.

Received IR messages generate event in the input queue.
IR messages are sent using an input IOCTL.
---
drivers/input/Kconfig | 2
drivers/input/Makefile | 1
drivers/input/input.c | 3
drivers/input/ir/Kconfig | 15 +
drivers/input/ir/Makefile | 8
drivers/input/ir/ir-core.c | 735 ++++++++++++++++++++++++++++++++++++++++++++
drivers/input/ir/ir.h | 63 ++++
include/linux/input.h | 5
8 files changed, 830 insertions(+), 2 deletions(-)
create mode 100644 drivers/input/ir/Kconfig
create mode 100644 drivers/input/ir/Makefile
create mode 100644 drivers/input/ir/ir-core.c
create mode 100644 drivers/input/ir/ir.h

diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig
index 816639c..a1e35ee 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 99e2b5e..fec8972 100644
--- a/drivers/input/Makefile
+++ b/drivers/input/Makefile
@@ -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/input.c b/drivers/input/input.c
index a531cf5..2843b84 100644
--- a/drivers/input/input.c
+++ b/drivers/input/input.c
@@ -23,6 +23,8 @@
#include <linux/rcupdate.h>
#include <linux/smp_lock.h>

+#include "ir/ir.h"
+
MODULE_AUTHOR("Vojtech Pavlik <[email protected]>");
MODULE_DESCRIPTION("Input core");
MODULE_LICENSE("GPL");
@@ -1167,6 +1169,7 @@ static void input_dev_release(struct device *device)
struct input_dev *dev = to_input_dev(device);

input_ff_destroy(dev);
+ input_ir_destroy(dev);
kfree(dev);

module_put(THIS_MODULE);
diff --git a/drivers/input/ir/Kconfig b/drivers/input/ir/Kconfig
new file mode 100644
index 0000000..a6f3f25
--- /dev/null
+++ b/drivers/input/ir/Kconfig
@@ -0,0 +1,15 @@
+#
+# LIRC driver(s) configuration
+#
+menuconfig INPUT_IR
+ select CONFIGFS_FS
+ tristate "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..6acb665
--- /dev/null
+++ b/drivers/input/ir/Makefile
@@ -0,0 +1,8 @@
+# Makefile for the ir drivers.
+#
+
+# Each configuration option enables a list of files.
+
+obj-$(CONFIG_INPUT_IR) += ir.o
+ir-objs := ir-core.o
+
diff --git a/drivers/input/ir/ir-core.c b/drivers/input/ir/ir-core.c
new file mode 100644
index 0000000..85adfcb
--- /dev/null
+++ b/drivers/input/ir/ir-core.c
@@ -0,0 +1,735 @@
+/*
+ * Core routines for IR support
+ *
+ * Copyright (C) 2008 Jon Smirl <[email protected]>
+ */
+
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/input.h>
+
+#include "ir.h"
+
+void input_ir_translate(struct input_dev *dev, int protocol, int device, int command)
+{
+ /* generate the IR format event */
+ input_report_ir(dev, IR_PROTOCOL, protocol);
+ input_report_ir(dev, IR_DEVICE, device);
+ input_report_ir(dev, IR_COMMAND, command);
+ input_sync(dev);
+}
+
+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;
+ int protocol, device, command;
+
+ 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)) {
+
+ protocol = (sony->state == 26) ? IR_PROTOCOL_SONY_12 :
+ (sony->state == 32) ? IR_PROTOCOL_SONY_15 : IR_PROTOCOL_SONY_20;
+
+ if (sony->state == 26) {
+ device = sony->code & 0x1F;
+ command = sony->code >> 5;
+ } else {
+ device = sony->code & 0xFF;
+ command = sony->code >> 8;
+ }
+ input_ir_translate(dev, protocol, device, command);
+ 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_ir_translate(dev, IR_PROTOCOL_JVC, jvc->code >> 8, jvc->code & 0xFF);
+ 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_ir_translate(dev, IR_PROTOCOL_NEC, nec->code >> 16, nec->code & 0xFFFF);
+ 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_ir_translate(dev, IR_PROTOCOL_PHILIPS_RC6, rc6->code >> 8, rc6->code & 0xFF);
+ 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, int sample)
+{
+ int head = dev->ir->raw.head;
+
+ head += 1;
+ if (head > ARRAY_SIZE(dev->ir->raw.buffer))
+ head = 0;
+
+ if (head != dev->ir->raw.tail) {
+ dev->ir->raw.buffer[dev->ir->raw.head] = sample;
+ dev->ir->raw.head = head;
+ }
+}
+
+void input_ir_decode(struct input_dev *dev, int sample)
+{
+ int delta, bit;
+
+ record_raw(dev, sample);
+
+ if (sample < 0) {
+ delta = -sample;
+ bit = 1;
+ } else {
+ delta = sample;
+ bit = 0;
+ }
+ PDEBUG("IR bit %d %d\n", 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 void ir_event(struct work_struct *work)
+{
+ unsigned long flags;
+ unsigned int sample;
+ struct ir_device *ir_dev = container_of(work, struct ir_device, work);
+ struct input_dev *dev = ir_dev->input;
+
+ while (1) {
+ spin_lock_irqsave(dev->ir->queue.lock, flags);
+ if (dev->ir->queue.tail == dev->ir->queue.head) {
+ spin_unlock_irqrestore(dev->ir->queue.lock, flags);
+ break;
+ }
+ sample = dev->ir->queue.samples[dev->ir->queue.tail];
+
+ dev->ir->queue.tail++;
+ if (dev->ir->queue.tail >= MAX_SAMPLES)
+ dev->ir->queue.tail = 0;
+
+ spin_unlock_irqrestore(dev->ir->queue.lock, flags);
+
+ input_ir_decode(dev->ir->input, sample);
+ }
+}
+
+void input_ir_queue(struct input_dev *dev, int sample)
+{
+ unsigned int next;
+
+ spin_lock(dev->ir->queue.lock);
+ dev->ir->queue.samples[dev->ir->queue.head] = sample;
+ next = dev->ir->queue.head + 1;
+ dev->ir->queue.head = (next >= MAX_SAMPLES ? 0 : next);
+ spin_unlock(dev->ir->queue.lock);
+
+ schedule_work(&dev->ir->work);
+}
+EXPORT_SYMBOL_GPL(input_ir_queue);
+
+
+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);
+
+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, count = 0;
+
+ for (i = input_dev->ir->raw.tail; i != input_dev->ir->raw.head; ) {
+
+ count += snprintf(&buf[count], PAGE_SIZE - 1, "%i\n", input_dev->ir->raw.buffer[i++]);
+ if (i > ARRAY_SIZE(input_dev->ir->raw.buffer))
+ i = 0;
+ if (count >= PAGE_SIZE - 1) {
+ input_dev->ir->raw.tail = i;
+ return PAGE_SIZE - 1;
+ }
+ }
+ 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 ssize_t ir_debug_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_debug_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(raw, S_IRUGO | S_IWUSR, ir_raw_show, ir_raw_store);
+static DEVICE_ATTR(carrier, S_IRUGO | S_IWUSR, ir_carrier_show, ir_carrier_store);
+static DEVICE_ATTR(xmitter, S_IRUGO | S_IWUSR, ir_xmitter_show, ir_xmitter_store);
+static DEVICE_ATTR(debug, S_IRUGO | S_IWUSR, ir_debug_show, ir_debug_store);
+
+static struct attribute *input_ir_attrs[] = {
+ &dev_attr_raw.attr,
+ &dev_attr_carrier.attr,
+ &dev_attr_xmitter.attr,
+ &dev_attr_debug.attr,
+ NULL
+ };
+
+static struct attribute_group input_ir_group = {
+ .name = "ir",
+ .attrs = input_ir_attrs,
+};
+
+int input_ir_register(struct input_dev *dev)
+{
+ if (!dev->ir)
+ return 0;
+
+ return sysfs_create_group(&dev->dev.kobj, &input_ir_group);
+}
+
+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;
+ dev->ir->input = dev;
+
+ spin_lock_init(&dev->ir->queue.lock);
+ INIT_WORK(&dev->ir->work, ir_event);
+
+ 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;
+ sysfs_remove_group(&dev->dev.kobj, &input_ir_group);
+ }
+}
+
+static int __init input_ir_init(void)
+{
+ return 0;
+}
+module_init(input_ir_init);
+
+static void __exit input_ir_exit(void)
+{
+}
+module_exit(input_ir_exit);
diff --git a/drivers/input/ir/ir.h b/drivers/input/ir/ir.h
new file mode 100644
index 0000000..d0cdc9e
--- /dev/null
+++ b/drivers/input/ir/ir.h
@@ -0,0 +1,63 @@
+/*
+ * Private defines for IR support
+ *
+ * Copyright (C) 2008 Jon Smirl <[email protected]>
+ */
+
+#include <linux/configfs.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
+
+struct ir_protocol {
+ unsigned int state, code, good, count, bits, mode;
+};
+
+#define MAX_SAMPLES 200
+
+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 input_dev *input;
+ struct {
+ unsigned int buffer[MAX_SAMPLES];
+ unsigned int count;
+ } send;
+ struct {
+ int buffer[MAX_SAMPLES];
+ unsigned int head;
+ unsigned int tail;
+ unsigned int carrier;
+ unsigned int xmitter;
+ } raw;
+ struct {
+ spinlock_t lock;
+ int head, tail;
+ unsigned int samples[MAX_SAMPLES];
+ } queue;
+ struct work_struct work;
+};
+
+extern struct configfs_subsystem input_ir_remotes;
+void input_ir_translate(struct input_dev *dev, int protocol, int device, int command);
+
+#ifdef CONFIG_INPUT_IR
+void input_ir_destroy(struct input_dev *dev);
+#else
+static inline void input_ir_destroy(struct input_dev *dev) {}
+#endif
+
+
+
diff --git a/include/linux/input.h b/include/linux/input.h
index 159a99d..08f7bda 100644
--- a/include/linux/input.h
+++ b/include/linux/input.h
@@ -1480,9 +1480,10 @@ typedef int (*send_func)(void *private, unsigned int *buffer, unsigned int count
unsigned int frequency, unsigned int xmitters);

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);
+void input_ir_decode(struct input_dev *dev, int sample);
+void input_ir_queue(struct input_dev *dev, int sample);
+
int input_ir_send(struct input_dev *dev, struct ir_command *ir_command, struct file *file);
int input_ir_register(struct input_dev *dev);

2009-11-27 01:34:54

by [email protected]

[permalink] [raw]
Subject: [IR-RFC PATCH v4 3/6] Configfs support for IR

Now uses configfs to build mappings from remote buttons to key strokes. When ir-core loads it creates /config/remotes. Make a directory for each remote you have; this will cause a new input devices to be created. Inside these directories make a directory for each key on the remote. In the key directory attributes fill in the protocol, device, command, keycode. Since this is configfs all of this can be easily scripted.

Now when a key is pressed on a remote, the configfs directories are searched for a match on protocol, device, command. If a matches is found, a key stroke corresponding to keycode is created and sent on the input device that was created when the directory for the remote was made.

The configfs directories are pretty flexible. You can use them to map multiple remotes to the same key stroke, or send a single button push to multiple apps.
---
drivers/input/ir/Makefile | 2
drivers/input/ir/ir-configfs.c | 348 ++++++++++++++++++++++++++++++++++++++++
drivers/input/ir/ir-core.c | 15 +-
3 files changed, 354 insertions(+), 11 deletions(-)
create mode 100644 drivers/input/ir/ir-configfs.c

diff --git a/drivers/input/ir/Makefile b/drivers/input/ir/Makefile
index 6acb665..2ccdda3 100644
--- a/drivers/input/ir/Makefile
+++ b/drivers/input/ir/Makefile
@@ -4,5 +4,5 @@
# Each configuration option enables a list of files.

obj-$(CONFIG_INPUT_IR) += ir.o
-ir-objs := ir-core.o
+ir-objs := ir-core.o ir-configfs.o

diff --git a/drivers/input/ir/ir-configfs.c b/drivers/input/ir/ir-configfs.c
new file mode 100644
index 0000000..e0819bb
--- /dev/null
+++ b/drivers/input/ir/ir-configfs.c
@@ -0,0 +1,348 @@
+/*
+ * Configfs routines for IR support
+ *
+ * configfs root
+ * --remotes
+ * ----specific remote
+ * ------keymap
+ * --------protocol
+ * --------device
+ * --------command
+ * --------keycode
+ * ------repeat keymaps
+ * --------....
+ * ----another remote
+ * ------more keymaps
+ * --------....
+ *
+ * Copyright (C) 2008 Jon Smirl <[email protected]>
+ */
+
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/input.h>
+
+#include "ir.h"
+
+struct keymap {
+ struct config_item item;
+ int protocol;
+ int device;
+ int command;
+ int keycode;
+};
+
+static inline struct keymap *to_keymap(struct config_item *item)
+{
+ return item ? container_of(item, struct keymap, item) : NULL;
+}
+
+struct remote {
+ struct config_group group;
+ struct input_dev *input;
+};
+
+static inline struct remote *to_remote(struct config_group *group)
+{
+ return group ? container_of(group, struct remote, group) : NULL;
+}
+
+
+static struct configfs_attribute item_protocol = {
+ .ca_owner = THIS_MODULE,
+ .ca_name = "protocol",
+ .ca_mode = S_IRUGO | S_IWUSR,
+};
+
+static struct configfs_attribute item_device = {
+ .ca_owner = THIS_MODULE,
+ .ca_name = "device",
+ .ca_mode = S_IRUGO | S_IWUSR,
+};
+
+static struct configfs_attribute item_command = {
+ .ca_owner = THIS_MODULE,
+ .ca_name = "command",
+ .ca_mode = S_IRUGO | S_IWUSR,
+};
+
+static struct configfs_attribute item_keycode = {
+ .ca_owner = THIS_MODULE,
+ .ca_name = "keycode",
+ .ca_mode = S_IRUGO | S_IWUSR,
+};
+
+static ssize_t item_show(struct config_item *item,
+ struct configfs_attribute *attr,
+ char *page)
+{
+ struct keymap *keymap = to_keymap(item);
+
+ if (attr == &item_protocol)
+ return sprintf(page, "%d\n", keymap->protocol);
+ if (attr == &item_device)
+ return sprintf(page, "%d\n", keymap->device);
+ if (attr == &item_command)
+ return sprintf(page, "%d\n", keymap->command);
+ return sprintf(page, "%d\n", keymap->keycode);
+}
+
+static ssize_t item_store(struct config_item *item,
+ struct configfs_attribute *attr,
+ const char *page, size_t count)
+{
+ struct keymap *keymap = to_keymap(item);
+ struct remote *remote;
+ unsigned long tmp;
+ char *p = (char *) page;
+
+ tmp = simple_strtoul(p, &p, 10);
+ if (!p || (*p && (*p != '\n')))
+ return -EINVAL;
+
+ if (tmp > INT_MAX)
+ return -ERANGE;
+
+ if (attr == &item_protocol)
+ keymap->protocol = tmp;
+ else if (attr == &item_device)
+ keymap->device = tmp;
+ else if (attr == &item_command)
+ keymap->command = tmp;
+ else {
+ if (tmp < KEY_MAX) {
+ remote = to_remote(to_config_group(item->ci_parent));
+ set_bit(tmp, remote->input->keybit);
+ keymap->keycode = tmp;
+ }
+ }
+ return count;
+}
+
+static void keymap_release(struct config_item *item)
+{
+ struct keymap *keymap = to_keymap(item);
+ struct remote *remote = to_remote(to_config_group(item->ci_parent));
+
+ printk("keymap release\n");
+ clear_bit(keymap->keycode, remote->input->keybit);
+ kfree(keymap);
+}
+
+static struct configfs_item_operations keymap_ops = {
+ .release = keymap_release,
+ .show_attribute = item_show,
+ .store_attribute = item_store,
+};
+
+/* Start the definition of the all of the attributes
+ * in a single keymap directory
+ */
+static struct configfs_attribute *keymap_attrs[] = {
+ &item_protocol,
+ &item_device,
+ &item_command,
+ &item_keycode,
+ NULL,
+};
+
+static struct config_item_type keymap_type = {
+ .ct_item_ops = &keymap_ops,
+ .ct_attrs = keymap_attrs,
+ .ct_owner = THIS_MODULE,
+};
+
+static struct config_item *make_keymap(struct config_group *group, const char *name)
+{
+ struct keymap *keymap;
+
+ keymap = kzalloc(sizeof(*keymap), GFP_KERNEL);
+ if (!keymap)
+ return ERR_PTR(-ENOMEM);
+
+ config_item_init_type_name(&keymap->item, name, &keymap_type);
+ return &keymap->item;
+}
+
+/*
+ * Note that, since no extra work is required on ->drop_item(),
+ * no ->drop_item() is provided.
+ */
+static struct configfs_group_operations remote_group_ops = {
+ .make_item = make_keymap,
+};
+
+static ssize_t remote_show(struct config_item *item,
+ struct configfs_attribute *attr,
+ char *page)
+{
+ struct config_group *group = to_config_group(item);
+ struct remote *remote = to_remote(group);
+ const char *path;
+
+ if (strcmp(attr->ca_name, "path") == 0) {
+ path = kobject_get_path(&remote->input->dev.kobj, GFP_KERNEL);
+ strcpy(page, path);
+ kfree(path);
+ return strlen(page);
+ }
+ return sprintf(page,
+"Map for a specific remote\n"
+"Remote signals matching this map will be translated into keyboard/mouse events\n");
+}
+
+static void remote_release(struct config_item *item)
+{
+ struct config_group *group = to_config_group(item);
+ struct remote *remote = to_remote(group);
+
+ printk("remote_release\n");
+ input_free_device(remote->input);
+ kfree(remote);
+}
+
+static struct configfs_item_operations remote_item_ops = {
+ .release = remote_release,
+ .show_attribute = remote_show,
+};
+
+static struct configfs_attribute remote_attr_description = {
+ .ca_owner = THIS_MODULE,
+ .ca_name = "description",
+ .ca_mode = S_IRUGO,
+};
+
+static struct configfs_attribute remote_attr_path = {
+ .ca_owner = THIS_MODULE,
+ .ca_name = "path",
+ .ca_mode = S_IRUGO,
+};
+
+static struct configfs_attribute *remote_attrs[] = {
+ &remote_attr_description,
+ &remote_attr_path,
+ NULL,
+};
+
+static struct config_item_type remote_type = {
+ .ct_item_ops = &remote_item_ops,
+ .ct_group_ops = &remote_group_ops,
+ .ct_attrs = remote_attrs,
+ .ct_owner = THIS_MODULE,
+};
+
+/* Top level remotes directory for all remotes */
+
+/* Create a new remote group */
+static struct config_group *make_remote(struct config_group *parent, const char *name)
+{
+ struct remote *remote;
+ int ret;
+
+ remote = kzalloc(sizeof(*remote), GFP_KERNEL);
+ if (!remote)
+ return ERR_PTR(-ENOMEM);
+
+ remote->input = input_allocate_device();
+ if (!remote->input) {
+ ret = -ENOMEM;
+ goto free_mem;
+ }
+ remote->input->id.bustype = BUS_VIRTUAL;
+ remote->input->name = name;
+ remote->input->phys = "remotes";
+
+ remote->input->evbit[0] = BIT_MASK(EV_KEY);
+
+ ret = input_register_device(remote->input);
+ if (ret)
+ goto free_input;
+
+ config_group_init_type_name(&remote->group, name, &remote_type);
+ return &remote->group;
+
+ free_input:
+ input_free_device(remote->input);
+ free_mem:
+ kfree(remote);
+ return ERR_PTR(ret);
+}
+
+static ssize_t remotes_show_description(struct config_item *item,
+ struct configfs_attribute *attr,
+ char *page)
+{
+ return sprintf(page,
+"This subsystem allows the creation of IR remote control maps.\n"
+"Maps allow IR signals to be mapped into key strokes or mouse events.\n");
+}
+
+static struct configfs_item_operations remotes_item_ops = {
+ .show_attribute = remotes_show_description,
+};
+
+static struct configfs_attribute remotes_attr_description = {
+ .ca_owner = THIS_MODULE,
+ .ca_name = "description",
+ .ca_mode = S_IRUGO,
+};
+
+static struct configfs_attribute *remotes_attrs[] = {
+ &remotes_attr_description,
+ NULL,
+};
+
+/*
+ * Note that, since no extra work is required on ->drop_item(),
+ * no ->drop_item() is provided.
+ */
+static struct configfs_group_operations remotes_group_ops = {
+ .make_group = make_remote,
+};
+
+static struct config_item_type remotes_type = {
+ .ct_item_ops = &remotes_item_ops,
+ .ct_group_ops = &remotes_group_ops,
+ .ct_attrs = remotes_attrs,
+ .ct_owner = THIS_MODULE,
+};
+
+struct configfs_subsystem input_ir_remotes = {
+ .su_group = {
+ .cg_item = {
+ .ci_namebuf = "remotes",
+ .ci_type = &remotes_type,
+ },
+ },
+};
+
+void input_ir_translate(struct input_dev *dev, int protocol, int device, int command)
+{
+ struct config_item *i, *j;
+ struct config_group *g;
+ struct remote *remote;
+ struct keymap *keymap;
+
+ /* generate the IR format event */
+ input_report_ir(dev, IR_PROTOCOL, protocol);
+ input_report_ir(dev, IR_DEVICE, device);
+ input_report_ir(dev, IR_COMMAND, command);
+ input_sync(dev);
+
+ mutex_lock(&input_ir_remotes.su_mutex);
+
+ /* search the translation maps to translate into key stroke */
+ list_for_each_entry(i, &input_ir_remotes.su_group.cg_children, ci_entry) {
+ g = to_config_group(i);
+ list_for_each_entry(j, &g->cg_children, ci_entry) {
+ keymap = to_keymap(j);
+ if ((keymap->protocol == protocol) && (keymap->device == device)
+ && (keymap->command == command)) {
+ remote = to_remote(g);
+ input_report_key(remote->input, keymap->keycode, 1);
+ input_sync(remote->input);
+ }
+ }
+ }
+ mutex_unlock(&input_ir_remotes.su_mutex);
+}
diff --git a/drivers/input/ir/ir-core.c b/drivers/input/ir/ir-core.c
index 85adfcb..9c81bac 100644
--- a/drivers/input/ir/ir-core.c
+++ b/drivers/input/ir/ir-core.c
@@ -10,15 +10,6 @@

#include "ir.h"

-void input_ir_translate(struct input_dev *dev, int protocol, int device, int command)
-{
- /* generate the IR format event */
- input_report_ir(dev, IR_PROTOCOL, protocol);
- input_report_ir(dev, IR_DEVICE, device);
- input_report_ir(dev, IR_COMMAND, command);
- input_sync(dev);
-}
-
static int encode_sony(struct ir_device *ir, struct ir_command *command)
{
/* Sony SIRC IR code */
@@ -725,11 +716,15 @@ void input_ir_destroy(struct input_dev *dev)

static int __init input_ir_init(void)
{
- return 0;
+ config_group_init(&input_ir_remotes.su_group);
+ mutex_init(&input_ir_remotes.su_mutex);
+
+ return configfs_register_subsystem(&input_ir_remotes);
}
module_init(input_ir_init);

static void __exit input_ir_exit(void)
{
+ configfs_unregister_subsystem(&input_ir_remotes);
}
module_exit(input_ir_exit);

2009-11-27 01:34:56

by [email protected]

[permalink] [raw]
Subject: [IR-RFC PATCH v4 4/6] GPT driver for in-kernel IR support.

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 | 3 +
drivers/input/ir/ir-gpt.c | 184 +++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 193 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 a6f3f25..172c0c6 100644
--- a/drivers/input/ir/Kconfig
+++ b/drivers/input/ir/Kconfig
@@ -12,4 +12,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 2ccdda3..ab0da3f 100644
--- a/drivers/input/ir/Makefile
+++ b/drivers/input/ir/Makefile
@@ -6,3 +6,6 @@
obj-$(CONFIG_INPUT_IR) += ir.o
ir-objs := ir-core.o ir-configfs.o

+
+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..41d2fa6
--- /dev/null
+++ b/drivers/input/ir/ir-gpt.c
@@ -0,0 +1,184 @@
+/*
+ * GPT timer based IR device
+ *
+ * Copyright (C) 2008 Jon Smirl <[email protected]>
+ */
+
+#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>
+
+struct ir_gpt {
+ struct input_dev *input;
+ int irq, previous;
+ struct mpc52xx_gpt __iomem *regs;
+};
+
+/*
+ * Interrupt handlers
+ */
+static irqreturn_t dpeak_ir_irq(int irq, void *_ir)
+{
+ struct ir_gpt *ir_gpt = _ir;
+ int sample, count, delta, bit, wrap;
+
+ sample = in_be32(&ir_gpt->regs->status);
+ out_be32(&ir_gpt->regs->status, 0xF);
+
+ count = sample >> 16;
+ wrap = (sample >> 12) & 7;
+ bit = (sample >> 8) & 1;
+
+ delta = count - ir_gpt->previous;
+ delta += wrap * 0x10000;
+
+ ir_gpt->previous = count;
+
+ if (bit)
+ delta = -delta;
+
+ input_ir_queue(ir_gpt->input, delta);
+
+ 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;
+ ret = input_ir_register(ir_gpt->input);
+ if (ret)
+ goto free_input;
+
+ /* 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_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_unregister_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);

2009-11-27 01:35:39

by [email protected]

[permalink] [raw]
Subject: [IR-RFC PATCH v4 5/6] Example of PowerPC device tree support for GPT based IR


---
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 429bb2f..50cc247 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";
@@ -320,6 +310,14 @@
reg = <0x8000 0x4000>;
};

+ ir0@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.
*/
@@ -335,7 +333,6 @@
&gpt3 0 0 /* timer3 13d x6-4 */
&gpt4 0 0 /* timer4 61c x2-16 */
&gpt6 0 0 /* timer6 60c x8-15 */
- &gpt7 0 0 /* timer7 36a x17-9 */
>;
};
};

2009-11-27 01:35:04

by [email protected]

[permalink] [raw]
Subject: [IR-RFC PATCH v4 6/6] Microsoft mceusb2 driver for in-kernel IR subsystem

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 | 745 +++++++++++++++++++++++++++++++++++++++++
3 files changed, 752 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 172c0c6..b854900 100644
--- a/drivers/input/ir/Kconfig
+++ b/drivers/input/ir/Kconfig
@@ -17,5 +17,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 ab0da3f..0bdafb0 100644
--- a/drivers/input/ir/Makefile
+++ b/drivers/input/ir/Makefile
@@ -8,4 +8,5 @@ ir-objs := ir-core.o ir-configfs.o


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..1bc1155
--- /dev/null
+++ b/drivers/input/ir/ir-mceusb2.c
@@ -0,0 +1,745 @@
+/*
+ * LIRC driver for Philips eHome USB Infrared Transceiver
+ * and the Microsoft MCE 2005 Remote Control
+ *
+ * (C) by Martin A. Blatter <[email protected]>
+ *
+ * Transmitter support and reception code cleanup.
+ * (C) by Daniel Melander <[email protected]>
+ *
+ * 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
+ *
+ */
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/input.h>
+
+#define DRIVER_AUTHOR "Daniel Melander <[email protected]>, " \
+ "Martin Blatter <[email protected]>"
+#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);
+ if (bit)
+ delta = -delta;
+
+ input_ir_queue(ir->input, delta);
+ }
+ }
+ }
+ 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;
+ ret = input_ir_register(ir->input);
+ if (ret)
+ goto free_input;
+
+ return 0;
+
+free_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;
+ input_unregister_device(ir->input);
+
+ 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);

2009-11-27 03:07:54

by Jarod Wilson

[permalink] [raw]
Subject: Re: [IR-RFC PATCH v4 0/6] In-kernel IR support using evdev

First up, thank you for reposting this. I believe several of us were
keeping this code in mind for the in-kernel decoding portion of this
discussion, but it hadn't been stated outright in the recent threads here.

On 11/26/2009 08:34 PM, Jon Smirl wrote:
> This is a repost of the LIRC input patch series I wrote a year ago.
> Many of the ideas being discussed in the currect "Should we create a
> raw input interface for IR's?" thread have already been implemented
> in this code.
>
> ---------------------------------------------------
>
> All of this kernel code is tiny, about 20K including a driver.
>
> Basic flow works like this:
>
> patch: Minimal changes to the core input system
> Add a new IR data type to the input framework
>
> patch: Core IR module
> In-kernel decoding of core IR protocols - RC5, RC6, etc
> This only converts from protocol pulse timing to common command format.
> Check out: http://www.sbprojects.com/knowledge/ir/sirc.htm

I think we can definitely take advantage of this for the in-kernel side
of the initial hybrid approach I think we're converging on.

> patch: Configfs support for IR
> Decoded core protocols pass through a translation map based on configfs
> When core protocol matches an entry in configfs it is turned into a
> keycode event.

This part... Not so wild about. The common thought I'm seeing from
people is that we should be using setkeycode to load keymaps. I mean,
sure, I suppose this could be abstracted away so the user never sees it,
but it seems to be reinventing a way to set up key mapping when
setkeycode already exists, and is used by a number of existing IR
devices in the v4l/dvb subsystem (as well as misc things like the ati rf
remotes, iirc). Is there some distinct advantage to going this route vs.
setkeycode that I'm missing?

> patch: Microsoft mceusb2 driver for in-kernel IR subsystem
> Example mceusb IR input driver

Bah. I completely forgot you'd actually did have transmit code too.

...
> Raw IR pulse data is available in a FIFO via sysfs. You can use this
> to figure out new remote protocols.
>
> Two input events are generated
> 1) an event for the decoded raw IR protocol
> 2) a keycode event if the decoded raw IR protocol matches an entry in
> the configfs
>
> You can also send pulses.
...
> 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.

Also neglected to recall there was raw IR data access too. However, a
few things... One, this is, in some sense, cheating, as its not an input
layer interface being used. :) Granted though, it *is* an existing
kernel interface being used, instead of adding a new one. Two, there's
no userspace to do anything with it at this time. I mean, sure, in
theory, writing it wouldn't be that hard, but we can already do the same
thing using the lirc interface, and already have userspace code for it.
I think users need/desire to use raw IR modes may be more prevalent that
most people think.

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

I'd be game to try hacking up the lirc_mceusb driver (which has, since
your work was done, merged mce v1 and v2 transceiver support) to support
both your in-kernel interface and the lirc interface as an example of
the hybrid approach I'm thinking of.

> 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.

And there's the hangup for me. The lirc drivers and interface have been
pretty heavily battle-tested over years and years in the field. And
there are those 15 more drivers that already work with the lirc
interface. I'm woefully short on time to work on any porting myself, and
about to get even shorter with some new responsibilities at work
requiring even more of my attention.

If we go with a hybrid approach, all those existing drivers can be
brought in supporting just the lirc interface initially, and have
in-kernel decode support added as folks have time to work on them, if it
actually makes sense for those devices.

Just trying to find a happy middle ground that minimizes regressions for
users *and* gives us maximum flexibility.

--
Jarod Wilson
[email protected]

2009-11-27 03:34:52

by [email protected]

[permalink] [raw]
Subject: Re: [IR-RFC PATCH v4 0/6] In-kernel IR support using evdev

On Thu, Nov 26, 2009 at 10:12 PM, Jarod Wilson <[email protected]> wrote:
> This part... Not so wild about. The common thought I'm seeing from people is
> that we should be using setkeycode to load keymaps. I mean, sure, I suppose
> this could be abstracted away so the user never sees it, but it seems to be
> reinventing a way to set up key mapping when setkeycode already exists, and
> is used by a number of existing IR devices in the v4l/dvb subsystem (as well
> as misc things like the ati rf remotes, iirc). Is there some distinct
> advantage to going this route vs. setkeycode that I'm missing?

The configfs scheme and keymaps offer the same abilities. One is an
ancient binary protocol and the other one uses Unix standard commands
like mkdir and echo to build the map. You need special commands -
setkeycodes, getkeycodes, showkey, loadkeys, xmodmap, dump-keys to use
a keymap. I've been using Linux forever and I can't remember how
these commands work.

Keymaps are a binary protocol written by Risto Kankkunen in 1993.
Configfs was added by Oracle about two years ago but it has not been
used for mapping purposes.

It's another discussion, but if IR goes the configfs route I'd
consider writing a patch to switch keymaps/keycodes onto the configfs
model. It is a huge advantage to get rid of these pointless special
purpose commands that nobody knows how to use. I'd keep the legacy
IOCTLs working and redirect the data structure to a configfs one
instead of the existing structure.

The same idea is behind getting rid of IOCTLs and using sysfs. Normal
Unix commands can manipulate sysfs. IOCTLs have problems with strace,
endianess and the size of int (32/64b).

--
Jon Smirl
[email protected]

2009-11-27 03:59:02

by [email protected]

[permalink] [raw]
Subject: Re: [IR-RFC PATCH v4 0/6] In-kernel IR support using evdev

On Thu, Nov 26, 2009 at 10:12 PM, Jarod Wilson <[email protected]> wrote:
>> 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.
>
> Also neglected to recall there was raw IR data access too. However, a few
> things... One, this is, in some sense, cheating, as its not an input layer
> interface being used. :) Granted though, it *is* an existing kernel
> interface being used, instead of adding a new one. Two, there's no userspace
> to do anything with it at this time. I mean, sure, in theory, writing it
> wouldn't be that hard, but we can already do the same thing using the lirc
> interface, and already have userspace code for it. I think users need/desire
> to use raw IR modes may be more prevalent that most people think.

I would view raw mode as a developer interface which would be used to
implement a new protocol decode engine. There is an assumption in my
design that all IR timings can be decoded/constructed by a protocol
engine. Sure we may end up with 40 protocol engines, but they are
only about 50 lines of code. You could put each engine into a loadable
module if you want.

You see raw data used a lot with Sony remotes. Sony remotes mix
multiple protocols in a single remote. The irrecord program does not
understand these mixed protocols and generates a raw mode config file.
The mixed protocols don't bother the protocol state machine system.
This happens with other mixed protocol remotes too.

> Two drivers are supplied. mceusb2 implements send and receive support
>> for the Microsoft USB IR dongle.
>
> I'd be game to try hacking up the lirc_mceusb driver (which has, since your
> work was done, merged mce v1 and v2 transceiver support) to support both
> your in-kernel interface and the lirc interface as an example of the hybrid
> approach I'm thinking of.
>
>> 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.
>
> And there's the hangup for me. The lirc drivers and interface have been
> pretty heavily battle-tested over years and years in the field. And there
> are those 15 more drivers that already work with the lirc interface. I'm
> woefully short on time to work on any porting myself, and about to get even
> shorter with some new responsibilities at work requiring even more of my
> attention.
>
> If we go with a hybrid approach, all those existing drivers can be brought
> in supporting just the lirc interface initially, and have in-kernel decode
> support added as folks have time to work on them, if it actually makes sense
> for those devices.
>
> Just trying to find a happy middle ground that minimizes regressions for
> users *and* gives us maximum flexibility.

You are going to have to choose. Recreate the problems of type
specific devices like /dev/mouse and /dev/kbd that evdev was created
to fix, or skip those type specific devices and go straight to evdev.
We've known for years the /dev/mouse was badly broken. How many more
years is it going to be before it can be removed? /dev/lirc has the
same type of problems that /dev/mouse has. The only reason that
/dev/lirc works now is because there is a single app that uses it.

Also, implementing a new evdev based system in the kernel in no way
breaks existing lirc installations. Just don't load the new
implementation and everything works exactly the same way as before.

I'd go the evdev only route for in-kernel and leave existing lirc out
of tree. Existing lirc will continue to work. This is probably the
most stable strategy, even more so than a hybrid approach. The
in-kernel implementation will then be free to evolve without the
constraint of legacy APIs. As people become happy with it they can
switch over.


--
Jon Smirl
[email protected]

2009-11-27 07:22:11

by Stefan Richter

[permalink] [raw]
Subject: Re: [IR-RFC PATCH v4 0/6] In-kernel IR support using evdev

Jarod Wilson wrote:
> On 11/26/2009 08:34 PM, Jon Smirl wrote:
>> 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.
>
> Also neglected to recall there was raw IR data access too. However, a
> few things... One, this is, in some sense, cheating, as its not an input
> layer interface being used. :) Granted though, it *is* an existing
> kernel interface being used, instead of adding a new one. Two, there's
> no userspace to do anything with it at this time.

No; it is a new interface, just using an existing mechanism (sysfs). Not
all of sysfs in itself is an interface really; rather there is a number
of interfaces which are implemented by means of sysfs.

sysfs is primarily meant for simple textual attributes though, not for
I/O streams.
--
Stefan Richter
-=====-==--= =-== ==-==
http://arcgraph.de/sr/

2009-11-29 07:21:28

by Dmitry Torokhov

[permalink] [raw]
Subject: Re: [IR-RFC PATCH v4 0/6] In-kernel IR support using evdev

On Thu, Nov 26, 2009 at 10:34:55PM -0500, Jon Smirl wrote:
> On Thu, Nov 26, 2009 at 10:12 PM, Jarod Wilson <[email protected]> wrote:
> > This part... Not so wild about. The common thought I'm seeing from people is
> > that we should be using setkeycode to load keymaps. I mean, sure, I suppose
> > this could be abstracted away so the user never sees it, but it seems to be
> > reinventing a way to set up key mapping when setkeycode already exists, and
> > is used by a number of existing IR devices in the v4l/dvb subsystem (as well
> > as misc things like the ati rf remotes, iirc). Is there some distinct
> > advantage to going this route vs. setkeycode that I'm missing?
>
> The configfs scheme and keymaps offer the same abilities. One is an
> ancient binary protocol and the other one uses Unix standard commands
> like mkdir and echo to build the map. You need special commands -
> setkeycodes, getkeycodes, showkey, loadkeys, xmodmap, dump-keys to use
> a keymap. I've been using Linux forever and I can't remember how
> these commands work.

Nor you really should - it all is mostly being used transparently for
the end-user. I mean udev loading your laptop-specific keymap is not
using loadkeys but specially written utility that issues EVIOCSKEYCODE
directly.

>
> Keymaps are a binary protocol written by Risto Kankkunen in 1993.
> Configfs was added by Oracle about two years ago but it has not been
> used for mapping purposes.

Nor it is even enabled by default... Do we want to make in mandatory on
all consumer systems out there?

>
> It's another discussion, but if IR goes the configfs route I'd
> consider writing a patch to switch keymaps/keycodes onto the configfs
> model. It is a huge advantage to get rid of these pointless special
> purpose commands that nobody knows how to use. I'd keep the legacy
> IOCTLs working and redirect the data structure to a configfs one
> instead of the existing structure.

What is the memory footprint for configfs solution though? I would hate
to see the cost of user-modifiable keymap explode tenfold so that we can
rely less (not even get rid of, since it is published userspace API/ABI)
on setkeycodes and related ioctls.

>
> The same idea is behind getting rid of IOCTLs and using sysfs. Normal
> Unix commands can manipulate sysfs. IOCTLs have problems with strace,
> endianess and the size of int (32/64b).

The size of long you mean, right? Besides, now that we know better we
should simply use explicitely-sized fields in ioctl structures.

--
Dmitry

2009-11-29 07:34:35

by Dmitry Torokhov

[permalink] [raw]
Subject: Re: [IR-RFC PATCH v4 0/6] In-kernel IR support using evdev

On Thu, Nov 26, 2009 at 10:58:59PM -0500, Jon Smirl wrote:
> >
> >> 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.
> >
> > And there's the hangup for me. The lirc drivers and interface have been
> > pretty heavily battle-tested over years and years in the field. And there
> > are those 15 more drivers that already work with the lirc interface. I'm
> > woefully short on time to work on any porting myself, and about to get even
> > shorter with some new responsibilities at work requiring even more of my
> > attention.
> >
> > If we go with a hybrid approach, all those existing drivers can be brought
> > in supporting just the lirc interface initially, and have in-kernel decode
> > support added as folks have time to work on them, if it actually makes sense
> > for those devices.
> >
> > Just trying to find a happy middle ground that minimizes regressions for
> > users *and* gives us maximum flexibility.
>
> You are going to have to choose. Recreate the problems of type
> specific devices like /dev/mouse and /dev/kbd that evdev was created
> to fix, or skip those type specific devices and go straight to evdev.
> We've known for years the /dev/mouse was badly broken. How many more
> years is it going to be before it can be removed? /dev/lirc has the
> same type of problems that /dev/mouse has. The only reason that
> /dev/lirc works now is because there is a single app that uses it.
>

Pardon my ignorance here but it does not seem that /dev/lirc actually
multiplexes data stream from different receivers but rather creates a
device per receiver... Multiplexing is the main issue with /dev/mouse
for me.

What are the other issues with LIRC interface _for lircd-type applications_
do we see? I see for example that it is not 32/64 bit clean so that is
somethign that we may consider re-evaluating before just acceptig it
into kernel. Is there anything else?

> Also, implementing a new evdev based system in the kernel in no way
> breaks existing lirc installations. Just don't load the new
> implementation and everything works exactly the same way as before.
>
> I'd go the evdev only route for in-kernel and leave existing lirc out
> of tree. Existing lirc will continue to work. This is probably the
> most stable strategy, even more so than a hybrid approach. The
> in-kernel implementation will then be free to evolve without the
> constraint of legacy APIs. As people become happy with it they can
> switch over.
>

If by evdev-based system you mean adding EV_IR event to the input core I
think it would be a mistake. Such data (EV_IR/IR_XXX) will have to be read
from an event device, processed (probably in userspace) and re-injected
back through uinput as (EV_KEY/KEY_XXX) for consumption again. Such
looping interface would be a mistake IMHO.

Still, the end result of the transformation should be EV_KEY/KEY_XXX
delivered on one of the event devices (preferrably one per remote, not
one per receiver), I believe everyone agrees on that.

--
Dmitry

2009-11-29 17:17:27

by Greg KH

[permalink] [raw]
Subject: Re: [IR-RFC PATCH v4 2/6] Core IR module

On Thu, Nov 26, 2009 at 08:34:23PM -0500, Jon Smirl wrote:
> 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.
>
> Received IR messages generate event in the input queue.
> IR messages are sent using an input IOCTL.

As you are creating new sysfs files here, please document them in
Documentation/ABI/

thanks,

greg k-h

2009-11-29 17:17:46

by Greg KH

[permalink] [raw]
Subject: Re: [IR-RFC PATCH v4 2/6] Core IR module

> +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, count = 0;
> +
> + for (i = input_dev->ir->raw.tail; i != input_dev->ir->raw.head; ) {
> +
> + count += snprintf(&buf[count], PAGE_SIZE - 1, "%i\n", input_dev->ir->raw.buffer[i++]);
> + if (i > ARRAY_SIZE(input_dev->ir->raw.buffer))
> + i = 0;
> + if (count >= PAGE_SIZE - 1) {
> + input_dev->ir->raw.tail = i;
> + return PAGE_SIZE - 1;
> + }
> + }
> + input_dev->ir->raw.tail = i;
> + return count;
> +}

This looks like it violates the "one value per sysfs file" rule that we
have. What exactly are you outputting here? It does not look like this
belongs in sysfs at all.

> +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;
> +}

What type of data are you expecting here? More than one value?

thanks,

greg k-h

2009-11-29 17:37:32

by [email protected]

[permalink] [raw]
Subject: Re: [IR-RFC PATCH v4 2/6] Core IR module

On Sun, Nov 29, 2009 at 12:14 PM, Greg KH <[email protected]> wrote:
> On Thu, Nov 26, 2009 at 08:34:23PM -0500, Jon Smirl wrote:
>> 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.
>>
>> Received IR messages generate event in the input queue.
>> IR messages are sent using an input IOCTL.
>
> As you are creating new sysfs files here, please document them in
> Documentation/ABI/

This code is not going to get merged as is. It's just a starting point
to get a discussion started about designing an IR subsystem. I expect
the final design will look a lot different.

I'm trying to demonstrate that IR is an input device and that it can
be supported by the Linux input subsystem without the need to create a
special IR device.

--
Jon Smirl
[email protected]

2009-11-29 17:41:19

by [email protected]

[permalink] [raw]
Subject: Re: [IR-RFC PATCH v4 2/6] Core IR module

On Sun, Nov 29, 2009 at 12:17 PM, Greg KH <[email protected]> wrote:
>> +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, count = 0;
>> +
>> + ? ? for (i = input_dev->ir->raw.tail; i != input_dev->ir->raw.head; ) {
>> +
>> + ? ? ? ? ? ? count += snprintf(&buf[count], PAGE_SIZE - 1, "%i\n", input_dev->ir->raw.buffer[i++]);
>> + ? ? ? ? ? ? if (i > ARRAY_SIZE(input_dev->ir->raw.buffer))
>> + ? ? ? ? ? ? ? ? ? ? i = 0;
>> + ? ? ? ? ? ? if (count >= PAGE_SIZE - 1) {
>> + ? ? ? ? ? ? ? ? ? ? input_dev->ir->raw.tail = i;
>> + ? ? ? ? ? ? ? ? ? ? return PAGE_SIZE - 1;
>> + ? ? ? ? ? ? }
>> + ? ? }
>> + ? ? input_dev->ir->raw.tail = i;
>> + ? ? return count;
>> +}
>
> This looks like it violates the "one value per sysfs file" rule that we
> have. ?What exactly are you outputting here? ?It does not look like this
> belongs in sysfs at all.

It should be on a debug switch or maybe a debug device. If the rest of
the system is working right you shouldn't need this data.

>
>> +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;
>> +}
>
> What type of data are you expecting here? ?More than one value?
>
> thanks,
>
> greg k-h
>



--
Jon Smirl
[email protected]

2009-11-29 19:11:45

by Greg KH

[permalink] [raw]
Subject: Re: [IR-RFC PATCH v4 2/6] Core IR module

On Sun, Nov 29, 2009 at 12:37:36PM -0500, Jon Smirl wrote:
> On Sun, Nov 29, 2009 at 12:14 PM, Greg KH <[email protected]> wrote:
> > On Thu, Nov 26, 2009 at 08:34:23PM -0500, Jon Smirl wrote:
> >> 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.
> >>
> >> Received IR messages generate event in the input queue.
> >> IR messages are sent using an input IOCTL.
> >
> > As you are creating new sysfs files here, please document them in
> > Documentation/ABI/
>
> This code is not going to get merged as is. It's just a starting point
> to get a discussion started about designing an IR subsystem. I expect
> the final design will look a lot different.

Then show the design by adding the documentation for new sysfs files, to
make it easier to understand by everyone please.

thanks,

greg k-h

2009-11-29 19:11:58

by Greg KH

[permalink] [raw]
Subject: Re: [IR-RFC PATCH v4 2/6] Core IR module

On Sun, Nov 29, 2009 at 12:41:22PM -0500, Jon Smirl wrote:
> On Sun, Nov 29, 2009 at 12:17 PM, Greg KH <[email protected]> wrote:
> >> +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, count = 0;
> >> +
> >> + ? ? for (i = input_dev->ir->raw.tail; i != input_dev->ir->raw.head; ) {
> >> +
> >> + ? ? ? ? ? ? count += snprintf(&buf[count], PAGE_SIZE - 1, "%i\n", input_dev->ir->raw.buffer[i++]);
> >> + ? ? ? ? ? ? if (i > ARRAY_SIZE(input_dev->ir->raw.buffer))
> >> + ? ? ? ? ? ? ? ? ? ? i = 0;
> >> + ? ? ? ? ? ? if (count >= PAGE_SIZE - 1) {
> >> + ? ? ? ? ? ? ? ? ? ? input_dev->ir->raw.tail = i;
> >> + ? ? ? ? ? ? ? ? ? ? return PAGE_SIZE - 1;
> >> + ? ? ? ? ? ? }
> >> + ? ? }
> >> + ? ? input_dev->ir->raw.tail = i;
> >> + ? ? return count;
> >> +}
> >
> > This looks like it violates the "one value per sysfs file" rule that we
> > have. ?What exactly are you outputting here? ?It does not look like this
> > belongs in sysfs at all.
>
> It should be on a debug switch or maybe a debug device. If the rest of
> the system is working right you shouldn't need this data.

Then why export it at all?

If it's debug stuff, please use debugfs, that is what it is there for.

thanks,

greg k-h