libspark: use IRMP for converting /dev/lirc to input device

This commit is contained in:
Stefan Seyfried
2012-02-05 19:15:10 +01:00
parent 026a6df70d
commit 99fad7b7d7
3 changed files with 204 additions and 144 deletions

View File

@@ -25,6 +25,7 @@ libstb_hal_a_LIBADD += \
libspark/audio.o \
libspark/dmx.o \
libspark/irmp.o \
libspark/lt_dfbinput.o \
libspark/playback.o \
libspark/video.o
endif

View File

@@ -1,6 +1,6 @@
/*
* Simulate a linux input device via uinput
* Get td remote events via DirectFB and inject them via uinput
* Get lirc remote events, decode with IRMP and inject them via uinput
*
* (C) 2012 Stefan Seyfried
*
@@ -18,7 +18,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* the C++ compiler does not like this code, so let's put it into a
/* the C++ compiler did not like this code, so let's put it into a
* separate file and compile with gcc insead of g++...
*/
@@ -35,13 +35,14 @@
#include <linux/ioctl.h>
#include <linux/input.h>
#include <linux/uinput.h>
#include <inttypes.h>
#include <errno.h>
#include <directfb.h>
#include "lt_dfbinput.h"
/* needed for videodecoder watchdog */
#include "video_lib.h"
extern cVideo *videoDecoder;
extern "C" {
#include "irmp.h"
}
static uint8_t IRMP_PIN;
/* same defines as in neutrino's rcinput.h */
#define KEY_TTTV KEY_FN_1
@@ -55,18 +56,63 @@ extern cVideo *videoDecoder;
#define KEY_ZOOMOUT KEY_FN_F
#endif
#define DFBCHECK(x...) \
err = x; \
if (err != DFB_OK) { \
fprintf(stderr, "%s <%d>:\n\t", __FILE__, __LINE__ ); \
DirectFBErrorFatal(#x, err ); \
}
typedef struct {
uint16_t ir; /* IR command */
int code; /* input key code */
} key_map_t;
typedef struct _DeviceInfo DeviceInfo;
struct _DeviceInfo {
DFBInputDeviceID device_id;
DFBInputDeviceDescription desc;
DeviceInfo *next;
static const key_map_t key_map[] = {
{ 0x13, KEY_0 },
{ 0x1a, KEY_1 },
{ 0x1f, KEY_2 },
{ 0x58, KEY_3 },
{ 0x16, KEY_4 },
{ 0x1b, KEY_5 },
{ 0x54, KEY_6 },
{ 0x12, KEY_7 },
{ 0x17, KEY_8 },
{ 0x50, KEY_9 },
{ 0x5f, KEY_OK },
{ 0x59, KEY_TIME },
{ 0x43, KEY_FAVORITES },
{ 0x4f, KEY_SAT },
{ 0x0f, KEY_NEXT }, /* V.Format */
{ 0x1e, KEY_POWER },
{ 0x5a, KEY_MUTE },
{ 0x1c, KEY_MENU },
{ 0x5d, KEY_EPG },
{ 0x07, KEY_INFO },
{ 0x60, KEY_EXIT },
{ 0x48, KEY_PAGEUP },
{ 0x44, KEY_PAGEDOWN },
{ 0x02, KEY_LEFT },
{ 0x40, KEY_RIGHT },
{ 0x03, KEY_UP },
{ 0x5e, KEY_DOWN },
{ 0x0a, KEY_VOLUMEUP },
{ 0x06, KEY_VOLUMEDOWN },
{ 0x49, KEY_RED },
{ 0x4e, KEY_GREEN },
{ 0x11, KEY_YELLOW },
{ 0x4a, KEY_BLUE },
{ 0x4c, KEY_TV }, /* TV/Radio */
{ 0x5c, KEY_VIDEO }, /* FIND */
{ 0x19, KEY_AUDIO }, /* FOLDER */
/* KEY_AUX,
KEY_TEXT,
KEY_TTTV,
KEY_TTZOOM,
KEY_REVEAL,
*/
{ 0x01, KEY_REWIND },
{ 0x53, KEY_FORWARD },
{ 0x22, KEY_STOP },
{ 0x4d, KEY_PAUSE },
{ 0x15, KEY_PLAY },
/* KEY_PREV, */
// KEY_EJECTCD,
{ 0x10, KEY_RECORD }
/* KEY_NEXT, */
};
static const int key_list[] = {
@@ -83,6 +129,7 @@ static const int key_list[] = {
KEY_OK,
KEY_TIME,
KEY_FAVORITES,
KEY_SAT,
KEY_ZOOMOUT,
KEY_ZOOMIN,
KEY_NEXT,
@@ -115,6 +162,7 @@ static const int key_list[] = {
KEY_REWIND,
KEY_STOP,
KEY_PAUSE,
KEY_PLAY,
KEY_FORWARD,
/* KEY_PREV, */
KEY_EJECTCD,
@@ -123,49 +171,38 @@ static const int key_list[] = {
-1
};
static IDirectFBEventBuffer *events;
static DeviceInfo *inputs = NULL;
static pthread_t thread;
static int thread_running;
static DFBEnumerationResult enum_input_device(DFBInputDeviceID device_id,
DFBInputDeviceDescription desc,
void *data)
{
DeviceInfo **devices = (DeviceInfo **)data;
DeviceInfo *device;
device = (DeviceInfo *)malloc(sizeof(DeviceInfo));
device->device_id = device_id;
device->desc = desc;
device->next = *devices;
*devices = device;
return DFENUM_OK;
}
static void *input_thread(void *data)
static void *input_thread(void *)
{
int uinput;
int i;
struct input_event u;
struct uinput_user_dev ud;
FILE *f;
int lircfd;
int pulse;
int i = 0;
int last_pulse = 1;
int last_code = -1;
uint32_t lircdata; /* lirc_t to be correct... */
unsigned int count = 0; /* how many timeouts? */
unsigned int nodec = 0; /* how many timeouts since last decoded? */
IRMP_DATA d;
DFBResult err;
IDirectFB *dfb = (IDirectFB *)data;
fprintf(stderr, "DFB input converter thread starting...\n");
fprintf(stderr, "LIRC/IRMP input converter thread starting...\n");
/* modprobe does not complain if the module is already loaded... */
system("/sbin/modprobe uinput");
system("/sbin/modprobe evdev");
uinput = open("/dev/misc/uinput", O_WRONLY|O_NDELAY);
do {
usleep(100000); /* mdev needs some time to create the device? */
uinput = open("/dev/uinput", O_WRONLY|O_NDELAY);
} while (uinput < 0 && ++count < 100);
if (uinput < 0)
{
fprintf(stderr, "DFB input thread: unable to open /dev/misc/uinput (%m)\n");
fprintf(stderr, "LIRC/IRMP input thread: unable to open /dev/uinput (%m)\n");
thread_running = 2;
return NULL;
}
@@ -181,7 +218,7 @@ static void *input_thread(void *data)
/* configure the device */
memset(&ud, 0, sizeof(ud));
strncpy(ud.name, "Neutrino TD to Input Device converter", UINPUT_MAX_NAME_SIZE);
strncpy(ud.name, "Neutrino LIRC/IRMP to Input Device converter", UINPUT_MAX_NAME_SIZE);
ud.id.version = 0x42;
ud.id.vendor = 0x1234;
ud.id.product = 0x5678;
@@ -190,7 +227,7 @@ static void *input_thread(void *data)
if (ioctl(uinput, UI_DEV_CREATE))
{
perror("DFB input thread UI_DEV_CREATE");
perror("LIRC/IRMP input thread UI_DEV_CREATE");
close(uinput);
return NULL;
}
@@ -226,7 +263,7 @@ static void *input_thread(void *data)
}
evdev = atoi(p + 6);
sprintf(newdev, "event%d", evdev);
fprintf(stderr, "DFB input thread: symlink /dev/input/nevis_ir to %s\n", newdev);
fprintf(stderr, "LIRC/IRMP input thread: symlink /dev/input/nevis_ir to %s\n", newdev);
unlink("/dev/input/nevis_ir");
symlink(newdev, "/dev/input/nevis_ir");
break;
@@ -243,119 +280,141 @@ static void *input_thread(void *data)
u.type = EV_KEY;
u.value = 0; /* initialize: first event wil be a key press */
dfb->EnumInputDevices(dfb, enum_input_device, &inputs);
DFBCHECK(dfb->CreateInputEventBuffer(dfb, DICAPS_ALL, DFB_FALSE, &events));
lircfd = open("/dev/lirc", O_RDONLY);
if (lircfd < 0)
{
perror ("open /dev/lirc");
goto out;
}
IRMP_PIN = 0xFF;
/* 50 ms. This should be longer than the longest light pulse */
#define POLL_MS (100 * 1000)
#define LIRC_PULSE 0x01000000
#define LIRC_PULSE_MASK 0x00FFFFFF
fprintf(stderr, "LIRC/IRMP input converter going into main loop...\n");
/* TODO: ioctl to find out if we have a compatible LIRC_MODE2 device */
thread_running = 1;
while (thread_running)
{
/* check every 250ms (if a key is pressed on remote, we might
* even check earlier, but it does not really hurt... */
if (videoDecoder)
videoDecoder->VideoParamWatchdog();
fd_set fds;
struct timeval tv;
int ret;
if (events->WaitForEventWithTimeout(events, 0, 250) == DFB_TIMEOUT)
continue;
DFBInputEvent e;
while (events->GetEvent(events, DFB_EVENT(&e)) == DFB_OK)
{
#if 0
fprintf(stderr, "type: %x devid: %x flags: %03x "
"key_id: %4x key_sym: %4x keycode: %d\n",
e.type, e.device_id, e.flags,
e.key_id, e.key_symbol, e.key_code);
#endif
switch (e.key_symbol)
{
/* will a lookup table be more efficient? */
case 0x0030: u.code = KEY_0; break;
case 0x0031: u.code = KEY_1; break;
case 0x0032: u.code = KEY_2; break;
case 0x0033: u.code = KEY_3; break;
case 0x0034: u.code = KEY_4; break;
case 0x0035: u.code = KEY_5; break;
case 0x0036: u.code = KEY_6; break;
case 0x0037: u.code = KEY_7; break;
case 0x0038: u.code = KEY_8; break;
case 0x0039: u.code = KEY_9; break;
case 0x000d: u.code = KEY_OK; break;
case 0xf504: u.code = KEY_TIME; break;
case 0xf01a: u.code = KEY_FAVORITES; break; /* blue heart */
case 0xf021: u.code = KEY_ZOOMOUT; break;
case 0xf022: u.code = KEY_ZOOMIN; break;
case 0xf505: u.code = KEY_NEXT; break; /* red hand */
case 0xf00f: u.code = KEY_POWER; break;
case 0xf04e: u.code = KEY_MUTE; break;
case 0xf012: u.code = KEY_MENU; break;
case 0xf01b: u.code = KEY_EPG; break;
case 0xf014: u.code = KEY_INFO; break;
case 0x001b: u.code = KEY_EXIT; break;
case 0xf046: u.code = KEY_PAGEUP; break;
case 0xf047: u.code = KEY_PAGEDOWN; break;
case 0xf000: u.code = KEY_LEFT; break;
case 0xf001: u.code = KEY_RIGHT; break;
case 0xf002: u.code = KEY_UP; break;
case 0xf003: u.code = KEY_DOWN; break;
case 0xf04c: u.code = KEY_VOLUMEUP; break;
case 0xf04d: u.code = KEY_VOLUMEDOWN; break;
case 0xf042: u.code = KEY_RED; break;
case 0xf043: u.code = KEY_GREEN; break;
case 0xf044: u.code = KEY_YELLOW; break;
case 0xf045: u.code = KEY_BLUE; break;
case 0xf027: u.code = KEY_TV; break;
case 0xf035: u.code = KEY_VIDEO; break;
case 0xf033: u.code = KEY_AUDIO; break;
case 0xf034: u.code = KEY_AUX; break;
case 0xf032: u.code = KEY_TEXT; break;
case 0xf501: u.code = KEY_TTTV; break;
case 0xf502: u.code = KEY_TTZOOM; break;
case 0xf503: u.code = KEY_REVEAL; break;
case 0xf059: u.code = KEY_REWIND; break;
case 0xf052: u.code = KEY_STOP; break;
case 0xf051: u.code = KEY_PAUSE; break;
case 0xf05a: u.code = KEY_FORWARD; break;
/* case 0xf05b: u.code = KEY_PREV; break; */
case 0xf057: u.code = KEY_EJECTCD; break;
case 0xf056: u.code = KEY_RECORD; break;
/* case 0xf05c: u.code = KEY_NEXT; break; */
default:
continue;
}
switch (e.type)
{
case 1: if (u.value < 2) /* 1 = key press */
u.value++; /* 2 = key repeat */
break;
case 2: u.value = 0; break; /* 0 = key release */
default:
continue;
}
// fprintf(stderr, "uinput write: value: %d code: %d\n", u.value, u.code);
write(uinput, &u, sizeof(u));
FD_ZERO(&fds);
FD_SET(lircfd, &fds);
tv.tv_sec = 0;
tv.tv_usec = POLL_MS;
/* any singal can interrupt select. we rely on the linux-only feature
* that the timeout is automatcally recalculated in this case! */
do {
ret = select(lircfd + 1, &fds, NULL, NULL, &tv);
} while (ret == -1 && errno == EINTR);
if (ret == -1) {
/* errno != EINTR... */
perror("lirmp: select");
break;
}
if (ret == 0)
{
count++;
nodec++;
lircdata = POLL_MS; /* timeout */
pulse = !last_pulse; /* lirc sends data on signal change */
if (last_code != -1 && nodec > 1)
{
fprintf(stderr, "timeout!\n");
u.code = last_code;
u.value = 0; /* release */
write(uinput, &u, sizeof(u));
last_code = -1;
}
}
else
{
if (read(lircfd, &lircdata, sizeof(lircdata)) != sizeof(lircdata))
{
perror("read");
break;
}
pulse = (lircdata & LIRC_PULSE); /* we got light... */
last_pulse = pulse;
lircdata &= LIRC_PULSE_MASK; /* how long the pulse was in microseconds */
}
if (ret && count)
{
if (count * POLL_MS > lircdata)
lircdata = 0;
else
lircdata -= count * POLL_MS;
count = 0;
}
//printf("lircdata: ret:%d c:%d %d\n", ret, ch - '0', lircdata);
lircdata /= (1000000 / F_INTERRUPTS);
if (pulse)
IRMP_PIN = 0x00;
else
IRMP_PIN = 0xff;
do {
(void) irmp_ISR (IRMP_PIN);
if (irmp_get_data (&d))
{
nodec = 0;
printf("protocol: %2d address: 0x%04x command: 0x%04x flags: %d\n",
d.protocol, d.address, d.command, d.flags);
/* todo: do we need to complete the loop if we already
* detected the singal in this pulse? */
if (d.protocol == IRMP_NEC_PROTOCOL && d.address == 0x5a45)
{
for (i = 0; i < (int)(sizeof(key_map)/sizeof(key_map_t)); i++)
{
if (key_map[i].ir == d.command)
{
if (last_code != -1 && last_code != key_map[i].code)
{
u.code = last_code;
u.value = 0;
write(uinput, &u, sizeof(u));
}
u.code = key_map[i].code;
u.value = (d.flags & 0x1) + 1;
fprintf(stderr, "uinput write: value: %d code: %d\n", u.value, u.code);
last_code = u.code;
write(uinput, &u, sizeof(u));
break;
}
}
}
}
} while (lircdata-- > 0);
}
/* clean up */
close (lircfd);
out:
ioctl(uinput, UI_DEV_DESTROY);
while (inputs) {
DeviceInfo *next = inputs->next;
free(inputs);
inputs = next;
}
events->Release(events);
return NULL;
}
void start_input_thread(IDirectFB *dfb)
void start_input_thread(void)
{
if (pthread_create(&thread, 0, input_thread, dfb) != 0)
if (pthread_create(&thread, 0, input_thread, NULL) != 0)
{
perror("DFB input thread pthread_create");
perror("LIRC/IRMP input thread pthread_create");
thread_running = 0;
return;
}
/* wait until the device is created before continuing */
while (! thread_running)
usleep(1000);
if (thread_running == 2) /* failed... :-( */
thread_running = 0;
}
void stop_input_thread(void)

View File

@@ -2,6 +2,6 @@
#ifndef __LT_DFB_INPUT_H_
#define __LT_DFB_INPUT_H_
void start_input_thread(IDirectFB *dfb);
void start_input_thread(void);
void stop_input_thread(void);
#endif