MythTV  0.28pre
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Groups Pages
cardutil.cpp
Go to the documentation of this file.
1 // Standard UNIX C headers
2 #include <fcntl.h>
3 #include <unistd.h>
4 
5 #include <algorithm>
6 
7 #if defined(USING_V4L2) || defined(USING_DVB)
8 #include <sys/ioctl.h>
9 #endif
10 
11 // Qt headers
12 #include <QMap>
13 #include <QDir>
14 
15 // MythTV headers
16 #include "mythconfig.h"
17 #include "cardutil.h"
18 #include "videosource.h"
19 #include "dvbchannel.h"
20 #include "diseqcsettings.h"
21 #include "sourceutil.h"
22 #include "mythdb.h"
23 #include "mythlogging.h"
24 
25 #ifdef USING_DVB
26 #include "dvbtypes.h"
27 #endif
28 
29 #ifdef USING_V4L1
30 #include <linux/videodev.h>
31 #endif
32 
33 #ifdef USING_V4L2
34 #include <linux/videodev2.h>
35 #endif
36 
37 #ifdef USING_HDHOMERUN
38 #include "hdhomerun.h"
39 #endif
40 
41 #ifdef USING_ASI
42 #include <sys/types.h>
43 #include <sys/stat.h>
44 #include <dveo/asi.h>
45 #include <dveo/master.h>
46 #endif
47 
48 #define LOC QString("CardUtil: ")
49 
51 {
52  QString cardTypes = "";
53 
54 #ifdef USING_DVB
55  cardTypes += "'DVB'";
56 #endif // USING_DVB
57 
58 #ifdef USING_V4L2
59  if (!cardTypes.isEmpty())
60  cardTypes += ",";
61  cardTypes += "'V4L'";
62 # ifdef USING_IVTV
63  cardTypes += ",'MPEG'";
64 # endif // USING_IVTV
65 #endif // USING_V4L2
66 
67 #ifdef USING_IPTV
68  if (!cardTypes.isEmpty())
69  cardTypes += ",";
70  cardTypes += "'FREEBOX'";
71 #endif // USING_IPTV
72 
73 #ifdef USING_HDHOMERUN
74  if (!cardTypes.isEmpty())
75  cardTypes += ",";
76  cardTypes += "'HDHOMERUN'";
77 #endif // USING_HDHOMERUN
78 
79 #ifdef USING_ASI
80  if (!cardTypes.isEmpty())
81  cardTypes += ",";
82  cardTypes += "'ASI'";
83 #endif
84 
85 #ifdef USING_CETON
86  if (!cardTypes.isEmpty())
87  cardTypes += ",";
88  cardTypes += "'CETON'";
89 #endif // USING_CETON
90 
91  if (cardTypes.isEmpty())
92  cardTypes = "'DUMMY'";
93 
94  return QString("(%1)").arg(cardTypes);
95 }
96 
98  const QString &cardType)
99 {
100  if (cardType == "HDHOMERUN")
101  {
102 #ifdef USING_HDHOMERUN
103  hdhomerun_device_t *hdhr;
104  hdhomerun_tuner_status_t status;
105  QString device = GetVideoDevice(cardid);
106  hdhr = hdhomerun_device_create_from_str(device.toLatin1(), NULL);
107  if (!hdhr)
108  return false;
109 
110  int oob = -1;
111  oob = hdhomerun_device_get_oob_status(hdhr, NULL, &status);
112 
113  // if no OOB tuner, oob will be < 1. If no CC present, OOB
114  // status will be "none."
115  if (oob > 0 && (strncmp(status.channel, "none", 4) != 0))
116  {
117  LOG(VB_GENERAL, LOG_INFO, "Cardutil: HDHomeRun Cablecard Present.");
118  hdhomerun_device_destroy(hdhr);
119  return true;
120  }
121 
122  hdhomerun_device_destroy(hdhr);
123 
124 #endif
125  return false;
126  }
127  else if (cardType == "CETON")
128  {
129 #ifdef USING_CETON
130  // TODO FIXME implement detection of Cablecard presence
131  LOG(VB_GENERAL, LOG_INFO, "Cardutil: TODO Ceton Is Cablecard Present?");
132  return true;
133 #else
134  return false;
135 #endif
136  }
137  else
138  return false;
139 }
140 
141 bool CardUtil::IsTunerShared(uint cardidA, uint cardidB)
142 {
143  LOG(VB_GENERAL, LOG_DEBUG, QString("IsTunerShared(%1,%2)")
144  .arg(cardidA).arg(cardidB));
145 
146  MSqlQuery query(MSqlQuery::InitCon());
147  query.prepare("SELECT videodevice, hostname, cardtype "
148  "FROM capturecard "
149  "WHERE ( (cardid = :CARDID_A) OR "
150  " (cardid = :CARDID_B) )");
151  query.bindValue(":CARDID_A", cardidA);
152  query.bindValue(":CARDID_B", cardidB);
153 
154  if (!query.exec())
155  {
156  MythDB::DBError("CardUtil::is_tuner_shared", query);
157  return false;
158  }
159 
160  if (!query.next())
161  return false;
162 
163  const QString vdevice = query.value(0).toString();
164  const QString hostname = query.value(1).toString();
165  const QString cardtype = query.value(2).toString();
166 
167  if (!IsTunerSharingCapable(cardtype.toUpper()))
168  return false;
169 
170  if (!query.next())
171  return false;
172 
173  bool ret = ((vdevice == query.value(0).toString()) &&
174  (hostname == query.value(1).toString()) &&
175  (cardtype == query.value(2).toString()));
176 
177  LOG(VB_RECORD, LOG_DEBUG, QString("IsTunerShared(%1,%2) -> %3")
178  .arg(cardidA).arg(cardidB).arg(ret));
179 
180  return ret;
181 }
182 
188 bool CardUtil::IsCardTypePresent(const QString &rawtype, QString hostname)
189 {
190  if (hostname.isEmpty())
191  hostname = gCoreContext->GetHostName();
192 
193  MSqlQuery query(MSqlQuery::InitCon());
194  QString qstr =
195  "SELECT count(cardtype) "
196  "FROM capturecard, cardinput "
197  "WHERE cardinput.cardid = capturecard.cardid AND "
198  " capturecard.hostname = :HOSTNAME";
199 
200  if (!rawtype.isEmpty())
201  qstr += " AND capturecard.cardtype = :CARDTYPE";
202 
203  query.prepare(qstr);
204 
205  if (!rawtype.isEmpty())
206  query.bindValue(":CARDTYPE", rawtype.toUpper());
207 
208  query.bindValue(":HOSTNAME", hostname);
209 
210  if (!query.exec())
211  {
212  MythDB::DBError("CardUtil::IsCardTypePresent", query);
213  return false;
214  }
215 
216  uint count = 0;
217  if (query.next())
218  count = query.value(0).toUInt();
219 
220  return count > 0;
221 }
222 
223 QStringList CardUtil::GetCardTypes(void)
224 {
225  QStringList cardtypes;
226 
227  MSqlQuery query(MSqlQuery::InitCon());
228  query.prepare("SELECT DISTINCT cardtype "
229  "FROM capturecard");
230 
231  if (!query.exec())
232  {
233  MythDB::DBError("CardUtil::GetCardTypes()", query);
234  }
235  else
236  {
237  while (query.next())
238  cardtypes.push_back(query.value(0).toString());
239  }
240 
241  return cardtypes;
242 }
243 
249 QStringList CardUtil::GetVideoDevices(const QString &rawtype, QString hostname)
250 {
251  QStringList list;
252 
253  if (hostname.isEmpty())
254  hostname = gCoreContext->GetHostName();
255 
256  MSqlQuery query(MSqlQuery::InitCon());
257  QString qstr =
258  "SELECT videodevice "
259  "FROM capturecard "
260  "WHERE hostname = :HOSTNAME";
261 
262  if (!rawtype.isEmpty())
263  qstr += " AND cardtype = :CARDTYPE";
264 
265  query.prepare(qstr);
266 
267  if (!rawtype.isEmpty())
268  query.bindValue(":CARDTYPE", rawtype.toUpper());
269 
270  query.bindValue(":HOSTNAME", hostname);
271 
272  if (!query.exec())
273  {
274  MythDB::DBError("CardUtil::GetVideoDevices", query);
275  return list;
276  }
277 
278  QMap<QString,bool> dup;
279  while (query.next())
280  {
281  QString videodevice = query.value(0).toString();
282  if (dup[videodevice])
283  continue;
284 
285  list.push_back(videodevice);
286  dup[videodevice] = true;
287  }
288 
289  return list;
290 }
291 
292 QStringList CardUtil::ProbeVideoDevices(const QString &rawtype)
293 {
294  QStringList devs;
295 
296  if (rawtype.toUpper() == "DVB")
297  {
298  QDir dir("/dev/dvb", "adapter*", QDir::Name, QDir::Dirs);
299  const QFileInfoList il = dir.entryInfoList();
300  if (il.isEmpty())
301  return devs;
302 
303  QFileInfoList::const_iterator it = il.begin();
304 
305  for (; it != il.end(); ++it)
306  {
307  QDir subdir(it->filePath(), "frontend*", QDir::Name, QDir::Files | QDir::System);
308  const QFileInfoList subil = subdir.entryInfoList();
309  if (subil.isEmpty())
310  continue;
311 
312  QFileInfoList::const_iterator subit = subil.begin();
313  for (; subit != subil.end(); ++subit)
314  devs.push_back(subit->filePath());
315  }
316  }
317  else if (rawtype.toUpper() == "ASI")
318  {
319  QDir dir("/dev/", "asirx*", QDir::Name, QDir::System);
320  const QFileInfoList il = dir.entryInfoList();
321  if (il.isEmpty())
322  return devs;
323 
324  QFileInfoList::const_iterator it = il.begin();
325  for (; it != il.end(); ++it)
326  {
327  if (GetASIDeviceNumber(it->filePath()) >= 0)
328  {
329  devs.push_back(it->filePath());
330  continue;
331  }
332  break;
333  }
334  }
335 #ifdef USING_HDHOMERUN
336  else if (rawtype.toUpper() == "HDHOMERUN")
337  {
338  uint32_t target_ip = 0;
339  uint32_t device_type = HDHOMERUN_DEVICE_TYPE_TUNER;
340  uint32_t device_id = HDHOMERUN_DEVICE_ID_WILDCARD;
341  const int max_count = 50;
342  hdhomerun_discover_device_t result_list[max_count];
343 
344  int result = hdhomerun_discover_find_devices_custom(
345  target_ip, device_type, device_id, result_list, max_count);
346 
347  if (result == -1)
348  {
349  LOG(VB_GENERAL, LOG_ERR, "Error finding HDHomerun devices");
350  return devs;
351  }
352 
353  if (result >= max_count)
354  {
355  LOG(VB_GENERAL, LOG_WARNING,
356  "Warning: may be > 50 HDHomerun devices");
357  }
358 
359  // Return "deviceid ipaddress" pairs
360  for (int i = 0; i < result; i++)
361  {
362  QString id = QString("%1").arg(result_list[i].device_id, 0, 16);
363  QString ip = QString("%1.%2.%3.%4")
364  .arg((result_list[i].ip_addr>>24) & 0xFF)
365  .arg((result_list[i].ip_addr>>16) & 0xFF)
366  .arg((result_list[i].ip_addr>> 8) & 0xFF)
367  .arg((result_list[i].ip_addr>> 0) & 0xFF);
368 
369  for (int tuner = 0; tuner < result_list[i].tuner_count; tuner++)
370  {
371  QString hdhrdev = id.toUpper() + " " + ip + " " +
372  QString("%1").arg(tuner);
373  devs.push_back(hdhrdev);
374  }
375  }
376  }
377 #endif // USING_HDHOMERUN
378 #ifdef USING_CETON
379  else if (rawtype.toUpper() == "CETON")
380  {
381  // TODO implement CETON probing.
382  LOG(VB_GENERAL, LOG_INFO, "CardUtil::ProbeVideoDevices: "
383  "TODO Probe Ceton devices");
384  }
385 #endif // USING_CETON
386  else
387  {
388  LOG(VB_GENERAL, LOG_ERR, QString("Raw Type: '%1' is not supported")
389  .arg(rawtype));
390  }
391 
392  return devs;
393 }
394 
395 QString CardUtil::ProbeDVBType(const QString &device)
396 {
397  QString ret = "ERROR_UNKNOWN";
398 
399  if (device.isEmpty())
400  return ret;
401 
402 #ifdef USING_DVB
403  QString dvbdev = CardUtil::GetDeviceName(DVB_DEV_FRONTEND, device);
404  QByteArray dev = dvbdev.toLatin1();
405 
406  int fd_frontend = open(dev.constData(), O_RDWR | O_NONBLOCK);
407  if (fd_frontend < 0)
408  {
409  LOG(VB_GENERAL, LOG_ERR, QString("Can't open DVB frontend (%1) for %2.")
410  .arg(dvbdev).arg(device));
411  return ret;
412  }
413 
414  struct dvb_frontend_info info;
415  memset(&info, 0, sizeof(info));
416  int err = ioctl(fd_frontend, FE_GET_INFO, &info);
417  if (err < 0)
418  {
419  close(fd_frontend);
420  LOG(VB_GENERAL, LOG_ERR, QString("FE_GET_INFO ioctl failed (%1)")
421  .arg(dvbdev) + ENO);
422  return ret;
423  }
424  close(fd_frontend);
425 
426  DTVTunerType type(info.type);
427 #if HAVE_FE_CAN_2G_MODULATION
428  if (type == DTVTunerType::kTunerTypeDVBS1 &&
429  (info.caps & FE_CAN_2G_MODULATION))
431 #endif // HAVE_FE_CAN_2G_MODULATION
432  ret = (type.toString() != "UNKNOWN") ? type.toString().toUpper() : ret;
433 #endif // USING_DVB
434 
435  return ret;
436 }
437 
441 QString CardUtil::ProbeDVBFrontendName(const QString &device)
442 {
443  QString ret = "ERROR_UNKNOWN";
444  (void) device;
445 
446 #ifdef USING_DVB
447  QString dvbdev = CardUtil::GetDeviceName(DVB_DEV_FRONTEND, device);
448  QByteArray dev = dvbdev.toLatin1();
449  int fd_frontend = open(dev.constData(), O_RDWR | O_NONBLOCK);
450  if (fd_frontend < 0)
451  return "ERROR_OPEN";
452 
453  struct dvb_frontend_info info;
454  memset(&info, 0, sizeof(info));
455  int err = ioctl(fd_frontend, FE_GET_INFO, &info);
456  if (err < 0)
457  {
458  close(fd_frontend);
459  return "ERROR_PROBE";
460  }
461 
462  ret = info.name;
463 
464  close(fd_frontend);
465 #endif // USING_DVB
466 
467  return ret;
468 }
469 
487 bool CardUtil::HasDVBCRCBug(const QString &device)
488 {
489  QString name = ProbeDVBFrontendName(device);
490  return ((name == "VLSI VES1x93 DVB-S") || // munges PMT
491  (name == "ST STV0299 DVB-S")); // munges PAT
492 }
493 
495 {
496  QString name = ProbeDVBFrontendName(device);
497  if (name.indexOf("DVB-S") >= 0)
498  return 300;
499  if (name == "DiBcom 3000P/M-C DVB-T")
500  return 100;
501  return 25;
502 }
503 
505 {
506  QString type = GetRawCardType(cardid);
507  if ("DVB" != type)
508  return type;
509 
510  QString device = GetVideoDevice(cardid);
511 
512  if (device.isEmpty())
513  return "ERROR_OPEN";
514 
515  return ProbeDVBType(device);
516 }
517 
519 bool CardUtil::IsDVBCardType(const QString &card_type)
520 {
521  QString ct = card_type.toUpper();
522  return (ct == "DVB") || (ct == "QAM") || (ct == "QPSK") ||
523  (ct == "OFDM") || (ct == "ATSC") || (ct == "DVB_S2");
524 }
525 
526 QString get_on_cardid(const QString &to_get, uint cardid)
527 {
528  MSqlQuery query(MSqlQuery::InitCon());
529  query.prepare(
530  QString("SELECT %1 ").arg(to_get) +
531  "FROM capturecard "
532  "WHERE capturecard.cardid = :CARDID");
533  query.bindValue(":CARDID", cardid);
534 
535  if (!query.exec())
536  MythDB::DBError("CardUtil::get_on_source", query);
537  else if (query.next())
538  return query.value(0).toString();
539 
540  return QString::null;
541 }
542 
543 bool set_on_source(const QString &to_set, uint cardid, uint sourceid,
544  const QString &value)
545 {
546  QString tmp = get_on_cardid("capturecard.cardid", cardid);
547  if (tmp.isEmpty())
548  return false;
549 
550  bool ok;
551  uint input_cardid = tmp.toUInt(&ok);
552  if (!ok)
553  return false;
554 
555  MSqlQuery query(MSqlQuery::InitCon());
556  query.prepare(
557  QString("UPDATE capturecard SET %1 = :VALUE ").arg(to_set) +
558  "WHERE cardid = :CARDID");
559  query.bindValue(":CARDID", input_cardid);
560  query.bindValue(":VALUE", value);
561 
562  if (query.exec())
563  return true;
564 
565  MythDB::DBError("CardUtil::set_on_source", query);
566  return false;
567 }
568 
569 QString get_on_inputid(const QString &to_get, uint inputid)
570 {
571  MSqlQuery query(MSqlQuery::InitCon());
572  query.prepare(
573  QString("SELECT %1 ").arg(to_get) +
574  "FROM cardinput "
575  "WHERE cardinput.cardinputid = :INPUTID");
576  query.bindValue(":INPUTID", inputid);
577 
578  if (!query.exec())
579  MythDB::DBError("CardUtil::get_on_inputid", query);
580  else if (query.next())
581  return query.value(0).toString();
582 
583  return QString::null;
584 }
585 
586 bool set_on_input(const QString &to_set, uint inputid, const QString &value)
587 {
588  QString tmp = get_on_inputid("cardinput.cardinputid", inputid);
589  if (tmp.isEmpty())
590  return false;
591 
592  bool ok;
593  uint input_cardinputid = tmp.toUInt(&ok);
594  if (!ok)
595  return false;
596 
597  MSqlQuery query(MSqlQuery::InitCon());
598  query.prepare(
599  QString("UPDATE cardinput SET %1 = :VALUE ").arg(to_set) +
600  "WHERE cardinputid = :INPUTID");
601  query.bindValue(":INPUTID", input_cardinputid);
602  query.bindValue(":VALUE", value);
603 
604  if (query.exec())
605  return true;
606 
607  MythDB::DBError("CardUtil::set_on_input", query);
608  return false;
609 }
610 
620 vector<uint> CardUtil::GetCardIDs(QString videodevice,
621  QString rawtype,
622  QString hostname)
623 {
624  vector<uint> list;
625 
626  if (hostname.isEmpty())
627  hostname = gCoreContext->GetHostName();
628 
629  MSqlQuery query(MSqlQuery::InitCon());
630  QString qstr =
631  (videodevice.isEmpty()) ?
632  "SELECT cardid "
633  "FROM capturecard "
634  "WHERE hostname = :HOSTNAME" :
635 
636  "SELECT cardid "
637  "FROM capturecard "
638  "WHERE videodevice = :DEVICE AND "
639  " hostname = :HOSTNAME";
640 
641  if (!rawtype.isEmpty())
642  qstr += " AND cardtype = :CARDTYPE";
643 
644  qstr += " ORDER BY cardid";
645 
646  query.prepare(qstr);
647 
648  if (!videodevice.isEmpty())
649  query.bindValue(":DEVICE", videodevice);
650 
651  query.bindValue(":HOSTNAME", hostname);
652 
653  if (!rawtype.isEmpty())
654  query.bindValue(":CARDTYPE", rawtype.toUpper());
655 
656  if (!query.exec())
657  MythDB::DBError("CardUtil::GetCardIDs(videodevice...)", query);
658  else
659  {
660  while (query.next())
661  list.push_back(query.value(0).toUInt());
662  }
663 
664  return list;
665 }
666 
667 static uint clone_capturecard(uint src_cardid, uint orig_dst_cardid)
668 {
669  uint dst_cardid = orig_dst_cardid;
670 
671  MSqlQuery query(MSqlQuery::InitCon());
672  if (!dst_cardid)
673  {
674  query.prepare(
675  "DELETE FROM capturecard "
676  "WHERE videodevice = 'temp_dummy'");
677 
678  if (!query.exec())
679  {
680  MythDB::DBError("clone_capturecard -- delete temp", query);
681  return 0;
682  }
683 
684  query.prepare(
685  "INSERT INTO capturecard "
686  "SET videodevice = 'temp_dummy'");
687 
688  if (!query.exec())
689  {
690  MythDB::DBError("clone_capturecard -- insert temp", query);
691  return 0;
692  }
693 
694  query.prepare(
695  "SELECT cardid "
696  "FROM capturecard "
697  "WHERE videodevice = 'temp_dummy'");
698 
699  if (!query.exec())
700  {
701  MythDB::DBError("clone_capturecard -- get temp id", query);
702  return 0;
703  }
704 
705  if (!query.next())
706  {
707  LOG(VB_GENERAL, LOG_ERR, "clone_capturecard -- get temp id");
708  return 0;
709  }
710 
711  dst_cardid = query.value(0).toUInt();
712  }
713 
714  query.prepare(
715  "SELECT videodevice, cardtype, "
716  " hostname, signal_timeout, channel_timeout, "
717  " dvb_wait_for_seqstart, dvb_on_demand, dvb_tuning_delay, "
718  " dvb_diseqc_type, diseqcid, dvb_eitscan "
719  "FROM capturecard "
720  "WHERE cardid = :CARDID");
721  query.bindValue(":CARDID", src_cardid);
722 
723  if (!query.exec())
724  {
725  MythDB::DBError("clone_capturecard -- get data", query);
726  return 0;
727  }
728  if (!query.next())
729  {
730  LOG(VB_GENERAL, LOG_ERR, "clone_cardinput -- get data 2");
731  return 0;
732  }
733 
734  MSqlQuery query2(MSqlQuery::InitCon());
735  query2.prepare(
736  "UPDATE capturecard "
737  "SET videodevice = :V0, "
738  " cardtype = :V1, "
739  " hostname = :V2, "
740  " signal_timeout = :V3, "
741  " channel_timeout = :V4, "
742  " dvb_wait_for_seqstart = :V5, "
743  " dvb_on_demand = :V6, "
744  " dvb_tuning_delay = :V7, "
745  " dvb_diseqc_type = :V8, "
746  " diseqcid = :V9,"
747  " dvb_eitscan = :V10 "
748  "WHERE cardid = :CARDID");
749  for (uint i = 0; i < 11; i++)
750  query2.bindValue(QString(":V%1").arg(i), query.value(i).toString());
751  query2.bindValue(":CARDID", dst_cardid);
752 
753  if (!query2.exec())
754  {
755  MythDB::DBError("clone_capturecard -- save data", query2);
756  if (!orig_dst_cardid)
757  CardUtil::DeleteCard(dst_cardid);
758  return 0;
759  }
760 
761  return dst_cardid;
762 }
763 
764 static bool clone_cardinputs(uint src_cardid, uint dst_cardid)
765 {
766  vector<uint> src_inputs = CardUtil::GetInputIDs(src_cardid);
767  vector<uint> dst_inputs = CardUtil::GetInputIDs(dst_cardid);
768  vector<QString> src_names;
769  vector<QString> dst_names;
770  QMap<uint,bool> dst_keep;
771 
772  for (uint i = 0; i < src_inputs.size(); i++)
773  src_names.push_back(CardUtil::GetInputName(src_inputs[i]));
774 
775  for (uint i = 0; i < dst_inputs.size(); i++)
776  dst_names.push_back(CardUtil::GetInputName(dst_inputs[i]));
777 
778  bool ok = true;
779 
780  MSqlQuery query(MSqlQuery::InitCon());
781  MSqlQuery query2(MSqlQuery::InitCon());
782 
783  for (uint i = 0; i < src_inputs.size(); i++)
784  {
785  query.prepare(
786  "SELECT sourceid, inputname, externalcommand, "
787  " tunechan, startchan, displayname, "
788  " dishnet_eit, recpriority, quicktune, "
789  " schedorder, livetvorder "
790  "FROM cardinput "
791  "WHERE cardinputid = :INPUTID");
792  query.bindValue(":INPUTID", src_inputs[i]);
793  if (!query.exec())
794  {
795  MythDB::DBError("clone_cardinput -- get data", query);
796  ok = false;
797  break;
798  }
799  if (!query.next())
800  {
801  LOG(VB_GENERAL, LOG_ERR, "clone_cardinput -- get data 2");
802  ok = false;
803  break;
804  }
805 
806  int match = -1;
807  for (uint j = 0; j < dst_inputs.size(); j++)
808  {
809  if (src_names[i] == dst_names[j])
810  {
811  match = (int) j;
812  break;
813  }
814  }
815 
816  uint dst_inputid = 0;
817  if (match >= 0)
818  {
819  dst_keep[match] = true;
820 
821  // copy data from src[i] to dst[match]
822  query2.prepare(
823  "UPDATE cardinput "
824  "SET sourceid = :V0, "
825  " inputname = :V1, "
826  " externalcommand = :V2, "
827  " tunechan = :V3, "
828  " startchan = :V4, "
829  " displayname = :V5, "
830  " dishnet_eit = :V6, "
831  " recpriority = :V7, "
832  " quicktune = :V8, "
833  " schedorder = :V9, "
834  " livetvorder = :V10 "
835  "WHERE cardinputid = :INPUTID");
836 
837  for (uint j = 0; j < 11; j++)
838  {
839  query2.bindValue(QString(":V%1").arg(j),
840  query.value(j).toString());
841  }
842  query2.bindValue(":INPUTID", dst_inputs[match]);
843 
844  if (!query2.exec())
845  {
846  MythDB::DBError("clone_cardinput -- update data", query2);
847  ok = false;
848  break;
849  }
850 
851  dst_inputid = dst_inputs[match];
852  }
853  else
854  {
855  // create new input for dst with data from src
856 
857  query2.prepare(
858  "INSERT cardinput "
859  "SET cardid = :CARDID, "
860  " sourceid = :V0, "
861  " inputname = :V1, "
862  " externalcommand = :V2, "
863  " tunechan = :V3, "
864  " startchan = :V4, "
865  " displayname = :V5, "
866  " dishnet_eit = :V6, "
867  " recpriority = :V7, "
868  " quicktune = :V8, "
869  " schedorder = :V9, "
870  " livetvorder = :V10 ");
871 
872  query2.bindValue(":CARDID", dst_cardid);
873  for (uint j = 0; j < 11; j++)
874  {
875  query2.bindValue(QString(":V%1").arg(j),
876  query.value(j).toString());
877  }
878 
879  if (!query2.exec())
880  {
881  MythDB::DBError("clone_cardinput -- insert data", query2);
882  ok = false;
883  break;
884  }
885 
886  query2.prepare(
887  "SELECT cardinputid "
888  "FROM cardinput "
889  "WHERE cardid = :CARDID AND "
890  " inputname = :NAME");
891  query2.bindValue(":CARDID", dst_cardid);
892  query2.bindValue(":NAME", query.value(1).toString());
893  if (!query2.exec())
894  {
895  MythDB::DBError("clone_cardinput -- "
896  "insert, query inputid", query2);
897  ok = false;
898  break;
899  }
900  if (!query2.next())
901  {
902  LOG(VB_GENERAL, LOG_ERR, "clone_cardinput -- insert failed");
903  ok = false;
904  break;
905  }
906 
907  dst_inputid = query2.value(0).toUInt();
908  }
909 
910  // copy input group linkages
911  vector<uint> src_grps = CardUtil::GetInputGroups(src_inputs[i]);
912  vector<uint> dst_grps = CardUtil::GetInputGroups(dst_inputid);
913  for (uint j = 0; j < dst_grps.size(); j++)
914  CardUtil::UnlinkInputGroup(dst_inputid, dst_grps[j]);
915  for (uint j = 0; j < src_grps.size(); j++)
916  CardUtil::LinkInputGroup(dst_inputid, src_grps[j]);
917 
918  // clone diseqc_config (just points to the same diseqc_tree row)
919  DiSEqCDevSettings diseqc;
920  if (diseqc.Load(src_inputs[i]))
921  diseqc.Store(dst_inputid);
922  }
923 
924  // delete extra inputs in dst
925  for (uint i = 0; i < dst_inputs.size(); i++)
926  {
927  if (!dst_keep[i])
928  ok &= CardUtil::DeleteInput(dst_inputs[i]);
929  }
930 
931  return ok;
932 }
933 
934 bool CardUtil::CloneCard(uint src_cardid, uint orig_dst_cardid)
935 {
936  QString type = CardUtil::GetRawCardType(src_cardid);
937  if (!IsTunerSharingCapable(type))
938  return false;
939 
940  uint dst_cardid = clone_capturecard(src_cardid, orig_dst_cardid);
941  if (!dst_cardid)
942  return false;
943 
944  if (!clone_cardinputs(src_cardid, dst_cardid) && !orig_dst_cardid)
945  {
946  DeleteCard(dst_cardid);
947  return false;
948  }
949 
950  return true;
951 }
952 
953 vector<uint> CardUtil::GetCloneCardIDs(uint cardid)
954 {
955  vector<uint> list;
956  MSqlQuery query(MSqlQuery::InitCon());
957  query.prepare(
958  "SELECT cardtype, videodevice, hostname "
959  "FROM capturecard "
960  "WHERE cardid = :CARDID");
961  query.bindValue(":CARDID", cardid);
962 
963  if (!query.exec())
964  {
965  MythDB::DBError("CardUtil::GetCloneCardIDs() 1", query);
966  return list;
967  }
968 
969  if (!query.next())
970  return list;
971 
972  QString rawtype = query.value(0).toString();
973  QString videodevice = query.value(1).toString();
974  QString hostname = query.value(2).toString();
975 
976  if (!IsTunerSharingCapable(rawtype))
977  return list;
978 
979  query.prepare(
980  "SELECT cardid "
981  "FROM capturecard "
982  "WHERE cardid != :CARDID AND "
983  " videodevice = :DEVICE AND "
984  " cardtype = :TYPE AND "
985  " hostname = :HOSTNAME");
986  query.bindValue(":CARDID", cardid);
987  query.bindValue(":DEVICE", videodevice);
988  query.bindValue(":TYPE", rawtype);
989  query.bindValue(":HOSTNAME", hostname);
990 
991  if (!query.exec())
992  {
993  MythDB::DBError("CardUtil::GetCloneCardIDs() 2", query);
994  return list;
995  }
996 
997  while (query.next())
998  list.push_back(query.value(0).toUInt());
999 
1000  return list;
1001 }
1002 
1004 {
1005  QString fwnode;
1006 
1007  MSqlQuery query(MSqlQuery::InitCon());
1008  query.prepare("SELECT changer_device "
1009  "FROM cardinput WHERE cardinputid = :INPUTID ");
1010  query.bindValue(":CARDID", inputid);
1011 
1012  if (query.exec() && query.next())
1013  {
1014  fwnode = query.value(0).toString();
1015  }
1016 
1017  return fwnode;
1018 }
1019 
1021 {
1022  QString fwnode;
1023 
1024  MSqlQuery query(MSqlQuery::InitCon());
1025  query.prepare("SELECT changer_model "
1026  "FROM cardinput WHERE cardinputid = :INPUTID ");
1027  query.bindValue(":CARDID", inputid);
1028 
1029  if (query.exec() && query.next())
1030  {
1031  fwnode = query.value(0).toString();
1032  }
1033 
1034  return fwnode;
1035 }
1036 
1037 vector<uint> CardUtil::GetCardIDs(uint sourceid)
1038 {
1039  MSqlQuery query(MSqlQuery::InitCon());
1040 
1041  query.prepare(
1042  "SELECT DISTINCT cardid "
1043  "FROM cardinput "
1044  "WHERE sourceid = :SOURCEID");
1045  query.bindValue(":SOURCEID", sourceid);
1046 
1047  vector<uint> list;
1048 
1049  if (!query.exec())
1050  {
1051  MythDB::DBError("CardUtil::GetCardIDs()", query);
1052  return list;
1053  }
1054 
1055  while (query.next())
1056  list.push_back(query.value(0).toUInt());
1057 
1058  return list;
1059 }
1060 
1062  uint cardid, const QString &channum, QString &inputname)
1063 {
1064  MSqlQuery query(MSqlQuery::InitCon());
1065  query.prepare(
1066  "SELECT cardinputid, inputname "
1067  "FROM channel, capturecard, cardinput "
1068  "WHERE channel.channum = :CHANNUM AND "
1069  " channel.sourceid = cardinput.sourceid AND "
1070  " cardinput.cardid = capturecard.cardid AND "
1071  " capturecard.cardid = :CARDID");
1072  query.bindValue(":CHANNUM", channum);
1073  query.bindValue(":CARDID", cardid);
1074 
1075  if (!query.exec() || !query.isActive())
1076  MythDB::DBError("get_cardinputid", query);
1077  else if (query.next())
1078  {
1079  inputname = query.value(1).toString();
1080  return query.value(0).toInt();
1081  }
1082 
1083  return -1;
1084 }
1085 
1086 bool CardUtil::SetStartChannel(uint cardinputid, const QString &channum)
1087 {
1088  MSqlQuery query(MSqlQuery::InitCon());
1089  query.prepare("UPDATE cardinput "
1090  "SET startchan = :CHANNUM "
1091  "WHERE cardinputid = :INPUTID");
1092  query.bindValue(":CHANNUM", channum);
1093  query.bindValue(":INPUTID", cardinputid);
1094 
1095  if (!query.exec())
1096  {
1097  MythDB::DBError("set_startchan", query);
1098  return false;
1099  }
1100 
1101  return true;
1102 }
1103 
1110 {
1111  QString str = QString::null;
1112  MSqlQuery query(MSqlQuery::InitCon());
1113  query.prepare("SELECT inputname "
1114  "FROM cardinput "
1115  "WHERE cardinput.cardid = :CARDID "
1116  "ORDER BY livetvorder = 0, livetvorder, cardinputid "
1117  "LIMIT 1");
1118  query.bindValue(":CARDID", nCardID);
1119 
1120  if (!query.exec() || !query.isActive())
1121  MythDB::DBError("CardUtil::GetStartInput()", query);
1122  else if (query.next())
1123  str = query.value(0).toString();
1124 
1125  return str;
1126 }
1127 
1128 QStringList CardUtil::GetInputNames(uint cardid, uint sourceid)
1129 {
1130  QStringList list;
1131  MSqlQuery query(MSqlQuery::InitCon());
1132 
1133  if (sourceid)
1134  {
1135  query.prepare("SELECT inputname "
1136  "FROM cardinput "
1137  "WHERE sourceid = :SOURCEID AND "
1138  " cardid = :CARDID");
1139  query.bindValue(":SOURCEID", sourceid);
1140  }
1141  else
1142  {
1143  query.prepare("SELECT inputname "
1144  "FROM cardinput "
1145  "WHERE cardid = :CARDID");
1146  }
1147  query.bindValue(":CARDID", cardid);
1148 
1149  if (!query.exec())
1150  {
1151  MythDB::DBError("CardUtil::GetInputNames()", query);
1152  }
1153  else
1154  {
1155  while (query.next())
1156  list.append( query.value(0).toString() );
1157  }
1158 
1159  return list;
1160 }
1161 
1162 bool CardUtil::GetInputInfo(InputInfo &input, vector<uint> *groupids)
1163 {
1164  if (!input.inputid)
1165  return false;
1166 
1167  MSqlQuery query(MSqlQuery::InitCon());
1168  query.prepare("SELECT "
1169  "inputname, sourceid, cardid, livetvorder, "
1170  "schedorder, displayname, recpriority, quicktune "
1171  "FROM cardinput "
1172  "WHERE cardinputid = :INPUTID");
1173  query.bindValue(":INPUTID", input.inputid);
1174 
1175  if (!query.exec())
1176  {
1177  MythDB::DBError("CardUtil::GetInputInfo()", query);
1178  return false;
1179  }
1180 
1181  if (!query.next())
1182  return false;
1183 
1184  input.name = query.value(0).toString();
1185  input.sourceid = query.value(1).toUInt();
1186  input.cardid = query.value(2).toUInt();
1187  input.livetvorder = query.value(3).toUInt();
1188  input.scheduleOrder = query.value(4).toUInt();
1189  input.displayName = query.value(5).toString();
1190  input.recPriority = query.value(6).toInt();
1191  input.quickTune = query.value(7).toBool();
1192 
1193  if (groupids)
1194  *groupids = GetInputGroups(input.inputid);
1195 
1196  return true;
1197 }
1198 
1199 QList<InputInfo> CardUtil::GetAllInputInfo()
1200 {
1201  QList<InputInfo> infoInputList;
1202 
1203  MSqlQuery query(MSqlQuery::InitCon());
1204  query.prepare("SELECT cardinputid, "
1205  "inputname, sourceid, cardid, livetvorder, "
1206  "schedorder, displayname, recpriority, quicktune "
1207  "FROM cardinput");
1208 
1209  if (!query.exec())
1210  {
1211  MythDB::DBError("CardUtil::GetAllInputInfo()", query);
1212  return infoInputList;
1213  }
1214 
1215  while (query.next())
1216  {
1217  InputInfo input;
1218  input.inputid = query.value(0).toUInt();
1219  input.name = query.value(1).toString();
1220  input.sourceid = query.value(2).toUInt();
1221  input.cardid = query.value(3).toUInt();
1222  input.livetvorder = query.value(4).toUInt();
1223  input.scheduleOrder = query.value(5).toUInt();
1224  input.displayName = query.value(6).toString();
1225  input.recPriority = query.value(7).toInt();
1226  input.quickTune = query.value(8).toBool();
1227 
1228  infoInputList.push_back(input);
1229  }
1230 
1231  return infoInputList;
1232 }
1233 
1235 {
1236  InputInfo info(QString::null, 0, inputid, 0, 0, 0);
1237  GetInputInfo(info);
1238  return info.cardid;
1239 }
1240 
1242 {
1243  InputInfo info(QString::null, 0, inputid, 0, 0, 0);
1244  GetInputInfo(info);
1245  return info.name;
1246 }
1247 
1249 {
1250  MSqlQuery query(MSqlQuery::InitCon());
1251  query.prepare("SELECT startchan "
1252  "FROM cardinput "
1253  "WHERE cardinputid = :INPUTID");
1254  query.bindValue(":INPUTID", inputid);
1255 
1256  if (!query.exec())
1257  MythDB::DBError("CardUtil::GetStartingChannel(uint)", query);
1258  else if (query.next())
1259  return query.value(0).toString();
1260 
1261  return QString::null;
1262 }
1263 
1265 {
1266  if (!inputid)
1267  return QString::null;
1268 
1269  MSqlQuery query(MSqlQuery::InitCon());
1270  query.prepare("SELECT displayname, cardid, inputname "
1271  "FROM cardinput "
1272  "WHERE cardinputid = :INPUTID");
1273  query.bindValue(":INPUTID", inputid);
1274 
1275  if (!query.exec())
1276  MythDB::DBError("CardUtil::GetDisplayName(uint)", query);
1277  else if (query.next())
1278  {
1279  QString result = query.value(0).toString();
1280  if (result.isEmpty())
1281  result = QString("%1: %2").arg(query.value(1).toInt())
1282  .arg(query.value(2).toString());
1283  return result;
1284  }
1285 
1286  return QString::null;
1287 }
1288 
1289 uint CardUtil::GetInputID(uint cardid, const QString &inputname)
1290 {
1291  MSqlQuery query(MSqlQuery::InitCon());
1292  query.prepare("SELECT cardinputid "
1293  "FROM cardinput "
1294  "WHERE inputname = :INPUTNAME AND "
1295  " cardid = :CARDID");
1296  query.bindValue(":INPUTNAME", inputname);
1297  query.bindValue(":CARDID", cardid);
1298 
1299  if (!query.exec())
1300  MythDB::DBError("CardUtil::GetInputID(uint,QString)", query);
1301  else if (query.next())
1302  return query.value(0).toUInt();
1303 
1304  return 0;
1305 }
1306 
1308 {
1309  MSqlQuery query(MSqlQuery::InitCon());
1310  query.prepare("SELECT cardinputid "
1311  "FROM cardinput "
1312  "WHERE sourceid = :SOURCEID AND "
1313  " cardid = :CARDID");
1314  query.bindValue(":SOURCEID", sourceid);
1315  query.bindValue(":CARDID", cardid);
1316 
1317  if (!query.exec())
1318  MythDB::DBError("CardUtil::GetInputID(uint,uint)", query);
1319  else if (query.next())
1320  return query.value(0).toUInt();
1321 
1322  return 0;
1323 }
1324 
1326 {
1327  MSqlQuery query(MSqlQuery::InitCon());
1328  query.prepare(
1329  "SELECT sourceid "
1330  "FROM cardinput "
1331  "WHERE cardinputid = :INPUTID");
1332  query.bindValue(":INPUTID", inputid);
1333  if (!query.exec() || !query.isActive())
1334  MythDB::DBError("CardUtil::GetSourceID()", query);
1335  else if (query.next())
1336  return query.value(0).toUInt();
1337 
1338  return 0;
1339 }
1340 
1341 vector<uint> CardUtil::GetAllInputIDs(void)
1342 {
1343  vector<uint> list;
1344 
1345  MSqlQuery query(MSqlQuery::InitCon());
1346  query.prepare(
1347  "SELECT cardinputid "
1348  "FROM cardinput");
1349 
1350  if (!query.exec())
1351  {
1352  MythDB::DBError("CardUtil::GetAllInputIDs(uint)", query);
1353  return list;
1354  }
1355 
1356  while (query.next())
1357  list.push_back(query.value(0).toUInt());
1358 
1359  return list;
1360 }
1361 
1362 vector<uint> CardUtil::GetInputIDs(uint cardid)
1363 {
1364  vector<uint> list;
1365 
1366  MSqlQuery query(MSqlQuery::InitCon());
1367  query.prepare(
1368  "SELECT cardinputid "
1369  "FROM cardinput "
1370  "WHERE cardid = :CARDID");
1371 
1372  query.bindValue(":CARDID", cardid);
1373 
1374  if (!query.exec())
1375  {
1376  MythDB::DBError("CardUtil::GetInputIDs(uint)", query);
1377  return list;
1378  }
1379 
1380  while (query.next())
1381  list.push_back(query.value(0).toUInt());
1382 
1383  return list;
1384 }
1385 
1387  const uint sourceid,
1388  const QString &inputname,
1389  const QString &externalcommand,
1390  const QString &changer_device,
1391  const QString &changer_model,
1392  const QString &hostname,
1393  const QString &tunechan,
1394  const QString &startchan,
1395  const QString &displayname,
1396  bool dishnet_eit,
1397  const uint recpriority,
1398  const uint quicktune,
1399  const uint schedorder,
1400  const uint livetvorder)
1401 {
1402  MSqlQuery query(MSqlQuery::InitCon());
1403 
1404  query.prepare(
1405  "INSERT INTO cardinput "
1406  "(cardid, sourceid, inputname, externalcommand, changer_device, "
1407  "changer_model, tunechan, startchan, displayname, dishnet_eit, "
1408  "recpriority, quicktune, schedorder, livetvorder) "
1409  "VALUES (:CARDID, :SOURCEID, :INPUTNAME, :EXTERNALCOMMAND, "
1410  ":CHANGERDEVICE, :CHANGERMODEL, :TUNECHAN, :STARTCHAN, :DISPLAYNAME, "
1411  ":DISHNETEIT, :RECPRIORITY, :QUICKTUNE, :SCHEDORDER, :LIVETVORDER ) ");
1412 
1413  query.bindValue(":CARDID", cardid);
1414  query.bindValue(":SOURCEID", sourceid);
1415  query.bindValue(":INPUTNAME", inputname);
1416  query.bindValue(":EXTERNALCOMMAND", externalcommand);
1417  query.bindValue(":CHANGERDEVICE", changer_device);
1418  query.bindValue(":CHANGERMODEL", changer_model);
1419  query.bindValue(":TUNECHAN", tunechan);
1420  query.bindValue(":STARTCHAN", startchan);
1421  query.bindValue(":DISPLAYNAME", displayname.isNull() ? "" : displayname);
1422  query.bindValue(":DISHNETEIT", dishnet_eit);
1423  query.bindValue(":RECPRIORITY", recpriority);
1424  query.bindValue(":QUICKTUNE", quicktune);
1425  query.bindValue(":SCHEDORDER", schedorder);
1426  query.bindValue(":LIVETVORDER", livetvorder);
1427 
1428  if (!query.exec())
1429  {
1430  MythDB::DBError("CreateCardInput", query);
1431  return -1;
1432  }
1433 
1434  query.prepare("SELECT MAX(cardinputid) FROM cardinput");
1435 
1436  if (!query.exec())
1437  {
1438  MythDB::DBError("CreateCardInput maxinput", query);
1439  return -1;
1440  }
1441 
1442  int inputid = -1; /* must be int not uint because of return type. */
1443 
1444  if (query.next())
1445  inputid = query.value(0).toInt();
1446 
1447  return inputid;
1448 }
1449 
1451 {
1452  MSqlQuery query(MSqlQuery::InitCon());
1453  query.prepare(
1454  "DELETE FROM cardinput "
1455  "WHERE cardinputid = :INPUTID");
1456  query.bindValue(":INPUTID", inputid);
1457 
1458  if (!query.exec())
1459  {
1460  MythDB::DBError("DeleteInput", query);
1461  return false;
1462  }
1463 
1464  return true;
1465 }
1466 
1468 {
1469  MSqlQuery query(MSqlQuery::InitCon());
1470  query.prepare("SELECT cardinputid "
1471  "FROM cardinput "
1472  "LEFT JOIN capturecard "
1473  "ON (capturecard.cardid = cardinput.cardid) "
1474  "WHERE capturecard.cardid IS NULL");
1475  if (!query.exec())
1476  {
1477  MythDB::DBError("DeleteOrphanInputs -- query disconnects", query);
1478  return false;
1479  }
1480 
1481  bool ok = true;
1482  while (query.next())
1483  {
1484  uint inputid = query.value(0).toUInt();
1485  if (DeleteInput(inputid))
1486  {
1487  LOG(VB_GENERAL, LOG_NOTICE, QString("Removed orphan input %1")
1488  .arg(inputid));
1489  }
1490  else
1491  {
1492  ok = false;
1493  LOG(VB_GENERAL, LOG_ERR, QString("Failed to remove orphan input %1")
1494  .arg(inputid));
1495  }
1496  }
1497 
1498  return ok;
1499 }
1500 
1502 {
1503  MSqlQuery query(MSqlQuery::InitCon());
1504  query.prepare("SELECT MAX(inputgroupid) FROM inputgroup");
1505  if (!query.exec())
1506  {
1507  MythDB::DBError("CreateNewInputGroup 1", query);
1508  return 0;
1509  }
1510  uint inputgroupid = (query.next()) ? query.value(0).toUInt() + 1 : 1;
1511 
1512  query.prepare(
1513  "INSERT INTO inputgroup "
1514  " (cardinputid, inputgroupid, inputgroupname) "
1515  "VALUES (:INPUTID, :GROUPID, :GROUPNAME ) ");
1516 
1517  query.bindValue(":INPUTID", 0);
1518  query.bindValue(":GROUPID", inputgroupid);
1519  query.bindValue(":GROUPNAME", name);
1520 
1521  if (!query.exec())
1522  {
1523  MythDB::DBError("CreateNewInputGroup 2", query);
1524  return 0;
1525  }
1526 
1527  return inputgroupid;
1528 }
1529 
1531 {
1532  // Make sure the card's inputs are all in a single
1533  // input group, create one if needed.
1534  vector<uint> ingrps = CardUtil::GetSharedInputGroups(cardid);
1535  vector<uint> inputs = CardUtil::GetInputIDs(cardid);
1536 
1537  if (ingrps.empty() && !inputs.empty())
1538  {
1539  QString name = CardUtil::GetRawCardType(cardid) + "_" +
1540  CardUtil::GetVideoDevice(cardid);
1541  uint id = 0;
1542  for (uint i = 0; !id && (i < 100); i++)
1543  {
1544  if (i)
1545  name += QString(":%1").arg(i);
1546  id = CardUtil::CreateInputGroup(name);
1547  }
1548  if (!id)
1549  {
1550  LOG(VB_GENERAL, LOG_ERR, "Failed to create input group");
1551  return false;
1552  }
1553 
1554  bool ok = true;
1555  for (uint i = 0; i < inputs.size(); i++)
1556  ok &= CardUtil::LinkInputGroup(inputs[i], id);
1557 
1558  if (!ok)
1559  LOG(VB_GENERAL, LOG_ERR, "Failed to link to new input group");
1560 
1561  return ok;
1562  }
1563 
1564  return true;
1565 }
1566 
1567 bool CardUtil::LinkInputGroup(uint inputid, uint inputgroupid)
1568 {
1569  MSqlQuery query(MSqlQuery::InitCon());
1570 
1571  query.prepare(
1572  "SELECT cardinputid, inputgroupid, inputgroupname "
1573  "FROM inputgroup "
1574  "WHERE inputgroupid = :GROUPID "
1575  "ORDER BY inputgroupid, cardinputid, inputgroupname");
1576  query.bindValue(":GROUPID", inputgroupid);
1577 
1578  if (!query.exec())
1579  {
1580  MythDB::DBError("CardUtil::CreateInputGroup() 1", query);
1581  return false;
1582  }
1583 
1584  if (!query.next())
1585  return false;
1586 
1587  const QString name = query.value(2).toString();
1588 
1589  query.prepare(
1590  "INSERT INTO inputgroup "
1591  " (cardinputid, inputgroupid, inputgroupname) "
1592  "VALUES (:INPUTID, :GROUPID, :GROUPNAME ) ");
1593 
1594  query.bindValue(":INPUTID", inputid);
1595  query.bindValue(":GROUPID", inputgroupid);
1596  query.bindValue(":GROUPNAME", name);
1597 
1598  if (!query.exec())
1599  {
1600  MythDB::DBError("CardUtil::CreateInputGroup() 2", query);
1601  return false;
1602  }
1603 
1604  return true;
1605 }
1606 
1607 bool CardUtil::UnlinkInputGroup(uint inputid, uint inputgroupid)
1608 {
1609  MSqlQuery query(MSqlQuery::InitCon());
1610 
1611  if (!inputid && !inputgroupid)
1612  {
1613  query.prepare(
1614  "DELETE FROM inputgroup "
1615  "WHERE cardinputid NOT IN "
1616  "( SELECT cardinputid FROM cardinput )");
1617  }
1618  else
1619  {
1620  query.prepare(
1621  "DELETE FROM inputgroup "
1622  "WHERE cardinputid = :INPUTID AND "
1623  " inputgroupid = :GROUPID ");
1624 
1625  query.bindValue(":INPUTID", inputid);
1626  query.bindValue(":GROUPID", inputgroupid);
1627  }
1628 
1629  if (!query.exec())
1630  {
1631  MythDB::DBError("CardUtil::DeleteInputGroup()", query);
1632  return false;
1633  }
1634 
1635  return true;
1636 }
1637 
1638 vector<uint> CardUtil::GetInputGroups(uint inputid)
1639 {
1640  vector<uint> list;
1641 
1642  MSqlQuery query(MSqlQuery::InitCon());
1643 
1644  query.prepare(
1645  "SELECT inputgroupid "
1646  "FROM inputgroup "
1647  "WHERE cardinputid = :INPUTID "
1648  "ORDER BY inputgroupid, cardinputid, inputgroupname");
1649 
1650  query.bindValue(":INPUTID", inputid);
1651 
1652  if (!query.exec())
1653  {
1654  MythDB::DBError("CardUtil::GetInputGroups()", query);
1655  return list;
1656  }
1657 
1658  while (query.next())
1659  list.push_back(query.value(0).toUInt());
1660 
1661  return list;
1662 }
1663 
1665 {
1666  vector<uint> list;
1667 
1668  vector<uint> inputs = GetInputIDs(cardid);
1669  if (inputs.empty())
1670  return list;
1671 
1672  list = GetInputGroups(inputs[0]);
1673  for (uint i = 1; (i < inputs.size()) && !list.empty(); i++)
1674  {
1675  vector<uint> curlist = GetInputGroups(inputs[i]);
1676  vector<uint> newlist;
1677  for (uint j = 0; j < list.size(); j++)
1678  {
1679  if (find(curlist.begin(), curlist.end(), list[j]) != curlist.end())
1680  newlist.push_back(list[j]);
1681  }
1682  list = newlist;
1683  }
1684 
1685  return list;
1686 }
1687 
1688 vector<uint> CardUtil::GetGroupCardIDs(uint inputgroupid)
1689 {
1690  vector<uint> list;
1691 
1692  MSqlQuery query(MSqlQuery::InitCon());
1693 
1694  query.prepare(
1695  "SELECT DISTINCT cardid "
1696  "FROM cardinput, inputgroup "
1697  "WHERE inputgroupid = :GROUPID AND "
1698  " cardinput.cardinputid = inputgroup.cardinputid "
1699  "ORDER BY cardid");
1700 
1701  query.bindValue(":GROUPID", inputgroupid);
1702 
1703  if (!query.exec())
1704  {
1705  MythDB::DBError("CardUtil::GetGroupCardIDs()", query);
1706  return list;
1707  }
1708 
1709  while (query.next())
1710  list.push_back(query.value(0).toUInt());
1711 
1712  return list;
1713 }
1714 
1715 vector<uint> CardUtil::GetConflictingCards(uint inputid, uint exclude_cardid)
1716 {
1717  vector<uint> inputgroupids = CardUtil::GetInputGroups(inputid);
1718 
1719  for (uint i = 0; i < inputgroupids.size(); i++)
1720  {
1721  LOG(VB_RECORD, LOG_INFO, LOC + QString(" Group ID %1")
1722  .arg(inputgroupids[i]));
1723  }
1724 
1725  vector<uint> cardids;
1726  for (uint i = 0; i < inputgroupids.size(); i++)
1727  {
1728  vector<uint> tmp = CardUtil::GetGroupCardIDs(inputgroupids[i]);
1729  for (uint j = 0; j < tmp.size(); j++)
1730  {
1731  if (tmp[j] == exclude_cardid)
1732  continue;
1733 
1734  if (find(cardids.begin(), cardids.end(), tmp[j]) != cardids.end())
1735  continue;
1736 
1737  cardids.push_back(tmp[j]);
1738  }
1739  }
1740 
1741  for (uint i = 0; i < cardids.size(); i++)
1742  LOG(VB_RECORD, LOG_INFO, LOC + QString(" Card ID %1").arg(cardids[i]));
1743 
1744  return cardids;
1745 }
1746 
1748  uint &signal_timeout, uint &channel_timeout)
1749 {
1750  MSqlQuery query(MSqlQuery::InitCon());
1751  query.prepare(
1752  "SELECT signal_timeout, channel_timeout "
1753  "FROM capturecard "
1754  "WHERE cardid = :CARDID");
1755  query.bindValue(":CARDID", cardid);
1756 
1757  if (!query.exec() || !query.isActive())
1758  MythDB::DBError("CardUtil::GetTimeouts()", query);
1759  else if (query.next())
1760  {
1761  signal_timeout = (uint) max(query.value(0).toInt(), 250);
1762  channel_timeout = (uint) max(query.value(1).toInt(), 500);
1763  return true;
1764  }
1765 
1766  return false;
1767 }
1768 
1770 {
1771  DiSEqCDev dev;
1772  DiSEqCDevTree *diseqc_tree = dev.FindTree(cardid);
1773 
1774  bool needsConf = false;
1775  if (diseqc_tree)
1776  needsConf = diseqc_tree->IsInNeedOfConf();
1777 
1778  return needsConf;
1779 }
1780 
1781 uint CardUtil::GetQuickTuning(uint cardid, const QString &input_name)
1782 {
1783  uint quicktune = 0;
1784 
1785  MSqlQuery query(MSqlQuery::InitCon());
1786  query.prepare(
1787  "SELECT quicktune "
1788  "FROM cardinput "
1789  "WHERE cardid = :CARDID AND "
1790  " inputname = :INPUTNAME");
1791  query.bindValue(":CARDID", cardid);
1792  query.bindValue(":INPUTNAME", input_name);
1793 
1794  if (!query.exec() || !query.isActive())
1795  MythDB::DBError("CardUtil::GetQuickTuning()", query);
1796  else if (query.next())
1797  quicktune = query.value(0).toUInt();
1798 
1799  return quicktune;
1800 }
1801 
1802 bool CardUtil::hasV4L2(int videofd)
1803 {
1804  (void) videofd;
1805 #ifdef USING_V4L2
1806  struct v4l2_capability vcap;
1807  memset(&vcap, 0, sizeof(vcap));
1808 
1809  return ((ioctl(videofd, VIDIOC_QUERYCAP, &vcap) >= 0) &&
1810  (vcap.capabilities & V4L2_CAP_VIDEO_CAPTURE));
1811 #else // if !USING_V4L2
1812  return false;
1813 #endif // !USING_V4L2
1814 }
1815 
1817  int videofd, QString &card, QString &driver, uint32_t &version,
1818  uint32_t &capabilities)
1819 {
1820  card = driver = QString::null;
1821  version = 0;
1822  capabilities = 0;
1823 
1824  if (videofd < 0)
1825  return false;
1826 
1827 #ifdef USING_V4L2
1828  // First try V4L2 query
1829  struct v4l2_capability capability;
1830  memset(&capability, 0, sizeof(struct v4l2_capability));
1831  if (ioctl(videofd, VIDIOC_QUERYCAP, &capability) >= 0)
1832  {
1833  card = QString::fromAscii((const char*)capability.card);
1834  driver = QString::fromAscii((const char*)capability.driver);
1835  version = capability.version;
1836  capabilities = capability.capabilities;
1837  }
1838 #ifdef USING_V4L1
1839  else // Fallback to V4L1 query
1840  {
1841  struct video_capability capability;
1842  if (ioctl(videofd, VIDIOCGCAP, &capability) >= 0)
1843  card = QString::fromAscii((const char*)capability.name);
1844  }
1845 #endif // USING_V4L1
1846 #endif // USING_V4L2
1847 
1848  if (!driver.isEmpty())
1849  driver.remove( QRegExp("\\[[0-9]\\]$") );
1850 
1851  return !card.isEmpty();
1852 }
1853 
1855 {
1856  (void) videofd;
1857 
1858  InputNames list;
1859  ok = false;
1860 
1861 #ifdef USING_V4L2
1862  bool usingv4l2 = hasV4L2(videofd);
1863 
1864  // V4L v2 query
1865  struct v4l2_input vin;
1866  memset(&vin, 0, sizeof(vin));
1867  while (usingv4l2 && (ioctl(videofd, VIDIOC_ENUMINPUT, &vin) >= 0))
1868  {
1869  QString input((char *)vin.name);
1870  list[vin.index] = input;
1871  vin.index++;
1872  }
1873  if (vin.index)
1874  {
1875  ok = true;
1876  return list;
1877  }
1878 
1879 #ifdef USING_V4L1
1880  // V4L v1 query
1881  struct video_capability vidcap;
1882  memset(&vidcap, 0, sizeof(vidcap));
1883  if (ioctl(videofd, VIDIOCGCAP, &vidcap) != 0)
1884  {
1885  QString msg = QObject::tr("Could not query inputs.");
1886  LOG(VB_GENERAL, LOG_ERR, "ProbeV4LVideoInputs(): Error, " + msg + ENO);
1887  list[-1] = msg;
1888  vidcap.channels = 0;
1889  }
1890 
1891  for (int i = 0; i < vidcap.channels; i++)
1892  {
1893  struct video_channel test;
1894  memset(&test, 0, sizeof(test));
1895  test.channel = i;
1896 
1897  if (ioctl(videofd, VIDIOCGCHAN, &test) != 0)
1898  {
1899  LOG(VB_GENERAL, LOG_ERR, "ProbeV4LVideoInputs(): Error, " +
1900  QString("Could determine name of input #%1"
1901  "\n\t\t\tNot adding it to the list.")
1902  .arg(test.channel) + ENO);
1903  continue;
1904  }
1905 
1906  list[i] = test.name;
1907  }
1908 #endif // USING_V4L1
1909 
1910  // Create an input on single input cards that don't advertise input
1911  if (list.isEmpty())
1912  list[0] = "Television";
1913 
1914  ok = true;
1915 #else // if !USING_V4L2
1916  list[-1] += QObject::tr("ERROR, Compile with V4L support to query inputs");
1917 #endif // !USING_V4L2
1918  return list;
1919 }
1920 
1922 {
1923  (void) videofd;
1924 
1925  InputNames list;
1926  ok = false;
1927 
1928 #ifdef USING_V4L2
1929  bool usingv4l2 = hasV4L2(videofd);
1930 
1931  // V4L v2 query
1932  struct v4l2_audio ain;
1933  memset(&ain, 0, sizeof(ain));
1934  while (usingv4l2 && (ioctl(videofd, VIDIOC_ENUMAUDIO, &ain) >= 0))
1935  {
1936  QString input((char *)ain.name);
1937  list[ain.index] = input;
1938  ain.index++;
1939  }
1940  if (ain.index)
1941  {
1942  ok = true;
1943  return list;
1944  }
1945 
1946  ok = true;
1947 #else // if !USING_V4L2
1948  list[-1] += QObject::tr(
1949  "ERROR, Compile with V4L support to query audio inputs");
1950 #endif // !USING_V4L2
1951  return list;
1952 }
1953 
1955 {
1956  InputNames list;
1957  MSqlQuery query(MSqlQuery::InitCon());
1958  query.prepare(
1959  "SELECT cardinputid, inputname "
1960  "FROM cardinput "
1961  "WHERE cardid = :CARDID");
1962  query.bindValue(":CARDID", cardid);
1963 
1964  if (!query.exec() || !query.isActive())
1965  MythDB::DBError("CardUtil::GetConfiguredDVBInputs", query);
1966  else
1967  {
1968  while (query.next())
1969  list[query.value(0).toUInt()] = query.value(1).toString();
1970  }
1971  return list;
1972 }
1973 
1974 QStringList CardUtil::ProbeVideoInputs(QString device, QString cardtype)
1975 {
1976  QStringList ret;
1977 
1978  if (IsSingleInputCard(cardtype))
1979  ret += "MPEG2TS";
1980  else if ("DVB" == cardtype)
1981  ret += ProbeDVBInputs(device);
1982  else
1983  ret += ProbeV4LVideoInputs(device);
1984 
1985  return ret;
1986 }
1987 
1988 QStringList CardUtil::ProbeAudioInputs(QString device, QString cardtype)
1989 {
1990  LOG(VB_GENERAL, LOG_DEBUG, QString("ProbeAudioInputs(%1,%2)")
1991  .arg(device).arg(cardtype));
1992  QStringList ret;
1993 
1994  if ("HDPVR" == cardtype)
1995  ret += ProbeV4LAudioInputs(device);
1996 
1997  return ret;
1998 }
1999 
2000 QStringList CardUtil::ProbeV4LVideoInputs(QString device)
2001 {
2002  bool ok;
2003  QStringList ret;
2004  QByteArray dev = device.toLatin1();
2005  int videofd = open(dev.constData(), O_RDWR);
2006  if (videofd < 0)
2007  {
2008  ret += QObject::tr("Could not open '%1' "
2009  "to probe its inputs.").arg(device);
2010  return ret;
2011  }
2012  InputNames list = CardUtil::ProbeV4LVideoInputs(videofd, ok);
2013  close(videofd);
2014 
2015  if (!ok)
2016  {
2017  ret += list[-1];
2018  return ret;
2019  }
2020 
2021  InputNames::iterator it;
2022  for (it = list.begin(); it != list.end(); ++it)
2023  {
2024  if (it.key() >= 0)
2025  ret += *it;
2026  }
2027 
2028  return ret;
2029 }
2030 
2031 QStringList CardUtil::ProbeV4LAudioInputs(QString device)
2032 {
2033  LOG(VB_GENERAL, LOG_DEBUG, QString("ProbeV4LAudioInputs(%1)").arg(device));
2034 
2035  bool ok;
2036  QStringList ret;
2037  int videofd = open(device.toLatin1().constData(), O_RDWR);
2038  if (videofd < 0)
2039  {
2040  LOG(VB_GENERAL, LOG_ERR, "ProbeAudioInputs() -> couldn't open device");
2041  ret += QObject::tr("Could not open '%1' to probe its inputs.")
2042  .arg(device);
2043  return ret;
2044  }
2045  InputNames list = CardUtil::ProbeV4LAudioInputs(videofd, ok);
2046  close(videofd);
2047 
2048  if (!ok)
2049  {
2050  ret += list[-1];
2051  return ret;
2052  }
2053 
2054  InputNames::iterator it;
2055  for (it = list.begin(); it != list.end(); ++it)
2056  {
2057  if (it.key() >= 0)
2058  ret += *it;
2059  }
2060 
2061  return ret;
2062 }
2063 
2064 QStringList CardUtil::ProbeDVBInputs(QString device)
2065 {
2066  QStringList ret;
2067 
2068 #ifdef USING_DVB
2069  uint cardid = CardUtil::GetFirstCardID(device);
2070  if (!cardid)
2071  return ret;
2072 
2073  InputNames list = GetConfiguredDVBInputs(cardid);
2074  InputNames::iterator it;
2075  for (it = list.begin(); it != list.end(); ++it)
2076  {
2077  if (it.key())
2078  ret += *it;
2079  }
2080 #else
2081  (void) device;
2082  ret += QObject::tr("ERROR, Compile with DVB support to query inputs");
2083 #endif
2084 
2085  return ret;
2086 }
2087 
2088 QString CardUtil::GetDeviceLabel(const QString &cardtype,
2089  const QString &videodevice)
2090 {
2091  return QString("[ %1 : %2 ]").arg(cardtype).arg(videodevice);
2092 }
2093 
2095 {
2096  QString devlabel;
2097  MSqlQuery query(MSqlQuery::InitCon());
2098  query.prepare("SELECT cardtype, videodevice "
2099  "FROM capturecard WHERE cardid = :CARDID ");
2100  query.bindValue(":CARDID", cardid);
2101 
2102  if (query.exec() && query.next())
2103  {
2104  return GetDeviceLabel(query.value(0).toString(),
2105  query.value(1).toString());
2106  }
2107 
2108  return "[ UNKNOWN ]";
2109 }
2110 
2112  uint cardid,
2113  const QString &device,
2114  const QString &cardtype,
2115  QStringList &inputLabels,
2116  vector<CardInput*> &cardInputs)
2117 {
2118  QStringList inputs;
2119 
2120  if (IsSingleInputCard(cardtype))
2121  inputs += "MPEG2TS";
2122  else if ("DVB" != cardtype)
2123  inputs += ProbeV4LVideoInputs(device);
2124 
2125  QString dev_label = GetDeviceLabel(cardtype, device);
2126 
2127  QStringList::iterator it = inputs.begin();
2128  for (; it != inputs.end(); ++it)
2129  {
2130  CardInput *cardinput = new CardInput(cardtype, false, cardid);
2131  cardinput->loadByInput(cardid, (*it));
2132  inputLabels.push_back(
2133  dev_label + QString(" (%1) -> %2")
2134  .arg(*it).arg(cardinput->getSourceName()));
2135  cardInputs.push_back(cardinput);
2136  }
2137 
2138 #ifdef USING_DVB
2139  if ("DVB" == cardtype)
2140  {
2141  bool needs_conf = IsInNeedOfExternalInputConf(cardid);
2142  InputNames list = GetConfiguredDVBInputs(cardid);
2143  if (!needs_conf && list.empty())
2144  list[0] = "DVBInput";
2145 
2146  InputNames::const_iterator it;
2147  for (it = list.begin(); it != list.end(); ++it)
2148  {
2149  CardInput *cardinput = new CardInput(cardtype, false, cardid);
2150  cardinput->loadByInput(cardid, *it);
2151  inputLabels.push_back(
2152  dev_label + QString(" (%1) -> %2")
2153  .arg(*it).arg(cardinput->getSourceName()));
2154  cardInputs.push_back(cardinput);
2155  }
2156 
2157  // plus add one "new" input
2158  if (needs_conf)
2159  {
2160  CardInput *newcard = new CardInput(cardtype, true, cardid);
2161  QString newname = QString("DVBInput #%1").arg(list.size() + 1);
2162  newcard->loadByInput(cardid, newname);
2163  inputLabels.push_back(dev_label + " " + QObject::tr("New Input"));
2164  cardInputs.push_back(newcard);
2165  }
2166  }
2167 #endif // USING_DVB
2168 }
2169 
2170 int CardUtil::CreateCaptureCard(const QString &videodevice,
2171  const QString &audiodevice,
2172  const QString &vbidevice,
2173  const QString &cardtype,
2174  const uint audioratelimit,
2175  const QString &hostname,
2176  const uint dvb_swfilter,
2177  const uint dvb_sat_type,
2178  bool dvb_wait_for_seqstart,
2179  bool skipbtaudio,
2180  bool dvb_on_demand,
2181  const uint dvb_diseqc_type,
2182  const uint firewire_speed,
2183  const QString &firewire_model,
2184  const uint firewire_connection,
2185  const uint signal_timeout,
2186  const uint channel_timeout,
2187  const uint dvb_tuning_delay,
2188  const uint contrast,
2189  const uint brightness,
2190  const uint colour,
2191  const uint hue,
2192  const uint diseqcid,
2193  bool dvb_eitscan)
2194 {
2195  MSqlQuery query(MSqlQuery::InitCon());
2196 
2197  query.prepare(
2198  "INSERT INTO capturecard "
2199  "(videodevice, audiodevice, vbidevice, cardtype, "
2200  "audioratelimit, hostname, dvb_swfilter, dvb_sat_type, "
2201  "dvb_wait_for_seqstart, skipbtaudio, dvb_on_demand, dvb_diseqc_type, "
2202  "firewire_speed, firewire_model, firewire_connection, signal_timeout, "
2203  "channel_timeout, dvb_tuning_delay, contrast, brightness, colour, "
2204  "hue, diseqcid, dvb_eitscan) "
2205  "VALUES (:VIDEODEVICE, :AUDIODEVICE, :VBIDEVICE, :CARDTYPE, "
2206  ":AUDIORATELIMIT, :HOSTNAME, :DVBSWFILTER, :DVBSATTYPE, "
2207  ":DVBWAITFORSEQSTART, :SKIPBTAUDIO, :DVBONDEMAND, :DVBDISEQCTYPE, "
2208  ":FIREWIRESPEED, :FIREWIREMODEL, :FIREWIRECONNECTION, :SIGNALTIMEOUT, "
2209  ":CHANNELTIMEOUT, :DVBTUNINGDELAY, :CONTRAST, :BRIGHTNESS, :COLOUR, "
2210  ":HUE, :DISEQCID, :DVBEITSCAN ) ");
2211 
2212  query.bindValue(":VIDEODEVICE", videodevice);
2213  query.bindValue(":AUDIODEVICE", audiodevice);
2214  query.bindValue(":VBIDEVICE", vbidevice);
2215  query.bindValue(":CARDTYPE", cardtype);
2216  query.bindValue(":AUDIORATELIMIT", audioratelimit);
2217  query.bindValue(":HOSTNAME", hostname);
2218  query.bindValue(":DVBSWFILTER", dvb_swfilter);
2219  query.bindValue(":DVBSATTYPE", dvb_sat_type);
2220  query.bindValue(":DVBWAITFORSEQSTART", dvb_wait_for_seqstart);
2221  query.bindValue(":SKIPBTAUDIO", skipbtaudio);
2222  query.bindValue(":DVBONDEMAND", dvb_on_demand);
2223  query.bindValue(":DVBDISEQCTYPE", dvb_diseqc_type);
2224  query.bindValue(":FIREWIRESPEED", firewire_speed);
2225  query.bindValue(":FIREWIREMODEL", firewire_model);
2226  query.bindValue(":FIREWIRECONNECTION", firewire_connection);
2227  query.bindValue(":SIGNALTIMEOUT", signal_timeout);
2228  query.bindValue(":CHANNELTIMEOUT", channel_timeout);
2229  query.bindValue(":DVBTUNINGDELAY", dvb_tuning_delay);
2230  query.bindValue(":CONTRAST", contrast);
2231  query.bindValue(":BRIGHTNESS", brightness);
2232  query.bindValue(":COLOUR", colour);
2233  query.bindValue(":HUE", hue);
2234  query.bindValue(":DISEQCID", diseqcid);
2235  query.bindValue(":DVBEITSCAN", dvb_eitscan);
2236 
2237  if (!query.exec())
2238  {
2239  MythDB::DBError("CreateCaptureCard", query);
2240  return -1;
2241  }
2242 
2243  query.prepare("SELECT MAX(cardid) FROM capturecard");
2244 
2245  if (!query.exec())
2246  {
2247  MythDB::DBError("CreateCaptureCard maxcard", query);
2248  return -1;
2249  }
2250 
2251  int cardid = -1; /* must be int not uint because of return type. */
2252 
2253  if (query.next())
2254  cardid = query.value(0).toInt();
2255 
2256  return cardid;
2257 }
2258 
2260 {
2261  MSqlQuery query(MSqlQuery::InitCon());
2262  bool ok = true;
2263 
2264  if (!cardid)
2265  return true;
2266 
2267  // delete any DiSEqC device tree
2268  DiSEqCDevTree tree;
2269  tree.Load(cardid);
2270  if (!tree.Root())
2271  {
2272  tree.SetRoot(NULL);
2273  tree.Store(cardid);
2274  }
2275 
2276  // delete any clones
2277  QString rawtype = GetRawCardType(cardid);
2278  QString videodevice = GetVideoDevice(cardid);
2279  if (IsTunerSharingCapable(rawtype) && !videodevice.isEmpty())
2280  {
2281  query.prepare(
2282  "SELECT cardid "
2283  "FROM capturecard "
2284  "WHERE videodevice = :DEVICE AND "
2285  " cardid > :CARDID");
2286  query.bindValue(":DEVICE", videodevice);
2287  query.bindValue(":CARDID", cardid);
2288 
2289  if (!query.exec())
2290  {
2291  MythDB::DBError("DeleteCard -- find clone cards", query);
2292  return false;
2293  }
2294 
2295  while (query.next())
2296  ok &= DeleteCard(query.value(0).toUInt());
2297 
2298  if (!ok)
2299  return false;
2300  }
2301 
2302  // delete inputs
2303  vector<uint> inputs = CardUtil::GetInputIDs(cardid);
2304  for (uint i = 0; i < inputs.size(); i++)
2305  ok &= CardUtil::DeleteInput(inputs[i]);
2306 
2307  if (!ok)
2308  return false;
2309 
2310  // actually delete the capturecard row for this card
2311  query.prepare("DELETE FROM capturecard WHERE cardid = :CARDID");
2312  query.bindValue(":CARDID", cardid);
2313 
2314  if (!query.exec())
2315  {
2316  MythDB::DBError("DeleteCard -- delete row", query);
2317  ok = false;
2318  }
2319 
2320  if (ok)
2321  {
2322  // delete any orphaned inputs & unused input groups
2324  UnlinkInputGroup(0,0);
2325  }
2326 
2327  return ok;
2328 }
2329 
2331 {
2332  MSqlQuery query(MSqlQuery::InitCon());
2333  return (query.exec("TRUNCATE TABLE inputgroup") &&
2334  query.exec("TRUNCATE TABLE diseqc_config") &&
2335  query.exec("TRUNCATE TABLE diseqc_tree") &&
2336  query.exec("TRUNCATE TABLE cardinput") &&
2337  query.exec("TRUNCATE TABLE capturecard"));
2338 }
2339 
2340 vector<uint> CardUtil::GetCardList(void)
2341 {
2342  vector<uint> list;
2343 
2344  MSqlQuery query(MSqlQuery::InitCon());
2345  query.prepare(
2346  "SELECT cardid "
2347  "FROM capturecard "
2348  "ORDER BY cardid");
2349 
2350  if (!query.exec())
2351  MythDB::DBError("CardUtil::GetCardList()", query);
2352  else
2353  {
2354  while (query.next())
2355  list.push_back(query.value(0).toUInt());
2356  }
2357 
2358  return list;
2359 }
2360 
2362 {
2363  vector<uint> list;
2364 
2365  MSqlQuery query(MSqlQuery::InitCon());
2366  query.prepare(
2367  "SELECT DISTINCT cardid "
2368  "FROM cardinput "
2369  "WHERE livetvorder <> 0 "
2370  "ORDER BY livetvorder");
2371 
2372  if (!query.exec())
2373  MythDB::DBError("CardUtil::GetCardList()", query);
2374  else
2375  {
2376  while (query.next())
2377  list.push_back(query.value(0).toUInt());
2378  }
2379 
2380  return list;
2381 }
2382 
2383 
2384 QString CardUtil::GetDeviceName(dvb_dev_type_t type, const QString &device)
2385 {
2386  QString devname = QString(device);
2387 
2388  if (DVB_DEV_FRONTEND == type)
2389  return devname;
2390  else if (DVB_DEV_DVR == type)
2391  return devname.replace(devname.indexOf("frontend"), 8, "dvr");
2392  else if (DVB_DEV_DEMUX == type)
2393  return devname.replace(devname.indexOf("frontend"), 8, "demux");
2394  else if (DVB_DEV_CA == type)
2395  return devname.replace(devname.indexOf("frontend"), 8, "ca");
2396  else if (DVB_DEV_AUDIO == type)
2397  return devname.replace(devname.indexOf("frontend"), 8, "audio");
2398  else if (DVB_DEV_VIDEO == type)
2399  return devname.replace(devname.indexOf("frontend"), 8, "video");
2400 
2401  return "";
2402 }
2403 
2413 bool CardUtil::HDHRdoesDVB(const QString &device)
2414 {
2415  (void) device;
2416 
2417 #ifdef USING_HDHOMERUN
2418  hdhomerun_device_t *hdhr;
2419  hdhr = hdhomerun_device_create_from_str(device.toLatin1(), NULL);
2420  if (!hdhr)
2421  return false;
2422 
2423  const char *model = hdhomerun_device_get_model_str(hdhr);
2424  if (model && strstr(model, "dvb"))
2425  {
2426  hdhomerun_device_destroy(hdhr);
2427  return true;
2428  }
2429 
2430  hdhomerun_device_destroy(hdhr);
2431 
2432 #endif
2433 
2434  return false;
2435 }
2436 
2441 QString CardUtil::GetHDHRdesc(const QString &device)
2442 {
2443  QString connectErr = QObject::tr("Unable to connect to device.");
2444 
2445 #ifdef USING_HDHOMERUN
2446  bool deviceIsIP = false;
2447  uint32_t dev;
2448 
2449  if (device.contains('.')) // Simplistic check, but also allows DNS names
2450  deviceIsIP = true;
2451  else
2452  {
2453  bool validID;
2454 
2455  dev = device.toUInt(&validID, 16);
2456  if (!validID || !hdhomerun_discover_validate_device_id(dev))
2457  return QObject::tr("Invalid Device ID");
2458  }
2459  (void) deviceIsIP;
2460 
2461  LOG(VB_GENERAL, LOG_INFO, "CardUtil::GetHDHRdescription(" + device +
2462  ") - trying to locate device");
2463 
2464  hdhomerun_device_t *hdhr;
2465  hdhr = hdhomerun_device_create_from_str(device.toLatin1(), NULL);
2466  if (!hdhr)
2467  return QObject::tr("Invalid Device ID or address.");
2468 
2469  const char *model = hdhomerun_device_get_model_str(hdhr);
2470  if (!model)
2471  {
2472  hdhomerun_device_destroy(hdhr);
2473  return connectErr;
2474  }
2475 
2476 
2477  QString description = model;
2478  char *sVersion;
2479  uint32_t iVersion;
2480 
2481  if (hdhomerun_device_get_version(hdhr, &sVersion, &iVersion))
2482  description += QObject::tr(", firmware: %2").arg(sVersion);
2483 
2484  hdhomerun_device_destroy(hdhr);
2485 
2486  return description;
2487 #else
2488 
2489  (void) device;
2490  return connectErr;
2491 #endif
2492 }
2493 
2494 #ifdef USING_ASI
2495 static QString sys_dev(uint device_num, QString dev)
2496 {
2497  return QString("/sys/class/asi/asirx%1/%2").arg(device_num).arg(dev);
2498 }
2499 
2500 static QString read_sys(QString sys_dev)
2501 {
2502  QFile f(sys_dev);
2503  f.open(QIODevice::ReadOnly);
2504  QByteArray sdba = f.readAll();
2505  f.close();
2506  return sdba;
2507 }
2508 
2509 static bool write_sys(QString sys_dev, QString str)
2510 {
2511  QFile f(sys_dev);
2512  f.open(QIODevice::WriteOnly);
2513  QByteArray ba = str.toLocal8Bit();
2514  qint64 offset = 0;
2515  for (uint tries = 0; (offset < ba.size()) && tries < 5; tries++)
2516  {
2517  qint64 written = f.write(ba.data()+offset, ba.size()-offset);
2518  if (written < 0)
2519  return false;
2520  offset += written;
2521  }
2522  return true;
2523 }
2524 #endif
2525 
2526 int CardUtil::GetASIDeviceNumber(const QString &device, QString *error)
2527 {
2528 #ifdef USING_ASI
2529  // basic confirmation
2530  struct stat statbuf;
2531  memset(&statbuf, 0, sizeof(statbuf));
2532  if (stat(device.toLocal8Bit().constData(), &statbuf) < 0)
2533  {
2534  if (error)
2535  *error = QString("Unable to stat '%1'").arg(device) + ENO;
2536  return -1;
2537  }
2538 
2539  if (!S_ISCHR(statbuf.st_mode))
2540  {
2541  if (error)
2542  *error = QString("'%1' is not a character device").arg(device);
2543  return -1;
2544  }
2545 
2546  if (!(statbuf.st_rdev & 0x0080))
2547  {
2548  if (error)
2549  *error = QString("'%1' not a DVEO ASI receiver").arg(device);
2550  return -1;
2551  }
2552 
2553  int device_num = statbuf.st_rdev & 0x007f;
2554 
2555  // extra confirmation
2556  QString sys_dev_contents = read_sys(sys_dev(device_num, "dev"));
2557  QStringList sys_dev_clist = sys_dev_contents.split(":");
2558  if (2 != sys_dev_clist.size())
2559  {
2560  if (error)
2561  {
2562  *error = QString("Unable to read '%1'")
2563  .arg(sys_dev(device_num, "dev"));
2564  }
2565  return -1;
2566  }
2567  if (sys_dev_clist[0].toUInt() != (statbuf.st_rdev>>8))
2568  {
2569  if (error)
2570  *error = QString("'%1' not a DVEO ASI device").arg(device);
2571  return -1;
2572  }
2573 
2574  return device_num;
2575 #else
2576  (void) device;
2577  if (error)
2578  *error = "Not compiled with ASI support.";
2579  return -1;
2580 #endif
2581 }
2582 
2584 {
2585 #ifdef USING_ASI
2586  // get the buffer size
2587  QString sys_bufsize_contents = read_sys(sys_dev(device_num, "bufsize"));
2588  bool ok;
2589  uint buf_size = sys_bufsize_contents.toUInt(&ok);
2590  if (!ok)
2591  {
2592  if (error)
2593  {
2594  *error = QString("Failed to read buffer size from '%1'")
2595  .arg(sys_dev(device_num, "bufsize"));
2596  }
2597  return 0;
2598  }
2599  return buf_size;
2600 #else
2601  (void) device_num;
2602  if (error)
2603  *error = "Not compiled with ASI support.";
2604  return 0;
2605 #endif
2606 }
2607 
2609 {
2610 #ifdef USING_ASI
2611  // get the buffer size
2612  QString sys_numbuffers_contents = read_sys(sys_dev(device_num, "buffers"));
2613  bool ok;
2614  uint num_buffers = sys_numbuffers_contents.toUInt(&ok);
2615  if (!ok)
2616  {
2617  if (error)
2618  {
2619  *error = QString("Failed to read num buffers from '%1'")
2620  .arg(sys_dev(device_num, "buffers"));
2621  }
2622  return 0;
2623  }
2624  return num_buffers;
2625 #else
2626  (void) device_num;
2627  if (error)
2628  *error = "Not compiled with ASI support.";
2629  return 0;
2630 #endif
2631 }
2632 
2633 int CardUtil::GetASIMode(uint device_num, QString *error)
2634 {
2635 #ifdef USING_ASI
2636  QString sys_bufsize_contents = read_sys(sys_dev(device_num, "mode"));
2637  bool ok;
2638  uint mode = sys_bufsize_contents.toUInt(&ok);
2639  if (!ok)
2640  {
2641  if (error)
2642  {
2643  *error = QString("Failed to read mode from '%1'")
2644  .arg(sys_dev(device_num, "mode"));
2645  }
2646  return -1;
2647  }
2648  return mode;
2649 #else
2650  (void) device_num;
2651  if (error)
2652  *error = "Not compiled with ASI support.";
2653  return -1;
2654 #endif
2655 }
2656 
2657 bool CardUtil::SetASIMode(uint device_num, uint mode, QString *error)
2658 {
2659 #ifdef USING_ASI
2660  QString sys_bufsize_contents = read_sys(sys_dev(device_num, "mode"));
2661  bool ok;
2662  uint old_mode = sys_bufsize_contents.toUInt(&ok);
2663  if (ok && old_mode == mode)
2664  return true;
2665  ok = write_sys(sys_dev(device_num, "mode"), QString("%1\n").arg(mode));
2666  if (!ok && error)
2667  {
2668  *error = QString("Failed to set mode to %1 using '%2'")
2669  .arg(mode).arg(sys_dev(device_num, "mode"));
2670  }
2671  return ok;
2672 #else
2673  (void) device_num;
2674  if (error)
2675  *error = "Not compiled with ASI support.";
2676  return false;
2677 #endif
2678 }
static QStringList GetCardTypes(void)
Definition: cardutil.cpp:223
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
Definition: mythdbcon.cpp:765
uint scheduleOrder
Definition: inputinfo.h:77
dvb_dev_type_t
Definition: cardutil.h:33
static bool IsTunerSharingCapable(const QString &rawtype)
Definition: cardutil.h:152
static bool HasDVBCRCBug(const QString &device)
Returns true if and only if the device munges PAT/PMT tables, and then doesn't fix the CRC...
Definition: cardutil.cpp:487
int recPriority
Definition: inputinfo.h:76
open(CONF, $file) or die("\nERROR
static QStringList ProbeVideoDevices(const QString &rawtype)
Definition: cardutil.cpp:292
void bindValue(const QString &placeholder, const QVariant &val)
Definition: mythdbcon.cpp:857
DVB-S device settings class.
Definition: diseqc.h:37
static QString GetInputName(uint inputid)
Definition: cardutil.cpp:1241
typedef void(__LZO_CDECL *lzo_free_func_t)(lzo_callback_p self
DiSEqCDevDevice * Root(void)
Retrieves the root node in the tree.
Definition: diseqc.h:93
static uint GetASINumBuffers(uint device_num, QString *error=NULL)
Definition: cardutil.cpp:2608
static bool CloneCard(uint src_cardid, uint dst_cardid)
Definition: cardutil.cpp:934
static vector< uint > GetGroupCardIDs(uint inputgroupid)
Definition: cardutil.cpp:1688
DiSEqCDevTree * FindTree(uint cardid)
Retrieve device tree.
Definition: diseqc.cpp:248
static QStringList GetVideoDevices(const QString &rawtype, QString hostname=QString::null)
Returns the videodevices of the matching cards, duplicates removed.
Definition: cardutil.cpp:249
bool Store(uint cardid)
Stores the device tree to the database.
Definition: diseqc.cpp:402
static int CreateCaptureCard(const QString &videodevice, const QString &audiodevice, const QString &vbidevice, const QString &cardtype, const uint audioratelimit, const QString &hostname, const uint dvb_swfilter, const uint dvb_sat_type, bool dvb_wait_for_seqstart, bool skipbtaudio, bool dvb_on_demand, const uint dvb_diseqc_type, const uint firewire_speed, const QString &firewire_model, const uint firewire_connection, const uint signal_timeout, const uint channel_timeout, const uint dvb_tuning_delay, const uint contrast, const uint brightness, const uint colour, const uint hue, const uint diseqcid, bool dvb_eitscan)
Definition: cardutil.cpp:2170
static vector< uint > GetSharedInputGroups(uint cardid)
Definition: cardutil.cpp:1664
static uint GetFirstCardID(const QString &videodevice)
Convenience function for GetCardIDs()
Definition: cardutil.h:229
static uint CreateInputGroup(const QString &name)
Definition: cardutil.cpp:1501
bool Load(uint cardid)
Loads the device tree from the database.
Definition: diseqc.cpp:331
static QString ProbeDVBType(const QString &device)
Definition: cardutil.cpp:395
my tmp
Definition: twit.tv.pl:144
QVariant value(int i) const
Definition: mythdbcon.h:182
static int CreateCardInput(const uint cardid, const uint sourceid, const QString &inputname, const QString &externalcommand, const QString &changer_device, const QString &changer_model, const QString &hostname, const QString &tunechan, const QString &startchan, const QString &displayname, bool dishnet_eit, const uint recpriority, const uint quicktune, const uint schedorder, const uint livetvorder)
Definition: cardutil.cpp:1386
uint sourceid
associated channel listings source
Definition: inputinfo.h:71
QSqlQuery wrapper that fetches a DB connection from the connection pool.
Definition: mythdbcon.h:125
static pid_list_t::iterator find(const PIDInfoMap &map, pid_list_t &list, pid_list_t::iterator begin, pid_list_t::iterator end, bool find_open)
static QString ProbeDVBFrontendName(const QString &device)
Returns the card type from the video device.
Definition: cardutil.cpp:441
static bool IsTunerShared(uint cardidA, uint cardidB)
Definition: cardutil.cpp:141
static QString GetRawCardType(uint cardid)
Definition: cardutil.h:248
static QStringList ProbeDVBInputs(QString device)
Definition: cardutil.cpp:2064
register const lzo_bytep ip
Definition: minilzo.cpp:3226
static bool write_sys(QString sys_dev, QString str)
Definition: cardutil.cpp:2509
static bool IsInNeedOfExternalInputConf(uint cardid)
Definition: cardutil.cpp:1769
static vector< uint > GetConflictingCards(uint inputid, uint exclude_cardid)
Definition: cardutil.cpp:1715
unsigned int uint
Definition: compat.h:135
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
static bool IsCardTypePresent(const QString &rawtype, QString hostname=QString::null)
Returns true if the card type is present and connected to an input.
Definition: cardutil.cpp:188
static uint clone_capturecard(uint src_cardid, uint orig_dst_cardid)
Definition: cardutil.cpp:667
GLint GLenum GLsizei GLint GLenum GLenum type
static bool DeleteCard(uint cardid)
Definition: cardutil.cpp:2259
static bool GetInputInfo(InputInfo &info, vector< uint > *groupids=NULL)
Definition: cardutil.cpp:1162
bool set_on_input(const QString &to_set, uint inputid, const QString &value)
Definition: cardutil.cpp:586
static bool DeleteInput(uint inputid)
Definition: cardutil.cpp:1450
static QString GetScanableCardTypes(void)
Definition: cardutil.cpp:50
static const int kTunerTypeDVBS1
uint cardid
card id associated with input
Definition: inputinfo.h:73
bool Load(uint card_input_id)
Loads configuration chain from DB for specified card input id.
Definition: diseqc.cpp:137
static QString sys_dev(uint device_num, QString dev)
Definition: cardutil.cpp:2495
static bool clone_cardinputs(uint src_cardid, uint dst_cardid)
Definition: cardutil.cpp:764
return memset(s, c, len)
static QStringList ProbeVideoInputs(QString device, QString cardtype=QString::null)
Definition: cardutil.cpp:1974
QString getSourceName(void) const
QString displayName
Definition: inputinfo.h:75
voidpf uLong offset
Definition: ioapi.h:142
static const int kTunerTypeDVBS2
void loadByInput(int cardid, QString input)
static InputNames ProbeV4LAudioInputs(int videofd, bool &ok)
Definition: cardutil.cpp:1921
static QString GetStartInput(uint cardid)
Returns the start input for the card.
Definition: cardutil.cpp:1109
void SetRoot(DiSEqCDevDevice *root)
Changes the root node of the tree.
Definition: diseqc.cpp:611
uint livetvorder
order for live TV use
Definition: inputinfo.h:78
static QString GetDeviceName(dvb_dev_type_t, const QString &device)
Definition: cardutil.cpp:2384
static bool DeleteOrphanInputs(void)
Definition: cardutil.cpp:1467
static uint GetCardID(uint inputid)
Definition: cardutil.cpp:1234
then echo error
Definition: unittests.sh:42
static vector< uint > GetInputIDs(uint cardid)
Definition: cardutil.cpp:1362
static QString GetFirewireChangerModel(uint inputid)
Definition: cardutil.cpp:1020
static bool UnlinkInputGroup(uint inputid, uint inputgroupid)
Definition: cardutil.cpp:1607
static MSqlQueryInfo InitCon(ConnectionReuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
Definition: mythdbcon.cpp:529
static bool IsSingleInputCard(const QString &rawtype)
Definition: cardutil.h:180
static QList< InputInfo > GetAllInputInfo()
Definition: cardutil.cpp:1199
static bool HDHRdoesDVB(const QString &device)
If the device is valid, check if the model does DVB.
Definition: cardutil.cpp:2413
const char * name
Definition: ParseText.cpp:338
static uint GetASIBufferSize(uint device_num, QString *error=NULL)
Definition: cardutil.cpp:2583
uint inputid
unique key in DB for this input
Definition: inputinfo.h:72
static QString ProbeSubTypeName(uint cardid)
Definition: cardutil.cpp:504
bool isActive(void) const
Definition: mythdbcon.h:188
static int GetASIDeviceNumber(const QString &device, QString *error=NULL)
Definition: cardutil.cpp:2526
static bool SetASIMode(uint device_num, uint mode, QString *error=NULL)
Definition: cardutil.cpp:2657
QString toString() const
static vector< uint > GetCloneCardIDs(uint cardid)
Definition: cardutil.cpp:953
static QString GetFirewireChangerNode(uint inputid)
Definition: cardutil.cpp:1003
static InputNames GetConfiguredDVBInputs(uint cardid)
Definition: cardutil.cpp:1954
static QStringList ProbeAudioInputs(QString device, QString cardtype=QString::null)
Definition: cardutil.cpp:1988
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
Definition: mythdbcon.cpp:790
bool quickTune
Definition: inputinfo.h:79
static QString GetVideoDevice(uint cardid)
Definition: cardutil.h:250
static QStringList GetInputNames(uint cardid, uint sourceid=0)
Definition: cardutil.cpp:1128
static vector< uint > GetCardList(void)
Definition: cardutil.cpp:2340
static uint GetQuickTuning(uint cardid, const QString &inputname)
Definition: cardutil.cpp:1781
static bool GetTimeouts(uint cardid, uint &signal_timeout, uint &channel_timeout)
Definition: cardutil.cpp:1747
bool set_on_source(const QString &to_set, uint cardid, uint sourceid, const QString &value)
Definition: cardutil.cpp:543
print $fh[client] channum
QString get_on_cardid(const QString &to_get, uint cardid)
Definition: cardutil.cpp:526
QString name
input name
Definition: inputinfo.h:70
static QString GetDeviceLabel(const QString &cardtype, const QString &videodevice)
Definition: cardutil.cpp:2088
static void GetCardInputs(uint cardid, const QString &device, const QString &cardtype, QStringList &inputLabels, vector< CardInput * > &cardInputs)
Definition: cardutil.cpp:2111
static bool SetStartChannel(uint cardinputid, const QString &channum)
Definition: cardutil.cpp:1086
QMap< int, QString > InputNames
Definition: cardutil.h:22
static const QString LOC
static uint GetSourceID(uint inputid)
Definition: cardutil.cpp:1325
static bool hasV4L2(int videofd)
Definition: cardutil.cpp:1802
const char int mode
Definition: ioapi.h:135
bool Store(uint card_input_id) const
Stores configuration chain to DB for specified card input id.
Definition: diseqc.cpp:171
static int GetCardInputID(uint cardid, const QString &channum, QString &inputname)
Definition: cardutil.cpp:1061
static bool GetV4LInfo(int videofd, QString &card, QString &driver, uint32_t &version, uint32_t &capabilities)
Definition: cardutil.cpp:1816
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
Definition: mythdbcon.cpp:597
typedef int
Definition: lzoconf.h:279
static bool LinkInputGroup(uint inputid, uint inputgroupid)
Definition: cardutil.cpp:1567
static void DBError(const QString &where, const MSqlQuery &query)
Definition: mythdb.cpp:181
static bool DeleteAllCards(void)
Definition: cardutil.cpp:2330
static uint GetInputID(uint cardid, const QString &inputname)
Definition: cardutil.cpp:1289
close(CACHE)
static bool IsCableCardPresent(uint cardid, const QString &cardType)
Definition: cardutil.cpp:97
QString GetHostName(void)
static vector< uint > GetLiveTVCardList(void)
Definition: cardutil.cpp:2361
static vector< uint > GetCardIDs(QString videodevice=QString::null, QString rawtype=QString::null, QString hostname=QString::null)
Returns all cardids of cards that uses the specified videodevice if specified, and optionally rawtype...
Definition: cardutil.cpp:620
DVB-S device tree class.
Definition: diseqc.h:75
static vector< uint > GetAllInputIDs(void)
Definition: cardutil.cpp:1341
bool IsInNeedOfConf(void) const
Definition: diseqc.cpp:810
static InputNames ProbeV4LVideoInputs(int videofd, bool &ok)
Definition: cardutil.cpp:1854
static int GetASIMode(uint device_num, QString *error=NULL)
Definition: cardutil.cpp:2633
static QString GetHDHRdesc(const QString &device)
Get a nicely formatted string describing the device.
Definition: cardutil.cpp:2441
static bool CreateInputGroupIfNeeded(uint cardid)
Definition: cardutil.cpp:1530
static bool IsDVBCardType(const QString &card_type)
Returns true iff the card_type is one of the DVB types.
Definition: cardutil.cpp:519
static vector< uint > GetInputGroups(uint inputid)
Definition: cardutil.cpp:1638
static uint GetMinSignalMonitoringDelay(const QString &device)
Definition: cardutil.cpp:494
QString get_on_inputid(const QString &to_get, uint inputid)
Definition: cardutil.cpp:569
static QString GetStartingChannel(uint inputid)
Definition: cardutil.cpp:1248
Main DVB-S device interface.
Definition: diseqc.h:52
static QString read_sys(QString sys_dev)
Definition: cardutil.cpp:2500
static QString GetDisplayName(uint inputid)
Definition: cardutil.cpp:1264