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 <QDateTime>
32
33// MythTV headers
36
37#include "firewirerecorder.h"
38#include "linuxavcinfo.h"
39#include "linuxfirewiredevice.h"
40
41#define LOC QString("LFireDev(%1): ").arg(guid_to_string(m_guid))
42
43static constexpr std::chrono::milliseconds kNoDataTimeout { 50ms };
44static constexpr std::chrono::milliseconds kResetTimeout { 1s };
45
46using handle_to_lfd_t = QHash<raw1394handle_t,LinuxFirewireDevice*>;
47
49{
50 public:
51 LFDPriv() = default;
52
54 {
55 for (const auto & device : std::as_const(m_devices))
56 delete device;
57 m_devices.clear();
58
60 {
63 }
64 }
65
67 bool m_resetTimerOn {false};
69
70 bool m_runPortHandler {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};
81
82 bool m_isP2pNodeOpen {false};
83 bool m_isBcastNodeOpen {false};
84 bool m_isStreaming {false};
85
87
88 avcinfo_list_t m_devices;
89
90 static QMutex s_lock;
92};
93QMutex LFDPriv::s_lock;
95
96static void add_handle(raw1394handle_t handle, LinuxFirewireDevice *dev)
97{
98 QMutexLocker slocker(&LFDPriv::s_lock);
99 LFDPriv::s_handle_info[handle] = dev;
100}
101
102static void remove_handle(raw1394handle_t handle)
103{
104 QMutexLocker slocker(&LFDPriv::s_lock);
105 LFDPriv::s_handle_info.remove(handle);
106}
107
111const uint LinuxFirewireDevice::kMaxBufferedPackets = 4 * 1024 * 1024 / 188;
112
113// callback function for libiec61883
115 unsigned char *tspacket, int len, uint dropped, void *callback_data);
117static bool has_data(int fd, std::chrono::milliseconds msec);
118static 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
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 {
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
259 GetInfoPtr()->GetSubunitInfo();
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
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_openPortCnt < 1)
300 return false;
301
303
304 if (m_openPortCnt != 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
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
343
344 if (!m_listeners.empty())
345 {
346 OpenNode();
347 OpenAVStream();
349 }
350}
351
353{
354 QMutexLocker locker(&m_lock);
355
357
358 if (m_listeners.empty())
359 {
362 CloseNode();
363 }
364}
365
367 const std::vector<uint8_t> &cmd,
368 std::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{
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
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,
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)
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{
470 return false;
471
473 return true;
474
475 if (m_priv->m_avstream)
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
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
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(2500us);
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() > 100ms);
598 if (handle_reset)
599 m_priv->m_resetTimerOn = false;
600 LFDPriv::s_lock.unlock();
601
602 if (handle_reset)
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 {
634
635 LOG(VB_GENERAL, LOG_WARNING, LOC + QString("No Input in %1 msec...")
636 .arg(m_priv->m_noDataCnt * kNoDataTimeout.count()));
637 }
638
639 // Confirm that we won't block, now that we have the lock...
640 if (ready && has_data(fwfd, 1ms))
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{
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{
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 = std::max(size_in_bytes, 50 * TSPacket::kSize);
716 size_t buffered_packets = std::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),
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
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
800std::vector<AVCInfo> LinuxFirewireDevice::GetSTBList(void)
801{
802 std::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 std::vector<AVCInfo> list;
823
824 for (const auto & device : std::as_const(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
840{
841 raw1394handle_t m_handle;
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 std::array<raw1394_portinfo,16> port_info {};
859 int numcards = raw1394_get_port_info(item.m_handle, port_info.data(),
860 port_info.size());
861 if (numcards < 1)
862 {
863 raw1394_destroy_handle(item.m_handle);
864 return true;
865 }
866
867 std::map<uint64_t,bool> guid_online;
868 for (int port = 0; port < numcards; port++)
869 {
870 if (raw1394_set_port(item.m_handle, port) < 0)
871 {
872 LOG(VB_GENERAL, LOG_ERR, QString("LinuxFirewireDevice: "
873 "Couldn't set port to %1").arg(port));
874 continue;
875 }
876
877 for (int node = 0; node < raw1394_get_nodecount(item.m_handle); node++)
878 {
879 uint64_t guid = 0;
880
881 guid = rom1394_get_guid(item.m_handle, node);
882 item.m_port = port;
883 item.m_node = node;
884 UpdateDeviceListItem(guid, &item);
885 guid_online[guid] = true;
886 }
887
888 raw1394_destroy_handle(item.m_handle);
889
890 item.m_handle = raw1394_new_handle();
891 if (!item.m_handle)
892 {
893 LOG(VB_GENERAL, LOG_ERR, QString("LinuxFirewireDevice: ") +
894 "Couldn't get handle " +
895 QString("(after setting port %1").arg(port) + ENO);
896 item.m_handle = nullptr;
897 break;
898 }
899
900 numcards = raw1394_get_port_info(item.m_handle, port_info.data(),
901 port_info.size());
902 }
903
904 if (item.m_handle)
905 {
906 raw1394_destroy_handle(item.m_handle);
907 item.m_handle = nullptr;
908 }
909
910 item.m_port = -1;
911 item.m_node = -1;
912 for (auto it = m_priv->m_devices.begin(); it != m_priv->m_devices.end(); ++it)
913 {
914 if (!guid_online[it.key()])
915 UpdateDeviceListItem(it.key(), &item);
916 }
917
918 return true;
919}
920
921void LinuxFirewireDevice::UpdateDeviceListItem(uint64_t guid, void *pitem)
922{
923 avcinfo_list_t::iterator it = m_priv->m_devices.find(guid);
924
925 if (it == m_priv->m_devices.end())
926 {
927 auto *ptr = new LinuxAVCInfo();
928
929 LOG(VB_RECORD, LOG_INFO, LOC + QString("Adding 0x%1").arg(guid,0,16));
930
931 m_priv->m_devices[guid] = ptr;
932 it = m_priv->m_devices.find(guid);
933 }
934
935 if (it != m_priv->m_devices.end())
936 {
937 dev_item &item = *((dev_item*) pitem);
938 LOG(VB_RECORD, LOG_INFO,
939 LOC + QString("Updating 0x%1 port: %2 node: %3")
940 .arg(guid,0,16).arg(item.m_port).arg(item.m_node));
941
942 (*it)->Update(guid, item.m_handle, item.m_port, item.m_node);
943 }
944}
945
947{
948 if (!m_priv)
949 return nullptr;
950
951 avcinfo_list_t::iterator it = m_priv->m_devices.find(m_guid);
952 return (it == m_priv->m_devices.end()) ? nullptr : *it;
953}
954
955const LinuxAVCInfo *LinuxFirewireDevice::GetInfoPtr(void) const
956{
957 if (!m_priv)
958 return nullptr;
959
960 avcinfo_list_t::iterator it = m_priv->m_devices.find(m_guid);
961 return (it == m_priv->m_devices.end()) ? nullptr : *it;
962}
963
965 unsigned char *tspacket, int len, uint dropped, void *callback_data)
966{
967 auto *fw = reinterpret_cast<LinuxFirewireDevice*>(callback_data);
968 if (!fw)
969 return 0;
970
971 if (dropped)
972 fw->PrintDropped(dropped);
973
974 if (len > 0)
975 fw->BroadcastToListeners(tspacket, len);
976
977 return 1;
978}
979
980static bool has_data(int fd, std::chrono::milliseconds msec)
981{
982 fd_set rfds;
983 FD_ZERO(&rfds); // NOLINT(readability-isolate-declaration)
984 FD_SET(fd, &rfds);
985
986 struct timeval tv {};
987 tv.tv_sec = msec.count() / 1000;
988 tv.tv_usec = (msec.count() % 1000) * 1000;
989
990 int ready = select(fd + 1, &rfds, nullptr, nullptr, &tv);
991
992 if (ready < 0)
993 LOG(VB_GENERAL, LOG_ERR, "LFireDev: Select Error" + ENO);
994
995 return ready > 0;
996}
997
998static QString speed_to_string(uint speed)
999{
1000 if (speed > 3)
1001 return QString("Invalid Speed (%1)").arg(speed);
1002
1003 static constexpr std::array<const uint,4> kSpeeds { 100, 200, 400, 800 };
1004 return QString("%1Mbps").arg(kSpeeds[speed]);
1005}
1006
1008 raw1394handle_t handle, unsigned int generation)
1009{
1010 QMutexLocker locker(&LFDPriv::s_lock);
1011
1012 handle_to_lfd_t::iterator it = LFDPriv::s_handle_info.find(handle);
1013
1014 if (it != LFDPriv::s_handle_info.end())
1015 (*it)->SignalReset(generation);
1016
1017 return 0;
1018}
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:283
bool wait(std::chrono::milliseconds time=std::chrono::milliseconds::max())
Wait for the MThread to exit, with a maximum timeout.
Definition: mthread.cpp:300
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: freesurround.h:24
#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