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