14#include <IOKit/IOMessage.h>
15#include <IOKit/IOKitLib.h>
16#include <IOKit/firewire/IOFireWireLib.h>
17#include <IOKit/firewire/IOFireWireLibIsoch.h>
18#include <IOKit/firewire/IOFireWireFamilyCommon.h>
19#include <IOKit/avc/IOFireWireAVCLib.h>
20#include <CoreServices/CoreServices.h>
29#include "libmythbase/mythconfig.h"
36#include <AVCVideoServices/StringLogger.h>
37#include <AVCVideoServices/AVSShared.h>
38#include <AVCVideoServices/MPEG2Receiver.h>
41#define IOMainPort IOMasterPort
49 MPEG2Receiver **ppReceiver,
50 DataPushProc dataPushProcHandler,
51 void *pDataPushProcRefCon = nil,
52 MPEG2ReceiverMessageProc messageProcHandler = nil,
53 void *pMessageProcRefCon = nil,
54 StringLogger *stringLogger = nil,
55 IOFireWireLibNubRef nubInterface = nil,
56 unsigned int cyclesPerSegment =
57 kCyclesPerReceiveSegment,
58 unsigned int numSegments =
60 bool doIRMAllocations =
false);
64#define LOC QString("DFireDev(%1): ").arg(guid_to_string(m_guid))
66#define kAnyAvailableIsochChannel 0xFFFFFFFF
71 UInt32 tsPacketCount, UInt32 **ppBuf,
void *callback_data);
76 UInt32 param2,
void *callback_data);
124 uint64_t guid,
uint subunitid,
uint speed) :
134 LOG(VB_GENERAL, LOG_ERR,
LOC +
"dtor called with open port");
151 mach_port_t master_port;
152 int ret =
IOMainPort(bootstrap_port, &master_port);
153 if (kIOReturnSuccess == ret)
161 kCFRunLoopDefaultMode);
163 ret = IOServiceAddMatchingNotification(
165 IOServiceMatching(
"IOFireWireAVCUnit"),
169 if (kIOReturnSuccess == ret)
174 if (kIOReturnSuccess == ret)
177 QMutexLocker locker(&
m_lock);
193 std::this_thread::sleep_for(5ms);
226 std::this_thread::sleep_for(100ms);
233 QMutexLocker locker(&
m_lock);
235 LOG(VB_RECORD, LOG_INFO,
LOC +
"OpenPort()");
247 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Unable to start firewire thread.");
253 LOG(VB_GENERAL, LOG_ERR,
LOC +
"No IEEE-1394 device with our GUID");
259 LOG(VB_RECORD, LOG_INFO,
LOC +
"Opening AVC Device");
265 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"No STB at guid: 0x%1")
275 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Unable to get handle for port");
285 LOG(VB_GENERAL, LOG_WARNING,
LOC +
"Failed to query local node");
291 LOG(VB_GENERAL, LOG_WARNING,
LOC +
"Failed to query remote node");
303 QMutexLocker locker(&
m_lock);
305 LOG(VB_RECORD, LOG_INFO,
LOC +
"ClosePort()");
317 LOG(VB_RECORD, LOG_INFO,
LOC +
"Closing AVC Device");
335 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Max Speed: %1, Our speed: %2")
341 LOG(VB_GENERAL, LOG_INFO,
LOC +
342 QString(
"STB is %1already streaming on fwchan: %2")
343 .arg(streaming?
"":
"not ").arg(fwchan));
354 AVS::kCyclesPerReceiveSegment,
355 AVS::kNumReceiveSegments,
358 if (kIOReturnSuccess != ret)
360 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Couldn't create A/V stream object");
372 IOFireWireLibDeviceRef fw_handle =
GetInfoPtr()->fw_handle;
374 if ((*fw_handle)->version < 4)
377 io_object_t dev = (*fw_handle)->GetDevice(fw_handle);
381 int ret = (*fw_handle)->ReadQuadlet(
382 fw_handle, dev, &addr, (UInt32*) &val,
false, 0);
383 val = EndianU32_BtoN(val);
385 return (ret == kIOReturnSuccess) ? (int)((val>>30) & 0x3) : -1;
388 uint32_t generation = 0;
390 int ret = (*fw_handle)->GetBusGeneration(fw_handle, (UInt32*)&generation);
391 if (kIOReturnSuccess == ret)
393 ret = (*fw_handle)->GetSpeedBetweenNodes(
397 return (ret == kIOReturnSuccess) ? (int)speed : -1;
402 IOFireWireLibDeviceRef fw_handle =
GetInfoPtr()->fw_handle;
403 io_object_t dev = (*fw_handle)->GetDevice(fw_handle);
407 int ret = (*fw_handle)->ReadQuadlet(
408 fw_handle, dev, &addr, (UInt32*) &val,
false, 0);
409 val = EndianU32_BtoN(val);
411 if (ret != kIOReturnSuccess)
414 if (val & (kIOFWPCRBroadcast | kIOFWPCRP2PCount))
417 *fw_channel = (val & kIOFWPCRChannel) >> kIOFWPCRChannelPhase;
432 LOG(VB_RECORD, LOG_INFO,
LOC +
"Destroying A/V stream object");
446 LOG(VB_GENERAL, LOG_DEBUG,
LOC +
"ResetBus() -- begin");
451 IOFireWireLibDeviceRef fw_handle =
GetInfoPtr()->fw_handle;
452 bool ok = (*fw_handle)->BusReset(fw_handle) == kIOReturnSuccess;
455 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Bus Reset failed" +
ENO);
457 LOG(VB_GENERAL, LOG_DEBUG,
LOC +
"ResetBus() -- end");
467 LOG(VB_RECORD, LOG_INFO,
LOC +
"Starting A/V streaming");
471 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Starting A/V streaming: FAILED");
481 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Starting A/V streaming: %1")
492 LOG(VB_RECORD, LOG_INFO,
LOC +
"Stopping A/V streaming");
499 LOG(VB_RECORD, LOG_ERR,
LOC +
"Failed to stop A/V streaming");
503 LOG(VB_RECORD, LOG_INFO,
LOC +
"Stopped A/V streaming");
508 std::vector<uint8_t> &result,
511 return GetInfoPtr()->SendAVCCommand(cmd, result, retry_cnt);
516 QMutexLocker locker(&
m_lock);
526 QMutexLocker locker(&
m_lock);
536 QMutexLocker locker(&
m_lock);
548 const unsigned char *data,
uint dataSize)
550 QMutexLocker locker(&
m_lock);
565 LOG(VB_GENERAL, LOG_WARNING,
LOC + QString(
"No Input in %1 msecs")
577 uint32_t msg, uint32_t param1, uint32_t param2)
581 if (AVS::kMpeg2ReceiverAllocateIsochPort == msg)
584 int fw_channel = param2;
587 plug_number, fw_channel, speed,
true,
false);
589 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"AllocateIsochPort(%1,%2) %3")
590 .arg(fw_channel).arg(speed).arg(((ok)?
"ok":
"error")));
592 else if (AVS::kMpeg2ReceiverReleaseIsochPort == msg)
596 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"ReleaseIsochPort %1")
597 .arg((kIOReturnSuccess == ret)?
"ok":
"error"));
599 else if (AVS::kMpeg2ReceiverDCLOverrun == msg)
601 LOG(VB_GENERAL, LOG_ERR,
LOC +
"DCL Overrun");
603 else if (AVS::kMpeg2ReceiverReceivedBadPacket == msg)
605 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Received Bad Packet");
609 LOG(VB_GENERAL, LOG_INFO,
LOC +
610 QString(
"Streaming Message: %1").arg(msg));
616 std::vector<AVCInfo> list;
638 LOG(VB_GENERAL, LOG_DEBUG,
LOC +
"GetSTBListPrivate -- begin");
640 QMutexLocker locker(&
m_lock);
642 LOG(VB_GENERAL, LOG_DEBUG,
LOC +
"GetSTBListPrivate -- got lock");
645 std::vector<AVCInfo> list;
652 list.push_back(*dev);
657 LOG(VB_GENERAL, LOG_DEBUG,
LOC +
"GetSTBListPrivate -- end");
664 QMutexLocker locker(&
m_lock);
670 auto *ptr =
new DarwinAVCInfo();
672 LOG(VB_GENERAL, LOG_INFO,
LOC +
673 QString(
"Adding 0x%1").arg(guid, 0, 16));
681 io_object_t &item = *((io_object_t*) pitem);
682 LOG(VB_GENERAL, LOG_INFO,
LOC +
683 QString(
"Updating 0x%1").arg(guid, 0, 16));
703 uint plug_number,
int new_fw_chan,
int new_speed,
704 bool add_plug,
bool remove_plug)
709 IOFireWireLibDeviceRef fw_handle =
GetInfoPtr()->fw_handle;
713 io_object_t dev = (*fw_handle)->GetDevice(fw_handle);
716 uint low_addr = kPCRBaseAddress + 4 + (plug_number << 2);
718 uint32_t old_plug_val;
719 if (kIOReturnSuccess != (*fw_handle)->ReadQuadlet(
720 fw_handle, dev, &addr, (UInt32*) &old_plug_val,
false, 0))
724 old_plug_val = EndianU32_BtoN(old_plug_val);
726 int old_plug_cnt = (old_plug_val >> 24) & 0x3f;
727 int old_fw_chan = (old_plug_val >> 16) & 0x3f;
728 int old_speed = (old_plug_val >> 14) & 0x03;
730 int new_plug_cnt = (int) old_plug_cnt;
731 new_plug_cnt += ((add_plug) ? 1 : 0) - ((remove_plug) ? 1 : 0);
732 if ((new_plug_cnt > 0x3f) || (new_plug_cnt < 0))
734 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"Invalid Plug Count %1")
739 new_fw_chan = (new_fw_chan >= 0) ? new_fw_chan : old_fw_chan;
740 if (old_plug_cnt && (new_fw_chan != old_fw_chan))
742 LOG(VB_GENERAL, LOG_WARNING,
LOC +
743 "Ignoring FWChan change request, plug already open");
745 new_fw_chan = old_fw_chan;
748 new_speed = (new_speed >= 0) ? new_speed : old_speed;
749 if (old_plug_cnt && (new_speed != old_speed))
751 LOG(VB_GENERAL, LOG_WARNING,
LOC +
752 "Ignoring speed change request, plug already open");
754 new_speed = old_speed;
757 uint32_t new_plug_val = old_plug_val;
759 new_plug_val &= ~(0x3f<<24);
760 new_plug_val &= (remove_plug) ? ~kIOFWPCRBroadcast : ~0x0;
761 new_plug_val |= (new_plug_cnt & 0x3f) << 24;
763 new_plug_val &= ~(0x3f<<16);
764 new_plug_val |= (new_fw_chan & 0x3F) << 16;
766 new_plug_val &= ~(0x03<<14);
767 new_plug_val |= (new_speed & 0x03) << 14;
769 old_plug_val = EndianU32_NtoB(old_plug_val);
770 new_plug_val = EndianU32_NtoB(new_plug_val);
772 return (kIOReturnSuccess == (*fw_handle)->CompareSwap(
773 fw_handle, dev, &addr, old_plug_val, new_plug_val,
false, 0));
792 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Reset: Failed to reconnect");
794 LOG(VB_RECORD, LOG_INFO,
LOC +
"Reset: Reconnected succesfully");
798 uint plug_number,
int fw_chan,
int speed,
799 bool add_plug,
bool remove_plug,
uint retry_cnt)
806 for (
uint i = 0; (i < retry_cnt) && !ok; i++)
809 plug_number, fw_chan, speed, add_plug, remove_plug);
819 QString loc =
LOC +
"HandleDeviceChange: ";
821 if (kIOMessageServiceIsTerminated == messageType)
823 LOG(VB_RECORD, LOG_INFO, loc +
"Disconnect");
828 if (kIOMessageServiceIsAttemptingOpen == messageType)
830 LOG(VB_RECORD, LOG_INFO, loc +
"Attempting open");
834 if (kIOMessageServiceWasClosed == messageType)
836 LOG(VB_RECORD, LOG_INFO, loc +
"Device Closed");
841 if (kIOMessageServiceIsSuspended == messageType)
843 LOG(VB_RECORD, LOG_INFO, loc +
"kIOMessageServiceIsSuspended");
848 if (kIOMessageServiceIsResumed == messageType)
854 if (kIOMessageServiceIsTerminated == messageType)
855 LOG(VB_RECORD, LOG_INFO, loc +
"kIOMessageServiceIsTerminated");
856 else if (kIOMessageServiceIsRequestingClose == messageType)
857 LOG(VB_RECORD, LOG_INFO, loc +
"kIOMessageServiceIsRequestingClose");
858 else if (kIOMessageServiceIsAttemptingOpen == messageType)
859 LOG(VB_RECORD, LOG_INFO, loc +
"kIOMessageServiceIsAttemptingOpen");
860 else if (kIOMessageServiceWasClosed == messageType)
861 LOG(VB_RECORD, LOG_INFO, loc +
"kIOMessageServiceWasClosed");
862 else if (kIOMessageServiceBusyStateChange == messageType)
863 LOG(VB_RECORD, LOG_INFO, loc +
"kIOMessageServiceBusyStateChange");
864 else if (kIOMessageCanDevicePowerOff == messageType)
865 LOG(VB_RECORD, LOG_INFO, loc +
"kIOMessageCanDevicePowerOff");
866 else if (kIOMessageDeviceWillPowerOff == messageType)
867 LOG(VB_RECORD, LOG_INFO, loc +
"kIOMessageDeviceWillPowerOff");
868 else if (kIOMessageDeviceWillNotPowerOff == messageType)
869 LOG(VB_RECORD, LOG_INFO, loc +
"kIOMessageDeviceWillNotPowerOff");
870 else if (kIOMessageDeviceHasPoweredOn == messageType)
871 LOG(VB_RECORD, LOG_INFO, loc +
"kIOMessageDeviceHasPoweredOn");
872 else if (kIOMessageCanSystemPowerOff == messageType)
873 LOG(VB_RECORD, LOG_INFO, loc +
"kIOMessageCanSystemPowerOff");
874 else if (kIOMessageSystemWillPowerOff == messageType)
875 LOG(VB_RECORD, LOG_INFO, loc +
"kIOMessageSystemWillPowerOff");
876 else if (kIOMessageSystemWillNotPowerOff == messageType)
877 LOG(VB_RECORD, LOG_INFO, loc +
"kIOMessageSystemWillNotPowerOff");
878 else if (kIOMessageCanSystemSleep == messageType)
879 LOG(VB_RECORD, LOG_INFO, loc +
"kIOMessageCanSystemSleep");
880 else if (kIOMessageSystemWillSleep == messageType)
881 LOG(VB_RECORD, LOG_INFO, loc +
"kIOMessageSystemWillSleep");
882 else if (kIOMessageSystemWillNotSleep == messageType)
883 LOG(VB_RECORD, LOG_INFO, loc +
"kIOMessageSystemWillNotSleep");
884 else if (kIOMessageSystemHasPoweredOn == messageType)
885 LOG(VB_RECORD, LOG_INFO, loc +
"kIOMessageSystemHasPoweredOn");
886 else if (kIOMessageSystemWillRestart == messageType)
887 LOG(VB_RECORD, LOG_INFO, loc +
"kIOMessageSystemWillRestart");
890 LOG(VB_RECORD, LOG_ERR, loc + QString(
"unknown message 0x%1")
891 .arg(messageType, 0, 16));
914 ProcessNoDataMessage();
916 return kIOReturnSuccess;
920 UInt32 param2,
void *callback_data)
923 ProcessStreamingMessage(msg, param1, param2);
931 return kIOReturnBadArgument;
933 for (uint32_t i = 0; i < tsPacketCount; ++i)
936 return kIOReturnSuccess;
940 UInt32 tsPacketCount, UInt32 **ppBuf,
void *callback_data)
943 tsPacketCount, (uint32_t**)ppBuf, callback_data);
951 while ((it = IOIteratorNext(deviter)))
955 CFMutableDictionaryRef props;
956 int ret = IORegistryEntryCreateCFProperties(
957 it, &props, kCFAllocatorDefault, kNilOptions);
959 if (kIOReturnSuccess == ret)
961 auto GUIDDesc = (CFNumberRef)
962 CFDictionaryGetValue(props, CFSTR(
"GUID"));
963 CFNumberGetValue(GUIDDesc, kCFNumberSInt64Type, &guid);
972 LOG(VB_RECORD, LOG_INFO, QString(
"MPEG2Receiver: %1").arg(msg));
DFDPriv & operator=(const DFDPriv &)=delete
AVS::MPEG2Receiver * m_avstream
CFRunLoopSourceRef m_notify_source
bool m_controller_thread_running
DFDPriv(const DFDPriv &)=delete
CFRunLoopRef m_controller_thread_cf_ref
IONotificationPortRef m_notify_port
AVS::StringLogger * m_logger
pthread_t m_controller_thread
MythTimer m_no_data_timer
bool OpenPort(void) override
bool IsPortOpen(void) const override
friend int dfd_no_data_notification(void *callback_data)
void HandleDeviceChange(uint messageType)
bool ClosePort(void) override
std::vector< AVCInfo > GetSTBListPrivate(void)
bool UpdatePlugRegister(uint plug_number, int fw_chan, int speed, bool add_plug, bool remove_plug, uint retry_cnt=4)
bool SendAVCCommand(const std::vector< uint8_t > &cmd, std::vector< uint8_t > &result, int) override
DarwinAVCInfo * GetInfoPtr(void)
friend void * dfd_controller_thunk(void *callback_data)
void ProcessNoDataMessage(void)
void HandleBusReset(void)
static std::vector< AVCInfo > GetSTBList(void)
void RemoveListener(TSDataListener *) override
DarwinFirewireDevice(uint64_t guid, uint subunitid, uint speed)
void BroadcastToListeners(const unsigned char *data, uint dataSize) override
bool IsSTBStreaming(uint *fw_channel=nullptr)
bool StartStreaming(void)
bool IsAVStreamOpen(void) const
bool ResetBus(void) override
void ProcessStreamingMessage(uint32_t msg, uint32_t param1, uint32_t param2)
bool UpdatePlugRegisterPrivate(uint plug_number, int fw_chan, int new_speed, bool add_plug, bool remove_plug)
void UpdateDeviceListItem(uint64_t guid, void *item)
void StartController(void)
void StopController(void)
friend void dfd_stream_msg(UInt32 msg, UInt32 param1, UInt32 param2, void *callback_data)
void AddListener(TSDataListener *) override
virtual void AddListener(TSDataListener *listener)
virtual void RemoveListener(TSDataListener *listener)
std::vector< TSDataListener * > m_listeners
virtual void BroadcastToListeners(const unsigned char *data, uint dataSize)
static void ThreadCleanup(void)
This is to be called on exit in those few threads that haven't been ported to MThread.
static void ThreadSetup(const QString &name)
This is to be called on startup in those few threads that haven't been ported to MThread.
A QElapsedTimer based timer to replace use of QTime as a timer.
std::chrono::milliseconds elapsed(void)
Returns milliseconds elapsed since last start() or restart()
void start(void)
starts measuring elapsed time.
#define kAnyAvailableIsochChannel
static constexpr std::chrono::milliseconds kNoDataTimeout
int dfd_no_data_notification(void *callback_data)
static void dfd_streaming_log_message(char *msg)
void * dfd_controller_thunk(void *callback_data)
static IOReturn dfd_tspacket_handler_thunk(UInt32 tsPacketCount, UInt32 **ppBuf, void *callback_data)
void dfd_update_device_list_item(DarwinFirewireDevice *dev, uint64_t guid, void *item)
#define IOMainPort
DarwinFirewireChannel Copyright (c) 2005 by Jim Westfall SA3250HD support Copyright (c) 2005 by Matt ...
static constexpr std::chrono::milliseconds kResetTimeout
int dfd_tspacket_handler(uint tsPacketCount, uint32_t **ppBuf, void *callback_data)
static void dfd_update_device_list(void *dfd, io_iterator_t deviter)
void dfd_stream_msg(UInt32 msg, UInt32 param1, UInt32 param2, void *callback_data)
#define ENO
This can be appended to the LOG args with "+".
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
IOReturn CreateMPEG2Receiver(MPEG2Receiver **ppReceiver, DataPushProc dataPushProcHandler, void *pDataPushProcRefCon=nil, MPEG2ReceiverMessageProc messageProcHandler=nil, void *pMessageProcRefCon=nil, StringLogger *stringLogger=nil, IOFireWireLibNubRef nubInterface=nil, unsigned int cyclesPerSegment=kCyclesPerReceiveSegment, unsigned int numSegments=kNumReceiveSegments, bool doIRMAllocations=false)
IOReturn DestroyMPEG2Receiver(MPEG2Receiver *pReceiver)