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