MythTV master
mediamonitor-darwin.cpp
Go to the documentation of this file.
1
8#include <thread>
9
10#include <QDir>
11#include <QMetaType>
12
13#include "libmythbase/mythconfig.h"
15#include "libmythbase/mythhdd.h"
17
18#include "mediamonitor.h"
19#include "mediamonitor-darwin.h"
20
21#include <IOKit/IOKitLib.h>
22#include <IOKit/storage/IOMedia.h>
23#include <IOKit/storage/IOCDMedia.h>
24#include <IOKit/storage/IODVDMedia.h>
25#include <IOKit/storage/IOBlockStorageDevice.h>
26#include <IOKit/storage/IOStorageDeviceCharacteristics.h>
27#include <IOKit/storage/IOStorageProtocolCharacteristics.h>
28#include <DiskArbitration/DiskArbitration.h>
29
30#if !HAVE_IOMAINPORT
31#define IOMainPort IOMasterPort
32#endif
33
34// These aren't external, they are defined in this file.
35// The 'extern "C"' forces them in the C namespace, not the C++
36extern "C" void diskAppearedCallback(DADiskRef disk, void *context);
37extern "C" void diskDisappearedCallback(DADiskRef disk, void *context);
38extern "C" void diskChangedCallback(DADiskRef disk,
39 CFArrayRef keys, void *context);
40extern "C" MythMediaType MediaTypeForBSDName(const char *bsdName);
41
42static mach_port_t sMasterPort;
43
44
48MythMediaType FindMediaType(io_service_t service)
49{
50 kern_return_t kernResult = 0;
51 io_iterator_t iter = 0;
53 QString msg = QString("FindMediaType() - ");
54
55 // Create an iterator across all parents of the service object passed in.
56 kernResult = IORegistryEntryCreateIterator(service,
57 kIOServicePlane,
58 kIORegistryIterateRecursively
59 | kIORegistryIterateParents,
60 &iter);
61
62 if (KERN_SUCCESS != kernResult)
63 {
64 LOG(VB_GENERAL, LOG_CRIT, msg +
65 QString("IORegistryEntryCreateIterator returned %1")
66 .arg(kernResult));
67 }
68 else if (!iter)
69 {
70 LOG(VB_GENERAL, LOG_CRIT, msg +
71 "IORegistryEntryCreateIterator returned NULL iterator");
72 }
73 else
74 {
75 // A reference on the initial service object is released in
76 // the do-while loop below, so add a reference to balance
77 IOObjectRetain(service);
78
79 while (service && (mediaType == MEDIATYPE_UNKNOWN))
80 {
81 bool isWholeMedia = false;
82 if (IOObjectConformsTo(service, kIOMediaClass))
83 {
84 CFTypeRef wholeMedia = nullptr;
85
86 wholeMedia = IORegistryEntryCreateCFProperty
87 (service, CFSTR(kIOMediaWholeKey),
88 kCFAllocatorDefault, 0);
89
90 if (!wholeMedia)
91 {
92 LOG(VB_GENERAL, LOG_ALERT, msg +
93 "Could not retrieve Whole property");
94 }
95 else
96 {
97 isWholeMedia = CFBooleanGetValue((CFBooleanRef)wholeMedia);
98 CFRelease(wholeMedia);
99 }
100 }
101
102 if (isWholeMedia)
103 {
104 if (IOObjectConformsTo(service, kIODVDMediaClass))
105 mediaType = MEDIATYPE_DVD;
106 else if (IOObjectConformsTo(service, kIOCDMediaClass))
107 mediaType = MEDIATYPE_AUDIO;
108 }
109
110 IOObjectRelease(service);
111 service = IOIteratorNext(iter);
112 }
113
114 IOObjectRelease(iter);
115 }
116 return mediaType;
117}
118
123{
124 CFMutableDictionaryRef matchingDict = nullptr;
125 kern_return_t kernResult = 0;
126 io_iterator_t iter = 0;
127 io_service_t service = 0;
128 QString msg = QString("MediaTypeForBSDName(%1)")
129 .arg(bsdName);
130
131 if (!bsdName || !*bsdName)
132 {
133 LOG(VB_GENERAL, LOG_ALERT, msg + " - No name supplied?");
134 return MEDIATYPE_UNKNOWN;
135 }
136
137 matchingDict = IOBSDNameMatching(sMasterPort, 0, bsdName);
138 if (!matchingDict)
139 {
140 LOG(VB_GENERAL, LOG_ALERT,
141 msg + " - IOBSDNameMatching() returned a NULL dictionary.");
142 return MEDIATYPE_UNKNOWN;
143 }
144
145 // Return an iterator across all objects with the matching
146 // BSD node name. Note that there should only be one match!
147 kernResult = IOServiceGetMatchingServices(sMasterPort, matchingDict, &iter);
148
149 if (KERN_SUCCESS != kernResult)
150 {
151 LOG(VB_GENERAL, LOG_ALERT,
152 QString(msg + " - IOServiceGetMatchingServices() returned %2")
153 .arg(kernResult));
154 return MEDIATYPE_UNKNOWN;
155 }
156 if (!iter)
157 {
158 LOG(VB_GENERAL, LOG_ALERT,
159 msg + " - IOServiceGetMatchingServices() returned a NULL "
160 "iterator");
161 return MEDIATYPE_UNKNOWN;
162 }
163
164 service = IOIteratorNext(iter);
165
166 // Release this now because we only expect
167 // the iterator to contain a single io_service_t.
168 IOObjectRelease(iter);
169
170 if (!service)
171 {
172 LOG(VB_GENERAL, LOG_ALERT,
173 msg + " - IOIteratorNext() returned a NULL iterator");
174 return MEDIATYPE_UNKNOWN;
175 }
176 MythMediaType mediaType = FindMediaType(service);
177 IOObjectRelease(service);
178 return mediaType;
179}
180
181
185static std::string getVolName(CFDictionaryRef diskDetails)
186{
187 CFStringRef name = nullptr;
188 CFIndex size = 0;
189
190 name = (CFStringRef)
191 CFDictionaryGetValue(diskDetails, kDADiskDescriptionVolumeNameKey);
192
193 if (!name)
194 return {};
195
196 size = CFStringGetLength(name) + 1;
197 std::string volName;
198 try {
199 volName.resize(size);
200 } catch (std::bad_alloc &ex) {
201 LOG(VB_GENERAL, LOG_ALERT,
202 QString("getVolName() - Can't resize string(%1)?").arg(size));
203 return {};
204 }
205
206 if (!CFStringGetCString(name, volName.data(), size, kCFStringEncodingUTF8))
207 return {};
208
209 return { volName };
210}
211
212/*
213 * Given a DA description, return a compound description to help identify it.
214 */
215static QString getModel(CFDictionaryRef diskDetails)
216{
217 QString desc;
218 const void *strRef = nullptr;
219
220 // Location
221 if (kCFBooleanTrue ==
222 CFDictionaryGetValue(diskDetails,
223 kDADiskDescriptionDeviceInternalKey))
224 desc.append("Internal ");
225
226 // Manufacturer
227 strRef = CFDictionaryGetValue(diskDetails,
228 kDADiskDescriptionDeviceVendorKey);
229 if (strRef)
230 {
231 desc.append(CFStringGetCStringPtr((CFStringRef)strRef,
232 kCFStringEncodingMacRoman));
233 desc.append(' ');
234 }
235
236 // Product
237 strRef = CFDictionaryGetValue(diskDetails,
238 kDADiskDescriptionDeviceModelKey);
239 if (strRef)
240 {
241 desc.append(CFStringGetCStringPtr((CFStringRef)strRef,
242 kCFStringEncodingMacRoman));
243 desc.append(' ');
244 }
245
246 // Remove the trailing space
247 desc.truncate(desc.length() - 1);
248
249 // and multiple spaces
250 desc.remove(" ");
251
252 return desc;
253}
254
255
256/*
257 * Callbacks which the Disk Arbitration session invokes
258 * whenever a disk comes or goes, or is renamed
259 */
260
261void diskAppearedCallback(DADiskRef disk, void *context)
262{
263 const char *BSDname = DADiskGetBSDName(disk);
264 CFDictionaryRef details = nullptr;
265 bool isCDorDVD = false;
266 QString model;
267 MonitorThreadDarwin *mtd = nullptr;
268 QString msg = "diskAppearedCallback() - ";
269 std::string volName;
270
271
272 if (!BSDname)
273 {
274 LOG(VB_MEDIA, LOG_INFO, msg + "Skipping non-local device");
275 return;
276 }
277
278 if (!context)
279 {
280 LOG(VB_GENERAL, LOG_ALERT, msg + "Error. Invoked with a NULL context.");
281 return;
282 }
283
284 mtd = reinterpret_cast<MonitorThreadDarwin*>(context);
285
286
287 // We want to monitor CDs/DVDs and USB cameras or flash drives,
288 // but probably not hard disk or network drives. For now, ignore
289 // any disk or partitions that are not on removable media.
290 // Seems OK for hot-plug USB/FireWire disks (i.e. they are removable)
291
292 details = DADiskCopyDescription(disk);
293
294 if (kCFBooleanFalse ==
295 CFDictionaryGetValue(details, kDADiskDescriptionMediaRemovableKey))
296 {
297 LOG(VB_MEDIA, LOG_INFO, msg + QString("Skipping non-removable %1")
298 .arg(BSDname));
299 CFRelease(details);
300 return;
301 }
302
303 // Get the volume and model name for more user-friendly interaction
304 volName = getVolName(details);
305 if (volName.empty())
306 {
307 LOG(VB_MEDIA, LOG_INFO, msg + QString("No volume name for dev %1")
308 .arg(BSDname));
309 CFRelease(details);
310 return;
311 }
312
313 model = getModel(details);
314
315 if (model.contains("Disk Image"))
316 {
317 LOG(VB_MEDIA, LOG_INFO, msg + QString("DMG %1 mounted, ignoring")
318 .arg(BSDname));
319 CFRelease(details);
320 return;
321 }
322
323 MythMediaType mediaType = MediaTypeForBSDName(BSDname);
324 isCDorDVD = (mediaType == MEDIATYPE_DVD) || (mediaType == MEDIATYPE_AUDIO);
325
326
327 // We know it is removable, and have guessed the type.
328 // Call a helper function to create appropriate objects and insert
329
330 LOG(VB_MEDIA, LOG_INFO, QString("Found disk %1 - volume name '%2'.")
331 .arg(BSDname).arg(volName.c_str()));
332
333 mtd->diskInsert(BSDname, volName.c_str(), model, isCDorDVD);
334
335 CFRelease(details);
336}
337
338void diskDisappearedCallback(DADiskRef disk, void *context)
339{
340 const char *BSDname = DADiskGetBSDName(disk);
341
342 if (context)
343 reinterpret_cast<MonitorThreadDarwin *>(context)->diskRemove(BSDname);
344}
345
346void diskChangedCallback(DADiskRef disk, CFArrayRef keys, void *context)
347{
348 if (CFArrayContainsValue(keys, CFRangeMake(0, CFArrayGetCount(keys)),
349 kDADiskDescriptionVolumeNameKey))
350 {
351 const char *BSDname = DADiskGetBSDName(disk);
352 CFDictionaryRef details = DADiskCopyDescription(disk);
353 std::string volName = getVolName(details);
354
355 LOG(VB_MEDIA, LOG_INFO, QString("Disk %1 - changed name to '%2'.")
356 .arg(BSDname).arg(volName.c_str()));
357
358 reinterpret_cast<MonitorThreadDarwin *>(context)
359 ->diskRename(BSDname, volName.c_str());
360 CFRelease(details);
361 }
362}
363
364
369{
370 RunProlog();
371 CFDictionaryRef match = kDADiskDescriptionMatchVolumeMountable;
372 DASessionRef daSession = DASessionCreate(kCFAllocatorDefault);
373
374 if (daSession == nullptr)
375 {
376 LOG(VB_GENERAL, LOG_ALERT, "Couldn't create session for MonitorThreadDarwin.");
377 RunEpilog();
378 return;
379 }
380
381 IOMainPort(MACH_PORT_NULL, &sMasterPort);
382
383 DARegisterDiskAppearedCallback(daSession, match,
385 DARegisterDiskDisappearedCallback(daSession, match,
387 DARegisterDiskDescriptionChangedCallback(daSession, match,
388 kDADiskDescriptionWatchVolumeName,
389 diskChangedCallback, this);
390
391 DASessionScheduleWithRunLoop(daSession,
392 CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
393
394
395 // Nice and simple, as long as our monitor is valid and active,
396 // loop and let daSession check the devices.
397 while (m_monitor && m_monitor->IsActive())
398 {
399 // Run the run loop for interval (milliseconds) - this will
400 // handle any disk arbitration appeared/dissappeared events
401 CFRunLoopRunInMode(kCFRunLoopDefaultMode,
402 (float) m_interval / 1000.0F, false );
403 }
404
405 DAUnregisterCallback(daSession, (void(*))diskChangedCallback, this);
406 DAUnregisterCallback(daSession, (void(*))diskDisappearedCallback, this);
407 DAUnregisterCallback(daSession, (void(*))diskAppearedCallback, this);
408 CFRelease(daSession);
409 RunEpilog();
410}
411
418void MonitorThreadDarwin::diskInsert(const char *devName,
419 const char *volName,
420 const QString& model, bool isCDorDVD)
421{
422 MythMediaDevice *media = nullptr;
423 QString msg = "MonitorThreadDarwin::diskInsert";
424
425 LOG(VB_MEDIA, LOG_DEBUG, msg + QString("(%1,%2,'%3',%4)")
426 .arg(devName).arg(volName).arg(model).arg(isCDorDVD));
427
428 if (isCDorDVD)
429 media = MythCDROM::get(nullptr, devName, true, m_monitor->m_allowEject);
430 else
431 media = MythHDD::Get(nullptr, devName, true, false);
432
433 if (!media)
434 {
435 LOG(VB_GENERAL, LOG_ALERT, msg + "Couldn't create MythMediaDevice.");
436 return;
437 }
438
439 // We store the volume name for user activities like ChooseAndEjectMedia().
440 media->setVolumeID(volName);
441 media->setDeviceModel(model.toLatin1().constData()); // Same for the Manufacturer and model
442
443 // Mac OS X devices are pre-mounted here:
444 QString mnt = "/Volumes/"; mnt += volName;
445 media->setMountPath(mnt.toLatin1().constData());
446
447 int attempts = 0;
448 QDir d(mnt);
449 while (!d.exists())
450 {
451 LOG(VB_MEDIA, LOG_WARNING,
452 (msg + "() - Waiting for mount '%1' to become stable.").arg(mnt));
453 std::this_thread::sleep_for(120ms);
454 if ( ++attempts > 4 )
455 std::this_thread::sleep_for(200ms);
456 if ( attempts > 8 )
457 {
458 delete media;
459 LOG(VB_MEDIA, LOG_ALERT, msg + "() - Giving up");
460 return;
461 }
462 }
463
465
466 // This is checked in AddDevice(), but checking earlier means
467 // we can avoid scanning all the files to determine its type
468 if (m_monitor->shouldIgnore(media))
469 return;
470
471 // We want to use MythMedia's code to work out the mediaType.
472 // media->onDeviceMounted() is protected,
473 // so to call it indirectly, we pretend to mount it here.
474 media->mount();
475
476 m_monitor->AddDevice(media);
477}
478
479void MonitorThreadDarwin::diskRemove(const QString& devName)
480{
481 LOG(VB_MEDIA, LOG_DEBUG,
482 QString("MonitorThreadDarwin::diskRemove(%1)").arg(devName));
483
484 MythMediaDevice *pDevice = m_monitor->GetMedia(devName);
485
486 if (pDevice) // Probably should ValidateAndLock() here?
487 pDevice->setStatus(MEDIASTAT_NODISK);
488 else
489 LOG(VB_MEDIA, LOG_INFO, "Couldn't find MythMediaDevice: " + devName);
490
491 m_monitor->RemoveDevice(devName);
492}
493
500void MonitorThreadDarwin::diskRename(const char *devName, const char *volName)
501{
502 LOG(VB_MEDIA, LOG_DEBUG,
503 QString("MonitorThreadDarwin::diskRename(%1,%2)")
504 .arg(devName).arg(volName));
505
506 MythMediaDevice *pDevice = m_monitor->GetMedia(devName);
507
508 if (m_monitor->ValidateAndLock(pDevice))
509 {
510 // Send message to plugins to ignore this drive:
511 pDevice->setStatus(MEDIASTAT_NODISK);
512
513 pDevice->setVolumeID(volName);
514 pDevice->setMountPath((QString("/Volumes/") + volName).toLatin1().constData());
515
516 // Plugins can now use it again:
518
519 m_monitor->Unlock(pDevice);
520 }
521 else
522 {
523 LOG(VB_MEDIA, LOG_INFO,
524 QString("Couldn't find MythMediaDevice: %1").arg(devName));
525 }
526}
527
535{
536 // Sanity check
537 if (m_active)
538 return;
539
540 // If something (like the MythMusic plugin) stops and starts monitoring,
541 // DiskArbitration would re-add the same drives several times over.
542 // So, we make sure the device list is deleted.
543 m_devices.clear();
544
545
546 if (!m_thread)
548
549 qRegisterMetaType<MythMediaStatus>("MythMediaStatus");
550
551 LOG(VB_MEDIA, LOG_NOTICE, "Starting MediaMonitor");
552 m_active = true;
553 m_thread->start();
554}
555
562{
563 if ( !pDevice )
564 {
565 LOG(VB_GENERAL, LOG_ERR, "MediaMonitor::AddDevice(null)");
566 return false;
567 }
568
569 // If the user doesn't want this device to be monitored, stop now:
570 if (shouldIgnore(pDevice))
571 return false;
572
573 m_devices.push_back( pDevice );
574 m_useCount[pDevice] = 0;
575
576
577 // Devices on Mac OS X don't change status the way Linux ones do,
578 // so we force a status change for mediaStatusChanged() to send an event
579 pDevice->setStatus(MEDIASTAT_NODISK);
580 connect(pDevice, &MythMediaDevice::statusChanged,
583
584
585 return true;
586}
587
588/*
589 * Given a device, return a compound description to help identify it.
590 * We try to find out if it is internal, its manufacturer, and model.
591 *
592 * The Core Foundation library owns all data returned returned by
593 * "Get" functions. There can't be a memory leak here.
594 * NOLINTBEGIN(clang-analyzer-osx.cocoa.RetainCount)
595 */
596static QString getModel(io_object_t drive)
597{
598 QString desc;
599 CFMutableDictionaryRef props = nullptr;
600
601 props = (CFMutableDictionaryRef) IORegistryEntrySearchCFProperty(drive, kIOServicePlane, CFSTR(kIOPropertyProtocolCharacteristicsKey), kCFAllocatorDefault, kIORegistryIterateParents | kIORegistryIterateRecursively);
602CFShow(props);
603 if (props)
604 {
605 const void *location = CFDictionaryGetValue(props, CFSTR(kIOPropertyPhysicalInterconnectLocationKey));
606 if (CFEqual(location, CFSTR("Internal")))
607 desc.append("Internal ");
608 }
609
610 props = (CFMutableDictionaryRef) IORegistryEntrySearchCFProperty(drive, kIOServicePlane, CFSTR(kIOPropertyDeviceCharacteristicsKey), kCFAllocatorDefault, kIORegistryIterateParents | kIORegistryIterateRecursively);
611 if (props)
612 {
613 const void *product = CFDictionaryGetValue(props, CFSTR(kIOPropertyProductNameKey));
614 const void *vendor = CFDictionaryGetValue(props, CFSTR(kIOPropertyVendorNameKey));
615 if (vendor)
616 {
617 desc.append(CFStringGetCStringPtr((CFStringRef)vendor, kCFStringEncodingMacRoman));
618 desc.append(" ");
619 }
620 if (product)
621 {
622 desc.append(CFStringGetCStringPtr((CFStringRef)product, kCFStringEncodingMacRoman));
623 desc.append(" ");
624 }
625 }
626
627 // Omit the trailing space
628 desc.truncate(desc.length() - 1);
629
630 return desc;
631}
632// NOLINTEND(clang-analyzer-osx.cocoa.RetainCount)
633
644{
645 kern_return_t kernResult = 0;
646 CFMutableDictionaryRef devices = nullptr;
647 io_iterator_t iter = 0;
648 QStringList list;
649 QString msg = QString("GetCDRomBlockDevices() - ");
650
651
652 devices = IOServiceMatching(kIOBlockStorageDeviceClass);
653 if (!devices)
654 {
655 LOG(VB_GENERAL, LOG_ALERT, msg + "No Storage Devices? Unlikely!");
656 return list;
657 }
658
659 // Create an iterator across all parents of the service object passed in.
660 kernResult = IOServiceGetMatchingServices(sMasterPort, devices, &iter);
661
662 if (KERN_SUCCESS != kernResult)
663 {
664 LOG(VB_GENERAL, LOG_ALERT, msg +
665 QString("IORegistryEntryCreateIterator returned %1")
666 .arg(kernResult));
667 return list;
668 }
669 if (!iter)
670 {
671 LOG(VB_GENERAL, LOG_ALERT, msg +
672 "IORegistryEntryCreateIterator returned a NULL iterator");
673 return list;
674 }
675
676 io_object_t drive = 0;
677
678 while ((drive = IOIteratorNext(iter)))
679 {
680 CFMutableDictionaryRef p = nullptr; // properties of drive
681
682 IORegistryEntryCreateCFProperties(drive, &p, kCFAllocatorDefault, 0);
683 if (p)
684 {
685 const void *type = CFDictionaryGetValue(p, CFSTR("device-type"));
686
687 if (CFEqual(type, CFSTR("DVD")) || CFEqual(type, CFSTR("CD")))
688 {
689 QString desc = getModel(drive);
690
691 list.append(desc);
692 LOG(VB_MEDIA, LOG_INFO, desc.prepend("Found CD/DVD: "));
693 CFRelease(p);
694 }
695 }
696 else
697 {
698 LOG(VB_GENERAL, LOG_ALERT,
699 msg + "Could not retrieve drive properties");
700 }
701
702 IOObjectRelease(drive);
703 }
704
705 IOObjectRelease(iter);
706
707 return list;
708}
void RunProlog(void)
Sets up a thread, call this if you reimplement run().
Definition: mthread.cpp:180
void start(QThread::Priority p=QThread::InheritPriority)
Tell MThread to start running the thread in the near future.
Definition: mthread.cpp:267
void RunEpilog(void)
Cleans up a thread's resources, call this if you reimplement run().
Definition: mthread.cpp:193
void StartMonitoring(void) override
Start the monitoring thread if needed.
bool AddDevice(MythMediaDevice *pDevice) override
Simpler version of MediaMonitorUnix::AddDevice()
QStringList GetCDROMBlockDevices(void) override
List of CD/DVD devices.
unsigned long m_monitorPollingInterval
Definition: mediamonitor.h:134
void mediaStatusChanged(MythMediaStatus oldStatus, MythMediaDevice *pMedia) const
Slot which is called when the device status changes and posts a media event to the mainwindow.
MonitorThread * m_thread
Definition: mediamonitor.h:133
bool shouldIgnore(const MythMediaDevice *device)
Check user preferences to see if this device should be monitored.
bool volatile m_active
Was MonitorThread started?
Definition: mediamonitor.h:131
friend class MonitorThreadDarwin
Definition: mediamonitor.h:52
QList< MythMediaDevice * > m_devices
Definition: mediamonitor.h:124
QMap< MythMediaDevice *, int > m_useCount
Definition: mediamonitor.h:126
void diskInsert(const char *devName, const char *volName, const QString &model, bool isCDorDVD=1)
Create a MythMedia instance and insert in MythMediaMonitor list.
void diskRename(const char *devName, const char *volName)
Deal with the user, or another program, renaming a volume.
void diskRemove(const QString &devName)
void run(void) override
Use the DiskArbitration Daemon to inform us of media changes.
unsigned long m_interval
Definition: mediamonitor.h:44
QPointer< MediaMonitor > m_monitor
Definition: mediamonitor.h:43
static MythCDROM * get(QObject *par, const QString &devicePath, bool SuperMount, bool AllowEject)
Definition: mythcdrom.cpp:44
static MythHDD * Get(QObject *par, const char *devicePath, bool SuperMount, bool AllowEject)
Helper function used to create a new instance of a hard disk device.
Definition: mythhdd.cpp:15
MythMediaStatus setStatus(MythMediaStatus newStat, bool CloseIt=false)
Definition: mythmedia.cpp:469
void statusChanged(MythMediaStatus oldStatus, MythMediaDevice *pMedia)
void setMountPath(const char *path)
Definition: mythmedia.h:59
void setDeviceModel(const char *model)
Definition: mythmedia.h:68
void setVolumeID(const char *vol)
Definition: mythmedia.h:73
static const iso6937table * d
MythMediaType MediaTypeForBSDName(const char *bsdName)
Given a BSD device node name, guess its media type.
MythMediaType FindMediaType(io_service_t service)
Guess the media that a volume/partition is on.
void diskDisappearedCallback(DADiskRef disk, void *context)
void diskAppearedCallback(DADiskRef disk, void *context)
static QString getModel(CFDictionaryRef diskDetails)
static mach_port_t sMasterPort
static std::string getVolName(CFDictionaryRef diskDetails)
Given a description of a disk, copy and return the volume name.
#define IOMainPort
void diskChangedCallback(DADiskRef disk, CFArrayRef keys, void *context)
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
MythMediaType
Definition: mythmedia.h:24
@ MEDIATYPE_DVD
Definition: mythmedia.h:29
@ MEDIATYPE_AUDIO
Definition: mythmedia.h:28
@ MEDIATYPE_UNKNOWN
Definition: mythmedia.h:25
@ MEDIASTAT_NODISK
CD/DVD tray closed but empty, device unusable.
Definition: mythmedia.h:17
@ MEDIASTAT_USEABLE
Definition: mythmedia.h:19
@ MEDIASTAT_MOUNTED
Definition: mythmedia.h:21