MythTV  0.26-pre
lirc.cpp
Go to the documentation of this file.
00001 
00002 // Own header
00003 #include "lirc.h"
00004 
00005 // C headers
00006 #include <cstdio>
00007 #include <cerrno>
00008 #include <cstdlib>
00009 
00010 // C++ headers
00011 #include <algorithm>
00012 #include <vector>
00013 using namespace std;
00014 
00015 // Qt headers
00016 #include <QCoreApplication>
00017 #include <QEvent>
00018 #include <QKeySequence>
00019 #include <QStringList>
00020 
00021 // MythTV headers
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         // Connect the unix socket
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         // On Linux, select() can indicate data when there isn't
00261         // any due to TCP checksum in-particular; to avoid getting
00262         // stuck on a read() call add the O_NONBLOCK flag.
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         // Attempt to inline out-of-band messages and keep the connection open..
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     // parse the config file
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     // lirc_code2char will make code point to a static datafer..
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         // Send a dummy keycode if we couldn't convert the key sequence.
00353         // This is done so the main code can output a warning for bad
00354         // mappings.
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     /* Process all events read */
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); // wait a while before we retry..
00430             
00431             continue;
00432         }
00433 
00434         fd_set readfds;
00435         FD_ZERO(&readfds);
00436         FD_SET(d->lircState->lirc_lircd, &readfds);
00437 
00438         // the maximum time select() should wait
00439         struct timeval timeout;
00440         timeout.tv_sec = 1; // 1 second
00441         timeout.tv_usec = 100 * 1000; // 100 ms
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         //  0: Timer expired with no data, repeat select
00453         // -1: Iinterrupted while waiting, repeat select
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:   // 107 (according to asm-generic/errno.h)
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 
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends