MythTV  0.26-pre
darwinfirewiredevice.cpp
Go to the documentation of this file.
00001 
00009 // POSIX headers
00010 #include <pthread.h>
00011 
00012 // OS X headers
00013 #undef always_inline
00014 #include <IOKit/IOMessage.h>
00015 #include <IOKit/IOKitLib.h>
00016 #include <IOKit/firewire/IOFireWireLib.h>
00017 #include <IOKit/firewire/IOFireWireLibIsoch.h>
00018 #include <IOKit/firewire/IOFireWireFamilyCommon.h>
00019 #include <IOKit/avc/IOFireWireAVCLib.h>
00020 #include <CoreServices/CoreServices.h>   // for EndianU32_BtoN() etc.
00021 
00022 // Std C++ headers
00023 #include <algorithm>
00024 #include <vector>
00025 using namespace std;
00026 
00027 // MythTV headers
00028 #include "darwinfirewiredevice.h"
00029 #include "darwinavcinfo.h"
00030 #include "mythlogging.h"
00031 #include "mthread.h"
00032 
00033 // Apple Firewire example headers
00034 #include <AVCVideoServices/StringLogger.h>
00035 #include <AVCVideoServices/AVSShared.h>
00036 #include <AVCVideoServices/MPEG2Receiver.h>
00037 
00038 // header not used because it also requires MPEG2Transmitter.h
00039 //#include <AVCVideoServices/FireWireMPEG.h>
00040 namespace AVS
00041 {
00042     IOReturn CreateMPEG2Receiver(
00043         MPEG2Receiver           **ppReceiver,
00044         DataPushProc              dataPushProcHandler,
00045         void                     *pDataPushProcRefCon = nil,
00046         MPEG2ReceiverMessageProc  messageProcHandler  = nil,
00047         void                     *pMessageProcRefCon  = nil,
00048         StringLogger             *stringLogger        = nil,
00049         IOFireWireLibNubRef       nubInterface        = nil,
00050         unsigned int              cyclesPerSegment    =
00051             kCyclesPerReceiveSegment,
00052         unsigned int              numSegments         =
00053             kNumReceiveSegments,
00054         bool                      doIRMAllocations    = false);
00055     IOReturn DestroyMPEG2Receiver(MPEG2Receiver *pReceiver);
00056 }
00057 
00058 #define LOC      QString("DFireDev(%1): ").arg(guid_to_string(m_guid))
00059 
00060 #define kAnyAvailableIsochChannel 0xFFFFFFFF
00061 #define kNoDataTimeout            300  /* msec */
00062 #define kResetTimeout             1500 /* msec */
00063 
00064 static IOReturn dfd_tspacket_handler_thunk(
00065     UInt32 tsPacketCount, UInt32 **ppBuf, void *callback_data);
00066 static void dfd_update_device_list(void *dfd, io_iterator_t iterator);
00067 static void dfd_streaming_log_message(char *pString);
00068 void *dfd_controller_thunk(void *param);
00069 void dfd_stream_msg(UInt32 msg, UInt32 param1,
00070                     UInt32 param2, void *callback_data);
00071 int dfd_no_data_notification(void *callback_data);
00072 
00073 class DFDPriv
00074 {
00075   public:
00076     DFDPriv() :
00077         controller_thread_cf_ref(NULL), controller_thread_running(false),
00078         notify_port(NULL), notify_source(NULL), deviter(NULL),
00079         actual_fwchan(-1), is_streaming(false), avstream(NULL), logger(NULL),
00080         no_data_cnt(0), no_data_timer_set(false)
00081     {
00082         logger = new AVS::StringLogger(dfd_streaming_log_message);
00083     }
00084 
00085     ~DFDPriv()
00086     {
00087         avcinfo_list_t::iterator it = devices.begin();
00088         for (; it != devices.end(); ++it)
00089             delete (*it);
00090         devices.clear();
00091 
00092         if (logger)
00093         {
00094             delete logger;
00095             logger = NULL;
00096         }
00097     }
00098 
00099     pthread_t                 controller_thread;
00100     CFRunLoopRef              controller_thread_cf_ref;
00101     bool                      controller_thread_running;
00102 
00103     IONotificationPortRef     notify_port;
00104     CFRunLoopSourceRef        notify_source;
00105     io_iterator_t             deviter;
00106 
00107     int                       actual_fwchan;
00108     bool                      is_streaming;
00109     AVS::MPEG2Receiver       *avstream;
00110     AVS::StringLogger        *logger;
00111     uint                      no_data_cnt;
00112     bool                      no_data_timer_set;
00113     MythTimer                 no_data_timer;
00114 
00115     avcinfo_list_t            devices;
00116 };
00117 
00118 DarwinFirewireDevice::DarwinFirewireDevice(
00119     uint64_t guid, uint subunitid, uint speed) :
00120     FirewireDevice(guid, subunitid, speed),
00121     m_local_node(-1), m_remote_node(-1), m_priv(new DFDPriv())
00122 {
00123 
00124 
00125 }
00126 
00127 DarwinFirewireDevice::~DarwinFirewireDevice()
00128 {
00129     if (IsPortOpen())
00130     {
00131         LOG(VB_GENERAL, LOG_ERR, LOC + "dtor called with open port");
00132         while (IsPortOpen())
00133             ClosePort();
00134     }
00135 
00136     if (m_priv)
00137     {
00138         delete m_priv;
00139         m_priv = NULL;
00140     }
00141 }
00142 
00143 void DarwinFirewireDevice::RunController(void)
00144 {
00145     m_priv->controller_thread_cf_ref = CFRunLoopGetCurrent();
00146 
00147     // Set up IEEE-1394 bus change notification
00148     mach_port_t master_port;
00149     int ret = IOMasterPort(bootstrap_port, &master_port);
00150     if (kIOReturnSuccess == ret)
00151     {
00152         m_priv->notify_port   = IONotificationPortCreate(master_port);
00153         m_priv->notify_source = IONotificationPortGetRunLoopSource(
00154             m_priv->notify_port);
00155 
00156         CFRunLoopAddSource(m_priv->controller_thread_cf_ref,
00157                            m_priv->notify_source,
00158                            kCFRunLoopDefaultMode);
00159 
00160         ret = IOServiceAddMatchingNotification(
00161             m_priv->notify_port, kIOMatchedNotification,
00162             IOServiceMatching("IOFireWireAVCUnit"),
00163             dfd_update_device_list, this, &m_priv->deviter);
00164     }
00165 
00166     if (kIOReturnSuccess == ret)
00167         dfd_update_device_list(this, m_priv->deviter);
00168 
00169     m_priv->controller_thread_running = true;
00170 
00171     if (kIOReturnSuccess == ret)
00172         CFRunLoopRun();
00173 
00174     QMutexLocker locker(&m_lock); // ensure that controller_thread_running seen
00175 
00176     m_priv->controller_thread_running = false;
00177 }
00178 
00179 void DarwinFirewireDevice::StartController(void)
00180 {
00181     m_lock.unlock();
00182 
00183     pthread_create(&m_priv->controller_thread, NULL,
00184                    dfd_controller_thunk, this);
00185 
00186     m_lock.lock();
00187     while (!m_priv->controller_thread_running)
00188     {
00189         m_lock.unlock();
00190         usleep(5000);
00191         m_lock.lock();
00192     }
00193 }
00194 
00195 void DarwinFirewireDevice::StopController(void)
00196 {
00197     if (!m_priv->controller_thread_running)
00198         return;
00199 
00200     if (m_priv->deviter)
00201     {
00202         IOObjectRelease(m_priv->deviter);
00203         m_priv->deviter = NULL;
00204     }
00205 
00206     if (m_priv->notify_source)
00207     {
00208         CFRunLoopSourceInvalidate(m_priv->notify_source);
00209         m_priv->notify_source = NULL;
00210     }
00211 
00212     if (m_priv->notify_port)
00213     {
00214         IONotificationPortDestroy(m_priv->notify_port);
00215         m_priv->notify_port = NULL;
00216     }
00217 
00218     CFRunLoopStop(m_priv->controller_thread_cf_ref);
00219 
00220     while (m_priv->controller_thread_running)
00221     {
00222         m_lock.unlock();
00223         usleep(100 * 1000);
00224         m_lock.lock();
00225     }
00226 }
00227 
00228 bool DarwinFirewireDevice::OpenPort(void)
00229 {
00230     QMutexLocker locker(&m_lock);
00231 
00232     LOG(VB_RECORD, LOG_INFO, LOC + "OpenPort()");
00233 
00234     if (GetInfoPtr() && GetInfoPtr()->IsPortOpen())
00235     {
00236         m_open_port_cnt++;
00237         return true;
00238     }
00239 
00240     StartController();
00241 
00242     if (!m_priv->controller_thread_running)
00243     {
00244         LOG(VB_GENERAL, LOG_ERR, LOC + "Unable to start firewire thread.");
00245         return false;
00246     }
00247 
00248     if (!GetInfoPtr())
00249     {
00250         LOG(VB_GENERAL, LOG_ERR, LOC + "No IEEE-1394 device with our GUID");
00251 
00252         StopController();
00253         return false;
00254     }
00255 
00256     LOG(VB_RECORD, LOG_INFO, LOC + "Opening AVC Device");
00257     LOG(VB_RECORD, LOG_INFO, LOC + GetInfoPtr()->GetSubunitInfoString());
00258 
00259     if (!GetInfoPtr()->IsSubunitType(kAVCSubunitTypeTuner) ||
00260         !GetInfoPtr()->IsSubunitType(kAVCSubunitTypePanel))
00261     {
00262         LOG(VB_GENERAL, LOG_ERR, LOC + QString("No STB at guid: 0x%1")
00263                 .arg(m_guid,0,16));
00264 
00265         StopController();
00266         return false;
00267     }
00268 
00269     bool ok = GetInfoPtr()->OpenPort(m_priv->controller_thread_cf_ref);
00270     if (!ok)
00271     {
00272         LOG(VB_GENERAL, LOG_ERR, LOC + "Unable to get handle for port");
00273 
00274         return false;
00275     }
00276 
00277     // TODO FIXME -- these can change after a reset... (at least local)
00278     if (!GetInfoPtr()->GetDeviceNodes(m_local_node, m_remote_node))
00279     {
00280         if (m_local_node < 0)
00281         {
00282             LOG(VB_GENERAL, LOG_WARNING, LOC + "Failed to query local node");
00283             m_local_node = 0;
00284         }
00285 
00286         if (m_remote_node < 0)
00287         {
00288             LOG(VB_GENERAL, LOG_WARNING, LOC + "Failed to query remote node");
00289             m_remote_node = 0;
00290         }
00291     }
00292 
00293     m_open_port_cnt++;
00294 
00295     return true;
00296 }
00297 
00298 bool DarwinFirewireDevice::ClosePort(void)
00299 {
00300     QMutexLocker locker(&m_lock);
00301 
00302     LOG(VB_RECORD, LOG_INFO, LOC + "ClosePort()");
00303 
00304     if (m_open_port_cnt < 1)
00305         return false;
00306 
00307     m_open_port_cnt--;
00308 
00309     if (m_open_port_cnt != 0)
00310         return true;
00311 
00312     if (GetInfoPtr() && GetInfoPtr()->IsPortOpen())
00313     {
00314         LOG(VB_RECORD, LOG_INFO, LOC + "Closing AVC Device");
00315 
00316         GetInfoPtr()->ClosePort();
00317     }
00318 
00319     StopController();
00320     m_local_node  = -1;
00321     m_remote_node = -1;
00322 
00323     return true;
00324 }
00325 
00326 bool DarwinFirewireDevice::OpenAVStream(void)
00327 {
00328     if (IsAVStreamOpen())
00329         return true;
00330 
00331     int max_speed = GetMaxSpeed();
00332     LOG(VB_GENERAL, LOG_INFO, LOC + QString("Max Speed: %1, Our speed: %2")
00333                           .arg(max_speed).arg(m_speed));
00334     m_speed = min((uint)max_speed, m_speed);
00335 
00336     uint fwchan = 0;
00337     bool streaming = IsSTBStreaming(&fwchan);
00338     LOG(VB_GENERAL, LOG_INFO, LOC +
00339         QString("STB is %1already streaming on fwchan: %2")
00340             .arg(streaming?"":"not ").arg(fwchan));
00341 
00342     // TODO we should use the stream if it already exists,
00343     //      this is especially true if it is a broadcast stream...
00344 
00345     int ret = AVS::CreateMPEG2Receiver(
00346         &m_priv->avstream,
00347         dfd_tspacket_handler_thunk, this,
00348         dfd_stream_msg, this,
00349         m_priv->logger /* StringLogger */,
00350         GetInfoPtr()->fw_handle,
00351         AVS::kCyclesPerReceiveSegment,
00352         AVS::kNumReceiveSegments,
00353         true /* p2p */);
00354 
00355     if (kIOReturnSuccess != ret)
00356     {
00357         LOG(VB_GENERAL, LOG_ERR, LOC + "Couldn't create A/V stream object");
00358         return false;
00359     }
00360 
00361     m_priv->avstream->registerNoDataNotificationCallback(
00362         dfd_no_data_notification, this, kNoDataTimeout);
00363 
00364     return true;
00365 }
00366 
00367 int DarwinFirewireDevice::GetMaxSpeed(void)
00368 {
00369     IOFireWireLibDeviceRef fw_handle = GetInfoPtr()->fw_handle;
00370 
00371     if ((*fw_handle)->version < 4)
00372     {
00373         // Just get the STB's info & assume we can handle it
00374         io_object_t dev = (*fw_handle)->GetDevice(fw_handle);
00375 
00376         FWAddress addr(0xffff, 0xf0000900, m_remote_node);
00377         uint32_t val;
00378         int ret = (*fw_handle)->ReadQuadlet(
00379             fw_handle, dev, &addr, (UInt32*) &val, false, 0);
00380         val = EndianU32_BtoN(val);
00381 
00382         return (ret == kIOReturnSuccess) ? (int)((val>>30) & 0x3) : -1;
00383     }
00384 
00385     uint32_t generation = 0;
00386     IOFWSpeed speed;
00387     int ret = (*fw_handle)->GetBusGeneration(fw_handle, (UInt32*)&generation);
00388     if (kIOReturnSuccess == ret)
00389     {
00390         ret = (*fw_handle)->GetSpeedBetweenNodes(
00391             fw_handle, generation, m_remote_node, m_local_node, &speed) ;
00392     }
00393 
00394     return (ret == kIOReturnSuccess) ? (int)speed : -1;
00395 }
00396 
00397 bool DarwinFirewireDevice::IsSTBStreaming(uint *fw_channel)
00398 {
00399     IOFireWireLibDeviceRef fw_handle = GetInfoPtr()->fw_handle;
00400     io_object_t dev = (*fw_handle)->GetDevice(fw_handle);
00401 
00402     FWAddress addr(0xffff, 0xf0000904, m_remote_node);
00403     uint32_t val;
00404     int ret = (*fw_handle)->ReadQuadlet(
00405         fw_handle, dev, &addr, (UInt32*) &val, false, 0);
00406     val = EndianU32_BtoN(val);
00407 
00408     if (ret != kIOReturnSuccess)
00409         return false;
00410 
00411     if (val & (kIOFWPCRBroadcast | kIOFWPCRP2PCount))
00412     {
00413         if (fw_channel)
00414             *fw_channel = (val & kIOFWPCRChannel) >> kIOFWPCRChannelPhase;
00415 
00416         return true;
00417     }
00418 
00419     return false;
00420 }
00421 
00422 bool DarwinFirewireDevice::CloseAVStream(void)
00423 {
00424     if (!m_priv->avstream)
00425         return true;
00426 
00427     StopStreaming();
00428 
00429     LOG(VB_RECORD, LOG_INFO, LOC + "Destroying A/V stream object");
00430     AVS::DestroyMPEG2Receiver(m_priv->avstream);
00431     m_priv->avstream = NULL;
00432 
00433     return true;
00434 }
00435 
00436 bool DarwinFirewireDevice::IsAVStreamOpen(void) const
00437 {
00438     return m_priv->avstream;
00439 }
00440 
00441 bool DarwinFirewireDevice::ResetBus(void)
00442 {
00443     LOG(VB_GENERAL, LOG_DEBUG, LOC + "ResetBus() -- begin");
00444 
00445     if (!GetInfoPtr() || !GetInfoPtr()->fw_handle)
00446         return false;
00447 
00448     IOFireWireLibDeviceRef fw_handle = GetInfoPtr()->fw_handle;
00449     bool ok = (*fw_handle)->BusReset(fw_handle) == kIOReturnSuccess;
00450 
00451     if (!ok)
00452         LOG(VB_GENERAL, LOG_ERR, LOC + "Bus Reset failed" + ENO);
00453 
00454     LOG(VB_GENERAL, LOG_DEBUG, LOC + "ResetBus() -- end");
00455 
00456     return ok;
00457 }
00458 
00459 bool DarwinFirewireDevice::StartStreaming(void)
00460 {
00461     if (m_priv->is_streaming)
00462         return m_priv->is_streaming;
00463 
00464     LOG(VB_RECORD, LOG_INFO, LOC + "Starting A/V streaming");
00465 
00466     if (!IsAVStreamOpen() && !OpenAVStream())
00467     {
00468         LOG(VB_GENERAL, LOG_ERR, LOC + "Starting A/V streaming: FAILED");
00469         return false;
00470     }
00471 
00472     m_priv->avstream->setReceiveIsochChannel(kAnyAvailableIsochChannel);
00473     m_priv->avstream->setReceiveIsochSpeed((IOFWSpeed) m_speed);
00474     int ret = m_priv->avstream->startReceive();
00475 
00476     m_priv->is_streaming = (kIOReturnSuccess == ret);
00477 
00478     LOG(VB_GENERAL, LOG_INFO, LOC + QString("Starting A/V streaming: %1")
00479                           .arg((m_priv->is_streaming)?"success":"failure"));
00480 
00481     return m_priv->is_streaming;
00482 }
00483 
00484 bool DarwinFirewireDevice::StopStreaming(void)
00485 {
00486     if (!m_priv->is_streaming)
00487         return true;
00488 
00489     LOG(VB_RECORD, LOG_INFO, LOC + "Stopping A/V streaming");
00490 
00491     bool ok = (kIOReturnSuccess == m_priv->avstream->stopReceive());
00492     m_priv->is_streaming = !ok;
00493 
00494     if (!ok)
00495     {
00496         LOG(VB_RECORD, LOG_ERR, LOC + "Failed to stop A/V streaming");
00497         return false;
00498     }
00499 
00500     LOG(VB_RECORD, LOG_INFO, LOC + "Stopped A/V streaming");
00501     return true;
00502 }
00503 
00504 bool DarwinFirewireDevice::SendAVCCommand(const vector<uint8_t> &cmd,
00505                                           vector<uint8_t>       &result,
00506                                           int                   retry_cnt)
00507 {
00508     return GetInfoPtr()->SendAVCCommand(cmd, result, retry_cnt);
00509 }
00510 
00511 bool DarwinFirewireDevice::IsPortOpen(void) const
00512 {
00513     QMutexLocker locker(&m_lock);
00514 
00515     if (!GetInfoPtr())
00516         return false;
00517 
00518     return GetInfoPtr()->IsPortOpen();
00519 }
00520 
00521 void DarwinFirewireDevice::AddListener(TSDataListener *listener)
00522 {
00523     QMutexLocker locker(&m_lock);
00524 
00525     FirewireDevice::AddListener(listener);
00526 
00527     if (!m_listeners.empty())
00528         StartStreaming();
00529 }
00530 
00531 void DarwinFirewireDevice::RemoveListener(TSDataListener *listener)
00532 {
00533     QMutexLocker locker(&m_lock);
00534 
00535     FirewireDevice::RemoveListener(listener);
00536 
00537     if (m_priv->is_streaming && m_listeners.empty())
00538     {
00539         StopStreaming();
00540         CloseAVStream();
00541     }
00542 }
00543 
00544 void DarwinFirewireDevice::BroadcastToListeners(
00545     const unsigned char *data, uint dataSize)
00546 {
00547     QMutexLocker locker(&m_lock);
00548     FirewireDevice::BroadcastToListeners(data, dataSize);
00549 }
00550 
00551 void DarwinFirewireDevice::ProcessNoDataMessage(void)
00552 {
00553     if (m_priv->no_data_timer_set)
00554     {
00555         int short_interval = kNoDataTimeout + (kNoDataTimeout>>1);
00556         bool recent = m_priv->no_data_timer.elapsed() <= short_interval;
00557         m_priv->no_data_cnt = (recent) ? m_priv->no_data_cnt + 1 : 1;
00558     }
00559     m_priv->no_data_timer_set = true;
00560     m_priv->no_data_timer.start();
00561 
00562     LOG(VB_GENERAL, LOG_WARNING, LOC + QString("No Input in %1 msecs")
00563             .arg(m_priv->no_data_cnt * kNoDataTimeout));
00564 
00565     if (m_priv->no_data_cnt > (kResetTimeout / kNoDataTimeout))
00566     {
00567         m_priv->no_data_timer_set = false;
00568         m_priv->no_data_cnt = 0;
00569         ResetBus();
00570     }
00571 }
00572 
00573 void DarwinFirewireDevice::ProcessStreamingMessage(
00574     uint32_t msg, uint32_t param1, uint32_t param2)
00575 {
00576     int plug_number = 0;
00577 
00578     if (AVS::kMpeg2ReceiverAllocateIsochPort == msg)
00579     {
00580         int speed = param1, fw_channel = param2;
00581 
00582         bool ok = UpdatePlugRegister(
00583             plug_number, fw_channel, speed, true, false);
00584 
00585         LOG(VB_GENERAL, LOG_INFO, LOC + QString("AllocateIsochPort(%1,%2) %3")
00586                 .arg(fw_channel).arg(speed).arg(((ok)?"ok":"error")));
00587     }
00588     else if (AVS::kMpeg2ReceiverReleaseIsochPort == msg)
00589     {
00590         int ret = UpdatePlugRegister(plug_number, -1, -1, false, true);
00591 
00592         LOG(VB_GENERAL, LOG_INFO, LOC + QString("ReleaseIsochPort %1")
00593                               .arg((kIOReturnSuccess == ret)?"ok":"error"));
00594     }
00595     else if (AVS::kMpeg2ReceiverDCLOverrun == msg)
00596     {
00597         LOG(VB_GENERAL, LOG_ERR, LOC + "DCL Overrun");
00598     }
00599     else if (AVS::kMpeg2ReceiverReceivedBadPacket == msg)
00600     {
00601         LOG(VB_GENERAL, LOG_ERR, LOC + "Received Bad Packet");
00602     }
00603     else
00604     {
00605         LOG(VB_GENERAL, LOG_INFO, LOC +
00606             QString("Streaming Message: %1").arg(msg));
00607     }
00608 }
00609 
00610 vector<AVCInfo> DarwinFirewireDevice::GetSTBList(void)
00611 {
00612     vector<AVCInfo> list;
00613 
00614     {
00615         DarwinFirewireDevice dev(0,0,0);
00616 
00617         dev.m_lock.lock();
00618         dev.StartController();
00619         dev.m_lock.unlock();
00620 
00621         list = dev.GetSTBListPrivate();
00622 
00623         dev.m_lock.lock();
00624         dev.StopController();
00625         dev.m_lock.unlock();
00626     }
00627 
00628     return list;
00629 }
00630 
00631 vector<AVCInfo> DarwinFirewireDevice::GetSTBListPrivate(void)
00632 {
00633 #if 0
00634     LOG(VB_GENERAL, LOG_DEBUG, LOC + "GetSTBListPrivate -- begin");
00635 #endif
00636     QMutexLocker locker(&m_lock);
00637 #if 0
00638     LOG(VB_GENERAL, LOG_DEBUG, LOC + "GetSTBListPrivate -- got lock");
00639 #endif
00640 
00641     vector<AVCInfo> list;
00642 
00643     avcinfo_list_t::iterator it = m_priv->devices.begin();
00644     for (; it != m_priv->devices.end(); ++it)
00645     {
00646         if ((*it)->IsSubunitType(kAVCSubunitTypeTuner) &&
00647             (*it)->IsSubunitType(kAVCSubunitTypePanel))
00648         {
00649             list.push_back(*(*it));
00650         }
00651     }
00652 
00653 #if 0
00654     LOG(VB_GENERAL, LOG_DEBUG, LOC + "GetSTBListPrivate -- end");
00655 #endif
00656     return list;
00657 }
00658 
00659 void DarwinFirewireDevice::UpdateDeviceListItem(uint64_t guid, void *pitem)
00660 {
00661     QMutexLocker locker(&m_lock);
00662 
00663     avcinfo_list_t::iterator it = m_priv->devices.find(guid);
00664 
00665     if (it == m_priv->devices.end())
00666     {
00667         DarwinAVCInfo *ptr = new DarwinAVCInfo();
00668 
00669         LOG(VB_GENERAL, LOG_INFO, LOC +
00670             QString("Adding   0x%1").arg(guid, 0, 16));
00671 
00672         m_priv->devices[guid] = ptr;
00673         it = m_priv->devices.find(guid);
00674     }
00675 
00676     io_object_t &item = *((io_object_t*) pitem);
00677     if (it != m_priv->devices.end())
00678     {
00679         LOG(VB_GENERAL, LOG_INFO, LOC +
00680             QString("Updating 0x%1").arg(guid, 0, 16));
00681         (*it)->Update(guid, this, m_priv->notify_port,
00682                       m_priv->controller_thread_cf_ref, item);
00683     }
00684 }
00685 
00686 DarwinAVCInfo *DarwinFirewireDevice::GetInfoPtr(void)
00687 {
00688     avcinfo_list_t::iterator it = m_priv->devices.find(m_guid);
00689     return (it == m_priv->devices.end()) ? NULL : *it;
00690 }
00691 
00692 const DarwinAVCInfo *DarwinFirewireDevice::GetInfoPtr(void) const
00693 {
00694     avcinfo_list_t::iterator it = m_priv->devices.find(m_guid);
00695     return (it == m_priv->devices.end()) ? NULL : *it;
00696 }
00697 
00698 
00699 bool DarwinFirewireDevice::UpdatePlugRegisterPrivate(
00700     uint plug_number, int new_fw_chan, int new_speed,
00701     bool add_plug, bool remove_plug)
00702 {
00703     if (!GetInfoPtr())
00704         return false;
00705 
00706     IOFireWireLibDeviceRef fw_handle = GetInfoPtr()->fw_handle;
00707     if (!fw_handle)
00708         return false;
00709 
00710     io_object_t dev = (*fw_handle)->GetDevice(fw_handle);
00711 
00712     // Read the register
00713     uint      low_addr = kPCRBaseAddress + 4 + (plug_number << 2);
00714     FWAddress addr(0xffff, low_addr, m_remote_node);
00715     uint32_t  old_plug_val;
00716     if (kIOReturnSuccess != (*fw_handle)->ReadQuadlet(
00717             fw_handle, dev, &addr, (UInt32*) &old_plug_val, false, 0))
00718     {
00719         return false;
00720     }
00721     old_plug_val = EndianU32_BtoN(old_plug_val);
00722 
00723     int old_plug_cnt = (old_plug_val >> 24) & 0x3f;
00724     int old_fw_chan  = (old_plug_val >> 16) & 0x3f;
00725     int old_speed    = (old_plug_val >> 14) & 0x03;
00726 
00727     int new_plug_cnt = (int) old_plug_cnt;
00728     new_plug_cnt += ((add_plug) ? 1 : 0) - ((remove_plug) ? 1 : 0);
00729     if ((new_plug_cnt > 0x3f) || (new_plug_cnt < 0))
00730     {
00731         LOG(VB_GENERAL, LOG_ERR, LOC + QString("Invalid Plug Count %1")
00732                               .arg(new_plug_cnt));
00733         return false;
00734     }
00735 
00736     new_fw_chan = (new_fw_chan >= 0) ? new_fw_chan : old_fw_chan;
00737     if (old_plug_cnt && (new_fw_chan != old_fw_chan))
00738     {
00739         LOG(VB_GENERAL, LOG_WARNING, LOC +
00740             "Ignoring FWChan change request, plug already open");
00741 
00742         new_fw_chan = old_fw_chan;
00743     }
00744 
00745     new_speed = (new_speed >= 0) ? new_speed : old_speed;
00746     if (old_plug_cnt && (new_speed != old_speed))
00747     {
00748         LOG(VB_GENERAL, LOG_WARNING, LOC +
00749             "Ignoring speed change request, plug already open");
00750 
00751         new_speed = old_speed;
00752     }
00753 
00754     uint32_t new_plug_val = old_plug_val;
00755 
00756     new_plug_val &= ~(0x3f<<24);
00757     new_plug_val &= (remove_plug) ? ~kIOFWPCRBroadcast : ~0x0;
00758     new_plug_val |= (new_plug_cnt & 0x3f) << 24;
00759 
00760     new_plug_val &= ~(0x3f<<16);
00761     new_plug_val |= (new_fw_chan & 0x3F) << 16;
00762 
00763     new_plug_val &= ~(0x03<<14);
00764     new_plug_val |= (new_speed & 0x03) << 14;
00765 
00766     old_plug_val = EndianU32_NtoB(old_plug_val); 
00767     new_plug_val = EndianU32_NtoB(new_plug_val);
00768 
00769     return (kIOReturnSuccess == (*fw_handle)->CompareSwap(
00770                 fw_handle, dev, &addr, old_plug_val, new_plug_val, false, 0));
00771 }
00772 
00773 void DarwinFirewireDevice::HandleBusReset(void)
00774 {
00775     int plug_number = 0;
00776     if (!GetInfoPtr())
00777         return;
00778 
00779     int fw_channel = m_priv->actual_fwchan;
00780     bool ok = UpdatePlugRegister(plug_number, fw_channel,
00781                                  m_speed, true, false);
00782     if (!ok)
00783     {
00784         ok = UpdatePlugRegister(plug_number, kAnyAvailableIsochChannel,
00785                                 m_speed, true, false);
00786     }
00787 
00788     if (!ok)
00789         LOG(VB_GENERAL, LOG_ERR, LOC + "Reset: Failed to reconnect");
00790     else
00791         LOG(VB_RECORD, LOG_INFO, LOC + "Reset: Reconnected succesfully");
00792 }
00793 
00794 bool DarwinFirewireDevice::UpdatePlugRegister(
00795     uint plug_number, int fw_chan, int speed,
00796     bool add_plug, bool remove_plug, uint retry_cnt)
00797 {
00798     if (!GetInfoPtr() || !GetInfoPtr()->fw_handle)
00799         return false;
00800 
00801     bool ok = false;
00802 
00803     for (uint i = 0; (i < retry_cnt) && !ok; i++)
00804     {
00805         ok = UpdatePlugRegisterPrivate(
00806             plug_number, fw_chan, speed, add_plug, remove_plug);
00807     }
00808 
00809     m_priv->actual_fwchan = (ok) ? fw_chan : kAnyAvailableIsochChannel;
00810 
00811     return ok;
00812 }
00813 
00814 void DarwinFirewireDevice::HandleDeviceChange(uint messageType)
00815 {
00816     QString loc = LOC + "HandleDeviceChange: ";
00817 
00818     if (kIOMessageServiceIsTerminated == messageType)
00819     {
00820         LOG(VB_RECORD, LOG_INFO, loc + "Disconnect");
00821         // stop printing no data messages.. don't try to open
00822         return;
00823     }
00824 
00825     if (kIOMessageServiceIsAttemptingOpen == messageType)
00826     {
00827         LOG(VB_RECORD, LOG_INFO, loc + "Attempting open");
00828         return;
00829     }
00830 
00831     if (kIOMessageServiceWasClosed == messageType)
00832     {
00833         LOG(VB_RECORD, LOG_INFO, loc + "Device Closed");
00834         // fill unit_table
00835         return;
00836     }
00837 
00838     if (kIOMessageServiceIsSuspended == messageType)
00839     {
00840         LOG(VB_RECORD, LOG_INFO, loc + "kIOMessageServiceIsSuspended");
00841         // start of reset
00842         return;
00843     }
00844 
00845     if (kIOMessageServiceIsResumed == messageType)
00846     {
00847         // end of reset
00848         HandleBusReset();
00849     }
00850 
00851     if (kIOMessageServiceIsTerminated == messageType)
00852         LOG(VB_RECORD, LOG_INFO, loc + "kIOMessageServiceIsTerminated");
00853     else if (kIOMessageServiceIsRequestingClose == messageType)
00854         LOG(VB_RECORD, LOG_INFO, loc + "kIOMessageServiceIsRequestingClose");
00855     else if (kIOMessageServiceIsAttemptingOpen == messageType)
00856         LOG(VB_RECORD, LOG_INFO, loc + "kIOMessageServiceIsAttemptingOpen");
00857     else if (kIOMessageServiceWasClosed == messageType)
00858         LOG(VB_RECORD, LOG_INFO, loc + "kIOMessageServiceWasClosed");
00859     else if (kIOMessageServiceBusyStateChange == messageType)
00860         LOG(VB_RECORD, LOG_INFO, loc + "kIOMessageServiceBusyStateChange");
00861     else if (kIOMessageCanDevicePowerOff == messageType)
00862         LOG(VB_RECORD, LOG_INFO, loc + "kIOMessageCanDevicePowerOff");
00863     else if (kIOMessageDeviceWillPowerOff == messageType)
00864         LOG(VB_RECORD, LOG_INFO, loc + "kIOMessageDeviceWillPowerOff");
00865     else if (kIOMessageDeviceWillNotPowerOff == messageType)
00866         LOG(VB_RECORD, LOG_INFO, loc + "kIOMessageDeviceWillNotPowerOff");
00867     else if (kIOMessageDeviceHasPoweredOn == messageType)
00868         LOG(VB_RECORD, LOG_INFO, loc + "kIOMessageDeviceHasPoweredOn");
00869     else if (kIOMessageCanSystemPowerOff == messageType)
00870         LOG(VB_RECORD, LOG_INFO, loc + "kIOMessageCanSystemPowerOff");
00871     else if (kIOMessageSystemWillPowerOff == messageType)
00872         LOG(VB_RECORD, LOG_INFO, loc + "kIOMessageSystemWillPowerOff");
00873     else if (kIOMessageSystemWillNotPowerOff == messageType)
00874         LOG(VB_RECORD, LOG_INFO, loc + "kIOMessageSystemWillNotPowerOff");
00875     else if (kIOMessageCanSystemSleep == messageType)
00876         LOG(VB_RECORD, LOG_INFO, loc + "kIOMessageCanSystemSleep");
00877     else if (kIOMessageSystemWillSleep == messageType)
00878         LOG(VB_RECORD, LOG_INFO, loc + "kIOMessageSystemWillSleep");
00879     else if (kIOMessageSystemWillNotSleep == messageType)
00880         LOG(VB_RECORD, LOG_INFO, loc + "kIOMessageSystemWillNotSleep");
00881     else if (kIOMessageSystemHasPoweredOn == messageType)
00882         LOG(VB_RECORD, LOG_INFO, loc + "kIOMessageSystemHasPoweredOn");
00883     else if (kIOMessageSystemWillRestart == messageType)
00884         LOG(VB_RECORD, LOG_INFO, loc + "kIOMessageSystemWillRestart");
00885     else
00886     {
00887         LOG(VB_RECORD, LOG_ERR, loc + QString("unknown message 0x%1")
00888                            .arg(messageType, 0, 16));
00889     }
00890 }
00891 
00892 // Various message callbacks.
00893 
00894 void *dfd_controller_thunk(void *callback_data)
00895 {
00896     MThread::ThreadSetup("DarwinController");
00897     reinterpret_cast<DarwinFirewireDevice*>(callback_data)->RunController();
00898     MThread::ThreadCleanup();
00899     return NULL;
00900 }
00901 
00902 void dfd_update_device_list_item(
00903     DarwinFirewireDevice *dev, uint64_t guid, void *item)
00904 {
00905     dev->UpdateDeviceListItem(guid, item);
00906 }
00907 
00908 int dfd_no_data_notification(void *callback_data)
00909 {
00910     reinterpret_cast<DarwinFirewireDevice*>(callback_data)->
00911         ProcessNoDataMessage();
00912 
00913     return kIOReturnSuccess;
00914 }
00915 
00916 void dfd_stream_msg(UInt32 msg, UInt32 param1,
00917                     UInt32 param2, void *callback_data)
00918 {
00919     reinterpret_cast<DarwinFirewireDevice*>(callback_data)->
00920         ProcessStreamingMessage(msg, param1, param2);
00921 }
00922 
00923 int dfd_tspacket_handler(uint tsPacketCount, uint32_t **ppBuf,
00924                          void *callback_data)
00925 {
00926     DarwinFirewireDevice *fw =
00927         reinterpret_cast<DarwinFirewireDevice*>(callback_data);
00928 
00929     if (!fw)
00930         return kIOReturnBadArgument;
00931 
00932     for (uint32_t i = 0; i < tsPacketCount; ++i)
00933         fw->BroadcastToListeners((const unsigned char*) ppBuf[i], 188);
00934 
00935     return kIOReturnSuccess;
00936 }
00937 
00938 static IOReturn dfd_tspacket_handler_thunk(
00939     UInt32 tsPacketCount, UInt32 **ppBuf, void *callback_data)
00940 {
00941     return dfd_tspacket_handler(
00942         tsPacketCount, (uint32_t**)ppBuf, callback_data);
00943 }
00944 
00945 static void dfd_update_device_list(void *dfd, io_iterator_t deviter)
00946 {
00947     DarwinFirewireDevice *dev = reinterpret_cast<DarwinFirewireDevice*>(dfd);
00948 
00949     io_object_t it = NULL;
00950     while ((it = IOIteratorNext(deviter)))
00951     {
00952         uint64_t guid = 0;
00953 
00954         CFMutableDictionaryRef props;
00955         int ret = IORegistryEntryCreateCFProperties(
00956             it, &props, kCFAllocatorDefault, kNilOptions);
00957 
00958         if (kIOReturnSuccess == ret)
00959         {
00960             CFNumberRef GUIDDesc = (CFNumberRef)
00961                 CFDictionaryGetValue(props, CFSTR("GUID"));
00962             CFNumberGetValue(GUIDDesc, kCFNumberSInt64Type, &guid);
00963             CFRelease(props);
00964             dfd_update_device_list_item(dev, guid, &it);
00965         }
00966     }
00967 }
00968 
00969 static void dfd_streaming_log_message(char *msg)
00970 {
00971     LOG(VB_RECORD, LOG_INFO, QString("MPEG2Receiver: %1").arg(msg));
00972 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends