diff --git a/src/driver/rcinput.cpp b/src/driver/rcinput.cpp index a7a3da9de..bbcfbb2b7 100644 --- a/src/driver/rcinput.cpp +++ b/src/driver/rcinput.cpp @@ -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 #include -#include #include #include #include @@ -50,6 +49,8 @@ #include #include +#include + #include #include @@ -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 , 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::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 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 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::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; } @@ -1436,11 +1497,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)) ; } } @@ -1694,9 +1755,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 @@ -1706,7 +1770,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 diff --git a/src/driver/rcinput.h b/src/driver/rcinput.h index 4bd580f3f..9a859b929 100644 --- a/src/driver/rcinput.h +++ b/src/driver/rcinput.h @@ -39,6 +39,9 @@ #include #include +#include +#include + #ifdef BOXMODEL_CS_HD2 #ifdef HAVE_COOLSTREAM_CS_IR_GENERIC_H #include @@ -138,6 +141,12 @@ class CRCInput bool correct_time; }; + struct in_dev + { + int fd; + std::string path; + }; + uint32_t timerid; std::vector timers; @@ -147,21 +156,19 @@ class CRCInput int fd_pipe_high_priority[2]; int fd_pipe_low_priority[2]; int fd_gamerc; -#ifdef HAVE_SPARK_HARDWARE -#define NUMBER_OF_EVENT_DEVICES 2 -#else -#define NUMBER_OF_EVENT_DEVICES 1 -#endif - int fd_rc[NUMBER_OF_EVENT_DEVICES]; + std::vector indev; int fd_keyb; int fd_event; int fd_max; int clickfd; __u16 rc_last_key; + OpenThreads::Mutex mutex; void set_dsp(); - void open(int dev = -1); + void open(bool recheck = false); + bool checkpath(in_dev id); + bool checkdev(); void close(); int translate(int code); void calculateMaxFd(void); @@ -280,7 +287,9 @@ class CRCInput inline int getFileHandle(void) /* used for plugins (i.e. games) only */ { - return fd_rc[0]; + if (indev.empty()) + return -1; + return indev[0].fd; } void stopInput(const bool ext = false); void restartInput(const bool ext = false);