MythTV  master
linuxfirewiredevice.cpp
Go to the documentation of this file.
1 
10 // POSIX headers
11 #include <sys/select.h>
12 #include <unistd.h>
13 #include <fcntl.h>
14 
15 // Linux headers
16 #include <libraw1394/raw1394.h>
17 #include <libraw1394/csr.h>
18 #include <libiec61883/iec61883.h>
19 #include <libavc1394/avc1394.h>
20 #include <libavc1394/rom1394.h>
21 
22 #include <netinet/in.h>
23 
24 // C++ headers
25 #include <algorithm>
26 #include <chrono> // for milliseconds
27 #include <map>
28 #include <thread> // for sleep_for
29 using namespace std;
30 
31 // Qt headers
32 #include <QDateTime>
33 
34 // MythTV headers
35 #include "linuxfirewiredevice.h"
36 #include "firewirerecorder.h"
37 #include "mythcorecontext.h"
38 #include "linuxavcinfo.h"
39 #include "mythlogging.h"
40 
41 #define LOC QString("LFireDev(%1): ").arg(guid_to_string(m_guid))
42 
43 #define kNoDataTimeout 50 /* msec */
44 #define kResetTimeout 1000 /* msec */
45 
46 using handle_to_lfd_t = QMap<raw1394handle_t,LinuxFirewireDevice*>;
47 
48 class LFDPriv
49 {
50  public:
51  LFDPriv() = default;
52 
54  {
55  foreach (auto & device, m_devices)
56  delete device;
57  m_devices.clear();
58 
59  if (m_portHandlerThread)
60  {
61  m_portHandlerThread->wait();
62  delete m_portHandlerThread;
63  }
64  }
65 
66  uint m_generation {0};
67  bool m_resetTimerOn {false};
69 
70  bool m_runPortHandler {false};
71  bool m_isPortHandlerRunning {false};
72  QWaitCondition m_portHandlerWait;
74 
75  iec61883_mpeg2_t m_avstream {nullptr};
76  int m_channel {-1};
77  int m_outputPlug {-1};
78  int m_inputPlug {-1};
79  int m_bandwidth {0};
80  uint m_noDataCnt {0};
81 
82  bool m_isP2pNodeOpen {false};
83  bool m_isBcastNodeOpen {false};
84  bool m_isStreaming {false};
85 
86  MThread *m_portHandlerThread {nullptr};
87 
89 
90  static QMutex s_lock;
92 };
93 QMutex LFDPriv::s_lock;
95 
96 static void add_handle(raw1394handle_t handle, LinuxFirewireDevice *dev)
97 {
98  QMutexLocker slocker(&LFDPriv::s_lock);
99  LFDPriv::s_handle_info[handle] = dev;
100 }
101 
102 static void remove_handle(raw1394handle_t handle)
103 {
104  QMutexLocker slocker(&LFDPriv::s_lock);
105  LFDPriv::s_handle_info.remove(handle);
106 }
107 
111 const uint LinuxFirewireDevice::kMaxBufferedPackets = 4 * 1024 * 1024 / 188;
112 
113 // callback function for libiec61883
115  unsigned char *tspacket, int len, uint dropped, void *callback_data);
117 static bool has_data(int fd, int msec);
118 static QString speed_to_string(uint speed);
120  raw1394handle_t handle, uint generation);
121 
123  uint64_t guid, uint subunitid,
124  uint speed, bool use_p2p, uint av_buffer_size_in_bytes) :
125  FirewireDevice(guid, subunitid, speed),
126  m_bufsz(av_buffer_size_in_bytes),
127  m_useP2P(use_p2p), m_priv(new LFDPriv())
128 {
129  if (!m_bufsz)
130  m_bufsz = gCoreContext->GetNumSetting("HDRingbufferSize");
131 
132  m_dbResetDisabled = gCoreContext->GetBoolSetting("DisableFirewireReset", false);
133 
135 }
136 
138 {
140  {
141  LOG(VB_GENERAL, LOG_ERR, LOC + "ctor called with open port");
144  }
145 
146  if (m_priv)
147  {
148  delete m_priv;
149  m_priv = nullptr;
150  }
151 }
152 
154 {
155  const QString loc = LOC + QString("SignalReset(%1->%2)")
156  .arg(m_priv->m_generation).arg(generation);
157 
158  LOG(VB_GENERAL, LOG_INFO, loc);
159 
160  if (GetInfoPtr())
161  raw1394_update_generation(GetInfoPtr()->m_fwHandle, generation);
162 
163  m_priv->m_generation = generation;
164 
165  LOG(VB_GENERAL, LOG_INFO, loc + ": Updating device list -- begin");
167  LOG(VB_GENERAL, LOG_INFO, loc + ": Updating device list -- end");
168 
169  m_priv->m_resetTimerOn = true;
171 }
172 
174 {
175  const QString loc = LOC + "HandleBusReset";
176 
177  if (!GetInfoPtr() || !GetInfoPtr()->m_fwHandle)
178  return;
179 
180  if (m_priv->m_isP2pNodeOpen)
181  {
182  LOG(VB_GENERAL, LOG_INFO, loc + ": Reconnecting P2P connection");
183  nodeid_t output = GetInfoPtr()->GetNode() | 0xffc0;
184  nodeid_t input = raw1394_get_local_id(GetInfoPtr()->m_fwHandle);
185 
186  int fwchan = iec61883_cmp_reconnect(
187  GetInfoPtr()->m_fwHandle,
189  input, &m_priv->m_inputPlug,
191 
192  if (fwchan < 0)
193  {
194  LOG(VB_GENERAL, LOG_ERR, LOC + "Bus Reset: Failed to reconnect");
195  }
196  else if (fwchan != m_priv->m_channel)
197  {
198  LOG(VB_GENERAL, LOG_WARNING, LOC + QString("FWChan changed %1->%2")
199  .arg(m_priv->m_channel).arg(fwchan));
200  }
201  m_priv->m_channel = fwchan;
202 
203  LOG(VB_GENERAL, LOG_INFO,
204  loc + QString(": Reconnected fwchan: %1\n\t\t\toutput: 0x%2 "
205  "input: 0x%3")
206  .arg(fwchan).arg(output,0,16).arg(input,0,16));
207  }
208 
210  {
211  nodeid_t output = GetInfoPtr()->GetNode() | 0xffc0;
212 
213  LOG(VB_RECORD, LOG_INFO, loc + ": Restarting broadcast connection on " +
214  QString("node %1, channel %2")
215  .arg(GetInfoPtr()->GetNode()).arg(m_priv->m_channel));
216 
217  int err = iec61883_cmp_create_bcast_output(
218  GetInfoPtr()->m_fwHandle,
221 
222  if (err < 0)
223  {
224  LOG(VB_GENERAL, LOG_ERR, LOC + "Bus Reset : Failed to reconnect");
225  }
226  }
227 }
228 
230 {
231  LOG(VB_RECORD, LOG_INFO, LOC + "Starting Port Handler Thread");
232  QMutexLocker locker(&m_priv->m_startStopPortHandlerLock);
233  LOG(VB_RECORD, LOG_INFO, LOC + "Starting Port Handler Thread -- locked");
234 
235  LOG(VB_RECORD, LOG_INFO, LOC + "OpenPort()");
236 
237  QMutexLocker mlocker(&m_lock);
238 
239  LOG(VB_RECORD, LOG_INFO, LOC + "OpenPort() -- got lock");
240 
241  if (!GetInfoPtr())
242  return false;
243 
244  if (GetInfoPtr()->IsPortOpen())
245  {
246  m_open_port_cnt++;
247  return true;
248  }
249 
250  if (!GetInfoPtr()->OpenPort())
251  return false;
252 
253  add_handle(GetInfoPtr()->m_fwHandle, this);
254 
255  m_priv->m_generation = raw1394_get_generation(GetInfoPtr()->m_fwHandle);
256  raw1394_set_bus_reset_handler(
258 
260  LOG(VB_RECORD, LOG_INFO, LOC + GetInfoPtr()->GetSubunitInfoString());
261 
262  if (!GetInfoPtr()->IsSubunitType(kAVCSubunitTypeTuner) ||
263  !GetInfoPtr()->IsSubunitType(kAVCSubunitTypePanel))
264  {
265  LOG(VB_GENERAL, LOG_ERR, LOC + QString("Not an STB"));
266 
267  mlocker.unlock();
268  ClosePort();
269 
270  return false;
271  }
272 
273  m_priv->m_runPortHandler = true;
274 
275  LOG(VB_RECORD, LOG_INFO, LOC + "Starting port handler thread");
276  m_priv->m_portHandlerThread = new MThread("LinuxController", this);
278 
280  m_priv->m_portHandlerWait.wait(mlocker.mutex(), 100);
281 
282  LOG(VB_RECORD, LOG_INFO, LOC + "Port handler thread started");
283 
284  m_open_port_cnt++;
285 
286  return true;
287 }
288 
290 {
291  LOG(VB_RECORD, LOG_INFO, LOC + "Stopping Port Handler Thread");
292  QMutexLocker locker(&m_priv->m_startStopPortHandlerLock);
293  LOG(VB_RECORD, LOG_INFO, LOC + "Stopping Port Handler Thread -- locked");
294 
295  QMutexLocker mlocker(&m_lock);
296 
297  LOG(VB_RECORD, LOG_INFO, LOC + "ClosePort()");
298 
299  if (m_open_port_cnt < 1)
300  return false;
301 
302  m_open_port_cnt--;
303 
304  if (m_open_port_cnt != 0)
305  return true;
306 
307  if (!GetInfoPtr())
308  return false;
309 
310  if (GetInfoPtr()->IsPortOpen())
311  {
312  if (IsNodeOpen())
313  CloseNode();
314 
315  LOG(VB_RECORD, LOG_INFO,
316  LOC + "Waiting for port handler thread to stop");
317  m_priv->m_runPortHandler = false;
318  m_priv->m_portHandlerWait.wakeAll();
319 
320  mlocker.unlock();
322  mlocker.relock();
323 
324  delete m_priv->m_portHandlerThread;
325  m_priv->m_portHandlerThread = nullptr;
326 
327  LOG(VB_RECORD, LOG_INFO, LOC + "Joined port handler thread");
328 
329  remove_handle(GetInfoPtr()->m_fwHandle);
330 
331  if (!GetInfoPtr()->ClosePort())
332  return false;
333  }
334 
335  return true;
336 }
337 
339 {
340  QMutexLocker locker(&m_lock);
341 
342  FirewireDevice::AddListener(listener);
343 
344  if (!m_listeners.empty())
345  {
346  OpenNode();
347  OpenAVStream();
348  StartStreaming();
349  }
350 }
351 
353 {
354  QMutexLocker locker(&m_lock);
355 
357 
358  if (m_listeners.empty())
359  {
360  StopStreaming();
361  CloseAVStream();
362  CloseNode();
363  }
364 }
365 
367  const vector<uint8_t> &cmd,
368  vector<uint8_t> &result,
369  int retry_cnt)
370 {
371  return GetInfoPtr()->SendAVCCommand(cmd, result, retry_cnt);
372 }
373 
375 {
376  QMutexLocker locker(&m_lock);
377 
378  if (!GetInfoPtr())
379  return false;
380 
381  return GetInfoPtr()->IsPortOpen();
382 }
383 
385 // Private methods
386 
388 {
389  if (m_useP2P)
390  return OpenP2PNode();
391  return OpenBroadcastNode();
392 }
393 
395 {
396  if (m_priv->m_isP2pNodeOpen)
397  return CloseP2PNode();
398 
400  return CloseBroadcastNode();
401 
402  return true;
403 }
404 
405 // This may in fact open a broadcast connection, but it tries to open
406 // a P2P connection first.
408 {
410  return false;
411 
412  if (m_priv->m_isP2pNodeOpen)
413  return true;
414 
415  LOG(VB_RECORD, LOG_INFO, LOC + "Opening P2P connection");
416 
417  m_priv->m_bandwidth = +1; // +1 == allocate bandwidth
418  m_priv->m_outputPlug = -1; // -1 == find first online plug
419  m_priv->m_inputPlug = -1; // -1 == find first online plug
420  nodeid_t output = GetInfoPtr()->GetNode() | 0xffc0;
421  nodeid_t input = raw1394_get_local_id(GetInfoPtr()->m_fwHandle);
422  m_priv->m_channel = iec61883_cmp_connect(GetInfoPtr()->m_fwHandle,
424  input, &m_priv->m_inputPlug,
425  &m_priv->m_bandwidth);
426 
427  if (m_priv->m_channel < 0)
428  {
429  LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to create P2P connection");
430 
431  m_priv->m_bandwidth = 0;
432 
433  return false;
434  }
435 
436  m_priv->m_isP2pNodeOpen = true;
437 
438  return true;
439 }
440 
442 {
443  if (m_priv->m_isP2pNodeOpen && (m_priv->m_channel >= 0))
444  {
445  LOG(VB_RECORD, LOG_INFO, LOC + "Closing P2P connection");
446 
447  if (m_priv->m_avstream)
448  CloseAVStream();
449 
450  nodeid_t output = GetInfoPtr()->GetNode() | 0xffc0;
451  nodeid_t input = raw1394_get_local_id(GetInfoPtr()->m_fwHandle);
452 
453  iec61883_cmp_disconnect(GetInfoPtr()->m_fwHandle,
455  input, m_priv->m_inputPlug,
457 
458  m_priv->m_channel = -1;
459  m_priv->m_outputPlug = -1;
460  m_priv->m_inputPlug = -1;
461  m_priv->m_isP2pNodeOpen = false;
462  }
463 
464  return true;
465 }
466 
468 {
469  if (m_priv->m_isP2pNodeOpen)
470  return false;
471 
473  return true;
474 
475  if (m_priv->m_avstream)
476  CloseAVStream();
477 
479  m_priv->m_outputPlug = 0;
480  m_priv->m_inputPlug = 0;
481  nodeid_t output = GetInfoPtr()->GetNode() | 0xffc0;
482 
483  LOG(VB_RECORD, LOG_INFO, LOC + "Opening broadcast connection on " +
484  QString("node %1, channel %2")
485  .arg(GetInfoPtr()->GetNode()).arg(m_priv->m_channel));
486 
487  int err = iec61883_cmp_create_bcast_output(
488  GetInfoPtr()->m_fwHandle,
491 
492  if (err != 0)
493  {
494  LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to create Broadcast connection");
495 
496  m_priv->m_channel = -1;
497  m_priv->m_outputPlug = -1;
498  m_priv->m_inputPlug = -1;
499 
500  return false;
501  }
502 
503  m_priv->m_isBcastNodeOpen = true;
504 
505  return true;
506 }
507 
509 {
511  {
512  LOG(VB_RECORD, LOG_INFO, LOC + "Closing broadcast connection");
513 
514  m_priv->m_channel = -1;
515  m_priv->m_outputPlug = -1;
516  m_priv->m_inputPlug = -1;
517  m_priv->m_isBcastNodeOpen = false;
518  }
519  return true;
520 }
521 
523 {
524  LOG(VB_RECORD, LOG_INFO, LOC + "OpenAVStream");
525 
526  if (!GetInfoPtr() || !GetInfoPtr()->IsPortOpen())
527  {
528  LOG(VB_GENERAL, LOG_ERR, LOC +
529  "Cannot open AVStream without open IEEE 1394 port");
530 
531  return false;
532  }
533 
534  if (!IsNodeOpen() && !OpenNode())
535  return false;
536 
537  if (m_priv->m_avstream)
538  return true;
539 
540  LOG(VB_RECORD, LOG_INFO, LOC + "Opening A/V stream object");
541 
542  m_priv->m_avstream = iec61883_mpeg2_recv_init(
544 
545  if (!m_priv->m_avstream)
546  {
547  LOG(VB_GENERAL, LOG_ERR, LOC + "Unable to open AVStream" + ENO);
548 
549  return false;
550  }
551 
552  iec61883_mpeg2_set_synch(m_priv->m_avstream, 1 /* sync on close */);
553 
554  if (m_bufsz)
556 
557  return true;
558 }
559 
561 {
562  if (!m_priv->m_avstream)
563  return true;
564 
565  LOG(VB_RECORD, LOG_INFO, LOC + "Closing A/V stream object");
566 
567  while (!m_listeners.empty())
569 
570  if (m_priv->m_isStreaming)
571  StopStreaming();
572 
573  iec61883_mpeg2_close(m_priv->m_avstream);
574  m_priv->m_avstream = nullptr;
575 
576  return true;
577 }
578 
580 {
581  LOG(VB_RECORD, LOG_INFO, LOC + "RunPortHandler -- start");
582  m_lock.lock();
583  LOG(VB_RECORD, LOG_INFO, LOC + "RunPortHandler -- got first lock");
585  m_priv->m_portHandlerWait.wakeAll();
586  // we need to unlock & sleep to allow wakeAll to wake other threads.
587  m_lock.unlock();
588  std::this_thread::sleep_for(std::chrono::microseconds(2500));
589  m_lock.lock();
590 
591  m_priv->m_noDataCnt = 0;
592  while (m_priv->m_runPortHandler)
593  {
594  LFDPriv::s_lock.lock();
595  bool reset_timer_on = m_priv->m_resetTimerOn;
596  bool handle_reset = reset_timer_on &&
597  (m_priv->m_resetTimer.elapsed() > 100);
598  if (handle_reset)
599  m_priv->m_resetTimerOn = false;
600  LFDPriv::s_lock.unlock();
601 
602  if (handle_reset)
603  HandleBusReset();
604 
605  if (!reset_timer_on && m_priv->m_isStreaming &&
607  {
608  m_priv->m_noDataCnt = 0;
609  ResetBus();
610  }
611 
612  int fwfd = raw1394_get_fd(GetInfoPtr()->m_fwHandle);
613  if (fwfd < 0)
614  {
615  // We unlock here because this can take a long time
616  // and we don't want to block other actions.
618 
619  m_priv->m_noDataCnt += (m_priv->m_isStreaming) ? 1 : 0;
620  continue;
621  }
622 
623  // We unlock here because this can take a long time and we
624  // don't want to block other actions. All reads and writes
625  // are done with the lock, so this is safe so long as we
626  // check that we really have data once we get the lock.
627  m_lock.unlock();
628  bool ready = has_data(fwfd, kNoDataTimeout);
629  m_lock.lock();
630 
631  if (!ready && m_priv->m_isStreaming)
632  {
633  m_priv->m_noDataCnt++;
634 
635  LOG(VB_GENERAL, LOG_WARNING, LOC + QString("No Input in %1 msec...")
637  }
638 
639  // Confirm that we won't block, now that we have the lock...
640  if (ready && has_data(fwfd, 1 /* msec */))
641  {
642  // Performs blocking read of next 4 bytes off bus and handles
643  // them. Most firewire commands do their own loop_iterate
644  // internally to check for results, but some things like
645  // streaming data and FireWire bus resets must be handled
646  // as well, which we do here...
647  int ret = raw1394_loop_iterate(GetInfoPtr()->m_fwHandle);
648  if (-1 == ret)
649  {
650  LOG(VB_GENERAL, LOG_ERR, LOC + "raw1394_loop_iterate" + ENO);
651  }
652  }
653  }
654 
656  m_priv->m_portHandlerWait.wakeAll();
657  m_lock.unlock();
658  LOG(VB_RECORD, LOG_INFO, LOC + "RunPortHandler -- end");
659 }
660 
662 {
663  if (m_priv->m_isStreaming)
664  return m_priv->m_isStreaming;
665 
666  if (!IsAVStreamOpen() && !OpenAVStream())
667  return false;
668 
669  if (m_priv->m_channel < 0)
670  {
671  LOG(VB_GENERAL, LOG_ERR, LOC + "Starting A/V streaming, no channel");
672  return false;
673  }
674 
675  LOG(VB_RECORD, LOG_INFO, LOC + "Starting A/V streaming -- really");
676 
677  if (iec61883_mpeg2_recv_start(m_priv->m_avstream, m_priv->m_channel) == 0)
678  {
679  m_priv->m_isStreaming = true;
680  }
681  else
682  {
683  LOG(VB_GENERAL, LOG_ERR, LOC + "Starting A/V streaming " + ENO);
684  }
685 
686  LOG(VB_RECORD, LOG_INFO, LOC + "Starting A/V streaming -- done");
687 
688  return m_priv->m_isStreaming;
689 }
690 
692 {
693  if (m_priv->m_isStreaming)
694  {
695  LOG(VB_RECORD, LOG_INFO, LOC + "Stopping A/V streaming -- really");
696 
697  m_priv->m_isStreaming = false;
698 
699  iec61883_mpeg2_recv_stop(m_priv->m_avstream);
700 
701  raw1394_iso_recv_flush(GetInfoPtr()->m_fwHandle);
702  }
703 
704  LOG(VB_RECORD, LOG_INFO, LOC + "Stopped A/V streaming");
705 
706  return true;
707 }
708 
710 {
711  if (!m_priv->m_avstream)
712  return false;
713 
714  // Set buffered packets size
715  uint buffer_size = max(size_in_bytes, 50 * TSPacket::kSize);
716  size_t buffered_packets = min(buffer_size / 4, kMaxBufferedPackets);
717 
718  iec61883_mpeg2_set_buffers(m_priv->m_avstream, buffered_packets);
719 
720  LOG(VB_GENERAL, LOG_INFO, LOC + QString("Buffered packets %1 (%2 KB)")
721  .arg(buffered_packets).arg(buffered_packets * 4));
722 
723  return true;
724 }
725 
727 {
728  if (!m_priv->m_avstream)
729  return false;
730 
731  uint curspeed = iec61883_mpeg2_get_speed(m_priv->m_avstream);
732 
733  if (curspeed == speed)
734  {
735  m_speed = speed;
736  return true;
737  }
738 
739  LOG(VB_RECORD, LOG_INFO, LOC + QString("Changing Speed %1 -> %2")
740  .arg(speed_to_string(curspeed))
741  .arg(speed_to_string(m_speed)));
742 
743  iec61883_mpeg2_set_speed(m_priv->m_avstream, speed);
744 
745  if (speed == (uint)iec61883_mpeg2_get_speed(m_priv->m_avstream))
746  {
747  m_speed = speed;
748  return true;
749  }
750 
751  LOG(VB_GENERAL, LOG_WARNING, LOC + "Unable to set firewire speed.");
752 
753  return false;
754 }
755 
757 {
759 }
760 
762 {
763  return m_priv->m_avstream;
764 }
765 
767 {
768  LOG(VB_GENERAL, LOG_INFO, LOC + "ResetBus() -- begin");
769 
770  if (m_dbResetDisabled)
771  {
772  LOG(VB_GENERAL, LOG_WARNING, LOC + "Bus Reset disabled" + ENO);
773  LOG(VB_GENERAL, LOG_INFO, LOC + "ResetBus() -- end");
774  return true;
775  }
776 
777  bool ok = (raw1394_reset_bus_new(GetInfoPtr()->m_fwHandle,
778  RAW1394_LONG_RESET) == 0);
779  if (!ok)
780  LOG(VB_GENERAL, LOG_ERR, LOC + "Bus Reset failed" + ENO);
781 
782  LOG(VB_GENERAL, LOG_INFO, LOC + "ResetBus() -- end");
783 
784  return ok;
785 }
786 
788 {
789  if (dropped_packets == 1)
790  {
791  LOG(VB_RECORD, LOG_ERR, LOC + "Dropped a TS packet");
792  }
793  else if (dropped_packets > 1)
794  {
795  LOG(VB_RECORD, LOG_ERR, LOC + QString("Dropped %1 TS packets")
796  .arg(dropped_packets));
797  }
798 }
799 
800 vector<AVCInfo> LinuxFirewireDevice::GetSTBList(void)
801 {
802  vector<AVCInfo> list;
803 
804  {
805  LinuxFirewireDevice dev(0,0,0,false);
806  list = dev.GetSTBListPrivate();
807  }
808 
809  return list;
810 }
811 
813 {
814 #if 0
815  LOG(VB_GENERAL, LOG_DEBUG, "GetSTBListPrivate -- begin");
816 #endif
817  QMutexLocker locker(&m_lock);
818 #if 0
819  LOG(VB_GENERAL, LOG_DEBUG, "GetSTBListPrivate -- got lock");
820 #endif
821 
822  vector<AVCInfo> list;
823 
824  foreach (auto & device, m_priv->m_devices)
825  {
826  if (device->IsSubunitType(kAVCSubunitTypeTuner) &&
827  device->IsSubunitType(kAVCSubunitTypePanel))
828  {
829  list.push_back(*device);
830  }
831  }
832 
833 #if 0
834  LOG(VB_GENERAL, LOG_DEBUG, "GetSTBListPrivate -- end");
835 #endif
836  return list;
837 }
838 
839 struct dev_item
840 {
841  raw1394handle_t m_handle;
842  int m_port;
843  int m_node;
844 };
845 
847 {
848  dev_item item {};
849 
850  item.m_handle = raw1394_new_handle();
851  if (!item.m_handle)
852  {
853  LOG(VB_GENERAL, LOG_ERR, QString("LinuxFirewireDevice: ") +
854  "Couldn't get handle" + ENO);
855  return false;
856  }
857 
858  struct raw1394_portinfo port_info[16];
859  int numcards = raw1394_get_port_info(item.m_handle, port_info, 16);
860  if (numcards < 1)
861  {
862  raw1394_destroy_handle(item.m_handle);
863  return true;
864  }
865 
866  map<uint64_t,bool> guid_online;
867  for (int port = 0; port < numcards; port++)
868  {
869  if (raw1394_set_port(item.m_handle, port) < 0)
870  {
871  LOG(VB_GENERAL, LOG_ERR, QString("LinuxFirewireDevice: "
872  "Couldn't set port to %1").arg(port));
873  continue;
874  }
875 
876  for (int node = 0; node < raw1394_get_nodecount(item.m_handle); node++)
877  {
878  uint64_t guid = 0;
879 
880  guid = rom1394_get_guid(item.m_handle, node);
881  item.m_port = port;
882  item.m_node = node;
883  UpdateDeviceListItem(guid, &item);
884  guid_online[guid] = true;
885  }
886 
887  raw1394_destroy_handle(item.m_handle);
888 
889  item.m_handle = raw1394_new_handle();
890  if (!item.m_handle)
891  {
892  LOG(VB_GENERAL, LOG_ERR, QString("LinuxFirewireDevice: ") +
893  "Couldn't get handle " +
894  QString("(after setting port %1").arg(port) + ENO);
895  item.m_handle = nullptr;
896  break;
897  }
898 
899  numcards = raw1394_get_port_info(item.m_handle, port_info, 16);
900  }
901 
902  if (item.m_handle)
903  {
904  raw1394_destroy_handle(item.m_handle);
905  item.m_handle = nullptr;
906  }
907 
908  item.m_port = -1;
909  item.m_node = -1;
910  for (auto it = m_priv->m_devices.begin(); it != m_priv->m_devices.end(); ++it)
911  {
912  if (!guid_online[it.key()])
913  UpdateDeviceListItem(it.key(), &item);
914  }
915 
916  return true;
917 }
918 
919 void LinuxFirewireDevice::UpdateDeviceListItem(uint64_t guid, void *pitem)
920 {
921  avcinfo_list_t::iterator it = m_priv->m_devices.find(guid);
922 
923  if (it == m_priv->m_devices.end())
924  {
925  auto *ptr = new LinuxAVCInfo();
926 
927  LOG(VB_RECORD, LOG_INFO, LOC + QString("Adding 0x%1").arg(guid,0,16));
928 
929  m_priv->m_devices[guid] = ptr;
930  it = m_priv->m_devices.find(guid);
931  }
932 
933  if (it != m_priv->m_devices.end())
934  {
935  dev_item &item = *((dev_item*) pitem);
936  LOG(VB_RECORD, LOG_INFO,
937  LOC + QString("Updating 0x%1 port: %2 node: %3")
938  .arg(guid,0,16).arg(item.m_port).arg(item.m_node));
939 
940  (*it)->Update(guid, item.m_handle, item.m_port, item.m_node);
941  }
942 }
943 
945 {
946  if (!m_priv)
947  return nullptr;
948 
949  avcinfo_list_t::iterator it = m_priv->m_devices.find(m_guid);
950  return (it == m_priv->m_devices.end()) ? nullptr : *it;
951 }
952 
954 {
955  if (!m_priv)
956  return nullptr;
957 
958  avcinfo_list_t::iterator it = m_priv->m_devices.find(m_guid);
959  return (it == m_priv->m_devices.end()) ? nullptr : *it;
960 }
961 
963  unsigned char *tspacket, int len, uint dropped, void *callback_data)
964 {
965  auto *fw = reinterpret_cast<LinuxFirewireDevice*>(callback_data);
966  if (!fw)
967  return 0;
968 
969  if (dropped)
970  fw->PrintDropped(dropped);
971 
972  if (len > 0)
973  fw->BroadcastToListeners(tspacket, len);
974 
975  return 1;
976 }
977 
978 static bool has_data(int fd, int msec)
979 {
980  fd_set rfds;
981  FD_ZERO(&rfds); // NOLINT(readability-isolate-declaration)
982  FD_SET(fd, &rfds);
983 
984  struct timeval tv {};
985  tv.tv_sec = msec / 1000;
986  tv.tv_usec = (msec % 1000) * 1000;
987 
988  int ready = select(fd + 1, &rfds, nullptr, nullptr, &tv);
989 
990  if (ready < 0)
991  LOG(VB_GENERAL, LOG_ERR, "LFireDev: Select Error" + ENO);
992 
993  return ready > 0;
994 }
995 
996 static QString speed_to_string(uint speed)
997 {
998  if (speed > 3)
999  return QString("Invalid Speed (%1)").arg(speed);
1000 
1001  static constexpr uint kSpeeds[] = { 100, 200, 400, 800 };
1002  return QString("%1Mbps").arg(kSpeeds[speed]);
1003 }
1004 
1006  raw1394handle_t handle, unsigned int generation)
1007 {
1008  QMutexLocker locker(&LFDPriv::s_lock);
1009 
1010  handle_to_lfd_t::iterator it = LFDPriv::s_handle_info.find(handle);
1011 
1012  if (it != LFDPriv::s_handle_info.end())
1013  (*it)->SignalReset(generation);
1014 
1015  return 0;
1016 }
void RemoveListener(TSDataListener *listener) override
This is a wrapper around QThread that does several additional things.
Definition: mthread.h:46
A QElapsedTimer based timer to replace use of QTime as a timer.
Definition: mythtimer.h:13
virtual void RemoveListener(TSDataListener *listener)
MThread * m_portHandlerThread
bool SendAVCCommand(const vector< uint8_t > &cmd, vector< uint8_t > &result, int retry_cnt) override
MythTimer m_resetTimer
static void add_handle(raw1394handle_t handle, LinuxFirewireDevice *dev)
bool ResetBus(void) override
void UpdateDeviceListItem(uint64_t guid, void *pitem)
bool m_isPortHandlerRunning
bool wait(unsigned long time=ULONG_MAX)
Wait for the MThread to exit, with a maximum timeout.
Definition: mthread.cpp:311
LinuxFirewireDevice(uint64_t guid, uint subunitid, uint speed, bool use_p2p, uint av_buffer_size_in_bytes=0)
static const uint kConnectionP2P
static const uint kBroadcastChannel
bool IsAVStreamOpen(void) const
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
bool SendAVCCommand(const vector< uint8_t > &cmd, vector< uint8_t > &result, int retry_cnt) override
static vector< AVCInfo > GetSTBList(void)
bool GetSubunitInfo(void)
Definition: avcinfo.cpp:53
virtual void AddListener(TSDataListener *listener)
void run(void) override
bool SetAVStreamSpeed(uint speed)
QMap< raw1394handle_t, LinuxFirewireDevice * > handle_to_lfd_t
static void remove_handle(raw1394handle_t handle)
static int linux_firewire_device_bus_reset_handler(raw1394handle_t handle, uint generation)
bool SetAVStreamBufferSize(uint size_in_bytes)
vector< TSDataListener * > m_listeners
bool IsPortOpen(void) const
Definition: linuxavcinfo.h:39
static QMutex s_lock
friend int linux_firewire_device_tspacket_handler(unsigned char *tspacket, int len, uint dropped, void *callback_data)
QWaitCondition m_portHandlerWait
void AddListener(TSDataListener *listener) override
iec61883_mpeg2_t m_avstream
#define kResetTimeout
void * linux_firewire_device_port_handler_thunk(void *param)
unsigned int uint
Definition: compat.h:140
vector< AVCInfo > GetSTBListPrivate(void)
#define ENO
This can be appended to the LOG args with "+".
Definition: mythlogging.h:99
LinuxAVCInfo * GetInfoPtr(void)
static const uint kMaxBufferedPackets
static QString speed_to_string(uint speed)
QMap< uint64_t, DarwinAVCInfo * > avcinfo_list_t
Definition: darwinavcinfo.h:65
bool IsNodeOpen(void) const
QMutex m_startStopPortHandlerLock
int GetNumSetting(const QString &key, int defaultval=0)
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
static const unsigned int kSize
Definition: tspacket.h:220
#define LOC
bool GetBoolSetting(const QString &key, bool defaultval=false)
bool OpenPort(void) override
int elapsed(void)
Returns milliseconds elapsed since last start() or restart()
Definition: mythtimer.cpp:90
void SignalReset(uint generation)
void PrintDropped(uint dropped_packets)
int GetNode(void) const
Returns remote node.
Definition: linuxavcinfo.h:42
static const uint kConnectionBroadcast
void start(QThread::Priority p=QThread::InheritPriority)
Tell MThread to start running the thread in the near future.
Definition: mthread.cpp:294
static handle_to_lfd_t s_handle_info
#define kNoDataTimeout
static bool has_data(int fd, int msec)
avcinfo_list_t m_devices
raw1394handle_t m_handle
void start(void)
starts measuring elapsed time.
Definition: mythtimer.cpp:47
int linux_firewire_device_tspacket_handler(unsigned char *tspacket, int len, uint dropped, void *callback_data)
bool ClosePort(void) override
#define output
bool IsPortOpen(void) const override