rcinput: use a dynamic list of input devices

Instead of the fixed, static list of input devices, scan /dev/input/ for
proper event devices. A "good" input device is one that supports the
EVIOCGBIT ioctl and at least the EV_KEY event type.
This probably needs further fixes, e.g. in repeat rate setting code.


Origin commit data
------------------
Branch: ni/coolstream
Commit: 98557462ef
Author: Stefan Seyfried <seife@tuxbox-git.slipkontur.de>
Date: 2017-02-19 (Sun, 19 Feb 2017)



------------------
This commit was generated by Migit
This commit is contained in:
Stefan Seyfried
2017-02-19 23:16:09 +01:00
parent 9734b25d6d
commit b6ef97d367
2 changed files with 139 additions and 66 deletions

View File

@@ -4,7 +4,7 @@
Copyright (C) 2001 Steffen Hehn 'McClean'
2003 thegoodguy
Copyright (C) 2008-2014,2016 Stefan Seyfried
Copyright (C) 2008-2014,2016-2017 Stefan Seyfried
Copyright (C) 2013-2014 martii
License: GPL
@@ -33,7 +33,6 @@
#include <system/helpers.h>
#include <stdio.h>
#include <asm/types.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/time.h>
@@ -50,6 +49,8 @@
#include <sys/socket.h>
#include <netinet/in.h>
#include <dirent.h>
#include <eventserver.h>
#include <global.h>
@@ -65,17 +66,6 @@
#define ENABLE_REPEAT_CHECK
#if HAVE_SPARK_HARDWARE
/* this relies on event0 being the AOTOM frontpanel driver device
* TODO: what if another input device is present? */
const char * const RC_EVENT_DEVICE[NUMBER_OF_EVENT_DEVICES] = {"/dev/input/nevis_ir", "/dev/input/event0"};
#elif HAVE_GENERIC_HARDWARE
/* the FIFO created by libstb-hal */
const char * const RC_EVENT_DEVICE[NUMBER_OF_EVENT_DEVICES] = {"/tmp/neutrino.input"};
#else
//const char * const RC_EVENT_DEVICE[NUMBER_OF_EVENT_DEVICES] = {"/dev/input/nevis_ir", "/dev/input/event0"};
const char * const RC_EVENT_DEVICE[NUMBER_OF_EVENT_DEVICES] = {"/dev/input/nevis_ir"};
#endif
typedef struct input_event t_input_event;
#ifdef KEYBOARD_INSTEAD_OF_REMOTE_CONTROL
@@ -83,6 +73,13 @@ static struct termio orig_termio;
static bool saved_orig_termio = false;
#endif /* KEYBOARD_INSTEAD_OF_REMOTE_CONTROL */
static bool input_stopped = false;
static struct timespec devinput_mtime = { 0, 0 };
#ifdef RCDEBUG
#define d_printf printf
#else
#define d_printf(...)
#endif
/*********************************************************************************
* Constructor - opens rc-input device, selects rc-hardware and starts threads
@@ -146,13 +143,9 @@ CRCInput::CRCInput()
perror("[neutrino] listen failed...\n");
exit( -1 );
}
for (int i = 0; i < NUMBER_OF_EVENT_DEVICES; i++)
{
fd_rc[i] = -1;
}
clickfd = -1;
repeat_block = repeat_block_generic = 0;
checkdev();
open();
rc_last_key = KEY_MAX;
firstKey = true;
@@ -162,21 +155,93 @@ CRCInput::CRCInput()
set_rc_hw();
}
/* if dev is given, open device with index <dev>, if not (re)open all */
void CRCInput::open(int dev)
bool CRCInput::checkdev()
{
if (dev == -1)
close();
for (int i = 0; i < NUMBER_OF_EVENT_DEVICES; i++)
{
if (dev != -1) {
if (i != dev || fd_rc[i] != -1)
continue;
/* stat()ing the directory is fast and cheap. If a device gets added or
* removed, the mtime of /dev/input/ will change, which in turn
* warrants a more thorough investigation */
struct stat st;
if (stat("/dev/input/", &st) == 0) {
if (st.st_mtim.tv_sec != devinput_mtime.tv_sec ||
st.st_mtim.tv_nsec != devinput_mtime.tv_nsec) {
devinput_mtime.tv_sec = st.st_mtim.tv_sec;
devinput_mtime.tv_nsec = st.st_mtim.tv_nsec;
printf("[rcinput:%s] /dev/input mtime changed\n", __func__);
return true;
}
return false; /* still the same... */
}
printf("[rcinput:%s] stat /dev/input failed: %m\n", __func__);
return true; /* need to check anyway... */
}
bool CRCInput::checkpath(in_dev id)
{
for (std::vector<in_dev>::iterator it = indev.begin(); it != indev.end(); ++it) {
if ((*it).path == id.path) {
printf("[rcinput:%s] skipping already opened %s\n", __func__, id.path.c_str());
return true;
}
}
return false;
}
/* if recheck == true, only not already opened devices are opened, if not, close then (re)open all */
void CRCInput::open(bool recheck)
{
if (recheck == false)
close();
/* close() takes the lock, too... */
OpenThreads::ScopedLock<OpenThreads::Mutex> m_lock(mutex);
unsigned long evbit;
struct in_dev id;
DIR *dir;
struct dirent *dentry;
dir = opendir("/dev/input");
if (! dir) {
printf("[rcinput:%s] opendir failed: %m\n", __func__);
return;
}
while ((dentry = readdir(dir)) != NULL)
{
if (dentry->d_type != DT_CHR) {
d_printf("[rcinput:%s] skipping '%s'\n", __func__, dentry->d_name);
continue;
}
d_printf("[rcinput:%s] considering '%s'\n", __func__, dentry->d_name);
id.path = "/dev/input/" + std::string(dentry->d_name);
if (checkpath(id))
continue;
id.fd = ::open(id.path.c_str(), O_RDWR|O_NONBLOCK|O_CLOEXEC);
if (id.fd == -1) {
printf("[rcinput:%s] open %s failed: %m\n", __func__, id.path.c_str());
continue;
}
if (ioctl(id.fd, EVIOCGBIT(0, EV_MAX), &evbit) < 0) {
::close(id.fd); /* not a proper input device, e.g. /dev/input/mice */
continue;
}
if ((evbit & (1 << EV_KEY)) == 0) {
printf("[rcinput:%s] %s is bad; no EV_KEY support (0x%lx)\n", __func__, id.path.c_str(), evbit);
::close(id.fd);
continue;
}
printf("[rcinput:%s] opened %s (fd %d) ev 0x%lx\n", __func__, id.path.c_str(), id.fd, evbit);
indev.push_back(id);
}
closedir(dir);
id.path = "/tmp/neutrino.input";
if (! checkpath(id)) {
id.fd = ::open(id.path.c_str(), O_RDWR|O_NONBLOCK|O_CLOEXEC);
if (id.fd == -1) {
/* debug, because it only matters for HAVE_GENERIC_HARDWARE */
d_printf("[rcinput:%s] open %s failed: %m\n", __func__, id.path.c_str());
} else {
printf("[rcinput:%s] opened %s (fd %d)\n", __func__, id.path.c_str(), id.fd);
indev.push_back(id);
}
if ((fd_rc[i] = ::open(RC_EVENT_DEVICE[i], O_RDWR|O_NONBLOCK|O_CLOEXEC)) == -1)
perror(RC_EVENT_DEVICE[i]);
printf("CRCInput::open: %s fd %d\n", RC_EVENT_DEVICE[i], fd_rc[i]);
}
//+++++++++++++++++++++++++++++++++++++++
@@ -224,12 +289,10 @@ void CRCInput::open(int dev)
void CRCInput::close()
{
for (int i = 0; i < NUMBER_OF_EVENT_DEVICES; i++) {
if (fd_rc[i] != -1) {
::close(fd_rc[i]);
fd_rc[i] = -1;
}
}
OpenThreads::ScopedLock<OpenThreads::Mutex> m_lock(mutex);
for (unsigned int i = 0; i < indev.size(); i++)
::close(indev[i].fd);
indev.clear();
#ifdef KEYBOARD_INSTEAD_OF_REMOTE_CONTROL
if (saved_orig_termio)
{
@@ -251,9 +314,9 @@ void CRCInput::calculateMaxFd()
{
fd_max = fd_event;
for (int i = 0; i < NUMBER_OF_EVENT_DEVICES; i++)
if (fd_rc[i] > fd_max)
fd_max = fd_rc[i];
for (unsigned int i = 0; i < indev.size(); i++)
if (indev[i].fd > fd_max)
fd_max = indev[i].fd;
if(fd_pipe_high_priority[0] > fd_max)
fd_max = fd_pipe_high_priority[0];
@@ -558,10 +621,8 @@ void CRCInput::getMsg_us(neutrino_msg_t * msg, neutrino_msg_data_t * data, uint6
* TODO: real hot-plugging, e.g. of keyboards and triggering this loop...
* right now it is only run if some event is happening "by accident" */
if (!input_stopped) {
for (int i = 0; i < NUMBER_OF_EVENT_DEVICES; i++) {
if (fd_rc[i] == -1)
open(i);
}
if (checkdev())
open(true);
}
// wiederholung reinmachen - dass wirklich die ganze zeit bis timeout gewartet wird!
@@ -595,10 +656,10 @@ void CRCInput::getMsg_us(neutrino_msg_t * msg, neutrino_msg_data_t * data, uint6
tvselect.tv_usec = targetTimeout%1000000;
FD_ZERO(&rfds);
for (int i = 0; i < NUMBER_OF_EVENT_DEVICES; i++)
for (unsigned int i = 0; i < indev.size(); i++)
{
if (fd_rc[i] != -1)
FD_SET(fd_rc[i], &rfds);
if (indev[i].fd != -1)
FD_SET(indev[i].fd, &rfds);
}
#ifdef KEYBOARD_INSTEAD_OF_REMOTE_CONTROL
if (true)
@@ -1234,19 +1295,19 @@ void CRCInput::getMsg_us(neutrino_msg_t * msg, neutrino_msg_data_t * data, uint6
}
}
for (int i = 0; i < NUMBER_OF_EVENT_DEVICES; i++) {
if ((fd_rc[i] != -1) && (FD_ISSET(fd_rc[i], &rfds))) {
for (std::vector<in_dev>::iterator i = indev.begin(); i != indev.end(); ++i) {
if (((*i).fd != -1) && (FD_ISSET((*i).fd, &rfds))) {
uint64_t now_pressed = 0;
t_input_event ev;
memset(&ev, 0, sizeof(ev));
/* we later check for ev.type = EV_SYN = 0x00, so set something invalid here... */
ev.type = EV_MAX;
int ret = read(fd_rc[i], &ev, sizeof(t_input_event));
int ret = read((*i).fd, &ev, sizeof(t_input_event));
if (ret != sizeof(t_input_event)) {
if (errno == ENODEV) {
/* hot-unplugged? */
::close(fd_rc[i]);
fd_rc[i] = -1;
::close((*i).fd);
indev.erase(i);
}
continue;
}
@@ -1430,11 +1491,11 @@ void CRCInput::clearRCMsg()
{
t_input_event ev;
for (int i = 0; i < NUMBER_OF_EVENT_DEVICES; i++)
for (unsigned int i = 0; i < indev.size(); i++)
{
if (fd_rc[i] != -1)
if (indev[i].fd != -1)
{
while (read(fd_rc[i], &ev, sizeof(t_input_event)) == sizeof(t_input_event))
while (read(indev[i].fd, &ev, sizeof(t_input_event)) == sizeof(t_input_event))
;
}
}
@@ -1688,9 +1749,12 @@ void CRCInput::play_click()
void CRCInput::set_rc_hw(ir_protocol_t ir_protocol, unsigned int ir_address)
{
int ioctl_ret = -1;
if (indev.empty()) {
printf("[rcinput:%s] indev is empty!\n", __func__);
return;
}
//fixme?: for now fd_rc[] is hardcoded to 0 since only fd_rc[0] is used at the moment
ioctl_ret = ::ioctl(fd_rc[0], IOC_IR_SET_PRI_PROTOCOL, ir_protocol);
ioctl_ret = ::ioctl(indev[0].fd, IOC_IR_SET_PRI_PROTOCOL, ir_protocol);
if(ioctl_ret < 0)
perror("IOC_IR_SET_PRI_PROTOCOL");
else
@@ -1700,7 +1764,7 @@ void CRCInput::set_rc_hw(ir_protocol_t ir_protocol, unsigned int ir_address)
if(ir_address > 0)
{
//fixme?: for now fd_rc[] is hardcoded to 0 since only fd_rc[0] is used at the moment
ioctl_ret = ::ioctl(fd_rc[0], IOC_IR_SET_PRI_ADDRESS, ir_address);
ioctl_ret = ::ioctl(indev[0].fd, IOC_IR_SET_PRI_ADDRESS, ir_address);
if(ioctl_ret < 0)
perror("IOC_IR_SET_PRI_ADDRESS");
else