MythTV  0.26-pre
cardutil.cpp
Go to the documentation of this file.
00001 // Standard UNIX C headers
00002 #include <fcntl.h>
00003 #include <unistd.h>
00004 
00005 #include <algorithm>
00006 
00007 #if defined(USING_V4L2) || defined(USING_DVB)
00008 #include <sys/ioctl.h>
00009 #endif
00010 
00011 // Qt headers
00012 #include <QMap>
00013 #include <QDir>
00014 
00015 // MythTV headers
00016 #include "mythconfig.h"
00017 #include "cardutil.h"
00018 #include "videosource.h"
00019 #include "dvbchannel.h"
00020 #include "diseqcsettings.h"
00021 #include "sourceutil.h"
00022 #include "mythdb.h"
00023 #include "mythlogging.h"
00024 
00025 #ifdef USING_DVB
00026 #include "dvbtypes.h"
00027 #endif
00028 
00029 #ifdef USING_V4L1
00030 #include <linux/videodev.h>
00031 #endif
00032 
00033 #ifdef USING_V4L2
00034 #include <linux/videodev2.h>
00035 #endif
00036 
00037 #ifdef USING_HDHOMERUN
00038 #include "hdhomerun.h"
00039 #endif
00040 
00041 #ifdef USING_ASI
00042 #include <dveo/asi.h>
00043 #include <dveo/master.h>
00044 #endif
00045 
00046 #define LOC      QString("CardUtil: ")
00047 
00048 QString CardUtil::GetScanableCardTypes(void)
00049 {
00050     QString cardTypes = "";
00051 
00052 #ifdef USING_DVB
00053     cardTypes += "'DVB'";
00054 #endif // USING_DVB
00055 
00056 #ifdef USING_V4L2
00057     if (!cardTypes.isEmpty())
00058         cardTypes += ",";
00059     cardTypes += "'V4L'";
00060 # ifdef USING_IVTV
00061     cardTypes += ",'MPEG'";
00062 # endif // USING_IVTV
00063 #endif // USING_V4L2
00064 
00065 #ifdef USING_IPTV
00066     if (!cardTypes.isEmpty())
00067         cardTypes += ",";
00068     cardTypes += "'FREEBOX'";
00069 #endif // USING_IPTV
00070 
00071 #ifdef USING_HDHOMERUN
00072     if (!cardTypes.isEmpty())
00073         cardTypes += ",";
00074     cardTypes += "'HDHOMERUN'";
00075 #endif // USING_HDHOMERUN
00076 
00077 #ifdef USING_ASI
00078     if (!cardTypes.isEmpty())
00079         cardTypes += ",";
00080     cardTypes += "'ASI'";
00081 #endif
00082 
00083 #ifdef USING_CETON
00084     if (!cardTypes.isEmpty())
00085         cardTypes += ",";
00086     cardTypes += "'CETON'";
00087 #endif // USING_CETON
00088 
00089     if (cardTypes.isEmpty())
00090         cardTypes = "'DUMMY'";
00091 
00092     return QString("(%1)").arg(cardTypes);
00093 }
00094 
00095 bool CardUtil::IsCableCardPresent(uint cardid,
00096                                   const QString &cardType)
00097 {
00098     if (cardType == "HDHOMERUN")
00099     {
00100 #ifdef USING_HDHOMERUN
00101         hdhomerun_device_t *hdhr;
00102         hdhomerun_tuner_status_t status;
00103         QString device = GetVideoDevice(cardid);
00104         hdhr = hdhomerun_device_create_from_str(device.toAscii(), NULL);
00105         if (!hdhr)
00106             return false;
00107 
00108         int oob = -1;
00109         oob = hdhomerun_device_get_oob_status(hdhr, NULL, &status);
00110 
00111         // if no OOB tuner, oob will be < 1.  If no CC present, OOB
00112         // status will be "none."
00113         if (oob > 0 && (strncmp(status.channel, "none", 4) != 0))
00114         {
00115             LOG(VB_GENERAL, LOG_INFO, "Cardutil: HDHomeRun Cablecard Present.");
00116             return true;
00117         }
00118         else
00119 #endif
00120             return false;
00121     }
00122     else if (cardType == "CETON")
00123     {
00124 #ifdef USING_CETON
00125         // TODO FIXME implement detection of Cablecard presence
00126         LOG(VB_GENERAL, LOG_INFO, "Cardutil: TODO Ceton Is Cablecard Present?");
00127         return true;
00128 #else
00129         return false;
00130 #endif
00131     }
00132     else
00133         return false;
00134 }
00135 
00136 bool CardUtil::IsTunerShared(uint cardidA, uint cardidB)
00137 {
00138     LOG(VB_GENERAL, LOG_DEBUG, QString("IsTunerShared(%1,%2)")
00139             .arg(cardidA).arg(cardidB));
00140 
00141     MSqlQuery query(MSqlQuery::InitCon());
00142     query.prepare("SELECT videodevice, hostname, cardtype "
00143                   "FROM capturecard "
00144                   "WHERE ( (cardid = :CARDID_A) OR "
00145                   "        (cardid = :CARDID_B) )");
00146     query.bindValue(":CARDID_A", cardidA);
00147     query.bindValue(":CARDID_B", cardidB);
00148 
00149     if (!query.exec())
00150     {
00151         MythDB::DBError("CardUtil::is_tuner_shared", query);
00152         return false;
00153     }
00154 
00155     if (!query.next())
00156         return false;
00157 
00158     const QString vdevice  = query.value(0).toString();
00159     const QString hostname = query.value(1).toString();
00160     const QString cardtype = query.value(2).toString();
00161 
00162     if (!IsTunerSharingCapable(cardtype.toUpper()))
00163         return false;
00164 
00165     if (!query.next())
00166         return false;
00167 
00168     bool ret = ((vdevice  == query.value(0).toString()) &&
00169                 (hostname == query.value(1).toString()) &&
00170                 (cardtype == query.value(2).toString()));
00171 
00172     LOG(VB_RECORD, LOG_DEBUG, QString("IsTunerShared(%1,%2) -> %3")
00173             .arg(cardidA).arg(cardidB).arg(ret));
00174 
00175     return ret;
00176 }
00177 
00183 bool CardUtil::IsCardTypePresent(const QString &rawtype, QString hostname)
00184 {
00185     if (hostname.isEmpty())
00186         hostname = gCoreContext->GetHostName();
00187 
00188     MSqlQuery query(MSqlQuery::InitCon());
00189     QString qstr =
00190         "SELECT count(cardtype) "
00191         "FROM capturecard, cardinput "
00192         "WHERE cardinput.cardid = capturecard.cardid AND "
00193         "      capturecard.hostname = :HOSTNAME";
00194 
00195     if (!rawtype.isEmpty())
00196         qstr += " AND capturecard.cardtype = :CARDTYPE";
00197 
00198     query.prepare(qstr);
00199 
00200     if (!rawtype.isEmpty())
00201         query.bindValue(":CARDTYPE", rawtype.toUpper());
00202 
00203     query.bindValue(":HOSTNAME", hostname);
00204 
00205     if (!query.exec())
00206     {
00207         MythDB::DBError("CardUtil::IsCardTypePresent", query);
00208         return false;
00209     }
00210 
00211     uint count = 0;
00212     if (query.next())
00213         count = query.value(0).toUInt();
00214 
00215     return count > 0;
00216 }
00217 
00218 QStringList CardUtil::GetCardTypes(void)
00219 {
00220     QStringList cardtypes;
00221 
00222     MSqlQuery query(MSqlQuery::InitCon());
00223     query.prepare("SELECT DISTINCT cardtype "
00224                   "FROM capturecard");
00225 
00226     if (!query.exec())
00227     {
00228         MythDB::DBError("CardUtil::GetCardTypes()", query);
00229     }
00230     else
00231     {
00232         while (query.next())
00233             cardtypes.push_back(query.value(0).toString());
00234     }
00235 
00236     return cardtypes;
00237 }
00238 
00244 QStringList CardUtil::GetVideoDevices(const QString &rawtype, QString hostname)
00245 {
00246     QStringList list;
00247 
00248     if (hostname.isEmpty())
00249         hostname = gCoreContext->GetHostName();
00250 
00251     MSqlQuery query(MSqlQuery::InitCon());
00252     QString qstr =
00253         "SELECT videodevice "
00254         "FROM capturecard "
00255         "WHERE hostname = :HOSTNAME";
00256 
00257     if (!rawtype.isEmpty())
00258         qstr += " AND cardtype = :CARDTYPE";
00259 
00260     query.prepare(qstr);
00261 
00262     if (!rawtype.isEmpty())
00263         query.bindValue(":CARDTYPE", rawtype.toUpper());
00264 
00265     query.bindValue(":HOSTNAME", hostname);
00266 
00267     if (!query.exec())
00268     {
00269         MythDB::DBError("CardUtil::GetVideoDevices", query);
00270         return list;
00271     }
00272 
00273     QMap<QString,bool> dup;
00274     while (query.next())
00275     {
00276         QString videodevice = query.value(0).toString();
00277         if (dup[videodevice])
00278             continue;
00279 
00280         list.push_back(videodevice);
00281         dup[videodevice] = true;
00282     }
00283 
00284     return list;
00285 }
00286 
00287 QStringList CardUtil::ProbeVideoDevices(const QString &rawtype)
00288 {
00289     QStringList devs;
00290 
00291     if (rawtype.toUpper() == "DVB")
00292     {
00293         QDir dir("/dev/dvb", "adapter*", QDir::Name, QDir::Dirs);
00294         const QFileInfoList il = dir.entryInfoList();
00295         if (il.isEmpty())
00296             return devs;
00297 
00298         QFileInfoList::const_iterator it = il.begin();
00299 
00300         for (; it != il.end(); ++it)
00301         {
00302             QDir subdir(it->filePath(), "frontend*", QDir::Name, QDir::Files | QDir::System);
00303             const QFileInfoList subil = subdir.entryInfoList();
00304             if (subil.isEmpty())
00305                 continue;
00306 
00307             QFileInfoList::const_iterator subit = subil.begin();
00308             for (; subit != subil.end(); ++subit)
00309                 devs.push_back(subit->filePath());
00310         }
00311     }
00312     else if (rawtype.toUpper() == "ASI")
00313     {
00314         QDir dir("/dev/", "asirx*", QDir::Name, QDir::System);
00315         const QFileInfoList il = dir.entryInfoList();
00316         if (il.isEmpty())
00317             return devs;
00318 
00319         QFileInfoList::const_iterator it = il.begin();
00320         for (; it != il.end(); ++it)
00321         {
00322             if (GetASIDeviceNumber(it->filePath()) >= 0)
00323             {
00324                 devs.push_back(it->filePath());
00325                 continue;
00326             }
00327             break;
00328         }
00329     }
00330 #ifdef USING_HDHOMERUN
00331     else if (rawtype.toUpper() == "HDHOMERUN")
00332     {
00333         uint32_t  target_ip   = 0;
00334         uint32_t  device_type = HDHOMERUN_DEVICE_TYPE_TUNER;
00335         uint32_t  device_id   = HDHOMERUN_DEVICE_ID_WILDCARD;
00336         const int max_count   = 50;
00337         hdhomerun_discover_device_t result_list[max_count];
00338 
00339         int result = hdhomerun_discover_find_devices_custom(
00340             target_ip, device_type, device_id, result_list, max_count);
00341 
00342         if (result == -1)
00343         {
00344             LOG(VB_GENERAL, LOG_ERR, "Error finding HDHomerun devices");
00345             return devs;
00346         }
00347 
00348         if (result >= max_count)
00349         {
00350             LOG(VB_GENERAL, LOG_WARNING,
00351                 "Warning: may be > 50 HDHomerun devices");
00352         }
00353 
00354         // Return "deviceid ipaddress" pairs
00355         for (int i = 0; i < result; i++)
00356         {
00357             QString id = QString("%1").arg(result_list[i].device_id, 0, 16);
00358             QString ip = QString("%1.%2.%3.%4")
00359                                  .arg((result_list[i].ip_addr>>24) & 0xFF)
00360                                  .arg((result_list[i].ip_addr>>16) & 0xFF)
00361                                  .arg((result_list[i].ip_addr>> 8) & 0xFF)
00362                                  .arg((result_list[i].ip_addr>> 0) & 0xFF);
00363 
00364             for (int tuner = 0; tuner < result_list[i].tuner_count; tuner++)
00365             {
00366                 QString hdhrdev = id.toUpper() + " " + ip + " " +
00367                                   QString("%1").arg(tuner);
00368                 devs.push_back(hdhrdev);
00369             }
00370         }
00371     }
00372 #endif // USING_HDHOMERUN
00373 #ifdef USING_CETON
00374     else if (rawtype.toUpper() == "CETON")
00375     {
00376         // TODO implement CETON probing.
00377         LOG(VB_GENERAL, LOG_INFO, "CardUtil::ProbeVideoDevices: "
00378             "TODO Probe Ceton devices");
00379     }
00380 #endif // USING_CETON
00381     else
00382     {
00383         LOG(VB_GENERAL, LOG_ERR, QString("Raw Type: '%1' is not supported")
00384                                      .arg(rawtype));
00385     }
00386 
00387     return devs;
00388 }
00389 
00390 QString CardUtil::ProbeDVBType(const QString &device)
00391 {
00392     QString ret = "ERROR_UNKNOWN";
00393 
00394     if (device.isEmpty())
00395         return ret;
00396 
00397 #ifdef USING_DVB
00398     QString dvbdev = CardUtil::GetDeviceName(DVB_DEV_FRONTEND, device);
00399     QByteArray dev = dvbdev.toAscii();
00400     int fd_frontend = open(dev.constData(), O_RDONLY | O_NONBLOCK);
00401     if (fd_frontend < 0)
00402     {
00403         LOG(VB_GENERAL, LOG_ERR, QString("Can't open DVB frontend (%1) for %2.")
00404                 .arg(dvbdev).arg(device));
00405         return ret;
00406     }
00407 
00408     struct dvb_frontend_info info;
00409     int err = ioctl(fd_frontend, FE_GET_INFO, &info);
00410     if (err < 0)
00411     {
00412         close(fd_frontend);
00413         LOG(VB_GENERAL, LOG_ERR, QString("FE_GET_INFO ioctl failed (%1)")
00414                                          .arg(dvbdev) + ENO);
00415         return ret;
00416     }
00417     close(fd_frontend);
00418 
00419     DTVTunerType type(info.type);
00420 #if HAVE_FE_CAN_2G_MODULATION
00421     if (type == DTVTunerType::kTunerTypeDVBS1 &&
00422         (info.caps & FE_CAN_2G_MODULATION))
00423         type = DTVTunerType::kTunerTypeDVBS2;
00424 #endif // HAVE_FE_CAN_2G_MODULATION
00425     ret = (type.toString() != "UNKNOWN") ? type.toString().toUpper() : ret;
00426 #endif // USING_DVB
00427 
00428     return ret;
00429 }
00430 
00434 QString CardUtil::ProbeDVBFrontendName(const QString &device)
00435 {
00436     QString ret = "ERROR_UNKNOWN";
00437     (void) device;
00438 
00439 #ifdef USING_DVB
00440     QString dvbdev = CardUtil::GetDeviceName(DVB_DEV_FRONTEND, device);
00441     QByteArray dev = dvbdev.toAscii();
00442     int fd_frontend = open(dev.constData(), O_RDWR | O_NONBLOCK);
00443     if (fd_frontend < 0)
00444         return "ERROR_OPEN";
00445 
00446     struct dvb_frontend_info info;
00447     int err = ioctl(fd_frontend, FE_GET_INFO, &info);
00448     if (err < 0)
00449     {
00450         close(fd_frontend);
00451         return "ERROR_PROBE";
00452     }
00453 
00454     ret = info.name;
00455 
00456     close(fd_frontend);
00457 #endif // USING_DVB
00458 
00459     return ret;
00460 }
00461 
00479 bool CardUtil::HasDVBCRCBug(const QString &device)
00480 {
00481     QString name = ProbeDVBFrontendName(device);
00482     return ((name == "VLSI VES1x93 DVB-S")      || // munges PMT
00483             (name == "ST STV0299 DVB-S"));         // munges PAT
00484 }
00485 
00486 uint CardUtil::GetMinSignalMonitoringDelay(const QString &device)
00487 {
00488     QString name = ProbeDVBFrontendName(device);
00489     if (name.indexOf("DVB-S") >= 0)
00490         return 300;
00491     if (name == "DiBcom 3000P/M-C DVB-T")
00492         return 100;
00493     return 25;
00494 }
00495 
00496 QString CardUtil::ProbeSubTypeName(uint cardid)
00497 {
00498     QString type = GetRawCardType(cardid);
00499     if ("DVB" != type)
00500         return type;
00501 
00502     QString device = GetVideoDevice(cardid);
00503 
00504     if (device.isEmpty())
00505         return "ERROR_OPEN";
00506 
00507     return ProbeDVBType(device);
00508 }
00509 
00511 bool CardUtil::IsDVBCardType(const QString &card_type)
00512 {
00513     QString ct = card_type.toUpper();
00514     return (ct == "DVB") || (ct == "QAM") || (ct == "QPSK") ||
00515         (ct == "OFDM") || (ct == "ATSC") || (ct == "DVB_S2");
00516 }
00517 
00518 QString get_on_cardid(const QString &to_get, uint cardid)
00519 {
00520     MSqlQuery query(MSqlQuery::InitCon());
00521     query.prepare(
00522         QString("SELECT %1 ").arg(to_get) +
00523         "FROM capturecard "
00524         "WHERE capturecard.cardid = :CARDID");
00525     query.bindValue(":CARDID", cardid);
00526 
00527     if (!query.exec())
00528         MythDB::DBError("CardUtil::get_on_source", query);
00529     else if (query.next())
00530         return query.value(0).toString();
00531 
00532     return QString::null;
00533 }
00534 
00535 bool set_on_source(const QString &to_set, uint cardid, uint sourceid,
00536                    const QString value)
00537 {
00538     QString tmp = get_on_cardid("capturecard.cardid", cardid);
00539     if (tmp.isEmpty())
00540         return false;
00541 
00542     bool ok;
00543     uint input_cardid = tmp.toUInt(&ok);
00544     if (!ok)
00545         return false;
00546 
00547     MSqlQuery query(MSqlQuery::InitCon());
00548     query.prepare(
00549         QString("UPDATE capturecard SET %1 = :VALUE ").arg(to_set) +
00550         "WHERE cardid = :CARDID");
00551     query.bindValue(":CARDID", input_cardid);
00552     query.bindValue(":VALUE",  value);
00553 
00554     if (query.exec())
00555         return true;
00556 
00557     MythDB::DBError("CardUtil::set_on_source", query);
00558     return false;
00559 }
00560 
00561 QString get_on_inputid(const QString &to_get, uint inputid)
00562 {
00563     MSqlQuery query(MSqlQuery::InitCon());
00564     query.prepare(
00565         QString("SELECT %1 ").arg(to_get) +
00566         "FROM cardinput "
00567         "WHERE cardinput.cardinputid = :INPUTID");
00568     query.bindValue(":INPUTID", inputid);
00569 
00570     if (!query.exec())
00571         MythDB::DBError("CardUtil::get_on_inputid", query);
00572     else if (query.next())
00573         return query.value(0).toString();
00574 
00575     return QString::null;
00576 }
00577 
00578 bool set_on_input(const QString &to_set, uint inputid, const QString value)
00579 {
00580     QString tmp = get_on_inputid("cardinput.cardinputid", inputid);
00581     if (tmp.isEmpty())
00582         return false;
00583 
00584     bool ok;
00585     uint input_cardinputid = tmp.toUInt(&ok);
00586     if (!ok)
00587         return false;
00588 
00589     MSqlQuery query(MSqlQuery::InitCon());
00590     query.prepare(
00591         QString("UPDATE cardinput SET %1 = :VALUE ").arg(to_set) +
00592         "WHERE cardinputid = :INPUTID");
00593     query.bindValue(":INPUTID", input_cardinputid);
00594     query.bindValue(":VALUE",  value);
00595 
00596     if (query.exec())
00597         return true;
00598 
00599     MythDB::DBError("CardUtil::set_on_input", query);
00600     return false;
00601 }
00602 
00612 vector<uint> CardUtil::GetCardIDs(QString videodevice,
00613                                   QString rawtype,
00614                                   QString hostname)
00615 {
00616     vector<uint> list;
00617 
00618     if (hostname.isEmpty())
00619         hostname = gCoreContext->GetHostName();
00620 
00621     MSqlQuery query(MSqlQuery::InitCon());
00622     QString qstr =
00623         (videodevice.isEmpty()) ?
00624         "SELECT cardid "
00625         "FROM capturecard "
00626         "WHERE hostname    = :HOSTNAME" :
00627 
00628         "SELECT cardid "
00629         "FROM capturecard "
00630         "WHERE videodevice = :DEVICE AND "
00631         "      hostname    = :HOSTNAME";
00632 
00633     if (!rawtype.isEmpty())
00634         qstr += " AND cardtype = :CARDTYPE";
00635 
00636     qstr += " ORDER BY cardid";
00637 
00638     query.prepare(qstr);
00639 
00640     if (!videodevice.isEmpty())
00641         query.bindValue(":DEVICE",   videodevice);
00642 
00643     query.bindValue(":HOSTNAME", hostname);
00644 
00645     if (!rawtype.isEmpty())
00646         query.bindValue(":CARDTYPE", rawtype.toUpper());
00647 
00648     if (!query.exec())
00649         MythDB::DBError("CardUtil::GetCardIDs(videodevice...)", query);
00650     else
00651     {
00652         while (query.next())
00653             list.push_back(query.value(0).toUInt());
00654     }
00655 
00656     return list;
00657 }
00658 
00659 static uint clone_capturecard(uint src_cardid, uint orig_dst_cardid)
00660 {
00661     uint dst_cardid = orig_dst_cardid;
00662 
00663     MSqlQuery query(MSqlQuery::InitCon());
00664     if (!dst_cardid)
00665     {
00666         query.prepare(
00667             "DELETE FROM capturecard "
00668             "WHERE videodevice = 'temp_dummy'");
00669 
00670         if (!query.exec())
00671         {
00672             MythDB::DBError("clone_capturecard -- delete temp", query);
00673             return 0;
00674         }
00675 
00676         query.prepare(
00677             "INSERT INTO capturecard "
00678             "SET videodevice = 'temp_dummy'");
00679 
00680         if (!query.exec())
00681         {
00682             MythDB::DBError("clone_capturecard -- insert temp", query);
00683             return 0;
00684         }
00685 
00686         query.prepare(
00687             "SELECT cardid "
00688             "FROM capturecard "
00689             "WHERE videodevice = 'temp_dummy'");
00690 
00691         if (!query.exec())
00692         {
00693             MythDB::DBError("clone_capturecard -- get temp id", query);
00694             return 0;
00695         }
00696 
00697         if (!query.next())
00698         {
00699             LOG(VB_GENERAL, LOG_ERR, "clone_capturecard -- get temp id");
00700             return 0;
00701         }
00702 
00703         dst_cardid = query.value(0).toUInt();
00704     }
00705 
00706     query.prepare(
00707         "SELECT videodevice,           cardtype,                         "
00708         "       hostname,              signal_timeout, channel_timeout,  "
00709         "       dvb_wait_for_seqstart, dvb_on_demand,  dvb_tuning_delay, "
00710         "       dvb_diseqc_type,       diseqcid,       dvb_eitscan "
00711         "FROM capturecard "
00712         "WHERE cardid = :CARDID");
00713     query.bindValue(":CARDID", src_cardid);
00714 
00715     if (!query.exec())
00716     {
00717         MythDB::DBError("clone_capturecard -- get data", query);
00718         return 0;
00719     }
00720     if (!query.next())
00721     {
00722         LOG(VB_GENERAL, LOG_ERR, "clone_cardinput -- get data 2");
00723         return 0;
00724     }
00725 
00726     MSqlQuery query2(MSqlQuery::InitCon());
00727     query2.prepare(
00728         "UPDATE capturecard "
00729         "SET videodevice           = :V0, "
00730         "    cardtype              = :V1, "
00731         "    hostname              = :V2, "
00732         "    signal_timeout        = :V3, "
00733         "    channel_timeout       = :V4, "
00734         "    dvb_wait_for_seqstart = :V5, "
00735         "    dvb_on_demand         = :V6, "
00736         "    dvb_tuning_delay      = :V7, "
00737         "    dvb_diseqc_type       = :V8, "
00738         "    diseqcid              = :V9,"
00739         "    dvb_eitscan           = :V10 "
00740         "WHERE cardid = :CARDID");
00741     for (uint i = 0; i < 11; i++)
00742         query2.bindValue(QString(":V%1").arg(i), query.value(i).toString());
00743     query2.bindValue(":CARDID", dst_cardid);
00744 
00745     if (!query2.exec())
00746     {
00747         MythDB::DBError("clone_capturecard -- save data", query2);
00748         if (!orig_dst_cardid)
00749             CardUtil::DeleteCard(dst_cardid);
00750         return 0;
00751     }
00752 
00753     return dst_cardid;
00754 }
00755 
00756 static bool clone_cardinputs(uint src_cardid, uint dst_cardid)
00757 {
00758     vector<uint> src_inputs = CardUtil::GetInputIDs(src_cardid);
00759     vector<uint> dst_inputs = CardUtil::GetInputIDs(dst_cardid);
00760     vector<QString> src_names;
00761     vector<QString> dst_names;
00762     QMap<uint,bool> dst_keep;
00763 
00764     for (uint i = 0; i < src_inputs.size(); i++)
00765         src_names.push_back(CardUtil::GetInputName(src_inputs[i]));
00766 
00767     for (uint i = 0; i < dst_inputs.size(); i++)
00768         dst_names.push_back(CardUtil::GetInputName(dst_inputs[i]));
00769 
00770     bool ok = true;
00771 
00772     MSqlQuery query(MSqlQuery::InitCon());
00773     MSqlQuery query2(MSqlQuery::InitCon());
00774 
00775     for (uint i = 0; i < src_inputs.size(); i++)
00776     {
00777         query.prepare(
00778             "SELECT sourceid,        inputname,       externalcommand, "
00779             "       tunechan,        startchan,       displayname,     "
00780             "       dishnet_eit,     recpriority,     quicktune,       "
00781             "       schedorder,      livetvorder                       "
00782             "FROM cardinput "
00783             "WHERE cardinputid = :INPUTID");
00784         query.bindValue(":INPUTID", src_inputs[i]);
00785         if (!query.exec())
00786         {
00787             MythDB::DBError("clone_cardinput -- get data", query);
00788             ok = false;
00789             break;
00790         }
00791         if (!query.next())
00792         {
00793             LOG(VB_GENERAL, LOG_ERR, "clone_cardinput -- get data 2");
00794             ok = false;
00795             break;
00796         }
00797 
00798         int match = -1;
00799         for (uint j = 0; j < dst_inputs.size(); j++)
00800         {
00801             if (src_names[i] == dst_names[j])
00802             {
00803                 match = (int) j;
00804                 break;
00805             }
00806         }
00807 
00808         uint dst_inputid = 0;
00809         if (match >= 0)
00810         {
00811             dst_keep[match] = true;
00812 
00813             // copy data from src[i] to dst[match]
00814             query2.prepare(
00815                 "UPDATE cardinput "
00816                 "SET sourceid        = :V0, "
00817                 "    inputname       = :V1, "
00818                 "    externalcommand = :V2, "
00819                 "    tunechan        = :V3, "
00820                 "    startchan       = :V4, "
00821                 "    displayname     = :V5, "
00822                 "    dishnet_eit     = :V6, "
00823                 "    recpriority     = :V7, "
00824                 "    quicktune       = :V8, "
00825                 "    schedorder      = :V9, "
00826                 "    livetvorder     = :V10 "
00827                 "WHERE cardinputid = :INPUTID");
00828 
00829             for (uint j = 0; j < 11; j++)
00830             {
00831                 query2.bindValue(QString(":V%1").arg(j),
00832                                  query.value(j).toString());
00833             }
00834             query2.bindValue(":INPUTID", dst_inputs[match]);
00835 
00836             if (!query2.exec())
00837             {
00838                 MythDB::DBError("clone_cardinput -- update data", query2);
00839                 ok = false;
00840                 break;
00841             }
00842 
00843             dst_inputid = dst_inputs[match];
00844         }
00845         else
00846         {
00847             // create new input for dst with data from src
00848 
00849             query2.prepare(
00850                 "INSERT cardinput "
00851                 "SET cardid          = :CARDID, "
00852                 "    sourceid        = :V0, "
00853                 "    inputname       = :V1, "
00854                 "    externalcommand = :V2, "
00855                 "    tunechan        = :V3, "
00856                 "    startchan       = :V4, "
00857                 "    displayname     = :V5, "
00858                 "    dishnet_eit     = :V6, "
00859                 "    recpriority     = :V7, "
00860                 "    quicktune       = :V8, "
00861                 "    schedorder      = :V9, "
00862                 "    livetvorder     = :V10 ");
00863 
00864             query2.bindValue(":CARDID", dst_cardid);
00865             for (uint j = 0; j < 11; j++)
00866             {
00867                 query2.bindValue(QString(":V%1").arg(j),
00868                                  query.value(j).toString());
00869             }
00870 
00871             if (!query2.exec())
00872             {
00873                 MythDB::DBError("clone_cardinput -- insert data", query2);
00874                 ok = false;
00875                 break;
00876             }
00877 
00878             query2.prepare(
00879                 "SELECT cardinputid "
00880                 "FROM cardinput "
00881                 "WHERE cardid    = :CARDID AND "
00882                 "      inputname = :NAME");
00883             query2.bindValue(":CARDID", dst_cardid);
00884             query2.bindValue(":NAME", query.value(1).toString());
00885             if (!query2.exec())
00886             {
00887                 MythDB::DBError("clone_cardinput -- "
00888                                      "insert, query inputid", query2);
00889                 ok = false;
00890                 break;
00891             }
00892             if (!query2.next())
00893             {
00894                 LOG(VB_GENERAL, LOG_ERR, "clone_cardinput -- insert failed");
00895                 ok = false;
00896                 break;
00897             }
00898 
00899             dst_inputid = query2.value(0).toUInt();
00900         }
00901 
00902         // copy input group linkages
00903         vector<uint> src_grps = CardUtil::GetInputGroups(src_inputs[i]);
00904         vector<uint> dst_grps = CardUtil::GetInputGroups(dst_inputid);
00905         for (uint j = 0; j < dst_grps.size(); j++)
00906             CardUtil::UnlinkInputGroup(dst_inputid, dst_grps[j]);
00907         for (uint j = 0; j < src_grps.size(); j++)
00908             CardUtil::LinkInputGroup(dst_inputid, src_grps[j]);
00909 
00910         // clone diseqc_config (just points to the same diseqc_tree row)
00911         DiSEqCDevSettings diseqc;
00912         if (diseqc.Load(src_inputs[i]))
00913             diseqc.Store(dst_inputid);
00914     }
00915 
00916     // delete extra inputs in dst
00917     for (uint i = 0; i < dst_inputs.size(); i++)
00918     {
00919         if (!dst_keep[i])
00920             ok &= CardUtil::DeleteInput(dst_inputs[i]);
00921     }
00922 
00923     return ok;
00924 }
00925 
00926 bool CardUtil::CloneCard(uint src_cardid, uint orig_dst_cardid)
00927 {
00928     QString type = CardUtil::GetRawCardType(src_cardid);
00929     if (!IsTunerSharingCapable(type))
00930         return false;
00931 
00932     uint dst_cardid = clone_capturecard(src_cardid, orig_dst_cardid);
00933     if (!dst_cardid)
00934         return false;
00935 
00936     if (!clone_cardinputs(src_cardid, dst_cardid) && !orig_dst_cardid)
00937     {
00938         DeleteCard(dst_cardid);
00939         return false;
00940     }
00941 
00942     return true;
00943 }
00944 
00945 vector<uint> CardUtil::GetCloneCardIDs(uint cardid)
00946 {
00947     vector<uint> list;
00948     MSqlQuery query(MSqlQuery::InitCon());
00949     query.prepare(
00950         "SELECT cardtype, videodevice, hostname "
00951         "FROM capturecard "
00952         "WHERE cardid = :CARDID");
00953     query.bindValue(":CARDID",   cardid);
00954 
00955     if (!query.exec())
00956     {
00957         MythDB::DBError("CardUtil::GetCloneCardIDs() 1", query);
00958         return list;
00959     }
00960 
00961     if (!query.next())
00962         return list;
00963 
00964     QString rawtype     = query.value(0).toString();
00965     QString videodevice = query.value(1).toString();
00966     QString hostname    = query.value(2).toString();
00967 
00968     if (!IsTunerSharingCapable(rawtype))
00969         return list;
00970 
00971     query.prepare(
00972         "SELECT cardid "
00973         "FROM capturecard "
00974         "WHERE cardid      != :CARDID AND "
00975         "      videodevice  = :DEVICE AND "
00976         "      cardtype     = :TYPE   AND "
00977         "      hostname     = :HOSTNAME");
00978     query.bindValue(":CARDID",   cardid);
00979     query.bindValue(":DEVICE",   videodevice);
00980     query.bindValue(":TYPE",     rawtype);
00981     query.bindValue(":HOSTNAME", hostname);
00982 
00983     if (!query.exec())
00984     {
00985         MythDB::DBError("CardUtil::GetCloneCardIDs() 2", query);
00986         return list;
00987     }
00988 
00989     while (query.next())
00990         list.push_back(query.value(0).toUInt());
00991 
00992     return list;
00993 }
00994 
00995 QString CardUtil::GetFirewireChangerNode(uint inputid)
00996 {
00997     QString fwnode;
00998 
00999     MSqlQuery query(MSqlQuery::InitCon());
01000     query.prepare("SELECT changer_device "
01001                   "FROM cardinput WHERE cardinputid = :INPUTID ");
01002     query.bindValue(":CARDID", inputid);
01003 
01004     if (query.exec() && query.next())
01005     {
01006         fwnode = query.value(0).toString();
01007     }
01008 
01009     return fwnode;
01010 }
01011 
01012 QString CardUtil::GetFirewireChangerModel(uint inputid)
01013 {
01014     QString fwnode;
01015 
01016     MSqlQuery query(MSqlQuery::InitCon());
01017     query.prepare("SELECT changer_model "
01018                   "FROM cardinput WHERE cardinputid = :INPUTID ");
01019     query.bindValue(":CARDID", inputid);
01020 
01021     if (query.exec() && query.next())
01022     {
01023         fwnode = query.value(0).toString();
01024     }
01025 
01026     return fwnode;
01027 }
01028 
01029 vector<uint> CardUtil::GetCardIDs(uint sourceid)
01030 {
01031     MSqlQuery query(MSqlQuery::InitCon());
01032 
01033     query.prepare(
01034         "SELECT DISTINCT cardid "
01035         "FROM cardinput "
01036         "WHERE sourceid = :SOURCEID");
01037     query.bindValue(":SOURCEID", sourceid);
01038 
01039     vector<uint> list;
01040 
01041     if (!query.exec())
01042     {
01043         MythDB::DBError("CardUtil::GetCardIDs()", query);
01044         return list;
01045     }
01046 
01047     while (query.next())
01048         list.push_back(query.value(0).toUInt());
01049 
01050     return list;
01051 }
01052 
01053 int CardUtil::GetCardInputID(
01054     uint cardid, const QString &channum, QString &inputname)
01055 {
01056     MSqlQuery query(MSqlQuery::InitCon());
01057     query.prepare(
01058         "SELECT cardinputid, inputname "
01059         "FROM channel, capturecard, cardinput "
01060         "WHERE channel.channum      = :CHANNUM           AND "
01061         "      channel.sourceid     = cardinput.sourceid AND "
01062         "      cardinput.cardid     = capturecard.cardid AND "
01063         "      capturecard.cardid   = :CARDID");
01064     query.bindValue(":CHANNUM", channum);
01065     query.bindValue(":CARDID", cardid);
01066 
01067     if (!query.exec() || !query.isActive())
01068         MythDB::DBError("get_cardinputid", query);
01069     else if (query.next())
01070     {
01071         inputname = query.value(1).toString();
01072         return query.value(0).toInt();
01073     }
01074 
01075     return -1;
01076 }
01077 
01078 bool CardUtil::SetStartChannel(uint cardinputid, const QString &channum)
01079 {
01080     MSqlQuery query(MSqlQuery::InitCon());
01081     query.prepare("UPDATE cardinput "
01082                   "SET startchan = :CHANNUM "
01083                   "WHERE cardinputid = :INPUTID");
01084     query.bindValue(":CHANNUM", channum);
01085     query.bindValue(":INPUTID", cardinputid);
01086 
01087     if (!query.exec())
01088     {
01089         MythDB::DBError("set_startchan", query);
01090         return false;
01091     }
01092 
01093     return true;
01094 }
01095 
01101 QString CardUtil::GetStartInput(uint nCardID)
01102 {
01103     QString str = QString::null;
01104     MSqlQuery query(MSqlQuery::InitCon());
01105     query.prepare("SELECT inputname "
01106                   "FROM cardinput "
01107                   "WHERE cardinput.cardid = :CARDID " 
01108                   "ORDER BY livetvorder = 0, livetvorder, cardinputid "
01109                   "LIMIT 1");
01110     query.bindValue(":CARDID", nCardID);
01111 
01112     if (!query.exec() || !query.isActive())
01113         MythDB::DBError("CardUtil::GetStartInput()", query);
01114     else if (query.next())
01115         str = query.value(0).toString();
01116 
01117     return str;
01118 }
01119 
01120 QStringList CardUtil::GetInputNames(uint cardid, uint sourceid)
01121 {
01122     QStringList list;
01123     MSqlQuery query(MSqlQuery::InitCon());
01124 
01125     if (sourceid)
01126     {
01127         query.prepare("SELECT inputname "
01128                       "FROM cardinput "
01129                       "WHERE sourceid = :SOURCEID AND "
01130                       "      cardid   = :CARDID");
01131         query.bindValue(":SOURCEID", sourceid);
01132     }
01133     else
01134     {
01135         query.prepare("SELECT inputname "
01136                       "FROM cardinput "
01137                       "WHERE cardid   = :CARDID");
01138     }
01139     query.bindValue(":CARDID",   cardid);
01140 
01141     if (!query.exec())
01142     {
01143         MythDB::DBError("CardUtil::GetInputNames()", query);
01144     }
01145     else
01146     {
01147         while (query.next())
01148             list.append( query.value(0).toString() );
01149     }
01150 
01151     return list;
01152 }
01153 
01154 bool CardUtil::GetInputInfo(InputInfo &input, vector<uint> *groupids)
01155 {
01156     if (!input.inputid)
01157         return false;
01158 
01159     MSqlQuery query(MSqlQuery::InitCon());
01160     query.prepare("SELECT inputname, sourceid, cardid, livetvorder "
01161                   "FROM cardinput "
01162                   "WHERE cardinputid = :INPUTID");
01163     query.bindValue(":INPUTID", input.inputid);
01164 
01165     if (!query.exec())
01166     {
01167         MythDB::DBError("CardUtil::GetInputInfo()", query);
01168         return false;
01169     }
01170 
01171     if (!query.next())
01172         return false;
01173 
01174     input.name     = query.value(0).toString();
01175     input.sourceid = query.value(1).toUInt();
01176     input.cardid   = query.value(2).toUInt();
01177     input.livetvorder = query.value(3).toUInt();
01178 
01179     if (groupids)
01180         *groupids = GetInputGroups(input.inputid);
01181 
01182     return true;
01183 }
01184 
01185 uint CardUtil::GetCardID(uint inputid)
01186 {
01187     InputInfo info(QString::null, 0, inputid, 0, 0, 0);
01188     GetInputInfo(info);
01189     return info.cardid;
01190 }
01191 
01192 QString CardUtil::GetInputName(uint inputid)
01193 {
01194     InputInfo info(QString::null, 0, inputid, 0, 0, 0);
01195     GetInputInfo(info);
01196     return info.name;
01197 }
01198 
01199 QString CardUtil::GetStartingChannel(uint inputid)
01200 {
01201     MSqlQuery query(MSqlQuery::InitCon());
01202     query.prepare("SELECT startchan "
01203                   "FROM cardinput "
01204                   "WHERE cardinputid = :INPUTID");
01205     query.bindValue(":INPUTID", inputid);
01206 
01207     if (!query.exec())
01208         MythDB::DBError("CardUtil::GetStartingChannel(uint)", query);
01209     else if (query.next())
01210         return query.value(0).toString();
01211 
01212     return QString::null;
01213 }
01214 
01215 QString CardUtil::GetDisplayName(uint inputid)
01216 {
01217     if (!inputid)
01218         return QString::null;
01219 
01220     MSqlQuery query(MSqlQuery::InitCon());
01221     query.prepare("SELECT displayname, cardid, inputname "
01222                   "FROM cardinput "
01223                   "WHERE cardinputid = :INPUTID");
01224     query.bindValue(":INPUTID", inputid);
01225 
01226     if (!query.exec())
01227         MythDB::DBError("CardUtil::GetDisplayName(uint)", query);
01228     else if (query.next())
01229     {
01230         QString result = query.value(0).toString();
01231         if (result.isEmpty())
01232             result = QString("%1: %2").arg(query.value(1).toInt())
01233                                       .arg(query.value(2).toString());
01234         return result;
01235     }
01236 
01237     return QString::null;
01238 }
01239 
01240 uint CardUtil::GetInputID(uint cardid, const QString &inputname)
01241 {
01242     MSqlQuery query(MSqlQuery::InitCon());
01243     query.prepare("SELECT cardinputid "
01244                   "FROM cardinput "
01245                   "WHERE inputname = :INPUTNAME AND "
01246                   "      cardid    = :CARDID");
01247     query.bindValue(":INPUTNAME", inputname);
01248     query.bindValue(":CARDID",    cardid);
01249 
01250     if (!query.exec())
01251         MythDB::DBError("CardUtil::GetInputID(uint,QString)", query);
01252     else if (query.next())
01253         return query.value(0).toUInt();
01254 
01255     return 0;
01256 }
01257 
01258 uint CardUtil::GetInputID(uint cardid, uint sourceid)
01259 {
01260     MSqlQuery query(MSqlQuery::InitCon());
01261     query.prepare("SELECT cardinputid "
01262                   "FROM cardinput "
01263                   "WHERE sourceid  = :SOURCEID AND "
01264                   "      cardid    = :CARDID");
01265     query.bindValue(":SOURCEID", sourceid);
01266     query.bindValue(":CARDID",    cardid);
01267 
01268     if (!query.exec())
01269         MythDB::DBError("CardUtil::GetInputID(uint,uint)", query);
01270     else if (query.next())
01271         return query.value(0).toUInt();
01272 
01273     return 0;
01274 }
01275 
01276 uint CardUtil::GetSourceID(uint inputid)
01277 {
01278     MSqlQuery query(MSqlQuery::InitCon());
01279     query.prepare(
01280         "SELECT sourceid "
01281         "FROM cardinput "
01282         "WHERE cardinputid = :INPUTID");
01283     query.bindValue(":INPUTID", inputid);
01284     if (!query.exec() || !query.isActive())
01285         MythDB::DBError("CardUtil::GetSourceID()", query);
01286     else if (query.next())
01287         return query.value(0).toUInt();
01288 
01289     return 0;
01290 }
01291 
01292 vector<uint> CardUtil::GetAllInputIDs(void)
01293 {
01294     vector<uint> list;
01295 
01296     MSqlQuery query(MSqlQuery::InitCon());
01297     query.prepare(
01298         "SELECT cardinputid "
01299         "FROM cardinput");
01300 
01301     if (!query.exec())
01302     {
01303         MythDB::DBError("CardUtil::GetAllInputIDs(uint)", query);
01304         return list;
01305     }
01306 
01307     while (query.next())
01308         list.push_back(query.value(0).toUInt());
01309 
01310     return list;
01311 }
01312 
01313 vector<uint> CardUtil::GetInputIDs(uint cardid)
01314 {
01315     vector<uint> list;
01316 
01317     MSqlQuery query(MSqlQuery::InitCon());
01318     query.prepare(
01319         "SELECT cardinputid "
01320         "FROM cardinput "
01321         "WHERE cardid = :CARDID");
01322 
01323     query.bindValue(":CARDID", cardid);
01324 
01325     if (!query.exec())
01326     {
01327         MythDB::DBError("CardUtil::GetInputIDs(uint)", query);
01328         return list;
01329     }
01330 
01331     while (query.next())
01332         list.push_back(query.value(0).toUInt());
01333 
01334     return list;
01335 }
01336 
01337 int CardUtil::CreateCardInput(const uint cardid,
01338                               const uint sourceid,
01339                               const QString &inputname,
01340                               const QString &externalcommand,
01341                               const QString &changer_device,
01342                               const QString &changer_model,
01343                               const QString &hostname,
01344                               const QString &tunechan,
01345                               const QString &startchan,
01346                               const QString &displayname,
01347                               bool          dishnet_eit,
01348                               const uint recpriority,
01349                               const uint quicktune,
01350                               const uint schedorder,
01351                               const uint livetvorder)
01352 {
01353     MSqlQuery query(MSqlQuery::InitCon());
01354 
01355     query.prepare(
01356         "INSERT INTO cardinput "
01357         "(cardid, sourceid, inputname, externalcommand, changer_device, "
01358         "changer_model, tunechan, startchan, displayname, dishnet_eit, "
01359         "recpriority, quicktune, schedorder, livetvorder) "
01360         "VALUES (:CARDID, :SOURCEID, :INPUTNAME, :EXTERNALCOMMAND, "
01361         ":CHANGERDEVICE, :CHANGERMODEL, :TUNECHAN, :STARTCHAN, :DISPLAYNAME, "
01362         ":DISHNETEIT, :RECPRIORITY, :QUICKTUNE, :SCHEDORDER, :LIVETVORDER ) ");
01363 
01364     query.bindValue(":CARDID", cardid);
01365     query.bindValue(":SOURCEID", sourceid);
01366     query.bindValue(":INPUTNAME", inputname);
01367     query.bindValue(":EXTERNALCOMMAND", externalcommand);
01368     query.bindValue(":CHANGERDEVICE", changer_device);
01369     query.bindValue(":CHANGERMODEL", changer_model);
01370     query.bindValue(":TUNECHAN", tunechan);
01371     query.bindValue(":STARTCHAN", startchan);
01372     query.bindValue(":DISPLAYNAME", displayname.isNull() ? "" : displayname);
01373     query.bindValue(":DISHNETEIT", dishnet_eit);
01374     query.bindValue(":RECPRIORITY", recpriority);
01375     query.bindValue(":QUICKTUNE", quicktune);
01376     query.bindValue(":SCHEDORDER", schedorder);
01377     query.bindValue(":LIVETVORDER", livetvorder);
01378 
01379     if (!query.exec())
01380     {
01381         MythDB::DBError("CreateCardInput", query);
01382         return -1;
01383     }
01384 
01385     query.prepare("SELECT MAX(cardinputid) FROM cardinput");
01386 
01387     if (!query.exec())
01388     {
01389         MythDB::DBError("CreateCardInput maxinput", query);
01390         return -1;
01391     }
01392 
01393     uint inputid = -1;
01394 
01395     if (query.next())
01396         inputid = query.value(0).toUInt();
01397 
01398     return inputid;
01399 }
01400 
01401 bool CardUtil::DeleteInput(uint inputid)
01402 {
01403     MSqlQuery query(MSqlQuery::InitCon());
01404     query.prepare(
01405         "DELETE FROM cardinput "
01406         "WHERE cardinputid = :INPUTID");
01407     query.bindValue(":INPUTID", inputid);
01408 
01409     if (!query.exec())
01410     {
01411         MythDB::DBError("DeleteInput", query);
01412         return false;
01413     }
01414 
01415     return true;
01416 }
01417 
01418 bool CardUtil::DeleteOrphanInputs(void)
01419 {
01420     MSqlQuery query(MSqlQuery::InitCon());
01421     query.prepare("SELECT cardinputid "
01422                   "FROM cardinput "
01423                   "LEFT JOIN capturecard "
01424                   "ON (capturecard.cardid = cardinput.cardid) "
01425                   "WHERE capturecard.cardid IS NULL");
01426     if (!query.exec())
01427     {
01428         MythDB::DBError("DeleteOrphanInputs -- query disconnects", query);
01429         return false;
01430     }
01431 
01432     bool ok = true;
01433     while (query.next())
01434     {
01435         uint inputid = query.value(0).toUInt();
01436         if (DeleteInput(inputid))
01437         {
01438             LOG(VB_GENERAL, LOG_NOTICE, QString("Removed orphan input %1")
01439                      .arg(inputid));
01440         }
01441         else
01442         {
01443             ok = false;
01444             LOG(VB_GENERAL, LOG_ERR, QString("Failed to remove orphan input %1")
01445                      .arg(inputid));
01446         }
01447     }
01448 
01449     return ok;
01450 }
01451 
01452 uint CardUtil::CreateInputGroup(const QString &name)
01453 {
01454     MSqlQuery query(MSqlQuery::InitCon());
01455     query.prepare("SELECT MAX(inputgroupid) FROM inputgroup");
01456     if (!query.exec())
01457     {
01458         MythDB::DBError("CreateNewInputGroup 1", query);
01459         return 0;
01460     }
01461     uint inputgroupid = (query.next()) ? query.value(0).toUInt() + 1 : 1;
01462 
01463     query.prepare(
01464         "INSERT INTO inputgroup "
01465         "       (cardinputid, inputgroupid, inputgroupname) "
01466         "VALUES (:INPUTID,    :GROUPID,     :GROUPNAME    ) ");
01467 
01468     query.bindValue(":INPUTID",   0);
01469     query.bindValue(":GROUPID",   inputgroupid);
01470     query.bindValue(":GROUPNAME", name);
01471 
01472     if (!query.exec())
01473     {
01474         MythDB::DBError("CreateNewInputGroup 2", query);
01475         return 0;
01476     }
01477 
01478     return inputgroupid;
01479 }
01480 
01481 bool CardUtil::CreateInputGroupIfNeeded(uint cardid)
01482 {
01483     // Make sure the card's inputs are all in a single
01484     // input group, create one if needed.
01485     vector<uint> ingrps = CardUtil::GetSharedInputGroups(cardid);
01486     vector<uint> inputs = CardUtil::GetInputIDs(cardid);
01487 
01488     if (ingrps.empty() && !inputs.empty())
01489     {
01490         QString name = CardUtil::GetRawCardType(cardid) + "_" +
01491             CardUtil::GetVideoDevice(cardid);
01492         uint id = 0;
01493         for (uint i = 0; !id && (i < 100); i++)
01494         {
01495             if (i)
01496                 name += QString(":%1").arg(i);
01497             id = CardUtil::CreateInputGroup(name);
01498         }
01499         if (!id)
01500         {
01501             LOG(VB_GENERAL, LOG_ERR, "Failed to create input group");
01502             return false;
01503         }
01504 
01505         bool ok = true;
01506         for (uint i = 0; i < inputs.size(); i++)
01507             ok &= CardUtil::LinkInputGroup(inputs[i], id);
01508 
01509         if (!ok)
01510             LOG(VB_GENERAL, LOG_ERR, "Failed to link to new input group");
01511 
01512         return ok;
01513     }
01514 
01515     return true;
01516 }
01517 
01518 bool CardUtil::LinkInputGroup(uint inputid, uint inputgroupid)
01519 {
01520     MSqlQuery query(MSqlQuery::InitCon());
01521 
01522     query.prepare(
01523         "SELECT cardinputid, inputgroupid, inputgroupname "
01524         "FROM inputgroup "
01525         "WHERE inputgroupid = :GROUPID "
01526         "ORDER BY inputgroupid, cardinputid, inputgroupname");
01527     query.bindValue(":GROUPID", inputgroupid);
01528 
01529     if (!query.exec())
01530     {
01531         MythDB::DBError("CardUtil::CreateInputGroup() 1", query);
01532         return false;
01533     }
01534 
01535     if (!query.next())
01536         return false;
01537 
01538     const QString name = query.value(2).toString();
01539 
01540     query.prepare(
01541         "INSERT INTO inputgroup "
01542         "       (cardinputid, inputgroupid, inputgroupname) "
01543         "VALUES (:INPUTID,    :GROUPID,     :GROUPNAME    ) ");
01544 
01545     query.bindValue(":INPUTID",   inputid);
01546     query.bindValue(":GROUPID",   inputgroupid);
01547     query.bindValue(":GROUPNAME", name);
01548 
01549     if (!query.exec())
01550     {
01551         MythDB::DBError("CardUtil::CreateInputGroup() 2", query);
01552         return false;
01553     }
01554 
01555     return true;
01556 }
01557 
01558 bool CardUtil::UnlinkInputGroup(uint inputid, uint inputgroupid)
01559 {
01560     MSqlQuery query(MSqlQuery::InitCon());
01561 
01562     if (!inputid && !inputgroupid)
01563     {
01564         query.prepare(
01565             "DELETE FROM inputgroup "
01566             "WHERE cardinputid = 0 ");
01567     }
01568     else
01569     {
01570         query.prepare(
01571             "DELETE FROM inputgroup "
01572             "WHERE cardinputid  = :INPUTID AND "
01573             "      inputgroupid = :GROUPID ");
01574 
01575         query.bindValue(":INPUTID", inputid);
01576         query.bindValue(":GROUPID", inputgroupid);
01577     }
01578 
01579     if (!query.exec())
01580     {
01581         MythDB::DBError("CardUtil::DeleteInputGroup()", query);
01582         return false;
01583     }
01584 
01585     return true;
01586 }
01587 
01588 vector<uint> CardUtil::GetInputGroups(uint inputid)
01589 {
01590     vector<uint> list;
01591 
01592     MSqlQuery query(MSqlQuery::InitCon());
01593 
01594     query.prepare(
01595         "SELECT inputgroupid "
01596         "FROM inputgroup "
01597         "WHERE cardinputid = :INPUTID "
01598         "ORDER BY inputgroupid, cardinputid, inputgroupname");
01599 
01600     query.bindValue(":INPUTID", inputid);
01601 
01602     if (!query.exec())
01603     {
01604         MythDB::DBError("CardUtil::GetInputGroups()", query);
01605         return list;
01606     }
01607 
01608     while (query.next())
01609         list.push_back(query.value(0).toUInt());
01610 
01611     return list;
01612 }
01613 
01614 vector<uint> CardUtil::GetSharedInputGroups(uint cardid)
01615 {
01616     vector<uint> list;
01617 
01618     vector<uint> inputs = GetInputIDs(cardid);
01619     if (inputs.empty())
01620         return list;
01621 
01622     list = GetInputGroups(inputs[0]);
01623     for (uint i = 1; (i < inputs.size()) && list.size(); i++)
01624     {
01625         vector<uint> curlist = GetInputGroups(inputs[i]);
01626         vector<uint> newlist;
01627         for (uint j = 0; j < list.size(); j++)
01628         {
01629             if (find(curlist.begin(), curlist.end(), list[j]) != curlist.end())
01630                 newlist.push_back(list[j]);
01631         }
01632         list = newlist;
01633     }
01634 
01635     return list;
01636 }
01637 
01638 vector<uint> CardUtil::GetGroupCardIDs(uint inputgroupid)
01639 {
01640     vector<uint> list;
01641 
01642     MSqlQuery query(MSqlQuery::InitCon());
01643 
01644     query.prepare(
01645         "SELECT DISTINCT cardid "
01646         "FROM cardinput, inputgroup "
01647         "WHERE inputgroupid = :GROUPID AND "
01648         "      cardinput.cardinputid = inputgroup.cardinputid "
01649         "ORDER BY cardid");
01650 
01651     query.bindValue(":GROUPID", inputgroupid);
01652 
01653     if (!query.exec())
01654     {
01655         MythDB::DBError("CardUtil::GetGroupCardIDs()", query);
01656         return list;
01657     }
01658 
01659     while (query.next())
01660         list.push_back(query.value(0).toUInt());
01661 
01662     return list;
01663 }
01664 
01665 vector<uint> CardUtil::GetConflictingCards(uint inputid, uint exclude_cardid)
01666 {
01667     vector<uint> inputgroupids = CardUtil::GetInputGroups(inputid);
01668 
01669     for (uint i = 0; i < inputgroupids.size(); i++)
01670     {
01671         LOG(VB_RECORD, LOG_INFO, LOC + QString("  Group ID %1")
01672                                      .arg(inputgroupids[i]));
01673     }
01674 
01675     vector<uint> cardids;
01676     for (uint i = 0; i < inputgroupids.size(); i++)
01677     {
01678         vector<uint> tmp = CardUtil::GetGroupCardIDs(inputgroupids[i]);
01679         for (uint j = 0; j < tmp.size(); j++)
01680         {
01681             if (tmp[j] == exclude_cardid)
01682                 continue;
01683 
01684             if (find(cardids.begin(), cardids.end(), tmp[j]) != cardids.end())
01685                 continue;
01686 
01687             cardids.push_back(tmp[j]);
01688         }
01689     }
01690 
01691     for (uint i = 0; i < cardids.size(); i++)
01692         LOG(VB_RECORD, LOG_INFO, LOC + QString("  Card ID %1").arg(cardids[i]));
01693 
01694     return cardids;
01695 }
01696 
01697 bool CardUtil::GetTimeouts(uint cardid,
01698                            uint &signal_timeout, uint &channel_timeout)
01699 {
01700     MSqlQuery query(MSqlQuery::InitCon());
01701     query.prepare(
01702         "SELECT signal_timeout, channel_timeout "
01703         "FROM capturecard "
01704         "WHERE cardid = :CARDID");
01705     query.bindValue(":CARDID", cardid);
01706 
01707     if (!query.exec() || !query.isActive())
01708         MythDB::DBError("CardUtil::GetTimeouts()", query);
01709     else if (query.next())
01710     {
01711         signal_timeout  = (uint) max(query.value(0).toInt(), 250);
01712         channel_timeout = (uint) max(query.value(1).toInt(), 500);
01713         return true;
01714     }
01715 
01716     return false;
01717 }
01718 
01719 bool CardUtil::IsInNeedOfExternalInputConf(uint cardid)
01720 {
01721     DiSEqCDev dev;
01722     DiSEqCDevTree *diseqc_tree = dev.FindTree(cardid);
01723 
01724     bool needsConf = false;
01725     if (diseqc_tree)
01726         needsConf = diseqc_tree->IsInNeedOfConf();
01727 
01728     return needsConf;
01729 }
01730 
01731 uint CardUtil::GetQuickTuning(uint cardid, const QString &input_name)
01732 {
01733     uint quicktune = 0;
01734 
01735     MSqlQuery query(MSqlQuery::InitCon());
01736     query.prepare(
01737         "SELECT quicktune "
01738         "FROM cardinput "
01739         "WHERE cardid    = :CARDID AND "
01740         "      inputname = :INPUTNAME");
01741     query.bindValue(":CARDID",    cardid);
01742     query.bindValue(":INPUTNAME", input_name);
01743 
01744     if (!query.exec() || !query.isActive())
01745         MythDB::DBError("CardUtil::GetQuickTuning()", query);
01746     else if (query.next())
01747         quicktune = query.value(0).toUInt();
01748 
01749     return quicktune;
01750 }
01751 
01752 bool CardUtil::hasV4L2(int videofd)
01753 {
01754     (void) videofd;
01755 #ifdef USING_V4L2
01756     struct v4l2_capability vcap;
01757     memset(&vcap, 0, sizeof(vcap));
01758 
01759     return ((ioctl(videofd, VIDIOC_QUERYCAP, &vcap) >= 0) &&
01760             (vcap.capabilities & V4L2_CAP_VIDEO_CAPTURE));
01761 #else // if !USING_V4L2
01762     return false;
01763 #endif // !USING_V4L2
01764 }
01765 
01766 bool CardUtil::GetV4LInfo(
01767     int videofd, QString &card, QString &driver, uint32_t &version,
01768     uint32_t &capabilities)
01769 {
01770     card = driver = QString::null;
01771     version = 0;
01772     capabilities = 0;
01773 
01774     if (videofd < 0)
01775         return false;
01776 
01777 #ifdef USING_V4L2
01778     // First try V4L2 query
01779     struct v4l2_capability capability;
01780     memset(&capability, 0, sizeof(struct v4l2_capability));
01781     if (ioctl(videofd, VIDIOC_QUERYCAP, &capability) >= 0)
01782     {
01783         card = QString::fromAscii((const char*)capability.card);
01784         driver = QString::fromAscii((const char*)capability.driver);
01785         version = capability.version;
01786         capabilities = capability.capabilities;
01787     }
01788 #ifdef USING_V4L1
01789     else // Fallback to V4L1 query
01790     {
01791         struct video_capability capability;
01792         if (ioctl(videofd, VIDIOCGCAP, &capability) >= 0)
01793             card = QString::fromAscii((const char*)capability.name);
01794     }
01795 #endif // USING_V4L1
01796 #endif // USING_V4L2
01797 
01798     if (!driver.isEmpty())
01799         driver.remove( QRegExp("\\[[0-9]\\]$") );
01800 
01801     return !card.isEmpty();
01802 }
01803 
01804 InputNames CardUtil::ProbeV4LVideoInputs(int videofd, bool &ok)
01805 {
01806     (void) videofd;
01807 
01808     InputNames list;
01809     ok = false;
01810 
01811 #ifdef USING_V4L2
01812     bool usingv4l2 = hasV4L2(videofd);
01813 
01814     // V4L v2 query
01815     struct v4l2_input vin;
01816     memset(&vin, 0, sizeof(vin));
01817     while (usingv4l2 && (ioctl(videofd, VIDIOC_ENUMINPUT, &vin) >= 0))
01818     {
01819         QString input((char *)vin.name);
01820         list[vin.index] = input;
01821         vin.index++;
01822     }
01823     if (vin.index)
01824     {
01825         ok = true;
01826         return list;
01827     }
01828 
01829 #ifdef USING_V4L1
01830     // V4L v1 query
01831     struct video_capability vidcap;
01832     memset(&vidcap, 0, sizeof(vidcap));
01833     if (ioctl(videofd, VIDIOCGCAP, &vidcap) != 0)
01834     {
01835         QString msg = QObject::tr("Could not query inputs.");
01836         LOG(VB_GENERAL, LOG_ERR, "ProbeV4LVideoInputs(): Error, " + msg + ENO);
01837         list[-1] = msg;
01838         vidcap.channels = 0;
01839     }
01840 
01841     for (int i = 0; i < vidcap.channels; i++)
01842     {
01843         struct video_channel test;
01844         memset(&test, 0, sizeof(test));
01845         test.channel = i;
01846 
01847         if (ioctl(videofd, VIDIOCGCHAN, &test) != 0)
01848         {
01849             LOG(VB_GENERAL, LOG_ERR, "ProbeV4LVideoInputs(): Error, " +
01850                     QString("Could determine name of input #%1"
01851                             "\n\t\t\tNot adding it to the list.")
01852                     .arg(test.channel) + ENO);
01853             continue;
01854         }
01855 
01856         list[i] = test.name;
01857     }
01858 #endif // USING_V4L1
01859 
01860     // Create an input on single input cards that don't advertise input
01861     if (!list.size())
01862         list[0] = "Television";
01863 
01864     ok = true;
01865 #else // if !USING_V4L2
01866     list[-1] += QObject::tr("ERROR, Compile with V4L support to query inputs");
01867 #endif // !USING_V4L2
01868     return list;
01869 }
01870 
01871 InputNames CardUtil::ProbeV4LAudioInputs(int videofd, bool &ok)
01872 {
01873     (void) videofd;
01874 
01875     InputNames list;
01876     ok = false;
01877 
01878 #ifdef USING_V4L2
01879     bool usingv4l2 = hasV4L2(videofd);
01880 
01881     // V4L v2 query
01882     struct v4l2_audio ain;
01883     memset(&ain, 0, sizeof(ain));
01884     while (usingv4l2 && (ioctl(videofd, VIDIOC_ENUMAUDIO, &ain) >= 0))
01885     {
01886         QString input((char *)ain.name);
01887         list[ain.index] = input;
01888         ain.index++;
01889     }
01890     if (ain.index)
01891     {
01892         ok = true;
01893         return list;
01894     }
01895 
01896     ok = true;
01897 #else // if !USING_V4L2
01898     list[-1] += QObject::tr(
01899         "ERROR, Compile with V4L support to query audio inputs");
01900 #endif // !USING_V4L2
01901     return list;
01902 }
01903 
01904 InputNames CardUtil::GetConfiguredDVBInputs(uint cardid)
01905 {
01906     InputNames list;
01907     MSqlQuery query(MSqlQuery::InitCon());
01908     query.prepare(
01909         "SELECT cardinputid, inputname "
01910         "FROM cardinput "
01911         "WHERE cardid = :CARDID");
01912     query.bindValue(":CARDID", cardid);
01913 
01914     if (!query.exec() || !query.isActive())
01915         MythDB::DBError("CardUtil::GetConfiguredDVBInputs", query);
01916     else
01917     {
01918         while (query.next())
01919             list[query.value(0).toUInt()] = query.value(1).toString();
01920     }
01921     return list;
01922 }
01923 
01924 QStringList CardUtil::ProbeVideoInputs(QString device, QString cardtype)
01925 {
01926     QStringList ret;
01927 
01928     if (IsSingleInputCard(cardtype))
01929         ret += "MPEG2TS";
01930     else if ("DVB" == cardtype)
01931         ret += ProbeDVBInputs(device);
01932     else
01933         ret += ProbeV4LVideoInputs(device);
01934 
01935     return ret;
01936 }
01937 
01938 QStringList CardUtil::ProbeAudioInputs(QString device, QString cardtype)
01939 {
01940     LOG(VB_GENERAL, LOG_DEBUG, QString("ProbeAudioInputs(%1,%2)")
01941                                    .arg(device).arg(cardtype));
01942     QStringList ret;
01943 
01944     if ("HDPVR" == cardtype)
01945         ret += ProbeV4LAudioInputs(device);
01946 
01947     return ret;
01948 }
01949 
01950 QStringList CardUtil::ProbeV4LVideoInputs(QString device)
01951 {
01952     bool ok;
01953     QStringList ret;
01954     QByteArray dev = device.toAscii();
01955     int videofd = open(dev.constData(), O_RDWR);
01956     if (videofd < 0)
01957     {
01958         ret += QObject::tr("Could not open '%1' "
01959                            "to probe its inputs.").arg(device);
01960         return ret;
01961     }
01962     InputNames list = CardUtil::ProbeV4LVideoInputs(videofd, ok);
01963     close(videofd);
01964 
01965     if (!ok)
01966     {
01967         ret += list[-1];
01968         return ret;
01969     }
01970 
01971     InputNames::iterator it;
01972     for (it = list.begin(); it != list.end(); ++it)
01973     {
01974         if (it.key() >= 0)
01975             ret += *it;
01976     }
01977 
01978     return ret;
01979 }
01980 
01981 QStringList CardUtil::ProbeV4LAudioInputs(QString device)
01982 {
01983     LOG(VB_GENERAL, LOG_DEBUG, QString("ProbeV4LAudioInputs(%1)").arg(device));
01984 
01985     bool ok;
01986     QStringList ret;
01987     int videofd = open(device.toAscii().constData(), O_RDWR);
01988     if (videofd < 0)
01989     {
01990         LOG(VB_GENERAL, LOG_ERR, "ProbeAudioInputs() -> couldn't open device");
01991         ret += QObject::tr("Could not open '%1' to probe its inputs.")
01992                    .arg(device);
01993         return ret;
01994     }
01995     InputNames list = CardUtil::ProbeV4LAudioInputs(videofd, ok);
01996     close(videofd);
01997 
01998     if (!ok)
01999     {
02000         ret += list[-1];
02001         return ret;
02002     }
02003 
02004     InputNames::iterator it;
02005     for (it = list.begin(); it != list.end(); ++it)
02006     {
02007         if (it.key() >= 0)
02008             ret += *it;
02009     }
02010 
02011     return ret;
02012 }
02013 
02014 QStringList CardUtil::ProbeDVBInputs(QString device)
02015 {
02016     QStringList ret;
02017 
02018 #ifdef USING_DVB
02019     uint cardid = CardUtil::GetFirstCardID(device);
02020     if (!cardid)
02021         return ret;
02022 
02023     InputNames list = GetConfiguredDVBInputs(cardid);
02024     InputNames::iterator it;
02025     for (it = list.begin(); it != list.end(); ++it)
02026     {
02027         if (it.key())
02028             ret += *it;
02029     }
02030 #else
02031     (void) device;
02032     ret += QObject::tr("ERROR, Compile with DVB support to query inputs");
02033 #endif
02034 
02035     return ret;
02036 }
02037 
02038 QString CardUtil::GetDeviceLabel(const QString &cardtype,
02039                                  const QString &videodevice)
02040 {
02041     return QString("[ %1 : %2 ]").arg(cardtype).arg(videodevice);
02042 }
02043 
02044 QString CardUtil::GetDeviceLabel(uint cardid)
02045 {
02046     QString devlabel;
02047     MSqlQuery query(MSqlQuery::InitCon());
02048     query.prepare("SELECT cardtype, videodevice "
02049                   "FROM capturecard WHERE cardid = :CARDID ");
02050     query.bindValue(":CARDID", cardid);
02051 
02052     if (query.exec() && query.next())
02053     {
02054         return GetDeviceLabel(query.value(0).toString(),
02055                               query.value(1).toString());
02056     }
02057 
02058     return "[ UNKNOWN ]";
02059 }
02060 
02061 void CardUtil::GetCardInputs(
02062     uint                cardid,
02063     const QString      &device,
02064     const QString      &cardtype,
02065     QStringList        &inputLabels,
02066     vector<CardInput*> &cardInputs)
02067 {
02068     QStringList inputs;
02069     bool is_dtv = !IsEncoder(cardtype) && !IsUnscanable(cardtype);
02070 
02071     if (IsSingleInputCard(cardtype))
02072         inputs += "MPEG2TS";
02073     else if ("DVB" != cardtype)
02074         inputs += ProbeV4LVideoInputs(device);
02075 
02076     QString dev_label = GetDeviceLabel(cardtype, device);
02077 
02078     QStringList::iterator it = inputs.begin();
02079     for (; it != inputs.end(); ++it)
02080     {
02081         CardInput *cardinput = new CardInput(is_dtv, false, false, cardid);
02082         cardinput->loadByInput(cardid, (*it));
02083         inputLabels.push_back(
02084             dev_label + QString(" (%1) -> %2")
02085             .arg(*it).arg(cardinput->getSourceName()));
02086         cardInputs.push_back(cardinput);
02087     }
02088 
02089 #ifdef USING_DVB
02090     if ("DVB" == cardtype)
02091     {
02092         bool needs_conf = IsInNeedOfExternalInputConf(cardid);
02093         InputNames list = GetConfiguredDVBInputs(cardid);
02094         if (!needs_conf && list.empty())
02095             list[0] = "DVBInput";
02096 
02097         InputNames::const_iterator it;
02098         for (it = list.begin(); it != list.end(); ++it)
02099         {
02100             CardInput *cardinput = new CardInput(is_dtv, true, false, cardid);
02101             cardinput->loadByInput(cardid, *it);
02102             inputLabels.push_back(
02103                 dev_label + QString(" (%1) -> %2")
02104                 .arg(*it).arg(cardinput->getSourceName()));
02105             cardInputs.push_back(cardinput);
02106         }
02107 
02108         // plus add one "new" input
02109         if (needs_conf)
02110         {
02111             CardInput *newcard = new CardInput(is_dtv, true, true, cardid);
02112             QString newname = QString("DVBInput #%1").arg(list.size() + 1);
02113             newcard->loadByInput(cardid, newname);
02114             inputLabels.push_back(dev_label + " " + QObject::tr("New Input"));
02115             cardInputs.push_back(newcard);
02116         }
02117     }
02118 #endif // USING_DVB
02119 }
02120 
02121 int CardUtil::CreateCaptureCard(const QString &videodevice,
02122                                  const QString &audiodevice,
02123                                  const QString &vbidevice,
02124                                  const QString &cardtype,
02125                                  const uint audioratelimit,
02126                                  const QString &hostname,
02127                                  const uint dvb_swfilter,
02128                                  const uint dvb_sat_type,
02129                                  bool       dvb_wait_for_seqstart,
02130                                  bool       skipbtaudio,
02131                                  bool       dvb_on_demand,
02132                                  const uint dvb_diseqc_type,
02133                                  const uint firewire_speed,
02134                                  const QString &firewire_model,
02135                                  const uint firewire_connection,
02136                                  const uint signal_timeout,
02137                                  const uint channel_timeout,
02138                                  const uint dvb_tuning_delay,
02139                                  const uint contrast,
02140                                  const uint brightness,
02141                                  const uint colour,
02142                                  const uint hue,
02143                                  const uint diseqcid,
02144                                  bool       dvb_eitscan)
02145 {
02146     MSqlQuery query(MSqlQuery::InitCon());
02147 
02148     query.prepare(
02149         "INSERT INTO capturecard "
02150         "(videodevice, audiodevice, vbidevice, cardtype, "
02151         "audioratelimit, hostname, dvb_swfilter, dvb_sat_type, "
02152         "dvb_wait_for_seqstart, skipbtaudio, dvb_on_demand, dvb_diseqc_type, "
02153         "firewire_speed, firewire_model, firewire_connection, signal_timeout, "
02154         "channel_timeout, dvb_tuning_delay, contrast, brightness, colour, "
02155         "hue, diseqcid, dvb_eitscan) "
02156         "VALUES (:VIDEODEVICE, :AUDIODEVICE, :VBIDEVICE, :CARDTYPE, "
02157         ":AUDIORATELIMIT, :HOSTNAME, :DVBSWFILTER, :DVBSATTYPE, "
02158         ":DVBWAITFORSEQSTART, :SKIPBTAUDIO, :DVBONDEMAND, :DVBDISEQCTYPE, "
02159         ":FIREWIRESPEED, :FIREWIREMODEL, :FIREWIRECONNECTION, :SIGNALTIMEOUT, "
02160         ":CHANNELTIMEOUT, :DVBTUNINGDELAY, :CONTRAST, :BRIGHTNESS, :COLOUR, "
02161         ":HUE, :DISEQCID, :DVBEITSCAN ) ");
02162 
02163     query.bindValue(":VIDEODEVICE", videodevice);
02164     query.bindValue(":AUDIODEVICE", audiodevice);
02165     query.bindValue(":VBIDEVICE", vbidevice);
02166     query.bindValue(":CARDTYPE", cardtype);
02167     query.bindValue(":AUDIORATELIMIT", audioratelimit);
02168     query.bindValue(":HOSTNAME", hostname);
02169     query.bindValue(":DVBSWFILTER", dvb_swfilter);
02170     query.bindValue(":DVBSATTYPE", dvb_sat_type);
02171     query.bindValue(":DVBWAITFORSEQSTART", dvb_wait_for_seqstart);
02172     query.bindValue(":SKIPBTAUDIO", skipbtaudio);
02173     query.bindValue(":DVBONDEMAND", dvb_on_demand);
02174     query.bindValue(":DVBDISEQCTYPE", dvb_diseqc_type);
02175     query.bindValue(":FIREWIRESPEED", firewire_speed);
02176     query.bindValue(":FIREWIREMODEL", firewire_model);
02177     query.bindValue(":FIREWIRECONNECTION", firewire_connection);
02178     query.bindValue(":SIGNALTIMEOUT", signal_timeout);
02179     query.bindValue(":CHANNELTIMEOUT", channel_timeout);
02180     query.bindValue(":DVBTUNINGDELAY", dvb_tuning_delay);
02181     query.bindValue(":CONTRAST", contrast);
02182     query.bindValue(":BRIGHTNESS", brightness);
02183     query.bindValue(":COLOUR", colour);
02184     query.bindValue(":HUE", hue);
02185     query.bindValue(":DISEQCID", diseqcid);
02186     query.bindValue(":DVBEITSCAN", dvb_eitscan);
02187 
02188     if (!query.exec())
02189     {
02190         MythDB::DBError("CreateCaptureCard", query);
02191         return -1;
02192     }
02193 
02194     query.prepare("SELECT MAX(cardid) FROM capturecard");
02195 
02196     if (!query.exec())
02197     {
02198         MythDB::DBError("CreateCaptureCard maxcard", query);
02199         return -1;
02200     }
02201 
02202     uint cardid = -1;
02203 
02204     if (query.next())
02205         cardid = query.value(0).toUInt();
02206 
02207     return cardid;
02208 }
02209 
02210 bool CardUtil::DeleteCard(uint cardid)
02211 {
02212     MSqlQuery query(MSqlQuery::InitCon());
02213     bool ok = true;
02214 
02215     if (!cardid)
02216         return true;
02217 
02218     // delete any DiSEqC device tree
02219     DiSEqCDevTree tree;
02220     tree.Load(cardid);
02221     if (!tree.Root())
02222     {
02223         tree.SetRoot(NULL);
02224         tree.Store(cardid);
02225     }
02226 
02227     // delete any clones
02228     QString rawtype     = GetRawCardType(cardid);
02229     QString videodevice = GetVideoDevice(cardid);
02230     if (IsTunerSharingCapable(rawtype) && !videodevice.isEmpty())
02231     {
02232         query.prepare(
02233             "SELECT cardid "
02234             "FROM capturecard "
02235             "WHERE videodevice = :DEVICE AND "
02236             "      cardid      > :CARDID");
02237         query.bindValue(":DEVICE", videodevice);
02238         query.bindValue(":CARDID", cardid);
02239 
02240         if (!query.exec())
02241         {
02242             MythDB::DBError("DeleteCard -- find clone cards", query);
02243             return false;
02244         }
02245 
02246         while (query.next())
02247             ok &= DeleteCard(query.value(0).toUInt());
02248 
02249         if (!ok)
02250             return false;
02251     }
02252 
02253     // delete inputs
02254     vector<uint> inputs = CardUtil::GetInputIDs(cardid);
02255     for (uint i = 0; i < inputs.size(); i++)
02256         ok &= CardUtil::DeleteInput(inputs[i]);
02257 
02258     if (!ok)
02259         return false;
02260 
02261     // actually delete the capturecard row for this card
02262     query.prepare("DELETE FROM capturecard WHERE cardid = :CARDID");
02263     query.bindValue(":CARDID", cardid);
02264 
02265     if (!query.exec())
02266     {
02267         MythDB::DBError("DeleteCard -- delete row", query);
02268         ok = false;
02269     }
02270 
02271     if (ok)
02272     {
02273         // delete any orphaned inputs & unused input groups
02274         DeleteOrphanInputs();
02275         UnlinkInputGroup(0,0);
02276     }
02277 
02278     return ok;
02279 }
02280 
02281 bool CardUtil::DeleteAllCards(void)
02282 {
02283     MSqlQuery query(MSqlQuery::InitCon());
02284     return (query.exec("TRUNCATE TABLE inputgroup") &&
02285             query.exec("TRUNCATE TABLE diseqc_config") &&
02286             query.exec("TRUNCATE TABLE diseqc_tree") &&
02287             query.exec("TRUNCATE TABLE cardinput") &&
02288             query.exec("TRUNCATE TABLE capturecard"));
02289 }
02290 
02291 vector<uint> CardUtil::GetCardList(void)
02292 {
02293     vector<uint> list;
02294 
02295     MSqlQuery query(MSqlQuery::InitCon());
02296     query.prepare(
02297         "SELECT cardid "
02298         "FROM capturecard "
02299         "ORDER BY cardid");
02300 
02301     if (!query.exec())
02302         MythDB::DBError("CardUtil::GetCardList()", query);
02303     else
02304     {
02305         while (query.next())
02306             list.push_back(query.value(0).toUInt());
02307     }
02308 
02309     return list;
02310 }
02311 
02312 
02313 QString CardUtil::GetDeviceName(dvb_dev_type_t type, const QString &device)
02314 {
02315     QString devname = QString(device);
02316 
02317     if (DVB_DEV_FRONTEND == type)
02318         return devname;
02319     else if (DVB_DEV_DVR == type)
02320         return devname.replace(devname.indexOf("frontend"), 8, "dvr");
02321     else if (DVB_DEV_DEMUX == type)
02322         return devname.replace(devname.indexOf("frontend"), 8, "demux");
02323     else if (DVB_DEV_CA == type)
02324         return devname.replace(devname.indexOf("frontend"), 8, "ca");
02325     else if (DVB_DEV_AUDIO == type)
02326         return devname.replace(devname.indexOf("frontend"), 8, "audio");
02327     else if (DVB_DEV_VIDEO == type)
02328         return devname.replace(devname.indexOf("frontend"), 8, "video");
02329 
02330     return "";
02331 }
02332 
02342 bool CardUtil::HDHRdoesDVB(const QString &device)
02343 {
02344     (void) device;
02345 
02346 #ifdef USING_HDHOMERUN
02347     hdhomerun_device_t  *hdhr;
02348     hdhr = hdhomerun_device_create_from_str(device.toAscii(), NULL);
02349     if (!hdhr)
02350         return false;
02351 
02352     const char *model = hdhomerun_device_get_model_str(hdhr);
02353     if (model && strstr(model, "dvb"))
02354         return true;
02355 #endif
02356 
02357     return false;
02358 }
02359 
02364 QString CardUtil::GetHDHRdesc(const QString &device)
02365 {
02366     QString connectErr = QObject::tr("Unable to connect to device.");
02367 
02368 #ifdef USING_HDHOMERUN
02369     bool      deviceIsIP = false;
02370     uint32_t  dev;
02371 
02372     if (device.contains('.'))  // Simplistic check, but also allows DNS names
02373         deviceIsIP = true;
02374     else
02375     {
02376         bool validID;
02377 
02378         dev = device.toUInt(&validID, 16);
02379         if (!validID || !hdhomerun_discover_validate_device_id(dev))
02380             return QObject::tr("Invalid Device ID");
02381     }
02382 
02383 
02384     LOG(VB_GENERAL, LOG_INFO, "CardUtil::GetHDHRdescription(" + device +
02385                               ") - trying to locate device");
02386 
02387     hdhomerun_device_t  *hdhr;
02388     hdhr = hdhomerun_device_create_from_str(device.toAscii(), NULL);
02389     if (!hdhr)
02390         return QObject::tr("Invalid Device ID or address.");
02391 
02392     const char *model = hdhomerun_device_get_model_str(hdhr);
02393     if (!model)
02394         return connectErr;
02395 
02396 
02397     QString   description = model;
02398     char     *sVersion;
02399     uint32_t  iVersion;
02400 
02401     if (hdhomerun_device_get_version(hdhr, &sVersion, &iVersion))
02402         description += QObject::tr(", firmware: %2").arg(sVersion);
02403 
02404     return description;
02405 #else
02406 
02407     (void) device;
02408     return connectErr;
02409 #endif
02410 }
02411 
02412 #ifdef USING_ASI
02413 static QString sys_dev(uint device_num, QString dev)
02414 {
02415     return QString("/sys/class/asi/asirx%1/%2").arg(device_num).arg(dev);
02416 }
02417 
02418 static QString read_sys(QString sys_dev)
02419 {
02420     QFile f(sys_dev);
02421     f.open(QIODevice::ReadOnly);
02422     QByteArray sdba = f.readAll();
02423     f.close();
02424     return sdba;
02425 }
02426 
02427 static bool write_sys(QString sys_dev, QString str)
02428 {
02429     QFile f(sys_dev);
02430     f.open(QIODevice::WriteOnly);
02431     QByteArray ba = str.toLocal8Bit();
02432     qint64 offset = 0;
02433     for (uint tries = 0; (offset < ba.size()) && tries < 5; tries++)
02434     {
02435         qint64 written = f.write(ba.data()+offset, ba.size()-offset);
02436         if (written < 0)
02437             return false;
02438         offset += written;
02439     }
02440     return true;
02441 }
02442 #endif
02443 
02444 int CardUtil::GetASIDeviceNumber(const QString &device, QString *error)
02445 {
02446 #ifdef USING_ASI
02447     // basic confirmation
02448     struct stat statbuf;
02449     memset(&statbuf, 0, sizeof(statbuf));
02450     if (stat(device.toLocal8Bit().constData(), &statbuf) < 0)
02451     {
02452         if (error)
02453             *error = QString("Unable to stat '%1'").arg(device) + ENO;
02454         return -1;
02455     }
02456 
02457     if (!S_ISCHR(statbuf.st_mode))
02458     {
02459         if (error)
02460             *error = QString("'%1' is not a character device").arg(device);
02461         return -1;
02462     }
02463 
02464     if (!(statbuf.st_rdev & 0x0080))
02465     {
02466         if (error)
02467             *error = QString("'%1' not a DVEO ASI receiver").arg(device);
02468         return -1;
02469     }
02470 
02471     int device_num = statbuf.st_rdev & 0x007f;
02472 
02473     // extra confirmation
02474     QString sys_dev_contents = read_sys(sys_dev(device_num, "dev"));
02475     QStringList sys_dev_clist = sys_dev_contents.split(":");
02476     if (2 != sys_dev_clist.size())
02477     {
02478         if (error)
02479         {
02480             *error = QString("Unable to read '%1'")
02481                 .arg(sys_dev(device_num, "dev"));
02482         }
02483         return -1;
02484     }
02485     if (sys_dev_clist[0].toUInt() != (statbuf.st_rdev>>8))
02486     {
02487         if (error)
02488             *error = QString("'%1' not a DVEO ASI device").arg(device);
02489         return -1;
02490     }
02491 
02492     return device_num;
02493 #else
02494     (void) device;
02495     if (error)
02496         *error = "Not compiled with ASI support.";
02497     return -1;
02498 #endif
02499 }
02500 
02501 uint CardUtil::GetASIBufferSize(uint device_num, QString *error)
02502 {
02503 #ifdef USING_ASI
02504     // get the buffer size
02505     QString sys_bufsize_contents = read_sys(sys_dev(device_num, "bufsize"));
02506     bool ok;
02507     uint buf_size = sys_bufsize_contents.toUInt(&ok);
02508     if (!ok)
02509     {
02510         if (error)
02511         {
02512             *error = QString("Failed to read buffer size from '%1'")
02513                 .arg(sys_dev(device_num, "bufsize"));
02514         }
02515         return 0;
02516     }
02517     return buf_size;
02518 #else
02519     (void) device_num;
02520     if (error)
02521         *error = "Not compiled with ASI support.";
02522     return 0;
02523 #endif
02524 }
02525 
02526 int CardUtil::GetASIMode(uint device_num, QString *error)
02527 {
02528 #ifdef USING_ASI
02529     QString sys_bufsize_contents = read_sys(sys_dev(device_num, "mode"));
02530     bool ok;
02531     uint mode = sys_bufsize_contents.toUInt(&ok);
02532     if (!ok)
02533     {
02534         if (error)
02535         {
02536             *error = QString("Failed to read mode from '%1'")
02537                 .arg(sys_dev(device_num, "mode"));
02538         }
02539         return -1;
02540     }
02541     return mode;
02542 #else
02543     (void) device_num;
02544     if (error)
02545         *error = "Not compiled with ASI support.";
02546     return -1;
02547 #endif
02548 }
02549 
02550 bool CardUtil::SetASIMode(uint device_num, uint mode, QString *error)
02551 {
02552 #ifdef USING_ASI
02553     QString sys_bufsize_contents = read_sys(sys_dev(device_num, "mode"));
02554     bool ok;
02555     uint old_mode = sys_bufsize_contents.toUInt(&ok);
02556     if (ok && old_mode == mode)
02557         return true;
02558     ok = write_sys(sys_dev(device_num, "mode"), QString("%1\n").arg(mode));
02559     if (!ok && error)
02560     {
02561         *error = QString("Failed to set mode to %1 using '%2'")
02562             .arg(mode).arg(sys_dev(device_num, "mode"));
02563     }
02564     return ok;
02565 #else
02566     (void) device_num;
02567     if (error)
02568         *error = "Not compiled with ASI support.";
02569     return false;
02570 #endif
02571 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends