Ticket #2598: mtd-darwin.cpp

File mtd-darwin.cpp, 9.9 KB (added by awk@…, 19 years ago)

mtd-darwin.cpp - Darwin subclass of mtd disk checking thread uses DiskArbitration?

Line 
1#include "mtd-darwin.h"
2
3#include <mythtv/mythcontext.h>
4#include <mythtv/mythmedia.h>
5#include <IOKit/IOKitLib.h>
6#include <IOKit/storage/IOMedia.h>
7#include <IOKit/storage/IOCDMedia.h>
8#include <IOKit/storage/IODVDMedia.h>
9#include <DiskArbitration/DiskArbitration.h>
10
11static mach_port_t sMasterPort;
12
13DarwinDiscCheckingThread::DarwinDiscCheckingThread(MTD *owner,
14                    DVDProbe *probe,
15                    QMutex *drive_access_mutex,
16                    QMutex *mutex_for_titles) : DiscCheckingThread(owner, probe, drive_access_mutex, mutex_for_titles)
17{
18}
19
20
21/**
22 * Guess the media that a volume/partition is on
23 */
24MediaType FindMediaType(io_service_t service)
25{
26    kern_return_t  kernResult;
27    io_iterator_t  iter;
28    MediaType      mediaType = MEDIATYPE_UNKNOWN;
29    QString        msg = QString("FindMediaType() - ");
30    bool           isWholeMedia = false;
31
32    // Create an iterator across all parents of the service object passed in.
33    kernResult = IORegistryEntryCreateIterator(service,
34                                               kIOServicePlane,
35                                               kIORegistryIterateRecursively
36                                               | kIORegistryIterateParents,
37                                               &iter);
38
39    if (KERN_SUCCESS != kernResult)
40        VERBOSE(VB_IMPORTANT, (msg + "IORegistryEntryCreateIterator"
41                                   + " returned %1").arg(kernResult));
42    else if (!iter)
43        VERBOSE(VB_IMPORTANT, msg + "IORegistryEntryCreateIterator"
44                                  + " returned a NULL iterator");
45    else
46    {
47        // A reference on the initial service object is released in
48        // the do-while loop below, so add a reference to balance
49        IOObjectRetain(service);
50       
51        do
52        {
53            isWholeMedia = false;
54            if (IOObjectConformsTo(service, kIOMediaClass))
55            {
56                CFTypeRef wholeMedia;
57
58                wholeMedia = IORegistryEntryCreateCFProperty
59                             (service, CFSTR(kIOMediaWholeKey),
60                              kCFAllocatorDefault, 0);
61
62                if (NULL == wholeMedia)
63                    VERBOSE(VB_IMPORTANT,
64                            msg + "Could not retrieve Whole property");
65                else
66                {
67                    isWholeMedia = CFBooleanGetValue((CFBooleanRef)wholeMedia);
68                    CFRelease(wholeMedia);
69                }
70            }
71
72            if (isWholeMedia)
73            {
74                if (IOObjectConformsTo(service, kIODVDMediaClass))
75                    mediaType = MEDIATYPE_DVD;
76                else if (IOObjectConformsTo(service, kIOCDMediaClass))
77                    mediaType = MEDIATYPE_AUDIO;
78            }
79
80            IOObjectRelease(service);
81
82        } while ((service = IOIteratorNext(iter))
83                 && (mediaType == MEDIATYPE_UNKNOWN));
84
85        IOObjectRelease(iter);
86    }
87    return mediaType;
88}
89
90/**
91 * Given a BSD device node name, guess its media type
92 */
93MediaType MediaTypeForBSDName(const char *bsdName)
94{
95    CFMutableDictionaryRef  matchingDict;
96    kern_return_t           kernResult;
97    io_iterator_t           iter;
98    io_service_t            service;
99    QString                 msg = QString("MediaTypeForBSDName(%1)")
100                                  .arg(bsdName);
101    MediaType               mediaType;
102
103
104    if (!bsdName || !*bsdName)
105    {
106        VERBOSE(VB_IMPORTANT, msg + " - Error. No name supplied?");
107        return MEDIATYPE_UNKNOWN;
108    }
109
110    matchingDict = IOBSDNameMatching(sMasterPort, 0, bsdName);
111    if (NULL == matchingDict)
112    {
113        VERBOSE(VB_IMPORTANT, msg + " - Error. IOBSDNameMatching()"
114                                  + " returned a NULL dictionary.");
115        return MEDIATYPE_UNKNOWN;
116    }
117
118    // Return an iterator across all objects with the matching
119    // BSD node name. Note that there should only be one match!
120    kernResult = IOServiceGetMatchingServices(sMasterPort, matchingDict, &iter);
121
122    if (KERN_SUCCESS != kernResult)
123    {
124        VERBOSE(VB_IMPORTANT, (msg + " - Error. IOServiceGetMatchingServices()"
125                                   + " returned %2").arg(kernResult));
126        return MEDIATYPE_UNKNOWN;
127    }
128    if (!iter)
129    {
130        VERBOSE(VB_IMPORTANT, msg + " - Error. IOServiceGetMatchingServices()"
131                                  + " returned a NULL iterator");
132        return MEDIATYPE_UNKNOWN;
133    }
134
135    service = IOIteratorNext(iter);
136
137    // Release this now because we only expect
138    // the iterator to contain a single io_service_t.
139    IOObjectRelease(iter);
140
141    if (!service)
142    {
143        VERBOSE(VB_IMPORTANT, msg + " - Error. IOIteratorNext()"
144                                  + " returned a NULL iterator");
145        return MEDIATYPE_UNKNOWN;
146    }
147    mediaType = FindMediaType(service);
148    IOObjectRelease(service);
149    return mediaType;
150}
151
152/*
153 * Callbacks which the Disk Arbitration session invokes
154 * whenever a disk comes or goes, or is renamed
155 */
156
157void diskAppearedCallback(DADiskRef disk, void *context)
158{
159    const char          *BSDname = DADiskGetBSDName(disk);
160    CFDictionaryRef      details;
161    bool                 isCDorDVD;
162    DarwinDiscCheckingThread *ddct;
163    QString              msg = "diskAppearedCallback() - ";
164    MediaType           mediaType;
165
166    if (!BSDname)
167    {
168        VERBOSE(VB_UPNP, msg + "Skipping non-local device");
169        return;
170    }
171
172    if (!context)
173    {
174        VERBOSE(VB_IMPORTANT, msg + "Error. Invoked with a NULL context.");
175        return;
176    }
177
178    ddct = reinterpret_cast<DarwinDiscCheckingThread*>(context);
179
180
181    // We want to monitor CDs/DVDs and USB cameras or flash drives,
182    // but probably not hard disk or network drives. For now, ignore
183    // any disk or partitions that are not on removable media.
184    // Seems OK for hot-plug USB/FireWire disks (i.e. they are removable)
185
186    details = DADiskCopyDescription(disk);
187    if (kCFBooleanFalse ==
188        CFDictionaryGetValue(details, kDADiskDescriptionMediaRemovableKey))
189    {
190        VERBOSE(VB_UPNP, msg + "Skipping non-removable " + BSDname);
191        CFRelease(details);
192        return;
193    }
194
195    // Get the volume name for more user-friendly interaction
196    //volName = getVolName(details);
197
198    mediaType = MediaTypeForBSDName(BSDname);
199    isCDorDVD = (mediaType == MEDIATYPE_DVD) || (mediaType == MEDIATYPE_AUDIO);
200
201
202    // We know it is removable, and have guessed the type.
203    // Call a helper function to create appropriate objects and insert
204
205    if (mediaType == MEDIATYPE_DVD)
206    {
207        VERBOSE(VB_IMPORTANT, QString("Found disk %1 ").arg(BSDname));
208        ddct->DoProbe(BSDname);
209    }
210   
211//    ddct->diskInsert(BSDname, volName, isCDorDVD);
212
213    CFRelease(details);
214//    free(volName);
215}
216
217void diskDisappearedCallback(DADiskRef disk, void *context)
218{
219    const char  *BSDname = DADiskGetBSDName(disk);
220
221    if (context)
222    {
223        VERBOSE(VB_IMPORTANT, QString("diskDisappearedCallback - bsdname = %1").arg(BSDname));
224        reinterpret_cast<DarwinDiscCheckingThread*>(context)->DiskRemoved(BSDname);
225//        reinterpret_cast<DarwinDiscCheckingThread *>(context)->diskRemove(BSDname);
226    }
227}
228
229void diskChangedCallback(DADiskRef disk, CFArrayRef keys, void *context)
230{
231    (void)context;
232    if (CFArrayContainsValue(keys, CFRangeMake(0, CFArrayGetCount(keys)),
233                             kDADiskDescriptionVolumeNameKey))
234    {
235        const char     *BSDname = DADiskGetBSDName(disk);
236        CFDictionaryRef details = DADiskCopyDescription(disk);
237//        char           *volName = getVolName(details);
238
239        VERBOSE(VB_IMPORTANT, QString("diskChangedCallback - bsdname = %1").arg(BSDname));
240//        reinterpret_cast<DarwinDiscCheckingThread *>(context)->diskRename(BSDname, volName);
241        CFRelease(details);
242//        free(volName);
243    }
244}
245
246void DarwinDiscCheckingThread::run()
247{
248    CFDictionaryRef match     = kDADiskDescriptionMatchVolumeMountable;
249    DASessionRef    daSession = DASessionCreate(kCFAllocatorDefault);
250
251    DARegisterDiskAppearedCallback(daSession, match,
252                                   diskAppearedCallback, this);
253    DARegisterDiskDisappearedCallback(daSession, match,
254                                      diskDisappearedCallback, this);
255    DARegisterDiskDescriptionChangedCallback(daSession, match,
256                                             kDADiskDescriptionWatchVolumeName,
257                                             diskChangedCallback, this);
258
259    DASessionScheduleWithRunLoop(daSession,
260                                 CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
261
262
263    // Nice and simple, as long as our monitor is valid and active,
264    // loop and let daSession check the devices.
265    while (keepGoing())       
266    {
267        // Run the run loop for 3 seconds - this will
268        // handle any disk arbitration appeared/dissappeared events
269        CFRunLoopRunInMode(kCFRunLoopDefaultMode,
270                           (float) 3.0f, false );
271        sleep(1);
272    }
273
274    DAUnregisterCallback(daSession, (void(*))diskChangedCallback,     this);
275    DAUnregisterCallback(daSession, (void(*))diskDisappearedCallback, this);
276    DAUnregisterCallback(daSession, (void(*))diskAppearedCallback,    this);
277    CFRelease(daSession);
278   
279   
280}
281
282void DarwinDiscCheckingThread::DoProbe(const char *inDeviceName)
283{
284    dvd_probe->setDevice(QString("/dev/r%1").arg(inDeviceName));
285
286    while(!dvd_drive_access->tryLock())
287    {
288        sleep(3);
289    }
290    while(!titles_mutex->tryLock())
291    {
292        sleep(3);
293    }
294
295    if (dvd_probe->probe())
296        have_disc = true;
297       
298    titles_mutex->unlock();
299    dvd_drive_access->unlock();
300}
301
302void DarwinDiscCheckingThread::DiskRemoved(const char *inDeviceName)
303{
304    (void)inDeviceName;
305    dvd_probe->setDevice("");
306
307    while(!dvd_drive_access->tryLock())
308    {
309        sleep(3);
310    }
311    while(!titles_mutex->tryLock())
312    {
313        sleep(3);
314    }
315
316    have_disc = false;
317
318    titles_mutex->unlock();
319    dvd_drive_access->unlock();
320}