00001
00002
00003 #include "lirc.h"
00004
00005
00006 #include <cstdio>
00007 #include <cerrno>
00008 #include <cstdlib>
00009
00010
00011 #include <algorithm>
00012 #include <vector>
00013 using namespace std;
00014
00015
00016 #include <QCoreApplication>
00017 #include <QEvent>
00018 #include <QKeySequence>
00019 #include <QStringList>
00020
00021
00022 #include "mythdb.h"
00023 #include "mythsystem.h"
00024 #include "lircevent.h"
00025 #include "lirc_client.h"
00026
00027 #include <sys/types.h>
00028 #include <sys/socket.h>
00029 #include <sys/select.h>
00030 #include <sys/un.h>
00031 #include <netinet/in.h>
00032 #include <arpa/inet.h>
00033 #include <netdb.h>
00034 #include <unistd.h>
00035 #include <fcntl.h>
00036 #include <sys/wait.h>
00037 #include "mythlogging.h"
00038
00039 #define LOC QString("LIRC: ")
00040
00041 class LIRCPriv
00042 {
00043 public:
00044 LIRCPriv() : lircState(NULL), lircConfig(NULL) {}
00045 ~LIRCPriv()
00046 {
00047 if (lircState)
00048 {
00049 lirc_deinit(lircState);
00050 lircState = NULL;
00051 }
00052 if (lircConfig)
00053 {
00054 lirc_freeconfig(lircConfig);
00055 lircConfig = NULL;
00056 }
00057 }
00058
00059 struct lirc_state *lircState;
00060 struct lirc_config *lircConfig;
00061 };
00062
00063 QMutex LIRC::lirclib_lock;
00064
00072 LIRC::LIRC(QObject *main_window,
00073 const QString &lircd_device,
00074 const QString &our_program,
00075 const QString &config_file)
00076 : MThread("LIRC"),
00077 lock(QMutex::Recursive),
00078 m_mainWindow(main_window),
00079 lircdDevice(lircd_device),
00080 program(our_program),
00081 configFile(config_file),
00082 doRun(false),
00083 buf_offset(0),
00084 eofCount(0),
00085 retryCount(0),
00086 d(new LIRCPriv())
00087 {
00088 lircdDevice.detach();
00089 program.detach();
00090 configFile.detach();
00091 buf.resize(0);
00092 }
00093
00094 LIRC::~LIRC()
00095 {
00096 TeardownAll();
00097 }
00098
00099 void LIRC::deleteLater(void)
00100 {
00101 TeardownAll();
00102 QObject::deleteLater();
00103 }
00104
00105 void LIRC::TeardownAll(void)
00106 {
00107 QMutexLocker locker(&lock);
00108 if (doRun)
00109 {
00110 doRun = false;
00111 lock.unlock();
00112 wait();
00113 lock.lock();
00114 }
00115
00116 if (d)
00117 {
00118 delete d;
00119 d = NULL;
00120 }
00121 }
00122
00123 static QByteArray get_ip(const QString &h)
00124 {
00125 QByteArray hba = h.toLatin1();
00126 struct in_addr sin_addr;
00127 if (inet_aton(hba.constData(), &sin_addr))
00128 return hba;
00129
00130 struct addrinfo hints;
00131 memset(&hints, 0, sizeof(hints));
00132 hints.ai_family = AF_INET;
00133 hints.ai_socktype = SOCK_STREAM;
00134 hints.ai_protocol = IPPROTO_TCP;
00135
00136 struct addrinfo *result;
00137 int err = getaddrinfo(hba.constData(), NULL, &hints, &result);
00138 if (err)
00139 {
00140 LOG(VB_GENERAL, LOG_DEBUG,
00141 QString("get_ip: %1").arg(gai_strerror(err)));
00142 return QString("").toLatin1();
00143 }
00144
00145 int addrlen = result->ai_addrlen;
00146 if (!addrlen)
00147 {
00148 freeaddrinfo(result);
00149 return QString("").toLatin1();
00150 }
00151
00152 if (result->ai_addr->sa_family != AF_INET)
00153 {
00154 freeaddrinfo(result);
00155 return QString("").toLatin1();
00156 }
00157
00158 sin_addr = ((struct sockaddr_in*)(result->ai_addr))->sin_addr;
00159 hba = QByteArray(inet_ntoa(sin_addr));
00160 freeaddrinfo(result);
00161
00162 return hba;
00163 }
00164
00165 bool LIRC::Init(void)
00166 {
00167 QMutexLocker locker(&lock);
00168 if (d->lircState)
00169 return true;
00170
00171 uint64_t vtype = (0 == retryCount) ? VB_GENERAL : VB_FILE;
00172
00173 int lircd_socket = -1;
00174 if (lircdDevice.startsWith('/'))
00175 {
00176
00177 QByteArray dev = lircdDevice.toLocal8Bit();
00178 if (dev.size() > 107)
00179 {
00180 LOG(vtype, LOG_ERR, LOC +
00181 QString("lircdDevice '%1'").arg(lircdDevice) +
00182 " is too long for the 'unix' socket API");
00183
00184 return false;
00185 }
00186
00187 lircd_socket = socket(AF_UNIX, SOCK_STREAM, 0);
00188 if (lircd_socket < 0)
00189 {
00190 LOG(vtype, LOG_ERR, LOC + QString("Failed to open Unix socket '%1'")
00191 .arg(lircdDevice) + ENO);
00192
00193 return false;
00194 }
00195
00196 struct sockaddr_un addr;
00197 memset(&addr, 0, sizeof(sockaddr_un));
00198 addr.sun_family = AF_UNIX;
00199 strncpy(addr.sun_path, dev.constData(),107);
00200
00201 int ret = ::connect(lircd_socket, (struct sockaddr*) &addr,
00202 sizeof(addr));
00203
00204 if (ret < 0)
00205 {
00206 LOG(vtype, LOG_ERR, LOC +
00207 QString("Failed to connect to Unix socket '%1'")
00208 .arg(lircdDevice) + ENO);
00209
00210 close(lircd_socket);
00211 return false;
00212 }
00213 }
00214 else
00215 {
00216 lircd_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
00217 if (lircd_socket < 0)
00218 {
00219 LOG(vtype, LOG_ERR, LOC + QString("Failed to open TCP socket '%1'")
00220 .arg(lircdDevice) + ENO);
00221
00222 return false;
00223 }
00224
00225 QString dev = lircdDevice;
00226 uint port = 8765;
00227 QStringList tmp = lircdDevice.split(':');
00228 if (2 == tmp.size())
00229 {
00230 dev = tmp[0];
00231 port = (tmp[1].toUInt()) ? tmp[1].toUInt() : port;
00232 }
00233 QByteArray device = get_ip(dev);
00234 struct sockaddr_in addr;
00235 memset(&addr, 0, sizeof(sockaddr_in));
00236 addr.sin_family = AF_INET;
00237 addr.sin_port = htons(port);
00238
00239 if (!inet_aton(device.constData(), &addr.sin_addr))
00240 {
00241 LOG(vtype, LOG_ERR, LOC + QString("Failed to parse IP address '%1'")
00242 .arg(dev));
00243
00244 close(lircd_socket);
00245 return false;
00246 }
00247
00248 int ret = ::connect(lircd_socket, (struct sockaddr*) &addr,
00249 sizeof(addr));
00250 if (ret < 0)
00251 {
00252 LOG(vtype, LOG_ERR, LOC +
00253 QString("Failed to connect TCP socket '%1'")
00254 .arg(lircdDevice) + ENO);
00255
00256 close(lircd_socket);
00257 return false;
00258 }
00259
00260
00261
00262
00263 int flags = fcntl(lircd_socket, F_GETFD);
00264 if (flags >= 0)
00265 {
00266 ret = fcntl(lircd_socket, F_SETFD, flags | O_NONBLOCK);
00267 if (ret < 0)
00268 {
00269 LOG(VB_GENERAL, LOG_WARNING, LOC +
00270 QString("Failed set flags for socket '%1'")
00271 .arg(lircdDevice) + ENO);
00272 }
00273 }
00274
00275
00276 int i = 1;
00277 setsockopt(lircd_socket, SOL_SOCKET, SO_OOBINLINE, &i, sizeof(i));
00278 i = 1;
00279 setsockopt(lircd_socket, SOL_SOCKET, SO_KEEPALIVE, &i, sizeof(i));
00280 }
00281
00282 d->lircState = lirc_init("/etc/lircrc", ".lircrc", "mythtv", NULL, 0);
00283 if (!d->lircState)
00284 {
00285 close(lircd_socket);
00286 return false;
00287 }
00288 d->lircState->lirc_lircd = lircd_socket;
00289
00290
00291 if (!d->lircConfig)
00292 {
00293 QMutexLocker static_lock(&lirclib_lock);
00294 QByteArray cfg = configFile.toLocal8Bit();
00295 if (lirc_readconfig(d->lircState, cfg.constData(), &d->lircConfig, NULL))
00296 {
00297 LOG(vtype, LOG_ERR, LOC +
00298 QString("Failed to read config file '%1'").arg(configFile));
00299
00300 lirc_deinit(d->lircState);
00301 d->lircState = NULL;
00302 return false;
00303 }
00304 }
00305
00306 LOG(VB_GENERAL, LOG_INFO, LOC +
00307 QString("Successfully initialized '%1' using '%2' config")
00308 .arg(lircdDevice).arg(configFile));
00309
00310 return true;
00311 }
00312
00313 void LIRC::start(void)
00314 {
00315 QMutexLocker locker(&lock);
00316
00317 if (!d->lircState)
00318 {
00319 LOG(VB_GENERAL, LOG_ERR, "start() called without lircd socket");
00320 return;
00321 }
00322
00323 doRun = true;
00324 MThread::start();
00325 }
00326
00327 bool LIRC::IsDoRunSet(void) const
00328 {
00329 QMutexLocker locker(&lock);
00330 return doRun;
00331 }
00332
00333 void LIRC::Process(const QByteArray &data)
00334 {
00335 QMutexLocker static_lock(&lirclib_lock);
00336
00337
00338 char *code = NULL;
00339 int ret = lirc_code2char(
00340 d->lircState, d->lircConfig, const_cast<char*>(data.constData()), &code);
00341
00342 while ((0 == ret) && code)
00343 {
00344 QString lirctext(code);
00345 QString qtcode = code;
00346 qtcode.replace("ctrl-", "ctrl+", Qt::CaseInsensitive);
00347 qtcode.replace("alt-", "alt+", Qt::CaseInsensitive);
00348 qtcode.replace("shift-", "shift+", Qt::CaseInsensitive);
00349 qtcode.replace("meta-", "meta+", Qt::CaseInsensitive);
00350 QKeySequence a(qtcode);
00351
00352
00353
00354
00355 if (!a.count())
00356 {
00357 QCoreApplication::postEvent(
00358 m_mainWindow, new LircKeycodeEvent(
00359 QEvent::KeyPress, 0,
00360 (Qt::KeyboardModifiers)
00361 LircKeycodeEvent::kLIRCInvalidKeyCombo,
00362 QString(), lirctext));
00363 }
00364
00365 vector<LircKeycodeEvent*> keyReleases;
00366 for (unsigned int i = 0; i < a.count(); i++)
00367 {
00368 int keycode = a[i];
00369 Qt::KeyboardModifiers mod = Qt::NoModifier;
00370 mod |= (Qt::SHIFT & keycode) ? Qt::ShiftModifier : Qt::NoModifier;
00371 mod |= (Qt::META & keycode) ? Qt::MetaModifier : Qt::NoModifier;
00372 mod |= (Qt::CTRL & keycode) ? Qt::ControlModifier: Qt::NoModifier;
00373 mod |= (Qt::ALT & keycode) ? Qt::AltModifier : Qt::NoModifier;
00374
00375 keycode &= ~Qt::MODIFIER_MASK;
00376
00377 QString text = "";
00378 if (!mod)
00379 text = QString(QChar(keycode));
00380
00381 QCoreApplication::postEvent(
00382 m_mainWindow, new LircKeycodeEvent(
00383 QEvent::KeyPress, keycode, mod, text, lirctext));
00384
00385 keyReleases.push_back(
00386 new LircKeycodeEvent(
00387 QEvent::KeyRelease, keycode, mod, text, lirctext));
00388 }
00389
00390 for (int i = (int)keyReleases.size() - 1; i>=0; i--)
00391 QCoreApplication::postEvent(m_mainWindow, keyReleases[i]);
00392
00393 ret = lirc_code2char(
00394 d->lircState, d->lircConfig, const_cast<char*>(data.constData()), &code);
00395 }
00396 }
00397
00398 void LIRC::run(void)
00399 {
00400 RunProlog();
00401 #if 0
00402 LOG(VB_GENERAL, LOG_DEBUG, LOC + "run -- start");
00403 #endif
00404
00405 while (IsDoRunSet())
00406 {
00407 if (eofCount && retryCount)
00408 usleep(100 * 1000);
00409
00410 if ((eofCount >= 10) || (!d->lircState))
00411 {
00412 QMutexLocker locker(&lock);
00413 eofCount = 0;
00414 if (++retryCount > 1000)
00415 {
00416 LOG(VB_GENERAL, LOG_ERR, LOC +
00417 "Failed to reconnect, exiting LIRC thread.");
00418 doRun = false;
00419 continue;
00420 }
00421 LOG(VB_FILE, LOG_WARNING, LOC + "EOF -- reconnecting");
00422
00423 lirc_deinit(d->lircState);
00424 d->lircState = NULL;
00425
00426 if (Init())
00427 retryCount = 0;
00428 else
00429 sleep(2);
00430
00431 continue;
00432 }
00433
00434 fd_set readfds;
00435 FD_ZERO(&readfds);
00436 FD_SET(d->lircState->lirc_lircd, &readfds);
00437
00438
00439 struct timeval timeout;
00440 timeout.tv_sec = 1;
00441 timeout.tv_usec = 100 * 1000;
00442
00443 int ret = select(d->lircState->lirc_lircd + 1, &readfds, NULL, NULL,
00444 &timeout);
00445
00446 if (ret < 0 && errno != EINTR)
00447 {
00448 LOG(VB_GENERAL, LOG_ERR, LOC + "select() failed" + ENO);
00449 continue;
00450 }
00451
00452
00453
00454 if (ret <= 0)
00455 continue;
00456
00457 QList<QByteArray> codes = GetCodes();
00458 for (uint i = 0; i < (uint) codes.size(); i++)
00459 Process(codes[i]);
00460 }
00461 #if 0
00462 LOG(VB_GENERAL, LOG_DEBUG, LOC + "run -- end");
00463 #endif
00464 RunEpilog();
00465 }
00466
00467 QList<QByteArray> LIRC::GetCodes(void)
00468 {
00469 QList<QByteArray> ret;
00470 ssize_t len = -1;
00471
00472 uint buf_size = buf.size() + 128;
00473 buf.resize(buf_size);
00474
00475 while (true)
00476 {
00477 len = read(d->lircState->lirc_lircd, buf.data() + buf_offset, 128);
00478 if (len >= 0)
00479 break;
00480
00481 switch (errno)
00482 {
00483 case EINTR:
00484 continue;
00485
00486 case EAGAIN:
00487 return ret;
00488
00489 case ENOTCONN:
00490 if (!eofCount)
00491 LOG(VB_GENERAL, LOG_NOTICE, LOC + "GetCodes -- EOF?");
00492 eofCount++;
00493 return ret;
00494
00495 default:
00496 LOG(VB_GENERAL, LOG_ERR, LOC + "Could not read socket" + ENO);
00497 return ret;
00498 }
00499 }
00500
00501 if (!len)
00502 {
00503 if (!eofCount)
00504 LOG(VB_GENERAL, LOG_NOTICE, LOC + "GetCodes -- eof?");
00505 eofCount++;
00506 return ret;
00507 }
00508
00509 eofCount = 0;
00510 retryCount = 0;
00511
00512 buf_offset += len;
00513 buf.truncate(buf_offset);
00514 ret = buf.split('\n');
00515 if (buf.endsWith('\n'))
00516 {
00517 buf_offset = 0;
00518 return ret;
00519 }
00520
00521 buf = ret.takeLast();
00522 buf_offset = buf.size();
00523 return ret;
00524 }
00525