mirror of
https://github.com/VDR4Arch/vdr.git
synced 2023-10-10 13:36:52 +02:00
505 lines
12 KiB
C
505 lines
12 KiB
C
/*
|
|
* remote.c: Interface to the Remote Control Unit
|
|
*
|
|
* See the main source file 'vdr.c' for copyright information and
|
|
* how to reach the author.
|
|
*
|
|
* Ported to LIRC by Carsten Koch <Carsten.Koch@icem.de> 2000-06-16.
|
|
*
|
|
* $Id: remote.c 1.27 2002/05/18 12:55:39 kls Exp $
|
|
*/
|
|
|
|
#include "remote.h"
|
|
#include <fcntl.h>
|
|
#include <netinet/in.h>
|
|
#include <string.h>
|
|
#include <sys/types.h>
|
|
#include <sys/time.h>
|
|
#include <termios.h>
|
|
#include <unistd.h>
|
|
|
|
#if defined REMOTE_LIRC
|
|
#include <sys/socket.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/un.h>
|
|
#endif
|
|
|
|
#if defined REMOTE_KBD
|
|
#include <ncurses.h>
|
|
#endif
|
|
|
|
#include "config.h"
|
|
#include "tools.h"
|
|
|
|
// --- cRcIoBase -------------------------------------------------------------
|
|
|
|
cRcIoBase::cRcIoBase(void)
|
|
{
|
|
t = 0;
|
|
}
|
|
|
|
cRcIoBase::~cRcIoBase()
|
|
{
|
|
}
|
|
|
|
// --- cRcIoKBD --------------------------------------------------------------
|
|
|
|
#if defined REMOTE_KBD
|
|
|
|
cRcIoKBD::cRcIoKBD(void)
|
|
{
|
|
f.Open(0); // stdin
|
|
}
|
|
|
|
cRcIoKBD::~cRcIoKBD()
|
|
{
|
|
}
|
|
|
|
void cRcIoKBD::Flush(int WaitMs)
|
|
{
|
|
int t0 = time_ms();
|
|
|
|
timeout(10);
|
|
for (;;) {
|
|
while (getch() > 0)
|
|
t0 = time_ms();
|
|
if (time_ms() - t0 >= WaitMs)
|
|
break;
|
|
}
|
|
}
|
|
|
|
bool cRcIoKBD::InputAvailable(void)
|
|
{
|
|
return f.Ready(false);
|
|
}
|
|
|
|
bool cRcIoKBD::GetCommand(unsigned int *Command, bool *Repeat, bool *Release)
|
|
{
|
|
if (Command) {
|
|
*Command = getch();
|
|
return int(*Command) > 0;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// --- cRcIoRCU --------------------------------------------------------------
|
|
|
|
#elif defined REMOTE_RCU
|
|
|
|
#define REPEATLIMIT 20 // ms
|
|
#define REPEATDELAY 350 // ms
|
|
|
|
cRcIoRCU::cRcIoRCU(char *DeviceName)
|
|
{
|
|
dp = 0;
|
|
mode = modeB;
|
|
code = 0;
|
|
address = 0xFFFF;
|
|
receivedAddress = 0;
|
|
receivedCommand = 0;
|
|
receivedData = receivedRepeat = receivedRelease = false;
|
|
lastNumber = 0;
|
|
if ((f = open(DeviceName, O_RDWR | O_NONBLOCK)) >= 0) {
|
|
struct termios t;
|
|
if (tcgetattr(f, &t) == 0) {
|
|
cfsetspeed(&t, B9600);
|
|
cfmakeraw(&t);
|
|
if (tcsetattr(f, TCSAFLUSH, &t) == 0) {
|
|
Start();
|
|
return;
|
|
}
|
|
}
|
|
LOG_ERROR_STR(DeviceName);
|
|
close(f);
|
|
}
|
|
else
|
|
LOG_ERROR_STR(DeviceName);
|
|
f = -1;
|
|
}
|
|
|
|
cRcIoRCU::~cRcIoRCU()
|
|
{
|
|
Cancel();
|
|
}
|
|
|
|
void cRcIoRCU::Action(void)
|
|
{
|
|
#pragma pack(1)
|
|
union {
|
|
struct {
|
|
unsigned short address;
|
|
unsigned int command;
|
|
} data;
|
|
unsigned char raw[6];
|
|
} buffer;
|
|
#pragma pack()
|
|
|
|
dsyslog("RCU remote control thread started (pid=%d)", getpid());
|
|
|
|
int FirstTime = 0;
|
|
unsigned int LastCommand = 0;
|
|
|
|
for (; f >= 0;) {
|
|
|
|
LOCK_THREAD;
|
|
|
|
if (ReceiveByte(REPEATLIMIT) == 'X') {
|
|
for (int i = 0; i < 6; i++) {
|
|
int b = ReceiveByte();
|
|
if (b >= 0) {
|
|
buffer.raw[i] = b;
|
|
if (i == 5) {
|
|
unsigned short Address = ntohs(buffer.data.address); // the PIC sends bytes in "network order"
|
|
unsigned int Command = ntohl(buffer.data.command);
|
|
if (code == 'B' && address == 0x0000 && Command == 0x00004000)
|
|
// Well, well, if it isn't the "d-box"...
|
|
// This remote control sends the above command before and after
|
|
// each keypress - let's just drop this:
|
|
break;
|
|
if (!receivedData) { // only accept new data the previous data has been fetched
|
|
int Now = time_ms();
|
|
if (Command != LastCommand) {
|
|
receivedAddress = Address;
|
|
receivedCommand = Command;
|
|
receivedData = true;
|
|
receivedRepeat = receivedRelease = false;
|
|
FirstTime = Now;
|
|
}
|
|
else {
|
|
if (Now - FirstTime < REPEATDELAY)
|
|
break; // repeat function kicks in after a short delay
|
|
receivedData = receivedRepeat = true;
|
|
}
|
|
LastCommand = Command;
|
|
WakeUp();
|
|
}
|
|
}
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
}
|
|
else if (receivedData) { // the last data before releasing the key hasn't been fetched yet
|
|
if (receivedRepeat) { // it was a repeat, so let's make it a release
|
|
receivedRepeat = false;
|
|
receivedRelease = true;
|
|
LastCommand = 0;
|
|
WakeUp();
|
|
}
|
|
}
|
|
else if (receivedRepeat) { // all data has already been fetched, but the last one was a repeat, so let's generate a release
|
|
receivedData = receivedRelease = true;
|
|
receivedRepeat = false;
|
|
LastCommand = 0;
|
|
WakeUp();
|
|
}
|
|
else
|
|
LastCommand = 0;
|
|
}
|
|
}
|
|
|
|
int cRcIoRCU::ReceiveByte(int TimeoutMs)
|
|
{
|
|
// Returns the byte if one was received within a timeout, -1 otherwise
|
|
if (cFile::FileReady(f, TimeoutMs)) {
|
|
unsigned char b;
|
|
if (safe_read(f, &b, 1) == 1)
|
|
return b;
|
|
else
|
|
LOG_ERROR;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
bool cRcIoRCU::SendByteHandshake(unsigned char c)
|
|
{
|
|
if (f >= 0) {
|
|
int w = write(f, &c, 1);
|
|
if (w == 1) {
|
|
for (int reply = ReceiveByte(REPEATLIMIT); reply >= 0;) {
|
|
if (reply == c)
|
|
return true;
|
|
else if (reply == 'X') {
|
|
// skip any incoming RC code - it will come again
|
|
for (int i = 6; i--;) {
|
|
if (ReceiveByte() < 0)
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
return false;
|
|
}
|
|
}
|
|
LOG_ERROR;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool cRcIoRCU::SendByte(unsigned char c)
|
|
{
|
|
LOCK_THREAD;
|
|
|
|
for (int retry = 5; retry--;) {
|
|
if (SendByteHandshake(c))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool cRcIoRCU::SetCode(unsigned char Code, unsigned short Address)
|
|
{
|
|
code = Code;
|
|
address = Address;
|
|
return SendCommand(code);
|
|
}
|
|
|
|
bool cRcIoRCU::SetMode(unsigned char Mode)
|
|
{
|
|
mode = Mode;
|
|
return SendCommand(mode);
|
|
}
|
|
|
|
void cRcIoRCU::Flush(int WaitMs)
|
|
{
|
|
LOCK_THREAD;
|
|
|
|
int t0 = time_ms();
|
|
for (;;) {
|
|
while (ReceiveByte() >= 0)
|
|
t0 = time_ms();
|
|
if (time_ms() - t0 >= WaitMs)
|
|
break;
|
|
}
|
|
receivedData = receivedRepeat = false;
|
|
}
|
|
|
|
bool cRcIoRCU::GetCommand(unsigned int *Command, bool *Repeat, bool *Release)
|
|
{
|
|
if (receivedData) { // first we check the boolean flag without a lock, to avoid delays
|
|
|
|
LOCK_THREAD;
|
|
|
|
if (receivedData) { // need to check again, since the status might have changed while waiting for the lock
|
|
if (Command)
|
|
*Command = receivedCommand;
|
|
if (Repeat)
|
|
*Repeat = receivedRepeat;
|
|
if (Release)
|
|
*Release = receivedRelease;
|
|
receivedData = false;
|
|
return true;
|
|
}
|
|
}
|
|
if (time(NULL) - t > 60) {
|
|
SendCommand(code); // in case the PIC listens to the wrong code
|
|
t = time(NULL);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool cRcIoRCU::SendCommand(unsigned char Cmd)
|
|
{
|
|
return SendByte(Cmd | 0x80);
|
|
}
|
|
|
|
bool cRcIoRCU::Digit(int n, int v)
|
|
{
|
|
return SendByte(((n & 0x03) << 5) | (v & 0x0F) | (((dp >> n) & 0x01) << 4));
|
|
}
|
|
|
|
bool cRcIoRCU::Number(int n, bool Hex)
|
|
{
|
|
LOCK_THREAD;
|
|
|
|
if (!Hex) {
|
|
char buf[8];
|
|
sprintf(buf, "%4d", n & 0xFFFF);
|
|
n = 0;
|
|
for (char *d = buf; *d; d++) {
|
|
if (*d == ' ')
|
|
*d = 0xF;
|
|
n = (n << 4) | ((*d - '0') & 0x0F);
|
|
}
|
|
}
|
|
lastNumber = n;
|
|
for (int i = 0; i < 4; i++) {
|
|
if (!Digit(i, n))
|
|
return false;
|
|
n >>= 4;
|
|
}
|
|
return SendCommand(mode);
|
|
}
|
|
|
|
bool cRcIoRCU::String(char *s)
|
|
{
|
|
LOCK_THREAD;
|
|
|
|
const char *chars = mode == modeH ? "0123456789ABCDEF" : "0123456789-EHLP ";
|
|
int n = 0;
|
|
|
|
for (int i = 0; *s && i < 4; s++, i++) {
|
|
n <<= 4;
|
|
for (const char *c = chars; *c; c++) {
|
|
if (*c == *s) {
|
|
n |= c - chars;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return Number(n, true);
|
|
}
|
|
|
|
void cRcIoRCU::SetPoints(unsigned char Dp, bool On)
|
|
{
|
|
if (On)
|
|
dp |= Dp;
|
|
else
|
|
dp &= ~Dp;
|
|
Number(lastNumber, true);
|
|
}
|
|
|
|
bool cRcIoRCU::DetectCode(unsigned char *Code, unsigned short *Address)
|
|
{
|
|
// Caller should initialize 'Code' to 0 and call DetectCode()
|
|
// until it returns true. Whenever DetectCode() returns false
|
|
// and 'Code' is not 0, the caller can use 'Code' to display
|
|
// a message like "Trying code '%c'". If false is returned and
|
|
// 'Code' is 0, all possible codes have been tried and the caller
|
|
// can either stop calling DetectCode() (and give some error
|
|
// message), or start all over again.
|
|
if (*Code < 'A' || *Code > 'D') {
|
|
*Code = 'A';
|
|
return false;
|
|
}
|
|
if (*Code <= 'D') {
|
|
SetMode(modeH);
|
|
char buf[5];
|
|
sprintf(buf, "C0D%c", *Code);
|
|
String(buf);
|
|
SetCode(*Code, 0);
|
|
delay_ms(REPEATDELAY);
|
|
receivedData = receivedRepeat = 0;
|
|
delay_ms(REPEATDELAY);
|
|
if (GetCommand()) {
|
|
*Address = receivedAddress;
|
|
SetMode(modeB);
|
|
String("----");
|
|
return true;
|
|
}
|
|
if (*Code < 'D') {
|
|
(*Code)++;
|
|
return false;
|
|
}
|
|
}
|
|
*Code = 0;
|
|
return false;
|
|
}
|
|
|
|
// --- cRcIoLIRC -------------------------------------------------------------
|
|
|
|
#elif defined REMOTE_LIRC
|
|
|
|
#define REPEATLIMIT 20 // ms
|
|
#define REPEATDELAY 350 // ms
|
|
|
|
cRcIoLIRC::cRcIoLIRC(char *DeviceName)
|
|
{
|
|
*keyName = 0;
|
|
receivedData = receivedRepeat = false;
|
|
struct sockaddr_un addr;
|
|
addr.sun_family = AF_UNIX;
|
|
strcpy(addr.sun_path, DeviceName);
|
|
if ((f = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0) {
|
|
if (connect(f, (struct sockaddr *)&addr, sizeof(addr)) >= 0) {
|
|
Start();
|
|
return;
|
|
}
|
|
LOG_ERROR_STR(DeviceName);
|
|
close(f);
|
|
}
|
|
else
|
|
LOG_ERROR_STR(DeviceName);
|
|
f = -1;
|
|
}
|
|
|
|
cRcIoLIRC::~cRcIoLIRC()
|
|
{
|
|
Cancel();
|
|
}
|
|
|
|
void cRcIoLIRC::Action(void)
|
|
{
|
|
dsyslog("LIRC remote control thread started (pid=%d)", getpid());
|
|
|
|
int FirstTime = 0;
|
|
int LastTime = 0;
|
|
char buf[LIRC_BUFFER_SIZE];
|
|
char LastKeyName[LIRC_KEY_BUF];
|
|
|
|
for (; f >= 0;) {
|
|
|
|
LOCK_THREAD;
|
|
|
|
if (cFile::FileReady(f, REPEATLIMIT) && safe_read(f, buf, sizeof(buf)) > 21) {
|
|
if (!receivedData) { // only accept new data the previous data has been fetched
|
|
int count;
|
|
sscanf(buf, "%*x %x %29s", &count, LastKeyName); // '29' in '%29s' is LIRC_KEY_BUF-1!
|
|
int Now = time_ms();
|
|
if (count == 0) {
|
|
strcpy(keyName, LastKeyName);
|
|
receivedData = true;
|
|
receivedRepeat = receivedRelease = false;
|
|
FirstTime = Now;
|
|
}
|
|
else {
|
|
if (Now - FirstTime < REPEATDELAY)
|
|
continue; // repeat function kicks in after a short delay
|
|
receivedData = receivedRepeat = true;
|
|
receivedRelease = false;
|
|
}
|
|
LastTime = Now;
|
|
WakeUp();
|
|
}
|
|
}
|
|
else if (receivedData) { // the last data before releasing the key hasn't been fetched yet
|
|
if (receivedRepeat) { // it was a repeat, so let's make it a release
|
|
if (time_ms() - LastTime > REPEATDELAY) {
|
|
receivedRepeat = false;
|
|
receivedRelease = true;
|
|
WakeUp();
|
|
}
|
|
}
|
|
}
|
|
else if (receivedRepeat) { // all data has already been fetched, but the last one was a repeat, so let's generate a release
|
|
if (time_ms() - LastTime > REPEATDELAY) {
|
|
receivedData = receivedRelease = true;
|
|
receivedRepeat = false;
|
|
WakeUp();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool cRcIoLIRC::GetCommand(unsigned int *Command, bool *Repeat, bool *Release)
|
|
{
|
|
if (receivedData) { // first we check the boolean flag without a lock, to avoid delays
|
|
|
|
LOCK_THREAD;
|
|
|
|
if (receivedData) { // need to check again, since the status might have changed while waiting for the lock
|
|
if (Command)
|
|
*Command = Keys.Encode(keyName);
|
|
if (Repeat)
|
|
*Repeat = receivedRepeat;
|
|
if (Release)
|
|
*Release = receivedRelease;
|
|
receivedData = false;
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
#endif
|
|
|